Skip to content

Implementing an abstract var with a val is unsound but compiles #23474

Closed
@katrinafyi

Description

@katrinafyi

Compiler version

3.7.0 Next and 3.3.6 LTS

Minimized code

Implementing an abstract var with a val is unsound but compiles. It is unsound because a non-abstract class should be required by the type-checker to implement all abstract methods. The mutable variable requires a setter definition, but implementing it with a constant val does not define a setter.

scastie for code below: https://scastie.scala-lang.org/qh1Vo7vZSmSCBJ84kcyoTw

trait Comment {
  var comment: String
}

final case class Y(val comment: String) extends Comment {}

object Main {
  def main(args: Array[String]): Unit = {
    val y = Y("foo")
    println(y)
    println(y.comment)
    println("-----")
    
    y.comment = "Bar" // AbstractMethodError
    println(y)
    println(y.comment)
    println("-----")
  }
}

this alternate definition of Y will also compile and throw the same error at runtime:

final class Y extends Comment {
  def comment: String = "a"
}

Output

java.lang.AbstractMethodError: Method Y.comment_$eq(Ljava/lang/String;)V is abstract
	at Y.comment_$eq(main.scala)
	at Main$.main(main.scala:15)
	at Main.main(main.scala)

Expectation

Implementing an abstract var with a val is not a sound thing to do, so this should lead to a compile-time error.

c.f. this code which correctly returns a compile-time error. i would expect the first code example to have a similar error.

trait Comment {
  def comment: String
  def comment_=(x: String): Unit
}

final case class Y(val comment: String) extends Comment {}
class Y needs to be abstract, since def comment_=(x: String): Unit in trait Comment is not defined 

This also reveals a difference in behaviour between a var and separately-defined getter and setter methods, in contravention of 4.2 Variable Definitions.

Related issues

It is important to note that this issue is about implementing an abstract var, not overriding, so it should not be confused with the many bug reports about overriding.

Regarding var/val/def implementations of abstract members, there could be a number of cases to explore here. Namely, var vs val vs def getter vs def getter+setter and each of these 4 cases can be in the trait or the implementation, leading to 16 cases in total.

Metadata

Metadata

Assignees

Labels

itype:bugstat:needs triageEvery issue needs to have an "area" and "itype" label

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions