From 415cab1a7670f745f94cf3a5b512e0bb2aef3187 Mon Sep 17 00:00:00 2001 From: Martin Ilgner Date: Fri, 10 Apr 2026 14:52:13 +0200 Subject: [PATCH 1/4] Parse Type Annotation --- effekt/shared/src/main/scala/effekt/Parser.scala | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/effekt/shared/src/main/scala/effekt/Parser.scala b/effekt/shared/src/main/scala/effekt/Parser.scala index d73a164ed..1d40f82ba 100644 --- a/effekt/shared/src/main/scala/effekt/Parser.scala +++ b/effekt/shared/src/main/scala/effekt/Parser.scala @@ -1005,14 +1005,13 @@ class Parser(tokens: Seq[Token], source: Source) { nonterminal: peek.kind match { case `__` => skip(); IgnorePattern(span()) - case _ if isVariable => - idRef() match { + case _ if isVariable => idRef() match { case id if peek(`(`) => TagPattern(id, many(matchPattern, `(`, `,`, `)`).unspan, span()) - case IdRef(Nil, name, span) => AnyPattern(IdDef(name, span), span) + case IdRef(Nil, name, span) => + maybeValueTypeAnnotation() + AnyPattern(IdDef(name, span), span) case IdRef(_, name, _) => fail("Cannot use qualified names to bind a pattern variable") } - case _ if isVariable => - AnyPattern(idDef(), span()) case _ if isLiteral => LiteralPattern(literal(), span()) case `(` => some(matchPattern, `(`, `,`, `)`) match { case Many(p :: Nil , _) => fail("Pattern matching on tuples requires more than one element") From ae6fde19dd866b8085be533a6fc10c3b5bce6bc7 Mon Sep 17 00:00:00 2001 From: Martin Ilgner Date: Fri, 17 Apr 2026 08:48:17 +0200 Subject: [PATCH 2/4] Add pos test --- .../patternmatching/pattern-annotations.check | 14 ++++++++ .../pattern-annotations.effekt | 34 +++++++++++++++++++ 2 files changed, 48 insertions(+) create mode 100644 examples/pos/patternmatching/pattern-annotations.check create mode 100644 examples/pos/patternmatching/pattern-annotations.effekt diff --git a/examples/pos/patternmatching/pattern-annotations.check b/examples/pos/patternmatching/pattern-annotations.check new file mode 100644 index 000000000..b294057ce --- /dev/null +++ b/examples/pos/patternmatching/pattern-annotations.check @@ -0,0 +1,14 @@ +1 +hello +--- +() +--- +0 +1 +2 +--- +test +world +--- +true +false diff --git a/examples/pos/patternmatching/pattern-annotations.effekt b/examples/pos/patternmatching/pattern-annotations.effekt new file mode 100644 index 000000000..0c0930383 --- /dev/null +++ b/examples/pos/patternmatching/pattern-annotations.effekt @@ -0,0 +1,34 @@ +def ex1(lst: List[Int]) = lst match { + case Cons(x: Int, y) => x.show + case _ => "hello" +} + +def f { g: ((Int, String)) => Unit }: Unit = g((0, "")) + +def ex2() = f { case (x: Int, y) => () } + +def ex3() = for[(Int, String)] { [(0, "a"), (1, "b"), (2, "c")].each } { case (i: Int, s: String) => + println(i.show) +} + +def ex4(opt: Option[String]) = if (opt is Some(x: String)) x else "world" + +def ex5(opt: Option[Bool]) = { + val Some(x: Bool) = opt else false + x +} + +def main() = { + println(ex1([1, 2])) + println(ex1([])) + println("---") + println(ex2()) + println("---") + ex3() + println("---") + println(ex4(Some("test"))) + println(ex4(None())) + println("---") + println(ex5(Some(true))) + println(ex5(None())) +} \ No newline at end of file From b1db30b9d15624b76274dda72460966fbe4fcd0b Mon Sep 17 00:00:00 2001 From: Martin Ilgner Date: Mon, 20 Apr 2026 16:06:56 +0200 Subject: [PATCH 3/4] Add problematic example --- examples/pos/patternmatching/pattern-annotations.check | 3 +++ examples/pos/patternmatching/pattern-annotations.effekt | 8 ++++++++ 2 files changed, 11 insertions(+) diff --git a/examples/pos/patternmatching/pattern-annotations.check b/examples/pos/patternmatching/pattern-annotations.check index b294057ce..008fde02f 100644 --- a/examples/pos/patternmatching/pattern-annotations.check +++ b/examples/pos/patternmatching/pattern-annotations.check @@ -12,3 +12,6 @@ world --- true false +--- +world +test diff --git a/examples/pos/patternmatching/pattern-annotations.effekt b/examples/pos/patternmatching/pattern-annotations.effekt index 0c0930383..2103a2026 100644 --- a/examples/pos/patternmatching/pattern-annotations.effekt +++ b/examples/pos/patternmatching/pattern-annotations.effekt @@ -18,6 +18,11 @@ def ex5(opt: Option[Bool]) = { x } +def ex6(str: String) = str match { + case "hello" => "world" + case s: String => s +} + def main() = { println(ex1([1, 2])) println(ex1([])) @@ -31,4 +36,7 @@ def main() = { println("---") println(ex5(Some(true))) println(ex5(None())) + println("---") + println(ex6("hello")) + println(ex6("test")) } \ No newline at end of file From 6074bb46256a64851d0e6a462fa793d1d7a768a5 Mon Sep 17 00:00:00 2001 From: Martin Ilgner Date: Sun, 26 Apr 2026 10:25:26 +0200 Subject: [PATCH 4/4] Use atomic type annotation to fix problems --- effekt/shared/src/main/scala/effekt/Parser.scala | 10 +++++++++- effekt/shared/src/main/scala/effekt/source/Tree.scala | 1 + examples/pos/patternmatching/pattern-annotations.check | 2 ++ .../pos/patternmatching/pattern-annotations.effekt | 6 ++++++ 4 files changed, 18 insertions(+), 1 deletion(-) diff --git a/effekt/shared/src/main/scala/effekt/Parser.scala b/effekt/shared/src/main/scala/effekt/Parser.scala index 1d40f82ba..33362c124 100644 --- a/effekt/shared/src/main/scala/effekt/Parser.scala +++ b/effekt/shared/src/main/scala/effekt/Parser.scala @@ -836,6 +836,10 @@ class Parser(tokens: Seq[Token], source: Source) { nonterminal: when(`at`) { Maybe.Some(captureSet(), span()) } { Maybe.None(span()) } + def maybeAtomicTypeAnnotation(): Option[AtomicType] = + nonterminal: + if peek(`:`) then Some(atomicTypeAnnotation()) else None + def maybeValueTypeAnnotation(): Option[ValueType] = nonterminal: if peek(`:`) then Some(valueTypeAnnotation()) else None @@ -852,6 +856,10 @@ class Parser(tokens: Seq[Token], source: Source) { if peek(`:`) then `:` ~> effectful() else fail("return type annotation", peek.kind) + def atomicTypeAnnotation(): AtomicType = + if peek(`:`) then `:` ~> atomicType() + else fail("a type annotation", peek.kind) + def valueTypeAnnotation(): ValueType = if peek(`:`) then `:` ~> valueType() else fail("a type annotation", peek.kind) @@ -1008,7 +1016,7 @@ class Parser(tokens: Seq[Token], source: Source) { case _ if isVariable => idRef() match { case id if peek(`(`) => TagPattern(id, many(matchPattern, `(`, `,`, `)`).unspan, span()) case IdRef(Nil, name, span) => - maybeValueTypeAnnotation() + maybeAtomicTypeAnnotation() AnyPattern(IdDef(name, span), span) case IdRef(_, name, _) => fail("Cannot use qualified names to bind a pattern variable") } diff --git a/effekt/shared/src/main/scala/effekt/source/Tree.scala b/effekt/shared/src/main/scala/effekt/source/Tree.scala index 45486e0aa..3c26ff8f4 100644 --- a/effekt/shared/src/main/scala/effekt/source/Tree.scala +++ b/effekt/shared/src/main/scala/effekt/source/Tree.scala @@ -741,6 +741,7 @@ case class Effectful(tpe: ValueType, eff: Effects, span: Span) extends Type // These are just type aliases for documentation purposes. type BlockType = Type type ValueType = Type +type AtomicType = Type /** * Represents an annotated set of effects. Before name resolution, we cannot know diff --git a/examples/pos/patternmatching/pattern-annotations.check b/examples/pos/patternmatching/pattern-annotations.check index 008fde02f..28fbb899d 100644 --- a/examples/pos/patternmatching/pattern-annotations.check +++ b/examples/pos/patternmatching/pattern-annotations.check @@ -15,3 +15,5 @@ false --- world test +--- +42 diff --git a/examples/pos/patternmatching/pattern-annotations.effekt b/examples/pos/patternmatching/pattern-annotations.effekt index 2103a2026..352422cad 100644 --- a/examples/pos/patternmatching/pattern-annotations.effekt +++ b/examples/pos/patternmatching/pattern-annotations.effekt @@ -23,6 +23,10 @@ def ex6(str: String) = str match { case s: String => s } +def ex7(f: Int => Int at {}) = f match { + case f: (Int => Int at {}) => f(41).show +} + def main() = { println(ex1([1, 2])) println(ex1([])) @@ -39,4 +43,6 @@ def main() = { println("---") println(ex6("hello")) println(ex6("test")) + println("---") + println(ex7(box { x => x + 1 })) } \ No newline at end of file