Skip to content

Commit a70c821

Browse files
committed
Merge pull request scala#1521 from paulp/whats-in-a-name
Saving symbol lookup from typedIdent.
2 parents 2dd7142 + 578c4c6 commit a70c821

File tree

17 files changed

+588
-438
lines changed

17 files changed

+588
-438
lines changed

src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -802,7 +802,10 @@ trait ContextErrors {
802802
)
803803
}
804804

805-
def AccessError(tree: Tree, sym: Symbol, pre: Type, owner0: Symbol, explanation: String) = {
805+
def AccessError(tree: Tree, sym: Symbol, ctx: Context, explanation: String): AbsTypeError =
806+
AccessError(tree, sym, ctx.enclClass.owner.thisType, ctx.enclClass.owner, explanation)
807+
808+
def AccessError(tree: Tree, sym: Symbol, pre: Type, owner0: Symbol, explanation: String): AbsTypeError = {
806809
def errMsg = {
807810
val location = if (sym.isClassConstructor) owner0 else pre.widen.directObjectString
808811

@@ -835,7 +838,7 @@ trait ContextErrors {
835838

836839
// side-effect on the tree, break the overloaded type cycle in infer
837840
private def setErrorOnLastTry(lastTry: Boolean, tree: Tree) = if (lastTry) setError(tree)
838-
841+
839842
def NoBestMethodAlternativeError(tree: Tree, argtpes: List[Type], pt: Type, lastTry: Boolean) = {
840843
issueNormalTypeError(tree,
841844
applyErrorMsg(tree, " cannot be applied to ", argtpes, pt))
@@ -848,7 +851,7 @@ trait ContextErrors {
848851

849852
def AmbiguousMethodAlternativeError(tree: Tree, pre: Type, best: Symbol,
850853
firstCompeting: Symbol, argtpes: List[Type], pt: Type, lastTry: Boolean) = {
851-
854+
852855
if (!(argtpes exists (_.isErroneous)) && !pt.isErroneous) {
853856
val msg0 =
854857
"argument types " + argtpes.mkString("(", ",", ")") +
@@ -858,7 +861,7 @@ trait ContextErrors {
858861
setErrorOnLastTry(lastTry, tree)
859862
} else setError(tree) // do not even try further attempts because they should all fail
860863
// even if this is not the last attempt (because of the SO's possibility on the horizon)
861-
864+
862865
}
863866

864867
def NoBestExprAlternativeError(tree: Tree, pt: Type, lastTry: Boolean) = {
@@ -1191,7 +1194,7 @@ trait ContextErrors {
11911194
setError(arg)
11921195
} else arg
11931196
}
1194-
1197+
11951198
def WarnAfterNonSilentRecursiveInference(param: Symbol, arg: Tree)(implicit context: Context) = {
11961199
val note = "type-checking the invocation of "+ param.owner +" checks if the named argument expression '"+ param.name + " = ...' is a valid assignment\n"+
11971200
"in the current scope. The resulting type inference error (see above) can be fixed by providing an explicit type in the local definition for "+ param.name +"."

src/compiler/scala/tools/nsc/typechecker/Contexts.scala

Lines changed: 244 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,11 @@ trait Contexts { self: Analyzer =>
3535
val completeList = JavaLangPackage :: ScalaPackage :: PredefModule :: Nil
3636
}
3737

38+
def ambiguousImports(imp1: ImportInfo, imp2: ImportInfo) =
39+
LookupAmbiguous(s"it is imported twice in the same scope by\n$imp1\nand $imp2")
40+
def ambiguousDefnAndImport(owner: Symbol, imp: ImportInfo) =
41+
LookupAmbiguous(s"it is both defined in $owner and imported subsequently by \n$imp")
42+
3843
private val startContext = {
3944
NoContext.make(
4045
Template(List(), emptyValDef, List()) setSymbol global.NoSymbol setType global.NoType,
@@ -480,8 +485,7 @@ trait Contexts { self: Analyzer =>
480485
c
481486
}
482487

483-
/** Is `sym` accessible as a member of tree `site` with type
484-
* `pre` in current context?
488+
/** Is `sym` accessible as a member of `pre` in current context?
485489
*/
486490
def isAccessible(sym: Symbol, pre: Type, superAccess: Boolean = false): Boolean = {
487491
lastAccessCheckDetails = ""
@@ -632,7 +636,7 @@ trait Contexts { self: Analyzer =>
632636
case ImportSelector(from, _, to, _) :: sels1 =>
633637
var impls = collect(sels1) filter (info => info.name != from)
634638
if (to != nme.WILDCARD) {
635-
for (sym <- imp.importedSymbol(to).alternatives)
639+
for (sym <- importedAccessibleSymbol(imp, to).alternatives)
636640
if (isQualifyingImplicit(to, sym, pre, imported = true))
637641
impls = new ImplicitInfo(to, pre, sym) :: impls
638642
}
@@ -678,6 +682,241 @@ trait Contexts { self: Analyzer =>
678682
implicitsCache
679683
}
680684

685+
/** It's possible that seemingly conflicting identifiers are
686+
* identifiably the same after type normalization. In such cases,
687+
* allow compilation to proceed. A typical example is:
688+
* package object foo { type InputStream = java.io.InputStream }
689+
* import foo._, java.io._
690+
*/
691+
def isAmbiguousImport(imp1: ImportInfo, imp2: ImportInfo, name: Name): Boolean = {
692+
// The imported symbols from each import.
693+
def imp1Symbol = importedAccessibleSymbol(imp1, name)
694+
def imp2Symbol = importedAccessibleSymbol(imp2, name)
695+
// The types of the qualifiers from which the ambiguous imports come.
696+
// If the ambiguous name is a value, these must be the same.
697+
def t1 = imp1.qual.tpe
698+
def t2 = imp2.qual.tpe
699+
// The types of the ambiguous symbols, seen as members of their qualifiers.
700+
// If the ambiguous name is a monomorphic type, we can relax this far.
701+
def mt1 = t1 memberType imp1Symbol
702+
def mt2 = t2 memberType imp2Symbol
703+
704+
def characterize = List(
705+
s"types: $t1 =:= $t2 ${t1 =:= t2} members: ${mt1 =:= mt2}",
706+
s"member type 1: $mt1",
707+
s"member type 2: $mt2"
708+
).mkString("\n ")
709+
710+
imp1Symbol.exists && imp2Symbol.exists && (
711+
// The symbol names are checked rather than the symbols themselves because
712+
// each time an overloaded member is looked up it receives a new symbol.
713+
// So foo.member("x") != foo.member("x") if x is overloaded. This seems
714+
// likely to be the cause of other bugs too...
715+
if (t1 =:= t2 && imp1Symbol.name == imp2Symbol.name) {
716+
log(s"Suppressing ambiguous import: $t1 =:= $t2 && $imp1Symbol == $imp2Symbol")
717+
false
718+
}
719+
// Monomorphism restriction on types is in part because type aliases could have the
720+
// same target type but attach different variance to the parameters. Maybe it can be
721+
// relaxed, but doesn't seem worth it at present.
722+
else if (mt1 =:= mt2 && name.isTypeName && imp1Symbol.isMonomorphicType && imp2Symbol.isMonomorphicType) {
723+
log(s"Suppressing ambiguous import: $mt1 =:= $mt2 && $imp1Symbol and $imp2Symbol are equivalent")
724+
false
725+
}
726+
else {
727+
log(s"Import is genuinely ambiguous:\n " + characterize)
728+
true
729+
}
730+
)
731+
}
732+
733+
/** The symbol with name `name` imported via the import in `imp`,
734+
* if any such symbol is accessible from this context.
735+
*/
736+
def importedAccessibleSymbol(imp: ImportInfo, name: Name) = {
737+
imp importedSymbol name filter (s => isAccessible(s, imp.qual.tpe, superAccess = false))
738+
}
739+
740+
/** Is `sym` defined in package object of package `pkg`?
741+
* Since sym may be defined in some parent of the package object,
742+
* we cannot inspect its owner only; we have to go through the
743+
* info of the package object. However to avoid cycles we'll check
744+
* what other ways we can before pushing that way.
745+
*/
746+
def isInPackageObject(sym: Symbol, pkg: Symbol) = {
747+
val pkgClass = if (pkg.isTerm) pkg.moduleClass else pkg
748+
def matchesInfo = (
749+
pkg.isInitialized && {
750+
// need to be careful here to not get a cyclic reference during bootstrap
751+
val module = pkg.info member nme.PACKAGEkw
752+
module.isInitialized && (module.info.member(sym.name).alternatives contains sym)
753+
}
754+
)
755+
def inPackageObject(sym: Symbol) = (
756+
!sym.isPackage
757+
&& !sym.owner.isPackageClass
758+
&& (sym.owner ne NoSymbol)
759+
&& (sym.owner.owner == pkgClass || matchesInfo)
760+
)
761+
762+
pkgClass.isPackageClass && (
763+
if (sym.isOverloaded) sym.alternatives forall inPackageObject
764+
else inPackageObject(sym)
765+
)
766+
}
767+
768+
/** Find the symbol of a simple name starting from this context.
769+
* All names are filtered through the "qualifies" predicate,
770+
* the search continuing as long as no qualifying name is found.
771+
*/
772+
def lookupSymbol(name: Name, qualifies: Symbol => Boolean): NameLookup = {
773+
var lookupError: NameLookup = null // set to non-null if a definite error is encountered
774+
var inaccessible: NameLookup = null // records inaccessible symbol for error reporting in case none is found
775+
var defEntry: ScopeEntry = null // the scope entry of defSym, if defined in a local scope
776+
var defSym: Symbol = NoSymbol // the directly found symbol
777+
var pre: Type = NoPrefix // the prefix type of defSym, if a class member
778+
var cx: Context = this
779+
var needsQualifier = false // working around package object overloading bug
780+
781+
def defEntrySymbol = if (defEntry eq null) NoSymbol else defEntry.sym
782+
def localScopeDepth = if (defEntry eq null) 0 else cx.scope.nestingLevel - defEntry.owner.nestingLevel
783+
784+
def finish(qual: Tree, sym: Symbol): NameLookup = (
785+
if (lookupError ne null) lookupError
786+
else sym match {
787+
case NoSymbol if inaccessible ne null => inaccessible
788+
case NoSymbol => LookupNotFound
789+
case _ => LookupSucceeded(qual, sym)
790+
}
791+
)
792+
def isPackageOwnedInDifferentUnit(s: Symbol) = (
793+
s.isDefinedInPackage && (
794+
!currentRun.compiles(s)
795+
|| unit.exists && s.sourceFile != unit.source.file
796+
)
797+
)
798+
def requiresQualifier(s: Symbol) = needsQualifier || (
799+
s.owner.isClass
800+
&& !s.owner.isPackageClass
801+
&& !s.isTypeParameterOrSkolem
802+
)
803+
def lookupInPrefix(name: Name) = pre member name filter qualifies
804+
def accessibleInPrefix(s: Symbol) = isAccessible(s, pre, superAccess = false)
805+
806+
def correctForPackageObject(sym: Symbol): Symbol = {
807+
if (sym.isTerm && isInPackageObject(sym, pre.typeSymbol)) {
808+
val sym1 = lookupInPrefix(sym.name)
809+
if ((sym1 eq NoSymbol) || (sym eq sym1)) sym else {
810+
needsQualifier = true
811+
log(s"""
812+
| !!! Overloaded package object member resolved incorrectly.
813+
| prefix: $pre
814+
| Discarded: ${sym.defString}
815+
| Using: ${sym1.defString}
816+
""".stripMargin)
817+
sym1
818+
}
819+
}
820+
else sym
821+
}
822+
823+
def searchPrefix = {
824+
cx = cx.enclClass
825+
val found0 = lookupInPrefix(name)
826+
val found1 = found0 filter accessibleInPrefix
827+
if (found0.exists && !found1.exists && inaccessible == null)
828+
inaccessible = LookupInaccessible(found0, analyzer.lastAccessCheckDetails)
829+
830+
found1
831+
}
832+
// cx.scope eq null arises during FixInvalidSyms in Duplicators
833+
while (defSym == NoSymbol && (cx ne NoContext) && (cx.scope ne null)) {
834+
pre = cx.enclClass.prefix
835+
// !!! FIXME. This call to lookupEntry is at the root of all the
836+
// bad behavior with overloading in package objects. lookupEntry
837+
// just takes the first symbol it finds in scope, ignoring the rest.
838+
// When a selection on a package object arrives here, the first
839+
// overload is always chosen. "correctForPackageObject" exists to
840+
// undo that decision. Obviously it would be better not to do it in
841+
// the first place; however other things seem to be tied to obtaining
842+
// that ScopeEntry, specifically calculating the nesting depth.
843+
defEntry = cx.scope lookupEntry name
844+
defSym = defEntrySymbol filter qualifies map correctForPackageObject orElse searchPrefix
845+
if (!defSym.exists)
846+
cx = cx.outer
847+
}
848+
849+
val symbolDepth = cx.depth - localScopeDepth
850+
var impSym: Symbol = NoSymbol
851+
var imports = Context.this.imports // impSym != NoSymbol => it is imported from imports.head
852+
def imp1 = imports.head
853+
854+
while (!qualifies(impSym) && imports.nonEmpty && imp1.depth > symbolDepth) {
855+
impSym = importedAccessibleSymbol(imp1, name)
856+
if (!impSym.exists)
857+
imports = imports.tail
858+
}
859+
if (defSym.exists && impSym.exists) {
860+
// imported symbols take precedence over package-owned symbols in different compilation units.
861+
if (isPackageOwnedInDifferentUnit(defSym))
862+
defSym = NoSymbol
863+
// Defined symbols take precedence over erroneous imports.
864+
else if (impSym.isError || impSym.name == nme.CONSTRUCTOR)
865+
impSym = NoSymbol
866+
// Otherwise they are irreconcilably ambiguous
867+
else
868+
return ambiguousDefnAndImport(defSym.owner, imp1)
869+
}
870+
871+
// At this point only one or the other of defSym and impSym might be set.
872+
if (defSym.exists) {
873+
if (requiresQualifier(defSym))
874+
finish(gen.mkAttributedQualifier(pre), defSym)
875+
else
876+
finish(EmptyTree, defSym)
877+
}
878+
else if (impSym.exists) {
879+
// Imports against which we will test impSym for any ambiguities
880+
var importsTail = imports.tail
881+
val imp1Explicit = imp1 isExplicitImport name
882+
def imp2 = importsTail.head
883+
def sameDepth = imp1.depth == imp2.depth
884+
def isDone = importsTail.isEmpty || imp1Explicit && !sameDepth
885+
886+
while (lookupError == null && !isDone) {
887+
val other = importedAccessibleSymbol(imp2, name)
888+
// Ambiguity check between imports.
889+
// The same name imported again is potentially ambiguous if the name is:
890+
// - after explicit import, explicitly imported again at the same or lower depth
891+
// - after explicit import, wildcard imported at lower depth
892+
// - after wildcard import, wildcard imported at the same depth
893+
// Under all such conditions isAmbiguousImport is called, which will
894+
// examine the imports in case they are importing the same thing; if that
895+
// can't be established conclusively, an error is issued.
896+
if (qualifies(other)) {
897+
val imp2Explicit = imp2 isExplicitImport name
898+
val needsCheck = (
899+
if (sameDepth) imp1Explicit == imp2Explicit
900+
else imp1Explicit || imp2Explicit
901+
)
902+
log(s"Import ambiguity: imp1=$imp1, imp2=$imp2, sameDepth=$sameDepth, needsCheck=$needsCheck")
903+
if (needsCheck && isAmbiguousImport(imp1, imp2, name))
904+
lookupError = ambiguousImports(imp1, imp2)
905+
else if (imp2Explicit) {
906+
// if we weren't ambiguous and imp2 is explicit, imp2 replaces imp1
907+
// as the current winner.
908+
impSym = other
909+
imports = importsTail
910+
}
911+
}
912+
importsTail = importsTail.tail
913+
}
914+
// optimization: don't write out package prefixes
915+
finish(resetPos(imp1.qual.duplicate), impSym)
916+
}
917+
else finish(EmptyTree, NoSymbol)
918+
}
919+
681920
/**
682921
* Find a symbol in this context or one of its outers.
683922
*
@@ -705,8 +944,8 @@ trait Contexts { self: Analyzer =>
705944
/** The prefix expression */
706945
def qual: Tree = tree.symbol.info match {
707946
case ImportType(expr) => expr
708-
case ErrorType => tree setType NoType // fix for #2870
709-
case _ => throw new FatalError("symbol " + tree.symbol + " has bad type: " + tree.symbol.info) //debug
947+
case ErrorType => tree setType NoType // fix for #2870
948+
case _ => throw new FatalError("symbol " + tree.symbol + " has bad type: " + tree.symbol.info) //debug
710949
}
711950

712951
/** Is name imported explicitly, not via wildcard? */

src/compiler/scala/tools/nsc/typechecker/Modes.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,7 @@ trait Modes {
109109
final def inFunMode(mode: Int) = (mode & FUNmode) != 0
110110
final def inPolyMode(mode: Int) = (mode & POLYmode) != 0
111111
final def inPatternMode(mode: Int) = (mode & PATTERNmode) != 0
112+
final def inPatternNotFunMode(mode: Int) = inPatternMode(mode) && !inFunMode(mode)
112113
final def inExprModeOr(mode: Int, others: Int) = (mode & (EXPRmode | others)) != 0
113114
final def inExprModeButNot(mode: Int, prohibited: Int) =
114115
(mode & (EXPRmode | prohibited)) == EXPRmode

0 commit comments

Comments
 (0)