Skip to content

Commit 1a27719

Browse files
committed
Check method arguments with parametricity when static
When a global static is called, allow for a cold argument if the corresponding parameter is not `Matchable`.
1 parent e35b6ff commit 1a27719

File tree

2 files changed

+34
-10
lines changed

2 files changed

+34
-10
lines changed

compiler/src/dotty/tools/dotc/transform/init/Semantic.scala

+30-10
Original file line numberDiff line numberDiff line change
@@ -410,8 +410,8 @@ object Semantic {
410410
def select(f: Symbol, source: Tree): Contextual[Result] =
411411
value.select(f, source) ++ errors
412412

413-
def call(meth: Symbol, args: List[ArgInfo], superType: Type, source: Tree): Contextual[Result] =
414-
value.call(meth, args, superType, source) ++ errors
413+
def call(meth: Symbol, args: List[ArgInfo], receiver: Type, superType: Type, source: Tree): Contextual[Result] =
414+
value.call(meth, args, receiver, superType, source) ++ errors
415415

416416
def callConstructor(ctor: Symbol, args: List[ArgInfo], source: Tree): Contextual[Result] =
417417
value.callConstructor(ctor, args, source) ++ errors
@@ -587,7 +587,7 @@ object Semantic {
587587
}
588588
}
589589

590-
def call(meth: Symbol, args: List[ArgInfo], superType: Type, source: Tree, needResolve: Boolean = true): Contextual[Result] = log("call " + meth.show + ", args = " + args, printer, (_: Result).show) {
590+
def call(meth: Symbol, args: List[ArgInfo], receiver: Type, superType: Type, source: Tree, needResolve: Boolean = true): Contextual[Result] = log("call " + meth.show + ", args = " + args, printer, (_: Result).show) {
591591
def checkArgs = args.flatMap(_.promote)
592592

593593
def isSyntheticApply(meth: Symbol) =
@@ -600,6 +600,19 @@ object Semantic {
600600
|| (meth eq defn.Object_ne)
601601
|| (meth eq defn.Any_isInstanceOf)
602602

603+
def checkArgsWithParametricity() =
604+
val methodType = meth.info.stripPoly
605+
var allArgsPromote = true
606+
val errors = methodType.paramInfoss.flatten.zip(args).flatMap { (info, arg) =>
607+
val isMatchable = info.repeatedToSingle <:< defn.MatchableType
608+
//val isTypeVar =
609+
val isAnyRef = info.repeatedToSingle <:< defn.AnyRefType
610+
val errors = arg.promote
611+
allArgsPromote = allArgsPromote && errors.isEmpty
612+
if !isMatchable then Nil else errors
613+
}
614+
(errors, allArgsPromote)
615+
603616
// fast track if the current object is already initialized
604617
if promoted.isCurrentObjectPromoted then Result(Hot, Nil)
605618
else if isAlwaysSafe(meth) then Result(Hot, Nil)
@@ -610,7 +623,14 @@ object Semantic {
610623
val klass = meth.owner.companionClass.asClass
611624
instantiate(klass, klass.primaryConstructor, args, source)
612625
else
613-
Result(Hot, checkArgs)
626+
if meth.isStatic || receiver.isSingleton then
627+
val (errors, allArgsPromote) = checkArgsWithParametricity()
628+
if allArgsPromote || errors.nonEmpty then
629+
Result(Hot, errors)
630+
else
631+
Result(Cold, errors)
632+
else
633+
Result(Hot, checkArgs)
614634

615635
case Cold =>
616636
val error = CallCold(meth, source, trace.toVector)
@@ -666,7 +686,7 @@ object Semantic {
666686
}
667687

668688
case RefSet(refs) =>
669-
val resList = refs.map(_.call(meth, args, superType, source))
689+
val resList = refs.map(_.call(meth, args, receiver, superType, source))
670690
val value2 = resList.map(_.value).join
671691
val errors = resList.flatMap(_.errors)
672692
Result(value2, errors)
@@ -946,7 +966,7 @@ object Semantic {
946966
locally {
947967
given Trace = trace2
948968
val args = member.info.paramInfoss.flatten.map(_ => ArgInfo(Hot, EmptyTree))
949-
val res = warm.call(member, args, superType = NoType, source = member.defTree)
969+
val res = warm.call(member, args, receiver = NoType, superType = NoType, source = member.defTree)
950970
buffer ++= res.ensureHot(msg, source).errors
951971
}
952972
else
@@ -1126,14 +1146,14 @@ object Semantic {
11261146
case Select(supert: Super, _) =>
11271147
val SuperType(thisTp, superTp) = supert.tpe
11281148
val thisValue2 = resolveThis(thisTp.classSymbol.asClass, thisV, klass, ref)
1129-
Result(thisValue2, errors).call(ref.symbol, args, superTp, expr)
1149+
Result(thisValue2, errors).call(ref.symbol, args, thisTp, superTp, expr)
11301150

11311151
case Select(qual, _) =>
11321152
val res = eval(qual, thisV, klass) ++ errors
11331153
if ref.symbol.isConstructor then
11341154
res.callConstructor(ref.symbol, args, source = expr)
11351155
else
1136-
res.call(ref.symbol, args, superType = NoType, source = expr)
1156+
res.call(ref.symbol, args, receiver = qual.tpe, superType = NoType, source = expr)
11371157

11381158
case id: Ident =>
11391159
id.tpe match
@@ -1142,13 +1162,13 @@ object Semantic {
11421162
val enclosingClass = id.symbol.owner.enclosingClass.asClass
11431163
val thisValue2 = resolveThis(enclosingClass, thisV, klass, id)
11441164
// local methods are not a member, but we can reuse the method `call`
1145-
thisValue2.call(id.symbol, args, superType = NoType, expr, needResolve = false)
1165+
thisValue2.call(id.symbol, args, receiver = NoType, superType = NoType, expr, needResolve = false)
11461166
case TermRef(prefix, _) =>
11471167
val res = cases(prefix, thisV, klass, id) ++ errors
11481168
if id.symbol.isConstructor then
11491169
res.callConstructor(id.symbol, args, source = expr)
11501170
else
1151-
res.call(id.symbol, args, superType = NoType, source = expr)
1171+
res.call(id.symbol, args, receiver = prefix, superType = NoType, source = expr)
11521172

11531173
case Select(qualifier, name) =>
11541174
val qualRes = eval(qualifier, thisV, klass)

tests/init/pos/inner-enum.scala

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
class Outer:
2+
enum MyEnum {
3+
case Case
4+
}

0 commit comments

Comments
 (0)