diff --git a/compiler/src/dotty/tools/dotc/ast/Desugar.scala b/compiler/src/dotty/tools/dotc/ast/Desugar.scala index 2d99cf201375..9c8b2566ed7e 100644 --- a/compiler/src/dotty/tools/dotc/ast/Desugar.scala +++ b/compiler/src/dotty/tools/dotc/ast/Desugar.scala @@ -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) } diff --git a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala index b1c71f34e2dc..5fdd01a61079 100644 --- a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala +++ b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala @@ -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`) @@ -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") diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index ade395f835ca..2c6c2f19a78a 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -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") diff --git a/docs/_docs/internals/syntax.md b/docs/_docs/internals/syntax.md index 10f068e53c7f..4207a13ea66d 100644 --- a/docs/_docs/internals/syntax.md +++ b/docs/_docs/internals/syntax.md @@ -431,6 +431,7 @@ EndMarkerTag ::= id | ‘if’ | ‘while’ | ‘for’ | ‘match’ | ### Definitions ```ebnf RefineDcl ::= ‘val’ ValDcl + | ‘var’ ValDcl | ‘def’ DefDcl | ‘type’ {nl} TypeDef ValDcl ::= ids ‘:’ Type diff --git a/docs/_docs/reference/syntax.md b/docs/_docs/reference/syntax.md index 1980bc4e0ab2..f8e7ba6a5cbc 100644 --- a/docs/_docs/reference/syntax.md +++ b/docs/_docs/reference/syntax.md @@ -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 diff --git a/tests/neg/i13703.check b/tests/neg/i13703.check index eb782c982295..a02bbdf407f7 100644 --- a/tests/neg/i13703.check +++ b/tests/neg/i13703.check @@ -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} diff --git a/tests/neg/i13703.scala b/tests/neg/i13703.scala index e8e54db8807d..6616b4f2e11c 100644 --- a/tests/neg/i13703.scala +++ b/tests/neg/i13703.scala @@ -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 diff --git a/tests/neg/i19809.check b/tests/neg/i19809.check new file mode 100644 index 000000000000..269eacd18fd2 --- /dev/null +++ b/tests/neg/i19809.check @@ -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 at line 2 and + | def x_=(x: Int): Unit in trait at line 3 + | have the same type after erasure. + | + | Consider adding a @targetName annotation to one of the conflicting definitions + | for disambiguation. diff --git a/tests/neg/i19809.scala b/tests/neg/i19809.scala new file mode 100644 index 000000000000..02eb9b08faf5 --- /dev/null +++ b/tests/neg/i19809.scala @@ -0,0 +1,4 @@ +type A = Any { + var x : Int + def x_=(x: Int): Unit // error +} diff --git a/tests/neg/i4496b.scala b/tests/neg/i4496b.scala index e84c29fd9347..b19d4915474f 100644 --- a/tests/neg/i4496b.scala +++ b/tests/neg/i4496b.scala @@ -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 diff --git a/tests/neg/illegal-refinements.scala b/tests/neg/illegal-refinements.scala index 4a170bc345d8..374d3dca7a84 100644 --- a/tests/neg/illegal-refinements.scala +++ b/tests/neg/illegal-refinements.scala @@ -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 } diff --git a/tests/neg/structural.scala b/tests/neg/structural.scala index de70092c0396..e8fad254a801 100644 --- a/tests/neg/structural.scala +++ b/tests/neg/structural.scala @@ -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 } diff --git a/tests/pos/i19809.scala b/tests/pos/i19809.scala new file mode 100644 index 000000000000..4c1b55d01eeb --- /dev/null +++ b/tests/pos/i19809.scala @@ -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 }]