diff options
Diffstat (limited to 'java/com/google/turbine/binder/TypeBinder.java')
-rw-r--r-- | java/com/google/turbine/binder/TypeBinder.java | 242 |
1 files changed, 208 insertions, 34 deletions
diff --git a/java/com/google/turbine/binder/TypeBinder.java b/java/com/google/turbine/binder/TypeBinder.java index a28acd9..92d2827 100644 --- a/java/com/google/turbine/binder/TypeBinder.java +++ b/java/com/google/turbine/binder/TypeBinder.java @@ -16,6 +16,7 @@ package com.google.turbine.binder; +import static com.google.common.collect.Iterables.getLast; import static java.util.Objects.requireNonNull; import com.google.common.base.Joiner; @@ -27,6 +28,7 @@ import com.google.turbine.binder.bound.SourceTypeBoundClass; import com.google.turbine.binder.bound.TypeBoundClass.FieldInfo; import com.google.turbine.binder.bound.TypeBoundClass.MethodInfo; import com.google.turbine.binder.bound.TypeBoundClass.ParamInfo; +import com.google.turbine.binder.bound.TypeBoundClass.RecordComponentInfo; import com.google.turbine.binder.bound.TypeBoundClass.TyVarInfo; import com.google.turbine.binder.env.Env; import com.google.turbine.binder.lookup.CompoundScope; @@ -37,6 +39,7 @@ import com.google.turbine.binder.sym.ClassSymbol; import com.google.turbine.binder.sym.FieldSymbol; import com.google.turbine.binder.sym.MethodSymbol; import com.google.turbine.binder.sym.ParamSymbol; +import com.google.turbine.binder.sym.RecordComponentSymbol; import com.google.turbine.binder.sym.Symbol; import com.google.turbine.binder.sym.TyVarSymbol; import com.google.turbine.diag.TurbineError.ErrorKind; @@ -63,6 +66,7 @@ import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; +import org.jspecify.nullness.Nullable; /** Type binding. */ public class TypeBinder { @@ -79,7 +83,7 @@ public class TypeBinder { } @Override - public LookupResult lookup(LookupKey lookup) { + public @Nullable LookupResult lookup(LookupKey lookup) { if (name.equals(lookup.first().value())) { return new LookupResult(sym, lookup); } @@ -96,7 +100,7 @@ public class TypeBinder { } @Override - public LookupResult lookup(LookupKey lookupKey) { + public @Nullable LookupResult lookup(LookupKey lookupKey) { Symbol sym = tps.get(lookupKey.first().value()); return sym != null ? new LookupResult(sym, lookupKey) : null; } @@ -116,14 +120,14 @@ public class TypeBinder { } @Override - public LookupResult lookup(LookupKey lookup) { + public @Nullable LookupResult lookup(LookupKey lookup) { ClassSymbol curr = sym; while (curr != null) { - HeaderBoundClass info = env.get(curr); Symbol result = Resolve.resolve(env, sym, curr, lookup.first()); if (result != null) { return new LookupResult(result, lookup); } + HeaderBoundClass info = env.getNonNull(curr); result = info.typeParameters().get(lookup.first().value()); if (result != null) { return new LookupResult(result, lookup); @@ -168,8 +172,10 @@ public class TypeBinder { CompoundScope enclosingScope = base.scope() .toScope(Resolve.resolveFunction(env, owner)) - .append(new SingletonScope(base.decl().name().value(), owner)) - .append(new ClassMemberScope(base.owner(), env)); + .append(new SingletonScope(base.decl().name().value(), owner)); + if (base.owner() != null) { + enclosingScope = enclosingScope.append(new ClassMemberScope(base.owner(), env)); + } ImmutableList<AnnoInfo> annotations = bindAnnotations(enclosingScope, base.decl().annos()); @@ -212,6 +218,9 @@ public class TypeBinder { } superClassType = Type.ClassTy.OBJECT; break; + case RECORD: + superClassType = Type.ClassTy.asNonParametricClassTy(ClassSymbol.RECORD); + break; default: throw new AssertionError(base.decl().tykind()); } @@ -220,26 +229,43 @@ public class TypeBinder { interfaceTypes.add(bindClassTy(bindingScope, i)); } + ImmutableList.Builder<ClassSymbol> permits = ImmutableList.builder(); + for (Tree.ClassTy i : base.decl().permits()) { + Type type = bindClassTy(bindingScope, i); + if (!type.tyKind().equals(Type.TyKind.CLASS_TY)) { + throw new AssertionError(type.tyKind()); + } + permits.add(((Type.ClassTy) type).sym()); + } + CompoundScope scope = base.scope() .toScope(Resolve.resolveFunction(env, owner)) .append(new SingletonScope(base.decl().name().value(), owner)) .append(new ClassMemberScope(owner, env)); - List<MethodInfo> methods = + SyntheticMethods syntheticMethods = new SyntheticMethods(); + + ImmutableList<RecordComponentInfo> components = bindComponents(scope, base.decl().components()); + + ImmutableList.Builder<MethodInfo> methods = ImmutableList.<MethodInfo>builder() - .addAll(syntheticMethods()) - .addAll(bindMethods(scope, base.decl().members())) - .build(); + .addAll(syntheticMethods(syntheticMethods, components)) + .addAll(bindMethods(scope, base.decl().members(), components)); + if (base.kind().equals(TurbineTyKind.RECORD)) { + methods.addAll(syntheticRecordMethods(syntheticMethods, components)); + } ImmutableList<FieldInfo> fields = bindFields(scope, base.decl().members()); return new SourceTypeBoundClass( interfaceTypes.build(), + permits.build(), superClassType, typeParameterTypes, base.access(), - ImmutableList.copyOf(methods), + components, + methods.build(), fields, base.owner(), base.kind(), @@ -254,23 +280,79 @@ public class TypeBinder { base.decl()); } + /** + * A generated for synthetic {@link MethodSymbol}s. + * + * <p>Each {@link MethodSymbol} contains an index into its enclosing class, to enable comparing + * the symbols for equality. For synthetic methods we use an arbitrary unique negative index. + */ + private static class SyntheticMethods { + + private int idx = -1; + + MethodSymbol create(ClassSymbol owner, String name) { + return new MethodSymbol(idx--, owner, name); + } + } + + private ImmutableList<RecordComponentInfo> bindComponents( + CompoundScope scope, ImmutableList<Tree.VarDecl> components) { + ImmutableList.Builder<RecordComponentInfo> result = ImmutableList.builder(); + for (Tree.VarDecl p : components) { + int access = 0; + for (TurbineModifier m : p.mods()) { + access |= m.flag(); + } + RecordComponentInfo param = + new RecordComponentInfo( + new RecordComponentSymbol(owner, p.name().value()), + bindTy(scope, p.ty()), + bindAnnotations(scope, p.annos()), + access); + result.add(param); + } + return result.build(); + } + /** Collect synthetic and implicit methods, including default constructors and enum methods. */ - ImmutableList<MethodInfo> syntheticMethods() { + ImmutableList<MethodInfo> syntheticMethods( + SyntheticMethods syntheticMethods, ImmutableList<RecordComponentInfo> components) { switch (base.kind()) { case CLASS: - return maybeDefaultConstructor(); + return maybeDefaultConstructor(syntheticMethods); + case RECORD: + return maybeDefaultRecordConstructor(syntheticMethods, components); case ENUM: - return syntheticEnumMethods(); + return syntheticEnumMethods(syntheticMethods); default: return ImmutableList.of(); } } - private ImmutableList<MethodInfo> maybeDefaultConstructor() { + private ImmutableList<MethodInfo> maybeDefaultRecordConstructor( + SyntheticMethods syntheticMethods, ImmutableList<RecordComponentInfo> components) { if (hasConstructor()) { return ImmutableList.of(); } - MethodSymbol symbol = new MethodSymbol(-1, owner, "<init>"); + MethodSymbol symbol = syntheticMethods.create(owner, "<init>"); + ImmutableList.Builder<ParamInfo> params = ImmutableList.builder(); + for (RecordComponentInfo component : components) { + params.add( + new ParamInfo( + new ParamSymbol(symbol, component.name()), + component.type(), + component.annotations(), + component.access())); + } + return ImmutableList.of( + syntheticConstructor(symbol, params.build(), TurbineVisibility.fromAccess(base.access()))); + } + + private ImmutableList<MethodInfo> maybeDefaultConstructor(SyntheticMethods syntheticMethods) { + if (hasConstructor()) { + return ImmutableList.of(); + } + MethodSymbol symbol = syntheticMethods.create(owner, "<init>"); ImmutableList<ParamInfo> formals; if (hasEnclosingInstance(base)) { formals = ImmutableList.of(enclosingInstanceParameter(symbol)); @@ -285,6 +367,10 @@ public class TypeBinder { MethodSymbol symbol, ImmutableList<ParamInfo> formals, TurbineVisibility visibility) { int access = visibility.flag(); access |= (base.access() & TurbineFlag.ACC_STRICT); + if (!formals.isEmpty() + && (getLast(formals).access() & TurbineFlag.ACC_VARARGS) == TurbineFlag.ACC_VARARGS) { + access |= TurbineFlag.ACC_VARARGS; + } return new MethodInfo( symbol, ImmutableMap.of(), @@ -307,7 +393,7 @@ public class TypeBinder { } int enclosingInstances = 0; for (ClassSymbol sym = base.owner(); sym != null; ) { - HeaderBoundClass info = env.get(sym); + HeaderBoundClass info = env.getNonNull(sym); if (((info.access() & TurbineFlag.ACC_STATIC) == TurbineFlag.ACC_STATIC) || info.owner() == null) { break; @@ -338,15 +424,15 @@ public class TypeBinder { TurbineFlag.ACC_SYNTHETIC)); } - private ImmutableList<MethodInfo> syntheticEnumMethods() { + private ImmutableList<MethodInfo> syntheticEnumMethods(SyntheticMethods syntheticMethods) { ImmutableList.Builder<MethodInfo> methods = ImmutableList.builder(); int access = 0; access |= (base.access() & TurbineFlag.ACC_STRICT); if (!hasConstructor()) { - MethodSymbol symbol = new MethodSymbol(-1, owner, "<init>"); + MethodSymbol symbol = syntheticMethods.create(owner, "<init>"); methods.add(syntheticConstructor(symbol, enumCtorParams(symbol), TurbineVisibility.PRIVATE)); } - MethodSymbol valuesMethod = new MethodSymbol(-2, owner, "values"); + MethodSymbol valuesMethod = syntheticMethods.create(owner, "values"); methods.add( new MethodInfo( valuesMethod, @@ -359,7 +445,7 @@ public class TypeBinder { null, ImmutableList.of(), null)); - MethodSymbol valueOfMethod = new MethodSymbol(-3, owner, "valueOf"); + MethodSymbol valueOfMethod = syntheticMethods.create(owner, "valueOf"); methods.add( new MethodInfo( valueOfMethod, @@ -380,6 +466,71 @@ public class TypeBinder { return methods.build(); } + private ImmutableList<MethodInfo> syntheticRecordMethods( + SyntheticMethods syntheticMethods, ImmutableList<RecordComponentInfo> components) { + ImmutableList.Builder<MethodInfo> methods = ImmutableList.builder(); + MethodSymbol toStringMethod = syntheticMethods.create(owner, "toString"); + methods.add( + new MethodInfo( + toStringMethod, + ImmutableMap.of(), + Type.ClassTy.STRING, + ImmutableList.of(), + ImmutableList.of(), + TurbineFlag.ACC_PUBLIC | TurbineFlag.ACC_FINAL, + null, + null, + ImmutableList.of(), + null)); + MethodSymbol hashCodeMethod = syntheticMethods.create(owner, "hashCode"); + methods.add( + new MethodInfo( + hashCodeMethod, + ImmutableMap.of(), + Type.PrimTy.create(TurbineConstantTypeKind.INT, ImmutableList.of()), + ImmutableList.of(), + ImmutableList.of(), + TurbineFlag.ACC_PUBLIC | TurbineFlag.ACC_FINAL, + null, + null, + ImmutableList.of(), + null)); + MethodSymbol equalsMethod = syntheticMethods.create(owner, "equals"); + methods.add( + new MethodInfo( + equalsMethod, + ImmutableMap.of(), + Type.PrimTy.create(TurbineConstantTypeKind.BOOLEAN, ImmutableList.of()), + ImmutableList.of( + new ParamInfo( + new ParamSymbol(equalsMethod, "other"), + Type.ClassTy.OBJECT, + ImmutableList.of(), + TurbineFlag.ACC_MANDATED)), + ImmutableList.of(), + TurbineFlag.ACC_PUBLIC | TurbineFlag.ACC_FINAL, + null, + null, + ImmutableList.of(), + null)); + for (RecordComponentInfo c : components) { + MethodSymbol componentMethod = syntheticMethods.create(owner, c.name()); + methods.add( + new MethodInfo( + componentMethod, + ImmutableMap.of(), + c.type(), + ImmutableList.of(), + ImmutableList.of(), + TurbineFlag.ACC_PUBLIC, + null, + null, + c.annotations(), + null)); + } + return methods.build(); + } + private boolean hasConstructor() { for (Tree m : base.decl().members()) { if (m.kind() != Kind.METH_DECL) { @@ -409,21 +560,25 @@ public class TypeBinder { new TyVarInfo( IntersectionTy.create(bounds.build()), /* lowerBound= */ null, annotations)); } - return result.build(); + return result.buildOrThrow(); } - private List<MethodInfo> bindMethods(CompoundScope scope, ImmutableList<Tree> members) { + private List<MethodInfo> bindMethods( + CompoundScope scope, + ImmutableList<Tree> members, + ImmutableList<RecordComponentInfo> components) { List<MethodInfo> methods = new ArrayList<>(); int idx = 0; for (Tree member : members) { if (member.kind() == Tree.Kind.METH_DECL) { - methods.add(bindMethod(idx++, scope, (Tree.MethDecl) member)); + methods.add(bindMethod(idx++, scope, (MethDecl) member, components)); } } return methods; } - private MethodInfo bindMethod(int idx, CompoundScope scope, Tree.MethDecl t) { + private MethodInfo bindMethod( + int idx, CompoundScope scope, MethDecl t, ImmutableList<RecordComponentInfo> components) { MethodSymbol sym = new MethodSymbol(idx, owner, t.name().value()); @@ -433,7 +588,7 @@ public class TypeBinder { for (Tree.TyParam pt : t.typarams()) { builder.put(pt.name().value(), new TyVarSymbol(sym, pt.name().value())); } - typeParameters = builder.build(); + typeParameters = builder.buildOrThrow(); } // type parameters can refer to each other in f-bounds, so update the scope first @@ -453,8 +608,26 @@ public class TypeBinder { if (name.equals("<init>")) { if (hasEnclosingInstance(base)) { parameters.add(enclosingInstanceParameter(sym)); - } else if (base.kind() == TurbineTyKind.ENUM && name.equals("<init>")) { - parameters.addAll(enumCtorParams(sym)); + } else { + switch (base.kind()) { + case ENUM: + parameters.addAll(enumCtorParams(sym)); + break; + case RECORD: + if (t.mods().contains(TurbineModifier.COMPACT_CTOR)) { + for (RecordComponentInfo component : components) { + parameters.add( + new ParamInfo( + new ParamSymbol(sym, component.name()), + component.type(), + component.annotations(), + component.access())); + } + } + break; + default: + break; + } } } ParamInfo receiver = null; @@ -582,8 +755,8 @@ public class TypeBinder { return result.build(); } - private ClassSymbol resolveAnnoSymbol( - Anno tree, ImmutableList<Ident> name, LookupResult lookupResult) { + private @Nullable ClassSymbol resolveAnnoSymbol( + Anno tree, ImmutableList<Ident> name, @Nullable LookupResult lookupResult) { if (lookupResult == null) { log.error(tree.position(), ErrorKind.CANNOT_RESOLVE, Joiner.on('.').join(name)); return null; @@ -595,13 +768,13 @@ public class TypeBinder { return null; } } - if (env.get(sym).kind() != TurbineTyKind.ANNOTATION) { + if (env.getNonNull(sym).kind() != TurbineTyKind.ANNOTATION) { log.error(tree.position(), ErrorKind.NOT_AN_ANNOTATION, sym); } return sym; } - private ClassSymbol resolveNext(ClassSymbol sym, Ident bit) { + private @Nullable ClassSymbol resolveNext(ClassSymbol sym, Ident bit) { ClassSymbol next = Resolve.resolve(env, owner, sym, bit); if (next == null) { log.error( @@ -705,10 +878,11 @@ public class TypeBinder { sym, bindTyArgs(scope, flat.get(idx++).tyargs()), annotations)); for (; idx < flat.size(); idx++) { Tree.ClassTy curr = flat.get(idx); - sym = resolveNext(sym, curr.name()); - if (sym == null) { + ClassSymbol next = resolveNext(sym, curr.name()); + if (next == null) { return Type.ErrorTy.create(bits); } + sym = next; annotations = bindAnnotations(scope, curr.annos()); classes.add( |