Skip to content

Commit aada2ca

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

40 files changed

+571
-108
lines changed

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

Lines changed: 37 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -293,14 +293,49 @@ object desugar {
293293
vparam => toDefParam(vparam, keepAnnotations = true, keepDefault = false)
294294
}
295295

296+
def ignoreErrorsAndRun[R](op: => R): R =
297+
val savedState = ctx.typerState.snapshot()
298+
val savedReporter = ctx.reporter
299+
ctx.typerState.setReporter(Reporter.NoReporter)
300+
val ret = op
301+
ctx.typerState.setReporter(savedReporter)
302+
ctx.typerState.resetTo(savedState)
303+
ret
304+
305+
// gets arguments should be considered precise
306+
val paramPrecises: List[Boolean] =
307+
// indication for the type parameters preciseness
308+
val preciseMap : Map[TypeName, Boolean] = {
309+
meth.leadingTypeParams.map(t =>
310+
(t.name, t.mods.annotations.exists {
311+
//TODO: this is a hack. Typing causes problems
312+
case Apply(Select(New(Ident(p)), _), _) => p.toString == "precise"
313+
case _ => false
314+
})
315+
).toMap
316+
}
317+
// mapping type parameters preciseness onto term parameter preciseness
318+
meth.termParamss.view.flatten.map(p => p.tpt).map {
319+
case Ident(n) => preciseMap.getOrElse(n.asTypeName, false)
320+
case _ => false
321+
}.toList
322+
296323
def defaultGetters(paramss: List[ParamClause], n: Int): List[DefDef] = paramss match
297324
case ValDefs(vparam :: vparams) :: paramss1 =>
298325
def defaultGetter: DefDef =
326+
val (rhs, tpt) =
327+
// if the parameter is precise, then we add explicit return type for the
328+
// definition to keep the precise type after precise typing.
329+
if paramPrecises(n) then
330+
val rhsTyped = withMode(Mode.Precise){ctx.typer.typedExpr(vparam.rhs)}
331+
(TypedSplice(rhsTyped), TypeTree(rhsTyped.tpe))
332+
// otherwise, the desugaring is unchanged from the status quo
333+
else (vparam.rhs, TypeTree())
299334
DefDef(
300335
name = DefaultGetterName(meth.name, n),
301336
paramss = getterParamss(n),
302-
tpt = TypeTree(),
303-
rhs = vparam.rhs
337+
tpt = tpt,
338+
rhs = rhs
304339
)
305340
.withMods(Modifiers(
306341
meth.mods.flags & (AccessFlags | Synthetic) | (vparam.mods.flags & Inline),

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.mode.is(Mode.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/Mode.scala

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,4 +129,9 @@ object Mode {
129129
* Type `Null` becomes a subtype of non-primitive value types in TypeComparer.
130130
*/
131131
val RelaxedOverriding: Mode = newMode(30, "RelaxedOverriding")
132+
133+
/**
134+
* Indication that argument widening should not take place.
135+
*/
136+
val Precise: Mode = newMode(31, "Precise")
132137
}

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 --------------------------------------------------------

0 commit comments

Comments
 (0)