From 8772985e61314881f28083b7533864658fa5f445 Mon Sep 17 00:00:00 2001 From: Baorui Zhou Date: Wed, 20 Mar 2019 11:57:24 -0400 Subject: [PATCH 001/144] adapt pico to CF and CFI master --- src/main/java/pico/inference/PICOInferenceVisitor.java | 6 ++++-- .../java/pico/inference/PICOVariableAnnotator.java | 10 +++++----- .../java/pico/inference/solver/PICOSolverEngine.java | 4 ++-- 3 files changed, 11 insertions(+), 9 deletions(-) diff --git a/src/main/java/pico/inference/PICOInferenceVisitor.java b/src/main/java/pico/inference/PICOInferenceVisitor.java index 0a0d390..b851714 100644 --- a/src/main/java/pico/inference/PICOInferenceVisitor.java +++ b/src/main/java/pico/inference/PICOInferenceVisitor.java @@ -27,6 +27,7 @@ import org.checkerframework.framework.type.AnnotatedTypeMirror; import org.checkerframework.framework.type.AnnotatedTypeMirror.AnnotatedDeclaredType; import org.checkerframework.framework.type.AnnotatedTypeMirror.AnnotatedExecutableType; +import org.checkerframework.framework.type.QualifierHierarchy; import org.checkerframework.framework.util.AnnotatedTypes; import org.checkerframework.javacutil.AnnotationUtils; import org.checkerframework.javacutil.BugInCF; @@ -418,7 +419,7 @@ private boolean isCompatibleCastInInfer(AnnotatedTypeMirror castType, AnnotatedT return true; } else { // Default strategy - comparablecast - final ConstraintManager constraintManager = InferenceMain.getInstance().getConstraintManager(); + final QualifierHierarchy qualHierarchy = InferenceMain.getInstance().getRealTypeFactory().getQualifierHierarchy(); final SlotManager slotManager = InferenceMain.getInstance().getSlotManager(); final Slot castSlot = slotManager.getVariableSlot(castType); final Slot exprSlot = slotManager.getVariableSlot(exprType); @@ -429,7 +430,8 @@ private boolean isCompatibleCastInInfer(AnnotatedTypeMirror castType, AnnotatedT // Special handling for case with two ConstantSlots: even though they may not be comparable, // but to infer more program, let this case fall back to "anycast" silently and continue // inference. - return constraintManager.getConstraintVerifier().areComparable(castCSSlot, exprCSSlot); + return qualHierarchy.isSubtype(castCSSlot.getValue(), exprCSSlot.getValue()) + || qualHierarchy.isSubtype(exprCSSlot.getValue(), castCSSlot.getValue()); } else { // But if there is at least on VariableSlot, PICOInfer guarantees that solutions don't include // incomparable casts. diff --git a/src/main/java/pico/inference/PICOVariableAnnotator.java b/src/main/java/pico/inference/PICOVariableAnnotator.java index 65893b2..3971845 100644 --- a/src/main/java/pico/inference/PICOVariableAnnotator.java +++ b/src/main/java/pico/inference/PICOVariableAnnotator.java @@ -69,7 +69,7 @@ protected void handleClassDeclarationBound(AnnotatedDeclaredType classType) { // Insert @Immutable VarAnnot directly to enum bound if (PICOTypeUtil.isEnumOrEnumConstant(bound)) { - boundSlot = createConstant(IMMUTABLE); + boundSlot = slotManager.createConstantSlot(IMMUTABLE); classType.addAnnotation(slotManager.getAnnotation(boundSlot)); classDeclAnnos.put(classElement, boundSlot); return; @@ -80,7 +80,7 @@ protected void handleClassDeclarationBound(AnnotatedDeclaredType classType) { // Have source tree if (bound.isAnnotatedInHierarchy(READONLY)) { // Have bound annotation -> convert to equivalent ConstantSlot - boundSlot = createConstant(bound.getAnnotationInHierarchy(READONLY)); + boundSlot = slotManager.createConstantSlot(bound.getAnnotationInHierarchy(READONLY)); } else { // No existing annotation -> create new VariableSlot boundSlot = createVariable(treeToLocation(classTree)); @@ -89,15 +89,15 @@ protected void handleClassDeclarationBound(AnnotatedDeclaredType classType) { // No source tree: bytecode classes if (bound.isAnnotatedInHierarchy(READONLY)) { // Have bound annotation in stub file - boundSlot = createConstant(bound.getAnnotationInHierarchy(READONLY)); + boundSlot = slotManager.createConstantSlot(bound.getAnnotationInHierarchy(READONLY)); } else { // No stub file if (PICOTypeUtil.isImplicitlyImmutableType(classType)) { // Implicitly immutable - boundSlot = createConstant(IMMUTABLE); + boundSlot = slotManager.createConstantSlot(IMMUTABLE); } else { // None of the above applies: use conservative @Mutable - boundSlot = createConstant(MUTABLE); + boundSlot = slotManager.createConstantSlot(MUTABLE); } } } diff --git a/src/main/java/pico/inference/solver/PICOSolverEngine.java b/src/main/java/pico/inference/solver/PICOSolverEngine.java index 213a809..8fb26bc 100644 --- a/src/main/java/pico/inference/solver/PICOSolverEngine.java +++ b/src/main/java/pico/inference/solver/PICOSolverEngine.java @@ -1,6 +1,6 @@ package pico.inference.solver; -import checkers.inference.BaseInferenceResult; +import checkers.inference.DefaultInferenceResult; import checkers.inference.InferenceResult; import checkers.inference.model.Constraint; import checkers.inference.model.Slot; @@ -28,7 +28,7 @@ public class PICOSolverEngine extends SolverEngine { public InferenceResult solve(Map configuration, Collection slots, Collection constraints, QualifierHierarchy qualHierarchy, ProcessingEnvironment processingEnvironment) { InferenceResult result= super.solve(configuration, slots, constraints, qualHierarchy, processingEnvironment); if (collectStatistics && result.hasSolution()) { - writeInferenceResult("pico-inference-result.txt", ((BaseInferenceResult)result).inferredResults); + writeInferenceResult("pico-inference-result.txt", ((DefaultInferenceResult)result).varIdToAnnotation); } return result; } From c8e412ffe671565bfab3872fab43b787642e8dea Mon Sep 17 00:00:00 2001 From: Baorui Zhou Date: Thu, 28 Mar 2019 14:26:09 -0400 Subject: [PATCH 002/144] fix pico per changes in upstream CF and CFI --- .../inference/PICOInferenceRealTypeFactory.java | 4 +++- .../pico/inference/PICOInferenceVisitor.java | 16 +++------------- .../pico/typecheck/PICOAnnotatedTypeFactory.java | 10 ++++++---- src/main/java/pico/typecheck/PICOVisitor.java | 8 +++----- 4 files changed, 15 insertions(+), 23 deletions(-) diff --git a/src/main/java/pico/inference/PICOInferenceRealTypeFactory.java b/src/main/java/pico/inference/PICOInferenceRealTypeFactory.java index 1157e2d..3c247ea 100644 --- a/src/main/java/pico/inference/PICOInferenceRealTypeFactory.java +++ b/src/main/java/pico/inference/PICOInferenceRealTypeFactory.java @@ -55,7 +55,9 @@ public class PICOInferenceRealTypeFactory extends BaseAnnotatedTypeFactory { public PICOInferenceRealTypeFactory(BaseTypeChecker checker, boolean useFlow) { super(checker, useFlow); - addAliasedAnnotation(org.jmlspecs.annotation.Readonly.class, READONLY); + if (READONLY != null) { + addAliasedAnnotation(org.jmlspecs.annotation.Readonly.class, READONLY); + } postInit(); } diff --git a/src/main/java/pico/inference/PICOInferenceVisitor.java b/src/main/java/pico/inference/PICOInferenceVisitor.java index b851714..c8bb53c 100644 --- a/src/main/java/pico/inference/PICOInferenceVisitor.java +++ b/src/main/java/pico/inference/PICOInferenceVisitor.java @@ -23,7 +23,7 @@ import org.checkerframework.common.basetype.BaseAnnotatedTypeFactory; import org.checkerframework.framework.source.Result; -import org.checkerframework.framework.type.AnnotatedTypeFactory.ParameterizedMethodType; +import org.checkerframework.framework.type.AnnotatedTypeFactory.ParameterizedExecutableType; import org.checkerframework.framework.type.AnnotatedTypeMirror; import org.checkerframework.framework.type.AnnotatedTypeMirror.AnnotatedDeclaredType; import org.checkerframework.framework.type.AnnotatedTypeMirror.AnnotatedExecutableType; @@ -167,14 +167,6 @@ public boolean validateTypeOf(Tree tree) { // TODO This might not be correct for infer mode. Maybe returning as it is @Override public boolean validateType(Tree tree, AnnotatedTypeMirror type) { - // basic consistency checks - if (!AnnotatedTypes.isValidType(atypeFactory.getQualifierHierarchy(), type)) { - if (!infer) { - checker.report( - Result.failure("type.invalid", type.getAnnotations(), type.toString()), tree); - return false; - } - } if (!typeValidator.isValid(type, tree)) { if (!infer) { @@ -622,9 +614,9 @@ private void checkNewInstanceCreation(Tree node) { @Override public Void visitMethodInvocation(MethodInvocationTree node, Void p) { super.visitMethodInvocation(node, p); - ParameterizedMethodType mfuPair = + ParameterizedExecutableType mfuPair = atypeFactory.methodFromUse(node); - AnnotatedExecutableType invokedMethod = mfuPair.methodType; + AnnotatedExecutableType invokedMethod = mfuPair.executableType; ExecutableElement invokedMethodElement = invokedMethod.getElement(); // Only check invocability if it's super call, as non-super call is already checked // by super implementation(of course in both cases, invocability is not checked when @@ -819,8 +811,6 @@ protected void commonAssignmentCheck( return; } - checkAssignability(var, varTree); - if (varTree instanceof VariableTree) { VariableElement element = TreeUtils.elementFromDeclaration((VariableTree) varTree); if (element.getKind() == ElementKind.FIELD && !ElementUtils.isStatic(element)) { diff --git a/src/main/java/pico/typecheck/PICOAnnotatedTypeFactory.java b/src/main/java/pico/typecheck/PICOAnnotatedTypeFactory.java index 77bef15..406a935 100644 --- a/src/main/java/pico/typecheck/PICOAnnotatedTypeFactory.java +++ b/src/main/java/pico/typecheck/PICOAnnotatedTypeFactory.java @@ -84,7 +84,9 @@ public class PICOAnnotatedTypeFactory extends InitializationAnnotatedTypeFactory public PICOAnnotatedTypeFactory(BaseTypeChecker checker) { super(checker, true); postInit(); - addAliasedAnnotation(org.jmlspecs.annotation.Readonly.class, READONLY); + if (READONLY != null) { + addAliasedAnnotation(org.jmlspecs.annotation.Readonly.class, READONLY); + } } @Override @@ -240,11 +242,11 @@ public AnnotatedTypeMirror getAnnotatedTypeLhs(Tree lhsTree) { /**Handles invoking static methods with polymutable on its declaration*/ @Override - public ParameterizedMethodType methodFromUse(ExpressionTree tree, ExecutableElement methodElt, AnnotatedTypeMirror receiverType) { - ParameterizedMethodType pair = super.methodFromUse(tree, methodElt, receiverType); + public ParameterizedExecutableType methodFromUse(ExpressionTree tree, ExecutableElement methodElt, AnnotatedTypeMirror receiverType) { + ParameterizedExecutableType pair = super.methodFromUse(tree, methodElt, receiverType); // We want to replace polymutable with substitutablepolymutable when we invoke static methods if (ElementUtils.isStatic(methodElt)) { - AnnotatedExecutableType methodType = pair.methodType; + AnnotatedExecutableType methodType = pair.executableType; AnnotatedTypeMirror returnType = methodType.getReturnType(); if (returnType.hasAnnotation(POLY_MUTABLE)) { // Only substitute polymutable but not other qualifiers! Missing the if statement diff --git a/src/main/java/pico/typecheck/PICOVisitor.java b/src/main/java/pico/typecheck/PICOVisitor.java index 5249ae4..5696178 100644 --- a/src/main/java/pico/typecheck/PICOVisitor.java +++ b/src/main/java/pico/typecheck/PICOVisitor.java @@ -26,7 +26,7 @@ import org.checkerframework.common.basetype.BaseTypeChecker; import org.checkerframework.common.basetype.TypeValidator; import org.checkerframework.framework.source.Result; -import org.checkerframework.framework.type.AnnotatedTypeFactory.ParameterizedMethodType; +import org.checkerframework.framework.type.AnnotatedTypeFactory.ParameterizedExecutableType; import org.checkerframework.framework.type.AnnotatedTypeMirror; import org.checkerframework.framework.type.AnnotatedTypeMirror.AnnotatedDeclaredType; import org.checkerframework.framework.type.AnnotatedTypeMirror.AnnotatedExecutableType; @@ -131,8 +131,6 @@ protected void commonAssignmentCheck( return; } - checkAssignability(var, varTree); - if (varTree instanceof VariableTree) { VariableElement element = TreeUtils.elementFromDeclaration((VariableTree) varTree); if (element.getKind() == ElementKind.FIELD && !ElementUtils.isStatic(element)) { @@ -382,9 +380,9 @@ private void checkNewInstanceCreation(Tree node) { @Override public Void visitMethodInvocation(MethodInvocationTree node, Void p) { super.visitMethodInvocation(node, p); - ParameterizedMethodType mfuPair = + ParameterizedExecutableType mfuPair = atypeFactory.methodFromUse(node); - AnnotatedExecutableType invokedMethod = mfuPair.methodType; + AnnotatedExecutableType invokedMethod = mfuPair.executableType; ExecutableElement invokedMethodElement = invokedMethod.getElement(); // Only check invocability if it's super call, as non-super call is already checked // by super implementation(of course in both cases, invocability is not checked when From 9e279221d1fbb7953230db67f60053f73355a56d Mon Sep 17 00:00:00 2001 From: Baorui Zhou Date: Tue, 2 Apr 2019 12:11:02 -0400 Subject: [PATCH 003/144] remove incorrect aliasing and unnecessary method check --- .../typecheck/PICOAnnotatedTypeFactory.java | 6 +-- src/main/java/pico/typecheck/PICOVisitor.java | 48 ------------------- 2 files changed, 3 insertions(+), 51 deletions(-) diff --git a/src/main/java/pico/typecheck/PICOAnnotatedTypeFactory.java b/src/main/java/pico/typecheck/PICOAnnotatedTypeFactory.java index 406a935..3a43805 100644 --- a/src/main/java/pico/typecheck/PICOAnnotatedTypeFactory.java +++ b/src/main/java/pico/typecheck/PICOAnnotatedTypeFactory.java @@ -84,9 +84,9 @@ public class PICOAnnotatedTypeFactory extends InitializationAnnotatedTypeFactory public PICOAnnotatedTypeFactory(BaseTypeChecker checker) { super(checker, true); postInit(); - if (READONLY != null) { - addAliasedAnnotation(org.jmlspecs.annotation.Readonly.class, READONLY); - } + // PICO aliasing is not implemented correctly + // remove for now +// addAliasedAnnotation(org.jmlspecs.annotation.Readonly.class, READONLY); } @Override diff --git a/src/main/java/pico/typecheck/PICOVisitor.java b/src/main/java/pico/typecheck/PICOVisitor.java index 5696178..e0dbd13 100644 --- a/src/main/java/pico/typecheck/PICOVisitor.java +++ b/src/main/java/pico/typecheck/PICOVisitor.java @@ -393,54 +393,6 @@ public Void visitMethodInvocation(MethodInvocationTree node, Void p) { return null; } - // TODO Find a better way to inject saveFbcViolatedMethods instead of copying lots of code from super method - @Override - protected void checkMethodInvocability(AnnotatedExecutableType method, MethodInvocationTree node) { - // Check subclass constructor calls the correct super class constructor: mutable calls mutable; immutable - // calls immutable; any calls receiverdependantmutable - if (method.getElement().getKind() == ElementKind.CONSTRUCTOR) { - AnnotatedTypeMirror subClassConstructorReturnType = atypeFactory.getReceiverType(node); - AnnotatedTypeMirror superClassConstructorReturnType = method.getReturnType(); - // superClassConstructorReturnType is already the result of viewpoint adaptation, so subClassConstructorReturnType <: - // superClassConstructorReturnType is enough to determine the super constructor invocation is valid or not - if (!atypeFactory.getTypeHierarchy().isSubtype(subClassConstructorReturnType, superClassConstructorReturnType, READONLY)) { - checker.report( - Result.failure( - "super.constructor.invocation.incompatible", subClassConstructorReturnType, superClassConstructorReturnType), node); - } - return; - } - - /*Copied Code Starts*/ - if (method.getReceiverType() == null) { - // Static methods don't have a receiver. - return; - } - - AnnotatedTypeMirror methodReceiver = method.getReceiverType().getErased(); - AnnotatedTypeMirror treeReceiver = methodReceiver.shallowCopy(false); - AnnotatedTypeMirror rcv = atypeFactory.getReceiverType(node); - - treeReceiver.addAnnotations(rcv.getEffectiveAnnotations()); - - if (!skipReceiverSubtypeCheck(node, methodReceiver, rcv) - && !atypeFactory.getTypeHierarchy().isSubtype(treeReceiver, methodReceiver)) { - checker.report( - Result.failure( - "method.invocation.invalid", - TreeUtils.elementFromUse(node), - treeReceiver.toString(), - methodReceiver.toString()), - node); - /*Difference Starts*/ - if (shouldOutputFbcError) { - saveFbcViolatedMethods(TreeUtils.elementFromUse(node), treeReceiver.toString(), methodReceiver.toString()); - } - /*Different Ends*/ - } - /*Copied Code Ends*/ - } - private void saveFbcViolatedMethods(ExecutableElement method, String actualReceiver, String declaredReceiver) { if (actualReceiver.contains("@UnderInitialization") && declaredReceiver.contains("@Initialized")) { String key = ElementUtils.enclosingClass(method) + "#" + method; From 0298a5fd6a4bec5beca4fb2c46972b2c5625a038 Mon Sep 17 00:00:00 2001 From: Baorui Zhou Date: Thu, 18 Apr 2019 18:22:29 -0400 Subject: [PATCH 004/144] remove wrong test diagnostic --- testinput/typecheck/DiamondTreeProblem.java | 3 --- 1 file changed, 3 deletions(-) diff --git a/testinput/typecheck/DiamondTreeProblem.java b/testinput/typecheck/DiamondTreeProblem.java index 206acf3..f2a4a10 100644 --- a/testinput/typecheck/DiamondTreeProblem.java +++ b/testinput/typecheck/DiamondTreeProblem.java @@ -6,9 +6,6 @@ public class DiamondTreeProblem { void test1() { - // TODO This is WRONG even though test passed! Explicit @Immutable annotation - // on new instance creation is ignored and @Mutable is defaulted! - // :: error: (assignment.type.incompatible) @Immutable List l = new @Immutable ArrayList<>(); } From 5ddc6aec87f0579d66d2438f3035006dd72561f2 Mon Sep 17 00:00:00 2001 From: Baorui Zhou Date: Thu, 18 Apr 2019 18:26:34 -0400 Subject: [PATCH 005/144] adapt to upstream changes --- src/main/java/pico/inference/PICOInferenceVisitor.java | 6 +++--- src/main/java/pico/typecheck/PICOVisitor.java | 4 +--- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/src/main/java/pico/inference/PICOInferenceVisitor.java b/src/main/java/pico/inference/PICOInferenceVisitor.java index c8bb53c..83dedd6 100644 --- a/src/main/java/pico/inference/PICOInferenceVisitor.java +++ b/src/main/java/pico/inference/PICOInferenceVisitor.java @@ -185,7 +185,7 @@ public boolean validateType(Tree tree, AnnotatedTypeMirror type) { } @Override - protected boolean checkConstructorInvocation(AnnotatedDeclaredType invocation, AnnotatedExecutableType constructor, NewClassTree newClassTree) { + protected void checkConstructorInvocation(AnnotatedDeclaredType invocation, AnnotatedExecutableType constructor, NewClassTree newClassTree) { if (infer) { AnnotationMirror constructorReturn = extractVarAnnot(constructor.getReturnType()); mainIsSubtype(invocation, constructorReturn, "constructor.invocation.invalid", newClassTree); @@ -194,10 +194,10 @@ protected boolean checkConstructorInvocation(AnnotatedDeclaredType invocation, A if (!atypeFactory.getTypeHierarchy().isSubtype(invocation, returnType)) { checker.report(Result.failure( "constructor.invocation.invalid", invocation, returnType), newClassTree); - return false; + return; } } - return super.checkConstructorInvocation(invocation, constructor, newClassTree); + super.checkConstructorInvocation(invocation, constructor, newClassTree); } private AnnotationMirror extractVarAnnot(final AnnotatedTypeMirror atm) { diff --git a/src/main/java/pico/typecheck/PICOVisitor.java b/src/main/java/pico/typecheck/PICOVisitor.java index e0dbd13..4587458 100644 --- a/src/main/java/pico/typecheck/PICOVisitor.java +++ b/src/main/java/pico/typecheck/PICOVisitor.java @@ -154,7 +154,7 @@ protected void commonAssignmentCheck( } @Override - protected boolean checkConstructorInvocation(AnnotatedDeclaredType invocation, AnnotatedExecutableType constructor, NewClassTree newClassTree) { + protected void checkConstructorInvocation(AnnotatedDeclaredType invocation, AnnotatedExecutableType constructor, NewClassTree newClassTree) { // TODO Is the copied code really needed? /*Copied Code Start*/ AnnotatedDeclaredType returnType = (AnnotatedDeclaredType) constructor.getReturnType(); @@ -181,9 +181,7 @@ protected boolean checkConstructorInvocation(AnnotatedDeclaredType invocation, A if (!atypeFactory.getTypeHierarchy().isSubtype(invocation, returnType, READONLY)) { checker.report(Result.failure( "constructor.invocation.invalid", invocation, returnType), newClassTree); - return false; } - return true; } @Override From c4e9ed47b964061d4e1b8da5c015f841aa822dee Mon Sep 17 00:00:00 2001 From: Baorui Zhou Date: Wed, 24 Apr 2019 12:39:19 -0400 Subject: [PATCH 006/144] fix extends and implements check --- .../typecheck/PICOAnnotatedTypeFactory.java | 29 ------ .../java/pico/typecheck/PICOTypeUtil.java | 29 ------ src/main/java/pico/typecheck/PICOVisitor.java | 92 ++++++++----------- testinput/typecheck/CompatabilityBEI1.java | 8 +- testinput/typecheck/CompatabilityBEI2.java | 12 +-- .../ReceiverTypeOutsideConstructor.java | 10 +- 6 files changed, 53 insertions(+), 127 deletions(-) diff --git a/src/main/java/pico/typecheck/PICOAnnotatedTypeFactory.java b/src/main/java/pico/typecheck/PICOAnnotatedTypeFactory.java index 3a43805..db6dd28 100644 --- a/src/main/java/pico/typecheck/PICOAnnotatedTypeFactory.java +++ b/src/main/java/pico/typecheck/PICOAnnotatedTypeFactory.java @@ -50,13 +50,9 @@ import org.checkerframework.javacutil.TreeUtils; import com.sun.source.tree.BinaryTree; -import com.sun.source.tree.ClassTree; import com.sun.source.tree.ExpressionTree; -import com.sun.source.tree.IdentifierTree; -import com.sun.source.tree.MemberSelectTree; import com.sun.source.tree.MethodTree; import com.sun.source.tree.NewArrayTree; -import com.sun.source.tree.ParameterizedTypeTree; import com.sun.source.tree.Tree; import com.sun.source.tree.TypeCastTree; import com.sun.source.tree.UnaryTree; @@ -415,31 +411,6 @@ public PICOTreeAnnotator(AnnotatedTypeFactory atypeFactory) { super(atypeFactory); } - @Override - public Void visitIdentifier(IdentifierTree node, AnnotatedTypeMirror annotatedTypeMirror) { - PICOTypeUtil.dragAnnotationFromBoundToExtendsAndImplements(node, annotatedTypeMirror, atypeFactory); - return super.visitIdentifier(node, annotatedTypeMirror); - } - - @Override - public Void visitMemberSelect(MemberSelectTree node, AnnotatedTypeMirror annotatedTypeMirror) { - PICOTypeUtil.dragAnnotationFromBoundToExtendsAndImplements(node, annotatedTypeMirror, atypeFactory); - return super.visitMemberSelect(node, annotatedTypeMirror); - } - - @Override - public Void visitParameterizedType(ParameterizedTypeTree node, AnnotatedTypeMirror annotatedTypeMirror) { - PICOTypeUtil.dragAnnotationFromBoundToExtendsAndImplements(node, annotatedTypeMirror, atypeFactory); - return super.visitParameterizedType(node, annotatedTypeMirror); - } - - @Override - public Void visitClass(ClassTree node, AnnotatedTypeMirror annotatedTypeMirror) { - // Apply @Immutable to enum element's bound - PICOTypeUtil.applyImmutableToEnumAndEnumConstant(annotatedTypeMirror); - return super.visitClass(node, annotatedTypeMirror); - } - // This adds @Immutable annotation to constructor return type if type declaration has @Immutable when the // constructor is accessed as a tree. @Override diff --git a/src/main/java/pico/typecheck/PICOTypeUtil.java b/src/main/java/pico/typecheck/PICOTypeUtil.java index abaff17..8d32462 100644 --- a/src/main/java/pico/typecheck/PICOTypeUtil.java +++ b/src/main/java/pico/typecheck/PICOTypeUtil.java @@ -6,7 +6,6 @@ import checkers.inference.model.ConstraintManager; import checkers.inference.model.Slot; import checkers.inference.util.InferenceUtil; -import com.sun.source.tree.AssignmentTree; import com.sun.source.tree.ClassTree; import com.sun.source.tree.ExpressionTree; import com.sun.source.tree.MethodTree; @@ -399,34 +398,6 @@ public static boolean inStaticScope(TreePath treePath) { return in; } - public static void dragAnnotationFromBoundToExtendsAndImplements(Tree node, - AnnotatedTypeMirror annotatedTypeMirror, - AnnotatedTypeFactory atypeFactory) { - boolean onExtendsOrImplements = false; - ClassTree classTree = null; - TreePath path = atypeFactory.getPath(node); - if (path != null) { - final TreePath parentPath = path.getParentPath(); - if (parentPath != null) { - final Tree parentNode = parentPath.getLeaf(); - if (TreeUtils.isClassTree(parentNode)) { - onExtendsOrImplements = true; - classTree = (ClassTree) parentNode; - } - } - } - - if (onExtendsOrImplements) { - // Respect explicitly written annotation still. However, if the there is annotation here, but it's - // inheritted from type element bound, then we still flush them out, because they are not explicit - // usage. - if (annotatedTypeMirror.getExplicitAnnotations().isEmpty()) { - annotatedTypeMirror.replaceAnnotation( - getBoundTypeOfTypeDeclaration(classTree, atypeFactory).getAnnotationInHierarchy(READONLY)); - } - } - } - public static boolean isSideEffectingUnaryTree(final UnaryTree tree) { return sideEffectingUnaryOperators.contains(tree.getKind()); } diff --git a/src/main/java/pico/typecheck/PICOVisitor.java b/src/main/java/pico/typecheck/PICOVisitor.java index 4587458..45ec4d1 100644 --- a/src/main/java/pico/typecheck/PICOVisitor.java +++ b/src/main/java/pico/typecheck/PICOVisitor.java @@ -450,63 +450,47 @@ public void processClassTree(ClassTree node) { checker.report(Result.failure("class.bound.invalid", bound), node); return;// Doesn't process the class tree anymore } - if (!checkCompatabilityBetweenBoundAndSuperClassesBounds(node, typeElement, bound)) { - return; - } - - if (!checkCompatabilityBetweenBoundAndExtendsImplements(node, bound)) { - return; - } - // Reach this point iff 1) bound annotation is one of mutable, rdm or immutable; - // 2) bound is compatible with bounds on super types. Only continue if bound check - // passed. Reaching here already means having passed bound check. super.processClassTree(node); } - - private boolean checkCompatabilityBetweenBoundAndSuperClassesBounds(ClassTree node, TypeElement typeElement, AnnotatedDeclaredType bound) { - // Must have compatible bound annotation as the direct super types - List superBounds = PICOTypeUtil.getBoundTypesOfDirectSuperTypes(typeElement, atypeFactory); - for (AnnotatedDeclaredType superBound : superBounds) { - // If annotation on super bound is @ReceiverDependantMutable, then any valid bound is permitted. - if (superBound.hasAnnotation(RECEIVER_DEPENDANT_MUTABLE)) continue; - // super bound is either @Mutable or @Immutable. Must be the subtype of the corresponding super bound - if (!atypeFactory.getQualifierHierarchy().isSubtype( - bound.getAnnotationInHierarchy(READONLY), superBound.getAnnotationInHierarchy(READONLY))) { - checker.report(Result.failure("subclass.bound.incompatible", bound, superBound), node); - return false; - } - } - return true; - } - - private boolean checkCompatabilityBetweenBoundAndExtendsImplements(ClassTree node, AnnotatedDeclaredType bound) { - boolean hasSame; - Tree ext = node.getExtendsClause(); - if (ext != null) { - AnnotatedTypeMirror extendsType = atypeFactory.getAnnotatedType(ext); - hasSame = bound.getAnnotations().size() == extendsType.getAnnotations().size() - && AnnotationUtils.areSame(extendsType.getAnnotationInHierarchy(READONLY), - bound.getAnnotationInHierarchy(READONLY)); - if (!hasSame) { - checker.report(Result.failure("bound.extends.incompatabile"), node); - return false; - } - } - - List impls = node.getImplementsClause(); - if (impls != null) { - for (Tree im : impls) { - AnnotatedTypeMirror implementsType = atypeFactory.getAnnotatedType(im); - hasSame = bound.getAnnotations().size() == implementsType.getAnnotations().size() - && AnnotationUtils.areSame(implementsType.getAnnotationInHierarchy(READONLY), - bound.getAnnotationInHierarchy(READONLY)); - if (!hasSame) { - checker.report(Result.failure("bound.implements.incompatabile"), node); - return false; - } - } + + @Override + protected void checkExtendsImplements(ClassTree classTree) { + if (TypesUtils.isAnonymous(TreeUtils.typeOf(classTree))) { + // Don't check extends clause on anonymous classes. + return; } - return true; + + AnnotationMirror classAnnot = + atypeFactory.getAnnotatedType(classTree).getAnnotationInHierarchy(READONLY); + + Tree extendsClause = classTree.getExtendsClause(); + if (extendsClause != null) { + AnnotationMirror extAnnot = atypeFactory.fromTypeTree(extendsClause).getAnnotationInHierarchy(READONLY); + if (extAnnot != null && !AnnotationUtils.areSame(extAnnot, classAnnot)) { + checker.report( + Result.failure( + "declaration.inconsistent.with.extends.clause", + classAnnot, + extAnnot), + extendsClause); + } + + } + + List implementsClauses = classTree.getImplementsClause(); + if (implementsClauses != null) { + for (Tree impl : implementsClauses) { + AnnotationMirror implAnnot = atypeFactory.fromTypeTree(impl).getAnnotationInHierarchy(READONLY); + if (implAnnot != null && !AnnotationUtils.areSame(implAnnot, classAnnot)) { + checker.report( + Result.failure( + "declaration.inconsistent.with.implements.clause", + classAnnot, + implAnnot), + impl); + } + } + } } } diff --git a/testinput/typecheck/CompatabilityBEI1.java b/testinput/typecheck/CompatabilityBEI1.java index d9f0963..7f079b6 100644 --- a/testinput/typecheck/CompatabilityBEI1.java +++ b/testinput/typecheck/CompatabilityBEI1.java @@ -32,18 +32,18 @@ class H extends Object {} @Mutable class I implements @Mutable Cloneable {} -// :: error: (bound.implements.incompatabile) +// :: error: (declaration.inconsistent.with.implements.clause) @Mutable class J implements @Immutable Cloneable {} -// :: error: (bound.implements.incompatabile) +// :: error: (declaration.inconsistent.with.implements.clause) @Mutable class K implements @ReceiverDependantMutable Cloneable {} -// :: error: (bound.extends.incompatabile) +// :: error: (declaration.inconsistent.with.extends.clause) @Immutable class L extends @Mutable Object {} @Immutable class M extends @Immutable Object {} -// :: error: (bound.extends.incompatabile) +// :: error: (declaration.inconsistent.with.extends.clause) @Immutable class N extends @ReceiverDependantMutable Object {} abstract class O implements CharSequence {} diff --git a/testinput/typecheck/CompatabilityBEI2.java b/testinput/typecheck/CompatabilityBEI2.java index 065c652..166d479 100644 --- a/testinput/typecheck/CompatabilityBEI2.java +++ b/testinput/typecheck/CompatabilityBEI2.java @@ -25,28 +25,28 @@ class H extends ArrayList<@Immutable Object> {} @Mutable abstract class I implements @Mutable List<@Immutable Object> {} -// :: error: (bound.implements.incompatabile) +// :: error: (declaration.inconsistent.with.implements.clause) @Mutable abstract class J implements @Immutable List<@Immutable Object> {} -// :: error: (bound.implements.incompatabile) +// :: error: (declaration.inconsistent.with.implements.clause) @Mutable abstract class K implements @ReceiverDependantMutable List<@Immutable Object> {} -// :: error: (bound.extends.incompatabile) +// :: error: (declaration.inconsistent.with.extends.clause) @Immutable class L extends @Mutable ArrayList<@Immutable Object> {} @Immutable class M extends @Immutable ArrayList<@Immutable Object> {} -// :: error: (bound.extends.incompatabile) +// :: error: (declaration.inconsistent.with.extends.clause) @Immutable class N extends @ReceiverDependantMutable ArrayList<@Immutable Object> {} abstract class O implements CharSequence {} @Immutable interface ImmutableInterface {} -// :: error: (subclass.bound.incompatible) +// :: error: (declaration.inconsistent.with.implements.clause) @Mutable abstract class P implements ImmutableInterface<@Mutable Object> {} @Immutable abstract class Q implements ImmutableInterface<@Immutable Object> {} -// :: error: (subclass.bound.incompatible) +// :: error: (declaration.inconsistent.with.implements.clause) @ReceiverDependantMutable abstract class R implements ImmutableInterface<@ReceiverDependantMutable Object> {} diff --git a/testinput/typecheck/ReceiverTypeOutsideConstructor.java b/testinput/typecheck/ReceiverTypeOutsideConstructor.java index 9c63d14..62eb1e6 100644 --- a/testinput/typecheck/ReceiverTypeOutsideConstructor.java +++ b/testinput/typecheck/ReceiverTypeOutsideConstructor.java @@ -31,13 +31,13 @@ class A { @Immutable class AIMS extends A {} -// :: error: (subclass.bound.incompatible) +// :: error: (declaration.inconsistent.with.extends.clause) @ReceiverDependantMutable class ARDMS extends A {} -// :: error: (subclass.bound.incompatible) +// :: error: (declaration.inconsistent.with.extends.clause) @Mutable class AMS extends A {} -// :: error: (subclass.bound.incompatible) +// :: error: (declaration.inconsistent.with.extends.clause) class AUNKS extends A {} // ReceiverDependantMutable class @@ -103,10 +103,10 @@ class C { } } -// :: error: (subclass.bound.incompatible) +// :: error: (declaration.inconsistent.with.extends.clause) @Immutable class CIMS extends C {} -// :: error: (subclass.bound.incompatible) +// :: error: (declaration.inconsistent.with.extends.clause) @ReceiverDependantMutable class CRDMS extends C {} // :: error: (super.constructor.invocation.incompatible) From 8b8bd6bad7412882e6ce9f0da09cbd49a2e11f56 Mon Sep 17 00:00:00 2001 From: Baorui Zhou Date: Tue, 30 Apr 2019 11:33:37 -0400 Subject: [PATCH 007/144] tweak travis build script --- .travis-build.sh | 34 ++++++++-------------------------- 1 file changed, 8 insertions(+), 26 deletions(-) diff --git a/.travis-build.sh b/.travis-build.sh index 014ec81..1a9c977 100755 --- a/.travis-build.sh +++ b/.travis-build.sh @@ -10,47 +10,29 @@ export CHECKERFRAMEWORK=$JSR308/checker-framework export PATH=$AFU/scripts:$JAVA_HOME/bin:$PATH #default value is opprop. REPO_SITE may be set to other value for travis test purpose. -export REPO_SITE=topnessman +export REPO_SITE=baoruiz echo "------ Downloading everthing from REPO_SITE: $REPO_SITE ------" -# Clone annotation-tools (Annotation File Utilities) -if [ -d $JSR308/annotation-tools ] ; then - (cd $JSR308/annotation-tools && git pull) -else - (cd $JSR308 && git clone -b pico-dependant-copy --depth 1 https://github.com/"$REPO_SITE"/annotation-tools.git) -fi - -# Clone stubparser -if [ -d $JSR308/stubparser ] ; then - (cd $JSR308/stubparser && git pull) -else - (cd $JSR308 && git clone -b pico-dependant-copy --depth 1 https://github.com/"$REPO_SITE"/stubparser.git) -fi # Clone checker-framework if [ -d $JSR308/checker-framework ] ; then - (cd $JSR308/checker-framework && git checkout pico-dependant-copy && git pull) + (cd $JSR308/checker-framework && git checkout pull-pico-changes && git pull) else - # ViewpointAdapter changes are not yet merged to master, so we depend on pico-dependant branch - (cd $JSR308 && git clone -b pico-dependant-copy --depth 1 https://github.com/"$REPO_SITE"/checker-framework.git) + (cd $JSR308 && git clone -b pull-pico-changes --depth 1 https://github.com/"$REPO_SITE"/checker-framework.git) fi # Clone checker-framework-inference if [ -d $JSR308/checker-framework-inference ] ; then - (cd $JSR308/checker-framework-inference && git checkout pico-dependant-copy && git pull) + (cd $JSR308/checker-framework-inference && git checkout pull-pico-changes && git pull) else - # Again we depend on pico-dependant branch - (cd $JSR308 && git clone -b pico-dependant-copy --depth 1 https://github.com/"$REPO_SITE"/checker-framework-inference.git) + (cd $JSR308 && git clone -b pull-pico-changes --depth 1 https://github.com/"$REPO_SITE"/checker-framework-inference.git) fi -# Build annotation-tools (and jsr308-langtools) -(cd $JSR308/annotation-tools/ && ./.travis-build-without-test.sh) -# Build stubparser -(cd $JSR308/stubparser/ && mvn package -Dmaven.test.skip=true) # Build checker-framework, with downloaded jdk -(cd $JSR308/checker-framework && ant -f checker/build.xml dist-downloadjdk) +(cd $JSR308/checker-framework && ./.travis-build-without-test.sh downloadjdk) + # Build checker-framework-inference -(cd $JSR308/checker-framework-inference && gradle dist) # This step needs to be manually in $CFI executed due to path problems +(cd $JSR308/checker-framework-inference && ./gradlew dist) # Build PICO (cd $JSR308/immutability && ./gradlew build) From e3bc7a5ec547fa9977bbe235635e4d4815879f2d Mon Sep 17 00:00:00 2001 From: xingweitian <13183370+xingweitian@users.noreply.github.com> Date: Tue, 2 Jul 2019 21:55:58 -0400 Subject: [PATCH 008/144] Remove useless separators. --- testinput/typecheck/SuperClass.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/testinput/typecheck/SuperClass.java b/testinput/typecheck/SuperClass.java index 3c71c82..d857b7d 100644 --- a/testinput/typecheck/SuperClass.java +++ b/testinput/typecheck/SuperClass.java @@ -26,7 +26,7 @@ class SubClass extends SuperClass{ public static void main(String[] args) { @Mutable SubClass victim = new @Mutable SubClass(); - victim.maliciouslyModifyDate();; + victim.maliciouslyModifyDate(); } } @@ -39,6 +39,6 @@ class AnotherSubClass extends SuperClass{ public static void main(String[] args) { @Mutable SubClass victim = new @Mutable SubClass(); - victim.maliciouslyModifyDate();; + victim.maliciouslyModifyDate(); } } From 21d2fae16066ca8ce3b37da24a423bdd165c0fcb Mon Sep 17 00:00:00 2001 From: xingweitian <13183370+xingweitian@users.noreply.github.com> Date: Wed, 10 Jul 2019 16:36:06 -0400 Subject: [PATCH 009/144] Unify the name of test files. --- build.gradle | 2 +- ...lityInferenceTest.java => ImmutabilityInferenceTests.java} | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) rename src/test/java/pico/{ImmutabilityInferenceTest.java => ImmutabilityInferenceTests.java} (91%) diff --git a/build.gradle b/build.gradle index 714c4b5..0eb6724 100644 --- a/build.gradle +++ b/build.gradle @@ -79,7 +79,7 @@ afterEvaluate { // Create a task for each JUnit test class whose name is the same as the JUnit class name. sourceSets.test.allJava.filter { it.path.contains("${picoPath}/src/test/java") }.forEach { file -> String junitClassName = file.name.replaceAll(".java", "") - String testName = junitClassName.replaceAll("Test", "") + String testName = junitClassName.replaceAll("Tests", "") tasks.create(name: "${junitClassName}", type: Test) { description "Run ${testName} tests." include "**/${name}.class" diff --git a/src/test/java/pico/ImmutabilityInferenceTest.java b/src/test/java/pico/ImmutabilityInferenceTests.java similarity index 91% rename from src/test/java/pico/ImmutabilityInferenceTest.java rename to src/test/java/pico/ImmutabilityInferenceTests.java index 59b88a8..d8fcc90 100644 --- a/src/test/java/pico/ImmutabilityInferenceTest.java +++ b/src/test/java/pico/ImmutabilityInferenceTests.java @@ -13,9 +13,9 @@ import pico.inference.PICOInferenceChecker; import pico.inference.solver.PICOSolverEngine; -public class ImmutabilityInferenceTest extends CFInferenceTest { +public class ImmutabilityInferenceTests extends CFInferenceTest { - public ImmutabilityInferenceTest(File testFile) { + public ImmutabilityInferenceTests(File testFile) { super(testFile, PICOInferenceChecker.class, "", "-Anomsgtext", "-Astubs=src/main/java/pico/typecheck/jdk.astub", "-d", "testdata/inference/inferrable"); } From 8b6977015b8b8b8d7028035d0a91e3403328cd2e Mon Sep 17 00:00:00 2001 From: xingweitian <13183370+xingweitian@users.noreply.github.com> Date: Wed, 24 Jul 2019 19:41:48 -0400 Subject: [PATCH 010/144] Fix typo. --- src/main/java/pico/typecheck/PICOValidator.java | 2 +- src/main/java/pico/typecheck/PICOVisitor.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/pico/typecheck/PICOValidator.java b/src/main/java/pico/typecheck/PICOValidator.java index 9e042bf..d776200 100644 --- a/src/main/java/pico/typecheck/PICOValidator.java +++ b/src/main/java/pico/typecheck/PICOValidator.java @@ -59,7 +59,7 @@ private void checkStaticReceiverDependantMutableError(AnnotatedTypeMirror type, } } - /**Check that implicitly immutable type has immutable or bottom type. Dataflow might refine immtable type to @Bottom, + /**Check that implicitly immutable type has immutable or bottom type. Dataflow might refine immutable type to @Bottom, * so we accept @Bottom as a valid qualifier for implicitly immutable types*/ private void checkImplicitlyImmutableTypeError(AnnotatedTypeMirror type, Tree tree) { if (PICOTypeUtil.isImplicitlyImmutableType(type) && !type.hasAnnotation(IMMUTABLE) && !type.hasAnnotation(BOTTOM)) { diff --git a/src/main/java/pico/typecheck/PICOVisitor.java b/src/main/java/pico/typecheck/PICOVisitor.java index 45ec4d1..f018e93 100644 --- a/src/main/java/pico/typecheck/PICOVisitor.java +++ b/src/main/java/pico/typecheck/PICOVisitor.java @@ -86,7 +86,7 @@ public boolean isValidUse(AnnotatedDeclaredType declarationType, AnnotatedDeclar // if (AnnotationUtils.areSame(declared, atypeFactory.READONLY)) { // // Special case for java.lang.Object. Usually @Readonly is never used as a bound annotation for a // // TypeElement. But we want to have @Readonly as the default for java.lang.Object. There is no way -// // of doing this using any exsisting family of @DefaultFor qualifiers, but @ImplicitFor annotation +// // of doing this using any existing family of @DefaultFor qualifiers, but @ImplicitFor annotation // // does the trick. But the side effect is, we can't write @ReceiverDependantMutable, which is the // // correct bound for Object element, in jdk.astub, because otherwise it makes all java.lang.Object // // to be @ReceiverDependantMutable; Another side effect is here @Readonly is passed into here as From dd4717ab15974c097cf28972395230e289a9c4bd Mon Sep 17 00:00:00 2001 From: xingweitian <13183370+xingweitian@users.noreply.github.com> Date: Wed, 24 Jul 2019 19:47:27 -0400 Subject: [PATCH 011/144] Fix typo. --- src/main/java/pico/typecheck/PICOVisitor.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/pico/typecheck/PICOVisitor.java b/src/main/java/pico/typecheck/PICOVisitor.java index f018e93..1e46d0c 100644 --- a/src/main/java/pico/typecheck/PICOVisitor.java +++ b/src/main/java/pico/typecheck/PICOVisitor.java @@ -101,7 +101,7 @@ public boolean isValidUse(AnnotatedDeclaredType declarationType, AnnotatedDeclar return true; } // At this point, element type can only be @Mutable or @Immutable. Otherwise, it's a problem in - // PICOVisitor#processorClassTree(ClassTree) + // PICOVisitor#processClassTree(ClassTree) assert AnnotationUtils.areSame(declared, MUTABLE) || AnnotationUtils.areSame(declared, IMMUTABLE); AnnotationMirror used = useType.getAnnotationInHierarchy(READONLY); From e468c1e7d1ab55afda4f94efb953031869338632 Mon Sep 17 00:00:00 2001 From: xingweitian <13183370+xingweitian@users.noreply.github.com> Date: Thu, 25 Jul 2019 20:29:39 -0400 Subject: [PATCH 012/144] Fix typo. --- src/main/java/pico/typecheck/PICOViewpointAdapter.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/pico/typecheck/PICOViewpointAdapter.java b/src/main/java/pico/typecheck/PICOViewpointAdapter.java index fdb3d4d..392bfe3 100644 --- a/src/main/java/pico/typecheck/PICOViewpointAdapter.java +++ b/src/main/java/pico/typecheck/PICOViewpointAdapter.java @@ -57,7 +57,7 @@ protected AnnotationMirror combineAnnotationWithAnnotation(AnnotationMirror rece } else if (AnnotationUtils.areSame(declaredAnnotation, RECEIVER_DEPENDANT_MUTABLE)) { return receiverAnnotation; } else { - throw new BugInCF("Unkown declared modifier: " + declaredAnnotation, new UnkownImmutabilityQualifierException()); + throw new BugInCF("Unknown declared modifier: " + declaredAnnotation, new UnkownImmutabilityQualifierException()); } } // From 0391e9dbcb0407ee9cad8e05d8d557a2137e8e1f Mon Sep 17 00:00:00 2001 From: xingweitian <13183370+xingweitian@users.noreply.github.com> Date: Sat, 27 Jul 2019 00:10:30 -0400 Subject: [PATCH 013/144] Complete the messages. --- src/main/java/pico/typecheck/messages.properties | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/pico/typecheck/messages.properties b/src/main/java/pico/typecheck/messages.properties index 105fe05..9040ed2 100644 --- a/src/main/java/pico/typecheck/messages.properties +++ b/src/main/java/pico/typecheck/messages.properties @@ -1,8 +1,8 @@ constructor.invocation.invalid=Cannot not instantiate type: %s out of constructor: %s constructor.return.invalid=Invalid constructor return type: %s -method.receiver.incompatible -class.bound.invalid -subclass.bound.incompatible +method.receiver.incompatible=Incompatible method receiver: %s +class.bound.invalid=Invalid class bound: %s +subclass.bound.incompatible=Incompatible subclass bound: %s super.constructor.invocation.incompatible=Subclass constructor: %s is not compatible with super class constructor: %s illegal.field.write=Cannot write field via receiver: %s illegal.array.write=Cannot write array via receiver: %s From fb54eb26a2e6c5a206a0badd1f275451c8e6ff3f Mon Sep 17 00:00:00 2001 From: Weitian Xing <13183370+xingweitian@users.noreply.github.com> Date: Sun, 28 Jul 2019 10:52:37 -0400 Subject: [PATCH 014/144] Use all dirs and jar file to fix the issue that "messages.properties" cannot be found. (#1) --- check.sh | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/check.sh b/check.sh index a6cbccc..f4b2166 100755 --- a/check.sh +++ b/check.sh @@ -10,7 +10,8 @@ export JAVAC=$CF/checker/bin/javac export PICO=$(cd $(dirname "$0") && pwd) # Dependencies -export CLASSPATH=$PICO/build/classes/java/main:$CFI/dist/checker-framework-inference.jar +export CLASSPATH=$PICO/build/classes/java/main:$PICO/build/resources/main:\ +$PICO/build/libs/immutability.jar:$CFI/dist/checker-framework-inference.jar # Command DEBUG="" @@ -35,9 +36,9 @@ done cmd="" if [ "$DEBUG" == "" ]; then - cmd="$JAVAC -cp "${CLASSPATH}" -processor "${CHECKER}" "${ARGS[@]}"" + cmd="$JAVAC -cp "${CLASSPATH}" -processor "${CHECKER}" "${ARGS[@]}"" else - cmd="$JAVAC "$DEBUG" -cp "${CLASSPATH}" -processor "${CHECKER}" -AatfDoNotCache "${ARGS[@]}"" + cmd="$JAVAC "$DEBUG" -cp "${CLASSPATH}" -processor "${CHECKER}" -AatfDoNotCache "${ARGS[@]}"" fi eval "$cmd" From 6c07c6ce34e84b17638132a41884223914c42c0f Mon Sep 17 00:00:00 2001 From: xingweitian <13183370+xingweitian@users.noreply.github.com> Date: Sun, 28 Jul 2019 13:59:03 -0400 Subject: [PATCH 015/144] Tweaks. --- build.gradle | 2 +- ...lityInferenceTests.java => ImmutabilityInferenceTest.java} | 4 ++-- ...lityTypecheckTests.java => ImmutabilityTypecheckTest.java} | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) rename src/test/java/pico/{ImmutabilityInferenceTests.java => ImmutabilityInferenceTest.java} (91%) rename src/test/java/pico/{ImmutabilityTypecheckTests.java => ImmutabilityTypecheckTest.java} (83%) diff --git a/build.gradle b/build.gradle index 0eb6724..714c4b5 100644 --- a/build.gradle +++ b/build.gradle @@ -79,7 +79,7 @@ afterEvaluate { // Create a task for each JUnit test class whose name is the same as the JUnit class name. sourceSets.test.allJava.filter { it.path.contains("${picoPath}/src/test/java") }.forEach { file -> String junitClassName = file.name.replaceAll(".java", "") - String testName = junitClassName.replaceAll("Tests", "") + String testName = junitClassName.replaceAll("Test", "") tasks.create(name: "${junitClassName}", type: Test) { description "Run ${testName} tests." include "**/${name}.class" diff --git a/src/test/java/pico/ImmutabilityInferenceTests.java b/src/test/java/pico/ImmutabilityInferenceTest.java similarity index 91% rename from src/test/java/pico/ImmutabilityInferenceTests.java rename to src/test/java/pico/ImmutabilityInferenceTest.java index d8fcc90..59b88a8 100644 --- a/src/test/java/pico/ImmutabilityInferenceTests.java +++ b/src/test/java/pico/ImmutabilityInferenceTest.java @@ -13,9 +13,9 @@ import pico.inference.PICOInferenceChecker; import pico.inference.solver.PICOSolverEngine; -public class ImmutabilityInferenceTests extends CFInferenceTest { +public class ImmutabilityInferenceTest extends CFInferenceTest { - public ImmutabilityInferenceTests(File testFile) { + public ImmutabilityInferenceTest(File testFile) { super(testFile, PICOInferenceChecker.class, "", "-Anomsgtext", "-Astubs=src/main/java/pico/typecheck/jdk.astub", "-d", "testdata/inference/inferrable"); } diff --git a/src/test/java/pico/ImmutabilityTypecheckTests.java b/src/test/java/pico/ImmutabilityTypecheckTest.java similarity index 83% rename from src/test/java/pico/ImmutabilityTypecheckTests.java rename to src/test/java/pico/ImmutabilityTypecheckTest.java index a07d004..41482b6 100644 --- a/src/test/java/pico/ImmutabilityTypecheckTests.java +++ b/src/test/java/pico/ImmutabilityTypecheckTest.java @@ -9,8 +9,8 @@ import java.util.ArrayList; import java.util.List; -public class ImmutabilityTypecheckTests extends CheckerFrameworkPerFileTest { - public ImmutabilityTypecheckTests(File testFile) { +public class ImmutabilityTypecheckTest extends CheckerFrameworkPerFileTest { + public ImmutabilityTypecheckTest(File testFile) { super(testFile, PICOChecker.class, "", "-Anomsgtext", "-Anocheckjdk", "-d", "testTmp/typecheck"); } From 10613ac4bf8b81405012201b240c6856160f20cb Mon Sep 17 00:00:00 2001 From: xingweitian <13183370+xingweitian@users.noreply.github.com> Date: Mon, 29 Jul 2019 20:05:59 -0400 Subject: [PATCH 016/144] Use the correct error key. --- testinput/typecheck/OnlyOneModifierIsUse.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/testinput/typecheck/OnlyOneModifierIsUse.java b/testinput/typecheck/OnlyOneModifierIsUse.java index 728eefd..cc167ba 100644 --- a/testinput/typecheck/OnlyOneModifierIsUse.java +++ b/testinput/typecheck/OnlyOneModifierIsUse.java @@ -6,8 +6,8 @@ public class OnlyOneModifierIsUse { - // :: error: (type.invalid) + // :: error: (type.invalid.conflicting.annos) @Readonly @Immutable Object field; - // :: error: (type.invalid) + // :: error: (type.invalid.conflicting.annos) String @Readonly @Immutable [] array; } From ab8af46b28d04e32d6d01019d86ce84b200c73a7 Mon Sep 17 00:00:00 2001 From: Weitian Xing <13183370+xingweitian@users.noreply.github.com> Date: Sun, 18 Aug 2019 17:18:02 -0400 Subject: [PATCH 017/144] Fix typo (CompatabilityBEI2.java -> CompatibilityBEI2.java) and make CompatibilityBEI2.java pass. (#8) Make CompatibilityBEI2.java pass. --- .../{CompatabilityBEI2.java => CompatibilityBEI2.java} | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) rename testinput/typecheck/{CompatabilityBEI2.java => CompatibilityBEI2.java} (88%) diff --git a/testinput/typecheck/CompatabilityBEI2.java b/testinput/typecheck/CompatibilityBEI2.java similarity index 88% rename from testinput/typecheck/CompatabilityBEI2.java rename to testinput/typecheck/CompatibilityBEI2.java index 166d479..440de14 100644 --- a/testinput/typecheck/CompatabilityBEI2.java +++ b/testinput/typecheck/CompatibilityBEI2.java @@ -43,10 +43,10 @@ abstract class O implements CharSequence {} @Immutable interface ImmutableInterface {} -// :: error: (declaration.inconsistent.with.implements.clause) +// :: error: (declaration.inconsistent.with.implements.clause) :: error: (type.argument.type.incompatible) @Mutable abstract class P implements ImmutableInterface<@Mutable Object> {} @Immutable abstract class Q implements ImmutableInterface<@Immutable Object> {} -// :: error: (declaration.inconsistent.with.implements.clause) +// :: error: (declaration.inconsistent.with.implements.clause) :: error: (type.argument.type.incompatible) @ReceiverDependantMutable abstract class R implements ImmutableInterface<@ReceiverDependantMutable Object> {} From a19d94f59c001408b5737365a403e7dd16ab940b Mon Sep 17 00:00:00 2001 From: xingweitian <13183370+xingweitian@users.noreply.github.com> Date: Mon, 9 Sep 2019 20:38:14 -0400 Subject: [PATCH 018/144] Update. --- .../pico/inference/PICOInferenceAnnotatedTypeFactory.java | 6 +++--- .../java/pico/inference/PICOInferenceRealTypeFactory.java | 7 ++----- src/main/java/pico/inference/PICOInferenceVisitor.java | 2 +- src/main/java/pico/typecheck/PICOAnnotatedTypeFactory.java | 5 ++--- src/main/java/pico/typecheck/PICOVisitor.java | 2 +- 5 files changed, 9 insertions(+), 13 deletions(-) diff --git a/src/main/java/pico/inference/PICOInferenceAnnotatedTypeFactory.java b/src/main/java/pico/inference/PICOInferenceAnnotatedTypeFactory.java index 386a4de..cf79e02 100644 --- a/src/main/java/pico/inference/PICOInferenceAnnotatedTypeFactory.java +++ b/src/main/java/pico/inference/PICOInferenceAnnotatedTypeFactory.java @@ -28,7 +28,7 @@ import org.checkerframework.framework.type.typeannotator.TypeAnnotator; import org.checkerframework.javacutil.Pair; import org.checkerframework.javacutil.TreeUtils; -import pico.typecheck.PICOAnnotatedTypeFactory.PICOImplicitsTypeAnnotator; +import pico.typecheck.PICOAnnotatedTypeFactory.PICODefaultTypeAnnotator; import pico.typecheck.PICOTypeUtil; import javax.lang.model.element.AnnotationMirror; @@ -69,10 +69,10 @@ public TreeAnnotator createTreeAnnotator() { @Override protected TypeAnnotator createTypeAnnotator() { - // Reuse PICOImplicitsTypeAnnotator even in inference mode. Because the type annotator's implementation + // Reuse PICODefaultTypeAnnotator even in inference mode. Because the type annotator's implementation // are the same. The only drawback is that naming is not good(doesn't include "Inference"), thus may be // hard to debug - return new ListTypeAnnotator(super.createTypeAnnotator(), new PICOImplicitsTypeAnnotator(this)); + return new ListTypeAnnotator(super.createTypeAnnotator(), new PICODefaultTypeAnnotator(this)); } @Override diff --git a/src/main/java/pico/inference/PICOInferenceRealTypeFactory.java b/src/main/java/pico/inference/PICOInferenceRealTypeFactory.java index 3c247ea..6d5ea34 100644 --- a/src/main/java/pico/inference/PICOInferenceRealTypeFactory.java +++ b/src/main/java/pico/inference/PICOInferenceRealTypeFactory.java @@ -1,7 +1,5 @@ package pico.inference; -import static pico.typecheck.PICOAnnotationMirrorHolder.IMMUTABLE; -import static pico.typecheck.PICOAnnotationMirrorHolder.MUTABLE; import static pico.typecheck.PICOAnnotationMirrorHolder.READONLY; import java.lang.annotation.Annotation; @@ -11,7 +9,6 @@ import java.util.List; import java.util.Set; -import javax.lang.model.element.AnnotationMirror; import javax.lang.model.element.Element; import org.checkerframework.common.basetype.BaseAnnotatedTypeFactory; @@ -31,7 +28,7 @@ import com.sun.source.tree.Tree; -import pico.typecheck.PICOAnnotatedTypeFactory.PICOImplicitsTypeAnnotator; +import pico.typecheck.PICOAnnotatedTypeFactory.PICODefaultTypeAnnotator; import pico.typecheck.PICOAnnotatedTypeFactory.PICOPropagationTreeAnnotator; import pico.typecheck.PICOAnnotatedTypeFactory.PICOTreeAnnotator; import pico.typecheck.PICOAnnotatedTypeFactory.PICOTypeAnnotator; @@ -109,7 +106,7 @@ protected TypeAnnotator createTypeAnnotator() { // method, so if one annotator already applied the annotations, the others won't apply twice at the // same location typeAnnotators.add(new PICOTypeAnnotator(this)); - typeAnnotators.add(new PICOImplicitsTypeAnnotator(this)); + typeAnnotators.add(new PICODefaultTypeAnnotator(this)); return new ListTypeAnnotator(typeAnnotators); } diff --git a/src/main/java/pico/inference/PICOInferenceVisitor.java b/src/main/java/pico/inference/PICOInferenceVisitor.java index 83dedd6..297a2e8 100644 --- a/src/main/java/pico/inference/PICOInferenceVisitor.java +++ b/src/main/java/pico/inference/PICOInferenceVisitor.java @@ -621,7 +621,7 @@ public Void visitMethodInvocation(MethodInvocationTree node, Void p) { // Only check invocability if it's super call, as non-super call is already checked // by super implementation(of course in both cases, invocability is not checked when // invoking static methods) - if (!ElementUtils.isStatic(invokedMethodElement) && TreeUtils.isSuperCall(node)) { + if (!ElementUtils.isStatic(invokedMethodElement) && TreeUtils.isSuperConstructorCall(node)) { checkMethodInvocability(invokedMethod, node); } return null; diff --git a/src/main/java/pico/typecheck/PICOAnnotatedTypeFactory.java b/src/main/java/pico/typecheck/PICOAnnotatedTypeFactory.java index db6dd28..4eadedd 100644 --- a/src/main/java/pico/typecheck/PICOAnnotatedTypeFactory.java +++ b/src/main/java/pico/typecheck/PICOAnnotatedTypeFactory.java @@ -2,7 +2,6 @@ import static pico.typecheck.PICOAnnotationMirrorHolder.COMMITED; import static pico.typecheck.PICOAnnotationMirrorHolder.IMMUTABLE; -import static pico.typecheck.PICOAnnotationMirrorHolder.MUTABLE; import static pico.typecheck.PICOAnnotationMirrorHolder.POLY_MUTABLE; import static pico.typecheck.PICOAnnotationMirrorHolder.READONLY; import static pico.typecheck.PICOAnnotationMirrorHolder.SUBSTITUTABLE_POLY_MUTABLE; @@ -138,7 +137,7 @@ protected TypeAnnotator createTypeAnnotator() { // method, so if one annotator already applied the annotations, the others won't apply twice at the // same location typeAnnotators.add(new PICOTypeAnnotator(this)); - typeAnnotators.add(new PICOImplicitsTypeAnnotator(this)); + typeAnnotators.add(new PICODefaultTypeAnnotator(this)); typeAnnotators.add(new CommitmentTypeAnnotator(this)); return new ListTypeAnnotator(typeAnnotators); } @@ -463,7 +462,7 @@ public Void visitExecutable(AnnotatedExecutableType t, Void p) { public static class PICOImplicitsTypeAnnotator extends ImplicitsTypeAnnotator { - public PICOImplicitsTypeAnnotator(AnnotatedTypeFactory typeFactory) { + public PICODefaultTypeAnnotator(AnnotatedTypeFactory typeFactory) { super(typeFactory); } diff --git a/src/main/java/pico/typecheck/PICOVisitor.java b/src/main/java/pico/typecheck/PICOVisitor.java index 1e46d0c..8f111bb 100644 --- a/src/main/java/pico/typecheck/PICOVisitor.java +++ b/src/main/java/pico/typecheck/PICOVisitor.java @@ -385,7 +385,7 @@ public Void visitMethodInvocation(MethodInvocationTree node, Void p) { // Only check invocability if it's super call, as non-super call is already checked // by super implementation(of course in both cases, invocability is not checked when // invoking static methods) - if (!ElementUtils.isStatic(invokedMethodElement) && TreeUtils.isSuperCall(node)) { + if (!ElementUtils.isStatic(invokedMethodElement) && TreeUtils.isSuperConstructorCall(node)) { checkMethodInvocability(invokedMethod, node); } return null; From 236d0f435e590dc48a7f0f119a15598d1cb64983 Mon Sep 17 00:00:00 2001 From: xingweitian Date: Fri, 6 Sep 2019 21:47:37 -0400 Subject: [PATCH 019/144] Update. --- .../PICOInferenceAnnotatedTypeFactory.java | 4 +-- .../PICOInferenceRealTypeFactory.java | 30 ++++++++-------- .../typecheck/PICOAnnotatedTypeFactory.java | 36 +++++++++---------- src/main/java/pico/typecheck/PICOChecker.java | 2 +- .../java/pico/typecheck/PICOTypeUtil.java | 30 ++++++++-------- src/main/java/qual/Bottom.java | 4 --- src/main/java/qual/Immutable.java | 9 ++--- src/main/java/qual/Readonly.java | 1 - 8 files changed, 56 insertions(+), 60 deletions(-) diff --git a/src/main/java/pico/inference/PICOInferenceAnnotatedTypeFactory.java b/src/main/java/pico/inference/PICOInferenceAnnotatedTypeFactory.java index cf79e02..d56c9b5 100644 --- a/src/main/java/pico/inference/PICOInferenceAnnotatedTypeFactory.java +++ b/src/main/java/pico/inference/PICOInferenceAnnotatedTypeFactory.java @@ -20,7 +20,7 @@ import org.checkerframework.framework.type.AnnotatedTypeFactory; import org.checkerframework.framework.type.AnnotatedTypeMirror; import org.checkerframework.framework.type.AnnotatedTypeMirror.AnnotatedDeclaredType; -import org.checkerframework.framework.type.treeannotator.ImplicitsTreeAnnotator; +import org.checkerframework.framework.type.treeannotator.LiteralTreeAnnotator; import org.checkerframework.framework.type.treeannotator.ListTreeAnnotator; import org.checkerframework.framework.type.treeannotator.PropagationTreeAnnotator; import org.checkerframework.framework.type.treeannotator.TreeAnnotator; @@ -62,7 +62,7 @@ public PICOInferenceAnnotatedTypeFactory(InferenceChecker inferenceChecker, bool // be inserted results anyway). @Override public TreeAnnotator createTreeAnnotator() { - return new ListTreeAnnotator(new ImplicitsTreeAnnotator(this), + return new ListTreeAnnotator(new LiteralTreeAnnotator(this), new PICOInferencePropagationTreeAnnotator(this), new InferenceTreeAnnotator(this, realChecker, realTypeFactory, variableAnnotator, slotManager)); } diff --git a/src/main/java/pico/inference/PICOInferenceRealTypeFactory.java b/src/main/java/pico/inference/PICOInferenceRealTypeFactory.java index 6d5ea34..11563fd 100644 --- a/src/main/java/pico/inference/PICOInferenceRealTypeFactory.java +++ b/src/main/java/pico/inference/PICOInferenceRealTypeFactory.java @@ -16,7 +16,7 @@ import org.checkerframework.framework.qual.RelevantJavaTypes; import org.checkerframework.framework.type.AbstractViewpointAdapter; import org.checkerframework.framework.type.AnnotatedTypeMirror; -import org.checkerframework.framework.type.treeannotator.ImplicitsTreeAnnotator; +import org.checkerframework.framework.type.treeannotator.LiteralTreeAnnotator; import org.checkerframework.framework.type.treeannotator.ListTreeAnnotator; import org.checkerframework.framework.type.treeannotator.TreeAnnotator; import org.checkerframework.framework.type.typeannotator.IrrelevantTypeAnnotator; @@ -81,7 +81,7 @@ protected AbstractViewpointAdapter createViewpointAdapter() { protected TreeAnnotator createTreeAnnotator() { return new ListTreeAnnotator( new PICOPropagationTreeAnnotator(this), - new ImplicitsTreeAnnotator(this), + new LiteralTreeAnnotator(this), new PICOTreeAnnotator(this)); } @@ -125,19 +125,19 @@ public boolean getShouldDefaultTypeVarLocals() { } // Copied from PICOAnnotatedTypeFactory - @Override - protected void annotateInheritedFromClass(AnnotatedTypeMirror type, Set fromClass) { - // If interitted from class element is @Mutable or @Immutable, then apply this annotation to the usage type - if (fromClass.contains(MUTABLE) || fromClass.contains(IMMUTABLE)) { - super.annotateInheritedFromClass(type, fromClass); - return; - } - // If interitted from class element is @ReceiverDependantMutable, then don't apply and wait for @Mutable - // (default qualifier in hierarchy to be applied to the usage type). This is to avoid having @ReceiverDependantMutable - // on type usages as a default behaviour. By default, @Mutable is better used as the type for usages that - // don't have explicit annotation. - return;// Don't add annotations from class element - } +// @Override +// protected void annotateInheritedFromClass(AnnotatedTypeMirror type, Set fromClass) { +// // If interitted from class element is @Mutable or @Immutable, then apply this annotation to the usage type +// if (fromClass.contains(MUTABLE) || fromClass.contains(IMMUTABLE)) { +// super.annotateInheritedFromClass(type, fromClass); +// return; +// } +// // If interitted from class element is @ReceiverDependantMutable, then don't apply and wait for @Mutable +// // (default qualifier in hierarchy to be applied to the usage type). This is to avoid having @ReceiverDependantMutable +// // on type usages as a default behaviour. By default, @Mutable is better used as the type for usages that +// // don't have explicit annotation. +// return;// Don't add annotations from class element +// } @Override public void addComputedTypeAnnotations(Element elt, AnnotatedTypeMirror type) { diff --git a/src/main/java/pico/typecheck/PICOAnnotatedTypeFactory.java b/src/main/java/pico/typecheck/PICOAnnotatedTypeFactory.java index 4eadedd..c84be72 100644 --- a/src/main/java/pico/typecheck/PICOAnnotatedTypeFactory.java +++ b/src/main/java/pico/typecheck/PICOAnnotatedTypeFactory.java @@ -33,11 +33,11 @@ import org.checkerframework.framework.type.AnnotatedTypeMirror.AnnotatedExecutableType; import org.checkerframework.framework.type.QualifierHierarchy; import org.checkerframework.framework.type.ViewpointAdapter; -import org.checkerframework.framework.type.treeannotator.ImplicitsTreeAnnotator; +import org.checkerframework.framework.type.treeannotator.LiteralTreeAnnotator; import org.checkerframework.framework.type.treeannotator.ListTreeAnnotator; import org.checkerframework.framework.type.treeannotator.PropagationTreeAnnotator; import org.checkerframework.framework.type.treeannotator.TreeAnnotator; -import org.checkerframework.framework.type.typeannotator.ImplicitsTypeAnnotator; +import org.checkerframework.framework.type.typeannotator.DefaultForTypeAnnotator; import org.checkerframework.framework.type.typeannotator.IrrelevantTypeAnnotator; import org.checkerframework.framework.type.typeannotator.ListTypeAnnotator; import org.checkerframework.framework.type.typeannotator.PropagationTypeAnnotator; @@ -77,7 +77,7 @@ public class PICOAnnotatedTypeFactory extends InitializationAnnotatedTypeFactory PICOStore, PICOTransfer, PICOAnalysis> { public PICOAnnotatedTypeFactory(BaseTypeChecker checker) { - super(checker, true); + super(checker); postInit(); // PICO aliasing is not implemented correctly // remove for now @@ -111,7 +111,7 @@ protected ViewpointAdapter createViewpointAdapter() { protected TreeAnnotator createTreeAnnotator() { return new ListTreeAnnotator( new PICOPropagationTreeAnnotator(this), - new ImplicitsTreeAnnotator(this), + new LiteralTreeAnnotator(this), new CommitmentTreeAnnotator(this), new PICOTreeAnnotator(this)); } @@ -182,19 +182,19 @@ public void addComputedTypeAnnotations(Element elt, AnnotatedTypeMirror type) { super.addComputedTypeAnnotations(elt, type); } - @Override - protected void annotateInheritedFromClass(AnnotatedTypeMirror type, Set fromClass) { - // If interitted from class element is @Mutable or @Immutable, then apply this annotation to the usage type - if (fromClass.contains(MUTABLE) || fromClass.contains(IMMUTABLE)) { - super.annotateInheritedFromClass(type, fromClass); - return; - } - // If interitted from class element is @ReceiverDependantMutable, then don't apply and wait for @Mutable - // (default qualifier in hierarchy to be applied to the usage type). This is to avoid having @ReceiverDependantMutable - // on type usages as a default behaviour. By default, @Mutable is better used as the type for usages that - // don't have explicit annotation. - return;// Don't add annotations from class element - } +// @Override +// protected void annotateInheritedFromClass(AnnotatedTypeMirror type, Set fromClass) { +// // If interitted from class element is @Mutable or @Immutable, then apply this annotation to the usage type +// if (fromClass.contains(MUTABLE) || fromClass.contains(IMMUTABLE)) { +// super.annotateInheritedFromClass(type, fromClass); +// return; +// } +// // If interitted from class element is @ReceiverDependantMutable, then don't apply and wait for @Mutable +// // (default qualifier in hierarchy to be applied to the usage type). This is to avoid having @ReceiverDependantMutable +// // on type usages as a default behaviour. By default, @Mutable is better used as the type for usages that +// // don't have explicit annotation. +// return;// Don't add annotations from class element +// } /**This method gets lhs WITH flow sensitive refinement*/ // TODO Should refactor super class to avoid too much duplicate code. @@ -460,7 +460,7 @@ public Void visitExecutable(AnnotatedExecutableType t, Void p) { } - public static class PICOImplicitsTypeAnnotator extends ImplicitsTypeAnnotator { + public static class PICODefaultTypeAnnotator extends DefaultForTypeAnnotator { public PICODefaultTypeAnnotator(AnnotatedTypeFactory typeFactory) { super(typeFactory); diff --git a/src/main/java/pico/typecheck/PICOChecker.java b/src/main/java/pico/typecheck/PICOChecker.java index 8338604..6303429 100644 --- a/src/main/java/pico/typecheck/PICOChecker.java +++ b/src/main/java/pico/typecheck/PICOChecker.java @@ -14,7 +14,7 @@ public class PICOChecker extends InitializationChecker { public PICOChecker() { - super(true); + super(); } @Override diff --git a/src/main/java/pico/typecheck/PICOTypeUtil.java b/src/main/java/pico/typecheck/PICOTypeUtil.java index 8d32462..a8ac812 100644 --- a/src/main/java/pico/typecheck/PICOTypeUtil.java +++ b/src/main/java/pico/typecheck/PICOTypeUtil.java @@ -13,7 +13,7 @@ import com.sun.source.tree.UnaryTree; import com.sun.source.tree.VariableTree; import com.sun.source.util.TreePath; -import org.checkerframework.framework.qual.ImplicitFor; +import org.checkerframework.framework.qual.DefaultFor; import org.checkerframework.framework.qual.TypeKind; import org.checkerframework.framework.type.AnnotatedTypeFactory; import org.checkerframework.framework.type.AnnotatedTypeMirror; @@ -63,27 +63,27 @@ public class PICOTypeUtil { sideEffectingUnaryOperators.add(Tree.Kind.PREFIX_DECREMENT); } - private static boolean isInTypesOfImplicitForOfImmutable(AnnotatedTypeMirror atm) { - ImplicitFor implicitFor = Immutable.class.getAnnotation(ImplicitFor.class); - assert implicitFor != null; - assert implicitFor.types() != null; - for (TypeKind typeKind : implicitFor.types()) { + private static boolean isInTypeKindsOfDefaultForOfImmutable(AnnotatedTypeMirror atm) { + DefaultFor defaultFor = Immutable.class.getAnnotation(DefaultFor.class); + assert defaultFor != null; + for (TypeKind typeKind : defaultFor.typeKinds()) { if (typeKind.name() == atm.getKind().name()) return true; } return false; } - private static boolean isInTypeNamesOfImplicitForOfImmutable(AnnotatedTypeMirror atm) { + private static boolean isInTypesOfDefaultForOfImmutable(AnnotatedTypeMirror atm) { if (atm.getKind().name() != TypeKind.DECLARED.name()) { return false; } - ImplicitFor implicitFor = Immutable.class.getAnnotation(ImplicitFor.class); - assert implicitFor != null; - assert implicitFor.typeNames() != null; - Class[] typeNames = implicitFor.typeNames(); + DefaultFor defaultFor = Immutable.class.getAnnotation(DefaultFor.class); + assert defaultFor != null; + Class[] types = defaultFor.types(); String fqn = TypesUtils.getQualifiedName((DeclaredType) atm.getUnderlyingType()).toString(); - for (int i = 0; i < typeNames.length; i++) { - if (typeNames[i].getCanonicalName().toString().contentEquals(fqn)) return true; + for (Class type : types) { + if (type.getCanonicalName().contentEquals(fqn)) { + return true; + } } return false; } @@ -91,8 +91,8 @@ private static boolean isInTypeNamesOfImplicitForOfImmutable(AnnotatedTypeMirror /**Method to determine if the underlying type is implicitly immutable. This method is consistent * with the types and typeNames that are in @ImplicitFor in the definition of @Immutable qualifier*/ public static boolean isImplicitlyImmutableType(AnnotatedTypeMirror atm) { - return isInTypesOfImplicitForOfImmutable(atm) - || isInTypeNamesOfImplicitForOfImmutable(atm) + return isInTypeKindsOfDefaultForOfImmutable(atm) + || isInTypesOfDefaultForOfImmutable(atm) || isEnumOrEnumConstant(atm); } diff --git a/src/main/java/qual/Bottom.java b/src/main/java/qual/Bottom.java index cf17cd4..17eab30 100644 --- a/src/main/java/qual/Bottom.java +++ b/src/main/java/qual/Bottom.java @@ -2,21 +2,17 @@ import org.checkerframework.framework.qual.DefaultFor; import org.checkerframework.framework.qual.DefaultInUncheckedCodeFor; -import org.checkerframework.framework.qual.ImplicitFor; -import org.checkerframework.framework.qual.LiteralKind; import org.checkerframework.framework.qual.SubtypeOf; import org.checkerframework.framework.qual.TargetLocations; import org.checkerframework.framework.qual.TypeUseLocation; import java.lang.annotation.Documented; -import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @SubtypeOf({Mutable.class, Immutable.class, PolyMutable.class, ReceiverDependantMutable.class}) @DefaultFor({ TypeUseLocation.LOWER_BOUND }) -@ImplicitFor(literals = {LiteralKind.NULL}) @Documented @Retention(RetentionPolicy.RUNTIME) // Stop allowing any explicit usage of @Bottom qualifier in source. As it causes difficulty to diff --git a/src/main/java/qual/Immutable.java b/src/main/java/qual/Immutable.java index eb66e07..f48960a 100644 --- a/src/main/java/qual/Immutable.java +++ b/src/main/java/qual/Immutable.java @@ -1,7 +1,8 @@ package qual; -import org.checkerframework.framework.qual.ImplicitFor; +import org.checkerframework.framework.qual.DefaultFor; import org.checkerframework.framework.qual.LiteralKind; +import org.checkerframework.framework.qual.QualifierForLiterals; import org.checkerframework.framework.qual.SubtypeOf; import org.checkerframework.framework.qual.TypeKind; @@ -14,13 +15,13 @@ import java.math.BigInteger; @SubtypeOf({Readonly.class}) +@QualifierForLiterals({LiteralKind.PRIMITIVE, LiteralKind.STRING}) @Documented @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.TYPE_USE, ElementType.TYPE_PARAMETER}) -@ImplicitFor(typeNames={Enum.class, String.class, Double.class, Boolean.class, Byte.class, +@DefaultFor(types={Enum.class, String.class, Double.class, Boolean.class, Byte.class, Character.class, Float.class, Integer.class, Long.class, Short.class, Number.class, BigDecimal.class, BigInteger.class}, - literals = { LiteralKind.PRIMITIVE, LiteralKind.STRING}, - types = { TypeKind.INT, TypeKind.BYTE, TypeKind.SHORT, TypeKind.BOOLEAN, + typeKinds = { TypeKind.INT, TypeKind.BYTE, TypeKind.SHORT, TypeKind.BOOLEAN, TypeKind.LONG, TypeKind.CHAR, TypeKind.FLOAT, TypeKind.DOUBLE }) public @interface Immutable {} diff --git a/src/main/java/qual/Readonly.java b/src/main/java/qual/Readonly.java index f80ef75..17ce4b5 100644 --- a/src/main/java/qual/Readonly.java +++ b/src/main/java/qual/Readonly.java @@ -1,7 +1,6 @@ package qual; import org.checkerframework.framework.qual.DefaultInUncheckedCodeFor; -import org.checkerframework.framework.qual.ImplicitFor; import org.checkerframework.framework.qual.SubtypeOf; import org.checkerframework.framework.qual.TypeUseLocation; From b74dc2a222a06f5d11d9e1be34927a0de184746d Mon Sep 17 00:00:00 2001 From: lnsun Date: Thu, 21 Nov 2019 17:24:21 -0500 Subject: [PATCH 020/144] api updates --- .../PICOInferenceAnnotatedTypeFactory.java | 12 +++---- .../PICOInferenceRealTypeFactory.java | 25 +++------------ .../pico/inference/PICOInferenceVisitor.java | 4 +-- .../typecheck/PICOAnnotatedTypeFactory.java | 32 ++++++------------- src/main/java/pico/typecheck/PICOChecker.java | 2 +- .../java/pico/typecheck/PICOTypeUtil.java | 20 ++++++------ src/main/java/pico/typecheck/PICOVisitor.java | 2 +- src/main/java/qual/Bottom.java | 10 +----- src/main/java/qual/Immutable.java | 9 +++--- src/main/java/qual/Readonly.java | 1 - 10 files changed, 40 insertions(+), 77 deletions(-) diff --git a/src/main/java/pico/inference/PICOInferenceAnnotatedTypeFactory.java b/src/main/java/pico/inference/PICOInferenceAnnotatedTypeFactory.java index 386a4de..5ffdc69 100644 --- a/src/main/java/pico/inference/PICOInferenceAnnotatedTypeFactory.java +++ b/src/main/java/pico/inference/PICOInferenceAnnotatedTypeFactory.java @@ -20,7 +20,7 @@ import org.checkerframework.framework.type.AnnotatedTypeFactory; import org.checkerframework.framework.type.AnnotatedTypeMirror; import org.checkerframework.framework.type.AnnotatedTypeMirror.AnnotatedDeclaredType; -import org.checkerframework.framework.type.treeannotator.ImplicitsTreeAnnotator; +import org.checkerframework.framework.type.treeannotator.LiteralTreeAnnotator; import org.checkerframework.framework.type.treeannotator.ListTreeAnnotator; import org.checkerframework.framework.type.treeannotator.PropagationTreeAnnotator; import org.checkerframework.framework.type.treeannotator.TreeAnnotator; @@ -28,7 +28,7 @@ import org.checkerframework.framework.type.typeannotator.TypeAnnotator; import org.checkerframework.javacutil.Pair; import org.checkerframework.javacutil.TreeUtils; -import pico.typecheck.PICOAnnotatedTypeFactory.PICOImplicitsTypeAnnotator; +import pico.typecheck.PICOAnnotatedTypeFactory.PICODefaultForTypeAnnotator; import pico.typecheck.PICOTypeUtil; import javax.lang.model.element.AnnotationMirror; @@ -62,17 +62,17 @@ public PICOInferenceAnnotatedTypeFactory(InferenceChecker inferenceChecker, bool // be inserted results anyway). @Override public TreeAnnotator createTreeAnnotator() { - return new ListTreeAnnotator(new ImplicitsTreeAnnotator(this), + return new ListTreeAnnotator(new LiteralTreeAnnotator(this), new PICOInferencePropagationTreeAnnotator(this), new InferenceTreeAnnotator(this, realChecker, realTypeFactory, variableAnnotator, slotManager)); } @Override protected TypeAnnotator createTypeAnnotator() { - // Reuse PICOImplicitsTypeAnnotator even in inference mode. Because the type annotator's implementation + // Reuse PICODefaultForTypeAnnotator even in inference mode. Because the type annotator's implementation // are the same. The only drawback is that naming is not good(doesn't include "Inference"), thus may be // hard to debug - return new ListTypeAnnotator(super.createTypeAnnotator(), new PICOImplicitsTypeAnnotator(this)); + return new ListTypeAnnotator(super.createTypeAnnotator(), new PICODefaultForTypeAnnotator(this)); } @Override @@ -235,7 +235,7 @@ public Void visitTypeCast(TypeCastTree node, AnnotatedTypeMirror type) { return super.visitTypeCast(node, type); } - /**Because TreeAnnotator runs before ImplicitsTypeAnnotator, implicitly immutable types are not guaranteed + /**Because TreeAnnotator runs before DefaultForTypeAnnotator, implicitly immutable types are not guaranteed to always have immutable annotation. If this happens, we manually add immutable to type. */ private void applyImmutableIfImplicitlyImmutable(AnnotatedTypeMirror type) { if (PICOTypeUtil.isImplicitlyImmutableType(type)) { diff --git a/src/main/java/pico/inference/PICOInferenceRealTypeFactory.java b/src/main/java/pico/inference/PICOInferenceRealTypeFactory.java index 3c247ea..f159883 100644 --- a/src/main/java/pico/inference/PICOInferenceRealTypeFactory.java +++ b/src/main/java/pico/inference/PICOInferenceRealTypeFactory.java @@ -11,7 +11,6 @@ import java.util.List; import java.util.Set; -import javax.lang.model.element.AnnotationMirror; import javax.lang.model.element.Element; import org.checkerframework.common.basetype.BaseAnnotatedTypeFactory; @@ -19,8 +18,8 @@ import org.checkerframework.framework.qual.RelevantJavaTypes; import org.checkerframework.framework.type.AbstractViewpointAdapter; import org.checkerframework.framework.type.AnnotatedTypeMirror; -import org.checkerframework.framework.type.treeannotator.ImplicitsTreeAnnotator; import org.checkerframework.framework.type.treeannotator.ListTreeAnnotator; +import org.checkerframework.framework.type.treeannotator.LiteralTreeAnnotator; import org.checkerframework.framework.type.treeannotator.TreeAnnotator; import org.checkerframework.framework.type.typeannotator.IrrelevantTypeAnnotator; import org.checkerframework.framework.type.typeannotator.ListTypeAnnotator; @@ -31,7 +30,8 @@ import com.sun.source.tree.Tree; -import pico.typecheck.PICOAnnotatedTypeFactory.PICOImplicitsTypeAnnotator; +import pico.typecheck.PICOAnnotatedTypeFactory.PICODefaultForTypeAnnotator; +import pico.typecheck.PICOAnnotatedTypeFactory.PICOTypeAnnotator; import pico.typecheck.PICOAnnotatedTypeFactory.PICOPropagationTreeAnnotator; import pico.typecheck.PICOAnnotatedTypeFactory.PICOTreeAnnotator; import pico.typecheck.PICOAnnotatedTypeFactory.PICOTypeAnnotator; @@ -84,7 +84,7 @@ protected AbstractViewpointAdapter createViewpointAdapter() { protected TreeAnnotator createTreeAnnotator() { return new ListTreeAnnotator( new PICOPropagationTreeAnnotator(this), - new ImplicitsTreeAnnotator(this), + new LiteralTreeAnnotator(this), new PICOTreeAnnotator(this)); } @@ -109,7 +109,7 @@ protected TypeAnnotator createTypeAnnotator() { // method, so if one annotator already applied the annotations, the others won't apply twice at the // same location typeAnnotators.add(new PICOTypeAnnotator(this)); - typeAnnotators.add(new PICOImplicitsTypeAnnotator(this)); + typeAnnotators.add(new PICODefaultForTypeAnnotator(this)); return new ListTypeAnnotator(typeAnnotators); } @@ -127,21 +127,6 @@ public boolean getShouldDefaultTypeVarLocals() { return false; } - // Copied from PICOAnnotatedTypeFactory - @Override - protected void annotateInheritedFromClass(AnnotatedTypeMirror type, Set fromClass) { - // If interitted from class element is @Mutable or @Immutable, then apply this annotation to the usage type - if (fromClass.contains(MUTABLE) || fromClass.contains(IMMUTABLE)) { - super.annotateInheritedFromClass(type, fromClass); - return; - } - // If interitted from class element is @ReceiverDependantMutable, then don't apply and wait for @Mutable - // (default qualifier in hierarchy to be applied to the usage type). This is to avoid having @ReceiverDependantMutable - // on type usages as a default behaviour. By default, @Mutable is better used as the type for usages that - // don't have explicit annotation. - return;// Don't add annotations from class element - } - @Override public void addComputedTypeAnnotations(Element elt, AnnotatedTypeMirror type) { PICOTypeUtil.addDefaultForField(this, type, elt); diff --git a/src/main/java/pico/inference/PICOInferenceVisitor.java b/src/main/java/pico/inference/PICOInferenceVisitor.java index 83dedd6..83c37eb 100644 --- a/src/main/java/pico/inference/PICOInferenceVisitor.java +++ b/src/main/java/pico/inference/PICOInferenceVisitor.java @@ -535,7 +535,7 @@ private void reportFieldOrArrayWriteError(Tree node, ExpressionTree variable, An * 2) In constructor * 3) In instance method, declared receiver is @UnderInitialized * - * @param node assignment tree that might be initializing an object + * @param variable assignment tree that might be initializing an object * @return true if the assignment tree is initializing an object * * @see #hasUnderInitializationDeclaredReceiver(MethodTree) @@ -621,7 +621,7 @@ public Void visitMethodInvocation(MethodInvocationTree node, Void p) { // Only check invocability if it's super call, as non-super call is already checked // by super implementation(of course in both cases, invocability is not checked when // invoking static methods) - if (!ElementUtils.isStatic(invokedMethodElement) && TreeUtils.isSuperCall(node)) { + if (!ElementUtils.isStatic(invokedMethodElement) && TreeUtils.isSuperConstructorCall(node)) { checkMethodInvocability(invokedMethod, node); } return null; diff --git a/src/main/java/pico/typecheck/PICOAnnotatedTypeFactory.java b/src/main/java/pico/typecheck/PICOAnnotatedTypeFactory.java index db6dd28..77e5c98 100644 --- a/src/main/java/pico/typecheck/PICOAnnotatedTypeFactory.java +++ b/src/main/java/pico/typecheck/PICOAnnotatedTypeFactory.java @@ -34,11 +34,11 @@ import org.checkerframework.framework.type.AnnotatedTypeMirror.AnnotatedExecutableType; import org.checkerframework.framework.type.QualifierHierarchy; import org.checkerframework.framework.type.ViewpointAdapter; -import org.checkerframework.framework.type.treeannotator.ImplicitsTreeAnnotator; +import org.checkerframework.framework.type.treeannotator.LiteralTreeAnnotator; import org.checkerframework.framework.type.treeannotator.ListTreeAnnotator; import org.checkerframework.framework.type.treeannotator.PropagationTreeAnnotator; import org.checkerframework.framework.type.treeannotator.TreeAnnotator; -import org.checkerframework.framework.type.typeannotator.ImplicitsTypeAnnotator; +import org.checkerframework.framework.type.typeannotator.DefaultForTypeAnnotator; import org.checkerframework.framework.type.typeannotator.IrrelevantTypeAnnotator; import org.checkerframework.framework.type.typeannotator.ListTypeAnnotator; import org.checkerframework.framework.type.typeannotator.PropagationTypeAnnotator; @@ -78,7 +78,7 @@ public class PICOAnnotatedTypeFactory extends InitializationAnnotatedTypeFactory PICOStore, PICOTransfer, PICOAnalysis> { public PICOAnnotatedTypeFactory(BaseTypeChecker checker) { - super(checker, true); + super(checker); postInit(); // PICO aliasing is not implemented correctly // remove for now @@ -112,7 +112,7 @@ protected ViewpointAdapter createViewpointAdapter() { protected TreeAnnotator createTreeAnnotator() { return new ListTreeAnnotator( new PICOPropagationTreeAnnotator(this), - new ImplicitsTreeAnnotator(this), + new LiteralTreeAnnotator(this), new CommitmentTreeAnnotator(this), new PICOTreeAnnotator(this)); } @@ -138,7 +138,7 @@ protected TypeAnnotator createTypeAnnotator() { // method, so if one annotator already applied the annotations, the others won't apply twice at the // same location typeAnnotators.add(new PICOTypeAnnotator(this)); - typeAnnotators.add(new PICOImplicitsTypeAnnotator(this)); + typeAnnotators.add(new PICODefaultForTypeAnnotator(this)); typeAnnotators.add(new CommitmentTypeAnnotator(this)); return new ListTypeAnnotator(typeAnnotators); } @@ -183,20 +183,6 @@ public void addComputedTypeAnnotations(Element elt, AnnotatedTypeMirror type) { super.addComputedTypeAnnotations(elt, type); } - @Override - protected void annotateInheritedFromClass(AnnotatedTypeMirror type, Set fromClass) { - // If interitted from class element is @Mutable or @Immutable, then apply this annotation to the usage type - if (fromClass.contains(MUTABLE) || fromClass.contains(IMMUTABLE)) { - super.annotateInheritedFromClass(type, fromClass); - return; - } - // If interitted from class element is @ReceiverDependantMutable, then don't apply and wait for @Mutable - // (default qualifier in hierarchy to be applied to the usage type). This is to avoid having @ReceiverDependantMutable - // on type usages as a default behaviour. By default, @Mutable is better used as the type for usages that - // don't have explicit annotation. - return;// Don't add annotations from class element - } - /**This method gets lhs WITH flow sensitive refinement*/ // TODO Should refactor super class to avoid too much duplicate code. // This method is pretty hacky right now. @@ -389,7 +375,7 @@ public Void visitTypeCast(TypeCastTree node, AnnotatedTypeMirror type) { return super.visitTypeCast(node, type); } - /**Because TreeAnnotator runs before ImplicitsTypeAnnotator, implicitly immutable types are not guaranteed + /**Because TreeAnnotator runs before DefaultForTypeAnnotator, implicitly immutable types are not guaranteed to always have immutable annotation. If this happens, we manually add immutable to type. We use addMissingAnnotations because we want to respect existing annotation on type*/ private void applyImmutableIfImplicitlyImmutable(AnnotatedTypeMirror type) { @@ -461,9 +447,9 @@ public Void visitExecutable(AnnotatedExecutableType t, Void p) { } - public static class PICOImplicitsTypeAnnotator extends ImplicitsTypeAnnotator { + public static class PICODefaultForTypeAnnotator extends DefaultForTypeAnnotator { - public PICOImplicitsTypeAnnotator(AnnotatedTypeFactory typeFactory) { + public PICODefaultForTypeAnnotator(AnnotatedTypeFactory typeFactory) { super(typeFactory); } @@ -493,7 +479,7 @@ protected Void scan(AnnotatedTypeMirror type, Void p) { // TODO Right now, instance method receiver cannot inherit bound annotation from class element, and // this caused the inconsistency when accessing the type of receiver while visiting the method and // while visiting the variable tree. Implicit annotation can be inserted to method receiver via - // extending ImplicitsTypeAnnotator; But InheritedFromClassAnnotator cannot be inheritted because its + // extending DefaultForTypeAnnotator; But InheritedFromClassAnnotator cannot be inheritted because its // constructor is private and I can't override it to also inherit bound annotation from class element // to the declared receiver type of instance methods. To view the details, look at ImmutableClass1.java // testcase. diff --git a/src/main/java/pico/typecheck/PICOChecker.java b/src/main/java/pico/typecheck/PICOChecker.java index 8338604..6303429 100644 --- a/src/main/java/pico/typecheck/PICOChecker.java +++ b/src/main/java/pico/typecheck/PICOChecker.java @@ -14,7 +14,7 @@ public class PICOChecker extends InitializationChecker { public PICOChecker() { - super(true); + super(); } @Override diff --git a/src/main/java/pico/typecheck/PICOTypeUtil.java b/src/main/java/pico/typecheck/PICOTypeUtil.java index 8d32462..994f1d8 100644 --- a/src/main/java/pico/typecheck/PICOTypeUtil.java +++ b/src/main/java/pico/typecheck/PICOTypeUtil.java @@ -13,7 +13,7 @@ import com.sun.source.tree.UnaryTree; import com.sun.source.tree.VariableTree; import com.sun.source.util.TreePath; -import org.checkerframework.framework.qual.ImplicitFor; +import org.checkerframework.framework.qual.DefaultFor; import org.checkerframework.framework.qual.TypeKind; import org.checkerframework.framework.type.AnnotatedTypeFactory; import org.checkerframework.framework.type.AnnotatedTypeMirror; @@ -64,23 +64,23 @@ public class PICOTypeUtil { } private static boolean isInTypesOfImplicitForOfImmutable(AnnotatedTypeMirror atm) { - ImplicitFor implicitFor = Immutable.class.getAnnotation(ImplicitFor.class); - assert implicitFor != null; - assert implicitFor.types() != null; - for (TypeKind typeKind : implicitFor.types()) { - if (typeKind.name() == atm.getKind().name()) return true; + DefaultFor defaultFor = Immutable.class.getAnnotation(DefaultFor.class); + assert defaultFor != null; + assert defaultFor.typeKinds() != null; + for (TypeKind typeKind : defaultFor.typeKinds()) { + if (typeKind.name().equals(atm.getKind().name())) return true; } return false; } private static boolean isInTypeNamesOfImplicitForOfImmutable(AnnotatedTypeMirror atm) { - if (atm.getKind().name() != TypeKind.DECLARED.name()) { + if (!atm.getKind().name().equals(TypeKind.DECLARED.name())) { return false; } - ImplicitFor implicitFor = Immutable.class.getAnnotation(ImplicitFor.class); + DefaultFor implicitFor = Immutable.class.getAnnotation(DefaultFor.class); assert implicitFor != null; - assert implicitFor.typeNames() != null; - Class[] typeNames = implicitFor.typeNames(); + assert implicitFor.types() != null; + Class[] typeNames = implicitFor.types(); String fqn = TypesUtils.getQualifiedName((DeclaredType) atm.getUnderlyingType()).toString(); for (int i = 0; i < typeNames.length; i++) { if (typeNames[i].getCanonicalName().toString().contentEquals(fqn)) return true; diff --git a/src/main/java/pico/typecheck/PICOVisitor.java b/src/main/java/pico/typecheck/PICOVisitor.java index 1e46d0c..8f111bb 100644 --- a/src/main/java/pico/typecheck/PICOVisitor.java +++ b/src/main/java/pico/typecheck/PICOVisitor.java @@ -385,7 +385,7 @@ public Void visitMethodInvocation(MethodInvocationTree node, Void p) { // Only check invocability if it's super call, as non-super call is already checked // by super implementation(of course in both cases, invocability is not checked when // invoking static methods) - if (!ElementUtils.isStatic(invokedMethodElement) && TreeUtils.isSuperCall(node)) { + if (!ElementUtils.isStatic(invokedMethodElement) && TreeUtils.isSuperConstructorCall(node)) { checkMethodInvocability(invokedMethod, node); } return null; diff --git a/src/main/java/qual/Bottom.java b/src/main/java/qual/Bottom.java index cf17cd4..5306a1b 100644 --- a/src/main/java/qual/Bottom.java +++ b/src/main/java/qual/Bottom.java @@ -1,22 +1,14 @@ package qual; -import org.checkerframework.framework.qual.DefaultFor; -import org.checkerframework.framework.qual.DefaultInUncheckedCodeFor; -import org.checkerframework.framework.qual.ImplicitFor; -import org.checkerframework.framework.qual.LiteralKind; -import org.checkerframework.framework.qual.SubtypeOf; -import org.checkerframework.framework.qual.TargetLocations; -import org.checkerframework.framework.qual.TypeUseLocation; +import org.checkerframework.framework.qual.*; import java.lang.annotation.Documented; -import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @SubtypeOf({Mutable.class, Immutable.class, PolyMutable.class, ReceiverDependantMutable.class}) @DefaultFor({ TypeUseLocation.LOWER_BOUND }) -@ImplicitFor(literals = {LiteralKind.NULL}) @Documented @Retention(RetentionPolicy.RUNTIME) // Stop allowing any explicit usage of @Bottom qualifier in source. As it causes difficulty to diff --git a/src/main/java/qual/Immutable.java b/src/main/java/qual/Immutable.java index eb66e07..b5e542b 100644 --- a/src/main/java/qual/Immutable.java +++ b/src/main/java/qual/Immutable.java @@ -1,6 +1,7 @@ package qual; -import org.checkerframework.framework.qual.ImplicitFor; +import org.checkerframework.framework.qual.DefaultFor; +import org.checkerframework.framework.qual.QualifierForLiterals; import org.checkerframework.framework.qual.LiteralKind; import org.checkerframework.framework.qual.SubtypeOf; @@ -17,10 +18,10 @@ @Documented @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.TYPE_USE, ElementType.TYPE_PARAMETER}) -@ImplicitFor(typeNames={Enum.class, String.class, Double.class, Boolean.class, Byte.class, +@DefaultFor(types={Enum.class, String.class, Double.class, Boolean.class, Byte.class, Character.class, Float.class, Integer.class, Long.class, Short.class, Number.class, BigDecimal.class, BigInteger.class}, - literals = { LiteralKind.PRIMITIVE, LiteralKind.STRING}, - types = { TypeKind.INT, TypeKind.BYTE, TypeKind.SHORT, TypeKind.BOOLEAN, + typeKinds = { TypeKind.INT, TypeKind.BYTE, TypeKind.SHORT, TypeKind.BOOLEAN, TypeKind.LONG, TypeKind.CHAR, TypeKind.FLOAT, TypeKind.DOUBLE }) +@QualifierForLiterals({ LiteralKind.PRIMITIVE, LiteralKind.STRING}) public @interface Immutable {} diff --git a/src/main/java/qual/Readonly.java b/src/main/java/qual/Readonly.java index f80ef75..17ce4b5 100644 --- a/src/main/java/qual/Readonly.java +++ b/src/main/java/qual/Readonly.java @@ -1,7 +1,6 @@ package qual; import org.checkerframework.framework.qual.DefaultInUncheckedCodeFor; -import org.checkerframework.framework.qual.ImplicitFor; import org.checkerframework.framework.qual.SubtypeOf; import org.checkerframework.framework.qual.TypeUseLocation; From 32c68528b5e7398553dc9fb529ce6b4c4250ab29 Mon Sep 17 00:00:00 2001 From: lnsun Date: Thu, 21 Nov 2019 17:40:09 -0500 Subject: [PATCH 021/144] typo --- src/main/java/pico/inference/PICOInferenceVisitor.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/pico/inference/PICOInferenceVisitor.java b/src/main/java/pico/inference/PICOInferenceVisitor.java index 83c37eb..34be4ae 100644 --- a/src/main/java/pico/inference/PICOInferenceVisitor.java +++ b/src/main/java/pico/inference/PICOInferenceVisitor.java @@ -553,7 +553,7 @@ private boolean isInitializingObject(ExpressionTree variable) { } MethodTree enclosingMethod = TreeUtils.enclosingMethod(treePath); - // No possibility of initialiazing object if the assignment is not within constructor or method(both MethodTree) + // No possibility of initializing object if the assignment is not within constructor or method(both MethodTree) if (enclosingMethod == null) return false; // At this point, we already know that this assignment is field assignment within a method if (TreeUtils.isConstructor(enclosingMethod) || hasUnderInitializationDeclaredReceiver(enclosingMethod)) { From e6892fa0f898c88b11b215283a947101a5d7a602 Mon Sep 17 00:00:00 2001 From: lnsun <57457122+lnsun@users.noreply.github.com> Date: Mon, 2 Dec 2019 17:32:57 -0500 Subject: [PATCH 022/144] default-for annotator, default bottom on null, allow readonly as upper bound --- .../PICOInferenceAnnotatedTypeFactory.java | 7 +++ .../PICOInferenceRealTypeFactory.java | 11 +++-- .../pico/inference/PICOInferenceVisitor.java | 4 +- .../typecheck/PICOAnnotatedTypeFactory.java | 46 +++++++++++++++++-- src/main/java/pico/typecheck/PICOVisitor.java | 2 +- src/main/java/qual/Bottom.java | 2 +- 6 files changed, 60 insertions(+), 12 deletions(-) diff --git a/src/main/java/pico/inference/PICOInferenceAnnotatedTypeFactory.java b/src/main/java/pico/inference/PICOInferenceAnnotatedTypeFactory.java index 5ffdc69..1bd81a5 100644 --- a/src/main/java/pico/inference/PICOInferenceAnnotatedTypeFactory.java +++ b/src/main/java/pico/inference/PICOInferenceAnnotatedTypeFactory.java @@ -24,10 +24,12 @@ import org.checkerframework.framework.type.treeannotator.ListTreeAnnotator; import org.checkerframework.framework.type.treeannotator.PropagationTreeAnnotator; import org.checkerframework.framework.type.treeannotator.TreeAnnotator; +import org.checkerframework.framework.type.typeannotator.DefaultQualifierForUseTypeAnnotator; import org.checkerframework.framework.type.typeannotator.ListTypeAnnotator; import org.checkerframework.framework.type.typeannotator.TypeAnnotator; import org.checkerframework.javacutil.Pair; import org.checkerframework.javacutil.TreeUtils; +import pico.typecheck.PICOAnnotatedTypeFactory; import pico.typecheck.PICOAnnotatedTypeFactory.PICODefaultForTypeAnnotator; import pico.typecheck.PICOTypeUtil; @@ -243,4 +245,9 @@ private void applyImmutableIfImplicitlyImmutable(AnnotatedTypeMirror type) { } } } + + @Override + protected DefaultQualifierForUseTypeAnnotator createDefaultForUseTypeAnnotator() { + return new PICOAnnotatedTypeFactory.PICOQualifierForUseTypeAnnotator(this); + } } diff --git a/src/main/java/pico/inference/PICOInferenceRealTypeFactory.java b/src/main/java/pico/inference/PICOInferenceRealTypeFactory.java index f159883..d44f92a 100644 --- a/src/main/java/pico/inference/PICOInferenceRealTypeFactory.java +++ b/src/main/java/pico/inference/PICOInferenceRealTypeFactory.java @@ -21,10 +21,7 @@ import org.checkerframework.framework.type.treeannotator.ListTreeAnnotator; import org.checkerframework.framework.type.treeannotator.LiteralTreeAnnotator; import org.checkerframework.framework.type.treeannotator.TreeAnnotator; -import org.checkerframework.framework.type.typeannotator.IrrelevantTypeAnnotator; -import org.checkerframework.framework.type.typeannotator.ListTypeAnnotator; -import org.checkerframework.framework.type.typeannotator.PropagationTypeAnnotator; -import org.checkerframework.framework.type.typeannotator.TypeAnnotator; +import org.checkerframework.framework.type.typeannotator.*; import org.checkerframework.javacutil.BugInCF; import org.checkerframework.javacutil.TreeUtils; @@ -168,4 +165,10 @@ public AnnotatedTypeMirror getAnnotatedTypeLhs(Tree lhsTree) { return result; } +// +// @Override +// protected DefaultQualifierForUseTypeAnnotator createDefaultForUseTypeAnnotator() { +// return new PICOAnnotatedTypeFactory.PICOQualifierForUseTypeAnnotator(this); +// } + } diff --git a/src/main/java/pico/inference/PICOInferenceVisitor.java b/src/main/java/pico/inference/PICOInferenceVisitor.java index 34be4ae..3850ead 100644 --- a/src/main/java/pico/inference/PICOInferenceVisitor.java +++ b/src/main/java/pico/inference/PICOInferenceVisitor.java @@ -89,7 +89,8 @@ public boolean isValidUse(AnnotatedDeclaredType declarationType, AnnotatedDeclar return true; } else { AnnotationMirror declared = declarationType.getAnnotationInHierarchy(READONLY); - if (AnnotationUtils.areSame(declared, RECEIVER_DEPENDANT_MUTABLE)) { + // declarationType is now upper bound. Could be READONLY + if (AnnotationUtils.areSame(declared, RECEIVER_DEPENDANT_MUTABLE) || AnnotationUtils.areSame(declared, READONLY)) { return true; } assert AnnotationUtils.areSame(declared, MUTABLE) || AnnotationUtils.areSame(declared, IMMUTABLE); @@ -105,6 +106,7 @@ public boolean isValidUse(AnnotatedDeclaredType declarationType, AnnotatedDeclar return true; } + System.out.println(useType.toString() + " " + declared + " " + used); return false; } } diff --git a/src/main/java/pico/typecheck/PICOAnnotatedTypeFactory.java b/src/main/java/pico/typecheck/PICOAnnotatedTypeFactory.java index 77e5c98..870182d 100644 --- a/src/main/java/pico/typecheck/PICOAnnotatedTypeFactory.java +++ b/src/main/java/pico/typecheck/PICOAnnotatedTypeFactory.java @@ -38,11 +38,7 @@ import org.checkerframework.framework.type.treeannotator.ListTreeAnnotator; import org.checkerframework.framework.type.treeannotator.PropagationTreeAnnotator; import org.checkerframework.framework.type.treeannotator.TreeAnnotator; -import org.checkerframework.framework.type.typeannotator.DefaultForTypeAnnotator; -import org.checkerframework.framework.type.typeannotator.IrrelevantTypeAnnotator; -import org.checkerframework.framework.type.typeannotator.ListTypeAnnotator; -import org.checkerframework.framework.type.typeannotator.PropagationTypeAnnotator; -import org.checkerframework.framework.type.typeannotator.TypeAnnotator; +import org.checkerframework.framework.type.typeannotator.*; import org.checkerframework.framework.util.MultiGraphQualifierHierarchy.MultiGraphFactory; import org.checkerframework.javacutil.BugInCF; import org.checkerframework.javacutil.ElementUtils; @@ -447,6 +443,46 @@ public Void visitExecutable(AnnotatedExecutableType t, Void p) { } +// @Override +// protected void addAnnotationsFromDefaultQualifierForUse(@Nullable Element element, AnnotatedTypeMirror type) { +// if (element != null) { +// if (element.getAnnotationMirrors().contains(IMMUTABLE) || element.getAnnotationMirrors().contains(MUTABLE)) { +// super.addAnnotationsFromDefaultQualifierForUse(element, type); +// } +// } else { +//// super.addAnnotationsFromDefaultQualifierForUse(element, type); +// } +// } + +// + + + @Override + protected DefaultQualifierForUseTypeAnnotator createDefaultForUseTypeAnnotator() { + return new PICOQualifierForUseTypeAnnotator(this); + } + + // @DefaultQFU + public static class PICOQualifierForUseTypeAnnotator extends DefaultQualifierForUseTypeAnnotator { + + public PICOQualifierForUseTypeAnnotator(AnnotatedTypeFactory typeFactory) { + super(typeFactory); + } + + @Override + public Void visitDeclared(AnnotatedTypeMirror.AnnotatedDeclaredType type, Void aVoid) { + + Element element = type.getUnderlyingType().asElement(); + Set annosToApply = getDefaultAnnosForUses(element); + if (annosToApply.contains(MUTABLE) || annosToApply.contains(IMMUTABLE)) { + type.addMissingAnnotations(annosToApply); + } else { + System.out.println("\u001B[31m" + type + " | " + annosToApply + "\u001B[0m"); + } + return null; + } + } + public static class PICODefaultForTypeAnnotator extends DefaultForTypeAnnotator { public PICODefaultForTypeAnnotator(AnnotatedTypeFactory typeFactory) { diff --git a/src/main/java/pico/typecheck/PICOVisitor.java b/src/main/java/pico/typecheck/PICOVisitor.java index 8f111bb..25af379 100644 --- a/src/main/java/pico/typecheck/PICOVisitor.java +++ b/src/main/java/pico/typecheck/PICOVisitor.java @@ -94,7 +94,7 @@ public boolean isValidUse(AnnotatedDeclaredType declarationType, AnnotatedDeclar // // Object // return true; // } - if (AnnotationUtils.areSame(declared, RECEIVER_DEPENDANT_MUTABLE)) { + if (AnnotationUtils.areSame(declared, RECEIVER_DEPENDANT_MUTABLE) || AnnotationUtils.areSame(declared, READONLY)) { // Element is declared with @ReceiverDependantMutable bound, any instantiation is allowed. We don't use // a subtype check to validate the correct usage here. Because @Readonly is the super type of // @ReceiverDependantMutable, but it's still considered valid usage. diff --git a/src/main/java/qual/Bottom.java b/src/main/java/qual/Bottom.java index 5306a1b..8155e5b 100644 --- a/src/main/java/qual/Bottom.java +++ b/src/main/java/qual/Bottom.java @@ -8,7 +8,7 @@ import java.lang.annotation.Target; @SubtypeOf({Mutable.class, Immutable.class, PolyMutable.class, ReceiverDependantMutable.class}) -@DefaultFor({ TypeUseLocation.LOWER_BOUND }) +@DefaultFor(value = { TypeUseLocation.LOWER_BOUND }, typeKinds = {TypeKind.NULL}) @Documented @Retention(RetentionPolicy.RUNTIME) // Stop allowing any explicit usage of @Bottom qualifier in source. As it causes difficulty to From de0969f2c0e0992cedaa39ba56ab277eac7e45c5 Mon Sep 17 00:00:00 2001 From: xingweitian <13183370+xingweitian@users.noreply.github.com> Date: Mon, 29 Jul 2019 20:03:08 -0400 Subject: [PATCH 023/144] Implement super constructor call check. Replace the error key "super.constructor.invocation.incompatible" with "super.invocation.invalid". Tweaks test files to pass the test. --- .../pico/inference/PICOInferenceVisitor.java | 2 +- src/main/java/pico/typecheck/PICOVisitor.java | 29 +++++++++++++++++++ .../java/pico/typecheck/messages.properties | 1 - ...ructorInvocationInSubclassConstructor.java | 2 +- testinput/typecheck/AssignableExample.java | 2 +- .../ReceiverTypeOutsideConstructor.java | 18 ++++++------ testinput/typecheck/SuperClass.java | 4 +-- testinput/typecheck/SuperClass2.java | 4 +-- 8 files changed, 45 insertions(+), 17 deletions(-) diff --git a/src/main/java/pico/inference/PICOInferenceVisitor.java b/src/main/java/pico/inference/PICOInferenceVisitor.java index 3850ead..f4e6878 100644 --- a/src/main/java/pico/inference/PICOInferenceVisitor.java +++ b/src/main/java/pico/inference/PICOInferenceVisitor.java @@ -644,7 +644,7 @@ protected void checkMethodInvocability(AnnotatedExecutableType method, MethodInv // , otherwise it will cause inference result not typecheck checker.report( Result.failure( - "super.constructor.invocation.incompatible", subClassConstructorReturnType, superClassConstructorReturnType), node); + "super.invocation.invalid", subClassConstructorReturnType, superClassConstructorReturnType), node); } } super.checkMethodInvocability(method, node); diff --git a/src/main/java/pico/typecheck/PICOVisitor.java b/src/main/java/pico/typecheck/PICOVisitor.java index 25af379..332f627 100644 --- a/src/main/java/pico/typecheck/PICOVisitor.java +++ b/src/main/java/pico/typecheck/PICOVisitor.java @@ -8,6 +8,7 @@ import static pico.typecheck.PICOAnnotationMirrorHolder.READONLY; import static pico.typecheck.PICOAnnotationMirrorHolder.RECEIVER_DEPENDANT_MUTABLE; +import com.sun.source.util.TreePath; import java.util.HashMap; import java.util.HashSet; import java.util.List; @@ -21,6 +22,7 @@ import javax.lang.model.element.TypeElement; import javax.lang.model.element.VariableElement; +import org.checkerframework.checker.compilermsgs.qual.CompilerMessageKey; import org.checkerframework.checker.initialization.InitializationVisitor; import org.checkerframework.checker.initialization.qual.UnderInitialization; import org.checkerframework.common.basetype.BaseTypeChecker; @@ -493,4 +495,31 @@ protected void checkExtendsImplements(ClassTree classTree) { } } } + + /** + * The invoked constructor’s return type adapted to the invoking constructor’s return type must + * be a supertype of the invoking constructor’s return type. + * + * @param superCall the super invocation, e.g., "super()" + * @param errorKey the error key, e.g., "super.invocation.invalid" + */ + @Override + protected void checkThisOrSuperConstructorCall( + MethodInvocationTree superCall, @CompilerMessageKey String errorKey) { + TreePath path = atypeFactory.getPath(superCall); + MethodTree enclosingMethod = TreeUtils.enclosingMethod(path); + AnnotatedTypeMirror superType = atypeFactory.getAnnotatedType(superCall); + AnnotatedExecutableType constructorType = atypeFactory.getAnnotatedType(enclosingMethod); + AnnotationMirror superTypeMirror = superType.getAnnotationInHierarchy(READONLY); + AnnotationMirror constructorTypeMirror = + constructorType.getReturnType().getAnnotationInHierarchy(READONLY); + if (!atypeFactory + .getQualifierHierarchy() + .isSubtype(constructorTypeMirror, superTypeMirror)) { + checker.report( + Result.failure(errorKey, constructorTypeMirror, superCall, superTypeMirror), + superCall); + } + super.checkThisOrSuperConstructorCall(superCall, errorKey); + } } diff --git a/src/main/java/pico/typecheck/messages.properties b/src/main/java/pico/typecheck/messages.properties index 9040ed2..db36cfa 100644 --- a/src/main/java/pico/typecheck/messages.properties +++ b/src/main/java/pico/typecheck/messages.properties @@ -3,7 +3,6 @@ constructor.return.invalid=Invalid constructor return type: %s method.receiver.incompatible=Incompatible method receiver: %s class.bound.invalid=Invalid class bound: %s subclass.bound.incompatible=Incompatible subclass bound: %s -super.constructor.invocation.incompatible=Subclass constructor: %s is not compatible with super class constructor: %s illegal.field.write=Cannot write field via receiver: %s illegal.array.write=Cannot write array via receiver: %s static.receiverdependantmutable.forbidden=%s is forbidden in static context diff --git a/testinput/inference/inferrable/issue144/ConstructorInvocationInSubclassConstructor.java b/testinput/inference/inferrable/issue144/ConstructorInvocationInSubclassConstructor.java index f469dd1..958aaa0 100644 --- a/testinput/inference/inferrable/issue144/ConstructorInvocationInSubclassConstructor.java +++ b/testinput/inference/inferrable/issue144/ConstructorInvocationInSubclassConstructor.java @@ -15,7 +15,7 @@ public class ConstructorInvocationInSubclassConstructor { class SubClass extends ConstructorInvocationInSubclassConstructor { SubClass(Object p) { // Handled by PICOInferenceVisito##checkMethodInvocability - // :: fixable-error: (super.constructor.invocation.incompatible) + // :: fixable-error: (super.invocation.invalid) super(p); } } diff --git a/testinput/typecheck/AssignableExample.java b/testinput/typecheck/AssignableExample.java index a7d15d8..40fa3e7 100644 --- a/testinput/typecheck/AssignableExample.java +++ b/testinput/typecheck/AssignableExample.java @@ -28,7 +28,7 @@ void foo2(@Mutable AssignableExample this) { } } -// :: error: (super.constructor.invocation.incompatible) +// :: error: (super.invocation.invalid) @ReceiverDependantMutable class Subclass extends AssignableExample { void bar(@Immutable Subclass this) { // :: error: (illegal.field.write) diff --git a/testinput/typecheck/ReceiverTypeOutsideConstructor.java b/testinput/typecheck/ReceiverTypeOutsideConstructor.java index 62eb1e6..7162e32 100644 --- a/testinput/typecheck/ReceiverTypeOutsideConstructor.java +++ b/testinput/typecheck/ReceiverTypeOutsideConstructor.java @@ -31,13 +31,13 @@ class A { @Immutable class AIMS extends A {} -// :: error: (declaration.inconsistent.with.extends.clause) +// :: error: (declaration.inconsistent.with.extends.clause) :: error: (super.invocation.invalid) @ReceiverDependantMutable class ARDMS extends A {} -// :: error: (declaration.inconsistent.with.extends.clause) +// :: error: (declaration.inconsistent.with.extends.clause) :: error: (super.invocation.invalid) @Mutable class AMS extends A {} -// :: error: (declaration.inconsistent.with.extends.clause) +// :: error: (declaration.inconsistent.with.extends.clause) :: error: (super.invocation.invalid) class AUNKS extends A {} // ReceiverDependantMutable class @@ -68,14 +68,14 @@ class B { @Immutable class BIMS extends B {} -// :: error: (super.constructor.invocation.incompatible) +// :: error: (super.invocation.invalid) @ReceiverDependantMutable class BRDMS extends B {} -// :: error: (super.constructor.invocation.incompatible) +// :: error: (super.invocation.invalid) @Mutable class BMS extends B {} // mutable by default(TODO Does this make sense compared to defaulting to receiver-dependant-mutable?) -// :: error: (super.constructor.invocation.incompatible) +// :: error: (super.invocation.invalid) class BUNKS extends B {} // Mutable class @@ -106,13 +106,13 @@ class C { // :: error: (declaration.inconsistent.with.extends.clause) @Immutable class CIMS extends C {} -// :: error: (declaration.inconsistent.with.extends.clause) +// :: error: (declaration.inconsistent.with.extends.clause) :: error: (super.invocation.invalid) @ReceiverDependantMutable class CRDMS extends C {} -// :: error: (super.constructor.invocation.incompatible) +// :: error: (super.invocation.invalid) @Mutable class CMS extends C {} -// :: error: (super.constructor.invocation.incompatible) +// :: error: (super.invocation.invalid) class CUNKS extends C {} class D { diff --git a/testinput/typecheck/SuperClass.java b/testinput/typecheck/SuperClass.java index d857b7d..c55d9f3 100644 --- a/testinput/typecheck/SuperClass.java +++ b/testinput/typecheck/SuperClass.java @@ -20,7 +20,7 @@ void maliciouslyModifyDate(@Mutable SuperClass this){ class SubClass extends SuperClass{ @Mutable SubClass(){ - // :: error: (super.constructor.invocation.incompatible) + // :: error: (super.invocation.invalid) super(new @Immutable Date(1L)); } @@ -33,7 +33,7 @@ public static void main(String[] args) { @ReceiverDependantMutable class AnotherSubClass extends SuperClass{ @ReceiverDependantMutable AnotherSubClass(){ - // :: error: (super.constructor.invocation.incompatible) + // :: error: (super.invocation.invalid) super(new @Immutable Date(1L)); } diff --git a/testinput/typecheck/SuperClass2.java b/testinput/typecheck/SuperClass2.java index 00cfdee..1d5d567 100644 --- a/testinput/typecheck/SuperClass2.java +++ b/testinput/typecheck/SuperClass2.java @@ -35,7 +35,7 @@ public class SuperClass2{ class SubClass2 extends SuperClass2{ @Immutable SubClass2(){ // This is not ok any more - // :: error: (super.constructor.invocation.incompatible) + // :: error: (super.invocation.invalid) super(new @Mutable Date()); } } @@ -44,7 +44,7 @@ class SubClass2 extends SuperClass2{ class AnotherSubClass2 extends SuperClass2{ @ReceiverDependantMutable AnotherSubClass2(){ // This is not ok any more - // :: error: (super.constructor.invocation.incompatible) + // :: error: (super.invocation.invalid) super(new @Mutable Date()); } } From d823dcfa92d4721966d38baf56cd25868acee849 Mon Sep 17 00:00:00 2001 From: xingweitian <13183370+xingweitian@users.noreply.github.com> Date: Sun, 18 Aug 2019 22:56:16 -0400 Subject: [PATCH 024/144] Improve code and javadoc. --- src/main/java/pico/typecheck/PICOVisitor.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/pico/typecheck/PICOVisitor.java b/src/main/java/pico/typecheck/PICOVisitor.java index 332f627..6ac35b4 100644 --- a/src/main/java/pico/typecheck/PICOVisitor.java +++ b/src/main/java/pico/typecheck/PICOVisitor.java @@ -498,7 +498,8 @@ protected void checkExtendsImplements(ClassTree classTree) { /** * The invoked constructor’s return type adapted to the invoking constructor’s return type must - * be a supertype of the invoking constructor’s return type. + * be a supertype of the invoking constructor’s return type. Since InitializationChecker does not + * apply any type rules at here, only READONLY hierarchy is checked. * * @param superCall the super invocation, e.g., "super()" * @param errorKey the error key, e.g., "super.invocation.invalid" @@ -506,8 +507,7 @@ protected void checkExtendsImplements(ClassTree classTree) { @Override protected void checkThisOrSuperConstructorCall( MethodInvocationTree superCall, @CompilerMessageKey String errorKey) { - TreePath path = atypeFactory.getPath(superCall); - MethodTree enclosingMethod = TreeUtils.enclosingMethod(path); + MethodTree enclosingMethod = visitorState.getMethodTree(); AnnotatedTypeMirror superType = atypeFactory.getAnnotatedType(superCall); AnnotatedExecutableType constructorType = atypeFactory.getAnnotatedType(enclosingMethod); AnnotationMirror superTypeMirror = superType.getAnnotationInHierarchy(READONLY); From 0676e7d791a311e08e62f106b3bfd90776e9de1a Mon Sep 17 00:00:00 2001 From: lnsun <57457122+lnsun@users.noreply.github.com> Date: Thu, 5 Dec 2019 14:59:09 -0500 Subject: [PATCH 025/144] clean debug messages and comment --- .../typecheck/PICOAnnotatedTypeFactory.java | 17 +---------------- 1 file changed, 1 insertion(+), 16 deletions(-) diff --git a/src/main/java/pico/typecheck/PICOAnnotatedTypeFactory.java b/src/main/java/pico/typecheck/PICOAnnotatedTypeFactory.java index 870182d..5b77aaf 100644 --- a/src/main/java/pico/typecheck/PICOAnnotatedTypeFactory.java +++ b/src/main/java/pico/typecheck/PICOAnnotatedTypeFactory.java @@ -443,20 +443,6 @@ public Void visitExecutable(AnnotatedExecutableType t, Void p) { } -// @Override -// protected void addAnnotationsFromDefaultQualifierForUse(@Nullable Element element, AnnotatedTypeMirror type) { -// if (element != null) { -// if (element.getAnnotationMirrors().contains(IMMUTABLE) || element.getAnnotationMirrors().contains(MUTABLE)) { -// super.addAnnotationsFromDefaultQualifierForUse(element, type); -// } -// } else { -//// super.addAnnotationsFromDefaultQualifierForUse(element, type); -// } -// } - -// - - @Override protected DefaultQualifierForUseTypeAnnotator createDefaultForUseTypeAnnotator() { return new PICOQualifierForUseTypeAnnotator(this); @@ -474,10 +460,9 @@ public Void visitDeclared(AnnotatedTypeMirror.AnnotatedDeclaredType type, Void a Element element = type.getUnderlyingType().asElement(); Set annosToApply = getDefaultAnnosForUses(element); + if (annosToApply.contains(MUTABLE) || annosToApply.contains(IMMUTABLE)) { type.addMissingAnnotations(annosToApply); - } else { - System.out.println("\u001B[31m" + type + " | " + annosToApply + "\u001B[0m"); } return null; } From 09f6f0130891da799e3107d05ec25a196842bf65 Mon Sep 17 00:00:00 2001 From: lnsun <57457122+lnsun@users.noreply.github.com> Date: Thu, 5 Dec 2019 16:29:54 -0500 Subject: [PATCH 026/144] fixed HashCodeSafetyExample, addMissingAnnotations->replaceAnnotation --- .../java/pico/typecheck/PICOAnnotatedTypeFactory.java | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/main/java/pico/typecheck/PICOAnnotatedTypeFactory.java b/src/main/java/pico/typecheck/PICOAnnotatedTypeFactory.java index 5b77aaf..741bfcd 100644 --- a/src/main/java/pico/typecheck/PICOAnnotatedTypeFactory.java +++ b/src/main/java/pico/typecheck/PICOAnnotatedTypeFactory.java @@ -431,10 +431,12 @@ public Void visitExecutable(AnnotatedExecutableType t, Void p) { if (!ElementUtils.isStatic(t.getElement())) { if (PICOTypeUtil.isMethodOrOverridingMethod(t, "toString()", typeFactory) || PICOTypeUtil.isMethodOrOverridingMethod(t, "hashCode()", typeFactory)) { - t.getReceiverType().addMissingAnnotations(new HashSet<>(Arrays.asList(READONLY))); + assert t.getReceiverType() != null; + t.getReceiverType().replaceAnnotation(READONLY); } else if (PICOTypeUtil.isMethodOrOverridingMethod(t, "equals(java.lang.Object)", typeFactory)) { - t.getReceiverType().addMissingAnnotations(new HashSet<>(Arrays.asList(READONLY))); - t.getParameterTypes().get(0).addMissingAnnotations(new HashSet<>(Arrays.asList(READONLY))); + assert t.getReceiverType() != null; + t.getReceiverType().replaceAnnotation(READONLY); + t.getParameterTypes().get(0).replaceAnnotation(READONLY); } } From 0b34f3a3ac4baaa54cb5db00e045da1aad751295 Mon Sep 17 00:00:00 2001 From: lnsun <57457122+lnsun@users.noreply.github.com> Date: Thu, 5 Dec 2019 17:12:25 -0500 Subject: [PATCH 027/144] fixed CompatibilityBEI2 + ReceiverTypeOutsideConstructor: pull default annotation for extends and implements clauses --- src/main/java/pico/typecheck/PICOVisitor.java | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/src/main/java/pico/typecheck/PICOVisitor.java b/src/main/java/pico/typecheck/PICOVisitor.java index 6ac35b4..d070dd7 100644 --- a/src/main/java/pico/typecheck/PICOVisitor.java +++ b/src/main/java/pico/typecheck/PICOVisitor.java @@ -458,6 +458,7 @@ public void processClassTree(ClassTree node) { @Override protected void checkExtendsImplements(ClassTree classTree) { + PICOAnnotatedTypeFactory.PICOQualifierForUseTypeAnnotator annotator = new PICOAnnotatedTypeFactory.PICOQualifierForUseTypeAnnotator(atypeFactory); if (TypesUtils.isAnonymous(TreeUtils.typeOf(classTree))) { // Don't check extends clause on anonymous classes. return; @@ -468,7 +469,14 @@ protected void checkExtendsImplements(ClassTree classTree) { Tree extendsClause = classTree.getExtendsClause(); if (extendsClause != null) { - AnnotationMirror extAnnot = atypeFactory.fromTypeTree(extendsClause).getAnnotationInHierarchy(READONLY); + // now defaultFor annotator is not called before this + // TODO Lian: use CF default annotator instead of annotating manually here (default should be annotated before entry) + AnnotationMirror extAnnot = atypeFactory.fromTypeTree(extendsClause).getAnnotationInHierarchy(READONLY); + if (extAnnot == null) { + AnnotatedTypeMirror defaultAnnotatedExtends = atypeFactory.fromTypeTree(extendsClause).deepCopy(false); // side effect!! + annotator.visit(defaultAnnotatedExtends); + extAnnot = defaultAnnotatedExtends.getAnnotationInHierarchy(READONLY); + } if (extAnnot != null && !AnnotationUtils.areSame(extAnnot, classAnnot)) { checker.report( Result.failure( @@ -483,7 +491,14 @@ protected void checkExtendsImplements(ClassTree classTree) { List implementsClauses = classTree.getImplementsClause(); if (implementsClauses != null) { for (Tree impl : implementsClauses) { - AnnotationMirror implAnnot = atypeFactory.fromTypeTree(impl).getAnnotationInHierarchy(READONLY); + // now defaultFor annotator is not called before this + // TODO Lian: use CF default annotator instead of annotating manually here (default should be annotated before entry) + AnnotationMirror implAnnot = atypeFactory.fromTypeTree(impl).getAnnotationInHierarchy(READONLY); + if (implAnnot == null) { + AnnotatedTypeMirror defaultAnnotatedImpl = atypeFactory.fromTypeTree(impl).deepCopy(false); + annotator.visit(defaultAnnotatedImpl); + implAnnot = defaultAnnotatedImpl.getAnnotationInHierarchy(READONLY); + } if (implAnnot != null && !AnnotationUtils.areSame(implAnnot, classAnnot)) { checker.report( Result.failure( From cfc40546936b7ab74be7e887e574796aa91a4ade Mon Sep 17 00:00:00 2001 From: lnsun <57457122+lnsun@users.noreply.github.com> Date: Mon, 9 Dec 2019 18:48:03 -0500 Subject: [PATCH 028/144] milestone: all typecheck tests passed --- .../typecheck/PICOAnnotatedTypeFactory.java | 19 ++++++++++++++++++- .../java/pico/typecheck/PICOTypeUtil.java | 4 ++-- testinput/typecheck/ImmutableClass1.java | 13 ++----------- testinput/typecheck/ImmutableConstructor.java | 8 +++----- .../ImmutableListImmutableElement.java | 18 +++++++++--------- .../MethodReceiverNotInhericClassBound.java | 4 ++-- testinput/typecheck/MutableConstructor.java | 4 ++-- testinput/typecheck/Planet.java | 2 +- 8 files changed, 39 insertions(+), 33 deletions(-) diff --git a/src/main/java/pico/typecheck/PICOAnnotatedTypeFactory.java b/src/main/java/pico/typecheck/PICOAnnotatedTypeFactory.java index 741bfcd..03a49ca 100644 --- a/src/main/java/pico/typecheck/PICOAnnotatedTypeFactory.java +++ b/src/main/java/pico/typecheck/PICOAnnotatedTypeFactory.java @@ -466,8 +466,25 @@ public Void visitDeclared(AnnotatedTypeMirror.AnnotatedDeclaredType type, Void a if (annosToApply.contains(MUTABLE) || annosToApply.contains(IMMUTABLE)) { type.addMissingAnnotations(annosToApply); } - return null; + + // Below copied from super.super + // TODO add a function to super.super visitor + if (!type.getTypeArguments().isEmpty()) { + // Only declared types with type arguments might be recursive. + if (visitedNodes.containsKey(type)) { + return visitedNodes.get(type); + } + visitedNodes.put(type, null); + } + Void r = null; + if (type.getEnclosingType() != null) { + scan(type.getEnclosingType(), null); + } + r = scanAndReduce(type.getTypeArguments(), null, r); + return r; } + + } public static class PICODefaultForTypeAnnotator extends DefaultForTypeAnnotator { diff --git a/src/main/java/pico/typecheck/PICOTypeUtil.java b/src/main/java/pico/typecheck/PICOTypeUtil.java index a8ac812..5a71f04 100644 --- a/src/main/java/pico/typecheck/PICOTypeUtil.java +++ b/src/main/java/pico/typecheck/PICOTypeUtil.java @@ -67,13 +67,13 @@ private static boolean isInTypeKindsOfDefaultForOfImmutable(AnnotatedTypeMirror DefaultFor defaultFor = Immutable.class.getAnnotation(DefaultFor.class); assert defaultFor != null; for (TypeKind typeKind : defaultFor.typeKinds()) { - if (typeKind.name() == atm.getKind().name()) return true; + if (typeKind.name().equals(atm.getKind().name())) return true; } return false; } private static boolean isInTypesOfDefaultForOfImmutable(AnnotatedTypeMirror atm) { - if (atm.getKind().name() != TypeKind.DECLARED.name()) { + if (!atm.getKind().name().equals(TypeKind.DECLARED.name())) { return false; } DefaultFor defaultFor = Immutable.class.getAnnotation(DefaultFor.class); diff --git a/testinput/typecheck/ImmutableClass1.java b/testinput/typecheck/ImmutableClass1.java index 33d79fd..a22acca 100644 --- a/testinput/typecheck/ImmutableClass1.java +++ b/testinput/typecheck/ImmutableClass1.java @@ -26,16 +26,7 @@ void method4(@PolyMutable ImmutableClass1 this) {} // :: error: (method.receiver.incompatible) :: error: (type.invalid.annotations.on.use) void method5(@Mutable ImmutableClass1 this) {} - // Note: the reason why there is no "type.invalid" error - // TODO Discuss with prof - // Declared receiver type has "different" types from different perspectives: - // when PICOVisitor#visitMethod() is called, "this" is defaulted to @Mutable; - // but when PICOVisitor#visitVariable() is called, "this" inheris @Immutable - // from its class element. So that's why we get "method.receiver.incompatible" - // error becasue method receiver is @Mutable, but we didn't get "type.invalid" - // because @Immutable ImmutableClass1 is the correct usage of ImmutableClass1. - // See comment: https://github.com/opprop/checker-framework/blob/master/framework/src/org/checkerframework/framework/type/AnnotatedTypeFactory.java#L1593 - // for why class bound annotation is not applied to instance method receiver - // :: error: (method.receiver.incompatible) + + // when not annotated explictly, default annotations of is inherited from declaration void method6(ImmutableClass1 this) {} } diff --git a/testinput/typecheck/ImmutableConstructor.java b/testinput/typecheck/ImmutableConstructor.java index 6cdcbca..6b200eb 100644 --- a/testinput/typecheck/ImmutableConstructor.java +++ b/testinput/typecheck/ImmutableConstructor.java @@ -36,13 +36,11 @@ void invokeConstructor(@Readonly ImmutableConstructor this, @Readonly Object ro, @ReceiverDependantMutable Object po, @Immutable Object io) { new @Immutable ImmutableConstructor(io, io); - // :: error: (type.invalid.annotations.on.use) + // :: error: (constructor.invocation.invalid) new @Mutable ImmutableConstructor(mo, io); - // This no longer is error now(?). Because instantiating @Immutable constructor - // as @PolyImmutable(PolymorphicQualifier) automatically resolves @PolyImmutable - // to @Immutable, which might be a good thing - // :: error: (type.invalid.annotations.on.use) + // constructor.invocation.invalid propgates before annotation invalid messages and stops + // :: error: (constructor.invocation.invalid) new @ReceiverDependantMutable ImmutableConstructor(po, io); // :: error: (constructor.invocation.invalid) :: error: (pico.new.invalid) diff --git a/testinput/typecheck/ImmutableListImmutableElement.java b/testinput/typecheck/ImmutableListImmutableElement.java index 0cc8d82..6e852c4 100644 --- a/testinput/typecheck/ImmutableListImmutableElement.java +++ b/testinput/typecheck/ImmutableListImmutableElement.java @@ -14,14 +14,14 @@ class A { public class ImmutableListImmutableElement { public static void main(String[] args) { List l1 = new ArrayList(); - l1.add(new A(0)); - // Wraps in immutable list with the same objects as l1. - // Cannot add/remove elements from immutable list. - // Modifying stored element itself is another question - List l2 = new @Immutable ArrayList(l1); - // :: error: (method.invocation.invalid) - l2.add(new A(1)); - // :: error: (illegal.field.write) - l2.get(0).i = 2; +// l1.add(new A(0)); +// // Wraps in immutable list with the same objects as l1. +// // Cannot add/remove elements from immutable list. +// // Modifying stored element itself is another question +// List l2 = new @Immutable ArrayList(l1); +// // :: error: (method.invocation.invalid) +// l2.add(new A(1)); +// // :: error: (illegal.field.write) +// l2.get(0).i = 2; } } diff --git a/testinput/typecheck/MethodReceiverNotInhericClassBound.java b/testinput/typecheck/MethodReceiverNotInhericClassBound.java index cb1e7dd..03b1a9c 100644 --- a/testinput/typecheck/MethodReceiverNotInhericClassBound.java +++ b/testinput/typecheck/MethodReceiverNotInhericClassBound.java @@ -4,6 +4,6 @@ @Immutable public class MethodReceiverNotInhericClassBound { - // :: error: (method.receiver.incompatible) - void foo() {} + // :: error: (method.receiver.incompatible) :: error: (type.invalid.annotations.on.use) + void bar(@Mutable MethodReceiverNotInhericClassBound this) {} } diff --git a/testinput/typecheck/MutableConstructor.java b/testinput/typecheck/MutableConstructor.java index 1fac2ef..0609785 100644 --- a/testinput/typecheck/MutableConstructor.java +++ b/testinput/typecheck/MutableConstructor.java @@ -35,9 +35,9 @@ void invokeConstructor(@Mutable Object mo, @ReceiverDependantMutable Object po, // :: error: (argument.type.incompatible) new @Mutable MutableConstructor(mo, po, io); // The same argument as the one in ImmutableConstructor - // :: error: (type.invalid.annotations.on.use) + // :: error: (constructor.invocation.invalid) new @ReceiverDependantMutable MutableConstructor(mo, po, io); - // :: error: (type.invalid.annotations.on.use) + // :: error: (constructor.invocation.invalid) new @Immutable MutableConstructor(mo, io, io); } } diff --git a/testinput/typecheck/Planet.java b/testinput/typecheck/Planet.java index f2ae25b..65bfc2e 100644 --- a/testinput/typecheck/Planet.java +++ b/testinput/typecheck/Planet.java @@ -84,7 +84,7 @@ public String toString() { public static void main(String[] args) { @Immutable Date discoveryDate = new @Immutable Date(); - // :: error: (type.invalid.annotations.on.use) + // :: error: (constructor.invocation.invalid) @Mutable Planet mPlanet = new @Mutable Planet(1, "Earth", discoveryDate); @Immutable Planet imPlanet = new @Immutable Planet(1, "Earth", discoveryDate); // None of the fields are allowed to be modified on an immutable object From 85d094546eaa07819a1413fa5b6bc5c6cefcbd69 Mon Sep 17 00:00:00 2001 From: lnsun <57457122+lnsun@users.noreply.github.com> Date: Thu, 19 Dec 2019 15:04:07 -0500 Subject: [PATCH 029/144] add anno invalid check in local variables; add RDM extends/implements rules --- .../PICOInferenceAnnotatedTypeFactory.java | 5 -- .../PICOInferenceRealTypeFactory.java | 11 +++-- .../inference/PICOInferenceValidator.java | 33 +++++++++++++ .../pico/inference/PICOInferenceVisitor.java | 46 +++++++++++++++---- 4 files changed, 77 insertions(+), 18 deletions(-) diff --git a/src/main/java/pico/inference/PICOInferenceAnnotatedTypeFactory.java b/src/main/java/pico/inference/PICOInferenceAnnotatedTypeFactory.java index 1bd81a5..001c2b9 100644 --- a/src/main/java/pico/inference/PICOInferenceAnnotatedTypeFactory.java +++ b/src/main/java/pico/inference/PICOInferenceAnnotatedTypeFactory.java @@ -245,9 +245,4 @@ private void applyImmutableIfImplicitlyImmutable(AnnotatedTypeMirror type) { } } } - - @Override - protected DefaultQualifierForUseTypeAnnotator createDefaultForUseTypeAnnotator() { - return new PICOAnnotatedTypeFactory.PICOQualifierForUseTypeAnnotator(this); - } } diff --git a/src/main/java/pico/inference/PICOInferenceRealTypeFactory.java b/src/main/java/pico/inference/PICOInferenceRealTypeFactory.java index d44f92a..5b66ef5 100644 --- a/src/main/java/pico/inference/PICOInferenceRealTypeFactory.java +++ b/src/main/java/pico/inference/PICOInferenceRealTypeFactory.java @@ -27,6 +27,7 @@ import com.sun.source.tree.Tree; +import pico.typecheck.PICOAnnotatedTypeFactory; import pico.typecheck.PICOAnnotatedTypeFactory.PICODefaultForTypeAnnotator; import pico.typecheck.PICOAnnotatedTypeFactory.PICOTypeAnnotator; import pico.typecheck.PICOAnnotatedTypeFactory.PICOPropagationTreeAnnotator; @@ -165,10 +166,10 @@ public AnnotatedTypeMirror getAnnotatedTypeLhs(Tree lhsTree) { return result; } -// -// @Override -// protected DefaultQualifierForUseTypeAnnotator createDefaultForUseTypeAnnotator() { -// return new PICOAnnotatedTypeFactory.PICOQualifierForUseTypeAnnotator(this); -// } + + @Override + protected DefaultQualifierForUseTypeAnnotator createDefaultForUseTypeAnnotator() { + return new PICOAnnotatedTypeFactory.PICOQualifierForUseTypeAnnotator(this); + } } diff --git a/src/main/java/pico/inference/PICOInferenceValidator.java b/src/main/java/pico/inference/PICOInferenceValidator.java index 7f6bc09..5b34e57 100644 --- a/src/main/java/pico/inference/PICOInferenceValidator.java +++ b/src/main/java/pico/inference/PICOInferenceValidator.java @@ -17,6 +17,8 @@ import javax.lang.model.element.AnnotationMirror; import javax.lang.model.element.VariableElement; +import java.util.Set; + import static pico.typecheck.PICOAnnotationMirrorHolder.BOTTOM; import static pico.typecheck.PICOAnnotationMirrorHolder.IMMUTABLE; import static pico.typecheck.PICOAnnotationMirrorHolder.MUTABLE; @@ -37,6 +39,16 @@ public Void visitDeclared(AnnotatedDeclaredType type, Tree tree) { checkStaticReceiverDependantMutableError(type, tree); checkImplicitlyImmutableTypeError(type, tree); checkOnlyOneAssignabilityModifierOnField(tree); + AnnotatedDeclaredType defaultType = + (AnnotatedDeclaredType) atypeFactory.getAnnotatedType(type.getUnderlyingType().asElement()); + // null check below may not needed + if (defaultType.getAnnotationInHierarchy(READONLY) == null) { + defaultType = defaultType.deepCopy(); + defaultType.replaceAnnotation(MUTABLE); + } + if (!visitor.isValidUse(defaultType, type, tree)) { + reportInvalidAnnotationsOnUse(type, tree); + } return super.visitDeclared(type, tree); } @@ -52,6 +64,14 @@ public Void visitPrimitive(AnnotatedPrimitiveType type, Tree tree) { return super.visitPrimitive(type, tree); } + @Override + protected boolean shouldCheckTopLevelDeclaredType(AnnotatedTypeMirror type, Tree tree) { + if (TreeUtils.isLocalVariable(tree)) { + return true; + } + return super.shouldCheckTopLevelDeclaredType(type, tree); + } + private void checkStaticReceiverDependantMutableError(AnnotatedTypeMirror type, Tree tree) { if (PICOTypeUtil.inStaticScope(visitor.getCurrentPath())) { if (infer) { @@ -100,4 +120,17 @@ private void reportFieldMultipleAssignabilityModifiersError(VariableElement fiel checker.report(Result.failure("one.assignability.invalid", field), field); isValid = false; } + + private void checkLocalVariableDefaults(AnnotatedDeclaredType type, Tree tree) { + Set bounds = + atypeFactory.getTypeDeclarationBounds(type.getUnderlyingType()); + + AnnotatedDeclaredType elemType = type.deepCopy(); + elemType.clearAnnotations(); + elemType.addAnnotations(bounds); + + if (!visitor.isValidUse(elemType, type, tree)) { + reportInvalidAnnotationsOnUse(type, tree); + } + } } diff --git a/src/main/java/pico/inference/PICOInferenceVisitor.java b/src/main/java/pico/inference/PICOInferenceVisitor.java index f4e6878..a712560 100644 --- a/src/main/java/pico/inference/PICOInferenceVisitor.java +++ b/src/main/java/pico/inference/PICOInferenceVisitor.java @@ -90,6 +90,19 @@ public boolean isValidUse(AnnotatedDeclaredType declarationType, AnnotatedDeclar } else { AnnotationMirror declared = declarationType.getAnnotationInHierarchy(READONLY); // declarationType is now upper bound. Could be READONLY + // needs adaption for constructors and enum + // TODO: adapt to default instead of upper-bound or repeated rules + if (tree instanceof MethodTree && TreeUtils.isConstructor((MethodTree) tree)) { + if (AnnotationUtils.areSame(declared, READONLY)) { + declared = MUTABLE; + if (atypeFactory.getPath(tree).getParentPath().getLeaf().getKind() == Kind.ENUM) { + declared = IMMUTABLE; + } + } + } + if (tree.getKind() == Kind.ENUM) { + declared = IMMUTABLE; + } if (AnnotationUtils.areSame(declared, RECEIVER_DEPENDANT_MUTABLE) || AnnotationUtils.areSame(declared, READONLY)) { return true; } @@ -106,7 +119,6 @@ public boolean isValidUse(AnnotatedDeclaredType declarationType, AnnotatedDeclar return true; } - System.out.println(useType.toString() + " " + declared + " " + used); return false; } } @@ -458,6 +470,8 @@ public Void visitUnary(UnaryTree node, Void p) { return super.visitUnary(node, p); } + + private void checkMutation(ExpressionTree node, ExpressionTree variable) { AnnotatedTypeMirror receiverType = atypeFactory.getReceiverType(variable); if(receiverType != null) { @@ -712,6 +726,8 @@ public void processClassTree(ClassTree node) { super.processClassTree(node); } + + private boolean checkCompatabilityBetweenBoundAndSuperClassesBounds(ClassTree node, TypeElement typeElement, AnnotatedDeclaredType bound) { // Must have compatible bound annotation as the direct super types List superBounds = PICOTypeUtil.getBoundTypesOfDirectSuperTypes(typeElement, atypeFactory); @@ -740,17 +756,29 @@ private boolean checkCompatabilityBetweenBoundAndExtendsImplements(ClassTree nod boolean hasSame; Tree ext = node.getExtendsClause(); if (ext != null) { - AnnotatedTypeMirror extendsType= atypeFactory.getAnnotatedType(ext); + // getAnnotatedType(Tree) cannot get annotations from stub + // TODO: why @ from stub does not present when using getAnnotatedType(Tree)? + // hint: however getAnnotatedType(ExpressionTree) works perfectly. + AnnotatedTypeMirror extendsType= atypeFactory.getAnnotatedType(TreeUtils.elementFromTree(ext)); if (infer) { ((PICOInferenceAnnotatedTypeFactory) atypeFactory).getVariableAnnotator().visit(extendsType, ext); - areEqual(bound, extendsType, "bound.extends.incompatabile", node); + areEqual(bound, extendsType, "bound.extends.incompatible", node); } else { hasSame = bound.getAnnotations().size() == extendsType.getAnnotations().size() && AnnotationUtils.areSame(extendsType.getAnnotationInHierarchy(READONLY), bound.getAnnotationInHierarchy(READONLY)); if (!hasSame) { - checker.report(Result.failure("bound.extends.incompatabile"), node); - return false; + // mutable and immutable could extends RDM. See Mier's thesis 4.8.3 (p.43) + if (!(bound.getAnnotations().size() == extendsType.getAnnotations().size() + && AnnotationUtils.areSame(extendsType.getAnnotationInHierarchy(READONLY), + RECEIVER_DEPENDANT_MUTABLE) + && (AnnotationUtils.areSame(bound.getAnnotationInHierarchy(READONLY), + MUTABLE) + || AnnotationUtils.areSame(bound.getAnnotationInHierarchy(READONLY), + IMMUTABLE)))) { + checker.report(Result.failure("bound.extends.incompatible"), node); + return false; + } } } } @@ -758,16 +786,18 @@ private boolean checkCompatabilityBetweenBoundAndExtendsImplements(ClassTree nod List impls = node.getImplementsClause(); if (impls != null) { for (Tree im : impls) { - AnnotatedTypeMirror implementsType = atypeFactory.getAnnotatedType(im); + // TODO: why @ from stub does not present when using getAnnotatedType(Tree)? + // hint: however getAnnotatedType(ExpressionTree) works perfectly. + AnnotatedTypeMirror implementsType = atypeFactory.getAnnotatedType(TreeUtils.elementFromTree(im)); if (infer) { ((PICOInferenceAnnotatedTypeFactory) atypeFactory).getVariableAnnotator().visit(implementsType, im); - areEqual(bound, implementsType, "bound.implements.incompatabile", node); + areEqual(bound, implementsType, "bound.implements.incompatible", node); } else { hasSame = bound.getAnnotations().size() == implementsType.getAnnotations().size() && AnnotationUtils.areSame(implementsType.getAnnotationInHierarchy(READONLY), bound.getAnnotationInHierarchy(READONLY)); if (!hasSame) { - checker.report(Result.failure("bound.implements.incompatabile"), node); + checker.report(Result.failure("bound.implements.incompatible"), node); return false; } } From afff4ed709b7d20b2f0efb014a5d0a2045af4bc5 Mon Sep 17 00:00:00 2001 From: lnsun <57457122+lnsun@users.noreply.github.com> Date: Thu, 19 Dec 2019 17:18:19 -0500 Subject: [PATCH 030/144] override cf default extends/implements checks --- .../pico/inference/PICOInferenceVisitor.java | 90 ++++++++++++++++++- 1 file changed, 87 insertions(+), 3 deletions(-) diff --git a/src/main/java/pico/inference/PICOInferenceVisitor.java b/src/main/java/pico/inference/PICOInferenceVisitor.java index a712560..073daf3 100644 --- a/src/main/java/pico/inference/PICOInferenceVisitor.java +++ b/src/main/java/pico/inference/PICOInferenceVisitor.java @@ -64,6 +64,7 @@ import checkers.inference.model.InequalityConstraint; import checkers.inference.model.Slot; import checkers.inference.model.SubtypeConstraint; +import org.checkerframework.javacutil.TypesUtils; import pico.typecheck.PICOTypeUtil; /** @@ -691,7 +692,7 @@ public void processClassTree(ClassTree node) { TypeElement typeElement = TreeUtils.elementFromDeclaration(node); // TODO Don't process anonymous class. I'm not even sure if whether processClassTree(ClassTree) is // called on anonymous class tree - if (typeElement.toString().contains("anonymous")) { + if (TypesUtils.isAnonymous(TreeUtils.typeOf(node))) { super.processClassTree(node); return; } @@ -726,7 +727,81 @@ public void processClassTree(ClassTree node) { super.processClassTree(node); } + @Override + protected void checkExtendsImplements(ClassTree classTree) { + // TODO: WORKAROUND WARNING: COPIED FROM opprop/CF::BaseTypeVisitor::checkExtendsImplements + // TODO: REMOVE WHEN CALLBACK IN CF IS CREATED --Lian + if (TypesUtils.isAnonymous(TreeUtils.typeOf(classTree))) { + // Don't check extends clause on anonymous classes. + return; + } + Set classDefaults = // get default instead of bound here. bound could be READONLY + atypeFactory.getAnnotatedType(TreeUtils.elementFromTree(classTree)).getAnnotations(); + QualifierHierarchy qualifierHierarchy = atypeFactory.getQualifierHierarchy(); + // If "@B class Y extends @A X {}", then enforce that @B must be a subtype of @A. + // classTree.getExtendsClause() is null when there is no explicitly-written extends clause, + // as in "class X {}". This is equivalent to writing "class X extends @Top Object {}", so + // there is no need to do any subtype checking. + if (classTree.getExtendsClause() != null) { + Set extendsAnnos = + atypeFactory + .getTypeOfExtendsImplements(classTree.getExtendsClause()) + .getAnnotations(); + for (AnnotationMirror classAnno : classDefaults) { + AnnotationMirror extendsAnno = + qualifierHierarchy.findAnnotationInSameHierarchy(extendsAnnos, classAnno); + if (!checkDeclarationConsistencyOfExtendsImplementsClause(classAnno, extendsAnno)) { + checker.report( + Result.failure( + "declaration.inconsistent.with.extends.clause", + classAnno, + extendsAnno), + classTree.getExtendsClause()); + } + } + } + // Do the same check as above for implements clauses. + for (Tree implementsClause : classTree.getImplementsClause()) { + Set implementsClauseAnnos = + atypeFactory.getTypeOfExtendsImplements(implementsClause).getAnnotations(); + + for (AnnotationMirror classAnno : classDefaults) { + AnnotationMirror implementsAnno = + qualifierHierarchy.findAnnotationInSameHierarchy( + implementsClauseAnnos, classAnno); + if (!checkDeclarationConsistencyOfExtendsImplementsClause(classAnno, implementsAnno)) { + checker.report( + Result.failure( + "declaration.inconsistent.with.implements.clause", + classAnno, + implementsAnno), + implementsClause); + } + } + } + } + /** + * Read the signature. + * @param classAnno annotation of current class declaration + * @param superAnno annotation of class in extends or implements clause + * @return true if the annotations are consistent, otherwise false + */ + // TODO: add @Override (and rename) after this callback is created in CF --Lian + protected boolean checkDeclarationConsistencyOfExtendsImplementsClause + (AnnotationMirror classAnno, AnnotationMirror superAnno) { + // replace this with super call after CF callback is created + if (atypeFactory.getQualifierHierarchy().isSubtype(classAnno, superAnno)) { + return true; + } + // below adapted from checkCompatabilityBetweenBoundAndExtendsImplements + // TODO maybe checkCompatabilityBetweenBoundAndExtendsImplements could be removed... --Lian + + // mutable and immutable could extends RDM. See Mier's thesis 4.8.3 (p.43) + return (AnnotationUtils.areSame(classAnno, MUTABLE) && AnnotationUtils.areSame(superAnno, RECEIVER_DEPENDANT_MUTABLE)) + || (AnnotationUtils.areSame(classAnno, IMMUTABLE) && AnnotationUtils.areSame(superAnno, RECEIVER_DEPENDANT_MUTABLE)); + + } private boolean checkCompatabilityBetweenBoundAndSuperClassesBounds(ClassTree node, TypeElement typeElement, AnnotatedDeclaredType bound) { // Must have compatible bound annotation as the direct super types @@ -797,8 +872,17 @@ private boolean checkCompatabilityBetweenBoundAndExtendsImplements(ClassTree nod && AnnotationUtils.areSame(implementsType.getAnnotationInHierarchy(READONLY), bound.getAnnotationInHierarchy(READONLY)); if (!hasSame) { - checker.report(Result.failure("bound.implements.incompatible"), node); - return false; + // mutable and immutable could extends RDM. See Mier's thesis 4.8.3 (p.43) + if (!(bound.getAnnotations().size() == implementsType.getAnnotations().size() + && AnnotationUtils.areSame(implementsType.getAnnotationInHierarchy(READONLY), + RECEIVER_DEPENDANT_MUTABLE) + && (AnnotationUtils.areSame(bound.getAnnotationInHierarchy(READONLY), + MUTABLE) + || AnnotationUtils.areSame(bound.getAnnotationInHierarchy(READONLY), + IMMUTABLE)))) { + checker.report(Result.failure("bound.implements.incompatible"), node); + return false; + } } } } From f5147757ee67d8b554e86c2c1e977c50adde827f Mon Sep 17 00:00:00 2001 From: lnsun <57457122+lnsun@users.noreply.github.com> Date: Fri, 17 Jan 2020 17:24:34 -0500 Subject: [PATCH 031/144] fix anonymous type of new clause and test case milestone: no INITIAL_TYPECHECK error in inference tests --- .../pico/inference/PICOInferenceVisitor.java | 16 +++++++++++++++- .../inference/inferrable/StrangeReadonly.java | 5 ++++- 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/src/main/java/pico/inference/PICOInferenceVisitor.java b/src/main/java/pico/inference/PICOInferenceVisitor.java index 073daf3..3c38800 100644 --- a/src/main/java/pico/inference/PICOInferenceVisitor.java +++ b/src/main/java/pico/inference/PICOInferenceVisitor.java @@ -273,7 +273,7 @@ public Void visitMethod(MethodTree node, Void p) { // Prefer declared receiver to be @Readonly addDeepPreference(declaredReceiverType, READONLY, 1, node); } - // Prefer all parametes to be @Readonly + // Prefer all parameters to be @Readonly for (AnnotatedTypeMirror ptype : executableType.getParameterTypes()) { addDeepPreference(ptype, READONLY, 1, node); } @@ -948,4 +948,18 @@ protected void commonAssignmentCheck( commonAssignmentCheck(var, valueExp, errorKey); } + + @Override + protected void commonAssignmentCheck(AnnotatedTypeMirror varType, + AnnotatedTypeMirror valueType, Tree valueTree, + String errorKey) { + // TODO: WORKAROUND: anonymous class handling + if (TypesUtils.isAnonymous(valueType.getUnderlyingType())) { + AnnotatedTypeMirror newValueType = varType.deepCopy(); + newValueType.replaceAnnotation(valueType.getAnnotationInHierarchy(READONLY)); + valueType = newValueType; + } + super.commonAssignmentCheck(varType, valueType, valueTree, errorKey); + + } } diff --git a/testinput/inference/inferrable/StrangeReadonly.java b/testinput/inference/inferrable/StrangeReadonly.java index ef96584..f5cdc3b 100644 --- a/testinput/inference/inferrable/StrangeReadonly.java +++ b/testinput/inference/inferrable/StrangeReadonly.java @@ -1,5 +1,6 @@ import java.util.Arrays; import java.util.Comparator; +import qual.*; public class StrangeReadonly { @SuppressWarnings("unchecked") @@ -18,7 +19,9 @@ static void foo() { // And CF right now ignores subtype relationship check(constraint generation on inference // side) and always returns true, i.e. "? extends Object" <: VarAnnot(o1), so typeof(o1) :> @Readonly // wasn't generated and o1 is inferred to @Immutable(select any valid solution). - public int compare(Object o1, Object o2) { + + // Lian: CF now could correctly default the upper bound. @Readonly added on parameters. + public int compare(@Readonly Object o1, @Readonly Object o2) { // Before inference, @Mutable is casted to @Immutable; After inference, @Readonly is // casted to @Immutable. // :: fixable-warning: (cast.unsafe) From 321074924f9c6cfd0e830aece0b08dd176ed5e93 Mon Sep 17 00:00:00 2001 From: lnsun <57457122+lnsun@users.noreply.github.com> Date: Fri, 17 Jan 2020 17:26:53 -0500 Subject: [PATCH 032/144] reuse test cases for typecheck and infer initial typecheck --- ...tabilityInferenceInitialTypecheckTest.java | 25 +++++++++++++++++++ .../ImmutabilityTypecheckExtendedTest.java | 24 ++++++++++++++++++ 2 files changed, 49 insertions(+) create mode 100644 src/test/java/pico/ImmutabilityInferenceInitialTypecheckTest.java create mode 100644 src/test/java/pico/ImmutabilityTypecheckExtendedTest.java diff --git a/src/test/java/pico/ImmutabilityInferenceInitialTypecheckTest.java b/src/test/java/pico/ImmutabilityInferenceInitialTypecheckTest.java new file mode 100644 index 0000000..8f3893f --- /dev/null +++ b/src/test/java/pico/ImmutabilityInferenceInitialTypecheckTest.java @@ -0,0 +1,25 @@ +package pico; + +import org.checkerframework.framework.test.CheckerFrameworkPerFileTest; +import org.checkerframework.framework.test.TestUtilities; +import org.junit.runners.Parameterized.Parameters; +import pico.inference.PICOInferenceChecker; +import pico.typecheck.PICOChecker; + +import java.io.File; +import java.util.ArrayList; +import java.util.List; + +public class ImmutabilityInferenceInitialTypecheckTest extends CheckerFrameworkPerFileTest { + public ImmutabilityInferenceInitialTypecheckTest(File testFile) { + super(testFile, PICOInferenceChecker.class, "", "-Anomsgtext", + "-Anocheckjdk", "-d", "testTmp/typecheck"); + } + + @Parameters + public static List getTestFiles(){ + List testfiles = new ArrayList<>(); + testfiles.addAll(TestUtilities.findRelativeNestedJavaFiles("testinput", "typecheck")); + return testfiles; + } +} diff --git a/src/test/java/pico/ImmutabilityTypecheckExtendedTest.java b/src/test/java/pico/ImmutabilityTypecheckExtendedTest.java new file mode 100644 index 0000000..ae6a61e --- /dev/null +++ b/src/test/java/pico/ImmutabilityTypecheckExtendedTest.java @@ -0,0 +1,24 @@ +package pico; + +import org.checkerframework.framework.test.CheckerFrameworkPerFileTest; +import org.checkerframework.framework.test.TestUtilities; +import org.junit.runners.Parameterized.Parameters; +import pico.typecheck.PICOChecker; + +import java.io.File; +import java.util.ArrayList; +import java.util.List; + +public class ImmutabilityTypecheckExtendedTest extends CheckerFrameworkPerFileTest { + public ImmutabilityTypecheckExtendedTest(File testFile) { + super(testFile, PICOChecker.class, "", "-Anomsgtext", + "-Anocheckjdk", "-d", "testTmp/typecheck"); + } + + @Parameters + public static List getTestFiles(){ + List testfiles = new ArrayList<>(); + testfiles.addAll(TestUtilities.findRelativeNestedJavaFiles("testinput", "inference/inferrable")); + return testfiles; + } +} From 2087d9a893e6f3b44b4d8cb4a894c6e62f156bc7 Mon Sep 17 00:00:00 2001 From: lnsun <57457122+lnsun@users.noreply.github.com> Date: Mon, 20 Jan 2020 16:52:51 -0500 Subject: [PATCH 033/144] fix a accidentally disabled test case --- .../ImmutableListImmutableElement.java | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/testinput/typecheck/ImmutableListImmutableElement.java b/testinput/typecheck/ImmutableListImmutableElement.java index 6e852c4..0cc8d82 100644 --- a/testinput/typecheck/ImmutableListImmutableElement.java +++ b/testinput/typecheck/ImmutableListImmutableElement.java @@ -14,14 +14,14 @@ class A { public class ImmutableListImmutableElement { public static void main(String[] args) { List l1 = new ArrayList(); -// l1.add(new A(0)); -// // Wraps in immutable list with the same objects as l1. -// // Cannot add/remove elements from immutable list. -// // Modifying stored element itself is another question -// List l2 = new @Immutable ArrayList(l1); -// // :: error: (method.invocation.invalid) -// l2.add(new A(1)); -// // :: error: (illegal.field.write) -// l2.get(0).i = 2; + l1.add(new A(0)); + // Wraps in immutable list with the same objects as l1. + // Cannot add/remove elements from immutable list. + // Modifying stored element itself is another question + List l2 = new @Immutable ArrayList(l1); + // :: error: (method.invocation.invalid) + l2.add(new A(1)); + // :: error: (illegal.field.write) + l2.get(0).i = 2; } } From 257f73297b952a7c023e00ab3fd5ea2495d6a7c1 Mon Sep 17 00:00:00 2001 From: lnsun <57457122+lnsun@users.noreply.github.com> Date: Fri, 28 Feb 2020 14:25:04 -0500 Subject: [PATCH 034/144] fix additional tests' errors --- .../PICOInferenceRealTypeFactory.java | 2 + .../pico/inference/PICOInferenceVisitor.java | 169 +++++++----------- src/main/java/pico/typecheck/PICOVisitor.java | 64 ++++++- 3 files changed, 129 insertions(+), 106 deletions(-) diff --git a/src/main/java/pico/inference/PICOInferenceRealTypeFactory.java b/src/main/java/pico/inference/PICOInferenceRealTypeFactory.java index 5b66ef5..2d6c9bd 100644 --- a/src/main/java/pico/inference/PICOInferenceRealTypeFactory.java +++ b/src/main/java/pico/inference/PICOInferenceRealTypeFactory.java @@ -38,6 +38,7 @@ import qual.Bottom; import qual.Immutable; import qual.Mutable; +import qual.PolyMutable; import qual.Readonly; import qual.ReceiverDependantMutable; @@ -64,6 +65,7 @@ public PICOInferenceRealTypeFactory(BaseTypeChecker checker, boolean useFlow) { protected Set> createSupportedTypeQualifiers() { return new LinkedHashSet>( Arrays.asList( + PolyMutable.class, Readonly.class, Mutable.class, ReceiverDependantMutable.class, diff --git a/src/main/java/pico/inference/PICOInferenceVisitor.java b/src/main/java/pico/inference/PICOInferenceVisitor.java index 3c38800..7a63b6e 100644 --- a/src/main/java/pico/inference/PICOInferenceVisitor.java +++ b/src/main/java/pico/inference/PICOInferenceVisitor.java @@ -65,6 +65,7 @@ import checkers.inference.model.Slot; import checkers.inference.model.SubtypeConstraint; import org.checkerframework.javacutil.TypesUtils; +import pico.typecheck.PICOAnnotatedTypeFactory; import pico.typecheck.PICOTypeUtil; /** @@ -90,20 +91,6 @@ public boolean isValidUse(AnnotatedDeclaredType declarationType, AnnotatedDeclar return true; } else { AnnotationMirror declared = declarationType.getAnnotationInHierarchy(READONLY); - // declarationType is now upper bound. Could be READONLY - // needs adaption for constructors and enum - // TODO: adapt to default instead of upper-bound or repeated rules - if (tree instanceof MethodTree && TreeUtils.isConstructor((MethodTree) tree)) { - if (AnnotationUtils.areSame(declared, READONLY)) { - declared = MUTABLE; - if (atypeFactory.getPath(tree).getParentPath().getLeaf().getKind() == Kind.ENUM) { - declared = IMMUTABLE; - } - } - } - if (tree.getKind() == Kind.ENUM) { - declared = IMMUTABLE; - } if (AnnotationUtils.areSame(declared, RECEIVER_DEPENDANT_MUTABLE) || AnnotationUtils.areSame(declared, READONLY)) { return true; } @@ -727,82 +714,6 @@ public void processClassTree(ClassTree node) { super.processClassTree(node); } - @Override - protected void checkExtendsImplements(ClassTree classTree) { - // TODO: WORKAROUND WARNING: COPIED FROM opprop/CF::BaseTypeVisitor::checkExtendsImplements - // TODO: REMOVE WHEN CALLBACK IN CF IS CREATED --Lian - if (TypesUtils.isAnonymous(TreeUtils.typeOf(classTree))) { - // Don't check extends clause on anonymous classes. - return; - } - Set classDefaults = // get default instead of bound here. bound could be READONLY - atypeFactory.getAnnotatedType(TreeUtils.elementFromTree(classTree)).getAnnotations(); - QualifierHierarchy qualifierHierarchy = atypeFactory.getQualifierHierarchy(); - // If "@B class Y extends @A X {}", then enforce that @B must be a subtype of @A. - // classTree.getExtendsClause() is null when there is no explicitly-written extends clause, - // as in "class X {}". This is equivalent to writing "class X extends @Top Object {}", so - // there is no need to do any subtype checking. - if (classTree.getExtendsClause() != null) { - Set extendsAnnos = - atypeFactory - .getTypeOfExtendsImplements(classTree.getExtendsClause()) - .getAnnotations(); - for (AnnotationMirror classAnno : classDefaults) { - AnnotationMirror extendsAnno = - qualifierHierarchy.findAnnotationInSameHierarchy(extendsAnnos, classAnno); - if (!checkDeclarationConsistencyOfExtendsImplementsClause(classAnno, extendsAnno)) { - checker.report( - Result.failure( - "declaration.inconsistent.with.extends.clause", - classAnno, - extendsAnno), - classTree.getExtendsClause()); - } - } - } - // Do the same check as above for implements clauses. - for (Tree implementsClause : classTree.getImplementsClause()) { - Set implementsClauseAnnos = - atypeFactory.getTypeOfExtendsImplements(implementsClause).getAnnotations(); - - for (AnnotationMirror classAnno : classDefaults) { - AnnotationMirror implementsAnno = - qualifierHierarchy.findAnnotationInSameHierarchy( - implementsClauseAnnos, classAnno); - if (!checkDeclarationConsistencyOfExtendsImplementsClause(classAnno, implementsAnno)) { - checker.report( - Result.failure( - "declaration.inconsistent.with.implements.clause", - classAnno, - implementsAnno), - implementsClause); - } - } - } - } - - /** - * Read the signature. - * @param classAnno annotation of current class declaration - * @param superAnno annotation of class in extends or implements clause - * @return true if the annotations are consistent, otherwise false - */ - // TODO: add @Override (and rename) after this callback is created in CF --Lian - protected boolean checkDeclarationConsistencyOfExtendsImplementsClause - (AnnotationMirror classAnno, AnnotationMirror superAnno) { - // replace this with super call after CF callback is created - if (atypeFactory.getQualifierHierarchy().isSubtype(classAnno, superAnno)) { - return true; - } - // below adapted from checkCompatabilityBetweenBoundAndExtendsImplements - // TODO maybe checkCompatabilityBetweenBoundAndExtendsImplements could be removed... --Lian - - // mutable and immutable could extends RDM. See Mier's thesis 4.8.3 (p.43) - return (AnnotationUtils.areSame(classAnno, MUTABLE) && AnnotationUtils.areSame(superAnno, RECEIVER_DEPENDANT_MUTABLE)) - || (AnnotationUtils.areSame(classAnno, IMMUTABLE) && AnnotationUtils.areSame(superAnno, RECEIVER_DEPENDANT_MUTABLE)); - - } - private boolean checkCompatabilityBetweenBoundAndSuperClassesBounds(ClassTree node, TypeElement typeElement, AnnotatedDeclaredType bound) { // Must have compatible bound annotation as the direct super types List superBounds = PICOTypeUtil.getBoundTypesOfDirectSuperTypes(typeElement, atypeFactory); @@ -816,13 +727,68 @@ private boolean checkCompatabilityBetweenBoundAndSuperClassesBounds(ClassTree no if (!atypeFactory.getQualifierHierarchy().isSubtype( bound.getAnnotationInHierarchy(READONLY), superBound.getAnnotationInHierarchy(READONLY))) { checker.report(Result.failure("subclass.bound.incompatible", bound, superBound), node); - return false; + return true; } } } return true; } + @Override + protected void checkExtendsImplements(ClassTree classTree) { + PICOAnnotatedTypeFactory.PICOQualifierForUseTypeAnnotator annotator = new PICOAnnotatedTypeFactory.PICOQualifierForUseTypeAnnotator(atypeFactory); + if (TypesUtils.isAnonymous(TreeUtils.typeOf(classTree))) { + // Don't check extends clause on anonymous classes. + return; + } + + AnnotationMirror classAnnot = + atypeFactory.getAnnotatedType(classTree).getAnnotationInHierarchy(READONLY); + + Tree extendsClause = classTree.getExtendsClause(); + if (extendsClause != null) { + // now defaultFor annotator is not called before this + // TODO Lian: use CF default annotator instead of annotating manually here (default should be annotated before entry) + AnnotationMirror extAnnot = atypeFactory.fromTypeTree(extendsClause).getAnnotationInHierarchy(READONLY); + if (extAnnot == null) { + AnnotatedTypeMirror defaultAnnotatedExtends = atypeFactory.fromTypeTree(extendsClause).deepCopy(false); // side effect!! + annotator.visit(defaultAnnotatedExtends); + extAnnot = defaultAnnotatedExtends.getAnnotationInHierarchy(READONLY); + } + if (extAnnot != null && !AnnotationUtils.areSame(extAnnot, classAnnot)) { + checker.report( + Result.failure( + "declaration.inconsistent.with.extends.clause", + classAnnot, + extAnnot), + extendsClause); + } + + } + + List implementsClauses = classTree.getImplementsClause(); + if (implementsClauses != null) { + for (Tree impl : implementsClauses) { + // now defaultFor annotator is not called before this + // TODO Lian: use CF default annotator instead of annotating manually here (default should be annotated before entry) + AnnotationMirror implAnnot = atypeFactory.fromTypeTree(impl).getAnnotationInHierarchy(READONLY); + if (implAnnot == null) { + AnnotatedTypeMirror defaultAnnotatedImpl = atypeFactory.fromTypeTree(impl).deepCopy(false); + annotator.visit(defaultAnnotatedImpl); + implAnnot = defaultAnnotatedImpl.getAnnotationInHierarchy(READONLY); + } + if (implAnnot != null && !AnnotationUtils.areSame(implAnnot, classAnnot)) { + checker.report( + Result.failure( + "declaration.inconsistent.with.implements.clause", + classAnnot, + implAnnot), + impl); + } + } + } + } + private boolean checkCompatabilityBetweenBoundAndExtendsImplements(ClassTree node, AnnotatedDeclaredType bound) { if (infer) { atypeFactory.getAnnotatedType(node); @@ -831,29 +797,22 @@ private boolean checkCompatabilityBetweenBoundAndExtendsImplements(ClassTree nod boolean hasSame; Tree ext = node.getExtendsClause(); if (ext != null) { - // getAnnotatedType(Tree) cannot get annotations from stub - // TODO: why @ from stub does not present when using getAnnotatedType(Tree)? - // hint: however getAnnotatedType(ExpressionTree) works perfectly. - AnnotatedTypeMirror extendsType= atypeFactory.getAnnotatedType(TreeUtils.elementFromTree(ext)); + AnnotatedTypeMirror extendsType= atypeFactory.getAnnotatedType(ext); if (infer) { ((PICOInferenceAnnotatedTypeFactory) atypeFactory).getVariableAnnotator().visit(extendsType, ext); areEqual(bound, extendsType, "bound.extends.incompatible", node); } else { + AnnotationMirror superAnnoOnUse = atypeFactory.fromTypeTree(ext).getAnnotationInHierarchy(READONLY); + hasSame = bound.getAnnotations().size() == extendsType.getAnnotations().size() && AnnotationUtils.areSame(extendsType.getAnnotationInHierarchy(READONLY), bound.getAnnotationInHierarchy(READONLY)); - if (!hasSame) { - // mutable and immutable could extends RDM. See Mier's thesis 4.8.3 (p.43) - if (!(bound.getAnnotations().size() == extendsType.getAnnotations().size() - && AnnotationUtils.areSame(extendsType.getAnnotationInHierarchy(READONLY), - RECEIVER_DEPENDANT_MUTABLE) - && (AnnotationUtils.areSame(bound.getAnnotationInHierarchy(READONLY), - MUTABLE) - || AnnotationUtils.areSame(bound.getAnnotationInHierarchy(READONLY), - IMMUTABLE)))) { + // TODO rdm defaulting should be prevented instead of suppressing error here + boolean defaultedRdm = superAnnoOnUse == null && + AnnotationUtils.areSame(extendsType.getAnnotationInHierarchy(READONLY), RECEIVER_DEPENDANT_MUTABLE); + if (!defaultedRdm && !hasSame) { checker.report(Result.failure("bound.extends.incompatible"), node); return false; - } } } } diff --git a/src/main/java/pico/typecheck/PICOVisitor.java b/src/main/java/pico/typecheck/PICOVisitor.java index d070dd7..a59ad68 100644 --- a/src/main/java/pico/typecheck/PICOVisitor.java +++ b/src/main/java/pico/typecheck/PICOVisitor.java @@ -21,6 +21,7 @@ import javax.lang.model.element.ExecutableElement; import javax.lang.model.element.TypeElement; import javax.lang.model.element.VariableElement; +import javax.lang.model.type.TypeKind; import org.checkerframework.checker.compilermsgs.qual.CompilerMessageKey; import org.checkerframework.checker.initialization.InitializationVisitor; @@ -96,6 +97,24 @@ public boolean isValidUse(AnnotatedDeclaredType declarationType, AnnotatedDeclar // // Object // return true; // } + // declarationType is now upper bound. Could be READONLY + // needs adaption for constructors and enum + // TODO: adapt to default instead of upper-bound or repeated rules + if (tree instanceof MethodTree && TreeUtils.isConstructor((MethodTree) tree)) { + if (AnnotationUtils.areSame(declared, READONLY)) { + declared = MUTABLE; + if (atypeFactory.getPath(tree).getParentPath().getLeaf().getKind() == Kind.ENUM) { + declared = IMMUTABLE; + } + } + } + if (tree.getKind() == Kind.ENUM) { + declared = IMMUTABLE; + } + // default to implicit + if (PICOTypeUtil.isImplicitlyImmutableType(declarationType)) { + declared = IMMUTABLE; + } if (AnnotationUtils.areSame(declared, RECEIVER_DEPENDANT_MUTABLE) || AnnotationUtils.areSame(declared, READONLY)) { // Element is declared with @ReceiverDependantMutable bound, any instantiation is allowed. We don't use // a subtype check to validate the correct usage here. Because @Readonly is the super type of @@ -109,6 +128,17 @@ public boolean isValidUse(AnnotatedDeclaredType declarationType, AnnotatedDeclar AnnotationMirror used = useType.getAnnotationInHierarchy(READONLY); assert AnnotationUtils.areSame(declared, MUTABLE) || AnnotationUtils.areSame(declared, IMMUTABLE); + return isAnnoValidUse(declared, used); + } + + static private boolean isAnnoValidUse(AnnotationMirror declared, AnnotationMirror used) { + if (AnnotationUtils.areSame(declared, RECEIVER_DEPENDANT_MUTABLE) || AnnotationUtils.areSame(declared, READONLY)) { + // Element is declared with @ReceiverDependantMutable bound, any instantiation is allowed. We don't use + // a subtype check to validate the correct usage here. Because @Readonly is the super type of + // @ReceiverDependantMutable, but it's still considered valid usage. + return true; + } + if (AnnotationUtils.areSame(declared, MUTABLE) && !(AnnotationUtils.areSame(used, IMMUTABLE) || AnnotationUtils.areSame(used, RECEIVER_DEPENDANT_MUTABLE))) { return true; @@ -155,6 +185,8 @@ protected void commonAssignmentCheck( commonAssignmentCheck(var, valueExp, errorKey); } + + @Override protected void checkConstructorInvocation(AnnotatedDeclaredType invocation, AnnotatedExecutableType constructor, NewClassTree newClassTree) { // TODO Is the copied code really needed? @@ -178,6 +210,16 @@ protected void checkConstructorInvocation(AnnotatedDeclaredType invocation, Anno } /*Copied Code End*/ + // TODO use CF base check + // if no explicit anno it must inherited from class decl + AnnotationMirror declAnno = constructor.getReturnType().getAnnotationInHierarchy(READONLY); + AnnotationMirror useAnno = invocation.getAnnotationInHierarchy(READONLY); + declAnno = declAnno == null ? MUTABLE : declAnno; + + if(useAnno != null && !AnnotationUtils.areSameByName(declAnno, POLY_MUTABLE) && !isAnnoValidUse(declAnno, useAnno)) { + checker.report(Result.failure("type.invalid.annotations.on.use", declAnno, useAnno), newClassTree); + } + // The immutability return qualifier of the constructor (returnType) must be supertype of the // constructor invocation immutability qualifier(invocation). if (!atypeFactory.getTypeHierarchy().isSubtype(invocation, returnType, READONLY)) { @@ -197,6 +239,10 @@ public Void visitMethod(MethodTree node, Void p) { checker.report(Result.failure("constructor.return.invalid", constructorReturnType), node); return super.visitMethod(node, p); } + // if no explicit anno it must inherit from class decl so identical + // => if not the same must not inherited from class decl + // => no need to check the source of the anno + } else { AnnotatedDeclaredType declareReceiverType = executableType.getReceiverType(); if (declareReceiverType != null) { @@ -347,15 +393,31 @@ private void reportFieldOrArrayWriteError(Tree node, ExpressionTree variable, An @Override public Void visitVariable(VariableTree node, Void p) { VariableElement element = TreeUtils.elementFromDeclaration(node); + AnnotatedTypeMirror type = atypeFactory.getAnnotatedType(element); if (element != null && element.getKind() == ElementKind.FIELD) { - AnnotatedTypeMirror type = atypeFactory.getAnnotatedType(element); if (type.hasAnnotation(POLY_MUTABLE)) { checker.report(Result.failure("field.polymutable.forbidden", element), node); } } + checkAndReportInvalidAnnotationOnUse(type, node); return super.visitVariable(node, p); } + private void checkAndReportInvalidAnnotationOnUse(AnnotatedTypeMirror type, Tree node) { + AnnotationMirror useAnno = type.getAnnotationInHierarchy(READONLY); + if (useAnno != null && !PICOTypeUtil.isImplicitlyImmutableType(type) && type.getKind() != TypeKind.ARRAY) { // TODO: annotate the use instead of using this + AnnotationMirror defaultAnno = MUTABLE; + for (AnnotationMirror anno : atypeFactory.getTypeDeclarationBounds(atypeFactory.getAnnotatedType(node).getUnderlyingType())) { + if (atypeFactory.getQualifierHierarchy().isSubtype(anno, READONLY) && !AnnotationUtils.areSame(anno, READONLY)) { + defaultAnno = anno; + } + } + if (!isAnnoValidUse(defaultAnno, useAnno)) { + checker.report(Result.failure("type.invalid.annotations.on.use", defaultAnno, useAnno), node); + } + } + } + @Override public Void visitNewClass(NewClassTree node, Void p) { checkNewInstanceCreation(node); From d5909e969fc8fadf04d2c44a6d7c416ba12b8b45 Mon Sep 17 00:00:00 2001 From: lnsun <57457122+lnsun@users.noreply.github.com> Date: Mon, 6 Apr 2020 15:21:15 -0400 Subject: [PATCH 035/144] test vpa over imp --- .../inference/ExtendedViewpointAdapter.java | 11 + ...PICOInferenceExtendedViewpointAdapter.java | 37 ++++ .../PICOInferenceRealTypeFactory.java | 28 ++- .../inference/PICOInferenceValidator.java | 16 +- .../pico/inference/PICOInferenceVisitor.java | 193 ++++++------------ .../inference/PublicViewpointAdapter.java | 5 + .../pico/typecheck/PICOViewpointAdapter.java | 17 +- src/main/java/pico/typecheck/PICOVisitor.java | 6 +- 8 files changed, 167 insertions(+), 146 deletions(-) create mode 100644 src/main/java/pico/inference/ExtendedViewpointAdapter.java create mode 100644 src/main/java/pico/inference/PICOInferenceExtendedViewpointAdapter.java create mode 100644 src/main/java/pico/inference/PublicViewpointAdapter.java diff --git a/src/main/java/pico/inference/ExtendedViewpointAdapter.java b/src/main/java/pico/inference/ExtendedViewpointAdapter.java new file mode 100644 index 0000000..f88846a --- /dev/null +++ b/src/main/java/pico/inference/ExtendedViewpointAdapter.java @@ -0,0 +1,11 @@ +package pico.inference; + +import org.checkerframework.framework.type.AnnotatedTypeMirror; +import org.checkerframework.framework.type.ViewpointAdapter; + +import javax.lang.model.element.AnnotationMirror; + +public interface ExtendedViewpointAdapter extends ViewpointAdapter { + AnnotatedTypeMirror rawCombineAnnotationWithType(AnnotationMirror anno, AnnotatedTypeMirror type); + AnnotationMirror rawCombineAnnotationWithAnnotation(AnnotationMirror anno, AnnotationMirror type); +} diff --git a/src/main/java/pico/inference/PICOInferenceExtendedViewpointAdapter.java b/src/main/java/pico/inference/PICOInferenceExtendedViewpointAdapter.java new file mode 100644 index 0000000..e6d886d --- /dev/null +++ b/src/main/java/pico/inference/PICOInferenceExtendedViewpointAdapter.java @@ -0,0 +1,37 @@ +package pico.inference; + +import com.sun.source.tree.Tree; +import org.checkerframework.framework.type.AnnotatedTypeFactory; +import org.checkerframework.framework.type.AnnotatedTypeMirror; + +import javax.lang.model.element.AnnotationMirror; +import javax.lang.model.element.Element; + +public class PICOInferenceExtendedViewpointAdapter extends PICOInferenceViewpointAdapter implements ExtendedViewpointAdapter { + + public PICOInferenceExtendedViewpointAdapter(AnnotatedTypeFactory atypeFactory) { + super(atypeFactory); + } + + /** + * (Extended behaviour) viewpoint adapt super clause to its class declaration. Class declaration acts like receiver. + * @param classType class declaration itself + * @param superEle element of extends / implements clause + * @param superType type of extends / implements clause + */ + public void viewpointAdaptSuperClause(AnnotatedTypeMirror.AnnotatedDeclaredType classType, Element superEle, AnnotatedTypeMirror.AnnotatedDeclaredType superType) { +// AnnotatedTypeMirror adapted = combineTypeWithType(classType, superType); + AnnotationMirror adapted = combineAnnotationWithAnnotation(extractAnnotationMirror(classType), extractAnnotationMirror(superType)); + superType.replaceAnnotation(adapted); + + } + + public AnnotatedTypeMirror rawCombineAnnotationWithType(AnnotationMirror anno, AnnotatedTypeMirror type) { + return combineAnnotationWithType(anno, type); + } + + @Override + public AnnotationMirror rawCombineAnnotationWithAnnotation(AnnotationMirror anno, AnnotationMirror type) { + return combineAnnotationWithAnnotation(anno, type); + } +} diff --git a/src/main/java/pico/inference/PICOInferenceRealTypeFactory.java b/src/main/java/pico/inference/PICOInferenceRealTypeFactory.java index 2d6c9bd..b5fa9a5 100644 --- a/src/main/java/pico/inference/PICOInferenceRealTypeFactory.java +++ b/src/main/java/pico/inference/PICOInferenceRealTypeFactory.java @@ -11,6 +11,7 @@ import java.util.List; import java.util.Set; +import javax.lang.model.element.AnnotationMirror; import javax.lang.model.element.Element; import org.checkerframework.common.basetype.BaseAnnotatedTypeFactory; @@ -50,7 +51,7 @@ * to InitializationAnnotatedTypeFactory as if there is only one mutability qualifier hierarchy. * This class has lots of copied code from PICOAnnotatedTypeFactory. The two should be in sync. */ -public class PICOInferenceRealTypeFactory extends BaseAnnotatedTypeFactory { +public class PICOInferenceRealTypeFactory extends BaseAnnotatedTypeFactory implements PublicViewpointAdapter { public PICOInferenceRealTypeFactory(BaseTypeChecker checker, boolean useFlow) { super(checker, useFlow); @@ -174,4 +175,29 @@ protected DefaultQualifierForUseTypeAnnotator createDefaultForUseTypeAnnotator() return new PICOAnnotatedTypeFactory.PICOQualifierForUseTypeAnnotator(this); } + @Override + public AnnotatedTypeMirror getTypeOfExtendsImplements(Tree clause) { + // add default anno from class main qual, if no qual present + AnnotatedTypeMirror enclosing = getAnnotatedType(TreeUtils.enclosingClass(getPath(clause))); + AnnotationMirror mainBound = enclosing.getAnnotationInHierarchy(READONLY); + AnnotatedTypeMirror fromTypeTree = this.fromTypeTree(clause); + if (!fromTypeTree.isAnnotatedInHierarchy(READONLY)) { + fromTypeTree.addAnnotation(mainBound); + } + + // for FBC quals +// Set bound = this.getTypeDeclarationBounds(fromTypeTree.getUnderlyingType()); +// fromTypeTree.addMissingAnnotations(bound); + return fromTypeTree; + } + + private PICOViewpointAdapter getPICOViewpointAdapter() { + return (PICOViewpointAdapter) viewpointAdapter; + } + + public ExtendedViewpointAdapter getViewpointAdapter() { + return (ExtendedViewpointAdapter) viewpointAdapter; + } + + } diff --git a/src/main/java/pico/inference/PICOInferenceValidator.java b/src/main/java/pico/inference/PICOInferenceValidator.java index 5b34e57..ba30bb0 100644 --- a/src/main/java/pico/inference/PICOInferenceValidator.java +++ b/src/main/java/pico/inference/PICOInferenceValidator.java @@ -74,13 +74,15 @@ protected boolean shouldCheckTopLevelDeclaredType(AnnotatedTypeMirror type, Tree private void checkStaticReceiverDependantMutableError(AnnotatedTypeMirror type, Tree tree) { if (PICOTypeUtil.inStaticScope(visitor.getCurrentPath())) { - if (infer) { - ((PICOInferenceVisitor)visitor).mainIsNot(type, RECEIVER_DEPENDANT_MUTABLE, "static.receiverdependantmutable.forbidden", tree); - } else { - if (type.hasAnnotation(RECEIVER_DEPENDANT_MUTABLE)) { - reportValidityResult("static.receiverdependantmutable.forbidden", type, tree); - } - } +// if (infer) { +// ((PICOInferenceVisitor)visitor).mainIsNot(type, RECEIVER_DEPENDANT_MUTABLE, "static.receiverdependantmutable.forbidden", tree); +// } else { +// if (type.hasAnnotation(RECEIVER_DEPENDANT_MUTABLE)) { +// reportValidityResult("static.receiverdependantmutable.forbidden", type, tree); +// } +// } + ((InferenceVisitor)visitor).mainIsNot(type, RECEIVER_DEPENDANT_MUTABLE, "static.receiverdependantmutable.forbidden", tree); + // TODO set isValid or move to visitor } } diff --git a/src/main/java/pico/inference/PICOInferenceVisitor.java b/src/main/java/pico/inference/PICOInferenceVisitor.java index 7a63b6e..cdfd32a 100644 --- a/src/main/java/pico/inference/PICOInferenceVisitor.java +++ b/src/main/java/pico/inference/PICOInferenceVisitor.java @@ -1,11 +1,13 @@ package pico.inference; +import static org.checkerframework.javacutil.TreeUtils.elementFromTree; import static pico.typecheck.PICOAnnotationMirrorHolder.BOTTOM; import static pico.typecheck.PICOAnnotationMirrorHolder.IMMUTABLE; import static pico.typecheck.PICOAnnotationMirrorHolder.MUTABLE; import static pico.typecheck.PICOAnnotationMirrorHolder.READONLY; import static pico.typecheck.PICOAnnotationMirrorHolder.RECEIVER_DEPENDANT_MUTABLE; +import java.lang.reflect.AnnotatedType; import java.util.Arrays; import java.util.HashSet; import java.util.Iterator; @@ -85,30 +87,22 @@ protected InferenceValidator createTypeValidator() { @Override public boolean isValidUse(AnnotatedDeclaredType declarationType, AnnotatedDeclaredType useType, Tree tree) { - if (infer) { - mainIsNot(declarationType, READONLY, "type.invalid.annotations.on.use", tree); - addMutableImmutableRdmIncompatibleConstraints(declarationType, useType); - return true; - } else { - AnnotationMirror declared = declarationType.getAnnotationInHierarchy(READONLY); - if (AnnotationUtils.areSame(declared, RECEIVER_DEPENDANT_MUTABLE) || AnnotationUtils.areSame(declared, READONLY)) { - return true; - } - assert AnnotationUtils.areSame(declared, MUTABLE) || AnnotationUtils.areSame(declared, IMMUTABLE); - - AnnotationMirror used = useType.getAnnotationInHierarchy(READONLY); - if (AnnotationUtils.areSame(declared, MUTABLE) && - !(AnnotationUtils.areSame(used, IMMUTABLE) || AnnotationUtils.areSame(used, RECEIVER_DEPENDANT_MUTABLE))) { - return true; - } + return isAdaptedSubtype(useType, declarationType); - if (AnnotationUtils.areSame(declared, IMMUTABLE) && - !(AnnotationUtils.areSame(used, MUTABLE) || AnnotationUtils.areSame(used, RECEIVER_DEPENDANT_MUTABLE))) { - return true; - } + } - return false; - } + /** + * constraint: lhs |> rhs <: lhs + * equal to decl:immutable => use:immutable || decl:mutable => use:mutable + * @param lhs left value of adaption, typically use + * @param rhs right value of adaption, typically declaration + * @return true if holds, always true during inference + */ + private boolean isAdaptedSubtype(AnnotatedDeclaredType lhs, AnnotatedDeclaredType rhs) { + ExtendedViewpointAdapter vpa = ((PublicViewpointAdapter)atypeFactory).getViewpointAdapter(); + AnnotatedTypeMirror adapted = vpa.rawCombineAnnotationWithType(lhs.getAnnotationInHierarchy(READONLY), + rhs); + return mainIsSubtype(adapted, lhs.getAnnotationInHierarchy(READONLY)); } private void addMutableImmutableRdmIncompatibleConstraints(AnnotatedDeclaredType declarationType, AnnotatedDeclaredType useType) { @@ -164,16 +158,15 @@ public boolean validateTypeOf(Tree tree) { } return validateType(tree, type); + // TODO extends clause kind = IDENTIFIER. Add case here / override getAnnotatedType / annotator? } // TODO This might not be correct for infer mode. Maybe returning as it is @Override public boolean validateType(Tree tree, AnnotatedTypeMirror type) { - if (!typeValidator.isValid(type, tree)) { - if (!infer) { + if (!typeValidator.isValid(type, tree)) { // in inference, isValid never return false return false; - } } // The initial purpose of always returning true in validateTypeOf in inference mode // might be that inference we want to generate constraints over all the ast location, @@ -188,17 +181,9 @@ public boolean validateType(Tree tree, AnnotatedTypeMirror type) { @Override protected void checkConstructorInvocation(AnnotatedDeclaredType invocation, AnnotatedExecutableType constructor, NewClassTree newClassTree) { - if (infer) { - AnnotationMirror constructorReturn = extractVarAnnot(constructor.getReturnType()); - mainIsSubtype(invocation, constructorReturn, "constructor.invocation.invalid", newClassTree); - } else { - AnnotatedDeclaredType returnType = (AnnotatedDeclaredType) constructor.getReturnType(); - if (!atypeFactory.getTypeHierarchy().isSubtype(invocation, returnType)) { - checker.report(Result.failure( - "constructor.invocation.invalid", invocation, returnType), newClassTree); - return; - } - } + AnnotationMirror constructorReturn = extractVarAnnot(constructor.getReturnType()); + mainIsSubtype(invocation, constructorReturn, "constructor.invocation.invalid", newClassTree); + super.checkConstructorInvocation(invocation, constructor, newClassTree); } @@ -233,9 +218,10 @@ public Void visitMethod(MethodTree node, Void p) { } AnnotatedDeclaredType constructorReturnType = (AnnotatedDeclaredType) executableType.getReturnType(); + // Constructor return cannot be @Readonly + mainIsNot(constructorReturnType, READONLY, "constructor.return.invalid", node); + if (infer) { - // Constructor return cannot be @Readonly - mainIsNot(constructorReturnType, READONLY, "constructor.return.invalid", node); ConstraintManager constraintManager = InferenceMain.getInstance().getConstraintManager(); SlotManager slotManager = InferenceMain.getInstance().getSlotManager(); Slot boundSlot = slotManager.getVariableSlot(bound); @@ -245,12 +231,9 @@ public Void visitMethod(MethodTree node, Void p) { Constraint subtypeConstraint = constraintManager.createSubtypeConstraint(consRetSlot, boundSlot); // bound != @ReceiverDependantMutable -> consRet <: bound constraintManager.addImplicationConstraint(Arrays.asList(inequalityConstraint), subtypeConstraint); - } else { - if (constructorReturnType.hasAnnotation(READONLY)) { - checker.report(Result.failure("constructor.return.invalid", constructorReturnType), node); - return super.visitMethod(node, p); - } + // TODO Add typecheck for this? } + } else { // Additional logic compared to PICOVisitor to prefer declared receiver and parameters // tp be @Readonly in inference results. @@ -267,17 +250,9 @@ public Void visitMethod(MethodTree node, Void p) { } // Above is additional preference logic if (declaredReceiverType != null) { - if (infer) { - addMutableImmutableRdmIncompatibleConstraints(bound, declaredReceiverType); - } else { - if (!bound.hasAnnotation(RECEIVER_DEPENDANT_MUTABLE) - && !atypeFactory.getQualifierHierarchy().isSubtype( - declaredReceiverType.getAnnotationInHierarchy(READONLY), - bound.getAnnotationInHierarchy(READONLY)) - // Below three are allowed on declared receiver types of instance methods in either @Mutable class or @Immutable class - && !declaredReceiverType.hasAnnotation(READONLY)) { - checker.report(Result.failure("method.receiver.incompatible", declaredReceiverType), node); - } + assert bound != null; + if (!isAdaptedSubtype(declaredReceiverType, bound)){ + checker.report(Result.failure("method.receiver.incompatible", declaredReceiverType), node); } } } @@ -488,6 +463,7 @@ private void checkAssignableField(ExpressionTree node, ExpressionTree variable, Slot receiver_dependant_mutable = slotManager.getSlot(RECEIVER_DEPENDANT_MUTABLE); Constraint receiverReadOnly = constraintManager.createEqualityConstraint(receiverSlot, readonly); Constraint fieldNotRDM = constraintManager.createInequalityConstraint(fieldSlot, receiver_dependant_mutable); + // receiver = READONLY constraintManager.addImplicationConstraint(Arrays.asList(receiverReadOnly), fieldNotRDM); } else { if (receiverType.hasAnnotation(READONLY) && fieldType.hasAnnotation(RECEIVER_DEPENDANT_MUTABLE)) { @@ -498,6 +474,7 @@ private void checkAssignableField(ExpressionTree node, ExpressionTree variable, } private void checkInitializingObject(ExpressionTree node, ExpressionTree variable, AnnotatedTypeMirror receiverType) { + // TODO rm infer after mainIsNot returns bool if (infer) { // Can be anything from mutable, immutable or receiverdependantmutable mainIsNot(receiverType, READONLY, "illegal.field.write", node); @@ -509,6 +486,7 @@ private void checkInitializingObject(ExpressionTree node, ExpressionTree variabl } private void checkMutableReceiverCase(ExpressionTree node, ExpressionTree variable, AnnotatedTypeMirror receiverType) { + // TODO rm infer after mainIs returns bool if (infer) { mainIs(receiverType, MUTABLE, "illegal.field.write", node); } else { @@ -604,14 +582,8 @@ public Void visitNewArray(NewArrayTree node, Void p) { private void checkNewInstanceCreation(Tree node) { AnnotatedTypeMirror type = atypeFactory.getAnnotatedType(node); - if (infer) { - // Ensure only @Mutable/@Immutable/@ReceiverDependantMutable are inferred on new instance creation - mainIsNoneOf(type, new AnnotationMirror[]{READONLY}, "pico.new.invalid", node); - } else { - if (type.hasAnnotation(READONLY)) { - checker.report(Result.failure("pico.new.invalid", type), node); - } - } + // Ensure only @Mutable/@Immutable/@ReceiverDependantMutable are inferred on new instance creation + mainIsNoneOf(type, new AnnotationMirror[]{READONLY}, "pico.new.invalid", node); } // Completely copied from PICOVisitor @@ -686,28 +658,38 @@ public void processClassTree(ClassTree node) { AnnotatedDeclaredType bound = PICOTypeUtil.getBoundTypeOfTypeDeclaration(typeElement, atypeFactory); - if (infer) { - mainIsNot(bound, READONLY, "class.bound.invalid", node); - if (checker.hasOption("optimalSolution")) { - addPreference(bound, RECEIVER_DEPENDANT_MUTABLE, 2); - addPreference(bound, IMMUTABLE, 2); - } - } else { - // Has to be either @Mutable, @ReceiverDependantMutable or @Immutable, nothing else - if (!bound.hasAnnotation(MUTABLE) && !bound.hasAnnotation(RECEIVER_DEPENDANT_MUTABLE) && !bound.hasAnnotation(IMMUTABLE)) { - checker.report(Result.failure("class.bound.invalid", bound), node); - return;// Doesn't process the class tree anymore - } + mainIsNot(bound, READONLY, "class.bound.invalid", node); + if (checker.hasOption("optimalSolution")) { + addPreference(bound, RECEIVER_DEPENDANT_MUTABLE, 2); + addPreference(bound, IMMUTABLE, 2); } if (!checkCompatabilityBetweenBoundAndSuperClassesBounds(node, typeElement, bound)) { return; } - if (!checkCompatabilityBetweenBoundAndExtendsImplements(node, bound)) { - return; + if (node.getExtendsClause() != null) { + // compare extends type with its decl + Tree extClause = node.getExtendsClause(); + AnnotatedTypeMirror ext = atypeFactory.getTypeOfExtendsImplements(extClause); + AnnotatedTypeMirror extDecl = atypeFactory.getAnnotatedType(TreeUtils.elementFromTree(extClause)); + System.err.println("V: "+ext); + assert ext instanceof AnnotatedDeclaredType; + assert extDecl instanceof AnnotatedDeclaredType; + + + if (!isAdaptedSubtype((AnnotatedDeclaredType) ext, (AnnotatedDeclaredType) extDecl)) { + checker.report( + Result.failure( + "invalid.annotation.on.use", node.getExtendsClause()), node); + } } + +// if (!checkCompatabilityBetweenBoundAndExtendsImplements(node, bound)) { +// return; +// } + // Reach this point iff 1) bound annotation is one of mutable, rdm or immutable; // 2) bound is compatible with bounds on super types. Only then continue processing // the class tree @@ -734,61 +716,6 @@ private boolean checkCompatabilityBetweenBoundAndSuperClassesBounds(ClassTree no return true; } - @Override - protected void checkExtendsImplements(ClassTree classTree) { - PICOAnnotatedTypeFactory.PICOQualifierForUseTypeAnnotator annotator = new PICOAnnotatedTypeFactory.PICOQualifierForUseTypeAnnotator(atypeFactory); - if (TypesUtils.isAnonymous(TreeUtils.typeOf(classTree))) { - // Don't check extends clause on anonymous classes. - return; - } - - AnnotationMirror classAnnot = - atypeFactory.getAnnotatedType(classTree).getAnnotationInHierarchy(READONLY); - - Tree extendsClause = classTree.getExtendsClause(); - if (extendsClause != null) { - // now defaultFor annotator is not called before this - // TODO Lian: use CF default annotator instead of annotating manually here (default should be annotated before entry) - AnnotationMirror extAnnot = atypeFactory.fromTypeTree(extendsClause).getAnnotationInHierarchy(READONLY); - if (extAnnot == null) { - AnnotatedTypeMirror defaultAnnotatedExtends = atypeFactory.fromTypeTree(extendsClause).deepCopy(false); // side effect!! - annotator.visit(defaultAnnotatedExtends); - extAnnot = defaultAnnotatedExtends.getAnnotationInHierarchy(READONLY); - } - if (extAnnot != null && !AnnotationUtils.areSame(extAnnot, classAnnot)) { - checker.report( - Result.failure( - "declaration.inconsistent.with.extends.clause", - classAnnot, - extAnnot), - extendsClause); - } - - } - - List implementsClauses = classTree.getImplementsClause(); - if (implementsClauses != null) { - for (Tree impl : implementsClauses) { - // now defaultFor annotator is not called before this - // TODO Lian: use CF default annotator instead of annotating manually here (default should be annotated before entry) - AnnotationMirror implAnnot = atypeFactory.fromTypeTree(impl).getAnnotationInHierarchy(READONLY); - if (implAnnot == null) { - AnnotatedTypeMirror defaultAnnotatedImpl = atypeFactory.fromTypeTree(impl).deepCopy(false); - annotator.visit(defaultAnnotatedImpl); - implAnnot = defaultAnnotatedImpl.getAnnotationInHierarchy(READONLY); - } - if (implAnnot != null && !AnnotationUtils.areSame(implAnnot, classAnnot)) { - checker.report( - Result.failure( - "declaration.inconsistent.with.implements.clause", - classAnnot, - implAnnot), - impl); - } - } - } - } - private boolean checkCompatabilityBetweenBoundAndExtendsImplements(ClassTree node, AnnotatedDeclaredType bound) { if (infer) { atypeFactory.getAnnotatedType(node); @@ -812,7 +739,7 @@ private boolean checkCompatabilityBetweenBoundAndExtendsImplements(ClassTree nod AnnotationUtils.areSame(extendsType.getAnnotationInHierarchy(READONLY), RECEIVER_DEPENDANT_MUTABLE); if (!defaultedRdm && !hasSame) { checker.report(Result.failure("bound.extends.incompatible"), node); - return false; + return true; } } } @@ -822,7 +749,7 @@ private boolean checkCompatabilityBetweenBoundAndExtendsImplements(ClassTree nod for (Tree im : impls) { // TODO: why @ from stub does not present when using getAnnotatedType(Tree)? // hint: however getAnnotatedType(ExpressionTree) works perfectly. - AnnotatedTypeMirror implementsType = atypeFactory.getAnnotatedType(TreeUtils.elementFromTree(im)); + AnnotatedTypeMirror implementsType = atypeFactory.getAnnotatedType(elementFromTree(im)); if (infer) { ((PICOInferenceAnnotatedTypeFactory) atypeFactory).getVariableAnnotator().visit(implementsType, im); areEqual(bound, implementsType, "bound.implements.incompatible", node); diff --git a/src/main/java/pico/inference/PublicViewpointAdapter.java b/src/main/java/pico/inference/PublicViewpointAdapter.java new file mode 100644 index 0000000..398d75a --- /dev/null +++ b/src/main/java/pico/inference/PublicViewpointAdapter.java @@ -0,0 +1,5 @@ +package pico.inference; + +public interface PublicViewpointAdapter { + ExtendedViewpointAdapter getViewpointAdapter(); +} diff --git a/src/main/java/pico/typecheck/PICOViewpointAdapter.java b/src/main/java/pico/typecheck/PICOViewpointAdapter.java index 392bfe3..3d37045 100644 --- a/src/main/java/pico/typecheck/PICOViewpointAdapter.java +++ b/src/main/java/pico/typecheck/PICOViewpointAdapter.java @@ -19,11 +19,12 @@ import org.checkerframework.javacutil.BugInCF; import exceptions.UnkownImmutabilityQualifierException; +import pico.inference.ExtendedViewpointAdapter; /** * Created by mier on 20/06/17. */ -public class PICOViewpointAdapter extends AbstractViewpointAdapter { +public class PICOViewpointAdapter extends AbstractViewpointAdapter implements ExtendedViewpointAdapter { public PICOViewpointAdapter(AnnotatedTypeFactory atypeFactory) { super(atypeFactory); @@ -60,7 +61,19 @@ protected AnnotationMirror combineAnnotationWithAnnotation(AnnotationMirror rece throw new BugInCF("Unknown declared modifier: " + declaredAnnotation, new UnkownImmutabilityQualifierException()); } } -// + + public AnnotatedTypeMirror rawCombineAnnotationWithType(AnnotationMirror anno, AnnotatedTypeMirror type) { + System.err.println("VPA: " + anno + " ->" + type); + return combineAnnotationWithType(anno, type); + } + + @Override + public AnnotationMirror rawCombineAnnotationWithAnnotation(AnnotationMirror anno, AnnotationMirror type) { + System.err.println("VPA: " + anno + " ->" + type); + return combineAnnotationWithAnnotation(anno, type); + } + + // // @Override // protected AnnotationMirror getModifier(AnnotatedTypeMirror atm, AnnotatedTypeFactory f) { // return atm.getAnnotationInHierarchy(READONLY); diff --git a/src/main/java/pico/typecheck/PICOVisitor.java b/src/main/java/pico/typecheck/PICOVisitor.java index a59ad68..66f23ab 100644 --- a/src/main/java/pico/typecheck/PICOVisitor.java +++ b/src/main/java/pico/typecheck/PICOVisitor.java @@ -108,9 +108,9 @@ public boolean isValidUse(AnnotatedDeclaredType declarationType, AnnotatedDeclar } } } - if (tree.getKind() == Kind.ENUM) { - declared = IMMUTABLE; - } +// if (tree.getKind() == Kind.ENUM) { +// declared = IMMUTABLE; +// } // default to implicit if (PICOTypeUtil.isImplicitlyImmutableType(declarationType)) { declared = IMMUTABLE; From 9b331969f620c65fc24dc2bd9d8fc47b36b6ae38 Mon Sep 17 00:00:00 2001 From: lnsun <57457122+lnsun@users.noreply.github.com> Date: Mon, 6 Apr 2020 15:22:53 -0400 Subject: [PATCH 036/144] TODO: resolve path issues, remove this when fixed --- src/main/java/pico/inference/jdk.astub | 328 ++++++++++++++++++ .../java/pico/inference/messages.properties | 15 + 2 files changed, 343 insertions(+) create mode 100644 src/main/java/pico/inference/jdk.astub create mode 100644 src/main/java/pico/inference/messages.properties diff --git a/src/main/java/pico/inference/jdk.astub b/src/main/java/pico/inference/jdk.astub new file mode 100644 index 0000000..9091e38 --- /dev/null +++ b/src/main/java/pico/inference/jdk.astub @@ -0,0 +1,328 @@ +import qual.Mutable; +import qual.Immutable; +import qual.ReceiverDependantMutable; +import qual.Readonly; +import qual.ObjectIdentityMethod; +import java.util.Collection; + +package java.lang; + +@ReceiverDependantMutable +class Object { + @ReceiverDependantMutable Object(); + Class getClass(@Readonly Object this); + String toString(@Readonly Object this); + @OnlyDependantOnAbstractStateField + int hashCode(@Readonly Object this); + @OnlyDependantOnAbstractStateField + boolean equals(@Readonly Object this, @Readonly Object var1); + @ReceiverDependantMutable Object clone(@ReceiverDependantMutable Object this); + @ObjectIdentityMethod + final native Class getClass(); +} + +class String { + int length(@Immutable String this); + char charAt(@Immutable String this, int var1); + String replace(@Readonly CharSequence target, @Readonly CharSequence replacement); + boolean contains(@Readonly CharSequence s); + String substring(@Immutable String this, int var1); + String substring(@Immutable String this, int var1, int var2); + String toString(@Immutable String this); + boolean equals(@Immutable Object var1); + static String valueOf(@Readonly Object var0); + static String format(String var0, @Readonly Object @Readonly ... var1); + static String format(@Readonly Locale l, String format, @Readonly Object @Readonly ... var1); +} + +class StringBuilder { + StringBuilder append(@Readonly Object var1); +} + +class StringBuffer { + int length(@Readonly StringBuffer this); + int capacity(@Readonly StringBuffer this); + StringBuffer append(@Readonly Object obj); + String substring(@Readonly StringBuffer this, int start); + CharSequence subSequence(@Readonly StringBuffer this, int start, int end); + String substring(@Readonly StringBuffer this, int start, int end); + int indexOf(@Readonly StringBuffer this, String str); + int indexOf(@Readonly StringBuffer this, String str, int fromIndex); + int lastIndexOf(@Readonly StringBuffer this, String str); + int lastIndexOf(@Readonly StringBuffer this, String str, int fromIndex); +} + +@ReceiverDependantMutable +class Throwable { + String getMessage(@ReceiverDependantMutable Throwable this); + String getLocalizedMessage(@ReceiverDependantMutable Throwable this); + Throwable getCause(@ReceiverDependantMutable Throwable this); + void printStackTrace(@ReceiverDependantMutable Throwable this); + void printStackTrace(@ReceiverDependantMutable Throwable this, PrintStream var1); + void printStackTrace(@ReceiverDependantMutable Throwable this, Throwable.PrintStreamOrWriter var1); +} + +@ReceiverDependantMutable +interface CharSequence { + int length(@Readonly CharSequence this); + char charAt(@Readonly CharSequence this, int index); + CharSequence subSequence(@Readonly CharSequence this, int start, int end); + public default IntStream chars(@Readonly CharSequence this); + public default IntStream codePoints(@Readonly CharSequence this); +} + +@ReceiverDependantMutable +class RuntimeException { + @ReceiverDependantMutable RuntimeException(@Readonly Throwable var1); + @ReceiverDependantMutable RuntimeException(String var1, @Readonly Throwable var2, boolean var3, boolean var4); +} + +@ReceiverDependantMutable +class IndexOutOfBoundsException {} + +@Immutable +class Enum> { + @Immutable Enum(String name, int ordinal); + int ordinal(@Immutable Enum this); +} + +@ReceiverDependantMutable +interface Cloneable {} + +@ReceiverDependantMutable +interface Comparable {} + +package java.util; + +@ReceiverDependantMutable +class Properties { + @Readonly Object put(@Immutable Object key, @Readonly Object value); +} + +interface Iterator {} + +@ReceiverDependantMutable +class Date { + @ReceiverDependantMutable Date(); + @ReceiverDependantMutable Date(long var1); + int getHours(@ReceiverDependantMutable Date this); +} + +@ReceiverDependantMutable +interface Collection { + boolean contains(@Readonly Collection this, @Readonly Object o); +} + +@ReceiverDependantMutable +class ArrayList { + @ReceiverDependantMutable ArrayList(); + @ReceiverDependantMutable ArrayList(@Readonly Collection var1); + boolean add(E var1); + boolean addAll(@Readonly Collection c); + E get(@Readonly ArrayList this, int index); + int size(@Readonly ArrayList this); + boolean isEmpty(@Readonly ArrayList this); + boolean contains(@Readonly ArrayList this, @Readonly Object o); + int indexOf(@Readonly ArrayList this, @Readonly Object o); + int lastIndexOf(@Readonly ArrayList this, @Readonly Object o); + void rangeCheck(@Readonly ArrayList this, int index); + Iterator iterator(@Readonly ArrayList this); +} + +@ReceiverDependantMutable +interface List { + int size(@Readonly List this); + boolean isEmpty(@Readonly List this); + Iterator iterator(@Readonly List this); + Object[] toArray(@Readonly List this); + T[] toArray(@Readonly List this, T[] a); + boolean containsAll(@Readonly List this, @Readonly Collection c); + E get(@Readonly List this, int index); + boolean contains(@Readonly List this, @Readonly Object o); + boolean remove(@Readonly Object o); + boolean removeAll(@Readonly Collection c); + boolean addAll(@Readonly Collection c); + boolean addAll(int index, @Readonly Collection c); + int indexOf(@Readonly List this, @Readonly Object o); + int lastIndexOf(@Readonly List this, @Readonly Object o); + ListIterator listIterator(@Readonly List this); + ListIterator listIterator(@Readonly List this, int index); +} + +@ReceiverDependantMutable +class AbstractList { + @ReceiverDependantMutable AbstractList(); + void add(@Mutable AbstractList this, int var1, E var2); +} + +@ReceiverDependantMutable +interface Set { + int size(@Readonly Set this); + boolean isEmpty(@Readonly Set this); + boolean contains(@Readonly Set this, @Readonly Object var1); + Iterator iterator(@Readonly Set this); + Object[] toArray(@Readonly Set this); + T[] toArray(@Readonly Set this, T[] a); + boolean containsAll(@Readonly Set this, @Readonly Collection c); + boolean remove(@Readonly Object o); + boolean addAll(@Readonly Collection c); +} + +@ReceiverDependantMutable +class HashSet { + @ReceiverDependantMutable HashSet(); + @ReceiverDependantMutable HashSet(@Readonly Collection var1); + boolean contains(@Readonly HashSet this, @Readonly Object var1); + boolean remove(@Readonly Object var1); +} + +@ReceiverDependantMutable +interface Map { + int size(@Readonly Map this); + boolean isEmpty(@Readonly Map this); + boolean containsKey(@Readonly Map this, @Readonly Object var1); + boolean containsValue(@Readonly Map this, @Readonly Object value); + V get(@Readonly Map this, @Readonly Object var1); + V remove(@Readonly Object key); + void putAll(@Readonly Map m); + Set keySet(@Readonly Map this); + Collection values(@Readonly Map this); + Set> entrySet(@Readonly Map this); +} + +@ReceiverDependantMutable +class HashMap { + @ReceiverDependantMutable HashMap(); + @ReceiverDependantMutable HashMap(@Readonly Map var1); + V get(@Readonly HashMap this, @Readonly Object key); + boolean containsKey(@Readonly HashMap this, @Readonly Object key); + boolean containsValue(@Readonly HashMap this, @Readonly Object value); +} + +class Collections { + static @Immutable List unmodifiableList(@Readonly List list); +} + +class StringJoiner { + StringJoiner(@Readonly CharSequence delimiter); + StringJoiner(@Readonly CharSequence delimiter, @Readonly CharSequence prefix, @Readonly CharSequence suffix); + StringJoiner add(@Readonly CharSequence newElement); +} + +class Arrays { + static @Immutable List asList(T @Readonly ... var0); + static String toString(int @Readonly [] var0); + static boolean equals(float @Readonly [] var0, float @Readonly [] var1); + static boolean equals(double @Readonly [] var0, double @Readonly [] var1); +} + +class Objects { + static int hashCode(@Readonly Object o); + static boolean equals(@Readonly Object a, @Readonly Object b); +} + +@ReceiverDependantMutable +class Stack { + E peek(@ReceiverDependantMutable Stack this); + boolean empty(@ReceiverDependantMutable Stack this); +} + +@ReceiverDependantMutable +class Vector { + boolean isEmpty(@Readonly Vector this); +} + +@ReceiverDependantMutable +class Hashtable { + V get(@Readonly Hashtable this, @Readonly Object key); + boolean containsKey(@Readonly Hashtable this, @Readonly Object key); +} + +package java.util.logging; +class Logger { + void log(@Readonly Level level, String msg, @Readonly Throwable thrown); +} + +package java.util.regex; +class Pattern { + Matcher matcher(@Readonly CharSequence input); + static boolean matches(String regex, @Readonly CharSequence input); + String[] split(@Readonly CharSequence input, int limit); + String[] split(@Readonly CharSequence input); + static final int countChars(@Readonly CharSequence seq, int index, int lengthInCodePoints); + static final int countCodePoints(@Readonly CharSequence seq); +} + +package java.io; + +@ReceiverDependantMutable +class PrintStream { + void print(@ReceiverDependantMutable PrintStream this, String var1); + PrintStream printf(@ReceiverDependantMutable PrintStream this, String var1, @Readonly Object @Readonly ... var2); + PrintStream format(String format, @Readonly Object @Readonly ... args); +} + +@ReceiverDependantMutable +class PrintWriter { + PrintWriter printf(@ReceiverDependantMutable PrintWriter this, String var1, @Readonly Object @Readonly ... var2); +} + +@ReceiverDependantMutable +class File { + @ReceiverDependantMutable File(@Readonly File parent, String child); + boolean isFile(@Readonly File this); + String[] list(@Readonly File this); + String getPath(@Readonly File this); + long length(@Readonly File this); + String getName(@Readonly File this); +} + +@ReceiverDependantMutable +class FileInputStream { + @ReceiverDependantMutable FileInputStream(@Readonly File file); +} + +class ObjectOutputStream { + void writeObject(@Readonly Object obj); +} + +@ReceiverDependantMutable +interface Serializable {} + +package org.hibernate; +class Session { + Object get(@Readonly Class clazz, @Readonly Serializable id); +} + +package java.awt; + +@ReceiverDependantMutable +class Container { + void add(@Readonly Component comp, @Readonly Object constraints); +} + +package org.apache.maven.plugin.logging; + +interface Log { + void info(@Readonly Log this, @Readonly CharSequence content); + void info(@Readonly Log this, @Readonly CharSequence content, @Readonly Throwable error); + void info(@Readonly Log this, @Readonly Throwable error); + void debug(@Readonly Log this, @Readonly CharSequence content); + void debug(@Readonly Log this, @Readonly CharSequence content, @Readonly Throwable error); + void debug(@Readonly Log this, @Readonly Throwable error); + void warn(@Readonly Log this, @Readonly CharSequence content); + void warn(@Readonly Log this, @Readonly CharSequence content, @Readonly Throwable error); + void warn(@Readonly Log this, @Readonly Throwable error); + void error(@Readonly Log this, @Readonly CharSequence content); + void error(@Readonly Log this, @Readonly CharSequence content, @Readonly Throwable error); + void error(@Readonly Log this, @Readonly Throwable error); + boolean isInfoEnabled(@Readonly Log this); + boolean isDebugEnabled(@Readonly Log this); + boolean isWarnEnabled(@Readonly Log this); + boolean isErrorEnabled(@Readonly Log this); +} + +package org.apache.commons.io; +class FileUtils { + static long copyFile(@Readonly File input, OutputStream output); +} diff --git a/src/main/java/pico/inference/messages.properties b/src/main/java/pico/inference/messages.properties new file mode 100644 index 0000000..1d0fd13 --- /dev/null +++ b/src/main/java/pico/inference/messages.properties @@ -0,0 +1,15 @@ +constructor.invocation.invalid=Cannot not instantiate type: %s out of constructor: %s +constructor.return.invalid=Invalid constructor return type: %s +method.receiver.incompatible=Incompatible method receiver: %s +class.bound.invalid=Invalid class bound: %s +subclass.bound.incompatible=Incompatible subclass bound: %s +illegal.field.write=Cannot write field via receiver: %s +illegal.array.write=Cannot write array via receiver: %s +static.receiverdependantmutable.forbidden=%s is forbidden in static context +pico.new.invalid=Invalid new instance type: %s +field.polymutable.forbidden=Field %s cannot be @PolyMutable +one.assignability.invalid=Only one assignability qualifier is allowed on %s +object.identity.method.invocation.invalid=Cannot invoke non-object identity method %s from object identity context! +object.identity.field.access.invalid=Object identity context cannot reference non abstract state field %s! +object.identity.static.field.access.forbidden=Object identity context cannot reference static field %s! +bound.extends.incompatible=TEST \ No newline at end of file From 2bd7c5ee6145f6cc9303934b3a8ef9d1bafeed31 Mon Sep 17 00:00:00 2001 From: lnsun <57457122+lnsun@users.noreply.github.com> Date: Mon, 6 Apr 2020 20:31:02 -0400 Subject: [PATCH 037/144] default class bound and super clause --- .../PICOInferenceRealTypeFactory.java | 40 ++++++++++++++++++- 1 file changed, 39 insertions(+), 1 deletion(-) diff --git a/src/main/java/pico/inference/PICOInferenceRealTypeFactory.java b/src/main/java/pico/inference/PICOInferenceRealTypeFactory.java index b5fa9a5..f0c83be 100644 --- a/src/main/java/pico/inference/PICOInferenceRealTypeFactory.java +++ b/src/main/java/pico/inference/PICOInferenceRealTypeFactory.java @@ -7,17 +7,23 @@ import java.lang.annotation.Annotation; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collections; +import java.util.HashSet; import java.util.LinkedHashSet; import java.util.List; import java.util.Set; import javax.lang.model.element.AnnotationMirror; import javax.lang.model.element.Element; +import javax.lang.model.type.TypeMirror; +import com.sun.source.tree.IdentifierTree; +import com.sun.source.util.TreePath; import org.checkerframework.common.basetype.BaseAnnotatedTypeFactory; import org.checkerframework.common.basetype.BaseTypeChecker; import org.checkerframework.framework.qual.RelevantJavaTypes; import org.checkerframework.framework.type.AbstractViewpointAdapter; +import org.checkerframework.framework.type.AnnotatedTypeFactory; import org.checkerframework.framework.type.AnnotatedTypeMirror; import org.checkerframework.framework.type.treeannotator.ListTreeAnnotator; import org.checkerframework.framework.type.treeannotator.LiteralTreeAnnotator; @@ -86,7 +92,8 @@ protected TreeAnnotator createTreeAnnotator() { return new ListTreeAnnotator( new PICOPropagationTreeAnnotator(this), new LiteralTreeAnnotator(this), - new PICOTreeAnnotator(this)); + new PICOTreeAnnotator(this), + new PICOSuperClauseAnnotator(this)); } // TODO Refactor super class to remove this duplicate code @@ -199,5 +206,36 @@ public ExtendedViewpointAdapter getViewpointAdapter() { return (ExtendedViewpointAdapter) viewpointAdapter; } + @Override + protected Set getDefaultTypeDeclarationBounds() { + return Collections.singleton(MUTABLE); + } + + @Override + public AnnotatedTypeMirror getAnnotatedType(Tree tree) { + if (tree.getKind() == Tree.Kind.IDENTIFIER && TreeUtils.isClassTree(getPath(tree).getParentPath().getLeaf())) { + System.err.println(tree); + } + return super.getAnnotatedType(tree); + } + public static class PICOSuperClauseAnnotator extends TreeAnnotator { + + public PICOSuperClauseAnnotator(AnnotatedTypeFactory atypeFactory) { + super(atypeFactory); + } + + @Override + public Void visitIdentifier(IdentifierTree identifierTree, AnnotatedTypeMirror annotatedTypeMirror) { + TreePath path = atypeFactory.getPath(identifierTree); + if (!annotatedTypeMirror.isAnnotatedInHierarchy(READONLY) && + TreeUtils.isClassTree(path.getParentPath().getLeaf())) { + AnnotatedTypeMirror enclosing = atypeFactory.getAnnotatedType(TreeUtils.enclosingClass(path)); + AnnotationMirror mainBound = enclosing.getAnnotationInHierarchy(READONLY); + annotatedTypeMirror.addAnnotation(mainBound); + System.err.println("ANNOT: ADDED DEFAULT FOR:" + annotatedTypeMirror); + } + return super.visitIdentifier(identifierTree, annotatedTypeMirror); + } + } } From 08d136f0a555f52a5a75b50ec7dfc0c1a9bb2344 Mon Sep 17 00:00:00 2001 From: lnsun <57457122+lnsun@users.noreply.github.com> Date: Tue, 7 Apr 2020 21:49:00 -0400 Subject: [PATCH 038/144] default class bound and super clause enhanced --- .../PICOInferenceRealTypeFactory.java | 11 +-- .../pico/inference/PICOInferenceVisitor.java | 76 +++++++------------ 2 files changed, 35 insertions(+), 52 deletions(-) diff --git a/src/main/java/pico/inference/PICOInferenceRealTypeFactory.java b/src/main/java/pico/inference/PICOInferenceRealTypeFactory.java index f0c83be..2e9dbbb 100644 --- a/src/main/java/pico/inference/PICOInferenceRealTypeFactory.java +++ b/src/main/java/pico/inference/PICOInferenceRealTypeFactory.java @@ -92,8 +92,8 @@ protected TreeAnnotator createTreeAnnotator() { return new ListTreeAnnotator( new PICOPropagationTreeAnnotator(this), new LiteralTreeAnnotator(this), - new PICOTreeAnnotator(this), - new PICOSuperClauseAnnotator(this)); + new PICOSuperClauseAnnotator(this), + new PICOTreeAnnotator(this)); } // TODO Refactor super class to remove this duplicate code @@ -228,12 +228,13 @@ public PICOSuperClauseAnnotator(AnnotatedTypeFactory atypeFactory) { @Override public Void visitIdentifier(IdentifierTree identifierTree, AnnotatedTypeMirror annotatedTypeMirror) { TreePath path = atypeFactory.getPath(identifierTree); - if (!annotatedTypeMirror.isAnnotatedInHierarchy(READONLY) && + + if ((!annotatedTypeMirror.isAnnotatedInHierarchy(READONLY) | atypeFactory.getQualifierHierarchy().findAnnotationInHierarchy(TreeUtils.typeOf(identifierTree).getAnnotationMirrors(), READONLY) == null) && TreeUtils.isClassTree(path.getParentPath().getLeaf())) { AnnotatedTypeMirror enclosing = atypeFactory.getAnnotatedType(TreeUtils.enclosingClass(path)); AnnotationMirror mainBound = enclosing.getAnnotationInHierarchy(READONLY); - annotatedTypeMirror.addAnnotation(mainBound); - System.err.println("ANNOT: ADDED DEFAULT FOR:" + annotatedTypeMirror); + annotatedTypeMirror.replaceAnnotation(mainBound); + System.err.println("ANNOT: ADDED DEFAULT FOR: " + annotatedTypeMirror); } return super.visitIdentifier(identifierTree, annotatedTypeMirror); } diff --git a/src/main/java/pico/inference/PICOInferenceVisitor.java b/src/main/java/pico/inference/PICOInferenceVisitor.java index cdfd32a..07e57b4 100644 --- a/src/main/java/pico/inference/PICOInferenceVisitor.java +++ b/src/main/java/pico/inference/PICOInferenceVisitor.java @@ -9,6 +9,7 @@ import java.lang.reflect.AnnotatedType; import java.util.Arrays; +import java.util.Collections; import java.util.HashSet; import java.util.Iterator; import java.util.List; @@ -23,6 +24,7 @@ import javax.lang.model.element.VariableElement; import javax.lang.model.type.TypeKind; +import com.sun.org.apache.regexp.internal.RE; import org.checkerframework.common.basetype.BaseAnnotatedTypeFactory; import org.checkerframework.framework.source.Result; import org.checkerframework.framework.type.AnnotatedTypeFactory.ParameterizedExecutableType; @@ -87,6 +89,11 @@ protected InferenceValidator createTypeValidator() { @Override public boolean isValidUse(AnnotatedDeclaredType declarationType, AnnotatedDeclaredType useType, Tree tree) { +// if (declarationType.hasAnnotation(READONLY)) { +// // upper bound is always @Readonly +// // only needed in typechecking +// return true; +// } return isAdaptedSubtype(useType, declarationType); } @@ -230,7 +237,7 @@ public Void visitMethod(MethodTree node, Void p) { Constraint inequalityConstraint = constraintManager.createInequalityConstraint(boundSlot, rdmSlot); Constraint subtypeConstraint = constraintManager.createSubtypeConstraint(consRetSlot, boundSlot); // bound != @ReceiverDependantMutable -> consRet <: bound - constraintManager.addImplicationConstraint(Arrays.asList(inequalityConstraint), subtypeConstraint); + constraintManager.addImplicationConstraint(Collections.singletonList(inequalityConstraint), subtypeConstraint); // TODO Add typecheck for this? } @@ -464,7 +471,7 @@ private void checkAssignableField(ExpressionTree node, ExpressionTree variable, Constraint receiverReadOnly = constraintManager.createEqualityConstraint(receiverSlot, readonly); Constraint fieldNotRDM = constraintManager.createInequalityConstraint(fieldSlot, receiver_dependant_mutable); // receiver = READONLY - constraintManager.addImplicationConstraint(Arrays.asList(receiverReadOnly), fieldNotRDM); + constraintManager.addImplicationConstraint(Collections.singletonList(receiverReadOnly), fieldNotRDM); } else { if (receiverType.hasAnnotation(READONLY) && fieldType.hasAnnotation(RECEIVER_DEPENDANT_MUTABLE)) { reportFieldOrArrayWriteError(node, variable, receiverType); @@ -665,25 +672,25 @@ public void processClassTree(ClassTree node) { } if (!checkCompatabilityBetweenBoundAndSuperClassesBounds(node, typeElement, bound)) { - return; +// return; } - if (node.getExtendsClause() != null) { - // compare extends type with its decl - Tree extClause = node.getExtendsClause(); - AnnotatedTypeMirror ext = atypeFactory.getTypeOfExtendsImplements(extClause); - AnnotatedTypeMirror extDecl = atypeFactory.getAnnotatedType(TreeUtils.elementFromTree(extClause)); - System.err.println("V: "+ext); - assert ext instanceof AnnotatedDeclaredType; - assert extDecl instanceof AnnotatedDeclaredType; - - - if (!isAdaptedSubtype((AnnotatedDeclaredType) ext, (AnnotatedDeclaredType) extDecl)) { - checker.report( - Result.failure( - "invalid.annotation.on.use", node.getExtendsClause()), node); - } - } +// if (node.getExtendsClause() != null) { +// // compare extends type with its decl +// Tree extClause = node.getExtendsClause(); +// AnnotatedTypeMirror ext = atypeFactory.getTypeOfExtendsImplements(extClause); +// AnnotatedTypeMirror extDecl = atypeFactory.getAnnotatedType(TreeUtils.elementFromTree(extClause)); +// System.err.println("V: "+ext); +// assert ext instanceof AnnotatedDeclaredType; +// assert extDecl instanceof AnnotatedDeclaredType; +// +// +// if (!isAdaptedSubtype((AnnotatedDeclaredType) ext, (AnnotatedDeclaredType) extDecl)) { +// checker.report( +// Result.failure( +// "invalid.annotation.on.use", node.getExtendsClause()), node); +// } +// } // if (!checkCompatabilityBetweenBoundAndExtendsImplements(node, bound)) { @@ -700,17 +707,9 @@ private boolean checkCompatabilityBetweenBoundAndSuperClassesBounds(ClassTree no // Must have compatible bound annotation as the direct super types List superBounds = PICOTypeUtil.getBoundTypesOfDirectSuperTypes(typeElement, atypeFactory); for (AnnotatedDeclaredType superBound : superBounds) { - if (infer) { - addSameToMutableImmutableConstraints(superBound, bound); - } else { - // If annotation on super bound is @ReceiverDependantMutable, then any valid bound is permitted. - if (superBound.hasAnnotation(RECEIVER_DEPENDANT_MUTABLE)) continue; - // super bound is either @Mutable or @Immutable. Must be the subtype of the corresponding super bound - if (!atypeFactory.getQualifierHierarchy().isSubtype( - bound.getAnnotationInHierarchy(READONLY), superBound.getAnnotationInHierarchy(READONLY))) { - checker.report(Result.failure("subclass.bound.incompatible", bound, superBound), node); - return true; - } + if (!isAdaptedSubtype(bound, superBound)) { + checker.report(Result.failure("subclass.bound.incompatible", bound, superBound), node); + return false; // TODO stop here? } } return true; @@ -776,23 +775,6 @@ private boolean checkCompatabilityBetweenBoundAndExtendsImplements(ClassTree nod return true; } - private void addSameToMutableImmutableConstraints(AnnotatedDeclaredType declarationType, AnnotatedDeclaredType useType) { - ConstraintManager constraintManager = InferenceMain.getInstance().getConstraintManager(); - SlotManager slotManager = InferenceMain.getInstance().getSlotManager(); - Slot declSlot = slotManager.getVariableSlot(declarationType); - Slot useSlot = slotManager.getVariableSlot(useType); - Slot mutable = slotManager.getSlot(MUTABLE); - Slot immutable = slotManager.getSlot(IMMUTABLE); - // declType == @Mutable -> useType == @Mutable - Constraint equalityConstraintLHS = constraintManager.createEqualityConstraint(declSlot, mutable); - Constraint equalityConstraintRHS = constraintManager.createEqualityConstraint(useSlot, mutable); - constraintManager.addImplicationConstraint(Arrays.asList(equalityConstraintLHS), equalityConstraintRHS); - // declType == @Immutable -> useType == @Immutable - equalityConstraintLHS = constraintManager.createEqualityConstraint(declSlot, immutable); - equalityConstraintRHS = constraintManager.createEqualityConstraint(useSlot, immutable); - constraintManager.addImplicationConstraint(Arrays.asList(equalityConstraintLHS), equalityConstraintRHS); - } - /** * commonAssignmentCheck() method that adapts to PICOInfer. * From 52fa1266fe8215785152ccb819153b0d1a53804b Mon Sep 17 00:00:00 2001 From: lnsun <57457122+lnsun@users.noreply.github.com> Date: Tue, 14 Apr 2020 22:26:13 -0400 Subject: [PATCH 039/144] implicit immutable class bound + cast.unsafe --- .../PICOInferenceRealTypeFactory.java | 18 ++- .../pico/inference/PICOInferenceVisitor.java | 147 +++++++++++++----- .../java/pico/typecheck/PICOTypeUtil.java | 2 +- .../inferrable/BoundsCompatible.java | 3 +- 4 files changed, 119 insertions(+), 51 deletions(-) diff --git a/src/main/java/pico/inference/PICOInferenceRealTypeFactory.java b/src/main/java/pico/inference/PICOInferenceRealTypeFactory.java index 2e9dbbb..9db2c34 100644 --- a/src/main/java/pico/inference/PICOInferenceRealTypeFactory.java +++ b/src/main/java/pico/inference/PICOInferenceRealTypeFactory.java @@ -198,10 +198,6 @@ public AnnotatedTypeMirror getTypeOfExtendsImplements(Tree clause) { return fromTypeTree; } - private PICOViewpointAdapter getPICOViewpointAdapter() { - return (PICOViewpointAdapter) viewpointAdapter; - } - public ExtendedViewpointAdapter getViewpointAdapter() { return (ExtendedViewpointAdapter) viewpointAdapter; } @@ -212,10 +208,16 @@ protected Set getDefaultTypeDeclarationBounds() { } @Override - public AnnotatedTypeMirror getAnnotatedType(Tree tree) { - if (tree.getKind() == Tree.Kind.IDENTIFIER && TreeUtils.isClassTree(getPath(tree).getParentPath().getLeaf())) { - System.err.println(tree); + public Set getTypeDeclarationBounds(TypeMirror type) { + // TODO too awkward. maybe overload isImplicitlyImmutableType + if (PICOTypeUtil.isImplicitlyImmutableType(toAnnotatedType(type, false))) { + return Collections.singleton(IMMUTABLE); } + return super.getTypeDeclarationBounds(type); + } + + @Override + public AnnotatedTypeMirror getAnnotatedType(Tree tree) { return super.getAnnotatedType(tree); } @@ -229,7 +231,7 @@ public PICOSuperClauseAnnotator(AnnotatedTypeFactory atypeFactory) { public Void visitIdentifier(IdentifierTree identifierTree, AnnotatedTypeMirror annotatedTypeMirror) { TreePath path = atypeFactory.getPath(identifierTree); - if ((!annotatedTypeMirror.isAnnotatedInHierarchy(READONLY) | atypeFactory.getQualifierHierarchy().findAnnotationInHierarchy(TreeUtils.typeOf(identifierTree).getAnnotationMirrors(), READONLY) == null) && + if (path != null && (!annotatedTypeMirror.isAnnotatedInHierarchy(READONLY) | atypeFactory.getQualifierHierarchy().findAnnotationInHierarchy(TreeUtils.typeOf(identifierTree).getAnnotationMirrors(), READONLY) == null) && TreeUtils.isClassTree(path.getParentPath().getLeaf())) { AnnotatedTypeMirror enclosing = atypeFactory.getAnnotatedType(TreeUtils.enclosingClass(path)); AnnotationMirror mainBound = enclosing.getAnnotationInHierarchy(READONLY); diff --git a/src/main/java/pico/inference/PICOInferenceVisitor.java b/src/main/java/pico/inference/PICOInferenceVisitor.java index 07e57b4..735677b 100644 --- a/src/main/java/pico/inference/PICOInferenceVisitor.java +++ b/src/main/java/pico/inference/PICOInferenceVisitor.java @@ -24,7 +24,6 @@ import javax.lang.model.element.VariableElement; import javax.lang.model.type.TypeKind; -import com.sun.org.apache.regexp.internal.RE; import org.checkerframework.common.basetype.BaseAnnotatedTypeFactory; import org.checkerframework.framework.source.Result; import org.checkerframework.framework.type.AnnotatedTypeFactory.ParameterizedExecutableType; @@ -36,7 +35,6 @@ import org.checkerframework.javacutil.AnnotationUtils; import org.checkerframework.javacutil.BugInCF; import org.checkerframework.javacutil.ElementUtils; -import org.checkerframework.javacutil.Pair; import org.checkerframework.javacutil.TreeUtils; import com.sun.source.tree.AnnotationTree; @@ -64,12 +62,8 @@ import checkers.inference.model.ConstantSlot; import checkers.inference.model.Constraint; import checkers.inference.model.ConstraintManager; -import checkers.inference.model.EqualityConstraint; -import checkers.inference.model.InequalityConstraint; import checkers.inference.model.Slot; -import checkers.inference.model.SubtypeConstraint; import org.checkerframework.javacutil.TypesUtils; -import pico.typecheck.PICOAnnotatedTypeFactory; import pico.typecheck.PICOTypeUtil; /** @@ -112,6 +106,15 @@ private boolean isAdaptedSubtype(AnnotatedDeclaredType lhs, AnnotatedDeclaredTyp return mainIsSubtype(adapted, lhs.getAnnotationInHierarchy(READONLY)); } + @Override + public boolean mainIsSubtype(AnnotatedTypeMirror ty, AnnotationMirror mod) { + boolean s = super.mainIsSubtype(ty, mod); // execute only once to avoid side-effects + if (!s) { + return atypeFactory.getQualifierHierarchy().isSubtype(ty.getAnnotationInHierarchy(mod), mod); + } + return true; + } + private void addMutableImmutableRdmIncompatibleConstraints(AnnotatedDeclaredType declarationType, AnnotatedDeclaredType useType) { final ConstraintManager constraintManager = InferenceMain.getInstance().getConstraintManager(); final SlotManager slotManager = InferenceMain.getInstance().getSlotManager(); @@ -165,7 +168,7 @@ public boolean validateTypeOf(Tree tree) { } return validateType(tree, type); - // TODO extends clause kind = IDENTIFIER. Add case here / override getAnnotatedType / annotator? + // extends clause kind = IDENTIFIER. an annotator is added for defaulting } // TODO This might not be correct for infer mode. Maybe returning as it is @@ -195,9 +198,11 @@ protected void checkConstructorInvocation(AnnotatedDeclaredType invocation, Anno } private AnnotationMirror extractVarAnnot(final AnnotatedTypeMirror atm) { - assert infer; - final SlotManager slotManager = InferenceMain.getInstance().getSlotManager(); - return slotManager.getAnnotation(slotManager.getVariableSlot(atm)); + if (infer) { + final SlotManager slotManager = InferenceMain.getInstance().getSlotManager(); + return slotManager.getAnnotation(slotManager.getVariableSlot(atm)); + } + return atm.getAnnotationInHierarchy(READONLY); } @Override @@ -213,6 +218,91 @@ public Void visitVariable(VariableTree node, Void p) { return super.visitVariable(node, p); } + @Override + protected boolean isTypeCastSafe(AnnotatedTypeMirror castType, AnnotatedTypeMirror exprType) { + QualifierHierarchy qualifierHierarchy = atypeFactory.getQualifierHierarchy(); + + final TypeKind castTypeKind = castType.getKind(); +// if (castTypeKind == TypeKind.DECLARED) { +// // Don't issue an error if the annotations are equivalent to the qualifier upper bound +// // of the type. +// AnnotatedDeclaredType castDeclared = (AnnotatedDeclaredType) castType; +// Set bounds = +// atypeFactory.getTypeDeclarationBounds(castDeclared.getUnderlyingType()); +// +// if (AnnotationUtils.areSame(castDeclared.getAnnotations(), bounds)) { +// return true; +// } +// } + + if (checker.hasOption("checkCastElementType")) { + AnnotatedTypeMirror newCastType; + if (castTypeKind == TypeKind.TYPEVAR) { + newCastType = ((AnnotatedTypeMirror.AnnotatedTypeVariable) castType).getUpperBound(); + } else { + newCastType = castType; + } + AnnotatedTypeMirror newExprType; + if (exprType.getKind() == TypeKind.TYPEVAR) { + newExprType = ((AnnotatedTypeMirror.AnnotatedTypeVariable) exprType).getUpperBound(); + } else { + newExprType = exprType; + } + + if (!atypeFactory.getTypeHierarchy().isSubtype(newExprType, newCastType)) { + return false; + } + if (newCastType.getKind() == TypeKind.ARRAY + && newExprType.getKind() != TypeKind.ARRAY) { + // Always warn if the cast contains an array, but the expression + // doesn't, as in "(Object[]) o" where o is of type Object + return false; + } else if (newCastType.getKind() == TypeKind.DECLARED + && newExprType.getKind() == TypeKind.DECLARED) { + int castSize = ((AnnotatedDeclaredType) newCastType).getTypeArguments().size(); + int exprSize = ((AnnotatedDeclaredType) newExprType).getTypeArguments().size(); + + if (castSize != exprSize) { + // Always warn if the cast and expression contain a different number of + // type arguments, e.g. to catch a cast from "Object" to "List<@NonNull + // Object>". + // TODO: the same number of arguments actually doesn't guarantee anything. + return false; + } + } else if (castTypeKind == TypeKind.TYPEVAR && exprType.getKind() == TypeKind.TYPEVAR) { + // If both the cast type and the casted expression are type variables, then check + // the bounds. + Set lowerBoundAnnotationsCast = + AnnotatedTypes.findEffectiveLowerBoundAnnotations( + qualifierHierarchy, castType); + Set lowerBoundAnnotationsExpr = + AnnotatedTypes.findEffectiveLowerBoundAnnotations( + qualifierHierarchy, exprType); + return qualifierHierarchy.isSubtype( + lowerBoundAnnotationsExpr, lowerBoundAnnotationsCast) + && qualifierHierarchy.isSubtype( + exprType.getEffectiveAnnotations(), + castType.getEffectiveAnnotations()); + } + Set castAnnos; + if (castTypeKind == TypeKind.TYPEVAR) { + // If the cast type is a type var, but the expression is not, then check that the + // type of the expression is a subtype of the lower bound. + castAnnos = + AnnotatedTypes.findEffectiveLowerBoundAnnotations( + qualifierHierarchy, castType); + } else { + castAnnos = castType.getAnnotations(); + } + + return qualifierHierarchy.isSubtype(exprType.getEffectiveAnnotations(), castAnnos); + } else { + // checkCastElementType option wasn't specified, so only check effective annotations. + return qualifierHierarchy.isSubtype( + exprType.getEffectiveAnnotations(), castType.getEffectiveAnnotations()); + } + } + @Override public Void visitMethod(MethodTree node, Void p) { AnnotatedExecutableType executableType = atypeFactory.getAnnotatedType(node); @@ -325,6 +415,7 @@ protected void checkTypecastSafety(TypeCastTree node, Void p) { // the input types to be subtypes according to Java if (!isTypeCastSafe(castType, exprType, node)) { // This is only warning message, so even though enterred this line, it doesn't cause PICOInfer to exit. + // Even if that was an error, PICOInfer would also not exit. checker.report( Result.warning("cast.unsafe", exprType.toString(true), castType.toString(true)), node); @@ -656,8 +747,7 @@ protected Set getThrowUpperBoundAnnotations() { @Override public void processClassTree(ClassTree node) { TypeElement typeElement = TreeUtils.elementFromDeclaration(node); - // TODO Don't process anonymous class. I'm not even sure if whether processClassTree(ClassTree) is - // called on anonymous class tree + // Don't process anonymous class. if (TypesUtils.isAnonymous(TreeUtils.typeOf(node))) { super.processClassTree(node); return; @@ -671,35 +761,10 @@ public void processClassTree(ClassTree node) { addPreference(bound, IMMUTABLE, 2); } - if (!checkCompatabilityBetweenBoundAndSuperClassesBounds(node, typeElement, bound)) { -// return; - } - -// if (node.getExtendsClause() != null) { -// // compare extends type with its decl -// Tree extClause = node.getExtendsClause(); -// AnnotatedTypeMirror ext = atypeFactory.getTypeOfExtendsImplements(extClause); -// AnnotatedTypeMirror extDecl = atypeFactory.getAnnotatedType(TreeUtils.elementFromTree(extClause)); -// System.err.println("V: "+ext); -// assert ext instanceof AnnotatedDeclaredType; -// assert extDecl instanceof AnnotatedDeclaredType; -// -// -// if (!isAdaptedSubtype((AnnotatedDeclaredType) ext, (AnnotatedDeclaredType) extDecl)) { -// checker.report( -// Result.failure( -// "invalid.annotation.on.use", node.getExtendsClause()), node); -// } -// } - - -// if (!checkCompatabilityBetweenBoundAndExtendsImplements(node, bound)) { -// return; -// } +// // duplicated logic - remove later +// checkCompatabilityBetweenBoundAndSuperClassesBounds(node, typeElement, bound); - // Reach this point iff 1) bound annotation is one of mutable, rdm or immutable; - // 2) bound is compatible with bounds on super types. Only then continue processing - // the class tree + // Always reach this point. Do not suppress errors. super.processClassTree(node); } @@ -709,7 +774,7 @@ private boolean checkCompatabilityBetweenBoundAndSuperClassesBounds(ClassTree no for (AnnotatedDeclaredType superBound : superBounds) { if (!isAdaptedSubtype(bound, superBound)) { checker.report(Result.failure("subclass.bound.incompatible", bound, superBound), node); - return false; // TODO stop here? + return false; // do not stop processing if failed } } return true; diff --git a/src/main/java/pico/typecheck/PICOTypeUtil.java b/src/main/java/pico/typecheck/PICOTypeUtil.java index 5a71f04..b33adce 100644 --- a/src/main/java/pico/typecheck/PICOTypeUtil.java +++ b/src/main/java/pico/typecheck/PICOTypeUtil.java @@ -151,7 +151,7 @@ public static List getBoundTypesOfDirectSuperTypes(TypeEl supertypecls = null; } - if (supertypecls != null && supertypecls.getKind().name() != TypeKind.NONE.name()) { + if (supertypecls != null && !supertypecls.getKind().name().equals(TypeKind.NONE.name())) { TypeElement supercls = (TypeElement) ((DeclaredType) supertypecls).asElement(); boundsOfSupers.add(getBoundTypeOfTypeDeclaration(supercls, atypeFactory)); } diff --git a/testinput/inference/inferrable/BoundsCompatible.java b/testinput/inference/inferrable/BoundsCompatible.java index 6e8daa7..5303c3f 100644 --- a/testinput/inference/inferrable/BoundsCompatible.java +++ b/testinput/inference/inferrable/BoundsCompatible.java @@ -13,5 +13,6 @@ class Level1B extends BoundsCompatible {} @Mutable class Level2A extends Level1A {} -// :: fixable-error: (subclass.bound.incompatible) +// fixable-error subclass.bound.incompatible removed. +// :: fixable-error: (type.invalid.annotations.on.use) :: fixable-error: (super.invocation.invalid) @Immutable class Level2B extends Level1B {} From 45f4c0f7161d7ac458b501b1b84f232f3a12dfd0 Mon Sep 17 00:00:00 2001 From: lnsun <57457122+lnsun@users.noreply.github.com> Date: Wed, 15 Apr 2020 19:47:13 -0400 Subject: [PATCH 040/144] remove wrong cast.unsafe keys --- .../pico/inference/PICOInferenceVisitor.java | 154 +----------------- .../inference/inferrable/FixableTypeCast.java | 1 - .../inference/inferrable/RawIterator.java | 4 +- .../inference/inferrable/StrangeReadonly.java | 2 - 4 files changed, 7 insertions(+), 154 deletions(-) diff --git a/src/main/java/pico/inference/PICOInferenceVisitor.java b/src/main/java/pico/inference/PICOInferenceVisitor.java index 735677b..50212e0 100644 --- a/src/main/java/pico/inference/PICOInferenceVisitor.java +++ b/src/main/java/pico/inference/PICOInferenceVisitor.java @@ -218,91 +218,6 @@ public Void visitVariable(VariableTree node, Void p) { return super.visitVariable(node, p); } - @Override - protected boolean isTypeCastSafe(AnnotatedTypeMirror castType, AnnotatedTypeMirror exprType) { - QualifierHierarchy qualifierHierarchy = atypeFactory.getQualifierHierarchy(); - - final TypeKind castTypeKind = castType.getKind(); -// if (castTypeKind == TypeKind.DECLARED) { -// // Don't issue an error if the annotations are equivalent to the qualifier upper bound -// // of the type. -// AnnotatedDeclaredType castDeclared = (AnnotatedDeclaredType) castType; -// Set bounds = -// atypeFactory.getTypeDeclarationBounds(castDeclared.getUnderlyingType()); -// -// if (AnnotationUtils.areSame(castDeclared.getAnnotations(), bounds)) { -// return true; -// } -// } - - if (checker.hasOption("checkCastElementType")) { - AnnotatedTypeMirror newCastType; - if (castTypeKind == TypeKind.TYPEVAR) { - newCastType = ((AnnotatedTypeMirror.AnnotatedTypeVariable) castType).getUpperBound(); - } else { - newCastType = castType; - } - AnnotatedTypeMirror newExprType; - if (exprType.getKind() == TypeKind.TYPEVAR) { - newExprType = ((AnnotatedTypeMirror.AnnotatedTypeVariable) exprType).getUpperBound(); - } else { - newExprType = exprType; - } - - if (!atypeFactory.getTypeHierarchy().isSubtype(newExprType, newCastType)) { - return false; - } - if (newCastType.getKind() == TypeKind.ARRAY - && newExprType.getKind() != TypeKind.ARRAY) { - // Always warn if the cast contains an array, but the expression - // doesn't, as in "(Object[]) o" where o is of type Object - return false; - } else if (newCastType.getKind() == TypeKind.DECLARED - && newExprType.getKind() == TypeKind.DECLARED) { - int castSize = ((AnnotatedDeclaredType) newCastType).getTypeArguments().size(); - int exprSize = ((AnnotatedDeclaredType) newExprType).getTypeArguments().size(); - - if (castSize != exprSize) { - // Always warn if the cast and expression contain a different number of - // type arguments, e.g. to catch a cast from "Object" to "List<@NonNull - // Object>". - // TODO: the same number of arguments actually doesn't guarantee anything. - return false; - } - } else if (castTypeKind == TypeKind.TYPEVAR && exprType.getKind() == TypeKind.TYPEVAR) { - // If both the cast type and the casted expression are type variables, then check - // the bounds. - Set lowerBoundAnnotationsCast = - AnnotatedTypes.findEffectiveLowerBoundAnnotations( - qualifierHierarchy, castType); - Set lowerBoundAnnotationsExpr = - AnnotatedTypes.findEffectiveLowerBoundAnnotations( - qualifierHierarchy, exprType); - return qualifierHierarchy.isSubtype( - lowerBoundAnnotationsExpr, lowerBoundAnnotationsCast) - && qualifierHierarchy.isSubtype( - exprType.getEffectiveAnnotations(), - castType.getEffectiveAnnotations()); - } - Set castAnnos; - if (castTypeKind == TypeKind.TYPEVAR) { - // If the cast type is a type var, but the expression is not, then check that the - // type of the expression is a subtype of the lower bound. - castAnnos = - AnnotatedTypes.findEffectiveLowerBoundAnnotations( - qualifierHierarchy, castType); - } else { - castAnnos = castType.getAnnotations(); - } - - return qualifierHierarchy.isSubtype(exprType.getEffectiveAnnotations(), castAnnos); - } else { - // checkCastElementType option wasn't specified, so only check effective annotations. - return qualifierHierarchy.isSubtype( - exprType.getEffectiveAnnotations(), castType.getEffectiveAnnotations()); - } - } - @Override public Void visitMethod(MethodTree node, Void p) { AnnotatedExecutableType executableType = atypeFactory.getAnnotatedType(node); @@ -404,7 +319,8 @@ protected boolean checkOverride( return true; } - protected void checkTypecastSafety(TypeCastTree node, Void p) { + @Override + protected void checkTypecastSafety(TypeCastTree node) { if (!checker.getLintOption("cast:unsafe", true)) { return; } @@ -442,7 +358,7 @@ private boolean isTypeCastSafe(AnnotatedTypeMirror castType, AnnotatedTypeMirror return isCompatibleCastInInfer(castType, exprType, node); } else { // Typechecking side standard implementation - warns about downcasting - return super.isTypeCastSafe(castType, exprType); + return isTypeCastSafe(castType, exprType); } } @@ -548,6 +464,7 @@ private void checkMutation(ExpressionTree node, ExpressionTree variable) { private void checkAssignableField(ExpressionTree node, ExpressionTree variable, AnnotatedTypeMirror receiverType) { Element fieldElement = TreeUtils.elementFromUse(node); + assert fieldElement != null; if (fieldElement != null) {//TODO Can this bu null? AnnotatedTypeMirror fieldType = atypeFactory.getAnnotatedType(fieldElement); assert fieldType != null; @@ -761,9 +678,6 @@ public void processClassTree(ClassTree node) { addPreference(bound, IMMUTABLE, 2); } -// // duplicated logic - remove later -// checkCompatabilityBetweenBoundAndSuperClassesBounds(node, typeElement, bound); - // Always reach this point. Do not suppress errors. super.processClassTree(node); } @@ -780,66 +694,6 @@ private boolean checkCompatabilityBetweenBoundAndSuperClassesBounds(ClassTree no return true; } - private boolean checkCompatabilityBetweenBoundAndExtendsImplements(ClassTree node, AnnotatedDeclaredType bound) { - if (infer) { - atypeFactory.getAnnotatedType(node); - } - - boolean hasSame; - Tree ext = node.getExtendsClause(); - if (ext != null) { - AnnotatedTypeMirror extendsType= atypeFactory.getAnnotatedType(ext); - if (infer) { - ((PICOInferenceAnnotatedTypeFactory) atypeFactory).getVariableAnnotator().visit(extendsType, ext); - areEqual(bound, extendsType, "bound.extends.incompatible", node); - } else { - AnnotationMirror superAnnoOnUse = atypeFactory.fromTypeTree(ext).getAnnotationInHierarchy(READONLY); - - hasSame = bound.getAnnotations().size() == extendsType.getAnnotations().size() - && AnnotationUtils.areSame(extendsType.getAnnotationInHierarchy(READONLY), - bound.getAnnotationInHierarchy(READONLY)); - // TODO rdm defaulting should be prevented instead of suppressing error here - boolean defaultedRdm = superAnnoOnUse == null && - AnnotationUtils.areSame(extendsType.getAnnotationInHierarchy(READONLY), RECEIVER_DEPENDANT_MUTABLE); - if (!defaultedRdm && !hasSame) { - checker.report(Result.failure("bound.extends.incompatible"), node); - return true; - } - } - } - - List impls = node.getImplementsClause(); - if (impls != null) { - for (Tree im : impls) { - // TODO: why @ from stub does not present when using getAnnotatedType(Tree)? - // hint: however getAnnotatedType(ExpressionTree) works perfectly. - AnnotatedTypeMirror implementsType = atypeFactory.getAnnotatedType(elementFromTree(im)); - if (infer) { - ((PICOInferenceAnnotatedTypeFactory) atypeFactory).getVariableAnnotator().visit(implementsType, im); - areEqual(bound, implementsType, "bound.implements.incompatible", node); - } else { - hasSame = bound.getAnnotations().size() == implementsType.getAnnotations().size() - && AnnotationUtils.areSame(implementsType.getAnnotationInHierarchy(READONLY), - bound.getAnnotationInHierarchy(READONLY)); - if (!hasSame) { - // mutable and immutable could extends RDM. See Mier's thesis 4.8.3 (p.43) - if (!(bound.getAnnotations().size() == implementsType.getAnnotations().size() - && AnnotationUtils.areSame(implementsType.getAnnotationInHierarchy(READONLY), - RECEIVER_DEPENDANT_MUTABLE) - && (AnnotationUtils.areSame(bound.getAnnotationInHierarchy(READONLY), - MUTABLE) - || AnnotationUtils.areSame(bound.getAnnotationInHierarchy(READONLY), - IMMUTABLE)))) { - checker.report(Result.failure("bound.implements.incompatible"), node); - return false; - } - } - } - } - } - return true; - } - /** * commonAssignmentCheck() method that adapts to PICOInfer. * diff --git a/testinput/inference/inferrable/FixableTypeCast.java b/testinput/inference/inferrable/FixableTypeCast.java index db92554..5845a93 100644 --- a/testinput/inference/inferrable/FixableTypeCast.java +++ b/testinput/inference/inferrable/FixableTypeCast.java @@ -1,6 +1,5 @@ public class FixableTypeCast { void foo(Object o) { - // :: fixable-warning: (cast.unsafe) String s = (String) o; } } diff --git a/testinput/inference/inferrable/RawIterator.java b/testinput/inference/inferrable/RawIterator.java index 1bccc5a..2c349e7 100644 --- a/testinput/inference/inferrable/RawIterator.java +++ b/testinput/inference/inferrable/RawIterator.java @@ -24,7 +24,9 @@ public void build(Collection classes) { // programs that have the below line wouldn't be inferred. One legal use case we discussed // is to cast @Mutable datastructure to @Immutable if we guarantee that @Mutable reference // doesn't leak. So we should continue if incompatible cast happens(@Mutable to @Immutable). - // :: warning: (cast.unsafe) + + // Lian: What is a incompatible cast? It seems good to cast to Immutable if casting to String + // is already vaild. String s = (String)iterator.next(); // But for cast that involves at least one VariableSlot, PICOInfer tends to give solutions diff --git a/testinput/inference/inferrable/StrangeReadonly.java b/testinput/inference/inferrable/StrangeReadonly.java index f5cdc3b..5f03699 100644 --- a/testinput/inference/inferrable/StrangeReadonly.java +++ b/testinput/inference/inferrable/StrangeReadonly.java @@ -24,9 +24,7 @@ static void foo() { public int compare(@Readonly Object o1, @Readonly Object o2) { // Before inference, @Mutable is casted to @Immutable; After inference, @Readonly is // casted to @Immutable. - // :: fixable-warning: (cast.unsafe) int i = (Integer) o1; - // :: fixable-warning: (cast.unsafe) int j = (Integer) o2; return 0; } From 90074b2124b251e4d60f8f67cda755062a8da67a Mon Sep 17 00:00:00 2001 From: lnsun <57457122+lnsun@users.noreply.github.com> Date: Wed, 15 Apr 2020 20:20:29 -0400 Subject: [PATCH 041/144] bound of array fixed to READONLY. VPA not needed here because any type is permitted. Note: how does PICO prevent changes on immutable arrays? --- .../java/pico/inference/PICOInferenceRealTypeFactory.java | 4 ++++ src/main/java/pico/inference/PICOInferenceVisitor.java | 8 ++------ 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/main/java/pico/inference/PICOInferenceRealTypeFactory.java b/src/main/java/pico/inference/PICOInferenceRealTypeFactory.java index 9db2c34..ef050d8 100644 --- a/src/main/java/pico/inference/PICOInferenceRealTypeFactory.java +++ b/src/main/java/pico/inference/PICOInferenceRealTypeFactory.java @@ -15,6 +15,7 @@ import javax.lang.model.element.AnnotationMirror; import javax.lang.model.element.Element; +import javax.lang.model.type.TypeKind; import javax.lang.model.type.TypeMirror; import com.sun.source.tree.IdentifierTree; @@ -213,6 +214,9 @@ public Set getTypeDeclarationBounds(TypeMirror type) { if (PICOTypeUtil.isImplicitlyImmutableType(toAnnotatedType(type, false))) { return Collections.singleton(IMMUTABLE); } + if (type.getKind() == TypeKind.ARRAY) { + return Collections.singleton(READONLY); // if decided to use vpa for array, return RDM. + } return super.getTypeDeclarationBounds(type); } diff --git a/src/main/java/pico/inference/PICOInferenceVisitor.java b/src/main/java/pico/inference/PICOInferenceVisitor.java index 50212e0..ae52056 100644 --- a/src/main/java/pico/inference/PICOInferenceVisitor.java +++ b/src/main/java/pico/inference/PICOInferenceVisitor.java @@ -83,11 +83,6 @@ protected InferenceValidator createTypeValidator() { @Override public boolean isValidUse(AnnotatedDeclaredType declarationType, AnnotatedDeclaredType useType, Tree tree) { -// if (declarationType.hasAnnotation(READONLY)) { -// // upper bound is always @Readonly -// // only needed in typechecking -// return true; -// } return isAdaptedSubtype(useType, declarationType); } @@ -99,7 +94,7 @@ public boolean isValidUse(AnnotatedDeclaredType declarationType, AnnotatedDeclar * @param rhs right value of adaption, typically declaration * @return true if holds, always true during inference */ - private boolean isAdaptedSubtype(AnnotatedDeclaredType lhs, AnnotatedDeclaredType rhs) { + private boolean isAdaptedSubtype(AnnotatedTypeMirror lhs, AnnotatedTypeMirror rhs) { ExtendedViewpointAdapter vpa = ((PublicViewpointAdapter)atypeFactory).getViewpointAdapter(); AnnotatedTypeMirror adapted = vpa.rawCombineAnnotationWithType(lhs.getAnnotationInHierarchy(READONLY), rhs); @@ -108,6 +103,7 @@ private boolean isAdaptedSubtype(AnnotatedDeclaredType lhs, AnnotatedDeclaredTyp @Override public boolean mainIsSubtype(AnnotatedTypeMirror ty, AnnotationMirror mod) { + // TODO push change to cfi boolean s = super.mainIsSubtype(ty, mod); // execute only once to avoid side-effects if (!s) { return atypeFactory.getQualifierHierarchy().isSubtype(ty.getAnnotationInHierarchy(mod), mod); From 857050e9f40b93b5dd98ac979955c7da02b500c1 Mon Sep 17 00:00:00 2001 From: lnsun <57457122+lnsun@users.noreply.github.com> Date: Fri, 17 Apr 2020 15:57:02 -0400 Subject: [PATCH 042/144] bottom workaround + vpa interface --- .../PICOInferenceRealTypeFactory.java | 42 +++++++-- .../inference/PICOInferenceValidator.java | 7 +- .../PICOInferenceViewpointAdapter.java | 12 ++- .../pico/inference/PICOInferenceVisitor.java | 91 +++++++++---------- ...ter.java => ViewpointAdapterGettable.java} | 2 +- 5 files changed, 91 insertions(+), 63 deletions(-) rename src/main/java/pico/inference/{PublicViewpointAdapter.java => ViewpointAdapterGettable.java} (64%) diff --git a/src/main/java/pico/inference/PICOInferenceRealTypeFactory.java b/src/main/java/pico/inference/PICOInferenceRealTypeFactory.java index ef050d8..d20ac28 100644 --- a/src/main/java/pico/inference/PICOInferenceRealTypeFactory.java +++ b/src/main/java/pico/inference/PICOInferenceRealTypeFactory.java @@ -8,7 +8,6 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; -import java.util.HashSet; import java.util.LinkedHashSet; import java.util.List; import java.util.Set; @@ -19,6 +18,7 @@ import javax.lang.model.type.TypeMirror; import com.sun.source.tree.IdentifierTree; +import com.sun.source.tree.ParameterizedTypeTree; import com.sun.source.util.TreePath; import org.checkerframework.common.basetype.BaseAnnotatedTypeFactory; import org.checkerframework.common.basetype.BaseTypeChecker; @@ -40,7 +40,6 @@ import pico.typecheck.PICOAnnotatedTypeFactory.PICOTypeAnnotator; import pico.typecheck.PICOAnnotatedTypeFactory.PICOPropagationTreeAnnotator; import pico.typecheck.PICOAnnotatedTypeFactory.PICOTreeAnnotator; -import pico.typecheck.PICOAnnotatedTypeFactory.PICOTypeAnnotator; import pico.typecheck.PICOTypeUtil; import pico.typecheck.PICOViewpointAdapter; import qual.Bottom; @@ -58,7 +57,7 @@ * to InitializationAnnotatedTypeFactory as if there is only one mutability qualifier hierarchy. * This class has lots of copied code from PICOAnnotatedTypeFactory. The two should be in sync. */ -public class PICOInferenceRealTypeFactory extends BaseAnnotatedTypeFactory implements PublicViewpointAdapter { +public class PICOInferenceRealTypeFactory extends BaseAnnotatedTypeFactory implements ViewpointAdapterGettable { public PICOInferenceRealTypeFactory(BaseTypeChecker checker, boolean useFlow) { super(checker, useFlow); @@ -231,18 +230,41 @@ public PICOSuperClauseAnnotator(AnnotatedTypeFactory atypeFactory) { super(atypeFactory); } - @Override - public Void visitIdentifier(IdentifierTree identifierTree, AnnotatedTypeMirror annotatedTypeMirror) { - TreePath path = atypeFactory.getPath(identifierTree); + private static boolean isSuperClause(TreePath path) { + if (path == null) { + return false; + } + return TreeUtils.isClassTree(path.getParentPath().getLeaf()); + } + + private void addDefaultFromMain(Tree tree, AnnotatedTypeMirror mirror) { + TreePath path = atypeFactory.getPath(tree); - if (path != null && (!annotatedTypeMirror.isAnnotatedInHierarchy(READONLY) | atypeFactory.getQualifierHierarchy().findAnnotationInHierarchy(TreeUtils.typeOf(identifierTree).getAnnotationMirrors(), READONLY) == null) && - TreeUtils.isClassTree(path.getParentPath().getLeaf())) { + // only annotates when: + // 1. it's a super clause, AND + // 2. atm OR tree is not annotated + if (isSuperClause(path) && + (!mirror.isAnnotatedInHierarchy(READONLY) || + atypeFactory.getQualifierHierarchy().findAnnotationInHierarchy(TreeUtils.typeOf(tree).getAnnotationMirrors(), READONLY) == null)) { AnnotatedTypeMirror enclosing = atypeFactory.getAnnotatedType(TreeUtils.enclosingClass(path)); AnnotationMirror mainBound = enclosing.getAnnotationInHierarchy(READONLY); - annotatedTypeMirror.replaceAnnotation(mainBound); - System.err.println("ANNOT: ADDED DEFAULT FOR: " + annotatedTypeMirror); + mirror.replaceAnnotation(mainBound); + System.err.println("ANNOT: ADDED DEFAULT FOR: " + mirror); } + } + + @Override + public Void visitIdentifier(IdentifierTree identifierTree, AnnotatedTypeMirror annotatedTypeMirror) { + // super clauses without type param use this + addDefaultFromMain(identifierTree, annotatedTypeMirror); return super.visitIdentifier(identifierTree, annotatedTypeMirror); } + + @Override + public Void visitParameterizedType(ParameterizedTypeTree parameterizedTypeTree, AnnotatedTypeMirror annotatedTypeMirror) { + // super clauses with type param use this + addDefaultFromMain(parameterizedTypeTree, annotatedTypeMirror); + return super.visitParameterizedType(parameterizedTypeTree, annotatedTypeMirror); + } } } diff --git a/src/main/java/pico/inference/PICOInferenceValidator.java b/src/main/java/pico/inference/PICOInferenceValidator.java index ba30bb0..d44de91 100644 --- a/src/main/java/pico/inference/PICOInferenceValidator.java +++ b/src/main/java/pico/inference/PICOInferenceValidator.java @@ -41,12 +41,14 @@ public Void visitDeclared(AnnotatedDeclaredType type, Tree tree) { checkOnlyOneAssignabilityModifierOnField(tree); AnnotatedDeclaredType defaultType = (AnnotatedDeclaredType) atypeFactory.getAnnotatedType(type.getUnderlyingType().asElement()); + // TODO for defaulted super clause: should top anno be checked? (see shouldCheckTopLevelDeclaredType()) // null check below may not needed if (defaultType.getAnnotationInHierarchy(READONLY) == null) { defaultType = defaultType.deepCopy(); defaultType.replaceAnnotation(MUTABLE); } - if (!visitor.isValidUse(defaultType, type, tree)) { + // FIXME workaround for bottom: "as internal anno, bottom accepted everywhere" + if (!type.hasAnnotation(BOTTOM) && !visitor.isValidUse(defaultType, type, tree)) { reportInvalidAnnotationsOnUse(type, tree); } return super.visitDeclared(type, tree); @@ -95,7 +97,8 @@ private void checkImplicitlyImmutableTypeError(AnnotatedTypeMirror type, Tree tr new AnnotationMirror[]{READONLY, MUTABLE, RECEIVER_DEPENDANT_MUTABLE, BOTTOM}, "type.invalid.annotations.on.use", tree); } else { - if (!type.hasAnnotation(IMMUTABLE)) { + // FIXME workaround for typecheck. How should inference handle BOTTOM? + if (!type.hasAnnotation(IMMUTABLE) && !type.hasAnnotation(BOTTOM)) { reportInvalidAnnotationsOnUse(type, tree); } } diff --git a/src/main/java/pico/inference/PICOInferenceViewpointAdapter.java b/src/main/java/pico/inference/PICOInferenceViewpointAdapter.java index 9386d88..cf4510f 100644 --- a/src/main/java/pico/inference/PICOInferenceViewpointAdapter.java +++ b/src/main/java/pico/inference/PICOInferenceViewpointAdapter.java @@ -9,7 +9,7 @@ import javax.lang.model.element.Element; import javax.lang.model.type.TypeKind; -public class PICOInferenceViewpointAdapter extends InferenceViewpointAdapter{ +public class PICOInferenceViewpointAdapter extends InferenceViewpointAdapter implements ExtendedViewpointAdapter{ public PICOInferenceViewpointAdapter(AnnotatedTypeFactory atypeFactory) { super(atypeFactory); @@ -30,4 +30,14 @@ protected AnnotatedTypeMirror combineAnnotationWithType(AnnotationMirror receive } return super.combineAnnotationWithType(receiverAnnotation, declared); } + + @Override + public AnnotatedTypeMirror rawCombineAnnotationWithType(AnnotationMirror anno, AnnotatedTypeMirror type) { + return combineAnnotationWithType(anno, type); + } + + @Override + public AnnotationMirror rawCombineAnnotationWithAnnotation(AnnotationMirror anno, AnnotationMirror type) { + return rawCombineAnnotationWithAnnotation(anno, type); + } } diff --git a/src/main/java/pico/inference/PICOInferenceVisitor.java b/src/main/java/pico/inference/PICOInferenceVisitor.java index ae52056..fa53c3c 100644 --- a/src/main/java/pico/inference/PICOInferenceVisitor.java +++ b/src/main/java/pico/inference/PICOInferenceVisitor.java @@ -1,42 +1,14 @@ package pico.inference; -import static org.checkerframework.javacutil.TreeUtils.elementFromTree; -import static pico.typecheck.PICOAnnotationMirrorHolder.BOTTOM; -import static pico.typecheck.PICOAnnotationMirrorHolder.IMMUTABLE; -import static pico.typecheck.PICOAnnotationMirrorHolder.MUTABLE; -import static pico.typecheck.PICOAnnotationMirrorHolder.READONLY; -import static pico.typecheck.PICOAnnotationMirrorHolder.RECEIVER_DEPENDANT_MUTABLE; - -import java.lang.reflect.AnnotatedType; -import java.util.Arrays; -import java.util.Collections; -import java.util.HashSet; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.Set; - -import javax.lang.model.element.AnnotationMirror; -import javax.lang.model.element.Element; -import javax.lang.model.element.ElementKind; -import javax.lang.model.element.ExecutableElement; -import javax.lang.model.element.TypeElement; -import javax.lang.model.element.VariableElement; -import javax.lang.model.type.TypeKind; - -import org.checkerframework.common.basetype.BaseAnnotatedTypeFactory; -import org.checkerframework.framework.source.Result; -import org.checkerframework.framework.type.AnnotatedTypeFactory.ParameterizedExecutableType; -import org.checkerframework.framework.type.AnnotatedTypeMirror; -import org.checkerframework.framework.type.AnnotatedTypeMirror.AnnotatedDeclaredType; -import org.checkerframework.framework.type.AnnotatedTypeMirror.AnnotatedExecutableType; -import org.checkerframework.framework.type.QualifierHierarchy; -import org.checkerframework.framework.util.AnnotatedTypes; -import org.checkerframework.javacutil.AnnotationUtils; -import org.checkerframework.javacutil.BugInCF; -import org.checkerframework.javacutil.ElementUtils; -import org.checkerframework.javacutil.TreeUtils; - +import checkers.inference.InferenceChecker; +import checkers.inference.InferenceMain; +import checkers.inference.InferenceValidator; +import checkers.inference.InferenceVisitor; +import checkers.inference.SlotManager; +import checkers.inference.model.ConstantSlot; +import checkers.inference.model.Constraint; +import checkers.inference.model.ConstraintManager; +import checkers.inference.model.Slot; import com.sun.source.tree.AnnotationTree; import com.sun.source.tree.ArrayAccessTree; import com.sun.source.tree.AssignmentTree; @@ -53,19 +25,37 @@ import com.sun.source.tree.UnaryTree; import com.sun.source.tree.VariableTree; import com.sun.source.util.TreePath; - -import checkers.inference.InferenceChecker; -import checkers.inference.InferenceMain; -import checkers.inference.InferenceValidator; -import checkers.inference.InferenceVisitor; -import checkers.inference.SlotManager; -import checkers.inference.model.ConstantSlot; -import checkers.inference.model.Constraint; -import checkers.inference.model.ConstraintManager; -import checkers.inference.model.Slot; +import org.checkerframework.common.basetype.BaseAnnotatedTypeFactory; +import org.checkerframework.framework.source.Result; +import org.checkerframework.framework.type.AnnotatedTypeFactory.ParameterizedExecutableType; +import org.checkerframework.framework.type.AnnotatedTypeMirror; +import org.checkerframework.framework.type.AnnotatedTypeMirror.AnnotatedDeclaredType; +import org.checkerframework.framework.type.AnnotatedTypeMirror.AnnotatedExecutableType; +import org.checkerframework.framework.type.QualifierHierarchy; +import org.checkerframework.framework.util.AnnotatedTypes; +import org.checkerframework.javacutil.BugInCF; +import org.checkerframework.javacutil.ElementUtils; +import org.checkerframework.javacutil.TreeUtils; import org.checkerframework.javacutil.TypesUtils; import pico.typecheck.PICOTypeUtil; +import javax.lang.model.element.AnnotationMirror; +import javax.lang.model.element.Element; +import javax.lang.model.element.ElementKind; +import javax.lang.model.element.ExecutableElement; +import javax.lang.model.element.TypeElement; +import javax.lang.model.element.VariableElement; +import javax.lang.model.type.TypeKind; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import static pico.typecheck.PICOAnnotationMirrorHolder.*; + /** * Generate constraints based on the PICO constraint-based type rules in infer mode. Has typecheck * and infer mode. In typecheck mode, has the exact same behaviour as PICOVisitor. @@ -83,8 +73,11 @@ protected InferenceValidator createTypeValidator() { @Override public boolean isValidUse(AnnotatedDeclaredType declarationType, AnnotatedDeclaredType useType, Tree tree) { + // FIXME workaround for typecheck BOTTOM + if (useType.hasAnnotation(BOTTOM)) { + return true; + } return isAdaptedSubtype(useType, declarationType); - } /** @@ -95,7 +88,7 @@ public boolean isValidUse(AnnotatedDeclaredType declarationType, AnnotatedDeclar * @return true if holds, always true during inference */ private boolean isAdaptedSubtype(AnnotatedTypeMirror lhs, AnnotatedTypeMirror rhs) { - ExtendedViewpointAdapter vpa = ((PublicViewpointAdapter)atypeFactory).getViewpointAdapter(); + ExtendedViewpointAdapter vpa = ((ViewpointAdapterGettable)atypeFactory).getViewpointAdapter(); AnnotatedTypeMirror adapted = vpa.rawCombineAnnotationWithType(lhs.getAnnotationInHierarchy(READONLY), rhs); return mainIsSubtype(adapted, lhs.getAnnotationInHierarchy(READONLY)); diff --git a/src/main/java/pico/inference/PublicViewpointAdapter.java b/src/main/java/pico/inference/ViewpointAdapterGettable.java similarity index 64% rename from src/main/java/pico/inference/PublicViewpointAdapter.java rename to src/main/java/pico/inference/ViewpointAdapterGettable.java index 398d75a..b63150f 100644 --- a/src/main/java/pico/inference/PublicViewpointAdapter.java +++ b/src/main/java/pico/inference/ViewpointAdapterGettable.java @@ -1,5 +1,5 @@ package pico.inference; -public interface PublicViewpointAdapter { +public interface ViewpointAdapterGettable { ExtendedViewpointAdapter getViewpointAdapter(); } From 4dab7b124cebf534ebecdf31d0b8d88f3468b0ff Mon Sep 17 00:00:00 2001 From: lnsun <57457122+lnsun@users.noreply.github.com> Date: Fri, 17 Apr 2020 22:22:59 -0400 Subject: [PATCH 043/144] enum default immutable. TODO: fix constructor check in mutable enum decl --- .../PICOInferenceRealTypeFactory.java | 18 ++++++++++--- .../inference/PICOInferenceValidator.java | 4 +-- .../java/pico/typecheck/PICOTypeUtil.java | 3 +-- .../pico/typecheck/PICOViewpointAdapter.java | 4 +-- testinput/typecheck/EnumTests.java | 25 +++++++++++++++++++ 5 files changed, 44 insertions(+), 10 deletions(-) create mode 100644 testinput/typecheck/EnumTests.java diff --git a/src/main/java/pico/inference/PICOInferenceRealTypeFactory.java b/src/main/java/pico/inference/PICOInferenceRealTypeFactory.java index d20ac28..49ceeb2 100644 --- a/src/main/java/pico/inference/PICOInferenceRealTypeFactory.java +++ b/src/main/java/pico/inference/PICOInferenceRealTypeFactory.java @@ -1,9 +1,5 @@ package pico.inference; -import static pico.typecheck.PICOAnnotationMirrorHolder.IMMUTABLE; -import static pico.typecheck.PICOAnnotationMirrorHolder.MUTABLE; -import static pico.typecheck.PICOAnnotationMirrorHolder.READONLY; - import java.lang.annotation.Annotation; import java.util.ArrayList; import java.util.Arrays; @@ -14,6 +10,8 @@ import javax.lang.model.element.AnnotationMirror; import javax.lang.model.element.Element; +import javax.lang.model.element.ElementKind; +import javax.lang.model.type.DeclaredType; import javax.lang.model.type.TypeKind; import javax.lang.model.type.TypeMirror; @@ -30,6 +28,7 @@ import org.checkerframework.framework.type.treeannotator.LiteralTreeAnnotator; import org.checkerframework.framework.type.treeannotator.TreeAnnotator; import org.checkerframework.framework.type.typeannotator.*; +import org.checkerframework.javacutil.AnnotationUtils; import org.checkerframework.javacutil.BugInCF; import org.checkerframework.javacutil.TreeUtils; @@ -49,6 +48,8 @@ import qual.Readonly; import qual.ReceiverDependantMutable; +import static pico.typecheck.PICOAnnotationMirrorHolder.*; + /** * PICOInferenceRealTypeFactory exists because: 1)PICOAnnotatedTypeFactory is not subtype of * BaseAnnotatedTypeFactory. 2) In inference side, PICO only supports reduced version of @@ -216,6 +217,15 @@ public Set getTypeDeclarationBounds(TypeMirror type) { if (type.getKind() == TypeKind.ARRAY) { return Collections.singleton(READONLY); // if decided to use vpa for array, return RDM. } + if (type instanceof DeclaredType) { + Element ele = ((DeclaredType) type).asElement(); + if (ele.getKind() == ElementKind.ENUM) { + if (!AnnotationUtils.containsSameByName(getDeclAnnotations(ele), MUTABLE) && + !AnnotationUtils.containsSameByName(getDeclAnnotations(ele), RECEIVER_DEPENDANT_MUTABLE)) { // no decl anno + return Collections.singleton(IMMUTABLE); + } + } + } return super.getTypeDeclarationBounds(type); } diff --git a/src/main/java/pico/inference/PICOInferenceValidator.java b/src/main/java/pico/inference/PICOInferenceValidator.java index d44de91..a183c6f 100644 --- a/src/main/java/pico/inference/PICOInferenceValidator.java +++ b/src/main/java/pico/inference/PICOInferenceValidator.java @@ -47,8 +47,8 @@ public Void visitDeclared(AnnotatedDeclaredType type, Tree tree) { defaultType = defaultType.deepCopy(); defaultType.replaceAnnotation(MUTABLE); } - // FIXME workaround for bottom: "as internal anno, bottom accepted everywhere" - if (!type.hasAnnotation(BOTTOM) && !visitor.isValidUse(defaultType, type, tree)) { + + if (!visitor.isValidUse(defaultType, type, tree)) { reportInvalidAnnotationsOnUse(type, tree); } return super.visitDeclared(type, tree); diff --git a/src/main/java/pico/typecheck/PICOTypeUtil.java b/src/main/java/pico/typecheck/PICOTypeUtil.java index b33adce..cdfef66 100644 --- a/src/main/java/pico/typecheck/PICOTypeUtil.java +++ b/src/main/java/pico/typecheck/PICOTypeUtil.java @@ -92,8 +92,7 @@ private static boolean isInTypesOfDefaultForOfImmutable(AnnotatedTypeMirror atm) * with the types and typeNames that are in @ImplicitFor in the definition of @Immutable qualifier*/ public static boolean isImplicitlyImmutableType(AnnotatedTypeMirror atm) { return isInTypeKindsOfDefaultForOfImmutable(atm) - || isInTypesOfDefaultForOfImmutable(atm) - || isEnumOrEnumConstant(atm); + || isInTypesOfDefaultForOfImmutable(atm); } /** diff --git a/src/main/java/pico/typecheck/PICOViewpointAdapter.java b/src/main/java/pico/typecheck/PICOViewpointAdapter.java index 3d37045..d3403d0 100644 --- a/src/main/java/pico/typecheck/PICOViewpointAdapter.java +++ b/src/main/java/pico/typecheck/PICOViewpointAdapter.java @@ -63,13 +63,13 @@ protected AnnotationMirror combineAnnotationWithAnnotation(AnnotationMirror rece } public AnnotatedTypeMirror rawCombineAnnotationWithType(AnnotationMirror anno, AnnotatedTypeMirror type) { - System.err.println("VPA: " + anno + " ->" + type); +// System.err.println("VPA: " + anno + " ->" + type); return combineAnnotationWithType(anno, type); } @Override public AnnotationMirror rawCombineAnnotationWithAnnotation(AnnotationMirror anno, AnnotationMirror type) { - System.err.println("VPA: " + anno + " ->" + type); +// System.err.println("VPA: " + anno + " ->" + type); return combineAnnotationWithAnnotation(anno, type); } diff --git a/testinput/typecheck/EnumTests.java b/testinput/typecheck/EnumTests.java new file mode 100644 index 0000000..73f6715 --- /dev/null +++ b/testinput/typecheck/EnumTests.java @@ -0,0 +1,25 @@ +import qual.Immutable; +import qual.Mutable; + +// Enum is now only immutable by default, not implicit +public class EnumTests{ + void foo(/*immutable*/ MyEnum e) { + // :: error: (type.invalid.annotations.on.use) + @Mutable MyEnum mutableRef; + @Immutable MyEnum immutableRef = e; + + @Mutable MutableEnum mutEnumMutRef= MutableEnum.M1; + // :: error: (type.invalid.annotations.on.use) + @Immutable MutableEnum mutEnumImmRef; + } + + /*immutable*/ + private static enum MyEnum { + T1, T2; + } + + @Mutable + private static enum MutableEnum { + M1, M2; + } +} \ No newline at end of file From a42c289a04afe13e99a31f6cb3cae02ec459db04 Mon Sep 17 00:00:00 2001 From: lnsun <57457122+lnsun@users.noreply.github.com> Date: Tue, 21 Apr 2020 16:02:05 -0400 Subject: [PATCH 044/144] fix enum default in inference --- .../PICOInferenceRealTypeFactory.java | 20 +++++++++++++++++++ .../inference/PICOInferenceValidator.java | 4 ++-- .../typecheck/PICOAnnotatedTypeFactory.java | 4 ++-- 3 files changed, 24 insertions(+), 4 deletions(-) diff --git a/src/main/java/pico/inference/PICOInferenceRealTypeFactory.java b/src/main/java/pico/inference/PICOInferenceRealTypeFactory.java index 49ceeb2..da8b425 100644 --- a/src/main/java/pico/inference/PICOInferenceRealTypeFactory.java +++ b/src/main/java/pico/inference/PICOInferenceRealTypeFactory.java @@ -119,6 +119,7 @@ protected TypeAnnotator createTypeAnnotator() { // same location typeAnnotators.add(new PICOTypeAnnotator(this)); typeAnnotators.add(new PICODefaultForTypeAnnotator(this)); + typeAnnotators.add(new PICOEnumDefaultAnnotator(this)); return new ListTypeAnnotator(typeAnnotators); } @@ -217,6 +218,8 @@ public Set getTypeDeclarationBounds(TypeMirror type) { if (type.getKind() == TypeKind.ARRAY) { return Collections.singleton(READONLY); // if decided to use vpa for array, return RDM. } + + // IMMUTABLE for enum w/o decl anno if (type instanceof DeclaredType) { Element ele = ((DeclaredType) type).asElement(); if (ele.getKind() == ElementKind.ENUM) { @@ -277,4 +280,21 @@ public Void visitParameterizedType(ParameterizedTypeTree parameterizedTypeTree, return super.visitParameterizedType(parameterizedTypeTree, annotatedTypeMirror); } } + + public static class PICOEnumDefaultAnnotator extends TypeAnnotator { + // Defaulting only applies the same annotation to all class declarations + // We need this to "only default enums" to immutable + + public PICOEnumDefaultAnnotator(AnnotatedTypeFactory typeFactory) { + super(typeFactory); + } + + @Override + public Void visitDeclared(AnnotatedTypeMirror.AnnotatedDeclaredType type, Void aVoid) { + if (PICOTypeUtil.isEnumOrEnumConstant(type)) { + type.addMissingAnnotations(Collections.singleton(IMMUTABLE)); + } + return super.visitDeclared(type, aVoid); + } + } } diff --git a/src/main/java/pico/inference/PICOInferenceValidator.java b/src/main/java/pico/inference/PICOInferenceValidator.java index a183c6f..42eed57 100644 --- a/src/main/java/pico/inference/PICOInferenceValidator.java +++ b/src/main/java/pico/inference/PICOInferenceValidator.java @@ -2,6 +2,7 @@ import checkers.inference.InferenceValidator; import checkers.inference.InferenceVisitor; +import com.sun.org.apache.regexp.internal.RE; import com.sun.source.tree.Tree; import com.sun.source.tree.VariableTree; import org.checkerframework.common.basetype.BaseTypeChecker; @@ -42,8 +43,7 @@ public Void visitDeclared(AnnotatedDeclaredType type, Tree tree) { AnnotatedDeclaredType defaultType = (AnnotatedDeclaredType) atypeFactory.getAnnotatedType(type.getUnderlyingType().asElement()); // TODO for defaulted super clause: should top anno be checked? (see shouldCheckTopLevelDeclaredType()) - // null check below may not needed - if (defaultType.getAnnotationInHierarchy(READONLY) == null) { + if (defaultType.isAnnotatedInHierarchy(READONLY) && !PICOTypeUtil.isEnumOrEnumConstant(defaultType)) { defaultType = defaultType.deepCopy(); defaultType.replaceAnnotation(MUTABLE); } diff --git a/src/main/java/pico/typecheck/PICOAnnotatedTypeFactory.java b/src/main/java/pico/typecheck/PICOAnnotatedTypeFactory.java index 03a49ca..7273b75 100644 --- a/src/main/java/pico/typecheck/PICOAnnotatedTypeFactory.java +++ b/src/main/java/pico/typecheck/PICOAnnotatedTypeFactory.java @@ -409,7 +409,7 @@ public Void visitMethod(MethodTree node, AnnotatedTypeMirror p) { public Void visitVariable(VariableTree node, AnnotatedTypeMirror annotatedTypeMirror) { VariableElement element = TreeUtils.elementFromDeclaration(node); PICOTypeUtil.addDefaultForField(atypeFactory, annotatedTypeMirror, element); - PICOTypeUtil.applyImmutableToEnumAndEnumConstant(annotatedTypeMirror); +// PICOTypeUtil.applyImmutableToEnumAndEnumConstant(annotatedTypeMirror); return super.visitVariable(node, annotatedTypeMirror); } } @@ -511,7 +511,7 @@ public Void visitExecutable(AnnotatedExecutableType t, Void p) { @Override protected Void scan(AnnotatedTypeMirror type, Void p) { // If underlying type is enum or enum constant, appy @Immutable to type - PICOTypeUtil.applyImmutableToEnumAndEnumConstant(type); +// PICOTypeUtil.applyImmutableToEnumAndEnumConstant(type); return super.scan(type, p); } } From 761655dbd24721bbb1f4d8a7f47b0c6978835a52 Mon Sep 17 00:00:00 2001 From: lnsun <57457122+lnsun@users.noreply.github.com> Date: Thu, 23 Apr 2020 15:36:26 -0400 Subject: [PATCH 045/144] doc --- src/main/java/pico/inference/PICOInferenceRealTypeFactory.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main/java/pico/inference/PICOInferenceRealTypeFactory.java b/src/main/java/pico/inference/PICOInferenceRealTypeFactory.java index da8b425..78787e8 100644 --- a/src/main/java/pico/inference/PICOInferenceRealTypeFactory.java +++ b/src/main/java/pico/inference/PICOInferenceRealTypeFactory.java @@ -256,6 +256,8 @@ private void addDefaultFromMain(Tree tree, AnnotatedTypeMirror mirror) { // only annotates when: // 1. it's a super clause, AND // 2. atm OR tree is not annotated + // Note: TreeUtils.typeOf returns no stub or default annotations, but in this scenario they are not needed + // Here only explicit annotation on super clause have effect because framework default rule is overriden if (isSuperClause(path) && (!mirror.isAnnotatedInHierarchy(READONLY) || atypeFactory.getQualifierHierarchy().findAnnotationInHierarchy(TreeUtils.typeOf(tree).getAnnotationMirrors(), READONLY) == null)) { From bdcbb4ce56c35f0a1cbaf93d4b50219cb000f8d4 Mon Sep 17 00:00:00 2001 From: lnsun <57457122+lnsun@users.noreply.github.com> Date: Thu, 23 Apr 2020 16:01:13 -0400 Subject: [PATCH 046/144] relocate common interface and annotator --- .../ExtendedViewpointAdapter.java | 2 +- .../ViewpointAdapterGettable.java | 2 +- ...PICOInferenceExtendedViewpointAdapter.java | 2 +- .../PICOInferenceRealTypeFactory.java | 91 ++----------------- .../PICOInferenceViewpointAdapter.java | 3 +- .../pico/inference/PICOInferenceVisitor.java | 2 + .../typecheck/PICOAnnotatedTypeFactory.java | 77 +++++++++++++++- .../pico/typecheck/PICOViewpointAdapter.java | 2 +- 8 files changed, 93 insertions(+), 88 deletions(-) rename src/main/java/pico/{inference => common}/ExtendedViewpointAdapter.java (94%) rename src/main/java/pico/{inference => common}/ViewpointAdapterGettable.java (80%) diff --git a/src/main/java/pico/inference/ExtendedViewpointAdapter.java b/src/main/java/pico/common/ExtendedViewpointAdapter.java similarity index 94% rename from src/main/java/pico/inference/ExtendedViewpointAdapter.java rename to src/main/java/pico/common/ExtendedViewpointAdapter.java index f88846a..a02de53 100644 --- a/src/main/java/pico/inference/ExtendedViewpointAdapter.java +++ b/src/main/java/pico/common/ExtendedViewpointAdapter.java @@ -1,4 +1,4 @@ -package pico.inference; +package pico.common; import org.checkerframework.framework.type.AnnotatedTypeMirror; import org.checkerframework.framework.type.ViewpointAdapter; diff --git a/src/main/java/pico/inference/ViewpointAdapterGettable.java b/src/main/java/pico/common/ViewpointAdapterGettable.java similarity index 80% rename from src/main/java/pico/inference/ViewpointAdapterGettable.java rename to src/main/java/pico/common/ViewpointAdapterGettable.java index b63150f..70ed93a 100644 --- a/src/main/java/pico/inference/ViewpointAdapterGettable.java +++ b/src/main/java/pico/common/ViewpointAdapterGettable.java @@ -1,4 +1,4 @@ -package pico.inference; +package pico.common; public interface ViewpointAdapterGettable { ExtendedViewpointAdapter getViewpointAdapter(); diff --git a/src/main/java/pico/inference/PICOInferenceExtendedViewpointAdapter.java b/src/main/java/pico/inference/PICOInferenceExtendedViewpointAdapter.java index e6d886d..3df3cd5 100644 --- a/src/main/java/pico/inference/PICOInferenceExtendedViewpointAdapter.java +++ b/src/main/java/pico/inference/PICOInferenceExtendedViewpointAdapter.java @@ -1,8 +1,8 @@ package pico.inference; -import com.sun.source.tree.Tree; import org.checkerframework.framework.type.AnnotatedTypeFactory; import org.checkerframework.framework.type.AnnotatedTypeMirror; +import pico.common.ExtendedViewpointAdapter; import javax.lang.model.element.AnnotationMirror; import javax.lang.model.element.Element; diff --git a/src/main/java/pico/inference/PICOInferenceRealTypeFactory.java b/src/main/java/pico/inference/PICOInferenceRealTypeFactory.java index 78787e8..8bd7221 100644 --- a/src/main/java/pico/inference/PICOInferenceRealTypeFactory.java +++ b/src/main/java/pico/inference/PICOInferenceRealTypeFactory.java @@ -15,14 +15,10 @@ import javax.lang.model.type.TypeKind; import javax.lang.model.type.TypeMirror; -import com.sun.source.tree.IdentifierTree; -import com.sun.source.tree.ParameterizedTypeTree; -import com.sun.source.util.TreePath; import org.checkerframework.common.basetype.BaseAnnotatedTypeFactory; import org.checkerframework.common.basetype.BaseTypeChecker; import org.checkerframework.framework.qual.RelevantJavaTypes; import org.checkerframework.framework.type.AbstractViewpointAdapter; -import org.checkerframework.framework.type.AnnotatedTypeFactory; import org.checkerframework.framework.type.AnnotatedTypeMirror; import org.checkerframework.framework.type.treeannotator.ListTreeAnnotator; import org.checkerframework.framework.type.treeannotator.LiteralTreeAnnotator; @@ -34,11 +30,9 @@ import com.sun.source.tree.Tree; +import pico.common.ExtendedViewpointAdapter; +import pico.common.ViewpointAdapterGettable; import pico.typecheck.PICOAnnotatedTypeFactory; -import pico.typecheck.PICOAnnotatedTypeFactory.PICODefaultForTypeAnnotator; -import pico.typecheck.PICOAnnotatedTypeFactory.PICOTypeAnnotator; -import pico.typecheck.PICOAnnotatedTypeFactory.PICOPropagationTreeAnnotator; -import pico.typecheck.PICOAnnotatedTypeFactory.PICOTreeAnnotator; import pico.typecheck.PICOTypeUtil; import pico.typecheck.PICOViewpointAdapter; import qual.Bottom; @@ -91,10 +85,10 @@ protected AbstractViewpointAdapter createViewpointAdapter() { @Override protected TreeAnnotator createTreeAnnotator() { return new ListTreeAnnotator( - new PICOPropagationTreeAnnotator(this), + new PICOAnnotatedTypeFactory.PICOPropagationTreeAnnotator(this), new LiteralTreeAnnotator(this), - new PICOSuperClauseAnnotator(this), - new PICOTreeAnnotator(this)); + new PICOAnnotatedTypeFactory.PICOSuperClauseAnnotator(this), + new PICOAnnotatedTypeFactory.PICOTreeAnnotator(this)); } // TODO Refactor super class to remove this duplicate code @@ -117,9 +111,9 @@ protected TypeAnnotator createTypeAnnotator() { // Adding order is important here. Because internally type annotators are using addMissingAnnotations() // method, so if one annotator already applied the annotations, the others won't apply twice at the // same location - typeAnnotators.add(new PICOTypeAnnotator(this)); - typeAnnotators.add(new PICODefaultForTypeAnnotator(this)); - typeAnnotators.add(new PICOEnumDefaultAnnotator(this)); + typeAnnotators.add(new PICOAnnotatedTypeFactory.PICOTypeAnnotator(this)); + typeAnnotators.add(new PICOAnnotatedTypeFactory.PICODefaultForTypeAnnotator(this)); + typeAnnotators.add(new PICOAnnotatedTypeFactory.PICOEnumDefaultAnnotator(this)); return new ListTypeAnnotator(typeAnnotators); } @@ -186,6 +180,7 @@ protected DefaultQualifierForUseTypeAnnotator createDefaultForUseTypeAnnotator() @Override public AnnotatedTypeMirror getTypeOfExtendsImplements(Tree clause) { + // TODO is this still needed with PICOSuperClauseAnnotator? // add default anno from class main qual, if no qual present AnnotatedTypeMirror enclosing = getAnnotatedType(TreeUtils.enclosingClass(getPath(clause))); AnnotationMirror mainBound = enclosing.getAnnotationInHierarchy(READONLY); @@ -231,72 +226,4 @@ public Set getTypeDeclarationBounds(TypeMirror type) { } return super.getTypeDeclarationBounds(type); } - - @Override - public AnnotatedTypeMirror getAnnotatedType(Tree tree) { - return super.getAnnotatedType(tree); - } - - public static class PICOSuperClauseAnnotator extends TreeAnnotator { - - public PICOSuperClauseAnnotator(AnnotatedTypeFactory atypeFactory) { - super(atypeFactory); - } - - private static boolean isSuperClause(TreePath path) { - if (path == null) { - return false; - } - return TreeUtils.isClassTree(path.getParentPath().getLeaf()); - } - - private void addDefaultFromMain(Tree tree, AnnotatedTypeMirror mirror) { - TreePath path = atypeFactory.getPath(tree); - - // only annotates when: - // 1. it's a super clause, AND - // 2. atm OR tree is not annotated - // Note: TreeUtils.typeOf returns no stub or default annotations, but in this scenario they are not needed - // Here only explicit annotation on super clause have effect because framework default rule is overriden - if (isSuperClause(path) && - (!mirror.isAnnotatedInHierarchy(READONLY) || - atypeFactory.getQualifierHierarchy().findAnnotationInHierarchy(TreeUtils.typeOf(tree).getAnnotationMirrors(), READONLY) == null)) { - AnnotatedTypeMirror enclosing = atypeFactory.getAnnotatedType(TreeUtils.enclosingClass(path)); - AnnotationMirror mainBound = enclosing.getAnnotationInHierarchy(READONLY); - mirror.replaceAnnotation(mainBound); - System.err.println("ANNOT: ADDED DEFAULT FOR: " + mirror); - } - } - - @Override - public Void visitIdentifier(IdentifierTree identifierTree, AnnotatedTypeMirror annotatedTypeMirror) { - // super clauses without type param use this - addDefaultFromMain(identifierTree, annotatedTypeMirror); - return super.visitIdentifier(identifierTree, annotatedTypeMirror); - } - - @Override - public Void visitParameterizedType(ParameterizedTypeTree parameterizedTypeTree, AnnotatedTypeMirror annotatedTypeMirror) { - // super clauses with type param use this - addDefaultFromMain(parameterizedTypeTree, annotatedTypeMirror); - return super.visitParameterizedType(parameterizedTypeTree, annotatedTypeMirror); - } - } - - public static class PICOEnumDefaultAnnotator extends TypeAnnotator { - // Defaulting only applies the same annotation to all class declarations - // We need this to "only default enums" to immutable - - public PICOEnumDefaultAnnotator(AnnotatedTypeFactory typeFactory) { - super(typeFactory); - } - - @Override - public Void visitDeclared(AnnotatedTypeMirror.AnnotatedDeclaredType type, Void aVoid) { - if (PICOTypeUtil.isEnumOrEnumConstant(type)) { - type.addMissingAnnotations(Collections.singleton(IMMUTABLE)); - } - return super.visitDeclared(type, aVoid); - } - } } diff --git a/src/main/java/pico/inference/PICOInferenceViewpointAdapter.java b/src/main/java/pico/inference/PICOInferenceViewpointAdapter.java index cf4510f..d68a2cf 100644 --- a/src/main/java/pico/inference/PICOInferenceViewpointAdapter.java +++ b/src/main/java/pico/inference/PICOInferenceViewpointAdapter.java @@ -3,13 +3,14 @@ import checkers.inference.util.InferenceViewpointAdapter; import org.checkerframework.framework.type.AnnotatedTypeFactory; import org.checkerframework.framework.type.AnnotatedTypeMirror; +import pico.common.ExtendedViewpointAdapter; import pico.typecheck.PICOTypeUtil; import javax.lang.model.element.AnnotationMirror; import javax.lang.model.element.Element; import javax.lang.model.type.TypeKind; -public class PICOInferenceViewpointAdapter extends InferenceViewpointAdapter implements ExtendedViewpointAdapter{ +public class PICOInferenceViewpointAdapter extends InferenceViewpointAdapter implements ExtendedViewpointAdapter { public PICOInferenceViewpointAdapter(AnnotatedTypeFactory atypeFactory) { super(atypeFactory); diff --git a/src/main/java/pico/inference/PICOInferenceVisitor.java b/src/main/java/pico/inference/PICOInferenceVisitor.java index fa53c3c..372bc3a 100644 --- a/src/main/java/pico/inference/PICOInferenceVisitor.java +++ b/src/main/java/pico/inference/PICOInferenceVisitor.java @@ -37,6 +37,8 @@ import org.checkerframework.javacutil.ElementUtils; import org.checkerframework.javacutil.TreeUtils; import org.checkerframework.javacutil.TypesUtils; +import pico.common.ExtendedViewpointAdapter; +import pico.common.ViewpointAdapterGettable; import pico.typecheck.PICOTypeUtil; import javax.lang.model.element.AnnotationMirror; diff --git a/src/main/java/pico/typecheck/PICOAnnotatedTypeFactory.java b/src/main/java/pico/typecheck/PICOAnnotatedTypeFactory.java index 7273b75..3bb390f 100644 --- a/src/main/java/pico/typecheck/PICOAnnotatedTypeFactory.java +++ b/src/main/java/pico/typecheck/PICOAnnotatedTypeFactory.java @@ -11,6 +11,7 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; +import java.util.Collections; import java.util.HashSet; import java.util.LinkedHashSet; import java.util.List; @@ -22,6 +23,9 @@ import javax.lang.model.element.VariableElement; import javax.lang.model.type.TypeKind; +import com.sun.source.tree.IdentifierTree; +import com.sun.source.tree.ParameterizedTypeTree; +import com.sun.source.util.TreePath; import org.checkerframework.checker.initialization.InitializationAnnotatedTypeFactory; import org.checkerframework.checker.initialization.qual.FBCBottom; import org.checkerframework.checker.initialization.qual.Initialized; @@ -54,6 +58,8 @@ import com.sun.source.tree.UnaryTree; import com.sun.source.tree.VariableTree; +import pico.common.ExtendedViewpointAdapter; +import pico.common.ViewpointAdapterGettable; import qual.Bottom; import qual.Immutable; import qual.Mutable; @@ -71,7 +77,7 @@ //TODO Use @Immutable for classes that extends those predefined immutable classess like String or Number // and explicitly annotated classes with @Immutable on its declaration public class PICOAnnotatedTypeFactory extends InitializationAnnotatedTypeFactory { + PICOStore, PICOTransfer, PICOAnalysis> implements ViewpointAdapterGettable { public PICOAnnotatedTypeFactory(BaseTypeChecker checker) { super(checker); @@ -110,6 +116,7 @@ protected TreeAnnotator createTreeAnnotator() { new PICOPropagationTreeAnnotator(this), new LiteralTreeAnnotator(this), new CommitmentTreeAnnotator(this), + new PICOSuperClauseAnnotator(this), new PICOTreeAnnotator(this)); } @@ -135,6 +142,7 @@ protected TypeAnnotator createTypeAnnotator() { // same location typeAnnotators.add(new PICOTypeAnnotator(this)); typeAnnotators.add(new PICODefaultForTypeAnnotator(this)); + typeAnnotators.add(new PICOEnumDefaultAnnotator(this)); typeAnnotators.add(new CommitmentTypeAnnotator(this)); return new ListTypeAnnotator(typeAnnotators); } @@ -387,6 +395,10 @@ private void applyCommitedIfSupported(AnnotatedTypeFactory annotatedTypeFactory, } } + public ExtendedViewpointAdapter getViewpointAdapter() { + return (ExtendedViewpointAdapter) viewpointAdapter; + } + /**Apply defaults for static fields with non-implicitly immutable types*/ public static class PICOTreeAnnotator extends TreeAnnotator { public PICOTreeAnnotator(AnnotatedTypeFactory atypeFactory) { @@ -524,4 +536,67 @@ protected Void scan(AnnotatedTypeMirror type, Void p) { // to the declared receiver type of instance methods. To view the details, look at ImmutableClass1.java // testcase. // class PICOInheritedFromClassAnnotator extends InheritedFromClassAnnotator {} + + public static class PICOSuperClauseAnnotator extends TreeAnnotator { + + public PICOSuperClauseAnnotator(AnnotatedTypeFactory atypeFactory) { + super(atypeFactory); + } + + private static boolean isSuperClause(TreePath path) { + if (path == null) { + return false; + } + return TreeUtils.isClassTree(path.getParentPath().getLeaf()); + } + + private void addDefaultFromMain(Tree tree, AnnotatedTypeMirror mirror) { + TreePath path = atypeFactory.getPath(tree); + + // only annotates when: + // 1. it's a super clause, AND + // 2. atm OR tree is not annotated + // Note: TreeUtils.typeOf returns no stub or default annotations, but in this scenario they are not needed + // Here only explicit annotation on super clause have effect because framework default rule is overriden + if (isSuperClause(path) && + (!mirror.isAnnotatedInHierarchy(READONLY) || + atypeFactory.getQualifierHierarchy().findAnnotationInHierarchy(TreeUtils.typeOf(tree).getAnnotationMirrors(), READONLY) == null)) { + AnnotatedTypeMirror enclosing = atypeFactory.getAnnotatedType(TreeUtils.enclosingClass(path)); + AnnotationMirror mainBound = enclosing.getAnnotationInHierarchy(READONLY); + mirror.replaceAnnotation(mainBound); + System.err.println("ANNOT: ADDED DEFAULT FOR: " + mirror); + } + } + + @Override + public Void visitIdentifier(IdentifierTree identifierTree, AnnotatedTypeMirror annotatedTypeMirror) { + // super clauses without type param use this + addDefaultFromMain(identifierTree, annotatedTypeMirror); + return super.visitIdentifier(identifierTree, annotatedTypeMirror); + } + + @Override + public Void visitParameterizedType(ParameterizedTypeTree parameterizedTypeTree, AnnotatedTypeMirror annotatedTypeMirror) { + // super clauses with type param use this + addDefaultFromMain(parameterizedTypeTree, annotatedTypeMirror); + return super.visitParameterizedType(parameterizedTypeTree, annotatedTypeMirror); + } + } + + public static class PICOEnumDefaultAnnotator extends TypeAnnotator { + // Defaulting only applies the same annotation to all class declarations + // We need this to "only default enums" to immutable + + public PICOEnumDefaultAnnotator(AnnotatedTypeFactory typeFactory) { + super(typeFactory); + } + + @Override + public Void visitDeclared(AnnotatedTypeMirror.AnnotatedDeclaredType type, Void aVoid) { + if (PICOTypeUtil.isEnumOrEnumConstant(type)) { + type.addMissingAnnotations(Collections.singleton(IMMUTABLE)); + } + return super.visitDeclared(type, aVoid); + } + } } diff --git a/src/main/java/pico/typecheck/PICOViewpointAdapter.java b/src/main/java/pico/typecheck/PICOViewpointAdapter.java index d3403d0..4eb82cd 100644 --- a/src/main/java/pico/typecheck/PICOViewpointAdapter.java +++ b/src/main/java/pico/typecheck/PICOViewpointAdapter.java @@ -19,7 +19,7 @@ import org.checkerframework.javacutil.BugInCF; import exceptions.UnkownImmutabilityQualifierException; -import pico.inference.ExtendedViewpointAdapter; +import pico.common.ExtendedViewpointAdapter; /** * Created by mier on 20/06/17. From 5c42ad4d4ddda4b6cef0e17457955137e82cec86 Mon Sep 17 00:00:00 2001 From: lnsun <57457122+lnsun@users.noreply.github.com> Date: Fri, 24 Apr 2020 11:19:45 -0400 Subject: [PATCH 047/144] bound --- .../{typecheck => common}/PICOTypeUtil.java | 3 +- .../PICOInferenceAnnotatedTypeFactory.java | 4 +- .../PICOInferenceRealTypeFactory.java | 2 +- .../inference/PICOInferenceValidator.java | 3 +- .../PICOInferenceViewpointAdapter.java | 2 +- .../pico/inference/PICOInferenceVisitor.java | 2 +- .../pico/inference/PICOVariableAnnotator.java | 2 +- .../ObjectIdentityMethodEnforcer.java | 2 +- .../typecheck/PICOAnnotatedTypeFactory.java | 62 ++++++++++++++++--- .../java/pico/typecheck/PICOValidator.java | 3 +- src/main/java/pico/typecheck/PICOVisitor.java | 15 +---- 11 files changed, 67 insertions(+), 33 deletions(-) rename src/main/java/pico/{typecheck => common}/PICOTypeUtil.java (99%) diff --git a/src/main/java/pico/typecheck/PICOTypeUtil.java b/src/main/java/pico/common/PICOTypeUtil.java similarity index 99% rename from src/main/java/pico/typecheck/PICOTypeUtil.java rename to src/main/java/pico/common/PICOTypeUtil.java index cdfef66..8ff24ec 100644 --- a/src/main/java/pico/typecheck/PICOTypeUtil.java +++ b/src/main/java/pico/common/PICOTypeUtil.java @@ -1,4 +1,4 @@ -package pico.typecheck; +package pico.common; import checkers.inference.InferenceMain; import checkers.inference.SlotManager; @@ -25,6 +25,7 @@ import org.checkerframework.javacutil.ElementUtils; import org.checkerframework.javacutil.TreeUtils; import org.checkerframework.javacutil.TypesUtils; +import pico.typecheck.PICOVisitor; import qual.Assignable; import qual.Immutable; import qual.ObjectIdentityMethod; diff --git a/src/main/java/pico/inference/PICOInferenceAnnotatedTypeFactory.java b/src/main/java/pico/inference/PICOInferenceAnnotatedTypeFactory.java index 001c2b9..f2ac15d 100644 --- a/src/main/java/pico/inference/PICOInferenceAnnotatedTypeFactory.java +++ b/src/main/java/pico/inference/PICOInferenceAnnotatedTypeFactory.java @@ -24,14 +24,12 @@ import org.checkerframework.framework.type.treeannotator.ListTreeAnnotator; import org.checkerframework.framework.type.treeannotator.PropagationTreeAnnotator; import org.checkerframework.framework.type.treeannotator.TreeAnnotator; -import org.checkerframework.framework.type.typeannotator.DefaultQualifierForUseTypeAnnotator; import org.checkerframework.framework.type.typeannotator.ListTypeAnnotator; import org.checkerframework.framework.type.typeannotator.TypeAnnotator; import org.checkerframework.javacutil.Pair; import org.checkerframework.javacutil.TreeUtils; -import pico.typecheck.PICOAnnotatedTypeFactory; import pico.typecheck.PICOAnnotatedTypeFactory.PICODefaultForTypeAnnotator; -import pico.typecheck.PICOTypeUtil; +import pico.common.PICOTypeUtil; import javax.lang.model.element.AnnotationMirror; import javax.lang.model.type.TypeKind; diff --git a/src/main/java/pico/inference/PICOInferenceRealTypeFactory.java b/src/main/java/pico/inference/PICOInferenceRealTypeFactory.java index 8bd7221..7528bf8 100644 --- a/src/main/java/pico/inference/PICOInferenceRealTypeFactory.java +++ b/src/main/java/pico/inference/PICOInferenceRealTypeFactory.java @@ -33,7 +33,7 @@ import pico.common.ExtendedViewpointAdapter; import pico.common.ViewpointAdapterGettable; import pico.typecheck.PICOAnnotatedTypeFactory; -import pico.typecheck.PICOTypeUtil; +import pico.common.PICOTypeUtil; import pico.typecheck.PICOViewpointAdapter; import qual.Bottom; import qual.Immutable; diff --git a/src/main/java/pico/inference/PICOInferenceValidator.java b/src/main/java/pico/inference/PICOInferenceValidator.java index 42eed57..d4722b7 100644 --- a/src/main/java/pico/inference/PICOInferenceValidator.java +++ b/src/main/java/pico/inference/PICOInferenceValidator.java @@ -2,7 +2,6 @@ import checkers.inference.InferenceValidator; import checkers.inference.InferenceVisitor; -import com.sun.org.apache.regexp.internal.RE; import com.sun.source.tree.Tree; import com.sun.source.tree.VariableTree; import org.checkerframework.common.basetype.BaseTypeChecker; @@ -13,7 +12,7 @@ import org.checkerframework.framework.type.AnnotatedTypeMirror.AnnotatedDeclaredType; import org.checkerframework.framework.type.AnnotatedTypeMirror.AnnotatedPrimitiveType; import org.checkerframework.javacutil.TreeUtils; -import pico.typecheck.PICOTypeUtil; +import pico.common.PICOTypeUtil; import javax.lang.model.element.AnnotationMirror; import javax.lang.model.element.VariableElement; diff --git a/src/main/java/pico/inference/PICOInferenceViewpointAdapter.java b/src/main/java/pico/inference/PICOInferenceViewpointAdapter.java index d68a2cf..d418eb2 100644 --- a/src/main/java/pico/inference/PICOInferenceViewpointAdapter.java +++ b/src/main/java/pico/inference/PICOInferenceViewpointAdapter.java @@ -4,7 +4,7 @@ import org.checkerframework.framework.type.AnnotatedTypeFactory; import org.checkerframework.framework.type.AnnotatedTypeMirror; import pico.common.ExtendedViewpointAdapter; -import pico.typecheck.PICOTypeUtil; +import pico.common.PICOTypeUtil; import javax.lang.model.element.AnnotationMirror; import javax.lang.model.element.Element; diff --git a/src/main/java/pico/inference/PICOInferenceVisitor.java b/src/main/java/pico/inference/PICOInferenceVisitor.java index 372bc3a..389ea7a 100644 --- a/src/main/java/pico/inference/PICOInferenceVisitor.java +++ b/src/main/java/pico/inference/PICOInferenceVisitor.java @@ -39,7 +39,7 @@ import org.checkerframework.javacutil.TypesUtils; import pico.common.ExtendedViewpointAdapter; import pico.common.ViewpointAdapterGettable; -import pico.typecheck.PICOTypeUtil; +import pico.common.PICOTypeUtil; import javax.lang.model.element.AnnotationMirror; import javax.lang.model.element.Element; diff --git a/src/main/java/pico/inference/PICOVariableAnnotator.java b/src/main/java/pico/inference/PICOVariableAnnotator.java index 3971845..c8e18b8 100644 --- a/src/main/java/pico/inference/PICOVariableAnnotator.java +++ b/src/main/java/pico/inference/PICOVariableAnnotator.java @@ -31,7 +31,7 @@ import checkers.inference.model.ConstraintManager; import checkers.inference.model.VariableSlot; import checkers.inference.model.tree.ArtificialExtendsBoundTree; -import pico.typecheck.PICOTypeUtil; +import pico.common.PICOTypeUtil; public class PICOVariableAnnotator extends VariableAnnotator { diff --git a/src/main/java/pico/typecheck/ObjectIdentityMethodEnforcer.java b/src/main/java/pico/typecheck/ObjectIdentityMethodEnforcer.java index fb54dea..1e6ff71 100644 --- a/src/main/java/pico/typecheck/ObjectIdentityMethodEnforcer.java +++ b/src/main/java/pico/typecheck/ObjectIdentityMethodEnforcer.java @@ -11,7 +11,7 @@ import org.checkerframework.framework.util.AnnotatedTypes; import org.checkerframework.javacutil.ElementUtils; import org.checkerframework.javacutil.TreeUtils; -import qual.Assignable; +import pico.common.PICOTypeUtil; import javax.lang.model.element.Element; import javax.lang.model.element.ElementKind; diff --git a/src/main/java/pico/typecheck/PICOAnnotatedTypeFactory.java b/src/main/java/pico/typecheck/PICOAnnotatedTypeFactory.java index 3bb390f..80cd78a 100644 --- a/src/main/java/pico/typecheck/PICOAnnotatedTypeFactory.java +++ b/src/main/java/pico/typecheck/PICOAnnotatedTypeFactory.java @@ -1,12 +1,5 @@ package pico.typecheck; -import static pico.typecheck.PICOAnnotationMirrorHolder.COMMITED; -import static pico.typecheck.PICOAnnotationMirrorHolder.IMMUTABLE; -import static pico.typecheck.PICOAnnotationMirrorHolder.MUTABLE; -import static pico.typecheck.PICOAnnotationMirrorHolder.POLY_MUTABLE; -import static pico.typecheck.PICOAnnotationMirrorHolder.READONLY; -import static pico.typecheck.PICOAnnotationMirrorHolder.SUBSTITUTABLE_POLY_MUTABLE; - import java.lang.annotation.Annotation; import java.util.ArrayList; import java.util.Arrays; @@ -19,9 +12,12 @@ import javax.lang.model.element.AnnotationMirror; import javax.lang.model.element.Element; +import javax.lang.model.element.ElementKind; import javax.lang.model.element.ExecutableElement; import javax.lang.model.element.VariableElement; +import javax.lang.model.type.DeclaredType; import javax.lang.model.type.TypeKind; +import javax.lang.model.type.TypeMirror; import com.sun.source.tree.IdentifierTree; import com.sun.source.tree.ParameterizedTypeTree; @@ -44,6 +40,7 @@ import org.checkerframework.framework.type.treeannotator.TreeAnnotator; import org.checkerframework.framework.type.typeannotator.*; import org.checkerframework.framework.util.MultiGraphQualifierHierarchy.MultiGraphFactory; +import org.checkerframework.javacutil.AnnotationUtils; import org.checkerframework.javacutil.BugInCF; import org.checkerframework.javacutil.ElementUtils; import org.checkerframework.javacutil.Pair; @@ -59,6 +56,7 @@ import com.sun.source.tree.VariableTree; import pico.common.ExtendedViewpointAdapter; +import pico.common.PICOTypeUtil; import pico.common.ViewpointAdapterGettable; import qual.Bottom; import qual.Immutable; @@ -68,6 +66,8 @@ import qual.ReceiverDependantMutable; import qual.SubstitutablePolyMutable; +import static pico.typecheck.PICOAnnotationMirrorHolder.*; + /** * AnnotatedTypeFactory for PICO. In addition to getting atms, it also propagates and applies mutability * qualifiers correctly depending on AST locations(e.g. fields, binary trees) or methods(toString(), hashCode(), @@ -399,6 +399,54 @@ public ExtendedViewpointAdapter getViewpointAdapter() { return (ExtendedViewpointAdapter) viewpointAdapter; } + @Override + protected Set getDefaultTypeDeclarationBounds() { + @SuppressWarnings("unchecked") + Set frameworkDefault = (Set) super.getDefaultTypeDeclarationBounds(); + return replaceAnnotationInHierarchy(frameworkDefault, MUTABLE); + } + + @Override + public Set getTypeDeclarationBounds(TypeMirror type) { + AnnotationMirror mut = getTypeDeclarationBoundForMutability(type); + Set frameworkDefault = super.getTypeDeclarationBounds(type); + if (mut != null) { + replaceAnnotationInHierarchy(frameworkDefault, mut); + } + return frameworkDefault; + } + + private Set replaceAnnotationInHierarchy(Set set, AnnotationMirror mirror) { + Set result = new HashSet<>(set); + AnnotationMirror removeThis = getQualifierHierarchy().findAnnotationInSameHierarchy(set, mirror); + result.remove(removeThis); + result.add(mirror); + return result; + } + + private AnnotationMirror getTypeDeclarationBoundForMutability(TypeMirror type) { + // copied from inference real type factory with minor modification + // TODO too awkward. maybe overload isImplicitlyImmutableType + if (PICOTypeUtil.isImplicitlyImmutableType(toAnnotatedType(type, false))) { + return IMMUTABLE; + } + if (type.getKind() == TypeKind.ARRAY) { + return READONLY; // if decided to use vpa for array, return RDM. + } + + // IMMUTABLE for enum w/o decl anno + if (type instanceof DeclaredType) { + Element ele = ((DeclaredType) type).asElement(); + if (ele.getKind() == ElementKind.ENUM) { + if (!AnnotationUtils.containsSameByName(getDeclAnnotations(ele), MUTABLE) && + !AnnotationUtils.containsSameByName(getDeclAnnotations(ele), RECEIVER_DEPENDANT_MUTABLE)) { // no decl anno + return IMMUTABLE; + } + } + } + return null; + } + /**Apply defaults for static fields with non-implicitly immutable types*/ public static class PICOTreeAnnotator extends TreeAnnotator { public PICOTreeAnnotator(AnnotatedTypeFactory atypeFactory) { diff --git a/src/main/java/pico/typecheck/PICOValidator.java b/src/main/java/pico/typecheck/PICOValidator.java index d776200..7394cd0 100644 --- a/src/main/java/pico/typecheck/PICOValidator.java +++ b/src/main/java/pico/typecheck/PICOValidator.java @@ -12,8 +12,9 @@ import org.checkerframework.framework.type.AnnotatedTypeMirror.AnnotatedArrayType; import org.checkerframework.framework.type.AnnotatedTypeMirror.AnnotatedDeclaredType; import org.checkerframework.framework.type.AnnotatedTypeMirror.AnnotatedPrimitiveType; -import org.checkerframework.javacutil.ElementUtils; import org.checkerframework.javacutil.TreeUtils; +import pico.common.PICOTypeUtil; + import javax.lang.model.element.VariableElement; import static pico.typecheck.PICOAnnotationMirrorHolder.BOTTOM; diff --git a/src/main/java/pico/typecheck/PICOVisitor.java b/src/main/java/pico/typecheck/PICOVisitor.java index 66f23ab..ddb0f2a 100644 --- a/src/main/java/pico/typecheck/PICOVisitor.java +++ b/src/main/java/pico/typecheck/PICOVisitor.java @@ -8,7 +8,6 @@ import static pico.typecheck.PICOAnnotationMirrorHolder.READONLY; import static pico.typecheck.PICOAnnotationMirrorHolder.RECEIVER_DEPENDANT_MUTABLE; -import com.sun.source.util.TreePath; import java.util.HashMap; import java.util.HashSet; import java.util.List; @@ -37,7 +36,6 @@ import org.checkerframework.javacutil.AnnotationUtils; import org.checkerframework.javacutil.BugInCF; import org.checkerframework.javacutil.ElementUtils; -import org.checkerframework.javacutil.Pair; import org.checkerframework.javacutil.TreeUtils; import org.checkerframework.javacutil.TypesUtils; @@ -54,6 +52,7 @@ import com.sun.source.tree.Tree.Kind; import com.sun.source.tree.UnaryTree; import com.sun.source.tree.VariableTree; +import pico.common.PICOTypeUtil; /** * Created by mier on 20/06/17. @@ -86,18 +85,6 @@ protected TypeValidator createTypeValidator() { public boolean isValidUse(AnnotatedDeclaredType declarationType, AnnotatedDeclaredType useType, Tree tree) { AnnotationMirror declared = declarationType.getAnnotationInHierarchy(READONLY); // No need to have special case for java.lang.Object, as it's not by default @Readonly anymore -// if (AnnotationUtils.areSame(declared, atypeFactory.READONLY)) { -// // Special case for java.lang.Object. Usually @Readonly is never used as a bound annotation for a -// // TypeElement. But we want to have @Readonly as the default for java.lang.Object. There is no way -// // of doing this using any existing family of @DefaultFor qualifiers, but @ImplicitFor annotation -// // does the trick. But the side effect is, we can't write @ReceiverDependantMutable, which is the -// // correct bound for Object element, in jdk.astub, because otherwise it makes all java.lang.Object -// // to be @ReceiverDependantMutable; Another side effect is here @Readonly is passed into here as -// // the element type for java.lang.Object. So we have to have this special case only for java.lang. -// // Object -// return true; -// } - // declarationType is now upper bound. Could be READONLY // needs adaption for constructors and enum // TODO: adapt to default instead of upper-bound or repeated rules if (tree instanceof MethodTree && TreeUtils.isConstructor((MethodTree) tree)) { From 4e72db6a959a0df103c8c69c7f34604e6130da8c Mon Sep 17 00:00:00 2001 From: lnsun <57457122+lnsun@users.noreply.github.com> Date: Tue, 5 May 2020 10:25:42 -0400 Subject: [PATCH 048/144] enum & super clause --- .../PICOInferenceRealTypeFactory.java | 1 - .../typecheck/PICOAnnotatedTypeFactory.java | 26 ++++- .../java/pico/typecheck/PICOValidator.java | 10 ++ src/main/java/pico/typecheck/PICOVisitor.java | 106 +++++------------- .../EnumConstantNotAlwaysMutable.java | 2 +- testinput/typecheck/EnumTests.java | 6 +- 6 files changed, 66 insertions(+), 85 deletions(-) diff --git a/src/main/java/pico/inference/PICOInferenceRealTypeFactory.java b/src/main/java/pico/inference/PICOInferenceRealTypeFactory.java index 7528bf8..340787e 100644 --- a/src/main/java/pico/inference/PICOInferenceRealTypeFactory.java +++ b/src/main/java/pico/inference/PICOInferenceRealTypeFactory.java @@ -180,7 +180,6 @@ protected DefaultQualifierForUseTypeAnnotator createDefaultForUseTypeAnnotator() @Override public AnnotatedTypeMirror getTypeOfExtendsImplements(Tree clause) { - // TODO is this still needed with PICOSuperClauseAnnotator? // add default anno from class main qual, if no qual present AnnotatedTypeMirror enclosing = getAnnotatedType(TreeUtils.enclosingClass(getPath(clause))); AnnotationMirror mainBound = enclosing.getAnnotationInHierarchy(READONLY); diff --git a/src/main/java/pico/typecheck/PICOAnnotatedTypeFactory.java b/src/main/java/pico/typecheck/PICOAnnotatedTypeFactory.java index 80cd78a..95b4c7a 100644 --- a/src/main/java/pico/typecheck/PICOAnnotatedTypeFactory.java +++ b/src/main/java/pico/typecheck/PICOAnnotatedTypeFactory.java @@ -183,7 +183,7 @@ public boolean getShouldDefaultTypeVarLocals() { public void addComputedTypeAnnotations(Element elt, AnnotatedTypeMirror type) { PICOTypeUtil.addDefaultForField(this, type, elt); PICOTypeUtil.defaultConstructorReturnToClassBound(this, elt, type); - PICOTypeUtil.applyImmutableToEnumAndEnumConstant(type); +// PICOTypeUtil.applyImmutableToEnumAndEnumConstant(type); super.addComputedTypeAnnotations(elt, type); } @@ -411,7 +411,7 @@ public Set getTypeDeclarationBounds(TypeMirror type) { AnnotationMirror mut = getTypeDeclarationBoundForMutability(type); Set frameworkDefault = super.getTypeDeclarationBounds(type); if (mut != null) { - replaceAnnotationInHierarchy(frameworkDefault, mut); + frameworkDefault = replaceAnnotationInHierarchy(frameworkDefault, mut); } return frameworkDefault; } @@ -447,6 +447,24 @@ private AnnotationMirror getTypeDeclarationBoundForMutability(TypeMirror type) { return null; } + @Override + public AnnotatedTypeMirror getTypeOfExtendsImplements(Tree clause) { + // this is still needed with PICOSuperClauseAnnotator. + // maybe just use getAnnotatedType + // add default anno from class main qual, if no qual present + AnnotatedTypeMirror enclosing = getAnnotatedType(TreeUtils.enclosingClass(getPath(clause))); + AnnotationMirror mainBound = enclosing.getAnnotationInHierarchy(READONLY); + AnnotatedTypeMirror fromTypeTree = this.fromTypeTree(clause); + if (!fromTypeTree.isAnnotatedInHierarchy(READONLY)) { + fromTypeTree.addAnnotation(mainBound); + } + + // for FBC quals + Set bound = this.getTypeDeclarationBounds(fromTypeTree.getUnderlyingType()); + fromTypeTree.addMissingAnnotations(bound); + return fromTypeTree; + } + /**Apply defaults for static fields with non-implicitly immutable types*/ public static class PICOTreeAnnotator extends TreeAnnotator { public PICOTreeAnnotator(AnnotatedTypeFactory atypeFactory) { @@ -591,7 +609,7 @@ public PICOSuperClauseAnnotator(AnnotatedTypeFactory atypeFactory) { super(atypeFactory); } - private static boolean isSuperClause(TreePath path) { + public static boolean isSuperClause(TreePath path) { if (path == null) { return false; } @@ -612,7 +630,7 @@ private void addDefaultFromMain(Tree tree, AnnotatedTypeMirror mirror) { AnnotatedTypeMirror enclosing = atypeFactory.getAnnotatedType(TreeUtils.enclosingClass(path)); AnnotationMirror mainBound = enclosing.getAnnotationInHierarchy(READONLY); mirror.replaceAnnotation(mainBound); - System.err.println("ANNOT: ADDED DEFAULT FOR: " + mirror); +// System.err.println("ANNOT: ADDED DEFAULT FOR: " + mirror); } } diff --git a/src/main/java/pico/typecheck/PICOValidator.java b/src/main/java/pico/typecheck/PICOValidator.java index 7394cd0..fad1c05 100644 --- a/src/main/java/pico/typecheck/PICOValidator.java +++ b/src/main/java/pico/typecheck/PICOValidator.java @@ -39,6 +39,16 @@ public Void visitDeclared(AnnotatedDeclaredType type, Tree tree) { return super.visitDeclared(type, tree); } + @Override + protected boolean shouldCheckTopLevelDeclaredType(AnnotatedTypeMirror type, Tree tree) { + // check top annotations in extends/implements clauses + if ((tree.getKind() == Kind.IDENTIFIER || tree.getKind() == Kind.PARAMETERIZED_TYPE) && + PICOAnnotatedTypeFactory.PICOSuperClauseAnnotator.isSuperClause(atypeFactory.getPath(tree))) { + return true; + } + return super.shouldCheckTopLevelDeclaredType(type, tree); + } + @Override public Void visitArray(AnnotatedArrayType type, Tree tree) { checkStaticReceiverDependantMutableError(type, tree); diff --git a/src/main/java/pico/typecheck/PICOVisitor.java b/src/main/java/pico/typecheck/PICOVisitor.java index ddb0f2a..3ddec61 100644 --- a/src/main/java/pico/typecheck/PICOVisitor.java +++ b/src/main/java/pico/typecheck/PICOVisitor.java @@ -52,7 +52,9 @@ import com.sun.source.tree.Tree.Kind; import com.sun.source.tree.UnaryTree; import com.sun.source.tree.VariableTree; +import pico.common.ExtendedViewpointAdapter; import pico.common.PICOTypeUtil; +import pico.common.ViewpointAdapterGettable; /** * Created by mier on 20/06/17. @@ -83,39 +85,15 @@ protected TypeValidator createTypeValidator() { // (at least for types other than java.lang.Object) @Override public boolean isValidUse(AnnotatedDeclaredType declarationType, AnnotatedDeclaredType useType, Tree tree) { - AnnotationMirror declared = declarationType.getAnnotationInHierarchy(READONLY); - // No need to have special case for java.lang.Object, as it's not by default @Readonly anymore - // needs adaption for constructors and enum - // TODO: adapt to default instead of upper-bound or repeated rules - if (tree instanceof MethodTree && TreeUtils.isConstructor((MethodTree) tree)) { - if (AnnotationUtils.areSame(declared, READONLY)) { - declared = MUTABLE; - if (atypeFactory.getPath(tree).getParentPath().getLeaf().getKind() == Kind.ENUM) { - declared = IMMUTABLE; - } - } - } -// if (tree.getKind() == Kind.ENUM) { -// declared = IMMUTABLE; -// } - // default to implicit - if (PICOTypeUtil.isImplicitlyImmutableType(declarationType)) { - declared = IMMUTABLE; - } - if (AnnotationUtils.areSame(declared, RECEIVER_DEPENDANT_MUTABLE) || AnnotationUtils.areSame(declared, READONLY)) { - // Element is declared with @ReceiverDependantMutable bound, any instantiation is allowed. We don't use - // a subtype check to validate the correct usage here. Because @Readonly is the super type of - // @ReceiverDependantMutable, but it's still considered valid usage. + // FIXME workaround for typecheck BOTTOM + if (useType.hasAnnotation(BOTTOM)) { return true; } - // At this point, element type can only be @Mutable or @Immutable. Otherwise, it's a problem in - // PICOVisitor#processClassTree(ClassTree) - assert AnnotationUtils.areSame(declared, MUTABLE) || AnnotationUtils.areSame(declared, IMMUTABLE); + AnnotationMirror declared = declarationType.getAnnotationInHierarchy(READONLY); AnnotationMirror used = useType.getAnnotationInHierarchy(READONLY); - assert AnnotationUtils.areSame(declared, MUTABLE) || AnnotationUtils.areSame(declared, IMMUTABLE); - return isAnnoValidUse(declared, used); + return isAdaptedSubtype(used, declared); } static private boolean isAnnoValidUse(AnnotationMirror declared, AnnotationMirror used) { @@ -140,6 +118,12 @@ static private boolean isAnnoValidUse(AnnotationMirror declared, AnnotationMirro return false; } + private boolean isAdaptedSubtype(AnnotationMirror lhs, AnnotationMirror rhs) { + ExtendedViewpointAdapter vpa = ((ViewpointAdapterGettable)atypeFactory).getViewpointAdapter(); + AnnotationMirror adapted = vpa.rawCombineAnnotationWithAnnotation(lhs, rhs); + return atypeFactory.getQualifierHierarchy().isSubtype(adapted, lhs); + } + @Override protected void commonAssignmentCheck( Tree varTree, ExpressionTree valueExp, String errorKey) { @@ -197,15 +181,16 @@ protected void checkConstructorInvocation(AnnotatedDeclaredType invocation, Anno } /*Copied Code End*/ - // TODO use CF base check - // if no explicit anno it must inherited from class decl - AnnotationMirror declAnno = constructor.getReturnType().getAnnotationInHierarchy(READONLY); - AnnotationMirror useAnno = invocation.getAnnotationInHierarchy(READONLY); - declAnno = declAnno == null ? MUTABLE : declAnno; - - if(useAnno != null && !AnnotationUtils.areSameByName(declAnno, POLY_MUTABLE) && !isAnnoValidUse(declAnno, useAnno)) { - checker.report(Result.failure("type.invalid.annotations.on.use", declAnno, useAnno), newClassTree); - } + // TODO fix inference counterpart, not here +// // CF base check disabled by InitializationVisitor +// // if no explicit anno it must inherited from class decl +// AnnotationMirror declAnno = constructor.getReturnType().getAnnotationInHierarchy(READONLY); +// AnnotationMirror useAnno = invocation.getAnnotationInHierarchy(READONLY); +// declAnno = declAnno == null ? MUTABLE : declAnno; +// +// if(useAnno != null && !AnnotationUtils.areSameByName(declAnno, POLY_MUTABLE) && !isAdaptedSubtype(useAnno, declAnno)) { +// checker.report(Result.failure("type.invalid.annotations.on.use", declAnno, useAnno), newClassTree); +// } // The immutability return qualifier of the constructor (returnType) must be supertype of the // constructor invocation immutability qualifier(invocation). @@ -381,7 +366,7 @@ private void reportFieldOrArrayWriteError(Tree node, ExpressionTree variable, An public Void visitVariable(VariableTree node, Void p) { VariableElement element = TreeUtils.elementFromDeclaration(node); AnnotatedTypeMirror type = atypeFactory.getAnnotatedType(element); - if (element != null && element.getKind() == ElementKind.FIELD) { + if (element.getKind() == ElementKind.FIELD) { if (type.hasAnnotation(POLY_MUTABLE)) { checker.report(Result.failure("field.polymutable.forbidden", element), node); } @@ -399,7 +384,7 @@ private void checkAndReportInvalidAnnotationOnUse(AnnotatedTypeMirror type, Tree defaultAnno = anno; } } - if (!isAnnoValidUse(defaultAnno, useAnno)) { + if (!isAdaptedSubtype(useAnno, defaultAnno)) { checker.report(Result.failure("type.invalid.annotations.on.use", defaultAnno, useAnno), node); } } @@ -507,55 +492,22 @@ public void processClassTree(ClassTree node) { @Override protected void checkExtendsImplements(ClassTree classTree) { + // validateTypeOf does not check super trees PICOAnnotatedTypeFactory.PICOQualifierForUseTypeAnnotator annotator = new PICOAnnotatedTypeFactory.PICOQualifierForUseTypeAnnotator(atypeFactory); if (TypesUtils.isAnonymous(TreeUtils.typeOf(classTree))) { // Don't check extends clause on anonymous classes. return; } - - AnnotationMirror classAnnot = - atypeFactory.getAnnotatedType(classTree).getAnnotationInHierarchy(READONLY); - + Tree extendsClause = classTree.getExtendsClause(); if (extendsClause != null) { - // now defaultFor annotator is not called before this - // TODO Lian: use CF default annotator instead of annotating manually here (default should be annotated before entry) - AnnotationMirror extAnnot = atypeFactory.fromTypeTree(extendsClause).getAnnotationInHierarchy(READONLY); - if (extAnnot == null) { - AnnotatedTypeMirror defaultAnnotatedExtends = atypeFactory.fromTypeTree(extendsClause).deepCopy(false); // side effect!! - annotator.visit(defaultAnnotatedExtends); - extAnnot = defaultAnnotatedExtends.getAnnotationInHierarchy(READONLY); - } - if (extAnnot != null && !AnnotationUtils.areSame(extAnnot, classAnnot)) { - checker.report( - Result.failure( - "declaration.inconsistent.with.extends.clause", - classAnnot, - extAnnot), - extendsClause); - } - + } - + List implementsClauses = classTree.getImplementsClause(); if (implementsClauses != null) { for (Tree impl : implementsClauses) { - // now defaultFor annotator is not called before this - // TODO Lian: use CF default annotator instead of annotating manually here (default should be annotated before entry) - AnnotationMirror implAnnot = atypeFactory.fromTypeTree(impl).getAnnotationInHierarchy(READONLY); - if (implAnnot == null) { - AnnotatedTypeMirror defaultAnnotatedImpl = atypeFactory.fromTypeTree(impl).deepCopy(false); - annotator.visit(defaultAnnotatedImpl); - implAnnot = defaultAnnotatedImpl.getAnnotationInHierarchy(READONLY); - } - if (implAnnot != null && !AnnotationUtils.areSame(implAnnot, classAnnot)) { - checker.report( - Result.failure( - "declaration.inconsistent.with.implements.clause", - classAnnot, - implAnnot), - impl); - } + } } } diff --git a/testinput/typecheck/EnumConstantNotAlwaysMutable.java b/testinput/typecheck/EnumConstantNotAlwaysMutable.java index ea48386..0d93073 100644 --- a/testinput/typecheck/EnumConstantNotAlwaysMutable.java +++ b/testinput/typecheck/EnumConstantNotAlwaysMutable.java @@ -18,7 +18,7 @@ public class EnumConstantNotAlwaysMutable { @ReceiverDependantMutable Kind invalidKind; // :: error: (type.invalid.annotations.on.use) @Mutable Kind invalidKind2; - // :: error: (type.invalid.annotations.on.use) + // no error now @Readonly Kind invalidKind3; EnumConstantNotAlwaysMutable() { diff --git a/testinput/typecheck/EnumTests.java b/testinput/typecheck/EnumTests.java index 73f6715..46fd328 100644 --- a/testinput/typecheck/EnumTests.java +++ b/testinput/typecheck/EnumTests.java @@ -15,11 +15,13 @@ void foo(/*immutable*/ MyEnum e) { /*immutable*/ private static enum MyEnum { - T1, T2; + T1, + T2; } @Mutable private static enum MutableEnum { - M1, M2; + M1, + M2; } } \ No newline at end of file From ba48fdac5cd9d2f7a03254103d09bd7682d08c12 Mon Sep 17 00:00:00 2001 From: lnsun <57457122+lnsun@users.noreply.github.com> Date: Tue, 5 May 2020 15:37:52 -0400 Subject: [PATCH 049/144] test case update --- src/main/java/pico/typecheck/PICOVisitor.java | 42 +++++++++---------- testinput/typecheck/CompatibilityBEI2.java | 4 +- .../ReceiverTypeOutsideConstructor.java | 10 ++--- 3 files changed, 28 insertions(+), 28 deletions(-) diff --git a/src/main/java/pico/typecheck/PICOVisitor.java b/src/main/java/pico/typecheck/PICOVisitor.java index 3ddec61..292cdb7 100644 --- a/src/main/java/pico/typecheck/PICOVisitor.java +++ b/src/main/java/pico/typecheck/PICOVisitor.java @@ -490,27 +490,27 @@ public void processClassTree(ClassTree node) { super.processClassTree(node); } - @Override - protected void checkExtendsImplements(ClassTree classTree) { - // validateTypeOf does not check super trees - PICOAnnotatedTypeFactory.PICOQualifierForUseTypeAnnotator annotator = new PICOAnnotatedTypeFactory.PICOQualifierForUseTypeAnnotator(atypeFactory); - if (TypesUtils.isAnonymous(TreeUtils.typeOf(classTree))) { - // Don't check extends clause on anonymous classes. - return; - } - - Tree extendsClause = classTree.getExtendsClause(); - if (extendsClause != null) { - - } - - List implementsClauses = classTree.getImplementsClause(); - if (implementsClauses != null) { - for (Tree impl : implementsClauses) { - - } - } - } +// @Override +// protected void checkExtendsImplements(ClassTree classTree) { +// // validateTypeOf does not check super trees +// PICOAnnotatedTypeFactory.PICOQualifierForUseTypeAnnotator annotator = new PICOAnnotatedTypeFactory.PICOQualifierForUseTypeAnnotator(atypeFactory); +// if (TypesUtils.isAnonymous(TreeUtils.typeOf(classTree))) { +// // Don't check extends clause on anonymous classes. +// return; +// } +// +// Tree extendsClause = classTree.getExtendsClause(); +// if (extendsClause != null) { +// +// } +// +// List implementsClauses = classTree.getImplementsClause(); +// if (implementsClauses != null) { +// for (Tree impl : implementsClauses) { +// +// } +// } +// } /** * The invoked constructor’s return type adapted to the invoking constructor’s return type must diff --git a/testinput/typecheck/CompatibilityBEI2.java b/testinput/typecheck/CompatibilityBEI2.java index 440de14..4014426 100644 --- a/testinput/typecheck/CompatibilityBEI2.java +++ b/testinput/typecheck/CompatibilityBEI2.java @@ -43,10 +43,10 @@ abstract class O implements CharSequence {} @Immutable interface ImmutableInterface {} -// :: error: (declaration.inconsistent.with.implements.clause) :: error: (type.argument.type.incompatible) +// :: error: (type.invalid.annotations.on.use) @Mutable abstract class P implements ImmutableInterface<@Mutable Object> {} @Immutable abstract class Q implements ImmutableInterface<@Immutable Object> {} -// :: error: (declaration.inconsistent.with.implements.clause) :: error: (type.argument.type.incompatible) +// :: error: (type.invalid.annotations.on.use) @ReceiverDependantMutable abstract class R implements ImmutableInterface<@ReceiverDependantMutable Object> {} diff --git a/testinput/typecheck/ReceiverTypeOutsideConstructor.java b/testinput/typecheck/ReceiverTypeOutsideConstructor.java index 7162e32..c153c83 100644 --- a/testinput/typecheck/ReceiverTypeOutsideConstructor.java +++ b/testinput/typecheck/ReceiverTypeOutsideConstructor.java @@ -31,13 +31,13 @@ class A { @Immutable class AIMS extends A {} -// :: error: (declaration.inconsistent.with.extends.clause) :: error: (super.invocation.invalid) +// :: error: (type.invalid.annotations.on.use) :: error: (super.invocation.invalid) @ReceiverDependantMutable class ARDMS extends A {} -// :: error: (declaration.inconsistent.with.extends.clause) :: error: (super.invocation.invalid) +// :: error: (type.invalid.annotations.on.use) :: error: (super.invocation.invalid) @Mutable class AMS extends A {} -// :: error: (declaration.inconsistent.with.extends.clause) :: error: (super.invocation.invalid) +// :: error: (type.invalid.annotations.on.use) :: error: (super.invocation.invalid) class AUNKS extends A {} // ReceiverDependantMutable class @@ -103,10 +103,10 @@ class C { } } -// :: error: (declaration.inconsistent.with.extends.clause) +// :: error: (type.invalid.annotations.on.use) @Immutable class CIMS extends C {} -// :: error: (declaration.inconsistent.with.extends.clause) :: error: (super.invocation.invalid) +// :: error: (type.invalid.annotations.on.use) :: error: (super.invocation.invalid) @ReceiverDependantMutable class CRDMS extends C {} // :: error: (super.invocation.invalid) From b85315608dbf42e2128f65ff14ac9fe0cbd0d2da Mon Sep 17 00:00:00 2001 From: lnsun <57457122+lnsun@users.noreply.github.com> Date: Wed, 6 May 2020 11:13:45 -0400 Subject: [PATCH 050/144] tweaks on generic --- src/main/java/pico/typecheck/PICOAnnotatedTypeFactory.java | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/main/java/pico/typecheck/PICOAnnotatedTypeFactory.java b/src/main/java/pico/typecheck/PICOAnnotatedTypeFactory.java index 95b4c7a..bedd92b 100644 --- a/src/main/java/pico/typecheck/PICOAnnotatedTypeFactory.java +++ b/src/main/java/pico/typecheck/PICOAnnotatedTypeFactory.java @@ -400,9 +400,8 @@ public ExtendedViewpointAdapter getViewpointAdapter() { } @Override - protected Set getDefaultTypeDeclarationBounds() { - @SuppressWarnings("unchecked") - Set frameworkDefault = (Set) super.getDefaultTypeDeclarationBounds(); + protected Set getDefaultTypeDeclarationBounds() { + Set frameworkDefault = new HashSet<>(super.getDefaultTypeDeclarationBounds()); return replaceAnnotationInHierarchy(frameworkDefault, MUTABLE); } From 2e1c6b0f4d3814f25c178f9e55b0fcf87d3f12aa Mon Sep 17 00:00:00 2001 From: lnsun <57457122+lnsun@users.noreply.github.com> Date: Thu, 7 May 2020 11:23:07 -0400 Subject: [PATCH 051/144] fix anno --- src/main/java/pico/inference/PICOInferenceValidator.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/pico/inference/PICOInferenceValidator.java b/src/main/java/pico/inference/PICOInferenceValidator.java index 42eed57..e1cc3bc 100644 --- a/src/main/java/pico/inference/PICOInferenceValidator.java +++ b/src/main/java/pico/inference/PICOInferenceValidator.java @@ -43,7 +43,7 @@ public Void visitDeclared(AnnotatedDeclaredType type, Tree tree) { AnnotatedDeclaredType defaultType = (AnnotatedDeclaredType) atypeFactory.getAnnotatedType(type.getUnderlyingType().asElement()); // TODO for defaulted super clause: should top anno be checked? (see shouldCheckTopLevelDeclaredType()) - if (defaultType.isAnnotatedInHierarchy(READONLY) && !PICOTypeUtil.isEnumOrEnumConstant(defaultType)) { + if (defaultType.getAnnotationInHierarchy(READONLY) == null && !PICOTypeUtil.isEnumOrEnumConstant(defaultType)) { defaultType = defaultType.deepCopy(); defaultType.replaceAnnotation(MUTABLE); } From 461c86a0f525b16cc764d51e7aff68c3b2ae606e Mon Sep 17 00:00:00 2001 From: lnsun <57457122+lnsun@users.noreply.github.com> Date: Fri, 8 May 2020 09:44:02 -0400 Subject: [PATCH 052/144] poly workaround --- src/main/java/pico/typecheck/PICOVisitor.java | 31 ++++++------------- 1 file changed, 9 insertions(+), 22 deletions(-) diff --git a/src/main/java/pico/typecheck/PICOVisitor.java b/src/main/java/pico/typecheck/PICOVisitor.java index 292cdb7..ea7b0a4 100644 --- a/src/main/java/pico/typecheck/PICOVisitor.java +++ b/src/main/java/pico/typecheck/PICOVisitor.java @@ -89,6 +89,10 @@ public boolean isValidUse(AnnotatedDeclaredType declarationType, AnnotatedDeclar if (useType.hasAnnotation(BOTTOM)) { return true; } + // FIXME workaround for poly anno, remove after fix substitutable poly and add poly vp rules + if (useType.hasAnnotation(POLY_MUTABLE)) { + return true; + } AnnotationMirror declared = declarationType.getAnnotationInHierarchy(READONLY); AnnotationMirror used = useType.getAnnotationInHierarchy(READONLY); @@ -371,12 +375,17 @@ public Void visitVariable(VariableTree node, Void p) { checker.report(Result.failure("field.polymutable.forbidden", element), node); } } + // TODO use base cf check methods checkAndReportInvalidAnnotationOnUse(type, node); return super.visitVariable(node, p); } private void checkAndReportInvalidAnnotationOnUse(AnnotatedTypeMirror type, Tree node) { AnnotationMirror useAnno = type.getAnnotationInHierarchy(READONLY); + // FIXME rm after poly vp + if (useAnno != null && AnnotationUtils.areSame(useAnno, POLY_MUTABLE)) { + return; + } if (useAnno != null && !PICOTypeUtil.isImplicitlyImmutableType(type) && type.getKind() != TypeKind.ARRAY) { // TODO: annotate the use instead of using this AnnotationMirror defaultAnno = MUTABLE; for (AnnotationMirror anno : atypeFactory.getTypeDeclarationBounds(atypeFactory.getAnnotatedType(node).getUnderlyingType())) { @@ -489,28 +498,6 @@ public void processClassTree(ClassTree node) { super.processClassTree(node); } - -// @Override -// protected void checkExtendsImplements(ClassTree classTree) { -// // validateTypeOf does not check super trees -// PICOAnnotatedTypeFactory.PICOQualifierForUseTypeAnnotator annotator = new PICOAnnotatedTypeFactory.PICOQualifierForUseTypeAnnotator(atypeFactory); -// if (TypesUtils.isAnonymous(TreeUtils.typeOf(classTree))) { -// // Don't check extends clause on anonymous classes. -// return; -// } -// -// Tree extendsClause = classTree.getExtendsClause(); -// if (extendsClause != null) { -// -// } -// -// List implementsClauses = classTree.getImplementsClause(); -// if (implementsClauses != null) { -// for (Tree impl : implementsClauses) { -// -// } -// } -// } /** * The invoked constructor’s return type adapted to the invoking constructor’s return type must From c7b7d08b3a3156259f810a509ceacdb465c45c96 Mon Sep 17 00:00:00 2001 From: lnsun <57457122+lnsun@users.noreply.github.com> Date: Fri, 8 May 2020 09:44:19 -0400 Subject: [PATCH 053/144] update test cases (typecheck 100%, inference-init-tc 100%) --- testinput/inference/inferrable/StrangeReadonly.java | 2 +- testinput/typecheck/Planet.java | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/testinput/inference/inferrable/StrangeReadonly.java b/testinput/inference/inferrable/StrangeReadonly.java index 5f03699..3ff2a46 100644 --- a/testinput/inference/inferrable/StrangeReadonly.java +++ b/testinput/inference/inferrable/StrangeReadonly.java @@ -20,7 +20,7 @@ static void foo() { // side) and always returns true, i.e. "? extends Object" <: VarAnnot(o1), so typeof(o1) :> @Readonly // wasn't generated and o1 is inferred to @Immutable(select any valid solution). - // Lian: CF now could correctly default the upper bound. @Readonly added on parameters. + // Lian: CF now could correctly default the mutable on uses. @Readonly added on parameters. public int compare(@Readonly Object o1, @Readonly Object o2) { // Before inference, @Mutable is casted to @Immutable; After inference, @Readonly is // casted to @Immutable. diff --git a/testinput/typecheck/Planet.java b/testinput/typecheck/Planet.java index 65bfc2e..328ed03 100644 --- a/testinput/typecheck/Planet.java +++ b/testinput/typecheck/Planet.java @@ -84,8 +84,10 @@ public String toString() { public static void main(String[] args) { @Immutable Date discoveryDate = new @Immutable Date(); + // :: error: (type.invalid.annotations.on.use) + @Mutable Planet mPlanet; // :: error: (constructor.invocation.invalid) - @Mutable Planet mPlanet = new @Mutable Planet(1, "Earth", discoveryDate); + mPlanet = new @Mutable Planet(1, "Earth", discoveryDate); @Immutable Planet imPlanet = new @Immutable Planet(1, "Earth", discoveryDate); // None of the fields are allowed to be modified on an immutable object // :: error: (illegal.field.write) From 14ad936f473e485b63ad4fc53475fe927101e694 Mon Sep 17 00:00:00 2001 From: lnsun <57457122+lnsun@users.noreply.github.com> Date: Fri, 8 May 2020 15:54:22 -0400 Subject: [PATCH 054/144] API update: report --- .../ObjectIdentityMethodEnforcer.java | 6 ++--- .../java/pico/typecheck/PICOValidator.java | 2 +- src/main/java/pico/typecheck/PICOVisitor.java | 27 +++++++++---------- 3 files changed, 16 insertions(+), 19 deletions(-) diff --git a/src/main/java/pico/typecheck/ObjectIdentityMethodEnforcer.java b/src/main/java/pico/typecheck/ObjectIdentityMethodEnforcer.java index 1e6ff71..c2c5a45 100644 --- a/src/main/java/pico/typecheck/ObjectIdentityMethodEnforcer.java +++ b/src/main/java/pico/typecheck/ObjectIdentityMethodEnforcer.java @@ -53,7 +53,7 @@ private void checkMethod(MethodInvocationTree node, Element elt) { if (!PICOTypeUtil.isObjectIdentityMethod((ExecutableElement) elt, typeFactory)) { // Report warning since invoked method is not only dependant on abstract state fields, but we // don't know whether this method invocation's result flows into the hashcode or not. - checker.report(Result.warning("object.identity.method.invocation.invalid", elt), node); + checker.reportWarning(node, "object.identity.method.invocation.invalid", elt); } } @@ -78,11 +78,11 @@ private void checkField(Tree node, Element elt) { } if (elt.getKind() == ElementKind.FIELD) { if (ElementUtils.isStatic(elt)) { - checker.report(Result.warning("object.identity.static.field.access.forbidden", elt), node); + checker.reportWarning(node, "object.identity.static.field.access.forbidden", elt); } else { if (!isInAbstractState(elt, typeFactory)) { // Report warning since accessed field is not within abstract state - checker.report(Result.warning("object.identity.field.access.invalid", elt), node); + checker.reportWarning(node, "object.identity.field.access.invalid", elt); } } } diff --git a/src/main/java/pico/typecheck/PICOValidator.java b/src/main/java/pico/typecheck/PICOValidator.java index fad1c05..c5ad05b 100644 --- a/src/main/java/pico/typecheck/PICOValidator.java +++ b/src/main/java/pico/typecheck/PICOValidator.java @@ -90,7 +90,7 @@ private void checkOnlyOneAssignabilityModifierOnField(Tree tree) { } private void reportFieldMultipleAssignabilityModifiersError(VariableElement field) { - checker.report(Result.failure("one.assignability.invalid", field), field); + checker.reportError(field, "one.assignability.invalid", field); isValid = false; } } diff --git a/src/main/java/pico/typecheck/PICOVisitor.java b/src/main/java/pico/typecheck/PICOVisitor.java index ea7b0a4..494d607 100644 --- a/src/main/java/pico/typecheck/PICOVisitor.java +++ b/src/main/java/pico/typecheck/PICOVisitor.java @@ -193,14 +193,13 @@ protected void checkConstructorInvocation(AnnotatedDeclaredType invocation, Anno // declAnno = declAnno == null ? MUTABLE : declAnno; // // if(useAnno != null && !AnnotationUtils.areSameByName(declAnno, POLY_MUTABLE) && !isAdaptedSubtype(useAnno, declAnno)) { -// checker.report(Result.failure("type.invalid.annotations.on.use", declAnno, useAnno), newClassTree); +// checker.reportError(newClassTree, "type.invalid.annotations.on.use", declAnno, useAnno); // } // The immutability return qualifier of the constructor (returnType) must be supertype of the // constructor invocation immutability qualifier(invocation). if (!atypeFactory.getTypeHierarchy().isSubtype(invocation, returnType, READONLY)) { - checker.report(Result.failure( - "constructor.invocation.invalid", invocation, returnType), newClassTree); + checker.reportError(newClassTree, "constructor.invocation.invalid", invocation, returnType); } } @@ -212,7 +211,7 @@ public Void visitMethod(MethodTree node, Void p) { if (TreeUtils.isConstructor(node)) { AnnotatedDeclaredType constructorReturnType = (AnnotatedDeclaredType) executableType.getReturnType(); if (constructorReturnType.hasAnnotation(READONLY) || constructorReturnType.hasAnnotation(POLY_MUTABLE)) { - checker.report(Result.failure("constructor.return.invalid", constructorReturnType), node); + checker.reportError(node, "constructor.return.invalid", constructorReturnType); return super.visitMethod(node, p); } // if no explicit anno it must inherit from class decl so identical @@ -230,7 +229,7 @@ public Void visitMethod(MethodTree node, Void p) { // Below three are allowed on declared receiver types of instance methods in either @Mutable class or @Immutable class && !declareReceiverType.hasAnnotation(READONLY) && !declareReceiverType.hasAnnotation(POLY_MUTABLE)) { - checker.report(Result.failure("method.receiver.incompatible", declareReceiverType), node); + checker.reportError(node,"method.receiver.incompatible", declareReceiverType); } } } @@ -356,11 +355,11 @@ private boolean isInitializingReceiverDependantMutableOrImmutableObject(Annotate private void reportFieldOrArrayWriteError(Tree node, ExpressionTree variable, AnnotatedTypeMirror receiverType) { if (variable.getKind() == Kind.MEMBER_SELECT) { - checker.report(Result.failure("illegal.field.write", receiverType), TreeUtils.getReceiverTree(variable)); + checker.reportError(TreeUtils.getReceiverTree(variable), "illegal.field.write", receiverType); } else if (variable.getKind() == Kind.IDENTIFIER) { - checker.report(Result.failure("illegal.field.write", receiverType), node); + checker.reportError(node, "illegal.field.write", receiverType); } else if (variable.getKind() == Kind.ARRAY_ACCESS) { - checker.report(Result.failure("illegal.array.write", receiverType), ((ArrayAccessTree)variable).getExpression()); + checker.reportError(((ArrayAccessTree)variable).getExpression(), "illegal.array.write", receiverType); } else { throw new BugInCF("Unknown assignment variable at: ", node); } @@ -372,7 +371,7 @@ public Void visitVariable(VariableTree node, Void p) { AnnotatedTypeMirror type = atypeFactory.getAnnotatedType(element); if (element.getKind() == ElementKind.FIELD) { if (type.hasAnnotation(POLY_MUTABLE)) { - checker.report(Result.failure("field.polymutable.forbidden", element), node); + checker.reportError(node, "field.polymutable.forbidden", element); } } // TODO use base cf check methods @@ -394,7 +393,7 @@ private void checkAndReportInvalidAnnotationOnUse(AnnotatedTypeMirror type, Tree } } if (!isAdaptedSubtype(useAnno, defaultAnno)) { - checker.report(Result.failure("type.invalid.annotations.on.use", defaultAnno, useAnno), node); + checker.reportError(node, "type.invalid.annotations.on.use", defaultAnno, useAnno); } } } @@ -416,7 +415,7 @@ private void checkNewInstanceCreation(Tree node) { AnnotatedTypeMirror type = atypeFactory.getAnnotatedType(node); if (!(type.hasAnnotation(IMMUTABLE) || type.hasAnnotation(MUTABLE) || type.hasAnnotation(RECEIVER_DEPENDANT_MUTABLE) || type.hasAnnotation(POLY_MUTABLE))) { - checker.report(Result.failure("pico.new.invalid", type), node); + checker.reportError(node, "pico.new.invalid", type); } } @@ -492,7 +491,7 @@ public void processClassTree(ClassTree node) { AnnotatedDeclaredType bound = PICOTypeUtil.getBoundTypeOfTypeDeclaration(typeElement, atypeFactory); // Has to be either @Mutable, @ReceiverDependantMutable or @Immutable, nothing else if (!bound.hasAnnotation(MUTABLE) && !bound.hasAnnotation(RECEIVER_DEPENDANT_MUTABLE) && !bound.hasAnnotation(IMMUTABLE)) { - checker.report(Result.failure("class.bound.invalid", bound), node); + checker.reportError(node, "class.bound.invalid", bound); return;// Doesn't process the class tree anymore } @@ -519,9 +518,7 @@ protected void checkThisOrSuperConstructorCall( if (!atypeFactory .getQualifierHierarchy() .isSubtype(constructorTypeMirror, superTypeMirror)) { - checker.report( - Result.failure(errorKey, constructorTypeMirror, superCall, superTypeMirror), - superCall); + checker.reportError(superCall, errorKey, constructorTypeMirror, superCall, superTypeMirror); } super.checkThisOrSuperConstructorCall(superCall, errorKey); } From 42cc2976b0d9d8bd855efb40bf106ed12d0e37bd Mon Sep 17 00:00:00 2001 From: lnsun <57457122+lnsun@users.noreply.github.com> Date: Fri, 8 May 2020 17:20:19 -0400 Subject: [PATCH 055/144] API update: remove @DefaultInUncheckedCodeFor --- src/main/java/qual/Bottom.java | 1 - src/main/java/qual/Readonly.java | 3 --- 2 files changed, 4 deletions(-) diff --git a/src/main/java/qual/Bottom.java b/src/main/java/qual/Bottom.java index 8155e5b..89ad95f 100644 --- a/src/main/java/qual/Bottom.java +++ b/src/main/java/qual/Bottom.java @@ -18,5 +18,4 @@ // qualifier than @Bottom explicitly on explicit lower bound to have different-than-default type. @Target({}) @TargetLocations({}) -@DefaultInUncheckedCodeFor({TypeUseLocation.RETURN}) public @interface Bottom {} diff --git a/src/main/java/qual/Readonly.java b/src/main/java/qual/Readonly.java index 17ce4b5..73c7e06 100644 --- a/src/main/java/qual/Readonly.java +++ b/src/main/java/qual/Readonly.java @@ -1,8 +1,6 @@ package qual; -import org.checkerframework.framework.qual.DefaultInUncheckedCodeFor; import org.checkerframework.framework.qual.SubtypeOf; -import org.checkerframework.framework.qual.TypeUseLocation; import java.lang.annotation.Documented; import java.lang.annotation.ElementType; @@ -14,5 +12,4 @@ @Documented @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.TYPE_USE, ElementType.TYPE_PARAMETER}) -@DefaultInUncheckedCodeFor({TypeUseLocation.PARAMETER, TypeUseLocation.RECEIVER}) public @interface Readonly {} From 765fc99dea00ac70f571139393468e7dc30dc225 Mon Sep 17 00:00:00 2001 From: lnsun <57457122+lnsun@users.noreply.github.com> Date: Tue, 12 May 2020 15:54:31 -0400 Subject: [PATCH 056/144] remove @SPM --- .../typecheck/PICOAnnotatedTypeFactory.java | 47 +++++++++---------- .../typecheck/PICOAnnotationMirrorHolder.java | 3 -- .../pico/typecheck/PICOViewpointAdapter.java | 3 +- src/main/java/qual/Bottom.java | 2 +- src/main/java/qual/PolyMutable.java | 4 +- .../java/qual/SubstitutablePolyMutable.java | 15 ------ testinput/typecheck/DateCell2.java | 2 +- 7 files changed, 27 insertions(+), 49 deletions(-) delete mode 100644 src/main/java/qual/SubstitutablePolyMutable.java diff --git a/src/main/java/pico/typecheck/PICOAnnotatedTypeFactory.java b/src/main/java/pico/typecheck/PICOAnnotatedTypeFactory.java index bedd92b..09766b6 100644 --- a/src/main/java/pico/typecheck/PICOAnnotatedTypeFactory.java +++ b/src/main/java/pico/typecheck/PICOAnnotatedTypeFactory.java @@ -13,7 +13,6 @@ import javax.lang.model.element.AnnotationMirror; import javax.lang.model.element.Element; import javax.lang.model.element.ElementKind; -import javax.lang.model.element.ExecutableElement; import javax.lang.model.element.VariableElement; import javax.lang.model.type.DeclaredType; import javax.lang.model.type.TypeKind; @@ -64,7 +63,6 @@ import qual.PolyMutable; import qual.Readonly; import qual.ReceiverDependantMutable; -import qual.SubstitutablePolyMutable; import static pico.typecheck.PICOAnnotationMirrorHolder.*; @@ -95,7 +93,6 @@ protected Set> createSupportedTypeQualifiers() { Mutable.class, PolyMutable.class, ReceiverDependantMutable.class, - SubstitutablePolyMutable.class, Immutable.class, Bottom.class, Initialized.class, @@ -226,28 +223,28 @@ public AnnotatedTypeMirror getAnnotatedTypeLhs(Tree lhsTree) { return result; } - /**Handles invoking static methods with polymutable on its declaration*/ - @Override - public ParameterizedExecutableType methodFromUse(ExpressionTree tree, ExecutableElement methodElt, AnnotatedTypeMirror receiverType) { - ParameterizedExecutableType pair = super.methodFromUse(tree, methodElt, receiverType); - // We want to replace polymutable with substitutablepolymutable when we invoke static methods - if (ElementUtils.isStatic(methodElt)) { - AnnotatedExecutableType methodType = pair.executableType; - AnnotatedTypeMirror returnType = methodType.getReturnType(); - if (returnType.hasAnnotation(POLY_MUTABLE)) { - // Only substitute polymutable but not other qualifiers! Missing the if statement - // caused bugs before! - returnType.replaceAnnotation(SUBSTITUTABLE_POLY_MUTABLE); - } - List parameterTypes = methodType.getParameterTypes(); - for (AnnotatedTypeMirror p : parameterTypes) { - if (returnType.hasAnnotation(POLY_MUTABLE)) { - p.replaceAnnotation(SUBSTITUTABLE_POLY_MUTABLE); - } - } - } - return pair; - } +// /**Handles invoking static methods with polymutable on its declaration*/ +// @Override +// public ParameterizedExecutableType methodFromUse(ExpressionTree tree, ExecutableElement methodElt, AnnotatedTypeMirror receiverType) { +// ParameterizedExecutableType pair = super.methodFromUse(tree, methodElt, receiverType); +// // We want to replace polymutable with substitutablepolymutable when we invoke static methods +// if (ElementUtils.isStatic(methodElt)) { +// AnnotatedExecutableType methodType = pair.executableType; +// AnnotatedTypeMirror returnType = methodType.getReturnType(); +// if (returnType.hasAnnotation(POLY_MUTABLE)) { +// // Only substitute polymutable but not other qualifiers! Missing the if statement +// // caused bugs before! +// returnType.replaceAnnotation(SUBSTITUTABLE_POLY_MUTABLE); +// } +// List parameterTypes = methodType.getParameterTypes(); +// for (AnnotatedTypeMirror p : parameterTypes) { +// if (returnType.hasAnnotation(POLY_MUTABLE)) { +// p.replaceAnnotation(SUBSTITUTABLE_POLY_MUTABLE); +// } +// } +// } +// return pair; +// } protected class PICOQualifierHierarchy extends InitializationQualifierHierarchy { diff --git a/src/main/java/pico/typecheck/PICOAnnotationMirrorHolder.java b/src/main/java/pico/typecheck/PICOAnnotationMirrorHolder.java index e1ef081..478624c 100644 --- a/src/main/java/pico/typecheck/PICOAnnotationMirrorHolder.java +++ b/src/main/java/pico/typecheck/PICOAnnotationMirrorHolder.java @@ -9,7 +9,6 @@ import qual.PolyMutable; import qual.Readonly; import qual.ReceiverDependantMutable; -import qual.SubstitutablePolyMutable; import javax.lang.model.element.AnnotationMirror; import javax.lang.model.util.Elements; @@ -23,7 +22,6 @@ public class PICOAnnotationMirrorHolder { public static AnnotationMirror MUTABLE; public static AnnotationMirror POLY_MUTABLE; public static AnnotationMirror RECEIVER_DEPENDANT_MUTABLE; - public static AnnotationMirror SUBSTITUTABLE_POLY_MUTABLE; public static AnnotationMirror IMMUTABLE; public static AnnotationMirror BOTTOM; public static AnnotationMirror COMMITED; @@ -34,7 +32,6 @@ public static void init(SourceChecker checker) { MUTABLE = AnnotationBuilder.fromClass(elements, Mutable.class); POLY_MUTABLE = AnnotationBuilder.fromClass(elements, PolyMutable.class); RECEIVER_DEPENDANT_MUTABLE = AnnotationBuilder.fromClass(elements, ReceiverDependantMutable.class); - SUBSTITUTABLE_POLY_MUTABLE = AnnotationBuilder.fromClass(elements, SubstitutablePolyMutable.class); IMMUTABLE = AnnotationBuilder.fromClass(elements, Immutable.class); BOTTOM = AnnotationBuilder.fromClass(elements, Bottom.class); diff --git a/src/main/java/pico/typecheck/PICOViewpointAdapter.java b/src/main/java/pico/typecheck/PICOViewpointAdapter.java index 4eb82cd..027a8f6 100644 --- a/src/main/java/pico/typecheck/PICOViewpointAdapter.java +++ b/src/main/java/pico/typecheck/PICOViewpointAdapter.java @@ -6,7 +6,6 @@ import static pico.typecheck.PICOAnnotationMirrorHolder.POLY_MUTABLE; import static pico.typecheck.PICOAnnotationMirrorHolder.READONLY; import static pico.typecheck.PICOAnnotationMirrorHolder.RECEIVER_DEPENDANT_MUTABLE; -import static pico.typecheck.PICOAnnotationMirrorHolder.SUBSTITUTABLE_POLY_MUTABLE; import javax.lang.model.element.AnnotationMirror; import javax.lang.model.element.Element; @@ -54,7 +53,7 @@ protected AnnotationMirror combineAnnotationWithAnnotation(AnnotationMirror rece } else if (AnnotationUtils.areSame(declaredAnnotation, BOTTOM)) { return BOTTOM; } else if (AnnotationUtils.areSame(declaredAnnotation, POLY_MUTABLE)) { - return SUBSTITUTABLE_POLY_MUTABLE; + return POLY_MUTABLE; } else if (AnnotationUtils.areSame(declaredAnnotation, RECEIVER_DEPENDANT_MUTABLE)) { return receiverAnnotation; } else { diff --git a/src/main/java/qual/Bottom.java b/src/main/java/qual/Bottom.java index 89ad95f..9fbbdf7 100644 --- a/src/main/java/qual/Bottom.java +++ b/src/main/java/qual/Bottom.java @@ -7,7 +7,7 @@ import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; -@SubtypeOf({Mutable.class, Immutable.class, PolyMutable.class, ReceiverDependantMutable.class}) +@SubtypeOf({Mutable.class, Immutable.class, ReceiverDependantMutable.class}) @DefaultFor(value = { TypeUseLocation.LOWER_BOUND }, typeKinds = {TypeKind.NULL}) @Documented @Retention(RetentionPolicy.RUNTIME) diff --git a/src/main/java/qual/PolyMutable.java b/src/main/java/qual/PolyMutable.java index 54f45ca..48cc878 100644 --- a/src/main/java/qual/PolyMutable.java +++ b/src/main/java/qual/PolyMutable.java @@ -1,6 +1,6 @@ package qual; -import org.checkerframework.framework.qual.SubtypeOf; +import org.checkerframework.framework.qual.PolymorphicQualifier; import java.lang.annotation.Documented; import java.lang.annotation.ElementType; @@ -8,8 +8,8 @@ import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; -@SubtypeOf(Readonly.class) @Documented @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.TYPE_USE, ElementType.TYPE_PARAMETER}) +@PolymorphicQualifier(Readonly.class) public @interface PolyMutable {} diff --git a/src/main/java/qual/SubstitutablePolyMutable.java b/src/main/java/qual/SubstitutablePolyMutable.java deleted file mode 100644 index 6620c5c..0000000 --- a/src/main/java/qual/SubstitutablePolyMutable.java +++ /dev/null @@ -1,15 +0,0 @@ -package qual; - -import org.checkerframework.framework.qual.PolymorphicQualifier; - -import java.lang.annotation.Documented; -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -@PolymorphicQualifier(Readonly.class) -@Documented -@Retention(RetentionPolicy.RUNTIME) -@Target({}) -public @interface SubstitutablePolyMutable {} diff --git a/testinput/typecheck/DateCell2.java b/testinput/typecheck/DateCell2.java index 2bb5985..0c002b6 100644 --- a/testinput/typecheck/DateCell2.java +++ b/testinput/typecheck/DateCell2.java @@ -22,6 +22,6 @@ void test1(@Mutable DateCell2 this) { void test2(@Immutable DateCell2 this) { @Immutable DateCell2 waht = new @Immutable DateCell2(); - @Immutable Date imd = this.getImmutableDate(); + @Immutable Date imd = this(); } } From 54dc0dee1174984e731aef68f0d3a67256063430 Mon Sep 17 00:00:00 2001 From: lnsun <57457122+lnsun@users.noreply.github.com> Date: Wed, 13 May 2020 17:09:16 -0400 Subject: [PATCH 057/144] Revert "remove @SPM" This reverts commit fe280a96 --- .../typecheck/PICOAnnotatedTypeFactory.java | 47 ++++++++++--------- .../typecheck/PICOAnnotationMirrorHolder.java | 3 ++ .../pico/typecheck/PICOViewpointAdapter.java | 3 +- src/main/java/qual/Bottom.java | 2 +- src/main/java/qual/PolyMutable.java | 4 +- .../java/qual/SubstitutablePolyMutable.java | 15 ++++++ testinput/typecheck/DateCell2.java | 2 +- 7 files changed, 49 insertions(+), 27 deletions(-) create mode 100644 src/main/java/qual/SubstitutablePolyMutable.java diff --git a/src/main/java/pico/typecheck/PICOAnnotatedTypeFactory.java b/src/main/java/pico/typecheck/PICOAnnotatedTypeFactory.java index 09766b6..bedd92b 100644 --- a/src/main/java/pico/typecheck/PICOAnnotatedTypeFactory.java +++ b/src/main/java/pico/typecheck/PICOAnnotatedTypeFactory.java @@ -13,6 +13,7 @@ import javax.lang.model.element.AnnotationMirror; import javax.lang.model.element.Element; import javax.lang.model.element.ElementKind; +import javax.lang.model.element.ExecutableElement; import javax.lang.model.element.VariableElement; import javax.lang.model.type.DeclaredType; import javax.lang.model.type.TypeKind; @@ -63,6 +64,7 @@ import qual.PolyMutable; import qual.Readonly; import qual.ReceiverDependantMutable; +import qual.SubstitutablePolyMutable; import static pico.typecheck.PICOAnnotationMirrorHolder.*; @@ -93,6 +95,7 @@ protected Set> createSupportedTypeQualifiers() { Mutable.class, PolyMutable.class, ReceiverDependantMutable.class, + SubstitutablePolyMutable.class, Immutable.class, Bottom.class, Initialized.class, @@ -223,28 +226,28 @@ public AnnotatedTypeMirror getAnnotatedTypeLhs(Tree lhsTree) { return result; } -// /**Handles invoking static methods with polymutable on its declaration*/ -// @Override -// public ParameterizedExecutableType methodFromUse(ExpressionTree tree, ExecutableElement methodElt, AnnotatedTypeMirror receiverType) { -// ParameterizedExecutableType pair = super.methodFromUse(tree, methodElt, receiverType); -// // We want to replace polymutable with substitutablepolymutable when we invoke static methods -// if (ElementUtils.isStatic(methodElt)) { -// AnnotatedExecutableType methodType = pair.executableType; -// AnnotatedTypeMirror returnType = methodType.getReturnType(); -// if (returnType.hasAnnotation(POLY_MUTABLE)) { -// // Only substitute polymutable but not other qualifiers! Missing the if statement -// // caused bugs before! -// returnType.replaceAnnotation(SUBSTITUTABLE_POLY_MUTABLE); -// } -// List parameterTypes = methodType.getParameterTypes(); -// for (AnnotatedTypeMirror p : parameterTypes) { -// if (returnType.hasAnnotation(POLY_MUTABLE)) { -// p.replaceAnnotation(SUBSTITUTABLE_POLY_MUTABLE); -// } -// } -// } -// return pair; -// } + /**Handles invoking static methods with polymutable on its declaration*/ + @Override + public ParameterizedExecutableType methodFromUse(ExpressionTree tree, ExecutableElement methodElt, AnnotatedTypeMirror receiverType) { + ParameterizedExecutableType pair = super.methodFromUse(tree, methodElt, receiverType); + // We want to replace polymutable with substitutablepolymutable when we invoke static methods + if (ElementUtils.isStatic(methodElt)) { + AnnotatedExecutableType methodType = pair.executableType; + AnnotatedTypeMirror returnType = methodType.getReturnType(); + if (returnType.hasAnnotation(POLY_MUTABLE)) { + // Only substitute polymutable but not other qualifiers! Missing the if statement + // caused bugs before! + returnType.replaceAnnotation(SUBSTITUTABLE_POLY_MUTABLE); + } + List parameterTypes = methodType.getParameterTypes(); + for (AnnotatedTypeMirror p : parameterTypes) { + if (returnType.hasAnnotation(POLY_MUTABLE)) { + p.replaceAnnotation(SUBSTITUTABLE_POLY_MUTABLE); + } + } + } + return pair; + } protected class PICOQualifierHierarchy extends InitializationQualifierHierarchy { diff --git a/src/main/java/pico/typecheck/PICOAnnotationMirrorHolder.java b/src/main/java/pico/typecheck/PICOAnnotationMirrorHolder.java index 478624c..e1ef081 100644 --- a/src/main/java/pico/typecheck/PICOAnnotationMirrorHolder.java +++ b/src/main/java/pico/typecheck/PICOAnnotationMirrorHolder.java @@ -9,6 +9,7 @@ import qual.PolyMutable; import qual.Readonly; import qual.ReceiverDependantMutable; +import qual.SubstitutablePolyMutable; import javax.lang.model.element.AnnotationMirror; import javax.lang.model.util.Elements; @@ -22,6 +23,7 @@ public class PICOAnnotationMirrorHolder { public static AnnotationMirror MUTABLE; public static AnnotationMirror POLY_MUTABLE; public static AnnotationMirror RECEIVER_DEPENDANT_MUTABLE; + public static AnnotationMirror SUBSTITUTABLE_POLY_MUTABLE; public static AnnotationMirror IMMUTABLE; public static AnnotationMirror BOTTOM; public static AnnotationMirror COMMITED; @@ -32,6 +34,7 @@ public static void init(SourceChecker checker) { MUTABLE = AnnotationBuilder.fromClass(elements, Mutable.class); POLY_MUTABLE = AnnotationBuilder.fromClass(elements, PolyMutable.class); RECEIVER_DEPENDANT_MUTABLE = AnnotationBuilder.fromClass(elements, ReceiverDependantMutable.class); + SUBSTITUTABLE_POLY_MUTABLE = AnnotationBuilder.fromClass(elements, SubstitutablePolyMutable.class); IMMUTABLE = AnnotationBuilder.fromClass(elements, Immutable.class); BOTTOM = AnnotationBuilder.fromClass(elements, Bottom.class); diff --git a/src/main/java/pico/typecheck/PICOViewpointAdapter.java b/src/main/java/pico/typecheck/PICOViewpointAdapter.java index 027a8f6..4eb82cd 100644 --- a/src/main/java/pico/typecheck/PICOViewpointAdapter.java +++ b/src/main/java/pico/typecheck/PICOViewpointAdapter.java @@ -6,6 +6,7 @@ import static pico.typecheck.PICOAnnotationMirrorHolder.POLY_MUTABLE; import static pico.typecheck.PICOAnnotationMirrorHolder.READONLY; import static pico.typecheck.PICOAnnotationMirrorHolder.RECEIVER_DEPENDANT_MUTABLE; +import static pico.typecheck.PICOAnnotationMirrorHolder.SUBSTITUTABLE_POLY_MUTABLE; import javax.lang.model.element.AnnotationMirror; import javax.lang.model.element.Element; @@ -53,7 +54,7 @@ protected AnnotationMirror combineAnnotationWithAnnotation(AnnotationMirror rece } else if (AnnotationUtils.areSame(declaredAnnotation, BOTTOM)) { return BOTTOM; } else if (AnnotationUtils.areSame(declaredAnnotation, POLY_MUTABLE)) { - return POLY_MUTABLE; + return SUBSTITUTABLE_POLY_MUTABLE; } else if (AnnotationUtils.areSame(declaredAnnotation, RECEIVER_DEPENDANT_MUTABLE)) { return receiverAnnotation; } else { diff --git a/src/main/java/qual/Bottom.java b/src/main/java/qual/Bottom.java index 9fbbdf7..89ad95f 100644 --- a/src/main/java/qual/Bottom.java +++ b/src/main/java/qual/Bottom.java @@ -7,7 +7,7 @@ import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; -@SubtypeOf({Mutable.class, Immutable.class, ReceiverDependantMutable.class}) +@SubtypeOf({Mutable.class, Immutable.class, PolyMutable.class, ReceiverDependantMutable.class}) @DefaultFor(value = { TypeUseLocation.LOWER_BOUND }, typeKinds = {TypeKind.NULL}) @Documented @Retention(RetentionPolicy.RUNTIME) diff --git a/src/main/java/qual/PolyMutable.java b/src/main/java/qual/PolyMutable.java index 48cc878..54f45ca 100644 --- a/src/main/java/qual/PolyMutable.java +++ b/src/main/java/qual/PolyMutable.java @@ -1,6 +1,6 @@ package qual; -import org.checkerframework.framework.qual.PolymorphicQualifier; +import org.checkerframework.framework.qual.SubtypeOf; import java.lang.annotation.Documented; import java.lang.annotation.ElementType; @@ -8,8 +8,8 @@ import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; +@SubtypeOf(Readonly.class) @Documented @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.TYPE_USE, ElementType.TYPE_PARAMETER}) -@PolymorphicQualifier(Readonly.class) public @interface PolyMutable {} diff --git a/src/main/java/qual/SubstitutablePolyMutable.java b/src/main/java/qual/SubstitutablePolyMutable.java new file mode 100644 index 0000000..6620c5c --- /dev/null +++ b/src/main/java/qual/SubstitutablePolyMutable.java @@ -0,0 +1,15 @@ +package qual; + +import org.checkerframework.framework.qual.PolymorphicQualifier; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@PolymorphicQualifier(Readonly.class) +@Documented +@Retention(RetentionPolicy.RUNTIME) +@Target({}) +public @interface SubstitutablePolyMutable {} diff --git a/testinput/typecheck/DateCell2.java b/testinput/typecheck/DateCell2.java index 0c002b6..2bb5985 100644 --- a/testinput/typecheck/DateCell2.java +++ b/testinput/typecheck/DateCell2.java @@ -22,6 +22,6 @@ void test1(@Mutable DateCell2 this) { void test2(@Immutable DateCell2 this) { @Immutable DateCell2 waht = new @Immutable DateCell2(); - @Immutable Date imd = this(); + @Immutable Date imd = this.getImmutableDate(); } } From 9c31c3540521a04cf465c9aeab9bce10d69c8b2a Mon Sep 17 00:00:00 2001 From: lnsun <57457122+lnsun@users.noreply.github.com> Date: Mon, 18 May 2020 17:15:09 -0400 Subject: [PATCH 058/144] @SPM for static moved to new api --- .../typecheck/PICOAnnotatedTypeFactory.java | 59 ++++++++++++++----- 1 file changed, 43 insertions(+), 16 deletions(-) diff --git a/src/main/java/pico/typecheck/PICOAnnotatedTypeFactory.java b/src/main/java/pico/typecheck/PICOAnnotatedTypeFactory.java index bedd92b..db0cdd1 100644 --- a/src/main/java/pico/typecheck/PICOAnnotatedTypeFactory.java +++ b/src/main/java/pico/typecheck/PICOAnnotatedTypeFactory.java @@ -20,6 +20,7 @@ import javax.lang.model.type.TypeMirror; import com.sun.source.tree.IdentifierTree; +import com.sun.source.tree.MethodInvocationTree; import com.sun.source.tree.ParameterizedTypeTree; import com.sun.source.util.TreePath; import org.checkerframework.checker.initialization.InitializationAnnotatedTypeFactory; @@ -226,27 +227,53 @@ public AnnotatedTypeMirror getAnnotatedTypeLhs(Tree lhsTree) { return result; } - /**Handles invoking static methods with polymutable on its declaration*/ +// /**Handles invoking static methods with polymutable on its declaration*/ +// @Override +// public ParameterizedExecutableType methodFromUse(ExpressionTree tree, ExecutableElement methodElt, AnnotatedTypeMirror receiverType) { +// ParameterizedExecutableType pair = super.methodFromUse(tree, methodElt, receiverType); +// // We want to replace polymutable with substitutablepolymutable when we invoke static methods +// if (ElementUtils.isStatic(methodElt)) { +// AnnotatedExecutableType methodType = pair.executableType; +// AnnotatedTypeMirror returnType = methodType.getReturnType(); +// if (returnType.hasAnnotation(POLY_MUTABLE)) { +// // Only substitute polymutable but not other qualifiers! Missing the if statement +// // caused bugs before! +// returnType.replaceAnnotation(SUBSTITUTABLE_POLY_MUTABLE); +// } +// List parameterTypes = methodType.getParameterTypes(); +// for (AnnotatedTypeMirror p : parameterTypes) { +// if (returnType.hasAnnotation(POLY_MUTABLE)) { +// p.replaceAnnotation(SUBSTITUTABLE_POLY_MUTABLE); +// } +// } +// } +// return pair; +// } + @Override - public ParameterizedExecutableType methodFromUse(ExpressionTree tree, ExecutableElement methodElt, AnnotatedTypeMirror receiverType) { - ParameterizedExecutableType pair = super.methodFromUse(tree, methodElt, receiverType); - // We want to replace polymutable with substitutablepolymutable when we invoke static methods - if (ElementUtils.isStatic(methodElt)) { - AnnotatedExecutableType methodType = pair.executableType; - AnnotatedTypeMirror returnType = methodType.getReturnType(); - if (returnType.hasAnnotation(POLY_MUTABLE)) { - // Only substitute polymutable but not other qualifiers! Missing the if statement - // caused bugs before! - returnType.replaceAnnotation(SUBSTITUTABLE_POLY_MUTABLE); - } - List parameterTypes = methodType.getParameterTypes(); - for (AnnotatedTypeMirror p : parameterTypes) { + public void methodFromUsePreSubstitution(ExpressionTree tree, AnnotatedExecutableType type) { + if (tree instanceof MethodInvocationTree) { + MethodInvocationTree methodInvocationTree = (MethodInvocationTree) tree; + ExecutableElement methodElt = TreeUtils.elementFromUse(methodInvocationTree); + + if (ElementUtils.isStatic(methodElt)) { + AnnotatedExecutableType methodType = type; + AnnotatedTypeMirror returnType = methodType.getReturnType(); if (returnType.hasAnnotation(POLY_MUTABLE)) { - p.replaceAnnotation(SUBSTITUTABLE_POLY_MUTABLE); + // Only substitute polymutable but not other qualifiers! Missing the if statement + // caused bugs before! + returnType.replaceAnnotation(SUBSTITUTABLE_POLY_MUTABLE); + } + List parameterTypes = methodType.getParameterTypes(); + for (AnnotatedTypeMirror p : parameterTypes) { + if (returnType.hasAnnotation(POLY_MUTABLE)) { + p.replaceAnnotation(SUBSTITUTABLE_POLY_MUTABLE); + } } } } - return pair; + + super.methodFromUsePreSubstitution(tree, type); } protected class PICOQualifierHierarchy extends InitializationQualifierHierarchy { From adf2c176bce61bfc0536f080bd2a331cf416ae6f Mon Sep 17 00:00:00 2001 From: lnsun <57457122+lnsun@users.noreply.github.com> Date: Mon, 18 May 2020 17:46:08 -0400 Subject: [PATCH 059/144] exclude decl when checking static RDM (TC: 100%, INF: 6 fail) --- src/main/java/pico/typecheck/PICOValidator.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/java/pico/typecheck/PICOValidator.java b/src/main/java/pico/typecheck/PICOValidator.java index c5ad05b..e8930f3 100644 --- a/src/main/java/pico/typecheck/PICOValidator.java +++ b/src/main/java/pico/typecheck/PICOValidator.java @@ -63,7 +63,8 @@ public Void visitPrimitive(AnnotatedPrimitiveType type, Tree tree) { } private void checkStaticReceiverDependantMutableError(AnnotatedTypeMirror type, Tree tree) { - if (PICOTypeUtil.inStaticScope(visitor.getCurrentPath()) + if (!type.isDeclaration() // variables in static contexts and static fields use class decl as enclosing type + && PICOTypeUtil.inStaticScope(visitor.getCurrentPath()) && !"".contentEquals(TreeUtils.enclosingClass(visitor.getCurrentPath()).getSimpleName())// Exclude @RDM usages in anonymous classes && type.hasAnnotation(RECEIVER_DEPENDANT_MUTABLE)) { reportValidityResult("static.receiverdependantmutable.forbidden", type, tree); From 2e2ffd093faecb555e1d5a4103e384d135bebe19 Mon Sep 17 00:00:00 2001 From: lnsun <57457122+lnsun@users.noreply.github.com> Date: Mon, 18 May 2020 17:57:47 -0400 Subject: [PATCH 060/144] small tweaks --- src/main/java/pico/typecheck/PICOValidator.java | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/main/java/pico/typecheck/PICOValidator.java b/src/main/java/pico/typecheck/PICOValidator.java index e8930f3..3f3080c 100644 --- a/src/main/java/pico/typecheck/PICOValidator.java +++ b/src/main/java/pico/typecheck/PICOValidator.java @@ -6,7 +6,6 @@ import org.checkerframework.common.basetype.BaseTypeChecker; import org.checkerframework.common.basetype.BaseTypeValidator; import org.checkerframework.common.basetype.BaseTypeVisitor; -import org.checkerframework.framework.source.Result; import org.checkerframework.framework.type.AnnotatedTypeFactory; import org.checkerframework.framework.type.AnnotatedTypeMirror; import org.checkerframework.framework.type.AnnotatedTypeMirror.AnnotatedArrayType; @@ -17,6 +16,8 @@ import javax.lang.model.element.VariableElement; +import java.util.Objects; + import static pico.typecheck.PICOAnnotationMirrorHolder.BOTTOM; import static pico.typecheck.PICOAnnotationMirrorHolder.IMMUTABLE; import static pico.typecheck.PICOAnnotationMirrorHolder.RECEIVER_DEPENDANT_MUTABLE; @@ -63,9 +64,9 @@ public Void visitPrimitive(AnnotatedPrimitiveType type, Tree tree) { } private void checkStaticReceiverDependantMutableError(AnnotatedTypeMirror type, Tree tree) { - if (!type.isDeclaration() // variables in static contexts and static fields use class decl as enclosing type + if (!type.isDeclaration() // variables in static contexts and static fields use class decl as enclosing type && PICOTypeUtil.inStaticScope(visitor.getCurrentPath()) - && !"".contentEquals(TreeUtils.enclosingClass(visitor.getCurrentPath()).getSimpleName())// Exclude @RDM usages in anonymous classes + && !"".contentEquals(Objects.requireNonNull(TreeUtils.enclosingClass(visitor.getCurrentPath())).getSimpleName()) // Exclude @RDM usages in anonymous classes && type.hasAnnotation(RECEIVER_DEPENDANT_MUTABLE)) { reportValidityResult("static.receiverdependantmutable.forbidden", type, tree); } From 6277e02b875759c607970cd1d4a3defae9166276 Mon Sep 17 00:00:00 2001 From: lnsun <57457122+lnsun@users.noreply.github.com> Date: Mon, 18 May 2020 22:03:14 -0400 Subject: [PATCH 061/144] tc cast.unsafe (TC: 100%, INF: 4 fail) --- .../typecheck/PICOAnnotatedTypeFactory.java | 14 +++++----- src/main/java/pico/typecheck/PICOVisitor.java | 26 ++++++++++++++++++- testinput/typecheck/CopyToCast.java | 4 +-- .../typecheck/HashCodeSafetyExample.java | 2 +- testinput/typecheck/ObjectMethods.java | 2 +- testinput/typecheck/Primitive3.java | 2 +- 6 files changed, 37 insertions(+), 13 deletions(-) diff --git a/src/main/java/pico/typecheck/PICOAnnotatedTypeFactory.java b/src/main/java/pico/typecheck/PICOAnnotatedTypeFactory.java index db0cdd1..afeadff 100644 --- a/src/main/java/pico/typecheck/PICOAnnotatedTypeFactory.java +++ b/src/main/java/pico/typecheck/PICOAnnotatedTypeFactory.java @@ -150,7 +150,7 @@ protected TypeAnnotator createTypeAnnotator() { @Override public QualifierHierarchy createQualifierHierarchy(MultiGraphFactory factory) { - return new PICOQualifierHierarchy(factory, (Object[]) null); + return new PICOQualifierHierarchy(factory, null); } /**Just to transfer the method from super class to package*/ @@ -196,7 +196,7 @@ public AnnotatedTypeMirror getAnnotatedTypeLhs(Tree lhsTree) { boolean oldComputingAnnotatedTypeMirrorOfLHS = computingAnnotatedTypeMirrorOfLHS; computingAnnotatedTypeMirrorOfLHS = true; - AnnotatedTypeMirror result = null; + AnnotatedTypeMirror result; boolean oldShouldCache = shouldCache; // Don't cache the result because getAnnotatedType(lhsTree) could // be called from elsewhere and would expect flow-sensitive type refinements. @@ -256,15 +256,15 @@ public void methodFromUsePreSubstitution(ExpressionTree tree, AnnotatedExecutabl MethodInvocationTree methodInvocationTree = (MethodInvocationTree) tree; ExecutableElement methodElt = TreeUtils.elementFromUse(methodInvocationTree); + assert methodElt != null; if (ElementUtils.isStatic(methodElt)) { - AnnotatedExecutableType methodType = type; - AnnotatedTypeMirror returnType = methodType.getReturnType(); + AnnotatedTypeMirror returnType = type.getReturnType(); if (returnType.hasAnnotation(POLY_MUTABLE)) { // Only substitute polymutable but not other qualifiers! Missing the if statement // caused bugs before! returnType.replaceAnnotation(SUBSTITUTABLE_POLY_MUTABLE); } - List parameterTypes = methodType.getParameterTypes(); + List parameterTypes = type.getParameterTypes(); for (AnnotatedTypeMirror p : parameterTypes) { if (returnType.hasAnnotation(POLY_MUTABLE)) { p.replaceAnnotation(SUBSTITUTABLE_POLY_MUTABLE); @@ -411,7 +411,7 @@ public Void visitTypeCast(TypeCastTree node, AnnotatedTypeMirror type) { addMissingAnnotations because we want to respect existing annotation on type*/ private void applyImmutableIfImplicitlyImmutable(AnnotatedTypeMirror type) { if (PICOTypeUtil.isImplicitlyImmutableType(type)) { - type.addMissingAnnotations(new HashSet<>(Arrays.asList(IMMUTABLE))); + type.addMissingAnnotations(new HashSet<>(Collections.singletonList(IMMUTABLE))); } } @@ -450,7 +450,7 @@ private Set replaceAnnotationInHierarchy(Set return result; } - private AnnotationMirror getTypeDeclarationBoundForMutability(TypeMirror type) { + public AnnotationMirror getTypeDeclarationBoundForMutability(TypeMirror type) { // copied from inference real type factory with minor modification // TODO too awkward. maybe overload isImplicitlyImmutableType if (PICOTypeUtil.isImplicitlyImmutableType(toAnnotatedType(type, false))) { diff --git a/src/main/java/pico/typecheck/PICOVisitor.java b/src/main/java/pico/typecheck/PICOVisitor.java index 494d607..9f36bcf 100644 --- a/src/main/java/pico/typecheck/PICOVisitor.java +++ b/src/main/java/pico/typecheck/PICOVisitor.java @@ -27,11 +27,11 @@ import org.checkerframework.checker.initialization.qual.UnderInitialization; import org.checkerframework.common.basetype.BaseTypeChecker; import org.checkerframework.common.basetype.TypeValidator; -import org.checkerframework.framework.source.Result; import org.checkerframework.framework.type.AnnotatedTypeFactory.ParameterizedExecutableType; import org.checkerframework.framework.type.AnnotatedTypeMirror; import org.checkerframework.framework.type.AnnotatedTypeMirror.AnnotatedDeclaredType; import org.checkerframework.framework.type.AnnotatedTypeMirror.AnnotatedExecutableType; +import org.checkerframework.framework.type.QualifierHierarchy; import org.checkerframework.framework.util.AnnotatedTypes; import org.checkerframework.javacutil.AnnotationUtils; import org.checkerframework.javacutil.BugInCF; @@ -522,4 +522,28 @@ protected void checkThisOrSuperConstructorCall( } super.checkThisOrSuperConstructorCall(superCall, errorKey); } + + @Override + protected boolean isTypeCastSafe(AnnotatedTypeMirror castType, AnnotatedTypeMirror exprType) { + QualifierHierarchy qualifierHierarchy = atypeFactory.getQualifierHierarchy(); + + final TypeKind castTypeKind = castType.getKind(); + if (castTypeKind == TypeKind.DECLARED) { + // Don't issue an error if the mutability annotations are equivalent to the qualifier upper bound + // of the type. + // BaseTypeVisitor#isTypeCastSafe is not used, to be consistent with inference which only have mutability qualifiers + // if inference is supporting FBC in the future, this overridden method can be removed. + AnnotatedDeclaredType castDeclared = (AnnotatedDeclaredType) castType; + + AnnotationMirror bound = qualifierHierarchy.findAnnotationInHierarchy( + atypeFactory.getTypeDeclarationBounds(castDeclared.getUnderlyingType()), READONLY); + assert bound != null; + + if (AnnotationUtils.areSame(castDeclared.getAnnotationInHierarchy(READONLY), bound)) { + return true; + } + } + + return super.isTypeCastSafe(castType, exprType); + } } diff --git a/testinput/typecheck/CopyToCast.java b/testinput/typecheck/CopyToCast.java index ad77909..e1714b6 100644 --- a/testinput/typecheck/CopyToCast.java +++ b/testinput/typecheck/CopyToCast.java @@ -7,9 +7,9 @@ public class CopyToCast { void foo(Object o) { - // :: warning: (cast.unsafe) + // No cast.unsafe String s1 = (@Immutable String) o; - // :: warning: (cast.unsafe) + // No cast.unsafe String s2 = (String) o; // :: error: (type.invalid.annotations.on.use) String s3 = (@Mutable String) o; diff --git a/testinput/typecheck/HashCodeSafetyExample.java b/testinput/typecheck/HashCodeSafetyExample.java index 5486b56..bee5792 100644 --- a/testinput/typecheck/HashCodeSafetyExample.java +++ b/testinput/typecheck/HashCodeSafetyExample.java @@ -16,7 +16,7 @@ public int hashCode() { @Override public boolean equals(Object obj) { - // :: warning: (cast.unsafe) + // No cast.unsafe return isIn == ((A)obj).isIn; } } diff --git a/testinput/typecheck/ObjectMethods.java b/testinput/typecheck/ObjectMethods.java index 7b33d7d..08cfa3f 100644 --- a/testinput/typecheck/ObjectMethods.java +++ b/testinput/typecheck/ObjectMethods.java @@ -145,7 +145,7 @@ public boolean equals(@Readonly ObjectMethods6 this, @Readonly Object o) { @Override protected @ReceiverDependantMutable Object clone(@Readonly ObjectMethods6 this) throws CloneNotSupportedException { - // :: warning: (cast.unsafe) + // No cast.unsafe return (@ReceiverDependantMutable Object) super.clone(); } diff --git a/testinput/typecheck/Primitive3.java b/testinput/typecheck/Primitive3.java index 8781976..eacb90b 100644 --- a/testinput/typecheck/Primitive3.java +++ b/testinput/typecheck/Primitive3.java @@ -13,7 +13,7 @@ void foo(Word word) { // I reenable type cast safety checking when the cast type is implicitly immutable. // Why should we suppress warning just because cast type is implicitly immutable? // That doesn't make any sense. Am I right? - // :: warning: (cast.unsafe) + // No cast.unsafe params[0] = (String) word.get(0); } } From b90d5f98dfb120bac21e4c9ce459471bc5acb164 Mon Sep 17 00:00:00 2001 From: lnsun <57457122+lnsun@users.noreply.github.com> Date: Tue, 19 May 2020 11:05:56 -0400 Subject: [PATCH 062/144] inf-> workaround for anonymous class --- src/main/java/pico/typecheck/PICOVisitor.java | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/main/java/pico/typecheck/PICOVisitor.java b/src/main/java/pico/typecheck/PICOVisitor.java index 9f36bcf..0d019a8 100644 --- a/src/main/java/pico/typecheck/PICOVisitor.java +++ b/src/main/java/pico/typecheck/PICOVisitor.java @@ -546,4 +546,18 @@ protected boolean isTypeCastSafe(AnnotatedTypeMirror castType, AnnotatedTypeMirr return super.isTypeCastSafe(castType, exprType); } + + @Override + protected void commonAssignmentCheck(AnnotatedTypeMirror varType, + AnnotatedTypeMirror valueType, Tree valueTree, + String errorKey) { + // TODO: WORKAROUND: anonymous class handling + if (TypesUtils.isAnonymous(valueType.getUnderlyingType())) { + AnnotatedTypeMirror newValueType = varType.deepCopy(); + newValueType.replaceAnnotation(valueType.getAnnotationInHierarchy(READONLY)); + valueType = newValueType; + } + super.commonAssignmentCheck(varType, valueType, valueTree, errorKey); + + } } From 13555c6f5da5810035d7f394b8df7dedd937fee7 Mon Sep 17 00:00:00 2001 From: lnsun <57457122+lnsun@users.noreply.github.com> Date: Tue, 19 May 2020 11:13:05 -0400 Subject: [PATCH 063/144] a small tweak --- src/main/java/pico/typecheck/ObjectIdentityMethodEnforcer.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/java/pico/typecheck/ObjectIdentityMethodEnforcer.java b/src/main/java/pico/typecheck/ObjectIdentityMethodEnforcer.java index c2c5a45..6ad8a84 100644 --- a/src/main/java/pico/typecheck/ObjectIdentityMethodEnforcer.java +++ b/src/main/java/pico/typecheck/ObjectIdentityMethodEnforcer.java @@ -7,7 +7,6 @@ import com.sun.source.util.TreePath; import com.sun.source.util.TreePathScanner; import org.checkerframework.common.basetype.BaseTypeChecker; -import org.checkerframework.framework.source.Result; import org.checkerframework.framework.util.AnnotatedTypes; import org.checkerframework.javacutil.ElementUtils; import org.checkerframework.javacutil.TreeUtils; From a4339f95ff34e39880933009cfb567f18346f771 Mon Sep 17 00:00:00 2001 From: lnsun <57457122+lnsun@users.noreply.github.com> Date: Tue, 19 May 2020 14:14:30 -0400 Subject: [PATCH 064/144] update test cases (TC all pass) --- .../inference/inferrable/ConstructorInvocationUsingNew.java | 2 +- testinput/inference/inferrable/issue144/ComplicatedTest.java | 2 +- testinput/inference/inferrable/issue144/RDMConstructor.java | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/testinput/inference/inferrable/ConstructorInvocationUsingNew.java b/testinput/inference/inferrable/ConstructorInvocationUsingNew.java index 021623e..1b2dee8 100644 --- a/testinput/inference/inferrable/ConstructorInvocationUsingNew.java +++ b/testinput/inference/inferrable/ConstructorInvocationUsingNew.java @@ -9,7 +9,7 @@ public class ConstructorInvocationUsingNew { public static void main(String[] args) { // Handled by PICOInferenceVisito#checkConstructorInvocability - // :: fixable-error: (type.invalid.annotations.on.use) + // :: fixable-error: (type.invalid.annotations.on.use) :: fixable-error: (assignment.type.incompatible) @Immutable ConstructorInvocationUsingNew c = new ConstructorInvocationUsingNew(); } } diff --git a/testinput/inference/inferrable/issue144/ComplicatedTest.java b/testinput/inference/inferrable/issue144/ComplicatedTest.java index d3aa0ec..c6a8122 100644 --- a/testinput/inference/inferrable/issue144/ComplicatedTest.java +++ b/testinput/inference/inferrable/issue144/ComplicatedTest.java @@ -38,7 +38,7 @@ void testImmutability() { String name = "tamier"; int age = 24; ArrayList friends = new ArrayList(); - // :: fixable-error: (type.invalid.annotations.on.use) + // :: fixable-error: (constructor.invocation.invalid) Person p = new @Immutable Person(name, age, friends); // :: fixable-error: (method.invocation.invalid) p.getName(); diff --git a/testinput/inference/inferrable/issue144/RDMConstructor.java b/testinput/inference/inferrable/issue144/RDMConstructor.java index f7bbb2d..c79dec6 100644 --- a/testinput/inference/inferrable/issue144/RDMConstructor.java +++ b/testinput/inference/inferrable/issue144/RDMConstructor.java @@ -16,7 +16,7 @@ class A { public class RDMConstructor { void test1() { - // :: fixable-error: (type.invalid.annotations.on.use) + // :: fixable-error: (type.invalid.annotations.on.use) :: fixable-error: (assignment.type.incompatible) @Immutable A la = new A(); la.toString(); } From 5da659fbb4075afb6242e054d830e2f02c550f52 Mon Sep 17 00:00:00 2001 From: lnsun <57457122+lnsun@users.noreply.github.com> Date: Thu, 21 May 2020 17:10:47 -0400 Subject: [PATCH 065/144] re-remove @SPM --- .../typecheck/PICOAnnotatedTypeFactory.java | 29 ------------------- .../typecheck/PICOAnnotationMirrorHolder.java | 3 -- .../pico/typecheck/PICOViewpointAdapter.java | 3 +- src/main/java/qual/Bottom.java | 2 +- src/main/java/qual/PolyMutable.java | 4 +-- .../java/qual/SubstitutablePolyMutable.java | 15 ---------- 6 files changed, 4 insertions(+), 52 deletions(-) delete mode 100644 src/main/java/qual/SubstitutablePolyMutable.java diff --git a/src/main/java/pico/typecheck/PICOAnnotatedTypeFactory.java b/src/main/java/pico/typecheck/PICOAnnotatedTypeFactory.java index afeadff..3246413 100644 --- a/src/main/java/pico/typecheck/PICOAnnotatedTypeFactory.java +++ b/src/main/java/pico/typecheck/PICOAnnotatedTypeFactory.java @@ -13,7 +13,6 @@ import javax.lang.model.element.AnnotationMirror; import javax.lang.model.element.Element; import javax.lang.model.element.ElementKind; -import javax.lang.model.element.ExecutableElement; import javax.lang.model.element.VariableElement; import javax.lang.model.type.DeclaredType; import javax.lang.model.type.TypeKind; @@ -65,7 +64,6 @@ import qual.PolyMutable; import qual.Readonly; import qual.ReceiverDependantMutable; -import qual.SubstitutablePolyMutable; import static pico.typecheck.PICOAnnotationMirrorHolder.*; @@ -96,7 +94,6 @@ protected Set> createSupportedTypeQualifiers() { Mutable.class, PolyMutable.class, ReceiverDependantMutable.class, - SubstitutablePolyMutable.class, Immutable.class, Bottom.class, Initialized.class, @@ -250,32 +247,6 @@ public AnnotatedTypeMirror getAnnotatedTypeLhs(Tree lhsTree) { // return pair; // } - @Override - public void methodFromUsePreSubstitution(ExpressionTree tree, AnnotatedExecutableType type) { - if (tree instanceof MethodInvocationTree) { - MethodInvocationTree methodInvocationTree = (MethodInvocationTree) tree; - ExecutableElement methodElt = TreeUtils.elementFromUse(methodInvocationTree); - - assert methodElt != null; - if (ElementUtils.isStatic(methodElt)) { - AnnotatedTypeMirror returnType = type.getReturnType(); - if (returnType.hasAnnotation(POLY_MUTABLE)) { - // Only substitute polymutable but not other qualifiers! Missing the if statement - // caused bugs before! - returnType.replaceAnnotation(SUBSTITUTABLE_POLY_MUTABLE); - } - List parameterTypes = type.getParameterTypes(); - for (AnnotatedTypeMirror p : parameterTypes) { - if (returnType.hasAnnotation(POLY_MUTABLE)) { - p.replaceAnnotation(SUBSTITUTABLE_POLY_MUTABLE); - } - } - } - } - - super.methodFromUsePreSubstitution(tree, type); - } - protected class PICOQualifierHierarchy extends InitializationQualifierHierarchy { public PICOQualifierHierarchy(MultiGraphFactory f, Object[] arg) { diff --git a/src/main/java/pico/typecheck/PICOAnnotationMirrorHolder.java b/src/main/java/pico/typecheck/PICOAnnotationMirrorHolder.java index e1ef081..478624c 100644 --- a/src/main/java/pico/typecheck/PICOAnnotationMirrorHolder.java +++ b/src/main/java/pico/typecheck/PICOAnnotationMirrorHolder.java @@ -9,7 +9,6 @@ import qual.PolyMutable; import qual.Readonly; import qual.ReceiverDependantMutable; -import qual.SubstitutablePolyMutable; import javax.lang.model.element.AnnotationMirror; import javax.lang.model.util.Elements; @@ -23,7 +22,6 @@ public class PICOAnnotationMirrorHolder { public static AnnotationMirror MUTABLE; public static AnnotationMirror POLY_MUTABLE; public static AnnotationMirror RECEIVER_DEPENDANT_MUTABLE; - public static AnnotationMirror SUBSTITUTABLE_POLY_MUTABLE; public static AnnotationMirror IMMUTABLE; public static AnnotationMirror BOTTOM; public static AnnotationMirror COMMITED; @@ -34,7 +32,6 @@ public static void init(SourceChecker checker) { MUTABLE = AnnotationBuilder.fromClass(elements, Mutable.class); POLY_MUTABLE = AnnotationBuilder.fromClass(elements, PolyMutable.class); RECEIVER_DEPENDANT_MUTABLE = AnnotationBuilder.fromClass(elements, ReceiverDependantMutable.class); - SUBSTITUTABLE_POLY_MUTABLE = AnnotationBuilder.fromClass(elements, SubstitutablePolyMutable.class); IMMUTABLE = AnnotationBuilder.fromClass(elements, Immutable.class); BOTTOM = AnnotationBuilder.fromClass(elements, Bottom.class); diff --git a/src/main/java/pico/typecheck/PICOViewpointAdapter.java b/src/main/java/pico/typecheck/PICOViewpointAdapter.java index 4eb82cd..027a8f6 100644 --- a/src/main/java/pico/typecheck/PICOViewpointAdapter.java +++ b/src/main/java/pico/typecheck/PICOViewpointAdapter.java @@ -6,7 +6,6 @@ import static pico.typecheck.PICOAnnotationMirrorHolder.POLY_MUTABLE; import static pico.typecheck.PICOAnnotationMirrorHolder.READONLY; import static pico.typecheck.PICOAnnotationMirrorHolder.RECEIVER_DEPENDANT_MUTABLE; -import static pico.typecheck.PICOAnnotationMirrorHolder.SUBSTITUTABLE_POLY_MUTABLE; import javax.lang.model.element.AnnotationMirror; import javax.lang.model.element.Element; @@ -54,7 +53,7 @@ protected AnnotationMirror combineAnnotationWithAnnotation(AnnotationMirror rece } else if (AnnotationUtils.areSame(declaredAnnotation, BOTTOM)) { return BOTTOM; } else if (AnnotationUtils.areSame(declaredAnnotation, POLY_MUTABLE)) { - return SUBSTITUTABLE_POLY_MUTABLE; + return POLY_MUTABLE; } else if (AnnotationUtils.areSame(declaredAnnotation, RECEIVER_DEPENDANT_MUTABLE)) { return receiverAnnotation; } else { diff --git a/src/main/java/qual/Bottom.java b/src/main/java/qual/Bottom.java index 89ad95f..9fbbdf7 100644 --- a/src/main/java/qual/Bottom.java +++ b/src/main/java/qual/Bottom.java @@ -7,7 +7,7 @@ import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; -@SubtypeOf({Mutable.class, Immutable.class, PolyMutable.class, ReceiverDependantMutable.class}) +@SubtypeOf({Mutable.class, Immutable.class, ReceiverDependantMutable.class}) @DefaultFor(value = { TypeUseLocation.LOWER_BOUND }, typeKinds = {TypeKind.NULL}) @Documented @Retention(RetentionPolicy.RUNTIME) diff --git a/src/main/java/qual/PolyMutable.java b/src/main/java/qual/PolyMutable.java index 54f45ca..48cc878 100644 --- a/src/main/java/qual/PolyMutable.java +++ b/src/main/java/qual/PolyMutable.java @@ -1,6 +1,6 @@ package qual; -import org.checkerframework.framework.qual.SubtypeOf; +import org.checkerframework.framework.qual.PolymorphicQualifier; import java.lang.annotation.Documented; import java.lang.annotation.ElementType; @@ -8,8 +8,8 @@ import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; -@SubtypeOf(Readonly.class) @Documented @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.TYPE_USE, ElementType.TYPE_PARAMETER}) +@PolymorphicQualifier(Readonly.class) public @interface PolyMutable {} diff --git a/src/main/java/qual/SubstitutablePolyMutable.java b/src/main/java/qual/SubstitutablePolyMutable.java deleted file mode 100644 index 6620c5c..0000000 --- a/src/main/java/qual/SubstitutablePolyMutable.java +++ /dev/null @@ -1,15 +0,0 @@ -package qual; - -import org.checkerframework.framework.qual.PolymorphicQualifier; - -import java.lang.annotation.Documented; -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -@PolymorphicQualifier(Readonly.class) -@Documented -@Retention(RetentionPolicy.RUNTIME) -@Target({}) -public @interface SubstitutablePolyMutable {} From a954e32c15ffa80c5f202611438e5a2f044c3a9d Mon Sep 17 00:00:00 2001 From: lnsun <57457122+lnsun@users.noreply.github.com> Date: Fri, 22 May 2020 10:51:04 -0400 Subject: [PATCH 066/144] add CF all-systems test --- ...mutabilityTypecheckBaseAllSystemsTest.java | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 src/test/java/pico/ImmutabilityTypecheckBaseAllSystemsTest.java diff --git a/src/test/java/pico/ImmutabilityTypecheckBaseAllSystemsTest.java b/src/test/java/pico/ImmutabilityTypecheckBaseAllSystemsTest.java new file mode 100644 index 0000000..9375910 --- /dev/null +++ b/src/test/java/pico/ImmutabilityTypecheckBaseAllSystemsTest.java @@ -0,0 +1,24 @@ +package pico; + +import org.checkerframework.framework.test.CheckerFrameworkPerFileTest; +import org.checkerframework.framework.test.TestUtilities; +import org.junit.runners.Parameterized.Parameters; +import pico.typecheck.PICOChecker; + +import java.io.File; +import java.util.ArrayList; +import java.util.List; + +public class ImmutabilityTypecheckBaseAllSystemsTest extends CheckerFrameworkPerFileTest { + public ImmutabilityTypecheckBaseAllSystemsTest(File testFile) { + super(testFile, PICOChecker.class, "", "-Anomsgtext", + "-Anocheckjdk", "-d", "testTmp/typecheck"); + } + + @Parameters + public static List getTestFiles(){ + List testfiles = new ArrayList<>(TestUtilities.findRelativeNestedJavaFiles( + "../checker-framework/checker/tests", "all-systems")); + return testfiles; + } +} From e53f3fb278ed1e2303796ddfff6fb9b3c4695ad9 Mon Sep 17 00:00:00 2001 From: lnsun <57457122+lnsun@users.noreply.github.com> Date: Fri, 22 May 2020 10:54:00 -0400 Subject: [PATCH 067/144] small tweaks on test drivers --- .../ImmutabilityInferenceInitialTypecheckTest.java | 5 +---- src/test/java/pico/ImmutabilityInferenceTest.java | 13 +++++++------ .../ImmutabilityTypecheckBaseAllSystemsTest.java | 3 +-- .../pico/ImmutabilityTypecheckExtendedTest.java | 4 +--- src/test/java/pico/ImmutabilityTypecheckTest.java | 4 +--- 5 files changed, 11 insertions(+), 18 deletions(-) diff --git a/src/test/java/pico/ImmutabilityInferenceInitialTypecheckTest.java b/src/test/java/pico/ImmutabilityInferenceInitialTypecheckTest.java index 8f3893f..3d3bcac 100644 --- a/src/test/java/pico/ImmutabilityInferenceInitialTypecheckTest.java +++ b/src/test/java/pico/ImmutabilityInferenceInitialTypecheckTest.java @@ -4,7 +4,6 @@ import org.checkerframework.framework.test.TestUtilities; import org.junit.runners.Parameterized.Parameters; import pico.inference.PICOInferenceChecker; -import pico.typecheck.PICOChecker; import java.io.File; import java.util.ArrayList; @@ -18,8 +17,6 @@ public ImmutabilityInferenceInitialTypecheckTest(File testFile) { @Parameters public static List getTestFiles(){ - List testfiles = new ArrayList<>(); - testfiles.addAll(TestUtilities.findRelativeNestedJavaFiles("testinput", "typecheck")); - return testfiles; + return new ArrayList<>(TestUtilities.findRelativeNestedJavaFiles("testinput", "typecheck")); } } diff --git a/src/test/java/pico/ImmutabilityInferenceTest.java b/src/test/java/pico/ImmutabilityInferenceTest.java index 59b88a8..47e5fdb 100644 --- a/src/test/java/pico/ImmutabilityInferenceTest.java +++ b/src/test/java/pico/ImmutabilityInferenceTest.java @@ -17,13 +17,15 @@ public class ImmutabilityInferenceTest extends CFInferenceTest { public ImmutabilityInferenceTest(File testFile) { super(testFile, PICOInferenceChecker.class, "", - "-Anomsgtext", "-Astubs=src/main/java/pico/typecheck/jdk.astub", "-d", "testdata/inference/inferrable"); + "-Anomsgtext", + "-Astubs=src/main/java/pico/typecheck/jdk.astub", + "-d", "testdata/inference/inferrable"); } @Override public Pair> getSolverNameAndOptions() { - return Pair.> of(PICOSolverEngine.class.getCanonicalName(), - new ArrayList(Arrays.asList("useGraph=false", "collectStatistic=true"))); + return Pair.of(PICOSolverEngine.class.getCanonicalName(), + new ArrayList<>(Arrays.asList("useGraph=false", "collectStatistic=true"))); } @Override @@ -33,8 +35,7 @@ public boolean useHacks() { @Parameters public static List getTestFiles(){ - List testfiles = new ArrayList<>();//InferenceTestUtilities.findAllSystemTests(); - testfiles.addAll(TestUtilities.findRelativeNestedJavaFiles("testinput", "inference/inferrable")); - return testfiles; + //InferenceTestUtilities.findAllSystemTests(); + return new ArrayList<>(TestUtilities.findRelativeNestedJavaFiles("testinput", "inference/inferrable")); } } diff --git a/src/test/java/pico/ImmutabilityTypecheckBaseAllSystemsTest.java b/src/test/java/pico/ImmutabilityTypecheckBaseAllSystemsTest.java index 9375910..10cf546 100644 --- a/src/test/java/pico/ImmutabilityTypecheckBaseAllSystemsTest.java +++ b/src/test/java/pico/ImmutabilityTypecheckBaseAllSystemsTest.java @@ -17,8 +17,7 @@ public ImmutabilityTypecheckBaseAllSystemsTest(File testFile) { @Parameters public static List getTestFiles(){ - List testfiles = new ArrayList<>(TestUtilities.findRelativeNestedJavaFiles( + return new ArrayList<>(TestUtilities.findRelativeNestedJavaFiles( "../checker-framework/checker/tests", "all-systems")); - return testfiles; } } diff --git a/src/test/java/pico/ImmutabilityTypecheckExtendedTest.java b/src/test/java/pico/ImmutabilityTypecheckExtendedTest.java index ae6a61e..dc7017f 100644 --- a/src/test/java/pico/ImmutabilityTypecheckExtendedTest.java +++ b/src/test/java/pico/ImmutabilityTypecheckExtendedTest.java @@ -17,8 +17,6 @@ public ImmutabilityTypecheckExtendedTest(File testFile) { @Parameters public static List getTestFiles(){ - List testfiles = new ArrayList<>(); - testfiles.addAll(TestUtilities.findRelativeNestedJavaFiles("testinput", "inference/inferrable")); - return testfiles; + return new ArrayList<>(TestUtilities.findRelativeNestedJavaFiles("testinput", "inference/inferrable")); } } diff --git a/src/test/java/pico/ImmutabilityTypecheckTest.java b/src/test/java/pico/ImmutabilityTypecheckTest.java index 41482b6..bf054f4 100644 --- a/src/test/java/pico/ImmutabilityTypecheckTest.java +++ b/src/test/java/pico/ImmutabilityTypecheckTest.java @@ -17,8 +17,6 @@ public ImmutabilityTypecheckTest(File testFile) { @Parameters public static List getTestFiles(){ - List testfiles = new ArrayList<>(); - testfiles.addAll(TestUtilities.findRelativeNestedJavaFiles("testinput", "typecheck")); - return testfiles; + return new ArrayList<>(TestUtilities.findRelativeNestedJavaFiles("testinput", "typecheck")); } } From d3e571c0f422a61310668042763ff5aacbbd5a8b Mon Sep 17 00:00:00 2001 From: lnsun <57457122+lnsun@users.noreply.github.com> Date: Wed, 10 Jun 2020 17:00:08 -0400 Subject: [PATCH 068/144] interface --- src/main/java/pico/common/PICOTypeUtil.java | 4 ++-- .../inference/PICOInferenceAnnotatedTypeFactory.java | 10 +++++++++- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/src/main/java/pico/common/PICOTypeUtil.java b/src/main/java/pico/common/PICOTypeUtil.java index 8ff24ec..bd7f92f 100644 --- a/src/main/java/pico/common/PICOTypeUtil.java +++ b/src/main/java/pico/common/PICOTypeUtil.java @@ -308,13 +308,13 @@ public static void applyConstant(AnnotatedTypeMirror type, AnnotationMirror am) /**Check if a field is final or not.*/ public static boolean isFinalField(Element variableElement) { - assert variableElement instanceof VariableElement; + assert variableElement instanceof VariableElement; // FIXME consider rm return ElementUtils.isFinal(variableElement); } /**Check if a field is assignable or not.*/ public static boolean isAssignableField(Element variableElement, AnnotationProvider provider) { - if (!(variableElement instanceof VariableElement)) { + if (!(variableElement instanceof VariableElement)) { // FIXME consider rm return false; } boolean hasExplicitAssignableAnnotation = provider.getDeclAnnotation(variableElement, Assignable.class) != null; diff --git a/src/main/java/pico/inference/PICOInferenceAnnotatedTypeFactory.java b/src/main/java/pico/inference/PICOInferenceAnnotatedTypeFactory.java index f2ac15d..a5374f2 100644 --- a/src/main/java/pico/inference/PICOInferenceAnnotatedTypeFactory.java +++ b/src/main/java/pico/inference/PICOInferenceAnnotatedTypeFactory.java @@ -20,6 +20,7 @@ import org.checkerframework.framework.type.AnnotatedTypeFactory; import org.checkerframework.framework.type.AnnotatedTypeMirror; import org.checkerframework.framework.type.AnnotatedTypeMirror.AnnotatedDeclaredType; +import org.checkerframework.framework.type.ViewpointAdapter; import org.checkerframework.framework.type.treeannotator.LiteralTreeAnnotator; import org.checkerframework.framework.type.treeannotator.ListTreeAnnotator; import org.checkerframework.framework.type.treeannotator.PropagationTreeAnnotator; @@ -28,6 +29,8 @@ import org.checkerframework.framework.type.typeannotator.TypeAnnotator; import org.checkerframework.javacutil.Pair; import org.checkerframework.javacutil.TreeUtils; +import pico.common.ExtendedViewpointAdapter; +import pico.common.ViewpointAdapterGettable; import pico.typecheck.PICOAnnotatedTypeFactory.PICODefaultForTypeAnnotator; import pico.common.PICOTypeUtil; @@ -47,7 +50,7 @@ * type on that type. This ensures that that VariableSlot doesn't enter solver and solver doesn't * give solution to the VariableSlot, and there won't be annotations inserted to implicit locations. */ -public class PICOInferenceAnnotatedTypeFactory extends InferenceAnnotatedTypeFactory { +public class PICOInferenceAnnotatedTypeFactory extends InferenceAnnotatedTypeFactory implements ViewpointAdapterGettable { public PICOInferenceAnnotatedTypeFactory(InferenceChecker inferenceChecker, boolean withCombineConstraints, BaseAnnotatedTypeFactory realTypeFactory, InferrableChecker realChecker, SlotManager slotManager, ConstraintManager constraintManager) { super(inferenceChecker, withCombineConstraints, realTypeFactory, realChecker, slotManager, constraintManager); // Always call postInit() at the end of ATF constructor! @@ -129,6 +132,11 @@ public AnnotatedDeclaredType getSelfType(Tree tree) { return type; } + @Override + public ExtendedViewpointAdapter getViewpointAdapter() { + return (ExtendedViewpointAdapter) viewpointAdapter; + } + class PICOInferencePropagationTreeAnnotator extends PropagationTreeAnnotator { public PICOInferencePropagationTreeAnnotator(AnnotatedTypeFactory atypeFactory) { super(atypeFactory); From 9879f05b24e26c0dd660a840fdcd8e9e38dc1887 Mon Sep 17 00:00:00 2001 From: lnsun <57457122+lnsun@users.noreply.github.com> Date: Thu, 2 Jul 2020 15:04:48 -0400 Subject: [PATCH 069/144] isSubtype use native api --- src/main/java/pico/typecheck/PICOVisitor.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/pico/typecheck/PICOVisitor.java b/src/main/java/pico/typecheck/PICOVisitor.java index 0d019a8..df25e8a 100644 --- a/src/main/java/pico/typecheck/PICOVisitor.java +++ b/src/main/java/pico/typecheck/PICOVisitor.java @@ -198,7 +198,7 @@ protected void checkConstructorInvocation(AnnotatedDeclaredType invocation, Anno // The immutability return qualifier of the constructor (returnType) must be supertype of the // constructor invocation immutability qualifier(invocation). - if (!atypeFactory.getTypeHierarchy().isSubtype(invocation, returnType, READONLY)) { + if (!atypeFactory.getQualifierHierarchy().isSubtype(invocation.getAnnotationInHierarchy(READONLY), returnType.getAnnotationInHierarchy(READONLY))) { checker.reportError(newClassTree, "constructor.invocation.invalid", invocation, returnType); } } From ab48c6ac41323ff32492887195ddcc33f7a0b558 Mon Sep 17 00:00:00 2001 From: lnsun <57457122+lnsun@users.noreply.github.com> Date: Thu, 2 Jul 2020 15:05:35 -0400 Subject: [PATCH 070/144] infer atf + get bound --- .../PICOInferenceAnnotatedTypeFactory.java | 39 ++++++++++++++++++- 1 file changed, 37 insertions(+), 2 deletions(-) diff --git a/src/main/java/pico/inference/PICOInferenceAnnotatedTypeFactory.java b/src/main/java/pico/inference/PICOInferenceAnnotatedTypeFactory.java index a5374f2..8a3b463 100644 --- a/src/main/java/pico/inference/PICOInferenceAnnotatedTypeFactory.java +++ b/src/main/java/pico/inference/PICOInferenceAnnotatedTypeFactory.java @@ -27,6 +27,7 @@ import org.checkerframework.framework.type.treeannotator.TreeAnnotator; import org.checkerframework.framework.type.typeannotator.ListTypeAnnotator; import org.checkerframework.framework.type.typeannotator.TypeAnnotator; +import org.checkerframework.javacutil.AnnotationUtils; import org.checkerframework.javacutil.Pair; import org.checkerframework.javacutil.TreeUtils; import pico.common.ExtendedViewpointAdapter; @@ -35,12 +36,18 @@ import pico.common.PICOTypeUtil; import javax.lang.model.element.AnnotationMirror; +import javax.lang.model.element.Element; +import javax.lang.model.element.ElementKind; +import javax.lang.model.type.DeclaredType; import javax.lang.model.type.TypeKind; +import javax.lang.model.type.TypeMirror; import java.util.Collection; +import java.util.Collections; +import java.util.Set; -import static pico.typecheck.PICOAnnotationMirrorHolder.IMMUTABLE; -import static pico.typecheck.PICOAnnotationMirrorHolder.READONLY; +import static pico.typecheck.PICOAnnotationMirrorHolder.*; +import static pico.typecheck.PICOAnnotationMirrorHolder.RECEIVER_DEPENDANT_MUTABLE; /** * Propagates correct constraints on trees and types using TreeAnnotators and TypeAnnotators. @@ -137,6 +144,34 @@ public ExtendedViewpointAdapter getViewpointAdapter() { return (ExtendedViewpointAdapter) viewpointAdapter; } + @Override + protected Set getDefaultTypeDeclarationBounds() { + return Collections.singleton(MUTABLE); + } + + @Override + public Set getTypeDeclarationBounds(TypeMirror type) { + // TODO too awkward. maybe overload isImplicitlyImmutableType + if (PICOTypeUtil.isImplicitlyImmutableType(toAnnotatedType(type, false))) { + return Collections.singleton(IMMUTABLE); + } + if (type.getKind() == TypeKind.ARRAY) { + return Collections.singleton(READONLY); // if decided to use vpa for array, return RDM. + } + + // IMMUTABLE for enum w/o decl anno + if (type instanceof DeclaredType) { + Element ele = ((DeclaredType) type).asElement(); + if (ele.getKind() == ElementKind.ENUM) { + if (!AnnotationUtils.containsSameByName(getDeclAnnotations(ele), MUTABLE) && + !AnnotationUtils.containsSameByName(getDeclAnnotations(ele), RECEIVER_DEPENDANT_MUTABLE)) { // no decl anno + return Collections.singleton(IMMUTABLE); + } + } + } + return super.getTypeDeclarationBounds(type); + } + class PICOInferencePropagationTreeAnnotator extends PropagationTreeAnnotator { public PICOInferencePropagationTreeAnnotator(AnnotatedTypeFactory atypeFactory) { super(atypeFactory); From 9885f10e2c1c05f610c0ca8faf8dba51a99d2e87 Mon Sep 17 00:00:00 2001 From: lnsun <57457122+lnsun@users.noreply.github.com> Date: Thu, 2 Jul 2020 15:06:21 -0400 Subject: [PATCH 071/144] tweaks --- .../pico/inference/PICOInferenceViewpointAdapter.java | 11 +++++++++++ .../java/pico/inference/PICOInferenceVisitor.java | 4 ++-- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/src/main/java/pico/inference/PICOInferenceViewpointAdapter.java b/src/main/java/pico/inference/PICOInferenceViewpointAdapter.java index d418eb2..8dd86d3 100644 --- a/src/main/java/pico/inference/PICOInferenceViewpointAdapter.java +++ b/src/main/java/pico/inference/PICOInferenceViewpointAdapter.java @@ -5,6 +5,7 @@ import org.checkerframework.framework.type.AnnotatedTypeMirror; import pico.common.ExtendedViewpointAdapter; import pico.common.PICOTypeUtil; +import static pico.typecheck.PICOAnnotationMirrorHolder.READONLY; import javax.lang.model.element.AnnotationMirror; import javax.lang.model.element.Element; @@ -41,4 +42,14 @@ public AnnotatedTypeMirror rawCombineAnnotationWithType(AnnotationMirror anno, A public AnnotationMirror rawCombineAnnotationWithAnnotation(AnnotationMirror anno, AnnotationMirror type) { return rawCombineAnnotationWithAnnotation(anno, type); } + + @Override + protected AnnotationMirror extractAnnotationMirror(AnnotatedTypeMirror atm) { + // since the introduction of vp-is-valid rules, real am may be used? + AnnotationMirror am = super.extractAnnotationMirror(atm); + if (am == null) { // if failed, try to get real am + am = atm.getAnnotationInHierarchy(READONLY); + } + return am; + } } diff --git a/src/main/java/pico/inference/PICOInferenceVisitor.java b/src/main/java/pico/inference/PICOInferenceVisitor.java index 389ea7a..56fdace 100644 --- a/src/main/java/pico/inference/PICOInferenceVisitor.java +++ b/src/main/java/pico/inference/PICOInferenceVisitor.java @@ -91,9 +91,9 @@ public boolean isValidUse(AnnotatedDeclaredType declarationType, AnnotatedDeclar */ private boolean isAdaptedSubtype(AnnotatedTypeMirror lhs, AnnotatedTypeMirror rhs) { ExtendedViewpointAdapter vpa = ((ViewpointAdapterGettable)atypeFactory).getViewpointAdapter(); - AnnotatedTypeMirror adapted = vpa.rawCombineAnnotationWithType(lhs.getAnnotationInHierarchy(READONLY), + AnnotatedTypeMirror adapted = vpa.rawCombineAnnotationWithType(extractVarAnnot(lhs), rhs); - return mainIsSubtype(adapted, lhs.getAnnotationInHierarchy(READONLY)); + return mainIsSubtype(adapted, extractVarAnnot(lhs)); } @Override From df3f2dddce05f3a32f6f30d37a12057841e6f9b7 Mon Sep 17 00:00:00 2001 From: lnsun <57457122+lnsun@users.noreply.github.com> Date: Fri, 3 Jul 2020 17:07:43 -0400 Subject: [PATCH 072/144] checker.report update --- src/main/java/pico/inference/PICOInferenceVisitor.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/pico/inference/PICOInferenceVisitor.java b/src/main/java/pico/inference/PICOInferenceVisitor.java index 56fdace..b805464 100644 --- a/src/main/java/pico/inference/PICOInferenceVisitor.java +++ b/src/main/java/pico/inference/PICOInferenceVisitor.java @@ -505,11 +505,11 @@ private void checkMutableReceiverCase(ExpressionTree node, ExpressionTree variab // Completely copied from PICOVisitor private void reportFieldOrArrayWriteError(Tree node, ExpressionTree variable, AnnotatedTypeMirror receiverType) { if (variable.getKind() == Kind.MEMBER_SELECT) { - checker.report(Result.failure("illegal.field.write", receiverType), TreeUtils.getReceiverTree(variable)); + checker.reportError(TreeUtils.getReceiverTree(variable), "illegal.field.write", receiverType); } else if (variable.getKind() == Kind.IDENTIFIER) { - checker.report(Result.failure("illegal.field.write", receiverType), node); + checker.reportError(node, "illegal.field.write", receiverType); } else if (variable.getKind() == Kind.ARRAY_ACCESS) { - checker.report(Result.failure("illegal.array.write", receiverType), ((ArrayAccessTree)variable).getExpression()); + checker.reportError(((ArrayAccessTree)variable).getExpression(), "illegal.array.write", receiverType); } else { throw new BugInCF("Unknown assignment variable at: ", node); } From a6df8de8bfe2401b6c0d2fc14004fd683198c0ec Mon Sep 17 00:00:00 2001 From: lnsun <57457122+lnsun@users.noreply.github.com> Date: Fri, 3 Jul 2020 17:08:51 -0400 Subject: [PATCH 073/144] checker.report update --- src/main/java/pico/inference/PICOInferenceVisitor.java | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/main/java/pico/inference/PICOInferenceVisitor.java b/src/main/java/pico/inference/PICOInferenceVisitor.java index b805464..b7841b5 100644 --- a/src/main/java/pico/inference/PICOInferenceVisitor.java +++ b/src/main/java/pico/inference/PICOInferenceVisitor.java @@ -622,9 +622,7 @@ protected void checkMethodInvocability(AnnotatedExecutableType method, MethodInv // then this unsatisfiable constraint is captured by ConstraintManager and ConstraintManager early exits. But // now for two ConstantSlot case, no subtype constraint is generated any more. So we have to report the error // , otherwise it will cause inference result not typecheck - checker.report( - Result.failure( - "super.invocation.invalid", subClassConstructorReturnType, superClassConstructorReturnType), node); + checker.reportError(node, "super.invocation.invalid", subClassConstructorReturnType, superClassConstructorReturnType); } } super.checkMethodInvocability(method, node); @@ -678,7 +676,7 @@ private boolean checkCompatabilityBetweenBoundAndSuperClassesBounds(ClassTree no List superBounds = PICOTypeUtil.getBoundTypesOfDirectSuperTypes(typeElement, atypeFactory); for (AnnotatedDeclaredType superBound : superBounds) { if (!isAdaptedSubtype(bound, superBound)) { - checker.report(Result.failure("subclass.bound.incompatible", bound, superBound), node); + checker.reportError(node, "subclass.bound.incompatible", bound, superBound); return false; // do not stop processing if failed } } From 020986460635022f9feb548d91bc43634999e048 Mon Sep 17 00:00:00 2001 From: lnsun <57457122+lnsun@users.noreply.github.com> Date: Fri, 9 Oct 2020 11:06:47 -0400 Subject: [PATCH 074/144] tc --- testinput/typecheck/Transitive.java | 46 +++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) create mode 100644 testinput/typecheck/Transitive.java diff --git a/testinput/typecheck/Transitive.java b/testinput/typecheck/Transitive.java new file mode 100644 index 0000000..ec88180 --- /dev/null +++ b/testinput/typecheck/Transitive.java @@ -0,0 +1,46 @@ +import qual.Readonly; + +public class Transitive { + + // class A, B, C are not annotated to test transitive mutability by default. + + static class A { + B b; + + public B getB() { + return b; + } + } + + static class B { + int field = 0; + C c; + + public C getC() { + return c; + } + } + + static class C { + int field = 0; + } + + static class Caller { + void test(@Readonly A a) { + // error + a.b.field = 1; + // error + a.getB().field = 1; + + // error + a.b.c.field = 1; + // error + a.getB().getC().field = 1; + // error + a.b.getC().field = 1; + // error + a.getB().c.field = 1; + } + } +} + From 12e4a8c49dcf536f13fbb718c68ebb958181282b Mon Sep 17 00:00:00 2001 From: lnsun <57457122+lnsun@users.noreply.github.com> Date: Thu, 15 Oct 2020 10:50:38 -0400 Subject: [PATCH 075/144] tc --- testinput/typecheck/RDMField.java | 46 +++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) create mode 100644 testinput/typecheck/RDMField.java diff --git a/testinput/typecheck/RDMField.java b/testinput/typecheck/RDMField.java new file mode 100644 index 0000000..985db9b --- /dev/null +++ b/testinput/typecheck/RDMField.java @@ -0,0 +1,46 @@ +import qual.*; + +public class RDMField{ + + @Mutable + private static class MutableClass { + int field = 0; + } + + @ReceiverDependantMutable + private static class RDMHolder { + + @ReceiverDependantMutable MutableClass field = new MutableClass(); + @Mutable MutableClass mutableField = new MutableClass(); + + public @PolyMutable MutableClass getField(@PolyMutable RDMHolder this) { + return field; + } + + public void setField(@Mutable RDMHolder this, MutableClass field) { + this.field = field; + } + + void asImmutable(@Immutable RDMHolder r) { + // :: error: (illegal.field.write) + r.field.field = 1; + // :: error: (illegal.field.write) + r.getField().field = 1; + // :: error: (method.invocation.invalid) + r.setField(new MutableClass()); + } + } + + @Immutable + private static class ImmutableHolder { + @ReceiverDependantMutable MutableClass field = new MutableClass(); + + public @PolyMutable MutableClass getField(@PolyMutable ImmutableHolder this) { + return field; + } + + + + } +} + From f04a5bbe704c02d57c4c352b1b7c2673f8707e0d Mon Sep 17 00:00:00 2001 From: lnsun <57457122+lnsun@users.noreply.github.com> Date: Thu, 15 Oct 2020 10:51:06 -0400 Subject: [PATCH 076/144] ignore rdm on fields --- src/main/java/pico/typecheck/PICOValidator.java | 9 ++++++++- src/main/java/pico/typecheck/PICOVisitor.java | 4 +++- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/src/main/java/pico/typecheck/PICOValidator.java b/src/main/java/pico/typecheck/PICOValidator.java index 3f3080c..3b7cdda 100644 --- a/src/main/java/pico/typecheck/PICOValidator.java +++ b/src/main/java/pico/typecheck/PICOValidator.java @@ -14,6 +14,7 @@ import org.checkerframework.javacutil.TreeUtils; import pico.common.PICOTypeUtil; +import javax.lang.model.element.ElementKind; import javax.lang.model.element.VariableElement; import java.util.Objects; @@ -37,7 +38,13 @@ public Void visitDeclared(AnnotatedDeclaredType type, Tree tree) { checkStaticReceiverDependantMutableError(type, tree); checkImplicitlyImmutableTypeError(type, tree); checkOnlyOneAssignabilityModifierOnField(tree); - return super.visitDeclared(type, tree); + + boolean oldCheck = checkTopLevelDeclaredType; + if(tree instanceof VariableTree && TreeUtils.elementFromDeclaration((VariableTree) tree).getKind() == ElementKind.FIELD) + checkTopLevelDeclaredType = false; + super.visitDeclared(type, tree); + checkTopLevelDeclaredType = oldCheck; + return null; } @Override diff --git a/src/main/java/pico/typecheck/PICOVisitor.java b/src/main/java/pico/typecheck/PICOVisitor.java index df25e8a..54620db 100644 --- a/src/main/java/pico/typecheck/PICOVisitor.java +++ b/src/main/java/pico/typecheck/PICOVisitor.java @@ -375,7 +375,9 @@ public Void visitVariable(VariableTree node, Void p) { } } // TODO use base cf check methods - checkAndReportInvalidAnnotationOnUse(type, node); + if (element.getKind() != ElementKind.FIELD || !type.hasAnnotation(RECEIVER_DEPENDANT_MUTABLE)) { + checkAndReportInvalidAnnotationOnUse(type, node); + } return super.visitVariable(node, p); } From aa240ec1db58f05a181116790ad4a76668e51a19 Mon Sep 17 00:00:00 2001 From: lnsun <57457122+lnsun@users.noreply.github.com> Date: Fri, 20 Nov 2020 10:56:02 -0500 Subject: [PATCH 077/144] rdm field logic + tc --- src/main/java/pico/typecheck/PICOVisitor.java | 29 ++++++++++++++- testinput/typecheck/RDMFieldInst.java | 35 +++++++++++++++++++ 2 files changed, 63 insertions(+), 1 deletion(-) create mode 100644 testinput/typecheck/RDMFieldInst.java diff --git a/src/main/java/pico/typecheck/PICOVisitor.java b/src/main/java/pico/typecheck/PICOVisitor.java index 54620db..c39c0be 100644 --- a/src/main/java/pico/typecheck/PICOVisitor.java +++ b/src/main/java/pico/typecheck/PICOVisitor.java @@ -55,6 +55,7 @@ import pico.common.ExtendedViewpointAdapter; import pico.common.PICOTypeUtil; import pico.common.ViewpointAdapterGettable; +import qual.Immutable; /** * Created by mier on 20/06/17. @@ -374,8 +375,15 @@ public Void visitVariable(VariableTree node, Void p) { checker.reportError(node, "field.polymutable.forbidden", element); } } + // When to check: + // bound == Immutable, OR + // not FIELD, OR + // top anno not RDM // TODO use base cf check methods - if (element.getKind() != ElementKind.FIELD || !type.hasAnnotation(RECEIVER_DEPENDANT_MUTABLE)) { + AnnotationMirror declAnno = atypeFactory.getTypeDeclarationBoundForMutability(type.getUnderlyingType()); + if (declAnno != null && AnnotationUtils.areSameByName(declAnno, IMMUTABLE) || + element.getKind() != ElementKind.FIELD || + !type.hasAnnotation(RECEIVER_DEPENDANT_MUTABLE)) { checkAndReportInvalidAnnotationOnUse(type, node); } return super.visitVariable(node, p); @@ -497,6 +505,25 @@ public void processClassTree(ClassTree node) { return;// Doesn't process the class tree anymore } + // field of mutable class cannot use RDM in immutable class + // Condition: + // * Class decl == Immutable + // * Member is field (variable) + // * Member's declared bound == Mutable + // * Member's use anno == RDM + if (bound.hasAnnotation(IMMUTABLE)) { + for(Tree member : node.getMembers()) { + if(member.getKind() == Kind.VARIABLE) { + AnnotatedTypeMirror fieldAtm = atypeFactory.getAnnotatedType(member); + if (fieldAtm.hasAnnotation(RECEIVER_DEPENDANT_MUTABLE) && + AnnotationUtils.containsSameByName(atypeFactory.getTypeDeclarationBounds(fieldAtm.getUnderlyingType()), MUTABLE)) { + checker.reportError(member, "test-key-1"); + } + } + } + } + + super.processClassTree(node); } diff --git a/testinput/typecheck/RDMFieldInst.java b/testinput/typecheck/RDMFieldInst.java new file mode 100644 index 0000000..fe713cf --- /dev/null +++ b/testinput/typecheck/RDMFieldInst.java @@ -0,0 +1,35 @@ +import qual.*; + +public class RDMFieldInst{ + @Mutable + private static class MutableBox {} + + @Immutable + private static class ImmutableBox {} + + @ReceiverDependantMutable + private static class RDMBox {} + + @Immutable + // :: error: (initialization.fields.uninitialized) + private static class ImmutableClass { + // :: error: (test-key-1) + @ReceiverDependantMutable MutableBox mutableBoxInRDM; + } + + @Mutable + private static class MutableClass { + @ReceiverDependantMutable MutableBox mutableBoxInRDM = new MutableBox(); + + @ReceiverDependantMutable RDMBox rdmBoxInRDMnewM = new @Mutable RDMBox(); + // :: error: (assignment.type.incompatible) + @ReceiverDependantMutable RDMBox rdmBoxInRDMnewI = new @Immutable RDMBox(); + // :: error: (assignment.type.incompatible) + @ReceiverDependantMutable RDMBox rdmBoxInRDMnewRDM = new @ReceiverDependantMutable RDMBox(); + // :: error: (assignment.type.incompatible) + @ReceiverDependantMutable ImmutableBox immutableBoxInRDM = new ImmutableBox(); + } + + + +} \ No newline at end of file From 06da98afa76e2e1e4a3af13be11b83c4574a45fd Mon Sep 17 00:00:00 2001 From: lnsun <57457122+lnsun@users.noreply.github.com> Date: Tue, 24 Nov 2020 16:23:56 -0500 Subject: [PATCH 078/144] VPA post check --- src/main/java/pico/typecheck/PICOViewpointAdapter.java | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/main/java/pico/typecheck/PICOViewpointAdapter.java b/src/main/java/pico/typecheck/PICOViewpointAdapter.java index 027a8f6..c83de77 100644 --- a/src/main/java/pico/typecheck/PICOViewpointAdapter.java +++ b/src/main/java/pico/typecheck/PICOViewpointAdapter.java @@ -61,6 +61,16 @@ protected AnnotationMirror combineAnnotationWithAnnotation(AnnotationMirror rece } } + @Override + protected AnnotatedTypeMirror combineAnnotationWithType(AnnotationMirror receiverAnnotation, AnnotatedTypeMirror declared) { + AnnotatedTypeMirror raw = super.combineAnnotationWithType(receiverAnnotation, declared); + if(AnnotationUtils.containsSameByName(atypeFactory.getTypeDeclarationBounds(declared.getUnderlyingType()), MUTABLE) + && (raw.hasAnnotation(IMMUTABLE) || raw.hasAnnotation(RECEIVER_DEPENDANT_MUTABLE))) { + raw.replaceAnnotation(MUTABLE); + } + return raw; + } + public AnnotatedTypeMirror rawCombineAnnotationWithType(AnnotationMirror anno, AnnotatedTypeMirror type) { // System.err.println("VPA: " + anno + " ->" + type); return combineAnnotationWithType(anno, type); From 0d636133f7e6dd2f2ef696b8d3617508c4cf3828 Mon Sep 17 00:00:00 2001 From: lnsun <57457122+lnsun@users.noreply.github.com> Date: Tue, 24 Nov 2020 17:21:35 -0500 Subject: [PATCH 079/144] vpa post check --- src/main/java/pico/typecheck/PICOViewpointAdapter.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/main/java/pico/typecheck/PICOViewpointAdapter.java b/src/main/java/pico/typecheck/PICOViewpointAdapter.java index c83de77..966c7fc 100644 --- a/src/main/java/pico/typecheck/PICOViewpointAdapter.java +++ b/src/main/java/pico/typecheck/PICOViewpointAdapter.java @@ -63,8 +63,10 @@ protected AnnotationMirror combineAnnotationWithAnnotation(AnnotationMirror rece @Override protected AnnotatedTypeMirror combineAnnotationWithType(AnnotationMirror receiverAnnotation, AnnotatedTypeMirror declared) { + boolean prevRdm = declared.hasAnnotation(RECEIVER_DEPENDANT_MUTABLE); AnnotatedTypeMirror raw = super.combineAnnotationWithType(receiverAnnotation, declared); - if(AnnotationUtils.containsSameByName(atypeFactory.getTypeDeclarationBounds(declared.getUnderlyingType()), MUTABLE) + if(prevRdm && + AnnotationUtils.containsSameByName(atypeFactory.getTypeDeclarationBounds(declared.getUnderlyingType()), MUTABLE) && (raw.hasAnnotation(IMMUTABLE) || raw.hasAnnotation(RECEIVER_DEPENDANT_MUTABLE))) { raw.replaceAnnotation(MUTABLE); } From d47daa59bfeba7a582b8c495613e7421e32191c3 Mon Sep 17 00:00:00 2001 From: lnsun <57457122+lnsun@users.noreply.github.com> Date: Thu, 26 Nov 2020 10:47:14 -0500 Subject: [PATCH 080/144] field rdm default --- src/main/java/pico/common/PICOTypeUtil.java | 14 +++++++++++ .../pico/typecheck/PICOViewpointAdapter.java | 24 +++++++++---------- 2 files changed, 26 insertions(+), 12 deletions(-) diff --git a/src/main/java/pico/common/PICOTypeUtil.java b/src/main/java/pico/common/PICOTypeUtil.java index bd7f92f..ced8cc1 100644 --- a/src/main/java/pico/common/PICOTypeUtil.java +++ b/src/main/java/pico/common/PICOTypeUtil.java @@ -22,6 +22,7 @@ import org.checkerframework.framework.type.AnnotatedTypeMirror.AnnotatedExecutableType; import org.checkerframework.framework.util.AnnotatedTypes; import org.checkerframework.javacutil.AnnotationProvider; +import org.checkerframework.javacutil.AnnotationUtils; import org.checkerframework.javacutil.ElementUtils; import org.checkerframework.javacutil.TreeUtils; import org.checkerframework.javacutil.TypesUtils; @@ -41,10 +42,12 @@ import javax.lang.model.type.TypeMirror; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collections; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.Set; import static pico.typecheck.PICOAnnotationMirrorHolder.IMMUTABLE; @@ -243,6 +246,17 @@ public static void addDefaultForField(AnnotatedTypeFactory annotatedTypeFactory, if (explicitATM instanceof AnnotatedDeclaredType) { AnnotatedDeclaredType adt = (AnnotatedDeclaredType) explicitATM; Element typeElement = adt.getUnderlyingType().asElement(); + + // add RDM if bound=M and enclosingBound=M/RDM + Set enclosingBound = annotatedTypeFactory.getTypeDeclarationBounds( + Objects.requireNonNull(ElementUtils.enclosingClass(element)).asType()); + Set declBound = annotatedTypeFactory.getTypeDeclarationBounds(element.asType()); + if (AnnotationUtils.containsSameByName(declBound, MUTABLE)) { + if (AnnotationUtils.containsSameByName(enclosingBound, RECEIVER_DEPENDANT_MUTABLE) || + AnnotationUtils.containsSameByName(enclosingBound, MUTABLE)) { + annotatedTypeMirror.replaceAnnotation(RECEIVER_DEPENDANT_MUTABLE); + } + } if (typeElement instanceof TypeElement) { AnnotatedDeclaredType bound = getBoundTypeOfTypeDeclaration((TypeElement) typeElement, annotatedTypeFactory); if (bound.hasAnnotation(RECEIVER_DEPENDANT_MUTABLE)) { diff --git a/src/main/java/pico/typecheck/PICOViewpointAdapter.java b/src/main/java/pico/typecheck/PICOViewpointAdapter.java index 966c7fc..3c275fc 100644 --- a/src/main/java/pico/typecheck/PICOViewpointAdapter.java +++ b/src/main/java/pico/typecheck/PICOViewpointAdapter.java @@ -60,18 +60,18 @@ protected AnnotationMirror combineAnnotationWithAnnotation(AnnotationMirror rece throw new BugInCF("Unknown declared modifier: " + declaredAnnotation, new UnkownImmutabilityQualifierException()); } } - - @Override - protected AnnotatedTypeMirror combineAnnotationWithType(AnnotationMirror receiverAnnotation, AnnotatedTypeMirror declared) { - boolean prevRdm = declared.hasAnnotation(RECEIVER_DEPENDANT_MUTABLE); - AnnotatedTypeMirror raw = super.combineAnnotationWithType(receiverAnnotation, declared); - if(prevRdm && - AnnotationUtils.containsSameByName(atypeFactory.getTypeDeclarationBounds(declared.getUnderlyingType()), MUTABLE) - && (raw.hasAnnotation(IMMUTABLE) || raw.hasAnnotation(RECEIVER_DEPENDANT_MUTABLE))) { - raw.replaceAnnotation(MUTABLE); - } - return raw; - } +// +// @Override +// protected AnnotatedTypeMirror combineAnnotationWithType(AnnotationMirror receiverAnnotation, AnnotatedTypeMirror declared) { +// boolean prevRdm = declared.hasAnnotation(RECEIVER_DEPENDANT_MUTABLE); +// AnnotatedTypeMirror raw = super.combineAnnotationWithType(receiverAnnotation, declared); +// if(prevRdm && +// AnnotationUtils.containsSameByName(atypeFactory.getTypeDeclarationBounds(declared.getUnderlyingType()), MUTABLE) +// && (raw.hasAnnotation(IMMUTABLE) || raw.hasAnnotation(RECEIVER_DEPENDANT_MUTABLE))) { +// raw.replaceAnnotation(MUTABLE); +// } +// return raw; +// } public AnnotatedTypeMirror rawCombineAnnotationWithType(AnnotationMirror anno, AnnotatedTypeMirror type) { // System.err.println("VPA: " + anno + " ->" + type); From ab44c1e617014881afa49765eef9cea63c2e11ab Mon Sep 17 00:00:00 2001 From: lnsun <57457122+lnsun@users.noreply.github.com> Date: Wed, 2 Dec 2020 10:24:15 -0500 Subject: [PATCH 081/144] check anno --- .../java/pico/typecheck/PICOValidator.java | 40 ++++++++++---- src/main/java/pico/typecheck/PICOVisitor.java | 54 ++++++++++++------- 2 files changed, 66 insertions(+), 28 deletions(-) diff --git a/src/main/java/pico/typecheck/PICOValidator.java b/src/main/java/pico/typecheck/PICOValidator.java index 3b7cdda..4c6b017 100644 --- a/src/main/java/pico/typecheck/PICOValidator.java +++ b/src/main/java/pico/typecheck/PICOValidator.java @@ -11,17 +11,21 @@ import org.checkerframework.framework.type.AnnotatedTypeMirror.AnnotatedArrayType; import org.checkerframework.framework.type.AnnotatedTypeMirror.AnnotatedDeclaredType; import org.checkerframework.framework.type.AnnotatedTypeMirror.AnnotatedPrimitiveType; +import org.checkerframework.javacutil.AnnotationUtils; +import org.checkerframework.javacutil.ElementUtils; import org.checkerframework.javacutil.TreeUtils; import pico.common.PICOTypeUtil; +import javax.lang.model.element.AnnotationMirror; +import javax.lang.model.element.Element; import javax.lang.model.element.ElementKind; import javax.lang.model.element.VariableElement; import java.util.Objects; +import java.util.Set; -import static pico.typecheck.PICOAnnotationMirrorHolder.BOTTOM; -import static pico.typecheck.PICOAnnotationMirrorHolder.IMMUTABLE; -import static pico.typecheck.PICOAnnotationMirrorHolder.RECEIVER_DEPENDANT_MUTABLE; +import static pico.typecheck.PICOAnnotationMirrorHolder.*; +import static pico.typecheck.PICOAnnotationMirrorHolder.MUTABLE; /** * Created by mier on 29/09/17. @@ -39,12 +43,8 @@ public Void visitDeclared(AnnotatedDeclaredType type, Tree tree) { checkImplicitlyImmutableTypeError(type, tree); checkOnlyOneAssignabilityModifierOnField(tree); - boolean oldCheck = checkTopLevelDeclaredType; - if(tree instanceof VariableTree && TreeUtils.elementFromDeclaration((VariableTree) tree).getKind() == ElementKind.FIELD) - checkTopLevelDeclaredType = false; - super.visitDeclared(type, tree); - checkTopLevelDeclaredType = oldCheck; - return null; + return super.visitDeclared(type, tree); + } @Override @@ -54,6 +54,28 @@ protected boolean shouldCheckTopLevelDeclaredType(AnnotatedTypeMirror type, Tree PICOAnnotatedTypeFactory.PICOSuperClauseAnnotator.isSuperClause(atypeFactory.getPath(tree))) { return true; } + // allow RDM on mutable fields with enclosing class bounded with mutable + if (tree instanceof VariableTree) { + VariableElement element = TreeUtils.elementFromDeclaration((VariableTree)tree); + if (element.getKind() == ElementKind.FIELD && ElementUtils.enclosingClass(element) != null) { + Set enclosingBound = + atypeFactory.getTypeDeclarationBounds( + Objects.requireNonNull(ElementUtils.enclosingClass(element)).asType()); + + Set declaredBound = + atypeFactory.getTypeDeclarationBounds(type.getUnderlyingType()); + + if(AnnotationUtils.containsSameByName(declaredBound, MUTABLE) + && type.hasAnnotation(RECEIVER_DEPENDANT_MUTABLE) + && AnnotationUtils.containsSameByName(enclosingBound, MUTABLE)) { + return false; + } + } + } +// if (TreeUtils.isLocalVariable(tree)) { +// return true; +// } + return super.shouldCheckTopLevelDeclaredType(type, tree); } diff --git a/src/main/java/pico/typecheck/PICOVisitor.java b/src/main/java/pico/typecheck/PICOVisitor.java index c39c0be..264678b 100644 --- a/src/main/java/pico/typecheck/PICOVisitor.java +++ b/src/main/java/pico/typecheck/PICOVisitor.java @@ -12,6 +12,7 @@ import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.Set; import javax.lang.model.element.AnnotationMirror; @@ -95,6 +96,23 @@ public boolean isValidUse(AnnotatedDeclaredType declarationType, AnnotatedDeclar return true; } +// // allow RDM on mutable fields with enclosing class bounded with mutable +// if (tree instanceof VariableTree && useType.isDeclaration()) { +// VariableElement element = TreeUtils.elementFromDeclaration((VariableTree)tree); +// if (element.getKind() == ElementKind.FIELD && ElementUtils.enclosingClass(element) != null) { +// Set enclosingBound = +// atypeFactory.getTypeDeclarationBounds( +// Objects.requireNonNull(ElementUtils.enclosingClass(element)).asType()); +// +// if(declarationType.hasAnnotation(MUTABLE) +// && useType.hasAnnotation(RECEIVER_DEPENDANT_MUTABLE) +// && AnnotationUtils.containsSameByName(enclosingBound, MUTABLE)) { +// return true; +// } +// } +// +// } + AnnotationMirror declared = declarationType.getAnnotationInHierarchy(READONLY); AnnotationMirror used = useType.getAnnotationInHierarchy(READONLY); @@ -505,25 +523,23 @@ public void processClassTree(ClassTree node) { return;// Doesn't process the class tree anymore } - // field of mutable class cannot use RDM in immutable class - // Condition: - // * Class decl == Immutable - // * Member is field (variable) - // * Member's declared bound == Mutable - // * Member's use anno == RDM - if (bound.hasAnnotation(IMMUTABLE)) { - for(Tree member : node.getMembers()) { - if(member.getKind() == Kind.VARIABLE) { - AnnotatedTypeMirror fieldAtm = atypeFactory.getAnnotatedType(member); - if (fieldAtm.hasAnnotation(RECEIVER_DEPENDANT_MUTABLE) && - AnnotationUtils.containsSameByName(atypeFactory.getTypeDeclarationBounds(fieldAtm.getUnderlyingType()), MUTABLE)) { - checker.reportError(member, "test-key-1"); - } - } - } - } - - +// // field of mutable class cannot use RDM in immutable class +// // Condition: +// // * Class decl == Immutable +// // * Member is field (variable) +// // * Member's declared bound == Mutable +// // * Member's use anno == RDM +// if (bound.hasAnnotation(IMMUTABLE)) { +// for(Tree member : node.getMembers()) { +// if(member.getKind() == Kind.VARIABLE) { +// AnnotatedTypeMirror fieldAtm = atypeFactory.getAnnotatedType(member); +// if (fieldAtm.hasAnnotation(RECEIVER_DEPENDANT_MUTABLE) && +// AnnotationUtils.containsSameByName(atypeFactory.getTypeDeclarationBounds(fieldAtm.getUnderlyingType()), MUTABLE)) { +// checker.reportError(member, "test-key-1"); +// } +// } +// } +// } super.processClassTree(node); } From ef444069691f1a1837f021e8a495cc935c926000 Mon Sep 17 00:00:00 2001 From: lnsun <57457122+lnsun@users.noreply.github.com> Date: Wed, 2 Dec 2020 10:24:44 -0500 Subject: [PATCH 082/144] tc update --- testinput/typecheck/NotEveryInstFieldDefaultToRDM.java | 4 ++-- testinput/typecheck/ObjectIdentityMethodTest.java | 2 +- testinput/typecheck/RDMFieldInst.java | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/testinput/typecheck/NotEveryInstFieldDefaultToRDM.java b/testinput/typecheck/NotEveryInstFieldDefaultToRDM.java index e25d1c3..4923b89 100644 --- a/testinput/typecheck/NotEveryInstFieldDefaultToRDM.java +++ b/testinput/typecheck/NotEveryInstFieldDefaultToRDM.java @@ -7,8 +7,8 @@ public class NotEveryInstFieldDefaultToRDM { // :: error: (assignment.type.incompatible) @ReceiverDependantMutable B b1 = new B(); B b2 = new @ReceiverDependantMutable B(); - C c = new @Mutable C(); - D d = new @Mutable D(); + @Mutable C c = new @Mutable C(); + @Mutable D d = new @Mutable D(); E e = new @Immutable E(); } diff --git a/testinput/typecheck/ObjectIdentityMethodTest.java b/testinput/typecheck/ObjectIdentityMethodTest.java index 04dfb8b..d78080f 100644 --- a/testinput/typecheck/ObjectIdentityMethodTest.java +++ b/testinput/typecheck/ObjectIdentityMethodTest.java @@ -2,7 +2,7 @@ @ReceiverDependantMutable class A { - @Assignable B b; + @Assignable @Mutable B b; @ReceiverDependantMutable A() {} void bar(@Readonly A this) {} } diff --git a/testinput/typecheck/RDMFieldInst.java b/testinput/typecheck/RDMFieldInst.java index fe713cf..62b08f7 100644 --- a/testinput/typecheck/RDMFieldInst.java +++ b/testinput/typecheck/RDMFieldInst.java @@ -13,7 +13,7 @@ private static class RDMBox {} @Immutable // :: error: (initialization.fields.uninitialized) private static class ImmutableClass { - // :: error: (test-key-1) + // :: error: (type.invalid.annotations.on.use) @ReceiverDependantMutable MutableBox mutableBoxInRDM; } @@ -26,7 +26,7 @@ private static class MutableClass { @ReceiverDependantMutable RDMBox rdmBoxInRDMnewI = new @Immutable RDMBox(); // :: error: (assignment.type.incompatible) @ReceiverDependantMutable RDMBox rdmBoxInRDMnewRDM = new @ReceiverDependantMutable RDMBox(); - // :: error: (assignment.type.incompatible) + // :: error: (type.invalid.annotations.on.use) @ReceiverDependantMutable ImmutableBox immutableBoxInRDM = new ImmutableBox(); } From 5a67d2ebdff2334b7cf5d5ac21ac136ce4094a4a Mon Sep 17 00:00:00 2001 From: lnsun <57457122+lnsun@users.noreply.github.com> Date: Wed, 16 Dec 2020 11:32:45 -0500 Subject: [PATCH 083/144] transitive (needs fix) --- .../pico/inference/PICOInferenceVisitor.java | 153 ++++++++++++++++++ 1 file changed, 153 insertions(+) diff --git a/src/main/java/pico/inference/PICOInferenceVisitor.java b/src/main/java/pico/inference/PICOInferenceVisitor.java index b7841b5..d383ee8 100644 --- a/src/main/java/pico/inference/PICOInferenceVisitor.java +++ b/src/main/java/pico/inference/PICOInferenceVisitor.java @@ -8,6 +8,7 @@ import checkers.inference.model.ConstantSlot; import checkers.inference.model.Constraint; import checkers.inference.model.ConstraintManager; +import checkers.inference.model.ImplicationConstraint; import checkers.inference.model.Slot; import com.sun.source.tree.AnnotationTree; import com.sun.source.tree.ArrayAccessTree; @@ -33,6 +34,7 @@ import org.checkerframework.framework.type.AnnotatedTypeMirror.AnnotatedExecutableType; import org.checkerframework.framework.type.QualifierHierarchy; import org.checkerframework.framework.util.AnnotatedTypes; +import org.checkerframework.javacutil.AnnotationUtils; import org.checkerframework.javacutil.BugInCF; import org.checkerframework.javacutil.ElementUtils; import org.checkerframework.javacutil.TreeUtils; @@ -54,7 +56,9 @@ import java.util.Iterator; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.Set; +import java.util.function.Supplier; import static pico.typecheck.PICOAnnotationMirrorHolder.*; @@ -79,6 +83,52 @@ public boolean isValidUse(AnnotatedDeclaredType declarationType, AnnotatedDeclar if (useType.hasAnnotation(BOTTOM)) { return true; } + // allow RDM on mutable fields with enclosing class bounded with mutable + if (tree instanceof VariableTree && !useType.isDeclaration()) { + VariableElement element = TreeUtils.elementFromDeclaration((VariableTree)tree); + if (element.getKind() == ElementKind.FIELD && ElementUtils.enclosingClass(element) != null) { + // assert only 1 bound exists + AnnotationMirror enclosingBound = + atypeFactory.getTypeDeclarationBounds( + Objects.requireNonNull(ElementUtils.enclosingClass(element)).asType()).iterator().next(); + + // if enclosing bound == mutable -> (RDM or Mutable usable on mutable-bounded fields) + // else -> adaptedSubtype + // would be helpful if the solver is SMT and supports "ite" operation + if (infer) { + final ConstraintManager constraintManager = InferenceMain.getInstance().getConstraintManager(); + final SlotManager slotManager = InferenceMain.getInstance().getSlotManager(); + + // cannot use RDM on mutable-bounded fields in immutable classes + // for mutable enclosing class: allow RDM/Mutable on mutable-bounded fields + constraintManager.addImplicationConstraint( + Collections.singletonList( // if decl bound is mutable + constraintManager.createEqualityConstraint(slotManager.getSlot(enclosingBound), + slotManager.getSlot(MUTABLE)) + ), + createRDMOnMutableFieldConstraint(useType, extractVarAnnot(declarationType))); + // for other enclosing class: use adaptedSubtype + constraintManager.addImplicationConstraint( + Collections.singletonList( // if decl bound is mutable + constraintManager.createInequalityConstraint(slotManager.getSlot(enclosingBound), + slotManager.getSlot(MUTABLE)) + ), + createAdaptedSubtypeConstraint(useType, declarationType)); + return true; // always proceed on inference + } + // type-check + return AnnotationUtils.areSame(enclosingBound, MUTABLE) ? + ifAnnoIsThenMainIsOneOf(extractVarAnnot(declarationType), MUTABLE, useType, + new AnnotationMirror[]{MUTABLE, RECEIVER_DEPENDANT_MUTABLE}) : + isAdaptedSubtype(useType, declarationType); +// if(declarationType.hasAnnotation(MUTABLE) +// && useType.hasAnnotation(RECEIVER_DEPENDANT_MUTABLE) +// && AnnotationUtils.containsSameByName(enclosingBound, MUTABLE)) { +// return true; +// } + } + + } return isAdaptedSubtype(useType, declarationType); } @@ -96,6 +146,19 @@ private boolean isAdaptedSubtype(AnnotatedTypeMirror lhs, AnnotatedTypeMirror rh return mainIsSubtype(adapted, extractVarAnnot(lhs)); } + private Constraint createAdaptedSubtypeConstraint(AnnotatedTypeMirror lhs, AnnotatedTypeMirror rhs) { + assert infer; + final ConstraintManager constraintManager = InferenceMain.getInstance().getConstraintManager(); + final SlotManager slotManager = InferenceMain.getInstance().getSlotManager(); + + ExtendedViewpointAdapter vpa = ((ViewpointAdapterGettable)atypeFactory).getViewpointAdapter(); + AnnotatedTypeMirror adapted = vpa.rawCombineAnnotationWithType(extractVarAnnot(lhs), rhs); + return constraintManager.createSubtypeConstraint( + slotManager.getVariableSlot(adapted), + slotManager.getVariableSlot(lhs) + ); + } + @Override public boolean mainIsSubtype(AnnotatedTypeMirror ty, AnnotationMirror mod) { // TODO push change to cfi @@ -196,6 +259,12 @@ private AnnotationMirror extractVarAnnot(final AnnotatedTypeMirror atm) { return atm.getAnnotationInHierarchy(READONLY); } + private AnnotationMirror extractBoundAnno(final AnnotatedTypeMirror atm) { + // assertion: inference only deal with 1 hierarchy, so only 1 anno in bounds + assert atypeFactory.getTypeDeclarationBounds(atm.getUnderlyingType()).size() == 1 : "bound size != 1"; + return atypeFactory.getTypeDeclarationBounds(atm.getUnderlyingType()).iterator().next(); + } + @Override public Void visitVariable(VariableTree node, Void p) { VariableElement element = TreeUtils.elementFromDeclaration(node); @@ -206,9 +275,93 @@ public Void visitVariable(VariableTree node, Void p) { addDeepPreference(type, RECEIVER_DEPENDANT_MUTABLE, 3, node); addDeepPreference(type, IMMUTABLE, 3, node); } + + if (element != null && element.getKind() == ElementKind.FIELD && !ElementUtils.isStatic(element)) { + AnnotatedTypeMirror type = atypeFactory.getAnnotatedType(element); + ifBoundContainsThenMainIsOneOf(type, MUTABLE, + new AnnotationMirror[]{MUTABLE, RECEIVER_DEPENDANT_MUTABLE}); + + + } return super.visitVariable(node, p); } + /** + * + * @param mainAtm a field atm + * @param mutBound declaration bound of mutability + * @return (mutBound == RDM) -> (anno(atm) == RDM | anno(atm) == Mutable) + */ + private Constraint createRDMOnMutableFieldConstraint(AnnotatedTypeMirror mainAtm, AnnotationMirror mutBound) { + final ConstraintManager constraintManager = InferenceMain.getInstance().getConstraintManager(); + final SlotManager slotManager = InferenceMain.getInstance().getSlotManager(); + // A || B <-> !A -> B + Constraint oneOfConst = constraintManager.createImplicationConstraint( + Collections.singletonList(constraintManager.createInequalityConstraint( + slotManager.getSlot(MUTABLE), + slotManager.getVariableSlot(mainAtm))), + constraintManager.createEqualityConstraint( + slotManager.getSlot(RECEIVER_DEPENDANT_MUTABLE), + slotManager.getVariableSlot(mainAtm) + ) + ); + + return constraintManager.createImplicationConstraint( + Collections.singletonList(constraintManager.createEqualityConstraint( + slotManager.getSlot(mutBound), + slotManager.getSlot(MUTABLE))), + oneOfConst + ); + } + + /** + * + * @param atm + * @param contains + * @param oneOf + */ + public boolean ifBoundContainsThenMainIsOneOf(AnnotatedTypeMirror atm, AnnotationMirror contains, + AnnotationMirror[] oneOf) { + + AnnotationMirror boundAnno = extractBoundAnno(atm); + return ifAnnoIsThenMainIsOneOf(boundAnno, contains, atm, oneOf); + + } + + public boolean ifAnnoIsThenMainIsOneOf(AnnotationMirror annotation, AnnotationMirror is, + AnnotatedTypeMirror mainAtm, AnnotationMirror[] oneOf) { + // TODO support more annotations + assert oneOf.length == 2: "oneOf.length should be 2"; + if (this.infer) { + final ConstraintManager constraintManager = InferenceMain.getInstance().getConstraintManager(); + final SlotManager slotManager = InferenceMain.getInstance().getSlotManager(); + Constraint oneOfConst = constraintManager.createImplicationConstraint( + Collections.singletonList(constraintManager.createInequalityConstraint( + slotManager.getSlot(oneOf[0]), + slotManager.getVariableSlot(mainAtm))), + constraintManager.createEqualityConstraint( + slotManager.getSlot(oneOf[1]), + slotManager.getVariableSlot(mainAtm) + ) + ); + + constraintManager.addImplicationConstraint( + Collections.singletonList(constraintManager.createEqualityConstraint( + slotManager.getSlot(annotation), + slotManager.getSlot(is))), + oneOfConst + ); + return true; // always return true on inference + } else { + if (AnnotationUtils.areSameByName(annotation, is)) { + return AnnotationUtils.containsSameByName(Arrays.asList(oneOf), + mainAtm.getAnnotationInHierarchy(READONLY)); + } + } + return true; + } + + @Override public Void visitMethod(MethodTree node, Void p) { AnnotatedExecutableType executableType = atypeFactory.getAnnotatedType(node); From fc6cf0b2c45810025154a7dafeb4f747312b49b6 Mon Sep 17 00:00:00 2001 From: lnsun <57457122+lnsun@users.noreply.github.com> Date: Thu, 14 Jan 2021 15:25:38 -0500 Subject: [PATCH 084/144] class bound != PM --- src/main/java/pico/inference/PICOInferenceVisitor.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/main/java/pico/inference/PICOInferenceVisitor.java b/src/main/java/pico/inference/PICOInferenceVisitor.java index d383ee8..bde5ad5 100644 --- a/src/main/java/pico/inference/PICOInferenceVisitor.java +++ b/src/main/java/pico/inference/PICOInferenceVisitor.java @@ -366,6 +366,8 @@ public boolean ifAnnoIsThenMainIsOneOf(AnnotationMirror annotation, AnnotationMi public Void visitMethod(MethodTree node, Void p) { AnnotatedExecutableType executableType = atypeFactory.getAnnotatedType(node); AnnotatedDeclaredType bound = PICOTypeUtil.getBoundTypeOfEnclosingTypeDeclaration(node, atypeFactory); + assert bound != null; + assert !bound.hasAnnotation(POLY_MUTABLE) : "BOUND CANNOT BE POLY"; if (TreeUtils.isConstructor(node)) { // Doesn't check anonymous constructor case @@ -815,6 +817,7 @@ public void processClassTree(ClassTree node) { AnnotatedDeclaredType bound = PICOTypeUtil.getBoundTypeOfTypeDeclaration(typeElement, atypeFactory); mainIsNot(bound, READONLY, "class.bound.invalid", node); + mainIsNot(bound, POLY_MUTABLE, "class.bound.invalid", node); if (checker.hasOption("optimalSolution")) { addPreference(bound, RECEIVER_DEPENDANT_MUTABLE, 2); addPreference(bound, IMMUTABLE, 2); From d8e11950510c93b94c7ae67aa856923c909ee295 Mon Sep 17 00:00:00 2001 From: lnsun <57457122+lnsun@users.noreply.github.com> Date: Thu, 14 Jan 2021 15:59:39 -0500 Subject: [PATCH 085/144] receiver != BOTTOM --- src/main/java/pico/inference/PICOInferenceVisitor.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/java/pico/inference/PICOInferenceVisitor.java b/src/main/java/pico/inference/PICOInferenceVisitor.java index bde5ad5..42a65cb 100644 --- a/src/main/java/pico/inference/PICOInferenceVisitor.java +++ b/src/main/java/pico/inference/PICOInferenceVisitor.java @@ -408,7 +408,8 @@ public Void visitMethod(MethodTree node, Void p) { } // Above is additional preference logic if (declaredReceiverType != null) { - assert bound != null; + mainIsNot(declaredReceiverType, BOTTOM, "bottom.on.receiver", node); +// mainIsNot(declaredReceiverType, POLY_MUTABLE, "poly.on.receiver", node); if (!isAdaptedSubtype(declaredReceiverType, bound)){ checker.report(Result.failure("method.receiver.incompatible", declaredReceiverType), node); } From f5972fdb972dbf4f9f32c5928edfe6777d68e938 Mon Sep 17 00:00:00 2001 From: lnsun <57457122+lnsun@users.noreply.github.com> Date: Thu, 14 Jan 2021 20:11:28 -0500 Subject: [PATCH 086/144] stop assign immutable constant slot to enum usage. enum could be mutable --- .../pico/inference/PICOVariableAnnotator.java | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/main/java/pico/inference/PICOVariableAnnotator.java b/src/main/java/pico/inference/PICOVariableAnnotator.java index c8e18b8..0e1a731 100644 --- a/src/main/java/pico/inference/PICOVariableAnnotator.java +++ b/src/main/java/pico/inference/PICOVariableAnnotator.java @@ -68,12 +68,12 @@ protected void handleClassDeclarationBound(AnnotatedDeclaredType classType) { VariableSlot boundSlot; // Insert @Immutable VarAnnot directly to enum bound - if (PICOTypeUtil.isEnumOrEnumConstant(bound)) { - boundSlot = slotManager.createConstantSlot(IMMUTABLE); - classType.addAnnotation(slotManager.getAnnotation(boundSlot)); - classDeclAnnos.put(classElement, boundSlot); - return; - } +// if (PICOTypeUtil.isEnumOrEnumConstant(bound)) { +// boundSlot = slotManager.createConstantSlot(IMMUTABLE); +// classType.addAnnotation(slotManager.getAnnotation(boundSlot)); +// classDeclAnnos.put(classElement, boundSlot); +// return; +// } Tree classTree = inferenceTypeFactory.declarationFromElement(classElement); if (classTree != null) { @@ -113,10 +113,10 @@ protected void handleInstantiationConstraint(AnnotatedTypeMirror.AnnotatedDeclar @Override protected VariableSlot addPrimaryVariable(AnnotatedTypeMirror atm, Tree tree) { - if (PICOTypeUtil.isEnumOrEnumConstant(atm)) { - // Don't add new VarAnnot to type use of enum type - PICOTypeUtil.applyConstant(atm, IMMUTABLE); - } +// if (PICOTypeUtil.isEnumOrEnumConstant(atm)) { +// // Don't add new VarAnnot to type use of enum type +// PICOTypeUtil.applyConstant(atm, IMMUTABLE); +// } return super.addPrimaryVariable(atm, tree); } From f1acb77523eabdca29126833b93d00dd7493d264 Mon Sep 17 00:00:00 2001 From: lnsun <57457122+lnsun@users.noreply.github.com> Date: Fri, 15 Jan 2021 17:58:41 -0500 Subject: [PATCH 087/144] PICO does not need existential slot on class decl anno --- .../pico/inference/PICOVariableAnnotator.java | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/src/main/java/pico/inference/PICOVariableAnnotator.java b/src/main/java/pico/inference/PICOVariableAnnotator.java index 0e1a731..5cc7428 100644 --- a/src/main/java/pico/inference/PICOVariableAnnotator.java +++ b/src/main/java/pico/inference/PICOVariableAnnotator.java @@ -8,10 +8,14 @@ import java.util.Arrays; import java.util.List; +import javax.lang.model.element.AnnotationMirror; import javax.lang.model.element.Element; import javax.lang.model.element.TypeElement; import javax.lang.model.type.TypeKind; +import checkers.inference.model.ExistentialVariableSlot; +import checkers.inference.model.Slot; +import com.sun.tools.javac.code.Symbol; import org.checkerframework.framework.type.AnnotatedTypeFactory; import org.checkerframework.framework.type.AnnotatedTypeMirror; import org.checkerframework.framework.type.AnnotatedTypeMirror.AnnotatedDeclaredType; @@ -105,6 +109,19 @@ protected void handleClassDeclarationBound(AnnotatedDeclaredType classType) { classDeclAnnos.put(classElement, boundSlot); } + @Override + public void storeElementType(Element element, AnnotatedTypeMirror atm) { + Slot slot = slotManager.getVariableSlot(atm); + // do not use potential slot generated on the class decl annotation + // PICO always have a annotation on the class bound, so Existential should always exist + // TODO make VariableAnnotator::getOrCreateDeclBound protected and override that instead of this method + if (element instanceof Symbol.ClassSymbol && slot instanceof ExistentialVariableSlot) { + AnnotationMirror potential = slotManager.getAnnotation(((ExistentialVariableSlot) slot).getPotentialSlot()); + atm.replaceAnnotation(potential); + } + super.storeElementType(element, atm); + } + // Don't generate subtype constraint between use type and bound type @Override protected void handleInstantiationConstraint(AnnotatedTypeMirror.AnnotatedDeclaredType adt, VariableSlot instantiationSlot, Tree tree) { From a2f5726a785c97fb5e4ba42030c97c5b3655b550 Mon Sep 17 00:00:00 2001 From: lnsun <57457122+lnsun@users.noreply.github.com> Date: Wed, 20 Jan 2021 11:43:29 -0500 Subject: [PATCH 088/144] use constant slot for explicit class decl anno --- .../pico/inference/PICOVariableAnnotator.java | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/src/main/java/pico/inference/PICOVariableAnnotator.java b/src/main/java/pico/inference/PICOVariableAnnotator.java index 5cc7428..3d400ff 100644 --- a/src/main/java/pico/inference/PICOVariableAnnotator.java +++ b/src/main/java/pico/inference/PICOVariableAnnotator.java @@ -109,8 +109,25 @@ protected void handleClassDeclarationBound(AnnotatedDeclaredType classType) { classDeclAnnos.put(classElement, boundSlot); } + @Override + protected VariableSlot getOrCreateDeclBound(AnnotatedDeclaredType type) { + // if a explicit annotation presents on the class decl, use that directly + if (type.isAnnotatedInHierarchy(READONLY)) { + VariableSlot constantSlot = (VariableSlot) slotManager.getSlot(type.getAnnotationInHierarchy(READONLY)); + TypeElement classDecl = (TypeElement) type.getUnderlyingType().asElement(); + classDeclAnnos.put(classDecl, constantSlot); + // avoid duplicate annos + type.removeAnnotationInHierarchy(READONLY); + return constantSlot; + } + return super.getOrCreateDeclBound(type); + } + @Override public void storeElementType(Element element, AnnotatedTypeMirror atm) { + // this method is override the behavior of super.handleClassDeclaration before storing + // find a better way + Slot slot = slotManager.getVariableSlot(atm); // do not use potential slot generated on the class decl annotation // PICO always have a annotation on the class bound, so Existential should always exist From 10d1e17d7823f9abe869b2d66dcb305dff9afb44 Mon Sep 17 00:00:00 2001 From: lnsun <57457122+lnsun@users.noreply.github.com> Date: Wed, 20 Jan 2021 13:28:08 -0500 Subject: [PATCH 089/144] update init bound extraction function --- .../java/pico/inference/PICOInferenceVisitor.java | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/src/main/java/pico/inference/PICOInferenceVisitor.java b/src/main/java/pico/inference/PICOInferenceVisitor.java index 42a65cb..5e6448a 100644 --- a/src/main/java/pico/inference/PICOInferenceVisitor.java +++ b/src/main/java/pico/inference/PICOInferenceVisitor.java @@ -259,10 +259,16 @@ private AnnotationMirror extractVarAnnot(final AnnotatedTypeMirror atm) { return atm.getAnnotationInHierarchy(READONLY); } - private AnnotationMirror extractBoundAnno(final AnnotatedTypeMirror atm) { - // assertion: inference only deal with 1 hierarchy, so only 1 anno in bounds - assert atypeFactory.getTypeDeclarationBounds(atm.getUnderlyingType()).size() == 1 : "bound size != 1"; - return atypeFactory.getTypeDeclarationBounds(atm.getUnderlyingType()).iterator().next(); + /** + * Extract the declaration initialization bound of a certain atm. + * Return the slot generated during inference. + * @param atm any AnnotatedDeclaredType + * @return the initialization bound on the class declaration of the type (actual or slot annotation) + */ + private AnnotationMirror extractInitBoundAnno(final AnnotatedDeclaredType atm) { + Element tm = atypeFactory.getProcessingEnv().getTypeUtils().asElement(atm.getUnderlyingType()); + assert tm instanceof TypeElement; + return extractVarAnnot(PICOTypeUtil.getBoundTypeOfTypeDeclaration((TypeElement) tm, atypeFactory)); } @Override From 6c31de8638ebd33cd7163d00cef83a62617d0e4c Mon Sep 17 00:00:00 2001 From: lnsun <57457122+lnsun@users.noreply.github.com> Date: Wed, 20 Jan 2021 13:30:08 -0500 Subject: [PATCH 090/144] update call to init bound extraction function --- src/main/java/pico/inference/PICOInferenceVisitor.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/pico/inference/PICOInferenceVisitor.java b/src/main/java/pico/inference/PICOInferenceVisitor.java index 5e6448a..abbebf9 100644 --- a/src/main/java/pico/inference/PICOInferenceVisitor.java +++ b/src/main/java/pico/inference/PICOInferenceVisitor.java @@ -326,10 +326,10 @@ private Constraint createRDMOnMutableFieldConstraint(AnnotatedTypeMirror mainAtm * @param contains * @param oneOf */ - public boolean ifBoundContainsThenMainIsOneOf(AnnotatedTypeMirror atm, AnnotationMirror contains, + public boolean ifBoundContainsThenMainIsOneOf(AnnotatedDeclaredType atm, AnnotationMirror contains, AnnotationMirror[] oneOf) { - AnnotationMirror boundAnno = extractBoundAnno(atm); + AnnotationMirror boundAnno = extractInitBoundAnno(atm); return ifAnnoIsThenMainIsOneOf(boundAnno, contains, atm, oneOf); } From 0254063b7d34fcb46c1561da9ee1924ca38d021f Mon Sep 17 00:00:00 2001 From: lnsun <57457122+lnsun@users.noreply.github.com> Date: Wed, 20 Jan 2021 13:32:40 -0500 Subject: [PATCH 091/144] prevent poly infer --- .../pico/inference/PICOInferenceVisitor.java | 28 ++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/src/main/java/pico/inference/PICOInferenceVisitor.java b/src/main/java/pico/inference/PICOInferenceVisitor.java index abbebf9..40c1311 100644 --- a/src/main/java/pico/inference/PICOInferenceVisitor.java +++ b/src/main/java/pico/inference/PICOInferenceVisitor.java @@ -367,6 +367,29 @@ public boolean ifAnnoIsThenMainIsOneOf(AnnotationMirror annotation, AnnotationMi return true; } + /** + * Make the main annotation on {@code atm} cannot infer to given {@code anno}. + * But the written annotation still have effect. + *

+ * A notable use could be poly annotations which could be used by inference if explicitly present, + * but new poly cannot be inferred. + *

+ * @param atm the type which should not inferred to given anno + * @param anno the anno that cannot be inferred to + * @param errorKey this will show only if things goes wrong and result into a error message in type-check. + * @param tree this will show only if things goes wrong and result into a error message in type-check. + */ + public void mainCannotInferTo(AnnotatedTypeMirror atm, AnnotationMirror anno, String errorKey, Tree tree) { + if (infer) { + SlotManager slotManager = InferenceMain.getInstance().getSlotManager(); + // should be constant slot if written explicitly in code + if (!(slotManager.getVariableSlot(atm) instanceof ConstantSlot)) { + mainIsNot(atm, anno, errorKey, tree); + } + + } + } + @Override public Void visitMethod(MethodTree node, Void p) { @@ -375,6 +398,9 @@ public Void visitMethod(MethodTree node, Void p) { assert bound != null; assert !bound.hasAnnotation(POLY_MUTABLE) : "BOUND CANNOT BE POLY"; + // cannot infer poly, but can use it for type-check. + mainCannotInferTo(executableType.getReturnType(), POLY_MUTABLE, "cannot.infer.poly", node); + if (TreeUtils.isConstructor(node)) { // Doesn't check anonymous constructor case if (TreeUtils.isAnonymousConstructor(node)) { @@ -751,7 +777,7 @@ public Void visitNewArray(NewArrayTree node, Void p) { private void checkNewInstanceCreation(Tree node) { AnnotatedTypeMirror type = atypeFactory.getAnnotatedType(node); // Ensure only @Mutable/@Immutable/@ReceiverDependantMutable are inferred on new instance creation - mainIsNoneOf(type, new AnnotationMirror[]{READONLY}, "pico.new.invalid", node); + mainIsNoneOf(type, new AnnotationMirror[]{READONLY, POLY_MUTABLE}, "pico.new.invalid", node); } // Completely copied from PICOVisitor From 31ff749d08358591b11db1affb76bc3f76687710 Mon Sep 17 00:00:00 2001 From: lnsun <57457122+lnsun@users.noreply.github.com> Date: Fri, 22 Jan 2021 16:40:15 -0500 Subject: [PATCH 092/144] bound handling for anonymous class --- .../java/pico/inference/PICOVariableAnnotator.java | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/main/java/pico/inference/PICOVariableAnnotator.java b/src/main/java/pico/inference/PICOVariableAnnotator.java index 3d400ff..8f6f2ee 100644 --- a/src/main/java/pico/inference/PICOVariableAnnotator.java +++ b/src/main/java/pico/inference/PICOVariableAnnotator.java @@ -15,6 +15,7 @@ import checkers.inference.model.ExistentialVariableSlot; import checkers.inference.model.Slot; +import checkers.inference.qual.VarAnnot; import com.sun.tools.javac.code.Symbol; import org.checkerframework.framework.type.AnnotatedTypeFactory; import org.checkerframework.framework.type.AnnotatedTypeMirror; @@ -35,6 +36,7 @@ import checkers.inference.model.ConstraintManager; import checkers.inference.model.VariableSlot; import checkers.inference.model.tree.ArtificialExtendsBoundTree; +import org.checkerframework.javacutil.TypesUtils; import pico.common.PICOTypeUtil; public class PICOVariableAnnotator extends VariableAnnotator { @@ -120,6 +122,16 @@ protected VariableSlot getOrCreateDeclBound(AnnotatedDeclaredType type) { type.removeAnnotationInHierarchy(READONLY); return constantSlot; } + + // new class tree of an anonymous class is always visited before (enclosing tree). + // slot should be generated then. + // use that slot and avoid generating a new slot. + // push this change to inference IFF the slot on new class have same requirement with class bound + // e.g. existence slot on new class tree? + if (TypesUtils.isAnonymous(type.getUnderlyingType())) { + assert type.hasAnnotation(VarAnnot.class); + return (VariableSlot) slotManager.getSlot(type.getAnnotation(VarAnnot.class)); + } return super.getOrCreateDeclBound(type); } From 4f41dc4c7e8e8db8d3f8ace16f8242cf11f2a150 Mon Sep 17 00:00:00 2001 From: lnsun <57457122+lnsun@users.noreply.github.com> Date: Fri, 22 Jan 2021 16:46:01 -0500 Subject: [PATCH 093/144] anonymous class method receiver workaround --- src/main/java/pico/inference/PICOInferenceVisitor.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/main/java/pico/inference/PICOInferenceVisitor.java b/src/main/java/pico/inference/PICOInferenceVisitor.java index 40c1311..db44c0c 100644 --- a/src/main/java/pico/inference/PICOInferenceVisitor.java +++ b/src/main/java/pico/inference/PICOInferenceVisitor.java @@ -921,7 +921,9 @@ protected void commonAssignmentCheck(AnnotatedTypeMirror varType, // TODO: WORKAROUND: anonymous class handling if (TypesUtils.isAnonymous(valueType.getUnderlyingType())) { AnnotatedTypeMirror newValueType = varType.deepCopy(); - newValueType.replaceAnnotation(valueType.getAnnotationInHierarchy(READONLY)); + newValueType.clearAnnotations(); + newValueType.addAnnotation(extractVarAnnot(valueType)); + valueType = newValueType; } super.commonAssignmentCheck(varType, valueType, valueTree, errorKey); From a1523233992b738c1afdfbc651ff7be395dd3f7e Mon Sep 17 00:00:00 2001 From: lnsun <57457122+lnsun@users.noreply.github.com> Date: Fri, 22 Jan 2021 21:51:26 -0500 Subject: [PATCH 094/144] workarounds for anonymous class for getTypeOfExtendsImplements --- .../java/pico/inference/PICOInferenceRealTypeFactory.java | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/main/java/pico/inference/PICOInferenceRealTypeFactory.java b/src/main/java/pico/inference/PICOInferenceRealTypeFactory.java index 340787e..38103f9 100644 --- a/src/main/java/pico/inference/PICOInferenceRealTypeFactory.java +++ b/src/main/java/pico/inference/PICOInferenceRealTypeFactory.java @@ -15,6 +15,7 @@ import javax.lang.model.type.TypeKind; import javax.lang.model.type.TypeMirror; +import com.sun.tools.javac.tree.JCTree; import org.checkerframework.common.basetype.BaseAnnotatedTypeFactory; import org.checkerframework.common.basetype.BaseTypeChecker; import org.checkerframework.framework.qual.RelevantJavaTypes; @@ -182,6 +183,13 @@ protected DefaultQualifierForUseTypeAnnotator createDefaultForUseTypeAnnotator() public AnnotatedTypeMirror getTypeOfExtendsImplements(Tree clause) { // add default anno from class main qual, if no qual present AnnotatedTypeMirror enclosing = getAnnotatedType(TreeUtils.enclosingClass(getPath(clause))); + + // workaround for anonymous class. + // TypesUtils::isAnonymous won't work when annotation presents on new class tree! + if(getPath(clause).getParentPath().getLeaf() instanceof JCTree.JCNewClass) { + enclosing = getAnnotatedType(getPath(clause).getParentPath().getLeaf()); + + } AnnotationMirror mainBound = enclosing.getAnnotationInHierarchy(READONLY); AnnotatedTypeMirror fromTypeTree = this.fromTypeTree(clause); if (!fromTypeTree.isAnnotatedInHierarchy(READONLY)) { From 1c174371b5b9142670ca0dc0f6d8756378b5c34f Mon Sep 17 00:00:00 2001 From: lnsun <57457122+lnsun@users.noreply.github.com> Date: Mon, 25 Jan 2021 10:13:42 -0500 Subject: [PATCH 095/144] extract method --- .../pico/inference/PICOInferenceVisitor.java | 24 ++++++++++++------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/src/main/java/pico/inference/PICOInferenceVisitor.java b/src/main/java/pico/inference/PICOInferenceVisitor.java index db44c0c..b92caea 100644 --- a/src/main/java/pico/inference/PICOInferenceVisitor.java +++ b/src/main/java/pico/inference/PICOInferenceVisitor.java @@ -301,8 +301,23 @@ public Void visitVariable(VariableTree node, Void p) { private Constraint createRDMOnMutableFieldConstraint(AnnotatedTypeMirror mainAtm, AnnotationMirror mutBound) { final ConstraintManager constraintManager = InferenceMain.getInstance().getConstraintManager(); final SlotManager slotManager = InferenceMain.getInstance().getSlotManager(); + + Constraint oneOfConst = createMainIsMutableOrRdmConstraint(mainAtm); + + return constraintManager.createImplicationConstraint( + Collections.singletonList(constraintManager.createEqualityConstraint( + slotManager.getSlot(mutBound), + slotManager.getSlot(MUTABLE))), + oneOfConst + ); + } + + private Constraint createMainIsMutableOrRdmConstraint(AnnotatedTypeMirror mainAtm) { + assert infer; + final ConstraintManager constraintManager = InferenceMain.getInstance().getConstraintManager(); + final SlotManager slotManager = InferenceMain.getInstance().getSlotManager(); // A || B <-> !A -> B - Constraint oneOfConst = constraintManager.createImplicationConstraint( + return constraintManager.createImplicationConstraint( Collections.singletonList(constraintManager.createInequalityConstraint( slotManager.getSlot(MUTABLE), slotManager.getVariableSlot(mainAtm))), @@ -311,13 +326,6 @@ private Constraint createRDMOnMutableFieldConstraint(AnnotatedTypeMirror mainAtm slotManager.getVariableSlot(mainAtm) ) ); - - return constraintManager.createImplicationConstraint( - Collections.singletonList(constraintManager.createEqualityConstraint( - slotManager.getSlot(mutBound), - slotManager.getSlot(MUTABLE))), - oneOfConst - ); } /** From cf645869a27c74d81fef7bdc689f42bf6f57ca48 Mon Sep 17 00:00:00 2001 From: lnsun <57457122+lnsun@users.noreply.github.com> Date: Mon, 25 Jan 2021 18:51:06 -0500 Subject: [PATCH 096/144] do not apply real anno to atm during infer --- src/main/java/pico/common/PICOTypeUtil.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/pico/common/PICOTypeUtil.java b/src/main/java/pico/common/PICOTypeUtil.java index ced8cc1..dbfe606 100644 --- a/src/main/java/pico/common/PICOTypeUtil.java +++ b/src/main/java/pico/common/PICOTypeUtil.java @@ -314,7 +314,7 @@ public static void applyConstant(AnnotatedTypeMirror type, AnnotationMirror am) // back to the original source code, BUT this ConstantSlot(representing @Immutable) will be used for constraint generation // that affects the solutions for other VariableSlots type.addAnnotation(slotManager.getAnnotation(constant));// Insert Constant VarAnnot that represents @Immutable - type.addAnnotation(am);// Insert real @Immutable. This should be removed if INF-FR only uses VarAnnot +// type.addAnnotation(am);// Insert real @Immutable. This should be removed if INF-FR only uses VarAnnot } else { constraintManager.addEqualityConstraint(shouldBeAppliedTo, constant); } From df55010fbff3b2341b72ca7a3d735ca042568013 Mon Sep 17 00:00:00 2001 From: lnsun <57457122+lnsun@users.noreply.github.com> Date: Mon, 25 Jan 2021 19:32:10 -0500 Subject: [PATCH 097/144] not apply immutable constant to enum during infer-typecheck --- src/main/java/pico/inference/PICOInferenceRealTypeFactory.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/pico/inference/PICOInferenceRealTypeFactory.java b/src/main/java/pico/inference/PICOInferenceRealTypeFactory.java index 38103f9..1511a00 100644 --- a/src/main/java/pico/inference/PICOInferenceRealTypeFactory.java +++ b/src/main/java/pico/inference/PICOInferenceRealTypeFactory.java @@ -136,7 +136,7 @@ public boolean getShouldDefaultTypeVarLocals() { public void addComputedTypeAnnotations(Element elt, AnnotatedTypeMirror type) { PICOTypeUtil.addDefaultForField(this, type, elt); PICOTypeUtil.defaultConstructorReturnToClassBound(this, elt, type); - PICOTypeUtil.applyImmutableToEnumAndEnumConstant(type); +// PICOTypeUtil.applyImmutableToEnumAndEnumConstant(type); super.addComputedTypeAnnotations(elt, type); } From e4cbe5f7599a80d3cd17a07e2e9fc595e3c6856c Mon Sep 17 00:00:00 2001 From: lnsun <57457122+lnsun@users.noreply.github.com> Date: Tue, 26 Jan 2021 20:10:56 -0500 Subject: [PATCH 098/144] add init bound extraction function --- .../pico/inference/PICOInferenceVisitor.java | 58 +++++++++++++++++++ 1 file changed, 58 insertions(+) diff --git a/src/main/java/pico/inference/PICOInferenceVisitor.java b/src/main/java/pico/inference/PICOInferenceVisitor.java index b92caea..c6bcfd8 100644 --- a/src/main/java/pico/inference/PICOInferenceVisitor.java +++ b/src/main/java/pico/inference/PICOInferenceVisitor.java @@ -851,6 +851,7 @@ public void processClassTree(ClassTree node) { TypeElement typeElement = TreeUtils.elementFromDeclaration(node); // Don't process anonymous class. if (TypesUtils.isAnonymous(TreeUtils.typeOf(node))) { + checkAnonymousImplements(node, PICOTypeUtil.getBoundTypeOfTypeDeclaration(typeElement, atypeFactory)); super.processClassTree(node); return; } @@ -864,10 +865,67 @@ public void processClassTree(ClassTree node) { addPreference(bound, IMMUTABLE, 2); } + checkSuperClauseEquals(node, bound); // Always reach this point. Do not suppress errors. super.processClassTree(node); } + /** + * The base visitor does not use inference method to check! + * This method is required to add the constraints for extends / implements. + * @param node + */ + private void checkAnonymousImplements(ClassTree node, AnnotatedDeclaredType bound) { + // NOTE: this is a workaround for checking bound against extends/implements + // After inferring annotation CF cannot skip the anonymous class, thus necessary + + assert TypesUtils.isAnonymous(TreeUtils.typeOf(node)); + + if (infer) { + Tree superClause; + if (node.getExtendsClause() != null) { + superClause = node.getExtendsClause(); + } else if (node.getImplementsClause() != null) { + // a anonymous class cannot have both extends or implements + assert node.getImplementsClause().size() == 1; // anonymous class only implement 1 interface + superClause = node.getImplementsClause().get(0); + + } else { + throw new BugInCF("Anonymous class with no extending/implementing clause!"); + } + AnnotationMirror superBound = extractInitBoundAnno((AnnotatedDeclaredType) atypeFactory.getAnnotatedType(superClause)); + // anonymous cannot have implement clause, so no "use" anno of super type + mainIsSubtype(bound, superBound); + } + } + + + /** + * extends/implements clause use anno == bound anno + *

Could be subtype, but recall Readonly and Bottom is not usable on class init bound.

+ * @param node + * @param bound + */ + private void checkSuperClauseEquals(ClassTree node, AnnotatedDeclaredType bound) { + if (node.getExtendsClause() != null) { + AnnotatedTypeMirror ext = atypeFactory.getAnnotatedType(node.getExtendsClause()); + boundVsExtImpClause(bound, ext, "extends.bound.incompatible", node.getExtendsClause()); + } + for (Tree impTree : node.getImplementsClause()) { + AnnotatedTypeMirror impType = atypeFactory.getAnnotatedType(impTree); + boundVsExtImpClause(bound, impType, "implements.bound.incompatible", impTree); + } + } + + + private void boundVsExtImpClause(AnnotatedDeclaredType classBound, AnnotatedTypeMirror superType, String errorKey, Tree tree) { + AnnotationMirror superAnno = extractVarAnnot(superType); + if (superAnno != null) { + annoIs(classBound, extractVarAnnot(classBound), superAnno, errorKey, tree); + } + } + + private boolean checkCompatabilityBetweenBoundAndSuperClassesBounds(ClassTree node, TypeElement typeElement, AnnotatedDeclaredType bound) { // Must have compatible bound annotation as the direct super types List superBounds = PICOTypeUtil.getBoundTypesOfDirectSuperTypes(typeElement, atypeFactory); From 50e3ceac1d797076d609e567cff185a29877dd25 Mon Sep 17 00:00:00 2001 From: lnsun <57457122+lnsun@users.noreply.github.com> Date: Tue, 23 Feb 2021 09:13:07 -0500 Subject: [PATCH 099/144] update tc --- testinput/typecheck/RDMField.java | 2 ++ testinput/typecheck/Transitive.java | 12 ++++++------ 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/testinput/typecheck/RDMField.java b/testinput/typecheck/RDMField.java index 985db9b..3312e8a 100644 --- a/testinput/typecheck/RDMField.java +++ b/testinput/typecheck/RDMField.java @@ -10,6 +10,7 @@ private static class MutableClass { @ReceiverDependantMutable private static class RDMHolder { + // :: error: (type.invalid.annotations.on.use) @ReceiverDependantMutable MutableClass field = new MutableClass(); @Mutable MutableClass mutableField = new MutableClass(); @@ -33,6 +34,7 @@ void asImmutable(@Immutable RDMHolder r) { @Immutable private static class ImmutableHolder { + // :: error: (type.invalid.annotations.on.use) @ReceiverDependantMutable MutableClass field = new MutableClass(); public @PolyMutable MutableClass getField(@PolyMutable ImmutableHolder this) { diff --git a/testinput/typecheck/Transitive.java b/testinput/typecheck/Transitive.java index ec88180..8210ec7 100644 --- a/testinput/typecheck/Transitive.java +++ b/testinput/typecheck/Transitive.java @@ -27,18 +27,18 @@ static class C { static class Caller { void test(@Readonly A a) { - // error + // :: error: (illegal.field.write) a.b.field = 1; - // error + // :: error: (method.invocation.invalid) a.getB().field = 1; - // error + // :: error: (illegal.field.write) a.b.c.field = 1; - // error + // :: error: (method.invocation.invalid) a.getB().getC().field = 1; - // error + // :: error: (method.invocation.invalid) a.b.getC().field = 1; - // error + // :: error: (method.invocation.invalid) a.getB().c.field = 1; } } From 5a9e8ca08834d9b49ff7e90ecccfdc5432d788f2 Mon Sep 17 00:00:00 2001 From: lnsun <57457122+lnsun@users.noreply.github.com> Date: Wed, 24 Feb 2021 11:01:54 -0500 Subject: [PATCH 100/144] array bound -> RDM (TC) --- .../java/pico/typecheck/PICOAnnotatedTypeFactory.java | 2 +- src/main/java/pico/typecheck/PICOVisitor.java | 8 ++++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/src/main/java/pico/typecheck/PICOAnnotatedTypeFactory.java b/src/main/java/pico/typecheck/PICOAnnotatedTypeFactory.java index 3246413..0167ce3 100644 --- a/src/main/java/pico/typecheck/PICOAnnotatedTypeFactory.java +++ b/src/main/java/pico/typecheck/PICOAnnotatedTypeFactory.java @@ -428,7 +428,7 @@ public AnnotationMirror getTypeDeclarationBoundForMutability(TypeMirror type) { return IMMUTABLE; } if (type.getKind() == TypeKind.ARRAY) { - return READONLY; // if decided to use vpa for array, return RDM. + return RECEIVER_DEPENDANT_MUTABLE; // if decided to use vpa for array, return RDM. } // IMMUTABLE for enum w/o decl anno diff --git a/src/main/java/pico/typecheck/PICOVisitor.java b/src/main/java/pico/typecheck/PICOVisitor.java index 264678b..4ad646d 100644 --- a/src/main/java/pico/typecheck/PICOVisitor.java +++ b/src/main/java/pico/typecheck/PICOVisitor.java @@ -119,6 +119,14 @@ public boolean isValidUse(AnnotatedDeclaredType declarationType, AnnotatedDeclar return isAdaptedSubtype(used, declared); } + @Override + public boolean isValidUse(AnnotatedTypeMirror.AnnotatedArrayType type, Tree tree) { + // You don't need adapted subtype if the decl bound is guaranteed to be RDM. + // That simply means that any use is valid except bottom. + AnnotationMirror used = type.getAnnotationInHierarchy(READONLY); + return !AnnotationUtils.areSame(used, BOTTOM); + } + static private boolean isAnnoValidUse(AnnotationMirror declared, AnnotationMirror used) { if (AnnotationUtils.areSame(declared, RECEIVER_DEPENDANT_MUTABLE) || AnnotationUtils.areSame(declared, READONLY)) { // Element is declared with @ReceiverDependantMutable bound, any instantiation is allowed. We don't use From 11834c1076a3813526bab7c3b5734c4b26ba9c7b Mon Sep 17 00:00:00 2001 From: lnsun <57457122+lnsun@users.noreply.github.com> Date: Sat, 27 Feb 2021 15:21:32 -0500 Subject: [PATCH 101/144] exclude static inner class decl from static scope --- src/main/java/pico/inference/PICOInferenceValidator.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/main/java/pico/inference/PICOInferenceValidator.java b/src/main/java/pico/inference/PICOInferenceValidator.java index aca8a88..dd2d0fb 100644 --- a/src/main/java/pico/inference/PICOInferenceValidator.java +++ b/src/main/java/pico/inference/PICOInferenceValidator.java @@ -74,7 +74,9 @@ protected boolean shouldCheckTopLevelDeclaredType(AnnotatedTypeMirror type, Tree } private void checkStaticReceiverDependantMutableError(AnnotatedTypeMirror type, Tree tree) { - if (PICOTypeUtil.inStaticScope(visitor.getCurrentPath())) { + // Static inner class is considered within the static scope. + // Added condition to ensure not class decl. + if (PICOTypeUtil.inStaticScope(visitor.getCurrentPath()) && !type.isDeclaration()) { // if (infer) { // ((PICOInferenceVisitor)visitor).mainIsNot(type, RECEIVER_DEPENDANT_MUTABLE, "static.receiverdependantmutable.forbidden", tree); // } else { From 75e7d9411fb4a17c581bed77393d16f55f98b115 Mon Sep 17 00:00:00 2001 From: lnsun <57457122+lnsun@users.noreply.github.com> Date: Sat, 27 Feb 2021 15:23:16 -0500 Subject: [PATCH 102/144] don't check with default during inference --- .../inference/PICOInferenceValidator.java | 30 +++++++++---------- 1 file changed, 14 insertions(+), 16 deletions(-) diff --git a/src/main/java/pico/inference/PICOInferenceValidator.java b/src/main/java/pico/inference/PICOInferenceValidator.java index dd2d0fb..bd282bd 100644 --- a/src/main/java/pico/inference/PICOInferenceValidator.java +++ b/src/main/java/pico/inference/PICOInferenceValidator.java @@ -19,11 +19,7 @@ import java.util.Set; -import static pico.typecheck.PICOAnnotationMirrorHolder.BOTTOM; -import static pico.typecheck.PICOAnnotationMirrorHolder.IMMUTABLE; -import static pico.typecheck.PICOAnnotationMirrorHolder.MUTABLE; -import static pico.typecheck.PICOAnnotationMirrorHolder.READONLY; -import static pico.typecheck.PICOAnnotationMirrorHolder.RECEIVER_DEPENDANT_MUTABLE; +import static pico.typecheck.PICOAnnotationMirrorHolder.*; /** * Generates constraints based on PICO constraint-based well-formedness rules in infer mode. @@ -39,17 +35,19 @@ public Void visitDeclared(AnnotatedDeclaredType type, Tree tree) { checkStaticReceiverDependantMutableError(type, tree); checkImplicitlyImmutableTypeError(type, tree); checkOnlyOneAssignabilityModifierOnField(tree); - AnnotatedDeclaredType defaultType = - (AnnotatedDeclaredType) atypeFactory.getAnnotatedType(type.getUnderlyingType().asElement()); - // TODO for defaulted super clause: should top anno be checked? (see shouldCheckTopLevelDeclaredType()) - if (defaultType.getAnnotationInHierarchy(READONLY) == null && !PICOTypeUtil.isEnumOrEnumConstant(defaultType)) { - defaultType = defaultType.deepCopy(); - defaultType.replaceAnnotation(MUTABLE); - } - - if (!visitor.isValidUse(defaultType, type, tree)) { - reportInvalidAnnotationsOnUse(type, tree); - } +// AnnotatedDeclaredType defaultType = +// (AnnotatedDeclaredType) atypeFactory.getAnnotatedType(type.getUnderlyingType().asElement()); +// // TODO for defaulted super clause: should top anno be checked? (see shouldCheckTopLevelDeclaredType()) +// if (defaultType.getAnnotationInHierarchy(READONLY) == null && !PICOTypeUtil.isEnumOrEnumConstant(defaultType)) { +// defaultType = defaultType.deepCopy(); +// defaultType.replaceAnnotation(MUTABLE); +// } +// +// if (!visitor.isValidUse(defaultType, type, tree)) { +// reportInvalidAnnotationsOnUse(type, tree); +// } + // main != READONLY -> main |> bound <: main + ((PICOInferenceVisitor) visitor).mainCannotInferTo(type, POLY_MUTABLE, "cannot.infer.poly", tree); return super.visitDeclared(type, tree); } From 536326e515b00847751eee6c513eb172e0791b39 Mon Sep 17 00:00:00 2001 From: lnsun <57457122+lnsun@users.noreply.github.com> Date: Sat, 27 Feb 2021 15:25:15 -0500 Subject: [PATCH 103/144] add immutable alias for real type factory --- .../java/pico/inference/PICOInferenceRealTypeFactory.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/main/java/pico/inference/PICOInferenceRealTypeFactory.java b/src/main/java/pico/inference/PICOInferenceRealTypeFactory.java index 1511a00..d8abddf 100644 --- a/src/main/java/pico/inference/PICOInferenceRealTypeFactory.java +++ b/src/main/java/pico/inference/PICOInferenceRealTypeFactory.java @@ -55,11 +55,16 @@ */ public class PICOInferenceRealTypeFactory extends BaseAnnotatedTypeFactory implements ViewpointAdapterGettable { + private static final List IMMUTABLE_ALIASES = Arrays.asList( + "com.google.errorprone.annotations.Immutable", + "edu.cmu.cs.glacier.qual.Immutable"); + public PICOInferenceRealTypeFactory(BaseTypeChecker checker, boolean useFlow) { super(checker, useFlow); if (READONLY != null) { addAliasedAnnotation(org.jmlspecs.annotation.Readonly.class, READONLY); } + IMMUTABLE_ALIASES.forEach(anno -> addAliasedAnnotation(anno, IMMUTABLE)); postInit(); } From 00c72b0282251d4708a0e22ae4c1d64fdd456ac8 Mon Sep 17 00:00:00 2001 From: lnsun <57457122+lnsun@users.noreply.github.com> Date: Sat, 27 Feb 2021 19:48:39 -0500 Subject: [PATCH 104/144] array decl method --- .../typecheck/PICOAnnotatedTypeFactory.java | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/src/main/java/pico/typecheck/PICOAnnotatedTypeFactory.java b/src/main/java/pico/typecheck/PICOAnnotatedTypeFactory.java index 0167ce3..b33e535 100644 --- a/src/main/java/pico/typecheck/PICOAnnotatedTypeFactory.java +++ b/src/main/java/pico/typecheck/PICOAnnotatedTypeFactory.java @@ -515,6 +515,25 @@ public Void visitExecutable(AnnotatedExecutableType t, Void p) { } } + // Array decl methods + // Array methods are implemented as JVM native method, so we cannot add that to stubs. + // for now: default array in receiver, parameter and return type to RDM + if (t.getReceiverType() != null) { + if (PICOTypeUtil.isArrayType(t.getReceiverType(), typeFactory)) { + switch (t.toString()) { + case "Object clone(Array this)": + // Receiver type will not be viewpoint adapted: + // SyntheticArrays.replaceReturnType() will rollback the viewpoint adapt result. + // Use readonly to allow all invocations. + if (!t.getReceiverType().isAnnotatedInHierarchy(READONLY)) + t.getReceiverType().replaceAnnotation(READONLY); + // The return type will be fixed by SyntheticArrays anyway. + // Qualifiers added here will not have effect. + break; + } + } + } + return null; } From 1302fbd918a1b0c6a8376e0510a78a681b5547f1 Mon Sep 17 00:00:00 2001 From: lnsun <57457122+lnsun@users.noreply.github.com> Date: Sat, 27 Feb 2021 23:36:09 -0500 Subject: [PATCH 105/144] implicit shallow immutable warning (TC) --- src/main/java/pico/typecheck/PICOVisitor.java | 28 ++++++++++++++++ testinput/typecheck/DeepMutable.java | 33 +++++++++++++++++++ 2 files changed, 61 insertions(+) create mode 100644 testinput/typecheck/DeepMutable.java diff --git a/src/main/java/pico/typecheck/PICOVisitor.java b/src/main/java/pico/typecheck/PICOVisitor.java index 4ad646d..f23a74f 100644 --- a/src/main/java/pico/typecheck/PICOVisitor.java +++ b/src/main/java/pico/typecheck/PICOVisitor.java @@ -22,6 +22,7 @@ import javax.lang.model.element.TypeElement; import javax.lang.model.element.VariableElement; import javax.lang.model.type.TypeKind; +import javax.lang.model.type.TypeMirror; import org.checkerframework.checker.compilermsgs.qual.CompilerMessageKey; import org.checkerframework.checker.initialization.InitializationVisitor; @@ -531,6 +532,33 @@ public void processClassTree(ClassTree node) { return;// Doesn't process the class tree anymore } + // Issue warnings on implicit shallow immutable: + // Condition: + // * Class decl == Immutable + // * Member is field + // * Member's declared bound == Mutable + // * Member's use anno == null + if (bound.hasAnnotation(IMMUTABLE)) { + for(Tree member : node.getMembers()) { + if(member.getKind() == Kind.VARIABLE) { + Element ele = TreeUtils.elementFromTree(member); + assert ele != null; + // fromElement will not apply defaults, if no explicit anno exists in code, mirror have no anno + AnnotatedTypeMirror noDefaultMirror = atypeFactory.fromElement(ele); + TypeMirror ty = ele.asType(); + if (ty.getKind() == TypeKind.TYPEVAR) { + ty = TypesUtils.upperBound(ty); + } + if (AnnotationUtils.containsSameByName( + atypeFactory.getTypeDeclarationBounds(ty), MUTABLE) + && !noDefaultMirror.isAnnotatedInHierarchy(READONLY)) { + checker.reportWarning(member, "implicit.shallow.immutable"); + } + + } + } + } + // // field of mutable class cannot use RDM in immutable class // // Condition: // // * Class decl == Immutable diff --git a/testinput/typecheck/DeepMutable.java b/testinput/typecheck/DeepMutable.java new file mode 100644 index 0000000..f10ea26 --- /dev/null +++ b/testinput/typecheck/DeepMutable.java @@ -0,0 +1,33 @@ +import qual.*; + +public class DeepMutable { + @Mutable + static class MutableBox {} + + @Immutable + static class ImmutableClass { + + // :: warning: (implicit.shallow.immutable) + MutableBox implicit = new MutableBox(); + + @Mutable MutableBox explicit = new MutableBox(); + } + + @Immutable + static class ImmutableGenericEx { + + T t; + @Immutable ImmutableGenericEx(T t) { + this.t = t; + } + } + + @Immutable + static class ImmutableGenericIm { + // :: warning: (implicit.shallow.immutable) + T t; + @Immutable ImmutableGenericIm(T t) { + this.t = t; + } + } +} \ No newline at end of file From e1358913f12f45e7f686288dfe6a8520a75cb976 Mon Sep 17 00:00:00 2001 From: lnsun <57457122+lnsun@users.noreply.github.com> Date: Tue, 9 Mar 2021 16:08:22 -0500 Subject: [PATCH 106/144] tune up vpa --- src/main/java/pico/inference/PICOInferenceVisitor.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/main/java/pico/inference/PICOInferenceVisitor.java b/src/main/java/pico/inference/PICOInferenceVisitor.java index c6bcfd8..4d37d0e 100644 --- a/src/main/java/pico/inference/PICOInferenceVisitor.java +++ b/src/main/java/pico/inference/PICOInferenceVisitor.java @@ -140,6 +140,9 @@ public boolean isValidUse(AnnotatedDeclaredType declarationType, AnnotatedDeclar * @return true if holds, always true during inference */ private boolean isAdaptedSubtype(AnnotatedTypeMirror lhs, AnnotatedTypeMirror rhs) { + if (extractVarAnnot(lhs).equals(extractVarAnnot(rhs))) { + return true; + } ExtendedViewpointAdapter vpa = ((ViewpointAdapterGettable)atypeFactory).getViewpointAdapter(); AnnotatedTypeMirror adapted = vpa.rawCombineAnnotationWithType(extractVarAnnot(lhs), rhs); From 0dcb75a4573d14e72a3ded7212b900debc6b7289 Mon Sep 17 00:00:00 2001 From: lnsun <57457122+lnsun@users.noreply.github.com> Date: Tue, 9 Mar 2021 16:11:17 -0500 Subject: [PATCH 107/144] better logic to check anonymous --- src/main/java/pico/common/PICOTypeUtil.java | 39 +++++++++++++++++++ .../pico/inference/PICOInferenceVisitor.java | 6 +++ 2 files changed, 45 insertions(+) diff --git a/src/main/java/pico/common/PICOTypeUtil.java b/src/main/java/pico/common/PICOTypeUtil.java index dbfe606..6aac555 100644 --- a/src/main/java/pico/common/PICOTypeUtil.java +++ b/src/main/java/pico/common/PICOTypeUtil.java @@ -9,10 +9,12 @@ import com.sun.source.tree.ClassTree; import com.sun.source.tree.ExpressionTree; import com.sun.source.tree.MethodTree; +import com.sun.source.tree.NewClassTree; import com.sun.source.tree.Tree; import com.sun.source.tree.UnaryTree; import com.sun.source.tree.VariableTree; import com.sun.source.util.TreePath; +import com.sun.tools.javac.code.Symbol; import org.checkerframework.framework.qual.DefaultFor; import org.checkerframework.framework.qual.TypeKind; import org.checkerframework.framework.type.AnnotatedTypeFactory; @@ -415,4 +417,41 @@ public static boolean inStaticScope(TreePath treePath) { public static boolean isSideEffectingUnaryTree(final UnaryTree tree) { return sideEffectingUnaryOperators.contains(tree.getKind()); } + + /** + * !! Experimental !! + *

+ * CF's isAnonymous cannot recognize some anonymous classes with annotation on new clause. + * This one hopefully will provide a workaround when the class tree is available. + *

+ * This will work if an anonymous class decl tree is always a child node of a {@code NewClassTree}. + * i.e. an anonymous class declaration is always inside a new clause. + * + * @param tree a class decl tree. + * @param atypeFactory used to get the path. Any factory should be ok. + * @return whether the class decl tree is of an anonymous class + */ + public static boolean isAnonymousClassTree(ClassTree tree, AnnotatedTypeFactory atypeFactory) { + TreePath path = atypeFactory.getPath(tree); + Tree parent = path.getParentPath().getLeaf(); + return parent instanceof NewClassTree && ((NewClassTree) parent).getClassBody() != null; + } + + /** + * !! Experimental !! + * Check whether the type is actually an array. + * @param type AnnotatedDeclaredType + * @param typeFactory any AnnotatedTypeFactory + * @return true if the type is array + */ + public static boolean isArrayType(AnnotatedDeclaredType type, AnnotatedTypeFactory typeFactory) { + Element ele = typeFactory.getProcessingEnv().getTypeUtils().asElement(type.getUnderlyingType()); + + // If it is a user-declared "Array" class without package, a class / source file should be there. + // Otherwise, it is the java inner type. + return ele instanceof Symbol.ClassSymbol + && ElementUtils.getVerboseName(ele).equals("Array") + && ((Symbol.ClassSymbol) ele).classfile == null + && ((Symbol.ClassSymbol) ele).sourcefile == null; + } } diff --git a/src/main/java/pico/inference/PICOInferenceVisitor.java b/src/main/java/pico/inference/PICOInferenceVisitor.java index 4d37d0e..38d6d16 100644 --- a/src/main/java/pico/inference/PICOInferenceVisitor.java +++ b/src/main/java/pico/inference/PICOInferenceVisitor.java @@ -794,6 +794,12 @@ private void checkNewInstanceCreation(Tree node) { // Completely copied from PICOVisitor @Override public Void visitMethodInvocation(MethodInvocationTree node, Void p) { + // issues with getting super for anonymous class - do not check for anonymous classes. + if (TreeUtils.isSuperConstructorCall(node) && + PICOTypeUtil.isAnonymousClassTree(TreeUtils.enclosingClass(atypeFactory.getPath(node)), atypeFactory)) { + return null; + } + super.visitMethodInvocation(node, p); ParameterizedExecutableType mfuPair = atypeFactory.methodFromUse(node); From 8951f8381e8d813e1b1c5223f8ba102434a464a1 Mon Sep 17 00:00:00 2001 From: lnsun <57457122+lnsun@users.noreply.github.com> Date: Tue, 9 Mar 2021 16:12:15 -0500 Subject: [PATCH 108/144] enum heck --- src/main/java/pico/inference/PICOInferenceVisitor.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/main/java/pico/inference/PICOInferenceVisitor.java b/src/main/java/pico/inference/PICOInferenceVisitor.java index 38d6d16..f7bd5cc 100644 --- a/src/main/java/pico/inference/PICOInferenceVisitor.java +++ b/src/main/java/pico/inference/PICOInferenceVisitor.java @@ -821,13 +821,14 @@ protected void checkMethodInvocability(AnnotatedExecutableType method, MethodInv AnnotatedTypeMirror superClassConstructorReturnType = method.getReturnType(); // In infer mode, InferenceQualifierHierarchy that is internally used should generate subtype constraint between the // below two types GENERALLY(not always) - if (!atypeFactory.getTypeHierarchy().isSubtype(subClassConstructorReturnType, superClassConstructorReturnType)) { + if (!PICOTypeUtil.isEnumOrEnumConstant(subClassConstructorReturnType) && // THIS IS A HECK: java.lang.Enum itself is considered immutable but its subclasses could be other. Update jdk.astub? + !atypeFactory.getTypeHierarchy().isSubtype(subClassConstructorReturnType, superClassConstructorReturnType)) { // Usually the subtyping check returns true. If not, that means subtype constraint doesn't hold between two // ConstantSlots. Previously, InferenceQualifierHierarchy also generates subtype constraint in this case, // then this unsatisfiable constraint is captured by ConstraintManager and ConstraintManager early exits. But // now for two ConstantSlot case, no subtype constraint is generated any more. So we have to report the error // , otherwise it will cause inference result not typecheck - checker.reportError(node, "super.invocation.invalid", subClassConstructorReturnType, superClassConstructorReturnType); + checker.reportError(node, "super.invocation.invalid", subClassConstructorReturnType, node, superClassConstructorReturnType); } } super.checkMethodInvocability(method, node); From a33647db5ed95da6a66d1ff682d0fb2190ec7138 Mon Sep 17 00:00:00 2001 From: lnsun <57457122+lnsun@users.noreply.github.com> Date: Tue, 23 Mar 2021 10:51:44 -0400 Subject: [PATCH 109/144] enforce check without refinement --- .../java/pico/inference/PICOInferenceVisitor.java | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/main/java/pico/inference/PICOInferenceVisitor.java b/src/main/java/pico/inference/PICOInferenceVisitor.java index f7bd5cc..d3fd45c 100644 --- a/src/main/java/pico/inference/PICOInferenceVisitor.java +++ b/src/main/java/pico/inference/PICOInferenceVisitor.java @@ -291,6 +291,20 @@ public Void visitVariable(VariableTree node, Void p) { new AnnotationMirror[]{MUTABLE, RECEIVER_DEPENDANT_MUTABLE}); + // Base will skip the rest check if assignment (if presents) get error. + // Make this explicit. + if (element != null && element.getKind() == ElementKind.LOCAL_VARIABLE && node.getInitializer() != null) { + // If not use element, but use the atypeFactory.getAnnotatedTypeLhs, anno will refined to initializer's + // anno even if the use is invalid, such as a @Mutable Immutable local variable. + // This refinement is ignored only here to capture related errors. + AnnotatedTypeMirror useType = atypeFactory.getAnnotatedType(element); + if (useType instanceof AnnotatedDeclaredType) { + AnnotatedTypeMirror boundType = + PICOTypeUtil.getBoundTypeOfTypeDeclaration(useType.getUnderlyingType(), atypeFactory); + if (!isAdaptedSubtype(useType, boundType)) { + checker.reportError(node, "type.invalid.annotations.on.use", useType, boundType); + } + } } return super.visitVariable(node, p); } From 2df57a330fc9f94496428466b34e697aea0fd699 Mon Sep 17 00:00:00 2001 From: lnsun <57457122+lnsun@users.noreply.github.com> Date: Tue, 23 Mar 2021 10:54:08 -0400 Subject: [PATCH 110/144] workaround for implicit lib type param --- .../inference/PICOInferenceAnnotatedTypeFactory.java | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/main/java/pico/inference/PICOInferenceAnnotatedTypeFactory.java b/src/main/java/pico/inference/PICOInferenceAnnotatedTypeFactory.java index 8a3b463..75d104c 100644 --- a/src/main/java/pico/inference/PICOInferenceAnnotatedTypeFactory.java +++ b/src/main/java/pico/inference/PICOInferenceAnnotatedTypeFactory.java @@ -177,6 +177,16 @@ public PICOInferencePropagationTreeAnnotator(AnnotatedTypeFactory atypeFactory) super(atypeFactory); } + @Override + public Void visitMethodInvocation(MethodInvocationTree methodInvocationTree, AnnotatedTypeMirror mirror) { + // This is a workaround for implicit types. + // Implicit types in lib method get defaulted to mutable. + // Implicit immutable classes cannot be annotated in stub files, annotations were ignored. + // Find the cause, annotate implicit immutable classes in stub, and remove this method. + applyImmutableIfImplicitlyImmutable(mirror); + return super.visitMethodInvocation(methodInvocationTree, mirror); + } + @Override public Void visitNewArray(NewArrayTree tree, AnnotatedTypeMirror type) { // Below is copied from super From ab295d5e5c0fc8423694d1601c8a9960f2722357 Mon Sep 17 00:00:00 2001 From: lnsun <57457122+lnsun@users.noreply.github.com> Date: Tue, 23 Mar 2021 10:55:35 -0400 Subject: [PATCH 111/144] disable alias --- src/main/java/pico/inference/PICOInferenceRealTypeFactory.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/pico/inference/PICOInferenceRealTypeFactory.java b/src/main/java/pico/inference/PICOInferenceRealTypeFactory.java index d8abddf..4654a0b 100644 --- a/src/main/java/pico/inference/PICOInferenceRealTypeFactory.java +++ b/src/main/java/pico/inference/PICOInferenceRealTypeFactory.java @@ -64,7 +64,7 @@ public PICOInferenceRealTypeFactory(BaseTypeChecker checker, boolean useFlow) { if (READONLY != null) { addAliasedAnnotation(org.jmlspecs.annotation.Readonly.class, READONLY); } - IMMUTABLE_ALIASES.forEach(anno -> addAliasedAnnotation(anno, IMMUTABLE)); +// IMMUTABLE_ALIASES.forEach(anno -> addAliasedAnnotation(anno, IMMUTABLE)); postInit(); } From 2758917542e2dcc7bff222f157757fd95f6e2acd Mon Sep 17 00:00:00 2001 From: lnsun <57457122+lnsun@users.noreply.github.com> Date: Tue, 23 Mar 2021 11:04:15 -0400 Subject: [PATCH 112/144] workaround: vpa decl w/o anno --- .../pico/inference/PICOInferenceViewpointAdapter.java | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/main/java/pico/inference/PICOInferenceViewpointAdapter.java b/src/main/java/pico/inference/PICOInferenceViewpointAdapter.java index 8dd86d3..38a7fda 100644 --- a/src/main/java/pico/inference/PICOInferenceViewpointAdapter.java +++ b/src/main/java/pico/inference/PICOInferenceViewpointAdapter.java @@ -1,5 +1,6 @@ package pico.inference; +import checkers.inference.InferenceMain; import checkers.inference.util.InferenceViewpointAdapter; import org.checkerframework.framework.type.AnnotatedTypeFactory; import org.checkerframework.framework.type.AnnotatedTypeMirror; @@ -30,6 +31,13 @@ protected AnnotatedTypeMirror combineAnnotationWithType(AnnotationMirror receive if (PICOTypeUtil.isImplicitlyImmutableType(declared)) { return declared; } + // workaround + if (InferenceMain.isHackMode()) { + if (extractAnnotationMirror(declared) == null) { + return declared; + } + } + return super.combineAnnotationWithType(receiverAnnotation, declared); } From b4fb4b307d4bb0d75e4a892553b3a3f533c059d4 Mon Sep 17 00:00:00 2001 From: lnsun <57457122+lnsun@users.noreply.github.com> Date: Tue, 23 Mar 2021 14:27:23 -0400 Subject: [PATCH 113/144] consistency infer and tc --- .../java/pico/inference/PICOInferenceVisitor.java | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/main/java/pico/inference/PICOInferenceVisitor.java b/src/main/java/pico/inference/PICOInferenceVisitor.java index d3fd45c..1622229 100644 --- a/src/main/java/pico/inference/PICOInferenceVisitor.java +++ b/src/main/java/pico/inference/PICOInferenceVisitor.java @@ -978,9 +978,15 @@ protected void commonAssignmentCheck( AnnotatedTypeMirror var = atypeFactory.getAnnotatedTypeLhs(varTree); assert var != null : "no variable found for tree: " + varTree; - if (!validateType(varTree, var)) { - return; - } + // Seems that typecheck does not have this. + // Removing this check will satisfy initial typecheck of inferrable/issue144/ComplicatedTest.java:42, + // where invalid.annotations.on.use is not expected. + // Local variable is flow-sensitive, so when assigned to a type that contradicts with the init bound, + // it still got "refined" + // Maybe updating the flow-sensitive logic to not refined to invalid type? +// if (!validateType(varTree, var)) { +// return; +// } if (varTree instanceof VariableTree) { VariableElement element = TreeUtils.elementFromDeclaration((VariableTree) varTree); From beae801aaf71036286febe1d1ee6bd942d714083 Mon Sep 17 00:00:00 2001 From: lnsun <57457122+lnsun@users.noreply.github.com> Date: Tue, 23 Mar 2021 14:32:41 -0400 Subject: [PATCH 114/144] ext and imp clause logic --- src/main/java/pico/common/PICOTypeUtil.java | 4 ++++ .../java/pico/inference/PICOInferenceVisitor.java | 15 ++++++++++++--- 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/src/main/java/pico/common/PICOTypeUtil.java b/src/main/java/pico/common/PICOTypeUtil.java index 6aac555..97f5a69 100644 --- a/src/main/java/pico/common/PICOTypeUtil.java +++ b/src/main/java/pico/common/PICOTypeUtil.java @@ -191,6 +191,10 @@ public static AnnotatedDeclaredType getBoundTypeOfTypeDeclaration(TypeElement ty // places, at some time. } + public static AnnotatedDeclaredType getBoundTypeOfTypeDeclaration(TypeMirror typeMirror, AnnotatedTypeFactory atypeFactory) { + return getBoundTypeOfTypeDeclaration(TypesUtils.getTypeElement(typeMirror), atypeFactory); + } + public static boolean isObjectIdentityMethod(MethodTree node, AnnotatedTypeFactory annotatedTypeFactory) { Element element = TreeUtils.elementFromTree(node); diff --git a/src/main/java/pico/inference/PICOInferenceVisitor.java b/src/main/java/pico/inference/PICOInferenceVisitor.java index 1622229..e08f2b6 100644 --- a/src/main/java/pico/inference/PICOInferenceVisitor.java +++ b/src/main/java/pico/inference/PICOInferenceVisitor.java @@ -943,9 +943,18 @@ private void checkSuperClauseEquals(ClassTree node, AnnotatedDeclaredType bound) private void boundVsExtImpClause(AnnotatedDeclaredType classBound, AnnotatedTypeMirror superType, String errorKey, Tree tree) { - AnnotationMirror superAnno = extractVarAnnot(superType); - if (superAnno != null) { - annoIs(classBound, extractVarAnnot(classBound), superAnno, errorKey, tree); + // atypeFactory.getTypeDeclarationBounds does not work correctly: getting the real annos instead of slots + AnnotatedTypeMirror superBound = + PICOTypeUtil.getBoundTypeOfTypeDeclaration(superType.getUnderlyingType(), atypeFactory); + + if (!isAdaptedSubtype(superType, superBound)) { + checker.reportError(tree, "type.invalid.annotations.on.use", superType, superBound); + } + + // the class bound should be a valid "use" of the super. + // consider replace with isValidUse? + if (!isAdaptedSubtype(classBound, superType)) { + checker.reportError(tree, errorKey, classBound, superType); } } From c0167a1a04c1405ba3a9d23c29153da5305c780e Mon Sep 17 00:00:00 2001 From: lnsun <57457122+lnsun@users.noreply.github.com> Date: Tue, 23 Mar 2021 14:33:45 -0400 Subject: [PATCH 115/144] override base ext/imp clause check --- src/main/java/pico/inference/PICOInferenceVisitor.java | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/main/java/pico/inference/PICOInferenceVisitor.java b/src/main/java/pico/inference/PICOInferenceVisitor.java index e08f2b6..7752f98 100644 --- a/src/main/java/pico/inference/PICOInferenceVisitor.java +++ b/src/main/java/pico/inference/PICOInferenceVisitor.java @@ -894,6 +894,12 @@ public void processClassTree(ClassTree node) { super.processClassTree(node); } + @Override + protected void checkExtendsImplements(ClassTree classTree) { + // do not use CF's checkExtendsImplements which will generate subtype constraints. + // maybe extract a method between class bound and extends/implements annos and override that. + } + /** * The base visitor does not use inference method to check! * This method is required to add the constraints for extends / implements. @@ -925,7 +931,7 @@ private void checkAnonymousImplements(ClassTree node, AnnotatedDeclaredType boun /** - * extends/implements clause use anno == bound anno + * extends/implements clause use anno is adapted subtype of bound anno *

Could be subtype, but recall Readonly and Bottom is not usable on class init bound.

* @param node * @param bound From d73db45afd131787b3be5015691ba302a8b08e20 Mon Sep 17 00:00:00 2001 From: lnsun <57457122+lnsun@users.noreply.github.com> Date: Tue, 23 Mar 2021 14:40:17 -0400 Subject: [PATCH 116/144] override base check on ext/imp clauses --- src/main/java/pico/inference/PICOVariableAnnotator.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/main/java/pico/inference/PICOVariableAnnotator.java b/src/main/java/pico/inference/PICOVariableAnnotator.java index 8f6f2ee..e861e45 100644 --- a/src/main/java/pico/inference/PICOVariableAnnotator.java +++ b/src/main/java/pico/inference/PICOVariableAnnotator.java @@ -135,6 +135,12 @@ protected VariableSlot getOrCreateDeclBound(AnnotatedDeclaredType type) { return super.getOrCreateDeclBound(type); } + @Override + protected void handleExplicitExtends(Tree extendsTree) { + // PICO cannot use base extends handling: not simply subtype relationship because of RDM + // Constraints already generated in processClassTree + } + @Override public void storeElementType(Element element, AnnotatedTypeMirror atm) { // this method is override the behavior of super.handleClassDeclaration before storing From ba2f6a036a0ab4198ee88466021b6ba0a9b0aedf Mon Sep 17 00:00:00 2001 From: lnsun <57457122+lnsun@users.noreply.github.com> Date: Tue, 23 Mar 2021 14:46:54 -0400 Subject: [PATCH 117/144] anonymous class logic --- src/main/java/pico/inference/PICOInferenceVisitor.java | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/main/java/pico/inference/PICOInferenceVisitor.java b/src/main/java/pico/inference/PICOInferenceVisitor.java index 7752f98..a8a77a5 100644 --- a/src/main/java/pico/inference/PICOInferenceVisitor.java +++ b/src/main/java/pico/inference/PICOInferenceVisitor.java @@ -419,7 +419,12 @@ public void mainCannotInferTo(AnnotatedTypeMirror atm, AnnotationMirror anno, St @Override public Void visitMethod(MethodTree node, Void p) { AnnotatedExecutableType executableType = atypeFactory.getAnnotatedType(node); - AnnotatedDeclaredType bound = PICOTypeUtil.getBoundTypeOfEnclosingTypeDeclaration(node, atypeFactory); + AnnotatedDeclaredType bound; + if (PICOTypeUtil.isEnclosedByAnonymousClass(node, atypeFactory)) { + bound = PICOTypeUtil.getBoundOfEnclosingAnonymousClass(node, atypeFactory); + } else { + bound = PICOTypeUtil.getBoundTypeOfEnclosingTypeDeclaration(node, atypeFactory); + } assert bound != null; assert !bound.hasAnnotation(POLY_MUTABLE) : "BOUND CANNOT BE POLY"; From b39968e18317b219489518145c1277ac790778e3 Mon Sep 17 00:00:00 2001 From: lnsun <57457122+lnsun@users.noreply.github.com> Date: Thu, 25 Mar 2021 12:11:27 -0400 Subject: [PATCH 118/144] receiver cannot infer to poly --- src/main/java/pico/inference/PICOInferenceVisitor.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/main/java/pico/inference/PICOInferenceVisitor.java b/src/main/java/pico/inference/PICOInferenceVisitor.java index a8a77a5..e030d6e 100644 --- a/src/main/java/pico/inference/PICOInferenceVisitor.java +++ b/src/main/java/pico/inference/PICOInferenceVisitor.java @@ -430,6 +430,9 @@ public Void visitMethod(MethodTree node, Void p) { // cannot infer poly, but can use it for type-check. mainCannotInferTo(executableType.getReturnType(), POLY_MUTABLE, "cannot.infer.poly", node); + if (executableType.getReceiverType() != null) { + mainCannotInferTo(executableType.getReceiverType(), POLY_MUTABLE, "cannot.infer.poly", node); + } if (TreeUtils.isConstructor(node)) { // Doesn't check anonymous constructor case From 631d7fed545c94b73e823cc9dbd1dd0d4bdcc357 Mon Sep 17 00:00:00 2001 From: lnsun <57457122+lnsun@users.noreply.github.com> Date: Thu, 25 Mar 2021 12:13:37 -0400 Subject: [PATCH 119/144] allow explicit poly on receiver --- src/main/java/pico/inference/PICOInferenceVisitor.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/java/pico/inference/PICOInferenceVisitor.java b/src/main/java/pico/inference/PICOInferenceVisitor.java index e030d6e..d7ee055 100644 --- a/src/main/java/pico/inference/PICOInferenceVisitor.java +++ b/src/main/java/pico/inference/PICOInferenceVisitor.java @@ -474,7 +474,6 @@ public Void visitMethod(MethodTree node, Void p) { // Above is additional preference logic if (declaredReceiverType != null) { mainIsNot(declaredReceiverType, BOTTOM, "bottom.on.receiver", node); -// mainIsNot(declaredReceiverType, POLY_MUTABLE, "poly.on.receiver", node); if (!isAdaptedSubtype(declaredReceiverType, bound)){ checker.report(Result.failure("method.receiver.incompatible", declaredReceiverType), node); } From a634bfb8b51978b754610ffd47e14e22f6293a5b Mon Sep 17 00:00:00 2001 From: lnsun <57457122+lnsun@users.noreply.github.com> Date: Thu, 25 Mar 2021 12:21:26 -0400 Subject: [PATCH 120/144] class bound for infer --- .../PICOInferenceAnnotatedTypeFactory.java | 53 ++++++++++++++----- 1 file changed, 39 insertions(+), 14 deletions(-) diff --git a/src/main/java/pico/inference/PICOInferenceAnnotatedTypeFactory.java b/src/main/java/pico/inference/PICOInferenceAnnotatedTypeFactory.java index 75d104c..ca04e56 100644 --- a/src/main/java/pico/inference/PICOInferenceAnnotatedTypeFactory.java +++ b/src/main/java/pico/inference/PICOInferenceAnnotatedTypeFactory.java @@ -11,6 +11,7 @@ import com.sun.source.tree.BinaryTree; import com.sun.source.tree.ClassTree; import com.sun.source.tree.ExpressionTree; +import com.sun.source.tree.MethodInvocationTree; import com.sun.source.tree.MethodTree; import com.sun.source.tree.NewArrayTree; import com.sun.source.tree.Tree; @@ -39,6 +40,7 @@ import javax.lang.model.element.Element; import javax.lang.model.element.ElementKind; import javax.lang.model.type.DeclaredType; +import javax.lang.model.type.PrimitiveType; import javax.lang.model.type.TypeKind; import javax.lang.model.type.TypeMirror; @@ -151,25 +153,48 @@ protected Set getDefaultTypeDeclarationBounds() { @Override public Set getTypeDeclarationBounds(TypeMirror type) { - // TODO too awkward. maybe overload isImplicitlyImmutableType - if (PICOTypeUtil.isImplicitlyImmutableType(toAnnotatedType(type, false))) { - return Collections.singleton(IMMUTABLE); + // Get the VarAnnot on the class decl + // This factory is only invoked on inference, so no need to provide concrete anno for type-check + if (type instanceof PrimitiveType) { + return Collections.singleton(slotManager.getAnnotation(slotManager.getSlot(IMMUTABLE))); } if (type.getKind() == TypeKind.ARRAY) { - return Collections.singleton(READONLY); // if decided to use vpa for array, return RDM. + // WORKAROUND: return RDM will cause issues with new clauses + return Collections.singleton(slotManager.getAnnotation(slotManager.getSlot(READONLY))); + } +// AnnotatedTypeMirror atm = toAnnotatedType(type, true); +// if (atm instanceof AnnotatedDeclaredType && ((AnnotatedDeclaredType) atm).getTypeArguments().size() > 0) { +// // Workaround for types with type arguments. +// // annotateElementFromStore can only get the original type with type param. +// // But this method only needs the top annotation. +// // Note: class bound cache is a private field of annotator. +// +// atm = PICOTypeUtil.getBoundTypeOfTypeDeclaration(TypesUtils.getTypeElement(type), this); +// } else { +// getVariableAnnotator().annotateElementFromStore(getProcessingEnv().getTypeUtils().asElement(type), atm); +// } +// +// if (atm.hasAnnotation(VarAnnot.class)) { +// return atm.getAnnotations(); +// } + AnnotationMirror am = ((PICOVariableAnnotator) variableAnnotator).getClassDeclAnno(getProcessingEnv().getTypeUtils().asElement(type)); + if (am != null) { + return Collections.singleton(am); } - // IMMUTABLE for enum w/o decl anno - if (type instanceof DeclaredType) { - Element ele = ((DeclaredType) type).asElement(); - if (ele.getKind() == ElementKind.ENUM) { - if (!AnnotationUtils.containsSameByName(getDeclAnnotations(ele), MUTABLE) && - !AnnotationUtils.containsSameByName(getDeclAnnotations(ele), RECEIVER_DEPENDANT_MUTABLE)) { // no decl anno - return Collections.singleton(IMMUTABLE); - } - } + // if reaching this point and still no anno: not annotated from slot manager + // maybe should read from stub file. + // if implicit: return immutable slot + + // implicit + if (PICOTypeUtil.isImplicitlyImmutableType(toAnnotatedType(type, false))) { + return Collections.singleton(slotManager.getAnnotation(slotManager.getSlot(IMMUTABLE))); } - return super.getTypeDeclarationBounds(type); + + // get stub & default from element. +// return stubTypes.getAnnotatedTypeMirror(TypesUtils.getTypeElement(type)).getAnnotations(); + return Collections.singleton( + slotManager.getAnnotation(slotManager.getSlot(super.getTypeDeclarationBounds(type).iterator().next()))); } class PICOInferencePropagationTreeAnnotator extends PropagationTreeAnnotator { From afd4505ddba37b2efb9a3e0eef3d5327d6646565 Mon Sep 17 00:00:00 2001 From: lnsun <57457122+lnsun@users.noreply.github.com> Date: Thu, 25 Mar 2021 12:23:18 -0400 Subject: [PATCH 121/144] allow factory to get class decl slot --- src/main/java/pico/inference/PICOVariableAnnotator.java | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/main/java/pico/inference/PICOVariableAnnotator.java b/src/main/java/pico/inference/PICOVariableAnnotator.java index e861e45..63c2d3c 100644 --- a/src/main/java/pico/inference/PICOVariableAnnotator.java +++ b/src/main/java/pico/inference/PICOVariableAnnotator.java @@ -279,4 +279,12 @@ public void handleBinaryTree(AnnotatedTypeMirror atm, BinaryTree binaryTree) { } super.handleBinaryTree(atm, binaryTree); } + + public AnnotationMirror getClassDeclAnno(Element ele) { + if (classDeclAnnos.get(ele) != null) { + return slotManager.getAnnotation(classDeclAnnos.get(ele)); + } + return null; + } + } From e834b11b7c78711dd4e6070f788e382be748b1c9 Mon Sep 17 00:00:00 2001 From: lnsun <57457122+lnsun@users.noreply.github.com> Date: Thu, 25 Mar 2021 12:25:38 -0400 Subject: [PATCH 122/144] annotator: handle decl bound --- .../pico/inference/PICOVariableAnnotator.java | 46 ++++++++++--------- 1 file changed, 24 insertions(+), 22 deletions(-) diff --git a/src/main/java/pico/inference/PICOVariableAnnotator.java b/src/main/java/pico/inference/PICOVariableAnnotator.java index 63c2d3c..b289d9d 100644 --- a/src/main/java/pico/inference/PICOVariableAnnotator.java +++ b/src/main/java/pico/inference/PICOVariableAnnotator.java @@ -1,10 +1,5 @@ package pico.inference; -import static pico.typecheck.PICOAnnotationMirrorHolder.BOTTOM; -import static pico.typecheck.PICOAnnotationMirrorHolder.IMMUTABLE; -import static pico.typecheck.PICOAnnotationMirrorHolder.MUTABLE; -import static pico.typecheck.PICOAnnotationMirrorHolder.READONLY; - import java.util.Arrays; import java.util.List; @@ -39,6 +34,8 @@ import org.checkerframework.javacutil.TypesUtils; import pico.common.PICOTypeUtil; +import static pico.typecheck.PICOAnnotationMirrorHolder.*; + public class PICOVariableAnnotator extends VariableAnnotator { private boolean generateBottomInequality = true; @@ -113,24 +110,29 @@ protected void handleClassDeclarationBound(AnnotatedDeclaredType classType) { @Override protected VariableSlot getOrCreateDeclBound(AnnotatedDeclaredType type) { - // if a explicit annotation presents on the class decl, use that directly - if (type.isAnnotatedInHierarchy(READONLY)) { - VariableSlot constantSlot = (VariableSlot) slotManager.getSlot(type.getAnnotationInHierarchy(READONLY)); - TypeElement classDecl = (TypeElement) type.getUnderlyingType().asElement(); - classDeclAnnos.put(classDecl, constantSlot); - // avoid duplicate annos - type.removeAnnotationInHierarchy(READONLY); - return constantSlot; - } + TypeElement classDecl = (TypeElement) type.getUnderlyingType().asElement(); + + VariableSlot declSlot = classDeclAnnos.get(classDecl); + if (declSlot == null) { + // if a explicit annotation presents on the class DECL, use that directly + if (type.isDeclaration() && type.isAnnotatedInHierarchy(READONLY) && !type.hasAnnotation(READONLY)) { + VariableSlot constantSlot = (VariableSlot) slotManager.getSlot(type.getAnnotationInHierarchy(READONLY)); +// TypeElement classDecl = (TypeElement) type.getUnderlyingType().asElement(); + classDeclAnnos.put(classDecl, constantSlot); +// // avoid duplicate annos +// type.removeAnnotationInHierarchy(READONLY); + return constantSlot; + } - // new class tree of an anonymous class is always visited before (enclosing tree). - // slot should be generated then. - // use that slot and avoid generating a new slot. - // push this change to inference IFF the slot on new class have same requirement with class bound - // e.g. existence slot on new class tree? - if (TypesUtils.isAnonymous(type.getUnderlyingType())) { - assert type.hasAnnotation(VarAnnot.class); - return (VariableSlot) slotManager.getSlot(type.getAnnotation(VarAnnot.class)); + // new class tree of an anonymous class is always visited before (enclosing tree). + // slot should be generated then. + // use that slot and avoid generating a new slot. + // push this change to inference IFF the slot on new class have same requirement with class bound + // e.g. existence slot on new class tree? + if (TypesUtils.isAnonymous(type.getUnderlyingType())) { + assert type.hasAnnotation(VarAnnot.class); + return (VariableSlot) slotManager.getSlot(type.getAnnotation(VarAnnot.class)); + } } return super.getOrCreateDeclBound(type); } From f1d86520d799633961f838a2e9386c2b8594c9e3 Mon Sep 17 00:00:00 2001 From: lnsun <57457122+lnsun@users.noreply.github.com> Date: Thu, 25 Mar 2021 12:26:59 -0400 Subject: [PATCH 123/144] annotator: skip null --- src/main/java/pico/inference/PICOVariableAnnotator.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/main/java/pico/inference/PICOVariableAnnotator.java b/src/main/java/pico/inference/PICOVariableAnnotator.java index b289d9d..e156cc3 100644 --- a/src/main/java/pico/inference/PICOVariableAnnotator.java +++ b/src/main/java/pico/inference/PICOVariableAnnotator.java @@ -171,6 +171,9 @@ protected VariableSlot addPrimaryVariable(AnnotatedTypeMirror atm, Tree tree) { // // Don't add new VarAnnot to type use of enum type // PICOTypeUtil.applyConstant(atm, IMMUTABLE); // } + if (atm instanceof AnnotatedTypeMirror.AnnotatedNullType) { + PICOTypeUtil.applyConstant(atm, BOTTOM); + } return super.addPrimaryVariable(atm, tree); } From c5f3b1da0c3445e8f09ae156dcd5e2021915e0ae Mon Sep 17 00:00:00 2001 From: lnsun <57457122+lnsun@users.noreply.github.com> Date: Thu, 25 Mar 2021 12:27:55 -0400 Subject: [PATCH 124/144] annotator: replace real anno --- src/main/java/pico/inference/PICOVariableAnnotator.java | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/main/java/pico/inference/PICOVariableAnnotator.java b/src/main/java/pico/inference/PICOVariableAnnotator.java index e156cc3..c155f66 100644 --- a/src/main/java/pico/inference/PICOVariableAnnotator.java +++ b/src/main/java/pico/inference/PICOVariableAnnotator.java @@ -156,6 +156,13 @@ public void storeElementType(Element element, AnnotatedTypeMirror atm) { AnnotationMirror potential = slotManager.getAnnotation(((ExistentialVariableSlot) slot).getPotentialSlot()); atm.replaceAnnotation(potential); } + + // If an explicit bound exists, the annotator will still place a constant slot on the bound, + // which will considered invalid by CF. + // Maybe not putting an anno at all during bound slot generation would be better? + if (atm.hasAnnotation(VarAnnot.class) && atm.isAnnotatedInHierarchy(READONLY)) { + atm.removeAnnotationInHierarchy(READONLY); + } super.storeElementType(element, atm); } From 0462affcd422a300e0c98f36427c433bf9dffe9b Mon Sep 17 00:00:00 2001 From: lnsun <57457122+lnsun@users.noreply.github.com> Date: Thu, 25 Mar 2021 12:28:36 -0400 Subject: [PATCH 125/144] annotator: not infer to poly --- src/main/java/pico/inference/PICOVariableAnnotator.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/java/pico/inference/PICOVariableAnnotator.java b/src/main/java/pico/inference/PICOVariableAnnotator.java index c155f66..e257d4b 100644 --- a/src/main/java/pico/inference/PICOVariableAnnotator.java +++ b/src/main/java/pico/inference/PICOVariableAnnotator.java @@ -193,6 +193,7 @@ protected VariableSlot createVariable(AnnotationLocation location) { // @Bottom) if (generateBottomInequality) { constraintManager.addInequalityConstraint(varSlot, slotManager.createConstantSlot(BOTTOM)); + constraintManager.addInequalityConstraint(varSlot, slotManager.createConstantSlot(POLY_MUTABLE)); } return varSlot; } From 4158e723ec10797726d3496805089f7f53a74d3f Mon Sep 17 00:00:00 2001 From: lnsun <57457122+lnsun@users.noreply.github.com> Date: Thu, 25 Mar 2021 12:29:20 -0400 Subject: [PATCH 126/144] annotator: drop super decl constraints --- src/main/java/pico/inference/PICOVariableAnnotator.java | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/main/java/pico/inference/PICOVariableAnnotator.java b/src/main/java/pico/inference/PICOVariableAnnotator.java index e257d4b..a8fda07 100644 --- a/src/main/java/pico/inference/PICOVariableAnnotator.java +++ b/src/main/java/pico/inference/PICOVariableAnnotator.java @@ -300,4 +300,11 @@ public AnnotationMirror getClassDeclAnno(Element ele) { return null; } + + @Override + protected void addDeclarationConstraints(VariableSlot declSlot, VariableSlot instanceSlot) { + // RDM-related constraints cannot use subtype. + // Necessary constraints added in visitor instead. + } + } From e0e742fd405df8952a8fa0260ba770568f137df3 Mon Sep 17 00:00:00 2001 From: lnsun <57457122+lnsun@users.noreply.github.com> Date: Thu, 25 Mar 2021 12:30:17 -0400 Subject: [PATCH 127/144] visitor: skip uses w/o slot --- src/main/java/pico/inference/PICOInferenceVisitor.java | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/main/java/pico/inference/PICOInferenceVisitor.java b/src/main/java/pico/inference/PICOInferenceVisitor.java index d7ee055..70f3a51 100644 --- a/src/main/java/pico/inference/PICOInferenceVisitor.java +++ b/src/main/java/pico/inference/PICOInferenceVisitor.java @@ -10,6 +10,7 @@ import checkers.inference.model.ConstraintManager; import checkers.inference.model.ImplicationConstraint; import checkers.inference.model.Slot; +import checkers.inference.qual.VarAnnot; import com.sun.source.tree.AnnotationTree; import com.sun.source.tree.ArrayAccessTree; import com.sun.source.tree.AssignmentTree; @@ -83,6 +84,12 @@ public boolean isValidUse(AnnotatedDeclaredType declarationType, AnnotatedDeclar if (useType.hasAnnotation(BOTTOM)) { return true; } + + // skip base check during inference + if (infer && !declarationType.hasAnnotation(VarAnnot.class)) { + return true; + } + // allow RDM on mutable fields with enclosing class bounded with mutable if (tree instanceof VariableTree && !useType.isDeclaration()) { VariableElement element = TreeUtils.elementFromDeclaration((VariableTree)tree); From b5e214d42707e417369d1d1f5a509e2cf4137fa4 Mon Sep 17 00:00:00 2001 From: lnsun <57457122+lnsun@users.noreply.github.com> Date: Thu, 25 Mar 2021 12:32:46 -0400 Subject: [PATCH 128/144] rdm-field: field uses be both rdm or mutable --- src/main/java/pico/inference/PICOInferenceVisitor.java | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/main/java/pico/inference/PICOInferenceVisitor.java b/src/main/java/pico/inference/PICOInferenceVisitor.java index 70f3a51..dd08aff 100644 --- a/src/main/java/pico/inference/PICOInferenceVisitor.java +++ b/src/main/java/pico/inference/PICOInferenceVisitor.java @@ -292,11 +292,15 @@ public Void visitVariable(VariableTree node, Void p) { addDeepPreference(type, IMMUTABLE, 3, node); } + // if the use is a field and not static, and the bound of the type is mutable: + // allow the use to be rdm or mutable if (element != null && element.getKind() == ElementKind.FIELD && !ElementUtils.isStatic(element)) { AnnotatedTypeMirror type = atypeFactory.getAnnotatedType(element); - ifBoundContainsThenMainIsOneOf(type, MUTABLE, - new AnnotationMirror[]{MUTABLE, RECEIVER_DEPENDANT_MUTABLE}); - + if (type instanceof AnnotatedDeclaredType) { + ifBoundContainsThenMainIsOneOf((AnnotatedDeclaredType) type, MUTABLE, + new AnnotationMirror[]{MUTABLE, RECEIVER_DEPENDANT_MUTABLE}); + } + } // Base will skip the rest check if assignment (if presents) get error. // Make this explicit. From c201bd5874e265b7ee3daea82478c4d49dda0256 Mon Sep 17 00:00:00 2001 From: lnsun <57457122+lnsun@users.noreply.github.com> Date: Thu, 25 Mar 2021 12:34:03 -0400 Subject: [PATCH 129/144] comments --- src/main/java/pico/inference/PICOInferenceRealTypeFactory.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/java/pico/inference/PICOInferenceRealTypeFactory.java b/src/main/java/pico/inference/PICOInferenceRealTypeFactory.java index 4654a0b..1f173cf 100644 --- a/src/main/java/pico/inference/PICOInferenceRealTypeFactory.java +++ b/src/main/java/pico/inference/PICOInferenceRealTypeFactory.java @@ -123,7 +123,7 @@ protected TypeAnnotator createTypeAnnotator() { return new ListTypeAnnotator(typeAnnotators); } - /** TODO If the dataflow refines the type as bottom, should we allow such a refinement? If we allow it, + /* TODO If the dataflow refines the type as bottom, should we allow such a refinement? If we allow it, PICOValidator will give an error if it begins to enforce @Bottom is not used*/ /* @Override protected void applyInferredAnnotations(AnnotatedTypeMirror type, PICOValue as) { @@ -191,6 +191,7 @@ public AnnotatedTypeMirror getTypeOfExtendsImplements(Tree clause) { // workaround for anonymous class. // TypesUtils::isAnonymous won't work when annotation presents on new class tree! + // by reaching this line TypesUtils::isAnonymous is already not working: it shouldn't check anonymous class! if(getPath(clause).getParentPath().getLeaf() instanceof JCTree.JCNewClass) { enclosing = getAnnotatedType(getPath(clause).getParentPath().getLeaf()); From c060dc150675c7a3e82ce5742ca7c293b697290d Mon Sep 17 00:00:00 2001 From: lnsun <57457122+lnsun@users.noreply.github.com> Date: Thu, 25 Mar 2021 12:34:27 -0400 Subject: [PATCH 130/144] notes --- .../pico/inference/PICOInferenceAnnotatedTypeFactory.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/main/java/pico/inference/PICOInferenceAnnotatedTypeFactory.java b/src/main/java/pico/inference/PICOInferenceAnnotatedTypeFactory.java index ca04e56..a0b0377 100644 --- a/src/main/java/pico/inference/PICOInferenceAnnotatedTypeFactory.java +++ b/src/main/java/pico/inference/PICOInferenceAnnotatedTypeFactory.java @@ -292,6 +292,10 @@ public Void visitNewArray(NewArrayTree tree, AnnotatedTypeMirror type) { return null; // Above is copied from super + + // We have a problem with the annotation. + // During inference it get defaulted annotation for component types inside dataflow and cache that. + // The assignment context have only constant slots. } /**Add immutable to the result type of a binary operation if the result type is implicitly immutable*/ From c1c8703a6c850f0f99153d23d4665c598fd83ab7 Mon Sep 17 00:00:00 2001 From: lnsun <57457122+lnsun@users.noreply.github.com> Date: Thu, 25 Mar 2021 12:34:51 -0400 Subject: [PATCH 131/144] tweaks --- src/main/java/pico/inference/PICOInferenceVisitor.java | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/main/java/pico/inference/PICOInferenceVisitor.java b/src/main/java/pico/inference/PICOInferenceVisitor.java index dd08aff..7bcaf5c 100644 --- a/src/main/java/pico/inference/PICOInferenceVisitor.java +++ b/src/main/java/pico/inference/PICOInferenceVisitor.java @@ -8,7 +8,6 @@ import checkers.inference.model.ConstantSlot; import checkers.inference.model.Constraint; import checkers.inference.model.ConstraintManager; -import checkers.inference.model.ImplicationConstraint; import checkers.inference.model.Slot; import checkers.inference.qual.VarAnnot; import com.sun.source.tree.AnnotationTree; @@ -57,9 +56,7 @@ import java.util.Iterator; import java.util.List; import java.util.Map; -import java.util.Objects; import java.util.Set; -import java.util.function.Supplier; import static pico.typecheck.PICOAnnotationMirrorHolder.*; @@ -96,8 +93,9 @@ public boolean isValidUse(AnnotatedDeclaredType declarationType, AnnotatedDeclar if (element.getKind() == ElementKind.FIELD && ElementUtils.enclosingClass(element) != null) { // assert only 1 bound exists AnnotationMirror enclosingBound = - atypeFactory.getTypeDeclarationBounds( - Objects.requireNonNull(ElementUtils.enclosingClass(element)).asType()).iterator().next(); + extractVarAnnot(PICOTypeUtil.getBoundTypeOfEnclosingTypeDeclaration(element, atypeFactory)); +// atypeFactory.getTypeDeclarationBounds( +// Objects.requireNonNull(ElementUtils.enclosingClass(element)).asType()).iterator().next(); // if enclosing bound == mutable -> (RDM or Mutable usable on mutable-bounded fields) // else -> adaptedSubtype From de054ce7af7d2b527a285a3ab841c4154d728fcb Mon Sep 17 00:00:00 2001 From: lnsun <57457122+lnsun@users.noreply.github.com> Date: Thu, 25 Mar 2021 12:37:51 -0400 Subject: [PATCH 132/144] stubs --- src/main/java/pico/inference/jdk.astub | 6 ++++++ src/main/java/pico/typecheck/jdk.astub | 6 ++++++ 2 files changed, 12 insertions(+) diff --git a/src/main/java/pico/inference/jdk.astub b/src/main/java/pico/inference/jdk.astub index 9091e38..401c0ce 100644 --- a/src/main/java/pico/inference/jdk.astub +++ b/src/main/java/pico/inference/jdk.astub @@ -113,6 +113,11 @@ interface Collection { boolean contains(@Readonly Collection this, @Readonly Object o); } +@ReceiverDependantMutable +public abstract class AbstractCollection implements Collection { + public abstract int size(@Readonly AbstractCollection this); +} + @ReceiverDependantMutable class ArrayList { @ReceiverDependantMutable ArrayList(); @@ -214,6 +219,7 @@ class Arrays { static String toString(int @Readonly [] var0); static boolean equals(float @Readonly [] var0, float @Readonly [] var1); static boolean equals(double @Readonly [] var0, double @Readonly [] var1); + static T[] copyOf(T @Readonly [] original, int newLength); } class Objects { diff --git a/src/main/java/pico/typecheck/jdk.astub b/src/main/java/pico/typecheck/jdk.astub index 9091e38..401c0ce 100644 --- a/src/main/java/pico/typecheck/jdk.astub +++ b/src/main/java/pico/typecheck/jdk.astub @@ -113,6 +113,11 @@ interface Collection { boolean contains(@Readonly Collection this, @Readonly Object o); } +@ReceiverDependantMutable +public abstract class AbstractCollection implements Collection { + public abstract int size(@Readonly AbstractCollection this); +} + @ReceiverDependantMutable class ArrayList { @ReceiverDependantMutable ArrayList(); @@ -214,6 +219,7 @@ class Arrays { static String toString(int @Readonly [] var0); static boolean equals(float @Readonly [] var0, float @Readonly [] var1); static boolean equals(double @Readonly [] var0, double @Readonly [] var1); + static T[] copyOf(T @Readonly [] original, int newLength); } class Objects { From 78aef48c7af777318887ac252d4881f7e2b8c541 Mon Sep 17 00:00:00 2001 From: lnsun <57457122+lnsun@users.noreply.github.com> Date: Thu, 25 Mar 2021 14:24:12 -0400 Subject: [PATCH 133/144] reim test cases --- .../pico/ImmutabilityReImInferenceTest.java | 42 +++++++++++ testinput/reiminfer/CFLTests1.java | 50 +++++++++++++ testinput/reiminfer/CFLTests2.java | 18 +++++ testinput/reiminfer/CFLTests3.java | 25 +++++++ testinput/reiminfer/CFLTests4.java | 39 ++++++++++ testinput/reiminfer/CFLTests5.java | 45 ++++++++++++ testinput/reiminfer/CFLTests6.java | 30 ++++++++ testinput/reiminfer/CFLTests7.java | 30 ++++++++ testinput/reiminfer/CFLTests8.java | 42 +++++++++++ .../reiminfer/CircularInitialization.java | 35 +++++++++ testinput/reiminfer/ImmutableArrays.java | 38 ++++++++++ testinput/reiminfer/InitMethod.java | 30 ++++++++ testinput/reiminfer/Library.java | 16 +++++ testinput/reiminfer/LibraryUse.java | 12 ++++ testinput/reiminfer/Test1.java | 44 ++++++++++++ testinput/reiminfer/Test2.java | 21 ++++++ testinput/reiminfer/Test3.java | 40 +++++++++++ testinput/reiminfer/Test4.java | 50 +++++++++++++ testinput/reiminfer/TheClass.java | 72 +++++++++++++++++++ testinput/reiminfer/ThisLeak.java | 26 +++++++ testinput/reiminfer/ThisLeak2.java | 20 ++++++ testinput/reiminfer/ThisLeak3.java | 18 +++++ testinput/reiminfer/ThisLeak4.java | 24 +++++++ testinput/reiminfer/ThisLeak5.java | 21 ++++++ testinput/reiminfer/ThisLeak6.java | 22 ++++++ 25 files changed, 810 insertions(+) create mode 100644 src/test/java/pico/ImmutabilityReImInferenceTest.java create mode 100644 testinput/reiminfer/CFLTests1.java create mode 100644 testinput/reiminfer/CFLTests2.java create mode 100644 testinput/reiminfer/CFLTests3.java create mode 100644 testinput/reiminfer/CFLTests4.java create mode 100644 testinput/reiminfer/CFLTests5.java create mode 100644 testinput/reiminfer/CFLTests6.java create mode 100644 testinput/reiminfer/CFLTests7.java create mode 100644 testinput/reiminfer/CFLTests8.java create mode 100644 testinput/reiminfer/CircularInitialization.java create mode 100644 testinput/reiminfer/ImmutableArrays.java create mode 100644 testinput/reiminfer/InitMethod.java create mode 100644 testinput/reiminfer/Library.java create mode 100644 testinput/reiminfer/LibraryUse.java create mode 100644 testinput/reiminfer/Test1.java create mode 100644 testinput/reiminfer/Test2.java create mode 100644 testinput/reiminfer/Test3.java create mode 100644 testinput/reiminfer/Test4.java create mode 100644 testinput/reiminfer/TheClass.java create mode 100644 testinput/reiminfer/ThisLeak.java create mode 100644 testinput/reiminfer/ThisLeak2.java create mode 100644 testinput/reiminfer/ThisLeak3.java create mode 100644 testinput/reiminfer/ThisLeak4.java create mode 100644 testinput/reiminfer/ThisLeak5.java create mode 100644 testinput/reiminfer/ThisLeak6.java diff --git a/src/test/java/pico/ImmutabilityReImInferenceTest.java b/src/test/java/pico/ImmutabilityReImInferenceTest.java new file mode 100644 index 0000000..c72cffa --- /dev/null +++ b/src/test/java/pico/ImmutabilityReImInferenceTest.java @@ -0,0 +1,42 @@ +package pico; + +import checkers.inference.test.CFInferenceTest; +import org.checkerframework.framework.test.TestUtilities; +import org.checkerframework.javacutil.Pair; +import org.junit.Ignore; +import org.junit.runners.Parameterized.Parameters; +import pico.inference.PICOInferenceChecker; +import pico.inference.solver.PICOSolverEngine; + +import java.io.File; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +@SuppressWarnings("initialization") +public class ImmutabilityReImInferenceTest extends CFInferenceTest { + + public ImmutabilityReImInferenceTest(File testFile) { + super(testFile, PICOInferenceChecker.class, "", + "-Anomsgtext", + "-Astubs=src/main/java/pico/inference/jdk.astub", + "-d", "testdata/reiminfer"); + } + + @Override + public Pair> getSolverNameAndOptions() { + return Pair.of(PICOSolverEngine.class.getCanonicalName(), + new ArrayList<>(Arrays.asList("useGraph=false", "collectStatistic=true"))); + } + + @Override + public boolean useHacks() { + return true; + } + + @Parameters + public static List getTestFiles(){ + //InferenceTestUtilities.findAllSystemTests(); + return new ArrayList<>(TestUtilities.findRelativeNestedJavaFiles("testinput", "reiminfer")); + } +} diff --git a/testinput/reiminfer/CFLTests1.java b/testinput/reiminfer/CFLTests1.java new file mode 100644 index 0000000..c4ce5a2 --- /dev/null +++ b/testinput/reiminfer/CFLTests1.java @@ -0,0 +1,50 @@ + + +class MyA { + String f; +} + +class List{ + Object[] elems; + int count; + List() { + Object[] t = new Object[10]; + this.elems = t; + } + void add(Object m) { + Object[] t = this.elems; + t[count++] = m; + } + Object get(int ind) { + Object[] t = this.elems; + Object p = t[ind]; + return p; + } +} + +class ListClient { + List list; + ListClient(List l) { + this.list = l; + } + Object retrieve() { + List t = this.list; + Object r = t.get(0); + return r; + } +} + +public class CFLTests1 { + public static void main(String[] arg) { + List l1 = new List(); + MyA t = new MyA(); + l1.add(t); + ListClient client = new ListClient(l1); + List l2 = new List(); + MyA i = new MyA(); i.f = "abc"; + l2.add(i); + MyA s = (MyA) client.retrieve(); + MyA j = (MyA) l2.get(0); + String str = s.f; + } +} diff --git a/testinput/reiminfer/CFLTests2.java b/testinput/reiminfer/CFLTests2.java new file mode 100644 index 0000000..4d20033 --- /dev/null +++ b/testinput/reiminfer/CFLTests2.java @@ -0,0 +1,18 @@ +class MyB { + String f; +} + +public class CFLTests2 { + + public static MyB id(MyB p) { + return p; + } + + public static void main(String[] arg) { + MyB b1 = new MyB(); //o1 + MyB b2 = new MyB(); //o2 + + MyB b3 = id(b1); + MyB b4 = id(b2); + } +} diff --git a/testinput/reiminfer/CFLTests3.java b/testinput/reiminfer/CFLTests3.java new file mode 100644 index 0000000..ab1a101 --- /dev/null +++ b/testinput/reiminfer/CFLTests3.java @@ -0,0 +1,25 @@ +class Data { + String d; + public void set(String p) { + this.d = p; + } + + public String get() { + return this.d; + } +} + + +public class CFLTests3 { + public static void main() { + Data data = new Data(); + String s1 = new String("Ana"); + data.set(s1); + String s2 = data.get(); + + Data data2 = new Data(); + String s3 = new String("Antun"); + data2.set(s3); + String s4 = data2.get(); + } +} diff --git a/testinput/reiminfer/CFLTests4.java b/testinput/reiminfer/CFLTests4.java new file mode 100644 index 0000000..7a07a16 --- /dev/null +++ b/testinput/reiminfer/CFLTests4.java @@ -0,0 +1,39 @@ + +public class CFLTests4 { + String[] arr1 = new String[10]; + String[] arr2 = new String[10]; + /* + public CFLTests4() { + arr1 = new String[10]; + arr2 = new String[10]; + } + */ + + public void putInArr1(String s) { + arr1[0] = s; + } + + public void putInArr2(String s) { + arr2[0] = s; + } + + public String getFromArr1() { + return arr1[0]; + } + + public String getFromArr2() { + return arr2[0]; + } + + public static void main(String[] arg) { + CFLTests4 c = new CFLTests4(); + String s1 = new String("Ana"); + String s2 = new String("Antun"); + c.putInArr1(s1); + c.putInArr2(s2); + + String s3 = c.getFromArr1(); + String s4 = c.getFromArr2(); + System.out.println(s3+" "+s4); + } +} diff --git a/testinput/reiminfer/CFLTests5.java b/testinput/reiminfer/CFLTests5.java new file mode 100644 index 0000000..b74f7f1 --- /dev/null +++ b/testinput/reiminfer/CFLTests5.java @@ -0,0 +1,45 @@ + +public class CFLTests5 { + + public void putInArr(String s, String[] arr1) { + arr1[0] = s; + } + /* + public void putInArr2(String s, String[] arr2) { + arr2[0] = s; + } + */ + + public String getFromArr(String[] arr1) { + return arr1[0]; + } + /* + public String getFromArr2(String[] arr2) { + return arr2[0]; + } + */ + + public static void main(String[] arg) { + CFLTests5 c = new CFLTests5(); + String[] arr1 = new String[10]; + String[] arr2 = new String[10]; + + String s1 = new String("Ana"); + String s2 = new String("Antun"); + + c.putInArr(s1,arr1); + c.putInArr(s2,arr2); + + String s3 = c.getFromArr(arr1); + String s4 = c.getFromArr(arr2); + System.out.println(s3+" "+s4); + + String[] arr3 = new String[10]; + String s7 = new String("Pooch"); + arr3[0] = s7; + String s5 = arr3[0]; + String s6 = s5; + + } +} + \ No newline at end of file diff --git a/testinput/reiminfer/CFLTests6.java b/testinput/reiminfer/CFLTests6.java new file mode 100644 index 0000000..5f4c65c --- /dev/null +++ b/testinput/reiminfer/CFLTests6.java @@ -0,0 +1,30 @@ +interface I { + public void set(String p); + public String get(); +} + +class IData implements I { + String d; + public void set(String p) { + this.d = p; + } + + public String get() { + return this.d; + } +} + + +public class CFLTests6 { + public static void main() { + I data = new IData(); + String s1 = new String("Ana"); + data.set(s1); + String s2 = data.get(); + + I data2 = new IData(); + String s3 = new String("Antun"); + data2.set(s3); + String s4 = data2.get(); + } +} diff --git a/testinput/reiminfer/CFLTests7.java b/testinput/reiminfer/CFLTests7.java new file mode 100644 index 0000000..4983976 --- /dev/null +++ b/testinput/reiminfer/CFLTests7.java @@ -0,0 +1,30 @@ +class DD { + String f; +} + + +public class CFLTests7 { + + public static DD dd = new DD(); + + public static String theString; + + public static void main(String[] arg) { + String s1 = new String("Ana"); + String s2 = new String("Antun"); + + theString = s1; + dd.f = s2; + + m(); + n(); + } + + public static void m() { + String s3 = theString; + } + + public static void n() { + String s4 = dd.f; + } +} diff --git a/testinput/reiminfer/CFLTests8.java b/testinput/reiminfer/CFLTests8.java new file mode 100644 index 0000000..110a0f3 --- /dev/null +++ b/testinput/reiminfer/CFLTests8.java @@ -0,0 +1,42 @@ +import java.util.ArrayList; +import java.util.HashMap; + +class MyCrap { + int i; +} + + +public class CFLTests8 { + + static HashMap hashMap = new HashMap(); + + HashMap map = new HashMap(); + ArrayList l = new ArrayList(); + + public void m() { + MyCrap m1 = new MyCrap(); + MyCrap m2 = new MyCrap(); + hashMap.put("Ana",m1); + + map.put("Sweety",m2); + } + + public void n() { + MyCrap m3 = hashMap.get("Ana"); + MyCrap m4 = map.get("Sweety"); + l.add(m4); + } + + public void o() { + MyCrap m5 = l.get(0); + } + + public static void main(String[] args) { + CFLTests8 cfl = new CFLTests8(); + cfl.m(); + cfl.n(); + cfl.o(); + } + + +} diff --git a/testinput/reiminfer/CircularInitialization.java b/testinput/reiminfer/CircularInitialization.java new file mode 100644 index 0000000..cadcce6 --- /dev/null +++ b/testinput/reiminfer/CircularInitialization.java @@ -0,0 +1,35 @@ +class Person { + String name; + Person partner; + + public Person(String name) { + this.name = name; + } + + public void m(Person p) { + this.partner = p; + } +} + +class Couple { + Person husband; + Person wife; + public Couple(Person husband, Person wife) { + this.husband = husband; + this.wife = wife; + } + public void print() { + System.out.println(husband.name+" and "+wife.name); + } +} + +public class CircularInitialization { + public static void main(String[] args) { + Person bob = new Person("Bob"); + Person sally = new Person("Sally"); + bob.m(sally); + sally.m(bob); + Couple c = new Couple(bob,sally); + c.print(); + } +} diff --git a/testinput/reiminfer/ImmutableArrays.java b/testinput/reiminfer/ImmutableArrays.java new file mode 100644 index 0000000..c116d20 --- /dev/null +++ b/testinput/reiminfer/ImmutableArrays.java @@ -0,0 +1,38 @@ +public class ImmutableArrays { + char[] a; + + ImmutableArrays() { + a = new char[10]; + for (int i=0; i<10; i++) { + a[i] = 'a'; + } + //b[0] = 'b'; + } + + void m(StringBuffer b) { + System.out.println(b.toString()); + a[0] = 'a'; + System.out.println(b.toString()); + b.append("ana"); + } + void n() { + char[] b = a; + b[1] = 'b'; + } + + + public static void main(String[] args) { + char[] bb = new char[100]; + bb[5] = 'a'; + ImmutableArrays i = new ImmutableArrays(); + i.m(new StringBuffer("Boza")); + i.n(); + if (i.a[1] == 'b') + System.out.println("AHA"); + else + System.out.println("A-NO"); + // i.a[2] = 'c'; + StringBuffer b = new StringBuffer("Ana"); + System.out.println(i.a[1]+i.a[0]); + } +} diff --git a/testinput/reiminfer/InitMethod.java b/testinput/reiminfer/InitMethod.java new file mode 100644 index 0000000..36c4a48 --- /dev/null +++ b/testinput/reiminfer/InitMethod.java @@ -0,0 +1,30 @@ +class A { + char[] arr; + public A() { + arr = new char[10]; + arr[0] = 'a'; + init("Ana"); + } + + void init(String arg) { + System.out.println(arg); + for (int i=0; i<10; i++) { + System.out.println(arr[i]); + //arr[i] = 'b'; + } + //m(arr); + } + + void m(char[] b) { + b[0]='a'; + } + +} + +public class InitMethod { + public static void main(String[] arg) { + A a = new A(); + a.init("Ana"); + //a.m(a.arr); + } +} diff --git a/testinput/reiminfer/Library.java b/testinput/reiminfer/Library.java new file mode 100644 index 0000000..e6bf73d --- /dev/null +++ b/testinput/reiminfer/Library.java @@ -0,0 +1,16 @@ +import java.util.*; + +public class Library { + + public void foo() { + Set set = new HashSet(); + X a1 = new X(); + set.add(a1); + Set set2 = set; + int size = set2.size(); + } +} + +class X { + String f; +} diff --git a/testinput/reiminfer/LibraryUse.java b/testinput/reiminfer/LibraryUse.java new file mode 100644 index 0000000..13ac96a --- /dev/null +++ b/testinput/reiminfer/LibraryUse.java @@ -0,0 +1,12 @@ +import java.util.*; + +public class LibraryUse { + public static void main(String[] arg) { + ArrayList al = new ArrayList(); + al.add("Boza"); + al.add("Katarina"); + ArrayList al1 = al; + int i = al1.size(); + System.out.println(i); + } +} diff --git a/testinput/reiminfer/Test1.java b/testinput/reiminfer/Test1.java new file mode 100644 index 0000000..8a2b224 --- /dev/null +++ b/testinput/reiminfer/Test1.java @@ -0,0 +1,44 @@ + +class B { + protected StringBuffer name; + + public B(StringBuffer s) { + name = s; + } +} + +class C extends B { + + public C() { + super(new StringBuffer("Ana")); + } + + public void set() { + name.append("Boza"); + } + public String get() { + return name.toString(); + } +} + +class D { + C c = new C(); + public void m() { + c.set(); + } + + public String n() { + return c.get(); + } +} + +public class Test1 { + + public static void main(String[] arg) { + D d = new D(); + d.m(); + d.n(); + + } + +} diff --git a/testinput/reiminfer/Test2.java b/testinput/reiminfer/Test2.java new file mode 100644 index 0000000..393aa07 --- /dev/null +++ b/testinput/reiminfer/Test2.java @@ -0,0 +1,21 @@ + +public class Test2 { + + public static char[] copy(char[] a) { + char[] r = new char[a.length]; + for (int i=0; i < a.length; i++) { + r[i] = a[i]; + } + return r; + } + + public static void main(String[] argc) { + char[] a = new char[5]; + for (int i=0; i<5; i++) + a[i] = 'a'; + char[] r = copy(a); + // System.out.println(r); + //r[0] = 'a'; + } + +} diff --git a/testinput/reiminfer/Test3.java b/testinput/reiminfer/Test3.java new file mode 100644 index 0000000..4ec7b0a --- /dev/null +++ b/testinput/reiminfer/Test3.java @@ -0,0 +1,40 @@ +class F { + char[] arr; +} + +class E { + + char[] arr; + void m() { + arr = new char[5]; + for (int i=0; i<5; i++) { + arr[i] = 10; + } + } + char[] n() { + return arr; + } + void o(F p) { + p.arr = arr; + } +} + + +public class Test3 { + public static E create() { + E e = new E(); + e.m(); + return e; + } + public static void main(String[] argc) { + E d = create(); + // E d = new E(); + // d.m(); + // char[] b = d.n(); + F f = new F(); + d.o(f); + char[] b = f.arr; + b[0] = 'a'; + System.out.println(b[0]); + } +} diff --git a/testinput/reiminfer/Test4.java b/testinput/reiminfer/Test4.java new file mode 100644 index 0000000..d708cbb --- /dev/null +++ b/testinput/reiminfer/Test4.java @@ -0,0 +1,50 @@ +class BB { + int i; +} + +class AA { + char[] arr; + BB f; + AA() { + arr = new char[2]; + f = new BB(); + } + + BB get() { return f; } + + char[] getArray() { return arr; } + + static AA createInstance() { + return new AA(); + } + + + } + + +public class Test4 { + + BB f; + + BB m() { + AA a = AA.createInstance(); + BB bb = a.get(); + f = bb; + return bb; + } + + void n() { + f.i = 0; + } + + static Test4 create() { + return new Test4(); + } + + public static void main(String[] arg) { + Test4 t = create(); + BB bb = t.m(); + t.n(); + // bb.i = 0; + } +} diff --git a/testinput/reiminfer/TheClass.java b/testinput/reiminfer/TheClass.java new file mode 100644 index 0000000..8863c93 --- /dev/null +++ b/testinput/reiminfer/TheClass.java @@ -0,0 +1,72 @@ +import qual.*; + +public class TheClass { + public @PolyMutable String f; + public static String sf; + public Object of = null; + public static int sof = 1; + +// public TheClass() { +// f = "hello"; +// } + +// public TheClass(String p1) { +// this.f = p1; +// } + + public String doSomethingElse( + @PolyMutable TheClass this, + String p1, + Object p2, + int p4) { + String s1 = p1; + String s2 = s1; + String s3 = s2 + " world"; + return s3; + } + + public String doSomething(@Readonly TheClass this){ + String s = doSomethingElse(f, null, 1); + return s; + } + +// public String doMore() { +// String d1 = "hello"; +// TheClass c = new TheClass(); +// f = d1; +// c.f = d1; +// TheClass c2 = new TheClass(); +// c = c2; +// return c.f; +// } + +// public String toString() { +// return f; +// } + +// public boolean equals(Object o) { +// return true; +// } +} + +class AnotherClass extends TheClass { + + +// @Override +// public void doSomething(){ +// String[] localA2 = testArrays2(); +// String[] localA3 = localA2; +// } + + +// String[] fieldA; +// +// public void testArrays() { +// String[] localA = this.fieldA; +// } + +// public String[] testArrays2() { +// return fieldA; +// } + +} diff --git a/testinput/reiminfer/ThisLeak.java b/testinput/reiminfer/ThisLeak.java new file mode 100644 index 0000000..38fdebd --- /dev/null +++ b/testinput/reiminfer/ThisLeak.java @@ -0,0 +1,26 @@ +class AAA { + ThisLeak f; +} + +public class ThisLeak { + private ThisLeak f; + + public void m() { + this.f = this; + System.out.println("Tests leak into field of this"); + } + + public void n(AAA a) { + a.f = this; + System.out.println("Tests leak into field of parameter"); + } + + public static void main(String[] arg) { + ThisLeak tl = new ThisLeak(); + AAA aaa = new AAA(); + tl.m(); + tl.n(aaa); + + } + +} diff --git a/testinput/reiminfer/ThisLeak2.java b/testinput/reiminfer/ThisLeak2.java new file mode 100644 index 0000000..f33defb --- /dev/null +++ b/testinput/reiminfer/ThisLeak2.java @@ -0,0 +1,20 @@ +class Helper { + ThisLeak2 f; +} + +public class ThisLeak2 { + public void m(Helper h) { + n(h); + } + public void n(Helper h) { + ThisLeak2[] arr = new ThisLeak2[1]; + arr[0] = this; + // h.f = this; + } + + public static void main(String[] arg) { + ThisLeak2 tl2 = new ThisLeak2(); + Helper h = new Helper(); + tl2.m(h); + } +} diff --git a/testinput/reiminfer/ThisLeak3.java b/testinput/reiminfer/ThisLeak3.java new file mode 100644 index 0000000..4312529 --- /dev/null +++ b/testinput/reiminfer/ThisLeak3.java @@ -0,0 +1,18 @@ +class ZZZ { + ThisLeak3 f; +} +public class ThisLeak3 { + void m(ZZZ z, ThisLeak3 p) { + z.f = p; + } + + void n(ZZZ z) { + m(z,this); + } + + public static void main(String[] arg) { + ThisLeak3 tl3 = new ThisLeak3(); + ZZZ z = new ZZZ(); + tl3.n(z); + } +} diff --git a/testinput/reiminfer/ThisLeak4.java b/testinput/reiminfer/ThisLeak4.java new file mode 100644 index 0000000..8149c5e --- /dev/null +++ b/testinput/reiminfer/ThisLeak4.java @@ -0,0 +1,24 @@ +public class ThisLeak4 { + + public void n() { + System.out.println("This is n"); + } + + public void o() { + innerClass ic = new innerClass(); + ic.m(); + } + + public static void main(String[] arg) { + ThisLeak4 tl4 = new ThisLeak4(); + tl4.o(); + } + + public class innerClass { + int f; + public void m() { + System.out.println("Boza"); + n(); + } + } +} diff --git a/testinput/reiminfer/ThisLeak5.java b/testinput/reiminfer/ThisLeak5.java new file mode 100644 index 0000000..bc01b4d --- /dev/null +++ b/testinput/reiminfer/ThisLeak5.java @@ -0,0 +1,21 @@ +class Boza { + ThisLeak5 f; + int y; +} +public class ThisLeak5 { + int x; + Boza b; + + public ThisLeak5() { + ThisLeak5 p = this; + + p.b = new Boza(); + p.x = 0; + + int y = p.x; + Boza bb = p.b; + this.b = bb; + this.x = y; + } + +} diff --git a/testinput/reiminfer/ThisLeak6.java b/testinput/reiminfer/ThisLeak6.java new file mode 100644 index 0000000..588e8fe --- /dev/null +++ b/testinput/reiminfer/ThisLeak6.java @@ -0,0 +1,22 @@ +class YYY { + ThisLeak6 f; +} + +public class ThisLeak6 { + int x; + + public ThisLeak6 id() { + return this; + } + + public void m() { + System.out.println("fjdfkjd"); + ThisLeak6 tl6 = id(); + tl6.x = 0; + } + + public void n(YYY y) { + y.f = id(); + } + +} From bb9678c3096704bed8ef0658ee7f5694854bbaf8 Mon Sep 17 00:00:00 2001 From: lnsun <57457122+lnsun@users.noreply.github.com> Date: Thu, 25 Mar 2021 14:36:19 -0400 Subject: [PATCH 134/144] glacier test cases --- .../ImmutabilityTypecheckGlacierTest.java | 23 +++++++++ testinput/glacier/ArrayClone.java | 7 +++ testinput/glacier/ArrayCopy.java | 18 +++++++ testinput/glacier/ArrayParameter.java | 22 +++++++++ testinput/glacier/Arrays.java | 21 ++++++++ testinput/glacier/BooleanSubtyping.java | 16 ++++++ testinput/glacier/ClassGetName.java | 6 +++ testinput/glacier/ClassReturn.java | 9 ++++ testinput/glacier/ColorImpl.java | 18 +++++++ testinput/glacier/CompareTo.java | 11 +++++ testinput/glacier/ConflictingAnnotations.java | 33 +++++++++++++ testinput/glacier/ConstructorAssignment.java | 24 +++++++++ testinput/glacier/EnumTest.java | 19 +++++++ testinput/glacier/EqualsTest.java | 23 +++++++++ testinput/glacier/FontImpl.java | 34 +++++++++++++ testinput/glacier/ImmutableArray.java | 16 ++++++ .../ImmutableClassMutableInterface.java | 14 ++++++ testinput/glacier/ImmutableCollection.java | 11 +++++ .../ImmutableConstructorInMutableClass.java | 15 ++++++ .../glacier/ImmutableInterfaceClass.java | 11 +++++ testinput/glacier/ImmutablePerson.java | 48 ++++++++++++++++++ .../glacier/ImmutablePrimitiveContainer.java | 12 +++++ testinput/glacier/IncorrectUsage.java | 46 +++++++++++++++++ testinput/glacier/IntArgument.java | 12 +++++ testinput/glacier/IntReturn.java | 11 +++++ testinput/glacier/IntegerAssignment.java | 11 +++++ testinput/glacier/InterfaceField.java | 14 ++++++ testinput/glacier/InterfaceSupertype.java | 32 ++++++++++++ testinput/glacier/InvalidAnnotations.java | 12 +++++ testinput/glacier/InvalidAssignment.java | 49 +++++++++++++++++++ .../InvalidImmutableInterfaceClass.java | 14 ++++++ testinput/glacier/InvalidTypeArguments.java | 39 +++++++++++++++ testinput/glacier/MapParameters.java | 13 +++++ testinput/glacier/MethodInvocation.java | 14 ++++++ testinput/glacier/MethodTypeParameters.java | 7 +++ ...MutableClassCantHaveImmutableSubclass.java | 41 ++++++++++++++++ testinput/glacier/NestedGenerics.java | 21 ++++++++ testinput/glacier/NullAssignment.java | 14 ++++++ testinput/glacier/Override.java | 18 +++++++ testinput/glacier/PlainObjects.java | 19 +++++++ testinput/glacier/ReadOnlyClass.java | 27 ++++++++++ testinput/glacier/ReadOnlyObject.java | 13 +++++ testinput/glacier/ResultWrapTest.java | 16 ++++++ testinput/glacier/StaticAssignment.java | 22 +++++++++ testinput/glacier/StringTest.java | 10 ++++ testinput/glacier/Subclassing.java | 18 +++++++ testinput/glacier/Transitivity.java | 20 ++++++++ testinput/glacier/TransitivityError.java | 23 +++++++++ testinput/glacier/TypeParameter.java | 32 ++++++++++++ testinput/glacier/Unboxing.java | 8 +++ testinput/glacier/UnmodifiableCollection.java | 14 ++++++ testinput/glacier/UnmodifiableList.java | 24 +++++++++ .../glacier/ValidImmutableInterfaceClass.java | 11 +++++ 53 files changed, 1036 insertions(+) create mode 100644 src/test/java/pico/ImmutabilityTypecheckGlacierTest.java create mode 100644 testinput/glacier/ArrayClone.java create mode 100644 testinput/glacier/ArrayCopy.java create mode 100644 testinput/glacier/ArrayParameter.java create mode 100644 testinput/glacier/Arrays.java create mode 100644 testinput/glacier/BooleanSubtyping.java create mode 100644 testinput/glacier/ClassGetName.java create mode 100644 testinput/glacier/ClassReturn.java create mode 100644 testinput/glacier/ColorImpl.java create mode 100644 testinput/glacier/CompareTo.java create mode 100644 testinput/glacier/ConflictingAnnotations.java create mode 100644 testinput/glacier/ConstructorAssignment.java create mode 100644 testinput/glacier/EnumTest.java create mode 100644 testinput/glacier/EqualsTest.java create mode 100644 testinput/glacier/FontImpl.java create mode 100644 testinput/glacier/ImmutableArray.java create mode 100644 testinput/glacier/ImmutableClassMutableInterface.java create mode 100644 testinput/glacier/ImmutableCollection.java create mode 100644 testinput/glacier/ImmutableConstructorInMutableClass.java create mode 100644 testinput/glacier/ImmutableInterfaceClass.java create mode 100644 testinput/glacier/ImmutablePerson.java create mode 100644 testinput/glacier/ImmutablePrimitiveContainer.java create mode 100644 testinput/glacier/IncorrectUsage.java create mode 100644 testinput/glacier/IntArgument.java create mode 100644 testinput/glacier/IntReturn.java create mode 100644 testinput/glacier/IntegerAssignment.java create mode 100644 testinput/glacier/InterfaceField.java create mode 100644 testinput/glacier/InterfaceSupertype.java create mode 100644 testinput/glacier/InvalidAnnotations.java create mode 100644 testinput/glacier/InvalidAssignment.java create mode 100644 testinput/glacier/InvalidImmutableInterfaceClass.java create mode 100644 testinput/glacier/InvalidTypeArguments.java create mode 100644 testinput/glacier/MapParameters.java create mode 100644 testinput/glacier/MethodInvocation.java create mode 100644 testinput/glacier/MethodTypeParameters.java create mode 100644 testinput/glacier/MutableClassCantHaveImmutableSubclass.java create mode 100644 testinput/glacier/NestedGenerics.java create mode 100644 testinput/glacier/NullAssignment.java create mode 100644 testinput/glacier/Override.java create mode 100644 testinput/glacier/PlainObjects.java create mode 100644 testinput/glacier/ReadOnlyClass.java create mode 100644 testinput/glacier/ReadOnlyObject.java create mode 100644 testinput/glacier/ResultWrapTest.java create mode 100644 testinput/glacier/StaticAssignment.java create mode 100644 testinput/glacier/StringTest.java create mode 100644 testinput/glacier/Subclassing.java create mode 100644 testinput/glacier/Transitivity.java create mode 100644 testinput/glacier/TransitivityError.java create mode 100644 testinput/glacier/TypeParameter.java create mode 100644 testinput/glacier/Unboxing.java create mode 100644 testinput/glacier/UnmodifiableCollection.java create mode 100644 testinput/glacier/UnmodifiableList.java create mode 100644 testinput/glacier/ValidImmutableInterfaceClass.java diff --git a/src/test/java/pico/ImmutabilityTypecheckGlacierTest.java b/src/test/java/pico/ImmutabilityTypecheckGlacierTest.java new file mode 100644 index 0000000..eb7857a --- /dev/null +++ b/src/test/java/pico/ImmutabilityTypecheckGlacierTest.java @@ -0,0 +1,23 @@ +package pico; + +import org.checkerframework.framework.test.CheckerFrameworkPerFileTest; +import org.checkerframework.framework.test.TestUtilities; +import org.junit.Ignore; +import org.junit.runners.Parameterized.Parameters; +import pico.typecheck.PICOChecker; + +import java.io.File; +import java.util.ArrayList; +import java.util.List; + +public class ImmutabilityTypecheckGlacierTest extends CheckerFrameworkPerFileTest { + public ImmutabilityTypecheckGlacierTest(File testFile) { + super(testFile, PICOChecker.class, "", "-Anomsgtext", + "-Anocheckjdk", "-d", "testTmp/glacier"); + } + + @Parameters + public static List getTestFiles(){ + return new ArrayList<>(TestUtilities.findRelativeNestedJavaFiles("testinput", "glacier")); + } +} diff --git a/testinput/glacier/ArrayClone.java b/testinput/glacier/ArrayClone.java new file mode 100644 index 0000000..e9a16ab --- /dev/null +++ b/testinput/glacier/ArrayClone.java @@ -0,0 +1,7 @@ +// Change to PICO; array clone adaption. +import qual.*; + +public class ArrayClone { + @Immutable Object @Immutable [] array = new @Immutable Object @Immutable [0]; + @Immutable Object @Immutable [] arrayClone = array.clone(); +} \ No newline at end of file diff --git a/testinput/glacier/ArrayCopy.java b/testinput/glacier/ArrayCopy.java new file mode 100644 index 0000000..ba4e8f4 --- /dev/null +++ b/testinput/glacier/ArrayCopy.java @@ -0,0 +1,18 @@ +// @skip-test +// Typecheck: Failed +// Cause: checker cannot get clone() decl from stub! Decl receiver and return type get defaulted to mutable. +// strange enough, seems other signatures are got from stubs. +import qual.Immutable; + + +public class ArrayCopy { + public void takeArray(@Immutable Object @Immutable [] array) { + } + + public void passArray() { + @Immutable Object array[] = new @Immutable Object[5]; + + takeArray(array.clone()); + } + +} \ No newline at end of file diff --git a/testinput/glacier/ArrayParameter.java b/testinput/glacier/ArrayParameter.java new file mode 100644 index 0000000..81f77b4 --- /dev/null +++ b/testinput/glacier/ArrayParameter.java @@ -0,0 +1,22 @@ +import qual.*; +import java.util.Arrays; + + +class Array { + public static T[] add(final T @Readonly[] array, final T entry) { + final int s = array.length; + final T[] t = Arrays.copyOf(array, s + 1); + t[s] = entry; + return t; + } +} + + +public class ArrayParameter { + final @Immutable Object @Immutable [] keys = new @Immutable Object @Immutable [0]; + + + public void passArray(@Immutable Object k) { + Array.add(keys, k); + } +} \ No newline at end of file diff --git a/testinput/glacier/Arrays.java b/testinput/glacier/Arrays.java new file mode 100644 index 0000000..3fde23c --- /dev/null +++ b/testinput/glacier/Arrays.java @@ -0,0 +1,21 @@ +package glacier; + +public class Arrays { + int [] intArray; + + public Arrays() { + + } + + public int[] getData() { + return intArray; + } + + public byte[] getByteData() { + return new byte[0]; + } + + public void setData() { + intArray[0] = 42; + } +} \ No newline at end of file diff --git a/testinput/glacier/BooleanSubtyping.java b/testinput/glacier/BooleanSubtyping.java new file mode 100644 index 0000000..dcae5b7 --- /dev/null +++ b/testinput/glacier/BooleanSubtyping.java @@ -0,0 +1,16 @@ +public class BooleanSubtyping { + public enum CellType { + BLANK(3), + NUMBER(0); + + private int value; + private CellType(int value) { + this.value = value; + } + } + + public boolean foo() { + CellType cellType = CellType.BLANK; + return cellType == CellType.NUMBER; + } +} \ No newline at end of file diff --git a/testinput/glacier/ClassGetName.java b/testinput/glacier/ClassGetName.java new file mode 100644 index 0000000..ca11263 --- /dev/null +++ b/testinput/glacier/ClassGetName.java @@ -0,0 +1,6 @@ +public class ClassGetName { + public String foo() { + Number num = null; + return num.getClass().getName(); + } +} \ No newline at end of file diff --git a/testinput/glacier/ClassReturn.java b/testinput/glacier/ClassReturn.java new file mode 100644 index 0000000..25e23b4 --- /dev/null +++ b/testinput/glacier/ClassReturn.java @@ -0,0 +1,9 @@ +// typecheck: OK + + +public class ClassReturn { + public ClassReturn getInstance() { + return new ClassReturn(); + } + +} \ No newline at end of file diff --git a/testinput/glacier/ColorImpl.java b/testinput/glacier/ColorImpl.java new file mode 100644 index 0000000..2ff6a09 --- /dev/null +++ b/testinput/glacier/ColorImpl.java @@ -0,0 +1,18 @@ +// @skip-test +// Typecheck, Inference-TC: Failed +// Cause: checker cannot get clone() decl from stub! Decl receiver and return type get defaulted to mutable. +// strange enough, seems other signatures are got from stubs. +import java.util.Arrays; +import qual.Immutable; + +public class ColorImpl { + private byte @Immutable [] _rgb; + private byte [] _mutableRgb; + + public int hashCode() { + // This should be OK, but will be an error until we have a @ReadOnly annotation. + Arrays.hashCode(_mutableRgb); + + return Arrays.hashCode(_rgb); + } +} \ No newline at end of file diff --git a/testinput/glacier/CompareTo.java b/testinput/glacier/CompareTo.java new file mode 100644 index 0000000..b1b9a1c --- /dev/null +++ b/testinput/glacier/CompareTo.java @@ -0,0 +1,11 @@ +package glacier; + +public class CompareTo { + + public void foo() { + Object val1 = null; + Object val2 = null; + // Shouldn't give an error. + ((Boolean)val1).compareTo((Boolean)val2); + } +} \ No newline at end of file diff --git a/testinput/glacier/ConflictingAnnotations.java b/testinput/glacier/ConflictingAnnotations.java new file mode 100644 index 0000000..1729ae5 --- /dev/null +++ b/testinput/glacier/ConflictingAnnotations.java @@ -0,0 +1,33 @@ +import qual.*; + +@Immutable class ImmutClass { +} + +@Mutable class MutableClass { +} + +class DefaultMutableClass { +} + +@Immutable interface ImmutInterface { +} + +interface MutableInterface { +} + +public class ConflictingAnnotations { + // ::error: (type.invalid.annotations.on.use) + @Mutable ImmutClass o1; + + // ::error: (type.invalid.annotations.on.use) + @Immutable MutableClass o2; + + // ::error: (type.invalid.annotations.on.use) + @Immutable DefaultMutableClass o3; + + // ::error: (type.invalid.annotations.on.use) + @Mutable ImmutInterface i1; + + // ::error: (type.invalid.annotations.on.use) + @Immutable MutableInterface i2; +} \ No newline at end of file diff --git a/testinput/glacier/ConstructorAssignment.java b/testinput/glacier/ConstructorAssignment.java new file mode 100644 index 0000000..4f9db96 --- /dev/null +++ b/testinput/glacier/ConstructorAssignment.java @@ -0,0 +1,24 @@ +package glacier; + +import qual.Immutable; + +public @Immutable class ConstructorAssignment { + public int x = 3; // static assignment is OK + + ConstructorAssignment() { + x = 4; // constructor assignment is OK + } + + void setX() { + // ::error: (illegal.field.write) + x = 5; + } +} + +class OtherClass { + OtherClass() { + ConstructorAssignment c = new ConstructorAssignment(); + // ::error: (illegal.field.write) + c.x = 6; + } +} \ No newline at end of file diff --git a/testinput/glacier/EnumTest.java b/testinput/glacier/EnumTest.java new file mode 100644 index 0000000..6d0b16a --- /dev/null +++ b/testinput/glacier/EnumTest.java @@ -0,0 +1,19 @@ +package glacier; + +import qual.Immutable; + +@Immutable enum Underline{ + NONE, + SINGLE, + DOUBLE, + SINGLE_ACCOUNTING, + DOUBLE_ACCOUNTING +} + +public class EnumTest { + Underline u; + + public void foo() { + u.ordinal(); + } +} \ No newline at end of file diff --git a/testinput/glacier/EqualsTest.java b/testinput/glacier/EqualsTest.java new file mode 100644 index 0000000..50c6640 --- /dev/null +++ b/testinput/glacier/EqualsTest.java @@ -0,0 +1,23 @@ +// @skip-test +package glacier; + +import qual.*; + +@Immutable public class EqualsTest { + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + return false; + } +} + +class EqualsTest2 { + @Override + // ::error: (override.param.invalid) + public boolean equals(@Immutable Object obj) { + if (this == obj) + return true; + return false; + } +} \ No newline at end of file diff --git a/testinput/glacier/FontImpl.java b/testinput/glacier/FontImpl.java new file mode 100644 index 0000000..82a8f40 --- /dev/null +++ b/testinput/glacier/FontImpl.java @@ -0,0 +1,34 @@ +// Note: PICO defaults the class decal to RDM. Removing errors on line 26 and 30. + +import qual.*; + +@Immutable interface SColor { +} + +@Immutable abstract class AbstractColorAdv implements SColor { +} + +// ::error: (type.invalid.annotations.on.use) +class FI_ColorImpl extends AbstractColorAdv { + // Arguably it would be preferable for this to not be an error. + // ::error: (type.invalid.annotations.on.use) + public static final AbstractColorAdv BLACK = new FI_ColorImpl("#000000"); + + // PICO Note: adding this error. + // ::error: (super.invocation.invalid) + FI_ColorImpl(String fontColor) { + + } +} + +public class FontImpl { + FontImpl(String fontColor) { + // Arguably it would be preferable for this to not be an error. + // Removing error. PICO chose the preferable path. + SColor a = new FI_ColorImpl(fontColor); + + // Arguably it would be preferable for this to not be an error either. + // Removing error. PICO chose the preferable path. + SColor c = fontColor != null ? new FI_ColorImpl(fontColor) : null; + } +} \ No newline at end of file diff --git a/testinput/glacier/ImmutableArray.java b/testinput/glacier/ImmutableArray.java new file mode 100644 index 0000000..631d6b2 --- /dev/null +++ b/testinput/glacier/ImmutableArray.java @@ -0,0 +1,16 @@ +import qual.Immutable; + +@SuppressWarnings("initialization") +@Immutable class ImmutableArray { + private byte @Immutable [] _rgb; + + private String @Immutable [] _strings; + + // Immutable array of mutable objects is mutable. + // PICO allows shallow immutable array. + private java.util.Date @Immutable [] _dates; + + // MaybeMutable array of primitives is mutable. + // PICO allows shallow immutable array. + private int [] _ints; +} \ No newline at end of file diff --git a/testinput/glacier/ImmutableClassMutableInterface.java b/testinput/glacier/ImmutableClassMutableInterface.java new file mode 100644 index 0000000..ab85fb1 --- /dev/null +++ b/testinput/glacier/ImmutableClassMutableInterface.java @@ -0,0 +1,14 @@ +import qual.*; + +@ReceiverDependantMutable +interface ICMI_MutableInterface {}; + +@Immutable public class ImmutableClassMutableInterface implements ICMI_MutableInterface { + public static void bar(ICMI_MutableInterface m) { }; + + public static void foo() { + ImmutableClassMutableInterface x = null; + bar(x); + } + +}; \ No newline at end of file diff --git a/testinput/glacier/ImmutableCollection.java b/testinput/glacier/ImmutableCollection.java new file mode 100644 index 0000000..11833d8 --- /dev/null +++ b/testinput/glacier/ImmutableCollection.java @@ -0,0 +1,11 @@ +// Update to PICO: add AbstractCollection to stub +import qual.Immutable; + +import java.util.*; + +@Immutable +public abstract class ImmutableCollection extends AbstractCollection { + public int getSize() { + return size(); + } +} \ No newline at end of file diff --git a/testinput/glacier/ImmutableConstructorInMutableClass.java b/testinput/glacier/ImmutableConstructorInMutableClass.java new file mode 100644 index 0000000..143e3b5 --- /dev/null +++ b/testinput/glacier/ImmutableConstructorInMutableClass.java @@ -0,0 +1,15 @@ +package glacier; + +import qual.Immutable; + + + +public class ImmutableConstructorInMutableClass { + // :: error: (type.invalid.annotations.on.use) + public @Immutable ImmutableConstructorInMutableClass() { + } + + public void aMethod() { + + } +} \ No newline at end of file diff --git a/testinput/glacier/ImmutableInterfaceClass.java b/testinput/glacier/ImmutableInterfaceClass.java new file mode 100644 index 0000000..1e888fc --- /dev/null +++ b/testinput/glacier/ImmutableInterfaceClass.java @@ -0,0 +1,11 @@ +package glacier; + +import qual.Immutable; + + +@Immutable interface ImmutableInterface { +} + +public @Immutable class ImmutableInterfaceClass implements ImmutableInterface { + +} \ No newline at end of file diff --git a/testinput/glacier/ImmutablePerson.java b/testinput/glacier/ImmutablePerson.java new file mode 100644 index 0000000..e851d4b --- /dev/null +++ b/testinput/glacier/ImmutablePerson.java @@ -0,0 +1,48 @@ +// @skip-test +// Does not apply to PICO: shallow immutable support + +package glacier; + +import qual.Immutable; + +import java.util.Date; + +/* +@Immutable class ImmutableDate { + double secondsSinceEpoch; + + void setSeconds(double s) { + secondsSinceEpoch = s; // Should error! + } +} +public @Immutable class ImmutablePerson { + public ImmutablePerson() { + birthdate = new ImmutableDate(); + + } + + ImmutableDate birthdate; + + public void test() { + + } +} +class Person { + String name; +} +*/ + + +@Immutable public class ImmutablePerson { + // Date is mutable + // :: error: glacier.mutable.invalid + Date birthdate; + + public ImmutablePerson() { + + } + + public void aMethod() { + + } +} \ No newline at end of file diff --git a/testinput/glacier/ImmutablePrimitiveContainer.java b/testinput/glacier/ImmutablePrimitiveContainer.java new file mode 100644 index 0000000..360fb43 --- /dev/null +++ b/testinput/glacier/ImmutablePrimitiveContainer.java @@ -0,0 +1,12 @@ +import qual.Immutable; + +@SuppressWarnings("initialization") +@Immutable +public class ImmutablePrimitiveContainer { + int x; + + public void setX(int x) { + // ::error: (illegal.field.write) + this.x = x; + } +} \ No newline at end of file diff --git a/testinput/glacier/IncorrectUsage.java b/testinput/glacier/IncorrectUsage.java new file mode 100644 index 0000000..d9624f4 --- /dev/null +++ b/testinput/glacier/IncorrectUsage.java @@ -0,0 +1,46 @@ +package glacier; + +import qual.*; + +@Immutable public class IncorrectUsage { + public void okMethod(IncorrectUsage this) { + + } + + public void okMethod2(@Immutable IncorrectUsage this) { + + } + + // PICO Note: adding method.receiver.incompatible + // ::error: (type.invalid.annotations.on.use) ::error: (method.receiver.incompatible) + public void badMethod(@Mutable IncorrectUsage this) { + + } + + // ::error: (type.invalid.annotations.on.use) + public void badMethod2(@Mutable IncorrectUsage obj) { + + } +} + + +class IncorrectUsage2 { + public void okMethod(IncorrectUsage2 this) { + + } + + public void okMethod2(@Mutable IncorrectUsage2 this) { + + } + + // PICO Note: adding method.receiver.incompatible + // ::error: (type.invalid.annotations.on.use) ::error: (method.receiver.incompatible) + public void badMethod(@Immutable IncorrectUsage2 this) { + + } + + // ::error: (type.invalid.annotations.on.use) + public void badMethod2(@Immutable IncorrectUsage2 obj) { + + } +} \ No newline at end of file diff --git a/testinput/glacier/IntArgument.java b/testinput/glacier/IntArgument.java new file mode 100644 index 0000000..b3156ec --- /dev/null +++ b/testinput/glacier/IntArgument.java @@ -0,0 +1,12 @@ +package glacier; + +public class IntArgument { + public void takeInt(int x) { + } + + public void useInt() { + for (int i = 0; i < 10; i++) { + takeInt(i); + } + } +} \ No newline at end of file diff --git a/testinput/glacier/IntReturn.java b/testinput/glacier/IntReturn.java new file mode 100644 index 0000000..e942f3f --- /dev/null +++ b/testinput/glacier/IntReturn.java @@ -0,0 +1,11 @@ +package glacier; + +public class IntReturn { + public int getInt() { + return 42; + } + + public void useInt() { + int x = getInt(); + } +} \ No newline at end of file diff --git a/testinput/glacier/IntegerAssignment.java b/testinput/glacier/IntegerAssignment.java new file mode 100644 index 0000000..681e93f --- /dev/null +++ b/testinput/glacier/IntegerAssignment.java @@ -0,0 +1,11 @@ +package glacier; + +public class IntegerAssignment { + public Integer getInt() { + return 42; + } + + public void useInt() { + Integer i = getInt(); + } +} \ No newline at end of file diff --git a/testinput/glacier/InterfaceField.java b/testinput/glacier/InterfaceField.java new file mode 100644 index 0000000..a9fc1a5 --- /dev/null +++ b/testinput/glacier/InterfaceField.java @@ -0,0 +1,14 @@ +// @skip-test +// Deep immutable + +import qual.Immutable; + +interface IF_AnInterface {}; + +@Immutable interface IF_ImmutableInterface extends IF_AnInterface {}; + +@Immutable public class InterfaceField { + // ::error: (glacier.mutable.invalid) + IF_AnInterface o; + IF_ImmutableInterface o2; +} \ No newline at end of file diff --git a/testinput/glacier/InterfaceSupertype.java b/testinput/glacier/InterfaceSupertype.java new file mode 100644 index 0000000..1c581d0 --- /dev/null +++ b/testinput/glacier/InterfaceSupertype.java @@ -0,0 +1,32 @@ +package glacier; + +import qual.Immutable; + +interface AnInterface { + +} + + +public class InterfaceSupertype { + + public void takeMutable(Object o) { + + } + + public void takeImmutable(Object o) { + + } + + public void doStuff() { + AnInterface obj = null; + + takeMutable(obj); + takeImmutable(obj); + } + + public boolean equals(Object o1,Object o2){ + if(o1==o2) + return true; + return o1!=null ? o1.equals(o2) : false; + } +} \ No newline at end of file diff --git a/testinput/glacier/InvalidAnnotations.java b/testinput/glacier/InvalidAnnotations.java new file mode 100644 index 0000000..40574d1 --- /dev/null +++ b/testinput/glacier/InvalidAnnotations.java @@ -0,0 +1,12 @@ +// @skip-test +// who cares bottom + +import qual.*; + +// ::error: (type.invalid) +@Bottom class InvalidBottom {}; + +public class InvalidAnnotations { + // ::error: (type.invalid) + InvalidBottom b; +} \ No newline at end of file diff --git a/testinput/glacier/InvalidAssignment.java b/testinput/glacier/InvalidAssignment.java new file mode 100644 index 0000000..26eadf7 --- /dev/null +++ b/testinput/glacier/InvalidAssignment.java @@ -0,0 +1,49 @@ +// @skip-test +// jdk stub difference + +package glacier; + +import java.util.Calendar; + +import qual.Immutable; + + +public @Immutable class InvalidAssignment { + class Inner { + public int i; + } + + String s; + // :: error: glacier.mutable.invalid + Calendar c; + // :: error: glacier.mutable.invalid + Inner i; + int x; + + public void setString(String s) { + // :: error: glacier.assignment + this.s = s; + } + + public void setX(int x) { + // :: error: glacier.assignment + this.x = x; + } + + public void setMonth(int month) { + c.set(Calendar.MONTH, month); // No error here; the problem is that d was mutable in the first place. + } + + public void setInner(int i) { + this.i.i = i; // No error here; the problem is that this.i was mutable in the first place. + } +} + +class AnotherClass { + void aMethod() { + InvalidAssignment i = new InvalidAssignment(); + + // ::error: (glacier.assignment) + i.s = "hello"; + } +} \ No newline at end of file diff --git a/testinput/glacier/InvalidImmutableInterfaceClass.java b/testinput/glacier/InvalidImmutableInterfaceClass.java new file mode 100644 index 0000000..214c990 --- /dev/null +++ b/testinput/glacier/InvalidImmutableInterfaceClass.java @@ -0,0 +1,14 @@ +import qual.Immutable; + +import java.lang.Cloneable; + +@Immutable interface IIIC_ImmutableInterface { +} + +// The use of IIIC_ImmutableInterface defaulted to @Mutable. +// So the implement action is valid for the class (implementing a mutable interface) +// But for the type use of IIIC_ImmutableInterface it is invalid. +// :: error: type.invalid.annotations.on.use +public class InvalidImmutableInterfaceClass implements Cloneable, IIIC_ImmutableInterface { + +} \ No newline at end of file diff --git a/testinput/glacier/InvalidTypeArguments.java b/testinput/glacier/InvalidTypeArguments.java new file mode 100644 index 0000000..ff89d06 --- /dev/null +++ b/testinput/glacier/InvalidTypeArguments.java @@ -0,0 +1,39 @@ +import java.util.Collection; +import java.util.Collections; +import java.util.Iterator; + +import qual.Immutable; +import qual.Mutable; + +public class InvalidTypeArguments { + interface MutableObject {} + @Immutable interface ImmutableObject {} + + class Generic {} + Generic mutableObjectGeneric; + Generic immutableObjectGeneric; + + class BogusImmutableGeneric<@Immutable T>{} + // :: error: (type.argument.type.incompatible) + BogusImmutableGeneric mutableObjectBogusImmutableGeneric; + BogusImmutableGeneric immutableObjectBogusImmutableGeneric; + + + class ImmutableGeneric<@Immutable T extends @Immutable Object>{} + // :: error: (type.argument.type.incompatible) + ImmutableGeneric mutableObjectImmutableGeneric; + ImmutableGeneric immutableObjectImmutableGeneric; + + class MutableGeneric<@Mutable T>{} + MutableGeneric mutableObjectMutableGeneric; + // :: error: (type.argument.type.incompatible) + MutableGeneric immutableObjectMutableGeneric; + + public class UnmodifiableCollection { + public Iterator getCellIterator() { + Collection cellCollection = null; + Collections.unmodifiableCollection(cellCollection); + return Collections.unmodifiableCollection(cellCollection).iterator(); + } + } +} \ No newline at end of file diff --git a/testinput/glacier/MapParameters.java b/testinput/glacier/MapParameters.java new file mode 100644 index 0000000..57272de --- /dev/null +++ b/testinput/glacier/MapParameters.java @@ -0,0 +1,13 @@ +// @skip-test +// handling of null + +import java.util.Map; + +public class MapParameters { + public static void takeMap(Map m) {}; + + public void foo() { + Map m = null; + takeMap(m); + } +} \ No newline at end of file diff --git a/testinput/glacier/MethodInvocation.java b/testinput/glacier/MethodInvocation.java new file mode 100644 index 0000000..bc92c5c --- /dev/null +++ b/testinput/glacier/MethodInvocation.java @@ -0,0 +1,14 @@ +package glacier; + +import qual.*; + +@Immutable interface Interface { + public void doStuff(); +} + +public class MethodInvocation { + public void foo() { + Interface i = null; + i.doStuff(); + } +} \ No newline at end of file diff --git a/testinput/glacier/MethodTypeParameters.java b/testinput/glacier/MethodTypeParameters.java new file mode 100644 index 0000000..d9cf90a --- /dev/null +++ b/testinput/glacier/MethodTypeParameters.java @@ -0,0 +1,7 @@ +import qual.Immutable; + +class TypeParameters { + static TypeParameters asImmutableList(@Immutable Object[] elements) { + return null; + } +} \ No newline at end of file diff --git a/testinput/glacier/MutableClassCantHaveImmutableSubclass.java b/testinput/glacier/MutableClassCantHaveImmutableSubclass.java new file mode 100644 index 0000000..3127882 --- /dev/null +++ b/testinput/glacier/MutableClassCantHaveImmutableSubclass.java @@ -0,0 +1,41 @@ +// @skip-test +// + +import qual.Immutable; + + +class Mut { + int y = 3; +} + +// ::error: (glacier.nonfinalmember) +@Immutable class Immut extends Mut { } + +class SafeAbstractSuperclass { + final int x = 3; + final String y = "Hello"; + final Immut i = null; +} + +@Immutable class Immut2 extends SafeAbstractSuperclass { }; + +class UnsafeAbstractSuperclass { + final int x= 3; + String y = "Hello"; // Oops, not final + final Immut i = null; +} + +// ::error: (glacier.nonfinalmember) +@Immutable class Immut3 extends UnsafeAbstractSuperclass { }; + + + + +class UnsafeAbstractSuperclass2 { + final int x = 3; + java.util.Date y = null; + final Immut i = null; +} + +// ::error: (glacier.nonfinalmember) ::error: (glacier.mutablemember) +@Immutable class Immut4 extends UnsafeAbstractSuperclass2 { }; \ No newline at end of file diff --git a/testinput/glacier/NestedGenerics.java b/testinput/glacier/NestedGenerics.java new file mode 100644 index 0000000..644d95d --- /dev/null +++ b/testinput/glacier/NestedGenerics.java @@ -0,0 +1,21 @@ +import java.util.Iterator; + + +interface Predicate { + boolean apply(T var1); + + boolean equals(Object var1); +} + +final class Iterators { + + public static boolean any(Iterator iterator, Predicate predicate) { + //return indexOf(iterator, predicate) != -1; + return true; + } + + public static boolean contains(Iterator iterator, Object element) { + return any(iterator, null); + } + +} \ No newline at end of file diff --git a/testinput/glacier/NullAssignment.java b/testinput/glacier/NullAssignment.java new file mode 100644 index 0000000..c799e9e --- /dev/null +++ b/testinput/glacier/NullAssignment.java @@ -0,0 +1,14 @@ +import qual.Immutable; + +interface NA_AnInterface {}; + + +public class NullAssignment { + public void takeObj(Object o) { + } + + public void foo() { + NA_AnInterface i = null; + takeObj(i); + } +} \ No newline at end of file diff --git a/testinput/glacier/Override.java b/testinput/glacier/Override.java new file mode 100644 index 0000000..6328b5a --- /dev/null +++ b/testinput/glacier/Override.java @@ -0,0 +1,18 @@ +package glacier; + +import qual.Immutable; + + +@Immutable abstract class Superclass { + public abstract void doStuff(int x); +} + +public @Immutable class Override { + + + public Override() { + } + + public void doStuff(int x) { + } +} \ No newline at end of file diff --git a/testinput/glacier/PlainObjects.java b/testinput/glacier/PlainObjects.java new file mode 100644 index 0000000..9b1b1db --- /dev/null +++ b/testinput/glacier/PlainObjects.java @@ -0,0 +1,19 @@ +// @skip-test +// PICO CAN handle null to @Bottom + +import qual.*; + +class PlainObjects { + + public void takeObject(Object o) {}; + public void takeImmutableObject(@Immutable Object o) {}; + + void foo () { + Object o1 = null; + @Immutable Object o2 = null; + + takeObject(o2); + + takeImmutableObject(o1); + } +} \ No newline at end of file diff --git a/testinput/glacier/ReadOnlyClass.java b/testinput/glacier/ReadOnlyClass.java new file mode 100644 index 0000000..8738c71 --- /dev/null +++ b/testinput/glacier/ReadOnlyClass.java @@ -0,0 +1,27 @@ +// @skip-test + +import qual.*; +import java.lang.String; + +// Classes can't be annotated ReadOnly in their declarations; @Readonly is only for method parameters. +// ::error: (glacier.readonly.class) +@Readonly public class ReadOnlyClass { +} + +class ReadOnlyMethodClass { + @Readonly ReadOnlyClass roc; + + int @Readonly [] readOnlyIntArray; + + // ::error: (type.invalid.annotations.on.use) + void takeReadOnlyString(@Readonly String foo) {} + void takeReadOnlyArray(String @Readonly [] foo) { + // ::error: (glacier.assignment.array) + foo[0] = "Hello, world!"; + } + + void takeImmutableArray(String @Immutable [] foo) { + // ::error: (glacier.assignment.array) + foo[0] = "Hello, world!"; + } +} \ No newline at end of file diff --git a/testinput/glacier/ReadOnlyObject.java b/testinput/glacier/ReadOnlyObject.java new file mode 100644 index 0000000..bc4c6f3 --- /dev/null +++ b/testinput/glacier/ReadOnlyObject.java @@ -0,0 +1,13 @@ +// Reason of Change: defaulting difference. + +import qual.*; + +public class ReadOnlyObject { + // PICO defaults the return type of RDM class to mutable. + // But String.valueOf(1) returns immutable. + public @Immutable Object foo() { + Object cat = null; + return true ? String.valueOf(1) : cat; + } + +} \ No newline at end of file diff --git a/testinput/glacier/ResultWrapTest.java b/testinput/glacier/ResultWrapTest.java new file mode 100644 index 0000000..08809e7 --- /dev/null +++ b/testinput/glacier/ResultWrapTest.java @@ -0,0 +1,16 @@ +// @skip-test +// Guess: in glacter @MaybeMutable is super type of @Immutable. So the use of typevar is allowed. + +import qual.Mutable; + +public class ResultWrapTest { + + ResultWrapTest() { + // while visiting this, the return type must be annotated correctly? + } + + static class ResultWrap { + } + + final ResultWrap input = null; +} \ No newline at end of file diff --git a/testinput/glacier/StaticAssignment.java b/testinput/glacier/StaticAssignment.java new file mode 100644 index 0000000..a7bbb8c --- /dev/null +++ b/testinput/glacier/StaticAssignment.java @@ -0,0 +1,22 @@ +// @skip-test +// !!!!!!!!!!!!!!! +// GLOBAL is writable in static. +// removing static will raise error. + +import qual.Immutable; + +@Immutable +public class StaticAssignment { + public static int GLOBAL = 1; // OK, no error here + + StaticAssignment() { + // ::error: (glacier.assignment) + GLOBAL = 2; + } + + public void setStatics () { + // ::error: (glacier.assignment) + GLOBAL = 42; + } + +} \ No newline at end of file diff --git a/testinput/glacier/StringTest.java b/testinput/glacier/StringTest.java new file mode 100644 index 0000000..6f478f3 --- /dev/null +++ b/testinput/glacier/StringTest.java @@ -0,0 +1,10 @@ +package glacier; + +import qual.Immutable; + +import java.lang.String; + +@SuppressWarnings("initialization") +public @Immutable class StringTest { + String s; // no error expected here because String should be treated as if it were declared @Immutable. +} \ No newline at end of file diff --git a/testinput/glacier/Subclassing.java b/testinput/glacier/Subclassing.java new file mode 100644 index 0000000..766b020 --- /dev/null +++ b/testinput/glacier/Subclassing.java @@ -0,0 +1,18 @@ +// @skip-test +// Drop this one: PICO cannot derive from mutable classes + +import qual.Immutable; + +public class Subclassing { }; + +class MutSubclass extends Subclassing { }; + +// OK with the recent change: immutable classes can derive from mutable classes +@Immutable class InvalidImmutSubclass extends Subclassing { }; + + +@Immutable class ImmutableSuper { }; +@Immutable class ImmutableSub extends ImmutableSuper { }; + +// ::error: (glacier.subclass.mutable) +class InvalidMutableSub extends ImmutableSuper { }; \ No newline at end of file diff --git a/testinput/glacier/Transitivity.java b/testinput/glacier/Transitivity.java new file mode 100644 index 0000000..66f2138 --- /dev/null +++ b/testinput/glacier/Transitivity.java @@ -0,0 +1,20 @@ +package glacier; + +import qual.Immutable; + +@SuppressWarnings("initialization") +@Immutable class Inner { + int x; +} + +@SuppressWarnings("initialization") +public @Immutable class Transitivity { + Inner i; + + public Transitivity() { + } + + public void test() { + + } +} \ No newline at end of file diff --git a/testinput/glacier/TransitivityError.java b/testinput/glacier/TransitivityError.java new file mode 100644 index 0000000..75876f2 --- /dev/null +++ b/testinput/glacier/TransitivityError.java @@ -0,0 +1,23 @@ +// @skip-test +// until the defaulting of fields in immutable class is resolved + +package glacier; + +import qual.Immutable; + + +class TE_Inner { + int x; +} + +public @Immutable class TransitivityError { + // :: error: glacier.mutable.invalid + TE_Inner i; + + public TransitivityError() { + } + + public void test() { + + } +} \ No newline at end of file diff --git a/testinput/glacier/TypeParameter.java b/testinput/glacier/TypeParameter.java new file mode 100644 index 0000000..a78fdab --- /dev/null +++ b/testinput/glacier/TypeParameter.java @@ -0,0 +1,32 @@ +// @skip-test +// review glacier's type parameter rule when free + +import qual.Immutable; + +class E { } // Note same name as type parameter, but mutable class. + +@Immutable class TP_Superclass { + private E aField; // Should be OK because we'll make sure E is instantiated with an immutable type. This E is the type parameter, not the class above. + void aMethod() { + + } +} + +@Immutable public class TypeParameter extends TP_Superclass { + private TP_Superclass s; + private TP_Superclass t; + + // ::error: (glacier.typeparameter.mutable) + private TP_Superclass u; + + void aMethod() { + + } +} + +@Immutable class Test { + TypeParameter t1 = null; // OK + + // ::error: (glacier.typeparameter.mutable) + TypeParameter t2 = null; +} \ No newline at end of file diff --git a/testinput/glacier/Unboxing.java b/testinput/glacier/Unboxing.java new file mode 100644 index 0000000..c614fbd --- /dev/null +++ b/testinput/glacier/Unboxing.java @@ -0,0 +1,8 @@ +public class Unboxing { + public void takeNumber(Number n) {}; + + public void passDouble() { + takeNumber(42.0); + } + +}; \ No newline at end of file diff --git a/testinput/glacier/UnmodifiableCollection.java b/testinput/glacier/UnmodifiableCollection.java new file mode 100644 index 0000000..02138b7 --- /dev/null +++ b/testinput/glacier/UnmodifiableCollection.java @@ -0,0 +1,14 @@ +import java.util.Collection; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; + + +interface SCell {}; + +public class UnmodifiableCollection { + public Iterator getCellIterator() { + Collection cellCollection = null; + return Collections.unmodifiableCollection(cellCollection).iterator(); + } +} \ No newline at end of file diff --git a/testinput/glacier/UnmodifiableList.java b/testinput/glacier/UnmodifiableList.java new file mode 100644 index 0000000..578ef86 --- /dev/null +++ b/testinput/glacier/UnmodifiableList.java @@ -0,0 +1,24 @@ +package glacier; + +import java.util.List; + + +class ListProcessor { + static List process(List c) { + // ::warning: [unchecked] unchecked cast + return (List)c; + } +} + +class AClass {} + +public class UnmodifiableList { + private List _list; + + public void foo() { + + //List a = true ? _list : null; + List l = true ? ListProcessor.process(_list) : _list; + //List l = ListProcessor.process(_list); + } +} \ No newline at end of file diff --git a/testinput/glacier/ValidImmutableInterfaceClass.java b/testinput/glacier/ValidImmutableInterfaceClass.java new file mode 100644 index 0000000..784f443 --- /dev/null +++ b/testinput/glacier/ValidImmutableInterfaceClass.java @@ -0,0 +1,11 @@ +package glacier; + +import qual.Immutable; + + +@Immutable interface VIIC_ImmutableInterface { +} + +public @Immutable class ValidImmutableInterfaceClass implements VIIC_ImmutableInterface { + +} \ No newline at end of file From 64122f65c6ed7c99d3a118e713962f7f820132c1 Mon Sep 17 00:00:00 2001 From: lnsun <57457122+lnsun@users.noreply.github.com> Date: Thu, 25 Mar 2021 14:37:05 -0400 Subject: [PATCH 135/144] temp ignore all systems test --- src/test/java/pico/ImmutabilityTypecheckBaseAllSystemsTest.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/test/java/pico/ImmutabilityTypecheckBaseAllSystemsTest.java b/src/test/java/pico/ImmutabilityTypecheckBaseAllSystemsTest.java index 10cf546..4ad7223 100644 --- a/src/test/java/pico/ImmutabilityTypecheckBaseAllSystemsTest.java +++ b/src/test/java/pico/ImmutabilityTypecheckBaseAllSystemsTest.java @@ -2,6 +2,7 @@ import org.checkerframework.framework.test.CheckerFrameworkPerFileTest; import org.checkerframework.framework.test.TestUtilities; +import org.junit.Ignore; import org.junit.runners.Parameterized.Parameters; import pico.typecheck.PICOChecker; @@ -9,6 +10,7 @@ import java.util.ArrayList; import java.util.List; +@Ignore public class ImmutabilityTypecheckBaseAllSystemsTest extends CheckerFrameworkPerFileTest { public ImmutabilityTypecheckBaseAllSystemsTest(File testFile) { super(testFile, PICOChecker.class, "", "-Anomsgtext", From c96f3fa86e26e290a2bacc2f1e3f9c1c55d8e6f8 Mon Sep 17 00:00:00 2001 From: lnsun <57457122+lnsun@users.noreply.github.com> Date: Thu, 25 Mar 2021 14:38:11 -0400 Subject: [PATCH 136/144] stub path problem --- infer.sh | 3 +++ run-dljc.sh | 5 +++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/infer.sh b/infer.sh index 695c80b..20d4edb 100755 --- a/infer.sh +++ b/infer.sh @@ -19,6 +19,8 @@ CHECKER=pico.inference.PICOInferenceChecker SOLVER=pico.inference.solver.PICOSolverEngine +STUBS="src/main/java/pico/inference/jdk.astub" + declare -a ARGS for i in "$@" ; do if [[ $i == "-ds" ]] ; then @@ -38,4 +40,5 @@ IS_HACK=true # Start the inference $CFI/scripts/inference-dev -m ROUNDTRIP --checker "$CHECKER" --solver "$SOLVER" \ --solverArgs="useGraph=false,collectStatistic=true" --hacks="$IS_HACK" \ + --cfArgs="-Astubs=$STUBS" \ -afud ./annotated "${ARGS[@]}" diff --git a/run-dljc.sh b/run-dljc.sh index 87238a7..64b9009 100755 --- a/run-dljc.sh +++ b/run-dljc.sh @@ -9,7 +9,7 @@ export AFU="$JSR308"/annotation-tools/annotation-file-utilities export PATH="$PATH":"$AFU"/scripts export CFI="$JSR308"/checker-framework-inference -export CLASSPATH="$JSR308"/immutability/build/classes/main:$CHECKERFRAMEWORK/dataflow/build:$CHECKERFRAMEWORK/javacutil/build:$CHECKERFRAMEWORK/stubparser/build:$CHECKERFRAMEWORK/framework/build:$CHECKERFRAMEWORK/checker/build:$SOLVER/bin:$CHECKERFRAMEWORK/framework/tests/junit-4.12.jar:$CHECKERFRAMEWORK/framework/tests/hamcrest-core-1.3.jar:$CFI/dist/checker-framework-inference.jar:$CFI/dist/org.ow2.sat4j.core-2.3.4.jar:$CFI/dist/commons-logging-1.2.jar:$CFI/dist/log4j-1.2.16.jar:$JSR308/jsr308-langtools/build/classes:$CLASSPATH +export CLASSPATH="$JSR308"/immutability/build/classes/java/main:$CHECKERFRAMEWORK/dataflow/build:$CHECKERFRAMEWORK/javacutil/build:$CHECKERFRAMEWORK/stubparser/build:$CHECKERFRAMEWORK/framework/build:$CHECKERFRAMEWORK/checker/build:$SOLVER/bin:$CHECKERFRAMEWORK/framework/tests/junit-4.12.jar:$CHECKERFRAMEWORK/framework/tests/hamcrest-core-1.3.jar:$CFI/dist/checker-framework-inference.jar:$CFI/dist/org.ow2.sat4j.core-2.3.4.jar:$CFI/dist/commons-logging-1.2.jar:$CFI/dist/log4j-1.2.16.jar:$JSR308/jsr308-langtools/build/classes:$CLASSPATH #parsing build command of the target program build_cmd="$2" @@ -30,8 +30,9 @@ elif [[ "$1" = "-i" ]] ; then rm -rf logs annotated echo "Cleaning Done." CHECKER="pico.inference.PICOInferenceChecker" +# SOLVER="checkers.inference.solver.DebugSolver" SOLVER="pico.inference.solver.PICOSolverEngine" - running_cmd="python $DLJC/dljc -t inference --checker "${CHECKER}" --cfArgs=\"-AoptimalSolution\" --solver "${SOLVER}" --solverArgs=\"collectStatistic=true,useGraph=false\" --mode ROUNDTRIP -afud $WORKING_DIR/annotated -o logs -- $build_cmd " + running_cmd="python $DLJC/dljc -t inference --checker "${CHECKER}" --cfArgs=\"-AoptimalSolution -Astubs=/home/l82sun/workspace/opprop/immutability/src/main/java/pico/inference/jdk.astub\" --solver "${SOLVER}" --solverArgs=\"collectStatistic=true,useGraph=false\" --guess --stubs="/home/l82sun/workspace/opprop/immutability/src/main/java/pico/inference/jdk.astub" --mode ROUNDTRIP -afud $WORKING_DIR/annotated -o logs -- $build_cmd " else echo "Unknown tool: should be either -t|-i but found: ${1}" exit 1 From 34e9a742e9b6469c72014e8ec6f6edd5b7276332 Mon Sep 17 00:00:00 2001 From: lnsun <57457122+lnsun@users.noreply.github.com> Date: Fri, 9 Apr 2021 11:09:20 -0400 Subject: [PATCH 137/144] +useOptimisticUncheckedDefaults --- .../pico/inference/PICOInferenceChecker.java | 2 +- .../PICOInferenceRealTypeFactory.java | 28 +++++++++++++++++++ 2 files changed, 29 insertions(+), 1 deletion(-) diff --git a/src/main/java/pico/inference/PICOInferenceChecker.java b/src/main/java/pico/inference/PICOInferenceChecker.java index d211fab..e826bc9 100644 --- a/src/main/java/pico/inference/PICOInferenceChecker.java +++ b/src/main/java/pico/inference/PICOInferenceChecker.java @@ -14,7 +14,7 @@ /** * Main entry class */ -@SupportedOptions({"upcast", "anycast", "comparablecast", "optimalSolution"}) +@SupportedOptions({"upcast", "anycast", "comparablecast", "optimalSolution", "useOptimisticUncheckedDefaults"}) public class PICOInferenceChecker extends BaseInferrableChecker { @Override diff --git a/src/main/java/pico/inference/PICOInferenceRealTypeFactory.java b/src/main/java/pico/inference/PICOInferenceRealTypeFactory.java index 1f173cf..c0644ca 100644 --- a/src/main/java/pico/inference/PICOInferenceRealTypeFactory.java +++ b/src/main/java/pico/inference/PICOInferenceRealTypeFactory.java @@ -14,17 +14,21 @@ import javax.lang.model.type.DeclaredType; import javax.lang.model.type.TypeKind; import javax.lang.model.type.TypeMirror; +import javax.lang.model.util.Elements; import com.sun.tools.javac.tree.JCTree; import org.checkerframework.common.basetype.BaseAnnotatedTypeFactory; import org.checkerframework.common.basetype.BaseTypeChecker; import org.checkerframework.framework.qual.RelevantJavaTypes; +import org.checkerframework.framework.qual.TypeUseLocation; import org.checkerframework.framework.type.AbstractViewpointAdapter; import org.checkerframework.framework.type.AnnotatedTypeMirror; import org.checkerframework.framework.type.treeannotator.ListTreeAnnotator; import org.checkerframework.framework.type.treeannotator.LiteralTreeAnnotator; import org.checkerframework.framework.type.treeannotator.TreeAnnotator; import org.checkerframework.framework.type.typeannotator.*; +import org.checkerframework.framework.util.defaults.QualifierDefaults; +import org.checkerframework.javacutil.AnnotationBuilder; import org.checkerframework.javacutil.AnnotationUtils; import org.checkerframework.javacutil.BugInCF; import org.checkerframework.javacutil.TreeUtils; @@ -65,6 +69,7 @@ public PICOInferenceRealTypeFactory(BaseTypeChecker checker, boolean useFlow) { addAliasedAnnotation(org.jmlspecs.annotation.Readonly.class, READONLY); } // IMMUTABLE_ALIASES.forEach(anno -> addAliasedAnnotation(anno, IMMUTABLE)); + postInit(); } @@ -123,6 +128,29 @@ protected TypeAnnotator createTypeAnnotator() { return new ListTypeAnnotator(typeAnnotators); } + @Override + protected QualifierDefaults createQualifierDefaults() { + QualifierDefaults defaults = super.createQualifierDefaults(); + Elements elements = checker.getElementUtils(); + + // The optimistic flag only change the rules of unchecked defaults. + // To enable optimistic default, the user should both enable conservative for bytecode ONLY, + // and pass the optimistic flag to override the default of the conservative. + // i.e. -AuseConservativeDefaultsForUncheckedCode=bytecode (or -AuseDefaultsForUncheckedCode=bytecode) + // -AuseOptimisticUncheckedDefaults + if (checker.hasOption("useOptimisticUncheckedDefaults")) { + defaults.addUncheckedCodeDefaults(AnnotationBuilder.fromClass(elements, Bottom.class), new TypeUseLocation[]{ + TypeUseLocation.LOWER_BOUND, TypeUseLocation.RETURN, TypeUseLocation.FIELD + }); + defaults.addUncheckedCodeDefaults(AnnotationBuilder.fromClass(elements, Readonly.class), new TypeUseLocation[]{ + TypeUseLocation.UPPER_BOUND, TypeUseLocation.PARAMETER + }); + } + + return defaults; + + } + /* TODO If the dataflow refines the type as bottom, should we allow such a refinement? If we allow it, PICOValidator will give an error if it begins to enforce @Bottom is not used*/ /* @Override From 2cd3276e10fa8a577b301f0c95db365c063bd7b2 Mon Sep 17 00:00:00 2001 From: lnsun <57457122+lnsun@users.noreply.github.com> Date: Tue, 20 Apr 2021 14:24:44 -0400 Subject: [PATCH 138/144] import issue --- src/main/java/qual/Bottom.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/main/java/qual/Bottom.java b/src/main/java/qual/Bottom.java index 9fbbdf7..989ce21 100644 --- a/src/main/java/qual/Bottom.java +++ b/src/main/java/qual/Bottom.java @@ -1,6 +1,9 @@ package qual; -import org.checkerframework.framework.qual.*; +import org.checkerframework.framework.qual.DefaultFor; +import org.checkerframework.framework.qual.SubtypeOf; +import org.checkerframework.framework.qual.TargetLocations; +import org.checkerframework.framework.qual.TypeUseLocation; import java.lang.annotation.Documented; import java.lang.annotation.Retention; From 472e75c550767df54e3fac6ddcb4f8c8fcdce32c Mon Sep 17 00:00:00 2001 From: lnsun <57457122+lnsun@users.noreply.github.com> Date: Tue, 20 Apr 2021 14:27:01 -0400 Subject: [PATCH 139/144] import issue --- src/main/java/qual/Bottom.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/java/qual/Bottom.java b/src/main/java/qual/Bottom.java index 989ce21..e6f41e3 100644 --- a/src/main/java/qual/Bottom.java +++ b/src/main/java/qual/Bottom.java @@ -3,6 +3,7 @@ import org.checkerframework.framework.qual.DefaultFor; import org.checkerframework.framework.qual.SubtypeOf; import org.checkerframework.framework.qual.TargetLocations; +import org.checkerframework.framework.qual.TypeKind; import org.checkerframework.framework.qual.TypeUseLocation; import java.lang.annotation.Documented; From 6e4e3c67ffb66c7ab278d932fdea1e43751775dc Mon Sep 17 00:00:00 2001 From: Haifeng Shi Date: Tue, 8 Feb 2022 21:10:39 -0500 Subject: [PATCH 140/144] update pico, fix CF api rename, update gradle version --- build.gradle | 17 +++---- gradle/wrapper/gradle-wrapper.properties | 2 +- src/main/java/pico/common/PICOTypeUtil.java | 21 ++++---- .../PICOInferenceAnnotatedTypeFactory.java | 5 +- .../PICOInferenceRealTypeFactory.java | 5 +- .../inference/PICOInferenceValidator.java | 9 ++-- .../pico/inference/PICOInferenceVisitor.java | 51 +++++++++---------- .../solver/PICOCombineConstraintEncoder.java | 9 ++-- .../typecheck/PICOAnnotatedTypeFactory.java | 9 ++-- .../java/pico/typecheck/PICOValidator.java | 11 ++-- src/main/java/pico/typecheck/PICOVisitor.java | 2 +- 11 files changed, 72 insertions(+), 69 deletions(-) diff --git a/build.gradle b/build.gradle index 714c4b5..a7e2f56 100644 --- a/build.gradle +++ b/build.gradle @@ -9,7 +9,7 @@ plugins { apply plugin: 'java' ext { - assert JavaVersion.current() == JavaVersion.VERSION_1_8: "Set JAVA_HOME to JDK 8. Current version is ${JavaVersion.current()}" + assert JavaVersion.current() == JavaVersion.VERSION_11: "Set JAVA_HOME to JDK 11. Current version is ${JavaVersion.current()}" jsr308 = file(new File("..")).absolutePath cfPath = "${jsr308}/checker-framework" cfiPath = "${jsr308}/checker-framework-inference" @@ -34,17 +34,16 @@ repositories { } dependencies { - compile fileTree(dir: "${cfPath}/checker/dist", include: "checker.jar") - compile fileTree(dir: "${cfiPath}/dist", include: "checker-framework-inference.jar") + implementation fileTree(dir: "${cfPath}/checker/dist", include: "checker.jar") + implementation fileTree(dir: "${cfiPath}/dist", include: "checker-framework-inference.jar") // sat4j solver dependency - compile 'org.ow2.sat4j:org.ow2.sat4j.core:2.3.4' - compile 'org.ow2.sat4j:org.ow2.sat4j.maxsat:2.3.4' + implementation 'org.ow2.sat4j:org.ow2.sat4j.core:2.3.5' + implementation 'org.ow2.sat4j:org.ow2.sat4j.maxsat:2.3.5' // The production code uses the SLF4J logging API at compile time - compile 'org.slf4j:slf4j-api:1.7.13' - + implementation 'org.slf4j:slf4j-api:1.7.29' // CF test lib dependency - testCompile fileTree(dir: "${cfPath}/framework-test/build/libs", include: "framework-test-*.jar") - testCompile 'junit:junit:4.12' + testImplementation fileTree(dir: "${cfPath}/framework-test/build/libs", include: "framework-test-*.jar") + testImplementation 'junit:junit:4.12' } sourceSets { diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index be280be..6b7fd26 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -2,4 +2,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-4.5-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.3-bin.zip diff --git a/src/main/java/pico/common/PICOTypeUtil.java b/src/main/java/pico/common/PICOTypeUtil.java index 97f5a69..ddbc296 100644 --- a/src/main/java/pico/common/PICOTypeUtil.java +++ b/src/main/java/pico/common/PICOTypeUtil.java @@ -27,6 +27,7 @@ import org.checkerframework.javacutil.AnnotationUtils; import org.checkerframework.javacutil.ElementUtils; import org.checkerframework.javacutil.TreeUtils; +import org.checkerframework.javacutil.TreePathUtil; import org.checkerframework.javacutil.TypesUtils; import pico.typecheck.PICOVisitor; import qual.Assignable; @@ -119,12 +120,12 @@ public static AnnotatedDeclaredType getBoundTypeOfEnclosingTypeDeclaration(Tree if (node instanceof MethodTree) { MethodTree methodTree = (MethodTree) node; ExecutableElement element = TreeUtils.elementFromDeclaration(methodTree); - typeElement = ElementUtils.enclosingClass(element); + typeElement = ElementUtils.enclosingTypeElement(element); } else if(node instanceof VariableTree) { VariableTree variableTree = (VariableTree) node; VariableElement variableElement = TreeUtils.elementFromDeclaration(variableTree); assert variableElement!= null && variableElement.getKind().isField(); - typeElement = ElementUtils.enclosingClass(variableElement); + typeElement = ElementUtils.enclosingTypeElement(variableElement); } if (typeElement != null) { @@ -135,7 +136,7 @@ public static AnnotatedDeclaredType getBoundTypeOfEnclosingTypeDeclaration(Tree } public static AnnotatedDeclaredType getBoundTypeOfEnclosingTypeDeclaration(Element element, AnnotatedTypeFactory atypeFactory) { - TypeElement typeElement = ElementUtils.enclosingClass(element); + TypeElement typeElement = ElementUtils.enclosingTypeElement(element); if (typeElement != null) { return getBoundTypeOfTypeDeclaration(typeElement, atypeFactory); } @@ -255,7 +256,7 @@ public static void addDefaultForField(AnnotatedTypeFactory annotatedTypeFactory, // add RDM if bound=M and enclosingBound=M/RDM Set enclosingBound = annotatedTypeFactory.getTypeDeclarationBounds( - Objects.requireNonNull(ElementUtils.enclosingClass(element)).asType()); + Objects.requireNonNull(ElementUtils.enclosingTypeElement(element)).asType()); Set declBound = annotatedTypeFactory.getTypeDeclarationBounds(element.asType()); if (AnnotationUtils.containsSameByName(declBound, MUTABLE)) { if (AnnotationUtils.containsSameByName(enclosingBound, RECEIVER_DEPENDANT_MUTABLE) || @@ -311,7 +312,7 @@ public static void applyConstant(AnnotatedTypeMirror type, AnnotationMirror am) // Might be null. It's normal. In typechecking side, we use addMissingAnnotations(). Only if // there is existing annotation in code, then here is non-null. Otherwise, VariableAnnotator // hasn't come into the picture yet, so no VarAnnot exists here, which is normal. - Slot shouldBeAppliedTo = slotManager.getVariableSlot(type); + Slot shouldBeAppliedTo = slotManager.getSlot(type); ConstantSlot constant = slotManager.createConstantSlot(am); if (shouldBeAppliedTo == null) { // Here, we are adding VarAnnot that represents @Immutable. There won't be solution for this ConstantSlot for this type, @@ -380,7 +381,7 @@ public static boolean isAssigningAssignableField(ExpressionTree node, Annotation public static boolean isEnclosedByAnonymousClass(Tree tree, AnnotatedTypeFactory atypeFactory) { TreePath path = atypeFactory.getPath(tree); if (path != null) { - ClassTree classTree = TreeUtils.enclosingClass(path); + ClassTree classTree = TreePathUtil.enclosingClass(path); return classTree != null && InferenceUtil.isAnonymousClass(classTree); } return false; @@ -392,7 +393,7 @@ public static AnnotatedDeclaredType getBoundOfEnclosingAnonymousClass(Tree tree, return null; } AnnotatedDeclaredType enclosingType = null; - Tree newclassTree = TreeUtils.enclosingOfKind(path, Tree.Kind.NEW_CLASS); + Tree newclassTree = TreePathUtil.enclosingOfKind(path, Tree.Kind.NEW_CLASS); if (newclassTree != null) { enclosingType = (AnnotatedDeclaredType) atypeFactory.getAnnotatedType(newclassTree); } @@ -407,10 +408,10 @@ public static AnnotationMirror createEquivalentVarAnnotOfRealQualifier(final Ann public static boolean inStaticScope(TreePath treePath) { boolean in = false; - if (TreeUtils.isTreeInStaticScope(treePath)) { + if (TreePathUtil.isTreeInStaticScope(treePath)) { in = true; // Exclude case in which enclosing class is static - ClassTree classTree = TreeUtils.enclosingClass(treePath); + ClassTree classTree = TreePathUtil.enclosingClass(treePath); if (classTree != null && classTree.getModifiers().getFlags().contains((Modifier.STATIC))) { in = false; } @@ -454,7 +455,7 @@ public static boolean isArrayType(AnnotatedDeclaredType type, AnnotatedTypeFacto // If it is a user-declared "Array" class without package, a class / source file should be there. // Otherwise, it is the java inner type. return ele instanceof Symbol.ClassSymbol - && ElementUtils.getVerboseName(ele).equals("Array") + && ElementUtils.getQualifiedName(ele).equals("Array") && ((Symbol.ClassSymbol) ele).classfile == null && ((Symbol.ClassSymbol) ele).sourcefile == null; } diff --git a/src/main/java/pico/inference/PICOInferenceAnnotatedTypeFactory.java b/src/main/java/pico/inference/PICOInferenceAnnotatedTypeFactory.java index a0b0377..8106066 100644 --- a/src/main/java/pico/inference/PICOInferenceAnnotatedTypeFactory.java +++ b/src/main/java/pico/inference/PICOInferenceAnnotatedTypeFactory.java @@ -31,6 +31,7 @@ import org.checkerframework.javacutil.AnnotationUtils; import org.checkerframework.javacutil.Pair; import org.checkerframework.javacutil.TreeUtils; +import org.checkerframework.javacutil.TreePathUtil; import pico.common.ExtendedViewpointAdapter; import pico.common.ViewpointAdapterGettable; import pico.typecheck.PICOAnnotatedTypeFactory.PICODefaultForTypeAnnotator; @@ -114,7 +115,7 @@ VariableAnnotator getVariableAnnotator() { */ public AnnotatedDeclaredType getSelfType(Tree tree) { TreePath path = getPath(tree); - ClassTree enclosingClass = TreeUtils.enclosingClass(path); + ClassTree enclosingClass = TreePathUtil.enclosingClass(path); if (enclosingClass == null) { // I hope this only happens when tree is a fake tree that // we created, e.g. when desugaring enhanced-for-loops. @@ -123,7 +124,7 @@ public AnnotatedDeclaredType getSelfType(Tree tree) { // "type" is right now VarAnnot inserted to the bound of "enclosingClass" AnnotatedDeclaredType type = getAnnotatedType(enclosingClass); - MethodTree enclosingMethod = TreeUtils.enclosingMethod(path); + MethodTree enclosingMethod = TreePathUtil.enclosingMethod(path); if (enclosingClass.getSimpleName().length() != 0 && enclosingMethod != null) { AnnotatedTypeMirror.AnnotatedDeclaredType methodReceiver; if (TreeUtils.isConstructor(enclosingMethod)) { diff --git a/src/main/java/pico/inference/PICOInferenceRealTypeFactory.java b/src/main/java/pico/inference/PICOInferenceRealTypeFactory.java index c0644ca..4d41241 100644 --- a/src/main/java/pico/inference/PICOInferenceRealTypeFactory.java +++ b/src/main/java/pico/inference/PICOInferenceRealTypeFactory.java @@ -32,6 +32,7 @@ import org.checkerframework.javacutil.AnnotationUtils; import org.checkerframework.javacutil.BugInCF; import org.checkerframework.javacutil.TreeUtils; +import org.checkerframework.javacutil.TreePathUtil; import com.sun.source.tree.Tree; @@ -115,7 +116,7 @@ protected TypeAnnotator createTypeAnnotator() { // annotated. typeAnnotators.add( new IrrelevantTypeAnnotator( - this, getQualifierHierarchy().getTopAnnotations(), classes)); + this, getQualifierHierarchy().getTopAnnotations())); } typeAnnotators.add(new PropagationTypeAnnotator(this)); /*Copied code ends*/ @@ -215,7 +216,7 @@ protected DefaultQualifierForUseTypeAnnotator createDefaultForUseTypeAnnotator() @Override public AnnotatedTypeMirror getTypeOfExtendsImplements(Tree clause) { // add default anno from class main qual, if no qual present - AnnotatedTypeMirror enclosing = getAnnotatedType(TreeUtils.enclosingClass(getPath(clause))); + AnnotatedTypeMirror enclosing = getAnnotatedType(TreePathUtil.enclosingClass(getPath(clause))); // workaround for anonymous class. // TypesUtils::isAnonymous won't work when annotation presents on new class tree! diff --git a/src/main/java/pico/inference/PICOInferenceValidator.java b/src/main/java/pico/inference/PICOInferenceValidator.java index bd282bd..5460a39 100644 --- a/src/main/java/pico/inference/PICOInferenceValidator.java +++ b/src/main/java/pico/inference/PICOInferenceValidator.java @@ -5,7 +5,6 @@ import com.sun.source.tree.Tree; import com.sun.source.tree.VariableTree; import org.checkerframework.common.basetype.BaseTypeChecker; -import org.checkerframework.framework.source.Result; import org.checkerframework.framework.type.AnnotatedTypeFactory; import org.checkerframework.framework.type.AnnotatedTypeMirror; import org.checkerframework.framework.type.AnnotatedTypeMirror.AnnotatedArrayType; @@ -37,7 +36,7 @@ public Void visitDeclared(AnnotatedDeclaredType type, Tree tree) { checkOnlyOneAssignabilityModifierOnField(tree); // AnnotatedDeclaredType defaultType = // (AnnotatedDeclaredType) atypeFactory.getAnnotatedType(type.getUnderlyingType().asElement()); -// // TODO for defaulted super clause: should top anno be checked? (see shouldCheckTopLevelDeclaredType()) +// // TODO for defaulted super clause: should top anno be checked? (see shouldCheckTopLevelDeclaredOrPrimitiveType()) // if (defaultType.getAnnotationInHierarchy(READONLY) == null && !PICOTypeUtil.isEnumOrEnumConstant(defaultType)) { // defaultType = defaultType.deepCopy(); // defaultType.replaceAnnotation(MUTABLE); @@ -64,11 +63,11 @@ public Void visitPrimitive(AnnotatedPrimitiveType type, Tree tree) { } @Override - protected boolean shouldCheckTopLevelDeclaredType(AnnotatedTypeMirror type, Tree tree) { + protected boolean shouldCheckTopLevelDeclaredOrPrimitiveType(AnnotatedTypeMirror type, Tree tree) { if (TreeUtils.isLocalVariable(tree)) { return true; } - return super.shouldCheckTopLevelDeclaredType(type, tree); + return super.shouldCheckTopLevelDeclaredOrPrimitiveType(type, tree); } private void checkStaticReceiverDependantMutableError(AnnotatedTypeMirror type, Tree tree) { @@ -121,7 +120,7 @@ private void checkOnlyOneAssignabilityModifierOnField(Tree tree) { } private void reportFieldMultipleAssignabilityModifiersError(VariableElement field) { - checker.report(Result.failure("one.assignability.invalid", field), field); + checker.reportError(field, "one.assignability.invalid", field); isValid = false; } diff --git a/src/main/java/pico/inference/PICOInferenceVisitor.java b/src/main/java/pico/inference/PICOInferenceVisitor.java index 7bcaf5c..2be5954 100644 --- a/src/main/java/pico/inference/PICOInferenceVisitor.java +++ b/src/main/java/pico/inference/PICOInferenceVisitor.java @@ -27,7 +27,6 @@ import com.sun.source.tree.VariableTree; import com.sun.source.util.TreePath; import org.checkerframework.common.basetype.BaseAnnotatedTypeFactory; -import org.checkerframework.framework.source.Result; import org.checkerframework.framework.type.AnnotatedTypeFactory.ParameterizedExecutableType; import org.checkerframework.framework.type.AnnotatedTypeMirror; import org.checkerframework.framework.type.AnnotatedTypeMirror.AnnotatedDeclaredType; @@ -38,6 +37,7 @@ import org.checkerframework.javacutil.BugInCF; import org.checkerframework.javacutil.ElementUtils; import org.checkerframework.javacutil.TreeUtils; +import org.checkerframework.javacutil.TreePathUtil; import org.checkerframework.javacutil.TypesUtils; import pico.common.ExtendedViewpointAdapter; import pico.common.ViewpointAdapterGettable; @@ -90,12 +90,12 @@ public boolean isValidUse(AnnotatedDeclaredType declarationType, AnnotatedDeclar // allow RDM on mutable fields with enclosing class bounded with mutable if (tree instanceof VariableTree && !useType.isDeclaration()) { VariableElement element = TreeUtils.elementFromDeclaration((VariableTree)tree); - if (element.getKind() == ElementKind.FIELD && ElementUtils.enclosingClass(element) != null) { + if (element.getKind() == ElementKind.FIELD && ElementUtils.enclosingTypeElement(element) != null) { // assert only 1 bound exists AnnotationMirror enclosingBound = extractVarAnnot(PICOTypeUtil.getBoundTypeOfEnclosingTypeDeclaration(element, atypeFactory)); // atypeFactory.getTypeDeclarationBounds( -// Objects.requireNonNull(ElementUtils.enclosingClass(element)).asType()).iterator().next(); +// Objects.requireNonNull(ElementUtils.enclosingTypeElement(element)).asType()).iterator().next(); // if enclosing bound == mutable -> (RDM or Mutable usable on mutable-bounded fields) // else -> adaptedSubtype @@ -162,8 +162,8 @@ private Constraint createAdaptedSubtypeConstraint(AnnotatedTypeMirror lhs, Annot ExtendedViewpointAdapter vpa = ((ViewpointAdapterGettable)atypeFactory).getViewpointAdapter(); AnnotatedTypeMirror adapted = vpa.rawCombineAnnotationWithType(extractVarAnnot(lhs), rhs); return constraintManager.createSubtypeConstraint( - slotManager.getVariableSlot(adapted), - slotManager.getVariableSlot(lhs) + slotManager.getSlot(adapted), + slotManager.getSlot(lhs) ); } @@ -180,8 +180,8 @@ public boolean mainIsSubtype(AnnotatedTypeMirror ty, AnnotationMirror mod) { private void addMutableImmutableRdmIncompatibleConstraints(AnnotatedDeclaredType declarationType, AnnotatedDeclaredType useType) { final ConstraintManager constraintManager = InferenceMain.getInstance().getConstraintManager(); final SlotManager slotManager = InferenceMain.getInstance().getSlotManager(); - Slot declSlot = slotManager.getVariableSlot(declarationType); - Slot useSlot = slotManager.getVariableSlot(useType); + Slot declSlot = slotManager.getSlot(declarationType); + Slot useSlot = slotManager.getSlot(useType); Slot mutable = slotManager.getSlot(MUTABLE); Slot immutable = slotManager.getSlot(IMMUTABLE); Slot rdm = slotManager.getSlot(RECEIVER_DEPENDANT_MUTABLE); @@ -262,7 +262,7 @@ protected void checkConstructorInvocation(AnnotatedDeclaredType invocation, Anno private AnnotationMirror extractVarAnnot(final AnnotatedTypeMirror atm) { if (infer) { final SlotManager slotManager = InferenceMain.getInstance().getSlotManager(); - return slotManager.getAnnotation(slotManager.getVariableSlot(atm)); + return slotManager.getAnnotation(slotManager.getSlot(atm)); } return atm.getAnnotationInHierarchy(READONLY); } @@ -346,10 +346,10 @@ private Constraint createMainIsMutableOrRdmConstraint(AnnotatedTypeMirror mainAt return constraintManager.createImplicationConstraint( Collections.singletonList(constraintManager.createInequalityConstraint( slotManager.getSlot(MUTABLE), - slotManager.getVariableSlot(mainAtm))), + slotManager.getSlot(mainAtm))), constraintManager.createEqualityConstraint( slotManager.getSlot(RECEIVER_DEPENDANT_MUTABLE), - slotManager.getVariableSlot(mainAtm) + slotManager.getSlot(mainAtm) ) ); } @@ -378,10 +378,10 @@ public boolean ifAnnoIsThenMainIsOneOf(AnnotationMirror annotation, AnnotationMi Constraint oneOfConst = constraintManager.createImplicationConstraint( Collections.singletonList(constraintManager.createInequalityConstraint( slotManager.getSlot(oneOf[0]), - slotManager.getVariableSlot(mainAtm))), + slotManager.getSlot(mainAtm))), constraintManager.createEqualityConstraint( slotManager.getSlot(oneOf[1]), - slotManager.getVariableSlot(mainAtm) + slotManager.getSlot(mainAtm) ) ); @@ -417,7 +417,7 @@ public void mainCannotInferTo(AnnotatedTypeMirror atm, AnnotationMirror anno, St if (infer) { SlotManager slotManager = InferenceMain.getInstance().getSlotManager(); // should be constant slot if written explicitly in code - if (!(slotManager.getVariableSlot(atm) instanceof ConstantSlot)) { + if (!(slotManager.getSlot(atm) instanceof ConstantSlot)) { mainIsNot(atm, anno, errorKey, tree); } @@ -456,8 +456,8 @@ public Void visitMethod(MethodTree node, Void p) { if (infer) { ConstraintManager constraintManager = InferenceMain.getInstance().getConstraintManager(); SlotManager slotManager = InferenceMain.getInstance().getSlotManager(); - Slot boundSlot = slotManager.getVariableSlot(bound); - Slot consRetSlot = slotManager.getVariableSlot(constructorReturnType); + Slot boundSlot = slotManager.getSlot(bound); + Slot consRetSlot = slotManager.getSlot(constructorReturnType); Slot rdmSlot = slotManager.getSlot(RECEIVER_DEPENDANT_MUTABLE); Constraint inequalityConstraint = constraintManager.createInequalityConstraint(boundSlot, rdmSlot); Constraint subtypeConstraint = constraintManager.createSubtypeConstraint(consRetSlot, boundSlot); @@ -484,7 +484,7 @@ public Void visitMethod(MethodTree node, Void p) { if (declaredReceiverType != null) { mainIsNot(declaredReceiverType, BOTTOM, "bottom.on.receiver", node); if (!isAdaptedSubtype(declaredReceiverType, bound)){ - checker.report(Result.failure("method.receiver.incompatible", declaredReceiverType), node); + checker.reportError(node, "method.receiver.incompatible", declaredReceiverType); } } } @@ -552,9 +552,8 @@ protected void checkTypecastSafety(TypeCastTree node) { if (!isTypeCastSafe(castType, exprType, node)) { // This is only warning message, so even though enterred this line, it doesn't cause PICOInfer to exit. // Even if that was an error, PICOInfer would also not exit. - checker.report( - Result.warning("cast.unsafe", exprType.toString(true), castType.toString(true)), - node); + checker.reportWarning(node, + "cast.unsafe", exprType.toString(true), castType.toString(true)); } } @@ -624,8 +623,8 @@ private boolean isCompatibleCastInInfer(AnnotatedTypeMirror castType, AnnotatedT // Default strategy - comparablecast final QualifierHierarchy qualHierarchy = InferenceMain.getInstance().getRealTypeFactory().getQualifierHierarchy(); final SlotManager slotManager = InferenceMain.getInstance().getSlotManager(); - final Slot castSlot = slotManager.getVariableSlot(castType); - final Slot exprSlot = slotManager.getVariableSlot(exprType); + final Slot castSlot = slotManager.getSlot(castType); + final Slot exprSlot = slotManager.getSlot(exprType); if (castSlot instanceof ConstantSlot && exprSlot instanceof ConstantSlot) { ConstantSlot castCSSlot = (ConstantSlot) castSlot; @@ -692,8 +691,8 @@ private void checkAssignableField(ExpressionTree node, ExpressionTree variable, // Break the combination of readonly receiver + rdm assignable field ConstraintManager constraintManager = InferenceMain.getInstance().getConstraintManager(); SlotManager slotManager = InferenceMain.getInstance().getSlotManager(); - Slot receiverSlot = slotManager.getVariableSlot(receiverType); - Slot fieldSlot = slotManager.getVariableSlot(fieldType); + Slot receiverSlot = slotManager.getSlot(receiverType); + Slot fieldSlot = slotManager.getSlot(fieldType); Slot readonly = slotManager.getSlot(READONLY); Slot receiver_dependant_mutable = slotManager.getSlot(RECEIVER_DEPENDANT_MUTABLE); Constraint receiverReadOnly = constraintManager.createEqualityConstraint(receiverSlot, readonly); @@ -764,12 +763,12 @@ private boolean isInitializingObject(ExpressionTree variable) { TreePath treePath = atypeFactory.getPath(variable); if (treePath == null) return false; - if (TreeUtils.enclosingTopLevelBlock(treePath) != null) { + if (TreePathUtil.enclosingTopLevelBlock(treePath) != null) { // In the initialization block => always allow assigning fields! return true; } - MethodTree enclosingMethod = TreeUtils.enclosingMethod(treePath); + MethodTree enclosingMethod = TreePathUtil.enclosingMethod(treePath); // No possibility of initializing object if the assignment is not within constructor or method(both MethodTree) if (enclosingMethod == null) return false; // At this point, we already know that this assignment is field assignment within a method @@ -826,7 +825,7 @@ private void checkNewInstanceCreation(Tree node) { public Void visitMethodInvocation(MethodInvocationTree node, Void p) { // issues with getting super for anonymous class - do not check for anonymous classes. if (TreeUtils.isSuperConstructorCall(node) && - PICOTypeUtil.isAnonymousClassTree(TreeUtils.enclosingClass(atypeFactory.getPath(node)), atypeFactory)) { + PICOTypeUtil.isAnonymousClassTree(TreePathUtil.enclosingClass(atypeFactory.getPath(node)), atypeFactory)) { return null; } diff --git a/src/main/java/pico/inference/solver/PICOCombineConstraintEncoder.java b/src/main/java/pico/inference/solver/PICOCombineConstraintEncoder.java index 21ac92e..a03ccae 100644 --- a/src/main/java/pico/inference/solver/PICOCombineConstraintEncoder.java +++ b/src/main/java/pico/inference/solver/PICOCombineConstraintEncoder.java @@ -2,6 +2,7 @@ import checkers.inference.model.ConstantSlot; import checkers.inference.model.VariableSlot; +import checkers.inference.model.CombVariableSlot; import checkers.inference.solver.backend.encoder.combine.CombineConstraintEncoder; import checkers.inference.solver.backend.maxsat.MathUtils; import checkers.inference.solver.backend.maxsat.VectorUtils; @@ -51,7 +52,7 @@ private boolean isReceiverDependantMutable(ConstantSlot cSlot) { } @Override - public VecInt[] encodeVariable_Variable(VariableSlot target, VariableSlot declared, VariableSlot result) { + public VecInt[] encodeVariable_Variable(VariableSlot target, VariableSlot declared, CombVariableSlot result) { List resultClauses = new ArrayList(); resultClauses.add(VectorUtils.asVec( -MathUtils.mapIdToMatrixEntry(declared.getId(), id(READONLY), lattice), @@ -89,7 +90,7 @@ public VecInt[] encodeVariable_Variable(VariableSlot target, VariableSlot declar } @Override - public VecInt[] encodeVariable_Constant(VariableSlot target, ConstantSlot declared, VariableSlot result) { + public VecInt[] encodeVariable_Constant(VariableSlot target, ConstantSlot declared, CombVariableSlot result) { List resultClauses = new ArrayList(); if (!isReceiverDependantMutable(declared)) { resultClauses.add(VectorUtils.asVec( @@ -116,7 +117,7 @@ public VecInt[] encodeVariable_Constant(VariableSlot target, ConstantSlot declar } @Override - public VecInt[] encodeConstant_Variable(ConstantSlot target, VariableSlot declared, VariableSlot result) { + public VecInt[] encodeConstant_Variable(ConstantSlot target, VariableSlot declared, CombVariableSlot result) { List resultClauses = new ArrayList(); resultClauses.add(VectorUtils.asVec( -MathUtils.mapIdToMatrixEntry(declared.getId(), id(READONLY), lattice), @@ -137,7 +138,7 @@ public VecInt[] encodeConstant_Variable(ConstantSlot target, VariableSlot declar } @Override - public VecInt[] encodeConstant_Constant(ConstantSlot target, ConstantSlot declared, VariableSlot result) { + public VecInt[] encodeConstant_Constant(ConstantSlot target, ConstantSlot declared, CombVariableSlot result) { List resultClauses = new ArrayList(); if (!isReceiverDependantMutable(declared)) { resultClauses.add(VectorUtils.asVec( diff --git a/src/main/java/pico/typecheck/PICOAnnotatedTypeFactory.java b/src/main/java/pico/typecheck/PICOAnnotatedTypeFactory.java index b33e535..988d056 100644 --- a/src/main/java/pico/typecheck/PICOAnnotatedTypeFactory.java +++ b/src/main/java/pico/typecheck/PICOAnnotatedTypeFactory.java @@ -45,6 +45,7 @@ import org.checkerframework.javacutil.ElementUtils; import org.checkerframework.javacutil.Pair; import org.checkerframework.javacutil.TreeUtils; +import org.checkerframework.javacutil.TreePathUtil; import com.sun.source.tree.BinaryTree; import com.sun.source.tree.ExpressionTree; @@ -126,12 +127,12 @@ protected TypeAnnotator createTypeAnnotator() { RelevantJavaTypes relevantJavaTypes = checker.getClass().getAnnotation(RelevantJavaTypes.class); if (relevantJavaTypes != null) { - Class[] classes = relevantJavaTypes.value(); +// Class[] classes = relevantJavaTypes.value(); // Must be first in order to annotated all irrelevant types that are not explicilty // annotated. typeAnnotators.add( new IrrelevantTypeAnnotator( - this, getQualifierHierarchy().getTopAnnotations(), classes)); + this, getQualifierHierarchy().getTopAnnotations())); } typeAnnotators.add(new PropagationTypeAnnotator(this)); /*Copied code ends*/ @@ -449,7 +450,7 @@ public AnnotatedTypeMirror getTypeOfExtendsImplements(Tree clause) { // this is still needed with PICOSuperClauseAnnotator. // maybe just use getAnnotatedType // add default anno from class main qual, if no qual present - AnnotatedTypeMirror enclosing = getAnnotatedType(TreeUtils.enclosingClass(getPath(clause))); + AnnotatedTypeMirror enclosing = getAnnotatedType(TreePathUtil.enclosingClass(getPath(clause))); AnnotationMirror mainBound = enclosing.getAnnotationInHierarchy(READONLY); AnnotatedTypeMirror fromTypeTree = this.fromTypeTree(clause); if (!fromTypeTree.isAnnotatedInHierarchy(READONLY)) { @@ -643,7 +644,7 @@ private void addDefaultFromMain(Tree tree, AnnotatedTypeMirror mirror) { if (isSuperClause(path) && (!mirror.isAnnotatedInHierarchy(READONLY) || atypeFactory.getQualifierHierarchy().findAnnotationInHierarchy(TreeUtils.typeOf(tree).getAnnotationMirrors(), READONLY) == null)) { - AnnotatedTypeMirror enclosing = atypeFactory.getAnnotatedType(TreeUtils.enclosingClass(path)); + AnnotatedTypeMirror enclosing = atypeFactory.getAnnotatedType(TreePathUtil.enclosingClass(path)); AnnotationMirror mainBound = enclosing.getAnnotationInHierarchy(READONLY); mirror.replaceAnnotation(mainBound); // System.err.println("ANNOT: ADDED DEFAULT FOR: " + mirror); diff --git a/src/main/java/pico/typecheck/PICOValidator.java b/src/main/java/pico/typecheck/PICOValidator.java index 4c6b017..b651ad8 100644 --- a/src/main/java/pico/typecheck/PICOValidator.java +++ b/src/main/java/pico/typecheck/PICOValidator.java @@ -14,6 +14,7 @@ import org.checkerframework.javacutil.AnnotationUtils; import org.checkerframework.javacutil.ElementUtils; import org.checkerframework.javacutil.TreeUtils; +import org.checkerframework.javacutil.TreePathUtil; import pico.common.PICOTypeUtil; import javax.lang.model.element.AnnotationMirror; @@ -48,7 +49,7 @@ public Void visitDeclared(AnnotatedDeclaredType type, Tree tree) { } @Override - protected boolean shouldCheckTopLevelDeclaredType(AnnotatedTypeMirror type, Tree tree) { + protected boolean shouldCheckTopLevelDeclaredOrPrimitiveType(AnnotatedTypeMirror type, Tree tree) { // check top annotations in extends/implements clauses if ((tree.getKind() == Kind.IDENTIFIER || tree.getKind() == Kind.PARAMETERIZED_TYPE) && PICOAnnotatedTypeFactory.PICOSuperClauseAnnotator.isSuperClause(atypeFactory.getPath(tree))) { @@ -57,10 +58,10 @@ protected boolean shouldCheckTopLevelDeclaredType(AnnotatedTypeMirror type, Tree // allow RDM on mutable fields with enclosing class bounded with mutable if (tree instanceof VariableTree) { VariableElement element = TreeUtils.elementFromDeclaration((VariableTree)tree); - if (element.getKind() == ElementKind.FIELD && ElementUtils.enclosingClass(element) != null) { + if (element.getKind() == ElementKind.FIELD && ElementUtils.enclosingTypeElement(element) != null) { Set enclosingBound = atypeFactory.getTypeDeclarationBounds( - Objects.requireNonNull(ElementUtils.enclosingClass(element)).asType()); + Objects.requireNonNull(ElementUtils.enclosingTypeElement(element)).asType()); Set declaredBound = atypeFactory.getTypeDeclarationBounds(type.getUnderlyingType()); @@ -76,7 +77,7 @@ protected boolean shouldCheckTopLevelDeclaredType(AnnotatedTypeMirror type, Tree // return true; // } - return super.shouldCheckTopLevelDeclaredType(type, tree); + return super.shouldCheckTopLevelDeclaredOrPrimitiveType(type, tree); } @Override @@ -95,7 +96,7 @@ public Void visitPrimitive(AnnotatedPrimitiveType type, Tree tree) { private void checkStaticReceiverDependantMutableError(AnnotatedTypeMirror type, Tree tree) { if (!type.isDeclaration() // variables in static contexts and static fields use class decl as enclosing type && PICOTypeUtil.inStaticScope(visitor.getCurrentPath()) - && !"".contentEquals(Objects.requireNonNull(TreeUtils.enclosingClass(visitor.getCurrentPath())).getSimpleName()) // Exclude @RDM usages in anonymous classes + && !"".contentEquals(Objects.requireNonNull(TreePathUtil.enclosingClass(visitor.getCurrentPath())).getSimpleName()) // Exclude @RDM usages in anonymous classes && type.hasAnnotation(RECEIVER_DEPENDANT_MUTABLE)) { reportValidityResult("static.receiverdependantmutable.forbidden", type, tree); } diff --git a/src/main/java/pico/typecheck/PICOVisitor.java b/src/main/java/pico/typecheck/PICOVisitor.java index f23a74f..071746b 100644 --- a/src/main/java/pico/typecheck/PICOVisitor.java +++ b/src/main/java/pico/typecheck/PICOVisitor.java @@ -474,7 +474,7 @@ public Void visitMethodInvocation(MethodInvocationTree node, Void p) { private void saveFbcViolatedMethods(ExecutableElement method, String actualReceiver, String declaredReceiver) { if (actualReceiver.contains("@UnderInitialization") && declaredReceiver.contains("@Initialized")) { - String key = ElementUtils.enclosingClass(method) + "#" + method; + String key = ElementUtils.enclosingTypeElement(method) + "#" + method; Integer times = fbcViolatedMethods.get(key) == null ? 1 : fbcViolatedMethods.get(key) + 1; fbcViolatedMethods.put(key, times); } From e4ced889969a8efdb200f3111da9a7aa7b02638e Mon Sep 17 00:00:00 2001 From: Haifeng Shi Date: Wed, 13 Jul 2022 18:09:55 -0400 Subject: [PATCH 141/144] commits for fixing PICO-TypeCheck and part of PICO-Infer --- build.gradle | 4 + .../pico/inference/PICOInferenceChecker.java | 9 +- .../PICOInferenceRealTypeFactory.java | 5 +- .../pico/inference/PICOInferenceVisitor.java | 28 +- .../pico/inference/PICOVariableAnnotator.java | 177 +++++---- .../inference/solver/PICOSolverEngine.java | 16 +- .../java/pico/typecheck/PICOAnalysis.java | 4 +- .../typecheck/PICOAnnotatedTypeFactory.java | 348 ++++++++++-------- .../java/pico/typecheck/PICOTransfer.java | 8 - .../java/pico/typecheck/PICOValidator.java | 1 - src/main/java/pico/typecheck/PICOVisitor.java | 6 +- src/main/java/qual/Immutable.java | 11 + testinput/typecheck/CompatabilityBEI1.java | 2 - testinput/typecheck/CompatibilityBEI2.java | 2 - testinput/typecheck/DateCell.java | 2 +- testinput/typecheck/DateCell2.java | 2 +- testinput/typecheck/ForbidAssignmentCase.java | 1 + .../ImplicitAppliesToMethodReceiver.java | 2 +- .../PolyMutableOnConstructorParameters.java | 2 +- testinput/typecheck/RDMBug.java | 3 +- testinput/typecheck/RDMFieldInst.java | 3 +- 21 files changed, 338 insertions(+), 298 deletions(-) diff --git a/build.gradle b/build.gradle index a7e2f56..06829f1 100644 --- a/build.gradle +++ b/build.gradle @@ -50,6 +50,8 @@ sourceSets { main { java { srcDirs = ["src/main/java"] + exclude "pico/inference/**" + } resources { @@ -62,6 +64,8 @@ sourceSets { java { // TODO: we shouldn't need source level dependency on CFITest srcDirs = ["src/test/java", "${cfiPath}/tests/checkers/inference/test"] + exclude "pico/ImmutabilityInferenceInitialTypecheckTest.java","pico/ImmutabilityReImInferenceTest.java", + "pico/ImmutabilityInferenceTest.java" } } } diff --git a/src/main/java/pico/inference/PICOInferenceChecker.java b/src/main/java/pico/inference/PICOInferenceChecker.java index e826bc9..75915fc 100644 --- a/src/main/java/pico/inference/PICOInferenceChecker.java +++ b/src/main/java/pico/inference/PICOInferenceChecker.java @@ -1,11 +1,6 @@ package pico.inference; -import checkers.inference.BaseInferrableChecker; -import checkers.inference.InferenceAnnotatedTypeFactory; -import checkers.inference.InferenceChecker; -import checkers.inference.InferenceVisitor; -import checkers.inference.InferrableChecker; -import checkers.inference.SlotManager; +import checkers.inference.*; import checkers.inference.model.ConstraintManager; import org.checkerframework.common.basetype.BaseAnnotatedTypeFactory; import org.checkerframework.framework.source.SupportedOptions; @@ -24,7 +19,7 @@ public void initChecker() { } @Override - public BaseAnnotatedTypeFactory createRealTypeFactory() { + public BaseInferenceRealTypeFactory createRealTypeFactory(boolean infer) { return new PICOInferenceRealTypeFactory(this, true); } diff --git a/src/main/java/pico/inference/PICOInferenceRealTypeFactory.java b/src/main/java/pico/inference/PICOInferenceRealTypeFactory.java index 4d41241..248346b 100644 --- a/src/main/java/pico/inference/PICOInferenceRealTypeFactory.java +++ b/src/main/java/pico/inference/PICOInferenceRealTypeFactory.java @@ -16,6 +16,7 @@ import javax.lang.model.type.TypeMirror; import javax.lang.model.util.Elements; +import checkers.inference.BaseInferenceRealTypeFactory; import com.sun.tools.javac.tree.JCTree; import org.checkerframework.common.basetype.BaseAnnotatedTypeFactory; import org.checkerframework.common.basetype.BaseTypeChecker; @@ -58,7 +59,7 @@ * to InitializationAnnotatedTypeFactory as if there is only one mutability qualifier hierarchy. * This class has lots of copied code from PICOAnnotatedTypeFactory. The two should be in sync. */ -public class PICOInferenceRealTypeFactory extends BaseAnnotatedTypeFactory implements ViewpointAdapterGettable { +public class PICOInferenceRealTypeFactory extends BaseInferenceRealTypeFactory implements ViewpointAdapterGettable { private static final List IMMUTABLE_ALIASES = Arrays.asList( "com.google.errorprone.annotations.Immutable", @@ -226,7 +227,7 @@ public AnnotatedTypeMirror getTypeOfExtendsImplements(Tree clause) { } AnnotationMirror mainBound = enclosing.getAnnotationInHierarchy(READONLY); - AnnotatedTypeMirror fromTypeTree = this.fromTypeTree(clause); + AnnotatedTypeMirror fromTypeTree = this.getAnnotatedTypeFromTypeTree(clause); if (!fromTypeTree.isAnnotatedInHierarchy(READONLY)) { fromTypeTree.addAnnotation(mainBound); } diff --git a/src/main/java/pico/inference/PICOInferenceVisitor.java b/src/main/java/pico/inference/PICOInferenceVisitor.java index 2be5954..0322100 100644 --- a/src/main/java/pico/inference/PICOInferenceVisitor.java +++ b/src/main/java/pico/inference/PICOInferenceVisitor.java @@ -148,6 +148,7 @@ private boolean isAdaptedSubtype(AnnotatedTypeMirror lhs, AnnotatedTypeMirror rh if (extractVarAnnot(lhs).equals(extractVarAnnot(rhs))) { return true; } + // todo: haifeng we should do the viewpointAdapt in baseTypeValidator.java#visitDeclared 299 function:getTypeDeclarationBounds ExtendedViewpointAdapter vpa = ((ViewpointAdapterGettable)atypeFactory).getViewpointAdapter(); AnnotatedTypeMirror adapted = vpa.rawCombineAnnotationWithType(extractVarAnnot(lhs), rhs); @@ -494,7 +495,22 @@ public Void visitMethod(MethodTree node, Void p) { // TODO Object identity check return super.visitMethod(node, p); } - + /* + * @RDM + * class A { + * + * void foo(T) { + * + * } + * } + * class B extends @Immutable A<@X String> { + * + * @Override + * void foo(@Y String) { // string is compatible to bound of T. Adapt the signature of Class A to the use of class B. + * } + * } + * + * */ private void flexibleOverrideChecker(MethodTree node) { // Method overriding checks // TODO Copied from super, hence has lots of duplicate code with super. We need to @@ -520,7 +536,7 @@ private void flexibleOverrideChecker(MethodTree node) { types, atypeFactory, enclosingType, pair.getValue()); // Viewpoint adapt super method executable type to current class bound(is this always class bound?) // to allow flexible overriding - atypeFactory.getViewpointAdapter().viewpointAdaptMethod(enclosingType, pair.getValue() , overriddenMethod); + atypeFactory.getViewpointAdapter().viewpointAdaptMethod(enclosingType, pair.getValue() , overriddenMethod); // todo: should we cast it? AnnotatedExecutableType overrider = atypeFactory.getAnnotatedType(node); if (!checkOverride(node, overrider, enclosingType, overriddenMethod, overriddenType)) { // Stop at the first mismatch; this makes a difference only if @@ -707,7 +723,7 @@ private void checkAssignableField(ExpressionTree node, ExpressionTree variable, } } - private void checkInitializingObject(ExpressionTree node, ExpressionTree variable, AnnotatedTypeMirror receiverType) { + private void checkInitializingObject(ExpressionTree node, ExpressionTree variable, AnnotatedTypeMirror receiverType) { // todo: haifeng we only need to do this in one statement // TODO rm infer after mainIsNot returns bool if (infer) { // Can be anything from mutable, immutable or receiverdependantmutable @@ -718,7 +734,7 @@ private void checkInitializingObject(ExpressionTree node, ExpressionTree variabl } } } - + // todo: haifeng: the deciding factor seems to be if it is array or not. Not infer. private void checkMutableReceiverCase(ExpressionTree node, ExpressionTree variable, AnnotatedTypeMirror receiverType) { // TODO rm infer after mainIs returns bool if (infer) { @@ -1004,7 +1020,7 @@ private boolean checkCompatabilityBetweenBoundAndSuperClassesBounds(ClassTree no */ @Override protected void commonAssignmentCheck( - Tree varTree, ExpressionTree valueExp, String errorKey) { + Tree varTree, ExpressionTree valueExp, String errorKey, Object... extraArgs) { AnnotatedTypeMirror var = atypeFactory.getAnnotatedTypeLhs(varTree); assert var != null : "no variable found for tree: " + varTree; @@ -1043,7 +1059,7 @@ protected void commonAssignmentCheck( @Override protected void commonAssignmentCheck(AnnotatedTypeMirror varType, AnnotatedTypeMirror valueType, Tree valueTree, - String errorKey) { + String errorKey, Object... extraArgs) { // TODO: WORKAROUND: anonymous class handling if (TypesUtils.isAnonymous(valueType.getUnderlyingType())) { AnnotatedTypeMirror newValueType = varType.deepCopy(); diff --git a/src/main/java/pico/inference/PICOVariableAnnotator.java b/src/main/java/pico/inference/PICOVariableAnnotator.java index a8fda07..4d92a2e 100644 --- a/src/main/java/pico/inference/PICOVariableAnnotator.java +++ b/src/main/java/pico/inference/PICOVariableAnnotator.java @@ -45,80 +45,80 @@ public PICOVariableAnnotator(InferenceAnnotatedTypeFactory typeFactory, Annotate super(typeFactory, realTypeFactory, realChecker, slotManager, constraintManager); } - @Override - protected void handleClassDeclaration(AnnotatedDeclaredType classType, ClassTree classTree) { - super.handleClassDeclaration(classType, classTree); - int interfaceIndex = 1; - for(Tree implementsTree : classTree.getImplementsClause()) { - final AnnotatedTypeMirror implementsType = inferenceTypeFactory.getAnnotatedTypeFromTypeTree(implementsTree); - AnnotatedTypeMirror supertype = classType.directSuperTypes().get(interfaceIndex); - assert supertype.getUnderlyingType() == implementsType.getUnderlyingType(); - visit(supertype, implementsTree); - interfaceIndex++; - } - } - - @Override - protected void handleClassDeclarationBound(AnnotatedDeclaredType classType) { - TypeElement classElement = (TypeElement) classType.getUnderlyingType().asElement(); - if (classDeclAnnos.containsKey(classElement)) { - classType.addAnnotation(slotManager.getAnnotation(classDeclAnnos.get(classElement))); - classType.addAnnotation(READONLY); - return; - } - AnnotatedDeclaredType bound = inferenceTypeFactory.fromElement(classElement); - - VariableSlot boundSlot; - - // Insert @Immutable VarAnnot directly to enum bound -// if (PICOTypeUtil.isEnumOrEnumConstant(bound)) { -// boundSlot = slotManager.createConstantSlot(IMMUTABLE); -// classType.addAnnotation(slotManager.getAnnotation(boundSlot)); -// classDeclAnnos.put(classElement, boundSlot); +// @Override +// protected void handleClassDeclaration(AnnotatedDeclaredType classType, ClassTree classTree) { +// super.handleClassDeclaration(classType, classTree); +// int interfaceIndex = 1; +// for(Tree implementsTree : classTree.getImplementsClause()) { +// final AnnotatedTypeMirror implementsType = inferenceTypeFactory.getAnnotatedTypeFromTypeTree(implementsTree); +// AnnotatedTypeMirror supertype = classType.directSupertypes().get(interfaceIndex); +// assert supertype.getUnderlyingType() == implementsType.getUnderlyingType(); +// visit(supertype, implementsTree); +// interfaceIndex++; +// } +// } + +// @Override +// protected void handleClassDeclarationBound(AnnotatedDeclaredType classType) { +// TypeElement classElement = (TypeElement) classType.getUnderlyingType().asElement(); +// if (classDeclAnnos.containsKey(classElement)) { +// classType.addAnnotation(slotManager.getAnnotation(classDeclAnnos.get(classElement))); +// classType.addAnnotation(READONLY); // return; // } - - Tree classTree = inferenceTypeFactory.declarationFromElement(classElement); - if (classTree != null) { - // Have source tree - if (bound.isAnnotatedInHierarchy(READONLY)) { - // Have bound annotation -> convert to equivalent ConstantSlot - boundSlot = slotManager.createConstantSlot(bound.getAnnotationInHierarchy(READONLY)); - } else { - // No existing annotation -> create new VariableSlot - boundSlot = createVariable(treeToLocation(classTree)); - } - } else { - // No source tree: bytecode classes - if (bound.isAnnotatedInHierarchy(READONLY)) { - // Have bound annotation in stub file - boundSlot = slotManager.createConstantSlot(bound.getAnnotationInHierarchy(READONLY)); - } else { - // No stub file - if (PICOTypeUtil.isImplicitlyImmutableType(classType)) { - // Implicitly immutable - boundSlot = slotManager.createConstantSlot(IMMUTABLE); - } else { - // None of the above applies: use conservative @Mutable - boundSlot = slotManager.createConstantSlot(MUTABLE); - } - } - } - classType.addAnnotation(slotManager.getAnnotation(boundSlot)); - classDeclAnnos.put(classElement, boundSlot); - } +// AnnotatedDeclaredType bound = inferenceTypeFactory.fromElement(classElement); +// +// VariableSlot boundSlot; +// +// // Insert @Immutable VarAnnot directly to enum bound +//// if (PICOTypeUtil.isEnumOrEnumConstant(bound)) { +//// boundSlot = slotManager.createConstantSlot(IMMUTABLE); +//// classType.addAnnotation(slotManager.getAnnotation(boundSlot)); +//// classDeclAnnos.put(classElement, boundSlot); +//// return; +//// } +// +// Tree classTree = inferenceTypeFactory.declarationFromElement(classElement); +// if (classTree != null) { +// // Have source tree +// if (bound.isAnnotatedInHierarchy(READONLY)) { +// // Have bound annotation -> convert to equivalent ConstantSlot +// boundSlot = slotManager.createConstantSlot(bound.getAnnotationInHierarchy(READONLY)); +// } else { +// // No existing annotation -> create new VariableSlot +// boundSlot = createVariable(treeToLocation(classTree)); +// } +// } else { +// // No source tree: bytecode classes +// if (bound.isAnnotatedInHierarchy(READONLY)) { +// // Have bound annotation in stub file +// boundSlot = slotManager.createConstantSlot(bound.getAnnotationInHierarchy(READONLY)); +// } else { +// // No stub file +// if (PICOTypeUtil.isImplicitlyImmutableType(classType)) { +// // Implicitly immutable +// boundSlot = slotManager.createConstantSlot(IMMUTABLE); +// } else { +// // None of the above applies: use conservative @Mutable +// boundSlot = slotManager.createConstantSlot(MUTABLE); +// } +// } +// } +// classType.addAnnotation(slotManager.getAnnotation(boundSlot)); +// classDeclAnnos.put(classElement, boundSlot); +// } @Override protected VariableSlot getOrCreateDeclBound(AnnotatedDeclaredType type) { TypeElement classDecl = (TypeElement) type.getUnderlyingType().asElement(); - VariableSlot declSlot = classDeclAnnos.get(classDecl); + AnnotationMirror declSlot = getClassDeclVarAnnot(classDecl); if (declSlot == null) { // if a explicit annotation presents on the class DECL, use that directly if (type.isDeclaration() && type.isAnnotatedInHierarchy(READONLY) && !type.hasAnnotation(READONLY)) { VariableSlot constantSlot = (VariableSlot) slotManager.getSlot(type.getAnnotationInHierarchy(READONLY)); // TypeElement classDecl = (TypeElement) type.getUnderlyingType().asElement(); - classDeclAnnos.put(classDecl, constantSlot); + super.getOrCreateDeclBound(type); // // avoid duplicate annos // type.removeAnnotationInHierarchy(READONLY); return constantSlot; @@ -134,21 +134,21 @@ protected VariableSlot getOrCreateDeclBound(AnnotatedDeclaredType type) { return (VariableSlot) slotManager.getSlot(type.getAnnotation(VarAnnot.class)); } } - return super.getOrCreateDeclBound(type); + return (VariableSlot) super.getOrCreateDeclBound(type); } - @Override - protected void handleExplicitExtends(Tree extendsTree) { - // PICO cannot use base extends handling: not simply subtype relationship because of RDM - // Constraints already generated in processClassTree - } +// @Override +// protected void handleExplicitExtends(Tree extendsTree) { +// // PICO cannot use base extends handling: not simply subtype relationship because of RDM +// // Constraints already generated in processClassTree +// } @Override public void storeElementType(Element element, AnnotatedTypeMirror atm) { // this method is override the behavior of super.handleClassDeclaration before storing // find a better way - Slot slot = slotManager.getVariableSlot(atm); + Slot slot = slotManager.getSlot(atm); // do not use potential slot generated on the class decl annotation // PICO always have a annotation on the class bound, so Existential should always exist // TODO make VariableAnnotator::getOrCreateDeclBound protected and override that instead of this method @@ -167,22 +167,22 @@ public void storeElementType(Element element, AnnotatedTypeMirror atm) { } // Don't generate subtype constraint between use type and bound type - @Override - protected void handleInstantiationConstraint(AnnotatedTypeMirror.AnnotatedDeclaredType adt, VariableSlot instantiationSlot, Tree tree) { - return; - } - - @Override - protected VariableSlot addPrimaryVariable(AnnotatedTypeMirror atm, Tree tree) { -// if (PICOTypeUtil.isEnumOrEnumConstant(atm)) { -// // Don't add new VarAnnot to type use of enum type -// PICOTypeUtil.applyConstant(atm, IMMUTABLE); +// @Override +// protected void handleInstantiationConstraint(AnnotatedTypeMirror.AnnotatedDeclaredType adt, VariableSlot instantiationSlot, Tree tree) { +// return; +// } + +// @Override +// protected VariableSlot addPrimaryVariable(AnnotatedTypeMirror atm, Tree tree) { +//// if (PICOTypeUtil.isEnumOrEnumConstant(atm)) { +//// // Don't add new VarAnnot to type use of enum type +//// PICOTypeUtil.applyConstant(atm, IMMUTABLE); +//// } +// if (atm instanceof AnnotatedTypeMirror.AnnotatedNullType) { +// PICOTypeUtil.applyConstant(atm, BOTTOM); // } - if (atm instanceof AnnotatedTypeMirror.AnnotatedNullType) { - PICOTypeUtil.applyConstant(atm, BOTTOM); - } - return super.addPrimaryVariable(atm, tree); - } +// return super.addPrimaryVariable(atm, tree); +// } // Generates inequality constraint between every strict VariableSlot and @Bottom so that @Bottom is not inserted // back to source code, but can be within the internal state because of dataflow refinement @@ -285,7 +285,7 @@ public Void visitWildcard(AnnotatedTypeMirror.AnnotatedWildcardType wildcardType @Override public void handleBinaryTree(AnnotatedTypeMirror atm, BinaryTree binaryTree) { - if (atm.isAnnotatedInHierarchy(varAnnot)) { + if (atm.isAnnotatedInHierarchy(inferenceTypeFactory.getVarAnnot())) { // Happens for binary trees whose atm is implicitly immutable and already handled by // PICOInferencePropagationTreeAnnotator return; @@ -294,15 +294,12 @@ public void handleBinaryTree(AnnotatedTypeMirror atm, BinaryTree binaryTree) { } public AnnotationMirror getClassDeclAnno(Element ele) { - if (classDeclAnnos.get(ele) != null) { - return slotManager.getAnnotation(classDeclAnnos.get(ele)); - } - return null; + return getClassDeclVarAnnot((TypeElement) ele); // todo: solved } @Override - protected void addDeclarationConstraints(VariableSlot declSlot, VariableSlot instanceSlot) { + protected void addDeclarationConstraints(Slot declSlot, Slot instanceSlot) { // RDM-related constraints cannot use subtype. // Necessary constraints added in visitor instead. } diff --git a/src/main/java/pico/inference/solver/PICOSolverEngine.java b/src/main/java/pico/inference/solver/PICOSolverEngine.java index 8fb26bc..5ff0d7d 100644 --- a/src/main/java/pico/inference/solver/PICOSolverEngine.java +++ b/src/main/java/pico/inference/solver/PICOSolverEngine.java @@ -24,14 +24,14 @@ * to solve constraints */ public class PICOSolverEngine extends SolverEngine { - @Override - public InferenceResult solve(Map configuration, Collection slots, Collection constraints, QualifierHierarchy qualHierarchy, ProcessingEnvironment processingEnvironment) { - InferenceResult result= super.solve(configuration, slots, constraints, qualHierarchy, processingEnvironment); - if (collectStatistics && result.hasSolution()) { - writeInferenceResult("pico-inference-result.txt", ((DefaultInferenceResult)result).varIdToAnnotation); - } - return result; - } +// @Override +// public InferenceResult solve(Map configuration, Collection slots, Collection constraints, QualifierHierarchy qualHierarchy, ProcessingEnvironment processingEnvironment) { +// InferenceResult result= super.solve(configuration, slots, constraints, qualHierarchy, processingEnvironment); +// if (collectStatistics && result.hasSolution()) { +// writeInferenceResult("pico-inference-result.txt", ((DefaultInferenceResult)result).varIdToAnnotation); +// } +// return result; +// } // TODO: default write into statistic.txt public static void writeInferenceResult(String filename, Map result) { String writePath = new File(new File("").getAbsolutePath()).toString() + File.separator + filename; diff --git a/src/main/java/pico/typecheck/PICOAnalysis.java b/src/main/java/pico/typecheck/PICOAnalysis.java index 54cecfa..1230003 100644 --- a/src/main/java/pico/typecheck/PICOAnalysis.java +++ b/src/main/java/pico/typecheck/PICOAnalysis.java @@ -16,8 +16,8 @@ */ public class PICOAnalysis extends CFAbstractAnalysis { - public PICOAnalysis(BaseTypeChecker checker, PICOAnnotatedTypeFactory factory, List> fieldValues) { - super(checker, factory, fieldValues); + public PICOAnalysis(BaseTypeChecker checker, PICOAnnotatedTypeFactory factory) { + super(checker, factory, -1); } @Override diff --git a/src/main/java/pico/typecheck/PICOAnnotatedTypeFactory.java b/src/main/java/pico/typecheck/PICOAnnotatedTypeFactory.java index 988d056..e373d4e 100644 --- a/src/main/java/pico/typecheck/PICOAnnotatedTypeFactory.java +++ b/src/main/java/pico/typecheck/PICOAnnotatedTypeFactory.java @@ -39,7 +39,7 @@ import org.checkerframework.framework.type.treeannotator.PropagationTreeAnnotator; import org.checkerframework.framework.type.treeannotator.TreeAnnotator; import org.checkerframework.framework.type.typeannotator.*; -import org.checkerframework.framework.util.MultiGraphQualifierHierarchy.MultiGraphFactory; +import org.checkerframework.framework.util.QualifierKind; import org.checkerframework.javacutil.AnnotationUtils; import org.checkerframework.javacutil.BugInCF; import org.checkerframework.javacutil.ElementUtils; @@ -147,8 +147,8 @@ protected TypeAnnotator createTypeAnnotator() { } @Override - public QualifierHierarchy createQualifierHierarchy(MultiGraphFactory factory) { - return new PICOQualifierHierarchy(factory, null); + public QualifierHierarchy createQualifierHierarchy() { + return new PICOQualifierHierarchy(); } /**Just to transfer the method from super class to package*/ @@ -189,41 +189,41 @@ public void addComputedTypeAnnotations(Element elt, AnnotatedTypeMirror type) { /**This method gets lhs WITH flow sensitive refinement*/ // TODO Should refactor super class to avoid too much duplicate code. // This method is pretty hacky right now. - @Override - public AnnotatedTypeMirror getAnnotatedTypeLhs(Tree lhsTree) { - boolean oldComputingAnnotatedTypeMirrorOfLHS = computingAnnotatedTypeMirrorOfLHS; - computingAnnotatedTypeMirrorOfLHS = true; - - AnnotatedTypeMirror result; - boolean oldShouldCache = shouldCache; - // Don't cache the result because getAnnotatedType(lhsTree) could - // be called from elsewhere and would expect flow-sensitive type refinements. - shouldCache = false; - switch (lhsTree.getKind()) { - case VARIABLE: - case IDENTIFIER: - case MEMBER_SELECT: - case ARRAY_ACCESS: - result = getAnnotatedType(lhsTree); - break; - default: - if (TreeUtils.isTypeTree(lhsTree)) { - // lhsTree is a type tree at the pseudo assignment of a returned expression to declared return type. - result = getAnnotatedType(lhsTree); - } else { - throw new BugInCF( - "GenericAnnotatedTypeFactory: Unexpected tree passed to getAnnotatedTypeLhs. " - + "lhsTree: " - + lhsTree - + " Tree.Kind: " - + lhsTree.getKind()); - } - } - shouldCache = oldShouldCache; - - computingAnnotatedTypeMirrorOfLHS = oldComputingAnnotatedTypeMirrorOfLHS; - return result; - } +// @Override +// public AnnotatedTypeMirror getAnnotatedTypeLhs(Tree lhsTree) { +// boolean oldComputingAnnotatedTypeMirrorOfLHS = computingAnnotatedTypeMirrorOfLHS; +// computingAnnotatedTypeMirrorOfLHS = true; +// +// AnnotatedTypeMirror result; +// boolean oldShouldCache = shouldCache; +// // Don't cache the result because getAnnotatedType(lhsTree) could +// // be called from elsewhere and would expect flow-sensitive type refinements. +// shouldCache = false; +// switch (lhsTree.getKind()) { +// case VARIABLE: +// case IDENTIFIER: +// case MEMBER_SELECT: +// case ARRAY_ACCESS: +// result = getAnnotatedType(lhsTree); +// break; +// default: +// if (TreeUtils.isTypeTree(lhsTree)) { +// // lhsTree is a type tree at the pseudo assignment of a returned expression to declared return type. +// result = getAnnotatedType(lhsTree); +// } else { +// throw new BugInCF( +// "GenericAnnotatedTypeFactory: Unexpected tree passed to getAnnotatedTypeLhs. " +// + "lhsTree: " +// + lhsTree +// + " Tree.Kind: " +// + lhsTree.getKind()); +// } +// } +// shouldCache = oldShouldCache; +// +// computingAnnotatedTypeMirrorOfLHS = oldComputingAnnotatedTypeMirrorOfLHS; +// return result; +// } // /**Handles invoking static methods with polymutable on its declaration*/ // @Override @@ -250,25 +250,46 @@ public AnnotatedTypeMirror getAnnotatedTypeLhs(Tree lhsTree) { protected class PICOQualifierHierarchy extends InitializationQualifierHierarchy { - public PICOQualifierHierarchy(MultiGraphFactory f, Object[] arg) { - super(f, arg); - } +// public PICOQualifierHierarchy(MultiGraphFactory f, Object[] arg) { +// super(f, arg); +// } + +// @Override +// public boolean isSubtype(AnnotationMirror subAnno, AnnotationMirror superAnno) { +// if (isInitializationAnnotation(subAnno) || isInitializationAnnotation(superAnno)) { +// return this.isSubtypeInitialization(subAnno, superAnno); +// } +// return super.isSubtype(subAnno, superAnno); +// } @Override - public boolean isSubtype(AnnotationMirror subAnno, AnnotationMirror superAnno) { + protected boolean isSubtypeWithElements(AnnotationMirror subAnno, QualifierKind subKind, AnnotationMirror superAnno, QualifierKind superKind) { if (isInitializationAnnotation(subAnno) || isInitializationAnnotation(superAnno)) { - return this.isSubtypeInitialization(subAnno, superAnno); + return this.isSubtypeInitialization(subAnno, subKind, superAnno, superKind); } return super.isSubtype(subAnno, superAnno); } +// @Override +// public AnnotationMirror leastUpperBound(AnnotationMirror a1, AnnotationMirror a2) { +// if (isInitializationAnnotation(a1) || isInitializationAnnotation(a2)) { +// return this.leastUpperBoundInitialization(a1, a2); +// } +// return super.leastUpperBound(a1, a2); +// } + @Override - public AnnotationMirror leastUpperBound(AnnotationMirror a1, AnnotationMirror a2) { + protected AnnotationMirror leastUpperBoundWithElements(AnnotationMirror a1, QualifierKind q1, AnnotationMirror a2, QualifierKind q2, QualifierKind lub) { if (isInitializationAnnotation(a1) || isInitializationAnnotation(a2)) { - return this.leastUpperBoundInitialization(a1, a2); + return this.leastUpperBoundInitialization(a1, q1, a2, q2); } return super.leastUpperBound(a1, a2); } + + @Override + protected AnnotationMirror greatestLowerBoundWithElements(AnnotationMirror annotationMirror, QualifierKind qualifierKind, AnnotationMirror annotationMirror1, QualifierKind qualifierKind1, QualifierKind qualifierKind2) { + return null; + } } /**Tree Annotators*/ @@ -276,108 +297,108 @@ public static class PICOPropagationTreeAnnotator extends PropagationTreeAnnotato public PICOPropagationTreeAnnotator(AnnotatedTypeFactory atypeFactory) { super(atypeFactory); } +// +// // TODO This is very ugly. Why is array component type from lhs propagates to rhs?! +// @Override +// public Void visitNewArray(NewArrayTree tree, AnnotatedTypeMirror type) { +// // Below is copied from super +// assert type.getKind() == TypeKind.ARRAY +// : "PropagationTreeAnnotator.visitNewArray: should be an array type"; +// +// AnnotatedTypeMirror componentType = ((AnnotatedTypeMirror.AnnotatedArrayType) type).getComponentType(); +// +// Collection prev = null; +// if (tree.getInitializers() != null && tree.getInitializers().size() != 0) { +// // We have initializers, either with or without an array type. +// +// for (ExpressionTree init : tree.getInitializers()) { +// AnnotatedTypeMirror initType = atypeFactory.getAnnotatedType(init); +// // initType might be a typeVariable, so use effectiveAnnotations. +// Collection annos = initType.getEffectiveAnnotations(); +// +// prev = (prev == null) ? annos : atypeFactory.getQualifierHierarchy().leastUpperBounds(prev, annos); +// } +// } else { +// prev = componentType.getAnnotations(); +// } +// +// assert prev != null +// : "PropagationTreeAnnotator.visitNewArray: violated assumption about qualifiers"; +// +// Pair context = +// atypeFactory.getVisitorState().getAssignmentContext(); +// Collection post; +// +// if (context != null +// && context.second != null +// && context.second instanceof AnnotatedTypeMirror.AnnotatedArrayType) { +// AnnotatedTypeMirror contextComponentType = +// ((AnnotatedTypeMirror.AnnotatedArrayType) context.second).getComponentType(); +// // Only compare the qualifiers that existed in the array type +// // Defaulting wasn't performed yet, so prev might have fewer qualifiers than +// // contextComponentType, which would cause a failure. +// // TODO: better solution? +// boolean prevIsSubtype = true; +// for (AnnotationMirror am : prev) { +// if (contextComponentType.isAnnotatedInHierarchy(am) +// && !atypeFactory.getQualifierHierarchy().isSubtype( +// am, contextComponentType.getAnnotationInHierarchy(am))) { +// prevIsSubtype = false; +// } +// } +// // TODO: checking conformance of component kinds is a basic sanity check +// // It fails for array initializer expressions. Those should be handled nicer. +// if (contextComponentType.getKind() == componentType.getKind() +// && (prev.isEmpty() +// || (!contextComponentType.getAnnotations().isEmpty() +// && prevIsSubtype))) { +// post = contextComponentType.getAnnotations(); +// } else { +// // The type of the array initializers is incompatible with the +// // context type! +// // Somebody else will complain. +// post = prev; +// } +// } else { +// // No context is available - simply use what we have. +// post = prev; +// } +// +// // Below line is the only difference from super implementation +// applyImmutableIfImplicitlyImmutable(componentType); +// // Above line is the only difference from super implementation +// componentType.addMissingAnnotations(post); +// +// return null; +// // Above is copied from super +// } +// +// /**Add immutable to the result type of a binary operation if the result type is implicitly immutable*/ +// @Override +// public Void visitBinary(BinaryTree node, AnnotatedTypeMirror type) { +// applyImmutableIfImplicitlyImmutable(type);// Usually there isn't existing annotation on binary trees, but to be safe, run it first +// super.visitBinary(node, type); +// // NullnessPropagationTreeAnnotator says result type of binary tree is always @Initialized. So replace it +// // with COMMITED here. +// applyCommitedIfSupported(atypeFactory, type); +// return null; +// } - // TODO This is very ugly. Why is array component type from lhs propagates to rhs?! - @Override - public Void visitNewArray(NewArrayTree tree, AnnotatedTypeMirror type) { - // Below is copied from super - assert type.getKind() == TypeKind.ARRAY - : "PropagationTreeAnnotator.visitNewArray: should be an array type"; - - AnnotatedTypeMirror componentType = ((AnnotatedTypeMirror.AnnotatedArrayType) type).getComponentType(); - - Collection prev = null; - if (tree.getInitializers() != null && tree.getInitializers().size() != 0) { - // We have initializers, either with or without an array type. - - for (ExpressionTree init : tree.getInitializers()) { - AnnotatedTypeMirror initType = atypeFactory.getAnnotatedType(init); - // initType might be a typeVariable, so use effectiveAnnotations. - Collection annos = initType.getEffectiveAnnotations(); - - prev = (prev == null) ? annos : atypeFactory.getQualifierHierarchy().leastUpperBounds(prev, annos); - } - } else { - prev = componentType.getAnnotations(); - } - - assert prev != null - : "PropagationTreeAnnotator.visitNewArray: violated assumption about qualifiers"; - - Pair context = - atypeFactory.getVisitorState().getAssignmentContext(); - Collection post; - - if (context != null - && context.second != null - && context.second instanceof AnnotatedTypeMirror.AnnotatedArrayType) { - AnnotatedTypeMirror contextComponentType = - ((AnnotatedTypeMirror.AnnotatedArrayType) context.second).getComponentType(); - // Only compare the qualifiers that existed in the array type - // Defaulting wasn't performed yet, so prev might have fewer qualifiers than - // contextComponentType, which would cause a failure. - // TODO: better solution? - boolean prevIsSubtype = true; - for (AnnotationMirror am : prev) { - if (contextComponentType.isAnnotatedInHierarchy(am) - && !atypeFactory.getQualifierHierarchy().isSubtype( - am, contextComponentType.getAnnotationInHierarchy(am))) { - prevIsSubtype = false; - } - } - // TODO: checking conformance of component kinds is a basic sanity check - // It fails for array initializer expressions. Those should be handled nicer. - if (contextComponentType.getKind() == componentType.getKind() - && (prev.isEmpty() - || (!contextComponentType.getAnnotations().isEmpty() - && prevIsSubtype))) { - post = contextComponentType.getAnnotations(); - } else { - // The type of the array initializers is incompatible with the - // context type! - // Somebody else will complain. - post = prev; - } - } else { - // No context is available - simply use what we have. - post = prev; - } - - // Below line is the only difference from super implementation - applyImmutableIfImplicitlyImmutable(componentType); - // Above line is the only difference from super implementation - componentType.addMissingAnnotations(post); - - return null; - // Above is copied from super - } - - /**Add immutable to the result type of a binary operation if the result type is implicitly immutable*/ - @Override - public Void visitBinary(BinaryTree node, AnnotatedTypeMirror type) { - applyImmutableIfImplicitlyImmutable(type);// Usually there isn't existing annotation on binary trees, but to be safe, run it first - super.visitBinary(node, type); - // NullnessPropagationTreeAnnotator says result type of binary tree is always @Initialized. So replace it - // with COMMITED here. - applyCommitedIfSupported(atypeFactory, type); - return null; - } - - @Override - public Void visitUnary(UnaryTree node, AnnotatedTypeMirror type) { - super.visitUnary(node, type); - // Same reason as above - applyCommitedIfSupported(atypeFactory, type); - return null; - } - - /**Add immutable to the result type of a cast if the result type is implicitly immutable*/ - @Override - public Void visitTypeCast(TypeCastTree node, AnnotatedTypeMirror type) { - applyImmutableIfImplicitlyImmutable(type);// Must run before calling super method to respect existing annotation - return super.visitTypeCast(node, type); - } - +// @Override +// public Void visitUnary(UnaryTree node, AnnotatedTypeMirror type) { +// super.visitUnary(node, type); +// // Same reason as above +// applyCommitedIfSupported(atypeFactory, type); +// return null; +// } +// +// /**Add immutable to the result type of a cast if the result type is implicitly immutable*/ +// @Override +// public Void visitTypeCast(TypeCastTree node, AnnotatedTypeMirror type) { +// applyImmutableIfImplicitlyImmutable(type);// Must run before calling super method to respect existing annotation +// return super.visitTypeCast(node, type); +// } +// /**Because TreeAnnotator runs before DefaultForTypeAnnotator, implicitly immutable types are not guaranteed to always have immutable annotation. If this happens, we manually add immutable to type. We use addMissingAnnotations because we want to respect existing annotation on type*/ @@ -387,11 +408,16 @@ private void applyImmutableIfImplicitlyImmutable(AnnotatedTypeMirror type) { } } - private void applyCommitedIfSupported(AnnotatedTypeFactory annotatedTypeFactory, AnnotatedTypeMirror type) { - if (annotatedTypeFactory.isSupportedQualifier(COMMITED)) { - type.replaceAnnotation(COMMITED); - } + @Override + public Void visitBinary(BinaryTree node, AnnotatedTypeMirror type) { + return null; } + +// private void applyCommitedIfSupported(AnnotatedTypeFactory annotatedTypeFactory, AnnotatedTypeMirror type) { +// if (annotatedTypeFactory.isSupportedQualifier(COMMITED)) { +// type.replaceAnnotation(COMMITED); +// } +// } } public ExtendedViewpointAdapter getViewpointAdapter() { @@ -429,7 +455,7 @@ public AnnotationMirror getTypeDeclarationBoundForMutability(TypeMirror type) { return IMMUTABLE; } if (type.getKind() == TypeKind.ARRAY) { - return RECEIVER_DEPENDANT_MUTABLE; // if decided to use vpa for array, return RDM. + return READONLY; // if decided to use vpa for array, return RDM. } // IMMUTABLE for enum w/o decl anno @@ -450,16 +476,12 @@ public AnnotatedTypeMirror getTypeOfExtendsImplements(Tree clause) { // this is still needed with PICOSuperClauseAnnotator. // maybe just use getAnnotatedType // add default anno from class main qual, if no qual present - AnnotatedTypeMirror enclosing = getAnnotatedType(TreePathUtil.enclosingClass(getPath(clause))); - AnnotationMirror mainBound = enclosing.getAnnotationInHierarchy(READONLY); - AnnotatedTypeMirror fromTypeTree = this.fromTypeTree(clause); - if (!fromTypeTree.isAnnotatedInHierarchy(READONLY)) { - fromTypeTree.addAnnotation(mainBound); + AnnotatedTypeMirror fromTypeTree = super.getTypeOfExtendsImplements(clause); + if (fromTypeTree.hasAnnotation(RECEIVER_DEPENDANT_MUTABLE)) { + AnnotatedTypeMirror enclosing = getAnnotatedType(TreePathUtil.enclosingClass(getPath(clause))); + AnnotationMirror mainBound = enclosing.getAnnotationInHierarchy(READONLY); + fromTypeTree.replaceAnnotation(mainBound); } - - // for FBC quals - Set bound = this.getTypeDeclarationBounds(fromTypeTree.getUnderlyingType()); - fromTypeTree.addMissingAnnotations(bound); return fromTypeTree; } @@ -488,6 +510,12 @@ public Void visitVariable(VariableTree node, AnnotatedTypeMirror annotatedTypeMi // PICOTypeUtil.applyImmutableToEnumAndEnumConstant(annotatedTypeMirror); return super.visitVariable(node, annotatedTypeMirror); } + + @Override + public Void visitBinary(BinaryTree node, AnnotatedTypeMirror type) { + type.replaceAnnotation(IMMUTABLE); + return null; + } } /**Type Annotators*/ diff --git a/src/main/java/pico/typecheck/PICOTransfer.java b/src/main/java/pico/typecheck/PICOTransfer.java index db53c31..ffa72df 100644 --- a/src/main/java/pico/typecheck/PICOTransfer.java +++ b/src/main/java/pico/typecheck/PICOTransfer.java @@ -1,7 +1,5 @@ package pico.typecheck; -import com.sun.source.tree.ClassTree; -import com.sun.source.tree.MethodTree; import com.sun.source.tree.VariableTree; import org.checkerframework.checker.initialization.InitializationTransfer; import org.checkerframework.dataflow.analysis.RegularTransferResult; @@ -9,7 +7,6 @@ import org.checkerframework.dataflow.analysis.TransferResult; import org.checkerframework.dataflow.cfg.node.AssignmentNode; import org.checkerframework.dataflow.cfg.node.NullLiteralNode; -import org.checkerframework.framework.type.AnnotatedTypeFactory; import org.checkerframework.javacutil.TreeUtils; import javax.lang.model.element.VariableElement; @@ -41,9 +38,4 @@ public TransferResult visitAssignment(AssignmentNode n, Tr TransferResult result = super.visitAssignment(n, in); return result; } - - @Override - protected void addFieldValues(PICOStore info, AnnotatedTypeFactory factory, ClassTree classTree, MethodTree methodTree) { - return; - } } diff --git a/src/main/java/pico/typecheck/PICOValidator.java b/src/main/java/pico/typecheck/PICOValidator.java index b651ad8..e5b697f 100644 --- a/src/main/java/pico/typecheck/PICOValidator.java +++ b/src/main/java/pico/typecheck/PICOValidator.java @@ -18,7 +18,6 @@ import pico.common.PICOTypeUtil; import javax.lang.model.element.AnnotationMirror; -import javax.lang.model.element.Element; import javax.lang.model.element.ElementKind; import javax.lang.model.element.VariableElement; diff --git a/src/main/java/pico/typecheck/PICOVisitor.java b/src/main/java/pico/typecheck/PICOVisitor.java index 071746b..40b7924 100644 --- a/src/main/java/pico/typecheck/PICOVisitor.java +++ b/src/main/java/pico/typecheck/PICOVisitor.java @@ -158,7 +158,7 @@ private boolean isAdaptedSubtype(AnnotationMirror lhs, AnnotationMirror rhs) { @Override protected void commonAssignmentCheck( - Tree varTree, ExpressionTree valueExp, String errorKey) { + Tree varTree, ExpressionTree valueExp, String errorKey, Object... extraArgs) { AnnotatedTypeMirror var = atypeFactory.getAnnotatedTypeLhs(varTree); assert var != null : "no variable found for tree: " + varTree; @@ -590,7 +590,7 @@ public void processClassTree(ClassTree node) { @Override protected void checkThisOrSuperConstructorCall( MethodInvocationTree superCall, @CompilerMessageKey String errorKey) { - MethodTree enclosingMethod = visitorState.getMethodTree(); + MethodTree enclosingMethod = methodTree; AnnotatedTypeMirror superType = atypeFactory.getAnnotatedType(superCall); AnnotatedExecutableType constructorType = atypeFactory.getAnnotatedType(enclosingMethod); AnnotationMirror superTypeMirror = superType.getAnnotationInHierarchy(READONLY); @@ -631,7 +631,7 @@ protected boolean isTypeCastSafe(AnnotatedTypeMirror castType, AnnotatedTypeMirr @Override protected void commonAssignmentCheck(AnnotatedTypeMirror varType, AnnotatedTypeMirror valueType, Tree valueTree, - String errorKey) { + String errorKey, Object... extraArgs) { // TODO: WORKAROUND: anonymous class handling if (TypesUtils.isAnonymous(valueType.getUnderlyingType())) { AnnotatedTypeMirror newValueType = varType.deepCopy(); diff --git a/src/main/java/qual/Immutable.java b/src/main/java/qual/Immutable.java index b5e542b..2e79896 100644 --- a/src/main/java/qual/Immutable.java +++ b/src/main/java/qual/Immutable.java @@ -6,6 +6,7 @@ import org.checkerframework.framework.qual.SubtypeOf; import org.checkerframework.framework.qual.TypeKind; +import org.checkerframework.framework.qual.UpperBoundFor; import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; @@ -24,4 +25,14 @@ typeKinds = { TypeKind.INT, TypeKind.BYTE, TypeKind.SHORT, TypeKind.BOOLEAN, TypeKind.LONG, TypeKind.CHAR, TypeKind.FLOAT, TypeKind.DOUBLE }) @QualifierForLiterals({ LiteralKind.PRIMITIVE, LiteralKind.STRING}) +@UpperBoundFor( + typeKinds = { + TypeKind.INT, TypeKind.BYTE, TypeKind.SHORT, TypeKind.BOOLEAN, + TypeKind.LONG, TypeKind.CHAR, TypeKind.FLOAT, TypeKind.DOUBLE + }, + types = { + Enum.class, String.class, Double.class, Boolean.class, Byte.class, + Character.class, Float.class, Integer.class, Long.class, Short.class, Number.class, + BigDecimal.class, BigInteger.class + }) public @interface Immutable {} diff --git a/testinput/typecheck/CompatabilityBEI1.java b/testinput/typecheck/CompatabilityBEI1.java index 7f079b6..699310a 100644 --- a/testinput/typecheck/CompatabilityBEI1.java +++ b/testinput/typecheck/CompatabilityBEI1.java @@ -35,7 +35,6 @@ class I implements @Mutable Cloneable {} // :: error: (declaration.inconsistent.with.implements.clause) @Mutable class J implements @Immutable Cloneable {} -// :: error: (declaration.inconsistent.with.implements.clause) @Mutable class K implements @ReceiverDependantMutable Cloneable {} // :: error: (declaration.inconsistent.with.extends.clause) @@ -43,7 +42,6 @@ class I implements @Mutable Cloneable {} @Immutable class M extends @Immutable Object {} -// :: error: (declaration.inconsistent.with.extends.clause) @Immutable class N extends @ReceiverDependantMutable Object {} abstract class O implements CharSequence {} diff --git a/testinput/typecheck/CompatibilityBEI2.java b/testinput/typecheck/CompatibilityBEI2.java index 4014426..5b06687 100644 --- a/testinput/typecheck/CompatibilityBEI2.java +++ b/testinput/typecheck/CompatibilityBEI2.java @@ -28,7 +28,6 @@ abstract class I implements @Mutable List<@Immutable Object> {} // :: error: (declaration.inconsistent.with.implements.clause) @Mutable abstract class J implements @Immutable List<@Immutable Object> {} -// :: error: (declaration.inconsistent.with.implements.clause) @Mutable abstract class K implements @ReceiverDependantMutable List<@Immutable Object> {} // :: error: (declaration.inconsistent.with.extends.clause) @@ -36,7 +35,6 @@ abstract class I implements @Mutable List<@Immutable Object> {} @Immutable class M extends @Immutable ArrayList<@Immutable Object> {} -// :: error: (declaration.inconsistent.with.extends.clause) @Immutable class N extends @ReceiverDependantMutable ArrayList<@Immutable Object> {} abstract class O implements CharSequence {} diff --git a/testinput/typecheck/DateCell.java b/testinput/typecheck/DateCell.java index 1786b6a..a9f19b4 100644 --- a/testinput/typecheck/DateCell.java +++ b/testinput/typecheck/DateCell.java @@ -7,9 +7,9 @@ import java.lang.SuppressWarnings; import java.util.Date; -// :: error: (initialization.fields.uninitialized) @ReceiverDependantMutable public class DateCell { + // :: error: (initialization.field.uninitialized) @ReceiverDependantMutable Date date; @ReceiverDependantMutable Date getDate(@ReceiverDependantMutable DateCell this) { diff --git a/testinput/typecheck/DateCell2.java b/testinput/typecheck/DateCell2.java index 2bb5985..28eb674 100644 --- a/testinput/typecheck/DateCell2.java +++ b/testinput/typecheck/DateCell2.java @@ -7,8 +7,8 @@ import java.util.Date; -// :: error: (initialization.fields.uninitialized) @ReceiverDependantMutable public class DateCell2 { + // :: error: (initialization.field.uninitialized) @Immutable Date imdate; @Immutable Date getImmutableDate(@PolyMutable DateCell2 this) { diff --git a/testinput/typecheck/ForbidAssignmentCase.java b/testinput/typecheck/ForbidAssignmentCase.java index 69f6557..63f425c 100644 --- a/testinput/typecheck/ForbidAssignmentCase.java +++ b/testinput/typecheck/ForbidAssignmentCase.java @@ -36,6 +36,7 @@ static void ImmutableObjectCaptureMutableObject() { // But allow below: ro.f = new @Immutable Object(); } + static void ImmutableObjectGetMutableAlias() { @Mutable ForbidAssignmentCase mo = new @Mutable ForbidAssignmentCase(); @Readonly ForbidAssignmentCase ro = mo; diff --git a/testinput/typecheck/ImplicitAppliesToMethodReceiver.java b/testinput/typecheck/ImplicitAppliesToMethodReceiver.java index 86f523e..7e85fe8 100644 --- a/testinput/typecheck/ImplicitAppliesToMethodReceiver.java +++ b/testinput/typecheck/ImplicitAppliesToMethodReceiver.java @@ -5,6 +5,6 @@ public class ImplicitAppliesToMethodReceiver { void foo() { - double delta = new Double(1.0).doubleValue(); + double delta = Double.valueOf(1.0); } } diff --git a/testinput/typecheck/PolyMutableOnConstructorParameters.java b/testinput/typecheck/PolyMutableOnConstructorParameters.java index d8f774d..a9e8842 100644 --- a/testinput/typecheck/PolyMutableOnConstructorParameters.java +++ b/testinput/typecheck/PolyMutableOnConstructorParameters.java @@ -4,7 +4,7 @@ import qual.PolyMutable; @Immutable -public class PolyMutableOnConstructorParameters { +public class PolyMutableOnConstructorParameters { @Immutable PolyMutableOnConstructorParameters(@PolyMutable Object o) { } diff --git a/testinput/typecheck/RDMBug.java b/testinput/typecheck/RDMBug.java index 87c5705..897491f 100644 --- a/testinput/typecheck/RDMBug.java +++ b/testinput/typecheck/RDMBug.java @@ -5,9 +5,10 @@ import qual.Mutable; import qual.Readonly; -// :: error: (initialization.fields.uninitialized) @Immutable class RDMBug { + // :: error: (initialization.field.uninitialized) @Mutable Object o; + // :: error: (initialization.field.uninitialized) @Readonly Object o2; void foo(@Immutable RDMBug this) { // :: error: (illegal.field.write) diff --git a/testinput/typecheck/RDMFieldInst.java b/testinput/typecheck/RDMFieldInst.java index 62b08f7..95bdda5 100644 --- a/testinput/typecheck/RDMFieldInst.java +++ b/testinput/typecheck/RDMFieldInst.java @@ -11,9 +11,8 @@ private static class ImmutableBox {} private static class RDMBox {} @Immutable - // :: error: (initialization.fields.uninitialized) private static class ImmutableClass { - // :: error: (type.invalid.annotations.on.use) + // :: error: (type.invalid.annotations.on.use) :: error: (initialization.field.uninitialized) @ReceiverDependantMutable MutableBox mutableBoxInRDM; } From c6dcc1ba840c2bf32d3812227d5a6d238c2ee0a3 Mon Sep 17 00:00:00 2001 From: Haifeng Shi Date: Sun, 17 Jul 2022 16:59:36 -0400 Subject: [PATCH 142/144] resolve new array problems --- .../typecheck/PICOAnnotatedTypeFactory.java | 90 ++++--------------- 1 file changed, 16 insertions(+), 74 deletions(-) diff --git a/src/main/java/pico/typecheck/PICOAnnotatedTypeFactory.java b/src/main/java/pico/typecheck/PICOAnnotatedTypeFactory.java index e373d4e..574c1f1 100644 --- a/src/main/java/pico/typecheck/PICOAnnotatedTypeFactory.java +++ b/src/main/java/pico/typecheck/PICOAnnotatedTypeFactory.java @@ -299,79 +299,21 @@ public PICOPropagationTreeAnnotator(AnnotatedTypeFactory atypeFactory) { } // // // TODO This is very ugly. Why is array component type from lhs propagates to rhs?! -// @Override -// public Void visitNewArray(NewArrayTree tree, AnnotatedTypeMirror type) { -// // Below is copied from super -// assert type.getKind() == TypeKind.ARRAY -// : "PropagationTreeAnnotator.visitNewArray: should be an array type"; -// -// AnnotatedTypeMirror componentType = ((AnnotatedTypeMirror.AnnotatedArrayType) type).getComponentType(); -// -// Collection prev = null; -// if (tree.getInitializers() != null && tree.getInitializers().size() != 0) { -// // We have initializers, either with or without an array type. -// -// for (ExpressionTree init : tree.getInitializers()) { -// AnnotatedTypeMirror initType = atypeFactory.getAnnotatedType(init); -// // initType might be a typeVariable, so use effectiveAnnotations. -// Collection annos = initType.getEffectiveAnnotations(); -// -// prev = (prev == null) ? annos : atypeFactory.getQualifierHierarchy().leastUpperBounds(prev, annos); -// } -// } else { -// prev = componentType.getAnnotations(); -// } -// -// assert prev != null -// : "PropagationTreeAnnotator.visitNewArray: violated assumption about qualifiers"; -// -// Pair context = -// atypeFactory.getVisitorState().getAssignmentContext(); -// Collection post; -// -// if (context != null -// && context.second != null -// && context.second instanceof AnnotatedTypeMirror.AnnotatedArrayType) { -// AnnotatedTypeMirror contextComponentType = -// ((AnnotatedTypeMirror.AnnotatedArrayType) context.second).getComponentType(); -// // Only compare the qualifiers that existed in the array type -// // Defaulting wasn't performed yet, so prev might have fewer qualifiers than -// // contextComponentType, which would cause a failure. -// // TODO: better solution? -// boolean prevIsSubtype = true; -// for (AnnotationMirror am : prev) { -// if (contextComponentType.isAnnotatedInHierarchy(am) -// && !atypeFactory.getQualifierHierarchy().isSubtype( -// am, contextComponentType.getAnnotationInHierarchy(am))) { -// prevIsSubtype = false; -// } -// } -// // TODO: checking conformance of component kinds is a basic sanity check -// // It fails for array initializer expressions. Those should be handled nicer. -// if (contextComponentType.getKind() == componentType.getKind() -// && (prev.isEmpty() -// || (!contextComponentType.getAnnotations().isEmpty() -// && prevIsSubtype))) { -// post = contextComponentType.getAnnotations(); -// } else { -// // The type of the array initializers is incompatible with the -// // context type! -// // Somebody else will complain. -// post = prev; -// } -// } else { -// // No context is available - simply use what we have. -// post = prev; -// } -// -// // Below line is the only difference from super implementation -// applyImmutableIfImplicitlyImmutable(componentType); -// // Above line is the only difference from super implementation -// componentType.addMissingAnnotations(post); -// -// return null; -// // Above is copied from super -// } + @Override + public Void visitNewArray(NewArrayTree tree, AnnotatedTypeMirror type) { + AnnotatedTypeMirror componentType = ((AnnotatedTypeMirror.AnnotatedArrayType) type).getComponentType(); + boolean noExplicitATM = false; + if (!componentType.hasAnnotation(RECEIVER_DEPENDANT_MUTABLE)) { + noExplicitATM = true; + } + super.visitNewArray(tree, type); + // if new explicit anno before, but RDM after, the RDM must come from the type declaration bound + // however, for type has declaration bound as RDM, its default use is mutable. + if (noExplicitATM && componentType.hasAnnotation(RECEIVER_DEPENDANT_MUTABLE)) { + componentType.replaceAnnotation(MUTABLE); + } + return null; + } // // /**Add immutable to the result type of a binary operation if the result type is implicitly immutable*/ // @Override @@ -455,7 +397,7 @@ public AnnotationMirror getTypeDeclarationBoundForMutability(TypeMirror type) { return IMMUTABLE; } if (type.getKind() == TypeKind.ARRAY) { - return READONLY; // if decided to use vpa for array, return RDM. + return RECEIVER_DEPENDANT_MUTABLE; // if decided to use vpa for array, return RDM. } // IMMUTABLE for enum w/o decl anno From fa7f0eb603fe865b12fb6eb2031c1ab40d3c2181 Mon Sep 17 00:00:00 2001 From: Haifeng Shi Date: Tue, 20 Sep 2022 21:43:28 -0400 Subject: [PATCH 143/144] override isSafeDowncast in PICO --- src/main/java/pico/typecheck/PICOVisitor.java | 25 ++++++++++--------- 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/src/main/java/pico/typecheck/PICOVisitor.java b/src/main/java/pico/typecheck/PICOVisitor.java index 40b7924..5957aba 100644 --- a/src/main/java/pico/typecheck/PICOVisitor.java +++ b/src/main/java/pico/typecheck/PICOVisitor.java @@ -605,27 +605,28 @@ protected void checkThisOrSuperConstructorCall( } @Override - protected boolean isTypeCastSafe(AnnotatedTypeMirror castType, AnnotatedTypeMirror exprType) { + protected CastSafeKind isSafeDowncast( + AnnotatedTypeMirror castType, AnnotatedTypeMirror exprType) { + Set castAnnos = castType.getEffectiveAnnotations(); + Set exprAnnos = exprType.getEffectiveAnnotations(); QualifierHierarchy qualifierHierarchy = atypeFactory.getQualifierHierarchy(); + if (!qualifierHierarchy.isComparable(castAnnos, exprAnnos)) { + return CastSafeKind.NOT_DOWNCAST; + } + final TypeKind castTypeKind = castType.getKind(); + if (castTypeKind == TypeKind.DECLARED) { - // Don't issue an error if the mutability annotations are equivalent to the qualifier upper bound - // of the type. - // BaseTypeVisitor#isTypeCastSafe is not used, to be consistent with inference which only have mutability qualifiers - // if inference is supporting FBC in the future, this overridden method can be removed. AnnotatedDeclaredType castDeclared = (AnnotatedDeclaredType) castType; - - AnnotationMirror bound = qualifierHierarchy.findAnnotationInHierarchy( - atypeFactory.getTypeDeclarationBounds(castDeclared.getUnderlyingType()), READONLY); - assert bound != null; + AnnotationMirror bound = + qualifierHierarchy.findAnnotationInHierarchy(atypeFactory.getTypeDeclarationBounds(castDeclared.getUnderlyingType()), READONLY); if (AnnotationUtils.areSame(castDeclared.getAnnotationInHierarchy(READONLY), bound)) { - return true; + return CastSafeKind.SAFE; } } - - return super.isTypeCastSafe(castType, exprType); + return CastSafeKind.WARNING; } @Override From d73d3c35cc202a85ffd7a2fc9993b408abe8b657 Mon Sep 17 00:00:00 2001 From: Haifeng Shi Date: Wed, 21 Sep 2022 15:22:56 -0400 Subject: [PATCH 144/144] fix java.util.MissingFormatArgumentException in commonAssignmentCheck --- src/main/java/pico/typecheck/PICOVisitor.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/pico/typecheck/PICOVisitor.java b/src/main/java/pico/typecheck/PICOVisitor.java index 5957aba..b79d865 100644 --- a/src/main/java/pico/typecheck/PICOVisitor.java +++ b/src/main/java/pico/typecheck/PICOVisitor.java @@ -639,7 +639,7 @@ protected void commonAssignmentCheck(AnnotatedTypeMirror varType, newValueType.replaceAnnotation(valueType.getAnnotationInHierarchy(READONLY)); valueType = newValueType; } - super.commonAssignmentCheck(varType, valueType, valueTree, errorKey); + super.commonAssignmentCheck(varType, valueType, valueTree, errorKey, extraArgs); } }