Skip to content

Add support for var in refinements #19982

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Apr 3, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
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
17 changes: 16 additions & 1 deletion compiler/src/dotty/tools/dotc/ast/Desugar.scala
Original file line number Diff line number Diff line change
Expand Up @@ -1943,12 +1943,27 @@ object desugar {
case AndType(tp1, tp2) => stripToCore(tp1) ::: stripToCore(tp2)
case _ => defn.AnyType :: Nil
}

val refinements1 = Trees.flatten:
refinements.mapConserve {
case tree: ValDef if tree.mods.is(Mutable) =>
val getter =
cpy.DefDef(tree)(name = tree.name, paramss = Nil, tpt = tree.tpt, rhs = tree.rhs)
.withFlags(tree.mods.flags & (AccessFlags | Synthetic))
val setterParam = makeSyntheticParameter(tpt = tree.tpt)
val setter =
cpy.DefDef(tree)(name = tree.name.setterName, paramss = List(List(setterParam)), tpt = untpd.scalaUnit, rhs = EmptyTree)
.withFlags(tree.mods.flags & (AccessFlags | Synthetic))
Thicket(getter, setter)
case tree => tree
}

val parentCores = stripToCore(parent.tpe)
val untpdParent = TypedSplice(parent)
val (classParents, self) =
if (parentCores.length == 1 && (parent.tpe eq parentCores.head)) (untpdParent :: Nil, EmptyValDef)
else (parentCores map TypeTree, ValDef(nme.WILDCARD, untpdParent, EmptyTree))
val impl = Template(emptyConstructor, classParents, Nil, self, refinements)
val impl = Template(emptyConstructor, classParents, Nil, self, refinements1)
TypeDef(tpnme.REFINE_CLASS, impl).withFlags(Trait)
}

