diff --git a/compiler/src/dotty/tools/dotc/ast/Desugar.scala b/compiler/src/dotty/tools/dotc/ast/Desugar.scala index c59e020f1873..ec3df458b67b 100644 --- a/compiler/src/dotty/tools/dotc/ast/Desugar.scala +++ b/compiler/src/dotty/tools/dotc/ast/Desugar.scala @@ -930,6 +930,10 @@ object desugar { else tree } + /** Invent a name for an anonympus given of type or template `impl`. */ + def inventGivenName(impl: Tree)(implicit ctx: Context): SimpleName = + avoidIllegalChars(s"${inventName(impl)}_given".toTermName.asSimpleName) + /** The normalized name of `mdef`. This means * 1. Check that the name does not redefine a Scala core class. * If it does redefine, issue an error and return a mangled name instead of the original one. @@ -937,7 +941,7 @@ object desugar { */ def normalizeName(mdef: MemberDef, impl: Tree)(implicit ctx: Context): Name = { var name = mdef.name - if (name.isEmpty) name = name.likeSpaced(avoidIllegalChars(s"${inventName(impl)}_given".toTermName.asSimpleName)) + if (name.isEmpty) name = name.likeSpaced(inventGivenName(impl)) if (ctx.owner == defn.ScalaPackageClass && defn.reservedScalaClassNames.contains(name.toTypeName)) { def kind = if (name.isTypeName) "class" else "object" ctx.error(em"illegal redefinition of standard $kind $name", mdef.sourcePos) diff --git a/compiler/src/dotty/tools/dotc/ast/Trees.scala b/compiler/src/dotty/tools/dotc/ast/Trees.scala index 761fb4b145fc..6b7c92b6d09e 100644 --- a/compiler/src/dotty/tools/dotc/ast/Trees.scala +++ b/compiler/src/dotty/tools/dotc/ast/Trees.scala @@ -308,25 +308,12 @@ object Trees { /** Tree defines a new symbol */ trait DefTree[-T >: Untyped] extends DenotingTree[T] { type ThisTree[-T >: Untyped] <: DefTree[T] - override def isDef: Boolean = true - def namedType: NamedType = tpe.asInstanceOf[NamedType] - } - - /** Tree defines a new symbol and carries modifiers. - * The position of a MemberDef contains only the defined identifier or pattern. - * The envelope of a MemberDef contains the whole definition and has its point - * on the opening keyword (or the next token after that if keyword is missing). - */ - abstract class MemberDef[-T >: Untyped](implicit @constructorOnly src: SourceFile) extends NameTree[T] with DefTree[T] { - type ThisTree[-T >: Untyped] <: MemberDef[T] private[this] var myMods: untpd.Modifiers = null private[dotc] def rawMods: untpd.Modifiers = if (myMods == null) untpd.EmptyModifiers else myMods - def rawComment: Option[Comment] = getAttachment(DocComment) - def withAnnotations(annots: List[untpd.Tree]): ThisTree[Untyped] = withMods(rawMods.withAnnotations(annots)) def withMods(mods: untpd.Modifiers): ThisTree[Untyped] = { @@ -338,14 +325,28 @@ object Trees { def withFlags(flags: FlagSet): ThisTree[Untyped] = withMods(untpd.Modifiers(flags)) def withAddedFlags(flags: FlagSet): ThisTree[Untyped] = withMods(rawMods | flags) + /** Destructively update modifiers. To be used with care. */ + def setMods(mods: untpd.Modifiers): Unit = myMods = mods + + override def isDef: Boolean = true + def namedType: NamedType = tpe.asInstanceOf[NamedType] + } + + /** Tree defines a new symbol and carries modifiers. + * The position of a MemberDef contains only the defined identifier or pattern. + * The envelope of a MemberDef contains the whole definition and has its point + * on the opening keyword (or the next token after that if keyword is missing). + */ + abstract class MemberDef[-T >: Untyped](implicit @constructorOnly src: SourceFile) extends NameTree[T] with DefTree[T] { + type ThisTree[-T >: Untyped] <: MemberDef[T] + + def rawComment: Option[Comment] = getAttachment(DocComment) + def setComment(comment: Option[Comment]): this.type = { comment.map(putAttachment(DocComment, _)) this } - /** Destructively update modifiers. To be used with care. */ - def setMods(mods: untpd.Modifiers): Unit = myMods = mods - /** The position of the name defined by this definition. * This is a point position if the definition is synthetic, or a range position * if the definition comes from source. diff --git a/compiler/src/dotty/tools/dotc/ast/untpd.scala b/compiler/src/dotty/tools/dotc/ast/untpd.scala index 8b77d31ff2cc..547f732b3f41 100644 --- a/compiler/src/dotty/tools/dotc/ast/untpd.scala +++ b/compiler/src/dotty/tools/dotc/ast/untpd.scala @@ -457,7 +457,7 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo { abstract class ModsDecorator { def mods: Modifiers } - implicit class modsDeco(val mdef: MemberDef)(implicit ctx: Context) { + implicit class modsDeco(val mdef: DefTree)(implicit ctx: Context) { def mods: Modifiers = mdef.rawMods } diff --git a/compiler/src/dotty/tools/dotc/core/Definitions.scala b/compiler/src/dotty/tools/dotc/core/Definitions.scala index be936383aa08..9d7962800b8b 100644 --- a/compiler/src/dotty/tools/dotc/core/Definitions.scala +++ b/compiler/src/dotty/tools/dotc/core/Definitions.scala @@ -220,6 +220,7 @@ class Definitions { @tu lazy val Compiletime_constValue : Symbol = CompiletimePackageObject.requiredMethod("constValue") @tu lazy val Compiletime_constValueOpt: Symbol = CompiletimePackageObject.requiredMethod("constValueOpt") @tu lazy val Compiletime_code : Symbol = CompiletimePackageObject.requiredMethod("code") + @tu lazy val Compiletime_summonFrom : Symbol = CompiletimePackageObject.requiredMethod("summonFrom") @tu lazy val CompiletimeTestingPackageObject: Symbol = ctx.requiredModule("scala.compiletime.testing.package") @tu lazy val CompiletimeTesting_typeChecks : Symbol = CompiletimeTestingPackageObject.requiredMethod("typeChecks") diff --git a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala index 72fa249f297a..e69c2cae6bae 100644 --- a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala +++ b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala @@ -14,6 +14,7 @@ import Flags._ import Contexts._ import Names._ import NameKinds.WildcardParamName +import NameOps._ import ast.{Positioned, Trees} import ast.Trees._ import StdNames._ @@ -202,8 +203,8 @@ object Parsers { } && !in.isSoftModifierInModifierPosition def isExprIntro: Boolean = - if (in.token == IMPLIED || in.token == GIVEN) in.lookaheadIn(BitSet(MATCH)) - else (canStartExpressionTokens.contains(in.token) && !in.isSoftModifierInModifierPosition) + if (in.token == GIVEN) false + else canStartExpressionTokens.contains(in.token) && !in.isSoftModifierInModifierPosition def isDefIntro(allowedMods: BitSet, excludedSoftModifiers: Set[TermName] = Set.empty): Boolean = in.token == AT || @@ -1645,15 +1646,6 @@ object Parsers { if (in.token == MATCH) impliedMatch(start, imods) else implicitClosure(start, location, imods) } - else if (in.token == IMPLIED || in.token == GIVEN) { - in.nextToken() - if (in.token == MATCH) - impliedMatch(start, EmptyModifiers) - else { - syntaxError("`match` expected") - EmptyTree - } - } else { val saved = placeholderParams placeholderParams = Nil @@ -2350,16 +2342,34 @@ object Parsers { if (isIdent(nme.raw.BAR)) { in.nextToken(); pattern1() :: patternAlts() } else Nil - /** Pattern1 ::= Pattern2 [Ascription] + /** Pattern1 ::= Pattern2 [Ascription] + * | ‘given’ PatVar ‘:’ RefinedType */ - def pattern1(): Tree = { - val p = pattern2() - if (in.token == COLON) { - in.nextToken() - ascription(p, Location.InPattern) + def pattern1(): Tree = + if (in.token == GIVEN) { + val givenMod = atSpan(in.skipToken())(Mod.Given()) + atSpan(in.offset) { + in.token match { + case IDENTIFIER | USCORE if in.name.isVariableName => + val name = in.name + in.nextToken() + accept(COLON) + val typed = ascription(Ident(nme.WILDCARD), Location.InPattern) + Bind(name, typed).withMods(addMod(Modifiers(), givenMod)) + case _ => + syntaxErrorOrIncomplete("pattern variable expected") + errorTermTree + } + } + } + else { + val p = pattern2() + if (in.token == COLON) { + in.nextToken() + ascription(p, Location.InPattern) + } + else p } - else p - } /** Pattern2 ::= [id `@'] InfixPattern */ @@ -3297,10 +3307,17 @@ object Parsers { Template(constr, parents, Nil, EmptyValDef, Nil) } - /** GivenDef ::= [id] [DefTypeParamClause] GivenBody - * GivenBody ::= [‘as ConstrApp {‘,’ ConstrApp }] {GivenParamClause} [TemplateBody] - * | ‘as’ Type {GivenParamClause} ‘=’ Expr - * | ‘(’ DefParam ‘)’ TemplateBody + /** OLD: + * GivenDef ::= [id] [DefTypeParamClause] GivenBody + * GivenBody ::= [‘as ConstrApp {‘,’ ConstrApp }] {GivenParamClause} [TemplateBody] + * | ‘as’ Type {GivenParamClause} ‘=’ Expr + * | ‘(’ DefParam ‘)’ TemplateBody + * NEW: + * GivenDef ::= [GivenSig (‘:’ | <:)] Type ‘=’ Expr + * | [GivenSig ‘:’] [ConstrApp {‘,’ ConstrApp }] [TemplateBody] + * // | [id ‘:’] [ExtParamClause] TemplateBody (not yet implemented) + * ExtParamClause ::= [DefTypeParamClause] DefParamClause {GivenParamClause} + * GivenSig ::= [id] [DefTypeParamClause] {GivenParamClause} */ def instanceDef(newStyle: Boolean, start: Offset, mods: Modifiers, instanceMod: Mod) = atSpan(start, nameStart) { var mods1 = addMod(mods, instanceMod) diff --git a/compiler/src/dotty/tools/dotc/parsing/Tokens.scala b/compiler/src/dotty/tools/dotc/parsing/Tokens.scala index cbe9ae05c4b5..d97b4363d510 100644 --- a/compiler/src/dotty/tools/dotc/parsing/Tokens.scala +++ b/compiler/src/dotty/tools/dotc/parsing/Tokens.scala @@ -221,7 +221,7 @@ object Tokens extends TokensCommon { USCORE, NULL, THIS, SUPER, TRUE, FALSE, RETURN, QUOTEID, XMLSTART) final val canStartExpressionTokens: TokenSet = atomicExprTokens | BitSet( - LBRACE, LPAREN, INDENT, QUOTE, IF, DO, WHILE, FOR, NEW, TRY, THROW, IMPLIED, GIVEN) + LBRACE, LPAREN, INDENT, QUOTE, IF, DO, WHILE, FOR, NEW, TRY, THROW, GIVEN) final val canStartTypeTokens: TokenSet = literalTokens | identifierTokens | BitSet( THIS, SUPER, USCORE, LPAREN, AT) diff --git a/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala b/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala index ab527e173f15..2de86ab1d550 100644 --- a/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala +++ b/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala @@ -479,7 +479,7 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) { if (lo eq hi) optText(lo)(" = " ~ _) else optText(lo)(" >: " ~ _) ~ optText(hi)(" <: " ~ _) case Bind(name, body) => - ("given ": Text).provided(tree.symbol.is(Implicit) && !homogenizedView) ~ // Used for scala.quoted.Type in quote patterns (not pickled) + ("given ": Text).provided(tree.symbol.isOneOf(GivenOrImplicit) && !homogenizedView) ~ // Used for scala.quoted.Type in quote patterns (not pickled) changePrec(InfixPrec) { toText(name) ~ " @ " ~ toText(body) } case Alternative(trees) => changePrec(OrPrec) { toText(trees, " | ") } @@ -699,7 +699,7 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) { } /** Print modifiers from symbols if tree has type, overriding the untpd behavior. */ - implicit def modsDeco(mdef: untpd.MemberDef)(implicit ctx: Context): untpd.ModsDecorator = + implicit def modsDeco(mdef: untpd.DefTree)(implicit ctx: Context): untpd.ModsDecorator = new untpd.ModsDecorator { def mods = if (mdef.hasType) Modifiers(mdef.symbol) else mdef.rawMods } diff --git a/compiler/src/dotty/tools/dotc/typer/Applications.scala b/compiler/src/dotty/tools/dotc/typer/Applications.scala index ec4c9543d7b9..339556f62d2b 100644 --- a/compiler/src/dotty/tools/dotc/typer/Applications.scala +++ b/compiler/src/dotty/tools/dotc/typer/Applications.scala @@ -890,7 +890,31 @@ trait Applications extends Compatibility { case err: ErrorType => cpy.Apply(tree)(fun1, proto.unforcedTypedArgs).withType(err) case TryDynamicCallType => typedDynamicApply(tree, pt) case _ => - if (originalProto.isDropped) fun1 + if originalProto.isDropped then fun1 + else if fun1.symbol == defn.Compiletime_summonFrom then + // Special handling of `summonFrom { ... }`. + // We currently cannot use a macro for that since unlike other inline methods + // summonFrom needs to expand lazily. For instance, in + // + // summonFrom { + // case given A[t] => + // summonFrom + // case given `t` => ... + // } + // } + // + // the second `summonFrom` should expand only once the first `summonFrom` is + // evaluated and `t` is bound. But normal inline expansion does not behave that + // way: arguments to inline function are expanded before the function call. + // To make this work using regular inlining, we'd need a way to annotate + // an inline function that it should expand only if there are no enclosing + // applications of inline functions. + tree.args match { + case (arg @ Match(EmptyTree, cases)) :: Nil => + typed(untpd.InlineMatch(EmptyTree, cases).withSpan(arg.span), pt) + case _ => + errorTree(tree, em"argument to summonFrom must be a pattern matching closure") + } else tryEither { simpleApply(fun1, proto) diff --git a/compiler/src/dotty/tools/dotc/typer/Inliner.scala b/compiler/src/dotty/tools/dotc/typer/Inliner.scala index aa9c9e036b1e..dbf633f6095f 100644 --- a/compiler/src/dotty/tools/dotc/typer/Inliner.scala +++ b/compiler/src/dotty/tools/dotc/typer/Inliner.scala @@ -413,7 +413,7 @@ class Inliner(call: tpd.Tree, rhsToInline: tpd.Tree)(implicit ctx: Context) { /** Register type of leaf node */ private def registerLeaf(tree: Tree): Unit = tree match { case _: This | _: Ident | _: TypeTree => - tree.tpe.foreachPart(registerType, stopAtStatic = true) + tree.typeOpt.foreachPart(registerType, stopAtStatic = true) case _ => } @@ -433,11 +433,12 @@ class Inliner(call: tpd.Tree, rhsToInline: tpd.Tree)(implicit ctx: Context) { /** The Inlined node representing the inlined call */ def inlined(sourcePos: SourcePosition): Tree = { + // Special handling of `constValue[T]` and `constValueOpt[T]` if (callTypeArgs.length == 1) if (inlinedMethod == defn.Compiletime_constValue) { val constVal = tryConstValue if (!constVal.isEmpty) return constVal - ctx.error(i"not a constant type: ${callTypeArgs.head}; cannot take constValue", call.sourcePos) + ctx.error(em"not a constant type: ${callTypeArgs.head}; cannot take constValue", call.sourcePos) } else if (inlinedMethod == defn.Compiletime_constValueOpt) { val constVal = tryConstValue @@ -795,9 +796,9 @@ class Inliner(call: tpd.Tree, rhsToInline: tpd.Tree)(implicit ctx: Context) { /** Reduce an inline match * @param mtch the match tree * @param scrutinee the scrutinee expression, assumed to be pure, or - * EmptyTree for a delegate match + * EmptyTree for a summonFrom * @param scrutType its fully defined type, or - * ImplicitScrutineeTypeRef for a delegate match + * ImplicitScrutineeTypeRef for a summonFrom * @param typer The current inline typer * @return optionally, if match can be reduced to a matching case: A pair of * bindings for all pattern-bound variables and the RHS of the case. @@ -1071,11 +1072,10 @@ class Inliner(call: tpd.Tree, rhsToInline: tpd.Tree)(implicit ctx: Context) { override def typedApply(tree: untpd.Apply, pt: Type)(implicit ctx: Context): Tree = constToLiteral(betaReduce(super.typedApply(tree, pt))) match { - case res: Apply - if res.symbol == defn.InternalQuoted_exprSplice && - level == 0 && - call.symbol.is(Macro) && - !suppressInline => + case res: Apply if res.symbol == defn.InternalQuoted_exprSplice + && level == 0 + && call.symbol.is(Macro) + && !suppressInline => expandMacro(res.args.head, tree.span) case res => res } @@ -1114,7 +1114,7 @@ class Inliner(call: tpd.Tree, rhsToInline: tpd.Tree)(implicit ctx: Context) { def patStr(cdef: untpd.CaseDef) = i"case ${cdef.pat}${guardStr(cdef.guard)}" val msg = if (tree.selector.isEmpty) - em"""cannot reduce delegate match with + em"""cannot reduce summonFrom with | patterns : ${tree.cases.map(patStr).mkString("\n ")}""" else em"""cannot reduce inline match with diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index c19a53ef9d13..a7bf1dacee77 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -1090,7 +1090,7 @@ class Typer extends Namer tree.selector match { case EmptyTree => if (tree.isInline) { - checkInInlineContext("delegate match", tree.posd) + checkInInlineContext("summonFrom", tree.posd) val cases1 = tree.cases.mapconserve { case cdef @ CaseDef(pat @ Typed(Ident(nme.WILDCARD), _), _, _) => // case _ : T --> case evidence$n : T @@ -1478,18 +1478,23 @@ class Typer extends Namer tpd.cpy.UnApply(body1)(fn, Nil, typed(untpd.Bind(tree.name, untpd.TypedSplice(arg)).withSpan(tree.span), arg.tpe) :: Nil) case _ => - if (tree.name == nme.WILDCARD) body1 + var name = tree.name + if (name == nme.WILDCARD && tree.mods.is(Given)) { + val Typed(_, tpt): @unchecked = tree.body + name = desugar.inventGivenName(tpt) + } + if (name == nme.WILDCARD) body1 else { // for a singleton pattern like `x @ Nil`, `x` should get the type from the scrutinee // see tests/neg/i3200b.scala and SI-1503 val symTp = if (body1.tpe.isInstanceOf[TermRef]) pt1 else body1.tpe.underlyingIfRepeated(isJava = false) - val sym = ctx.newPatternBoundSymbol(tree.name, symTp, tree.span) - if (pt == defn.ImplicitScrutineeTypeRef) sym.setFlag(Given) + val sym = ctx.newPatternBoundSymbol(name, symTp, tree.span) + if (pt == defn.ImplicitScrutineeTypeRef || tree.mods.is(Given)) sym.setFlag(Given) if (ctx.mode.is(Mode.InPatternAlternative)) ctx.error(i"Illegal variable ${sym.name} in pattern alternative", tree.sourcePos) - assignType(cpy.Bind(tree)(tree.name, body1), sym) + assignType(cpy.Bind(tree)(name, body1), sym) } } } diff --git a/docs/docs/internals/syntax.md b/docs/docs/internals/syntax.md index 1d8888698c58..a67d54565417 100644 --- a/docs/docs/internals/syntax.md +++ b/docs/docs/internals/syntax.md @@ -232,8 +232,8 @@ Quoted ::= ‘'’ ‘{’ Block ‘}’ ExprsInParens ::= ExprInParens {‘,’ ExprInParens} ExprInParens ::= PostfixExpr ‘:’ Type -- normal Expr allows only RefinedType here | Expr -ParArgumentExprs ::= ‘(’ ExprsInParens ‘)’ exprs - | ‘(’ [ExprsInParens ‘,’] PostfixExpr ‘:’ ‘_’ ‘*’ ‘)’ exprs :+ Typed(expr, Ident(wildcardStar)) +ParArgumentExprs ::= ‘(’ [‘given’] ExprsInParens ‘)’ exprs + | ‘(’ [ExprsInParens ‘,’] PostfixExpr ‘:’ ‘_’ ‘*’ ‘)’ exprs :+ Typed(expr, Ident(wildcardStar)) ArgumentExprs ::= ParArgumentExprs | [cnl] BlockExpr BlockExpr ::= ‘{’ CaseClauses | Block ‘}’ @@ -262,6 +262,7 @@ TypeCaseClause ::= ‘case’ InfixType ‘=>’ Type [nl] Pattern ::= Pattern1 { ‘|’ Pattern1 } Alternative(pats) Pattern1 ::= Pattern2 [‘:’ RefinedType] Bind(name, Typed(Ident(wildcard), tpe)) + | ‘given’ PatVar ‘:’ RefinedType Pattern2 ::= [id ‘@’] InfixPattern Bind(name, pat) InfixPattern ::= SimplePattern { id [cnl] SimplePattern } InfixOp(pat, op, pat) SimplePattern ::= PatVar Ident(wildcard) @@ -298,7 +299,7 @@ HkTypeParam ::= {Annotation} [‘+’ | ‘-’] (Id[HkTypeParamClause] | ClsParamClauses ::= {ClsParamClause} [[nl] ‘(’ [‘implicit’] ClsParams ‘)’] | {ClsParamClause} {GivenClsParamClause} ClsParamClause ::= [nl] ‘(’ ClsParams ‘)’ -GivenClsParamClause::= ‘given’ (‘(’ ClsParams ‘)’ | GivenTypes) +GivenClsParamClause::= ‘(’ ‘given’ (ClsParams | GivenTypes) ‘)’ ClsParams ::= ClsParam {‘,’ ClsParam} ClsParam ::= {Annotation} ValDef(mods, id, tpe, expr) -- point of mods on val/var [{Modifier} (‘val’ | ‘var’) | ‘inline’] Param @@ -308,7 +309,7 @@ Param ::= id ‘:’ ParamType [‘=’ Expr] DefParamClauses ::= {DefParamClause} [[nl] ‘(’ [‘implicit’] DefParams ‘)’] | {DefParamClause} {GivenParamClause} DefParamClause ::= [nl] ‘(’ DefParams ‘)’ -GivenParamClause ::= ‘given’ (‘(’ DefParams ‘)’ | GivenTypes) +GivenParamClause ::= ‘(’ ‘given’ (DefParams | GivenTypes) ‘)’ DefParams ::= DefParam {‘,’ DefParam} DefParam ::= {Annotation} [‘inline’] Param ValDef(mods, id, tpe, expr) -- point of mods at id. GivenTypes ::= AnnotType {‘,’ AnnotType} @@ -335,12 +336,16 @@ AccessQualifier ::= ‘[’ (id | ‘this’) ‘]’ Annotation ::= ‘@’ SimpleType {ParArgumentExprs} Apply(tpe, args) -Import ::= ‘import’ [‘given’] ImportExpr {‘,’ ImportExpr} -ImportExpr ::= StableId ‘.’ (id | ‘_’ | ImportSelectors) Import(expr, sels) -ImportSelectors ::= ‘{’ {ImportSelector ‘,’} FinalSelector ‘}’ -FinalSelector ::= ImportSelector Ident(name) - | ‘_’ [‘:’ Type] TypeBoundsTree(EmptyTree, tpt) -ImportSelector ::= id [‘=>’ id | ‘=>’ ‘_’] +Import ::= ‘import’ ImportExpr {‘,’ ImportExpr} +ImportExpr ::= StableId ‘.’ ImportSpec Import(expr, sels) +ImportSpec ::= id + | ‘_’ + | ‘given’ + | ‘{’ ImportSelectors) ‘}’ +ImportSelectors ::= [‘given’] id [‘=>’ id | ‘=>’ ‘_’] [‘,’ ImportSelectors] + | WildCardSelector {‘,’ WildCardSelector} +WildCardSelector ::= ‘given’ [‘as’ InfixType] + | ‘_' [‘:’ InfixType] Export ::= ‘export’ [‘given’] ImportExpr {‘,’ ImportExpr} ``` @@ -382,17 +387,15 @@ ClassConstr ::= [ClsTypeParamClause] [ConstrMods] ClsParamClauses ConstrMods ::= {Annotation} [AccessModifier] ObjectDef ::= id [Template] ModuleDef(mods, name, template) // no constructor EnumDef ::= id ClassConstr InheritClauses EnumBody EnumDef(mods, name, tparams, template) -GivenDef ::= [id] [DefTypeParamClause] GivenBody -GivenBody ::= [‘as’ ConstrApp {‘,’ ConstrApp }] {GivenParamClause} [TemplateBody] - | ‘as’ Type {GivenParamClause} ‘=’ Expr - | ‘(’ DefParam ‘)’ TemplateBody +GivenDef ::= [GivenSig (‘:’ | <:)] Type ‘=’ Expr + | [GivenSig ‘:’] [ConstrApp {‘,’ ConstrApp }] [TemplateBody] + | [GivenSig ‘:’] [DefTypeParamClause] DefParamClause TemplateBody +GivenSig ::= [id] [DefTypeParamClause] {GivenParamClause} Template ::= InheritClauses [TemplateBody] Template(constr, parents, self, stats) InheritClauses ::= [‘extends’ ConstrApps] [‘derives’ QualId {‘,’ QualId}] ConstrApps ::= ConstrApp {‘with’ ConstrApp} | ConstrApp {‘,’ ConstrApp} -ConstrApp ::= SimpleConstrApp - | ‘(’ SimpleConstrApp {‘given’ (PrefixExpr | ParArgumentExprs)} ‘)’ -SimpleConstrApp ::= AnnotType {ArgumentExprs} Apply(tp, args) +ConstrApp ::= AnnotType {ArgumentExprs} Apply(tp, args) ConstrExpr ::= SelfInvocation | ‘{’ SelfInvocation {semi BlockStat} ‘}’ SelfInvocation ::= ‘this’ ArgumentExprs {ArgumentExprs} diff --git a/docs/docs/reference/contextual/context-bounds.md b/docs/docs/reference/contextual/context-bounds.md index 2593bb355d43..b80285827a50 100644 --- a/docs/docs/reference/contextual/context-bounds.md +++ b/docs/docs/reference/contextual/context-bounds.md @@ -9,13 +9,13 @@ A context bound is a shorthand for expressing the common pattern of an implicit ```scala def maximum[T: Ord](xs: List[T]): T = xs.reduceLeft(max) ``` -A bound like `: Ord` on a type parameter `T` of a method or class indicates an implicit parameter `given Ord[T]`. The implicit parameter(s) generated from context bounds come last in the definition of the containing method or class. E.g., +A bound like `: Ord` on a type parameter `T` of a method or class indicates an implicit parameter `(given Ord[T])`. The implicit parameter(s) generated from context bounds come last in the definition of the containing method or class. E.g., ```scala -def f[T: C1 : C2, U: C3](x: T) given (y: U, z: V): R +def f[T: C1 : C2, U: C3](x: T)(given y: U, z: V): R ``` would expand to ```scala -def f[T, U](x: T) given (y: U, z: V) given C1[T], C2[T], C3[U]: R +def f[T, U](x: T)(given y: U, z: V)(given C1[T], C2[T], C3[U]): R ``` Context bounds can be combined with subtype bounds. If both are present, subtype bounds come first, e.g. ```scala diff --git a/docs/docs/reference/contextual/conversions.md b/docs/docs/reference/contextual/conversions.md index 255bde2fd665..6b1ccc8fbbb3 100644 --- a/docs/docs/reference/contextual/conversions.md +++ b/docs/docs/reference/contextual/conversions.md @@ -10,13 +10,13 @@ abstract class Conversion[-T, +U] extends (T => U) ``` For example, here is an implicit conversion from `String` to `Token`: ```scala -given as Conversion[String, Token] { +given Conversion[String, Token] { def apply(str: String): Token = new KeyWord(str) } ``` Using an alias this can be expressed more concisely as: ```scala -given as Conversion[String, Token] = new KeyWord(_) +given Conversion[String, Token] = new KeyWord(_) ``` An implicit conversion is applied automatically by the compiler in three situations: @@ -37,7 +37,7 @@ If such an instance `C` is given, the expression `e` is replaced by `C.apply(e)` primitive number types to subclasses of `java.lang.Number`. For instance, the conversion from `Int` to `java.lang.Integer` can be defined as follows: ```scala -given int2Integer as Conversion[Int, java.lang.Integer] = +given int2Integer: Conversion[Int, java.lang.Integer] = java.lang.Integer.valueOf(_) ``` @@ -59,9 +59,9 @@ object Completions { // // CompletionArg.fromStatusCode(statusCode) - given fromString as Conversion[String, CompletionArg] = Error(_) - given fromFuture as Conversion[Future[HttpResponse], CompletionArg] = Response(_) - given fromStatusCode as Conversion[Future[StatusCode], CompletionArg] = Status(_) + given fromString : Conversion[String, CompletionArg] = Error(_) + given fromFuture : Conversion[Future[HttpResponse], CompletionArg] = Response(_) + given fromStatusCode : Conversion[Future[StatusCode], CompletionArg] = Status(_) } import CompletionArg._ diff --git a/docs/docs/reference/contextual/delegates.md b/docs/docs/reference/contextual/delegates.md index 184eccb5410a..4ea3dbd4f543 100644 --- a/docs/docs/reference/contextual/delegates.md +++ b/docs/docs/reference/contextual/delegates.md @@ -13,12 +13,12 @@ trait Ord[T] { def (x: T) > (y: T) = compare(x, y) > 0 } -given IntOrd as Ord[Int] { +given intOrd: Ord[Int] { def compare(x: Int, y: Int) = if (x < y) -1 else if (x > y) +1 else 0 } -given ListOrd[T] as Ord[List[T]] given (ord: Ord[T]) { +given listOrd[T](given ord: Ord[T]): Ord[List[T]] { def compare(xs: List[T], ys: List[T]): Int = (xs, ys) match { case (Nil, Nil) => 0 @@ -30,19 +30,19 @@ given ListOrd[T] as Ord[List[T]] given (ord: Ord[T]) { } } ``` -This code defines a trait `Ord` with two given instances. `IntOrd` defines -a given for the type `Ord[Int]` whereas `ListOrd[T]` defines givens +This code defines a trait `Ord` with two given instances. `intOrd` defines +a given for the type `Ord[Int]` whereas `listOrd[T]` defines givens for `Ord[List[T]]` for all types `T` that come with a given instance for `Ord[T]` themselves. -The `given (ord: Ord[T])` clause in `ListOrd` defines an implicit parameter. -Given clauses are further explained in [given clauses](./given-clauses.md). +The `(given ord: Ord[T])` clause in `listOrd` defines an implicit parameter. +Given clauses are further explained in the [next section](./given-clauses.md). ## Anonymous Given Instances The name of a given instance can be left out. So the definitions of the last section can also be expressed like this: ```scala -given as Ord[Int] { ... } -given [T] as Ord[List[T]] given Ord[T] { ... } +given Ord[Int] { ... } +given [T](given Ord[T]): Ord[List[T]] { ... } ``` If the name of a given is missing, the compiler will synthesize a name from the type(s) in the `as` clause. @@ -51,7 +51,7 @@ the type(s) in the `as` clause. An alias can be used to define a given instance that is equal to some expression. E.g.: ```scala -given global as ExecutionContext = new ForkJoinPool() +given global: ExecutionContext = new ForkJoinPool() ``` This creates a given `global` of type `ExecutionContext` that resolves to the right hand side `new ForkJoinPool()`. @@ -60,8 +60,8 @@ returned for this and all subsequent accesses to `global`. Alias givens can be anonymous, e.g. ```scala -given as Position = enclosingTree.position -given as Context given (outer: Context) = outer.withOwner(currentOwner) +given Position = enclosingTree.position +given (given outer: Context): Context = outer.withOwner(currentOwner) ``` An alias given can have type parameters and given clauses just like any other given instance, but it can only implement a single type. @@ -78,14 +78,10 @@ Here is the new syntax of given instances, seen as a delta from the [standard co ``` TmplDef ::= ... | ‘given’ GivenDef -GivenDef ::= [id] [DefTypeParamClause] GivenBody -GivenBody ::= [‘as’ ConstrApp {‘,’ ConstrApp }] {GivenParamClause} [TemplateBody] - | ‘as’ Type {GivenParamClause} ‘=’ Expr -ConstrApp ::= SimpleConstrApp - | ‘(’ SimpleConstrApp {‘given’ (PrefixExpr | ParArgumentExprs)} ‘)’ -SimpleConstrApp ::= AnnotType {ArgumentExprs} -GivenParamClause ::= ‘given’ (‘(’ [DefParams] ‘)’ | GivenTypes) +GivenDef ::= [GivenSig (‘:’ | <:)] Type ‘=’ Expr + | [GivenSig ‘:’] [ConstrApp {‘,’ ConstrApp }] [TemplateBody] +GivenSig ::= [id] [DefTypeParamClause] {GivenParamClause} +GivenParamClause ::= ‘(’ ‘given’ (DefParams | GivenTypes) ‘)’ GivenTypes ::= AnnotType {‘,’ AnnotType} ``` -The identifier `id` can be omitted only if either the `as` part or the template body is present. -If the `as` part is missing, the template body must define at least one extension method. +The identifier `id` can be omitted only if some types are implemented or the template body defines at least one extension method. diff --git a/docs/docs/reference/contextual/derivation.md b/docs/docs/reference/contextual/derivation.md index 47f8ef51c307..fc4506c4e526 100644 --- a/docs/docs/reference/contextual/derivation.md +++ b/docs/docs/reference/contextual/derivation.md @@ -19,9 +19,9 @@ The `derives` clause generates the following given instances for the `Eq`, `Orde companion object of `Tree`, ```scala -given [T: Eq] as Eq[Tree[T]] = Eq.derived -given [T: Ordering] as Ordering[Tree] = Ordering.derived -given [T: Show] as Show[Tree] = Show.derived +given [T: Eq] : Eq[Tree[T]] = Eq.derived +given [T: Ordering] : Ordering[Tree] = Ordering.derived +given [T: Show] : Show[Tree] = Show.derived ``` We say that `Tree` is the _deriving type_ and that the `Eq`, `Ordering` and `Show` instances are _derived instances_. @@ -45,7 +45,7 @@ derivation support. /** the type being mirrored */ type MirroredType - + /** the type of the elements of the mirrored type */ type MirroredElemTypes @@ -130,7 +130,7 @@ Note the following properties of `Mirror` types, possibly parameterized, tuple type. Dotty's metaprogramming facilities can be used to work with these tuple types as-is, and higher level libraries can be built on top of them. + The methods `ordinal` and `fromProduct` are defined in terms of `MirroredMonoType` which is the type of kind-`*` - which is obtained from `MirroredType` by wildcarding its type parameters. + which is obtained from `MirroredType` by wildcarding its type parameters. ### Type classes supporting automatic deriving @@ -139,7 +139,7 @@ signature and implementation of a `derived` method for a type class `TC[_]` are following form, ```scala - def derived[T] given Mirror.Of[T]: TC[T] = ... + def derived[T](given Mirror.Of[T]): TC[T] = ... ``` That is, the `derived` method takes a given parameter of (some subtype of) type `Mirror` which defines the shape of @@ -175,7 +175,7 @@ we need to implement a method `Eq.derived` on the companion object of `Eq` that a `Mirror[T]`. Here is a possible implementation, ```scala -inline given derived[T] as Eq[T] given (m: Mirror.Of[T]) = { +inline given derived[T](given m: Mirror.Of[T]): Eq[T] = { val elemInstances = summonAll[m.MirroredElemTypes] // (1) inline m match { // (2) case s: Mirror.SumOf[T] => eqSum(s, elemInstances) @@ -256,7 +256,7 @@ trait Eq[T] { } object Eq { - given as Eq[Int] { + given Eq[Int] { def eqv(x: Int, y: Int) = x == y } @@ -281,7 +281,7 @@ object Eq { } } - inline given derived[T] as Eq[T] given (m: Mirror.Of[T]) = { + inline given derived[T](given m: Mirror.Of[T]): Eq[T] = { val elemInstances = summonAll[m.MirroredElemTypes] inline m match { case s: Mirror.SumOf[T] => eqSum(s, elemInstances) @@ -301,7 +301,7 @@ enum Opt[+T] derives Eq { object Test extends App { import Opt._ - val eqoi = the[Eq[Opt[Int]]] + val eqoi = summon[Eq[Opt[Int]]] assert(eqoi.eqv(Sm(23), Sm(23))) assert(!eqoi.eqv(Sm(23), Sm(13))) assert(!eqoi.eqv(Sm(23), Nn)) @@ -312,11 +312,11 @@ In this case the code that is generated by the inline expansion for the derived following, after a little polishing, ```scala -given derived$Eq[T] as Eq[Opt[T]] given (eqT: Eq[T]) = - eqSum(the[Mirror[Opt[T]]], +given derived$Eq[T](given eqT: Eq[T]): Eq[Opt[T]] = + eqSum(summon[Mirror[Opt[T]]], List( - eqProduct(the[Mirror[Sm[T]]], List(the[Eq[T]])) - eqProduct(the[Mirror[Nn.type]], Nil) + eqProduct(summon[Mirror[Sm[T]]], List(summon[Eq[T]])) + eqProduct(summon[Mirror[Nn.type]], Nil) ) ) ``` @@ -329,20 +329,20 @@ As a third example, using a higher level library such as shapeless the type clas `derived` method as, ```scala -given eqSum[A] as Eq[A] given (inst: => K0.CoproductInstances[Eq, A]) { +given eqSum[A](given inst: => K0.CoproductInstances[Eq, A]): Eq[A] { def eqv(x: A, y: A): Boolean = inst.fold2(x, y)(false)( [t] => (eqt: Eq[t], t0: t, t1: t) => eqt.eqv(t0, t1) ) } -given eqProduct[A] as Eq[A] given (inst: K0.ProductInstances[Eq, A]) { +given eqProduct[A](given inst: K0.ProductInstances[Eq, A]): Eq[A] { def eqv(x: A, y: A): Boolean = inst.foldLeft2(x, y)(true: Boolean)( [t] => (acc: Boolean, eqt: Eq[t], t0: t, t1: t) => Complete(!eqt.eqv(t0, t1))(false)(true) ) } -inline def derived[A] given (gen: K0.Generic[A]): Eq[A] = gen.derive(eqSum, eqProduct) +inline def derived[A](given gen: K0.Generic[A]): Eq[A] = gen.derive(eqSum, eqProduct) ``` The framework described here enables all three of these approaches without mandating any of them. @@ -354,7 +354,7 @@ change the code of the ADT itself. To do this, simply define an instance using as right-hand side. E.g, to implement `Ordering` for `Option` define, ```scala -given [T: Ordering] as Ordering[Option[T]] = Ordering.derived +given [T: Ordering] : Ordering[Option[T]] = Ordering.derived ``` Assuming the `Ordering.derived` method has a given parameter of type `Mirror[T]` it will be satisfied by the diff --git a/docs/docs/reference/contextual/extension-methods.md b/docs/docs/reference/contextual/extension-methods.md index 653eb0c98a20..1fe324dd5315 100644 --- a/docs/docs/reference/contextual/extension-methods.md +++ b/docs/docs/reference/contextual/extension-methods.md @@ -50,7 +50,7 @@ trait StringSeqOps { ``` We can make the extension method available by defining a given `StringSeqOps` instance, like this: ```scala -given ops1 as StringSeqOps +given ops1: StringSeqOps ``` Then ```scala @@ -155,7 +155,7 @@ def (xs: List[List[T]]) flattened [T] = xs.foldLeft[List[T]](Nil)(_ ++ _) def (x: T) + [T : Numeric](y: T): T = - the[Numeric[T]].plus(x, y) + summon[Numeric[T]].plus(x, y) ``` As usual, type parameters of the extension method follow the defined method name. Nevertheless, such type parameters can already be used in the preceding parameter clause. diff --git a/docs/docs/reference/contextual/given-clauses.md b/docs/docs/reference/contextual/given-clauses.md index 020fa95ef7e9..aa9c8b86ea33 100644 --- a/docs/docs/reference/contextual/given-clauses.md +++ b/docs/docs/reference/contextual/given-clauses.md @@ -12,15 +12,15 @@ repetitive arguments instead of the programmer having to write them explicitly. For example, with the [given instances](./delegates.md) defined previously, a maximum function that works for any arguments for which an ordering exists can be defined as follows: ```scala -def max[T](x: T, y: T) given (ord: Ord[T]): T = +def max[T](x: T, y: T)(given ord: Ord[T]): T = if (ord.compare(x, y) < 0) y else x ``` Here, `ord` is an _implicit parameter_ introduced with a `given` clause. The `max` method can be applied as follows: ```scala -max(2, 3) given IntOrd +max(2, 3)(given IntOrd) ``` -The `given IntOrd` part passes `IntOrd` as an argument for the `ord` parameter. But the point of +The `(given IntOrd)` part passes `IntOrd` as an argument for the `ord` parameter. But the point of implicit parameters is that this argument can also be left out (and it usually is). So the following applications are equally valid: ```scala @@ -35,65 +35,68 @@ mentioned explicitly at all, since it is used only in synthesized arguments for other implicit parameters. In that case one can avoid defining a parameter name and just provide its type. Example: ```scala -def maximum[T](xs: List[T]) given Ord[T]: T = +def maximum[T](xs: List[T])(given Ord[T]): T = xs.reduceLeft(max) ``` `maximum` takes an implicit parameter of type `Ord` only to pass it on as an inferred argument to `max`. The name of the parameter is left out. -Generally, implicit parameters may be defined either as a parameter list `(p_1: T_1, ..., p_n: T_n)` -or as a sequence of types, separated by commas. +Generally, implicit parameters may be defined either as a full parameter list `(given p_1: T_1, ..., p_n: T_n)` or just as a sequence of types `(given T_1, ..., T_n)`. +Vararg given parameters are not allowed. ## Inferring Complex Arguments Here are two other methods that have an implicit parameter of type `Ord[T]`: ```scala -def descending[T] given (asc: Ord[T]): Ord[T] = new Ord[T] { +def descending[T](given asc: Ord[T]): Ord[T] = new Ord[T] { def compare(x: T, y: T) = asc.compare(y, x) } -def minimum[T](xs: List[T]) given Ord[T] = - maximum(xs) given descending +def minimum[T](xs: List[T])(given Ord[T]) = + maximum(xs)(given descending) ``` The `minimum` method's right hand side passes `descending` as an explicit argument to `maximum(xs)`. With this setup, the following calls are all well-formed, and they all normalize to the last one: ```scala minimum(xs) -maximum(xs) given descending -maximum(xs) given (descending given ListOrd) -maximum(xs) given (descending given (ListOrd given IntOrd)) +maximum(xs)(given descending) +maximum(xs)(given descending(given ListOrd)) +maximum(xs)(given descending(given ListOrd(given IntOrd))) ``` ## Multiple Given Clauses -There can be several given clauses in a definition. Example: +There can be several `given` parameter clauses in a definition and `given` parameter clauses can be freely +mixed with normal ones. Example: ```scala -def f given (u: Universe) given (x: u.Context) = ... +def f(u: Universe)(given c: u.Context)(given s: ctx.Symbol, k: ctx.Kind) = ... ``` -However, all `given` clauses in a definition must come after any normal parameter clauses. Multiple given clauses are matched left-to-right in applications. Example: ```scala -given global as Universe { type Context = ... } -given ctx as global.Context { ... } +object global extends Universe { type Context = ... } +given ctx : global.Context { type Symbol = ...; type Kind = ... } +given sym : ctx.Symbol +given kind : ctx.Kind ``` Then the following calls are all valid (and normalize to the last one) ```scala f -(f given global) -(f given global) given ctx +f(global) +f(global)(given ctx) +f(global)(given ctx)(given sym, kind) ``` -But `f given ctx` would give a type error. +But `f(global)(given sym, kind)` would give a type error. ## Summoning Instances -A method `the` in `Predef` returns the given instance of a specific type. For example, +The method `summon` in `Predef` returns the given instance of a specific type. For example, the given instance for `Ord[List[Int]]` is produced by ```scala -the[Ord[List[Int]]] // reduces to ListOrd given IntOrd +summon[Ord[List[Int]]] // reduces to ListOrd given IntOrd ``` -The `the` method is simply defined as the (non-widening) identity function over a implicit parameter. +The `summon` method is simply defined as the (non-widening) identity function over a implicit parameter. ```scala -def the[T] given (x: T): x.type = x +def summon[T](given x: T): x.type = x ``` ## Syntax @@ -102,12 +105,12 @@ Here is the new syntax of parameters and arguments seen as a delta from the [sta ``` ClsParamClauses ::= ... | {ClsParamClause} {GivenClsParamClause} -GivenClsParamClause ::= ‘given’ (‘(’ ClsParams ‘)’ | GivenTypes) +GivenClsParamClause ::= ‘(’ ‘given’ (ClsParams | GivenTypes) ‘)’ DefParamClauses ::= ... | {DefParamClause} {GivenParamClause} -GivenParamClause ::= ‘given’ (‘(’ DefParams ‘)’ | GivenTypes) +GivenParamClause ::= ‘(’ ‘given’ (DefParams | GivenTypes) ‘)’ GivenTypes ::= AnnotType {‘,’ AnnotType} -InfixExpr ::= ... - | InfixExpr ‘given’ (InfixExpr | ParArgumentExprs) +ParArgumentExprs ::= ... + | ‘(’ ‘given’ ExprsInParens ‘)’ ``` diff --git a/docs/docs/reference/contextual/implicit-by-name-parameters.md b/docs/docs/reference/contextual/implicit-by-name-parameters.md index fa850454d623..e98ad96fca8b 100644 --- a/docs/docs/reference/contextual/implicit-by-name-parameters.md +++ b/docs/docs/reference/contextual/implicit-by-name-parameters.md @@ -10,16 +10,16 @@ trait Codec[T] { def write(x: T): Unit } -given intCodec as Codec[Int] = ??? +given intCodec: Codec[Int] = ??? -given optionCodec[T] as Codec[Option[T]] given (ev: => Codec[T]) { +given optionCodec[T](given ev: => Codec[T]): Codec[Option[T]] { def write(xo: Option[T]) = xo match { case Some(x) => ev.write(x) case None => } } -val s = the[Codec[Option[Int]]] +val s = summon[Codec[Option[Int]]] s.write(Some(33)) s.write(None) @@ -36,7 +36,7 @@ The precise steps for synthesizing an argument for an implicit by-name parameter 1. Create a new given instance of type `T`: ```scala - given lv as T = ??? + given lv: T = ??? ``` where `lv` is an arbitrary fresh name. @@ -46,7 +46,7 @@ The precise steps for synthesizing an argument for an implicit by-name parameter ```scala - { given lv as T = E; lv } + { given lv: T = E; lv } ``` Otherwise, return `E` unchanged. @@ -54,7 +54,7 @@ The precise steps for synthesizing an argument for an implicit by-name parameter In the example above, the definition of `s` would be expanded as follows. ```scala -val s = the[Test.Codec[Option[Int]]]( +val s = summon[Test.Codec[Option[Int]]]( optionCodec[Int](intCodec) ) ``` diff --git a/docs/docs/reference/contextual/implicit-function-types-spec.md b/docs/docs/reference/contextual/implicit-function-types-spec.md index 148f857fabee..d7ef525af829 100644 --- a/docs/docs/reference/contextual/implicit-function-types-spec.md +++ b/docs/docs/reference/contextual/implicit-function-types-spec.md @@ -6,12 +6,12 @@ title: "Implicit Function Types - More Details" ## Syntax Type ::= ... - | `given' FunArgTypes `=>' Type + | `(' `given' FunArgTypes `)' `=>' Type Expr ::= ... - | `given' FunParams `=>' Expr + | `(' `given' FunParams `)' `=>' Expr Implicit function types associate to the right, e.g. -`given S => given T => U` is the same as `given S => (given T => U)`. +`(given S) => (given T) => U` is the same as `(given S) => ((given T) => U)`. ## Implementation @@ -22,13 +22,13 @@ methods with implicit parameters. Specifically, the `N`-ary function type ```scala package scala trait ImplicitFunctionN[-T1 , ... , -TN, +R] { - def apply given (x1: T1 , ... , xN: TN): R + def apply(given x1: T1 , ... , xN: TN): R } ``` Implicit function types erase to normal function types, so these classes are generated on the fly for typechecking, but not realized in actual code. -Implicit function literals `given (x1: T1, ..., xn: Tn) => e` map +Implicit function literals `(given x1: T1, ..., xn: Tn) => e` map implicit parameters `xi` of types `Ti` to the result of evaluating the expression `e`. The scope of each implicit parameter `xi` is `e`. The parameters must have pairwise distinct names. @@ -45,12 +45,9 @@ The implicit function literal is evaluated as the instance creation expression ```scala new scala.ImplicitFunctionN[T1, ..., Tn, T] { - def apply given (x1: T1, ..., xn: Tn): T = e + def apply(given x1: T1, ..., xn: Tn): T = e } ``` -In the case of a single untyped parameter, `given (x) => e` can be -abbreviated to `given x => e`. - An implicit parameter may also be a wildcard represented by an underscore `_`. In that case, a fresh name for the parameter is chosen arbitrarily. @@ -58,7 +55,7 @@ Note: The closing paragraph of the [Anonymous Functions section](https://www.scala-lang.org/files/archive/spec/2.12/06-expressions.html#anonymous-functions) of Scala 2.12 is subsumed by implicit function types and should be removed. -Implicit function literals `given (x1: T1, ..., xn: Tn) => e` are +Implicit function literals `(given x1: T1, ..., xn: Tn) => e` are automatically created for any expression `e` whose expected type is `scala.ImplicitFunctionN[T1, ..., Tn, R]`, unless `e` is itself a implicit function literal. This is analogous to the automatic diff --git a/docs/docs/reference/contextual/implicit-function-types.md b/docs/docs/reference/contextual/implicit-function-types.md index b863871331e5..63d8f764de20 100644 --- a/docs/docs/reference/contextual/implicit-function-types.md +++ b/docs/docs/reference/contextual/implicit-function-types.md @@ -7,23 +7,23 @@ _Implicit functions_ are functions with (only) implicit parameters. Their types are _implicit function types_. Here is an example of an implicit function type: ```scala -type Executable[T] = given ExecutionContext => T +type Executable[T] = (given ExecutionContext) => T ``` An implicit function is applied to synthesized arguments, in the same way a method with a given clause is applied. For instance: ```scala - given ec as ExecutionContext = ... + given ec: ExecutionContext = ... def f(x: Int): Executable[Int] = ... - f(2) given ec // explicit argument + f(2)(given ec) // explicit argument f(2) // argument is inferred ``` Conversely, if the expected type of an expression `E` is an implicit function type -`given (T_1, ..., T_n) => U` and `E` is not already an +`(given T_1, ..., T_n) => U` and `E` is not already an implicit function literal, `E` is converted to an implicit function literal by rewriting to ```scala - given (x_1: T1, ..., x_n: Tn) => E + (given x_1: T1, ..., x_n: Tn) => E ``` where the names `x_1`, ..., `x_n` are arbitrary. This expansion is performed before the expression `E` is typechecked, which means that `x_1`, ..., `x_n` @@ -38,11 +38,11 @@ For example, continuing with the previous definitions, ```scala def g(arg: Executable[Int]) = ... - g(22) // is expanded to g(given ev => 22) + g(22) // is expanded to g((given ev) => 22) - g(f(2)) // is expanded to g(given ev => (f(2) given ev)) + g(f(2)) // is expanded to g((given ev) => f(2)(given ev)) - g(given ctx => f(22) given ctx) // is left as it is + g((given ctx) => f(22)(given ctx)) // is left as it is ``` ### Example: Builder Pattern @@ -82,32 +82,32 @@ Then, the `table`, `row` and `cell` constructor methods can be defined with implicit function types as parameters to avoid the plumbing boilerplate that would otherwise be necessary. ```scala - def table(init: given Table => Unit) = { - given t as Table + def table(init: (given Table) => Unit) = { + given t: Table init t } - def row(init: given Row => Unit) given (t: Table) = { - given r as Row + def row(init: (given Row) => Unit)(given t: Table) = { + given r: Row init t.add(r) } - def cell(str: String) given (r: Row) = + def cell(str: String)(given r: Row) = r.add(new Cell(str)) ``` With that setup, the table construction code above compiles and expands to: ```scala - table { given ($t: Table) => - row { given ($r: Row) => - cell("top left") given $r - cell("top right") given $r - } given $t - row { given ($r: Row) => - cell("bottom left") given $r - cell("bottom right") given $r - } given $t + table { (given $t: Table) => + row { (given $r: Row) => + cell("top left")(given $r) + cell("top right")(given $r) + } (given $t) + row { (given $r: Row) => + cell("bottom left")(given $r) + cell("bottom right")(given $r) + } (given $t) } ``` ### Example: Postconditions @@ -118,10 +118,10 @@ As a larger example, here is a way to define constructs for checking arbitrary p object PostConditions { opaque type WrappedResult[T] = T - def result[T] given (r: WrappedResult[T]): T = r + def result[T](given r: WrappedResult[T]): T = r - def (x: T) ensuring[T](condition: given WrappedResult[T] => Boolean): T = { - assert(condition given x) + def (x: T) ensuring[T](condition: (given WrappedResult[T]) => Boolean): T = { + assert(condition(given x)) x } } @@ -129,7 +129,7 @@ import PostConditions.{ensuring, result} val s = List(1, 2, 3).sum.ensuring(result == 6) ``` -**Explanations**: We use an implicit function type `given WrappedResult[T] => Boolean` +**Explanations**: We use an implicit function type `(given WrappedResult[T]) => Boolean` as the type of the condition of `ensuring`. An argument to `ensuring` such as `(result == 6)` will therefore have a given instance of type `WrappedResult[T]` in scope to pass along to the `result` method. `WrappedResult` is a fresh type, to make sure diff --git a/docs/docs/reference/contextual/import-delegate.md b/docs/docs/reference/contextual/import-delegate.md index c9be556de9ef..ddf1bdaead61 100644 --- a/docs/docs/reference/contextual/import-delegate.md +++ b/docs/docs/reference/contextual/import-delegate.md @@ -1,29 +1,34 @@ --- layout: doc-page -title: "Given Imports" +title: "Import Given" --- -A special form of import is used to import given instances. Example: +A special form of import wildcard selector is used to import given instances. Example: ```scala object A { class TC - given tc as TC - def f where TC = ??? + given tc: TC + def f(given TC) = ??? } object B { import A._ - import given A._ + import A.given } ``` In the code above, the `import A._` clause of object `B` will import all members -of `A` _except_ the given instance `tc`. Conversely, the second import `import given A._` will import _only_ that given instance. +of `A` _except_ the given instance `tc`. Conversely, the second import `import A.given` will import _only_ that given instance. +The two import clauses can also be merged into one: +```scala +object B + import A.{given, _} +``` -Generally, a normal import clause brings definitions other than given instances into scope whereas a `given` import brings only given instances into scope. +Generally, a normal wildcard selector `_` brings all definitions other than given instances into scope whereas a `given` selector brings all given instances into scope. There are two main benefits arising from these rules: - It is made clearer where givens in scope are coming from. - In particular, it is not possible to hide imported givens in a long list of regular imports. + In particular, it is not possible to hide imported givens in a long list of regular wildcard imports. - It enables importing all givens without importing anything else. This is particularly important since givens can be anonymous, so the usual recourse of using named imports is not @@ -34,32 +39,32 @@ There are two main benefits arising from these rules: Since givens can be anonymous it is not always practical to import them by their name, and wildcard imports are typically used instead. By-type imports provide a more specific alternative to wildcard imports, which makes it clearer what is imported. Example: ```scala -import given A.{_: TC} +import A.{given TC} ``` This imports any given in `A` that has a type which conforms to `TC`. Importing givens of several types `T1,...,Tn` -is expressed by bounding with a union type. +is expressed by multiple `given` selectors. ``` -import given A.{_: T1 | ... | Tn} +import A.{given T1, ..., given Tn} ``` -Importing all instances of a parameterized if expressed by wildcard arguments. +Importing all given instances of a parameterized type is expressed by wildcard arguments. For instance, assuming the object ```scala object Instances { - given intOrd as Ordering[Int] - given [T: Ordering] listOrd as Ordering[List[T]] - given ec as ExecutionContext = ... - given im as Monoid[Int] + given intOrd: Ordering[Int] + given [T: Ordering] listOrd: Ordering[List[T]] + given ec: ExecutionContext = ... + given im: Monoid[Int] } ``` the import ```scala -import given Instances.{_: Ordering[?] | ExecutionContext} +import Instances.{given Ordering[?], given ExecutionContext} ``` would import the `intOrd`, `listOrd`, and `ec` instances but leave out the `im` instance, since it fits none of the specified bounds. By-type imports can be mixed with by-name imports. If both are present in an import clause, by-type imports come last. For instance, the import clause ```scala -import given Instances.{im, _: Ordering[?]} +import Instances.{im, given Ordering[?]} ``` would import `im`, `intOrd`, and `listOrd` but leave out `ec`. @@ -77,21 +82,35 @@ leaves the `isPrimary` method alone. ### Migration -The rules for imports given above have the consequence that a library +The rules for imports stated above have the consequence that a library would have to migrate in lockstep with all its users from old style implicits and normal imports to given instances and imports. The following modifications avoid this hurdle to migration. - 1. An "import given" also brings old style implicits into scope. So, in Scala 3.0 - an old-style implicit definition can be brought into scope either by a normal import or by an import given. + 1. A `given` import selector also brings old style implicits into scope. So, in Scala 3.0 + an old-style implicit definition can be brought into scope either by a `_` wildcard import or by a `given` import. - 2. In Scala 3.1, old-style implicits accessed through a normal import - will give a deprecation warning. + 2. In Scala 3.1, old-style implicits accessed through a `_` wildcard import will give a deprecation warning. - 3. In some version after 3.1, old-style implicits accessed through a normal import - will give a compiler error. + 3. In some version after 3.1, old-style implicits accessed through a `_` wildcard import will give a compiler error. -These rules mean that library users can use `import given` to access old-style implicits in Scala 3.0, +These rules mean that library users can use `given` imports to access old-style implicits in Scala 3.0, and will be gently nudged and then forced to do so in later versions. Libraries can then switch to representation clauses once their user base has migrated. + +### Syntax + +``` +Import ::= ‘import’ ImportExpr {‘,’ ImportExpr} +ImportExpr ::= StableId ‘.’ ImportSpec +ImportSpec ::= id + | ‘_’ + | ‘given’ + | ‘{’ ImportSelectors) ‘}’ +ImportSelectors ::= id [‘=>’ id | ‘=>’ ‘_’] [‘,’ ImportSelectors] + | WildCardSelector {‘,’ WildCardSelector} +WildCardSelector ::= ‘given’ [InfixType] + | ‘_' [‘:’ InfixType] +Export ::= ‘export’ ImportExpr {‘,’ ImportExpr} +``` \ No newline at end of file diff --git a/docs/docs/reference/contextual/multiversal-equality.md b/docs/docs/reference/contextual/multiversal-equality.md index ee5a76641704..434486d8cc07 100644 --- a/docs/docs/reference/contextual/multiversal-equality.md +++ b/docs/docs/reference/contextual/multiversal-equality.md @@ -33,7 +33,7 @@ class T derives Eql ``` Alternatively, one can also provide an `Eql` given instance directly, like this: ```scala -given as Eql[T, T] = Eql.derived +given Eql[T, T] = Eql.derived ``` This definition effectively says that values of type `T` can (only) be compared to other values of type `T` when using `==` or `!=`. The definition @@ -59,10 +59,10 @@ definitions below make values of type `A` and type `B` comparable with each other, but not comparable to anything else: ```scala -given as Eql[A, A] = Eql.derived -given as Eql[B, B] = Eql.derived -given as Eql[A, B] = Eql.derived -given as Eql[B, A] = Eql.derived +given Eql[A, A] = Eql.derived +given Eql[B, B] = Eql.derived +given Eql[A, B] = Eql.derived +given Eql[B, A] = Eql.derived ``` The `scala.Eql` object defines a number of `Eql` givens that together define a rule book for what standard types can be compared (more details below). @@ -97,7 +97,7 @@ class Box[T](x: T) derives Eql By the usual rules if [typeclass derivation](./derivation.md), this generates the following `Eql` instance in the companion object of `Box`: ```scala -given [T, U] as Eql[Box[T], Box[U]] given Eql[T, U] = Eql.derived +given [T, U](given Eql[T, U]) : Eql[Box[T], Box[U]] = Eql.derived ``` That is, two boxes are comparable with `==` or `!=` if their elements are. Examples: ```scala @@ -175,7 +175,7 @@ This generic version of `contains` is the one used in the current (Scala 2.12) v It looks different but it admits exactly the same applications as the `contains(x: Any)` definition we started with. However, we can make it more useful (i.e. restrictive) by adding an `Eql` parameter: ```scala - def contains[U >: T](x: U) given Eql[T, U]: Boolean // (1) + def contains[U >: T](x: U)(given Eql[T, U]): Boolean // (1) ``` This version of `contains` is equality-safe! More precisely, given `x: T`, `xs: List[T]` and `y: U`, then `xs.contains(y)` is type-correct if and only if @@ -183,7 +183,7 @@ This version of `contains` is equality-safe! More precisely, given Unfortunately, the crucial ability to "lift" equality type checking from simple equality and pattern matching to arbitrary user-defined operations gets lost if we restrict ourselves to an equality class with a single type parameter. Consider the following signature of `contains` with a hypothetical `Eql1[T]` type class: ```scala - def contains[U >: T](x: U) given Eql1[U]: Boolean // (2) + def contains[U >: T](x: U)(given Eql1[U]): Boolean // (2) ``` This version could be applied just as widely as the original `contains(x: Any)` method, since the `Eql1[Any]` fallback is always available! So we have gained nothing. What got lost in the transition to a single parameter type class was the original rule that `Eql[A, B]` is available only if neither `A` nor `B` have a reflexive `Eql` given. That rule simply cannot be expressed if there is a single type parameter for `Eql`. diff --git a/docs/docs/reference/contextual/relationship-implicits.md b/docs/docs/reference/contextual/relationship-implicits.md index 396da6d8c248..51825dd25798 100644 --- a/docs/docs/reference/contextual/relationship-implicits.md +++ b/docs/docs/reference/contextual/relationship-implicits.md @@ -13,7 +13,7 @@ Given instances can be mapped to combinations of implicit objects, classes and i 1. Given instances without parameters are mapped to implicit objects. E.g., ```scala - given IntOrd as Ord[Int] { ... } + given intOrd: Ord[Int] { ... } ``` maps to ```scala @@ -21,7 +21,7 @@ Given instances can be mapped to combinations of implicit objects, classes and i ``` 2. Parameterized given instances are mapped to combinations of classes and implicit methods. E.g., ```scala - given ListOrd[T] as Ord[List[T]] given (ord: Ord[T]) { ... } + given listOrd[T](given (ord: Ord[T]): Ord[List[T]] { ... } ``` maps to ```scala @@ -35,10 +35,10 @@ Given instances can be mapped to combinations of implicit objects, classes and i Examples: ```scala - given global as ExecutionContext = new ForkJoinContext() + given global: ExecutionContext = new ForkJoinContext() val ctx: Context - given as Context = ctx + given Context = ctx ``` would map to ```scala @@ -50,14 +50,14 @@ Given instances can be mapped to combinations of implicit objects, classes and i Anonymous given instances get compiler synthesized names, which are generated in a reproducible way from the implemented type(s). For example, if the names of the `IntOrd` and `ListOrd` givens above were left out, the following names would be synthesized instead: ```scala - given Ord_Int_given as Ord[Int] { ... } - given Ord_List_given[T] as Ord[List[T]] { ... } + given given_Ord_Int : Ord[Int] { ... } + given given_Ord_List[T] : Ord[List[T]] { ... } ``` The synthesized type names are formed from + - the prefix `given_`, - the simple name(s) of the implemented type(s), leaving out any prefixes, - - the simple name(s) of the toplevel argument type constructors to these types - - the suffix `_given`. + - the simple name(s) of the toplevel argument type constructors to these types. Anonymous given instances that define extension methods without also implementing a type get their name from the name of the first extension method and the toplevel type @@ -67,13 +67,13 @@ constructor of its first parameter. For example, the given instance def (xs: List[T]) second[T] = ... } ``` -gets the synthesized name `second_of_List_T_given`. +gets the synthesized name `given_second_of_List_T`. ### Given Clauses Given clauses corresponds largely to Scala-2's implicit parameter clauses. E.g. ```scala - def max[T](x: T, y: T) given (ord: Ord[T]): T + def max[T](x: T, y: T)(given ord: Ord[T]): T ``` would be written ```scala @@ -81,7 +81,7 @@ would be written ``` in Scala 2. The main difference concerns applications of such parameters. Explicit arguments to parameters of given clauses _must_ be written using `given`, -mirroring the definition syntax. E.g, `max(2, 3) given IntOrd`. +mirroring the definition syntax. E.g, `max(2, 3)(given IntOrd`). Scala 2 uses normal applications `max(2, 3)(IntOrd)` instead. The Scala 2 syntax has some inherent ambiguities and restrictions which are overcome by the new syntax. For instance, multiple implicit parameter lists are not available in the old syntax, even though they can be simulated using auxiliary objects in the "Aux" pattern. The `the` method corresponds to `implicitly` in Scala 2. @@ -134,7 +134,7 @@ Implicit conversion methods in Scala 2 can be expressed as given instances of th ``` one can write ```scala - given stringToToken as Conversion[String, Token] { + given stringToToken: Conversion[String, Token] { def apply(str: String): Token = new KeyWord(str) } ``` @@ -153,7 +153,7 @@ E.g., Scala 2's can be expressed in Dotty as ```scala lazy val pos: Position = tree.sourcePos - given as Position = pos + given Position = pos ``` ### Abstract Implicits @@ -165,7 +165,7 @@ An abstract implicit `val` or `def` in Scala 2 can be expressed in Dotty using a can be expressed in Dotty as ```scala def symDeco: SymDeco - given as SymDeco = symDeco + given SymDeco = symDeco ``` ## Implementation Status and Timeline diff --git a/docs/docs/reference/contextual/typeclasses.md b/docs/docs/reference/contextual/typeclasses.md index 435320bbe4f0..a605bb72fd56 100644 --- a/docs/docs/reference/contextual/typeclasses.md +++ b/docs/docs/reference/contextual/typeclasses.md @@ -17,15 +17,15 @@ trait Monoid[T] extends SemiGroup[T] { def unit: T } object Monoid { - def apply[T] given Monoid[T] = the[Monoid[T]] + def apply[T](given Monoid[T]) = summon[Monoid[T]] } -given as Monoid[String] { +given Monoid[String] { def (x: String) combine (y: String): String = x.concat(y) def unit: String = "" } -given as Monoid[Int] { +given Monoid[Int] { def (x: Int) combine (y: Int): Int = x + y def unit: Int = 0 } @@ -48,14 +48,14 @@ trait Monad[F[_]] extends Functor[F] { def pure[A](x: A): F[A] } -given ListMonad as Monad[List] { +given listMonad: Monad[List] { def (xs: List[A]) flatMap [A, B] (f: A => List[B]): List[B] = xs.flatMap(f) def pure[A](x: A): List[A] = List(x) } -given ReaderMonad[Ctx] as Monad[[X] =>> Ctx => X] { +given readerMonad[Ctx]: Monad[[X] =>> Ctx => X] { def (r: Ctx => A) flatMap [A, B] (f: A => Ctx => B): Ctx => B = ctx => f(r(ctx))(ctx) def pure[A](x: A): Ctx => A = diff --git a/docs/docs/reference/metaprogramming/inline.md b/docs/docs/reference/metaprogramming/inline.md index 9b12b9e02d30..39a0157bd94f 100644 --- a/docs/docs/reference/metaprogramming/inline.md +++ b/docs/docs/reference/metaprogramming/inline.md @@ -406,7 +406,7 @@ inline def fail(p1: => Any) = { fail(indentity("foo")) // error: failed on: indentity("foo") ``` -## Implicit Matches +## Summoning Implicits Selectively It is foreseen that many areas of typelevel programming can be done with rewrite methods instead of implicits. But sometimes implicits are unavoidable. The @@ -441,27 +441,25 @@ There are some proposals to improve the situation in specific areas, for instance by allowing more elaborate schemes to specify priorities. But they all keep the viral nature of implicit search programs based on logic programming. -By contrast, the new `implicit match` construct makes implicit search available +By contrast, the new `summonFrom` construct makes implicit search available in a functional context. To solve the problem of creating the right set, one would use it as follows: ```scala -inline def setFor[T]: Set[T] = implicit match { - case ord: Ordering[T] => new TreeSet[T] - case _ => new HashSet[T] +import scala.compiletime.summonFrom + +inline def setFor[T]: Set[T] = summonFrom match { + case given ord: Ordering[T] => new TreeSet[T] + case _ => new HashSet[T] } ``` -An implicit match uses the `implicit` keyword in the place of the scrutinee. Its -patterns are type ascriptions of the form `identifier : Type`. +A `summonFrom` call takes a pattern matching closure as argument. All patterns +in the closure are type ascriptions of the form `identifier : Type`. Patterns are tried in sequence. The first case with a pattern `x: T` such that -an implicit value of type `T` can be summoned is chosen. The variable `x` is -then bound to the implicit value for the remainder of the case. It can in turn -be used as an implicit in the right hand side of the case. It is an error if one -of the tested patterns gives rise to an ambiguous implicit search. +an implicit value of type `T` can be summoned is chosen. If the pattern is prefixed +with `given`, the variable `x` is bound to the implicit value for the remainder of the case. It can in turn be used as an implicit in the right hand side of the case. It is an error if one of the tested patterns gives rise to an ambiguous implicit search. -An implicit matches is considered to be a special kind of a inline match. This -means it can only occur in the body of an inline method, and it must be reduced -at compile time. +`summonFrom` applications must be reduced at compile time. Consequently, if we summon an `Ordering[String]` the code above will return a new instance of `TreeSet[String]`. @@ -472,7 +470,7 @@ the[Ordering[String]] println(setFor[String].getClass) // prints class scala.collection.immutable.TreeSet ``` -**Note** implicit matches can raise ambiguity errors. Consider the following +**Note** `summonFrom` applications can raise ambiguity errors. Consider the following code with two implicit values in scope of type `A`. The single pattern match case of the implicit match with type ascription of an `A` raises the ambiguity error. @@ -482,13 +480,12 @@ class A implicit val a1: A = new A implicit val a2: A = new A -inline def f: Any = implicit match { +inline def f: Any = summonFrom { case _: A => ??? // error: ambiguous implicits } ``` ### Reference -For more info, see [PR #4927](https://github.com/lampepfl/dotty/pull/4768), -which explains how inline methods can be used for typelevel programming and code -specialization. +For more info, see [PR #4768](https://github.com/lampepfl/dotty/pull/4768), +which explains how `summonFrom`'s predecessor (implicit matches) can be used for typelevel programming and code specialization and [PR #7201](https://github.com/lampepfl/dotty/pull/7201) which explains the new `summonFrom` syntax. diff --git a/library/src/scala/compiletime/package.scala b/library/src/scala/compiletime/package.scala index 7152c2b110bd..b2bf5e84853d 100644 --- a/library/src/scala/compiletime/package.scala +++ b/library/src/scala/compiletime/package.scala @@ -37,5 +37,20 @@ package object compiletime { inline def constValue[T]: T = ??? + /** Summons first given matching one of the listed cases. E.g. in + * + * given B { ... } + * + * summonFrom { + * case given A => 1 + * case given B => 2 + * case given C => 3 + * case _ => 4 + * } + * + * the returned value would be `2`. + */ + inline def summonFrom(f: Nothing => Any) <: Any = ??? + type S[X <: Int] <: Int } diff --git a/library/src/scala/tasty/reflect/Core.scala b/library/src/scala/tasty/reflect/Core.scala index ebddcc878ce1..36bb20ebc8b7 100644 --- a/library/src/scala/tasty/reflect/Core.scala +++ b/library/src/scala/tasty/reflect/Core.scala @@ -216,7 +216,7 @@ trait Core { /** Tree representing a pattern match `x match { ... }` in the source code */ type Match = internal.Match - /** Tree representing a pattern match `delegate match { ... }` in the source code */ + /** Tree representing a pattern match `delegate match { ... }` in the source code */ // TODO: drop type ImpliedMatch = internal.ImpliedMatch /** Tree representing a try catch `try x catch { ... } finally { ... }` in the source code */ diff --git a/tests/neg/cannot-reduce-delegate-match.check b/tests/neg/cannot-reduce-delegate-match.check deleted file mode 100644 index c46031f5b5ba..000000000000 --- a/tests/neg/cannot-reduce-delegate-match.check +++ /dev/null @@ -1,8 +0,0 @@ --- Error: tests/neg/cannot-reduce-delegate-match.scala:4:13 ------------------------------------------------------------ -4 | delegate match { // error - | ^ - | cannot reduce delegate match with - | patterns : case evidence$1 @ _:Int - | This location is in code that was inlined at cannot-reduce-delegate-match.scala:13 -5 | case _: Int => -6 | } diff --git a/tests/neg/cannot-reduce-delegate-match.scala b/tests/neg/cannot-reduce-summonFrom.scala similarity index 74% rename from tests/neg/cannot-reduce-delegate-match.scala rename to tests/neg/cannot-reduce-summonFrom.scala index 9c8a78fa9f58..ba3b1cdff26d 100644 --- a/tests/neg/cannot-reduce-delegate-match.scala +++ b/tests/neg/cannot-reduce-summonFrom.scala @@ -1,7 +1,7 @@ object Test { inline def bar() = - delegate match { // error + compiletime.summonFrom { // error case _: Int => } diff --git a/tests/neg/given-pattern.scala b/tests/neg/given-pattern.scala new file mode 100644 index 000000000000..9902a548f633 --- /dev/null +++ b/tests/neg/given-pattern.scala @@ -0,0 +1,14 @@ + + +class Test { + import scala.collection.immutable.{TreeSet, HashSet} + + def f2[T](x: Ordering[T]) = { + val (given y: Ordering[T]) = x + new TreeSet[T] // error: no implicit ordering defined for T + } + def f3[T](x: Ordering[T]) = { + val given y: Ordering[T] = x // error: pattern expected + new TreeSet[T] // error: no implicit ordering defined for T + } +} \ No newline at end of file diff --git a/tests/neg/implicitMatch-syntax.scala b/tests/neg/implicitMatch-syntax.scala deleted file mode 100644 index 3e5f8969456a..000000000000 --- a/tests/neg/implicitMatch-syntax.scala +++ /dev/null @@ -1,33 +0,0 @@ -object Test { - import collection.immutable.TreeSet - import collection.immutable.HashSet - - inline def f1[T] = delegate delegate match { // error: illegal modifier // error: ';' expected, but 'match' found // error: Declaration of method f1 not allowed here - case ord: Ordered[T] => new TreeSet[T] - case _ => new HashSet[T] - - } - - inline def f2[T] = delegate erased match { // error: illegal modifier // error: illegal modifier // error: Declaration of method f1 not allowed here - case ord: Ordered[T] => new TreeSet[T] - case _ => new HashSet[T] - } - - inline def f3[T] = erased delegate match { // error: illegal modifier - case ord: Ordered[T] => new TreeSet[T] - case _ => new HashSet[T] - } - - inline def f4() = delegate match { - case Nil => ??? // error: not a legal pattern - case x :: xs => ??? // error: not a legal pattern - } - - inline def f5[T] = locally { delegate match { // Ok - case _ => new HashSet[T] - }} - - def f6[T] = delegate match { // error: delegate match cannot be used here - case _ => new HashSet[T] - } -} \ No newline at end of file diff --git a/tests/neg/implicit-match-ambiguous-bind.scala b/tests/neg/summonFrom-ambiguous-bind.scala similarity index 82% rename from tests/neg/implicit-match-ambiguous-bind.scala rename to tests/neg/summonFrom-ambiguous-bind.scala index fdac7f8261e5..81c4091782e6 100644 --- a/tests/neg/implicit-match-ambiguous-bind.scala +++ b/tests/neg/summonFrom-ambiguous-bind.scala @@ -2,7 +2,7 @@ object `implicit-match-ambiguous-bind` { case class Box[T](value: T) implicit val ibox: Box[Int] = Box(0) implicit val sbox: Box[String] = Box("") - inline def unbox = delegate match { + inline def unbox = compiletime.summonFrom { case b: Box[t] => b.value // error } val unboxed = unbox diff --git a/tests/invalid/neg/implicitMatch-ambiguous.scala b/tests/neg/summonFrom-ambiguous.scala similarity index 75% rename from tests/invalid/neg/implicitMatch-ambiguous.scala rename to tests/neg/summonFrom-ambiguous.scala index a0b848bf95fd..6e4cd6e343d8 100644 --- a/tests/invalid/neg/implicitMatch-ambiguous.scala +++ b/tests/neg/summonFrom-ambiguous.scala @@ -4,7 +4,7 @@ object Test { implicit val a1: A = new A implicit val a2: A = new A - inline def f: Any = delegate match { + inline def f: Any = compiletime.summonFrom { case _: A => ??? // error: ambiguous implicits } diff --git a/tests/neg/typeclass-derivation2.scala b/tests/neg/typeclass-derivation2.scala index 6a4ca681e30a..23bf00cf05c0 100644 --- a/tests/neg/typeclass-derivation2.scala +++ b/tests/neg/typeclass-derivation2.scala @@ -210,10 +210,10 @@ trait Show[T] { def show(x: T): String } object Show { - import scala.compiletime.{erasedValue, error} + import scala.compiletime.{erasedValue, error, summonFrom} import TypeLevel._ - inline def tryShow[T](x: T): String = delegate match { + inline def tryShow[T](x: T): String = summonFrom { case s: Show[T] => s.show(x) } diff --git a/tests/pending/pos/implicit-match.scala b/tests/pending/pos/summonFrom.scala similarity index 73% rename from tests/pending/pos/implicit-match.scala rename to tests/pending/pos/summonFrom.scala index ffba877f7985..52275a90d624 100644 --- a/tests/pending/pos/implicit-match.scala +++ b/tests/pending/pos/summonFrom.scala @@ -1,9 +1,10 @@ -// delegate matches that bind parameters don't work yet. -object `implicit-match` { +// summonFroms that bind parameters don't work yet. +import compiletime.summonFrom +object summonFroms { object invariant { case class Box[T](value: T) implicit val box: Box[Int] = Box(0) - inline def unbox <: Any = delegate match { + inline def unbox <: Any = summonFrom { case b: Box[t] => b.value } val i: Int = unbox @@ -14,7 +15,7 @@ object `implicit-match` { object covariant { case class Box[+T](value: T) implicit val box: Box[Int] = Box(0) - inline def unbox <: Any = delegate match { + inline def unbox <: Any = summonFrom { case b: Box[t] => b.value } val i: Int = unbox @@ -25,7 +26,7 @@ object `implicit-match` { object contravariant { case class TrashCan[-T](trash: T => Unit) implicit val trashCan: TrashCan[Int] = TrashCan { i => ; } - inline def trash <: Nothing => Unit = delegate match { + inline def trash <: Nothing => Unit = summonFrom { case c: TrashCan[t] => c.trash } val t1: Int => Unit = trash diff --git a/tests/pos-special/typeclass-scaling.scala b/tests/pos-special/typeclass-scaling.scala index 0f4dab284bcc..22510917f881 100644 --- a/tests/pos-special/typeclass-scaling.scala +++ b/tests/pos-special/typeclass-scaling.scala @@ -207,6 +207,7 @@ object datatypes { } object typeclasses { + import compiletime.summonFrom // A typeclass trait Eq[T] { def eql(x: T, y: T): Boolean @@ -217,7 +218,7 @@ object typeclasses { import compiletime._ import scala.deriving._ - inline def tryEql[TT](x: TT, y: TT): Boolean = delegate match { + inline def tryEql[TT](x: TT, y: TT): Boolean = summonFrom { case eq: Eq[TT] => eq.eql(x, y) } @@ -237,7 +238,7 @@ object typeclasses { inline erasedValue[Alts] match { case _: (alt *: alts1) => if (ord == n) - delegate match { + summonFrom { case m: Mirror.ProductOf[`alt`] => eqlElems[m.MirroredElemTypes](0)(x, y) } else eqlCases[alts1](n + 1)(x, y, ord) @@ -274,7 +275,7 @@ object typeclasses { def nextInt(buf: mutable.ListBuffer[Int]): Int = try buf.head finally buf.trimStart(1) - inline def tryPickle[T](buf: mutable.ListBuffer[Int], x: T): Unit = delegate match { + inline def tryPickle[T](buf: mutable.ListBuffer[Int], x: T): Unit = summonFrom { case pkl: Pickler[T] => pkl.pickle(buf, x) } @@ -290,14 +291,14 @@ object typeclasses { inline erasedValue[Alts] match { case _: (alt *: alts1) => if (ord == n) - delegate match { + summonFrom { case m: Mirror.ProductOf[`alt`] => pickleElems[m.MirroredElemTypes](0)(buf, x) } else pickleCases[alts1](n + 1)(buf, x, ord) case _: Unit => } - inline def tryUnpickle[T](buf: mutable.ListBuffer[Int]): T = delegate match { + inline def tryUnpickle[T](buf: mutable.ListBuffer[Int]): T = summonFrom { case pkl: Pickler[T] => pkl.unpickle(buf) } @@ -324,7 +325,7 @@ object typeclasses { inline erasedValue[Alts] match { case _: (alt *: alts1) => if (ord == n) - delegate match { + summonFrom { case m: Mirror.ProductOf[`alt` & T] => unpickleCase[`alt` & T, m.MirroredElemTypes](buf, m) } diff --git a/tests/pos/given-pattern.scala b/tests/pos/given-pattern.scala new file mode 100644 index 000000000000..7986dc56402f --- /dev/null +++ b/tests/pos/given-pattern.scala @@ -0,0 +1,30 @@ + + +class Test { + import scala.collection.immutable.{TreeSet, HashSet} + + inline def trySummon[S, T](f: PartialFunction[S, T]) <: T = ??? + + inline def setFor[T]: Set[T] = trySummon { + case given ord: Ordering[T] => new TreeSet[T] + case given _: Ordering[T] => new TreeSet[T] + case _ => new HashSet[T] + } + + def f1[T](x: Ordering[T]) = (x, x) match { + case (given y: Ordering[T], _) => new TreeSet[T] + } + def f2[T](x: Ordering[T]) = { + val xs = List(x, x, x) + for given y: Ordering[T] <- xs + yield new TreeSet[T] + } + def f3[T](x: Ordering[T]) = (x, x) match { + case (given _: Ordering[T], _) => new TreeSet[T] + } + def f4[T](x: Ordering[T]) = { + val xs = List(x, x, x) + for given _: Ordering[T] <- xs + yield new TreeSet[T] + } +} \ No newline at end of file diff --git a/tests/pos/i5938.scala b/tests/pos/i5938.scala index 900b1141f683..c650fca779d7 100644 --- a/tests/pos/i5938.scala +++ b/tests/pos/i5938.scala @@ -1,11 +1,14 @@ +import compiletime.summonFrom + trait Link[T, A] -inline def link[T] = delegate match { - case _: Link[T, s] => - delegate match { - case stuff: s => stuff - } -} +inline def link[T] = + summonFrom { + case _: Link[T, s] => + summonFrom { + case stuff: s => stuff + } + } class Foo object Foo { diff --git a/tests/pos/i6014-gadt.scala b/tests/pos/i6014-gadt.scala index 382de8a27582..0c3217a2653f 100644 --- a/tests/pos/i6014-gadt.scala +++ b/tests/pos/i6014-gadt.scala @@ -10,7 +10,7 @@ object Test1 { } object Test2 { - inline def summon[T] = delegate match { + inline def summon[T] = summonFrom { case t: T => t } @@ -25,7 +25,7 @@ object Test2 { } object Test3 { - inline def summon[T] = delegate match { + inline def summon[T] = summonFrom { case t: T => t } @@ -42,7 +42,7 @@ object Test3 { } object Test4 { - inline def summon[T] = delegate match { + inline def summon[T] = summonFrom { case t: T => t } diff --git a/tests/pos/implicit-match-and-inline-match.scala b/tests/pos/implicit-match-and-inline-match.scala index 360f239a5415..a985b788ccec 100644 --- a/tests/pos/implicit-match-and-inline-match.scala +++ b/tests/pos/implicit-match-and-inline-match.scala @@ -5,7 +5,7 @@ object `implicit-match-and-inline-match` { implicit val ibox: Box[Int] = Box(0) object a { - inline def isTheBoxInScopeAnInt = delegate match { + inline def isTheBoxInScopeAnInt = summonFrom { case _: Box[t] => inline erasedValue[t] match { case _: Int => true } @@ -14,7 +14,7 @@ object `implicit-match-and-inline-match` { } object b { - inline def isTheBoxInScopeAnInt = delegate match { + inline def isTheBoxInScopeAnInt = summonFrom { case _: Box[t] => inline 0 match { case _: t => true } diff --git a/tests/pos/implicit-match-nested.scala b/tests/pos/implicit-match-nested.scala index 32675edf3084..62cfc7ac2180 100644 --- a/tests/pos/implicit-match-nested.scala +++ b/tests/pos/implicit-match-nested.scala @@ -1,4 +1,6 @@ object `implicit-match-nested` { + import compiletime.summonFrom + case class A[T]() case class B[T]() @@ -6,11 +8,13 @@ object `implicit-match-nested` { implicit val b1: B[Int] = B[Int]() implicit val b2: B[String] = B[String]() - inline def locateB <: B[_] = delegate match { - case _: A[t] => delegate match { - case b: B[`t`] => b + inline def locateB <: B[_] = + summonFrom { + case _: A[t] => + summonFrom { + case b: B[`t`] => b + } } - } locateB } diff --git a/tests/pos/inline-separate/A_1.scala b/tests/pos/inline-separate/A_1.scala index b5eb1437f427..72783578df80 100644 --- a/tests/pos/inline-separate/A_1.scala +++ b/tests/pos/inline-separate/A_1.scala @@ -1,5 +1,5 @@ object A { - inline def summon[T] = delegate match { + inline def summon[T] = compiletime.summonFrom { case t: T => t } } diff --git a/tests/pos/reference/compile-time.scala b/tests/pos/reference/compile-time.scala index a4a648450e75..87b75a424c34 100644 --- a/tests/pos/reference/compile-time.scala +++ b/tests/pos/reference/compile-time.scala @@ -38,6 +38,8 @@ class Test { case _: Succ[n] => toIntT[n] + 1 } + inline def summonFrom(f: Nothing => Any) <: Any = ??? + final val two = toIntT[Succ[Succ[Zero.type]]] } \ No newline at end of file diff --git a/tests/pos/reference/delegate-match.scala b/tests/pos/reference/delegate-match.scala index 27577a8d4915..6a1b8e6073cf 100644 --- a/tests/pos/reference/delegate-match.scala +++ b/tests/pos/reference/delegate-match.scala @@ -1,22 +1,25 @@ package implicitmatch -class Test { +class Test extends App { import scala.collection.immutable.{TreeSet, HashSet} + import scala.compiletime.summonFrom - inline def setFor[T]: Set[T] = implicit match { - case ord: Ordering[T] => new TreeSet[T] - case _ => new HashSet[T] + inline def setFor[T] <: Set[T] = summonFrom { + case given ord: Ordering[T] => new TreeSet[T] + case _ => new HashSet[T] } the[Ordering[String]] - println(setFor[String].getClass) // prints class scala.collection.immutable.TreeSet + val sf = setFor[String] - class A - implicit val a1: A = new A - implicit val a2: A = new A + println(sf.getClass) // prints class scala.collection.immutable.TreeSet - inline def f: Any = implicit match { - case _: A => ??? // error: ambiguous implicits + class A(val x: String) + implicit val a1: A = new A("a1") + implicit val a2: A = new A("a2") + + inline def f: Any = summonFrom { + case ev: A => println(ev.x) // error: ambiguous implicits } } \ No newline at end of file diff --git a/tests/run/companion-loading.scala b/tests/run/companion-loading.scala index 53d27d20481f..b34f67750f4b 100644 --- a/tests/run/companion-loading.scala +++ b/tests/run/companion-loading.scala @@ -19,10 +19,12 @@ implicit object FooAssoc extends Assoc[Foo] { def foo(t: Foo): Int = t.i } +import compiletime.summonFrom + inline def link[T] <: Any = - delegate match { + summonFrom { case _: Link[T, s] => - delegate match { + summonFrom { case stuff: s => stuff } } diff --git a/tests/run/summonFrom.check b/tests/run/summonFrom.check new file mode 100644 index 000000000000..e660fb515087 --- /dev/null +++ b/tests/run/summonFrom.check @@ -0,0 +1,5 @@ +class scala.collection.immutable.TreeSet +class scala.collection.immutable.HashSet +class scala.collection.immutable.TreeSet +class scala.collection.immutable.HashSet +B diff --git a/tests/run/impliedMatch.scala b/tests/run/summonFrom.scala similarity index 80% rename from tests/run/impliedMatch.scala rename to tests/run/summonFrom.scala index 6b280b1efad5..49076f35528c 100644 --- a/tests/run/impliedMatch.scala +++ b/tests/run/summonFrom.scala @@ -1,13 +1,14 @@ object Test extends App { import collection.immutable.TreeSet import collection.immutable.HashSet + import compiletime.summonFrom - inline def f1[T]() = delegate match { + inline def f1[T]() = summonFrom { case ord: Ordering[T] => new TreeSet[T] case _ => new HashSet[T] } - inline def f2[T]() = delegate match { + inline def f2[T]() = summonFrom { case _: Ordering[T] => new TreeSet[T] case _ => new HashSet[T] } @@ -16,7 +17,7 @@ object Test extends App { class B implicit val b: B = new B - inline def g = delegate match { + inline def g = summonFrom { case _: A => println("A") case _: B => println("B") } diff --git a/tests/run/typeclass-derivation1.scala b/tests/run/typeclass-derivation1.scala index 4f3e2f411382..39cf6eeb34f3 100644 --- a/tests/run/typeclass-derivation1.scala +++ b/tests/run/typeclass-derivation1.scala @@ -38,7 +38,7 @@ object Deriving { } object Eq { - inline def tryEq[T](x: T, y: T) = delegate match { + inline def tryEq[T](x: T, y: T) = summonFrom { case eq: Eq[T] => eq.equals(x, y) } diff --git a/tests/run/typeclass-derivation2.scala b/tests/run/typeclass-derivation2.scala index 8b5b297c0791..2f6d332225e1 100644 --- a/tests/run/typeclass-derivation2.scala +++ b/tests/run/typeclass-derivation2.scala @@ -224,10 +224,10 @@ trait Eq[T] { } object Eq { - import scala.compiletime.{erasedValue, error} + import scala.compiletime.{erasedValue, error, summonFrom} import TypeLevel._ - inline def tryEql[T](x: T, y: T) = delegate match { + inline def tryEql[T](x: T, y: T) = summonFrom { case eq: Eq[T] => eq.eql(x, y) } @@ -283,12 +283,12 @@ trait Pickler[T] { } object Pickler { - import scala.compiletime.{erasedValue, constValue, error} + import scala.compiletime.{erasedValue, constValue, error, summonFrom} import TypeLevel._ def nextInt(buf: mutable.ListBuffer[Int]): Int = try buf.head finally buf.trimStart(1) - inline def tryPickle[T](buf: mutable.ListBuffer[Int], x: T): Unit = delegate match { + inline def tryPickle[T](buf: mutable.ListBuffer[Int], x: T): Unit = summonFrom { case pkl: Pickler[T] => pkl.pickle(buf, x) } @@ -321,7 +321,7 @@ object Pickler { case _: Unit => } - inline def tryUnpickle[T](buf: mutable.ListBuffer[Int]): T = delegate match { + inline def tryUnpickle[T](buf: mutable.ListBuffer[Int]): T = summonFrom { case pkl: Pickler[T] => pkl.unpickle(buf) } @@ -379,10 +379,10 @@ trait Show[T] { def show(x: T): String } object Show { - import scala.compiletime.{erasedValue, error} + import scala.compiletime.{erasedValue, error, summonFrom} import TypeLevel._ - inline def tryShow[T](x: T): String = delegate match { + inline def tryShow[T](x: T): String = summonFrom { case s: Show[T] => s.show(x) } diff --git a/tests/run/typeclass-derivation2a.scala b/tests/run/typeclass-derivation2a.scala index 137049f5c50b..b923a3a70c52 100644 --- a/tests/run/typeclass-derivation2a.scala +++ b/tests/run/typeclass-derivation2a.scala @@ -219,10 +219,10 @@ trait Eq[T] { } object Eq { - import scala.compiletime.erasedValue + import scala.compiletime.{erasedValue, summonFrom} import TypeLevel._ - inline def tryEql[T](x: T, y: T) = delegate match { + inline def tryEql[T](x: T, y: T) = summonFrom { case eq: Eq[T] => eq.eql(x, y) } @@ -270,12 +270,12 @@ trait Pickler[T] { } object Pickler { - import scala.compiletime.{erasedValue, constValue} + import scala.compiletime.{erasedValue, constValue, summonFrom} import TypeLevel._ def nextInt(buf: mutable.ListBuffer[Int]): Int = try buf.head finally buf.trimStart(1) - inline def tryPickle[T](buf: mutable.ListBuffer[Int], x: T): Unit = delegate match { + inline def tryPickle[T](buf: mutable.ListBuffer[Int], x: T): Unit = summonFrom { case pkl: Pickler[T] => pkl.pickle(buf, x) } @@ -295,7 +295,7 @@ object Pickler { case _: Unit => } - inline def tryUnpickle[T](buf: mutable.ListBuffer[Int]): T = delegate match { + inline def tryUnpickle[T](buf: mutable.ListBuffer[Int]): T = summonFrom { case pkl: Pickler[T] => pkl.unpickle(buf) } @@ -357,10 +357,10 @@ trait Show[T] { def show(x: T): String } object Show { - import scala.compiletime.erasedValue + import scala.compiletime.{erasedValue, summonFrom} import TypeLevel._ - inline def tryShow[T](x: T): String = delegate match { + inline def tryShow[T](x: T): String = summonFrom { case s: Show[T] => s.show(x) } diff --git a/tests/run/typeclass-derivation2b.scala b/tests/run/typeclass-derivation2b.scala index b2e0b2f8e441..e293f51f9e87 100644 --- a/tests/run/typeclass-derivation2b.scala +++ b/tests/run/typeclass-derivation2b.scala @@ -91,10 +91,10 @@ trait Eq[T] { } object Eq { - import scala.compiletime.erasedValue + import scala.compiletime.{erasedValue, summonFrom} import TypeLevel._ - inline def tryEql[T](x: T, y: T) = delegate match { + inline def tryEql[T](x: T, y: T) = summonFrom { case eq: Eq[T] => eq.eql(x, y) } @@ -233,7 +233,7 @@ object Eq { import scala.compiletime.erasedValue import TypeLevel._ - inline def tryEql[T](x: T, y: T) = delegate match { + inline def tryEql[T](x: T, y: T) = summonFrom { case eq: Eq[T] => eq.eql(x, y) } @@ -286,7 +286,7 @@ object Pickler { def nextInt(buf: mutable.ListBuffer[Int]): Int = try buf.head finally buf.trimStart(1) - inline def tryPickle[T](buf: mutable.ListBuffer[Int], x: T): Unit = delegate match { + inline def tryPickle[T](buf: mutable.ListBuffer[Int], x: T): Unit = summonFrom { case pkl: Pickler[T] => pkl.pickle(buf, x) } @@ -306,7 +306,7 @@ object Pickler { case _: Unit => } - inline def tryUnpickle[T](buf: mutable.ListBuffer[Int]): T = delegate match { + inline def tryUnpickle[T](buf: mutable.ListBuffer[Int]): T = summonFrom { case pkl: Pickler[T] => pkl.unpickle(buf) } @@ -371,7 +371,7 @@ object Show { import scala.compiletime.erasedValue import TypeLevel._ - inline def tryShow[T](x: T): String = delegate match { + inline def tryShow[T](x: T): String = summonFrom { case s: Show[T] => s.show(x) } diff --git a/tests/run/typeclass-derivation2c.scala b/tests/run/typeclass-derivation2c.scala index ae8903dd96a7..abc949c0c542 100644 --- a/tests/run/typeclass-derivation2c.scala +++ b/tests/run/typeclass-derivation2c.scala @@ -1,5 +1,6 @@ import scala.collection.mutable import scala.annotation.tailrec +import scala.compiletime.summonFrom // Simulation of an alternative typeclass derivation scheme proposed in #6153 @@ -54,7 +55,7 @@ object Deriving { type CaseLabel <: String /** The represented value */ - inline def singletonValue = delegate match { + inline def singletonValue = summonFrom { case ev: ValueOf[T] => ev.value } } @@ -212,7 +213,7 @@ trait Eq[T] { object Eq { import scala.compiletime.erasedValue - inline def tryEql[T](x: T, y: T) = delegate match { + inline def tryEql[T](x: T, y: T) = summonFrom { case eq: Eq[T] => eq.eql(x, y) } @@ -267,7 +268,7 @@ object Pickler { def nextInt(buf: mutable.ListBuffer[Int]): Int = try buf.head finally buf.trimStart(1) - inline def tryPickle[T](buf: mutable.ListBuffer[Int], x: T): Unit = delegate match { + inline def tryPickle[T](buf: mutable.ListBuffer[Int], x: T): Unit = summonFrom { case pkl: Pickler[T] => pkl.pickle(buf, x) } @@ -292,7 +293,7 @@ object Pickler { } else pickleCases[T](g, n + 1)(buf, x, ord) - inline def tryUnpickle[T](buf: mutable.ListBuffer[Int]): T = delegate match { + inline def tryUnpickle[T](buf: mutable.ListBuffer[Int]): T = summonFrom { case pkl: Pickler[T] => pkl.unpickle(buf) } @@ -357,7 +358,7 @@ trait Show[T] { object Show { import scala.compiletime.{erasedValue, constValue} - inline def tryShow[T](x: T): String = delegate match { + inline def tryShow[T](x: T): String = summonFrom { case s: Show[T] => s.show(x) } diff --git a/tests/run/typeclass-derivation2d.scala b/tests/run/typeclass-derivation2d.scala index e423bfbcf373..600581989416 100644 --- a/tests/run/typeclass-derivation2d.scala +++ b/tests/run/typeclass-derivation2d.scala @@ -197,9 +197,9 @@ trait Eq[T] { } object Eq { - import scala.compiletime.erasedValue + import scala.compiletime.{erasedValue, summonFrom} - inline def tryEql[T](x: T, y: T) = delegate match { + inline def tryEql[T](x: T, y: T) = summonFrom { case eq: Eq[T] => eq.eql(x, y) } @@ -219,7 +219,7 @@ object Eq { inline erasedValue[Alts] match { case _: (alt *: alts1) => if (ord == n) - delegate match { + summonFrom { case m: Mirror.ProductOf[`alt`] => eqlElems[m.ElemTypes](0)(x, y) } else eqlCases[alts1](n + 1)(x, y, ord) @@ -251,11 +251,11 @@ trait Pickler[T] { } object Pickler { - import scala.compiletime.{erasedValue, constValue} + import scala.compiletime.{erasedValue, constValue, summonFrom} def nextInt(buf: mutable.ListBuffer[Int]): Int = try buf.head finally buf.trimStart(1) - inline def tryPickle[T](buf: mutable.ListBuffer[Int], x: T): Unit = delegate match { + inline def tryPickle[T](buf: mutable.ListBuffer[Int], x: T): Unit = summonFrom { case pkl: Pickler[T] => pkl.pickle(buf, x) } @@ -271,14 +271,14 @@ object Pickler { inline erasedValue[Alts] match { case _: (alt *: alts1) => if (ord == n) - delegate match { + summonFrom { case m: Mirror.ProductOf[`alt`] => pickleElems[m.ElemTypes](0)(buf, x) } else pickleCases[alts1](n + 1)(buf, x, ord) case _: Unit => } - inline def tryUnpickle[T](buf: mutable.ListBuffer[Int]): T = delegate match { + inline def tryUnpickle[T](buf: mutable.ListBuffer[Int]): T = summonFrom { case pkl: Pickler[T] => pkl.unpickle(buf) } @@ -305,7 +305,7 @@ object Pickler { inline erasedValue[Alts] match { case _: (alt *: alts1) => if (ord == n) - delegate match { + summonFrom { case m: Mirror.ProductOf[`alt` & T] => unpickleCase[`alt` & T, m.ElemTypes](buf, m) } @@ -346,9 +346,9 @@ trait Show[T] { def show(x: T): String } object Show { - import scala.compiletime.{erasedValue, constValue} + import scala.compiletime.{erasedValue, constValue, summonFrom} - inline def tryShow[T](x: T): String = delegate match { + inline def tryShow[T](x: T): String = summonFrom { case s: Show[T] => s.show(x) } @@ -377,7 +377,7 @@ object Show { inline erasedValue[Alts] match { case _: (alt *: alts1) => if (ord == n) - delegate match { + summonFrom { case m: Mirror.ProductOf[`alt`] => showCase(x, m) } diff --git a/tests/run/typeclass-derivation3.scala b/tests/run/typeclass-derivation3.scala index 051060850f7c..d844afcea8f3 100644 --- a/tests/run/typeclass-derivation3.scala +++ b/tests/run/typeclass-derivation3.scala @@ -28,11 +28,11 @@ object typeclasses { } object Eq { - import scala.compiletime.erasedValue + import scala.compiletime.{erasedValue, summonFrom} import compiletime._ import scala.deriving._ - inline def tryEql[TT](x: TT, y: TT): Boolean = delegate match { + inline def tryEql[TT](x: TT, y: TT): Boolean = summonFrom { case eq: Eq[TT] => eq.eql(x, y) } @@ -52,7 +52,7 @@ object typeclasses { inline erasedValue[Alts] match { case _: (alt *: alts1) => if (ord == n) - delegate match { + summonFrom { case m: Mirror.ProductOf[`alt`] => eqlElems[m.MirroredElemTypes](0)(x, y) } else eqlCases[alts1](n + 1)(x, y, ord) @@ -83,13 +83,13 @@ object typeclasses { } object Pickler { - import scala.compiletime.{erasedValue, constValue} + import scala.compiletime.{erasedValue, constValue, summonFrom} import compiletime._ import deriving._ def nextInt(buf: mutable.ListBuffer[Int]): Int = try buf.head finally buf.trimStart(1) - inline def tryPickle[T](buf: mutable.ListBuffer[Int], x: T): Unit = delegate match { + inline def tryPickle[T](buf: mutable.ListBuffer[Int], x: T): Unit = summonFrom { case pkl: Pickler[T] => pkl.pickle(buf, x) } @@ -105,14 +105,14 @@ object typeclasses { inline erasedValue[Alts] match { case _: (alt *: alts1) => if (ord == n) - delegate match { + summonFrom { case m: Mirror.ProductOf[`alt`] => pickleElems[m.MirroredElemTypes](0)(buf, x) } else pickleCases[alts1](n + 1)(buf, x, ord) case _: Unit => } - inline def tryUnpickle[T](buf: mutable.ListBuffer[Int]): T = delegate match { + inline def tryUnpickle[T](buf: mutable.ListBuffer[Int]): T = summonFrom { case pkl: Pickler[T] => pkl.unpickle(buf) } @@ -139,7 +139,7 @@ object typeclasses { inline erasedValue[Alts] match { case _: (alt *: alts1) => if (ord == n) - delegate match { + summonFrom { case m: Mirror.ProductOf[`alt` & T] => unpickleCase[`alt` & T, m.MirroredElemTypes](buf, m) } @@ -179,11 +179,11 @@ object typeclasses { def show(x: T): String } object Show { - import scala.compiletime.erasedValue + import scala.compiletime.{erasedValue, summonFrom} import compiletime._ import deriving._ - inline def tryShow[T](x: T): String = delegate match { + inline def tryShow[T](x: T): String = summonFrom { case s: Show[T] => s.show(x) } @@ -212,7 +212,7 @@ object typeclasses { inline erasedValue[Alts] match { case _: (alt *: alts1) => if (ord == n) - delegate match { + summonFrom { case m: Mirror.ProductOf[`alt`] => showCase(x, m) }