Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions effekt/jvm/src/test/scala/effekt/LSPTests.scala
Original file line number Diff line number Diff line change
Expand Up @@ -1883,6 +1883,15 @@ class LSPTests extends FunSuite {
),
kind = "Term"
),
TermBinding(
qualifier = List("Bar"),
name = "Bar",
origin = "Defined",
`type` = Some(
value = "Int => Bar"
),
kind = "Term"
),
TermBinding(
qualifier = Nil,
name = "main",
Expand Down
45 changes: 25 additions & 20 deletions effekt/shared/src/main/scala/effekt/Namer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -219,7 +219,7 @@ object Namer extends Phase[Parsed, NameResolved] {
ExternInterface(Context.nameFor(id), tps, decl)
})

case d @ source.ExternDef(capture, id, tparams, vparams, bparams, ret, bodies, doc, span) => {
case d @ source.ExternDef(capture, id, tparams, vparams, bparams, ret, bodies, doc, span) =>
val name = Context.nameFor(id)
val capt = resolve(capture)
Context.define(id, Context scoped {
Expand All @@ -234,7 +234,6 @@ object Namer extends Phase[Parsed, NameResolved] {

ExternFunction(name, tps.unspan, vps.unspan, bps.unspan, tpe, eff, capt, bodies, d)
})
}

case d @ source.ExternResource(id, tpe, doc, span) =>
val name = Context.nameFor(id)
Expand Down Expand Up @@ -346,14 +345,14 @@ object Namer extends Phase[Parsed, NameResolved] {
}
}

