diff --git a/compiler/src/dotty/tools/dotc/Compiler.scala b/compiler/src/dotty/tools/dotc/Compiler.scala index 6ba8e85d0238..45ec0b60ce0c 100644 --- a/compiler/src/dotty/tools/dotc/Compiler.scala +++ b/compiler/src/dotty/tools/dotc/Compiler.scala @@ -62,6 +62,7 @@ class Compiler { new ExpandSAMs, // Expand single abstract method closures to anonymous classes new ProtectedAccessors, // Add accessors for protected members new ExtensionMethods, // Expand methods of value classes with extension methods + new CacheAliasImplicits, // Cache RHS of parameterless alias implicits new ShortcutImplicits, // Allow implicit functions without creating closures new ByNameClosures, // Expand arguments to by-name parameters to closures new HoistSuperArgs, // Hoist complex arguments of supercalls to enclosing scope diff --git a/compiler/src/dotty/tools/dotc/ast/Desugar.scala b/compiler/src/dotty/tools/dotc/ast/Desugar.scala index 6d6b22739b69..6d1eae9018c8 100644 --- a/compiler/src/dotty/tools/dotc/ast/Desugar.scala +++ b/compiler/src/dotty/tools/dotc/ast/Desugar.scala @@ -500,7 +500,15 @@ object desugar { case _ => constrVparamss } - New(classTypeRef, vparamss.nestedMap(refOfDef)) + val nu = (makeNew(classTypeRef) /: vparamss) { (nu, vparams) => + val app = Apply(nu, vparams.map(refOfDef)) + vparams match { + case vparam :: _ if vparam.mods.is(Given) => app.pushAttachment(ApplyGiven, ()) + case _ => + } + app + } + ensureApplied(nu) } val copiedAccessFlags = if (ctx.scala2Setting) EmptyFlags else AccessFlags diff --git a/compiler/src/dotty/tools/dotc/ast/NavigateAST.scala b/compiler/src/dotty/tools/dotc/ast/NavigateAST.scala index 9fa11c8bf80d..85add107119d 100644 --- a/compiler/src/dotty/tools/dotc/ast/NavigateAST.scala +++ b/compiler/src/dotty/tools/dotc/ast/NavigateAST.scala @@ -70,6 +70,7 @@ object NavigateAST { */ def pathTo(span: Span, from: Positioned, skipZeroExtent: Boolean = false)(implicit ctx: Context): List[Positioned] = { def childPath(it: Iterator[Any], path: List[Positioned]): List[Positioned] = { + var bestFit: List[Positioned] = path while (it.hasNext) { val path1 = it.next() match { case p: Positioned => singlePath(p, path) @@ -77,9 +78,13 @@ object NavigateAST { case xs: List[_] => childPath(xs.iterator, path) case _ => path } - if (path1 ne path) return path1 + if ((path1 ne path) && + ((bestFit eq path) || + bestFit.head.span != path1.head.span && + bestFit.head.span.contains(path1.head.span))) + bestFit = path1 } - path + bestFit } def singlePath(p: Positioned, path: List[Positioned]): List[Positioned] = if (p.span.exists && !(skipZeroExtent && p.span.isZeroExtent) && p.span.contains(span)) { diff --git a/compiler/src/dotty/tools/dotc/ast/Positioned.scala b/compiler/src/dotty/tools/dotc/ast/Positioned.scala index a8e91ca2076c..7ee0f4798164 100644 --- a/compiler/src/dotty/tools/dotc/ast/Positioned.scala +++ b/compiler/src/dotty/tools/dotc/ast/Positioned.scala @@ -147,6 +147,11 @@ abstract class Positioned(implicit @constructorOnly src: SourceFile) extends Pro } } + /** A hook that can be overridden if overlap checking in `checkPos` should be + * disabled for this node. + */ + def disableOverlapChecks = false + /** Check that all positioned items in this tree satisfy the following conditions: * - Parent spans contain child spans * - If item is a non-empty tree, it has a position @@ -169,7 +174,7 @@ abstract class Positioned(implicit @constructorOnly src: SourceFile) extends Pro s"position error: position not set for $tree # ${tree.uniqueId}") case _ => } - if (nonOverlapping) { + if (nonOverlapping && !disableOverlapChecks) { this match { case _: XMLBlock => // FIXME: Trees generated by the XML parser do not satisfy `checkPos` diff --git a/compiler/src/dotty/tools/dotc/ast/Trees.scala b/compiler/src/dotty/tools/dotc/ast/Trees.scala index 2e9ca19b7b19..a74dc2e4c069 100644 --- a/compiler/src/dotty/tools/dotc/ast/Trees.scala +++ b/compiler/src/dotty/tools/dotc/ast/Trees.scala @@ -746,6 +746,10 @@ object Trees { assert(tpt != genericEmptyTree) def unforced: LazyTree = preRhs protected def force(x: AnyRef): Unit = preRhs = x + + override def disableOverlapChecks = rawMods.is(Flags.Implied) + // disable order checks for implicit aliases since their given clause follows + // their for clause, but the two appear swapped in the DefDef. } class BackquotedDefDef[-T >: Untyped] private[ast] (name: TermName, tparams: List[TypeDef[T]], @@ -783,6 +787,10 @@ object Trees { def parents: List[Tree[T]] = parentsOrDerived // overridden by DerivingTemplate def derived: List[untpd.Tree] = Nil // overridden by DerivingTemplate + + override def disableOverlapChecks = true + // disable overlaps checks since templates of instance definitions have their + // `given` clause come last, which means that the constructor span can contain the parent spans. } diff --git a/compiler/src/dotty/tools/dotc/ast/tpd.scala b/compiler/src/dotty/tools/dotc/ast/tpd.scala index 28f11a24de21..c26408edc5cd 100644 --- a/compiler/src/dotty/tools/dotc/ast/tpd.scala +++ b/compiler/src/dotty/tools/dotc/ast/tpd.scala @@ -57,6 +57,9 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo { def unitLiteral(implicit ctx: Context): Literal = Literal(Constant(())) + def nullLiteral(implicit ctx: Context): Literal = + Literal(Constant(null)) + def New(tpt: Tree)(implicit ctx: Context): New = ta.assignType(untpd.New(tpt), tpt) @@ -496,7 +499,7 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo { else if (tpw isRef defn.DoubleClass) Literal(Constant(0d)) else if (tpw isRef defn.ByteClass) Literal(Constant(0.toByte)) else if (tpw isRef defn.ShortClass) Literal(Constant(0.toShort)) - else Literal(Constant(null)).select(defn.Any_asInstanceOf).appliedToType(tpe) + else nullLiteral.select(defn.Any_asInstanceOf).appliedToType(tpe) } private class FindLocalDummyAccumulator(cls: ClassSymbol)(implicit ctx: Context) extends TreeAccumulator[Symbol] { @@ -915,7 +918,7 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo { Typed(tree, TypeTree(defn.AnyRefType)) } else tree.ensureConforms(defn.ObjectType) - receiver.select(defn.Object_ne).appliedTo(Literal(Constant(null))) + receiver.select(defn.Object_ne).appliedTo(nullLiteral) } /** If inititializer tree is `_', the default value of its type, diff --git a/compiler/src/dotty/tools/dotc/ast/untpd.scala b/compiler/src/dotty/tools/dotc/ast/untpd.scala index 94ca12203539..bd2d64d205a5 100644 --- a/compiler/src/dotty/tools/dotc/ast/untpd.scala +++ b/compiler/src/dotty/tools/dotc/ast/untpd.scala @@ -68,7 +68,7 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo { override def isType: Boolean = body.isType } - /** A function type with `implicit`, `erased`, or `contextual` modifiers */ + /** A function type with `implicit`, `erased`, or `given` modifiers */ class FunctionWithMods(args: List[Tree], body: Tree, val mods: Modifiers)(implicit @constructorOnly src: SourceFile) extends Function(args, body) @@ -343,7 +343,13 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo { * navigation into these arguments from the IDE, and to do the right thing in * PrepareInlineable. */ - def New(tpt: Tree, argss: List[List[Tree]])(implicit ctx: Context): Tree = { + def New(tpt: Tree, argss: List[List[Tree]])(implicit ctx: Context): Tree = + ensureApplied((makeNew(tpt) /: argss)(Apply(_, _))) + + /** A new expression with constrictor and possibly type arguments. See + * `New(tpt, argss)` for details. + */ + def makeNew(tpt: Tree)(implicit ctx: Context): Tree = { val (tycon, targs) = tpt match { case AppliedTypeTree(tycon, targs) => (tycon, targs) @@ -354,9 +360,8 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo { case _ => (tpt, Nil) } - var prefix: Tree = Select(New(tycon), nme.CONSTRUCTOR) - if (targs.nonEmpty) prefix = TypeApply(prefix, targs) - ensureApplied((prefix /: argss)(Apply(_, _))) + val nu: Tree = Select(New(tycon), nme.CONSTRUCTOR) + if (targs.nonEmpty) TypeApply(nu, targs) else nu } def Block(stat: Tree, expr: Tree)(implicit src: SourceFile): Block = @@ -415,9 +420,9 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo { vdef.withMods(mods | Param) } - def makeSyntheticParameter(n: Int = 1, tpt: Tree = null, flags: FlagSet = EmptyFlags)(implicit ctx: Context): ValDef = + def makeSyntheticParameter(n: Int = 1, tpt: Tree = null, flags: FlagSet = SyntheticTermParam)(implicit ctx: Context): ValDef = ValDef(nme.syntheticParamName(n), if (tpt == null) TypeTree() else tpt, EmptyTree) - .withFlags(flags | SyntheticTermParam) + .withFlags(flags) def lambdaAbstract(tparams: List[TypeDef], tpt: Tree)(implicit ctx: Context): Tree = if (tparams.isEmpty) tpt else LambdaTypeTree(tparams, tpt) diff --git a/compiler/src/dotty/tools/dotc/core/Flags.scala b/compiler/src/dotty/tools/dotc/core/Flags.scala index b0341eb597ea..ff23c09dd29f 100644 --- a/compiler/src/dotty/tools/dotc/core/Flags.scala +++ b/compiler/src/dotty/tools/dotc/core/Flags.scala @@ -394,6 +394,9 @@ object Flags { /** Symbol is an enum class or enum case (if used with case) */ final val Enum: FlagSet = commonFlag(40, "") + /** An export forwarder */ + final val Exported: FlagSet = commonFlag(41, "exported") + /** Labeled with `erased` modifier (erased value) */ final val Erased: FlagSet = termFlag(42, "erased") @@ -459,7 +462,7 @@ object Flags { /** Flags representing source modifiers */ private val CommonSourceModifierFlags: FlagSet = - commonFlags(Private, Protected, Final, Case, Implicit, Implied, Override, JavaStatic) + commonFlags(Private, Protected, Final, Case, Implicit, Implied, Given, Override, JavaStatic) final val TypeSourceModifierFlags: FlagSet = CommonSourceModifierFlags.toTypeFlags | Abstract | Sealed | Opaque diff --git a/compiler/src/dotty/tools/dotc/core/NameKinds.scala b/compiler/src/dotty/tools/dotc/core/NameKinds.scala index 42955de15a50..ec0a6be801b3 100644 --- a/compiler/src/dotty/tools/dotc/core/NameKinds.scala +++ b/compiler/src/dotty/tools/dotc/core/NameKinds.scala @@ -359,6 +359,7 @@ object NameKinds { val InlineAccessorName: PrefixNameKind = new PrefixNameKind(INLINEACCESSOR, "inline$") val AvoidClashName: SuffixNameKind = new SuffixNameKind(AVOIDCLASH, "$_avoid_name_clash_$") + val CacheName = new SuffixNameKind(CACHE, "$_cache") val DirectMethodName: SuffixNameKind = new SuffixNameKind(DIRECT, "$direct") { override def definesNewName = true } val FieldName: SuffixNameKind = new SuffixNameKind(FIELD, "$$local") { override def mkString(underlying: TermName, info: ThisInfo) = underlying.toString diff --git a/compiler/src/dotty/tools/dotc/core/NameTags.scala b/compiler/src/dotty/tools/dotc/core/NameTags.scala index f5c2923ffb11..fe351ccc61e7 100644 --- a/compiler/src/dotty/tools/dotc/core/NameTags.scala +++ b/compiler/src/dotty/tools/dotc/core/NameTags.scala @@ -36,6 +36,8 @@ object NameTags extends TastyFormat.NameTags { final val IMPLMETH = 32 // Used to define methods in implementation classes // (can probably be removed). + final val CACHE = 33 // Used as a cache for the rhs of an alias implicit. + def nameTagToString(tag: Int): String = tag match { case UTF8 => "UTF8" case QUALIFIED => "QUALIFIED" diff --git a/compiler/src/dotty/tools/dotc/core/tasty/TastyFormat.scala b/compiler/src/dotty/tools/dotc/core/tasty/TastyFormat.scala index 1bed9ebf976e..3f389fe1f8d2 100644 --- a/compiler/src/dotty/tools/dotc/core/tasty/TastyFormat.scala +++ b/compiler/src/dotty/tools/dotc/core/tasty/TastyFormat.scala @@ -208,6 +208,7 @@ Standard-Section: "ASTs" TopLevelStat* EXTENSION -- An extension method GIVEN -- A new style implicit parameter, introduced with `given` PARAMsetter -- The setter part `x_=` of a var parameter `x` which itself is pickled as a PARAM + EXPORTED -- An export forwarder Annotation Annotation = ANNOTATION Length tycon_Type fullAnnotation_Term -- An annotation, given (class) type of constructor, and full application tree @@ -329,6 +330,7 @@ object TastyFormat { final val GIVEN = 37 final val IMPLIED = 38 final val PARAMsetter = 39 + final val EXPORTED = 40 // Cat. 2: tag Nat @@ -457,7 +459,7 @@ object TastyFormat { /** Useful for debugging */ def isLegalTag(tag: Int): Boolean = - firstSimpleTreeTag <= tag && tag <= PARAMsetter || + firstSimpleTreeTag <= tag && tag <= EXPORTED || firstNatTreeTag <= tag && tag <= SYMBOLconst || firstASTTreeTag <= tag && tag <= SINGLETONtpt || firstNatASTTreeTag <= tag && tag <= NAMEDARG || @@ -500,6 +502,7 @@ object TastyFormat { | EXTENSION | GIVEN | PARAMsetter + | EXPORTED | ANNOTATION | PRIVATEqualified | PROTECTEDqualified => true @@ -560,6 +563,7 @@ object TastyFormat { case EXTENSION => "EXTENSION" case GIVEN => "GIVEN" case PARAMsetter => "PARAMsetter" + case EXPORTED => "EXPORTED" case SHAREDterm => "SHAREDterm" case SHAREDtype => "SHAREDtype" diff --git a/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala b/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala index fa818934839b..de15d03c31d7 100644 --- a/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala +++ b/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala @@ -656,6 +656,7 @@ class TreePickler(pickler: TastyPickler) { if (flags is Extension) writeByte(EXTENSION) if (flags is Given) writeByte(GIVEN) if (flags is ParamAccessor) writeByte(PARAMsetter) + if (flags is Exported) writeByte(EXPORTED) assert(!(flags is Label)) } else { if (flags is Sealed) writeByte(SEALED) diff --git a/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala b/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala index 71a14e25b6d9..aa3993ff9b01 100644 --- a/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala +++ b/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala @@ -632,8 +632,8 @@ class TreeUnpickler(reader: TastyReader, case STABLE => addFlag(StableRealizable) case EXTENSION => addFlag(Extension) case GIVEN => addFlag(Given) - case PARAMsetter => - addFlag(ParamAccessor) + case PARAMsetter => addFlag(ParamAccessor) + case EXPORTED => addFlag(Exported) case PRIVATEqualified => readByte() privateWithin = readWithin(ctx) diff --git a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala index ab5484380d37..f7a51234f137 100644 --- a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala +++ b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala @@ -575,22 +575,26 @@ object Parsers { } else recur(operand()) } - else if (in.token == GIVEN) { + else if (in.token == GIVEN && !isType) { val top1 = reduceStack(base, top, minInfixPrec, leftAssoc = true, nme.WITHkw, isType) assert(opStack `eq` base) - val app = atSpan(startOffset(top1), in.offset) { - in.nextToken() - val args = if (in.token == LPAREN) parArgumentExprs() else operand() :: Nil - Apply(top, args) - } - app.pushAttachment(ApplyGiven, ()) - recur(app) + recur(applyGiven(top1, operand)) } else reduceStack(base, top, minPrec, leftAssoc = true, in.name, isType) recur(first) } + def applyGiven(t: Tree, operand: () => Tree): Tree = { + val app = atSpan(startOffset(t), in.offset) { + in.nextToken() + val args = if (in.token == LPAREN) parArgumentExprs() else operand() :: Nil + Apply(t, args) + } + app.pushAttachment(ApplyGiven, ()) + app + } + /* -------- IDENTIFIERS AND LITERALS ------------------------------------------- */ /** Accept identifier and return its name as a term name. */ @@ -2110,15 +2114,16 @@ object Parsers { def typeParamClauseOpt(ownerKind: ParamOwner.Value): List[TypeDef] = if (in.token == LBRACKET) typeParamClause(ownerKind) else Nil - /** ClsParamClause ::= [nl] [‘erased’] ‘(’ [ClsParams] ‘)’ - * | ‘given’ [‘erased’] (‘(’ ClsParams ‘)’ | GivenTypes) - * ClsParams ::= ClsParam {`' ClsParam} - * ClsParam ::= {Annotation} [{ParamModifier} (`val' | `var') | `inline'] Param - * DefParamClause ::= [nl] [‘erased’] ‘(’ [DefParams] ‘)’ | GivenParamClause + /** ClsParamClause ::= [‘erased’] (‘(’ ClsParams ‘)’ + * GivenClsParamClause::= ‘given’ [‘erased’] (‘(’ ClsParams ‘)’ | GivenTypes) + * ClsParams ::= ClsParam {‘,’ ClsParam} + * ClsParam ::= {Annotation} + * + * DefParamClause ::= [‘erased’] (‘(’ DefParams ‘)’ * GivenParamClause ::= ‘given’ [‘erased’] (‘(’ DefParams ‘)’ | GivenTypes) - * GivenTypes ::= RefinedType {`,' RefinedType} - * DefParams ::= DefParam {`,' DefParam} - * DefParam ::= {Annotation} [`inline'] Param + * DefParams ::= DefParam {‘,’ DefParam} + * DefParam ::= {Annotation} [‘inline’] Param + * * Param ::= id `:' ParamType [`=' Expr] * * @return the list of parameter definitions @@ -2200,14 +2205,16 @@ object Parsers { } /** ClsParamClauses ::= {ClsParamClause} [[nl] ‘(’ [‘implicit’] ClsParams ‘)’] + * | {ClsParamClause} {GivenClsParamClause} * DefParamClauses ::= {DefParamClause} [[nl] ‘(’ [‘implicit’] DefParams ‘)’] + * | {DefParamClause} {GivenParamClause} * * @return The parameter definitions */ def paramClauses(ofClass: Boolean = false, ofCaseClass: Boolean = false, ofInstance: Boolean = false): List[List[ValDef]] = { - def recur(firstClause: Boolean, nparams: Int): List[List[ValDef]] = { + def recur(firstClause: Boolean, nparams: Int, contextualOnly: Boolean): List[List[ValDef]] = { var initialMods = EmptyModifiers if (in.token == GIVEN) { in.nextToken() @@ -2232,8 +2239,9 @@ object Parsers { } } if (in.token == LPAREN && isParamClause) { - if (ofInstance && !isContextual) - syntaxError(em"parameters of instance definitions must come after `given'") + if (contextualOnly && !isContextual) + if (ofInstance) syntaxError(em"parameters of instance definitions must come after `given'") + else syntaxError(em"normal parameters cannot come after `given' clauses") val params = paramClause( ofClass = ofClass, ofCaseClass = ofCaseClass, @@ -2241,18 +2249,19 @@ object Parsers { initialMods = initialMods) val lastClause = params.nonEmpty && params.head.mods.flags.is(Implicit, butNot = Given) - params :: (if (lastClause) Nil else recur(firstClause = false, nparams + params.length)) + params :: (if (lastClause) Nil else recur(firstClause = false, nparams + params.length, isContextual)) } else if (isContextual) { val tps = commaSeparated(() => annotType()) var counter = nparams def nextIdx = { counter += 1; counter } - val params = tps.map(makeSyntheticParameter(nextIdx, _, Given | Implicit)) - params :: recur(firstClause = false, nparams + params.length) + val paramFlags = if (ofClass) Private | Local | ParamAccessor else Param + val params = tps.map(makeSyntheticParameter(nextIdx, _, paramFlags | Synthetic | Given | Implicit)) + params :: recur(firstClause = false, nparams + params.length, isContextual) } else Nil } - recur(firstClause = true, 0) + recur(firstClause = true, 0, ofInstance) } /* -------- DEFS ------------------------------------------- */ @@ -2673,22 +2682,22 @@ object Parsers { Template(constr, parents, Nil, EmptyValDef, Nil) } - /** InstanceDef ::= [id] InstanceParams InstanceBody + /** InstanceDef ::= [id] [DefTypeParamClause] InstanceBody * InstanceParams ::= [DefTypeParamClause] {GivenParamClause} - * InstanceBody ::= [‘for’ ConstrApp {‘,’ ConstrApp }] [TemplateBody] - * | ‘for’ Type ‘=’ Expr + * InstanceBody ::= [‘for’ ConstrApp {‘,’ ConstrApp }] {GivenParamClause} [TemplateBody] + * | ‘for’ Type {GivenParamClause} ‘=’ Expr */ def instanceDef(start: Offset, mods: Modifiers, instanceMod: Mod) = atSpan(start, nameStart) { var mods1 = addMod(mods, instanceMod) val name = if (isIdent) ident() else EmptyTermName val tparams = typeParamClauseOpt(ParamOwner.Def) - val vparamss = paramClauses(ofInstance = true) val parents = if (in.token == FOR) { in.nextToken() tokenSeparated(COMMA, constrApp) } else Nil + val vparamss = paramClauses(ofInstance = true) val instDef = if (in.token == EQUALS && parents.length == 1 && parents.head.isType) { in.nextToken() @@ -2709,13 +2718,43 @@ object Parsers { /* -------- TEMPLATES ------------------------------------------- */ - /** ConstrApp ::= SimpleType {ParArgumentExprs} + /** SimpleConstrApp ::= AnnotType {ParArgumentExprs} + * ConstrApp ::= SimpleConstrApp + * | ‘(’ SimpleConstrApp {‘given’ (PrefixExpr | ParArgumentExprs)} ‘)’ */ val constrApp: () => Tree = () => { - // Using Ident(nme.ERROR) to avoid causing cascade errors on non-user-written code - val t = rejectWildcardType(annotType(), fallbackTree = Ident(nme.ERROR)) - if (in.token == LPAREN) parArgumentExprss(wrapNew(t)) - else t + + def isAnnotType(t: Tree) = t match { + case _: Ident + | _: Select + | _: AppliedTypeTree + | _: Tuple + | _: Parens + | _: RefinedTypeTree + | _: SingletonTypeTree + | _: TypSplice + | _: Annotated => true + case _ => false + } + + def givenArgs(t: Tree): Tree = { + if (in.token == GIVEN) givenArgs(applyGiven(t, prefixExpr)) else t + } + + if (in.token == LPAREN) + inParens { + val t = toplevelTyp() + if (isAnnotType(t)) + if (in.token == LPAREN) givenArgs(parArgumentExprss(wrapNew(t))) + else if (in.token == GIVEN) givenArgs(wrapNew(t)) + else t + else Parens(t) + } + else { + val t = rejectWildcardType(annotType(), fallbackTree = Ident(nme.ERROR)) + // Using Ident(nme.ERROR) to avoid causing cascade errors on non-user-written code + if (in.token == LPAREN) parArgumentExprss(wrapNew(t)) else t + } } /** ConstrApps ::= ConstrApp {‘with’ ConstrApp} (to be deprecated in 3.1) diff --git a/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala b/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala index c8ba4f862774..a393f90c2f60 100644 --- a/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala +++ b/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala @@ -350,6 +350,11 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) { keywordStr("'{") ~ toTextGlobal(args, ", ") ~ keywordStr("}") else if (!ctx.settings.YprintDebug.value && fun.hasType && fun.symbol == defn.InternalQuoted_exprSplice) keywordStr("${") ~ toTextGlobal(args, ", ") ~ keywordStr("}") + else if (tree.getAttachment(untpd.ApplyGiven).isDefined && !homogenizedView) + changePrec(InfixPrec) { + toTextLocal(fun) ~ " given " ~ + (if (args.length == 1) toTextLocal(args.head) else "(" ~ toTextGlobal(args, ", ") ~ ")") + } else toTextLocal(fun) ~ "(" ~ toTextGlobal(args, ", ") ~ ")" case tree: TypeApply => @@ -700,7 +705,10 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) { val (leading, paramss) = if (isExtension && vparamss.nonEmpty) (paramsText(vparamss.head) ~ " " ~ txt, vparamss.tail) else (txt, vparamss) - (txt /: paramss)((txt, params) => txt ~ paramsText(params)) + (txt /: paramss)((txt, params) => + txt ~ + (Str(" given ") provided params.nonEmpty && params.head.mods.is(Given)) ~ + paramsText(params)) } protected def valDefToText[T >: Untyped](tree: ValDef[T]): Text = { import untpd.{modsDeco => _} @@ -792,7 +800,9 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) { else if (suppressKw) PrintableFlags(isType) &~ Private else PrintableFlags(isType) if (homogenizedView && mods.flags.isTypeFlags) flagMask &~= ImplicitOrImplied // drop implicit/implied from classes - val flags = (if (sym.exists) sym.flags else (mods.flags)) & flagMask + val rawFlags = if (sym.exists) sym.flags else mods.flags + if (rawFlags.is(Param)) flagMask = flagMask &~ Given + val flags = rawFlags & flagMask val flagsText = if (flags.isEmpty) "" else keywordStr(flags.toString) val annotations = if (sym.exists) sym.annotations.filterNot(ann => dropAnnotForModText(ann.symbol)).map(_.tree) diff --git a/compiler/src/dotty/tools/dotc/transform/CacheAliasImplicits.scala b/compiler/src/dotty/tools/dotc/transform/CacheAliasImplicits.scala new file mode 100644 index 000000000000..e53fc0e6379a --- /dev/null +++ b/compiler/src/dotty/tools/dotc/transform/CacheAliasImplicits.scala @@ -0,0 +1,104 @@ +package dotty.tools.dotc +package transform + +import MegaPhase._ +import core.DenotTransformers.{IdentityDenotTransformer} +import core.Symbols._ +import core.Contexts._ +import core.Types._ +import core.Flags._ +import core.StdNames.nme +import core.NameKinds.CacheName +import core.Constants.Constant +import core.Decorators._ +import core.TypeErasure.erasure +import ast.tpd + +object CacheAliasImplicits { + val name: String = "cacheAliasImplicits" + + /** Flags that disable caching */ + val NoCacheFlags = + StableRealizable | // It's a simple forwarder, leave it as one + Exported // Export forwarders are never cached +} + +/** This phase ensures that the right hand side of parameterless alias implicits + * is cached. It applies to all alias implicits that have neither type parameters + * nor a given clause. Example: The alias + * + * implicit a for TC = rhs + * + * is expanded before this phase + * + * implicit def a: TC = rhs + * + * It is then expanded further as follows: + * + * 1. If `rhs` is a simple name `x` (possibly with a `this.` prefix), leave the definition as is. + * 2. Otherwise, if `rhs` is a pure path, replace the definition with + * + * implicit val a: TC = rhs + * + * 3. Otherwise, if `TC` is a reference type, replace the definition with + * + * private[this] var a$_cache: TC = null + * implicit def a: TC = { if (a$_cache == null) a$_cache = rhs; a$_cache } + * + * 4. Otherwise `TC` is a value type. Replace the definition with + * + * lazy implicit val a: TC = rhs + */ +class CacheAliasImplicits extends MiniPhase with IdentityDenotTransformer { thisPhase => + import tpd._ + + override def phaseName: String = CacheAliasImplicits.name + + override def transformDefDef(tree: DefDef)(implicit ctx: Context): Tree = { + val sym = tree.symbol + sym.info match { + case ExprType(rhsType) if sym.is(Implied, butNot = CacheAliasImplicits.NoCacheFlags) => + // If rhs is a simple TermRef, leave a def. + tree.rhs.tpe match { + case TermRef(pre, _) => + pre match { + case NoPrefix => return tree + case pre: ThisType if pre.cls == ctx.owner.enclosingClass => return tree + case _ => + } + case _ => + } + def makeVal(additionalFlags: FlagSet) = { + sym.copySymDenotation( + initFlags = sym.flags &~ Method | additionalFlags, + info = rhsType) + .installAfter(thisPhase) + cpy.ValDef(tree)(tree.name, tree.tpt, tree.rhs) + } + if (isPurePath(tree.rhs)) makeVal(EmptyFlags) + else if (rhsType.classSymbol.isValueClass || + !erasure(rhsType).typeSymbol.derivesFrom(defn.ObjectClass)) makeVal(Lazy) + else { + val cacheFlags = if (ctx.owner.isClass) Private | Local | Mutable else Mutable + val cacheSym = + ctx.newSymbol(ctx.owner, CacheName(tree.name), cacheFlags, rhsType, coord = sym.coord) + if (ctx.owner.isClass) cacheSym.enteredAfter(thisPhase) + val cacheDef = ValDef(cacheSym, tpd.defaultValue(rhsType)) + val cachingDef = cpy.DefDef(tree)(rhs = + Block( + If( + ref(cacheSym).select(defn.Any_==).appliedTo(nullLiteral), + Assign(ref(cacheSym), tree.rhs), + unitLiteral) :: Nil, + ref(cacheSym) + ) + ) + Thicket(cacheDef, cachingDef) + } + case _ => tree + } + } +} + + + diff --git a/compiler/src/dotty/tools/dotc/transform/Constructors.scala b/compiler/src/dotty/tools/dotc/transform/Constructors.scala index f8448696f1b0..ecdb06326d54 100644 --- a/compiler/src/dotty/tools/dotc/transform/Constructors.scala +++ b/compiler/src/dotty/tools/dotc/transform/Constructors.scala @@ -235,7 +235,7 @@ class Constructors extends MiniPhase with IdentityDenotTransformer { thisPhase = else { // insert test: if ($outer eq null) throw new NullPointerException val nullTest = - If(ref(param).select(defn.Object_eq).appliedTo(Literal(Constant(null))), + If(ref(param).select(defn.Object_eq).appliedTo(nullLiteral), Throw(New(defn.NullPointerExceptionClass.typeRef, Nil)), unitLiteral) nullTest :: assigns diff --git a/compiler/src/dotty/tools/dotc/transform/Erasure.scala b/compiler/src/dotty/tools/dotc/transform/Erasure.scala index 9e3435225151..86b459c8adae 100644 --- a/compiler/src/dotty/tools/dotc/transform/Erasure.scala +++ b/compiler/src/dotty/tools/dotc/transform/Erasure.scala @@ -218,7 +218,7 @@ object Erasure { adaptToType(tree, underlying) else if (!(tree.tpe <:< tycon)) { assert(!(tree.tpe.typeSymbol.isPrimitiveValueClass)) - val nullTree = Literal(Constant(null)) + val nullTree = nullLiteral val unboxedNull = adaptToType(nullTree, underlying) evalOnce(tree) { t => diff --git a/compiler/src/dotty/tools/dotc/transform/LazyVals.scala b/compiler/src/dotty/tools/dotc/transform/LazyVals.scala index 473197bc8eff..1f57105fa9fe 100644 --- a/compiler/src/dotty/tools/dotc/transform/LazyVals.scala +++ b/compiler/src/dotty/tools/dotc/transform/LazyVals.scala @@ -189,14 +189,12 @@ class LazyVals extends MiniPhase with IdentityDenotTransformer { holders:::stats } - private def nullOut(nullables: List[Symbol])(implicit ctx: Context): List[Tree] = { - val nullConst = Literal(Constant(null)) + private def nullOut(nullables: List[Symbol])(implicit ctx: Context): List[Tree] = nullables.map { field => assert(field.isField) field.setFlag(Mutable) - ref(field).becomes(nullConst) + ref(field).becomes(nullLiteral) } - } /** Create non-threadsafe lazy accessor equivalent to such code * ``` @@ -237,7 +235,7 @@ class LazyVals extends MiniPhase with IdentityDenotTransformer { val targetRef = ref(target) val stats = targetRef.becomes(rhs) :: nullOut(nullableFor(sym)) val init = If( - targetRef.select(nme.eq).appliedTo(Literal(Constant(null))), + targetRef.select(nme.eq).appliedTo(nullLiteral), Block(stats.init, stats.last), unitLiteral ) diff --git a/compiler/src/dotty/tools/dotc/transform/Memoize.scala b/compiler/src/dotty/tools/dotc/transform/Memoize.scala index 725a92c82f9b..6689729d8502 100644 --- a/compiler/src/dotty/tools/dotc/transform/Memoize.scala +++ b/compiler/src/dotty/tools/dotc/transform/Memoize.scala @@ -106,8 +106,8 @@ class Memoize extends MiniPhase with IdentityDenotTransformer { thisPhase => val NoFieldNeeded = Lazy | Deferred | JavaDefined | (if (ctx.settings.YnoInline.value) EmptyFlags else Inline) def erasedBottomTree(sym: Symbol) = { - if (sym eq defn.NothingClass) Throw(Literal(Constant(null))) - else if (sym eq defn.NullClass) Literal(Constant(null)) + if (sym eq defn.NothingClass) Throw(nullLiteral) + else if (sym eq defn.NullClass) nullLiteral else if (sym eq defn.BoxedUnitClass) ref(defn.BoxedUnit_UNIT) else { assert(false, sym + " has no erased bottom tree") diff --git a/compiler/src/dotty/tools/dotc/transform/PatternMatcher.scala b/compiler/src/dotty/tools/dotc/transform/PatternMatcher.scala index d81321fc0058..2236c29462f2 100644 --- a/compiler/src/dotty/tools/dotc/transform/PatternMatcher.scala +++ b/compiler/src/dotty/tools/dotc/transform/PatternMatcher.scala @@ -361,7 +361,7 @@ object PatternMatcher { val unappPlan = if (defn.isBottomType(scrutinee.info)) { // Generate a throwaway but type-correct plan. // This plan will never execute because it'll be guarded by a `NonNullTest`. - ResultPlan(tpd.Throw(tpd.Literal(Constant(null)))) + ResultPlan(tpd.Throw(tpd.nullLiteral)) } else { val mt @ MethodType(_) = extractor.tpe.widen var unapp = extractor.appliedTo(ref(scrutinee).ensureConforms(mt.paramInfos.head)) diff --git a/compiler/src/dotty/tools/dotc/typer/Namer.scala b/compiler/src/dotty/tools/dotc/typer/Namer.scala index bbf1541fe222..4958a9aba6d2 100644 --- a/compiler/src/dotty/tools/dotc/typer/Namer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Namer.scala @@ -981,14 +981,14 @@ class Namer { typer: Typer => if (mbr.isType) ctx.newSymbol( cls, alias.toTypeName, - Final, + Exported | Final, fwdInfo(path.tpe.select(mbr.symbol), mbr.info), coord = span) else { val maybeStable = if (mbr.symbol.isStableMember) StableRealizable else EmptyFlags ctx.newSymbol( cls, alias, - Method | Final | maybeStable | mbr.symbol.flags & ImplicitOrImplied, + Exported | Method | Final | maybeStable | mbr.symbol.flags & ImplicitOrImplied, mbr.info.ensureMethodic, coord = span) } diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index 841651128888..55d7969ebe60 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -2591,6 +2591,8 @@ class Typer extends Namer tree else if (wtp.isContextual) adaptNoArgs(wtp) // insert arguments implicitly + else if (tree.symbol.isPrimaryConstructor && tree.symbol.info.firstParamTypes.isEmpty) + readapt(tree.appliedToNone) // insert () to primary constructors else errorTree(tree, em"Missing arguments for $methodStr") case _ => tryInsertApplyOrImplicit(tree, pt, locked) { diff --git a/docs/docs/internals/syntax.md b/docs/docs/internals/syntax.md index 7c071a2c9169..fad421ca4ef7 100644 --- a/docs/docs/internals/syntax.md +++ b/docs/docs/internals/syntax.md @@ -292,8 +292,9 @@ HkTypeParam ::= {Annotation} [‘+’ | ‘-’] (Id[HkTypeParamClause] | SubtypeBounds ClsParamClauses ::= {ClsParamClause} [[nl] ‘(’ [‘implicit’] ClsParams ‘)’] -ClsParamClause ::= [nl] [‘erased’] ‘(’ [ClsParams] ‘)’ - | ‘given’ [‘erased’] (‘(’ ClsParams ‘)’ | GivenTypes) + | {ClsParamClause} {GivenClsParamClause} +ClsParamClause ::= [‘erased’] (‘(’ ClsParams ‘)’ +GivenClsParamClause::= ‘given’ [‘erased’] (‘(’ ClsParams ‘)’ | GivenTypes) ClsParams ::= ClsParam {‘,’ ClsParam} ClsParam ::= {Annotation} ValDef(mods, id, tpe, expr) -- point of mods on val/var [{Modifier} (‘val’ | ‘var’) | ‘inline’] Param @@ -301,7 +302,8 @@ Param ::= id ‘:’ ParamType [‘=’ Expr] | INT DefParamClauses ::= {DefParamClause} [[nl] ‘(’ [‘implicit’] DefParams ‘)’] -DefParamClause ::= [nl] [‘erased’] ‘(’ [DefParams] ‘)’ | GivenParamClause + | {DefParamClause} {GivenParamClause} +DefParamClause ::= [‘erased’] (‘(’ DefParams ‘)’ GivenParamClause ::= ‘given’ [‘erased’] (‘(’ DefParams ‘)’ | GivenTypes) DefParams ::= DefParam {‘,’ DefParam} DefParam ::= {Annotation} [‘inline’] Param ValDef(mods, id, tpe, expr) -- point of mods at id. @@ -378,16 +380,17 @@ 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) -InstanceDef ::= [id] InstanceParams InstanceBody +InstanceDef ::= [id] [DefTypeParamClause] InstanceBody InstanceParams ::= [DefTypeParamClause] {GivenParamClause} -InstanceBody ::= [‘for’ ConstrApp {‘,’ ConstrApp }] [TemplateBody] - | ‘for’ Type ‘=’ Expr +InstanceBody ::= [‘for’ ConstrApp {‘,’ ConstrApp }] {GivenParamClause} [TemplateBody] + | ‘for’ Type {GivenParamClause} ‘=’ Expr Template ::= InheritClauses [TemplateBody] Template(constr, parents, self, stats) InheritClauses ::= [‘extends’ ConstrApps] [‘derives’ QualId {‘,’ QualId}] ConstrApps ::= ConstrApp {‘with’ ConstrApp} | ConstrApp {‘,’ ConstrApp} -ConstrApp ::= AnnotType {ArgumentExprs} Apply(tp, args) - | ‘(’ ConstrApp {‘given’ (InfixExpr | ParArgumentExprs)} ‘)’ +ConstrApp ::= SimpleConstrApp + | ‘(’ SimpleConstrApp {‘given’ (PrefixExpr | ParArgumentExprs)} ‘)’ +SimpleConstrApp ::= AnnotType {ArgumentExprs} Apply(tp, args) ConstrExpr ::= SelfInvocation | ConstrBlock SelfInvocation ::= ‘this’ ArgumentExprs {ArgumentExprs} diff --git a/docs/docs/reference/contextual-implicit/instance-defs.md b/docs/docs/reference/contextual-implicit/instance-defs.md index f5b7690a6ac5..e8c40d5e617e 100644 --- a/docs/docs/reference/contextual-implicit/instance-defs.md +++ b/docs/docs/reference/contextual-implicit/instance-defs.md @@ -71,12 +71,13 @@ An implicit instance without type parameters or given clause is created on-deman Here is the new syntax of implicit instances, seen as a delta from the [standard context free syntax of Scala 3](http://dotty.epfl.ch/docs/internals/syntax.html). ``` TmplDef ::= ... - | ‘implicit’ InstanceDef + | ‘implicit’ InstanceDef InstanceDef ::= [id] [DefTypeParamClause] InstanceBody -InstanceBody ::= [‘of’ ConstrApp {‘,’ ConstrApp }] {GivenParamClause} [TemplateBody] - | ‘of’ Type {GivenParamClause} ‘=’ Expr -ConstrApp ::= AnnotType {ArgumentExprs} - | ‘(’ ConstrApp {‘given’ (InfixExpr | ParArgumentExprs)} ‘)’ +InstanceBody ::= [‘for’ ConstrApp {‘,’ ConstrApp }] {GivenParamClause} [TemplateBody] + | ‘for’ Type {GivenParamClause} ‘=’ Expr +ConstrApp ::= SimpleConstrApp + | ‘(’ SimpleConstrApp {‘given’ (PrefixExpr | ParArgumentExprs)} ‘)’ +SimpleConstrApp ::= AnnotType {ArgumentExprs} GivenParamClause ::= ‘given’ (‘(’ [DefParams] ‘)’ | GivenTypes) GivenTypes ::= AnnotType {‘,’ AnnotType} ``` diff --git a/docs/docs/reference/contextual-implicit/relationship-implicits.md b/docs/docs/reference/contextual-implicit/relationship-implicits.md index 3eecbf99c95e..7cd67abbd81a 100644 --- a/docs/docs/reference/contextual-implicit/relationship-implicits.md +++ b/docs/docs/reference/contextual-implicit/relationship-implicits.md @@ -32,7 +32,7 @@ Implicit instances can be mapped to combinations of implicit objects and implici - If the right hand side is a simple reference, we can use a forwarder to that reference without caching it. - - If the right hand side is more complex, but still known to be pure, we can + - If the right hand side is more complex, but still known to be a pure path, we can create a `val` that computes it ahead of time. Examples: @@ -41,20 +41,21 @@ Implicit instances can be mapped to combinations of implicit objects and implici implicit global for ExecutionContext = new ForkJoinContext() implicit config for Config = default.config - def ctx: Context + val ctx: Context implicit for Context = ctx ``` would map to ```scala - private[this] var global$cache: ExecutionContext | Null = null + private[this] var global$_cache: ExecutionContext | Null = null final implicit def global: ExecutionContext = { - if (global$cache == null) global$cache = new ForkJoinContext() - global$cache + if (global$_cache == null) global$_cache = new ForkJoinContext() + global$_cache } final implicit val config: Config = default.config - final implicit def Context_repr = ctx + val ctx: Context + final implicit def Context_ev = ctx ``` ### Anonymous Implicit Instances diff --git a/docs/docs/reference/contextual/context-bounds.md b/docs/docs/reference/contextual/context-bounds.md index 3458c5cf6cd1..f6420fecdc4e 100644 --- a/docs/docs/reference/contextual/context-bounds.md +++ b/docs/docs/reference/contextual/context-bounds.md @@ -5,11 +5,11 @@ title: "Context Bounds" ## Context Bounds -A context bound is a shorthand for expressing a common pattern of an inferable parameter that depends on a type parameter. Using a context bound, the `maximum` function of the last section can be written like this: +A context bound is a shorthand for expressing a common pattern of a context parameter that depends on a type parameter. Using a context bound, the `maximum` function of the last section can be written like this: ```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 inferable parameter `given Ord[T]`. The inferable 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 a context parameter `given Ord[T]`. The context 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 ``` diff --git a/docs/docs/reference/contextual/conversions.md b/docs/docs/reference/contextual/conversions.md index 39efad0b1ccc..277a2a88a0af 100644 --- a/docs/docs/reference/contextual/conversions.md +++ b/docs/docs/reference/contextual/conversions.md @@ -14,7 +14,7 @@ implied for Conversion[String, Token] { def apply(str: String): Token = new KeyWord(str) } ``` -Using an implied alias this can be expressed more concisely as: +Using an implied alias instance this can be expressed more concisely as: ```scala implied for Conversion[String, Token] = new KeyWord(_) ``` @@ -25,7 +25,7 @@ An implicit conversion is applied automatically by the compiler in three situati 3. In an application `e.m(args)` with `e` of type `T`, if `T` does define some member(s) named `m`, but none of these members can be applied to the arguments `args`. -In the first case, the compiler looks in the implied scope for a an instance of +In the first case, the compiler looks for an implied instance of `scala.Conversion` that maps an argument of type `T` to type `S`. In the second and third case, it looks for an instance of `scala.Conversion` that maps an argument of type `T` to a type that defines a member `m` which can be applied to `args` if present. @@ -59,9 +59,9 @@ object Completions { // // CompletionArg.fromStatusCode(statusCode) - implied fromString for Conversion[String, CompletionArg] = Error(_) - implied fromFuture for Conversion[Future[HttpResponse], CompletionArg] = Response(_) - implied fromStatusCode for Conversion[Future[StatusCode], CompletionArg] = Status(_) + implied fromString for Conversion[String, CompletionArg] = Error(_) + implied fromFuture for Conversion[Future[HttpResponse], CompletionArg] = Response(_) + implied fromStatusCode for Conversion[Future[StatusCode], CompletionArg] = Status(_) } import CompletionArg._ diff --git a/docs/docs/reference/contextual/derivation.md b/docs/docs/reference/contextual/derivation.md index 568a3a9ff9b2..7dfc425cb02d 100644 --- a/docs/docs/reference/contextual/derivation.md +++ b/docs/docs/reference/contextual/derivation.md @@ -3,7 +3,7 @@ layout: doc-page title: Typeclass Derivation --- -Typeclass derivation is a way to generate instances of certain type classes automatically or with minimal code hints. A type class in this sense is any trait or class with a type parameter that describes the type being operated on. Commonly used examples are `Eql`, `Ordering`, `Show`, or `Pickling`. Example: +Typeclass derivation is a way to generate implied instances of certain type classes automatically or with minimal code hints. A type class in this sense is any trait or class with a type parameter that describes the type being operated on. Commonly used examples are `Eql`, `Ordering`, `Show`, or `Pickling`. Example: ```scala enum Tree[T] derives Eql, Ordering, Pickling { case Branch(left: Tree[T], right: Tree[T]) @@ -12,14 +12,14 @@ enum Tree[T] derives Eql, Ordering, Pickling { ``` The `derives` clause generates implied instances of the `Eql`, `Ordering`, and `Pickling` traits in the companion object `Tree`: ```scala -implied [T: Eql] for Eql[Tree[T]] = Eql.derived -implied [T: Ordering] for Ordering[Tree[T]] = Ordering.derived -implied [T: Pickling] for Pickling[Tree[T]] = Pickling.derived +implied [T: Eql] for Eql[Tree[T]] = Eql.derived +implied [T: Ordering] for Ordering[Tree[T]] = Ordering.derived +implied [T: Pickling] for Pickling[Tree[T]] = Pickling.derived ``` ### Deriving Types -Besides for `enums`, typeclasses can also be derived for other sets of classes and objects that form an algebraic data type. These are: +Besides for enums, typeclasses can also be derived for other sets of classes and objects that form an algebraic data type. These are: - individual case classes or case objects - sealed classes or traits that have only case classes and case objects as children. @@ -42,7 +42,7 @@ A trait or class can appear in a `derives` clause if its companion object define ```scala def derived[T] given Generic[T] = ... ``` -That is, the `derived` method takes an inferable parameter of type `Generic` that determines the _shape_ of the deriving type `T` and it computes the typeclass implementation according to that shape. An implied `Generic` instance is generated automatically for any type that derives a typeclass with a `derived` +That is, the `derived` method takes a context parameter of type `Generic` that determines the _shape_ of the deriving type `T` and it computes the typeclass implementation according to that shape. An implied `Generic` instance is generated automatically for any type that derives a typeclass with a `derived` method that refers to `Generic`. One can also derive `Generic` alone, which means a `Generic` instance is generated without any other type class instances. E.g.: ```scala sealed trait ParseResult[T] derives Generic @@ -142,7 +142,7 @@ abstract class Generic[T] { ``` It defines the `Shape` type for the ADT `T`, as well as two methods that map between a type `T` and a generic representation of `T`, which we call a `Mirror`: -The `reflect` method maps an instance value of the ADT `T` to its mirror whereas +The `reflect` method maps an instance of the ADT `T` to its mirror whereas the `reify` method goes the other way. There's also a `common` method that returns a value of type `GenericClass` which contains information that is the same for all instances of a class (right now, this consists of the runtime `Class` value and @@ -150,7 +150,7 @@ the names of the cases and their parameters). ### Mirrors -A mirror is a generic representation of an instance value of an ADT. `Mirror` objects have three components: +A mirror is a generic representation of an instance of an ADT. `Mirror` objects have three components: - `adtClass: GenericClass`: The representation of the ADT class - `ordinal: Int`: The ordinal number of the case among all cases of the ADT, starting from 0 @@ -208,15 +208,15 @@ a mirror over that array, and finally uses the `reify` method in `Reflected` to ### How to Write Generic Typeclasses -Based on the machinery developed so far it becomes possible to define type classes generically. This means that the `derived` method will compute a type class instance for any ADT that has a `Generic` instance, recursively. +Based on the machinery developed so far it becomes possible to define type classes generically. This means that the `derived` method will compute a type class instance for any ADT that has an implied `Generic` instance, recursively. The implementation of these methods typically uses three new type-level constructs in Dotty: inline methods, inline matches, and implicit matches. As an example, here is one possible implementation of a generic `Eql` type class, with explanations. Let's assume `Eql` is defined by the following trait: ```scala trait Eql[T] { def eql(x: T, y: T): Boolean } ``` -We need to implement a method `Eql.derived` that produces an instance of `Eql[T]` provided -there exists evidence of type `Generic[T]`. Here's a possible solution: +We need to implement a method `Eql.derived` that produces an instance for `Eql[T]` provided +there exists an implied instance for `Generic[T]`. Here's a possible solution: ```scala inline def derived[T] given (ev: Generic[T]): Eql[T] = new Eql[T] { def eql(x: T, y: T): Boolean = { @@ -307,7 +307,7 @@ The last, and in a sense most interesting part of the derivation is the comparis } ``` `tryEql` is an inline method that takes an element type `T` and two element values of that type as arguments. It is defined using an `implicit match` that tries to find an implied instance of `Eql[T]`. If an instance `ev` is found, it proceeds by comparing the arguments using `ev.eql`. On the other hand, if no instance is found -this signals a compilation error: the user tried a generic derivation of `Eql` for a class with an element type that does not support an `Eql` instance itself. The error is signaled by +this signals a compilation error: the user tried a generic derivation of `Eql` for a class with an element type that does not have an `Eql` instance itself. The error is signaled by calling the `error` method defined in `scala.compiletime`. **Note:** At the moment our error diagnostics for metaprogramming does not support yet interpolated string arguments for the `scala.compiletime.error` method that is called in the second case above. As an alternative, one can simply leave off the second case, then a missing typeclass would result in a "failure to reduce match" error. @@ -315,7 +315,7 @@ calling the `error` method defined in `scala.compiletime`. **Example:** Here is a slightly polished and compacted version of the code that's generated by inline expansion for the derived `Eql` instance of class `Tree`. ```scala -implied [T] given (elemEq: Eql[T]) for Eql[Tree[T]] { +implied [T] for Eql[Tree[T]] given (elemEq: Eql[T]) { def eql(x: Tree[T], y: Tree[T]): Boolean = { val ev = the[Generic[Tree[T]]] val mx = ev.reflect(x) @@ -336,14 +336,14 @@ implied [T] given (elemEq: Eql[T]) for Eql[Tree[T]] { One important difference between this approach and Scala-2 typeclass derivation frameworks such as Shapeless or Magnolia is that no automatic attempt is made to generate typeclass instances of elements recursively using the generic derivation framework. There must be an implied instance of `Eql[T]` (which can of course be produced in turn using `Eql.derived`), or the compilation will fail. The advantage of this more restrictive approach to typeclass derivation is that it avoids uncontrolled transitive typeclass derivation by design. This keeps code sizes smaller, compile times lower, and is generally more predictable. -### Derived Instances Elsewhere +### Deriving Instances Elsewhere Sometimes one would like to derive a typeclass instance for an ADT after the ADT is defined, without being able to change the code of the ADT itself. To do this, simply define an instance with the `derived` method of the typeclass as right-hand side. E.g, to implement `Ordering` for `Option`, define: ```scala -implied [T: Ordering]: Ordering[Option[T]] = Ordering.derived +implied [T: Ordering] for Ordering[Option[T]] = Ordering.derived ``` -Usually, the `Ordering.derived` clause has an inferable parameter of type +Usually, the `Ordering.derived` clause has a context parameter of type `Generic[Option[T]]`. Since the `Option` trait has a `derives` clause, the necessary implied instance is already present in the companion object of `Option`. If the ADT in question does not have a `derives` clause, an implied `Generic` instance diff --git a/docs/docs/reference/contextual/extension-methods.md b/docs/docs/reference/contextual/extension-methods.md index 5faee3fa81f5..c7a84d929fe0 100644 --- a/docs/docs/reference/contextual/extension-methods.md +++ b/docs/docs/reference/contextual/extension-methods.md @@ -90,11 +90,11 @@ implied StringOps { } } -implied ListOps { +implied { def (xs: List[T]) second[T] = xs.tail.head } ``` -If such implied instances are anonymous (as in the examples above), their name is synthesized from the name +If such a representative is anonymous (as in the second clause above), its name is synthesized from the name of the first defined extension method. ### Operators @@ -144,7 +144,3 @@ to the [current syntax](https://github.com/lampepfl/dotty/blob/master/docs/docs/ DefSig ::= ... | ‘(’ DefParam ‘)’ [nl] id [DefTypeParamClause] DefParamClauses ``` - - - - diff --git a/docs/docs/reference/contextual/import-implied.md b/docs/docs/reference/contextual/import-implied.md index dc10f3585d79..edf7f77dbcbd 100644 --- a/docs/docs/reference/contextual/import-implied.md +++ b/docs/docs/reference/contextual/import-implied.md @@ -1,6 +1,6 @@ --- layout: doc-page -title: "Implied Imports" +title: "Importing Implied Instances" --- A special form of import is used to import implied instances. Example: @@ -18,7 +18,7 @@ object B { In the code above, the `import A._` clause of object `B` will import all members of `A` _except_ the implied instance `tc`. Conversely, the second import `import implied A._` will import _only_ that implied instance. -Generally, a normal import clause brings all definitions except implied instances into scope whereas an `import implied` clause brings only implied instances into scope. +Generally, a normal import clause brings all members except implied instances into scope whereas an `import implied` clause brings only implied instances into scope. There are two main benefits arising from these rules: @@ -28,22 +28,22 @@ There are two main benefits arising from these rules: instances can be anonymous, so the usual recourse of using named imports is not practical. -### Relationship with Old-Style Implicits +### Migration -The rules of implied imports above have the consequence that a library +The rules for `import implied` 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 implied instances and imports. The following modifications avoid this hurdle to migration. - 1. An implied import 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 or - by an implied import. + 1. An `import implied` 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 implied`. - 2. In Scala 3.1, an old-style implicits accessed implicitly through a normal import + 2. In Scala 3.1, old-style implicits accessed through a normal import will give a deprecation warning. - 3. In some version after 3.1, an old-style implicits accessed implicitly through a normal import + 3. In some version after 3.1, an old-style implicit accessed through a normal import will give a compiler error. These rules mean that library users can use `import implied` to access old-style implicits in Scala 3.0, diff --git a/docs/docs/reference/contextual/inferable-by-name-parameters.md b/docs/docs/reference/contextual/inferable-by-name-parameters.md index 3e7c01b3ecfd..d5bd56b46d9d 100644 --- a/docs/docs/reference/contextual/inferable-by-name-parameters.md +++ b/docs/docs/reference/contextual/inferable-by-name-parameters.md @@ -1,9 +1,9 @@ --- layout: doc-page -title: "Inferable By-Name Parameters" +title: "By-Name Context Parameters" --- -Inferable by-name parameters can be used to avoid a divergent inferred expansion. Example: +Context parameters can be declared by-name to avoid a divergent inferred expansion. Example: ```scala trait Codec[T] { @@ -12,7 +12,7 @@ trait Codec[T] { implied intCodec for Codec[Int] = ??? -implied optionCodec[T] given (ev: => Codec[T]) for Codec[Option[T]] { +implied optionCodec[T] for Codec[Option[T]] given (ev: => Codec[T]) { def write(xo: Option[T]) = xo match { case Some(x) => ev.write(x) case None => @@ -24,14 +24,14 @@ val s = the[Codec[Option[Int]]] s.write(Some(33)) s.write(None) ``` -As is the case for a normal by-name parameter, the argument for the inferable parameter `ev` +As is the case for a normal by-name parameter, the argument for the context parameter `ev` is evaluated on demand. In the example above, if the option value `x` is `None`, it is not evaluated at all. -The synthesized argument for an inferable parameter is backed by a local val +The synthesized argument for a context parameter is backed by a local val if this is necessary to prevent an otherwise diverging expansion. -The precise steps for constructing an inferable argument for a by-name parameter of type `=> T` are as follows. +The precise steps for synthesizing an argument for a by-name context parameter of type `=> T` are as follows. 1. Create a new implied instance of type `T`: diff --git a/docs/docs/reference/contextual/inferable-params.md b/docs/docs/reference/contextual/inferable-params.md index a3d2b1e80fdc..7a0afbb717c3 100644 --- a/docs/docs/reference/contextual/inferable-params.md +++ b/docs/docs/reference/contextual/inferable-params.md @@ -1,12 +1,12 @@ --- layout: doc-page -title: "Inferable Parameters" +title: "Given Clauses" --- Functional programming tends to express most dependencies as simple function parameterization. This is clean and powerful, but it sometimes leads to functions that take many parameters and call trees where the same value is passed over and over again in long call chains to many -functions. Inferable parameters can help here since they enable the compiler to synthesize +functions. Given clauses can help here since they enable the compiler to synthesize repetitive arguments instead of the programmer having to write them explicitly. For example, given the [implied instances](./instance-defs.md) defined previously, @@ -15,37 +15,38 @@ a maximum function that works for any arguments for which an ordering exists can def max[T](x: T, y: T) given (ord: Ord[T]): T = if (ord.compare(x, y) < 1) y else x ``` -Here, `ord` is an _inferable parameter_. Inferable parameters are introduced with a `given` clause. +Here, `ord` is an _context parameter_ introduced with a `given` clause. The `max` method can be applied as follows: ```scala max(2, 3) given IntOrd ``` -The `given IntOrd` part provides the `IntOrd` instance as an argument for the `ord` parameter. But the point of inferable parameters is that this argument can also be left out (and it usually is). So the following +The `given IntOrd` part passes `IntOrd` as an argument for the `ord` parameter. But the point of +context parameters is that this argument can also be left out (and it usually is). So the following applications are equally valid: ```scala max(2, 3) max(List(1, 2, 3), Nil) ``` -## Anonymous Inferable Parameters +## Anonymous Context Parameters -In many situations, the name of an inferable parameter of a method need not be -mentioned explicitly at all, since it is only used in synthesized arguments for -other inferable parameters. In that case one can avoid defining a parameter name +In many situations, the name of a context parameter need not be +mentioned explicitly at all, since it is used only in synthesized arguments for +other context 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 = xs.reduceLeft(max) ``` -`maximum` takes an inferable parameter of type `Ord` only to pass it on as an +`maximum` takes a context parameter of type `Ord` only to pass it on as an inferred argument to `max`. The name of the parameter is left out. -Generally, inferable parameters may be given either as a parameter list `(p_1: T_1, ..., p_n: T_n)` +Generally, context 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. ## Inferring Complex Arguments -Here are two other methods that have an inferable parameter of type `Ord[T]`: +Here are two other methods that have a context parameter of type `Ord[T]`: ```scala def descending[T] given (asc: Ord[T]): Ord[T] = new Ord[T] { def compare(x: T, y: T) = asc.compare(y, x) @@ -63,49 +64,51 @@ maximum(xs) given (descending given ListOrd) maximum(xs) given (descending given (ListOrd given IntOrd)) ``` -## Mixing Inferable And Normal Parameters +## Multiple Given Clauses -Inferable parameters can be freely mixed with normal parameters. -An inferable parameter may be followed by a normal parameter and _vice versa_. -There can be several inferable parameter lists in a definition. Example: +There can be several given clauses in a definition. Example: ```scala -def f given (u: Universe) (x: u.T) given Context = ... - -implied global for Universe { type T = String ... } -implied ctx for Context { ... } +def f given (u: Universe) given (x: u.Context) = ... +``` +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 +implied global for Universe { type Context = ... } +implied ctx for global.Context { ... } ``` Then the following calls are all valid (and normalize to the last one) ```scala -f("abc") -(f given global)("abc") -f("abc") given ctx -(f given global)("abc") given ctx +f +(f given global) +(f given global) given ctx ``` +But `f given ctx` would give a type error. -## Querying Implied Instances +## Summoning Implied Instances -A method `the` in `Predef` creates an implied instance of a given type. For example, -the implied instance of `Ord[List[Int]]` is generated by +A method `the` in `Predef` returns the implied instance of a given type. For example, +the implied instance of `Ord[List[Int]]` is produced by ```scala -the[Ord[List[Int]]] // reduces to ListOrd given IntOrd +the[Ord[List[Int]]] // reduces to: ListOrd given IntOrd ``` -The `the` method is simply defined as the (non-widening) identity function over an inferable parameter. +The `the` method is simply defined as the (non-widening) identity function over a context parameter. ```scala def the[T] given (x: T): x.type = x ``` -Functions like `the` that have only inferable parameters are also called _context queries_. +Functions like `the` that have only context parameters are also called _context functions_. ## Syntax Here is the new syntax of parameters and arguments seen as a delta from the [standard context free syntax of Scala 3](http://dotty.epfl.ch/docs/internals/syntax.html). ``` -ClsParamClause ::= ... - | ‘given’ (‘(’ [ClsParams] ‘)’ | GivenTypes) -DefParamClause ::= ... - | GivenParamClause -GivenParamClause ::= ‘given’ (‘(’ DefParams ‘)’ | GivenTypes) -GivenTypes ::= AnnotType {‘,’ AnnotType} - -InfixExpr ::= ... - | InfixExpr ‘given’ (InfixExpr | ParArgumentExprs) +ClsParamClauses ::= ... + | {ClsParamClause} {GivenClsParamClause} +GivenClsParamClause ::= ‘given’ [‘erased’] (‘(’ ClsParams ‘)’ | GivenTypes) +DefParamClauses ::= ... + | {DefParamClause} {GivenParamClause} +GivenParamClause ::= ‘given’ [‘erased’] (‘(’ DefParams ‘)’ | GivenTypes) +GivenTypes ::= AnnotType {‘,’ AnnotType} + +InfixExpr ::= ... + | InfixExpr ‘given’ (InfixExpr | ParArgumentExprs) ``` diff --git a/docs/docs/reference/contextual/instance-defs.md b/docs/docs/reference/contextual/instance-defs.md index 79fddb295b52..b5775c307e9d 100644 --- a/docs/docs/reference/contextual/instance-defs.md +++ b/docs/docs/reference/contextual/instance-defs.md @@ -3,9 +3,9 @@ layout: doc-page title: "Implied Instances" --- -Implied instance definitions define "canonical" values of given types -that serve for synthesizing arguments to [inferable parameters](./inferable-params.html). Example: - +Implied instances define "canonical" values of given types +that can be synthesized by the compiler as arguments for +[given clauses](./inferable-params.html). Example: ```scala trait Ord[T] { def compare(x: T, y: T): Int @@ -18,7 +18,7 @@ implied IntOrd for Ord[Int] { if (x < y) -1 else if (x > y) +1 else 0 } -implied ListOrd[T] given (ord: Ord[T]) for Ord[List[T]] { +implied ListOrd[T] for Ord[List[T]] given (ord: Ord[T]) { def compare(xs: List[T], ys: List[T]): Int = (xs, ys) match { case (Nil, Nil) => 0 case (Nil, _) => -1 @@ -29,11 +29,11 @@ implied ListOrd[T] given (ord: Ord[T]) for Ord[List[T]] { } } ``` -This code defines a trait `Ord` and two implied instance definitions. `IntOrd` defines +This code defines a trait `Ord` and two implied instance clauses. `IntOrd` defines an implied instance for the type `Ord[Int]` whereas `ListOrd[T]` defines implied instances of `Ord[List[T]]` for all types `T` that come with an implied `Ord[T]` instance themselves. -The `given` clause in `ListOrd` defines an [inferable parameter](./inferable-params.html). -Inferable parameters are further explained in the next section. +The `given` clause in `ListOrd` defines an _context parameter_. +Given clauses are further explained in the [next section](./inferable-params.html). ## Anonymous Implied Instances @@ -41,24 +41,30 @@ The name of an implied instance can be left out. So the implied instance definit of the last section can also be expressed like this: ```scala implied for Ord[Int] { ... } -implied [T] given (ord: Ord[T]) for Ord[List[T]] { ... } +implied [T] for Ord[List[T]] given (ord: Ord[T]) { ... } ``` If the name of an instance is missing, the compiler will synthesize a name from the type(s) in the `for` clause. ## Implied Alias Instances -An implied alias instance defines an implied instance that is equal to some expression. E.g., assuming a global method `currentThreadPool` returning a value with a member `context`, one could define: +An implied alias instance defines an implied instance that is equal to some expression. E.g.: ```scala -implied ctx for ExecutionContext = currentThreadPool().context +implied global for ExecutionContext = new ForkJoinPool() ``` -This creates an implied instance `ctx` of type `ExecutionContext` that resolves to the right hand side `currentThreadPool().context`. Each time an implied instance of `ExecutionContext` is demanded, the result of evaluating the right-hand side expression is returned. +This creates an implied instance `global` of type `ExecutionContext` that resolves to the right hand side `new ForkJoinPool()`. +The first time `global` is accessed, a new `ForkJoinPool` is created, which is then +returned for this and all subsequent accesses to `global`. Alias instances may be anonymous, e.g. ```scala implied for Position = enclosingTree.position ``` -An implied alias instance can have type and context parameters just like any other implied instance definition, but it can only implement a single type. +An implied alias instance can have type parameters and given clauses just like any other implied instance, but it can only implement a single type. + +## Creating Implied Instances + +An implied instance without type parameters or given clause is created on-demand, the first time it is accessed. It is not required to ensure safe publication, which means that different threads might create different representatives for the same `implied` clause. If an implied instance has type parameters or a given clause, its definition is evaluated each time it is applied to arguments. ## Syntax @@ -66,11 +72,13 @@ Here is the new syntax of implied instance definitions, seen as a delta from the ``` TmplDef ::= ... | ‘implied’ InstanceDef -InstanceDef ::= [id] InstanceParams InstanceBody -InstanceParams ::= [DefTypeParamClause] {GivenParamClause} +InstanceDef ::= [id] [DefTypeParamClause] InstanceBody +InstanceBody ::= [‘for’ ConstrApp {‘,’ ConstrApp }] {GivenParamClause} [TemplateBody] + | ‘for’ Type {GivenParamClause} ‘=’ Expr +ConstrApp ::= SimpleConstrApp + | ‘(’ SimpleConstrApp {‘given’ (PrefixExpr | ParArgumentExprs)} ‘)’ +SimpleConstrApp ::= AnnotType {ArgumentExprs} GivenParamClause ::= ‘given’ (‘(’ [DefParams] ‘)’ | GivenTypes) -InstanceBody ::= [‘for’ ConstrApp {‘,’ ConstrApp }] [TemplateBody] - | ‘for’ Type ‘=’ Expr GivenTypes ::= AnnotType {‘,’ AnnotType} ``` The identifier `id` can be omitted only if either the `for` part or the template body is present. diff --git a/docs/docs/reference/contextual/motivation.md b/docs/docs/reference/contextual/motivation.md index 06bafd10c8bf..b625fb550212 100644 --- a/docs/docs/reference/contextual/motivation.md +++ b/docs/docs/reference/contextual/motivation.md @@ -47,11 +47,11 @@ Existing Scala programmers by and large have gotten used to the status quo and s The following pages introduce a redesign of contextual abstractions in Scala. They introduce four fundamental changes: - 1. [Implied Instances](./instance-defs.html) are a new way to define inferable terms. They replace implicit definitions. The core principle of the proposal is that, rather than mixing the `implicit` modifier with a large number of features, we have a single way to define terms that can be synthesized for types. + 1. [Implied Instances](./instance-defs.html) are a new way to define terms that can be synthesized. They replace the `implicit` modifiers on definitions. The core principle is that, rather than mixing the `implicit` modifier with a large number of features, we have a single way to define terms that can be synthesized for types. - 2. [Inferable Parameters](./inferable-params.html) are a new syntax for implicit _parameters_ and their _arguments_. Both are introduced with the same keyword, `given`. This unambiguously aligns parameters and arguments, solving a number of language warts. It also allows us to have several implicit parameter sections, and to have implicit parameters followed by normal ones. + 2. [Given Clauses](./inferable-params.html) are a new syntax for implicit _parameters_ and their _arguments_. Both are introduced with the same keyword, `given`. This unambiguously aligns parameters and arguments, solving a number of language warts. It also allows us to have several implicit parameter sections, and to have implicit parameters followed by normal ones. - 3. [Implied Imports](./import-implied.html) are new form of import that specifically imports implicit definitions and nothing else. New-style implicit definitions _must be_ imported with `import implied`, a plain import will no longer bring them into scope. + 3. [Import Implied](./import-implied.html) is new form of import that specifically imports implicit definitions and nothing else. New-style implied instances _must be_ imported with `import implied`, a plain import will no longer bring them into scope. 4. [Implicit Conversions](./conversions.html) are now expressed as implied instances of a standard `Conversion` class. All other forms of implicit conversions will be phased out. @@ -60,12 +60,12 @@ This section also contains pages describing other language features that are rel - [Context Bounds](./context-bounds.html), which carry over unchanged. - [Extension Methods](./extension-methods.html) replace implicit classes in a way that integrates better with typeclasses. - [Implementing Typeclasses](./typeclasses.html) demonstrates how some common typeclasses can be implemented using the new constructs. - - [Typeclass Derivation](./derivation.html) introduces constructs to automatically derive typeclasses for ADTs. + - [Typeclass Derivation](./derivation.html) introduces constructs to automatically derive implied typeclass instances for ADTs. - [Multiversal Equality](./multiversal-equality.html) introduces a special typeclass to support type safe equality. - - [Context Queries](./query-types.html) _aka_ implicit function types introduce a way to abstract over implicit parameterization. - - [Inferable By-Name Parameters](./inferable-by-name-parameters.html) are an essential tool to define recursive implicits without looping. - - [Relationship with Scala 2 Implicits](./relationship-implicits.html) discusses the relationship between old-style and new-style implicits and how to migrate from one to the other. + - [Contextual Functions](./query-types.html) provide a way to abstract over given clauses. + - [By-Name Context Parameters](./inferable-by-name-parameters.html) are an essential tool to define recursive synthesized values without looping. + - [Relationship with Scala 2 Implicits](./relationship-implicits.html) discusses the relationship between old-style implicits and new-style implied instances and given clauses and how to migrate from one to the other. Overall, the new design achieves a better separation of term inference from the rest of the language: There is a single way to define implied instances instead of a multitude of forms all taking an `implicit` modifier. There is a single way to introduce implicit parameters and arguments instead of conflating implicit with normal arguments. There is a separate way to import implied instances that does not allow them to hide in a sea of normal imports. And there is a single way to define an implicit conversion which is clearly marked as such and does not require special syntax. @@ -78,4 +78,3 @@ Could we achieve the same goals by tweaking existing implicits? After having tri - Third, even if we would somehow succeed with migration, we still have the problem how to teach this. We cannot make existing tutorials go away. Almost all existing tutorials start with implicit conversions, which will go away; they use normal imports, which will go away, and they explain calls to methods with implicit parameters by expanding them to plain applications, which will also go away. This means that we'd have to add modifications and qualifications to all existing literature and courseware, likely causing more confusion with beginners instead of less. By contrast, with a new syntax there is a clear criterion: Any book or courseware that mentions `implicit` is outdated and should be updated. - diff --git a/docs/docs/reference/contextual/multiversal-equality.md b/docs/docs/reference/contextual/multiversal-equality.md index 44631822c8b8..674fbd8f9bba 100644 --- a/docs/docs/reference/contextual/multiversal-equality.md +++ b/docs/docs/reference/contextual/multiversal-equality.md @@ -30,7 +30,7 @@ that derives `Eql`, e.g. ```scala class T derives Eql ``` -Alternatively, one can also provide the derived implied instance directly, like this: +Alternatively, one can also define an `Eql` instance directly, like this: ```scala implied for Eql[T, T] = Eql.derived ``` @@ -74,13 +74,13 @@ defined as follows: def eqlAny[L, R]: Eql[L, R] = Eql.derived ``` -Even though `eqlAny` is not declared `implied`, the compiler will still +Even though `eqlAny` is not declared with an `implied` clause, the compiler will still construct an `eqlAny` instance as answer to an implicit search for the type `Eql[L, R]`, unless `L` or `R` have `Eql` instances defined on them, or the language feature `strictEquality` is enabled The primary motivation for having `eqlAny` is backwards compatibility, -if this is of no concern one can disable `eqlAny` by enabling the language +if this is of no concern, one can disable `eqlAny` by enabling the language feature `strictEquality`. As for all language features this can be either done with an import @@ -98,7 +98,7 @@ class Box[T](x: T) derives Eql By the usual rules if [typeclass derivation](./derivation.html), this generates the following `Eql` instance in the companion object of `Box`: ```scala -implied [T, U] given Eql[T, U] for Eql[Box[T], Box[U]] = Eql.derived +implied [T, U] for Eql[Box[T], Box[U]] given Eql[T, U] = Eql.derived ``` That is, two boxes are comparable with `==` or `!=` if their elements are. Examples: ```scala @@ -140,7 +140,7 @@ The `Eql` object defines implied instances for - `java.lang.Number`, `java.lang.Boolean`, and `java.lang.Character`, - `scala.collection.Seq`, and `scala.collection.Set`. -Implied instances are defined so that everyone of these types is has a reflexive `Eql` instance, and the following holds: +Implied instances are defined so that every one of these types has a reflexive `Eql` instance, and the following holds: - Primitive numeric types can be compared with each other. - Primitive numeric types can be compared with subtypes of `java.lang.Number` (and _vice versa_). diff --git a/docs/docs/reference/contextual/query-types-spec.md b/docs/docs/reference/contextual/query-types-spec.md index 67c627ce79f4..608ea59c3443 100644 --- a/docs/docs/reference/contextual/query-types-spec.md +++ b/docs/docs/reference/contextual/query-types-spec.md @@ -1,6 +1,6 @@ --- layout: doc-page -title: "Context Query Types - More Details" +title: "Contextual Function Types - More Details" --- ## Syntax @@ -10,13 +10,13 @@ title: "Context Query Types - More Details" Expr ::= ... | `given' FunParams `=>' Expr -Context query types associate to the right, e.g. +Contextual function types associate to the right, e.g. `given S => given T => U` is the same as `given S => (given T => U)`. ## Implementation -Context query types are shorthands for class types that define `apply` -methods with inferable parameters. Specifically, the `N`-ary function type +Contextual function types are shorthands for class types that define `apply` +methods with given clauses. Specifically, the `N`-ary function type `T1, ..., TN => R` is a shorthand for the class type `ImplicitFunctionN[T1 , ... , TN, R]`. Such class types are assumed to have the following definitions, for any value of `N >= 1`: ```scala @@ -25,22 +25,23 @@ trait ImplicitFunctionN[-T1 , ... , -TN, +R] { def apply given (x1: T1 , ... , xN: TN): R } ``` -Context query types erase to normal function types, so these classes are +Contextual function types erase to normal function types, so these classes are generated on the fly for typechecking, but not realized in actual code. -Context query literals `given (x1: T1, ..., xn: Tn) => e` map -inferable parameters `xi` of types `Ti` to a result given by expression `e`. +Contextual lambdas `given (x1: T1, ..., xn: Tn) => e` map +context parameters `xi` of types `Ti` to the result of evaluating expression `e`. The scope of each implicit parameter `xi` is `e`. The parameters must have pairwise distinct names. -If the expected type of the query literal is of the form +If the expected type of the contextual lambda is of the form `scala.ImplicitFunctionN[S1, ..., Sn, R]`, the expected type of `e` is `R` and the type `Ti` of any of the parameters `xi` can be omitted, in which case `Ti -= Si` is assumed. If the expected type of the query literal is -some other type, all inferable parameter types must be explicitly given, and the expected type of `e` is undefined. The type of the query literal is `scala.ImplicitFunctionN[S1, ...,Sn, T]`, where `T` is the widened += Si` is assumed. If the expected type of the contextual lambda is +some other type, all context parameter types must be explicitly given, and the expected type of `e` is undefined. +The type of the contextual lambda is `scala.ImplicitFunctionN[S1, ...,Sn, T]`, where `T` is the widened type of `e`. `T` must be equivalent to a type which does not refer to any of -the inferable parameters `xi`. +the context parameters `xi`. -The query literal is evaluated as the instance creation +The contextual lambda is evaluated as the instance creation expression: ```scala new scala.ImplicitFunctionN[T1, ..., Tn, T] { @@ -50,20 +51,20 @@ new scala.ImplicitFunctionN[T1, ..., Tn, T] { In the case of a single untyped parameter, `given (x) => e` can be abbreviated to `given x => e`. -An inferable parameter may also be a wildcard represented by an underscore `_`. In +A context parameter may also be a wildcard represented by an underscore `_`. In that case, a fresh name for the parameter is chosen arbitrarily. 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 query types and should be removed. +of Scala 2.12 is subsumed by contextual function types and should be removed. -Query literals `given (x1: T1, ..., xn: Tn) => e` are +Contextual lambdas `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 query literal. This is analogous to the automatic +itself a contextual lambda. This is analogous to the automatic insertion of `scala.Function0` around expressions in by-name argument position. -Context query types generalize to `N > 22` in the same way that function types do, see [the corresponding +Contextual function types generalize to `N > 22` in the same way that function types do, see [the corresponding documentation](https://dotty.epfl.ch/docs/reference/dropped-features/limit22.html). ## Examples @@ -76,4 +77,4 @@ Gist](https://gist.github.com/OlivierBlanvillain/234d3927fe9e9c6fba074b53a7bd9 ### Type Checking -After desugaring no additional typing rules are required for context query types. +After desugaring no additional typing rules are required for contextual function types. diff --git a/docs/docs/reference/contextual/query-types.md b/docs/docs/reference/contextual/query-types.md index e903fdeedbc9..89a1f4419117 100644 --- a/docs/docs/reference/contextual/query-types.md +++ b/docs/docs/reference/contextual/query-types.md @@ -1,27 +1,27 @@ --- layout: doc-page -title: "Context Queries" +title: "Contextual Functions" --- -_Context queries_ are functions with (only) inferable parameters. -_Context query types_ are the types of first-class context queries. -Here is an example for a context query type: +_Contextual functions_ are functions with (only) context +parameters. Their types are _contextual function types_. Here is an +example of a contextual function type: ```scala -type Contextual[T] = given Context => T +type Executable[T] = given ExecutionContext => T ``` -A value of context query type is applied to inferred arguments, in -the same way a method with inferable parameters is applied. For instance: +A contextual function is applied to inferred arguments, in +the same way a method with context parameters is applied. For instance: ```scala - implied ctx for Context = ... + implied ec for ExecutionContext = ... - def f(x: Int): Contextual[Int] = ... + def f(x: Int): Executable[Int] = ... - f(2) given ctx // explicit argument + f(2) given ec // explicit argument f(2) // argument is inferred ``` -Conversely, if the expected type of an expression `E` is a context query +Conversely, if the expected type of an expression `E` is a contextual function type `given (T_1, ..., T_n) => U` and `E` is not already a -context query literal, `E` is converted to a context query literal by rewriting to +contextual lambda, `E` is converted to a contextual lambda by rewriting to ```scala given (x_1: T1, ..., x_n: Tn) => E ``` @@ -29,24 +29,24 @@ 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` are available as implied instances in `E`. -Like query types, query literals are written with a `given` prefix. They differ from normal function literals in two ways: +Like their types, contextual lamndas are written with a `given` prefix. They differ from normal lambdas in two ways: - 1. Their parameters are inferable. - 2. Their types are context query types. + 1. Their parameters are defined with a given clause. + 2. Their types are contextual function types. For example, continuing with the previous definitions, ```scala - def g(arg: Contextual[Int]) = ... + def g(arg: Executable[Int]) = ... - g(22) // is expanded to g(given ctx => 22) + g(22) // is expanded to g(given ev => 22) - g(f(2)) // is expanded to g(given ctx => f(2) given ctx) + 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 ``` ### Example: Builder Pattern -Context query types have considerable expressive power. For +Contextual function types have considerable expressive power. For instance, here is how they can support the "builder pattern", where the aim is to construct tables like this: ```scala @@ -79,7 +79,7 @@ addition of elements via `add`: case class Cell(elem: String) ``` Then, the `table`, `row` and `cell` constructor methods can be defined -in terms of query types to avoid the plumbing boilerplate +with contextual function types as parameters to avoid the plumbing boilerplate that would otherwise be necessary. ```scala def table(init: given Table => Unit) = { @@ -99,12 +99,12 @@ that would otherwise be necessary. ``` With that setup, the table construction code above compiles and expands to: ```scala - table { given $t: Table => - row { given $r: Row => + table { given ($t: Table) => + row { given ($r: Row) => cell("top left") given $r cell("top right") given $r } given $t - row { given $r: Row => + row { given ($r: Row) => cell("bottom left") given $r cell("bottom right") given $r } given $t @@ -112,7 +112,7 @@ With that setup, the table construction code above compiles and expands to: ``` ### Example: Postconditions -As a larger example, here is a way to define constructs for checking arbitrary postconditions using an extension method `ensuring`so that the checked result can be referred to simply by `result`. The example combines opaque aliases, context query types, and extension methods to provide a zero-overhead abstraction. +As a larger example, here is a way to define constructs for checking arbitrary postconditions using an extension method `ensuring`so that the checked result can be referred to simply by `result`. The example combines opaque aliases, contextual function types, and extension methods to provide a zero-overhead abstraction. ```scala object PostConditions { @@ -137,12 +137,12 @@ object Test { val s = List(1, 2, 3).sum.ensuring(result == 6) } ``` -**Explanations**: We use a context query type `given WrappedResult[T] => Boolean` +**Explanations**: We use a contextual 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 an implied instance of type `WrappedResult[T]` in scope to pass along to the `result` method. `WrappedResult` is a fresh type, to make sure that we do not get unwanted implied instances in scope (this is good practice in all cases -where inferable parameters are involved). Since `WrappedResult` is an opaque type alias, its +where context parameters are involved). Since `WrappedResult` is an opaque type alias, its values need not be boxed, and since `ensuring` is added as an extension method, its argument does not need boxing either. Hence, the implementation of `ensuring` is as about as efficient as the best possible code one could write by hand: diff --git a/docs/docs/reference/contextual/relationship-implicits.md b/docs/docs/reference/contextual/relationship-implicits.md index 9643e0eafe60..deb92c83d871 100644 --- a/docs/docs/reference/contextual/relationship-implicits.md +++ b/docs/docs/reference/contextual/relationship-implicits.md @@ -9,7 +9,7 @@ Many, but not all, of the new contextual abstraction features in Scala 3 can be ### Implied Instance Definitions -Implied instance definitions can be mapped to combinations of implicit objects, classes and implicit methods. +Implied instance definitions can be mapped to combinations of implicit objects and implicit methods together with normal classes. 1. Instance definitions without parameters are mapped to implicit objects. E.g., ```scala @@ -21,20 +21,40 @@ Implied instance definitions can be mapped to combinations of implicit objects, ``` 2. Parameterized instance definitions are mapped to combinations of classes and implicit methods. E.g., ```scala - implied ListOrd[T] given (ord: Ord[T]) for Ord[List[T]] { ... } + implied ListOrd[T] for Ord[List[T]] given (ord: Ord[T]) { ... } ``` maps to ```scala class ListOrd[T](implicit ord: Ord[T]) extends Ord[List[T]] { ... } final implicit def ListOrd[T](implicit ord: Ord[T]): ListOrd[T] = new ListOrd[T] ``` - 3. Implied alias instances map to implicit methods. E.g., + 3. Implied alias instances map to implicit methods. If an alias has neither type parameters nor a given clause, its right-hand side is cached in a variable. There are two cases that can be optimized: + + - If the right hand side is a simple reference, we can + use a forwarder to that reference without caching it. + - If the right hand side is more complex, but still known to be pure, we can + create a `val` that computes it ahead of time. + + Examples: + ```scala - implied ctx for ExecutionContext = ... + implied global for ExecutionContext = new ForkJoinContext() + implied config for Config = default.config + + val ctx: Context + implied for Context = ctx ``` - maps to + would map to ```scala - final implicit def ctx: ExecutionContext = ... + private[this] var global$cache: ExecutionContext | Null = null + final implicit def global: ExecutionContext = { + if (global$cache == null) global$cache = new ForkJoinContext() + global$cache + } + + final implicit val config: Config = default.config + + final implicit def Context_repr = ctx ``` ### Anonymous Implied Instances @@ -60,9 +80,9 @@ constructor of its first parameter. For example, the instance ``` gets the synthesized name `second_of_List_T_instance`. -### Inferable Parameters +### Context Parameters -The new inferable parameter syntax with `given` corresponds largely to Scala-2's implicit parameters. E.g. +The new context parameter syntax with `given` corresponds largely to Scala-2's implicit parameters. E.g. ```scala def max[T](x: T, y: T) given (ord: Ord[T]): T ``` @@ -71,7 +91,7 @@ would be written def max[T](x: T, y: T)(implicit ord: Ord[T]): T ``` in Scala 2. The main difference concerns applications of such parameters. -Explicit arguments to inferable parameters _must_ be written using `given`, +Explicit arguments to context parameters _must_ be written using `given`, 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. @@ -87,7 +107,7 @@ Context bounds are the same in both language versions. They expand to the respec **Note:** To ease migration, context bounds in Dotty map for a limited time to old-style implicit parameters for which arguments can be passed either with `given` or with a normal application. Once old-style implicits are deprecated, context bounds -will map to inferable parameters instead. +will map to given clauses instead. ### Extension Methods @@ -107,9 +127,9 @@ Extension methods in implicit instances have no direct counterpart in Scala-2. T Typeclass derivation has no direct counterpart in the Scala 2 language. Comparable functionality can be achieved by macro-based libraries such as Shapeless, Magnolia, or scalaz-deriving. -### Context Query types +### Contextual Function Types -Context Query types have no analogue in Scala 2. +Contextual function types have no analogue in Scala 2. ### Implicit By-Name Parameters @@ -149,7 +169,7 @@ can be expressed in Dotty as ### Abstract Implicits -An abstract implicit `val` or `def` in Scala 2 can be expressed in Dotty using a regular abstract definition and an implied alias. E.g., Scala 2's +An abstract implicit `val` or `def` in Scala 2 can be expressed in Dotty using a regular abstract definition and an implied alias instaance. E.g., Scala 2's ```scala implicit def symDeco: SymDeco ``` diff --git a/docs/docs/reference/contextual/typeclasses.md b/docs/docs/reference/contextual/typeclasses.md index 5c8ea5f0c5ac..e1547c82d069 100644 --- a/docs/docs/reference/contextual/typeclasses.md +++ b/docs/docs/reference/contextual/typeclasses.md @@ -3,9 +3,9 @@ layout: doc-page title: "Implementing Typeclasses" --- -Implied instance definitions, extension methods and context bounds +Implied instances, extension methods and context bounds allow a concise and natural expression of _typeclasses_. Typeclasses are just traits -with canonical implementations defined by implied instance definitions. Here are some examples of standard typeclasses: +with canonical implementations defined by implied instances. Here are some examples of standard typeclasses: ### Semigroups and monoids: diff --git a/library/src-3.x/dotty/DottyPredef.scala b/library/src-3.x/dotty/DottyPredef.scala index 701a96898898..49f275b0c98a 100644 --- a/library/src-3.x/dotty/DottyPredef.scala +++ b/library/src-3.x/dotty/DottyPredef.scala @@ -36,5 +36,5 @@ object DottyPredef { case ev: ValueOf[T] => ev.value } - inline def the[T](implicit x: T): x.type = x + inline def the[T] given (x: T): x.type = x } diff --git a/tests/neg/i5978.scala b/tests/neg/i5978.scala index 33fceb941082..7543ae141cd7 100644 --- a/tests/neg/i5978.scala +++ b/tests/neg/i5978.scala @@ -7,8 +7,8 @@ trait TokenParser[Token, R] object TextParser { implied TP for TokenParser[Char, Position[CharSequence]] {} - implied FromCharToken - given (T: TokenParser[Char, Position[CharSequence]]) for Conversion[Char, Position[CharSequence]] = ??? + implied FromCharToken for Conversion[Char, Position[CharSequence]] + given (T: TokenParser[Char, Position[CharSequence]]) = ??? } diff --git a/tests/neg/implicit-params.scala b/tests/neg/implicit-params.scala index ec38f07fa2aa..dfeb802541d0 100644 --- a/tests/neg/implicit-params.scala +++ b/tests/neg/implicit-params.scala @@ -1,24 +1,28 @@ object Test { case class C(x: Int) + case class D(x: Int) def f(x: Int) given (c: C) = x + c.x - def g(x: Int) given (c: C) (y: Int) = x + c.x + y + def g0(x: Int) given (c: C) (y: Int) = x + c.x + y // error + + def g(x: Int) given (c: C) given D = x + c.x + the[D].x // OK def h(x: Int) given () = x // error - implicit object C extends C(11) + implied C for C(11) + implied D for D(11) f(1) f(1) given C f given 2 // error f(1)(C) // error - g(1)(2) // OK - (g(1) given C)(2) // OK - g(1) given 2 // error - g(1) given C given 2 // error - g(1)(C)(2) // error - g(1)(C) given 2 // error + g(1) // OK + g(1) given C // OK + g(1) given C given D(0) // OK + g(1) given D // error + g(1)(D) // error + g(1)(C)(D) // error } \ No newline at end of file diff --git a/tests/neg/parser-stability-25.scala b/tests/neg/parser-stability-25.scala index 23db4498af0e..68fa28a9d9e1 100644 --- a/tests/neg/parser-stability-25.scala +++ b/tests/neg/parser-stability-25.scala @@ -11,5 +11,5 @@ class D extends (Int => 1) { } class Wrap(x: Int) -class E extends (Wrap)( // error +class E extends (Wrap)( // error // error // error \ No newline at end of file diff --git a/tests/pos/given-constrapps.scala b/tests/pos/given-constrapps.scala new file mode 100644 index 000000000000..020aa6231ebc --- /dev/null +++ b/tests/pos/given-constrapps.scala @@ -0,0 +1,29 @@ +class TC +val tc = TC() +class C given (x: TC) { + assert(x eq tc) +} +class C2(n: Int) given (x: TC) given List[TC] { + assert(x eq tc) + the[List[TC]].foreach(t => assert(t eq tc)) + + def this() given TC given List[TC] = this(1) +} + +class D extends (C given tc) +class D2 extends (C2(1) given tc given Nil) + +class Foo given TC { + assert(the[TC] != null) +} + +object Test extends App { + new (C given tc) + new (C() given tc) + new (C given tc) {} + new (C2(1) given tc given List(tc)) + new (C2(1) given tc given List(tc)) {} + new (C2() given tc given List(tc)) + def foo given TC = () + foo given tc +} \ No newline at end of file diff --git a/tests/pos/i5978.scala b/tests/pos/i5978.scala index 705c2a226346..4daf73e09fd1 100644 --- a/tests/pos/i5978.scala +++ b/tests/pos/i5978.scala @@ -9,8 +9,8 @@ package p1 { object TextParser { implied TP for TokenParser[Char, Position[CharSequence]] {} - implied FromCharToken - given (T: TokenParser[Char, Position[CharSequence]]) for Conversion[Char, Position[CharSequence]] = ??? + implied FromCharToken for Conversion[Char, Position[CharSequence]] + given (T: TokenParser[Char, Position[CharSequence]])= ??? } diff --git a/tests/pos/multiversal.scala b/tests/pos/multiversal.scala index 27197dbc6e2d..cbe295ff1ed4 100644 --- a/tests/pos/multiversal.scala +++ b/tests/pos/multiversal.scala @@ -1,7 +1,7 @@ object Test { import scala.Eql - implied [X, Y] given Eql[X, Y] for Eql[List[X], List[Y]] = Eql.derived + implied [X, Y] for Eql[List[X], List[Y]] given Eql[X, Y] = Eql.derived val b: Byte = 1 val c: Char = 2 diff --git a/tests/pos/reference/instances.scala b/tests/pos/reference/instances.scala index cb9631fb8d90..23f99bc207ee 100644 --- a/tests/pos/reference/instances.scala +++ b/tests/pos/reference/instances.scala @@ -37,7 +37,7 @@ object Instances extends Common { if (x < y) -1 else if (x > y) +1 else 0 } - implied ListOrd[T] given Ord[T] for Ord[List[T]] { + implied ListOrd[T] for Ord[List[T]] given Ord[T] { def (xs: List[T]) compareTo (ys: List[T]): Int = (xs, ys) match { case (Nil, Nil) => 0 case (Nil, _) => -1 @@ -132,7 +132,7 @@ object Instances extends Common { println(the[D[Int]]) } locally { - implied given Context for D[Int] + implied for D[Int] given Context println(the[D[Int]]) } } @@ -195,7 +195,7 @@ object AnonymousInstances extends Common { def (xs: List[T]) second[T] = xs.tail.head } - implied [From, To] given (c: Convertible[From, To]) for Convertible[List[From], List[To]] { + implied [From, To] for Convertible[List[From], List[To]] given (c: Convertible[From, To]) { def (x: List[From]) convert: List[To] = x.map(c.convert) } diff --git a/tests/pos/the-given.scala b/tests/pos/the-given.scala new file mode 100644 index 000000000000..0d4b1058b089 --- /dev/null +++ b/tests/pos/the-given.scala @@ -0,0 +1,8 @@ +object Test { + + class Encoder { def apply(x: Int): Int = x } + implied for Encoder + + the[Encoder](2) + +} \ No newline at end of file diff --git a/tests/run/implicit-alias.check b/tests/run/implicit-alias.check new file mode 100644 index 000000000000..6ca53492ab26 --- /dev/null +++ b/tests/run/implicit-alias.check @@ -0,0 +1,14 @@ += new +TC += x +TC += new VC +TC +TC += x.y +with given +TC +TC +with type params +TC +TC diff --git a/tests/run/implicit-alias.scala b/tests/run/implicit-alias.scala new file mode 100644 index 000000000000..56cd96edc951 --- /dev/null +++ b/tests/run/implicit-alias.scala @@ -0,0 +1,66 @@ +object Test extends App { + + class TC { + println("TC") + } + + class TC1 + implied for TC1 + + class TV(val tc: TC) extends AnyVal + + trait C { + val x: TC + implied for TC = x + the[TC] + the[TC] + } + class D extends C { + override val x = new TC + } + + locally{ + println("= new") + implied t for TC = new TC + the[TC] + the[TC] + } + + locally { + println("= x") + new D + } + + locally{ + println("= new VC") + implied t for TV = new TV(new TC) + the[TV] + the[TV] + } + + class TCC { + val tc: TC = new TC + } + + val tcc = new TCC + locally { + println("= x.y") + implied t for TC = tcc.tc + the[TC] + the[TC] + } + + locally { + println("with given") + implied t for TC given TC1 = new TC + the[TC] + the[TC] + } + + locally { + println("with type params") + implied t[X] for TC = new TC + the[TC] + the[TC] + } +} \ No newline at end of file diff --git a/tests/run/implicit-specifity.scala b/tests/run/implicit-specifity.scala index d21462d74f77..cc833cdc24d2 100644 --- a/tests/run/implicit-specifity.scala +++ b/tests/run/implicit-specifity.scala @@ -9,7 +9,7 @@ object Show { class Generic object Generic { implied gen for Generic = new Generic - implied showGen[T] given Generic for Show[T] = new Show[T](2) + implied showGen[T] for Show[T] given Generic = new Show[T](2) } class Generic2 @@ -25,9 +25,9 @@ object SubGen { object Contextual { trait Context implied ctx for Context - implied showGen[T] given Generic for Show[T] = new Show[T](2) - implied showGen[T] given Generic, Context for Show[T] = new Show[T](3) - implied showGen[T] given SubGen for Show[T] = new Show[T](4) + implied showGen[T] for Show[T] given Generic = new Show[T](2) + implied showGen[T] for Show[T] given Generic, Context = new Show[T](3) + implied showGen[T] for Show[T] given SubGen = new Show[T](4) } object Test extends App { diff --git a/tests/run/implied-divergence.scala b/tests/run/implied-divergence.scala index 739791581176..2557a7433816 100644 --- a/tests/run/implied-divergence.scala +++ b/tests/run/implied-divergence.scala @@ -6,7 +6,7 @@ implied e for E(null) object Test extends App { - implied f given (e: E) for E(e) + implied f for E(e) given (e: E) assert(the[E].toString == "E(E(null))") diff --git a/tests/run/implied-priority.scala b/tests/run/implied-priority.scala index cdd89d78631a..9e3a32b31b96 100644 --- a/tests/run/implied-priority.scala +++ b/tests/run/implied-priority.scala @@ -15,7 +15,7 @@ class LowPriorityImplicits { } object NormalImplicits extends LowPriorityImplicits { - implied t2[T] given Arg[T] for E[T]("norm") + implied t2[T]for E[T]("norm") given Arg[T] } def test1 = { @@ -38,8 +38,8 @@ object Priority { } object Impl2 { - implied t1[T] given Priority.Low for E[T]("low") - implied t2[T] given Priority.High given Arg[T] for E[T]("norm") + implied t1[T] for E[T]("low") given Priority.Low + implied t2[T] for E[T]("norm") given Priority.High given Arg[T] } def test2 = { @@ -102,7 +102,7 @@ def test3 = { */ object Impl4 { implied t1 for E[String]("string") - implied t2[T] given Arg[T] for E[T]("generic") + implied t2[T] for E[T]("generic") given Arg[T] } object fallback4 { @@ -133,7 +133,7 @@ object HigherPriority { } object fallback5 { - implied [T] given (ev: E[T] = new E[T]("fallback")) for (E[T] & HigherPriority) = HigherPriority.inject(ev) + implied [T] for (E[T] & HigherPriority) given (ev: E[T] = new E[T]("fallback")) = HigherPriority.inject(ev) } def test5 = { diff --git a/tests/run/implied-specifity-2.scala b/tests/run/implied-specifity-2.scala index 8818eb56c2fd..536a307fa271 100644 --- a/tests/run/implied-specifity-2.scala +++ b/tests/run/implied-specifity-2.scala @@ -15,36 +15,36 @@ class Foo[T](val i: Int) object Foo { def apply[T] given (fooT: Foo[T]): Int = fooT.i - implied foo[T] given Low for Foo[T](0) - implied foobar[T] given Low for Foo[Bar[T]](1) - implied foobarbaz given Low for Foo[Bar[Baz]](2) + implied foo[T] for Foo[T](0) given Low + implied foobar[T] for Foo[Bar[T]](1) given Low + implied foobarbaz for Foo[Bar[Baz]](2) given Low } class Bar[T] object Bar { - implied foobar[T] given Medium for Foo[Bar[T]](3) - implied foobarbaz given Medium for Foo[Bar[Baz]](4) + implied foobar[T] for Foo[Bar[T]](3) given Medium + implied foobarbaz for Foo[Bar[Baz]](4) given Medium } class Baz object Baz { - implied baz given High for Foo[Bar[Baz]](5) + implied baz for Foo[Bar[Baz]](5) given High } class Arg implied for Arg class Bam(val str: String) -implied lo given Low for Bam("lo") -implied hi given High given Arg for Bam("hi") +implied lo for Bam("lo") given Low +implied hi for Bam("hi") given High given Arg class Bam2(val str: String) -implied lo2 given Low for Bam2("lo") -implied mid2 given High given Arg for Bam2("mid") +implied lo2 for Bam2("lo") given Low +implied mid2 for Bam2("mid") given High given Arg implied hi2 for Bam2("hi") class Arg2 class Red(val str: String) -implied normal given Arg2 for Red("normal") -implied reduced given (ev: Arg2 | Low) for Red("reduced") +implied normal for Red("normal") given Arg2 +implied reduced for Red("reduced") given (ev: Arg2 | Low) object Test extends App { assert(Foo[Int] == 0) diff --git a/tests/run/tagless.scala b/tests/run/tagless.scala index e14a0c12b25e..8d199a762fd0 100644 --- a/tests/run/tagless.scala +++ b/tests/run/tagless.scala @@ -196,7 +196,7 @@ object Test extends App { // Added operation: negation pushdown enum NCtx { case Pos, Neg } - implied [T] given (e: Exp[T]) for Exp[NCtx => T] { + implied [T] for Exp[NCtx => T] given (e: Exp[T]) { import NCtx._ def lit(i: Int) = { case Pos => e.lit(i) @@ -216,7 +216,7 @@ object Test extends App { println(pushNeg(tf1[NCtx => String])) println(pushNeg(pushNeg(pushNeg(tf1))): String) - implied [T] given (e: Mult[T]) for Mult[NCtx => T] { + implied [T] for Mult[NCtx => T] given (e: Mult[T]) { import NCtx._ def mul(l: NCtx => T, r: NCtx => T): NCtx => T = { case Pos => e.mul(l(Pos), r(Pos))