mirror of
https://github.com/enso-org/enso.git
synced 2024-09-11 13:15:52 +03:00
Add support for writing type signatures (#833)
1. Adds support for type signature syntax. 2. Lifts the type operators into metdata. 3. Uses the signatures to determine arg suspension.
This commit is contained in:
parent
df887f20f2
commit
dd0f93c328
@ -179,6 +179,17 @@ layout of the code has no impact on semantics of the code:
|
||||
x.do_thing
|
||||
```
|
||||
|
||||
Alternatively, it is sufficient to type the binding for the block as
|
||||
`Suspended a` where `a` is the type of the block.
|
||||
|
||||
```ruby
|
||||
susp : Suspended a =
|
||||
x = foo x y z
|
||||
x.do_thing
|
||||
```
|
||||
|
||||
It should be noted that this does not yet work.
|
||||
|
||||
The following rules apply to code blocks:
|
||||
|
||||
- Code blocks are desugared into in-order applications of monadic bind (as in
|
||||
|
@ -75,6 +75,7 @@ pattern context. The following spans are pattern contexts:
|
||||
- The left-hand-side of the assignment operator (`=`).
|
||||
- The right-hand-side of the ascription operator (`:`).
|
||||
- The left-hand-side of the arrow operator (`->`).
|
||||
- Within the curly braces `{}` delimiting a typeset literal.
|
||||
|
||||
The following behaviours occur within a pattern context:
|
||||
|
||||
|
@ -78,9 +78,9 @@ working with types. These are listed below.
|
||||
| `<:` | `> !`, `< \|`, `> in` | 4 | Left | Asserts that the left operand is structurally subsumed by the right. |
|
||||
| `~` | `== <:` | 4 | Left | Asserts that the left and right operands are structurally equal. |
|
||||
| `;` | `< :`, `> =` | -2 | Left | Concatenates the left and right operand typesets to create a new typeset. |
|
||||
| `\|` | `> <:`, `> !`, `> in`, `> :` | 6 | Left | Computes the union of the left and right operand typesets. |
|
||||
| `&` | `= \|` | 6 | Left | Computes the intersection of the left and right operand typesets. |
|
||||
| `\` | `< \|`, `> <:` | 5 | Left | Computes the subtraction of the right typeset from the left typeset. |
|
||||
| `\|` | `> <:`, `> !`, `> in`, `> :` | 5 | Left | Computes the union of the left and right operand typesets. |
|
||||
| `&` | `> \|` | 6 | Left | Computes the intersection of the left and right operand typesets. |
|
||||
| `\` | `> &` | 7 | Left | Computes the subtraction of the right typeset from the left typeset. |
|
||||
| `:=` | `< :`, `> =`, `> ;` | -1 | Left | Creates a typeset member by assigning a value to a label. |
|
||||
|
||||
Solving this set of inequalities produces the _relative_ precedence levels for
|
||||
@ -118,9 +118,8 @@ bind (`=`) has a relative level of -3 in this ordering.
|
||||
(assert (> tsUnion err))
|
||||
(assert (> tsUnion in))
|
||||
(assert (> tsUnion ascrip))
|
||||
(assert (= tsInter tsUnion))
|
||||
(assert (< minus tsUnion))
|
||||
(assert (> minus sub))
|
||||
(assert (> tsInter tsUnion))
|
||||
(assert (> minus tsInter))
|
||||
(assert (< tsMember ascrip))
|
||||
(assert (> tsMember bind))
|
||||
(assert (> tsMember tsConcat))
|
||||
@ -152,6 +151,9 @@ These work as follows.
|
||||
label : Type := value
|
||||
```
|
||||
|
||||
Please note that the right-hand-side of the `:=` operator is _not_ a pattern
|
||||
context.
|
||||
|
||||
- **Member Concatenation:** Members can be combined into a typeset using the
|
||||
concatenation operator `;`.
|
||||
|
||||
@ -159,8 +161,10 @@ These work as follows.
|
||||
x ; y
|
||||
```
|
||||
|
||||
- **Typeset Literals:** A literal can be written using curly braces (`{}`) to
|
||||
delimit the literal.
|
||||
- **Typeset Literals:** A typeset literal consists of zero or more typeset
|
||||
member definitions concatenated while surrounded by curly braces `{}`. The
|
||||
braces are necessary as they delimit a pattern context to allow the
|
||||
introduction of new identifiers.
|
||||
|
||||
```ruby
|
||||
{ x: T ; y: Q }
|
||||
|
@ -97,7 +97,7 @@ The other key notion of typesets is that typesets are matched _structurally_,
|
||||
subject to the rules for nominal typing of atoms discussed above.
|
||||
|
||||
- Typeset members are themselves typesets.
|
||||
- A typeset member _must_ have a label, but may also have a type and a value
|
||||
- A typeset member _must_ have a label, but may also have a type and a value
|
||||
(`label : Type := value`)
|
||||
- An unspecified type is considered to be a free type variable.
|
||||
- The label and the type become part of the typing judgement where present, and
|
||||
@ -165,8 +165,8 @@ Just.nothing = not isJust
|
||||
|
||||
### Typeset Operators
|
||||
Enso defines a set of operations on typesets that can be used to combine and
|
||||
manipulate them. Any use of these operators introduces typing evidence which
|
||||
may later be discharged through pattern matching.
|
||||
manipulate them. Any use of these operators introduces typing evidence which
|
||||
may later be discharged through pattern matching.
|
||||
|
||||
They are as follows:
|
||||
|
||||
|
@ -9,11 +9,7 @@ import org.enso.compiler.pass.optimise.{
|
||||
LambdaConsolidate,
|
||||
UnreachableMatchBranches
|
||||
}
|
||||
import org.enso.compiler.pass.resolve.{
|
||||
DocumentationComments,
|
||||
IgnoredBindings,
|
||||
OverloadsResolution
|
||||
}
|
||||
import org.enso.compiler.pass.resolve._
|
||||
import org.enso.compiler.pass.{IRPass, PassConfiguration, PassManager}
|
||||
|
||||
class Passes(passes: Option[List[IRPass]] = None) {
|
||||
@ -37,8 +33,12 @@ class Passes(passes: Option[List[IRPass]] = None) {
|
||||
UnreachableMatchBranches,
|
||||
NestedPatternMatch,
|
||||
IgnoredBindings,
|
||||
TypeFunctions,
|
||||
TypeSignatures,
|
||||
AliasAnalysis,
|
||||
LambdaConsolidate,
|
||||
AliasAnalysis,
|
||||
SuspendedArguments,
|
||||
OverloadsResolution,
|
||||
AliasAnalysis,
|
||||
DemandAnalysis,
|
||||
|
@ -3,9 +3,9 @@ package org.enso.compiler.codegen
|
||||
import cats.Foldable
|
||||
import cats.implicits._
|
||||
import org.enso.compiler.core.IR
|
||||
import org.enso.compiler.core.IR.Name.MethodReference
|
||||
import org.enso.compiler.core.IR._
|
||||
import org.enso.compiler.exception.UnhandledEntity
|
||||
import org.enso.interpreter.Constants
|
||||
import org.enso.syntax.text.AST
|
||||
|
||||
import scala.annotation.tailrec
|
||||
@ -142,45 +142,89 @@ object AstToIr {
|
||||
Error.Syntax(inputAst, Error.Syntax.InvalidTypeDefinition)
|
||||
}
|
||||
case AstView.MethodDefinition(targetPath, name, args, definition) =>
|
||||
val (path, pathLoc) = if (targetPath.nonEmpty) {
|
||||
val nameStr = name match { case AST.Ident.Var.any(name) => name }
|
||||
|
||||
val methodRef = if (targetPath.nonEmpty) {
|
||||
val pathSegments = targetPath.collect {
|
||||
case AST.Ident.Cons.any(c) => c
|
||||
}
|
||||
val pathNames = pathSegments.map(c =>
|
||||
IR.Name.Literal(c.name, getIdentifiedLocation(c))
|
||||
)
|
||||
|
||||
val pathStr = pathSegments.map(_.name).mkString(".")
|
||||
val loc = pathSegments.headOption
|
||||
.flatMap(_.location)
|
||||
.flatMap(locationStart =>
|
||||
pathSegments.lastOption
|
||||
.flatMap(_.location)
|
||||
.flatMap(locationEnd =>
|
||||
Some(locationStart.copy(end = locationEnd.end))
|
||||
)
|
||||
val methodSegments = pathNames :+ Name.Literal(
|
||||
nameStr.name,
|
||||
getIdentifiedLocation(nameStr)
|
||||
)
|
||||
|
||||
(pathStr, loc)
|
||||
Name.MethodReference(
|
||||
methodSegments.init,
|
||||
methodSegments.last,
|
||||
MethodReference.genLocation(methodSegments)
|
||||
)
|
||||
} else {
|
||||
(Constants.Names.CURRENT_MODULE, None)
|
||||
val methodSegments = List(
|
||||
Name.Here(None),
|
||||
Name.Literal(nameStr.name, getIdentifiedLocation(nameStr))
|
||||
)
|
||||
|
||||
Name.MethodReference(
|
||||
List(methodSegments.head),
|
||||
methodSegments.last,
|
||||
MethodReference.genLocation(methodSegments)
|
||||
)
|
||||
}
|
||||
|
||||
val nameStr = name match { case AST.Ident.Var.any(name) => name }
|
||||
|
||||
Module.Scope.Definition.Method.Binding(
|
||||
Name.Literal(path, pathLoc.map(IdentifiedLocation(_))),
|
||||
Name.Literal(nameStr.name, getIdentifiedLocation(nameStr)),
|
||||
methodRef,
|
||||
args.map(translateArgumentDefinition(_)),
|
||||
translateExpression(definition),
|
||||
getIdentifiedLocation(inputAst)
|
||||
)
|
||||
case AstView.FunctionSugar(name, args, body) =>
|
||||
Module.Scope.Definition.Method.Binding(
|
||||
val methodSegments = List(
|
||||
Name.Here(None),
|
||||
Name.Literal(name.name, getIdentifiedLocation(name)),
|
||||
Name.Literal(name.name, getIdentifiedLocation(name))
|
||||
)
|
||||
val methodReference = Name.MethodReference(
|
||||
List(methodSegments.head),
|
||||
methodSegments.last,
|
||||
MethodReference.genLocation(methodSegments)
|
||||
)
|
||||
|
||||
Module.Scope.Definition.Method.Binding(
|
||||
methodReference,
|
||||
args.map(translateArgumentDefinition(_)),
|
||||
translateExpression(body),
|
||||
getIdentifiedLocation(inputAst)
|
||||
)
|
||||
case AST.Comment.any(comment) => translateComment(comment)
|
||||
case AstView.TypeAscription(typed, sig) =>
|
||||
typed match {
|
||||
case AST.Ident.any(ident) =>
|
||||
val methodSegments = List(
|
||||
Name.Here(None),
|
||||
Name.Literal(ident.name, getIdentifiedLocation(ident))
|
||||
)
|
||||
val methodReference = Name.MethodReference(
|
||||
List(methodSegments.head),
|
||||
methodSegments.last,
|
||||
MethodReference.genLocation(methodSegments)
|
||||
)
|
||||
|
||||
IR.Type.Ascription(
|
||||
methodReference,
|
||||
translateExpression(sig),
|
||||
getIdentifiedLocation(inputAst)
|
||||
)
|
||||
case AstView.MethodReference(_, _) =>
|
||||
IR.Type.Ascription(
|
||||
translateMethodReference(typed),
|
||||
translateExpression(sig),
|
||||
getIdentifiedLocation(inputAst)
|
||||
)
|
||||
case _ => Error.Syntax(typed, Error.Syntax.InvalidStandaloneSignature)
|
||||
}
|
||||
case _ =>
|
||||
throw new UnhandledEntity(inputAst, "translateModuleSymbol")
|
||||
}
|
||||
@ -222,6 +266,12 @@ object AstToIr {
|
||||
case atom @ AstView.Atom(_, _) => translateModuleSymbol(atom)
|
||||
case fs @ AstView.FunctionSugar(_, _, _) => translateExpression(fs)
|
||||
case AST.Comment.any(inputAST) => translateComment(inputAST)
|
||||
case AstView.TypeAscription(typed, sig) =>
|
||||
IR.Type.Ascription(
|
||||
translateExpression(typed),
|
||||
translateExpression(sig),
|
||||
getIdentifiedLocation(inputAst)
|
||||
)
|
||||
case assignment @ AstView.BasicAssignment(_, _) =>
|
||||
translateExpression(assignment)
|
||||
case _ =>
|
||||
@ -229,6 +279,23 @@ object AstToIr {
|
||||
}
|
||||
}
|
||||
|
||||
/** Translates a method reference from [[AST]] into [[IR]].
|
||||
*
|
||||
* @param inputAst the method reference to translate
|
||||
* @return the [[IR]] representation of `inputAst`
|
||||
*/
|
||||
def translateMethodReference(inputAst: AST): IR.Name.MethodReference = {
|
||||
inputAst match {
|
||||
case AstView.MethodReference(path, methodName) =>
|
||||
IR.Name.MethodReference(
|
||||
path.map(translateExpression(_).asInstanceOf[IR.Name]),
|
||||
translateExpression(methodName).asInstanceOf[IR.Name],
|
||||
getIdentifiedLocation(inputAst)
|
||||
)
|
||||
case _ => throw new UnhandledEntity(inputAst, "translateMethodReference")
|
||||
}
|
||||
}
|
||||
|
||||
/** Translates an arbitrary program expression from [[AST]] into [[IR]].
|
||||
*
|
||||
* @param maybeParensedInput the expresion to be translated
|
||||
@ -316,6 +383,11 @@ object AstToIr {
|
||||
case AST.Literal.any(inputAST) => translateLiteral(inputAST)
|
||||
case AST.Group.any(inputAST) => translateGroup(inputAST)
|
||||
case AST.Ident.any(inputAST) => translateIdent(inputAST)
|
||||
case AST.TypesetLiteral.any(tSet) =>
|
||||
IR.Application.Literal.Typeset(
|
||||
tSet.expression.map(translateExpression),
|
||||
getIdentifiedLocation(tSet)
|
||||
)
|
||||
case AST.SequenceLiteral.any(inputAST) =>
|
||||
translateSequenceLiteral(inputAST)
|
||||
case AstView.Block(lines, retLine) =>
|
||||
@ -518,12 +590,6 @@ object AstToIr {
|
||||
*/
|
||||
def translateApplicationLike(callable: AST): Expression = {
|
||||
callable match {
|
||||
case AstView.ContextAscription(expr, context) =>
|
||||
Type.Context(
|
||||
translateExpression(expr),
|
||||
translateExpression(context),
|
||||
getIdentifiedLocation(callable)
|
||||
)
|
||||
case AstView.Application(name, args) =>
|
||||
val (validArguments, hasDefaultsSuspended) =
|
||||
calculateDefaultsSuspension(args)
|
||||
|
@ -183,25 +183,6 @@ object AstView {
|
||||
}
|
||||
}
|
||||
|
||||
object ContextAscription {
|
||||
|
||||
/** Matches a usage of the `in` keyword for ascribing a monadic context to
|
||||
* an expression.
|
||||
*
|
||||
* @param ast the ast structure to match on
|
||||
* @return a pair containing the expression and the context
|
||||
*/
|
||||
def unapply(ast: AST): Option[(AST, AST)] = {
|
||||
ast match {
|
||||
case MaybeParensed(
|
||||
AST.App.Prefix(expr, AST.App.Prefix(AST.Ident.Var("in"), context))
|
||||
) =>
|
||||
Some((expr, context))
|
||||
case _ => None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
object LazyArgument {
|
||||
|
||||
/** Matches on a lazy argument definition or usage.
|
||||
@ -791,4 +772,18 @@ object AstView {
|
||||
case _ => None
|
||||
}
|
||||
}
|
||||
|
||||
object TypeAscription {
|
||||
|
||||
/** Matches a usage of the type ascription operator `:`.
|
||||
*
|
||||
* @param ast the structure to try and match on
|
||||
* @return the typed expression, and the ascribed type
|
||||
*/
|
||||
def unapply(ast: AST): Option[(AST, AST)] = ast match {
|
||||
case MaybeParensed(AST.App.Infix(typed, AST.Ident.Opr(":"), sig)) =>
|
||||
Some((typed, sig))
|
||||
case _ => None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -5,16 +5,11 @@ import com.oracle.truffle.api.source.{Source, SourceSection}
|
||||
import org.enso.compiler.core.IR
|
||||
import org.enso.compiler.core.IR.Module.Scope.Import
|
||||
import org.enso.compiler.core.IR.{Error, IdentifiedLocation, Pattern}
|
||||
import org.enso.compiler.exception.{
|
||||
BadPatternMatch,
|
||||
CompilerError,
|
||||
UnhandledEntity
|
||||
}
|
||||
import org.enso.compiler.exception.{BadPatternMatch, CompilerError}
|
||||
import org.enso.compiler.pass.analyse.AliasAnalysis.Graph.{Scope => AliasScope}
|
||||
import org.enso.compiler.pass.analyse.AliasAnalysis.{Graph => AliasGraph}
|
||||
import org.enso.compiler.pass.analyse.{
|
||||
AliasAnalysis,
|
||||
CachePreferenceAnalysis,
|
||||
DataflowAnalysis,
|
||||
TailCall
|
||||
}
|
||||
@ -345,7 +340,7 @@ class IrToTruffle(
|
||||
* @param ir the IR to generate code for
|
||||
* @return a truffle expression that represents the same program as `ir`
|
||||
*/
|
||||
def run(ir: IR): RuntimeExpression = {
|
||||
def run(ir: IR.Expression): RuntimeExpression = {
|
||||
val tailMeta = ir.unsafeGetMetadata(
|
||||
TailCall,
|
||||
"Missing tail call information on method."
|
||||
@ -359,6 +354,11 @@ class IrToTruffle(
|
||||
case function: IR.Function => processFunction(function)
|
||||
case binding: IR.Expression.Binding => processBinding(binding)
|
||||
case caseExpr: IR.Case => processCase(caseExpr)
|
||||
case typ: IR.Type => processType(typ)
|
||||
case _: IR.Empty =>
|
||||
throw new CompilerError(
|
||||
"Empty IR nodes should not exist during code generation."
|
||||
)
|
||||
case _: IR.Comment =>
|
||||
throw new CompilerError(
|
||||
"Comments should not be present during codegen."
|
||||
@ -368,7 +368,6 @@ class IrToTruffle(
|
||||
throw new CompilerError(
|
||||
s"Foreign expressions not yet implemented: $ir."
|
||||
)
|
||||
case _ => throw new UnhandledEntity(ir, "run")
|
||||
}
|
||||
|
||||
runtimeExpression.setTail(tailMeta)
|
||||
@ -428,7 +427,25 @@ class IrToTruffle(
|
||||
}
|
||||
}
|
||||
|
||||
/** Performs code generation for Enso case expression.
|
||||
/** Performs code generation for an Enso type operator.
|
||||
*
|
||||
* @param value the type operation to generate code for
|
||||
* @return the truffle nodes corresponding to `value`
|
||||
*/
|
||||
def processType(value: IR.Type): RuntimeExpression = {
|
||||
setLocation(
|
||||
ErrorNode.build(
|
||||
context.getBuiltins
|
||||
.syntaxError()
|
||||
.newInstance(
|
||||
"Type operators are not currently supported at runtime."
|
||||
)
|
||||
),
|
||||
value.location
|
||||
)
|
||||
}
|
||||
|
||||
/** Performs code generation for an Enso case expression.
|
||||
*
|
||||
* @param caseExpr the case expression to generate code for
|
||||
* @return the truffle nodes corresponding to `caseExpr`
|
||||
@ -437,7 +454,7 @@ class IrToTruffle(
|
||||
case IR.Case.Expr(scrutinee, branches, location, _, _) =>
|
||||
val scrutineeNode = this.run(scrutinee)
|
||||
|
||||
val maybeCases = branches.map(processCaseBranch)
|
||||
val maybeCases = branches.map(processCaseBranch)
|
||||
val allCasesValid = maybeCases.forall(_.isRight)
|
||||
|
||||
// TODO [AA] This is until we can resolve this statically in the
|
||||
@ -676,6 +693,10 @@ class IrToTruffle(
|
||||
throw new CompilerError(
|
||||
"Blanks should not be present at codegen time."
|
||||
)
|
||||
case _: IR.Name.MethodReference =>
|
||||
throw new CompilerError(
|
||||
"Method references should not be present at codegen time."
|
||||
)
|
||||
}
|
||||
|
||||
setLocation(nameExpr, name.location)
|
||||
@ -714,6 +735,8 @@ class IrToTruffle(
|
||||
context.getBuiltins.compileError().newInstance(err.message)
|
||||
case err: Error.Redefined.ThisArg =>
|
||||
context.getBuiltins.compileError().newInstance(err.message)
|
||||
case err: Error.Unexpected.TypeSignature =>
|
||||
context.getBuiltins.compileError().newInstance(err.message)
|
||||
}
|
||||
setLocation(ErrorNode.build(payload), error.location)
|
||||
}
|
||||
@ -860,6 +883,17 @@ class IrToTruffle(
|
||||
case IR.Application.Literal.Sequence(items, location, _, _) =>
|
||||
val itemNodes = items.map(run).toArray
|
||||
setLocation(SequenceLiteralNode.build(itemNodes), location)
|
||||
case _: IR.Application.Literal.Typeset =>
|
||||
setLocation(
|
||||
ErrorNode.build(
|
||||
context.getBuiltins
|
||||
.syntaxError()
|
||||
.newInstance(
|
||||
"Typeset literals are not yet supported at runtime."
|
||||
)
|
||||
),
|
||||
application.location
|
||||
)
|
||||
case op: IR.Application.Operator.Binary =>
|
||||
throw new CompilerError(
|
||||
s"Explicit operators not supported during codegen but $op found"
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -137,7 +137,7 @@ case object AliasAnalysis extends IRPass {
|
||||
|
||||
ir match {
|
||||
case m @ IR.Module.Scope.Definition.Method
|
||||
.Explicit(_, _, body, _, _, _) =>
|
||||
.Explicit(_, body, _, _, _) =>
|
||||
body match {
|
||||
case _: IR.Function =>
|
||||
m.copy(
|
||||
@ -173,6 +173,11 @@ case object AliasAnalysis extends IRPass {
|
||||
throw new CompilerError(
|
||||
"Documentation should not exist as an entity during alias analysis."
|
||||
)
|
||||
case _: IR.Type.Ascription =>
|
||||
throw new CompilerError(
|
||||
"Type signatures should not exist at the top level during " +
|
||||
"alias analysis."
|
||||
)
|
||||
case err: IR.Error => err
|
||||
}
|
||||
}
|
||||
@ -196,7 +201,7 @@ case object AliasAnalysis extends IRPass {
|
||||
expression: IR.Expression,
|
||||
graph: Graph,
|
||||
parentScope: Scope,
|
||||
lambdaReuseScope: Boolean = false,
|
||||
lambdaReuseScope: Boolean = false
|
||||
): IR.Expression = {
|
||||
expression match {
|
||||
case fn: IR.Function =>
|
||||
@ -379,6 +384,11 @@ case object AliasAnalysis extends IRPass {
|
||||
app.copy(target = analyseExpression(expr, graph, scope))
|
||||
case app @ IR.Application.Literal.Sequence(items, _, _, _) =>
|
||||
app.copy(items = items.map(analyseExpression(_, graph, scope)))
|
||||
case tSet @ IR.Application.Literal.Typeset(expr, _, _, _) =>
|
||||
val newScope = scope.addChild()
|
||||
tSet
|
||||
.copy(expression = expr.map(analyseExpression(_, graph, newScope)))
|
||||
.updateMetadata(this -->> Info.Scope.Child(graph, newScope))
|
||||
case _: IR.Application.Operator.Binary =>
|
||||
throw new CompilerError(
|
||||
"Binary operator occurred during Alias Analysis."
|
||||
@ -441,7 +451,7 @@ case object AliasAnalysis extends IRPass {
|
||||
body = analyseExpression(
|
||||
body,
|
||||
graph,
|
||||
currentScope,
|
||||
currentScope
|
||||
)
|
||||
)
|
||||
.updateMetadata(this -->> Info.Scope.Child(graph, currentScope))
|
||||
@ -527,7 +537,7 @@ case object AliasAnalysis extends IRPass {
|
||||
expression = analyseExpression(
|
||||
branch.expression,
|
||||
graph,
|
||||
currentScope,
|
||||
currentScope
|
||||
)
|
||||
)
|
||||
.updateMetadata(this -->> Info.Scope.Child(graph, currentScope))
|
||||
|
@ -93,7 +93,7 @@ case object CachePreferenceAnalysis extends IRPass {
|
||||
)
|
||||
.updateMetadata(this -->> weights)
|
||||
case method @ IR.Module.Scope.Definition.Method
|
||||
.Explicit(_, _, body, _, _, _) =>
|
||||
.Explicit(_, body, _, _, _) =>
|
||||
method
|
||||
.copy(body = analyseExpression(body, weights))
|
||||
.updateMetadata(this -->> weights)
|
||||
@ -109,9 +109,14 @@ case object CachePreferenceAnalysis extends IRPass {
|
||||
)
|
||||
case _: IR.Comment.Documentation =>
|
||||
throw new CompilerError(
|
||||
"Documentation should not exist as an entity during cacache " +
|
||||
"Documentation should not exist as an entity during cache " +
|
||||
"preference analysis."
|
||||
)
|
||||
case _: IR.Type.Ascription =>
|
||||
throw new CompilerError(
|
||||
"Type signatures should not exist at the top level during " +
|
||||
"cache preference analysis."
|
||||
)
|
||||
case err: IR.Error => err
|
||||
}
|
||||
|
||||
|
@ -100,7 +100,7 @@ case object DataflowAnalysis extends IRPass {
|
||||
)
|
||||
.updateMetadata(this -->> info)
|
||||
case method @ IR.Module.Scope.Definition.Method
|
||||
.Explicit(_, _, body, _, _, _) =>
|
||||
.Explicit(_, body, _, _, _) =>
|
||||
info.updateAt(asStatic(body), Set(asStatic(method)))
|
||||
|
||||
method
|
||||
@ -122,6 +122,11 @@ case object DataflowAnalysis extends IRPass {
|
||||
throw new CompilerError(
|
||||
"Documentation should not exist as an entity during dataflow analysis."
|
||||
)
|
||||
case _: IR.Type.Ascription =>
|
||||
throw new CompilerError(
|
||||
"Type signatures should not exist at the top level during " +
|
||||
"dataflow analysis."
|
||||
)
|
||||
case err: IR.Error => err
|
||||
}
|
||||
}
|
||||
@ -250,6 +255,12 @@ case object DataflowAnalysis extends IRPass {
|
||||
vector
|
||||
.copy(items = items.map(analyseExpression(_, info)))
|
||||
.updateMetadata(this -->> info)
|
||||
case tSet @ IR.Application.Literal.Typeset(expr, _, _, _) =>
|
||||
expr.foreach(exp => info.updateAt(asStatic(exp), Set(asStatic(tSet))))
|
||||
|
||||
tSet
|
||||
.copy(expression = expr.map(analyseExpression(_, info)))
|
||||
.updateMetadata(this -->> info)
|
||||
case _: IR.Application.Operator =>
|
||||
throw new CompilerError("Unexpected operator during Dataflow Analysis.")
|
||||
}
|
||||
@ -285,6 +296,16 @@ case object DataflowAnalysis extends IRPass {
|
||||
context = analyseExpression(context, info)
|
||||
)
|
||||
.updateMetadata(this -->> info)
|
||||
case err @ IR.Type.Error(typed, error, _, _, _) =>
|
||||
info.updateAt(asStatic(typed), Set(asStatic(err)))
|
||||
info.updateAt(asStatic(error), Set(asStatic(err)))
|
||||
|
||||
err
|
||||
.copy(
|
||||
typed = analyseExpression(typed, info),
|
||||
error = analyseExpression(error, info)
|
||||
)
|
||||
.updateMetadata(this -->> info)
|
||||
case member @ IR.Type.Set.Member(_, memberType, value, _, _, _) =>
|
||||
info.updateAt(asStatic(memberType), Set(asStatic(member)))
|
||||
info.updateAt(asStatic(value), Set(asStatic(member)))
|
||||
|
@ -162,6 +162,11 @@ case object DemandAnalysis extends IRPass {
|
||||
case lit: IR.Name.Literal => lit.copy(location = newNameLocation)
|
||||
case ths: IR.Name.This => ths.copy(location = newNameLocation)
|
||||
case here: IR.Name.Here => here.copy(location = newNameLocation)
|
||||
case _: IR.Name.MethodReference =>
|
||||
throw new CompilerError(
|
||||
"Method references should not be present by the time demand " +
|
||||
"analysis runs."
|
||||
)
|
||||
case _: IR.Name.Blank =>
|
||||
throw new CompilerError(
|
||||
"Blanks should not be present by the time demand analysis runs."
|
||||
@ -210,9 +215,14 @@ case object DemandAnalysis extends IRPass {
|
||||
)
|
||||
)
|
||||
)
|
||||
case _ =>
|
||||
case tSet @ IR.Application.Literal.Typeset(expr, _, _, _) =>
|
||||
tSet.copy(
|
||||
expression =
|
||||
expr.map(analyseExpression(_, isInsideCallArgument = false))
|
||||
)
|
||||
case _: IR.Application.Operator =>
|
||||
throw new CompilerError(
|
||||
"Unexpected application type during demand analysis."
|
||||
"Operators should not be present during demand analysis."
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -82,7 +82,7 @@ case object TailCall extends IRPass {
|
||||
): IR.Module.Scope.Definition = {
|
||||
definition match {
|
||||
case method @ IR.Module.Scope.Definition.Method
|
||||
.Explicit(_, _, body, _, _, _) =>
|
||||
.Explicit(_, body, _, _, _) =>
|
||||
method
|
||||
.copy(
|
||||
body = analyseExpression(body, isInTailPosition = true)
|
||||
@ -108,6 +108,11 @@ case object TailCall extends IRPass {
|
||||
throw new CompilerError(
|
||||
"Documentation should not exist as an entity during tail call analysis."
|
||||
)
|
||||
case _: IR.Type.Ascription =>
|
||||
throw new CompilerError(
|
||||
"Type signatures should not exist at the top level during " +
|
||||
"tail call analysis."
|
||||
)
|
||||
case err: IR.Error => err
|
||||
}
|
||||
}
|
||||
@ -212,6 +217,12 @@ case object TailCall extends IRPass {
|
||||
items.map(analyseExpression(_, isInTailPosition = false))
|
||||
)
|
||||
.updateMetadata(this -->> TailPosition.fromBool(isInTailPosition))
|
||||
case tSet @ IR.Application.Literal.Typeset(expr, _, _, _) =>
|
||||
tSet
|
||||
.copy(expression =
|
||||
expr.map(analyseExpression(_, isInTailPosition = false))
|
||||
)
|
||||
.updateMetadata(this -->> TailPosition.fromBool(isInTailPosition))
|
||||
case _: IR.Application.Operator =>
|
||||
throw new CompilerError("Unexpected binary operator.")
|
||||
}
|
||||
|
@ -2,8 +2,10 @@ package org.enso.compiler.pass.desugar
|
||||
|
||||
import org.enso.compiler.context.{InlineContext, ModuleContext}
|
||||
import org.enso.compiler.core.IR
|
||||
import org.enso.compiler.core.IR.IdentifiedLocation
|
||||
import org.enso.compiler.core.IR.Module.Scope.Definition
|
||||
import org.enso.compiler.core.IR.Module.Scope.Definition.Method
|
||||
import org.enso.compiler.core.IR.Name.MethodReference
|
||||
import org.enso.compiler.exception.CompilerError
|
||||
import org.enso.compiler.pass.IRPass
|
||||
import org.enso.compiler.pass.analyse.{
|
||||
@ -12,6 +14,7 @@ import org.enso.compiler.pass.analyse.{
|
||||
DemandAnalysis,
|
||||
TailCall
|
||||
}
|
||||
import org.enso.compiler.pass.desugar.ComplexType.genMethodDef
|
||||
import org.enso.compiler.pass.lint.UnusedBindings
|
||||
import org.enso.compiler.pass.optimise.{
|
||||
ApplicationSaturation,
|
||||
@ -105,32 +108,88 @@ case object ComplexType extends IRPass {
|
||||
case n: IR.Name => n
|
||||
}
|
||||
val namesToDefineMethodsOn = atomIncludes ++ atomDefs.map(_.name)
|
||||
val methods = typ.body.collect {
|
||||
case b: IR.Expression.Binding => b
|
||||
case f: IR.Function.Binding => f
|
||||
|
||||
val remainingEntities = typ.body.filterNot {
|
||||
case _: IR.Module.Scope.Definition.Atom => true
|
||||
case _: IR.Name => true
|
||||
case _ => false
|
||||
}
|
||||
|
||||
if ((atomDefs ::: atomIncludes ::: methods).length != typ.body.length) {
|
||||
throw new CompilerError(
|
||||
"All bindings in a type definition body should be accounted for."
|
||||
var lastSignature: Option[IR.Type.Ascription] = None
|
||||
|
||||
/** Pairs up signatures with method definitions, and then generates the
|
||||
* appropriate method definitions for the atoms in scope.
|
||||
*
|
||||
* @param name the name of the method
|
||||
* @param defn the definition of the method
|
||||
* @return a list of method definitions for `name`
|
||||
*/
|
||||
def matchSignaturesAndGenerate(
|
||||
name: IR.Name,
|
||||
defn: IR
|
||||
): List[IR.Module.Scope.Definition] = {
|
||||
var unusedSig: Option[IR.Type.Ascription] = None
|
||||
val sig = lastSignature match {
|
||||
case Some(IR.Type.Ascription(typed, _, _, _, _)) =>
|
||||
typed match {
|
||||
case IR.Name.Literal(nameStr, _, _, _) =>
|
||||
if (name.name == nameStr) {
|
||||
lastSignature
|
||||
} else {
|
||||
unusedSig = lastSignature
|
||||
None
|
||||
}
|
||||
case _ =>
|
||||
unusedSig = lastSignature
|
||||
None
|
||||
}
|
||||
case None => None
|
||||
}
|
||||
|
||||
lastSignature = None
|
||||
val unusedList: List[Definition] = unusedSig.toList
|
||||
unusedList ::: genMethodDef(
|
||||
defn,
|
||||
namesToDefineMethodsOn,
|
||||
sig
|
||||
)
|
||||
}
|
||||
|
||||
val methodDefs = methods.flatMap(genMethodDef(_, namesToDefineMethodsOn))
|
||||
val entityResults: List[Definition] = remainingEntities.flatMap {
|
||||
case sig: IR.Type.Ascription =>
|
||||
val res = lastSignature match {
|
||||
case Some(oldSig) => Some(oldSig)
|
||||
case None => None
|
||||
}
|
||||
|
||||
atomDefs ::: methodDefs
|
||||
lastSignature = Some(sig)
|
||||
res
|
||||
case binding @ IR.Expression.Binding(name, _, _, _, _) =>
|
||||
matchSignaturesAndGenerate(name, binding)
|
||||
case funSugar @ IR.Function.Binding(name, _, _, _, _, _, _) =>
|
||||
matchSignaturesAndGenerate(name, funSugar)
|
||||
case _ =>
|
||||
throw new CompilerError("Unexpected IR node in complex type body.")
|
||||
}
|
||||
val allEntities = entityResults ::: lastSignature.toList
|
||||
|
||||
atomDefs ::: allEntities
|
||||
}
|
||||
|
||||
/** Generates a method definition from a definition in complex type def body.
|
||||
*
|
||||
* The signature _must_ correctly match the method definition.
|
||||
*
|
||||
* @param ir the definition to generate a method from
|
||||
* @param names the names on which the method is being defined
|
||||
* @param signature the type signature for the method, if it exists
|
||||
* @return `ir` as a method
|
||||
*/
|
||||
def genMethodDef(
|
||||
ir: IR,
|
||||
names: List[IR.Name]
|
||||
): List[IR.Module.Scope.Definition.Method] = {
|
||||
names: List[IR.Name],
|
||||
signature: Option[IR.Type.Ascription]
|
||||
): List[IR.Module.Scope.Definition] = {
|
||||
ir match {
|
||||
case IR.Expression.Binding(name, expr, location, _, _) =>
|
||||
val realExpr = expr match {
|
||||
@ -139,17 +198,60 @@ case object ComplexType extends IRPass {
|
||||
case _ => expr
|
||||
}
|
||||
|
||||
names.map(typeName => {
|
||||
Method.Binding(typeName, name, List(), realExpr, location)
|
||||
})
|
||||
names.flatMap(
|
||||
genForName(_, name, List(), realExpr, location, signature)
|
||||
)
|
||||
case IR.Function.Binding(name, args, body, location, _, _, _) =>
|
||||
names.map(typeName => {
|
||||
Method.Binding(typeName, name, args, body, location)
|
||||
})
|
||||
names.flatMap(
|
||||
genForName(_, name, args, body, location, signature)
|
||||
)
|
||||
case _ =>
|
||||
throw new CompilerError(
|
||||
"Unexpected IR node during complex type desugaring."
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/** Generates a top-level method definition for the provided parameters.
|
||||
*
|
||||
* @param typeName the type name the method is being defined on
|
||||
* @param name the method being defined
|
||||
* @param args the definition arguments to the method
|
||||
* @param body the body of the method
|
||||
* @param location the source location of the method
|
||||
* @param signature the method's type signature, if it exists
|
||||
* @return a top-level method definition
|
||||
*/
|
||||
def genForName(
|
||||
typeName: IR.Name,
|
||||
name: IR.Name,
|
||||
args: List[IR.DefinitionArgument],
|
||||
body: IR.Expression,
|
||||
location: Option[IdentifiedLocation],
|
||||
signature: Option[IR.Type.Ascription]
|
||||
): List[IR.Module.Scope.Definition] = {
|
||||
val methodRef = IR.Name.MethodReference(
|
||||
List(typeName),
|
||||
name,
|
||||
MethodReference.genLocation(List(typeName, name))
|
||||
)
|
||||
|
||||
val newSig =
|
||||
signature.map(sig =>
|
||||
sig
|
||||
.copy(typed =
|
||||
methodRef.duplicate(keepMetadata = false, keepDiagnostics = false)
|
||||
)
|
||||
.duplicate(keepMetadata = false, keepDiagnostics = false)
|
||||
)
|
||||
|
||||
val binding = Method.Binding(
|
||||
methodRef.duplicate(keepMetadata = false, keepDiagnostics = false),
|
||||
args,
|
||||
body.duplicate(keepMetadata = false, keepDiagnostics = false),
|
||||
location
|
||||
)
|
||||
|
||||
newSig.toList :+ binding
|
||||
}
|
||||
}
|
||||
|
@ -6,7 +6,12 @@ import org.enso.compiler.core.IR.Module.Scope.Definition
|
||||
import org.enso.compiler.core.IR.Module.Scope.Definition.Method
|
||||
import org.enso.compiler.exception.CompilerError
|
||||
import org.enso.compiler.pass.IRPass
|
||||
import org.enso.compiler.pass.analyse.{AliasAnalysis, DataflowAnalysis, DemandAnalysis, TailCall}
|
||||
import org.enso.compiler.pass.analyse.{
|
||||
AliasAnalysis,
|
||||
DataflowAnalysis,
|
||||
DemandAnalysis,
|
||||
TailCall
|
||||
}
|
||||
import org.enso.compiler.pass.optimise.LambdaConsolidate
|
||||
import org.enso.compiler.pass.resolve.IgnoredBindings
|
||||
|
||||
@ -111,14 +116,14 @@ case object FunctionBinding extends IRPass {
|
||||
"Explicit method definitions should not exist during function " +
|
||||
"binding desugaring."
|
||||
)
|
||||
case Method.Binding(typeName, methName, args, body, loc, _, _) =>
|
||||
case Method.Binding(methRef, args, body, loc, _, _) =>
|
||||
val newBody = args
|
||||
.map(_.mapExpressions(desugarExpression))
|
||||
.foldRight(desugarExpression(body))((arg, body) =>
|
||||
IR.Function.Lambda(List(arg), body, None)
|
||||
)
|
||||
|
||||
Method.Explicit(typeName, methName, newBody, loc)
|
||||
Method.Explicit(methRef, newBody, loc)
|
||||
case _: IR.Module.Scope.Definition.Type =>
|
||||
throw new CompilerError(
|
||||
"Complex type definitions should not be present during " +
|
||||
@ -129,7 +134,8 @@ case object FunctionBinding extends IRPass {
|
||||
"Documentation should not be present during function binding" +
|
||||
"desugaring."
|
||||
)
|
||||
case e: IR.Error => e
|
||||
case a: IR.Type.Ascription => a
|
||||
case e: IR.Error => e
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -244,6 +244,8 @@ case object LambdaShorthandToLambda extends IRPass {
|
||||
)
|
||||
IR.Function.Lambda(List(defArg), body, locWithoutId)
|
||||
}
|
||||
case tSet @ IR.Application.Literal.Typeset(expr, _, _, _) =>
|
||||
tSet.copy(expression = expr.map(desugarExpression(_, freshNameSupply)))
|
||||
case _: IR.Application.Operator =>
|
||||
throw new CompilerError(
|
||||
"Operators should be desugared by the point of underscore " +
|
||||
|
@ -180,7 +180,10 @@ case object NestedPatternMatch extends IRPass {
|
||||
val scrutineeBinding =
|
||||
IR.Expression.Binding(scrutineeBindingName, scrutineeExpression, None)
|
||||
|
||||
val caseExprScrutinee = scrutineeBindingName.duplicate()
|
||||
val caseExprScrutinee = scrutineeBindingName.duplicate(
|
||||
keepDiagnostics = false,
|
||||
keepMetadata = false
|
||||
)
|
||||
|
||||
val processedBranches = branches.zipWithIndex.map {
|
||||
case (branch, ix) =>
|
||||
@ -234,9 +237,10 @@ case object NestedPatternMatch extends IRPass {
|
||||
val (lastNestedPattern, nestedPosition) =
|
||||
fields.zipWithIndex.findLast { case (pat, _) => isNested(pat) }.get
|
||||
|
||||
val newName = freshNameSupply.newName()
|
||||
val newField = Pattern.Name(newName, None)
|
||||
val nestedScrutinee = newName.duplicate()
|
||||
val newName = freshNameSupply.newName()
|
||||
val newField = Pattern.Name(newName, None)
|
||||
val nestedScrutinee =
|
||||
newName.duplicate(keepDiagnostics = false, keepMetadata = false)
|
||||
|
||||
val newFields =
|
||||
fields.take(nestedPosition) ++ (newField :: fields.drop(
|
||||
@ -244,7 +248,8 @@ case object NestedPatternMatch extends IRPass {
|
||||
))
|
||||
|
||||
val newPattern = cons.copy(
|
||||
fields = newFields.duplicate()
|
||||
fields =
|
||||
newFields.duplicate(keepDiagnostics = false, keepMetadata = false)
|
||||
)
|
||||
|
||||
val newExpression = generateNestedCase(
|
||||
@ -256,8 +261,10 @@ case object NestedPatternMatch extends IRPass {
|
||||
)
|
||||
|
||||
val partDesugaredBranch = IR.Case.Branch(
|
||||
pattern = newPattern.duplicate(),
|
||||
expression = newExpression.duplicate(),
|
||||
pattern = newPattern
|
||||
.duplicate(keepDiagnostics = false, keepMetadata = false),
|
||||
expression = newExpression
|
||||
.duplicate(keepDiagnostics = false, keepMetadata = false),
|
||||
None
|
||||
)
|
||||
|
||||
@ -314,13 +321,17 @@ case object NestedPatternMatch extends IRPass {
|
||||
remainingBranches: List[IR.Case.Branch]
|
||||
): IR.Expression = {
|
||||
val fallbackCase = IR.Case.Expr(
|
||||
topLevelScrutineeExpr.duplicate(),
|
||||
remainingBranches.duplicate(),
|
||||
topLevelScrutineeExpr.duplicate(keepDiagnostics = false),
|
||||
remainingBranches.duplicate(keepMetadata = false),
|
||||
None
|
||||
)
|
||||
|
||||
val patternBranch =
|
||||
IR.Case.Branch(pattern.duplicate(), currentBranchExpr.duplicate(), None)
|
||||
IR.Case.Branch(
|
||||
pattern.duplicate(keepDiagnostics = false),
|
||||
currentBranchExpr.duplicate(keepMetadata = false),
|
||||
None
|
||||
)
|
||||
val fallbackBranch = IR.Case.Branch(
|
||||
IR.Pattern.Name(IR.Name.Blank(None), None),
|
||||
fallbackCase,
|
||||
@ -328,7 +339,7 @@ case object NestedPatternMatch extends IRPass {
|
||||
)
|
||||
|
||||
IR.Case.Expr(
|
||||
nestedScrutinee.duplicate(),
|
||||
nestedScrutinee.duplicate(keepDiagnostics = false, keepMetadata = false),
|
||||
List(patternBranch, fallbackBranch),
|
||||
None
|
||||
)
|
||||
|
@ -5,12 +5,8 @@ import org.enso.compiler.core.IR
|
||||
import org.enso.compiler.core.IR.Application.Operator.Section
|
||||
import org.enso.compiler.exception.CompilerError
|
||||
import org.enso.compiler.pass.IRPass
|
||||
import org.enso.compiler.pass.analyse.{
|
||||
AliasAnalysis,
|
||||
DataflowAnalysis,
|
||||
DemandAnalysis,
|
||||
TailCall
|
||||
}
|
||||
import org.enso.compiler.pass.analyse._
|
||||
import org.enso.compiler.pass.lint.UnusedBindings
|
||||
|
||||
/** This pass converts operator sections to applications of binary operators.
|
||||
*
|
||||
@ -29,9 +25,11 @@ case object SectionsToBinOp extends IRPass {
|
||||
)
|
||||
override val invalidatedPasses: Seq[IRPass] = List(
|
||||
AliasAnalysis,
|
||||
CachePreferenceAnalysis,
|
||||
DataflowAnalysis,
|
||||
DemandAnalysis,
|
||||
TailCall
|
||||
TailCall,
|
||||
UnusedBindings
|
||||
)
|
||||
|
||||
/** Performs section to binary operator conversion on an IR module.
|
||||
@ -94,21 +92,95 @@ case object SectionsToBinOp extends IRPass {
|
||||
): IR.Expression = {
|
||||
section match {
|
||||
case Section.Left(arg, op, loc, passData, diagnostics) =>
|
||||
IR.Application.Prefix(
|
||||
op,
|
||||
List(arg),
|
||||
hasDefaultsSuspended = false,
|
||||
loc,
|
||||
passData,
|
||||
diagnostics
|
||||
val rightArgName = freshNameSupply.newName()
|
||||
val rightCallArg =
|
||||
IR.CallArgument.Specified(None, rightArgName, None, None)
|
||||
val rightDefArg = IR.DefinitionArgument.Specified(
|
||||
rightArgName.duplicate(
|
||||
keepLocations = false,
|
||||
keepDiagnostics = false,
|
||||
keepMetadata = false
|
||||
),
|
||||
None,
|
||||
suspended = false,
|
||||
None
|
||||
)
|
||||
|
||||
if (arg.value.isInstanceOf[IR.Name.Blank]) {
|
||||
val leftArgName = freshNameSupply.newName()
|
||||
val leftCallArg =
|
||||
IR.CallArgument.Specified(None, leftArgName, None, None)
|
||||
val leftDefArg = IR.DefinitionArgument.Specified(
|
||||
leftArgName.duplicate(
|
||||
keepLocations = false,
|
||||
keepDiagnostics = false,
|
||||
keepMetadata = false
|
||||
),
|
||||
None,
|
||||
suspended = false,
|
||||
None
|
||||
)
|
||||
val opCall = IR.Application.Prefix(
|
||||
op,
|
||||
List(leftCallArg, rightCallArg),
|
||||
hasDefaultsSuspended = false,
|
||||
loc,
|
||||
passData,
|
||||
diagnostics
|
||||
)
|
||||
|
||||
val rightLam = IR.Function.Lambda(
|
||||
List(rightDefArg),
|
||||
opCall,
|
||||
None
|
||||
)
|
||||
|
||||
IR.Function.Lambda(
|
||||
List(leftDefArg),
|
||||
rightLam,
|
||||
None
|
||||
)
|
||||
|
||||
} else {
|
||||
val opCall = IR.Application.Prefix(
|
||||
op,
|
||||
List(arg, rightCallArg),
|
||||
hasDefaultsSuspended = false,
|
||||
loc,
|
||||
passData,
|
||||
diagnostics
|
||||
)
|
||||
|
||||
IR.Function.Lambda(
|
||||
List(rightDefArg),
|
||||
opCall,
|
||||
None
|
||||
)
|
||||
}
|
||||
case Section.Sides(op, loc, passData, diagnostics) =>
|
||||
val leftArgName = freshNameSupply.newName()
|
||||
val leftCallArg =
|
||||
IR.CallArgument.Specified(None, leftArgName, None, None)
|
||||
val leftDefArg = IR.DefinitionArgument.Specified(
|
||||
// Ensure it has a different identifier
|
||||
leftArgName.copy(id = IR.randomId),
|
||||
leftArgName.duplicate(
|
||||
keepLocations = false,
|
||||
keepDiagnostics = false,
|
||||
keepMetadata = false
|
||||
),
|
||||
None,
|
||||
suspended = false,
|
||||
None
|
||||
)
|
||||
|
||||
val rightArgName = freshNameSupply.newName()
|
||||
val rightCallArg =
|
||||
IR.CallArgument.Specified(None, rightArgName, None, None)
|
||||
val rightDefArg = IR.DefinitionArgument.Specified(
|
||||
rightArgName.duplicate(
|
||||
keepLocations = false,
|
||||
keepDiagnostics = false,
|
||||
keepMetadata = false
|
||||
),
|
||||
None,
|
||||
suspended = false,
|
||||
None
|
||||
@ -116,23 +188,26 @@ case object SectionsToBinOp extends IRPass {
|
||||
|
||||
val opCall = IR.Application.Prefix(
|
||||
op,
|
||||
List(leftCallArg),
|
||||
List(leftCallArg, rightCallArg),
|
||||
hasDefaultsSuspended = false,
|
||||
loc,
|
||||
passData,
|
||||
diagnostics
|
||||
)
|
||||
|
||||
IR.Function.Lambda(
|
||||
List(leftDefArg),
|
||||
val rightLambda = IR.Function.Lambda(
|
||||
List(rightDefArg),
|
||||
opCall,
|
||||
loc,
|
||||
canBeTCO = true,
|
||||
passData,
|
||||
diagnostics
|
||||
None
|
||||
)
|
||||
|
||||
/* Note [Blanks in Right Sections]
|
||||
IR.Function.Lambda(
|
||||
List(leftDefArg),
|
||||
rightLambda,
|
||||
None
|
||||
)
|
||||
|
||||
/* Note [Blanks in Sections]
|
||||
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
* While the naiive compositional translation of `(- _)` first translates
|
||||
* the section into a function applying `-` to two arguments, one of which
|
||||
@ -147,6 +222,8 @@ case object SectionsToBinOp extends IRPass {
|
||||
* `(- _)` == `x -> (- x)` == `x -> y -> y - x`
|
||||
*
|
||||
* We implement this special case here.
|
||||
*
|
||||
* The same is true of left sections.
|
||||
*/
|
||||
|
||||
case Section.Right(op, arg, loc, passData, diagnostics) =>
|
||||
@ -155,20 +232,27 @@ case object SectionsToBinOp extends IRPass {
|
||||
IR.CallArgument.Specified(None, leftArgName, None, None)
|
||||
val leftDefArg =
|
||||
IR.DefinitionArgument.Specified(
|
||||
// Ensure it has a different identifier
|
||||
leftArgName.copy(id = IR.randomId),
|
||||
leftArgName.duplicate(
|
||||
keepLocations = false,
|
||||
keepMetadata = false,
|
||||
keepDiagnostics = false
|
||||
),
|
||||
None,
|
||||
suspended = false,
|
||||
None
|
||||
)
|
||||
|
||||
if (arg.value.isInstanceOf[IR.Name.Blank]) {
|
||||
// Note [Blanks in Right Sections]
|
||||
// Note [Blanks in Sections]
|
||||
val rightArgName = freshNameSupply.newName()
|
||||
val rightCallArg =
|
||||
IR.CallArgument.Specified(None, rightArgName, None, None)
|
||||
val rightDefArg = IR.DefinitionArgument.Specified(
|
||||
rightArgName.copy(id = IR.randomId),
|
||||
rightArgName.duplicate(
|
||||
keepLocations = false,
|
||||
keepMetadata = false,
|
||||
keepDiagnostics = false
|
||||
),
|
||||
None,
|
||||
suspended = false,
|
||||
None
|
||||
@ -192,10 +276,7 @@ case object SectionsToBinOp extends IRPass {
|
||||
IR.Function.Lambda(
|
||||
List(rightDefArg),
|
||||
leftLam,
|
||||
loc,
|
||||
canBeTCO = true,
|
||||
passData,
|
||||
diagnostics
|
||||
None
|
||||
)
|
||||
} else {
|
||||
val opCall = IR.Application.Prefix(
|
||||
@ -210,10 +291,7 @@ case object SectionsToBinOp extends IRPass {
|
||||
IR.Function.Lambda(
|
||||
List(leftDefArg),
|
||||
opCall,
|
||||
loc,
|
||||
canBeTCO = true,
|
||||
passData,
|
||||
diagnostics
|
||||
None
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -168,8 +168,7 @@ case object LambdaConsolidate extends IRPass {
|
||||
arguments = processedArgList,
|
||||
body = runExpression(newBody, inlineContext),
|
||||
location = newLocation,
|
||||
canBeTCO = chainedLambdas.last.canBeTCO,
|
||||
passData = MetadataStorage()
|
||||
canBeTCO = chainedLambdas.last.canBeTCO
|
||||
)
|
||||
case _: IR.Function.Binding =>
|
||||
throw new CompilerError(
|
||||
@ -307,9 +306,10 @@ case object LambdaConsolidate extends IRPass {
|
||||
case defSpec: IR.DefinitionArgument.Specified => defSpec.name.name
|
||||
}
|
||||
)
|
||||
case ths: IR.Name.This => ths
|
||||
case here: IR.Name.Here => here
|
||||
case blank: IR.Name.Blank => blank
|
||||
case ths: IR.Name.This => ths
|
||||
case here: IR.Name.Here => here
|
||||
case blank: IR.Name.Blank => blank
|
||||
case ref: IR.Name.MethodReference => ref
|
||||
}
|
||||
} else {
|
||||
name
|
||||
|
@ -105,6 +105,7 @@ case object DocumentationComments extends IRPass {
|
||||
tpe.copy(body = resolveList(tpe.body).map(resolveIr))
|
||||
case d: IR.Module.Scope.Definition.Atom => d
|
||||
case doc: IR.Comment.Documentation => doc
|
||||
case tySig: IR.Type.Ascription => tySig
|
||||
case err: IR.Error => err
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,265 @@
|
||||
package org.enso.compiler.pass.resolve
|
||||
|
||||
import org.enso.compiler.context.{InlineContext, ModuleContext}
|
||||
import org.enso.compiler.core.IR
|
||||
import org.enso.compiler.core.IR.Module.Scope.Definition
|
||||
import org.enso.compiler.core.IR.Module.Scope.Definition.Method
|
||||
import org.enso.compiler.core.IR.Type
|
||||
import org.enso.compiler.exception.CompilerError
|
||||
import org.enso.compiler.pass.IRPass
|
||||
import org.enso.compiler.pass.analyse._
|
||||
import org.enso.compiler.pass.desugar.ComplexType
|
||||
import org.enso.compiler.pass.lint.UnusedBindings
|
||||
import org.enso.compiler.pass.optimise.LambdaConsolidate
|
||||
import org.enso.compiler.pass.resolve.TypeSignatures.Signature
|
||||
|
||||
import scala.annotation.{nowarn, unused}
|
||||
|
||||
/** This pass is responsible for analysing type signatures to determine which
|
||||
* arguments in a function definition are suspended.
|
||||
*
|
||||
* It searches for a correspondence between an argument position and the same
|
||||
* position in the associated type signature, marking the argument as suspended
|
||||
* if its type contains the _top-level_ constructor `Suspended`.
|
||||
*
|
||||
* It is a _best effort_ attempt for now, nothing more:
|
||||
*
|
||||
* - It only works on the syntactic structure of the signature.
|
||||
* - It can only deal with a correspondence between a consolidated lambda and
|
||||
* the signature, not with extended signatures that cover returned functions
|
||||
* and the like.
|
||||
*
|
||||
* Additionally, we currently only support looking for `Suspended` alone, as
|
||||
* supporting expressions like `Suspended a` will require the pattern contexts
|
||||
* work.
|
||||
*
|
||||
* While the `~` syntax for suspension is still supported, the signature will
|
||||
* take precedence over the `~` marking.
|
||||
*
|
||||
* This pass requires the context to provide:
|
||||
*
|
||||
* - Nothing
|
||||
*/
|
||||
case object SuspendedArguments extends IRPass {
|
||||
override type Metadata = IRPass.Metadata.Empty
|
||||
override type Config = IRPass.Configuration.Default
|
||||
|
||||
override val precursorPasses: Seq[IRPass] = List(
|
||||
ComplexType,
|
||||
TypeSignatures,
|
||||
LambdaConsolidate
|
||||
)
|
||||
override val invalidatedPasses: Seq[IRPass] = List(
|
||||
AliasAnalysis,
|
||||
CachePreferenceAnalysis,
|
||||
DataflowAnalysis,
|
||||
DataflowAnalysis,
|
||||
DemandAnalysis,
|
||||
TailCall,
|
||||
UnusedBindings
|
||||
)
|
||||
|
||||
/** Resolves suspended arguments in a module.
|
||||
*
|
||||
* @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,
|
||||
@unused moduleContext: ModuleContext
|
||||
): IR.Module = ir.copy(
|
||||
bindings = ir.bindings.map(resolveModuleBinding)
|
||||
)
|
||||
|
||||
/** Resolves suspended arguments in an arbitrary expression.
|
||||
*
|
||||
* @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,
|
||||
@unused inlineContext: InlineContext
|
||||
): IR.Expression = resolveExpression(ir)
|
||||
|
||||
// === Pass Internals =======================================================
|
||||
|
||||
/** Resolves suspended arguments for a module binding.
|
||||
*
|
||||
* It is expected that module-level type signatures _do not_ include the
|
||||
* `this` argument.
|
||||
*
|
||||
* @param binding the top-level binding to resolve suspensions in
|
||||
* @return `binding`, with any suspended arguments resolved
|
||||
*/
|
||||
def resolveModuleBinding(
|
||||
binding: IR.Module.Scope.Definition
|
||||
): IR.Module.Scope.Definition = {
|
||||
binding match {
|
||||
case explicit @ Method.Explicit(_, body, _, _, _) =>
|
||||
body match {
|
||||
case lam @ IR.Function.Lambda(args, lamBody, _, _, _, _) =>
|
||||
explicit.getMetadata(TypeSignatures) match {
|
||||
case Some(Signature(signature)) =>
|
||||
val newArgs = computeSuspensions(
|
||||
args.drop(1),
|
||||
signature
|
||||
)
|
||||
|
||||
explicit.copy(body =
|
||||
lam.copy(
|
||||
arguments = args.head :: newArgs,
|
||||
body = resolveExpression(lamBody)
|
||||
)
|
||||
)
|
||||
case None =>
|
||||
explicit.copy(
|
||||
body = lam.copy(body = resolveExpression(lamBody))
|
||||
)
|
||||
}
|
||||
case _ =>
|
||||
throw new CompilerError(
|
||||
"Method bodies must be lambdas at this point."
|
||||
)
|
||||
}
|
||||
case _: Method.Binding => throw new CompilerError("")
|
||||
case atom: Definition.Atom => atom
|
||||
case err: IR.Error => err
|
||||
case _: Definition.Type =>
|
||||
throw new CompilerError(
|
||||
"Complex type definitions should not be present."
|
||||
)
|
||||
case _: Type.Ascription =>
|
||||
throw new CompilerError("Type ascriptions should not be present.")
|
||||
case _: IR.Comment =>
|
||||
throw new CompilerError("Comments should not be present.")
|
||||
}
|
||||
}
|
||||
|
||||
/** Resolves suspended arguments in an arbitrary expression.
|
||||
*
|
||||
* @param expression the expression to perform resolution in
|
||||
* @return `expression`, with any suspended arguments resolved
|
||||
*/
|
||||
def resolveExpression(expression: IR.Expression): IR.Expression = {
|
||||
expression.transformExpressions {
|
||||
case bind @ IR.Expression.Binding(_, expr, _, _, _) =>
|
||||
val newExpr = bind.getMetadata(TypeSignatures) match {
|
||||
case Some(Signature(signature)) =>
|
||||
expr match {
|
||||
case lam @ IR.Function.Lambda(args, body, _, _, _, _) =>
|
||||
lam.copy(
|
||||
arguments = computeSuspensions(args, signature),
|
||||
body = resolveExpression(body)
|
||||
)
|
||||
case _ => expr
|
||||
}
|
||||
case None => expr
|
||||
}
|
||||
|
||||
bind.copy(expression = newExpr)
|
||||
case lam @ IR.Function.Lambda(args, body, _, _, _, _) =>
|
||||
lam.getMetadata(TypeSignatures) match {
|
||||
case Some(Signature(signature)) =>
|
||||
lam.copy(
|
||||
arguments = computeSuspensions(args, signature),
|
||||
body = resolveExpression(body)
|
||||
)
|
||||
case None => lam.copy(body = resolveExpression(body))
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/** Converts a type signature into segments.
|
||||
*
|
||||
* Segments are defined as the portions between the top-level lambdas in the
|
||||
* type signature.
|
||||
*
|
||||
* @param signature the type signature to split
|
||||
* @return the segments of `signature`
|
||||
*/
|
||||
def toSegments(signature: IR.Expression): List[IR.Expression] = {
|
||||
signature match {
|
||||
case IR.Application.Operator.Binary(
|
||||
l,
|
||||
IR.Name.Literal("->", _, _, _),
|
||||
r,
|
||||
_,
|
||||
_,
|
||||
_
|
||||
) =>
|
||||
l.value :: toSegments(r.value)
|
||||
case IR.Function.Lambda(args, body, _, _, _, _) =>
|
||||
args.map(_.name) ::: toSegments(body)
|
||||
case _ => List(signature)
|
||||
}
|
||||
}
|
||||
|
||||
/** Checks if a value represents a suspended argument.
|
||||
*
|
||||
* @param value the value to check
|
||||
* @return `true` if `value` represents a suspended argument, otherwise
|
||||
* `false`
|
||||
*/
|
||||
def representsSuspended(value: IR.Expression): Boolean = {
|
||||
value match {
|
||||
case IR.Name.Literal("Suspended", _, _, _) => true
|
||||
case _ => false
|
||||
}
|
||||
}
|
||||
|
||||
/** Marks an argument as suspended if it corresponds to a `Suspended` portion
|
||||
* of the type signature.
|
||||
*
|
||||
* @param pair an argument and its corresponding type signature segment
|
||||
* @return the argument from `pair`, with its suspension marked appropriately
|
||||
*/
|
||||
def markSuspended(
|
||||
pair: (IR.DefinitionArgument, IR.Expression)
|
||||
): IR.DefinitionArgument = pair match {
|
||||
case (arg, typ) =>
|
||||
arg match {
|
||||
case spec: IR.DefinitionArgument.Specified =>
|
||||
if (representsSuspended(typ)) {
|
||||
spec.copy(suspended = true)
|
||||
} else spec.copy(suspended = false)
|
||||
}
|
||||
}
|
||||
|
||||
/** Computes the suspensions for the arguments list of a function.
|
||||
*
|
||||
* @param args the function arguments
|
||||
* @param signature the signature of the function
|
||||
* @return `args`, appropriately marked as suspended or not
|
||||
*/
|
||||
def computeSuspensions(
|
||||
args: List[IR.DefinitionArgument],
|
||||
signature: IR.Expression
|
||||
): List[IR.DefinitionArgument] = {
|
||||
val signatureSegments = toSegments(signature)
|
||||
|
||||
val toComputeArgs =
|
||||
if (args.length == signatureSegments.length) {
|
||||
args.zip(signatureSegments)
|
||||
} else if (args.length > signatureSegments.length) {
|
||||
val additionalSegments = signatureSegments ::: List.fill(
|
||||
signatureSegments.length - args.length
|
||||
)(IR.Empty(None))
|
||||
|
||||
args.zip(additionalSegments)
|
||||
} else {
|
||||
args.zip(
|
||||
signatureSegments
|
||||
.dropRight(signatureSegments.length - args.length)
|
||||
)
|
||||
}
|
||||
|
||||
toComputeArgs.map(markSuspended)
|
||||
}
|
||||
}
|
@ -0,0 +1,207 @@
|
||||
package org.enso.compiler.pass.resolve
|
||||
|
||||
import org.enso.compiler.context.{InlineContext, ModuleContext}
|
||||
import org.enso.compiler.core.IR
|
||||
import org.enso.compiler.core.IR.Application
|
||||
import org.enso.compiler.exception.CompilerError
|
||||
import org.enso.compiler.pass.IRPass
|
||||
import org.enso.compiler.pass.analyse._
|
||||
import org.enso.compiler.pass.desugar.{
|
||||
LambdaShorthandToLambda,
|
||||
OperatorToFunction,
|
||||
SectionsToBinOp
|
||||
}
|
||||
import org.enso.compiler.pass.lint.UnusedBindings
|
||||
|
||||
import scala.annotation.unused
|
||||
|
||||
/** This pass is responsible for lifting applications of type functions such as
|
||||
* `:` and `in` and `!` into their specific IR nodes.
|
||||
*
|
||||
* This pass requires the context to provide:
|
||||
*
|
||||
* - Nothing
|
||||
*/
|
||||
case object TypeFunctions extends IRPass {
|
||||
override type Metadata = IRPass.Metadata.Empty
|
||||
override type Config = IRPass.Configuration.Default
|
||||
|
||||
override val precursorPasses: Seq[IRPass] = List(
|
||||
IgnoredBindings,
|
||||
LambdaShorthandToLambda,
|
||||
OperatorToFunction,
|
||||
SectionsToBinOp
|
||||
)
|
||||
|
||||
override val invalidatedPasses: Seq[IRPass] = List(
|
||||
AliasAnalysis,
|
||||
CachePreferenceAnalysis,
|
||||
DataflowAnalysis,
|
||||
DemandAnalysis,
|
||||
TailCall,
|
||||
UnusedBindings
|
||||
)
|
||||
|
||||
/** Performs typing function resolution on a module.
|
||||
*
|
||||
* @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,
|
||||
@unused moduleContext: ModuleContext
|
||||
): IR.Module = ir.transformExpressions {
|
||||
case a => resolveExpression(a)
|
||||
}
|
||||
|
||||
/** Performs typing function resolution on an expression.
|
||||
*
|
||||
* @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,
|
||||
@unused inlineContext: InlineContext
|
||||
): IR.Expression = ir.transformExpressions {
|
||||
case a => resolveExpression(a)
|
||||
}
|
||||
|
||||
// === Pass Internals =======================================================
|
||||
|
||||
/** The names of the known typing functions. */
|
||||
val knownTypingFunctions: Set[String] = Set(
|
||||
IR.Type.Ascription.name,
|
||||
IR.Type.Context.name,
|
||||
IR.Type.Error.name,
|
||||
IR.Type.Set.Concat.name,
|
||||
IR.Type.Set.Subsumption.name,
|
||||
IR.Type.Set.Equality.name,
|
||||
IR.Type.Set.Union.name,
|
||||
IR.Type.Set.Intersection.name,
|
||||
IR.Type.Set.Subtraction.name
|
||||
)
|
||||
|
||||
/** Performs resolution of typing functions in an arbitrary expression.
|
||||
*
|
||||
* @param expr the expression to perform resolution in
|
||||
* @return `expr`, with any typing functions resolved
|
||||
*/
|
||||
def resolveExpression(expr: IR.Expression): IR.Expression = {
|
||||
expr.transformExpressions {
|
||||
case app: IR.Application => resolveApplication(app)
|
||||
}
|
||||
}
|
||||
|
||||
/** Performs resolution of typing functions in an application.
|
||||
*
|
||||
* @param app the application to perform resolution in
|
||||
* @return `app`, with any typing functions resolved
|
||||
*/
|
||||
def resolveApplication(app: IR.Application): IR.Expression = {
|
||||
app match {
|
||||
case pre @ Application.Prefix(fn, arguments, _, _, _, _) =>
|
||||
fn match {
|
||||
case name: IR.Name if knownTypingFunctions.contains(name.name) =>
|
||||
resolveKnownFunction(pre)
|
||||
case _ =>
|
||||
pre.copy(
|
||||
function = resolveExpression(fn),
|
||||
arguments = arguments.map(resolveCallArgument)
|
||||
)
|
||||
}
|
||||
case force @ Application.Force(target, _, _, _) =>
|
||||
force.copy(target = resolveExpression(target))
|
||||
case seq @ Application.Literal.Sequence(items, _, _, _) =>
|
||||
seq.copy(
|
||||
items = items.map(resolveExpression)
|
||||
)
|
||||
case tSet @ Application.Literal.Typeset(expr, _, _, _) =>
|
||||
tSet.copy(
|
||||
expression = expr.map(resolveExpression)
|
||||
)
|
||||
case _: Application.Operator =>
|
||||
throw new CompilerError(
|
||||
"Operators should not be present during typing functions lifting."
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/** Resolves a known typing function to its IR node.
|
||||
*
|
||||
* @param prefix the application to resolve
|
||||
* @return the IR node representing `prefix`
|
||||
*/
|
||||
def resolveKnownFunction(prefix: IR.Application.Prefix): IR.Expression = {
|
||||
val expectedNumArgs = 2
|
||||
val lengthIsValid = prefix.arguments.length == expectedNumArgs
|
||||
val argsAreValid = prefix.arguments.forall(isValidCallArg)
|
||||
|
||||
if (lengthIsValid && argsAreValid) {
|
||||
val leftArg = resolveExpression(prefix.arguments.head.value)
|
||||
val rightArg = resolveExpression(prefix.arguments.last.value)
|
||||
|
||||
prefix.function.asInstanceOf[IR.Name].name match {
|
||||
case IR.Type.Ascription.name =>
|
||||
IR.Type.Ascription(leftArg, rightArg, prefix.location)
|
||||
case IR.Type.Context.name =>
|
||||
IR.Type.Context(leftArg, rightArg, prefix.location)
|
||||
case IR.Type.Error.name =>
|
||||
IR.Type.Error(leftArg, rightArg, prefix.location)
|
||||
case IR.Type.Set.Concat.name =>
|
||||
IR.Type.Set.Concat(leftArg, rightArg, prefix.location)
|
||||
case IR.Type.Set.Subsumption.name =>
|
||||
IR.Type.Set.Subsumption(leftArg, rightArg, prefix.location)
|
||||
case IR.Type.Set.Equality.name =>
|
||||
IR.Type.Set.Equality(leftArg, rightArg, prefix.location)
|
||||
case IR.Type.Set.Union.name =>
|
||||
IR.Type.Set.Union(leftArg, rightArg, prefix.location)
|
||||
case IR.Type.Set.Intersection.name =>
|
||||
IR.Type.Set.Intersection(leftArg, rightArg, prefix.location)
|
||||
case IR.Type.Set.Subtraction.name =>
|
||||
IR.Type.Set.Subtraction(leftArg, rightArg, prefix.location)
|
||||
}
|
||||
} else {
|
||||
IR.Error.InvalidIR(prefix)
|
||||
}
|
||||
}
|
||||
|
||||
/** Performs resolution of typing functions in a call argument.
|
||||
*
|
||||
* @param arg the argument to perform resolution in
|
||||
* @return `arg`, with any call arguments resolved
|
||||
*/
|
||||
def resolveCallArgument(arg: IR.CallArgument): IR.CallArgument = {
|
||||
arg match {
|
||||
case spec @ IR.CallArgument.Specified(_, value, _, _, _, _) =>
|
||||
spec.copy(
|
||||
value = resolveExpression(value)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// === Utilities ============================================================
|
||||
|
||||
/** Checks if a call argument is valid for a typing expression.
|
||||
*
|
||||
* As all typing functions are _operators_ in the source, their arguments
|
||||
* must:
|
||||
*
|
||||
* - Not have a name defined.
|
||||
* - Have no suspension info or not be suspended
|
||||
*
|
||||
* @param arg the argument to check
|
||||
* @return `true` if `arg` is valid, otherwise `false`
|
||||
*/
|
||||
def isValidCallArg(arg: IR.CallArgument): Boolean = {
|
||||
arg match {
|
||||
case IR.CallArgument.Specified(name, _, _, susp, _, _) =>
|
||||
name.isEmpty && (susp.isEmpty || susp.get)
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,209 @@
|
||||
package org.enso.compiler.pass.resolve
|
||||
|
||||
import org.enso.compiler.context.{InlineContext, ModuleContext}
|
||||
import org.enso.compiler.core.IR
|
||||
import org.enso.compiler.core.ir.MetadataStorage._
|
||||
import org.enso.compiler.exception.CompilerError
|
||||
import org.enso.compiler.pass.IRPass
|
||||
import org.enso.compiler.pass.analyse._
|
||||
import org.enso.compiler.pass.lint.UnusedBindings
|
||||
|
||||
import scala.annotation.unused
|
||||
|
||||
/** This pass is responsible for resolving type signatures and associating
|
||||
* them as metadata with the typed object.
|
||||
*
|
||||
* Please note that this pass currently does not support typed patterns (and
|
||||
* hence doesn't support inline types in lambdas). This support will come later
|
||||
* with the work on expanding pattern contexts.
|
||||
*
|
||||
* This pass requires the context to provide:
|
||||
*
|
||||
* - Nothing
|
||||
*/
|
||||
case object TypeSignatures extends IRPass {
|
||||
override type Metadata = Signature
|
||||
override type Config = IRPass.Configuration.Default
|
||||
|
||||
override val precursorPasses: Seq[IRPass] = List(
|
||||
TypeFunctions
|
||||
)
|
||||
override val invalidatedPasses: Seq[IRPass] = List(
|
||||
AliasAnalysis,
|
||||
CachePreferenceAnalysis,
|
||||
DataflowAnalysis,
|
||||
DemandAnalysis,
|
||||
TailCall,
|
||||
UnusedBindings
|
||||
)
|
||||
|
||||
/** Resolves type signatures in a module.
|
||||
*
|
||||
* @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,
|
||||
@unused moduleContext: ModuleContext
|
||||
): IR.Module = resolveModule(ir)
|
||||
|
||||
/** Resolves type signatures in an expression.
|
||||
*
|
||||
* @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,
|
||||
@unused inlineContext: InlineContext
|
||||
): IR.Expression = resolveExpression(ir)
|
||||
|
||||
// === Pass Internals =======================================================
|
||||
|
||||
/** Resolves type signatures in a module.
|
||||
*
|
||||
* @param mod the module to resolve signatures in
|
||||
* @return `mod`, with type signatures resolved
|
||||
*/
|
||||
def resolveModule(mod: IR.Module): IR.Module = {
|
||||
var lastSignature: Option[IR.Type.Ascription] = None
|
||||
|
||||
val newBindings: List[IR.Module.Scope.Definition] = mod.bindings.flatMap {
|
||||
case sig: IR.Type.Ascription =>
|
||||
val res = lastSignature match {
|
||||
case Some(oldSig) => Some(IR.Error.Unexpected.TypeSignature(oldSig))
|
||||
case None => None
|
||||
}
|
||||
|
||||
lastSignature = Some(sig)
|
||||
res
|
||||
case meth: IR.Module.Scope.Definition.Method =>
|
||||
val newMethod = meth.mapExpressions(resolveExpression)
|
||||
val res = lastSignature match {
|
||||
case Some(asc @ IR.Type.Ascription(typed, sig, _, _, _)) =>
|
||||
val methodRef = meth.methodReference
|
||||
|
||||
typed match {
|
||||
case ref: IR.Name.MethodReference =>
|
||||
if (ref isSameReferenceAs methodRef) {
|
||||
Some(newMethod.updateMetadata(this -->> Signature(sig)))
|
||||
} else {
|
||||
List(IR.Error.Unexpected.TypeSignature(asc), newMethod)
|
||||
}
|
||||
case _ =>
|
||||
List(IR.Error.Unexpected.TypeSignature(asc), newMethod)
|
||||
}
|
||||
case None => Some(newMethod)
|
||||
}
|
||||
|
||||
lastSignature = None
|
||||
res
|
||||
case atom: IR.Module.Scope.Definition.Atom =>
|
||||
Some(atom.mapExpressions(resolveExpression))
|
||||
case err: IR.Error => Some(err)
|
||||
case _: IR.Module.Scope.Definition.Type =>
|
||||
throw new CompilerError(
|
||||
"Complex type definitions should not be present during type " +
|
||||
"signature resolution."
|
||||
)
|
||||
case _: IR.Comment.Documentation =>
|
||||
throw new CompilerError(
|
||||
"Documentation comments should not be present during type " +
|
||||
"signature resolution."
|
||||
)
|
||||
} ::: lastSignature
|
||||
.map(asc => IR.Error.Unexpected.TypeSignature(asc))
|
||||
.toList
|
||||
|
||||
mod.copy(
|
||||
bindings = newBindings
|
||||
)
|
||||
}
|
||||
|
||||
/** Resolves type signatures in an arbitrary expression.
|
||||
*
|
||||
* @param expr the expression to resolve signatures in
|
||||
* @return `expr`, with any type signatures resolved
|
||||
*/
|
||||
def resolveExpression(expr: IR.Expression): IR.Expression = {
|
||||
expr.transformExpressions {
|
||||
case block: IR.Expression.Block => resolveBlock(block)
|
||||
case sig: IR.Type.Ascription => resolveAscription(sig)
|
||||
}
|
||||
}
|
||||
|
||||
/** Resolves type signatures in an ascription.
|
||||
*
|
||||
* @param sig the signature to convert
|
||||
* @return the typed expression in `sig`, with `signature` attached
|
||||
*/
|
||||
def resolveAscription(sig: IR.Type.Ascription): IR.Expression = {
|
||||
val newTyped = sig.typed.mapExpressions(resolveExpression)
|
||||
val newSig = sig.signature.mapExpressions(resolveExpression)
|
||||
newTyped.updateMetadata(this -->> Signature(newSig))
|
||||
}
|
||||
|
||||
/** Resolves type signatures in a block.
|
||||
*
|
||||
* @param block the block to resolve signatures in
|
||||
* @return `block`, with any type signatures resolved
|
||||
*/
|
||||
def resolveBlock(block: IR.Expression.Block): IR.Expression.Block = {
|
||||
var lastSignature: Option[IR.Type.Ascription] = None
|
||||
val allBlockExpressions =
|
||||
block.expressions :+ block.returnValue
|
||||
|
||||
val newExpressions = allBlockExpressions.flatMap {
|
||||
case sig: IR.Type.Ascription =>
|
||||
val res = lastSignature match {
|
||||
case Some(oldSig) => Some(IR.Error.Unexpected.TypeSignature(oldSig))
|
||||
case None => None
|
||||
}
|
||||
|
||||
lastSignature = Some(sig)
|
||||
res
|
||||
case binding: IR.Expression.Binding =>
|
||||
val newBinding = binding.mapExpressions(resolveExpression)
|
||||
val res = lastSignature match {
|
||||
case Some(asc @ IR.Type.Ascription(typed, sig, _, _, _)) =>
|
||||
val name = binding.name
|
||||
|
||||
typed match {
|
||||
case typedName: IR.Name =>
|
||||
if (typedName.name == name.name) {
|
||||
Some(newBinding.updateMetadata(this -->> Signature(sig)))
|
||||
} else {
|
||||
List(IR.Error.Unexpected.TypeSignature(asc), newBinding)
|
||||
}
|
||||
case _ =>
|
||||
List(IR.Error.Unexpected.TypeSignature(asc), newBinding)
|
||||
}
|
||||
case None => Some(newBinding)
|
||||
}
|
||||
|
||||
lastSignature = None
|
||||
res
|
||||
case a => Some(resolveExpression(a))
|
||||
} ::: lastSignature.map(IR.Error.Unexpected.TypeSignature(_)).toList
|
||||
|
||||
block.copy(
|
||||
expressions = newExpressions.init,
|
||||
returnValue = newExpressions.last
|
||||
)
|
||||
}
|
||||
|
||||
// === Metadata =============================================================
|
||||
|
||||
/** A representation of a type signature.
|
||||
*
|
||||
* @param signature the expression for the type signature
|
||||
*/
|
||||
case class Signature(signature: IR.Expression) extends IRPass.Metadata {
|
||||
override val metadataName: String = "TypeSignatures.Signature"
|
||||
}
|
||||
}
|
@ -24,7 +24,7 @@ trait CompilerRunner {
|
||||
*
|
||||
* @return [[source]] as an AST
|
||||
*/
|
||||
def toAST: AST = {
|
||||
def toAst: AST = {
|
||||
val parser: Parser = Parser()
|
||||
val unresolvedAST = parser.runWithIds(source)
|
||||
|
||||
@ -44,7 +44,7 @@ trait CompilerRunner {
|
||||
* @return the [[IR]] representing [[source]]
|
||||
*/
|
||||
def toIrModule: IR.Module = {
|
||||
AstToIr.translate(source.toAST)
|
||||
AstToIr.translate(source.toAst)
|
||||
}
|
||||
}
|
||||
|
||||
@ -60,7 +60,7 @@ trait CompilerRunner {
|
||||
* @return the [[IR]] representing [[source]], if it is a valid expression
|
||||
*/
|
||||
def toIrExpression: Option[IR.Expression] = {
|
||||
AstToIr.translateInline(source.toAST)
|
||||
AstToIr.translateInline(source.toAst)
|
||||
}
|
||||
}
|
||||
|
||||
@ -163,8 +163,11 @@ trait CompilerRunner {
|
||||
*/
|
||||
def asMethod: IR.Module.Scope.Definition.Method = {
|
||||
IR.Module.Scope.Definition.Method.Explicit(
|
||||
IR.Name.Literal("TestType", None),
|
||||
IR.Name.Literal("testMethod", None),
|
||||
IR.Name.MethodReference(
|
||||
List(IR.Name.Literal("TestType", None)),
|
||||
IR.Name.Literal("testMethod", None),
|
||||
None
|
||||
),
|
||||
ir,
|
||||
None
|
||||
)
|
||||
|
@ -8,7 +8,13 @@ import org.enso.compiler.pass.analyse.AliasAnalysis
|
||||
import org.enso.compiler.pass.desugar._
|
||||
import org.enso.compiler.pass.lint.ShadowedPatternFields
|
||||
import org.enso.compiler.pass.optimise.UnreachableMatchBranches
|
||||
import org.enso.compiler.pass.resolve.{DocumentationComments, IgnoredBindings}
|
||||
import org.enso.compiler.pass.resolve.{
|
||||
DocumentationComments,
|
||||
IgnoredBindings,
|
||||
SuspendedArguments,
|
||||
TypeFunctions,
|
||||
TypeSignatures
|
||||
}
|
||||
|
||||
class PassesTest extends CompilerTest {
|
||||
|
||||
@ -50,7 +56,9 @@ class PassesTest extends CompilerTest {
|
||||
ShadowedPatternFields,
|
||||
UnreachableMatchBranches,
|
||||
NestedPatternMatch,
|
||||
IgnoredBindings
|
||||
IgnoredBindings,
|
||||
TypeFunctions,
|
||||
TypeSignatures
|
||||
)
|
||||
)
|
||||
}
|
||||
|
@ -1,7 +1,9 @@
|
||||
package org.enso.compiler.test.codegen
|
||||
|
||||
import org.enso.compiler.core.IR
|
||||
import org.enso.compiler.core.IR.Error.Syntax
|
||||
import org.enso.compiler.test.CompilerTest
|
||||
import org.enso.syntax.text.Debug
|
||||
|
||||
import scala.annotation.unused
|
||||
|
||||
@ -513,7 +515,7 @@ class AstToIrTest extends CompilerTest {
|
||||
}
|
||||
}
|
||||
|
||||
"Documentation comments" should {
|
||||
"AST translation for documentation comments" should {
|
||||
"work at the top level" in {
|
||||
val ir =
|
||||
"""
|
||||
@ -545,4 +547,240 @@ class AstToIrTest extends CompilerTest {
|
||||
.doc shouldEqual " Some docs for b"
|
||||
}
|
||||
}
|
||||
|
||||
"AST translation for type operators" should {
|
||||
"support type ascription" in {
|
||||
val ir =
|
||||
"""
|
||||
|a : Type
|
||||
|""".stripMargin.toIrExpression.get
|
||||
|
||||
ir shouldBe an[IR.Application.Operator.Binary]
|
||||
}
|
||||
|
||||
"support context ascription" in {
|
||||
val ir =
|
||||
"""
|
||||
|a in IO
|
||||
|""".stripMargin.toIrExpression.get
|
||||
|
||||
ir shouldBe an[IR.Application.Operator.Binary]
|
||||
}
|
||||
|
||||
"support error ascription" in {
|
||||
val ir =
|
||||
"""
|
||||
|IO ! FileNotFound
|
||||
|""".stripMargin.toIrExpression.get
|
||||
|
||||
ir shouldBe an[IR.Application.Operator.Binary]
|
||||
}
|
||||
|
||||
"support the subsumption operator" in {
|
||||
val ir =
|
||||
"""
|
||||
|IO.Read <: IO
|
||||
|""".stripMargin.toIrExpression.get
|
||||
|
||||
ir shouldBe an[IR.Application.Operator.Binary]
|
||||
}
|
||||
|
||||
"support the equality operator" in {
|
||||
val ir =
|
||||
"""
|
||||
|T ~ Q
|
||||
|""".stripMargin.toIrExpression.get
|
||||
|
||||
ir shouldBe an[IR.Application.Operator.Binary]
|
||||
}
|
||||
|
||||
"support the concatenation operator" in {
|
||||
val ir =
|
||||
"""
|
||||
|a ; b
|
||||
|""".stripMargin.toIrExpression.get
|
||||
|
||||
ir shouldBe an[IR.Application.Operator.Binary]
|
||||
}
|
||||
|
||||
"support the union operator" in {
|
||||
val ir =
|
||||
"""
|
||||
|A | B
|
||||
|""".stripMargin.toIrExpression.get
|
||||
|
||||
ir shouldBe an[IR.Application.Operator.Binary]
|
||||
}
|
||||
|
||||
"support the intersection operator" in {
|
||||
val ir =
|
||||
"""
|
||||
|A & B
|
||||
|""".stripMargin.toIrExpression.get
|
||||
|
||||
ir shouldBe an[IR.Application.Operator.Binary]
|
||||
}
|
||||
|
||||
"support the minus operator" in {
|
||||
val ir =
|
||||
"""
|
||||
|A \ B
|
||||
|""".stripMargin.toIrExpression.get
|
||||
|
||||
ir shouldBe an[IR.Application.Operator.Binary]
|
||||
}
|
||||
}
|
||||
|
||||
"AST translation for typeset literals" should {
|
||||
"work properly" in {
|
||||
val ir =
|
||||
"""
|
||||
|{ x := 0 ; y := 0 }
|
||||
|""".stripMargin.toIrExpression.get
|
||||
|
||||
ir shouldBe an[IR.Application.Literal.Typeset]
|
||||
}
|
||||
}
|
||||
|
||||
"AST translation for top-level type signatures" should {
|
||||
"work for method signatures" in {
|
||||
val ir =
|
||||
"""
|
||||
|MyAtom.foo : Number -> Number -> Number
|
||||
|MyAtom.foo = a -> b -> a + b
|
||||
|""".stripMargin.toIrModule.bindings.head
|
||||
|
||||
ir shouldBe an[IR.Type.Ascription]
|
||||
|
||||
val asc = ir.asInstanceOf[IR.Type.Ascription]
|
||||
asc.typed shouldBe an[IR.Name.MethodReference]
|
||||
}
|
||||
|
||||
"work for module-method signatures" in {
|
||||
val ir =
|
||||
"""
|
||||
|foo : Number -> Number
|
||||
|foo a = a
|
||||
|""".stripMargin.toIrModule.bindings.head
|
||||
|
||||
ir shouldBe an[IR.Type.Ascription]
|
||||
val asc = ir.asInstanceOf[IR.Type.Ascription]
|
||||
asc.typed shouldBe an[IR.Name.MethodReference]
|
||||
}
|
||||
|
||||
"work with sugared syntax" in {
|
||||
val ir =
|
||||
"""
|
||||
|MyAtom.foo : Number -> Number -> Number
|
||||
|MyAtom.foo a b = a + b
|
||||
|""".stripMargin.toIrModule.bindings.head
|
||||
|
||||
ir shouldBe an[IR.Type.Ascription]
|
||||
|
||||
val asc = ir.asInstanceOf[IR.Type.Ascription]
|
||||
asc.typed shouldBe an[IR.Name.MethodReference]
|
||||
}
|
||||
|
||||
"result in a syntax error if not valid" in {
|
||||
val ir =
|
||||
"""
|
||||
|a b : Number -> Number -> Number
|
||||
|MyAtom.foo a b = a + b
|
||||
|""".stripMargin.toIrModule.bindings.head
|
||||
|
||||
ir shouldBe an[IR.Error.Syntax]
|
||||
ir.asInstanceOf[IR.Error.Syntax]
|
||||
.reason shouldBe an[Syntax.InvalidStandaloneSignature.type]
|
||||
}
|
||||
|
||||
"work inside type bodies" in {
|
||||
val ir =
|
||||
"""
|
||||
|type MyType
|
||||
| type MyAtom
|
||||
|
|
||||
| foo : this -> integer
|
||||
| foo = 0
|
||||
|""".stripMargin.toIrModule.bindings.head
|
||||
.asInstanceOf[IR.Module.Scope.Definition.Type]
|
||||
|
||||
ir.body.length shouldEqual 3
|
||||
ir.body(1) shouldBe an[IR.Type.Ascription]
|
||||
}
|
||||
}
|
||||
|
||||
"AST translation for expression-level type signatures" should {
|
||||
"work in block contexts" in {
|
||||
val ir =
|
||||
"""
|
||||
|x : Number
|
||||
|x = 10
|
||||
|""".stripMargin.toIrExpression.get.asInstanceOf[IR.Expression.Block]
|
||||
|
||||
ir.expressions.head shouldBe an[IR.Application.Operator.Binary]
|
||||
}
|
||||
|
||||
"work in expression contexts" in {
|
||||
val ir =
|
||||
"""
|
||||
|(a + b) : Number
|
||||
|""".stripMargin.toIrExpression.get
|
||||
|
||||
ir shouldBe an[IR.Application.Operator.Binary]
|
||||
}
|
||||
|
||||
"work properly when used in assignments" in {
|
||||
val ir =
|
||||
"""
|
||||
|x = a : Number
|
||||
|""".stripMargin.toIrExpression.get
|
||||
|
||||
ir shouldBe an[IR.Expression.Binding]
|
||||
|
||||
ir.asInstanceOf[IR.Expression.Binding]
|
||||
.expression shouldBe an[IR.Application.Operator.Binary]
|
||||
}
|
||||
|
||||
"properly support nested ascriptions" in {
|
||||
val ir =
|
||||
"""
|
||||
|x : (a : Type) -> (b : Type -> Type) -> (c : Type)
|
||||
|""".stripMargin.toIrExpression.get
|
||||
.asInstanceOf[IR.Application.Operator.Binary]
|
||||
|
||||
ir.right.value
|
||||
.asInstanceOf[IR.Application.Operator.Binary]
|
||||
.left
|
||||
.value shouldBe an[IR.Application.Operator.Binary]
|
||||
}
|
||||
|
||||
"properly support the `in` context ascription operator" in {
|
||||
val ir =
|
||||
"""
|
||||
|x : Number in (Maybe | IO)
|
||||
|""".stripMargin.toIrExpression.get
|
||||
.asInstanceOf[IR.Application.Operator.Binary]
|
||||
|
||||
ir.right.value shouldBe an[IR.Application.Operator.Binary]
|
||||
ir.right.value
|
||||
.asInstanceOf[IR.Application.Operator.Binary]
|
||||
.operator
|
||||
.name shouldEqual "in"
|
||||
}
|
||||
|
||||
"properly support the `!` error ascription operator" in {
|
||||
val ir =
|
||||
"""
|
||||
|x : Number in IO ! OverflowError
|
||||
|""".stripMargin.toIrExpression.get
|
||||
.asInstanceOf[IR.Application.Operator.Binary]
|
||||
.right
|
||||
.value
|
||||
|
||||
ir shouldBe an[IR.Application.Operator.Binary]
|
||||
ir.asInstanceOf[IR.Application.Operator.Binary]
|
||||
.operator
|
||||
.name shouldEqual "!"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -964,6 +964,33 @@ class AliasAnalysisTest extends CompilerTest {
|
||||
}
|
||||
}
|
||||
|
||||
"Alias analysis on typeset literals" should {
|
||||
implicit val ctx: ModuleContext = mkModuleContext
|
||||
|
||||
val method =
|
||||
"""
|
||||
|main =
|
||||
| { x := 1, b := 2 }
|
||||
|""".stripMargin.preprocessModule.analyse.bindings.head
|
||||
.asInstanceOf[Method]
|
||||
|
||||
val block = method.body
|
||||
.asInstanceOf[IR.Function.Lambda]
|
||||
.body
|
||||
.asInstanceOf[IR.Expression.Block]
|
||||
|
||||
val blockScope =
|
||||
block.unsafeGetMetadata(AliasAnalysis, "").unsafeAs[Info.Scope.Child]
|
||||
|
||||
val literal = block.returnValue.asInstanceOf[IR.Application.Literal.Typeset]
|
||||
val literalScope =
|
||||
literal.unsafeGetMetadata(AliasAnalysis, "").unsafeAs[Info.Scope.Child]
|
||||
|
||||
"create a new scope for the literal" in {
|
||||
blockScope.scope.childScopes should contain(literalScope.scope)
|
||||
}
|
||||
}
|
||||
|
||||
"Redefinitions" should {
|
||||
"be caught for bindings" in {
|
||||
implicit val ctx: ModuleContext = mkModuleContext
|
||||
|
@ -873,6 +873,25 @@ class DataflowAnalysisTest extends CompilerTest {
|
||||
depInfo.getDirect(vecId) shouldEqual Some(Set(lamId))
|
||||
}
|
||||
|
||||
"work properly for typeset literals" in {
|
||||
implicit val inlineContext: InlineContext = mkInlineContext
|
||||
|
||||
val ir =
|
||||
"""
|
||||
|{ x := a ; y := b }
|
||||
|""".stripMargin.preprocessExpression.get.analyse
|
||||
|
||||
val depInfo = ir.getMetadata(DataflowAnalysis).get
|
||||
|
||||
val literal = ir.asInstanceOf[IR.Application.Literal.Typeset]
|
||||
val literalExpression = literal.expression.get
|
||||
|
||||
val literalId = mkStaticDep(literal.getId)
|
||||
val literalExpressionId = mkStaticDep(literalExpression.getId)
|
||||
|
||||
depInfo.getDirect(literalExpressionId).get shouldEqual Set(literalId)
|
||||
}
|
||||
|
||||
"work properly for case expressions" in {
|
||||
implicit val inlineContext: InlineContext = mkInlineContext
|
||||
|
||||
@ -891,9 +910,9 @@ class DataflowAnalysisTest extends CompilerTest {
|
||||
val caseBindingExpr =
|
||||
caseBinding.expression.asInstanceOf[IR.Application.Prefix]
|
||||
val caseBindingName = caseBinding.name.asInstanceOf[IR.Name.Literal]
|
||||
val caseExpr = caseBlock.returnValue.asInstanceOf[IR.Case.Expr]
|
||||
val scrutinee = caseExpr.scrutinee.asInstanceOf[IR.Name.Literal]
|
||||
val consBranch = caseExpr.branches.head
|
||||
val caseExpr = caseBlock.returnValue.asInstanceOf[IR.Case.Expr]
|
||||
val scrutinee = caseExpr.scrutinee.asInstanceOf[IR.Name.Literal]
|
||||
val consBranch = caseExpr.branches.head
|
||||
|
||||
val consBranchPattern =
|
||||
consBranch.pattern.asInstanceOf[Pattern.Constructor]
|
||||
|
@ -62,6 +62,9 @@ class GatherDiagnosticsTest extends CompilerTest {
|
||||
val method2Name = IR.Name.Literal("baz", None)
|
||||
val fooName = IR.Name.Literal("foo", None)
|
||||
|
||||
val method1Ref = IR.Name.MethodReference(List(typeName), method1Name, None)
|
||||
val method2Ref = IR.Name.MethodReference(List(typeName), method2Name, None)
|
||||
|
||||
val module = IR.Module(
|
||||
List(),
|
||||
List(
|
||||
@ -74,9 +77,9 @@ class GatherDiagnosticsTest extends CompilerTest {
|
||||
None
|
||||
),
|
||||
IR.Module.Scope.Definition.Method
|
||||
.Explicit(typeName, method1Name, lam, None),
|
||||
.Explicit(method1Ref, lam, None),
|
||||
IR.Module.Scope.Definition.Method
|
||||
.Explicit(typeName, method2Name, error3, None)
|
||||
.Explicit(method2Ref, error3, None)
|
||||
),
|
||||
None
|
||||
)
|
||||
|
@ -59,11 +59,17 @@ class ComplexTypeTest extends CompilerTest {
|
||||
| Nothing
|
||||
| type Just a
|
||||
|
|
||||
| invalid_sig : Int
|
||||
|
|
||||
| is_just : this -> Boolean
|
||||
| is_just = case this of
|
||||
| Nothing -> false
|
||||
| Just _ -> true
|
||||
|
|
||||
| f : (a: Monoid) -> a
|
||||
| f a = this + a
|
||||
|
|
||||
| bad_trailing_sig : Double
|
||||
|""".stripMargin.preprocessModule.desugar
|
||||
|
||||
"have their atoms desugared to top-level atoms" in {
|
||||
@ -80,27 +86,110 @@ class ComplexTypeTest extends CompilerTest {
|
||||
}
|
||||
|
||||
"have their methods desugared to methods on included atoms" in {
|
||||
ir.bindings(1) shouldBe an[Definition.Method.Binding]
|
||||
val justIsJust = ir.bindings(1).asInstanceOf[Definition.Method.Binding]
|
||||
ir.bindings(3) shouldBe an[Definition.Method.Binding]
|
||||
val justIsJust = ir.bindings(3).asInstanceOf[Definition.Method.Binding]
|
||||
justIsJust.methodName.name shouldEqual "is_just"
|
||||
justIsJust.typeName.name shouldEqual "Nothing"
|
||||
|
||||
ir.bindings(3) shouldBe an[Definition.Method.Binding]
|
||||
val justF = ir.bindings(3).asInstanceOf[Definition.Method.Binding]
|
||||
ir.bindings(7) shouldBe an[Definition.Method.Binding]
|
||||
val justF = ir.bindings(7).asInstanceOf[Definition.Method.Binding]
|
||||
justF.methodName.name shouldEqual "f"
|
||||
justF.typeName.name shouldEqual "Nothing"
|
||||
}
|
||||
|
||||
"have their methods desugared to methods on the defined atoms" in {
|
||||
ir.bindings(2) shouldBe an[Definition.Method.Binding]
|
||||
val justIsJust = ir.bindings(2).asInstanceOf[Definition.Method.Binding]
|
||||
ir.bindings(5) shouldBe an[Definition.Method.Binding]
|
||||
val justIsJust = ir.bindings(5).asInstanceOf[Definition.Method.Binding]
|
||||
justIsJust.methodName.name shouldEqual "is_just"
|
||||
justIsJust.typeName.name shouldEqual "Just"
|
||||
|
||||
ir.bindings(4) shouldBe an[Definition.Method.Binding]
|
||||
val justF = ir.bindings(4).asInstanceOf[Definition.Method.Binding]
|
||||
ir.bindings(9) shouldBe an[Definition.Method.Binding]
|
||||
val justF = ir.bindings(9).asInstanceOf[Definition.Method.Binding]
|
||||
justF.methodName.name shouldEqual "f"
|
||||
justF.typeName.name shouldEqual "Just"
|
||||
}
|
||||
|
||||
"have type signatures copied to above each method" in {
|
||||
ir.bindings(2) shouldBe an[IR.Type.Ascription]
|
||||
ir.bindings(6) shouldBe an[IR.Type.Ascription]
|
||||
ir.bindings(4) shouldBe an[IR.Type.Ascription]
|
||||
ir.bindings(8) shouldBe an[IR.Type.Ascription]
|
||||
|
||||
val nothingIsJustSigName = ir
|
||||
.bindings(2)
|
||||
.asInstanceOf[IR.Type.Ascription]
|
||||
.typed
|
||||
.asInstanceOf[IR.Name.MethodReference]
|
||||
val nothingIsJustMethodName = ir
|
||||
.bindings(3)
|
||||
.asInstanceOf[Definition.Method.Binding]
|
||||
.methodReference
|
||||
|
||||
assert(
|
||||
nothingIsJustSigName isSameReferenceAs nothingIsJustMethodName,
|
||||
"The type signature and method did not have the same reference."
|
||||
)
|
||||
|
||||
val nothingFSigName = ir
|
||||
.bindings(6)
|
||||
.asInstanceOf[IR.Type.Ascription]
|
||||
.typed
|
||||
.asInstanceOf[IR.Name.MethodReference]
|
||||
val nothingFMethodName = ir
|
||||
.bindings(7)
|
||||
.asInstanceOf[Definition.Method.Binding]
|
||||
.methodReference
|
||||
|
||||
assert(
|
||||
nothingFSigName isSameReferenceAs nothingFMethodName,
|
||||
"The type signature and method did not have the same reference."
|
||||
)
|
||||
|
||||
val justIsJustSigName = ir
|
||||
.bindings(4)
|
||||
.asInstanceOf[IR.Type.Ascription]
|
||||
.typed
|
||||
.asInstanceOf[IR.Name.MethodReference]
|
||||
val justIsJustMethodName = ir
|
||||
.bindings(5)
|
||||
.asInstanceOf[Definition.Method.Binding]
|
||||
.methodReference
|
||||
|
||||
assert(
|
||||
justIsJustSigName isSameReferenceAs justIsJustMethodName,
|
||||
"The type signature and method did not have the same reference."
|
||||
)
|
||||
|
||||
val justFSigName = ir
|
||||
.bindings(8)
|
||||
.asInstanceOf[IR.Type.Ascription]
|
||||
.typed
|
||||
.asInstanceOf[IR.Name.MethodReference]
|
||||
val justFMethodName = ir
|
||||
.bindings(9)
|
||||
.asInstanceOf[Definition.Method.Binding]
|
||||
.methodReference
|
||||
|
||||
assert(
|
||||
justFSigName isSameReferenceAs justFMethodName,
|
||||
"The type signature and method did not have the same reference."
|
||||
)
|
||||
}
|
||||
|
||||
"leave un-associated signatures intact" in {
|
||||
ir.bindings(1) shouldBe an[IR.Type.Ascription]
|
||||
ir.bindings(1)
|
||||
.asInstanceOf[IR.Type.Ascription]
|
||||
.typed
|
||||
.asInstanceOf[IR.Name.Literal]
|
||||
.name shouldEqual "invalid_sig"
|
||||
|
||||
ir.bindings(10) shouldBe an[IR.Type.Ascription]
|
||||
ir.bindings(10)
|
||||
.asInstanceOf[IR.Type.Ascription]
|
||||
.typed
|
||||
.asInstanceOf[IR.Name.Literal]
|
||||
.name shouldEqual "bad_trailing_sig"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -225,15 +225,21 @@ class LambdaShorthandToLambdaTest extends CompilerTest {
|
||||
|""".stripMargin.preprocessExpression.get.desugar
|
||||
|
||||
ir shouldBe an[IR.Function.Lambda]
|
||||
val irFn = ir.asInstanceOf[IR.Function.Lambda]
|
||||
val argName = irFn.arguments.head.name
|
||||
val body = irFn.body.asInstanceOf[IR.Application.Prefix]
|
||||
val underscoreFn = ir.asInstanceOf[IR.Function.Lambda]
|
||||
val underscoreArgName = underscoreFn.arguments.head.name
|
||||
|
||||
body.arguments.length shouldEqual 1
|
||||
val rightArgLambda = underscoreFn.body.asInstanceOf[IR.Function.Lambda]
|
||||
rightArgLambda.arguments.length shouldEqual 1
|
||||
val rightArgLambdaArgName = rightArgLambda.arguments.head.name
|
||||
|
||||
val leftArgName = body.arguments.head.value.asInstanceOf[IR.Name.Literal]
|
||||
val body = rightArgLambda.body.asInstanceOf[IR.Application.Prefix]
|
||||
body.arguments.length shouldEqual 2
|
||||
|
||||
argName.name shouldEqual leftArgName.name
|
||||
val leftCallArgName = body.arguments.head.value.asInstanceOf[IR.Name.Literal]
|
||||
val rightCallArgName = body.arguments(1).value.asInstanceOf[IR.Name.Literal]
|
||||
|
||||
underscoreArgName.name shouldEqual leftCallArgName.name
|
||||
rightArgLambdaArgName.name shouldEqual rightCallArgName.name
|
||||
}
|
||||
|
||||
"work correctly for centre operator sections" in {
|
||||
|
@ -56,8 +56,25 @@ class SectionsToBinOpTest extends CompilerTest {
|
||||
|(1 +)
|
||||
|""".stripMargin.preprocessExpression.get.desugar
|
||||
|
||||
ir shouldBe an[IR.Application.Prefix]
|
||||
ir.asInstanceOf[IR.Application.Prefix].arguments.length shouldEqual 1
|
||||
ir shouldBe an[IR.Function.Lambda]
|
||||
|
||||
val irLam = ir.asInstanceOf[IR.Function.Lambda]
|
||||
irLam.arguments.length shouldEqual 1
|
||||
|
||||
val lamArgName =
|
||||
irLam.arguments.head.asInstanceOf[IR.DefinitionArgument.Specified].name
|
||||
|
||||
val lamBody = irLam.body.asInstanceOf[IR.Application.Prefix]
|
||||
lamBody.arguments.length shouldEqual 2
|
||||
val lamBodyFirstArg =
|
||||
lamBody
|
||||
.arguments(1)
|
||||
.asInstanceOf[IR.CallArgument.Specified]
|
||||
.value
|
||||
.asInstanceOf[IR.Name.Literal]
|
||||
|
||||
lamBodyFirstArg.name shouldEqual lamArgName.name
|
||||
lamBodyFirstArg.getId should not equal lamArgName.getId
|
||||
}
|
||||
|
||||
"work for sides sections" in {
|
||||
@ -70,22 +87,34 @@ class SectionsToBinOpTest extends CompilerTest {
|
||||
|
||||
ir shouldBe an[IR.Function.Lambda]
|
||||
|
||||
val irLam = ir.asInstanceOf[IR.Function.Lambda]
|
||||
irLam.arguments.length shouldEqual 1
|
||||
val leftLam = ir.asInstanceOf[IR.Function.Lambda]
|
||||
leftLam.arguments.length shouldEqual 1
|
||||
val leftLamArgName =
|
||||
leftLam.arguments.head
|
||||
.asInstanceOf[IR.DefinitionArgument.Specified]
|
||||
.name
|
||||
|
||||
val lamArgName =
|
||||
irLam.arguments.head.asInstanceOf[IR.DefinitionArgument.Specified].name
|
||||
val rightLam = leftLam.body.asInstanceOf[IR.Function.Lambda]
|
||||
rightLam.arguments.length shouldEqual 1
|
||||
val rightLamArgName = rightLam.arguments.head
|
||||
.asInstanceOf[IR.DefinitionArgument.Specified]
|
||||
.name
|
||||
|
||||
val lamBody = irLam.body.asInstanceOf[IR.Application.Prefix]
|
||||
lamBody.arguments.length shouldEqual 1
|
||||
val lamBodyFirstArg =
|
||||
lamBody.arguments.head
|
||||
.asInstanceOf[IR.CallArgument.Specified]
|
||||
.value
|
||||
.asInstanceOf[IR.Name.Literal]
|
||||
val lamBody = rightLam.body.asInstanceOf[IR.Application.Prefix]
|
||||
lamBody.arguments.length shouldEqual 2
|
||||
val lamBodyFirstArg = lamBody.arguments.head
|
||||
.asInstanceOf[IR.CallArgument.Specified]
|
||||
.value
|
||||
.asInstanceOf[IR.Name.Literal]
|
||||
val lamBodySecondArg = lamBody.arguments(1)
|
||||
.asInstanceOf[IR.CallArgument.Specified]
|
||||
.value
|
||||
.asInstanceOf[IR.Name.Literal]
|
||||
|
||||
lamBodyFirstArg.name shouldEqual lamArgName.name
|
||||
lamBodyFirstArg.getId should not equal lamArgName.getId
|
||||
lamBodyFirstArg.name shouldEqual leftLamArgName.name
|
||||
lamBodySecondArg.name shouldEqual rightLamArgName.name
|
||||
lamBodyFirstArg.getId should not equal leftLamArgName.getId
|
||||
lamBodySecondArg.getId should not equal rightLamArgName.getId
|
||||
}
|
||||
|
||||
"work for right sections" in {
|
||||
@ -125,8 +154,15 @@ class SectionsToBinOpTest extends CompilerTest {
|
||||
|""".stripMargin.preprocessExpression.get.desugar
|
||||
.asInstanceOf[IR.Function.Lambda]
|
||||
|
||||
ir.body shouldBe an[IR.Application.Prefix]
|
||||
ir.body.asInstanceOf[IR.Application.Prefix].arguments.length shouldEqual 1
|
||||
ir.body
|
||||
.asInstanceOf[IR.Function.Lambda]
|
||||
.body shouldBe an[IR.Application.Prefix]
|
||||
ir.body
|
||||
.asInstanceOf[IR.Function.Lambda]
|
||||
.body
|
||||
.asInstanceOf[IR.Application.Prefix]
|
||||
.arguments
|
||||
.length shouldEqual 2
|
||||
}
|
||||
|
||||
"flip the arguments when a right section's argument is a blank" in {
|
||||
|
@ -26,7 +26,7 @@ class IgnoredBindingsTest extends CompilerTest {
|
||||
*
|
||||
* @param ir the IR to desugar
|
||||
*/
|
||||
implicit class DesugarExpression(ir: IR.Expression) {
|
||||
implicit class ResolveExpression(ir: IR.Expression) {
|
||||
|
||||
/** Runs ignores desugaring on [[ir]].
|
||||
*
|
||||
@ -34,7 +34,7 @@ class IgnoredBindingsTest extends CompilerTest {
|
||||
* place
|
||||
* @return [[ir]], with all ignores desugared
|
||||
*/
|
||||
def desugar(implicit inlineContext: InlineContext): IR.Expression = {
|
||||
def resolve(implicit inlineContext: InlineContext): IR.Expression = {
|
||||
IgnoredBindings.runExpression(ir, inlineContext)
|
||||
}
|
||||
}
|
||||
@ -55,7 +55,7 @@ class IgnoredBindingsTest extends CompilerTest {
|
||||
val ir =
|
||||
"""
|
||||
|_ -> (x = _ -> 1) -> x
|
||||
|""".stripMargin.preprocessExpression.get.desugar
|
||||
|""".stripMargin.preprocessExpression.get.resolve
|
||||
.asInstanceOf[IR.Function.Lambda]
|
||||
val blankArg =
|
||||
ir.arguments.head.asInstanceOf[IR.DefinitionArgument.Specified]
|
||||
@ -98,7 +98,7 @@ class IgnoredBindingsTest extends CompilerTest {
|
||||
| _ = f a b
|
||||
| x = y
|
||||
| 10
|
||||
|""".stripMargin.preprocessExpression.get.desugar
|
||||
|""".stripMargin.preprocessExpression.get.resolve
|
||||
.asInstanceOf[IR.Expression.Binding]
|
||||
|
||||
val bindingName = ir.name
|
||||
@ -137,7 +137,7 @@ class IgnoredBindingsTest extends CompilerTest {
|
||||
|case x of
|
||||
| Cons a _ -> case y of
|
||||
| MyCons a _ -> 10
|
||||
|""".stripMargin.preprocessExpression.get.desugar
|
||||
|""".stripMargin.preprocessExpression.get.resolve
|
||||
.asInstanceOf[IR.Expression.Block]
|
||||
.returnValue
|
||||
.asInstanceOf[IR.Case.Expr]
|
||||
|
@ -0,0 +1,196 @@
|
||||
package org.enso.compiler.test.pass.resolve
|
||||
|
||||
import org.enso.compiler.Passes
|
||||
import org.enso.compiler.context.{FreshNameSupply, InlineContext, ModuleContext}
|
||||
import org.enso.compiler.core.IR
|
||||
import org.enso.compiler.core.IR.Module.Scope.Definition.Method
|
||||
import org.enso.compiler.pass.analyse.AliasAnalysis
|
||||
import org.enso.compiler.pass.resolve.SuspendedArguments
|
||||
import org.enso.compiler.pass.{IRPass, PassConfiguration, PassManager}
|
||||
import org.enso.compiler.test.CompilerTest
|
||||
import org.enso.interpreter.runtime.scope.LocalScope
|
||||
import org.enso.compiler.pass.PassConfiguration._
|
||||
|
||||
class SuspendedArgumentsTest extends CompilerTest {
|
||||
|
||||
// === Test Setup ===========================================================
|
||||
|
||||
val passes = new Passes
|
||||
|
||||
val precursorPasses: List[IRPass] =
|
||||
passes.getPrecursors(SuspendedArguments).get
|
||||
|
||||
val passConfiguration: PassConfiguration = PassConfiguration(
|
||||
AliasAnalysis -->> AliasAnalysis.Configuration()
|
||||
)
|
||||
|
||||
implicit val passManager: PassManager =
|
||||
new PassManager(precursorPasses, passConfiguration)
|
||||
|
||||
/** Adds an extension method to a module for performing suspended argument
|
||||
* resolution.
|
||||
*
|
||||
* @param ir the IR to add the extension method to
|
||||
*/
|
||||
implicit class ResolveModule(ir: IR.Module) {
|
||||
|
||||
/** Resolves suspended arguments in [[ir]].
|
||||
*
|
||||
* @param moduleContext the context in which resolution is taking place
|
||||
* @return [[ir]], with all suspended arguments resolved
|
||||
*/
|
||||
def resolve(implicit moduleContext: ModuleContext): IR.Module = {
|
||||
SuspendedArguments.runModule(ir, moduleContext)
|
||||
}
|
||||
}
|
||||
|
||||
/** Adds an extension method to an expression for performing suspended
|
||||
* argument resolution.
|
||||
*
|
||||
* @param ir the expression to add the extension method to
|
||||
*/
|
||||
implicit class ResolveExpression(ir: IR.Expression) {
|
||||
|
||||
/** Resolves suspended arguments in [[ir]].
|
||||
*
|
||||
* @param inlineContext the context in which resolution is taking place
|
||||
* @return [[ir]], with all suspended arguments resolved
|
||||
*/
|
||||
def resolve(implicit inlineContext: InlineContext): IR.Expression = {
|
||||
SuspendedArguments.runExpression(ir, inlineContext)
|
||||
}
|
||||
}
|
||||
|
||||
/** Creates a defaulted module context.
|
||||
*
|
||||
* @return a defaulted module context
|
||||
*/
|
||||
def mkModuleContext: ModuleContext = {
|
||||
ModuleContext(freshNameSupply = Some(new FreshNameSupply))
|
||||
}
|
||||
|
||||
/** Creates a defaulted inline context.
|
||||
*
|
||||
* @return a defaulted inline context
|
||||
*/
|
||||
def mkInlineContext: InlineContext = {
|
||||
InlineContext(
|
||||
freshNameSupply = Some(new FreshNameSupply),
|
||||
localScope = Some(LocalScope.root)
|
||||
)
|
||||
}
|
||||
|
||||
// === The Tests ============================================================
|
||||
|
||||
"Suspended arguments resolution in modules" should {
|
||||
"correctly mark arguments as suspended based on their type signatures" in {
|
||||
implicit val ctx: ModuleContext = mkModuleContext
|
||||
|
||||
val ir =
|
||||
"""
|
||||
|Any.id : Suspended -> a
|
||||
|Any.id a = a
|
||||
|""".stripMargin.preprocessModule.resolve.bindings.head
|
||||
.asInstanceOf[Method]
|
||||
|
||||
val bodyLam = ir.body.asInstanceOf[IR.Function.Lambda]
|
||||
|
||||
bodyLam.arguments.length shouldEqual 2
|
||||
assert(
|
||||
!bodyLam.arguments.head.suspended,
|
||||
"the `this` argument is suspended"
|
||||
)
|
||||
assert(
|
||||
bodyLam.arguments(1).suspended,
|
||||
"the `a` argument is not suspended"
|
||||
)
|
||||
}
|
||||
|
||||
"work recursively" in {
|
||||
implicit val ctx: ModuleContext = mkModuleContext
|
||||
|
||||
val ir =
|
||||
"""
|
||||
|Any.id : Suspended -> a
|
||||
|Any.id a =
|
||||
| lazy_id : Suspended -> a
|
||||
| lazy_id x = x
|
||||
|
|
||||
| lazy_id a
|
||||
|""".stripMargin.preprocessModule.resolve.bindings.head
|
||||
.asInstanceOf[Method]
|
||||
|
||||
val bodyBlock = ir.body
|
||||
.asInstanceOf[IR.Function.Lambda]
|
||||
.body
|
||||
.asInstanceOf[IR.Expression.Block]
|
||||
|
||||
val lazyId = bodyBlock.expressions.head
|
||||
.asInstanceOf[IR.Expression.Binding]
|
||||
.expression
|
||||
.asInstanceOf[IR.Function.Lambda]
|
||||
|
||||
assert(lazyId.arguments.head.suspended, "x was not suspended")
|
||||
}
|
||||
}
|
||||
|
||||
"Suspended arguments resolution in expressions" should {
|
||||
"correctly mark arguments as suspended in blocks" in {
|
||||
implicit val ctx: InlineContext = mkInlineContext
|
||||
|
||||
val ir =
|
||||
"""
|
||||
|f : a -> Suspended -> b
|
||||
|f a b = b
|
||||
|""".stripMargin.preprocessExpression.get.resolve
|
||||
.asInstanceOf[IR.Expression.Block]
|
||||
|
||||
val func = ir.returnValue
|
||||
.asInstanceOf[IR.Expression.Binding]
|
||||
.expression
|
||||
.asInstanceOf[IR.Function.Lambda]
|
||||
assert(!func.arguments.head.suspended, "a is suspended")
|
||||
assert(func.arguments(1).suspended, "b is not suspended")
|
||||
}
|
||||
|
||||
"correctly mark arguments as suspended using inline expressions" in {
|
||||
implicit val ctx: InlineContext = mkInlineContext
|
||||
|
||||
val ir =
|
||||
"""
|
||||
|(x -> y -> y + x) : Suspended -> a -> a
|
||||
|""".stripMargin.preprocessExpression.get.resolve
|
||||
|
||||
ir shouldBe an[IR.Function.Lambda]
|
||||
val lam = ir.asInstanceOf[IR.Function.Lambda]
|
||||
assert(lam.arguments.head.suspended, "x is not suspended")
|
||||
assert(!lam.arguments(1).suspended, "y is suspended")
|
||||
}
|
||||
|
||||
"work recursively" in {
|
||||
implicit val ctx: InlineContext = mkInlineContext
|
||||
|
||||
val ir =
|
||||
"""
|
||||
|x -> y ->
|
||||
| f : a -> Suspended -> a
|
||||
| f a b = a + b
|
||||
|
|
||||
| f 100 50
|
||||
|""".stripMargin.preprocessExpression.get.resolve
|
||||
|
||||
ir shouldBe an[IR.Function.Lambda]
|
||||
val lam = ir.asInstanceOf[IR.Function.Lambda]
|
||||
val f = lam.body
|
||||
.asInstanceOf[IR.Expression.Block]
|
||||
.expressions
|
||||
.head
|
||||
.asInstanceOf[IR.Expression.Binding]
|
||||
.expression
|
||||
.asInstanceOf[IR.Function.Lambda]
|
||||
|
||||
assert(!f.arguments.head.suspended, "a was suspended")
|
||||
assert(f.arguments(1).suspended, "b was not suspended")
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,199 @@
|
||||
package org.enso.compiler.test.pass.resolve
|
||||
|
||||
import org.enso.compiler.Passes
|
||||
import org.enso.compiler.context.{FreshNameSupply, InlineContext}
|
||||
import org.enso.compiler.core.IR
|
||||
import org.enso.compiler.pass.resolve.{IgnoredBindings, TypeFunctions}
|
||||
import org.enso.compiler.pass.{IRPass, PassConfiguration, PassManager}
|
||||
import org.enso.compiler.test.CompilerTest
|
||||
|
||||
class TypeFunctionsTest extends CompilerTest {
|
||||
|
||||
// === Test Setup ===========================================================
|
||||
|
||||
val passes = new Passes
|
||||
|
||||
val precursorPasses: List[IRPass] = passes.getPrecursors(TypeFunctions).get
|
||||
|
||||
val passConfiguration: PassConfiguration = PassConfiguration()
|
||||
|
||||
implicit val passManager: PassManager =
|
||||
new PassManager(precursorPasses, passConfiguration)
|
||||
|
||||
/** Adds an extension method to resolve typing functions to an expression.
|
||||
*
|
||||
* @param ir the expression to resolve typing functions in
|
||||
*/
|
||||
implicit class ResolveExpression(ir: IR.Expression) {
|
||||
|
||||
/** Resolves typing functions on [[ir]].
|
||||
*
|
||||
* @param inlineContext the context win which resolution takes place
|
||||
* @return [[ir]], with typing functions resolved
|
||||
*/
|
||||
def resolve(implicit inlineContext: InlineContext): IR.Expression = {
|
||||
TypeFunctions.runExpression(ir, inlineContext)
|
||||
}
|
||||
}
|
||||
|
||||
/** Makes an inline context.
|
||||
*
|
||||
* @return a new inline context
|
||||
*/
|
||||
def mkInlineContext: InlineContext = {
|
||||
InlineContext(freshNameSupply = Some(new FreshNameSupply))
|
||||
}
|
||||
|
||||
// === The Tests ============================================================
|
||||
|
||||
"Type functions resolution" should {
|
||||
implicit val ctx: InlineContext = mkInlineContext
|
||||
|
||||
"work for saturated applications" in {
|
||||
val ir =
|
||||
"""
|
||||
|a : B
|
||||
|""".stripMargin.preprocessExpression.get.resolve
|
||||
|
||||
ir shouldBe an[IR.Type.Ascription]
|
||||
}
|
||||
|
||||
"work for left sections" in {
|
||||
val ir =
|
||||
"""
|
||||
|(a :)
|
||||
|""".stripMargin.preprocessExpression.get.resolve
|
||||
|
||||
ir shouldBe an[IR.Function.Lambda]
|
||||
ir.asInstanceOf[IR.Function.Lambda].body shouldBe an[IR.Type.Ascription]
|
||||
}
|
||||
|
||||
"work for centre sections" in {
|
||||
val ir =
|
||||
"""
|
||||
|(:)
|
||||
|""".stripMargin.preprocessExpression.get.resolve
|
||||
|
||||
ir shouldBe an[IR.Function.Lambda]
|
||||
ir.asInstanceOf[IR.Function.Lambda]
|
||||
.body
|
||||
.asInstanceOf[IR.Function.Lambda]
|
||||
.body shouldBe an[IR.Type.Ascription]
|
||||
}
|
||||
|
||||
"work for right sections" in {
|
||||
val ir =
|
||||
"""
|
||||
|(: a)
|
||||
|""".stripMargin.preprocessExpression.get.resolve
|
||||
|
||||
ir shouldBe an[IR.Function.Lambda]
|
||||
ir.asInstanceOf[IR.Function.Lambda].body shouldBe an[IR.Type.Ascription]
|
||||
}
|
||||
|
||||
"work for underscore arguments on the left" in {
|
||||
val ir =
|
||||
"""
|
||||
|_ : A
|
||||
|""".stripMargin.preprocessExpression.get.resolve
|
||||
|
||||
ir shouldBe an[IR.Function.Lambda]
|
||||
ir.asInstanceOf[IR.Function.Lambda].body shouldBe an[IR.Type.Ascription]
|
||||
}
|
||||
|
||||
"work for underscore arguments on the right" in {
|
||||
val ir =
|
||||
"""
|
||||
|a : _
|
||||
|""".stripMargin.preprocessExpression.get.resolve
|
||||
|
||||
ir shouldBe an[IR.Function.Lambda]
|
||||
ir.asInstanceOf[IR.Function.Lambda].body shouldBe an[IR.Type.Ascription]
|
||||
}
|
||||
}
|
||||
|
||||
"Resolution" should {
|
||||
implicit val ctx: InlineContext = mkInlineContext
|
||||
|
||||
"resolve type ascription" in {
|
||||
val ir =
|
||||
"""
|
||||
|a : A
|
||||
|""".stripMargin.preprocessExpression.get.resolve
|
||||
|
||||
ir shouldBe an[IR.Type.Ascription]
|
||||
}
|
||||
|
||||
"resolve context ascription" in {
|
||||
val ir =
|
||||
"""
|
||||
|a in IO
|
||||
|""".stripMargin.preprocessExpression.get.resolve
|
||||
|
||||
ir shouldBe an[IR.Type.Context]
|
||||
}
|
||||
|
||||
"resolve error ascription" in {
|
||||
val ir =
|
||||
"""
|
||||
|IO ! Error
|
||||
|""".stripMargin.preprocessExpression.get.resolve
|
||||
|
||||
ir shouldBe an[IR.Type.Error]
|
||||
}
|
||||
|
||||
"resolve subsumption" in {
|
||||
val ir =
|
||||
"""
|
||||
|T <: P
|
||||
|""".stripMargin.preprocessExpression.get.resolve
|
||||
|
||||
ir shouldBe an[IR.Type.Set.Subsumption]
|
||||
}
|
||||
|
||||
"resolve equality" in {
|
||||
val ir =
|
||||
"""
|
||||
|T ~ P
|
||||
|""".stripMargin.preprocessExpression.get.resolve
|
||||
|
||||
ir shouldBe an[IR.Type.Set.Equality]
|
||||
}
|
||||
|
||||
"resolve concatenation" in {
|
||||
val ir =
|
||||
"""
|
||||
|T ; P
|
||||
|""".stripMargin.preprocessExpression.get.resolve
|
||||
|
||||
ir shouldBe an[IR.Type.Set.Concat]
|
||||
}
|
||||
|
||||
"resolve union" in {
|
||||
val ir =
|
||||
"""
|
||||
|T | P
|
||||
|""".stripMargin.preprocessExpression.get.resolve
|
||||
|
||||
ir shouldBe an[IR.Type.Set.Union]
|
||||
}
|
||||
|
||||
"resolve intersection" in {
|
||||
val ir =
|
||||
"""
|
||||
|T & P
|
||||
|""".stripMargin.preprocessExpression.get.resolve
|
||||
|
||||
ir shouldBe an[IR.Type.Set.Intersection]
|
||||
}
|
||||
|
||||
"resolve subtraction" in {
|
||||
val ir =
|
||||
"""
|
||||
|T \ P
|
||||
|""".stripMargin.preprocessExpression.get.resolve
|
||||
|
||||
ir shouldBe an[IR.Type.Set.Subtraction]
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,210 @@
|
||||
package org.enso.compiler.test.pass.resolve
|
||||
|
||||
import org.enso.compiler.Passes
|
||||
import org.enso.compiler.context.{FreshNameSupply, InlineContext, ModuleContext}
|
||||
import org.enso.compiler.core.IR
|
||||
import org.enso.compiler.pass.{IRPass, PassConfiguration, PassManager}
|
||||
import org.enso.compiler.pass.resolve.TypeSignatures
|
||||
import org.enso.compiler.test.CompilerTest
|
||||
|
||||
class TypeSignaturesTest extends CompilerTest {
|
||||
|
||||
// === Test Setup ===========================================================
|
||||
|
||||
val passes = new Passes
|
||||
|
||||
val precursorPasses: List[IRPass] = passes.getPrecursors(TypeSignatures).get
|
||||
|
||||
val passConfiguration: PassConfiguration = PassConfiguration()
|
||||
|
||||
implicit val passManager: PassManager =
|
||||
new PassManager(precursorPasses, passConfiguration)
|
||||
|
||||
/** Adds an extension method to a module for performing type signature
|
||||
* resolution.
|
||||
*
|
||||
* @param ir the IR to add the extension method to
|
||||
*/
|
||||
implicit class ResolveModule(ir: IR.Module) {
|
||||
|
||||
/** Resolves type signatures in [[ir]].
|
||||
*
|
||||
* @param moduleContext the context in which resolution is taking place
|
||||
* @return [[ir]], with all type signatures resolved
|
||||
*/
|
||||
def resolve(implicit moduleContext: ModuleContext): IR.Module = {
|
||||
TypeSignatures.runModule(ir, moduleContext)
|
||||
}
|
||||
}
|
||||
|
||||
/** Adds an extension method to an expression for performing type signature
|
||||
* resolution.
|
||||
*
|
||||
* @param ir the expression to add the extension method to
|
||||
*/
|
||||
implicit class ResolveExpression(ir: IR.Expression) {
|
||||
|
||||
/** Resolves type signatures in [[ir]].
|
||||
*
|
||||
* @param inlineContext the context in which resolution is taking place
|
||||
* @return [[ir]], with all type signatures resolved
|
||||
*/
|
||||
def resolve(implicit inlineContext: InlineContext): IR.Expression = {
|
||||
TypeSignatures.runExpression(ir, inlineContext)
|
||||
}
|
||||
}
|
||||
|
||||
/** Creates a defaulted module context.
|
||||
*
|
||||
* @return a defaulted module context
|
||||
*/
|
||||
def mkModuleContext: ModuleContext = {
|
||||
ModuleContext(freshNameSupply = Some(new FreshNameSupply))
|
||||
}
|
||||
|
||||
/** Creates a defaulted inline context.
|
||||
*
|
||||
* @return a defaulted inline context
|
||||
*/
|
||||
def mkInlineContext: InlineContext = {
|
||||
InlineContext(freshNameSupply = Some(new FreshNameSupply))
|
||||
}
|
||||
|
||||
// === The Tests ============================================================
|
||||
|
||||
"Resolution of type signatures in modules" should {
|
||||
implicit val ctx: ModuleContext = mkModuleContext
|
||||
|
||||
"associate signatures with method definitions" in {
|
||||
val ir =
|
||||
"""
|
||||
|MyAtom.bar : Number -> Suspended Number -> Number
|
||||
|MyAtom.bar a b = a + b
|
||||
|""".stripMargin.preprocessModule.resolve
|
||||
|
||||
ir.bindings.length shouldEqual 1
|
||||
ir.bindings.head.getMetadata(TypeSignatures) shouldBe defined
|
||||
}
|
||||
|
||||
"raise an error if a signature is divorced from its definition" in {
|
||||
val ir =
|
||||
"""
|
||||
|MyAtom.quux : Fizz
|
||||
|MyAtom.foo : Number -> Number -> Number
|
||||
|
|
||||
|MyAtom.bar = 1
|
||||
|
|
||||
|MyAtom.foo a b = a + b
|
||||
|
|
||||
|MyAtom.baz : Int
|
||||
|""".stripMargin.preprocessModule.resolve
|
||||
|
||||
ir.bindings.length shouldEqual 5
|
||||
ir.bindings.head shouldBe an[IR.Error.Unexpected.TypeSignature]
|
||||
ir.bindings(1) shouldBe an[IR.Error.Unexpected.TypeSignature]
|
||||
ir.bindings(2) shouldBe an[IR.Module.Scope.Definition.Method]
|
||||
ir.bindings(3) shouldBe an[IR.Module.Scope.Definition.Method]
|
||||
ir.bindings(4) shouldBe an[IR.Error.Unexpected.TypeSignature]
|
||||
}
|
||||
|
||||
"work inside type definition bodies" in {
|
||||
val ir =
|
||||
"""
|
||||
|type MyType
|
||||
| type MyAtom
|
||||
|
|
||||
| is_atom : this -> Boolean
|
||||
| is_atom = true
|
||||
|
|
||||
| error_signature : Int
|
||||
|""".stripMargin.preprocessModule.resolve
|
||||
|
||||
ir.bindings.length shouldEqual 3
|
||||
ir.bindings.head shouldBe an[IR.Module.Scope.Definition.Atom]
|
||||
ir.bindings(1) shouldBe an[IR.Module.Scope.Definition.Method]
|
||||
ir.bindings(1).getMetadata(TypeSignatures) shouldBe defined
|
||||
ir.bindings(2) shouldBe an[IR.Error.Unexpected.TypeSignature]
|
||||
}
|
||||
|
||||
"recurse into bodies" in {
|
||||
val ir =
|
||||
"""
|
||||
|main =
|
||||
| f : a -> a
|
||||
| f a = a
|
||||
|
|
||||
| f 1
|
||||
|""".stripMargin.preprocessModule.resolve.bindings.head
|
||||
.asInstanceOf[IR.Module.Scope.Definition.Method]
|
||||
|
||||
val block = ir.body
|
||||
.asInstanceOf[IR.Function.Lambda]
|
||||
.body
|
||||
.asInstanceOf[IR.Expression.Block]
|
||||
|
||||
block.expressions.length shouldEqual 1
|
||||
block.expressions.head.getMetadata(TypeSignatures) shouldBe defined
|
||||
}
|
||||
}
|
||||
|
||||
"Resolution of type signatures for blocks" should {
|
||||
implicit val ctx: InlineContext = mkInlineContext
|
||||
|
||||
val ir =
|
||||
"""
|
||||
|block =
|
||||
| f : Int
|
||||
| f = 0
|
||||
|
|
||||
| g : Int
|
||||
| g =
|
||||
| f : Double
|
||||
| f = 0
|
||||
| f 1
|
||||
|
|
||||
| bad_sig : Int
|
||||
|""".stripMargin.preprocessExpression.get.resolve
|
||||
.asInstanceOf[IR.Expression.Binding]
|
||||
|
||||
val block = ir.expression.asInstanceOf[IR.Expression.Block]
|
||||
|
||||
"associate signatures with bindings" in {
|
||||
block.expressions.head shouldBe an[IR.Expression.Binding]
|
||||
block.expressions.head.getMetadata(TypeSignatures) shouldBe defined
|
||||
}
|
||||
|
||||
"raise an error if a signature is divorced from its definition" in {
|
||||
block.returnValue shouldBe an[IR.Error.Unexpected.TypeSignature]
|
||||
}
|
||||
|
||||
"work recursively" in {
|
||||
val nested = block
|
||||
.expressions(1)
|
||||
.asInstanceOf[IR.Expression.Binding]
|
||||
.expression
|
||||
.asInstanceOf[IR.Expression.Block]
|
||||
|
||||
nested.expressions.head shouldBe an[IR.Expression.Binding]
|
||||
nested.expressions.head.getMetadata(TypeSignatures) shouldBe defined
|
||||
}
|
||||
}
|
||||
|
||||
"Resolution of inline type signatures" should {
|
||||
implicit val ctx: InlineContext = mkInlineContext
|
||||
|
||||
val ir =
|
||||
"""
|
||||
|f a (b = 1 : Int) : Double
|
||||
|""".stripMargin.preprocessExpression.get.resolve
|
||||
|
||||
"associate the signature with the typed expression" in {
|
||||
ir shouldBe an[IR.Application.Prefix]
|
||||
ir.getMetadata(TypeSignatures) shouldBe defined
|
||||
}
|
||||
|
||||
"work recursively" in {
|
||||
val arg2Value = ir.asInstanceOf[IR.Application.Prefix].arguments(1).value
|
||||
arg2Value shouldBe an[IR.Literal.Number]
|
||||
}
|
||||
}
|
||||
}
|
@ -9,7 +9,9 @@ class SuspendedArgumentsTest extends InterpreterTest {
|
||||
val code =
|
||||
"""
|
||||
|main =
|
||||
| lazyId = ~x -> x
|
||||
| lazyId : Suspended -> a
|
||||
| lazyId = x -> x
|
||||
|
|
||||
| lazyId (1 + 1)
|
||||
|""".stripMargin
|
||||
|
||||
@ -20,7 +22,9 @@ class SuspendedArgumentsTest extends InterpreterTest {
|
||||
val code =
|
||||
"""
|
||||
|main =
|
||||
| foo = i -> ~x -> ~y -> ifZero i x y
|
||||
| foo : Number -> Suspended -> Suspended -> a
|
||||
| foo = i -> x -> y -> ifZero i x y
|
||||
|
|
||||
| foo 1 (IO.println 1) (IO.println 2)
|
||||
|""".stripMargin
|
||||
eval(code)
|
||||
@ -42,7 +46,9 @@ class SuspendedArgumentsTest extends InterpreterTest {
|
||||
val code =
|
||||
"""
|
||||
|main =
|
||||
| suspInc = ~x -> 1 + x
|
||||
| suspInc : Suspended -> Number
|
||||
| suspInc = x -> 1 + x
|
||||
|
|
||||
| suspInc (suspInc 10)
|
||||
|""".stripMargin
|
||||
|
||||
|
@ -345,6 +345,8 @@ object Shape extends ShapeImplicit {
|
||||
extends SpacelessAST[T]
|
||||
final case class Group[T](body: Option[T]) extends SpacelessAST[T]
|
||||
final case class SequenceLiteral[T](items: List[T]) extends SpacelessAST[T]
|
||||
final case class TypesetLiteral[T](expression: Option[T])
|
||||
extends SpacelessAST[T]
|
||||
final case class Def[T](name: AST.Cons, args: List[T], body: Option[T])
|
||||
extends SpacelessAST[T]
|
||||
final case class Foreign[T](indent: Int, lang: String, code: List[String])
|
||||
@ -1009,6 +1011,15 @@ object Shape extends ShapeImplicit {
|
||||
|
||||
}
|
||||
|
||||
object TypesetLiteral {
|
||||
implicit def ftor: Functor[TypesetLiteral] = semi.functor
|
||||
implicit def fold: Foldable[Def] = semi.foldable
|
||||
implicit def repr[T: Repr]: Repr[TypesetLiteral[T]] =
|
||||
t => s"{ ${t.expression.repr.build()} }"
|
||||
implicit def ozip[T]: OffsetZip[TypesetLiteral, T] = _.map(Index.Start -> _)
|
||||
implicit def span[T]: HasSpan[TypesetLiteral[T]] = _ => 0
|
||||
}
|
||||
|
||||
object Def {
|
||||
implicit def ftor: Functor[Def] = semi.functor
|
||||
implicit def fold: Foldable[Def] = semi.foldable
|
||||
@ -1103,6 +1114,7 @@ sealed trait ShapeImplicit {
|
||||
case s: Mixfix[T] => s.repr
|
||||
case s: Group[T] => s.repr
|
||||
case s: SequenceLiteral[T] => s.repr
|
||||
case s: TypesetLiteral[T] => s.repr
|
||||
case s: Def[T] => s.repr
|
||||
case s: Foreign[T] => s.repr
|
||||
}
|
||||
@ -1141,6 +1153,7 @@ sealed trait ShapeImplicit {
|
||||
case s: Mixfix[T] => OffsetZip[Mixfix, T].zipWithOffset(s)
|
||||
case s: Group[T] => OffsetZip[Group, T].zipWithOffset(s)
|
||||
case s: SequenceLiteral[T] => OffsetZip[SequenceLiteral, T].zipWithOffset(s)
|
||||
case s: TypesetLiteral[T] => OffsetZip[TypesetLiteral, T].zipWithOffset(s)
|
||||
case s: Def[T] => OffsetZip[Def, T].zipWithOffset(s)
|
||||
case s: Foreign[T] => OffsetZip[Foreign, T].zipWithOffset(s)
|
||||
}
|
||||
@ -1180,6 +1193,7 @@ sealed trait ShapeImplicit {
|
||||
case s: Mixfix[T] => s.span()
|
||||
case s: Group[T] => s.span()
|
||||
case s: SequenceLiteral[T] => s.span()
|
||||
case s: TypesetLiteral[T] => s.span()
|
||||
case s: Def[T] => s.span()
|
||||
case s: Foreign[T] => s.span()
|
||||
}
|
||||
@ -2333,7 +2347,7 @@ object AST {
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
//// SequenceLiteral /////////////////////////////////////////////////////////////
|
||||
//// SequenceLiteral /////////////////////////////////////////////////////////
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
type SequenceLiteral = ASTOf[Shape.SequenceLiteral]
|
||||
@ -2344,6 +2358,19 @@ object AST {
|
||||
def apply(items: List[AST]): SequenceLiteral = Shape.SequenceLiteral(items)
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
//// Typeset Literal /////////////////////////////////////////////////////////
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
type TypesetLiteral = ASTOf[Shape.TypesetLiteral]
|
||||
object TypesetLiteral {
|
||||
val any = UnapplyByType[TypesetLiteral]
|
||||
def unapply(t: AST): Option[Option[AST]] =
|
||||
Unapply[TypesetLiteral].run(_.expression)(t)
|
||||
def apply(expression: Option[AST]): TypesetLiteral =
|
||||
Shape.TypesetLiteral(expression)
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
//// Def /////////////////////////////////////////////////////////////////////
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -49,6 +49,25 @@ object Builtin {
|
||||
}
|
||||
}
|
||||
|
||||
val typesetLiteral = {
|
||||
val expression = Pattern.Expr(allowBlocks = false).opt
|
||||
Definition(
|
||||
Opr("{") -> expression,
|
||||
Opr("}")
|
||||
) { ctx =>
|
||||
ctx.body match {
|
||||
case List(seg1, _) =>
|
||||
val body = seg1.body.toStream.map(_.wrapped)
|
||||
body match {
|
||||
case List(expr) =>
|
||||
AST.TypesetLiteral(Some(expr))
|
||||
case _ => AST.TypesetLiteral(None)
|
||||
}
|
||||
case _ => internalError
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val defn = Definition(Var("type") -> {
|
||||
val head = Pattern.Cons().or("missing name").tag("name")
|
||||
val args =
|
||||
@ -287,6 +306,7 @@ object Builtin {
|
||||
Registry(
|
||||
group,
|
||||
sequenceLiteral,
|
||||
typesetLiteral,
|
||||
case_of,
|
||||
if_then,
|
||||
if_then_else,
|
||||
|
@ -22,6 +22,7 @@ object Assoc {
|
||||
|
||||
def of(op: String): Assoc =
|
||||
if (isApplicative(op)) Assoc.Left
|
||||
else if (op == "in") Assoc.Left
|
||||
else if (op.foldLeft(0)(_ + charAssoc(_)) >= 0) Assoc.Left
|
||||
else Assoc.Right
|
||||
}
|
||||
|
@ -1,18 +1,26 @@
|
||||
package org.enso.syntax.text.ast.opr
|
||||
|
||||
object Prec {
|
||||
/** The precedence hierarchy, from loosest binding to tightest binding. */
|
||||
val hierarchy = List(
|
||||
List("=", "#="),
|
||||
List(";"),
|
||||
List(":="),
|
||||
List(":"),
|
||||
List("->", "<-"),
|
||||
List("~>", "<~"),
|
||||
List("!"),
|
||||
List("in"),
|
||||
List("<:", "~"),
|
||||
List("|"),
|
||||
List("&"),
|
||||
List("!", "?", "~"),
|
||||
List("\\"),
|
||||
List("?"),
|
||||
List("<*", "<*>", "*>", "<$", "<$>", "$>", "<+", "<+>", "+>"),
|
||||
List("<", ">"),
|
||||
List(":", ","),
|
||||
List(","),
|
||||
List("+", "-"),
|
||||
List("*", "/", "\\", "%"),
|
||||
List("*", "/", "%"),
|
||||
List("^"),
|
||||
List("."),
|
||||
List(" ")
|
||||
|
@ -206,7 +206,7 @@ case class ParserDef() extends flexer.Parser[AST.Module] {
|
||||
ident.current = Some(opr)
|
||||
}
|
||||
|
||||
val char: Pattern = anyOf("!$%&*+-/<>?^~|:\\")
|
||||
val char: Pattern = anyOf(";!$%&*+-/<>?^~|:\\")
|
||||
val errChar: Pattern = char | "=" | "," | "."
|
||||
val errSfx: Pattern = errChar.many1
|
||||
val body: Pattern = char.many1
|
||||
|
@ -0,0 +1,49 @@
|
||||
package org.enso.syntax.text
|
||||
|
||||
import cats.implicits._
|
||||
import org.enso.syntax.text.AST.App
|
||||
|
||||
/** This preprocessor step is responsible for hoisting occurrences of `in` as a
|
||||
* variable identifier to occurrences of an _operator_ identifier, letting it
|
||||
* behave properly in the syntax.
|
||||
*/
|
||||
case object InHoisting {
|
||||
private val inName: String = "in"
|
||||
|
||||
/** Executes the hoisting procedure on the provided token stream.
|
||||
*
|
||||
* @param tokenStream the input token stream
|
||||
* @return `tokenStream` with all occurrences of `Var("in")` replaced with
|
||||
* `Opr("in")`
|
||||
*/
|
||||
def run(tokenStream: AST.Module): AST.Module = {
|
||||
tokenStream.setLines(
|
||||
tokenStream.lines.map(l => l.copy(elem = l.elem.map(process)))
|
||||
)
|
||||
}
|
||||
|
||||
/** Maps over the token stream, replacing occurrences of `Var("in")` with
|
||||
* `Opr("in")` at the same location.
|
||||
*
|
||||
* @param ast the ast element to process
|
||||
* @return `ast`, with occurrences of "in" replaced
|
||||
*/
|
||||
def process(ast: AST): AST = ast match {
|
||||
case App.Prefix.any(app) =>
|
||||
App.Prefix(process(app.func), app.off, process(app.arg))
|
||||
case AST.Ident.Var.any(v) =>
|
||||
if (v.name == inName) {
|
||||
v.copy(
|
||||
shape = Shape.Opr("in")
|
||||
)
|
||||
} else {
|
||||
v
|
||||
}
|
||||
case AST.Block.any(block) =>
|
||||
val newFirstLine = block.firstLine.map(process)
|
||||
val newLines = block.lines.map(_.map(_.map(process)))
|
||||
|
||||
block.replaceFirstLine(newFirstLine).replaceLines(newLines)
|
||||
case n => n.map(process)
|
||||
}
|
||||
}
|
@ -228,7 +228,8 @@ class Parser {
|
||||
|
||||
/** Parse input with provided IdMap into AST */
|
||||
def run(input: Reader, idMap: IDMap): AST.Module = {
|
||||
val tokenStream = engine.run(input)
|
||||
val tokenStream = engine.run(input).map(InHoisting.run)
|
||||
|
||||
val spanned = tokenStream.map(attachModuleLocations)
|
||||
val resolved = spanned.map(Macro.run) match {
|
||||
case flexer.Parser.Result(_, flexer.Parser.Result.Success(mod)) =>
|
||||
|
Loading…
Reference in New Issue
Block a user