Skip to content
Original file line number Diff line number Diff line change
Expand Up @@ -120,9 +120,15 @@ public void viewpointAdaptConstructor(
AnnotatedTypeMirror p = combineTypeWithType(receiverType, parameterType);
mappings.put(parameterType, p);
}
for (AnnotatedTypeMirror typeVariable : typeVariables) {
AnnotatedTypeMirror tv = combineTypeWithType(receiverType, typeVariable);
mappings.put(typeVariable, tv);
// Adapt type-variable declarations separately. AnnotatedTypeCopierWithReplacement does not
// replace executable type parameters (see its visitTypeVariable), so install the adapted

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't the AnnotatedTypeCopierWithReplacement do this? Do you understand the reasoning given there?

// declarations explicitly below.
List<AnnotatedTypeVariable> adaptedTypeVariables = new ArrayList<>(typeVariables.size());
for (AnnotatedTypeVariable typeVariable : typeVariables) {
AnnotatedTypeVariable adapted =
(AnnotatedTypeVariable) combineTypeWithType(receiverType, typeVariable);
mappings.put(typeVariable, adapted);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we still need to add it to the mapping? Why add it to the mapping if AnnotatedTypeCopierWithReplacement doesn't do anything with the method type variables?

adaptedTypeVariables.add(adapted);
}
AnnotatedTypeMirror cr = combineTypeWithType(receiverType, constructorReturn);
mappings.put(constructorReturn, cr);
Expand All @@ -133,7 +139,7 @@ public void viewpointAdaptConstructor(
unsubstitutedConstructorType, mappings);

constructorType.setParameterTypes(unsubstitutedConstructorType.getParameterTypes());
constructorType.setTypeVariables(unsubstitutedConstructorType.getTypeVariables());
constructorType.setTypeVariables(adaptedTypeVariables);
constructorType.setReturnType(unsubstitutedConstructorType.getReturnType());
}

Expand Down Expand Up @@ -163,9 +169,17 @@ public void viewpointAdaptMethod(
mappings.put(parameterType, p);
}

// Adapt type-variable declarations separately. AnnotatedTypeCopierWithReplacement does not
// replace executable type parameters (see its visitTypeVariable), so install the adapted
// declarations explicitly below. Without this, inference and checkTypeArguments would see
// the unadapted declaration bounds (e.g. @ReceiverDependentQual instead of the
// receiver-adapted qualifier).
List<AnnotatedTypeVariable> adaptedTypeVariables = new ArrayList<>(typeVariables.size());
for (AnnotatedTypeVariable typeVariable : typeVariables) {
AnnotatedTypeMirror tv = combineTypeWithType(receiverType, typeVariable);
mappings.put(typeVariable, tv);
AnnotatedTypeVariable adapted =
(AnnotatedTypeVariable) combineTypeWithType(receiverType, typeVariable);
mappings.put(typeVariable, adapted);
adaptedTypeVariables.add(adapted);
}

if (returnType.getKind() != TypeKind.VOID) {
Expand All @@ -184,11 +198,11 @@ public void viewpointAdaptMethod(
unsubstitutedMethodType, mappings);

// Because we can't viewpoint adapt asMemberOf result, we adapt the declared method first,
// and sets the corresponding parts to asMemberOf result
// and set the corresponding parts on the asMemberOf result.
methodType.setReturnType(unsubstitutedMethodType.getReturnType());
methodType.setReceiverType(unsubstitutedMethodType.getReceiverType());
methodType.setParameterTypes(unsubstitutedMethodType.getParameterTypes());
methodType.setTypeVariables(unsubstitutedMethodType.getTypeVariables());
methodType.setTypeVariables(adaptedTypeVariables);
}

