Skip to content
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

Add supports for type cast and filtering type for field and method owner in global initialization checker #19612

Merged
merged 8 commits into from
Mar 8, 2024
Merged
Show file tree
Hide file tree
Changes from 2 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
29 changes: 23 additions & 6 deletions compiler/src/dotty/tools/dotc/transform/init/Objects.scala
Original file line number Diff line number Diff line change
Expand Up @@ -599,6 +599,22 @@ object Objects:

case _ => a

def filterType(tpe: Type)(using Context): Value =
val baseClasses = tpe.baseClasses
if baseClasses.isEmpty then a
else filterClass(baseClasses.head)

def filterClass(sym: Symbol)(using Context): Value =
if !sym.isClass then a
else
val klass = sym.asClass
a match
case Cold => Cold
case ref: Ref if ref.klass.isSubClass(klass) => ref
Copy link
Contributor

Choose a reason for hiding this comment

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

For consistency with other cases, I'd move the if inside the case body and combine with the following case.

case ref: Ref => Bottom
case ValueSet(values) => values.map(v => v.filterClass(klass)).join
case _ => a // TODO: could be more precise for OfArray; possibly add class information for Fun
Copy link
Contributor

Choose a reason for hiding this comment

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

As discussed in the meeting, we can further filter OfArray here. Meanwhile, we can remove the old code that partially handles this.


extension (value: Ref | Cold.type)
def widenRefOrCold(height : Int)(using Context) : Ref | Cold.type = value.widen(height).asInstanceOf[ThisValue]

Expand All @@ -617,7 +633,7 @@ object Objects:
* @param needResolve Whether the target of the call needs resolution?
*/
def call(value: Value, meth: Symbol, args: List[ArgInfo], receiver: Type, superType: Type, needResolve: Boolean = true): Contextual[Value] = log("call " + meth.show + ", this = " + value.show + ", args = " + args.map(_.value.show), printer, (_: Value).show) {
value match
value.filterClass(meth.owner) match
case Cold =>
report.warning("Using cold alias. " + Trace.show, Trace.position)
Bottom
Expand Down Expand Up @@ -733,7 +749,6 @@ object Objects:
* @param args Arguments of the constructor call (all parameter blocks flatten to a list).
*/
def callConstructor(value: Value, ctor: Symbol, args: List[ArgInfo]): Contextual[Value] = log("call " + ctor.show + ", args = " + args.map(_.value.show), printer, (_: Value).show) {

value match
case ref: Ref =>
if ctor.hasSource then
Expand Down Expand Up @@ -768,7 +783,7 @@ object Objects:
* @param needResolve Whether the target of the selection needs resolution?
*/
def select(value: Value, field: Symbol, receiver: Type, needResolve: Boolean = true): Contextual[Value] = log("select " + field.show + ", this = " + value.show, printer, (_: Value).show) {
value match
value.filterClass(field.owner) match
case Cold =>
report.warning("Using cold alias", Trace.position)
Bottom
Expand Down Expand Up @@ -839,7 +854,7 @@ object Objects:
* @param rhsTyp The type of the right-hand side.
*/
def assign(lhs: Value, field: Symbol, rhs: Value, rhsTyp: Type): Contextual[Value] = log("Assign" + field.show + " of " + lhs.show + ", rhs = " + rhs.show, printer, (_: Value).show) {
lhs match
lhs.filterClass(field.owner) match
case fun: Fun =>
report.warning("[Internal error] unexpected tree in assignment, fun = " + fun.code.show + Trace.show, Trace.position)

Expand Down Expand Up @@ -876,8 +891,7 @@ object Objects:
* @param args The arguments passsed to the constructor.
*/
def instantiate(outer: Value, klass: ClassSymbol, ctor: Symbol, args: List[ArgInfo]): Contextual[Value] = log("instantiating " + klass.show + ", outer = " + outer + ", args = " + args.map(_.value.show), printer, (_: Value).show) {
outer match

outer.filterClass(klass.owner) match
case _ : Fun | _: OfArray =>
report.warning("[Internal error] unexpected outer in instantiating a class, outer = " + outer.show + ", class = " + klass.show + ", " + Trace.show, Trace.position)
Bottom
Expand Down Expand Up @@ -1091,6 +1105,9 @@ object Objects:
instantiate(outer, cls, ctor, args)
}

case TypeCast(elem, tpe) =>
eval(elem, thisV, klass).filterType(tpe)

case Apply(ref, arg :: Nil) if ref.symbol == defn.InitRegionMethod =>
val regions2 = Regions.extend(expr.sourcePos)
if Regions.exists(expr.sourcePos) then
Expand Down
7 changes: 7 additions & 0 deletions compiler/src/dotty/tools/dotc/transform/init/Util.scala
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,13 @@ object Util:
case _ =>
None

object TypeCast:
def unapply(tree: Tree)(using Context): Option[(Tree, Type)] =
tree match
case TypeApply(Select(qual, _), typeArg) if tree.symbol.isTypeCast =>
Some(qual, typeArg.head.tpe)
Copy link
Contributor

Choose a reason for hiding this comment

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

Minor: It's better to changetypeArg to typeArgs.

case _ => None

def resolve(cls: ClassSymbol, sym: Symbol)(using Context): Symbol = log("resove " + cls + ", " + sym, printer, (_: Symbol).show):
if sym.isEffectivelyFinal then sym
else sym.matchingMember(cls.appliedRef)
Expand Down
18 changes: 18 additions & 0 deletions tests/init-global/neg/TypeCast.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
object A {
val f: Int = 10
def m() = f
}
object B {
val f: Int = g()
def g(): Int = f // error
}
object C {
val a: A.type | B.type = if ??? then A else B
def cast[T](a: Any): T = a.asInstanceOf[T]
val c: A.type = cast[A.type](a) // abstraction for c is {A, B}
val d = c.f // treat as c.asInstanceOf[owner of f].f
val e = c.m() // treat as c.asInstanceOf[owner of f].m()
val c2: B.type = cast[B.type](a)
val g = c2.f // no error here
}

15 changes: 15 additions & 0 deletions tests/init-global/pos/i18882.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
class A:
var a = 20

class B:
var b = 20

object O:
val o: A | B = new A
if o.isInstanceOf[A] then
o.asInstanceOf[A].a += 1
else
o.asInstanceOf[B].b += 1 // o.asInstanceOf[B] is treated as bottom
o match
case o: A => o.a += 1
case o: B => o.b += 1
Loading