diff --git a/README.lex b/README.lex
new file mode 100644
index 0000000..ac6e114
--- /dev/null
+++ b/README.lex
@@ -0,0 +1,2 @@
+JAVA_HOME=/usr/local/soylatte16-amd64
+PATH=/Users/spoon/java/expannots/langtools/dist/bin:$JAVA_HOME/bin:$PATH
diff --git a/checkers/build.properties b/checkers/build.properties
index 922acf6..095067a 100644
--- a/checkers/build.properties
+++ b/checkers/build.properties
@@ -2,8 +2,8 @@
 ## checkers and framework.
 
 # The location of the JSR 308 OpenJDK distribution.
-compiler.lib=../../jsr308-langtools/dist/lib/javac.jar:../jsr308-langtools/dist/lib/javac.jar
-javadoc.lib=../../jsr308-langtools/dist/lib/javadoc.jar:../../jsr308-langtools/dist/lib/doclets.jar
+#compiler.lib=../../jsr308-langtools/dist/lib/javac.jar:../langtools/binary/javac.jar
+compiler.lib=../langtools/dist/lib/javac.jar
 
 # The location of the annotation file utilities.
 annotation-utils.lib=../annotation-file-utilities/annotation-file-utilities.jar:lib/asmx.jar
diff --git a/checkers/build.xml b/checkers/build.xml
index 9e40fb1..524d389 100644
--- a/checkers/build.xml
+++ b/checkers/build.xml
@@ -146,7 +146,7 @@ ${build.version} of the 'javac.jar' library."/>
             <arg line="-version"/>
         </java>
         <javadoc sourcepath="${src}" destdir="${dist.doc}" failonerror="true"
-		 executable="../../jsr308-langtools/dist/bin/javadoc"
+		 executable="../langtools/dist/bin/javadoc"
           classpath="${build}:${compiler.lib}:${javadoc.lib}"
           excludepackagenames="checkers.util.stub,checkers.util.dist"
           bootclasspath="${compiler.lib}:${javadoc.lib}:${java.home}/lib/rt.jar">
@@ -173,6 +173,7 @@ ${build.version} of the 'javac.jar' library."/>
             <include name="**/quals/*"/>
             <!-- Watch out for inner anonymous class within utilities -->
             <include name="**/NullnessUtils*"/>
+            <include name="**/model/*"/>
         </jar>
     </target>
 
@@ -345,7 +346,7 @@ ${build.version} of the 'javac.jar' library."/>
       Binary release of the Checker Framework; it includes javac.
      -->
 
-    <property name="javac.lib" value="../../jsr308-langtools/dist/lib/javac.jar"/>
+    <property name="javac.lib" value="../langtools/dist/lib/javac.jar"/>
     <property name="checkers.lib" value="checkers.jar"/>
     <property name="bindist.file" value="binary/jsr308-all.jar"/>
     <property name="temp.dir" value="binary/buildbin"/>