/**
Expand Down
79 changes: 79 additions & 0 deletions framework/tests/viewpointtest/ConstructorTypeVariableBounds.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
import viewpointtest.quals.*;

public class ConstructorTypeVariableBounds {
static class C {
// No-arg generic constructor: type argument is unused, so inference instantiates T to
// the adapted upper bound.
@SuppressWarnings({"inconsistent.constructor.type", "super.invocation.invalid"})
<T extends @ReceiverDependentQual Object> C() {}

@SuppressWarnings({"inconsistent.constructor.type", "super.invocation.invalid"})
<T extends @ReceiverDependentQual Object> C(T t) {}
}

void topViewpoint(@Top Object top, @A Object a, @B Object b, @Bottom Object bottom) {
// Constructed type @Top adapts @ReceiverDependentQual to @Lost. Creating @Top is also
// forbidden by the viewpoint test checker.
// :: error: (new.class.type.invalid) :: error: (type.argument.type.incompatible)
new @Top C();

// :: error: (new.class.type.invalid) :: error: (type.argument.type.incompatible)
new <@Top Object>@Top C(top);

// :: error: (new.class.type.invalid) :: error: (type.argument.type.incompatible)
new <@A Object>@Top C(a);

// :: error: (new.class.type.invalid) :: error: (type.argument.type.incompatible)
new <@B Object>@Top C(b);

// :: error: (new.class.type.invalid)
new <@Bottom Object>@Top C(bottom);

// :: error: (new.class.type.invalid) :: error: (type.arguments.not.inferred)
new @Top C(top);

// :: error: (new.class.type.invalid) :: error: (type.arguments.not.inferred)
new @Top C(a);

// :: error: (new.class.type.invalid) :: error: (type.arguments.not.inferred)
new @Top C(b);

// :: error: (new.class.type.invalid)
new @Top C(bottom);
}

void aViewpoint(@Top Object top, @A Object a, @B Object b, @Bottom Object bottom) {
// Constructed type @A adapts @ReceiverDependentQual to @A, so @A and @Bottom are within
// the adapted constructor type parameter bound. Inference instantiates T to @A for the
// no-arg constructor.
// :: warning: (cast.unsafe.constructor.invocation)
new @A C();

// :: error: (type.argument.type.incompatible) :: warning:
// (cast.unsafe.constructor.invocation)
Comment thread
aosen-xiong marked this conversation as resolved.
new <@Top Object>@A C(top);

// :: warning: (cast.unsafe.constructor.invocation)
new <@A Object>@A C(a);

// :: error: (type.argument.type.incompatible) :: warning:
// (cast.unsafe.constructor.invocation)
Comment thread
aosen-xiong marked this conversation as resolved.
new <@B Object>@A C(b);

// :: warning: (cast.unsafe.constructor.invocation)
new <@Bottom Object>@A C(bottom);

// :: error: (type.arguments.not.inferred)
new @A C(top);

// Inference succeeds: argument @A is within the adapted bound @A.
// :: warning: (cast.unsafe.constructor.invocation)
new @A C(a);

// :: error: (type.arguments.not.inferred)
new @A C(b);

// :: warning: (cast.unsafe.constructor.invocation)
new @A C(bottom);
}
}
72 changes: 72 additions & 0 deletions framework/tests/viewpointtest/MethodTypeVariableBounds.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import viewpointtest.quals.*;

public class MethodTypeVariableBounds {
static class Methods {
<T extends @ReceiverDependentQual Object> void noArg() {}

<T extends @ReceiverDependentQual Object> void withArg(T t) {}
}

void topReceiver(
@Top Methods methods,
@Top Object top,
@A Object a,
@B Object b,
@Bottom Object bottom) {
// @Top viewpoint-adapts @ReceiverDependentQual to @Lost, so only @Bottom is within the
// adapted method type parameter bound.
// :: error: (type.argument.type.incompatible)
methods.noArg();

// :: error: (type.argument.type.incompatible)
methods.<@Top Object>withArg(top);

// :: error: (type.argument.type.incompatible)
methods.<@A Object>withArg(a);

// :: error: (type.argument.type.incompatible)
methods.<@B Object>withArg(b);

methods.<@Bottom Object>withArg(bottom);

// :: error: (type.arguments.not.inferred)
methods.withArg(top);

// :: error: (type.arguments.not.inferred)
methods.withArg(a);

// :: error: (type.arguments.not.inferred)
methods.withArg(b);

methods.withArg(bottom);
}

void aReceiver(
@A Methods methods, @Top Object top, @A Object a, @B Object b, @Bottom Object bottom) {
// @A viewpoint-adapts @ReceiverDependentQual to @A, so @A and @Bottom are within the
// adapted method type parameter bound. Inference instantiates T to the adapted upper
// bound @A, which is a valid type argument.
methods.noArg();

// :: error: (type.argument.type.incompatible)
methods.<@Top Object>withArg(top);

methods.<@A Object>withArg(a);

// :: error: (type.argument.type.incompatible)
methods.<@B Object>withArg(b);

methods.<@Bottom Object>withArg(bottom);

// :: error: (type.arguments.not.inferred)
methods.withArg(top);

// Inference succeeds: argument @A is within the adapted bound @A.
methods.withArg(a);
Comment thread
aosen-xiong marked this conversation as resolved.

// :: error: (type.arguments.not.inferred)
methods.withArg(b);

methods.withArg(bottom);
}
}
Loading