JavaTXCompilerInJavaTX/javatx-src/main/java/de/dhbwstuttgart/target/generate/GenerateGenerics.java

975 lines
38 KiB
Java
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package de.dhbwstuttgart.target.generate;
import de.dhbwstuttgart.parser.NullToken;
import de.dhbwstuttgart.syntaxtree.ClassOrInterface;
import de.dhbwstuttgart.syntaxtree.Constructor;
import de.dhbwstuttgart.syntaxtree.GenericTypeVar;
import de.dhbwstuttgart.syntaxtree.Method;
import de.dhbwstuttgart.syntaxtree.statement.*;
import de.dhbwstuttgart.syntaxtree.type.*;
import de.dhbwstuttgart.syntaxtree.type.Void;
import de.dhbwstuttgart.target.tree.type.TargetGenericType;
import de.dhbwstuttgart.target.tree.type.TargetType;
import de.dhbwstuttgart.typeinference.result.PairTPHEqualTPH;
import de.dhbwstuttgart.typeinference.result.PairTPHequalRefTypeOrWildcardType;
import de.dhbwstuttgart.typeinference.result.PairTPHsmallerTPH;
import de.dhbwstuttgart.typeinference.result.ResultSet;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;
public abstract class GenerateGenerics {
private final ASTToTargetAST astToTargetAST;
public class TPH {
private final TypePlaceholder wrap;
TPH(TypePlaceholder wrap) {
this.wrap = wrap;
}
public TypePlaceholder resolve() {
return equality.getOrDefault(wrap, wrap);
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
TPH tph = (TPH) o;
return Objects.equals(resolve(), tph.resolve());
}
@Override
public int hashCode() {
return Objects.hash(resolve());
}
@Override
public String toString() {
return resolve().getName();
}
}
public abstract class Pair {
public final TPH left;
Pair(TPH left) {
this.left = left;
}
public abstract RefTypeOrTPHOrWildcardOrGeneric resolveRight();
}
public class PairLT extends Pair {
public final TPH right;
PairLT(TPH left, TPH right) {
super(left);
this.right = right;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
PairLT pairLT = (PairLT) o;
return Objects.equals(right, pairLT.right) && Objects.equals(left, pairLT.left);
}
@Override
public int hashCode() {
return Objects.hash(right, left);
}
@Override
public RefTypeOrTPHOrWildcardOrGeneric resolveRight() {
return right.resolve();
}
@Override
public String toString() {
return "(" + left + " < " + right + ")";
}
}
public class PairEQ extends Pair {
public final RefType right;
PairEQ(TPH left, RefType right) {
super(left);
this.right = right;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
PairEQ pairLT = (PairEQ) o;
return Objects.equals(right, pairLT.right) && Objects.equals(left, pairLT.left);
}
@Override
public int hashCode() {
return Objects.hash(right, left);
}
@Override
public RefTypeOrTPHOrWildcardOrGeneric resolveRight() {
return right;
}
@Override
public String toString() {
return "(" + left + " = " + right + ")";
}
}
final Map<Method, Set<Pair>> computedGenericsOfMethods = new HashMap<>();
final Map<ClassOrInterface, Set<Pair>> computedGenericsOfClasses = new HashMap<>();
final Map<Method, Set<TPH>> usedTPHsOfMethods = new HashMap<>();
final Map<Method, Set<Pair>> familyOfMethods = new HashMap<>();
final Set<PairLT> simplifiedConstraints = new HashSet<>();
final Map<TPH, RefTypeOrTPHOrWildcardOrGeneric> concreteTypes = new HashMap<>();
final Map<TypePlaceholder, TypePlaceholder> equality = new HashMap<>();
GenerateGenerics(ASTToTargetAST astToTargetAST, ResultSet constraints) {
this.astToTargetAST = astToTargetAST;
for (var constraint : constraints.results) {
if (constraint instanceof PairTPHsmallerTPH p) {
System.out.println(p.left + " " + p.left.getVariance());
simplifiedConstraints.add(new PairLT(new TPH(p.left), new TPH(p.right)));
} else if (constraint instanceof PairTPHEqualTPH p) {
equality.put(p.getLeft(), p.getRight());
} else if (constraint instanceof PairTPHequalRefTypeOrWildcardType p) {
System.out.println(p.left + " = " + p.right);
concreteTypes.put(new TPH(p.left), p.right);
}
}
System.out.println("Simplified constraints: " + simplifiedConstraints);
}
Set<TPH> findTypeVariables(RefTypeOrTPHOrWildcardOrGeneric type) {
var result = new HashSet<TPH>();
if (type instanceof TypePlaceholder tph) {
var nTph = new TPH(tph);
if (concreteTypes.containsKey(nTph)) {
result.addAll(findTypeVariables(concreteTypes.get(nTph)));
return result;
}
result.add(nTph);
} else if (type instanceof RefType refType) {
for (var t : refType.getParaList())
result.addAll(findTypeVariables(t));
} else if (type instanceof ExtendsWildcardType wildcardType) {
result.addAll(findTypeVariables(wildcardType.getInnerType()));
} else if (type instanceof SuperWildcardType wildcardType) {
result.addAll(findTypeVariables(wildcardType.getInnerType()));
}
return result;
}
boolean containsRelation(Set<Pair> result, PairLT pair) {
// Check if both the right and the left are already part of a relation
var containsLeft = false;
for (var pair2 : result) {
if (pair2.left.equals(pair.left)) {
containsLeft = true;
break;
}
}
var containsRight = false;
for (var pair2 : result)
if (pair2 instanceof PairLT plt) {
if (plt.right.equals(pair.right)) {
containsRight = true;
break;
}
}
return containsLeft && containsRight;
}
void addToPairs(Set<Pair> input, Pair pair) {
if (pair instanceof PairLT plt) {
input.removeIf(pair2 -> {
if (pair2 instanceof PairEQ peq) {
return peq.left.equals(plt.left) && peq.right.equals(ASTToTargetAST.OBJECT);
}
return false;
});
} else if (input.stream().anyMatch(p -> p.left.equals(pair.left))) {
return;
}
input.add(pair);
}
void addToEquality(TypePlaceholder from, TypePlaceholder to, Set<TPH> referenced) {
for (var entry : new HashSet<>(equality.entrySet())) {
if (entry.getValue().equals(from)) {
equality.remove(entry.getKey());
equality.put(entry.getKey(), to);
}
}
System.out.println(from + " -> " + to + " " + from.getVariance());
//from.setVariance(to.getVariance());
equality.put(from, to);
referenced.remove(new TPH(from));
referenced.add(new TPH(to));
}
Set<Pair> transitiveClosure(Set<? extends Pair> generics) {
Set<Pair> all = new HashSet<>(generics);
Set<Pair> toAdd = new HashSet<>();
int sizeBefore;
do {
sizeBefore = all.size();
toAdd.clear();
for (var g1 : all) {
for (var g2 : all) {
if (g1 instanceof PairLT pair && g2 instanceof PairLT pair2) {
if (pair2.left.equals(pair.right))
toAdd.add(new PairLT(pair.left, pair2.right));
}
}
}
all.addAll(toAdd);
} while (sizeBefore < all.size());
return all;
}
private void methodFindConstraints(
ClassOrInterface owner, Method method,
Set<TPH> typeVariables,
Set<TPH> typeVariablesOfClass,
Set<Pair> result
) {
var userDefinedGenericsOfClass = astToTargetAST.userDefinedGenerics.get(owner);
// Type variables with bounds that are also type variables of the method
for (var typeVariable : new HashSet<>(typeVariables)) {
if (classHasGeneric(userDefinedGenericsOfClass, typeVariablesOfClass, typeVariable))
continue;
for (var pair : simplifiedConstraints) {
if (pair.left.equals(typeVariable) && typeVariables.contains(pair.right)) {
addToPairs(result, new PairLT(pair.left, pair.right));
}
}
}
if (method.block != null)
method.block.accept(new TracingStatementVisitor() {
private RefTypeOrTPHOrWildcardOrGeneric superType = new de.dhbwstuttgart.syntaxtree.type.Void(new NullToken());
@Override
public void visit(MethodCall methodCall) {
//Anfang es werden Paare von TPHs gespeichert, die bei den Generated Generics ueber die Methodengrenzen hinweg
//betrachtet werden muessen
//Definition 7.2 (Family of generated generics). T1 <. R1 <.^ R2 <. T2
Set<TPH> T1s =
methodCall.getArgumentList()
.getArguments()
.stream()
.map(TypableStatement::getType)
.collect(Collectors.toCollection(HashSet::new))
.stream().filter(TypePlaceholder.class::isInstance)
.map(TypePlaceholder.class::cast)
.map(TPH::new)
.collect(Collectors.toCollection(HashSet::new));
Set<TPH> T2s = new HashSet<>();
findTphs(superType, T2s);
System.out.println("T1s: " + T1s + " T2s: " + T2s);
//Ende
superType = methodCall.receiverType;
methodCall.receiver.accept(this);
for (int i = 0; i < methodCall.arglist.getArguments().size(); i++) {
superType = methodCall.arglist.getArguments().get(i).getType();
methodCall.arglist.getArguments().get(i).accept(this);
}
if (methodCall.receiver instanceof ExpressionReceiver expressionReceiver) {
if (expressionReceiver.expr instanceof This) {
var optMethod = astToTargetAST.findMethod(owner, methodCall.name, methodCall.signatureArguments().stream().map(astToTargetAST::convert).toList());
if (optMethod.isEmpty()) return;
var method2 = optMethod.get();
System.out.println("In: " + method.getName() + " Method: " + method2.getName());
var generics = family(owner, method2);
// transitive and
var all = transitiveClosure(generics);
// reflexive
var toAdd = new HashSet<Pair>();
for (var generic : all) {
toAdd.add(new PairLT(generic.left, generic.left));
}
all.addAll(toAdd);
HashSet<PairLT> newPairs = new HashSet<>();
// Loop from hell
outer:
for (var R1 : typeVariables) {
if (typeVariablesOfClass.contains(R1)) continue;
for (var generic : all)
if (generic instanceof PairLT ptph) {
for (var pair : simplifiedConstraints) {
if (!(pair.left.equals(R1) && pair.right.equals(ptph.left)))
continue;
for (var R2 : typeVariables) {
for (var pair2 : simplifiedConstraints) {
if (!(pair2.right.equals(R2) && pair2.left.equals(ptph.right)))
continue;
if (R1.equals(R2)) continue;
if (!T1s.contains(R1) || !T2s.contains(R2)) continue;
var newPair = new PairLT(R1, R2);
System.out.println("New pair: " + newPair);
newPairs.add(newPair);
if (!containsRelation(result, newPair))
addToPairs(result, newPair);
continue outer;
}
}
}
}
}
simplifiedConstraints.addAll(newPairs);
}
}
}
@Override
public void visit(LambdaExpression lambdaExpression) {
superType = new Void(new NullToken());
lambdaExpression.methodBody.accept(this);
}
@Override
public void visit(Assign assign) {
superType = assign.rightSide.getType();
assign.rightSide.accept(this);
}
@Override
public void visit(BinaryExpr binary) {
superType = new Void(new NullToken());
binary.lexpr.accept(this);
superType = new Void(new NullToken());
binary.rexpr.accept(this);
}
@Override
public void visit(Block block) {
for (var expr : block.statements) {
superType = new de.dhbwstuttgart.syntaxtree.type.Void(new NullToken());
expr.accept(this);
}
}
@Override
public void visit(IfStmt ifStmt) {
superType = new Void(new NullToken());
ifStmt.expr.accept(this);
superType = new Void(new NullToken());
ifStmt.then_block.accept(this);
superType = new Void(new NullToken());
if (ifStmt.else_block != null)
ifStmt.else_block.accept(this);
}
@Override
public void visit(Return aReturn) {
superType = aReturn.getType();
aReturn.retexpr.accept(this);
}
@Override
public void visit(WhileStmt whileStmt) {
superType = new Void(new NullToken());
whileStmt.expr.accept(this);
superType = new Void(new NullToken());
whileStmt.loopBlock.accept(this);
}
@Override
public void visit(ArgumentList arglist) {
for (int i = 0; i < arglist.getArguments().size(); i++) {
superType = arglist.getArguments().get(i).getType();
arglist.getArguments().get(i).accept(this);
}
}
});
var closure = transitiveClosure(simplifiedConstraints);
// Type variables with bounds that are also type variables of the class
for (var typeVariable : new HashSet<>(typeVariables)) {
if (typeVariablesOfClass.contains(typeVariable)) continue;
var pairs = new HashSet<PairLT>();
for (var pair : closure) {
if (!(pair instanceof PairLT ptph)) continue;
if (ptph.left.equals(typeVariable) && typeVariablesOfClass.contains(ptph.right)) {
pairs.add(new PairLT(ptph.left, ptph.right));
}
}
// Find the closest pair with the minimum amount of steps
PairLT minimalPair = null;
var minSteps = Integer.MAX_VALUE;
for (var pair : pairs) {
var left = pair.left;
var visited = new HashSet<TPH>();
var steps = 0;
while (!left.equals(pair.right)) {
visited.add(left);
var found = false;
for (var pair2 : simplifiedConstraints) {
if (left.equals(pair2.left) && !visited.contains(pair2.right)) {
left = pair2.right;
steps += 1;
found = true;
break;
}
}
if (!found) break;
}
if (steps < minSteps) {
minSteps = steps;
minimalPair = pair;
}
}
if (minimalPair != null)
addToPairs(result, minimalPair);
}
// All unbounded type variables (bounds not in method)
outer:
for (var typeVariable : typeVariables) {
if (classHasGeneric(userDefinedGenericsOfClass, typeVariablesOfClass, typeVariable))
continue;
for (var pair : result) {
if (pair.left.equals(typeVariable))
continue outer;
}
addToPairs(result, new PairEQ(typeVariable, ASTToTargetAST.OBJECT));
}
// All unbounded bounds
outer:
for (var pair : simplifiedConstraints) {
for (var pair2 : simplifiedConstraints) {
if (pair.right.equals(pair2.left))
continue outer;
}
if (!classHasGeneric(userDefinedGenericsOfClass, typeVariablesOfClass, pair.right) && typeVariables.contains(pair.right)) {
addToPairs(result, new PairEQ(pair.right, ASTToTargetAST.OBJECT));
}
}
}
private boolean classHasGeneric(Set<GenericTypeVar> userDefinedGenericsOfClass, Set<TPH> typeVariablesOfClass, TPH typeVariable) {
return typeVariablesOfClass.contains(typeVariable) || userDefinedGenericsOfClass.stream().anyMatch(g -> g.getName().equals(typeVariable.resolve().getName()));
}
private void methodFindTypeVariables(
Method method,
Set<TPH> typeVariables
) {
if (!(method instanceof Constructor))
typeVariables.addAll(findTypeVariables(method.getReturnType()));
for (var arg : method.getParameterList().getFormalparalist()) {
typeVariables.addAll(findTypeVariables(arg.getType()));
}
if (method.block != null)
method.block.accept(new TracingStatementVisitor() {
@Override
public void visit(LocalVarDecl localVarDecl) {
typeVariables.addAll(findTypeVariables(localVarDecl.getType()));
}
@Override
public void visit(MethodCall methodCall) {
super.visit(methodCall);
typeVariables.addAll(findTypeVariables(methodCall.getType()));
}
});
}
abstract void generics(ClassOrInterface owner, Method method, Set<Pair> result, Set<TPH> javaTypeVariablesOfClass);
Set<Pair> family(ClassOrInterface owner, Method method) {
Set<Pair> result = new HashSet<>();
if (familyOfMethods.containsKey(method))
return familyOfMethods.get(method);
familyOfMethods.put(method, result);
var classGenerics = generics(owner);
HashSet<TPH> typeVariablesOfClass = new HashSet<>();
for (var pair : classGenerics) {
typeVariablesOfClass.add(pair.left);
}
HashSet<TPH> javaTypeVariables = new HashSet<>();
methodFindTypeVariables(method, javaTypeVariables);
methodFindConstraints(owner, method, javaTypeVariables, typeVariablesOfClass, result);
eliminateTransitives(result);
return result;
}
Set<Pair> generics(ClassOrInterface owner, Method method) {
if (computedGenericsOfMethods.containsKey(method)) {
var cached = computedGenericsOfMethods.get(method);
System.out.println("Cached " + method.getName() + ": " + cached);
return cached;
}
var result = new HashSet<Pair>();
computedGenericsOfMethods.put(method, result);
var classGenerics = generics(owner);
HashSet<TPH> typeVariablesOfClass = new HashSet<>();
for (var pair : classGenerics) {
typeVariablesOfClass.add(pair.left);
}
result.addAll(family(owner, method));
var referenced = new HashSet<TPH>();
var usedTphs = new HashSet<TPH>();
// For eliminating inner type variables we need to figure out which ones are actually used
for (var param : method.getParameterList().getFormalparalist()) {
usedTphs.addAll(findTypeVariables(param.getType()));
}
usedTphs.addAll(findTypeVariables(method.getReturnType()));
referenced.addAll(usedTphs);
referenced.addAll(typeVariablesOfClass);
generics(owner, method, result, referenced);
usedTPHsOfMethods.put(method, usedTphs);
normalize(result, classGenerics, usedTphs);
System.out.println(this.getClass().getSimpleName() + " " + method.name + ": " + result);
return result;
}
private void eliminateChain(Set<Pair> result, List<TPH> chain) {
for (var pair : new HashSet<>(result)) {
if (pair instanceof PairLT ptph && chain.get(chain.size() - 1).equals(ptph.left)) {
if (chain.contains(ptph.right)) return;
var copy = new ArrayList<>(chain);
copy.add(ptph.right);
if (copy.size() > 2)
result.remove(new PairLT(chain.get(0), ptph.right));
eliminateChain(result, copy);
}
}
}
void eliminateTransitives(Set<Pair> result) {
for (var pair : new HashSet<>(result))
if (pair instanceof PairLT ptph) {
var first = ptph.left;
var chain = new ArrayList<TPH>();
chain.add(ptph.left);
chain.add(ptph.right);
eliminateChain(result, chain);
}
}
void findAllBounds(RefTypeOrTPHOrWildcardOrGeneric type, Set<Pair> generics) {
if (type instanceof TypePlaceholder tph) {
var nTph = new TPH(tph);
var concreteType = concreteTypes.get(nTph);
if (concreteType != null) {
findAllBounds(concreteType, generics);
return;
}
var found = false;
for (var rsp : simplifiedConstraints) {
if (rsp.left.equals(nTph)) {
var pair = new PairLT(new TPH(tph), rsp.right);
if (!generics.contains(pair)) {
addToPairs(generics, pair);
findAllBounds(rsp.right.resolve(), generics);
found = true;
}
}
}
if (!found)
addToPairs(generics, new PairEQ(nTph, ASTToTargetAST.OBJECT));
} else if (type instanceof RefType refType) {
refType.getParaList().forEach(t -> findAllBounds(t, generics));
}
}
abstract void generics(ClassOrInterface classOrInterface, Set<Pair> result, Set<TPH> referenced);
Set<Pair> generics(ClassOrInterface classOrInterface) {
if (computedGenericsOfClasses.containsKey(classOrInterface))
return computedGenericsOfClasses.get(classOrInterface);
Set<Pair> javaResult = new HashSet<>();
computedGenericsOfClasses.put(classOrInterface, javaResult);
for (var field : classOrInterface.getFieldDecl()) {
findAllBounds(field.getType(), javaResult);
}
var referenced = new HashSet<TPH>();
eliminateTransitives(javaResult);
generics(classOrInterface, javaResult, referenced);
var referencedByClass = new HashSet<TPH>();
for (var field : classOrInterface.getFieldDecl()) {
findTphs(field.getType(), referencedByClass);
}
normalize(javaResult, null, referencedByClass);
System.out.println(this.getClass().getSimpleName() + " Class " + classOrInterface.getClassName().getClassName() + ": " + javaResult);
return javaResult;
}
void normalize(Set<Pair> result, Set<Pair> classGenerics, Set<TPH> usedTphs) {
outer:
for (var tph : usedTphs) {
for (var p1 : new HashSet<>(result)) {
if (p1 instanceof PairLT ptph && ptph.left.equals(ptph.right))
result.remove(p1); // TODO This is a bit strange
if (p1.left.equals(tph)) continue outer;
}
if (classGenerics == null || classGenerics.stream().noneMatch((pair) -> pair.left.equals(tph)))
addToPairs(result, new PairEQ(tph, ASTToTargetAST.OBJECT));
}
}
private record ToAdd(TypePlaceholder left, TypePlaceholder right) {}
void equalizeTypeVariables(Set<Pair> input, Set<TPH> referenced) {
var elementsToAddToEquality = new ArrayList<ToAdd>();
for (var pair : new HashSet<>(input)) {
if (pair instanceof PairLT ptph && referenced.contains(ptph.left)) {
var chain = new ArrayList<TPH>();
chain.add(ptph.left);
chain.add(ptph.right);
outer:
while (true) {
var added = false;
for (var pair2 : input) {
if (pair2 instanceof PairLT ptph2 && ptph2.left.equals(chain.get(chain.size() - 1))) {
if (chain.contains(ptph2.right)) break outer;
chain.add(ptph2.right);
added = true;
}
}
if (!added) break;
}
System.out.println(chain + " " + chain.stream().map(e -> e.resolve().getVariance()).toList());
var variance = chain.get(0).resolve().getVariance();
if (variance != 1) continue;
var index = 0;
for (var tph : chain) {
if (variance == 1 && tph.resolve().getVariance() == -1) {
variance = -1;
}
if (variance == -1 && tph.resolve().getVariance() == 1 && referenced.contains(tph)) {
break;
}
index++;
}
if (variance == 1) continue;
var start = chain.get(0);
var prev = start;
for (var i = 1; i < index; i++) {
var cur = chain.get(i);
if (!referenced.contains(cur)) continue;
elementsToAddToEquality.add(new ToAdd(cur.resolve(), start.resolve()));
//addToEquality(cur.resolve(), start.resolve(), referenced);
TPH finalPrev = prev;
input.removeIf(p -> p.equals(new PairLT(finalPrev, cur)));
for (var pair2 : new HashSet<>(input)) {
// TODO Maybe this would be unnecessary if we were to add the = constraints later on
if (pair2 instanceof PairEQ peq && pair.left.equals(cur)) {
input.remove(pair2);
input.add(new PairEQ(start, peq.right));
}
}
prev = chain.get(i);
}
}
}
for (var pair : elementsToAddToEquality) {
System.out.println(pair);
addToEquality(pair.left, pair.right, referenced);
}
}
void findTphs(RefTypeOrTPHOrWildcardOrGeneric type, Set<TPH> tphs) {
if (type instanceof RefType refType) {
refType.getParaList().forEach(t -> findTphs(t, tphs));
} else if (type instanceof TypePlaceholder tph) {
tph = equality.getOrDefault(tph, tph);
var concreteType = concreteTypes.get(new TPH(tph));
if (concreteType != null) {
findTphs(concreteType, tphs);
return;
}
tphs.add(new TPH(tph));
}
}
void eliminateInnerTypeVariablesOfClass(ClassOrInterface classOrInterface, Set<Pair> input, Set<TPH> referenced) {
for (var field : classOrInterface.getFieldDecl()) {
findTphs(field.getType(), referenced);
}
doIterationForMethods(classOrInterface);
for (var method : classOrInterface.getMethods()) {
referenced.addAll(usedTPHsOfMethods.get(method));
}
eliminateInnerTypeVariables(referenced, input);
}
void doIterationForMethods(ClassOrInterface classOrInterface) {
familyOfMethods.clear();
var oldFamily = new HashMap<Method, Set<Pair>>();
do {
oldFamily.clear();
oldFamily.putAll(familyOfMethods);
familyOfMethods.clear();
Stream.concat(classOrInterface.getMethods().stream(), classOrInterface.getConstructors().stream()).forEach(method -> {
family(classOrInterface, method);
});
} while(!oldFamily.equals(familyOfMethods));
Stream.concat(classOrInterface.getMethods().stream(), classOrInterface.getConstructors().stream()).forEach(method -> {
generics(classOrInterface, method);
});
}
private void findChain(Set<TPH> referenced, Set<Pair> input, Set<Pair> output, TPH start, TPH end, Set<TPH> chain) {
if (referenced.contains(end)) {
var pair = new PairLT(start, end);
output.add(pair);
return;
}
var foundNext = false;
for (var pair : input) {
if (pair instanceof PairLT ptph && ptph.left.equals(end)) {
if (chain.contains(ptph.right)) return;
chain = new HashSet<>(chain);
chain.add(ptph.right);
findChain(referenced, input, output, start, ptph.right, chain);
foundNext = true;
}
}
if (!foundNext) {
output.add(new PairEQ(start, ASTToTargetAST.OBJECT));
}
}
void eliminateInnerTypeVariables(Set<TPH> referenced, Set<Pair> input) {
var output = new HashSet<Pair>();
for (var tph : referenced) {
for (var pair : input) {
if (pair instanceof PairLT pthp && pthp.left.equals(tph)) {
var chain = new HashSet<TPH>();
chain.add(tph);
findChain(referenced, input, output, tph, pthp.right, chain);
}
}
}
for (var pair : input) {
if (pair instanceof PairEQ rtph) {
if (referenced.contains(rtph.left))
output.add(rtph);
}
}
input.clear();
input.addAll(output);
}
void eliminateCycles(Set<Pair> input, Set<TPH> referenced) {
var cycles = CycleFinder.findCycles(input);
for (var cycle : cycles) {
var newTph = TypePlaceholder.fresh(new NullToken());
var variance = cycle.get(0).resolve().getVariance();
for (var tph : cycle) {
if (tph.resolve().getVariance() != variance) {
variance = 0;
break;
}
}
newTph.setVariance(variance);
referenced.add(new TPH(newTph));
addToPairs(input, new PairEQ(new TPH(newTph), ASTToTargetAST.OBJECT));
cycle.add(cycle.get(0)); // Make it a complete cycle
for (var i = 0; i < cycle.size() - 1; i++) {
var left = cycle.get(i);
var right = cycle.get(i + 1);
var pair = new PairLT(left, right);
input.remove(pair);
addToEquality(left.resolve(), newTph, referenced);
}
}
}
Set<TPH> findConnectionToReturnType(Set<TPH> returnTypes, Set<Pair> input, Set<TPH> visited, TPH tph) {
if (returnTypes.contains(tph)) {
var res = new HashSet<TPH>();
res.add(tph);
return res;
} else {
for (var pair : input) if (pair instanceof PairLT ptph) {
if (ptph.left.equals(tph) && !visited.contains(ptph.right)) {
visited.add(ptph.right);
var result = findConnectionToReturnType(returnTypes, input, visited, ptph.right);
if (result.size() > 0) {
result.add(ptph.right);
return result;
};
}
}
}
return new HashSet<>();
}
void eliminateInfimaConnectedToReturnType(Method method, Set<Pair> input, Set<TPH> referenced) {
var foundInfima = false;
do {
foundInfima = false;
for (var constraint : new HashSet<>(input)) {
var left = constraint.left;
Set<PairLT> infima = new HashSet<>();
for (var pair : input) {
if (pair instanceof PairLT stph) {
if (pair.left.equals(constraint.left))
infima.add(stph);
}
}
if (infima.size() > 1) {
System.out.println(infima);
for (var pair : infima) {
var returnTypes = findTypeVariables(method.getReturnType());
var chain = findConnectionToReturnType(returnTypes, input, new HashSet<>(), pair.left);
System.out.println("Find: " + pair.left + " " + chain);
chain.remove(pair.left);
if (chain.size() > 0) {
for (var tph : chain)
addToEquality(pair.left.resolve(), tph.resolve(), referenced);
foundInfima = true;
}
}
}
}
} while (foundInfima);
input.removeIf((i) -> (i instanceof PairLT lt) && lt.left.equals(lt.right));
}
void eliminateInfima(Set<Pair> input, Set<TPH> referenced) {
var foundInfima = false;
do {
foundInfima = false;
for (var constraint : new HashSet<>(input)) {
var left = constraint.left;
Set<PairLT> infima = new HashSet<>();
for (var pair : input) {
if (pair instanceof PairLT stph) {
if (pair.left.equals(constraint.left))
infima.add(stph);
}
}
if (infima.size() > 1) {
foundInfima = true;
var newTph = TypePlaceholder.fresh(new NullToken());
var variance = infima.stream().findFirst().get().right.resolve().getVariance();
for (var pair : infima) {
if (pair.right.resolve().getVariance() != variance) {
variance = 0;
break;
}
}
newTph.setVariance(variance);
System.out.println(infima + " " + infima.stream().map(i -> i.right.resolve().getVariance()).toList());
System.out.println("Infima new TPH " + newTph + " variance " + variance);
//referenced.add(newTph);
addToPairs(input, new PairLT(left, new TPH(newTph)));
input.removeAll(infima);
for (var infimum : infima) {
addToEquality(infimum.right.resolve(), newTph, referenced);
new HashSet<>(input).forEach(pair -> {
if (pair.left.equals(infimum.right)) {
input.remove(pair);
if (pair instanceof PairLT stph) {
if (!newTph.equals(stph.right.resolve()))
addToPairs(input, new PairLT(new TPH(newTph), stph.right));
} else if (pair instanceof PairEQ rtph) {
addToPairs(input, new PairEQ(new TPH(newTph), rtph.right));
}
} else if (pair instanceof PairLT stph && stph.right.equals(infimum.right)) {
input.remove(pair);
if (!newTph.equals(stph.left.resolve()))
addToPairs(input, new PairLT(stph.left, new TPH(newTph)));
}
});
}
}
}
} while (foundInfima);
}
RefTypeOrTPHOrWildcardOrGeneric getType(RefTypeOrTPHOrWildcardOrGeneric type) {
if (type instanceof TypePlaceholder tph) {
if (equality.containsKey(tph)) {
return getType(equality.get(tph));
}
return concreteTypes.getOrDefault(new TPH(tph), tph);
}
return type;
}
TargetType getTargetType(RefTypeOrTPHOrWildcardOrGeneric in) {
if (in instanceof TypePlaceholder tph) {
if (equality.containsKey(tph)) {
return getTargetType(equality.get(tph));
}
var type = concreteTypes.get(new TPH(tph));
if (type == null) return new TargetGenericType(tph.getName());
return astToTargetAST.convert(type, this);
}
return astToTargetAST.convert(in, this);
}
}