diff --git a/effekt/jvm/src/test/scala/effekt/LSPTests.scala b/effekt/jvm/src/test/scala/effekt/LSPTests.scala index 3af24b596..142042ece 100644 --- a/effekt/jvm/src/test/scala/effekt/LSPTests.scala +++ b/effekt/jvm/src/test/scala/effekt/LSPTests.scala @@ -1957,10 +1957,11 @@ class LSPTests extends FunSuite { val outerBindings = hole.scope.outer.get.bindings - assertEquals(outerBindings.length, 3) - assertEquals(outerBindings(0).name, "e1") - assertEquals(outerBindings(1).name, "foo") - assertEquals(outerBindings(2).name, "e2") + assertEquals(outerBindings.length, 4) + assertEquals(outerBindings(0).name, "e1") // the type `effect e1(): Int / {}` + assertEquals(outerBindings(1).name, "e1") // the operation term + assertEquals(outerBindings(2).name, "foo") + assertEquals(outerBindings(3).name, "e2") // only the type since, but why? } } @@ -2137,6 +2138,7 @@ class LSPTests extends FunSuite { val hole = receivedHoles.head.holes.head val outerScope = hole.scope.outer.get + assertEquals(outerScope.bindings.length, expectedBindings.length) assertEquals(outerScope.bindings, expectedBindings) } } diff --git a/effekt/shared/src/main/scala/effekt/Namer.scala b/effekt/shared/src/main/scala/effekt/Namer.scala index 55b58377c..b000d9dce 100644 --- a/effekt/shared/src/main/scala/effekt/Namer.scala +++ b/effekt/shared/src/main/scala/effekt/Namer.scala @@ -220,7 +220,7 @@ object Namer extends Phase[Parsed, NameResolved] { ExternInterface(Context.nameFor(id), tps, decl) }) - case d @ source.ExternDef(id, tparams, vparams, bparams, captures, ret, bodies, doc, span) => { + case d @ source.ExternDef(id, tparams, vparams, bparams, captures, ret, bodies, doc, span) => val name = Context.nameFor(id) val capt = resolve(captures) Context.define(id, Context scoped { @@ -235,7 +235,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) @@ -348,14 +347,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) } @@ -372,10 +371,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 } } @@ -385,19 +390,26 @@ 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 { + val constructors = ctors map { case c @ source.Constructor(id, tparams, ps, doc, span) => val constructor = Context scoped { val name = Context.nameFor(id) val tps = tparams map resolve Constructor(name, data.tparams ++ tps.unspan, Nil, data, c) } - Context.define(id, constructor) + // DataType::Constructor() + Context.namespace(typeId.name) { + Context.define(id, constructor) + } constructor.fields = resolveFields(ps.unspan, constructor, false) constructor } + // export DataType::{Constructor1, ...} + constructors.foreach { c => Context.bind(c) } + + data.constructors = constructors // The record has been resolved as part of the preresolution step case d @ source.RecordDef(id, tparams, fs, doc, span) => @@ -526,8 +538,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 @@ -1136,15 +1148,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}") diff --git a/effekt/shared/src/main/scala/effekt/Parser.scala b/effekt/shared/src/main/scala/effekt/Parser.scala index daa9e3541..eafd39147 100644 --- a/effekt/shared/src/main/scala/effekt/Parser.scala +++ b/effekt/shared/src/main/scala/effekt/Parser.scala @@ -300,8 +300,8 @@ class Parser(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) } @@ -830,7 +830,7 @@ class Parser(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()) } /* @@ -1290,7 +1290,6 @@ class Parser(tokens: Seq[Token], source: Source) { val target = id.getOrElse(IdRef(Nil, "s", id.span.synthesized)) val doLits = strs.map { s => Do( - None, IdRef(Nil, "literal", s.span.synthesized), Nil, List(ValueArg.Unnamed(StringLit(s.unspan, s.span))), @@ -1300,7 +1299,6 @@ class Parser(tokens: Seq[Token], source: Source) { } val doSplices = args.map { arg => Do( - None, IdRef(Nil, "splice", arg.span.synthesized), Nil, List(ValueArg.Unnamed(arg.unspan)), diff --git a/effekt/shared/src/main/scala/effekt/Typer.scala b/effekt/shared/src/main/scala/effekt/Typer.scala index 9e839084e..fd1ed591c 100644 --- a/effekt/shared/src/main/scala/effekt/Typer.scala +++ b/effekt/shared/src/main/scala/effekt/Typer.scala @@ -187,7 +187,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 diff --git a/effekt/shared/src/main/scala/effekt/core/Transformer.scala b/effekt/shared/src/main/scala/effekt/core/Transformer.scala index 311685e26..f56064b43 100644 --- a/effekt/shared/src/main/scala/effekt/core/Transformer.scala +++ b/effekt/shared/src/main/scala/effekt/core/Transformer.scala @@ -508,7 +508,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.") } diff --git a/effekt/shared/src/main/scala/effekt/source/AnnotateCaptures.scala b/effekt/shared/src/main/scala/effekt/source/AnnotateCaptures.scala index f138221c4..5b0bddba5 100644 --- a/effekt/shared/src/main/scala/effekt/source/AnnotateCaptures.scala +++ b/effekt/shared/src/main/scala/effekt/source/AnnotateCaptures.scala @@ -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) diff --git a/effekt/shared/src/main/scala/effekt/source/ExplicitCapabilities.scala b/effekt/shared/src/main/scala/effekt/source/ExplicitCapabilities.scala index c37b09f0a..91a4ce270 100644 --- a/effekt/shared/src/main/scala/effekt/source/ExplicitCapabilities.scala +++ b/effekt/shared/src/main/scala/effekt/source/ExplicitCapabilities.scala @@ -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) @@ -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 = @@ -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 } diff --git a/effekt/shared/src/main/scala/effekt/source/Tree.scala b/effekt/shared/src/main/scala/effekt/source/Tree.scala index 627e6ebe2..1d637e778 100644 --- a/effekt/shared/src/main/scala/effekt/source/Tree.scala +++ b/effekt/shared/src/main/scala/effekt/source/Tree.scala @@ -524,11 +524,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., `(box { () => ... })()`; or a named function, i.e., `foo()` diff --git a/effekt/shared/src/main/scala/effekt/symbols/Scope.scala b/effekt/shared/src/main/scala/effekt/symbols/Scope.scala index ec47ada3f..d5bf15645 100644 --- a/effekt/shared/src/main/scala/effekt/symbols/Scope.scala +++ b/effekt/shared/src/main/scala/effekt/symbols/Scope.scala @@ -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 { @@ -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]] = diff --git a/effekt/shared/src/main/scala/effekt/typer/UnboxInference.scala b/effekt/shared/src/main/scala/effekt/typer/UnboxInference.scala index 27b060f03..0fad7093b 100644 --- a/effekt/shared/src/main/scala/effekt/typer/UnboxInference.scala +++ b/effekt/shared/src/main/scala/effekt/typer/UnboxInference.scala @@ -85,8 +85,8 @@ object UnboxInference 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) diff --git a/effekt/shared/src/main/scala/effekt/typer/Wellformedness.scala b/effekt/shared/src/main/scala/effekt/typer/Wellformedness.scala index 0b63d5a69..100c27f0d 100644 --- a/effekt/shared/src/main/scala/effekt/typer/Wellformedness.scala +++ b/effekt/shared/src/main/scala/effekt/typer/Wellformedness.scala @@ -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") diff --git a/examples/pos/namespaced_constructors.check b/examples/pos/namespaced_constructors.check new file mode 100644 index 000000000..d00491fd7 --- /dev/null +++ b/examples/pos/namespaced_constructors.check @@ -0,0 +1 @@ +1 diff --git a/examples/pos/namespaced_constructors.effekt b/examples/pos/namespaced_constructors.effekt new file mode 100644 index 000000000..47c62c815 --- /dev/null +++ b/examples/pos/namespaced_constructors.effekt @@ -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 +} + +def main() = { + println(consume(Bars::Foo(Foo::Bar()))) +} \ No newline at end of file diff --git a/examples/pos/namespaced_operations.check b/examples/pos/namespaced_operations.check new file mode 100644 index 000000000..a5c880627 --- /dev/null +++ b/examples/pos/namespaced_operations.check @@ -0,0 +1,2 @@ +3 +3 diff --git a/examples/pos/namespaced_operations.effekt b/examples/pos/namespaced_operations.effekt new file mode 100644 index 000000000..36af2be4f --- /dev/null +++ b/examples/pos/namespaced_operations.effekt @@ -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) } \ No newline at end of file