Skip to content

Commit ac31672

Browse files
mbovelsoronpo
authored andcommitted
[Experiment] Precise annotation
1 parent 634c580 commit ac31672

File tree

5 files changed

+170
-2
lines changed

5 files changed

+170
-2
lines changed

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

Lines changed: 37 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import config.Printers.typr
1212
import typer.ProtoTypes.{newTypeVar, representedParamRef}
1313
import UnificationDirection.*
1414
import NameKinds.AvoidNameKind
15-
15+
import annotation.tailrec
1616
/** Methods for adding constraints and solving them.
1717
*
1818
* What goes into a Constraint as opposed to a ConstrainHandler?
@@ -545,8 +545,43 @@ trait ConstraintHandling {
545545
case WildcardType(optBounds) => optBounds.exists && isSingleton(optBounds.bounds.hi)
546546
case _ => isSubTypeWhenFrozen(tp, defn.SingletonType)
547547

548+
def derivesPreciseAnnot(tp: Type): Boolean =
549+
tp.derivesAnnotWith(_.matches(defn.PreciseAnnot))
550+
551+
def dependsOnParam(tp: Type, param: TypeParamRef) : Boolean =
552+
tp match
553+
case v: TypeVar => v.origin == param
554+
case AppliedType(_, args) => args.exists(dependsOnParam(_, param))
555+
case nothing => nothing =:= defn.NothingType
556+
557+
def isPrecise(tp: Type, checkUninstVars: Boolean = true): Boolean = tp match
558+
//the type parameter is annotated as precise
559+
case param: TypeParamRef if derivesPreciseAnnot(param) => true
560+
case param: TypeParamRef =>
561+
//the type parameter is from an applied type and there it is annotated as precise
562+
val preciseTypeParam = constraint.domainLambdas.view.flatMap(_.resType.paramInfoss.flatten).flatMap {
563+
case AppliedType(tycon, args) =>
564+
val preciseArgIdx = tycon.typeParams.indexWhere(t => derivesPreciseAnnot(t.paramInfo.hiBound))
565+
if (preciseArgIdx >= 0) Some(args(preciseArgIdx) == param)
566+
else None
567+
case p : TypeParamRef => if (p == param) Some(false) else None
568+
case _ => None
569+
}.headOption.getOrElse(false)
570+
if (preciseTypeParam) true
571+
//the type parameter is being dependent upon a different precise type parameter
572+
else if (checkUninstVars)
573+
constraint.uninstVars.view.map(_.origin)
574+
//getting all the entries' low-bound
575+
.map(v => (v, constraint.entry(v).loBound))
576+
//... and if an uninstantiated typevars is precise, then we check its arguments
577+
// and if one of the arguments is the same param we are considering to widen, then we don't
578+
// and consider this param to be precise
579+
.exists((v, lo) => dependsOnParam(lo, param) && isPrecise(v, false))
580+
else false
581+
case _ => false
582+
548583
val wideInst =
549-
if isSingleton(bound) then inst
584+
if isSingleton(bound) || isPrecise(bound) then inst
550585
else dropTransparentTraits(widenIrreducible(widenOr(widenSingle(inst))), bound)
551586
wideInst match
552587
case wideInst: TypeRef if wideInst.symbol.is(Module) =>

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -962,6 +962,7 @@ class Definitions {
962962
@tu lazy val NowarnAnnot: ClassSymbol = requiredClass("scala.annotation.nowarn")
963963
@tu lazy val TransparentTraitAnnot: ClassSymbol = requiredClass("scala.annotation.transparentTrait")
964964
@tu lazy val NativeAnnot: ClassSymbol = requiredClass("scala.native")
965+
@tu lazy val PreciseAnnot: ClassSymbol = requiredClass("scala.annotation.precise")
965966
@tu lazy val RepeatedAnnot: ClassSymbol = requiredClass("scala.annotation.internal.Repeated")
966967
@tu lazy val SourceFileAnnot: ClassSymbol = requiredClass("scala.annotation.internal.SourceFile")
967968
@tu lazy val ScalaSignatureAnnot: ClassSymbol = requiredClass("scala.reflect.ScalaSignature")
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
package scala.annotation
2+
3+
import scala.annotation.Annotation
4+
5+
@experimental
6+
class precise extends Annotation {
7+
8+
}

project/MiMaFilters.scala

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,5 +30,8 @@ object MiMaFilters {
3030
ProblemFilters.exclude[ReversedMissingMethodProblem]("scala.quoted.Quotes#reflectModule#TypeTreeModule.ref"),
3131

3232
ProblemFilters.exclude[MissingClassProblem]("scala.annotation.since"),
33+
34+
// APIs will be added in 3.3.0
35+
ProblemFilters.exclude[MissingClassProblem]("scala.annotation.precise"),
3336
)
3437
}

tests/neg/precise-type-params.scala

Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
import annotation.precise
2+
import language.implicitConversions
3+
4+
object preciseDefs:
5+
def id[T <: Any@precise](x: T): T = x
6+
class Box[T]
7+
def idBox[T <: Any@precise](t: T): Box[T] = ???
8+
9+
final val x = id(3)
10+
val xTest: 3 = x
11+
12+
final val x3 = id(id(id(3)))
13+
val x3Test: 3 = x3
14+
15+
final val tpl = id((1, 2))
16+
val tplTest: (1, 2) = tpl
17+
18+
final val list1 = id(1 :: Nil)
19+
val list1Test: List[1] = list1
20+
21+
final val list1hi = id(1 :: "hi" :: Nil)
22+
val list1hiTest: List[1 | "hi"] = list1hi
23+
24+
val c : Boolean = ???
25+
val ifVal = idBox(if c then 2 else 3)
26+
val ifValTest: Box[2 | 3] = ifVal
27+
28+
def same[T <: Any@precise](x: T, y: T) : Box[T] = ???
29+
final val sameVal = same(1, 2)
30+
val sameValTest: Box[1 | 2] = sameVal
31+
32+
def sameVarArgs[T <: Any@precise](a: T*) : Box[T] = ???
33+
final val sameVal123hi = sameVarArgs(1, 2, 3, "hi")
34+
val sameVal123hiTest: Box[1 | 2 | 3 | "hi"] = sameVal123hi
35+
36+
def dep1[T1 <: Any@precise, T2 <: T1](t1: T1)(t2: T2): Box[T1] = ???
37+
val d1 = dep1(1)(2) // error
38+
39+
def dep2[T1 <: Any, T2 <: T1@precise](t1: T1)(t2: T2): Box[T1] = ???
40+
final val d2 = dep2(1)(2)
41+
val d2Test: Box[Int] = d2
42+
43+
def dep12[T1 <: Any@precise, T2 <: T1@precise](t1: T1)(t2: T2): Box[T1] = ???
44+
final val d12 = dep12(1)(2)
45+
val d12Test: Box[1 | 2] = d12
46+
47+
class PreciseBox[T <: Any@precise]
48+
given [T]: PreciseBox[T] with {}
49+
50+
def check[T](t: T)(using PreciseBox[T]): Box[T] = ???
51+
val c1 = check(1)
52+
val c1Test: Box[Int] = c1
53+
54+
55+
object preciseInvariance:
56+
class Box[T <: Any @precise](x: T)
57+
val c: Boolean = ???
58+
val b = Box(if c then 2 else 3)
59+
val bTest: Box[2 | 3] = b
60+
final val b3 = Box(Box(Box(3)))
61+
val b3Test: Box[Box[Box[3]]] = b3
62+
63+
final val tpl = Box((1, (2, 3), "hi"))
64+
val tplTest: Box[(1, (2, 3), "hi")] = tpl
65+
66+
final val tpl2: (1, (2, 3), "hi") = (1, (2, 3), "hi")
67+
final val tpl3 = Box(tpl2)
68+
val tpl3Test: Box[tpl2.type] = tpl3
69+
70+
implicit def toBox[T <: Any @precise](from: T): Box[T] = Box(from)
71+
def box[T](b: Box[T]): Box[T] = b
72+
73+
final val btpl = box(((1, 2), (3, 4)))
74+
val btplTest: Box[((1, 2), (3, 4))] = btpl
75+
76+
final val b1 = box(1)
77+
val b1Test: Box[1] = b1
78+
79+
class Boxx[T <: Any@precise](x: T*)
80+
val b123 = Boxx(1, 2, 3)
81+
val b123Test: Boxx[1 | 2 | 3] = b123
82+
83+
84+
object preciseCovariance:
85+
class Box[+A <: Any@precise](x: A)
86+
def fromBox[B <: Any](x: Box[B]): Box[B] = x
87+
final val b1 = Box(1)
88+
val b11 = fromBox(b1)
89+
val b11Test: Box[1] = b11
90+
val b11CovTest: Box[Int] = b11
91+
92+
class Inv[A, B]
93+
class BoxCI[+C <: Any@precise, +I](c: C, i: I)
94+
def fromBoxCI[C, I](x: BoxCI[C, I]): Inv[C, I] = ???
95+
val bci = BoxCI(1, 2)
96+
val bciTest: BoxCI[1, Int] = bci
97+
val fbci = fromBoxCI(bci)
98+
val fbciTest: Inv[1, Int] = fbci
99+
val fbci12 = fromBoxCI(??? : BoxCI[1, 2])
100+
val fbci12Test: Inv[1, Int] = fbci12
101+
102+
class BoxIC[+I, +C <: Any@precise](i: I, c: C)
103+
def fromBoxIC[I, C](x: BoxIC[I, C]): Inv[I, C] = ???
104+
val bic = BoxIC(1, 2)
105+
val bicTest: BoxIC[Int, 2] = bic
106+
val fbic = fromBoxIC(bic)
107+
val fbicTest: Inv[Int, 2] = fbic
108+
val fbic12 = fromBoxIC(??? : BoxIC[1, 2])
109+
val fbic12Test: Inv[Int, 2] = fbic12
110+
111+
112+
/*
113+
errors we are not supposed to have
114+
*/
115+
object problems:
116+
class Box[T]
117+
def idBox[T <: Any@precise](t: T): Box[T] = ???
118+
def idBoxBox[BB](x: Box[BB]): Box[BB] = x
119+
120+
val b1 = idBoxBox(idBox(1))
121+
val b1Test: Box[1] = b1

0 commit comments

Comments
 (0)