forked from JavaTX/JavaCompilerCore
977 lines
39 KiB
Java
977 lines
39 KiB
Java
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()) {
|
||
var usedTPHs = usedTPHsOfMethods.get(method);
|
||
if (usedTPHs != null)
|
||
referenced.addAll(usedTPHs);
|
||
}
|
||
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);
|
||
}
|
||
}
|