Skip to content

Commit c420b4c

Browse files
authored
Merge pull request #1465 from dotty-staging/fix-#1457
Fix #1457: Three incompatbilities with scalac
2 parents 186f955 + 062b413 commit c420b4c

File tree

9 files changed

+110
-51
lines changed

9 files changed

+110
-51
lines changed

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ class ImportInfo(symf: => Symbol, val selectors: List[untpd.Tree], val isRootImp
6464
myExcluded += name
6565
case Pair(Ident(from: TermName), Ident(to: TermName)) =>
6666
myMapped = myMapped.updated(to, from)
67+
myExcluded += from
6768
myOriginals += from
6869
case Ident(nme.WILDCARD) =>
6970
myWildcardImport = true

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

Lines changed: 91 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,13 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit
7272
*/
7373
private var importedFromRoot: Set[Symbol] = Set()
7474

75+
/** Temporary data item for single call to typed ident:
76+
* This symbol would be found under Scala2 mode, but is not
77+
* in dotty (because dotty conforms to spec section 2
78+
* wrt to package member resolution but scalac doe not).
79+
*/
80+
private var foundUnderScala2: Type = NoType
81+
7582
def newLikeThis: Typer = new Typer
7683

7784
/** Attribute an identifier consisting of a simple name or wildcard
@@ -133,14 +140,20 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit
133140
* imported by <tree>
134141
* or defined in <symbol>
135142
*/
136-
def bindingString(prec: Int, whereFound: Context, qualifier: String = "") =
143+
def bindingString(prec: Int, whereFound: Context, qualifier: String = "")(implicit ctx: Context) =
137144
if (prec == wildImport || prec == namedImport) ex"imported$qualifier by ${whereFound.importInfo}"
138145
else ex"defined$qualifier in ${whereFound.owner}"
139146

140147
/** Check that any previously found result from an inner context
141148
* does properly shadow the new one from an outer context.
149+
* @param found The newly found result
150+
* @param newPrec Its precedence
151+
* @param scala2pkg Special mode where we check members of the same package, but defined
152+
* in different compilation units under Scala2. If set, and the
153+
* previous and new contexts do not have the same scope, we select
154+
* the previous (inner) definition. This models what scalac does.
142155
*/
143-
def checkNewOrShadowed(found: Type, newPrec: Int): Type =
156+
def checkNewOrShadowed(found: Type, newPrec: Int, scala2pkg: Boolean = false)(implicit ctx: Context): Type =
144157
if (!previous.exists || ctx.typeComparer.isSameRef(previous, found)) found
145158
else if ((prevCtx.scope eq ctx.scope) &&
146159
(newPrec == definition ||
@@ -150,7 +163,7 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit
150163
found
151164
}
152165
else {
153-
if (!previous.isError && !found.isError) {
166+
if (!scala2pkg && !previous.isError && !found.isError) {
154167
error(
155168
ex"""reference to $name is ambiguous;
156169
|it is both ${bindingString(newPrec, ctx, "")}
@@ -163,7 +176,7 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit
163176
/** The type representing a named import with enclosing name when imported
164177
* from given `site` and `selectors`.
165178
*/
166-
def namedImportRef(site: Type, selectors: List[untpd.Tree]): Type = {
179+
def namedImportRef(site: Type, selectors: List[untpd.Tree])(implicit ctx: Context): Type = {
167180
def checkUnambiguous(found: Type) = {
168181
val other = namedImportRef(site, selectors.tail)
169182
if (other.exists && found.exists && (found != other))
@@ -190,7 +203,7 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit
190203
/** The type representing a wildcard import with enclosing name when imported
191204
* from given import info
192205
*/
193-
def wildImportRef(imp: ImportInfo): Type = {
206+
def wildImportRef(imp: ImportInfo)(implicit ctx: Context): Type = {
194207
if (imp.isWildcardImport) {
195208
val pre = imp.site
196209
if (!isDisabled(imp, pre) && !(imp.excluded contains name.toTermName) && name != nme.CONSTRUCTOR) {
@@ -204,54 +217,71 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit
204217
/** Is (some alternative of) the given predenotation `denot`
205218
* defined in current compilation unit?
206219
*/
207-
def isDefinedInCurrentUnit(denot: Denotation): Boolean = denot match {
220+
def isDefinedInCurrentUnit(denot: Denotation)(implicit ctx: Context): Boolean = denot match {
208221
case MultiDenotation(d1, d2) => isDefinedInCurrentUnit(d1) || isDefinedInCurrentUnit(d2)
209222
case denot: SingleDenotation => denot.symbol.sourceFile == ctx.source.file
210223
}
211224

212225
/** Is `denot` the denotation of a self symbol? */
213-
def isSelfDenot(denot: Denotation) = denot match {
226+
def isSelfDenot(denot: Denotation)(implicit ctx: Context) = denot match {
214227
case denot: SymDenotation => denot is SelfName
215228
case _ => false
216229
}
217230

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

257287
// begin typedIdent
@@ -264,12 +294,28 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit
264294
return typed(desugar.patternVar(tree), pt)
265295
}
266296

267-
val saved = importedFromRoot
268-
importedFromRoot = Set.empty
269297

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

274320
val ownType =
275321
if (rawType.exists)

tests/pending/hkt/hkt.scala renamed to tests/disabled/not-representable/hkt/hkt.scala

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
// This one is unavoidable. Dotty does not allow several overloaded
2+
// parameterless methods, so it picks the one in the subclass.
3+
14
import scala.language.higherKinds
25
// Minimal reproduction for:
36
// scala.collection.mutable.ArrayStack.empty[Int]

tests/pending/import-rewrite/compiler.error

Lines changed: 0 additions & 6 deletions
This file was deleted.
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
// This one should be rejected according to spec. The import takes precedence
2+
// over the type in the same package because the typeis declared in a
3+
// different compilation unit. scalac does not conform to spec here.
4+
package naming.resolution
5+
6+
import java.nio.file._ // Imports `Files`
7+
8+
object Resolution {
9+
def gimmeFiles: Files = Files.list(Paths.get("."))
10+
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
package naming
2+
3+
package object resolution {
4+
type Files = java.util.stream.Stream[java.nio.file.Path]
5+
}

0 commit comments

Comments
 (0)