diff --git a/compiler/src/dotty/tools/dotc/ast/tpd.scala b/compiler/src/dotty/tools/dotc/ast/tpd.scala index 0c12eff2a0ae..2db0bd6de2d4 100644 --- a/compiler/src/dotty/tools/dotc/ast/tpd.scala +++ b/compiler/src/dotty/tools/dotc/ast/tpd.scala @@ -121,9 +121,9 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo { Closure(Nil, call, targetTpt)) } - /** A closure whole anonymous function has the given method type */ + /** A closure whose anonymous function has the given method type */ def Lambda(tpe: MethodType, rhsFn: List[Tree] => Tree)(using Context): Block = { - val meth = newSymbol(ctx.owner, nme.ANON_FUN, Synthetic | Method, tpe) + val meth = newAnonFun(ctx.owner, tpe) Closure(meth, tss => rhsFn(tss.head).changeOwner(ctx.owner, meth)) } @@ -1104,6 +1104,21 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo { if (sym.exists) sym.defTree = tree tree } + + def etaExpandCFT(using Context): Tree = + def expand(target: Tree, tp: Type)(using Context): Tree = tp match + case defn.ContextFunctionType(argTypes, resType, isErased) => + val anonFun = newAnonFun( + ctx.owner, + MethodType.companion(isContextual = true, isErased = isErased)(argTypes, resType), + coord = ctx.owner.coord) + def lambdaBody(refss: List[List[Tree]]) = + expand(target.select(nme.apply).appliedToArgss(refss), resType)( + using ctx.withOwner(anonFun)) + Closure(anonFun, lambdaBody) + case _ => + target + expand(tree, tree.tpe.widen) } inline val MapRecursionLimit = 10 diff --git a/compiler/src/dotty/tools/dotc/core/Definitions.scala b/compiler/src/dotty/tools/dotc/core/Definitions.scala index 60f375c116fc..16ce0e381c75 100644 --- a/compiler/src/dotty/tools/dotc/core/Definitions.scala +++ b/compiler/src/dotty/tools/dotc/core/Definitions.scala @@ -1353,23 +1353,6 @@ class Definitions { def isBoxedUnitClass(cls: Symbol): Boolean = cls.isClass && (cls.owner eq ScalaRuntimePackageClass) && cls.name == tpnme.BoxedUnit - /** Returns the erased class of the function class `cls` - * - FunctionN for N > 22 becomes FunctionXXL - * - FunctionN for 22 > N >= 0 remains as FunctionN - * - ContextFunctionN for N > 22 becomes FunctionXXL - * - ContextFunctionN for N <= 22 becomes FunctionN - * - ErasedFunctionN becomes Function0 - * - ImplicitErasedFunctionN becomes Function0 - * - anything else becomes a NoSymbol - */ - def erasedFunctionClass(cls: Symbol): Symbol = { - val arity = scalaClassName(cls).functionArity - if (cls.name.isErasedFunction) FunctionClass(0) - else if (arity > 22) FunctionXXLClass - else if (arity >= 0) FunctionClass(arity) - else NoSymbol - } - /** Returns the erased type of the function class `cls` * - FunctionN for N > 22 becomes FunctionXXL * - FunctionN for 22 > N >= 0 remains as FunctionN @@ -1379,13 +1362,12 @@ class Definitions { * - ImplicitErasedFunctionN becomes Function0 * - anything else becomes a NoType */ - def erasedFunctionType(cls: Symbol): Type = { + def functionTypeErasure(cls: Symbol): Type = val arity = scalaClassName(cls).functionArity - if (cls.name.isErasedFunction) FunctionType(0) - else if (arity > 22) FunctionXXLClass.typeRef - else if (arity >= 0) FunctionType(arity) + if cls.name.isErasedFunction then FunctionType(0) + else if arity > 22 then FunctionXXLClass.typeRef + else if arity >= 0 then FunctionType(arity) else NoType - } val predefClassNames: Set[Name] = Set("Predef$", "DeprecatedPredef", "LowPriorityImplicits").map(_.toTypeName.unmangleClassName) diff --git a/compiler/src/dotty/tools/dotc/core/NameKinds.scala b/compiler/src/dotty/tools/dotc/core/NameKinds.scala index e17e772de993..b77f870b72b7 100644 --- a/compiler/src/dotty/tools/dotc/core/NameKinds.scala +++ b/compiler/src/dotty/tools/dotc/core/NameKinds.scala @@ -365,7 +365,7 @@ object NameKinds { val ExtMethName: SuffixNameKind = new SuffixNameKind(EXTMETH, "$extension") val ParamAccessorName: SuffixNameKind = new SuffixNameKind(PARAMACC, "$accessor") val ModuleClassName: SuffixNameKind = new SuffixNameKind(OBJECTCLASS, "$", optInfoString = "ModuleClass") - val ImplMethName: SuffixNameKind = new SuffixNameKind(IMPLMETH, "$") + val DirectMethName: SuffixNameKind = new SuffixNameKind(DIRECT, "$direct") val AdaptedClosureName: SuffixNameKind = new SuffixNameKind(ADAPTEDCLOSURE, "$adapted") { override def definesNewName = true } val SyntheticSetterName: SuffixNameKind = new SuffixNameKind(SETTER, "_$eq") diff --git a/compiler/src/dotty/tools/dotc/core/NameTags.scala b/compiler/src/dotty/tools/dotc/core/NameTags.scala index 63aea8853235..67dfcec73c53 100644 --- a/compiler/src/dotty/tools/dotc/core/NameTags.scala +++ b/compiler/src/dotty/tools/dotc/core/NameTags.scala @@ -24,8 +24,9 @@ object NameTags extends TastyFormat.NameTags { final val ADAPTEDCLOSURE = 31 // Used in Erasure to adapt closures over primitive types. - final val IMPLMETH = 32 // Used to define methods in implementation classes - // (can probably be removed). + final val DIRECT = 32 // Used to define implementations of methods with + // erased context function results that can override some + // other method. final val PARAMACC = 33 // Used for a private parameter alias @@ -48,7 +49,7 @@ object NameTags extends TastyFormat.NameTags { case INITIALIZER => "INITIALIZER" case FIELD => "FIELD" case EXTMETH => "EXTMETH" - case IMPLMETH => "IMPLMETH" + case DIRECT => "DIRECT" case PARAMACC => "PARAMACC" case ADAPTEDCLOSURE => "ADAPTEDCLOSURE" case OBJECTCLASS => "OBJECTCLASS" diff --git a/compiler/src/dotty/tools/dotc/core/Symbols.scala b/compiler/src/dotty/tools/dotc/core/Symbols.scala index 9e4730534bf7..e49399ff9791 100644 --- a/compiler/src/dotty/tools/dotc/core/Symbols.scala +++ b/compiler/src/dotty/tools/dotc/core/Symbols.scala @@ -712,6 +712,10 @@ object Symbols { coord: Coord = NoCoord)(using Context): TermSymbol = newSymbol(cls, nme.CONSTRUCTOR, flags | Method, MethodType(paramNames, paramTypes, cls.typeRef), privateWithin, coord) + /** Create an anonymous function symbol */ + def newAnonFun(owner: Symbol, info: Type, coord: Coord = NoCoord)(using Context): TermSymbol = + newSymbol(owner, nme.ANON_FUN, Synthetic | Method, info, coord = coord) + /** Create an empty default constructor symbol for given class `cls`. */ def newDefaultConstructor(cls: ClassSymbol)(using Context): TermSymbol = newConstructor(cls, EmptyFlags, Nil, Nil) diff --git a/compiler/src/dotty/tools/dotc/core/TypeErasure.scala b/compiler/src/dotty/tools/dotc/core/TypeErasure.scala index 7e89b4e2c2c9..6435d0622ebd 100644 --- a/compiler/src/dotty/tools/dotc/core/TypeErasure.scala +++ b/compiler/src/dotty/tools/dotc/core/TypeErasure.scala @@ -581,7 +581,7 @@ class TypeErasure(sourceLanguage: SourceLanguage, semiEraseVCs: Boolean, isConst val sym = tp.symbol if (!sym.isClass) this(tp.translucentSuperType) else if (semiEraseVCs && isDerivedValueClass(sym)) eraseDerivedValueClass(tp) - else if (defn.isSyntheticFunctionClass(sym)) defn.erasedFunctionType(sym) + else if (defn.isSyntheticFunctionClass(sym)) defn.functionTypeErasure(sym) else eraseNormalClassRef(tp) case tp: AppliedType => val tycon = tp.tycon @@ -791,7 +791,7 @@ class TypeErasure(sourceLanguage: SourceLanguage, semiEraseVCs: Boolean, isConst if (erasedVCRef.exists) return sigName(erasedVCRef) } if (defn.isSyntheticFunctionClass(sym)) - sigName(defn.erasedFunctionType(sym)) + sigName(defn.functionTypeErasure(sym)) else val cls = normalizeClass(sym.asClass) val fullName = diff --git a/compiler/src/dotty/tools/dotc/transform/AccessProxies.scala b/compiler/src/dotty/tools/dotc/transform/AccessProxies.scala index 81d029e023fb..de25cb140da0 100644 --- a/compiler/src/dotty/tools/dotc/transform/AccessProxies.scala +++ b/compiler/src/dotty/tools/dotc/transform/AccessProxies.scala @@ -51,7 +51,10 @@ abstract class AccessProxies { forwardedArgss.nonEmpty && forwardedArgss.head.nonEmpty) // defensive conditions accessRef.becomes(forwardedArgss.head.head) else - accessRef.appliedToTypeTrees(forwardedTpts).appliedToArgss(forwardedArgss) + accessRef + .appliedToTypeTrees(forwardedTpts) + .appliedToArgss(forwardedArgss) + .etaExpandCFT(using ctx.withOwner(accessor)) rhs.withSpan(accessed.span) }) diff --git a/compiler/src/dotty/tools/dotc/transform/Bridges.scala b/compiler/src/dotty/tools/dotc/transform/Bridges.scala index d91840f247b6..f7c00ef74b94 100644 --- a/compiler/src/dotty/tools/dotc/transform/Bridges.scala +++ b/compiler/src/dotty/tools/dotc/transform/Bridges.scala @@ -9,6 +9,11 @@ import ast.untpd import collection.{mutable, immutable} import util.Spans.Span import util.SrcPos +import ContextFunctionResults.{contextResultCount, contextFunctionResultTypeAfter} +import StdNames.nme +import Constants.Constant +import TypeErasure.transformInfo +import Erasure.Boxing.adaptClosure /** A helper class for generating bridge methods in class `root`. */ class Bridges(root: ClassSymbol, thisPhase: DenotTransformer)(using Context) { @@ -112,12 +117,52 @@ class Bridges(root: ClassSymbol, thisPhase: DenotTransformer)(using Context) { toBeRemoved += other } - def bridgeRhs(argss: List[List[Tree]]) = { + val memberCount = contextResultCount(member) + + /** Eta expand application `ref(args)` as needed. + * To do this correctly, we have to look at the member's original pre-erasure + * type and figure out which context function types in its result are + * not yet instantiated. + */ + def etaExpand(ref: Tree, args: List[Tree])(using Context): Tree = + def expand(args: List[Tree], tp: Type, n: Int)(using Context): Tree = + if n <= 0 then + assert(ctx.typer.isInstanceOf[Erasure.Typer]) + ctx.typer.typed(untpd.cpy.Apply(ref)(ref, args), member.info.finalResultType) + else + val defn.ContextFunctionType(argTypes, resType, isErased) = tp: @unchecked + val anonFun = newAnonFun(ctx.owner, + MethodType(if isErased then Nil else argTypes, resType), + coord = ctx.owner.coord) + anonFun.info = transformInfo(anonFun, anonFun.info) + + def lambdaBody(refss: List[List[Tree]]) = + val refs :: Nil = refss: @unchecked + val expandedRefs = refs.map(_.withSpan(ctx.owner.span.endPos)) match + case (bunchedParam @ Ident(nme.ALLARGS)) :: Nil => + argTypes.indices.toList.map(n => + bunchedParam + .select(nme.primitive.arrayApply) + .appliedTo(Literal(Constant(n)))) + case refs1 => refs1 + expand(args ::: expandedRefs, resType, n - 1)(using ctx.withOwner(anonFun)) + + val unadapted = Closure(anonFun, lambdaBody) + cpy.Block(unadapted)(unadapted.stats, + adaptClosure(unadapted.expr.asInstanceOf[Closure])) + end expand + + val otherCount = contextResultCount(other) + val start = contextFunctionResultTypeAfter(member, otherCount)(using preErasureCtx) + expand(args, start, memberCount - otherCount)(using ctx.withOwner(bridge)) + end etaExpand + + def bridgeRhs(argss: List[List[Tree]]) = assert(argss.tail.isEmpty) val ref = This(root).select(member) - if (member.info.isParameterless) ref // can happen if `member` is a module - else Erasure.partialApply(ref, argss.head) - } + if member.info.isParameterless then ref // can happen if `member` is a module + else if memberCount == 0 then ref.appliedToTermArgs(argss.head) + else etaExpand(ref, argss.head) bridges += DefDef(bridge, bridgeRhs(_).withSpan(bridge.span)) } diff --git a/compiler/src/dotty/tools/dotc/transform/ByNameClosures.scala b/compiler/src/dotty/tools/dotc/transform/ByNameClosures.scala index 00bcd1e5076a..51242e7f2dbe 100644 --- a/compiler/src/dotty/tools/dotc/transform/ByNameClosures.scala +++ b/compiler/src/dotty/tools/dotc/transform/ByNameClosures.scala @@ -30,11 +30,9 @@ class ByNameClosures extends TransformByNameApply with IdentityDenotTransformer // ExpanSAMs applied to partial functions creates methods that need // to be fully defined before converting. Test case is pos/i9391.scala. - override def mkByNameClosure(arg: Tree, argType: Type)(using Context): Tree = { - val meth = newSymbol( - ctx.owner, nme.ANON_FUN, Synthetic | Method, MethodType(Nil, Nil, argType)) + override def mkByNameClosure(arg: Tree, argType: Type)(using Context): Tree = + val meth = newAnonFun(ctx.owner, MethodType(Nil, argType)) Closure(meth, _ => arg.changeOwnerAfter(ctx.owner, meth, thisPhase)).withSpan(arg.span) - } } object ByNameClosures { diff --git a/compiler/src/dotty/tools/dotc/transform/ContextFunctionResults.scala b/compiler/src/dotty/tools/dotc/transform/ContextFunctionResults.scala index ee56767054fd..14134b4bb1fa 100644 --- a/compiler/src/dotty/tools/dotc/transform/ContextFunctionResults.scala +++ b/compiler/src/dotty/tools/dotc/transform/ContextFunctionResults.scala @@ -8,12 +8,16 @@ import StdNames.nme import ast.untpd import ast.tpd._ import config.Config +import Decorators.* object ContextFunctionResults: /** Annotate methods that have context function result types directly matched by context * closures on their right-hand side. Parameters to such closures will be integrated * as additional method parameters in erasure. + * + * A @ContextResultCount(n) annotation means that the method's result type + * consists of a string of `n` nested context closures. */ def annotateContextResults(mdef: DefDef)(using Context): Unit = def contextResultCount(rhs: Tree, tp: Type): Int = tp match @@ -50,6 +54,15 @@ object ContextFunctionResults: crCount case none => 0 + /** True iff `ContextResultCount` is not zero and all context functions in the result + * type are erased. + */ + def contextResultsAreErased(sym: Symbol)(using Context): Boolean = + def allErased(tp: Type): Boolean = tp.dealias match + case defn.ContextFunctionType(_, resTpe, isErased) => isErased && allErased(resTpe) + case _ => true + contextResultCount(sym) > 0 && allErased(sym.info.finalResultType) + /** Turn the first `crCount` context function types in the result type of `tp` * into the curried method types. */ @@ -86,33 +99,13 @@ object ContextFunctionResults: normalParamCount(sym.info) end totalParamCount - /** The rightmost context function type in the result type of `meth` - * that represents `paramCount` curried, non-erased parameters that - * are included in the `contextResultCount` of `meth`. - * Example: - * - * Say we have `def m(x: A): B ?=> (C1, C2, C3) ?=> D ?=> E ?=> F`, - * paramCount == 4, and the contextResultCount of `m` is 3. - * Then we return the type `(C1, C2, C3) ?=> D ?=> E ?=> F`, since this - * type covers the 4 rightmost parameters C1, C2, C3 and D before the - * contextResultCount runs out at E ?=> F. - * Erased parameters are ignored; they contribute nothing to the - * parameter count. - */ - def contextFunctionResultTypeCovering(meth: Symbol, paramCount: Int)(using Context) = - atPhase(erasurePhase) { - // Recursive instances return pairs of context types and the - // # of parameters they represent. - def missingCR(tp: Type, crCount: Int): (Type, Int) = - if crCount == 0 then (tp, 0) - else - val defn.ContextFunctionType(formals, resTpe, isErased) = tp: @unchecked - val result @ (rt, nparams) = missingCR(resTpe, crCount - 1) - assert(nparams <= paramCount) - if nparams == paramCount || isErased then result - else (tp, nparams + formals.length) - missingCR(meth.info.finalResultType, contextResultCount(meth))._1 - } + /** The `depth` levels nested context function type in the result type of `meth` */ + def contextFunctionResultTypeAfter(meth: Symbol, depth: Int)(using Context) = + def recur(tp: Type, n: Int): Type = + if n == 0 then tp + else tp match + case defn.ContextFunctionType(_, resTpe, _) => recur(resTpe, n - 1) + recur(meth.info.finalResultType, depth) /** Should selection `tree` be eliminated since it refers to an `apply` * node of a context function type whose parameters will end up being diff --git a/compiler/src/dotty/tools/dotc/transform/Erasure.scala b/compiler/src/dotty/tools/dotc/transform/Erasure.scala index 2875e2e2d865..7d2ff375a9b8 100644 --- a/compiler/src/dotty/tools/dotc/transform/Erasure.scala +++ b/compiler/src/dotty/tools/dotc/transform/Erasure.scala @@ -12,7 +12,7 @@ import core.Types._ import core.Names._ import core.StdNames._ import core.NameOps._ -import core.NameKinds.{AdaptedClosureName, BodyRetainerName} +import core.NameKinds.{AdaptedClosureName, BodyRetainerName, DirectMethName} import core.Scopes.newScopeWith import core.Decorators._ import core.Constants._ @@ -58,6 +58,18 @@ class Erasure extends Phase with DenotTransformer { } } + def erasedName = + if ref.is(Flags.Method) + && contextResultsAreErased(ref.symbol) + && (ref.owner.is(Flags.Trait) || ref.symbol.allOverriddenSymbols.hasNext) + then + // Add a `$direct` to prevent this method from having the same signature + // as a method it overrides. We need a bridge between the + // two methods, so they are not allowed to already override after erasure. + DirectMethName(ref.targetName.asTermName) + else + ref.targetName + assert(ctx.phase == this, s"transforming $ref at ${ctx.phase}") if (ref.symbol eq defn.ObjectClass) { // After erasure, all former Any members are now Object members @@ -80,7 +92,7 @@ class Erasure extends Phase with DenotTransformer { val oldOwner = ref.owner val newOwner = if oldOwner == defn.AnyClass then defn.ObjectClass else oldOwner val oldName = ref.name - val newName = ref.targetName + val newName = erasedName val oldInfo = ref.info var newInfo = transformInfo(oldSymbol, oldInfo) val oldFlags = ref.flags @@ -372,8 +384,8 @@ object Erasure { case _: FunProto | AnyFunctionProto => tree case _ => tree.tpe.widen match case mt: MethodType if tree.isTerm => - if mt.paramInfos.isEmpty then adaptToType(tree.appliedToNone, pt) - else etaExpand(tree, mt, pt) + assert(mt.paramInfos.isEmpty) + adaptToType(tree.appliedToNone, pt) case tpw => if (pt.isInstanceOf[ProtoType] || tree.tpe <:< pt) tree @@ -392,7 +404,6 @@ object Erasure { cast(tree, pt) end adaptToType - /** The following code: * * val f: Function1[Int, Any] = x => ... @@ -523,61 +534,6 @@ object Erasure { else tree end adaptClosure - - /** Eta expand given `tree` that has the given method type `mt`, so that - * it conforms to erased result type `pt`. - * To do this correctly, we have to look at the tree's original pre-erasure - * type and figure out which context function types in its result are - * not yet instantiated. - */ - def etaExpand(tree: Tree, mt: MethodType, pt: Type)(using Context): Tree = - report.log(i"eta expanding $tree") - val defs = new mutable.ListBuffer[Tree] - val tree1 = LiftErased.liftApp(defs, tree) - val xmt = if tree.isInstanceOf[Apply] then mt else expandedMethodType(mt, tree) - val targetLength = xmt.paramInfos.length - val origOwner = ctx.owner - - // The original type from which closures should be constructed - val origType = contextFunctionResultTypeCovering(tree.symbol, targetLength) - - def abstracted(args: List[Tree], tp: Type, pt: Type)(using Context): Tree = - if args.length < targetLength then - try - val defn.ContextFunctionType(argTpes, resTpe, isErased) = tp: @unchecked - if isErased then abstracted(args, resTpe, pt) - else - val anonFun = newSymbol( - ctx.owner, nme.ANON_FUN, Flags.Synthetic | Flags.Method, - MethodType(argTpes, resTpe), coord = tree.span.endPos) - anonFun.info = transformInfo(anonFun, anonFun.info) - def lambdaBody(refss: List[List[Tree]]) = - val refs :: Nil = refss: @unchecked - val expandedRefs = refs.map(_.withSpan(tree.span.endPos)) match - case (bunchedParam @ Ident(nme.ALLARGS)) :: Nil => - argTpes.indices.toList.map(n => - bunchedParam - .select(nme.primitive.arrayApply) - .appliedTo(Literal(Constant(n)))) - case refs1 => refs1 - abstracted(args ::: expandedRefs, resTpe, anonFun.info.finalResultType)( - using ctx.withOwner(anonFun)) - - val unadapted = Closure(anonFun, lambdaBody) - cpy.Block(unadapted)(unadapted.stats, adaptClosure(unadapted.expr.asInstanceOf[Closure])) - catch case ex: MatchError => - println(i"error while abstracting tree = $tree | mt = $mt | args = $args%, % | tp = $tp | pt = $pt") - throw ex - else - assert(args.length == targetLength, i"wrong # args tree = $tree | args = $args%, % | mt = $mt | tree type = ${tree.tpe}") - val app = untpd.cpy.Apply(tree1)(tree1, args) - assert(ctx.typer.isInstanceOf[Erasure.Typer]) - ctx.typer.typed(app, pt) - .changeOwnerAfter(origOwner, ctx.owner, erasurePhase.asInstanceOf[Erasure]) - - seq(defs.toList, abstracted(Nil, origType, pt)) - end etaExpand - end Boxing class Typer(erasurePhase: DenotTransformer) extends typer.ReTyper with NoChecking { @@ -714,7 +670,7 @@ object Erasure { assert(sym.isConstructor, s"${sym.showLocated}") defn.specialErasure(owner) else if defn.isSyntheticFunctionClass(owner) then - defn.erasedFunctionClass(owner) + defn.functionTypeErasure(owner).typeSymbol else owner diff --git a/compiler/src/dotty/tools/dotc/transform/GenericSignatures.scala b/compiler/src/dotty/tools/dotc/transform/GenericSignatures.scala index c12c5c8b4a6e..18b028d1a024 100644 --- a/compiler/src/dotty/tools/dotc/transform/GenericSignatures.scala +++ b/compiler/src/dotty/tools/dotc/transform/GenericSignatures.scala @@ -274,10 +274,10 @@ object GenericSignatures { jsig(erasedUnderlying, toplevel, primitiveOK) } else if (defn.isSyntheticFunctionClass(sym)) { - val erasedSym = defn.erasedFunctionClass(sym) + val erasedSym = defn.functionTypeErasure(sym).typeSymbol classSig(erasedSym, pre, if (erasedSym.typeParams.isEmpty) Nil else args) } - else if (sym.isClass) + else if sym.isClass then classSig(sym, pre, args) else jsig(erasure(tp), toplevel, primitiveOK) diff --git a/compiler/src/dotty/tools/dotc/typer/Synthesizer.scala b/compiler/src/dotty/tools/dotc/typer/Synthesizer.scala index 75e7c55ada28..5d54798c812d 100644 --- a/compiler/src/dotty/tools/dotc/typer/Synthesizer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Synthesizer.scala @@ -70,7 +70,7 @@ class Synthesizer(typer: Typer)(using @constructorOnly c: Context): ref(defn.NoneModule)) } val tpe = MethodType(List(nme.s))(_ => List(tp1), mth => defn.OptionClass.typeRef.appliedTo(mth.newParamRef(0) & tp2)) - val meth = newSymbol(ctx.owner, nme.ANON_FUN, Synthetic | Method, tpe, coord = span) + val meth = newAnonFun(ctx.owner, tpe, coord = span) val typeTestType = defn.TypeTestClass.typeRef.appliedTo(List(tp1, tp2)) Closure(meth, tss => body(tss.head).changeOwner(ctx.owner, meth), targetType = typeTestType).withSpan(span) case _ => diff --git a/compiler/src/scala/quoted/runtime/impl/QuoteMatcher.scala b/compiler/src/scala/quoted/runtime/impl/QuoteMatcher.scala index 1ee0d9d34dfa..6d9ff6ca68a8 100644 --- a/compiler/src/scala/quoted/runtime/impl/QuoteMatcher.scala +++ b/compiler/src/scala/quoted/runtime/impl/QuoteMatcher.scala @@ -212,7 +212,7 @@ object QuoteMatcher { } val argTypes = args.map(x => x.tpe.widenTermRefExpr) val methTpe = MethodType(names)(_ => argTypes, _ => pattern.tpe) - val meth = newSymbol(ctx.owner, nme.ANON_FUN, Synthetic | Method, methTpe) + val meth = newAnonFun(ctx.owner, methTpe) def bodyFn(lambdaArgss: List[List[Tree]]): Tree = { val argsMap = args.map(_.symbol).zip(lambdaArgss.head).toMap val body = new TreeMap { diff --git a/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala b/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala index ab651408a0c4..b05311f162fd 100644 --- a/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala +++ b/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala @@ -385,7 +385,7 @@ class QuotesImpl private (using val ctx: Context) extends Quotes, QuoteUnpickler case t => t } val closureTpe = Types.MethodType(mtpe.paramNames, mtpe.paramInfos, closureResType) - val closureMethod = dotc.core.Symbols.newSymbol(owner, nme.ANON_FUN, Synthetic | Method, closureTpe) + val closureMethod = dotc.core.Symbols.newAnonFun(owner, closureTpe) tpd.Closure(closureMethod, tss => new tpd.TreeOps(self).appliedToTermArgs(tss.head).etaExpand(closureMethod)) case _ => self } @@ -793,7 +793,7 @@ class QuotesImpl private (using val ctx: Context) extends Quotes, QuoteUnpickler object Lambda extends LambdaModule: def apply(owner: Symbol, tpe: MethodType, rhsFn: (Symbol, List[Tree]) => Tree): Block = - val meth = dotc.core.Symbols.newSymbol(owner, nme.ANON_FUN, Synthetic | Method, tpe) + val meth = dotc.core.Symbols.newAnonFun(owner, tpe) withDefaultPos(tpd.Closure(meth, tss => xCheckMacroedOwners(xCheckMacroValidExpr(rhsFn(meth, tss.head.map(withDefaultPos))), meth))) def unapply(tree: Block): Option[(List[ValDef], Term)] = tree match { diff --git a/tests/run/i13691.scala b/tests/run/i13691.scala new file mode 100644 index 000000000000..6db01ee0de35 --- /dev/null +++ b/tests/run/i13691.scala @@ -0,0 +1,53 @@ +import language.experimental.erasedDefinitions + +erased class CanThrow[-E <: Exception] +erased class Foo +class Bar + +object unsafeExceptions: + given canThrowAny: CanThrow[Exception] = null + +object test1: + trait Decoder[+T]: + def apply(): T + + def deco: Decoder[CanThrow[Exception] ?=> Int] = new Decoder[CanThrow[Exception] ?=> Int]: + def apply(): CanThrow[Exception] ?=> Int = 1 + +object test2: + trait Decoder[+T]: + def apply(): T + + def deco: Decoder[(CanThrow[Exception], Foo) ?=> Int] = new Decoder[(CanThrow[Exception], Foo) ?=> Int]: + def apply(): (CanThrow[Exception], Foo) ?=> Int = 1 + +object test3: + trait Decoder[+T]: + def apply(): T + + def deco: Decoder[CanThrow[Exception] ?=> Foo ?=> Int] = new Decoder[CanThrow[Exception] ?=> Foo ?=> Int]: + def apply(): CanThrow[Exception] ?=> Foo ?=> Int = 1 + +object test4: + trait Decoder[+T]: + def apply(): T + + def deco: Decoder[CanThrow[Exception] ?=> Bar ?=> Int] = new Decoder[CanThrow[Exception] ?=> Bar ?=> Int]: + def apply(): CanThrow[Exception] ?=> Bar ?=> Int = 1 + +object test5: + trait Decoder[+T]: + def apply(): T + + def deco: Decoder[Bar ?=> CanThrow[Exception] ?=> Int] = new Decoder[Bar ?=> CanThrow[Exception] ?=> Int]: + def apply(): Bar ?=> CanThrow[Exception] ?=> Int = 1 + +@main def Test(): Unit = + import unsafeExceptions.canThrowAny + given Foo = ??? + given Bar = Bar() + test1.deco.apply().apply + test2.deco.apply().apply + test3.deco.apply().apply + test4.deco.apply().apply + test5.deco.apply().apply diff --git a/tests/run/i13961a.scala b/tests/run/i13961a.scala new file mode 100644 index 000000000000..9d49ab30b0a4 --- /dev/null +++ b/tests/run/i13961a.scala @@ -0,0 +1,11 @@ +import language.experimental.saferExceptions + +trait Decoder[+T]: + def apply(): T + +given Decoder[Int throws Exception] = new Decoder[Int throws Exception]: + def apply(): Int throws Exception = 1 + +@main def Test(): Unit = + import unsafeExceptions.canThrowAny + summon[Decoder[Int throws Exception]]() \ No newline at end of file