Repository: groovy Updated Branches: refs/heads/master 6f968d6c7 -> 1b7988218
GROOVY-8336: Static compilation requires casting inside instanceof check (additional cases) Project: http://git-wip-us.apache.org/repos/asf/groovy/repo Commit: http://git-wip-us.apache.org/repos/asf/groovy/commit/1b798821 Tree: http://git-wip-us.apache.org/repos/asf/groovy/tree/1b798821 Diff: http://git-wip-us.apache.org/repos/asf/groovy/diff/1b798821 Branch: refs/heads/master Commit: 1b79882184484086220516b182c56a6c78e56ffd Parents: 6f968d6 Author: paulk <pa...@asert.com.au> Authored: Sun Oct 1 16:53:25 2017 +1000 Committer: paulk <pa...@asert.com.au> Committed: Sun Oct 1 16:53:25 2017 +1000 ---------------------------------------------------------------------- .../asm/sc/StaticTypesCallSiteWriter.java | 2 +- .../stc/StaticTypeCheckingVisitor.java | 55 +++++++++++-- .../groovy/transform/stc/MiscSTCTest.groovy | 83 +++++++++++++++++++- 3 files changed, 127 insertions(+), 13 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/groovy/blob/1b798821/src/main/org/codehaus/groovy/classgen/asm/sc/StaticTypesCallSiteWriter.java ---------------------------------------------------------------------- diff --git a/src/main/org/codehaus/groovy/classgen/asm/sc/StaticTypesCallSiteWriter.java b/src/main/org/codehaus/groovy/classgen/asm/sc/StaticTypesCallSiteWriter.java index d558f9d..8d003eb 100644 --- a/src/main/org/codehaus/groovy/classgen/asm/sc/StaticTypesCallSiteWriter.java +++ b/src/main/org/codehaus/groovy/classgen/asm/sc/StaticTypesCallSiteWriter.java @@ -629,7 +629,7 @@ public class StaticTypesCallSiteWriter extends CallSiteWriter implements Opcodes } // now try with flow type instead of declaration type rType = receiver.getNodeMetaData(StaticTypesMarker.INFERRED_TYPE); - if (receiver instanceof VariableExpression && receiver.getNodeMetaData().isEmpty()) { + if (receiver instanceof VariableExpression && rType == null) { // TODO: can STCV be made smarter to avoid this check? VariableExpression ve = (VariableExpression) ((VariableExpression)receiver).getAccessedVariable(); rType = ve.getNodeMetaData(StaticTypesMarker.INFERRED_TYPE); http://git-wip-us.apache.org/repos/asf/groovy/blob/1b798821/src/main/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java ---------------------------------------------------------------------- diff --git a/src/main/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java b/src/main/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java index 807449a..39e2114 100644 --- a/src/main/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java +++ b/src/main/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java @@ -480,7 +480,24 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport { } } - if (! (vexp.getAccessedVariable() instanceof DynamicVariable)) return; + if (!(vexp.getAccessedVariable() instanceof DynamicVariable)) { + if (typeCheckingContext.getEnclosingClosure() == null) { + VariableExpression variable = null; + if (vexp.getAccessedVariable() instanceof Parameter) { + variable = new ParameterVariableExpression((Parameter) vexp.getAccessedVariable()); + } else if (vexp.getAccessedVariable() instanceof VariableExpression) { + variable = (VariableExpression) vexp.getAccessedVariable(); + } + if (variable != null) { + ClassNode inferredType = getInferredTypeFromTempInfo(variable, variable.getNodeMetaData(StaticTypesMarker.INFERRED_TYPE)); + // instanceof applies, stash away the type, reusing key used elsewhere + if (inferredType != null && !inferredType.getName().equals("java.lang.Object")) { + vexp.putNodeMetaData(StaticTypesMarker.INFERRED_RETURN_TYPE, inferredType); + } + } + } + return; + } // a dynamic variable is either an undeclared variable // or a member of a class used in a 'with' @@ -1005,9 +1022,9 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport { final Expression leftExpression, final ClassNode leftExpressionType, final Expression rightExpression, - final ClassNode inferredRightExpressionType) + final ClassNode inferredRightExpressionTypeOrig) { - + ClassNode inferredRightExpressionType = inferredRightExpressionTypeOrig; if (!typeCheckMultipleAssignmentAndContinue(leftExpression, rightExpression)) return; if (leftExpression instanceof VariableExpression @@ -1019,6 +1036,10 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport { if (addedReadOnlyPropertyError(leftExpression)) return; ClassNode leftRedirect = leftExpressionType.redirect(); + // see if instanceof applies + if (rightExpression instanceof VariableExpression && hasInferredReturnType(rightExpression) && assignmentExpression.getOperation().getType() == EQUAL) { + inferredRightExpressionType = rightExpression.getNodeMetaData(StaticTypesMarker.INFERRED_RETURN_TYPE); + } ClassNode wrappedRHS = adjustTypeForSpreading(inferredRightExpressionType, leftExpression); // check types are compatible for assignment @@ -1833,6 +1854,10 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport { if (typeCheckingContext.getEnclosingClosure()!=null) { return type; } + // handle instanceof cases + if ((expression instanceof VariableExpression) && hasInferredReturnType(expression)) { + type = expression.getNodeMetaData(StaticTypesMarker.INFERRED_RETURN_TYPE); + } MethodNode enclosingMethod = typeCheckingContext.getEnclosingMethod(); if (enclosingMethod != null && typeCheckingContext.getEnclosingClosure()==null) { if (!enclosingMethod.isVoidMethod() @@ -3205,7 +3230,9 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport { int depth = typeCheckingContext.temporaryIfBranchTypeInformation.size(); while (classNodes == null && depth > 0) { final Map<Object, List<ClassNode>> tempo = typeCheckingContext.temporaryIfBranchTypeInformation.get(--depth); - Object key = extractTemporaryTypeInfoKey(objectExpression); + Object key = objectExpression instanceof ParameterVariableExpression + ? ((ParameterVariableExpression) objectExpression).parameter + : extractTemporaryTypeInfoKey(objectExpression); classNodes = tempo.get(key); } return classNodes; @@ -3393,6 +3420,15 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport { typeCheckingContext.popTemporaryTypeInfo(); falseExpression.visit(this); ClassNode resultType; + ClassNode typeOfFalse = getType(falseExpression); + ClassNode typeOfTrue = getType(trueExpression); + // handle instanceof cases + if (hasInferredReturnType(falseExpression)) { + typeOfFalse = falseExpression.getNodeMetaData(StaticTypesMarker.INFERRED_RETURN_TYPE); + } + if (hasInferredReturnType(trueExpression)) { + typeOfTrue = trueExpression.getNodeMetaData(StaticTypesMarker.INFERRED_RETURN_TYPE); + } if (isNullConstant(trueExpression) || isNullConstant(falseExpression)) { BinaryExpression enclosingBinaryExpression = typeCheckingContext.getEnclosingBinaryExpression(); if (enclosingBinaryExpression != null && enclosingBinaryExpression.getRightExpression()==expression) { @@ -3400,20 +3436,23 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport { } else if (isNullConstant(trueExpression) && isNullConstant(falseExpression)) { resultType = OBJECT_TYPE; } else if (isNullConstant(trueExpression)) { - resultType = wrapTypeIfNecessary(getType(falseExpression)); + resultType = wrapTypeIfNecessary(typeOfFalse); } else { - resultType = wrapTypeIfNecessary(getType(trueExpression)); + resultType = wrapTypeIfNecessary(typeOfTrue); } } else { // store type information - final ClassNode typeOfTrue = getType(trueExpression); - final ClassNode typeOfFalse = getType(falseExpression); resultType = lowestUpperBound(typeOfTrue, typeOfFalse); } storeType(expression, resultType); popAssignmentTracking(oldTracker); } + private boolean hasInferredReturnType(Expression expression) { + ClassNode type = expression.getNodeMetaData(StaticTypesMarker.INFERRED_RETURN_TYPE); + return type != null && !type.getName().equals("java.lang.Object"); + } + @Override public void visitTryCatchFinally(final TryCatchStatement statement) { final List<CatchStatement> catchStatements = statement.getCatchStatements(); http://git-wip-us.apache.org/repos/asf/groovy/blob/1b798821/src/test/groovy/transform/stc/MiscSTCTest.groovy ---------------------------------------------------------------------- diff --git a/src/test/groovy/transform/stc/MiscSTCTest.groovy b/src/test/groovy/transform/stc/MiscSTCTest.groovy index 13bdb40..94d8c0e 100644 --- a/src/test/groovy/transform/stc/MiscSTCTest.groovy +++ b/src/test/groovy/transform/stc/MiscSTCTest.groovy @@ -22,8 +22,6 @@ import org.codehaus.groovy.transform.stc.StaticTypeCheckingVisitor /** * Unit tests for static type checking : miscellaneous tests. - * - * @author Cedric Champeau */ class MiscSTCTest extends StaticTypeCheckingTestCase { @@ -272,8 +270,85 @@ class MiscSTCTest extends StaticTypeCheckingTestCase { } } - public static class MiscSTCTestSupport { + static class MiscSTCTestSupport { static def foo() { '123' } } -} + void testTernaryParam() { + assertScript ''' + Date ternaryParam(Object input) { + input instanceof Date ? input : null + } + def d = new Date() + assert ternaryParam(42) == null + assert ternaryParam('foo') == null + assert ternaryParam(d) == d + ''' + } + + void testTernaryLocalVar() { + assertScript ''' + Date ternaryLocalVar(Object input) { + Object copy = input + copy instanceof Date ? copy : null + } + def d = new Date() + assert ternaryLocalVar(42) == null + assert ternaryLocalVar('foo') == null + assert ternaryLocalVar(d) == d + ''' + } + + void testIfThenElseParam() { + assertScript ''' + Date ifThenElseParam(Object input) { + if (input instanceof Date) { + input + } else { + null + } + } + def d = new Date() + assert ifThenElseParam(42) == null + assert ifThenElseParam('foo') == null + assert ifThenElseParam(d) == d + ''' + } + + void testIfThenElseLocalVar() { + assertScript ''' + Date ifThenElseLocalVar(Object input) { + Date result + if (input instanceof Date) { + result = input + } else { + result = null + } + result + } + def d = new Date() + assert ifThenElseLocalVar(42) == null + assert ifThenElseLocalVar('foo') == null + assert ifThenElseLocalVar(d) == d + ''' + } + + void testIfThenElseLocalVar2() { + assertScript ''' + class FooBase {} + class FooChild extends FooBase{} + FooChild ifThenElseLocalVar2(FooBase input) { + FooChild result + if (input instanceof FooChild) { + result = input + } else { + result = null + } + result + } + def fc = new FooChild() + assert ifThenElseLocalVar2(fc) == fc + assert ifThenElseLocalVar2(new FooBase()) == null + ''' + } +}