mirror of
https://github.com/enso-org/enso.git
synced 2024-12-27 18:54:03 +03:00
Better context info in Type_Error
raised from return type checks (#8566)
- Followup to #8502 that adds better error messages
This commit is contained in:
parent
d56b800c11
commit
dfdb547616
@ -59,8 +59,9 @@ type Type_Error
|
||||
Arguments:
|
||||
- expected: The expected type at the error location.
|
||||
- actual: The actual type at the error location.
|
||||
- name: The name of the argument whose type is mismatched.
|
||||
Error expected actual name
|
||||
- comment: Description of the value that was being checked,
|
||||
e.g. function argument, result or an arbitrary expression.
|
||||
Error expected actual comment
|
||||
|
||||
## PRIVATE
|
||||
Convert the Type_Error error to a human-readable format.
|
||||
@ -78,7 +79,7 @@ type Type_Error
|
||||
_ -> ". Try to apply " + (missing_args.join ", ") + " arguments"
|
||||
_ -> tpe.to_text
|
||||
|
||||
"Type error: expected `"+self.name+"` to be "+self.expected.to_display_text+", but got "+type_of_actual+"."
|
||||
"Type error: expected "+self.comment+" to be "+self.expected.to_display_text+", but got "+type_of_actual+"."
|
||||
|
||||
@Builtin_Type
|
||||
type Compile_Error
|
||||
|
@ -30,7 +30,7 @@ import scala.Option;
|
||||
@Persistable(clazz = GatherDiagnostics.DiagnosticsMeta.class, id = 1114)
|
||||
@Persistable(clazz = DocumentationComments.Doc.class, id = 1115)
|
||||
@Persistable(clazz = AliasAnalysis$Info$Occurrence.class, id = 1116)
|
||||
@Persistable(clazz = TypeSignatures.Signature.class, id = 1117)
|
||||
@Persistable(clazz = TypeSignatures.Signature.class, id = 2117)
|
||||
@Persistable(clazz = ModuleAnnotations.Annotations.class, id = 1118)
|
||||
@Persistable(clazz = AliasAnalysis$Info$Scope$Root.class, id = 1120)
|
||||
@Persistable(clazz = DataflowAnalysis$DependencyInfo$Type$Static.class, id = 1121)
|
||||
|
@ -488,7 +488,7 @@ final class SuggestionBuilder[A: IndexedSource](
|
||||
typeSignature: Option[TypeSignatures.Metadata]
|
||||
): Vector[TypeArg] =
|
||||
typeSignature match {
|
||||
case Some(TypeSignatures.Signature(typeExpr)) =>
|
||||
case Some(TypeSignatures.Signature(typeExpr, _)) =>
|
||||
buildTypeSignature(typeExpr)
|
||||
case _ =>
|
||||
Vector()
|
||||
|
@ -393,7 +393,7 @@ case object DataflowAnalysis extends IRPass {
|
||||
*/
|
||||
def analyseType(typ: Type, info: DependencyInfo): Type = {
|
||||
typ match {
|
||||
case asc @ Type.Ascription(typed, signature, _, _, _) =>
|
||||
case asc @ Type.Ascription(typed, signature, _, _, _, _) =>
|
||||
val ascrDep = asStatic(asc)
|
||||
val typedDep = asStatic(typed)
|
||||
val sigDep = asStatic(signature)
|
||||
|
@ -166,7 +166,7 @@ case object ComplexType extends IRPass {
|
||||
): List[Definition] = {
|
||||
var unusedSig: Option[Type.Ascription] = None
|
||||
val sig = lastSignature match {
|
||||
case Some(Type.Ascription(typed, _, _, _, _)) =>
|
||||
case Some(Type.Ascription(typed, _, _, _, _, _)) =>
|
||||
typed match {
|
||||
case literal: Name.Literal =>
|
||||
if (name.name == literal.name) {
|
||||
|
@ -115,7 +115,7 @@ case object SuspendedArguments extends IRPass {
|
||||
method.body match {
|
||||
case lam @ Function.Lambda(args, body, _, _, _, _) =>
|
||||
method.getMetadata(TypeSignatures) match {
|
||||
case Some(Signature(signature)) =>
|
||||
case Some(Signature(signature, _)) =>
|
||||
val newArgs = computeSuspensions(args.drop(1), signature)
|
||||
if (newArgs.head.suspended) {
|
||||
errors.Conversion(
|
||||
@ -163,7 +163,7 @@ case object SuspendedArguments extends IRPass {
|
||||
body match {
|
||||
case lam @ Function.Lambda(args, lamBody, _, _, _, _) =>
|
||||
explicit.getMetadata(TypeSignatures) match {
|
||||
case Some(Signature(signature)) =>
|
||||
case Some(Signature(signature, _)) =>
|
||||
val newArgs = computeSuspensions(
|
||||
args.drop(1),
|
||||
signature
|
||||
@ -214,7 +214,7 @@ case object SuspendedArguments extends IRPass {
|
||||
expression.transformExpressions {
|
||||
case bind @ Expression.Binding(_, expr, _, _, _) =>
|
||||
val newExpr = bind.getMetadata(TypeSignatures) match {
|
||||
case Some(Signature(signature)) =>
|
||||
case Some(Signature(signature, _)) =>
|
||||
expr match {
|
||||
case lam @ Function.Lambda(args, body, _, _, _, _) =>
|
||||
lam.copy(
|
||||
@ -229,7 +229,7 @@ case object SuspendedArguments extends IRPass {
|
||||
bind.copy(expression = newExpr)
|
||||
case lam @ Function.Lambda(args, body, _, _, _, _) =>
|
||||
lam.getMetadata(TypeSignatures) match {
|
||||
case Some(Signature(signature)) =>
|
||||
case Some(Signature(signature, _)) =>
|
||||
lam.copy(
|
||||
arguments = computeSuspensions(args, signature),
|
||||
body = resolveExpression(body)
|
||||
|
@ -186,7 +186,7 @@ case object TypeFunctions extends IRPass {
|
||||
|
||||
name.name match {
|
||||
case Type.Ascription.name =>
|
||||
Type.Ascription(leftArg, rightArg, location)
|
||||
Type.Ascription(leftArg, rightArg, None, location)
|
||||
case Type.Context.name =>
|
||||
Type.Context(leftArg, rightArg, location)
|
||||
case Type.Error.name =>
|
||||
|
@ -111,7 +111,8 @@ case object TypeNames extends IRPass {
|
||||
new MetadataPair(
|
||||
TypeSignatures,
|
||||
TypeSignatures.Signature(
|
||||
resolveSignature(typeParams, bindingsMap, s.signature)
|
||||
resolveSignature(typeParams, bindingsMap, s.signature),
|
||||
s.comment
|
||||
)
|
||||
)
|
||||
)
|
||||
|
@ -97,7 +97,7 @@ case object TypeSignatures extends IRPass {
|
||||
case _ =>
|
||||
}
|
||||
val res = lastSignature match {
|
||||
case Some(asc @ Type.Ascription(typed, sig, _, _, _)) =>
|
||||
case Some(asc @ Type.Ascription(typed, sig, comment, _, _, _)) =>
|
||||
val methodRef = meth.methodReference
|
||||
val newMethodWithDoc = asc
|
||||
.getMetadata(DocumentationComments)
|
||||
@ -121,7 +121,7 @@ case object TypeSignatures extends IRPass {
|
||||
if (ref isSameReferenceAs methodRef) {
|
||||
Some(
|
||||
newMethodWithAnnotations.updateMetadata(
|
||||
new MetadataPair(this, Signature(sig))
|
||||
new MetadataPair(this, Signature(sig, comment))
|
||||
)
|
||||
)
|
||||
} else {
|
||||
@ -246,7 +246,9 @@ case object TypeSignatures extends IRPass {
|
||||
private def resolveAscription(sig: Type.Ascription): Expression = {
|
||||
val newTyped = sig.typed.mapExpressions(resolveExpression)
|
||||
val newSig = sig.signature.mapExpressions(resolveExpression)
|
||||
newTyped.updateMetadata(new MetadataPair(this, Signature(newSig)))
|
||||
newTyped.updateMetadata(
|
||||
new MetadataPair(this, Signature(newSig, sig.comment))
|
||||
)
|
||||
}
|
||||
|
||||
/** Resolves type signatures in a block.
|
||||
@ -271,7 +273,7 @@ case object TypeSignatures extends IRPass {
|
||||
case binding: Expression.Binding =>
|
||||
val newBinding = binding.mapExpressions(resolveExpression)
|
||||
val res = lastSignature match {
|
||||
case Some(asc @ Type.Ascription(typed, sig, _, _, _)) =>
|
||||
case Some(asc @ Type.Ascription(typed, sig, comment, _, _, _)) =>
|
||||
val name = binding.name
|
||||
val newBindingWithDoc = asc
|
||||
.getMetadata(DocumentationComments)
|
||||
@ -287,7 +289,7 @@ case object TypeSignatures extends IRPass {
|
||||
if (typedName.name == name.name) {
|
||||
Some(
|
||||
newBindingWithDoc.updateMetadata(
|
||||
new MetadataPair(this, Signature(sig))
|
||||
new MetadataPair(this, Signature(sig, comment))
|
||||
)
|
||||
)
|
||||
} else {
|
||||
@ -321,8 +323,10 @@ case object TypeSignatures extends IRPass {
|
||||
/** A representation of a type signature.
|
||||
*
|
||||
* @param signature the expression for the type signature
|
||||
* @param comment an optional comment from which the potential error message will be derived
|
||||
*/
|
||||
case class Signature(signature: Expression) extends IRPass.IRMetadata {
|
||||
case class Signature(signature: Expression, comment: Option[String] = None)
|
||||
extends IRPass.IRMetadata {
|
||||
override val metadataName: String = "TypeSignatures.Signature"
|
||||
|
||||
/** @inheritdoc */
|
||||
@ -345,6 +349,6 @@ case object TypeSignatures extends IRPass {
|
||||
|
||||
/** @inheritdoc */
|
||||
override def duplicate(): Option[IRPass.IRMetadata] =
|
||||
Some(this.copy(signature = signature.duplicate()))
|
||||
Some(this.copy(signature = signature.duplicate(), comment = comment))
|
||||
}
|
||||
}
|
||||
|
@ -96,7 +96,7 @@ final class TreeToIr {
|
||||
methodReference = translateExpression(sig.getVariable());
|
||||
}
|
||||
var signature = translateType(sig.getType());
|
||||
var ascription = new Type.Ascription(methodReference, signature, getIdentifiedLocation(sig), meta(), diag());
|
||||
var ascription = new Type.Ascription(methodReference, signature, Option.empty(), getIdentifiedLocation(sig), meta(), diag());
|
||||
yield ascription;
|
||||
}
|
||||
default -> translateExpression(exprTree);
|
||||
@ -250,7 +250,8 @@ final class TreeToIr {
|
||||
yield join(error, appendTo);
|
||||
}
|
||||
|
||||
var ascribedBody = addTypeAscription(body, returnSignature, loc);
|
||||
String functionName = fn.getName().codeRepr();
|
||||
var ascribedBody = addTypeAscription(functionName, body, returnSignature, loc);
|
||||
var binding = new Method.Binding(
|
||||
methodRef,
|
||||
args,
|
||||
@ -317,7 +318,7 @@ final class TreeToIr {
|
||||
case Tree.TypeSignature sig -> {
|
||||
var methodReference = translateMethodReference(sig.getVariable(), true);
|
||||
var signature = translateType(sig.getType());
|
||||
var ascription = new Type.Ascription(methodReference, signature, getIdentifiedLocation(sig), meta(), diag());
|
||||
var ascription = new Type.Ascription(methodReference, signature, Option.empty(), getIdentifiedLocation(sig), meta(), diag());
|
||||
yield join(ascription, appendTo);
|
||||
}
|
||||
|
||||
@ -507,6 +508,7 @@ final class TreeToIr {
|
||||
|
||||
var loc = getIdentifiedLocation(fun);
|
||||
var body = translateExpression(treeBody);
|
||||
String functionName = name.name();
|
||||
if (args.isEmpty()) {
|
||||
if (body instanceof Expression.Block block) {
|
||||
// suspended block has a name and no arguments
|
||||
@ -524,14 +526,14 @@ final class TreeToIr {
|
||||
body = translateSyntaxError(fun, Syntax.UnexpectedExpression$.MODULE$);
|
||||
}
|
||||
|
||||
var ascribedBody = addTypeAscription(body, returnType, loc);
|
||||
var ascribedBody = addTypeAscription(functionName, body, returnType, loc);
|
||||
return new Expression.Binding(name, ascribedBody, loc, meta(), diag());
|
||||
} else {
|
||||
if (body == null) {
|
||||
return translateSyntaxError(fun, Syntax.UnexpectedDeclarationInType$.MODULE$);
|
||||
}
|
||||
|
||||
var ascribedBody = addTypeAscription(body, returnType, loc);
|
||||
var ascribedBody = addTypeAscription(functionName, body, returnType, loc);
|
||||
return new Function.Binding(name, args, ascribedBody, loc, true, meta(), diag());
|
||||
}
|
||||
}
|
||||
@ -551,17 +553,18 @@ final class TreeToIr {
|
||||
* <p>
|
||||
* If the type is {@code null}, the body is returned unchanged.
|
||||
*/
|
||||
private Expression addTypeAscription(Expression body, Expression type, Option<IdentifiedLocation> loc) {
|
||||
private Expression addTypeAscription(String functionName, Expression body, Expression type, Option<IdentifiedLocation> loc) {
|
||||
if (type == null) {
|
||||
return body;
|
||||
}
|
||||
|
||||
return new Type.Ascription(body, type, loc, meta(), diag());
|
||||
String comment = "the result of `" + functionName + "`";
|
||||
return new Type.Ascription(body, type, Option.apply(comment), loc, meta(), diag());
|
||||
}
|
||||
|
||||
private Type.Ascription translateTypeSignature(Tree sig, Tree type, Expression typeName) {
|
||||
var fn = translateType(type);
|
||||
return new Type.Ascription(typeName, fn, getIdentifiedLocation(sig), meta(), diag());
|
||||
return new Type.Ascription(typeName, fn, Option.empty(), getIdentifiedLocation(sig), meta(), diag());
|
||||
}
|
||||
|
||||
|
||||
|
@ -122,6 +122,7 @@ object Type {
|
||||
*
|
||||
* @param typed the expression being ascribed a type
|
||||
* @param signature the signature being ascribed to `typed`
|
||||
* @param comment a comment that may be used to add context to the type error
|
||||
* @param location the source location that the node corresponds to
|
||||
* @param passData the pass metadata associated with this node
|
||||
* @param diagnostics compiler diagnostics for this node
|
||||
@ -129,6 +130,7 @@ object Type {
|
||||
sealed case class Ascription(
|
||||
typed: Expression,
|
||||
signature: Expression,
|
||||
comment: Option[String],
|
||||
override val location: Option[IdentifiedLocation],
|
||||
override val passData: MetadataStorage = new MetadataStorage(),
|
||||
override val diagnostics: DiagnosticStorage = DiagnosticStorage()
|
||||
@ -141,6 +143,7 @@ object Type {
|
||||
*
|
||||
* @param typed the expression being ascribed a type
|
||||
* @param signature the signature being ascribed to `typed`
|
||||
* @param comment a comment that may be used to add context to the type error
|
||||
* @param location the source location that the node corresponds to
|
||||
* @param passData the pass metadata associated with this node
|
||||
* @param diagnostics compiler diagnostics for this node
|
||||
@ -150,12 +153,14 @@ object Type {
|
||||
def copy(
|
||||
typed: Expression = typed,
|
||||
signature: Expression = signature,
|
||||
comment: Option[String] = comment,
|
||||
location: Option[IdentifiedLocation] = location,
|
||||
passData: MetadataStorage = passData,
|
||||
diagnostics: DiagnosticStorage = diagnostics,
|
||||
id: UUID @Identifier = id
|
||||
): Ascription = {
|
||||
val res = Ascription(typed, signature, location, passData, diagnostics)
|
||||
val res =
|
||||
Ascription(typed, signature, comment, location, passData, diagnostics)
|
||||
res.id = id
|
||||
res
|
||||
}
|
||||
@ -205,6 +210,7 @@ object Type {
|
||||
s"""Type.Ascription(
|
||||
|typed = $typed,
|
||||
|signature = $signature,
|
||||
|comment = $comment,
|
||||
|location = $location,
|
||||
|passData = ${this.showPassData},
|
||||
|diagnostics = $diagnostics,
|
||||
|
@ -19,7 +19,6 @@ import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
import org.enso.compiler.core.ir.Name;
|
||||
import org.enso.interpreter.EnsoLanguage;
|
||||
import org.enso.interpreter.node.BaseNode.TailStatus;
|
||||
import org.enso.interpreter.node.EnsoRootNode;
|
||||
@ -49,11 +48,11 @@ import org.enso.interpreter.runtime.library.dispatch.TypesLibrary;
|
||||
import org.graalvm.collections.Pair;
|
||||
|
||||
public abstract class ReadArgumentCheckNode extends Node {
|
||||
private final String name;
|
||||
private final String comment;
|
||||
@CompilerDirectives.CompilationFinal private String expectedTypeMessage;
|
||||
|
||||
ReadArgumentCheckNode(String name) {
|
||||
this.name = name;
|
||||
ReadArgumentCheckNode(String comment) {
|
||||
this.comment = comment;
|
||||
}
|
||||
|
||||
/** */
|
||||
@ -62,7 +61,7 @@ public abstract class ReadArgumentCheckNode extends Node {
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes check or conversion of the value.abstract
|
||||
* Executes check or conversion of the value.
|
||||
*
|
||||
* @param frame frame requesting the conversion
|
||||
* @param value the value to convert
|
||||
@ -89,12 +88,12 @@ public abstract class ReadArgumentCheckNode extends Node {
|
||||
expectedTypeMessage = expectedTypeMessage();
|
||||
}
|
||||
var ctx = EnsoContext.get(this);
|
||||
var msg = name == null ? "expression" : name;
|
||||
var err = ctx.getBuiltins().error().makeTypeError(expectedTypeMessage, v, msg);
|
||||
var msg = comment == null ? "expression" : comment;
|
||||
var err = ctx.getBuiltins().error().makeTypeErrorOfComment(expectedTypeMessage, v, msg);
|
||||
throw new PanicException(err, this);
|
||||
}
|
||||
|
||||
public static ReadArgumentCheckNode allOf(Name argumentName, ReadArgumentCheckNode... checks) {
|
||||
public static ReadArgumentCheckNode allOf(String argumentName, ReadArgumentCheckNode... checks) {
|
||||
var list = Arrays.asList(checks);
|
||||
var flatten =
|
||||
list.stream()
|
||||
@ -105,27 +104,25 @@ public abstract class ReadArgumentCheckNode extends Node {
|
||||
return switch (arr.length) {
|
||||
case 0 -> null;
|
||||
case 1 -> arr[0];
|
||||
default -> new AllOfNode(argumentName.name(), arr);
|
||||
default -> new AllOfNode(argumentName, arr);
|
||||
};
|
||||
}
|
||||
|
||||
public static ReadArgumentCheckNode oneOf(Name argumentName, List<ReadArgumentCheckNode> checks) {
|
||||
public static ReadArgumentCheckNode oneOf(String comment, List<ReadArgumentCheckNode> checks) {
|
||||
var arr = toArray(checks);
|
||||
return switch (arr.length) {
|
||||
case 0 -> null;
|
||||
case 1 -> arr[0];
|
||||
default -> new OneOfNode(argumentName.name(), arr);
|
||||
default -> new OneOfNode(comment, arr);
|
||||
};
|
||||
}
|
||||
|
||||
public static ReadArgumentCheckNode build(Name argumentName, Type expectedType) {
|
||||
var n = argumentName == null ? null : argumentName.name();
|
||||
return ReadArgumentCheckNodeFactory.TypeCheckNodeGen.create(n, expectedType);
|
||||
public static ReadArgumentCheckNode build(String comment, Type expectedType) {
|
||||
return ReadArgumentCheckNodeFactory.TypeCheckNodeGen.create(comment, expectedType);
|
||||
}
|
||||
|
||||
public static ReadArgumentCheckNode meta(Name argumentName, Object metaObject) {
|
||||
var n = argumentName == null ? null : argumentName.name();
|
||||
return ReadArgumentCheckNodeFactory.MetaCheckNodeGen.create(n, metaObject);
|
||||
public static ReadArgumentCheckNode meta(String comment, Object metaObject) {
|
||||
return ReadArgumentCheckNodeFactory.MetaCheckNodeGen.create(comment, metaObject);
|
||||
}
|
||||
|
||||
public static boolean isWrappedThunk(Function fn) {
|
||||
|
@ -13,6 +13,6 @@ public class TypeError extends UniquelyConstructibleBuiltin {
|
||||
|
||||
@Override
|
||||
protected List<String> getConstructorParamNames() {
|
||||
return List.of("expected", "actual", "name");
|
||||
return List.of("expected", "actual", "comment");
|
||||
}
|
||||
}
|
||||
|
@ -176,11 +176,24 @@ public final class Error {
|
||||
*
|
||||
* @param expected the expected type
|
||||
* @param actual the actual type
|
||||
* @param name the name of the variable that is a type error
|
||||
* @param name name of the argument that was being checked
|
||||
* @return a runtime representation of the error.
|
||||
*/
|
||||
@CompilerDirectives.TruffleBoundary
|
||||
public Atom makeTypeError(Object expected, Object actual, String name) {
|
||||
return typeError.newInstance(expected, actual, Text.create(name));
|
||||
return typeError.newInstance(expected, actual, Text.create("`" + name + "`"));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an instance of the runtime representation of a {@code Type_Error}.
|
||||
*
|
||||
* @param expected the expected type
|
||||
* @param actual the actual type
|
||||
* @param comment description of the value that was being checked
|
||||
* @return a runtime representation of the error.
|
||||
*/
|
||||
public Atom makeTypeErrorOfComment(Object expected, Object actual, String comment) {
|
||||
return typeError.newInstance(expected, actual, Text.create(comment));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -713,24 +713,24 @@ class IrToTruffle(
|
||||
// ==========================================================================
|
||||
|
||||
private def extractAscribedType(
|
||||
name: Name,
|
||||
comment: String,
|
||||
t: Expression
|
||||
): ReadArgumentCheckNode = t match {
|
||||
case u: `type`.Set.Union =>
|
||||
ReadArgumentCheckNode.oneOf(
|
||||
name,
|
||||
u.operands.map(extractAscribedType(name, _)).asJava
|
||||
comment,
|
||||
u.operands.map(extractAscribedType(comment, _)).asJava
|
||||
)
|
||||
case i: `type`.Set.Intersection =>
|
||||
ReadArgumentCheckNode.allOf(
|
||||
name,
|
||||
extractAscribedType(name, i.left),
|
||||
extractAscribedType(name, i.right)
|
||||
comment,
|
||||
extractAscribedType(comment, i.left),
|
||||
extractAscribedType(comment, i.right)
|
||||
)
|
||||
case p: Application.Prefix => extractAscribedType(name, p.function)
|
||||
case p: Application.Prefix => extractAscribedType(comment, p.function)
|
||||
case _: Tpe.Function =>
|
||||
ReadArgumentCheckNode.build(
|
||||
name,
|
||||
comment,
|
||||
context.getTopScope().getBuiltins().function()
|
||||
)
|
||||
case t => {
|
||||
@ -740,7 +740,7 @@ class IrToTruffle(
|
||||
.Resolution(BindingsMap.ResolvedType(mod, tpe))
|
||||
) =>
|
||||
ReadArgumentCheckNode.build(
|
||||
name,
|
||||
comment,
|
||||
asScope(
|
||||
mod
|
||||
.unsafeAsModule()
|
||||
@ -753,7 +753,7 @@ class IrToTruffle(
|
||||
.Resolution(BindingsMap.ResolvedPolyglotSymbol(mod, symbol))
|
||||
) =>
|
||||
ReadArgumentCheckNode.meta(
|
||||
name,
|
||||
comment,
|
||||
asScope(
|
||||
mod
|
||||
.unsafeAsModule()
|
||||
@ -768,7 +768,8 @@ class IrToTruffle(
|
||||
private def checkAsTypes(
|
||||
arg: DefinitionArgument
|
||||
): ReadArgumentCheckNode = {
|
||||
arg.ascribedType.map(extractAscribedType(arg.name, _)).getOrElse(null)
|
||||
val comment = "`" + arg.name.name + "`"
|
||||
arg.ascribedType.map(extractAscribedType(comment, _)).getOrElse(null)
|
||||
}
|
||||
|
||||
/** Checks if the expression has a @Builtin_Method annotation
|
||||
@ -1074,9 +1075,11 @@ class IrToTruffle(
|
||||
ir match {
|
||||
case _: Expression.Binding =>
|
||||
case _ =>
|
||||
val types = ir.getMetadata(TypeSignatures)
|
||||
val types: Option[TypeSignatures.Signature] =
|
||||
ir.getMetadata(TypeSignatures)
|
||||
types.foreach { tpe =>
|
||||
val checkNode = extractAscribedType(null, tpe.signature);
|
||||
val checkNode =
|
||||
extractAscribedType(tpe.comment.orNull, tpe.signature);
|
||||
if (checkNode != null) {
|
||||
runtimeExpression =
|
||||
ReadArgumentCheckNode.wrap(runtimeExpression, checkNode)
|
||||
|
@ -66,7 +66,8 @@ public class SignaturePolyglotTest extends TestBase {
|
||||
var ret = fn.execute(new StringBuilder("Hi"));
|
||||
fail("Should fail, but got: " + ret);
|
||||
} catch (PolyglotException e) {
|
||||
assertTypeError("`x`", "java.time.format.DateTimeFormatter", "StringBuilder", e.getMessage());
|
||||
assertTypeError(
|
||||
"`x`", "java.time.format.DateTimeFormatter", "java.lang.StringBuilder", e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -506,7 +506,7 @@ public class SignatureTest extends TestBase {
|
||||
var v = module.invokeMember(MethodNames.Module.EVAL_EXPRESSION, "Bin.Zero One");
|
||||
fail("Expecting an error, not " + v);
|
||||
} catch (PolyglotException ex) {
|
||||
assertTypeError("`v`", "Zero", "Zero", ex.getMessage());
|
||||
assertTypeError("`v`", "Zero", "One", ex.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@ -639,7 +639,7 @@ public class SignatureTest extends TestBase {
|
||||
var v = module.invokeMember(MethodNames.Module.EVAL_EXPRESSION, "Bin.Vec 'Hi'");
|
||||
fail("Expecting an error, not " + v);
|
||||
} catch (PolyglotException ex) {
|
||||
assertTypeError("`v`", "Integer | Range | Vector", "Integer", ex.getMessage());
|
||||
assertTypeError("`v`", "Integer | Range | Vector", "Text", ex.getMessage());
|
||||
}
|
||||
var ok2 = module.invokeMember(MethodNames.Module.EVAL_EXPRESSION, "Bin.Either Zero");
|
||||
assertEquals("binary.Bin", ok2.getMetaObject().getMetaQualifiedName());
|
||||
@ -872,7 +872,8 @@ public class SignatureTest extends TestBase {
|
||||
var res = plusChecked.execute("a", "b");
|
||||
fail("Expecting an exception, not: " + res);
|
||||
} catch (PolyglotException e) {
|
||||
assertContains("expected `expression` to be Integer, but got Text", e.getMessage());
|
||||
assertContains(
|
||||
"expected the result of `plusChecked` to be Integer, but got Text", e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@ -923,7 +924,8 @@ public class SignatureTest extends TestBase {
|
||||
var res = plusChecked.execute(2, 3);
|
||||
fail("Expecting an exception, not: " + res);
|
||||
} catch (PolyglotException e) {
|
||||
assertContains("expected `expression` to be Integer, but got Text", e.getMessage());
|
||||
assertContains(
|
||||
"expected the result of `constant` to be Integer, but got Text", e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@ -944,13 +946,13 @@ public class SignatureTest extends TestBase {
|
||||
.buildLiteral();
|
||||
|
||||
var module = ctx.eval(src);
|
||||
var plusChecked = module.invokeMember(MethodNames.Module.EVAL_EXPRESSION, "foo");
|
||||
assertEquals(8, plusChecked.execute(2).asInt());
|
||||
var foo = module.invokeMember(MethodNames.Module.EVAL_EXPRESSION, "foo");
|
||||
assertEquals(8, foo.execute(2).asInt());
|
||||
try {
|
||||
var res = plusChecked.execute(".");
|
||||
var res = foo.execute(".");
|
||||
fail("Expecting an exception, not: " + res);
|
||||
} catch (PolyglotException e) {
|
||||
assertContains("expected `expression` to be Integer, but got Text", e.getMessage());
|
||||
assertContains("expected the result of `x` to be Integer, but got Text", e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@ -978,7 +980,7 @@ public class SignatureTest extends TestBase {
|
||||
var res = plusChecked.execute(".");
|
||||
fail("Expecting an exception, not: " + res);
|
||||
} catch (PolyglotException e) {
|
||||
assertContains("expected `expression` to be Integer, but got Text", e.getMessage());
|
||||
assertContains("expected the result of `x` to be Integer, but got Text", e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@ -1008,7 +1010,7 @@ public class SignatureTest extends TestBase {
|
||||
var res = foo.execute(2);
|
||||
fail("Expecting an exception, not: " + res);
|
||||
} catch (PolyglotException e) {
|
||||
assertContains("expected `expression` to be Integer, but got Text", e.getMessage());
|
||||
assertContains("expected the result of `foo` to be Integer, but got Text", e.getMessage());
|
||||
}
|
||||
|
||||
var res = foo.execute(3);
|
||||
@ -1043,8 +1045,7 @@ public class SignatureTest extends TestBase {
|
||||
var res = factorial.execute(20);
|
||||
fail("Expecting an exception, not: " + res);
|
||||
} catch (PolyglotException e) {
|
||||
// TODO we may want to change `expression` to 'the return type' or something
|
||||
assertContains("expected `expression` to be Integer, but got Text", e.getMessage());
|
||||
assertContains("expected the result of `go` to be Integer, but got Text", e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@ -1080,7 +1081,7 @@ public class SignatureTest extends TestBase {
|
||||
var res = foo.execute(n, 1);
|
||||
fail("Expecting an exception, not: " + res);
|
||||
} catch (PolyglotException e) {
|
||||
assertContains("expected `expression` to be Integer, but got Text", e.getMessage());
|
||||
assertContains("expected the result of `go` to be Integer, but got Text", e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@ -1113,20 +1114,16 @@ public class SignatureTest extends TestBase {
|
||||
var res = foo_bad.execute(n);
|
||||
fail("Expecting an exception, not: " + res);
|
||||
} catch (PolyglotException e) {
|
||||
assertContains("expected `expression` to be Integer, but got Text", e.getMessage());
|
||||
assertEquals(
|
||||
"Type error: expected the result of `foo_bad` to be Integer, but got Text.",
|
||||
e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
static void assertTypeError(String expArg, String expType, String realType, String msg) {
|
||||
if (!msg.contains(expArg)) {
|
||||
fail("Expecting value " + expArg + " in " + msg);
|
||||
}
|
||||
if (!msg.contains(expType)) {
|
||||
fail("Expecting value " + expType + " in " + msg);
|
||||
}
|
||||
if (!msg.contains(realType)) {
|
||||
fail("Expecting value " + realType + " in " + msg);
|
||||
}
|
||||
assertEquals(
|
||||
"Type error: expected " + expArg + " to be " + expType + ", but got " + realType + ".",
|
||||
msg);
|
||||
}
|
||||
|
||||
private static void assertContains(String exp, String msg) {
|
||||
|
@ -117,7 +117,7 @@ class TextTest extends InterpreterTest {
|
||||
|main =
|
||||
| IO.println (List.Cons Nothing Nothing).to_display_text
|
||||
| IO.println (Syntax_Error.Error "foo").to_display_text
|
||||
| IO.println (Type_Error.Error Nothing List.Nil "myvar").to_display_text
|
||||
| IO.println (Type_Error.Error Nothing List.Nil "`myvar`").to_display_text
|
||||
| IO.println (Compile_Error.Error "error :(").to_display_text
|
||||
| IO.println (Inexhaustive_Pattern_Match.Error 32).to_display_text
|
||||
| IO.println (Arithmetic_Error.Error "cannot frobnicate quaternions").to_display_text
|
||||
|
@ -17,13 +17,13 @@ type Arithmetic_Error
|
||||
|
||||
@Builtin_Type
|
||||
type Type_Error
|
||||
Error expected actual name
|
||||
Error expected actual comment
|
||||
|
||||
type_of_actual self =
|
||||
tpe = Meta.type_of self.actual
|
||||
if tpe.is_error then self.actual.to_display_text else tpe.to_display_text
|
||||
|
||||
to_display_text self = "Type error: expected `"+self.name+"` to be "+self.expected.to_display_text+", but got "+self.type_of_actual+"."
|
||||
to_display_text self = "Type error: expected "+self.comment+" to be "+self.expected.to_display_text+", but got "+self.type_of_actual+"."
|
||||
|
||||
@Builtin_Type
|
||||
type Not_Invokable
|
||||
|
Loading…
Reference in New Issue
Block a user