Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -56,10 +56,10 @@ private static void translateInnerClasses(ClassOrModuleOrModuleInstanciation mi,
* translates the given classDef
*/
private void translate() {
imClass = translator.getClassFor(classDef);
Map<TypeParamDef, ImTypeVar> ov = translator.getTypeVarOverridesForClass(classDef);
translator.pushTypeVarOverrides(ov);
try {
imClass = translator.getClassFor(classDef);
prog.getClasses().add(imClass);

addSuperClasses();
Expand Down Expand Up @@ -193,6 +193,7 @@ private void addOnDestroyActions(ImFunction f, List<ImStmt> addTo, ClassOrModule
typeArgs.add(tp.imTranslateToTypeArgument(translator));
}
}
typeArgs.addAll(translator.capturedTypeArguments(extended.getDef(), extended.getTypeArgBinding()));
addTo.add(ImFunctionCall(c, onDestroy, typeArgs, ImExprs(ImVarAccess(thisVar)), false, CallType.NORMAL));
}
}
Expand Down Expand Up @@ -427,6 +428,8 @@ private void createConstructFunc(ConstructorDef constr) {
typeArgs.add(bt.imTranslateToTypeArgument(translator));
}
}
typeArgs.addAll(translator.capturedTypeArguments(extendedTypeC.getDef(),
extendedTypeC.getTypeArgBinding()));
}
f.getBody().add(ImFunctionCall(trace, superConstrFunc, typeArgs, arguments, false, CallType.NORMAL));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1569,21 +1569,6 @@ private void moveFunctionsOutOfClass(ImClass c) {
typeArgs.add(JassIm.ImTypeArgument(JassIm.ImTypeVarRef(ta), Collections.emptyMap()));
}
rewriteGenerics(f, new GenericTypes(typeArgs), c.getTypeVariables());

// NEW: fill implicit type args for captured generics (Inner -> Inner<T>)
Map<String, ImTypeVar> scope = new HashMap<>();
for (ImTypeVar tv : f.getTypeVariables()) {
scope.put(tv.getName(), tv);
}

f.setReturnType(fillMissingTypeArgsFromScope(f.getReturnType(), scope));

for (ImVar p : f.getParameters()) {
p.setType(fillMissingTypeArgsFromScope(p.getType(), scope));
}
for (ImVar l : f.getLocals()) {
l.setType(fillMissingTypeArgsFromScope(l.getType(), scope));
}
}
}

Expand Down Expand Up @@ -3261,63 +3246,6 @@ public void eliminate() {
}
}

private ImType fillMissingTypeArgsFromScope(ImType t, Map<String, ImTypeVar> scope) {
return t.match(new ImType.Matcher<ImType>() {

@Override
public ImType case_ImClassType(ImClassType ct) {
int need = ct.getClassDef().getTypeVariables().size();
int have = ct.getTypeArguments().size();
if (need == 0 || have >= need) {
return ct;
}

ImTypeArguments newArgs = JassIm.ImTypeArguments();
// keep existing args
for (ImTypeArgument a : ct.getTypeArguments()) {
newArgs.add(a.copy());
}

// fill missing args by name from scope
for (int i = have; i < need; i++) {
ImTypeVar tv = ct.getClassDef().getTypeVariables().get(i);
ImTypeVar inScope = scope.get(tv.getName());
if (inScope == null) {
// no suitable type var in scope -> cannot fill
return ct;
}
newArgs.add(JassIm.ImTypeArgument(JassIm.ImTypeVarRef(inScope), Collections.emptyMap()));
}

return JassIm.ImClassType(ct.getClassDef(), newArgs);
}

@Override
public ImType case_ImArrayType(ImArrayType at) {
return JassIm.ImArrayType(fillMissingTypeArgsFromScope(at.getEntryType(), scope));
}

@Override
public ImType case_ImArrayTypeMulti(ImArrayTypeMulti at) {
return JassIm.ImArrayTypeMulti(fillMissingTypeArgsFromScope(at.getEntryType(), scope), at.getArraySize());
}

@Override
public ImType case_ImTupleType(ImTupleType tt) {
List<ImType> ts = new ArrayList<>();
for (ImType x : tt.getTypes()) {
ts.add(fillMissingTypeArgsFromScope(x, scope));
}
return JassIm.ImTupleType(ts, tt.getNames());
}

@Override public ImType case_ImVoid(ImVoid v) { return v; }
@Override public ImType case_ImAnyType(ImAnyType a) { return a; }
@Override public ImType case_ImSimpleType(ImSimpleType s) { return s; }
@Override public ImType case_ImTypeVarRef(ImTypeVarRef r) { return r; }
});
}

