[JSR308] Creating new annotated types.
Niko Matsakis
niko at alum.mit.edu
Wed Nov 12 10:56:51 EST 2008
> This is intentional. The goal was to deter checkers from
> manipulating the java type and from breaking java conformity.
> Can you explained your desired type changes further?
I would be more wary of hindering people from creating the type system
they want than of people making bad type systems. I mean, ultimately,
the type system can't make a program whose Java types are wrong pass,
it can only make otherwise good programs fail to compile, right?
Therefore, if it's causing too many correct programs to fail, people
can just stop using it. That said, in most cases it is not necessary
to manipulate the AnnotatedTypeMirrors-- except when it is.
>> In my case, the method is declared as:
>>
>> public static <X> X free(X ptr) { return ptr; }
>>
>> What I want to do is to change the annotations on the return type.
>> Because the return type and the argument type are linked, [...]
> Annotating type parameter (explicitly or effectively) is not kosher.
>
> You can special case the given senario, modifying the return type of
> the method invocation tree of the method (e.g. free()).
> annotateImplicit({MethodInvocation}Tree, AnnotatedTypeMirror) should
> be passed the resolved type of such method.
I'm not quite sure what you think is not kosher. However,
annotateImplicit() does not solve my problem, because -- due to the
use of a type parameter -- the ExecutableType I get back has linked
the return type and the type of the argument (they are the same
pointer). So if I modify the annotations on one, I modify the
annotations on the other. This is incorrect.
What free() does in my system is to indicate that the region in which
'ptr' is stored will not be used anymore, effectively. This does not
mean the data is gone (hence the return), but it does mean the data is
unaliased and can be transferred into another region and has other
nice properties. So, the region annotation on 'ptr' changes, but the
traditional Java type does not (likewise, any other annotations on
'ptr' such as Nonnull or Immutability would also not change).
Effectively, I want to substitute a completely different signature for
free(), but only in my type system -- therefore, the Java type
signature I showed above is the fallback.
>> It does a deep clone of a
>> type with appropriate overloadable methods at each point.
> Be careful with recursive type parameters (e.g. <T extends Enum<T>>).
Yes, I believe I handle such cases. In fact, I may have been overly
paranoid, in that I also catch circular uses of any type (which I
think cannot occur, but it was just as easy). The code is attached
below.
Niko
package checkers.types;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import checkers.types.AnnotatedTypeMirror.AnnotatedArrayType;
import checkers.types.AnnotatedTypeMirror.AnnotatedDeclaredType;
import checkers.types.AnnotatedTypeMirror.AnnotatedExecutableType;
import checkers.types.AnnotatedTypeMirror.AnnotatedNoType;
import checkers.types.AnnotatedTypeMirror.AnnotatedNullType;
import checkers.types.AnnotatedTypeMirror.AnnotatedPrimitiveType;
import checkers.types.AnnotatedTypeMirror.AnnotatedTypeVariable;
import checkers.types.AnnotatedTypeMirror.AnnotatedWildcardType;
import checkers.types.visitors.AnnotatedTypeVisitor;
/**
* Performs a deep clone of a type. Subclasses may override
* at various points to introduce changes.
*
* <p>The {@code visit}
* methods from {@link AnnotatedTypeVisitor} are invoked
* with a source type and a destination type, which is a shallow
* copy of the source type. Their job
* is to clone the fields from the source type and then
* set the corresponding entries in the destination type.
* Overloading these methods gives you complete control
* over what values are eventually used.
*
* <p>The default implementations of the {@code visit}
* methods simply recurse using {@code scan} to copy any
* subtypes, and then invoke the
* {@code setFieldsForClonedXxxType} methods.
* These can also be a convenient place to overload.
*
* @author NDM
*/
public class AnnotatedTypeCloner
implements AnnotatedTypeVisitor<Void, AnnotatedTypeMirror>
{
/** set to false to avoid including annotations from the existing
type by default */
protected boolean includeAnnotationsInCopies = true;
/** set to false to avoid detecting duplicates in {@link
#scan(AnnotatedTypeMirror)} */
protected boolean preserveGraph = true;
protected Map<AnnotatedTypeMirror, AnnotatedTypeMirror> cloned = new
HashMap<AnnotatedTypeMirror, AnnotatedTypeMirror>();
/** The outside entry method. */
@SuppressWarnings("unchecked")
public <X extends AnnotatedTypeMirror> X clone(X type) {
cloned.clear();
AnnotatedTypeMirror result = scan(type);
assert type.getClass() == result.getClass();
return (X)result;
}
/**
* Useful static method for when you just want to deep-clone a type
* without introducting any changes */
public static <X extends AnnotatedTypeMirror> X cloneStatic(X type) {
return new AnnotatedTypeCloner().clone(type);
}
protected AnnotatedTypeMirror scan(AnnotatedTypeMirror type) {
if (preserveGraph) {
AnnotatedTypeMirror result = cloned.get(type);
if (result != null)
return result;
}
AnnotatedTypeMirror result = type.getCopy(includeAnnotationsInCopies);
if (preserveGraph)
cloned.put(type, result);
type.accept(this, result);
return result;
}
protected List<AnnotatedTypeMirror> scanAll(Iterable<? extends
AnnotatedTypeMirror> types) {
List<AnnotatedTypeMirror> result = new
ArrayList<AnnotatedTypeMirror>();
for (AnnotatedTypeMirror type : types)
result.add(scan(type));
return result;
}
public Void visitArray(AnnotatedArrayType type, AnnotatedTypeMirror
result) {
AnnotatedTypeMirror compType = scan(type.getComponentType());
setFieldsForClonedArray(type, compType, (AnnotatedArrayType) result);
return null;
}
protected void setFieldsForClonedArray(
AnnotatedArrayType origType,
AnnotatedTypeMirror compType,
AnnotatedArrayType result)
{
result.setComponentType(compType);
}
public Void visitDeclared(AnnotatedDeclaredType type,
AnnotatedTypeMirror result) {
List<AnnotatedTypeMirror> typeArguments =
scanAll(type.getTypeArguments());
setFieldsForClonedDeclaredType(
type,
typeArguments,
(AnnotatedDeclaredType) result);
return null;
}
protected void setFieldsForClonedDeclaredType(
AnnotatedDeclaredType origType,
List<AnnotatedTypeMirror> typeArguments,
AnnotatedDeclaredType result)
{
result.setTypeArguments(typeArguments);
}
public Void visitExecutable(AnnotatedExecutableType type,
AnnotatedTypeMirror result)
{
AnnotatedTypeMirror returnType = scan(type.getReturnType());
AnnotatedDeclaredType rcvrType = (AnnotatedDeclaredType)
scan(type.getReceiverType());
List<AnnotatedTypeMirror> paramTypes =
scanAll(type.getParameterTypes());
List<AnnotatedTypeMirror> thrownTypes =
scanAll(type.getThrownTypes());
List<AnnotatedTypeMirror> typeVariablesMirrors =
scanAll(type.getTypeVariables());
List<AnnotatedTypeVariable> typeVariables = new
ArrayList<AnnotatedTypeVariable>();
for (AnnotatedTypeMirror typeVariableMirror :
typeVariablesMirrors)
typeVariables.add((AnnotatedTypeVariable) typeVariableMirror);
setFieldsForClonedExecutableType(
type,
returnType,
rcvrType,
paramTypes,
thrownTypes,
typeVariables,
(AnnotatedExecutableType) result);
return null;
}
protected void setFieldsForClonedExecutableType(
AnnotatedExecutableType origType,
AnnotatedTypeMirror returnType,
AnnotatedDeclaredType rcvrType,
List<AnnotatedTypeMirror> paramTypes,
List<AnnotatedTypeMirror> thrownTypes,
List<AnnotatedTypeVariable> typeVariables,
AnnotatedExecutableType result)
{
result.setReturnType(returnType);
result.setReceiverType(rcvrType);
result.setParameterTypes(paramTypes);
result.setThrownTypes(thrownTypes);
result.setTypeVariables(typeVariables);
}
public Void visitNoType(AnnotatedNoType type, AnnotatedTypeMirror
result) {
return null;
}
public Void visitNull(AnnotatedNullType type, AnnotatedTypeMirror
result) {
return null;
}
public Void visitPrimitive(AnnotatedPrimitiveType type,
AnnotatedTypeMirror result) {
return null;
}
public Void visitTypeVariable(AnnotatedTypeVariable type,
AnnotatedTypeMirror result)
{
AnnotatedTypeMirror lowerBound = scan(type.getLowerBound());
AnnotatedTypeMirror upperBound = scan(type.getUpperBound());
setFieldsForClonedTypeVariable(
type,
lowerBound,
upperBound,
(AnnotatedTypeVariable) result);
return null;
}
protected void setFieldsForClonedTypeVariable(
AnnotatedTypeVariable origType,
AnnotatedTypeMirror lowerBound,
AnnotatedTypeMirror upperBound,
AnnotatedTypeVariable result)
{
result.setLowerBound(lowerBound);
result.setUpperBound(upperBound);
}
public Void visitWildcard(AnnotatedWildcardType type,
AnnotatedTypeMirror result) {
AnnotatedTypeMirror extendsBound = scan(type.getExtendsBound());
AnnotatedTypeMirror superBound = scan(type.getSuperBound());
setFieldsForClonedWildcardType(
type,
extendsBound,
superBound,
(AnnotatedWildcardType) result);
return null;
}
protected void setFieldsForClonedWildcardType(
AnnotatedWildcardType origType,
AnnotatedTypeMirror extendsBound,
AnnotatedTypeMirror superBound,
AnnotatedWildcardType result)
{
result.setExtendsBound(extendsBound);
result.setSuperBound(superBound);
}
/** use {@link #clone(Object)} instead */
@Deprecated
public Void visit(AnnotatedTypeMirror type) {
return visit(type, null);
}
/** use {@link #clone(Object)} instead */
@Deprecated
public Void visit(AnnotatedTypeMirror type, AnnotatedTypeMirror
result) {
throw new UnsupportedOperationException();
}
}
More information about the JSR308
mailing list