Skip to content

Commit

Permalink
Only include accessible base classes in orDominator (#16477)
Browse files Browse the repository at this point in the history
By joining union types we could previously uncover inaccessible base
classes that could lead to access errors at runtime. We now filter out
such classes when computing the orDominator.

Fixes #16474
  • Loading branch information
smarter authored Dec 8, 2022
2 parents ab01e88 + 882bb41 commit 49c73ed
Show file tree
Hide file tree
Showing 9 changed files with 73 additions and 2 deletions.
10 changes: 8 additions & 2 deletions compiler/src/dotty/tools/dotc/core/TypeOps.scala
Original file line number Diff line number Diff line change
Expand Up @@ -209,7 +209,7 @@ object TypeOps:

/** Approximate union type by intersection of its dominators.
* That is, replace a union type Tn | ... | Tn
* by the smallest intersection type of base-class instances of T1,...,Tn.
* by the smallest intersection type of accessible base-class instances of T1,...,Tn.
* Example: Given
*
* trait C[+T]
Expand Down Expand Up @@ -370,8 +370,14 @@ object TypeOps:
}
}

def isAccessible(cls: ClassSymbol) =
if cls.isOneOf(AccessFlags) || cls.privateWithin.exists then
cls.isAccessibleFrom(tp.baseType(cls).normalizedPrefix)
else true

// Step 3: Intersect base classes of both sides
val commonBaseClasses = orBaseClasses(tp)
val commonBaseClasses = orBaseClasses(tp).filterConserve(isAccessible)

val doms = dominators(commonBaseClasses, Nil)
def baseTp(cls: ClassSymbol): Type =
tp.baseType(cls).mapReduceOr(identity)(mergeRefinedOrApplied)
Expand Down
13 changes: 13 additions & 0 deletions tests/pos/i16474.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
object o:

private[o] trait A
trait B
class C extends A, B
class D extends A, B

def test =
def f[T](x: T): T => T = identity
val g = f(if ??? then o.C() else o.D())
g(new o.B{})


6 changes: 6 additions & 0 deletions tests/run/i16474/BaseProvider.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
// BaseProvider.java
package repro.impl;

import repro.*;
abstract class BaseProvider{}

9 changes: 9 additions & 0 deletions tests/run/i16474/Case1.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
// Case1.java
package repro;

import repro.impl.*;

public class Case1 extends Case1Provider implements Encrypter {
public Case1(){}
}

5 changes: 5 additions & 0 deletions tests/run/i16474/Case1Provider.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
// Case1Provider.java
package repro.impl;

public abstract class Case1Provider extends BaseProvider {}

8 changes: 8 additions & 0 deletions tests/run/i16474/Case2.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
// Case2.java
package repro;

import repro.impl.*;

public class Case2 extends Case2Provider implements Encrypter {
public Case2(){}
}
5 changes: 5 additions & 0 deletions tests/run/i16474/Case2Provider.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
// Case2Provider.java
package repro.impl;

public abstract class Case2Provider extends BaseProvider {}

4 changes: 4 additions & 0 deletions tests/run/i16474/Encrypter.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
// Encrypter.java
package repro;
public interface Encrypter{}

15 changes: 15 additions & 0 deletions tests/run/i16474/test.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// scalajs: --skip
// test.scala
import repro.*
import scala.util.Try

def get(arg: Any): Try[Encrypter] = Try {
val x: Any = 1
arg match
case 1 => new Case1()
case 2 => new Case2()
case _ => throw new RuntimeException(s"Unsupported got $arg")
}

@main def Test =
val result = get(null)

0 comments on commit 49c73ed

Please sign in to comment.