Add Meta.get_annotation (#4049)

- add: `GeneralAnnotation` IR node for `@name expression` annotations
- update: compilation pipeline to process the annotation expressions
- update: rewrite `OverloadsResolution` compiler pass so that it keeps the order of module definitions
- add: `Meta.get_annotation` builtin function that returns the result of annotation expression
- misc: improvements (private methods, lazy arguments, build.sbt cleanup)
This commit is contained in:
Dmitry Bushev 2023-01-24 21:28:33 +03:00 committed by GitHub
parent 5b5a2be829
commit bf9508603f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
43 changed files with 1072 additions and 368 deletions

View File

@ -525,6 +525,7 @@
- [Introducing Meta.atom_with_hole][4023] - [Introducing Meta.atom_with_hole][4023]
- [Report failures in name resolution in type signatures][4030] - [Report failures in name resolution in type signatures][4030]
- [Attach visualizations to sub-expressions][4048] - [Attach visualizations to sub-expressions][4048]
- [Add Meta.get_annotation method][4049]
- [Resolve Fully Qualified Names][4056] - [Resolve Fully Qualified Names][4056]
- [Optimize Atom storage layouts][3862] - [Optimize Atom storage layouts][3862]
@ -613,6 +614,7 @@
[4023]: https://github.com/enso-org/enso/pull/4023 [4023]: https://github.com/enso-org/enso/pull/4023
[4030]: https://github.com/enso-org/enso/pull/4030 [4030]: https://github.com/enso-org/enso/pull/4030
[4048]: https://github.com/enso-org/enso/pull/4048 [4048]: https://github.com/enso-org/enso/pull/4048
[4056]: https://github.com/enso-org/enso/pull/4049
[4056]: https://github.com/enso-org/enso/pull/4056 [4056]: https://github.com/enso-org/enso/pull/4056
# Enso 2.0.0-alpha.18 (2021-10-12) # Enso 2.0.0-alpha.18 (2021-10-12)

View File

@ -1146,9 +1146,6 @@ lazy val `polyglot-api` = project
"org.scalacheck" %% "scalacheck" % scalacheckVersion % Test "org.scalacheck" %% "scalacheck" % scalacheckVersion % Test
), ),
libraryDependencies ++= jackson, libraryDependencies ++= jackson,
addCompilerPlugin(
"org.typelevel" %% "kind-projector" % kindProjectorVersion cross CrossVersion.full
),
GenerateFlatbuffers.flatcVersion := flatbuffersVersion, GenerateFlatbuffers.flatcVersion := flatbuffersVersion,
Compile / sourceGenerators += GenerateFlatbuffers.task Compile / sourceGenerators += GenerateFlatbuffers.task
) )
@ -1443,9 +1440,6 @@ lazy val runtime = (project in file("engine/runtime"))
"-s", "-s",
(Compile / sourceManaged).value.getAbsolutePath, (Compile / sourceManaged).value.getAbsolutePath,
"-Xlint:unchecked" "-Xlint:unchecked"
),
addCompilerPlugin(
"org.typelevel" %% "kind-projector" % kindProjectorVersion cross CrossVersion.full
) )
) )
.settings( .settings(

View File

@ -10,6 +10,7 @@ import project.Data.Time.Duration.Duration
import project.Data.Time.Time_Of_Day.Time_Of_Day import project.Data.Time.Time_Of_Day.Time_Of_Day
import project.Data.Time.Time_Zone.Time_Zone import project.Data.Time.Time_Zone.Time_Zone
import project.Data.Vector.Vector import project.Data.Vector.Vector
import project.Nothing.Nothing
import project.Polyglot.Java import project.Polyglot.Java
import project.Error.Error as Base_Error import project.Error.Error as Base_Error
@ -333,10 +334,22 @@ java_instance_check value typ =
Returns the type of the given value. Returns the type of the given value.
Arguments: Arguments:
- value: the value to get the type of. - value: The value to get the type of.
type_of : Any -> Any type_of : Any -> Any
type_of value = @Builtin_Method "Meta.type_of" type_of value = @Builtin_Method "Meta.type_of"
## UNSTABLE
ADVANCED
Given a type object, method name and a parameter name, return the associated annotation if it exists.
Arguments:
- target: The value or type to get the attribute from.
- method_name: The name of the method or constructor to get the attribute for.
- parameter_name: The name of the parameter to get the attribute for.
get_annotation : Any -> Text -> Text -> Any | Nothing
get_annotation target method_name parameter_name = @Builtin_Method "Meta.get_annotation"
## Represents a polyglot language. ## Represents a polyglot language.
type Language type Language

View File

@ -26,6 +26,11 @@ public final class EnsoCompiler implements AutoCloseable {
} }
} }
public IR.Module compile(CharSequence src) {
var tree = parser.parse(src);
return generateIR(tree);
}
boolean isReady() { boolean isReady() {
return parser != null; return parser != null;
} }

View File

