Skip to content

Commit 56bd08f

Browse files
oderskyMasseGuillaume
authored andcommitted
Refactoring of findRef
Three goals: 1. Fix crasher in compileStdLib by saving and restoring foundUnderScala2 analogous to iportedFromRoot. 2. Make behavior the same as scalac under Scala2 mode - ListBuffer behaved differently before. 3. Make findRef faster by making it tail-recursive as long as nothing was found.
1 parent 3175d4c commit 56bd08f

File tree

1 file changed

+83
-58
lines changed

1 file changed

+83
-58
lines changed

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

Lines changed: 83 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit
7878
* in dotty (because dotty conforms to spec section 2
7979
* wrt to package member resolution but scalac doe not).
8080
*/
81-
private var foundUnderScala2: Type = _
81+
private var foundUnderScala2: Type = NoType
8282

8383
def newLikeThis: Typer = new Typer
8484

@@ -141,14 +141,20 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit
141141
* imported by <tree>
142142
* or defined in <symbol>
143143
*/
144-
def bindingString(prec: Int, whereFound: Context, qualifier: String = "") =
144+
def bindingString(prec: Int, whereFound: Context, qualifier: String = "")(implicit ctx: Context) =
145145
if (prec == wildImport || prec == namedImport) ex"imported$qualifier by ${whereFound.importInfo}"
146146
else ex"defined$qualifier in ${whereFound.owner}"
147147

