diff options
author | Paul Phillips <paulp@improving.org> | 2012-12-05 12:34:13 -0800 |
---|---|---|
committer | Paul Phillips <paulp@improving.org> | 2012-12-05 13:13:55 -0800 |
commit | c42c1742e26dc47f940e4003e4ca25e8c738796d (patch) | |
tree | 637acdd45dbf93ba87ae6cb67d4237da652ac8c1 /src/compiler | |
parent | 4b2330b3d3db4263a8b1e19b792596dd60d79045 (diff) | |
parent | b84ecc5d1afcb71dd4047de9f1cc49060835d3df (diff) | |
download | scala-c42c1742e26dc47f940e4003e4ca25e8c738796d.tar.gz scala-c42c1742e26dc47f940e4003e4ca25e8c738796d.tar.bz2 scala-c42c1742e26dc47f940e4003e4ca25e8c738796d.zip |
Merge branch 'merge-2.10-wip' into merge-2.10
* merge-2.10-wip:
Fixing OSGi distribution.
Fix for rangepos crasher.
SI-6685 fixes error handling in typedApply
Test cases for SI-5726, SI-5733, SI-6320, SI-6551, SI-6722.
Asserts about Tree qualifiers.
Fix for SI-6731, dropped trees in selectDynamic.
neg test added
SI-5753 macros cannot be loaded when inherited from a class or a trait
Take advantage of the margin stripping interpolator.
Adds a margin stripping string interpolator.
SI-6718 fixes a volatile test
Mark pattern matcher synthetics as SYNTHETIC.
Set symbol flags at creation.
Fix for SI-6687, wrong isVar logic.
Fix for SI-6706, Symbol breakage under GC.
findEntry implementation code more concise and DRYer.
Fix for SI-6357, cycle with value classes.
Refactoring of adaptMethod
SI-6677 Insert required cast in `new qual.foo.T`
Conflicts:
src/compiler/scala/tools/nsc/transform/Erasure.scala
src/compiler/scala/tools/nsc/typechecker/Typers.scala
src/reflect/scala/reflect/internal/SymbolTable.scala
src/reflect/scala/reflect/internal/util/package.scala
test/files/neg/gadts1.check
Diffstat (limited to 'src/compiler')
13 files changed, 104 insertions, 112 deletions
diff --git a/src/compiler/scala/reflect/reify/Errors.scala b/src/compiler/scala/reflect/reify/Errors.scala index 0ff098e1da..3a68794c97 100644 --- a/src/compiler/scala/reflect/reify/Errors.scala +++ b/src/compiler/scala/reflect/reify/Errors.scala @@ -27,10 +27,10 @@ trait Errors { } def CannotConvertManifestToTagWithoutScalaReflect(tpe: Type, manifestInScope: Tree) = { - val msg = s""" - |to create a type tag here, it is necessary to interoperate with the manifest `$manifestInScope` in scope. - |however manifest -> typetag conversion requires Scala reflection, which is not present on the classpath. - |to proceed put scala-reflect.jar on your compilation classpath and recompile.""".trim.stripMargin + val msg = + sm"""to create a type tag here, it is necessary to interoperate with the manifest `$manifestInScope` in scope. + |however manifest -> typetag conversion requires Scala reflection, which is not present on the classpath. + |to proceed put scala-reflect.jar on your compilation classpath and recompile.""" throw new ReificationException(defaultErrorPosition, msg) } diff --git a/src/compiler/scala/tools/nsc/ast/Trees.scala b/src/compiler/scala/tools/nsc/ast/Trees.scala index c78c40dcd3..e796258967 100644 --- a/src/compiler/scala/tools/nsc/ast/Trees.scala +++ b/src/compiler/scala/tools/nsc/ast/Trees.scala @@ -56,7 +56,7 @@ trait Trees extends scala.reflect.internal.Trees { self: Global => * The class `C` is stored as a tree attachment. */ case class InjectDerivedValue(arg: Tree) - extends SymTree + extends SymTree with TermTree class PostfixSelect(qual: Tree, name: Name) extends Select(qual, name) diff --git a/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala b/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala index f430f1fc34..9bb7e1c3df 100644 --- a/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala +++ b/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala @@ -1898,7 +1898,7 @@ self => case _ => } val typeAppliedTree = in.token match { - case LBRACKET => atPos(start, in.offset)(TypeApply(convertToTypeId(t), typeArgs())) + case LBRACKET => atPos(start, in.offset)(AppliedTypeTree(convertToTypeId(t), typeArgs())) case _ => t } in.token match { diff --git a/src/compiler/scala/tools/nsc/transform/Erasure.scala b/src/compiler/scala/tools/nsc/transform/Erasure.scala index cfc3d0a377..ba799f9186 100644 --- a/src/compiler/scala/tools/nsc/transform/Erasure.scala +++ b/src/compiler/scala/tools/nsc/transform/Erasure.scala @@ -437,19 +437,19 @@ abstract class Erasure extends AddInterfaces noclash = false unit.error( if (member.owner == root) member.pos else root.pos, - s"""bridge generated for member ${fulldef(member)} - |which overrides ${fulldef(other)} - |clashes with definition of $what; - |both have erased type ${exitingPostErasure(bridge.tpe)}""".stripMargin) + sm"""bridge generated for member ${fulldef(member)} + |which overrides ${fulldef(other)} + |clashes with definition of $what; + |both have erased type ${exitingPostErasure(bridge.tpe)}""") } for (bc <- root.baseClasses) { if (settings.debug.value) exitingPostErasure(println( - s"""check bridge overrides in $bc - ${bc.info.nonPrivateDecl(bridge.name)} - ${site.memberType(bridge)} - ${site.memberType(bc.info.nonPrivateDecl(bridge.name) orElse IntClass)} - ${(bridge.matchingSymbol(bc, site))}""".stripMargin)) + sm"""check bridge overrides in $bc + |${bc.info.nonPrivateDecl(bridge.name)} + |${site.memberType(bridge)} + |${site.memberType(bc.info.nonPrivateDecl(bridge.name) orElse IntClass)} + |${(bridge.matchingSymbol(bc, site))}""")) def overriddenBy(sym: Symbol) = sym.matchingSymbol(bc, site).alternatives filter (sym => !sym.isBridge) @@ -693,7 +693,7 @@ abstract class Erasure extends AddInterfaces adaptToType(unbox(tree, pt), pt) else if (isPrimitiveValueType(tree.tpe) && !isPrimitiveValueType(pt)) { adaptToType(box(tree, pt.toString), pt) - } else if (tree.tpe.isInstanceOf[MethodType] && tree.tpe.params.isEmpty) { + } else if (isMethodTypeWithEmptyParams(tree.tpe)) { // [H] this assert fails when trying to typecheck tree !(SomeClass.this.bitmap) for single lazy val //assert(tree.symbol.isStable, "adapt "+tree+":"+tree.tpe+" to "+pt) adaptToType(Apply(tree, List()) setPos tree.pos setType tree.tpe.resultType, pt) @@ -774,16 +774,21 @@ abstract class Erasure extends AddInterfaces else if (!isPrimitiveValueType(qual1.tpe) && isPrimitiveValueMember(tree.symbol)) qual1 = unbox(qual1, tree.symbol.owner.tpe) - if (isPrimitiveValueMember(tree.symbol) && !isPrimitiveValueType(qual1.tpe)) + def selectFrom(qual: Tree) = treeCopy.Select(tree, qual, name) + + if (isPrimitiveValueMember(tree.symbol) && !isPrimitiveValueType(qual1.tpe)) { tree.symbol = NoSymbol - else if (qual1.tpe.isInstanceOf[MethodType] && qual1.tpe.params.isEmpty) { + selectFrom(qual1) + } else if (isMethodTypeWithEmptyParams(qual1.tpe)) { assert(qual1.symbol.isStable, qual1.symbol); - qual1 = Apply(qual1, List()) setPos qual1.pos setType qual1.tpe.resultType + val applied = Apply(qual1, List()) setPos qual1.pos setType qual1.tpe.resultType + adaptMember(selectFrom(applied)) } else if (!(qual1.isInstanceOf[Super] || (qual1.tpe.typeSymbol isSubClass tree.symbol.owner))) { assert(tree.symbol.owner != ArrayClass) - qual1 = cast(qual1, tree.symbol.owner.tpe) + selectFrom(cast(qual1, tree.symbol.owner.tpe)) + } else { + selectFrom(qual1) } - treeCopy.Select(tree, qual1, name) } case SelectFromArray(qual, name, erasure) => var qual1 = typedQualifier(qual) @@ -861,6 +866,11 @@ abstract class Erasure extends AddInterfaces tree1 } } + + private def isMethodTypeWithEmptyParams(tpe: Type) = tpe match { + case MethodType(Nil, _) => true + case _ => false + } } /** The erasure transformer */ diff --git a/src/compiler/scala/tools/nsc/transform/UnCurry.scala b/src/compiler/scala/tools/nsc/transform/UnCurry.scala index 4f889a1d86..8ae9490dbe 100644 --- a/src/compiler/scala/tools/nsc/transform/UnCurry.scala +++ b/src/compiler/scala/tools/nsc/transform/UnCurry.scala @@ -371,7 +371,7 @@ abstract class UnCurry extends InfoTransform } val isDefinedAtMethodDef = { - val methSym = anonClass.newMethod(nme.isDefinedAt, fun.pos, FINAL) + val methSym = anonClass.newMethod(nme.isDefinedAt, fun.pos, FINAL | SYNTHETIC) val params = methSym newSyntheticValueParams formals methSym setInfoAndEnter MethodType(params, BooleanClass.tpe) diff --git a/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala b/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala index bfc9f08553..a6d11f13d4 100644 --- a/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala +++ b/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala @@ -106,9 +106,9 @@ trait ContextErrors { s"$name extends Any, not AnyRef" ) if (isPrimitiveValueType(found) || isTrivialTopType(tp)) "" else "\n" + - s"""|Note that $what. - |Such types can participate in value classes, but instances - |cannot appear in singleton types or in reference comparisons.""".stripMargin + sm"""|Note that $what. + |Such types can participate in value classes, but instances + |cannot appear in singleton types or in reference comparisons.""" } import ErrorUtils._ @@ -1125,9 +1125,9 @@ trait ContextErrors { (isView: Boolean, pt: Type, tree: Tree)(implicit context0: Context) = { if (!info1.tpe.isErroneous && !info2.tpe.isErroneous) { def coreMsg = - s"""| $pre1 ${info1.sym.fullLocationString} of type ${info1.tpe} - | $pre2 ${info2.sym.fullLocationString} of type ${info2.tpe} - | $trailer""".stripMargin + sm"""| $pre1 ${info1.sym.fullLocationString} of type ${info1.tpe} + | $pre2 ${info2.sym.fullLocationString} of type ${info2.tpe} + | $trailer""" def viewMsg = { val found :: req :: _ = pt.typeArgs def explanation = { @@ -1138,19 +1138,19 @@ trait ContextErrors { // involving Any, are further explained from foundReqMsg. if (AnyRefClass.tpe <:< req) ( if (sym == AnyClass || sym == UnitClass) ( - s"""|Note: ${sym.name} is not implicitly converted to AnyRef. You can safely - |pattern match `x: AnyRef` or cast `x.asInstanceOf[AnyRef]` to do so.""".stripMargin + sm"""|Note: ${sym.name} is not implicitly converted to AnyRef. You can safely + |pattern match `x: AnyRef` or cast `x.asInstanceOf[AnyRef]` to do so.""" ) else boxedClass get sym map (boxed => - s"""|Note: an implicit exists from ${sym.fullName} => ${boxed.fullName}, but - |methods inherited from Object are rendered ambiguous. This is to avoid - |a blanket implicit which would convert any ${sym.fullName} to any AnyRef. - |You may wish to use a type ascription: `x: ${boxed.fullName}`.""".stripMargin + sm"""|Note: an implicit exists from ${sym.fullName} => ${boxed.fullName}, but + |methods inherited from Object are rendered ambiguous. This is to avoid + |a blanket implicit which would convert any ${sym.fullName} to any AnyRef. + |You may wish to use a type ascription: `x: ${boxed.fullName}`.""" ) getOrElse "" ) else - s"""|Note that implicit conversions are not applicable because they are ambiguous: - |${coreMsg}are possible conversion functions from $found to $req""".stripMargin + sm"""|Note that implicit conversions are not applicable because they are ambiguous: + |${coreMsg}are possible conversion functions from $found to $req""" } typeErrorMsg(found, req, infer.isPossiblyMissingArgs(found, req)) + ( if (explanation == "") "" else "\n" + explanation diff --git a/src/compiler/scala/tools/nsc/typechecker/Implicits.scala b/src/compiler/scala/tools/nsc/typechecker/Implicits.scala index eb45f9b847..739e28bf0c 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Implicits.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Implicits.scala @@ -1298,17 +1298,17 @@ trait Implicits { else { if (ReflectRuntimeUniverse == NoSymbol) { // todo. write a test for this - context.error(pos, s""" - |to create a manifest here, it is necessary to interoperate with the type tag `$tagInScope` in scope. - |however typetag -> manifest conversion requires Scala reflection, which is not present on the classpath. - |to proceed put scala-reflect.jar on your compilation classpath and recompile.""".trim.stripMargin) + context.error(pos, + sm"""to create a manifest here, it is necessary to interoperate with the type tag `$tagInScope` in scope. + |however typetag -> manifest conversion requires Scala reflection, which is not present on the classpath. + |to proceed put scala-reflect.jar on your compilation classpath and recompile.""") return SearchFailure } if (resolveClassTag(pos, tp, allowMaterialization = true) == EmptyTree) { - context.error(pos, s""" - |to create a manifest here, it is necessary to interoperate with the type tag `$tagInScope` in scope. - |however typetag -> manifest conversion requires a class tag for the corresponding type to be present. - |to proceed add a class tag to the type `$tp` (e.g. by introducing a context bound) and recompile.""".trim.stripMargin) + context.error(pos, + sm"""to create a manifest here, it is necessary to interoperate with the type tag `$tagInScope` in scope. + |however typetag -> manifest conversion requires a class tag for the corresponding type to be present. + |to proceed add a class tag to the type `$tp` (e.g. by introducing a context bound) and recompile.""") return SearchFailure } val cm = typed(Ident(ReflectRuntimeCurrentMirror)) diff --git a/src/compiler/scala/tools/nsc/typechecker/Macros.scala b/src/compiler/scala/tools/nsc/typechecker/Macros.scala index 4b534b0d2e..e40d978e6d 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Macros.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Macros.scala @@ -117,16 +117,9 @@ trait Macros extends scala.tools.reflect.FastTrack with Traces { } def pickle(macroImplRef: Tree): Tree = { - val macroImpl = macroImplRef.symbol + val MacroImplReference(owner, macroImpl, targs) = macroImplRef val paramss = macroImpl.paramss - // this logic relies on the assumptions that were valid for the old macro prototype - // namely that macro implementations can only be defined in top-level classes and modules - // with the new prototype that materialized in a SIP, macros need to be statically accessible, which is different - // for example, a macro def could be defined in a trait that is implemented by an object - // there are some more clever cases when seemingly non-static method ends up being statically accessible - // however, the code below doesn't account for these guys, because it'd take a look of time to get it right - // for now I leave it as a todo and move along to more the important stuff // todo. refactor when fixing SI-5498 def className: String = { def loop(sym: Symbol): String = sym match { @@ -138,7 +131,7 @@ trait Macros extends scala.tools.reflect.FastTrack with Traces { loop(sym.owner) + separator + sym.javaSimpleName.toString } - loop(macroImpl.owner.enclClass) + loop(owner) } def signature: List[Int] = { @@ -159,7 +152,7 @@ trait Macros extends scala.tools.reflect.FastTrack with Traces { // I just named it "macro", because it's macro-related, but I could as well name it "foobar" val nucleus = Ident(newTermName("macro")) val wrapped = Apply(nucleus, payload map { case (k, v) => Assign(pickleAtom(k), pickleAtom(v)) }) - val pickle = gen.mkTypeApply(wrapped, treeInfo.typeArguments(macroImplRef.duplicate)) + val pickle = gen.mkTypeApply(wrapped, targs map (_.duplicate)) // assign NoType to all freshly created AST nodes // otherwise pickler will choke on tree.tpe being null diff --git a/src/compiler/scala/tools/nsc/typechecker/PatternMatching.scala b/src/compiler/scala/tools/nsc/typechecker/PatternMatching.scala index c5245c4e9e..42c34526d7 100644 --- a/src/compiler/scala/tools/nsc/typechecker/PatternMatching.scala +++ b/src/compiler/scala/tools/nsc/typechecker/PatternMatching.scala @@ -1151,7 +1151,7 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL // ExplicitOuter replaces `Select(q, outerSym) OBJ_EQ expectedPrefix` by `Select(q, outerAccessor(outerSym.owner)) OBJ_EQ expectedPrefix` // if there's an outer accessor, otherwise the condition becomes `true` -- TODO: can we improve needsOuterTest so there's always an outerAccessor? - val outer = expectedTp.typeSymbol.newMethod(vpmName.outer) setInfo expectedTp.prefix setFlag SYNTHETIC | ARTIFACT + val outer = expectedTp.typeSymbol.newMethod(vpmName.outer, newFlags = SYNTHETIC | ARTIFACT) setInfo expectedTp.prefix (Select(codegen._asInstanceOf(testedBinder, expectedTp), outer)) OBJ_EQ expectedOuter } @@ -1413,7 +1413,7 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL // assert(owner ne null); assert(owner ne NoSymbol) def freshSym(pos: Position, tp: Type = NoType, prefix: String = "x") = - NoSymbol.newTermSymbol(freshName(prefix), pos) setInfo tp + NoSymbol.newTermSymbol(freshName(prefix), pos, newFlags = SYNTHETIC) setInfo tp def newSynthCaseLabel(name: String) = NoSymbol.newLabel(freshName(name), NoPosition) setFlag treeInfo.SYNTH_CASE_FLAGS @@ -3600,7 +3600,7 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL */ def matcher(scrut: Tree, scrutSym: Symbol, restpe: Type)(cases: List[Casegen => Tree], matchFailGen: Option[Tree => Tree]): Tree = { val matchEnd = newSynthCaseLabel("matchEnd") - val matchRes = NoSymbol.newValueParameter(newTermName("x"), NoPosition, SYNTHETIC) setInfo restpe.withoutAnnotations + val matchRes = NoSymbol.newValueParameter(newTermName("x"), NoPosition, newFlags = SYNTHETIC) setInfo restpe.withoutAnnotations matchEnd setInfo MethodType(List(matchRes), restpe) def newCaseSym = newSynthCaseLabel("case") setInfo MethodType(Nil, restpe) diff --git a/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala b/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala index 7a7c7c7d25..0ae225ccee 100644 --- a/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala +++ b/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala @@ -1382,8 +1382,8 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans private def checkCompileTimeOnly(sym: Symbol, pos: Position) = { if (sym.isCompileTimeOnly) { def defaultMsg = - s"""|Reference to ${sym.fullLocationString} should not have survived past type checking, - |it should have been processed and eliminated during expansion of an enclosing macro.""".stripMargin + sm"""Reference to ${sym.fullLocationString} should not have survived past type checking, + |it should have been processed and eliminated during expansion of an enclosing macro.""" // The getOrElse part should never happen, it's just here as a backstop. unit.error(pos, sym.compileTimeOnlyMessage getOrElse defaultMsg) } diff --git a/src/compiler/scala/tools/nsc/typechecker/SuperAccessors.scala b/src/compiler/scala/tools/nsc/typechecker/SuperAccessors.scala index 5fb9a5e67e..0992cd7955 100644 --- a/src/compiler/scala/tools/nsc/typechecker/SuperAccessors.scala +++ b/src/compiler/scala/tools/nsc/typechecker/SuperAccessors.scala @@ -525,8 +525,8 @@ abstract class SuperAccessors extends transform.Transform with transform.TypingT } def isJavaProtected = host.isTrait && sym.isJavaDefined && { restrictionError(pos, unit, - s"""|$clazz accesses protected $sym inside a concrete trait method. - |Add an accessor in a class extending ${sym.enclClass} as a workaround.""".stripMargin + sm"""$clazz accesses protected $sym inside a concrete trait method. + |Add an accessor in a class extending ${sym.enclClass} as a workaround.""" ) true } diff --git a/src/compiler/scala/tools/nsc/typechecker/TreeCheckers.scala b/src/compiler/scala/tools/nsc/typechecker/TreeCheckers.scala index be7554abe2..fb95c952d2 100644 --- a/src/compiler/scala/tools/nsc/typechecker/TreeCheckers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/TreeCheckers.scala @@ -268,11 +268,9 @@ abstract class TreeCheckers extends Analyzer { if (sym.owner != currentOwner) { val expected = currentOwner.ownerChain find (x => cond(x)) getOrElse { fail("DefTree can't find owner: ") ; NoSymbol } if (sym.owner != expected) - fail("""| - | currentOwner chain: %s - | symbol chain: %s""".stripMargin.format( - currentOwner.ownerChain take 3 mkString " -> ", - sym.ownerChain mkString " -> ") + fail(sm"""| + | currentOwner chain: ${currentOwner.ownerChain take 3 mkString " -> "} + | symbol chain: ${sym.ownerChain mkString " -> "}""" ) } } diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala index 42c7d4a6b8..e534e36a0d 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala @@ -3763,54 +3763,45 @@ trait Typers extends Modes with Adaptations with Tags { * */ def mkInvoke(cxTree: Tree, tree: Tree, qual: Tree, name: Name): Option[Tree] = { - debuglog(s"dyna.mkInvoke($cxTree, $tree, $qual, $name)") + log(s"dyna.mkInvoke($cxTree, $tree, $qual, $name)") + val treeSelection = treeInfo.methPart(tree) + def isDesugaredApply = treeSelection match { + case Select(`qual`, nme.apply) => true + case _ => false + } acceptsApplyDynamicWithType(qual, name) map { tp => - // tp eq NoType => can call xxxDynamic, but not passing any type args (unless specified explicitly by the user) - // in scala-virtualized, when not NoType, tp is passed as type argument (for selection on a staged Struct) - - // strip off type application -- we're not doing much with outer, - // so don't bother preserving cxTree's attributes etc - val cxTree1 = cxTree match { - case t: ValOrDefDef => t.rhs - case t => t - } - val cxTree2 = cxTree1 match { - case Typed(t, tpe) => t // ignore outer type annotation - case t => t - } - val (outer, explicitTargs) = cxTree2 match { - case TypeApply(fun, targs) => (fun, targs) - case Apply(TypeApply(fun, targs), args) => (Apply(fun, args), targs) - case Select(TypeApply(fun, targs), nme) => (Select(fun, nme), targs) - case t => (t, Nil) - } - def hasNamedArg(as: List[Tree]) = as.collectFirst{case AssignOrNamedArg(lhs, rhs) =>}.nonEmpty - - def desugaredApply = tree match { - case Select(`qual`, nme.apply) => true - case _ => false + // If tp == NoType, pass only explicit type arguments to applyXXX. Not used at all + // here - it is for scala-virtualized, where tp will be passed as an argument (for + // selection on a staged Struct) + def hasNamed(args: List[Tree]): Boolean = args exists (_.isInstanceOf[AssignOrNamedArg]) + // not supported: foo.bar(a1,..., an: _*) + def hasStar(args: List[Tree]) = treeInfo.isWildcardStarArgList(args) + def applyOp(args: List[Tree]) = if (hasNamed(args)) nme.applyDynamicNamed else nme.applyDynamic + def matches(t: Tree) = isDesugaredApply || treeInfo.methPart(t) == treeSelection + + /** Note that the trees which arrive here are potentially some distance from + * the trees of direct interest. `cxTree` is some enclosing expression which + * may apparently be arbitrarily larger than `tree`; and `tree` itself is + * too small, having at least in some cases lost its explicit type parameters. + * This logic is designed to use `tree` to pinpoint the immediately surrounding + * Apply/TypeApply/Select node, and only then creates the dynamic call. + * See SI-6731 among others. + */ + def findSelection(t: Tree): Option[(TermName, Tree)] = t match { + case Apply(fn, args) if hasStar(args) => DynamicVarArgUnsupported(tree, applyOp(args)) ; None + case Apply(fn, args) if matches(fn) => Some((applyOp(args), fn)) + case Assign(lhs, _) if matches(lhs) => Some((nme.updateDynamic, lhs)) + case _ if matches(t) => Some((nme.selectDynamic, t)) + case _ => t.children flatMap findSelection headOption } - // note: context.tree includes at most one Apply node - // thus, we can't use it to detect we're going to receive named args in expressions such as: - // qual.sel(a)(a2, arg2 = "a2") - val oper = outer match { - case Apply(q, as) if q == tree || desugaredApply => - val oper = - if (hasNamedArg(as)) nme.applyDynamicNamed - else nme.applyDynamic - // not supported: foo.bar(a1,..., an: _*) - if (treeInfo.isWildcardStarArgList(as)) { - DynamicVarArgUnsupported(tree, oper) - return Some(setError(tree)) - } else oper - case Assign(`tree`, _) => nme.updateDynamic - case _ => nme.selectDynamic + findSelection(cxTree) match { + case Some((opName, tapply)) => + val targs = treeInfo.typeArguments(tapply) + val fun = gen.mkTypeApply(Select(qual, opName), targs) + atPos(qual.pos)(Apply(fun, Literal(Constant(name.decode)) :: Nil)) + case _ => + setError(tree) } - - val dynSel = Select(qual, oper) - val tappSel = if (explicitTargs.nonEmpty) TypeApply(dynSel, explicitTargs) else dynSel - - atPos(qual.pos)(Apply(tappSel, List(Literal(Constant(name.decode))))) } } @@ -5015,7 +5006,7 @@ trait Typers extends Modes with Adaptations with Tags { case tt @ TypeTree() => tree setOriginal tt.original case _ => tree } - } + } else // we should get here only when something before failed // and we try again (@see tryTypedApply). In that case we can assign |