From a109510ed38c71daf57b1571c4c74d4b0a9c9a23 Mon Sep 17 00:00:00 2001 From: Fogus Date: Tue, 11 Mar 2025 09:08:24 -0400 Subject: [PATCH 01/10] wip --- src/jvm/clojure/lang/Compiler.java | 20 +++++++++++++------- test/clojure/test_clojure/param_tags.clj | 3 +-- test/java/clojure/test/SwissArmy.java | 3 ++- 3 files changed, 16 insertions(+), 10 deletions(-) diff --git a/src/jvm/clojure/lang/Compiler.java b/src/jvm/clojure/lang/Compiler.java index 6324456fa1..d64d6b2c60 100644 --- a/src/jvm/clojure/lang/Compiler.java +++ b/src/jvm/clojure/lang/Compiler.java @@ -1260,7 +1260,7 @@ private static Set aritySet(Class c, String methodName, MethodKind kind) { // Returns a list of methods or ctors matching the name and kind given. // Otherwise, will throw if the information provided results in no matches - private static List methodsWithName(Class c, String methodName, MethodKind kind) { + public static List methodsWithName(Class c, String methodName, MethodKind kind) { if (kind == MethodKind.CTOR) { List ctors = Arrays.asList(c.getConstructors()); if(ctors.isEmpty()) @@ -4359,18 +4359,24 @@ static public Expr parse(C context, ISeq form) { (KeywordExpr) fexpr, target); } - // Preserving the existing static field bug that replaces a reference in parens with - // the field itself rather than trying to invoke the value in the field. This is - // an exception to the uniform Class/member qualification per CLJ-2806 ticket. - if(fexpr instanceof StaticFieldExpr) - return fexpr; - PersistentVector args = PersistentVector.EMPTY; for(ISeq s = RT.seq(form.next()); s != null; s = s.next()) { args = args.cons(analyze(context, s.first())); } + // Preserving the existing static field bug that replaces a reference in parens with + // the field itself rather than trying to invoke the value in the field. This is + // an exception to the uniform Class/member qualification per CLJ-2806 ticket. + if(fexpr instanceof StaticFieldExpr) + if(RT.count(args) == 0) + return fexpr; + else { + Class c = ((StaticFieldExpr)fexpr).c; + Symbol sym = Symbol.intern(c.getName(), ((StaticFieldExpr)fexpr).fieldName); + return toHostExpr(new QualifiedMethodExpr(c, sym), (String) SOURCE.deref(), lineDeref(), columnDeref(), tagOf(form), tailPosition, args); + } + if(fexpr instanceof QualifiedMethodExpr) return toHostExpr((QualifiedMethodExpr)fexpr, (String) SOURCE.deref(), lineDeref(), columnDeref(), tagOf(form), tailPosition, args); diff --git a/test/clojure/test_clojure/param_tags.clj b/test/clojure/test_clojure/param_tags.clj index fee2d1c53e..bb39c8cbc7 100644 --- a/test/clojure/test_clojure/param_tags.clj +++ b/test/clojure/test_clojure/param_tags.clj @@ -165,8 +165,7 @@ (case (:type m) :constructor (let [{:keys [expected actual]} (exercise-constructor m)] (is (instance? expected actual))) - :static (let [{:keys [expected actual]} (exercise-static-method m)] - (is (= expected actual))) + :static (let [{:keys [expected actual]} (exercise-static-method m)] (is (= expected actual))) :instance (let [{:keys [expected actual]} (exercise-instance-method m)] (is (= expected actual)))))) diff --git a/test/java/clojure/test/SwissArmy.java b/test/java/clojure/test/SwissArmy.java index 339c3723c4..235a5ceb3a 100644 --- a/test/java/clojure/test/SwissArmy.java +++ b/test/java/clojure/test/SwissArmy.java @@ -1,6 +1,7 @@ package clojure.test; public class SwissArmy { + public static String doppelganger = "static-field"; public String ctorId; public SwissArmy() {this.ctorId = "1";} @@ -36,4 +37,4 @@ public class SwissArmy { public static String staticArityOverloadMethod(int a, int b) {return "int-int";} public static String staticArityOverloadMethod(int a, int b, int c) {return "int-int-int";} public static String doppelganger(int a, int b, long c) {return "int-int-long";} -} \ No newline at end of file +} From 6973437cd9ce254de0a139e58a16c6d9ed40880b Mon Sep 17 00:00:00 2001 From: Fogus Date: Mon, 14 Apr 2025 10:05:56 -0400 Subject: [PATCH 02/10] Added regression test for CLJ-2899 --- src/jvm/clojure/lang/Compiler.java | 11 +++++++---- test/clojure/test_clojure/param_tags.clj | 12 +++++++++++- test/java/clojure/test/SwissArmy.java | 1 + 3 files changed, 19 insertions(+), 5 deletions(-) diff --git a/src/jvm/clojure/lang/Compiler.java b/src/jvm/clojure/lang/Compiler.java index d64d6b2c60..072e0133db 100644 --- a/src/jvm/clojure/lang/Compiler.java +++ b/src/jvm/clojure/lang/Compiler.java @@ -4368,14 +4368,17 @@ static public Expr parse(C context, ISeq form) { // Preserving the existing static field bug that replaces a reference in parens with // the field itself rather than trying to invoke the value in the field. This is // an exception to the uniform Class/member qualification per CLJ-2806 ticket. - if(fexpr instanceof StaticFieldExpr) - if(RT.count(args) == 0) + if(fexpr instanceof StaticFieldExpr) { + Class c = ((StaticFieldExpr) fexpr).c; + String name = ((StaticFieldExpr) fexpr).fieldName; + + if (RT.count(args) == 0 && QualifiedMethodExpr.methodsWithName(c, name, QualifiedMethodExpr.MethodKind.STATIC).isEmpty()) return fexpr; else { - Class c = ((StaticFieldExpr)fexpr).c; - Symbol sym = Symbol.intern(c.getName(), ((StaticFieldExpr)fexpr).fieldName); + Symbol sym = Symbol.intern(c.getName(), name); return toHostExpr(new QualifiedMethodExpr(c, sym), (String) SOURCE.deref(), lineDeref(), columnDeref(), tagOf(form), tailPosition, args); } + } if(fexpr instanceof QualifiedMethodExpr) return toHostExpr((QualifiedMethodExpr)fexpr, (String) SOURCE.deref(), lineDeref(), columnDeref(), tagOf(form), tailPosition, args); diff --git a/test/clojure/test_clojure/param_tags.clj b/test/clojure/test_clojure/param_tags.clj index bb39c8cbc7..7118416afa 100644 --- a/test/clojure/test_clojure/param_tags.clj +++ b/test/clojure/test_clojure/param_tags.clj @@ -165,10 +165,20 @@ (case (:type m) :constructor (let [{:keys [expected actual]} (exercise-constructor m)] (is (instance? expected actual))) - :static (let [{:keys [expected actual]} (exercise-static-method m)] (is (= expected actual))) + :static (let [{:keys [expected actual]} (exercise-static-method m)] + (is (= expected actual))) :instance (let [{:keys [expected actual]} (exercise-instance-method m)] (is (= expected actual)))))) +(deftest field-shadows-method-CLJ-2899-regression + (is (= "static-field" clojure.test.SwissArmy/doppelganger)) + (is (= "" (clojure.test.SwissArmy/doppelganger))) ;; favor 0-arity call over unwrap static field bug + (is (= "int-int" (clojure.test.SwissArmy/.doppelganger (clojure.test.SwissArmy/new) (int 1) (int 2)))) + (is (= "int-int" (apply clojure.test.SwissArmy/.doppelganger (clojure.test.SwissArmy/new) (int 1) (int 2) []))) + ;; Can't distinguish field vs static method in value position + ;; (is (= "int-int-long" (apply clojure.test.SwissArmy/doppelganger (int 1) (int 2) (long 42) []))) + (is (= "int-int-long" (clojure.test.SwissArmy/doppelganger (int 1) (int 2) (long 42))))) + (defmacro arg-tags-called-in-macro [a-type b-type a b] `(^[~a-type ~b-type] SwissArmy/staticArityOverloadMethod ~a ~b)) diff --git a/test/java/clojure/test/SwissArmy.java b/test/java/clojure/test/SwissArmy.java index 235a5ceb3a..b563d85acb 100644 --- a/test/java/clojure/test/SwissArmy.java +++ b/test/java/clojure/test/SwissArmy.java @@ -37,4 +37,5 @@ public class SwissArmy { public static String staticArityOverloadMethod(int a, int b) {return "int-int";} public static String staticArityOverloadMethod(int a, int b, int c) {return "int-int-int";} public static String doppelganger(int a, int b, long c) {return "int-int-long";} + public static String doppelganger() {return "";} } From 552aad080fba6b5c9134008d112dc53773215d22 Mon Sep 17 00:00:00 2001 From: Fogus Date: Mon, 14 Apr 2025 10:34:35 -0400 Subject: [PATCH 03/10] not a field access if there are param-tags --- src/jvm/clojure/lang/Compiler.java | 6 ++++-- test/clojure/test_clojure/param_tags.clj | 3 ++- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/jvm/clojure/lang/Compiler.java b/src/jvm/clojure/lang/Compiler.java index 072e0133db..55cc4e0688 100644 --- a/src/jvm/clojure/lang/Compiler.java +++ b/src/jvm/clojure/lang/Compiler.java @@ -4372,7 +4372,9 @@ static public Expr parse(C context, ISeq form) { Class c = ((StaticFieldExpr) fexpr).c; String name = ((StaticFieldExpr) fexpr).fieldName; - if (RT.count(args) == 0 && QualifiedMethodExpr.methodsWithName(c, name, QualifiedMethodExpr.MethodKind.STATIC).isEmpty()) + if (RT.count(args) == 0 +// && ((form.first() instanceof Symbol) && (paramTagsOf((Symbol)form.first()) == null)) + && QualifiedMethodExpr.methodsWithName(c, name, QualifiedMethodExpr.MethodKind.STATIC).isEmpty()) return fexpr; else { Symbol sym = Symbol.intern(c.getName(), name); @@ -7838,7 +7840,7 @@ private static Expr analyzeSymbol(Symbol sym) { Class c = HostExpr.maybeClass(nsSym, false); if(c != null) { - if(Reflector.getField(c, sym.name, true) != null) + if((paramTagsOf(sym) == null) && (Reflector.getField(c, sym.name, true) != null)) return new StaticFieldExpr(lineDeref(), columnDeref(), c, sym.name, tag); else return new QualifiedMethodExpr(c, sym); diff --git a/test/clojure/test_clojure/param_tags.clj b/test/clojure/test_clojure/param_tags.clj index 7118416afa..dc148cf159 100644 --- a/test/clojure/test_clojure/param_tags.clj +++ b/test/clojure/test_clojure/param_tags.clj @@ -175,8 +175,9 @@ (is (= "" (clojure.test.SwissArmy/doppelganger))) ;; favor 0-arity call over unwrap static field bug (is (= "int-int" (clojure.test.SwissArmy/.doppelganger (clojure.test.SwissArmy/new) (int 1) (int 2)))) (is (= "int-int" (apply clojure.test.SwissArmy/.doppelganger (clojure.test.SwissArmy/new) (int 1) (int 2) []))) - ;; Can't distinguish field vs static method in value position + ;; Can't distinguish field vs static method in value position w/o param-tags ;; (is (= "int-int-long" (apply clojure.test.SwissArmy/doppelganger (int 1) (int 2) (long 42) []))) + (is (= "" (apply ^[] clojure.test.SwissArmy/doppelganger []))) (is (= "int-int-long" (clojure.test.SwissArmy/doppelganger (int 1) (int 2) (long 42))))) (defmacro arg-tags-called-in-macro From 9699878f69640885f36296c8baf61ec99c7366bc Mon Sep 17 00:00:00 2001 From: Fogus Date: Mon, 14 Apr 2025 12:04:46 -0400 Subject: [PATCH 04/10] Cleaning up comments --- src/jvm/clojure/lang/Compiler.java | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/jvm/clojure/lang/Compiler.java b/src/jvm/clojure/lang/Compiler.java index 55cc4e0688..66b4ba090e 100644 --- a/src/jvm/clojure/lang/Compiler.java +++ b/src/jvm/clojure/lang/Compiler.java @@ -4365,16 +4365,14 @@ static public Expr parse(C context, ISeq form) { args = args.cons(analyze(context, s.first())); } - // Preserving the existing static field bug that replaces a reference in parens with - // the field itself rather than trying to invoke the value in the field. This is - // an exception to the uniform Class/member qualification per CLJ-2806 ticket. if(fexpr instanceof StaticFieldExpr) { Class c = ((StaticFieldExpr) fexpr).c; String name = ((StaticFieldExpr) fexpr).fieldName; - if (RT.count(args) == 0 -// && ((form.first() instanceof Symbol) && (paramTagsOf((Symbol)form.first()) == null)) - && QualifiedMethodExpr.methodsWithName(c, name, QualifiedMethodExpr.MethodKind.STATIC).isEmpty()) + // Preserving the existing static field bug that replaces a reference in parens with + // the field itself rather than trying to invoke the value in the field. This is + // an exception to the uniform Class/member qualification per CLJ-2806 ticket. + if (RT.count(args) == 0 && QualifiedMethodExpr.methodsWithName(c, name, QualifiedMethodExpr.MethodKind.STATIC).isEmpty()) return fexpr; else { Symbol sym = Symbol.intern(c.getName(), name); From 2af7d086521546217102b1eb18af6a88f669e214 Mon Sep 17 00:00:00 2001 From: Fogus Date: Tue, 13 May 2025 08:46:53 -0400 Subject: [PATCH 05/10] Modified to check field/method overloads in analyzeSymbol. QME.emit/eval checks for overloads and emits field or thunk. --- src/jvm/clojure/lang/Compiler.java | 81 ++++++++++++++++++------------ 1 file changed, 49 insertions(+), 32 deletions(-) diff --git a/src/jvm/clojure/lang/Compiler.java b/src/jvm/clojure/lang/Compiler.java index 66b4ba090e..21da7a0575 100644 --- a/src/jvm/clojure/lang/Compiler.java +++ b/src/jvm/clojure/lang/Compiler.java @@ -1165,12 +1165,17 @@ static class QualifiedMethodExpr implements Expr { private final String methodName; private final MethodKind kind; private final Class tagClass; + private final StaticFieldExpr fieldOverload; private enum MethodKind { CTOR, INSTANCE, STATIC } - public QualifiedMethodExpr(Class methodClass, Symbol sym){ + public QualifiedMethodExpr(Class methodClass, Symbol sym) { + this(methodClass, sym, null); + } + + public QualifiedMethodExpr(Class methodClass, Symbol sym, StaticFieldExpr fieldOL) { c = methodClass; methodSymbol = sym; tagClass = tagOf(sym) != null ? HostExpr.tagToClass(tagOf(sym)) : AFn.class; @@ -1187,18 +1192,28 @@ else if(sym.name.equals("new")) { kind = MethodKind.STATIC; methodName = sym.name; } + fieldOverload = fieldOL; } // Expr impl - invocation, convert to fn expr + private boolean overloadsField() { + return fieldOverload != null && paramTagsOf(methodSymbol) == null; + } @Override public Object eval() { - return buildThunk(C.EVAL, this).eval(); + if(overloadsField()) + return fieldOverload.eval(); + else + return buildThunk(C.EVAL, this).eval(); } @Override public void emit(C context, ObjExpr objx, GeneratorAdapter gen) { - buildThunk(context, this).emit(context, objx, gen); + if(overloadsField()) + fieldOverload.emit(context, objx, gen); + else + buildThunk(context, this).emit(context, objx, gen); } // Expr impl - method value, always an AFn @@ -1258,18 +1273,9 @@ private static Set aritySet(Class c, String methodName, MethodKind kind) { return res; } - // Returns a list of methods or ctors matching the name and kind given. - // Otherwise, will throw if the information provided results in no matches - public static List methodsWithName(Class c, String methodName, MethodKind kind) { - if (kind == MethodKind.CTOR) { - List ctors = Arrays.asList(c.getConstructors()); - if(ctors.isEmpty()) - throw noMethodWithNameException(c, methodName, kind); - return ctors; - } - + public static List methodOverloads(Class c, String methodName, MethodKind kind) { final Executable[] methods = c.getMethods(); - List res = Arrays.stream(methods) + return Arrays.stream(methods) .filter(m -> m.getName().equals(methodName)) .filter(m -> { switch(kind) { @@ -1279,6 +1285,19 @@ public static List methodsWithName(Class c, String methodName, Metho } }) .collect(Collectors.toList()); + } + + // Returns a list of methods or ctors matching the name and kind given. + // Otherwise, will throw if the information provided results in no matches + private static List methodsWithName(Class c, String methodName, MethodKind kind) { + if (kind == MethodKind.CTOR) { + List ctors = Arrays.asList(c.getConstructors()); + if(ctors.isEmpty()) + throw noMethodWithNameException(c, methodName, kind); + return ctors; + } + + List res = methodOverloads(c, methodName, kind); if(res.isEmpty()) throw noMethodWithNameException(c, methodName, kind); @@ -4115,7 +4134,7 @@ static Object sigTag(int argcount, Var v){ return tagOf(sig); } return null; - } + } public InvokeExpr(String source, int line, int column, Symbol tag, Expr fexpr, IPersistentVector args, boolean tailPosition) { this.source = source; @@ -4359,27 +4378,18 @@ static public Expr parse(C context, ISeq form) { (KeywordExpr) fexpr, target); } + // Preserving the existing static field bug that replaces a reference in parens with + // the field itself rather than trying to invoke the value in the field. This is + // an exception to the uniform Class/member qualification per CLJ-2806 ticket. + if(fexpr instanceof StaticFieldExpr) + return fexpr; + PersistentVector args = PersistentVector.EMPTY; for(ISeq s = RT.seq(form.next()); s != null; s = s.next()) { args = args.cons(analyze(context, s.first())); } - if(fexpr instanceof StaticFieldExpr) { - Class c = ((StaticFieldExpr) fexpr).c; - String name = ((StaticFieldExpr) fexpr).fieldName; - - // Preserving the existing static field bug that replaces a reference in parens with - // the field itself rather than trying to invoke the value in the field. This is - // an exception to the uniform Class/member qualification per CLJ-2806 ticket. - if (RT.count(args) == 0 && QualifiedMethodExpr.methodsWithName(c, name, QualifiedMethodExpr.MethodKind.STATIC).isEmpty()) - return fexpr; - else { - Symbol sym = Symbol.intern(c.getName(), name); - return toHostExpr(new QualifiedMethodExpr(c, sym), (String) SOURCE.deref(), lineDeref(), columnDeref(), tagOf(form), tailPosition, args); - } - } - if(fexpr instanceof QualifiedMethodExpr) return toHostExpr((QualifiedMethodExpr)fexpr, (String) SOURCE.deref(), lineDeref(), columnDeref(), tagOf(form), tailPosition, args); @@ -7838,8 +7848,15 @@ private static Expr analyzeSymbol(Symbol sym) { Class c = HostExpr.maybeClass(nsSym, false); if(c != null) { - if((paramTagsOf(sym) == null) && (Reflector.getField(c, sym.name, true) != null)) - return new StaticFieldExpr(lineDeref(), columnDeref(), c, sym.name, tag); + if(Reflector.getField(c, sym.name, true) != null) + { + List maybeOverloads = QualifiedMethodExpr.methodOverloads(c, sym.name, QualifiedMethodExpr.MethodKind.STATIC); + + if(maybeOverloads.isEmpty()) + return new StaticFieldExpr(lineDeref(), columnDeref(), c, sym.name, tag); + else + return new QualifiedMethodExpr(c, sym, new StaticFieldExpr(lineDeref(), columnDeref(), c, sym.name, tag)); + } else return new QualifiedMethodExpr(c, sym); } From c3721cb17c0dbd9db3ada7e25e657a7660f69605 Mon Sep 17 00:00:00 2001 From: Fogus Date: Tue, 13 May 2025 09:09:20 -0400 Subject: [PATCH 06/10] preserving pre 1.12 error on non-overloaded field holding IFn in invoke position taking args. --- src/jvm/clojure/lang/Compiler.java | 20 ++++++++++++++------ test/clojure/test_clojure/param_tags.clj | 3 ++- test/java/clojure/test/SwissArmy.java | 4 ++++ 3 files changed, 20 insertions(+), 7 deletions(-) diff --git a/src/jvm/clojure/lang/Compiler.java b/src/jvm/clojure/lang/Compiler.java index 21da7a0575..7b7e9658dd 100644 --- a/src/jvm/clojure/lang/Compiler.java +++ b/src/jvm/clojure/lang/Compiler.java @@ -4378,18 +4378,26 @@ static public Expr parse(C context, ISeq form) { (KeywordExpr) fexpr, target); } - // Preserving the existing static field bug that replaces a reference in parens with - // the field itself rather than trying to invoke the value in the field. This is - // an exception to the uniform Class/member qualification per CLJ-2806 ticket. - if(fexpr instanceof StaticFieldExpr) - return fexpr; - PersistentVector args = PersistentVector.EMPTY; for(ISeq s = RT.seq(form.next()); s != null; s = s.next()) { args = args.cons(analyze(context, s.first())); } + // Preserving the existing static field syntax that replaces a reference in parens with + // the field itself rather than trying to invoke the value in the field. This is + // an exception to the uniform Class/member qualification per CLJ-2806 ticket. + if(fexpr instanceof StaticFieldExpr) + { + if(RT.count(args) == 0) + return fexpr; + else + throw new IllegalArgumentException("No matching method " + + ((StaticFieldExpr) fexpr).fieldName + + " found taking " + RT.count(args) + " args for " + + ((StaticFieldExpr) fexpr).c); + } + if(fexpr instanceof QualifiedMethodExpr) return toHostExpr((QualifiedMethodExpr)fexpr, (String) SOURCE.deref(), lineDeref(), columnDeref(), tagOf(form), tailPosition, args); diff --git a/test/clojure/test_clojure/param_tags.clj b/test/clojure/test_clojure/param_tags.clj index dc148cf159..d9f0fb618f 100644 --- a/test/clojure/test_clojure/param_tags.clj +++ b/test/clojure/test_clojure/param_tags.clj @@ -178,7 +178,8 @@ ;; Can't distinguish field vs static method in value position w/o param-tags ;; (is (= "int-int-long" (apply clojure.test.SwissArmy/doppelganger (int 1) (int 2) (long 42) []))) (is (= "" (apply ^[] clojure.test.SwissArmy/doppelganger []))) - (is (= "int-int-long" (clojure.test.SwissArmy/doppelganger (int 1) (int 2) (long 42))))) + (is (= "int-int-long" (clojure.test.SwissArmy/doppelganger (int 1) (int 2) (long 42)))) + (is (thrown? Exception (eval '(clojure.test.SwissArmy/idFn 42))))) (defmacro arg-tags-called-in-macro [a-type b-type a b] diff --git a/test/java/clojure/test/SwissArmy.java b/test/java/clojure/test/SwissArmy.java index b563d85acb..f38e2d44f5 100644 --- a/test/java/clojure/test/SwissArmy.java +++ b/test/java/clojure/test/SwissArmy.java @@ -1,8 +1,12 @@ package clojure.test; +import clojure.java.api.Clojure; +import clojure.lang.IFn; + public class SwissArmy { public static String doppelganger = "static-field"; public String ctorId; + public static IFn idFn = Clojure.var("clojure.core", "identity"); public SwissArmy() {this.ctorId = "1";} public SwissArmy(int a, long b) {this.ctorId = "2";} From f13a64e20369046534d671025eb9571161a24ad5 Mon Sep 17 00:00:00 2001 From: Fogus Date: Tue, 13 May 2025 09:12:33 -0400 Subject: [PATCH 07/10] tests for non-overloaded field holding IFn in invoke position --- test/clojure/test_clojure/param_tags.clj | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/test/clojure/test_clojure/param_tags.clj b/test/clojure/test_clojure/param_tags.clj index d9f0fb618f..c22dee67b5 100644 --- a/test/clojure/test_clojure/param_tags.clj +++ b/test/clojure/test_clojure/param_tags.clj @@ -179,7 +179,9 @@ ;; (is (= "int-int-long" (apply clojure.test.SwissArmy/doppelganger (int 1) (int 2) (long 42) []))) (is (= "" (apply ^[] clojure.test.SwissArmy/doppelganger []))) (is (= "int-int-long" (clojure.test.SwissArmy/doppelganger (int 1) (int 2) (long 42)))) - (is (thrown? Exception (eval '(clojure.test.SwissArmy/idFn 42))))) + (is (thrown? Exception (eval '(clojure.test.SwissArmy/idFn 42)))) + (is (= #'clojure.core/identity clojure.test.SwissArmy/idFn)) + (is (= #'clojure.core/identity (clojure.test.SwissArmy/idFn)))) (defmacro arg-tags-called-in-macro [a-type b-type a b] From 1c39356eedbf9362b9c991c86bd861b0a54fda14 Mon Sep 17 00:00:00 2001 From: Fogus Date: Tue, 13 May 2025 14:10:21 -0400 Subject: [PATCH 08/10] aggregating related tests --- test/clojure/test_clojure/param_tags.clj | 35 ++++++++++++++++-------- 1 file changed, 23 insertions(+), 12 deletions(-) diff --git a/test/clojure/test_clojure/param_tags.clj b/test/clojure/test_clojure/param_tags.clj index c22dee67b5..aabdbe93c8 100644 --- a/test/clojure/test_clojure/param_tags.clj +++ b/test/clojure/test_clojure/param_tags.clj @@ -170,18 +170,29 @@ :instance (let [{:keys [expected actual]} (exercise-instance-method m)] (is (= expected actual)))))) -(deftest field-shadows-method-CLJ-2899-regression - (is (= "static-field" clojure.test.SwissArmy/doppelganger)) - (is (= "" (clojure.test.SwissArmy/doppelganger))) ;; favor 0-arity call over unwrap static field bug - (is (= "int-int" (clojure.test.SwissArmy/.doppelganger (clojure.test.SwissArmy/new) (int 1) (int 2)))) - (is (= "int-int" (apply clojure.test.SwissArmy/.doppelganger (clojure.test.SwissArmy/new) (int 1) (int 2) []))) - ;; Can't distinguish field vs static method in value position w/o param-tags - ;; (is (= "int-int-long" (apply clojure.test.SwissArmy/doppelganger (int 1) (int 2) (long 42) []))) - (is (= "" (apply ^[] clojure.test.SwissArmy/doppelganger []))) - (is (= "int-int-long" (clojure.test.SwissArmy/doppelganger (int 1) (int 2) (long 42)))) - (is (thrown? Exception (eval '(clojure.test.SwissArmy/idFn 42)))) - (is (= #'clojure.core/identity clojure.test.SwissArmy/idFn)) - (is (= #'clojure.core/identity (clojure.test.SwissArmy/idFn)))) +(deftest field-overloads-method-CLJ-2899-regression + (testing "overloaded in value position" + (is (= "static-field" clojure.test.SwissArmy/doppelganger))) + + (testing "overloaded in value position, w/paramtags" + (is (= "" (apply ^[] clojure.test.SwissArmy/doppelganger [])))) + + (testing "overloaded, invoke no args" + (is (= "" (clojure.test.SwissArmy/doppelganger)))) + + (testing "overloaded, invoke w/args" + (is (= "int-int-long" (clojure.test.SwissArmy/doppelganger (int 1) (int 2) (long 42))))) + + (tesing "non-overloaded, field holds IFn, invoke w/args fails" + (is (thrown? Exception (eval '(clojure.test.SwissArmy/idFn 42)))) + (is (= #'clojure.core/identity clojure.test.SwissArmy/idFn))) + + (testing "non-overloaded, field holds IFn, invoke no args" + (is (= #'clojure.core/identity (clojure.test.SwissArmy/idFn)))) + + (testing "instance method overloads" + (is (= "int-int" (clojure.test.SwissArmy/.doppelganger (clojure.test.SwissArmy/new) (int 1) (int 2)))) + (is (= "int-int" (apply clojure.test.SwissArmy/.doppelganger (clojure.test.SwissArmy/new) (int 1) (int 2) []))))) (defmacro arg-tags-called-in-macro [a-type b-type a b] From 83e57d9e5eed28d717999d0ad376c4c31ab76291 Mon Sep 17 00:00:00 2001 From: Fogus Date: Tue, 13 May 2025 14:12:09 -0400 Subject: [PATCH 09/10] aggregating related tests --- test/clojure/test_clojure/param_tags.clj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/clojure/test_clojure/param_tags.clj b/test/clojure/test_clojure/param_tags.clj index aabdbe93c8..bccd3ef127 100644 --- a/test/clojure/test_clojure/param_tags.clj +++ b/test/clojure/test_clojure/param_tags.clj @@ -183,7 +183,7 @@ (testing "overloaded, invoke w/args" (is (= "int-int-long" (clojure.test.SwissArmy/doppelganger (int 1) (int 2) (long 42))))) - (tesing "non-overloaded, field holds IFn, invoke w/args fails" + (testing "non-overloaded, field holds IFn, invoke w/args fails" (is (thrown? Exception (eval '(clojure.test.SwissArmy/idFn 42)))) (is (= #'clojure.core/identity clojure.test.SwissArmy/idFn))) From e282531425977d6085783a76549c286ec2cab873 Mon Sep 17 00:00:00 2001 From: Fogus Date: Tue, 20 May 2025 08:54:41 -0400 Subject: [PATCH 10/10] Renaming overloadsField to preferOverloadField --- src/jvm/clojure/lang/Compiler.java | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/jvm/clojure/lang/Compiler.java b/src/jvm/clojure/lang/Compiler.java index 7b7e9658dd..0bae0f6d54 100644 --- a/src/jvm/clojure/lang/Compiler.java +++ b/src/jvm/clojure/lang/Compiler.java @@ -1195,14 +1195,15 @@ else if(sym.name.equals("new")) { fieldOverload = fieldOL; } - // Expr impl - invocation, convert to fn expr - private boolean overloadsField() { + private boolean preferOverloadedField() { return fieldOverload != null && paramTagsOf(methodSymbol) == null; } + // Expr impl - invocation, convert to fn expr + @Override public Object eval() { - if(overloadsField()) + if(preferOverloadedField()) return fieldOverload.eval(); else return buildThunk(C.EVAL, this).eval(); @@ -1210,7 +1211,7 @@ public Object eval() { @Override public void emit(C context, ObjExpr objx, GeneratorAdapter gen) { - if(overloadsField()) + if(preferOverloadedField()) fieldOverload.emit(context, objx, gen); else buildThunk(context, this).emit(context, objx, gen);