diff --git a/checkers/src/checkers/units/AnnotationInterpreter.java b/checkers/src/checkers/units/AnnotationInterpreter.java
new file mode 100644
index 0000000..18fa7d4
--- /dev/null
+++ b/checkers/src/checkers/units/AnnotationInterpreter.java
@@ -0,0 +1,291 @@
+package checkers.units;
+
+import static checkers.units.CheckerDimension.WILDCARD;
+import static java.util.Collections.singletonList;
+import checkers.types.AnnotatedTypeMirror;
+import checkers.units.CheckerDimension.*;
+import checkers.units.model.Dimension;
+import checkers.units.quals.Dim;
+import checkers.util.*;
+import checkers.util.AnnotationUtils.AnnotationBuilder;
+import com.sun.tools.javac.util.List;
+import com.sun.source.tree.*;
+import com.sun.tools.javac.code.Symbol;
+import com.sun.tools.javac.processing.JavacProcessingEnvironment;
+import com.sun.tools.javac.tree.TreeMaker;
+import com.sun.tools.javac.tree.JCTree.JCExpression;
+
+import java.util.*;
+
+import javax.annotation.processing.ProcessingEnvironment;
+import javax.lang.model.element.*;
+import javax.lang.model.type.TypeMirror;
+import javax.lang.model.util.*;
+
+/**
+ * This class converts {@link checkers.units.quals.Dim} annotations to and from
+ * instances of {@link CheckerDimension}.
+ */
+public class AnnotationInterpreter {
+    /**
+     * An exception indicating than an annotation is not a valid dimension.
+     */
+    public static class InvalidAnnotation extends Exception {
+	public InvalidAnnotation(String message) {
+	    super(message);
+	}
+    }
+
+    private final AnnotationUtils annoFactory;
+    private final Elements elements;
+    private final TreeMaker treeMaker;
+    private final Types types;
+    private final ProcessingEnvironment env;
+
+    public AnnotationInterpreter(ProcessingEnvironment env) {
+	this.env = env;
+	this.elements = env.getElementUtils();
+	this.types = env.getTypeUtils();
+	this.annoFactory = AnnotationUtils.getInstance(env);
+	/**
+	 * TODO(spoon) The checker framework needs to have an abstraction for
+	 * tree making.
+	 */
+	this.treeMaker = TreeMaker.instance(((JavacProcessingEnvironment) env)
+		.getContext());
+    }
+
+    public CheckerDimension interpretAsDimension(
+	    AnnotatedTypeMirror annotatedType) throws InvalidAnnotation {
+	return interpretAsDimension(annotatedType.getAnnotation(Dim.class
+		.getCanonicalName()));
+    }
+
+    /**
+     * Convert an annotation to a dimension. Converts <code>null</code> to the
+     * wildcard dimension.
+     */
+    public CheckerDimension interpretAsDimension(AnnotationMirror annot)
+	    throws InvalidAnnotation {
+
+	if (annot == null) {
+	    return WILDCARD;
+	}
+	assert (((TypeElement) annot.getAnnotationType().asElement())
+		.getQualifiedName().toString()
+		.equals("checkers.units.qual.Dim"));
+
+	Map<? extends ExecutableElement, ? extends AnnotationValue> annotValues = annot
+		.getElementValues();
+	assert (annotValues.size() == 1);
+	Object annotValue = annotValues.entrySet().iterator().next().getValue()
+		.getValue();
+	if (annotValue instanceof ExpressionTree) {
+	    return interpretAsDimension((ExpressionTree) annotValue);
+	}
+	return null;
+
+    }
+
+    /**
+     * Check whether a method symbol corresponds to
+     * {@link Dimension#div(Dimension)}.
+     */
+    public boolean isDimensionDiv(ExecutableElement methodSymbol) {
+	return isMethod(methodSymbol, Dimension.class, "div", Dimension.class);
+    }
+
+    /**
+     * Check whether <code>methodSymbol</code> specifies the method named
+     * <code>methodName</code> in class <code>methodClass</code>, with parameter
+     * type <code>paramClass</code>
+     */
+    public boolean isMethod(ExecutableElement methodSymbol,
+	    Class<?> methodClass, String methodName, Class<?> paramClass) {
+	if (methodSymbol == null) {
+	    return false;
+	}
+	if (!(methodSymbol.getEnclosingElement() instanceof TypeElement)) {
+	    return false;
+	}
+
+	String methodClassName = methodClass.getCanonicalName();
+	String paramClassName = paramClass.getCanonicalName();
+
+	TypeElement methodClassSymbol = (TypeElement) methodSymbol
+		.getEnclosingElement();
+	if (methodName.equals(methodSymbol.getSimpleName().toString())
+		&& methodClassName.equals(methodClassSymbol.getQualifiedName()
+			.toString())
+		&& methodSymbol.getParameters().size() == 1) {
+	    TypeMirror paramType = methodSymbol.getParameters().get(0).asType();
+	    Element paramTypeElement = types.asElement(paramType);
+	    if (paramTypeElement instanceof TypeElement) {
+		if (((TypeElement) paramTypeElement).getQualifiedName()
+			.toString().equals(paramClassName)) {
+		    return true;
+		}
+	    }
+	}
+
+	return false;
+    }
+
+    /**
+     * Convert a dimension back to an annotation.
+     */
+    public AnnotationMirror makeAnnotationFor(CheckerDimension dimension) {
+	ExpressionTree tree = makeTreeFor(dimension);
+	if (tree == null) {
+	    return null;
+	}
+	AnnotationBuilder annotBuilder = new AnnotationBuilder(env, Dim.class
+		.getCanonicalName());
+
+	annotBuilder.setValue("value", tree);
+	return annotBuilder.build();
+    }
+
+    /**
+     * Look up a method in the given class, with the given name, and whose sole
+     * argument has a type with the given class.
+     */
+    private Symbol lookupMethod(Class<Dimension> enclosingClazz, String name,
+	    Class<Dimension> argClazz) {
+
+	Name clazzName = elements.getName(enclosingClazz.getCanonicalName());
+	Name elementName = elements.getName(name);
+	Name argName = elements.getName(argClazz.getCanonicalName());
+
+	TypeElement typeElement = elements.getTypeElement(clazzName);
+	for (Element elem : elements.getAllMembers(typeElement)) {
+	    if (elem.getSimpleName().equals(elementName)) {
+		if (elem instanceof ExecutableElement) {
+		    ExecutableElement methodElem = (ExecutableElement) elem;
+		    if (methodElem.getParameters().size() == 1) {
+			TypeMirror methodArgType = methodElem.getParameters()
+				.get(0).asType();
+			Element methodArgTypeElement = types
+				.asElement(methodArgType);
+			if (methodArgTypeElement instanceof TypeElement) {
+			    if (((TypeElement) methodArgTypeElement)
+				    .getQualifiedName().equals(argName)) {
+				return (Symbol) elem;
+			    }
+			}
+		    }
+		}
+	    }
+	}
+	// TODO(spoon) there must be an internal processor exception? If
+	// not, make one for the units checker
+	throw new RuntimeException("Could not find member " + name
+		+ " in type " + clazzName);
+    }
+
+    /**
+     * Look up a field in the program being compiled whose name is
+     * <code>name</code> and enclosing class is <code>clazz</code>.
+     */
+    private Symbol lookupField(Class<Dimension> clazz, String name) {
+	String clazzName = clazz.getCanonicalName();
+	TypeElement typeElement = elements.getTypeElement(clazzName);
+	Name elementName = elements.getName(name);
+	for (Element elem : elements.getAllMembers(typeElement)) {
+	    if (elem.getSimpleName().equals(elementName)) {
+		return (Symbol) elem;
+	    }
+	}
+	// TODO(spoon) there must be an internal processor exception? If
+	// not, make one for the units checker
+	throw new RuntimeException("Could not find member " + name
+		+ " in type " + clazzName);
+    }
+
+    private CheckerDimension interpretAsDimension(ExpressionTree expr)
+	    throws InvalidAnnotation {
+	// check for a raw field reference
+	Element element = InternalUtils.symbol(expr);
+	if (element != null && (element instanceof VariableElement)) {
+	    // TODO(spoon): also check that the field is a valid Dimension
+	    // holder.
+	    // It must be a static, final, field
+	    return new FieldDimension((VariableElement) element);
+	}
+
+	// check for a call to Dimension.times or Dimension.div
+	if (expr instanceof MethodInvocationTree) {
+	    MethodInvocationTree methodInvocation = (MethodInvocationTree) expr;
+	    ExpressionTree methodSelect = methodInvocation.getMethodSelect();
+	    ExecutableElement methodSymbol = (ExecutableElement) InternalUtils
+		    .symbol(methodSelect);
+	    if (methodSelect instanceof MemberSelectTree
+		    && isDimensionDiv(methodSymbol)) {
+		MemberSelectTree methodSelectAsSelect = (MemberSelectTree) methodSelect;
+		CheckerDimension left = interpretAsDimension(methodSelectAsSelect
+			.getExpression());
+		CheckerDimension right = interpretAsDimension(methodInvocation
+			.getArguments().get(0));
+		return left.div(right);
+	    }
+	}
+
+	throw new InvalidAnnotation("Annotation not in a valid dimension form");
+    }
+
+    private JCExpression makeTreeFor(CheckerDimension dim) {
+	final JCExpression[] result = new JCExpression[1];
+	dim.accept(new Visitor() {
+	    @Override
+	    public void visitCompound(CompoundDimension dim) {
+		// first do the numerator
+		if (dim.getNumer().isEmpty()) {
+		    result[0] = makeTreeFor(ScalarDimension.SCALAR);
+		} else {
+		    result[0] = makeTreeFor(dim.getNumer().get(0));
+		    boolean first = true;
+		    for (FieldDimension d : dim.getNumer()) {
+			if (first) {
+			    first = false;
+			} else {
+			    Symbol dimTimesDimSymbol = lookupMethod(
+				    Dimension.class, "times", Dimension.class);
+			    JCExpression selectTimes = treeMaker.Select(
+				    result[0], dimTimesDimSymbol);
+			    List<JCExpression> noTypeArgs = List.nil();
+			    result[0] = treeMaker.Apply(noTypeArgs,
+				    selectTimes, List.of(makeTreeFor(d)));
+			}
+		    }
+		}
+
+		for (FieldDimension d : dim.getDenom()) {
+		    Symbol dimDivDimSymbol = lookupMethod(Dimension.class,
+			    "div", Dimension.class);
+		    JCExpression selectDiv = treeMaker.Select(result[0],
+			    dimDivDimSymbol);
+		    List<JCExpression> noTypeArgs = List.nil();
+		    result[0] = treeMaker.Apply(noTypeArgs, selectDiv, List
+			    .of(makeTreeFor(d)));
+		}
+	    }
+
+	    @Override
+	    public void visitField(FieldDimension dim) {
+		result[0] = treeMaker.Ident((Symbol) dim.getField());
+	    }
+
+	    @Override
+	    public void visitScalar(ScalarDimension dim) {
+		Symbol scalarSymbol = lookupField(Dimension.class, "SCALAR");
+		result[0] = treeMaker.Ident(scalarSymbol);
+	    }
+
+	    @Override
+	    public void visitWildcard(WildcardDimension dim) {
+		result[0] = null;
+	    }
+	});
+	return result[0];
+    }
+}
diff --git a/checkers/src/checkers/units/CheckerDimension.java b/checkers/src/checkers/units/CheckerDimension.java
new file mode 100644
index 0000000..d225cd6
--- /dev/null
+++ b/checkers/src/checkers/units/CheckerDimension.java
@@ -0,0 +1,271 @@
+package checkers.units;
+
+import checkers.units.model.UnitsModelUtils;
+
+import java.util.*;
+
+import javax.lang.model.element.VariableElement;
+
+/**
+ * A checker dimension is a dimension as represented inside the
+ * {@link UnitsChecker}.
+ */
+abstract class CheckerDimension {
+    public static class CompoundDimension extends CheckerDimension {
+	private final List<FieldDimension> denom;
+	private final List<FieldDimension> numer;
+
+	CompoundDimension(List<FieldDimension> numer, List<FieldDimension> denom) {
+	    this.numer = numer;
+	    this.denom = denom;
+	}
+
+	@Override
+	public void accept(Visitor visitor) {
+	    visitor.visitCompound(this);
+	}
+
+	@Override
+	public boolean equals(Object obj) {
+	    if (!(obj instanceof CompoundDimension)) {
+		return false;
+	    }
+	    CompoundDimension that = (CompoundDimension) obj;
+	    return isPermutation(numer, that.getNumer())
+		    && isPermutation(denom, that.getDenom());
+	}
+
+	@Override
+	public List<FieldDimension> getDenom() {
+	    return denom;
+	}
+
+	@Override
+	public List<FieldDimension> getNumer() {
+	    return numer;
+	}
+
+    }
+
+    /**
+     * A simple dimension defined by reference to a static field.
+     */
+    public static class FieldDimension extends CheckerDimension {
+	private final VariableElement field;
+
+	public FieldDimension(VariableElement field) {
+	    this.field = field;
+	}
+
+	@Override
+	public void accept(Visitor visitor) {
+	    visitor.visitField(this);
+	}
+
+	@Override
+	public boolean equals(Object obj) {
+	    if (!(obj instanceof FieldDimension)) {
+		return false;
+	    }
+	    FieldDimension that = (FieldDimension) obj;
+	    return sameField(field, that.getField());
+	}
+
+	public VariableElement getField() {
+	    return field;
+	}
+
+	@Override
+	public int hashCode() {
+	    return field.getSimpleName().hashCode() * 17
+		    + field.getEnclosingElement().hashCode();
+	}
+
+	@Override
+	public String toString() {
+	    return field.toString();
+	}
+
+	@Override
+	protected List<FieldDimension> getDenom() {
+	    return NO_DIMENSIONS;
+	}
+
+	@Override
+	protected List<FieldDimension> getNumer() {
+	    return Collections.singletonList(this);
+	}
+    }
+
+    public static class ScalarDimension extends CheckerDimension {
+
+	@Override
+	public void accept(Visitor visitor) {
+	    visitor.visitScalar(this);
+	}
+
+	@Override
+	protected List<FieldDimension> getDenom() {
+	    return NO_DIMENSIONS;
+	}
+
+	@Override
+	protected List<FieldDimension> getNumer() {
+	    return NO_DIMENSIONS;
+	}
+    }
+
+    public interface Visitor {
+	void visitCompound(CompoundDimension dim);
+
+	void visitField(FieldDimension dim);
+
+	void visitScalar(ScalarDimension dim);
+
+	void visitWildcard(WildcardDimension dim);
+    }
+
+    /**
+     * A wildcard dimension represents a completely unknown dimension, in the
+     * gradual-typing style.
+     */
+    public static class WildcardDimension extends CheckerDimension {
+	@Override
+	public void accept(Visitor visitor) {
+	    visitor.visitWildcard(this);
+	}
+
+	@Override
+	protected List<FieldDimension> getDenom() {
+	    throw new AssertionError("Should not be called");
+	}
+
+	@Override
+	protected List<FieldDimension> getNumer() {
+	    throw new AssertionError("Should not be called");
+	}
+    }
+
+    public static ScalarDimension SCALAR = new ScalarDimension();
+
+    public static WildcardDimension WILDCARD = new WildcardDimension();
+
+    @SuppressWarnings("unchecked")
+    private static final List<FieldDimension> NO_DIMENSIONS = Collections.EMPTY_LIST;
+
+    public static CheckerDimension normalize(List<FieldDimension> numer,
+	    List<FieldDimension> denom) {
+
+	List<FieldDimension> nums = new ArrayList<FieldDimension>(numer);
+	List<FieldDimension> dens = new ArrayList<FieldDimension>(denom);
+
+	for (FieldDimension d : denom) {
+	    if (nums.contains(d)) {
+		nums.remove(d);
+		dens.remove(d);
+	    }
+	}
+
+	nums = UnitsModelUtils.roughSort(nums);
+	dens = UnitsModelUtils.roughSort(dens);
+
+	if (nums.isEmpty() && dens.isEmpty()) {
+	    return SCALAR;
+	}
+	if (nums.size() == 1 && dens.isEmpty()) {
+	    return nums.get(0);
+	}
+	return new CompoundDimension(nums, dens);
+    }
+
+    private static <T> boolean isPermutation(List<T> list1, List<T> list2) {
+	if (list1.size() != list2.size()) {
+	    return false;
+	}
+
+	List<T> list2left = new ArrayList<T>(list2);
+	for (T x : list1) {
+	    if (!list2left.remove(x)) {
+		return false;
+	    }
+	}
+	return true;
+    }
+
+    private static boolean sameField(VariableElement field1,
+	    VariableElement field2) {
+	/*
+	 * For some reason, the Javac ClassReader neither hash-conses field
+	 * reference symbols nor implements equals() for them. Thus, compare the
+	 * classes and compare the names.
+	 */
+	return field1.getEnclosingElement()
+		.equals(field2.getEnclosingElement())
+		&& field1.getSimpleName().equals(field2.getSimpleName());
+    }
+
+    protected CheckerDimension() {
+    }
+
+    public abstract void accept(Visitor visitor);
+
+    /**
+     * Give the result of adding or subtracting measures with the specified
+     * dimensions. Returns <code>null</code> if the dimensions are incompatible.
+     */
+    public CheckerDimension combine(CheckerDimension that) {
+	if (this == WILDCARD) {
+	    return that;
+	}
+	if (that == WILDCARD) {
+	    return this;
+	}
+	if (isAssignableTo(that)) {
+	    return that;
+	}
+	return null;
+    }
+
+    public CheckerDimension div(CheckerDimension that) {
+	if (this == WILDCARD || that == WILDCARD) {
+	    return WILDCARD;
+	}
+	List<FieldDimension> newNumer = new ArrayList<FieldDimension>();
+	newNumer.addAll(getNumer());
+	newNumer.addAll(that.getDenom());
+
+	List<FieldDimension> newDenom = new ArrayList<FieldDimension>();
+	newDenom.addAll(getDenom());
+	newDenom.addAll(that.getNumer());
+
+	return normalize(newNumer, newDenom);
+    }
+
+    public boolean isAssignableTo(CheckerDimension that) {
+	if (this == WILDCARD || that == WILDCARD) {
+	    return true;
+	}
+
+	return isPermutation(getNumer(), that.getNumer())
+		&& isPermutation(getDenom(), that.getDenom());
+    }
+
+    public CheckerDimension times(CheckerDimension that) {
+	if (this == WILDCARD || that == WILDCARD) {
+	    return WILDCARD;
+	}
+	List<FieldDimension> newNumer = new ArrayList<FieldDimension>();
+	newNumer.addAll(getNumer());
+	newNumer.addAll(that.getNumer());
+
+	List<FieldDimension> newDenom = new ArrayList<FieldDimension>();
+	newDenom.addAll(getDenom());
+	newDenom.addAll(that.getDenom());
+
+	return normalize(newNumer, newDenom);
+    }
+
+    protected abstract List<FieldDimension> getDenom();
+
+    protected abstract List<FieldDimension> getNumer();
+}
diff --git a/checkers/src/checkers/units/UnitsChecker.java b/checkers/src/checkers/units/UnitsChecker.java
new file mode 100644
index 0000000..cf22d21
--- /dev/null
+++ b/checkers/src/checkers/units/UnitsChecker.java
@@ -0,0 +1,169 @@
+package checkers.units;
+
+import checkers.basetype.BaseTypeChecker;
+import checkers.quals.TypeQualifiers;
+import checkers.source.Result;
+import checkers.types.*;
+import checkers.units.AnnotationInterpreter.InvalidAnnotation;
+import checkers.units.model.Measure;
+import checkers.units.quals.Dim;
+import checkers.util.InternalUtils;
+
+import com.sun.source.tree.*;
+
+import java.util.*;
+
+import javax.lang.model.element.*;
+import javax.lang.model.type.*;
+
+@TypeQualifiers( { Dim.class })
+public class UnitsChecker extends BaseTypeChecker {
+    public class UnitsQualifierHierarchy extends QualifierHierarchy {
+	private final AnnotationInterpreter annotInterp = new AnnotationInterpreter(
+		getProcessingEnvironment());
+
+	@Override
+	public AnnotationMirror getRootAnnotation() {
+	    // There isn't an ultimate root type in this system
+	    return null;
+	}
+
+	@Override
+	public Set<Name> getTypeQualifiers() {
+	    return Collections.singleton(env.getElementUtils().getName(
+		    Dim.class.getName()));
+	}
+
+	@Override
+	public boolean isSubtype(AnnotationMirror a1, AnnotationMirror a2) {
+	    CheckerDimension dim1;
+	    CheckerDimension dim2;
+	    try {
+		dim1 = annotInterp.interpretAsDimension(a1);
+	    } catch (InvalidAnnotation e) {
+		// TODO(spoon) how to report this to the user?
+		return false;
+	    }
+	    try {
+		dim2 = annotInterp.interpretAsDimension(a2);
+	    } catch (InvalidAnnotation e) {
+		return false;
+	    }
+
+	    return dim1.isAssignableTo(dim2);
+	}
+
+	@Override
+	public AnnotationMirror leastUpperBound(AnnotationMirror a1,
+		AnnotationMirror a2) {
+	    // TODO(spoon)
+	    return a1;
+	}
+
+    }
+
+    public class UnitsTypeFactory extends AnnotatedTypeFactory {
+	private final AnnotationInterpreter annotInterp = new AnnotationInterpreter(
+		getProcessingEnvironment());
+
+	public UnitsTypeFactory(CompilationUnitTree root) {
+	    super(UnitsChecker.this, root);
+	}
+
+	@Override
+	public void annotateImplicit(Tree tree, AnnotatedTypeMirror type) {
+	    try {
+		if (tree instanceof MethodInvocationTree) {
+		    /*
+		     * In a call to Unit.times(double), the units are the same
+		     * as the receiver
+		     */
+		    if (tree.getKind() == Tree.Kind.METHOD_INVOCATION) {
+			MethodInvocationTree treeInvoke = (MethodInvocationTree) tree;
+			MemberSelectTree method = (MemberSelectTree) treeInvoke
+				.getMethodSelect();
+			ExecutableElement methodSymbol = (ExecutableElement) InternalUtils
+				.symbol(method);
+			TypeElement methodClassSymbol = (TypeElement) methodSymbol
+				.getEnclosingElement();
+			if ("times".equals(methodSymbol.getSimpleName()
+				.toString())
+				&& "checkers.units.model.Unit"
+					.equals(methodClassSymbol
+						.getQualifiedName().toString())
+				&& methodSymbol.getParameters().size() == 1) {
+			    TypeMirror paramType = getAnnotatedType(
+				    methodSymbol.getParameters().get(0))
+				    .getUnderlyingType();
+			    if (paramType.getKind() == TypeKind.DOUBLE) {
+				AnnotatedTypeMirror receiverType = getAnnotatedType(method
+					.getExpression());
+				type.addAnnotations(receiverType
+					.getAnnotations());
+				return;
+			    }
+			}
+
+			if (annotInterp.isMethod(methodSymbol, Measure.class,
+				"div", Measure.class)) {
+			    CheckerDimension left = annotInterp
+				    .interpretAsDimension(getAnnotatedType(method
+					    .getExpression()));
+			    CheckerDimension right = annotInterp
+				    .interpretAsDimension(getAnnotatedType(treeInvoke
+					    .getArguments().get(0)));
+			    CheckerDimension result = left.div(right);
+			    type.addAnnotation(makeAnnotationFor(result));
+			}
+
+			if (annotInterp.isMethod(methodSymbol, Measure.class,
+				"plus", Measure.class)
+				|| annotInterp.isMethod(methodSymbol,
+					Measure.class, "minus", Measure.class)) {
+			    CheckerDimension left = annotInterp
+				    .interpretAsDimension(getAnnotatedType(method
+					    .getExpression()));
+			    CheckerDimension right = annotInterp
+				    .interpretAsDimension(getAnnotatedType(treeInvoke
+					    .getArguments().get(0)));
+			    CheckerDimension result = left;
+			    if (left == CheckerDimension.WILDCARD) {
+				// If one side is a wildcard, use the other side.
+				// If both are wildcards, then use wildcard.
+				result = right;
+			    }
+			    if (!left.isAssignableTo(right)) {
+				// TODO(spoon) localize this error
+				report(Result.failure(
+					"Incompatible units: %s vs. %s", left,
+					right), tree);
+				result = CheckerDimension.WILDCARD;
+			    }
+			    AnnotationMirror resultAnnot = makeAnnotationFor(result);
+			    if (resultAnnot != null) {
+				type.addAnnotation(resultAnnot);
+			    }
+			}
+		    }
+		}
+	    } catch (InvalidAnnotation e) {
+		// Quietly do nothing. The error should be caught and reported
+		// elsewhere.
+	    }
+	}
+
+	private AnnotationMirror makeAnnotationFor(CheckerDimension dim) {
+	    return annotInterp.makeAnnotationFor(dim);
+	}
+    }
+
+    @Override
+    public AnnotatedTypeFactory createFactory(CompilationUnitTree root) {
+	return new UnitsTypeFactory(root);
+    }
+
+    @Override
+    public QualifierHierarchy createQualifierHierarchy() {
+	return new UnitsQualifierHierarchy();
+    }
+}
diff --git a/checkers/src/checkers/units/model/Dimension.java b/checkers/src/checkers/units/model/Dimension.java
new file mode 100644
index 0000000..a8e4cdb
--- /dev/null
+++ b/checkers/src/checkers/units/model/Dimension.java
@@ -0,0 +1,230 @@
+package checkers.units.model;
+
+import java.util.*;
+
+/**
+ * The dimension of a unit, such as length, time, or length divided by time. Two
+ * units are compatible if their dimension is the same.
+ */
+public abstract class Dimension {
+    /**
+     * A simple dimension is the most atomic element of a compound dimension.
+     * Instantiate this class to create a new kind of dimension for a particular
+     * application. Simple dimensions can be compared with object equality.
+     */
+    public static class SimpleDimension extends Dimension {
+	private final String name;
+
+	public SimpleDimension(String name) {
+	    this.name = name;
+	}
+
+	@Override
+	public String toString() {
+	    return name;
+	}
+
+	@Override
+	protected List<SimpleDimension> getDenominator() {
+	    return NO_DIMENSIONS;
+	}
+
+	@Override
+	protected List<SimpleDimension> getNumerator() {
+	    return singleton(this);
+	}
+    }
+
+    static class CompoundDimension extends Dimension {
+	List<SimpleDimension> denom;
+
+	List<SimpleDimension> numer;
+
+	public CompoundDimension(List<SimpleDimension> numer,
+		List<SimpleDimension> denom) {
+	    assert (numer.size() != 0 || denom.size() != 0);
+	    assert (denom.size() > 0 || numer.size() > 1);
+	    this.numer = numer;
+	    this.denom = denom;
+	}
+
+	@Override
+	public boolean equals(Object object) {
+	    if (!(object instanceof CompoundDimension)) {
+		return false;
+	    }
+	    CompoundDimension that = (CompoundDimension) object;
+	    return (sameElements(this.getNumerator(), that.getNumerator()) && sameElements(
+		    this.getDenominator(), that.getDenominator()));
+	}
+
+	@Override
+	public List<SimpleDimension> getDenominator() {
+	    return denom;
+	}
+
+	@Override
+	public List<SimpleDimension> getNumerator() {
+	    return numer;
+	}
+
+	@Override
+	public int hashCode() {
+	    int code = 31;
+	    for (SimpleDimension dim : numer) {
+		code = code * 17 + dim.hashCode();
+	    }
+	    for (SimpleDimension dim : denom) {
+		code = code * 17 + dim.hashCode();
+	    }
+	    return code;
+	}
+
+	@Override
+	public String toString() {
+	    StringBuilder sb = new StringBuilder();
+	    if (numer.size() == 0) {
+		sb.append("1");
+	    } else {
+		boolean first = true;
+		for (SimpleDimension dim : numer) {
+		    if (first) {
+			first = false;
+		    } else {
+			sb.append("*");
+		    }
+		    sb.append(dim);
+		}
+	    }
+	    if (denom.size() > 0) {
+		sb.append("/");
+		if (denom.size() > 1) {
+		    sb.append("(");
+		}
+		boolean first = true;
+		for (SimpleDimension dim : denom) {
+		    if (first) {
+			first = false;
+		    } else {
+			sb.append("*");
+		    }
+		    sb.append(dim);
+		}
+		if (denom.size() > 1) {
+		    sb.append(")");
+		}
+	    }
+	    return sb.toString();
+	}
+
+	private boolean sameElements(List<SimpleDimension> list1,
+		List<SimpleDimension> list2) {
+	    if (list1.size() != list2.size()) {
+		return false;
+	    }
+	    List<SimpleDimension> list2left = new ArrayList<SimpleDimension>(
+		    list2);
+	    for (SimpleDimension dim1 : list1) {
+		if (!list2left.contains(dim1)) {
+		    return false;
+		}
+		list2left.remove(dim1);
+	    }
+	    assert list2left.isEmpty();
+	    return true;
+	}
+    }
+
+    private static class ScalarDimension extends Dimension {
+	@Override
+	public String toString() {
+	    return "ScalarDimension";
+	}
+
+	@Override
+	protected List<SimpleDimension> getDenominator() {
+	    return NO_DIMENSIONS;
+	}
+
+	@Override
+	protected List<SimpleDimension> getNumerator() {
+	    return NO_DIMENSIONS;
+	}
+    }
+
+    @SuppressWarnings("unchecked")
+    private static final List<SimpleDimension> NO_DIMENSIONS = Collections.EMPTY_LIST;
+
+    /**
+     * The dimension of scalar values.
+     */
+    public static final Dimension SCALAR = new ScalarDimension();
+
+    private static Dimension normalize(List<SimpleDimension> numer,
+	    List<SimpleDimension> denom) {
+	List<SimpleDimension> nums = new ArrayList<SimpleDimension>(numer);
+	List<SimpleDimension> dens = new ArrayList<SimpleDimension>(denom);
+
+	for (SimpleDimension d : denom) {
+	    if (nums.contains(d)) {
+		nums.remove(d);
+		dens.remove(d);
+	    }
+	}
+
+	nums = UnitsModelUtils.roughSort(nums);
+	dens = UnitsModelUtils.roughSort(dens);
+
+	if (nums.isEmpty() && dens.isEmpty()) {
+	    return SCALAR;
+	}
+	if (nums.size() == 1 && dens.isEmpty()) {
+	    return nums.get(0);
+	}
+	return new CompoundDimension(nums, dens);
+    }
+
+    private static <T> List<T> singleton(T elem) {
+	return Collections.singletonList(elem);
+    }
+
+    public Dimension div(Dimension that) {
+	List<SimpleDimension> newnum = new ArrayList<SimpleDimension>();
+	newnum.addAll(this.getNumerator());
+	newnum.addAll(that.getDenominator());
+	List<SimpleDimension> newden = new ArrayList<SimpleDimension>();
+	newden.addAll(this.getDenominator());
+	newden.addAll(that.getNumerator());
+
+	return normalize(newnum, newden);
+    }
+
+    public Dimension exp(int exponent) {
+	List<SimpleDimension> newnum = new ArrayList<SimpleDimension>();
+	for (int i = 0; i < exponent; i++) {
+	    newnum.addAll(getNumerator());
+	}
+	List<SimpleDimension> newden = new ArrayList<SimpleDimension>();
+	for (int i = 0; i < exponent; i++) {
+	    newden.addAll(getDenominator());
+	}
+
+	return normalize(newnum, newden);
+
+    }
+
+    public Dimension times(Dimension that) {
+	List<SimpleDimension> newnum = new ArrayList<SimpleDimension>();
+	newnum.addAll(this.getNumerator());
+	newnum.addAll(that.getNumerator());
+	List<SimpleDimension> newden = new ArrayList<SimpleDimension>();
+	newden.addAll(this.getDenominator());
+	newden.addAll(that.getDenominator());
+
+	return normalize(newnum, newden);
+    }
+
+    protected abstract List<SimpleDimension> getDenominator();
+
+    protected abstract List<SimpleDimension> getNumerator();
+}
diff --git a/checkers/src/checkers/units/model/IncompatibleUnitsError.java b/checkers/src/checkers/units/model/IncompatibleUnitsError.java
new file mode 100644
index 0000000..173670e
--- /dev/null
+++ b/checkers/src/checkers/units/model/IncompatibleUnitsError.java
@@ -0,0 +1,17 @@
+package checkers.units.model;
+
+import checkers.units.model.Unit;
+
+/**
+ * An exception indicating an attempt to perform arithmetic on incompatible
+ * units.
+ */
+public class IncompatibleUnitsError extends RuntimeException {
+    public IncompatibleUnitsError(String msg) {
+	super(msg);
+    }
+
+    public IncompatibleUnitsError(Unit unit1, Unit unit2) {
+	this("Incompatible units: " + unit1 + " vs. " + unit2);
+    }
+}
diff --git a/checkers/src/checkers/units/model/Measure.java b/checkers/src/checkers/units/model/Measure.java
new file mode 100644
index 0000000..1cd0072
--- /dev/null
+++ b/checkers/src/checkers/units/model/Measure.java
@@ -0,0 +1,287 @@
+package checkers.units.model;
+
+public abstract class Measure implements Comparable<Measure> {
+    public static class SimpleMeasure extends Measure {
+	private final double scale;
+	private final Unit unit;
+
+	public SimpleMeasure(double scale, Unit unit) {
+	    this.scale = scale;
+	    this.unit = unit;
+	}
+
+	@Override
+	public int compareTo(Measure that) {
+	    Measure thatInMyUnits = that.inUnit(unit);
+	    if (scale == thatInMyUnits.getScale()) {
+		return 0;
+	    }
+	    if (scale < thatInMyUnits.getScale()) {
+		return -1;
+	    }
+	    return 1;
+	}
+
+	@Override
+	public Measure div(double scale) {
+	    return make(this.getScale() / scale, this.getUnit());
+	}
+
+	@Override
+	public Measure div(Measure that) {
+	    final Measure[] result = new Measure[1];
+	    that.accept(new Visitor() {
+		@Override
+		public void visitSimple(SimpleMeasure that) {
+		    result[0] = unit.div(that.getUnit()).times(
+			    scale / that.getScale());
+		}
+
+		@Override
+		public void visitZero(ZeroMeasure that) {
+		    result[0] = that;
+		}
+	    });
+	    return result[0];
+	}
+
+	@Override
+	public boolean equals(Object obj) {
+	    if (!(obj instanceof Measure)) {
+		return false;
+	    }
+	    return compareTo((Measure) obj) == 0;
+	}
+
+	@Override
+	public Measure exp(int k) {
+	    return make(Math.pow(scale, k), unit.exp(k));
+	}
+
+	@Override
+	public double getScale() {
+	    return scale;
+	}
+
+	public Unit getUnit() {
+	    return unit;
+	}
+
+	@Override
+	public int hashCode() {
+	    return ((Double) scale).hashCode() * 17 + unit.hashCode();
+	}
+
+	@Override
+	public Measure inUnit(Unit newUnit) {
+	    SimpleMeasure scaleMeasure = (SimpleMeasure) newUnit.div(unit);
+	    if (scaleMeasure.getUnit() != Unit.SCALAR) {
+		throw new IncompatibleUnitsError(unit, newUnit);
+	    }
+	    return make(scale * scaleMeasure.getScale(), newUnit);
+	}
+
+	@Override
+	public Measure minus(Measure that) {
+	    Measure thatScaled = that.inUnit(unit);
+	    return make(scale - thatScaled.getScale(), unit);
+	}
+
+	@Override
+	public Measure plus(Measure that) {
+	    Measure thatScaled = that.inUnit(unit);
+	    return make(scale + thatScaled.getScale(), unit);
+	}
+
+	@Override
+	public Measure times(double scale) {
+	    return make(this.getScale() * scale, this.getUnit());
+	}
+
+	@Override
+	public Measure times(Measure that) {
+	    final Measure[] result = new Measure[1];
+	    that.accept(new Visitor() {
+		@Override
+		public void visitSimple(SimpleMeasure that) {
+		    result[0] = unit.times(that.getUnit()).times(
+			    scale * that.getScale());
+		}
+
+		@Override
+		public void visitZero(ZeroMeasure that) {
+		    result[0] = that;
+		}
+	    });
+	    return result[0];
+	}
+
+	@Override
+	public String toString() {
+	    if (unit == Unit.SCALAR) {
+		return "" + scale;
+	    } else {
+		return scale + " " + unit;
+	    }
+	}
+
+	@Override
+	void accept(Visitor visitor) {
+	    visitor.visitSimple(this);
+	}
+    }
+
+    private interface Visitor {
+	void visitSimple(SimpleMeasure measure);
+
+	void visitZero(ZeroMeasure measure);
+    }
+
+    private static class ZeroMeasure extends Measure {
+	@Override
+	public int compareTo(Measure o) {
+	    final int[] result = new int[1];
+	    o.accept(new Visitor() {
+		@Override
+		public void visitSimple(SimpleMeasure measure) {
+		    if (measure.getScale() > 0) {
+			result[0] = -1;
+		    } else {
+			result[0] = 1;
+		    }
+		}
+
+		@Override
+		public void visitZero(ZeroMeasure measure) {
+		    result[0] = 0;
+		}
+	    });
+	    return result[0];
+	}
+
+	@Override
+	public Measure div(double scale) {
+	    if (scale == 0.0) {
+		throw new ArithmeticException("division by zero");
+	    }
+	    return this;
+	}
+
+	@Override
+	public Measure div(Measure that) {
+	    return this;
+	}
+
+	@Override
+	public Measure exp(int k) {
+	    if (k == 0) {
+		throw new ArithmeticException("raising 0 to the 0 power");
+	    }
+	    return this;
+	}
+
+	@Override
+	public double getScale() {
+	    return 0;
+	}
+
+	@Override
+	public Measure inUnit(Unit unit) {
+	    return this;
+	}
+
+	@Override
+	public Measure minus(Measure that) {
+	    final Measure[] result = new Measure[1];
+	    that.accept(new Visitor() {
+
+		@Override
+		public void visitSimple(SimpleMeasure measure) {
+		    result[0] = new SimpleMeasure(-measure.getScale(), measure
+			    .getUnit());
+		}
+
+		@Override
+		public void visitZero(ZeroMeasure measure) {
+		    result[0] = ZERO;
+		}
+
+	    });
+	    return result[0];
+	}
+
+	@Override
+	public Measure plus(Measure that) {
+	    return that;
+	}
+
+	@Override
+	public Measure times(double scale) {
+	    return this;
+	}
+
+	@Override
+	public Measure times(Measure that) {
+	    return this;
+	}
+
+	@Override
+	public String toString() {
+	    return "0.0";
+	}
+
+	@Override
+	void accept(Visitor visitor) {
+	    visitor.visitZero(this);
+	}
+    }
+
+    /**
+     * The zero measure. It has no units, and it is compatible with all other
+     * measures.
+     */
+    private static Measure ZERO = new ZeroMeasure();
+
+    public static Measure make(double scale) {
+	return make(scale, Unit.SCALAR);
+    }
+
+    public static Measure make(double scale, Unit unit) {
+	if (scale == 0) {
+	    return ZERO;
+	} else {
+	    return new SimpleMeasure(scale, unit);
+	}
+    }
+
+    protected Measure() {
+    }
+
+    public abstract Measure div(double scale);
+
+    public abstract Measure div(Measure that);
+
+    public Measure div(Unit unit) {
+	return this.div(make(1.0, unit));
+    }
+
+    public abstract Measure exp(int k);
+
+    public abstract double getScale();
+
+    public abstract Measure inUnit(Unit unit);
+
+    public abstract Measure minus(Measure that);
+
+    public abstract Measure plus(Measure that);
+
+    public abstract Measure times(double scale);
+
+    public abstract Measure times(Measure that);
+
+    public Measure times(Unit unit) {
+	return this.times(make(1.0, unit));
+    }
+
+    abstract void accept(Visitor visitor);
+}
diff --git a/checkers/src/checkers/units/model/StandardUnits.java b/checkers/src/checkers/units/model/StandardUnits.java
new file mode 100644
index 0000000..4e9618b
--- /dev/null
+++ b/checkers/src/checkers/units/model/StandardUnits.java
@@ -0,0 +1,26 @@
+package checkers.units.model;
+
+import checkers.units.model.*;
+import checkers.units.model.Dimension.SimpleDimension;
+import checkers.units.model.Unit.SimpleUnit;
+import checkers.units.quals.Dim;
+
+public class StandardUnits {
+    public static final SimpleDimension Length = new SimpleDimension("Length");
+    public static final SimpleDimension Time = new SimpleDimension("Time");
+
+    public static final @Dim(Time)
+    Unit s = new SimpleUnit(Time, 1.0, "s");
+
+    public static final @Dim(Time)
+    Unit min = new SimpleUnit(Time, 60.0, "min");
+
+    public static final @Dim(Length)
+    Unit m = new SimpleUnit(Length, 1.0, "m");
+
+    public static final @Dim(Length)
+    Unit km = new SimpleUnit(Length, 1000.0, "km");
+
+    public static final @Dim(Length.div(Time))
+    Measure mps = m.div(s);
+}
diff --git a/checkers/src/checkers/units/model/Unit.java b/checkers/src/checkers/units/model/Unit.java
new file mode 100644
index 0000000..7b0c502
--- /dev/null
+++ b/checkers/src/checkers/units/model/Unit.java
@@ -0,0 +1,479 @@
+package checkers.units.model;
+
+import checkers.units.model.Dimension.SimpleDimension;
+
+import java.util.*;
+
+/**
+ * A unit of measure, such as the meter or the second. It has a
+ * {@link Dimension}, and it has a scale factor within that {@link Dimension}
+ * that can be used to convert to another unit in the same dimension.
+ */
+public abstract class Unit {
+    public static class SimpleUnit extends Unit {
+	private final SimpleDimension dimension;
+	private final String name;
+	private final double scale;
+
+	public SimpleUnit(SimpleDimension dimension, double scale, String name) {
+	    this.dimension = dimension;
+	    this.scale = scale;
+	    this.name = name;
+	}
+
+	@Override
+	public SimpleDimension getDimension() {
+	    return dimension;
+	}
+
+	@Override
+	public double getScale() {
+	    return scale;
+	}
+
+	@Override
+	public String toString() {
+	    return name;
+	}
+
+	@Override
+	void accept(Visitor visitor) {
+	    visitor.visitSimple(this);
+	}
+
+	@Override
+	public Measure div(Unit that) {
+	    final Measure[] result = new Measure[1];
+	    that.accept(new Visitor() {
+		@Override
+		public void visitCompound(CompoundUnit that) {
+		    List<SimpleUnit> newNumer = new ArrayList<SimpleUnit>();
+		    newNumer.add(SimpleUnit.this);
+		    newNumer.addAll(that.getDenom());
+		    result[0] = normalize(newNumer, that.getNumer());
+		}
+
+		@Override
+		public void visitScalar(ScalarUnit that) {
+		    result[0] = Measure.make(1.0, SimpleUnit.this);
+		}
+
+		@Override
+		public void visitSimple(SimpleUnit that) {
+		    result[0] = normalize(singleton(SimpleUnit.this),
+			    singleton(that));
+		}
+	    });
+	    return result[0];
+	}
+
+	@Override
+	public Unit exp(int k) {
+	    if (k == 0) {
+		return SCALAR;
+	    }
+	    List<SimpleUnit> copies = Collections.nCopies(Math.abs(k), this);
+	    if (k > 0) {
+		return new CompoundUnit(copies, NO_UNITS);
+	    } else {
+		return new CompoundUnit(NO_UNITS, copies);
+	    }
+	}
+
+	@Override
+	public Measure times(Unit that) {
+	    final Measure[] result = new Measure[1];
+	    that.accept(new Visitor() {
+		@Override
+		public void visitCompound(CompoundUnit that) {
+		    List<SimpleUnit> newNumer = new ArrayList<SimpleUnit>();
+		    newNumer.add(SimpleUnit.this);
+		    newNumer.addAll(that.getNumer());
+		    result[0] = normalize(newNumer, that.getDenom());
+		}
+
+		@Override
+		public void visitScalar(ScalarUnit that) {
+		    result[0] = Measure.make(1.0, SimpleUnit.this);
+		}
+
+		@Override
+		public void visitSimple(SimpleUnit that) {
+		    result[0] = normalize(singleton(SimpleUnit.this),
+			    singleton(that));
+		}
+	    });
+	    return result[0];
+	}
+    }
+
+    private static List<SimpleDimension> extractDimensions(
+	    List<SimpleUnit> units) {
+	List<SimpleDimension> dims = new ArrayList<SimpleDimension>(units
+		.size());
+	for (SimpleUnit unit : units) {
+	    dims.add(unit.getDimension());
+	}
+	return dims;
+    }
+
+    private static class CompoundUnit extends Unit {
+	private final List<SimpleUnit> denom;
+	private final List<SimpleUnit> numer;
+	private final Dimension dimension;
+
+	public CompoundUnit(List<SimpleUnit> numer, List<SimpleUnit> denom) {
+	    this.numer = numer;
+	    this.denom = denom;
+
+	    this.dimension = new Dimension.CompoundDimension(
+		    extractDimensions(numer), extractDimensions(denom));
+	}
+
+	public List<SimpleUnit> getDenom() {
+	    return denom;
+	}
+
+	@Override
+	public Dimension getDimension() {
+	    return dimension;
+	}
+
+	public List<SimpleUnit> getNumer() {
+	    return numer;
+	}
+
+	@Override
+	public double getScale() {
+	    double scale = 1;
+	    for (SimpleUnit unit : numer) {
+		scale *= unit.getScale();
+	    }
+	    for (SimpleUnit unit : denom) {
+		scale /= unit.getScale();
+	    }
+	    return scale;
+	}
+
+	@Override
+	public String toString() {
+	    StringBuilder sb = new StringBuilder();
+	    if (numer.size() == 0) {
+		sb.append("ScalarDimension");
+	    } else {
+		boolean first = true;
+		for (SimpleUnit dim : numer) {
+		    if (first) {
+			first = false;
+		    } else {
+			sb.append("*");
+		    }
+		    sb.append(dim);
+		}
+	    }
+	    if (denom.size() > 0) {
+		sb.append("/");
+		if (denom.size() > 1) {
+		    sb.append("(");
+		}
+		boolean first = true;
+		for (SimpleUnit dim : denom) {
+		    if (first) {
+			first = false;
+		    } else {
+			sb.append("*");
+		    }
+		    sb.append(dim);
+		}
+		if (denom.size() > 1) {
+		    sb.append(")");
+		}
+	    }
+	    return sb.toString();
+	}
+
+	@Override
+	void accept(Visitor visitor) {
+	    visitor.visitCompound(this);
+	}
+
+	@Override
+	public Measure div(Unit that) {
+	    final Measure[] result = new Measure[1];
+
+	    that.accept(new Visitor() {
+		@Override
+		public void visitCompound(CompoundUnit that) {
+		    List<SimpleUnit> newNumer = new ArrayList<SimpleUnit>();
+		    newNumer.addAll(numer);
+		    newNumer.addAll(that.getDenom());
+
+		    List<SimpleUnit> newDenom = new ArrayList<SimpleUnit>();
+		    newDenom.addAll(denom);
+		    newDenom.addAll(that.getNumer());
+
+		    result[0] = normalize(newNumer, newDenom);
+		}
+
+		@Override
+		public void visitScalar(ScalarUnit that) {
+		    result[0] = Measure.make(1.0, CompoundUnit.this);
+		}
+
+		@Override
+		public void visitSimple(SimpleUnit that) {
+		    List<SimpleUnit> newDenom = new ArrayList<SimpleUnit>();
+		    newDenom.addAll(denom);
+		    newDenom.add(that);
+		    result[0] = normalize(numer, newDenom);
+		}
+	    });
+
+	    return result[0];
+	}
+
+	@Override
+	public Unit exp(int k) {
+	    if (k == 0) {
+		return SCALAR;
+	    }
+	    if (k > 0) {
+		return new CompoundUnit(UnitsModelUtils.copies(k, numer),
+			UnitsModelUtils.copies(k, denom));
+	    }
+	    return new CompoundUnit(UnitsModelUtils.copies(-k, denom),
+		    UnitsModelUtils.copies(-k, numer));
+	}
+
+	@Override
+	public Measure times(Unit that) {
+	    final Measure[] result = new Measure[1];
+
+	    that.accept(new Visitor() {
+		@Override
+		public void visitCompound(CompoundUnit that) {
+		    List<SimpleUnit> newNumer = new ArrayList<SimpleUnit>();
+		    newNumer.addAll(numer);
+		    newNumer.addAll(that.getNumer());
+
+		    List<SimpleUnit> newDenom = new ArrayList<SimpleUnit>();
+		    newDenom.addAll(denom);
+		    newDenom.addAll(that.getDenom());
+
+		    result[0] = normalize(newNumer, newDenom);
+		}
+
+		@Override
+		public void visitScalar(ScalarUnit that) {
+		    result[0] = Measure.make(1.0, CompoundUnit.this);
+		}
+
+		@Override
+		public void visitSimple(SimpleUnit that) {
+		    List<SimpleUnit> newNumer = new ArrayList<SimpleUnit>();
+		    newNumer.addAll(numer);
+		    newNumer.add(that);
+		    result[0] = normalize(newNumer, denom);
+		}
+	    });
+
+	    return result[0];
+	}
+    }
+
+    private static class ScalarUnit extends Unit {
+	@Override
+	public Measure div(Unit that) {
+	    final Measure[] result = new Measure[1];
+	    that.accept(new Visitor() {
+		@Override
+		public void visitCompound(CompoundUnit that) {
+		    result[0] = normalize(that.getDenom(), that.getNumer());
+		}
+
+		@Override
+		public void visitScalar(ScalarUnit that) {
+		    result[0] = Measure.make(1.0, SCALAR);
+		}
+
+		@Override
+		public void visitSimple(SimpleUnit that) {
+		    result[0] = normalize(NO_UNITS, singleton(that));
+		}
+
+	    });
+	    return result[0];
+	}
+
+	@Override
+	public Unit exp(int k) {
+	    return this;
+	}
+
+	@Override
+	public Dimension getDimension() {
+	    return Dimension.SCALAR;
+	}
+
+	@Override
+	public double getScale() {
+	    return 1.0;
+	}
+
+	@Override
+	public Measure times(Unit that) {
+	    return Measure.make(1.0, that);
+	}
+
+	@Override
+	public String toString() {
+	    return "ScalarUnit";
+	}
+
+	@Override
+	void accept(Visitor visitor) {
+	    visitor.visitScalar(this);
+	}
+    }
+
+    private interface Visitor {
+	void visitCompound(CompoundUnit unit);
+
+	void visitScalar(ScalarUnit unit);
+
+	void visitSimple(SimpleUnit unit);
+    }
+
+    @SuppressWarnings("unchecked")
+    public static final List<SimpleUnit> NO_UNITS = Collections.EMPTY_LIST;
+
+    public static Unit SCALAR = new ScalarUnit();
+
+    private static Measure normalize(List<SimpleUnit> numer,
+	    List<SimpleUnit> denom) {
+	List<SimpleUnit> nums = new ArrayList<SimpleUnit>(numer);
+	List<SimpleUnit> dens = new ArrayList<SimpleUnit>(denom);
+
+	/*
+	 * Cancelling can cause the scale to be other than one, if units are
+	 * cancelled that have the same dimension but differenc scale.
+	 */
+	double scale = 1.0;
+
+	for (SimpleUnit d : denom) {
+	    SimpleUnit sameDim = null;
+	    for (SimpleUnit unit : nums) {
+		if (unit.getDimension().equals(d.getDimension())) {
+		    sameDim = unit;
+		    break;
+		}
+	    }
+	    if (sameDim != null) {
+		nums.remove(sameDim);
+		dens.remove(d);
+		scale = scale * sameDim.getScale() / d.getScale();
+	    }
+	}
+
+	nums = roughSort(nums);
+	dens = roughSort(dens);
+
+	if (nums.isEmpty() && dens.isEmpty()) {
+	    return Measure.make(scale, SCALAR);
+	}
+	if (nums.size() == 1 && dens.isEmpty()) {
+	    return Measure.make(scale, nums.get(0));
+	}
+	return Measure.make(scale, new CompoundUnit(nums, dens));
+    }
+
+    private static List<SimpleUnit> roughSort(List<SimpleUnit> units) {
+	List<SimpleUnit> byDimension = roughSortByDimension(units);
+	return UnitsModelUtils.roughSort(byDimension);
+    }
+
+    private static List<SimpleUnit> roughSortByDimension(
+	    List<SimpleUnit> unsorted) {
+	List<SimpleUnit> unsortedLeft = new ArrayList<SimpleUnit>(unsorted);
+	List<SimpleUnit> newList = new ArrayList<SimpleUnit>(unsorted.size());
+
+	while (!unsortedLeft.isEmpty()) {
+	    SimpleUnit x = unsortedLeft.get(0);
+	    while (true) {
+		SimpleUnit sameDim = null;
+		for (SimpleUnit unit : unsortedLeft) {
+		    if (unit.getDimension() == x.getDimension()) {
+			sameDim = unit;
+			break;
+		    }
+		}
+		if (sameDim == null) {
+		    break;
+		}
+		newList.add(sameDim);
+		unsortedLeft.remove(sameDim);
+	    }
+	}
+	return newList;
+    }
+
+    private Unit() {
+    }
+
+    /**
+     * Divide two units. This returns a Measure, not just a Unit, because the
+     * result might need scaling in order to normalize the unit.
+     */
+    public abstract Measure div(Unit that);
+
+    /**
+     * Two units are equal if the have the same dimension and the same scale.
+     */
+    @Override
+    public boolean equals(Object object) {
+	if (!(object instanceof Unit)) {
+	    return false;
+	}
+	Unit that = (Unit) object;
+	return getDimension().equals(that.getDimension())
+		&& getScale() == that.getScale();
+    }
+
+    public abstract Unit exp(int k);
+
+    /**
+     * The general dimension of this unit, e.g. length or time. Units in the
+     * same dimension can be compared and can be used in arithmetic operations.
+     */
+    public abstract Dimension getDimension();
+
+    /**
+     * The relative scale of this unit within the dimension. Larger scales mean
+     * that each unit counts for more.
+     */
+    public abstract double getScale();
+
+    @Override
+    public int hashCode() {
+	int code = 41;
+	code = code * 17 + ((Double) getScale()).hashCode();
+	code = code * 17 + getDimension().hashCode();
+	return code;
+    }
+
+    public <T> List<T> singleton(T x) {
+	return Collections.singletonList(x);
+    }
+
+    public Measure times(double scale) {
+	return Measure.make(scale, this);
+    }
+
+    /**
+     * Multiply two units. This returns a Measure, not just a Unit, because the
+     * result might need scaling in order to normalize the unit.
+     */
+    public abstract Measure times(Unit that);
+
+    abstract void accept(Visitor visitor);
+}
diff --git a/checkers/src/checkers/units/model/UnitsModelUtils.java b/checkers/src/checkers/units/model/UnitsModelUtils.java
new file mode 100644
index 0000000..0c5db8a
--- /dev/null
+++ b/checkers/src/checkers/units/model/UnitsModelUtils.java
@@ -0,0 +1,36 @@
+package checkers.units.model;
+
+import java.util.*;
+
+/**
+ * Utilities for use within the units library. This class should not be accessed
+ * from outside the library.
+ */
+public class UnitsModelUtils {
+    /**
+     * Return a list with <code>n</code> copies of an input list.
+     */
+    static <T> List<T> copies(int n, List<T> list) {
+	List<T> copies = new ArrayList<T>();
+	for (int i = 0; i < n; i++) {
+	    copies.addAll(list);
+	}
+	return copies;
+    }
+
+    /**
+     * Rearrange a list so that equivalent items are next to each other.
+     */
+    public static <T> List<T> roughSort(List<T> unsorted) {
+	List<T> unsortedLeft = new ArrayList<T>(unsorted);
+	List<T> newList = new ArrayList<T>(unsorted.size());
+	while (!unsortedLeft.isEmpty()) {
+	    T x = unsortedLeft.get(0);
+	    while (unsortedLeft.contains(x)) {
+		newList.add(x);
+		unsortedLeft.remove(x);
+	    }
+	}
+	return newList;
+    }
+}
diff --git a/checkers/src/checkers/units/quals/Dim.java b/checkers/src/checkers/units/quals/Dim.java
new file mode 100644
index 0000000..d3c65e9
--- /dev/null
+++ b/checkers/src/checkers/units/quals/Dim.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2009 Google Inc.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ * 
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package checkers.units.quals;
+
+import checkers.quals.TypeQualifier;
+import checkers.units.model.Dimension;
+
+import java.lang.annotation.*;
+
+/**
+ * An annotation on a type indicating the dimensions of that type.
+ * The argument to the annotation must match the following syntax:
+ * <pre>
+ *   dim := Dimension.SCALAR
+ *       |  (any static final field)
+ *       |  dim.times(dim)
+ *       |  dim.div(dim)
+ *       |  dim.exp(integer_literal)
+ * </pre>
+ */
+@TypeQualifier
+@Retention(RetentionPolicy.CLASS)
+public @interface Dim {
+    @AllowNonConstant
+    Dimension value();
+}
diff --git a/checkers/src/checkers/util/AnnotationUtils.java b/checkers/src/checkers/util/AnnotationUtils.java
index fb96811..d8ca0cf 100644
--- a/checkers/src/checkers/util/AnnotationUtils.java
+++ b/checkers/src/checkers/util/AnnotationUtils.java
@@ -349,7 +349,7 @@ public class AnnotationUtils {
             if (p == null || !p)
                 return null;
 
-            /*@Nullable*/ R r = Enum.<R>valueOf(enumType, (/*@NonNull*/ String)c.getSimpleName().toString());
+            /*@Nullable*/ R r = Enum.<R>valueOf(enumType, c.getSimpleName().toString());
             assert r != null; /*nninvariant*/
             values.add(r);
             return null;
@@ -699,6 +699,10 @@ public class AnnotationUtils {
         public AnnotationBuilder setValue(CharSequence elementName, String value) {
             return setValue(elementName, (Object)value);
         }
+        
+        public AnnotationBuilder setValue(CharSequence elementName, Tree value) {
+            return setValue(elementName, (Object) value);
+        }
 
         public AnnotationBuilder setValue(CharSequence elementName, TypeMirror value) {
             assertNotBuilt();
@@ -762,7 +766,10 @@ public class AnnotationUtils {
             assertNotBuilt();
             AnnotationValue val = createValue(value);
             ExecutableElement var = findElement(key);
-            checkSubtype(var.getReturnType(), value);
+            if (!(value instanceof Tree)) {
+        	// Ideally the tree's type would be checked, but that's not trivial
+                checkSubtype(var.getReturnType(), value);
+            }
             elementValues.put(var, val);
             return this;
         }
@@ -847,6 +854,9 @@ public class AnnotationUtils {
 
                 @SuppressWarnings("unchecked")
                 @Override
+                /**
+                 * TODO(spoon) how to deal with visitors to nodes with ASTs
+                 */
                 public <R, P> R accept(AnnotationValueVisitor<R, P> v, P p) {
                     if (value instanceof AnnotationMirror)
                         return v.visitAnnotation((AnnotationMirror)value, p);
diff --git a/checkers/tests/src/tests/UnitsModelTest.java b/checkers/tests/src/tests/UnitsModelTest.java
new file mode 100644
index 0000000..d2531d7
--- /dev/null
+++ b/checkers/tests/src/tests/UnitsModelTest.java
@@ -0,0 +1,74 @@
+package tests;
+
+import static checkers.units.model.StandardUnits.*;
+import checkers.units.model.*;
+
+import junit.framework.TestCase;
+
+/**
+ * This tests the units checker's model.
+ */
+public class UnitsModelTest extends TestCase {
+    public void testBasics() {
+	{
+	    Measure threeMeters = m.times(3);
+	    Measure fourMeters = m.times(4);
+	    Measure sum = threeMeters.plus(fourMeters);
+	    assertEquals(m.times(7), sum);
+	}
+
+	{
+	    Measure threeMeters = m.times(3);
+	    Measure fourSeconds = s.times(4);
+	    try {
+		threeMeters.plus(fourSeconds);
+		fail("Should have raised an incompatible units exception");
+	    } catch (IncompatibleUnitsError e) {
+		// expected
+	    }
+	}
+
+	{
+	    Measure threeMeters = m.times(3);
+	    Measure fourMeters = m.times(4);
+	    Measure diff = threeMeters.minus(fourMeters);
+	    assertEquals(m.times(-1), diff);
+	}
+
+	{
+	    Measure threeMeters = m.times(3);
+	    Measure fourSeconds = s.times(4);
+	    try {
+		threeMeters.minus(fourSeconds);
+		fail("Should have raised an incompatible units exception");
+	    } catch (IncompatibleUnitsError e) {
+		// expected
+	    }
+	}
+
+	{
+	    Measure threeMeters = m.times(3);
+	    Measure fourSeconds = s.times(4);
+	    assertEquals(Measure.make(12).times(m.times(s)), threeMeters
+		    .times(fourSeconds));
+	}
+
+	{
+	    Measure eightMeters = m.times(8);
+	    Measure fourSeconds = s.times(4);
+	    assertEquals(Measure.make(2).times(m.div(s)), eightMeters
+		    .div(fourSeconds));
+	}
+    }
+
+    public void testDimensionToString() {
+	assertEquals("ScalarDimension", Dimension.SCALAR.toString());
+	assertEquals("Length", Length.toString());
+	assertEquals("Length*Length", Length.times(Length).toString());
+	assertEquals("1/Length", Dimension.SCALAR.div(Length).toString());
+	assertEquals("1/(Length*Time*Time)", Dimension.SCALAR.div(
+		Length.times(Time).times(Time)).toString());
+	assertEquals("Length*Length/Time", Length.times(Length).div(Time)
+		.toString());
+    }
+}
diff --git a/checkers/tests/units/Silly.class b/checkers/tests/units/Silly.class
new file mode 100644
index 0000000..524c80c
Binary files /dev/null and b/checkers/tests/units/Silly.class differ
diff --git a/checkers/tests/units/Silly.java b/checkers/tests/units/Silly.java
new file mode 100644
index 0000000..931b031
--- /dev/null
+++ b/checkers/tests/units/Silly.java
@@ -0,0 +1,7 @@
+import java.lang.annotation.AllowNonConstant;
+
+@interface Silly {
+    // Test: type Integer should be allowed iff @AllowNonConstant is applied
+     @AllowNonConstant
+    int[] value();
+}
\ No newline at end of file
diff --git a/checkers/tests/units/Units.class b/checkers/tests/units/Units.class
new file mode 100644
index 0000000..394754e
Binary files /dev/null and b/checkers/tests/units/Units.class differ
diff --git a/checkers/tests/units/Units.java b/checkers/tests/units/Units.java
new file mode 100644
index 0000000..36a55db
--- /dev/null
+++ b/checkers/tests/units/Units.java
@@ -0,0 +1,41 @@
+import checkers.units.quals.Dim;
+import checkers.units.model.*;
+import static checkers.units.StandardUnits.*;
+import static checkers.units.model.Dimension.SCALAR;
+
+class Units {
+    public @Dim(Length.div(Time))
+    Measure speed(@Dim(Length) Measure distance, @Dim(Time) Measure time) {
+	// return distance.div(time);
+	return null;
+    }
+
+    int plusOne(int x) {
+	return x + 1;
+    }
+
+    public void badUnits() {
+	// TODO(spoon) make a separate test case for things so malformed they
+	// don't even make it to the checker
+	// @Dim(Length + 1)
+	// Measure malformed = null;
+
+	// @Dim(m)
+	// Measure notAdimension = null;
+
+	@Dim(Length)
+	Measure distanceGood = m.times(3);
+
+	@Dim(Length)
+	Measure distanceBad = s.times(3);
+
+	@Dim(Length.div(Time))
+	Measure speedGood = m.times(3).div(s.times(1));
+
+	@Dim(Length.div(Time))
+	Measure speedBad = s.times(3).div(m.times(2));
+
+	@Dim(SCALAR)
+	Measure scalarGood = m.times(3).div(m.times(3));
+    }
+}
\ No newline at end of file
diff --git a/checkers/units-tutorial.html b/checkers/units-tutorial.html
new file mode 100644
index 0000000..f47b942
--- /dev/null
+++ b/checkers/units-tutorial.html
@@ -0,0 +1,185 @@
+<html>
+<head>
+<title>Units Checker for JSR 308</title>
+</head>
+
+<body>
+<h1>Units Checker for JSR 308</h1>
+
+<p>This is a simple package for units and dimensions, based on Andrew
+Kennedy's Ph.D. work.  It includes dynamic checks for unit
+compatibility.  In addition, it can statically check for errors using
+an annotation processor with an experimental version of the JSR 308
+tools.  Specifically, the experimental version supports annotation
+arguments that are Java expressions.
+
+<p>The package was developed by Lex Spoon.
+
+
+<h2>Prerequisites</h2>
+<p>To use this system, check out
+the <a href="http://types.cs.washington.edu/jsr308/">JSR 308 langtools
+and checker framework</a>.  Put them in sibling directories, and name
+the directories <code>checkers</code> and <code>langtools</code>.
+Then, apply <a href="unitsFor308.patch">this patch</a>
+with <code>patch -sp1</code>.  (TODO: convert the patch to a Mercurial
+branch) Build <code>langtools</code> and <code>checkers</code> as
+usual, and use the resulting <code>javac</code> for the rest of this
+tutorial.
+
+<h2>A quick example</h2>
+
+<p>The following import pulls in the units API:
+<pre>
+import checkers.units.model.*;
+</pre>
+
+<p>Additionally, many standard units, such as meters and seconds,
+are available in class <code>StandardUnits</code>.  These can be
+imported as follows:
+<pre>
+import static checkers.units.StandardUnits.*;
+</pre>
+
+<p>Given those imports, expressions like the following are legal:
+<pre>
+Measure distance = m.times(5);  // 5 meters
+Measure time = s.times(10);     // 10 seconds
+Measure speed = distance.div(time);   // 0.5 meters per second
+</pre>
+
+<p>Expressions that have mismatched units will raise a run-time exception:
+<pre>
+Measure nonsense = distance.plus(time);  // IncompatibleUnitsError
+</pre>
+
+<p>Here is a complete example program putting it all together:
+<pre>
+import checkers.units.model.*;
+import static checkers.units.model.StandardUnits.*;
+
+public class UnitsExample {
+  public static void main(String[] args) {
+    Measure distance = m.times(5);
+    Measure time = s.times(10);
+    Measure speed = distance.div(time);
+
+    System.out.println("Speed = " + speed);
+
+    Measure nonsense = distance.plus(time);
+  }
+}
+</pre>
+
+<p>Compile and run the example with <code>checkers-quals.jar</code> on
+your classpath:
+<pre>
+../langtools/dist/bin/javac -cp ../checkers/checkers-quals.jar UnitsExample.java
+java -cp ../checkers/checkers-quals.jar:. UnitsExample
+</pre>
+
+<p>You should see a printout of the speed followed by
+an <code>IncompatibleUnitsError</code>:
+<pre>
+Speed = 0.5 m/s
+Exception in thread "main" checkers.units.model.IncompatibleUnitsError: Incompatible units: s vs. m
+</pre>
+
+
+<h2>The model: units and dimensions</h2>
+
+There are three types you need to be familiar with to use this system.
+They are:
+<dl>
+<dt>Measure</dt>
+<dd>A scalar plus a unit.  For example, <q>5 kilometers</q>.
+
+<dt>Unit</dt>
+<dd>A unit of measure.  For example, <q>kilometers</q>.
+
+<dt>Dimension</dt>
+<dd>The property that compatible units must share.
+For example, <q>Length</q>.
+
+</dl>
+	
+<p>The library will automatically propagate units through your
+computation.  It will attempt to maintain the units you enter when
+possible, but will convert them if you add or subtract units that are
+compatible but different.  Any attempt to add or subtract measures
+with incompatible units will result in
+an <code>IncompatibleUnitsError</code>.
+
+
+<h2>Checking units at compile time</h2>
+
+<p>Units errors can be found at compile time rather than at run time.
+To do so, simply compile with <code>checkers.units.UnitsChecker</code>
+as an annotation processor.  For example:
+<pre>
+../langtools/dist/bin/javac -cp ../checkers/checkers.jar \
+  -processor checkers.units.UnitsChecker \
+  UnitsExample.java
+</pre>
+
+<p>In this case, no errors are produced.  That's because all of the
+variables in this example are annotated as an
+arbitrary <code>Measure</code>.  The checker ignores operations on an
+unannotated <code>Measure</code>, just like a 
+<a href="http://www.cs.colorado.edu/~siek/pubs/pubs/2006/siek06:_gradual.pdf">gradual
+type checker</a> ignores operations on an untyped variable.
+
+<p>To make the checker useful, you must add annotations to
+the <code>Measure</code> types to indicate the expected dimension of
+the measure involved.  The annotation type is <code>Dim</code>.  It
+is available via the following import:
+<code>
+import checkers.units.quals.Dim;
+</code>
+The argument to <code>Dim</code> is a dimension, such
+as <code>Length</code> or <code>Time</code>.  Here is an updated
+example with <code>Dim</code> annotations on all <code>Measure</code>
+types.
+<pre>
+import checkers.units.model.*;
+import static checkers.units.model.StandardUnits.*;
+import checkers.units.quals.Dim;
+
+public class UnitsExample {
+  public static void main(String[] args) {
+    @Dim(Length) Measure distance = m.times(5);
+    @Dim(Time) Measure time = s.times(10);
+    @Dim(Length.div(Time)) Measure speed = distance.div(time);
+
+    System.out.println("Speed = " + speed);
+
+    @Dim(Length) Measure nonsense = distance.plus(time);
+  }
+}
+</pre>
+With the annotations in place, the following error is emitted:
+<pre>
+UnitsExample.java:13: (Incompatible types: Length vs. Time)
+    @Dim(Length) Measure nonsense = distance.plus(time);
+                                                 ^
+1 error
+</pre>
+
+<p>As you can see, if you want to get the most of the static units
+checker, you must systematically use a <code>Dim</code> annotation on
+every <code>Measure</code> type.
+
+<p>When using a <code>Dim</code> annotation, you must supply an
+argument that the processor understands, or it will complain.  The
+argument must be a <em>static dimension expression</em>.  A static
+dimension expression is any of:
+<ul>
+<li>Any public, static, final field of type <code>Dimension</code>
+<li><code>Dimension.SCALAR</code>, for scalar quantities
+<li><code><em>dim1</em>.times(<em>dim2</em>)</code>, for
+    any static dimensions <em>dim1</em> and <em>dim2</em>
+<li><code><em>dim1</em>.div(<em>dim2</em>)</code>, for
+    any static dimensions <em>dim1</em> and <em>dim2</em>
+</ul>
+</body>
+</html>
diff --git a/langtools/src/share/classes/com/sun/javadoc/AnnotationValue.java b/langtools/src/share/classes/com/sun/javadoc/AnnotationValue.java
index c55c65d..e6e1120 100644
--- a/langtools/src/share/classes/com/sun/javadoc/AnnotationValue.java
+++ b/langtools/src/share/classes/com/sun/javadoc/AnnotationValue.java
@@ -43,6 +43,7 @@ public interface AnnotationValue {
      *     <li> <code>FieldDoc</code> (representing an enum constant)
      *     <li> <code>AnnotationDesc</code>
      *     <li> <code>AnnotationValue[]</code>
+     *     <li> <code>ExpressionTree</code>
      * </ul>
      *
      * @return the value.
diff --git a/langtools/src/share/classes/com/sun/tools/apt/mirror/declaration/AnnotationProxyMaker.java b/langtools/src/share/classes/com/sun/tools/apt/mirror/declaration/AnnotationProxyMaker.java
index e1b5ba8..07c46bb 100644
--- a/langtools/src/share/classes/com/sun/tools/apt/mirror/declaration/AnnotationProxyMaker.java
+++ b/langtools/src/share/classes/com/sun/tools/apt/mirror/declaration/AnnotationProxyMaker.java
@@ -241,6 +241,10 @@ class AnnotationProxyMaker {
                 value = null;   // indicates a type mismatch
             }
         }
+        
+        public void visitExpression(Attribute.Expression e) {
+            value = null; // TODO(spoon)
+        }
 
         public void visitError(Attribute.Error e) {
             value = null;       // indicates a type mismatch
@@ -253,9 +257,11 @@ class AnnotationProxyMaker {
         private void typeMismatch(final Method method, final Attribute attr) {
             value = new ExceptionProxy() {
                 private static final long serialVersionUID = 8473323277815075163L;
+                @Override
                 public String toString() {
                     return "<error>";   // eg:  @Anno(value=<error>)
                 }
+                @Override
                 protected RuntimeException generateException() {
                     return new AnnotationTypeMismatchException(method,
                                 attr.type.toString());
@@ -282,10 +288,12 @@ class AnnotationProxyMaker {
             ex = new MirroredTypeException(t);
         }
 
+        @Override
         public String toString() {
             return ex.getQualifiedName();
         }
 
+        @Override
         public int hashCode() {
             TypeMirror t = ex.getTypeMirror();
             return (t != null)
@@ -293,6 +301,7 @@ class AnnotationProxyMaker {
                     : ex.getQualifiedName().hashCode();
         }
 
+        @Override
         public boolean equals(Object obj) {
             TypeMirror t = ex.getTypeMirror();
             return t != null &&
@@ -301,6 +310,7 @@ class AnnotationProxyMaker {
                         ((MirroredTypeExceptionProxy) obj).ex.getTypeMirror());
         }
 
+        @Override
         protected RuntimeException generateException() {
             return (RuntimeException) ex.fillInStackTrace();
         }
@@ -324,10 +334,12 @@ class AnnotationProxyMaker {
             ex = new MirroredTypesException(ts);
         }
 
+        @Override
         public String toString() {
             return ex.getQualifiedNames().toString();
         }
 
+        @Override
         public int hashCode() {
             Collection<TypeMirror> ts = ex.getTypeMirrors();
             return (ts != null)
@@ -335,6 +347,7 @@ class AnnotationProxyMaker {
                     : ex.getQualifiedNames().hashCode();
         }
 
+        @Override
         public boolean equals(Object obj) {
             Collection<TypeMirror> ts = ex.getTypeMirrors();
             return ts != null &&
@@ -343,6 +356,7 @@ class AnnotationProxyMaker {
                       ((MirroredTypesExceptionProxy) obj).ex.getTypeMirrors());
         }
 
+        @Override
         protected RuntimeException generateException() {
             return (RuntimeException) ex.fillInStackTrace();
         }
diff --git a/langtools/src/share/classes/com/sun/tools/apt/mirror/declaration/AnnotationValueImpl.java b/langtools/src/share/classes/com/sun/tools/apt/mirror/declaration/AnnotationValueImpl.java
index b99bb8a..321a53e 100644
--- a/langtools/src/share/classes/com/sun/tools/apt/mirror/declaration/AnnotationValueImpl.java
+++ b/langtools/src/share/classes/com/sun/tools/apt/mirror/declaration/AnnotationValueImpl.java
@@ -57,6 +57,7 @@ public class AnnotationValueImpl implements AnnotationValue {
     /**
      * {@inheritDoc}
      */
+    @Override
     public String toString() {
         StringBuilder sb = new StringBuilder();
         Constants.Formatter fmtr = Constants.getFormatter(sb);
@@ -114,6 +115,10 @@ public class AnnotationValueImpl implements AnnotationValue {
             value = vals;
         }
 
+        public void visitExpression(Attribute.Expression e) {
+            value = e;
+        }
+        
         public void visitError(Attribute.Error e) {
             value = "<error>";  // javac will already have logged an error msg
         }
diff --git a/langtools/src/share/classes/com/sun/tools/javac/code/Attribute.java b/langtools/src/share/classes/com/sun/tools/javac/code/Attribute.java
index 09b2670..38f50b6 100644
--- a/langtools/src/share/classes/com/sun/tools/javac/code/Attribute.java
+++ b/langtools/src/share/classes/com/sun/tools/javac/code/Attribute.java
@@ -25,16 +25,19 @@
 
 package com.sun.tools.javac.code;
 
-import java.util.LinkedHashMap;
-import java.util.Map;
-import javax.lang.model.element.AnnotationMirror;
-import javax.lang.model.element.AnnotationValue;
-import javax.lang.model.element.AnnotationValueVisitor;
-import javax.lang.model.type.DeclaredType;
+import static com.sun.tools.javac.code.TypeTags.*;
+
+import com.sun.source.tree.ExpressionTree;
 import com.sun.tools.javac.code.Symbol.*;
+import com.sun.tools.javac.tree.JCTree.JCExpression;
 import com.sun.tools.javac.util.*;
+import com.sun.tools.javac.util.List;
+import com.sun.tools.javac.util.Name;
 
-import static com.sun.tools.javac.code.TypeTags.*;
+import java.util.*;
+
+import javax.lang.model.element.*;
+import javax.lang.model.type.DeclaredType;
 
 /** An annotation value.
  *
@@ -66,17 +69,21 @@ public abstract class Attribute implements AnnotationValue {
     /** The value for an annotation element of primitive type or String. */
     public static class Constant extends Attribute {
         public final Object value;
+        @Override
         public void accept(Visitor v) { v.visitConstant(this); }
         public Constant(Type type, Object value) {
             super(type);
             this.value = value;
         }
+        @Override
         public String toString() {
             return Constants.format(value, type);
         }
+        @Override
         public Object getValue() {
             return Constants.decode(value, type);
         }
+        @Override
         public <R, P> R accept(AnnotationValueVisitor<R, P> v, P p) {
             if (value instanceof String)
                 return v.visitString((String) value, p);
@@ -104,6 +111,7 @@ public abstract class Attribute implements AnnotationValue {
      */
     public static class Class extends Attribute {
         public final Type type;
+        @Override
         public void accept(Visitor v) { v.visitClass(this); }
         public Class(Types types, Type type) {
             super(makeClassType(types, type));
@@ -117,12 +125,15 @@ public abstract class Attribute implements AnnotationValue {
                                       List.of(arg),
                                       types.syms.classType.tsym);
         }
+        @Override
         public String toString() {
             return type + ".class";
         }
+        @Override
         public Type getValue() {
             return type;
         }
+        @Override
         public <R, P> R accept(AnnotationValueVisitor<R, P> v, P p) {
             return v.visitType(type, p);
         }
@@ -143,6 +154,7 @@ public abstract class Attribute implements AnnotationValue {
             super(type);
             this.values = values;
         }
+        @Override
         public void accept(Visitor v) { v.visitCompound(this); }
 
         /**
@@ -153,6 +165,7 @@ public abstract class Attribute implements AnnotationValue {
          *     @com.example.foo
          * Omit parens for marker annotations, and omit "value=" when allowed.
          */
+        @Override
         public String toString() {
             StringBuilder buf = new StringBuilder();
             buf.append("@");
@@ -183,10 +196,12 @@ public abstract class Attribute implements AnnotationValue {
             return null;
         }
 
+        @Override
         public Attribute.Compound getValue() {
             return this;
         }
 
+        @Override
         public <R, P> R accept(AnnotationValueVisitor<R, P> v, P p) {
             return v.visitAnnotation(this, p);
         }
@@ -227,7 +242,9 @@ public abstract class Attribute implements AnnotationValue {
             super(type);
             this.values = values;
         }
+        @Override
         public void accept(Visitor v) { v.visitArray(this); }
+        @Override
         public String toString() {
             StringBuilder buf = new StringBuilder();
             buf.append('{');
@@ -241,9 +258,11 @@ public abstract class Attribute implements AnnotationValue {
             buf.append('}');
             return buf.toString();
         }
+        @Override
         public List<Attribute> getValue() {
             return List.from(values);
         }
+        @Override
         public <R, P> R accept(AnnotationValueVisitor<R, P> v, P p) {
             return v.visitArray(getValue(), p);
         }
@@ -258,29 +277,61 @@ public abstract class Attribute implements AnnotationValue {
             assert value != null;
             this.value = value;
         }
+        @Override
         public void accept(Visitor v) { v.visitEnum(this); }
+        @Override
         public String toString() {
             return value.enclClass() + "." + value;     // qualified name
         }
+        @Override
         public VarSymbol getValue() {
             return value;
         }
+        @Override
         public <R, P> R accept(AnnotationValueVisitor<R, P> v, P p) {
             return v.visitEnumConstant(value, p);
         }
     }
+    
+    public static class Expression extends Attribute {
+      private JCExpression expr;
+
+      public Expression(Type type, JCExpression expr) {
+        super(type);
+        this.expr = expr;
+      }
+
+      @Override
+      public void accept(Visitor v) {
+        v.visitExpression(this);
+      }
+
+      @Override
+      public JCExpression getValue() {
+        return expr;
+      }
+      
+      @Override
+      public String toString() {
+        return expr.toString();
+      }
+    }
 
     public static class Error extends Attribute {
         public Error(Type type) {
             super(type);
         }
+        @Override
         public void accept(Visitor v) { v.visitError(this); }
+        @Override
         public String toString() {
             return "<error>";
         }
+        @Override
         public String getValue() {
             return toString();
         }
+        @Override
         public <R, P> R accept(AnnotationValueVisitor<R, P> v, P p) {
             return v.visitString(toString(), p);
         }
@@ -293,6 +344,7 @@ public abstract class Attribute implements AnnotationValue {
         void visitCompound(Attribute.Compound compound);
         void visitArray(Attribute.Array array);
         void visitEnum(Attribute.Enum e);
+        void visitExpression(Attribute.Expression e);
         void visitError(Attribute.Error e);
     }
 }
diff --git a/langtools/src/share/classes/com/sun/tools/javac/code/Lint.java b/langtools/src/share/classes/com/sun/tools/javac/code/Lint.java
index cec6e70..ecacfe2 100644
--- a/langtools/src/share/classes/com/sun/tools/javac/code/Lint.java
+++ b/langtools/src/share/classes/com/sun/tools/javac/code/Lint.java
@@ -119,6 +119,7 @@ public class Lint
         this.suppressedValues = other.suppressedValues.clone();
     }
 
+    @Override
     public String toString() {
         return "Lint:[values" + values + " suppressedValues" + suppressedValues + "]";
     }
@@ -312,6 +313,9 @@ public class Lint
 
         public void visitEnum(Attribute.Enum e) {
         }
+        
+        public void visitExpression(Attribute.Expression e) {
+        }
 
         public void visitError(Attribute.Error e) {
         }
diff --git a/langtools/src/share/classes/com/sun/tools/javac/code/Symtab.java b/langtools/src/share/classes/com/sun/tools/javac/code/Symtab.java
index 8d708bc..c197936 100644
--- a/langtools/src/share/classes/com/sun/tools/javac/code/Symtab.java
+++ b/langtools/src/share/classes/com/sun/tools/javac/code/Symtab.java
@@ -132,6 +132,7 @@ public class Symtab {
     public final Type assertionErrorType;
     public final Type cloneNotSupportedExceptionType;
     public final Type annotationType;
+    public final Type annotationAllowNonConstant;
     public final TypeSymbol enumSym;
     public final Type listType;
     public final Type collectionsType;
@@ -358,7 +359,8 @@ public class Symtab {
         rootPackage = new PackageSymbol(names.empty, null);
         final JavacMessages messages = JavacMessages.instance(context);
         unnamedPackage = new PackageSymbol(names.empty, rootPackage) {
-                public String toString() {
+                @Override
+		public String toString() {
                     return messages.getLocalizedString("compiler.misc.unnamed.package");
                 }
             };
@@ -454,6 +456,7 @@ public class Symtab {
             : enterClass("java.util.Collection");
         iteratorType = enterClass("java.util.Iterator");
         annotationTargetType = enterClass("java.lang.annotation.Target");
+        annotationAllowNonConstant = enterClass("java.lang.annotation.AllowNonConstant");
         overrideType = enterClass("java.lang.Override");
         retentionType = enterClass("java.lang.annotation.Retention");
         deprecatedType = enterClass("java.lang.Deprecated");
diff --git a/langtools/src/share/classes/com/sun/tools/javac/comp/Annotate.java b/langtools/src/share/classes/com/sun/tools/javac/comp/Annotate.java
index 0d882f7..82a67bf 100644
--- a/langtools/src/share/classes/com/sun/tools/javac/comp/Annotate.java
+++ b/langtools/src/share/classes/com/sun/tools/javac/comp/Annotate.java
@@ -187,15 +187,14 @@ public class Annotate {
     }
 
     Attribute enterAttributeValue(Type expected,
-                                  JCExpression tree,
+	                          JCExpression tree,
                                   Env<AttrContext> env) {
         if (expected.isPrimitive() || types.isSameType(expected, syms.stringType)) {
             Type result = attr.attribExpr(tree, env, expected);
             if (result.isErroneous())
                 return new Attribute.Error(expected);
             if (result.constValue() == null) {
-                log.error(tree.pos(), "attribute.value.must.be.constant");
-                return new Attribute.Error(expected);
+        	return new Attribute.Expression(expected, tree);
             }
             result = cfolder.coerce(result, expected);
             return new Attribute.Constant(expected, result.constValue());
@@ -205,8 +204,7 @@ public class Annotate {
             if (result.isErroneous())
                 return new Attribute.Error(expected);
             if (TreeInfo.name(tree) != names._class) {
-                log.error(tree.pos(), "annotation.value.must.be.class.literal");
-                return new Attribute.Error(expected);
+        	return new Attribute.Expression(expected, tree);
             }
             return new Attribute.Class(types,
                                        (((JCFieldAccess) tree).selected).type);
@@ -219,6 +217,7 @@ public class Annotate {
             return enterAnnotation((JCAnnotation)tree, expected, env);
         }
         if (expected.tag == TypeTags.ARRAY) { // should really be isArray()
+            // Array arguments are required to be constants
             if (tree.getTag() != JCTree.NEWARRAY) {
                 tree = make.at(tree.pos).
                     NewArray(null, List.<JCExpression>nil(), List.of(tree));
@@ -230,29 +229,39 @@ public class Annotate {
             }
             ListBuffer<Attribute> buf = new ListBuffer<Attribute>();
             for (List<JCExpression> l = na.elems; l.nonEmpty(); l=l.tail) {
-                buf.append(enterAttributeValue(types.elemtype(expected),
-                                               l.head,
-                                               env));
-            }
-            return new Attribute.
-                Array(expected, buf.toArray(new Attribute[buf.length()]));
-        }
+                Attribute attrib = enterAttributeValue(types.elemtype(expected),
+                	                       l.head,
+                                               env);
+                if (attrib instanceof Attribute.Expression) {
+                    log.error(l.head, "attribute.value.must.be.constant");
+		    return new Attribute.Error(types.elemtype(expected));
+		}
+		buf.append(attrib);
+	    }
+	    return new Attribute.Array(expected, buf.toArray(new Attribute[buf
+		    .length()]));
+	}
         if (expected.tag == TypeTags.CLASS &&
             (expected.tsym.flags() & Flags.ENUM) != 0) {
-            attr.attribExpr(tree, env, expected);
+            Type result = attr.attribExpr(tree, env, expected);
+            if (result.isErroneous()) {
+        	return new Attribute.Error(expected);
+            }
             Symbol sym = TreeInfo.symbol(tree);
             if (sym == null ||
                 TreeInfo.nonstaticSelect(tree) ||
                 sym.kind != Kinds.VAR ||
                 (sym.flags() & Flags.ENUM) == 0) {
-                log.error(tree.pos(), "enum.annotation.must.be.enum.constant");
-                return new Attribute.Error(expected);
+        	return new Attribute.Expression(expected, tree);
             }
             VarSymbol enumerator = (VarSymbol) sym;
             return new Attribute.Enum(expected, enumerator);
         }
-        if (!expected.isErroneous())
-            log.error(tree.pos(), "annotation.value.not.allowable.type");
-        return new Attribute.Error(attr.attribExpr(tree, env, expected));
+
+        Type result = attr.attribExpr(tree, env, expected);
+        if (result.isErroneous()) {
+            return new Attribute.Error(expected);
+        }
+        return new Attribute.Expression(expected, tree);
     }
 }
diff --git a/langtools/src/share/classes/com/sun/tools/javac/comp/Attr.java b/langtools/src/share/classes/com/sun/tools/javac/comp/Attr.java
index f344c72..92c1efa 100644
--- a/langtools/src/share/classes/com/sun/tools/javac/comp/Attr.java
+++ b/langtools/src/share/classes/com/sun/tools/javac/comp/Attr.java
@@ -26,7 +26,6 @@
 package com.sun.tools.javac.comp;
 
 import java.util.*;
-import java.util.Set;
 import javax.lang.model.element.ElementKind;
 import javax.tools.JavaFileObject;
 
@@ -591,6 +590,7 @@ public class Attr extends JCTree.Visitor {
         return t;
     }
 
+    @Override
     public void visitClassDef(JCClassDecl tree) {
         // Local classes have not been entered yet, so we need to do it now:
         if ((env.info.scope.owner.kind & (VAR | MTH)) != 0)
@@ -620,6 +620,7 @@ public class Attr extends JCTree.Visitor {
         }
     }
 
+    @Override
     public void visitMethodDef(JCMethodDecl tree) {
         MethodSymbol m = tree.sym;
 
@@ -664,8 +665,9 @@ public class Attr extends JCTree.Visitor {
 
             // Check that result type is well-formed.
             chk.validate(tree.restype, localEnv);
-            if ((owner.flags() & ANNOTATION) != 0)
-                chk.validateAnnotationType(tree.restype);
+            if ((owner.flags() & ANNOTATION) != 0
+		    && tree.sym.attribute(syms.annotationAllowNonConstant.tsym) == null)
+                chk.validateAnnotationTypeAsConstant(tree.restype);
 
             if ((owner.flags() & ANNOTATION) != 0)
                 chk.validateAnnotationMethod(tree.pos(), m);
@@ -728,13 +730,14 @@ public class Attr extends JCTree.Visitor {
             }
             localEnv.info.scope.leave();
             result = tree.type = m.type;
-            chk.validateAnnotations(tree.mods.annotations, m);
+            chk.validateAnnotations(tree.mods.annotations, m, env);
         }
         finally {
             chk.setLint(prevLint);
         }
     }
 
+    @Override
     public void visitVarDef(JCVariableDecl tree) {
         // Local variables have not been entered yet, so we need to do it now:
         if (env.info.scope.owner.kind == MTH) {
@@ -776,17 +779,19 @@ public class Attr extends JCTree.Visitor {
                 }
             }
             result = tree.type = v.type;
-            chk.validateAnnotations(tree.mods.annotations, v);
+            chk.validateAnnotations(tree.mods.annotations, v, env);
         }
         finally {
             chk.setLint(prevLint);
         }
     }
 
+    @Override
     public void visitSkip(JCSkip tree) {
         result = null;
     }
 
+    @Override
     public void visitBlock(JCBlock tree) {
         if (env.info.scope.owner.kind == TYP) {
             // Block is a static or instance initializer;
@@ -809,18 +814,21 @@ public class Attr extends JCTree.Visitor {
         result = null;
     }
 
+    @Override
     public void visitDoLoop(JCDoWhileLoop tree) {
         attribStat(tree.body, env.dup(tree));
         attribExpr(tree.cond, env, syms.booleanType);
         result = null;
     }
 
+    @Override
     public void visitWhileLoop(JCWhileLoop tree) {
         attribExpr(tree.cond, env, syms.booleanType);
         attribStat(tree.body, env.dup(tree));
         result = null;
     }
 
+    @Override
     public void visitForLoop(JCForLoop tree) {
         Env<AttrContext> loopEnv =
             env.dup(env.tree, env.info.dup(env.info.scope.dup()));
@@ -833,6 +841,7 @@ public class Attr extends JCTree.Visitor {
         result = null;
     }
 
+    @Override
     public void visitForeachLoop(JCEnhancedForLoop tree) {
         Env<AttrContext> loopEnv =
             env.dup(env.tree, env.info.dup(env.info.scope.dup()));
@@ -860,6 +869,7 @@ public class Attr extends JCTree.Visitor {
         result = null;
     }
 
+    @Override
     public void visitLabelled(JCLabeledStatement tree) {
         // Check that label is not used in an enclosing statement
         Env<AttrContext> env1 = env;
@@ -877,6 +887,7 @@ public class Attr extends JCTree.Visitor {
         result = null;
     }
 
+    @Override
     public void visitSwitch(JCSwitch tree) {
         Type seltype = attribExpr(tree.selector, env);
 
@@ -961,12 +972,14 @@ public class Attr extends JCTree.Visitor {
         return null;
     }
 
+    @Override
     public void visitSynchronized(JCSynchronized tree) {
         chk.checkRefType(tree.pos(), attribExpr(tree.lock, env));
         attribStat(tree.body, env);
         result = null;
     }
 
+    @Override
     public void visitTry(JCTry tree) {
         // Attribute body
         attribStat(tree.body, env.dup(tree, env.info.dup()));
@@ -992,6 +1005,7 @@ public class Attr extends JCTree.Visitor {
         result = null;
     }
 
+    @Override
     public void visitConditional(JCConditional tree) {
         attribExpr(tree.cond, env, syms.booleanType);
         attribExpr(tree.truepart, env);
@@ -1095,6 +1109,7 @@ public class Attr extends JCTree.Visitor {
             return types.lub(thentype.baseType(), elsetype.baseType());
         }
 
+    @Override
     public void visitIf(JCIf tree) {
         attribExpr(tree.cond, env, syms.booleanType);
         attribStat(tree.thenpart, env);
@@ -1104,16 +1119,19 @@ public class Attr extends JCTree.Visitor {
         result = null;
     }
 
+    @Override
     public void visitExec(JCExpressionStatement tree) {
         attribExpr(tree.expr, env);
         result = null;
     }
 
+    @Override
     public void visitBreak(JCBreak tree) {
         tree.target = findJumpTarget(tree.pos(), tree.getTag(), tree.label, env);
         result = null;
     }
 
+    @Override
     public void visitContinue(JCContinue tree) {
         tree.target = findJumpTarget(tree.pos(), tree.getTag(), tree.label, env);
         result = null;
@@ -1184,6 +1202,7 @@ public class Attr extends JCTree.Visitor {
             return null;
         }
 
+    @Override
     public void visitReturn(JCReturn tree) {
         // Check that there is an enclosing method which is
         // nested within than the enclosing class.
@@ -1208,11 +1227,13 @@ public class Attr extends JCTree.Visitor {
         result = null;
     }
 
+    @Override
     public void visitThrow(JCThrow tree) {
         attribExpr(tree.expr, env, syms.throwableType);
         result = null;
     }
 
+    @Override
     public void visitAssert(JCAssert tree) {
         attribExpr(tree.cond, env, syms.booleanType);
         if (tree.detail != null) {
@@ -1225,6 +1246,7 @@ public class Attr extends JCTree.Visitor {
      *  NOTE: The method part of an application will have in its type field
      *        the return type of the method, not the method's type itself!
      */
+    @Override
     public void visitApply(JCMethodInvocation tree) {
         // The local environment of a method application is
         // a new environment nested in the current one.
@@ -1409,6 +1431,7 @@ public class Attr extends JCTree.Visitor {
             return (typeargtypes == null) ? mt : (Type)new ForAll(typeargtypes, mt);
         }
 
+    @Override
     public void visitNewClass(JCNewClass tree) {
         Type owntype = types.createErrorType(tree.type);
 
@@ -1623,6 +1646,7 @@ public class Attr extends JCTree.Visitor {
         return tree;
     }
 
+    @Override
     public void visitNewArray(JCNewArray tree) {
         Type owntype = types.createErrorType(tree.type);
         Type elemtype;
@@ -1656,6 +1680,7 @@ public class Attr extends JCTree.Visitor {
         result = check(tree, owntype, VAL, pkind, pt);
     }
 
+    @Override
     public void visitParens(JCParens tree) {
         Type owntype = attribTree(tree.expr, env, pkind, pt);
         result = check(tree, owntype, pkind, pkind, pt);
@@ -1664,6 +1689,7 @@ public class Attr extends JCTree.Visitor {
             log.error(tree.pos(), "illegal.start.of.type");
     }
 
+    @Override
     public void visitAssign(JCAssign tree) {
         Type owntype = attribTree(tree.lhs, env.dup(tree), VAR, Type.noType);
         Type capturedType = capture(owntype);
@@ -1671,6 +1697,7 @@ public class Attr extends JCTree.Visitor {
         result = check(tree, capturedType, VAL, pkind, pt);
     }
 
+    @Override
     public void visitAssignop(JCAssignOp tree) {
         // Attribute arguments.
         Type owntype = attribTree(tree.lhs, env, VAR, Type.noType);
@@ -1694,6 +1721,7 @@ public class Attr extends JCTree.Visitor {
         result = check(tree, owntype, VAL, pkind, pt);
     }
 
+    @Override
     public void visitUnary(JCUnary tree) {
         // Attribute arguments.
         Type argtype = (JCTree.PREINC <= tree.getTag() && tree.getTag() <= JCTree.POSTDEC)
@@ -1731,6 +1759,7 @@ public class Attr extends JCTree.Visitor {
         result = check(tree, owntype, VAL, pkind, pt);
     }
 
+    @Override
     public void visitBinary(JCBinary tree) {
         // Attribute arguments.
         Type left = chk.checkNonVoid(tree.lhs.pos(), attribExpr(tree.lhs, env));
@@ -1782,6 +1811,7 @@ public class Attr extends JCTree.Visitor {
         result = check(tree, owntype, VAL, pkind, pt);
     }
 
+    @Override
     public void visitTypeCast(JCTypeCast tree) {
         Type clazztype = attribType(tree.clazz, env);
         chk.validate(tree.clazz, env);
@@ -1792,6 +1822,7 @@ public class Attr extends JCTree.Visitor {
         result = check(tree, capture(owntype), VAL, pkind, pt);
     }
 
+    @Override
     public void visitTypeTest(JCInstanceOf tree) {
         Type exprtype = chk.checkNullOrRefType(
             tree.expr.pos(), attribExpr(tree.expr, env));
@@ -1802,6 +1833,7 @@ public class Attr extends JCTree.Visitor {
         result = check(tree, syms.booleanType, VAL, pkind, pt);
     }
 
+    @Override
     public void visitIndexed(JCArrayAccess tree) {
         Type owntype = types.createErrorType(tree.type);
         Type atype = attribExpr(tree.indexed, env);
@@ -1814,6 +1846,7 @@ public class Attr extends JCTree.Visitor {
         result = check(tree, owntype, VAR, pkind, pt);
     }
 
+    @Override
     public void visitIdent(JCIdent tree) {
         Symbol sym;
         boolean varArgs = false;
@@ -1899,6 +1932,7 @@ public class Attr extends JCTree.Visitor {
         result = checkId(tree, env1.enclClass.sym.type, sym, env, pkind, pt, varArgs);
     }
 
+    @Override
     public void visitSelect(JCFieldAccess tree) {
         // Determine the expected kind of the qualifier expression.
         int skind = 0;
@@ -2509,6 +2543,7 @@ public class Attr extends JCTree.Visitor {
         }
     }
 
+    @Override
     public void visitLiteral(JCLiteral tree) {
         result = check(
             tree, litType(tree.typetag).constType(tree.value), VAL, pkind, pt);
@@ -2520,10 +2555,12 @@ public class Attr extends JCTree.Visitor {
         return (tag == TypeTags.CLASS) ? syms.stringType : syms.typeOfTag[tag];
     }
 
+    @Override
     public void visitTypeIdent(JCPrimitiveTypeTree tree) {
         result = check(tree, syms.typeOfTag[tree.typetag], TYP, pkind, pt);
     }
 
+    @Override
     public void visitTypeArray(JCArrayTypeTree tree) {
         Type etype = attribType(tree.elemtype, env);
         Type type = new ArrayType(etype, syms.arrayClass);
@@ -2534,6 +2571,7 @@ public class Attr extends JCTree.Visitor {
      *  Bound checking is left until later, since types are attributed
      *  before supertype structure is completely known
      */
+    @Override
     public void visitTypeApply(JCTypeApply tree) {
         Type owntype = types.createErrorType(tree.type);
 
@@ -2597,6 +2635,7 @@ public class Attr extends JCTree.Visitor {
         result = check(tree, owntype, TYP, pkind, pt);
     }
 
+    @Override
     public void visitTypeParameter(JCTypeParameter tree) {
         TypeVar a = (TypeVar)tree.type;
         Set<Type> boundSet = new HashSet<Type>();
@@ -2666,6 +2705,7 @@ public class Attr extends JCTree.Visitor {
     }
 
 
+    @Override
     public void visitWildcard(JCWildcard tree) {
         //- System.err.println("visitWildcard("+tree+");");//DEBUG
         Type type = (tree.kind.kind == BoundKind.UNBOUND)
@@ -2677,15 +2717,25 @@ public class Attr extends JCTree.Visitor {
                        TYP, pkind, pt);
     }
 
+    @Override
     public void visitAnnotation(JCAnnotation tree) {
         log.error(tree.pos(), "annotation.not.valid.for.type", pt);
         result = tree.type = syms.errType;
     }
 
+    @Override
     public void visitAnnotatedType(JCAnnotatedType tree) {
         result = tree.type = attribType(tree.getUnderlyingType(), env);
+        // Also check any trees within the annotations
+        // TODO(spoon): this should apply to annotations on other things than types
+        for (JCTypeAnnotation annot : tree.getAnnotations()) {
+          for (JCExpression arg : annot.getArguments()) {
+            attribExpr(arg, env);
+          }
+        }
     }
 
+    @Override
     public void visitErroneous(JCErroneous tree) {
         if (tree.errs != null)
             for (JCTree err : tree.errs)
@@ -2695,6 +2745,7 @@ public class Attr extends JCTree.Visitor {
 
     /** Default visitor method for all other trees.
      */
+    @Override
     public void visitTree(JCTree tree) {
         throw new AssertionError();
     }
@@ -2783,6 +2834,7 @@ public class Attr extends JCTree.Visitor {
         }
     }
 
+    @Override
     public void visitImport(JCImport tree) {
         // nothing to do
     }
@@ -2793,7 +2845,7 @@ public class Attr extends JCTree.Visitor {
         assert c == tree.sym;
 
         // Validate annotations
-        chk.validateAnnotations(tree.mods.annotations, c);
+        chk.validateAnnotations(tree.mods.annotations, c, env);
 
         // Validate type parameters, supertype and interfaces.
         attribBounds(tree.typarams);
@@ -2926,17 +2978,20 @@ public class Attr extends JCTree.Visitor {
     //where
     private final JCTree.Visitor typeAnnotationsValidator =
         new TreeScanner() {
+        @Override
         public void visitAnnotation(JCAnnotation tree) {
             if (tree instanceof JCTypeAnnotation) {
                 chk.validateTypeAnnotation((JCTypeAnnotation)tree, false);
             }
             super.visitAnnotation(tree);
         }
+        @Override
         public void visitTypeParameter(JCTypeParameter tree) {
-            chk.validateTypeAnnotations(tree.annotations, true);
+            chk.validateTypeAnnotations(tree.annotations, true, env);
             // don't call super. skip type annotations
             scan(tree.bounds);
         }
+        @Override
         public void visitMethodDef(JCMethodDecl tree) {
             // need to check static methods
             if ((tree.sym.flags() & Flags.STATIC) != 0) {
diff --git a/langtools/src/share/classes/com/sun/tools/javac/comp/Check.java b/langtools/src/share/classes/com/sun/tools/javac/comp/Check.java
index 09cddea..e169d2a 100644
--- a/langtools/src/share/classes/com/sun/tools/javac/comp/Check.java
+++ b/langtools/src/share/classes/com/sun/tools/javac/comp/Check.java
@@ -25,25 +25,24 @@
 
 package com.sun.tools.javac.comp;
 
-import java.util.*;
-import java.util.Set;
+import static com.sun.tools.javac.code.Flags.*;
+import static com.sun.tools.javac.code.Kinds.*;
+import static com.sun.tools.javac.code.TypeTags.*;
 
 import com.sun.tools.javac.code.*;
+import com.sun.tools.javac.code.Attribute.Compound;
+import com.sun.tools.javac.code.Flags.Flag;
+import com.sun.tools.javac.code.Lint.LintCategory;
+import com.sun.tools.javac.code.Symbol.*;
+import com.sun.tools.javac.code.Type.*;
 import com.sun.tools.javac.jvm.*;
 import com.sun.tools.javac.tree.*;
+import com.sun.tools.javac.tree.JCTree.*;
 import com.sun.tools.javac.util.*;
-import com.sun.tools.javac.util.JCDiagnostic.DiagnosticPosition;
 import com.sun.tools.javac.util.List;
+import com.sun.tools.javac.util.JCDiagnostic.DiagnosticPosition;
 
-import com.sun.tools.javac.tree.JCTree.*;
-import com.sun.tools.javac.code.Lint;
-import com.sun.tools.javac.code.Lint.LintCategory;
-import com.sun.tools.javac.code.Type.*;
-import com.sun.tools.javac.code.Symbol.*;
-
-import static com.sun.tools.javac.code.Flags.*;
-import static com.sun.tools.javac.code.Kinds.*;
-import static com.sun.tools.javac.code.TypeTags.*;
+import java.util.*;
 
 /** Type checking helper class for the attribution phase.
  *
@@ -914,7 +913,7 @@ public class Check {
                                           types.capture(tree.type).allparams());
                 while (args.nonEmpty() && tvars_cap.nonEmpty()) {
                     // Let the actual arguments know their bound
-                    args.head.type.withTypeVar((TypeVar)tvars_cap.head);
+                    args.head.type.withTypeVar(tvars_cap.head);
                     args = args.tail;
                     tvars_cap = tvars_cap.tail;
                 }
@@ -1332,7 +1331,7 @@ public class Check {
             Type st = types.supertype(origin.type);
             if (st.tag != CLASS)
                 return true;
-            MethodSymbol stimpl = m.implementation((ClassSymbol)st.tsym, types, false);
+            MethodSymbol stimpl = m.implementation(st.tsym, types, false);
 
             if (mc != null && ((mc.flags() & INTERFACE) != 0)) {
                 List<Type> intfs = types.interfaces(origin.type);
@@ -1858,25 +1857,25 @@ public class Check {
  * Check annotations
  **************************************************************************/
 
-    /** Annotation types are restricted to primitives, String, an
+    /** Annotation constants are restricted to primitives, String, an
      *  enum, an annotation, Class, Class<?>, Class<? extends
      *  Anything>, arrays of the preceding.
      */
-    void validateAnnotationType(JCTree restype) {
-        // restype may be null if an error occurred, so don't bother validating it
-        if (restype != null) {
-            validateAnnotationType(restype.pos(), restype.type);
-        }
+    void validateAnnotationTypeAsConstant(JCTree restype) {
+    // restype may be null if an error occurred, so don't bother validating it
+     if (restype != null) {
+	 validateAnnotationTypeAsConstant(restype.pos(), restype.type);
+     }
     }
 
-    void validateAnnotationType(DiagnosticPosition pos, Type type) {
+    void validateAnnotationTypeAsConstant(DiagnosticPosition pos, Type type) {
         if (type.isPrimitive()) return;
         if (types.isSameType(type, syms.stringType)) return;
         if ((type.tsym.flags() & Flags.ENUM) != 0) return;
         if ((type.tsym.flags() & Flags.ANNOTATION) != 0) return;
         if (types.lowerBound(type).tsym == syms.classType.tsym) return;
         if (types.isArray(type) && !types.isArray(types.elemtype(type))) {
-            validateAnnotationType(pos, types.elemtype(type));
+            validateAnnotationTypeAsConstant(pos, types.elemtype(type));
             return;
         }
         log.error(pos, "invalid.annotation.member.type");
@@ -1889,6 +1888,9 @@ public class Check {
      * or in the interface annotation.Annotation."
      *
      * @jls3 9.6 Annotation Types
+     * 
+     * Additionally: disallows @AllowNonConstant for methods that
+     * are of array types.  (Not yet specced.)
      */
     void validateAnnotationMethod(DiagnosticPosition pos, MethodSymbol m) {
         for (Type sup = syms.annotationType; sup.tag == CLASS; sup = types.supertype(sup)) {
@@ -1900,19 +1902,39 @@ public class Check {
                     log.error(pos, "intf.annotation.member.clash", e.sym, sup);
             }
         }
+        
+        if (m.getReturnType() instanceof ArrayType
+		&& m.attribute(syms.annotationAllowNonConstant.tsym) != null) {
+	    log.error(pos,
+		    "annotation.array.types.always.constant");
+	}
     }
 
     /** Check the annotations of a symbol.
      */
-    public void validateAnnotations(List<JCAnnotation> annotations, Symbol s) {
+    public void validateAnnotations(List<JCAnnotation> annotations, Symbol s, Env<AttrContext> env) {
         if (skipAnnotations) return;
         for (JCAnnotation a : annotations)
             validateAnnotation(a, s);
+
+        for (Compound attrib : s.getAnnotationMirrors()) {
+            for (Pair<MethodSymbol, Attribute> value : attrib.values) {
+		if (value.snd instanceof Attribute.Expression
+			&& value.fst
+				.attribute(syms.annotationAllowNonConstant.tsym) == null) {
+		    log.error(
+			    (((Attribute.Expression) value.snd)
+				    .getValue()).pos,
+			    "attribute.value.must.be.constant");
+		}
+	    }
+        }
     }
 
     /** Check the type annotations
      */
-    public void validateTypeAnnotations(List<JCTypeAnnotation> annotations, boolean isTypeParameter) {
+    public void validateTypeAnnotations(List<JCTypeAnnotation> annotations, boolean isTypeParameter,
+        Env<AttrContext> env) {
         if (skipAnnotations) return;
         for (JCTypeAnnotation a : annotations)
             validateTypeAnnotation(a, isTypeParameter);
diff --git a/langtools/src/share/classes/com/sun/tools/javac/jvm/ClassFile.java b/langtools/src/share/classes/com/sun/tools/javac/jvm/ClassFile.java
index c560ccb..cef4684 100644
--- a/langtools/src/share/classes/com/sun/tools/javac/jvm/ClassFile.java
+++ b/langtools/src/share/classes/com/sun/tools/javac/jvm/ClassFile.java
@@ -81,6 +81,19 @@ public class ClassFile {
     public final static int CONSTANT_InterfaceMethodref = 11;
     public final static int CONSTANT_NameandType = 12;
 
+    public final static int TREE_ArrayAccess = 1;
+    public final static int TREE_Binary = 2;
+    public final static int TREE_Conditional = 3;
+    public final static int TREE_FieldAccess = 4;
+    public final static int TREE_Ident = 5;
+    public final static int TREE_InstanceOf = 6;
+    public final static int TREE_Literal = 7;
+    public final static int TREE_MethodInvocation = 8;
+    public final static int TREE_NewArray = 9;
+    public final static int TREE_NewClass = 10;
+    public final static int TREE_TypeCast = 11;
+    public final static int TREE_Unary = 12;
+
     public final static int MAX_PARAMETERS = 0xff;
     public final static int MAX_DIMENSIONS = 0xff;
     public final static int MAX_CODE = 0xffff;
@@ -159,14 +172,16 @@ public class ClassFile {
             this.type = type;
         }
 
-        public boolean equals(Object other) {
+        @Override
+	public boolean equals(Object other) {
             return
                 other instanceof NameAndType &&
                 name == ((NameAndType) other).name &&
                 type.equals(((NameAndType) other).type);
         }
 
-        public int hashCode() {
+        @Override
+	public int hashCode() {
             return name.hashCode() * type.hashCode();
         }
     }
diff --git a/langtools/src/share/classes/com/sun/tools/javac/jvm/ClassReader.java b/langtools/src/share/classes/com/sun/tools/javac/jvm/ClassReader.java
index 4fef653..fd3a063 100644
--- a/langtools/src/share/classes/com/sun/tools/javac/jvm/ClassReader.java
+++ b/langtools/src/share/classes/com/sun/tools/javac/jvm/ClassReader.java
@@ -25,34 +25,32 @@
 
 package com.sun.tools.javac.jvm;
 
-import java.io.*;
-import java.net.URI;
-import java.nio.CharBuffer;
-import java.util.EnumSet;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.Set;
-import javax.lang.model.SourceVersion;
-import javax.tools.JavaFileObject;
-import javax.tools.JavaFileManager;
-import javax.tools.JavaFileManager.Location;
-import javax.tools.StandardJavaFileManager;
-
+import static com.sun.tools.javac.code.Flags.*;
+import static com.sun.tools.javac.code.Kinds.*;
+import static com.sun.tools.javac.code.TypeTags.CLASS;
+import static com.sun.tools.javac.jvm.ClassFile.*;
+import static com.sun.tools.javac.jvm.ClassFile.Version.*;
 import static javax.tools.StandardLocation.*;
 
-import com.sun.tools.javac.comp.Annotate;
 import com.sun.tools.javac.code.*;
-import com.sun.tools.javac.code.Type.*;
 import com.sun.tools.javac.code.Symbol.*;
-import com.sun.tools.javac.code.Symtab;
+import com.sun.tools.javac.code.Type.*;
+import com.sun.tools.javac.comp.Annotate;
 import com.sun.tools.javac.file.BaseFileObject;
+import com.sun.tools.javac.jvm.ClassFile.*;
+import com.sun.tools.javac.tree.*;
+import com.sun.tools.javac.tree.JCTree.*;
 import com.sun.tools.javac.util.*;
+import com.sun.tools.javac.util.List;
 
-import static com.sun.tools.javac.code.Flags.*;
-import static com.sun.tools.javac.code.Kinds.*;
-import static com.sun.tools.javac.code.TypeTags.*;
-import static com.sun.tools.javac.jvm.ClassFile.*;
-import static com.sun.tools.javac.jvm.ClassFile.Version.*;
+import java.io.*;
+import java.net.URI;
+import java.nio.CharBuffer;
+import java.util.*;
+
+import javax.lang.model.SourceVersion;
+import javax.tools.*;
+import javax.tools.JavaFileManager.Location;
 
 /** This class provides operations to read a classfile into an internal
  *  representation. The internal representation is anchored in a
@@ -124,6 +122,8 @@ public class ClassReader implements Completer {
 
     /** The name table. */
     final Names names;
+    
+    final TreeMaker treeMaker;
 
     /** Force a completion failure on this name
      */
@@ -233,6 +233,8 @@ public class ClassReader implements Completer {
         names = Names.instance(context);
         syms = Symtab.instance(context);
         types = Types.instance(context);
+        treeMaker = TreeMaker.instance(context);
+        
         fileManager = context.get(JavaFileManager.class);
         if (fileManager == null)
             throw new AssertionError("FileManager initialization error");
@@ -876,7 +878,8 @@ public class ClassReader implements Completer {
             // v45.3 attributes
 
             new AttributeReader(names.Code, V45_3, MEMBER_ATTRIBUTE) {
-                void read(Symbol sym, int attrLen) {
+                @Override
+		void read(Symbol sym, int attrLen) {
                     if (readAllOfClassFile || saveParameterNames)
                         ((MethodSymbol)sym).code = readCode(sym);
                     else
@@ -885,7 +888,8 @@ public class ClassReader implements Completer {
             },
 
             new AttributeReader(names.ConstantValue, V45_3, MEMBER_ATTRIBUTE) {
-                void read(Symbol sym, int attrLen) {
+                @Override
+		void read(Symbol sym, int attrLen) {
                     Object v = readPool(nextChar());
                     // Ignore ConstantValue attribute if field not final.
                     if ((sym.flags() & FINAL) != 0)
@@ -894,13 +898,15 @@ public class ClassReader implements Completer {
             },
 
             new AttributeReader(names.Deprecated, V45_3, CLASS_OR_MEMBER_ATTRIBUTE) {
-                void read(Symbol sym, int attrLen) {
+                @Override
+		void read(Symbol sym, int attrLen) {
                     sym.flags_field |= DEPRECATED;
                 }
             },
 
             new AttributeReader(names.Exceptions, V45_3, CLASS_OR_MEMBER_ATTRIBUTE) {
-                void read(Symbol sym, int attrLen) {
+                @Override
+		void read(Symbol sym, int attrLen) {
                     int nexceptions = nextChar();
                     List<Type> thrown = List.nil();
                     for (int j = 0; j < nexceptions; j++)
@@ -911,14 +917,16 @@ public class ClassReader implements Completer {
             },
 
             new AttributeReader(names.InnerClasses, V45_3, CLASS_ATTRIBUTE) {
-                void read(Symbol sym, int attrLen) {
+                @Override
+		void read(Symbol sym, int attrLen) {
                     ClassSymbol c = (ClassSymbol) sym;
                     readInnerClasses(c);
                 }
             },
 
             new AttributeReader(names.LocalVariableTable, V45_3, CLASS_OR_MEMBER_ATTRIBUTE) {
-                void read(Symbol sym, int attrLen) {
+                @Override
+		void read(Symbol sym, int attrLen) {
                     int newbp = bp + attrLen;
                     if (saveParameterNames) {
                         // pick up parameter names from the variable table
@@ -953,7 +961,8 @@ public class ClassReader implements Completer {
             },
 
             new AttributeReader(names.SourceFile, V45_3, CLASS_ATTRIBUTE) {
-                void read(Symbol sym, int attrLen) {
+                @Override
+		void read(Symbol sym, int attrLen) {
                     ClassSymbol c = (ClassSymbol) sym;
                     Name n = readName(nextChar());
                     c.sourcefile = new SourceFileObject(n, c.flatname);
@@ -961,7 +970,8 @@ public class ClassReader implements Completer {
             },
 
             new AttributeReader(names.Synthetic, V45_3, CLASS_OR_MEMBER_ATTRIBUTE) {
-                void read(Symbol sym, int attrLen) {
+                @Override
+		void read(Symbol sym, int attrLen) {
                     // bridge methods are visible when generics not enabled
                     if (allowGenerics || (sym.flags_field & BRIDGE) == 0)
                         sym.flags_field |= SYNTHETIC;
@@ -971,7 +981,8 @@ public class ClassReader implements Completer {
             // standard v49 attributes
 
             new AttributeReader(names.EnclosingMethod, V49, CLASS_ATTRIBUTE) {
-                void read(Symbol sym, int attrLen) {
+                @Override
+		void read(Symbol sym, int attrLen) {
                     int newbp = bp + attrLen;
                     readEnclosingMethodAttr(sym);
                     bp = newbp;
@@ -984,7 +995,8 @@ public class ClassReader implements Completer {
                     return super.accepts(kind) && allowGenerics;
                 }
 
-                void read(Symbol sym, int attrLen) {
+                @Override
+		void read(Symbol sym, int attrLen) {
                     if (sym.kind == TYP) {
                         ClassSymbol c = (ClassSymbol) sym;
                         readingClassAttr = true;
@@ -1013,31 +1025,36 @@ public class ClassReader implements Completer {
             // v49 annotation attributes
 
             new AttributeReader(names.AnnotationDefault, V49, CLASS_OR_MEMBER_ATTRIBUTE) {
-                void read(Symbol sym, int attrLen) {
+                @Override
+		void read(Symbol sym, int attrLen) {
                     attachAnnotationDefault(sym);
                 }
             },
 
             new AttributeReader(names.RuntimeInvisibleAnnotations, V49, CLASS_OR_MEMBER_ATTRIBUTE) {
-                void read(Symbol sym, int attrLen) {
+                @Override
+		void read(Symbol sym, int attrLen) {
                     attachAnnotations(sym);
                 }
             },
 
             new AttributeReader(names.RuntimeInvisibleParameterAnnotations, V49, CLASS_OR_MEMBER_ATTRIBUTE) {
-                void read(Symbol sym, int attrLen) {
+                @Override
+		void read(Symbol sym, int attrLen) {
                     attachParameterAnnotations(sym);
                 }
             },
 
             new AttributeReader(names.RuntimeVisibleAnnotations, V49, CLASS_OR_MEMBER_ATTRIBUTE) {
-                void read(Symbol sym, int attrLen) {
+                @Override
+		void read(Symbol sym, int attrLen) {
                     attachAnnotations(sym);
                 }
             },
 
             new AttributeReader(names.RuntimeVisibleParameterAnnotations, V49, CLASS_OR_MEMBER_ATTRIBUTE) {
-                void read(Symbol sym, int attrLen) {
+                @Override
+		void read(Symbol sym, int attrLen) {
                     attachParameterAnnotations(sym);
                 }
             },
@@ -1045,14 +1062,16 @@ public class ClassReader implements Completer {
             // additional "legacy" v49 attributes, superceded by flags
 
             new AttributeReader(names.Annotation, V49, CLASS_OR_MEMBER_ATTRIBUTE) {
-                void read(Symbol sym, int attrLen) {
+                @Override
+		void read(Symbol sym, int attrLen) {
                     if (allowAnnotations)
                         sym.flags_field |= ANNOTATION;
                 }
             },
 
             new AttributeReader(names.Bridge, V49, MEMBER_ATTRIBUTE) {
-                void read(Symbol sym, int attrLen) {
+                @Override
+		void read(Symbol sym, int attrLen) {
                     sym.flags_field |= BRIDGE;
                     if (!allowGenerics)
                         sym.flags_field &= ~SYNTHETIC;
@@ -1060,13 +1079,15 @@ public class ClassReader implements Completer {
             },
 
             new AttributeReader(names.Enum, V49, CLASS_OR_MEMBER_ATTRIBUTE) {
-                void read(Symbol sym, int attrLen) {
+                @Override
+		void read(Symbol sym, int attrLen) {
                     sym.flags_field |= ENUM;
                 }
             },
 
             new AttributeReader(names.Varargs, V49, CLASS_OR_MEMBER_ATTRIBUTE) {
-                void read(Symbol sym, int attrLen) {
+                @Override
+		void read(Symbol sym, int attrLen) {
                     if (allowVarargs)
                         sym.flags_field |= VARARGS;
                 }
@@ -1074,13 +1095,15 @@ public class ClassReader implements Completer {
 
             // v51 attributes
             new AttributeReader(names.RuntimeVisibleTypeAnnotations, V49, CLASS_OR_MEMBER_ATTRIBUTE) {
-                void read(Symbol sym, int attrLen) {
+                @Override
+		void read(Symbol sym, int attrLen) {
                     attachTypeAnnotations(sym);
                 }
             },
 
             new AttributeReader(names.RuntimeInvisibleTypeAnnotations, V49, CLASS_OR_MEMBER_ATTRIBUTE) {
-                void read(Symbol sym, int attrLen) {
+                @Override
+		void read(Symbol sym, int attrLen) {
                     attachTypeAnnotations(sym);
                 }
             },
@@ -1486,6 +1509,9 @@ public class ClassReader implements Completer {
                 l.append(readAttributeValue());
             return new ArrayAttributeProxy(l.toList());
         }
+        case 'E':
+            JCExpression expr = readExpression();
+            return new Attribute.Expression(expr.type, expr);
         case '@':
             return readCompoundAnnotation();
         default:
@@ -1493,6 +1519,117 @@ public class ClassReader implements Completer {
         }
     }
 
+    private JCExpression readExpression() {
+	byte exprTag = nextByte();
+	switch (exprTag) {
+	case ClassFile.TREE_ArrayAccess: {
+	    Type type = readType(nextChar());
+	    JCExpression indexed = readExpression();
+	    JCExpression index = readExpression();
+	    return treeMaker.Indexed(indexed, index).setType(type);
+	}
+
+	case ClassFile.TREE_Binary: {
+	    Type type = readType(nextChar());
+	    int tag = nextInt();
+	    JCExpression lhs = readExpression();
+	    JCExpression rhs = readExpression();
+	    return treeMaker.Binary(tag, lhs, rhs).setType(type);
+	}
+
+	case ClassFile.TREE_Conditional: {
+	    Type type = readType(nextChar());
+	    JCExpression cond = readExpression();
+	    JCExpression truepart = readExpression();
+	    JCExpression falsepart = readExpression();
+	    return treeMaker.Conditional(cond, truepart, falsepart).setType(
+		    type);
+	}
+
+	case ClassFile.TREE_FieldAccess: {
+	    Type type = readType(nextChar());
+	    JCExpression selected = readExpression();
+	    Symbol sym = (Symbol) readPool(nextChar());
+	    return treeMaker.Select(selected, sym).setType(type);
+	}
+
+	case ClassFile.TREE_Ident: {
+	    Type type = readType(nextChar());
+	    Symbol sym = (Symbol) readPool(nextChar());
+	    return treeMaker.Ident(sym).setType(type);
+	}
+
+	case ClassFile.TREE_InstanceOf: {
+	    Type type = readType(nextChar());
+	    JCExpression expr = readExpression();
+	    JCExpression clazz = readExpression();
+	    return treeMaker.TypeTest(expr, clazz).setType(type);
+	}
+
+	case ClassFile.TREE_Literal: {
+	    Type type = readType(nextChar());
+	    int typetag = nextChar();
+	    Object value = readPool(nextChar());
+	    return treeMaker.Literal(typetag, value).setType(type);
+	}
+
+	case ClassFile.TREE_MethodInvocation: {
+	    Type type = readType(nextChar());
+	    JCExpression meth = readExpression();
+	    List<JCExpression> typeargs = readExpressions();
+	    List<JCExpression> args = readExpressions();
+	    return treeMaker.Apply(null, meth, args).setType(type);
+	}
+
+	case ClassFile.TREE_NewArray: {
+	    Type type = readType(nextChar());
+	    JCExpression elemtype = readExpression();
+	    List<JCExpression> dims = readExpressions();
+	    List<JCExpression> elems = readExpressions();
+	    return treeMaker.NewArray(elemtype, dims, elems).setType(type);
+	}
+
+	case ClassFile.TREE_NewClass: {
+	    Type type = readType(nextChar());
+	    JCExpression encl = readExpression();
+	    List<JCExpression> typeargs = readExpressions();
+	    JCExpression clazz = readExpression();
+	    List<JCExpression> args = readExpressions();
+	    return treeMaker.NewClass(encl, typeargs, clazz, args, null)
+		    .setType(type);
+	}
+
+	case ClassFile.TREE_TypeCast: {
+	    Type type = readType(nextChar());
+	    JCExpression clazz = readExpression();
+	    JCExpression expr = readExpression();
+	    return treeMaker.TypeCast(clazz, expr).setType(type);
+	}
+
+	case ClassFile.TREE_Unary: {
+	    Type type = readType(nextChar());
+	    int tag = nextChar();
+	    JCExpression arg = readExpression();
+	    return treeMaker.Unary(tag, arg).setType(type);
+	}
+
+	default:
+	    throw new AssertionError("unknown tree tag: " + exprTag);
+	}
+    }
+
+    private List<JCExpression> readExpressions() {
+	int numExpressions = nextChar();
+	ListBuffer<JCExpression> exprs = new ListBuffer<JCExpression>();
+	for (int i = 0; i < numExpressions; i++) {
+	    exprs.add(readExpression());
+	}
+	return exprs.toList();
+    }
+
+    /**
+     * TODO(spoon) this interface probably needs a method for expressions
+     */
     interface ProxyVisitor extends Attribute.Visitor {
         void visitEnumAttributeProxy(EnumAttributeProxy proxy);
         void visitArrayAttributeProxy(ArrayAttributeProxy proxy);
@@ -1507,7 +1644,8 @@ public class ClassReader implements Completer {
             this.enumType = enumType;
             this.enumerator = enumerator;
         }
-        public void accept(Visitor v) { ((ProxyVisitor)v).visitEnumAttributeProxy(this); }
+        @Override
+	public void accept(Visitor v) { ((ProxyVisitor)v).visitEnumAttributeProxy(this); }
         @Override
         public String toString() {
             return "/*proxy enum*/" + enumType + "." + enumerator;
@@ -1520,7 +1658,8 @@ public class ClassReader implements Completer {
             super(null);
             this.values = values;
         }
-        public void accept(Visitor v) { ((ProxyVisitor)v).visitArrayAttributeProxy(this); }
+        @Override
+	public void accept(Visitor v) { ((ProxyVisitor)v).visitArrayAttributeProxy(this); }
         @Override
         public String toString() {
             return "{" + values + "}";
@@ -1536,7 +1675,8 @@ public class ClassReader implements Completer {
             super(type);
             this.values = values;
         }
-        public void accept(Visitor v) { ((ProxyVisitor)v).visitCompoundAnnotationProxy(this); }
+        @Override
+	public void accept(Visitor v) { ((ProxyVisitor)v).visitCompoundAnnotationProxy(this); }
         @Override
         public String toString() {
             StringBuffer buf = new StringBuffer();
@@ -1673,6 +1813,10 @@ public class ClassReader implements Completer {
             throw new AssertionError(); // shouldn't happen
         }
 
+        public void visitExpression(Attribute.Expression expr) {
+            result = expr;
+        }
+
         public void visitError(Attribute.Error e) {
             throw new AssertionError(); // shouldn't happen
         }
@@ -2579,7 +2723,8 @@ public class ClassReader implements Completer {
         }
 
         /** @deprecated see bug 6410637 */
-        @Deprecated
+        @Override
+	@Deprecated
         public String getName() {
             return name.toString();
         }
diff --git a/langtools/src/share/classes/com/sun/tools/javac/jvm/ClassWriter.java b/langtools/src/share/classes/com/sun/tools/javac/jvm/ClassWriter.java
index 4263632..64bde36 100644
--- a/langtools/src/share/classes/com/sun/tools/javac/jvm/ClassWriter.java
+++ b/langtools/src/share/classes/com/sun/tools/javac/jvm/ClassWriter.java
@@ -36,9 +36,11 @@ import javax.tools.JavaFileObject;
 import com.sun.tools.javac.code.*;
 import com.sun.tools.javac.code.Symbol.*;
 import com.sun.tools.javac.code.Type.*;
+import com.sun.tools.javac.tree.JCTree;
+import com.sun.tools.javac.tree.JCTree.*;
+import com.sun.tools.javac.tree.JCTree.Visitor;
 import com.sun.tools.javac.util.*;
 
-import static com.sun.tools.javac.code.BoundKind.*;
 import static com.sun.tools.javac.code.Flags.*;
 import static com.sun.tools.javac.code.Kinds.*;
 import static com.sun.tools.javac.code.TypeTags.*;
@@ -830,6 +832,124 @@ public class ClassWriter extends ClassFile {
         return vis;
     }
 
+    class TreeWriter extends Visitor {
+	public void visitArrayAccess(JCArrayAccess tree) {
+	    databuf.appendByte(ClassFile.TREE_ArrayAccess);
+	    databuf.appendChar(pool.put(typeSig(tree.type)));
+	    tree.indexed.accept(this);
+	    tree.index.accept(this);
+	}
+
+	@Override
+	public void visitBinary(JCBinary tree) {
+	    databuf.appendByte(ClassFile.TREE_Binary);
+	    databuf.appendChar(pool.put(typeSig(tree.type)));
+	    databuf.appendInt(tree.getTag());
+	    tree.getLeftOperand().accept(this);
+	    tree.getRightOperand().accept(this);
+	}
+
+	@Override
+	public void visitConditional(JCConditional tree) {
+	    databuf.appendByte(ClassFile.TREE_Conditional);
+	    databuf.appendChar(pool.put(typeSig(tree.type)));
+	    tree.cond.accept(this);
+	    tree.truepart.accept(this);
+	    tree.falsepart.accept(this);
+	}
+
+	@Override
+	public void visitSelect(JCFieldAccess tree) {
+	    databuf.appendByte(ClassFile.TREE_FieldAccess);
+	    databuf.appendChar(pool.put(typeSig(tree.type)));
+	    tree.selected.accept(this);
+	    databuf.appendChar(pool.put(tree.sym));
+	}
+
+	@Override
+	public void visitIdent(JCIdent tree) {
+	    databuf.appendByte(ClassFile.TREE_Ident);
+	    databuf.appendChar(pool.put(typeSig(tree.type)));
+	    databuf.appendChar(pool.put(tree.sym));
+	}
+
+	public void visiTypeTest(JCInstanceOf tree) {
+	    databuf.appendByte(ClassFile.TREE_InstanceOf);
+	    databuf.appendChar(pool.put(typeSig(tree.type)));
+	    tree.expr.accept(this);
+	    tree.clazz.accept(this);
+	}
+
+	@Override
+	public void visitLiteral(JCLiteral tree) {
+	    databuf.appendByte(ClassFile.TREE_Literal);
+	    databuf.appendChar(pool.put(typeSig(tree.type)));
+	    databuf.appendChar(tree.typetag);
+	    databuf.appendChar(pool.put(tree.value));
+	}
+
+	@Override
+	public void visitApply(JCMethodInvocation tree) {
+	    databuf.appendByte(ClassFile.TREE_MethodInvocation);
+	    databuf.appendChar(pool.put(typeSig(tree.type)));
+	    tree.meth.accept(this);
+	    writeTrees(tree.typeargs);
+	    writeTrees(tree.args);
+	}
+
+	@Override
+	public void visitNewArray(JCNewArray tree) {
+	    databuf.appendByte(ClassFile.TREE_NewArray);
+	    databuf.appendChar(pool.put(typeSig(tree.type)));
+	    tree.elemtype.accept(this);
+	    writeTrees(tree.dims);
+	    writeTrees(tree.elems);
+	}
+
+	@Override
+	public void visitNewClass(JCNewClass tree) {
+	    assert (tree.getClassBody() == null);
+	    databuf.appendByte(ClassFile.TREE_NewClass);
+	    databuf.appendChar(pool.put(typeSig(tree.type)));
+	    tree.encl.accept(this);
+	    writeTrees(tree.typeargs);
+	    tree.clazz.accept(this);
+	    writeTrees(tree.args);
+	}
+
+	@Override
+	public void visitTypeCast(JCTypeCast tree) {
+	    databuf.appendByte(ClassFile.TREE_TypeCast);
+	    databuf.appendChar(pool.put(typeSig(tree.type)));
+	    tree.clazz.accept(this);
+	    tree.expr.accept(this);
+	}
+
+	@Override
+	public void visitUnary(JCUnary tree) {
+	    databuf.appendByte(ClassFile.TREE_Unary);
+	    databuf.appendChar(pool.put(typeSig(tree.type)));
+	    databuf.appendChar(tree.getTag());
+	    tree.arg.accept(this);
+	}
+
+	@Override
+	public void visitTree(JCTree tree) {
+	    throw new AssertionError(
+		    "Unexpected tree as an annotation argument: " + tree + " ("
+			    + tree.getClass() + ")");
+	}
+
+	private void writeTrees(List<JCExpression> exprs) {
+	    databuf.appendChar(exprs.size());
+	    for (JCExpression arg : exprs) {
+		arg.accept(this);
+	    }
+	}
+    }
+
+    private Visitor treeWriter = new TreeWriter();
+	
     /** A visitor to write an attribute including its leading
      *  single-character marker.
      */
@@ -894,6 +1014,10 @@ public class ClassWriter extends ClassFile {
                 a.accept(this);
             }
         }
+        public void visitExpression(Attribute.Expression expr) {
+	    databuf.appendByte('E');
+	    expr.getValue().accept(treeWriter);
+	}
     }
     AttributeWriter awriter = new AttributeWriter();
 
@@ -1361,7 +1485,8 @@ public class ClassWriter extends ClassFile {
             SameFrame(int offsetDelta) {
                 this.offsetDelta = offsetDelta;
             }
-            int getFrameType() {
+            @Override
+	    int getFrameType() {
                 return (offsetDelta < SAME_FRAME_SIZE) ? offsetDelta : SAME_FRAME_EXTENDED;
             }
             @Override
@@ -1383,7 +1508,8 @@ public class ClassWriter extends ClassFile {
                 this.offsetDelta = offsetDelta;
                 this.stack = stack;
             }
-            int getFrameType() {
+            @Override
+	    int getFrameType() {
                 return (offsetDelta < SAME_FRAME_SIZE) ?
                        (SAME_FRAME_SIZE + offsetDelta) :
                        SAME_LOCALS_1_STACK_ITEM_EXTENDED;
@@ -1411,7 +1537,8 @@ public class ClassWriter extends ClassFile {
                 this.frameType = frameType;
                 this.offsetDelta = offsetDelta;
             }
-            int getFrameType() { return frameType; }
+            @Override
+	    int getFrameType() { return frameType; }
             @Override
             void write(ClassWriter writer) {
                 super.write(writer);
@@ -1431,7 +1558,8 @@ public class ClassWriter extends ClassFile {
                 this.offsetDelta = offsetDelta;
                 this.locals = locals;
             }
-            int getFrameType() { return frameType; }
+            @Override
+	    int getFrameType() { return frameType; }
             @Override
             void write(ClassWriter writer) {
                 super.write(writer);
@@ -1455,7 +1583,8 @@ public class ClassWriter extends ClassFile {
                 this.locals = locals;
                 this.stack = stack;
             }
-            int getFrameType() { return FULL_FRAME; }
+            @Override
+	    int getFrameType() { return FULL_FRAME; }
             @Override
             void write(ClassWriter writer) {
                 super.write(writer);
diff --git a/langtools/src/share/classes/com/sun/tools/javac/model/AnnotationProxyMaker.java b/langtools/src/share/classes/com/sun/tools/javac/model/AnnotationProxyMaker.java
index acd6d99..62b7516 100644
--- a/langtools/src/share/classes/com/sun/tools/javac/model/AnnotationProxyMaker.java
+++ b/langtools/src/share/classes/com/sun/tools/javac/model/AnnotationProxyMaker.java
@@ -240,7 +240,11 @@ public class AnnotationProxyMaker {
             }
         }
 
-        public void visitError(Attribute.Error e) {
+	public void visitExpression(Attribute.Expression e) {
+	    value = null;
+	}
+
+	public void visitError(Attribute.Error e) {
             value = null;       // indicates a type mismatch
         }
 
@@ -251,9 +255,11 @@ public class AnnotationProxyMaker {
         private void typeMismatch(final Method method, final Attribute attr) {
             value = new ExceptionProxy() {
                 static final long serialVersionUID = 269;
+                @Override
                 public String toString() {
                     return "<error>";   // eg:  @Anno(value=<error>)
                 }
+                @Override
                 protected RuntimeException generateException() {
                     return new AnnotationTypeMismatchException(method,
                                 attr.type.toString());
@@ -279,20 +285,24 @@ public class AnnotationProxyMaker {
             typeString = t.toString();
         }
 
+        @Override
         public String toString() {
             return typeString;
         }
 
+        @Override
         public int hashCode() {
             return (type != null ? type : typeString).hashCode();
         }
 
+        @Override
         public boolean equals(Object obj) {
             return type != null &&
                    obj instanceof MirroredTypeExceptionProxy &&
                    type.equals(((MirroredTypeExceptionProxy) obj).type);
         }
 
+        @Override
         protected RuntimeException generateException() {
             return new MirroredTypeException(type);
         }
@@ -315,14 +325,17 @@ public class AnnotationProxyMaker {
             typeStrings = ts.toString();
         }
 
+        @Override
         public String toString() {
             return typeStrings;
         }
 
+        @Override
         public int hashCode() {
             return (types != null ? types : typeStrings).hashCode();
         }
 
+        @Override
         public boolean equals(Object obj) {
             return types != null &&
                    obj instanceof MirroredTypesExceptionProxy &&
@@ -330,6 +343,7 @@ public class AnnotationProxyMaker {
                       ((MirroredTypesExceptionProxy) obj).types);
         }
 
+        @Override
         protected RuntimeException generateException() {
             return new MirroredTypesException(types);
         }
diff --git a/langtools/src/share/classes/com/sun/tools/javac/model/JavacElements.java b/langtools/src/share/classes/com/sun/tools/javac/model/JavacElements.java
index 7d233d5..182e9be 100644
--- a/langtools/src/share/classes/com/sun/tools/javac/model/JavacElements.java
+++ b/langtools/src/share/classes/com/sun/tools/javac/model/JavacElements.java
@@ -224,16 +224,20 @@ public class JavacElements implements Elements {
         Symbol sym = cast(Symbol.class, e);
         class Vis extends JCTree.Visitor {
             List<JCAnnotation> result = null;
-            public void visitTopLevel(JCCompilationUnit tree) {
+            @Override
+	    public void visitTopLevel(JCCompilationUnit tree) {
                 result = tree.packageAnnotations;
             }
-            public void visitClassDef(JCClassDecl tree) {
+            @Override
+	    public void visitClassDef(JCClassDecl tree) {
                 result = tree.mods.annotations;
             }
-            public void visitMethodDef(JCMethodDecl tree) {
+            @Override
+	    public void visitMethodDef(JCMethodDecl tree) {
                 result = tree.mods.annotations;
             }
-            public void visitVarDef(JCVariableDecl tree) {
+            @Override
+	    public void visitVarDef(JCVariableDecl tree) {
                 result = tree.mods.annotations;
             }
         }
@@ -308,6 +312,8 @@ public class JavacElements implements Elements {
             }
             public void visitEnum(Attribute.Enum e) {
             }
+            public void visitExpression(Attribute.Expression e) {
+            }
             public void visitError(Attribute.Error e) {
             }
         }
@@ -324,15 +330,18 @@ public class JavacElements implements Elements {
                                        final JCTree tree) {
         class TS extends TreeScanner {
             JCExpression result = null;
-            public void scan(JCTree t) {
+            @Override
+	    public void scan(JCTree t) {
                 if (t != null && result == null)
                     t.accept(this);
             }
-            public void visitAnnotation(JCAnnotation t) {
+            @Override
+	    public void visitAnnotation(JCAnnotation t) {
                 if (t == tree)
                     scan(t.args);
             }
-            public void visitAssign(JCAssign t) {
+            @Override
+	    public void visitAssign(JCAssign t) {
                 if (t.lhs.getTag() == JCTree.IDENT) {
                     JCIdent ident = (JCIdent) t.lhs;
                     if (ident.sym == sym)
diff --git a/langtools/src/share/classes/com/sun/tools/javac/resources/compiler.properties b/langtools/src/share/classes/com/sun/tools/javac/resources/compiler.properties
index 03993f6..5dc10f8 100644
--- a/langtools/src/share/classes/com/sun/tools/javac/resources/compiler.properties
+++ b/langtools/src/share/classes/com/sun/tools/javac/resources/compiler.properties
@@ -41,6 +41,8 @@ compiler.err.already.defined.static.single.import=\
     {0} is already defined in a static single-type import
 compiler.err.already.defined.this.unit=\
     {0} is already defined in this compilation unit
+compiler.err.annotation.array.types.always.constant=\
+    @AllowNonConstant cannot be used with array types
 compiler.err.annotation.missing.default.value=\
     annotation {0} is missing {1}
 compiler.err.annotation.not.valid.for.type=\
diff --git a/langtools/src/share/classes/com/sun/tools/javac/tree/TreeMaker.java b/langtools/src/share/classes/com/sun/tools/javac/tree/TreeMaker.java
index b48841d..73d19ea 100644
--- a/langtools/src/share/classes/com/sun/tools/javac/tree/TreeMaker.java
+++ b/langtools/src/share/classes/com/sun/tools/javac/tree/TreeMaker.java
@@ -749,6 +749,9 @@ public class TreeMaker implements JCTree.Factory {
         public void visitEnum(Attribute.Enum e) {
             result = QualIdent(e.value);
         }
+        public void visitExpression(Attribute.Expression e) {
+            result =  e.getValue();
+        }
         public void visitError(Attribute.Error e) {
             result = Erroneous();
         }
diff --git a/langtools/src/share/classes/com/sun/tools/javadoc/AnnotationValueImpl.java b/langtools/src/share/classes/com/sun/tools/javadoc/AnnotationValueImpl.java
index 0251512..559f18d 100644
--- a/langtools/src/share/classes/com/sun/tools/javadoc/AnnotationValueImpl.java
+++ b/langtools/src/share/classes/com/sun/tools/javadoc/AnnotationValueImpl.java
@@ -102,6 +102,10 @@ public class AnnotationValueImpl implements AnnotationValue {
             }
             value = vals;
         }
+        
+        public void visitExpression(Attribute.Expression e) {
+            value = e;
+        }
 
         public void visitError(Attribute.Error e) {
             value = "<error>";
@@ -114,6 +118,7 @@ public class AnnotationValueImpl implements AnnotationValue {
      * @return the text of a Java language annotation value expression
      *          whose value is the value of this annotation type element.
      */
+    @Override
     public String toString() {
         ToStringVisitor tv = new ToStringVisitor();
         attr.accept(tv);
@@ -123,7 +128,8 @@ public class AnnotationValueImpl implements AnnotationValue {
     private class ToStringVisitor implements Attribute.Visitor {
         private final StringBuffer sb = new StringBuffer();
 
-        public String toString() {
+        @Override
+	public String toString() {
             return sb.toString();
         }
 
@@ -164,6 +170,10 @@ public class AnnotationValueImpl implements AnnotationValue {
             // Omit braces from singleton.
             if (a.values.length != 1) sb.append('}');
         }
+        
+        public void visitExpression(Attribute.Expression e) {
+            sb.append(e);
+        }
 
         public void visitError(Attribute.Error e) {
             sb.append("<error>");
diff --git a/langtools/src/share/classes/java/lang/annotation/AllowNonConstant.java b/langtools/src/share/classes/java/lang/annotation/AllowNonConstant.java
new file mode 100644
index 0000000..1425446
--- /dev/null
+++ b/langtools/src/share/classes/java/lang/annotation/AllowNonConstant.java
@@ -0,0 +1,9 @@
+package java.lang.annotation;
+
+/**
+ * This annotation can be applied to a method within an annotation definition.
+ * It means that when that annotation is used, a non-constant value is allowed
+ * to be supplied for that method.
+ */
+public @interface AllowNonConstant {
+}
diff --git a/langtools/src/share/classes/javax/lang/model/element/AnnotationValue.java b/langtools/src/share/classes/javax/lang/model/element/AnnotationValue.java
index 9447af4..cb1fd4a 100644
--- a/langtools/src/share/classes/javax/lang/model/element/AnnotationValue.java
+++ b/langtools/src/share/classes/javax/lang/model/element/AnnotationValue.java
@@ -26,8 +26,6 @@
 package javax.lang.model.element;
 
 
-import java.util.List;
-import javax.lang.model.type.*;
 
 /**
  * Represents a value of an annotation type element.
@@ -39,6 +37,7 @@ import javax.lang.model.type.*;
  *     <li> {@code AnnotationMirror}
  *     <li> {@code List<? extends AnnotationValue>}
  *              (representing the elements, in declared order, if the value is an array)
+ *     <li> {@code ExpressionTree} (representing a general expression)
  * </ul>
  *
  * @author Joseph D. Darcy
