diff graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/parser/NodeParser.java @ 20940:476374f3fe9a

Truffle-DSL: generate better polymorphic execute signatures
author Christian Humer <christian.humer@gmail.com>
date Tue, 14 Apr 2015 15:12:48 +0200
parents 18c0f02fa4d2
children 4f45e4d3361c
line wrap: on
line diff
--- a/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/parser/NodeParser.java	Tue Apr 14 15:12:48 2015 +0200
+++ b/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/parser/NodeParser.java	Tue Apr 14 15:12:48 2015 +0200
@@ -33,6 +33,7 @@
 import com.oracle.truffle.api.*;
 import com.oracle.truffle.api.dsl.*;
 import com.oracle.truffle.api.dsl.internal.*;
+import com.oracle.truffle.api.frame.*;
 import com.oracle.truffle.api.nodes.*;
 import com.oracle.truffle.dsl.processor.*;
 import com.oracle.truffle.dsl.processor.expression.*;
@@ -43,7 +44,6 @@
 import com.oracle.truffle.dsl.processor.model.*;
 import com.oracle.truffle.dsl.processor.model.NodeChildData.Cardinality;
 import com.oracle.truffle.dsl.processor.model.SpecializationData.SpecializationKind;
-import com.oracle.truffle.dsl.processor.model.TemplateMethod.TypeSignature;
 
 @DSLOptions
 public class NodeParser extends AbstractParser<NodeData> {
@@ -104,8 +104,9 @@
         } catch (CompileErrorException e) {
             throw e;
         } catch (Throwable e) {
-            e.addSuppressed(new RuntimeException(String.format("Parsing of Node %s failed.", ElementUtils.getQualifiedName(rootType)), e));
-            throw e;
+            RuntimeException e2 = new RuntimeException(String.format("Parsing of Node %s failed.", ElementUtils.getQualifiedName(rootType)));
+            e2.addSuppressed(e);
+            throw e2;
         }
         if (node == null && !enclosedNodes.isEmpty()) {
             node = new NodeData(context, rootType);
@@ -147,17 +148,16 @@
         node.getFields().addAll(parseFields(lookupTypes, members));
         node.getChildren().addAll(parseChildren(lookupTypes, members));
         node.getChildExecutions().addAll(parseExecutions(node.getFields(), node.getChildren(), members));
-        node.setExecutableTypes(groupExecutableTypes(parseExecutableTypeData(members, node.getChildExecutions().size(), context.getFrameTypes())));
+        node.getExecutableTypes().addAll(parseExecutableTypeData(members, node.getChildExecutions().size(), context.getFrameTypes(), false));
 
         initializeExecutableTypes(node);
         initializeImportGuards(node, lookupTypes, members);
+        initializeChildren(node);
 
         if (node.hasErrors()) {
             return node; // error sync point
         }
 
-        initializeChildren(node);
-
         if (node.hasErrors()) {
             return node; // error sync point
         }
@@ -170,9 +170,10 @@
         if (node.hasErrors()) {
             return node;  // error sync point
         }
+        initializeSpecializations(members, node);
+        initializeExecutableTypeHierarchy(node);
 
         verifySpecializationSameLength(node);
-        initializeSpecializations(members, node);
         initializeShortCircuits(node); // requires specializations and polymorphic specializations
 
         verifyVisibilities(node);
@@ -183,6 +184,67 @@
         return node;
     }
 