@ -42,7 +42,8 @@ import org.enso.compiler.core.IR$Module$Scope$Import;
import org.enso.compiler.core.IR$Module$Scope$Import$Module; import org.enso.compiler.core.IR$Module$Scope$Import$Module;
import org.enso.compiler.core.IR$Module$Scope$Import$Polyglot; import org.enso.compiler.core.IR$Module$Scope$Import$Polyglot;
import org.enso.compiler.core.IR$Module$Scope$Import$Polyglot$Java; import org.enso.compiler.core.IR$Module$Scope$Import$Polyglot$Java;
import org.enso.compiler.core.IR$Name$Annotation; import org.enso.compiler.core.IR$Name$BuiltinAnnotation;
import org.enso.compiler.core.IR$Name$GenericAnnotation;
import org.enso.compiler.core.IR$Name$Blank; import org.enso.compiler.core.IR$Name$Blank;
import org.enso.compiler.core.IR$Name$Literal; import org.enso.compiler.core.IR$Name$Literal;
import org.enso.compiler.core.IR$Name$Self; import org.enso.compiler.core.IR$Name$Self;
@ -222,6 +223,7 @@ final class TreeToIr {
private List<IR$Module$Scope$Definition> translateModuleSymbolImpl(Tree inputAst, List<IR$Module$Scope$Definition> appendTo) throws SyntaxException { private List<IR$Module$Scope$Definition> translateModuleSymbolImpl(Tree inputAst, List<IR$Module$Scope$Definition> appendTo) throws SyntaxException {
return switch (inputAst) { return switch (inputAst) {
case null -> appendTo; case null -> appendTo;
case Tree.TypeDef def -> { case Tree.TypeDef def -> {
var typeName = buildName(def.getName(), true); var typeName = buildName(def.getName(), true);
List<IR> irBody = nil(); List<IR> irBody = nil();
@ -238,6 +240,7 @@ final class TreeToIr {
); );
yield cons(type, appendTo); yield cons(type, appendTo);
} }
case Tree.Function fn -> { case Tree.Function fn -> {
var methodRef = translateMethodReference(fn.getName(), false); var methodRef = translateMethodReference(fn.getName(), false);
var args = translateArgumentsDefinition(fn.getArgs()); var args = translateArgumentsDefinition(fn.getArgs());
@ -256,6 +259,7 @@ final class TreeToIr {
); );
yield cons(binding, appendTo); yield cons(binding, appendTo);
} }
case Tree.ForeignFunction fn when fn.getBody() instanceof Tree.TextLiteral body -> { case Tree.ForeignFunction fn when fn.getBody() instanceof Tree.TextLiteral body -> {
var name = fn.getName(); var name = fn.getName();
var nameLoc = getIdentifiedLocation(name); var nameLoc = getIdentifiedLocation(name);
@ -277,13 +281,21 @@ final class TreeToIr {
} }
case Tree.AnnotatedBuiltin anno -> { case Tree.AnnotatedBuiltin anno -> {
var annotation = new IR$Name$Annotation("@" + anno.getAnnotation().codeRepr(), getIdentifiedLocation(anno), meta(), diag()); var annotation = new IR$Name$BuiltinAnnotation("@" + anno.getAnnotation().codeRepr(), getIdentifiedLocation(anno), meta(), diag());
yield translateModuleSymbol(anno.getExpression(), cons(annotation, appendTo)); yield translateModuleSymbol(anno.getExpression(), cons(annotation, appendTo));
} }
case Tree.Annotated anno -> {
var annotationArgument = translateExpression(anno.getArgument());
var annotation = new IR$Name$GenericAnnotation(anno.getAnnotation().codeRepr(), annotationArgument, getIdentifiedLocation(anno), meta(), diag());
yield translateModuleSymbol(anno.getExpression(), cons(annotation, appendTo));
}
case Tree.Documented doc -> { case Tree.Documented doc -> {
var comment = translateComment(doc, doc.getDocumentation()); var comment = translateComment(doc, doc.getDocumentation());
yield translateModuleSymbol(doc.getExpression(), cons(comment, appendTo)); yield translateModuleSymbol(doc.getExpression(), cons(comment, appendTo));
} }
case Tree.Assignment a -> { case Tree.Assignment a -> {
var reference = translateMethodReference(a.getPattern(), false); var reference = translateMethodReference(a.getPattern(), false);
var body = translateExpression(a.getExpr()); var body = translateExpression(a.getExpr());
@ -307,6 +319,7 @@ final class TreeToIr {
var ascription = new IR$Type$Ascription(methodReference, signature, getIdentifiedLocation(sig), meta(), diag()); var ascription = new IR$Type$Ascription(methodReference, signature, getIdentifiedLocation(sig), meta(), diag());
yield cons(ascription, appendTo); yield cons(ascription, appendTo);
} }
default -> { default -> {
var error = translateSyntaxError(inputAst, IR$Error$Syntax$UnexpectedExpression$.MODULE$); var error = translateSyntaxError(inputAst, IR$Error$Syntax$UnexpectedExpression$.MODULE$);
yield cons(error, appendTo); yield cons(error, appendTo);
@ -328,7 +341,7 @@ final class TreeToIr {
var constructorName = buildName(inputAst, cons.getConstructor()); var constructorName = buildName(inputAst, cons.getConstructor());
List<IR.DefinitionArgument> args = translateArgumentsDefinition(cons.getArguments()); List<IR.DefinitionArgument> args = translateArgumentsDefinition(cons.getArguments());
var cAt = getIdentifiedLocation(inputAst); var cAt = getIdentifiedLocation(inputAst);
return new IR$Module$Scope$Definition$Data(constructorName, args, cAt, meta(), diag()); return new IR$Module$Scope$Definition$Data(constructorName, args, nil(), cAt, meta(), diag());
} catch (SyntaxException ex) { } catch (SyntaxException ex) {
return ex.toError(); return ex.toError();
} }
@ -353,12 +366,16 @@ final class TreeToIr {
var inputAst = maybeManyParensed(exp); var inputAst = maybeManyParensed(exp);
return switch (inputAst) { return switch (inputAst) {
case null -> appendTo; case null -> appendTo;
case Tree.ConstructorDefinition cons -> cons(translateConstructorDefinition(cons, inputAst), appendTo); case Tree.ConstructorDefinition cons -> cons(translateConstructorDefinition(cons, inputAst), appendTo);
case Tree.TypeDef def -> { case Tree.TypeDef def -> {
var ir = translateSyntaxError(def, IR$Error$Syntax$UnexpectedDeclarationInType$.MODULE$); var ir = translateSyntaxError(def, IR$Error$Syntax$UnexpectedDeclarationInType$.MODULE$);
yield cons(ir, appendTo); yield cons(ir, appendTo);
} }
case Tree.ArgumentBlockApplication app -> appendTo; case Tree.ArgumentBlockApplication app -> appendTo;
case Tree.TypeSignature sig -> { case Tree.TypeSignature sig -> {
var isMethod = false; var isMethod = false;
if (sig.getVariable() instanceof Tree.Ident ident) { if (sig.getVariable() instanceof Tree.Ident ident) {
@ -368,6 +385,7 @@ final class TreeToIr {
var ir = translateTypeSignature(sig, sig.getType(), typeName); var ir = translateTypeSignature(sig, sig.getType(), typeName);
yield cons(ir, appendTo); yield cons(ir, appendTo);
} }
case Tree.Function fun -> { case Tree.Function fun -> {
IR.Name name; IR.Name name;
if (fun.getName() instanceof Tree.Ident ident) { if (fun.getName() instanceof Tree.Ident ident) {
@ -379,6 +397,7 @@ final class TreeToIr {
var ir = translateFunction(fun, name, fun.getArgs(), fun.getBody()); var ir = translateFunction(fun, name, fun.getArgs(), fun.getBody());
yield cons(ir, appendTo); yield cons(ir, appendTo);
} }
// In some cases this is a `Function` in IR, but an `Assignment` in Tree. // In some cases this is a `Function` in IR, but an `Assignment` in Tree.
// See: https://discord.com/channels/401396655599124480/1001476608957349917 // See: https://discord.com/channels/401396655599124480/1001476608957349917
case Tree.Assignment assignment -> { case Tree.Assignment assignment -> {
@ -387,6 +406,7 @@ final class TreeToIr {
var ir = translateFunction(assignment, name, args, assignment.getExpr()); var ir = translateFunction(assignment, name, args, assignment.getExpr());
yield cons(ir, appendTo); yield cons(ir, appendTo);
} }
case Tree.ForeignFunction fn when fn.getBody() instanceof Tree.TextLiteral body -> { case Tree.ForeignFunction fn when fn.getBody() instanceof Tree.TextLiteral body -> {
var name = buildName(fn.getName()); var name = buildName(fn.getName());
var args = translateArgumentsDefinition(fn.getArgs()); var args = translateArgumentsDefinition(fn.getArgs());
@ -406,11 +426,19 @@ final class TreeToIr {
var irDoc = translateComment(doc, doc.getDocumentation()); var irDoc = translateComment(doc, doc.getDocumentation());
yield translateTypeBodyExpression(doc.getExpression(), cons(irDoc, appendTo)); yield translateTypeBodyExpression(doc.getExpression(), cons(irDoc, appendTo));
} }
case Tree.AnnotatedBuiltin anno -> { case Tree.AnnotatedBuiltin anno -> {
var ir = new IR$Name$Annotation("@" + anno.getAnnotation().codeRepr(), getIdentifiedLocation(anno), meta(), diag()); var ir = new IR$Name$BuiltinAnnotation("@" + anno.getAnnotation().codeRepr(), getIdentifiedLocation(anno), meta(), diag());
var annotation = translateAnnotation(ir, anno.getExpression(), nil()); var annotation = translateAnnotation(ir, anno.getExpression(), nil());
yield cons(annotation, appendTo); yield cons(annotation, appendTo);
} }
case Tree.Annotated anno -> {
var annotationArgument = translateExpression(anno.getArgument());
var annotation = new IR$Name$GenericAnnotation(anno.getAnnotation().codeRepr(), annotationArgument, getIdentifiedLocation(anno), meta(), diag());
yield translateTypeBodyExpression(anno.getExpression(), cons(annotation, appendTo));
}
default -> { default -> {
var ir = translateSyntaxError(inputAst, IR$Error$Syntax$UnexpectedDeclarationInType$.MODULE$); var ir = translateSyntaxError(inputAst, IR$Error$Syntax$UnexpectedDeclarationInType$.MODULE$);
yield cons(ir, appendTo); yield cons(ir, appendTo);
@ -937,7 +965,7 @@ final class TreeToIr {
case Tree.TemplateFunction templ -> translateExpression(templ.getAst(), false); case Tree.TemplateFunction templ -> translateExpression(templ.getAst(), false);
case Tree.Wildcard wild -> new IR$Name$Blank(getIdentifiedLocation(wild), meta(), diag()); case Tree.Wildcard wild -> new IR$Name$Blank(getIdentifiedLocation(wild), meta(), diag());
case Tree.AnnotatedBuiltin anno -> { case Tree.AnnotatedBuiltin anno -> {
var ir = new IR$Name$Annotation("@" + anno.getAnnotation().codeRepr(), getIdentifiedLocation(anno), meta(), diag()); var ir = new IR$Name$BuiltinAnnotation("@" + anno.getAnnotation().codeRepr(), getIdentifiedLocation(anno), meta(), diag());
yield translateAnnotation(ir, anno.getExpression(), nil()); yield translateAnnotation(ir, anno.getExpression(), nil());
} }
// Documentation can be attached to an expression in a few cases, like if someone documents a line of an // Documentation can be attached to an expression in a few cases, like if someone documents a line of an
@ -1133,7 +1161,7 @@ final class TreeToIr {
return new IR$Application$Prefix(pref.function(), withBlockArgs, pref.hasDefaultsSuspended(), pref.location(), meta(), diag()); return new IR$Application$Prefix(pref.function(), withBlockArgs, pref.hasDefaultsSuspended(), pref.location(), meta(), diag());
} }
private IR$Application$Prefix translateAnnotation(IR$Name$Annotation ir, Tree expr, List<IR.CallArgument> callArgs) { private IR$Application$Prefix translateAnnotation(IR$Name$BuiltinAnnotation ir, Tree expr, List<IR.CallArgument> callArgs) {
return switch (expr) { return switch (expr) {
case Tree.App fn -> { case Tree.App fn -> {
var fnAsArg = translateCallArgument(fn.getArg()); var fnAsArg = translateCallArgument(fn.getArg());

View File

@ -1,7 +1,6 @@
package org.enso.interpreter.node; package org.enso.interpreter.node;
import com.oracle.truffle.api.CompilerDirectives; import com.oracle.truffle.api.CompilerDirectives;
import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
import com.oracle.truffle.api.dsl.ReportPolymorphism; import com.oracle.truffle.api.dsl.ReportPolymorphism;
import com.oracle.truffle.api.nodes.Node; import com.oracle.truffle.api.nodes.Node;
import com.oracle.truffle.api.nodes.NodeInfo; import com.oracle.truffle.api.nodes.NodeInfo;
@ -26,7 +25,7 @@ public abstract class BaseNode extends Node {
} }
} }
private @CompilationFinal TailStatus tailStatus = TailStatus.NOT_TAIL; private @CompilerDirectives.CompilationFinal TailStatus tailStatus = TailStatus.NOT_TAIL;
/** /**
* Sets the new tail position status for this node. * Sets the new tail position status for this node.

View File

@ -2,6 +2,7 @@ package org.enso.interpreter.node.expression.builtin.meta;
import org.enso.interpreter.dsl.BuiltinMethod; import org.enso.interpreter.dsl.BuiltinMethod;
import org.enso.interpreter.runtime.EnsoContext; import org.enso.interpreter.runtime.EnsoContext;
import org.enso.interpreter.runtime.callable.Annotation;
import org.enso.interpreter.runtime.callable.argument.ArgumentDefinition; import org.enso.interpreter.runtime.callable.argument.ArgumentDefinition;
import org.enso.interpreter.runtime.callable.argument.CallArgumentInfo; import org.enso.interpreter.runtime.callable.argument.CallArgumentInfo;
import org.enso.interpreter.runtime.callable.atom.Atom; import org.enso.interpreter.runtime.callable.atom.Atom;
@ -9,7 +10,6 @@ import org.enso.interpreter.runtime.callable.atom.StructsLibrary;
import org.enso.interpreter.runtime.callable.function.Function; import org.enso.interpreter.runtime.callable.function.Function;
import org.enso.interpreter.runtime.callable.function.FunctionSchema; import org.enso.interpreter.runtime.callable.function.FunctionSchema;
import org.enso.interpreter.runtime.data.Array; import org.enso.interpreter.runtime.data.Array;
import org.enso.interpreter.runtime.data.Vector;
import org.enso.interpreter.runtime.error.PanicException; import org.enso.interpreter.runtime.error.PanicException;
import com.oracle.truffle.api.CompilerDirectives; import com.oracle.truffle.api.CompilerDirectives;
@ -19,12 +19,10 @@ import com.oracle.truffle.api.frame.VirtualFrame;
import com.oracle.truffle.api.interop.InteropLibrary; import com.oracle.truffle.api.interop.InteropLibrary;
import com.oracle.truffle.api.interop.TruffleObject; import com.oracle.truffle.api.interop.TruffleObject;
import com.oracle.truffle.api.interop.UnknownIdentifierException; import com.oracle.truffle.api.interop.UnknownIdentifierException;
import com.oracle.truffle.api.interop.UnsupportedMessageException;
import com.oracle.truffle.api.library.ExportLibrary; import com.oracle.truffle.api.library.ExportLibrary;
import com.oracle.truffle.api.library.ExportMessage; import com.oracle.truffle.api.library.ExportMessage;
import com.oracle.truffle.api.nodes.Node; import com.oracle.truffle.api.nodes.Node;
import com.oracle.truffle.api.nodes.RootNode; import com.oracle.truffle.api.nodes.RootNode;
import com.oracle.truffle.api.profiles.ValueProfile;
import org.enso.interpreter.node.callable.InvokeCallableNode; import org.enso.interpreter.node.callable.InvokeCallableNode;
import org.enso.interpreter.runtime.state.State; import org.enso.interpreter.runtime.state.State;
@ -96,7 +94,7 @@ public abstract class AtomWithAHoleNode extends Node {
}; };
} }
@ExportMessage Object getMembers(boolean includeInternal) throws UnsupportedMessageException { @ExportMessage Object getMembers(boolean includeInternal) {
return new Array("value", "fill"); return new Array("value", "fill");
} }
@ -118,7 +116,6 @@ public abstract class AtomWithAHoleNode extends Node {
} }
static final class SwapAtomFieldNode extends RootNode { static final class SwapAtomFieldNode extends RootNode {
private final FunctionSchema schema; private final FunctionSchema schema;
private final ValueProfile sameAtom = ValueProfile.createClassProfile();
@CompilerDirectives.CompilationFinal @CompilerDirectives.CompilationFinal
private int lastIndex = -1; private int lastIndex = -1;
@Child private StructsLibrary structs = StructsLibrary.getFactory().createDispatched(10); @Child private StructsLibrary structs = StructsLibrary.getFactory().createDispatched(10);
@ -130,7 +127,7 @@ public abstract class AtomWithAHoleNode extends Node {
new ArgumentDefinition(1, "value", ArgumentDefinition.ExecutionMode.EXECUTE) new ArgumentDefinition(1, "value", ArgumentDefinition.ExecutionMode.EXECUTE)
}, new boolean[]{ }, new boolean[]{
true, false true, false
}, new CallArgumentInfo[0]); }, new CallArgumentInfo[0], new Annotation[0]);
} }
static SwapAtomFieldNode create() { static SwapAtomFieldNode create() {

View File

@ -0,0 +1,76 @@
package org.enso.interpreter.node.expression.builtin.meta;
import com.oracle.truffle.api.CompilerDirectives;
import com.oracle.truffle.api.dsl.Cached;
import com.oracle.truffle.api.dsl.Specialization;
import com.oracle.truffle.api.frame.VirtualFrame;
import com.oracle.truffle.api.library.CachedLibrary;
import org.enso.interpreter.dsl.BuiltinMethod;
import org.enso.interpreter.node.BaseNode;
import org.enso.interpreter.node.callable.thunk.ThunkExecutorNode;
import org.enso.interpreter.node.expression.builtin.text.util.ExpectStringNode;
import org.enso.interpreter.runtime.EnsoContext;
import org.enso.interpreter.runtime.callable.Annotation;
import org.enso.interpreter.runtime.callable.atom.AtomConstructor;
import org.enso.interpreter.runtime.callable.function.Function;
import org.enso.interpreter.runtime.data.Type;
import org.enso.interpreter.runtime.library.dispatch.TypesLibrary;
import org.enso.interpreter.runtime.scope.ModuleScope;
import org.enso.interpreter.runtime.state.State;
@BuiltinMethod(
type = "Meta",
name = "get_annotation",
description = "Get annotation associated with an object",
autoRegister = false)
public abstract class GetAnnotationNode extends BaseNode {
abstract Object execute(
VirtualFrame frame, State state, Object target, Object method, Object parameter);
@Specialization
Object doExecute(
VirtualFrame frame,
State state,
Object target,
Object method,
Object parameter,
@CachedLibrary(limit = "3") TypesLibrary types,
@Cached ThunkExecutorNode thunkExecutorNode,
@Cached ExpectStringNode expectStringNode) {
String methodName = expectStringNode.execute(method);
Type targetType = types.getType(target);
ModuleScope scope = targetType.getDefinitionScope();
Function methodFunction = scope.lookupMethodDefinition(targetType, methodName);
if (methodFunction != null) {
String parameterName = expectStringNode.execute(parameter);
Annotation annotation = methodFunction.getSchema().getAnnotation(parameterName);
if (annotation != null) {
Function thunk =
Function.thunk(annotation.getExpression().getCallTarget(), frame.materialize());
return thunkExecutorNode.executeThunk(thunk, state, getTailStatus());
}
}
AtomConstructor constructor = getAtomConstructor(targetType, methodName);
if (constructor != null) {
Function constructorFunction = constructor.getConstructorFunction();
String parameterName = expectStringNode.execute(parameter);
Annotation annotation = constructorFunction.getSchema().getAnnotation(parameterName);
if (annotation != null) {
Function thunk =
Function.thunk(annotation.getExpression().getCallTarget(), frame.materialize());
return thunkExecutorNode.executeThunk(thunk, state, getTailStatus());
}
}
return EnsoContext.get(this).getNothing();
}
static GetAnnotationNode build() {
return GetAnnotationNodeGen.create();
}
@CompilerDirectives.TruffleBoundary
private static AtomConstructor getAtomConstructor(Type type, String name) {
return type.getConstructors().get(name);
}
}

View File

@ -0,0 +1,25 @@
package org.enso.interpreter.runtime.callable;
import com.oracle.truffle.api.nodes.RootNode;
/** Annotation with callable expression. */
public class Annotation {
private final RootNode expression;
private final String name;
public Annotation(String name, RootNode expression) {
this.name = name;
this.expression = expression;
}
/** @return the annotation name. */
public String getName() {
return name;
}
/** @return the annotation expression. */
public RootNode getExpression() {
return expression;
}
}

View File

@ -70,15 +70,15 @@ public final class CallArgumentInfo {
* Represents a mapping between the defined arguments of a function and the call site arguments. * Represents a mapping between the defined arguments of a function and the call site arguments.
*/ */
public static class ArgumentMappingBuilder { public static class ArgumentMappingBuilder {
private int[] appliedMapping; private final int[] appliedMapping;
private int[] oversaturatedArgumentMapping; private final int[] oversaturatedArgumentMapping;
private boolean[] argumentShouldExecute; private final boolean[] argumentShouldExecute;
private ArgumentDefinition[] definitions; private final ArgumentDefinition[] definitions;
private CallArgumentInfo[] callArgs; private final CallArgumentInfo[] callArgs;
private CallArgumentInfo[] existingOversaturatedArgs; private final CallArgumentInfo[] existingOversaturatedArgs;
private boolean[] argumentUsed; private final boolean[] argumentUsed;
private boolean[] callSiteArgApplied; private final boolean[] callSiteArgApplied;
private FunctionSchema originalSchema; private final FunctionSchema originalSchema;
private int oversaturatedWritePosition = 0; private int oversaturatedWritePosition = 0;
/** /**
@ -220,7 +220,11 @@ public final class CallArgumentInfo {
newOversaturatedArgInfo.length); newOversaturatedArgInfo.length);
return new FunctionSchema( return new FunctionSchema(
originalSchema.getCallerFrameAccess(), definitions, argumentUsed, oversaturatedArgInfo); originalSchema.getCallerFrameAccess(),
definitions,
argumentUsed,
oversaturatedArgInfo,
originalSchema.getAnnotations());
} }
} }

View File

@ -19,6 +19,7 @@ import org.enso.interpreter.node.expression.atom.InstantiateNode;
import org.enso.interpreter.node.expression.atom.QualifiedAccessorNode; import org.enso.interpreter.node.expression.atom.QualifiedAccessorNode;
import org.enso.interpreter.runtime.callable.atom.unboxing.Layout; import org.enso.interpreter.runtime.callable.atom.unboxing.Layout;
import org.enso.interpreter.runtime.EnsoContext; import org.enso.interpreter.runtime.EnsoContext;
import org.enso.interpreter.runtime.callable.Annotation;
import org.enso.interpreter.runtime.callable.argument.ArgumentDefinition; import org.enso.interpreter.runtime.callable.argument.ArgumentDefinition;
import org.enso.interpreter.runtime.callable.function.Function; import org.enso.interpreter.runtime.callable.function.Function;
import org.enso.interpreter.runtime.callable.function.FunctionSchema; import org.enso.interpreter.runtime.callable.function.FunctionSchema;
@ -50,7 +51,7 @@ public final class AtomConstructor implements TruffleObject {
/** /**
* Creates a new Atom constructor for a given name. The constructor is not valid until {@link * Creates a new Atom constructor for a given name. The constructor is not valid until {@link
* AtomConstructor#initializeFields(LocalScope, ExpressionNode[], ExpressionNode[], * AtomConstructor#initializeFields(LocalScope, ExpressionNode[], ExpressionNode[], Annotation[],
* ArgumentDefinition...)} is called. * ArgumentDefinition...)} is called.
* *
* @param name the name of the Atom constructor * @param name the name of the Atom constructor
@ -62,7 +63,7 @@ public final class AtomConstructor implements TruffleObject {
/** /**
* Creates a new Atom constructor for a given name. The constructor is not valid until {@link * Creates a new Atom constructor for a given name. The constructor is not valid until {@link
* AtomConstructor#initializeFields(LocalScope, ExpressionNode[], ExpressionNode[], * AtomConstructor#initializeFields(LocalScope, ExpressionNode[], ExpressionNode[], Annotation[],
* ArgumentDefinition...)} is called. * ArgumentDefinition...)} is called.
* *
* @param name the name of the Atom constructor * @param name the name of the Atom constructor
@ -95,7 +96,8 @@ public final class AtomConstructor implements TruffleObject {
for (int i = 0; i < args.length; i++) { for (int i = 0; i < args.length; i++) {
reads[i] = ReadArgumentNode.build(i, null); reads[i] = ReadArgumentNode.build(i, null);
} }
return initializeFields(LocalScope.root(), new ExpressionNode[0], reads, args); return initializeFields(
LocalScope.root(), new ExpressionNode[0], reads, new Annotation[0], args);
} }
/** /**
@ -111,6 +113,7 @@ public final class AtomConstructor implements TruffleObject {
LocalScope localScope, LocalScope localScope,
ExpressionNode[] assignments, ExpressionNode[] assignments,
ExpressionNode[] varReads, ExpressionNode[] varReads,
Annotation[] annotations,
ArgumentDefinition... args) { ArgumentDefinition... args) {
CompilerDirectives.transferToInterpreterAndInvalidate(); CompilerDirectives.transferToInterpreterAndInvalidate();
@ -122,7 +125,8 @@ public final class AtomConstructor implements TruffleObject {
if (Layout.isAritySupported(args.length)) { if (Layout.isAritySupported(args.length)) {
boxedLayout = Layout.create(args.length, 0); boxedLayout = Layout.create(args.length, 0);
} }
this.constructorFunction = buildConstructorFunction(localScope, assignments, varReads, args); this.constructorFunction =
buildConstructorFunction(localScope, assignments, varReads, annotations, args);
generateQualifiedAccessor(); generateQualifiedAccessor();
return this; return this;
} }
@ -144,6 +148,7 @@ public final class AtomConstructor implements TruffleObject {
LocalScope localScope, LocalScope localScope,
ExpressionNode[] assignments, ExpressionNode[] assignments,
ExpressionNode[] varReads, ExpressionNode[] varReads,
Annotation[] annotations,
ArgumentDefinition[] args) { ArgumentDefinition[] args) {
ExpressionNode instantiateNode = InstantiateNode.build(this, varReads); ExpressionNode instantiateNode = InstantiateNode.build(this, varReads);
@ -159,7 +164,7 @@ public final class AtomConstructor implements TruffleObject {
null, null,
false); false);
RootCallTarget callTarget = rootNode.getCallTarget(); RootCallTarget callTarget = rootNode.getCallTarget();
return new Function(callTarget, null, new FunctionSchema(args)); return new Function(callTarget, null, new FunctionSchema(annotations, args));
} }
private void generateQualifiedAccessor() { private void generateQualifiedAccessor() {

View File

@ -16,6 +16,7 @@ import org.enso.interpreter.node.callable.InteropApplicationNode;
import org.enso.interpreter.node.callable.dispatch.InvokeFunctionNode; import org.enso.interpreter.node.callable.dispatch.InvokeFunctionNode;
import org.enso.interpreter.node.expression.builtin.BuiltinRootNode; import org.enso.interpreter.node.expression.builtin.BuiltinRootNode;
import org.enso.interpreter.runtime.EnsoContext; import org.enso.interpreter.runtime.EnsoContext;
import org.enso.interpreter.runtime.callable.Annotation;
import org.enso.interpreter.runtime.callable.CallerInfo; import org.enso.interpreter.runtime.callable.CallerInfo;
import org.enso.interpreter.runtime.callable.argument.ArgumentDefinition; import org.enso.interpreter.runtime.callable.argument.ArgumentDefinition;
import org.enso.interpreter.runtime.data.Array; import org.enso.interpreter.runtime.data.Array;
@ -102,7 +103,8 @@ public final class Function implements TruffleObject {
public static Function fromBuiltinRootNodeWithCallerFrameAccess( public static Function fromBuiltinRootNodeWithCallerFrameAccess(
BuiltinRootNode node, ArgumentDefinition... args) { BuiltinRootNode node, ArgumentDefinition... args) {
RootCallTarget callTarget = node.getCallTarget(); RootCallTarget callTarget = node.getCallTarget();
FunctionSchema schema = new FunctionSchema(FunctionSchema.CallerFrameAccess.FULL, args); FunctionSchema schema =
new FunctionSchema(FunctionSchema.CallerFrameAccess.FULL, new Annotation[0], args);
return new Function(callTarget, null, schema); return new Function(callTarget, null, schema);
} }

View File

@ -3,9 +3,12 @@ package org.enso.interpreter.runtime.callable.function;
import com.oracle.truffle.api.CompilerDirectives; import com.oracle.truffle.api.CompilerDirectives;
import com.oracle.truffle.api.CompilerDirectives.CompilationFinal; import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
import org.enso.interpreter.node.callable.InvokeCallableNode; import org.enso.interpreter.node.callable.InvokeCallableNode;
import org.enso.interpreter.runtime.callable.Annotation;
import org.enso.interpreter.runtime.callable.argument.ArgumentDefinition; import org.enso.interpreter.runtime.callable.argument.ArgumentDefinition;
import org.enso.interpreter.runtime.callable.argument.CallArgumentInfo; import org.enso.interpreter.runtime.callable.argument.CallArgumentInfo;
import java.util.Arrays;
/** /**
* Holds the definition site argument information together with information on the partially applied * Holds the definition site argument information together with information on the partially applied
* arguments positions. * arguments positions.
@ -34,32 +37,35 @@ public final class FunctionSchema {
private final @CompilationFinal(dimensions = 1) ArgumentDefinition[] argumentInfos; private final @CompilationFinal(dimensions = 1) ArgumentDefinition[] argumentInfos;
private final @CompilationFinal(dimensions = 1) boolean[] hasPreApplied; private final @CompilationFinal(dimensions = 1) boolean[] hasPreApplied;
private final @CompilationFinal(dimensions = 1) CallArgumentInfo[] oversaturatedArguments; private final @CompilationFinal(dimensions = 1) CallArgumentInfo[] oversaturatedArguments;
private final @CompilationFinal(dimensions = 1) Annotation[] annotations;
private final boolean hasAnyPreApplied; private final boolean hasAnyPreApplied;
private final boolean hasOversaturatedArguments; private final boolean hasOversaturatedArguments;
private final CallerFrameAccess callerFrameAccess; private final CallerFrameAccess callerFrameAccess;
private final boolean isFullyApplied; private final boolean isFullyApplied;
/** /**
* Creates an {@link FunctionSchema} instance. * Creates an {@link FunctionSchema} instance.
* *
* @param callerFrameAccess the declaration of whether access to caller frame is required for this * @param callerFrameAccess the declaration of whether access to caller frame is required for this
* function * function.
* @param argumentInfos Definition site arguments information * @param argumentInfos Definition site arguments information.
* @param hasPreApplied A flags collection such that {@code hasPreApplied[i]} is true iff a * @param hasPreApplied A flags collection such that {@code hasPreApplied[i]} is true iff a
* function has a partially applied argument at position {@code i} * function has a partially applied argument at position {@code i}.
* @param oversaturatedArguments information about any unused, oversaturated arguments passed to * @param oversaturatedArguments information about any unused, oversaturated arguments passed to
* this function so far * this function so far.
* @param annotations the list of annotations defined on this function.
*/ */
public FunctionSchema( public FunctionSchema(
CallerFrameAccess callerFrameAccess, CallerFrameAccess callerFrameAccess,
ArgumentDefinition[] argumentInfos, ArgumentDefinition[] argumentInfos,
boolean[] hasPreApplied, boolean[] hasPreApplied,
CallArgumentInfo[] oversaturatedArguments) { CallArgumentInfo[] oversaturatedArguments,
Annotation[] annotations) {
this.argumentInfos = argumentInfos; this.argumentInfos = argumentInfos;
this.oversaturatedArguments = oversaturatedArguments; this.oversaturatedArguments = oversaturatedArguments;
this.hasPreApplied = hasPreApplied; this.hasPreApplied = hasPreApplied;
this.callerFrameAccess = callerFrameAccess; this.callerFrameAccess = callerFrameAccess;
this.annotations = annotations;
boolean hasAnyPreApplied = false; boolean hasAnyPreApplied = false;
for (boolean b : hasPreApplied) { for (boolean b : hasPreApplied) {
if (b) { if (b) {
@ -77,15 +83,20 @@ public final class FunctionSchema {
* Creates an {@link FunctionSchema} instance assuming the function has no partially applied * Creates an {@link FunctionSchema} instance assuming the function has no partially applied
* arguments. * arguments.
* *
* @param callerFrameAccess the declaration of need to access the caller frame from the function * @param callerFrameAccess the declaration of need to access the caller frame from the function.
* @param argumentInfos Definition site arguments information * @param argumentInfos Definition site arguments information.
* @param annotations the list of annotations defined on this function.
*/ */
public FunctionSchema(CallerFrameAccess callerFrameAccess, ArgumentDefinition... argumentInfos) { public FunctionSchema(
CallerFrameAccess callerFrameAccess,
Annotation[] annotations,
ArgumentDefinition... argumentInfos) {
this( this(
callerFrameAccess, callerFrameAccess,
argumentInfos, argumentInfos,
new boolean[argumentInfos.length], new boolean[argumentInfos.length],
new CallArgumentInfo[0]); new CallArgumentInfo[0],
annotations);
} }
/** /**
@ -94,10 +105,23 @@ public final class FunctionSchema {
* *
* <p>Caller frame access is assumed to be {@link CallerFrameAccess#NONE}. * <p>Caller frame access is assumed to be {@link CallerFrameAccess#NONE}.
* *
* @param annotations the list of annotations defined on this function.
* @param argumentInfos Definition site arguments information.
*/
public FunctionSchema(Annotation[] annotations, ArgumentDefinition... argumentInfos) {
this(CallerFrameAccess.NONE, annotations, argumentInfos);
}
/**
* Creates an {@link FunctionSchema} instance assuming the function has no annotations or
* partially applied arguments.
*
* <p>Caller frame access is assumed to be {@link CallerFrameAccess#NONE}.
*
* @param argumentInfos Definition site arguments information * @param argumentInfos Definition site arguments information
*/ */
public FunctionSchema(ArgumentDefinition... argumentInfos) { public FunctionSchema(ArgumentDefinition... argumentInfos) {
this(CallerFrameAccess.NONE, argumentInfos); this(CallerFrameAccess.NONE, new Annotation[0], argumentInfos);
} }
/** /**
@ -196,6 +220,25 @@ public final class FunctionSchema {
return callerFrameAccess; return callerFrameAccess;
} }
/** @return annotations defined on this function. */
public Annotation[] getAnnotations() {
return annotations;
}
/**
* Finds annotation by name.
*
* @param name the annotation name.
* @return the matching annotation expression.
*/
@CompilerDirectives.TruffleBoundary
public Annotation getAnnotation(String name) {
return Arrays.stream(annotations)
.filter(annotation -> annotation.getName().equals(name))
.findFirst()
.orElse(null);
}
/** /**
* Checks whether the function is already fully applied. * Checks whether the function is already fully applied.
* *

View File

@ -85,7 +85,8 @@ class Passes(
AliasAnalysis, AliasAnalysis,
DataflowAnalysis, DataflowAnalysis,
CachePreferenceAnalysis, CachePreferenceAnalysis,
UnusedBindings UnusedBindings,
GenericAnnotations
) )
) )
@ -96,7 +97,11 @@ class Passes(
* these dependencies. * these dependencies.
*/ */
val passOrdering: List[PassGroup] = passes.getOrElse( val passOrdering: List[PassGroup] = passes.getOrElse(
List(moduleDiscoveryPasses, globalTypingPasses, functionBodyPasses) List(
moduleDiscoveryPasses,
globalTypingPasses,
functionBodyPasses
)
) )
/** The ordered representation of all passes run by the compiler. */ /** The ordered representation of all passes run by the compiler. */

View File

@ -135,7 +135,10 @@ object AstToIr {
def translateModuleSymbol(inputAst: AST): Module.Scope.Definition = { def translateModuleSymbol(inputAst: AST): Module.Scope.Definition = {
inputAst match { inputAst match {
case AST.Ident.Annotation.any(annotation) => case AST.Ident.Annotation.any(annotation) =>
IR.Name.Annotation(annotation.name, getIdentifiedLocation(annotation)) IR.Name.BuiltinAnnotation(
annotation.name,
getIdentifiedLocation(annotation)
)
case AstView.Atom(consName, args) => case AstView.Atom(consName, args) =>
val newArgs = args.map(translateArgumentDefinition(_)) val newArgs = args.map(translateArgumentDefinition(_))
@ -306,16 +309,22 @@ object AstToIr {
inputAst match { inputAst match {
case AST.Ident.Cons.any(cons) => case AST.Ident.Cons.any(cons) =>
IR.Module.Scope.Definition IR.Module.Scope.Definition
.Data(buildName(cons), List(), getIdentifiedLocation(inputAst)) .Data(
buildName(cons),
List(),
List(),
getIdentifiedLocation(inputAst)
)
case AstView.SpacedList(AST.Ident.Cons.any(cons) :: args) => case AstView.SpacedList(AST.Ident.Cons.any(cons) :: args) =>
IR.Module.Scope.Definition IR.Module.Scope.Definition
.Data( .Data(
buildName(cons), buildName(cons),
args.map(translateArgumentDefinition(_)), args.map(translateArgumentDefinition(_)),
List(),
getIdentifiedLocation(inputAst) getIdentifiedLocation(inputAst)
) )
case AST.Ident.Annotation.any(ann) => case AST.Ident.Annotation.any(ann) =>
IR.Name.Annotation(ann.name, getIdentifiedLocation(ann)) IR.Name.BuiltinAnnotation(ann.name, getIdentifiedLocation(ann))
case AstView.FunctionSugar( case AstView.FunctionSugar(
AST.Ident.Var("foreign"), AST.Ident.Var("foreign"),
header, header,
@ -1040,7 +1049,7 @@ object AstToIr {
buildName(identifier) buildName(identifier)
} }
case AST.Ident.Annotation(name) => case AST.Ident.Annotation(name) =>
Name.Annotation(name, getIdentifiedLocation(identifier)) Name.BuiltinAnnotation(name, getIdentifiedLocation(identifier))
case AST.Ident.Cons(name) => case AST.Ident.Cons(name) =>
if (name == Constants.Names.SELF_TYPE_ARGUMENT) { if (name == Constants.Names.SELF_TYPE_ARGUMENT) {
Name.SelfType(getIdentifiedLocation(identifier)) Name.SelfType(getIdentifiedLocation(identifier))

View File

@ -23,6 +23,7 @@ import org.enso.compiler.pass.analyse.{
import org.enso.compiler.pass.optimise.ApplicationSaturation import org.enso.compiler.pass.optimise.ApplicationSaturation
import org.enso.compiler.pass.resolve.{ import org.enso.compiler.pass.resolve.{
ExpressionAnnotations, ExpressionAnnotations,
GenericAnnotations,
GlobalNames, GlobalNames,
MethodDefinitions, MethodDefinitions,
Patterns, Patterns,
@ -67,6 +68,7 @@ import org.enso.interpreter.runtime.callable.function.{
Function => RuntimeFunction Function => RuntimeFunction
} }
import org.enso.interpreter.runtime.callable.{ import org.enso.interpreter.runtime.callable.{
Annotation => RuntimeAnnotation,
UnresolvedConversion, UnresolvedConversion,
UnresolvedSymbol UnresolvedSymbol
} }
@ -80,6 +82,7 @@ import org.enso.interpreter.runtime.scope.{
import org.enso.interpreter.{Constants, EnsoLanguage} import org.enso.interpreter.{Constants, EnsoLanguage}
import java.math.BigInteger import java.math.BigInteger
import scala.annotation.tailrec import scala.annotation.tailrec
import scala.collection.mutable import scala.collection.mutable
import scala.collection.mutable.ArrayBuffer import scala.collection.mutable.ArrayBuffer
@ -87,8 +90,7 @@ import scala.jdk.OptionConverters._
import scala.jdk.CollectionConverters._ import scala.jdk.CollectionConverters._
/** This is an implementation of a codegeneration pass that lowers the Enso /** This is an implementation of a codegeneration pass that lowers the Enso
* [[IR]] into the truffle [[org.enso.compiler.core.Core.Node]] structures that * [[IR]] into the truffle structures that are actually executed.
* are actually executed.
* *
* It should be noted that, as is, there is no support for cross-module links, * It should be noted that, as is, there is no support for cross-module links,
* with each lowering pass operating solely on a single module. * with each lowering pass operating solely on a single module.
@ -258,11 +260,42 @@ class IrToTruffle(
} }
val (assignments, reads) = argumentExpressions.unzip val (assignments, reads) = argumentExpressions.unzip
// build annotations
val annotations = atomDefn.annotations.map { annotation =>
val scopeElements = Seq(
tpDef.name.name,
atomDefn.name.name,
annotation.name
)
val scopeName =
scopeElements.mkString(Constants.SCOPE_SEPARATOR)
val expressionProcessor = new ExpressionProcessor(
scopeName,
scopeInfo.graph,
scopeInfo.graph.rootScope,
dataflowInfo
)
val expressionNode =
expressionProcessor.run(annotation.expression)
val closureName = s"<default::$scopeName>"
val closureRootNode = ClosureRootNode.build(
language,
expressionProcessor.scope,
moduleScope,
expressionNode,
makeSection(moduleScope, annotation.location),
closureName,
true,
false
)
new RuntimeAnnotation(annotation.name, closureRootNode)
}
if (!atomCons.isInitialized) { if (!atomCons.isInitialized) {
atomCons.initializeFields( atomCons.initializeFields(
localScope, localScope,
assignments.toArray, assignments.toArray,
reads.toArray, reads.toArray,
annotations.toArray,
argDefs: _* argDefs: _*
) )
} }
@ -384,7 +417,7 @@ class IrToTruffle(
.map(fOpt => .map(fOpt =>
// Register builtin iff it has not been automatically registered at an early stage // Register builtin iff it has not been automatically registered at an early stage
// of builtins initialization. // of builtins initialization.
fOpt.filter(m => !m.isAutoRegister()).map(m => m.getFunction) fOpt.filter(m => !m.isAutoRegister).map(m => m.getFunction)
) )
case fn: IR.Function => case fn: IR.Function =>
val bodyBuilder = val bodyBuilder =
@ -405,12 +438,63 @@ class IrToTruffle(
) )
val callTarget = rootNode.getCallTarget val callTarget = rootNode.getCallTarget
val arguments = bodyBuilder.args() val arguments = bodyBuilder.args()
// build annotations
val annotations =
methodDef.getMetadata(GenericAnnotations).toVector.flatMap {
meta =>
meta.annotations
.collect { case annotation: IR.Name.GenericAnnotation =>
val scopeElements = Seq(
cons.getName,
methodDef.methodName.name,
annotation.name
)
val scopeName =
scopeElements.mkString(Constants.SCOPE_SEPARATOR)
val scopeInfo = annotation
.unsafeGetMetadata(
AliasAnalysis,
s"Missing scope information for annotation " +
s"${annotation.name} of method " +
scopeElements.init.mkString(Constants.SCOPE_SEPARATOR)
)
.unsafeAs[AliasAnalysis.Info.Scope.Root]
val dataflowInfo = annotation.unsafeGetMetadata(
DataflowAnalysis,
"Missing dataflow information for annotation " +
s"${annotation.name} of method " +
scopeElements.init.mkString(Constants.SCOPE_SEPARATOR)
)
val expressionProcessor = new ExpressionProcessor(
scopeName,
scopeInfo.graph,
scopeInfo.graph.rootScope,
dataflowInfo
)
val expressionNode =
expressionProcessor.run(annotation.expression)
val closureName =
s"<default::${expressionProcessor.scopeName}>"
val closureRootNode = ClosureRootNode.build(
language,
expressionProcessor.scope,
moduleScope,
expressionNode,
makeSection(moduleScope, annotation.location),
closureName,
true,
false
)
new RuntimeAnnotation(annotation.name, closureRootNode)
}
}
Right( Right(
Some( Some(
new RuntimeFunction( new RuntimeFunction(
callTarget, callTarget,
null, null,
new FunctionSchema(arguments: _*) new FunctionSchema(annotations.toArray, arguments: _*)
) )
) )
) )
@ -1655,7 +1739,7 @@ class IrToTruffle(
* @param application the function application to generate code for * @param application the function application to generate code for
* @return the truffle nodes corresponding to `application` * @return the truffle nodes corresponding to `application`
*/ */
def processApplication( private def processApplication(
application: IR.Application, application: IR.Application,
subjectToInstrumentation: Boolean subjectToInstrumentation: Boolean
): RuntimeExpression = ): RuntimeExpression =

View File

@ -21,7 +21,6 @@ class FreshNameSupply {
/** Generates a name guaranteed not to exist in this program. /** Generates a name guaranteed not to exist in this program.
* *
* @param isReferent whether or not the name should be marked as referent.
* @param isMethod whether or not the name should represent a method name. * @param isMethod whether or not the name should represent a method name.
* @return a new name * @return a new name
*/ */

View File

@ -75,6 +75,7 @@ final class SuggestionBuilder[A: IndexedSource](
arguments, arguments,
_, _,
_, _,
_,
_ _
) => ) =>
buildAtomConstructor( buildAtomConstructor(

View File

@ -11,6 +11,7 @@ import org.enso.interpreter.epb.EpbParser
import org.enso.syntax.text.{AST, Debug, Location} import org.enso.syntax.text.{AST, Debug, Location}
import java.util.UUID import java.util.UUID
import scala.annotation.unused import scala.annotation.unused
/** [[IR]] is a temporary and fairly unsophisticated internal representation /** [[IR]] is a temporary and fairly unsophisticated internal representation
@ -1068,6 +1069,7 @@ object IR {
* *
* @param name the name of the atom * @param name the name of the atom
* @param arguments the arguments to the atom constructor * @param arguments the arguments to the atom constructor
* @param annotations the list of annotations
* @param location the source location that the node corresponds to * @param location the source location that the node corresponds to
* @param passData the pass metadata associated with this node * @param passData the pass metadata associated with this node
* @param diagnostics compiler diagnostics for this node * @param diagnostics compiler diagnostics for this node
@ -1075,6 +1077,7 @@ object IR {
sealed case class Data( sealed case class Data(
name: IR.Name, name: IR.Name,
arguments: List[DefinitionArgument], arguments: List[DefinitionArgument],
annotations: List[IR.Name.GenericAnnotation],
override val location: Option[IdentifiedLocation], override val location: Option[IdentifiedLocation],
override val passData: MetadataStorage = MetadataStorage(), override val passData: MetadataStorage = MetadataStorage(),
override val diagnostics: DiagnosticStorage = DiagnosticStorage() override val diagnostics: DiagnosticStorage = DiagnosticStorage()
@ -1086,6 +1089,7 @@ object IR {
* *
* @param name the name of the atom * @param name the name of the atom
* @param arguments the arguments to the atom constructor * @param arguments the arguments to the atom constructor
* @param annotations the list of annotations
* @param location the source location that the node corresponds to * @param location the source location that the node corresponds to
* @param passData the pass metadata associated with this node * @param passData the pass metadata associated with this node
* @param diagnostics compiler diagnostics for this node * @param diagnostics compiler diagnostics for this node
@ -1093,14 +1097,22 @@ object IR {
* @return a copy of `this`, updated with the specified values * @return a copy of `this`, updated with the specified values
*/ */
def copy( def copy(
name: IR.Name = name, name: IR.Name = name,
arguments: List[DefinitionArgument] = arguments, arguments: List[DefinitionArgument] = arguments,
location: Option[IdentifiedLocation] = location, annotations: List[IR.Name.GenericAnnotation] = annotations,
passData: MetadataStorage = passData, location: Option[IdentifiedLocation] = location,
diagnostics: DiagnosticStorage = diagnostics, passData: MetadataStorage = passData,
id: Identifier = id diagnostics: DiagnosticStorage = diagnostics,
id: Identifier = id
): Data = { ): Data = {
val res = Data(name, arguments, location, passData, diagnostics) val res = Data(
name,
arguments,
annotations,
location,
passData,
diagnostics
)
res.id = id res.id = id
res res
} }
@ -1142,17 +1154,19 @@ object IR {
/** @inheritdoc */ /** @inheritdoc */
override def mapExpressions(fn: Expression => Expression): Data = { override def mapExpressions(fn: Expression => Expression): Data = {
copy( copy(
name = name.mapExpressions(fn), name = name.mapExpressions(fn),
arguments = arguments.map(_.mapExpressions(fn)) arguments = arguments.map(_.mapExpressions(fn)),
annotations = annotations.map(_.mapExpressions(fn))
) )
} }
/** @inheritdoc */ /** @inheritdoc */
override def toString: String = override def toString: String =
s""" s"""
|IR.Module.Scope.Definition.Atom( |IR.Module.Scope.Definition.Data(
|name = $name, |name = $name,
|arguments = $arguments, |arguments = $arguments,
|annotations = $annotations,
|location = $location, |location = $location,
|passData = ${this.showPassData}, |passData = ${this.showPassData},
|diagnostics = $diagnostics, |diagnostics = $diagnostics,
@ -1161,7 +1175,7 @@ object IR {
|""".toSingleLine |""".toSingleLine
/** @inheritdoc */ /** @inheritdoc */
override def children: List[IR] = name :: arguments override def children: List[IR] = name :: arguments ::: annotations
/** @inheritdoc */ /** @inheritdoc */
override def showCode(indent: Int): String = { override def showCode(indent: Int): String = {
@ -2754,20 +2768,37 @@ object IR {
override def showCode(indent: Int): String = name override def showCode(indent: Int): String = name
} }
/** The representation of an annotation name. /** Base trait for annotations. */
sealed trait Annotation extends Name with IR.Module.Scope.Definition {
/** @inheritdoc */
override def mapExpressions(fn: Expression => Expression): Annotation
/** @inheritdoc */
override def setLocation(location: Option[IdentifiedLocation]): Annotation
/** @inheritdoc */
override def duplicate(
keepLocations: Boolean = true,
keepMetadata: Boolean = true,
keepDiagnostics: Boolean = true,
keepIdentifiers: Boolean = false
): Annotation
}
/** The representation of builtin annotation.
* *
* @param name the annotation text of the name * @param name the annotation text of the name
* @param location the source location that the node corresponds to * @param location the source location that the node corresponds to
* @param passData the pass metadata associated with this node * @param passData the pass metadata associated with this node
* @param diagnostics compiler diagnostics for this node * @param diagnostics compiler diagnostics for this node
*/ */
sealed case class Annotation( sealed case class BuiltinAnnotation(
override val name: String, override val name: String,
override val location: Option[IdentifiedLocation], override val location: Option[IdentifiedLocation],
override val passData: MetadataStorage = MetadataStorage(), override val passData: MetadataStorage = MetadataStorage(),
override val diagnostics: DiagnosticStorage = DiagnosticStorage() override val diagnostics: DiagnosticStorage = DiagnosticStorage()
) extends Name ) extends Annotation
with IR.Module.Scope.Definition
with IRKind.Primitive { with IRKind.Primitive {
override protected var id: Identifier = randomId override protected var id: Identifier = randomId
@ -2786,8 +2817,8 @@ object IR {
passData: MetadataStorage = passData, passData: MetadataStorage = passData,
diagnostics: DiagnosticStorage = diagnostics, diagnostics: DiagnosticStorage = diagnostics,
id: Identifier = id id: Identifier = id
): Annotation = { ): BuiltinAnnotation = {
val res = Annotation(name, location, passData, diagnostics) val res = BuiltinAnnotation(name, location, passData, diagnostics)
res.id = id res.id = id
res res
} }
@ -2798,7 +2829,7 @@ object IR {
keepMetadata: Boolean = true, keepMetadata: Boolean = true,
keepDiagnostics: Boolean = true, keepDiagnostics: Boolean = true,
keepIdentifiers: Boolean = false keepIdentifiers: Boolean = false
): Annotation = ): BuiltinAnnotation =
copy( copy(
location = if (keepLocations) location else None, location = if (keepLocations) location else None,
passData = passData =
@ -2811,17 +2842,19 @@ object IR {
/** @inheritdoc */ /** @inheritdoc */
override def setLocation( override def setLocation(
location: Option[IdentifiedLocation] location: Option[IdentifiedLocation]
): Annotation = ): BuiltinAnnotation =
copy(location = location) copy(location = location)
/** @inheritdoc */ /** @inheritdoc */
override def mapExpressions(fn: Expression => Expression): Annotation = override def mapExpressions(
fn: Expression => Expression
): BuiltinAnnotation =
this this
/** @inheritdoc */ /** @inheritdoc */
override def toString: String = override def toString: String =
s""" s"""
|IR.Name.Annotation( |IR.Name.BuiltinAnnotation(
|name = $name, |name = $name,
|location = $location, |location = $location,
|passData = ${this.showPassData}, |passData = ${this.showPassData},
@ -2834,7 +2867,97 @@ object IR {
override def children: List[IR] = List() override def children: List[IR] = List()
/** @inheritdoc */ /** @inheritdoc */
override def showCode(indent: Int): String = name override def showCode(indent: Int): String = s"@$name"
}
/** Common annotations of form `@name expression`.
*
* @param name the annotation text of the name
* @param expression the annotation expression
* @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
*/
sealed case class GenericAnnotation(
override val name: String,
expression: Expression,
override val location: Option[IdentifiedLocation],
override val passData: MetadataStorage = MetadataStorage(),
override val diagnostics: DiagnosticStorage = DiagnosticStorage()
) extends Annotation {
override protected var id: Identifier = randomId
/** Creates a copy of `this`.
*
* @param name the annotation text of the name
* @param expression the annotation expression
* @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
* @param id the identifier for the new node
* @return a copy of `this`, updated with the specified values
*/
def copy(
name: String = name,
expression: Expression = expression,
location: Option[IdentifiedLocation] = location,
passData: MetadataStorage = passData,
diagnostics: DiagnosticStorage = diagnostics,
id: Identifier = id
): GenericAnnotation = {
val res =
GenericAnnotation(name, expression, location, passData, diagnostics)
res.id = id
res
}
/** @inheritdoc */
override def duplicate(
keepLocations: Boolean = true,
keepMetadata: Boolean = true,
keepDiagnostics: Boolean = true,
keepIdentifiers: Boolean = false
): GenericAnnotation =
copy(
location = if (keepLocations) location else None,
passData =
if (keepMetadata) passData.duplicate else MetadataStorage(),
diagnostics =
if (keepDiagnostics) diagnostics.copy else DiagnosticStorage(),
id = if (keepIdentifiers) id else randomId
)
/** @inheritdoc */
override def setLocation(
location: Option[IdentifiedLocation]
): GenericAnnotation =
copy(location = location)
/** @inheritdoc */
override def mapExpressions(
fn: Expression => Expression
): GenericAnnotation =
copy(expression = fn(expression))
/** @inheritdoc */
override def toString: String =
s"""
|IR.Name.GenericAnnotation(
|name = $name,
|expression = $expression,
|location = $location,
|passData = ${this.showPassData},
|diagnostics = $diagnostics,
|id = $id
|)
|""".toSingleLine
/** @inheritdoc */
override def children: List[IR] = List(expression)
/** @inheritdoc */
override def showCode(indent: Int): String =
s"@$name ${expression.showCode(indent)}"
} }
/** A representation of the name `self`, used to refer to the current type. /** A representation of the name `self`, used to refer to the current type.
@ -4420,7 +4543,7 @@ object IR {
/** @inheritdoc */ /** @inheritdoc */
override def toString: String = override def toString: String =
s""" s"""
|IR.Function.Sugar( |IR.Function.Binding(
|name = $name, |name = $name,
|arguments = $arguments, |arguments = $arguments,
|body = $body, |body = $body,
@ -7512,7 +7635,7 @@ object IR {
/** A representation of an Enso syntax error. /** A representation of an Enso syntax error.
* *
* @param ast the erroneous AST * @param at the erroneous AST
* @param reason the cause of this error * @param reason the cause of this error
* @param passData the pass metadata associated with this node * @param passData the pass metadata associated with this node
* @param diagnostics compiler diagnostics for this node * @param diagnostics compiler diagnostics for this node
@ -8863,7 +8986,7 @@ object IR {
@throws[CompilerError] @throws[CompilerError]
def unsafeGetMetadata[K <: IRPass]( def unsafeGetMetadata[K <: IRPass](
pass: IRPass, pass: IRPass,
msg: String msg: => String
): pass.Metadata = { ): pass.Metadata = {
ir.passData.getUnsafe(pass)(msg) ir.passData.getUnsafe(pass)(msg)
} }

View File

@ -77,7 +77,7 @@ class MetadataStorage(
@throws[CompilerError] @throws[CompilerError]
def getUnsafe[K <: IRPass]( def getUnsafe[K <: IRPass](
pass: K pass: K
)(msg: String = s"Missing metadata for pass $pass"): pass.Metadata = { )(msg: => String = s"Missing metadata for pass $pass"): pass.Metadata = {
get(pass).getOrElse(throw new CompilerError(msg)) get(pass).getOrElse(throw new CompilerError(msg))
} }

View File

@ -28,7 +28,7 @@ class PassManager(
* @throws CompilerError if a valid pass ordering cannot be computed * @throws CompilerError if a valid pass ordering cannot be computed
* @return a valid pass ordering for the compiler, based on `passes` * @return a valid pass ordering for the compiler, based on `passes`
*/ */
def verifyPassOrdering(passes: List[IRPass]): List[IRPass] = { private def verifyPassOrdering(passes: List[IRPass]): List[IRPass] = {
var validPasses: Set[IRPass] = Set() var validPasses: Set[IRPass] = Set()
passes.foreach(pass => { passes.foreach(pass => {
@ -161,7 +161,7 @@ class PassManager(
* @param passGroup the pass group being run * @param passGroup the pass group being run
* @return `true` if the condition holds, otherwise `false` * @return `true` if the condition holds, otherwise `false`
*/ */
def isLastRunOf( private def isLastRunOf(
indexOfPassInGroup: Int, indexOfPassInGroup: Int,
pass: IRPass, pass: IRPass,
passGroup: PassGroup passGroup: PassGroup

View File

@ -14,7 +14,6 @@ import org.enso.syntax.text.Debug
import java.io.Serializable import java.io.Serializable
import scala.collection.mutable import scala.collection.mutable
import scala.collection.mutable.ListBuffer
import scala.reflect.ClassTag import scala.reflect.ClassTag
/** This pass performs scope identification and analysis, as well as symbol /** This pass performs scope identification and analysis, as well as symbol
@ -257,12 +256,23 @@ case object AliasAnalysis extends IRPass {
), ),
members = t.members.map(d => { members = t.members.map(d => {
val graph = new Graph val graph = new Graph
d.copy(arguments = d.copy(
analyseArgumentDefs( arguments = analyseArgumentDefs(
d.arguments, d.arguments,
graph, graph,
graph.rootScope graph.rootScope
) ),
annotations = d.annotations.map { ann =>
ann
.copy(
expression = analyseExpression(
ann.expression,
topLevelGraph,
topLevelGraph.rootScope
)
)
.updateMetadata(this -->> Info.Scope.Root(topLevelGraph))
}
).updateMetadata(this -->> Info.Scope.Root(graph)) ).updateMetadata(this -->> Info.Scope.Root(graph))
}) })
).updateMetadata(this -->> Info.Scope.Root(topLevelGraph)) ).updateMetadata(this -->> Info.Scope.Root(topLevelGraph))
@ -280,11 +290,21 @@ case object AliasAnalysis extends IRPass {
"Type signatures should not exist at the top level during " + "Type signatures should not exist at the top level during " +
"alias analysis." "alias analysis."
) )
case _: IR.Name.Annotation => case _: IR.Name.BuiltinAnnotation =>
throw new CompilerError( throw new CompilerError(
"Annotations should already be associated by the point of alias " + "Annotations should already be associated by the point of alias " +
"analysis." "analysis."
) )
case ann: IR.Name.GenericAnnotation =>
ann
.copy(expression =
analyseExpression(
ann.expression,
topLevelGraph,
topLevelGraph.rootScope
)
)
.updateMetadata(this -->> Info.Scope.Root(topLevelGraph))
case err: IR.Error => err case err: IR.Error => err
} }
} }
@ -561,7 +581,7 @@ case object AliasAnalysis extends IRPass {
* @param parentScope the scope in which the arguments are defined * @param parentScope the scope in which the arguments are defined
* @return `args`, with aliasing information attached to each argument * @return `args`, with aliasing information attached to each argument
*/ */
def analyseCallArguments( private def analyseCallArguments(
args: List[IR.CallArgument], args: List[IR.CallArgument],
graph: AliasAnalysis.Graph, graph: AliasAnalysis.Graph,
parentScope: AliasAnalysis.Graph.Scope parentScope: AliasAnalysis.Graph.Scope
@ -1207,7 +1227,8 @@ case object AliasAnalysis extends IRPass {
mapping.get(this) match { mapping.get(this) match {
case Some(newCorrespondingScope) => newCorrespondingScope case Some(newCorrespondingScope) => newCorrespondingScope
case None => case None =>
val childScopeCopies: mutable.ListBuffer[Scope] = ListBuffer() val childScopeCopies: mutable.ListBuffer[Scope] =
mutable.ListBuffer()
this.childScopes.foreach(scope => this.childScopes.foreach(scope =>
childScopeCopies += scope.deepCopy(mapping) childScopeCopies += scope.deepCopy(mapping)
) )

View File

@ -117,12 +117,13 @@ case object CachePreferenceAnalysis extends IRPass {
"Type signatures should not exist at the top level during " + "Type signatures should not exist at the top level during " +
"cache preference analysis." "cache preference analysis."
) )
case _: IR.Name.Annotation => case _: IR.Name.BuiltinAnnotation =>
throw new CompilerError( throw new CompilerError(
"Annotations should already be associated by the point of " + "Annotations should already be associated by the point of " +
"cache preference analysis." "cache preference analysis."
) )
case err: IR.Error => err case ann: IR.Name.GenericAnnotation => ann
case err: IR.Error => err
} }
/** Performs preference analysis on an arbitrary expression. /** Performs preference analysis on an arbitrary expression.

View File

@ -149,22 +149,21 @@ case object DataflowAnalysis extends IRPass {
info.dependencies.updateAt(tpDep, Set(paramDep)) info.dependencies.updateAt(tpDep, Set(paramDep))
analyseDefinitionArgument(param, info) analyseDefinitionArgument(param, info)
} }
val newMembers = members.map { val newMembers = members.map { data =>
case data @ IR.Module.Scope.Definition.Data(_, arguments, _, _, _) => val dataDep = asStatic(data)
val dataDep = asStatic(data) info.dependents.updateAt(dataDep, Set(tpDep))
info.dependents.updateAt(dataDep, Set(tpDep)) info.dependencies.updateAt(tpDep, Set(dataDep))
info.dependencies.updateAt(tpDep, Set(dataDep)) data.arguments.foreach(arg => {
arguments.foreach(arg => { val argDep = asStatic(arg)
val argDep = asStatic(arg) info.dependents.updateAt(argDep, Set(dataDep))
info.dependents.updateAt(argDep, Set(dataDep)) info.dependencies.updateAt(dataDep, Set(argDep))
info.dependencies.updateAt(dataDep, Set(argDep)) })
})
data data
.copy( .copy(
arguments = arguments.map(analyseDefinitionArgument(_, info)) arguments = data.arguments.map(analyseDefinitionArgument(_, info))
) )
.updateMetadata(this -->> info) .updateMetadata(this -->> info)
} }
tp.copy(params = newParams, members = newMembers) tp.copy(params = newParams, members = newMembers)
.updateMetadata(this -->> info) .updateMetadata(this -->> info)
@ -187,11 +186,15 @@ case object DataflowAnalysis extends IRPass {
"Type signatures should not exist at the top level during " + "Type signatures should not exist at the top level during " +
"dataflow analysis." "dataflow analysis."
) )
case _: IR.Name.Annotation => case _: IR.Name.BuiltinAnnotation =>
throw new CompilerError( throw new CompilerError(
"Annotations should already be associated by the point of " + "Annotations should already be associated by the point of " +
"dataflow analysis." "dataflow analysis."
) )
case ann: IR.Name.GenericAnnotation =>
ann
.copy(expression = analyseExpression(ann.expression, info))
.updateMetadata(this -->> info)
case err: IR.Error => err case err: IR.Error => err
} }
} }

View File

@ -88,7 +88,7 @@ case object TailCall extends IRPass {
* @param definition the top-level definition to analyse * @param definition the top-level definition to analyse
* @return `definition`, annotated with tail call information * @return `definition`, annotated with tail call information
*/ */
def analyseModuleBinding( private def analyseModuleBinding(
definition: IR.Module.Scope.Definition definition: IR.Module.Scope.Definition
): IR.Module.Scope.Definition = { ): IR.Module.Scope.Definition = {
definition match { definition match {
@ -126,11 +126,17 @@ case object TailCall extends IRPass {
"Type signatures should not exist at the top level during " + "Type signatures should not exist at the top level during " +
"tail call analysis." "tail call analysis."
) )
case _: IR.Name.Annotation => case _: IR.Name.BuiltinAnnotation =>
throw new CompilerError( throw new CompilerError(
"Annotations should already be associated by the point of " + "Annotations should already be associated by the point of " +
"tail call analysis." "tail call analysis."
) )
case ann: IR.Name.GenericAnnotation =>
ann
.copy(expression =
analyseExpression(ann.expression, isInTailPosition = true)
)
.updateMetadata(this -->> TailPosition.Tail)
case err: IR.Error => err case err: IR.Error => err
} }
} }

View File

@ -27,8 +27,6 @@ import org.enso.compiler.pass.resolve.{
} }
import org.enso.compiler.core.ir.MetadataStorage._ import org.enso.compiler.core.ir.MetadataStorage._
import scala.annotation.unused
/** Desugars complex type definitions to simple type definitions in the module /** Desugars complex type definitions to simple type definitions in the module
* scope. * scope.
* *
@ -75,7 +73,7 @@ case object ComplexType extends IRPass {
*/ */
override def runModule( override def runModule(
ir: IR.Module, ir: IR.Module,
@unused moduleContext: ModuleContext moduleContext: ModuleContext
): IR.Module = ): IR.Module =
ir.copy( ir.copy(
bindings = ir.bindings.flatMap { bindings = ir.bindings.flatMap {
@ -94,12 +92,12 @@ case object ComplexType extends IRPass {
*/ */
override def runExpression( override def runExpression(
ir: IR.Expression, ir: IR.Expression,
@unused inlineContext: InlineContext inlineContext: InlineContext
): IR.Expression = ir ): IR.Expression = ir
/** @inheritdoc */ /** @inheritdoc */
override def updateMetadataInDuplicate[T <: IR]( override def updateMetadataInDuplicate[T <: IR](
@unused sourceIr: T, sourceIr: T,
copyOfIr: T copyOfIr: T
): T = copyOfIr ): T = copyOfIr
@ -110,12 +108,25 @@ case object ComplexType extends IRPass {
* @param typ the type definition to desugar * @param typ the type definition to desugar
* @return the top-level definitions corresponding to the desugaring of `typ` * @return the top-level definitions corresponding to the desugaring of `typ`
*/ */
def desugarComplexType( private def desugarComplexType(
typ: IR.Module.Scope.Definition.SugaredType typ: IR.Module.Scope.Definition.SugaredType
): List[IR.Module.Scope.Definition] = { ): List[IR.Module.Scope.Definition] = {
val annotations = typ.getMetadata(ModuleAnnotations) val annotations = typ.getMetadata(ModuleAnnotations)
var lastAnnotations = Seq.empty[IR.Name.GenericAnnotation]
var seenAnnotations = Set.empty[IR.Name.GenericAnnotation]
val atomDefs = typ.body val atomDefs = typ.body
.collect { case d: IR.Module.Scope.Definition.Data => d } .flatMap {
case ann: IR.Name.GenericAnnotation =>
lastAnnotations :+= ann
None
case d: IR.Module.Scope.Definition.Data =>
val res = Some(d.copy(annotations = d.annotations ++ lastAnnotations))
seenAnnotations ++= lastAnnotations
lastAnnotations = Seq()
res
case _ =>
None
}
// TODO[MK] this is probably removable // TODO[MK] this is probably removable
.map(atom => .map(atom =>
annotations annotations
@ -133,6 +144,7 @@ case object ComplexType extends IRPass {
val remainingEntities = typ.body.filterNot { val remainingEntities = typ.body.filterNot {
case _: IR.Module.Scope.Definition.Data => true case _: IR.Module.Scope.Definition.Data => true
case ann: IR.Name.GenericAnnotation => seenAnnotations.contains(ann)
case _ => false case _ => false
} }
@ -185,7 +197,8 @@ case object ComplexType extends IRPass {
matchSignaturesAndGenerate(name, binding) matchSignaturesAndGenerate(name, binding)
case funSugar @ IR.Function.Binding(name, _, _, _, _, _, _) => case funSugar @ IR.Function.Binding(name, _, _, _, _, _, _) =>
matchSignaturesAndGenerate(name, funSugar) matchSignaturesAndGenerate(name, funSugar)
case err: IR.Error => Seq(err) case err: IR.Error => Seq(err)
case ann: IR.Name.GenericAnnotation => Seq(ann)
case _ => case _ =>
throw new CompilerError("Unexpected IR node in complex type body.") throw new CompilerError("Unexpected IR node in complex type body.")
} }
@ -217,11 +230,11 @@ case object ComplexType extends IRPass {
* The signature _must_ correctly match the method definition. * The signature _must_ correctly match the method definition.
* *
* @param ir the definition to generate a method from * @param ir the definition to generate a method from
* @param names the names on which the method is being defined * @param typeName the type name on which the method is being defined
* @param signature the type signature for the method, if it exists * @param signature the type signature for the method, if it exists
* @return `ir` as a method * @return `ir` as a method
*/ */
def genMethodDef( private def genMethodDef(
ir: IR, ir: IR,
typeName: IR.Name, typeName: IR.Name,
signature: Option[IR.Type.Ascription] signature: Option[IR.Type.Ascription]
@ -280,7 +293,7 @@ case object ComplexType extends IRPass {
* @param signature the method's type signature, if it exists * @param signature the method's type signature, if it exists
* @return a top-level method definition * @return a top-level method definition
*/ */
def genForName( private def genForName(
typeName: IR.Name, typeName: IR.Name,
name: IR.Name, name: IR.Name,
args: List[IR.DefinitionArgument], args: List[IR.DefinitionArgument],

View File

@ -18,8 +18,6 @@ import org.enso.compiler.pass.optimise.LambdaConsolidate
import org.enso.compiler.pass.resolve.IgnoredBindings import org.enso.compiler.pass.resolve.IgnoredBindings
import org.enso.interpreter.Constants import org.enso.interpreter.Constants
import scala.annotation.unused
/** This pass handles the desugaring of long-form function and method /** This pass handles the desugaring of long-form function and method
* definitions into standard bindings using lambdas. * definitions into standard bindings using lambdas.
* *
@ -64,7 +62,7 @@ case object FunctionBinding extends IRPass {
*/ */
override def runModule( override def runModule(
ir: IR.Module, ir: IR.Module,
@unused moduleContext: ModuleContext moduleContext: ModuleContext
): IR.Module = ir.copy(bindings = ir.bindings.map(desugarModuleSymbol)) ): IR.Module = ir.copy(bindings = ir.bindings.map(desugarModuleSymbol))
/** Runs desugaring of function bindings on an arbitrary expression. /** Runs desugaring of function bindings on an arbitrary expression.
@ -77,12 +75,12 @@ case object FunctionBinding extends IRPass {
*/ */
override def runExpression( override def runExpression(
ir: IR.Expression, ir: IR.Expression,
@unused inlineContext: InlineContext inlineContext: InlineContext
): IR.Expression = desugarExpression(ir) ): IR.Expression = desugarExpression(ir)
/** @inheritdoc */ /** @inheritdoc */
override def updateMetadataInDuplicate[T <: IR]( override def updateMetadataInDuplicate[T <: IR](
@unused sourceIr: T, sourceIr: T,
copyOfIr: T copyOfIr: T
): T = copyOfIr ): T = copyOfIr
@ -319,13 +317,14 @@ case object FunctionBinding extends IRPass {
"Documentation should not be present during function binding" + "Documentation should not be present during function binding" +
"desugaring." "desugaring."
) )
case _: IR.Name.Annotation => case _: IR.Name.BuiltinAnnotation =>
throw new CompilerError( throw new CompilerError(
"Annotations should already be associated by the point of " + "Annotations should already be associated by the point of " +
"function binding desugaring." "function binding desugaring."
) )
case a: IR.Type.Ascription => a case a: IR.Name.GenericAnnotation => a
case e: IR.Error => e case a: IR.Type.Ascription => a
case e: IR.Error => e
} }
} }
} }

View File

@ -9,8 +9,6 @@ import org.enso.compiler.exception.CompilerError
import org.enso.compiler.pass.IRPass import org.enso.compiler.pass.IRPass
import org.enso.compiler.pass.desugar.{ComplexType, GenerateMethodBodies} import org.enso.compiler.pass.desugar.{ComplexType, GenerateMethodBodies}
import scala.annotation.unused
/** Associates doc comments with the commented entities as metadata. /** Associates doc comments with the commented entities as metadata.
* *
* If the first module definition is a documentation comment, it is treated as * If the first module definition is a documentation comment, it is treated as
@ -61,7 +59,7 @@ case object DocumentationComments extends IRPass {
/** @inheritdoc */ /** @inheritdoc */
override def updateMetadataInDuplicate[T <: IR]( override def updateMetadataInDuplicate[T <: IR](
@unused sourceIr: T, sourceIr: T,
copyOfIr: T copyOfIr: T
): T = copyOfIr ): T = copyOfIr
@ -94,6 +92,8 @@ case object DocumentationComments extends IRPass {
private def resolveList[T <: IR](items: List[T]): List[T] = { private def resolveList[T <: IR](items: List[T]): List[T] = {
var lastDoc: Option[IR.Comment.Documentation] = None var lastDoc: Option[IR.Comment.Documentation] = None
items.flatMap { items.flatMap {
case annotation: IR.Name.Annotation =>
Some(annotation.asInstanceOf[T])
case doc: IR.Comment.Documentation => case doc: IR.Comment.Documentation =>
lastDoc = Some(doc) lastDoc = Some(doc)
None None
@ -158,10 +158,11 @@ case object DocumentationComments extends IRPass {
method.copy(body = resolveExpression(method.body)) method.copy(body = resolveExpression(method.body))
case tpe: IR.Module.Scope.Definition.SugaredType => case tpe: IR.Module.Scope.Definition.SugaredType =>
tpe.copy(body = resolveList(tpe.body).map(resolveIr)) tpe.copy(body = resolveList(tpe.body).map(resolveIr))
case doc: IR.Comment.Documentation => doc case doc: IR.Comment.Documentation => doc
case tySig: IR.Type.Ascription => tySig case tySig: IR.Type.Ascription => tySig
case err: IR.Error => err case err: IR.Error => err
case _: IR.Name.Annotation => case ann: IR.Name.GenericAnnotation => ann
case _: IR.Name.BuiltinAnnotation =>
throw new CompilerError( throw new CompilerError(
"Annotations should already be associated by the point of " + "Annotations should already be associated by the point of " +
"documentation comment resolution." "documentation comment resolution."

View File

@ -8,8 +8,6 @@ import org.enso.compiler.pass.IRPass
import org.enso.compiler.pass.analyse.AliasAnalysis import org.enso.compiler.pass.analyse.AliasAnalysis
import org.enso.compiler.pass.resolve.ModuleAnnotations.Annotations import org.enso.compiler.pass.resolve.ModuleAnnotations.Annotations
import scala.annotation.unused
case object ExpressionAnnotations extends IRPass { case object ExpressionAnnotations extends IRPass {
val tailCallName = "@Tail_Call" val tailCallName = "@Tail_Call"
val builtinMethodName = "@Builtin_Method" val builtinMethodName = "@Builtin_Method"
@ -62,7 +60,7 @@ case object ExpressionAnnotations extends IRPass {
/** @inheritdoc */ /** @inheritdoc */
override def updateMetadataInDuplicate[T <: IR]( override def updateMetadataInDuplicate[T <: IR](
@unused sourceIr: T, sourceIr: T,
copyOfIr: T copyOfIr: T
): T = copyOfIr ): T = copyOfIr
@ -71,7 +69,7 @@ case object ExpressionAnnotations extends IRPass {
): IR.Expression = ): IR.Expression =
ir.transformExpressions { ir.transformExpressions {
case app @ IR.Application.Prefix( case app @ IR.Application.Prefix(
ann: IR.Name.Annotation, ann: IR.Name.BuiltinAnnotation,
arguments, arguments,
_, _,
_, _,
@ -104,7 +102,7 @@ case object ExpressionAnnotations extends IRPass {
IR.Error.Resolution(ann, IR.Error.Resolution.UnknownAnnotation) IR.Error.Resolution(ann, IR.Error.Resolution.UnknownAnnotation)
app.copy(function = err) app.copy(function = err)
} }
case ann: IR.Name.Annotation => case ann: IR.Name.BuiltinAnnotation =>
if (isKnownAnnotation(ann.name)) { if (isKnownAnnotation(ann.name)) {
IR.Error.Resolution( IR.Error.Resolution(
ann, ann,
@ -120,7 +118,7 @@ case object ExpressionAnnotations extends IRPass {
* @param name the annotation name to check * @param name the annotation name to check
* @return `true` if `name` is a known annotation, otherwise `false` * @return `true` if `name` is a known annotation, otherwise `false`
*/ */
def isKnownAnnotation(name: String): Boolean = { private def isKnownAnnotation(name: String): Boolean = {
knownAnnotations.contains(name) knownAnnotations.contains(name)
} }
} }

View File

@ -0,0 +1,89 @@
package org.enso.compiler.pass.resolve
import org.enso.compiler.context.{InlineContext, ModuleContext}
import org.enso.compiler.core.IR
import org.enso.compiler.core.IR.Module.Scope.Definition
import org.enso.compiler.core.ir.MetadataStorage._
import org.enso.compiler.exception.CompilerError
import org.enso.compiler.pass.IRPass
/** A pass responsible for the discovery of [[IR.Name.GenericAnnotation]]
* annotations, and for associating them with the corresponding construct.
*
* Compilation pipeline of generic annotations:
* - [[ModuleAnnotations]] pass ignores generic annotations and leaves them in
* the tree so that the consequent passes are able to process the annotation
* expression.
* - [[org.enso.compiler.pass.desugar.ComplexType]] pass associates generic
* annotations with the type constructor definitions.
* - [[GenericAnnotations]] pass associates generic annotations that are left
* in the tree with the appropriate definitions.
*/
case object GenericAnnotations extends IRPass {
override type Metadata = ModuleAnnotations.Annotations
override type Config = IRPass.Configuration.Default
override val precursorPasses: Seq[IRPass] = Seq()
override val invalidatedPasses: Seq[IRPass] = Seq()
/** Resolves annotations.
*
* @param ir the Enso IR to process
* @param moduleContext a context object that contains the information needed
* to process a module
* @return `ir`, possibly having made transformations or annotations to that
* IR.
*/
override def runModule(
ir: IR.Module,
moduleContext: ModuleContext
): IR.Module = {
var lastAnnotations: Seq[IR.Name.GenericAnnotation] = Seq()
val newBindings = ir.bindings.map {
case _: IR.Name.BuiltinAnnotation =>
throw new CompilerError(
s"Builtin annotations should not be present at generic annotations pass."
)
case _: Definition.SugaredType =>
throw new CompilerError(
s"Sugared types should not be present at generic annotations pass."
)
case _: IR.Comment =>
throw new CompilerError(
"Comments should not be present at generic annotations pass."
)
case ann: IR.Name.GenericAnnotation =>
lastAnnotations :+= ann
None
case entity =>
val res = Some(
entity.updateMetadata(
this -->> ModuleAnnotations.Annotations(lastAnnotations)
)
)
lastAnnotations = Seq()
res
}
ir.copy(bindings = newBindings.flatten)
}
/** Execute the pass on an expression.
*
* As the pass only deals with module-level annotations this is a no-op.
*
* @param ir the Enso IR to process
* @param inlineContext a context object that contains the information needed
* for inline evaluation
* @return `ir`, possibly having made transformations or annotations to that
* IR.
*/
override def runExpression(
ir: IR.Expression,
inlineContext: InlineContext
): IR.Expression = ir
/** @inheritdoc */
override def updateMetadataInDuplicate[T <: IR](
sourceIr: T,
copyOfIr: T
): T = copyOfIr
}

View File

@ -15,8 +15,6 @@ import org.enso.compiler.pass.IRPass
import org.enso.compiler.pass.analyse.{AliasAnalysis, BindingAnalysis} import org.enso.compiler.pass.analyse.{AliasAnalysis, BindingAnalysis}
import org.enso.interpreter.Constants import org.enso.interpreter.Constants
import scala.annotation.unused
/** Resolves name occurences in non-pattern contexts. /** Resolves name occurences in non-pattern contexts.
* *
* 1. Attaches resolution metadata to encountered constructors, modules, * 1. Attaches resolution metadata to encountered constructors, modules,
@ -61,7 +59,7 @@ case object GlobalNames extends IRPass {
) )
val freshNameSupply = moduleContext.freshNameSupply.getOrElse( val freshNameSupply = moduleContext.freshNameSupply.getOrElse(
throw new CompilerError( throw new CompilerError(
"No fresh name supply passed to UppercaseNames resolver." "No fresh name supply passed to GlobalNames resolver."
) )
) )
val new_bindings = val new_bindings =
@ -96,7 +94,7 @@ case object GlobalNames extends IRPass {
/** @inheritdoc */ /** @inheritdoc */
override def updateMetadataInDuplicate[T <: IR]( override def updateMetadataInDuplicate[T <: IR](
@unused sourceIr: T, sourceIr: T,
copyOfIr: T copyOfIr: T
): T = copyOfIr ): T = copyOfIr
@ -139,7 +137,7 @@ case object GlobalNames extends IRPass {
freshNameSupply: FreshNameSupply, freshNameSupply: FreshNameSupply,
selfTypeResolution: Option[Resolution], selfTypeResolution: Option[Resolution],
isInsideApplication: Boolean = false isInsideApplication: Boolean = false
): IR.Expression = ): IR.Expression = {
ir.transformExpressions { ir.transformExpressions {
case selfTp: IR.Name.SelfType => case selfTp: IR.Name.SelfType =>
selfTypeResolution selfTypeResolution
@ -239,6 +237,7 @@ case object GlobalNames extends IRPass {
} }
} }
}
private def resolveReferantApplication( private def resolveReferantApplication(
app: IR.Application.Prefix, app: IR.Application.Prefix,

View File

@ -13,8 +13,6 @@ import org.enso.compiler.pass.desugar.{
GenerateMethodBodies GenerateMethodBodies
} }
import scala.annotation.unused
/** A pass responsible for the discovery of module annotations, and for /** A pass responsible for the discovery of module annotations, and for
* associating them with the corresponding construct. * associating them with the corresponding construct.
*/ */
@ -34,7 +32,7 @@ case object ModuleAnnotations extends IRPass {
* @param ir the Enso IR to process * @param ir the Enso IR to process
* @param moduleContext a context object that contains the information needed * @param moduleContext a context object that contains the information needed
* to process a module * to process a module
* @return `ir`, possibly having made transformations or annotations to that * @return `ir`, possibly having made transformations or annotations to that
* IR. * IR.
*/ */
override def runModule( override def runModule(
@ -42,27 +40,27 @@ case object ModuleAnnotations extends IRPass {
moduleContext: ModuleContext moduleContext: ModuleContext
): IR.Module = { ): IR.Module = {
var lastAnnotations: Seq[IR.Name.Annotation] = Seq() var lastAnnotations: Seq[IR.Name.Annotation] = Seq()
val newBindings = for (binding <- ir.bindings) yield { val newBindings = ir.bindings.map {
binding match { case ann: Name.BuiltinAnnotation =>
case ann: Name.Annotation => lastAnnotations :+= ann
lastAnnotations :+= ann None
None case ann: Name.GenericAnnotation =>
case comment: IR.Comment => Some(comment) Some(ann)
case typ: Definition.SugaredType => case comment: IR.Comment => Some(comment)
val res = Some( case typ: Definition.SugaredType =>
resolveComplexType(typ).updateMetadata( val res = Some(
this -->> Annotations(lastAnnotations) resolveComplexType(typ).updateMetadata(
) this -->> Annotations(lastAnnotations)
) )
lastAnnotations = Seq() )
res lastAnnotations = Seq()
case entity => res
val res = Some( case entity =>
entity.updateMetadata(this -->> Annotations(lastAnnotations)) val res = Some(
) entity.updateMetadata(this -->> Annotations(lastAnnotations))
lastAnnotations = Seq() )
res lastAnnotations = Seq()
} res
} }
ir.copy(bindings = newBindings.flatten) ir.copy(bindings = newBindings.flatten)
} }
@ -72,14 +70,16 @@ case object ModuleAnnotations extends IRPass {
* @param typ the type in which to resolve annotations * @param typ the type in which to resolve annotations
* @return `typ` with all top-level annotations resolved * @return `typ` with all top-level annotations resolved
*/ */
def resolveComplexType( private def resolveComplexType(
typ: Definition.SugaredType typ: Definition.SugaredType
): Definition.SugaredType = { ): Definition.SugaredType = {
var lastAnnotations: Seq[IR.Name.Annotation] = Seq() var lastAnnotations: Seq[IR.Name.Annotation] = Seq()
val newBodyElems = typ.body.flatMap { val newBodyElems = typ.body.flatMap {
case ann: Name.Annotation => case ann: Name.BuiltinAnnotation =>
lastAnnotations :+= ann lastAnnotations :+= ann
None None
case ann: Name.GenericAnnotation =>
Some(ann)
case comment: IR.Comment => Some(comment) case comment: IR.Comment => Some(comment)
case entity => case entity =>
val res = Some( val res = Some(
@ -98,17 +98,17 @@ case object ModuleAnnotations extends IRPass {
* @param ir the Enso IR to process * @param ir the Enso IR to process
* @param inlineContext a context object that contains the information needed * @param inlineContext a context object that contains the information needed
* for inline evaluation * for inline evaluation
* @return `ir`, possibly having made transformations or annotations to that * @return `ir`, possibly having made transformations or annotations to that
* IR. * IR.
*/ */
override def runExpression( override def runExpression(
ir: IR.Expression, ir: IR.Expression,
@unused inlineContext: InlineContext inlineContext: InlineContext
): IR.Expression = ir ): IR.Expression = ir
/** @inheritdoc */ /** @inheritdoc */
override def updateMetadataInDuplicate[T <: IR]( override def updateMetadataInDuplicate[T <: IR](
@unused sourceIr: T, sourceIr: T,
copyOfIr: T copyOfIr: T
): T = copyOfIr ): T = copyOfIr

View File

@ -2,10 +2,10 @@ package org.enso.compiler.pass.resolve
import org.enso.compiler.context.{InlineContext, ModuleContext} import org.enso.compiler.context.{InlineContext, ModuleContext}
import org.enso.compiler.core.IR import org.enso.compiler.core.IR
import org.enso.compiler.exception.CompilerError
import org.enso.compiler.pass.IRPass import org.enso.compiler.pass.IRPass
import org.enso.compiler.pass.desugar.{ComplexType, GenerateMethodBodies} import org.enso.compiler.pass.desugar.{ComplexType, GenerateMethodBodies}
import scala.annotation.unused
import scala.collection.mutable import scala.collection.mutable
/** This pass performs static detection of method overloads and emits errors /** This pass performs static detection of method overloads and emits errors
@ -45,59 +45,56 @@ case object OverloadsResolution extends IRPass {
*/ */
override def runModule( override def runModule(
ir: IR.Module, ir: IR.Module,
@unused moduleContext: ModuleContext moduleContext: ModuleContext
): IR.Module = { ): IR.Module = {
var seenTypes: Set[String] = Set() var seenTypes: Set[String] = Set()
var seenMethods: Map[Option[String], Set[(String, Boolean)]] = Map() var seenMethods: Map[Option[String], Set[(String, Boolean)]] = Map()
val types = ir.bindings.collect { val types = ir.bindings.collect {
case tp: IR.Module.Scope.Definition.Type => tp case tp: IR.Module.Scope.Definition.Type =>
}
val newTypes: List[IR.Module.Scope.Definition] = types.map(tp => {
if (seenTypes.contains(tp.name.name)) {
IR.Error.Redefined.Type(tp.name, tp.location)
} else {
seenTypes = seenTypes + tp.name.name
tp tp
} }
}) ir.bindings.collect {
val methods = ir.bindings.collect {
case meth: IR.Module.Scope.Definition.Method.Explicit => case meth: IR.Module.Scope.Definition.Method.Explicit =>
seenMethods = seenMethods + (meth.typeName.map(_.name) -> Set()) seenMethods += meth.typeName.map(_.name) -> Set()
meth meth
} }
val newMethods: List[IR.Module.Scope.Definition] = methods.map(method => {
if (
seenMethods(method.typeName.map(_.name))
.contains((method.methodName.name, method.isStatic))
) {
IR.Error.Redefined
.Method(method.typeName, method.methodName, method.location)
} else {
types.find(_.name.name.equals(method.methodName.name)) match {
case Some(clashedAtom) if method.typeName.isEmpty =>
IR.Error.Redefined.MethodClashWithAtom(
clashedAtom.name,
method.methodName,
method.location
)
case _ =>
val currentMethods: Set[(String, Boolean)] =
seenMethods(method.typeName.map(_.name))
seenMethods = seenMethods + (method.typeName.map(_.name) ->
(currentMethods + ((method.methodName.name, method.isStatic))))
method
}
}
})
val conversionsForType: mutable.Map[Option[String], Set[String]] = val conversionsForType: mutable.Map[Option[String], Set[String]] =
mutable.Map() mutable.Map()
val conversions: List[IR.Module.Scope.Definition] = ir.bindings.collect { val newBindings = ir.bindings.map {
case tp: IR.Module.Scope.Definition.Type =>
if (seenTypes.contains(tp.name.name)) {
IR.Error.Redefined.Type(tp.name, tp.location)
} else {
seenTypes += tp.name.name
tp
}
case method: IR.Module.Scope.Definition.Method.Explicit =>
if (
seenMethods(method.typeName.map(_.name))
.contains((method.methodName.name, method.isStatic))
) {
IR.Error.Redefined
.Method(method.typeName, method.methodName, method.location)
} else {
types.find(_.name.name.equals(method.methodName.name)) match {
case Some(clashedAtom) if method.typeName.isEmpty =>
IR.Error.Redefined.MethodClashWithAtom(
clashedAtom.name,
method.methodName,
method.location
)
case _ =>
val currentMethods: Set[(String, Boolean)] =
seenMethods(method.typeName.map(_.name))
seenMethods += (method.typeName.map(_.name) ->
(currentMethods + ((method.methodName.name, method.isStatic))))
method
}
}
case m: IR.Module.Scope.Definition.Method.Conversion => case m: IR.Module.Scope.Definition.Method.Conversion =>
val fromName = m.sourceTypeName.asInstanceOf[IR.Name] val fromName = m.sourceTypeName.asInstanceOf[IR.Name]
conversionsForType.get(m.typeName.map(_.name)) match { conversionsForType.get(m.typeName.map(_.name)) match {
@ -115,15 +112,32 @@ case object OverloadsResolution extends IRPass {
conversionsForType.put(m.typeName.map(_.name), Set(fromName.name)) conversionsForType.put(m.typeName.map(_.name), Set(fromName.name))
m m
} }
case diagnostic: IR.Diagnostic => diagnostic
case ann: IR.Name.GenericAnnotation => ann
case _: IR.Type.Ascription =>
throw new CompilerError(
"Type ascriptions should not be present during the overloads resolution."
)
case _: IR.Module.Scope.Definition.Method.Binding =>
throw new CompilerError(
"Method bindings should not be present during the overloads resolution."
)
case _: IR.Name.BuiltinAnnotation =>
throw new CompilerError(
"Builtin annotations should not be present during the overloads resolution."
)
case _: IR.Comment.Documentation =>
throw new CompilerError(
"Documentation comments should not be present during the overloads resolution."
)
case _: IR.Module.Scope.Definition.SugaredType =>
throw new CompilerError(
"Sugared types should not be present during the overloads resolution."
)
} }
val diagnostics = ir.bindings.collect { case diag: IR.Diagnostic => ir.copy(bindings = newBindings)
diag
}
ir.copy(
bindings = newTypes ::: newMethods ::: conversions ::: diagnostics
)
} }
/** This pass does nothing for the expression flow. /** This pass does nothing for the expression flow.
@ -141,7 +155,7 @@ case object OverloadsResolution extends IRPass {
/** @inheritdoc */ /** @inheritdoc */
override def updateMetadataInDuplicate[T <: IR]( override def updateMetadataInDuplicate[T <: IR](
@unused sourceIr: T, sourceIr: T,
copyOfIr: T copyOfIr: T
): T = copyOfIr ): T = copyOfIr
} }

View File

@ -13,8 +13,6 @@ import org.enso.compiler.pass.lint.UnusedBindings
import org.enso.compiler.pass.optimise.LambdaConsolidate import org.enso.compiler.pass.optimise.LambdaConsolidate
import org.enso.compiler.pass.resolve.TypeSignatures.Signature import org.enso.compiler.pass.resolve.TypeSignatures.Signature
import scala.annotation.unused
/** This pass is responsible for analysing type signatures to determine which /** This pass is responsible for analysing type signatures to determine which
* arguments in a function definition are suspended. * arguments in a function definition are suspended.
* *
@ -69,7 +67,7 @@ case object SuspendedArguments extends IRPass {
*/ */
override def runModule( override def runModule(
ir: IR.Module, ir: IR.Module,
@unused moduleContext: ModuleContext moduleContext: ModuleContext
): IR.Module = ): IR.Module =
ir.copy( ir.copy(
bindings = ir.bindings.map(resolveModuleBinding) bindings = ir.bindings.map(resolveModuleBinding)
@ -85,12 +83,12 @@ case object SuspendedArguments extends IRPass {
*/ */
override def runExpression( override def runExpression(
ir: IR.Expression, ir: IR.Expression,
@unused inlineContext: InlineContext inlineContext: InlineContext
): IR.Expression = resolveExpression(ir) ): IR.Expression = resolveExpression(ir)
/** @inheritdoc */ /** @inheritdoc */
override def updateMetadataInDuplicate[T <: IR]( override def updateMetadataInDuplicate[T <: IR](
@unused sourceIr: T, sourceIr: T,
copyOfIr: T copyOfIr: T
): T = copyOfIr ): T = copyOfIr
@ -104,7 +102,7 @@ case object SuspendedArguments extends IRPass {
* @param binding the top-level binding to resolve suspensions in * @param binding the top-level binding to resolve suspensions in
* @return `binding`, with any suspended arguments resolved * @return `binding`, with any suspended arguments resolved
*/ */
def resolveModuleBinding( private def resolveModuleBinding(
binding: IR.Module.Scope.Definition binding: IR.Module.Scope.Definition
): IR.Module.Scope.Definition = { ): IR.Module.Scope.Definition = {
binding match { binding match {
@ -193,11 +191,12 @@ case object SuspendedArguments extends IRPass {
throw new CompilerError("Type ascriptions should not be present.") throw new CompilerError("Type ascriptions should not be present.")
case _: IR.Comment => case _: IR.Comment =>
throw new CompilerError("Comments should not be present.") throw new CompilerError("Comments should not be present.")
case _: IR.Name.Annotation => case _: IR.Name.BuiltinAnnotation =>
throw new CompilerError( throw new CompilerError(
"Annotations should already be associated by the point of " + "Annotations should already be associated by the point of " +
"suspended arguments analysis." "suspended arguments analysis."
) )
case ann: IR.Name.GenericAnnotation => ann
} }
} }
@ -206,7 +205,7 @@ case object SuspendedArguments extends IRPass {
* @param expression the expression to perform resolution in * @param expression the expression to perform resolution in
* @return `expression`, with any suspended arguments resolved * @return `expression`, with any suspended arguments resolved
*/ */
def resolveExpression(expression: IR.Expression): IR.Expression = { private def resolveExpression(expression: IR.Expression): IR.Expression = {
expression.transformExpressions { expression.transformExpressions {
case bind @ IR.Expression.Binding(_, expr, _, _, _) => case bind @ IR.Expression.Binding(_, expr, _, _, _) =>
val newExpr = bind.getMetadata(TypeSignatures) match { val newExpr = bind.getMetadata(TypeSignatures) match {
@ -244,7 +243,7 @@ case object SuspendedArguments extends IRPass {
* @param signature the type signature to split * @param signature the type signature to split
* @return the segments of `signature` * @return the segments of `signature`
*/ */
def toSegments(signature: IR.Expression): List[IR.Expression] = { private def toSegments(signature: IR.Expression): List[IR.Expression] = {
signature match { signature match {
case IR.Type.Function(args, ret, _, _, _) => args :+ ret case IR.Type.Function(args, ret, _, _, _) => args :+ ret
case _ => List(signature) case _ => List(signature)
@ -270,7 +269,7 @@ case object SuspendedArguments extends IRPass {
* @param pair an argument and its corresponding type signature segment * @param pair an argument and its corresponding type signature segment
* @return the argument from `pair`, with its suspension marked appropriately * @return the argument from `pair`, with its suspension marked appropriately
*/ */
def markSuspended( private def markSuspended(
pair: (IR.DefinitionArgument, IR.Expression) pair: (IR.DefinitionArgument, IR.Expression)
): IR.DefinitionArgument = ): IR.DefinitionArgument =
pair match { pair match {
@ -289,7 +288,7 @@ case object SuspendedArguments extends IRPass {
* @param signature the signature of the function * @param signature the signature of the function
* @return `args`, appropriately marked as suspended or not * @return `args`, appropriately marked as suspended or not
*/ */
def computeSuspensions( private def computeSuspensions(
args: List[IR.DefinitionArgument], args: List[IR.DefinitionArgument],
signature: IR.Expression signature: IR.Expression
): List[IR.DefinitionArgument] = { ): List[IR.DefinitionArgument] = {

View File

@ -9,8 +9,6 @@ import org.enso.compiler.pass.IRPass
import org.enso.compiler.pass.analyse._ import org.enso.compiler.pass.analyse._
import org.enso.compiler.pass.lint.UnusedBindings import org.enso.compiler.pass.lint.UnusedBindings
import scala.annotation.unused
/** This pass is responsible for resolving type signatures and associating /** This pass is responsible for resolving type signatures and associating
* them as metadata with the typed object. * them as metadata with the typed object.
* *
@ -49,7 +47,7 @@ case object TypeSignatures extends IRPass {
*/ */
override def runModule( override def runModule(
ir: IR.Module, ir: IR.Module,
@unused moduleContext: ModuleContext moduleContext: ModuleContext
): IR.Module = resolveModule(ir) ): IR.Module = resolveModule(ir)
/** Resolves type signatures in an expression. /** Resolves type signatures in an expression.
@ -62,12 +60,12 @@ case object TypeSignatures extends IRPass {
*/ */
override def runExpression( override def runExpression(
ir: IR.Expression, ir: IR.Expression,
@unused inlineContext: InlineContext inlineContext: InlineContext
): IR.Expression = resolveExpression(ir) ): IR.Expression = resolveExpression(ir)
/** @inheritdoc */ /** @inheritdoc */
override def updateMetadataInDuplicate[T <: IR]( override def updateMetadataInDuplicate[T <: IR](
@unused sourceIr: T, sourceIr: T,
copyOfIr: T copyOfIr: T
): T = copyOfIr ): T = copyOfIr
@ -78,7 +76,7 @@ case object TypeSignatures extends IRPass {
* @param mod the module to resolve signatures in * @param mod the module to resolve signatures in
* @return `mod`, with type signatures resolved * @return `mod`, with type signatures resolved
*/ */
def resolveModule(mod: IR.Module): IR.Module = { private def resolveModule(mod: IR.Module): IR.Module = {
var lastSignature: Option[IR.Type.Ascription] = None var lastSignature: Option[IR.Type.Ascription] = None
val newBindings: List[IR.Module.Scope.Definition] = mod.bindings.flatMap { val newBindings: List[IR.Module.Scope.Definition] = mod.bindings.flatMap {
@ -133,13 +131,14 @@ case object TypeSignatures extends IRPass {
res res
case ut: IR.Module.Scope.Definition.Type => case ut: IR.Module.Scope.Definition.Type =>
Some(ut.mapExpressions(resolveExpression)) Some(ut.mapExpressions(resolveExpression))
case err: IR.Error => Some(err) case err: IR.Error => Some(err)
case ann: IR.Name.GenericAnnotation => Some(ann)
case _: IR.Module.Scope.Definition.SugaredType => case _: IR.Module.Scope.Definition.SugaredType =>
throw new CompilerError( throw new CompilerError(
"Complex type definitions should not be present during type " + "Complex type definitions should not be present during type " +
"signature resolution." "signature resolution."
) )
case _: IR.Name.Annotation => case _: IR.Name.BuiltinAnnotation =>
throw new CompilerError( throw new CompilerError(
"Annotations should already be associated by the point of " + "Annotations should already be associated by the point of " +
"type signature resolution." "type signature resolution."
@ -163,7 +162,7 @@ case object TypeSignatures extends IRPass {
* @param expr the expression to resolve signatures in * @param expr the expression to resolve signatures in
* @return `expr`, with any type signatures resolved * @return `expr`, with any type signatures resolved
*/ */
def resolveExpression(expr: IR.Expression): IR.Expression = { private def resolveExpression(expr: IR.Expression): IR.Expression = {
expr.transformExpressions { expr.transformExpressions {
case block: IR.Expression.Block => resolveBlock(block) case block: IR.Expression.Block => resolveBlock(block)
case sig: IR.Type.Ascription => resolveAscription(sig) case sig: IR.Type.Ascription => resolveAscription(sig)
@ -175,7 +174,7 @@ case object TypeSignatures extends IRPass {
* @param sig the signature to convert * @param sig the signature to convert
* @return the typed expression in `sig`, with `signature` attached * @return the typed expression in `sig`, with `signature` attached
*/ */
def resolveAscription(sig: IR.Type.Ascription): IR.Expression = { private def resolveAscription(sig: IR.Type.Ascription): IR.Expression = {
val newTyped = sig.typed.mapExpressions(resolveExpression) val newTyped = sig.typed.mapExpressions(resolveExpression)
val newSig = sig.signature.mapExpressions(resolveExpression) val newSig = sig.signature.mapExpressions(resolveExpression)
newTyped.updateMetadata(this -->> Signature(newSig)) newTyped.updateMetadata(this -->> Signature(newSig))
@ -186,7 +185,7 @@ case object TypeSignatures extends IRPass {
* @param block the block to resolve signatures in * @param block the block to resolve signatures in
* @return `block`, with any type signatures resolved * @return `block`, with any type signatures resolved
*/ */
def resolveBlock(block: IR.Expression.Block): IR.Expression.Block = { private def resolveBlock(block: IR.Expression.Block): IR.Expression.Block = {
var lastSignature: Option[IR.Type.Ascription] = None var lastSignature: Option[IR.Type.Ascription] = None
val allBlockExpressions = val allBlockExpressions =
block.expressions :+ block.returnValue block.expressions :+ block.returnValue

View File

@ -0,0 +1,97 @@
package org.enso.compiler;
import org.enso.compiler.core.IR$Function$Binding;
import org.enso.compiler.core.IR$Module$Scope$Definition$Data;
import org.enso.compiler.core.IR$Module$Scope$Definition$SugaredType;
import org.enso.compiler.core.IR$Name$Annotation;
import org.junit.Test;
import static org.junit.Assert.assertEquals;
public class AnnotationsCompilerTest extends CompilerTest {
@Test
public void testModuleMethod() throws Exception {
var ir = parse("""
@a expr
@b (x y)
foo a b = a b
""");
var annotation1 = (IR$Name$Annotation) ir.bindings().apply(0);
var annotation2 = (IR$Name$Annotation) ir.bindings().apply(1);
assertEquals(annotation1.name(), "a");
assertEquals(annotation2.name(), "b");
}
@Test
public void testExtensionMethod() throws Exception {
var ir = parse("""
@a expr
@b (x y)
Foo.foo a b = a b
""");
var annotation1 = (IR$Name$Annotation) ir.bindings().apply(0);
var annotation2 = (IR$Name$Annotation) ir.bindings().apply(1);
assertEquals(annotation1.name(), "a");
assertEquals(annotation2.name(), "b");
}
@Test
public void testTypeMethod() throws Exception {
var ir = parse("""
type Foo
@a foo
@b bar
method a b = a b
""");
var typeDefinition = (IR$Module$Scope$Definition$SugaredType) ir.bindings().apply(0);
var annotation1 = (IR$Name$Annotation) typeDefinition.body().apply(0);
var annotation2 = (IR$Name$Annotation) typeDefinition.body().apply(1);
var function = (IR$Function$Binding) typeDefinition.body().apply(2);
assertEquals(annotation1.name(), "a");
assertEquals(annotation2.name(), "b");
assertEquals(function.name().name(), "method");
}
@Test
public void testConstructor() throws Exception {
var ir = parse("""
type Foo
@a foo
@b bar
Cons a b = a b
""");
var typeDefinition = (IR$Module$Scope$Definition$SugaredType) ir.bindings().apply(0);
var annotation1 = (IR$Name$Annotation) typeDefinition.body().apply(0);
var annotation2 = (IR$Name$Annotation) typeDefinition.body().apply(1);
var constructor = (IR$Module$Scope$Definition$Data) typeDefinition.body().apply(2);
assertEquals(annotation1.name(), "a");
assertEquals(annotation2.name(), "b");
assertEquals(constructor.name().name(), "Cons");
}
@Test
public void testInvalidComplexType() throws Exception {
var ir = parse("""
type Foo
bar a =
""");
var typeDefinition = (IR$Module$Scope$Definition$SugaredType) ir.bindings().apply(0);
var method = (IR$Function$Binding) typeDefinition.body().apply(0);
assertEquals(method.name().name(), "bar");
// FIXME method body is null. Should be `IR.Error.Syntax.UnexpectedDeclarationInType`
assertEquals(method.body(), null);
}
}

View File

@ -0,0 +1,34 @@
package org.enso.compiler;
import com.oracle.truffle.api.source.Source;
import org.enso.compiler.core.IR;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import java.io.IOException;
import static org.junit.Assert.assertNotNull;
public abstract class CompilerTest {
protected static EnsoCompiler ensoCompiler;
@BeforeClass
public static void initEnsoCompiler() {
ensoCompiler = new EnsoCompiler();
}
@AfterClass
public static void closeEnsoCompiler() throws Exception {
ensoCompiler.close();
}
protected static IR.Module parse(String code) throws IOException {
var src =
Source.newBuilder("enso", code, "test-" + Integer.toHexString(code.hashCode()) + ".enso")
.build();
IR.Module ir = ensoCompiler.compile(src);
assertNotNull("IR was generated", ir);
return ir;
}
}

View File

@ -1229,7 +1229,6 @@ public class EnsoCompilerTest {
equivalenceTest("a = x", "a = SKIP FREEZE x + y"); equivalenceTest("a = x", "a = SKIP FREEZE x + y");
equivalenceTest("a = x", "a = SKIP FREEZE x.f"); equivalenceTest("a = x", "a = SKIP FREEZE x.f");
equivalenceTest("a = x", "a = SKIP FREEZE x.f y"); equivalenceTest("a = x", "a = SKIP FREEZE x.f y");
} }
@Test @Test

View File

@ -1,9 +1,5 @@
package org.enso.compiler; package org.enso.compiler;
import com.oracle.truffle.api.source.Source;
import java.io.IOException;
import org.enso.compiler.core.IR; import org.enso.compiler.core.IR;
import org.enso.compiler.core.IR$Error$Syntax; import org.enso.compiler.core.IR$Error$Syntax;
import org.enso.compiler.core.IR$Error$Syntax$InvalidEscapeSequence$; import org.enso.compiler.core.IR$Error$Syntax$InvalidEscapeSequence$;
@ -14,42 +10,28 @@ import org.enso.compiler.core.IR$Error$Syntax$UnrecognizedToken$;
import org.enso.compiler.core.IR$Error$Syntax$UnsupportedSyntax; import org.enso.compiler.core.IR$Error$Syntax$UnsupportedSyntax;
import org.enso.syntax.text.Location; import org.enso.syntax.text.Location;
import org.junit.AfterClass;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertTrue;
import org.junit.BeforeClass;
import org.junit.Test; import org.junit.Test;
import scala.collection.immutable.List; import scala.collection.immutable.List;
public class ErrorCompilerTest { public class ErrorCompilerTest extends CompilerTest {
private static EnsoCompiler ensoCompiler;
@BeforeClass
public static void initEnsoCompiler() {
ensoCompiler = new EnsoCompiler();
}
@AfterClass
public static void closeEnsoCompiler() throws Exception {
ensoCompiler.close();
}
@Test @Test
public void spaceRequired() throws Exception { public void spaceRequired() throws Exception {
var ir = parseTest("foo = if cond.x else.y"); var ir = parse("foo = if cond.x else.y");
assertSingleSyntaxError(ir, IR$Error$Syntax$UnexpectedExpression$.MODULE$, "Unexpected expression.", 6, 8); assertSingleSyntaxError(ir, IR$Error$Syntax$UnexpectedExpression$.MODULE$, "Unexpected expression.", 6, 8);
} }
@Test @Test
public void incompleteTypeDefinition() throws Exception { public void incompleteTypeDefinition() throws Exception {
var ir = parseTest("type"); var ir = parse("type");
assertSingleSyntaxError(ir, IR$Error$Syntax$UnexpectedExpression$.MODULE$, "Unexpected expression.", 0, 4); assertSingleSyntaxError(ir, IR$Error$Syntax$UnexpectedExpression$.MODULE$, "Unexpected expression.", 0, 4);
} }
@Test @Test
public void badCase1() throws Exception { public void badCase1() throws Exception {
var ir = parseTest(""" var ir = parse("""
foo = case x of foo = case x of
4 4
"""); """);
@ -58,7 +40,7 @@ public class ErrorCompilerTest {
@Test @Test
public void badCase2() throws Exception { public void badCase2() throws Exception {
var ir = parseTest(""" var ir = parse("""
foo = case x of foo = case x of
4 -> 4 ->
"""); """);
@ -67,7 +49,7 @@ public class ErrorCompilerTest {
@Test @Test
public void badCase3() throws Exception { public void badCase3() throws Exception {
var ir = parseTest(""" var ir = parse("""
foo = case x of foo = case x of
4-> 4->
"""); """);
@ -76,7 +58,7 @@ public class ErrorCompilerTest {
@Test @Test
public void badCase4() throws Exception { public void badCase4() throws Exception {
var ir = parseTest(""" var ir = parse("""
main = main =
case value of case value of
-1 ->"minus one" -1 ->"minus one"
@ -86,253 +68,253 @@ public class ErrorCompilerTest {
@Test @Test
public void malformedSequence1() throws Exception { public void malformedSequence1() throws Exception {
var ir = parseTest("(1, )"); var ir = parse("(1, )");
assertSingleSyntaxError(ir, IR$Error$Syntax$UnexpectedExpression$.MODULE$, "Unexpected expression.", 0, 5); assertSingleSyntaxError(ir, IR$Error$Syntax$UnexpectedExpression$.MODULE$, "Unexpected expression.", 0, 5);
} }
@Test @Test
public void malformedSequence2() throws Exception { public void malformedSequence2() throws Exception {
var ir = parseTest("foo = (1, )"); var ir = parse("foo = (1, )");
assertSingleSyntaxError(ir, IR$Error$Syntax$UnexpectedExpression$.MODULE$, "Unexpected expression.", 7, 9); assertSingleSyntaxError(ir, IR$Error$Syntax$UnexpectedExpression$.MODULE$, "Unexpected expression.", 7, 9);
} }
@Test @Test
public void unmatchedDemiliter1() throws Exception { public void unmatchedDemiliter1() throws Exception {
var ir = parseTest("("); var ir = parse("(");
assertSingleSyntaxError(ir, IR$Error$Syntax$UnexpectedExpression$.MODULE$, "Unexpected expression.", 0, 1); assertSingleSyntaxError(ir, IR$Error$Syntax$UnexpectedExpression$.MODULE$, "Unexpected expression.", 0, 1);
} }
@Test @Test
public void unmatchedDemiliter2() throws Exception { public void unmatchedDemiliter2() throws Exception {
var ir = parseTest(")"); var ir = parse(")");
assertSingleSyntaxError(ir, IR$Error$Syntax$UnexpectedExpression$.MODULE$, "Unexpected expression.", 0, 1); assertSingleSyntaxError(ir, IR$Error$Syntax$UnexpectedExpression$.MODULE$, "Unexpected expression.", 0, 1);
} }
@Test @Test
public void unmatchedDemiliter3() throws Exception { public void unmatchedDemiliter3() throws Exception {
var ir = parseTest("["); var ir = parse("[");
assertSingleSyntaxError(ir, IR$Error$Syntax$UnexpectedExpression$.MODULE$, "Unexpected expression.", 0, 1); assertSingleSyntaxError(ir, IR$Error$Syntax$UnexpectedExpression$.MODULE$, "Unexpected expression.", 0, 1);
} }
@Test @Test
public void unmatchedDemiliter4() throws Exception { public void unmatchedDemiliter4() throws Exception {
var ir = parseTest("["); var ir = parse("[");
assertSingleSyntaxError(ir, IR$Error$Syntax$UnexpectedExpression$.MODULE$, "Unexpected expression.", 0, 1); assertSingleSyntaxError(ir, IR$Error$Syntax$UnexpectedExpression$.MODULE$, "Unexpected expression.", 0, 1);
} }
@Test @Test
public void unmatchedDemiliter5() throws Exception { public void unmatchedDemiliter5() throws Exception {
var ir = parseTest("foo = ("); var ir = parse("foo = (");
assertSingleSyntaxError(ir, IR$Error$Syntax$UnexpectedExpression$.MODULE$, "Unexpected expression.", 6, 7); assertSingleSyntaxError(ir, IR$Error$Syntax$UnexpectedExpression$.MODULE$, "Unexpected expression.", 6, 7);
} }
@Test @Test
public void unmatchedDemiliter6() throws Exception { public void unmatchedDemiliter6() throws Exception {
var ir = parseTest("foo = )"); var ir = parse("foo = )");
assertSingleSyntaxError(ir, IR$Error$Syntax$UnexpectedExpression$.MODULE$, "Unexpected expression.", 6, 7); assertSingleSyntaxError(ir, IR$Error$Syntax$UnexpectedExpression$.MODULE$, "Unexpected expression.", 6, 7);
} }
@Test @Test
public void unmatchedDemiliter7() throws Exception { public void unmatchedDemiliter7() throws Exception {
var ir = parseTest("foo = ["); var ir = parse("foo = [");
assertSingleSyntaxError(ir, IR$Error$Syntax$UnexpectedExpression$.MODULE$, "Unexpected expression.", 6, 7); assertSingleSyntaxError(ir, IR$Error$Syntax$UnexpectedExpression$.MODULE$, "Unexpected expression.", 6, 7);
} }
@Test @Test
public void unmatchedDemiliter8() throws Exception { public void unmatchedDemiliter8() throws Exception {
var ir = parseTest("foo = ]"); var ir = parse("foo = ]");
assertSingleSyntaxError(ir, IR$Error$Syntax$UnexpectedExpression$.MODULE$, "Unexpected expression.", 6, 7); assertSingleSyntaxError(ir, IR$Error$Syntax$UnexpectedExpression$.MODULE$, "Unexpected expression.", 6, 7);
} }
@Test @Test
public void unexpectedSpecialOperator() throws Exception { public void unexpectedSpecialOperator() throws Exception {
var ir = parseTest("foo = 1, 2"); var ir = parse("foo = 1, 2");
assertSingleSyntaxError(ir, IR$Error$Syntax$UnexpectedExpression$.MODULE$, "Unexpected expression.", 6, 10); assertSingleSyntaxError(ir, IR$Error$Syntax$UnexpectedExpression$.MODULE$, "Unexpected expression.", 6, 10);
} }
@Test @Test
public void malformedImport1() throws Exception { public void malformedImport1() throws Exception {
var ir = parseTest("import"); var ir = parse("import");
assertSingleSyntaxError(ir, IR$Error$Syntax$UnexpectedExpression$.MODULE$, "Unexpected expression.", 0, 6); assertSingleSyntaxError(ir, IR$Error$Syntax$UnexpectedExpression$.MODULE$, "Unexpected expression.", 0, 6);
} }
@Test @Test
public void malformedImport2() throws Exception { public void malformedImport2() throws Exception {
var ir = parseTest("import as Foo"); var ir = parse("import as Foo");
assertSingleSyntaxError(ir, IR$Error$Syntax$UnexpectedExpression$.MODULE$, "Unexpected expression.", 0, 13); assertSingleSyntaxError(ir, IR$Error$Syntax$UnexpectedExpression$.MODULE$, "Unexpected expression.", 0, 13);
} }
@Test @Test
public void malformedImport3() throws Exception { public void malformedImport3() throws Exception {
var ir = parseTest("import Foo as Foo, Bar"); var ir = parse("import Foo as Foo, Bar");
assertSingleSyntaxError(ir, IR$Error$Syntax$InvalidImport$.MODULE$, "Imports must have a valid module path.", 14, 22); assertSingleSyntaxError(ir, IR$Error$Syntax$InvalidImport$.MODULE$, "Imports must have a valid module path.", 14, 22);
} }
@Test @Test
public void malformedImport4() throws Exception { public void malformedImport4() throws Exception {
var ir = parseTest("import Foo as Foo.Bar"); var ir = parse("import Foo as Foo.Bar");
assertSingleSyntaxError(ir, IR$Error$Syntax$InvalidImport$.MODULE$, "Imports must have a valid module path.", 14, 21); assertSingleSyntaxError(ir, IR$Error$Syntax$InvalidImport$.MODULE$, "Imports must have a valid module path.", 14, 21);
} }
@Test @Test
public void malformedImport5() throws Exception { public void malformedImport5() throws Exception {
var ir = parseTest("import Foo as"); var ir = parse("import Foo as");
assertSingleSyntaxError(ir, IR$Error$Syntax$InvalidImport$.MODULE$, "Imports must have a valid module path.", 13, 13); assertSingleSyntaxError(ir, IR$Error$Syntax$InvalidImport$.MODULE$, "Imports must have a valid module path.", 13, 13);
} }
@Test @Test
public void malformedImport6() throws Exception { public void malformedImport6() throws Exception {
var ir = parseTest("import Foo as Bar.Baz"); var ir = parse("import Foo as Bar.Baz");
assertSingleSyntaxError(ir, IR$Error$Syntax$InvalidImport$.MODULE$, "Imports must have a valid module path.", 14, 21); assertSingleSyntaxError(ir, IR$Error$Syntax$InvalidImport$.MODULE$, "Imports must have a valid module path.", 14, 21);
} }
@Test @Test
public void malformedImport7() throws Exception { public void malformedImport7() throws Exception {
var ir = parseTest("import Foo hiding"); var ir = parse("import Foo hiding");
assertSingleSyntaxError(ir, IR$Error$Syntax$InvalidImport$.MODULE$, "Imports must have a valid module path.", 17, 17); assertSingleSyntaxError(ir, IR$Error$Syntax$InvalidImport$.MODULE$, "Imports must have a valid module path.", 17, 17);
} }
@Test @Test
public void malformedImport8() throws Exception { public void malformedImport8() throws Exception {
var ir = parseTest("import Foo hiding X,"); var ir = parse("import Foo hiding X,");
assertSingleSyntaxError(ir, IR$Error$Syntax$InvalidImport$.MODULE$, "Imports must have a valid module path.", 18, 20); assertSingleSyntaxError(ir, IR$Error$Syntax$InvalidImport$.MODULE$, "Imports must have a valid module path.", 18, 20);
} }
@Test @Test
public void malformedImport9() throws Exception { public void malformedImport9() throws Exception {
var ir = parseTest("polyglot import Foo"); var ir = parse("polyglot import Foo");
assertSingleSyntaxError(ir, IR$Error$Syntax$UnrecognizedToken$.MODULE$, "Unrecognized token.", 0, 19); assertSingleSyntaxError(ir, IR$Error$Syntax$UnrecognizedToken$.MODULE$, "Unrecognized token.", 0, 19);
} }
@Test @Test
public void malformedImport10() throws Exception { public void malformedImport10() throws Exception {
var ir = parseTest("polyglot java import"); var ir = parse("polyglot java import");
assertSingleSyntaxError(ir, IR$Error$Syntax$UnexpectedExpression$.MODULE$, "Unexpected expression.", 0, 20); assertSingleSyntaxError(ir, IR$Error$Syntax$UnexpectedExpression$.MODULE$, "Unexpected expression.", 0, 20);
} }
@Test @Test
public void malformedImport11() throws Exception { public void malformedImport11() throws Exception {
var ir = parseTest("from import all"); var ir = parse("from import all");
assertSingleSyntaxError(ir, IR$Error$Syntax$InvalidImport$.MODULE$, "Imports must have a valid module path.", 4, 4); assertSingleSyntaxError(ir, IR$Error$Syntax$InvalidImport$.MODULE$, "Imports must have a valid module path.", 4, 4);
} }
@Test @Test
public void malformedImport12() throws Exception { public void malformedImport12() throws Exception {
var ir = parseTest("from Foo import all hiding"); var ir = parse("from Foo import all hiding");
assertSingleSyntaxError(ir, IR$Error$Syntax$InvalidImport$.MODULE$, "Imports must have a valid module path.", 26, 26); assertSingleSyntaxError(ir, IR$Error$Syntax$InvalidImport$.MODULE$, "Imports must have a valid module path.", 26, 26);
} }
@Test @Test
public void malformedImport13() throws Exception { public void malformedImport13() throws Exception {
var ir = parseTest("from Foo import all hiding X.Y"); var ir = parse("from Foo import all hiding X.Y");
assertSingleSyntaxError(ir, IR$Error$Syntax$InvalidImport$.MODULE$, "Imports must have a valid module path.", 27, 30); assertSingleSyntaxError(ir, IR$Error$Syntax$InvalidImport$.MODULE$, "Imports must have a valid module path.", 27, 30);
} }
@Test @Test
public void malformedExport1() throws Exception { public void malformedExport1() throws Exception {
var ir = parseTest("export"); var ir = parse("export");
assertSingleSyntaxError(ir, IR$Error$Syntax$UnexpectedExpression$.MODULE$, "Unexpected expression.", 0, 6); assertSingleSyntaxError(ir, IR$Error$Syntax$UnexpectedExpression$.MODULE$, "Unexpected expression.", 0, 6);
} }
@Test @Test
public void malformedExport2() throws Exception { public void malformedExport2() throws Exception {
var ir = parseTest("export as Foo"); var ir = parse("export as Foo");
assertSingleSyntaxError(ir, IR$Error$Syntax$UnexpectedExpression$.MODULE$, "Unexpected expression.", 0, 13); assertSingleSyntaxError(ir, IR$Error$Syntax$UnexpectedExpression$.MODULE$, "Unexpected expression.", 0, 13);
} }
@Test @Test
public void malformedExport3() throws Exception { public void malformedExport3() throws Exception {
var ir = parseTest("export Foo as Foo, Bar"); var ir = parse("export Foo as Foo, Bar");
assertSingleSyntaxError(ir, IR$Error$Syntax$InvalidImport$.MODULE$, "Imports must have a valid module path.", 14, 22); assertSingleSyntaxError(ir, IR$Error$Syntax$InvalidImport$.MODULE$, "Imports must have a valid module path.", 14, 22);
} }
@Test @Test
public void malformedExport4() throws Exception { public void malformedExport4() throws Exception {
var ir = parseTest("export Foo as Foo.Bar"); var ir = parse("export Foo as Foo.Bar");
assertSingleSyntaxError(ir, IR$Error$Syntax$InvalidImport$.MODULE$, "Imports must have a valid module path.", 14, 21); assertSingleSyntaxError(ir, IR$Error$Syntax$InvalidImport$.MODULE$, "Imports must have a valid module path.", 14, 21);
} }
@Test @Test
public void malformedExport5() throws Exception { public void malformedExport5() throws Exception {
var ir = parseTest("export Foo as"); var ir = parse("export Foo as");
assertSingleSyntaxError(ir, IR$Error$Syntax$InvalidImport$.MODULE$, "Imports must have a valid module path.", 13, 13); assertSingleSyntaxError(ir, IR$Error$Syntax$InvalidImport$.MODULE$, "Imports must have a valid module path.", 13, 13);
} }
@Test @Test
public void malformedExport6() throws Exception { public void malformedExport6() throws Exception {
var ir = parseTest("export Foo as Bar.Baz"); var ir = parse("export Foo as Bar.Baz");
assertSingleSyntaxError(ir, IR$Error$Syntax$InvalidImport$.MODULE$, "Imports must have a valid module path.", 14, 21); assertSingleSyntaxError(ir, IR$Error$Syntax$InvalidImport$.MODULE$, "Imports must have a valid module path.", 14, 21);
} }
@Test @Test
public void malformedExport7() throws Exception { public void malformedExport7() throws Exception {
var ir = parseTest("export Foo hiding"); var ir = parse("export Foo hiding");
assertSingleSyntaxError(ir, IR$Error$Syntax$InvalidImport$.MODULE$, "Imports must have a valid module path.", 7, 17); assertSingleSyntaxError(ir, IR$Error$Syntax$InvalidImport$.MODULE$, "Imports must have a valid module path.", 7, 17);
} }
@Test @Test
public void malformedExport8() throws Exception { public void malformedExport8() throws Exception {
var ir = parseTest("export Foo hiding X,"); var ir = parse("export Foo hiding X,");
assertSingleSyntaxError(ir, IR$Error$Syntax$InvalidImport$.MODULE$, "Imports must have a valid module path.", 7, 20); assertSingleSyntaxError(ir, IR$Error$Syntax$InvalidImport$.MODULE$, "Imports must have a valid module path.", 7, 20);
} }
@Test @Test
public void malformedExport9() throws Exception { public void malformedExport9() throws Exception {
var ir = parseTest("from export all"); var ir = parse("from export all");
assertSingleSyntaxError(ir, IR$Error$Syntax$InvalidImport$.MODULE$, "Imports must have a valid module path.", 4, 4); assertSingleSyntaxError(ir, IR$Error$Syntax$InvalidImport$.MODULE$, "Imports must have a valid module path.", 4, 4);
} }
@Test @Test
public void malformedExport10() throws Exception { public void malformedExport10() throws Exception {
var ir = parseTest("from Foo export all hiding"); var ir = parse("from Foo export all hiding");
assertSingleSyntaxError(ir, IR$Error$Syntax$InvalidImport$.MODULE$, "Imports must have a valid module path.", 26, 26); assertSingleSyntaxError(ir, IR$Error$Syntax$InvalidImport$.MODULE$, "Imports must have a valid module path.", 26, 26);
} }
@Test @Test
public void malformedExport11() throws Exception { public void malformedExport11() throws Exception {
var ir = parseTest("from Foo export all hiding X.Y"); var ir = parse("from Foo export all hiding X.Y");
assertSingleSyntaxError(ir, IR$Error$Syntax$InvalidImport$.MODULE$, "Imports must have a valid module path.", 27, 30); assertSingleSyntaxError(ir, IR$Error$Syntax$InvalidImport$.MODULE$, "Imports must have a valid module path.", 27, 30);
} }
@Test @Test
public void invalidToken1() throws Exception { public void invalidToken1() throws Exception {
var ir = parseTest("`"); var ir = parse("`");
assertSingleSyntaxError(ir, IR$Error$Syntax$UnexpectedExpression$.MODULE$, "Unexpected expression.", 0, 1); assertSingleSyntaxError(ir, IR$Error$Syntax$UnexpectedExpression$.MODULE$, "Unexpected expression.", 0, 1);
} }
@Test @Test
public void invalidToken2() throws Exception { public void invalidToken2() throws Exception {
var ir = parseTest("splice_outside_text = `"); var ir = parse("splice_outside_text = `");
assertSingleSyntaxError(ir, IR$Error$Syntax$UnexpectedExpression$.MODULE$, "Unexpected expression.", 22, 23); assertSingleSyntaxError(ir, IR$Error$Syntax$UnexpectedExpression$.MODULE$, "Unexpected expression.", 22, 23);
} }
@Test @Test
public void illegalForeignBody1() throws Exception { public void illegalForeignBody1() throws Exception {
var ir = parseTest("foreign 4"); var ir = parse("foreign 4");
assertSingleSyntaxError(ir, IR$Error$Syntax$UnexpectedExpression$.MODULE$, "Unexpected expression.", 0, 9); assertSingleSyntaxError(ir, IR$Error$Syntax$UnexpectedExpression$.MODULE$, "Unexpected expression.", 0, 9);
} }
@Test @Test
public void illegalForeignBody2() throws Exception { public void illegalForeignBody2() throws Exception {
var ir = parseTest("foreign 4 * 4"); var ir = parse("foreign 4 * 4");
assertSingleSyntaxError(ir, IR$Error$Syntax$UnexpectedExpression$.MODULE$, "Unexpected expression.", 0, 13); assertSingleSyntaxError(ir, IR$Error$Syntax$UnexpectedExpression$.MODULE$, "Unexpected expression.", 0, 13);
} }
@Test @Test
public void illegalForeignBody3() throws Exception { public void illegalForeignBody3() throws Exception {
var ir = parseTest("foreign foo = \"4\""); var ir = parse("foreign foo = \"4\"");
assertSingleSyntaxError(ir, IR$Error$Syntax$UnexpectedExpression$.MODULE$, "Unexpected expression.", 0, 17); assertSingleSyntaxError(ir, IR$Error$Syntax$UnexpectedExpression$.MODULE$, "Unexpected expression.", 0, 17);
} }
@Test @Test
public void illegalForeignBody4() throws Exception { public void illegalForeignBody4() throws Exception {
var ir = parseTest("foreign js foo = 4"); var ir = parse("foreign js foo = 4");
assertSingleSyntaxError(ir, IR$Error$Syntax$UnexpectedExpression$.MODULE$, "Unexpected expression.", 0, 18); assertSingleSyntaxError(ir, IR$Error$Syntax$UnexpectedExpression$.MODULE$, "Unexpected expression.", 0, 18);
} }
@Test @Test
public void illegalEscapeSequence() throws Exception { public void illegalEscapeSequence() throws Exception {
var ir = parseTest(""" var ir = parse("""
escape = 'wrong \\c sequence' escape = 'wrong \\c sequence'
"""); """);
assertSingleSyntaxError(ir, IR$Error$Syntax$InvalidEscapeSequence$.MODULE$.apply("wrong sequence"), "Invalid escape sequence wrong sequence.", 9, 28); assertSingleSyntaxError(ir, IR$Error$Syntax$InvalidEscapeSequence$.MODULE$.apply("wrong sequence"), "Invalid escape sequence wrong sequence.", 9, 28);
@ -340,7 +322,7 @@ public class ErrorCompilerTest {
@Test @Test
public void testNPE183814303() throws Exception { public void testNPE183814303() throws Exception {
var ir = parseTest(""" var ir = parse("""
from Standard.Base import all from Standard.Base import all
main = main =
@ -353,7 +335,7 @@ public class ErrorCompilerTest {
@Test @Test
public void testNPE183863754() throws Exception { public void testNPE183863754() throws Exception {
var ir = parseTest(""" var ir = parse("""
main = main =
# meh # meh
42 42
@ -380,13 +362,4 @@ public class ErrorCompilerTest {
assertEquals("Expecting errors: " + errors, count, errors.size()); assertEquals("Expecting errors: " + errors, count, errors.size());
return errors; return errors;
} }
private static IR.Module parseTest(String code) throws IOException {
var src =
Source.newBuilder("enso", code, "test-" + Integer.toHexString(code.hashCode()) + ".enso")
.build();
var ir = ensoCompiler.compile(src);
assertNotNull("IR was generated", ir);
return ir;
}
} }

View File

@ -1,5 +1,6 @@
package org.enso.compiler.test package org.enso.compiler.test
import org.enso.compiler.EnsoCompiler
import org.enso.compiler.codegen.AstToIr import org.enso.compiler.codegen.AstToIr
import org.enso.compiler.context.{FreshNameSupply, InlineContext, ModuleContext} import org.enso.compiler.context.{FreshNameSupply, InlineContext, ModuleContext}
import org.enso.compiler.core.IR import org.enso.compiler.core.IR
@ -18,6 +19,8 @@ import org.enso.pkg.QualifiedName
trait CompilerTest extends AnyWordSpecLike with Matchers with CompilerRunner trait CompilerTest extends AnyWordSpecLike with Matchers with CompilerRunner
trait CompilerRunner { trait CompilerRunner {
def useRustParser: Boolean = false
// === IR Utilities ========================================================= // === IR Utilities =========================================================
/** Adds an extension method for converting a string to its AST /** Adds an extension method for converting a string to its AST
@ -61,7 +64,13 @@ trait CompilerRunner {
* @return the [[IR]] representing [[source]] * @return the [[IR]] representing [[source]]
*/ */
def toIrModule: IR.Module = { def toIrModule: IR.Module = {
AstToIr.translate(source.toAst) if (useRustParser) {
val compiler = new EnsoCompiler()
try compiler.compile(source)
finally compiler.close()
} else {
AstToIr.translate(source.toAst)
}
} }
} }
@ -208,6 +217,7 @@ trait CompilerRunner {
None None
) )
), ),
List(),
None None
) )
} }

View File

@ -1041,7 +1041,7 @@ class AstToIrTest extends CompilerTest with Inside {
|type Foo a b |type Foo a b
|""".stripMargin.toIrModule |""".stripMargin.toIrModule
ir.bindings.head shouldBe an[IR.Name.Annotation] ir.bindings.head shouldBe an[IR.Name.BuiltinAnnotation]
ir.bindings(1) shouldBe an[IR.Module.Scope.Definition.SugaredType] ir.bindings(1) shouldBe an[IR.Module.Scope.Definition.SugaredType]
} }
@ -1059,8 +1059,8 @@ class AstToIrTest extends CompilerTest with Inside {
val complexType = val complexType =
ir.bindings.head.asInstanceOf[IR.Module.Scope.Definition.SugaredType] ir.bindings.head.asInstanceOf[IR.Module.Scope.Definition.SugaredType]
complexType.body.head shouldBe an[IR.Name.Annotation] complexType.body.head shouldBe an[IR.Name.BuiltinAnnotation]
complexType.body(2) shouldBe an[IR.Name.Annotation] complexType.body(2) shouldBe an[IR.Name.BuiltinAnnotation]
} }
} }

View File

@ -12,6 +12,8 @@ class ModuleAnnotationsTest extends CompilerTest {
// === Test Setup =========================================================== // === Test Setup ===========================================================
override val useRustParser = true
val passes = new Passes(defaultConfig) val passes = new Passes(defaultConfig)
val precursorPasses: PassGroup = passes.getPrecursors(ModuleAnnotations).get val precursorPasses: PassGroup = passes.getPrecursors(ModuleAnnotations).get
@ -115,60 +117,62 @@ class ModuleAnnotationsTest extends CompilerTest {
anns.length shouldEqual 1 anns.length shouldEqual 1
anns.head.asInstanceOf[IR.Name].name shouldEqual "@My_Annotation" anns.head.asInstanceOf[IR.Name].name shouldEqual "@My_Annotation"
} }
"not associate generic annotations with method definitions" in {
val ir =
"""@a expr_1
|@b expr_2
|My_Type.add a b = this.frob(a + b)
|""".stripMargin.preprocessModule.resolve
ir.bindings.length shouldEqual 3
ir.bindings(0) shouldBe a[IR.Name.GenericAnnotation]
ir.bindings(1) shouldBe a[IR.Name.GenericAnnotation]
}
} }
"Annotation desugaring in complex types" should { "Annotation desugaring in complex types" should {
implicit val moduleContext: ModuleContext = mkModuleContext implicit val moduleContext: ModuleContext = mkModuleContext
"associate annotations with atom definitions" in { "not associate generic annotations with atom definitions" in {
val ir = val ir =
"""@My_Annotation """@My_Annotation
|type Foo |type Foo
| @My_Annotation | @my annotation
| Bar | Bar
|""".stripMargin.preprocessModule.resolve |""".stripMargin.preprocessModule.resolve
ir.bindings.length shouldEqual 1 ir.bindings.length shouldEqual 1
ir.bindings.head shouldBe a[Definition.SugaredType] ir.bindings.head shouldBe a[Definition.SugaredType]
val typ = ir.bindings.head.asInstanceOf[Definition.SugaredType] val typ = ir.bindings.head.asInstanceOf[Definition.SugaredType]
typ.body.length shouldEqual 1 typ.body.length shouldEqual 2
typ.body.head shouldBe a[Definition.Data] typ.body(0) shouldBe an[IR.Name.GenericAnnotation]
typ.body.head typ.body(1) shouldBe a[Definition.Data]
.unsafeGetMetadata(ModuleAnnotations, "")
.annotations
.head
.asInstanceOf[IR.Name]
.name shouldEqual "@My_Annotation"
} }
"associate annotations with method definitions" in { "not associate generic annotations with method definitions" in {
val ir = val ir =
"""type Foo """type Foo
| Foo | Foo
| |
| @My_Annotation | @a expr
| my_method a = a | my_method a = a
|""".stripMargin.preprocessModule.resolve |""".stripMargin.preprocessModule.resolve
ir.bindings.length shouldEqual 1 ir.bindings.length shouldEqual 1
ir.bindings.head shouldBe a[Definition.SugaredType] ir.bindings.head shouldBe a[Definition.SugaredType]
val typ = ir.bindings.head.asInstanceOf[Definition.SugaredType] val typ = ir.bindings.head.asInstanceOf[Definition.SugaredType]
typ.body.length shouldEqual 2 typ.body.length shouldEqual 3
typ.body(1) shouldBe an[IR.Function.Binding] typ.body(1) shouldBe an[IR.Name.GenericAnnotation]
typ typ.body(2) shouldBe an[IR.Function.Binding]
.body(1)
.unsafeGetMetadata(ModuleAnnotations, "")
.annotations
.head
.asInstanceOf[IR.Name]
.name shouldEqual "@My_Annotation"
} }
"not associate annotations with comments" in { "not associate annotations with comments" in {
val ir = val ir =
""" """
|type Foo |type Foo
| @My_Annotation | @my annotation
| ## Doc comment | ## Doc comment
| Foo | Foo
|""".stripMargin.preprocessModule.resolve |""".stripMargin.preprocessModule.resolve
@ -176,16 +180,10 @@ class ModuleAnnotationsTest extends CompilerTest {
ir.bindings.length shouldEqual 1 ir.bindings.length shouldEqual 1
ir.bindings.head shouldBe a[Definition.SugaredType] ir.bindings.head shouldBe a[Definition.SugaredType]
val typ = ir.bindings.head.asInstanceOf[Definition.SugaredType] val typ = ir.bindings.head.asInstanceOf[Definition.SugaredType]
typ.body.length shouldEqual 2 typ.body.length shouldEqual 3
typ.body.head shouldBe an[IR.Comment] typ.body(0) shouldBe an[IR.Name.GenericAnnotation]
typ.body(1) shouldBe a[Definition.Data] typ.body(1) shouldBe an[IR.Comment]
typ typ.body(2) shouldBe a[Definition.Data]
.body(1)
.unsafeGetMetadata(ModuleAnnotations, "")
.annotations
.head
.asInstanceOf[IR.Name]
.name shouldEqual "@My_Annotation"
} }
} }
} }

View File

@ -14,10 +14,27 @@ import Standard.Test.Extensions
from Standard.Base.Error.Common import Uninitialized_State from Standard.Base.Error.Common import Uninitialized_State
type My_Type type My_Type
@foo (test_method)
@baz (My_Type.Value 1 2 3)
Value foo bar baz Value foo bar baz
@param (test_method 5 6)
first_method self param = param
second_method self param = param
@a (test_method 5)
@b (self -> self.foo)
other_method self a = a
@self ("se" + "lf")
My_Type.my_method self = self.foo + self.bar + self.baz My_Type.my_method self = self.foo + self.bar + self.baz
@a (test_method 3 4)
@b (Test_Type.Value 49)
@c (Error.throw "Error Value")
test_method a b = a + b
type Test_Type type Test_Type
Value x Value x
@ -172,6 +189,26 @@ spec =
(Test_Type.Value a)==(Test_Type.Value b) . should_be_true (Test_Type.Value a)==(Test_Type.Value b) . should_be_true
(Test_Type.Value a)==(Test_Type.Value c) . should_be_false (Test_Type.Value a)==(Test_Type.Value c) . should_be_false
Test.specify "get annotations" <|
Meta.get_annotation Meta_Spec "test_method" "a" . should_equal 7
Meta.get_annotation Meta_Spec "test_method" "b" . should_equal (Test_Type.Value 49)
Meta.get_annotation Meta_Spec "test_method" "c" . should_fail_with "Error Value"
Meta.get_annotation Meta_Spec "test_method" "x" . should_equal Nothing
value = My_Type.Value 99 "bar" True
Meta.get_annotation value "first_method" "param" . should_equal 11
Meta.get_annotation value "second_method" "param" . should_equal Nothing
Meta.get_annotation value "third_method" "param" . should_equal Nothing
Meta.get_annotation value "other_method" "a" 7 . should_equal 12
Meta.get_annotation value "other_method" "b" value . should_equal 99
Meta.get_annotation value "other_method" "c" . should_equal Nothing
Meta.get_annotation value "my_method" "self" . should_equal "self"
Meta.get_annotation value "Value" "foo" 7 8 . should_equal 15
Meta.get_annotation value "Value" "bar" . should_equal Nothing
Meta.get_annotation value "Value" "baz" . should_equal (My_Type.Value 1 2 3)
Test.group "Atom with holes" <| Test.group "Atom with holes" <|
Test.specify "construct and fill" <| Test.specify "construct and fill" <|
pair = Meta.atom_with_hole (e -> My_Type.Value 1 e 3) pair = Meta.atom_with_hole (e -> My_Type.Value 1 e 3)