Skip to content

Commit 9115947

Browse files
committed
Fix type test for trait parameter arguments
Fixes #11993
1 parent a070798 commit 9115947

File tree

9 files changed

+56
-35
lines changed

9 files changed

+56
-35
lines changed

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

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -652,7 +652,7 @@ trait TypedTreeInfo extends TreeInfo[Type] { self: Trees.Instance[Type] =>
652652
}
653653
}
654654

655-
/** The type arguemnts of a possibly curried call */
655+
/** The type arguments of a possibly curried call */
656656
def typeArgss(tree: Tree): List[List[Tree]] =
657657
@tailrec
658658
def loop(tree: Tree, argss: List[List[Tree]]): List[List[Tree]] = tree match
@@ -661,7 +661,7 @@ trait TypedTreeInfo extends TreeInfo[Type] { self: Trees.Instance[Type] =>
661661
case _ => argss
662662
loop(tree, Nil)
663663

664-
/** The term arguemnts of a possibly curried call */
664+
/** The term arguments of a possibly curried call */
665665
def termArgss(tree: Tree): List[List[Tree]] =
666666
@tailrec
667667
def loop(tree: Tree, argss: List[List[Tree]]): List[List[Tree]] = tree match
@@ -670,7 +670,7 @@ trait TypedTreeInfo extends TreeInfo[Type] { self: Trees.Instance[Type] =>
670670
case _ => argss
671671
loop(tree, Nil)
672672

673-
/** The type and term arguemnts of a possibly curried call, in the order they are given */
673+
/** The type and term arguments of a possibly curried call, in the order they are given */
674674
def allArgss(tree: Tree): List[List[Tree]] =
675675
@tailrec
676676
def loop(tree: Tree, argss: List[List[Tree]]): List[List[Tree]] = tree match

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2199,6 +2199,10 @@ object SymDenotations {
21992199
def paramAccessors(using Context): List[Symbol] =
22002200
unforcedDecls.filter(_.is(ParamAccessor))
22012201

2202+
/** The term parameter getters of this class. */
2203+
def paramGetters(using Context): List[Symbol] =
2204+
paramAccessors.filterNot(_.isSetter)
2205+
22022206
/** If this class has the same `decls` scope reference in `phase` and
22032207
* `phase.next`, install a new denotation with a cloned scope in `phase.next`.
22042208
*/

compiler/src/dotty/tools/dotc/reporting/ErrorMessageID.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -154,7 +154,7 @@ enum ErrorMessageID extends java.lang.Enum[ErrorMessageID] {
154154
ErasedTypesCanOnlyBeFunctionTypesID,
155155
CaseClassMissingNonImplicitParamListID,
156156
EnumerationsShouldNotBeEmptyID,
157-
UNUSED_1,
157+
IllegalParameterInitID,
158158
RedundantModifierID,
159159
TypedCaseDoesNotExplicitlyExtendTypedEnumID,
160160
IllegalRedefinitionOfStandardKindID,

compiler/src/dotty/tools/dotc/reporting/messages.scala

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1467,6 +1467,14 @@ import transform.SymUtils._
14671467
def msg = em"""$tp does not conform to its self type $selfType; cannot be instantiated"""
14681468
}
14691469

1470+
class IllegalParameterInit(found: Type, expected: Type, param: Symbol, cls: Symbol)(using Context)
1471+
extends TypeMismatchMsg(found, expected)(IllegalParameterInitID):
1472+
def msg =
1473+
em"""illegal parameter initialization of $param.
1474+
|
1475+
| The argument passed for $param has type: $found
1476+
| but $cls expects $param to have type: $expected"""
1477+
14701478
class AbstractMemberMayNotHaveModifier(sym: Symbol, flag: FlagSet)(
14711479
implicit ctx: Context)
14721480
extends SyntaxMsg(AbstractMemberMayNotHaveModifierID) {

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -132,7 +132,7 @@ class Constructors extends MiniPhase with IdentityDenotTransformer { thisPhase =
132132
// Produce aligned accessors and constructor parameters. We have to adjust
133133
// for any outer parameters, which are last in the sequence of original
134134
// parameter accessors but come first in the constructor parameter list.
135-
val accessors = cls.paramAccessors.filterNot(x => x.isSetter)
135+
val accessors = cls.paramGetters
136136
val vparamsWithOuterLast = vparams match {
137137
case vparam :: rest if vparam.name == nme.OUTER => rest ::: vparam :: Nil
138138
case _ => vparams

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

Lines changed: 22 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ import Constants.Constant
2626
import NullOpsDecorator._
2727

2828
object RefChecks {
29-
import tpd.{Tree, MemberDef, NamedArg, Literal, Template, DefDef}
29+
import tpd._
3030

3131
val name: String = "refchecks"
3232

@@ -133,6 +133,27 @@ object RefChecks {
133133
false
134134
}
135135

136+
/** Check that arguments passed to trait parameters conform to the parameter types
137+
* in the current class. This is necessary since parameter types might be narrowed
138+
* through intersection with other parent traits. See neg/i11018.scala.
139+
*
140+
def checkParamInits(app: Apply): Unit =
141+
val parentCls = app.tpe.classSymbol
142+
if parentCls.is(Trait) then
143+
val params = parentCls.asClass.paramGetters
144+
val args = termArgss(app).flatten
145+
for (param, arg) <- params.lazyZip(args) do
146+
if !param.is(Private) then // its type can be narrowed through intersection -> a check is needed
147+
val paramType = cls.thisType.memberInfo(param)
148+
if !(arg.tpe <:< paramType) then
149+
val argTypes = args.tpes
150+
// it could still be OK but we might need to substitute arguments for parameters
151+
// to account for dependent parameters. See pos/i11993.scala
152+
if !(arg.tpe.subst(params, argTypes) <:< paramType.subst(params, argTypes))
153+
then
154+
report.error(IllegalParameterInit(arg.tpe, paramType, param, cls), arg.srcPos)
155+
156+
for case app: Apply <- parentTrees do checkParamInits(app)
136157
case _ =>
137158
}
138159
@@ -801,34 +822,7 @@ object RefChecks {
801822
report.error(problem(), clazz.srcPos)
802823
}
803824
804-
// check that basetype and subtype agree on types of trait parameters
805-
//
806-
// I.e. trait and class parameters not only need to conform to the expected
807-
// type of the corresponding base-trait, but also to the type as seen by the
808-
// inheriting subtype.
809-
def checkTraitParametersOK() = for {
810-
parent <- clazz.info.parents
811-
parentSym = parent.classSymbol
812-
if parentSym.isClass
813-
cls = parentSym.asClass
814-
if cls.paramAccessors.nonEmpty
815-
param <- cls.paramAccessors
816-
} {
817-
val tpeFromParent = parent.memberInfo(param)
818-
val tpeFromClazz = clazz.thisType.memberInfo(param)
819-
if (!(tpeFromParent <:< tpeFromClazz)) {
820-
val msg =
821-
em"""illegal parameter: The types of $param do not match.
822-
|
823-
| $param in $cls has type: $tpeFromParent
824-
| but $clazz expects $param to have type: $tpeFromClazz"""
825-
826-
report.error(msg, clazz.srcPos)
827-
}
828-
}
829-
830825
checkParameterizedTraitsOK()
831-
checkTraitParametersOK()
832826
}
833827
834828
/** Check that `site` does not inherit conflicting generic instances of `baseCls`,

tests/generic-java-signatures/derivedNames.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ object Test {
99
if (scala.util.Properties.isWin)
1010
assert(returnType.toString == out1 || returnType.toString == out2)
1111
else if (scala.util.Properties.isMac)
12-
assert(returnType.toString == out1)
12+
assert(returnType.toString == out1, s"$returnType != $out1")
1313
else
1414
assert(returnType.toString == out2)
1515
}

tests/neg/i3989f.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ object Test extends App {
33
class B[+X](val y: X) extends A[X](y)
44
class C extends B(5) with A[String] // error: illegal inheritance
55

6-
class D extends B(5) with A[Any] // error: illegal parameter
6+
class D extends B(5) with A[Any]
77

88
def f(a: A[Int]): String = a match {
99
case c: C => c.x

tests/pos/i11993.scala

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
object test1:
2+
class Foo(
3+
val x: String,
4+
val y: Option[x.type]
5+
)
6+
7+
class Bar extends Foo("bar", Some("bar"))
8+
9+
object test2:
10+
trait Foo(
11+
val x: String,
12+
val y: Option[x.type]
13+
)
14+
15+
class Bar extends Foo("bar", Some("bar"))

0 commit comments

Comments
 (0)