diff options
author | cushon <cushon@google.com> | 2016-10-05 17:05:23 -0700 |
---|---|---|
committer | Liam Miller-Cushon <cushon@google.com> | 2016-10-05 21:12:30 -0700 |
commit | 4c1981b86b09d9802ce3f773c059c529546048a5 (patch) | |
tree | 0cd55d103d47045c2932a25fc40723630c55bf35 | |
parent | 5a08d0b091f99653ff51f60b10392340f4718f6c (diff) | |
download | turbine-4c1981b86b09d9802ce3f773c059c529546048a5.tar.gz |
Type canonicalization
Canonicalize qualified type names so qualifiers are always the declaring
class of the qualified type. For example, given:
```
class A<T> { class Inner {} }
class B extends A<String> {}
```
The type name `B.Inner` must be canonicalized as `A<String>.Inner` in bytecode.
MOE_MIGRATED_REVID=135300804
32 files changed, 1141 insertions, 44 deletions
diff --git a/java/com/google/turbine/binder/Binder.java b/java/com/google/turbine/binder/Binder.java index 77e85a7..8877e6f 100644 --- a/java/com/google/turbine/binder/Binder.java +++ b/java/com/google/turbine/binder/Binder.java @@ -30,6 +30,7 @@ import com.google.turbine.binder.bound.PackageSourceBoundClass; import com.google.turbine.binder.bound.SourceBoundClass; import com.google.turbine.binder.bound.SourceHeaderBoundClass; import com.google.turbine.binder.bound.SourceTypeBoundClass; +import com.google.turbine.binder.bound.TypeBoundClass; import com.google.turbine.binder.bytecode.BytecodeBoundClass; import com.google.turbine.binder.env.CompoundEnv; import com.google.turbine.binder.env.Env; @@ -82,6 +83,8 @@ public class Binder { Env<SourceTypeBoundClass> tenv = bindTypes(syms, henv, CompoundEnv.<HeaderBoundClass>of(classPathEnv).append(henv)); + tenv = canonicalizeTypes(syms, tenv, CompoundEnv.<TypeBoundClass>of(classPathEnv).append(tenv)); + ImmutableMap.Builder<ClassSymbol, SourceTypeBoundClass> result = ImmutableMap.builder(); for (ClassSymbol sym : syms) { result.put(sym, tenv.get(sym)); @@ -199,6 +202,15 @@ public class Binder { return builder.build(); } + private static Env<SourceTypeBoundClass> canonicalizeTypes( + ImmutableSet<ClassSymbol> syms, Env<SourceTypeBoundClass> stenv, Env<TypeBoundClass> tenv) { + SimpleEnv.Builder<SourceTypeBoundClass> builder = SimpleEnv.builder(); + for (ClassSymbol sym : syms) { + builder.putIfAbsent(sym, CanonicalTypeBinder.bind(sym, stenv.get(sym), tenv)); + } + return builder.build(); + } + /** The result of binding: bound nodes for sources in the compilation, and the classpath. */ public static class BindingResult { private final ImmutableMap<ClassSymbol, SourceTypeBoundClass> units; diff --git a/java/com/google/turbine/binder/CanonicalTypeBinder.java b/java/com/google/turbine/binder/CanonicalTypeBinder.java new file mode 100644 index 0000000..c6a17b6 --- /dev/null +++ b/java/com/google/turbine/binder/CanonicalTypeBinder.java @@ -0,0 +1,122 @@ +/* + * Copyright 2016 Google Inc. All Rights Reserved. + * + * 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 com.google.turbine.binder; + +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; +import com.google.turbine.binder.bound.SourceTypeBoundClass; +import com.google.turbine.binder.bound.SourceTypeBoundClass.FieldInfo; +import com.google.turbine.binder.bound.SourceTypeBoundClass.MethodInfo; +import com.google.turbine.binder.bound.SourceTypeBoundClass.ParamInfo; +import com.google.turbine.binder.bound.TypeBoundClass; +import com.google.turbine.binder.bound.TypeBoundClass.TyVarInfo; +import com.google.turbine.binder.env.Env; +import com.google.turbine.binder.sym.ClassSymbol; +import com.google.turbine.binder.sym.TyVarSymbol; +import com.google.turbine.type.Type; +import com.google.turbine.type.Type.ClassTy; +import com.google.turbine.types.Canonicalize; +import java.util.Map; + +/** + * Canonicalizes all qualified types in a {@link SourceTypeBoundClass} using {@link Canonicalize}. + */ +public class CanonicalTypeBinder { + + static SourceTypeBoundClass bind( + ClassSymbol sym, SourceTypeBoundClass base, Env<TypeBoundClass> env) { + ClassTy superClassType = null; + if (base.superClassType() != null) { + superClassType = Canonicalize.canonicalizeClassTy(env, sym, base.superClassType()); + } + ImmutableList.Builder<ClassTy> interfaceTypes = ImmutableList.builder(); + for (ClassTy i : base.interfaceTypes()) { + interfaceTypes.add(Canonicalize.canonicalizeClassTy(env, sym, i)); + } + ImmutableMap<TyVarSymbol, TyVarInfo> typParamTypes = + typeParameters(env, sym, base.typeParameterTypes()); + ImmutableList<MethodInfo> methods = methods(env, sym, base.methods()); + ImmutableList<FieldInfo> fields = fields(env, sym, base.fields()); + return new SourceTypeBoundClass( + interfaceTypes.build(), + superClassType, + typParamTypes, + base.access(), + methods, + fields, + base.owner(), + base.kind(), + base.children(), + base.superclass(), + base.interfaces(), + base.typeParameters()); + } + + private static ImmutableList<FieldInfo> fields( + Env<TypeBoundClass> env, ClassSymbol sym, ImmutableList<FieldInfo> fields) { + ImmutableList.Builder<FieldInfo> result = ImmutableList.builder(); + for (FieldInfo base : fields) { + result.add( + new FieldInfo( + base.sym(), Canonicalize.canonicalize(env, sym, base.type()), base.access())); + } + return result.build(); + } + + private static ImmutableList<MethodInfo> methods( + Env<TypeBoundClass> env, ClassSymbol sym, ImmutableList<MethodInfo> methods) { + ImmutableList.Builder<MethodInfo> result = ImmutableList.builder(); + for (MethodInfo base : methods) { + ImmutableMap<TyVarSymbol, TyVarInfo> tps = typeParameters(env, sym, base.tyParams()); + Type ret = Canonicalize.canonicalize(env, sym, base.returnType()); + ImmutableList.Builder<ParamInfo> parameters = ImmutableList.builder(); + for (ParamInfo parameter : base.parameters()) { + parameters.add( + new ParamInfo( + Canonicalize.canonicalize(env, sym, parameter.type()), parameter.synthetic())); + } + ImmutableList<Type> exceptions = canonicalizeList(env, sym, base.exceptions()); + result.add( + new MethodInfo(base.sym(), tps, ret, parameters.build(), exceptions, base.access())); + } + return result.build(); + } + + private static ImmutableMap<TyVarSymbol, TyVarInfo> typeParameters( + Env<TypeBoundClass> env, ClassSymbol sym, Map<TyVarSymbol, TyVarInfo> tps) { + ImmutableMap.Builder<TyVarSymbol, TyVarInfo> result = ImmutableMap.builder(); + for (Map.Entry<TyVarSymbol, TyVarInfo> e : tps.entrySet()) { + TyVarInfo info = e.getValue(); + Type superClassBound = null; + if (info.superClassBound() != null) { + superClassBound = Canonicalize.canonicalize(env, sym, info.superClassBound()); + } + ImmutableList<Type> interfaceBounds = canonicalizeList(env, sym, info.interfaceBounds()); + result.put(e.getKey(), new TyVarInfo(superClassBound, interfaceBounds)); + } + return result.build(); + } + + private static ImmutableList<Type> canonicalizeList( + Env<TypeBoundClass> env, ClassSymbol sym, ImmutableList<Type> types) { + ImmutableList.Builder<Type> result = ImmutableList.builder(); + for (Type type : types) { + result.add(Canonicalize.canonicalize(env, sym, type)); + } + return result.build(); + } +} diff --git a/java/com/google/turbine/binder/ClassPathBinder.java b/java/com/google/turbine/binder/ClassPathBinder.java index 73609fc..70c80ca 100644 --- a/java/com/google/turbine/binder/ClassPathBinder.java +++ b/java/com/google/turbine/binder/ClassPathBinder.java @@ -16,8 +16,7 @@ package com.google.turbine.binder; -import static com.google.turbine.binder.env.SimpleEnv.builder; - +import com.google.common.collect.ImmutableMap; import com.google.common.io.ByteStreams; import com.google.turbine.binder.bytecode.BytecodeBoundClass; import com.google.turbine.binder.env.CompoundEnv; @@ -29,9 +28,12 @@ import java.io.IOError; import java.io.IOException; import java.nio.file.Path; import java.util.Enumeration; +import java.util.HashMap; +import java.util.Map; import java.util.jar.JarEntry; import java.util.jar.JarFile; + /** Sets up an environment for symbols on the classpath. */ public class ClassPathBinder { @@ -51,15 +53,25 @@ public class ClassPathBinder { private static Env<BytecodeBoundClass> bindClasspath( TopLevelIndex.Builder tli, Iterable<Path> paths) throws IOException { - SimpleEnv.Builder<BytecodeBoundClass> result = builder(); + Map<ClassSymbol, BytecodeBoundClass> map = new HashMap<>(); + Env<BytecodeBoundClass> benv = + new Env<BytecodeBoundClass>() { + @Override + public BytecodeBoundClass get(ClassSymbol sym) { + return map.get(sym); + } + }; for (Path path : paths) { - bindJar(tli, path, result); + bindJar(tli, path, map, benv); } - return result.build(); + return new SimpleEnv<>(ImmutableMap.copyOf(map)); } private static void bindJar( - TopLevelIndex.Builder tli, Path path, SimpleEnv.Builder<BytecodeBoundClass> env) + TopLevelIndex.Builder tli, + Path path, + Map<ClassSymbol, BytecodeBoundClass> env, + Env<BytecodeBoundClass> benv) throws IOException { // TODO(cushon): consider creating a nio-friendly jar reading abstraction for testing, // that yields something like `Iterable<Pair<String, Supplier<byte[]>>>` @@ -73,7 +85,8 @@ public class ClassPathBinder { continue; } ClassSymbol sym = new ClassSymbol(name.substring(0, name.length() - ".class".length())); - if (env.putIfAbsent(sym, new BytecodeBoundClass(sym, () -> toByteArrayOrDie(jf, je)))) { + if (!env.containsKey(sym)) { + env.put(sym, new BytecodeBoundClass(sym, () -> toByteArrayOrDie(jf, je), benv)); tli.insert(sym); } } diff --git a/java/com/google/turbine/binder/TypeBinder.java b/java/com/google/turbine/binder/TypeBinder.java index 162c3f6..219bc1f 100644 --- a/java/com/google/turbine/binder/TypeBinder.java +++ b/java/com/google/turbine/binder/TypeBinder.java @@ -27,7 +27,7 @@ import com.google.turbine.binder.bound.SourceTypeBoundClass; import com.google.turbine.binder.bound.SourceTypeBoundClass.FieldInfo; import com.google.turbine.binder.bound.SourceTypeBoundClass.MethodInfo; import com.google.turbine.binder.bound.SourceTypeBoundClass.ParamInfo; -import com.google.turbine.binder.bound.SourceTypeBoundClass.TyVarInfo; +import com.google.turbine.binder.bound.TypeBoundClass.TyVarInfo; import com.google.turbine.binder.env.Env; import com.google.turbine.binder.lookup.CompoundScope; import com.google.turbine.binder.lookup.LookupKey; @@ -560,7 +560,7 @@ public class TypeBinder { } else if (t.upper().isPresent()) { return new Type.WildUpperBoundedTy(bindTy(env, scope, t.upper().get())); } else { - return new Type.WildTy(); + return Type.WILD_TY; } } } diff --git a/java/com/google/turbine/binder/bound/SourceTypeBoundClass.java b/java/com/google/turbine/binder/bound/SourceTypeBoundClass.java index bed37ee..8bbf9f0 100644 --- a/java/com/google/turbine/binder/bound/SourceTypeBoundClass.java +++ b/java/com/google/turbine/binder/bound/SourceTypeBoundClass.java @@ -27,7 +27,7 @@ import com.google.turbine.type.Type; import javax.annotation.Nullable; /** A HeaderBoundClass for classes compiled from source. */ -public class SourceTypeBoundClass implements HeaderBoundClass { +public class SourceTypeBoundClass implements TypeBoundClass { private final TurbineTyKind kind; private final ClassSymbol owner; @@ -102,7 +102,6 @@ public class SourceTypeBoundClass implements HeaderBoundClass { return children; } - /** Declared type parameters. */ @Override public ImmutableMap<String, TyVarSymbol> typeParameters() { return typeParameters; @@ -114,6 +113,7 @@ public class SourceTypeBoundClass implements HeaderBoundClass { } /** The super-class type. */ + @Override public Type.ClassTy superClassType() { return superClassType; } @@ -129,6 +129,7 @@ public class SourceTypeBoundClass implements HeaderBoundClass { } /** Declared type parameters. */ + @Override public ImmutableMap<TyVarSymbol, TyVarInfo> typeParameterTypes() { return typeParameterTypes; } @@ -249,25 +250,4 @@ public class SourceTypeBoundClass implements HeaderBoundClass { return access; } } - - /** A type parameter declaration. */ - public static class TyVarInfo { - private final Type superClassBound; - private final ImmutableList<Type> interfaceBounds; - - public TyVarInfo(Type superClassBound, ImmutableList<Type> interfaceBounds) { - this.superClassBound = superClassBound; - this.interfaceBounds = interfaceBounds; - } - - /** A class bound, or {@code null}. */ - public Type superClassBound() { - return superClassBound; - } - - /** Interface type bounds. */ - public ImmutableList<Type> interfaceBounds() { - return interfaceBounds; - } - } } diff --git a/java/com/google/turbine/binder/bound/TypeBoundClass.java b/java/com/google/turbine/binder/bound/TypeBoundClass.java new file mode 100644 index 0000000..169ceb2 --- /dev/null +++ b/java/com/google/turbine/binder/bound/TypeBoundClass.java @@ -0,0 +1,52 @@ +/* + * Copyright 2016 Google Inc. All Rights Reserved. + * + * 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 com.google.turbine.binder.bound; + +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; +import com.google.turbine.binder.sym.TyVarSymbol; +import com.google.turbine.type.Type; + +/** A bound node that augments {@link HeaderBoundClass} with type information. */ +public interface TypeBoundClass extends HeaderBoundClass { + + /** The super-class type. */ + Type.ClassTy superClassType(); + + ImmutableMap<TyVarSymbol, TyVarInfo> typeParameterTypes(); + + /** A type parameter declaration. */ + class TyVarInfo { + private final Type superClassBound; + private final ImmutableList<Type> interfaceBounds; + + public TyVarInfo(Type superClassBound, ImmutableList<Type> interfaceBounds) { + this.superClassBound = superClassBound; + this.interfaceBounds = interfaceBounds; + } + + /** A class bound, or {@code null}. */ + public Type superClassBound() { + return superClassBound; + } + + /** Interface type bounds. */ + public ImmutableList<Type> interfaceBounds() { + return interfaceBounds; + } + } +} diff --git a/java/com/google/turbine/binder/bytecode/BytecodeBinder.java b/java/com/google/turbine/binder/bytecode/BytecodeBinder.java new file mode 100644 index 0000000..85e26e2 --- /dev/null +++ b/java/com/google/turbine/binder/bytecode/BytecodeBinder.java @@ -0,0 +1,88 @@ +/* + * Copyright 2016 Google Inc. All Rights Reserved. + * + * 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 com.google.turbine.binder.bytecode; + +import com.google.common.collect.ImmutableList; +import com.google.turbine.binder.sym.ClassSymbol; +import com.google.turbine.binder.sym.TyVarSymbol; +import com.google.turbine.bytecode.sig.Sig; +import com.google.turbine.type.Type; +import com.google.turbine.type.Type.ConcreteTyArg; +import com.google.turbine.type.Type.TyVar; +import com.google.turbine.type.Type.WildLowerBoundedTy; +import com.google.turbine.type.Type.WildUpperBoundedTy; +import java.util.ArrayList; +import java.util.List; +import java.util.function.Function; + +/** Bind {@link Type}s from bytecode. */ +public class BytecodeBinder { + + static Type.ClassTy bindClassTy(Sig.ClassTySig sig, Function<String, TyVarSymbol> scope) { + StringBuilder sb = new StringBuilder(sig.pkg()); + boolean first = true; + List<Type.ClassTy.SimpleClassTy> classes = new ArrayList<>(); + for (Sig.SimpleClassTySig s : sig.classes()) { + sb.append(first ? '/' : '$'); + sb.append(s.simpleName()); + ClassSymbol sym = new ClassSymbol(sb.toString()); + + ImmutableList.Builder<Type.TyArg> tyArgs = ImmutableList.builder(); + for (Sig.TyArgSig sig1 : s.tyArgs()) { + tyArgs.add(bindTyArg(sig1, scope)); + } + + classes.add(new Type.ClassTy.SimpleClassTy(sym, tyArgs.build())); + first = false; + } + return new Type.ClassTy(classes); + } + + private static Type.TyArg bindTyArg(Sig.TyArgSig sig, Function<String, TyVarSymbol> scope) { + switch (sig.kind()) { + case UNBOUNDED: + return Type.WILD_TY; + case LOWER_BOUNDED: + return new WildLowerBoundedTy(bindTy(((Sig.LowerBoundTyArgSig) sig).bound(), scope)); + case UPPER_BOUNDED: + return new WildUpperBoundedTy(bindTy(((Sig.UpperBoundTyArgSig) sig).bound(), scope)); + case CONCRETE: + return new ConcreteTyArg(bindTy(((Sig.ConcreteTyArgSig) sig).type(), scope)); + default: + throw new AssertionError(sig.kind()); + } + } + + static Type bindTy(Sig.TySig sig, Function<String, TyVarSymbol> scope) { + switch (sig.kind()) { + case BASE_TY_SIG: + return new Type.PrimTy(((Sig.BaseTySig) sig).type()); + case CLASS_TY_SIG: + return bindClassTy((Sig.ClassTySig) sig, scope); + case TY_VAR_SIG: + return new TyVar(scope.apply(((Sig.TyVarSig) sig).name())); + case ARRAY_TY_SIG: + return bindArrayTy((Sig.ArrayTySig) sig, scope); + default: + throw new AssertionError(sig.kind()); + } + } + + private static Type bindArrayTy(Sig.ArrayTySig arrayTySig, Function<String, TyVarSymbol> scope) { + return new Type.ArrayTy(arrayTySig.dimension(), bindTy(arrayTySig.elementType(), scope)); + } +} diff --git a/java/com/google/turbine/binder/bytecode/BytecodeBoundClass.java b/java/com/google/turbine/binder/bytecode/BytecodeBoundClass.java index 1e546a7..9964b40 100644 --- a/java/com/google/turbine/binder/bytecode/BytecodeBoundClass.java +++ b/java/com/google/turbine/binder/bytecode/BytecodeBoundClass.java @@ -24,6 +24,8 @@ import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.google.turbine.binder.bound.BoundClass; import com.google.turbine.binder.bound.HeaderBoundClass; +import com.google.turbine.binder.bound.TypeBoundClass; +import com.google.turbine.binder.env.Env; import com.google.turbine.binder.sym.ClassSymbol; import com.google.turbine.binder.sym.TyVarSymbol; import com.google.turbine.bytecode.ClassFile; @@ -33,6 +35,10 @@ import com.google.turbine.bytecode.sig.Sig.ClassSig; import com.google.turbine.bytecode.sig.SigParser; import com.google.turbine.model.TurbineFlag; import com.google.turbine.model.TurbineTyKind; +import com.google.turbine.type.Type; +import com.google.turbine.type.Type.ClassTy; +import java.util.Map; +import java.util.function.Function; import javax.annotation.Nullable; /** @@ -43,13 +49,16 @@ import javax.annotation.Nullable; * resolved and canonicalized so there are no cycles. The laziness also minimizes the amount of work * done on the classpath. */ -public class BytecodeBoundClass implements BoundClass, HeaderBoundClass { +public class BytecodeBoundClass implements BoundClass, HeaderBoundClass, TypeBoundClass { private final ClassSymbol sym; + private final Env<BytecodeBoundClass> env; private final Supplier<ClassFile> classFile; - public BytecodeBoundClass(ClassSymbol sym, final Supplier<byte[]> bytes) { + public BytecodeBoundClass( + ClassSymbol sym, final Supplier<byte[]> bytes, Env<BytecodeBoundClass> env) { this.sym = sym; + this.env = env; this.classFile = Suppliers.memoize( new Supplier<ClassFile>() { @@ -220,4 +229,91 @@ public class BytecodeBoundClass implements BoundClass, HeaderBoundClass { public ImmutableList<ClassSymbol> interfaces() { return interfaces.get(); } + + Supplier<ClassTy> superClassType = + Suppliers.memoize( + new Supplier<ClassTy>() { + @Override + public ClassTy get() { + if (superclass() == null) { + return null; + } + if (sig.get() == null || sig.get().superClass() == null) { + return ClassTy.asNonParametricClassTy(superclass()); + } + return BytecodeBinder.bindClassTy( + sig.get().superClass(), makeScope(env, sym, ImmutableMap.of())); + } + }); + + @Override + public ClassTy superClassType() { + return superClassType.get(); + } + + Supplier<ImmutableMap<TyVarSymbol, TyVarInfo>> typeParameterTypes = + Suppliers.memoize( + new Supplier<ImmutableMap<TyVarSymbol, TyVarInfo>>() { + @Override + public ImmutableMap<TyVarSymbol, TyVarInfo> get() { + if (sig.get() == null) { + return ImmutableMap.of(); + } + ImmutableMap.Builder<TyVarSymbol, TyVarInfo> tparams = ImmutableMap.builder(); + Function<String, TyVarSymbol> scope = makeScope(env, sym, typeParameters()); + for (Sig.TyParamSig p : sig.get().tyParams()) { + tparams.put(typeParameters().get(p.name()), bindTyParam(p, scope)); + } + return tparams.build(); + } + }); + + private static TyVarInfo bindTyParam(Sig.TyParamSig sig, Function<String, TyVarSymbol> scope) { + Type superClassBound = null; + if (sig.classBound() != null) { + superClassBound = BytecodeBinder.bindTy(sig.classBound(), scope); + } + ImmutableList.Builder<Type> interfaceBounds = ImmutableList.builder(); + for (Sig.TySig t : sig.interfaceBounds()) { + interfaceBounds.add(BytecodeBinder.bindTy(t, scope)); + } + return new TyVarInfo(superClassBound, interfaceBounds.build()); + } + + @Override + public ImmutableMap<TyVarSymbol, TyVarInfo> typeParameterTypes() { + return typeParameterTypes.get(); + } + + /** + * Create a scope for resolving type variable symbols declared in the class, and any enclosing + * instances. + */ + private static Function<String, TyVarSymbol> makeScope( + final Env<BytecodeBoundClass> env, + final ClassSymbol sym, + final Map<String, TyVarSymbol> typeVariables) { + return new Function<String, TyVarSymbol>() { + @Override + public TyVarSymbol apply(String input) { + TyVarSymbol result = typeVariables.get(input); + if (result != null) { + return result; + } + ClassSymbol curr = sym; + while (curr != null) { + BytecodeBoundClass info = env.get(curr); + if (info == null) { + throw new AssertionError(curr); + } + result = info.typeParameters().get(input); + if (result != null) { + return result; + } + curr = info.owner(); + } + throw new AssertionError(input); + } + }; + } } diff --git a/java/com/google/turbine/lower/Lower.java b/java/com/google/turbine/lower/Lower.java index c759a20..9230e29 100644 --- a/java/com/google/turbine/lower/Lower.java +++ b/java/com/google/turbine/lower/Lower.java @@ -24,7 +24,7 @@ import com.google.turbine.binder.bound.SourceTypeBoundClass; import com.google.turbine.binder.bound.SourceTypeBoundClass.FieldInfo; import com.google.turbine.binder.bound.SourceTypeBoundClass.MethodInfo; import com.google.turbine.binder.bound.SourceTypeBoundClass.ParamInfo; -import com.google.turbine.binder.bound.SourceTypeBoundClass.TyVarInfo; +import com.google.turbine.binder.bound.TypeBoundClass.TyVarInfo; import com.google.turbine.binder.bytecode.BytecodeBoundClass; import com.google.turbine.binder.env.CompoundEnv; import com.google.turbine.binder.env.Env; diff --git a/java/com/google/turbine/type/Type.java b/java/com/google/turbine/type/Type.java index 004f974..5f9e8f8 100644 --- a/java/com/google/turbine/type/Type.java +++ b/java/com/google/turbine/type/Type.java @@ -295,11 +295,11 @@ public interface Type { } /** An unbounded wildcard type. */ - class WildTy extends TyArg { - - @Override - public TyArgKind tyArgKind() { - return TyArgKind.WILD; - } - } + TyArg WILD_TY = + new TyArg() { + @Override + public TyArgKind tyArgKind() { + return TyArgKind.WILD; + } + }; } diff --git a/java/com/google/turbine/types/Canonicalize.java b/java/com/google/turbine/types/Canonicalize.java new file mode 100644 index 0000000..8aa5beb --- /dev/null +++ b/java/com/google/turbine/types/Canonicalize.java @@ -0,0 +1,286 @@ +/* + * Copyright 2016 Google Inc. All Rights Reserved. + * + * 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 com.google.turbine.types; + +import com.google.common.base.Verify; +import com.google.common.collect.ImmutableList; +import com.google.turbine.binder.bound.TypeBoundClass; +import com.google.turbine.binder.env.Env; +import com.google.turbine.binder.sym.ClassSymbol; +import com.google.turbine.binder.sym.TyVarSymbol; +import com.google.turbine.model.TurbineFlag; +import com.google.turbine.type.Type; +import com.google.turbine.type.Type.ClassTy; +import java.util.ArrayDeque; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.Deque; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import javax.annotation.Nullable; + +/** + * Canonicalizes qualified type names so qualifiers are always the declaring class of the qualified + * type. + * + * <p>For example, given: + * + * <pre>{@code + * class A<T> { + * class Inner {} + * } + * class B extends A<String> { + * Inner i; + * } + * }</pre> + * + * <p>The canonical name of the type of {@code B.i} is {@code A<String>.Inner}, not {@code B.Inner}. + */ +public class Canonicalize { + + /** Canonicalizes the given type. */ + public static Type canonicalize(Env<TypeBoundClass> env, ClassSymbol base, Type type) { + switch (type.tyKind()) { + case PRIM_TY: + case VOID_TY: + case TY_VAR: + return type; + case ARRAY_TY: + { + Type.ArrayTy arrayTy = (Type.ArrayTy) type; + return new Type.ArrayTy( + arrayTy.dimension(), canonicalize(env, base, arrayTy.elementType())); + } + case CLASS_TY: + return canonicalizeClassTy(env, base, (ClassTy) type); + default: + throw new AssertionError(type.tyKind()); + } + } + + /** Canonicalize a qualified class type, excluding type arguments. */ + private static ClassTy canon(Env<TypeBoundClass> env, ClassSymbol base, ClassTy ty) { + // if the first name is a simple name resolved inside a nested class, add explicit qualifiers + // for the enclosing declarations + Iterator<ClassTy.SimpleClassTy> it = ty.classes.iterator(); + Collection<ClassTy.SimpleClassTy> lexicalBase = lexicalBase(env, ty.classes.get(0).sym(), base); + ClassTy canon = + !lexicalBase.isEmpty() + ? new ClassTy(lexicalBase) + : new ClassTy(Collections.singletonList(it.next())); + + // canonicalize each additional simple name that appeared in source + while (it.hasNext()) { + canon = canonOne(env, canon, it.next()); + } + return canon; + } + + /** Given a base symbol to canonicalize, find any implicit enclosing instances. */ + private static Collection<ClassTy.SimpleClassTy> lexicalBase( + Env<TypeBoundClass> env, ClassSymbol first, ClassSymbol owner) { + if ((env.get(first).access() & TurbineFlag.ACC_STATIC) == TurbineFlag.ACC_STATIC) { + return Collections.emptyList(); + } + ClassSymbol canonOwner = env.get(first).owner(); + Deque<ClassTy.SimpleClassTy> result = new ArrayDeque<>(); + while (canonOwner != null && owner != null) { + if (!isSubclass(env, owner, canonOwner)) { + owner = env.get(owner).owner(); + continue; + } + result.addFirst(uninstantiated(env, owner)); + if ((env.get(owner).access() & TurbineFlag.ACC_STATIC) == TurbineFlag.ACC_STATIC) { + break; + } + canonOwner = env.get(canonOwner).owner(); + } + return result; + } + + private static ClassTy.SimpleClassTy uninstantiated(Env<TypeBoundClass> env, ClassSymbol owner) { + ImmutableList.Builder<Type.TyArg> targs = ImmutableList.builder(); + for (TyVarSymbol p : env.get(owner).typeParameterTypes().keySet()) { + targs.add(new Type.ConcreteTyArg(new Type.TyVar(p))); + } + return new ClassTy.SimpleClassTy(owner, targs.build()); + } + + // is s a subclass (not interface) of t? + static boolean isSubclass(Env<TypeBoundClass> env, ClassSymbol s, ClassSymbol t) { + while (s != null) { + if (s.equals(t)) { + return true; + } + s = env.get(s).superclass(); + } + return false; + } + + /** + * Adds a simple class type to an existing canonical base class type, and canonicalizes the + * result. + */ + private static ClassTy canonOne(Env<TypeBoundClass> env, ClassTy base, ClassTy.SimpleClassTy ty) { + // if the class is static, it has a trivial canonical qualifier with no type arguments + if ((env.get(ty.sym()).access() & TurbineFlag.ACC_STATIC) == TurbineFlag.ACC_STATIC) { + return new ClassTy(Collections.singletonList(ty)); + } + ImmutableList.Builder<ClassTy.SimpleClassTy> simples = ImmutableList.builder(); + ClassSymbol owner = env.get(ty.sym()).owner(); + if (owner.equals(base.sym())) { + // if the canonical prefix is the owner the next symbol in the qualified name, + // the type is already in canonical form + simples.addAll(base.classes); + simples.add(ty); + return new ClassTy(simples.build()); + } + // ... otherwise, find the supertype the class was inherited from + // and instantiate it as a member of the current class + ClassTy curr = base; + Map<TyVarSymbol, Type.TyArg> mapping = new LinkedHashMap<>(); + while (curr != null) { + for (ClassTy.SimpleClassTy s : curr.classes) { + addInstantiation(env, mapping, s); + } + if (curr.sym().equals(owner)) { + for (ClassTy.SimpleClassTy s : curr.classes) { + simples.add(instantiate(env, mapping, s.sym())); + } + break; + } + curr = canon(env, curr.sym(), env.get(curr.sym()).superClassType()); + } + simples.add(ty); + return new ClassTy(simples.build()); + } + + /** Add the type arguments of a simple class type to a type mapping. */ + static void addInstantiation( + Env<TypeBoundClass> env, + Map<TyVarSymbol, Type.TyArg> mapping, + ClassTy.SimpleClassTy simpleType) { + Collection<TyVarSymbol> symbols = env.get(simpleType.sym()).typeParameters().values(); + if (simpleType.targs().isEmpty()) { + // the type is raw + for (TyVarSymbol sym : symbols) { + mapping.put(sym, null); + } + return; + } + // otherwise, it is an instantiated generic type + Verify.verify(symbols.size() == simpleType.targs().size()); + Iterator<Type.TyArg> typeArguments = simpleType.targs().iterator(); + for (TyVarSymbol sym : symbols) { + Type.TyArg argument = typeArguments.next(); + if (Objects.equals(tyVarSym(argument), sym)) { + continue; + } + mapping.put(sym, argument); + } + } + + /** Instantiate a simple class type for the given symbol, and with the given type mapping. */ + static ClassTy.SimpleClassTy instantiate( + Env<TypeBoundClass> env, Map<TyVarSymbol, Type.TyArg> mapping, ClassSymbol classSymbol) { + List<Type.TyArg> args = new ArrayList<>(); + for (TyVarSymbol sym : env.get(classSymbol).typeParameterTypes().keySet()) { + if (!mapping.containsKey(sym)) { + args.add(new Type.ConcreteTyArg(new Type.TyVar(sym))); + continue; + } + Type.TyArg arg = instantiate(mapping, mapping.get(sym)); + if (arg == null) { + // raw types + args.clear(); + break; + } + args.add(arg); + } + return new ClassTy.SimpleClassTy(classSymbol, ImmutableList.copyOf(args)); + } + + /** Instantiates a type argument using the given mapping. */ + private static Type.TyArg instantiate( + Map<TyVarSymbol, Type.TyArg> mapping, Type.TyArg typeArgument) { + if (typeArgument == null) { + return null; + } + TyVarSymbol sym = tyVarSym(typeArgument); + if (!mapping.containsKey(sym)) { + return typeArgument; + } + return instantiate(mapping, mapping.get(sym)); + } + + /** + * Returns the type variable symbol for a concrete type argument whose type is a type variable + * reference, or else {@code null}. + */ + @Nullable + static TyVarSymbol tyVarSym(Type.TyArg typeArgument) { + if (typeArgument.tyArgKind() != Type.TyArg.TyArgKind.CONCRETE) { + return null; + } + Type.ConcreteTyArg concrete = (Type.ConcreteTyArg) typeArgument; + if (concrete.type().tyKind() != Type.TyKind.TY_VAR) { + return null; + } + return ((Type.TyVar) concrete.type()).sym(); + } + + public static ClassTy canonicalizeClassTy(Env<TypeBoundClass> env, ClassSymbol base, ClassTy ty) { + // canonicalize type arguments first + ImmutableList.Builder<ClassTy.SimpleClassTy> args = ImmutableList.builder(); + for (ClassTy.SimpleClassTy s : ty.classes) { + args.add(new ClassTy.SimpleClassTy(s.sym(), canonicalizeTyArgs(s.targs(), base, env))); + } + ty = new ClassTy(args.build()); + return canon(env, base, ty); + } + + private static ImmutableList<Type.TyArg> canonicalizeTyArgs( + ImmutableList<Type.TyArg> targs, ClassSymbol base, Env<TypeBoundClass> env) { + ImmutableList.Builder<Type.TyArg> result = ImmutableList.builder(); + for (Type.TyArg a : targs) { + result.add(canonicalizeTyArg(a, base, env)); + } + return result.build(); + } + + private static Type.TyArg canonicalizeTyArg( + Type.TyArg tyArg, ClassSymbol base, Env<TypeBoundClass> env) { + switch (tyArg.tyArgKind()) { + case CONCRETE: + return new Type.ConcreteTyArg(canonicalize(env, base, ((Type.ConcreteTyArg) tyArg).type())); + case WILD: + return tyArg; + case LOWER_WILD: + return new Type.WildLowerBoundedTy( + canonicalize(env, base, ((Type.WildLowerBoundedTy) tyArg).bound())); + case UPPER_WILD: + return new Type.WildUpperBoundedTy( + canonicalize(env, base, ((Type.WildUpperBoundedTy) tyArg).bound())); + default: + throw new AssertionError(tyArg.tyArgKind()); + } + } +} diff --git a/javatests/com/google/turbine/binder/ClassPathBinderTest.java b/javatests/com/google/turbine/binder/ClassPathBinderTest.java index 66a1fcc..f494277 100644 --- a/javatests/com/google/turbine/binder/ClassPathBinderTest.java +++ b/javatests/com/google/turbine/binder/ClassPathBinderTest.java @@ -107,7 +107,8 @@ public class ClassPathBinderTest { } catch (IOException e) { throw new IOError(e); } - }); + }, + null); try { c.owner(); fail(); diff --git a/javatests/com/google/turbine/lower/LowerIntegrationTest.java b/javatests/com/google/turbine/lower/LowerIntegrationTest.java index 7391854..c640359 100644 --- a/javatests/com/google/turbine/lower/LowerIntegrationTest.java +++ b/javatests/com/google/turbine/lower/LowerIntegrationTest.java @@ -168,6 +168,24 @@ public class LowerIntegrationTest { "vanillaexception.test", "varargs.test", "wild.test", + "bytenoncanon.test", + "canon.test", + "genericnoncanon.test", + "genericnoncanon1.test", + "genericnoncanon10.test", + "genericnoncanon2.test", + "genericnoncanon3.test", + "genericnoncanon4.test", + "genericnoncanon5.test", + "genericnoncanon6.test", + "genericnoncanon8.test", + "genericnoncanon9.test", + "genericnoncanon_byte.test", + "genericnoncanon_method3.test", + "noncanon.test", + "rawcanon.test", + "wildboundcanon.test", + "wildcanon.test", }; return ImmutableList.copyOf(testCases).stream().map(x -> new Object[] {x}).collect(toList()); } diff --git a/javatests/com/google/turbine/lower/LowerSignatureTest.java b/javatests/com/google/turbine/lower/LowerSignatureTest.java index cfd57cf..6f3e87c 100644 --- a/javatests/com/google/turbine/lower/LowerSignatureTest.java +++ b/javatests/com/google/turbine/lower/LowerSignatureTest.java @@ -98,7 +98,7 @@ public class LowerSignatureTest { new Type.ClassTy.SimpleClassTy( new ClassSymbol("test/Test"), ImmutableList.of( - new Type.WildTy(), + Type.WILD_TY, new Type.WildLowerBoundedTy(Type.ClassTy.OBJECT), new Type.WildUpperBoundedTy(Type.ClassTy.OBJECT)))))))) .isEqualTo("Ltest/Test<*-Ljava/lang/Object;+Ljava/lang/Object;>;"); diff --git a/javatests/com/google/turbine/lower/testdata/bytenoncanon.test b/javatests/com/google/turbine/lower/testdata/bytenoncanon.test new file mode 100644 index 0000000..6e5a304 --- /dev/null +++ b/javatests/com/google/turbine/lower/testdata/bytenoncanon.test @@ -0,0 +1,22 @@ +%%% lib/A.java %%% +package lib; + +public class A { + public static class Inner { + } +} + +%%% lib/B.java %%% +package lib; + +public class B extends A { +} + +=== test/Test.java === +package test; + +import lib.B; + +public class Test { + B.Inner i; +} diff --git a/javatests/com/google/turbine/lower/testdata/canon.test b/javatests/com/google/turbine/lower/testdata/canon.test new file mode 100644 index 0000000..870364a --- /dev/null +++ b/javatests/com/google/turbine/lower/testdata/canon.test @@ -0,0 +1,11 @@ +=== C.java === + +public class C extends D.I { +} + +=== D.java === + +public class D { + public static class I { + } +} diff --git a/javatests/com/google/turbine/lower/testdata/genericnoncanon.test b/javatests/com/google/turbine/lower/testdata/genericnoncanon.test new file mode 100644 index 0000000..8df812e --- /dev/null +++ b/javatests/com/google/turbine/lower/testdata/genericnoncanon.test @@ -0,0 +1,45 @@ +=== test/A.java === +package test; + +public class A<T> { + public class Inner { + } +} + +=== test/B.java === +package test; + +public class B extends A<Test> { +} + +=== test/C.java === +package test; + +public class C<T> extends A<T> { +} + +=== test/D.java === +package test; + +public class D<T> { + public class Inner extends A<T> { + } + public class F<T> extends D<T>.Inner { + } +} + +=== test/E.java === +package test; + +public class E<T> extends D<T> { +} + +=== test/Test.java === +package test; + +public class Test { + B.Inner i0; + C<Test>.Inner i1; + E<Test>.Inner.Inner i2; + D<D>.F<Test>.Inner i3; +} diff --git a/javatests/com/google/turbine/lower/testdata/genericnoncanon1.test b/javatests/com/google/turbine/lower/testdata/genericnoncanon1.test new file mode 100644 index 0000000..1a1ad52 --- /dev/null +++ b/javatests/com/google/turbine/lower/testdata/genericnoncanon1.test @@ -0,0 +1,26 @@ +=== test/A0.java === +package test; + +public class A0<Z> { + public class Inner { + } +} + +=== test/A.java === +package test; + +public class A<N> extends A0<N> { +} + +=== test/B.java === +package test; + +public class B<U> extends A<U> { +} + +=== test/Test.java === +package test; + +public class Test { + B<Test>.Inner i0; +} diff --git a/javatests/com/google/turbine/lower/testdata/genericnoncanon10.test b/javatests/com/google/turbine/lower/testdata/genericnoncanon10.test new file mode 100644 index 0000000..6181131 --- /dev/null +++ b/javatests/com/google/turbine/lower/testdata/genericnoncanon10.test @@ -0,0 +1,22 @@ +=== test/B.java === +package test; + +public class B<U> { + public class One extends B<Long>.Two { + } + public class Two extends B<Short>.Three { + } + public class Three extends B<Float>.Four { + public class Inner { + } + } + public class Four { + } +} + +=== test/Test.java === +package test; + +public class Test { + B<Test>.One.Inner i3; +} diff --git a/javatests/com/google/turbine/lower/testdata/genericnoncanon2.test b/javatests/com/google/turbine/lower/testdata/genericnoncanon2.test new file mode 100644 index 0000000..2a45fd1 --- /dev/null +++ b/javatests/com/google/turbine/lower/testdata/genericnoncanon2.test @@ -0,0 +1,28 @@ +=== test/A.java === +package test; + +public class A<T> { + public class Inner { + } +} + +=== test/D.java === +package test; + +public class D<T> { + public class Inner extends A<T> { + } +} + +=== test/E.java === +package test; + +public class E<T> extends D<T> { +} + +=== test/Test.java === +package test; + +public class Test { + E<Test>.Inner.Inner i2; +} diff --git a/javatests/com/google/turbine/lower/testdata/genericnoncanon3.test b/javatests/com/google/turbine/lower/testdata/genericnoncanon3.test new file mode 100644 index 0000000..a49a193 --- /dev/null +++ b/javatests/com/google/turbine/lower/testdata/genericnoncanon3.test @@ -0,0 +1,24 @@ +=== test/A.java === +package test; + +public class A<U> { + public class Inner { + } +} + +=== test/D.java === +package test; + +public class D<Z> { + public class Inner extends A<Z> { + } + public class F<X> extends D<X>.Inner { + } +} + +=== test/Test.java === +package test; + +public class Test { + D<D<Test>>.F<Test>.Inner i3; +} diff --git a/javatests/com/google/turbine/lower/testdata/genericnoncanon4.test b/javatests/com/google/turbine/lower/testdata/genericnoncanon4.test new file mode 100644 index 0000000..5193e2b --- /dev/null +++ b/javatests/com/google/turbine/lower/testdata/genericnoncanon4.test @@ -0,0 +1,24 @@ +=== test/A.java === +package test; + +public class A<U> { + public class Inner { + } +} + +=== test/D.java === +package test; + +public class D<Z> { + public class Inner { + } + public class F<X> extends D<X>.Inner { + } +} + +=== test/Test.java === +package test; + +public class Test { + D<D<Test>>.F<Test> i3; +} diff --git a/javatests/com/google/turbine/lower/testdata/genericnoncanon5.test b/javatests/com/google/turbine/lower/testdata/genericnoncanon5.test new file mode 100644 index 0000000..1762ebe --- /dev/null +++ b/javatests/com/google/turbine/lower/testdata/genericnoncanon5.test @@ -0,0 +1,30 @@ +=== test/A.java === +package test; + +public class A<U> { + public class Inner { + } +} + +=== test/I.java === +package test; + +public class I<T> { + public class Holder extends A<T> { + } +} + +=== test/J.java === +package test; + +public class J extends I<String> { +} + +=== test/Test.java === +package test; + +public class Test extends J { + public class K extends J.Holder {} + + K.Inner i; +} diff --git a/javatests/com/google/turbine/lower/testdata/genericnoncanon6.test b/javatests/com/google/turbine/lower/testdata/genericnoncanon6.test new file mode 100644 index 0000000..9431dfd --- /dev/null +++ b/javatests/com/google/turbine/lower/testdata/genericnoncanon6.test @@ -0,0 +1,19 @@ +=== test/A.java === +package test; + +public class A<T> { + public class Inner { + } +} + +=== test/B.java === +package test; + +public class B extends A<String> { +} + +=== test/Test.java === +package test; + +public class Test<T extends B.Inner> { +} diff --git a/javatests/com/google/turbine/lower/testdata/genericnoncanon8.test b/javatests/com/google/turbine/lower/testdata/genericnoncanon8.test new file mode 100644 index 0000000..58e5079 --- /dev/null +++ b/javatests/com/google/turbine/lower/testdata/genericnoncanon8.test @@ -0,0 +1,23 @@ +=== test/A.java === +package test; + +public class A<U> { + public class Inner { + } +} + +=== test/D.java === +package test; + +public class D extends A<String> { + public class E extends A<Integer> { + } +} + +=== test/Test.java === +package test; + +public class Test { + D.Inner i0; + D.E.Inner i1; +} diff --git a/javatests/com/google/turbine/lower/testdata/genericnoncanon9.test b/javatests/com/google/turbine/lower/testdata/genericnoncanon9.test new file mode 100644 index 0000000..d80f1d6 --- /dev/null +++ b/javatests/com/google/turbine/lower/testdata/genericnoncanon9.test @@ -0,0 +1,22 @@ +=== test/B.java === +package test; + +public class B<U> { + public class Inner { + } + public class F<X> extends B<X>.Inner { + } +} + +=== test/D.java === +package test; + +public class D<Z> extends B<Z> { +} + +=== test/Test.java === +package test; + +public class Test { + D<Number>.F<String>i3; +} diff --git a/javatests/com/google/turbine/lower/testdata/genericnoncanon_byte.test b/javatests/com/google/turbine/lower/testdata/genericnoncanon_byte.test new file mode 100644 index 0000000..7b9c4f1 --- /dev/null +++ b/javatests/com/google/turbine/lower/testdata/genericnoncanon_byte.test @@ -0,0 +1,50 @@ +%%% test/A.java %%% +package test; + +public class A<T> { + public class Inner { + } +} + +%%% test/Foo.java %%% +package test; + +public class Foo {} + +%%% test/B.java %%% +package test; + +public class B extends A<Foo> { +} + +%%% test/C.java %%% +package test; + +public class C<T> extends A<T> { +} + +%%% test/D.java %%% +package test; + +public class D<T> { + public class Inner extends A<T> { + } + public class F<T> extends D<T>.Inner { + } +} + +%%% test/E.java %%% +package test; + +public class E<T> extends D<T> { +} + +=== test/Test.java === +package test; + +public class Test { + B.Inner i0; + C<Test>.Inner i1; + E<Test>.Inner.Inner i2; + D<D>.F<Test>.Inner i3; +} diff --git a/javatests/com/google/turbine/lower/testdata/genericnoncanon_method3.test b/javatests/com/google/turbine/lower/testdata/genericnoncanon_method3.test new file mode 100644 index 0000000..03bf113 --- /dev/null +++ b/javatests/com/google/turbine/lower/testdata/genericnoncanon_method3.test @@ -0,0 +1,31 @@ +=== test/A.java === +package test; + +public class A<U> { + public class Inner { + } +} + +=== test/D.java === +package test; + +public class D<Z> { + public class Inner extends A<Z> { + } + public class F<X> extends D<X>.Inner { + } +} + +=== test/Test.java === + +package test; + +public class Test<S> { + <T extends S> D<Foo>.F<T>.Inner f() { return null; } +} + +=== test/Foo.java === + +package test; + +public class Foo {} diff --git a/javatests/com/google/turbine/lower/testdata/noncanon.test b/javatests/com/google/turbine/lower/testdata/noncanon.test new file mode 100644 index 0000000..e1bcd17 --- /dev/null +++ b/javatests/com/google/turbine/lower/testdata/noncanon.test @@ -0,0 +1,16 @@ +=== C.java === + +public class C extends Sub.I { +} + +=== Sub.java === + +public class Sub extends Sup { +} + +=== Sup.java === + +public class Sup { + public static class I { + } +} diff --git a/javatests/com/google/turbine/lower/testdata/rawcanon.test b/javatests/com/google/turbine/lower/testdata/rawcanon.test new file mode 100644 index 0000000..c8fe4b1 --- /dev/null +++ b/javatests/com/google/turbine/lower/testdata/rawcanon.test @@ -0,0 +1,12 @@ +=== test/A.java === +package test; +public class A<T> { + public class Inner<X> { + } +} + +=== test/B.java === +package test; +public class B extends A { + B.Inner i; +} diff --git a/javatests/com/google/turbine/lower/testdata/wildboundcanon.test b/javatests/com/google/turbine/lower/testdata/wildboundcanon.test new file mode 100644 index 0000000..4e0c263 --- /dev/null +++ b/javatests/com/google/turbine/lower/testdata/wildboundcanon.test @@ -0,0 +1,12 @@ +=== test/A.java === +package test; +public class A<T> { + public class Inner<X> { + } +} + +=== test/B.java === +package test; +public class B<T extends String> extends A<T> { + B<?>.Inner<?> i; +} diff --git a/javatests/com/google/turbine/lower/testdata/wildcanon.test b/javatests/com/google/turbine/lower/testdata/wildcanon.test new file mode 100644 index 0000000..3a937d9 --- /dev/null +++ b/javatests/com/google/turbine/lower/testdata/wildcanon.test @@ -0,0 +1,12 @@ +=== test/A.java === +package test; +public class A<T> { + public class Inner<X> { + } +} + +=== test/B.java === +package test; +public class B<T> extends A<T> { + B<?>.Inner<?> i; +} |