Skip to content

Commit ede26ce

Browse files
author
dlsmith
committed
DynamicJava: Added a TypeSystem.isDisjoint function. Redefined casting and valid ==, !=, instanceof in terms of it.
git-svn-id: file:///tmp/test-svn/trunk@5064 fe72c1cf-3628-48e9-8b72-1c46755d3cff
1 parent 4b21424 commit ede26ce

File tree

4 files changed

+83
-46
lines changed

4 files changed

+83
-46
lines changed

dynamicjava/src/edu/rice/cs/dynamicjava/interpreter/ExpressionChecker.java

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1333,7 +1333,8 @@ private Type handleEqualityExpression(BinaryExpression node, Lambda2<Object, Obj
13331333
Type leftT = check(node.getLeftExpression());
13341334
Type rightT = check(node.getRightExpression());
13351335
if (ts.isReference(leftT) && ts.isReference(rightT)) {
1336-
if (!ts.isCastable(leftT, rightT) && !ts.isCastable(rightT, leftT)) {
1336+
if (ts.isDisjoint(leftT, rightT)) {
1337+
setErrorStrings(node, ts.userRepresentation(leftT), ts.userRepresentation(rightT));
13371338
throw new ExecutionError("compare.type", node);
13381339
}
13391340
setOperation(node, objectCase);
@@ -1355,11 +1356,13 @@ else if (getType(left) instanceof NumericType && getType(right) instanceof Numer
13551356
node.setRightExpression(promoted.second());
13561357
}
13571358
else {
1359+
setErrorStrings(node, ts.userRepresentation(leftT), ts.userRepresentation(rightT));
13581360
throw new ExecutionError("compare.type", node);
13591361
}
13601362
setOperation(node, primitiveCase);
13611363
}
13621364
catch (UnsupportedConversionException e) {
1365+
setErrorStrings(node, ts.userRepresentation(leftT), ts.userRepresentation(rightT));
13631366
throw new ExecutionError("compare.type", node);
13641367
}
13651368
}
@@ -1390,6 +1393,8 @@ private Type handleRelationalExpression(BinaryExpression node) {
13901393
return setType(node, TypeSystem.BOOLEAN);
13911394
}
13921395
catch (UnsupportedConversionException e) {
1396+
setErrorStrings(node, ts.userRepresentation(getType(node.getLeftExpression())),
1397+
ts.userRepresentation(getType(node.getRightExpression())));
13931398
throw new ExecutionError("compare.type", node);
13941399
}
13951400
}
@@ -1567,7 +1572,7 @@ private Type handleBooleanExpression(BinaryExpression node) {
15671572
@Override public Type visit(InstanceOfExpression node) {
15681573
Type expT = check(node.getExpression());
15691574
Type targetT = checkTypeName(node.getReferenceType());
1570-
if (!ts.isReference(expT) || !ts.isReference(targetT) || !ts.isCastable(targetT, expT)) {
1575+
if (!ts.isReference(expT) || !ts.isReference(targetT) || ts.isDisjoint(targetT, expT)) {
15711576
throw new ExecutionError("instanceof.type", node);
15721577
}
15731578
if (!ts.isReifiable(targetT)) {

dynamicjava/src/edu/rice/cs/dynamicjava/symbol/StandardTypeSystem.java

Lines changed: 73 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -230,15 +230,63 @@ private boolean isConcreteClass(DJClass c) {
230230
};
231231

232232

233-
/** Determine if {@link #cast} would succeed given an expression of the given type */
234-
public boolean isCastable(Type target, Type expT) {
235-
try {
236-
Expression e = TypeUtil.makeEmptyExpression();
237-
NodeProperties.setType(e, expT);
238-
cast(target, e);
239-
return true;
233+
/** Whether two types are known to be disjoint. (Standard version is implicitly defined in JLS 5.5.) */
234+
public boolean isDisjoint(final Type s, final Type t) {
235+
// By default, returns null for arrays and classes
236+
abstract class Visitor extends TypeAbstractVisitor<Boolean> {
237+
private final Type _other;
238+
public Visitor(Type other) { _other = other; }
239+
public abstract boolean recur(Type that);
240+
@Override public Boolean forPrimitiveType(PrimitiveType that) {
241+
return !isSubtype(that, _other) && !isSubtype(_other, that);
242+
}
243+
@Override public Boolean forNullType(NullType that) {
244+
return !isSubtype(that, _other);
245+
}
246+
@Override public Boolean forArrayType(ArrayType that) { return null; }
247+
@Override public Boolean forClassType(ClassType that) { return null; }
248+
@Override public Boolean forIntersectionType(IntersectionType that) {
249+
for (Type elt : that.ofTypes()) { if (recur(elt)) return true; }
250+
return false;
251+
}
252+
@Override public Boolean forUnionType(UnionType that) {
253+
for (Type elt : that.ofTypes()) { if (!recur(elt)) return false; }
254+
return true;
255+
}
256+
@Override public Boolean forVariableType(VariableType that) {
257+
// TODO: to be correct, we would need to recur (checked by a RecursionStack)
258+
return false;
259+
}
260+
@Override public Boolean forTopType(TopType s) { return false; }
261+
@Override public Boolean forBottomType(BottomType s) { return true; }
262+
}
263+
264+
Boolean sResult = s.apply(new Visitor(t) {
265+
public boolean recur(Type that) { return isDisjoint(that, t); }
266+
});
267+
if (sResult != null) { return sResult; }
268+
else {
269+
return t.apply(new Visitor(s) {
270+
public boolean recur(Type that) { return isDisjoint(s, that); }
271+
@Override public Boolean forArrayType(ArrayType t) {
272+
if (s instanceof ArrayType) { return isDisjoint(((ArrayType) s).ofType(), t.ofType()); }
273+
else { return !isSubtype(t, s); }
274+
}
275+
@Override public Boolean forClassType(ClassType t) {
276+
if (s instanceof ArrayType) { return !isSubtype(s, t); }
277+
else {
278+
ClassType sAsClass = (ClassType) s;
279+
if (sAsClass.ofClass().isFinal() || t.ofClass().isFinal() ||
280+
(!sAsClass.ofClass().isInterface() && !t.ofClass().isInterface())) {
281+
// either one of them is a final class or both are non-final classes
282+
if (!isSubtype(s, erase(t)) && !isSubtype(t, erase(s))) { return true; }
283+
}
284+
// TODO: The JLS also checks for disjoint type arguments (comparing *all* common superclasses)
285+
return false;
286+
}
287+
}
288+
});
240289
}
241-
catch (UnsupportedConversionException e) { return false; }
242290
}
243291

244292
/** Determine if {@link #assign} would succeed given a non-constant expression of the given type */
@@ -1149,8 +1197,7 @@ public Expression cast(final Type target, final Expression e) throws Unsupported
11491197
Expression result = makeReference(e);
11501198
Type source = NodeProperties.getType(result);
11511199
if (!isSubtype(source, target)) {
1152-
if (validCheckedCast(target, source) ||
1153-
(!_opt.prohibitUncheckedCasts() && validUncheckedCast(target, source))) {
1200+
if (!isDisjoint(source, target) && (!_opt.prohibitUncheckedCasts() || validCheckedCast(target, source))) {
11541201
NodeProperties.setCheckedType(result, erasedClass(target));
11551202
}
11561203
else { throw new UnsupportedConversionException(); }
@@ -1165,34 +1212,27 @@ public Expression cast(final Type target, final Expression e) throws Unsupported
11651212
}
11661213

11671214
/**
1168-
* Whether a reference down-cast is valid and guaranteed safe. See JLS 5.5. (This is not completely faithful
1169-
* to the JLS, simply because the details in the JLS are very complex.)
1215+
* Whether a reference down-cast is valid and guaranteed safe. May assume that
1216+
* source is not a subtype of target and that the two are not disjoint. See JLS 5.5.
11701217
*/
11711218
private boolean validCheckedCast(Type target, final Type source) {
11721219
return target.apply(new TypeAbstractVisitor<Boolean>() {
1173-
@Override public Boolean defaultCase(Type target) {
1174-
return isReifiable(target) && isSubtype(target, erase(source));
1175-
}
1176-
@Override public Boolean forClassType(ClassType target) {
1177-
return isReifiable(target) && (target.getClass().isInterface() || isSubtype(target, erase(source)));
1178-
}
1220+
@Override public Boolean defaultCase(Type target) { return isReifiable(target); }
11791221
@Override public Boolean forParameterizedClassType(ParameterizedClassType target) {
1180-
if (isReifiable(target)) { return target.ofClass().isInterface() || isSubtype(target, erase(source)); }
1181-
else {
1182-
if (isSubtype(target, source)) {
1183-
// Verify that that, given a value of type source & erase(target), it must have type target.
1184-
// Must show, where target=Target<T1..Tn>, forall X1..Xn, Target<X1..Xn> <: source implies Xi=Ti.
1185-
ParameterizedClassType wildCapt = capture(parameterize(new RawClassType(target.ofClass())));
1186-
Iterable<VariableType> unboundArgs =
1187-
IterUtil.filterInstances(IterUtil.relax(wildCapt.typeArguments()), VariableType.class);
1188-
Iterable<Type> boundArgs = inferTypeArguments(unboundArgs, EMPTY_TYPE_ITERABLE, wildCapt,
1189-
EMPTY_TYPE_ITERABLE, Option.some(source));
1190-
return boundArgs != null && IterUtil.and(boundArgs, target.typeArguments(), new Predicate2<Type, Type>() {
1191-
public boolean contains(Type inferred, Type orig) { return isEqual(inferred, orig); }
1192-
});
1193-
}
1194-
else { return false; }
1222+
if (isReifiable(target)) { return true; }
1223+
else if (isSubtype(target, source)) {
1224+
// Verify that that, given a value of type source & erase(target), it must have type target.
1225+
// Must show, where target=Target<T1..Tn>, forall X1..Xn, Target<X1..Xn> <: source implies Xi=Ti.
1226+
ParameterizedClassType wildCapt = capture(parameterize(new RawClassType(target.ofClass())));
1227+
Iterable<VariableType> unboundArgs =
1228+
IterUtil.filterInstances(IterUtil.relax(wildCapt.typeArguments()), VariableType.class);
1229+
Iterable<Type> boundArgs = inferTypeArguments(unboundArgs, EMPTY_TYPE_ITERABLE, wildCapt,
1230+
EMPTY_TYPE_ITERABLE, Option.some(source));
1231+
return boundArgs != null && IterUtil.and(boundArgs, target.typeArguments(), new Predicate2<Type, Type>() {
1232+
public boolean contains(Type inferred, Type orig) { return isEqual(inferred, orig); }
1233+
});
11951234
}
1235+
else { return false; }
11961236
}
11971237
@Override public Boolean forArrayType(ArrayType target) {
11981238
if (isArray(source)) { return validCheckedCast(target.ofType(), arrayElementType(source)); }
@@ -1201,14 +1241,6 @@ private boolean validCheckedCast(Type target, final Type source) {
12011241
});
12021242
}
12031243

1204-
/**
1205-
* Whether a reference down-cast is valid as an unchecked cast. See JLS 5.5. (This is not completely faithful
1206-
* to the JLS, simply because the details in the JLS are very complex.)
1207-
*/
1208-
private boolean validUncheckedCast(Type target, Type source) {
1209-
return isSubtype(target, erase(source)) || isSubtype(source, erase(target));
1210-
}
1211-
12121244
/**
12131245
* Prepare the given expression for assignment, wrapping it in any necessary conversions.
12141246
*

dynamicjava/src/edu/rice/cs/dynamicjava/symbol/TypeSystem.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -148,8 +148,8 @@ public boolean equals(Object o) {
148148
*/
149149
public abstract boolean isSubtype(Type subT, Type superT);
150150

151-
/** Determine if {@link #cast} would succeed given an expression of the given type */
152-
public abstract boolean isCastable(Type target, Type expT);
151+
/** Whether two types are known to be disjoint. */
152+
public abstract boolean isDisjoint(Type t1, Type t2);
153153

154154
/** Determine if {@link #assign} would succeed given a non-constant expression of the given type */
155155
public abstract boolean isAssignable(Type target, Type expT);

dynamicjava/src/koala/dynamicjava/interpreter/resources/messages.properties

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -195,7 +195,7 @@ cast.types = Bad types in cast: from %0 to %1
195195
catch.type = Bad 'catch' parameter type: %0 is not a Throwable
196196

197197
# Used by TypeChecker
198-
compare.type = Bad type in comparison expression
198+
compare.type = Illegal comparison: cannot compare a %0 to a %1
199199

200200
# Used by TypeChecker
201201
complement.expression.type = Bad type in complement expression

0 commit comments

Comments
 (0)