case source.InterfaceDef(id, tparams, operations, doc, span) =>
case source.InterfaceDef(interfaceId, tparams, operations, doc, span) =>
// symbol has already been introduced by the previous traversal
val interface = Context.symbolOf(id).asInterface
val interface = Context.symbolOf(interfaceId).asInterface
interface.operations = operations.map {
case op @ source.Operation(id, tparams, vparams, bparams, ret, doc, span) => Context.at(op) {
val name = Context.nameFor(id)

Context.scopedWithName(id.name) {
val opSym = Context.scopedWithName(id.name) {
// the parameters of the interface are in scope
interface.tparams.foreach { p => Context.bind(p) }

Expand All @@ -370,10 +369,16 @@ object Namer extends Phase[Parsed, NameResolved] {
// 2) the annotated type parameters on the concrete operation
val (result, effects) = resolve(ret)

val opSym = Operation(name, interface.tparams ++ tps.unspan, resVparams, resBparams, result, effects, interface, op)
Operation(name, interface.tparams ++ tps.unspan, resVparams, resBparams, result, effects, interface, op)
}

// define in namespace ...
Context.namespace(interfaceId.name) {
Context.define(id, opSym)
opSym
}
// ... and bind outside
Context.bind(opSym)
opSym
}
}

Expand All @@ -383,7 +388,7 @@ object Namer extends Phase[Parsed, NameResolved] {
}

// The type itself has already been resolved, now resolve constructors
case d @ source.DataDef(id, tparams, ctors, doc, span) =>
case d @ source.DataDef(typeId, tparams, ctors, doc, span) =>
val data = d.symbol
data.constructors = ctors map {
case c @ source.Constructor(id, tparams, ps, doc, span) =>
Expand All @@ -392,7 +397,13 @@ object Namer extends Phase[Parsed, NameResolved] {
val tps = tparams map resolve
Constructor(name, data.tparams ++ tps.unspan, Nil, data, c)
}
Context.define(id, constructor)
// define in namespace ...
Context.namespace(typeId.name) {
Context.define(id, constructor)
}
// ... and bind outside
Context.bind(constructor)

constructor.fields = resolveFields(ps.unspan, constructor, false)
constructor
}
Expand Down Expand Up @@ -528,8 +539,8 @@ object Namer extends Phase[Parsed, NameResolved] {
vargs foreach resolve
bargs foreach resolve

case source.Do(effect, target, targs, vargs, bargs, _) =>
Context.resolveEffectCall(effect map resolveBlockRef, target)
case source.Do(target, targs, vargs, bargs, _) =>
Context.resolveEffectCall(target)
targs foreach resolveValueType
vargs foreach resolve
bargs foreach resolve
Expand Down Expand Up @@ -1138,15 +1149,9 @@ trait NamerOps extends ContextOps { Context: Context =>
/**
* Resolves a potentially overloaded call to an effect
*/
private[namer] def resolveEffectCall(eff: Option[InterfaceType], id: IdRef): Unit = at(id) {

val syms = eff match {
case Some(tpe) =>
val interface = tpe.typeConstructor.asInterface
val operations = interface.operations.filter { op => op.name.name == id.name }
if (operations.isEmpty) Nil else List(operations.toSet)
case None => scope.lookupOperation(id.path, id.name)
}
private[namer] def resolveEffectCall(id: IdRef): Unit = at(id) {

val syms = scope.lookupOperation(id.path, id.name)

if (syms.isEmpty) {
abort(pretty"Cannot resolve effect operation ${id}")
Expand Down
10 changes: 5 additions & 5 deletions effekt/shared/src/main/scala/effekt/Parser.scala
Original file line number Diff line number Diff line change
Expand Up @@ -292,8 +292,8 @@ class Parser(positions: Positions, tokens: Seq[Token], source: Source) {
case Var(id, varSpan) =>
val tgt = IdTarget(id)
Return(Call(tgt, Nil, Nil, (BlockLiteral(Nil, vparams, bparams, body, body.span.synthesized)) :: Nil, varSpan), withSpan.synthesized)
case Do(effect, id, targs, vargs, bargs, doSpan) =>
Return(Do(effect, id, targs, vargs, bargs :+ BlockLiteral(Nil, vparams, bparams, body, body.span.synthesized), doSpan), withSpan.synthesized)
case Do(id, targs, vargs, bargs, doSpan) =>
Return(Do(id, targs, vargs, bargs :+ BlockLiteral(Nil, vparams, bparams, body, body.span.synthesized), doSpan), withSpan.synthesized)
case term =>
Return(Call(ExprTarget(term), Nil, Nil, (BlockLiteral(Nil, vparams, bparams, body, body.span.synthesized)) :: Nil, term.span.synthesized), withSpan.synthesized)
}
Expand Down Expand Up @@ -814,7 +814,7 @@ class Parser(positions: Positions, tokens: Seq[Token], source: Source) {
def doExpr(): Term =
nonterminal:
(`do` ~> idRef()) ~ arguments() match {
case id ~ (targs, vargs, bargs) => Do(None, id, targs, vargs, bargs, span())
case id ~ (targs, vargs, bargs) => Do(id, targs, vargs, bargs, span())
}

/*
Expand Down Expand Up @@ -1271,10 +1271,10 @@ class Parser(positions: Positions, tokens: Seq[Token], source: Source) {
case id ~ Template(strs, args) =>
val target = id.getOrElse(IdRef(Nil, "s", id.span.synthesized))
val doLits = strs.map { s =>
Do(None, IdRef(Nil, "literal", Span.missing(source)), Nil, List(ValueArg.Unnamed(StringLit(s, Span.missing(source)))), Nil, Span.missing(source))
Do(IdRef(Nil, "literal", Span.missing(source)), Nil, List(ValueArg.Unnamed(StringLit(s, Span.missing(source)))), Nil, Span.missing(source))
}
val doSplices = args.map { arg =>
Do(None, IdRef(Nil, "splice", Span.missing(source)), Nil, List(ValueArg.Unnamed(arg)), Nil, Span.missing(source))
Do(IdRef(Nil, "splice", Span.missing(source)), Nil, List(ValueArg.Unnamed(arg)), Nil, Span.missing(source))
}
val body = interleave(doLits, doSplices)
.foldRight(Return(UnitLit(Span.missing(source)), Span.missing(source))) { (term, acc) => ExprStmt(term, acc, Span.missing(source)) }
Expand Down
2 changes: 1 addition & 1 deletion effekt/shared/src/main/scala/effekt/Typer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,7 @@ object Typer extends Phase[NameResolved, Typechecked] {
case c @ source.Select(receiver, field, _) =>
checkOverloadedFunctionCall(c, field, Nil, List(source.ValueArg.Unnamed(receiver)), Nil, expected)

case c @ source.Do(effect, op, targs, vargs, bargs, _) =>
case c @ source.Do(op, targs, vargs, bargs, _) =>
// (1) first check the call
val Result(tpe, effs) = checkOverloadedFunctionCall(c, op, targs map { _.resolveValueType }, vargs, bargs, expected)
// (2) now we need to find a capability as the receiver of this effect operation
Expand Down
2 changes: 1 addition & 1 deletion effekt/shared/src/main/scala/effekt/core/Transformer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -518,7 +518,7 @@ object Transformer extends Phase[Typechecked, CoreTransformed] {
case c @ source.Call(s: source.ExprTarget, targs, vargs, bargs, _) =>
Context.panic("Should not happen. Unbox should have been inferred.")

case source.Do(effect, id, targs, vargs, bargs, _) =>
case source.Do(id, targs, vargs, bargs, _) =>
Context.panic("Should have been translated away (to explicit selection `@CAP.op()`) by capability passing.")
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ object AnnotateCaptures extends Phase[Typechecked, Typechecked], Query[Unit, Cap
}
query(term) ++ capt

case t @ source.Do(effect, op, targs, vargs, bargs, _) =>
case t @ source.Do(op, targs, vargs, bargs, _) =>
val cap = Context.annotation(Annotations.CapabilityReceiver, t)
combineAll(vargs.map(query)) ++ combineAll(bargs.map(query)) ++ CaptureSet(cap.capture)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ object ExplicitCapabilities extends Phase[Typechecked, Typechecked], Rewrite {
override def expr(using Context) = {

// an effect call -- translate to method call on the inferred capability
case c @ Do(effect, id, targs, vargs, bargs, span) =>
case c @ Do(id, targs, vargs, bargs, span) =>
val transformedValueArgs = vargs.map(rewrite)
val transformedBlockArgs = bargs.map(rewrite)

Expand Down Expand Up @@ -130,7 +130,7 @@ object ExplicitCapabilities extends Phase[Typechecked, Typechecked], Rewrite {
source.BlockLiteral(tps, vps, bps ++ capParams, rewrite(body), span)
}

override def rewrite(body: ExternBody)(using context.Context): ExternBody =
override def rewrite(body: ExternBody)(using context.Context): ExternBody =
body match {
case b @ source.ExternBody.StringExternBody(ff, body, span) =>
val rewrittenTemplate =
Expand All @@ -139,7 +139,7 @@ object ExplicitCapabilities extends Phase[Typechecked, Typechecked], Rewrite {
)
b.copy(template = rewrittenTemplate)
case b @ source.ExternBody.EffektExternBody(ff, body, span) =>
val rewrittenBody = rewrite(body)
val rewrittenBody = rewrite(body)
b.copy(body = rewrittenBody)
case u: source.ExternBody.Unsupported => u
}
Expand Down
5 changes: 1 addition & 4 deletions effekt/shared/src/main/scala/effekt/source/Tree.scala
Original file line number Diff line number Diff line change
Expand Up @@ -499,11 +499,8 @@ enum Term extends Tree {

/**
* A call to an effect operation, i.e., `do raise()`.
*
* The [[effect]] is the optionally annotated effect type (not possible in source ATM). In the future, this could
* look like `do Exc.raise()`, or `do[Exc] raise()`, or do[Exc].raise(), or simply Exc.raise() where Exc is a type.
*/
case Do(effect: Option[TypeRef], id: IdRef, targs: List[ValueType], vargs: List[ValueArg], bargs: List[Term], span: Span) extends Term, Reference
case Do(id: IdRef, targs: List[ValueType], vargs: List[ValueArg], bargs: List[Term], span: Span) extends Term, Reference

/**
* A call to either an expression, i.e., `(fun() { ...})()`; or a named function, i.e., `foo()`
Expand Down
11 changes: 3 additions & 8 deletions effekt/shared/src/main/scala/effekt/symbols/Scope.scala
Original file line number Diff line number Diff line change
Expand Up @@ -50,12 +50,6 @@ case class Bindings(

def getNamespace(name: String): Option[Bindings] =
namespaces.get(name)

def operations: Map[String, Set[Operation]] =
types.values.toSet.flatMap {
case BlockTypeConstructor.Interface(_, _, operations, _) => operations.toSet
case _ => Set.empty
}.groupMap(_.name.name)(op => op)
}

object Bindings {
Expand Down Expand Up @@ -228,12 +222,13 @@ object scopes {

def lookupOverloadedMethod(id: IdRef, filter: TermSymbol => Boolean)(using ErrorReporter): List[Set[Operation]] =
all(id.path, scope) { namespace =>
namespace.operations.getOrElse(id.name, Set.empty).filter(filter)
namespace.terms.getOrElse(id.name, Set.empty).collect { case op: Operation if filter(op) => op }
}

// the last element in the path can also be the type of the name.
def lookupOperation(path: List[String], name: String)(using ErrorReporter): List[Set[Operation]] =
all(path, scope) { namespace =>
namespace.operations.getOrElse(name, Set.empty)
namespace.terms.getOrElse(name, Set.empty).collect { case op: Operation => op }
}.filter { namespace => namespace.nonEmpty }

def lookupFunction(path: List[String], name: String)(using ErrorReporter): List[Set[Callable]] =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,8 +79,8 @@ object BoxUnboxInference extends Phase[NameResolved, NameResolved] {
case s @ Select(recv, name, span) =>
C.abort("selection on blocks not supported yet.")

case Do(effect, id, targs, vargs, bargs, span) =>
Do(effect, id, targs, vargs.map(rewriteAsExpr), bargs.map(rewriteAsBlock), span)
case Do(id, targs, vargs, bargs, span) =>
Do(id, targs, vargs.map(rewriteAsExpr), bargs.map(rewriteAsBlock), span)

case Call(fun, targs, vargs, bargs, span) =>
Call(rewrite(fun), targs, vargs.map(rewriteAsExpr), bargs.map(rewriteAsBlock), span)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -212,7 +212,7 @@ object Wellformedness extends Phase[Typechecked, Typechecked], Visit[WFContext]
vargs.foreach(query)
bargs.foreach(query)

case tree @ source.Do(effect, id, targs, vargs, bargs, _) =>
case tree @ source.Do(id, targs, vargs, bargs, _) =>
val inferredTypeArgs = Context.typeArguments(tree)
inferredTypeArgs.zipWithIndex.foreach { case (tpe, index) =>
wellformed(tpe, tree, pp" inferred as ${showPosition(index + 1)} type argument")
Expand Down
1 change: 1 addition & 0 deletions examples/pos/namespaced_constructors.check
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
1
20 changes: 20 additions & 0 deletions examples/pos/namespaced_constructors.effekt
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
type Foo {
Bar()
}

type Boo {
Bar()
}
type Bars {
Foo(f: Foo)
Boo(b: Boo)
}

def consume(b: Bars) = b match {
case Foo(Foo::Bar()) => 1
case Boo(Boo::Bar()) => 2
}
Comment on lines +13 to +16
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here you can see that we now can also disambiguate equally named constructors in patterns.


def main() = {
println(consume(Bars::Foo(Foo::Bar())))
}
2 changes: 2 additions & 0 deletions examples/pos/namespaced_operations.check
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
3
3
21 changes: 21 additions & 0 deletions examples/pos/namespaced_operations.effekt
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
interface Foo {
def bar(): Int
}

interface Boo {
def bar(): Int
}

def useBothEffect() =
println(do Foo::bar() + do Boo::bar())

def useBothExplicit {f: Foo} {b: Boo} =
println(f.Foo::bar() + b.Boo::bar())

def main() =
try {
useBothEffect()
useBothExplicit {f} {b}
}
with f: Foo { def bar() = resume(1) }
with b: Boo { def bar() = resume(2) }
Loading