diff --git a/compiler/src/dotty/tools/dotc/ast/Desugar.scala b/compiler/src/dotty/tools/dotc/ast/Desugar.scala index d6b69420596b..20ac961f32ff 100644 --- a/compiler/src/dotty/tools/dotc/ast/Desugar.scala +++ b/compiler/src/dotty/tools/dotc/ast/Desugar.scala @@ -5,7 +5,7 @@ package ast import core._ import util.Spans._, Types._, Contexts._, Constants._, Names._, NameOps._, Flags._ import Symbols._, StdNames._, Trees._ -import Decorators._, transform.SymUtils._ +import Decorators.{given _}, transform.SymUtils._ import NameKinds.{UniqueName, EvidenceParamName, DefaultGetterName} import typer.{FrontEnd, Namer} import util.{Property, SourceFile, SourcePosition} @@ -268,8 +268,10 @@ object desugar { Nil } - def normalizedVparamss = meth1.vparamss.map(_.map(vparam => - cpy.ValDef(vparam)(rhs = EmptyTree))) + def normalizedVparamss = meth1.vparamss.nestedMapConserve(vparam => + if vparam.rhs.isEmpty then vparam + else cpy.ValDef(vparam)(rhs = EmptyTree).withMods(vparam.mods | HasDefault) + ) def defaultGetters(vparamss: List[List[ValDef]], n: Int): List[DefDef] = vparamss match { case (vparam :: vparams) :: vparamss1 => @@ -277,7 +279,7 @@ object desugar { DefDef( name = DefaultGetterName(methName, n), tparams = meth.tparams.map(tparam => dropContextBounds(toDefParam(tparam, keepAnnotations = true))), - vparamss = takeUpTo(normalizedVparamss.nestedMap(toDefParam(_, keepAnnotations = true)), n), + vparamss = takeUpTo(normalizedVparamss.nestedMap(toDefParam(_, keepAnnotations = true, keepDefault = false)), n), tpt = TypeTree(), rhs = vparam.rhs ) @@ -294,7 +296,6 @@ object desugar { if (defGetters.isEmpty) meth1 else { val meth2 = cpy.DefDef(meth1)(vparamss = normalizedVparamss) - .withMods(meth1.mods | DefaultParameterized) Thicket(meth2 :: defGetters) } } @@ -386,10 +387,11 @@ object desugar { if (!keepAnnotations) mods = mods.withAnnotations(Nil) tparam.withMods(mods & EmptyFlags | Param) } - private def toDefParam(vparam: ValDef, keepAnnotations: Boolean): ValDef = { + private def toDefParam(vparam: ValDef, keepAnnotations: Boolean, keepDefault: Boolean): ValDef = { var mods = vparam.rawMods if (!keepAnnotations) mods = mods.withAnnotations(Nil) - vparam.withMods(mods & (GivenOrImplicit | Erased) | Param) + val hasDefault = if keepDefault then HasDefault else EmptyFlags + vparam.withMods(mods & (GivenOrImplicit | Erased | hasDefault) | Param) } /** The expansion of a class definition. See inline comments for what is involved */ @@ -463,7 +465,7 @@ object desugar { ctx.error(CaseClassMissingNonImplicitParamList(cdef), namePos) ListOfNil } - else originalVparamss.nestedMap(toDefParam(_, keepAnnotations = false)) + else originalVparamss.nestedMap(toDefParam(_, keepAnnotations = false, keepDefault = true)) val constr = cpy.DefDef(constr1)(tparams = constrTparams, vparamss = constrVparamss) val (normalizedBody, enumCases, enumCompanionRef) = { @@ -475,7 +477,7 @@ object desugar { defDef( addEvidenceParams( cpy.DefDef(ddef)(tparams = constrTparams ++ ddef.tparams), - evidenceParams(constr1).map(toDefParam(_, keepAnnotations = false))))) + evidenceParams(constr1).map(toDefParam(_, keepAnnotations = false, keepDefault = false))))) case stat => stat } @@ -501,16 +503,11 @@ object desugar { // Annotations are dropped from the constructor parameters but should be // preserved in all derived parameters. - val derivedTparams = { - val impliedTparamsIt = impliedTparams.iterator - constrTparams.map(tparam => derivedTypeParam(tparam) - .withAnnotations(impliedTparamsIt.next().mods.annotations)) - } - val derivedVparamss = { - val constrVparamsIt = constrVparamss.iterator.flatten - constrVparamss.nestedMap(vparam => derivedTermParam(vparam) - .withAnnotations(constrVparamsIt.next().mods.annotations)) - } + val derivedTparams = + constrTparams.zipWithConserve(impliedTparams)((tparam, impliedParam) => + derivedTypeParam(tparam).withAnnotations(impliedParam.mods.annotations)) + val derivedVparamss = + constrVparamss.nestedMap(vparam => derivedTermParam(vparam)) val arity = constrVparamss.head.length @@ -712,13 +709,16 @@ object desugar { val applyMeths = if (mods.is(Abstract)) Nil else { - val copiedFlagsMask = DefaultParameterized | (copiedAccessFlags & Private) + val copiedFlagsMask = copiedAccessFlags & Private val appMods = { val mods = Modifiers(Synthetic | constr1.mods.flags & copiedFlagsMask) if (restrictedAccess) mods.withPrivateWithin(constr1.mods.privateWithin) else mods } - val app = DefDef(nme.apply, derivedTparams, derivedVparamss, applyResultTpt, widenedCreatorExpr) + val appParamss = + derivedVparamss.nestedZipWithConserve(constrVparamss)((ap, cp) => + ap.withMods(ap.mods | (cp.mods.flags & HasDefault))) + val app = DefDef(nme.apply, derivedTparams, appParamss, applyResultTpt, widenedCreatorExpr) .withMods(appMods) app :: widenDefs } diff --git a/compiler/src/dotty/tools/dotc/ast/TreeTypeMap.scala b/compiler/src/dotty/tools/dotc/ast/TreeTypeMap.scala index 9aebea9bd594..7dbb62e43680 100644 --- a/compiler/src/dotty/tools/dotc/ast/TreeTypeMap.scala +++ b/compiler/src/dotty/tools/dotc/ast/TreeTypeMap.scala @@ -94,6 +94,7 @@ class TreeTypeMap( val (tmap1, tparams1) = transformDefs(ddef.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) res.symbol.transformAnnotations { case ann: BodyAnnotation => ann.derivedAnnotation(transform(ann.tree)) case ann => ann diff --git a/compiler/src/dotty/tools/dotc/ast/tpd.scala b/compiler/src/dotty/tools/dotc/ast/tpd.scala index e6ca5adc1e5f..d40314f9fdb7 100644 --- a/compiler/src/dotty/tools/dotc/ast/tpd.scala +++ b/compiler/src/dotty/tools/dotc/ast/tpd.scala @@ -9,7 +9,7 @@ import transform.TypeUtils._ import core._ import util.Spans._, Types._, Contexts._, Constants._, Names._, Flags._, NameOps._ import Symbols._, StdNames._, Annotations._, Trees._, Symbols._ -import Decorators._, DenotTransformers._ +import Decorators.{given _}, DenotTransformers._ import collection.{immutable, mutable} import util.{Property, SourceFile, NoSource} import NameKinds.{TempResultName, OuterSelectName} @@ -208,6 +208,7 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo { def DefDef(sym: TermSymbol, tparams: List[TypeSymbol], vparamss: List[List[TermSymbol]], resultType: Type, rhs: Tree)(implicit ctx: Context): DefDef = + sym.setParamss(tparams, vparamss) ta.assignType( untpd.DefDef( sym.name, @@ -223,15 +224,27 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo { def DefDef(sym: TermSymbol, rhsFn: List[List[Tree]] => Tree)(implicit ctx: 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 + * 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)(implicit ctx: Context): DefDef = { - val (tparams, mtp) = sym.info match { + + val (tparams, existingParamss, mtp) = sym.info match { case tp: PolyType => - val tparams = ctx.newTypeParams(sym, tp.paramNames, EmptyFlags, tp.instantiateParamInfos(_)) - (tparams, tp.instantiate(tparams map (_.typeRef))) - case tp => (Nil, tp) + val (tparams, existingParamss) = sym.rawParamss match + case tparams :: vparamss => + assert(tparams.hasSameLengthAs(tp.paramNames) && tparams.head.isType) + (tparams.asInstanceOf[List[TypeSymbol]], vparamss) + case _ => + (ctx.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): (List[List[TermSymbol]], Type) = tp match { + def valueParamss(tp: Type, existingParamss: List[List[Symbol]]): (List[List[TermSymbol]], Type) = tp match { case tp: MethodType => val isParamDependent = tp.isParamDependent val previousParamRefs = if (isParamDependent) mutable.ListBuffer[TermRef]() else null @@ -254,14 +267,23 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo { makeSym(origInfo) } - val params = tp.paramNames.lazyZip(tp.paramInfos).map(valueParam) - val (paramss, rtp) = valueParamss(tp.instantiate(params map (_.termRef))) + val (params, existingParamss1) = + if tp.paramInfos.isEmpty then (Nil, existingParamss) + else existingParamss match + case vparams :: existingParamss1 => + assert(vparams.hasSameLengthAs(tp.paramNames) && vparams.head.isTerm) + (vparams.asInstanceOf[List[TermSymbol]], existingParamss1) + case _ => + (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) + 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)) } diff --git a/compiler/src/dotty/tools/dotc/core/Decorators.scala b/compiler/src/dotty/tools/dotc/core/Decorators.scala index ee6f06b37059..e4c4705b1231 100644 --- a/compiler/src/dotty/tools/dotc/core/Decorators.scala +++ b/compiler/src/dotty/tools/dotc/core/Decorators.scala @@ -155,10 +155,13 @@ object Decorators { def & (ys: List[T]): List[T] = xs filter (ys contains _) } - implicit class ListOfListDecorator[T](val xss: List[List[T]]) extends AnyVal { - def nestedMap[U](f: T => U): List[List[U]] = xss map (_ map f) - def nestedMapconserve[U](f: T => U): List[List[U]] = xss mapconserve (_ mapconserve f) - } + extension ListOfListDecorator on [T, U](xss: List[List[T]]): + def nestedMap(f: T => U): List[List[U]] = + xss.map(_.map(f)) + def nestedMapConserve(f: T => U): List[List[U]] = + xss.mapconserve(_.mapconserve(f)) + def nestedZipWithConserve(yss: List[List[U]])(f: (T, U) => T): List[List[T]] = + xss.zipWithConserve(yss)((xs, ys) => xs.zipWithConserve(ys)(f)) implicit class TextToString(val text: Text) extends AnyVal { def show(implicit ctx: Context): String = text.mkString(ctx.settings.pageWidth.value, ctx.settings.printLines.value) diff --git a/compiler/src/dotty/tools/dotc/core/Flags.scala b/compiler/src/dotty/tools/dotc/core/Flags.scala index 50002e5e2fea..1bb664b63673 100644 --- a/compiler/src/dotty/tools/dotc/core/Flags.scala +++ b/compiler/src/dotty/tools/dotc/core/Flags.scala @@ -299,7 +299,7 @@ object Flags { val (SuperParamAliasOrScala2x @ _, SuperParamAlias @ _, Scala2x @ _) = newFlags(26, "", "") /** A method that has default params */ - val (_, DefaultParameterized @ _, _) = newFlags(27, "") + val (_, HasDefault @ _, _) = newFlags(27, "") /** An extension method, or a collective extension instance */ val (_, Extension @ _, _) = newFlags(28, "") @@ -391,18 +391,18 @@ object Flags { /** Translation of Scala2's EXPANDEDNAME flag. This flag is never stored in * symbols, is only used locally when reading the flags of a Scala2 symbol. - * It's therefore safe to share the code with `InheritedDefaultParams` because + * It's therefore safe to share the code with `HasDefaultParams` because * the latter is never present in Scala2 unpickle info. * / - * A method that is known to have inherited default parameters + * A method that is known to have (defined or inherited) default parameters */ - val (Scala2ExpandedName @ _, InheritedDefaultParams @ _, _) = newFlags(59, "") + val (Scala2ExpandedName @ _, HasDefaultParams @ _, _) = newFlags(59, "") /** A method that is known to have no default parameters * / * A type symbol with provisional empty bounds */ - val (_, NoDefaultParams @ _, Provisional @ _) = newFlags(60, "", "") + val (_, NoDefaultParams @ _, Provisional @ _) = newFlags(60, "", "") /** A denotation that is valid in all run-ids */ val (Permanent @ _, _, _) = newFlags(61, "") @@ -525,8 +525,7 @@ object Flags { val EnumCase: FlagSet = Case | Enum val CovariantLocal: FlagSet = Covariant | Local // A covariant type parameter val ContravariantLocal: FlagSet = Contravariant | Local // A contravariant type parameter - val HasDefaultParamsFlags: FlagSet = DefaultParameterized | InheritedDefaultParams // Has defined or inherited default parameters - val DefaultParameter: FlagSet = DefaultParameterized | Param // A Scala 2x default parameter + val DefaultParameter: FlagSet = HasDefault | Param // A Scala 2x default parameter val DeferredOrLazy: FlagSet = Deferred | Lazy val DeferredOrLazyOrMethod: FlagSet = Deferred | Lazy | Method val DeferredOrTermParamOrAccessor: FlagSet = Deferred | ParamAccessor | TermParam // term symbols without right-hand sides @@ -576,6 +575,7 @@ object Flags { val SyntheticGivenMethod: FlagSet = Synthetic | Given | Method val SyntheticModule: FlagSet = Synthetic | Module val SyntheticOpaque: FlagSet = Synthetic | Opaque + val SyntheticParam: FlagSet = Synthetic | Param val SyntheticTermParam: FlagSet = Synthetic | TermParam val SyntheticTypeParam: FlagSet = Synthetic | TypeParam } diff --git a/compiler/src/dotty/tools/dotc/core/SymDenotations.scala b/compiler/src/dotty/tools/dotc/core/SymDenotations.scala index 557d8cbe36d5..ee5cc6042c45 100644 --- a/compiler/src/dotty/tools/dotc/core/SymDenotations.scala +++ b/compiler/src/dotty/tools/dotc/core/SymDenotations.scala @@ -11,7 +11,7 @@ import Scopes.Scope import dotty.tools.io.AbstractFile import Decorators.SymbolIteratorDecorator import ast._ -import ast.Trees.{LambdaTypeTree, TypeBoundsTree} +import ast.Trees.{LambdaTypeTree, TypeBoundsTree, ValDef, TypeDef} import Trees.Literal import Variances.Variance import annotation.tailrec @@ -150,6 +150,7 @@ object SymDenotations { private var myFlags: FlagSet = adaptFlags(initFlags) private var myPrivateWithin: Symbol = initPrivateWithin private var myAnnotations: List[Annotation] = Nil + private var myParamss: List[List[Symbol]] = Nil /** The owner of the symbol; overridden in NoDenotation */ def owner: Symbol = maybeOwner @@ -372,6 +373,59 @@ object SymDenotations { case Nil => Nil } + /** If this is a method, the parameter symbols, by section. + * Both type and value parameters are included. Empty sections are skipped. + */ + final def rawParamss: List[List[Symbol]] = myParamss + final def rawParamss_=(pss: List[List[Symbol]]): Unit = + myParamss = pss + + final def setParamss(tparams: List[Symbol], vparamss: List[List[Symbol]])(using Context): Unit = + rawParamss = (if tparams.isEmpty then vparamss else 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))) + + /** A pair consistsing of type paremeter symbols and value parameter symbol lists + * of this method definition, or (Nil, Nil) for other symbols. + * Makes use of `rawParamss` when present, or constructs fresh parameter symbols otherwise. + * This method can be allocation-heavy. + */ + final def paramSymss(using ctx: Context): (List[TypeSymbol], List[List[TermSymbol]]) = + + def recurWithParamss(info: Type, paramss: List[List[Symbol]]): List[List[Symbol]] = + info match + case info: LambdaType => + if info.paramNames.isEmpty then Nil :: recurWithParamss(info.resType, paramss) + else paramss.head :: recurWithParamss(info.resType, paramss.tail) + case _ => + Nil + + def recurWithoutParamss(info: Type): List[List[Symbol]] = info match + case info: LambdaType => + val params = info.paramNames.lazyZip(info.paramInfos).map((pname, ptype) => + ctx.newSymbol(symbol, pname, SyntheticParam, ptype)) + val prefs = params.map(_.namedType) + for param <- params do + param.info = param.info.substParams(info, prefs) + params :: recurWithoutParamss(info.instantiate(prefs)) + case _ => + Nil + + try + val allParamss = + if rawParamss.isEmpty then recurWithoutParamss(info) + else recurWithParamss(info, rawParamss) + val result = info match + case info: PolyType => (allParamss.head, allParamss.tail) + case _ => (Nil, allParamss) + result.asInstanceOf[(List[TypeSymbol], List[List[TermSymbol]])] + catch case NonFatal(ex) => + println(i"paramSymss failure for $symbol, $info, $rawParamss") + throw ex + end paramSymss + /** The denotation is completed: info is not a lazy type and attributes have defined values */ final def isCompleted: Boolean = !myInfo.isInstanceOf[LazyType] @@ -916,15 +970,19 @@ object SymDenotations { def isAsConcrete(that: Symbol)(implicit ctx: Context): Boolean = !this.is(Deferred) || that.is(Deferred) - /** Does this symbol have defined or inherited default parameters? */ + /** Does this symbol have defined or inherited default parameters? + * Default parameters are recognized until erasure. + */ def hasDefaultParams(implicit ctx: Context): Boolean = - if (this.isOneOf(HasDefaultParamsFlags)) true - else if (this.is(NoDefaultParams)) false - else { - val result = allOverriddenSymbols exists (_.hasDefaultParams) - setFlag(if (result) InheritedDefaultParams else NoDefaultParams) + if ctx.erasedTypes then false + else if is(HasDefaultParams) then true + else if is(NoDefaultParams) then false + else + val result = + rawParamss.exists(_.exists(_.is(HasDefault))) + || allOverriddenSymbols.exists(_.hasDefaultParams) + setFlag(if result then HasDefaultParams else NoDefaultParams) result - } /** Symbol is an owner that would be skipped by effectiveOwner. Skipped are * - package objects @@ -1450,7 +1508,9 @@ object SymDenotations { initFlags: FlagSet = UndefinedFlags, info: Type = null, privateWithin: Symbol = null, - annotations: List[Annotation] = null)(implicit ctx: Context): SymDenotation = { + annotations: List[Annotation] = null, + rawParamss: List[List[Symbol]] = null)( + using ctx: Context): SymDenotation = { // simulate default parameters, while also passing implicit context ctx to the default values val initFlags1 = (if (initFlags != UndefinedFlags) initFlags else this.flags) val info1 = if (info != null) info else this.info @@ -1458,8 +1518,10 @@ object SymDenotations { assert(ctx.phase.changesParents, i"undeclared parent change at ${ctx.phase} for $this, was: $info, now: $info1") val privateWithin1 = if (privateWithin != null) privateWithin else this.privateWithin val annotations1 = if (annotations != null) annotations else this.annotations + val rawParamss1 = if rawParamss != null then rawParamss else this.rawParamss val d = ctx.SymDenotation(symbol, owner, name, initFlags1, info1, privateWithin1) d.annotations = annotations1 + d.rawParamss = rawParamss1 d.registeredCompanion = registeredCompanion d } diff --git a/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala b/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala index 7703108f5d0b..0e13d3e049a7 100644 --- a/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala +++ b/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala @@ -678,7 +678,7 @@ class TreePickler(pickler: TastyPickler) { if (flags.is(Mutable)) writeModTag(MUTABLE) if (flags.is(Accessor)) writeModTag(FIELDaccessor) if (flags.is(CaseAccessor)) writeModTag(CASEaccessor) - if (flags.is(DefaultParameterized)) writeModTag(DEFAULTparameterized) + if (flags.is(HasDefault)) writeModTag(HASDEFAULT) if (flags.is(StableRealizable)) writeModTag(STABLE) if (flags.is(Extension)) writeModTag(EXTENSION) if (flags.is(ParamAccessor)) writeModTag(PARAMsetter) diff --git a/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala b/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala index a25d1563f602..1bf6c9aee4bd 100644 --- a/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala +++ b/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala @@ -23,7 +23,7 @@ import util.Spans._ import util.SourceFile import ast.{TreeTypeMap, Trees, tpd, untpd} import Trees._ -import Decorators._ +import Decorators.{given _} import transform.SymUtils._ import dotty.tools.tasty.{TastyBuffer, TastyReader} @@ -639,7 +639,7 @@ class TreeUnpickler(reader: TastyReader, case COVARIANT => addFlag(Covariant) case CONTRAVARIANT => addFlag(Contravariant) case SCALA2X => addFlag(Scala2x) - case DEFAULTparameterized => addFlag(DefaultParameterized) + case HASDEFAULT => addFlag(HasDefault) case STABLE => addFlag(StableRealizable) case EXTENSION => addFlag(Extension) case GIVEN => addFlag(Given) @@ -786,9 +786,10 @@ class TreeUnpickler(reader: TastyReader, ta.assignType(untpd.ValDef(sym.name.asTermName, tpt, readRhs(localCtx)), sym) def DefDef(tparams: List[TypeDef], vparamss: List[List[ValDef]], tpt: Tree) = - ta.assignType( - untpd.DefDef(sym.name.asTermName, tparams, vparamss, tpt, readRhs(localCtx)), - sym) + sym.setParamssFromDefs(tparams, vparamss) + ta.assignType( + untpd.DefDef(sym.name.asTermName, tparams, vparamss, tpt, readRhs(localCtx)), + sym) def TypeDef(rhs: Tree) = ta.assignType(untpd.TypeDef(sym.name.asTypeName, rhs), sym) diff --git a/compiler/src/dotty/tools/dotc/core/unpickleScala2/PickleBuffer.scala b/compiler/src/dotty/tools/dotc/core/unpickleScala2/PickleBuffer.scala index 6d06a74188af..dfe10911c059 100644 --- a/compiler/src/dotty/tools/dotc/core/unpickleScala2/PickleBuffer.scala +++ b/compiler/src/dotty/tools/dotc/core/unpickleScala2/PickleBuffer.scala @@ -223,7 +223,7 @@ object PickleBuffer { STABLE -> StableRealizable, STATIC -> JavaStatic, CASEACCESSOR -> CaseAccessor, - DEFAULTPARAM -> (DefaultParameterized, Trait), + DEFAULTPARAM -> (HasDefault, Trait), BRIDGE -> Bridge, ACCESSOR -> Accessor, SUPERACCESSOR -> Scala2SuperAccessor, diff --git a/compiler/src/dotty/tools/dotc/core/unpickleScala2/Scala2Unpickler.scala b/compiler/src/dotty/tools/dotc/core/unpickleScala2/Scala2Unpickler.scala index d86e0c1490b5..c374bfc0acb4 100644 --- a/compiler/src/dotty/tools/dotc/core/unpickleScala2/Scala2Unpickler.scala +++ b/compiler/src/dotty/tools/dotc/core/unpickleScala2/Scala2Unpickler.scala @@ -176,6 +176,9 @@ class Scala2Unpickler(bytes: Array[Byte], classRoot: ClassDenotation, moduleClas /** A map from symbols to their associated `decls` scopes */ private val symScopes = mutable.AnyRefMap[Symbol, Scope]() + /** A mapping from method types to the parameters used in constructing them */ + private val paramsOfMethodType = new java.util.IdentityHashMap[MethodType, List[Symbol]] + protected def errorBadSignature(msg: String, original: Option[RuntimeException] = None)(implicit ctx: Context): Nothing = { val ex = new BadSignature( i"""error reading Scala signature of $classRoot from $source: @@ -449,12 +452,6 @@ class Scala2Unpickler(bytes: Array[Byte], classRoot: ClassDenotation, moduleClas val owner = readSymbolRef() var flags = unpickleScalaFlags(readLongNat(), name.isTypeName) - if (flags.isAllOf(DefaultParameter)) { - // DefaultParameterized flag now on method, not parameter - //assert(flags.is(Param), s"$name0 in $owner") - flags = flags &~ DefaultParameterized - owner.setFlag(DefaultParameterized) - } name = name.adjustIfModuleClass(flags) if (flags.is(Method)) @@ -561,6 +558,15 @@ class Scala2Unpickler(bytes: Array[Byte], classRoot: ClassDenotation, moduleClas class LocalUnpickler extends LazyType { def startCoord(denot: SymDenotation): Coord = denot.symbol.coord + + def paramssOfType(tp: Type): List[List[Symbol]] = tp match + case TempPolyType(tparams, restpe) => tparams :: paramssOfType(restpe) + case mt: MethodType => + val params = paramsOfMethodType.remove(mt) + val rest = paramssOfType(mt.resType) + if params == null then rest else params :: rest + case _ => Nil + def complete(denot: SymDenotation)(implicit ctx: Context): Unit = try { def parseToCompletion(denot: SymDenotation)(implicit ctx: Context) = { val tag = readByte() @@ -575,6 +581,7 @@ class Scala2Unpickler(bytes: Array[Byte], classRoot: ClassDenotation, moduleClas // println("reading type for " + denot) // !!! DEBUG val tp = at(inforef, () => readType()(ctx)) + if denot.is(Method) then denot.rawParamss = paramssOfType(tp) denot match { case denot: ClassDenotation if !isRefinementClass(denot.symbol) => @@ -792,12 +799,16 @@ class Scala2Unpickler(bytes: Array[Byte], classRoot: ClassDenotation, moduleClas val params = until(end, () => readSymbolRef()) val maker = MethodType.companion( isImplicit = tag == IMPLICITMETHODtpe || params.nonEmpty && params.head.is(Implicit)) - maker.fromSymbols(params, restpe) + val result = maker.fromSymbols(params, restpe) + if params.nonEmpty then paramsOfMethodType.put(result, params) + result case POLYtpe => val restpe = readTypeRef() val typeParams = until(end, () => readSymbolRef()) - if (typeParams.nonEmpty) TempPolyType(typeParams.asInstanceOf[List[TypeSymbol]], restpe.widenExpr) - else ExprType(restpe) + if typeParams.nonEmpty then + TempPolyType(typeParams.asInstanceOf[List[TypeSymbol]], restpe.widenExpr) + else + ExprType(restpe) case EXISTENTIALtpe => val restpe = readTypeRef() val boundSyms = until(end, () => readSymbolRef()) diff --git a/compiler/src/dotty/tools/dotc/reporting/Message.scala b/compiler/src/dotty/tools/dotc/reporting/Message.scala index 25343fb26419..a4697de85471 100644 --- a/compiler/src/dotty/tools/dotc/reporting/Message.scala +++ b/compiler/src/dotty/tools/dotc/reporting/Message.scala @@ -89,7 +89,7 @@ abstract class Message(val errorId: ErrorMessageID) { self => * they look weird and are normally follow-up errors to something that was * diagnosed before. */ - def isNonSensical: Boolean = { message; myIsNonSensical } + def isNonSensical: Boolean = { message; myIsNonSensical } /** The implicit `Context` in messages is a large thing that we don't want * persisted. This method gets around that by duplicating the message, diff --git a/compiler/src/dotty/tools/dotc/sbt/ExtractAPI.scala b/compiler/src/dotty/tools/dotc/sbt/ExtractAPI.scala index 12f8a150f1c5..c705ade7cc92 100644 --- a/compiler/src/dotty/tools/dotc/sbt/ExtractAPI.scala +++ b/compiler/src/dotty/tools/dotc/sbt/ExtractAPI.scala @@ -337,28 +337,17 @@ private class ExtractAPICollector(implicit val ctx: Context) extends ThunkHolder } def apiDef(sym: TermSymbol): api.Def = { - def paramLists(t: Type, start: Int = 0): List[api.ParameterList] = t match { + def paramLists(t: Type, paramss: List[List[Symbol]]): List[api.ParameterList] = t match { case pt: TypeLambda => - assert(start == 0) - paramLists(pt.resultType) + paramLists(pt.resultType, paramss) case mt @ MethodTpe(pnames, ptypes, restpe) => - // TODO: We shouldn't have to work so hard to find the default parameters - // of a method, Dotty should expose a convenience method for that, see #1143 - val defaults = - if (sym.is(DefaultParameterized)) { - val qual = - if (sym.isClassConstructor) - sym.owner.companionModule // default getters for class constructors are found in the companion object - else - sym.owner - pnames.indices.map(i => - qual.info.member(DefaultGetterName(sym.name, start + i)).exists) - } else - pnames.indices.map(Function.const(false)) - val params = pnames.lazyZip(ptypes).lazyZip(defaults).map((pname, ptype, isDefault) => - api.MethodParameter.of(pname.toString, apiType(ptype), - isDefault, api.ParameterModifier.Plain)) - api.ParameterList.of(params.toArray, mt.isImplicitMethod) :: paramLists(restpe, params.length) + assert(paramss.nonEmpty && paramss.head.hasSameLengthAs(pnames), + i"mismatch for $sym, ${sym.info}, ${sym.paramSymss}") + val apiParams = paramss.head.lazyZip(ptypes).map((param, ptype) => + api.MethodParameter.of(param.name.toString, apiType(ptype), + param.is(HasDefault), api.ParameterModifier.Plain)) + api.ParameterList.of(apiParams.toArray, mt.isImplicitMethod) + :: paramLists(restpe, paramss.tail) case _ => Nil } @@ -370,7 +359,7 @@ private class ExtractAPICollector(implicit val ctx: Context) extends ThunkHolder case _ => Nil } - val vparamss = paramLists(sym.info) + val vparamss = paramLists(sym.info, sym.paramSymss._2) val retTp = sym.info.finalResultType.widenExpr api.Def.of(sym.name.toString, apiAccess(sym), apiModifiers(sym), diff --git a/compiler/src/dotty/tools/dotc/tastyreflect/ReflectionCompilerInterface.scala b/compiler/src/dotty/tools/dotc/tastyreflect/ReflectionCompilerInterface.scala index 0c3184d723f7..6098d82931ec 100644 --- a/compiler/src/dotty/tools/dotc/tastyreflect/ReflectionCompilerInterface.scala +++ b/compiler/src/dotty/tools/dotc/tastyreflect/ReflectionCompilerInterface.scala @@ -1749,6 +1749,9 @@ class ReflectionCompilerInterface(val rootContext: core.Contexts.Context) extend private def isMethod(sym: Symbol)(using ctx: Context): Boolean = sym.isTerm && sym.is(Flags.Method) && !sym.isConstructor + def Symbol_paramSymss(self: Symbol)(using ctx: Context): (List[Symbol], List[List[Symbol]]) = + self.paramSymss + def Symbol_caseFields(self: Symbol)(using ctx: Context): List[Symbol] = if (!self.isClass) Nil else self.asClass.paramAccessors.collect { @@ -1852,7 +1855,7 @@ class ReflectionCompilerInterface(val rootContext: core.Contexts.Context) extend def Flags_Covariant: Flags = core.Flags.Covariant def Flags_Contravariant: Flags = core.Flags.Contravariant def Flags_Scala2X: Flags = core.Flags.Scala2x - def Flags_DefaultParameterized: Flags = core.Flags.DefaultParameterized + def Flags_HasDefault: Flags = core.Flags.HasDefault def Flags_StableRealizable: Flags = core.Flags.StableRealizable def Flags_Param: Flags = core.Flags.Param def Flags_ParamAccessor: Flags = core.Flags.ParamAccessor diff --git a/compiler/src/dotty/tools/dotc/transform/CompleteJavaEnums.scala b/compiler/src/dotty/tools/dotc/transform/CompleteJavaEnums.scala index eed261f62115..49ad48e3d3ee 100644 --- a/compiler/src/dotty/tools/dotc/transform/CompleteJavaEnums.scala +++ b/compiler/src/dotty/tools/dotc/transform/CompleteJavaEnums.scala @@ -97,8 +97,10 @@ class CompleteJavaEnums extends MiniPhase with InfoTransformer { thisPhase => override def transformDefDef(tree: DefDef)(implicit ctx: Context): DefDef = { val sym = tree.symbol if (sym.isConstructor && sym.owner.derivesFromJavaEnum) - cpy.DefDef(tree)( + val tree1 = cpy.DefDef(tree)( vparamss = tree.vparamss.init :+ (tree.vparamss.last ++ addedParams(sym, Param))) + sym.setParamssFromDefs(tree1.tparams, tree1.vparamss) + tree1 else if (sym.name == nme.DOLLAR_NEW && sym.owner.linkedClass.derivesFromJavaEnum) { val Block((tdef @ TypeDef(tpnme.ANON_CLASS, templ: Template)) :: Nil, call) = tree.rhs val args = tree.vparamss.last.takeRight(2).map(param => ref(param.symbol)).reverse diff --git a/compiler/src/dotty/tools/dotc/transform/Erasure.scala b/compiler/src/dotty/tools/dotc/transform/Erasure.scala index 25429b0dc4fc..0730ac5b3b7d 100644 --- a/compiler/src/dotty/tools/dotc/transform/Erasure.scala +++ b/compiler/src/dotty/tools/dotc/transform/Erasure.scala @@ -82,7 +82,7 @@ class Erasure extends Phase with DenotTransformer { val oldFlags = ref.flags var newFlags = if (oldSymbol.is(Flags.TermParam) && isCompacted(oldSymbol.owner)) oldFlags &~ Flags.Param - else oldFlags &~ Flags.HasDefaultParamsFlags // HasDefaultParamsFlags needs to be dropped because overriding might become overloading + else oldFlags val oldAnnotations = ref.annotations var newAnnotations = oldAnnotations if oldSymbol.isRetainedInlineMethod then diff --git a/compiler/src/dotty/tools/dotc/transform/FullParameterization.scala b/compiler/src/dotty/tools/dotc/transform/FullParameterization.scala index be4bb6d0b346..46d3180e1b94 100644 --- a/compiler/src/dotty/tools/dotc/transform/FullParameterization.scala +++ b/compiler/src/dotty/tools/dotc/transform/FullParameterization.scala @@ -5,7 +5,7 @@ import core._ import Types._ import Contexts._ import Symbols._ -import Decorators._ +import Decorators.{given _} import TypeUtils._ import StdNames.nme import NameOps._ diff --git a/compiler/src/dotty/tools/dotc/transform/TreeChecker.scala b/compiler/src/dotty/tools/dotc/transform/TreeChecker.scala index 86e9996f7401..22b5e6d0156a 100644 --- a/compiler/src/dotty/tools/dotc/transform/TreeChecker.scala +++ b/compiler/src/dotty/tools/dotc/transform/TreeChecker.scala @@ -422,6 +422,14 @@ class TreeChecker extends Phase with SymTransformer { } override def typedDefDef(ddef: untpd.DefDef, sym: Symbol)(implicit ctx: Context): Tree = + def defParamss = + (ddef.tparams :: ddef.vparamss).filter(!_.isEmpty).map(_.map(_.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)) diff --git a/compiler/src/dotty/tools/dotc/typer/Checking.scala b/compiler/src/dotty/tools/dotc/typer/Checking.scala index bee426610d1b..014b2cb4e320 100644 --- a/compiler/src/dotty/tools/dotc/typer/Checking.scala +++ b/compiler/src/dotty/tools/dotc/typer/Checking.scala @@ -894,10 +894,9 @@ trait Checking { if (decl is Synthetic) doubleDefError(other, decl) else doubleDefError(decl, other) } - if (decl.isOneOf(HasDefaultParamsFlags) && other.isOneOf(HasDefaultParamsFlags)) { + if decl.hasDefaultParams && other.hasDefaultParams then ctx.error(em"two or more overloaded variants of $decl have default arguments", decl.sourcePos) - decl.resetFlag(HasDefaultParamsFlags) - } + decl.resetFlag(HasDefaultParams) } if (!excludeFromDoubleDeclCheck(decl)) seen(decl.name) = decl :: seen(decl.name) diff --git a/compiler/src/dotty/tools/dotc/typer/Namer.scala b/compiler/src/dotty/tools/dotc/typer/Namer.scala index 41a721e91c0e..1275628d080c 100644 --- a/compiler/src/dotty/tools/dotc/typer/Namer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Namer.scala @@ -5,7 +5,8 @@ package typer import core._ import ast._ import Trees._, StdNames._, Scopes._, Denotations._, Comments._ -import Contexts._, Symbols._, Types._, SymDenotations._, Names._, NameOps._, Flags._, Decorators._ +import Contexts._, Symbols._, Types._, SymDenotations._, Names._, NameOps._, Flags._ +import Decorators.{given _} import NameKinds.DefaultGetterName import TypeApplications.TypeParamInfo import ast.desugar, ast.desugar._ @@ -1553,6 +1554,7 @@ class Namer { typer: Typer => vparamss foreach completeParams def typeParams = tparams map symbolOfTree val termParamss = ctx.normalizeIfConstructor(vparamss.nestedMap(symbolOfTree), isConstructor) + sym.setParamss(typeParams, termParamss) def wrapMethType(restpe: Type): Type = { instantiateDependent(restpe, typeParams, termParamss) ctx.methodType(tparams map symbolOfTree, termParamss, restpe, isJava = ddef.mods.is(JavaDefined)) diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index 426f1487dcf8..26990a8192b3 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -19,7 +19,7 @@ import Names._ import NameOps._ import NameKinds._ import Flags._ -import Decorators._ +import Decorators.{given _} import ErrorReporting._ import Checking._ import Inferencing._ @@ -1706,8 +1706,8 @@ class Typer extends Namer } val DefDef(name, tparams, vparamss, tpt, _) = ddef completeAnnotations(ddef, sym) - val tparams1 = tparams mapconserve (typed(_).asInstanceOf[TypeDef]) - val vparamss1 = vparamss nestedMapconserve (typed(_).asInstanceOf[ValDef]) + val tparams1 = tparams.mapconserve(typed(_).asInstanceOf[TypeDef]) + val vparamss1 = vparamss.nestedMapConserve(typed(_).asInstanceOf[ValDef]) vparamss1.foreach(checkNoForwardDependencies) if (sym.isOneOf(GivenOrImplicit)) checkImplicitConversionDefOK(sym) val tpt1 = checkSimpleKinded(typedType(tpt)) diff --git a/library/src/scala/tasty/Reflection.scala b/library/src/scala/tasty/Reflection.scala index 02b56143f026..39726418f8ed 100644 --- a/library/src/scala/tasty/Reflection.scala +++ b/library/src/scala/tasty/Reflection.scala @@ -2223,6 +2223,12 @@ class Reflection(private[scala] val internal: CompilerInterface) { self => def methods(using ctx: Context): List[Symbol] = internal.Symbol_methods(sym) + /** A pair consistsing of type paremeter symbols and value parameter symbol lists + * of this method definition, or (Nil, Nil) for other symbols. + */ + def paramSymss(using ctx: Context): (List[Symbol], List[List[Symbol]]) = + internal.Symbol_paramSymss(sym) + /** Fields of a case class type -- only the ones declared in primary constructor */ def caseFields(using ctx: Context): List[Symbol] = internal.Symbol_caseFields(sym) @@ -2635,8 +2641,8 @@ class Reflection(private[scala] val internal: CompilerInterface) { self => /** Was this symbol imported from Scala2.x */ def Scala2X: Flags = internal.Flags_Scala2X - /** Is this symbol a method with default parameters */ - def DefaultParameterized: Flags = internal.Flags_DefaultParameterized + /** Is this symbol a parameter with a default value? */ + def HasDefault: Flags = internal.Flags_HasDefault /** Is this symbol member that is assumed to be stable and realizable */ def StableRealizable: Flags = internal.Flags_StableRealizable diff --git a/library/src/scala/tasty/reflect/CompilerInterface.scala b/library/src/scala/tasty/reflect/CompilerInterface.scala index 408f0fe84bd6..a97366711604 100644 --- a/library/src/scala/tasty/reflect/CompilerInterface.scala +++ b/library/src/scala/tasty/reflect/CompilerInterface.scala @@ -1305,6 +1305,11 @@ trait CompilerInterface { /** Get all non-private methods declared or inherited */ def Symbol_methods(self: Symbol)(using ctx: Context): List[Symbol] + /** A pair consistsing of type paremeter symbols and value parameter symbol lists + * of this method definition, or (Nil, Nil) for other symbols. + */ + def Symbol_paramSymss(self: Symbol)(using ctx: Context): (List[Symbol], List[List[Symbol]]) + /** Fields of a case class type -- only the ones declared in primary constructor */ def Symbol_caseFields(self: Symbol)(using ctx: Context): List[Symbol] @@ -1400,7 +1405,7 @@ trait CompilerInterface { def Flags_Covariant: Flags def Flags_Contravariant: Flags def Flags_Scala2X: Flags - def Flags_DefaultParameterized: Flags + def Flags_HasDefault: Flags def Flags_StableRealizable: Flags def Flags_Param: Flags def Flags_ParamAccessor: Flags diff --git a/library/src/scala/tasty/reflect/ExtractorsPrinter.scala b/library/src/scala/tasty/reflect/ExtractorsPrinter.scala index f33165ea0e6d..72f9a46b5cd5 100644 --- a/library/src/scala/tasty/reflect/ExtractorsPrinter.scala +++ b/library/src/scala/tasty/reflect/ExtractorsPrinter.scala @@ -43,7 +43,7 @@ class ExtractorsPrinter[R <: Reflection & Singleton](val tasty: R) extends Print if (flags.is(Flags.Covariant)) flagList += "Flags.Covariant" if (flags.is(Flags.Contravariant)) flagList += "Flags.Contravariant" if (flags.is(Flags.Scala2X)) flagList += "Flags.Scala2X" - if (flags.is(Flags.DefaultParameterized)) flagList += "Flags.DefaultParameterized" + if (flags.is(Flags.HasDefault)) flagList += "Flags.HasDefault" if (flags.is(Flags.StableRealizable)) flagList += "Flags.StableRealizable" if (flags.is(Flags.Param)) flagList += "Flags.Param" if (flags.is(Flags.ParamAccessor)) flagList += "Flags.ParamAccessor" diff --git a/library/src/scala/tasty/reflect/SourceCodePrinter.scala b/library/src/scala/tasty/reflect/SourceCodePrinter.scala index 4f331338dce7..655bd75b317e 100644 --- a/library/src/scala/tasty/reflect/SourceCodePrinter.scala +++ b/library/src/scala/tasty/reflect/SourceCodePrinter.scala @@ -48,7 +48,7 @@ class SourceCodePrinter[R <: Reflection & Singleton](val tasty: R)(syntaxHighlig if (flags.is(Flags.Covariant)) flagList += "covariant" if (flags.is(Flags.Contravariant)) flagList += "contravariant" if (flags.is(Flags.Scala2X)) flagList += "scala2x" - if (flags.is(Flags.DefaultParameterized)) flagList += "defaultParameterized" + if (flags.is(Flags.HasDefault)) flagList += "hasDefault" if (flags.is(Flags.StableRealizable)) flagList += "stableRealizable" if (flags.is(Flags.Param)) flagList += "param" if (flags.is(Flags.ParamAccessor)) flagList += "paramAccessor" diff --git a/tasty/src/dotty/tools/tasty/TastyFormat.scala b/tasty/src/dotty/tools/tasty/TastyFormat.scala index ae40e14022e5..4836c0412530 100644 --- a/tasty/src/dotty/tools/tasty/TastyFormat.scala +++ b/tasty/src/dotty/tools/tasty/TastyFormat.scala @@ -203,7 +203,7 @@ Standard-Section: "ASTs" TopLevelStat* COVARIANT -- A type parameter marked “+” CONTRAVARIANT -- A type parameter marked “-” SCALA2X -- Imported from Scala2.x - DEFAULTparameterized -- Method with default parameters (default arguments are separate methods with DEFAULTGETTER names) + HASDEFAULT -- Parameter with default arg; method with default parameters (default arguments are separate methods with DEFAULTGETTER names) STABLE -- Method that is assumed to be stable, i.e. its applications are legal paths EXTENSION -- An extension method PARAMsetter -- The setter part `x_=` of a var parameter `x` which itself is pickled as a PARAM @@ -348,7 +348,7 @@ object TastyFormat { final val COVARIANT = 28 final val CONTRAVARIANT = 29 final val SCALA2X = 30 - final val DEFAULTparameterized = 31 + final val HASDEFAULT = 31 final val STABLE = 32 final val MACRO = 33 final val ERASED = 34 @@ -525,7 +525,7 @@ object TastyFormat { | COVARIANT | CONTRAVARIANT | SCALA2X - | DEFAULTparameterized + | HASDEFAULT | STABLE | EXTENSION | PARAMsetter @@ -586,7 +586,7 @@ object TastyFormat { case COVARIANT => "COVARIANT" case CONTRAVARIANT => "CONTRAVARIANT" case SCALA2X => "SCALA2X" - case DEFAULTparameterized => "DEFAULTparameterized" + case HASDEFAULT => "HASDEFAULT" case STABLE => "STABLE" case EXTENSION => "EXTENSION" case GIVEN => "GIVEN" diff --git a/tests/neg/i3171.scala b/tests/neg/i3171.scala index 3034e020d61c..831ce5209653 100644 --- a/tests/neg/i3171.scala +++ b/tests/neg/i3171.scala @@ -5,6 +5,6 @@ object Test { } def test: Unit = { - new C()("1") // error: missing argument + new C()("1") } } diff --git a/tests/pos-with-compiler/tasty/definitions.scala b/tests/pos-with-compiler/tasty/definitions.scala index 0bc5f5c44b4c..5a45840dec84 100644 --- a/tests/pos-with-compiler/tasty/definitions.scala +++ b/tests/pos-with-compiler/tasty/definitions.scala @@ -249,7 +249,7 @@ object definitions { def isCovariant: Boolean // type parameter marked “+” def isContravariant: Boolean // type parameter marked “-” def isScala2X: Boolean // Imported from Scala2.x - def isDefaultParameterized: Boolean // Method with default parameters + def hasDefault: Boolean // Parameter with default def isStable: Boolean // Method that is assumed to be stable } diff --git a/tests/run-macros/paramSymss.check b/tests/run-macros/paramSymss.check new file mode 100644 index 000000000000..2ffa55da1f4a --- /dev/null +++ b/tests/run-macros/paramSymss.check @@ -0,0 +1,28 @@ +sym: Test$.a +tparams: List() +vparamss: List() + +sym: Test$.b +tparams: List() +vparamss: List(List(Test$._$i)) + +sym: Test$.c +tparams: List() +vparamss: List(List(Test$._$x), List(Test$._$y), List(Test$._$z)) + +sym: Test$.d +tparams: List(Test$._$T) +vparamss: List() + +sym: Test$.c +tparams: List(Test$._$T, Test$._$U) +vparamss: List(List(Test$._$x, Test$._$x2), List(Test$._$y), List(Test$._$z)) + +sym: Test. +tparams: List(Test._$T) +vparamss: List(List(Test._$a)) + +sym: Test. +tparams: List(Test._$T) +vparamss: List(List(Test._$a, Test._$b)) + diff --git a/tests/run-macros/paramSymss/Macro_1.scala b/tests/run-macros/paramSymss/Macro_1.scala new file mode 100644 index 000000000000..dc69e5d91c7b --- /dev/null +++ b/tests/run-macros/paramSymss/Macro_1.scala @@ -0,0 +1,14 @@ +import scala.quoted._ + +inline def showParamSyms(inline x: Any): String = + ${ showParamSymsExpr('x) } + +def showParamSymsExpr(using QuoteContext)(x: Expr[Any]): Expr[String] = + val '{ $y: Any } = x // Drop Inlined not to access the symbol + val sym = y.unseal.symbol + val (tparams, vparamss) = sym.paramSymss + Expr( + s"""sym: ${sym.show} + |tparams: ${tparams.map(_.show)} + |vparamss: ${vparamss.map(_.map(_.show))} + |""".stripMargin) diff --git a/tests/run-macros/paramSymss/Test_2.scala b/tests/run-macros/paramSymss/Test_2.scala new file mode 100644 index 000000000000..4e2b53060c8b --- /dev/null +++ b/tests/run-macros/paramSymss/Test_2.scala @@ -0,0 +1,20 @@ + +object Test { + def a: Unit = () + def b(i: Int): Unit = () + def c(x: Int)(y: String)(z: Boolean): Unit = () + def d[T]: Unit = () + def c[T, U](x: Int, x2: Int)(y: String)(z: Boolean): Unit = () + + def main(args: Array[String]) = + println(showParamSyms(a)) + println(showParamSyms(b(1))) + println(showParamSyms(c(2)("")(true))) + println(showParamSyms(d[Int])) + println(showParamSyms(c[Int, Char](3, 4)("")(true))) + println(showParamSyms(new Test(1))) + println(showParamSyms(new Test(1, 2))) +} + +class Test[T](a: T): + def this(a: Int, b: T) = this(b)