+    private static void initializeExecutableTypeHierarchy(NodeData node) {
+        SpecializationData polymorphic = node.getPolymorphicSpecialization();
+        if (polymorphic != null) {
+            boolean polymorphicSignatureFound = false;
+            TypeMirror frame = polymorphic.getFrame() != null ? polymorphic.getFrame().getType() : null;
+            ExecutableTypeData polymorphicType = new ExecutableTypeData(polymorphic.getReturnType().getType(), "execute", frame, TemplateMethod.getSignatureTypes(polymorphic));
+            for (ExecutableTypeData type : node.getExecutableTypes()) {
+                if (polymorphicType.sameSignature(type)) {
+                    polymorphicSignatureFound = true;
+                    break;
+                }
+            }
+
+            if (!polymorphicSignatureFound) {
+                node.getExecutableTypes().add(polymorphicType);
+            }
+        }
+
+        List<ExecutableTypeData> rootTypes = buildExecutableHierarchy(node);
+        List<ExecutableTypeData> additionalAbstractRootTypes = new ArrayList<>();
+        for (int i = 1; i < rootTypes.size(); i++) {
+            ExecutableTypeData rootType = rootTypes.get(i);
+            if (rootType.isAbstract()) {
+                // cannot implemement root
+                additionalAbstractRootTypes.add(rootType);
+            } else {
+                node.getExecutableTypes().remove(rootType);
+            }
+        }
+        if (!additionalAbstractRootTypes.isEmpty()) {
+            node.addError("Incompatible abstract execute methods found %s.", rootTypes);
+        }
+
+    }
+
+    private static List<ExecutableTypeData> buildExecutableHierarchy(NodeData node) {
+        List<ExecutableTypeData> executes = node.getExecutableTypes();
+        if (executes.isEmpty()) {
+            return Collections.emptyList();
+        }
+        List<ExecutableTypeData> hierarchyExecutes = new ArrayList<>(executes);
+        Collections.sort(hierarchyExecutes);
+        ExecutableTypeData parent = hierarchyExecutes.get(0);
+        ListIterator<ExecutableTypeData> executesIterator = hierarchyExecutes.listIterator(1);
+        buildExecutableHierarchy(node, parent, executesIterator);
+        return hierarchyExecutes;
+    }
+
+    private static void buildExecutableHierarchy(NodeData node, ExecutableTypeData parent, ListIterator<ExecutableTypeData> executesIterator) {
+        while (executesIterator.hasNext()) {
+            ExecutableTypeData other = executesIterator.next();
+            if (other.canDelegateTo(node, parent)) {
+                parent.addDelegatedFrom(other);
+                executesIterator.remove();
+            }
+        }
+        for (int i = 1; i < parent.getDelegatedFrom().size(); i++) {
+            buildExecutableHierarchy(node, parent.getDelegatedFrom().get(i - 1), parent.getDelegatedFrom().listIterator(i));
+        }
+    }
+
     private List<Element> loadMembers(TypeElement templateType) {
         List<Element> members = new ArrayList<>(CompilerFactory.getCompiler(templateType).getAllMembersInDeclarationOrder(context.getEnvironment(), templateType));
 
@@ -512,13 +574,17 @@
         return executions;
     }
 
