aboutsummaryrefslogtreecommitdiff
path: root/java/com/google/turbine/binder/TypeBinder.java
diff options
context:
space:
mode:
Diffstat (limited to 'java/com/google/turbine/binder/TypeBinder.java')
-rw-r--r--java/com/google/turbine/binder/TypeBinder.java242
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(