diff --git a/.gitignore b/.gitignore index 57d0bc5..f3ca3f0 100644 --- a/.gitignore +++ b/.gitignore @@ -37,6 +37,10 @@ manual-tests/annotated manual-tests/inference manual-tests/typecheck manual-tests/insertionTest +annotated/ +unsatConstraints.txt +statistics.txt +solutions.txt # temp files manual-tests/unused* diff --git a/benchmarks/projects.yml b/benchmarks/projects.yml index f311ca0..2aba807 100644 --- a/benchmarks/projects.yml +++ b/benchmarks/projects.yml @@ -22,6 +22,11 @@ projects: build: mvn -B -DskipTests compile clean: mvn -B clean + commons-math: + giturl: https://github.com/txiang61-benchmarks/commons-math.git + build: mvn install -Drat.numUnapprovedLicenses=100 + clean: mvn -B clean + # Converted to use units # TODO: revert to unsat version jblas: diff --git a/scripts/run-units-infer.sh b/scripts/run-units-infer.sh index 78cc508..e4c164c 100755 --- a/scripts/run-units-infer.sh +++ b/scripts/run-units-infer.sh @@ -14,6 +14,9 @@ export PATH=$AFU/scripts:$PATH CHECKER=units.UnitsChecker SOLVER=units.solvers.backend.UnitsSolverEngine +DEBUG_SOLVER=checkers.inference.solver.DebugSolver +# SOLVER="$DEBUG_SOLVER" + if [ -n "$1" ] && [ $1 = "GJE" ]; then SOLVERARGS=solver=GJE,collectStatistics=true,writeSolutions=true,noAppend=true elif [ -n "$1" ] && [ $1 = "true" ]; then diff --git a/src/units/JavaAWT.astub b/src/units/JavaAWT.astub new file mode 100644 index 0000000..93a1cb2 --- /dev/null +++ b/src/units/JavaAWT.astub @@ -0,0 +1,9 @@ +import units.qual.*; + +package java.awt; + +abstract class Graphics2D { + abstract void rotate(@rad double theta); + abstract void rotate(@rad double theta, double x, double y); +} + diff --git a/src/units/JavaAWTGeom.astub b/src/units/JavaAWTGeom.astub new file mode 100644 index 0000000..014f982 --- /dev/null +++ b/src/units/JavaAWTGeom.astub @@ -0,0 +1,12 @@ +import units.qual.*; + +package java.awt.geom; + +class AffineTransform implements Cloneable, Serializable { + static AffineTransform getRotateInstance(@rad double theta); + static AffineTransform getRotateInstance(@rad double theta, double anchorx, double anchory); + void rotate(@rad double theta); + void rotate(@rad double theta, double x, double y); + void setToRotation(@rad double theta); + void setToRotation(@rad double theta, double anchorx, double anchory); +} \ No newline at end of file diff --git a/src/units/JavaLang.astub b/src/units/JavaLang.astub index 055fe6d..41eb062 100644 --- a/src/units/JavaLang.astub +++ b/src/units/JavaLang.astub @@ -2,6 +2,9 @@ import units.qual.*; package java.lang; +@UnitsBottom class Object{ +} + class System { static @ms long currentTimeMillis(); static @ns long nanoTime(); @@ -22,3 +25,12 @@ class String implements Serializable, Comparable, CharSequence { static String valueOf(@UnknownUnits float arg0); static String valueOf(@UnknownUnits double arg0); } + +class StringBuilder implements Serializable, CharSequence { + StringBuilder append(@UnknownUnits int i); + StringBuilder append(@UnknownUnits long lng); + StringBuilder append(@UnknownUnits float f); + StringBuilder append(@UnknownUnits double b); + StringBuilder append(@UnknownUnits char c); +} + diff --git a/src/units/JavaText.astub b/src/units/JavaText.astub new file mode 100644 index 0000000..039e3d0 --- /dev/null +++ b/src/units/JavaText.astub @@ -0,0 +1,10 @@ +import units.qual.*; + +package java.text; + +class NumberFormat { + String format(@UnknownUnits long number); + abstract StrinBuffer format(@UnknownUnits long number, StringBuffer toAppendTo, FieldPosition pos); + String format(@UnknownUnits double number); + abstract StrinBuffer format(@UnknownUnits double number, StringBuffer toAppendTo, FieldPosition pos); +} diff --git a/src/units/JavaUtilConcurrent.astub b/src/units/JavaUtilConcurrent.astub index 2e907d9..d80a05d 100644 --- a/src/units/JavaUtilConcurrent.astub +++ b/src/units/JavaUtilConcurrent.astub @@ -2,6 +2,7 @@ import units.qual.*; package java.util.concurrent; +@UnitsBottom enum TimeUnit { @ns NANOSECONDS, @us MICROSECONDS, @@ -14,23 +15,23 @@ enum TimeUnit { // For example, to convert 10 minutes to milliseconds, use: // TimeUnit.MILLISECONDS.convert(10L, TimeUnit.MINUTES) // TODO: enforce source value and unit are equal - @PolyUnit long convert(@PolyUnit TimeUnit this, @UnknownUnits long sourceDuration, + @RDU long convert(@UnknownUnits TimeUnit this, @UnknownUnits long sourceDuration, @UnknownUnits TimeUnit sourceUnit); // TODO: add dimensional bound on duration to be a time value - @ns long toNanos(@UnknownUnits long duration); + @ns long toNanos(@UnknownUnits TimeUnit this, @RDU long duration); - @us long toMicros(@UnknownUnits long duration); + @us long toMicros(@UnknownUnits TimeUnit this, @RDU long duration); - @ms long toMillis(@UnknownUnits long duration); + @ms long toMillis(@UnknownUnits TimeUnit this, @RDU long duration); - @s long toSeconds(@UnknownUnits long duration); + @s long toSeconds(@UnknownUnits TimeUnit this, @RDU long duration); - long toMinutes(@UnknownUnits long duration); + long toMinutes(@UnknownUnits TimeUnit this, @RDU long duration); - long toHours(@UnknownUnits long duration); + long toHours(@UnknownUnits TimeUnit this, @RDU long duration); - long toDays(@UnknownUnits long duration); + long toDays(@UnknownUnits TimeUnit this, @RDU long duration); // TODO: enforce "this" has same unit as "timeout" void timedWait(@UnknownUnits TimeUnit this, Object obj, @UnknownUnits long timeout); diff --git a/src/units/UnitsAnnotatedTypeFactory.java b/src/units/UnitsAnnotatedTypeFactory.java index cb221c3..4764ccc 100644 --- a/src/units/UnitsAnnotatedTypeFactory.java +++ b/src/units/UnitsAnnotatedTypeFactory.java @@ -19,7 +19,9 @@ import org.checkerframework.framework.type.AnnotationClassLoader; import org.checkerframework.framework.type.DefaultAnnotatedTypeFormatter; import org.checkerframework.framework.type.QualifierHierarchy; +import org.checkerframework.framework.type.ViewpointAdapter; import org.checkerframework.framework.type.treeannotator.ListTreeAnnotator; +import org.checkerframework.framework.type.treeannotator.LiteralTreeAnnotator; import org.checkerframework.framework.type.treeannotator.PropagationTreeAnnotator; import org.checkerframework.framework.type.treeannotator.TreeAnnotator; import org.checkerframework.framework.type.typeannotator.DefaultForTypeAnnotator; @@ -55,6 +57,11 @@ protected AnnotationClassLoader createAnnotationClassLoader() { return new UnitsAnnotationClassLoader(checker); } + @Override + protected ViewpointAdapter createViewpointAdapter() { + return new UnitsViewpointAdapter(this); + } + @Override protected Set> createSupportedTypeQualifiers() { // get all the loaded annotations @@ -63,7 +70,6 @@ protected Set> createSupportedTypeQualifiers() { // // load all the external units // loadAllExternalUnits(); - // // // copy all loaded external Units to qual set // qualSet.addAll(externalQualsMap.values()); @@ -121,7 +127,7 @@ public boolean isSupportedQualifier(AnnotationMirror anno) { if (AnnotationUtils.areSameByClass(anno, UnitsRep.class)) { return unitsRepUtils.hasAllBaseUnits(anno); } - // Anno is PolyAll, PolyUnit + // Anno is PolyUnit return AnnotationUtils.containsSame(this.getQualifierHierarchy().getTypeQualifiers(), anno); } @@ -145,28 +151,6 @@ protected void addCheckedCodeDefaults(QualifierDefaults defs) { defs.addCheckedCodeDefault(unitsRepUtils.TOP, TypeUseLocation.LOCAL_VARIABLE); } - // Note: remember to use - // --cfArgs="-AuseDefaultsForUncheckedCode=source,bytecode" in cmd line option - // -AuseDefaultsForUncheckedCode=bytecode // uses those defaults in byte code - // -AuseDefaultsForUncheckedCode=source,bytecode // also uses those defaults in - // source code - @Override - protected void addUncheckedCodeDefaults(QualifierDefaults defs) { - super.addUncheckedCodeDefaults(defs); - - // experiment with: - // This seems to have no effect thus far in the constraints generated in inference - // top param, receiver, bot return for inference, explain unsat - defs.addUncheckedCodeDefault(unitsRepUtils.TOP, TypeUseLocation.RECEIVER); - defs.addUncheckedCodeDefault(unitsRepUtils.TOP, TypeUseLocation.PARAMETER); - defs.addUncheckedCodeDefault(unitsRepUtils.BOTTOM, TypeUseLocation.RETURN); - - // bot param, top return for tightest api restriction?? - - // dimensionless is default for all other locations - // defs.addUncheckedCodeDefault(unitsRepUtils.DIMENSIONLESS, TypeUseLocation.OTHERWISE); - } - @Override public QualifierHierarchy createQualifierHierarchy(MultiGraphFactory factory) { return new UnitsQualifierHierarchy(factory); @@ -261,6 +245,7 @@ protected void finish( // Update tops tops.remove(unitsRepUtils.RAWUNITSREP); + tops.remove(unitsRepUtils.RECEIVER_DEPENDANT_UNIT); tops.add(unitsRepUtils.TOP); // System.err.println(" === Typecheck ATF "); @@ -298,7 +283,7 @@ public boolean isSubtype(AnnotationMirror subAnno, AnnotationMirror superAnno) { return true; } - // Case: @PolyAll and @PolyUnit are treated as @UnknownUnits + // Case: @PolyUnit are treated as @UnknownUnits if (AnnotationUtils.areSame(subAnno, unitsRepUtils.POLYUNIT)) { return isSubtype(unitsRepUtils.TOP, superAnno); } @@ -306,6 +291,14 @@ public boolean isSubtype(AnnotationMirror subAnno, AnnotationMirror superAnno) { return true; } + // Case: @RDU shouldn't appear. throw error? + if (AnnotationUtils.areSame(subAnno, unitsRepUtils.RECEIVER_DEPENDANT_UNIT)) { + return isSubtype(unitsRepUtils.TOP, superAnno); + } + if (AnnotationUtils.areSame(superAnno, unitsRepUtils.RECEIVER_DEPENDANT_UNIT)) { + return true; + } + // Case: @UnitsRep(x) <: @UnitsRep(y) if (AnnotationUtils.areSameByClass(subAnno, UnitsRep.class) && AnnotationUtils.areSameByClass(superAnno, UnitsRep.class)) { @@ -334,13 +327,18 @@ public boolean isSubtype(AnnotationMirror subAnno, AnnotationMirror superAnno) { @Override public TreeAnnotator createTreeAnnotator() { return new ListTreeAnnotator( - new UnitsTypecheckLiteralTreeAnnotator(), new UnitsPropagationTreeAnnotator()); + new UnitsLiteralTreeAnnotator(this), new UnitsPropagationTreeAnnotator()); } - protected final class UnitsTypecheckLiteralTreeAnnotator extends UnitsLiteralTreeAnnotator { + protected final class UnitsLiteralTreeAnnotator extends LiteralTreeAnnotator { // Programmatically set the qualifier implicits - public UnitsTypecheckLiteralTreeAnnotator() { - super(UnitsAnnotatedTypeFactory.this); + public UnitsLiteralTreeAnnotator(AnnotatedTypeFactory atf) { + super(atf); + // set BOTTOM as the literal qualifier for null literals + addLiteralKind(LiteralKind.NULL, unitsRepUtils.BOTTOM); + addLiteralKind(LiteralKind.STRING, unitsRepUtils.DIMENSIONLESS); + addLiteralKind(LiteralKind.CHAR, unitsRepUtils.DIMENSIONLESS); + addLiteralKind(LiteralKind.BOOLEAN, unitsRepUtils.DIMENSIONLESS); // in type checking mode, we also set dimensionless for the number literals addLiteralKind(LiteralKind.INT, unitsRepUtils.DIMENSIONLESS); addLiteralKind(LiteralKind.LONG, unitsRepUtils.DIMENSIONLESS); diff --git a/src/units/UnitsChecker.java b/src/units/UnitsChecker.java index 7d2e1d7..44b2c2e 100644 --- a/src/units/UnitsChecker.java +++ b/src/units/UnitsChecker.java @@ -19,8 +19,10 @@ "JavaMathTrig.astub", "JavaThread.astub", "JavaUtil.astub", + "JavaText.astub", + "JavaAWT.astub", // for josm "JavaUtilConcurrent.astub", // for ode4j, not yet annotated for hombucha - "ExperimentsJavaAwtGeomAffineTransform.astub", // for imgscalr experiment + // "ExperimentsJavaAwtGeomAffineTransform.astub", // for imgscalr experiment "ExperimentsSunMiscUnsafe.astub", // for JLargeArrays }) public class UnitsChecker extends BaseInferrableChecker { diff --git a/src/units/UnitsInferenceAnnotatedTypeFactory.java b/src/units/UnitsInferenceAnnotatedTypeFactory.java index 484ef03..af531cc 100644 --- a/src/units/UnitsInferenceAnnotatedTypeFactory.java +++ b/src/units/UnitsInferenceAnnotatedTypeFactory.java @@ -2,6 +2,7 @@ import checkers.inference.InferenceAnnotatedTypeFactory; import checkers.inference.InferenceChecker; +import checkers.inference.InferenceMain; import checkers.inference.InferenceQualifierHierarchy; import checkers.inference.InferenceTreeAnnotator; import checkers.inference.InferrableChecker; @@ -12,14 +13,18 @@ import checkers.inference.model.ConstraintManager; import checkers.inference.model.Slot; import checkers.inference.model.VariableSlot; +import checkers.inference.qual.VarAnnot; +import checkers.inference.util.InferenceViewpointAdapter; import com.sun.source.tree.BinaryTree; import com.sun.source.tree.ExpressionTree; import com.sun.source.tree.IdentifierTree; +import com.sun.source.tree.LiteralTree; import com.sun.source.tree.MemberSelectTree; import com.sun.source.tree.MethodInvocationTree; import com.sun.source.tree.NewClassTree; import com.sun.source.tree.Tree; import java.lang.annotation.Annotation; +import java.util.Collection; import java.util.HashSet; import java.util.Map; import java.util.Set; @@ -40,7 +45,9 @@ import org.checkerframework.framework.util.AnnotatedTypes; import org.checkerframework.framework.util.AnnotationFormatter; import org.checkerframework.framework.util.MultiGraphQualifierHierarchy.MultiGraphFactory; +import org.checkerframework.javacutil.AnnotationBuilder; 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; @@ -89,6 +96,11 @@ protected AnnotationClassLoader createAnnotationClassLoader() { return new UnitsAnnotationClassLoader(checker); } + @Override + protected InferenceViewpointAdapter createViewpointAdapter() { + return new UnitsInferenceViewpointAdapter(this); + } + // In Inference ATF, this returns the set of real qualifiers @Override protected Set> createSupportedTypeQualifiers() { @@ -212,6 +224,71 @@ protected void finish( bottoms [@checkers.inference.qual.VarAnnot] */ } + + @Override + public Set leastUpperBounds( + Collection annos1, + Collection annos2) { + if (InferenceMain.isHackMode(annos1.size() != annos2.size())) { + Set result = AnnotationUtils.createAnnotationSet(); + for (AnnotationMirror a1 : annos1) { + for (AnnotationMirror a2 : annos2) { + AnnotationMirror lub = leastUpperBound(a1, a2); + if (lub != null) { + result.add(lub); + } + } + } + + return result; + } + + if (annos1.isEmpty() || annos2.isEmpty()) { + throw new BugInCF( + "QualifierHierarchy.leastUpperBounds: tried to determine LUB with empty sets"); + } + + Set result = AnnotationUtils.createAnnotationSet(); + for (AnnotationMirror a1 : annos1) { + for (AnnotationMirror a2 : annos2) { + AnnotationMirror lub = leastUpperBound(a1, a2); + if (lub != null) { + result.add(lub); + } + } + } + + return result; + } + + // @Override + // public boolean isSubtype(AnnotationMirror subtype, AnnotationMirror supertype) { + // if (!isVarAnnot(subtype) && !isVarAnnot(supertype)) { + // // Case: All units <: Top + // if (AnnotationUtils.areSame(supertype, unitsRepUtils.TOP)) { + // return true; + // } + // // Case: Bottom <: All units + // if (AnnotationUtils.areSame(subtype, unitsRepUtils.BOTTOM)) { + // return true; + // } + // + // // Case: @UnitsRep(x) <: @UnitsRep(y) + // if (AnnotationUtils.areSameByClass(subtype, UnitsRep.class) + // && AnnotationUtils.areSameByClass(supertype, UnitsRep.class)) { + // return UnitsTypecheckUtils.unitsEqual(subtype, supertype); + // } + // } + // + // return super.isSubtype(subtype, supertype); + // } + } + + @Override + protected Set getDefaultTypeDeclarationBounds() { + Set top = new HashSet<>(); + top.add(unitsRepUtils.TOP); + return top; } @Override @@ -300,21 +377,10 @@ public void handleBinaryTree(AnnotatedTypeMirror atm, BinaryTree binaryTree) { @Override public TreeAnnotator createTreeAnnotator() { return new ListTreeAnnotator( - new UnitsInferenceLiteralTreeAnnotator(), new UnitsInferenceTreeAnnotator( this, realChecker, realTypeFactory, variableAnnotator, slotManager)); } - protected final class UnitsInferenceLiteralTreeAnnotator extends UnitsLiteralTreeAnnotator { - // Programmatically set the qualifier implicits - public UnitsInferenceLiteralTreeAnnotator() { - super(UnitsInferenceAnnotatedTypeFactory.this); - // in inference mode, we do not implicitly set dimensionless for the number - // literals as we want to treat them as polymorphic. A "cast" is inferred for - // each literal - } - } - private final class UnitsInferenceTreeAnnotator extends InferenceTreeAnnotator { // TODO: per design of InferenceTreeAnnotator, this code should be moved into // UnitsVariableAnnotator if it performs deep traversal @@ -388,10 +454,11 @@ private void generateVarSlotForStaticFinalConstants( && AnnotationUtils.areSame( ((ConstantSlot) slot).getValue(), unitsRepUtils.DIMENSIONLESS))) { - // Generate a fresh variable for inference + // Generate a fre sh variable for inference AnnotationLocation loc = VariableAnnotator.treeToLocation(atypeFactory, tree); VariableSlot varSlot = slotManager.createVariableSlot(loc); + atm.clearAnnotations(); atm.replaceAnnotation(slotManager.getAnnotation(varSlot)); } } @@ -518,6 +585,7 @@ public Void visitNewClass(NewClassTree newClassTree, AnnotatedTypeMirror atm) { // Replace the slot/annotation in the atm (callSiteReturnVarSlot) with the // varSlotForPolyReturn for upstream analysis + atm.clearAnnotations(); atm.replaceAnnotation(slotManager.getAnnotation(varSlotForPolyReturn)); } @@ -534,10 +602,44 @@ public Void visitMethodInvocation( variableAnnotator.getOrCreatePolyVar(methodInvocationTree); // disable insertion of polymorphic return variable slot varSlotForPolyReturn.setInsertable(false); + AnnotationBuilder ab = + new AnnotationBuilder(realTypeFactory.getProcessingEnv(), VarAnnot.class); + ab.setValue("value", varSlotForPolyReturn.getId()); + atm.clearAnnotations(); + atm.replaceAnnotation(ab.build()); } return null; } + + @Override + public Void visitLiteral(final LiteralTree tree, AnnotatedTypeMirror type) { + switch (tree.getKind()) { + case NULL_LITERAL: + replaceATM(type, unitsRepUtils.BOTTOM); + return null; + case CHAR_LITERAL: + replaceATM(type, unitsRepUtils.DIMENSIONLESS); + return null; + case BOOLEAN_LITERAL: + replaceATM(type, unitsRepUtils.DIMENSIONLESS); + return null; + case STRING_LITERAL: + replaceATM(type, unitsRepUtils.DIMENSIONLESS); + return null; + default: + return super.visitLiteral(tree, type); + } + } + + private void replaceATM(AnnotatedTypeMirror atm, AnnotationMirror am) { + final ConstantSlot cs = slotManager.createConstantSlot(am); + AnnotationBuilder ab = + new AnnotationBuilder(realTypeFactory.getProcessingEnv(), VarAnnot.class); + ab.setValue("value", cs.getId()); + atm.clearAnnotations(); + atm.replaceAnnotation(ab.build()); + } } // for use in AnnotatedTypeMirror.toString() diff --git a/src/units/UnitsInferenceViewpointAdapter.java b/src/units/UnitsInferenceViewpointAdapter.java new file mode 100644 index 0000000..fb1934f --- /dev/null +++ b/src/units/UnitsInferenceViewpointAdapter.java @@ -0,0 +1,45 @@ +package units; + +import checkers.inference.InferenceMain; +import checkers.inference.model.ConstantSlot; +import checkers.inference.model.Slot; +import checkers.inference.util.InferenceViewpointAdapter; +import javax.lang.model.element.AnnotationMirror; +import javax.lang.model.element.Element; +import org.checkerframework.framework.type.AnnotatedTypeFactory; +import org.checkerframework.framework.type.AnnotatedTypeMirror; +import org.checkerframework.javacutil.AnnotationUtils; +import units.representation.UnitsRepresentationUtils; + +public class UnitsInferenceViewpointAdapter extends InferenceViewpointAdapter { + + // static reference to the singleton instance + protected static UnitsRepresentationUtils unitsRepUtils; + + public UnitsInferenceViewpointAdapter(AnnotatedTypeFactory atypeFactory) { + super(atypeFactory); + unitsRepUtils = UnitsRepresentationUtils.getInstance(); + } + + @Override + protected boolean shouldAdaptMember(AnnotatedTypeMirror type, Element element) { + return false; + } + + @Override + protected AnnotationMirror combineAnnotationWithAnnotation( + AnnotationMirror receiverAnnotation, AnnotationMirror declaredAnnotation) { + if (InferenceMain.isHackMode(declaredAnnotation == null)) { + return unitsRepUtils.BOTTOM; + } + Slot declSlot = InferenceMain.getInstance().getSlotManager().getSlot(declaredAnnotation); + if (declSlot.isConstant()) { + ConstantSlot cs = (ConstantSlot) declSlot; + if (AnnotationUtils.areSame(cs.getValue(), unitsRepUtils.RECEIVER_DEPENDANT_UNIT)) { + return super.combineAnnotationWithAnnotation( + receiverAnnotation, declaredAnnotation); + } + } + return declaredAnnotation; + } +} diff --git a/src/units/UnitsLiteralTreeAnnotator.java b/src/units/UnitsLiteralTreeAnnotator.java deleted file mode 100644 index ef4cbec..0000000 --- a/src/units/UnitsLiteralTreeAnnotator.java +++ /dev/null @@ -1,26 +0,0 @@ -package units; - -import org.checkerframework.framework.qual.LiteralKind; -import org.checkerframework.framework.type.AnnotatedTypeFactory; -import org.checkerframework.framework.type.treeannotator.LiteralTreeAnnotator; -import units.representation.UnitsRepresentationUtils; - -/** Common base class for LiteralTreeAnnotator. */ -public abstract class UnitsLiteralTreeAnnotator extends LiteralTreeAnnotator { - - UnitsRepresentationUtils unitsRepUtils = UnitsRepresentationUtils.getInstance(); - - // Programmatically set the qualifier for literals - public UnitsLiteralTreeAnnotator(AnnotatedTypeFactory atf) { - super(atf); - - // set BOTTOM as the literal qualifier for null literals - addLiteralKind(LiteralKind.NULL, unitsRepUtils.BOTTOM); - addLiteralKind(LiteralKind.STRING, unitsRepUtils.DIMENSIONLESS); - addLiteralKind(LiteralKind.CHAR, unitsRepUtils.DIMENSIONLESS); - addLiteralKind(LiteralKind.BOOLEAN, unitsRepUtils.DIMENSIONLESS); - - // TODO: set BOTTOM as the literal qualifier for lower bounds? Its nice to - // infer a bound which is the current mode. - } -} diff --git a/src/units/UnitsViewpointAdapter.java b/src/units/UnitsViewpointAdapter.java new file mode 100644 index 0000000..dc9076b --- /dev/null +++ b/src/units/UnitsViewpointAdapter.java @@ -0,0 +1,39 @@ +package units; + +import javax.lang.model.element.AnnotationMirror; +import javax.lang.model.element.Element; +import org.checkerframework.framework.type.AbstractViewpointAdapter; +import org.checkerframework.framework.type.AnnotatedTypeFactory; +import org.checkerframework.framework.type.AnnotatedTypeMirror; +import org.checkerframework.javacutil.AnnotationUtils; +import units.representation.UnitsRepresentationUtils; + +public class UnitsViewpointAdapter extends AbstractViewpointAdapter { + + // static reference to the singleton instance + protected static UnitsRepresentationUtils unitsRepUtils; + + public UnitsViewpointAdapter(AnnotatedTypeFactory atypeFactory) { + super(atypeFactory); + unitsRepUtils = UnitsRepresentationUtils.getInstance(); + } + + @Override + protected boolean shouldAdaptMember(AnnotatedTypeMirror type, Element element) { + return false; + } + + @Override + protected AnnotationMirror extractAnnotationMirror(AnnotatedTypeMirror atm) { + return atm.getAnnotationInHierarchy(unitsRepUtils.TOP); + } + + @Override + protected AnnotationMirror combineAnnotationWithAnnotation( + AnnotationMirror receiverAnnotation, AnnotationMirror declaredAnnotation) { + if (AnnotationUtils.areSame(declaredAnnotation, unitsRepUtils.RECEIVER_DEPENDANT_UNIT)) { + return receiverAnnotation; + } + return declaredAnnotation; + } +} diff --git a/src/units/qual/RDU.java b/src/units/qual/RDU.java new file mode 100644 index 0000000..6be2671 --- /dev/null +++ b/src/units/qual/RDU.java @@ -0,0 +1,19 @@ +package units.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; +import org.checkerframework.framework.qual.SubtypeOf; + +/** + * UnitReceiverDependant is the top type of the type hierarchy. + * + * @checker_framework.manual #units-checker Units Checker + */ +@Documented +@SubtypeOf(UnitsRep.class) +@Retention(RetentionPolicy.RUNTIME) +@Target({ElementType.TYPE_USE, ElementType.TYPE_PARAMETER}) // ElementType.TYPE, +public @interface RDU {} diff --git a/src/units/representation/TypecheckUnit.java b/src/units/representation/TypecheckUnit.java index db35df7..6a45a05 100644 --- a/src/units/representation/TypecheckUnit.java +++ b/src/units/representation/TypecheckUnit.java @@ -11,6 +11,7 @@ public class TypecheckUnit { private boolean uu; private boolean ub; + private boolean rdu; private int prefixExponent; // Tree map maintaining sorted order on base unit names private final Map exponents; @@ -20,6 +21,8 @@ public TypecheckUnit() { uu = false; // default UU value is false ub = false; + // default RDU value is false + rdu = false; // default prefixExponent is 0 prefixExponent = 0; // default exponents are 0 @@ -35,6 +38,15 @@ public boolean isUnknownUnits() { return uu; } + public void setRDUnits(boolean val) { + rdu = val; + assert !(uu && ub); + } + + public boolean isRDUnits() { + return rdu; + } + public void setUnitsBottom(boolean val) { ub = val; assert !(uu && ub); diff --git a/src/units/representation/UnitsRepresentationUtils.java b/src/units/representation/UnitsRepresentationUtils.java index ed02991..a85e4f1 100644 --- a/src/units/representation/UnitsRepresentationUtils.java +++ b/src/units/representation/UnitsRepresentationUtils.java @@ -26,6 +26,7 @@ import units.qual.BUC; import units.qual.Dimensionless; import units.qual.PolyUnit; +import units.qual.RDU; import units.qual.UnitsAlias; import units.qual.UnitsBottom; import units.qual.UnitsRep; @@ -64,6 +65,8 @@ public class UnitsRepresentationUtils { public AnnotationMirror SURFACE_BOTTOM; + public AnnotationMirror RECEIVER_DEPENDANT_UNIT; + // /** Instance of {@link VarAnnot} for use in UnitsVisitor in infer mode. */ // public AnnotationMirror VARANNOT; @@ -209,6 +212,7 @@ protected Map createZeroFilledBaseUnitsMap() { // units public void postInit() { POLYUNIT = AnnotationBuilder.fromClass(elements, PolyUnit.class); + RECEIVER_DEPENDANT_UNIT = AnnotationBuilder.fromClass(elements, RDU.class); RAWUNITSREP = AnnotationBuilder.fromClass(elements, UnitsRep.class); @@ -494,6 +498,8 @@ public TypecheckUnit createTypecheckUnit(AnnotationMirror anno) { // if it is a polyunit annotation, generate top if (AnnotationUtils.areSameByClass(anno, PolyUnit.class)) { unit.setUnknownUnits(true); + } else if (AnnotationUtils.areSameByClass(anno, RDU.class)) { + unit.setRDUnits(true); } // if it is a units internal annotation, generate the internal unit else if (AnnotationUtils.areSameByClass(anno, UnitsRep.class)) { @@ -578,6 +584,10 @@ public void setSerializedBaseUnits(Set constantSlots) { TypecheckUnit unit = createTypecheckUnit(slot.getValue()); // System.err.println(unit); + if (unit.isRDUnits()) { + continue; + } + serializePrefix = serializePrefix || (unit.getPrefixExponent() != 0); for (Entry baseUnitExponents : unit.getExponents().entrySet()) { diff --git a/src/units/solvers/backend/z3smt/UnitsZ3SmtFormatTranslator.java b/src/units/solvers/backend/z3smt/UnitsZ3SmtFormatTranslator.java index 1208ad7..292e443 100644 --- a/src/units/solvers/backend/z3smt/UnitsZ3SmtFormatTranslator.java +++ b/src/units/solvers/backend/z3smt/UnitsZ3SmtFormatTranslator.java @@ -60,6 +60,7 @@ public String generateZ3SlotDeclaration(VariableSlot slot) { slotDeclaration.add(addZ3BoolDefinition(encodedSlot.getUnknownUnits())); slotDeclaration.add(addZ3BoolDefinition(encodedSlot.getUnitsBottom())); + slotDeclaration.add(addZ3BoolDefinition(encodedSlot.getRDUnits())); if (unitsRepUtils.serializePrefix()) { slotDeclaration.add(addZ3IntDefinition(encodedSlot.getPrefixExponent())); @@ -138,6 +139,8 @@ protected Z3InferenceUnit serializeConstantSlot(ConstantSlot slot) { encodedSlot.setUnknownUnits(true); } else if (unit.isUnitsBottom()) { encodedSlot.setUnitsBottom(true); + } else if (unit.isRDUnits()) { + encodedSlot.setRDUnits(true); } else { encodedSlot.setPrefixExponent(unit.getPrefixExponent()); for (String bu : unitsRepUtils.serializableBaseUnits()) { @@ -166,16 +169,18 @@ public void preAnalyzeSlots(Collection slots) { @Override public BoolExpr encodeSlotWellformnessConstraint(VariableSlot slot) { + Z3InferenceUnit serializedSlot = slot.serialize(this); if (slot instanceof ConstantSlot) { ConstantSlot cs = (ConstantSlot) slot; AnnotationMirror anno = cs.getValue(); - // encode PolyAll and PolyUnit as constant trues + // encode PolyUnit as constant trues if (AnnotationUtils.areSame(anno, unitsRepUtils.POLYUNIT)) { return ctx.mkTrue(); } + if (AnnotationUtils.areSame(anno, unitsRepUtils.RECEIVER_DEPENDANT_UNIT)) { + return serializedSlot.getRDUnits(); + } } - - Z3InferenceUnit serializedSlot = slot.serialize(this); return UnitsZ3SmtEncoderUtils.slotWellformedness(ctx, serializedSlot); } @@ -184,7 +189,7 @@ public BoolExpr encodeSlotPreferenceConstraint(VariableSlot slot) { if (slot instanceof ConstantSlot) { ConstantSlot cs = (ConstantSlot) slot; AnnotationMirror anno = cs.getValue(); - // encode PolyAll and PolyUnit as constant trues + // encode PolyUnit as constant trues if (AnnotationUtils.areSame(anno, unitsRepUtils.POLYUNIT)) { return ctx.mkTrue(); } diff --git a/src/units/solvers/backend/z3smt/encoder/UnitsZ3SmtCombineConstraintEncoder.java b/src/units/solvers/backend/z3smt/encoder/UnitsZ3SmtCombineConstraintEncoder.java new file mode 100644 index 0000000..c578096 --- /dev/null +++ b/src/units/solvers/backend/z3smt/encoder/UnitsZ3SmtCombineConstraintEncoder.java @@ -0,0 +1,63 @@ +package units.solvers.backend.z3smt.encoder; + +import backend.z3smt.Z3SmtFormatTranslator; +import backend.z3smt.encoder.Z3SmtAbstractConstraintEncoder; +import checkers.inference.model.ConstantSlot; +import checkers.inference.model.VariableSlot; +import checkers.inference.solver.backend.encoder.combine.CombineConstraintEncoder; +import checkers.inference.solver.frontend.Lattice; +import com.microsoft.z3.BoolExpr; +import com.microsoft.z3.Context; +import units.representation.TypecheckUnit; +import units.solvers.backend.z3smt.representation.Z3InferenceUnit; + +public class UnitsZ3SmtCombineConstraintEncoder + extends Z3SmtAbstractConstraintEncoder + implements CombineConstraintEncoder { + + public UnitsZ3SmtCombineConstraintEncoder( + Lattice lattice, + Context ctx, + Z3SmtFormatTranslator z3SmtFormatTranslator) { + super(lattice, ctx, z3SmtFormatTranslator); + } + + private BoolExpr receiver_dependent( + VariableSlot target, VariableSlot declared, VariableSlot result) { + Z3InferenceUnit tar = target.serialize(z3SmtFormatTranslator); + Z3InferenceUnit decl = declared.serialize(z3SmtFormatTranslator); + Z3InferenceUnit res = result.serialize(z3SmtFormatTranslator); + + return ctx.mkAnd( + ctx.mkImplies(decl.getRDUnits(), UnitsZ3SmtEncoderUtils.equality(ctx, tar, res)), + ctx.mkImplies( + ctx.mkNot(decl.getRDUnits()), + UnitsZ3SmtEncoderUtils.equality(ctx, decl, res))); + } + + @Override + public BoolExpr encodeVariable_Variable( + VariableSlot target, VariableSlot declared, VariableSlot result) { + return ctx.mkTrue(); + // return receiver_dependent(target, declared, result); + } + + @Override + public BoolExpr encodeVariable_Constant( + VariableSlot target, ConstantSlot declared, VariableSlot result) { + return receiver_dependent(target, declared, result); + } + + @Override + public BoolExpr encodeConstant_Variable( + ConstantSlot target, VariableSlot declared, VariableSlot result) { + return ctx.mkTrue(); + // return receiver_dependent(target, declared, result); + } + + @Override + public BoolExpr encodeConstant_Constant( + ConstantSlot target, ConstantSlot declared, VariableSlot result) { + return receiver_dependent(target, declared, result); + } +} diff --git a/src/units/solvers/backend/z3smt/encoder/UnitsZ3SmtConstraintEncoderFactory.java b/src/units/solvers/backend/z3smt/encoder/UnitsZ3SmtConstraintEncoderFactory.java index 83b8acc..2056456 100644 --- a/src/units/solvers/backend/z3smt/encoder/UnitsZ3SmtConstraintEncoderFactory.java +++ b/src/units/solvers/backend/z3smt/encoder/UnitsZ3SmtConstraintEncoderFactory.java @@ -63,7 +63,7 @@ public ExistentialConstraintEncoder createExistentialConstraintEncoder @Override public CombineConstraintEncoder createCombineConstraintEncoder() { - return null; + return new UnitsZ3SmtCombineConstraintEncoder(lattice, ctx, formatTranslator); } @Override diff --git a/src/units/solvers/backend/z3smt/encoder/UnitsZ3SmtEncoderUtils.java b/src/units/solvers/backend/z3smt/encoder/UnitsZ3SmtEncoderUtils.java index 7465a71..c044504 100644 --- a/src/units/solvers/backend/z3smt/encoder/UnitsZ3SmtEncoderUtils.java +++ b/src/units/solvers/backend/z3smt/encoder/UnitsZ3SmtEncoderUtils.java @@ -91,11 +91,15 @@ public static BoolExpr slotWellformedness(Context ctx, Z3InferenceUnit unit) { // variable declarations outputted for the z3 files BoolExpr allExponentsAreZero = allExponentsAreZero(ctx, unit); /* @formatter:off // this is for eclipse formatter */ - return UnitsZ3SmtEncoderUtils.mkOneHot( - ctx, - ctx.mkAnd(ctx.mkNot(unit.getUnknownUnits()), ctx.mkNot(unit.getUnitsBottom())), - ctx.mkAnd(unit.getUnknownUnits(), allExponentsAreZero), - ctx.mkAnd(unit.getUnitsBottom(), allExponentsAreZero)); + return ctx.mkAnd( + ctx.mkNot(unit.getRDUnits()), + UnitsZ3SmtEncoderUtils.mkOneHot( + ctx, + ctx.mkAnd( + ctx.mkNot(unit.getUnknownUnits()), + ctx.mkNot(unit.getUnitsBottom())), + ctx.mkAnd(unit.getUnknownUnits(), allExponentsAreZero), + ctx.mkAnd(unit.getUnitsBottom(), allExponentsAreZero))); /* @formatter:on // this is for eclipse formatter */ // simplify xor (xor ((not x and not y), x), y) @@ -127,6 +131,7 @@ private static BoolExpr mustBeDimensionless(Context ctx, Z3InferenceUnit unit) { BoolExpr allExponentsAreZero = allExponentsAreZero(ctx, unit); /* @formatter:off // this is for eclipse formatter */ return ctx.mkAnd( + ctx.mkNot(unit.getRDUnits()), ctx.mkNot(unit.getUnknownUnits()), ctx.mkNot(unit.getUnitsBottom()), allExponentsAreZero); @@ -141,6 +146,7 @@ public static BoolExpr equality(Context ctx, Z3InferenceUnit fst, Z3InferenceUni /* @formatter:off // this is for eclipse formatter */ BoolExpr equalityEncoding = ctx.mkAnd( + ctx.mkEq(fst.getRDUnits(), snd.getRDUnits()), ctx.mkEq(fst.getUnknownUnits(), snd.getUnknownUnits()), ctx.mkEq(fst.getUnitsBottom(), snd.getUnitsBottom())); if (UnitsRepresentationUtils.getInstance().serializePrefix()) { diff --git a/src/units/solvers/backend/z3smt/representation/Z3InferenceUnit.java b/src/units/solvers/backend/z3smt/representation/Z3InferenceUnit.java index c5890d0..633c1a9 100644 --- a/src/units/solvers/backend/z3smt/representation/Z3InferenceUnit.java +++ b/src/units/solvers/backend/z3smt/representation/Z3InferenceUnit.java @@ -20,6 +20,7 @@ public class Z3InferenceUnit { private BoolExpr uu; private BoolExpr ub; private IntExpr prefixExponent; + private BoolExpr rdu; // Tree map maintaining sorted order on base unit names private final Map exponents; @@ -40,6 +41,8 @@ public static Z3InferenceUnit makeConstantSlot(Context ctx, int slotID) { slot.uu = ctx.mkBool(false); // default UU value is false slot.ub = ctx.mkBool(false); + // default RDU value is false; + slot.rdu = ctx.mkBool(false); // default prefixExponent is 0 slot.prefixExponent = slot.intZero; @@ -62,6 +65,9 @@ public static Z3InferenceUnit makeVariableSlot(Context ctx, int slotID) { ctx.mkBoolConst( UnitsZ3SmtEncoderUtils.z3VarName( slotID, UnitsZ3SmtEncoderUtils.ubSlotName)); + + slot.rdu = ctx.mkBool(false); + slot.prefixExponent = ctx.mkIntConst( UnitsZ3SmtEncoderUtils.z3VarName( @@ -91,6 +97,14 @@ public BoolExpr getUnitsBottom() { return ub; } + public void setRDUnits(boolean val) { + rdu = ctx.mkBool(val); + } + + public BoolExpr getRDUnits() { + return rdu; + } + public void setPrefixExponent(int exp) { prefixExponent = ctx.mkInt(exp); } diff --git a/testing/inference/Casting.java b/testing/inference/Casting.java index 5bf3284..6d7dbd5 100644 --- a/testing/inference/Casting.java +++ b/testing/inference/Casting.java @@ -8,7 +8,14 @@ class Casting { int primDim = (int) 20.0f; - Integer boxDim = 20; + int boxDim = 20; + @m int boxM = (@m int) boxDim; - @m Integer boxM = (@m Integer) boxDim; + void cast() { + int x = 20; + @m int y = (@m int) x; + + Integer boxX = 20; + @m Integer boxY = (@m Integer) boxX; + } } diff --git a/testing/inference/Constructors.java b/testing/inference/Constructors.java index 07f26f4..928200d 100644 --- a/testing/inference/Constructors.java +++ b/testing/inference/Constructors.java @@ -6,7 +6,6 @@ class PolyUnitClass { } class MeterClass { - // :: error: (super.invocation.invalid) @m MeterClass(@m int x) {} } @@ -26,13 +25,13 @@ void polyUnitConstructorTest() { PolyUnitClass puc2 = new PolyUnitClass(5 * UnitsTools.m); // propagate @m from constructor return type - // :: fixable-error: (constructor.invocation.invalid) + // :: fixable-warning: (cast.unsafe.constructor.invocation) PolyUnitClass puc3 = new @m PolyUnitClass(5); - // :: fixable-error: (constructor.invocation.invalid) + // :: fixable-warning: (cast.unsafe.constructor.invocation) @m PolyUnitClass puc4 = new @m PolyUnitClass(5); - // :: fixable-error: (constructor.invocation.invalid) + // :: fixable-warning: (cast.unsafe.constructor.invocation) PolyUnitClass puc5 = new @m PolyUnitClass(5 * UnitsTools.s); } diff --git a/testing/inference/GenericMethods.java b/testing/inference/GenericMethods.java index 43bdaf0..caed357 100644 --- a/testing/inference/GenericMethods.java +++ b/testing/inference/GenericMethods.java @@ -12,7 +12,6 @@ public T idOne(T param) { } public String mStringCatOne(T param) { - // :: fixable-error: (method.invocation.invalid) return param.toString() + param; } diff --git a/testing/inference/JavaUtilConcurrentTimeUnit.java b/testing/inference/JavaUtilConcurrentTimeUnit.java index c22aa5c..66301a2 100644 --- a/testing/inference/JavaUtilConcurrentTimeUnit.java +++ b/testing/inference/JavaUtilConcurrentTimeUnit.java @@ -6,6 +6,7 @@ class JavaUtilConcurrentTimeUnit { void test(long time, TimeUnit unit) throws Exception { + // :: fixable-error: (argument.type.incompatible) long milliseconds = unit.toMillis(time); Thread.sleep(milliseconds); } @@ -28,4 +29,32 @@ void test2() { @ns long nanosec = ns.convert(10L, TimeUnit.DAYS); } + + void testParams() { + TimeUnit s_unit = TimeUnit.SECONDS; + TimeUnit ns_unit = NANOSECONDS; + + long ns = 1000; + long s = 1000; + + // :: fixable-error: (argument.type.incompatible) + TimeUnit.SECONDS.toMillis(s); + // :: fixable-error: (argument.type.incompatible) + TimeUnit.NANOSECONDS.toMillis(ns); + + // :: fixable-error: (argument.type.incompatible) + ns_unit.toMillis(ns); + // :: fixable-error: (argument.type.incompatible) + s_unit.toMillis(s); + } + + void testReturn(long s1, long s2) { + TimeUnit s = TimeUnit.SECONDS; + TimeUnit ms = TimeUnit.MILLISECONDS; + + // :: fixable-error: (assignment.type.incompatible) + s1 = s.convert(10L, NANOSECONDS); + // :: fixable-error: (assignment.type.incompatible) + s2 = TimeUnit.SECONDS.convert(10L, NANOSECONDS); + } } diff --git a/testing/inference/Methods.java b/testing/inference/Methods.java index 8dad62d..655a2cb 100644 --- a/testing/inference/Methods.java +++ b/testing/inference/Methods.java @@ -14,6 +14,15 @@ class Methods { return x; } + int method(int x) { + return x; + } + + void testMethod() { + // :: fixable-error: (assignment.type.incompatible) + @m int num = method(5); + } + void polyUnitMethodTest() { // :: fixable-error: (assignment.type.incompatible) @m int pum1 = polyUnitMethod(5); diff --git a/testing/inference/StaticFinalConstants.java b/testing/inference/StaticFinalConstants.java index dcfa473..5477cde 100644 --- a/testing/inference/StaticFinalConstants.java +++ b/testing/inference/StaticFinalConstants.java @@ -5,20 +5,6 @@ class StaticFinalConstants { - void inferRadians() { - // :: fixable-error: (argument.type.incompatible) - Math.sin(Math.E); - - // :: fixable-error: (argument.type.incompatible) - Math.cos(Math.PI); - - // :: fixable-error: (argument.type.incompatible) - sin(E); - - // :: fixable-error: (argument.type.incompatible) - cos(PI); - } - static class MyConstants { public static final double X = 10; public static final Integer Y = Integer.MAX_VALUE; diff --git a/testing/typecheck/Constructors.java b/testing/typecheck/Constructors.java index d496bb4..d8c4293 100644 --- a/testing/typecheck/Constructors.java +++ b/testing/typecheck/Constructors.java @@ -6,7 +6,6 @@ class PolyUnitClass { } class MeterClass { - // :: error: (super.invocation.invalid) @m MeterClass(@m int x) {} } @@ -21,13 +20,13 @@ void polyUnitConstructorTest() { PolyUnitClass puc2 = new PolyUnitClass(5 * UnitsTools.m); - // :: error: (constructor.invocation.invalid) + // :: warning: (cast.unsafe.constructor.invocation) PolyUnitClass puc3 = new @m PolyUnitClass(5); - // :: error: (constructor.invocation.invalid) + // :: warning: (cast.unsafe.constructor.invocation) @m PolyUnitClass puc4 = new @m PolyUnitClass(5); - // :: error: (constructor.invocation.invalid) + // :: warning: (cast.unsafe.constructor.invocation) PolyUnitClass puc5 = new @m PolyUnitClass(5 * UnitsTools.s); } diff --git a/testing/typecheck/JavaUtilConcurrentTimeUnit.java b/testing/typecheck/JavaUtilConcurrentTimeUnit.java index d548579..7a6da7c 100644 --- a/testing/typecheck/JavaUtilConcurrentTimeUnit.java +++ b/testing/typecheck/JavaUtilConcurrentTimeUnit.java @@ -23,6 +23,7 @@ void readFromEnumInSource() { } void test(long time, TimeUnit unit) throws Exception { + // :: error: (argument.type.incompatible) @ms long milliseconds = unit.toMillis(time); Thread.sleep(milliseconds); } @@ -36,10 +37,14 @@ void test2() { // :: error: (assignment.type.incompatible) ms = ns; - // ensure the poly convert works + // ensure RDU convert works @s long seconds = s.convert(10L, NANOSECONDS); + // :: error: (assignment.type.incompatible) + @s long not_seconds = ms.convert(10L, NANOSECONDS); @s long secondsTwo = TimeUnit.SECONDS.convert(10L, NANOSECONDS); + // :: error: (assignment.type.incompatible) + @s long not_secondsTwo = TimeUnit.MILLISECONDS.convert(10L, NANOSECONDS); // convert 10 minutes to milliseconds @ms long milliseconds = TimeUnit.MILLISECONDS.convert(10L, TimeUnit.MINUTES); @@ -59,4 +64,23 @@ void bad() { // :: error: (assignment.type.incompatible) @s TimeUnit s4 = NANOSECONDS; } + + void testParams(@ns long ns, @s long s) { + @s TimeUnit s_unit = TimeUnit.SECONDS; + @ns TimeUnit ns_unit = NANOSECONDS; + + TimeUnit.SECONDS.toMillis(s); + TimeUnit.NANOSECONDS.toMillis(ns); + // :: error: (argument.type.incompatible) + TimeUnit.NANOSECONDS.toMillis(s); + // :: error: (argument.type.incompatible) + TimeUnit.SECONDS.toMillis(ns); + + ns_unit.toMillis(ns); + s_unit.toMillis(s); + // :: error: (argument.type.incompatible) + s_unit.toMillis(ns); + // :: error: (argument.type.incompatible) + ns_unit.toMillis(s); + } } diff --git a/testing/typecheck/Methods.java b/testing/typecheck/Methods.java index da23277..363bd49 100644 --- a/testing/typecheck/Methods.java +++ b/testing/typecheck/Methods.java @@ -23,7 +23,7 @@ void polyUnitMethodTest() { // :: error: (assignment.type.incompatible) @m int pum3 = polyUnitMethod(5, 6); - @m int pum4 = polyUnitMethod(UnitsTools.m, UnitsTools.m); + int pum4 = polyUnitMethod(UnitsTools.m, UnitsTools.m); // :: error: (assignment.type.incompatible) @m int pum5 = polyUnitMethod(UnitsTools.m, UnitsTools.s); diff --git a/testing/typecheck/RDUMethodReceiver.java b/testing/typecheck/RDUMethodReceiver.java new file mode 100644 index 0000000..2d29c90 --- /dev/null +++ b/testing/typecheck/RDUMethodReceiver.java @@ -0,0 +1,46 @@ +import units.qual.*; + +public class RDUMethodReceiver { + + @m public class MeterClass { + @RDU + Object get(@m MeterClass this) { + return null; + } + + Object set(@m MeterClass this, @RDU Object x) { + return null; + } + } + + @s public class SecondClass { + @RDU + Object get(@s SecondClass this) { + return null; + } + + Object set(@s SecondClass this, @RDU Object x) { + return null; + } + } + + void invoke() { + MeterClass mc = new MeterClass(); + @m Object x = mc.get(); + mc.set(x); + + SecondClass sc = new SecondClass(); + @s Object y = sc.get(); + sc.set(y); + + // :: error: (assignment.type.incompatible) + @m Object z = sc.get(); + // :: error: (argument.type.incompatible) + sc.set(z); + + // :: error: (assignment.type.incompatible) + @s Object w = mc.get(); + // :: error: (argument.type.incompatible) + mc.set(w); + } +} diff --git a/testing/typecheck/StaticFinalConstants.java b/testing/typecheck/StaticFinalConstants.java new file mode 100644 index 0000000..49ba5c8 --- /dev/null +++ b/testing/typecheck/StaticFinalConstants.java @@ -0,0 +1,20 @@ +import static java.lang.Math.*; + +import units.qual.*; + +class StaticFinalConstants { + + void inferRadians() { + // :: error: (argument.type.incompatible) + Math.sin(Math.E); + + // :: error: (argument.type.incompatible) + Math.cos(Math.PI); + + // :: error: (argument.type.incompatible) + sin(E); + + // :: error: (argument.type.incompatible) + cos(PI); + } +}