diff --git a/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala b/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala index 03a79eb12647..bd67a12fcdeb 100644 --- a/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala +++ b/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala @@ -930,7 +930,7 @@ self => case _ => t } - /** Create tree representing (unencoded) binary operation expression or pattern. */ + /** Create tree representing (unencoded) binary operation expression or pattern. Pos set by caller. */ def makeBinop(isExpr: Boolean, left: Tree, op: TermName, right: Tree, opPos: Position, targs: List[Tree] = Nil): Tree = { require(isExpr || targs.isEmpty || targs.exists(_.isErroneous), s"Incompatible args to makeBinop: !isExpr but targs=$targs") @@ -941,42 +941,35 @@ self => val pos = (opPos union t.pos) makeTransparentIf rightAssoc val sel = atPos(pos)(Select(stripParens(t), op.encode)) if (targs.isEmpty) sel - else { - /* if it's right-associative, `targs` are between `op` and `t` so make the pos transparent */ + else + // if it's right-associative, (deprecated) `targs` are between `op` and `t` so make the pos transparent atPos((pos union targs.last.pos) makeTransparentIf rightAssoc) { TypeApply(sel, targs) } - } } def mkNamed(args: List[Tree]) = if (!isExpr) args else args.map(treeInfo.assignmentToMaybeNamedArg(_)) .tap(res => if (currentRun.isScala3 && args.lengthCompare(1) == 0 && (args.head ne res.head)) deprecationWarning(args.head.pos.point, "named argument is deprecated for infix syntax", since="2.13.16")) var isMultiarg = false - val arguments = right match { + def arguments(arg: Tree) = arg match { case Parens(Nil) => literalUnit :: Nil case Parens(args @ (_ :: Nil)) => mkNamed(args) case Parens(args) => isMultiarg = true ; mkNamed(args) - case _ => right :: Nil + case _ => arg :: Nil } def mkApply(fun: Tree, args: List[Tree]) = { val apply = Apply(fun, args).updateAttachment(InfixAttachment) if (isMultiarg) apply.updateAttachment(MultiargInfixAttachment) apply } - if (isExpr) { - if (rightAssoc) { - import symtab.Flags._ - val x = freshTermName(nme.RIGHT_ASSOC_OP_PREFIX) - val liftedArg = atPos(left.pos) { - ValDef(Modifiers(FINAL | SYNTHETIC | ARTIFACT), x, TypeTree(), stripParens(left)) - } - val apply = mkApply(mkSelection(right), List(Ident(x) setPos left.pos.focus)) - Block(liftedArg :: Nil, apply) - } else - mkApply(mkSelection(left), arguments) - } else - mkApply(Ident(op.encode), stripParens(left) :: arguments) + if (isExpr) + if (rightAssoc) + mkApply(mkSelection(right), arguments(left)).updateAttachment(RightAssociative) + else + mkApply(mkSelection(left), arguments(right)) + else + mkApply(Ident(op.encode), stripParens(left) :: arguments(right)) } /** Is current ident a `*`, and is it followed by a `)` or `, )`? */ @@ -1014,10 +1007,9 @@ self => } def checkHeadAssoc(leftAssoc: Boolean) = checkAssoc(opHead.offset, opHead.operator, leftAssoc) - def checkAssoc(offset: Offset, op: Name, leftAssoc: Boolean) = ( + def checkAssoc(offset: Offset, op: Name, leftAssoc: Boolean) = if (nme.isLeftAssoc(op) != leftAssoc) syntaxError(offset, "left- and right-associative operators with same precedence may not be mixed", skipIt = false) - ) def finishPostfixOp(start: Int, base: List[OpInfo], opinfo: OpInfo): Tree = { if (opinfo.targs.nonEmpty) @@ -1047,7 +1039,7 @@ self => def reduceStack(isExpr: Boolean, base: List[OpInfo], top: Tree): Tree = { val opPrecedence = if (isIdent) Precedence(in.name.toString) else Precedence(0) - val leftAssoc = !isIdent || (nme isLeftAssoc in.name) + val leftAssoc = !isIdent || nme.isLeftAssoc(in.name) reduceStack(isExpr, base, top, opPrecedence, leftAssoc) } diff --git a/src/compiler/scala/tools/nsc/transform/CleanUp.scala b/src/compiler/scala/tools/nsc/transform/CleanUp.scala index dc36cb269b31..cfa5f5b6aa0e 100644 --- a/src/compiler/scala/tools/nsc/transform/CleanUp.scala +++ b/src/compiler/scala/tools/nsc/transform/CleanUp.scala @@ -516,7 +516,7 @@ abstract class CleanUp extends Statics with Transform with ast.TreeDSL { !elemtpt.tpe.typeSymbol.isBottomClass && !elemtpt.tpe.typeSymbol.isPrimitiveValueClass /* can happen via specialization.*/ => classTagEvidence.attachments.get[analyzer.MacroExpansionAttachment] match { - case Some(att) if att.expandee.symbol.name == nme.materializeClassTag && tree.isInstanceOf[ApplyToImplicitArgs] => + case Some(att) if att.expandee.symbol.name == nme.materializeClassTag && tree.hasAttachment[AppliedToImplicitArgs.type] => super.transform(arg) case _ => typedWithPos(tree.pos) { diff --git a/src/compiler/scala/tools/nsc/typechecker/Contexts.scala b/src/compiler/scala/tools/nsc/typechecker/Contexts.scala index c633b51bf285..126686264f9d 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Contexts.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Contexts.scala @@ -256,6 +256,7 @@ trait Contexts { self: Analyzer with ImportTracking => /** A root import is never unused and always bumps context depth. (e.g scala._ / Predef._ and magic REPL imports) */ def isRootImport: Boolean = false + /** Accumulate stabilizers (PR #5999) and right-assoc locals (#5969 / #7741). See APPSELmode. */ var pendingStabilizers: List[Tree] = Nil /** Types for which implicit arguments are currently searched */ diff --git a/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala b/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala index 3ace140b9969..0a173b3fb7ba 100644 --- a/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala +++ b/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala @@ -17,7 +17,7 @@ import scala.annotation._ import scala.collection.mutable import scala.collection.mutable.ListBuffer import scala.reflect.internal.util.CodeAction -import scala.tools.nsc.Reporting.WarningCategory +import scala.tools.nsc.Reporting.WarningCategory, WarningCategory._ import scala.tools.nsc.settings.ScalaVersion import scala.tools.nsc.settings.NoScalaVersion import symtab.Flags._ @@ -994,11 +994,13 @@ abstract class RefChecks extends Transform { def apply(tp: Type) = mapOver(tp).normalize } - def checkImplicitViewOptionApply(pos: Position, fun: Tree, argss: List[List[Tree]]): Unit = if (settings.warnOptionImplicit) argss match { - case List(List(view: ApplyImplicitView)) if fun.symbol == currentRun.runDefinitions.Option_apply => - refchecksWarning(pos, s"Suspicious application of an implicit view (${view.fun}) in the argument to Option.apply.", WarningCategory.LintOptionImplicit) // scala/bug#6567 - case _ => - } + def checkImplicitViewOptionApply(pos: Position, fun: Tree, argss: List[List[Tree]]): Unit = + if (settings.warnOptionImplicit && fun.symbol == currentRun.runDefinitions.Option_apply) + argss match { + case (((view @ Apply(coercion, _)) :: Nil) :: Nil) if view.hasAttachment[AppliedImplicitView.type] => + refchecksWarning(pos, s"Suspicious application of an implicit view ($coercion) in the argument to Option.apply.", LintOptionImplicit) // scala/bug#6567 + case _ => + } private def isObjectOrAnyComparisonMethod(sym: Symbol) = sym match { case Object_eq | Object_ne | Object_== | Object_!= | Any_== | Any_!= => true @@ -2074,7 +2076,7 @@ abstract class RefChecks extends Transform { def checkImplicitlyAdaptedBlockResult(t: Tree): Unit = { def loop(t: Tree): Unit = t match { - case Apply(coercion, _) if t.isInstanceOf[ApplyImplicitView] => + case Apply(coercion, _) if t.hasAttachment[AppliedImplicitView.type] => coercion.symbol.paramLists match { case (p :: Nil) :: _ if p.isByNameParam => refchecksWarning(t.pos, s"Block result expression was adapted via implicit conversion (${coercion.symbol}) taking a by-name parameter; only the result was passed, not the entire block.", WarningCategory.LintBynameImplicit) case _ => diff --git a/src/compiler/scala/tools/nsc/typechecker/StdAttachments.scala b/src/compiler/scala/tools/nsc/typechecker/StdAttachments.scala index 7a920f21971f..04926d579e3e 100644 --- a/src/compiler/scala/tools/nsc/typechecker/StdAttachments.scala +++ b/src/compiler/scala/tools/nsc/typechecker/StdAttachments.scala @@ -35,7 +35,7 @@ trait StdAttachments { def macroExpanderAttachment(tree: Tree): MacroExpanderAttachment = tree.attachments.get[MacroExpanderAttachment] getOrElse { tree match { - case Apply(fn, _) if tree.isInstanceOf[ApplyToImplicitArgs] => macroExpanderAttachment(fn) + case Apply(fn, _) if tree.hasAttachment[AppliedToImplicitArgs.type] => macroExpanderAttachment(fn) case _ => MacroExpanderAttachment(tree, EmptyTree) } } diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala index ead8c6794627..3f6c7ab68b09 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala @@ -19,7 +19,7 @@ import scala.collection.mutable import mutable.ListBuffer import scala.reflect.internal.{Chars, TypesStats} import scala.reflect.internal.util.{CodeAction, FreshNameCreator, ListOfNil, Statistics} -import scala.tools.nsc.Reporting.{MessageFilter, Suppression, WConf, WarningCategory}, WarningCategory.Scala3Migration +import scala.tools.nsc.Reporting.{MessageFilter, Suppression, WConf, WarningCategory}, WarningCategory._ import scala.util.chaining._ import symtab.Flags._ import Mode._ @@ -40,11 +40,6 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper final val shortenImports = false - // All typechecked RHS of ValDefs for right-associative operator desugaring - private val rightAssocValDefs = new mutable.AnyRefMap[Symbol, Tree] - // Symbols of ValDefs for right-associative operator desugaring which are passed by name and have been inlined - private val inlinedRightAssocValDefs = new mutable.HashSet[Symbol] - // For each class, we collect a mapping from constructor param accessors that are aliases of their superclass // param accessors. At the end of the typer phase, when this information is available all the way up the superclass // chain, this is used to determine which are true aliases, ones where the field can be elided from this class. @@ -60,8 +55,6 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper resetContexts() resetImplicits() resetDocComments() - rightAssocValDefs.clear() - inlinedRightAssocValDefs.clear() superConstructorCalls.clear() } @@ -290,7 +283,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper for (arg <- args) ar.subst traverse arg } - new ApplyToImplicitArgs(fun, args) setPos fun.pos + ApplyToImplicitArgs(fun, args).setPos(fun.pos) case ErrorType => fun case x => throw new MatchError(x) @@ -644,22 +637,23 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper tree } - def stabilizeFun(tree: Tree, mode: Mode, pt: Type): Tree = { - val sym = tree.symbol - val pre = tree match { - case Select(qual, _) => qual.tpe - case _ => NoPrefix - } - def stabilizable = ( - pre.isStable - && sym.tpe.params.isEmpty - && (isStableContext(tree, mode, pt) || sym.isModule) - ) + def stabilizeFun(tree: Tree, mode: Mode, pt: Type): Tree = tree.tpe match { - case MethodType(_, _) if stabilizable => tree setType MethodType(Nil, singleType(pre, sym)) // TODO: should this be a NullaryMethodType? - case _ => tree + case MethodType(_, _) => + val sym = tree.symbol + val pre = tree match { + case Select(qual, _) => qual.tpe + case _ => NoPrefix + } + val stabilizable = ( + pre.isStable + && sym.tpe.params.isEmpty + && (isStableContext(tree, mode, pt) || sym.isModule) + ) + if (stabilizable) tree.setType(MethodType(Nil, singleType(pre, sym))) // TODO: should this be a NullaryMethodType? + else tree + case _ => tree } - } @deprecated("Use the overload accepting a Type.", "2.12.9") def member(qual: Tree, name: Name): Symbol = member(qual.tpe, name) @@ -1222,7 +1216,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper if (settings.logImplicitConv.value) context.echo(tree.pos, msg) else debuglog(msg) - val viewApplied = new ApplyImplicitView(coercion, List(tree)) setPos tree.pos + val viewApplied = ApplyImplicitView(coercion, List(tree)).setPos(tree.pos) val silentContext = context.makeImplicit(context.ambiguousErrors) val typedView = newTyper(silentContext).typed(viewApplied, mode, pt) @@ -1389,7 +1383,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper && !qtpe.isError && !qtpe.typeSymbol.isBottomClass && !qtpe.isWildcard - && !qual.isInstanceOf[ApplyImplicitView] // don't chain views + && !qual.hasAttachment[AppliedImplicitView.type] // don't chain views && (context.implicitsEnabled || context.enrichmentEnabled) // Elaborating `context.implicitsEnabled`: // don't try to adapt a top-level type that's the subject of an implicit search @@ -1416,7 +1410,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper if (currentRun.isScala3 && coercion.symbol == currentRun.runDefinitions.Predef_any2stringaddMethod) if (!currentRun.sourceFeatures.any2StringAdd) runReporting.warning(qual.pos, s"Converting to String for concatenation is not supported in Scala 3 (or with -Xsource-features:any2stringadd).", Scala3Migration, coercion.symbol) - typedQualifier(atPos(qual.pos)(new ApplyImplicitView(coercion, List(qual)))) + typedQualifier(atPos(qual.pos)(ApplyImplicitView(coercion, List(qual)))) } } else qual @@ -2229,15 +2223,12 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper } else tpt1.tpe transformedOrTyped(vdef.rhs, EXPRmode | BYVALmode, tpt2) } - val vdef1 = treeCopy.ValDef(vdef, typedMods, sym.name, tpt1, checkDead(context, rhs1)) setType NoType - if (sym.isSynthetic && sym.name.startsWith(nme.RIGHT_ASSOC_OP_PREFIX)) - rightAssocValDefs += ((sym, vdef1.rhs)) if (vdef.hasAttachment[PatVarDefAttachment.type]) sym.updateAttachment(PatVarDefAttachment) if (sym.isSynthetic && sym.owner.isClass && (tpt1.tpe eq UnitTpe) && vdef.hasAttachment[PatVarDefAttachment.type] && sym.isPrivateThis && vdef.mods.isPrivateLocal && !sym.enclClassChain.exists(_.isInterpreterWrapper)) { context.warning(vdef.pos, s"Pattern definition introduces Unit-valued member of ${sym.owner.name}; consider wrapping it in `locally { ... }`.", WarningCategory.OtherMatchAnalysis) } - vdef1 + treeCopy.ValDef(vdef, typedMods, sym.name, tpt1, checkDead(context, rhs1)) setType NoType } /** Analyze the super constructor call to record information used later to compute parameter aliases */ @@ -2644,13 +2635,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper val statsTyped = typedStats(block.stats, context.owner) val expr1 = typed(block.expr, mode &~ (FUNmode | QUALmode | APPSELmode), pt) - // Remove ValDef for right-associative by-value operator desugaring which has been inlined into expr1 - val statsTyped2 = statsTyped match { - case (vd: ValDef) :: Nil if inlinedRightAssocValDefs.remove(vd.symbol) => Nil - case _ => statsTyped - } - - treeCopy.Block(block, statsTyped2, expr1) + treeCopy.Block(block, statsTyped, expr1) .setType(if (treeInfo.isExprSafeToInline(block)) expr1.tpe else expr1.tpe.deconst) } finally { // enable escaping privates checking from the outside and recycle @@ -3695,8 +3680,8 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper val treeInfo.Applied(_, _, argss) = appl val needsAdjust = argss.find { - case (aiv: ApplyImplicitView) :: Nil => - aiv.args match { + case (view @ Apply(_, viewed)) :: Nil if view.hasAttachment[AppliedImplicitView.type] => + viewed match { case Block(_ :: _, _) :: Nil => true case _ => false } @@ -3908,29 +3893,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper case _ => tp } - // Inline RHS of ValDef for right-associative by-value operator desugaring. - // Remove the ValDef also if the argument is a constant-folded reference to it. var (args2, pos2) = (args1, tree.pos) - args1 match { - case List(lit: Literal) => - lit.attachments.get[OriginalTreeAttachment] match { - case Some(OriginalTreeAttachment(id: Ident)) if rightAssocValDefs.contains(id.symbol) => - inlinedRightAssocValDefs += id.symbol - rightAssocValDefs.subtractOne(id.symbol) - case _ => - } - - case List(id: Ident) if rightAssocValDefs.contains(id.symbol) => - mt.params match { - case List(p) if p.isByNameParam => - inlinedRightAssocValDefs += id.symbol - val rhs = rightAssocValDefs.remove(id.symbol).get - args2 = rhs.changeOwner(id.symbol -> context.owner) :: Nil - pos2 = wrappingPos(tree :: rhs :: Nil) - case _ => - } - case _ => - } if (!isPastTyper && args.isEmpty && canTranslateEmptyListToNil && currentRun.runDefinitions.isListApply(fun)) atPos(tree.pos)(gen.mkNil.setType(restpe)) @@ -5237,6 +5200,40 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper if (settings.areStatisticsEnabled) statistics.stopTimer(failedApplyNanos, appStart) reportError(error) } + def transformRassoc(applied: Tree): Tree = { + def isUserRassocArg(t: Tree): Boolean = t match { + case Block(_, expr) => isUserRassocArg(expr) + case _ if t.hasAttachment[RightAssociativeArg.type] => + t.removeAttachment[RightAssociativeArg.type] + true + case _ => false + } + def rewriteRightAssoc(arg: Tree): Tree = { + val vsym = context.owner.newValue(freshTermName(nme.RIGHT_ASSOC_OP_PREFIX), arg.pos.focus, FINAL | SYNTHETIC | ARTIFACT) + vsym.setInfo(arg.tpe) + val vdef = atPos(arg.pos) { ValDef(vsym, arg).setType(NoType) } + context.pendingStabilizers ::= vdef + arg.changeOwner(context.owner -> vsym) + Ident(vsym).setType(singleType(NoPrefix, vsym)).setPos(arg.pos.focus) + } + def needsRewrite(t: Tree) = !treeInfo.isStableIdentifier(t, allowVolatile = false) && !treeInfo.isExprSafeToInline(t) + def usesStab(t: Tree) = t.exists { case Ident(nm) => nm.startsWith(nme.STABILIZER_PREFIX) case _ => false } + applied match { + // rewrite if arg was adapted or needsRewrite but isn't by-name + case Apply(fn, arg :: Nil) => + fn.symbol.paramLists match { + case (h :: Nil) :: Nil if !h.isByNameParam && (!isUserRassocArg(arg) || needsRewrite(arg)) => + val arg1 = arg match { + case view @ Apply(coercion, coerced :: Nil) if view.hasAttachment[AppliedImplicitView.type] && usesStab(coercion) => + treeCopy.Apply(view, coercion, rewriteRightAssoc(coerced) :: Nil) + case arg => rewriteRightAssoc(arg) + } + treeCopy.Apply(applied, fn, arg1 :: Nil) + case _ => applied // unexpected + } + case _ => applied + } + } val silentResult = silent( op = _.typed(fun, mode.forFunMode, funpt), reportAmbiguousErrors = !mode.inExprMode && context.ambiguousErrors, @@ -5246,6 +5243,15 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper case SilentResultValue(fun1) => val fun2 = if (stableApplication) stabilizeFun(fun1, mode, pt) else fun1 if (settings.areStatisticsEnabled) statistics.incCounter(typedApplyCount) + val needsRewrite = { + val isRightAssoc = args.lengthCompare(1) == 0 && tree.hasAttachment[RightAssociative.type] + val needs = isRightAssoc && !isPastTyper && !mode.inPatternMode + if (needs) { + tree.removeAttachment[RightAssociative.type] + args.head.updateAttachment(RightAssociativeArg) + } + needs + } val isFirstTry = fun2 match { case Select(_, _) => mode.inExprMode && { val noSecondTry = ( @@ -5258,10 +5264,13 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper } case _ => false } - if (isFirstTry) - tryTypedApply(fun2, args) - else - doTypedApply(tree, fun2, args, mode, pt) + val applied = + if (isFirstTry) + tryTypedApply(fun2, args) + else + doTypedApply(tree, fun2, args, mode, pt) + if (needsRewrite && !applied.isErroneous) transformRassoc(applied) + else applied case err: SilentTypeError => onError(err) } } @@ -5281,7 +5290,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper val tree1: Tree = resolveClassTag(tree.pos, tagType) match { case EmptyTree => MissingClassTagError(tree, tagType) - case tag => atPos(tree.pos)(new ApplyToImplicitArgs(Select(tag, nme.newArray), arg :: Nil)) + case tag => atPos(tree.pos)(ApplyToImplicitArgs(Select(tag, nme.newArray), arg :: Nil)) } if (tree1.isErrorTyped) tree1 else typed(tree1, mode, pt) case Apply(Select(fun, nme.apply), _) if treeInfo.isSuperConstrCall(fun) => TooManyArgumentListsForConstructor(tree) //scala/bug#5696 @@ -5294,8 +5303,9 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper tree1 case tree1 @ Apply(_, args1) if settings.multiargInfix && tree.hasAttachment[MultiargInfixAttachment.type] && args1.lengthCompare(1) > 0 => warnMultiargInfix(tree1) + tree.removeAttachment[MultiargInfixAttachment.type] tree1 - case tree1 => tree1 + case tree1 => tree1 } } @@ -5337,7 +5347,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper mkAssign(Select(qq1, qual.symbol) setPos qual.pos) } - case Apply(fn, extra) if qual.isInstanceOf[ApplyToImplicitArgs] => + case Apply(fn, extra) if qual.hasAttachment[AppliedToImplicitArgs.type] => fn match { case treeInfo.Applied(Select(table, nme.apply), _, indices :: Nil) => // table(indices)(implicits) @@ -5464,7 +5474,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper } def checkDubiousUnitSelection(result: Tree): Unit = if (!isPastTyper && isUniversalMember(result.symbol)) - context.warning(tree.pos, s"dubious usage of ${result.symbol} with unit value", WarningCategory.LintUniversalMethods) + context.warning(tree.pos, s"dubious usage of ${result.symbol} with unit value", LintUniversalMethods) val sym = tree.symbol .orElse(member(qualTp, name)) @@ -5475,20 +5485,20 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper // member. Added `| PATTERNmode` to allow enrichment in patterns (so we can add e.g., an // xml member to StringContext, which in turn has an unapply[Seq] method) - def checkDubiousAdaptation(sel: Tree): Unit = if (!isPastTyper && settings.lintNumericMethods) { - val dubious = ScalaIntegralValueClasses(qualTp.typeSymbol) && sel.symbol != null && ( - sel.symbol.owner.eq(BoxedFloatClass) || sel.symbol.owner.eq(RichFloatClass)) - if (dubious) - context.warning(tree.pos, s"dubious usage of ${sel.symbol} with integer value", WarningCategory.LintNumericMethods) - } - val qual1 = adaptToMemberWithArgs(tree, qual, name, mode) - val fixed = - if ((qual1 ne qual) && !qual1.isErrorTyped) - typed(treeCopy.Select(tree, qual1, name), mode, pt).tap(checkDubiousAdaptation) - else - fixUpCaseTupled(tree, qual, name, mode) - if (!fixed.isEmpty) - return fixed + def checkDubiousAdaptation(sel: Tree): Unit = if (!isPastTyper && settings.lintNumericMethods) { + val dubious = ScalaIntegralValueClasses(qualTp.typeSymbol) && sel.symbol != null && ( + sel.symbol.owner.eq(BoxedFloatClass) || sel.symbol.owner.eq(RichFloatClass)) + if (dubious) + context.warning(tree.pos, s"dubious usage of ${sel.symbol} with integer value", LintNumericMethods) + } + val qual1 = adaptToMemberWithArgs(tree, qual, name, mode) + val fixed = + if ((qual1 ne qual) && !qual1.isErrorTyped) + typed(treeCopy.Select(tree, qual1, name), mode, pt).tap(checkDubiousAdaptation) + else + fixUpCaseTupled(tree, qual, name, mode) + if (!fixed.isEmpty) + return fixed } // This special-case complements the logic in `adaptMember` in erasure, it handles selections @@ -6292,16 +6302,34 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper val shouldPrintTyping = printTypings && !phase.erasedTypes && !noPrintTyping(tree) val shouldPopTypingStack = shouldPrintTyping && typingStack.beforeNextTyped(tree, mode, pt, context) - def shouldInsertStabilizersImpl = tree match { - case _ if phase.erasedTypes || mode.in(APPSELmode) || isMacroImplRef(tree) => false - case _: Select | _: Apply | _: TypeApply => true - case _ => false + // After type checking an Apply/Select, e.g., `a.foo(x).b.bar(z)`, which is not part of the qualifier of some + // outer Apply/Select, we emit stabilizers (PR #5999) and right-assoc locals (#5969 / #7741) at the end. + // { val stab$1 = a.foo(x); val stab$2 = stab$1.b; stab$2.bar(z) } + // Setting `APPSELmode` for typing the subexpressions along the qualifier enables accumulating stabilizers, + // so if we're already in `APPSELmode` we don't insert them here. + // See also doc on APPSELmode. + val shouldInsertStabilizers = !phase.erasedTypes && !mode.in(APPSELmode) && !isMacroImplRef(tree) && { + tree match { case _: Select | _: Apply | _: TypeApply => true case _ => false } } - - val shouldInsertStabilizers = shouldInsertStabilizersImpl val mode1: Mode = if (shouldInsertStabilizers) mode | APPSELmode else mode - val savedPendingStabilizer = context.pendingStabilizers - if (shouldInsertStabilizers) context.pendingStabilizers = Nil + + // When starting to type check an outermost Apply/Select, we have to stash pending stabilizers. + // Example: `a.b.foo(c.d.bar)`. When typing `c.d.bar`, there is a pending stabilizer for `a.b`. + // The `typedArg` method clears the `APPSELmode` for typing `c.d.bar`, so `shouldInsertStabilizers` is true, + // but the pending stabilizer should not be inserted here. + // There is a corner case: the tree may be type checked already and pending stabilizers might origin in the + // subtree, not in some outer tree. Example with adaptToMember: + // x.#::(y).#::(z) => adaptToMember creates `conv(conv(x).#::(rassoc$1))` + // The argument `conv(x).#::(rassoc$1)` is already typed and there's a pending rassoc. The stabilizer was + // not emitted because the expression was part of the qualifier of an outer application. + // But with the implicit conversion, the expression is now in argument position, so the stabilizer should be + // inserted. The attachment is used to identify pending stabilizers belonging to already typed trees. + val outerPendingStabilizers = if (!shouldInsertStabilizers) Nil else { + val inner = tree.attachments.get[Stabilizers].map(_.ts).getOrElse(Nil) + val outer = if (inner.nonEmpty) context.pendingStabilizers.diff(inner) else context.pendingStabilizers + context.pendingStabilizers = inner + outer + } try { val ptPlugins = pluginsPt(pt, this, tree, mode1) @@ -6341,9 +6369,13 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper } else result } - val result = - if (shouldInsertStabilizers) addStabilizers(context.pendingStabilizers, adapted) - else adapted + val result = adapted match { + case _ if shouldInsertStabilizers => addStabilizers(adapted) + case _ => + if (mode1.in(APPSELmode) && context.pendingStabilizers.nonEmpty) + adapted.updateAttachment(Stabilizers(context.pendingStabilizers)) + adapted + } if (shouldPrint) typingStack.showAdapt(tree1, result, ptPlugins, context) @@ -6375,16 +6407,19 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper } finally { if (shouldPopTypingStack) typingStack.pop(tree) if (settings.areHotStatisticsEnabled) statistics.popTimer(byTypeStack, startByType) - if (shouldInsertStabilizers) context.pendingStabilizers = savedPendingStabilizer + if (shouldInsertStabilizers) context.pendingStabilizers = outerPendingStabilizers } } - private def addStabilizers(newStabilizers: List[Tree], expr: Tree): Tree = { - if (newStabilizers.isEmpty) expr else { - devWarningIf(newStabilizers.forall(_.symbol.owner == context.owner))(s"${context.owner} - ${(newStabilizers.map(vd => (vd.symbol, vd.symbol.owner.fullNameString)), context.owner)}") - // Insert stabilizing ValDefs (if any) which might have been introduced during the typing of the original expression. - Block(newStabilizers.reverse, expr).setPos(expr.pos).setType(expr.tpe) - } + // Insert stabilizing ValDefs (if any) introduced during the typing of the original expression. + private def addStabilizers(expr: Tree): Tree = context.pendingStabilizers match { + case Nil => expr + case all => + devWarningIf(all.forall(_.symbol.owner == context.owner))(s"${context.owner} - ${(all.map(vd => (vd.symbol, vd.symbol.owner.fullNameString)), context.owner)}") + def isStab(x: Tree) = x match { case ValDef(_, nm, _, _) => nm.startsWith(nme.STABILIZER_PREFIX) case _ => false } + val (stabs, rassocs) = all.partition(isStab) + context.pendingStabilizers = Nil + Block(rassocs ++ stabs.reverse, expr).setPos(expr.pos).setType(expr.tpe) } def atOwner(owner: Symbol): Typer = @@ -6416,9 +6451,10 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper /** Types qualifier `tree` of a select node. * E.g. is tree occurs in a context like `tree.m`. + * TR: don't set BYVALmode, since qualifier might end up as by-name param to an implicit */ @inline final def typedQualifier(tree: Tree, mode: Mode, pt: Type): Tree = - typed(checkRootOfQualifier(tree, mode), PolyQualifierModes | mode.onlyTypePat, pt) // TR: don't set BYVALmode, since qualifier might end up as by-name param to an implicit + typed(checkRootOfQualifier(tree, mode), PolyQualifierModes | mode.onlyTypePat, pt) /** Types qualifier `tree` of a select node. * E.g. is tree occurs in a context like `tree.m`. diff --git a/src/partest/scala/tools/partest/TestState.scala b/src/partest/scala/tools/partest/TestState.scala index 3b6dc49444ac..d2cb117673a9 100644 --- a/src/partest/scala/tools/partest/TestState.scala +++ b/src/partest/scala/tools/partest/TestState.scala @@ -12,6 +12,8 @@ package scala.tools.partest +import scala.tools.nsc.util._ + sealed abstract class TestState { def testFile: java.io.File def what: String @@ -67,7 +69,7 @@ object TestState { } case class Crash(testFile: java.io.File, caught: Throwable, transcript: Array[String]) extends TestState { def what = "crash" - def reason = s"caught $caught_s - ${caught.getMessage}" + def reason = s"caught $caught_s - ${caught.getMessage}: ${caught.stackTracePrefixString(_ => true)}" override def shortStatus = "?!" private def caught_s = (caught.getClass.getName split '.').last diff --git a/src/reflect/scala/reflect/internal/Importers.scala b/src/reflect/scala/reflect/internal/Importers.scala index e54a7069cbbc..85b726d8db83 100644 --- a/src/reflect/scala/reflect/internal/Importers.scala +++ b/src/reflect/scala/reflect/internal/Importers.scala @@ -379,14 +379,8 @@ trait Importers { to: SymbolTable => new Typed(importTree(expr), importTree(tpt)) case from.TypeApply(fun, args) => new TypeApply(importTree(fun), args map importTree) - case from.Apply(fun, args) => their match { - case _: from.ApplyToImplicitArgs => - new ApplyToImplicitArgs(importTree(fun), args map importTree) - case _: from.ApplyImplicitView => - new ApplyImplicitView(importTree(fun), args map importTree) - case _ => - new Apply(importTree(fun), args map importTree) - } + case from.Apply(fun, args) => + new Apply(importTree(fun), args map importTree) case from.ApplyDynamic(qual, args) => new ApplyDynamic(importTree(qual), args map importTree) case from.Super(qual, mix) => diff --git a/src/reflect/scala/reflect/internal/StdAttachments.scala b/src/reflect/scala/reflect/internal/StdAttachments.scala index ed9e0aac1a32..086e0528dbfd 100644 --- a/src/reflect/scala/reflect/internal/StdAttachments.scala +++ b/src/reflect/scala/reflect/internal/StdAttachments.scala @@ -183,4 +183,16 @@ trait StdAttachments { case object DiscardedExpr extends PlainAttachment /** Anonymous parameter of `if (_)` may be inferred as Boolean. */ case object BooleanParameterType extends PlainAttachment + + /** Apply is right associative. */ + case object RightAssociative extends PlainAttachment + /** Arg to right associative infix application is candidate for rewriting. */ + case object RightAssociativeArg extends PlainAttachment + + /** Application is an implicit view. */ + case object AppliedImplicitView extends PlainAttachment + + case object AppliedToImplicitArgs extends PlainAttachment + + case class Stabilizers(ts: List[Tree]) extends PlainAttachment } diff --git a/src/reflect/scala/reflect/internal/Trees.scala b/src/reflect/scala/reflect/internal/Trees.scala index 20652b5fc8d2..88b8c28430f6 100644 --- a/src/reflect/scala/reflect/internal/Trees.scala +++ b/src/reflect/scala/reflect/internal/Trees.scala @@ -805,13 +805,9 @@ trait Trees extends api.Trees { } object Apply extends ApplyExtractor - // TODO remove this class, add a tree attachment to Apply to track whether implicits were involved - // copying trees will all too easily forget to distinguish subclasses - class ApplyToImplicitArgs(fun: Tree, args: List[Tree]) extends Apply(fun, args) + def ApplyToImplicitArgs(fun: Tree, args: List[Tree]) = Apply(fun, args).updateAttachment(AppliedToImplicitArgs) - // TODO remove this class, add a tree attachment to Apply to track whether implicits were involved - // copying trees will all too easily forget to distinguish subclasses - class ApplyImplicitView(fun: Tree, args: List[Tree]) extends Apply(fun, args) + def ApplyImplicitView(fun: Tree, args: List[Tree]) = Apply(fun, args).updateAttachment(AppliedImplicitView) def ApplyConstructor(tpt: Tree, args: List[Tree]) = Apply(Select(New(tpt), nme.CONSTRUCTOR), args) @@ -1112,9 +1108,7 @@ trait Trees extends api.Trees { def TypeApply(tree: Tree, fun: Tree, args: List[Tree]) = new TypeApply(fun, args).copyAttrs(tree) def Apply(tree: Tree, fun: Tree, args: List[Tree]) = - (tree match { // TODO: use a tree attachment to track whether this is an apply to implicit args or a view - case _: ApplyToImplicitArgs => new ApplyToImplicitArgs(fun, args) - case _: ApplyImplicitView => new ApplyImplicitView(fun, args) + (tree match { // TODO: ApplyConstructor ??? case self.pendingSuperCall => self.pendingSuperCall case _ => new Apply(fun, args) diff --git a/src/reflect/scala/reflect/internal/Types.scala b/src/reflect/scala/reflect/internal/Types.scala index 4a8ddb910df0..db9496cebeb2 100644 --- a/src/reflect/scala/reflect/internal/Types.scala +++ b/src/reflect/scala/reflect/internal/Types.scala @@ -71,7 +71,7 @@ import scala.util.chaining._ // [tparams]result where result is a (Nullary)MethodType or ClassInfoType // The remaining types are not used after phase `typer`. - case OverloadedType(pre, tparams, alts) => + case OverloadedType(pre, alts) => // all alternatives of an overloaded ident case AntiPolyType(pre, targs) => // rarely used, disappears when combined with a PolyType diff --git a/src/reflect/scala/reflect/internal/util/package.scala b/src/reflect/scala/reflect/internal/util/package.scala index 92086cb6c0b1..52bcf73035e0 100644 --- a/src/reflect/scala/reflect/internal/util/package.scala +++ b/src/reflect/scala/reflect/internal/util/package.scala @@ -10,11 +10,7 @@ * additional information regarding copyright ownership. */ -package scala -package reflect -package internal - -import scala.language.existentials // scala/bug#6541 +package scala.reflect.internal package object util { @@ -22,8 +18,6 @@ package object util { val ListOfNil: List[List[Nothing]] = Nil :: Nil val SomeOfNil: Option[List[Nothing]] = Some(Nil) - def andFalse(body: Unit): Boolean = false - // Shorten a name like Symbols$FooSymbol to FooSymbol. private def shortenName(name: String): String = { if (name == "") return "" @@ -45,7 +39,7 @@ package object util { if (isModule) (name split '$' filterNot (_ == "")).last + "$" else if (isAnon) - clazz.getSuperclass :: clazz.getInterfaces.toList map (c => shortClass(c)) mkString " with " + clazz.getInterfaces.toList.prepended(clazz.getSuperclass).map(shortClass).mkString(" with ") else shortenName(name) } diff --git a/src/reflect/scala/reflect/runtime/JavaUniverseForce.scala b/src/reflect/scala/reflect/runtime/JavaUniverseForce.scala index d9a44849e2ec..92475847a4f3 100644 --- a/src/reflect/scala/reflect/runtime/JavaUniverseForce.scala +++ b/src/reflect/scala/reflect/runtime/JavaUniverseForce.scala @@ -91,6 +91,11 @@ trait JavaUniverseForce { self: runtime.JavaUniverse => this.DiscardedValue this.DiscardedExpr this.BooleanParameterType + this.RightAssociative + this.RightAssociativeArg + this.AppliedImplicitView + this.AppliedToImplicitArgs + this.Stabilizers this.noPrint this.typeDebug // inaccessible: this.posAssigner diff --git a/src/scaladoc/scala/tools/nsc/doc/model/ModelFactoryImplicitSupport.scala b/src/scaladoc/scala/tools/nsc/doc/model/ModelFactoryImplicitSupport.scala index b39d0ffcd8d3..100a01eaa6d1 100644 --- a/src/scaladoc/scala/tools/nsc/doc/model/ModelFactoryImplicitSupport.scala +++ b/src/scaladoc/scala/tools/nsc/doc/model/ModelFactoryImplicitSupport.scala @@ -192,7 +192,7 @@ trait ModelFactoryImplicitSupport { // type the view application so we get the exact type of the result (not the formal type) val viewTree = result.tree.setType(viewSimplifiedType) - val appliedTree = new ApplyImplicitView(viewTree, List(Ident("") setType viewTree.tpe.paramTypes.head)) + val appliedTree = ApplyImplicitView(viewTree, List(Ident("") setType viewTree.tpe.paramTypes.head)) val appliedTreeTyped: Tree = { val newContext = context.makeImplicit(context.ambiguousErrors) newContext.macrosEnabled = false diff --git a/test/files/pos/t11467.scala b/test/files/pos/t11467.scala new file mode 100644 index 000000000000..b06a3f3f321a --- /dev/null +++ b/test/files/pos/t11467.scala @@ -0,0 +1,10 @@ +//> using options -Werror -Wunused + +//import language.existentials + +trait OtherType[T] +case class Existensialism(field: OtherType[_]) + +class C { + def f(clazz: Class[_]) = clazz.getSuperclass :: clazz.getInterfaces.toList +} diff --git a/test/files/pos/t1980.scala b/test/files/pos/t1980.scala new file mode 100644 index 000000000000..85630e167b04 --- /dev/null +++ b/test/files/pos/t1980.scala @@ -0,0 +1,15 @@ +// regression test from LazyListTest +// +class L { val ll: LazyList[Nothing] = LazyList.empty #::: ll } + +class M { + def arg: LazyList[Int] = LazyList.empty[Int] + def ll: LazyList[Int] = arg #::: ll +} + +/* was + private[this] val ll: scala.collection.immutable.LazyList[Nothing] = { + final val rassoc$1: scala.collection.immutable.LazyList[A] = LazyList.empty; + immutable.this.LazyList.toDeferrer[Nothing](L.this.ll).#:::[Nothing](rassoc$1[Nothing]) + }; + */ diff --git a/test/files/pos/t4518.scala b/test/files/pos/t4518.scala new file mode 100644 index 000000000000..1951c5b34416 --- /dev/null +++ b/test/files/pos/t4518.scala @@ -0,0 +1,16 @@ + +import language.existentials + +class Broke { + + val tempval = new AnyRef {val roleA = new AnyRef with Bar}.roleA + + new AnyRef {} -: tempval // when not assigning to anything, no problem + val broke_val = new AnyRef {} -: tempval // type mismatch error only when assigning + + trait Foo[AnyRef] { } + + trait Bar extends Foo[AnyRef] { + def -:(core: AnyRef): this.type with Foo[core.type] = throw new Exception() + } +} diff --git a/test/files/pos/t4518b.scala b/test/files/pos/t4518b.scala new file mode 100644 index 000000000000..ad2dbd50da02 --- /dev/null +++ b/test/files/pos/t4518b.scala @@ -0,0 +1,14 @@ + +object Test { + trait Foo[T] + + trait Bar { + def -:(core: AnyRef): Foo[core.type] = ??? + } + + val tempval: Bar = ??? + val cor: AnyRef = ??? + + val ok : Foo[cor.type] = tempval.-:(cor) + val oops : Foo[cor.type] = cor -: tempval +} diff --git a/test/files/pos/t5073.scala b/test/files/pos/t5073.scala new file mode 100644 index 000000000000..d041ab4ad924 --- /dev/null +++ b/test/files/pos/t5073.scala @@ -0,0 +1,47 @@ + +//> using options -Xlint -Werror + +import annotation.nowarn + +trait Test { + + trait T[X] + + class C { + def -:(x: AnyRef): T[x.type] = ??? + def +:(x: AnyRef)(i: Int): T[x.type] = ??? + } + + val c = new C + val x: AnyRef = ??? + + def ok: T[x.type] = c.-:(x) + def no: T[x.type] = x -: c + + def ok2: (Int => T[x.type]) = c.+:(x) _ + def no2: (Int => T[x.type]) = (x +: c) _ + def no3: (Int => T[x.type]) = (x +: c)(_) +} + +class A { + def /: (z: A)(op: Int): A = ??? + def self = this + def bar(x: A, y: A) = (x.self /: y.self)(1) +} + +class B { + @nowarn + def f(xs: List[Int]) = (0 /: xs) _ + def g = f(List(1,2,3,4)) + def test = g(_ + _) +} + +// issue 11117 +class A2[B2](val b: B2) { def c: List[b.type] = b :: Nil } + +// when extracting user arg to temp val, must include adapted application +class EmptyApplication { + def java() = 42 + @nowarn("cat=deprecation") + def test = java :: Nil +} diff --git a/test/files/pos/t5073b.scala b/test/files/pos/t5073b.scala new file mode 100644 index 000000000000..1d8a6f32837a --- /dev/null +++ b/test/files/pos/t5073b.scala @@ -0,0 +1,27 @@ +import scala.tools.nsc.Global + +abstract class C { + val global: Global + import global._ + def f(t: Tree): List[Tree] = + t match { + case DefDef(mods, name, tparams, vparams, tpe, rhs) => + //mods.annotations ::: tpe :: rhs :: vparams.flatten :::[Tree] tparams + //mods.annotations ::: tpe :: rhs :: vparams.flatten ::: tparams.asInstanceOf[List[Tree]] + mods.annotations ::: tpe :: rhs :: vparams.flatten ::: tparams + } +} + + +/* Motivating case for always lifting adapted arg instead of cherry-picking subexpressions. + + def f(t: C.this.global.Tree): List[C.this.global.Tree] = t match { + case (mods: C.this.global.Modifiers, name: C.this.global.TermName, tparams: List[C.this.global.TypeDef], vparamss: List[List[C.this.global.ValDef]], tpt: C.this.global.Tree, rhs: C.this.global.Tree): + C.this.global.DefDef((mods @ _), (name @ _), (tparams @ _), (vparams @ _), (tpe @ _), (rhs @ _)) => { +final val rassoc$1: List[C.this.global.ValDef] = vparams.flatten[C.this.global.ValDef]; +tparams.:::[C.this.global.MemberDef with java.io.Serializable{def name: C.this.global.Name{def newName(str: String): C.this.global.Name{type ThisNameType >: C.this.global.TypeName with C.this.global.TermName <: C.this.global.Name}; def subName(from: Int, to: Int): C.this.global.Name{type ThisNameType >: C.this.global.TypeName with C.this.global.TermName <: C.this.global.Name}; def companionName: C.this.global.Name{type ThisNameType >: C.this.global.TermName with C.this.global.TypeName <: C.this.global.Name}; def next: C.this.global.Name{type ThisNameType >: C.this.global.TypeName with C.this.global.TermName <: C.this.global.Name}; type ThisNameType >: C.this.global.TypeName with C.this.global.TermName <: C.this.global.Name{type ThisNameType >: C.this.global.TypeName with C.this.global.TermName <: C.this.global.Name}}}] +(rassoc$1(scala.Predef.$conforms[List[C.this.global.ValDef]])) + .::[C.this.global.Tree](rhs).::[C.this.global.Tree](tpe).:::[C.this.global.Tree](mods.annotations) + } + } +*/ diff --git a/test/files/run/analyzerPlugins.check b/test/files/run/analyzerPlugins.check index e06ac600610f..10056d8194fe 100644 --- a/test/files/run/analyzerPlugins.check +++ b/test/files/run/analyzerPlugins.check @@ -20,8 +20,7 @@ canAdaptAnnotations(Trees$Typed, Any) [1] canAdaptAnnotations(Trees$Typed, Int) [1] lub(List(1 @testAnn, 2)) [1] pluginsPt(?, Trees$Annotated) [6] -pluginsPt(?, Trees$Apply) [17] -pluginsPt(?, Trees$ApplyImplicitView) [2] +pluginsPt(?, Trees$Apply) [19] pluginsPt(?, Trees$Block) [4] pluginsPt(?, Trees$ClassDef) [2] pluginsPt(?, Trees$DefDef) [14] @@ -175,7 +174,7 @@ pluginsTyped(math.type, Trees$Select) [9] pluginsTyped(scala.annotation.Annotation, Trees$Apply) [1] pluginsTyped(scala.annotation.TypeConstraint, Trees$Select) [4] pluginsTyped(scala.annotation.TypeConstraint, Trees$TypeTree) [2] -pluginsTyped(scala.collection.StringOps, Trees$ApplyImplicitView) [2] +pluginsTyped(scala.collection.StringOps, Trees$Apply) [2] pluginsTyped(scala.collection.immutable.ArraySeq[Any], Trees$Apply) [1] pluginsTyped(scala.package.type, Trees$Select) [1] pluginsTyped(scala.type, Trees$Ident) [1] diff --git a/test/files/run/lisp.scala b/test/files/run/lisp.scala index ad0817571ba3..0a2e8121fb51 100644 --- a/test/files/run/lisp.scala +++ b/test/files/run/lisp.scala @@ -16,9 +16,10 @@ class LispTokenizer(s: String) extends Iterator[String] { if (hasNext) { val start = i if (isDelimiter(s charAt i)) i += 1 - else - do i = i + 1 - while (!isDelimiter(s charAt i)) + else { + i += 1 + while (!isDelimiter(s charAt i)) i += 1 + } s.substring(start, i) } else sys.error("premature end of string") } @@ -204,6 +205,7 @@ object LispCaseClasses extends Lisp { case _ => sys.error("illegal parameter list"); } + //FUN(PartialFunction.fromFunction(args => eval(expr, extendEnv(env, ps, args)))) FUN(args => eval(expr, extendEnv(env, ps, args))) } @@ -310,9 +312,9 @@ object LispAny extends Lisp { case Symbol("def") :: (name :: args) :: body :: expr :: Nil => normalize(Symbol("def") :: name :: (Symbol("lambda") :: args :: body :: Nil) :: expr :: Nil) case Symbol("cond") :: (Symbol("else") :: expr :: Nil) :: rest => - normalize(expr); + normalize(expr); case Symbol("cond") :: (test :: expr :: Nil) :: rest => - normalize(Symbol("if") :: test :: expr :: (Symbol("cond") :: rest) :: Nil) + normalize(Symbol("if") :: test :: expr :: (Symbol("cond") :: rest) :: Nil) case Symbol("cond") :: Symbol("else") :: expr :: Nil => normalize(expr) case h :: t => @@ -395,6 +397,7 @@ object LispAny extends Lisp { case _ => sys.error("illegal parameter list"); } + //Lambda(PartialFunction.fromFunction(args => eval(expr, extendEnv(env, ps, args)))) Lambda(args => eval(expr, extendEnv(env, ps, args))) } diff --git a/test/files/run/t10751.check b/test/files/run/t10751.check index 0142b6896a14..0806d88704df 100644 --- a/test/files/run/t10751.check +++ b/test/files/run/t10751.check @@ -6,32 +6,14 @@ [12]() }; [20:43]private[this] val n: [38]Int = [42:43]1; - [51:60]{ - [53:60]<53:60><53:60>C.foo_:[[53]Nothing]([51]1) - }; - [67:81]{ - [69:81]<69:81><69:81>C.foo_:[[75:78]]([67]1) - }; - [89:98]{ - [89:98]<91:98><91:98>C.foo_:[[91]Nothing]([89:90][89]Test.this.n) - }; - [105:119]{ - [105:119]<107:119><107:119>C.foo_:[[113:116]]([105:106][105]Test.this.n) - }; - [127:136]{ - [129:136]<129:136><129:136>C.bar_:[[129]Nothing]([127]1) - }; - [143:157]{ - [145:157]<145:157><145:157>C.bar_:[[151:154]]([143]1) - }; - [165:174]{ - [165:166]final val rassoc$7: [165]Int = [165:166][165]Test.this.n; - [167:174]<167:174><167:174>C.bar_:[[167]Nothing]([165]rassoc$7) - }; - [181:195]{ - [181:182]final val rassoc$8: [181]Int = [181:182][181]Test.this.n; - [183:195]<183:195><183:195>C.bar_:[[189:192]]([181]rassoc$8) - } + [51:60]<53:60><53:60>C.foo_:[[53]Nothing]([51:52]1); + [67:81]<69:81><69:81>C.foo_:[[75:78]]([67:68]1); + [89:98]<91:98><91:98>C.foo_:[[91]Nothing]([89:90][89]Test.this.n); + [105:119]<107:119><107:119>C.foo_:[[113:116]]([105:106][105]Test.this.n); + [127:136]<129:136><129:136>C.bar_:[[129]Nothing]([127:128]1); + [143:157]<145:157><145:157>C.bar_:[[151:154]]([143:144]1); + [165:174]<167:174><167:174>C.bar_:[[167]Nothing]([165:166][165]Test.this.n); + [181:195]<183:195><183:195>C.bar_:[[189:192]]([181:182][181]Test.this.n) } } diff --git a/test/files/run/t12019/Test.scala b/test/files/run/t12019/Test.scala index bc47c90c4b0a..f591a32da788 100644 --- a/test/files/run/t12019/Test.scala +++ b/test/files/run/t12019/Test.scala @@ -1,4 +1,3 @@ - import scala.tools.partest.DirectTest import scala.jdk.CollectionConverters._ diff --git a/test/files/run/t1980.scala b/test/files/run/t1980.scala index 228d7c92b083..5a7b6595fd38 100644 --- a/test/files/run/t1980.scala +++ b/test/files/run/t1980.scala @@ -1,5 +1,6 @@ -//> using options -Yrangepos -// + +import scala.util.chaining._ + class LazyList[+A](expr: => LazyList.Evaluated[A]) { def #:: [B >: A](elem: => B): LazyList[B] = new LazyList(Some((elem, this))) def ##:: [B >: A](elem: B): LazyList[B] = new LazyList(Some((elem, this))) @@ -68,8 +69,8 @@ object Test extends App { def :: (x: Int): C = this def :: (x: => String): C = { saved += (() => x); this } } - def genI(i: Int): Int = { println("Int "+i); i } - def genS(s: String): String = { println("String "+s); s } + def genI(i: Int): Int = i.tap(x => println(s"Int $x")) + def genS(s: String): String = s.tap(x => println(s"String $x")) val c = genI(1) :: genS("2") :: genI(3) :: genS("4") :: (new C) println("5. forcing") c.force diff --git a/test/files/run/t1980b.check b/test/files/run/t1980b.check new file mode 100644 index 000000000000..3fea7c78bdd4 --- /dev/null +++ b/test/files/run/t1980b.check @@ -0,0 +1,18 @@ +J +I +List(27, 42) +LazyList() +I +Cell(42) +LI(Some(42),LI(None,null)) +J +I +List(27, 42) +List(()) +f27 +g5 +f42 +empty +Cell(42) +Cell(27) +LI(Some(27),LI(Some(5),LI(Some(42),LI(None,null)))) diff --git a/test/files/run/t1980b.scala b/test/files/run/t1980b.scala new file mode 100644 index 000000000000..ffb87d5abe8e --- /dev/null +++ b/test/files/run/t1980b.scala @@ -0,0 +1,46 @@ +import language.implicitConversions +import collection.immutable.LazyList +import scala.util.chaining._ + +case class LI(n: Option[Int], rest: LI) { + def ##:: (elem: Cell): LI = LI(Some(elem.x), this) + def #:: (s: String): LI = LI(Some(s.toInt), this) + + case class Cell(x: Int) + object Cell { + implicit def `wrap element`(x: Int): Cell = Cell(x).tap(println) + } +} + +object LI { + def empty: LI = LI(None, null) +} + +class C { + def i = { println("I"); 42 } + def j = { println("J"); 27 } + def f = j :: i :: Nil + def g = j #:: i #:: LazyList.empty + def k = i ##:: LI.empty + def r = j ::[Int] i ::[Int] Nil + def s = "x"*2 ::[Unit] Nil // discarded +} + +class D { + def f(i: Int) = i.tap(n => println(s"f$n")) + def g(i: Int) = i.toString.tap(n => println(s"g$n")) + def xs = LI.empty.tap(_ => println("empty")) + def test = f(27) ##:: g(5) #:: f(42) ##:: xs +} + +// check order of evaluation with mixed stabilizers +object Test extends App { + val c = new C + println(c.f) + println(c.g) + println(c.k) + println(c.r) + println(c.s) + val d = new D + println(d.test) +} diff --git a/test/files/run/t1980c.scala b/test/files/run/t1980c.scala new file mode 100644 index 000000000000..aeace7e8304e --- /dev/null +++ b/test/files/run/t1980c.scala @@ -0,0 +1,21 @@ +//> using options -Xsource:3 + +import annotation.* + +@deprecated("Uses Stream in test", since="warnings are annoying") +object X { + var i = 0 + def bump: Int = { i += 1; i } + def f: Int = bump + def g: Int = bump + def h: Int = bump + val xs = f #:: g #:: h #:: Stream.empty +} +@nowarn +object Test extends App { + import X.* + assert(1 == i, s"Expected 1, got $i") + xs.force + assert(3 == i, s"Expected 3, got $i") + assert("Stream(1, 2, 3)" == xs.toString(), s"Expected Stream(1, 2, 3), got $xs") +} diff --git a/test/files/run/t4225e.check b/test/files/run/t4225e.check index a459e7005ca1..186f3adf5626 100644 --- a/test/files/run/t4225e.check +++ b/test/files/run/t4225e.check @@ -1,22 +1,33 @@ -t4225e.scala:12: warning: a pure expression does nothing in statement position; multiline expressions might require enclosing parentheses - def andThen_:(b : Bar) = { println("pre") ; b ; println("post") } - ^ bar foo +convert bar pre +use bar post foo bar +convert bar pre +use bar post -bar foo pre +bar +convert bar +use bar +bar +convert bar +use bar post foo pre bar +convert bar +use bar +bar +convert bar +use bar post diff --git a/test/files/run/t4225e.scala b/test/files/run/t4225e.scala index e85e5c5e90cb..c201948604d8 100644 --- a/test/files/run/t4225e.scala +++ b/test/files/run/t4225e.scala @@ -1,16 +1,16 @@ -//> using options -Yrangepos -// import scala.language.implicitConversions object Test extends App { class Foo { - class Bar + class Bar { + override def toString() = "bar" + } object Bar { - implicit def fromString(a: String): Bar = new Bar + implicit def fromString(a: String): Bar = { println("convert bar") ; new Bar } } - def andThen_:(b : Bar) = { println("pre") ; b ; println("post") } - def andThenByName_:(b : => Bar) = { println("pre") ; b ; println("post") } + def andThen_:(b: Bar) = { println("pre") ; println(s"use $b") ; println("post") } + def andThenByName_:(b: => Bar) = { println("pre") ; println(s"use $b") ; println(s"use $b") ; println("post") } } def mkFoo: Foo = { println("foo") ; new Foo } @@ -24,7 +24,6 @@ object Test extends App { println() - // bar should be deferred but isn't due to scala/bug#10693 mkBarString andThenByName_: mkFoo println()