private static String id(Object o) {
return o == null ? "null" : (o.getClass().getSimpleName() + "@" + System.identityHashCode(o));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet;
import it.unimi.dsi.fastutil.objects.Reference2IntOpenHashMap;
import it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet;
import io.vavr.control.Option;
import org.eclipse.jdt.annotation.Nullable;
import org.jetbrains.annotations.NotNull;

Expand Down Expand Up @@ -350,9 +351,72 @@ private static String typeParamOwnerName(TypeParamDef tp) {
return "type";
}

public Map<TypeParamDef, ImTypeVar> getTypeVarOverridesForClass(ClassDef cd) {
Map<TypeParamDef, ImTypeVar> m = capturedOwnerTypeVarsByStaticClass.get(cd);
return (m == null) ? Collections.emptyMap() : m;
/**
* The type variable each type parameter in scope of a member of {@code c} stands for: the class's own and, for a
* static class inside a generic class, the ones it captures from that class
* ({@link #addCapturedTypeVarsFromOwningGeneric}). A member is translated with these whatever the code which first
* asks for it, so the parameter of the enclosing class is the static class's captured variable everywhere in it.
*/
public Map<TypeParamDef, ImTypeVar> getTypeVarOverridesForClass(ClassOrInterface c) {
Map<TypeParamDef, ImTypeVar> result = new IdentityHashMap<>();
for (ImTypeVar tv : getClassFor(c).getTypeVariables()) {
TypeParamDef tp = typeVariableReverse.get(tv);
if (tp != null) {
result.put(tp, tv);
}
}
return result;
}

/** The overrides of {@link #getTypeVarOverridesForClass} for the class {@code member} belongs to, if any. */
private Map<TypeParamDef, ImTypeVar> getTypeVarOverridesForMember(de.peeeq.wurstscript.ast.Element member) {
ClassOrInterface c = member.attrNearestClassOrInterface();
return c == null ? Collections.emptyMap() : getTypeVarOverridesForClass(c);
}

/**
* The type arguments of a type of {@code c} for the type parameters it captures from the generic class around it
* ({@link #addCapturedTypeVarsFromOwningGeneric}), which follow its own in its type variables. Each is what
* {@code binding} binds the parameter to ({@code new Outer<int>().make()} returns a {@code Base} with T bound to
* int), or else the parameter as the code being translated sees it: the enclosing class's own variable in its
* members, and the captured variable in the members of a static class.
*/
public List<ImTypeArgument> capturedTypeArguments(ClassOrInterface c, VariableBinding binding) {
List<TypeParamDef> parameters = capturedTypeParameters(c);
if (parameters.isEmpty()) {
return Collections.emptyList();
}
List<ImTypeArgument> result = new ArrayList<>();
for (TypeParamDef tp : parameters) {
Option<WurstTypeBoundTypeParam> bound = binding.get(tp);
result.add(bound.isDefined()
? bound.get().imTranslateToTypeArgument(this)
: JassIm.ImTypeArgument(JassIm.ImTypeVarRef(getTypeVar(tp)), Collections.emptyMap()));
}
return result;
}

/**
* The type parameters of the generic class around {@code c} which the static class captures
* ({@link #addCapturedTypeVarsFromOwningGeneric}), in the order of its type variables.
*/
private List<TypeParamDef> capturedTypeParameters(ClassOrInterface c) {
if (!(c instanceof ClassDef cd)) {
return Collections.emptyList();
}
ImClass imClass = getClassFor(cd);
Map<TypeParamDef, ImTypeVar> captured = capturedOwnerTypeVarsByStaticClass.get(cd);
if (captured == null) {
return Collections.emptyList();
}
List<TypeParamDef> result = new ArrayList<>();
for (ImTypeVar tv : imClass.getTypeVariables()) {
TypeParamDef tp = typeVariableReverse.get(tv);
if (tp != null && captured.get(tp) == tv) {
result.add(tp);
}
}
return result;
}

public void pushTypeVarOverrides(Map<TypeParamDef, ImTypeVar> m) {
Expand Down Expand Up @@ -1191,16 +1255,11 @@ public ImClassType selfType(StructureDef classDef) {
return selfType(imClass);
}

/** The type of this in the members of the class: the class with its own type variables, the captured ones too. */
public ImClassType selfType(ImClass imClass) {
ImTypeArguments typeArgs = JassIm.ImTypeArguments();
for (ImTypeVar tv : imClass.getTypeVariables()) {
TypeParamDef tpd = typeVariableReverse.get(tv);

// If this ImTypeVar corresponds to an owner TypeParamDef (captured case),
// resolve it through context so owner context uses owner vars, Iterator context uses captured vars.
ImTypeVar tvForContext = (tpd != null) ? getTypeVar(tpd) : tv;

typeArgs.add(JassIm.ImTypeArgument(JassIm.ImTypeVarRef(tvForContext), Collections.emptyMap()));
typeArgs.add(JassIm.ImTypeArgument(JassIm.ImTypeVarRef(tv), Collections.emptyMap()));
}
return JassIm.ImClassType(imClass, typeArgs);
}
Expand Down Expand Up @@ -1291,12 +1350,17 @@ public ImFunction getFuncFor(TranslatedToImFunction funcDef) {

ImTypeVars typeVars = collectTypeVarsForFunction(funcDef);
ImFunction f = ImFunction(funcDef, name, typeVars, ImVars(), ImVoid(), ImVars(), ImStmts(), flags);
// The signature of a class member sees the type variables of its class, not those of the code asking for it.
Map<TypeParamDef, ImTypeVar> classOv = funcDef instanceof ExprClosure
? Collections.emptyMap() : getTypeVarOverridesForMember(funcDef);
Map<TypeParamDef, ImTypeVar> ov = getTypeVarOverridesForFunction(f);
pushTypeVarOverrides(classOv);
pushTypeVarOverrides(ov);
try {
funcDef.imCreateFuncSkeleton(this, f);
} finally {
popTypeVarOverrides(ov);
popTypeVarOverrides(classOv);
}

addFunction(f, funcDef);
Expand Down Expand Up @@ -1363,6 +1427,9 @@ public void case_FuncDef(FuncDef funcDef) {
ClassOrInterface owner = funcDef.attrNearestClassOrInterface();
if (owner != null) {
handleTypeParameters(owner.getTypeParameters());
// A static function is not in its class, so it takes the parameters a static class captures
// from the generic class around it as its own too.
capturedTypeParameters(owner).forEach(this::handleTypeParameter);
}
}
handleTypeParameters(funcDef.getTypeParameters());
Expand Down Expand Up @@ -1539,7 +1606,9 @@ public int getTupleIndex(TupleDef tupleDef, VarDef parameter) {
public ImVar getVarFor(VarDef varDef) {
ImVar v = varMap.get(varDef);
if (v == null) {
Map<TypeParamDef, ImTypeVar> ov = getOwnerTypeVarOverridesForStaticClassVar(varDef);
// A field sees the type variables of its class, not those of the code asking for it.
Map<TypeParamDef, ImTypeVar> ov = varDef instanceof GlobalVarDef
? getTypeVarOverridesForMember(varDef) : Collections.emptyMap();
pushTypeVarOverrides(ov);
ImType type;
try {
Expand All @@ -1558,25 +1627,6 @@ public ImVar getVarFor(VarDef varDef) {
return v;
}

private Map<TypeParamDef, ImTypeVar> getOwnerTypeVarOverridesForStaticClassVar(VarDef varDef) {
if (!(varDef instanceof GlobalVarDef) || !varDef.attrIsStatic()) {
return Collections.emptyMap();
}
ClassOrInterface owner = varDef.attrNearestClassOrInterface();
if (owner == null) {
return Collections.emptyMap();
}
Map<TypeParamDef, ImTypeVar> result = new IdentityHashMap<>();
if (owner instanceof AstElementWithTypeParameters astElementWithTypeParameters) {
for (TypeParamDef tp : astElementWithTypeParameters.getTypeParameters()) {
if (tp.getTypeParamConstraints() instanceof TypeExprList) {
result.put(tp, typeVariable.getFor(tp));
}
}
}
return result;
}

private boolean isNamedScopeVar(VarDef varDef) {
if (varDef.getParent() == null) {
return false;
Expand Down Expand Up @@ -2813,7 +2863,8 @@ public ImClass getClassFor(ClassOrInterface s) {
if (s1 instanceof AstElementWithTypeParameters astElementWithTypeParameters) {
for (TypeParamDef tp : astElementWithTypeParameters.getTypeParameters()) {
if (tp.getTypeParamConstraints() instanceof TypeExprList) {
ImTypeVar tv = getTypeVar(tp); // now context-aware (override stack)
// not getTypeVar: the class's variables must not depend on the code which first asks for it
ImTypeVar tv = typeVariable.getFor(tp);
if (!hasTypeVarNamed(typeVariables, tv.getName())) {
typeVariables.add(tv);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -188,35 +188,38 @@ private ImFunction implementationOfItsOwn(ImClass imClass, WurstTypeClass classT

/**
* The type arguments for {@code variables}, the type variables of {@code owner}, a class or interface above
* {@code classType}, as that class sees them.
* {@code classType}, as that class sees them: those of the type of {@code owner} among its supertypes, translated
* as in the class's own functions. So in a static class inside a generic class, the parameter of that class is
* the static class's captured variable, which no supertype binds.
*/
private List<ImTypeArgument> typeArgumentsAsSeenFrom(WurstTypeClass classType, ClassOrInterface owner,
List<ImTypeVar> variables) {
VariableBinding binding = VariableBinding.emptyMapping();
ArrayDeque<WurstTypeClassOrInterface> queue = new ArrayDeque<>();
queue.add(classType);
while (!queue.isEmpty()) {
WurstTypeClassOrInterface type = queue.removeFirst();
if (type.getDef() == owner) {
binding = type.getTypeArgBinding();
List<ImTypeArgument> arguments = translateAsIn(classType.getDef(), type).getTypeArguments().removeAll();
if (arguments.size() == variables.size()) {
return arguments;
}
break;
}
queue.addAll(type.directSupertypes());
}
List<ImTypeArgument> arguments = new java.util.ArrayList<>();
for (ImTypeVar variable : variables) {
TypeParamDef parameter = translator.getTypeParamDef(variable);
if (parameter == null) {
throw new CompileError(classType.getDef(), "Could not find the type argument of " + owner.getName()
+ " for " + variable.getName() + " as " + classType.getDef().getName() + " sees it.");
}
// A parameter no supertype binds is one of an enclosing generic class, which a static class inside it
// captures: the class sees it as it is, as its own functions do.
ImType type = binding.get(parameter).map(bound -> bound.imTranslateType(translator))
.getOrElse(() -> JassIm.ImTypeVarRef(translator.getTypeVar(parameter)));
arguments.add(JassIm.ImTypeArgument(type, Collections.emptyMap()));
throw new CompileError(classType.getDef(), "Could not find the type arguments of " + owner.getName()
+ " as " + classType.getDef().getName() + " sees it.");
}

/** {@code type} translated as in the functions of {@code c}. */
private ImClassType translateAsIn(ClassDef c, WurstTypeClassOrInterface type) {
Map<TypeParamDef, ImTypeVar> overrides = translator.getTypeVarOverridesForClass(c);
translator.pushTypeVarOverrides(overrides);
try {
return (ImClassType) type.imTranslateType(translator);
} finally {
translator.popTypeVarOverrides(overrides);
}
return arguments;
}


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,7 @@ public final ImType imTranslateType(ImTranslator tr) {
typeArgs.add(btp.imTranslateToTypeArgument(tr));
}
}
typeArgs.addAll(tr.capturedTypeArguments(getDef(), getTypeArgBinding()));
return JassIm.ImClassType(tr.getClassFor(getDef()), typeArgs);
}

Expand Down
Loading
Loading