148148
/** Check that any previously found result from an inner context
149149
* does properly shadow the new one from an outer context.
150+
* @param found The newly found result
151+
* @param newPrec Its precedence
152+
* @param scala2pkg Special mode where we check members of the same package, but defined
153+
* in different compilation units under Scala2. If set, and the
154+
* previous and new contexts do not have the same scope, we select
155+
* the previous (inner) definition. This models what scalac does.
150156
*/
151-
def checkNewOrShadowed(found: Type, newPrec: Int): Type =
157+
def checkNewOrShadowed(found: Type, newPrec: Int, scala2pkg: Boolean = false)(implicit ctx: Context): Type =
152158
if (!previous.exists || ctx.typeComparer.isSameRef(previous, found)) found
153159
else if ((prevCtx.scope eq ctx.scope) &&
154160
(newPrec == definition ||
@@ -158,7 +164,7 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit
158164
found
159165
}
160166
else {
161-
if (!previous.isError && !found.isError) {
167+
if (!scala2pkg && !previous.isError && !found.isError) {
162168
error(
163169
ex"""reference to $name is ambiguous;
164170
|it is both ${bindingString(newPrec, ctx, "")}
@@ -171,7 +177,7 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit
171177
/** The type representing a named import with enclosing name when imported
172178
* from given `site` and `selectors`.
173179
*/
174-
def namedImportRef(site: Type, selectors: List[untpd.Tree]): Type = {
180+
def namedImportRef(site: Type, selectors: List[untpd.Tree])(implicit ctx: Context): Type = {
175181
def checkUnambiguous(found: Type) = {
176182
val other = namedImportRef(site, selectors.tail)
177183
if (other.exists && found.exists && (found != other))
@@ -198,7 +204,7 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit
198204
/** The type representing a wildcard import with enclosing name when imported
199205
* from given import info
200206
*/
201-
def wildImportRef(imp: ImportInfo): Type = {
207+
def wildImportRef(imp: ImportInfo)(implicit ctx: Context): Type = {
202208
if (imp.isWildcardImport) {
203209
val pre = imp.site
204210
if (!isDisabled(imp, pre) && !(imp.excluded contains name.toTermName) && name != nme.CONSTRUCTOR) {
@@ -212,58 +218,71 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit
212218
/** Is (some alternative of) the given predenotation `denot`
213219
* defined in current compilation unit?
214220
*/
215-
def isDefinedInCurrentUnit(denot: Denotation): Boolean = denot match {
221+
def isDefinedInCurrentUnit(denot: Denotation)(implicit ctx: Context): Boolean = denot match {
216222
case MultiDenotation(d1, d2) => isDefinedInCurrentUnit(d1) || isDefinedInCurrentUnit(d2)
217223
case denot: SingleDenotation => denot.symbol.sourceFile == ctx.source.file
218224
}
219225

220226
/** Is `denot` the denotation of a self symbol? */
221-
def isSelfDenot(denot: Denotation) = denot match {
227+
def isSelfDenot(denot: Denotation)(implicit ctx: Context) = denot match {
222228
case denot: SymDenotation => denot is SelfName
223229
case _ => false
224230
}
225231

226-
// begin findRef
227-
if (ctx.scope == null) previous
228-
else {
229-
val outer = ctx.outer
230-
if ((ctx.scope ne outer.scope) || (ctx.owner ne outer.owner)) {
231-
val defDenot = ctx.denotNamed(name)
232-
if (qualifies(defDenot)) {
233-
val curOwner = ctx.owner
234-
val found =
235-
if (isSelfDenot(defDenot)) curOwner.enclosingClass.thisType
236-
else curOwner.thisType.select(name, defDenot)
237-
if (!(curOwner is Package) || isDefinedInCurrentUnit(defDenot))
238-
return checkNewOrShadowed(found, definition) // no need to go further out, we found highest prec entry
239-
else {
240-
if (ctx.scala2Mode)
241-
foundUnderScala2 = checkNewOrShadowed(found, definition)
242-
if (defDenot.symbol is Package)
243-
return checkNewOrShadowed(previous orElse found, packageClause)
244-
else if (prevPrec < packageClause)
245-
return findRef(found, packageClause, ctx)(outer)
232+
/** Would import of kind `prec` be not shadowed by a nested higher-precedence definition? */
233+
def isPossibleImport(prec: Int)(implicit ctx: Context) =
234+
prevPrec < prec || prevPrec == prec && (prevCtx.scope eq ctx.scope)
235+
236+
@tailrec def loop(implicit ctx: Context): Type = {
237+
if (ctx.scope == null) previous
238+
else {
239+
val outer = ctx.outer
240+
var result: Type = NoType
241+
242+
// find definition
243+
if ((ctx.scope ne outer.scope) || (ctx.owner ne outer.owner)) {
244+
val defDenot = ctx.denotNamed(name)
245+
if (qualifies(defDenot)) {
246+
val curOwner = ctx.owner
247+
val found =
248+
if (isSelfDenot(defDenot)) curOwner.enclosingClass.thisType
249+
else curOwner.thisType.select(name, defDenot)
250+
if (!(curOwner is Package) || isDefinedInCurrentUnit(defDenot))
251+
result = checkNewOrShadowed(found, definition) // no need to go further out, we found highest prec entry
252+
else {
253+
if (ctx.scala2Mode && !foundUnderScala2.exists)
254+
foundUnderScala2 = checkNewOrShadowed(found, definition, scala2pkg = true)
255+
if (defDenot.symbol is Package)
256+
result = checkNewOrShadowed(previous orElse found, packageClause)
257+
else if (prevPrec < packageClause)
258+
result = findRef(found, packageClause, ctx)(outer)
259+
}
246260
}
247261
}
248-
}
249-
val curImport = ctx.importInfo
250-
if (ctx.owner.is(Package) && curImport != null && curImport.isRootImport && previous.exists)
251-
return previous // no more conflicts possible in this case
252-
// would import of kind `prec` be not shadowed by a nested higher-precedence definition?
253-
def isPossibleImport(prec: Int) =
254-
prevPrec < prec || prevPrec == prec && (prevCtx.scope eq ctx.scope)
255-
if (isPossibleImport(namedImport) && (curImport ne outer.importInfo) && !curImport.sym.isCompleting) {
256-
val namedImp = namedImportRef(curImport.site, curImport.selectors)
257-
if (namedImp.exists)
258-
return findRef(checkNewOrShadowed(namedImp, namedImport), namedImport, ctx)(outer)
259-
if (isPossibleImport(wildImport)) {
260-
val wildImp = wildImportRef(curImport)
261-
if (wildImp.exists)
262-
return findRef(checkNewOrShadowed(wildImp, wildImport), wildImport, ctx)(outer)
262+
263+
if (result.exists) result
264+
else { // find import
265+
val curImport = ctx.importInfo
266+
if (ctx.owner.is(Package) && curImport != null && curImport.isRootImport && previous.exists)
267+
previous // no more conflicts possible in this case
268+
else if (isPossibleImport(namedImport) && (curImport ne outer.importInfo) && !curImport.sym.isCompleting) {
269+
val namedImp = namedImportRef(curImport.site, curImport.selectors)
270+
if (namedImp.exists)
271+
findRef(checkNewOrShadowed(namedImp, namedImport), namedImport, ctx)(outer)
272+
else if (isPossibleImport(wildImport)) {
273+
val wildImp = wildImportRef(curImport)
274+
if (wildImp.exists)
275+
findRef(checkNewOrShadowed(wildImp, wildImport), wildImport, ctx)(outer)
276+
else loop(outer)
277+
}
278+
else loop(outer)
279+
}
280+
else loop(outer)
263281
}
264282
}
265-
findRef(previous, prevPrec, prevCtx)(outer)
266283
}
284+
285+
loop
267286
}
268287

269288
// begin typedIdent
@@ -276,21 +295,27 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit
276295
return typed(desugar.patternVar(tree), pt)
277296
}
278297

279-
val saved = importedFromRoot
280-
importedFromRoot = Set.empty
281-
282-
foundUnderScala2 = NoType
283-
284-
var rawType =
285-
try findRef(NoType, BindingPrec.nothingBound, NoContext)
286-
finally importedFromRoot = saved
287298

288-
if (foundUnderScala2.exists && (foundUnderScala2 ne rawType)) {
289-
ctx.migrationWarning(
290-
ex"""Name resolution will change.
291-
| currently selected : $foundUnderScala2
292-
| in the future, without -language:Scala2: $rawType""", tree.pos)
293-
rawType = foundUnderScala2
299+
val rawType = {
300+
val saved1 = importedFromRoot
301+
val saved2 = foundUnderScala2
302+
importedFromRoot = Set.empty
303+
foundUnderScala2 = NoType
304+
try {
305+
var found = findRef(NoType, BindingPrec.nothingBound, NoContext)
306+
if (foundUnderScala2.exists && !(foundUnderScala2 =:= found)) {
307+
ctx.migrationWarning(
308+
ex"""Name resolution will change.
309+
| currently selected : $foundUnderScala2
310+
| in the future, without -language:Scala2: $found""", tree.pos)
311+
found = foundUnderScala2
312+
}
313+
found
314+
}
315+
finally {
316+
importedFromRoot = saved1
317+
foundUnderScala2 = saved2
318+
}
294319
}
295320

296321
val ownType =

0 commit comments

Comments
 (0)