Mostly finish up populating generics

This commit is contained in:
Victorious3 2022-06-23 18:52:30 +02:00
parent f00ee2598f
commit 7e259e2597
2 changed files with 191 additions and 150 deletions

View File

@ -1,6 +1,5 @@
package de.dhbwstuttgart.target.generate;
import de.dhbwstuttgart.parser.SyntaxTreeGenerator.AssignToLocal;
import de.dhbwstuttgart.syntaxtree.*;
import de.dhbwstuttgart.syntaxtree.statement.*;
import de.dhbwstuttgart.syntaxtree.type.*;
@ -11,7 +10,6 @@ import de.dhbwstuttgart.target.tree.type.*;
import de.dhbwstuttgart.typeinference.result.*;
import java.util.*;
import java.util.stream.Stream;
public class ASTToTargetAST {
@ -19,10 +17,11 @@ public class ASTToTargetAST {
protected Sigma sigma;
private class Sigma {
HashMap<Method, Set<TargetGeneric>> computedGenerics = new HashMap<>();
Map<Method, HashSet<TargetGeneric>> computedGenericsOfMethods = new HashMap<>();
Map<ClassOrInterface, HashSet<TargetGeneric>> computedGenericsOfClasses = new HashMap<>();
Set<PairTPHsmallerTPH> simplifiedConstraints = new HashSet<>();
Map<TypePlaceholder, TargetType> result = new HashMap<>();
HashSet<PairTPHsmallerTPH> simplifiedConstraints = new HashSet<>();
Map<TypePlaceholder, RefTypeOrTPHOrWildcardOrGeneric> concreteTypes = new HashMap<>();
Map<TypePlaceholder, TypePlaceholder> equality = new HashMap<>();
Sigma(ResultSet constraints) {
@ -62,11 +61,9 @@ public class ASTToTargetAST {
right = unified.get(right).get(0);
simplifiedConstraints.add(new PairTPHsmallerTPH(left, right));
result.put(right, new TargetGenericType(right.getName()));
result.put(left, new TargetGenericType(left.getName()));
}
}
System.out.println(simplifiedConstraints);
System.out.println("Simplified constraints: " + simplifiedConstraints);
for (var equality : equalitySet) {
var first = equality.get(0);
@ -76,155 +73,190 @@ public class ASTToTargetAST {
for (var constraint : constraints.results) {
if (constraint instanceof PairTPHequalRefTypeOrWildcardType p) {
result.put(p.left, convert(p.right));
concreteTypes.put(this.equality.getOrDefault(p.left, p.left), p.right);
}
}
}
void findTypeVariables(RefTypeOrTPHOrWildcardOrGeneric type, Set<TypePlaceholder> typeVariables) {
if (type instanceof TypePlaceholder tph) {
tph = equality.getOrDefault(tph, tph);
if (concreteTypes.containsKey(tph)) {
findTypeVariables(concreteTypes.get(tph), typeVariables);
return;
}
typeVariables.add(tph);
} else if (type instanceof RefType refType) {
for (var t : refType.getParaList())
findTypeVariables(t, typeVariables);
}
}
boolean hasBound(String name, Set<TargetGeneric> generics) {
return generics.stream().anyMatch(generic -> generic.name().equals(name));
}
// Family of generated Generics
Set<TargetGeneric> generics(ClassOrInterface owner, Method method) {
if (computedGenerics.containsKey(method))
return computedGenerics.get(method);
HashSet<TargetGeneric> generics(ClassOrInterface owner, Method method) {
if (computedGenericsOfMethods.containsKey(method))
return computedGenericsOfMethods.get(method);
Set<TargetGeneric> result = new HashSet<>();
computedGenerics.put(method, result);
HashSet<TargetGeneric> result = new HashSet<>();
computedGenericsOfMethods.put(method, result);
method.block.accept(new AbstractStatementVisitor() {
// These two methods do the actual work
var genericsOfClass = generics(owner);
var simplifiedConstraints = (HashSet<PairTPHsmallerTPH>) this.simplifiedConstraints.clone();
HashSet<TypePlaceholder> typeVariables = new HashSet<>();
HashSet<TypePlaceholder> typeVariablesOfFields = new HashSet<>();
for (var field : owner.getFieldDecl()) {
findTypeVariables(field.getType(), typeVariablesOfFields);
}
findTypeVariables(method.getReturnType(), typeVariables);
for (var arg : method.getParameterList().getFormalparalist()) {
findTypeVariables(arg.getType(), typeVariables);
}
method.block.accept(new TracingStatementVisitor() {
@Override
public void visit(LocalVarDecl localVarDecl) {
// TODO
findTypeVariables(localVarDecl.getType(), typeVariables);
}
});
method.block.accept(new TracingStatementVisitor() {
@Override
public void visit(MethodCall methodCall) {
super.visit(methodCall);
if (methodCall.receiver instanceof ExpressionReceiver expressionReceiver) {
if (expressionReceiver.expr instanceof This) {
// TODO This is going to fail spectacularly for overloaded methods
var optMethod = owner.getMethods().stream().filter(m -> m.name.equals(methodCall.name)).findFirst();
assert optMethod.isPresent();
var method = optMethod.get();
Set<TargetGeneric> generics = generics(owner, method);
var generics = generics(owner, method);
HashSet<TargetGeneric> all = new HashSet<>(generics);
// Reflexive and Transitive closure
HashSet<TargetGeneric> toAdd = new HashSet<>();
do {
toAdd.clear();
for (var g1 : all) {
for (var g2 : all) {
if (g1.bound() instanceof TargetGenericType type) {
if (g2.name().equals(type.name()) && generics.stream().anyMatch(generic -> generic.name().equals(type.name())))
toAdd.add(new TargetGeneric(g1.name(), g2.bound()));
}
}
}
all.addAll(toAdd);
} while (toAdd.size() > 0);
for (var generic : all) {
toAdd.add(new TargetGeneric(generic.name(), new TargetGenericType(generic.name())));
}
all.addAll(toAdd);
HashSet<PairTPHsmallerTPH> newPairs = new HashSet<>();
// Loop from hell
outer: for (var tph : typeVariables) {
for (var generic : all) {
if (!(generic.bound() instanceof TargetGenericType type))
continue;
for (var pair : simplifiedConstraints) {
if (!(pair.left.equals(tph) && pair.right.getName().equals(generic.name())))
continue;
for (var tph2 : typeVariables) {
for (var pair2 : simplifiedConstraints) {
if (!(pair2.right.equals(tph2) && pair2.left.getName().equals(type.name())))
continue;
newPairs.add(new PairTPHsmallerTPH(tph, tph2));
continue outer;
}
}
}
}
}
simplifiedConstraints.addAll(newPairs);
}
}
}
// Everything down here is just for walking the AST
@Override
public void visit(ArgumentList argumentList) {
argumentList.getArguments().forEach(expr -> expr.accept(this));
}
@Override
public void visit(LambdaExpression lambdaExpression) {
lambdaExpression.methodBody.accept(this);
}
@Override
public void visit(Assign assign) {
assign.rightSide.accept(this);
}
@Override
public void visit(BinaryExpr binary) {
binary.lexpr.accept(this);
binary.rexpr.accept(this);
}
@Override
public void visit(Block block) {
for (var expr : block.statements)
expr.accept(this);
}
@Override
public void visit(ForStmt forStmt) {
forStmt.body_Loop_block.accept(this);
}
@Override
public void visit(IfStmt ifStmt) {
ifStmt.then_block.accept(this);
ifStmt.else_block.accept(this);
}
@Override
public void visit(NewClass newClass) {
newClass.arglist.accept(this);
}
@Override
public void visit(NewArray newArray) {
newArray.expr.forEach(expr -> expr.accept(this));
}
@Override
public void visit(Return aReturn) {
aReturn.retexpr.accept(this);
}
@Override
public void visit(WhileStmt whileStmt) {
whileStmt.loopBlock.accept(this);
}
@Override
public void visit(DoStmt whileStmt) {
whileStmt.loopBlock.accept(this);
}
@Override
public void visit(UnaryExpr unaryExpr) {
unaryExpr.expr.accept(this);
}
});
return result;
}
void findAllBounds(String name, Set<TargetGeneric> generics) {
for (var rsp : simplifiedConstraints) {
var left = equality.getOrDefault(rsp.left, rsp.left);
var right = equality.getOrDefault(rsp.right, rsp.right);
if (left.getName().equals(name)) {
generics.add(new TargetGeneric(name, new TargetGenericType(right.getName())));
findAllBounds(right.getName(), generics);
return;
}
}
generics.add(new TargetGeneric(name, TargetType.Object));
}
Set<TargetGeneric> findAllBounds(TargetGenericType type) {
Set<TargetGeneric> result = new HashSet<>();
findAllBounds(type.name(), result);
return result;
}
Set<TargetGeneric> generics(ClassOrInterface classOrInterface) {
Set<TargetGeneric> result = new HashSet<>();
for (var field : classOrInterface.getFieldDecl()) {
if (field.getType() instanceof TypePlaceholder tph) {
var tpe = get(tph);
switch (tpe) {
case TargetSpecializedType specializedType:
specializedType.params().stream().flatMap(targetType -> {
if (targetType instanceof TargetSpecializedType spc)
return Stream.concat(Stream.of(targetType), spc.params().stream());
return Stream.of(targetType);
}).forEach(param -> {
if (param instanceof TargetGenericType genericType) {
result.addAll(findAllBounds(genericType));
}
});
break;
case TargetGenericType genericType:
result.addAll(findAllBounds(genericType));
default: break;
// Type variables with bounds that are also type variables of the method or type variables of fields
for (var typeVariable : (HashSet<TypePlaceholder>)typeVariables.clone()) {
for (var pair : simplifiedConstraints) {
if (pair.left.equals(typeVariable) && (typeVariables.contains(pair.right) || typeVariablesOfFields.contains(pair.right))) {
result.add(new TargetGeneric(pair.left.getName(), convert(pair.right)));
typeVariables.add(pair.right);
}
}
}
// All unbounded type variables
outer: for (var typeVariable : typeVariables) {
for (var pair : simplifiedConstraints) {
if (pair.left.equals(typeVariable) && typeVariables.contains(pair.right))
continue outer;
}
if (!hasBound(typeVariable.getName(), genericsOfClass))
result.add(new TargetGeneric(typeVariable.getName(), TargetType.Object));
}
// All unbounded bounds
outer: for (var pair : simplifiedConstraints) {
for (var pair2 : simplifiedConstraints) {
if (pair.right.equals(pair2.left))
continue outer;
}
if (!hasBound(pair.right.getName(), genericsOfClass) && typeVariables.contains(pair.right))
result.add(new TargetGeneric(pair.right.getName(), TargetType.Object));
}
System.out.println(method.name + ": " + result);
return result;
}
void findAllBounds(RefTypeOrTPHOrWildcardOrGeneric type, Set<TargetGeneric> generics) {
if (type instanceof TypePlaceholder tph) {
tph = equality.getOrDefault(tph, tph);
var concreteType = concreteTypes.get(tph);
if (concreteType != null) {
findAllBounds(concreteType, generics);
return;
}
for (var rsp : simplifiedConstraints) {
var left = equality.getOrDefault(rsp.left, rsp.left);
var right = equality.getOrDefault(rsp.right, rsp.right);
if (left.equals(tph)) {
generics.add(new TargetGeneric(tph.getName(), new TargetGenericType(right.getName())));
findAllBounds(right, generics);
return;
}
}
generics.add(new TargetGeneric(tph.getName(), TargetType.Object));
} else if (type instanceof RefType refType) {
refType.getParaList().forEach(t -> findAllBounds(t, generics));
}
}
HashSet<TargetGeneric> generics(ClassOrInterface classOrInterface) {
if (computedGenericsOfClasses.containsKey(classOrInterface))
return computedGenericsOfClasses.get(classOrInterface);
HashSet<TargetGeneric> result = new HashSet<>();
for (var field : classOrInterface.getFieldDecl()) {
findAllBounds(field.getType(), result);
}
computedGenericsOfClasses.put(classOrInterface, result);
return result;
}
@ -232,7 +264,9 @@ public class ASTToTargetAST {
if (equality.containsKey(tph)) {
return get(equality.get(tph));
}
return result.get(tph);
var type = concreteTypes.get(tph);
if (type == null) return new TargetGenericType(tph.getName());
return convert(type);
}
}

View File

@ -4,30 +4,41 @@ import de.dhbwstuttgart.parser.SyntaxTreeGenerator.AssignToLocal;
import de.dhbwstuttgart.syntaxtree.StatementVisitor;
import de.dhbwstuttgart.syntaxtree.statement.*;
public abstract class AbstractStatementVisitor implements StatementVisitor {
// This visitor walks the entire tree, individual methods may be overridden
public abstract class TracingStatementVisitor implements StatementVisitor {
@Override
public void visit(MethodCall methodCall) {
methodCall.receiver.accept(this);
methodCall.getArgumentList().accept(this);
}
@Override
public void visit(ArgumentList argumentList) {
argumentList.getArguments().forEach(expr -> expr.accept(this));
}
@Override
public void visit(LambdaExpression lambdaExpression) {
lambdaExpression.methodBody.accept(this);
}
@Override
public void visit(Assign assign) {
assign.rightSide.accept(this);
}
@Override
public void visit(BinaryExpr binary) {
binary.lexpr.accept(this);
binary.rexpr.accept(this);
}
@Override
public void visit(Block block) {
for (var expr : block.statements)
expr.accept(this);
}
@Override
@ -47,12 +58,13 @@ public abstract class AbstractStatementVisitor implements StatementVisitor {
@Override
public void visit(ForStmt forStmt) {
forStmt.body_Loop_block.accept(this);
}
@Override
public void visit(IfStmt ifStmt) {
ifStmt.then_block.accept(this);
ifStmt.else_block.accept(this);
}
@Override
@ -71,23 +83,18 @@ public abstract class AbstractStatementVisitor implements StatementVisitor {
}
@Override
public void visit(MethodCall methodCall) {
}
@Override
public void visit(NewClass methodCall) {
public void visit(NewClass newClass) {
this.visit((MethodCall) newClass);
}
@Override
public void visit(NewArray newArray) {
newArray.expr.forEach(expr -> expr.accept(this));
}
@Override
public void visit(Return aReturn) {
aReturn.retexpr.accept(this);
}
@Override
@ -112,12 +119,12 @@ public abstract class AbstractStatementVisitor implements StatementVisitor {
@Override
public void visit(WhileStmt whileStmt) {
whileStmt.loopBlock.accept(this);
}
@Override
public void visit(DoStmt whileStmt) {
whileStmt.loopBlock.accept(this);
}
@Override
@ -142,7 +149,7 @@ public abstract class AbstractStatementVisitor implements StatementVisitor {
@Override
public void visit(UnaryExpr unaryExpr) {
unaryExpr.expr.accept(this);
}
@Override