Qualified names & uppercase name resolution (#1062)

This commit is contained in:
Marcin Kostrzewa 2020-08-05 22:16:44 +02:00 committed by GitHub
parent c64298cb8e
commit ddb43af5a2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
39 changed files with 1063 additions and 162 deletions

View File

@ -398,7 +398,12 @@ need to follow these steps:
and add them to `truffleRunOptions` in [`build.sbt`](build.sbt). Remove the and add them to `truffleRunOptions` in [`build.sbt`](build.sbt). Remove the
portion of these options after `suspend=y`, including the comma. They are portion of these options after `suspend=y`, including the comma. They are
placeholders that we don't use. 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 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. SBT shell, run a command to execute the code you want to debug (e.g.
`testOnly *CurryingTest*`). This will open the standard debugger interface `testOnly *CurryingTest*`). This will open the standard debugger interface

View File

@ -25,6 +25,12 @@ public class AtomBenchmarks {
main.mainFunction().value().execute(main.mainConstructor(), fixtures.million()); 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) { private void benchOnList(DefaultInterpreterRunner.MainMethod main) {
main.mainFunction().value().execute(main.mainConstructor(), fixtures.millionElementList()); main.mainFunction().value().execute(main.mainConstructor(), fixtures.millionElementList());
} }

View File

@ -29,6 +29,16 @@ class AtomFixtures extends DefaultInterpreterRunner {
""".stripMargin """.stripMargin
val generateList = getMain(generateListCode) 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 = val reverseListCode =
""" """
|main = list -> |main = list ->

View File

@ -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);
}
}

View File

@ -323,7 +323,7 @@ public class Module implements TruffleObject {
Types.extractArguments(args, AtomConstructor.class, String.class); Types.extractArguments(args, AtomConstructor.class, String.class);
AtomConstructor cons = arguments.getFirst(); AtomConstructor cons = arguments.getFirst();
String name = arguments.getSecond(); 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) private static AtomConstructor getConstructor(ModuleScope scope, Object[] args)

View File

