Skip to content

Commit 6a65432

Browse files
committed
fix opaque widening and added more precise ref checks
1 parent ae96b4c commit 6a65432

11 files changed

+163
-72
lines changed

compiler/src/dotty/tools/dotc/ast/Desugar.scala

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -256,14 +256,15 @@ object desugar {
256256
evidenceParamBuf.toList)
257257
end elimContextBounds
258258

259+
//TODO: typing causes problems, so using this hack ATM that cannot handle all cases of applying @precise
259260
def isPreciseAnnot(tree: untpd.Tree)(using Context): Boolean =
260261
tree match
261-
case Apply(Select(New(clsSel), _), Nil) =>
262-
inContext(ctx.fresh.setReporter(Reporter.NoReporter).setExploreTyperState()) {
263-
try
264-
ctx.typer.typedExpr(clsSel).tpe.classSymbol == defn.PreciseAnnot
265-
catch case _ : Throwable => false
266-
}
262+
case Apply(Select(New(Ident(n)), _), Nil) => n == defn.PreciseAnnot.name
263+
// inContext(ctx.fresh.setReporter(Reporter.NoReporter).setExploreTyperState()) {
264+
// try
265+
// ctx.typer.typedExpr(clsSel).tpe.classSymbol == defn.PreciseAnnot
266+
// catch case _ : Throwable => false
267+
// }
267268
case _ => false
268269

269270
def addDefaultGetters(meth: DefDef)(using Context): Tree =

