Skip to content

Commit 7ebeab9

Browse files
committed
add checks for proper precise aliasing, extending, and overriding
1 parent b433244 commit 7ebeab9

File tree

4 files changed

+66
-4
lines changed

4 files changed

+66
-4
lines changed

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

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ import scala.util.control.NonFatal
2222
import typer.ProtoTypes.constrained
2323
import typer.Applications.productSelectorTypes
2424
import reporting.trace
25-
import annotation.constructorOnly
25+
import annotation.{constructorOnly, tailrec}
2626
import cc.{CapturingType, derivedCapturingType, CaptureSet, stripCapturing, isBoxedCapturing, boxed, boxedUnlessFun, boxedIfTypeParam}
2727

2828
/** Provides methods to compare types.
@@ -2103,7 +2103,7 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling
21032103
* to override `tp2` ? This is the case if they're pairwise >:>.
21042104
*/
21052105
def matchingPolyParams(tp1: PolyType, tp2: PolyType): Boolean = {
2106-
def loop(formals1: List[Type], formals2: List[Type]): Boolean = formals1 match {
2106+
@tailrec def loop(formals1: List[Type], formals2: List[Type]): Boolean = formals1 match {
21072107
case formal1 :: rest1 =>
21082108
formals2 match {
21092109
case formal2 :: rest2 =>
@@ -2116,7 +2116,7 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling
21162116
case nil =>
21172117
formals2.isEmpty
21182118
}
2119-
loop(tp1.paramInfos, tp2.paramInfos)
2119+
tp1.paramPrecises == tp2.paramPrecises && loop(tp1.paramInfos, tp2.paramInfos)
21202120
}
21212121

21222122
// Type equality =:=

compiler/src/dotty/tools/dotc/transform/PostTyper.scala

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import scala.collection.mutable
66
import core._
77
import dotty.tools.dotc.typer.Checking
88
import dotty.tools.dotc.inlines.Inlines
9-
import dotty.tools.dotc.typer.VarianceChecker
9+
import dotty.tools.dotc.typer.{VarianceChecker, PreciseChecker}
1010
import typer.ErrorReporting.errorTree
1111
import Types._, Contexts._, Names._, Flags._, DenotTransformers._, Phases._
1212
import SymDenotations._, StdNames._, Annotations._, Trees._, Scopes._
@@ -388,6 +388,8 @@ class PostTyper extends MacroTransform with IdentityDenotTransformer { thisPhase
388388
annotateExperimental(sym)
389389
tree.rhs match
390390
case impl: Template =>
391+
if (!sym.is(Flags.Given)) // skipping over given classes that are generated from `given ... with {}`
392+
PreciseChecker.checkClass(impl)
391393
for parent <- impl.parents do
392394
Checking.checkTraitInheritance(parent.tpe.classSymbol, sym.asClass, parent.srcPos)
393395
// Add SourceFile annotation to top-level classes
@@ -403,6 +405,7 @@ class PostTyper extends MacroTransform with IdentityDenotTransformer { thisPhase
403405
Checking.checkGoodBounds(tree.symbol)
404406
(tree.rhs, sym.info) match
405407
case (rhs: LambdaTypeTree, bounds: TypeBounds) =>
408+
PreciseChecker.checkLambda(rhs, sym.isOpaqueAlias)
406409
VarianceChecker.checkLambda(rhs, bounds)
407410
if sym.isOpaqueAlias then
408411
VarianceChecker.checkLambda(rhs, TypeBounds.upper(sym.opaqueAlias))
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
package dotty.tools.dotc
2+
package typer
3+
4+
import dotty.tools.dotc.ast.{ Trees, tpd }
5+
import core.*
6+
import Types.*, Contexts.*, Trees.*
7+
import Decorators.*
8+
9+
object PreciseChecker:
10+
enum Mode:
11+
case SamePrecise, AllowMorePrecise, AllowLessPrecise
12+
def check(tparams: List[tpd.TypeDef], applied: Type, mode : Mode)(using Context): Unit =
13+
applied match
14+
case tpe@AppliedType(_, args) =>
15+
args.foreach(check(tparams, _, mode)) //recursively checking applied params
16+
val appliedParamPreciseList = tpe.tyconTypeParams.map(_.paramPrecise)
17+
val tdefParamPreciseMap = tparams.view.map(p => (p.name, p.symbol.paramPrecise)).toMap
18+
19+
def label(precise: Boolean): String = if (precise) "precise" else "imprecise"
20+
args.view.zipWithIndex.foreach {
21+
case (a: TypeRef, i) if a.symbol.name.isTypeName =>
22+
val paramName = a.symbol.name.asTypeName
23+
val appliedParamPrecise = appliedParamPreciseList(i)
24+
tdefParamPreciseMap.get(paramName).foreach { tdefParamPrecise =>
25+
val preciseMismatch = mode match
26+
case Mode.SamePrecise => tdefParamPrecise != appliedParamPrecise
27+
case Mode.AllowMorePrecise => !tdefParamPrecise && appliedParamPrecise
28+
case Mode.AllowLessPrecise => tdefParamPrecise && !appliedParamPrecise
29+
if preciseMismatch then
30+
val pos = tparams.find(_.name == paramName).get.srcPos
31+
report.error(em"${label(tdefParamPrecise)} type parameter $paramName occurs in ${label(appliedParamPrecise)} position in $tpe", pos)
32+
}
33+
case _ =>
34+
}
35+
case _ =>
36+
37+
def checkClass(tree: tpd.Template)(using Context): Unit =
38+
val tparams = tree.constr.leadingTypeParams
39+
tree.parents.view.map(_.tpe.dealias).foreach(check(tparams, _, Mode.AllowMorePrecise))
40+
41+
def checkLambda(tree: tpd.LambdaTypeTree, isOpaque: Boolean)(using Context): Unit =
42+
tree.body.tpe.dealiasKeepOpaques match
43+
case at: AppliedType =>
44+
val mode = if (isOpaque) Mode.SamePrecise else Mode.AllowLessPrecise
45+
check(tree.tparams, at, mode)
46+
case tb: TypeBounds =>
47+
check(tree.tparams, tb.hi, Mode.AllowMorePrecise)
48+
check(tree.tparams, tb.lo, Mode.AllowLessPrecise)
49+
case _ =>

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

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -409,6 +409,14 @@ object RefChecks {
409409
!(syms1 exists (set2 contains _))
410410
}
411411

412+
def samePrecises(memberTp: Type, otherTp: Type): Boolean =
413+
(memberTp, otherTp) match
414+
case (mpt: PolyType, otp: PolyType) =>
415+
mpt.paramPrecises == otp.paramPrecises
416+
case (TypeBounds(_, mptHi: TypeLambda), TypeBounds(_, otpHi: TypeLambda)) =>
417+
mptHi.paramPrecises == otpHi.paramPrecises
418+
case _ => true
419+
412420
// o: public | protected | package-protected (aka java's default access)
413421
// ^-may be overridden by member with access privileges-v
414422
// m: public | public/protected | public/protected/package-protected-in-same-package-as-o
@@ -509,6 +517,8 @@ object RefChecks {
509517
else if (!compatTypes(memberTp(self), otherTp(self)) &&
510518
!compatTypes(memberTp(upwardsSelf), otherTp(upwardsSelf)))
511519
overrideError("has incompatible type", compareTypes = true)
520+
else if (!samePrecises(memberTp(self), otherTp(self)))
521+
overrideError("has different precise type parameter annotations")
512522
else if (member.targetName != other.targetName)
513523
if (other.targetName != other.name)
514524
overrideError(i"needs to be declared with @targetName(${"\""}${other.targetName}${"\""}) so that external names match")

0 commit comments

Comments
 (0)