Expand Down
6 changes: 2 additions & 4 deletions compiler/src/dotty/tools/dotc/parsing/Parsers.scala
Original file line number Diff line number Diff line change
Expand Up @@ -4380,6 +4380,7 @@ object Parsers {

/** RefineStatSeq ::= RefineStat {semi RefineStat}
* RefineStat ::= ‘val’ VarDef
* | ‘var’ VarDef
* | ‘def’ DefDef
* | ‘type’ {nl} TypeDef
* (in reality we admit class defs and vars and filter them out afterwards in `checkLegal`)
Expand All @@ -4392,10 +4393,7 @@ object Parsers {
syntaxError(msg, tree.span)
Nil
tree match
case tree: ValDef if tree.mods.is(Mutable) =>
fail(em"""refinement cannot be a mutable var.
|You can use an explicit getter ${tree.name} and setter ${tree.name}_= instead""")
case tree: MemberDef if !(tree.mods.flags & ModifierFlags).isEmpty =>
case tree: MemberDef if !(tree.mods.flags & (ModifierFlags &~ Mutable)).isEmpty =>
fail(em"refinement cannot be ${(tree.mods.flags & ModifierFlags).flagStrings().mkString("`", "`, `", "`")}")
case tree: DefDef if tree.termParamss.nestedExists(!_.rhs.isEmpty) =>
fail(em"refinement cannot have default arguments")
Expand Down
1 change: 0 additions & 1 deletion compiler/src/dotty/tools/dotc/typer/Typer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -2211,7 +2211,6 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer
val refineCls = createSymbol(refineClsDef).asClass
val TypeDef(_, impl: Template) = typed(refineClsDef): @unchecked
val refinements1 = impl.body
assert(tree.refinements.hasSameLengthAs(refinements1), i"${tree.refinements}%, % > $refinements1%, %")
val seen = mutable.Set[Symbol]()
for (refinement <- refinements1) { // TODO: get clarity whether we want to enforce these conditions
typr.println(s"adding refinement $refinement")
Expand Down
1 change: 1 addition & 0 deletions docs/_docs/internals/syntax.md
Original file line number Diff line number Diff line change
Expand Up @@ -431,6 +431,7 @@ EndMarkerTag ::= id | ‘if’ | ‘while’ | ‘for’ | ‘match’ |
### Definitions
```ebnf
RefineDcl ::= ‘val’ ValDcl
| ‘var’ ValDcl
Copy link
Contributor

Choose a reason for hiding this comment

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

Same change needed in reference/syntax.md.

| ‘def’ DefDcl
| ‘type’ {nl} TypeDef
ValDcl ::= ids ‘:’ Type
Expand Down
1 change: 1 addition & 0 deletions docs/_docs/reference/syntax.md
Original file line number Diff line number Diff line change
Expand Up @@ -415,6 +415,7 @@ EndMarkerTag ::= id | ‘if’ | ‘while’ | ‘for’ | ‘match’ |
```
RefineDcl ::= ‘val’ ValDcl
| ‘def’ DefDcl
| ‘var’ ValDcl
| ‘type’ {nl} TypeDef
ValDcl ::= ids ‘:’ Type
DefDcl ::= DefSig ‘:’ Type
Expand Down
9 changes: 2 additions & 7 deletions tests/neg/i13703.check
Original file line number Diff line number Diff line change
@@ -1,10 +1,5 @@
-- Error: tests/neg/i13703.scala:3:17 ----------------------------------------------------------------------------------
3 |val f: Foo { var i: Int } = new Foo { var i: Int = 0 } // error
| ^^^^^^^^^^
| refinement cannot be a mutable var.
| You can use an explicit getter i and setter i_= instead
-- [E007] Type Mismatch Error: tests/neg/i13703.scala:5:78 -------------------------------------------------------------
5 |val f2: Foo { val i: Int; def i_=(x: Int): Unit } = new Foo { var i: Int = 0 } // error
-- [E007] Type Mismatch Error: tests/neg/i13703.scala:3:78 -------------------------------------------------------------
3 |val f2: Foo { val i: Int; def i_=(x: Int): Unit } = new Foo { var i: Int = 0 } // error
| ^
| Found: Object with Foo {...}
| Required: Foo{val i: Int; def i_=(x: Int): Unit}
Expand Down
2 changes: 0 additions & 2 deletions tests/neg/i13703.scala
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
trait Foo extends reflect.Selectable

val f: Foo { var i: Int } = new Foo { var i: Int = 0 } // error

val f2: Foo { val i: Int; def i_=(x: Int): Unit } = new Foo { var i: Int = 0 } // error

val f3: Foo { def i: Int; def i_=(x: Int): Unit } = new Foo { var i: Int = 0 } // OK
10 changes: 10 additions & 0 deletions tests/neg/i19809.check
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
-- [E120] Naming Error: tests/neg/i19809.scala:3:6 ---------------------------------------------------------------------
3 | def x_=(x: Int): Unit // error
| ^
| Double definition:
| def x_=(x$1: Int): Unit in trait <refinement> at line 2 and
| def x_=(x: Int): Unit in trait <refinement> at line 3
| have the same type after erasure.
|
| Consider adding a @targetName annotation to one of the conflicting definitions
| for disambiguation.
4 changes: 4 additions & 0 deletions tests/neg/i19809.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
type A = Any {
var x : Int
def x_=(x: Int): Unit // error
}
2 changes: 1 addition & 1 deletion tests/neg/i4496b.scala
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ trait Foo2 { def a: Int }
trait Foo3 { var a: Int }

object TestStructuralVar {
type T0 = {var a: Int} // error
type T0 = {var a: Int}
object TestStructuralVar {
type T = {val a: Int; def a_=(x: Int): Unit}
def upcast1(v: Foo1): T = v // error
Expand Down
3 changes: 1 addition & 2 deletions tests/neg/illegal-refinements.scala
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,5 @@ trait x0 {

type T = String { val x: Int = 1 } // error: illegal refinement
type U = String { def x(): Int = 1 } // error: illegal refinement
type V = String { var x: Int } // error: illegal refinement

type V = String { var x: Int = 1 } // error: illegal refinement
}
2 changes: 1 addition & 1 deletion tests/neg/structural.scala
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ object Test3 {
type A = { def foo(x: Int): Unit; def foo(x: String): Unit } // error: overloaded definition // error: overloaded definition
type B = { val foo: Int; def foo: Int } // error: duplicate foo

type C = { var foo: Int } // error: refinements cannot have vars
type C = { var foo: Int }

trait Entry { type Key; val key: Key }
type D = { def foo(e: Entry, k: e.Key): Unit }
Expand Down
7 changes: 7 additions & 0 deletions tests/pos/i19809.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
type A = Any { var x: Int }

val f: Any { var i: Int } = new AnyRef { var i: Int = 0 }

def Test =
summon[Any { def x: Int; def x_=(x: Int): Unit } <:< Any { var x: Int }]
summon[Any { var x: Int } <:< Any { def x: Int; def x_=(x: Int): Unit }]