compiler/src/dotty/tools/dotc/core/TyperState.scala

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -96,10 +96,18 @@ class TyperState() {
9696

9797
private var upLevels: LevelMap = _
9898

99+
// Stack can be empty and precise conversion occur in `val x : Foo = from`
100+
// where `from` is implicitly and precisely converted into `Foo`. We don't
101+
// care about these conversions.
99102
private var myPreciseConvStack: List[Set[Tree]] = _
100-
def hasPreciseConversion(tree: Tree): Boolean = myPreciseConvStack.head.contains(tree)
103+
def hasPreciseConversion(tree: Tree): Boolean =
104+
myPreciseConvStack match
105+
case head :: _ => head.contains(tree)
106+
case _ => false
101107
def addPreciseConversion(tree: Tree): Unit =
102-
myPreciseConvStack = (myPreciseConvStack.head + tree) :: myPreciseConvStack.tail
108+
myPreciseConvStack = myPreciseConvStack match
109+
case head :: tail => (head + tree) :: tail
110+
case _ => myPreciseConvStack
103111
def pushPreciseConversionStack(): Unit =
104112
myPreciseConvStack = Set.empty[Tree] :: myPreciseConvStack
105113
def popPreciseConversionStack(): Unit =

compiler/src/dotty/tools/dotc/core/Types.scala

Lines changed: 21 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1315,7 +1315,7 @@ object Types {
13151315
* and going to the operands of & and |.
13161316
* Overridden and cached in OrType.
13171317
*/
1318-
def widenSingletons(using Context): Type = dealias match {
1318+
def widenSingletons(using Context): Type = dealiasKeepOpaques match {
13191319
case tp: SingletonType =>
13201320
tp.widen
13211321
case tp: OrType =>
@@ -4535,25 +4535,26 @@ object Types {
45354535
private var cachedPrecise: Option[Boolean] = None
45364536
protected[Types] def setPreciseSubstitute(p: Boolean): Unit = preciseSubstitute = p
45374537
override def isPrecise(using Context): Boolean =
4538-
// the param is substituting a precise type or upper-bounded by a precise type or...
4539-
preciseSubstitute || ctx.typerState.constraint.upper(this).exists(_.isPrecise) ||
4540-
cachedPrecise.getOrElse {
4541-
val precise =
4542-
// the param itself is annotated as precise or the param is first introduced
4543-
// in a precise position of an applied type argument
4544-
binder.paramPrecises(paramNum) || binder.resType.paramInfoss.view.flatten.map(_.dealias).flatMap {
4545-
case p: TypeParamRef if p == this => Some(false)
4546-
case AppliedType(tycon, args) =>
4547-
val paramIdx = args.indexOf(this)
4548-
val syms = tycon.typeParamSymbols
4549-
if syms.isEmpty then None
4550-
else if paramIdx >= 0 then Some(syms(paramIdx).paramPrecise)
4551-
else None
4552-
case _ => None
4553-
}.headOption.getOrElse(false)
4554-
cachedPrecise = Some(precise)
4555-
precise
4556-
}
4538+
// the param is substituting a precise type or...
4539+
preciseSubstitute || cachedPrecise.getOrElse {
4540+
val precise =
4541+
// the param itself is annotated as precise or the param is first introduced
4542+
// in a precise position of an applied type argument
4543+
binder.paramPrecises(paramNum) || binder.resType.paramInfoss.view.flatten.flatMap {
4544+
case p: TypeParamRef if p == this => Some(false)
4545+
case at @ AppliedType(_, args) =>
4546+
val paramIdx = args.indexOf(this)
4547+
val syms = at.tyconTypeParams
4548+
if syms.isEmpty then None
4549+
else if paramIdx >= 0 then Some(syms(paramIdx).paramPrecise)
4550+
else None
4551+
case _ => None
4552+
}.headOption.getOrElse(false)
4553+
cachedPrecise = Some(precise)
4554+
precise
4555+
} ||
4556+
//the param upper-bounded by a precise type
4557+
ctx.typerState.constraint.upper(this).filter(_.paramName != this.paramName).exists(_.isPrecise)
45574558

45584559
/** Optimized version of occursIn, avoid quadratic blowup when solving
45594560
* constraints over large ground types.

compiler/src/dotty/tools/dotc/typer/PreciseChecker.scala

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,9 @@ import Types.*, Contexts.*, Trees.*
77
import Decorators.*
88

99
object PreciseChecker:
10-
def check(tparams: List[tpd.TypeDef], applied: Type, allowMorePreciseDef: Boolean)(using Context): Unit =
10+
enum Mode:
11+
case SamePrecise, AllowMorePrecise, AllowLessPrecise
12+
def check(tparams: List[tpd.TypeDef], applied: Type, mode : Mode)(using Context): Unit =
1113
applied match
1214
case tpe@AppliedType(_, args) =>
1315
val appliedParamPreciseList = tpe.tyconTypeParams.map(_.paramPrecise)
@@ -19,9 +21,10 @@ object PreciseChecker:
1921
val paramName = a.symbol.name.asTypeName
2022
val appliedParamPrecise = appliedParamPreciseList(i)
2123
tdefParamPreciseMap.get(paramName).foreach { tdefParamPrecise =>
22-
val preciseMismatch =
23-
if allowMorePreciseDef then !tdefParamPrecise && appliedParamPrecise
24-
else tdefParamPrecise != appliedParamPrecise
24+
val preciseMismatch = mode match
25+
case Mode.SamePrecise => tdefParamPrecise != appliedParamPrecise
26+
case Mode.AllowMorePrecise => !tdefParamPrecise && appliedParamPrecise
27+
case Mode.AllowLessPrecise => tdefParamPrecise && !appliedParamPrecise
2528
if preciseMismatch then
2629
val pos = tparams.find(_.name == paramName).get.srcPos
2730
report.error(em"${label(tdefParamPrecise)} type parameter $paramName occurs in ${label(appliedParamPrecise)} position in $tpe", pos)
@@ -32,7 +35,13 @@ object PreciseChecker:
3235

3336
def checkClass(tree: tpd.Template)(using Context): Unit =
3437
val tparams = tree.constr.leadingTypeParams
35-
tree.parents.view.map(_.tpe).foreach(check(tparams, _, true))
38+
tree.parents.view.map(_.tpe).foreach(check(tparams, _, Mode.AllowMorePrecise))
3639

3740
def checkLambda(tree: tpd.LambdaTypeTree)(using Context): Unit =
38-
check(tree.tparams, tree.body.tpe, false)
41+
tree.body.tpe match
42+
case at: AppliedType =>
43+
check(tree.tparams, at, Mode.SamePrecise)
44+
case tb: TypeBounds =>
45+
check(tree.tparams, tb.hi, Mode.AllowMorePrecise)
46+
check(tree.tparams, tb.lo, Mode.AllowLessPrecise)
47+
case _ =>

compiler/src/dotty/tools/dotc/typer/RefChecks.scala

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -413,6 +413,8 @@ object RefChecks {
413413
(memberTp, otherTp) match
414414
case (mpt: PolyType, otp: PolyType) =>
415415
mpt.paramPrecises == otp.paramPrecises
416+
case (TypeBounds(_, mptHi: TypeLambda), TypeBounds(_, otpHi: TypeLambda)) =>
417+
mptHi.paramPrecises == otpHi.paramPrecises
416418
case _ => true
417419

418420
// o: public | protected | package-protected (aka java's default access)

tests/neg/precise-alias-extends.check

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,3 +34,19 @@
3434
48 | type FooConAlias[-A] = FooCon[A] // error
3535
| ^^
3636
| imprecise type parameter A occurs in precise position in preciseTypeAliasExtendLessPrecise.FooCon[A]
37+
-- Error: tests/neg/precise-alias-extends.scala:58:16 ------------------------------------------------------------------
38+
58 | type Less[T] <: PBox[T] // error
39+
| ^
40+
| imprecise type parameter T occurs in precise position in preciseTypeBounds.PBox[T]
41+
-- Error: tests/neg/precise-alias-extends.scala:63:25 ------------------------------------------------------------------
42+
63 | type More[@precise T] >: Box[T] // error
43+
| ^^^^^^^^^^
44+
| precise type parameter T occurs in imprecise position in preciseTypeBounds.Box[T]
45+
-- Error: tests/neg/precise-alias-extends.scala:67:21 ------------------------------------------------------------------
46+
67 | opaque type Less[T] <: PBox[T] = PBox[T] // error
47+
| ^
48+
| imprecise type parameter T occurs in precise position in preciseTypeBounds.PBox[T]
49+
-- Error: tests/neg/precise-alias-extends.scala:68:30 ------------------------------------------------------------------
50+
68 | opaque type More[@precise T] <: Box[T] = Box[T] // error
51+
| ^^^^^^^^^^
52+
| precise type parameter T occurs in imprecise position in preciseTypeBounds.Box[T]

tests/neg/precise-alias-extends.scala

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,3 +46,23 @@ object preciseTypeAliasExtendLessPrecise:
4646
trait FooCon[@precise -T]
4747
class FooConExtend[-A] extends FooCon[A] // error
4848
type FooConAlias[-A] = FooCon[A] // error
49+
50+
51+
object preciseTypeBounds:
52+
class Box[T]
53+
class PBox[@precise T]
54+
55+
object Alias:
56+
object Upper:
57+
type Same[@precise T] <: PBox[T]
58+
type Less[T] <: PBox[T] // error
59+
type More[@precise T] <: Box[T]
60+
object Lower:
61+
type Same[@precise T] >: PBox[T]
62+
type Less[T] >: PBox[T]
63+
type More[@precise T] >: Box[T] // error
64+
65+
object Opaque:
66+
opaque type Same[@precise T] <: PBox[T] = PBox[T]
67+
opaque type Less[T] <: PBox[T] = PBox[T] // error
68+
opaque type More[@precise T] <: Box[T] = Box[T] // error

tests/neg/precise-override.check

Lines changed: 20 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,24 @@
1-
-- [E163] Declaration Error: tests/neg/precise-override.scala:16:8 -----------------------------------------------------
2-
16 | def id[@precise T](t: T): T = ??? // error
3-
| ^
4-
| error overriding method id in class Foo of type [T](t: T): T;
5-
| method id of type [@precise T](t: T): T has incompatible type
1+
-- [E163] Declaration Error: tests/neg/precise-override.scala:14:10 ----------------------------------------------------
2+
14 | def id[@precise T](t: T): T = ??? // error
3+
| ^
4+
| error overriding method id in class Foo of type [T](t: T): T;
5+
| method id of type [@precise T](t: T): T has incompatible type
66
|
77
| longer explanation available when compiling with `-explain`
8-
-- [E163] Declaration Error: tests/neg/precise-override.scala:24:8 -----------------------------------------------------
9-
24 | def id[T](t: T): T = ??? // error
10-
| ^
11-
| error overriding method id in class Foo of type [@precise T](t: T): T;
12-
| method id of type [T](t: T): T has incompatible type
8+
-- [E163] Declaration Error: tests/neg/precise-override.scala:20:10 ----------------------------------------------------
9+
20 | def id[T](t: T): T = ??? // error
10+
| ^
11+
| error overriding method id in class Foo of type [@precise T](t: T): T;
12+
| method id of type [T](t: T): T has incompatible type
1313
|
1414
| longer explanation available when compiling with `-explain`
15+
-- [E164] Declaration Error: tests/neg/precise-override.scala:34:12 ----------------------------------------------------
16+
34 | class Box[T] // error
17+
| ^
18+
| error overriding type Box in trait Foo with bounds[T];
19+
| class Box has different precise type parameter annotations
20+
-- [E164] Declaration Error: tests/neg/precise-override.scala:40:12 ----------------------------------------------------
21+
40 | class Box[@precise T] // error
22+
| ^
23+
| error overriding type Box in trait Foo with bounds[T];
24+
| class Box has different precise type parameter annotations

tests/neg/precise-override.scala

Lines changed: 38 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,40 @@
11
import annotation.precise
22

3-
object preciseOverrideSamePrecise:
4-
abstract class Foo:
5-
def id[@precise T](t: T) : T
6-
7-
class Bar extends Foo:
8-
def id[@precise T](t: T): T = ???
9-
10-
11-
object preciseOverrideMorePrecise:
12-
abstract class Foo:
13-
def id[T](t: T) : T
14-
15-
class Bar extends Foo:
16-
def id[@precise T](t: T): T = ??? // error
17-
18-
19-
object preciseOverrideLessPrecise:
20-
abstract class Foo:
21-
def id[@precise T](t: T) : T
22-
23-
class Bar extends Foo:
24-
def id[T](t: T): T = ??? // error
3+
object preciseDefOverride:
4+
object SamePrecise:
5+
abstract class Foo:
6+
def id[@precise T](t: T) : T
7+
class Bar extends Foo:
8+
def id[@precise T](t: T): T = ???
9+
10+
object MorePrecise:
11+
abstract class Foo:
12+
def id[T](t: T) : T
13+
class Bar extends Foo:
14+
def id[@precise T](t: T): T = ??? // error
15+
16+
object LessPrecise:
17+
abstract class Foo:
18+
def id[@precise T](t: T) : T
19+
class Bar extends Foo:
20+
def id[T](t: T): T = ??? // error
21+
22+
23+
object preciseTypeAliasOverride:
24+
object SamePrecise:
25+
trait Foo:
26+
type Box[@precise T]
27+
class Bar extends Foo:
28+
class Box[@precise T]
29+
30+
object LessPrecise:
31+
trait Foo:
32+
type Box[@precise T]
33+
class Bar extends Foo:
34+
class Box[T] // error
35+
36+
object MorePrecise:
37+
trait Foo:
38+
type Box[T]
39+
class Bar extends Foo:
40+
class Box[@precise T] // error

tests/neg/precise-typecheck.check

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,16 @@
1-
-- Error: tests/neg/precise-typecheck.scala:301:16 ---------------------------------------------------------------------
2-
301 | val x = id(2L) // error
1+
-- Error: tests/neg/precise-typecheck.scala:309:16 ---------------------------------------------------------------------
2+
309 | val x = id(2L) // error
33
| ^
44
| Cannot prove that (2L : Long) =:= (1L : Long).
5-
-- [E007] Type Mismatch Error: tests/neg/precise-typecheck.scala:370:46 ------------------------------------------------
6-
370 | val id1Check: Id1 = [T] => (t : T) => Box(t) // error
5+
-- [E007] Type Mismatch Error: tests/neg/precise-typecheck.scala:378:46 ------------------------------------------------
6+
378 | val id1Check: Id1 = [T] => (t : T) => Box(t) // error
77
| ^
88
| Found: [T] => (t: T) => Box[T]
99
| Required: precisePolymorphicTypesAndValues.Id1
1010
|
1111
| longer explanation available when compiling with `-explain`
12-
-- [E007] Type Mismatch Error: tests/neg/precise-typecheck.scala:373:54 ------------------------------------------------
13-
373 | val id2Check: Id2 = [@precise T] => (t: T) => Box(t) // error
12+
-- [E007] Type Mismatch Error: tests/neg/precise-typecheck.scala:381:54 ------------------------------------------------
13+
381 | val id2Check: Id2 = [@precise T] => (t: T) => Box(t) // error
1414
| ^
1515
| Found: [@precise T] => (t: T) => Box[T]
1616
| Required: precisePolymorphicTypesAndValues.Id2

tests/neg/precise-typecheck.scala

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,14 @@ object preciseCovariance:
179179
val fbic12Test: Inv[Int, 2] = fbic12
180180

181181

182+
object preciseCovariantOpaque:
183+
opaque type BoxC[@precise +A] = A
184+
def fromBox[B <: Any](x: BoxC[B]): BoxC[B] = x
185+
val b1 : BoxC[1] = ???
186+
val b11 = fromBox(b1)
187+
val b11Test: BoxC[1] = b11
188+
189+
182190
object preciseExtDefs:
183191
object form1:
184192
extension [@precise T, V](t: T)

0 commit comments

Comments
 (0)