From 7e259e25971d413131fcb469fbc4000ce9f6f3d0 Mon Sep 17 00:00:00 2001 From: Victorious3 Date: Thu, 23 Jun 2022 18:52:30 +0200 Subject: [PATCH] Mostly finish up populating generics --- .../target/generate/ASTToTargetAST.java | 294 ++++++++++-------- ...itor.java => TracingStatementVisitor.java} | 47 +-- 2 files changed, 191 insertions(+), 150 deletions(-) rename src/main/java/de/dhbwstuttgart/target/generate/{AbstractStatementVisitor.java => TracingStatementVisitor.java} (68%) diff --git a/src/main/java/de/dhbwstuttgart/target/generate/ASTToTargetAST.java b/src/main/java/de/dhbwstuttgart/target/generate/ASTToTargetAST.java index 92ff74d8..51e27e42 100644 --- a/src/main/java/de/dhbwstuttgart/target/generate/ASTToTargetAST.java +++ b/src/main/java/de/dhbwstuttgart/target/generate/ASTToTargetAST.java @@ -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> computedGenerics = new HashMap<>(); + Map> computedGenericsOfMethods = new HashMap<>(); + Map> computedGenericsOfClasses = new HashMap<>(); - Set simplifiedConstraints = new HashSet<>(); - Map result = new HashMap<>(); + HashSet simplifiedConstraints = new HashSet<>(); + Map concreteTypes = new HashMap<>(); Map 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 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 generics) { + return generics.stream().anyMatch(generic -> generic.name().equals(name)); + } + // Family of generated Generics - Set generics(ClassOrInterface owner, Method method) { - if (computedGenerics.containsKey(method)) - return computedGenerics.get(method); + HashSet generics(ClassOrInterface owner, Method method) { + if (computedGenericsOfMethods.containsKey(method)) + return computedGenericsOfMethods.get(method); - Set result = new HashSet<>(); - computedGenerics.put(method, result); + HashSet 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) this.simplifiedConstraints.clone(); + + HashSet typeVariables = new HashSet<>(); + HashSet 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 generics = generics(owner, method); + var generics = generics(owner, method); + HashSet all = new HashSet<>(generics); + // Reflexive and Transitive closure + HashSet 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 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 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 findAllBounds(TargetGenericType type) { - Set result = new HashSet<>(); - findAllBounds(type.name(), result); - return result; - } - - Set generics(ClassOrInterface classOrInterface) { - Set 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)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 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 generics(ClassOrInterface classOrInterface) { + if (computedGenericsOfClasses.containsKey(classOrInterface)) + return computedGenericsOfClasses.get(classOrInterface); + + HashSet 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); } } diff --git a/src/main/java/de/dhbwstuttgart/target/generate/AbstractStatementVisitor.java b/src/main/java/de/dhbwstuttgart/target/generate/TracingStatementVisitor.java similarity index 68% rename from src/main/java/de/dhbwstuttgart/target/generate/AbstractStatementVisitor.java rename to src/main/java/de/dhbwstuttgart/target/generate/TracingStatementVisitor.java index b9e2243d..7a7352ad 100644 --- a/src/main/java/de/dhbwstuttgart/target/generate/AbstractStatementVisitor.java +++ b/src/main/java/de/dhbwstuttgart/target/generate/TracingStatementVisitor.java @@ -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