diff --git a/community-build/community-projects/intent b/community-build/community-projects/intent index 1436d2ca2439..38c7314fb764 160000 --- a/community-build/community-projects/intent +++ b/community-build/community-projects/intent @@ -1 +1 @@ -Subproject commit 1436d2ca243957d304ef43fd1f5d78f0e70cfcbf +Subproject commit 38c7314fb7643bc1786e4358b83a815194071d48 diff --git a/community-build/community-projects/shapeless b/community-build/community-projects/shapeless index dfa23f1ae4fe..3058735a54a2 160000 --- a/community-build/community-projects/shapeless +++ b/community-build/community-projects/shapeless @@ -1 +1 @@ -Subproject commit dfa23f1ae4fef5873d396ff32213c72274948dde +Subproject commit 3058735a54a23df67246ecce5b09f6a6cd3dfaec diff --git a/compiler/src-bootstrapped/scala/quoted/runtime/impl/QuotesImpl.scala b/compiler/src-bootstrapped/scala/quoted/runtime/impl/QuotesImpl.scala index 83f59b4b7002..04606a9a9377 100644 --- a/compiler/src-bootstrapped/scala/quoted/runtime/impl/QuotesImpl.scala +++ b/compiler/src-bootstrapped/scala/quoted/runtime/impl/QuotesImpl.scala @@ -55,14 +55,14 @@ class QuotesImpl private (using val ctx: Context) extends Quotes, QuoteUnpickler end extension - extension [X](self: scala.quoted.Expr[Any]) + extension (self: scala.quoted.Expr[Any]) /** Checks is the `quoted.Expr[?]` is valid expression of type `X` */ - def isExprOf(using scala.quoted.Type[X]): Boolean = + def isExprOf[X](using scala.quoted.Type[X]): Boolean = reflect.TypeReprMethods.<:<(reflect.asTerm(self).tpe)(reflect.TypeRepr.of[X]) /** Convert this to an `quoted.Expr[X]` if this expression is a valid expression of type `X` or throws */ - def asExprOf(using scala.quoted.Type[X]): scala.quoted.Expr[X] = { - if isExprOf[X] then + def asExprOf[X](using scala.quoted.Type[X]): scala.quoted.Expr[X] = { + if self.isExprOf[X] then self.asInstanceOf[scala.quoted.Expr[X]] else throw Exception( @@ -107,9 +107,9 @@ class QuotesImpl private (using val ctx: Context) extends Quotes, QuoteUnpickler case _ => throw new Exception("Expected a Term but was: " + self) end extension - extension [T](self: Tree) - def asExprOf(using tp: scala.quoted.Type[T]): scala.quoted.Expr[T] = - QuotesImpl.this.asExprOf[T](self.asExpr)(using tp) + extension (self: Tree) + def asExprOf[T](using tp: scala.quoted.Type[T]): scala.quoted.Expr[T] = + QuotesImpl.this.asExprOf(self.asExpr)[T](using tp) end extension extension [ThisTree <: Tree](self: ThisTree) @@ -257,17 +257,20 @@ class QuotesImpl private (using val ctx: Context) extends Quotes, QuoteUnpickler object DefDef extends DefDefModule: def apply(symbol: Symbol, rhsFn: List[TypeRepr] => List[List[Term]] => Option[Term]): DefDef = - withDefaultPos(tpd.polyDefDef(symbol.asTerm, tparams => vparamss => yCheckedOwners(rhsFn(tparams)(vparamss), symbol).getOrElse(tpd.EmptyTree))) + withDefaultPos(tpd.DefDef(symbol.asTerm, prefss => { + val (tparams, vparamss) = tpd.splitArgs(prefss) + yCheckedOwners(rhsFn(tparams.map(_.tpe))(vparamss), symbol).getOrElse(tpd.EmptyTree) + })) def copy(original: Tree)(name: String, typeParams: List[TypeDef], paramss: List[List[ValDef]], tpt: TypeTree, rhs: Option[Term]): DefDef = - tpd.cpy.DefDef(original)(name.toTermName, typeParams, paramss, tpt, yCheckedOwners(rhs, original.symbol).getOrElse(tpd.EmptyTree)) + tpd.cpy.DefDef(original)(name.toTermName, tpd.joinParams(typeParams, paramss), tpt, yCheckedOwners(rhs, original.symbol).getOrElse(tpd.EmptyTree)) def unapply(ddef: DefDef): (String, List[TypeDef], List[List[ValDef]], TypeTree, Option[Term]) = - (ddef.name.toString, ddef.typeParams, ddef.paramss, ddef.tpt, optional(ddef.rhs)) + (ddef.name.toString, ddef.typeParams, ddef.termParamss, ddef.tpt, optional(ddef.rhs)) end DefDef given DefDefMethods: DefDefMethods with extension (self: DefDef) - def typeParams: List[TypeDef] = self.tparams - def paramss: List[List[ValDef]] = self.vparamss + def typeParams: List[TypeDef] = self.leadingTypeParams // TODO: adapt to multiple type parameter clauses + def paramss: List[List[ValDef]] = self.termParamss def returnTpt: TypeTree = self.tpt def rhs: Option[Term] = optional(self.rhs) end extension @@ -380,7 +383,7 @@ class QuotesImpl private (using val ctx: Context) extends Quotes, QuoteUnpickler } val closureTpe = Types.MethodType(mtpe.paramNames, mtpe.paramInfos, closureResType) val closureMethod = dotc.core.Symbols.newSymbol(owner, nme.ANON_FUN, Synthetic | Method, closureTpe) - tpd.Closure(closureMethod, tss => new tpd.TreeOps(self).appliedToArgs(tss.head).etaExpand(closureMethod)) + tpd.Closure(closureMethod, tss => new tpd.TreeOps(self).appliedToTermArgs(tss.head).etaExpand(closureMethod)) case _ => self } diff --git a/compiler/src-non-bootstrapped/scala/quoted/runtime/impl/QuotesImpl.scala b/compiler/src-non-bootstrapped/scala/quoted/runtime/impl/QuotesImpl.scala index 7f65252b69c6..bac3e59375c5 100644 --- a/compiler/src-non-bootstrapped/scala/quoted/runtime/impl/QuotesImpl.scala +++ b/compiler/src-non-bootstrapped/scala/quoted/runtime/impl/QuotesImpl.scala @@ -60,7 +60,7 @@ class QuotesImpl private (using val ctx: Context) extends Quotes, QuoteUnpickler /** Convert this to an `quoted.Expr[X]` if this expression is a valid expression of type `X` or throws */ def asExprOf(using scala.quoted.Type[X]): scala.quoted.Expr[X] = { - if isExprOf[X] then + if self.isExprOf[X] then self.asInstanceOf[scala.quoted.Expr[X]] else throw Exception( @@ -255,17 +255,20 @@ class QuotesImpl private (using val ctx: Context) extends Quotes, QuoteUnpickler object DefDef extends DefDefModule: def apply(symbol: Symbol, rhsFn: List[TypeRepr] => List[List[Term]] => Option[Term]): DefDef = - withDefaultPos(tpd.polyDefDef(symbol.asTerm, tparams => vparamss => yCheckedOwners(rhsFn(tparams)(vparamss), symbol).getOrElse(tpd.EmptyTree))) + withDefaultPos(tpd.DefDef(symbol.asTerm, prefss => { + val (tparams, vparamss) = tpd.splitArgs(prefss) + yCheckedOwners(rhsFn(tparams.map(_.tpe))(vparamss), symbol).getOrElse(tpd.EmptyTree) + })) def copy(original: Tree)(name: String, typeParams: List[TypeDef], paramss: List[List[ValDef]], tpt: TypeTree, rhs: Option[Term]): DefDef = - tpd.cpy.DefDef(original)(name.toTermName, typeParams, paramss, tpt, yCheckedOwners(rhs, original.symbol).getOrElse(tpd.EmptyTree)) + tpd.cpy.DefDef(original)(name.toTermName, tpd.joinParams(typeParams, paramss), tpt, yCheckedOwners(rhs, original.symbol).getOrElse(tpd.EmptyTree)) def unapply(ddef: DefDef): (String, List[TypeDef], List[List[ValDef]], TypeTree, Option[Term]) = - (ddef.name.toString, ddef.typeParams, ddef.paramss, ddef.tpt, optional(ddef.rhs)) + (ddef.name.toString, ddef.typeParams, ddef.termParamss, ddef.tpt, optional(ddef.rhs)) end DefDef given DefDefMethods: DefDefMethods with extension (self: DefDef) - def typeParams: List[TypeDef] = self.tparams - def paramss: List[List[ValDef]] = self.vparamss + def typeParams: List[TypeDef] = self.leadingTypeParams // TODO: adapt to multiple type parameter clauses + def paramss: List[List[ValDef]] = self.termParamss def returnTpt: TypeTree = self.tpt def rhs: Option[Term] = optional(self.rhs) end extension @@ -378,7 +381,7 @@ class QuotesImpl private (using val ctx: Context) extends Quotes, QuoteUnpickler } val closureTpe = Types.MethodType(mtpe.paramNames, mtpe.paramInfos, closureResType) val closureMethod = dotc.core.Symbols.newSymbol(owner, nme.ANON_FUN, Synthetic | Method, closureTpe) - tpd.Closure(closureMethod, tss => new tpd.TreeOps(self).appliedToArgs(tss.head).etaExpand(closureMethod)) + tpd.Closure(closureMethod, tss => new tpd.TreeOps(self).appliedToTermArgs(tss.head).etaExpand(closureMethod)) case _ => self } @@ -2653,6 +2656,7 @@ class QuotesImpl private (using val ctx: Context) extends Quotes, QuoteUnpickler def startColumn: Int = self.startColumn def endColumn: Int = self.endColumn def sourceCode: Option[String] = + // TODO detect when we do not have a source and return None Some(new String(self.source.content(), self.start, self.end - self.start)) end extension end PositionMethods @@ -2667,6 +2671,7 @@ class QuotesImpl private (using val ctx: Context) extends Quotes, QuoteUnpickler extension (self: SourceFile) def jpath: java.nio.file.Path = self.file.jpath def content: Option[String] = + // TODO detect when we do not have a source and return None Some(new String(self.content())) end extension end SourceFileMethods @@ -2863,8 +2868,11 @@ class QuotesImpl private (using val ctx: Context) extends Quotes, QuoteUnpickler if pat1.isType then matcher.termMatch(scrutinee.asInstanceOf[matcher.qctx.reflect.Term], pat1.asInstanceOf[matcher.qctx.reflect.Term]) else matcher.termMatch(scrutinee.asInstanceOf[matcher.qctx.reflect.Term], pat1.asInstanceOf[matcher.qctx.reflect.Term]) + // val matchings = matcher.termMatch(scrutinee, pattern) if typeHoles.isEmpty then matchings else { + // After matching and doing all subtype checks, we have to approximate all the type bindings + // that we have found, seal them in a quoted.Type and add them to the result def typeHoleApproximation(sym: Symbol) = ctx1.gadt.approximation(sym, !sym.hasAnnotation(dotc.core.Symbols.defn.QuotedRuntimePatterns_fromAboveAnnot)).asInstanceOf[qctx1.reflect.TypeRepr].asType matchings.map { tup => diff --git a/compiler/src/dotty/tools/backend/jvm/BCodeSkelBuilder.scala b/compiler/src/dotty/tools/backend/jvm/BCodeSkelBuilder.scala index e3f740e3abee..9401307e8d62 100644 --- a/compiler/src/dotty/tools/backend/jvm/BCodeSkelBuilder.scala +++ b/compiler/src/dotty/tools/backend/jvm/BCodeSkelBuilder.scala @@ -181,7 +181,7 @@ trait BCodeSkelBuilder extends BCodeHelpers { def rewire(stat: Tree) = thisMap.transform(stat).changeOwner(claszSymbol.primaryConstructor, clInitSymbol) - val callConstructor = New(claszSymbol.typeRef).select(claszSymbol.primaryConstructor).appliedToArgs(Nil) + val callConstructor = New(claszSymbol.typeRef).select(claszSymbol.primaryConstructor).appliedToTermArgs(Nil) val assignModuleField = Assign(ref(moduleField), callConstructor) val remainingConstrStatsSubst = remainingConstrStats.map(rewire) val clinit = clinits match { @@ -667,7 +667,7 @@ trait BCodeSkelBuilder extends BCodeHelpers { val enclosingClass = origSym.owner.asClass new TreeTypeMap( typeMap = _.substThis(enclosingClass, selfParamRef.symbol.termRef) - .subst(dd.vparamss.head.map(_.symbol), regularParamRefs.map(_.symbol.termRef)), + .subst(dd.termParamss.head.map(_.symbol), regularParamRefs.map(_.symbol.termRef)), treeMap = { case tree: This if tree.symbol == enclosingClass => selfParamRef case tree => tree @@ -714,7 +714,7 @@ trait BCodeSkelBuilder extends BCodeHelpers { def genDefDef(dd: DefDef): Unit = { val rhs = dd.rhs - val vparamss = dd.vparamss + val vparamss = dd.termParamss // the only method whose implementation is not emitted: getClass() if (dd.symbol eq defn.Any_getClass) { return } assert(mnode == null, "GenBCode detected nested method.") diff --git a/compiler/src/dotty/tools/backend/sjs/JSCodeGen.scala b/compiler/src/dotty/tools/backend/sjs/JSCodeGen.scala index 564c8b1e7c33..0fc08751a973 100644 --- a/compiler/src/dotty/tools/backend/sjs/JSCodeGen.scala +++ b/compiler/src/dotty/tools/backend/sjs/JSCodeGen.scala @@ -1035,7 +1035,7 @@ class JSCodeGen()(using genCtx: Context) { private def genMethodWithCurrentLocalNameScope(dd: DefDef): Option[js.MethodDef] = { implicit val pos = dd.span val sym = dd.symbol - val vparamss = dd.vparamss + val vparamss = dd.termParamss val rhs = dd.rhs withScopedVars( diff --git a/compiler/src/dotty/tools/dotc/ast/Desugar.scala b/compiler/src/dotty/tools/dotc/ast/Desugar.scala index 65085b39e7bc..59d5be66bc78 100644 --- a/compiler/src/dotty/tools/dotc/ast/Desugar.scala +++ b/compiler/src/dotty/tools/dotc/ast/Desugar.scala @@ -177,11 +177,10 @@ object desugar { // The rhs gets filled in later, when field is generated and getter has parameters (see Memoize miniphase) val setterRhs = if (vdef.rhs.isEmpty) EmptyTree else unitLiteral val setter = cpy.DefDef(vdef)( - name = valName.setterName, - tparams = Nil, - vparamss = (setterParam :: Nil) :: Nil, - tpt = TypeTree(defn.UnitType), - rhs = setterRhs + name = valName.setterName, + paramss = (setterParam :: Nil) :: Nil, + tpt = TypeTree(defn.UnitType), + rhs = setterRhs ).withMods((mods | Accessor) &~ (CaseAccessor | GivenOrImplicit | Lazy)) Thicket(vdef1, setter) } @@ -195,6 +194,15 @@ object desugar { ValDef(epname, tpt, EmptyTree).withFlags(paramFlags | implicitFlag) } + def mapParamss(paramss: List[ParamClause]) + (mapTypeParam: TypeDef => TypeDef) + (mapTermParam: ValDef => ValDef)(using Context): List[ParamClause] = + paramss.mapConserve { + case TypeDefs(tparams) => tparams.mapConserve(mapTypeParam) + case ValDefs(vparams) => vparams.mapConserve(mapTermParam) + case _ => unreachable() + } + /** 1. Expand context bounds to evidence params. E.g., * * def f[T >: L <: H : B](params) @@ -209,83 +217,100 @@ object desugar { * def f$default$1[T] = 1 * def f$default$2[T](x: Int) = x + "m" */ - private def defDef(meth0: DefDef, isPrimaryConstructor: Boolean = false)(using Context): Tree = { - val meth @ DefDef(_, tparams, vparamss, tpt, rhs) = meth0 - val methName = normalizeName(meth, tpt).asTermName - val mods = meth.mods - val epbuf = ListBuffer[ValDef]() - def desugarContextBounds(rhs: Tree): Tree = rhs match { + private def defDef(meth: DefDef, isPrimaryConstructor: Boolean = false)(using Context): Tree = + addDefaultGetters(elimContextBounds(meth, isPrimaryConstructor)) + + private def elimContextBounds(meth: DefDef, isPrimaryConstructor: Boolean)(using Context): DefDef = + val DefDef(_, paramss, tpt, rhs) = meth + val evidenceParamBuf = ListBuffer[ValDef]() + + def desugarContextBounds(rhs: Tree): Tree = rhs match case ContextBounds(tbounds, cxbounds) => val iflag = if sourceVersion.isAtLeast(`3.1`) then Given else Implicit - epbuf ++= makeImplicitParameters(cxbounds, iflag, forPrimaryConstructor = isPrimaryConstructor) + evidenceParamBuf ++= makeImplicitParameters( + cxbounds, iflag, forPrimaryConstructor = isPrimaryConstructor) tbounds case LambdaTypeTree(tparams, body) => cpy.LambdaTypeTree(rhs)(tparams, desugarContextBounds(body)) case _ => rhs - } - def dropContextBounds(tparam: TypeDef): TypeDef = { - def dropInRhs(rhs: Tree): Tree = rhs match { + val paramssNoContextBounds = + mapParamss(paramss) { + tparam => cpy.TypeDef(tparam)(rhs = desugarContextBounds(tparam.rhs)) + }(identity) + + rhs match + case MacroTree(call) => + cpy.DefDef(meth)(rhs = call).withMods(meth.mods | Macro | Erased) + case _ => + addEvidenceParams( + cpy.DefDef(meth)( + name = normalizeName(meth, tpt).asTermName, + paramss = paramssNoContextBounds), + evidenceParamBuf.toList) + end elimContextBounds + + def addDefaultGetters(meth: DefDef)(using Context): Tree = + + /** The longest prefix of parameter lists in =paramss whose total number of + * ValDefs does not exceed `n` + */ + def takeUpTo(paramss: List[ParamClause], n: Int): List[ParamClause] = paramss match + case ValDefs(vparams) :: paramss1 => + val len = vparams.length + if len <= n then vparams :: takeUpTo(paramss1, n - len) else Nil + case TypeDefs(tparams) :: paramss1 => + tparams :: takeUpTo(paramss1, n) + case _ => + Nil + + def dropContextBounds(tparam: TypeDef): TypeDef = + def dropInRhs(rhs: Tree): Tree = rhs match case ContextBounds(tbounds, _) => tbounds case rhs @ LambdaTypeTree(tparams, body) => cpy.LambdaTypeTree(rhs)(tparams, dropInRhs(body)) case _ => rhs - } cpy.TypeDef(tparam)(rhs = dropInRhs(tparam.rhs)) - } - - val tparams1 = tparams mapConserve { tparam => - cpy.TypeDef(tparam)(rhs = desugarContextBounds(tparam.rhs)) - } - - val meth1 = - rhs match - case MacroTree(call) => cpy.DefDef(meth)(rhs = call).withMods(mods | Macro | Erased) - case _ => addEvidenceParams(cpy.DefDef(meth)(name = methName, tparams = tparams1), epbuf.toList) - /** The longest prefix of parameter lists in vparamss whose total length does not exceed `n` */ - def takeUpTo(vparamss: List[List[ValDef]], n: Int): List[List[ValDef]] = vparamss match { - case vparams :: vparamss1 => - val len = vparams.length - if (n >= len) vparams :: takeUpTo(vparamss1, n - len) else Nil - case _ => - Nil + def paramssNoRHS = mapParamss(meth.paramss)(identity) { + vparam => + if vparam.rhs.isEmpty then vparam + else cpy.ValDef(vparam)(rhs = EmptyTree).withMods(vparam.mods | HasDefault) } - def normalizedVparamss = meth1.vparamss.nestedMapConserve(vparam => - if vparam.rhs.isEmpty then vparam - else cpy.ValDef(vparam)(rhs = EmptyTree).withMods(vparam.mods | HasDefault) - ) + def getterParamss(n: Int): List[ParamClause] = + mapParamss(takeUpTo(paramssNoRHS, n)) { + tparam => dropContextBounds(toDefParam(tparam, keepAnnotations = true)) + } { + vparam => toDefParam(vparam, keepAnnotations = true, keepDefault = false) + } - def defaultGetters(vparamss: List[List[ValDef]], n: Int): List[DefDef] = vparamss match { - case (vparam :: vparams) :: vparamss1 => + def defaultGetters(paramss: List[ParamClause], n: Int): List[DefDef] = paramss match + case ValDefs(vparam :: vparams) :: paramss1 => def defaultGetter: DefDef = DefDef( - name = DefaultGetterName(methName, n), - tparams = meth.tparams.map(tparam => dropContextBounds(toDefParam(tparam, keepAnnotations = true))), - vparamss = takeUpTo(normalizedVparamss.nestedMap(toDefParam(_, keepAnnotations = true, keepDefault = false)), n), + name = DefaultGetterName(meth.name, n), + paramss = getterParamss(n), tpt = TypeTree(), rhs = vparam.rhs ) - .withMods(Modifiers(mods.flags & (AccessFlags | Synthetic), mods.privateWithin)) - val rest = defaultGetters(vparams :: vparamss1, n + 1) - if (vparam.rhs.isEmpty) rest else defaultGetter :: rest - case Nil :: vparamss1 => - defaultGetters(vparamss1, n) - case nil => + .withMods(Modifiers( + meth.mods.flags & (AccessFlags | Synthetic), + meth.mods.privateWithin)) + val rest = defaultGetters(vparams :: paramss1, n + 1) + if vparam.rhs.isEmpty then rest else defaultGetter :: rest + case _ :: paramss1 => // skip empty parameter lists and type parameters + defaultGetters(paramss1, n) + case Nil => Nil - } - val defGetters = defaultGetters(meth1.vparamss, 0) - if (defGetters.isEmpty) meth1 - else { - val meth2 = cpy.DefDef(meth1)(vparamss = normalizedVparamss) - Thicket(meth2 :: defGetters) - } - } + val defGetters = defaultGetters(meth.paramss, 0) + if defGetters.isEmpty then meth + else Thicket(cpy.DefDef(meth)(paramss = paramssNoRHS) :: defGetters) + end addDefaultGetters /** Add an explicit ascription to the `expectedTpt` to every tail splice. * @@ -323,26 +348,28 @@ object desugar { adaptToExpectedTpt(tree) } - // Add all evidence parameters in `params` as implicit parameters to `meth` */ + /** Add all evidence parameters in `params` as implicit parameters to `meth`. + * If the parameters of `meth` end in an implicit parameter list or using clause, + * evidence parameters are added in front of that list. Otherwise they are added + * as a separate parameter clause. + */ private def addEvidenceParams(meth: DefDef, params: List[ValDef])(using Context): DefDef = - params match { + params match case Nil => meth case evidenceParams => - val vparamss1 = meth.vparamss.reverse match { - case (vparams @ (vparam :: _)) :: rvparamss if vparam.mods.isOneOf(GivenOrImplicit) => - ((evidenceParams ++ vparams) :: rvparamss).reverse + val paramss1 = meth.paramss.reverse match + case ValDefs(vparams @ (vparam :: _)) :: rparamss if vparam.mods.isOneOf(GivenOrImplicit) => + ((evidenceParams ++ vparams) :: rparamss).reverse case _ => - meth.vparamss :+ evidenceParams - } - cpy.DefDef(meth)(vparamss = vparamss1) - } + meth.paramss :+ evidenceParams + cpy.DefDef(meth)(paramss = paramss1) /** The implicit evidence parameters of `meth`, as generated by `desugar.defDef` */ private def evidenceParams(meth: DefDef)(using Context): List[ValDef] = - meth.vparamss.reverse match { - case (vparams @ (vparam :: _)) :: _ if vparam.mods.isOneOf(GivenOrImplicit) => - vparams.dropWhile(!_.name.is(EvidenceParamName)) + meth.paramss.reverse match { + case ValDefs(vparams @ (vparam :: _)) :: _ if vparam.mods.isOneOf(GivenOrImplicit) => + vparams.takeWhile(_.name.is(EvidenceParamName)) case _ => Nil } @@ -411,8 +438,8 @@ object desugar { val isValueClass = parents.nonEmpty && isAnyVal(parents.head) // This is not watertight, but `extends AnyVal` will be replaced by `inline` later. - val originalTparams = constr1.tparams - val originalVparamss = constr1.vparamss + val originalTparams = constr1.leadingTypeParams + val originalVparamss = asTermOnly(constr1.trailingParamss) lazy val derivedEnumParams = enumClass.typeParams.map(derivedTypeParamWithVariance) val impliedTparams = if (isEnumCase) { @@ -451,7 +478,7 @@ object desugar { constrVparamss.nestedMap(vparam => derivedTermParam(vparam).withAnnotations(Nil)) - val constr = cpy.DefDef(constr1)(tparams = constrTparams, vparamss = constrVparamss) + val constr = cpy.DefDef(constr1)(paramss = joinParams(constrTparams, constrVparamss)) val (normalizedBody, enumCases, enumCompanionRef) = { // Add constructor type parameters and evidence implicit parameters @@ -461,7 +488,7 @@ object desugar { decompose( defDef( addEvidenceParams( - cpy.DefDef(ddef)(tparams = constrTparams ++ ddef.tparams), + cpy.DefDef(ddef)(paramss = joinParams(constrTparams, ddef.paramss)), evidenceParams(constr1).map(toDefParam(_, keepAnnotations = false, keepDefault = false))))) case stat => stat @@ -584,14 +611,14 @@ object desugar { // two errors without @uncheckedVariance, one of them spurious. val (caseClassMeths, enumScaffolding) = { def syntheticProperty(name: TermName, tpt: Tree, rhs: Tree) = - DefDef(name, Nil, Nil, tpt, rhs).withMods(synthetic) + DefDef(name, Nil, tpt, rhs).withMods(synthetic) def productElemMeths = val caseParams = derivedVparamss.head.toArray val selectorNamesInBody = normalizedBody.collect { case vdef: ValDef if vdef.name.isSelectorName => vdef.name - case ddef: DefDef if ddef.name.isSelectorName && ddef.tparams.isEmpty && ddef.vparamss.isEmpty => + case ddef: DefDef if ddef.name.isSelectorName && ddef.paramss.isEmpty => ddef.name } for i <- List.range(0, arity) @@ -617,8 +644,12 @@ object desugar { cpy.ValDef(vparam)(rhs = copyDefault(vparam))) val copyRestParamss = derivedVparamss.tail.nestedMap(vparam => cpy.ValDef(vparam)(rhs = EmptyTree)) - DefDef(nme.copy, derivedTparams, copyFirstParams :: copyRestParamss, TypeTree(), creatorExpr) - .withMods(Modifiers(Synthetic | constr1.mods.flags & copiedAccessFlags, constr1.mods.privateWithin)) :: Nil + DefDef( + nme.copy, + joinParams(derivedTparams, copyFirstParams :: copyRestParamss), + TypeTree(), + creatorExpr + ).withMods(Modifiers(Synthetic | constr1.mods.flags & copiedAccessFlags, constr1.mods.privateWithin)) :: Nil } } @@ -688,7 +719,7 @@ object desugar { val appParamss = derivedVparamss.nestedZipWithConserve(constrVparamss)((ap, cp) => ap.withMods(ap.mods | (cp.mods.flags & HasDefault))) - DefDef(nme.apply, derivedTparams, appParamss, TypeTree(), creatorExpr) + DefDef(nme.apply, joinParams(derivedTparams, appParamss), TypeTree(), creatorExpr) .withMods(appMods) :: Nil } val unapplyMeth = { @@ -699,11 +730,15 @@ object desugar { val unapplyParam = makeSyntheticParameter(tpt = classTypeRef) val unapplyRHS = if (arity == 0) Literal(Constant(true)) else Ident(unapplyParam.name) val unapplyResTp = if (arity == 0) Literal(Constant(true)) else TypeTree() - DefDef(methName, derivedTparams, (unapplyParam :: Nil) :: Nil, unapplyResTp, unapplyRHS) - .withMods(synthetic) + DefDef( + methName, + joinParams(derivedTparams, (unapplyParam :: Nil) :: Nil), + unapplyResTp, + unapplyRHS + ).withMods(synthetic) } val toStringMeth = - DefDef(nme.toString_, Nil, Nil, TypeTree(), Literal(Constant(className.toString))).withMods(Modifiers(Override | Synthetic)) + DefDef(nme.toString_, Nil, TypeTree(), Literal(Constant(className.toString))).withMods(Modifiers(Override | Synthetic)) companionDefs(anyRef, applyMeths ::: unapplyMeth :: toStringMeth :: companionMembers) } @@ -751,7 +786,9 @@ object desugar { } // implicit wrapper is typechecked in same scope as constructor, so // we can reuse the constructor parameters; no derived params are needed. - DefDef(className.toTermName, constrTparams, defParamss, classTypeRef, creatorExpr) + DefDef( + className.toTermName, joinParams(constrTparams, defParamss), + classTypeRef, creatorExpr) .withMods(companionMods | mods.flags.toTermFlags & GivenOrImplicit | Synthetic | Final) .withSpan(cdef.span) :: Nil } @@ -869,18 +906,24 @@ object desugar { /** Transform extension construct to list of extension methods */ def extMethods(ext: ExtMethods)(using Context): Tree = flatTree { for mdef <- ext.methods yield - if mdef.tparams.nonEmpty then - report.error("extension method cannot have type parameters here, all type parameters go after `extension`", - mdef.tparams.head.srcPos) defDef( cpy.DefDef(mdef)( name = normalizeName(mdef, ext).asTermName, - tparams = ext.tparams ++ mdef.tparams, - vparamss = mdef.vparamss match - case vparams1 :: vparamss1 if mdef.name.isRightAssocOperatorName => - vparams1 :: ext.vparamss ::: vparamss1 + paramss = mdef.paramss match + case params1 :: paramss1 if mdef.name.isRightAssocOperatorName => + def badRightAssoc(problem: String) = + report.error(i"right-associative extension method $problem", mdef.srcPos) + ext.paramss ++ mdef.paramss + params1 match + case ValDefs(vparam :: Nil) => + if !vparam.mods.is(Given) then + val (leadingUsing, otherExtParamss) = ext.paramss.span(isUsingOrTypeParamClause) + leadingUsing ::: params1 :: otherExtParamss ::: paramss1 + else badRightAssoc("cannot start with using clause") + case _ => + badRightAssoc("must start with a single parameter") case _ => - ext.vparamss ++ mdef.vparamss + ext.paramss ++ mdef.paramss ).withMods(mdef.mods | ExtensionMethod) ) } @@ -1262,7 +1305,7 @@ object desugar { */ def makeClosure(params: List[ValDef], body: Tree, tpt: Tree = null, isContextual: Boolean)(using Context): Block = Block( - DefDef(nme.ANON_FUN, Nil, params :: Nil, if (tpt == null) TypeTree() else tpt, body) + DefDef(nme.ANON_FUN, params :: Nil, if (tpt == null) TypeTree() else tpt, body) .withMods(synthetic | Artifact), Closure(Nil, Ident(nme.ANON_FUN), if (isContextual) ContextualEmptyTree else EmptyTree)) @@ -1314,7 +1357,7 @@ object desugar { val vdefs = params.zipWithIndex.map { case (param, idx) => - DefDef(param.name, Nil, Nil, param.tpt, selector(idx)).withSpan(param.span) + DefDef(param.name, Nil, param.tpt, selector(idx)).withSpan(param.span) } Function(param :: Nil, Block(vdefs, body)) } @@ -1357,7 +1400,7 @@ object desugar { } private def derivedDefDef(original: Tree, named: NameTree, tpt: Tree, rhs: Tree, mods: Modifiers)(implicit src: SourceFile) = - DefDef(named.name.asTermName, Nil, Nil, tpt, rhs) + DefDef(named.name.asTermName, Nil, tpt, rhs) .withMods(mods) .withSpan(original.span.withPoint(named.span.start)) @@ -1554,6 +1597,7 @@ object desugar { case Parens(body1) => makePolyFunction(targs, body1) case Function(vargs, res) => + assert(targs.nonEmpty) // TODO: Figure out if we need a `PolyFunctionWithMods` instead. val mods = body match { case body: FunctionWithMods => body.mods @@ -1570,7 +1614,7 @@ object desugar { case (p, n) => makeSyntheticParameter(n + 1, p).withAddedFlags(mods.flags) } RefinedTypeTree(polyFunctionTpt, List( - DefDef(nme.apply, applyTParams, List(applyVParams), res, EmptyTree) + DefDef(nme.apply, applyTParams :: applyVParams :: Nil, res, EmptyTree) )) } else { @@ -1580,7 +1624,7 @@ object desugar { val applyVParams = vargs.asInstanceOf[List[ValDef]] .map(varg => varg.withAddedFlags(mods.flags | Param)) New(Template(emptyConstructor, List(polyFunctionTpt), Nil, EmptyValDef, - List(DefDef(nme.apply, applyTParams, List(applyVParams), TypeTree(), res)) + List(DefDef(nme.apply, applyTParams :: applyVParams :: Nil, TypeTree(), res)) )) } case _ => diff --git a/compiler/src/dotty/tools/dotc/ast/DesugarEnums.scala b/compiler/src/dotty/tools/dotc/ast/DesugarEnums.scala index 41888539900f..d6d92c3fb078 100644 --- a/compiler/src/dotty/tools/dotc/ast/DesugarEnums.scala +++ b/compiler/src/dotty/tools/dotc/ast/DesugarEnums.scala @@ -130,7 +130,7 @@ object DesugarEnums { .withFlags(Private | Synthetic) val valuesDef = - DefDef(nme.values, Nil, Nil, defn.ArrayType.ofRawEnum, valuesDot(nme.clone_)) + DefDef(nme.values, Nil, defn.ArrayType.ofRawEnum, valuesDot(nme.clone_)) .withFlags(Synthetic) val valuesOfBody: Tree = @@ -142,7 +142,7 @@ object DesugarEnums { CaseDef(Literal(Constant(enumValue.name.toString)), EmptyTree, enumValue) ) ::: defaultCase :: Nil Match(Ident(nme.nameDollar), stringCases) - val valueOfDef = DefDef(nme.valueOf, Nil, List(param(nme.nameDollar, defn.StringType) :: Nil), + val valueOfDef = DefDef(nme.valueOf, List(param(nme.nameDollar, defn.StringType) :: Nil), TypeTree(), valuesOfBody) .withFlags(Synthetic) @@ -195,7 +195,7 @@ object DesugarEnums { self = EmptyValDef, body = fieldMethods ).withAttachment(ExtendsSingletonMirror, ())) - DefDef(nme.DOLLAR_NEW, Nil, + DefDef(nme.DOLLAR_NEW, List(List(param(nme.ordinalDollar_, defn.IntType), param(nme.nameDollar, defn.StringType))), TypeTree(), creator).withFlags(Private | Synthetic) } @@ -281,13 +281,13 @@ object DesugarEnums { private def isJavaEnum(using Context): Boolean = enumClass.derivesFrom(defn.JavaEnumClass) def ordinalMeth(body: Tree)(using Context): DefDef = - DefDef(nme.ordinal, Nil, Nil, TypeTree(defn.IntType), body).withAddedFlags(Synthetic) + DefDef(nme.ordinal, Nil, TypeTree(defn.IntType), body).withAddedFlags(Synthetic) def ordinalMethLit(ord: Int)(using Context): DefDef = ordinalMeth(Literal(Constant(ord))) def fromOrdinalMeth(body: Tree => Tree)(using Context): DefDef = - DefDef(nme.fromOrdinal, Nil, (param(nme.ordinal, defn.IntType) :: Nil) :: Nil, + DefDef(nme.fromOrdinal, (param(nme.ordinal, defn.IntType) :: Nil) :: Nil, rawRef(enumClass.typeRef), body(Ident(nme.ordinal))).withFlags(Synthetic) /** Expand a module definition representing a parameterless enum case */ diff --git a/compiler/src/dotty/tools/dotc/ast/MainProxies.scala b/compiler/src/dotty/tools/dotc/ast/MainProxies.scala index 9facdc08eff7..45364481a319 100644 --- a/compiler/src/dotty/tools/dotc/ast/MainProxies.scala +++ b/compiler/src/dotty/tools/dotc/ast/MainProxies.scala @@ -102,7 +102,7 @@ object MainProxies { val body = Try(call, handler :: Nil, EmptyTree) val mainArg = ValDef(nme.args, TypeTree(defn.ArrayType.appliedTo(defn.StringType)), EmptyTree) .withFlags(Param) - val mainMeth = DefDef(nme.main, Nil, (mainArg :: Nil) :: Nil, TypeTree(defn.UnitType), body) + val mainMeth = DefDef(nme.main, (mainArg :: Nil) :: Nil, TypeTree(defn.UnitType), body) .withFlags(JavaStatic) val mainTempl = Template(emptyConstructor, Nil, Nil, EmptyValDef, mainMeth :: Nil) val mainCls = TypeDef(mainFun.name.toTypeName, mainTempl) diff --git a/compiler/src/dotty/tools/dotc/ast/Positioned.scala b/compiler/src/dotty/tools/dotc/ast/Positioned.scala index 47dbd1826d4c..0c1f43b8f292 100644 --- a/compiler/src/dotty/tools/dotc/ast/Positioned.scala +++ b/compiler/src/dotty/tools/dotc/ast/Positioned.scala @@ -206,20 +206,15 @@ abstract class Positioned(implicit @constructorOnly src: SourceFile) extends Src this match { case tree: DefDef if tree.name == nme.CONSTRUCTOR && tree.mods.is(JavaDefined) => // Special treatment for constructors coming from Java: - // Leave out tparams, they are copied with wrong positions from parent class + // Leave out leading type params, they are copied with wrong positions from parent class check(tree.mods) - check(tree.vparamss) + check(tree.trailingParamss) case tree: DefDef if tree.mods.is(ExtensionMethod) => - tree.vparamss match { + tree.paramss match case vparams1 :: vparams2 :: rest if tree.name.isRightAssocOperatorName => - check(tree.tparams) - check(vparams2) - check(vparams1) - check(rest) + // omit check for right-associatiove extension methods; their parameters were swapped case _ => - check(tree.tparams) - check(tree.vparamss) - } + check(tree.paramss) check(tree.tpt) check(tree.rhs) case _ => diff --git a/compiler/src/dotty/tools/dotc/ast/TreeInfo.scala b/compiler/src/dotty/tools/dotc/ast/TreeInfo.scala index f29bde70744d..62ab6d2ba844 100644 --- a/compiler/src/dotty/tools/dotc/ast/TreeInfo.scala +++ b/compiler/src/dotty/tools/dotc/ast/TreeInfo.scala @@ -22,7 +22,7 @@ trait TreeInfo[T >: Untyped <: Type] { self: Trees.Instance[T] => def unsplice(tree: Trees.Tree[T]): Trees.Tree[T] = tree def isDeclarationOrTypeDef(tree: Tree): Boolean = unsplice(tree) match { - case DefDef(_, _, _, _, EmptyTree) + case DefDef(_, _, _, EmptyTree) | ValDef(_, _, EmptyTree) | TypeDef(_, _) => true case _ => false @@ -171,12 +171,6 @@ trait TreeInfo[T >: Untyped <: Type] { self: Trees.Instance[T] => case nil => EmptyTree } - /** The arguments to the first constructor in `stats`. */ - def firstConstructorArgs(stats: List[Tree]): List[Tree] = firstConstructor(stats) match { - case DefDef(_, _, args :: _, _, _) => args - case _ => Nil - } - /** Is tpt a vararg type of the form T* or => T*? */ def isRepeatedParamType(tpt: Tree)(using Context): Boolean = tpt match { case ByNameTypeTree(tpt1) => isRepeatedParamType(tpt1) @@ -198,7 +192,7 @@ trait TreeInfo[T >: Untyped <: Type] { self: Trees.Instance[T] => /** All type and value parameter symbols of this DefDef */ def allParamSyms(ddef: DefDef)(using Context): List[Symbol] = - (ddef.tparams ::: ddef.vparamss.flatten).map(_.symbol) + ddef.paramss.flatten.map(_.symbol) /** Does this argument list end with an argument of the form : _* ? */ def isWildcardStarArgList(trees: List[Tree])(using Context): Boolean = @@ -245,6 +239,18 @@ trait TreeInfo[T >: Untyped <: Type] { self: Trees.Instance[T] => /** Is this case guarded? */ def isGuardedCase(cdef: CaseDef): Boolean = cdef.guard ne EmptyTree + /** Is this parameter list a using clause? */ + def isUsingClause(params: ParamClause)(using Context): Boolean = params match + case ValDefs(vparam :: _) => + val sym = vparam.symbol + if sym.exists then sym.is(Given) else vparam.mods.is(Given) + case _ => + false + + def isUsingOrTypeParamClause(params: ParamClause)(using Context): Boolean = params match + case TypeDefs(_) => true + case _ => isUsingClause(params) + /** The underlying pattern ignoring any bindings */ def unbind(x: Tree): Tree = unsplice(x) match { case Bind(_, y) => unbind(y) @@ -305,11 +311,11 @@ trait UntypedTreeInfo extends TreeInfo[Untyped] { self: Trees.Instance[Untyped] case Function((param: untpd.ValDef) :: _, _) => param.mods.is(Given) case Closure(_, meth, _) => true case Block(Nil, expr) => isContextualClosure(expr) - case Block(DefDef(nme.ANON_FUN, _, params :: _, _, _) :: Nil, cl: Closure) => - params match { - case param :: _ => param.mods.is(Given) - case Nil => cl.tpt.eq(untpd.ContextualEmptyTree) || defn.isContextFunctionType(cl.tpt.typeOpt) - } + case Block(DefDef(nme.ANON_FUN, params :: _, _, _) :: Nil, cl: Closure) => + if params.isEmpty then + cl.tpt.eq(untpd.ContextualEmptyTree) || defn.isContextFunctionType(cl.tpt.typeOpt) + else + isUsingClause(params) case _ => false } @@ -320,11 +326,17 @@ trait UntypedTreeInfo extends TreeInfo[Untyped] { self: Trees.Instance[Untyped] case EmptyTree | _: Import => NoInitsInterface case tree: TypeDef => if (tree.isClassDef) NoInits else NoInitsInterface case tree: DefDef => - if (tree.unforcedRhs == EmptyTree && - tree.vparamss.forall(_.forall(_.rhs.isEmpty))) NoInitsInterface - else if (tree.mods.is(Given) && tree.tparams.isEmpty && tree.vparamss.isEmpty) + if tree.unforcedRhs == EmptyTree + && tree.paramss.forall { + case ValDefs(vparams) => vparams.forall(_.rhs.isEmpty) + case _ => true + } + then + NoInitsInterface + else if tree.mods.is(Given) && tree.paramss.isEmpty then EmptyFlags // might become a lazy val: TODO: check whether we need to suppress NoInits once we have new lazy val impl - else NoInits + else + NoInits case tree: ValDef => if (tree.unforcedRhs == EmptyTree) NoInitsInterface else EmptyFlags case _ => EmptyFlags } @@ -346,8 +358,6 @@ trait UntypedTreeInfo extends TreeInfo[Untyped] { self: Trees.Instance[Untyped] case _ => None } } - - // todo: fill with other methods from TreeInfo that only apply to untpd.Tree's } trait TypedTreeInfo extends TreeInfo[Type] { self: Trees.Instance[Type] => @@ -363,7 +373,7 @@ trait TypedTreeInfo extends TreeInfo[Type] { self: Trees.Instance[Type] => case EmptyTree | TypeDef(_, _) | Import(_, _) - | DefDef(_, _, _, _, _) => + | DefDef(_, _, _, _) => Pure case vdef @ ValDef(_, _, _) => if (vdef.symbol.flags is Mutable) Impure else exprPurity(vdef.rhs) `min` Pure @@ -554,6 +564,11 @@ trait TypedTreeInfo extends TreeInfo[Type] { self: Trees.Instance[Type] => } } + def isExtMethodApply(tree: Tree)(using Context): Boolean = methPart(tree) match + case Inlined(call, _, _) => isExtMethodApply(call) + case tree @ Select(qual, nme.apply) => tree.symbol.is(ExtensionMethod) || isExtMethodApply(qual) + case tree => tree.symbol.is(ExtensionMethod) + /** Is symbol potentially a getter of a mutable variable? */ def mayBeVarGetter(sym: Symbol)(using Context): Boolean = { @@ -606,24 +621,38 @@ trait TypedTreeInfo extends TreeInfo[Type] { self: Trees.Instance[Type] => } } - /** Decompose a call fn[targs](vargs_1)...(vargs_n) - * into its constituents (fn, targs, vargss). - * - * Note: targ and vargss may be empty - */ - def decomposeCall(tree: Tree): (Tree, List[Tree], List[List[Tree]]) = { + /** The type arguemnts of a possibly curried call */ + def typeArgss(tree: Tree): List[List[Tree]] = @tailrec - def loop(tree: Tree, targss: List[Tree], argss: List[List[Tree]]): (Tree, List[Tree], List[List[Tree]]) = - tree match { - case Apply(fn, args) => - loop(fn, targss, args :: argss) - case TypeApply(fn, targs) => - loop(fn, targs ::: targss, argss) - case _ => - (tree, targss, argss) - } - loop(tree, Nil, Nil) - } + def loop(tree: Tree, argss: List[List[Tree]]): List[List[Tree]] = tree match + case TypeApply(fn, args) => loop(fn, args :: argss) + case Apply(fn, args) => loop(fn, argss) + case _ => argss + loop(tree, Nil) + + /** The term arguemnts of a possibly curried call */ + def termArgss(tree: Tree): List[List[Tree]] = + @tailrec + def loop(tree: Tree, argss: List[List[Tree]]): List[List[Tree]] = tree match + case Apply(fn, args) => loop(fn, args :: argss) + case TypeApply(fn, args) => loop(fn, argss) + case _ => argss + loop(tree, Nil) + + /** The type and term arguemnts of a possibly curried call, in the order they are given */ + def allArgss(tree: Tree): List[List[Tree]] = + @tailrec + def loop(tree: Tree, argss: List[List[Tree]]): List[List[Tree]] = tree match + case tree: GenericApply => loop(tree.fun, tree.args :: argss) + case _ => argss + loop(tree, Nil) + + /** The function part of a possibly curried call. Unlike `methPart` this one does + * not decompose blocks + */ + def funPart(tree: Tree): Tree = tree match + case tree: GenericApply => funPart(tree.fun) + case tree => tree /** Decompose a template body into parameters and other statements */ def decomposeTemplateBody(body: List[Tree])(using Context): (List[Tree], List[Tree]) = @@ -886,7 +915,7 @@ trait TypedTreeInfo extends TreeInfo[Type] { self: Trees.Instance[Type] => * will return a term or type tree respectively. */ def unapply(tree: tpd.Tree)(using Context): Option[tpd.Tree] = tree match { - case tree: GenericApply[Type] if tree.symbol.isQuote => Some(tree.args.head) + case tree: GenericApply if tree.symbol.isQuote => Some(tree.args.head) case _ => None } } diff --git a/compiler/src/dotty/tools/dotc/ast/TreeMapWithImplicits.scala b/compiler/src/dotty/tools/dotc/ast/TreeMapWithImplicits.scala index 2dac4ab70f37..98376c497868 100644 --- a/compiler/src/dotty/tools/dotc/ast/TreeMapWithImplicits.scala +++ b/compiler/src/dotty/tools/dotc/ast/TreeMapWithImplicits.scala @@ -94,10 +94,9 @@ class TreeMapWithImplicits extends tpd.TreeMap { inContext(localCtx) { cpy.DefDef(tree)( tree.name, - transformSub(tree.tparams), - tree.vparamss mapConserve (transformSub(_)), + transformParamss(tree.paramss), transform(tree.tpt), - transform(tree.rhs)(using nestedScopeCtx(tree.vparamss.flatten))) + transform(tree.rhs)(using nestedScopeCtx(tree.paramss.flatten))) } case EmptyValDef => tree diff --git a/compiler/src/dotty/tools/dotc/ast/TreeTypeMap.scala b/compiler/src/dotty/tools/dotc/ast/TreeTypeMap.scala index 1d98d643ad89..fddde05d9a31 100644 --- a/compiler/src/dotty/tools/dotc/ast/TreeTypeMap.scala +++ b/compiler/src/dotty/tools/dotc/ast/TreeTypeMap.scala @@ -90,11 +90,10 @@ class TreeTypeMap( tree1.withType(mapType(tree1.tpe)) match { case id: Ident if tpd.needsSelect(id.tpe) => ref(id.tpe.asInstanceOf[TermRef]).withSpan(id.span) - case ddef @ DefDef(name, tparams, vparamss, tpt, _) => - val (tmap1, tparams1) = transformDefs(tparams) - val (tmap2, vparamss1) = tmap1.transformVParamss(vparamss) - val res = cpy.DefDef(ddef)(name, tparams1, vparamss1, tmap2.transform(tpt), tmap2.transform(ddef.rhs)) - res.symbol.setParamssFromDefs(tparams1, vparamss1) + case ddef @ DefDef(name, paramss, tpt, _) => + val (tmap1, paramss1) = transformAllParamss(paramss) + val res = cpy.DefDef(ddef)(name, paramss1, tmap1.transform(tpt), tmap1.transform(ddef.rhs)) + res.symbol.setParamssFromDefs(paramss1) res.symbol.transformAnnotations { case ann: BodyAnnotation => ann.derivedAnnotation(transform(ann.tree)) case ann => ann @@ -139,14 +138,15 @@ class TreeTypeMap( (tmap, tmap.transformSub(trees)) } - private def transformVParamss(vparamss: List[List[ValDef]]): (TreeTypeMap, List[List[ValDef]]) = vparamss match { - case vparams :: rest => - val (tmap1, vparams1) = transformDefs(vparams) - val (tmap2, vparamss2) = tmap1.transformVParamss(rest) - (tmap2, vparams1 :: vparamss2) + private def transformAllParamss(paramss: List[ParamClause]): (TreeTypeMap, List[ParamClause]) = paramss match + case params :: paramss1 => + val (tmap1, params1: ParamClause) = (params: @unchecked) match + case ValDefs(vparams) => transformDefs(vparams) + case TypeDefs(tparams) => transformDefs(tparams) + val (tmap2, paramss2) = tmap1.transformAllParamss(paramss1) + (tmap2, params1 :: paramss2) case nil => - (this, vparamss) - } + (this, paramss) def apply[ThisTree <: tpd.Tree](tree: ThisTree): ThisTree = transform(tree).asInstanceOf[ThisTree] diff --git a/compiler/src/dotty/tools/dotc/ast/Trees.scala b/compiler/src/dotty/tools/dotc/ast/Trees.scala index 7064ccc2e2c2..cde5acb2211a 100644 --- a/compiler/src/dotty/tools/dotc/ast/Trees.scala +++ b/compiler/src/dotty/tools/dotc/ast/Trees.scala @@ -382,6 +382,11 @@ object Trees { def rhs(using Context): Tree[T] = forceIfLazy } + trait ValOrTypeDef[-T >: Untyped] extends MemberDef[T]: + type ThisTree[-T >: Untyped] <: ValOrTypeDef[T] + + type ParamClause[T >: Untyped] = List[ValDef[T]] | List[TypeDef[T]] + // ----------- Tree case classes ------------------------------------ /** name */ @@ -743,7 +748,7 @@ object Trees { /** mods val name: tpt = rhs */ case class ValDef[-T >: Untyped] private[ast] (name: TermName, tpt: Tree[T], private var preRhs: LazyTree[T @uncheckedVariance])(implicit @constructorOnly src: SourceFile) - extends ValOrDefDef[T] { + extends ValOrDefDef[T], ValOrTypeDef[T] { type ThisTree[-T >: Untyped] = ValDef[T] assert(isEmpty || tpt != genericEmptyTree) def unforced: LazyTree[T] = preRhs @@ -751,13 +756,25 @@ object Trees { } /** mods def name[tparams](vparams_1)...(vparams_n): tpt = rhs */ - case class DefDef[-T >: Untyped] private[ast] (name: TermName, tparams: List[TypeDef[T]], - vparamss: List[List[ValDef[T]]], tpt: Tree[T], private var preRhs: LazyTree[T @uncheckedVariance])(implicit @constructorOnly src: SourceFile) + case class DefDef[-T >: Untyped] private[ast] (name: TermName, + paramss: List[ParamClause[T]], tpt: Tree[T], private var preRhs: LazyTree[T @uncheckedVariance])(implicit @constructorOnly src: SourceFile) extends ValOrDefDef[T] { type ThisTree[-T >: Untyped] = DefDef[T] assert(tpt != genericEmptyTree) def unforced: LazyTree[T] = preRhs protected def force(x: Tree[T @uncheckedVariance]): Unit = preRhs = x + + def leadingTypeParams(using Context): List[TypeDef[T]] = paramss match + case (tparams @ (tparam: TypeDef[_]) :: _) :: _ => tparams.asInstanceOf[List[TypeDef[T]]] + case _ => Nil + + def trailingParamss(using Context): List[ParamClause[T]] = paramss match + case ((tparam: TypeDef[_]) :: _) :: paramss1 => paramss1 + case _ => paramss + + def termParamss(using Context): List[List[ValDef[T]]] = + (if ctx.erasedTypes then paramss else untpd.termParamssIn(paramss)) + .asInstanceOf[List[List[ValDef[T]]]] } /** mods class name template or @@ -767,7 +784,7 @@ object Trees { * mods type name >: lo <: hi = rhs if rhs = TypeBoundsTree(lo, hi, alias) and opaque in mods */ case class TypeDef[-T >: Untyped] private[ast] (name: TypeName, rhs: Tree[T])(implicit @constructorOnly src: SourceFile) - extends MemberDef[T] { + extends MemberDef[T], ValOrTypeDef[T] { type ThisTree[-T >: Untyped] = TypeDef[T] /** Is this a definition of a class? */ @@ -959,8 +976,10 @@ object Trees { type NamedDefTree = Trees.NamedDefTree[T] type MemberDef = Trees.MemberDef[T] type ValOrDefDef = Trees.ValOrDefDef[T] + type ValOrTypeDef = Trees.ValOrTypeDef[T] type LazyTree = Trees.LazyTree[T] type LazyTreeList = Trees.LazyTreeList[T] + type ParamClause = Trees.ParamClause[T] type Ident = Trees.Ident[T] type SearchFailureIdent = Trees.SearchFailureIdent[T] @@ -970,6 +989,7 @@ object Trees { type Super = Trees.Super[T] type Apply = Trees.Apply[T] type TypeApply = Trees.TypeApply[T] + type GenericApply = Trees.GenericApply[T] type Literal = Trees.Literal[T] type New = Trees.New[T] type Typed = Trees.Typed[T] @@ -1203,9 +1223,9 @@ object Trees { case tree: ValDef if (name == tree.name) && (tpt eq tree.tpt) && (rhs eq tree.unforcedRhs) => tree case _ => finalize(tree, untpd.ValDef(name, tpt, rhs)(sourceFile(tree))) } - def DefDef(tree: Tree)(name: TermName, tparams: List[TypeDef], vparamss: List[List[ValDef]], tpt: Tree, rhs: LazyTree)(using Context): DefDef = tree match { - case tree: DefDef if (name == tree.name) && (tparams eq tree.tparams) && (vparamss eq tree.vparamss) && (tpt eq tree.tpt) && (rhs eq tree.unforcedRhs) => tree - case _ => finalize(tree, untpd.DefDef(name, tparams, vparamss, tpt, rhs)(sourceFile(tree))) + def DefDef(tree: Tree)(name: TermName, paramss: List[ParamClause], tpt: Tree, rhs: LazyTree)(using Context): DefDef = tree match { + case tree: DefDef if (name == tree.name) && (paramss eq tree.paramss) && (tpt eq tree.tpt) && (rhs eq tree.unforcedRhs) => tree + case _ => finalize(tree, untpd.DefDef(name, paramss, tpt, rhs)(sourceFile(tree))) } def TypeDef(tree: Tree)(name: TypeName, rhs: Tree)(using Context): TypeDef = tree match { case tree: TypeDef if (name == tree.name) && (rhs eq tree.rhs) => tree @@ -1250,8 +1270,8 @@ object Trees { UnApply(tree: Tree)(fun, implicits, patterns) def ValDef(tree: ValDef)(name: TermName = tree.name, tpt: Tree = tree.tpt, rhs: LazyTree = tree.unforcedRhs)(using Context): ValDef = ValDef(tree: Tree)(name, tpt, rhs) - def DefDef(tree: DefDef)(name: TermName = tree.name, tparams: List[TypeDef] = tree.tparams, vparamss: List[List[ValDef]] = tree.vparamss, tpt: Tree = tree.tpt, rhs: LazyTree = tree.unforcedRhs)(using Context): DefDef = - DefDef(tree: Tree)(name, tparams, vparamss, tpt, rhs) + def DefDef(tree: DefDef)(name: TermName = tree.name, paramss: List[ParamClause] = tree.paramss, tpt: Tree = tree.tpt, rhs: LazyTree = tree.unforcedRhs)(using Context): DefDef = + DefDef(tree: Tree)(name, paramss, tpt, rhs) def TypeDef(tree: TypeDef)(name: TypeName = tree.name, rhs: Tree = tree.rhs)(using Context): TypeDef = TypeDef(tree: Tree)(name, rhs) def Template(tree: Template)(constr: DefDef = tree.constr, parents: List[Tree] = tree.parents, derived: List[untpd.Tree] = tree.derived, self: ValDef = tree.self, body: LazyTreeList = tree.unforcedBody)(using Context): Template = @@ -1361,9 +1381,9 @@ object Trees { val rhs1 = transform(tree.rhs) cpy.ValDef(tree)(name, tpt1, rhs1) } - case tree @ DefDef(name, tparams, vparamss, tpt, _) => + case tree @ DefDef(name, paramss, tpt, _) => inContext(localCtx) { - cpy.DefDef(tree)(name, transformSub(tparams), vparamss mapConserve (transformSub(_)), transform(tpt), transform(tree.rhs)) + cpy.DefDef(tree)(name, transformParamss(paramss), transform(tpt), transform(tree.rhs)) } case tree @ TypeDef(name, rhs) => inContext(localCtx) { @@ -1396,6 +1416,10 @@ object Trees { transform(tree).asInstanceOf[Tr] def transformSub[Tr <: Tree](trees: List[Tr])(using Context): List[Tr] = transform(trees).asInstanceOf[List[Tr]] + def transformParams(params: ParamClause)(using Context): ParamClause = + transform(params).asInstanceOf[ParamClause] + def transformParamss(paramss: List[ParamClause])(using Context): List[ParamClause] = + paramss.mapConserve(transformParams) protected def transformMoreCases(tree: Tree)(using Context): Tree = { assert(ctx.reporter.errorsReported) @@ -1497,9 +1521,9 @@ object Trees { inContext(localCtx) { this(this(x, tpt), tree.rhs) } - case tree @ DefDef(_, tparams, vparamss, tpt, _) => + case tree @ DefDef(_, paramss, tpt, _) => inContext(localCtx) { - this(this(vparamss.foldLeft(this(x, tparams))(apply), tpt), tree.rhs) + this(this(paramss.foldLeft(x)(apply), tpt), tree.rhs) } case TypeDef(_, rhs) => inContext(localCtx) { @@ -1567,6 +1591,47 @@ object Trees { } }.asInstanceOf[tree.ThisTree[T]] + object TypeDefs: + def unapply(xs: List[Tree]): Option[List[TypeDef]] = xs match + case (x: TypeDef) :: _ => Some(xs.asInstanceOf[List[TypeDef]]) + case _ => None + + object ValDefs: + def unapply(xs: List[Tree]): Option[List[ValDef]] = xs match + case Nil => Some(Nil) + case (x: ValDef) :: _ => Some(xs.asInstanceOf[List[ValDef]]) + case _ => None + + def termParamssIn(paramss: List[ParamClause]): List[List[ValDef]] = paramss match + case ValDefs(vparams) :: paramss1 => + val paramss2 = termParamssIn(paramss1) + if paramss2 eq paramss1 then paramss.asInstanceOf[List[List[ValDef]]] + else vparams :: paramss2 + case _ :: paramss1 => + termParamssIn(paramss1) + case nil => + Nil + + /** If `tparams` is non-empty, add it to the left `paramss`, merging + * it with a leading type parameter list of `paramss`, if one exists. + */ + def joinParams(tparams: List[TypeDef], paramss: List[ParamClause]): List[ParamClause] = + if tparams.isEmpty then paramss + else paramss match + case TypeDefs(tparams1) :: paramss1 => (tparams ++ tparams1) :: paramss1 + case _ => tparams :: paramss + + def isTermOnly(paramss: List[ParamClause]): Boolean = paramss match + case Nil => true + case params :: paramss1 => + params match + case (param: untpd.TypeDef) :: _ => false + case _ => isTermOnly(paramss1) + + def asTermOnly(paramss: List[ParamClause]): List[List[ValDef]] = + assert(isTermOnly(paramss)) + paramss.asInstanceOf[List[List[ValDef]]] + /** Delegate to FunProto or FunProtoTyped depending on whether the prefix is `untpd` or `tpd`. */ protected def FunProto(args: List[Tree], resType: Type)(using Context): ProtoTypes.FunProto diff --git a/compiler/src/dotty/tools/dotc/ast/tpd.scala b/compiler/src/dotty/tools/dotc/ast/tpd.scala index c14238b06c06..60201ae532a0 100644 --- a/compiler/src/dotty/tools/dotc/ast/tpd.scala +++ b/compiler/src/dotty/tools/dotc/ast/tpd.scala @@ -42,12 +42,12 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo { Super(qual, if (mixName.isEmpty) untpd.EmptyTypeIdent else untpd.Ident(mixName), mixinClass) def Apply(fn: Tree, args: List[Tree])(using Context): Apply = { - assert(fn.isInstanceOf[RefTree] || fn.isInstanceOf[GenericApply[_]] || fn.isInstanceOf[Inlined] || fn.isInstanceOf[tasty.TreePickler.Hole]) + assert(fn.isInstanceOf[RefTree] || fn.isInstanceOf[GenericApply] || fn.isInstanceOf[Inlined] || fn.isInstanceOf[tasty.TreePickler.Hole]) ta.assignType(untpd.Apply(fn, args), fn, args) } def TypeApply(fn: Tree, args: List[Tree])(using Context): TypeApply = { - assert(fn.isInstanceOf[RefTree] || fn.isInstanceOf[GenericApply[_]]) + assert(fn.isInstanceOf[RefTree] || fn.isInstanceOf[GenericApply]) ta.assignType(untpd.TypeApply(fn, args), fn, args) } @@ -196,7 +196,7 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo { ta.assignType(untpd.Alternative(trees), trees) def UnApply(fun: Tree, implicits: List[Tree], patterns: List[Tree], proto: Type)(using Context): UnApply = { - assert(fun.isInstanceOf[RefTree] || fun.isInstanceOf[GenericApply[_]]) + assert(fun.isInstanceOf[RefTree] || fun.isInstanceOf[GenericApply]) ta.assignType(untpd.UnApply(fun, implicits, patterns), proto) } @@ -206,14 +206,17 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo { def SyntheticValDef(name: TermName, rhs: Tree)(using Context): ValDef = ValDef(newSymbol(ctx.owner, name, Synthetic, rhs.tpe.widen, coord = rhs.span), rhs) - def DefDef(sym: TermSymbol, tparams: List[TypeSymbol], vparamss: List[List[TermSymbol]], + def DefDef(sym: TermSymbol, paramss: List[List[Symbol]], resultType: Type, rhs: Tree)(using Context): DefDef = - sym.setParamss(tparams, vparamss) + sym.setParamss(paramss) ta.assignType( untpd.DefDef( sym.name, - tparams.map(tparam => TypeDef(tparam).withSpan(tparam.span)), - vparamss.nestedMap(vparam => ValDef(vparam).withSpan(vparam.span)), + paramss.map { + case TypeSymbols(params) => params.map(param => TypeDef(param).withSpan(param.span)) + case TermSymbols(params) => params.map(param => ValDef(param).withSpan(param.span)) + case _ => unreachable() + }, TypeTree(resultType), rhs), sym) @@ -221,71 +224,64 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo { def DefDef(sym: TermSymbol, rhs: Tree = EmptyTree)(using Context): DefDef = ta.assignType(DefDef(sym, Function.const(rhs) _), sym) - def DefDef(sym: TermSymbol, rhsFn: List[List[Tree]] => Tree)(using Context): DefDef = - polyDefDef(sym, Function.const(rhsFn)) - /** A DefDef with given method symbol `sym`. - * @rhsFn A function from type parameter types and term parameter references + * @rhsFn A function from parameter references * to the method's right-hand side. * Parameter symbols are taken from the `rawParamss` field of `sym`, or * are freshly generated if `rawParamss` is empty. */ - def polyDefDef(sym: TermSymbol, rhsFn: List[Type] => List[List[Tree]] => Tree)(using Context): DefDef = { + def DefDef(sym: TermSymbol, rhsFn: List[List[Tree]] => Tree)(using Context): DefDef = - val (tparams, existingParamss, mtp) = sym.info match { + // Map method type `tp` with remaining parameters stored in rawParamss to + // final result type and all (given or synthesized) parameters + def recur(tp: Type, remaining: List[List[Symbol]]): (Type, List[List[Symbol]]) = tp match case tp: PolyType => - val (tparams, existingParamss) = sym.rawParamss match - case tparams :: vparamss => + val (tparams: List[TypeSymbol], remaining1) = remaining match + case tparams :: remaining1 => assert(tparams.hasSameLengthAs(tp.paramNames) && tparams.head.isType) - (tparams.asInstanceOf[List[TypeSymbol]], vparamss) - case _ => + (tparams.asInstanceOf[List[TypeSymbol]], remaining1) + case nil => (newTypeParams(sym, tp.paramNames, EmptyFlags, tp.instantiateParamInfos(_)), Nil) - (tparams, existingParamss, tp.instantiate(tparams map (_.typeRef))) - case tp => (Nil, sym.rawParamss, tp) - } - - def valueParamss(tp: Type, existingParamss: List[List[Symbol]]): (List[List[TermSymbol]], Type) = tp match { + val (rtp, paramss) = recur(tp.instantiate(tparams.map(_.typeRef)), remaining1) + (rtp, tparams :: paramss) case tp: MethodType => val isParamDependent = tp.isParamDependent - val previousParamRefs = if (isParamDependent) mutable.ListBuffer[TermRef]() else null + val previousParamRefs = if isParamDependent then mutable.ListBuffer[TermRef]() else null - def valueParam(name: TermName, origInfo: Type): TermSymbol = { + def valueParam(name: TermName, origInfo: Type): TermSymbol = val maybeImplicit = - if (tp.isContextualMethod) Given - else if (tp.isImplicitMethod) Implicit + if tp.isContextualMethod then Given + else if tp.isImplicitMethod then Implicit else EmptyFlags - val maybeErased = if (tp.isErasedMethod) Erased else EmptyFlags + val maybeErased = if tp.isErasedMethod then Erased else EmptyFlags def makeSym(info: Type) = newSymbol(sym, name, TermParam | maybeImplicit | maybeErased, info, coord = sym.coord) - if (isParamDependent) { + if isParamDependent then val sym = makeSym(origInfo.substParams(tp, previousParamRefs.toList)) previousParamRefs += sym.termRef sym - } - else - makeSym(origInfo) - } + else makeSym(origInfo) + end valueParam - val (params, existingParamss1) = - if tp.paramInfos.isEmpty then (Nil, existingParamss) - else existingParamss match - case vparams :: existingParamss1 => + val (vparams: List[TermSymbol], remaining1) = + if tp.paramNames.isEmpty then (Nil, remaining) + else remaining match + case vparams :: remaining1 => assert(vparams.hasSameLengthAs(tp.paramNames) && vparams.head.isTerm) - (vparams.asInstanceOf[List[TermSymbol]], existingParamss1) - case _ => + (vparams.asInstanceOf[List[TermSymbol]], remaining1) + case nil => (tp.paramNames.lazyZip(tp.paramInfos).map(valueParam), Nil) - val (paramss, rtp) = - valueParamss(tp.instantiate(params map (_.termRef)), existingParamss1) - (params :: paramss, rtp) - case tp => (Nil, tp.widenExpr) - } - val (vparamss, rtp) = valueParamss(mtp, existingParamss) - val targs = tparams map (_.typeRef) - val argss = vparamss.nestedMap(vparam => Ident(vparam.termRef)) - sym.setParamss(tparams, vparamss) - DefDef(sym, tparams, vparamss, rtp, rhsFn(targs)(argss)) - } + val (rtp, paramss) = recur(tp.instantiate(vparams.map(_.termRef)), remaining1) + (rtp, vparams :: paramss) + case _ => + assert(remaining.isEmpty) + (tp.widenExpr, Nil) + end recur + + val (rtp, paramss) = recur(sym.info, sym.rawParamss) + DefDef(sym, paramss, rtp, rhsFn(paramss.nestedMap(ref))) + end DefDef def TypeDef(sym: TypeSymbol)(using Context): TypeDef = ta.assignType(untpd.TypeDef(sym.name, TypeTree(sym.info)), sym) @@ -353,7 +349,7 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo { for overridden <- fwdMeth.allOverriddenSymbols do if overridden.is(Extension) then fwdMeth.setFlag(Extension) if !overridden.is(Deferred) then fwdMeth.setFlag(Override) - polyDefDef(fwdMeth, tprefs => prefss => ref(fn).appliedToTypes(tprefs).appliedToArgss(prefss)) + DefDef(fwdMeth, ref(fn).appliedToArgss(_)) } val forwarders = fns.lazyZip(methNames).map(forwarder) val cdef = ClassDef(cls, DefDef(constr), forwarders) @@ -465,10 +461,10 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo { if (!ctx.erasedTypes) { assert(!TypeErasure.isGeneric(elemTpe), elemTpe) //needs to be done during typer. See Applications.convertNewGenericArray - newArr.appliedToTypeTrees(TypeTree(returnTpe) :: Nil).appliedToArgs(clsOf(elemTpe) :: clsOf(returnTpe) :: dims :: Nil).withSpan(span) + newArr.appliedToTypeTrees(TypeTree(returnTpe) :: Nil).appliedToTermArgs(clsOf(elemTpe) :: clsOf(returnTpe) :: dims :: Nil).withSpan(span) } else // after erasure - newArr.appliedToArgs(clsOf(elemTpe) :: clsOf(returnTpe) :: dims :: Nil).withSpan(span) + newArr.appliedToTermArgs(clsOf(elemTpe) :: clsOf(returnTpe) :: dims :: Nil).withSpan(span) } /** The wrapped array method name for an array of type elemtp */ @@ -502,7 +498,7 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo { New(tycon) .select(TermRef(tycon, constr)) .appliedToTypes(targs) - .appliedToArgs(args) + .appliedToTermArgs(args) } /** An object def @@ -900,28 +896,33 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo { /** A unary apply node with given argument: `tree(arg)` */ def appliedTo(arg: Tree)(using Context): Apply = - appliedToArgs(arg :: Nil) + appliedToTermArgs(arg :: Nil) /** An apply node with given arguments: `tree(arg, args0, ..., argsN)` */ def appliedTo(arg: Tree, args: Tree*)(using Context): Apply = - appliedToArgs(arg :: args.toList) + appliedToTermArgs(arg :: args.toList) /** An apply node with given argument list `tree(args(0), ..., args(args.length - 1))` */ - def appliedToArgs(args: List[Tree])(using Context): Apply = + def appliedToTermArgs(args: List[Tree])(using Context): Apply = Apply(tree, args) /** An applied node that accepts only varargs as arguments */ def appliedToVarargs(args: List[Tree], tpt: Tree)(using Context): Apply = appliedTo(repeated(args, tpt)) - /** The current tree applied to given argument lists: + /** An apply or type apply node with given argument list */ + def appliedToArgs(args: List[Tree])(using Context): GenericApply = args match + case arg :: args1 if arg.isType => TypeApply(tree, args) + case _ => Apply(tree, args) + + /** The current tree applied to given argument lists: * `tree (argss(0)) ... (argss(argss.length -1))` */ def appliedToArgss(argss: List[List[Tree]])(using Context): Tree = - argss.foldLeft(tree: Tree)(Apply(_, _)) + argss.foldLeft(tree: Tree)(_.appliedToArgs(_)) /** The current tree applied to (): `tree()` */ - def appliedToNone(using Context): Apply = appliedToArgs(Nil) + def appliedToNone(using Context): Apply = Apply(tree, Nil) /** The current tree applied to given type argument: `tree[targ]` */ def appliedToType(targ: Type)(using Context): Tree = @@ -1226,15 +1227,27 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo { letBindUnless(TreeInfo.Idempotent, tree)(within) def runtimeCall(name: TermName, args: List[Tree])(using Context): Tree = - Ident(defn.ScalaRuntimeModule.requiredMethod(name).termRef).appliedToArgs(args) + Ident(defn.ScalaRuntimeModule.requiredMethod(name).termRef).appliedToTermArgs(args) /** An extractor that pulls out type arguments */ - object MaybePoly { - def unapply(tree: Tree): Option[(Tree, List[Tree])] = tree match { + object MaybePoly: + def unapply(tree: Tree): Option[(Tree, List[Tree])] = tree match case TypeApply(tree, targs) => Some(tree, targs) case _ => Some(tree, Nil) - } - } + + object TypeArgs: + def unapply(ts: List[Tree]): Option[List[Tree]] = + if ts.nonEmpty && ts.head.isType then Some(ts) else None + + /** Split argument clauses into a leading type argument clause if it exists and + * remaining clauses + */ + def splitArgs(argss: List[List[Tree]]): (List[Tree], List[List[Tree]]) = argss match + case TypeArgs(targs) :: argss1 => (targs, argss1) + case _ => (Nil, argss) + + def joinArgs(targs: List[Tree], argss: List[List[Tree]]): List[List[Tree]] = + if targs.isEmpty then argss else targs :: argss /** A key to be used in a context property that tracks enclosing inlined calls */ private val InlinedCalls = Property.Key[List[Tree]]() diff --git a/compiler/src/dotty/tools/dotc/ast/untpd.scala b/compiler/src/dotty/tools/dotc/ast/untpd.scala index b98d7df11221..2a00e8fd9fd1 100644 --- a/compiler/src/dotty/tools/dotc/ast/untpd.scala +++ b/compiler/src/dotty/tools/dotc/ast/untpd.scala @@ -118,7 +118,7 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo { case class GenAlias(pat: Tree, expr: Tree)(implicit @constructorOnly src: SourceFile) extends Tree case class ContextBounds(bounds: TypeBoundsTree, cxBounds: List[Tree])(implicit @constructorOnly src: SourceFile) extends TypTree case class PatDef(mods: Modifiers, pats: List[Tree], tpt: Tree, rhs: Tree)(implicit @constructorOnly src: SourceFile) extends DefTree - case class ExtMethods(tparams: List[TypeDef], vparamss: List[List[ValDef]], methods: List[DefDef])(implicit @constructorOnly src: SourceFile) extends Tree + case class ExtMethods(paramss: List[ParamClause], methods: List[DefDef])(implicit @constructorOnly src: SourceFile) extends Tree case class MacroTree(expr: Tree)(implicit @constructorOnly src: SourceFile) extends Tree case class ImportSelector(imported: Ident, renamed: Tree = EmptyTree, bound: Tree = EmptyTree)(implicit @constructorOnly src: SourceFile) extends Tree { @@ -402,7 +402,7 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo { def Alternative(trees: List[Tree])(implicit src: SourceFile): Alternative = new Alternative(trees) def UnApply(fun: Tree, implicits: List[Tree], patterns: List[Tree])(implicit src: SourceFile): UnApply = new UnApply(fun, implicits, patterns) def ValDef(name: TermName, tpt: Tree, rhs: LazyTree)(implicit src: SourceFile): ValDef = new ValDef(name, tpt, rhs) - def DefDef(name: TermName, tparams: List[TypeDef], vparamss: List[List[ValDef]], tpt: Tree, rhs: LazyTree)(implicit src: SourceFile): DefDef = new DefDef(name, tparams, vparamss, tpt, rhs) + def DefDef(name: TermName, paramss: List[ParamClause], tpt: Tree, rhs: LazyTree)(implicit src: SourceFile): DefDef = new DefDef(name, paramss, tpt, rhs) def TypeDef(name: TypeName, rhs: Tree)(implicit src: SourceFile): TypeDef = new TypeDef(name, rhs) def Template(constr: DefDef, parents: List[Tree], derived: List[Tree], self: ValDef, body: LazyTreeList)(implicit src: SourceFile): Template = if (derived.isEmpty) new Template(constr, parents, self, body) @@ -481,7 +481,7 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo { def javaDotLangDot(name: Name)(implicit src: SourceFile): Select = Select(Select(Ident(nme.java), nme.lang), name) def makeConstructor(tparams: List[TypeDef], vparamss: List[List[ValDef]], rhs: Tree = EmptyTree)(using Context): DefDef = - DefDef(nme.CONSTRUCTOR, tparams, vparamss, TypeTree(), rhs) + DefDef(nme.CONSTRUCTOR, joinParams(tparams, vparamss), TypeTree(), rhs) def emptyConstructor(using Context): DefDef = makeConstructor(Nil, Nil) @@ -633,9 +633,9 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo { case tree: PatDef if (mods eq tree.mods) && (pats eq tree.pats) && (tpt eq tree.tpt) && (rhs eq tree.rhs) => tree case _ => finalize(tree, untpd.PatDef(mods, pats, tpt, rhs)(tree.source)) } - def ExtMethods(tree: Tree)(tparams: List[TypeDef], vparamss: List[List[ValDef]], methods: List[DefDef])(using Context): Tree = tree match - case tree: ExtMethods if (tparams eq tree.tparams) && (vparamss eq tree.vparamss) && (methods == tree.methods) => tree - case _ => finalize(tree, untpd.ExtMethods(tparams, vparamss, methods)(tree.source)) + def ExtMethods(tree: Tree)(paramss: List[ParamClause], methods: List[DefDef])(using Context): Tree = tree match + case tree: ExtMethods if (paramss eq tree.paramss) && (methods == tree.methods) => tree + case _ => finalize(tree, untpd.ExtMethods(paramss, methods)(tree.source)) def ImportSelector(tree: Tree)(imported: Ident, renamed: Tree, bound: Tree)(using Context): Tree = tree match { case tree: ImportSelector if (imported eq tree.imported) && (renamed eq tree.renamed) && (bound eq tree.bound) => tree case _ => finalize(tree, untpd.ImportSelector(imported, renamed, bound)(tree.source)) @@ -700,8 +700,8 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo { cpy.ContextBounds(tree)(transformSub(bounds), transform(cxBounds)) case PatDef(mods, pats, tpt, rhs) => cpy.PatDef(tree)(mods, transform(pats), transform(tpt), transform(rhs)) - case ExtMethods(tparams, vparamss, methods) => - cpy.ExtMethods(tree)(transformSub(tparams), vparamss.mapConserve(transformSub(_)), transformSub(methods)) + case ExtMethods(paramss, methods) => + cpy.ExtMethods(tree)(transformParamss(paramss), transformSub(methods)) case ImportSelector(imported, renamed, bound) => cpy.ImportSelector(tree)(transformSub(imported), transform(renamed), transform(bound)) case Number(_, _) | TypedSplice(_) => @@ -759,8 +759,8 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo { this(this(x, bounds), cxBounds) case PatDef(mods, pats, tpt, rhs) => this(this(this(x, pats), tpt), rhs) - case ExtMethods(tparams, vparamss, methods) => - this(vparamss.foldLeft(this(x, tparams))(apply), methods) + case ExtMethods(paramss, methods) => + this(paramss.foldLeft(x)(apply), methods) case ImportSelector(imported, renamed, bound) => this(this(this(x, imported), renamed), bound) case Number(_, _) => diff --git a/compiler/src/dotty/tools/dotc/core/NamerOps.scala b/compiler/src/dotty/tools/dotc/core/NamerOps.scala index 5a52cb54da5f..8997d5ed1e1c 100644 --- a/compiler/src/dotty/tools/dotc/core/NamerOps.scala +++ b/compiler/src/dotty/tools/dotc/core/NamerOps.scala @@ -14,22 +14,28 @@ object NamerOps: /** The given type, unless `sym` is a constructor, in which case the * type of the constructed instance is returned */ - def effectiveResultType(sym: Symbol, typeParams: List[Symbol], givenTp: Type)(using Context): Type = - if (sym.name == nme.CONSTRUCTOR) sym.owner.typeRef.appliedTo(typeParams.map(_.typeRef)) + def effectiveResultType(sym: Symbol, paramss: List[List[Symbol]], givenTp: Type)(using Context): Type = + if sym.name == nme.CONSTRUCTOR then + paramss match + case TypeSymbols(tparams) :: _ => sym.owner.typeRef.appliedTo(tparams.map(_.typeRef)) + case _ => sym.owner.typeRef else givenTp - /** if isConstructor, make sure it has one non-implicit parameter list */ - def normalizeIfConstructor(termParamss: List[List[Symbol]], isConstructor: Boolean)(using Context): List[List[Symbol]] = - if (isConstructor && - (termParamss.isEmpty || termParamss.head.nonEmpty && termParamss.head.head.isOneOf(GivenOrImplicit))) - Nil :: termParamss - else - termParamss + /** if isConstructor, make sure it has one leading non-implicit parameter list */ + def normalizeIfConstructor(paramss: List[List[Symbol]], isConstructor: Boolean)(using Context): List[List[Symbol]] = + if !isConstructor then paramss + else paramss match + case Nil :: _ => paramss + case TermSymbols(vparam :: _) :: _ if !vparam.isOneOf(GivenOrImplicit) => paramss + case TypeSymbols(tparams) :: paramss1 => tparams :: normalizeIfConstructor(paramss1, isConstructor) + case _ => Nil :: paramss /** The method type corresponding to given parameters and result type */ - def methodType(typeParams: List[Symbol], valueParamss: List[List[Symbol]], resultType: Type, isJava: Boolean = false)(using Context): Type = - val monotpe = - valueParamss.foldRight(resultType) { (params, resultType) => + def methodType(paramss: List[List[Symbol]], resultType: Type, isJava: Boolean = false)(using Context): Type = + def recur(paramss: List[List[Symbol]]): Type = (paramss: @unchecked) match + case Nil => + resultType + case TermSymbols(params) :: paramss1 => val (isContextual, isImplicit, isErased) = if params.isEmpty then (false, false, false) else (params.head.is(Given), params.head.is(Implicit), params.head.is(Erased)) @@ -37,11 +43,12 @@ object NamerOps: if isJava then for param <- params do if param.info.isDirectRef(defn.ObjectClass) then param.info = defn.AnyType - make.fromSymbols(params, resultType) - } - if typeParams.nonEmpty then PolyType.fromParams(typeParams.asInstanceOf[List[TypeSymbol]], monotpe) - else if valueParamss.isEmpty then ExprType(monotpe) - else monotpe + make.fromSymbols(params, recur(paramss1)) + case TypeSymbols(tparams) :: paramss1 => + PolyType.fromParams(tparams, recur(paramss1)) + + if paramss.isEmpty then ExprType(resultType) else recur(paramss) + end methodType /** Add moduleClass or sourceModule functionality to completer * for a module or module class diff --git a/compiler/src/dotty/tools/dotc/core/SymDenotations.scala b/compiler/src/dotty/tools/dotc/core/SymDenotations.scala index a5bf09325c29..05eb9c435fb7 100644 --- a/compiler/src/dotty/tools/dotc/core/SymDenotations.scala +++ b/compiler/src/dotty/tools/dotc/core/SymDenotations.scala @@ -291,17 +291,15 @@ object SymDenotations { final def rawParamss_=(pss: List[List[Symbol]]): Unit = myParamss = pss - final def setParamss(tparams: List[Symbol], vparamss: List[List[Symbol]])(using Context): Unit = - rawParamss = (tparams :: vparamss).filterConserve(!_.isEmpty) - - final def setParamssFromDefs(tparams: List[TypeDef[?]], vparamss: List[List[ValDef[?]]])(using Context): Unit = - setParamss(tparams.map(_.symbol), vparamss.map(_.map(_.symbol))) + final def setParamss(paramss: List[List[Symbol]])(using Context): Unit = + rawParamss = paramss.filterConserve(!_.isEmpty) + final def setParamssFromDefs(paramss: List[tpd.ParamClause])(using Context): Unit = + setParamss(paramss.map(_.map(_.symbol))) /** The symbols of each type parameter list and value parameter list of this * method, or Nil if this isn't a method. * - * * Makes use of `rawParamss` when present, or constructs fresh parameter symbols otherwise. * This method can be allocation-heavy. */ diff --git a/compiler/src/dotty/tools/dotc/core/Symbols.scala b/compiler/src/dotty/tools/dotc/core/Symbols.scala index 016a8cabaf59..855f88690be8 100644 --- a/compiler/src/dotty/tools/dotc/core/Symbols.scala +++ b/compiler/src/dotty/tools/dotc/core/Symbols.scala @@ -861,6 +861,22 @@ object Symbols { copies } + /** Matches lists of term symbols, including the empty list. + * All symbols in the list are assumed to be of the same kind. + */ + object TermSymbols: + def unapply(xs: List[Symbol])(using Context): Option[List[TermSymbol]] = xs match + case (x: Symbol) :: _ if x.isType => None + case _ => Some(xs.asInstanceOf[List[TermSymbol]]) + + /** Matches lists of type symbols, excluding the empty list. + * All symbols in the list are assumed to be of the same kind. + */ + object TypeSymbols: + def unapply(xs: List[Symbol])(using Context): Option[List[TypeSymbol]] = xs match + case (x: Symbol) :: _ if x.isType => Some(xs.asInstanceOf[List[TypeSymbol]]) + case _ => None + // ----- Locating predefined symbols ---------------------------------------- def requiredPackage(path: PreName)(using Context): TermSymbol = { diff --git a/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala b/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala index 08fde74149b4..10af44c76169 100644 --- a/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala +++ b/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala @@ -550,16 +550,20 @@ class TreePickler(pickler: TastyPickler) { case tree: ValDef => pickleDef(VALDEF, tree, tree.tpt, tree.rhs) case tree: DefDef => - def pickleParamss(paramss: List[List[ValDef]]): Unit = paramss match + def pickleParamss(paramss: List[ParamClause]): Unit = paramss match case Nil => - case params :: rest => + case Nil :: rest => + writeByte(EMPTYCLAUSE) + pickleParamss(rest) + case (params @ (param1 :: _)) :: rest => pickleParams(params) - if params.isEmpty || rest.nonEmpty then writeByte(PARAMEND) + rest match + case (param2 :: _) :: _ + if param1.isInstanceOf[untpd.TypeDef] == param2.isInstanceOf[untpd.TypeDef] => + writeByte(SPLITCLAUSE) + case _ => pickleParamss(rest) - def pickleAllParams = - pickleParams(tree.tparams) - pickleParamss(tree.vparamss) - pickleDef(DEFDEF, tree, tree.tpt, tree.rhs, pickleAllParams) + pickleDef(DEFDEF, tree, tree.tpt, tree.rhs, pickleParamss(tree.paramss)) case tree: TypeDef => pickleDef(TYPEDEF, tree, tree.rhs) case tree: Template => diff --git a/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala b/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala index 2bbdc68f9cc2..1c58ec570f24 100644 --- a/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala +++ b/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala @@ -139,7 +139,7 @@ class TreeUnpickler(reader: TastyReader, def skipParams(): Unit = while val tag = nextByte - tag == PARAM || tag == TYPEPARAM || tag == PARAMEND + tag == PARAM || tag == TYPEPARAM || tag == EMPTYCLAUSE || tag == SPLITCLAUSE do skipTree() /** Record all directly nested definitions and templates in current tree @@ -780,12 +780,15 @@ class TreeUnpickler(reader: TastyReader, val tag = readByte() val end = readEnd() - def readParamss(using Context): List[List[ValDef]] = nextByte match - case PARAM | PARAMEND => - readParams[ValDef](PARAM) :: - (if nextByte == PARAMEND then { readByte(); readParamss } else Nil) - case _ => - Nil + def readParamss()(using Context): List[ParamClause] = + def readRest() = + if nextByte == SPLITCLAUSE then readByte() + readParamss() + nextByte match + case PARAM => readParams[ValDef](PARAM) :: readRest() + case TYPEPARAM => readParams[TypeDef](TYPEPARAM) :: readRest() + case EMPTYCLAUSE => readByte(); Nil :: readRest() + case _ => Nil val localCtx = localContext(sym) @@ -803,10 +806,10 @@ class TreeUnpickler(reader: TastyReader, def ValDef(tpt: Tree) = ta.assignType(untpd.ValDef(sym.name.asTermName, tpt, readRhs(using localCtx)), sym) - def DefDef(tparams: List[TypeDef], vparamss: List[List[ValDef]], tpt: Tree) = - sym.setParamssFromDefs(tparams, vparamss) + def DefDef(paramss: List[ParamClause], tpt: Tree) = + sym.setParamssFromDefs(paramss) ta.assignType( - untpd.DefDef(sym.name.asTermName, tparams, vparamss, tpt, readRhs(using localCtx)), + untpd.DefDef(sym.name.asTermName, paramss, tpt, readRhs(using localCtx)), sym) def TypeDef(rhs: Tree) = @@ -818,15 +821,13 @@ class TreeUnpickler(reader: TastyReader, pickling.println(s"reading def of $name at $start") val tree: MemberDef = tag match { case DEFDEF => - val tparams = readParams[TypeDef](TYPEPARAM)(using localCtx) - val vparamss = readParamss(using localCtx) + val paramDefss = readParamss()(using localCtx) val tpt = readTpt()(using localCtx) - val typeParams = tparams.map(_.symbol) - val valueParamss = normalizeIfConstructor( - vparamss.nestedMap(_.symbol), name == nme.CONSTRUCTOR) - val resType = effectiveResultType(sym, typeParams, tpt.tpe) - sym.info = methodType(typeParams, valueParamss, resType) - DefDef(tparams, vparamss, tpt) + val paramss = normalizeIfConstructor( + paramDefss.nestedMap(_.symbol), name == nme.CONSTRUCTOR) + val resType = effectiveResultType(sym, paramss, tpt.tpe) + sym.info = methodType(paramss, resType) + DefDef(paramDefss, tpt) case VALDEF => val tpt = readTpt()(using localCtx) sym.info = tpt.tpe @@ -872,7 +873,7 @@ class TreeUnpickler(reader: TastyReader, else { sym.info = ExprType(tpt.tpe) pickling.println(i"reading param alias $name -> $currentAddr") - DefDef(Nil, Nil, tpt) + DefDef(Nil, tpt) } } goto(end) diff --git a/compiler/src/dotty/tools/dotc/interactive/Interactive.scala b/compiler/src/dotty/tools/dotc/interactive/Interactive.scala index a54b24fe62cc..aa2359f33eb2 100644 --- a/compiler/src/dotty/tools/dotc/interactive/Interactive.scala +++ b/compiler/src/dotty/tools/dotc/interactive/Interactive.scala @@ -111,7 +111,7 @@ object Interactive { val classTree = funSym.topLevelClass.asClass.rootTree val paramSymbol = for { - DefDef(_, _, paramss, _, _) <- tpd.defPath(funSym, classTree).lastOption + DefDef(_, paramss, _, _) <- tpd.defPath(funSym, classTree).lastOption param <- paramss.flatten.find(_.name == name) } yield param.symbol @@ -288,8 +288,7 @@ object Interactive { case tree: DefDef => assert(tree.symbol.exists) val localCtx = outer.localContext(tree, tree.symbol).setNewScope - for (tparam <- tree.tparams) localCtx.enter(tparam.symbol) - for (vparams <- tree.vparamss; vparam <- vparams) localCtx.enter(vparam.symbol) + for params <- tree.paramss; param <- params do localCtx.enter(param.symbol) // Note: this overapproximates visibility a bit, since value parameters are only visible // in subsequent parameter sections localCtx diff --git a/compiler/src/dotty/tools/dotc/parsing/JavaParsers.scala b/compiler/src/dotty/tools/dotc/parsing/JavaParsers.scala index 549ca054c136..e54ea0b2b6aa 100644 --- a/compiler/src/dotty/tools/dotc/parsing/JavaParsers.scala +++ b/compiler/src/dotty/tools/dotc/parsing/JavaParsers.scala @@ -134,7 +134,7 @@ object JavaParsers { def makeConstructor(formals: List[Tree], tparams: List[TypeDef], flags: FlagSet = Flags.JavaDefined): DefDef = { val vparams = formals.zipWithIndex.map { case (p, i) => makeSyntheticParam(i + 1, p) } - DefDef(nme.CONSTRUCTOR, tparams, List(vparams), TypeTree(), EmptyTree).withMods(Modifiers(flags)) + DefDef(nme.CONSTRUCTOR, joinParams(tparams, List(vparams)), TypeTree(), EmptyTree).withMods(Modifiers(flags)) } // ------------- general parsing --------------------------- @@ -509,8 +509,8 @@ object JavaParsers { optThrows() List { atSpan(start) { - DefDef(nme.CONSTRUCTOR, tparams, - List(vparams), TypeTree(), methodBody()).withMods(mods) + DefDef(nme.CONSTRUCTOR, joinParams(tparams, List(vparams)), + TypeTree(), methodBody()).withMods(mods) } } } @@ -547,7 +547,7 @@ object JavaParsers { //if (inInterface) mods1 |= Flags.Deferred List { atSpan(start, nameOffset) { - DefDef(name.toTermName, tparams, List(vparams), rtpt, body).withMods(mods1 | Flags.Method) + DefDef(name.toTermName, joinParams(tparams, List(vparams)), rtpt, body).withMods(mods1 | Flags.Method) } } } @@ -844,7 +844,7 @@ object JavaParsers { makeParam(dd.name, dd.tpt) } val constr = DefDef(nme.CONSTRUCTOR, - List(), List(constructorParams), TypeTree(), EmptyTree).withMods(Modifiers(Flags.JavaDefined)) + List(constructorParams), TypeTree(), EmptyTree).withMods(Modifiers(Flags.JavaDefined)) val templ = makeTemplate(annotationParents, constr :: body, List(), true) val annot = atSpan(start, nameOffset) { TypeDef(name, templ).withMods(mods | Flags.Abstract) @@ -879,12 +879,12 @@ object JavaParsers { (List(), List()) val predefs = List( DefDef( - nme.values, List(), + nme.values, ListOfNil, arrayOf(enumType), unimplementedExpr).withMods(Modifiers(Flags.JavaDefined | Flags.JavaStatic | Flags.Method)), DefDef( - nme.valueOf, List(), + nme.valueOf, List(List(makeParam("x".toTermName, TypeTree(StringType)))), enumType, unimplementedExpr).withMods(Modifiers(Flags.JavaDefined | Flags.JavaStatic | Flags.Method))) diff --git a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala index 9849400da403..17053130c34a 100644 --- a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala +++ b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala @@ -1298,7 +1298,7 @@ object Parsers { case stat: MemberDef if !stat.name.isEmpty => if stat.name == nme.CONSTRUCTOR then in.token == THIS else in.isIdent && in.name == stat.name.toTermName - case ExtMethods(_, _, _) => + case ExtMethods(_, _) => in.token == IDENTIFIER && in.name == nme.extension case PackageDef(pid: RefTree, _) => in.isIdent && in.name == pid.name @@ -2997,7 +2997,7 @@ object Parsers { if in.token == RPAREN && !prefix && !impliedMods.is(Given) then Nil else val clause = - if prefix then param() :: Nil + if prefix && !isIdent(nme.using) then param() :: Nil else paramMods() if givenOnly && !impliedMods.is(Given) then @@ -3306,7 +3306,7 @@ object Parsers { accept(EQUALS) expr() - val ddef = DefDef(name, tparams, vparamss, tpt, rhs) + val ddef = DefDef(name, joinParams(tparams, vparamss), tpt, rhs) if (isBackquoted(ident)) ddef.pushAttachment(Backquoted, ()) finalizeDef(ddef, mods1, start) } @@ -3389,7 +3389,6 @@ object Parsers { * | [‘case’] ‘object’ ObjectDef * | ‘enum’ EnumDef * | ‘given’ GivenDef - * | ‘extension’ ExtensionDef */ def tmplDef(start: Int, mods: Modifiers): Tree = in.token match { @@ -3507,9 +3506,9 @@ object Parsers { syntaxError(i"no extension method allowed here since leading parameter was already given", stat.span) else if !stat.mods.is(ExtensionMethod) && vparamss.isEmpty then syntaxError(i"an extension method is required here", stat.span) - else if tparams.nonEmpty && stat.tparams.nonEmpty then + else if tparams.nonEmpty && stat.leadingTypeParams.nonEmpty then syntaxError(i"extension method cannot have type parameters since some were already given previously", - stat.tparams.head.span) + stat.leadingTypeParams.head.span) else if stat.rhs.isEmpty then syntaxError(i"extension method cannot be abstract", stat.span) case EmptyTree => @@ -3544,11 +3543,11 @@ object Parsers { mods1 |= Lazy ValDef(name, parents.head, subExpr()) else - DefDef(name, tparams, vparamss, parents.head, subExpr()) + DefDef(name, joinParams(tparams, vparamss), parents.head, subExpr()) else if in.token != WITH && parentsIsType then if name.isEmpty then syntaxError(em"anonymous given cannot be abstract") - DefDef(name, tparams, vparamss, parents.head, EmptyTree) + DefDef(name, joinParams(tparams, vparamss), parents.head, EmptyTree) else val tparams1 = tparams.map(tparam => tparam.withMods(tparam.mods | PrivateLocal)) val vparamss1 = vparamss.map(_.map(vparam => @@ -3566,8 +3565,15 @@ object Parsers { def extension(): ExtMethods = val start = in.skipToken() val tparams = typeParamClauseOpt(ParamOwner.Def) - val extParams = paramClause(0, prefix = true) - val givenParamss = paramClauses(givenOnly = true) + val leadParamss = ListBuffer[List[ValDef]]() + var nparams = 0 + while + val extParams = paramClause(nparams, prefix = true) + leadParamss += extParams + nparams += extParams.length + isUsingClause(extParams) + do () + leadParamss ++= paramClauses(givenOnly = true) if in.token == COLON then syntaxError("no `:` expected here") in.nextToken() @@ -3579,7 +3585,7 @@ object Parsers { newLineOptWhenFollowedBy(LBRACE) if in.isNestedStart then inDefScopeBraces(extMethods()) else { syntaxError("Extension without extension methods"); Nil } - val result = atSpan(start)(ExtMethods(tparams, extParams :: givenParamss, methods)) + val result = atSpan(start)(ExtMethods(joinParams(tparams, leadParamss.toList), methods)) val comment = in.getDocComment(start) if comment.isDefined then for meth <- methods do @@ -3835,7 +3841,7 @@ object Parsers { val problem = tree match case tree: MemberDef if !(tree.mods.flags & ModifierFlags).isEmpty => i"refinement cannot be ${(tree.mods.flags & ModifierFlags).flagStrings().mkString("`", "`, `", "`")}" - case tree: DefDef if tree.vparamss.nestedExists(!_.rhs.isEmpty) => + case tree: DefDef if tree.termParamss.nestedExists(!_.rhs.isEmpty) => i"refinement cannot have default arguments" case tree: ValOrDefDef => if tree.rhs.isEmpty then "" diff --git a/compiler/src/dotty/tools/dotc/printing/DecompilerPrinter.scala b/compiler/src/dotty/tools/dotc/printing/DecompilerPrinter.scala index a72ffa474e43..bd1cc574cf27 100644 --- a/compiler/src/dotty/tools/dotc/printing/DecompilerPrinter.scala +++ b/compiler/src/dotty/tools/dotc/printing/DecompilerPrinter.scala @@ -18,13 +18,13 @@ class DecompilerPrinter(_ctx: Context) extends RefinedPrinter(_ctx) { override protected def blockToText[T >: Untyped](block: Block[T]): Text = block match { - case Block(DefDef(_, _, _, _, Trees.If(cond, Trees.Block(body :: Nil, _), _)) :: Nil, y) if y.symbol.name == nme.WHILE_PREFIX => + case Block(DefDef(_, _, _, Trees.If(cond, Trees.Block(body :: Nil, _), _)) :: Nil, y) if y.symbol.name == nme.WHILE_PREFIX => keywordText("while") ~ " (" ~ toText(cond) ~ ")" ~ toText(body) - case Block(DefDef(_, _, _, _, Trees.Block(body :: Nil, Trees.If(cond, _, _))) :: Nil, y) if y.symbol.name == nme.DO_WHILE_PREFIX => + case Block(DefDef(_, _, _, Trees.Block(body :: Nil, Trees.If(cond, _, _))) :: Nil, y) if y.symbol.name == nme.DO_WHILE_PREFIX => keywordText("do") ~ toText(body) ~ keywordText("while") ~ " (" ~ toText(cond) ~ ")" - case Block((meth @ DefDef(nme.ANON_FUN, _, _, _, _)) :: Nil, _: Closure[T]) => + case Block((meth @ DefDef(nme.ANON_FUN, _, _, _)) :: Nil, _: Closure[T]) => withEnclosingDef(meth) { - addVparamssText("", meth.vparamss) ~ " => " ~ toText(meth.rhs) + addParamssText("", meth.termParamss) ~ " => " ~ toText(meth.rhs) } case _ => super.blockToText(block) diff --git a/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala b/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala index dabc9d25acf1..5cf45fd2078d 100644 --- a/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala +++ b/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala @@ -492,7 +492,7 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) { toTextLocal(tpt) ~ "[" ~ Text(args.map(argText), ", ") ~ "]" case LambdaTypeTree(tparams, body) => changePrec(GlobalPrec) { - tparamsText(tparams) ~ " =>> " ~ toText(body) + paramsText(tparams) ~ " =>> " ~ toText(body) } case TermLambdaTypeTree(params, body) => changePrec(GlobalPrec) { @@ -523,7 +523,7 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) { ("(" ~ toTextGlobal(implicits, ", ") ~ ")" provided implicits.nonEmpty) case tree @ ValDef(_, _, _) => valDefToText(tree) - case tree @ DefDef(_, _, _, _, _) => + case tree @ DefDef(_, _, _, _) => defDefToText(tree) case tree @ TypeDef(name, rhs) => def typeDefText(tparamsText: => Text, rhsText: => Text) = @@ -538,7 +538,7 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) { case rhs: TypeBoundsTree => typeDefText(tparamsTxt, toText(rhs)) case LambdaTypeTree(tparams, body) if printMemberArgs => - recur(body, tparamsText(tparams), false) + recur(body, paramsText(tparams), false) case rhs: TypeTree if isBounds(rhs.typeOpt) => typeDefText(tparamsTxt, toText(rhs)) case rhs => @@ -549,9 +549,8 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) { keywordText("import ") ~ importText(expr, selectors) case Export(expr, selectors) => keywordText("export ") ~ importText(expr, selectors) - case ExtMethods(tparams, vparamss, mdefs) => - keywordText("extension ") - ~ addVparamssText(tparamsText(tparams), vparamss) + case ExtMethods(paramss, mdefs) => + addParamssText(keywordText("extension "), paramss) ~ " " ~ (if mdefs.length == 1 then toText(mdefs.head) else blockText(mdefs)) case packageDef: PackageDef => packageDefText(packageDef) @@ -646,8 +645,8 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) { keywordStr("${") ~ toTextGlobal(dropBlock(tree)) ~ keywordStr("}") case TypSplice(tree) => keywordStr("${") ~ toTextGlobal(dropBlock(tree)) ~ keywordStr("}") - case tree: Applications.IntegratedTypeArgs => - toText(tree.app) ~ Str("(with integrated type args)").provided(printDebug) + case tree: Applications.ExtMethodApply => + toText(tree.app) ~ Str("(ext method apply)").provided(printDebug) case Thicket(trees) => "Thicket {" ~~ toTextGlobal(trees, "\n") ~~ "}" case MacroTree(call) => @@ -772,11 +771,18 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) { else treeText } - def tparamsText[T >: Untyped](params: List[Tree[T]]): Text = - "[" ~ toText(params, ", ") ~ "]" provided params.nonEmpty + def paramsText[T>: Untyped](params: ParamClause[T]): Text = (params: @unchecked) match + case Nil => + "()" + case untpd.ValDefs(vparams @ (vparam :: _)) => + "(" ~ keywordText("using ").provided(vparam.mods.is(Given)) + ~ keywordText("erased ").provided(vparam.mods.is(Erased)) + ~ toText(vparams, ", ") ~ ")" + case untpd.TypeDefs(tparams) => + "[" ~ toText(tparams, ", ") ~ "]" - def addVparamssText[T >: Untyped](leading: Text, vparamss: List[List[ValDef[T]]]): Text = - vparamss.foldLeft(leading)((txt, params) => txt ~ paramsText(params)) + def addParamssText[T >: Untyped](leading: Text, paramss: List[ParamClause[T]]): Text = + paramss.foldLeft(leading)((txt, params) => txt ~ paramsText(params)) protected def valDefToText[T >: Untyped](tree: ValDef[T]): Text = { import untpd._ @@ -787,47 +793,60 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) { } } - private def paramsText[T>: Untyped](params: List[ValDef[T]]) = - "(" ~ keywordText("using ").provided(params.nonEmpty && params.head.mods.is(Given)) - ~ keywordText("erased ").provided(params.nonEmpty && params.head.mods.is(Erased)) - ~ toText(params, ", ") ~ ")" - protected def defDefToText[T >: Untyped](tree: DefDef[T]): Text = { import untpd._ dclTextOr(tree) { val defKeyword = modText(tree.mods, tree.symbol, keywordStr("def"), isType = false) val isExtension = tree.hasType && tree.symbol.is(ExtensionMethod) withEnclosingDef(tree) { - val (prefix, vparamss) = + val coreSig = if isExtension then - val (leadingParams, otherParamss) = (tree.vparamss: @unchecked) match - case vparams1 :: vparams2 :: rest if tree.name.isRightAssocOperatorName => - (vparams2, vparams1 :: rest) - case vparams1 :: rest => - (vparams1, rest) - (keywordStr("extension") ~~ paramsText(leadingParams) - ~~ (defKeyword ~~ valDefText(nameIdText(tree))).close, - otherParamss) - else (defKeyword ~~ valDefText(nameIdText(tree)), tree.vparamss) - - addVparamssText(prefix ~ tparamsText(tree.tparams), vparamss) ~ - optAscription(tree.tpt) ~ - optText(tree.rhs)(" = " ~ keywordText("macro ").provided(tree.symbol.isScala2Macro) ~ _) + val paramss = + if tree.name.isRightAssocOperatorName then + // we have the encoding: leadingUsingOrTypeParams rightParam trailingUsing leftParam + // we need to swap rightParam and leftParam + val (leadingUsing, rest1) = tree.paramss.span(isUsingOrTypeParamClause) + val (rightParamss, rest2) = rest1.splitAt(1) + val (trailingUsing, rest3) = rest2.span(isUsingClause) + val (leftParamss, rest4) = rest3.splitAt(1) + if leftParamss.nonEmpty then + leadingUsing ::: leftParamss ::: trailingUsing ::: rightParamss ::: rest4 + else + tree.paramss // it wasn't a binary operator, after all. + else + tree.paramss + val trailingParamss = paramss + .dropWhile(isUsingOrTypeParamClause) + .drop(1) + .dropWhile(isUsingClause) + val leadingParamss = paramss.take(paramss.length - trailingParamss.length) + addParamssText( + addParamssText(keywordStr("extension "), leadingParamss) + ~~ (defKeyword ~~ valDefText(nameIdText(tree))).close, + trailingParamss) + else + addParamssText(defKeyword ~~ valDefText(nameIdText(tree)), tree.paramss) + + coreSig + ~ optAscription(tree.tpt) + ~ optText(tree.rhs)(" = " ~ keywordText("macro ").provided(tree.symbol.isScala2Macro) ~ _) } } } protected def toTextTemplate(impl: Template, ofNew: Boolean = false): Text = { - val Template(constr @ DefDef(_, tparams, vparamss, _, _), _, self, _) = impl - val tparamsTxt = withEnclosingDef(constr) { tparamsText(tparams) } + val Template(constr @ DefDef(_, paramss, _, _), _, self, _) = impl + val tparamsTxt = withEnclosingDef(constr) { + paramsText(constr.leadingTypeParams) provided constr.leadingTypeParams.nonEmpty + } val primaryConstrs = if (constr.rhs.isEmpty) Nil else constr :: Nil val prefix: Text = - if (vparamss.isEmpty || primaryConstrs.nonEmpty) tparamsTxt + if (constr.trailingParamss.isEmpty || primaryConstrs.nonEmpty) tparamsTxt else { var modsText = modText(constr.mods, constr.symbol, "", isType = false) if (!modsText.isEmpty) modsText = " " ~ modsText if (constr.mods.hasAnnotations && !constr.mods.hasFlags) modsText = modsText ~~ " this" - withEnclosingDef(constr) { addVparamssText(tparamsTxt ~~ modsText, vparamss) } + withEnclosingDef(constr) { addParamssText(tparamsTxt ~~ modsText, constr.trailingParamss) } } val parentsText = Text(impl.parents.map(constrText), if (ofNew) keywordStr(" with ") else ", ") val derivedText = Text(impl.derived.map(toText(_)), ", ") diff --git a/compiler/src/dotty/tools/dotc/reporting/messages.scala b/compiler/src/dotty/tools/dotc/reporting/messages.scala index ec522e12efa5..2c3689749334 100644 --- a/compiler/src/dotty/tools/dotc/reporting/messages.scala +++ b/compiler/src/dotty/tools/dotc/reporting/messages.scala @@ -403,8 +403,8 @@ import transform.SymUtils._ def explain = { val TypeDef(name, impl @ Template(constr0, parents, self, _)) = cdef val exampleArgs = - if(constr0.vparamss.isEmpty) "..." - else constr0.vparamss(0).map(_.withMods(untpd.Modifiers()).show).mkString(", ") + if(constr0.termParamss.isEmpty) "..." + else constr0.termParamss(0).map(_.withMods(untpd.Modifiers()).show).mkString(", ") def defHasBody[T] = impl.body.exists(!_.isEmpty) val exampleBody = if (defHasBody) "{\n ...\n }" else "" em"""|There may not be any method, member or object in scope with the same name as @@ -2432,7 +2432,7 @@ import transform.SymUtils._ def explain = em"""|Extension method: | `${mdef}` - |has type parameters `[${mdef.tparams.map(_.show).mkString(",")}]`, while the extension clause has + |has type parameters `[${mdef.leadingTypeParams.map(_.show).mkString(",")}]`, while the extension clause has |it's own type parameters. Please consider moving these to the extension clause's type parameter list. |""".stripMargin } diff --git a/compiler/src/dotty/tools/dotc/semanticdb/ExtractSemanticDB.scala b/compiler/src/dotty/tools/dotc/semanticdb/ExtractSemanticDB.scala index 4ee3b4db9210..e1b5abe799e1 100644 --- a/compiler/src/dotty/tools/dotc/semanticdb/ExtractSemanticDB.scala +++ b/compiler/src/dotty/tools/dotc/semanticdb/ExtractSemanticDB.scala @@ -162,14 +162,13 @@ class ExtractSemanticDB extends Phase: traverse(tree.tpt) case tree: DefDef if tree.symbol.isConstructor => // ignore typeparams for secondary ctors - tree.vparamss.foreach(_.foreach(traverse)) + tree.trailingParamss.foreach(_.foreach(traverse)) traverse(tree.rhs) case tree: (DefDef | ValDef) if tree.symbol.isSyntheticWithIdent => tree match case tree: DefDef => - tree.tparams.foreach(tparam => registerSymbolSimple(tparam.symbol)) - tree.vparamss.foreach(_.foreach(vparam => registerSymbolSimple(vparam.symbol))) + tree.paramss.foreach(_.foreach(param => registerSymbolSimple(param.symbol))) case _ => if !tree.symbol.isGlobal then localBodies(tree.symbol) = tree.rhs @@ -185,7 +184,7 @@ class ExtractSemanticDB extends Phase: if !excludeDef(ctorSym) then traverseAnnotsOfDefinition(ctorSym) registerDefinition(ctorSym, tree.constr.nameSpan.startPos, Set.empty, tree.source) - ctorParams(tree.constr.vparamss, tree.body) + ctorParams(tree.constr.termParamss, tree.body) for parent <- tree.parentsOrDerived if parent.span.hasLength do traverse(parent) val selfSpan = tree.self.span @@ -498,7 +497,7 @@ class ExtractSemanticDB extends Phase: extension (tree: DefDef) private def isSetterDef(using Context): Boolean = - tree.name.isSetterName && tree.mods.is(Accessor) && tree.vparamss.isSingleArg + tree.name.isSetterName && tree.mods.is(Accessor) && tree.termParamss.isSingleArg private def findGetters(ctorParams: Set[Names.TermName], body: List[Tree])(using Context): Map[Names.TermName, ValDef] = if ctorParams.isEmpty || body.isEmpty then diff --git a/compiler/src/dotty/tools/dotc/transform/AccessProxies.scala b/compiler/src/dotty/tools/dotc/transform/AccessProxies.scala index 321aadac4130..6d6dc6fb3956 100644 --- a/compiler/src/dotty/tools/dotc/transform/AccessProxies.scala +++ b/compiler/src/dotty/tools/dotc/transform/AccessProxies.scala @@ -37,23 +37,24 @@ abstract class AccessProxies { */ private def accessorDefs(cls: Symbol)(using Context): Iterator[DefDef] = for (accessor <- cls.info.decls.iterator; accessed <- accessedBy.remove(accessor).toOption) yield - polyDefDef(accessor.asTerm, tps => argss => { + DefDef(accessor.asTerm, prefss => { def numTypeParams = accessed.info match { case info: PolyType => info.paramNames.length case _ => 0 } - val (accessRef, forwardedTypes, forwardedArgss) = + val (targs, argss) = splitArgs(prefss) + val (accessRef, forwardedTpts, forwardedArgss) = if (passReceiverAsArg(accessor.name)) - (argss.head.head.select(accessed), tps.takeRight(numTypeParams), argss.tail) + (argss.head.head.select(accessed), targs.takeRight(numTypeParams), argss.tail) else (if (accessed.isStatic) ref(accessed) else ref(TermRef(cls.thisType, accessed)), - tps, argss) + targs, argss) val rhs = if (accessor.name.isSetterName && forwardedArgss.nonEmpty && forwardedArgss.head.nonEmpty) // defensive conditions accessRef.becomes(forwardedArgss.head.head) else - accessRef.appliedToTypes(forwardedTypes).appliedToArgss(forwardedArgss) + accessRef.appliedToTypeTrees(forwardedTpts).appliedToArgss(forwardedArgss) rhs.withSpan(accessed.span) }) diff --git a/compiler/src/dotty/tools/dotc/transform/BetaReduce.scala b/compiler/src/dotty/tools/dotc/transform/BetaReduce.scala index 4d8e64eecaab..17ce6020e2a9 100644 --- a/compiler/src/dotty/tools/dotc/transform/BetaReduce.scala +++ b/compiler/src/dotty/tools/dotc/transform/BetaReduce.scala @@ -69,7 +69,7 @@ object BetaReduce: /** Beta-reduces a call to `ddef` with arguments `argSyms` */ def apply(ddef: DefDef, args: List[Tree])(using Context) = val bindings = List.newBuilder[ValDef] - val vparams = ddef.vparamss.iterator.flatten.toList + val vparams = ddef.termParamss.iterator.flatten.toList assert(args.hasSameLengthAs(vparams)) val argSyms = for (arg, param) <- args.zip(vparams) yield diff --git a/compiler/src/dotty/tools/dotc/transform/CompleteJavaEnums.scala b/compiler/src/dotty/tools/dotc/transform/CompleteJavaEnums.scala index 9a7665d2286e..4aa352f60f1b 100644 --- a/compiler/src/dotty/tools/dotc/transform/CompleteJavaEnums.scala +++ b/compiler/src/dotty/tools/dotc/transform/CompleteJavaEnums.scala @@ -89,8 +89,10 @@ class CompleteJavaEnums extends MiniPhase with InfoTransformer { thisPhase => val sym = tree.symbol if sym.isConstructor && sym.owner.derivesFromJavaEnum then val tree1 = cpy.DefDef(tree)( - vparamss = tree.vparamss.init :+ (tree.vparamss.last ++ addedParams(sym, isLocal=false, Param))) - sym.setParamssFromDefs(tree1.tparams, tree1.vparamss) + paramss = tree.paramss.init + :+ (tree.paramss.last.asInstanceOf[List[ValDef]] + ++ addedParams(sym, isLocal=false, Param))) + sym.setParamssFromDefs(tree1.paramss) tree1 else tree } diff --git a/compiler/src/dotty/tools/dotc/transform/Constructors.scala b/compiler/src/dotty/tools/dotc/transform/Constructors.scala index a778ed2325b5..16666795ab77 100644 --- a/compiler/src/dotty/tools/dotc/transform/Constructors.scala +++ b/compiler/src/dotty/tools/dotc/transform/Constructors.scala @@ -127,7 +127,7 @@ class Constructors extends MiniPhase with IdentityDenotTransformer { thisPhase = override def transformTemplate(tree: Template)(using Context): Tree = { val cls = ctx.owner.asClass - val constr @ DefDef(nme.CONSTRUCTOR, Nil, vparams :: Nil, _, EmptyTree) = tree.constr + val constr @ DefDef(nme.CONSTRUCTOR, (vparams: List[ValDef] @unchecked) :: Nil, _, EmptyTree) = tree.constr // Produce aligned accessors and constructor parameters. We have to adjust // for any outer parameters, which are last in the sequence of original @@ -207,7 +207,7 @@ class Constructors extends MiniPhase with IdentityDenotTransformer { thisPhase = constrStats += intoConstr(stat, sym) } else dropped += sym - case stat @ DefDef(name, _, _, tpt, _) + case stat @ DefDef(name, _, tpt, _) if stat.symbol.isGetter && stat.symbol.owner.is(Trait) && !stat.symbol.is(Lazy) && !stat.symbol.isConstExprFinalVal => val sym = stat.symbol assert(isRetained(sym), sym) @@ -232,7 +232,7 @@ class Constructors extends MiniPhase with IdentityDenotTransformer { thisPhase = else sym.accessorNamed(Mixin.traitSetterName(sym.asTerm)) constrStats += Apply(ref(setter), intoConstr(stat.rhs, sym).withSpan(stat.span) :: Nil) clsStats += cpy.DefDef(stat)(rhs = EmptyTree) - case DefDef(nme.CONSTRUCTOR, _, ((outerParam @ ValDef(nme.OUTER, _, _)) :: _) :: Nil, _, _) => + case DefDef(nme.CONSTRUCTOR, ((outerParam @ ValDef(nme.OUTER, _, _)) :: _) :: Nil, _, _) => clsStats += mapOuter(outerParam.symbol).transform(stat) case _: DefTree => clsStats += stat diff --git a/compiler/src/dotty/tools/dotc/transform/ElimRepeated.scala b/compiler/src/dotty/tools/dotc/transform/ElimRepeated.scala index c02a434da7ef..c1be520cf0d5 100644 --- a/compiler/src/dotty/tools/dotc/transform/ElimRepeated.scala +++ b/compiler/src/dotty/tools/dotc/transform/ElimRepeated.scala @@ -222,15 +222,14 @@ class ElimRepeated extends MiniPhase with InfoTransformer { thisPhase => .get .symbol.asTerm // Generate the method - val forwarderDef = polyDefDef(forwarderSym, trefs => vrefss => { - val init :+ (last :+ vararg) = vrefss + val forwarderDef = DefDef(forwarderSym, prefss => { + val init :+ (last :+ vararg) = prefss // Can't call `.argTypes` here because the underlying array type is of the // form `Array[? <: SomeType]`, so we need `.argInfos` to get the `TypeBounds`. val elemtp = vararg.tpe.widen.argInfos.head ref(sym.termRef) - .appliedToTypes(trefs) .appliedToArgss(init) - .appliedToArgs(last :+ wrapArray(vararg, elemtp)) + .appliedToTermArgs(last :+ wrapArray(vararg, elemtp)) }) Thicket(tree, forwarderDef) else diff --git a/compiler/src/dotty/tools/dotc/transform/Erasure.scala b/compiler/src/dotty/tools/dotc/transform/Erasure.scala index 28417bcf5930..0d80ef87056f 100644 --- a/compiler/src/dotty/tools/dotc/transform/Erasure.scala +++ b/compiler/src/dotty/tools/dotc/transform/Erasure.scala @@ -893,13 +893,15 @@ object Erasure { else val restpe = if sym.isConstructor then defn.UnitType else sym.info.resultType var vparams = outerParamDefs(sym) - ::: ddef.vparamss.flatten.filterConserve(!_.symbol.is(Flags.Erased)) + ::: ddef.paramss.collect { + case untpd.ValDefs(vparams) => vparams + }.flatten.filterConserve(!_.symbol.is(Flags.Erased)) def skipContextClosures(rhs: Tree, crCount: Int)(using Context): Tree = if crCount == 0 then rhs else rhs match case closureDef(meth) => - val contextParams = meth.vparamss.head + val contextParams = meth.termParamss.head for param <- contextParams do param.symbol.copySymDenotation(owner = sym).installAfter(erasurePhase) vparams ++= contextParams @@ -921,8 +923,7 @@ object Erasure { rhs1 = Block(paramDefs, rhs1) val ddef1 = untpd.cpy.DefDef(ddef)( - tparams = Nil, - vparamss = vparams :: Nil, + paramss = vparams :: Nil, tpt = untpd.TypedSplice(TypeTree(restpe).withSpan(ddef.tpt.span)), rhs = rhs1) super.typedDefDef(ddef1, sym) diff --git a/compiler/src/dotty/tools/dotc/transform/ExpandSAMs.scala b/compiler/src/dotty/tools/dotc/transform/ExpandSAMs.scala index 137ce38e9b12..c0ea4adee353 100644 --- a/compiler/src/dotty/tools/dotc/transform/ExpandSAMs.scala +++ b/compiler/src/dotty/tools/dotc/transform/ExpandSAMs.scala @@ -111,7 +111,7 @@ class ExpandSAMs extends MiniPhase: } } - val closureDef(anon @ DefDef(_, _, List(List(param)), _, _)) = tree + val closureDef(anon @ DefDef(_, List(List(param)), _, _)) = tree anon.rhs match { case PartialFunctionRHS(pf) => val anonSym = anon.symbol @@ -156,7 +156,7 @@ class ExpandSAMs extends MiniPhase: } def applyOrElseRhs(paramRefss: List[List[Tree]])(using Context) = { - val List(paramRef, defaultRef) = paramRefss.head + val List(paramRef, defaultRef) = paramRefss(1) def translateCase(cdef: CaseDef) = cdef.changeOwner(anonSym, applyOrElseFn) val defaultValue = defaultRef.select(nme.apply).appliedTo(paramRef) diff --git a/compiler/src/dotty/tools/dotc/transform/FirstTransform.scala b/compiler/src/dotty/tools/dotc/transform/FirstTransform.scala index 3f6693440207..12e85793205b 100644 --- a/compiler/src/dotty/tools/dotc/transform/FirstTransform.scala +++ b/compiler/src/dotty/tools/dotc/transform/FirstTransform.scala @@ -117,17 +117,14 @@ class FirstTransform extends MiniPhase with InfoTransformer { thisPhase => override def transformTemplate(impl: Template)(using Context): Tree = cpy.Template(impl)(self = EmptyValDef) - override def transformDefDef(ddef: DefDef)(using Context): Tree = { + override def transformDefDef(ddef: DefDef)(using Context): Tree = val meth = ddef.symbol.asTerm - if (meth.hasAnnotation(defn.NativeAnnot)) { + if meth.hasAnnotation(defn.NativeAnnot) then meth.resetFlag(Deferred) - polyDefDef(meth, - _ => _ => ref(defn.Sys_error.termRef).withSpan(ddef.span) + DefDef(meth, _ => + ref(defn.Sys_error.termRef).withSpan(ddef.span) .appliedTo(Literal(Constant(s"native method stub")))) - } - else ddef - } override def transformStats(trees: List[Tree])(using Context): List[Tree] = ast.Trees.flatten(atPhase(thisPhase.next)(reorderAndComplete(trees))) diff --git a/compiler/src/dotty/tools/dotc/transform/FullParameterization.scala b/compiler/src/dotty/tools/dotc/transform/FullParameterization.scala index fa8e39eb6c1e..93f72199617e 100644 --- a/compiler/src/dotty/tools/dotc/transform/FullParameterization.scala +++ b/compiler/src/dotty/tools/dotc/transform/FullParameterization.scala @@ -136,8 +136,9 @@ trait FullParameterization { */ private def allInstanceTypeParams(originalDef: DefDef, abstractOverClass: Boolean)(using Context): List[Symbol] = if (abstractOverClass) - originalDef.tparams.map(_.symbol) ::: originalDef.symbol.enclosingClass.typeParams - else originalDef.tparams.map(_.symbol) + originalDef.leadingTypeParams.map(_.symbol) ::: originalDef.symbol.enclosingClass.typeParams + else + originalDef.leadingTypeParams.map(_.symbol) /** Given an instance method definition `originalDef`, return a * fully parameterized method definition derived from `originalDef`, which @@ -147,11 +148,12 @@ trait FullParameterization { * of class that contained original defDef */ def fullyParameterizedDef(derived: TermSymbol, originalDef: DefDef, abstractOverClass: Boolean = true)(using Context): Tree = - polyDefDef(derived, trefs => vrefss => { + DefDef(derived, prefss => { + val (trefs, vrefss) = splitArgs(prefss) val origMeth = originalDef.symbol val origClass = origMeth.enclosingClass.asClass - val origTParams = allInstanceTypeParams(originalDef, abstractOverClass) - val origVParams = originalDef.vparamss.flatten map (_.symbol) + val origLeadingTypeParamSyms = allInstanceTypeParams(originalDef, abstractOverClass) + val origOtherParamSyms = originalDef.trailingParamss.flatten.map(_.symbol) val thisRef :: argRefs = vrefss.flatten /** If tree should be rewired, the rewired tree, otherwise EmptyTree. @@ -204,7 +206,7 @@ trait FullParameterization { new TreeTypeMap( typeMap = rewireType(_) - .subst(origTParams ++ origVParams, trefs ++ argRefs.map(_.tpe)) + .subst(origLeadingTypeParamSyms ++ origOtherParamSyms, (trefs ++ argRefs).tpes) .substThisUnlessStatic(origClass, thisRef.tpe), treeMap = { case tree: This if tree.symbol == origClass => thisRef @@ -221,25 +223,25 @@ trait FullParameterization { * - the value parameters of the original method `originalDef`. */ def forwarder(derived: TermSymbol, originalDef: DefDef, abstractOverClass: Boolean = true, liftThisType: Boolean = false)(using Context): Tree = { - val fun = + val fun: Tree = ref(derived.termRef) .appliedToTypes(allInstanceTypeParams(originalDef, abstractOverClass).map(_.typeRef)) .appliedTo(This(originalDef.symbol.enclosingClass.asClass)) - - (if (!liftThisType) - fun.appliedToArgss(originalDef.vparamss.nestedMap(vparam => ref(vparam.symbol))) - else { - // this type could have changed on forwarding. Need to insert a cast. - originalDef.vparamss.foldLeft(fun)((acc, vparams) => { + val fwd = + if !liftThisType then + fun.appliedToArgss(originalDef.trailingParamss.nestedMap(param => ref(param.symbol))) + else + // this type could have changed on forwarding. Need to insert a cast. + originalDef.trailingParamss.foldLeft(fun)((acc, params) => { val meth = acc.tpe.asInstanceOf[MethodType] - val paramTypes = meth.instantiateParamInfos(vparams.map(_.tpe)) + val paramTypes = meth.instantiateParamInfos(params.tpes) acc.appliedToArgs( - vparams.lazyZip(paramTypes).map((vparam, paramType) => { - assert(vparam.tpe <:< paramType.widen) // type should still conform to widened type - ref(vparam.symbol).ensureConforms(paramType) + params.lazyZip(paramTypes).map((param, paramType) => { + assert(param.tpe <:< paramType.widen) // type should still conform to widened type + ref(param.symbol).ensureConforms(paramType) })) - }) - }).withSpan(originalDef.rhs.span) + }) + fwd.withSpan(originalDef.rhs.span) } } diff --git a/compiler/src/dotty/tools/dotc/transform/FunctionXXLForwarders.scala b/compiler/src/dotty/tools/dotc/transform/FunctionXXLForwarders.scala index 3ef0debb93e0..4fd075aa0dba 100644 --- a/compiler/src/dotty/tools/dotc/transform/FunctionXXLForwarders.scala +++ b/compiler/src/dotty/tools/dotc/transform/FunctionXXLForwarders.scala @@ -34,7 +34,7 @@ class FunctionXXLForwarders extends MiniPhase with IdentityDenotTransformer { var idx = -1 val argss = receiver.tpe.widenDealias.paramInfoss.map(_.map { param => idx += 1 - argsApply.appliedToArgs(List(Literal(Constant(idx)))).cast(param) + argsApply.appliedToTermArgs(List(Literal(Constant(idx)))).cast(param) }) ref(receiver.symbol).appliedToArgss(argss).cast(defn.ObjectType) } diff --git a/compiler/src/dotty/tools/dotc/transform/HoistSuperArgs.scala b/compiler/src/dotty/tools/dotc/transform/HoistSuperArgs.scala index 851d65a1fcfc..219e51050961 100644 --- a/compiler/src/dotty/tools/dotc/transform/HoistSuperArgs.scala +++ b/compiler/src/dotty/tools/dotc/transform/HoistSuperArgs.scala @@ -129,8 +129,9 @@ class HoistSuperArgs extends MiniPhase with IdentityDenotTransformer { thisPhase cpy.Apply(arg)(fn, hoistSuperArg(arg1, cdef) :: Nil) case _ if arg.existsSubTree(needsHoist) => val superMeth = newSuperArgMethod(arg.tpe) - val superArgDef = polyDefDef(superMeth, trefs => vrefss => { - val paramSyms = trefs.map(_.typeSymbol) ::: vrefss.flatten.map(_.symbol) + val superArgDef = DefDef(superMeth, prefss => { + val paramSyms = prefss.flatten.map(pref => + if pref.isType then pref.tpe.typeSymbol else pref.symbol) val tmap = new TreeTypeMap( typeMap = new TypeMap { lazy val origToParam = origParams.zip(paramSyms).toMap diff --git a/compiler/src/dotty/tools/dotc/transform/InlinePatterns.scala b/compiler/src/dotty/tools/dotc/transform/InlinePatterns.scala index e86b8e5f3f89..fdef553a835d 100644 --- a/compiler/src/dotty/tools/dotc/transform/InlinePatterns.scala +++ b/compiler/src/dotty/tools/dotc/transform/InlinePatterns.scala @@ -56,6 +56,6 @@ class InlinePatterns extends MiniPhase: fn match case Block(TypeDef(_, template: Template) :: Nil, Apply(Select(New(_),_), Nil)) if template.constr.rhs.isEmpty => template.body match - case List(ddef @ DefDef(`name`, _, _, _, _)) => BetaReduce(ddef, args) + case List(ddef @ DefDef(`name`, _, _, _)) => BetaReduce(ddef, args) case _ => tree case _ => tree diff --git a/compiler/src/dotty/tools/dotc/transform/InterceptedMethods.scala b/compiler/src/dotty/tools/dotc/transform/InterceptedMethods.scala index eaa77ff56c6c..0b42162643f8 100644 --- a/compiler/src/dotty/tools/dotc/transform/InterceptedMethods.scala +++ b/compiler/src/dotty/tools/dotc/transform/InterceptedMethods.scala @@ -73,7 +73,7 @@ class InterceptedMethods extends MiniPhase { } if tree.fun.symbol == defn.Any_!= then - qual.select(defn.Any_==).appliedToArgs(tree.args).select(defn.Boolean_!).withSpan(tree.span) + qual.select(defn.Any_==).appliedToTermArgs(tree.args).select(defn.Boolean_!).withSpan(tree.span) else tree } diff --git a/compiler/src/dotty/tools/dotc/transform/LambdaLift.scala b/compiler/src/dotty/tools/dotc/transform/LambdaLift.scala index e8791efeb97c..2409d0f94815 100644 --- a/compiler/src/dotty/tools/dotc/transform/LambdaLift.scala +++ b/compiler/src/dotty/tools/dotc/transform/LambdaLift.scala @@ -236,7 +236,7 @@ object LambdaLift { // the free variables of the class. symSet(called, sym) += sym.owner - tree.vparamss.head.find(_.name == nme.OUTER) match { + tree.termParamss.head.find(_.name == nme.OUTER) match { case Some(vdef) => outerParam(sym) = vdef.symbol case _ => } @@ -454,7 +454,7 @@ object LambdaLift { tree match { case tree: DefDef => cpy.DefDef(tree)( - vparamss = tree.vparamss.map(freeParamDefs ++ _), + paramss = tree.termParamss.map(freeParamDefs ++ _), rhs = if (sym.isPrimaryConstructor && !sym.owner.is(Trait)) copyParams(tree.rhs) else tree.rhs) diff --git a/compiler/src/dotty/tools/dotc/transform/MegaPhase.scala b/compiler/src/dotty/tools/dotc/transform/MegaPhase.scala index 385d02638b9f..77671fc6498c 100644 --- a/compiler/src/dotty/tools/dotc/transform/MegaPhase.scala +++ b/compiler/src/dotty/tools/dotc/transform/MegaPhase.scala @@ -243,11 +243,11 @@ class MegaPhase(val miniPhases: Array[MiniPhase]) extends Phase { case tree: DefDef => inContext(prepDefDef(tree, start)(using outerCtx)) { def mapDefDef(using Context) = { - val tparams = transformSpecificTrees(tree.tparams, start) - val vparamss = tree.vparamss.mapConserve(transformSpecificTrees(_, start)) + val paramss = tree.paramss.mapConserve(transformSpecificTrees(_, start)) + .asInstanceOf[List[ParamClause]] val tpt = transformTree(tree.tpt, start) val rhs = transformTree(tree.rhs, start) - cpy.DefDef(tree)(tree.name, tparams, vparamss, tpt, rhs) + cpy.DefDef(tree)(tree.name, paramss, tpt, rhs) } goDefDef(inLocalContext(mapDefDef), start) } diff --git a/compiler/src/dotty/tools/dotc/transform/Memoize.scala b/compiler/src/dotty/tools/dotc/transform/Memoize.scala index fa5f97c2ae25..2f73ed8bd9eb 100644 --- a/compiler/src/dotty/tools/dotc/transform/Memoize.scala +++ b/compiler/src/dotty/tools/dotc/transform/Memoize.scala @@ -185,8 +185,9 @@ class Memoize extends MiniPhase with IdentityDenotTransformer { thisPhase => field.setFlag(Mutable) myState.classesThatNeedReleaseFence += sym.owner val initializer = - if (isErasableBottomField(field, tree.vparamss.head.head.tpt.tpe.classSymbol)) Literal(Constant(())) - else Assign(ref(field), adaptToField(field, ref(tree.vparamss.head.head.symbol))) + if isErasableBottomField(field, tree.termParamss.head.head.tpt.tpe.classSymbol) + then Literal(Constant(())) + else Assign(ref(field), adaptToField(field, ref(tree.termParamss.head.head.symbol))) val setterDef = cpy.DefDef(tree)(rhs = transformFollowingDeep(initializer)(using ctx.withOwner(sym))) removeUnwantedAnnotations(sym, defn.SetterMetaAnnot) setterDef diff --git a/compiler/src/dotty/tools/dotc/transform/Mixin.scala b/compiler/src/dotty/tools/dotc/transform/Mixin.scala index c3ff36f3e5cb..fc335b66a25c 100644 --- a/compiler/src/dotty/tools/dotc/transform/Mixin.scala +++ b/compiler/src/dotty/tools/dotc/transform/Mixin.scala @@ -216,7 +216,7 @@ class Mixin extends MiniPhase with SymTransformer { thisPhase => case _ => val Apply(sel @ Select(New(_), nme.CONSTRUCTOR), args) = tree val (callArgs, initArgs) = if (tree.symbol.owner.is(Trait)) (Nil, args) else (args, Nil) - (superRef(tree.symbol, tree.span).appliedToArgs(callArgs), Nil, initArgs) + (superRef(tree.symbol, tree.span).appliedToTermArgs(callArgs), Nil, initArgs) } val superCallsAndArgs: Map[Symbol, (Tree, List[Tree], List[Tree])] = ( @@ -285,12 +285,12 @@ class Mixin extends MiniPhase with SymTransformer { thisPhase => for (meth <- mixin.info.decls.toList if needsMixinForwarder(meth)) yield { util.Stats.record("mixin forwarders") - transformFollowing(polyDefDef(mkForwarderSym(meth.asTerm, Bridge), forwarderRhsFn(meth))) + transformFollowing(DefDef(mkForwarderSym(meth.asTerm, Bridge), forwarderRhsFn(meth))) } cpy.Template(impl)( constr = - if (cls.is(Trait)) cpy.DefDef(impl.constr)(vparamss = Nil :: Nil) + if (cls.is(Trait)) cpy.DefDef(impl.constr)(paramss = Nil :: Nil) else impl.constr, parents = impl.parents.map(p => TypeTree(p.tpe).withSpan(p.span)), body = diff --git a/compiler/src/dotty/tools/dotc/transform/MixinOps.scala b/compiler/src/dotty/tools/dotc/transform/MixinOps.scala index a563f6eabe03..ff73705645a4 100644 --- a/compiler/src/dotty/tools/dotc/transform/MixinOps.scala +++ b/compiler/src/dotty/tools/dotc/transform/MixinOps.scala @@ -74,22 +74,20 @@ class MixinOps(cls: ClassSymbol, thisPhase: DenotTransformer)(using Context) { isCurrent(meth) } - final val PrivateOrAccessor: FlagSet = Private | Accessor final val PrivateOrAccessorOrDeferred: FlagSet = Private | Accessor | Deferred - def forwarderRhsFn(target: Symbol): List[Type] => List[List[Tree]] => Tree = { - targs => vrefss => - val tapp = superRef(target).appliedToTypes(targs) - vrefss match { + def forwarderRhsFn(target: Symbol): List[List[Tree]] => Tree = + prefss => + val (targs, vargss) = splitArgs(prefss) + val tapp = superRef(target).appliedToTypeTrees(targs) + vargss match case Nil | List(Nil) => // Overriding is somewhat loose about `()T` vs `=> T`, so just pick // whichever makes sense for `target` tapp.ensureApplied case _ => - tapp.appliedToArgss(vrefss) - } - } + tapp.appliedToArgss(vargss) private def competingMethodsIterator(meth: Symbol): Iterator[Symbol] = cls.baseClasses.iterator diff --git a/compiler/src/dotty/tools/dotc/transform/PatternMatcher.scala b/compiler/src/dotty/tools/dotc/transform/PatternMatcher.scala index 1be6bbda98fb..169f68e2cad9 100644 --- a/compiler/src/dotty/tools/dotc/transform/PatternMatcher.scala +++ b/compiler/src/dotty/tools/dotc/transform/PatternMatcher.scala @@ -390,7 +390,7 @@ object PatternMatcher { case mt: MethodType => assert(mt.isImplicitMethod) val (args, rest) = implicits.splitAt(mt.paramNames.size) - applyImplicits(acc.appliedToArgs(args), rest, mt.resultType) + applyImplicits(acc.appliedToTermArgs(args), rest, mt.resultType) case _ => assert(implicits.isEmpty) acc diff --git a/compiler/src/dotty/tools/dotc/transform/PickleQuotes.scala b/compiler/src/dotty/tools/dotc/transform/PickleQuotes.scala index cb1ffa478d70..0f00add41809 100644 --- a/compiler/src/dotty/tools/dotc/transform/PickleQuotes.scala +++ b/compiler/src/dotty/tools/dotc/transform/PickleQuotes.scala @@ -166,7 +166,7 @@ class PickleQuotes extends MacroTransform { val literalValue = if lit.const.tag == Constants.NullTag || lit.const.tag == Constants.UnitTag then Nil else List(body) - val constant = reflect.select(s"${typeName}Constant".toTermName).select(nme.apply).appliedToArgs(literalValue) + val constant = reflect.select(s"${typeName}Constant".toTermName).select(nme.apply).appliedToTermArgs(literalValue) val literal = reflect.select("Literal".toTermName).select(nme.apply).appliedTo(constant) reflect.select("TreeMethods".toTermName).select("asExpr".toTermName).appliedTo(literal).asInstance(exprType) } diff --git a/compiler/src/dotty/tools/dotc/transform/ResolveSuper.scala b/compiler/src/dotty/tools/dotc/transform/ResolveSuper.scala index 9e3d3c2c3b85..30ccc69e37ca 100644 --- a/compiler/src/dotty/tools/dotc/transform/ResolveSuper.scala +++ b/compiler/src/dotty/tools/dotc/transform/ResolveSuper.scala @@ -49,7 +49,7 @@ class ResolveSuper extends MiniPhase with IdentityDenotTransformer { thisPhase = for (superAcc <- mixin.info.decls.filter(_.isSuperAccessor)) yield { util.Stats.record("super accessors") - polyDefDef(mkForwarderSym(superAcc.asTerm), forwarderRhsFn(rebindSuper(cls, superAcc))) + DefDef(mkForwarderSym(superAcc.asTerm), forwarderRhsFn(rebindSuper(cls, superAcc))) } val overrides = mixins.flatMap(superAccessors) @@ -64,7 +64,7 @@ class ResolveSuper extends MiniPhase with IdentityDenotTransformer { thisPhase = val cls = meth.owner.asClass val ops = new MixinOps(cls, thisPhase) import ops._ - polyDefDef(meth, forwarderRhsFn(rebindSuper(cls, meth))) + DefDef(meth, forwarderRhsFn(rebindSuper(cls, meth))) } else ddef } diff --git a/compiler/src/dotty/tools/dotc/transform/SpecializeFunctions.scala b/compiler/src/dotty/tools/dotc/transform/SpecializeFunctions.scala index ace121e1b016..91555dc2b995 100644 --- a/compiler/src/dotty/tools/dotc/transform/SpecializeFunctions.scala +++ b/compiler/src/dotty/tools/dotc/transform/SpecializeFunctions.scala @@ -23,8 +23,8 @@ class SpecializeFunctions extends MiniPhase { */ override def transformDefDef(ddef: DefDef)(using Context) = { if ddef.name != nme.apply - || ddef.vparamss.length != 1 - || ddef.vparamss.head.length > 2 + || ddef.termParamss.length != 1 + || ddef.termParamss.head.length > 2 || !ctx.owner.isClass then return ddef @@ -35,7 +35,7 @@ class SpecializeFunctions extends MiniPhase { var specName: Name = null def isSpecializable = { - val paramTypes = ddef.vparamss.head.map(_.symbol.info) + val paramTypes = ddef.termParamss.head.map(_.symbol.info) val retType = sym.info.finalResultType specName = nme.apply.specializedFunction(retType, paramTypes) defn.isSpecializableFunction(cls, paramTypes, retType) @@ -54,12 +54,12 @@ class SpecializeFunctions extends MiniPhase { DefDef(specializedApply.asTerm, vparamss => { ddef.rhs .changeOwner(ddef.symbol, specializedApply) - .subst(ddef.vparamss.head.map(_.symbol), vparamss.head.map(_.symbol)) + .subst(ddef.termParamss.head.map(_.symbol), vparamss.head.map(_.symbol)) }) // create a forwarding to the specialized apply - val args = ddef.vparamss.head.map(vparam => ref(vparam.symbol)) - val rhs = This(cls).select(specializedApply).appliedToArgs(args) + val args = ddef.termParamss.head.map(vparam => ref(vparam.symbol)) + val rhs = This(cls).select(specializedApply).appliedToTermArgs(args) val ddef1 = cpy.DefDef(ddef)(rhs = rhs) Thicket(ddef1, specializedDecl) } @@ -92,7 +92,7 @@ class SpecializeFunctions extends MiniPhase { } } - newSel.appliedToArgs(args) + newSel.appliedToTermArgs(args) case _ => tree } diff --git a/compiler/src/dotty/tools/dotc/transform/Splicer.scala b/compiler/src/dotty/tools/dotc/transform/Splicer.scala index fdf704dc63d7..66419c8c2006 100644 --- a/compiler/src/dotty/tools/dotc/transform/Splicer.scala +++ b/compiler/src/dotty/tools/dotc/transform/Splicer.scala @@ -175,7 +175,7 @@ object Splicer { } def checkIfValidStaticCall(tree: Tree)(using Env): Unit = tree match { - case closureDef(ddef @ DefDef(_, Nil, (ev :: Nil) :: Nil, _, _)) if ddef.symbol.info.isContextualMethod => + case closureDef(ddef @ DefDef(_, ValDefs(ev :: Nil) :: Nil, _, _)) if ddef.symbol.info.isContextualMethod => checkIfValidStaticCall(ddef.rhs)(using summon[Env] + ev.symbol) case Block(stats, expr) => @@ -265,7 +265,7 @@ object Splicer { else unexpectedTree(tree) - case closureDef((ddef @ DefDef(_, _, (arg :: Nil) :: Nil, _, _))) => + case closureDef((ddef @ DefDef(_, ValDefs(arg :: Nil) :: Nil, _, _))) => (obj: AnyRef) => interpretTree(ddef.rhs)(using env.updated(arg.symbol, obj)) // Interpret `foo(j = x, i = y)` which it is expanded to diff --git a/compiler/src/dotty/tools/dotc/transform/SyntheticMembers.scala b/compiler/src/dotty/tools/dotc/transform/SyntheticMembers.scala index 22841a5cf1a0..3302e5faa412 100644 --- a/compiler/src/dotty/tools/dotc/transform/SyntheticMembers.scala +++ b/compiler/src/dotty/tools/dotc/transform/SyntheticMembers.scala @@ -117,7 +117,7 @@ class SyntheticMembers(thisPhase: DenotTransformer) { coord = clazz.coord).enteredAfter(thisPhase).asTerm def forwardToRuntime(vrefs: List[Tree]): Tree = - ref(defn.runtimeMethodRef("_" + sym.name.toString)).appliedToArgs(This(clazz) :: vrefs) + ref(defn.runtimeMethodRef("_" + sym.name.toString)).appliedToTermArgs(This(clazz) :: vrefs) def ownName: Tree = Literal(Constant(clazz.name.stripModuleClassSuffix.toString)) diff --git a/compiler/src/dotty/tools/dotc/transform/TailRec.scala b/compiler/src/dotty/tools/dotc/transform/TailRec.scala index 10841bf552c9..9330016f3292 100644 --- a/compiler/src/dotty/tools/dotc/transform/TailRec.scala +++ b/compiler/src/dotty/tools/dotc/transform/TailRec.scala @@ -140,7 +140,7 @@ class TailRec extends MiniPhase { // than first one will collect info about which transformations and rewritings should be applied // and second one will actually apply, // now this speculatively transforms tree and throws away result in many cases - val transformer = new TailRecElimination(method, enclosingClass, tree.vparamss.head.map(_.symbol), mandatory) + val transformer = new TailRecElimination(method, enclosingClass, tree.termParamss.head.map(_.symbol), mandatory) val rhsSemiTransformed = transformer.transform(tree.rhs) if (transformer.rewrote) { diff --git a/compiler/src/dotty/tools/dotc/transform/TreeChecker.scala b/compiler/src/dotty/tools/dotc/transform/TreeChecker.scala index 68d15b82e362..7eaad36b18a2 100644 --- a/compiler/src/dotty/tools/dotc/transform/TreeChecker.scala +++ b/compiler/src/dotty/tools/dotc/transform/TreeChecker.scala @@ -442,7 +442,7 @@ class TreeChecker extends Phase with SymTransformer { val decls = cls.classInfo.decls.toList.toSet.filter(isNonMagicalMember) val defined = impl.body.map(_.symbol) - + def isAllowed(sym: Symbol): Boolean = sym.is(ConstructorProxy) && !ctx.phase.erasedTypes @@ -457,30 +457,27 @@ class TreeChecker extends Phase with SymTransformer { } override def typedDefDef(ddef: untpd.DefDef, sym: Symbol)(using Context): Tree = - def defParamss = - (ddef.tparams :: ddef.vparamss).filter(!_.isEmpty).map(_.map(_.symbol)) + def defParamss = ddef.paramss.filter(!_.isEmpty).nestedMap(_.symbol) def layout(symss: List[List[Symbol]]): String = symss.map(syms => i"($syms%, %)").mkString assert(ctx.erasedTypes || sym.rawParamss == defParamss, i"""param mismatch for ${sym.showLocated}: |defined in tree = ${layout(defParamss)} |stored in symbol = ${layout(sym.rawParamss)}""") - withDefinedSyms(ddef.tparams) { - withDefinedSyms(ddef.vparamss.flatten) { - if (!sym.isClassConstructor && !(sym.name eq nme.STATIC_CONSTRUCTOR)) - assert(isValidJVMMethodName(sym.name.encode), s"${sym.name.debugString} name is invalid on jvm") - - ddef.vparamss.foreach(_.foreach { vparam => - assert(vparam.symbol.is(Param), - s"Parameter ${vparam.symbol} of ${sym.fullName} does not have flag `Param` set") - assert(!vparam.symbol.isOneOf(AccessFlags), - s"Parameter ${vparam.symbol} of ${sym.fullName} has invalid flag(s): ${(vparam.symbol.flags & AccessFlags).flagsString}") - }) - - val tpdTree = super.typedDefDef(ddef, sym) - assert(isMethodType(sym.info), i"wrong type, expect a method type for ${sym.fullName}, but found: ${sym.info}") - tpdTree - } + withDefinedSyms(ddef.paramss.flatten) { + if (!sym.isClassConstructor && !(sym.name eq nme.STATIC_CONSTRUCTOR)) + assert(isValidJVMMethodName(sym.name.encode), s"${sym.name.debugString} name is invalid on jvm") + + ddef.termParamss.foreach(_.foreach { vparam => + assert(vparam.symbol.is(Param), + s"Parameter ${vparam.symbol} of ${sym.fullName} does not have flag `Param` set") + assert(!vparam.symbol.isOneOf(AccessFlags), + s"Parameter ${vparam.symbol} of ${sym.fullName} has invalid flag(s): ${(vparam.symbol.flags & AccessFlags).flagsString}") + }) + + val tpdTree = super.typedDefDef(ddef, sym) + assert(isMethodType(sym.info), i"wrong type, expect a method type for ${sym.fullName}, but found: ${sym.info}") + tpdTree } override def typedCase(tree: untpd.CaseDef, sel: Tree, selType: Type, pt: Type)(using Context): CaseDef = diff --git a/compiler/src/dotty/tools/dotc/transform/TupleOptimizations.scala b/compiler/src/dotty/tools/dotc/transform/TupleOptimizations.scala index a5ea7c19e45c..d208c19c5c52 100644 --- a/compiler/src/dotty/tools/dotc/transform/TupleOptimizations.scala +++ b/compiler/src/dotty/tools/dotc/transform/TupleOptimizations.scala @@ -49,7 +49,7 @@ class TupleOptimizations extends MiniPhase with IdentityDenotTransformer { else { // val it = Iterator.single(head) ++ tail.asInstanceOf[Product].productIterator // TupleN+1(it.next(), ..., it.next()) - val fullIterator = ref(defn.RuntimeTuples_consIterator).appliedToArgs(head :: tail :: Nil) + val fullIterator = ref(defn.RuntimeTuples_consIterator).appliedToTermArgs(head :: tail :: Nil) evalOnce(fullIterator) { it => knownTupleFromIterator(tpes.length, it).asInstance(tree.tpe) } @@ -127,7 +127,7 @@ class TupleOptimizations extends MiniPhase with IdentityDenotTransformer { else { // val it = self.asInstanceOf[Product].productIterator ++ that.asInstanceOf[Product].productIterator // TupleN+M(it.next(), ..., it.next()) - val fullIterator = ref(defn.RuntimeTuples_concatIterator).appliedToArgs(tree.args) + val fullIterator = ref(defn.RuntimeTuples_concatIterator).appliedToTermArgs(tree.args) evalOnce(fullIterator) { it => knownTupleFromIterator(n + m, it).asInstance(tree.tpe) } @@ -192,7 +192,7 @@ class TupleOptimizations extends MiniPhase with IdentityDenotTransformer { val size = elements.size assert(0 < size && size <= MaxTupleArity) val tupleModule = defn.TupleType(size).classSymbol.companionModule - ref(tupleModule).select(nme.apply).appliedToTypes(tpes).appliedToArgs(elements) + ref(tupleModule).select(nme.apply).appliedToTypes(tpes).appliedToTermArgs(elements) } private def knownTupleFromIterator(size: Int, it: Tree)(using Context): Tree = diff --git a/compiler/src/dotty/tools/dotc/transform/UncacheGivenAliases.scala b/compiler/src/dotty/tools/dotc/transform/UncacheGivenAliases.scala index e5d5bf4b3931..958fce04da77 100644 --- a/compiler/src/dotty/tools/dotc/transform/UncacheGivenAliases.scala +++ b/compiler/src/dotty/tools/dotc/transform/UncacheGivenAliases.scala @@ -59,7 +59,7 @@ class UncacheGivenAliases extends MiniPhase with IdentityDenotTransformer: initFlags = sym.flags &~ Lazy | Method, info = ExprType(sym.info)) .installAfter(thisPhase) - cpy.DefDef(tree)(tree.name, Nil, Nil, tree.tpt, tree.rhs) + cpy.DefDef(tree)(tree.name, Nil, tree.tpt, tree.rhs) else tree end UncacheGivenAliases diff --git a/compiler/src/dotty/tools/dotc/transform/init/Checking.scala b/compiler/src/dotty/tools/dotc/transform/init/Checking.scala index 90741d86124f..6a8007e06516 100644 --- a/compiler/src/dotty/tools/dotc/transform/init/Checking.scala +++ b/compiler/src/dotty/tools/dotc/transform/init/Checking.scala @@ -130,16 +130,13 @@ object Checking { tpl.parents.foreach { case tree @ Block(_, parent) => - val (ctor, _, _) = decomposeCall(parent) - checkConstructor(ctor.symbol, parent.tpe, tree) + checkConstructor(funPart(parent).symbol, parent.tpe, tree) case tree @ Apply(Block(_, parent), _) => - val (ctor, _, _) = decomposeCall(parent) - checkConstructor(ctor.symbol, tree.tpe, tree) + checkConstructor(funPart(parent).symbol, tree.tpe, tree) case parent : Apply => - val (ctor, _, argss) = decomposeCall(parent) - checkConstructor(ctor.symbol, parent.tpe, parent) + checkConstructor(funPart(parent).symbol, parent.tpe, parent) case ref => val cls = ref.tpe.classSymbol.asClass diff --git a/compiler/src/dotty/tools/dotc/transform/init/Potentials.scala b/compiler/src/dotty/tools/dotc/transform/init/Potentials.scala index 282e5344f7a3..b6ee6fb9dfca 100644 --- a/compiler/src/dotty/tools/dotc/transform/init/Potentials.scala +++ b/compiler/src/dotty/tools/dotc/transform/init/Potentials.scala @@ -1,4 +1,5 @@ -package dotty.tools.dotc +package dotty.tools +package dotc package transform package init @@ -101,7 +102,7 @@ object Potentials { case Some((parentCls, pots)) => val rebased: Potentials = outerPots.flatMap { Potentials.asSeenFrom(pots, _) } resolveOuter(parentCls, rebased, cls) - case None => ??? // impossible + case None => unreachable() } } } diff --git a/compiler/src/dotty/tools/dotc/transform/init/Summarization.scala b/compiler/src/dotty/tools/dotc/transform/init/Summarization.scala index 82293c63452e..258900311d3e 100644 --- a/compiler/src/dotty/tools/dotc/transform/init/Summarization.scala +++ b/compiler/src/dotty/tools/dotc/transform/init/Summarization.scala @@ -292,16 +292,16 @@ object Summarization { val effsAll = tpl.parents.foldLeft(effs) { (effs, parent) => effs ++ (parent match { case tree @ Block(stats, parent) => - val (ctor @ Select(qual, _), _, argss) = decomposeCall(parent) - parentArgEffsWithInit(qual :: stats ++ argss.flatten, ctor.symbol, tree) + val ctor @ Select(qual, _) = funPart(parent) + parentArgEffsWithInit(qual :: stats ++ termArgss(parent).flatten, ctor.symbol, tree) case tree @ Apply(Block(stats, parent), args) => - val (ctor @ Select(qual, _), _, argss) = decomposeCall(parent) - parentArgEffsWithInit(qual :: stats ++ args ++ argss.flatten, ctor.symbol, tree) + val ctor @ Select(qual, _) = funPart(parent) + parentArgEffsWithInit(qual :: stats ++ args ++ termArgss(parent).flatten, ctor.symbol, tree) case parent : Apply => - val (ctor @ Select(qual, _), _, argss) = decomposeCall(parent) - parentArgEffsWithInit(qual :: argss.flatten, ctor.symbol, parent) + val ctor @ Select(qual, _) = funPart(parent) + parentArgEffsWithInit(qual :: termArgss(parent).flatten, ctor.symbol, parent) case ref => val tref: TypeRef = ref.tpe.typeConstructor.asInstanceOf diff --git a/compiler/src/dotty/tools/dotc/transform/localopt/StringInterpolatorOpt.scala b/compiler/src/dotty/tools/dotc/transform/localopt/StringInterpolatorOpt.scala index e0a565737fff..a1c1708302f2 100644 --- a/compiler/src/dotty/tools/dotc/transform/localopt/StringInterpolatorOpt.scala +++ b/compiler/src/dotty/tools/dotc/transform/localopt/StringInterpolatorOpt.scala @@ -152,13 +152,13 @@ class StringInterpolatorOpt extends MiniPhase { val stringToString = defn.StringContextModule_processEscapes.info.asInstanceOf[MethodType] val process = tpd.Lambda(stringToString, args => - if (isRaw) args.head else ref(defn.StringContextModule_processEscapes).appliedToArgs(args)) + if (isRaw) args.head else ref(defn.StringContextModule_processEscapes).appliedToTermArgs(args)) evalOnce(pre) { sc => val parts = sc.select(defn.StringContext_parts) ref(defn.StringContextModule_standardInterpolator) - .appliedToArgs(List(process, args, parts)) + .appliedToTermArgs(List(process, args, parts)) } } else diff --git a/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala b/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala index a231dfe8d955..9d11ffb5a961 100644 --- a/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala +++ b/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala @@ -388,7 +388,7 @@ class SpaceEngine(using Context) extends SpaceLogic { projectSeq(pats) case UnApply(fun, _, pats) => - val (fun1, _, _) = decomposeCall(fun) + val fun1 = funPart(fun) val funRef = fun1.tpe.asInstanceOf[TermRef] if (fun.symbol.name == nme.unapplySeq) if (fun.symbol.owner == scalaSeqFactoryClass) diff --git a/compiler/src/dotty/tools/dotc/transform/sjs/PrepJSExports.scala b/compiler/src/dotty/tools/dotc/transform/sjs/PrepJSExports.scala index fa2eca8c211d..8fe0083fb61f 100644 --- a/compiler/src/dotty/tools/dotc/transform/sjs/PrepJSExports.scala +++ b/compiler/src/dotty/tools/dotc/transform/sjs/PrepJSExports.scala @@ -439,8 +439,8 @@ object PrepJSExports { private def genProxyDefDef(clsSym: ClassSymbol, trgSym: Symbol, proxySym: TermSymbol, span: Span)(using Context): Tree = { - polyDefDef(proxySym, { targs => argss => - This(clsSym).select(trgSym).appliedToTypes(targs).appliedToArgss(argss) + DefDef(proxySym, { argss => + This(clsSym).select(trgSym).appliedToArgss(argss) }).withSpan(span) } diff --git a/compiler/src/dotty/tools/dotc/typer/Applications.scala b/compiler/src/dotty/tools/dotc/typer/Applications.scala index a388b7d55e22..733a54705da1 100644 --- a/compiler/src/dotty/tools/dotc/typer/Applications.scala +++ b/compiler/src/dotty/tools/dotc/typer/Applications.scala @@ -196,12 +196,8 @@ object Applications { def wrapDefs(defs: mutable.ListBuffer[Tree], tree: Tree)(using Context): Tree = if (defs != null && defs.nonEmpty) tpd.Block(defs.toList, tree) else tree - /** A wrapper indicating that its `app` argument has already integrated the type arguments - * of the expected type, provided that type is a (possibly ignored) PolyProto. - * I.e., if the expected type is a PolyProto, then `app` will be a `TypeApply(_, args)` where - * `args` are the type arguments of the expected type. - */ - class IntegratedTypeArgs(val app: Tree)(implicit @constructorOnly src: SourceFile) extends ProxyTree { + abstract class AppProxy(implicit @constructorOnly src: SourceFile) extends ProxyTree { + def app: Tree override def span = app.span def forwardTo = app @@ -210,27 +206,12 @@ object Applications { def productElement(n: Int): Any = app.productElement(n) } - /** The unapply method of this extractor also recognizes IntegratedTypeArgs in closure blocks. - * This is necessary to deal with closures as left arguments of extension method applications. - * A test case is i5606.scala - */ - object IntegratedTypeArgs { - def apply(app: Tree)(using Context) = new IntegratedTypeArgs(app) - def unapply(tree: Tree)(using Context): Option[Tree] = tree match { - case tree: IntegratedTypeArgs => Some(tree.app) - case Block(stats, IntegratedTypeArgs(app)) => Some(tpd.cpy.Block(tree)(stats, app)) - case _ => None - } - } - /** A wrapper indicating that its argument is an application of an extension method. */ - class ExtMethodApply(app: Tree)(implicit @constructorOnly src: SourceFile) - extends IntegratedTypeArgs(app) { - overwriteType(WildcardType) + class ExtMethodApply(val app: Tree)(implicit @constructorOnly src: SourceFile) extends AppProxy: + overwriteType(app.tpe) // ExtMethodApply always has wildcard type in order not to prompt any further adaptations // such as eta expansion before the method is fully applied. - } } trait Applications extends Compatibility { @@ -434,7 +415,7 @@ trait Applications extends Compatibility { /** Splice new method reference into existing application */ def spliceMeth(meth: Tree, app: Tree): Tree = app match { case Apply(fn, args) => - spliceMeth(meth, fn).appliedToArgs(args) + spliceMeth(meth, fn).appliedToTermArgs(args) case TypeApply(fn, targs) => // Note: It is important that the type arguments `targs` are passed in new trees // instead of being spliced in literally. Otherwise, a type argument to a default @@ -1060,8 +1041,6 @@ trait Applications extends Compatibility { val typedArgs = if (isNamed) typedNamedArgs(tree.args) else tree.args.mapconserve(typedType(_)) record("typedTypeApply") typedExpr(tree.fun, PolyProto(typedArgs, pt)) match { - case IntegratedTypeArgs(app) => - app case _: TypeApply if !ctx.isAfterTyper => errorTree(tree, "illegal repeated type application") case typedFn => @@ -1095,7 +1074,7 @@ trait Applications extends Compatibility { def newGenericArrayCall = ref(defn.DottyArraysModule) .select(defn.newGenericArrayMethod).withSpan(tree.span) - .appliedToTypeTrees(targs).appliedToArgs(args) + .appliedToTypeTrees(targs).appliedToTermArgs(args) if (TypeErasure.isGeneric(targ.tpe)) newGenericArrayCall @@ -1384,7 +1363,10 @@ trait Applications extends Compatibility { */ def hasExtensionMethodNamed(tp: Type, xname: TermName, argType: Type, resultType: Type)(using Context) = { def qualifies(mbr: Denotation) = - mbr.exists && isApplicableType(tp.select(xname, mbr), argType :: Nil, resultType) + mbr.exists + && isApplicableType( + normalize(tp.select(xname, mbr), WildcardType), + argType :: Nil, resultType) tp.memberBasedOnFlags(xname, required = ExtensionMethod) match { case mbr: SingleDenotation => qualifies(mbr) case mbr => mbr.hasAltWith(qualifies(_)) @@ -1950,7 +1932,7 @@ trait Applications extends Compatibility { else val deepPt = pt.deepenProto deepPt match - case pt @ FunProto(_, resType: FunProto) => + case pt @ FunProto(_, resType: FunOrPolyProto) => // try to narrow further with snd argument list resolveMapped(candidates, skipParamClause(pt.typedArgs().tpes), resType) case _ => @@ -2146,9 +2128,6 @@ trait Applications extends Compatibility { // Always hide expected member to allow for chained extensions (needed for i6900.scala) case _: SelectionProto => (tree, IgnoredProto(currentPt)) - case PolyProto(targs, restpe) => - val tree1 = untpd.TypeApply(tree, targs.map(untpd.TypedSplice(_))) - normalizePt(tree1, restpe) case _ => (tree, currentPt) diff --git a/compiler/src/dotty/tools/dotc/typer/Checking.scala b/compiler/src/dotty/tools/dotc/typer/Checking.scala index 7bd92e991354..e2f160142e6d 100644 --- a/compiler/src/dotty/tools/dotc/typer/Checking.scala +++ b/compiler/src/dotty/tools/dotc/typer/Checking.scala @@ -1195,9 +1195,8 @@ trait Checking { if (stat.symbol.is(Case)) stat match { - case TypeDef(_, Template(DefDef(_, tparams, vparamss, _, _), parents, _, _)) => - tparams.foreach(check) - vparamss.foreach(_.foreach(check)) + case TypeDef(_, Template(DefDef(_, paramss, _, _), parents, _, _)) => + paramss.foreach(_.foreach(check)) parents.foreach(check) case vdef: ValDef => vdef.rhs match { @@ -1212,7 +1211,7 @@ trait Checking { stat match { case TypeDef(_, impl: Template) => for ((defaultGetter @ - DefDef(DefaultGetterName(nme.CONSTRUCTOR, _), _, _, _, _)) <- impl.body) + DefDef(DefaultGetterName(nme.CONSTRUCTOR, _), _, _, _)) <- impl.body) check(defaultGetter.rhs) case _ => } diff --git a/compiler/src/dotty/tools/dotc/typer/Deriving.scala b/compiler/src/dotty/tools/dotc/typer/Deriving.scala index a964e0326978..1218d2c10094 100644 --- a/compiler/src/dotty/tools/dotc/typer/Deriving.scala +++ b/compiler/src/dotty/tools/dotc/typer/Deriving.scala @@ -271,15 +271,17 @@ trait Deriving { import tpd._ /** The type class instance definition with symbol `sym` */ - def typeclassInstance(sym: Symbol)(using Context): List[Type] => (List[List[tpd.Tree]] => tpd.Tree) = { - (tparamRefs: List[Type]) => (paramRefss: List[List[tpd.Tree]]) => - val tparams = tparamRefs.map(_.typeSymbol.asType) - val params = if (paramRefss.isEmpty) Nil else paramRefss.head.map(_.symbol.asTerm) + def typeclassInstance(sym: Symbol)(using Context): List[List[tpd.Tree]] => tpd.Tree = + (paramRefss: List[List[tpd.Tree]]) => + val (tparamRefs, vparamRefss) = splitArgs(paramRefss) + val tparamTypes = tparamRefs.tpes + val tparams = tparamTypes.map(_.typeSymbol.asType) + val vparams = if (vparamRefss.isEmpty) Nil else vparamRefss.head.map(_.symbol.asTerm) tparams.foreach(ctx.enter(_)) - params.foreach(ctx.enter(_)) + vparams.foreach(ctx.enter(_)) def instantiated(info: Type): Type = info match { - case info: PolyType => instantiated(info.instantiate(tparamRefs)) - case info: MethodType => info.instantiate(params.map(_.termRef)) + case info: PolyType => instantiated(info.instantiate(tparamTypes)) + case info: MethodType => info.instantiate(vparams.map(_.termRef)) case info => info.widenExpr } def companionRef(tp: Type): TermRef = tp match { @@ -292,11 +294,11 @@ trait Deriving { val module = untpd.ref(companionRef(resultType)).withSpan(sym.span) val rhs = untpd.Select(module, nme.derived) typed(rhs, resultType) - } + end typeclassInstance def syntheticDef(sym: Symbol): Tree = inContext(ctx.fresh.setOwner(sym).setNewScope) { - if sym.is(Method) then tpd.polyDefDef(sym.asTerm, typeclassInstance(sym)) - else tpd.ValDef(sym.asTerm, typeclassInstance(sym)(Nil)(Nil)) + if sym.is(Method) then tpd.DefDef(sym.asTerm, typeclassInstance(sym)) + else tpd.ValDef(sym.asTerm, typeclassInstance(sym)(Nil)) } synthetics.map(syntheticDef).toList diff --git a/compiler/src/dotty/tools/dotc/typer/Dynamic.scala b/compiler/src/dotty/tools/dotc/typer/Dynamic.scala index 9ee5b43f9c93..355fcf4b42dc 100644 --- a/compiler/src/dotty/tools/dotc/typer/Dynamic.scala +++ b/compiler/src/dotty/tools/dotc/typer/Dynamic.scala @@ -160,10 +160,11 @@ trait Dynamic { * where c11, ..., cNn are the classOf constants representing the erasures of T11, ..., TNn. * * It's an error if U is neither a value nor a method type, or a dependent method - * type. + * type */ def handleStructural(tree: Tree)(using Context): Tree = { - val (fun @ Select(qual, name), targs, vargss) = decomposeCall(tree) + val fun @ Select(qual, name) = funPart(tree) + val vargss = termArgss(tree) def structuralCall(selectorName: TermName, classOfs: => List[Tree]) = { val selectable = adapt(qual, defn.SelectableClass.typeRef) diff --git a/compiler/src/dotty/tools/dotc/typer/ErrorReporting.scala b/compiler/src/dotty/tools/dotc/typer/ErrorReporting.scala index 3d50e4b3e519..be512af9a7c6 100644 --- a/compiler/src/dotty/tools/dotc/typer/ErrorReporting.scala +++ b/compiler/src/dotty/tools/dotc/typer/ErrorReporting.scala @@ -309,7 +309,7 @@ class ImplicitSearchError( // implicitly[Int => String] // found: x => f[Any](x) val call = tpd.closureBody(alt.tree) // the tree itself if not a closure - val (_, targs, _) = tpd.decomposeCall(call) + val targs = tpd.typeArgss(call).flatten val args = resolveTypes(targs)(using ctx.fresh.setTyperState(alt.tstate)) userDefinedErrorString(raw, params, args) } @@ -336,7 +336,8 @@ class ImplicitSearchError( */ private def userDefinedImplicitNotFoundParamMessage: Option[String] = paramSymWithMethodCallTree.flatMap { (sym, applTree) => userDefinedMsg(sym, defn.ImplicitNotFoundAnnot).map { rawMsg => - val (fn, targs, _) = tpd.decomposeCall(applTree) + val fn = tpd.funPart(applTree) + val targs = tpd.typeArgss(applTree).flatten val methodOwner = fn.symbol.owner val methodOwnerType = tpd.qualifier(fn).tpe val methodTypeParams = fn.symbol.paramSymss.flatten.filter(_.isType) diff --git a/compiler/src/dotty/tools/dotc/typer/Implicits.scala b/compiler/src/dotty/tools/dotc/typer/Implicits.scala index 58d009708e36..761d550afc96 100644 --- a/compiler/src/dotty/tools/dotc/typer/Implicits.scala +++ b/compiler/src/dotty/tools/dotc/typer/Implicits.scala @@ -937,7 +937,7 @@ trait Implicits: case Select(qual, _) => apply(x, qual) case Apply(fn, _) => apply(x, fn) case TypeApply(fn, _) => apply(x, fn) - case tree: Applications.IntegratedTypeArgs => apply(x, tree.app) + case tree: Applications.AppProxy => apply(x, tree.app) case _: This => false case _ => foldOver(x, tree) } diff --git a/compiler/src/dotty/tools/dotc/typer/Inferencing.scala b/compiler/src/dotty/tools/dotc/typer/Inferencing.scala index ef49574a46e7..d374ca7678e3 100644 --- a/compiler/src/dotty/tools/dotc/typer/Inferencing.scala +++ b/compiler/src/dotty/tools/dotc/typer/Inferencing.scala @@ -61,16 +61,15 @@ object Inferencing { ).process(tp) /** Instantiate any type variables in `tp` whose bounds contain a reference to - * one of the parameters in `tparams` or `vparamss`. + * one of the parameters in `paramss`. */ - def instantiateDependent(tp: Type, tparams: List[Symbol], vparamss: List[List[Symbol]])(using Context): Unit = { + def instantiateDependent(tp: Type, paramss: List[List[Symbol]])(using Context): Unit = { val dependentVars = new TypeAccumulator[Set[TypeVar]] { - @threadUnsafe lazy val params = (tparams :: vparamss).flatten def apply(tvars: Set[TypeVar], tp: Type) = tp match { case tp: TypeVar if !tp.isInstantiated && TypeComparer.bounds(tp.origin) - .namedPartsWith(ref => params.contains(ref.symbol)) + .namedPartsWith(ref => paramss.exists(_.contains(ref.symbol))) .nonEmpty => tvars + tp case _ => diff --git a/compiler/src/dotty/tools/dotc/typer/Inliner.scala b/compiler/src/dotty/tools/dotc/typer/Inliner.scala index c6c42ebe1b9f..db6165ed5f2f 100644 --- a/compiler/src/dotty/tools/dotc/typer/Inliner.scala +++ b/compiler/src/dotty/tools/dotc/typer/Inliner.scala @@ -200,10 +200,9 @@ object Inliner { flags = meth.flags &~ (Inline | Macro | Override) | Private, coord = mdef.rhs.span.startPos).asTerm retainer.deriveTargetNameAnnotation(meth, name => BodyRetainerName(name.asTermName)) - polyDefDef(retainer, targs => prefss => + DefDef(retainer, prefss => inlineCall( - ref(meth).appliedToTypes(targs).appliedToArgss(prefss) - .withSpan(mdef.rhs.span.startPos))( + ref(meth).appliedToArgss(prefss).withSpan(mdef.rhs.span.startPos))( using ctx.withOwner(retainer))) .showing(i"retainer for $meth: $result", inlining) @@ -365,7 +364,9 @@ class Inliner(call: tpd.Tree, rhsToInline: tpd.Tree)(using Context) { import tpd._ import Inliner._ - private val (methPart, callTypeArgs, callValueArgss) = decomposeCall(call) + private val methPart = funPart(call) + private val callTypeArgs = typeArgss(call).flatten + private val callValueArgss = termArgss(call) private val inlinedMethod = methPart.symbol private val inlineCallPrefix = qualifier(methPart).orElse(This(inlinedMethod.enclosingClass.asClass)) @@ -457,7 +458,7 @@ class Inliner(call: tpd.Tree, rhsToInline: tpd.Tree)(using Context) { paramSpan(name) = arg.span paramBinding(name) = arg.tpe.stripTypeVar } - computeParamBindings(tp.resultType, Nil, argss) + computeParamBindings(tp.resultType, targs.drop(tp.paramNames.length), argss) case tp: MethodType => if argss.isEmpty then report.error(i"missing arguments for inline method $inlinedMethod", call.srcPos) @@ -523,7 +524,7 @@ class Inliner(call: tpd.Tree, rhsToInline: tpd.Tree)(using Context) { case EmptyTree | TypeDef(_, _) | Import(_, _) - | DefDef(_, _, _, _, _) => + | DefDef(_, _, _, _) => true case vdef @ ValDef(_, _, _) => if (vdef.symbol.flags is Mutable) false else apply(vdef.rhs) @@ -961,7 +962,7 @@ class Inliner(call: tpd.Tree, rhsToInline: tpd.Tree)(using Context) { def betaReduce(tree: Tree)(using Context): Tree = tree match { case Apply(Select(cl @ closureDef(ddef), nme.apply), args) if defn.isFunctionType(cl.tpe) => ddef.tpe.widen match { - case mt: MethodType if ddef.vparamss.head.length == args.length => + case mt: MethodType if ddef.paramss.head.length == args.length => val bindingsBuf = new mutable.ListBuffer[ValOrDefDef] val argSyms = mt.paramNames.lazyZip(mt.paramInfos).lazyZip(args).map { (name, paramtp, arg) => arg.tpe.dealias match { @@ -975,7 +976,7 @@ class Inliner(call: tpd.Tree, rhsToInline: tpd.Tree)(using Context) { val expander = new TreeTypeMap( oldOwners = ddef.symbol :: Nil, newOwners = ctx.owner :: Nil, - substFrom = ddef.vparamss.head.map(_.symbol), + substFrom = ddef.paramss.head.map(_.symbol), substTo = argSyms) Block(bindingsBuf.toList, expander.transform(ddef.rhs)) case _ => tree @@ -1367,7 +1368,7 @@ class Inliner(call: tpd.Tree, rhsToInline: tpd.Tree)(using Context) { val bindingOfSym = MutableSymbolMap[MemberDef]() def isInlineable(binding: MemberDef) = binding match { - case ddef @ DefDef(_, Nil, Nil, _, _) => isElideableExpr(ddef.rhs) + case ddef @ DefDef(_, Nil, _, _) => isElideableExpr(ddef.rhs) case vdef @ ValDef(_, _, _) => isElideableExpr(vdef.rhs) case _ => false } diff --git a/compiler/src/dotty/tools/dotc/typer/Namer.scala b/compiler/src/dotty/tools/dotc/typer/Namer.scala index 691dc1f1ad6c..0969fe29009e 100644 --- a/compiler/src/dotty/tools/dotc/typer/Namer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Namer.scala @@ -696,7 +696,7 @@ class Namer { typer: Typer => protected def typeSig(sym: Symbol): Type = original match { case original: ValDef => if (sym.is(Module)) moduleValSig(sym) - else valOrDefDefSig(original, sym, Nil, Nil, identity)(using localContext(sym).setNewScope) + else valOrDefDefSig(original, sym, Nil, identity)(using localContext(sym).setNewScope) case original: DefDef => val typer1 = ctx.typer.newLikeThis nestedTyper(sym) = typer1 @@ -1011,21 +1011,23 @@ class Namer { typer: Typer => * provided `mbr` is accessible and of the right implicit/non-implicit kind. */ def addForwarder(alias: TermName, mbr: SingleDenotation, span: Span): Unit = + def adaptForwarderParams(acc: List[List[tpd.Tree]], tp: Type, prefss: List[List[tpd.Tree]]) : List[List[tpd.Tree]] = tp match - case mt: MethodType => + case mt: MethodType + if mt.paramInfos.nonEmpty && mt.paramInfos.last.isRepeatedParam => // Note: in this branch we use the assumptions // that `prefss.head` corresponds to `mt.paramInfos` and // that `prefss.tail` corresponds to `mt.resType` - if mt.paramInfos.nonEmpty && mt.paramInfos.last.isRepeatedParam then - val init :+ vararg = prefss.head - val prefs = init :+ ctx.typeAssigner.seqToRepeated(vararg) - adaptForwarderParams(prefs :: acc, mt.resType, prefss.tail) - else - adaptForwarderParams(prefss.head :: acc, mt.resType, prefss.tail) + val init :+ vararg = prefss.head + val prefs = init :+ ctx.typeAssigner.seqToRepeated(vararg) + adaptForwarderParams(prefs :: acc, mt.resType, prefss.tail) + case mt: MethodOrPoly => + adaptForwarderParams(prefss.head :: acc, mt.resultType, prefss.tail) case _ => acc.reverse ::: prefss - if (whyNoForwarder(mbr) == "") { + + if whyNoForwarder(mbr) == "" then val sym = mbr.symbol val forwarder = if mbr.isType then @@ -1060,9 +1062,8 @@ class Namer { typer: Typer => else { import tpd._ val ref = path.select(sym.asTerm) - val ddef = tpd.polyDefDef(forwarder.asTerm, targs => prefss => - ref.appliedToTypes(targs) - .appliedToArgss(adaptForwarderParams(Nil, sym.info.stripPoly, prefss)) + val ddef = tpd.DefDef(forwarder.asTerm, prefss => + ref.appliedToArgss(adaptForwarderParams(Nil, sym.info, prefss)) ) if forwarder.isInlineMethod then PrepareInlineable.registerInlineInfo(forwarder, ddef.rhs) @@ -1070,7 +1071,7 @@ class Namer { typer: Typer => } buf += forwarderDef.withSpan(span) - } + end addForwarder def addForwardersNamed(name: TermName, alias: TermName, span: Span): Unit = { val size = buf.size @@ -1329,7 +1330,7 @@ class Namer { typer: Typer => * @param paramFn A wrapping function that produces the type of the * defined symbol, given its final return type */ - def valOrDefDefSig(mdef: ValOrDefDef, sym: Symbol, typeParams: List[Symbol], paramss: List[List[Symbol]], paramFn: Type => Type)(using Context): Type = { + def valOrDefDefSig(mdef: ValOrDefDef, sym: Symbol, paramss: List[List[Symbol]], paramFn: Type => Type)(using Context): Type = { def inferredType = { /** A type for this definition that might be inherited from elsewhere: @@ -1340,35 +1341,37 @@ class Namer { typer: Typer => */ val inherited = if (sym.owner.isTerm) NoType - else { + else // TODO: Look only at member of supertype instead? lazy val schema = paramFn(WildcardType) val site = sym.owner.thisType + sym.owner.info.baseClasses.tail.foldLeft(NoType: Type) { (tp, cls) => - def instantiatedResType(info: Type, tparams: List[Symbol], paramss: List[List[Symbol]]): Type = info match { + def instantiatedResType(info: Type, paramss: List[List[Symbol]]): Type = info match case info: PolyType => - if (info.paramNames.length == typeParams.length) - instantiatedResType(info.instantiate(tparams.map(_.typeRef)), Nil, paramss) - else NoType + paramss match + case TypeSymbols(tparams) :: paramss1 if info.paramNames.length == tparams.length => + instantiatedResType(info.instantiate(tparams.map(_.typeRef)), paramss1) + case _ => + NoType case info: MethodType => - paramss match { - case params :: paramss1 if info.paramNames.length == params.length => - instantiatedResType(info.instantiate(params.map(_.termRef)), tparams, paramss1) + paramss match + case TermSymbols(vparams) :: paramss1 if info.paramNames.length == vparams.length => + instantiatedResType(info.instantiate(vparams.map(_.termRef)), paramss1) case _ => NoType - } case _ => - if (tparams.isEmpty && paramss.isEmpty) info.widenExpr + if paramss.isEmpty then info.widenExpr else NoType - } + val iRawInfo = cls.info.nonPrivateDecl(sym.name).matchingDenotation(site, schema, sym.targetName).info - val iResType = instantiatedResType(iRawInfo, typeParams, paramss).asSeenFrom(site, cls) + val iResType = instantiatedResType(iRawInfo, paramss).asSeenFrom(site, cls) if (iResType.exists) typr.println(i"using inherited type for ${mdef.name}; raw: $iRawInfo, inherited: $iResType") tp & iResType } - } + end inherited /** The proto-type to be used when inferring the result type from * the right hand side. This is `WildcardType` except if the definition @@ -1419,6 +1422,7 @@ class Namer { typer: Typer => var rhsCtx = ctx.fresh.addMode(Mode.InferringReturnType) if sym.isInlineMethod then rhsCtx = rhsCtx.addMode(Mode.InlineableBody) if sym.is(ExtensionMethod) then rhsCtx = rhsCtx.addMode(Mode.InExtensionMethod) + val typeParams = paramss.collect { case TypeSymbols(tparams) => tparams }.flatten if (typeParams.nonEmpty) { // we'll be typing an expression from a polymorphic definition's body, // so we must allow constraining its type parameters @@ -1453,6 +1457,7 @@ class Namer { typer: Typer => lhsType orElse WildcardType } } + lazy val termParamss = paramss.collect { case TermSymbols(vparams) => vparams } val tptProto = mdef.tpt match { case _: untpd.DerivedTypeTree => @@ -1460,7 +1465,7 @@ class Namer { typer: Typer => case TypeTree() => checkMembersOK(inferredType, mdef.srcPos) case DependentTypeTree(tpFun) => - val tpe = tpFun(paramss.head) + val tpe = tpFun(termParamss.head) if (isFullyDefined(tpe, ForceDegree.none)) tpe else typedAheadExpr(mdef.rhs, tpe).tpe case TypedSplice(tpt: TypeTree) if !isFullyDefined(tpt.tpe, ForceDegree.none) => @@ -1479,11 +1484,11 @@ class Namer { typer: Typer => // are better ways to achieve this. It would be good if we could get rid of this code. // It seems at least partially redundant with the nesting level checking on TypeVar // instantiation. - val hygienicType = TypeOps.avoid(rhsType, paramss.flatten) + val hygienicType = TypeOps.avoid(rhsType, termParamss.flatten) if (!hygienicType.isValueType || !(hygienicType <:< tpt.tpe)) report.error(i"return type ${tpt.tpe} of lambda cannot be made hygienic;\n" + i"it is not a supertype of the hygienic type $hygienicType", mdef.srcPos) - //println(i"lifting $rhsType over $paramss -> $hygienicType = ${tpt.tpe}") + //println(i"lifting $rhsType over $termParamss -> $hygienicType = ${tpt.tpe}") //println(TypeComparer.explained { implicit ctx => hygienicType <:< tpt.tpe }) case _ => } @@ -1500,7 +1505,6 @@ class Namer { typer: Typer => /** The type signature of a DefDef with given symbol */ def defDefSig(ddef: DefDef, sym: Symbol)(using Context): Type = { // Beware: ddef.name need not match sym.name if sym was freshened! - val DefDef(_, tparams, vparamss, _, _) = ddef val isConstructor = sym.name == nme.CONSTRUCTOR // The following 3 lines replace what was previously just completeParams(tparams). @@ -1526,23 +1530,22 @@ class Namer { typer: Typer => // 3. Info of CP is computed (to be copied to DP). // 4. CP is completed. // 5. Info of CP is copied to DP and DP is completed. - index(tparams) + index(ddef.leadingTypeParams) if (isConstructor) sym.owner.typeParams.foreach(_.ensureCompleted()) - for (tparam <- tparams) typedAheadExpr(tparam) + for (tparam <- ddef.leadingTypeParams) typedAheadExpr(tparam) - vparamss foreach completeParams - def typeParams = tparams map symbolOfTree - val termParamss = normalizeIfConstructor(vparamss.nestedMap(symbolOfTree), isConstructor) - sym.setParamss(typeParams, termParamss) + ddef.trailingParamss.foreach(completeParams) + val paramSymss = normalizeIfConstructor(ddef.paramss.nestedMap(symbolOfTree), isConstructor) + sym.setParamss(paramSymss) def wrapMethType(restpe: Type): Type = { - instantiateDependent(restpe, typeParams, termParamss) - methodType(tparams map symbolOfTree, termParamss, restpe, isJava = ddef.mods.is(JavaDefined)) + instantiateDependent(restpe, paramSymss) + methodType(paramSymss, restpe, isJava = ddef.mods.is(JavaDefined)) } if (isConstructor) { // set result type tree to unit, but take the current class as result type of the symbol typedAheadType(ddef.tpt, defn.UnitType) - wrapMethType(effectiveResultType(sym, typeParams, NoType)) + wrapMethType(effectiveResultType(sym, paramSymss, NoType)) } - else valOrDefDefSig(ddef, sym, typeParams, termParamss, wrapMethType) + else valOrDefDefSig(ddef, sym, paramSymss, wrapMethType) } } diff --git a/compiler/src/dotty/tools/dotc/typer/PrepareInlineable.scala b/compiler/src/dotty/tools/dotc/typer/PrepareInlineable.scala index 80682af5affa..46bfbbdeeba6 100644 --- a/compiler/src/dotty/tools/dotc/typer/PrepareInlineable.scala +++ b/compiler/src/dotty/tools/dotc/typer/PrepareInlineable.scala @@ -122,9 +122,10 @@ object PrepareInlineable { def preTransform(tree: Tree)(using Context): Tree = tree match { case _: Apply | _: TypeApply | _: RefTree if needsAccessor(tree.symbol) && tree.isTerm && !tree.symbol.isConstructor => - val (refPart, targs, argss) = decomposeCall(tree) + val refPart = funPart(tree) + val argss = allArgss(tree) val qual = qualifier(refPart) - inlining.println(i"adding receiver passing inline accessor for $tree/$refPart -> (${qual.tpe}, $refPart: ${refPart.getClass}, [$targs%, %], ($argss%, %))") + inlining.println(i"adding receiver passing inline accessor for $tree/$refPart -> (${qual.tpe}, $refPart: ${refPart.getClass}, $argss%, %") // Need to dealias in order to cagtch all possible references to abstracted over types in // substitutions @@ -159,10 +160,12 @@ object PrepareInlineable { accessorInfo = abstractQualType(addQualType(dealiasMap(accessedType))), accessed = accessed) - ref(accessor) - .appliedToTypeTrees(localRefs.map(TypeTree(_)) ++ targs) - .appliedToArgss((qual :: Nil) :: argss) - .withSpan(tree.span) + val (leadingTypeArgs, otherArgss) = splitArgs(argss) + val argss1 = joinArgs( + localRefs.map(TypeTree(_)) ++ leadingTypeArgs, // TODO: pass type parameters in two sections? + (qual :: Nil) :: otherArgss + ) + ref(accessor).appliedToArgss(argss1).withSpan(tree.span) // TODO: Handle references to non-public types. // This is quite tricky, as such types can appear anywhere, including as parts @@ -265,7 +268,7 @@ object PrepareInlineable { case Block(List(stat), Literal(Constants.Constant(()))) => checkMacro(stat) case Block(Nil, expr) => checkMacro(expr) case Typed(expr, _) => checkMacro(expr) - case Block(DefDef(nme.ANON_FUN, _, _, _, _) :: Nil, Closure(_, fn, _)) if fn.symbol.info.isImplicitMethod => + case Block(DefDef(nme.ANON_FUN, _, _, _) :: Nil, Closure(_, fn, _)) if fn.symbol.info.isImplicitMethod => // TODO Support this pattern report.error( """Macros using a return type of the form `foo(): X ?=> Y` are not yet supported. diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index 75004a90b7e5..91ebd333c051 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -30,7 +30,7 @@ import TypeComparer.CompareResult import util.Spans._ import util.common._ import util.{Property, SimpleIdentityMap, SrcPos} -import Applications.{ExtMethodApply, IntegratedTypeArgs, productSelectorTypes, wrapDefs} +import Applications.{ExtMethodApply, productSelectorTypes, wrapDefs} import collection.mutable import annotation.tailrec @@ -548,11 +548,8 @@ class Typer extends Namer report.error(StableIdentPattern(tree, pt), tree.srcPos) def typedSelect(tree: untpd.Select, pt: Type, qual: Tree)(using Context): Tree = qual match { - case qual @ IntegratedTypeArgs(app) => - pt.revealIgnored match { - case _: PolyProto => qual // keep the IntegratedTypeArgs to strip at next typedTypeApply - case _ => app - } + case qual: ExtMethodApply => + qual.app case qual => val select = assignType(cpy.Select(tree)(qual, tree.name), qual) val select1 = toNotNullTermRef(select, pt) @@ -1088,14 +1085,14 @@ class Typer extends Namer if funFlags.is(Given) then params.map(_.withAddedFlags(Given)) else params val params2 = params1.map(fixThis.transformSub) - val appDef0 = untpd.DefDef(nme.apply, Nil, List(params2), body, EmptyTree).withSpan(tree.span) + val appDef0 = untpd.DefDef(nme.apply, List(params2), body, EmptyTree).withSpan(tree.span) index(appDef0 :: Nil) val appDef = typed(appDef0).asInstanceOf[DefDef] val mt = appDef.symbol.info.asInstanceOf[MethodType] if (mt.isParamDependent) report.error(i"$mt is an illegal function type because it has inter-parameter dependencies", tree.srcPos) val resTpt = TypeTree(mt.nonDependentResultApprox).withSpan(body.span) - val typeArgs = appDef.vparamss.head.map(_.tpt) :+ resTpt + val typeArgs = appDef.termParamss.head.map(_.tpt) :+ resTpt val tycon = TypeTree(funCls.typeRef) val core = AppliedTypeTree(tycon, typeArgs) RefinedTypeTree(core, List(appDef), ctx.owner.asClass) @@ -2000,34 +1997,36 @@ class Typer extends Namer sym.owner.info.decls.openForMutations.unlink(sym) return EmptyTree } - val DefDef(name, tparams, vparamss, tpt, _) = ddef + val DefDef(name, paramss, tpt, _) = ddef completeAnnotations(ddef, sym) - val tparams1 = tparams.mapconserve(typed(_).asInstanceOf[TypeDef]) - val vparamss1 = vparamss.nestedMapConserve(typed(_).asInstanceOf[ValDef]) - vparamss1.foreach(checkNoForwardDependencies) + val paramss1 = paramss.nestedMapConserve(typed(_)).asInstanceOf[List[ParamClause]] + for case ValDefs(vparams) <- paramss1 do + checkNoForwardDependencies(vparams) if (sym.isOneOf(GivenOrImplicit)) checkImplicitConversionDefOK(sym) val tpt1 = checkSimpleKinded(typedType(tpt)) val rhsCtx = ctx.fresh - if (tparams1.nonEmpty) { + val tparamss = paramss1.collect { + case untpd.TypeDefs(tparams) => tparams + } + if tparamss.nonEmpty then rhsCtx.setFreshGADTBounds - if (!sym.isConstructor) + val tparamSyms = tparamss.flatten.map(_.symbol) + if !sym.isConstructor then // we're typing a polymorphic definition's body, // so we allow constraining all of its type parameters // constructors are an exception as we don't allow constraining type params of classes - rhsCtx.gadt.addToConstraint(tparams1.map(_.symbol)) - else if (!sym.isPrimaryConstructor) { + rhsCtx.gadt.addToConstraint(tparamSyms) + else if !sym.isPrimaryConstructor then // otherwise, for secondary constructors we need a context that "knows" // that their type parameters are aliases of the class type parameters. // See pos/i941.scala - rhsCtx.gadt.addToConstraint(tparams1.map(_.symbol)) - tparams1.lazyZip(sym.owner.typeParams).foreach { (tdef, tparam) => + rhsCtx.gadt.addToConstraint(tparamSyms) + tparamSyms.lazyZip(sym.owner.typeParams).foreach { (psym, tparam) => val tr = tparam.typeRef - rhsCtx.gadt.addBound(tdef.symbol, tr, isUpper = false) - rhsCtx.gadt.addBound(tdef.symbol, tr, isUpper = true) + rhsCtx.gadt.addBound(psym, tr, isUpper = false) + rhsCtx.gadt.addBound(psym, tr, isUpper = true) } - } - } if sym.isInlineMethod then rhsCtx.addMode(Mode.InlineableBody) if sym.is(ExtensionMethod) then rhsCtx.addMode(Mode.InExtensionMethod) @@ -2045,7 +2044,7 @@ class Typer extends Namer if (sym.targetName != sym.name) report.error(em"@targetName annotation may not be used on a constructor", ddef.srcPos) - for (param <- tparams1 ::: vparamss1.flatten) + for params <- paramss1; param <- params do checkRefsLegal(param, sym.owner, (name, sym) => sym.is(TypeParam), "secondary constructor") def checkThisConstrCall(tree: Tree): Unit = tree match { @@ -2065,7 +2064,7 @@ class Typer extends Namer annot.tree.sourcePos ) - val ddef2 = assignType(cpy.DefDef(ddef)(name, tparams1, vparamss1, tpt1, rhs1), sym) + val ddef2 = assignType(cpy.DefDef(ddef)(name, paramss1, tpt1, rhs1), sym) checkSignatureRepeatedParam(sym) ddef2.setDefTree @@ -2385,7 +2384,7 @@ class Typer extends Namer nestedCtx.typerState.commit() if sourceVersion.isAtLeast(`3.1`) then lazy val (prefix, suffix) = res match { - case Block(mdef @ DefDef(_, _, vparams :: Nil, _, _) :: Nil, _: Closure) => + case Block(mdef @ DefDef(_, vparams :: Nil, _, _) :: Nil, _: Closure) => val arity = vparams.length if (arity > 0) ("", "") else ("(() => ", "())") case _ => @@ -2602,18 +2601,16 @@ class Typer extends Namer } /** Interpolate and simplify the type of the given tree. */ - protected def simplify(tree: Tree, pt: Type, locked: TypeVars)(using Context): tree.type = { - if (!tree.denot.isOverloaded && - // for overloaded trees: resolve overloading before simplifying - !tree.isInstanceOf[Applications.IntegratedTypeArgs]) - // don't interpolate in the middle of an extension method application - if (!tree.tpe.widen.isInstanceOf[MethodOrPoly] // wait with simplifying until method is fully applied - || tree.isDef) { // ... unless tree is a definition + protected def simplify(tree: Tree, pt: Type, locked: TypeVars)(using Context): tree.type = + if !tree.denot.isOverloaded // for overloaded trees: resolve overloading before simplifying + && !tree.isInstanceOf[Applications.AppProxy] // don't interpolate in the middle of an extension method application + then + if !tree.tpe.widen.isInstanceOf[MethodOrPoly] // wait with simplifying until method is fully applied + || tree.isDef // ... unless tree is a definition + then interpolateTypeVars(tree, pt, locked) tree.overwriteType(tree.tpe.simplified) - } tree - } protected def makeContextualFunction(tree: untpd.Tree, pt: Type)(using Context): Tree = { val defn.FunctionOf(formals, _, true, _) = pt.dropDependentRefinement @@ -3636,10 +3633,7 @@ class Typer extends Namer if tree.symbol.isAllOf(ApplyProxyFlags) then newExpr else adaptToArgs(wtp, pt) case pt: PolyProto => - tree match { - case _: IntegratedTypeArgs => tree - case _ => tryInsertApplyOrImplicit(tree, pt, locked)(tree) // error will be reported in typedTypeApply - } + tryInsertApplyOrImplicit(tree, pt, locked)(tree) // error will be reported in typedTypeApply case pt: SelectionProto if tree.isInstanceOf[ExtMethodApply] => tree case _ => diff --git a/compiler/src/dotty/tools/dotc/typer/VarianceChecker.scala b/compiler/src/dotty/tools/dotc/typer/VarianceChecker.scala index 9170c8edf8ad..5ece0b3d8128 100644 --- a/compiler/src/dotty/tools/dotc/typer/VarianceChecker.scala +++ b/compiler/src/dotty/tools/dotc/typer/VarianceChecker.scala @@ -208,10 +208,9 @@ class VarianceChecker(using Context) { } case tree: ValDef => checkVariance(sym, tree.srcPos) - case DefDef(_, tparams, vparamss, _, _) => + case DefDef(_, paramss, _, _) => checkVariance(sym, tree.srcPos) - tparams foreach traverse - vparamss foreach (_ foreach traverse) + paramss.foreach(_.foreach(traverse)) case _ => } catch { diff --git a/compiler/src/dotty/tools/package.scala b/compiler/src/dotty/tools/package.scala index 1625e24b388c..e07cb42d1179 100644 --- a/compiler/src/dotty/tools/package.scala +++ b/compiler/src/dotty/tools/package.scala @@ -50,4 +50,7 @@ package object tools { type WrappedResult[T] = resultWrapper.WrappedResult[T] def WrappedResult[T](x: T) = resultWrapper.wrap(x) def result[T](using x: WrappedResult[T]): T = resultWrapper.unwrap(x) + + def unreachable(x: Any = "<< this case was declared unreachable >>"): Nothing = + throw new MatchError(x) } diff --git a/compiler/src/dotty/tools/repl/ReplCompiler.scala b/compiler/src/dotty/tools/repl/ReplCompiler.scala index 3580336830be..96ca56a91c47 100644 --- a/compiler/src/dotty/tools/repl/ReplCompiler.scala +++ b/compiler/src/dotty/tools/repl/ReplCompiler.scala @@ -280,7 +280,7 @@ class ReplCompiler extends Compiler { if (errorsAllowed || !ctx.reporter.hasErrors) unwrapped(unit.tpdTree, src) else - ctx.reporter.removeBufferedMessages.errors[tpd.ValDef] // Workaround #4988 + ctx.reporter.removeBufferedMessages.errors } } } diff --git a/compiler/test/dotty/tools/dotc/CompilationTests.scala b/compiler/test/dotty/tools/dotc/CompilationTests.scala index 41c083d0fd78..749c3b02114c 100644 --- a/compiler/test/dotty/tools/dotc/CompilationTests.scala +++ b/compiler/test/dotty/tools/dotc/CompilationTests.scala @@ -157,7 +157,6 @@ class CompilationTests { compileFile("tests/neg-custom-args/missing-alpha.scala", defaultOptions.and("-Yrequire-targetName", "-Xfatal-warnings")), compileFile("tests/neg-custom-args/wildcards.scala", defaultOptions.and("-source", "3.1", "-deprecation", "-Xfatal-warnings")), compileFile("tests/neg-custom-args/indentRight.scala", defaultOptions.and("-noindent", "-Xfatal-warnings")), - compileFile("tests/neg-custom-args/extmethods-tparams.scala", defaultOptions.and("-deprecation", "-Xfatal-warnings")), compileDir("tests/neg-custom-args/adhoc-extension", defaultOptions.and("-source", "3.1", "-feature", "-Xfatal-warnings")), compileFile("tests/neg/i7575.scala", defaultOptions.withoutLanguageFeatures.and("-language:_")), compileFile("tests/neg-custom-args/kind-projector.scala", defaultOptions.and("-Ykind-projector")), diff --git a/compiler/test/dotty/tools/dotc/parsing/DeSugarTest.scala b/compiler/test/dotty/tools/dotc/parsing/DeSugarTest.scala index b19c35244854..51327e99dd01 100644 --- a/compiler/test/dotty/tools/dotc/parsing/DeSugarTest.scala +++ b/compiler/test/dotty/tools/dotc/parsing/DeSugarTest.scala @@ -59,8 +59,8 @@ class DeSugarTest extends ParserTest { cpy.UnApply(tree1)(transform(fun, Expr), transform(implicits), transform(patterns)) case tree1 @ ValDef(name, tpt, _) => cpy.ValDef(tree1)(name, transform(tpt, Type), transform(tree1.rhs)) - case tree1 @ DefDef(name, tparams, vparamss, tpt, _) => - cpy.DefDef(tree1)(name, transformSub(tparams), vparamss mapConserve (transformSub(_)), transform(tpt, Type), transform(tree1.rhs)) + case tree1 @ DefDef(name, paramss, tpt, _) => + cpy.DefDef(tree1)(name, transformParamss(paramss), transform(tpt, Type), transform(tree1.rhs)) case tree1 @ TypeDef(name, rhs) => cpy.TypeDef(tree1)(name, transform(rhs, Type)) case impl @ Template(constr, parents, self, _) => diff --git a/compiler/test/dotty/tools/dotc/parsing/ModifiersParsingTest.scala b/compiler/test/dotty/tools/dotc/parsing/ModifiersParsingTest.scala index 952e8dfbe27f..15d43653025a 100644 --- a/compiler/test/dotty/tools/dotc/parsing/ModifiersParsingTest.scala +++ b/compiler/test/dotty/tools/dotc/parsing/ModifiersParsingTest.scala @@ -24,22 +24,22 @@ object ModifiersParsingTest { extension (code: Tree) { def firstConstrValDef: ValDef = code match { case d.TypeDef(_, d.Template(constr, _, _, _)) => - constr.vparamss.head.head + constr.termParamss.head.head } def firstTypeParam: TypeDef = code match { case d.TypeDef(_, d.Template(constr, _, _, _)) => - constr.tparams.head + constr.leadingTypeParams.head } def defParam(i: Int): ValDef = code match { - case d.DefDef(_, _, vparamss, _, _) => - vparamss.head.toArray.apply(i) + case code @ d.DefDef(_, _, _, _) => + code.termParamss.head.toArray.apply(i) } def defParam(i: Int, j: Int): ValDef = code match { - case d.DefDef(_, _, vparamss, _, _) => - vparamss.toArray.apply(i).toArray.apply(j) + case code @ d.DefDef(_, _, _, _) => + code.termParamss.toArray.apply(i).toArray.apply(j) } def funParam(i: Int): Tree = code match { diff --git a/compiler/test/dotty/tools/dotc/printing/PrinterTests.scala b/compiler/test/dotty/tools/dotc/printing/PrinterTests.scala index acf7610238a2..8ce53e28b93d 100644 --- a/compiler/test/dotty/tools/dotc/printing/PrinterTests.scala +++ b/compiler/test/dotty/tools/dotc/printing/PrinterTests.scala @@ -46,7 +46,7 @@ class PrinterTests extends DottyTest { checkCompile("typer", source) { (tree, context) => given Context = context - val bar @ Trees.DefDef(_, _, _, _, _) = tree.find(tree => tree.symbol.name == termName("bar2")).get + val bar @ Trees.DefDef(_, _, _, _) = tree.find(tree => tree.symbol.name == termName("bar2")).get assertEquals("Int & (Boolean | String)", bar.tpt.show) } } diff --git a/doc-tool/src/dotty/tools/dottydoc/core/DocImplicitsPhase.scala b/doc-tool/src/dotty/tools/dottydoc/core/DocImplicitsPhase.scala index 479dce95c068..e9f61c47dce5 100644 --- a/doc-tool/src/dotty/tools/dottydoc/core/DocImplicitsPhase.scala +++ b/doc-tool/src/dotty/tools/dottydoc/core/DocImplicitsPhase.scala @@ -13,15 +13,13 @@ class DocImplicitsPhase extends MiniPhase { def phaseName = "addImplicitsPhase" override def transformDefDef(tree: DefDef)(using Context): Tree = { - if ( - tree.symbol.isOneOf(Flags.GivenOrImplicit) && // has to have an implicit flag - tree.symbol.owner.isStaticOwner && // owner has to be static (e.g. top-level `object`) - tree.vparamss.length > 0 && - tree.vparamss(0).length == 1 // should only take one arg, since it has to be a transformation - ) { - val convertee = tree.vparamss(0)(0).symbol.info.widenDealias.finalResultType.typeSymbol // the pimped type (i.e. `class`) + if tree.symbol.isOneOf(Flags.GivenOrImplicit) // has to have an implicit flag + && tree.symbol.owner.isStaticOwner // owner has to be static (e.g. top-level `object`) + && tree.termParamss.length > 0 + && tree.termParamss(0).length == 1 // should only take one arg, since it has to be a transformation + then + val convertee = tree.termParamss(0)(0).symbol.info.widenDealias.finalResultType.typeSymbol // the pimped type (i.e. `class`) ctx.docbase.addDef(convertee, tree.symbol.info.widenDealias.finalResultType.typeSymbol) - } tree } diff --git a/docs/docs/reference/contextual/extension-methods.md b/docs/docs/reference/contextual/extension-methods.md index 6a0a2eac0375..b650e2ea81cb 100644 --- a/docs/docs/reference/contextual/extension-methods.md +++ b/docs/docs/reference/contextual/extension-methods.md @@ -59,8 +59,8 @@ Note the swap of the two parameters `x` and `xs` when translating the right-associative operator `+:` to an extension method. This is analogous to the implementation of right binding operators as normal methods. The Scala compiler preprocesses an infix operation `x +: xs` to `xs.+:(x)`, so the extension -method ends up being applied to the sequence as first argument (in other words, -the two swaps cancel each other out). +method ends up being applied to the sequence as first argument (in other words, the +two swaps cancel each other out). See [here for details](./right-associative-extension-methods.html). ### Generic Extensions @@ -74,27 +74,32 @@ extension [T: Numeric](x: T) def + (y: T): T = summon[Numeric[T]].plus(x, y) ``` -If an extension method has type parameters, they come immediately after `extension` and are followed by the extended parameter. -When calling a generic extension method, any explicitly given type arguments follow the method name. -So the `second` method could be instantiated as follows: - +Type parameters on extensions can also be combined with type parameters on the methods +themselves: ```scala -List(1, 2, 3).second[Int] +extension [T](xs: List[T]) + def def sumBy[B](f: A => B)(using Numeric[B]): B = ... ``` -Of course, the type argument here would usually be left out since it can be inferred. - +Type arguments matching method type parameters are passed as usual: +```scala +List("a", "bb", "ccc").sumBy[Int](_.length) +``` +By contrast, type arguments matching type parameters following `extension` can be passed +only if the method is referenced as a regular method: +```scala +List[String]("a", "bb", "ccc").sumBy(_.length) +``` +or, passing, both type arguments +```scala +List[String]("a", "bb", "ccc").sumBy[Int](_.length) +``` Extensions can also take using clauses. For instance, the `+` extension above could equivalently be written with a using clause: ```scala extension [T](x: T)(using n: Numeric[T]) def + (y: T): T = n.plus(x, y) ``` - -**Note**: Type parameters have to be given after the `extension` keyword; they cannot be given after the `def`. -This restriction might be lifted in the future once we support multiple type parameter clauses in a method. -By contrast, using clauses can be defined for the `extension` as well as per `def`. - ### Collective Extensions Sometimes, one wants to define several extension methods that share the same diff --git a/docs/docs/reference/contextual/right-associative-extension-methods.md b/docs/docs/reference/contextual/right-associative-extension-methods.md new file mode 100644 index 000000000000..64b7da8e695c --- /dev/null +++ b/docs/docs/reference/contextual/right-associative-extension-methods.md @@ -0,0 +1,45 @@ +--- +layout: doc-page +title: "Right-Associative Extension Methods: Details" +--- + +The most general form of leading parameters of an extension method is as follows: + + - A possibly empty list of using clauses `leadingUsing` + - A single parameter `extensionParam` + - A possibly empty list of using clauses `trailingUsing` + +This is then followed by `def`, the method name, and possibly further parameters +`otherParams`. An example is: + +```scala + extension (using a: A, b: B)(using c: C) // <-- leadingUsing + (x: X) // <-- extensionParam + (using d: D) // <-- trailingUsing + def +:: (y: Y)(using e: E)(z: Z) // <-- otherParams +``` +An extension method is treated as a right associative operator if +it has a name ending in `:` and is immediately followed by a +single parameter. In the example above, that parameter is `(y: Y)`. + +The Scala compiler pre-processes a right-associative infix operation such as `x +: xs` +to `xs.+:(x)` if `x` is a pure expression or a call-by-name parameter and to `val y = x; xs.+:(y)` otherwise. This is necessary since a regular right-associative infix method +is defined in the class of its right operand. To make up for this swap, +the expansion of right-associative extension methods performs an analogous parameter swap. More precisely, if `otherParams` consists of a single parameter +`rightParam` followed by `remaining`, the total parameter sequence +of the extension method's expansion is: +``` + leadingUsing rightParam trailingUsing extensionParam remaining +``` +For instance, the `+::` method above would become +```scala + def +:: (using a: A, b: B)(using c: C) + (y: Y) + (using d: D) + (x: X) + (using e: E)(z: Z) +``` +This expansion has to be kept in mind when writing right-associative extension +methods with inter-parameter dependencies. + +An overall simpler design could be obtained if right-associative operators could _only_ be defined as extension methods, and would be disallowed as normal methods. In that case neither arguments nor parameters would have to be swapped. Future versions of Scala should strive to achieve this simplification. diff --git a/docs/docs/reference/contextual/type-classes.md b/docs/docs/reference/contextual/type-classes.md index e771595a28e5..5911eff48eed 100644 --- a/docs/docs/reference/contextual/type-classes.md +++ b/docs/docs/reference/contextual/type-classes.md @@ -101,16 +101,16 @@ As in the previous example of Monoids, [`extension` methods](extension-methods.m ```scala trait Functor[F[_]]: - extension [A, B](x: F[A]) - def map(f: A => B): F[B] + extension [A](x: F[A]) + def map[B](f: A => B): F[B] ``` The instance of `Functor` for `List` now becomes: ```scala given Functor[List] with - extension [A, B](xs: List[A]) - def map(f: A => B): List[B] = + extension [A](xs: List[A]) + def map[B](f: A => B): List[B] = xs.map(f) // List already has a `map` method ``` @@ -143,12 +143,12 @@ trait Monad[F[_]] extends Functor[F]: /** The unit value for a monad */ def pure[A](x: A): F[A] - extension [A, B](x: F[A]) + extension [A](x: F[A]) /** The fundamental composition operation */ - def flatMap(f: A => F[B]): F[B] + def flatMap[B](f: A => F[B]): F[B] /** The `map` operation can now be defined in terms of `flatMap` */ - def map(f: A => B) = x.flatMap(f.andThen(pure)) + def map[B](f: A => B) = x.flatMap(f.andThen(pure)) end Monad ``` @@ -161,8 +161,8 @@ A `List` can be turned into a monad via this `given` instance: given listMonad: Monad[List] with def pure[A](x: A): List[A] = List(x) - extension [A, B](xs: List[A]) - def flatMap(f: A => List[B]): List[B] = + extension [A](xs: List[A]) + def flatMap[B](f: A => List[B]): List[B] = xs.flatMap(f) // rely on the existing `flatMap` method of `List` ``` @@ -178,8 +178,8 @@ it explicitly. given optionMonad: Monad[Option] with def pure[A](x: A): Option[A] = Option(x) - extension [A, B](xo: Option[A]) - def flatMap(f: A => Option[B]): Option[B] = xo match + extension [A](xo: Option[A]) + def flatMap[B](f: A => Option[B]): Option[B] = xo match case Some(x) => f(x) case None => None ``` @@ -227,8 +227,8 @@ given configDependentMonad: Monad[ConfigDependent] with def pure[A](x: A): ConfigDependent[A] = config => x - extension [A, B](x: ConfigDependent[A]) - def flatMap(f: A => ConfigDependent[B]): ConfigDependent[B] = + extension [A](x: ConfigDependent[A]) + def flatMap[B](f: A => ConfigDependent[B]): ConfigDependent[B] = config => f(x(config))(config) end configDependentMonad @@ -248,8 +248,8 @@ given configDependentMonad: Monad[[Result] =>> Config => Result] with def pure[A](x: A): Config => A = config => x - extension [A, B](x: Config => A) - def flatMap(f: A => Config => B): Config => B = + extension [A](x: Config => A) + def flatMap[B](f: A => Config => B): Config => B = config => f(x(config))(config) end configDependentMonad @@ -263,8 +263,8 @@ given readerMonad[Ctx]: Monad[[X] =>> Ctx => X] with def pure[A](x: A): Ctx => A = ctx => x - extension [A, B](x: Ctx => A) - def flatMap(f: A => Ctx => B): Ctx => B = + extension [A](x: Ctx => A) + def flatMap[B](f: A => Ctx => B): Ctx => B = ctx => f(x(ctx))(ctx) end readerMonad diff --git a/library/src-bootstrapped/scala/IArray.scala b/library/src-bootstrapped/scala/IArray.scala index 21a36e00b565..75fcb758a3dc 100644 --- a/library/src-bootstrapped/scala/IArray.scala +++ b/library/src-bootstrapped/scala/IArray.scala @@ -43,7 +43,7 @@ object opaques: extension [T](arr: IArray[T]) def length: Int = arr.asInstanceOf[Array[T]].length /** Returns this array concatenated with the given array. */ - extension [T, U >: T: ClassTag](arr: IArray[T]) def ++(that: IArray[U]): IArray[U] = + extension [T](arr: IArray[T]) def ++ [U >: T: ClassTag](that: IArray[U]): IArray[U] = genericArrayOps(arr) ++ that /** Tests whether this array contains a given value as an element. */ @@ -53,15 +53,15 @@ object opaques: genericArrayOps(arr).exists(_ == elem) /** Copy elements of this array to another array. */ - extension [T, U >: T](arr: IArray[T]) def copyToArray(xs: Array[U]): Int = + extension [T](arr: IArray[T]) def copyToArray[U >: T](xs: Array[U]): Int = genericArrayOps(arr).copyToArray(xs) /** Copy elements of this array to another array. */ - extension [T, U >: T](arr: IArray[T]) def copyToArray(xs: Array[U], start: Int): Int = + extension [T](arr: IArray[T]) def copyToArray[U >: T](xs: Array[U], start: Int): Int = genericArrayOps(arr).copyToArray(xs, start) /** Copy elements of this array to another array. */ - extension [T, U >: T](arr: IArray[T]) def copyToArray(xs: Array[U], start: Int, len: Int): Int = + extension [T](arr: IArray[T]) def copyToArray[U >: T](xs: Array[U], start: Int, len: Int): Int = genericArrayOps(arr).copyToArray(xs, start, len) /** Counts the number of elements in this array which satisfy a predicate */ @@ -98,26 +98,26 @@ object opaques: /** Builds a new array by applying a function to all elements of this array * and using the elements of the resulting collections. */ - extension [T, U: ClassTag](arr: IArray[T]) def flatMap(f: T => IterableOnce[U]): IArray[U] = + extension [T](arr: IArray[T]) def flatMap[U: ClassTag](f: T => IterableOnce[U]): IArray[U] = genericArrayOps(arr).flatMap(f) /** Flattens a two-dimensional array by concatenating all its rows * into a single array. */ - extension [T, U: ClassTag](arr: IArray[T]) def flatten(using T => Iterable[U]): IArray[U] = + extension [T](arr: IArray[T]) def flatten[U: ClassTag](using T => Iterable[U]): IArray[U] = genericArrayOps(arr).flatten /** Folds the elements of this array using the specified associative binary operator. */ - extension [T, U >: T: ClassTag](arr: IArray[T]) def fold(z: U)(op: (U, U) => U): U = + extension [T](arr: IArray[T]) def fold[U >: T: ClassTag](z: U)(op: (U, U) => U): U = genericArrayOps(arr).fold(z)(op) /** Applies a binary operator to a start value and all elements of this array, * going left to right. */ - extension [T, U: ClassTag](arr: IArray[T]) def foldLeft(z: U)(op: (U, T) => U): U = + extension [T](arr: IArray[T]) def foldLeft[U: ClassTag](z: U)(op: (U, T) => U): U = genericArrayOps(arr).foldLeft(z)(op) /** Applies a binary operator to all elements of this array and a start value, * going right to left. */ - extension [T, U: ClassTag](arr: IArray[T]) def foldRight(z: U)(op: (T, U) => U): U = + extension [T](arr: IArray[T]) def foldRight[U: ClassTag](z: U)(op: (T, U) => U): U = genericArrayOps(arr).foldRight(z)(op) /** Tests whether a predicate holds for all elements of this array. */ @@ -125,7 +125,7 @@ object opaques: genericArrayOps(arr).forall(p) /** Apply `f` to each element for its side effects. */ - extension [T, U](arr: IArray[T]) def foreach(f: T => U): Unit = + extension [T](arr: IArray[T]) def foreach[U](f: T => U): Unit = genericArrayOps(arr).foreach(f) /** Selects the first element of this array. */ @@ -181,7 +181,7 @@ object opaques: genericArrayOps(arr).lastIndexWhere(p, end) /** Builds a new array by applying a function to all elements of this array. */ - extension [T, U: ClassTag](arr: IArray[T]) def map(f: T => U): IArray[U] = + extension [T](arr: IArray[T]) def map[U: ClassTag](f: T => U): IArray[U] = genericArrayOps(arr).map(f) /** Tests whether the array is not empty. */ @@ -197,17 +197,17 @@ object opaques: genericArrayOps(arr).reverse /** Computes a prefix scan of the elements of the array. */ - extension [T, U >: T: ClassTag](arr: IArray[T]) def scan(z: U)(op: (U, U) => U): IArray[U] = + extension [T](arr: IArray[T]) def scan[U >: T: ClassTag](z: U)(op: (U, U) => U): IArray[U] = genericArrayOps(arr).scan(z)(op) /** Produces an array containing cumulative results of applying the binary * operator going left to right. */ - extension [T, U: ClassTag](arr: IArray[T]) def scanLeft(z: U)(op: (U, T) => U): IArray[U] = + extension [T](arr: IArray[T]) def scanLeft[U: ClassTag](z: U)(op: (U, T) => U): IArray[U] = genericArrayOps(arr).scanLeft(z)(op) /** Produces an array containing cumulative results of applying the binary * operator going right to left. */ - extension [T, U: ClassTag](arr: IArray[T]) def scanRight(z: U)(op: (T, U) => U): IArray[U] = + extension [T](arr: IArray[T]) def scanRight[U: ClassTag](z: U)(op: (T, U) => U): IArray[U] = genericArrayOps(arr).scanRight(z)(op) /** The size of this array. */ @@ -220,7 +220,7 @@ object opaques: /** Sorts this array according to the Ordering which results from transforming * an implicitly given Ordering with a transformation function. */ - extension [T, U: ClassTag](arr: IArray[T]) def sortBy(f: T => U)(using math.Ordering[U]): IArray[T] = + extension [T](arr: IArray[T]) def sortBy[U: ClassTag](f: T => U)(using math.Ordering[U]): IArray[T] = genericArrayOps(arr).sortBy(f) /** Sorts this array according to a comparison function. */ @@ -240,7 +240,7 @@ object opaques: genericArrayOps(arr).splitAt(n) /** Tests whether this array starts with the given array. */ - extension [T, U >: T: ClassTag](arr: IArray[T]) def startsWith(that: IArray[U], offset: Int = 0): Boolean = + extension [T](arr: IArray[T]) def startsWith[U >: T: ClassTag](that: IArray[U], offset: Int = 0): Boolean = genericArrayOps(arr).startsWith(that) /** The rest of the array without its first element. */ @@ -270,7 +270,7 @@ object opaques: /** Returns an array formed from this array and another iterable collection * by combining corresponding elements in pairs. * If one of the two collections is longer than the other, its remaining elements are ignored. */ - extension [T, U: ClassTag](arr: IArray[T]) def zip(that: IArray[U]): IArray[(T, U)] = + extension [T](arr: IArray[T]) def zip[U: ClassTag](that: IArray[U]): IArray[(T, U)] = genericArrayOps(arr).zip(that) } end opaques diff --git a/library/src-bootstrapped/scala/quoted/Quotes.scala b/library/src-bootstrapped/scala/quoted/Quotes.scala index f96f373ea6e8..1bdadd3e85af 100644 --- a/library/src-bootstrapped/scala/quoted/Quotes.scala +++ b/library/src-bootstrapped/scala/quoted/Quotes.scala @@ -53,12 +53,12 @@ trait Quotes { self: runtime.QuoteUnpickler & runtime.QuoteMatching => end extension // Extension methods for `Expr[Any]` that take another explicit type parameter - extension [X](self: Expr[Any]) + extension (self: Expr[Any]) /** Checks is the `quoted.Expr[?]` is valid expression of type `X` */ - def isExprOf(using Type[X]): Boolean + def isExprOf[X](using Type[X]): Boolean /** Convert this to an `quoted.Expr[X]` if this expression is a valid expression of type `X` or throws */ - def asExprOf(using Type[X]): Expr[X] + def asExprOf[X](using Type[X]): Expr[X] end extension /** Low-level Typed AST metaprogramming API. @@ -248,8 +248,8 @@ trait Quotes { self: runtime.QuoteUnpickler & runtime.QuoteMatching => end extension /** Convert this tree to an `quoted.Expr[T]` if the tree is a valid expression or throws */ - extension [T](self: Tree) - def asExprOf(using Type[T]): Expr[T] + extension (self: Tree) + def asExprOf[T](using Type[T]): Expr[T] extension [ThisTree <: Tree](self: ThisTree) /** Changes the owner of the symbols in the tree */ diff --git a/library/src/scala/quoted/ExprMap.scala b/library/src/scala/quoted/ExprMap.scala index c6163f14207b..36d98845453b 100644 --- a/library/src/scala/quoted/ExprMap.scala +++ b/library/src/scala/quoted/ExprMap.scala @@ -145,7 +145,9 @@ trait ExprMap: trees.mapConserve(x => transformTypeCaseDef(x)(owner)) } - new MapChildren().transformTermChildren(e.asTerm, TypeRepr.of[T])(Symbol.spliceOwner).asExprOf[T] + new MapChildren() + .transformTermChildren(e.asTerm, TypeRepr.of[T])(Symbol.spliceOwner) + .asExprOf[T] } end ExprMap diff --git a/scala3doc-testcases/src/tests/extensionMethodSignatures.scala b/scala3doc-testcases/src/tests/extensionMethodSignatures.scala index 50152cc2ef1d..34237da32983 100644 --- a/scala3doc-testcases/src/tests/extensionMethodSignatures.scala +++ b/scala3doc-testcases/src/tests/extensionMethodSignatures.scala @@ -15,7 +15,7 @@ class ClassOne = 56 extension (c: ClassTwo) - def |||:(a: Int, b: Int, d: Int)(e: String): Int + def |||:(a: Int)(b: Int, d: Int)(e: String): Int = 56 def ++:(a: Int): Int = 45 diff --git a/tasty/src/dotty/tools/tasty/TastyFormat.scala b/tasty/src/dotty/tools/tasty/TastyFormat.scala index d9ec4fc49bc1..88c44a21ea2c 100644 --- a/tasty/src/dotty/tools/tasty/TastyFormat.scala +++ b/tasty/src/dotty/tools/tasty/TastyFormat.scala @@ -62,17 +62,20 @@ Standard-Section: "ASTs" TopLevelStat* IMPORT Length qual_Term Selector* -- import qual selectors EXPORT Length qual_Term Selector* -- export qual selectors ValOrDefDef = VALDEF Length NameRef type_Term rhs_Term? Modifier* -- modifiers val name : type (= rhs)? - DEFDEF Length NameRef TypeParam* Params* returnType_Term - rhs_Term? Modifier* -- modifiers def name [typeparams] paramss : returnType (= rhs)? + DEFDEF Length NameRef Param* returnType_Term rhs_Term? + Modifier* -- modifiers def name [typeparams] paramss : returnType (= rhs)? Selector = IMPORTED name_NameRef -- name, "_" for normal wildcards, "" for given wildcards RENAMED to_NameRef -- => name BOUNDED type_Term -- type bound TypeParam = TYPEPARAM Length NameRef type_Term Modifier* -- modifiers name bounds - Param = PARAM Length NameRef type_Term rhs_Term? Modifier* -- modifiers name : type (= rhs_Term)?. `rhsTerm` is present in the case of an aliased class parameter - PARAMEND -- ends a parameter clause - -- needed if previous parameter clause is empty or another parameter clause follows - Template = TEMPLATE Length TypeParam* Param* parent_Term* Self? Stat* -- [typeparams] paramss extends parents { self => stats }, where Stat* always starts with the primary constructor. + TermParam = PARAM Length NameRef type_Term rhs_Term? Modifier* -- modifiers name : type (= rhs_Term)?. `rhsTerm` is present in the case of an aliased class parameter + EMPTYCLAUSE -- an empty parameter clause () + SPLITCLAUSE -- splits two non-empty parameter clauses of the same kind + Param = TypeParam + TermParam + Template = TEMPLATE Length TypeParam* TermParam* parent_Term* Self? + Stat* -- [typeparams] paramss extends parents { self => stats }, where Stat* always starts with the primary constructor. Self = SELFDEF selfName_NameRef selfType_Term -- selfName : selfType Term = Path -- Paths represent both types and terms @@ -222,9 +225,9 @@ Note: The signature of a SELECTin or TERMREFin node is the signature of the sele Note: Tree tags are grouped into 5 categories that determine what follows, and thus allow to compute the size of the tagged tree in a generic way. - Category 1 (tags 1-49) : tag - Category 2 (tags 50-79) : tag Nat - Category 3 (tags 80-109) : tag AST + Category 1 (tags 1-59) : tag + Category 2 (tags 60-89) : tag Nat + Category 3 (tags 90-109) : tag AST Category 4 (tags 110-127): tag Nat AST Category 5 (tags 128-255): tag Length @@ -260,8 +263,8 @@ Standard Section: "Comments" Comment* object TastyFormat { final val header: Array[Int] = Array(0x5C, 0xA1, 0xAB, 0x1F) - val MajorVersion: Int = 26 - val MinorVersion: Int = 1 + val MajorVersion: Int = 27 + val MinorVersion: Int = 0 final val ASTsSection = "ASTs" final val PositionsSection = "Positions" @@ -374,46 +377,47 @@ object TastyFormat { final val PARAMsetter = 38 final val EXPORTED = 39 final val OPEN = 40 - final val PARAMEND = 41 - final val PARAMalias = 42 - final val TRANSPARENT = 43 - final val INFIX = 44 + final val PARAMalias = 41 + final val TRANSPARENT = 42 + final val INFIX = 43 + final val EMPTYCLAUSE = 44 + final val SPLITCLAUSE = 45 // Cat. 2: tag Nat - final val SHAREDterm = 50 - final val SHAREDtype = 51 - final val TERMREFdirect = 52 - final val TYPEREFdirect = 53 - final val TERMREFpkg = 54 - final val TYPEREFpkg = 55 - final val RECthis = 56 - final val BYTEconst = 57 - final val SHORTconst = 58 - final val CHARconst = 59 - final val INTconst = 60 - final val LONGconst = 61 - final val FLOATconst = 62 - final val DOUBLEconst = 63 - final val STRINGconst = 64 - final val IMPORTED = 65 - final val RENAMED = 66 + final val SHAREDterm = 60 + final val SHAREDtype = 61 + final val TERMREFdirect = 62 + final val TYPEREFdirect = 63 + final val TERMREFpkg = 64 + final val TYPEREFpkg = 65 + final val RECthis = 66 + final val BYTEconst = 67 + final val SHORTconst = 68 + final val CHARconst = 69 + final val INTconst = 70 + final val LONGconst = 71 + final val FLOATconst = 72 + final val DOUBLEconst = 73 + final val STRINGconst = 74 + final val IMPORTED = 75 + final val RENAMED = 76 // Cat. 3: tag AST - final val THIS = 80 - final val QUALTHIS = 81 - final val CLASSconst = 82 - final val BYNAMEtype = 83 - final val BYNAMEtpt = 84 - final val NEW = 85 - final val THROW = 86 - final val IMPLICITarg = 87 - final val PRIVATEqualified = 88 - final val PROTECTEDqualified = 89 - final val RECtype = 90 - final val SINGLETONtpt = 91 - final val BOUNDED = 92 + final val THIS = 90 + final val QUALTHIS = 91 + final val CLASSconst = 92 + final val BYNAMEtype = 93 + final val BYNAMEtpt = 94 + final val NEW = 95 + final val THROW = 96 + final val IMPLICITarg = 97 + final val PRIVATEqualified = 98 + final val PROTECTEDqualified = 99 + final val RECtype = 100 + final val SINGLETONtpt = 101 + final val BOUNDED = 102 // Cat. 4: tag Nat AST @@ -493,7 +497,7 @@ object TastyFormat { /** Useful for debugging */ def isLegalTag(tag: Int): Boolean = - firstSimpleTreeTag <= tag && tag <= INFIX || + firstSimpleTreeTag <= tag && tag <= SPLITCLAUSE || firstNatTreeTag <= tag && tag <= RENAMED || firstASTTreeTag <= tag && tag <= BOUNDED || firstNatASTTreeTag <= tag && tag <= NAMEDARG || @@ -602,8 +606,9 @@ object TastyFormat { case PARAMsetter => "PARAMsetter" case EXPORTED => "EXPORTED" case OPEN => "OPEN" - case PARAMEND => "PARAMEND" case PARAMalias => "PARAMalias" + case EMPTYCLAUSE => "EMPTYCLAUSE" + case SPLITCLAUSE => "SPLITCLAUSE" case SHAREDterm => "SHAREDterm" case SHAREDtype => "SHAREDtype" diff --git a/tests/neg-custom-args/extmethods-tparams.scala b/tests/neg-custom-args/extmethods-tparams.scala deleted file mode 100644 index 9cf6e94c14c4..000000000000 --- a/tests/neg-custom-args/extmethods-tparams.scala +++ /dev/null @@ -1,2 +0,0 @@ -extension (self: T) def foo[T] = ??? // error -extension [T1](self: T1) def bar[T2] = ??? // error \ No newline at end of file diff --git a/tests/neg/extension-cannot-have-type.scala b/tests/neg/extension-cannot-have-type.scala deleted file mode 100644 index c6fd2d3df4b4..000000000000 --- a/tests/neg/extension-cannot-have-type.scala +++ /dev/null @@ -1,5 +0,0 @@ -object Test { - extension [T] (t: T) { - def f[U](u: U): T = ??? // error: extension method cannot have type parameters here, all type parameters go after `extension` - } -} \ No newline at end of file diff --git a/tests/neg/i6900.scala b/tests/neg/i6900.scala deleted file mode 100644 index 18231f4e6cda..000000000000 --- a/tests/neg/i6900.scala +++ /dev/null @@ -1,15 +0,0 @@ -object Test2 { - - // Works with extension method - extension [A](a: A) - def foo[C]: C => A = _ => a // error: extension method cannot have type parameters - - 1.foo.foo - - // ... but have to pass 2 parameters - 1.foo.foo[Any => Int, String] - 1.foo[Int, String].foo - 1.foo[Int, String].foo[String => Int, String] - -} - diff --git a/tests/neg/rightassoc-extmethod.check b/tests/neg/rightassoc-extmethod.check new file mode 100644 index 000000000000..a1d2328ed2ff --- /dev/null +++ b/tests/neg/rightassoc-extmethod.check @@ -0,0 +1,8 @@ +-- Error: tests/neg/rightassoc-extmethod.scala:1:23 -------------------------------------------------------------------- +1 |extension (x: Int) def +: (using String): Int = x // error + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | right-associative extension method cannot start with using clause +-- Error: tests/neg/rightassoc-extmethod.scala:2:23 -------------------------------------------------------------------- +2 |extension (x: Int) def *: (y: Int, z: Int) = x // error + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | right-associative extension method must start with a single parameter diff --git a/tests/neg/rightassoc-extmethod.scala b/tests/neg/rightassoc-extmethod.scala new file mode 100644 index 000000000000..4a136ca6eac3 --- /dev/null +++ b/tests/neg/rightassoc-extmethod.scala @@ -0,0 +1,3 @@ +extension (x: Int) def +: (using String): Int = x // error +extension (x: Int) def *: (y: Int, z: Int) = x // error + diff --git a/tests/pos-macros/asExprOf.scala b/tests/pos-macros/asExprOf.scala new file mode 100644 index 000000000000..dd0c4e57a2d0 --- /dev/null +++ b/tests/pos-macros/asExprOf.scala @@ -0,0 +1,7 @@ +import scala.quoted._ + +def test(using Quotes)(x: Expr[_]) = { + import quotes.reflect._ + x.asTerm.asExprOf[Any] +} + diff --git a/tests/pos/extmethods.scala b/tests/pos/extmethods.scala index fe95a1c79b04..368b4f439916 100644 --- a/tests/pos/extmethods.scala +++ b/tests/pos/extmethods.scala @@ -20,3 +20,38 @@ object CollectionStrawMan { protected[this] def newBuilder = new ArrayBuffer[A].mapResult(_.toArray(elemTag)) } } + +extension [A](xs: List[A]) + inline def foldl[B](z: B)(op: (B, A) => B): B = + (xs: List[A]).foldLeft(z)(op) + inline def concat[B <: A](ys: List[B]): List[A] = xs ++ ys + +val x = List("a", "b").foldl[Int](0)((x, y) => x + y.length) +val y = Nil.concat(1 :: Nil) +val y1: List[Int] = y +val z = (1 :: Nil).concat(Nil) +val z1: List[Int] = z + +trait TT: + type A + val m: A + def f[B <: A](x: B): A = if ??? then m else x + +extension (x: TT) + def foo[B <: x.A](y: B) = x.f(y) + +object CC extends TT: + type A = Seq[Int] + val m = Nil + +val xx = CC.foo(List(1, 2, 3)) + +extension (x: TT) + def ff[X](): Int = 1 + def ff[X](s: String): Int = s.length + def ff[X](n: Int): Int = n + +val yy = + CC.ff[Int]() + + CC.ff[String]("abc") + + CC.ff[Int](22) diff --git a/tests/pos/i6900.scala b/tests/pos/i6900.scala index 30a52ff1a01c..55587a18b4ba 100644 --- a/tests/pos/i6900.scala +++ b/tests/pos/i6900.scala @@ -21,15 +21,14 @@ object Test1 { object Test2 { // Works with extension method - extension [A, C](a: A) - def foo: C => A = _ => a + extension [A](a: A) + def foo[C]: C => A = _ => a 1.foo.foo - // ... but have to pass 2 parameters - 1.foo.foo[Any => Int, String] - 1.foo[Int, String].foo - 1.foo[Int, String].foo[String => Int, String] + 1.foo.foo[String] + 1.foo[String].foo + 1.foo[String].foo[String] } diff --git a/tests/pos/i9530.scala b/tests/pos/i9530.scala new file mode 100644 index 000000000000..32b2f26dbd6c --- /dev/null +++ b/tests/pos/i9530.scala @@ -0,0 +1,37 @@ +trait Food +case class Banana(color: String) extends Food + +trait Diet[A <: Animal]: + type F <: Food + def food: Seq[F] + +trait Animal +object Animal: + extension [A <: Animal](using diet: Diet[A])(animal: A) def food1 = diet.food + extension [A <: Animal](animal: A)(using diet: Diet[A]) def food2 = diet.food + +extension [A <: Animal](using diet: Diet[A])(animal: A) def food3 = diet.food +extension [A <: Animal](animal: A)(using diet: Diet[A]) def food4 = diet.food + +trait Monkey extends Animal + +given Diet[Monkey] with + type F = Banana + def food: Seq[Banana] = Seq(new Banana("yellow"), Banana("green")) + +trait FoodOps +given FoodOps with + extension [A <: Animal](using diet: Diet[A])(animal: A) def food5 = diet.food + extension [A <: Animal](animal: A)(using diet: Diet[A]) def food6 = diet.food + + +val monkey = new Monkey {} + +val foods = Seq( + monkey.food1, + monkey.food2, + monkey.food3, + monkey.food4, + monkey.food5, + monkey.food6, +) diff --git a/tests/pos/reference/delegates.scala b/tests/pos/reference/delegates.scala index 9a05d0717788..88002d939330 100644 --- a/tests/pos/reference/delegates.scala +++ b/tests/pos/reference/delegates.scala @@ -15,11 +15,11 @@ class Common: def unit: T trait Functor[F[_]]: - extension [A, B](x: F[A]) def map (f: A => B): F[B] + extension [A](x: F[A]) def map[B](f: A => B): F[B] trait Monad[F[_]] extends Functor[F]: - extension [A, B](x: F[A]) def flatMap (f: A => F[B]): F[B] - extension [A, B](x: F[A]) def map (f: A => B) = x.flatMap(f `andThen` pure) + extension [A](x: F[A]) def flatMap[B](f: A => F[B]): F[B] + extension [A](x: F[A]) def map[B](f: A => B) = x.flatMap(f `andThen` pure) def pure[A](x: A): F[A] end Common @@ -50,13 +50,13 @@ object Instances extends Common: def third = xs.tail.tail.head given listMonad: Monad[List] with - extension [A, B](xs: List[A]) def flatMap (f: A => List[B]): List[B] = + extension [A](xs: List[A]) def flatMap[B](f: A => List[B]): List[B] = xs.flatMap(f) def pure[A](x: A): List[A] = List(x) given readerMonad[Ctx]: Monad[[X] =>> Ctx => X] with - extension [A, B](r: Ctx => A) def flatMap (f: A => Ctx => B): Ctx => B = + extension [A](r: Ctx => A) def flatMap[B](f: A => Ctx => B): Ctx => B = ctx => f(r(ctx))(ctx) def pure[A](x: A): Ctx => A = ctx => x diff --git a/tests/pos/reference/extension-methods.scala b/tests/pos/reference/extension-methods.scala index 560255a1f8d6..daa187cafa50 100644 --- a/tests/pos/reference/extension-methods.scala +++ b/tests/pos/reference/extension-methods.scala @@ -20,7 +20,7 @@ object ExtMethods: extension [T](xs: List[T]) def second = xs.tail.head - assert(List(1, 2, 3).second[Int] == List(1, 2, 3).second) + assert(second[Int](List(1, 2, 3)) == List(1, 2, 3).second) extension [T: Numeric](x: T) def + (y: T): T = summon[Numeric[T]].plus(x, y) diff --git a/tests/run-bootstrapped/iarray-extmtds.scala b/tests/run-bootstrapped/iarray-extmtds.scala index ddc7610dd794..d075a5c1eca4 100644 --- a/tests/run-bootstrapped/iarray-extmtds.scala +++ b/tests/run-bootstrapped/iarray-extmtds.scala @@ -38,7 +38,7 @@ object Test extends App { assertDifferent(arr1.flatMap(x => List(x, x)), arr1) val twoDArr = IArray(List(1, 2), List(3, 4)) - assertDifferent(twoDArr.flatten[List[Int], Int], twoDArr) + assertDifferent(twoDArr.flatten[Int], twoDArr) println(arr1.fold(0)(_ + _)) diff --git a/tests/run/extension-methods.scala b/tests/run/extension-methods.scala index 04aa06d882eb..38183dcbeccb 100644 --- a/tests/run/extension-methods.scala +++ b/tests/run/extension-methods.scala @@ -85,26 +85,26 @@ object Test extends App { println(max(List(1, 2, 3), List(2))) trait Functor[F[_]] { - extension [A, B](x: F[A]) def map (f: A => B): F[B] + extension [A](x: F[A]) def map[B](f: A => B): F[B] } trait Monad[F[_]] extends Functor[F] { - extension [A, B](x: F[A]) - def flatMap (f: A => F[B]): F[B] - def map (f: A => B) = x.flatMap(f `andThen` pure) + extension [A](x: F[A]) + def flatMap[B](f: A => F[B]): F[B] + def map[B](f: A => B) = x.flatMap(f `andThen` pure) def pure[A](x: A): F[A] } implicit object ListMonad extends Monad[List] { - extension [A, B](xs: List[A]) def flatMap (f: A => List[B]): List[B] = + extension [A](xs: List[A]) def flatMap[B](f: A => List[B]): List[B] = xs.flatMap(f) def pure[A](x: A): List[A] = List(x) } class ReaderMonad[Ctx] extends Monad[[X] =>> Ctx => X] { - extension [A, B](r: Ctx => A) def flatMap (f: A => Ctx => B): Ctx => B = + extension [A](r: Ctx => A) def flatMap[B](f: A => Ctx => B): Ctx => B = ctx => f(r(ctx))(ctx) def pure[A](x: A): Ctx => A = ctx => x @@ -115,6 +115,6 @@ object Test extends App { fs.foldLeft(implicitly[Monad[F]].pure(x))((x: F[T], f: T => T) => if (true) implicitly[Monad[F]].map(x)(f) else if (true) x.map(f) - else x.map[T, T](f) + else x.map[T](f) ) } \ No newline at end of file diff --git a/tests/run/extmethods2.scala b/tests/run/extmethods2.scala index 2f61d36f260c..10b5640c4901 100644 --- a/tests/run/extmethods2.scala +++ b/tests/run/extmethods2.scala @@ -21,8 +21,8 @@ object Test extends App { def third: T = xs.tail.tail.head def concat(ys: List[T]) = xs ++ ys } - extension [T, U](xs: List[T]) { - def zipp(ys: List[U]): List[(T, U)] = xs.zip(ys) + extension [T](xs: List[T]) { + def zipp[U](ys: List[U]): List[(T, U)] = xs.zip(ys) } extension (xs: List[Int]) { def prod = (1 /: xs)(_ * _) @@ -32,14 +32,14 @@ object Test extends App { object B { import A._ val xs = List(1, 2, 3) - assert(xs.second[Int] == 2) + assert(xs.second == 2) assert(xs.third == 3) assert(A.second[Int](xs) == 2) assert(A.third(xs) == 3) assert(xs.prod == 6) assert(xs.concat(xs).length == 6) assert(xs.zipp(xs).map(_ + _).prod == 36) - assert(xs.zipp[Int, Int](xs).map(_ + _).prod == 36) + assert(xs.zipp[Int](xs).map(_ + _).prod == 36) } } diff --git a/tests/run/i9530.check b/tests/run/i9530.check new file mode 100644 index 000000000000..186fd084a859 --- /dev/null +++ b/tests/run/i9530.check @@ -0,0 +1,6 @@ +Expr(123) +123 +Expr(123) +123 +Expr(1234) +1234 diff --git a/tests/run/i9530.scala b/tests/run/i9530.scala new file mode 100644 index 000000000000..e0262764039f --- /dev/null +++ b/tests/run/i9530.scala @@ -0,0 +1,35 @@ +trait Scope: + type Expr + type Value + def expr(x: String): Expr + def value(e: Expr): Value + def combine(e1: Expr, e2: Expr): Expr + +extension (using s: Scope)(expr: s.Expr) + def show = expr.toString + def eval = s.value(expr) + def *: (other: s.Expr) = s.combine(expr, other) + +def f(using s: Scope)(x: s.Expr): (String, s.Value) = + (x.show, x.eval) + +given scope: Scope with + case class Expr(str: String) + type Value = Int + def expr(x: String) = Expr(x) + def value(e: Expr) = e.str.toInt + def combine(e1: Expr, e2: Expr) = Expr(e1.str ++ e2.str) + +@main def Test = + val e = scope.Expr("123") + val (s, v) = f(e) + println(s) + println(v) + val ss = e.show + println(ss) + val vv = e.eval + println(vv) + val e2 = e *: scope.Expr("4") + println(e2.show) + println(e2.eval) + diff --git a/tests/run/instances-anonymous.scala b/tests/run/instances-anonymous.scala index 11fa89c41416..7a42496504ee 100644 --- a/tests/run/instances-anonymous.scala +++ b/tests/run/instances-anonymous.scala @@ -91,25 +91,25 @@ object Test extends App { println(max(List(1, 2, 3), List(2))) trait Functor[F[_]] { - extension [A, B](x: F[A]) def map (f: A => B): F[B] + extension [A](x: F[A]) def map[B](f: A => B): F[B] } trait Monad[F[_]] extends Functor[F] { - extension [A, B](x: F[A]) def flatMap (f: A => F[B]): F[B] - extension [A, B](x: F[A]) def map (f: A => B) = x.flatMap(f `andThen` pure) + extension [A](x: F[A]) def flatMap[B](f: A => F[B]): F[B] + extension [A](x: F[A]) def map[B](f: A => B) = x.flatMap(f `andThen` pure) def pure[A](x: A): F[A] } given Monad[List] with { - extension [A, B](xs: List[A]) def flatMap (f: A => List[B]): List[B] = + extension [A](xs: List[A]) def flatMap[B](f: A => List[B]): List[B] = xs.flatMap(f) def pure[A](x: A): List[A] = List(x) } given [Ctx]: Monad[[X] =>> Ctx => X] with { - extension [A, B](r: Ctx => A) def flatMap (f: A => Ctx => B): Ctx => B = + extension [A](r: Ctx => A) def flatMap[B](f: A => Ctx => B): Ctx => B = ctx => f(r(ctx))(ctx) def pure[A](x: A): Ctx => A = ctx => x @@ -119,6 +119,6 @@ object Test extends App { fs.foldLeft(implicitly[Monad[F]].pure(x))((x: F[T], f: T => T) => if (true) implicitly[Monad[F]].map(x)(f) else if (true) x.map(f) - else x.map[T, T](f) + else x.map[T](f) ) } diff --git a/tests/run/instances.scala b/tests/run/instances.scala index e49896cbba21..128ea0700e02 100644 --- a/tests/run/instances.scala +++ b/tests/run/instances.scala @@ -89,24 +89,24 @@ object Test extends App { println(max(List(1, 2, 3), List(2))) trait Functor[F[_]]: - extension [A, B](x: F[A]) def map (f: A => B): F[B] + extension [A](x: F[A]) def map[B](f: A => B): F[B] end Functor trait Monad[F[_]] extends Functor[F]: - extension [A, B](x: F[A]) def flatMap (f: A => F[B]): F[B] - extension [A, B](x: F[A]) def map (f: A => B) = x.flatMap(f `andThen` pure) + extension [A](x: F[A]) def flatMap[B](f: A => F[B]): F[B] + extension [A](x: F[A]) def map[B](f: A => B) = x.flatMap(f `andThen` pure) def pure[A](x: A): F[A] end Monad given listMonad: Monad[List] with - extension [A, B](xs: List[A]) def flatMap (f: A => List[B]): List[B] = + extension [A](xs: List[A]) def flatMap[B](f: A => List[B]): List[B] = xs.flatMap(f) def pure[A](x: A): List[A] = List(x) given readerMonad[Ctx]: Monad[[X] =>> Ctx => X] with - extension [A, B](r: Ctx => A) def flatMap (f: A => Ctx => B): Ctx => B = + extension [A](r: Ctx => A) def flatMap[B](f: A => Ctx => B): Ctx => B = ctx => f(r(ctx))(ctx) def pure[A](x: A): Ctx => A = ctx => x @@ -115,6 +115,6 @@ object Test extends App { fs.foldLeft(summon[Monad[F]].pure(x))((x: F[T], f: T => T) => if true then summon[Monad[F]].map(x)(f) else if true then x.map(f) - else x.map[T, T](f) + else x.map[T](f) ) } \ No newline at end of file diff --git a/tests/run/singleton-ops-flags.scala b/tests/run/singleton-ops-flags.scala index 66fcc69e21b3..8e2cda6a38c7 100644 --- a/tests/run/singleton-ops-flags.scala +++ b/tests/run/singleton-ops-flags.scala @@ -56,8 +56,9 @@ package example { extension (s: EmptyFlagSet) def next: SingletonFlagSet[0] = 1 extension [N <: Int: ValueOf](s: SingletonFlagSet[N]) def next: SingletonFlagSet[S[N]] = valueOf[N].succ.shift extension [N <: Int: ValueOf](s: SingletonFlagSet[N]) def idx: N = valueOf[N] - extension [N <: Int](s: FlagSet) def toSingletonSets: SingletonSets[N] = s - extension (s: FlagSet) def | (t: FlagSet): FlagSet = s | t + extension (s: FlagSet) + def toSingletonSets[N <: Int]: SingletonSets[N] = s + def | (t: FlagSet): FlagSet = s | t extension [A, N <: Int: ValueOf](ss: SingletonSets[N]) def map(f: [t <: Int] => (s: SingletonFlagSet[t]) => A): List[A] = val maxFlag = valueOf[N]