Skip to content

Commit 0f3d3bb

Browse files
committed
Preliminary select refactorings
Move some code out of `adapt` and `assignType`, to be re-used later in `typedSelect`. The aim of the refactoring is to move all select-dependent adaptations and re-tries into typedSelect. Right now some are in `adapt` and some are in `assignType`. This is awkward since it means that - we do too much in `TypeAssigner`. Sine it aims to be minimal `TypeAssigner` should have no business doing adaptations and retries. - wo do some adaptations too early in `adpt`. This means we need the cludge of wrapping trees in `ExtMethodApply` which causes scala#8182, among others.
1 parent 5672999 commit 0f3d3bb

File tree

3 files changed

+63
-54
lines changed

3 files changed

+63
-54
lines changed

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

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,11 @@ object Inferencing {
3838
result
3939
}
4040

41+
def canDefineFurther(tp: Type)(using Context): Boolean =
42+
val prevConstraint = ctx.typerState.constraint
43+
isFullyDefined(tp, force = ForceDegree.all)
44+
&& (ctx.typerState.constraint ne prevConstraint)
45+
4146
/** The fully defined type, where all type variables are forced.
4247
* Throws an error if type contains wildcards.
4348
*/

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

Lines changed: 32 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,25 @@ trait TypeAssigner {
141141
else if (!qualType.isInstanceOf[TermType])
142142
qualType = errorType(em"$qualType is illegal as a selection prefix", qual1.srcPos)
143143

144+
def arrayElemType = qual1.tpe.widen match
145+
case JavaArrayType(elemtp) => elemtp
146+
case qualType =>
147+
report.error("Expected Array but was " + qualType.show, tree.srcPos)
148+
defn.NothingType
149+
144150
val name = tree.name
151+
val p = nme.primitive
152+
name match
153+
case p.arrayApply => return MethodType(defn.IntType :: Nil, arrayElemType)
154+
case p.arrayUpdate => return MethodType(defn.IntType :: arrayElemType :: Nil, defn.UnitType)
155+
case p.arrayLength => return MethodType(Nil, defn.IntType)
156+
// Note that we do not need to handle calls to Array[T]#clone() specially:
157+
// The JLS section 10.7 says "The return type of the clone method of an array type
158+
// T[] is T[]", but the actual return type at the bytecode level is Object which
159+
// is casted to T[] by javac. Since the return type of Array[T]#clone() is Array[T],
160+
// this is exactly what Erasure will do.
161+
case _ =>
162+
145163
val pre = maybeSkolemizePrefix(qualType, name)
146164
val mbr = qualType.findMember(name, pre)
147165
def isDynamicExpansion(tree: untpd.RefTree): Boolean = {
@@ -165,15 +183,19 @@ trait TypeAssigner {
165183
selectionType(tree, qual1)
166184
else if (name == nme.CONSTRUCTOR)
167185
errorType(ex"$qualType does not have a constructor", tree.srcPos)
168-
else {
169-
val kind = if (name.isTypeName) "type" else "value"
170-
def addendum = err.selectErrorAddendum(tree, qual1, qualType, importSuggestionAddendum)
171-
errorType(NotAMember(qualType, name, kind, addendum), tree.srcPos)
172-
}
186+
else
187+
NoType
173188
}
174189

175190
def importSuggestionAddendum(pt: Type)(using Context): String = ""
176191

192+
def notAMember(tree: untpd.Select, qual: Tree)(using Context): ErrorType =
193+
val kind = if tree.isType then "type" else "value"
194+
val qualType = qual.tpe.widenIfUnstable
195+
def addendum = err.selectErrorAddendum(tree, qual, qualType, importSuggestionAddendum)
196+
errorType(NotAMember(qualType, tree.name, kind, addendum), tree.srcPos)
197+
198+
177199
/** The type of the selection in `tree`, where `qual1` is the typed qualifier part.
178200
* The selection type is additionally checked for accessibility.
179201
*/
@@ -189,32 +211,12 @@ trait TypeAssigner {
189211
def assignType(tree: untpd.Ident, tp: Type)(using Context): Ident =
190212
tree.withType(tp)
191213

192-
def assignType(tree: untpd.Select, qual: Tree)(using Context): Select = {
193-
def qualType = qual.tpe.widen
194-
def arrayElemType = {
195-
qualType match {
196-
case JavaArrayType(elemtp) => elemtp
197-
case _ =>
198-
report.error("Expected Array but was " + qualType.show, tree.srcPos)
199-
defn.NothingType
200-
}
201-
}
202-
val p = nme.primitive
203-
val tp = tree.name match {
204-
case p.arrayApply => MethodType(defn.IntType :: Nil, arrayElemType)
205-
case p.arrayUpdate => MethodType(defn.IntType :: arrayElemType :: Nil, defn.UnitType)
206-
case p.arrayLength => MethodType(Nil, defn.IntType)
207-
208-
// Note that we do not need to handle calls to Array[T]#clone() specially:
209-
// The JLS section 10.7 says "The return type of the clone method of an array type
210-
// T[] is T[]", but the actual return type at the bytecode level is Object which
211-
// is casted to T[] by javac. Since the return type of Array[T]#clone() is Array[T],
212-
// this is exactly what Erasure will do.
213-
214-
case _ => accessibleSelectionType(tree, qual)
215-
}
214+
def assignType(tree: untpd.Select, tp: Type)(using Context): Select =
216215
ConstFold.Select(tree.withType(tp))
217-
}
216+
217+
def assignType(tree: untpd.Select, qual: Tree)(using Context): Select =
218+
val ownType = accessibleSelectionType(tree, qual).orElse(notAMember(tree, qual))
219+
assignType(tree, ownType)
218220

219221
/** Normalize type T appearing in a new T by following eta expansions to
220222
* avoid higher-kinded types.

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

Lines changed: 26 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -551,18 +551,22 @@ class Typer extends Namer
551551
case qual: ExtMethodApply =>
552552
qual.app
553553
case qual =>
554-
val select = assignType(cpy.Select(tree)(qual, tree.name), qual)
555-
val select1 = toNotNullTermRef(select, pt)
556-
557-
if (tree.name.isTypeName) checkStable(qual.tpe, qual.srcPos, "type prefix")
558-
559-
if select1.tpe ne TryDynamicCallType then
560-
checkStableIdentPattern(select1, pt)
561-
ConstFold(select1)
562-
else if pt.isInstanceOf[FunOrPolyProto] || pt == AssignProto then
563-
select1
564-
else
565-
typedDynamicSelect(tree, Nil, pt)
554+
val untpdSelect = cpy.Select(tree)(qual, tree.name)
555+
val ownType = accessibleSelectionType(untpdSelect, qual)
556+
if ownType.exists then
557+
val select = assignType(untpdSelect, ownType)
558+
val select1 = toNotNullTermRef(select, pt)
559+
560+
if (tree.name.isTypeName) checkStable(qual.tpe, qual.srcPos, "type prefix")
561+
562+
if select1.tpe ne TryDynamicCallType then
563+
checkStableIdentPattern(select1, pt)
564+
ConstFold(select1)
565+
else if pt.isInstanceOf[FunOrPolyProto] || pt == AssignProto then
566+
select1
567+
else
568+
typedDynamicSelect(tree, Nil, pt)
569+
else notAMember(untpdSelect, qual)
566570
}
567571

568572
def typedSelect(tree: untpd.Select, pt: Type)(using Context): Tree = {
@@ -3439,7 +3443,7 @@ class Typer extends Namer
34393443
case _ => tp
34403444
}
34413445

3442-
def adaptToSubType(wtp: Type): Tree = {
3446+
def adaptToSubType(wtp: Type): Tree =
34433447
// try converting a constant to the target type
34443448
ConstFold(tree).tpe.widenTermRefExpr.normalized match
34453449
case ConstantType(x) =>
@@ -3478,6 +3482,13 @@ class Typer extends Namer
34783482
case _ =>
34793483
}
34803484

3485+
// try an Any -> Matchable conversion
3486+
if pt.isMatchableBound && !wtp.derivesFrom(defn.MatchableClass) then
3487+
checkMatchable(wtp, tree.srcPos, pattern = false)
3488+
val target = AndType(tree.tpe.widenExpr, defn.MatchableType)
3489+
if target <:< pt then
3490+
return readapt(tree.cast(target))
3491+
34813492
// try GADT approximation, but only if we're trying to select a member
34823493
// Member lookup cannot take GADTs into account b/c of cache, so we
34833494
// approximate types based on GADT constraints instead. For an example,
@@ -3520,18 +3531,9 @@ class Typer extends Namer
35203531
case _ =>
35213532
}
35223533

3523-
// try an Any -> Matchable conversion
3524-
if pt.isMatchableBound && !wtp.derivesFrom(defn.MatchableClass) then
3525-
checkMatchable(wtp, tree.srcPos, pattern = false)
3526-
val target = AndType(tree.tpe.widenExpr, defn.MatchableType)
3527-
if target <:< pt then
3528-
return readapt(tree.cast(target))
3529-
35303534
// try an implicit conversion
3531-
val prevConstraint = ctx.typerState.constraint
35323535
def recover(failure: SearchFailureType) =
3533-
if (isFullyDefined(wtp, force = ForceDegree.all) &&
3534-
ctx.typerState.constraint.ne(prevConstraint)) readapt(tree)
3536+
if canDefineFurther(wtp) then readapt(tree)
35353537
else err.typeMismatch(tree, pt, failure)
35363538

35373539
if ctx.mode.is(Mode.ImplicitsEnabled) && tree.typeOpt.isValueType then
@@ -3556,7 +3558,7 @@ class Typer extends Namer
35563558
else recover(failure.reason)
35573559
}
35583560
else recover(NoMatchingImplicits)
3559-
}
3561+
end adaptToSubType
35603562

35613563
def adaptType(tp: Type): Tree = {
35623564
val tree1 =

0 commit comments

Comments
 (0)