This is an automated email from the ASF dual-hosted git repository. emilles pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/groovy.git
The following commit(s) were added to refs/heads/master by this push: new fb954ba542 GROOVY-11375: STC: binary operator handling fb954ba542 is described below commit fb954ba5424facdd89ddfcb4ee08b733b019a1d8 Author: Eric Milles <eric.mil...@thomsonreuters.com> AuthorDate: Wed Jun 26 20:41:47 2024 -0500 GROOVY-11375: STC: binary operator handling --- .../classgen/asm/sc/StaticTypesCallSiteWriter.java | 120 +++++++++------------ .../classgen/asm/sc/StaticTypesTypeChooser.java | 15 +-- .../transform/stc/StaticTypeCheckingVisitor.java | 10 +- .../groovy/transform/stc/STCAssignmentTest.groovy | 2 +- .../transform/stc/STCnAryExpressionTest.groovy | 20 ++++ 5 files changed, 89 insertions(+), 78 deletions(-) diff --git a/src/main/java/org/codehaus/groovy/classgen/asm/sc/StaticTypesCallSiteWriter.java b/src/main/java/org/codehaus/groovy/classgen/asm/sc/StaticTypesCallSiteWriter.java index 9d4158e431..ee6949c80a 100644 --- a/src/main/java/org/codehaus/groovy/classgen/asm/sc/StaticTypesCallSiteWriter.java +++ b/src/main/java/org/codehaus/groovy/classgen/asm/sc/StaticTypesCallSiteWriter.java @@ -19,7 +19,6 @@ package org.codehaus.groovy.classgen.asm.sc; import org.codehaus.groovy.GroovyBugError; -import org.codehaus.groovy.ast.ASTNode; import org.codehaus.groovy.ast.ClassHelper; import org.codehaus.groovy.ast.ClassNode; import org.codehaus.groovy.ast.FieldNode; @@ -41,7 +40,6 @@ import org.codehaus.groovy.classgen.asm.CallSiteWriter; import org.codehaus.groovy.classgen.asm.CompileStack; import org.codehaus.groovy.classgen.asm.MethodCallerMultiAdapter; import org.codehaus.groovy.classgen.asm.OperandStack; -import org.codehaus.groovy.classgen.asm.TypeChooser; import org.codehaus.groovy.classgen.asm.VariableSlotLoader; import org.codehaus.groovy.runtime.InvokerHelper; import org.codehaus.groovy.syntax.SyntaxException; @@ -568,79 +566,67 @@ public class StaticTypesCallSiteWriter extends CallSiteWriter { } @Override - public void makeSingleArgumentCall(final Expression receiver, final String message, final Expression arguments, final boolean safe) { - ClassNode classNode = controller.getClassNode(); - TypeChooser typeChooser = controller.getTypeChooser(); - ClassNode rType = typeChooser.resolveType(receiver, classNode); - ClassNode aType = typeChooser.resolveType(arguments, classNode); - if (trySubscript(receiver, message, arguments, rType, aType, safe)) { - return; - } - // now try with flow type instead of declaration type - rType = receiver.getNodeMetaData(StaticTypesMarker.INFERRED_TYPE); - if (receiver instanceof VariableExpression && rType == null) { - // TODO: can STCV be made smarter to avoid this check? - ASTNode node = (ASTNode) ((VariableExpression) receiver).getAccessedVariable(); - rType = node.getNodeMetaData(StaticTypesMarker.INFERRED_TYPE); - } - if (rType!=null && trySubscript(receiver, message, arguments, rType, aType, safe)) { + public void makeSingleArgumentCall(final Expression receiver, final String message, final Expression argument, final boolean safe) { + ClassNode rType = controller.getTypeChooser().resolveType(receiver, controller.getClassNode()); + ClassNode aType = controller.getTypeChooser().resolveType(argument, controller.getClassNode()); + if (getWrapper(rType).isDerivedFrom(Number_TYPE) && getWrapper(aType).isDerivedFrom(Number_TYPE)) { + switch (message) { + case "plus": + case "minus": + case "multiply": + case "div"/*ide*/: + writeNumberNumberCall(receiver, message, argument); + return; + case "power": + writePowerCall(receiver, argument, rType, aType); + return; + case "and": + case "or": + case "xor": + case "implies": + case "remainder": + case "leftShift": + case "rightShift": + case "rightShiftUnsigned": + writeOperatorCall(receiver, argument, message); + return; + } + } else if ("plus".equals(message) && isStringType(rType)) { + writeStringPlusCall(receiver, message, argument); return; + } else if ("getAt".equals(message)) { + if (rType.isArray() && getWrapper(aType).isDerivedFrom(Number_TYPE) && !safe) { + writeArrayGet(receiver, argument, rType, aType); + return; + } + MethodNode getAt = findGetAt(rType, aType); + if (getAt != null) { + MethodCallExpression call = callX(receiver, "getAt", argument); + call.setImplicitThis(false); + call.setMethodTarget(getAt); + call.setSafe(safe); + call.setSourcePosition(argument); + call.visit(controller.getAcg()); + return; + } + if (isOrImplements(rType, MAP_TYPE)) { // Map#get accepts Object + MethodCallExpression call = callX(receiver, "get", argument); + call.setImplicitThis(false); + call.setMethodTarget(MAP_GET_METHOD); + call.setSafe(safe); + call.setSourcePosition(argument); + call.visit(controller.getAcg()); + return; + } } - // TODO: more cases + throw new GroovyBugError( "at line " + receiver.getLineNumber() + " column " + receiver.getColumnNumber() + "\n" + - "On receiver: " + receiver.getText() + " with message: " + message + " and arguments: " + arguments.getText() + "\n" + + "On receiver: " + receiver.getText() + " with message: " + message + " and arguments: " + argument.getText() + "\n" + "This method should not have been called. Please try to create a simple example reproducing " + "this error and file a bug report at https://issues.apache.org/jira/browse/GROOVY"); } - private boolean trySubscript(final Expression receiver, final String message, final Expression arguments, final ClassNode rType, final ClassNode aType, final boolean safe) { - if (getWrapper(rType).isDerivedFrom(Number_TYPE) - && getWrapper(aType).isDerivedFrom(Number_TYPE)) { - if ("plus".equals(message) || "minus".equals(message) || "multiply".equals(message) || "div".equals(message)) { - writeNumberNumberCall(receiver, message, arguments); - return true; - } else if ("power".equals(message)) { - writePowerCall(receiver, arguments, rType, aType); - return true; - } else if ("remainder".equals(message) || "leftShift".equals(message) - || "rightShift".equals(message) || "rightShiftUnsigned".equals(message) - || "and".equals(message) || "or".equals(message) || "xor".equals(message) || "implies".equals(message)) { - writeOperatorCall(receiver, arguments, message); - return true; - } - } else if (isStringType(rType) && "plus".equals(message)) { - writeStringPlusCall(receiver, message, arguments); - return true; - } else if ("getAt".equals(message)) { - if (rType.isArray() && getWrapper(aType).isDerivedFrom(Number_TYPE) && !safe) { - writeArrayGet(receiver, arguments, rType, aType); - return true; - } else { // check the receiver for a getAt method - MethodNode getAtNode = findGetAt(rType, aType); - if (getAtNode != null) { - MethodCallExpression call = callX(receiver, "getAt", arguments); - call.setImplicitThis(false); - call.setMethodTarget(getAtNode); - call.setSafe(safe); - call.setSourcePosition(arguments); - call.visit(controller.getAcg()); - return true; - } - if (isOrImplements(rType, MAP_TYPE)) { // fallback to Map#get - MethodCallExpression call = callX(receiver, "get", arguments); - call.setImplicitThis(false); - call.setMethodTarget(MAP_GET_METHOD); - call.setSafe(safe); - call.setSourcePosition(arguments); - call.visit(controller.getAcg()); - return true; - } - } - } - return false; - } - private MethodNode findGetAt(final ClassNode rType, final ClassNode aType) { // TODO: find "best" match or find all matches and deal with ambiguity // TODO: handle getAt with more than one parameter diff --git a/src/main/java/org/codehaus/groovy/classgen/asm/sc/StaticTypesTypeChooser.java b/src/main/java/org/codehaus/groovy/classgen/asm/sc/StaticTypesTypeChooser.java index 5f7750575f..4918ca9012 100644 --- a/src/main/java/org/codehaus/groovy/classgen/asm/sc/StaticTypesTypeChooser.java +++ b/src/main/java/org/codehaus/groovy/classgen/asm/sc/StaticTypesTypeChooser.java @@ -32,20 +32,23 @@ import static org.codehaus.groovy.ast.ClassHelper.isPrimitiveVoid; * type information from node metadata generated by the static type checker. */ public class StaticTypesTypeChooser extends StatementMetaTypeChooser { + @Override public ClassNode resolveType(final Expression exp, final ClassNode current) { - ASTNode target = getTarget(exp); // see GROOVY-9344, GROOVY-9607 - - ClassNode inferredType = target.getNodeMetaData(StaticTypesMarker.DECLARATION_INFERRED_TYPE); + ClassNode inferredType = exp.getNodeMetaData(StaticTypesMarker.DECLARATION_INFERRED_TYPE); + if (inferredType == null) { + inferredType = exp.getNodeMetaData(StaticTypesMarker.INFERRED_TYPE); + } if (inferredType == null) { - inferredType = target.getNodeMetaData(StaticTypesMarker.INFERRED_TYPE); + var ast = getTarget(exp); // GROOVY-9344, GROOVY-9607, GROOVY-11375 + inferredType = ast.getNodeMetaData(StaticTypesMarker.INFERRED_TYPE); } if (inferredType != null && !isPrimitiveVoid(inferredType)) { return inferredType; } - // AsmClassGenerator may create "this" expressions that the type checker knows nothing about - if (target instanceof VariableExpression && ((VariableExpression) target).isThisExpression()) { + // AsmClassGenerator creates "this" expressions that the type checker knows nothing about + if (exp instanceof VariableExpression && ((VariableExpression) exp).isThisExpression()) { return current; } diff --git a/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java b/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java index 49e9784380..59f97bd538 100644 --- a/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java +++ b/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java @@ -914,8 +914,6 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport { // check for implicit conversion like "String a = 123", "int[] b = [1,2,3]", "List c = [].stream()", etc. if (!implementsInterfaceOrIsSubclassOf(wrapTypeIfNecessary(resultType), wrapTypeIfNecessary(originType))) { resultType = originType; - } else if (isPrimitiveType(originType) && resultType.equals(getWrapper(originType))) { - resultType = originType; // retain primitive semantics } else { // GROOVY-7549: RHS type may not be accessible to enclosing class int modifiers = resultType.getModifiers(); @@ -4604,12 +4602,16 @@ trying: for (ClassNode[] signature : signatures) { if (op == EQUAL || op == ELVIS_EQUAL) { if (leftExpression instanceof VariableExpression) { ClassNode initialType = getOriginalDeclarationType(leftExpression); + if (isDynamicTyped(initialType)) { // GROOVY-11375 + ClassNode inferredType = leftExpression.getNodeMetaData(INFERRED_TYPE); + if (inferredType != null && !isPrimitiveType(inferredType)) initialType = OBJECT_TYPE; + } - if (isPrimitiveType(rightRedirect) && initialType.isDerivedFrom(Number_TYPE)) { + if (isPrimitiveType(rightRedirect) && (initialType.isDerivedFrom(Number_TYPE) || (isObjectType(initialType) && !isDynamicTyped(initialType)))) { return getWrapper(right); } - if (isPrimitiveType(initialType) && rightRedirect.isDerivedFrom(Number_TYPE)) { + if (isPrimitiveType(initialType) && (rightRedirect.isDerivedFrom(Number_TYPE) || rightRedirect == getWrapper(initialType))) { // GROOVY-6574 return getUnwrapper(right); } diff --git a/src/test/groovy/transform/stc/STCAssignmentTest.groovy b/src/test/groovy/transform/stc/STCAssignmentTest.groovy index 9aeabcfa4a..bfa56abf3c 100644 --- a/src/test/groovy/transform/stc/STCAssignmentTest.groovy +++ b/src/test/groovy/transform/stc/STCAssignmentTest.groovy @@ -358,7 +358,7 @@ class STCAssignmentTest extends StaticTypeCheckingTestCase { s = 1 ((Set) s) ''', - 'Inconvertible types: cannot cast int to java.util.Set' + 'Inconvertible types: cannot cast java.lang.Integer to java.util.Set' } void testArrayLength() { diff --git a/src/test/groovy/transform/stc/STCnAryExpressionTest.groovy b/src/test/groovy/transform/stc/STCnAryExpressionTest.groovy index c7d40be540..c4903fcef6 100644 --- a/src/test/groovy/transform/stc/STCnAryExpressionTest.groovy +++ b/src/test/groovy/transform/stc/STCnAryExpressionTest.groovy @@ -141,6 +141,26 @@ class STCnAryExpressionTest extends StaticTypeCheckingTestCase { ''' } + // GROOVY-11375 + void testShiftOnPrimitivesVariableFlowType() { + assertScript ''' + def x = "--" + x = x.size() + def y = x << x + assert y === 8 + ''' + } + + // GROOVY-11375 + void testPowerOnPrimitivesVariableFlowType() { + assertScript ''' + def x = "--" + x = x.size() + def y = x ** x + assert y === 4 + ''' + } + // GROOVY-5644 void testSpaceshipOperatorNoAmbiguousError() { assertScript '''