@ -13,6 +13,7 @@ import org.enso.interpreter.node.ExpressionNode;
import org.enso.interpreter.node.callable.argument.ReadArgumentNode; import org.enso.interpreter.node.callable.argument.ReadArgumentNode;
import org.enso.interpreter.node.expression.atom.GetFieldNode; import org.enso.interpreter.node.expression.atom.GetFieldNode;
import org.enso.interpreter.node.expression.atom.InstantiateNode; 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.node.expression.builtin.InstantiateAtomNode;
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;
@ -82,11 +83,26 @@ public class AtomConstructor implements TruffleObject {
} }
private void generateMethods(ArgumentDefinition[] args) { private void generateMethods(ArgumentDefinition[] args) {
generateQualifiedAccessor();
for (ArgumentDefinition arg : args) { for (ArgumentDefinition arg : args) {
definitionScope.registerMethod(this, arg.getName(), generateGetter(arg.getPosition())); 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) { private Function generateGetter(int position) {
GetFieldNode node = new GetFieldNode(null, position); GetFieldNode node = new GetFieldNode(null, position);
RootCallTarget callTarget = Truffle.getRuntime().createCallTarget(node); RootCallTarget callTarget = Truffle.getRuntime().createCallTarget(node);

View File

@ -105,6 +105,7 @@ public class ModuleScope {
* @param function the {@link Function} associated with this definition * @param function the {@link Function} associated with this definition
*/ */
public void registerMethod(AtomConstructor atom, String method, Function function) { public void registerMethod(AtomConstructor atom, String method, Function function) {
method = method.toLowerCase();
Map<String, Function> methodMap = ensureMethodMapFor(atom); Map<String, Function> methodMap = ensureMethodMapFor(atom);
if (methodMap.containsKey(method)) { if (methodMap.containsKey(method)) {
@ -147,16 +148,17 @@ public class ModuleScope {
*/ */
@CompilerDirectives.TruffleBoundary @CompilerDirectives.TruffleBoundary
public Function lookupMethodDefinition(AtomConstructor atom, String name) { 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) { if (definedWithAtom != null) {
return definedWithAtom; return definedWithAtom;
} }
Function definedHere = getMethodMapFor(atom).get(name); Function definedHere = getMethodMapFor(atom).get(lowerName);
if (definedHere != null) { if (definedHere != null) {
return definedHere; return definedHere;
} }
return imports.stream() return imports.stream()
.map(scope -> scope.getMethodMapFor(atom).get(name)) .map(scope -> scope.getMethodMapFor(atom).get(lowerName))
.filter(Objects::nonNull) .filter(Objects::nonNull)
.findFirst() .findFirst()
.orElse(null); .orElse(null);
@ -179,6 +181,11 @@ public class ModuleScope {
return methods; return methods;
} }
/** @return the polyglot symbols imported into this scope. */
public Map<String, Object> getPolyglotSymbols() {
return polyglotSymbols;
}
public void reset() { public void reset() {
imports = new HashSet<>(); imports = new HashSet<>();
methods = new HashMap<>(); methods = new HashMap<>();

View File

@ -42,6 +42,8 @@ class Passes(passes: Option[List[PassGroup]] = None) {
TypeFunctions, TypeFunctions,
TypeSignatures, TypeSignatures,
AliasAnalysis, AliasAnalysis,
UppercaseNames,
AliasAnalysis,
LambdaConsolidate, LambdaConsolidate,
AliasAnalysis, AliasAnalysis,
SuspendedArguments, SuspendedArguments,

View File

@ -116,7 +116,7 @@ object AstToIr {
case AstView.Atom(consName, args) => case AstView.Atom(consName, args) =>
Module.Scope.Definition Module.Scope.Definition
.Atom( .Atom(
Name.Literal(consName.name, getIdentifiedLocation(consName)), buildName(consName),
args.map(translateArgumentDefinition(_)), args.map(translateArgumentDefinition(_)),
getIdentifiedLocation(inputAst) getIdentifiedLocation(inputAst)
) )
@ -131,7 +131,7 @@ object AstToIr {
if (containsAtomDefOrInclude && !hasArgs) { if (containsAtomDefOrInclude && !hasArgs) {
Module.Scope.Definition.Type( Module.Scope.Definition.Type(
Name.Literal(typeName.name, getIdentifiedLocation(typeName)), buildName(typeName),
args.map(translateArgumentDefinition(_)), args.map(translateArgumentDefinition(_)),
translatedBody, translatedBody,
getIdentifiedLocation(inputAst) getIdentifiedLocation(inputAst)
@ -148,14 +148,9 @@ object AstToIr {
val pathSegments = targetPath.collect { val pathSegments = targetPath.collect {
case AST.Ident.Cons.any(c) => c case AST.Ident.Cons.any(c) => c
} }
val pathNames = pathSegments.map(c => val pathNames = pathSegments.map(buildName)
IR.Name.Literal(c.name, getIdentifiedLocation(c))
)
val methodSegments = pathNames :+ Name.Literal( val methodSegments = pathNames :+ buildName(nameStr)
nameStr.name,
getIdentifiedLocation(nameStr)
)
val typeSegments = methodSegments.init val typeSegments = methodSegments.init
@ -168,10 +163,8 @@ object AstToIr {
MethodReference.genLocation(methodSegments) MethodReference.genLocation(methodSegments)
) )
} else { } else {
val typeName = Name.Here(None) val typeName = Name.Here(None)
val methodName = val methodName = buildName(nameStr)
Name.Literal(nameStr.name, getIdentifiedLocation(nameStr))
Name.MethodReference( Name.MethodReference(
typeName, typeName,
methodName, methodName,
@ -187,7 +180,7 @@ object AstToIr {
) )
case AstView.FunctionSugar(name, args, body) => case AstView.FunctionSugar(name, args, body) =>
val typeName = Name.Here(None) val typeName = Name.Here(None)
val methodName = Name.Literal(name.name, getIdentifiedLocation(name)) val methodName = buildName(name)
val methodReference = Name.MethodReference( val methodReference = Name.MethodReference(
typeName, typeName,
@ -205,10 +198,10 @@ object AstToIr {
case AstView.TypeAscription(typed, sig) => case AstView.TypeAscription(typed, sig) =>
typed match { typed match {
case AST.Ident.any(ident) => case AST.Ident.any(ident) =>
val typeName = Name.Here(None) val typeName = Name.Here(None)
val methodName = Name.Literal(ident.name, getIdentifiedLocation(ident)) val methodName = buildName(ident)
val methodReference = Name.MethodReference( val methodReference = Name.MethodReference(
typeName, typeName,
methodName, methodName,
methodName.location methodName.location
) )
@ -322,7 +315,7 @@ object AstToIr {
) )
case _ => case _ =>
IR.Application.Prefix( IR.Application.Prefix(
IR.Name.Literal("negate", None), IR.Name.Literal("negate", isReferent = false, None),
List( List(
IR.CallArgument.Specified( IR.CallArgument.Specified(
None, None,
@ -344,7 +337,7 @@ object AstToIr {
case AstView case AstView
.SuspendedBlock(name, block @ AstView.Block(lines, lastLine)) => .SuspendedBlock(name, block @ AstView.Block(lines, lastLine)) =>
Expression.Binding( Expression.Binding(
Name.Literal(name.name, getIdentifiedLocation(name)), buildName(name),
Expression.Block( Expression.Block(
lines.map(translateExpression), lines.map(translateExpression),
translateExpression(lastLine), translateExpression(lastLine),
@ -366,7 +359,7 @@ object AstToIr {
// Note [Uniform Call Syntax Translation] // Note [Uniform Call Syntax Translation]
Application.Prefix( Application.Prefix(
translateExpression(name), translateIdent(name),
(target :: validArguments).map(translateCallArgument), (target :: validArguments).map(translateCallArgument),
hasDefaultsSuspended = hasDefaultsSuspended, hasDefaultsSuspended = hasDefaultsSuspended,
getIdentifiedLocation(inputAst) getIdentifiedLocation(inputAst)
@ -556,7 +549,7 @@ object AstToIr {
case AstView.AssignedArgument(left, right) => case AstView.AssignedArgument(left, right) =>
CallArgument CallArgument
.Specified( .Specified(
Some(Name.Literal(left.name, getIdentifiedLocation(left))), Some(buildName(left)),
translateExpression(right), translateExpression(right),
getIdentifiedLocation(arg) getIdentifiedLocation(arg)
) )
@ -631,7 +624,7 @@ object AstToIr {
} else { } else {
Application.Operator.Binary( Application.Operator.Binary(
leftArg, leftArg,
Name.Literal(fn.name, getIdentifiedLocation(fn)), buildName(fn),
rightArg, rightArg,
getIdentifiedLocation(callable) getIdentifiedLocation(callable)
) )
@ -680,13 +673,13 @@ object AstToIr {
} else { } else {
Application.Operator.Section.Left( Application.Operator.Section.Left(
leftArg, leftArg,
Name.Literal(left.opr.name, getIdentifiedLocation(left.opr)), buildName(left.opr),
getIdentifiedLocation(left) getIdentifiedLocation(left)
) )
} }
case AST.App.Section.Sides.any(sides) => case AST.App.Section.Sides.any(sides) =>
Application.Operator.Section.Sides( Application.Operator.Section.Sides(
Name.Literal(sides.opr.name, getIdentifiedLocation(sides.opr)), buildName(sides.opr),
getIdentifiedLocation(sides) getIdentifiedLocation(sides)
) )
case AST.App.Section.Right.any(right) => case AST.App.Section.Right.any(right) =>
@ -696,7 +689,7 @@ object AstToIr {
Error.Syntax(section, Error.Syntax.NamedArgInSection) Error.Syntax(section, Error.Syntax.NamedArgInSection)
} else { } else {
Application.Operator.Section.Right( Application.Operator.Section.Right(
Name.Literal(right.opr.name, getIdentifiedLocation(right.opr)), buildName(right.opr),
translateCallArgument(right.arg), translateCallArgument(right.arg),
getIdentifiedLocation(right) getIdentifiedLocation(right)
) )
@ -718,10 +711,10 @@ object AstToIr {
} else if (name == "here") { } else if (name == "here") {
Name.Here(getIdentifiedLocation(identifier)) Name.Here(getIdentifiedLocation(identifier))
} else { } else {
Name.Literal(name, getIdentifiedLocation(identifier)) buildName(identifier)
} }
case AST.Ident.Cons(name) => case AST.Ident.Cons(_) =>
Name.Literal(name, getIdentifiedLocation(identifier)) buildName(identifier)
case AST.Ident.Blank(_) => case AST.Ident.Blank(_) =>
Name.Blank(getIdentifiedLocation(identifier)) Name.Blank(getIdentifiedLocation(identifier))
case AST.Ident.Opr.any(_) => case AST.Ident.Opr.any(_) =>
@ -802,9 +795,14 @@ object AstToIr {
*/ */
def translatePattern(pattern: AST): Pattern = { def translatePattern(pattern: AST): Pattern = {
AstView.MaybeManyParensed.unapply(pattern).getOrElse(pattern) match { 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( Pattern.Constructor(
translateIdent(cons).asInstanceOf[IR.Name], name,
fields.map(translatePattern), fields.map(translatePattern),
getIdentifiedLocation(pattern) getIdentifiedLocation(pattern)
) )
@ -904,4 +902,13 @@ object AstToIr {
throw new UnhandledEntity(comment, "processComment") 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))
} }

View File

@ -443,6 +443,13 @@ object AstView {
object MethodCall { 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. /** Matches on a method call.
* *
* A method call has the form `<obj>.<fn-name> <args...>` where `<obj>` is * 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])] = def unapply(ast: AST): Option[(AST, AST.Ident, List[AST])] =
ast match { ast match {
case OperatorDot(target, Application(ConsOrVar(ident), args)) => case OperatorDot(target, Application(ConsOrVar(ident), args)) =>
Some((target, ident, args)) Some((target, consToVar(ident), args))
case AST.App.Section.Left( case AST.App.Section.Left(
MethodCall(target, ident, List()), MethodCall(target, ident, List()),
susp @ SuspendDefaultsOperator(_) susp @ SuspendDefaultsOperator(_)
) => ) =>
Some((target, ident, List(susp))) Some((target, ident, List(susp)))
case OperatorDot(target, ConsOrVar(ident)) => case OperatorDot(target, ConsOrVar(ident)) =>
Some((target, ident, List())) Some((target, consToVar(ident), List()))
case _ => None 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 { object ConstructorPattern {
/** Matches on a constructor pattern. /** Matches on a constructor pattern.
@ -803,12 +821,12 @@ object AstView {
* @param ast the structure to try and match on * @param ast the structure to try and match on
* @return the pattern * @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 { 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 => case SpacedList(elems) if elems.nonEmpty =>
elems.head match { elems.head match {
case AST.Ident.Cons.any(refName) => case QualifiedName(refName) =>
val allFieldsValid = elems.tail.forall { val allFieldsValid = elems.tail.forall {
case Pattern(_) => true case Pattern(_) => true
case _ => false case _ => false

View File

@ -15,7 +15,11 @@ import org.enso.compiler.pass.analyse.{
TailCall TailCall
} }
import org.enso.compiler.pass.optimise.ApplicationSaturation 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.argument.ReadArgumentNode
import org.enso.interpreter.node.callable.function.{ import org.enso.interpreter.node.callable.function.{
BlockNode, BlockNode,
@ -60,7 +64,6 @@ import org.enso.interpreter.{Constants, Language}
import scala.collection.mutable import scala.collection.mutable
import scala.collection.mutable.ArrayBuffer import scala.collection.mutable.ArrayBuffer
import scala.jdk.OptionConverters._
/** 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 [[org.enso.compiler.core.Core.Node]] structures that
@ -205,12 +208,21 @@ class IrToTruffle(
methodDef.methodReference.typePointer methodDef.methodReference.typePointer
.getMetadata(MethodDefinitions) .getMetadata(MethodDefinitions)
.map { .map {
case BindingsMap.Resolution(BindingsMap.ResolvedModule(module)) => res =>
module.getScope.getAssociatedType res.target match {
case BindingsMap.Resolution( case BindingsMap.ResolvedModule(module) =>
BindingsMap.ResolvedConstructor(definitionModule, cons) module.getScope.getAssociatedType
) => case BindingsMap.ResolvedConstructor(definitionModule, cons) =>
definitionModule.getScope.getConstructors.get(cons.name) 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 { consOpt.foreach {
@ -555,6 +567,22 @@ class IrToTruffle(
) )
) => ) =>
Right(mod.getScope.getConstructors.get(cons.name)) 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 = { def processName(name: IR.Name): RuntimeExpression = {
val nameExpr = name match { val nameExpr = name match {
case IR.Name.Literal(nameStr, _, _, _) => case IR.Name.Literal(nameStr, _, _, _, _) =>
val useInfo = name val useInfo = name
.unsafeGetMetadata( .unsafeGetMetadata(
AliasAnalysis, AliasAnalysis,
@ -693,17 +721,28 @@ class IrToTruffle(
) )
.unsafeAs[AliasAnalysis.Info.Occurrence] .unsafeAs[AliasAnalysis.Info.Occurrence]
val slot = scope.getFramePointer(useInfo.id) val slot = scope.getFramePointer(useInfo.id)
val atomCons = moduleScope.getConstructor(nameStr).toScala val global = name.getMetadata(UppercaseNames)
val polySymbol = moduleScope.lookupPolyglotSymbol(nameStr).toScala if (slot.isDefined) {
if (nameStr == Constants.Names.CURRENT_MODULE) {
ConstructorNode.build(moduleScope.getAssociatedType)
} else if (slot.isDefined) {
ReadLocalVariableNode.build(slot.get) ReadLocalVariableNode.build(slot.get)
} else if (atomCons.isDefined) { } else if (global.isDefined) {
ConstructorNode.build(atomCons.get) val resolution = global.get.target
} else if (polySymbol.isDefined) { resolution match {
ConstantObjectNode.build(polySymbol.get) 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 { } else {
DynamicSymbolNode.build( DynamicSymbolNode.build(
UnresolvedSymbol.build(nameStr, moduleScope) UnresolvedSymbol.build(nameStr, moduleScope)
@ -713,7 +752,12 @@ class IrToTruffle(
ConstructorNode.build(moduleScope.getAssociatedType) ConstructorNode.build(moduleScope.getAssociatedType)
case IR.Name.This(location, passData, _) => case IR.Name.This(location, passData, _) =>
processName( 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 => case _: IR.Name.Blank =>
throw new CompilerError( throw new CompilerError(
@ -727,7 +771,7 @@ class IrToTruffle(
throw new CompilerError( throw new CompilerError(
"Qualified names should not be present at codegen time." "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) setLocation(nameExpr, name.location)
@ -768,7 +812,8 @@ class IrToTruffle(
context.getBuiltins.error().compileError().newInstance(err.message) context.getBuiltins.error().compileError().newInstance(err.message)
case err: Error.Unexpected.TypeSignature => case err: Error.Unexpected.TypeSignature =>
context.getBuiltins.error().compileError().newInstance(err.message) 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 => case _: Error.Pattern =>
throw new CompilerError( throw new CompilerError(
"Impossible here, should be handled in the pattern match." "Impossible here, should be handled in the pattern match."

View File

@ -13,17 +13,20 @@ class FreshNameSupply {
* @param numId the numeric identifier to use in the name * @param numId the numeric identifier to use in the name
* @return a new name * @return a new name
*/ */
private def mkName(numId: Long): IR.Name.Literal = private def mkName(numId: Long, isReferent: Boolean): IR.Name.Literal = {
IR.Name.Literal(s"<internal-${numId}>", None) 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. /** 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 * @return a new name
*/ */
def newName(): IR.Name.Literal = { def newName(isReferent: Boolean = false): IR.Name.Literal = {
val num = counter val num = counter
counter += 1 counter += 1
mkName(num) mkName(num, isReferent)
} }
} }

View File

@ -1439,25 +1439,21 @@ object IR {
keepDiagnostics: Boolean = true keepDiagnostics: Boolean = true
): Name ): 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. * 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 = { def isReferent: Boolean
name.split("_").filterNot(_.isEmpty).forall(_.head.isUpper)
}
/** Checks whether a name is in variable form. /** Checks whether a name is in variable form.
* *
* Please see the syntax specification for more details on this 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 def isVariable: Boolean = !isReferent
// TODO [AA] toReferant and toVariable for converting forms
} }
object Name { object Name {
@ -1510,6 +1506,8 @@ object IR {
res res
} }
override def isReferent: Boolean = true
override def duplicate( override def duplicate(
keepLocations: Boolean = true, keepLocations: Boolean = true,
keepMetadata: Boolean = true, keepMetadata: Boolean = true,
@ -1621,6 +1619,8 @@ object IR {
override def setLocation(location: Option[IdentifiedLocation]): Name = override def setLocation(location: Option[IdentifiedLocation]): Name =
copy(location = location) copy(location = location)
override def isReferent: Boolean = true
/** Creates a copy of `this`. /** Creates a copy of `this`.
* *
* @param parts the segments of the name * @param parts the segments of the name
@ -1706,6 +1706,8 @@ object IR {
res res
} }
override def isReferent: Boolean = false
override def duplicate( override def duplicate(
keepLocations: Boolean = true, keepLocations: Boolean = true,
keepMetadata: Boolean = true, keepMetadata: Boolean = true,
@ -1744,12 +1746,14 @@ object IR {
/** The representation of a literal name. /** The representation of a literal name.
* *
* @param name the literal text of the 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 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 Literal( sealed case class Literal(
override val name: String, override val name: String,
override val isReferent: Boolean,
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()
@ -1759,6 +1763,7 @@ object IR {
/** Creates a copy of `this`. /** Creates a copy of `this`.
* *
* @param name the literal text of the 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 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
@ -1767,12 +1772,13 @@ object IR {
*/ */
def copy( def copy(
name: String = name, name: String = name,
isReferent: Boolean = isReferent,
location: Option[IdentifiedLocation] = location, location: Option[IdentifiedLocation] = location,
passData: MetadataStorage = passData, passData: MetadataStorage = passData,
diagnostics: DiagnosticStorage = diagnostics, diagnostics: DiagnosticStorage = diagnostics,
id: Identifier = id id: Identifier = id
): Literal = { ): Literal = {
val res = Literal(name, location, passData, diagnostics) val res = Literal(name, isReferent, location, passData, diagnostics)
res.id = id res.id = id
res res
} }
@ -1800,6 +1806,7 @@ object IR {
s""" s"""
|IR.Name.Literal( |IR.Name.Literal(
|name = $name, |name = $name,
|isReferent = $isReferent,
|location = $location, |location = $location,
|passData = ${this.showPassData}, |passData = ${this.showPassData},
|diagnostics = $diagnostics, |diagnostics = $diagnostics,
@ -1826,6 +1833,8 @@ object IR {
override protected var id: Identifier = randomId override protected var id: Identifier = randomId
override val name: String = "this" override val name: String = "this"
override def isReferent: Boolean = false
/** Creates a copy of `this`. /** Creates a copy of `this`.
* *
* @param location the source location that the node corresponds to * @param location the source location that the node corresponds to
@ -1894,6 +1903,8 @@ object IR {
override protected var id: Identifier = randomId override protected var id: Identifier = randomId
override val name: String = "here" override val name: String = "here"
override def isReferent: Boolean = false
/** Creates a copy of `this`. /** Creates a copy of `this`.
* *
* @param location the source location that the node corresponds to * @param location the source location that the node corresponds to
@ -4995,6 +5006,8 @@ object IR {
with IR.Name { with IR.Name {
override val name: String = originalName.name override val name: String = originalName.name
override def isReferent: Boolean = originalName.isReferent
override def mapExpressions(fn: Expression => Expression): Resolution = override def mapExpressions(fn: Expression => Expression): Resolution =
this this
@ -5052,19 +5065,51 @@ object IR {
object Resolution { 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. * An error coming from name resolver.
* *
* @param err the original error. * @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. * Provides a human-readable explanation of the error.
* @param originalName the original unresolved name. * @param originalName the original unresolved name.
* @return a human-readable message. * @return a human-readable message.
*/ */
def explain(originalName: IR.Name): String = override def explain(originalName: IR.Name): String =
err match { err match {
case BindingsMap.ResolutionAmbiguous(candidates) => case BindingsMap.ResolutionAmbiguous(candidates) =>
val firstLine = val firstLine =
@ -5077,6 +5122,10 @@ object IR {
s" Type ${cons.name} defined in module ${definitionModule.getName};" s" Type ${cons.name} defined in module ${definitionModule.getName};"
case BindingsMap.ResolvedModule(module) => case BindingsMap.ResolvedModule(module) =>
s" The module ${module.getName};" 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") (firstLine :: lines).mkString("\n")
case BindingsMap.ResolutionNotFound => case BindingsMap.ResolutionNotFound =>

View File

@ -8,10 +8,14 @@ import org.enso.interpreter.runtime.Module
* A utility structure for resolving symbols in a given module. * A utility structure for resolving symbols in a given module.
* *
* @param types the types defined in the current 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 * @param currentModule the module holding these bindings
*/ */
case class BindingsMap( case class BindingsMap(
types: List[BindingsMap.Cons], types: List[BindingsMap.Cons],
polyglotSymbols: List[BindingsMap.PolyglotSymbol],
moduleMethods: List[BindingsMap.ModuleMethod],
currentModule: Module currentModule: Module
) extends IRPass.Metadata { ) extends IRPass.Metadata {
import BindingsMap._ import BindingsMap._
@ -33,11 +37,28 @@ case class BindingsMap(
.map(ResolvedConstructor(currentModule, _)) .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] = { private def findLocalCandidates(name: String): List[ResolvedName] = {
if (currentModule.getName.item == name) { if (currentModule.getName.item == name) {
List(ResolvedModule(currentModule)) List(ResolvedModule(currentModule))
} else { } 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" "Wrong pass ordering. Running resolution on an unparsed module"
) )
} }
.flatMap(_.findConstructorCandidates(name)) .flatMap(_.findExportedSymbolsFor(name))
} }
private def handleAmbiguity( 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. * Resolves a name in the context of current module.
* *
@ -90,6 +118,52 @@ case class BindingsMap(
} }
handleAmbiguity(findExportedCandidatesInImports(name)) 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 { object BindingsMap {
@ -102,6 +176,20 @@ object BindingsMap {
*/ */
case class Cons(name: String, arity: Int) 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. * A result of successful name resolution.
*/ */
@ -123,6 +211,22 @@ object BindingsMap {
*/ */
case class ResolvedModule(module: Module) extends ResolvedName 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. * A representation of an error during name resolution.
*/ */
@ -141,9 +245,8 @@ object BindingsMap {
*/ */
case object ResolutionNotFound extends ResolutionError case object ResolutionNotFound extends ResolutionError
/** A metadata-friendly storage for resolutions */ /** A metadata-friendly storage for resolutions */
case class Resolution(target: ResolvedName) case class Resolution(target: ResolvedName) extends IRPass.Metadata {
extends IRPass.Metadata {
/** The name of the metadata as a string. */ /** The name of the metadata as a string. */
override val metadataName: String = "Resolution" override val metadataName: String = "Resolution"

View File

@ -554,7 +554,7 @@ case object AliasAnalysis extends IRPass {
): IR.Pattern = { ): IR.Pattern = {
pattern match { pattern match {
case named @ Pattern.Name(name, _, _, _) => case named @ Pattern.Name(name, _, _, _) =>
if (name.isReferant) { if (name.isReferent) {
throw new CompilerError( throw new CompilerError(
"Nested patterns should be desugared by the point of alias " + "Nested patterns should be desugared by the point of alias " +
"analysis." "analysis."
@ -666,8 +666,9 @@ case object AliasAnalysis extends IRPass {
*/ */
def copy: Graph = { def copy: Graph = {
val graph = new Graph val graph = new Graph
graph.links = links graph.links = links
graph.rootScope = rootScope.copy graph.rootScope = rootScope.copy
graph.nextIdCounter = nextIdCounter
graph graph
} }

View File

@ -2,6 +2,7 @@ package org.enso.compiler.pass.analyse
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.core.IR.Module.Scope.Import.Polyglot
import org.enso.compiler.core.ir.MetadataStorage.ToPair import org.enso.compiler.core.ir.MetadataStorage.ToPair
import org.enso.compiler.data.BindingsMap import org.enso.compiler.data.BindingsMap
import org.enso.compiler.pass.IRPass import org.enso.compiler.pass.IRPass
@ -48,9 +49,38 @@ case object BindingAnalysis extends IRPass {
case cons: IR.Module.Scope.Definition.Atom => case cons: IR.Module.Scope.Definition.Atom =>
BindingsMap.Cons(cons.name.name, cons.arguments.length) 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( ir.updateMetadata(
this -->> BindingsMap( this -->> BindingsMap(
definedConstructors, definedConstructors,
importedPolyglot,
moduleMethods,
moduleContext.module moduleContext.module
) )
) )

View File

@ -133,7 +133,7 @@ case object ComplexType extends IRPass {
val sig = lastSignature match { val sig = lastSignature match {
case Some(IR.Type.Ascription(typed, _, _, _, _)) => case Some(IR.Type.Ascription(typed, _, _, _, _)) =>
typed match { typed match {
case IR.Name.Literal(nameStr, _, _, _) => case IR.Name.Literal(nameStr, _, _, _, _) =>
if (name.name == nameStr) { if (name.name == nameStr) {
lastSignature lastSignature
} else { } else {

View File

@ -131,7 +131,7 @@ case object LambdaShorthandToLambda extends IRPass {
IR.Function.Lambda( IR.Function.Lambda(
List( List(
IR.DefinitionArgument.Specified( IR.DefinitionArgument.Specified(
IR.Name.Literal(newName.name, None), IR.Name.Literal(newName.name, isReferent = false, None),
None, None,
suspended = false, suspended = false,
None None
@ -207,7 +207,8 @@ case object LambdaShorthandToLambda extends IRPass {
IR.Function.Lambda( IR.Function.Lambda(
List( List(
IR.DefinitionArgument.Specified( IR.DefinitionArgument.Specified(
IR.Name.Literal(updatedName.get, fn.location), IR.Name
.Literal(updatedName.get, isReferent = false, fn.location),
None, None,
suspended = false, suspended = false,
None None
@ -320,7 +321,11 @@ case object LambdaShorthandToLambda extends IRPass {
case IR.CallArgument.Specified(_, value, _, _, passData, diagnostics) => case IR.CallArgument.Specified(_, value, _, _, passData, diagnostics) =>
// Note [Safe Casting to IR.Name.Literal] // Note [Safe Casting to IR.Name.Literal]
val defArgName = 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( Some(
IR.DefinitionArgument.Specified( IR.DefinitionArgument.Specified(

View File

@ -249,9 +249,9 @@ case object IgnoredBindings extends IRPass {
*/ */
def isIgnore(ir: IR.Name): Boolean = { def isIgnore(ir: IR.Name): Boolean = {
ir match { ir match {
case _: IR.Name.Blank => true case _: IR.Name.Blank => true
case IR.Name.Literal(name, _, _, _) => name == "_" case IR.Name.Literal(name, _, _, _, _) => name == "_"
case _ => false case _ => false
} }
} }

View File

@ -55,12 +55,33 @@ case object MethodDefinitions extends IRPass {
BindingsMap.ResolvedModule(availableSymbolsMap.currentModule) BindingsMap.ResolvedModule(availableSymbolsMap.currentModule)
) )
) )
case tp @ IR.Name.Qualified(List(item), _, _, _) => case tp @ IR.Name.Qualified(names, _, _, _) =>
availableSymbolsMap.resolveUppercaseName(item.name) match { val items = names.map(_.name)
availableSymbolsMap.resolveQualifiedName(items) match {
case Left(err) => case Left(err) =>
IR.Error.Resolution(tp, IR.Error.Resolution.Reason(err)) IR.Error.Resolution(tp, IR.Error.Resolution.ResolverError(err))
case Right(candidate) => case Right(value: BindingsMap.ResolvedConstructor) =>
tp.updateMetadata(this -->> BindingsMap.Resolution(candidate)) 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 tp: IR.Error.Resolution => tp
case _ => case _ =>

View File

@ -4,6 +4,7 @@ import org.enso.compiler.context.{InlineContext, ModuleContext}
import org.enso.compiler.core.IR import org.enso.compiler.core.IR
import org.enso.compiler.core.ir.MetadataStorage.ToPair import org.enso.compiler.core.ir.MetadataStorage.ToPair
import org.enso.compiler.data.BindingsMap import org.enso.compiler.data.BindingsMap
import org.enso.compiler.exception.CompilerError
import org.enso.compiler.pass.IRPass 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.compiler.pass.desugar.{GenerateMethodBodies, NestedPatternMatch} import org.enso.compiler.pass.desugar.{GenerateMethodBodies, NestedPatternMatch}
@ -69,27 +70,60 @@ object Patterns extends IRPass {
val newBranches = caseExpr.branches.map { branch => val newBranches = caseExpr.branches.map { branch =>
val resolvedPattern = branch.pattern match { val resolvedPattern = branch.pattern match {
case consPat: IR.Pattern.Constructor => 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 => case lit: IR.Name.Literal =>
val resolution = bindings.resolveUppercaseName(lit.name) Some(bindings.resolveUppercaseName(lit.name))
resolution match { case _ => None
case Left(err) =>
IR.Error.Resolution(
consPat.constructor,
IR.Error.Resolution.Reason(err)
)
case Right(value) =>
lit.updateMetadata(
this -->> BindingsMap.Resolution(value)
)
}
case other => other
} }
val resolution = resolvedName.getMetadata(this) val resolvedName = resolution
val expectedArity = resolution.map { res => .map {
case Left(err) =>
IR.Error.Resolution(
consPat.constructor,
IR.Error.Resolution.ResolverError(err)
)
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"
)
)
}
.getOrElse(consName)
val actualResolution = resolvedName.getMetadata(this)
val expectedArity = actualResolution.map { res =>
res.target match { res.target match {
case BindingsMap.ResolvedConstructor(_, cons) => cons.arity case BindingsMap.ResolvedConstructor(_, cons) => cons.arity
case BindingsMap.ResolvedModule(_) => 0 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 { expectedArity match {

View File

@ -189,7 +189,7 @@ case object SuspendedArguments extends IRPass {
signature match { signature match {
case IR.Application.Operator.Binary( case IR.Application.Operator.Binary(
l, l,
IR.Name.Literal("->", _, _, _), IR.Name.Literal("->", _, _, _, _),
r, r,
_, _,
_, _,
@ -210,8 +210,8 @@ case object SuspendedArguments extends IRPass {
*/ */
def representsSuspended(value: IR.Expression): Boolean = { def representsSuspended(value: IR.Expression): Boolean = {
value match { value match {
case IR.Name.Literal("Suspended", _, _, _) => true case IR.Name.Literal("Suspended", _, _, _, _) => true
case _ => false case _ => false
} }
} }

View File

@ -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
}
}

View File

@ -21,14 +21,29 @@ object StubIrBuilder {
* @return the built stub IR. * @return the built stub IR.
*/ */
def build(module: Module): IR.Module = { def build(module: Module): IR.Module = {
val ir = IR.Module(List(), List(), None) 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] = val definedConstructors: List[BindingsMap.Cons] =
module.getScope.getConstructors.asScala.toList.map { conses.toList.map {
case (name, cons) => case (name, cons) =>
BindingsMap.Cons(name, cons.getArity) 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( val meta = BindingsMap(
definedConstructors, definedConstructors,
polyglot,
moduleMethods,
module module
) )
ir.updateMetadata(BindingAnalysis -->> meta) ir.updateMetadata(BindingAnalysis -->> meta)

View File

@ -149,7 +149,7 @@ trait CompilerRunner {
* @return an IR name representing the name `str` * @return an IR name representing the name `str`
*/ */
def nameFromString(str: String): IR.Name.Literal = { def nameFromString(str: String): IR.Name.Literal = {
IR.Name.Literal(str, None) IR.Name.Literal(str, isReferent = false, None)
} }
// === IR Testing Utils ===================================================== // === IR Testing Utils =====================================================
@ -167,8 +167,11 @@ trait CompilerRunner {
def asMethod: IR.Module.Scope.Definition.Method = { def asMethod: IR.Module.Scope.Definition.Method = {
IR.Module.Scope.Definition.Method.Explicit( IR.Module.Scope.Definition.Method.Explicit(
IR.Name.MethodReference( IR.Name.MethodReference(
IR.Name.Qualified(List(IR.Name.Literal("TestType", None)), None), IR.Name.Qualified(
IR.Name.Literal("testMethod", None), List(IR.Name.Literal("TestType", isReferent = true, None)),
None
),
IR.Name.Literal("testMethod", isReferent = false, None),
None None
), ),
ir, ir,
@ -182,11 +185,11 @@ trait CompilerRunner {
*/ */
def asAtomDefaultArg: IR.Module.Scope.Definition.Atom = { def asAtomDefaultArg: IR.Module.Scope.Definition.Atom = {
IR.Module.Scope.Definition.Atom( IR.Module.Scope.Definition.Atom(
IR.Name.Literal("TestAtom", None), IR.Name.Literal("TestAtom", isReferent = true, None),
List( List(
IR.DefinitionArgument IR.DefinitionArgument
.Specified( .Specified(
IR.Name.Literal("arg", None), IR.Name.Literal("arg", isReferent = false, None),
Some(ir), Some(ir),
suspended = false, suspended = false,
None None

View File

@ -320,7 +320,11 @@ class AstToIrTest extends CompilerTest with Inside {
ir shouldBe an[IR.Application.Prefix] ir shouldBe an[IR.Application.Prefix]
val fn = ir.asInstanceOf[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] val fooArg = fn.arguments.head.asInstanceOf[IR.CallArgument.Specified]
fooArg.value shouldBe an[IR.Name.Literal] fooArg.value shouldBe an[IR.Name.Literal]

View File

@ -4,7 +4,7 @@ import org.enso.compiler.Passes
import org.enso.compiler.context.{FreshNameSupply, ModuleContext} import org.enso.compiler.context.{FreshNameSupply, ModuleContext}
import org.enso.compiler.core.IR import org.enso.compiler.core.IR
import org.enso.compiler.data.BindingsMap 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.analyse.BindingAnalysis
import org.enso.compiler.pass.{PassConfiguration, PassGroup, PassManager} import org.enso.compiler.pass.{PassConfiguration, PassGroup, PassManager}
import org.enso.compiler.test.CompilerTest import org.enso.compiler.test.CompilerTest
@ -50,18 +50,24 @@ class BindingAnalysisTest extends CompilerTest {
val ir = val ir =
""" """
|polyglot java import foo.bar.baz.MyClass
|
|type Foo a b c |type Foo a b c
|type Bar |type Bar
|type Baz x y |type Baz x y
| |
|Baz.foo = 123 |Baz.foo = 123
|Bar.baz = Baz 1 2 . foo |Bar.baz = Baz 1 2 . foo
|
|foo = 123
|""".stripMargin.preprocessModule.analyse |""".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( ir.getMetadata(BindingAnalysis) shouldEqual Some(
BindingsMap( BindingsMap(
List(Cons("Foo", 3), Cons("Bar", 0), Cons("Baz", 2)), List(Cons("Foo", 3), Cons("Bar", 0), Cons("Baz", 2)),
List(PolyglotSymbol("MyClass")),
List(ModuleMethod("foo")),
ctx.module ctx.module
) )
) )

View File

@ -303,7 +303,7 @@ class DataflowAnalysisTest extends CompilerTest {
val printlnFn = printlnExpr.function.asInstanceOf[IR.Name.Literal] val printlnFn = printlnExpr.function.asInstanceOf[IR.Name.Literal]
val printlnArgIO = val printlnArgIO =
printlnExpr.arguments.head.asInstanceOf[IR.CallArgument.Specified] 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 = val printlnArgB =
printlnExpr.arguments(1).asInstanceOf[IR.CallArgument.Specified] printlnExpr.arguments(1).asInstanceOf[IR.CallArgument.Specified]
val printlnArgBExpr = printlnArgB.value.asInstanceOf[IR.Name.Literal] val printlnArgBExpr = printlnArgB.value.asInstanceOf[IR.Name.Literal]

View File

@ -16,7 +16,7 @@ class GatherDiagnosticsTest extends CompilerTest {
AST.Invalid.Unrecognized("@@"), AST.Invalid.Unrecognized("@@"),
IR.Error.Syntax.UnrecognizedToken IR.Error.Syntax.UnrecognizedToken
) )
val plusOp = IR.Name.Literal("+", None) val plusOp = IR.Name.Literal("+", isReferent = false, None)
val plusApp = IR.Application.Prefix( val plusApp = IR.Application.Prefix(
plusOp, plusOp,
List( List(
@ -29,7 +29,7 @@ class GatherDiagnosticsTest extends CompilerTest {
List( List(
IR.DefinitionArgument IR.DefinitionArgument
.Specified( .Specified(
IR.Name.Literal("bar", None), IR.Name.Literal("bar", isReferent = false, None),
None, None,
suspended = false, suspended = false,
None None
@ -59,10 +59,10 @@ class GatherDiagnosticsTest extends CompilerTest {
IR.Error.Syntax.UnexpectedExpression IR.Error.Syntax.UnexpectedExpression
) )
val typeName = IR.Name.Literal("Foo", None) val typeName = IR.Name.Literal("Foo", isReferent = false, None)
val method1Name = IR.Name.Literal("bar", None) val method1Name = IR.Name.Literal("bar", isReferent = false, None)
val method2Name = IR.Name.Literal("baz", None) val method2Name = IR.Name.Literal("baz", isReferent = false, None)
val fooName = IR.Name.Literal("foo", None) val fooName = IR.Name.Literal("foo", isReferent = false, None)
val method1Ref = val method1Ref =
IR.Name.MethodReference( IR.Name.MethodReference(

View File

@ -45,14 +45,14 @@ class OperatorToFunctionTest extends CompilerTest {
// === The Tests ============================================================ // === The Tests ============================================================
"Operators" should { "Operators" should {
val opName = IR.Name.Literal("=:=", None) val opName = IR.Name.Literal("=:=", isReferent = false, None)
val left = IR.Empty(None) val left = IR.Empty(None)
val right = IR.Empty(None) val right = IR.Empty(None)
val rightArg = IR.CallArgument.Specified(None, IR.Empty(None), None) val rightArg = IR.CallArgument.Specified(None, IR.Empty(None), None)
val (operator, operatorFn) = genOprAndFn(opName, left, right) val (operator, operatorFn) = genOprAndFn(opName, left, right)
val oprArg = IR.CallArgument.Specified(None, operator, None) val oprArg = IR.CallArgument.Specified(None, operator, None)
val oprFnArg = IR.CallArgument.Specified(None, operatorFn, None) val oprFnArg = IR.CallArgument.Specified(None, operatorFn, None)
"be translated to functions" in { "be translated to functions" in {
@ -76,7 +76,10 @@ class OperatorToFunctionTest extends CompilerTest {
None None
) )
OperatorToFunction.runExpression(recursiveIR, ctx) shouldEqual recursiveIRResult OperatorToFunction.runExpression(
recursiveIR,
ctx
) shouldEqual recursiveIRResult
} }
} }
} }

View File

@ -35,7 +35,7 @@ class ApplicationSaturationTest extends CompilerTest {
val name = if (positional) { val name = if (positional) {
None None
} else { } 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)) List.fill(n)(IR.CallArgument.Specified(name, IR.Empty(None), None))
@ -83,7 +83,7 @@ class ApplicationSaturationTest extends CompilerTest {
"Known applications" should { "Known applications" should {
val plusFn = IR.Application val plusFn = IR.Application
.Prefix( .Prefix(
IR.Name.Literal("+", None), IR.Name.Literal("+", isReferent = false, None),
genNArgs(2), genNArgs(2),
hasDefaultsSuspended = false, hasDefaultsSuspended = false,
None None
@ -93,7 +93,7 @@ class ApplicationSaturationTest extends CompilerTest {
val bazFn = IR.Application val bazFn = IR.Application
.Prefix( .Prefix(
IR.Name.Literal("baz", None), IR.Name.Literal("baz", isReferent = false, None),
genNArgs(2), genNArgs(2),
hasDefaultsSuspended = false, hasDefaultsSuspended = false,
None None
@ -103,7 +103,7 @@ class ApplicationSaturationTest extends CompilerTest {
val fooFn = IR.Application val fooFn = IR.Application
.Prefix( .Prefix(
IR.Name.Literal("foo", None), IR.Name.Literal("foo", isReferent = false, None),
genNArgs(5), genNArgs(5),
hasDefaultsSuspended = false, hasDefaultsSuspended = false,
None None
@ -113,8 +113,8 @@ class ApplicationSaturationTest extends CompilerTest {
val fooFnByName = IR.Application val fooFnByName = IR.Application
.Prefix( .Prefix(
IR.Name.Literal("foo", None), IR.Name.Literal("foo", isReferent = false, None),
genNArgs(4, positional = false), genNArgs(4, positional = false),
hasDefaultsSuspended = false, hasDefaultsSuspended = false,
None None
) )
@ -159,7 +159,7 @@ class ApplicationSaturationTest extends CompilerTest {
"Unknown applications" should { "Unknown applications" should {
val unknownFn = IR.Application val unknownFn = IR.Application
.Prefix( .Prefix(
IR.Name.Literal("unknown", None), IR.Name.Literal("unknown", isReferent = false, None),
genNArgs(10), genNArgs(10),
hasDefaultsSuspended = false, hasDefaultsSuspended = false,
None None
@ -180,7 +180,7 @@ class ApplicationSaturationTest extends CompilerTest {
val empty = IR.Empty(None) val empty = IR.Empty(None)
val knownPlus = IR.Application val knownPlus = IR.Application
.Prefix( .Prefix(
IR.Name.Literal("+", None), IR.Name.Literal("+", isReferent = false, None),
genNArgs(2), genNArgs(2),
hasDefaultsSuspended = false, hasDefaultsSuspended = false,
None None
@ -190,7 +190,7 @@ class ApplicationSaturationTest extends CompilerTest {
val undersaturatedPlus = IR.Application val undersaturatedPlus = IR.Application
.Prefix( .Prefix(
IR.Name.Literal("+", None), IR.Name.Literal("+", isReferent = false, None),
genNArgs(1), genNArgs(1),
hasDefaultsSuspended = false, hasDefaultsSuspended = false,
None None
@ -200,7 +200,7 @@ class ApplicationSaturationTest extends CompilerTest {
val oversaturatedPlus = IR.Application val oversaturatedPlus = IR.Application
.Prefix( .Prefix(
IR.Name.Literal("+", None), IR.Name.Literal("+", isReferent = false, None),
genNArgs(3), genNArgs(3),
hasDefaultsSuspended = false, hasDefaultsSuspended = false,
None None
@ -222,7 +222,7 @@ class ApplicationSaturationTest extends CompilerTest {
def outerPlus(argExpr: IR.Expression): IR.Application.Prefix = { def outerPlus(argExpr: IR.Expression): IR.Application.Prefix = {
IR.Application IR.Application
.Prefix( .Prefix(
IR.Name.Literal("+", None), IR.Name.Literal("+", isReferent = false, None),
List( List(
IR.CallArgument.Specified(None, argExpr, None), IR.CallArgument.Specified(None, argExpr, None),
IR.CallArgument.Specified(None, empty, None) IR.CallArgument.Specified(None, empty, None)

View File

@ -206,13 +206,13 @@ class LambdaConsolidateTest extends CompilerTest {
List( List(
IR.DefinitionArgument IR.DefinitionArgument
.Specified( .Specified(
IR.Name.Literal("a", None), IR.Name.Literal("a", isReferent = false, None),
None, None,
suspended = false, suspended = false,
None None
), ),
IR.DefinitionArgument.Specified( IR.DefinitionArgument.Specified(
IR.Name.Literal("b", None), IR.Name.Literal("b", isReferent = false, None),
None, None,
suspended = false, suspended = false,
None None
@ -221,13 +221,13 @@ class LambdaConsolidateTest extends CompilerTest {
IR.Function.Lambda( IR.Function.Lambda(
List( List(
IR.DefinitionArgument.Specified( IR.DefinitionArgument.Specified(
IR.Name.Literal("c", None), IR.Name.Literal("c", isReferent = false, None),
None, None,
suspended = false, suspended = false,
None None
) )
), ),
IR.Name.Literal("c", None), IR.Name.Literal("c", isReferent = false, None),
None None
), ),
None None

View File

@ -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]
}
}
}

View File

@ -17,7 +17,7 @@ class ConstructorsTest extends InterpreterTest {
val patternMatchingCode = val patternMatchingCode =
""" """
|main = |main =
| x = Cons 1 Nil | x = Builtins.Cons 1 Nil
| case x of | case x of
| Cons h t -> h | Cons h t -> h
| Nil -> 0 | Nil -> 0

View File

@ -19,10 +19,10 @@ class PatternMatchTest extends InterpreterTest {
""" """
|main = |main =
| f = case _ of | f = case _ of
| Cons a _ -> a | Builtins.Cons a _ -> a
| Nil -> -10 | Builtins.Nil -> -10
| |
| (10.Cons Nil . f) - Nil.f | (Builtins.Cons 10 Builtins.Nil . f) - Nil.f
|""".stripMargin |""".stripMargin
eval(code) shouldEqual 20 eval(code) shouldEqual 20
@ -38,7 +38,7 @@ class PatternMatchTest extends InterpreterTest {
| MyAtom a -> a | MyAtom a -> a
| _ -> -100 | _ -> -100
| |
| (50.MyAtom . f) + Nil.f | (MyAtom 50 . f) + Nil.f
|""".stripMargin |""".stripMargin
eval(code) shouldEqual -50 eval(code) shouldEqual -50
@ -54,7 +54,7 @@ class PatternMatchTest extends InterpreterTest {
| MyAtom a -> a | MyAtom a -> a
| a -> a + 5 | a -> a + 5
| |
| (50.MyAtom . f) + 30.f | (MyAtom 50 . f) + 30.f
|""".stripMargin |""".stripMargin
eval(code) shouldEqual 85 eval(code) shouldEqual 85

View File

@ -43,22 +43,26 @@ object WithDebugCommand {
val printAssemblyOption = "--printAssembly" val printAssemblyOption = "--printAssembly"
val debuggerOption = "--debugger"
val argSeparator = "--" val argSeparator = "--"
val commandName = "withDebug" val commandName = "withDebug"
val benchOnlyCommandName = "benchOnly" val benchOnlyCommandName = "benchOnly"
val runCommandName = "run" val runCommandName = "run"
val testOnlyCommandName = "testOnly"
/** The main logic for parsing and transforming the debug flags into JVM level flags */ /** The main logic for parsing and transforming the debug flags into JVM level flags */
def withDebug: Command = Command.args(commandName, "<arguments>") { def withDebug: Command =
(state, args) => Command.args(commandName, "<arguments>") { (state, args) =>
val (debugFlags, prefixedRunArgs) = args.span(_ != argSeparator) val (debugFlags, prefixedRunArgs) = args.span(_ != argSeparator)
val runArgs = " " + prefixedRunArgs.drop(1).mkString(" ") val runArgs = " " + prefixedRunArgs.drop(1).mkString(" ")
val taskKey = val taskKey =
if (debugFlags.contains(benchOnlyCommandName)) BenchTasks.benchOnly if (debugFlags.contains(benchOnlyCommandName)) BenchTasks.benchOnly
else if (debugFlags.contains(runCommandName)) Compile / Keys.run else if (debugFlags.contains(runCommandName)) Compile / Keys.run
else if (debugFlags.contains(testOnlyCommandName)) Test / Keys.testOnly
else throw new IllegalArgumentException("Invalid command name.") else throw new IllegalArgumentException("Invalid command name.")
val dumpGraphsOpts = val dumpGraphsOpts =
@ -72,11 +76,18 @@ object WithDebugCommand {
if (debugFlags.contains(printAssemblyOption)) if (debugFlags.contains(printAssemblyOption))
trufflePrintAssemblyOptions trufflePrintAssemblyOptions
else Seq() 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( val javaOpts: Seq[String] = Seq(
truffleNoBackgroundCompilationOptions, truffleNoBackgroundCompilationOptions,
dumpGraphsOpts, dumpGraphsOpts,
showCompilationsOpts, showCompilationsOpts,
printAssemblyOpts printAssemblyOpts,
debuggerOpts
).flatten ).flatten
val extracted = Project.extract(state) val extracted = Project.extract(state)
@ -88,5 +99,5 @@ object WithDebugCommand {
.extract(withJavaOpts) .extract(withJavaOpts)
.runInputTask(taskKey, runArgs, withJavaOpts) .runInputTask(taskKey, runArgs, withJavaOpts)
state state
} }
} }

View File

@ -2,8 +2,10 @@ import Base.Test
import Test.List_Spec import Test.List_Spec
import Test.Number_Spec import Test.Number_Spec
import Test.Import_Loop_Spec import Test.Import_Loop_Spec
import Test.Names_Spec
main = Suite.runMain <| main = Suite.runMain <|
List_Spec.spec List_Spec.spec
Number_Spec.spec Number_Spec.spec
Import_Loop_Spec.spec Import_Loop_Spec.spec
Names_Spec.spec

View 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

View 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