-    private List<ExecutableTypeData> parseExecutableTypeData(List<? extends Element> elements, int signatureSize, List<TypeMirror> frameTypes) {
+    private List<ExecutableTypeData> parseExecutableTypeData(List<? extends Element> elements, int signatureSize, List<TypeMirror> frameTypes, boolean includeFinals) {
         List<ExecutableTypeData> typeData = new ArrayList<>();
         for (ExecutableElement method : ElementFilter.methodsIn(elements)) {
             Set<Modifier> modifiers = method.getModifiers();
             if (modifiers.contains(Modifier.PRIVATE) || modifiers.contains(Modifier.STATIC)) {
                 continue;
             }
+            if (!includeFinals && modifiers.contains(Modifier.FINAL)) {
+                continue;
+            }
+
             if (!method.getSimpleName().toString().startsWith("execute")) {
                 continue;
             }
@@ -545,6 +611,19 @@
         }
 
         Collections.sort(typeData);
+
+        List<String> names = new ArrayList<>();
+        for (ExecutableTypeData type : typeData) {
+            names.add(type.getUniqueName());
+        }
+        while (renameDuplicateIds(names)) {
+            // fix point
+        }
+
+        for (int i = 0; i < typeData.size(); i++) {
+            typeData.get(i).setUniqueName(names.get(i));
+        }
+
         return typeData;
     }
 
@@ -582,47 +661,16 @@
 
         node.setFrameType(frameType);
 
-        int totalGenericCount = 0;
-        int totalVoidCount = 0;
-        for (Integer evaluatedCount : evaluatedCounts) {
-            List<ExecutableTypeData> genericExecutes = node.findGenericExecutableTypes(context, evaluatedCount);
-            int genericCount = 0;
-            int voidCount = 0;
-            for (ExecutableTypeData executableTypeData : genericExecutes) {
-                if (!executableTypeData.getMethod().getModifiers().contains(Modifier.FINAL)) {
-                    if (ElementUtils.isVoid(executableTypeData.getReturnType())) {
-                        voidCount++;
-                    } else {
-                        genericCount++;
-                    }
-                }
+        boolean genericFound = false;
+        for (ExecutableTypeData type : node.getExecutableTypes()) {
+            if (!type.hasUnexpectedValue(context)) {
+                genericFound = true;
+                break;
             }
-            // multiple generic execute
-            if (evaluatedCount == 0) {
-                if (voidCount > 1) {
-                    List<String> methodSignatures = new ArrayList<>();
-                    for (ExecutableTypeData type : genericExecutes) {
-                        if (context.isType(type.getReturnType(), void.class)) {
-                            methodSignatures.add(ElementUtils.createReferenceName(type.getMethod()));
-                        }
-                    }
-                    node.addWarning("Multiple accessible and overridable generic execute methods found %s. Remove all but one or mark all but one as final.", methodSignatures);
-                } else if (genericCount > 1) {
-                    List<String> methodSignatures = new ArrayList<>();
-                    for (ExecutableTypeData type : genericExecutes) {
-                        if (!context.isType(type.getReturnType(), void.class)) {
-                            methodSignatures.add(ElementUtils.createReferenceName(type.getMethod()));
-                        }
-                    }
-                    node.addWarning("Multiple accessible and overridable generic execute methods found %s. Remove all but one or mark all but one as final.", methodSignatures);
-                }
-            }
-            totalGenericCount += genericCount;
-            totalVoidCount += voidCount;
         }
 
         // no generic executes
-        if (totalGenericCount + totalVoidCount == 0) {
+        if (!genericFound) {
             node.addError("No accessible and overridable generic execute method found. Generic execute methods usually have the "
                             + "signature 'public abstract {Type} execute(VirtualFrame)' and must not throw any checked exceptions.");
         }
@@ -658,25 +706,6 @@
 
     }
 
-    private static Map<Integer, List<ExecutableTypeData>> groupExecutableTypes(List<ExecutableTypeData> executableTypes) {
-        Map<Integer, List<ExecutableTypeData>> groupedTypes = new TreeMap<>();
-        for (ExecutableTypeData type : executableTypes) {
-            int evaluatedCount = type.getEvaluatedCount();
-
-            List<ExecutableTypeData> types = groupedTypes.get(evaluatedCount);
-            if (types == null) {
-                types = new ArrayList<>();
-                groupedTypes.put(evaluatedCount, types);
-            }
-            types.add(type);
-        }
-
-        for (List<ExecutableTypeData> types : groupedTypes.values()) {
-            Collections.sort(types);
-        }
-        return groupedTypes;
-    }
-
     private void initializeChildren(NodeData node) {
         initializeExecuteWith(node);
 
@@ -759,7 +788,7 @@
         if (parentNode.getFrameType() != null) {
             frameTypes = Arrays.asList(parentNode.getFrameType());
         }
-        node.setExecutableTypes(groupExecutableTypes(parseExecutableTypeData(members, child.getExecuteWith().size(), frameTypes)));
+        node.getExecutableTypes().addAll(parseExecutableTypeData(members, child.getExecuteWith().size(), frameTypes, true));
         node.setFrameType(parentNode.getFrameType());
         return node;
     }
@@ -1259,7 +1288,7 @@
         if (execution == null) {
             allowedTypes = spec.getAllowedTypes();
         } else {
-            allowedTypes = node.getPossibleTypes(execution);
+            allowedTypes = node.getGenericTypes(execution);
         }
         if (allowedTypes.size() == 1) {
             return allowedTypes.iterator().next();
@@ -1301,36 +1330,68 @@
 
         SpecializationData generic = node.getGenericSpecialization();
 
-        List<TypeMirror> polymorphicSignature = new ArrayList<>();
-        List<Parameter> updatePolymorphic = Arrays.asList();
-        for (Parameter genericParameter : updatePolymorphic) {
-            if (!genericParameter.getSpecification().isSignature()) {
-                continue;
-            }
+        List<VariableElement> types = new ArrayList<>();
 
-            Set<TypeMirror> usedTypes = new HashSet<>();
-            for (SpecializationData specialization : node.getSpecializations()) {
-                if (!specialization.isSpecialized()) {
-                    continue;
-                }
-                Parameter parameter = specialization.findParameter(genericParameter.getLocalName());
-                if (parameter == null) {
-                    throw new AssertionError("Parameter existed in generic specialization but not in specialized. param = " + genericParameter.getLocalName());
-                }
-                usedTypes.add(parameter.getType());
+        Set<TypeMirror> frameTypes = new HashSet<>();
+        for (SpecializationData specialization : node.getSpecializations()) {
+            if (specialization.getFrame() != null) {
+                frameTypes.add(specialization.getFrame().getType());
             }
-
-            TypeMirror polymorphicType;
-            if (usedTypes.size() == 1) {
-                polymorphicType = usedTypes.iterator().next();
+        }
+        if (!frameTypes.isEmpty()) {
+            TypeMirror frameType;
+            if (frameTypes.size() == 1) {
+                frameType = frameTypes.iterator().next();
             } else {
-                polymorphicType = context.getType(Object.class);
+                frameType = context.getType(Frame.class);
             }
-            polymorphicSignature.add(polymorphicType);
+            types.add(new CodeVariableElement(frameType, "frameValue"));
         }
 
-        SpecializationData polymorphic = new SpecializationData(node, generic, SpecializationKind.POLYMORPHIC);
-        polymorphic.updateSignature(new TypeSignature(polymorphicSignature));
+        TypeMirror returnType = null;
+        int index = 0;
+        for (Parameter genericParameter : generic.getReturnTypeAndParameters()) {
+            TypeMirror polymorphicType;
+            if (!genericParameter.getSpecification().isSignature()) {
+                polymorphicType = genericParameter.getType();
+            } else {
+                Set<TypeMirror> usedTypes = new HashSet<>();
+                for (SpecializationData specialization : node.getSpecializations()) {
+                    if (specialization.isUninitialized()) {
+                        continue;
+                    }
+                    Parameter parameter = specialization.findParameter(genericParameter.getLocalName());
+                    if (parameter == specialization.getReturnType() && specialization.isFallback() && specialization.getMethod() == null) {
+                        continue;
+                    }
+                    if (parameter == null) {
+                        throw new AssertionError("Parameter existed in generic specialization but not in specialized. param = " + genericParameter.getLocalName());
+                    }
+                    usedTypes.add(parameter.getType());
+                }
+
+                if (usedTypes.size() == 1) {
+                    polymorphicType = usedTypes.iterator().next();
+
+                    if (node.getTypeSystem().hasImplicitSourceTypes(polymorphicType)) {
+                        polymorphicType = context.getType(Object.class);
+                    }
+                } else {
+                    polymorphicType = context.getType(Object.class);
+                }
+            }
+            if (genericParameter == generic.getReturnType()) {
+                returnType = polymorphicType;
+            } else {
+                types.add(new CodeVariableElement(polymorphicType, "param" + index));
+            }
+            index++;
+        }
+
+        SpecializationMethodParser parser = new SpecializationMethodParser(context, node);
+
+        SpecializationData polymorphic = parser.create("Polymorphic", TemplateMethod.NO_NATURAL_ORDER, null, null, returnType, types);
+        polymorphic.setKind(SpecializationKind.POLYMORPHIC);
         node.getSpecializations().add(polymorphic);
     }