# HG changeset patch # User Christian Humer # Date 1377796740 -7200 # Node ID 43eab069ca9b7f2a849d812fbc0668694804c4d3 # Parent 4830676526e3dcd3fe76b2e6bd53a8ecccdcd25c Truffle-DSL: improved error recovery of type systems and improved error testability infrastructure. diff -r 4830676526e3 -r 43eab069ca9b graal/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/CompilerErrorTest.java --- a/graal/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/CompilerErrorTest.java Thu Aug 29 17:39:55 2013 +0200 +++ b/graal/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/CompilerErrorTest.java Thu Aug 29 19:19:00 2013 +0200 @@ -38,7 +38,7 @@ } - @ExpectError("Nodes containing the @Specialization annotation cannot be private.") + @ExpectError("Classes containing a @Specialization annotation must not be private.") private abstract static class Visiblity02 extends ValueNode { @Specialization diff -r 4830676526e3 -r 43eab069ca9b graal/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/TypeSystemErrorsTest.java --- /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/TypeSystemErrorsTest.java Thu Aug 29 19:19:00 2013 +0200 @@ -0,0 +1,62 @@ +/* + * 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 com.oracle.truffle.api.dsl.*; + +public class TypeSystemErrorsTest { + + @TypeSystem({int.class, boolean.class}) + static class Types0 { + + } + + @ExpectError("Invalid type order. The type(s) [java.lang.String] are inherited from a earlier defined type java.lang.CharSequence.") + @TypeSystem({CharSequence.class, String.class}) + static class Types1 { + + } + + @TypeSystem({int.class, boolean.class}) + static class Types2 { + + @TypeCast + @ExpectError("The provided return type \"String\" does not match expected return type \"int\".%") + String asInteger(Object value) { + return (String) value; + } + + } + + @TypeSystem({int.class, boolean.class}) + static class Types3 { + + @TypeCast + @ExpectError("The provided return type \"boolean\" does not match expected return type \"int\".%") + boolean asInteger(Object value) { + return (boolean) value; + } + + } + +} diff -r 4830676526e3 -r 43eab069ca9b graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/Utils.java --- a/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/Utils.java Thu Aug 29 17:39:55 2013 +0200 +++ b/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/Utils.java Thu Aug 29 19:19:00 2013 +0200 @@ -423,6 +423,8 @@ return getSimpleName(mirror); case ERROR: throw new CompileErrorException("Type error " + mirror); + case EXECUTABLE: + return ((ExecutableType) mirror).toString(); case NONE: return "$none"; default: diff -r 4830676526e3 -r 43eab069ca9b graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/node/CreateCastParser.java --- a/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/node/CreateCastParser.java Thu Aug 29 17:39:55 2013 +0200 +++ b/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/node/CreateCastParser.java Thu Aug 29 19:19:00 2013 +0200 @@ -61,7 +61,7 @@ } @Override - public CreateCastData create(TemplateMethod method) { + public CreateCastData create(TemplateMethod method, boolean invalid) { AnnotationMirror mirror = method.getMarkerAnnotation(); List childNames = Utils.getAnnotationValueList(String.class, mirror, "value"); CreateCastData cast = new CreateCastData(method, childNames); diff -r 4830676526e3 -r 43eab069ca9b graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/node/ExecutableTypeMethodParser.java --- a/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/node/ExecutableTypeMethodParser.java Thu Aug 29 17:39:55 2013 +0200 +++ b/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/node/ExecutableTypeMethodParser.java Thu Aug 29 19:19:00 2013 +0200 @@ -83,7 +83,7 @@ } @Override - public ExecutableTypeData create(TemplateMethod method) { + public ExecutableTypeData create(TemplateMethod method, boolean invalid) { TypeData resolvedType = method.getReturnType().getTypeSystemType(); return new ExecutableTypeData(method, method.getMethod(), getNode().getTypeSystem(), resolvedType); } diff -r 4830676526e3 -r 43eab069ca9b graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/node/GenericParser.java --- a/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/node/GenericParser.java Thu Aug 29 17:39:55 2013 +0200 +++ b/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/node/GenericParser.java Thu Aug 29 19:19:00 2013 +0200 @@ -61,7 +61,7 @@ } @Override - public SpecializationData create(TemplateMethod method) { + public SpecializationData create(TemplateMethod method, boolean invalid) { SpecializationData data = new SpecializationData(method, true, false, false); return data; } diff -r 4830676526e3 -r 43eab069ca9b graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/node/NodeParser.java --- a/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/node/NodeParser.java Thu Aug 29 17:39:55 2013 +0200 +++ b/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/node/NodeParser.java Thu Aug 29 19:19:00 2013 +0200 @@ -30,8 +30,6 @@ import javax.lang.model.util.*; import javax.tools.Diagnostic.Kind; -import org.omg.CORBA.*; - import com.oracle.truffle.api.dsl.*; import com.oracle.truffle.api.nodes.*; import com.oracle.truffle.dsl.processor.*; @@ -202,7 +200,7 @@ for (NodeData splittedNode : nodes) { if (templateType.getModifiers().contains(Modifier.PRIVATE) && splittedNode.getSpecializations().size() > 0) { - splittedNode.addError("Nodes containing the @%s annotation cannot be private.", Specialization.class.getSimpleName()); + splittedNode.addError("Classes containing a @%s annotation must not be private.", Specialization.class.getSimpleName()); } finalizeSpecializations(elements, splittedNode); diff -r 4830676526e3 -r 43eab069ca9b graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/node/ShortCircuitParser.java --- a/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/node/ShortCircuitParser.java Thu Aug 29 17:39:55 2013 +0200 +++ b/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/node/ShortCircuitParser.java Thu Aug 29 19:19:00 2013 +0200 @@ -58,7 +58,7 @@ } @Override - public ShortCircuitData create(TemplateMethod method) { + public ShortCircuitData create(TemplateMethod method, boolean invalid) { String shortCircuitValue = Utils.getAnnotationValue(String.class, method.getMarkerAnnotation(), "value"); if (!shortCircuitValues.contains(shortCircuitValue)) { diff -r 4830676526e3 -r 43eab069ca9b graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/node/SpecializationListenerParser.java --- a/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/node/SpecializationListenerParser.java Thu Aug 29 17:39:55 2013 +0200 +++ b/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/node/SpecializationListenerParser.java Thu Aug 29 19:19:00 2013 +0200 @@ -47,7 +47,7 @@ } @Override - public SpecializationListenerData create(TemplateMethod method) { + public SpecializationListenerData create(TemplateMethod method, boolean invalid) { return new SpecializationListenerData(method); } diff -r 4830676526e3 -r 43eab069ca9b graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/node/SpecializationMethodParser.java --- a/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/node/SpecializationMethodParser.java Thu Aug 29 17:39:55 2013 +0200 +++ b/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/node/SpecializationMethodParser.java Thu Aug 29 19:19:00 2013 +0200 @@ -44,7 +44,7 @@ } @Override - public SpecializationData create(TemplateMethod method) { + public SpecializationData create(TemplateMethod method, boolean invalid) { return parseSpecialization(method); } diff -r 4830676526e3 -r 43eab069ca9b graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/template/MessageContainer.java --- a/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/template/MessageContainer.java Thu Aug 29 17:39:55 2013 +0200 +++ b/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/template/MessageContainer.java Thu Aug 29 19:19:00 2013 +0200 @@ -52,25 +52,17 @@ public abstract Element getMessageElement(); public final void emitMessages(ProcessorContext context, TypeElement baseElement, Log log) { - emitMessagesImpl(context, baseElement, log, new HashSet()); + emitMessagesImpl(context, baseElement, log, new HashSet(), null); } - private void emitMessagesImpl(ProcessorContext context, TypeElement baseElement, Log log, Set visitedSinks) { - TypeElement expectError = context.getTruffleTypes().getExpectError(); - if (expectError != null) { - Element element = getMessageElement(); - AnnotationMirror mirror = Utils.findAnnotationMirror(element.getAnnotationMirrors(), expectError); - if (mirror != null) { - List values = Utils.getAnnotationValueList(String.class, mirror, "value"); - if (values == null) { - values = Collections.emptyList(); - } - List msgs = getMessages(); - if (values.size() != msgs.size()) { - log.message(Kind.ERROR, element, mirror, Utils.getAnnotationValue(mirror, "value"), String.format("Error count expected %s but was %s.", values.size(), msgs.size())); - } - } + private void emitMessagesImpl(ProcessorContext context, TypeElement baseElement, Log log, Set visitedSinks, List verifiedMessages) { + List childMessages; + if (verifiedMessages == null) { + childMessages = collectMessagesWithElementChildren(new HashSet(), getMessageElement()); + } else { + childMessages = verifiedMessages; } + verifyExpectedMessages(context, log, childMessages); for (Message message : getMessages()) { emitDefault(context, baseElement, log, message); @@ -82,7 +74,44 @@ } visitedSinks.add(sink); - sink.emitMessagesImpl(context, baseElement, log, visitedSinks); + if (sink.getMessageElement() == this.getMessageElement()) { + sink.emitMessagesImpl(context, baseElement, log, visitedSinks, childMessages); + } else { + sink.emitMessagesImpl(context, baseElement, log, visitedSinks, null); + } + } + } + + private List collectMessagesWithElementChildren(Set visitedSinks, Element e) { + if (visitedSinks.contains(this)) { + return Collections.emptyList(); + } + visitedSinks.add(this); + + List foundMessages = new ArrayList<>(); + if (Utils.typeEquals(getMessageElement().asType(), e.asType())) { + foundMessages.addAll(getMessages()); + } + for (MessageContainer sink : findChildContainers()) { + foundMessages.addAll(sink.collectMessagesWithElementChildren(visitedSinks, e)); + } + return foundMessages; + } + + private void verifyExpectedMessages(ProcessorContext context, Log log, List msgs) { + TypeElement expectError = context.getTruffleTypes().getExpectError(); + if (expectError != null) { + Element element = getMessageElement(); + AnnotationMirror mirror = Utils.findAnnotationMirror(element.getAnnotationMirrors(), expectError); + if (mirror != null) { + List values = Utils.getAnnotationValueList(String.class, mirror, "value"); + if (values == null) { + values = Collections.emptyList(); + } + if (values.size() != msgs.size()) { + log.message(Kind.ERROR, element, mirror, Utils.getAnnotationValue(mirror, "value"), String.format("Error count expected %s but was %s.", values.size(), msgs.size())); + } + } } } @@ -99,7 +128,8 @@ String text = message.getText(); TypeElement rootEnclosing = Utils.findRootEnclosingType(getMessageElement()); - if (rootEnclosing == null || !Utils.typeEquals(baseType.asType(), rootEnclosing.asType()) || this != message.getOriginalContainer()) { + TypeElement baseEnclosing = Utils.findRootEnclosingType(baseType); + if (rootEnclosing == null || !Utils.typeEquals(baseEnclosing.asType(), rootEnclosing.asType()) || this != message.getOriginalContainer()) { // redirect message MessageContainer original = message.getOriginalContainer(); messageElement = baseType; @@ -113,11 +143,22 @@ AnnotationMirror mirror = Utils.findAnnotationMirror(messageElement.getAnnotationMirrors(), expectError); if (mirror != null) { List expectedTexts = Utils.getAnnotationValueList(String.class, mirror, "value"); - if (!expectedTexts.contains(text)) { - log.message(kind, messageElement, mirror, Utils.getAnnotationValue(mirror, "value"), String.format("Message expected one of '%s' but was '%s'.", expectedTexts, text)); + boolean found = false; + for (String expectedText : expectedTexts) { + if (expectedText.endsWith("%") && text.startsWith(expectedText.substring(0, expectedText.length() - 1))) { + found = true; + break; + } else if (text.equals(expectedText)) { + found = true; + break; + } + } + if (!found) { + log.message(kind, messageElement, mirror, Utils.getAnnotationValue(mirror, "value"), "Message expected one of '%s' but was '%s'.", expectedTexts, text); } else { return; } + } } diff -r 4830676526e3 -r 43eab069ca9b graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/template/TemplateMethodParser.java --- a/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/template/TemplateMethodParser.java Thu Aug 29 17:39:55 2013 +0200 +++ b/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/template/TemplateMethodParser.java Thu Aug 29 19:19:00 2013 +0200 @@ -76,7 +76,7 @@ public abstract MethodSpec createSpecification(ExecutableElement method, AnnotationMirror mirror); - public abstract E create(TemplateMethod method); + public abstract E create(TemplateMethod method, boolean invalid); public abstract boolean isParsable(ExecutableElement method); @@ -143,7 +143,7 @@ ActualParameter returnTypeMirror = matchParameter(returnTypeSpec, method.getReturnType(), template, 0, false); if (returnTypeMirror == null) { if (emitErrors) { - E invalidMethod = create(new TemplateMethod(id, template, methodSpecification, method, annotation, returnTypeMirror, Collections. emptyList())); + E invalidMethod = create(new TemplateMethod(id, template, methodSpecification, method, annotation, returnTypeMirror, Collections. emptyList()), true); String expectedReturnType = returnTypeSpec.toSignatureString(true); String actualReturnType = Utils.getSimpleName(method.getReturnType()); @@ -164,7 +164,7 @@ List parameters = parseParameters(methodSpecification, parameterTypes); if (parameters == null) { if (isEmitErrors()) { - E invalidMethod = create(new TemplateMethod(id, template, methodSpecification, method, annotation, returnTypeMirror, Collections. emptyList())); + E invalidMethod = create(new TemplateMethod(id, template, methodSpecification, method, annotation, returnTypeMirror, Collections. emptyList()), true); String message = String.format("Method signature %s does not match to the expected signature: \n%s", createActualSignature(methodSpecification, method), methodSpecification.toSignatureString(method.getSimpleName().toString())); invalidMethod.addError(message); @@ -174,7 +174,7 @@ } } - return create(new TemplateMethod(id, template, methodSpecification, method, annotation, returnTypeMirror, parameters)); + return create(new TemplateMethod(id, template, methodSpecification, method, annotation, returnTypeMirror, parameters), false); } private static String createActualSignature(MethodSpec spec, ExecutableElement method) { diff -r 4830676526e3 -r 43eab069ca9b graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/typesystem/GuardParser.java --- a/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/typesystem/GuardParser.java Thu Aug 29 17:39:55 2013 +0200 +++ b/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/typesystem/GuardParser.java Thu Aug 29 19:19:00 2013 +0200 @@ -79,7 +79,7 @@ } @Override - public GuardData create(TemplateMethod method) { + public GuardData create(TemplateMethod method, boolean invalid) { GuardData guard = new GuardData(method, specialization, negated); /* * Update parameters in way that parameter specifications match again the node field names diff -r 4830676526e3 -r 43eab069ca9b graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/typesystem/TypeCastParser.java --- a/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/typesystem/TypeCastParser.java Thu Aug 29 17:39:55 2013 +0200 +++ b/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/typesystem/TypeCastParser.java Thu Aug 29 19:19:00 2013 +0200 @@ -48,10 +48,25 @@ } @Override - public TypeCastData create(TemplateMethod method) { + public TypeCastData create(TemplateMethod method, boolean invalid) { + if (invalid) { + return new TypeCastData(method, null, null); + } + TypeData targetType = findTypeByMethodName(method, "as"); ActualParameter parameter = method.findParameter("valueValue"); - return new TypeCastData(method, parameter.getTypeSystemType(), targetType); + + TypeData sourceType = null; + if (parameter != null) { + sourceType = getTypeSystem().findTypeData(parameter.getType()); + } + TypeCastData cast = new TypeCastData(method, sourceType, targetType); + + if (targetType != method.getReturnType().getTypeSystemType()) { + cast.addError("Cast type %s does not match to the returned type %s.", Utils.getSimpleName(targetType.getPrimitiveType()), + method.getReturnType() != null ? Utils.getSimpleName(method.getReturnType().getTypeSystemType().getPrimitiveType()) : null); + } + return cast; } @Override diff -r 4830676526e3 -r 43eab069ca9b graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/typesystem/TypeCheckParser.java --- a/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/typesystem/TypeCheckParser.java Thu Aug 29 17:39:55 2013 +0200 +++ b/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/typesystem/TypeCheckParser.java Thu Aug 29 19:19:00 2013 +0200 @@ -48,7 +48,7 @@ } @Override - public TypeCheckData create(TemplateMethod method) { + public TypeCheckData create(TemplateMethod method, boolean invalid) { TypeData checkedType = findTypeByMethodName(method, "is"); assert checkedType != null; ActualParameter parameter = method.findParameter("valueValue"); diff -r 4830676526e3 -r 43eab069ca9b graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/typesystem/TypeSystemData.java --- a/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/typesystem/TypeSystemData.java Thu Aug 29 17:39:55 2013 +0200 +++ b/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/typesystem/TypeSystemData.java Thu Aug 29 19:19:00 2013 +0200 @@ -36,6 +36,9 @@ private List primitiveTypeMirrors = new ArrayList<>(); private List boxedTypeMirrors = new ArrayList<>(); + private List casts; + private List checks; + private TypeMirror genericType; private TypeData voidType; @@ -58,6 +61,14 @@ } } + public void setCasts(List casts) { + this.casts = casts; + } + + public void setChecks(List checks) { + this.checks = checks; + } + void setGenericType(TypeMirror genericType) { this.genericType = genericType; } @@ -72,6 +83,12 @@ if (types != null) { sinks.addAll(types); } + if (checks != null) { + sinks.addAll(checks); + } + if (casts != null) { + sinks.addAll(casts); + } return sinks; } diff -r 4830676526e3 -r 43eab069ca9b graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/typesystem/TypeSystemParser.java --- a/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/typesystem/TypeSystemParser.java Thu Aug 29 17:39:55 2013 +0200 +++ b/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/typesystem/TypeSystemParser.java Thu Aug 29 19:19:00 2013 +0200 @@ -84,11 +84,17 @@ verifyExclusiveMethodAnnotation(typeSystem, TypeCast.class, TypeCheck.class); List elements = new ArrayList<>(context.getEnvironment().getElementUtils().getAllMembers(templateType)); - + List implicitCasts = new ImplicitCastParser(context, typeSystem).parse(elements); List casts = new TypeCastParser(context, typeSystem).parse(elements); List checks = new TypeCheckParser(context, typeSystem).parse(elements); - if (casts == null || checks == null) { + if (casts == null || checks == null || implicitCasts == null) { + return typeSystem; + } + typeSystem.setCasts(casts); + typeSystem.setChecks(checks); + + if (typeSystem.hasErrors()) { return typeSystem; }