mirror of
https://github.com/enso-org/enso.git
synced 2024-11-22 11:52:59 +03:00
Qualified names & uppercase name resolution (#1062)
This commit is contained in:
parent
c64298cb8e
commit
ddb43af5a2
@ -398,7 +398,12 @@ need to follow these steps:
|
||||
and add them to `truffleRunOptions` in [`build.sbt`](build.sbt). Remove the
|
||||
portion of these options after `suspend=y`, including the comma. They are
|
||||
placeholders that we don't use.
|
||||
6. Now, when you want to debug something, you can place a breakpoint as usual in
|
||||
6. Alternatively, certain tasks, such as `run`, `benchOnly` and `testOnly` can
|
||||
be used through the `withDebug` SBT command. For this to work, your remote
|
||||
configuration must specify the host of `localhost` and the port `5005`.
|
||||
The command syntax is `withDebug --debugger TASK_NAME -- TASK_PARAMETERS`,
|
||||
e.g. `withDebug --debugger testOnly -- *AtomConstructors*`.
|
||||
7. Now, when you want to debug something, you can place a breakpoint as usual in
|
||||
IntelliJ, and then execute your remote debugging configuration. Now, in the
|
||||
SBT shell, run a command to execute the code you want to debug (e.g.
|
||||
`testOnly *CurryingTest*`). This will open the standard debugger interface
|
||||
|
@ -25,6 +25,12 @@ public class AtomBenchmarks {
|
||||
main.mainFunction().value().execute(main.mainConstructor(), fixtures.million());
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
public void benchGenerateListQualified() {
|
||||
DefaultInterpreterRunner.MainMethod main = fixtures.generateListQualified();
|
||||
main.mainFunction().value().execute(main.mainConstructor(), fixtures.million());
|
||||
}
|
||||
|
||||
private void benchOnList(DefaultInterpreterRunner.MainMethod main) {
|
||||
main.mainFunction().value().execute(main.mainConstructor(), fixtures.millionElementList());
|
||||
}
|
||||
|
@ -29,6 +29,16 @@ class AtomFixtures extends DefaultInterpreterRunner {
|
||||
""".stripMargin
|
||||
val generateList = getMain(generateListCode)
|
||||
|
||||
val generateListQualifiedCode =
|
||||
"""
|
||||
|main = length ->
|
||||
| generator = acc -> i -> if i == 0 then acc else generator (Builtins.cons i acc) (i - 1)
|
||||
|
|
||||
| res = generator Builtins.nil length
|
||||
| res
|
||||
""".stripMargin
|
||||
val generateListQualified = getMain(generateListQualifiedCode)
|
||||
|
||||
val reverseListCode =
|
||||
"""
|
||||
|main = list ->
|
||||
|
@ -0,0 +1,39 @@
|
||||
package org.enso.interpreter.node.expression.atom;
|
||||
|
||||
import com.oracle.truffle.api.TruffleLanguage;
|
||||
import com.oracle.truffle.api.frame.VirtualFrame;
|
||||
import com.oracle.truffle.api.nodes.NodeInfo;
|
||||
import com.oracle.truffle.api.nodes.RootNode;
|
||||
import org.enso.interpreter.runtime.callable.atom.Atom;
|
||||
import org.enso.interpreter.runtime.callable.atom.AtomConstructor;
|
||||
import org.enso.interpreter.runtime.callable.function.Function;
|
||||
import org.enso.interpreter.runtime.state.Stateful;
|
||||
|
||||
@NodeInfo(
|
||||
shortName = "get_cons",
|
||||
description = "A base for auto-generated module-level atom constructor getters.")
|
||||
public class QualifiedAccessorNode extends RootNode {
|
||||
private final AtomConstructor atomConstructor;
|
||||
|
||||
/**
|
||||
* Creates a new instance of this node.
|
||||
*
|
||||
* @param language the current language instance.
|
||||
* @param atomConstructor the constructor to return.
|
||||
*/
|
||||
public QualifiedAccessorNode(TruffleLanguage<?> language, AtomConstructor atomConstructor) {
|
||||
super(language);
|
||||
this.atomConstructor = atomConstructor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes the node, returning the predefined constructor.
|
||||
*
|
||||
* @param frame current execution frame
|
||||
* @return the constant constructor
|
||||
*/
|
||||
public Stateful execute(VirtualFrame frame) {
|
||||
Object state = Function.ArgumentsHelper.getState(frame.getArguments());
|
||||
return new Stateful(state, atomConstructor);
|
||||
}
|
||||
}
|
@ -323,7 +323,7 @@ public class Module implements TruffleObject {
|
||||
Types.extractArguments(args, AtomConstructor.class, String.class);
|
||||
AtomConstructor cons = arguments.getFirst();
|
||||
String name = arguments.getSecond();
|
||||
return scope.getMethods().get(cons).get(name);
|
||||
return scope.getMethods().get(cons).get(name.toLowerCase());
|
||||
}
|
||||
|
||||
private static AtomConstructor getConstructor(ModuleScope scope, Object[] args)
|
||||
|
@ -13,6 +13,7 @@ import org.enso.interpreter.node.ExpressionNode;
|
||||
import org.enso.interpreter.node.callable.argument.ReadArgumentNode;
|
||||
import org.enso.interpreter.node.expression.atom.GetFieldNode;
|
||||
import org.enso.interpreter.node.expression.atom.InstantiateNode;
|
||||
import org.enso.interpreter.node.expression.atom.QualifiedAccessorNode;
|
||||
import org.enso.interpreter.node.expression.builtin.InstantiateAtomNode;
|
||||
import org.enso.interpreter.runtime.callable.argument.ArgumentDefinition;
|
||||
import org.enso.interpreter.runtime.callable.function.Function;
|
||||
@ -82,11 +83,26 @@ public class AtomConstructor implements TruffleObject {
|
||||
}
|
||||
|
||||
private void generateMethods(ArgumentDefinition[] args) {
|
||||
generateQualifiedAccessor();
|
||||
for (ArgumentDefinition arg : args) {
|
||||
definitionScope.registerMethod(this, arg.getName(), generateGetter(arg.getPosition()));
|
||||
}
|
||||
}
|
||||
|
||||
private void generateQualifiedAccessor() {
|
||||
QualifiedAccessorNode node = new QualifiedAccessorNode(null, this);
|
||||
RootCallTarget callTarget = Truffle.getRuntime().createCallTarget(node);
|
||||
Function function =
|
||||
new Function(
|
||||
callTarget,
|
||||
null,
|
||||
new FunctionSchema(
|
||||
FunctionSchema.CallStrategy.ALWAYS_DIRECT,
|
||||
new ArgumentDefinition(0, "this", ArgumentDefinition.ExecutionMode.EXECUTE)));
|
||||
definitionScope.registerMethod(
|
||||
definitionScope.getAssociatedType(), this.name.toLowerCase(), function);
|
||||
}
|
||||
|
||||
private Function generateGetter(int position) {
|
||||
GetFieldNode node = new GetFieldNode(null, position);
|
||||
RootCallTarget callTarget = Truffle.getRuntime().createCallTarget(node);
|
||||
|
@ -105,6 +105,7 @@ public class ModuleScope {
|
||||
* @param function the {@link Function} associated with this definition
|
||||
*/
|
||||
public void registerMethod(AtomConstructor atom, String method, Function function) {
|
||||
method = method.toLowerCase();
|
||||
Map<String, Function> methodMap = ensureMethodMapFor(atom);
|
||||
|
||||
if (methodMap.containsKey(method)) {
|
||||
@ -147,16 +148,17 @@ public class ModuleScope {
|
||||
*/
|
||||
@CompilerDirectives.TruffleBoundary
|
||||
public Function lookupMethodDefinition(AtomConstructor atom, String name) {
|
||||
Function definedWithAtom = atom.getDefinitionScope().getMethodMapFor(atom).get(name);
|
||||
String lowerName = name.toLowerCase();
|
||||
Function definedWithAtom = atom.getDefinitionScope().getMethodMapFor(atom).get(lowerName);
|
||||
if (definedWithAtom != null) {
|
||||
return definedWithAtom;
|
||||
}
|
||||
Function definedHere = getMethodMapFor(atom).get(name);
|
||||
Function definedHere = getMethodMapFor(atom).get(lowerName);
|
||||
if (definedHere != null) {
|
||||
return definedHere;
|
||||
}
|
||||
return imports.stream()
|
||||
.map(scope -> scope.getMethodMapFor(atom).get(name))
|
||||
.map(scope -> scope.getMethodMapFor(atom).get(lowerName))
|
||||
.filter(Objects::nonNull)
|
||||
.findFirst()
|
||||
.orElse(null);
|
||||
@ -179,6 +181,11 @@ public class ModuleScope {
|
||||
return methods;
|
||||
}
|
||||
|
||||
/** @return the polyglot symbols imported into this scope. */
|
||||
public Map<String, Object> getPolyglotSymbols() {
|
||||
return polyglotSymbols;
|
||||
}
|
||||
|
||||
public void reset() {
|
||||
imports = new HashSet<>();
|
||||
methods = new HashMap<>();
|
||||
|
@ -42,6 +42,8 @@ class Passes(passes: Option[List[PassGroup]] = None) {
|
||||
TypeFunctions,
|
||||
TypeSignatures,
|
||||
AliasAnalysis,
|
||||
UppercaseNames,
|
||||
AliasAnalysis,
|
||||
LambdaConsolidate,
|
||||
AliasAnalysis,
|
||||
SuspendedArguments,
|
||||
|
@ -116,7 +116,7 @@ object AstToIr {
|
||||
case AstView.Atom(consName, args) =>
|
||||
Module.Scope.Definition
|
||||
.Atom(
|
||||
Name.Literal(consName.name, getIdentifiedLocation(consName)),
|
||||
buildName(consName),
|
||||
args.map(translateArgumentDefinition(_)),
|
||||
getIdentifiedLocation(inputAst)
|
||||
)
|
||||
@ -131,7 +131,7 @@ object AstToIr {
|
||||
|
||||
if (containsAtomDefOrInclude && !hasArgs) {
|
||||
Module.Scope.Definition.Type(
|
||||
Name.Literal(typeName.name, getIdentifiedLocation(typeName)),
|
||||
buildName(typeName),
|
||||
args.map(translateArgumentDefinition(_)),
|
||||
translatedBody,
|
||||
getIdentifiedLocation(inputAst)
|
||||
@ -148,14 +148,9 @@ object AstToIr {
|
||||
val pathSegments = targetPath.collect {
|
||||
case AST.Ident.Cons.any(c) => c
|
||||
}
|
||||
val pathNames = pathSegments.map(c =>
|
||||
IR.Name.Literal(c.name, getIdentifiedLocation(c))
|
||||
)
|
||||
val pathNames = pathSegments.map(buildName)
|
||||
|
||||
val methodSegments = pathNames :+ Name.Literal(
|
||||
nameStr.name,
|
||||
getIdentifiedLocation(nameStr)
|
||||
)
|
||||
val methodSegments = pathNames :+ buildName(nameStr)
|
||||
|
||||
val typeSegments = methodSegments.init
|
||||
|
||||
@ -169,9 +164,7 @@ object AstToIr {
|
||||
)
|
||||
} else {
|
||||
val typeName = Name.Here(None)
|
||||
val methodName =
|
||||
Name.Literal(nameStr.name, getIdentifiedLocation(nameStr))
|
||||
|
||||
val methodName = buildName(nameStr)
|
||||
Name.MethodReference(
|
||||
typeName,
|
||||
methodName,
|
||||
@ -187,7 +180,7 @@ object AstToIr {
|
||||
)
|
||||
case AstView.FunctionSugar(name, args, body) =>
|
||||
val typeName = Name.Here(None)
|
||||
val methodName = Name.Literal(name.name, getIdentifiedLocation(name))
|
||||
val methodName = buildName(name)
|
||||
|
||||
val methodReference = Name.MethodReference(
|
||||
typeName,
|
||||
@ -206,7 +199,7 @@ object AstToIr {
|
||||
typed match {
|
||||
case AST.Ident.any(ident) =>
|
||||
val typeName = Name.Here(None)
|
||||
val methodName = Name.Literal(ident.name, getIdentifiedLocation(ident))
|
||||
val methodName = buildName(ident)
|
||||
val methodReference = Name.MethodReference(
|
||||
typeName,
|
||||
methodName,
|
||||
@ -322,7 +315,7 @@ object AstToIr {
|
||||
)
|
||||
case _ =>
|
||||
IR.Application.Prefix(
|
||||
IR.Name.Literal("negate", None),
|
||||
IR.Name.Literal("negate", isReferent = false, None),
|
||||
List(
|
||||
IR.CallArgument.Specified(
|
||||
None,
|
||||
@ -344,7 +337,7 @@ object AstToIr {
|
||||
case AstView
|
||||
.SuspendedBlock(name, block @ AstView.Block(lines, lastLine)) =>
|
||||
Expression.Binding(
|
||||
Name.Literal(name.name, getIdentifiedLocation(name)),
|
||||
buildName(name),
|
||||
Expression.Block(
|
||||
lines.map(translateExpression),
|
||||
translateExpression(lastLine),
|
||||
@ -366,7 +359,7 @@ object AstToIr {
|
||||
|
||||
// Note [Uniform Call Syntax Translation]
|
||||
Application.Prefix(
|
||||
translateExpression(name),
|
||||
translateIdent(name),
|
||||
(target :: validArguments).map(translateCallArgument),
|
||||
hasDefaultsSuspended = hasDefaultsSuspended,
|
||||
getIdentifiedLocation(inputAst)
|
||||
@ -556,7 +549,7 @@ object AstToIr {
|
||||
case AstView.AssignedArgument(left, right) =>
|
||||
CallArgument
|
||||
.Specified(
|
||||
Some(Name.Literal(left.name, getIdentifiedLocation(left))),
|
||||
Some(buildName(left)),
|
||||
translateExpression(right),
|
||||
getIdentifiedLocation(arg)
|
||||
)
|
||||
@ -631,7 +624,7 @@ object AstToIr {
|
||||
} else {
|
||||
Application.Operator.Binary(
|
||||
leftArg,
|
||||
Name.Literal(fn.name, getIdentifiedLocation(fn)),
|
||||
buildName(fn),
|
||||
rightArg,
|
||||
getIdentifiedLocation(callable)
|
||||
)
|
||||
@ -680,13 +673,13 @@ object AstToIr {
|
||||
} else {
|
||||
Application.Operator.Section.Left(
|
||||
leftArg,
|
||||
Name.Literal(left.opr.name, getIdentifiedLocation(left.opr)),
|
||||
buildName(left.opr),
|
||||
getIdentifiedLocation(left)
|
||||
)
|
||||
}
|
||||
case AST.App.Section.Sides.any(sides) =>
|
||||
Application.Operator.Section.Sides(
|
||||
Name.Literal(sides.opr.name, getIdentifiedLocation(sides.opr)),
|
||||
buildName(sides.opr),
|
||||
getIdentifiedLocation(sides)
|
||||
)
|
||||
case AST.App.Section.Right.any(right) =>
|
||||
@ -696,7 +689,7 @@ object AstToIr {
|
||||
Error.Syntax(section, Error.Syntax.NamedArgInSection)
|
||||
} else {
|
||||
Application.Operator.Section.Right(
|
||||
Name.Literal(right.opr.name, getIdentifiedLocation(right.opr)),
|
||||
buildName(right.opr),
|
||||
translateCallArgument(right.arg),
|
||||
getIdentifiedLocation(right)
|
||||
)
|
||||
@ -718,10 +711,10 @@ object AstToIr {
|
||||
} else if (name == "here") {
|
||||
Name.Here(getIdentifiedLocation(identifier))
|
||||
} else {
|
||||
Name.Literal(name, getIdentifiedLocation(identifier))
|
||||
buildName(identifier)
|
||||
}
|
||||
case AST.Ident.Cons(name) =>
|
||||
Name.Literal(name, getIdentifiedLocation(identifier))
|
||||
case AST.Ident.Cons(_) =>
|
||||
buildName(identifier)
|
||||
case AST.Ident.Blank(_) =>
|
||||
Name.Blank(getIdentifiedLocation(identifier))
|
||||
case AST.Ident.Opr.any(_) =>
|
||||
@ -802,9 +795,14 @@ object AstToIr {
|
||||
*/
|
||||
def translatePattern(pattern: AST): Pattern = {
|
||||
AstView.MaybeManyParensed.unapply(pattern).getOrElse(pattern) match {
|
||||
case AstView.ConstructorPattern(cons, fields) =>
|
||||
case AstView.ConstructorPattern(conses, fields) =>
|
||||
val irConses = conses.map(translateIdent(_).asInstanceOf[IR.Name])
|
||||
val name = irConses match {
|
||||
case List(n) => n
|
||||
case _ => IR.Name.Qualified(irConses, None)
|
||||
}
|
||||
Pattern.Constructor(
|
||||
translateIdent(cons).asInstanceOf[IR.Name],
|
||||
name,
|
||||
fields.map(translatePattern),
|
||||
getIdentifiedLocation(pattern)
|
||||
)
|
||||
@ -904,4 +902,13 @@ object AstToIr {
|
||||
throw new UnhandledEntity(comment, "processComment")
|
||||
}
|
||||
}
|
||||
|
||||
private def isReferant(ident: AST.Ident): Boolean =
|
||||
ident match {
|
||||
case AST.Ident.Cons.any(_) => true
|
||||
case _ => false
|
||||
}
|
||||
|
||||
private def buildName(ident: AST.Ident): IR.Name.Literal =
|
||||
IR.Name.Literal(ident.name, isReferant(ident), getIdentifiedLocation(ident))
|
||||
}
|
||||
|
@ -443,6 +443,13 @@ object AstView {
|
||||
|
||||
object MethodCall {
|
||||
|
||||
private def consToVar(ast: AST.Ident): AST.Ident =
|
||||
ast match {
|
||||
case AST.Ident.Cons(c) =>
|
||||
AST.Ident.Var(c).setLocation(ast.location).setID(ast.id)
|
||||
case _ => ast
|
||||
}
|
||||
|
||||
/** Matches on a method call.
|
||||
*
|
||||
* A method call has the form `<obj>.<fn-name> <args...>` where `<obj>` is
|
||||
@ -456,14 +463,14 @@ object AstView {
|
||||
def unapply(ast: AST): Option[(AST, AST.Ident, List[AST])] =
|
||||
ast match {
|
||||
case OperatorDot(target, Application(ConsOrVar(ident), args)) =>
|
||||
Some((target, ident, args))
|
||||
Some((target, consToVar(ident), args))
|
||||
case AST.App.Section.Left(
|
||||
MethodCall(target, ident, List()),
|
||||
susp @ SuspendDefaultsOperator(_)
|
||||
) =>
|
||||
Some((target, ident, List(susp)))
|
||||
case OperatorDot(target, ConsOrVar(ident)) =>
|
||||
Some((target, ident, List()))
|
||||
Some((target, consToVar(ident), List()))
|
||||
case _ => None
|
||||
}
|
||||
}
|
||||
@ -792,6 +799,17 @@ object AstView {
|
||||
}
|
||||
}
|
||||
|
||||
object QualifiedName {
|
||||
def unapply(ast: AST): Option[List[AST.Ident.Cons]] =
|
||||
ast match {
|
||||
case OperatorDot(l, AST.Ident.Cons.any(name)) =>
|
||||
unapply(l).map(_ :+ name)
|
||||
case AST.Ident.Cons.any(name) =>
|
||||
Some(List(name))
|
||||
case _ => None
|
||||
}
|
||||
}
|
||||
|
||||
object ConstructorPattern {
|
||||
|
||||
/** Matches on a constructor pattern.
|
||||
@ -803,12 +821,12 @@ object AstView {
|
||||
* @param ast the structure to try and match on
|
||||
* @return the pattern
|
||||
*/
|
||||
def unapply(ast: AST): Option[(AST.Ident, List[AST])] = {
|
||||
def unapply(ast: AST): Option[(List[AST.Ident.Cons], List[AST])] = {
|
||||
MaybeManyParensed.unapply(ast).getOrElse(ast) match {
|
||||
case AST.Ident.Cons.any(cons) => Some((cons, List()))
|
||||
case QualifiedName(cons) => Some((cons, List()))
|
||||
case SpacedList(elems) if elems.nonEmpty =>
|
||||
elems.head match {
|
||||
case AST.Ident.Cons.any(refName) =>
|
||||
case QualifiedName(refName) =>
|
||||
val allFieldsValid = elems.tail.forall {
|
||||
case Pattern(_) => true
|
||||
case _ => false
|
||||
|
@ -15,7 +15,11 @@ import org.enso.compiler.pass.analyse.{
|
||||
TailCall
|
||||
}
|
||||
import org.enso.compiler.pass.optimise.ApplicationSaturation
|
||||
import org.enso.compiler.pass.resolve.{MethodDefinitions, Patterns}
|
||||
import org.enso.compiler.pass.resolve.{
|
||||
MethodDefinitions,
|
||||
Patterns,
|
||||
UppercaseNames
|
||||
}
|
||||
import org.enso.interpreter.node.callable.argument.ReadArgumentNode
|
||||
import org.enso.interpreter.node.callable.function.{
|
||||
BlockNode,
|
||||
@ -60,7 +64,6 @@ import org.enso.interpreter.{Constants, Language}
|
||||
|
||||
import scala.collection.mutable
|
||||
import scala.collection.mutable.ArrayBuffer
|
||||
import scala.jdk.OptionConverters._
|
||||
|
||||
/** This is an implementation of a codegeneration pass that lowers the Enso
|
||||
* [[IR]] into the truffle [[org.enso.compiler.core.Core.Node]] structures that
|
||||
@ -205,12 +208,21 @@ class IrToTruffle(
|
||||
methodDef.methodReference.typePointer
|
||||
.getMetadata(MethodDefinitions)
|
||||
.map {
|
||||
case BindingsMap.Resolution(BindingsMap.ResolvedModule(module)) =>
|
||||
res =>
|
||||
res.target match {
|
||||
case BindingsMap.ResolvedModule(module) =>
|
||||
module.getScope.getAssociatedType
|
||||
case BindingsMap.Resolution(
|
||||
BindingsMap.ResolvedConstructor(definitionModule, cons)
|
||||
) =>
|
||||
case BindingsMap.ResolvedConstructor(definitionModule, cons) =>
|
||||
definitionModule.getScope.getConstructors.get(cons.name)
|
||||
case BindingsMap.ResolvedPolyglotSymbol(_, _) =>
|
||||
throw new CompilerError(
|
||||
"Impossible polyglot symbol, should be caught by MethodDefinitions pass."
|
||||
)
|
||||
case _: BindingsMap.ResolvedMethod =>
|
||||
throw new CompilerError(
|
||||
"Impossible here, should be caught by MethodDefinitions pass."
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
consOpt.foreach {
|
||||
@ -555,6 +567,22 @@ class IrToTruffle(
|
||||
)
|
||||
) =>
|
||||
Right(mod.getScope.getConstructors.get(cons.name))
|
||||
case Some(
|
||||
BindingsMap.Resolution(
|
||||
BindingsMap.ResolvedPolyglotSymbol(_, _)
|
||||
)
|
||||
) =>
|
||||
throw new CompilerError(
|
||||
"Impossible polyglot symbol here, should be caught by Patterns resolution pass."
|
||||
)
|
||||
case Some(
|
||||
BindingsMap.Resolution(
|
||||
BindingsMap.ResolvedMethod(_, _)
|
||||
)
|
||||
) =>
|
||||
throw new CompilerError(
|
||||
"Impossible method here, should be caught by Patterns resolution pass."
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@ -685,7 +713,7 @@ class IrToTruffle(
|
||||
*/
|
||||
def processName(name: IR.Name): RuntimeExpression = {
|
||||
val nameExpr = name match {
|
||||
case IR.Name.Literal(nameStr, _, _, _) =>
|
||||
case IR.Name.Literal(nameStr, _, _, _, _) =>
|
||||
val useInfo = name
|
||||
.unsafeGetMetadata(
|
||||
AliasAnalysis,
|
||||
@ -694,16 +722,27 @@ class IrToTruffle(
|
||||
.unsafeAs[AliasAnalysis.Info.Occurrence]
|
||||
|
||||
val slot = scope.getFramePointer(useInfo.id)
|
||||
val atomCons = moduleScope.getConstructor(nameStr).toScala
|
||||
val polySymbol = moduleScope.lookupPolyglotSymbol(nameStr).toScala
|
||||
if (nameStr == Constants.Names.CURRENT_MODULE) {
|
||||
ConstructorNode.build(moduleScope.getAssociatedType)
|
||||
} else if (slot.isDefined) {
|
||||
val global = name.getMetadata(UppercaseNames)
|
||||
if (slot.isDefined) {
|
||||
ReadLocalVariableNode.build(slot.get)
|
||||
} else if (atomCons.isDefined) {
|
||||
ConstructorNode.build(atomCons.get)
|
||||
} else if (polySymbol.isDefined) {
|
||||
ConstantObjectNode.build(polySymbol.get)
|
||||
} else if (global.isDefined) {
|
||||
val resolution = global.get.target
|
||||
resolution match {
|
||||
case BindingsMap.ResolvedConstructor(definitionModule, cons) =>
|
||||
ConstructorNode.build(
|
||||
definitionModule.getScope.getConstructors.get(cons.name)
|
||||
)
|
||||
case BindingsMap.ResolvedModule(module) =>
|
||||
ConstructorNode.build(module.getScope.getAssociatedType)
|
||||
case BindingsMap.ResolvedPolyglotSymbol(module, symbol) =>
|
||||
ConstantObjectNode.build(
|
||||
module.getScope.getPolyglotSymbols.get(symbol.name)
|
||||
)
|
||||
case BindingsMap.ResolvedMethod(_, _) =>
|
||||
throw new CompilerError(
|
||||
"Impossible here, should be desugared by UppercaseNames resolver"
|
||||
)
|
||||
}
|
||||
} else {
|
||||
DynamicSymbolNode.build(
|
||||
UnresolvedSymbol.build(nameStr, moduleScope)
|
||||
@ -713,7 +752,12 @@ class IrToTruffle(
|
||||
ConstructorNode.build(moduleScope.getAssociatedType)
|
||||
case IR.Name.This(location, passData, _) =>
|
||||
processName(
|
||||
IR.Name.Literal(Constants.Names.THIS_ARGUMENT, location, passData)
|
||||
IR.Name.Literal(
|
||||
Constants.Names.THIS_ARGUMENT,
|
||||
isReferent = false,
|
||||
location,
|
||||
passData
|
||||
)
|
||||
)
|
||||
case _: IR.Name.Blank =>
|
||||
throw new CompilerError(
|
||||
@ -727,7 +771,7 @@ class IrToTruffle(
|
||||
throw new CompilerError(
|
||||
"Qualified names should not be present at codegen time."
|
||||
)
|
||||
case _: IR.Error.Resolution => throw new RuntimeException("todo")
|
||||
case err: IR.Error.Resolution => processError(err)
|
||||
}
|
||||
|
||||
setLocation(nameExpr, name.location)
|
||||
@ -768,7 +812,8 @@ class IrToTruffle(
|
||||
context.getBuiltins.error().compileError().newInstance(err.message)
|
||||
case err: Error.Unexpected.TypeSignature =>
|
||||
context.getBuiltins.error().compileError().newInstance(err.message)
|
||||
case _: Error.Resolution => throw new RuntimeException("bleee")
|
||||
case err: Error.Resolution =>
|
||||
context.getBuiltins.error().compileError().newInstance(err.message)
|
||||
case _: Error.Pattern =>
|
||||
throw new CompilerError(
|
||||
"Impossible here, should be handled in the pattern match."
|
||||
|
@ -13,17 +13,20 @@ class FreshNameSupply {
|
||||
* @param numId the numeric identifier to use in the name
|
||||
* @return a new name
|
||||
*/
|
||||
private def mkName(numId: Long): IR.Name.Literal =
|
||||
IR.Name.Literal(s"<internal-${numId}>", None)
|
||||
private def mkName(numId: Long, isReferent: Boolean): IR.Name.Literal = {
|
||||
val refMarker = if (isReferent) "ref" else ""
|
||||
IR.Name.Literal(s"<internal-$refMarker-${numId}>", isReferent, None)
|
||||
}
|
||||
|
||||
/** Generates a name guaranteed not to exist in this program.
|
||||
*
|
||||
* @param isReferent whether or not the name should be marked as referent.
|
||||
* @return a new name
|
||||
*/
|
||||
def newName(): IR.Name.Literal = {
|
||||
def newName(isReferent: Boolean = false): IR.Name.Literal = {
|
||||
val num = counter
|
||||
counter += 1
|
||||
|
||||
mkName(num)
|
||||
mkName(num, isReferent)
|
||||
}
|
||||
}
|
||||
|
@ -1439,25 +1439,21 @@ object IR {
|
||||
keepDiagnostics: Boolean = true
|
||||
): Name
|
||||
|
||||
/** Checks whether a name is in referant form.
|
||||
/** Checks whether a name is in referent form.
|
||||
*
|
||||
* Please see the syntax specification for more details on this form.
|
||||
*
|
||||
* @return `true` if `this` is in referant form, otherwise `false`
|
||||
* @return `true` if `this` is in referent form, otherwise `false`
|
||||
*/
|
||||
def isReferant: Boolean = {
|
||||
name.split("_").filterNot(_.isEmpty).forall(_.head.isUpper)
|
||||
}
|
||||
def isReferent: Boolean
|
||||
|
||||
/** Checks whether a name is in variable form.
|
||||
*
|
||||
* Please see the syntax specification for more details on this form.
|
||||
*
|
||||
* @return `true` if `this` is in referant form, otherwise `false`
|
||||
* @return `true` if `this` is in referent form, otherwise `false`
|
||||
*/
|
||||
def isVariable: Boolean = !isReferant
|
||||
|
||||
// TODO [AA] toReferant and toVariable for converting forms
|
||||
def isVariable: Boolean = !isReferent
|
||||
}
|
||||
object Name {
|
||||
|
||||
@ -1510,6 +1506,8 @@ object IR {
|
||||
res
|
||||
}
|
||||
|
||||
override def isReferent: Boolean = true
|
||||
|
||||
override def duplicate(
|
||||
keepLocations: Boolean = true,
|
||||
keepMetadata: Boolean = true,
|
||||
@ -1621,6 +1619,8 @@ object IR {
|
||||
override def setLocation(location: Option[IdentifiedLocation]): Name =
|
||||
copy(location = location)
|
||||
|
||||
override def isReferent: Boolean = true
|
||||
|
||||
/** Creates a copy of `this`.
|
||||
*
|
||||
* @param parts the segments of the name
|
||||
@ -1706,6 +1706,8 @@ object IR {
|
||||
res
|
||||
}
|
||||
|
||||
override def isReferent: Boolean = false
|
||||
|
||||
override def duplicate(
|
||||
keepLocations: Boolean = true,
|
||||
keepMetadata: Boolean = true,
|
||||
@ -1744,12 +1746,14 @@ object IR {
|
||||
/** The representation of a literal name.
|
||||
*
|
||||
* @param name the literal text of the name
|
||||
* @param isReferent is this a referent name
|
||||
* @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 Literal(
|
||||
override val name: String,
|
||||
override val isReferent: Boolean,
|
||||
override val location: Option[IdentifiedLocation],
|
||||
override val passData: MetadataStorage = MetadataStorage(),
|
||||
override val diagnostics: DiagnosticStorage = DiagnosticStorage()
|
||||
@ -1759,6 +1763,7 @@ object IR {
|
||||
/** Creates a copy of `this`.
|
||||
*
|
||||
* @param name the literal text of the name
|
||||
* @param isReferent is this a referent name
|
||||
* @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
|
||||
@ -1767,12 +1772,13 @@ object IR {
|
||||
*/
|
||||
def copy(
|
||||
name: String = name,
|
||||
isReferent: Boolean = isReferent,
|
||||
location: Option[IdentifiedLocation] = location,
|
||||
passData: MetadataStorage = passData,
|
||||
diagnostics: DiagnosticStorage = diagnostics,
|
||||
id: Identifier = id
|
||||
): Literal = {
|
||||
val res = Literal(name, location, passData, diagnostics)
|
||||
val res = Literal(name, isReferent, location, passData, diagnostics)
|
||||
res.id = id
|
||||
res
|
||||
}
|
||||
@ -1800,6 +1806,7 @@ object IR {
|
||||
s"""
|
||||
|IR.Name.Literal(
|
||||
|name = $name,
|
||||
|isReferent = $isReferent,
|
||||
|location = $location,
|
||||
|passData = ${this.showPassData},
|
||||
|diagnostics = $diagnostics,
|
||||
@ -1826,6 +1833,8 @@ object IR {
|
||||
override protected var id: Identifier = randomId
|
||||
override val name: String = "this"
|
||||
|
||||
override def isReferent: Boolean = false
|
||||
|
||||
/** Creates a copy of `this`.
|
||||
*
|
||||
* @param location the source location that the node corresponds to
|
||||
@ -1894,6 +1903,8 @@ object IR {
|
||||
override protected var id: Identifier = randomId
|
||||
override val name: String = "here"
|
||||
|
||||
override def isReferent: Boolean = false
|
||||
|
||||
/** Creates a copy of `this`.
|
||||
*
|
||||
* @param location the source location that the node corresponds to
|
||||
@ -4995,6 +5006,8 @@ object IR {
|
||||
with IR.Name {
|
||||
override val name: String = originalName.name
|
||||
|
||||
override def isReferent: Boolean = originalName.isReferent
|
||||
|
||||
override def mapExpressions(fn: Expression => Expression): Resolution =
|
||||
this
|
||||
|
||||
@ -5052,19 +5065,51 @@ object IR {
|
||||
|
||||
object Resolution {
|
||||
|
||||
/**
|
||||
* A representation of a symbol resolution error.
|
||||
*/
|
||||
sealed trait Reason {
|
||||
def explain(originalName: IR.Name): String
|
||||
}
|
||||
|
||||
/**
|
||||
* An error coming from an unexpected occurence of a polyglot symbol.
|
||||
*
|
||||
* @param context the description of a context in which the error
|
||||
* happened.
|
||||
*/
|
||||
case class UnexpectedPolyglot(context: String) extends Reason {
|
||||
override def explain(originalName: Name): String =
|
||||
s"The name ${originalName.name} resolved to a polyglot symbol," +
|
||||
s"but polyglot symbols are not allowed in $context."
|
||||
}
|
||||
|
||||
/**
|
||||
* An error coming from an unexpected occurence of a static method.
|
||||
*
|
||||
* @param context the description of a context in which the error
|
||||
* happened.
|
||||
*/
|
||||
case class UnexpectedMethod(context: String) extends Reason {
|
||||
override def explain(originalName: Name): String =
|
||||
s"The name ${originalName.name} resolved to a method," +
|
||||
s"but methods are not allowed in $context."
|
||||
}
|
||||
|
||||
/**
|
||||
* An error coming from name resolver.
|
||||
*
|
||||
* @param err the original error.
|
||||
*/
|
||||
case class Reason(err: BindingsMap.ResolutionError) {
|
||||
case class ResolverError(err: BindingsMap.ResolutionError)
|
||||
extends Reason {
|
||||
|
||||
/**
|
||||
* Provides a human-readable explanation of the error.
|
||||
* @param originalName the original unresolved name.
|
||||
* @return a human-readable message.
|
||||
*/
|
||||
def explain(originalName: IR.Name): String =
|
||||
override def explain(originalName: IR.Name): String =
|
||||
err match {
|
||||
case BindingsMap.ResolutionAmbiguous(candidates) =>
|
||||
val firstLine =
|
||||
@ -5077,6 +5122,10 @@ object IR {
|
||||
s" Type ${cons.name} defined in module ${definitionModule.getName};"
|
||||
case BindingsMap.ResolvedModule(module) =>
|
||||
s" The module ${module.getName};"
|
||||
case BindingsMap.ResolvedPolyglotSymbol(_, symbol) =>
|
||||
s" The imported polyglot symbol ${symbol.name};"
|
||||
case BindingsMap.ResolvedMethod(module, symbol) =>
|
||||
s" The method ${symbol.name} defined in module ${module.getName}"
|
||||
}
|
||||
(firstLine :: lines).mkString("\n")
|
||||
case BindingsMap.ResolutionNotFound =>
|
||||
|
@ -8,10 +8,14 @@ import org.enso.interpreter.runtime.Module
|
||||
* A utility structure for resolving symbols in a given module.
|
||||
*
|
||||
* @param types the types defined in the current module
|
||||
* @param polyglotSymbols the polyglot symbols imported into the scope
|
||||
* @param moduleMethods the methods defined with current module as `this`
|
||||
* @param currentModule the module holding these bindings
|
||||
*/
|
||||
case class BindingsMap(
|
||||
types: List[BindingsMap.Cons],
|
||||
polyglotSymbols: List[BindingsMap.PolyglotSymbol],
|
||||
moduleMethods: List[BindingsMap.ModuleMethod],
|
||||
currentModule: Module
|
||||
) extends IRPass.Metadata {
|
||||
import BindingsMap._
|
||||
@ -33,11 +37,28 @@ case class BindingsMap(
|
||||
.map(ResolvedConstructor(currentModule, _))
|
||||
}
|
||||
|
||||
private def findPolyglotCandidates(
|
||||
name: String
|
||||
): List[ResolvedPolyglotSymbol] = {
|
||||
polyglotSymbols
|
||||
.filter(_.name == name)
|
||||
.map(ResolvedPolyglotSymbol(currentModule, _))
|
||||
}
|
||||
|
||||
private def findMethodCandidates(name: String): List[ResolvedName] = {
|
||||
moduleMethods
|
||||
.filter(_.name.toLowerCase == name.toLowerCase)
|
||||
.map(ResolvedMethod(currentModule, _))
|
||||
}
|
||||
|
||||
private def findLocalCandidates(name: String): List[ResolvedName] = {
|
||||
if (currentModule.getName.item == name) {
|
||||
List(ResolvedModule(currentModule))
|
||||
} else {
|
||||
findConstructorCandidates(name)
|
||||
val conses = findConstructorCandidates(name)
|
||||
val polyglot = findPolyglotCandidates(name)
|
||||
val methods = findMethodCandidates(name)
|
||||
conses ++ polyglot ++ methods
|
||||
}
|
||||
}
|
||||
|
||||
@ -57,7 +78,7 @@ case class BindingsMap(
|
||||
"Wrong pass ordering. Running resolution on an unparsed module"
|
||||
)
|
||||
}
|
||||
.flatMap(_.findConstructorCandidates(name))
|
||||
.flatMap(_.findExportedSymbolsFor(name))
|
||||
}
|
||||
|
||||
private def handleAmbiguity(
|
||||
@ -70,6 +91,13 @@ case class BindingsMap(
|
||||
}
|
||||
}
|
||||
|
||||
private def getBindingsFrom(module: Module): BindingsMap = {
|
||||
module.getIr.unsafeGetMetadata(
|
||||
BindingAnalysis,
|
||||
"imported module has no binding map info"
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves a name in the context of current module.
|
||||
*
|
||||
@ -90,6 +118,52 @@ case class BindingsMap(
|
||||
}
|
||||
handleAmbiguity(findExportedCandidatesInImports(name))
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves a qualified name to a symbol in the context of this module.
|
||||
*
|
||||
* @param name the name to resolve
|
||||
* @return a resolution for `name`
|
||||
*/
|
||||
def resolveQualifiedName(
|
||||
name: List[String]
|
||||
): Either[ResolutionError, ResolvedName] =
|
||||
name match {
|
||||
case List() => Left(ResolutionNotFound)
|
||||
case List(item) => resolveUppercaseName(item)
|
||||
case List(module, cons) =>
|
||||
resolveUppercaseName(module).flatMap {
|
||||
case ResolvedModule(mod) =>
|
||||
getBindingsFrom(mod).resolveExportedName(cons)
|
||||
case _ => Left(ResolutionNotFound)
|
||||
}
|
||||
case _ =>
|
||||
// TODO[MK] Implement when exports possible. Currently this has
|
||||
// no viable interpretation.
|
||||
Left(ResolutionNotFound)
|
||||
}
|
||||
|
||||
private def findExportedSymbolsFor(name: String): List[ResolvedName] = {
|
||||
val matchingConses = types
|
||||
.filter(_.name.toLowerCase == name.toLowerCase)
|
||||
.map(ResolvedConstructor(currentModule, _))
|
||||
val matchingMethods = moduleMethods
|
||||
.filter(_.name.toLowerCase == name.toLowerCase)
|
||||
.map(ResolvedMethod(currentModule, _))
|
||||
matchingConses ++ matchingMethods
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves a name exported by this module.
|
||||
*
|
||||
* @param name the name to resolve
|
||||
* @return the resolution for `name`
|
||||
*/
|
||||
def resolveExportedName(
|
||||
name: String
|
||||
): Either[ResolutionError, ResolvedName] = {
|
||||
handleAmbiguity(findExportedSymbolsFor(name))
|
||||
}
|
||||
}
|
||||
|
||||
object BindingsMap {
|
||||
@ -102,6 +176,20 @@ object BindingsMap {
|
||||
*/
|
||||
case class Cons(name: String, arity: Int)
|
||||
|
||||
/**
|
||||
* A representation of an imported polyglot symbol.
|
||||
*
|
||||
* @param name the name of the symbol.
|
||||
*/
|
||||
case class PolyglotSymbol(name: String)
|
||||
|
||||
/**
|
||||
* A representation of a method defined on the current module.
|
||||
*
|
||||
* @param name the name of the method.
|
||||
*/
|
||||
case class ModuleMethod(name: String)
|
||||
|
||||
/**
|
||||
* A result of successful name resolution.
|
||||
*/
|
||||
@ -123,6 +211,22 @@ object BindingsMap {
|
||||
*/
|
||||
case class ResolvedModule(module: Module) extends ResolvedName
|
||||
|
||||
/**
|
||||
* A representation of a name being resolved to a method call.
|
||||
* @param module the module defining the method.
|
||||
* @param method the method representation.
|
||||
*/
|
||||
case class ResolvedMethod(module: Module, method: ModuleMethod)
|
||||
extends ResolvedName
|
||||
|
||||
/**
|
||||
* A representation of a name being resolved to a polyglot symbol.
|
||||
*
|
||||
* @param symbol the imported symbol name.
|
||||
*/
|
||||
case class ResolvedPolyglotSymbol(module: Module, symbol: PolyglotSymbol)
|
||||
extends ResolvedName
|
||||
|
||||
/**
|
||||
* A representation of an error during name resolution.
|
||||
*/
|
||||
@ -142,8 +246,7 @@ object BindingsMap {
|
||||
case object ResolutionNotFound extends ResolutionError
|
||||
|
||||
/** A metadata-friendly storage for resolutions */
|
||||
case class Resolution(target: ResolvedName)
|
||||
extends IRPass.Metadata {
|
||||
case class Resolution(target: ResolvedName) extends IRPass.Metadata {
|
||||
|
||||
/** The name of the metadata as a string. */
|
||||
override val metadataName: String = "Resolution"
|
||||
|
@ -554,7 +554,7 @@ case object AliasAnalysis extends IRPass {
|
||||
): IR.Pattern = {
|
||||
pattern match {
|
||||
case named @ Pattern.Name(name, _, _, _) =>
|
||||
if (name.isReferant) {
|
||||
if (name.isReferent) {
|
||||
throw new CompilerError(
|
||||
"Nested patterns should be desugared by the point of alias " +
|
||||
"analysis."
|
||||
@ -668,6 +668,7 @@ case object AliasAnalysis extends IRPass {
|
||||
val graph = new Graph
|
||||
graph.links = links
|
||||
graph.rootScope = rootScope.copy
|
||||
graph.nextIdCounter = nextIdCounter
|
||||
|
||||
graph
|
||||
}
|
||||
|
@ -2,6 +2,7 @@ package org.enso.compiler.pass.analyse
|
||||
|
||||
import org.enso.compiler.context.{InlineContext, ModuleContext}
|
||||
import org.enso.compiler.core.IR
|
||||
import org.enso.compiler.core.IR.Module.Scope.Import.Polyglot
|
||||
import org.enso.compiler.core.ir.MetadataStorage.ToPair
|
||||
import org.enso.compiler.data.BindingsMap
|
||||
import org.enso.compiler.pass.IRPass
|
||||
@ -48,9 +49,38 @@ case object BindingAnalysis extends IRPass {
|
||||
case cons: IR.Module.Scope.Definition.Atom =>
|
||||
BindingsMap.Cons(cons.name.name, cons.arguments.length)
|
||||
}
|
||||
val importedPolyglot = ir.imports.collect {
|
||||
case poly: IR.Module.Scope.Import.Polyglot =>
|
||||
val sym = poly.entity match {
|
||||
case Polyglot.Java(_, className) => className
|
||||
}
|
||||
BindingsMap.PolyglotSymbol(sym)
|
||||
}
|
||||
val moduleMethods = ir.bindings
|
||||
.collect {
|
||||
case method: IR.Module.Scope.Definition.Method.Explicit =>
|
||||
val ref = method.methodReference
|
||||
ref.typePointer match {
|
||||
case IR.Name.Qualified(List(), _, _, _) => Some(ref.methodName.name)
|
||||
case IR.Name.Qualified(List(n), _, _, _) =>
|
||||
if (n.name == moduleContext.module.getName.item)
|
||||
Some(ref.methodName.name)
|
||||
else None
|
||||
case IR.Name.Here(_, _, _) => Some(ref.methodName.name)
|
||||
case IR.Name.Literal(n, _, _, _, _) =>
|
||||
if (n == moduleContext.module.getName.item)
|
||||
Some(ref.methodName.name)
|
||||
else None
|
||||
case _ => None
|
||||
}
|
||||
}
|
||||
.flatten
|
||||
.map(BindingsMap.ModuleMethod)
|
||||
ir.updateMetadata(
|
||||
this -->> BindingsMap(
|
||||
definedConstructors,
|
||||
importedPolyglot,
|
||||
moduleMethods,
|
||||
moduleContext.module
|
||||
)
|
||||
)
|
||||
|
@ -133,7 +133,7 @@ case object ComplexType extends IRPass {
|
||||
val sig = lastSignature match {
|
||||
case Some(IR.Type.Ascription(typed, _, _, _, _)) =>
|
||||
typed match {
|
||||
case IR.Name.Literal(nameStr, _, _, _) =>
|
||||
case IR.Name.Literal(nameStr, _, _, _, _) =>
|
||||
if (name.name == nameStr) {
|
||||
lastSignature
|
||||
} else {
|
||||
|
@ -131,7 +131,7 @@ case object LambdaShorthandToLambda extends IRPass {
|
||||
IR.Function.Lambda(
|
||||
List(
|
||||
IR.DefinitionArgument.Specified(
|
||||
IR.Name.Literal(newName.name, None),
|
||||
IR.Name.Literal(newName.name, isReferent = false, None),
|
||||
None,
|
||||
suspended = false,
|
||||
None
|
||||
@ -207,7 +207,8 @@ case object LambdaShorthandToLambda extends IRPass {
|
||||
IR.Function.Lambda(
|
||||
List(
|
||||
IR.DefinitionArgument.Specified(
|
||||
IR.Name.Literal(updatedName.get, fn.location),
|
||||
IR.Name
|
||||
.Literal(updatedName.get, isReferent = false, fn.location),
|
||||
None,
|
||||
suspended = false,
|
||||
None
|
||||
@ -320,7 +321,11 @@ case object LambdaShorthandToLambda extends IRPass {
|
||||
case IR.CallArgument.Specified(_, value, _, _, passData, diagnostics) =>
|
||||
// Note [Safe Casting to IR.Name.Literal]
|
||||
val defArgName =
|
||||
IR.Name.Literal(value.asInstanceOf[IR.Name.Literal].name, None)
|
||||
IR.Name.Literal(
|
||||
value.asInstanceOf[IR.Name.Literal].name,
|
||||
isReferent = false,
|
||||
None
|
||||
)
|
||||
|
||||
Some(
|
||||
IR.DefinitionArgument.Specified(
|
||||
|
@ -250,7 +250,7 @@ case object IgnoredBindings extends IRPass {
|
||||
def isIgnore(ir: IR.Name): Boolean = {
|
||||
ir match {
|
||||
case _: IR.Name.Blank => true
|
||||
case IR.Name.Literal(name, _, _, _) => name == "_"
|
||||
case IR.Name.Literal(name, _, _, _, _) => name == "_"
|
||||
case _ => false
|
||||
}
|
||||
}
|
||||
|
@ -55,12 +55,33 @@ case object MethodDefinitions extends IRPass {
|
||||
BindingsMap.ResolvedModule(availableSymbolsMap.currentModule)
|
||||
)
|
||||
)
|
||||
case tp @ IR.Name.Qualified(List(item), _, _, _) =>
|
||||
availableSymbolsMap.resolveUppercaseName(item.name) match {
|
||||
case tp @ IR.Name.Qualified(names, _, _, _) =>
|
||||
val items = names.map(_.name)
|
||||
availableSymbolsMap.resolveQualifiedName(items) match {
|
||||
case Left(err) =>
|
||||
IR.Error.Resolution(tp, IR.Error.Resolution.Reason(err))
|
||||
case Right(candidate) =>
|
||||
tp.updateMetadata(this -->> BindingsMap.Resolution(candidate))
|
||||
IR.Error.Resolution(tp, IR.Error.Resolution.ResolverError(err))
|
||||
case Right(value: BindingsMap.ResolvedConstructor) =>
|
||||
tp.updateMetadata(
|
||||
this -->> BindingsMap.Resolution(value)
|
||||
)
|
||||
case Right(value: BindingsMap.ResolvedModule) =>
|
||||
tp.updateMetadata(
|
||||
this -->> BindingsMap.Resolution(value)
|
||||
)
|
||||
case Right(_: BindingsMap.ResolvedPolyglotSymbol) =>
|
||||
IR.Error.Resolution(
|
||||
tp,
|
||||
IR.Error.Resolution.UnexpectedPolyglot(
|
||||
"a method definition target"
|
||||
)
|
||||
)
|
||||
case Right(_: BindingsMap.ResolvedMethod) =>
|
||||
IR.Error.Resolution(
|
||||
tp,
|
||||
IR.Error.Resolution.UnexpectedMethod(
|
||||
"a method definition target"
|
||||
)
|
||||
)
|
||||
}
|
||||
case tp: IR.Error.Resolution => tp
|
||||
case _ =>
|
||||
|
@ -4,6 +4,7 @@ import org.enso.compiler.context.{InlineContext, ModuleContext}
|
||||
import org.enso.compiler.core.IR
|
||||
import org.enso.compiler.core.ir.MetadataStorage.ToPair
|
||||
import org.enso.compiler.data.BindingsMap
|
||||
import org.enso.compiler.exception.CompilerError
|
||||
import org.enso.compiler.pass.IRPass
|
||||
import org.enso.compiler.pass.analyse.{AliasAnalysis, BindingAnalysis}
|
||||
import org.enso.compiler.pass.desugar.{GenerateMethodBodies, NestedPatternMatch}
|
||||
@ -69,27 +70,60 @@ object Patterns extends IRPass {
|
||||
val newBranches = caseExpr.branches.map { branch =>
|
||||
val resolvedPattern = branch.pattern match {
|
||||
case consPat: IR.Pattern.Constructor =>
|
||||
val resolvedName = consPat.constructor match {
|
||||
val consName = consPat.constructor
|
||||
val resolution = consName match {
|
||||
case qual: IR.Name.Qualified =>
|
||||
val parts = qual.parts.map(_.name)
|
||||
Some(bindings.resolveQualifiedName(parts))
|
||||
case lit: IR.Name.Literal =>
|
||||
val resolution = bindings.resolveUppercaseName(lit.name)
|
||||
resolution match {
|
||||
Some(bindings.resolveUppercaseName(lit.name))
|
||||
case _ => None
|
||||
}
|
||||
val resolvedName = resolution
|
||||
.map {
|
||||
case Left(err) =>
|
||||
IR.Error.Resolution(
|
||||
consPat.constructor,
|
||||
IR.Error.Resolution.Reason(err)
|
||||
IR.Error.Resolution.ResolverError(err)
|
||||
)
|
||||
case Right(value) =>
|
||||
lit.updateMetadata(
|
||||
case Right(value: BindingsMap.ResolvedConstructor) =>
|
||||
consName.updateMetadata(
|
||||
this -->> BindingsMap.Resolution(value)
|
||||
)
|
||||
case Right(value: BindingsMap.ResolvedModule) =>
|
||||
consName.updateMetadata(
|
||||
this -->> BindingsMap.Resolution(value)
|
||||
)
|
||||
case Right(_: BindingsMap.ResolvedPolyglotSymbol) =>
|
||||
IR.Error.Resolution(
|
||||
consName,
|
||||
IR.Error.Resolution.UnexpectedPolyglot(
|
||||
"a pattern match"
|
||||
)
|
||||
)
|
||||
case Right(_: BindingsMap.ResolvedMethod) =>
|
||||
IR.Error.Resolution(
|
||||
consName,
|
||||
IR.Error.Resolution.UnexpectedMethod(
|
||||
"a pattern match"
|
||||
)
|
||||
)
|
||||
}
|
||||
case other => other
|
||||
}
|
||||
val resolution = resolvedName.getMetadata(this)
|
||||
val expectedArity = resolution.map { res =>
|
||||
.getOrElse(consName)
|
||||
|
||||
val actualResolution = resolvedName.getMetadata(this)
|
||||
val expectedArity = actualResolution.map { res =>
|
||||
res.target match {
|
||||
case BindingsMap.ResolvedConstructor(_, cons) => cons.arity
|
||||
case BindingsMap.ResolvedModule(_) => 0
|
||||
case BindingsMap.ResolvedPolyglotSymbol(_, _) =>
|
||||
throw new CompilerError(
|
||||
"Impossible, should be transformed into an error before."
|
||||
)
|
||||
case BindingsMap.ResolvedMethod(_, _) =>
|
||||
throw new CompilerError(
|
||||
"Impossible, should be transformed into an error before."
|
||||
)
|
||||
}
|
||||
}
|
||||
expectedArity match {
|
||||
|
@ -189,7 +189,7 @@ case object SuspendedArguments extends IRPass {
|
||||
signature match {
|
||||
case IR.Application.Operator.Binary(
|
||||
l,
|
||||
IR.Name.Literal("->", _, _, _),
|
||||
IR.Name.Literal("->", _, _, _, _),
|
||||
r,
|
||||
_,
|
||||
_,
|
||||
@ -210,7 +210,7 @@ case object SuspendedArguments extends IRPass {
|
||||
*/
|
||||
def representsSuspended(value: IR.Expression): Boolean = {
|
||||
value match {
|
||||
case IR.Name.Literal("Suspended", _, _, _) => true
|
||||
case IR.Name.Literal("Suspended", _, _, _, _) => true
|
||||
case _ => false
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,280 @@
|
||||
package org.enso.compiler.pass.resolve
|
||||
|
||||
import org.enso.compiler.context.{FreshNameSupply, InlineContext, ModuleContext}
|
||||
import org.enso.compiler.core.IR
|
||||
import org.enso.compiler.core.ir.MetadataStorage.ToPair
|
||||
import org.enso.compiler.data.BindingsMap
|
||||
import org.enso.compiler.data.BindingsMap.{
|
||||
Resolution,
|
||||
ResolvedConstructor,
|
||||
ResolvedMethod
|
||||
}
|
||||
import org.enso.compiler.exception.CompilerError
|
||||
import org.enso.compiler.pass.IRPass
|
||||
import org.enso.compiler.pass.analyse.{AliasAnalysis, BindingAnalysis}
|
||||
import org.enso.interpreter.Constants
|
||||
|
||||
/**
|
||||
* Resolves and desugars referent name occurences in non-pattern contexts.
|
||||
*
|
||||
* 1. Attaches resolution metadata to encountered constructors, modules,
|
||||
* and polygot symbols.
|
||||
* 2. Desugars encountered method references into proper applications.
|
||||
* 3. Resolves qualified calls to constructors, i.e. a call of the form
|
||||
* `KnownModule.consName a b c` is transformed into `KnownCons a b c`,
|
||||
* if `consName` refers to a constructor and `KnownModule` was successfully
|
||||
* resolved to a module.
|
||||
*/
|
||||
case object UppercaseNames extends IRPass {
|
||||
|
||||
/** The type of the metadata object that the pass writes to the IR. */
|
||||
override type Metadata = BindingsMap.Resolution
|
||||
|
||||
/** The type of configuration for the pass. */
|
||||
override type Config = IRPass.Configuration.Default
|
||||
|
||||
/** The passes that this pass depends _directly_ on to run. */
|
||||
override val precursorPasses: Seq[IRPass] =
|
||||
Seq(AliasAnalysis, BindingAnalysis)
|
||||
|
||||
/** The passes that are invalidated by running this pass. */
|
||||
override val invalidatedPasses: Seq[IRPass] = Seq(AliasAnalysis)
|
||||
|
||||
/** Executes the pass on the provided `ir`, and returns a possibly transformed
|
||||
* or annotated version of `ir`.
|
||||
*
|
||||
* @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 = {
|
||||
val scopeMap = ir.unsafeGetMetadata(
|
||||
BindingAnalysis,
|
||||
"No binding analysis on the module"
|
||||
)
|
||||
val freshNameSupply = moduleContext.freshNameSupply.getOrElse(
|
||||
throw new CompilerError(
|
||||
"No fresh name supply passed to UppercaseNames resolver."
|
||||
)
|
||||
)
|
||||
ir.mapExpressions(processExpression(_, scopeMap, freshNameSupply))
|
||||
}
|
||||
|
||||
/** Executes the pass on the provided `ir`, and returns a possibly transformed
|
||||
* or annotated version of `ir` in an inline context.
|
||||
*
|
||||
* @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 = {
|
||||
val scopeMap = inlineContext.module.getIr.unsafeGetMetadata(
|
||||
BindingAnalysis,
|
||||
"No binding analysis on the module"
|
||||
)
|
||||
val freshNameSupply = inlineContext.freshNameSupply.getOrElse(
|
||||
throw new CompilerError(
|
||||
"No fresh name supply passed to UppercaseNames resolver."
|
||||
)
|
||||
)
|
||||
processExpression(ir, scopeMap, freshNameSupply)
|
||||
}
|
||||
|
||||
private def processExpression(
|
||||
ir: IR.Expression,
|
||||
bindings: BindingsMap,
|
||||
freshNameSupply: FreshNameSupply,
|
||||
isInsideApplication: Boolean = false
|
||||
): IR.Expression =
|
||||
ir.transformExpressions {
|
||||
case lit: IR.Name.Literal =>
|
||||
if (lit.isReferent && !isLocalVar(lit)) {
|
||||
val resolution = bindings.resolveUppercaseName(lit.name)
|
||||
resolution match {
|
||||
case Left(error) =>
|
||||
IR.Error.Resolution(
|
||||
lit,
|
||||
IR.Error.Resolution.ResolverError(error)
|
||||
)
|
||||
case Right(r @ BindingsMap.ResolvedMethod(mod, method)) =>
|
||||
if (isInsideApplication) {
|
||||
lit.updateMetadata(this -->> BindingsMap.Resolution(r))
|
||||
|
||||
} else {
|
||||
val self = freshNameSupply
|
||||
.newName(isReferent = true)
|
||||
.updateMetadata(
|
||||
this -->> BindingsMap.Resolution(
|
||||
BindingsMap.ResolvedModule(mod)
|
||||
)
|
||||
)
|
||||
val fun = lit.copy(name = method.name)
|
||||
val app = IR.Application.Prefix(
|
||||
fun,
|
||||
List(IR.CallArgument.Specified(None, self, None)),
|
||||
hasDefaultsSuspended = false,
|
||||
None
|
||||
)
|
||||
app
|
||||
}
|
||||
case Right(value) =>
|
||||
lit.updateMetadata(this -->> BindingsMap.Resolution(value))
|
||||
}
|
||||
|
||||
} else { lit }
|
||||
case app: IR.Application.Prefix =>
|
||||
app.function match {
|
||||
case n: IR.Name.Literal =>
|
||||
if (n.isReferent)
|
||||
resolveReferantApplication(app, bindings, freshNameSupply)
|
||||
else resolveLocalApplication(app, bindings, freshNameSupply)
|
||||
case _ =>
|
||||
app.mapExpressions(processExpression(_, bindings, freshNameSupply))
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private def resolveReferantApplication(
|
||||
app: IR.Application.Prefix,
|
||||
bindingsMap: BindingsMap,
|
||||
freshNameSupply: FreshNameSupply
|
||||
): IR.Expression = {
|
||||
val processedFun = processExpression(
|
||||
app.function,
|
||||
bindingsMap,
|
||||
freshNameSupply,
|
||||
isInsideApplication = true
|
||||
)
|
||||
val processedArgs = app.arguments.map(
|
||||
_.mapExpressions(processExpression(_, bindingsMap, freshNameSupply))
|
||||
)
|
||||
processedFun.getMetadata(this) match {
|
||||
case Some(Resolution(ResolvedMethod(mod, method))) =>
|
||||
val self = freshNameSupply
|
||||
.newName(isReferent = true)
|
||||
.updateMetadata(
|
||||
this -->> BindingsMap.Resolution(
|
||||
BindingsMap.ResolvedModule(mod)
|
||||
)
|
||||
)
|
||||
val selfArg = IR.CallArgument.Specified(None, self, None)
|
||||
processedFun.passData.remove(this)
|
||||
val renamed = rename(processedFun, method.name)
|
||||
app.copy(function = renamed, arguments = selfArg :: processedArgs)
|
||||
case _ => app.copy(function = processedFun, arguments = processedArgs)
|
||||
}
|
||||
}
|
||||
|
||||
private def rename(name: IR.Expression, newName: String): IR.Expression =
|
||||
name match {
|
||||
case lit: IR.Name.Literal => lit.copy(name = newName)
|
||||
case _ => name
|
||||
}
|
||||
|
||||
private def resolveLocalApplication(
|
||||
app: IR.Application.Prefix,
|
||||
bindings: BindingsMap,
|
||||
freshNameSupply: FreshNameSupply
|
||||
): IR.Expression = {
|
||||
val processedFun =
|
||||
processExpression(app.function, bindings, freshNameSupply)
|
||||
val processedArgs =
|
||||
app.arguments.map(
|
||||
_.mapExpressions(processExpression(_, bindings, freshNameSupply))
|
||||
)
|
||||
val newApp: Option[IR.Expression] = for {
|
||||
thisArgPos <- findThisPosition(processedArgs)
|
||||
thisArg = processedArgs(thisArgPos)
|
||||
thisArgResolution <- thisArg.value.getMetadata(this)
|
||||
funAsVar <- asGlobalVar(processedFun)
|
||||
cons <- resolveToCons(thisArgResolution, funAsVar)
|
||||
newFun =
|
||||
buildSymbolFor(cons, freshNameSupply).setLocation(funAsVar.location)
|
||||
newArgs = processedArgs.patch(thisArgPos, Nil, 1)
|
||||
} yield buildConsApplication(app, cons.cons, newFun, newArgs)
|
||||
newApp.getOrElse(
|
||||
app.copy(function = processedFun, arguments = processedArgs)
|
||||
)
|
||||
}
|
||||
|
||||
private def buildConsApplication(
|
||||
originalApp: IR.Application.Prefix,
|
||||
calledCons: BindingsMap.Cons,
|
||||
newFun: IR.Expression,
|
||||
newArgs: List[IR.CallArgument]
|
||||
): IR.Expression = {
|
||||
if (
|
||||
newArgs.isEmpty && (!originalApp.hasDefaultsSuspended || calledCons.arity == 0)
|
||||
) {
|
||||
newFun
|
||||
} else {
|
||||
originalApp.copy(function = newFun, arguments = newArgs)
|
||||
}
|
||||
}
|
||||
|
||||
private def buildSymbolFor(
|
||||
cons: BindingsMap.ResolvedConstructor,
|
||||
freshNameSupply: FreshNameSupply
|
||||
): IR.Expression = {
|
||||
freshNameSupply
|
||||
.newName(isReferent = true)
|
||||
.updateMetadata(this -->> BindingsMap.Resolution(cons))
|
||||
}
|
||||
|
||||
private def resolveToCons(
|
||||
thisResolution: BindingsMap.Resolution,
|
||||
consName: IR.Name.Literal
|
||||
): Option[BindingsMap.ResolvedConstructor] =
|
||||
thisResolution.target match {
|
||||
case BindingsMap.ResolvedModule(module) =>
|
||||
val resolution = module.getIr
|
||||
.unsafeGetMetadata(
|
||||
BindingAnalysis,
|
||||
"Imported module without bindings analysis results"
|
||||
)
|
||||
.resolveExportedName(consName.name)
|
||||
resolution match {
|
||||
case Right(cons @ ResolvedConstructor(_, _)) => Some(cons)
|
||||
case _ => None
|
||||
}
|
||||
case _ => None
|
||||
}
|
||||
|
||||
private def findThisPosition(args: List[IR.CallArgument]): Option[Int] = {
|
||||
val ix = args.indexWhere(arg =>
|
||||
arg.name.exists(
|
||||
_.name == Constants.Names.THIS_ARGUMENT
|
||||
) || arg.name.isEmpty
|
||||
)
|
||||
if (ix == -1) None else Some(ix)
|
||||
}
|
||||
|
||||
private def asGlobalVar(ir: IR): Option[IR.Name.Literal] =
|
||||
ir match {
|
||||
case name: IR.Name.Literal =>
|
||||
if (isLocalVar(name) || name.isReferent) None else Some(name)
|
||||
case _ => None
|
||||
}
|
||||
|
||||
private def isLocalVar(name: IR.Name.Literal): Boolean = {
|
||||
val aliasInfo = name
|
||||
.unsafeGetMetadata(
|
||||
AliasAnalysis,
|
||||
"no alias analysis info on a name"
|
||||
)
|
||||
.unsafeAs[AliasAnalysis.Info.Occurrence]
|
||||
val defLink = aliasInfo.graph.defLinkFor(aliasInfo.id)
|
||||
defLink.isDefined
|
||||
}
|
||||
}
|
@ -22,13 +22,28 @@ object StubIrBuilder {
|
||||
*/
|
||||
def build(module: Module): IR.Module = {
|
||||
val ir = IR.Module(List(), List(), None)
|
||||
val scope = module.getScope
|
||||
val conses = scope.getConstructors.asScala
|
||||
val consNames = conses.keys.map(_.toLowerCase()).toSet
|
||||
val definedConstructors: List[BindingsMap.Cons] =
|
||||
module.getScope.getConstructors.asScala.toList.map {
|
||||
conses.toList.map {
|
||||
case (name, cons) =>
|
||||
BindingsMap.Cons(name, cons.getArity)
|
||||
}
|
||||
val moduleMethods = Option(scope.getMethods.get(scope.getAssociatedType))
|
||||
.map(methods =>
|
||||
methods.asScala.keys
|
||||
.filter(!consNames.contains(_))
|
||||
.map(name => BindingsMap.ModuleMethod(name))
|
||||
.toList
|
||||
)
|
||||
.getOrElse(List())
|
||||
val polyglot = scope.getPolyglotSymbols.asScala.keys.toList
|
||||
.map(BindingsMap.PolyglotSymbol)
|
||||
val meta = BindingsMap(
|
||||
definedConstructors,
|
||||
polyglot,
|
||||
moduleMethods,
|
||||
module
|
||||
)
|
||||
ir.updateMetadata(BindingAnalysis -->> meta)
|
||||
|
@ -149,7 +149,7 @@ trait CompilerRunner {
|
||||
* @return an IR name representing the name `str`
|
||||
*/
|
||||
def nameFromString(str: String): IR.Name.Literal = {
|
||||
IR.Name.Literal(str, None)
|
||||
IR.Name.Literal(str, isReferent = false, None)
|
||||
}
|
||||
|
||||
// === IR Testing Utils =====================================================
|
||||
@ -167,8 +167,11 @@ trait CompilerRunner {
|
||||
def asMethod: IR.Module.Scope.Definition.Method = {
|
||||
IR.Module.Scope.Definition.Method.Explicit(
|
||||
IR.Name.MethodReference(
|
||||
IR.Name.Qualified(List(IR.Name.Literal("TestType", None)), None),
|
||||
IR.Name.Literal("testMethod", None),
|
||||
IR.Name.Qualified(
|
||||
List(IR.Name.Literal("TestType", isReferent = true, None)),
|
||||
None
|
||||
),
|
||||
IR.Name.Literal("testMethod", isReferent = false, None),
|
||||
None
|
||||
),
|
||||
ir,
|
||||
@ -182,11 +185,11 @@ trait CompilerRunner {
|
||||
*/
|
||||
def asAtomDefaultArg: IR.Module.Scope.Definition.Atom = {
|
||||
IR.Module.Scope.Definition.Atom(
|
||||
IR.Name.Literal("TestAtom", None),
|
||||
IR.Name.Literal("TestAtom", isReferent = true, None),
|
||||
List(
|
||||
IR.DefinitionArgument
|
||||
.Specified(
|
||||
IR.Name.Literal("arg", None),
|
||||
IR.Name.Literal("arg", isReferent = false, None),
|
||||
Some(ir),
|
||||
suspended = false,
|
||||
None
|
||||
|
@ -320,7 +320,11 @@ class AstToIrTest extends CompilerTest with Inside {
|
||||
ir shouldBe an[IR.Application.Prefix]
|
||||
|
||||
val fn = ir.asInstanceOf[IR.Application.Prefix]
|
||||
fn.function shouldEqual IR.Name.Literal("negate", None)
|
||||
fn.function shouldEqual IR.Name.Literal(
|
||||
"negate",
|
||||
isReferent = false,
|
||||
None
|
||||
)
|
||||
|
||||
val fooArg = fn.arguments.head.asInstanceOf[IR.CallArgument.Specified]
|
||||
fooArg.value shouldBe an[IR.Name.Literal]
|
||||
|
@ -4,7 +4,7 @@ import org.enso.compiler.Passes
|
||||
import org.enso.compiler.context.{FreshNameSupply, ModuleContext}
|
||||
import org.enso.compiler.core.IR
|
||||
import org.enso.compiler.data.BindingsMap
|
||||
import org.enso.compiler.data.BindingsMap.Cons
|
||||
import org.enso.compiler.data.BindingsMap.{Cons, ModuleMethod, PolyglotSymbol}
|
||||
import org.enso.compiler.pass.analyse.BindingAnalysis
|
||||
import org.enso.compiler.pass.{PassConfiguration, PassGroup, PassManager}
|
||||
import org.enso.compiler.test.CompilerTest
|
||||
@ -50,18 +50,24 @@ class BindingAnalysisTest extends CompilerTest {
|
||||
|
||||
val ir =
|
||||
"""
|
||||
|polyglot java import foo.bar.baz.MyClass
|
||||
|
|
||||
|type Foo a b c
|
||||
|type Bar
|
||||
|type Baz x y
|
||||
|
|
||||
|Baz.foo = 123
|
||||
|Bar.baz = Baz 1 2 . foo
|
||||
|
|
||||
|foo = 123
|
||||
|""".stripMargin.preprocessModule.analyse
|
||||
|
||||
"discover all atoms in a module" in {
|
||||
"discover all atoms, methods, and polyglot symbols in a module" in {
|
||||
ir.getMetadata(BindingAnalysis) shouldEqual Some(
|
||||
BindingsMap(
|
||||
List(Cons("Foo", 3), Cons("Bar", 0), Cons("Baz", 2)),
|
||||
List(PolyglotSymbol("MyClass")),
|
||||
List(ModuleMethod("foo")),
|
||||
ctx.module
|
||||
)
|
||||
)
|
||||
|
@ -303,7 +303,7 @@ class DataflowAnalysisTest extends CompilerTest {
|
||||
val printlnFn = printlnExpr.function.asInstanceOf[IR.Name.Literal]
|
||||
val printlnArgIO =
|
||||
printlnExpr.arguments.head.asInstanceOf[IR.CallArgument.Specified]
|
||||
val printlnArgIOExpr = printlnArgIO.value.asInstanceOf[IR.Name.Literal]
|
||||
val printlnArgIOExpr = printlnArgIO.value.asInstanceOf[IR.Error.Resolution]
|
||||
val printlnArgB =
|
||||
printlnExpr.arguments(1).asInstanceOf[IR.CallArgument.Specified]
|
||||
val printlnArgBExpr = printlnArgB.value.asInstanceOf[IR.Name.Literal]
|
||||
|
@ -16,7 +16,7 @@ class GatherDiagnosticsTest extends CompilerTest {
|
||||
AST.Invalid.Unrecognized("@@"),
|
||||
IR.Error.Syntax.UnrecognizedToken
|
||||
)
|
||||
val plusOp = IR.Name.Literal("+", None)
|
||||
val plusOp = IR.Name.Literal("+", isReferent = false, None)
|
||||
val plusApp = IR.Application.Prefix(
|
||||
plusOp,
|
||||
List(
|
||||
@ -29,7 +29,7 @@ class GatherDiagnosticsTest extends CompilerTest {
|
||||
List(
|
||||
IR.DefinitionArgument
|
||||
.Specified(
|
||||
IR.Name.Literal("bar", None),
|
||||
IR.Name.Literal("bar", isReferent = false, None),
|
||||
None,
|
||||
suspended = false,
|
||||
None
|
||||
@ -59,10 +59,10 @@ class GatherDiagnosticsTest extends CompilerTest {
|
||||
IR.Error.Syntax.UnexpectedExpression
|
||||
)
|
||||
|
||||
val typeName = IR.Name.Literal("Foo", None)
|
||||
val method1Name = IR.Name.Literal("bar", None)
|
||||
val method2Name = IR.Name.Literal("baz", None)
|
||||
val fooName = IR.Name.Literal("foo", None)
|
||||
val typeName = IR.Name.Literal("Foo", isReferent = false, None)
|
||||
val method1Name = IR.Name.Literal("bar", isReferent = false, None)
|
||||
val method2Name = IR.Name.Literal("baz", isReferent = false, None)
|
||||
val fooName = IR.Name.Literal("foo", isReferent = false, None)
|
||||
|
||||
val method1Ref =
|
||||
IR.Name.MethodReference(
|
||||
|
@ -45,7 +45,7 @@ class OperatorToFunctionTest extends CompilerTest {
|
||||
// === The Tests ============================================================
|
||||
|
||||
"Operators" should {
|
||||
val opName = IR.Name.Literal("=:=", None)
|
||||
val opName = IR.Name.Literal("=:=", isReferent = false, None)
|
||||
val left = IR.Empty(None)
|
||||
val right = IR.Empty(None)
|
||||
val rightArg = IR.CallArgument.Specified(None, IR.Empty(None), None)
|
||||
@ -76,7 +76,10 @@ class OperatorToFunctionTest extends CompilerTest {
|
||||
None
|
||||
)
|
||||
|
||||
OperatorToFunction.runExpression(recursiveIR, ctx) shouldEqual recursiveIRResult
|
||||
OperatorToFunction.runExpression(
|
||||
recursiveIR,
|
||||
ctx
|
||||
) shouldEqual recursiveIRResult
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -35,7 +35,7 @@ class ApplicationSaturationTest extends CompilerTest {
|
||||
val name = if (positional) {
|
||||
None
|
||||
} else {
|
||||
Some(IR.Name.Literal("a", None))
|
||||
Some(IR.Name.Literal("a", isReferent = false, None))
|
||||
}
|
||||
|
||||
List.fill(n)(IR.CallArgument.Specified(name, IR.Empty(None), None))
|
||||
@ -83,7 +83,7 @@ class ApplicationSaturationTest extends CompilerTest {
|
||||
"Known applications" should {
|
||||
val plusFn = IR.Application
|
||||
.Prefix(
|
||||
IR.Name.Literal("+", None),
|
||||
IR.Name.Literal("+", isReferent = false, None),
|
||||
genNArgs(2),
|
||||
hasDefaultsSuspended = false,
|
||||
None
|
||||
@ -93,7 +93,7 @@ class ApplicationSaturationTest extends CompilerTest {
|
||||
|
||||
val bazFn = IR.Application
|
||||
.Prefix(
|
||||
IR.Name.Literal("baz", None),
|
||||
IR.Name.Literal("baz", isReferent = false, None),
|
||||
genNArgs(2),
|
||||
hasDefaultsSuspended = false,
|
||||
None
|
||||
@ -103,7 +103,7 @@ class ApplicationSaturationTest extends CompilerTest {
|
||||
|
||||
val fooFn = IR.Application
|
||||
.Prefix(
|
||||
IR.Name.Literal("foo", None),
|
||||
IR.Name.Literal("foo", isReferent = false, None),
|
||||
genNArgs(5),
|
||||
hasDefaultsSuspended = false,
|
||||
None
|
||||
@ -113,7 +113,7 @@ class ApplicationSaturationTest extends CompilerTest {
|
||||
|
||||
val fooFnByName = IR.Application
|
||||
.Prefix(
|
||||
IR.Name.Literal("foo", None),
|
||||
IR.Name.Literal("foo", isReferent = false, None),
|
||||
genNArgs(4, positional = false),
|
||||
hasDefaultsSuspended = false,
|
||||
None
|
||||
@ -159,7 +159,7 @@ class ApplicationSaturationTest extends CompilerTest {
|
||||
"Unknown applications" should {
|
||||
val unknownFn = IR.Application
|
||||
.Prefix(
|
||||
IR.Name.Literal("unknown", None),
|
||||
IR.Name.Literal("unknown", isReferent = false, None),
|
||||
genNArgs(10),
|
||||
hasDefaultsSuspended = false,
|
||||
None
|
||||
@ -180,7 +180,7 @@ class ApplicationSaturationTest extends CompilerTest {
|
||||
val empty = IR.Empty(None)
|
||||
val knownPlus = IR.Application
|
||||
.Prefix(
|
||||
IR.Name.Literal("+", None),
|
||||
IR.Name.Literal("+", isReferent = false, None),
|
||||
genNArgs(2),
|
||||
hasDefaultsSuspended = false,
|
||||
None
|
||||
@ -190,7 +190,7 @@ class ApplicationSaturationTest extends CompilerTest {
|
||||
|
||||
val undersaturatedPlus = IR.Application
|
||||
.Prefix(
|
||||
IR.Name.Literal("+", None),
|
||||
IR.Name.Literal("+", isReferent = false, None),
|
||||
genNArgs(1),
|
||||
hasDefaultsSuspended = false,
|
||||
None
|
||||
@ -200,7 +200,7 @@ class ApplicationSaturationTest extends CompilerTest {
|
||||
|
||||
val oversaturatedPlus = IR.Application
|
||||
.Prefix(
|
||||
IR.Name.Literal("+", None),
|
||||
IR.Name.Literal("+", isReferent = false, None),
|
||||
genNArgs(3),
|
||||
hasDefaultsSuspended = false,
|
||||
None
|
||||
@ -222,7 +222,7 @@ class ApplicationSaturationTest extends CompilerTest {
|
||||
def outerPlus(argExpr: IR.Expression): IR.Application.Prefix = {
|
||||
IR.Application
|
||||
.Prefix(
|
||||
IR.Name.Literal("+", None),
|
||||
IR.Name.Literal("+", isReferent = false, None),
|
||||
List(
|
||||
IR.CallArgument.Specified(None, argExpr, None),
|
||||
IR.CallArgument.Specified(None, empty, None)
|
||||
|
@ -206,13 +206,13 @@ class LambdaConsolidateTest extends CompilerTest {
|
||||
List(
|
||||
IR.DefinitionArgument
|
||||
.Specified(
|
||||
IR.Name.Literal("a", None),
|
||||
IR.Name.Literal("a", isReferent = false, None),
|
||||
None,
|
||||
suspended = false,
|
||||
None
|
||||
),
|
||||
IR.DefinitionArgument.Specified(
|
||||
IR.Name.Literal("b", None),
|
||||
IR.Name.Literal("b", isReferent = false, None),
|
||||
None,
|
||||
suspended = false,
|
||||
None
|
||||
@ -221,13 +221,13 @@ class LambdaConsolidateTest extends CompilerTest {
|
||||
IR.Function.Lambda(
|
||||
List(
|
||||
IR.DefinitionArgument.Specified(
|
||||
IR.Name.Literal("c", None),
|
||||
IR.Name.Literal("c", isReferent = false, None),
|
||||
None,
|
||||
suspended = false,
|
||||
None
|
||||
)
|
||||
),
|
||||
IR.Name.Literal("c", None),
|
||||
IR.Name.Literal("c", isReferent = false, None),
|
||||
None
|
||||
),
|
||||
None
|
||||
|
@ -0,0 +1,131 @@
|
||||
package org.enso.compiler.test.pass.resolve
|
||||
|
||||
import org.enso.compiler.Passes
|
||||
import org.enso.compiler.context.{FreshNameSupply, ModuleContext}
|
||||
import org.enso.compiler.core.IR
|
||||
import org.enso.compiler.data.BindingsMap.{
|
||||
Cons,
|
||||
Resolution,
|
||||
ResolvedConstructor,
|
||||
ResolvedModule
|
||||
}
|
||||
import org.enso.compiler.pass.resolve.UppercaseNames
|
||||
import org.enso.compiler.pass.{PassConfiguration, PassGroup, PassManager}
|
||||
import org.enso.compiler.test.CompilerTest
|
||||
|
||||
class UppercaseNamesTest extends CompilerTest {
|
||||
|
||||
// === Test Setup ===========================================================
|
||||
|
||||
def mkModuleContext: ModuleContext =
|
||||
buildModuleContext(
|
||||
freshNameSupply = Some(new FreshNameSupply)
|
||||
)
|
||||
|
||||
val passes = new Passes
|
||||
|
||||
val precursorPasses: PassGroup =
|
||||
passes.getPrecursors(UppercaseNames).get
|
||||
|
||||
val passConfiguration: PassConfiguration = PassConfiguration()
|
||||
|
||||
implicit val passManager: PassManager =
|
||||
new PassManager(List(precursorPasses), passConfiguration)
|
||||
|
||||
/** Adds an extension method to analyse an Enso module.
|
||||
*
|
||||
* @param ir the ir to analyse
|
||||
*/
|
||||
implicit class AnalyseModule(ir: IR.Module) {
|
||||
|
||||
/** Performs tail call analysis on [[ir]].
|
||||
*
|
||||
* @param context the module context in which analysis takes place
|
||||
* @return [[ir]], with tail call analysis metadata attached
|
||||
*/
|
||||
def analyse(implicit context: ModuleContext) = {
|
||||
UppercaseNames.runModule(ir, context)
|
||||
}
|
||||
}
|
||||
|
||||
// === The Tests ============================================================
|
||||
|
||||
"Method definition resolution" should {
|
||||
implicit val ctx: ModuleContext = mkModuleContext
|
||||
|
||||
val code = """
|
||||
|main =
|
||||
| x1 = My_Cons 1 2 3
|
||||
| x2 = Constant
|
||||
| x3 = Add_One 1
|
||||
| x4 = Test_Module.My_Cons 1 2 3
|
||||
| x5 = Does_Not_Exist 32
|
||||
| 0
|
||||
|
|
||||
|type My_Cons a b c
|
||||
|
|
||||
|constant = 2
|
||||
|
|
||||
|add_one x = x + 1
|
||||
|
|
||||
|""".stripMargin
|
||||
val preIr = code.preprocessModule
|
||||
ctx.module.unsafeSetIr(preIr)
|
||||
|
||||
val ir = preIr.analyse
|
||||
|
||||
val bodyExprs = ir
|
||||
.bindings(0)
|
||||
.asInstanceOf[IR.Module.Scope.Definition.Method.Explicit]
|
||||
.body
|
||||
.asInstanceOf[IR.Function.Lambda]
|
||||
.body
|
||||
.asInstanceOf[IR.Expression.Block]
|
||||
.expressions
|
||||
.map(expr => expr.asInstanceOf[IR.Expression.Binding].expression)
|
||||
|
||||
"resolve visible constructors" in {
|
||||
bodyExprs(0)
|
||||
.asInstanceOf[IR.Application.Prefix]
|
||||
.function
|
||||
.getMetadata(UppercaseNames) shouldEqual Some(
|
||||
Resolution(ResolvedConstructor(ctx.module, Cons("My_Cons", 3)))
|
||||
)
|
||||
}
|
||||
|
||||
"resolve uppercase method names to applications" in {
|
||||
val expr = bodyExprs(1)
|
||||
expr shouldBe an[IR.Application.Prefix]
|
||||
val app = expr.asInstanceOf[IR.Application.Prefix]
|
||||
app.function.asInstanceOf[IR.Name.Literal].name shouldEqual "constant"
|
||||
app.arguments.length shouldEqual 1
|
||||
app.arguments(0).value.getMetadata(UppercaseNames) shouldEqual Some(
|
||||
Resolution(ResolvedModule(ctx.module))
|
||||
)
|
||||
}
|
||||
|
||||
"resolve uppercase method names in applications by adding the self argument" in {
|
||||
val expr = bodyExprs(2)
|
||||
expr shouldBe an[IR.Application.Prefix]
|
||||
val app = expr.asInstanceOf[IR.Application.Prefix]
|
||||
app.function.asInstanceOf[IR.Name.Literal].name shouldEqual "add_one"
|
||||
app.arguments.length shouldEqual 2
|
||||
app.arguments(0).value.getMetadata(UppercaseNames) shouldEqual Some(
|
||||
Resolution(ResolvedModule(ctx.module))
|
||||
)
|
||||
}
|
||||
|
||||
"resolve qualified uses of constructors into a simplified form when possible" in {
|
||||
val app = bodyExprs(3).asInstanceOf[IR.Application.Prefix]
|
||||
app.arguments.length shouldBe 3
|
||||
app.function.getMetadata(UppercaseNames) shouldEqual Some(
|
||||
Resolution(ResolvedConstructor(ctx.module, Cons("My_Cons", 3)))
|
||||
)
|
||||
}
|
||||
|
||||
"indicate resolution failures" in {
|
||||
val app = bodyExprs(4).asInstanceOf[IR.Application.Prefix]
|
||||
app.function shouldBe an[IR.Error.Resolution]
|
||||
}
|
||||
}
|
||||
}
|
@ -17,7 +17,7 @@ class ConstructorsTest extends InterpreterTest {
|
||||
val patternMatchingCode =
|
||||
"""
|
||||
|main =
|
||||
| x = Cons 1 Nil
|
||||
| x = Builtins.Cons 1 Nil
|
||||
| case x of
|
||||
| Cons h t -> h
|
||||
| Nil -> 0
|
||||
|
@ -19,10 +19,10 @@ class PatternMatchTest extends InterpreterTest {
|
||||
"""
|
||||
|main =
|
||||
| f = case _ of
|
||||
| Cons a _ -> a
|
||||
| Nil -> -10
|
||||
| Builtins.Cons a _ -> a
|
||||
| Builtins.Nil -> -10
|
||||
|
|
||||
| (10.Cons Nil . f) - Nil.f
|
||||
| (Builtins.Cons 10 Builtins.Nil . f) - Nil.f
|
||||
|""".stripMargin
|
||||
|
||||
eval(code) shouldEqual 20
|
||||
@ -38,7 +38,7 @@ class PatternMatchTest extends InterpreterTest {
|
||||
| MyAtom a -> a
|
||||
| _ -> -100
|
||||
|
|
||||
| (50.MyAtom . f) + Nil.f
|
||||
| (MyAtom 50 . f) + Nil.f
|
||||
|""".stripMargin
|
||||
|
||||
eval(code) shouldEqual -50
|
||||
@ -54,7 +54,7 @@ class PatternMatchTest extends InterpreterTest {
|
||||
| MyAtom a -> a
|
||||
| a -> a + 5
|
||||
|
|
||||
| (50.MyAtom . f) + 30.f
|
||||
| (MyAtom 50 . f) + 30.f
|
||||
|""".stripMargin
|
||||
|
||||
eval(code) shouldEqual 85
|
||||
|
@ -43,22 +43,26 @@ object WithDebugCommand {
|
||||
|
||||
val printAssemblyOption = "--printAssembly"
|
||||
|
||||
val debuggerOption = "--debugger"
|
||||
|
||||
val argSeparator = "--"
|
||||
|
||||
val commandName = "withDebug"
|
||||
|
||||
val benchOnlyCommandName = "benchOnly"
|
||||
val runCommandName = "run"
|
||||
val testOnlyCommandName = "testOnly"
|
||||
|
||||
/** The main logic for parsing and transforming the debug flags into JVM level flags */
|
||||
def withDebug: Command = Command.args(commandName, "<arguments>") {
|
||||
(state, args) =>
|
||||
def withDebug: Command =
|
||||
Command.args(commandName, "<arguments>") { (state, args) =>
|
||||
val (debugFlags, prefixedRunArgs) = args.span(_ != argSeparator)
|
||||
val runArgs = " " + prefixedRunArgs.drop(1).mkString(" ")
|
||||
|
||||
val taskKey =
|
||||
if (debugFlags.contains(benchOnlyCommandName)) BenchTasks.benchOnly
|
||||
else if (debugFlags.contains(runCommandName)) Compile / Keys.run
|
||||
else if (debugFlags.contains(testOnlyCommandName)) Test / Keys.testOnly
|
||||
else throw new IllegalArgumentException("Invalid command name.")
|
||||
|
||||
val dumpGraphsOpts =
|
||||
@ -72,11 +76,18 @@ object WithDebugCommand {
|
||||
if (debugFlags.contains(printAssemblyOption))
|
||||
trufflePrintAssemblyOptions
|
||||
else Seq()
|
||||
val debuggerOpts =
|
||||
if (debugFlags.contains(debuggerOption))
|
||||
Seq(
|
||||
"-agentlib:jdwp=transport=dt_socket,server=n,address=localhost:5005,suspend=y"
|
||||
)
|
||||
else Seq()
|
||||
val javaOpts: Seq[String] = Seq(
|
||||
truffleNoBackgroundCompilationOptions,
|
||||
dumpGraphsOpts,
|
||||
showCompilationsOpts,
|
||||
printAssemblyOpts
|
||||
printAssemblyOpts,
|
||||
debuggerOpts
|
||||
).flatten
|
||||
|
||||
val extracted = Project.extract(state)
|
||||
|
@ -2,8 +2,10 @@ import Base.Test
|
||||
import Test.List_Spec
|
||||
import Test.Number_Spec
|
||||
import Test.Import_Loop_Spec
|
||||
import Test.Names_Spec
|
||||
|
||||
main = Suite.runMain <|
|
||||
List_Spec.spec
|
||||
Number_Spec.spec
|
||||
Import_Loop_Spec.spec
|
||||
Names_Spec.spec
|
||||
|
6
test/Test/src/Names/Definitions.enso
Normal file
6
test/Test/src/Names/Definitions.enso
Normal file
@ -0,0 +1,6 @@
|
||||
type Foo a b c
|
||||
|
||||
Foo.sum = case this of
|
||||
Foo a b c -> a + b + c
|
||||
|
||||
another_constant = 10
|
39
test/Test/src/Names_Spec.enso
Normal file
39
test/Test/src/Names_Spec.enso
Normal file
@ -0,0 +1,39 @@
|
||||
import Test.Names.Definitions
|
||||
import Base.Test
|
||||
|
||||
Definitions.Foo.my_method = case this of
|
||||
Definitions.Foo x y z -> x * y * z
|
||||
|
||||
get_foo module = module.Foo
|
||||
|
||||
constant = 1
|
||||
|
||||
add_one (x = 0) = x + 1
|
||||
|
||||
spec =
|
||||
describe "Qualified Names" <|
|
||||
it "should allow to call constructors in a qualified manner" <|
|
||||
Definitions.Foo 1 2 3 . sum . should_equal 6
|
||||
Definitions . Foo 1 2 3 . sum . should_equal 6
|
||||
it "should allow pattern matching in a qualified manner" <|
|
||||
v = Foo 1 2 3
|
||||
res = case v of
|
||||
Definitions.Foo a b c -> a + b + c
|
||||
res.should_equal 6
|
||||
it "should allow defining methods on qualified names" <|
|
||||
v = Definitions.Foo 2 3 5
|
||||
v.my_method.should_equal 30
|
||||
it "should allow using constructors from value-bound modules" <|
|
||||
v = here.get_foo Definitions 1 2 3
|
||||
v.sum.should_equal 6
|
||||
describe "Uppercase Methods" <|
|
||||
it "should allow calling methods without a target, through uppercase resolution" <|
|
||||
v = Constant
|
||||
v.should_equal 1
|
||||
it "should allow calling methods that use defaulted arguments" <|
|
||||
Add_One.should_equal 1
|
||||
Add_One 100 . should_equal 101
|
||||
it "should allow calling methods imported from another module" <|
|
||||
## TODO
|
||||
This should only work with `all` import.
|
||||
Another_Constant.should_equal 10
|
Loading…
Reference in New Issue
Block a user