changeset 11545:2fb276f5e3e9

Truffle-DSL: implemented implicit casts.
author Christian Humer <christian.humer@gmail.com>
date Fri, 06 Sep 2013 16:16:40 +0200
parents e5b5a5cb0ac7
children f1c3f50ac36e
files graal/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/ImplicitCastTest.java graal/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/TypeSystemTest.java graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/node/NodeChildData.java graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/node/NodeCodeGenerator.java graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/node/NodeData.java graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/node/NodeParser.java graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/typesystem/ImplicitCastData.java graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/typesystem/ImplicitCastParser.java graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/typesystem/TypeSystemCodeGenerator.java graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/typesystem/TypeSystemData.java graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/typesystem/TypeSystemParser.java
diffstat 11 files changed, 982 insertions(+), 231 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/ImplicitCastTest.java	Fri Sep 06 16:16:40 2013 +0200
@@ -0,0 +1,84 @@
+/*
+ * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package com.oracle.truffle.api.dsl.test;
+
+import org.junit.*;
+
+import com.oracle.truffle.api.dsl.*;
+import com.oracle.truffle.api.dsl.test.ImplicitCastTestFactory.ImplicitCast0NodeFactory;
+import com.oracle.truffle.api.dsl.test.NodeContainerTest.Str;
+import com.oracle.truffle.api.dsl.test.TypeSystemTest.TestRootNode;
+import com.oracle.truffle.api.dsl.test.TypeSystemTest.ValueNode;
+import com.oracle.truffle.api.frame.*;
+
+public class ImplicitCastTest {
+
+    @TypeSystem({int.class, boolean.class, String.class, Str.class})
+    static class ImplicitCast0Types {
+
+        @ImplicitCast
+        boolean castInt(int intvalue) {
+            return intvalue == 1 ? true : false;
+        }
+
+        @ImplicitCast
+        boolean castString(String strvalue) {
+            return strvalue.equals("1");
+        }
+
+    }
+
+    @TypeSystemReference(ImplicitCast0Types.class)
+    @NodeChild(value = "operand", type = ImplicitCast0Node.class)
+    abstract static class ImplicitCast0Node extends ValueNode {
+
+        public abstract Object executeEvaluated(VirtualFrame frame, Object value2);
+
+        @Specialization(order = 1)
+        public String op1(String value) throws RuntimeException {
+            return value;
+        }
+
+        @Specialization(order = 2)
+        public boolean op1(boolean value) throws RuntimeException {
+            return value;
+        }
+
+    }
+
+    @Test
+    public void testImplicitCast0() {
+        ImplicitCast0Node node = ImplicitCast0NodeFactory.create(null);
+        TestRootNode<ImplicitCast0Node> root = new TestRootNode<>(node);
+        Assert.assertEquals("2", root.getNode().executeEvaluated(null, "2"));
+        Assert.assertEquals(true, root.getNode().executeEvaluated(null, 1));
+        Assert.assertEquals("1", root.getNode().executeEvaluated(null, "1"));
+        Assert.assertEquals(true, root.getNode().executeEvaluated(null, 1));
+        Assert.assertEquals(true, root.getNode().executeEvaluated(null, true));
+    }
+
+    // TODO assert implicit casts only in one direction
+
+    // test example that covers the most cases
+
+}
--- a/graal/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/TypeSystemTest.java	Fri Sep 06 16:11:15 2013 +0200
+++ b/graal/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/TypeSystemTest.java	Fri Sep 06 16:16:40 2013 +0200
@@ -48,6 +48,11 @@
             return (int) value;
         }
 
+        @ImplicitCast
+        Str castStr(String strvalue) {
+            return new Str(strvalue);
+        }
+
     }
 
     @TypeSystemReference(SimpleTypes.class)
--- a/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/node/NodeChildData.java	Fri Sep 06 16:11:15 2013 +0200
+++ b/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/node/NodeChildData.java	Fri Sep 06 16:16:40 2013 +0200
@@ -49,6 +49,7 @@
         DEFAULT, SHORT_CIRCUIT
     }
 
+    private final NodeData parent;
     private final Element sourceElement;
     private final AnnotationMirror sourceAnnotationMirror;
 
@@ -64,8 +65,9 @@
 
     private NodeData nodeData;
 
-    public NodeChildData(Element sourceElement, AnnotationMirror sourceMirror, String name, TypeMirror nodeType, TypeMirror originalNodeType, Element accessElement, Cardinality cardinality,
-                    ExecutionKind executionKind) {
+    public NodeChildData(NodeData parent, Element sourceElement, AnnotationMirror sourceMirror, String name, TypeMirror nodeType, TypeMirror originalNodeType, Element accessElement,
+                    Cardinality cardinality, ExecutionKind executionKind) {
+        this.parent = parent;
         this.sourceElement = sourceElement;
         this.sourceAnnotationMirror = sourceMirror;
         this.name = name;
@@ -84,6 +86,31 @@
         this.executeWith = executeWith;
     }
 
+    public boolean needsImplicitCast(ProcessorContext context) {
+        if (!parent.needsRewrites(context)) {
+            return false;
+        }
+
+        boolean used = false;
+        SpecializationData generic = parent.getGenericSpecialization();
+        for (ActualParameter param : generic.getParameters()) {
+            if (!param.getSpecification().isSignature()) {
+                continue;
+            }
+            NodeChildData child = parent.findChild(param.getSpecification().getName());
+            if (child == this) {
+                used = true;
+                break;
+            }
+        }
+
+        if (!used) {
+            return false;
+        }
+
+        return getNodeData().getTypeSystem().getImplicitCasts() != null && !getNodeData().getTypeSystem().getImplicitCasts().isEmpty();
+    }
+
     public ExecutableTypeData findExecutableType(ProcessorContext context, TypeData targetType) {
         ExecutableTypeData executableType = nodeData.findExecutableType(targetType, getExecuteWith().size());
         if (executableType == null) {
--- a/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/node/NodeCodeGenerator.java	Fri Sep 06 16:11:15 2013 +0200
+++ b/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/node/NodeCodeGenerator.java	Fri Sep 06 16:16:40 2013 +0200
@@ -62,12 +62,17 @@
         return node.getNodeId() + "Factory";
     }
 
+    private static String nodeCastClassName(NodeData node, TypeData type) {
+        String nodeid = resolveNodeId(node);
+        if (type == null) {
+            return nodeid + "ImplicitCast";
+        } else {
+            return Utils.firstLetterUpperCase(Utils.getSimpleName(type.getPrimitiveType())) + "Cast";
+        }
+    }
+
     private static String nodeSpecializationClassName(SpecializationData specialization) {
-        String nodeid = specialization.getNode().getNodeId();
-        if (nodeid.endsWith("Node") && !nodeid.equals("Node")) {
-            nodeid = nodeid.substring(0, nodeid.length() - 4);
-        }
-
+        String nodeid = resolveNodeId(specialization.getNode());
         String name = Utils.firstLetterUpperCase(nodeid);
         name += Utils.firstLetterUpperCase(specialization.getId());
         name += "Node";
@@ -75,10 +80,7 @@
     }
 
     private static String nodePolymorphicClassName(NodeData node, SpecializationData specialization) {
-        String nodeid = node.getNodeId();
-        if (nodeid.endsWith("Node") && !nodeid.equals("Node")) {
-            nodeid = nodeid.substring(0, nodeid.length() - 4);
-        }
+        String nodeid = resolveNodeId(node);
 
         String name = Utils.firstLetterUpperCase(nodeid);
         if (specialization == node.getGenericPolymorphicSpecialization()) {
@@ -89,10 +91,22 @@
         return name;
     }
 
+    private static String resolveNodeId(NodeData node) {
+        String nodeid = node.getNodeId();
+        if (nodeid.endsWith("Node") && !nodeid.equals("Node")) {
+            nodeid = nodeid.substring(0, nodeid.length() - 4);
+        }
+        return nodeid;
+    }
+
     private static String valueNameEvaluated(ActualParameter targetParameter) {
         return valueName(targetParameter) + "Evaluated";
     }
 
+    private static String typeName(ActualParameter param) {
+        return param.getLocalName() + "Type";
+    }
+
     private static String valueName(ActualParameter param) {
         return param.getLocalName();
     }
@@ -147,7 +161,8 @@
         }
     }
 
-    private void addInternalValueParameterNames(CodeTreeBuilder builder, TemplateMethod source, TemplateMethod specialization, String unexpectedValueName, boolean forceFrame, boolean includeImplicit) {
+    private void addInternalValueParameterNames(CodeTreeBuilder builder, TemplateMethod source, TemplateMethod specialization, String unexpectedValueName, boolean forceFrame, boolean includeImplicit,
+                    Map<String, String> customNames) {
         if (forceFrame && specialization.getSpecification().findParameterSpec("frame") != null) {
             builder.string("frameValue");
         }
@@ -166,7 +181,9 @@
 
             ActualParameter sourceParameter = source.findParameter(parameter.getLocalName());
 
-            if (unexpectedValueName != null && parameter.getLocalName().equals(unexpectedValueName)) {
+            if (customNames != null && customNames.containsKey(parameter.getLocalName())) {
+                builder.string(customNames.get(parameter.getLocalName()));
+            } else if (unexpectedValueName != null && parameter.getLocalName().equals(unexpectedValueName)) {
                 builder.cast(parameter.getType(), CodeTreeBuilder.singleString("ex.getResult()"));
             } else if (sourceParameter != null) {
                 builder.string(valueName(sourceParameter, parameter));
@@ -298,10 +315,7 @@
     }
 
     private static String baseClassName(NodeData node) {
-        String nodeid = node.getNodeId();
-        if (nodeid.endsWith("Node") && !nodeid.equals("Node")) {
-            nodeid = nodeid.substring(0, nodeid.length() - 4);
-        }
+        String nodeid = resolveNodeId(node);
         String name = Utils.firstLetterUpperCase(nodeid);
         name += "BaseNode";
         return name;
@@ -362,7 +376,7 @@
         builder.startThrow().startNew(getContext().getType(UnsupportedOperationException.class));
         builder.startCall("createInfo0");
         builder.doubleQuote("Unsupported values");
-        addInternalValueParameterNames(builder, current, current, null, false, true);
+        addInternalValueParameterNames(builder, current, current, null, false, true, null);
         builder.end().end().end();
     }
 
@@ -422,7 +436,11 @@
     }
 
     @Override
+    @SuppressWarnings("unchecked")
     protected void createChildren(NodeData node) {
+        List<CodeTypeElement> casts = new ArrayList<>(getElement().getEnclosedElements());
+        getElement().getEnclosedElements().clear();
+
         Map<NodeData, List<TypeElement>> childTypes = new LinkedHashMap<>();
         if (node.getDeclaredNodes() != null && !node.getDeclaredNodes().isEmpty()) {
             for (NodeData nodeChild : node.getDeclaredNodes()) {
@@ -432,14 +450,35 @@
         }
 
         if (node.needsFactory() || node.getNodeDeclaringChildren().size() > 0) {
-            add(new NodeFactoryFactory(context, childTypes), node);
+            NodeFactoryFactory factory = new NodeFactoryFactory(context, childTypes);
+            add(factory, node);
+            factory.getElement().getEnclosedElements().addAll(casts);
+        }
+    }
+
+    protected CodeTree createCastType(NodeData node, TypeData sourceType, TypeData targetType, boolean expect, CodeTree value) {
+        if (targetType == null) {
+            return value;
+        } else if (sourceType != null && !sourceType.needsCastTo(getContext(), targetType)) {
+            return value;
         }
+
+        CodeTreeBuilder builder = CodeTreeBuilder.createBuilder();
+        String targetMethodName;
+        if (expect) {
+            targetMethodName = TypeSystemCodeGenerator.expectTypeMethodName(targetType);
+        } else {
+            targetMethodName = TypeSystemCodeGenerator.asTypeMethodName(targetType);
+        }
+        startCallTypeSystemMethod(getContext(), builder, node, targetMethodName);
+        builder.tree(value);
+        builder.end().end();
+        return builder.getRoot();
     }
 
     private class NodeFactoryFactory extends ClassElementFactory<NodeData> {
 
         private final Map<NodeData, List<TypeElement>> childTypes;
-
         private CodeTypeElement generatedNode;
 
         public NodeFactoryFactory(ProcessorContext context, Map<NodeData, List<TypeElement>> childElements) {
@@ -853,7 +892,8 @@
                     ExecutableElement getter = (ExecutableElement) child.getAccessElement();
                     CodeExecutableElement method = CodeExecutableElement.clone(getContext().getEnvironment(), getter);
                     method.getModifiers().remove(Modifier.ABSTRACT);
-                    method.createBuilder().startReturn().string("this.").string(child.getName()).end();
+                    CodeTreeBuilder builder = method.createBuilder();
+                    builder.startReturn().string("this.").string(child.getName()).end();
                     clazz.add(method);
                 }
             }
@@ -1107,29 +1147,58 @@
                 builder.startStatement();
                 String fieldName = var.getSimpleName().toString();
 
-                CodeTree fieldInit = CodeTreeBuilder.singleString(var.getSimpleName().toString());
-                builder.string("this.").string(var.getSimpleName().toString());
-
                 NodeChildData child = node.findChild(fieldName);
-                if (child != null) {
-                    CreateCastData createCast = node.findCast(child.getName());
-                    if (createCast != null) {
-                        fieldInit = createTemplateMethodCall(builder, null, node.getGenericSpecialization(), createCast, null, child.getName());
-                    }
-                }
-
-                if (Utils.isAssignable(getContext(), var.asType(), getContext().getTruffleTypes().getNode())) {
-                    builder.string(" = adoptChild(").tree(fieldInit).string(")");
-                } else if (Utils.isAssignable(getContext(), var.asType(), getContext().getTruffleTypes().getNodeArray())) {
-                    builder.string(" = adoptChildren(").tree(fieldInit).string(")");
-                } else {
-                    builder.string(" = ").tree(fieldInit);
-                }
+
+                CodeTree init = createStaticCast(builder, child, fieldName);
+                init = createAdoptChild(builder, var.asType(), init);
+
+                builder.string("this.").string(fieldName).string(" = ").tree(init);
                 builder.end();
             }
             return method;
         }
 
+        private CodeTree createStaticCast(CodeTreeBuilder parent, NodeChildData child, String fieldName) {
+            NodeData parentNode = getModel().getNode();
+            if (child != null) {
+                CreateCastData createCast = parentNode.findCast(child.getName());
+                if (createCast != null) {
+                    return createTemplateMethodCall(parent, null, parentNode.getGenericSpecialization(), createCast, null, fieldName);
+                }
+            }
+            return CodeTreeBuilder.singleString(fieldName);
+        }
+
+        private CodeTree createAdoptChild(CodeTreeBuilder parent, TypeMirror type, CodeTree value) {
+            CodeTreeBuilder builder = new CodeTreeBuilder(parent);
+            if (Utils.isAssignable(getContext(), type, getContext().getTruffleTypes().getNode())) {
+                builder.string("adoptChild(").tree(value).string(")");
+            } else if (Utils.isAssignable(getContext(), type, getContext().getTruffleTypes().getNodeArray())) {
+                builder.string("adoptChildren(").tree(value).string(")");
+            } else {
+                builder.tree(value);
+            }
+            return builder.getRoot();
+        }
+
+        private CodeTree createCopyArray(CodeTreeBuilder parent, NodeChildData child, TypeMirror arrayType, CodeBlock<String> accessElement) {
+            CodeTreeBuilder builder = parent.create();
+            NodeData node = getModel().getNode();
+            builder.string("new ").type(arrayType).string(" {");
+            builder.startCommaGroup();
+            for (ActualParameter parameter : getModel().getParameters()) {
+                NodeChildData foundChild = node.findChild(parameter.getSpecification().getName());
+                if (foundChild == child) {
+                    builder.startGroup();
+                    builder.tree(accessElement.create(builder, String.valueOf(parameter.getIndex())));
+                    builder.end();
+                }
+            }
+            builder.end();
+            builder.end().string("}");
+            return builder.getRoot();
+        }
+
         private CodeExecutableElement createCopyConstructor(CodeTypeElement type, ExecutableElement superConstructor) {
             CodeExecutableElement method = new CodeExecutableElement(null, type.getSimpleName().toString());
             CodeTreeBuilder builder = method.createBuilder();
@@ -1141,34 +1210,22 @@
 
             for (VariableElement var : type.getFields()) {
                 builder.startStatement();
-                String varName = var.getSimpleName().toString();
-                builder.string("this.").string(varName);
-                if (Utils.isAssignable(getContext(), var.asType(), getContext().getTruffleTypes().getNode())) {
-                    builder.string(" = adoptChild(copy.").string(varName).string(")");
-                } else if (Utils.isAssignable(getContext(), var.asType(), getContext().getTruffleTypes().getNodeArray())) {
-                    NodeData node = getModel().getNode();
-                    NodeChildData child = node.findChild(varName);
-                    if (child != null) {
-                        builder.string(" = adoptChildren(");
-                        builder.string("new ").type((child.getNodeType())).string(" {");
-                        builder.startCommaGroup();
-                        for (ActualParameter parameter : getModel().getParameters()) {
-                            NodeChildData foundChild = node.findChild(parameter.getSpecification().getName());
-                            if (foundChild == child) {
-                                builder.startGroup();
-                                builder.string("copy.").string(varName).string("[").string(String.valueOf(parameter.getIndex())).string("]");
-                                builder.end();
-                            }
+                final String varName = var.getSimpleName().toString();
+                final TypeMirror varType = var.asType();
+
+                final String copyAccess = "copy." + varName;
+                CodeTree init = CodeTreeBuilder.singleString(copyAccess);
+                if (Utils.isAssignable(getContext(), var.asType(), getContext().getTruffleTypes().getNodeArray())) {
+                    NodeChildData child = getModel().getNode().findChild(varName);
+                    init = createCopyArray(builder, child, varType, new CodeBlock<String>() {
+
+                        public CodeTree create(CodeTreeBuilder parent, String index) {
+                            return CodeTreeBuilder.singleString(copyAccess + "[" + index + "]");
                         }
-
-                        builder.end().string("})");
-                    } else {
-                        builder.string(" = adoptChildren(copy.").string(varName).string(")");
-                    }
-                } else {
-                    builder.string(" = copy.").string(varName);
+                    });
                 }
-                builder.end();
+                init = createAdoptChild(builder, varType, init);
+                builder.startStatement().string("this.").string(varName).string(" = ").tree(init).end();
             }
             if (getModel().getNode().isPolymorphic()) {
                 builder.statement("this.next0 = adoptChild(copy.next0)");
@@ -1184,7 +1241,8 @@
         }
 
         private CodeVariableElement createChildField(NodeChildData child) {
-            CodeVariableElement var = new CodeVariableElement(child.getNodeType(), child.getName());
+            TypeMirror type = child.getNodeType();
+            CodeVariableElement var = new CodeVariableElement(type, child.getName());
             var.getModifiers().add(Modifier.PROTECTED);
 
             DeclaredType annotationType;
@@ -1223,7 +1281,7 @@
             }
 
             builder.startStatement().string("String message = ").startCall("createInfo0").string("reason");
-            addInternalValueParameterNames(builder, node.getGenericSpecialization(), node.getGenericSpecialization(), null, false, true);
+            addInternalValueParameterNames(builder, node.getGenericSpecialization(), node.getGenericSpecialization(), null, false, true, null);
             builder.end().end();
 
             final String currentNodeVar = currentNode;
@@ -1434,7 +1492,7 @@
                     guardsAnd = " && ";
                 }
 
-                CodeTree cast = createCast(castBuilder, child, valueParam, typeGuard.getType());
+                CodeTree cast = createCast(castBuilder, child, valueParam, typeGuard.getType(), minimumState);
                 if (cast != null) {
                     castBuilder.tree(cast);
                 }
@@ -1526,7 +1584,15 @@
                 builder.string(" || ");
             }
 
-            startCallTypeSystemMethod(getContext(), builder, node, TypeSystemCodeGenerator.isTypeMethodName(targetType));
+            String castMethodName;
+            List<TypeData> types = getModel().getNode().getTypeSystem().lookupSourceTypes(targetType);
+            if (types.size() > 1) {
+                castMethodName = TypeSystemCodeGenerator.isImplicitTypeMethodName(targetType);
+            } else {
+                castMethodName = TypeSystemCodeGenerator.isTypeMethodName(targetType);
+            }
+
+            startCallTypeSystemMethod(getContext(), builder, node, castMethodName);
             builder.string(valueName(source));
             builder.end().end(); // call
 
@@ -1539,7 +1605,7 @@
             return builder.getRoot();
         }
 
-        private CodeTree createCast(CodeTreeBuilder parent, NodeChildData field, ActualParameter source, TypeData targetType) {
+        private CodeTree createCast(CodeTreeBuilder parent, NodeChildData field, ActualParameter source, TypeData targetType, boolean minimumState) {
             NodeData node = field.getNodeData();
             TypeData sourceType = source.getTypeSystemType();
 
@@ -1554,9 +1620,24 @@
                 condition = CodeTreeBuilder.singleString(valueName(shortCircuit));
             }
 
-            CodeTree value = createCallTypeSystemMethod(context, parent, node, TypeSystemCodeGenerator.asTypeMethodName(targetType), CodeTreeBuilder.singleString(valueName(source)));
-
-            return createLazyAssignment(parent, castValueName(source), targetType.getPrimitiveType(), condition, value);
+            String castMethodName;
+            List<TypeData> types = getModel().getNode().getTypeSystem().lookupSourceTypes(targetType);
+            if (types.size() > 1) {
+                castMethodName = TypeSystemCodeGenerator.asImplicitTypeMethodName(targetType);
+            } else {
+                castMethodName = TypeSystemCodeGenerator.asTypeMethodName(targetType);
+            }
+
+            CodeTree value = createCallTypeSystemMethod(context, parent, node, castMethodName, CodeTreeBuilder.singleString(valueName(source)));
+
+            CodeTreeBuilder builder = parent.create();
+            builder.tree(createLazyAssignment(parent, castValueName(source), targetType.getPrimitiveType(), condition, value));
+            if (minimumState && types.size() > 1) {
+                CodeTree castType = createCallTypeSystemMethod(context, parent, node, TypeSystemCodeGenerator.getImplicitClass(targetType), CodeTreeBuilder.singleString(valueName(source)));
+                builder.tree(createLazyAssignment(builder, typeName(source), getContext().getType(Class.class), condition, castType));
+            }
+
+            return builder.getRoot();
         }
 
         private CodeTree createMethodGuard(CodeTreeBuilder parent, String prefix, SpecializationData source, GuardData guard) {
@@ -1595,7 +1676,6 @@
                 builder.tree(createRewriteGeneric(builder, source, current, currentNodeVar));
                 builder.end();
             } else {
-                // simple rewrite
                 if (current.getExceptions().isEmpty()) {
                     builder.tree(createGenericInvoke(builder, source, current, createReplaceCall(builder, current, currentNodeVar, currentNodeVar, null), null));
                 } else {
@@ -1669,7 +1749,7 @@
             }
             if (current.isGeneric()) {
                 builder.startReturn().tree(replace).string(".").startCall(EXECUTE_GENERIC_NAME);
-                addInternalValueParameterNames(builder, source, current, null, current.getNode().needsFrame(), true);
+                addInternalValueParameterNames(builder, source, current, null, current.getNode().needsFrame(), true, null);
                 builder.end().end();
             } else if (current.getMethod() == null) {
                 if (replaceCall != null) {
@@ -1696,7 +1776,19 @@
             } else {
                 replaceCall.startCall("replace");
             }
-            replaceCall.startGroup().startNew(className).string(source).end().end();
+            replaceCall.startGroup().startNew(className).string(source);
+            for (ActualParameter param : current.getParameters()) {
+                if (!param.getSpecification().isSignature()) {
+                    continue;
+                }
+                NodeChildData child = getModel().getNode().findChild(param.getSpecification().getName());
+                List<TypeData> types = child.getNodeData().getTypeSystem().lookupSourceTypes(param.getTypeSystemType());
+                if (types.size() > 1) {
+                    replaceCall.string(typeName(param));
+                }
+            }
+            replaceCall.end().end();
+
             if (message == null) {
                 replaceCall.string("message");
             } else {
@@ -1732,7 +1824,7 @@
 
             builder.startReturn();
             builder.startCall(currentNode + ".next0", executeCachedName(node.getGenericPolymorphicSpecialization()));
-            addInternalValueParameterNames(builder, node.getGenericSpecialization(), node.getGenericSpecialization(), null, true, true);
+            addInternalValueParameterNames(builder, node.getGenericSpecialization(), node.getGenericSpecialization(), null, true, true, null);
             builder.end();
             builder.end();
 
@@ -1776,7 +1868,7 @@
                 executeParameterNames[i] = valueName(executeParameters.get(i));
             }
 
-            builder.tree(createExecuteChildren(builder, executable, specialization, executeParameters, null, true));
+            builder.tree(createExecuteChildren(builder, executable, specialization, executeParameters, null));
 
             CodeTree primaryExecuteCall = createTemplateMethodCall(builder, null, executable, castExecutable, null, executeParameterNames);
             if (needsTry) {
@@ -1827,134 +1919,180 @@
             return createCastType(node, sourceType, castedType.getType(), hasUnexpected, value);
         }
 
-        protected CodeTree createCastType(NodeData node, TypeData sourceType, TypeData targetType, boolean expect, CodeTree value) {
-            if (targetType == null) {
-                return value;
-            } else if (!sourceType.needsCastTo(getContext(), targetType)) {
-                return value;
-            }
-
-            CodeTreeBuilder builder = CodeTreeBuilder.createBuilder();
-            String targetMethodName;
-            if (expect) {
-                targetMethodName = TypeSystemCodeGenerator.expectTypeMethodName(targetType);
-            } else {
-                targetMethodName = TypeSystemCodeGenerator.asTypeMethodName(targetType);
-            }
-            startCallTypeSystemMethod(getContext(), builder, node, targetMethodName);
-
-            builder.tree(value);
-            builder.end().end();
-            return builder.getRoot();
-        }
-
         protected CodeTree createExecuteChildren(CodeTreeBuilder parent, ExecutableTypeData sourceExecutable, SpecializationData specialization, List<ActualParameter> targetParameters,
-                        ActualParameter unexpectedParameter, boolean cast) {
-            NodeData sourceNode = specialization.getNode();
-
-            CodeTreeBuilder builder = new CodeTreeBuilder(parent);
-
+                        ActualParameter unexpectedParameter) {
+            CodeTreeBuilder builder = parent.create();
+            NodeData node = specialization.getNode();
             for (ActualParameter targetParameter : targetParameters) {
-                NodeChildData field = sourceNode.findChild(targetParameter.getSpecification().getName());
+                NodeChildData child = node.findChild(targetParameter.getSpecification().getName());
                 if (!targetParameter.getSpecification().isSignature()) {
                     continue;
                 }
-
                 TypeData targetType = targetParameter.getTypeSystemType();
                 ExecutableTypeData targetExecutable = null;
-                if (field != null) {
-                    targetExecutable = field.findExecutableType(getContext(), targetType);
+                if (child != null) {
+                    targetExecutable = child.findExecutableType(getContext(), targetType);
+                }
+
+                if (targetExecutable == null) {
+                    // TODO what to do? assertion?
+                    continue;
                 }
 
-                ActualParameter sourceParameter = sourceExecutable.findParameter(targetParameter.getLocalName());
-
-                String targetVariableName = valueName(targetParameter);
-
-                CodeTree executionExpression = null;
-                if ((sourceParameter != null && cast) || sourceParameter != null) {
-                    TypeData sourceType = sourceParameter.getTypeSystemType();
-                    if (targetExecutable == null || !sourceType.needsCastTo(getContext(), targetType)) {
-                        if (field != null && field.isShortCircuit() && sourceParameter != null) {
-                            builder.tree(createShortCircuitValue(builder, specialization, field, targetParameter.getPreviousParameter(), unexpectedParameter));
-                        }
-                        builder.startStatement();
-                        builder.type(targetParameter.getType()).string(" ");
-                        builder.string(targetVariableName).string(" = ");
-                        builder.tree(CodeTreeBuilder.singleString(valueNameEvaluated(targetParameter)));
-                        builder.end();
-                        continue;
+                CodeTree executionExpressions = createExecutionExpresssions(builder, child, sourceExecutable, targetExecutable, targetParameter, unexpectedParameter);
+
+                String targetVarName = valueName(targetParameter);
+                CodeTree unexpectedTree = createCatchUnexpectedTree(builder, executionExpressions, targetVarName, specialization, sourceExecutable, targetExecutable, targetParameter,
+                                isShortCircuit(child));
+                CodeTree shortCircuitTree = createShortCircuitTree(builder, unexpectedTree, targetVarName, specialization, targetParameter, unexpectedParameter);
+
+                if (shortCircuitTree == executionExpressions) {
+                    if (containsNewLine(executionExpressions)) {
+                        builder.declaration(sourceExecutable.getType().getPrimitiveType(), targetVarName);
+                        builder.tree(shortCircuitTree);
                     } else {
-                        CodeTree valueTree = CodeTreeBuilder.singleString(valueNameEvaluated(targetParameter));
-                        executionExpression = createExpectExecutableType(sourceNode, sourceType, targetExecutable, valueTree);
+                        builder.startStatement().type(targetParameter.getType()).string(" ").tree(shortCircuitTree).end();
                     }
-                } else if (sourceParameter == null) {
-                    executionExpression = createExecuteChildExpression(builder, field, targetParameter, unexpectedParameter);
-                }
-
-                if (executionExpression != null) {
-                    CodeTreeVariable executionVar = new CodeTreeVariable();
-                    CodeTree shortCircuitTree = createShortCircuitTree(builder, executionVar, targetVariableName, specialization, targetParameter, unexpectedParameter);
-                    CodeTree unexpectedTree = createCatchUnexpectedTree(builder, executionExpression, targetVariableName, specialization, sourceExecutable, targetExecutable, targetParameter,
-                                    shortCircuitTree != executionVar);
-
-                    executionVar.set(unexpectedTree);
+                } else {
                     builder.tree(shortCircuitTree);
                 }
+
+            }
+            return builder.getRoot();
+        }
+
+        private CodeTree createExecutionExpresssions(CodeTreeBuilder parent, NodeChildData child, ExecutableTypeData sourceExecutable, ExecutableTypeData targetExecutable, ActualParameter param,
+                        ActualParameter unexpectedParameter) {
+            CodeTreeBuilder builder = parent.create();
+
+            TypeData type = param.getTypeSystemType();
+            List<TypeData> targetTypes = child.getNodeData().getTypeSystem().lookupSourceTypes(type);
+
+            if (targetTypes.size() > 1) {
+                boolean elseIf = false;
+                int index = 0;
+                for (TypeData typeData : targetTypes) {
+                    if (index < targetTypes.size() - 1) {
+                        elseIf = builder.startIf(elseIf);
+                        builder.string(typeName(param)).string(" == ").typeLiteral(typeData.getPrimitiveType());
+                        builder.end();
+                        builder.startBlock();
+                    } else {
+                        builder.startElseBlock();
+                    }
+
+                    ExecutableTypeData implictExecutableTypeData = child.getNodeData().findExecutableType(typeData, targetExecutable.getEvaluatedCount());
+                    ImplicitCastData cast = child.getNodeData().getTypeSystem().lookupCast(typeData, param.getTypeSystemType());
+                    CodeTree execute = createExecuteExpression(parent, child, sourceExecutable, implictExecutableTypeData, param, unexpectedParameter, cast);
+                    builder.statement(execute);
+                    builder.end();
+                    index++;
+                }
+            } else {
+                builder.tree(createExecuteExpression(parent, child, sourceExecutable, targetExecutable, param, unexpectedParameter, null));
             }
             return builder.getRoot();
         }
 
+        private CodeTree createExecuteExpression(CodeTreeBuilder parent, NodeChildData child, ExecutableTypeData sourceExecutable, ExecutableTypeData targetExecutable,
+                        ActualParameter targetParameter, ActualParameter unexpectedParameter, ImplicitCastData cast) {
+            CodeTreeBuilder builder = parent.create();
+            builder.string(valueName(targetParameter));
+            builder.string(" = ");
+            if (cast != null) {
+                startCallTypeSystemMethod(getContext(), builder, child.getNodeData(), cast.getMethodName());
+            }
+
+            NodeData node = getModel().getNode();
+            ActualParameter sourceParameter = sourceExecutable.findParameter(targetParameter.getLocalName());
+            if (sourceParameter == null) {
+                builder.tree(createExecuteChildExpression(builder, child, targetParameter, targetExecutable, unexpectedParameter));
+            } else {
+                CodeTree var = CodeTreeBuilder.singleString(valueNameEvaluated(targetParameter));
+                builder.tree(createExpectExecutableType(node, sourceParameter.getTypeSystemType(), targetExecutable, var));
+            }
+            if (cast != null) {
+                builder.end().end();
+            }
+
+            return builder.getRoot();
+        }
+
+        private boolean containsNewLine(CodeTree tree) {
+            if (tree.getCodeKind() == CodeTreeKind.NEW_LINE) {
+                return true;
+            }
+
+            for (CodeTree codeTree : tree.getEnclosedElements()) {
+                if (containsNewLine(codeTree)) {
+                    return true;
+                }
+            }
+            return false;
+        }
+
+        private boolean hasUnexpected(ExecutableTypeData target, ActualParameter sourceParameter, ActualParameter targetParameter) {
+            List<TypeData> types = getModel().getNode().getTypeSystem().lookupSourceTypes(targetParameter.getTypeSystemType());
+            NodeChildData child = getModel().getNode().findChild(targetParameter.getSpecification().getName());
+            boolean hasUnexpected = false;
+            for (TypeData type : types) {
+                if (hasUnexpected) {
+                    continue;
+                }
+                ExecutableTypeData execTarget = target;
+                if (type != execTarget.getType()) {
+                    execTarget = child.findExecutableType(getContext(), type);
+                }
+                hasUnexpected = hasUnexpected || hasUnexpectedType(execTarget, sourceParameter, type);
+            }
+            return hasUnexpected;
+        }
+
+        private boolean hasUnexpectedType(ExecutableTypeData target, ActualParameter sourceParameter, TypeData type) {
+            if (sourceParameter == null) {
+                return target.hasUnexpectedValue(getContext());
+            } else {
+                if (sourceParameter.getTypeSystemType().needsCastTo(getContext(), type)) {
+                    return target.hasUnexpectedValue(getContext());
+                }
+                return false;
+            }
+        }
+
         private CodeTree createCatchUnexpectedTree(CodeTreeBuilder parent, CodeTree body, String targetVariableName, SpecializationData specialization, ExecutableTypeData currentExecutable,
                         ExecutableTypeData targetExecutable, ActualParameter param, boolean shortCircuit) {
             CodeTreeBuilder builder = new CodeTreeBuilder(parent);
-            boolean unexpected = targetExecutable.hasUnexpectedValue(getContext());
-            boolean cast = false;
-            if (targetExecutable.getType().needsCastTo(getContext(), param.getTypeSystemType())) {
-                unexpected = true;
-                cast = true;
+            ActualParameter sourceParameter = currentExecutable.findParameter(param.getLocalName());
+            boolean unexpected = hasUnexpected(targetExecutable, sourceParameter, param);
+            if (!unexpected) {
+                return body;
             }
 
-            builder.startStatement();
-
             if (!shortCircuit) {
-                builder.type(param.getType()).string(" ").string(targetVariableName);
+                builder.declaration(param.getType(), targetVariableName);
             }
-
-            if (unexpected) {
-                if (!shortCircuit) {
-                    builder.end();
-                }
-                builder.startTryBlock();
-                builder.startStatement();
-                builder.string(targetVariableName);
-            } else if (shortCircuit) {
-                builder.startStatement();
-                builder.string(targetVariableName);
-            }
-            builder.string(" = ");
-            if (cast) {
-                builder.tree(createCastType(specialization.getNode(), targetExecutable.getType(), param.getTypeSystemType(), true, body));
-            } else {
+            builder.startTryBlock();
+
+            if (containsNewLine(body)) {
                 builder.tree(body);
+            } else {
+                builder.statement(body);
             }
-            builder.end();
-
-            if (unexpected) {
-                builder.end().startCatchBlock(getUnexpectedValueException(), "ex");
-                SpecializationData generic = specialization.getNode().getGenericSpecialization();
-                ActualParameter genericParameter = generic.findParameter(param.getLocalName());
-
-                List<ActualParameter> genericParameters = generic.getParametersAfter(genericParameter);
-                builder.tree(createDeoptimize(builder));
-                builder.tree(createExecuteChildren(parent, currentExecutable, generic, genericParameters, genericParameter, false));
-                if (specialization.isPolymorphic()) {
-                    builder.tree(createReturnOptimizeTypes(builder, currentExecutable, specialization, param));
-                } else {
-                    builder.tree(createReturnExecuteAndSpecialize(builder, currentExecutable, specialization, param,
-                                    "Expected " + param.getLocalName() + " instanceof " + Utils.getSimpleName(param.getType())));
-                }
-                builder.end(); // catch block
+
+            builder.end().startCatchBlock(getUnexpectedValueException(), "ex");
+            SpecializationData generic = specialization.getNode().getGenericSpecialization();
+            ActualParameter genericParameter = generic.findParameter(param.getLocalName());
+
+            List<ActualParameter> genericParameters = generic.getParametersAfter(genericParameter);
+            builder.tree(createDeoptimize(builder));
+            builder.tree(createExecuteChildren(parent, currentExecutable, generic, genericParameters, genericParameter));
+            if (specialization.isPolymorphic()) {
+                builder.tree(createReturnOptimizeTypes(builder, currentExecutable, specialization, param));
+            } else {
+                builder.tree(createReturnExecuteAndSpecialize(builder, currentExecutable, specialization, param,
+                                "Expected " + param.getLocalName() + " instanceof " + Utils.getSimpleName(param.getType())));
             }
+            builder.end(); // catch block
 
             return builder.getRoot();
         }
@@ -1969,7 +2107,7 @@
 
             CodeTreeBuilder execute = new CodeTreeBuilder(builder);
             execute.startCall("next0", executeCachedName(generic));
-            addInternalValueParameterNames(execute, specialization, generic, param.getLocalName(), true, true);
+            addInternalValueParameterNames(execute, specialization, generic, param.getLocalName(), true, true, null);
             execute.end();
 
             TypeData sourceType = generic.getReturnType().getTypeSystemType();
@@ -1980,37 +2118,26 @@
             return builder.getRoot();
         }
 
-        private CodeTree createExecuteChildExpression(CodeTreeBuilder parent, NodeChildData targetField, ActualParameter sourceParameter, ActualParameter unexpectedParameter) {
-            TypeData type = sourceParameter.getTypeSystemType();
-            ExecutableTypeData execType = targetField.findExecutableType(getContext(), type);
-
+        private CodeTree createExecuteChildExpression(CodeTreeBuilder parent, NodeChildData targetChild, ActualParameter targetParameter, ExecutableTypeData targetExecutable,
+                        ActualParameter unexpectedParameter) {
             CodeTreeBuilder builder = new CodeTreeBuilder(parent);
-            if (targetField != null) {
-                Element accessElement = targetField.getAccessElement();
-                if (accessElement == null || accessElement.getKind() == ElementKind.METHOD) {
-                    builder.string("this.").string(targetField.getName());
-                } else if (accessElement.getKind() == ElementKind.FIELD) {
-                    builder.string("this.").string(accessElement.getSimpleName().toString());
-                } else {
-                    throw new AssertionError();
-                }
-                if (sourceParameter.getSpecification().isIndexed()) {
-                    builder.string("[" + sourceParameter.getIndex() + "]");
-                }
+            if (targetChild != null) {
+                builder.tree(createAccessChild(builder, targetChild, targetParameter));
                 builder.string(".");
             }
 
-            builder.startCall(execType.getMethodName());
-
+            builder.startCall(targetExecutable.getMethodName());
+
+            // TODO this should be merged with #createTemplateMethodCall
             int index = 0;
-            for (ActualParameter parameter : execType.getParameters()) {
+            for (ActualParameter parameter : targetExecutable.getParameters()) {
 
                 if (!parameter.getSpecification().isSignature()) {
                     builder.string(parameter.getLocalName());
                 } else {
 
-                    if (index < targetField.getExecuteWith().size()) {
-                        NodeChildData child = targetField.getExecuteWith().get(index);
+                    if (index < targetChild.getExecuteWith().size()) {
+                        NodeChildData child = targetChild.getExecuteWith().get(index);
 
                         ParameterSpec spec = getModel().getSpecification().findParameterSpec(child.getName());
                         List<ActualParameter> specializationParams = getModel().findParameters(spec);
@@ -2049,32 +2176,50 @@
             return builder.getRoot();
         }
 
+        private CodeTree createAccessChild(CodeTreeBuilder parent, NodeChildData targetChild, ActualParameter targetParameter) throws AssertionError {
+            CodeTreeBuilder builder = parent.create();
+            Element accessElement = targetChild.getAccessElement();
+            if (accessElement == null || accessElement.getKind() == ElementKind.METHOD) {
+                builder.string("this.").string(targetChild.getName());
+            } else if (accessElement.getKind() == ElementKind.FIELD) {
+                builder.string("this.").string(accessElement.getSimpleName().toString());
+            } else {
+                throw new AssertionError();
+            }
+            if (targetParameter.getSpecification().isIndexed()) {
+                builder.string("[" + targetParameter.getIndex() + "]");
+            }
+            return builder.getRoot();
+        }
+
         private CodeTree createShortCircuitTree(CodeTreeBuilder parent, CodeTree body, String targetVariableName, SpecializationData specialization, ActualParameter parameter,
                         ActualParameter exceptionParam) {
-            CodeTreeBuilder builder = new CodeTreeBuilder(parent);
-
             NodeChildData forField = specialization.getNode().findChild(parameter.getSpecification().getName());
-            if (forField == null) {
+            if (!isShortCircuit(forField)) {
                 return body;
             }
 
-            if (forField.getExecutionKind() != ExecutionKind.SHORT_CIRCUIT) {
-                return body;
-            }
-
+            CodeTreeBuilder builder = new CodeTreeBuilder(parent);
             ActualParameter shortCircuitParam = specialization.getPreviousParam(parameter);
-
             builder.tree(createShortCircuitValue(builder, specialization, forField, shortCircuitParam, exceptionParam));
-
             builder.declaration(parameter.getType(), targetVariableName, CodeTreeBuilder.createBuilder().defaultValue(parameter.getType()));
             builder.startIf().string(shortCircuitParam.getLocalName()).end();
             builder.startBlock();
-            builder.tree(body);
+
+            if (containsNewLine(body)) {
+                builder.tree(body);
+            } else {
+                builder.statement(body);
+            }
             builder.end();
 
             return builder.getRoot();
         }
 
+        private boolean isShortCircuit(NodeChildData forField) {
+            return forField != null && forField.getExecutionKind() == ExecutionKind.SHORT_CIRCUIT;
+        }
+
         private CodeTree createShortCircuitValue(CodeTreeBuilder parent, SpecializationData specialization, NodeChildData forField, ActualParameter shortCircuitParam, ActualParameter exceptionParam) {
             CodeTreeBuilder builder = new CodeTreeBuilder(parent);
             int shortCircuitIndex = 0;
@@ -2109,7 +2254,7 @@
             CodeTreeBuilder specializeCall = new CodeTreeBuilder(parent);
             specializeCall.startCall(EXECUTE_SPECIALIZE_NAME);
             specializeCall.string(String.valueOf(node.getSpecializations().indexOf(current)));
-            addInternalValueParameterNames(specializeCall, generic, node.getGenericSpecialization(), exceptionParam != null ? exceptionParam.getLocalName() : null, true, true);
+            addInternalValueParameterNames(specializeCall, generic, node.getGenericSpecialization(), exceptionParam != null ? exceptionParam.getLocalName() : null, true, true, null);
             specializeCall.doubleQuote(reason);
             specializeCall.end().end();
 
@@ -2199,6 +2344,233 @@
         }
     }
 
+    private class BaseCastNodeFactory extends ClassElementFactory<NodeData> {
+
+        protected final Set<TypeData> usedTargetTypes;
+
+        public BaseCastNodeFactory(ProcessorContext context, Set<TypeData> usedTargetTypes) {
+            super(context);
+            this.usedTargetTypes = usedTargetTypes;
+        }
+
+        @Override
+        protected CodeTypeElement create(NodeData m) {
+            CodeTypeElement type = createClass(m, modifiers(STATIC), nodeCastClassName(m, null), context.getTruffleTypes().getNode(), false);
+
+            CodeVariableElement delegate = new CodeVariableElement(m.getNodeType(), "delegate");
+            delegate.getModifiers().add(PROTECTED);
+            delegate.getAnnotationMirrors().add(new CodeAnnotationMirror(getContext().getTruffleTypes().getChildAnnotation()));
+
+            type.add(delegate);
+            type.add(createConstructorUsingFields(modifiers(), type));
+            return type;
+        }
+
+        @Override
+        protected void createChildren(NodeData m) {
+            CodeTypeElement type = getElement();
+            type.add(createExecute(EXECUTE_SPECIALIZE_NAME, true));
+            type.add(createExecute(EXECUTE_GENERIC_NAME, false));
+
+            for (ExecutableTypeData targetExecutable : m.getExecutableTypes()) {
+                if (!usedTargetTypes.contains(targetExecutable.getType()) && targetExecutable.hasUnexpectedValue(getContext())) {
+                    continue;
+                }
+                CodeExecutableElement execute = createCastExecute(targetExecutable, targetExecutable, false);
+                CodeExecutableElement expect = createCastExecute(targetExecutable, targetExecutable, true);
+                if (execute != null) {
+                    getElement().add(execute);
+                }
+                if (expect != null) {
+                    getElement().add(expect);
+                }
+            }
+            Set<TypeData> sourceTypes = new TreeSet<>();
+            List<ImplicitCastData> casts = getModel().getTypeSystem().getImplicitCasts();
+            for (ImplicitCastData cast : casts) {
+                sourceTypes.add(cast.getSourceType());
+            }
+
+            CodeTypeElement baseType = getElement();
+            for (TypeData sourceType : sourceTypes) {
+                add(new SpecializedCastNodeFactory(context, baseType, sourceType, usedTargetTypes), getModel());
+            }
+        }
+
+        private CodeExecutableElement createExecute(String name, boolean specialize) {
+            NodeData node = getModel();
+            TypeMirror objectType = node.getTypeSystem().getGenericType();
+            CodeExecutableElement method = new CodeExecutableElement(modifiers(PUBLIC), objectType, name, new CodeVariableElement(objectType, "value"));
+            if (specialize) {
+                method.getModifiers().add(FINAL);
+            }
+            CodeTreeBuilder builder = method.createBuilder();
+
+            List<ImplicitCastData> casts = node.getTypeSystem().getImplicitCasts();
+            boolean elseIf = false;
+            for (ImplicitCastData cast : casts) {
+                elseIf = builder.startIf(elseIf);
+                startCallTypeSystemMethod(context, builder, getModel(), TypeSystemCodeGenerator.isTypeMethodName(cast.getSourceType()));
+                builder.string("value");
+                builder.end().end();
+                builder.end();
+                builder.startBlock();
+
+                if (specialize) {
+                    builder.startStatement().startCall("replace").startNew(nodeCastClassName(getModel(), cast.getSourceType())).string("delegate").end().doubleQuote("Added cast").end().end();
+                }
+                builder.startReturn();
+
+                startCallTypeSystemMethod(context, builder, getModel(), cast.getMethodName());
+                startCallTypeSystemMethod(context, builder, getModel(), TypeSystemCodeGenerator.asTypeMethodName(cast.getSourceType()));
+                builder.string("value");
+                builder.end().end();
+                builder.end().end();
+
+                builder.end();
+                builder.end();
+            }
+
+            builder.startReturn().string("value").end();
+
+            return method;
+        }
+
+        protected CodeExecutableElement createCastExecute(ExecutableTypeData sourceExecutable, ExecutableTypeData targetExecutable, boolean expect) {
+            ImplicitCastData cast = null;
+            if (!sourceExecutable.getType().equals(targetExecutable.getType())) {
+                cast = getModel().getTypeSystem().lookupCast(sourceExecutable.getType(), targetExecutable.getType());
+                if (cast == null) {
+                    return null;
+                }
+            }
+
+            if (expect) {
+                if (targetExecutable.getEvaluatedCount() > 0) {
+                    return null;
+                } else if (Utils.isObject(targetExecutable.getType().getPrimitiveType())) {
+                    return null;
+                }
+            }
+
+            boolean hasTargetUnexpected = targetExecutable.hasUnexpectedValue(getContext());
+            boolean hasSourceUnexpected = sourceExecutable.hasUnexpectedValue(getContext());
+
+            CodeExecutableElement method = copyTemplateMethod(targetExecutable);
+            method.getModifiers().add(PUBLIC);
+
+            CodeTreeBuilder builder = method.createBuilder();
+
+            if (hasSourceUnexpected && cast != null) {
+                builder.startTryBlock();
+            }
+
+            if (expect) {
+                method.getParameters().clear();
+                String expectMethodName;
+                if (hasTargetUnexpected) {
+                    expectMethodName = TypeSystemCodeGenerator.expectTypeMethodName(targetExecutable.getType());
+                } else {
+                    expectMethodName = TypeSystemCodeGenerator.asTypeMethodName(targetExecutable.getType());
+                }
+                method.setSimpleName(CodeNames.of(expectMethodName));
+                method.addParameter(new CodeVariableElement(getModel().getTypeSystem().getGenericType(), "value"));
+            }
+
+            builder.startReturn();
+            CodeTree executeCall;
+            if (expect) {
+                executeCall = createCastType(getModel(), getModel().getTypeSystem().getGenericTypeData(), sourceExecutable.getType(), hasSourceUnexpected, CodeTreeBuilder.singleString("value"));
+            } else {
+                executeCall = createTemplateMethodCall(builder, CodeTreeBuilder.singleString("delegate."), targetExecutable, sourceExecutable, null);
+            }
+            if (cast != null) {
+                startCallTypeSystemMethod(context, builder, getModel(), cast.getMethodName());
+                builder.tree(executeCall);
+                builder.end().end();
+            } else {
+                builder.tree(executeCall);
+            }
+            builder.end();
+
+            if (hasSourceUnexpected && cast != null) {
+                builder.end();
+                builder.startCatchBlock(getContext().getTruffleTypes().getUnexpectedValueException(), "ex");
+                builder.startStatement().startCall("replace").startNew(nodeCastClassName(getModel(), null)).string("delegate").end().doubleQuote("Removed cast").end().end();
+
+                if (hasTargetUnexpected) {
+                    builder.startThrow().string("ex").end();
+                } else {
+                    builder.startThrow().startNew(getContext().getType(AssertionError.class)).end().end();
+                }
+                builder.end();
+            }
+
+            return method;
+        }
+
+        private CodeExecutableElement copyTemplateMethod(TemplateMethod targetExecutable) {
+            CodeExecutableElement method = CodeExecutableElement.clone(getContext().getEnvironment(), targetExecutable.getMethod());
+            method.getModifiers().remove(ABSTRACT);
+            method.getAnnotationMirrors().clear();
+            Modifier visibility = Utils.getVisibility(method.getModifiers());
+            if (visibility != null) {
+                method.getModifiers().remove(visibility);
+            }
+            int index = 0;
+            for (ActualParameter parameter : targetExecutable.getParameters()) {
+                ((CodeVariableElement) method.getParameters().get(index)).setName(parameter.getLocalName());
+                index++;
+            }
+            return method;
+        }
+
+    }
+
+    private class SpecializedCastNodeFactory extends BaseCastNodeFactory {
+
+        private final CodeTypeElement baseType;
+        private final TypeData sourceType;
+
+        public SpecializedCastNodeFactory(ProcessorContext context, CodeTypeElement baseType, TypeData type, Set<TypeData> usedTargetTypes) {
+            super(context, usedTargetTypes);
+            this.baseType = baseType;
+            this.sourceType = type;
+        }
+
+        @Override
+        protected CodeTypeElement create(NodeData m) {
+            CodeTypeElement type = createClass(m, modifiers(PRIVATE, STATIC, FINAL), nodeCastClassName(m, sourceType), baseType.asType(), false);
+            type.add(createConstructorUsingFields(modifiers(), type));
+            return type;
+        }
+
+        @Override
+        protected void createChildren(NodeData node) {
+            for (TypeData targetType : usedTargetTypes) {
+                for (ExecutableTypeData targetExecutable : node.getExecutableTypes()) {
+                    if (targetExecutable.getType().equals(targetType)) {
+                        ExecutableTypeData sourceExecutable = node.findExecutableType(sourceType, targetExecutable.getEvaluatedCount());
+                        if (sourceExecutable == null) {
+                            // TODO what if there is no evaluated version?
+                            continue;
+                        }
+                        CodeExecutableElement execute = createCastExecute(sourceExecutable, targetExecutable, false);
+                        CodeExecutableElement expect = createCastExecute(sourceExecutable, targetExecutable, true);
+                        if (execute != null) {
+                            getElement().add(execute);
+                        }
+                        if (expect != null) {
+                            getElement().add(expect);
+                        }
+                    }
+                }
+
+            }
+        }
+
+    }
+
     private class SpecializedNodeFactory extends NodeBaseFactory {
 
         protected final CodeTypeElement nodeGen;
@@ -2277,15 +2649,31 @@
                 }
 
                 CodeExecutableElement superConstructor = createSuperConstructor(clazz, constructor);
+                CodeTree body = superConstructor.getBodyTree();
+                CodeTreeBuilder builder = superConstructor.createBuilder();
+                builder.tree(body);
 
                 if (superConstructor != null) {
                     if (getModel().isGeneric() && node.isPolymorphic()) {
-                        CodeTree body = superConstructor.getBodyTree();
-                        CodeTreeBuilder builder = superConstructor.createBuilder();
-                        builder.tree(body);
                         builder.statement("this.next0 = null");
                     }
 
+                    for (ActualParameter param : getModel().getParameters()) {
+                        if (!param.getSpecification().isSignature()) {
+                            continue;
+                        }
+                        NodeChildData child = getModel().getNode().findChild(param.getSpecification().getName());
+                        List<TypeData> types = child.getNodeData().getTypeSystem().lookupSourceTypes(param.getTypeSystemType());
+                        if (types.size() > 1) {
+                            clazz.add(new CodeVariableElement(modifiers(PRIVATE, FINAL), getContext().getType(Class.class), typeName(param)));
+                            superConstructor.getParameters().add(new CodeVariableElement(getContext().getType(Class.class), typeName(param)));
+
+                            builder.startStatement();
+                            builder.string("this.").string(typeName(param)).string(" = ").string(typeName(param));
+                            builder.end();
+                        }
+                    }
+
                     clazz.add(superConstructor);
                 }
             }
@@ -2333,7 +2721,7 @@
                 } else {
                     CodeTreeBuilder elseBuilder = new CodeTreeBuilder(builder);
                     elseBuilder.startReturn().startCall("this.next0", executeCachedName(polymorphic));
-                    addInternalValueParameterNames(elseBuilder, polymorphic, polymorphic, null, true, true);
+                    addInternalValueParameterNames(elseBuilder, polymorphic, polymorphic, null, true, true, null);
                     elseBuilder.end().end();
 
                     boolean forceElse = specialization.getExceptions().size() > 0;
@@ -2375,7 +2763,7 @@
             CodeTreeBuilder specializeCall = new CodeTreeBuilder(builder);
             specializeCall.startCall(EXECUTE_SPECIALIZE_NAME);
             specializeCall.string("0");
-            addInternalValueParameterNames(specializeCall, specialization, node.getGenericSpecialization(), null, true, true);
+            addInternalValueParameterNames(specializeCall, specialization, node.getGenericSpecialization(), null, true, true, null);
             specializeCall.startGroup().doubleQuote("Uninitialized polymorphic (").string(" + depth + ").doubleQuote("/" + node.getPolymorphicDepth() + ")").end();
             specializeCall.end().end();
 
@@ -2482,7 +2870,7 @@
                 builder.tree(createDeoptimize(builder));
             }
 
-            builder.tree(createExecuteChildren(builder, executable, specialization, specialization.getParameters(), null, false));
+            builder.tree(createExecuteChildren(builder, executable, specialization, specialization.getParameters(), null));
 
             CodeTree returnSpecialized = null;
 
@@ -2519,26 +2907,25 @@
             CodeTreeBuilder returnBuilder = new CodeTreeBuilder(parent);
             if (specialization.isPolymorphic()) {
                 returnBuilder.startCall("next0", executeCachedName(specialization));
-                addInternalValueParameterNames(returnBuilder, specialization, specialization, null, true, true);
+                addInternalValueParameterNames(returnBuilder, specialization, specialization, null, true, true, null);
                 returnBuilder.end();
             } else if (specialization.isUninitialized()) {
                 returnBuilder.startCall("super", EXECUTE_SPECIALIZE_NAME);
                 returnBuilder.string("0");
-                addInternalValueParameterNames(returnBuilder, specialization, specialization, null, true, true);
+                addInternalValueParameterNames(returnBuilder, specialization, specialization, null, true, true, null);
                 returnBuilder.doubleQuote("Uninitialized monomorphic");
                 returnBuilder.end();
             } else if (specialization.getMethod() == null && !node.needsRewrites(context)) {
                 emitEncounteredSynthetic(builder, specialization);
             } else if (specialization.isGeneric()) {
                 returnBuilder.startCall("super", EXECUTE_GENERIC_NAME);
-                addInternalValueParameterNames(returnBuilder, specialization, specialization, null, node.needsFrame(), true);
+                addInternalValueParameterNames(returnBuilder, specialization, specialization, null, node.needsFrame(), true, null);
                 returnBuilder.end();
             } else {
                 returnBuilder.tree(createTemplateMethodCall(returnBuilder, null, specialization, specialization, null));
             }
 
             if (!returnBuilder.isEmpty()) {
-
                 ExecutableTypeData sourceExecutableType = node.findExecutableType(specialization.getReturnType().getTypeSystemType(), 0);
                 boolean sourceThrowsUnexpected = sourceExecutableType != null && sourceExecutableType.hasUnexpectedValue(getContext());
                 boolean targetSupportsUnexpected = executable.hasUnexpectedValue(getContext());
--- a/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/node/NodeData.java	Fri Sep 06 16:11:15 2013 +0200
+++ b/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/node/NodeData.java	Fri Sep 06 16:16:40 2013 +0200
@@ -32,7 +32,7 @@
 import com.oracle.truffle.dsl.processor.template.*;
 import com.oracle.truffle.dsl.processor.typesystem.*;
 
-public class NodeData extends Template {
+public class NodeData extends Template implements Comparable<NodeData> {
 
     private final String nodeId;
     private NodeData declaringNode;
@@ -90,6 +90,15 @@
         return false;
     }
 
+    public boolean needsImplicitCast(ProcessorContext context) {
+        for (NodeChildData child : getChildren()) {
+            if (child.needsImplicitCast(context)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
     public int getPolymorphicDepth() {
         return polymorphicDepth;
     }
@@ -574,4 +583,7 @@
         return null;
     }
 
+    public int compareTo(NodeData o) {
+        return getNodeId().compareTo(o.getNodeId());
+    }
 }
--- a/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/node/NodeParser.java	Fri Sep 06 16:11:15 2013 +0200
+++ b/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/node/NodeParser.java	Fri Sep 06 16:16:40 2013 +0200
@@ -332,7 +332,7 @@
         nodeData.setNodeContainer(nodeContainer != null);
         nodeData.setTypeSystem(typeSystem);
         nodeData.setFields(parseFields(typeHierarchy, elements));
-        nodeData.setChildren(parseChildren(elements, typeHierarchy));
+        nodeData.setChildren(parseChildren(nodeData, elements, typeHierarchy));
         nodeData.setExecutableTypes(groupExecutableTypes(new ExecutableTypeMethodParser(context, nodeData).parse(elements)));
 
         // resolveChildren invokes cyclic parsing.
@@ -407,7 +407,7 @@
         }
     }
 
-    private List<NodeChildData> parseChildren(List<? extends Element> elements, final List<TypeElement> typeHierarchy) {
+    private List<NodeChildData> parseChildren(NodeData parent, List<? extends Element> elements, final List<TypeElement> typeHierarchy) {
         Set<String> shortCircuits = new HashSet<>();
         for (ExecutableElement method : ElementFilter.methodsIn(elements)) {
             AnnotationMirror mirror = Utils.findAnnotationMirror(processingEnv, method, ShortCircuit.class);
@@ -472,7 +472,7 @@
                     kind = ExecutionKind.SHORT_CIRCUIT;
                 }
 
-                NodeChildData nodeChild = new NodeChildData(type, childMirror, name, childType, originalChildType, getter, cardinality, kind);
+                NodeChildData nodeChild = new NodeChildData(parent, type, childMirror, name, childType, originalChildType, getter, cardinality, kind);
 
                 parsedChildren.add(nodeChild);
 
--- a/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/typesystem/ImplicitCastData.java	Fri Sep 06 16:11:15 2013 +0200
+++ b/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/typesystem/ImplicitCastData.java	Fri Sep 06 16:16:40 2013 +0200
@@ -26,8 +26,32 @@
 
 public class ImplicitCastData extends TemplateMethod {
 
-    public ImplicitCastData(TemplateMethod method) {
+    private final TypeData sourceType;
+    private final TypeData targetType;
+
+    public ImplicitCastData(TemplateMethod method, TypeData sourceType, TypeData targetType) {
         super(method);
+        this.sourceType = sourceType;
+        this.targetType = targetType;
+    }
+
+    public TypeData getSourceType() {
+        return sourceType;
+    }
+
+    public TypeData getTargetType() {
+        return targetType;
+    }
+
+    @Override
+    public int compareTo(TemplateMethod o) {
+        if (o instanceof ImplicitCastData) {
+            // implicit casts are ordered by source type since
+            // its also the order in which they are checked.
+            TypeData otherSourceType = ((ImplicitCastData) o).getSourceType();
+            return this.sourceType.compareTo(otherSourceType);
+        }
+        return super.compareTo(o);
     }
 
 }
--- a/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/typesystem/ImplicitCastParser.java	Fri Sep 06 16:11:15 2013 +0200
+++ b/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/typesystem/ImplicitCastParser.java	Fri Sep 06 16:16:40 2013 +0200
@@ -56,7 +56,20 @@
 
     @Override
     public ImplicitCastData create(TemplateMethod method, boolean invalid) {
-        return new ImplicitCastData(method);
+        if (invalid) {
+            return new ImplicitCastData(method, null, null);
+        }
+
+        ActualParameter target = method.findParameter("targetValue");
+        ActualParameter source = method.findParameter("sourceValue");
+
+        TypeData targetType = target.getTypeSystemType();
+        TypeData sourceType = source.getTypeSystemType();
+
+        if (targetType.equals(sourceType)) {
+            method.addError("Target type and source type of an @%s must not be the same type.", ImplicitCast.class.getSimpleName());
+        }
+
+        return new ImplicitCastData(method, sourceType, targetType);
     }
-
 }
--- a/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/typesystem/TypeSystemCodeGenerator.java	Fri Sep 06 16:11:15 2013 +0200
+++ b/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/typesystem/TypeSystemCodeGenerator.java	Fri Sep 06 16:16:40 2013 +0200
@@ -44,10 +44,22 @@
         return "is" + Utils.getTypeId(type.getBoxedType());
     }
 
+    public static String isImplicitTypeMethodName(TypeData type) {
+        return "isImplicit" + Utils.getTypeId(type.getBoxedType());
+    }
+
     public static String asTypeMethodName(TypeData type) {
         return "as" + Utils.getTypeId(type.getBoxedType());
     }
 
+    public static String asImplicitTypeMethodName(TypeData type) {
+        return "asImplicit" + Utils.getTypeId(type.getBoxedType());
+    }
+
+    public static String getImplicitClass(TypeData type) {
+        return "getImplicit" + Utils.getTypeId(type.getBoxedType()) + "Class";
+    }
+
     public static String expectTypeMethodName(TypeData type) {
         return "expect" + Utils.getTypeId(type.getBoxedType());
     }
@@ -100,6 +112,20 @@
                             clazz.add(expect);
                         }
                     }
+
+                    CodeExecutableElement asImplicit = createAsImplicitTypeMethod(type);
+                    if (asImplicit != null) {
+                        clazz.add(asImplicit);
+                    }
+                    CodeExecutableElement isImplicit = createIsImplicitTypeMethod(type);
+                    if (isImplicit != null) {
+                        clazz.add(isImplicit);
+                    }
+
+                    CodeExecutableElement typeIndex = createGetTypeIndex(type);
+                    if (typeIndex != null) {
+                        clazz.add(typeIndex);
+                    }
                 }
             }
 
@@ -133,6 +159,95 @@
             return field;
         }
 
+        private CodeExecutableElement createIsImplicitTypeMethod(TypeData type) {
+            TypeSystemData typeSystem = getModel();
+            List<ImplicitCastData> casts = typeSystem.lookupByTargetType(type);
+            if (casts.isEmpty()) {
+                return null;
+            }
+            CodeExecutableElement method = new CodeExecutableElement(modifiers(PUBLIC), getContext().getType(boolean.class), TypeSystemCodeGenerator.isImplicitTypeMethodName(type));
+            method.addParameter(new CodeVariableElement(getContext().getType(Object.class), LOCAL_VALUE));
+            CodeTreeBuilder builder = method.createBuilder();
+
+            List<TypeData> sourceTypes = typeSystem.lookupSourceTypes(type);
+
+            builder.startReturn();
+            String sep = "";
+            for (TypeData sourceType : sourceTypes) {
+                builder.string(sep);
+                builder.startCall(isTypeMethodName(sourceType)).string(LOCAL_VALUE).end();
+                sep = " || ";
+            }
+            builder.end();
+            return method;
+        }
+
+        private CodeExecutableElement createAsImplicitTypeMethod(TypeData type) {
+            TypeSystemData typeSystem = getModel();
+            List<ImplicitCastData> casts = typeSystem.lookupByTargetType(type);
+            if (casts.isEmpty()) {
+                return null;
+            }
+            CodeExecutableElement method = new CodeExecutableElement(modifiers(PUBLIC), type.getPrimitiveType(), TypeSystemCodeGenerator.asImplicitTypeMethodName(type));
+            method.addParameter(new CodeVariableElement(getContext().getType(Object.class), LOCAL_VALUE));
+
+            List<TypeData> sourceTypes = typeSystem.lookupSourceTypes(type);
+
+            CodeTreeBuilder builder = method.createBuilder();
+            boolean elseIf = false;
+            for (TypeData sourceType : sourceTypes) {
+                elseIf = builder.startIf(elseIf);
+                builder.startCall(isTypeMethodName(sourceType)).string(LOCAL_VALUE).end();
+                builder.end().startBlock();
+
+                builder.startReturn();
+                ImplicitCastData cast = typeSystem.lookupCast(sourceType, type);
+                if (cast != null) {
+                    builder.startCall(cast.getMethodName());
+                }
+                builder.startCall(asTypeMethodName(sourceType)).string(LOCAL_VALUE).end();
+                if (cast != null) {
+                    builder.end();
+                }
+                builder.end();
+                builder.end();
+            }
+
+            builder.startElseBlock();
+            builder.startStatement().startStaticCall(getContext().getTruffleTypes().getCompilerDirectives(), "transferToInterpreter").end().end();
+            builder.startThrow().startNew(getContext().getType(IllegalArgumentException.class)).doubleQuote("Illegal type ").end().end();
+            builder.end();
+            return method;
+        }
+
+        private CodeExecutableElement createGetTypeIndex(TypeData type) {
+            TypeSystemData typeSystem = getModel();
+            List<ImplicitCastData> casts = typeSystem.lookupByTargetType(type);
+            if (casts.isEmpty()) {
+                return null;
+            }
+            CodeExecutableElement method = new CodeExecutableElement(modifiers(PUBLIC), getContext().getType(Class.class), TypeSystemCodeGenerator.getImplicitClass(type));
+            method.addParameter(new CodeVariableElement(getContext().getType(Object.class), LOCAL_VALUE));
+
+            List<TypeData> sourceTypes = typeSystem.lookupSourceTypes(type);
+            CodeTreeBuilder builder = method.createBuilder();
+            boolean elseIf = false;
+            for (TypeData sourceType : sourceTypes) {
+                elseIf = builder.startIf(elseIf);
+                builder.startCall(isTypeMethodName(sourceType)).string(LOCAL_VALUE).end();
+                builder.end().startBlock();
+                builder.startReturn().typeLiteral(sourceType.getPrimitiveType()).end();
+                builder.end();
+            }
+
+            builder.startElseBlock();
+            builder.startStatement().startStaticCall(getContext().getTruffleTypes().getCompilerDirectives(), "transferToInterpreter").end().end();
+            builder.startThrow().startNew(getContext().getType(IllegalArgumentException.class)).doubleQuote("Illegal type ").end().end();
+            builder.end();
+
+            return method;
+        }
+
         private CodeExecutableElement createIsTypeMethod(TypeData type) {
             if (!type.getTypeChecks().isEmpty()) {
                 return null;
@@ -174,8 +289,8 @@
             method.addThrownType(getContext().getTruffleTypes().getUnexpectedValueException());
 
             CodeTreeBuilder body = method.createBuilder();
-            body.startIf().startCall(null, TypeSystemCodeGenerator.isTypeMethodName(expectedType)).string(LOCAL_VALUE).end().end().startBlock();
-            body.startReturn().startCall(null, TypeSystemCodeGenerator.asTypeMethodName(expectedType)).string(LOCAL_VALUE).end().end();
+            body.startIf().startCall(TypeSystemCodeGenerator.isTypeMethodName(expectedType)).string(LOCAL_VALUE).end().end().startBlock();
+            body.startReturn().startCall(TypeSystemCodeGenerator.asTypeMethodName(expectedType)).string(LOCAL_VALUE).end().end();
             body.end(); // if-block
             body.startThrow().startNew(getContext().getTruffleTypes().getUnexpectedValueException()).string(LOCAL_VALUE).end().end();
 
--- a/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/typesystem/TypeSystemData.java	Fri Sep 06 16:11:15 2013 +0200
+++ b/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/typesystem/TypeSystemData.java	Fri Sep 06 16:16:40 2013 +0200
@@ -36,6 +36,7 @@
     private List<TypeMirror> primitiveTypeMirrors = new ArrayList<>();
     private List<TypeMirror> boxedTypeMirrors = new ArrayList<>();
 
+    private List<ImplicitCastData> implicitCasts;
     private List<TypeCastData> casts;
     private List<TypeCheckData> checks;
 
@@ -61,6 +62,14 @@
         }
     }
 
+    public void setImplicitCasts(List<ImplicitCastData> implicitCasts) {
+        this.implicitCasts = implicitCasts;
+    }
+
+    public List<ImplicitCastData> getImplicitCasts() {
+        return implicitCasts;
+    }
+
     public void setCasts(List<TypeCastData> casts) {
         this.casts = casts;
     }
@@ -89,6 +98,9 @@
         if (casts != null) {
             sinks.addAll(casts);
         }
+        if (implicitCasts != null) {
+            sinks.addAll(implicitCasts);
+        }
         return sinks;
     }
 
@@ -161,4 +173,55 @@
         return getClass().getSimpleName() + "[template = " + Utils.getSimpleName(getTemplateType()) + ", types = " + types + "]";
     }
 
+    public Set<TypeData> lookupCastSourceTypes() {
+        if (getImplicitCasts() == null) {
+            return null;
+        }
+
+        Set<TypeData> sourceTypes = new TreeSet<>();
+        for (ImplicitCastData cast : getImplicitCasts()) {
+            sourceTypes.add(cast.getSourceType());
+        }
+        return sourceTypes;
+    }
+
+    public List<ImplicitCastData> lookupByTargetType(TypeData targetType) {
+        if (getImplicitCasts() == null) {
+            return Collections.emptyList();
+        }
+        List<ImplicitCastData> foundCasts = new ArrayList<>();
+        for (ImplicitCastData cast : getImplicitCasts()) {
+            if (cast.getTargetType().equals(targetType)) {
+                foundCasts.add(cast);
+            }
+        }
+        return foundCasts;
+    }
+
+    public ImplicitCastData lookupCast(TypeData sourceType, TypeData targetType) {
+        if (getImplicitCasts() == null) {
+            return null;
+        }
+        for (ImplicitCastData cast : getImplicitCasts()) {
+            if (cast.getSourceType().equals(sourceType) && cast.getTargetType().equals(targetType)) {
+                return cast;
+            }
+        }
+        return null;
+    }
+
+    public List<TypeData> lookupSourceTypes(TypeData type) {
+        List<TypeData> sourceTypes = new ArrayList<>();
+        sourceTypes.add(type);
+        if (getImplicitCasts() != null) {
+            for (ImplicitCastData cast : getImplicitCasts()) {
+                if (cast.getTargetType() == type) {
+                    sourceTypes.add(cast.getSourceType());
+                }
+            }
+        }
+        Collections.sort(sourceTypes);
+        return sourceTypes;
+    }
+
 }
--- a/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/typesystem/TypeSystemParser.java	Fri Sep 06 16:11:15 2013 +0200
+++ b/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/typesystem/TypeSystemParser.java	Fri Sep 06 16:16:40 2013 +0200
@@ -71,7 +71,7 @@
         }
 
         typeSystem.setTypes(parseTypes(typeSystem));
-        if (typeSystem.getTypes() == null) {
+        if (typeSystem.hasErrors()) {
             return typeSystem;
         }
 
@@ -91,6 +91,8 @@
         if (casts == null || checks == null || implicitCasts == null) {
             return typeSystem;
         }
+
+        typeSystem.setImplicitCasts(implicitCasts);
         typeSystem.setCasts(casts);
         typeSystem.setChecks(checks);
 
@@ -106,6 +108,7 @@
             cast.getTargetType().addTypeCast(cast);
         }
 
+        verifyImplicitCasts(typeSystem);
         verifyGenericTypeChecksAndCasts(typeSystem);
         verifyMethodSignatures(typeSystem);
         verifyNamesUnique(typeSystem);
@@ -113,6 +116,24 @@
         return typeSystem;
     }
 
+    private static void verifyImplicitCasts(TypeSystemData typeSystem) {
+        Set<TypeData> types = new HashSet<>();
+        Set<TypeData> duplicateSourceTypes = new HashSet<>();
+        for (ImplicitCastData cast : typeSystem.getImplicitCasts()) {
+            if (types.contains(cast.getSourceType())) {
+                duplicateSourceTypes.add(cast.getSourceType());
+            }
+            types.add(cast.getSourceType());
+        }
+        for (TypeData duplicateType : duplicateSourceTypes) {
+            for (ImplicitCastData cast : typeSystem.getImplicitCasts()) {
+                if (cast.getSourceType().equals(duplicateType)) {
+                    cast.addError("Duplicate cast source type %s.", Utils.getSimpleName(duplicateType.getPrimitiveType()), ImplicitCast.class.getSimpleName());
+                }
+            }
+        }
+    }
+
     private static void verifyGenericTypeChecksAndCasts(TypeSystemData typeSystem) {
         for (TypeData type : typeSystem.getTypes()) {
             if (!type.getTypeChecks().isEmpty()) {