Skip to content

Commit 65850f8

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

32 files changed

+381
-89
lines changed

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

Lines changed: 29 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,35 @@ 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 _ => false
556+
557+
def isPreciseRecur(tp: Type, uninstParams : List[TypeParamRef]): Boolean = tp match
558+
//the type parameter is annotated as precise
559+
case param: TypeParamRef if param.isPrecise | ctx.typerState.precise => true
560+
case param: TypeParamRef =>
561+
//the type parameter is from an applied type and there it is annotated as precise
562+
constraint.domainLambdas.view.flatMap(_.resType.paramInfoss.flatten).flatMap {
563+
case AppliedType(tycon, args) =>
564+
val preciseArgIdx = tycon.typeParamSymbols.indexWhere(_.paramPrecise)
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+
case _ => false
571+
572+
def isPrecise(tp: Type): Boolean =
573+
isPreciseRecur(tp, constraint.uninstVars.view.reverse.map(_.origin).toList)
574+
548575
val wideInst =
549-
if isSingleton(bound) then inst
576+
if isSingleton(bound) || isPrecise(bound) then inst
550577
else dropTransparentTraits(widenIrreducible(widenOr(widenSingle(inst))), bound)
551578
wideInst match
552579
case wideInst: TypeRef if wideInst.symbol.is(Module) =>

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

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -164,7 +164,7 @@ class Definitions {
164164
useCompleter: Boolean = false) = {
165165
val tparamNames = PolyType.syntheticParamNames(typeParamCount)
166166
val tparamInfos = tparamNames map (_ => bounds)
167-
def ptype = PolyType(tparamNames)(_ => tparamInfos, resultTypeFn)
167+
def ptype = PolyType(tparamNames, Nil)(_ => tparamInfos, resultTypeFn)
168168
val info =
169169
if (useCompleter)
170170
new LazyType {
@@ -695,7 +695,7 @@ class Definitions {
695695
case meth: MethodType =>
696696
info.derivedLambdaType(
697697
resType = meth.derivedLambdaType(
698-
paramNames = Nil, paramInfos = Nil))
698+
paramNames = Nil, paramPrecises = Nil, paramInfos = Nil))
699699
}
700700
}
701701
val argConstr = constr.copy().entered
@@ -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")

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -546,7 +546,7 @@ object Denotations {
546546
&& tp1.isErasedMethod == tp2.isErasedMethod =>
547547
val resType = infoMeet(tp1.resType, tp2.resType.subst(tp2, tp1), safeIntersection)
548548
if resType.exists then
549-
tp1.derivedLambdaType(mergeParamNames(tp1, tp2), tp1.paramInfos, resType)
549+
tp1.derivedLambdaType(mergeParamNames(tp1, tp2), Nil, tp1.paramInfos, resType)
550550
else NoType
551551
case _ => NoType
552552
case tp1: PolyType =>
@@ -556,6 +556,7 @@ object Denotations {
556556
if resType.exists then
557557
tp1.derivedLambdaType(
558558
mergeParamNames(tp1, tp2),
559+
Nil,
559560
tp1.paramInfos.zipWithConserve(tp2.paramInfos)( _ & _ ),
560561
resType)
561562
else NoType

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@ final class ProperGadtConstraint private(
9191
override def addToConstraint(params: List[Symbol])(using Context): Boolean = {
9292
import NameKinds.DepParamName
9393

94-
val poly1 = PolyType(params.map { sym => DepParamName.fresh(sym.name.toTypeName) })(
94+
val poly1 = PolyType(params.map { sym => DepParamName.fresh(sym.name.toTypeName) }, params.map(_.paramPrecise))(
9595
pt => params.map { param =>
9696
// In bound type `tp`, replace the symbols in dependent positions with their internal TypeParamRefs.
9797
// The replaced symbols will be later picked up in `ConstraintHandling#addToConstraint`

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -523,7 +523,7 @@ class OrderingConstraint(private val boundsMap: ParamBounds,
523523
val TypeBounds(lo, hi) :: pinfos1 = tl.paramInfos: @unchecked
524524
paramInfos = TypeBounds(lo, LazyRef.of(hi)) :: pinfos1
525525
}
526-
ensureFresh(tl.newLikeThis(tl.paramNames, paramInfos, tl.resultType))
526+
ensureFresh(tl.newLikeThis(tl.paramNames, tl.paramPrecises, paramInfos, tl.resultType))
527527
}
528528
else tl
529529

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,9 @@ trait ParamInfo {
3838
/** The variance of the type parameter */
3939
def paramVariance(using Context): Variance
4040

41+
/** The precise enforcement indicator of the type parameter */
42+
def paramPrecise(using Context): Boolean
43+
4144
/** The variance of the type parameter, as a number -1, 0, +1.
4245
* Bivariant is mapped to 1, i.e. it is treated like Covariant.
4346
*/

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

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1483,6 +1483,11 @@ object SymDenotations {
14831483
else if is(Contravariant) then Contravariant
14841484
else EmptyFlags
14851485

1486+
/** The precise enforcement indicator of this type parameter or type member
1487+
*/
1488+
final def precise(using Context): Boolean =
1489+
hasAnnotation(defn.PreciseAnnot)
1490+
14861491
/** The flags to be used for a type parameter owned by this symbol.
14871492
* Overridden by ClassDenotation.
14881493
*/

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

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -346,6 +346,12 @@ object Symbols {
346346
def paramInfoAsSeenFrom(pre: Type)(using Context): Type = pre.memberInfo(this)
347347
def paramInfoOrCompleter(using Context): Type = denot.infoOrCompleter
348348
def paramVariance(using Context): Variance = denot.variance
349+
def paramPrecise(using Context): Boolean =
350+
val owner = denot.owner
351+
if (owner.isConstructor)
352+
owner.owner.typeParams.exists(p => p.name == name && p.paramPrecise)
353+
else
354+
denot.precise
349355
def paramRef(using Context): TypeRef = denot.typeRef
350356

351357
// -------- Printing --------------------------------------------------------

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1019,7 +1019,7 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling
10191019
variancesConform(remainingTparams, tparams) && {
10201020
val adaptedTycon =
10211021
if d > 0 then
1022-
HKTypeLambda(remainingTparams.map(_.paramName))(
1022+
HKTypeLambda(remainingTparams.map(_.paramName), remainingTparams.map(_.paramPrecise))(
10231023
tl => remainingTparams.map(remainingTparam =>
10241024
tl.integrate(remainingTparams, remainingTparam.paramInfo).bounds),
10251025
tl => otherTycon.appliedTo(
@@ -2365,6 +2365,7 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling
23652365
else if (tparams1.hasSameLengthAs(tparams2))
23662366
HKTypeLambda(
23672367
paramNames = HKTypeLambda.syntheticParamNames(tparams1.length),
2368+
paramPrecises = Nil,
23682369
variances =
23692370
if tp1.isDeclaredVarianceLambda && tp2.isDeclaredVarianceLambda then
23702371
tparams1.lazyZip(tparams2).map((p1, p2) => combineVariance(p1.paramVariance, p2.paramVariance))

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

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -234,7 +234,7 @@ object TypeErasure {
234234

235235
def eraseParamBounds(tp: PolyType): Type =
236236
tp.derivedLambdaType(
237-
tp.paramNames, tp.paramNames map (Function.const(TypeBounds.upper(defn.ObjectType))), tp.resultType)
237+
tp.paramNames, tp.paramPrecises, tp.paramNames map (Function.const(TypeBounds.upper(defn.ObjectType))), tp.resultType)
238238

239239
if (defn.isPolymorphicAfterErasure(sym)) eraseParamBounds(sym.info.asInstanceOf[PolyType])
240240
else if (sym.isAbstractType) TypeAlias(WildcardType)
@@ -642,14 +642,14 @@ class TypeErasure(sourceLanguage: SourceLanguage, semiEraseVCs: Boolean, isConst
642642
val formals = formals0.mapConserve(paramErasure)
643643
eraseResult(tp.resultType) match {
644644
case rt: MethodType =>
645-
tp.derivedLambdaType(names ++ rt.paramNames, formals ++ rt.paramInfos, rt.resultType)
645+
tp.derivedLambdaType(names ++ rt.paramNames, Nil, formals ++ rt.paramInfos, rt.resultType)
646646
case NoType =>
647647
// Can happen if we smuggle in a Nothing in the qualifier. Normally we prevent that
648648
// in Checking.checkMembersOK, but compiler-generated code can bypass this test.
649649
// See i15377.scala for a test case.
650650
NoType
651651
case rt =>
652-
tp.derivedLambdaType(names, formals, rt)
652+
tp.derivedLambdaType(names, Nil, formals, rt)
653653
}
654654
case tp: PolyType =>
655655
this(tp.resultType)

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ object TypeEval:
1313
case tycon: TypeRef if defn.isCompiletimeAppliedType(tycon.symbol) =>
1414
extension (tp: Type) def fixForEvaluation: Type =
1515
tp.normalized.dealias match
16+
// deeper evaluation required
17+
case tp : AppliedType => tryCompiletimeConstantFold(tp)
1618
// enable operations for constant singleton terms. E.g.:
1719
// ```
1820
// final val one = 1

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

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,10 @@ class TyperState() {
8989
def ownedVars: TypeVars = myOwnedVars
9090
def ownedVars_=(vs: TypeVars): Unit = myOwnedVars = vs
9191

92+
private var myPrecise: Boolean = false
93+
def precise : Boolean = myPrecise
94+
def precise_=(p : Boolean): Unit = myPrecise = p
95+
9296
/** Initializes all fields except reporter, isCommittable, which need to be
9397
* set separately.
9498
*/
@@ -100,6 +104,10 @@ class TyperState() {
100104
this.previousConstraint = constraint
101105
this.myOwnedVars = SimpleIdentitySet.empty
102106
this.isCommitted = false
107+
previous match
108+
case ts : TyperState =>
109+
this.precise = ts.precise
110+
case null =>
103111
this
104112

105113
/** A fresh typer state with the same constraint as this one. */

0 commit comments

Comments
 (0)