diff --git a/community-build/community-projects/utest b/community-build/community-projects/utest index 5823688a9ae1..5e50fa9433f2 160000 --- a/community-build/community-projects/utest +++ b/community-build/community-projects/utest @@ -1 +1 @@ -Subproject commit 5823688a9ae1d3b911e374d7d29c45e73c3be3c2 +Subproject commit 5e50fa9433f295add461e386bedca0b410e19e68 diff --git a/compiler/src/dotty/tools/dotc/ast/Desugar.scala b/compiler/src/dotty/tools/dotc/ast/Desugar.scala index 36f71e6df03d..7a0adf49e4ea 100644 --- a/compiler/src/dotty/tools/dotc/ast/Desugar.scala +++ b/compiler/src/dotty/tools/dotc/ast/Desugar.scala @@ -559,7 +559,7 @@ object desugar { val copiedAccessFlags = if migrateTo3 then EmptyFlags else AccessFlags // Methods to add to a case class C[..](p1: T1, ..., pN: Tn)(moreParams) - // def _1: T1 = this.p1 + // def _1: T1 = this.p1 // ... // def _N: TN = this.pN (unless already given as valdef or parameterless defdef) // def copy(p1: T1 = p1: @uncheckedVariance, ..., @@ -572,7 +572,7 @@ object desugar { val caseClassMeths = { def syntheticProperty(name: TermName, tpt: Tree, rhs: Tree) = DefDef(name, Nil, Nil, tpt, rhs).withMods(synthetic) - + def productElemMeths = val caseParams = derivedVparamss.head.toArray val selectorNamesInBody = normalizedBody.collect { @@ -850,6 +850,7 @@ object desugar { * where every definition in `body` is expanded to an extension method * taking type parameters `tparams` and a leading paramter `(x: T)`. * See: collectiveExtensionBody + * TODO: drop this part */ def moduleDef(mdef: ModuleDef)(implicit ctx: Context): Tree = { val impl = mdef.impl @@ -906,6 +907,25 @@ object desugar { } } + /** Transform extension construct to list of extension methods */ + def extMethods(ext: ExtMethods)(using Context): Tree = flatTree { + for mdef <- ext.methods yield + if mdef.tparams.nonEmpty then + ctx.error("extension method cannot have type parameters here, all type parameters go after `extension`", + mdef.tparams.head.sourcePos) + defDef( + cpy.DefDef(mdef)( + name = mdef.name.toExtensionName, + tparams = ext.tparams ++ mdef.tparams, + vparamss = mdef.vparamss match + case vparams1 :: vparamss1 if !isLeftAssoc(mdef.name) => + vparams1 :: ext.vparamss ::: vparamss1 + case _ => + ext.vparamss ++ mdef.vparamss + ).withMods(mdef.mods | Extension) + ) + } + /** Transform the statements of a collective extension * @param stats the original statements as they were parsed * @param tparams the collective type parameters @@ -934,6 +954,7 @@ object desugar { stat match case mdef: DefDef => cpy.DefDef(mdef)( + name = mdef.name.toExtensionName, tparams = tparams ++ mdef.tparams, vparamss = vparamss ::: mdef.vparamss, ).withMods(mdef.mods | Extension) @@ -964,16 +985,21 @@ object desugar { /** The normalized name of `mdef`. This means * 1. Check that the name does not redefine a Scala core class. * If it does redefine, issue an error and return a mangled name instead of the original one. - * 2. If the name is missing (this can be the case for instance definitions), invent one instead. + * 2. Check that the name does not start with `extension_` unless the + * method is an extension method. + * 3. If the name is missing (this can be the case for instance definitions), invent one instead. */ def normalizeName(mdef: MemberDef, impl: Tree)(implicit ctx: Context): Name = { var name = mdef.name if (name.isEmpty) name = name.likeSpaced(inventGivenOrExtensionName(impl)) + def errPos = mdef.source.atSpan(mdef.nameSpan) if (ctx.owner == defn.ScalaPackageClass && defn.reservedScalaClassNames.contains(name.toTypeName)) { val kind = if (name.isTypeName) "class" else "object" - ctx.error(IllegalRedefinitionOfStandardKind(kind, name), mdef.sourcePos) + ctx.error(IllegalRedefinitionOfStandardKind(kind, name), errPos) name = name.errorName } + if name.isExtensionName && !mdef.mods.is(Extension) then + ctx.error(em"illegal method name: $name may not start with `extension_`", errPos) name } @@ -1247,7 +1273,7 @@ object desugar { } private def isTopLevelDef(stat: Tree)(using Context): Boolean = stat match - case _: ValDef | _: PatDef | _: DefDef | _: Export => true + case _: ValDef | _: PatDef | _: DefDef | _: Export | _: ExtMethods => true case stat: ModuleDef => stat.mods.isOneOf(GivenOrImplicit) case stat: TypeDef => diff --git a/compiler/src/dotty/tools/dotc/ast/untpd.scala b/compiler/src/dotty/tools/dotc/ast/untpd.scala index ff0b57f08155..36ef435764a6 100644 --- a/compiler/src/dotty/tools/dotc/ast/untpd.scala +++ b/compiler/src/dotty/tools/dotc/ast/untpd.scala @@ -114,6 +114,7 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo { case class ContextBounds(bounds: TypeBoundsTree, cxBounds: List[Tree])(implicit @constructorOnly src: SourceFile) extends TypTree case class PatDef(mods: Modifiers, pats: List[Tree], tpt: Tree, rhs: Tree)(implicit @constructorOnly src: SourceFile) extends DefTree case class Export(expr: Tree, selectors: List[ImportSelector])(implicit @constructorOnly src: SourceFile) extends Tree + case class ExtMethods(tparams: List[TypeDef], vparamss: List[List[ValDef]], methods: List[DefDef])(implicit @constructorOnly src: SourceFile) extends Tree case class MacroTree(expr: Tree)(implicit @constructorOnly src: SourceFile) extends Tree case class ImportSelector(imported: Ident, renamed: Tree = EmptyTree, bound: Tree = EmptyTree)(implicit @constructorOnly src: SourceFile) extends Tree { @@ -617,6 +618,9 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo { case tree: Export if (expr eq tree.expr) && (selectors eq tree.selectors) => tree case _ => finalize(tree, untpd.Export(expr, selectors)(tree.source)) } + def ExtMethods(tree: Tree)(tparams: List[TypeDef], vparamss: List[List[ValDef]], methods: List[DefDef])(using Context): Tree = tree match + case tree: ExtMethods if (tparams eq tree.tparams) && (vparamss eq tree.vparamss) && (methods == tree.methods) => tree + case _ => finalize(tree, untpd.ExtMethods(tparams, vparamss, methods)(tree.source)) def ImportSelector(tree: Tree)(imported: Ident, renamed: Tree, bound: Tree)(implicit ctx: Context): Tree = tree match { case tree: ImportSelector if (imported eq tree.imported) && (renamed eq tree.renamed) && (bound eq tree.bound) => tree case _ => finalize(tree, untpd.ImportSelector(imported, renamed, bound)(tree.source)) @@ -683,6 +687,8 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo { cpy.PatDef(tree)(mods, transform(pats), transform(tpt), transform(rhs)) case Export(expr, selectors) => cpy.Export(tree)(transform(expr), selectors) + case ExtMethods(tparams, vparamss, methods) => + cpy.ExtMethods(tree)(transformSub(tparams), vparamss.mapConserve(transformSub(_)), transformSub(methods)) case ImportSelector(imported, renamed, bound) => cpy.ImportSelector(tree)(transformSub(imported), transform(renamed), transform(bound)) case Number(_, _) | TypedSplice(_) => @@ -742,6 +748,8 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo { this(this(this(x, pats), tpt), rhs) case Export(expr, _) => this(x, expr) + case ExtMethods(tparams, vparamss, methods) => + this(vparamss.foldLeft(this(x, tparams))(apply), methods) case ImportSelector(imported, renamed, bound) => this(this(this(x, imported), renamed), bound) case Number(_, _) => diff --git a/compiler/src/dotty/tools/dotc/core/NameOps.scala b/compiler/src/dotty/tools/dotc/core/NameOps.scala index 550f3243df6e..c1423b61b204 100644 --- a/compiler/src/dotty/tools/dotc/core/NameOps.scala +++ b/compiler/src/dotty/tools/dotc/core/NameOps.scala @@ -131,6 +131,21 @@ object NameOps { else name.toTermName } + /** Does this name start with `extension_`? */ + def isExtensionName: Boolean = name match + case name: SimpleName => name.startsWith("extension_") + case _ => false + + /** Add an `extension_` in front of this name */ + def toExtensionName = termName("extension_" ++ name.toString) + + /** Drop `extension_` in front of this name, if it has this prefix */ + def dropExtension = name match + case name: SimpleName if name.startsWith("extension_") => + name.drop("extension_".length) + case _ => + name + /** The expanded name. * This is the fully qualified name of `base` with `ExpandPrefixName` as separator, * followed by `kind` and the name. diff --git a/compiler/src/dotty/tools/dotc/core/SymDenotations.scala b/compiler/src/dotty/tools/dotc/core/SymDenotations.scala index bd61fcb4da01..e5e5c1cad574 100644 --- a/compiler/src/dotty/tools/dotc/core/SymDenotations.scala +++ b/compiler/src/dotty/tools/dotc/core/SymDenotations.scala @@ -1190,6 +1190,15 @@ object SymDenotations { else if (this.exists) owner.enclosingMethod else NoSymbol + /** The closest enclosing extension method containing this definition, + * provided the extension method appears in the same class. + */ + final def enclosingExtensionMethod(using Context): Symbol = + if this.isAllOf(ExtensionMethod) then symbol + else if this.isClass then NoSymbol + else if this.exists then owner.enclosingExtensionMethod + else NoSymbol + /** The top-level class containing this denotation, * except for a toplevel module, where its module class is returned. */ diff --git a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala index e12d3a7fb8e3..c0ae8fbc2e76 100644 --- a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala +++ b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala @@ -227,7 +227,7 @@ object Parsers { || defIntroTokens.contains(in.token) || allowedMods.contains(in.token) || in.isSoftModifierInModifierPosition && !excludedSoftModifiers.contains(in.name) - || isIdent(nme.extension) && followingIsExtension() + || isIdent(nme.extension) && followingIsOldExtension() def isStatSep: Boolean = in.isNewLine || in.token == SEMI @@ -369,7 +369,7 @@ object Parsers { def acceptStatSep(): Unit = if in.isNewLine then in.nextToken() else accept(SEMI) - def acceptStatSepUnlessAtEnd(stats: ListBuffer[Tree], altEnd: Token = EOF): Unit = + def acceptStatSepUnlessAtEnd[T <: Tree](stats: ListBuffer[T], altEnd: Token = EOF): Unit = in.observeOutdented() in.token match case SEMI | NEWLINE | NEWLINES => @@ -921,7 +921,11 @@ object Parsers { skipParams() lookahead.isIdent(nme.as) - def followingIsExtension() = + def followingIsNewExtension() = + val next = in.lookahead.token + next == LBRACKET || next == LPAREN + + def followingIsOldExtension() = val lookahead = in.LookaheadScanner() lookahead.nextToken() if lookahead.isIdent && !lookahead.isIdent(nme.on) then @@ -932,6 +936,8 @@ object Parsers { || lookahead.token == LBRACE || lookahead.token == COLON + def followingIsExtension() = followingIsOldExtension() || followingIsNewExtension() + /* --------- OPERAND/OPERATOR STACK --------------------------------------- */ var opStack: List[OpInfo] = Nil @@ -1301,13 +1307,13 @@ object Parsers { else newLineOptWhenFollowedBy(LBRACE) - def checkEndMarker(stats: ListBuffer[Tree]): Unit = + def checkEndMarker[T <: Tree](stats: ListBuffer[T]): Unit = def matches(stat: Tree): Boolean = stat match case stat: MemberDef if !stat.name.isEmpty => if stat.name == nme.CONSTRUCTOR then in.token == THIS else in.isIdent && in.name == stat.name.toTermName - case ModuleDef(name, Template(_, Nil, _, _)) => + case ModuleDef(_, Template(_, Nil, _, _)) | ExtMethods(_, _, _) => in.token == IDENTIFIER && in.name == nme.extension case PackageDef(pid: RefTree, _) => in.isIdent && in.name == pid.name @@ -3177,11 +3183,6 @@ object Parsers { () => importSelection(simpleRef()) } - def posMods(start: Int, mods: Modifiers): Modifiers = { - in.nextToken() - mods - } - /** Def ::= val PatDef * | var VarDef * | def DefDef @@ -3202,9 +3203,9 @@ object Parsers { val mod1 = addMod(mods, mod) patDefOrDcl(start, mod1) case DEF => - defDefOrDcl(start, posMods(start, mods)) + defDefOrDcl(start, in.skipToken(mods)) case TYPE => - typeDefOrDcl(start, posMods(start, mods)) + typeDefOrDcl(start, in.skipToken(mods)) case CASE if inEnum => enumCase(start, mods) case _ => @@ -3269,7 +3270,7 @@ object Parsers { * DefSig ::= id [DefTypeParamClause] DefParamClauses * | ExtParamClause [nl] [‘.’] id DefParamClauses */ - def defDefOrDcl(start: Offset, mods: Modifiers): Tree = atSpan(start, nameStart) { + def defDefOrDcl(start: Offset, mods: Modifiers): DefDef = atSpan(start, nameStart) { def scala2ProcedureSyntax(resultTypeStr: String) = def toInsert = @@ -3319,7 +3320,8 @@ object Parsers { else (Nil, Nil) val ident = termIdent() - val name = ident.name.asTermName + var name = ident.name.asTermName + if mods1.is(Extension) then name = name.toExtensionName if isInfix && !name.isOperatorName then val infixAnnot = Apply(wrapNew(scalaAnnotationDot(tpnme.infix)), Nil) .withSpan(Span(start, start)) @@ -3449,23 +3451,23 @@ object Parsers { def tmplDef(start: Int, mods: Modifiers): Tree = in.token match { case TRAIT => - classDef(start, posMods(start, addFlag(mods, Trait))) + classDef(start, in.skipToken(addFlag(mods, Trait))) case SUPERTRAIT => - classDef(start, posMods(start, addFlag(mods, Trait | SuperTrait))) + classDef(start, in.skipToken(addFlag(mods, Trait | SuperTrait))) case CLASS => - classDef(start, posMods(start, mods)) + classDef(start, in.skipToken(mods)) case CASECLASS => - classDef(start, posMods(start, mods | Case)) + classDef(start, in.skipToken(mods | Case)) case OBJECT => - objectDef(start, posMods(start, mods | Module)) + objectDef(start, in.skipToken(mods | Module)) case CASEOBJECT => - objectDef(start, posMods(start, mods | Case | Module)) + objectDef(start, in.skipToken(mods | Case | Module)) case ENUM => - enumDef(start, posMods(start, mods | Enum)) + enumDef(start, in.skipToken(mods | Enum)) case GIVEN => givenDef(start, mods, atSpan(in.skipToken()) { Mod.Given() }) case _ => - if isIdent(nme.extension) && followingIsExtension() then + if isIdent(nme.extension) && followingIsOldExtension() then extensionDef(start, mods) else syntaxErrorOrIncomplete(ExpectedStartOfTopLevelDefinition()) @@ -3636,6 +3638,53 @@ object Parsers { val edef = atSpan(start, nameOffset, in.offset)(ModuleDef(name, templ)) finalizeDef(edef, addFlag(mods, Given | extensionFlag), start) + /** Extension ::= ‘extension’ [DefTypeParamClause] ‘(’ DefParam ‘)’ + * {UsingParamClause} ExtMethods + */ + def extension(): ExtMethods = + val start = in.skipToken() + val tparams = typeParamClauseOpt(ParamOwner.Def) + val extParams = paramClause(0, prefix = true) + val givenParamss = paramClauses(givenOnly = true) + in.observeColonEOL() + if (in.token == COLONEOL) in.nextToken() + val methods = + if isDefIntro(modifierTokens) then + extMethod() :: Nil + else + in.observeIndented() + newLineOptWhenFollowedBy(LBRACE) + if in.isNestedStart then inDefScopeBraces(extMethods()) + else { syntaxError("Extension without extension methods"); Nil } + val result = ExtMethods(tparams, extParams :: givenParamss, methods) + val comment = in.getDocComment(start) + if comment.isDefined then + for meth <- methods do + if !meth.rawComment.isDefined then meth.setComment(comment) + result + end extension + + /** ExtMethod ::= {Annotation [nl]} {Modifier} ‘def’ DefDef + */ + def extMethod(): DefDef = + val start = in.offset + val mods = defAnnotsMods(modifierTokens) + accept(DEF) + defDefOrDcl(start, mods) + + /** ExtMethods ::= ExtMethod | [nl] ‘{’ ExtMethod {semi ExtMethod ‘}’ + */ + def extMethods(): List[DefDef] = checkNoEscapingPlaceholders { + val meths = new ListBuffer[DefDef] + val exitOnError = false + while !isStatSeqEnd && !exitOnError do + setLastStatOffset() + meths += extMethod() + acceptStatSepUnlessAtEnd(meths) + if meths.isEmpty then syntaxError("`def` expected") + meths.toList + } + /* -------- TEMPLATES ------------------------------------------- */ /** ConstrApp ::= SimpleType1 {Annotation} {ParArgumentExprs} @@ -3750,6 +3799,7 @@ object Parsers { * | Annotations Modifiers Def * | Packaging * | package object objectDef + * | Extension * | */ def topStatSeq(outermost: Boolean = false): List[Tree] = { @@ -3768,7 +3818,9 @@ object Parsers { stats ++= importClause(IMPORT, mkImport(outermost)) else if (in.token == EXPORT) stats ++= importClause(EXPORT, Export.apply) - else if (in.token == AT || isDefIntro(modifierTokens)) + else if isIdent(nme.extension) && followingIsNewExtension() then + stats += extension() + else if isDefIntro(modifierTokens) stats +++= defOrDcl(in.offset, defAnnotsMods(modifierTokens)) else if (!isStatSep) if (in.token == CASE) @@ -3785,6 +3837,7 @@ object Parsers { * | Export * | Annotations Modifiers Def * | Annotations Modifiers Dcl + * | Extension * | Expr1 * | * EnumStat ::= TemplateStat @@ -3819,6 +3872,8 @@ object Parsers { stats ++= importClause(IMPORT, mkImport()) else if (in.token == EXPORT) stats ++= importClause(EXPORT, Export.apply) + else if isIdent(nme.extension) && followingIsNewExtension() then + stats += extension() else if (isDefIntro(modifierTokensOrCase)) stats +++= defOrDcl(in.offset, defAnnotsMods(modifierTokens)) else if (isExprIntro) @@ -3884,6 +3939,7 @@ object Parsers { * BlockStat ::= Import * | Annotations [implicit] [lazy] Def * | Annotations LocalModifiers TmplDef + * | Extension * | Expr1 * | */ @@ -3898,6 +3954,8 @@ object Parsers { stats += expr(Location.InBlock) else if in.token == IMPLICIT && !in.inModifierPosition() then stats += closure(in.offset, Location.InBlock, modifiers(BitSet(IMPLICIT))) + else if isIdent(nme.extension) && followingIsNewExtension() then + stats += extension() else if isDefIntro(localModifierTokens, excludedSoftModifiers = Set(nme.`opaque`)) then stats +++= localDef(in.offset) else if (!isStatSep && (in.token != CASE)) { diff --git a/compiler/src/dotty/tools/dotc/parsing/Scanners.scala b/compiler/src/dotty/tools/dotc/parsing/Scanners.scala index 9c4f16137e36..f8f0d5b2be72 100644 --- a/compiler/src/dotty/tools/dotc/parsing/Scanners.scala +++ b/compiler/src/dotty/tools/dotc/parsing/Scanners.scala @@ -296,6 +296,10 @@ object Scanners { off } + def skipToken[T](result: T): T = + nextToken() + result + def adjustSepRegions(lastToken: Token): Unit = (lastToken: @switch) match { case LPAREN | LBRACKET => currentRegion = InParens(lastToken, currentRegion) diff --git a/compiler/src/dotty/tools/dotc/printing/PlainPrinter.scala b/compiler/src/dotty/tools/dotc/printing/PlainPrinter.scala index 100891a50df0..4615e221635d 100644 --- a/compiler/src/dotty/tools/dotc/printing/PlainPrinter.scala +++ b/compiler/src/dotty/tools/dotc/printing/PlainPrinter.scala @@ -308,7 +308,7 @@ class PlainPrinter(_ctx: Context) extends Printer { } /** The string representation of this type used as a prefix, including separator */ - protected def toTextPrefix(tp: Type): Text = controlled { + def toTextPrefix(tp: Type): Text = controlled { homogenize(tp) match { case NoPrefix => "" case tp: SingletonType => toTextRef(tp) ~ "." diff --git a/compiler/src/dotty/tools/dotc/printing/Printer.scala b/compiler/src/dotty/tools/dotc/printing/Printer.scala index b2aae6e8bf2a..8584c889eeda 100644 --- a/compiler/src/dotty/tools/dotc/printing/Printer.scala +++ b/compiler/src/dotty/tools/dotc/printing/Printer.scala @@ -97,9 +97,12 @@ abstract class Printer { */ def toText(sym: Symbol): Text - /** Textual representation of singeton type reference */ + /** Textual representation of singleton type reference */ def toTextRef(tp: SingletonType): Text + /** Textual representation of a prefix of some reference, ending in `.` or `#` */ + def toTextPrefix(tp: Type): Text + /** Textual representation of symbol's declaration */ def dclText(sym: Symbol): Text diff --git a/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala b/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala index 5d5345262520..fa1218a80fec 100644 --- a/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala +++ b/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala @@ -541,6 +541,10 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) { keywordText("import ") ~ importText(expr, selectors) case Export(expr, selectors) => keywordText("export ") ~ importText(expr, selectors) + case ExtMethods(tparams, vparamss, mdefs) => + keywordText("extension ") + ~ addVparamssText(tparamsText(tparams), vparamss) + ~ " " ~ (if mdefs.length == 1 then toText(mdefs.head) else blockText(mdefs)) case packageDef: PackageDef => packageDefText(packageDef) case tree: Template => @@ -741,9 +745,11 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) { private def useSymbol(tree: untpd.Tree) = tree.hasType && tree.symbol.exists && ctx.settings.YprintSyms.value - protected def nameIdText[T >: Untyped](tree: NameTree[T]): Text = + protected def nameIdText[T >: Untyped](tree: NameTree[T], dropExtension: Boolean = false): Text = if (tree.hasType && tree.symbol.exists) { - val str: Text = nameString(tree.symbol) + var str = nameString(tree.symbol) + if tree.symbol.isExtensionMethod && dropExtension && str.startsWith("extension_") then + str = str.drop("extension_".length) tree match { case tree: RefTree => withPos(str, tree.sourcePos) case tree: MemberDef => withPos(str, tree.sourcePos.withSpan(tree.nameSpan)) @@ -788,7 +794,15 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) { val isExtension = tree.hasType && tree.symbol.isExtensionMethod withEnclosingDef(tree) { val (prefix, vparamss) = - if (isExtension) (defKeyword ~~ paramsText(tree.vparamss.head) ~~ valDefText(nameIdText(tree)), tree.vparamss.tail) + if isExtension then + val (leadingParams, otherParamss) = (tree.vparamss: @unchecked) match + case vparams1 :: vparams2 :: rest if !isLeftAssoc(tree.name) => + (vparams2, vparams1 :: rest) + case vparams1 :: rest => + (vparams1, rest) + (keywordStr("extension") ~~ paramsText(leadingParams) + ~~ (defKeyword ~~ valDefText(nameIdText(tree, dropExtension = true))).close, + otherParamss) else (defKeyword ~~ valDefText(nameIdText(tree)), tree.vparamss) addVparamssText(prefix ~ tparamsText(tree.tparams), vparamss) ~ diff --git a/compiler/src/dotty/tools/dotc/transform/PostTyper.scala b/compiler/src/dotty/tools/dotc/transform/PostTyper.scala index 316a2ed87707..d86aa086d917 100644 --- a/compiler/src/dotty/tools/dotc/transform/PostTyper.scala +++ b/compiler/src/dotty/tools/dotc/transform/PostTyper.scala @@ -10,7 +10,7 @@ import dotty.tools.dotc.typer.VarianceChecker import Types._, Contexts._, Names._, Flags._, DenotTransformers._, Phases._ import SymDenotations._, StdNames._, Annotations._, Trees._, Scopes._ import Decorators._ -import Symbols._, SymUtils._ +import Symbols._, SymUtils._, NameOps._ import ContextFunctionResults.annotateContextResults import config.Printers.typr import reporting.messages._ @@ -349,7 +349,9 @@ class PostTyper extends MacroTransform with IdentityDenotTransformer { thisPhase val seen = mutable.Set.empty[Name] def checkIdent(sel: untpd.ImportSelector): Unit = - if !exprTpe.member(sel.name).exists && !exprTpe.member(sel.name.toTypeName).exists then + if !exprTpe.member(sel.name).exists + && !exprTpe.member(sel.name.toTypeName).exists + && !exprTpe.member(sel.name.toExtensionName).exists then ctx.error(NotAMember(exprTpe, sel.name, "value"), sel.imported.sourcePos) if seen.contains(sel.name) then ctx.error(ImportRenamedTwice(sel.imported), sel.imported.sourcePos) diff --git a/compiler/src/dotty/tools/dotc/typer/Applications.scala b/compiler/src/dotty/tools/dotc/typer/Applications.scala index 6cafff446e8e..b8efedf721ab 100644 --- a/compiler/src/dotty/tools/dotc/typer/Applications.scala +++ b/compiler/src/dotty/tools/dotc/typer/Applications.scala @@ -18,6 +18,7 @@ import ErrorReporting._ import Trees._ import Names._ import StdNames._ +import NameOps._ import NameKinds.DefaultGetterName import ProtoTypes._ import Inferencing._ @@ -1338,13 +1339,14 @@ trait Applications extends Compatibility { followApply && tp.member(nme.apply).hasAltWith(d => p(TermRef(tp, nme.apply, d))) } - /** Does `tp` have an extension method named `name` with this-argument `argType` and + /** Does `tp` have an extension method named `extension_name` with this-argument `argType` and * result matching `resultType`? */ def hasExtensionMethod(tp: Type, name: TermName, argType: Type, resultType: Type)(using Context) = { + val xname = name.toExtensionName def qualifies(mbr: Denotation) = - mbr.exists && isApplicableType(tp.select(name, mbr), argType :: Nil, resultType) - tp.memberBasedOnFlags(name, required = ExtensionMethod) match { + mbr.exists && isApplicableType(tp.select(xname, mbr), argType :: Nil, resultType) + tp.memberBasedOnFlags(xname, required = ExtensionMethod) match { case mbr: SingleDenotation => qualifies(mbr) case mbr => mbr.hasAltWith(qualifies(_)) } @@ -2087,7 +2089,7 @@ trait Applications extends Compatibility { * * where comes from `pt` if it is a (possibly ignored) PolyProto. */ - def extMethodApply(methodRef: untpd.Tree, receiver: Tree, pt: Type)(using Context) = { + def extMethodApply(methodRef: untpd.Tree, receiver: Tree, pt: Type)(using Context): Tree = { /** Integrate the type arguments from `currentPt` into `methodRef`, and produce * a matching expected type. * If `currentPt` is ignored, the new expected type will be ignored too. diff --git a/compiler/src/dotty/tools/dotc/typer/Checking.scala b/compiler/src/dotty/tools/dotc/typer/Checking.scala index 465400e7cf0e..80b59d4f52f0 100644 --- a/compiler/src/dotty/tools/dotc/typer/Checking.scala +++ b/compiler/src/dotty/tools/dotc/typer/Checking.scala @@ -1125,7 +1125,7 @@ trait Checking { // would have produced the same symbol without errors def allowAccess(name: Name, sym: Symbol): Boolean = { val testCtx = caseCtx.fresh.setNewTyperState() - val ref = ctx.typer.typedIdent(untpd.Ident(name), WildcardType)(using testCtx) + val ref = ctx.typer.typedIdent(untpd.Ident(name).withSpan(stat.span), WildcardType)(using testCtx) ref.symbol == sym && !testCtx.reporter.hasErrors } checkRefsLegal(tree, cdef.symbol, allowAccess, "enum case") diff --git a/compiler/src/dotty/tools/dotc/typer/ErrorReporting.scala b/compiler/src/dotty/tools/dotc/typer/ErrorReporting.scala index 540901935613..158214e38f18 100644 --- a/compiler/src/dotty/tools/dotc/typer/ErrorReporting.scala +++ b/compiler/src/dotty/tools/dotc/typer/ErrorReporting.scala @@ -157,10 +157,10 @@ object ErrorReporting { if qualType.derivesFrom(defn.DynamicClass) then "\npossible cause: maybe a wrong Dynamic method signature?" else if attempts.nonEmpty then + val attemptStrings = attempts.map(_.showIndented(4)).distinct val extMethods = - if attempts.length > 1 then "Extension methods were" + if attemptStrings.length > 1 then "Extension methods were" else "An extension method was" - val attemptStrings = attempts.map(_.showIndented(4)) i""". |$extMethods tried, but could not be fully constructed: | diff --git a/compiler/src/dotty/tools/dotc/typer/Implicits.scala b/compiler/src/dotty/tools/dotc/typer/Implicits.scala index 85bec4651f96..ad9a73c01aa2 100644 --- a/compiler/src/dotty/tools/dotc/typer/Implicits.scala +++ b/compiler/src/dotty/tools/dotc/typer/Implicits.scala @@ -71,7 +71,7 @@ object Implicits { */ def hasExtMethod(tp: Type, expected: Type)(using Context) = expected match case SelectionProto(name, _, _, _) => - tp.memberBasedOnFlags(name, required = ExtensionMethod).exists + tp.memberBasedOnFlags(name.toExtensionName, required = ExtensionMethod).exists case _ => false def strictEquality(using Context): Boolean = @@ -91,13 +91,18 @@ object Implicits { /** The implicit references */ def refs: List[ImplicitRef] - private var SingletonClass: ClassSymbol = null + /** If comes from an implicit scope of a type, the companion objects making + * up that implicit scope, otherwise the empty set. + */ + def companionRefs: TermRefSet = TermRefSet.empty + + private var mySingletonClass: ClassSymbol = null /** Widen type so that it is neither a singleton type nor a type that inherits from scala.Singleton. */ private def widenSingleton(tp: Type)(using Context): Type = { - if (SingletonClass == null) SingletonClass = defn.SingletonClass + if (mySingletonClass == null) mySingletonClass = defn.SingletonClass val wtp = tp.widenSingleton - if (wtp.derivesFrom(SingletonClass)) defn.AnyType else wtp + if (wtp.derivesFrom(mySingletonClass)) defn.AnyType else wtp } protected def isAccessible(ref: TermRef)(using Context): Boolean @@ -106,6 +111,10 @@ object Implicits { protected def filterMatching(pt: Type)(using Context): List[Candidate] = { record("filterMatching") + val considerExtension = pt match + case ViewProto(_, _: SelectionProto) => true + case _ => false + def candidateKind(ref: TermRef)(using Context): Candidate.Kind = { /*trace(i"candidateKind $ref $pt")*/ def viewCandidateKind(tpw: Type, argType: Type, resType: Type): Candidate.Kind = { @@ -134,8 +143,9 @@ object Implicits { case rtp => viewCandidateKind(wildApprox(rtp), argType, resType) } - case tpw: TermRef => - Candidate.Conversion | Candidate.Extension // can't discard overloaded refs + case tpw: TermRef => // can't discard overloaded refs + Candidate.Conversion + | (if considerExtension then Candidate.Extension else Candidate.None) case tpw => // Only direct instances of Function1 and direct or indirect instances of <:< are eligible as views. // However, Predef.$conforms is not eligible, because it is a no-op. @@ -162,12 +172,11 @@ object Implicits { val isImplicitConversion = tpw.derivesFrom(defn.ConversionClass) // An implementation of <:< counts as a view val isConforms = tpw.derivesFrom(defn.SubTypeClass) - val hasExtensions = hasExtMethod(tpw, resType) val conversionKind = if (isFunctionInS2 || isImplicitConversion || isConforms) Candidate.Conversion else Candidate.None val extensionKind = - if (hasExtensions) Candidate.Extension + if considerExtension && hasExtMethod(tpw, resType) then Candidate.Extension else Candidate.None conversionKind | extensionKind } @@ -227,18 +236,27 @@ object Implicits { } - if (refs.isEmpty) Nil - else { + if refs.isEmpty && (!considerExtension || companionRefs.isEmpty) then + Nil + else val nestedCtx = ctx.fresh.addMode(Mode.TypevarsMissContext) - def matchingCandidate(ref: ImplicitRef): Option[Candidate] = - nestedCtx.test(candidateKind(ref.underlyingRef)) match { - case Candidate.None => None - case ckind => Some(new Candidate(ref, ckind, level)) - } - - refs.flatMap(matchingCandidate) - } + def matchingCandidate(ref: ImplicitRef, extensionOnly: Boolean): Option[Candidate] = + var ckind = nestedCtx.test(candidateKind(ref.underlyingRef)) + if extensionOnly then ckind &= Candidate.Extension + if ckind == Candidate.None then None + else Some(new Candidate(ref, ckind, level)) + + val extensionCandidates = + if considerExtension then + companionRefs.toList + .filterConserve(!_.symbol.isOneOf(GivenOrImplicit)) // implicit objects are already in `refs` + .flatMap(matchingCandidate(_, extensionOnly = true)) + else + Nil + val implicitCandidates = + refs.flatMap(matchingCandidate(_, extensionOnly = false)) + extensionCandidates ::: implicitCandidates } } @@ -246,7 +264,7 @@ object Implicits { * @param tp the type determining the implicit scope * @param companionRefs the companion objects in the implicit scope. */ - class OfTypeImplicits(tp: Type, val companionRefs: TermRefSet)(initctx: Context) extends ImplicitRefs(initctx) { + class OfTypeImplicits(tp: Type, override val companionRefs: TermRefSet)(initctx: Context) extends ImplicitRefs(initctx) { assert(initctx.typer != null) implicits.println(i"implicit scope of type $tp = ${companionRefs.toList}%, %") @threadUnsafe lazy val refs: List[ImplicitRef] = { @@ -492,8 +510,6 @@ trait ImplicitRunInfo: private val implicitScopeCache = mutable.AnyRefMap[Type, OfTypeImplicits]() - private val EmptyTermRefSet = new TermRefSet(using NoContext) - private def isExcluded(sym: Symbol) = if migrateTo3 then false else sym.is(Package) || sym.isPackageObject @@ -564,7 +580,7 @@ trait ImplicitRunInfo: case None => if seen.contains(t) then incomplete.addEntry(tp) // all references for `t` will be accounted for in `seen` so we return `EmptySet`. - EmptyTermRefSet // on the other hand, the refs of `tp` are now inaccurate, so `tp` is marked incomplete. + TermRefSet.empty // on the other hand, the refs of `tp` are now inaccurate, so `tp` is marked incomplete. else seen.addEntry(t) val is = recur(t) @@ -755,7 +771,7 @@ trait Implicits { self: Typer => } } - private var synthesizer: Synthesizer | Null = null + private var synthesizer: Synthesizer = null /** Find an implicit argument for parameter `formal`. * Return a failure as a SearchFailureType in the type of the returned tree. @@ -767,7 +783,7 @@ trait Implicits { self: Typer => if fail.isAmbiguous then failed else if synthesizer == null then synthesizer = Synthesizer(this) - synthesizer.nn.tryAll(formal, span).orElse(failed) + synthesizer.tryAll(formal, span).orElse(failed) /** Search an implicit argument and report error if not found */ def implicitArgTree(formal: Type, span: Span)(using Context): Tree = { @@ -1027,13 +1043,21 @@ trait Implicits { self: Typer => } pt match case SelectionProto(name: TermName, mbrType, _, _) if cand.isExtension => - val result = extMethodApply(untpd.Select(untpdGenerated, name), argument, mbrType) - if !ctx.reporter.hasErrors && cand.isConversion then - val testCtx = ctx.fresh.setExploreTyperState() - tryConversion(using testCtx) - if testCtx.reporter.hasErrors then - ctx.error(em"ambiguous implicit: $generated is eligible both as an implicit conversion and as an extension method container") - result + def tryExtension(using Context) = + extMethodApply(untpd.Select(untpdGenerated, name.toExtensionName), argument, mbrType) + if cand.isConversion then + val extensionCtx, conversionCtx = ctx.fresh.setNewTyperState() + val extensionResult = tryExtension(using extensionCtx) + val conversionResult = tryConversion(using conversionCtx) + if !extensionCtx.reporter.hasErrors then + extensionCtx.typerState.commit() + if !conversionCtx.reporter.hasErrors then + ctx.error(em"ambiguous implicit: $generated is eligible both as an implicit conversion and as an extension method container") + extensionResult + else + conversionCtx.typerState.commit() + conversionResult + else tryExtension case _ => tryConversion } @@ -1633,9 +1657,11 @@ final class SearchRoot extends SearchHistory { } /** A set of term references where equality is =:= */ -final class TermRefSet(using Context) { +sealed class TermRefSet(using Context): private val elems = new java.util.LinkedHashMap[TermSymbol, List[Type]] + def isEmpty = elems.size == 0 + def += (ref: TermRef): Unit = { val pre = ref.prefix val sym = ref.symbol.asTerm @@ -1663,4 +1689,11 @@ final class TermRefSet(using Context) { } override def toString = toList.toString -} + +object TermRefSet: + + @sharable val empty = new TermRefSet(using NoContext): + override def += (ref: TermRef): Unit = throw UnsupportedOperationException("+=") + +end TermRefSet + diff --git a/compiler/src/dotty/tools/dotc/typer/ImportSuggestions.scala b/compiler/src/dotty/tools/dotc/typer/ImportSuggestions.scala index 6bcb816b1ce4..0bdb6d2507c6 100644 --- a/compiler/src/dotty/tools/dotc/typer/ImportSuggestions.scala +++ b/compiler/src/dotty/tools/dotc/typer/ImportSuggestions.scala @@ -6,6 +6,7 @@ import core._ import Contexts._, Types._, Symbols._, Names._, Decorators._, ProtoTypes._ import Flags._, SymDenotations._ import NameKinds.FlatName +import NameOps._ import config.Printers.{implicits, implicitsDetailed} import util.Spans.Span import ast.{untpd, tpd} @@ -215,7 +216,7 @@ trait ImportSuggestions: * applicable to `argType`. */ def extensionMethod(site: TermRef, name: TermName, argType: Type): Option[TermRef] = - site.member(name) + site.member(name.toExtensionName) .alternatives .map(mbr => TermRef(site, mbr.symbol)) .filter(ref => @@ -300,7 +301,7 @@ trait ImportSuggestions: if filled < n && rest.nonEmpty then rest.toList.best(n - filled) else Nil top.take(filled).toList ++ remaining - end best + //end best TODO: re-enable with new syntax /** An addendum to an error message where the error might be fixed * by some implicit value of type `pt` that is however not found. @@ -315,7 +316,12 @@ trait ImportSuggestions: if fullMatches.nonEmpty then (fullMatches, "fix") else (headMatches, "make progress towards fixing") def importString(ref: TermRef): String = - s" import ${ctx.printer.toTextRef(ref).show}" + val imported = + if ref.symbol.isAllOf(ExtensionMethod) then + s"${ctx.printer.toTextPrefix(ref.prefix).show}${ref.symbol.name.dropExtension}" + else + ctx.printer.toTextRef(ref).show + s" import $imported" val suggestions = suggestedRefs .zip(suggestedRefs.map(importString)) .filter((ref, str) => str.contains('.')) // must be a real import with `.` diff --git a/compiler/src/dotty/tools/dotc/typer/Inferencing.scala b/compiler/src/dotty/tools/dotc/typer/Inferencing.scala index c6b241458ee1..401d44d40480 100644 --- a/compiler/src/dotty/tools/dotc/typer/Inferencing.scala +++ b/compiler/src/dotty/tools/dotc/typer/Inferencing.scala @@ -530,7 +530,7 @@ trait Inferencing { this: Typer => * is in i7558.scala: * * type Tr[+V1, +O1 <: V1] - * def [V2, O2 <: V2](tr: Tr[V2, O2]) sl: Tr[V2, O2] = ??? + * extension [V2, O2 <: V2](tr: Tr[V2, O2]) def sl: Tr[V2, O2] = ??? * def as[V3, O3 <: V3](tr: Tr[V3, O3]) : Tr[V3, O3] = tr.sl * * Here we interpolate at some point V2 and O2 given the constraint diff --git a/compiler/src/dotty/tools/dotc/typer/Namer.scala b/compiler/src/dotty/tools/dotc/typer/Namer.scala index c2da3d90b668..d6408d00776e 100644 --- a/compiler/src/dotty/tools/dotc/typer/Namer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Namer.scala @@ -489,15 +489,16 @@ class Namer { typer: Typer => tree.pushAttachment(ExpandedTree, expanded) } tree match { - case tree: DefTree => record(desugar.defTree(tree)) + case tree: DefTree => record(desugar.defTree(tree)) case tree: PackageDef => record(desugar.packageDef(tree)) - case _ => + case tree: ExtMethods => record(desugar.extMethods(tree)) + case _ => } } /** The expanded version of this tree, or tree itself if not expanded */ def expanded(tree: Tree)(using Context): Tree = tree match { - case _: DefTree | _: PackageDef => tree.attachmentOrElse(ExpandedTree, tree) + case _: DefTree | _: PackageDef | _: ExtMethods => tree.attachmentOrElse(ExpandedTree, tree) case _ => tree } @@ -1155,7 +1156,7 @@ class Namer { typer: Typer => def addForwardersNamed(name: TermName, alias: TermName, span: Span): Unit = { val size = buf.size - val mbrs = List(name, name.toTypeName).flatMap(path.tpe.member(_).alternatives) + val mbrs = List(name, name.toTypeName, name.toExtensionName).flatMap(path.tpe.member(_).alternatives) mbrs.foreach(addForwarder(alias, _, span)) if (buf.size == size) { val reason = mbrs.map(whyNoForwarder).dropWhile(_ == SKIP) match { diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index 12051ad2433e..635b1cead7d7 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -213,6 +213,9 @@ class Typer extends Namer def namedImportRef(imp: ImportInfo)(using Context): Type = { val termName = name.toTermName + def adjustExtension(name: Name) = + if required.is(Extension) then name.toExtensionName else name + def recur(selectors: List[untpd.ImportSelector]): Type = selectors match case selector :: rest => def checkUnambiguous(found: Type) = @@ -221,12 +224,12 @@ class Typer extends Namer refctx.error(em"reference to `$name` is ambiguous; it is imported twice", posd.sourcePos) found - if selector.rename == termName then + if adjustExtension(selector.rename) == termName then val memberName = if selector.name == termName then name else if name.isTypeName then selector.name.toTypeName else selector.name - checkUnambiguous(selection(imp, memberName, checkBounds = false)) + checkUnambiguous(selection(imp, adjustExtension(memberName), checkBounds = false)) else recur(rest) @@ -422,7 +425,7 @@ class Typer extends Namer * (2) Change imported symbols to selections. * (3) Change pattern Idents id (but not wildcards) to id @ _ */ - def typedIdent(tree: untpd.Ident, pt: Type)(using Context): Tree = { + def typedIdent(tree: untpd.Ident, pt: Type)(using Context): Tree = record("typedIdent") val name = tree.name def kind = if (name.isTermName) "" else "type " @@ -439,18 +442,24 @@ class Typer extends Namer if (name == nme.ROOTPKG) return tree.withType(defn.RootPackage.termRef) - /** Convert a reference `f` to an extension method in a collective extension - * on parameter `x` to `x.f` + /** Convert a reference `f` to an extension method select `p.f`, where + * `p` is the closest enclosing extension parameter, or else `this`. */ - def extensionMethodSelect(xmethod: Symbol): untpd.Tree = - val leadParamName = xmethod.info.paramNamess.head.head - def isLeadParam(sym: Symbol) = - sym.is(Param) && sym.owner.owner == xmethod.owner && sym.name == leadParamName - def leadParam(ctx: Context): Symbol = - ctx.scope.lookupAll(leadParamName).find(isLeadParam) match - case Some(param) => param - case None => leadParam(ctx.outersIterator.dropWhile(_.scope eq ctx.scope).next) - untpd.cpy.Select(tree)(untpd.ref(leadParam(ctx).termRef), name) + def extensionMethodSelect: untpd.Tree = + val xmethod = ctx.owner.enclosingExtensionMethod + val qualifier = + if xmethod.exists then // TODO: see whether we can use paramss for that + val leadParamName = xmethod.info.paramNamess.head.head + def isLeadParam(sym: Symbol) = + sym.is(Param) && sym.owner.owner == xmethod.owner && sym.name == leadParamName + def leadParam(ctx: Context): Symbol = + ctx.scope.lookupAll(leadParamName).find(isLeadParam) match + case Some(param) => param + case None => leadParam(ctx.outersIterator.dropWhile(_.scope eq ctx.scope).next) + untpd.ref(leadParam(ctx).termRef) + else + untpd.This(untpd.EmptyTypeIdent) + untpd.cpy.Select(tree)(qualifier, name) val rawType = { val saved1 = unimported @@ -474,53 +483,50 @@ class Typer extends Namer } } - val ownType = - if (rawType.exists) - ensureAccessible(rawType, superAccess = false, tree.sourcePos) - else if (name == nme._scope) - // gross hack to support current xml literals. - // awaiting a better implicits based solution for library-supported xml - return ref(defn.XMLTopScopeModule.termRef) - else if (name.toTermName == nme.ERROR) - UnspecifiedErrorType - else if (ctx.owner.isConstructor && !ctx.owner.isPrimaryConstructor && - ctx.owner.owner.unforcedDecls.lookup(tree.name).exists) + def setType(ownType: Type): Tree = + val tree1 = ownType match + case ownType: NamedType if !prefixIsElidable(ownType) => + ref(ownType).withSpan(tree.span) + case _ => + tree.withType(ownType) + val tree2 = toNotNullTermRef(tree1, pt) + checkStableIdentPattern(tree2, pt) + tree2 + + def fail: Tree = + if ctx.owner.isConstructor && !ctx.owner.isPrimaryConstructor + && ctx.owner.owner.unforcedDecls.lookup(tree.name).exists + then // we are in the arguments of a this(...) constructor call - errorType(ex"$tree is not accessible from constructor arguments", tree.sourcePos) + errorTree(tree, ex"$tree is not accessible from constructor arguments") else - errorType(MissingIdent(tree, kind, name), tree.sourcePos) - - val tree1 = ownType match { - case ownType: NamedType => - val sym = ownType.symbol - if sym.isAllOf(ExtensionMethod) - && sym.owner.isCollectiveExtensionClass - && ctx.owner.isContainedIn(sym.owner) - then typed(extensionMethodSelect(sym), pt) - else if prefixIsElidable(ownType) then tree.withType(ownType) - else ref(ownType).withSpan(tree.span) - case _ => - tree.withType(ownType) - } - - val tree2 = toNotNullTermRef(tree1, pt) - - checkStableIdentPattern(tree2, pt) - } + errorTree(tree, MissingIdent(tree, kind, name)) + + if rawType.exists then + setType(ensureAccessible(rawType, superAccess = false, tree.sourcePos)) + else if name == nme._scope then + // gross hack to support current xml literals. + // awaiting a better implicits based solution for library-supported xml + ref(defn.XMLTopScopeModule.termRef) + else if name.toTermName == nme.ERROR then + setType(UnspecifiedErrorType) + else if name.isTermName then + tryEither(typed(extensionMethodSelect, pt))((_, _) => fail) + else + fail + end typedIdent /** Check that a stable identifier pattern is indeed stable (SLS 8.1.5) */ - private def checkStableIdentPattern(tree: Tree, pt: Type)(using Context): tree.type = { - if (ctx.mode.is(Mode.Pattern) && - !tree.isType && - !pt.isInstanceOf[ApplyingProto] && - !tree.tpe.isStable && - !isWildcardArg(tree)) + private def checkStableIdentPattern(tree: Tree, pt: Type)(using Context): Unit = + if ctx.mode.is(Mode.Pattern) + && !tree.isType + && !pt.isInstanceOf[ApplyingProto] + && !tree.tpe.isStable + && !isWildcardArg(tree) + then ctx.error(StableIdentPattern(tree, pt), tree.sourcePos) - tree - } - def typedSelect(tree: untpd.Select, pt: Type, qual: Tree)(using Context): Tree = qual match { case qual @ IntegratedTypeArgs(app) => pt.revealIgnored match { @@ -533,9 +539,13 @@ class Typer extends Namer if (tree.name.isTypeName) checkStable(qual.tpe, qual.sourcePos, "type prefix") - if (select1.tpe ne TryDynamicCallType) ConstFold(checkStableIdentPattern(select1, pt)) - else if (pt.isInstanceOf[FunOrPolyProto] || pt == AssignProto) select1 - else typedDynamicSelect(tree, Nil, pt) + if select1.tpe ne TryDynamicCallType then + checkStableIdentPattern(select1, pt) + ConstFold(select1) + else if pt.isInstanceOf[FunOrPolyProto] || pt == AssignProto then + select1 + else + typedDynamicSelect(tree, Nil, pt) } def typedSelect(tree: untpd.Select, pt: Type)(using Context): Tree = { @@ -1922,6 +1932,7 @@ class Typer extends Namer } val ddef2 = assignType(cpy.DefDef(ddef)(name, tparams1, vparamss1, tpt1, rhs1), sym) + checkSignatureRepeatedParam(sym) ddef2.setDefTree //todo: make sure dependent method types do not depend on implicits or by-name params @@ -2550,11 +2561,14 @@ class Typer extends Namer traverse(rest) } case Thicket(stats) :: rest => - traverse(stats ++ rest) + traverse(stats ::: rest) case (stat: untpd.Export) :: rest => buf ++= stat.attachmentOrElse(ExportForwarders, Nil) // no attachment can happen in case of cyclic references traverse(rest) + case (stat: untpd.ExtMethods) :: rest => + val xtree = stat.removeAttachment(ExpandedTree).get + traverse(xtree :: rest) case stat :: rest => val stat1 = typed(stat)(using ctx.exprContext(stat, exprOwner)) checkStatementPurity(stat1)(stat, exprOwner) @@ -2984,7 +2998,9 @@ class Typer extends Namer arg.tpe match { case failed: AmbiguousImplicits => val pt1 = pt.deepenProto - if ((pt1 `ne` pt) && constrainResult(tree.symbol, wtp, pt1)) implicitArgs(formals, argIndex, pt1) + if (pt1 `ne` pt) && (pt1 ne sharpenedPt) + && constrainResult(tree.symbol, wtp, pt1) + then implicitArgs(formals, argIndex, pt1) else arg :: implicitArgs(formals1, argIndex + 1, pt1) case failed: SearchFailureType if !hasDefaultParams => // no need to search further, the adapt fails in any case @@ -3229,6 +3245,16 @@ class Typer extends Namer case _ => tp } + // If the expected type is a selection of an extension method, deepen it + // to also propagate the argument type (which is the receiver we have + // typechecked already). This is needed for i8311.scala. Doing so + // for all expected types does not work since it would block the case + // where we have an argument that must be converted with another + // implicit conversion to the receiver type. + def sharpenedPt = pt match + case pt: SelectionProto if pt.name.isExtensionName => pt.deepenProto + case _ => pt + def adaptNoArgs(wtp: Type): Tree = { val ptNorm = underlyingApplied(pt) def functionExpected = defn.isFunctionType(ptNorm) @@ -3242,7 +3268,7 @@ class Typer extends Namer case wtp: ExprType => readaptSimplified(tree.withType(wtp.resultType)) case wtp: MethodType if wtp.isImplicitMethod && - ({ resMatch = constrainResult(tree.symbol, wtp, pt); resMatch } || !functionExpected) => + ({ resMatch = constrainResult(tree.symbol, wtp, sharpenedPt); resMatch } || !functionExpected) => if (resMatch || ctx.mode.is(Mode.ImplicitsEnabled)) adaptNoArgsImplicitMethod(wtp) else @@ -3380,7 +3406,7 @@ class Typer extends Namer case SelectionProto(name, mbrType, _, _) => def tryExtension(using Context): Tree = try - findRef(name, WildcardType, ExtensionMethod, tree.posd) match { + findRef(name.toExtensionName, WildcardType, ExtensionMethod, tree.posd) match { case ref: TermRef => extMethodApply(untpd.ref(ref).withSpan(tree.span), tree, mbrType) case _ => EmptyTree diff --git a/compiler/test-resources/repl/i8716_i8717 b/compiler/test-resources/repl/i8716_i8717 deleted file mode 100644 index 36c4b9197c0b..000000000000 --- a/compiler/test-resources/repl/i8716_i8717 +++ /dev/null @@ -1,14 +0,0 @@ -scala>extension { } -1 |extension { } - |^ - |anonymous instance must implement a type or have at least one extension method -scala> extension as on -1 | extension as on - | ^ - | '(' expected, but eof found -scala> extension as on (s -1 | extension as on (s - | ^ - | ':' expected, but eof found -scala> extension as on (s: String) { def expand = ??? } -// defined object as diff --git a/docs/docs/contributing/debugging.md b/docs/docs/contributing/debugging.md index f50cee91b076..6d26501f284a 100644 --- a/docs/docs/contributing/debugging.md +++ b/docs/docs/contributing/debugging.md @@ -88,7 +88,7 @@ But you can also do: assertPositioned(tree.reporting(s"Tree is: $result")) ``` -`def (a: A).reporting(f: WrappedResult[T] ?=> String, p: Printer = Printers.default): A` is defined on all types. The function `f` can be written without the argument since it is a context function`. The `result` variable is a part of the `WrapperResult` – a tiny framework powering the `reporting` function. Basically, whenever you are using `reporting` on an object `A`, you can use the `result: A` variable from this function and it will be equal to the object you are calling `reporting` on. +`extension (a: A) def reporting(f: WrappedResult[T] ?=> String, p: Printer = Printers.default): A` is defined on all types. The function `f` can be written without the argument since it is a context function`. The `result` variable is a part of the `WrapperResult` – a tiny framework powering the `reporting` function. Basically, whenever you are using `reporting` on an object `A`, you can use the `result: A` variable from this function and it will be equal to the object you are calling `reporting` on. ## Printing out trees after phases To print out the trees you are compiling after the FrontEnd (scanner, parser, namer, typer) phases: diff --git a/docs/docs/internals/syntax.md b/docs/docs/internals/syntax.md index 8fef08a3474c..6d4c95b18513 100644 --- a/docs/docs/internals/syntax.md +++ b/docs/docs/internals/syntax.md @@ -248,6 +248,7 @@ Block ::= {BlockStat semi} [BlockResult] BlockStat ::= Import | {Annotation {nl}} [‘implicit’ | ‘lazy’] Def | {Annotation {nl}} {LocalModifier} TmplDef + | Extension | Expr1 | EndMarker @@ -392,7 +393,6 @@ TmplDef ::= ([‘case’] ‘class’ | [‘super’] ‘trait’) Cl | [‘case’] ‘object’ ObjectDef | ‘enum’ EnumDef | ‘given’ GivenDef - | ‘extension’ ExtensionDef ClassDef ::= id ClassConstr [Template] ClassDef(mods, name, tparams, templ) ClassConstr ::= [ClsTypeParamClause] [ConstrMods] ClsParamClauses with DefDef(_, , Nil, vparamss, EmptyTree, EmptyTree) as first stat ConstrMods ::= {Annotation} [AccessModifier] @@ -401,10 +401,10 @@ EnumDef ::= id ClassConstr InheritClauses EnumBody GivenDef ::= [GivenSig] Type ‘=’ Expr | [GivenSig] ConstrApps [TemplateBody] GivenSig ::= [id] [DefTypeParamClause] {UsingParamClause} ‘as’ -ExtensionDef ::= [id] [‘on’ ExtParamClause {UsingParamClause}] - TemplateBody -ExtMethods ::= [nl] ‘{’ ‘def’ DefDef {semi ‘def’ DefDef} ‘}’ -ExtParamClause ::= [DefTypeParamClause] ‘(’ DefParam ‘)’ +Extension ::= ‘extension’ [DefTypeParamClause] ‘(’ DefParam ‘)’ + {UsingParamClause}] ExtMethods +ExtMethods ::= ExtMethod | [nl] ‘{’ ExtMethod {semi ExtMethod ‘}’ +ExtMethod ::= {Annotation [nl]} {Modifier} ‘def’ DefDef Template ::= InheritClauses [TemplateBody] Template(constr, parents, self, stats) InheritClauses ::= [‘extends’ ConstrApps] [‘derives’ QualId {‘,’ QualId}] ConstrApps ::= ConstrApp {(‘,’ | ‘with’) ConstrApp} @@ -419,6 +419,7 @@ TemplateStat ::= Import | Export | {Annotation [nl]} {Modifier} Def | {Annotation [nl]} {Modifier} Dcl + | Extension | Expr1 | EndMarker | @@ -435,6 +436,7 @@ TopStatSeq ::= TopStat {semi TopStat} TopStat ::= Import | Export | {Annotation [nl]} {Modifier} Def + | Extension | Packaging | PackageObject | EndMarker diff --git a/docs/docs/reference/contextual/context-functions.md b/docs/docs/reference/contextual/context-functions.md index d98b034de4bb..21e2dcf39fa3 100644 --- a/docs/docs/reference/contextual/context-functions.md +++ b/docs/docs/reference/contextual/context-functions.md @@ -16,7 +16,7 @@ the same way methods with context parameters are applied. For instance: given ec as ExecutionContext = ... def f(x: Int): ExecutionContext ?=> Int = ... - + // could be written as follows with the type alias from above // def f(x: Int): Executable[Int] = ... @@ -125,7 +125,7 @@ object PostConditions { def result[T](using r: WrappedResult[T]): T = r - def [T] (x: T).ensuring(condition: WrappedResult[T] ?=> Boolean): T = { + extension [T](x: T) def ensuring(condition: WrappedResult[T] ?=> Boolean): T = { assert(condition(using x)) x } diff --git a/docs/docs/reference/contextual/derivation-macro.md b/docs/docs/reference/contextual/derivation-macro.md index e00a8a4d3bfd..7c327a338f16 100644 --- a/docs/docs/reference/contextual/derivation-macro.md +++ b/docs/docs/reference/contextual/derivation-macro.md @@ -120,7 +120,8 @@ Alternatively and what is shown below is that we can call the `eqv` method directly. The `eqGen` can trigger the derivation. ```scala -inline def [T](x: =>T) === (y: =>T)(using eq: Eq[T]): Boolean = eq.eqv(x, y) +extension [T](x: =>T) + inline def === (y: =>T)(using eq: Eq[T]): Boolean = eq.eqv(x, y) implicit inline def eqGen[T]: Eq[T] = ${ Eq.derived[T] } ``` @@ -216,7 +217,8 @@ object Eq { } object Macro3 { - inline def [T](x: =>T) === (y: =>T)(using eq: Eq[T]): Boolean = eq.eqv(x, y) + extension [T](x: =>T) + inline def === (y: =>T)(using eq: Eq[T]): Boolean = eq.eqv(x, y) implicit inline def eqGen[T]: Eq[T] = ${ Eq.derived[T] } } diff --git a/docs/docs/reference/contextual/extension-methods.md b/docs/docs/reference/contextual/extension-methods.md index 19a2a5d421da..dc44d708ed50 100644 --- a/docs/docs/reference/contextual/extension-methods.md +++ b/docs/docs/reference/contextual/extension-methods.md @@ -8,7 +8,8 @@ Extension methods allow one to add methods to a type after the type is defined. ```scala case class Circle(x: Double, y: Double, radius: Double) -def (c: Circle).circumference: Double = c.radius * math.Pi * 2 +extension (c: Circle) + def circumference: Double = c.radius * math.Pi * 2 ``` Like regular methods, extension methods can be invoked with infix `.`: @@ -20,249 +21,230 @@ circle.circumference ### Translation of Extension Methods -Extension methods are methods that have a parameter clause in front of the defined identifier. -They translate to functions where the leading parameter section is turned into the first argument list of the function. -So, the definition of `circumference` above translates to the following function, and can also be invoked as such: +Extension methods are methods that have a parameter clause in front of the defined identifier. +An extension method named `f` translates to method named `extension_f` that takes the leading parameter section as its first argument list. +So, the definition of `circumference` above translates to the following method, and can also be invoked as such: ```scala -def circumference(c: Circle): Double = c.radius * math.Pi * 2 +def extension_circumference(c: Circle): Double = c.radius * math.Pi * 2 -assert(circle.circumference == circumference(circle)) +assert(circle.circumference == extension_circumference(circle)) +``` +### Operators + +The extension method syntax can also be used to define operators. Examples: +```scala +extension (x: String) + def < (y: String): Boolean = ... +extension (x: Elem) + def +: (xs: Seq[Elem]): Seq[Elem] = ... +extension (x: Number) + @infix def min (y: Number): Number = ... + +"ab" < "c" +1 +: List(2, 3) +x min 3 +``` + +The three definitions above translate to +```scala +def extension_< (x: String)(y: String): Boolean = ... +def extension_+: (xs: Seq[Elem])(x: Elem): Seq[Elem] = ... +@infix def extension_min(x: Number)(y: Number): Number = ... +``` +Note the swap of the two parameters `x` and `xs` when translating +the right-associative operator `+:` to an extension method. This is analogous +to the implementation of right binding operators as normal methods. The Scala +compiler preprocesses an infix operation `x +: xs` to `xs.+:(x)`, so the extension +method ends up being applied to the sequence as first argument (in other words, +the two swaps cancel each other out). + +### Generic Extensions + + It is also possible to extend generic types by adding type parameters to an extension. For instance: + + ```scala + extension [T](xs: List[T]) + def second = xs.tail.head + + extension [T: Numeric](x: T) + def + (y: T): T = summon[Numeric[T]].plus(x, y) +``` + +If an extension method has type parameters, they come immediately after `extension` and are followed by the extended parameter. +When calling a generic extension method, any explicitly given type arguments follow the method name. So the `second` method could be instantiated as follows. +```scala + List(1, 2, 3).second[Int] +``` +Of course, the type argument here would usually be left out since it can be inferred. + + +Extensions can also take using clauses. For instance, the `+` extension above could equivalently be written with a using clause: +```scala + extension [T](x: T)(using n: Numeric[T]) + def - (y: T): T = n.minus(x, y) +``` + +**Note**: Type parameters have to be given after the `extension` keyword; +they cannot be given after the `def`. This restriction might be lifted in the future once we support multiple type parameter clauses in a method. By contrast, there can be using clauses in front as well as after the `def`. + +### Collective Extensions + +Sometimes, one wants to define several extension methods that share the same +left-hand parameter type. In this case one can "pull out" the common parameters into +a single extension and enclose all methods in braces or an indented region following a '`:`'. +Example: +```scala +extension (ss: Seq[String]) + + def longestStrings: Seq[String] = + val maxLength = ss.map(_.length).max + ss.filter(_.length == maxLength) + + def longestString: String = longestStrings.head +``` + +Note the right-hand side of `longestString`: it calls `longestStrings` directly, implicitly +assuming the common extended value `ss` as receiver. + +Collective extensions like these are a shorthand for individual extensions +where each method is defined separately. For instance, the first extension above expands to +```scala +extension (ss: Seq[String]) + def longestStrings: Seq[String] = + val maxLength = ss.map(_.length).max + ss.filter(_.length == maxLength) + +extension (ss: Seq[String]) + def longestString: String = ss.longestStrings.head +``` +Collective extensions also can take type parameters and have using clauses. Example +```scala +extension [T](xs: List[T])(using Ordering[T]) + def smallest(n: Int): List[T] = xs.sorted.take(n) + def smallestIndices(n: Int): List[Int] = + val limit = smallest(n).max + xs.zipWithIndex.collect { case (x, i) if x <= limit => i } ``` ### Translation of Calls to Extension Methods -When is an extension method applicable? There are two possibilities: +To convert a reference to an extension method, the compiler has to know about the extension +method. We say in this case that the extension method is _applicable_ at the point of reference. +There are four possible ways for an extension method to be applicable: - 1. An extension method is applicable if it is visible under a simple name, by being defined - or inherited or imported in a scope enclosing the application. - 2. An extension method is applicable if it is a member of some given instance at the point of the application. + 1. The extension method is visible under a simple name, by being defined or inherited or imported in a scope enclosing the reference. + 2. The extension method is a member of some given + instance that is visible at the point of the reference. + 3. The reference is of the form `r.m` and the extension method + is defined in the implicit scope of the type of `r`. + 4. The reference is of the form `r.m` and the extension method + is defined in some given instance in the implicit scope of the type of `r`. Here is an example for the first rule: ```scala -trait IntOps { - def (i: Int).isZero: Boolean = i == 0 +trait IntOps: + extension (i: Int) def isZero: Boolean = i == 0 - def (i: Int).safeMod(x: Int): Option[Int] = + extension (i: Int) def safeMod(x: Int): Option[Int] = // extension method defined in same scope IntOps if x.isZero then None else Some(i % x) -} -object IntOpsEx extends IntOps { - def (i: Int).safeDiv(x: Int): Option[Int] = +object IntOpsEx extends IntOps: + extension (i: Int) def safeDiv(x: Int): Option[Int] = // extension method brought into scope via inheritance from IntOps if x.isZero then None else Some(i / x) -} -trait SafeDiv { +trait SafeDiv: import IntOpsEx._ // brings safeDiv and safeMod into scope - def (i: Int) divide(d: Int) : Option[(Int, Int)] = + extension (i: Int) def divide(d: Int) : Option[(Int, Int)] = // extension methods imported and thus in scope - (i.safeDiv(d), i.safeMod(d)) match { + (i.safeDiv(d), i.safeMod(d)) match case (Some(d), Some(r)) => Some((d, r)) case _ => None - } -} ``` - -We build up on the above example to outline the second point. -We can make an extension method available by defining a given instance containing it, like this: +By the second rule, an extension method can be made available by defining a given instance containing it, like this: ```scala given ops1 as IntOps // brings safeMod into scope 1.safeMod(2) ``` -Then `safeMod` is legal everywhere `ops1` is available. Anonymous givens (and any other form of givens) are supported as well: +By the third and fourth rule, an extension method is available if it is in the implicit scope of the receiver type or in a given instance in that scope. Example: ```scala -given SafeDiv //brings divide into scope (safeMod and safeDiv are not automatically exported) - -1.divide(2) -``` +class List[T]: + ... +object List: -The precise rules for resolving a selection to an extension method are as follows. - -Assume a selection `e.m[Ts]` where `m` is not a member of `e`, where the type arguments `[Ts]` are optional, -and where `T` is the expected type. The following two rewritings are tried in order: + extension [T](xs: List[List[T]]) + def flatten: List[T] = xs.foldLeft(Nil: List[T])(_ ++ _) - 1. The selection is rewritten to `m[Ts](e)`. - 2. If the first rewriting does not typecheck with expected type `T`, and there is a given instance `g` - in either the current scope or in the context scope of `T`, and `g` defines an extension - method named `m`, then selection is expanded to `g.m[Ts](e)`. - This second rewriting is attempted at the time where the compiler also tries an implicit conversion - from `T` to a type containing `m`. If there is more than one way of rewriting, an ambiguity error results. + given [T: Ordering] as Ordering[List[T]]: + extension (xs: List[T]) + def < (ys: List[T]): Boolean = ... +end List -So `circle.circumference` translates to `CircleOps.circumference(circle)`, provided -`circle` has type `Circle` and `CircleOps` is given (i.e. it is visible at the point of call or it is defined in the companion object of `Circle`). +// extension method available since it is in the implicit scope of List[List[Int]] +List(List(1, 2), List(3, 4)).flatten -### Operators - -The extension method syntax also applies to the definition of operators. -This case is indicated by omitting the period between the leading parameter list and the operator. In each case the definition syntax mirrors the way the operator is applied. -Examples: -```scala -def (x: String) < (y: String) = ... -def (x: Elem) +: (xs: Seq[Elem]) = ... -def (x: Number) min (y: Number) = ... - -"ab" < "c" -1 +: List(2, 3) -x min 3 +// extension method available since it is in the given Ordering[List[T]], +// which is itself in the implicit scope of List[Int] +List(1, 2) < List(3) ``` -For alphanumeric extension operators like `min` an `@infix` annotation is implied. - - -The three definitions above translate to -```scala -def < (x: String)(y: String) = ... -def +: (xs: Seq[Elem])(x: Elem) = ... -def min(x: Number)(y: Number) = ... -``` -Note the swap of the two parameters `x` and `xs` when translating -the right-associative operator `+:` to an extension method. This is analogous -to the implementation of right binding operators as normal methods. +The precise rules for resolving a selection to an extension method are as follows. +Assume a selection `e.m[Ts]` where `m` is not a member of `e`, where the type arguments `[Ts]` are optional, and where `T` is the expected type. The following two rewritings are tried in order: -### Generic Extensions + 1. The selection is rewritten to `extension_m[Ts](e)`. + 2. If the first rewriting does not typecheck with expected type `T`, + and there is an extension method `m` in some eligible object `o`, the selection is rewritten to `o.extension_m[Ts](e)`. An object `o` is _eligible_ if -The `IntOps` examples extended a non generic type. -It is also possible to extend a specific instance of a generic type (e.g. Seq[String] -- see `stringOps` further below). -Moreover, it is also possible to extend generic types by adding type parameters to an extension method. Examples: + - `o` forms part of the implicit scope of `T`, or + - `o` is a given instance that is visible at the point of the application, or + - `o` is a given instance in the implicit scope of `T`. -```scala -def [T](xs: List[T]) second = - xs.tail.head + This second rewriting is attempted at the time where the compiler also tries an implicit conversion + from `T` to a type containing `m`. If there is more than one way of rewriting, an ambiguity error results. -def [T](xs: List[List[T]]) flattened = - xs.foldLeft[List[T]](Nil)(_ ++ _) +An extension method can also be used as an identifier by itself. If an identifier `m` does not +resolve, the identifier is rewritten to: -def [T: Numeric](x: T) + (y: T): T = - summon[Numeric[T]].plus(x, y) -``` + - `x.m` if the identifier appears in an extension with parameter `x` + - `this.m` otherwise -If an extension method has type parameters, they come immediately after the `def` and are followed by the extended parameter. -When calling a generic extension method, any explicitly given type arguments follow the method name. So the `second` method can be instantiated as follows: +and the rewritten term is again tried as an application of an extension method. Example: ```scala -List(1, 2, 3).second[Int] + extension (s: String) + def position(ch: Char, n: Int): Int = + if n < s.length && s(n) != ch then position(ch, n + 1) + else n ``` -(it's only a showcase, the compiler could of course infer the type). - -### Extension Instances - -It is quite common to wrap one or more extension methods in a given instance, -in order to make them available as methods without needing to be imported explicitly. -This pattern is supported by a special `extension` syntax. Example: +The recursive call `position(ch, n + 1)` expands to `s.position(ch, n + 1)` in this case. The whole extension method rewrites to ```scala -extension ops { - def (xs: Seq[String]).longestStrings: Seq[String] = { - val maxLength = xs.map(_.length).max - xs.filter(_.length == maxLength) - } - def (xs: Seq[String]).longestString: String = xs.longestStrings.head - def [T](xs: List[T]).second: T = xs.tail.head -} +def extension_position(s: String)(ch: Char, n: Int): Int = + if n < s.length && s(n) != ch then extension_position(s)(ch, n + 1) + else n ``` -An extension instance can only contain extension methods. Other definitions are not allowed. The name `ops` -of the extension is optional. It can be left out: -```scala -extension { - def (xs: Seq[String]).longestStrings: Seq[String] = ... - def [T](xs: List[T]).second: T = ... -} -``` -If the name of an extension is not explicitly given, it is synthesized from the name and type of the first implemented extension method. +### More Details -Extension instances map directly to given instances. The `ops` extension above -would expand to -```scala -given ops as AnyRef { - def (xs: Seq[String]).longestStrings: Seq[String] = ... - def (xs: Seq[String]).longestString: String = ... - def [T](xs: List[T]).second: T = ... -} -``` -The type "implemented" by this given instance is `AnyRef`, which -is not a type one can summon by itself. This means that the instance can -only be used for its extension methods. +1. To avoid confusion, names of normal methods are not allowed to start with `extension_`. -### Collective Extensions - -Sometimes, one wants to define several extension methods that share the same -left-hand parameter type. In this case one can "pull out" the common parameters into the extension instance itself. Examples: +2. A named import such as `import a.m` of an extension method in `a` will make `m` +only available as an extension method. To access it under +`extension_m` that name as to be imported separately. Example: ```scala -extension stringOps on (ss: Seq[String]) { - def longestStrings: Seq[String] = { - val maxLength = ss.map(_.length).max - ss.filter(_.length == maxLength) - } - def longestString: String = longestStrings.head -} - -extension listOps on [T](xs: List[T]) { - def second: T = xs.tail.head - def third: T = xs.tail.second -} +object DoubleOps: + extension (x: Double) def ** (exponent: Int): Double = + require(exponent > 0) + if exponent == 0 then 1 else x * (x ** (exponent - 1)) -extension on [T](xs: List[T])(using Ordering[T]) { - def largest(n: Int) = xs.sorted.takeRight(n) -} -``` -**Note**: If a collective extension defines type parameters in its prefix -(as the `listOps` extension above does), the extension methods themselves are not -allowed to have additional type parameters. This restriction might be lifted in the -future once we support multiple type parameter clauses in a method. - -Collective extensions like these are a shorthand for extension instances where -the parameters following the `on` are repeated for each implemented method. -For instance, the collective extensions above expand to the following extension instances: -```scala -extension stringOps { - def (ss: Seq[String]).longestStrings: Seq[String] = { - val maxLength = ss.map(_.length).max - ss.filter(_.length == maxLength) - } - def (ss: Seq[String]).longestString: String = - ss.longestStrings.head -} -extension listOps { - def [T](xs: List[T]).second: T = xs.tail.head - def [T](xs: List[T]).third: T = xs.tail.second -} -extension { - def [T](xs: List[T]).largest(using Ordering[T])(n: Int) = - xs.sorted.takeRight(n) -} -``` -One special tweak is shown in the `longestString` method of the `stringOps` extension. It's original definition was -```scala -def longestString: String = longestStrings.head -``` -This uses `longestStrings` as an implicit extension method call on the joint -parameter `ss`. The usage is made explicit when translating the method: -```scala -def (ss: Seq[String]).longestString: String = - ss.longestStrings.head -``` -By contrast, the meaning of `this` in a collective extension is as usual -a reference to the enclosing object (i.e. the one implementing the extension methods). -It's not a reference to the shared parameter. So this means that the following -implementation of `longestString` would be illegal: -```scala -def longestString: String = this.longestStrings.head // error: missing parameter -``` -But the following version would again be correct, since it calls the `longestString` -method as a regular non-extension method, passing the prefix parameter `ss` as a regular parameter: -```scala -def longestString: String = this.longestStrings(ss).head +import DoubleOps.{**, extension_**} +assert(2.0 ** 3 == extension_**(2.0)(3)) ``` ### Syntax @@ -270,22 +252,14 @@ def longestString: String = this.longestStrings(ss).head Here are the syntax changes for extension methods and collective extensions relative to the [current syntax](../../internals/syntax.md). ``` -DefSig ::= ... - | ExtParamClause [nl] [‘.’] id DefParamClauses -ExtParamClause ::= [DefTypeParamClause] ‘(’ DefParam ‘)’ -TmplDef ::= ... - | ‘extension’ ExtensionDef -ExtensionDef ::= [id] [‘on’ ExtParamClause {GivenParamClause}] TemplateBody -``` -The template body of an extension must consist only of extension method definitions for a regular -extension instance, and only of normal method definitions for a collective extension instance. -It must not be empty. - -`extension` and `on` are soft keywords, recognized only when they appear at the start of a -statement in one of the patterns -```scala -extension on ... -extension on ... -extension { ... -extension { ... +BlockStat ::= ... | Extension +TemplateStat ::= ... | Extension +TopStat ::= ... | Extension +Extension ::= ‘extension’ [DefTypeParamClause] ‘(’ DefParam ‘)’ + {UsingParamClause} ExtMethods +ExtMethods ::= ExtMethod | [nl] ‘{’ ExtMethod {semi ExtMethod ‘}’ +ExtMethod ::= {Annotation [nl]} {Modifier} ‘def’ DefDef ``` +`extension` is a soft keyword. It is recognized as a keyword only if it appears +at the start of a statement and is followed by `[` or `(`. In all other cases +it is treated as an identifier. diff --git a/docs/docs/reference/contextual/givens.md b/docs/docs/reference/contextual/givens.md index f13fee94e5fc..e4b72543545c 100644 --- a/docs/docs/reference/contextual/givens.md +++ b/docs/docs/reference/contextual/givens.md @@ -9,8 +9,8 @@ that serve for synthesizing arguments to [context parameters](./using-clauses.ht ```scala trait Ord[T] { def compare(x: T, y: T): Int - def (x: T) < (y: T) = compare(x, y) < 0 - def (x: T) > (y: T) = compare(x, y) > 0 + extension (x: T) def < (y: T) = compare(x, y) < 0 + extension (x: T) def > (y: T) = compare(x, y) > 0 } given intOrd as Ord[Int] { diff --git a/docs/docs/reference/contextual/relationship-implicits.md b/docs/docs/reference/contextual/relationship-implicits.md index 12c9ee3eb672..7b22a84ad8ef 100644 --- a/docs/docs/reference/contextual/relationship-implicits.md +++ b/docs/docs/reference/contextual/relationship-implicits.md @@ -61,23 +61,6 @@ The synthesized type names are formed from Tuples are treated as transparent, i.e. a type `F[(X, Y)]` would get the synthesized name `F_X_Y`. Directly implemented function types `A => B` are represented as `A_to_B`. Function types used as arguments to other type constructors are represented as `Function`. -### Anonymous Collective Extensions - -Anonymous collective extensions also get compiler synthesized names, which are formed from - - - the prefix `extension_` - - the name of the first defined extension method - - the simple name of the first parameter type of this extension method - - the simple name(s) of the toplevel argument type constructors to this type. - -For example, the extension -```scala -extension on [T] (xs: List[T]) { - def second = ... -} -``` -gets the synthesized name `extension_second_List_T`. - ### Given Clauses Given clauses correspond largely to Scala-2's implicit parameter clauses. E.g. @@ -111,7 +94,7 @@ will map to with clauses instead. Extension methods have no direct counterpart in Scala 2, but they can be simulated with implicit classes. For instance, the extension method ```scala -def (c: Circle).circumference: Double = c.radius * math.Pi * 2 +extension (c: Circle) def circumference: Double = c.radius * math.Pi * 2 ``` could be simulated to some degree by ```scala diff --git a/docs/docs/reference/contextual/type-classes.md b/docs/docs/reference/contextual/type-classes.md index f64213788a0a..e8489d393fe5 100644 --- a/docs/docs/reference/contextual/type-classes.md +++ b/docs/docs/reference/contextual/type-classes.md @@ -5,110 +5,84 @@ title: "Implementing Type classes" A _type class_ is an abstract, parameterized type that lets you add new behavior to any closed data type without using sub-typing. This can be useful in multiple use-cases, for example: * expressing how a type you don't own (from the standard or 3rd-party library) conforms to such behavior -* expressing such a behavior for multiple types without involving sub-typing relationships (one `extends` another) between those types (see: [ad hoc polymorphism](https://en.wikipedia.org/wiki/Ad_hoc_polymorphism) for instance) +* expressing such a behavior for multiple types without involving sub-typing relationships (one `extends` another) between those types (see: [ad hoc polymorphism](https://en.wikipedia.org/wiki/Ad_hoc_polymorphism) for instance) -Therefore in Scala 3, _type classes_ are just _traits_ with one or more parameters whose implementations are not defined through the `extends` keyword, but by **given instances**. -Here are some examples of usual type classes: +Therefore in Scala 3, _type classes_ are just _traits_ with one or more parameters whose implementations are not defined through the `extends` keyword, but by **given instances**. +Here are some examples of common type classes: ### Semigroups and monoids: Here's the `Monoid` type class definition: ```scala -trait SemiGroup[T] { - def (x: T) combine (y: T): T -} +trait SemiGroup[T]: + extension (x: T) def combine (y: T): T -trait Monoid[T] extends SemiGroup[T] { +trait Monoid[T] extends SemiGroup[T]: def unit: T -} ``` -An implementation of this `Monoid` type class for the type `String` can be the following: +An implementation of this `Monoid` type class for the type `String` can be the following: ```scala -given Monoid[String] { - def (x: String) combine (y: String): String = x.concat(y) +given Monoid[String]: + extension (x: String) def combine (y: String): String = x.concat(y) def unit: String = "" -} ``` Whereas for the type `Int` one could write the following: ```scala -given Monoid[Int] { - def (x: Int) combine (y: Int): Int = x + y +given Monoid[Int]: + extension (x: Int) def combine (y: Int): Int = x + y def unit: Int = 0 -} ``` This monoid can now be used as _context bound_ in the following `combineAll` method: ```scala def combineAll[T: Monoid](xs: List[T]): T = - xs.foldLeft(summon[Monoid[T]].unit)(_ combine _) + xs.foldLeft(summon[Monoid[T]].unit)(_.combine(_)) ``` To get rid of the `summon[...]` we can define a `Monoid` object as follows: ```scala -object Monoid { +object Monoid: def apply[T](using m: Monoid[T]) = m -} ``` -Which would allow to re-write the `combineAll` method this way: +Which would allow to re-write the `combineAll` method this way: ```scala def combineAll[T: Monoid](xs: List[T]): T = - xs.foldLeft(Monoid[T].unit)(_ combine _) -``` - -We can also benefit from [extension methods](extension-methods.html) to make this `combineAll` function accessible as a method on the `List` type: - - -```scala -def [T: Monoid](xs: List[T]).combineAll: T = - xs.foldLeft(Monoid[T].unit)(_ combine _) -``` - -Which allows one to write: - -```scala -assert("ab" == List("a", "b").combineAll) -``` -or: -```scala -assert(3 == List(1, 2).combineAll) + xs.foldLeft(Monoid[T].unit)(_.combine(_)) ``` ### Functors: A `Functor` for a type provides the ability for its values to be "mapped over", i.e. apply a function that transforms inside a value while remembering its shape. For example, to modify every element of a collection without dropping or adding elements. We can represent all types that can be "mapped over" with `F`. It's a type constructor: the type of its values becomes concrete when provided a type argument. -Therefore we write it `F[?]`, hinting that it is a type with internal details we can inspect. +Therefore we write it `F[_]`, hinting that the type `F` takes another type as argument. The definition of a generic `Functor` would thus be written as: ```scala -trait Functor[F[?]] { - def map[A, B](original: F[A], mapper: A => B): F[B] -} +trait Functor[F[_]]: + def map[A, B](x: F[A], f: A => B): F[B] ``` - -Which could read as follows: "A `Functor` for the type constructor `F[?]` represents the ability to transform `F[A]` to `F[B]` through the application of the `mapper` function whose type is `A => B`". We call the `Functor` definition here a _type class_. -This way, we could define an instance of `Functor` for the `List` type: +Which could read as follows: "A `Functor` for the type constructor `F[_]` represents the ability to transform `F[A]` to `F[B]` through the application of function `f` with type `A => B`". We call the `Functor` definition here a _type class_. +This way, we could define an instance of `Functor` for the `List` type: ```scala -given Functor[List] { - def map[A, B](original: List[A], mapper: A => B): List[B] = - original.map(mapper) // List already has a `map` method -} +given Functor[List]: + def map[A, B](x: List[A], f: A => B): List[B] = + x.map(f) // List already has a `map` method ``` With this `given` instance in scope, everywhere a `Functor` is expected, the compiler will accept a `List` to be used. For instance, we may write such a testing method: ```scala -def assertTransformation[F[?]: Functor, A, B](expected: F[B], original: F[A], mapping: A => B): Unit = +def assertTransformation[F[_]: Functor, A, B](expected: F[B], original: F[A], mapping: A => B): Unit = assert(expected == summon[Functor[F]].map(original, mapping)) ``` @@ -119,124 +93,135 @@ assertTransformation(List("a1", "b1"), List("a", "b"), elt => s"${elt}1") ``` That's a first step, but in practice we probably would like the `map` function to be a method directly accessible on the type `F`. So that we can call `map` directly on instances of `F`, and get rid of the `summon[Functor[F]]` part. -As in the previous example of Monoids, [`extension` methods](extension-methods.html) help achieving that. Let's re-define the `Functor` _type class_ with extension methods. +As in the previous example of Monoids, [`extension` methods](extension-methods.html) help achieving that. Let's re-define the `Functor` type class with extension methods. ```scala -trait Functor[F[?]] { - def [A, B](original: F[A]).map(mapper: A => B): F[B] -} +trait Functor[F[_]]: + extension [A, B](x: F[A]) + def map(f: A => B): F[B] ``` The instance of `Functor` for `List` now becomes: ```scala -given Functor[List] { - def [A, B](original: List[A]).map(mapper: A => B): List[B] = - original.map(mapper) // List already has a `map` method -} +given Functor[List]: + extension [A, B](xs: List[A]) + def map(f: A => B): List[B] = + xs.map(f) // List already has a `map` method + ``` It simplifies the `assertTransformation` method: ```scala -def assertTransformation[F[?]: Functor, A, B](expected: F[B], original: F[A], mapping: A => B): Unit = +def assertTransformation[F[_]: Functor, A, B](expected: F[B], original: F[A], mapping: A => B): Unit = assert(expected == original.map(mapping)) ``` - -The `map` method is now directly used on `original` since it is of type `F[A]` (where `F` is a `Functor`). +The `map` method is now directly used on `original`. It is available as an extension method +since `original`'s type is `F[A]` and a given instance for `Functor[F[A]]` which defines `map` +is in scope. ### Monads -Now we have a `Functor` for `List`. +Applying `map` in `Functor[List]` to a mapping function of type `A => B` results in a `List[B]`. So applying it to a mapping function of type `A => List[B]` results in a `List[List[B]]`. To avoid managing lists of lists, we may want to "flatten" the values in a single list. -Applying the `List.map` ability with the following mapping function as parameter: `mapping: A => B` would result in a `List[B]`. +That's where `Monad` comes in. A `Monad` for type `F[_]` is a `Functor[F]` with two more operations: +* `flatMap`, which turns an `F[A]` into an `F[B]` when given a function of type +`A => F[B]`, +* `pure`, which creates an `F[A]` from a single value `A`. -Now, applying the `List.map` ability with the following mapping function as parameter: `mapping: A => List[B]` would result in a `List[List[B]]`. +Here is the translation of this definition in Scala 3: -To avoid managing lists of lists, we may want to "flatten" the values in a single list. +```scala +trait Monad[F[_]] extends Functor[F]: -That's where `Monad` enters the party. A `Monad` for type `F[?]` is a `Functor[F]` with 2 more abilities: -* the flatten ability we just described: turning `F[A]` to `F[B]` when given a `mapping: A => F[B]` function -* the ability to create `F[A]` from a single value `A` + /** The unit value for a monad */ + def pure[A](x: A): F[A] -Here is the translation of this definition in Scala 3: + extension [A, B](x: F[A]) + /** The fundamental composition operation */ + def flatMap(f: A => F[B]): F[B] -```scala -trait Monad[F[?]] extends Functor[F] { // "A `Monad` for type `F[?]` is a `Functor[F]`" => thus has the `map` ability - def pure[A](x: A): F[A] // `pure` can construct F[A] from a single value A - def [A, B](x: F[A]).flatMap(f: A => F[B]): F[B] // the flattening ability is named `flatMap`, using extension methods as previous examples - def [A, B](x: F[A]).map(f: A => B) = x.flatMap(f `andThen` pure) // the `map(f)` ability is simply a combination of applying `f` then turning the result into an `F[A]` then applying `flatMap` to it -} + /** The `map` operation can now be defined in terms of `flatMap` */ + def map(f: A => B) = x.flatMap(f.andThen(pure)) + +end Monad ``` #### List -Let us declare the `Monad` ability for type `List` +A `List` can be turned into a monad via this `given` instance: ```scala -given listMonad as Monad[List] { +given listMonad as Monad[List]: def pure[A](x: A): List[A] = List(x) - def [A, B](xs: List[A]).flatMap(f: A => List[B]): List[B] = - xs.flatMap(f) // let's rely on the existing `flatMap` method of `List` -} + extension [A, B](xs: List[A]) + def flatMap(f: A => List[B]): List[B] = + xs.flatMap(f) // rely on the existing `flatMap` method of `List` ``` - -`map` implementation is no longer needed. +Since `Monad` is a subtype of `Functor`, `List` is also a functor. The Functor's `map` +operation is already provided by the `Monad` trait, so the instance does not need to define +it explicitly. #### Option `Option` is an other type having the same kind of behaviour: -* the `map` ability turning `Option[A]` into `Option[B]` if passed a function `f: A => B` -* the `flatMap` ability turning `Option[A]` into `Option[B]` if passed a function `f: A => Option[B]` -* the `pure` ability turning `A` into `Option[A]` ```scala -given optionMonad as Monad[Option] { +given optionMonad as Monad[Option]: def pure[A](x: A): Option[A] = Option(x) - def [A, B](xs: Option[A]).flatMap(f: A => Option[B]): Option[B] = - xs.flatMap(f) // let's rely on the existing `flatMap` method of `Option` -} + extension [A, B](xo: Option[A]) + def flatMap(f: A => Option[B]): Option[B] = xo match + case Some(x) => f(x) + case None => None ``` -#### The Reader Monad +#### Reader -Another example of a `Monad` is the Reader Monad. It no longer acts on a type like `List` or `Option`, but on a function. -It can be used for example for combining functions that all need the same type of parameter. For instance multiple functions needing access to some configuration, context, environment variables, etc. +Another example of a `Monad` is the _Reader_ Monad, which acts on functions instead of +data types like `List` or `Option`. It can be used to combine multiple functions +that all need the same parameter. For instance multiple functions needing access to some configuration, context, environment variables, etc. -Let us have a `Config` type, and two functions using it: +Let's define a `Config` type, and two functions using it: ```scala trait Config // ... def compute(i: Int)(config: Config): String = ??? -def show(str: String)(config: Config): Unit = ??? +def layout(str: String)(config: Config): Unit = ??? ``` -We may want to combine `compute` and `show` into a single function, accepting a `Config` as parameter, and showing the result of the computation. -If we had a `flatMap` function as in the examples above, we would be able to write the following: - +We may want to combine `compute` and `show` into a single function, accepting a `Config` as parameter, and showing the result of the computation, and we'd like to use +a monad to avoid passing the parameter explicitly multiple times. So postulating +the right `flatMap` operation, we could write: ```scala def computeAndShow(i: Int): Config => Unit = compute(i).flatMap(show) ``` - -Let's define this `Monad` then. First, we are going to define a type named `ConfigDependent` representing a function that when passed a `Config` produces a `Result`. +instead of +```scala +show(compute(i)(config))(config) +``` +Let's define this m then. First, we are going to define a type named `ConfigDependent` representing a function that when passed a `Config` produces a `Result`. ```scala -trait Config // the Config defined above type ConfigDependent[Result] = Config => Result ``` -The monad will look like this: +The monad instance will look like this: ```scala -given configDependentMonad as Monad[ConfigDependent] { - def [A, B](r: ConfigDependent[A]).flatMap(f: A => ConfigDependent[B]): ConfigDependent[B] = - config => f(r(config))(config) +given configDependentMonad as Monad[ConfigDependent]: + def pure[A](x: A): ConfigDependent[A] = config => x -} + + extension [A, B](x: ConfigDependent[A]) + def flatMap(f: A => ConfigDependent[B]): ConfigDependent[B] = + config => f(x(config))(config) + +end configDependentMonad ``` The type `ConfigDependent` can be written using [type lambdas](../new-types/type-lambdas.html): @@ -245,34 +230,43 @@ The type `ConfigDependent` can be written using [type lambdas](../new-types/type type ConfigDependent = [Result] =>> Config => Result ``` -Using this syntax would turn the previous `configReaderMonad` into: +Using this syntax would turn the previous `configDependentMonad` into: ```scala given configDependentMonad as Monad[[Result] =>> Config => Result] - def [A, B](r: Config => A).flatMap(f: A => Config => B): Config => B = - config => f(r(config))(config) + def pure[A](x: A): Config => A = config => x -``` + extension [A, B](x: Config => A) + def flatMap(f: A => Config => B): Config => B = + config => f(x(config))(config) +end configDependentMonad +``` It is likely that we would like to use this pattern with other kinds of environments than our `Config` trait. The Reader monad allows us to abstract away `Config` as a type _parameter_, named `Ctx` in the following definition: ```scala -given readerMonad[Ctx] as Monad[[X] =>> Ctx => X] { - def [A, B](r: Ctx => A).flatMap(f: A => Ctx => B): Ctx => B = - ctx => f(r(ctx))(ctx) +given readerMonad[Ctx] as Monad[[X] =>> Ctx => X]: + def pure[A](x: A): Ctx => A = ctx => x -} + + extension [A, B](x: Ctx => A) + def flatMap(f: A => Ctx => B): Ctx => B = + ctx => f(x(ctx))(ctx) + +end readerMonad ``` ### Summary -The definition of a _type class_ is expressed via a parameterised type with abstract members, such as a `trait`. -The main difference between object oriented polymorphism, and ad-hoc polymorphism with _type classes_, is how the definition of the _type class_ is implemented, in relation to the type it acts upon. -In the case of a _type class_, its implementation for a concrete type is expressed through a `given` term definition, which is supplied as an implicit argument alongside the value it acts upon. With object oriented polymorphism, the implementation is mixed into the parents of a class, and only a single term is required to perform a polymorphic operation. +The definition of a _type class_ is expressed with a parameterised type with abstract members, such as a `trait`. +The main difference between subtype polymorphism and ad-hoc polymorphism with _type classes_ is how the definition of the _type class_ is implemented, in relation to the type it acts upon. +In the case of a _type class_, its implementation for a concrete type is expressed through a `given` instance definition, which is supplied as an implicit argument alongside the value it acts upon. With subtype polymorphism, the implementation is mixed into the parents of a class, and only a single term is required to perform a polymorphic operation. The type class solution +takes more effort to set up, but is more extensible: Adding a new interface to a class +class requires changing the source code of that class. But contrast, instances for type classes can be defined anywhere. -To conclude, in addition to given instances, other constructs like extension methods, context bounds and type lambdas allow a concise and natural expression of _type classes_. +To conclude, we have seen that traits and given instances, combined with other constructs like extension methods, context bounds and type lambdas allow a concise and natural expression of _type classes_. diff --git a/docs/docs/reference/dropped-features/package-objects.md b/docs/docs/reference/dropped-features/package-objects.md index fc8a5fccbded..beb304a48b16 100644 --- a/docs/docs/reference/dropped-features/package-objects.md +++ b/docs/docs/reference/dropped-features/package-objects.md @@ -22,7 +22,7 @@ def b = a._2 case class C() implicit object Cops { - def (x: C).pair(y: C) = (x, y) + extension (x: C) def pair(y: C) = (x, y) } ``` There may be several source files in a package containing such toplevel definitions, and source files can freely mix toplevel value, method, and type definitions with classes and objects. diff --git a/docs/docs/reference/metaprogramming/macros.md b/docs/docs/reference/metaprogramming/macros.md index 8a918bf70a48..1a8d510ca595 100644 --- a/docs/docs/reference/metaprogramming/macros.md +++ b/docs/docs/reference/metaprogramming/macros.md @@ -717,7 +717,7 @@ This might be used to then perform an implicit search as in: ```scala -inline def (inline sc: StringContext).showMe(inline args: Any*): String = ${ showMeExpr('sc, 'args) } +extension (inline sc: StringContext) inline def showMe(inline args: Any*): String = ${ showMeExpr('sc, 'args) } private def showMeExpr(sc: Expr[StringContext], argsExpr: Expr[Seq[Any]])(using QuoteContext): Expr[String] = { argsExpr match { diff --git a/docs/docs/reference/other-new-features/indentation.md b/docs/docs/reference/other-new-features/indentation.md index f97163ac1296..7b8d0c712d6d 100644 --- a/docs/docs/reference/other-new-features/indentation.md +++ b/docs/docs/reference/other-new-features/indentation.md @@ -59,6 +59,7 @@ There are two rules: An indentation region can start - after the condition of an `if-else`, or + - after the leading parameters of an `extension`, or - after a ": at end of line" token (see below) - after one of the following tokens: ``` @@ -94,7 +95,7 @@ at the toplevel, inside braces `{...}`, but not inside parentheses `(...)`, patt ### Optional Braces Around Template Bodies -The Scala grammar uses the term _template body_ for the definitions of a class, trait, object, given instance or extension that are normally enclosed in braces. The braces around a template body can also be omitted by means of the following rule: +The Scala grammar uses the term _template body_ for the definitions of a class, trait, object or given instance that are normally enclosed in braces. The braces around a template body can also be omitted by means of the following rule If at the point where a template body can start there is a `:` that occurs at the end of a line, and that is followed by at least one indented statement, the recognized @@ -120,10 +121,10 @@ enum Color: type T = A: def f: Int -given [T] with Ord[T] as Ord[List[T]]: +given [T](using Ord[T]) as Ord[List[T]]: def compare(x: List[T], y: List[T]) = ??? -extension on (xs: List[Int]): +extension (xs: List[Int]) def second: Int = xs.tail.head new A: @@ -256,7 +257,7 @@ For instance, the following end markers are all legal: end given end C - extension on (x: C): + extension (x: C) def ff: String = x.f ++ x.f end extension diff --git a/docs/docs/reference/other-new-features/opaques.md b/docs/docs/reference/other-new-features/opaques.md index 69ab27ca367f..c5840fb34e15 100644 --- a/docs/docs/reference/other-new-features/opaques.md +++ b/docs/docs/reference/other-new-features/opaques.md @@ -21,7 +21,7 @@ object Logarithms { } // Extension methods define opaque types' public APIs - extension logarithmOps on (x: Logarithm) { + extension (x: Logarithm) { def toDouble: Double = math.exp(x) def + (y: Logarithm): Logarithm = Logarithm(math.exp(x) + math.exp(y)) def * (y: Logarithm): Logarithm = x + y @@ -29,15 +29,14 @@ object Logarithms { } ``` -This introduces `Logarithm` as a new abstract type, which is implemented as `Double`. +This introduces `Logarithm` as a new abstract type, which is implemented as `Double`. The fact that `Logarithm` is the same as `Double` is only known in the scope where `Logarithm` is defined which in the above example corresponds to the object `Logarithms`. -Or in other words, within the scope it is treated as type alias, but this is opaque to the outside world +Or in other words, within the scope it is treated as type alias, but this is opaque to the outside world where in consequence `Logarithm` is seen as an abstract type and has nothing to do with `Double`. -The public API of `Logarithm` consists of the `apply` and `safe` methods defined in the companion object. -They convert from `Double`s to `Logarithm` values. Moreover, a collective extension `logarithmOps` provides the extension methods `toDouble` that converts the other way, -and operations `+` and `*` on `Logarithm` values. +The public API of `Logarithm` consists of the `apply` and `safe` methods defined in the companion object. +They convert from `Double`s to `Logarithm` values. Moreover, an operation `toDouble` that converts the other way, and operations `+` and `*` are defined as extension methods on `Logarithm` values. The following operations would be valid because they use functionality implemented in the `Logarithms` object. ```scala @@ -68,10 +67,10 @@ object Access { opaque type PermissionChoice = Int opaque type Permission <: Permissions & PermissionChoice = Int - def (x: Permissions) & (y: Permissions): Permissions = x | y - def (x: PermissionChoice) | (y: PermissionChoice): PermissionChoice = x | y - def (granted: Permissions).is(required: Permissions) = (granted & required) == required - def (granted: Permissions).isOneOf(required: PermissionChoice) = (granted & required) != 0 + extension (x: Permissions) def & (y: Permissions): Permissions = x | y + extension (x: PermissionChoice) def | (y: PermissionChoice): PermissionChoice = x | y + extension (granted: Permissions) def is(required: Permissions) = (granted & required) == required + extension (granted: Permissions) def isOneOf(required: PermissionChoice) = (granted & required) != 0 val NoPermission: Permission = 0 val Read: Permission = 1 diff --git a/docs/docs/reference/other-new-features/quoted-pattern-spec.md b/docs/docs/reference/other-new-features/quoted-pattern-spec.md index 9d805389778f..ad52b0e7a0dc 100644 --- a/docs/docs/reference/other-new-features/quoted-pattern-spec.md +++ b/docs/docs/reference/other-new-features/quoted-pattern-spec.md @@ -46,11 +46,11 @@ type Env def notMatched = None def matched = Some(()) // aka Some(Tuple0()) def matched[T](x: T) = Some(Tuple1(x)) -def (x: Matching) && (y: Matching) = if (x == None || y == None) None else Some(x.get ++ y.get) +extension (x: Matching) def && (y: Matching) = if (x == None || y == None) None else Some(x.get ++ y.get) def fold[T](m: Mattching*)(using Env): Matching = m.fold(matched)(_ && _) // `a =#= b` stands for `a` matches `b` -def (scrutinee: Tree) =#= pattern: Tree)(using Env): Matching // described by cases in the tables below +extension (scrutinee: Tree) def =#= pattern: Tree)(using Env): Matching // described by cases in the tables below def envWith(equiv: (Symbol, Symbol)*)(using Env): Env // Adds to the current environment the fact that s1 from the scrutinee is equivalent to s2 in the pattern diff --git a/docs/docs/reference/other-new-features/tupled-function.md b/docs/docs/reference/other-new-features/tupled-function.md index d89aef2cde0e..ad94169210ad 100644 --- a/docs/docs/reference/other-new-features/tupled-function.md +++ b/docs/docs/reference/other-new-features/tupled-function.md @@ -44,7 +44,8 @@ The following defines `tupled` as [extension method](../contextual/extension-met * @tparam Args the tuple type with the same types as the function arguments of F * @tparam R the return type of F */ -def [F, Args <: Tuple, R](f: F).tupled(using tf: TupledFunction[F, Args => R]): Args => R = tf.tupled(f) +extension [F, Args <: Tuple, R](f: F) + def tupled(using tf: TupledFunction[F, Args => R]): Args => R = tf.tupled(f) ``` `TupledFunction` can be used to generalize the `Function.untupled` to a function of any arities ([full example](https://github.com/lampepfl/dotty/blob/master/tests/run/tupled-function-untupled.scala)) @@ -59,7 +60,8 @@ def [F, Args <: Tuple, R](f: F).tupled(using tf: TupledFunction[F, Args => R]): * @tparam Args the tuple type with the same types as the function arguments of F * @tparam R the return type of F */ -def [F, Args <: Tuple, R](f: Args => R).untupled(using tf: TupledFunction[F, Args => R]): F = tf.untupled(f) +extension [F, Args <: Tuple, R](f: Args => R) + def untupled(using tf: TupledFunction[F, Args => R]): F = tf.untupled(f) ``` `TupledFunction` can also be used to generalize the [`Tuple1.compose`](https://github.com/lampepfl/dotty/blob/master/tests/run/tupled-function-compose.scala) and [`Tuple1.andThen`](https://github.com/lampepfl/dotty/blob/master/tests/run/tupled-function-andThen.scala) methods to compose functions of larger arities and with functions that return tuples. @@ -73,7 +75,8 @@ def [F, Args <: Tuple, R](f: Args => R).untupled(using tf: TupledFunction[F, Arg * @tparam GArgs the tuple type with the same types as the function arguments of G * @tparam R the return type of F */ -def [F, G, FArgs <: Tuple, GArgs <: Tuple, R](f: F).compose(g: G)(using tg: TupledFunction[G, GArgs => FArgs], tf: TupledFunction[F, FArgs => R]): GArgs => R = { +extension [F, G, FArgs <: Tuple, GArgs <: Tuple, R](f: F) + def compose(g: G)(using tg: TupledFunction[G, GArgs => FArgs], tf: TupledFunction[F, FArgs => R]): GArgs => R = { (x: GArgs) => tf.tupled(f)(tg.tupled(g)(x)) } ``` diff --git a/tests/init/crash/i5606.scala b/tests/init/crash/i5606.scala index f84c752e9b90..aedfa0617dfc 100644 --- a/tests/init/crash/i5606.scala +++ b/tests/init/crash/i5606.scala @@ -1,6 +1,6 @@ object Test extends App { - def [A, B](f: A => B) `$` (a: A): B = f(a) + extension [A, B](f: A => B) def `$` (a: A): B = f(a) assert((((a: Int) => a.toString()) `$` 10) == "10") diff --git a/tests/init/crash/i7821.scala b/tests/init/crash/i7821.scala index 5903b63219cd..523fa1e7306c 100644 --- a/tests/init/crash/i7821.scala +++ b/tests/init/crash/i7821.scala @@ -4,7 +4,7 @@ object XObject { def anX: X = 5 given ops as Object { - def (x: X) + (y: X): X = x + y + extension (x: X) def + (y: X): X = x + y } } @@ -14,7 +14,7 @@ object MyXObject { def anX: MyX = XObject.anX given ops as Object { - def (x: MyX) + (y: MyX): MyX = x + y // error: warring: Infinite recursive call + extension (x: MyX) def + (y: MyX): MyX = x + y // error: warring: Infinite recursive call } } diff --git a/tests/neg-custom-args/extmethods-tparams.scala b/tests/neg-custom-args/extmethods-tparams.scala index 59a9780a7abf..9cf6e94c14c4 100644 --- a/tests/neg-custom-args/extmethods-tparams.scala +++ b/tests/neg-custom-args/extmethods-tparams.scala @@ -1,2 +1,2 @@ -def (self: T).foo[T] = ??? // error -def [T1](self: T1) bar[T2] = ??? // error // error \ No newline at end of file +extension (self: T) def foo[T] = ??? // error +extension [T1](self: T1) def bar[T2] = ??? // error \ No newline at end of file diff --git a/tests/neg-custom-args/fatal-warnings/i7821.scala b/tests/neg-custom-args/fatal-warnings/i7821.scala index 5903b63219cd..523fa1e7306c 100644 --- a/tests/neg-custom-args/fatal-warnings/i7821.scala +++ b/tests/neg-custom-args/fatal-warnings/i7821.scala @@ -4,7 +4,7 @@ object XObject { def anX: X = 5 given ops as Object { - def (x: X) + (y: X): X = x + y + extension (x: X) def + (y: X): X = x + y } } @@ -14,7 +14,7 @@ object MyXObject { def anX: MyX = XObject.anX given ops as Object { - def (x: MyX) + (y: MyX): MyX = x + y // error: warring: Infinite recursive call + extension (x: MyX) def + (y: MyX): MyX = x + y // error: warring: Infinite recursive call } } diff --git a/tests/neg-custom-args/infix.scala b/tests/neg-custom-args/infix.scala index 1bc65e8205b1..48827fb89558 100644 --- a/tests/neg-custom-args/infix.scala +++ b/tests/neg-custom-args/infix.scala @@ -8,9 +8,10 @@ class C: object C: given AnyRef: - def (x: C) iop (y: Int) = ??? - def (x: C).mop (y: Int) = ??? - def (x: C) ++ (y: Int) = ??? + extension (x: C) + @infix def iop (y: Int) = ??? + def mop (y: Int) = ??? + def ++ (y: Int) = ??? val c = C() def test() = { diff --git a/tests/neg-macros/i6432/Macro_1.scala b/tests/neg-macros/i6432/Macro_1.scala index 9d3874a0afd6..90e4d5b91cf8 100644 --- a/tests/neg-macros/i6432/Macro_1.scala +++ b/tests/neg-macros/i6432/Macro_1.scala @@ -4,7 +4,7 @@ import scala.quoted.autolift object Macro { - inline def (inline sc: StringContext).foo(args: String*): Unit = ${ impl('sc) } + extension (inline sc: StringContext) inline def foo(args: String*): Unit = ${ impl('sc) } def impl(sc: Expr[StringContext])(using qctx: QuoteContext) : Expr[Unit] = { import qctx.tasty._ diff --git a/tests/neg-macros/i6432b/Macro_1.scala b/tests/neg-macros/i6432b/Macro_1.scala index 9d3874a0afd6..90e4d5b91cf8 100644 --- a/tests/neg-macros/i6432b/Macro_1.scala +++ b/tests/neg-macros/i6432b/Macro_1.scala @@ -4,7 +4,7 @@ import scala.quoted.autolift object Macro { - inline def (inline sc: StringContext).foo(args: String*): Unit = ${ impl('sc) } + extension (inline sc: StringContext) inline def foo(args: String*): Unit = ${ impl('sc) } def impl(sc: Expr[StringContext])(using qctx: QuoteContext) : Expr[Unit] = { import qctx.tasty._ diff --git a/tests/neg-macros/i7698.scala b/tests/neg-macros/i7698.scala index c3e2800cdfce..0cbc8ac0dcd3 100644 --- a/tests/neg-macros/i7698.scala +++ b/tests/neg-macros/i7698.scala @@ -10,4 +10,4 @@ def showInterpolatorImpl(sc: Expr[StringContext], argsExpr: Expr[Seq[Any]])(usin case '[ Int ] => // error ??? -inline def (inline sc: StringContext) show (args: Any*): String = ${ showInterpolatorImpl('sc, 'args) } +extension (inline sc: StringContext) inline def show (args: Any*): String = ${ showInterpolatorImpl('sc, 'args) } diff --git a/tests/neg-macros/reflect-inline/assert_1.scala b/tests/neg-macros/reflect-inline/assert_1.scala index cee496888207..863cd9be32be 100644 --- a/tests/neg-macros/reflect-inline/assert_1.scala +++ b/tests/neg-macros/reflect-inline/assert_1.scala @@ -1,7 +1,7 @@ import scala.quoted._ object api { - inline def (inline x: String).stripMargin2: String = + extension (inline x: String) inline def stripMargin2: String = ${ stripImpl('x) } private def stripImpl(x: Expr[String])(using qctx: QuoteContext): Expr[String] = diff --git a/tests/neg-macros/tasty-string-interpolator-position-a/Macro_1.scala b/tests/neg-macros/tasty-string-interpolator-position-a/Macro_1.scala index 49503b208e2b..06556ce8e138 100644 --- a/tests/neg-macros/tasty-string-interpolator-position-a/Macro_1.scala +++ b/tests/neg-macros/tasty-string-interpolator-position-a/Macro_1.scala @@ -3,7 +3,7 @@ import scala.language.implicitConversions object Macro { - implicit inline def (strCtx: => StringContext).f2(args: =>Any*): String = ${FIntepolator.apply('strCtx, 'args)} + extension (strCtx: => StringContext) implicit inline def f2(args: =>Any*): String = ${FIntepolator.apply('strCtx, 'args)} } diff --git a/tests/neg-macros/tasty-string-interpolator-position-b/Macro_1.scala b/tests/neg-macros/tasty-string-interpolator-position-b/Macro_1.scala index bcf63788e3c2..1d9e849c5564 100644 --- a/tests/neg-macros/tasty-string-interpolator-position-b/Macro_1.scala +++ b/tests/neg-macros/tasty-string-interpolator-position-b/Macro_1.scala @@ -3,7 +3,7 @@ import scala.language.implicitConversions object Macro { - implicit inline def (strCtx: => StringContext).f3(args: =>Any*): String = ${FIntepolator.apply('strCtx, 'args)} + extension (strCtx: => StringContext) implicit inline def f3(args: =>Any*): String = ${FIntepolator.apply('strCtx, 'args)} } diff --git a/tests/neg/anonymous-instance-cannot-be-empty.check b/tests/neg/anonymous-instance-cannot-be-empty.check deleted file mode 100644 index f6b50962ebc4..000000000000 --- a/tests/neg/anonymous-instance-cannot-be-empty.check +++ /dev/null @@ -1,6 +0,0 @@ --- [E154] Syntax Error: tests/neg/anonymous-instance-cannot-be-empty.scala:2:15 ---------------------------------------- -2 | extension on[T] (t: T) { } // error - | ^^^^^^^^ - | anonymous instance must implement a type or have at least one extension method - -longer explanation available when compiling with `-explain` diff --git a/tests/neg/anonymous-instance-cannot-be-empty.scala b/tests/neg/anonymous-instance-cannot-be-empty.scala index 3581b17ebe88..a36d85e115d9 100644 --- a/tests/neg/anonymous-instance-cannot-be-empty.scala +++ b/tests/neg/anonymous-instance-cannot-be-empty.scala @@ -1,3 +1,3 @@ object Test { - extension on[T] (t: T) { } // error + extension [T] (t: T) { } // error } \ No newline at end of file diff --git a/tests/neg/capture1.scala b/tests/neg/capture1.scala index 7b166bf5af1d..c08312c5af7a 100644 --- a/tests/neg/capture1.scala +++ b/tests/neg/capture1.scala @@ -4,11 +4,11 @@ object Test extends App { val l: mutable.Seq[String] = mutable.ArrayBuffer() - def [T, U](xs: List[T]) emap (f: T => U): List[U] = xs.map(f) + extension [T, U](xs: List[T]) def emap (f: T => U): List[U] = xs.map(f) - def [T](xs: List[T]) ereduce (f: (T, T) => T): T = xs.reduceLeft(f) + extension [T](xs: List[T]) def ereduce (f: (T, T) => T): T = xs.reduceLeft(f) - def [T](xs: mutable.Seq[T]) append (ys: mutable.Seq[T]): mutable.Seq[T] = xs ++ ys + extension [T](xs: mutable.Seq[T]) def append (ys: mutable.Seq[T]): mutable.Seq[T] = xs ++ ys List(l, mutable.ArrayBuffer(1)) .emap(list => list) diff --git a/tests/neg/extension-cannot-have-type.check b/tests/neg/extension-cannot-have-type.check deleted file mode 100644 index b7bfc18ad778..000000000000 --- a/tests/neg/extension-cannot-have-type.check +++ /dev/null @@ -1,4 +0,0 @@ --- Error: tests/neg/extension-cannot-have-type.scala:3:10 -------------------------------------------------------------- -3 | def f[U](u: U): T = ??? // error : extension method cannot have type params - | ^ - | extension method cannot have type parameters since some were already given previously diff --git a/tests/neg/extension-cannot-have-type.scala b/tests/neg/extension-cannot-have-type.scala index 67470fe13f0f..c6fd2d3df4b4 100644 --- a/tests/neg/extension-cannot-have-type.scala +++ b/tests/neg/extension-cannot-have-type.scala @@ -1,5 +1,5 @@ object Test { - extension on[T] (t: T) { - def f[U](u: U): T = ??? // error : extension method cannot have type params + extension [T] (t: T) { + def f[U](u: U): T = ??? // error: extension method cannot have type parameters here, all type parameters go after `extension` } } \ No newline at end of file diff --git a/tests/neg/extension-method-not-allowed.check b/tests/neg/extension-method-not-allowed.check deleted file mode 100644 index 8886f484b869..000000000000 --- a/tests/neg/extension-method-not-allowed.check +++ /dev/null @@ -1,4 +0,0 @@ --- Error: tests/neg/extension-method-not-allowed.scala:3:8 ------------------------------------------------------------- -3 | def (c: T).f: T = ??? // error : No extension method allowed here - | ^^^^^^^^^^^^^^^^^^^^^ - | no extension method allowed here since leading parameter was already given diff --git a/tests/neg/extension-method-not-allowed.scala b/tests/neg/extension-method-not-allowed.scala index a50235481c22..d5b85c54693f 100644 --- a/tests/neg/extension-method-not-allowed.scala +++ b/tests/neg/extension-method-not-allowed.scala @@ -1,5 +1,5 @@ object Test { - extension on[T] (t: T) { - def (c: T).f: T = ??? // error : No extension method allowed here + extension [T] (t: T) { + extension (c: T) def f: T = ??? // error : No extension method allowed here } } \ No newline at end of file diff --git a/tests/neg/extension-methods.scala b/tests/neg/extension-methods.scala index 8396070e3282..e075105762f9 100644 --- a/tests/neg/extension-methods.scala +++ b/tests/neg/extension-methods.scala @@ -1,7 +1,7 @@ object Test { implicit object O { - def (x: String).l1 = x.length + extension (x: String) def l1 = x.length def l1(x: Int) = x * x def l2(x: String) = x.length } @@ -10,8 +10,8 @@ object Test { "".l2 // error 1.l1 // error - extension on [T](xs: List[T]) { - def (x: Int).f1: T = ??? // error: No extension method allowed here, since collective parameters are given + extension [T](xs: List[T]) { + extension (x: Int) def f1: T = ??? // error: No extension method allowed here, since collective parameters are given def f2[T]: T = ??? // error: T is already defined as type T def f3(xs: List[T]) = ??? // error: xs is already defined as value xs } diff --git a/tests/neg/extensions-can-only-have-defs.check b/tests/neg/extensions-can-only-have-defs.check deleted file mode 100644 index c155678f6fed..000000000000 --- a/tests/neg/extensions-can-only-have-defs.check +++ /dev/null @@ -1,4 +0,0 @@ --- Error: tests/neg/extensions-can-only-have-defs.scala:3:8 ------------------------------------------------------------ -3 | val v: T = ??? // error : extensions can only have defs - | ^^^^^^^^^^^^^^ - | extension clause can only define methods diff --git a/tests/neg/extensions-can-only-have-defs.scala b/tests/neg/extensions-can-only-have-defs.scala index 225d065e66f7..ac21105a2c5f 100644 --- a/tests/neg/extensions-can-only-have-defs.scala +++ b/tests/neg/extensions-can-only-have-defs.scala @@ -1,5 +1,5 @@ object Test { - extension on[T] (t: T) { - val v: T = ??? // error : extensions can only have defs + extension [T] (t: T) { + val v: T = ??? // error // error } } \ No newline at end of file diff --git a/tests/neg/extmethod-overload.scala b/tests/neg/extmethod-overload.scala index 33fafb70ddd1..c680acda9144 100644 --- a/tests/neg/extmethod-overload.scala +++ b/tests/neg/extmethod-overload.scala @@ -1,11 +1,12 @@ object Test { - extension a on (x: Int) { - def |+| (y: Int) = x + y - } - - extension b on (x: Int) { - def |+| (y: String) = x + y.length - } + given a as AnyRef: + extension (x: Int) { + def |+| (y: Int) = x + y + } + given b as AnyRef: + extension (x: Int) { + def |+| (y: String) = x + y.length + } assert((1 |+| 2) == 3) // error ambiguous locally { diff --git a/tests/neg/extmethod-override.scala b/tests/neg/extmethod-override.scala index 9b2e809f9baa..ec6f4770a6c7 100644 --- a/tests/neg/extmethod-override.scala +++ b/tests/neg/extmethod-override.scala @@ -1,8 +1,8 @@ class A { def f(x: Int)(y: Int): Int = 0 - def (x: Int).g(y: Int): Int = 1 + extension (x: Int) def g(y: Int): Int = 1 } class B extends A { - override def (x: Int).f(y: Int): Int = 1 // error + extension (x: Int) override def f(y: Int): Int = 1 // error override def g(x: Int)(y: Int): Int = 0 // error } \ No newline at end of file diff --git a/tests/neg/i5455.scala b/tests/neg/i5455.scala index e496cd68541f..0d6c601ef985 100644 --- a/tests/neg/i5455.scala +++ b/tests/neg/i5455.scala @@ -11,7 +11,7 @@ object Library { def toInt(n: Nat): Int = n } - extension on (x: Nat) { + extension (x: Nat) { def * (y: Nat): Nat = x * y def toInt: Int = x } diff --git a/tests/neg/i5773.scala b/tests/neg/i5773.scala index 4ae1fa220599..617720e085cf 100644 --- a/tests/neg/i5773.scala +++ b/tests/neg/i5773.scala @@ -1,16 +1,16 @@ trait Semigroup[T] { - def (lhs: T).append(rhs: T): T - def (lhs: Int).appendS(rhs: T): T = ??? + extension (lhs: T) def append(rhs: T): T + extension (lhs: Int) def appendS(rhs: T): T = ??? } object Semigroup { implicit object stringAppend extends Semigroup[String] { - override def (lhs: String).append(rhs: String): String = lhs + rhs + extension (lhs: String) override def append(rhs: String): String = lhs + rhs } implicit def sumSemigroup[N](implicit N: Numeric[N]): Semigroup[N] = new { - override def (lhs: N).append(rhs: N): N = N.plus(lhs, rhs) - def (lhs: Int).appendS(rhs: N): N = ??? // N.plus(lhs, rhs) + extension (lhs: N) override def append(rhs: N): N = N.plus(lhs, rhs) + extension (lhs: Int) def appendS(rhs: N): N = ??? // N.plus(lhs, rhs) } } diff --git a/tests/neg/i6662.scala b/tests/neg/i6662.scala index 3c50327e718b..ccad0675da71 100644 --- a/tests/neg/i6662.scala +++ b/tests/neg/i6662.scala @@ -1,9 +1,9 @@ opaque type Opt[A >: Null] = A -inline def [A >: Null](x: Opt[A]) nonEmpty: Boolean = x.get != null // error: Implementation restriction -inline def [A >: Null](x: Opt[A]) isEmpty: Boolean = x.get == null // error: Implementation restriction -inline def [A >: Null](x: Opt[A]) isDefined: Boolean = x.nonEmpty // error: Implementation restriction -inline def [A >: Null](x: Opt[A]) get: A = Opt.unOpt(x) // error: Implementation restriction +extension [A >: Null](x: Opt[A]) inline def nonEmpty: Boolean = x.get != null // error: Implementation restriction +extension [A >: Null](x: Opt[A]) inline def isEmpty: Boolean = x.get == null // error: Implementation restriction +extension [A >: Null](x: Opt[A]) inline def isDefined: Boolean = x.nonEmpty // error: Implementation restriction +extension [A >: Null](x: Opt[A]) inline def get: A = Opt.unOpt(x) // error: Implementation restriction object Opt { diff --git a/tests/neg/i6762b.scala b/tests/neg/i6762b.scala index e5ad5cee17ed..66677a9639ec 100644 --- a/tests/neg/i6762b.scala +++ b/tests/neg/i6762b.scala @@ -9,5 +9,5 @@ type Liftable given Liftable = ??? implicit object ExprOps { - def [T](x: T).toExpr(using Liftable): Expr[T] = ??? + extension [T](x: T) def toExpr(using Liftable): Expr[T] = ??? } diff --git a/tests/neg/i6779.check b/tests/neg/i6779.check index 396e3f829d6a..0a34f2a47321 100644 --- a/tests/neg/i6779.check +++ b/tests/neg/i6779.check @@ -8,8 +8,8 @@ | ^ | Found: F[T] | Required: F[G[T]] --- [E007] Type Mismatch Error: tests/neg/i6779.scala:13:31 ------------------------------------------------------------- -13 |def g3[T](x: T): F[G[T]] = f(x)(using summon[Stuff]) // error - | ^^^^^^^^^^^^^^^^^^^^^^^^^ +-- [E007] Type Mismatch Error: tests/neg/i6779.scala:13:41 ------------------------------------------------------------- +13 |def g3[T](x: T): F[G[T]] = extension_f(x)(using summon[Stuff]) // error + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | Found: F[T] | Required: F[G[T]] diff --git a/tests/neg/i6779.scala b/tests/neg/i6779.scala index dabf54f28db6..bd40387be3d9 100644 --- a/tests/neg/i6779.scala +++ b/tests/neg/i6779.scala @@ -3,11 +3,11 @@ type G[T] type Stuff given Stuff = ??? -def [T](x: T).f(using Stuff): F[T] = ??? +extension [T](x: T) def f(using Stuff): F[T] = ??? def g1[T](x: T): F[G[T]] = x.f(using summon[Stuff]) // error def g2[T](x: T): F[G[T]] = x.f // error -def g3[T](x: T): F[G[T]] = f(x)(using summon[Stuff]) // error +def g3[T](x: T): F[G[T]] = extension_f(x)(using summon[Stuff]) // error diff --git a/tests/neg/i6801.scala b/tests/neg/i6801.scala index b2a968cc7893..18295ca35246 100644 --- a/tests/neg/i6801.scala +++ b/tests/neg/i6801.scala @@ -1,4 +1,4 @@ -extension myNumericOps on [T](x: T) { +extension [T](x: T) { def + (y: T)(using n: Numeric[T]): T = n.plus(x,y) } def foo[T: Numeric](x: T) = 1f + x // error: no implicit argument of type Numeric[Any] diff --git a/tests/neg/i6900.scala b/tests/neg/i6900.scala index c9ead6a99051..7382c3a9df01 100644 --- a/tests/neg/i6900.scala +++ b/tests/neg/i6900.scala @@ -1,7 +1,7 @@ object Test2 { // Works with extension method - extension on [A](a: A): + extension [A](a: A): def foo[C]: C => A = _ => a // error: extension method cannot have type parameters 1.foo.foo diff --git a/tests/neg/i7060.scala b/tests/neg/i7060.scala index 38ab44d584a4..77f12a450a2c 100644 --- a/tests/neg/i7060.scala +++ b/tests/neg/i7060.scala @@ -12,7 +12,7 @@ object PostConditions { def res[T](using b: Box[T]): T = b.t - def [T](e: T) ensure (cond: Box[T] ?=> Boolean): T = { + extension [T](e: T) def ensure (cond: Box[T] ?=> Boolean): T = { if (cond(using Box(e))) e else throw new AssertionError("condition not fulfilled") } diff --git a/tests/neg/i7438.scala b/tests/neg/i7438.scala index a3168d28e703..32c375d55674 100644 --- a/tests/neg/i7438.scala +++ b/tests/neg/i7438.scala @@ -1,7 +1,7 @@ type Tr[+A] -inline def [A, B](tr: Tr[A]).map(f: A => B): Tr[B] = ??? +extension [A, B](tr: Tr[A]) inline def map(f: A => B): Tr[B] = ??? -def (d: Double).func: None.type => Some[Double] = ??? +extension (d: Double) def func: None.type => Some[Double] = ??? def run[A](query: None.type => Some[A]): Some[A] = ??? diff --git a/tests/neg/i7529.scala b/tests/neg/i7529.scala index 55aa2902e348..593897210828 100644 --- a/tests/neg/i7529.scala +++ b/tests/neg/i7529.scala @@ -1,4 +1,4 @@ -extension fooOps on [A](a: A): +extension [A](a: A) @nonsense // error: not found: nonsense def foo = ??? \ No newline at end of file diff --git a/tests/neg/i8050.scala b/tests/neg/i8050.scala index 905361fe4e49..029e8d8195d5 100644 --- a/tests/neg/i8050.scala +++ b/tests/neg/i8050.scala @@ -1,5 +1,5 @@ object stuff: def exec(dir: Int) = ??? -extension on (a: Int): +extension (a: Int) inline def exec: Unit = stuff.exec("aaa") // error diff --git a/tests/neg/i8894.scala b/tests/neg/i8894.scala index 60b6937b3591..6bab91992582 100644 --- a/tests/neg/i8894.scala +++ b/tests/neg/i8894.scala @@ -2,7 +2,7 @@ trait Extractor { inline def unapplySeq(inline tn: String): Option[Seq[String]] } -transparent inline def (inline sc: StringContext).poql: Extractor = new Extractor { +extension (inline sc: StringContext) transparent inline def poql: Extractor = new Extractor { inline def unapplySeq(inline tn: String): Option[Seq[String]] = ??? // error: Implementation restriction: nested inline methods are not supported } diff --git a/tests/neg/i9185.check b/tests/neg/i9185.check index 0e0b02b6029e..9fb873f57334 100644 --- a/tests/neg/i9185.check +++ b/tests/neg/i9185.check @@ -4,17 +4,17 @@ |value pure is not a member of String. |An extension method was tried, but could not be fully constructed: | - | M.pure[A, F]("ola")( + | M.extension_pure[A, F]("ola")( | /* ambiguous: both object listMonad in object M and object optionMonad in object M match type M[F] */summon[M[F]] | ) --- Error: tests/neg/i9185.scala:8:26 ----------------------------------------------------------------------------------- -8 | val value3 = pure("ola") // error - | ^ - |ambiguous implicit arguments: both object listMonad in object M and object optionMonad in object M match type M[F] of parameter m of method pure in object M +-- Error: tests/neg/i9185.scala:8:36 ----------------------------------------------------------------------------------- +8 | val value3 = extension_pure("ola") // error + | ^ + |ambiguous implicit arguments: both object listMonad in object M and object optionMonad in object M match type M[F] of parameter m of method extension_pure in object M -- [E008] Not Found Error: tests/neg/i9185.scala:11:16 ----------------------------------------------------------------- 11 | val l = "abc".len // error | ^^^^^^^^^ | value len is not a member of String. | An extension method was tried, but could not be fully constructed: | - | M.len("abc") + | M.extension_len("abc") diff --git a/tests/neg/i9185.scala b/tests/neg/i9185.scala index 911d380a98af..c4b47623ed0c 100644 --- a/tests/neg/i9185.scala +++ b/tests/neg/i9185.scala @@ -1,12 +1,12 @@ trait M[F[_]] { def pure[A](x: A): F[A] } object M { - def [A, F[A]](x: A).pure(using m: M[F]): F[A] = m.pure(x) + extension [A, F[A]](x: A) def pure(using m: M[F]): F[A] = m.pure(x) given listMonad as M[List] { def pure[A](x: A): List[A] = List(x) } given optionMonad as M[Option] { def pure[A](x: A): Option[A] = Some(x) } val value1: List[String] = "ola".pure val value2 = "ola".pure // error - val value3 = pure("ola") // error + val value3 = extension_pure("ola") // error - def (x: Int).len: Int = x + extension (x: Int) def len: Int = x val l = "abc".len // error } \ No newline at end of file diff --git a/tests/neg/illegal-extension.scala b/tests/neg/illegal-extension.scala new file mode 100644 index 000000000000..5adbc87b8f18 --- /dev/null +++ b/tests/neg/illegal-extension.scala @@ -0,0 +1,3 @@ +trait A { + def extension_n: String = "illegal method" // error: illegal method name: extension_n may not start with `extension_` +} diff --git a/tests/neg/indent.scala b/tests/neg/indent.scala index 2bac87c9efb7..247698cb78b4 100644 --- a/tests/neg/indent.scala +++ b/tests/neg/indent.scala @@ -1,6 +1,6 @@ object Test { - def (x: Int).gt(y: Int) = x > y + extension (x: Int) def gt(y: Int) = x > y val y3 = if (1) max 10 gt 0 // error: end of statement expected but integer literal found // error // error // error 1 diff --git a/tests/neg/missing-implicit1.check b/tests/neg/missing-implicit1.check index 4ff1d5d84225..5b3d0dc6a4e9 100644 --- a/tests/neg/missing-implicit1.check +++ b/tests/neg/missing-implicit1.check @@ -19,7 +19,7 @@ -- Error: tests/neg/missing-implicit1.scala:23:42 ---------------------------------------------------------------------- 23 | List(1, 2, 3).traverse(x => Option(x)) // error | ^ - |no implicit argument of type testObjectInstance.Zip[Option] was found for an implicit parameter of method traverse in trait Traverse + |no implicit argument of type testObjectInstance.Zip[Option] was found for an implicit parameter of method extension_traverse in trait Traverse | |The following import might fix the problem: | diff --git a/tests/neg/missing-implicit1.scala b/tests/neg/missing-implicit1.scala index 5c6ca73f5fa3..c94d78099068 100644 --- a/tests/neg/missing-implicit1.scala +++ b/tests/neg/missing-implicit1.scala @@ -1,15 +1,15 @@ object testObjectInstance: trait Zip[F[_]] trait Traverse[F[_]] { - def [A, B, G[_] : Zip](fa: F[A]) traverse(f: A => G[B]): G[F[B]] + extension [A, B, G[_] : Zip](fa: F[A]) def traverse(f: A => G[B]): G[F[B]] } object instances { given zipOption as Zip[Option] = ??? given traverseList as Traverse[List] = ??? - extension listExtension on [T](xs: List[T]): + extension [T](xs: List[T]) def second: T = xs.tail.head - def [T](xs: List[T]) first: T = xs.head + extension [T](xs: List[T]) def first: T = xs.head } def ff(using xs: Zip[Option]) = ??? diff --git a/tests/neg/missing-implicit4.check b/tests/neg/missing-implicit4.check index 4653dd8df351..2baa37b0e5e9 100644 --- a/tests/neg/missing-implicit4.check +++ b/tests/neg/missing-implicit4.check @@ -19,7 +19,7 @@ -- Error: tests/neg/missing-implicit4.scala:20:42 ---------------------------------------------------------------------- 20 | List(1, 2, 3).traverse(x => Option(x)) // error | ^ - |no implicit argument of type Zip[Option] was found for an implicit parameter of method traverse in trait Traverse + |no implicit argument of type Zip[Option] was found for an implicit parameter of method extension_traverse in trait Traverse | |The following import might fix the problem: | diff --git a/tests/neg/missing-implicit4.scala b/tests/neg/missing-implicit4.scala index 13db252f8753..a02d543c12f8 100644 --- a/tests/neg/missing-implicit4.scala +++ b/tests/neg/missing-implicit4.scala @@ -1,7 +1,7 @@ def testLocalInstance = trait Zip[F[_]] trait Traverse[F[_]] { - def [A, B, G[_] : Zip](fa: F[A]) traverse(f: A => G[B]): G[F[B]] + extension [A, B, G[_] : Zip](fa: F[A]) def traverse(f: A => G[B]): G[F[B]] } object instances { diff --git a/tests/neg/missing-implicit5.check b/tests/neg/missing-implicit5.check index 62e5fb2c3a2d..810d570746ad 100644 --- a/tests/neg/missing-implicit5.check +++ b/tests/neg/missing-implicit5.check @@ -14,7 +14,7 @@ | | The following import might fix the problem: | - | import testObjectInstance.instances.listExtension + | import testObjectInstance.instances.second | -- [E008] Not Found Error: tests/neg/missing-implicit5.scala:17:17 ----------------------------------------------------- 17 | Array(1, 2, 3).first // error, no hint diff --git a/tests/neg/missing-implicit5.scala b/tests/neg/missing-implicit5.scala index 4988229a1348..a2a0fd55315c 100644 --- a/tests/neg/missing-implicit5.scala +++ b/tests/neg/missing-implicit5.scala @@ -1,15 +1,15 @@ object testObjectInstance: trait Zip[F[_]] trait Traverse[F[_]] { - def [A, B, G[_] : Zip](fa: F[A]) traverse(f: A => G[B]): G[F[B]] + extension [A, B, G[_] : Zip](fa: F[A]) def traverse(f: A => G[B]): G[F[B]] } object instances { given zipOption as Zip[Option] = ??? given traverseList as Traverse[List] = ??? - extension listExtension on [T](xs: List[T]): + extension [T](xs: List[T]) def second: T = xs.tail.head - def [T](xs: List[T]) first: T = xs.head + extension [T](xs: List[T]) def first: T = xs.head } List(1, 2, 3).first // error diff --git a/tests/neg/opaque-bounds.scala b/tests/neg/opaque-bounds.scala index acaebccbb6fb..2eb2d7ecc0d1 100644 --- a/tests/neg/opaque-bounds.scala +++ b/tests/neg/opaque-bounds.scala @@ -19,10 +19,10 @@ object Access { opaque type PermissionChoice = Int opaque type Permission <: Permissions & PermissionChoice = Int - def (x: Permissions) & (y: Permissions): Permissions = x & y - def (x: PermissionChoice) | (y: PermissionChoice): PermissionChoice = x | y - def (x: Permissions).is(y: Permissions) = (x & y) == y - def (x: Permissions).isOneOf(y: PermissionChoice) = (x & y) != 0 + extension (x: Permissions) def & (y: Permissions): Permissions = x & y + extension (x: PermissionChoice) def | (y: PermissionChoice): PermissionChoice = x | y + extension (x: Permissions) def is(y: Permissions) = (x & y) == y + extension (x: Permissions) def isOneOf(y: PermissionChoice) = (x & y) != 0 val NoPermission: Permission = 0 val ReadOnly: Permission = 1 diff --git a/tests/neg/override-extension_normal-methods.check b/tests/neg/override-extension_normal-methods.check deleted file mode 100644 index fdd94fe558ee..000000000000 --- a/tests/neg/override-extension_normal-methods.check +++ /dev/null @@ -1,10 +0,0 @@ --- Error: tests/neg/override-extension_normal-methods.scala:6:6 -------------------------------------------------------- -6 | def m[T](x: T): String = "normal method" // error: normal method, cannot override an extension method. Also needs `override' modifier (but this error should be obfuscated). - | ^ - | error overriding method m in trait A of type [T](t: T): String; - | method m of type [T](x: T): String is a normal method, cannot override an extension method --- Error: tests/neg/override-extension_normal-methods.scala:14:6 ------------------------------------------------------- -14 | def [T](t: T).m: String = "extrnsion method" // error: extension method, cannot override an normal method. Also needs `override' modifier (but this error should be obfuscated). - | ^ - | error overriding method m in trait B of type [T](x: T): String; - | method m of type [T](t: T): String is an extension method, cannot override a normal method diff --git a/tests/neg/override-extension_normal-methods.scala b/tests/neg/override-extension_normal-methods.scala index 1293bf8d6b14..036458e91ac7 100644 --- a/tests/neg/override-extension_normal-methods.scala +++ b/tests/neg/override-extension_normal-methods.scala @@ -1,9 +1,9 @@ trait A { - def [T](t: T).m: String = "extrnsion method" + extension [T](t: T) def m: String = "extension method" } trait AAA extends A { - def m[T](x: T): String = "normal method" // error: normal method, cannot override an extension method. Also needs `override' modifier (but this error should be obfuscated). + override def m[T](x: T): String = "normal method" // error: does not override } trait B { @@ -11,5 +11,5 @@ trait B { } trait BBB extends B { - def [T](t: T).m: String = "extrnsion method" // error: extension method, cannot override an normal method. Also needs `override' modifier (but this error should be obfuscated). + extension [T](t: T) override def m: String = "extension method" // error: does not override } \ No newline at end of file diff --git a/tests/neg/scalaStandardRedefinition.check b/tests/neg/scalaStandardRedefinition.check deleted file mode 100644 index ca31f76ff523..000000000000 --- a/tests/neg/scalaStandardRedefinition.check +++ /dev/null @@ -1,6 +0,0 @@ --- [E149] Syntax Error: tests/neg/scalaStandardRedefinition.scala:2:8 -------------------------------------------------- -2 | class Any() // error - | ^^^^^^^^^^^ - | illegal redefinition of standard class Any - -longer explanation available when compiling with `-explain` diff --git a/tests/neg/missing-implicit.check b/tests/pending/neg/missing-implicit.check similarity index 100% rename from tests/neg/missing-implicit.check rename to tests/pending/neg/missing-implicit.check diff --git a/tests/pos-custom-args/i8875.scala b/tests/pos-custom-args/i8875.scala index ff2c394057ae..ac5e0bd5cf42 100644 --- a/tests/pos-custom-args/i8875.scala +++ b/tests/pos-custom-args/i8875.scala @@ -1,5 +1,5 @@ class A { - extension Ext on (a: Int) { + extension (a: Int) { def foo: Int = 1 } } \ No newline at end of file diff --git a/tests/pos/combine.scala b/tests/pos/combine.scala index 17ca3ecbb88b..fd7715b2ac19 100644 --- a/tests/pos/combine.scala +++ b/tests/pos/combine.scala @@ -1,5 +1,5 @@ trait Semigroup[A] { - def (x: A).combine(y: A): A + extension (x: A) def combine(y: A): A } given Semigroup[Int] = ??? given [A, B](using Semigroup[A], Semigroup[B]) as Semigroup[(A, B)] = ??? diff --git a/tests/pos/consume.scala b/tests/pos/consume.scala index 05662f6821ea..d17638bb84a0 100644 --- a/tests/pos/consume.scala +++ b/tests/pos/consume.scala @@ -18,13 +18,13 @@ object Test2 { object math3: trait Ord[T]: - def (x: T) > (t: T): Boolean = ??? - def (x: T) <= (t: T): Boolean = ??? + extension (x: T) def > (t: T): Boolean = ??? + extension (x: T) def <= (t: T): Boolean = ??? trait Numeric[T] extends Ord[T]: - def (x: T) + (y: T): T = ??? - def (x: T) - (y: T): T = ??? - def (x: Int).numeric: T = ??? + extension (x: T) def + (y: T): T = ??? + extension (x: T) def - (y: T): T = ??? + extension (x: Int) def numeric: T = ??? end math3 object Test3: diff --git a/tests/pos/endmarkers.scala b/tests/pos/endmarkers.scala index f889e7d63f12..5b85ce3763cd 100644 --- a/tests/pos/endmarkers.scala +++ b/tests/pos/endmarkers.scala @@ -51,7 +51,7 @@ package p1.p2: end given end C - extension on (x: C): + extension (x: C) def ff: String = x.f end extension diff --git a/tests/pos/i5773.scala b/tests/pos/i5773.scala index 76f1e491a63e..0cc0bfdf9207 100644 --- a/tests/pos/i5773.scala +++ b/tests/pos/i5773.scala @@ -1,18 +1,18 @@ trait Semigroup[T] { - def (lhs: T).append(rhs: T): T + extension (lhs: T) def append(rhs: T): T } object Semigroup { implicit object stringAppend extends Semigroup[String] { - override def (lhs: String).append(rhs: String): String = lhs + rhs + extension (lhs: String) override def append(rhs: String): String = lhs + rhs } implicit def sumSemigroup[N](implicit N: Numeric[N]): Semigroup[N] = new { - override def (lhs: N).append(rhs: N): N = ??? // N.plus(lhs, rhs) + extension (lhs: N) override def append(rhs: N): N = ??? // N.plus(lhs, rhs) } implicit class SumSemiGroupDeco[N](implicit N: Numeric[N]) extends Semigroup[N] { - override def (lhs: N).append(rhs: N): N = ??? // N.plus(lhs, rhs) + extension (lhs: N) override def append(rhs: N): N = ??? // N.plus(lhs, rhs) } } @@ -31,6 +31,6 @@ object Main { def f3 = { import Semigroup.SumSemiGroupDeco - sumSemigroup.append(1)(2) + sumSemigroup.extension_append(1)(2) } } \ No newline at end of file diff --git a/tests/pos/i5773a.scala b/tests/pos/i5773a.scala index 801fa10f3f3f..214d0b061821 100644 --- a/tests/pos/i5773a.scala +++ b/tests/pos/i5773a.scala @@ -1,12 +1,12 @@ trait Semigroup[T] { - def (x: T).combine(y: T): T + extension (x: T) def combine(y: T): T } object Test { implicit val IntSemigroup: Semigroup[Int] = new { - def (x: Int).combine(y: Int): Int = x + y + extension (x: Int) def combine(y: Int): Int = x + y } implicit def OptionSemigroup[T: Semigroup]: Semigroup[Option[T]] = new { - def (x: Option[T]).combine(y: Option[T]): Option[T] = for { + extension (x: Option[T]) def combine(y: Option[T]): Option[T] = for { x0 <- x y0 <- y } yield x0.combine(y0) diff --git a/tests/pos/i6395.scala b/tests/pos/i6395.scala index 1e75d9ea42e7..6a80ea002bab 100644 --- a/tests/pos/i6395.scala +++ b/tests/pos/i6395.scala @@ -1,5 +1,5 @@ object Foo { - inline def (self: Int).foo(that: Int): Int = 5 - def (self: Int).bar: Int = self + extension (self: Int) inline def foo(that: Int): Int = 5 + extension (self: Int) def bar: Int = self 1.foo(2).bar } \ No newline at end of file diff --git a/tests/pos/i6565.scala b/tests/pos/i6565.scala index da08245804ca..0672724724fd 100644 --- a/tests/pos/i6565.scala +++ b/tests/pos/i6565.scala @@ -3,8 +3,8 @@ class Err type Lifted[A] = Err | A def point[O](o: O): Lifted[O] = o -def [O, U](o: Lifted[O]).map(f: O => U): Lifted[U] = ??? -def [O, U](o: Lifted[O]).flatMap(f: O => Lifted[U]): Lifted[U] = ??? +extension [O, U](o: Lifted[O]) def map(f: O => U): Lifted[U] = ??? +extension [O, U](o: Lifted[O]) def flatMap(f: O => Lifted[U]): Lifted[U] = ??? val error: Err = Err() diff --git a/tests/pos/i6705.scala b/tests/pos/i6705.scala index 40ad62cf1713..4375fd919a57 100644 --- a/tests/pos/i6705.scala +++ b/tests/pos/i6705.scala @@ -5,7 +5,7 @@ trait StringTempl { object Test { - def (x: String) shouldBe(y: String): Boolean = ??? + extension (x: String) def shouldBe(y: String): Boolean = ??? def test(tmpl: StringTempl): Unit = { tmpl.mkString shouldBe "hello" // error diff --git a/tests/pos/i6734.scala b/tests/pos/i6734.scala index d7ebc726ae79..fa7d9946ada4 100644 --- a/tests/pos/i6734.scala +++ b/tests/pos/i6734.scala @@ -1,11 +1,13 @@ -object Bug { +object Bug: - def [A, B, Z](ab: (A, B)) pipe2(f: (A, B) => Z): Z = f(ab._1, ab._2) + extension [A, B, Z](ab: (A, B)) + def pipe2(f: (A, B) => Z): Z = f(ab._1, ab._2) - def [A, B](a: A) leftErr(b: B): A = (a, b).pipe2((a, b) => a) //Did not compile before. - def [A, B](a: A) leftOk1(b: B): A = Tuple2(a, b).pipe2((a, b) => a) //Compiles - def [A, B](a: A) leftOk2(b: B): A = { - val t = (a, b) - t.pipe2((a, b) => a) //Compiles - } -} + extension [A, B](a: A) + def leftErr(b: B): A = (a, b).pipe2((a, b) => a) //Did not compile before. + extension [A, B](a: A) + def leftOk1(b: B): A = Tuple2(a, b).pipe2((a, b) => a) //Compiles + extension [A, B](a: A) + def leftOk2(b: B): A = + val t = (a, b) + t.pipe2((a, b) => a) //Compiles diff --git a/tests/pos/i6847.scala b/tests/pos/i6847.scala index 3908a38b9930..6a95e3a710ca 100644 --- a/tests/pos/i6847.scala +++ b/tests/pos/i6847.scala @@ -1,11 +1,11 @@ trait Syntax[F[_]] { - def [A](a: A) ret: F[A] + extension [A](a: A) def ret: F[A] } trait Instance[A] implicit val instanceSyntax: Syntax[Instance] = new Syntax[Instance] { - def [A](a: A) ret: Instance[A] = new Instance[A] {} + extension [A](a: A) def ret: Instance[A] = new Instance[A] {} } object Instance { diff --git a/tests/pos/i6900.scala b/tests/pos/i6900.scala index 1975b641fcc2..45313c21921e 100644 --- a/tests/pos/i6900.scala +++ b/tests/pos/i6900.scala @@ -21,7 +21,7 @@ object Test1 { object Test2 { // Works with extension method - extension on [A, C](a: A): + extension [A, C](a: A): def foo: C => A = _ => a 1.foo.foo diff --git a/tests/pos/i7041.scala b/tests/pos/i7041.scala index e7a77576374c..32bafa304dfb 100644 --- a/tests/pos/i7041.scala +++ b/tests/pos/i7041.scala @@ -1,12 +1,13 @@ import scala.util.control.NonLocalReturns._ -inline def [T, E <: Throwable](op: => T) rescue (fallback: PartialFunction[E, T]) = - try op - catch { - case ex: ReturnThrowable[_] => throw ex - case ex: E => - if (fallback.isDefinedAt(ex)) fallback(ex) else throw ex - } +extension [T, E <: Throwable](op: => T) + inline def rescue (fallback: PartialFunction[E, T]) = + try op + catch { + case ex: ReturnThrowable[_] => throw ex + case ex: E => + if (fallback.isDefinedAt(ex)) fallback(ex) else throw ex + } def test: Unit = { 9 / 0 rescue { case _: ArithmeticException => 10 } diff --git a/tests/pos/i7070.scala b/tests/pos/i7070.scala index a7d4abe82840..ea08e7482a77 100644 --- a/tests/pos/i7070.scala +++ b/tests/pos/i7070.scala @@ -2,7 +2,7 @@ object Test { class C - def (str: String).foo: C ?=> Int = ??? + extension (str: String) def foo: C ?=> Int = ??? given C = ??? diff --git a/tests/pos/i7084.scala b/tests/pos/i7084.scala index 0eb5c5920570..2288cd3660d3 100644 --- a/tests/pos/i7084.scala +++ b/tests/pos/i7084.scala @@ -2,7 +2,7 @@ object Test { type Foo - extension on (y: Any) { + extension (y: Any) { def g(using Foo): Any = ??? } diff --git a/tests/pos/i7087.scala b/tests/pos/i7087.scala index f66a75ca4976..0948b83cee84 100644 --- a/tests/pos/i7087.scala +++ b/tests/pos/i7087.scala @@ -6,7 +6,7 @@ type F[T] = T match { case G[a] => String } -extension on [T](tup: T) { +extension [T](tup: T) { def g(using Foo: F[T]) = ??? } diff --git a/tests/pos/i7119.scala b/tests/pos/i7119.scala index 50da06cba682..3b797bebe770 100644 --- a/tests/pos/i7119.scala +++ b/tests/pos/i7119.scala @@ -1,6 +1,6 @@ class Impl -def (impl: Impl).prop(using Int) = ???//summon[Int] +extension (impl: Impl) def prop(using Int) = ???//summon[Int] def main(args: Array[String]): Unit = { diff --git a/tests/pos/i7401.scala b/tests/pos/i7401.scala index 3d90bed0cfab..fe7d11aba7a5 100644 --- a/tests/pos/i7401.scala +++ b/tests/pos/i7401.scala @@ -1,11 +1,11 @@ object Test { - extension ops on (a: Int) { + extension (a: Int) { def foo(i: Int): Unit = () def foo: Unit = () } val x: Int = 5 x.foo(4) x.foo - ops.foo(x)(4) - ops.foo(x) + Test.extension_foo(x)(4) + extension_foo(x) } \ No newline at end of file diff --git a/tests/pos/i7403.scala b/tests/pos/i7403.scala index 36817fc2c14c..22aee2f0d6c0 100644 --- a/tests/pos/i7403.scala +++ b/tests/pos/i7403.scala @@ -1,6 +1,6 @@ object Test { - extension on (i: Int): + extension (i: Int) def pow2 = i * i println(5.pow2) diff --git a/tests/pos/i7413.scala b/tests/pos/i7413.scala index 329890ea79d2..1e90ca7e8d74 100644 --- a/tests/pos/i7413.scala +++ b/tests/pos/i7413.scala @@ -3,7 +3,7 @@ import scala.language.implicitConversions trait Fixture[A] extends Conversion[0, A] trait TestFramework[A]: - def (testName: String).in(test: Fixture[A] ?=> Unit): Unit = ??? + extension (testName: String) def in(test: Fixture[A] ?=> Unit): Unit = ??? trait Greeter: def greet(name: String): String = s"Hello $name" diff --git a/tests/pos/i7458.scala b/tests/pos/i7458.scala index 530e97c9e7f6..83885895f55d 100644 --- a/tests/pos/i7458.scala +++ b/tests/pos/i7458.scala @@ -1,3 +1,3 @@ type Tr[+V1, +O1 <: V1] -def [V2, O2 <: V2](tr: Tr[V2, O2]) sl: Tr[V2, O2] = ??? +extension [V2, O2 <: V2](tr: Tr[V2, O2]) def sl: Tr[V2, O2] = ??? def as[V3, O3 <: V3](tr: Tr[V3, O3]) : Tr[V3, O3] = tr.sl diff --git a/tests/pos/i7700.scala b/tests/pos/i7700.scala index f058acc5153b..6d19f0811cc4 100644 --- a/tests/pos/i7700.scala +++ b/tests/pos/i7700.scala @@ -4,7 +4,7 @@ trait Show[-A]: def show(a: A): String object Macros: - inline def (sc: StringContext).show(args: =>Any*): String = ??? + extension (sc: StringContext) inline def show(args: =>Any*): String = ??? object Show: def[A] (a: A) show(using S: Show[A]): String = S.show(a) diff --git a/tests/pos/i8181.scala b/tests/pos/i8181.scala index 6dbf2b74660f..d36214c3068a 100644 --- a/tests/pos/i8181.scala +++ b/tests/pos/i8181.scala @@ -1,6 +1,6 @@ object Main: def main(args: Array[String]): Unit = - extension on (a: AnyRef): + extension (a: AnyRef) def putout(): Unit = println(a) "blub".putout() diff --git a/tests/pos/i8188a.scala b/tests/pos/i8188a.scala index 3e9582e4ce9f..b360497d42c4 100644 --- a/tests/pos/i8188a.scala +++ b/tests/pos/i8188a.scala @@ -1,5 +1,5 @@ object Test { - extension StrDeco on (tree: String) { + extension (tree: String) { def show(using DummyImplicit): String = ??? def show(color: Boolean)(using DummyImplicit): String = ??? } diff --git a/tests/pos/i8241.scala b/tests/pos/i8241.scala index e6d1f863e7ad..c9de697affd1 100644 --- a/tests/pos/i8241.scala +++ b/tests/pos/i8241.scala @@ -1,7 +1,7 @@ -extension NameOps on (name: String): +extension (name: String) def isWildcard = ??? -end NameOps +end extension -extension on (name: String): +extension (name: String) def f = ??? end extension diff --git a/tests/pos/i8311.scala b/tests/pos/i8311.scala new file mode 100644 index 000000000000..fa6c68b40536 --- /dev/null +++ b/tests/pos/i8311.scala @@ -0,0 +1,19 @@ + +trait Show[O]: + extension (o: O) + def show: String + +class Box[A] +class Foo + +object test: + + given box[A](using Show[A]) as Show[Box[A]] = _.toString + given foo as Show[Foo] = _.toString + + def run(s: Box[Box[Foo]]): Unit = + val x = summon[Show[Box[Box[Foo]]]] + x.extension_show(s) + val r: String = box.extension_show(s) + println(s"step: ${box[Box[Foo]].extension_show(s)}") + println(s"step: ${s.show}") diff --git a/tests/pos/i9202.scala b/tests/pos/i9202.scala index 5b744daefb5d..2579ffc17792 100644 --- a/tests/pos/i9202.scala +++ b/tests/pos/i9202.scala @@ -1,5 +1,5 @@ object i9202 { opaque type SomeUnrelatedOpaque = Int class A[T](val d: T) - extension Ex2SameNameToBeToImport on [T] (x: A[T]) { def value: T = x.d } + extension [T] (x: A[T]) { def value: T = x.d } } \ No newline at end of file diff --git a/tests/pos/implicit-scope.scala b/tests/pos/implicit-scope.scala index 5e013de27cda..0c5bf358bf21 100644 --- a/tests/pos/implicit-scope.scala +++ b/tests/pos/implicit-scope.scala @@ -9,7 +9,7 @@ object A { type FlagSet = opaques.FlagSet def FlagSet(bits: Long): FlagSet = opaques.FlagSet(bits) - extension on (xs: FlagSet) { + extension (xs: FlagSet) { def bits: Long = opaques.toBits(xs) def | (ys: FlagSet): FlagSet = FlagSet(xs.bits | ys.bits) } diff --git a/tests/pos/indent.scala b/tests/pos/indent.scala index 21016a1ab864..da97b9abd7f1 100644 --- a/tests/pos/indent.scala +++ b/tests/pos/indent.scala @@ -28,7 +28,7 @@ object Test: 1 else 2 - def (x: Int).gt(y: Int) = x > y + extension (x: Int) def gt(y: Int) = x > y val y3 = if (1) max 10 gt 0 diff --git a/tests/pos/matrixOps.scala b/tests/pos/matrixOps.scala index 28ea3c4d2013..ab96a09389a5 100644 --- a/tests/pos/matrixOps.scala +++ b/tests/pos/matrixOps.scala @@ -3,7 +3,7 @@ object Test: type Matrix = Array[Array[Double]] type Vector = Array[Double] - extension on (m: Matrix): + extension (m: Matrix) def nRows = m.length def nCols = m(0).length def row(i: Int): Vector = m(i) diff --git a/tests/pos/mirror-implicit-scope.scala b/tests/pos/mirror-implicit-scope.scala index dddf6da3123e..392675688b77 100644 --- a/tests/pos/mirror-implicit-scope.scala +++ b/tests/pos/mirror-implicit-scope.scala @@ -3,14 +3,14 @@ import scala.deriving._ object Test { object K0 { type Generic[T] = Mirror { type Scope = K0.type ; type MirroredType = T ; type MirroredElemTypes } - extension on [T <: Product](gen: Generic[T]) { + extension [T <: Product](gen: Generic[T]) { inline def toRepr (t: T): gen.MirroredElemTypes = Tuple.fromProduct(t).asInstanceOf } } object K1 { type Generic[F[_]] = Mirror { type Scope = K1.type ; type MirroredType = [X] =>> F[X] ; type MirroredElemTypes[_] } - extension on [F[_] <: Product, T](gen: Generic[F]) { + extension [F[_] <: Product, T](gen: Generic[F]) { inline def toRepr (t: F[T]): gen.MirroredElemTypes[T] = Tuple.fromProduct(t).asInstanceOf } } diff --git a/tests/pos/opaque-propability-xm.scala b/tests/pos/opaque-propability-xm.scala index 28b431e74a43..f77821fa0f3f 100644 --- a/tests/pos/opaque-propability-xm.scala +++ b/tests/pos/opaque-propability-xm.scala @@ -19,17 +19,17 @@ object prob { implicit val ordering: Ordering[Probability] = implicitly[Ordering[Double]] - def (p1: Probability).unary_~ : Probability = Certain - p1 - def (p1: Probability) & (p2: Probability): Probability = p1 * p2 - def (p1: Probability) | (p2: Probability): Probability = p1 + p2 - (p1 * p2) + extension (p1: Probability) def unary_~ : Probability = Certain - p1 + extension (p1: Probability) def & (p2: Probability): Probability = p1 * p2 + extension (p1: Probability) def | (p2: Probability): Probability = p1 + p2 - (p1 * p2) - def (p1: Probability).isImpossible: Boolean = p1 == Never - def (p1: Probability).isCertain: Boolean = p1 == Certain + extension (p1: Probability) def isImpossible: Boolean = p1 == Never + extension (p1: Probability) def isCertain: Boolean = p1 == Certain import scala.util.Random - def (p1: Probability).sample(r: Random = Random): Boolean = r.nextDouble <= p1 - def (p1: Probability).toDouble: Double = p1 + extension (p1: Probability) def sample(r: Random = Random): Boolean = r.nextDouble <= p1 + extension (p1: Probability) def toDouble: Double = p1 } val caughtTrain = Probability.unsafe(0.3) diff --git a/tests/pos/opaque-xm.scala b/tests/pos/opaque-xm.scala index a8619c287f58..de2210d1ae3d 100644 --- a/tests/pos/opaque-xm.scala +++ b/tests/pos/opaque-xm.scala @@ -16,9 +16,9 @@ object opaquetypes { // Extension methods define opaque types' public APIs // This is the second way to unlift the logarithm type - def (x: Logarithm).toDouble: Double = math.exp(x) - def (x: Logarithm) + (y: Logarithm) = Logarithm(math.exp(x) + math.exp(y)) - def (x: Logarithm) * (y: Logarithm): Logarithm = Logarithm(x + y) + extension (x: Logarithm) def toDouble: Double = math.exp(x) + extension (x: Logarithm) def + (y: Logarithm) = Logarithm(math.exp(x) + math.exp(y)) + extension (x: Logarithm) def * (y: Logarithm): Logarithm = Logarithm(x + y) } } object usesites { @@ -30,6 +30,6 @@ object usesites { // as a contextual implicit this takes precedence over the // implicit scope implicit LogarithmOps. // TODO: Remove any2stringadd - val d = Logarithm.toDouble(l3) + val d = Logarithm.extension_toDouble(l3) val l5: Logarithm = (1.0).asInstanceOf[Logarithm] } diff --git a/tests/pos/postconditions.scala b/tests/pos/postconditions.scala index ce0611d558c2..90117c17b207 100644 --- a/tests/pos/postconditions.scala +++ b/tests/pos/postconditions.scala @@ -3,7 +3,7 @@ object PostConditions: def result[T](using r: WrappedResult[T]): T = r - def [T](x: T) ensuring (condition: WrappedResult[T] ?=> Boolean): T = + extension [T](x: T) def ensuring (condition: WrappedResult[T] ?=> Boolean): T = given WrappedResult[T] = x assert(condition) x diff --git a/tests/pos/reference/delegates.scala b/tests/pos/reference/delegates.scala index c624afecfcee..ea7c7f5d8ec1 100644 --- a/tests/pos/reference/delegates.scala +++ b/tests/pos/reference/delegates.scala @@ -1,25 +1,25 @@ class Common: trait Ord[T]: - def (x: T).compareTo(y: T): Int - def (x: T) < (y: T) = x.compareTo(y) < 0 - def (x: T) > (y: T) = x.compareTo(y) > 0 + extension (x: T) def compareTo(y: T): Int + extension (x: T) def < (y: T) = x.compareTo(y) < 0 + extension (x: T) def > (y: T) = x.compareTo(y) > 0 trait Convertible[From, To]: - def (x: From).convert: To + extension (x: From) def convert: To trait SemiGroup[T]: - def (x: T).combine(y: T): T + extension (x: T) def combine(y: T): T trait Monoid[T] extends SemiGroup[T]: def unit: T trait Functor[F[_]]: - def [A, B](x: F[A]) map (f: A => B): F[B] + extension [A, B](x: F[A]) def map (f: A => B): F[B] trait Monad[F[_]] extends Functor[F]: - def [A, B](x: F[A]) flatMap (f: A => F[B]): F[B] - def [A, B](x: F[A]) map (f: A => B) = x.flatMap(f `andThen` pure) + extension [A, B](x: F[A]) def flatMap (f: A => F[B]): F[B] + extension [A, B](x: F[A]) def map (f: A => B) = x.flatMap(f `andThen` pure) def pure[A](x: A): F[A] end Common @@ -27,11 +27,11 @@ end Common object Instances extends Common: given intOrd as Ord[Int]: - def (x: Int).compareTo(y: Int) = + extension (x: Int) def compareTo(y: Int) = if (x < y) -1 else if (x > y) +1 else 0 given listOrd[T](using Ord[T]) as Ord[List[T]]: - def (xs: List[T]).compareTo(ys: List[T]): Int = (xs, ys) match + extension (xs: List[T]) def compareTo(ys: List[T]): Int = (xs, ys) match case (Nil, Nil) => 0 case (Nil, _) => -1 case (_, Nil) => +1 @@ -40,23 +40,23 @@ object Instances extends Common: if (fst != 0) fst else xs1.compareTo(ys1) end listOrd - extension stringOps on (xs: Seq[String]): + extension (xs: Seq[String]) def longestStrings: Seq[String] = val maxLength = xs.map(_.length).max xs.filter(_.length == maxLength) - extension on [T](xs: List[T]): + extension [T](xs: List[T]) def second = xs.tail.head def third = xs.tail.tail.head given listMonad as Monad[List]: - def [A, B](xs: List[A]) flatMap (f: A => List[B]): List[B] = + extension [A, B](xs: List[A]) def flatMap (f: A => List[B]): List[B] = xs.flatMap(f) def pure[A](x: A): List[A] = List(x) given readerMonad[Ctx] as Monad[[X] =>> Ctx => X]: - def [A, B](r: Ctx => A) flatMap (f: A => Ctx => B): Ctx => B = + extension [A, B](r: Ctx => A) def flatMap (f: A => Ctx => B): Ctx => B = ctx => f(r(ctx))(ctx) def pure[A](x: A): Ctx => A = ctx => x @@ -65,7 +65,7 @@ object Instances extends Common: xs.reduceLeft((x, y) => if (x < y) y else x) def descending[T](using asc: Ord[T]): Ord[T] = new Ord[T]: - def (x: T).compareTo(y: T) = asc.compareTo(y)(x) + extension (x: T) def compareTo(y: T) = asc.extension_compareTo(y)(x) def minimum[T](xs: List[T])(using Ord[T]) = maximum(xs)(using descending) @@ -88,14 +88,14 @@ object Instances extends Common: trait TastyAPI: type Symbol trait SymDeco: - def (sym: Symbol).name: String + extension (sym: Symbol) def name: String def symDeco: SymDeco given SymDeco = symDeco object TastyImpl extends TastyAPI: type Symbol = String val symDeco = new SymDeco: - def (sym: Symbol).name = sym + extension (sym: Symbol) def name = sym class D[T] @@ -134,7 +134,7 @@ object PostConditions: def result[T](using x: WrappedResult[T]): T = x - extension on [T](x: T): + extension [T](x: T) def ensuring(condition: WrappedResult[T] ?=> Boolean): T = assert(condition(using x)) x @@ -142,11 +142,11 @@ end PostConditions object AnonymousInstances extends Common: given Ord[Int]: - def (x: Int).compareTo(y: Int) = + extension (x: Int) def compareTo(y: Int) = if (x < y) -1 else if (x > y) +1 else 0 given [T: Ord] as Ord[List[T]]: - def (xs: List[T]).compareTo(ys: List[T]): Int = (xs, ys).match + extension (xs: List[T]) def compareTo(ys: List[T]): Int = (xs, ys).match case (Nil, Nil) => 0 case (Nil, _) => -1 case (_, Nil) => +1 @@ -154,19 +154,19 @@ object AnonymousInstances extends Common: val fst = x.compareTo(y) if (fst != 0) fst else xs1.compareTo(ys1) - extension on (xs: Seq[String]): + extension (xs: Seq[String]) def longestStrings: Seq[String] = val maxLength = xs.map(_.length).max xs.filter(_.length == maxLength) - extension on [T](xs: List[T]): + extension [T](xs: List[T]) def second = xs.tail.head given [From, To](using c: Convertible[From, To]) as Convertible[List[From], List[To]]: - def (x: List[From]).convert: List[To] = x.map(c.convert) + extension (x: List[From]) def convert: List[To] = x.map(c.extension_convert) given Monoid[String]: - def (x: String).combine(y: String): String = x.concat(y) + extension (x: String) def combine(y: String): String = x.concat(y) def unit: String = "" def sum[T: Monoid](xs: List[T]): T = @@ -175,11 +175,11 @@ end AnonymousInstances object Implicits extends Common: implicit object IntOrd extends Ord[Int]: - def (x: Int).compareTo(y: Int) = + extension (x: Int) def compareTo(y: Int) = if (x < y) -1 else if (x > y) +1 else 0 class ListOrd[T: Ord] extends Ord[List[T]]: - def (xs: List[T]).compareTo(ys: List[T]): Int = (xs, ys).match + extension (xs: List[T]) def compareTo(ys: List[T]): Int = (xs, ys).match case (Nil, Nil) => 0 case (Nil, _) => -1 case (_, Nil) => +1 @@ -190,7 +190,7 @@ object Implicits extends Common: class given_Convertible_List_List[From, To](implicit c: Convertible[From, To]) extends Convertible[List[From], List[To]]: - def (x: List[From]).convert: List[To] = x.map(c.convert) + extension (x: List[From]) def convert: List[To] = x.map(c.extension_convert) implicit def given_Convertible_List_List[From, To](implicit c: Convertible[From, To]) : Convertible[List[From], List[To]] = new given_Convertible_List_List[From, To] @@ -200,15 +200,14 @@ object Implicits extends Common: xs.reduceLeft((x, y) => if (x < y) y else x) def descending[T](implicit asc: Ord[T]): Ord[T] = new Ord[T]: - def (x: T).compareTo(y: T) = asc.compareTo(y)(x) + extension (x: T) def compareTo(y: T) = asc.extension_compareTo(y)(x) def minimum[T](xs: List[T])(implicit cmp: Ord[T]) = maximum(xs)(descending) object Test extends App: Instances.test() - import PostConditions.result - import PostConditions.{given _} + import PostConditions.{result, ensuring} val s = List(1, 2, 3).sum s.ensuring(result == 6) end Test diff --git a/tests/pos/reference/extension-methods.scala b/tests/pos/reference/extension-methods.scala index 78ba2233464f..b74815fbdf82 100644 --- a/tests/pos/reference/extension-methods.scala +++ b/tests/pos/reference/extension-methods.scala @@ -1,87 +1,123 @@ +import annotation.infix + object ExtMethods: case class Circle(x: Double, y: Double, radius: Double) - def (c: Circle).circumference: Double = c.radius * math.Pi * 2 + extension (c: Circle) + def circumference: Double = c.radius * math.Pi * 2 val circle = Circle(0, 0, 1) circle.circumference - assert(circle.circumference == circumference(circle)) + assert(circle.circumference == extension_circumference(circle)) - trait StringSeqOps { - def (xs: Seq[String]).longestStrings = { - val maxLength = xs.map(_.length).max - xs.filter(_.length == maxLength) - } - } - given ops1 as StringSeqOps + extension (x: String) def < (y: String) = x.compareTo(y) < 0 + extension [Elem](x: Elem) def #: (xs: Seq[Elem]) = x +: xs + extension (x: Number) @infix def min (y: Number) = x - List("here", "is", "a", "list").longestStrings + assert("a" < "bb") + val xs = 1 #: Vector(2, 3) + val n = java.lang.Integer(2) min java.lang.Double(3.0) - locally { - object ops2 extends StringSeqOps - import ops2.longestStrings - List("here", "is", "a", "list").longestStrings - } + extension [T](xs: List[T]) + def second = xs.tail.head - def (x: String) < (y: String) = x.compareTo(y) < 0 - def [Elem](x: Elem) #: (xs: Seq[Elem]) = x +: xs + assert(List(1, 2, 3).second[Int] == List(1, 2, 3).second) - assert("a" < "bb") - val xs = 1 #: Vector(2, 3) + extension [T: Numeric](x: T) + def + (y: T): T = summon[Numeric[T]].plus(x, y) - def [T](xs: List[T]) second = - xs.tail.head + extension [T](x: T)(using n: Numeric[T]) + def - (y: T): T = n.minus(x, y) - def [T](xs: List[List[T]]) flattened = - xs.foldLeft[List[T]](Nil)(_ ++ _) + extension (ss: Seq[String]) - def [T: Numeric](x: T) + (y: T): T = - summon[Numeric[T]].plus(x, y) + def longestStrings: Seq[String] = + val maxLength = ss.map(_.length).max + ss.filter(_.length == maxLength) - List(1, 2, 3).second[Int] + def longestString: String = longestStrings.head - extension stringOps on (xs: Seq[String]) { - def longestStrings: Seq[String] = { - val maxLength = xs.map(_.length).max - xs.filter(_.length == maxLength) - } - } + import math.Ordered.orderingToOrdered - extension listOps on [T](xs: List[T]): - def second = xs.tail.head - def third: T = xs.tail.tail.head - - - extension on [T](xs: List[T])(using Ordering[T]): - def largest(n: Int) = xs.sorted.takeRight(n) - - extension ops: - def (xs: Seq[String]).longestStrings: Seq[String] = - val maxLength = xs.map(_.length).max - xs.filter(_.length == maxLength) - def (xs: Seq[String]).longestString: String = xs.longestStrings.head - def [T](xs: List[T]).second: T = xs.tail.head - - extension: - def [T](xs: List[T]) longest (using Ordering[T])(n: Int) = - xs.sorted.takeRight(n) - - given stringOps2 as AnyRef { - def (xs: Seq[String]).longestStrings: Seq[String] = { - val maxLength = xs.map(_.length).max - xs.filter(_.length == maxLength) - } - } - - given listOps2 as AnyRef { - def [T](xs: List[T]) second = xs.tail.head - def [T](xs: List[T]) third: T = xs.tail.tail.head - } - - given AnyRef { - def [T](xs: List[T]) largest (using Ordering[T])(n: Int) = - xs.sorted.takeRight(n) - } + extension [T](xs: List[T])(using Ordering[T]) + def smallest(n: Int): List[T] = xs.sorted.take(n) + def smallestIndices(n: Int): List[Int] = + val limit = smallest(n).max + xs.zipWithIndex.collect { case (x, i) if x <= limit => i } + + trait IntOps: + extension (i: Int) def isZero: Boolean = i == 0 + + extension (i: Int) def safeMod(x: Int): Option[Int] = + // extension method defined in same scope IntOps + if x.isZero then None + else Some(i % x) + end IntOps + + object IntOpsEx extends IntOps: + extension (i: Int) def safeDiv(x: Int): Option[Int] = + // extension method brought into scope via inheritance from IntOps + if x.isZero then None + else Some(i / x) + + trait SafeDiv: + import IntOpsEx._ // brings safeDiv and safeMod into scope + + extension (i: Int) def divide(d: Int) : Option[(Int, Int)] = + // extension methods imported and thus in scope + (i.safeDiv(d), i.safeMod(d)) match + case (Some(d), Some(r)) => Some((d, r)) + case _ => None + end SafeDiv + + def test1 = + given ops1 as IntOps // brings safeMod into scope + 1.safeMod(2) + + class Lst[T](xs: T*): + private val elems = xs.toList + def foldLeft[U](x: U)(op: (U, T) => U): U = elems.foldLeft(x)(op) + def ++ (other: Lst[T]): Lst[T] = Lst(elems ++ other.elems: _*) + + trait Ord[T]: + extension (x: T) def less (y: T): Boolean + object Ord: + given Ord[Int]: + extension (x: Int) def less (y: Int): Boolean = x < y + end Ord + + object Lst: + + extension [T](xs: Lst[Lst[T]]) + def flatten: Lst[T] = xs.foldLeft(Lst())(_ ++ _) + + given ord[T: Ord] as Ord[Lst[T]]: + extension (xs: Lst[T]) + def less (ys: Lst[T]): Boolean = ??? + end Lst + + def test2 = + val xss = Lst(Lst(1, 2), Lst(3, 4)) + val xs: Lst[Int] = xss.flatten + + summon[Ord[Lst[Lst[Int]]]] + + assert(Lst.ord[Lst[Int]].extension_less(xss)(Lst(Lst(3)))) + assert(xss `less` Lst(Lst(3))) + assert(xss.flatten `less` Lst(3)) + + extension (s: String) + def position(ch: Char, n: Int): Int = + if n < s.length && s(n) != ch then position(ch, n + 1) + else n + + object DoubleOps: + extension (x: Double) def ** (exponent: Int): Double = + require(exponent > 0) + if exponent == 0 then 1 else x * (x ** (exponent - 1)) + + import DoubleOps.{**, extension_**} + assert(2.0 ** 3 == extension_**(2.0)(3)) end ExtMethods \ No newline at end of file diff --git a/tests/pos/reference/opaque.scala b/tests/pos/reference/opaque.scala index 69ce019b6aee..cf1271d01382 100644 --- a/tests/pos/reference/opaque.scala +++ b/tests/pos/reference/opaque.scala @@ -12,7 +12,7 @@ object Logarithms { } // Extension methods define opaque types' public APIs - extension on (x: Logarithm) { + extension (x: Logarithm) { def toDouble: Double = math.exp(x) def + (y: Logarithm): Logarithm = Logarithm(math.exp(x) + math.exp(y)) def * (y: Logarithm): Logarithm = Logarithm(x + y) @@ -36,10 +36,10 @@ object Access { opaque type PermissionChoice = Int opaque type Permission <: Permissions & PermissionChoice = Int - def (x: Permissions) & (y: Permissions): Permissions = x & y - def (x: PermissionChoice) | (y: PermissionChoice): PermissionChoice = x | y - def (x: Permissions).is(y: Permissions) = (x & y) == y - def (x: Permissions).isOneOf(y: PermissionChoice) = (x & y) != 0 + extension (x: Permissions) def & (y: Permissions): Permissions = x & y + extension (x: PermissionChoice) def | (y: PermissionChoice): PermissionChoice = x | y + extension (x: Permissions) def is(y: Permissions) = (x & y) == y + extension (x: Permissions) def isOneOf(y: PermissionChoice) = (x & y) != 0 val NoPermission: Permission = 0 val ReadOnly: Permission = 1 diff --git a/tests/pos/tasty-reflect-opaque-api-proto.scala b/tests/pos/tasty-reflect-opaque-api-proto.scala index 563a43598e61..fe077fd5b15e 100644 --- a/tests/pos/tasty-reflect-opaque-api-proto.scala +++ b/tests/pos/tasty-reflect-opaque-api-proto.scala @@ -10,7 +10,7 @@ class Reflect(val internal: CompilerInterface) { opaque type Term <: Tree = internal.Term object Tree { - extension ops on (tree: Tree) { + extension (tree: Tree) { def show: String = ??? } } diff --git a/tests/pos/toplevel-opaque-xm/Logarithm_1.scala b/tests/pos/toplevel-opaque-xm/Logarithm_1.scala index 884e29a1ad25..436097fee478 100644 --- a/tests/pos/toplevel-opaque-xm/Logarithm_1.scala +++ b/tests/pos/toplevel-opaque-xm/Logarithm_1.scala @@ -16,8 +16,8 @@ object Logarithm { given AnyRef { // This is the second way to unlift the logarithm type - def (x: Logarithm).toDouble: Double = math.exp(x) - def (x: Logarithm) + (y: Logarithm) = Logarithm(math.exp(x) + math.exp(y)) - def (x: Logarithm) * (y: Logarithm): Logarithm = Logarithm(x + y) + extension (x: Logarithm) def toDouble: Double = math.exp(x) + extension (x: Logarithm) def + (y: Logarithm) = Logarithm(math.exp(x) + math.exp(y)) + extension (x: Logarithm) def * (y: Logarithm): Logarithm = Logarithm(x + y) } } diff --git a/tests/pos/typeclass-encoding3.scala b/tests/pos/typeclass-encoding3.scala index 37a4dd2670a0..070c52f4af4f 100644 --- a/tests/pos/typeclass-encoding3.scala +++ b/tests/pos/typeclass-encoding3.scala @@ -324,18 +324,18 @@ object Test { case class Circle(radius: Double) implicit object RectangleArea - def (x: Rectangle).area: Double = x.width * self.height + extension (x: Rectangle) def area: Double = x.width * self.height trait HasArea[T] - def (x: T).area: Double + extension (x: T) def area: Double implicit object CircleHasArea extends HasArea[Circle] - def (x: Circle).area = x.radius * 2 * math.Pi + extension (x: Circle) def area = x.radius * 2 * math.Pi --------------------------------------------------------------------------------- trait SemiGroup[T] - def (x: T).combine(y: T): T + extension (x: T) def combine(y: T): T trait Monoid[T] extends SemiGroup[T] def unit: T @@ -348,23 +348,23 @@ object Test { // Class `Str` is not definable implicit object StringMonoid extends Monoid[String] - def (x: String).combine(y: String): String = x + y + extension (x: String) def combine(y: String): String = x + y def unit = "" trait Ord[T] - def (x: T).compareTo(y: T): Int - def (x: T) < (that: T) = x.compareTo(y) < 0 - def (x: T) > (that: T) = x.compareTo(y) > 0 + extension (x: T) def compareTo(y: T): Int + extension (x: T) def < (that: T) = x.compareTo(y) < 0 + extension (x: T) def > (that: T) = x.compareTo(y) > 0 val minimum: T } implicit object IntOrd { - def (x: Int).compareTo(y: Int) = + extension (x: Int) def compareTo(y: Int) = if (x < y) -1 else if (x > y) +1 else 0 } implicit class ListOrd[T: Ord] { - def (xs: List[T]).compareTo(ys: List[T]): Int = (xs, ys) match + extension (xs: List[T]) def compareTo(ys: List[T]): Int = (xs, ys) match case (Nil, Nil) => 0 case (Nil, _) => -1 case (_, Nil) => +1 @@ -381,19 +381,19 @@ object Test { --------------------------------------------------------------------------------- trait Functor[F[_]] - def (x: F[A]).map[A, B](f: A => B): F[B] + extension (x: F[A]) def map[A, B](f: A => B): F[B] trait Monad[F[_]] extends Functor[F] - def (x: F[A]).flatMap[A, B](f: A => F[B]): F[B] - def (x: F[A]).map[A, B](f: A => B) = x.flatMap(f `andThen` pure) + extension (x: F[A]) def flatMap[A, B](f: A => F[B]): F[B] + extension (x: F[A]) def map[A, B](f: A => B) = x.flatMap(f `andThen` pure) def pure[A]: F[A] implicit object ListMonad extends Monad[List] - def (xs: List[A]).flatMap[A, B](f: A => List[B]): List[B] = xs.flatMap(f) + extension (xs: List[A]) def flatMap[A, B](f: A => List[B]): List[B] = xs.flatMap(f) def pure[A]: List[A] = List.Nil implicit class ReaderMonad[Ctx] extends Monad[Ctx => _] - def (r: Ctx => A).flatMap[A, B](f: A => Ctx => B): Ctx => B = + extension (r: Ctx => A) def flatMap[A, B](f: A => Ctx => B): Ctx => B = ctx => f(r(ctx))(ctx) def pure[A](x: A): Ctx => A = ctx => x diff --git a/tests/printing/i620.check b/tests/printing/i620.check index 4563190dcf16..1dedb6d375a7 100644 --- a/tests/printing/i620.check +++ b/tests/printing/i620.check @@ -11,7 +11,7 @@ package O { protected[A] def f: Int = 0 def g: Int = 0 def g1(t: Int): Int = 0 - @_root_.scala.annotation.infix() def (c: D.this.C) g1: Int = 0 + extension (c: D.this.C) def g1: Int = 0 } private[D] class E() extends Object() {} private[this] class F() extends Object() {} diff --git a/tests/printing/i620.scala b/tests/printing/i620.scala index 21c9254f22d1..69c528c1e1e1 100644 --- a/tests/printing/i620.scala +++ b/tests/printing/i620.scala @@ -13,7 +13,7 @@ class D { protected[A] def f: Int = 0 def g: Int = 0 def g1(t: Int) = 0 - def (c: C) g1 = 0 + extension (c: C) def g1 = 0 } private[D] class E diff --git a/tests/run-macros/f-interpolator-neg/Macros_1.scala b/tests/run-macros/f-interpolator-neg/Macros_1.scala index 2c76709d6704..72d67301cb2d 100644 --- a/tests/run-macros/f-interpolator-neg/Macros_1.scala +++ b/tests/run-macros/f-interpolator-neg/Macros_1.scala @@ -6,7 +6,7 @@ import scala.language.implicitConversions object TestFooErrors { // Defined in tests implicit object StringContextOps { - inline def (inline ctx: StringContext).foo(inline args: Any*): List[(Boolean, Int, Int, Int, String)] = ${ Macro.fooErrors('ctx, 'args) } + extension (inline ctx: StringContext) inline def foo(inline args: Any*): List[(Boolean, Int, Int, Int, String)] = ${ Macro.fooErrors('ctx, 'args) } } } diff --git a/tests/run-macros/i6201/macro_1.scala b/tests/run-macros/i6201/macro_1.scala index ffbb1eb244b2..0fe7b233e51f 100644 --- a/tests/run-macros/i6201/macro_1.scala +++ b/tests/run-macros/i6201/macro_1.scala @@ -1,6 +1,6 @@ import scala.quoted._ -inline def (inline x: String) strip: String = +extension (inline x: String) inline def strip: String = ${ stripImpl('x) } def stripImpl(x: Expr[String])(using qctx: QuoteContext) : Expr[String] = diff --git a/tests/run-macros/i6201/test_2.scala b/tests/run-macros/i6201/test_2.scala index cb8c2053e673..86519b41eb3d 100644 --- a/tests/run-macros/i6201/test_2.scala +++ b/tests/run-macros/i6201/test_2.scala @@ -1,6 +1,6 @@ object Test { def main(args: Array[String]): Unit = { - assert(isHello(strip("hello"))) - assert(!isHello(strip("bonjour"))) + assert(isHello(extension_strip("hello"))) + assert(!isHello(extension_strip("bonjour"))) } } \ No newline at end of file diff --git a/tests/run-macros/i6253-b/quoted_1.scala b/tests/run-macros/i6253-b/quoted_1.scala index 593e7da59ae4..c6c4155f9516 100644 --- a/tests/run-macros/i6253-b/quoted_1.scala +++ b/tests/run-macros/i6253-b/quoted_1.scala @@ -3,7 +3,7 @@ import scala.quoted._ object Macros { - inline def (inline self: StringContext) xyz(args: => String*): String = ${impl('self, 'args)} + extension (inline self: StringContext) inline def xyz(args: => String*): String = ${impl('self, 'args)} private def impl(self: Expr[StringContext], args: Expr[Seq[String]])(using QuoteContext): Expr[String] = { self match { diff --git a/tests/run-macros/i6253-c/quoted_1.scala b/tests/run-macros/i6253-c/quoted_1.scala index c7a4a08e3256..a801b6b97ee2 100644 --- a/tests/run-macros/i6253-c/quoted_1.scala +++ b/tests/run-macros/i6253-c/quoted_1.scala @@ -3,8 +3,8 @@ import scala.quoted._ object Macros { - // Should be: inline def (inline self: StringContext) ... - inline def (self: => StringContext) xyz(args: => String*): String = ${impl('self, 'args)} + // Should be: extension (inline self: StringContext) inline def ... + extension (self: => StringContext) inline def xyz(args: => String*): String = ${impl('self, 'args)} private def impl(self: Expr[StringContext], args: Expr[Seq[String]])(using QuoteContext): Expr[String] = { self match { diff --git a/tests/run-macros/i6253/quoted_1.scala b/tests/run-macros/i6253/quoted_1.scala index 596db02e2fbc..3f36d0f69ab1 100644 --- a/tests/run-macros/i6253/quoted_1.scala +++ b/tests/run-macros/i6253/quoted_1.scala @@ -3,7 +3,7 @@ import scala.quoted._ object Macros { - inline def (inline self: StringContext) xyz(args: => String*): String = ${impl('self, 'args)} + extension (inline self: StringContext) inline def xyz(args: => String*): String = ${impl('self, 'args)} private def impl(self: Expr[StringContext], args: Expr[Seq[String]])(using QuoteContext): Expr[String] = { self match { diff --git a/tests/run-macros/i6270/Macro_1.scala b/tests/run-macros/i6270/Macro_1.scala index 40372b74107c..581dee3a9750 100644 --- a/tests/run-macros/i6270/Macro_1.scala +++ b/tests/run-macros/i6270/Macro_1.scala @@ -2,7 +2,7 @@ import scala.quoted._ import scala.quoted.show.SyntaxHighlight.ANSI object api { - inline def (inline x: String) reflect : String = + extension (inline x: String) inline def reflect : String = ${ reflImpl('x) } private def reflImpl(x: Expr[String])(using qctx: QuoteContext) : Expr[String] = { @@ -10,7 +10,7 @@ object api { Expr(x.show) } - inline def (x: => String) reflectColor : String = + extension (x: => String) inline def reflectColor : String = ${ reflImplColor('x) } private def reflImplColor(x: Expr[String])(using qctx: QuoteContext) : Expr[String] = { diff --git a/tests/run-macros/i6772/Macro_1.scala b/tests/run-macros/i6772/Macro_1.scala index 407b97ef87e6..cb8952618d43 100644 --- a/tests/run-macros/i6772/Macro_1.scala +++ b/tests/run-macros/i6772/Macro_1.scala @@ -7,7 +7,7 @@ object Macros { def mImpl()(using QuoteContext): Expr[Any] = List(Expr(1), Expr(2), Expr(3)).toExprOfList - def [T](list: List[Expr[T]]).toExprOfList(using Type[T], QuoteContext): Expr[List[T]] = '{ + extension [T](list: List[Expr[T]]) def toExprOfList(using Type[T], QuoteContext): Expr[List[T]] = '{ val buff = List.newBuilder[T] ${ Expr.block(list.map(v => '{ buff += $v }), '{ buff.result() }) } } diff --git a/tests/run-macros/i8007/Macro_3.scala b/tests/run-macros/i8007/Macro_3.scala index 7a01837eedca..a6e54a2bfc03 100644 --- a/tests/run-macros/i8007/Macro_3.scala +++ b/tests/run-macros/i8007/Macro_3.scala @@ -73,7 +73,7 @@ object Eq { } object Macro3 { - inline def [T](x: =>T) === (y: =>T)(using eq: Eq[T]): Boolean = eq.eqv(x, y) + extension [T](x: =>T) inline def === (y: =>T)(using eq: Eq[T]): Boolean = eq.eqv(x, y) implicit inline def eqGen[T]: Eq[T] = ${ Eq.derived[T] } } \ No newline at end of file diff --git a/tests/run-macros/quote-elide-prefix/quoted_1.scala b/tests/run-macros/quote-elide-prefix/quoted_1.scala index c7f86bccd281..cdad817e2a66 100644 --- a/tests/run-macros/quote-elide-prefix/quoted_1.scala +++ b/tests/run-macros/quote-elide-prefix/quoted_1.scala @@ -3,7 +3,7 @@ import scala.quoted._ object Macro { // By name StringContext is used to elide the prefix - inline def (inline sc: StringContext) ff (args: => Any*): String = ${ Macro.impl('sc, 'args) } + extension (inline sc: StringContext) inline def ff (args: => Any*): String = ${ Macro.impl('sc, 'args) } def impl(sc: Expr[StringContext], args: Expr[Seq[Any]])(using QuoteContext): Expr[String] = '{ $args.mkString } } diff --git a/tests/run-macros/quote-matcher-string-interpolator-2/quoted_1.scala b/tests/run-macros/quote-matcher-string-interpolator-2/quoted_1.scala index a6ff0769dc4e..f5acbfaca656 100644 --- a/tests/run-macros/quote-matcher-string-interpolator-2/quoted_1.scala +++ b/tests/run-macros/quote-matcher-string-interpolator-2/quoted_1.scala @@ -4,7 +4,7 @@ import scala.quoted._ object Macros { - inline def (inline self: StringContext) xyz(inline args: String*): String = ${impl('self, 'args)} + extension (inline self: StringContext) inline def xyz(inline args: String*): String = ${impl('self, 'args)} private def impl(self: Expr[StringContext], args: Expr[Seq[String]])(using QuoteContext): Expr[String] = { (self, args) match { diff --git a/tests/run-macros/quote-matcher-string-interpolator-3/quoted_1.scala b/tests/run-macros/quote-matcher-string-interpolator-3/quoted_1.scala index 8a0c5d182bc1..1e4f3d8d1b44 100644 --- a/tests/run-macros/quote-matcher-string-interpolator-3/quoted_1.scala +++ b/tests/run-macros/quote-matcher-string-interpolator-3/quoted_1.scala @@ -4,7 +4,7 @@ import scala.quoted._ object Macros { - inline def (inline self: StringContext) S(args: => String*): String = ${impl('self, 'args)} + extension (inline self: StringContext) inline def S(args: => String*): String = ${impl('self, 'args)} private def impl(self: Expr[StringContext], args: Expr[Seq[String]])(using QuoteContext): Expr[String] = { self match { diff --git a/tests/run-macros/quote-matcher-string-interpolator/quoted_1.scala b/tests/run-macros/quote-matcher-string-interpolator/quoted_1.scala index 128dd17a4679..385f1c00c26c 100644 --- a/tests/run-macros/quote-matcher-string-interpolator/quoted_1.scala +++ b/tests/run-macros/quote-matcher-string-interpolator/quoted_1.scala @@ -4,7 +4,7 @@ import scala.quoted._ object Macros { - inline def (inline self: StringContext) xyz(args: => String*): String = ${impl('self, 'args)} + extension (inline self: StringContext) inline def xyz(args: => String*): String = ${impl('self, 'args)} private def impl(self: Expr[StringContext], args: Expr[Seq[String]])(using QuoteContext): Expr[String] = { self match { diff --git a/tests/run-macros/reflect-inline/assert_1.scala b/tests/run-macros/reflect-inline/assert_1.scala index 54f4cc444863..a41e0bd7a81e 100644 --- a/tests/run-macros/reflect-inline/assert_1.scala +++ b/tests/run-macros/reflect-inline/assert_1.scala @@ -1,7 +1,7 @@ import scala.quoted._ object api { - inline def (inline x: String) stripMargin: String = + extension (inline x: String) inline def stripMargin: String = ${ stripImpl('x) } private def stripImpl(x: Expr[String])(using qctx: QuoteContext): Expr[String] = diff --git a/tests/run-macros/reflect-sourceCode/Macro_1.scala b/tests/run-macros/reflect-sourceCode/Macro_1.scala index 8be8239cae25..1dbcc0cfa6d5 100644 --- a/tests/run-macros/reflect-sourceCode/Macro_1.scala +++ b/tests/run-macros/reflect-sourceCode/Macro_1.scala @@ -1,7 +1,7 @@ import scala.quoted._ object api { - inline def [T](x: => T).reflect: String = + extension [T](x: => T) inline def reflect: String = ${ reflImpl('x) } private def reflImpl[T](x: Expr[T])(implicit qctx: QuoteContext): Expr[String] = { diff --git a/tests/run-macros/string-context-implicits/Macro_1.scala b/tests/run-macros/string-context-implicits/Macro_1.scala index 976cb948b542..877686ba2d9e 100644 --- a/tests/run-macros/string-context-implicits/Macro_1.scala +++ b/tests/run-macros/string-context-implicits/Macro_1.scala @@ -1,7 +1,7 @@ import scala.quoted._ -inline def (sc: StringContext) showMe(inline args: Any*): String = ${ showMeExpr('sc, 'args) } +extension (sc: StringContext) inline def showMe(inline args: Any*): String = ${ showMeExpr('sc, 'args) } private def showMeExpr(sc: Expr[StringContext], argsExpr: Expr[Seq[Any]])(using qctx: QuoteContext): Expr[String] = { argsExpr match { diff --git a/tests/run-macros/tasty-string-interpolation-reporter-test/Macros_1.scala b/tests/run-macros/tasty-string-interpolation-reporter-test/Macros_1.scala index 5416ba8e8518..ac80e197c724 100644 --- a/tests/run-macros/tasty-string-interpolation-reporter-test/Macros_1.scala +++ b/tests/run-macros/tasty-string-interpolation-reporter-test/Macros_1.scala @@ -6,14 +6,14 @@ import scala.language.implicitConversions object Foo { implicit object StringContextOps { - inline def (inline ctx: StringContext) foo (inline args: Any*): String = ${ Macro.foo('ctx, 'args) } + extension (inline ctx: StringContext) inline def foo (inline args: Any*): String = ${ Macro.foo('ctx, 'args) } } } object TestFooErrors { // Defined in tests implicit object StringContextOps { - inline def (inline ctx: StringContext) foo (inline args: Any*): List[(Int, Int, Int, String)] = ${ Macro.fooErrors('ctx, 'args) } + extension (inline ctx: StringContext) inline def foo (inline args: Any*): List[(Int, Int, Int, String)] = ${ Macro.fooErrors('ctx, 'args) } } } diff --git a/tests/run-macros/xml-interpolation-3/XmlQuote_1.scala b/tests/run-macros/xml-interpolation-3/XmlQuote_1.scala index 6f6eb5b058d7..d9b312665913 100644 --- a/tests/run-macros/xml-interpolation-3/XmlQuote_1.scala +++ b/tests/run-macros/xml-interpolation-3/XmlQuote_1.scala @@ -8,7 +8,7 @@ case class Xml(parts: String, args: List[Any]) object XmlQuote { implicit object SCOps { - inline def (inline ctx: StringContext) xml (args: => Any*): Xml = + extension (inline ctx: StringContext) inline def xml (args: => Any*): Xml = ${XmlQuote.impl('ctx, 'args)} } diff --git a/tests/run-macros/xml-interpolation-4/Macros_1.scala b/tests/run-macros/xml-interpolation-4/Macros_1.scala index e5afa0c4c517..07772047356b 100644 --- a/tests/run-macros/xml-interpolation-4/Macros_1.scala +++ b/tests/run-macros/xml-interpolation-4/Macros_1.scala @@ -6,7 +6,7 @@ import scala.language.implicitConversions object XmlQuote { implicit object SCOps { - inline def (ctx: => StringContext) xml (args: => (Scope ?=> Any)*)(using Scope): String = + extension (ctx: => StringContext) inline def xml (args: => (Scope ?=> Any)*)(using Scope): String = ${XmlQuote.impl('ctx, 'args, '{implicitly[Scope]})} } diff --git a/tests/run-macros/xml-interpolation-5/Macros_1.scala b/tests/run-macros/xml-interpolation-5/Macros_1.scala index 887ec2007f82..e0a948a75135 100644 --- a/tests/run-macros/xml-interpolation-5/Macros_1.scala +++ b/tests/run-macros/xml-interpolation-5/Macros_1.scala @@ -19,10 +19,10 @@ object XmlQuote { opaque type StringContext = scala.StringContext def apply(sc: scala.StringContext): StringContext = sc } - transparent inline def (inline ctx: StringContext).xml: SCOps.StringContext = SCOps(ctx) - inline def (inline ctx: SCOps.StringContext).apply(inline args: Any*): Xml = + extension (inline ctx: StringContext) transparent inline def xml: SCOps.StringContext = SCOps(ctx) + extension (inline ctx: SCOps.StringContext) inline def apply(inline args: Any*): Xml = ${XmlQuote.impl('ctx, 'args)} - // inline def (inline ctx: SCOps.StringContext).unapplySeq(...): Xml = ... + // extension (inline ctx: SCOps.StringContext) inline def unapplySeq(...): Xml = ... def impl(receiver: Expr[SCOps.StringContext], args: Expr[Seq[Any]])(using QuoteContext): Expr[Xml] = { diff --git a/tests/run-macros/xml-interpolation-6/Macros_1.scala b/tests/run-macros/xml-interpolation-6/Macros_1.scala index b6913f3b8ce1..565857c3b6fb 100644 --- a/tests/run-macros/xml-interpolation-6/Macros_1.scala +++ b/tests/run-macros/xml-interpolation-6/Macros_1.scala @@ -19,10 +19,10 @@ object XmlQuote { opaque type StringContext = scala.StringContext def apply(sc: scala.StringContext): StringContext = sc } - inline def (inline ctx: StringContext).xml: SCOps.StringContext = SCOps(ctx) - inline def (inline ctx: SCOps.StringContext).apply(inline args: Any*): Xml = + extension (inline ctx: StringContext) inline def xml: SCOps.StringContext = SCOps(ctx) + extension (inline ctx: SCOps.StringContext) inline def apply(inline args: Any*): Xml = ${XmlQuote.impl('ctx, 'args)} - // inline def (inline ctx: SCOps.StringContext).unapplySeq(...): Xml = ... + // extension (inline ctx: SCOps.StringContext) inline def unapplySeq(...): Xml = ... def impl(receiver: Expr[SCOps.StringContext], args: Expr[Seq[Any]])(using QuoteContext): Expr[Xml] = { diff --git a/tests/run-macros/xml-interpolation-7/Macros_1.scala b/tests/run-macros/xml-interpolation-7/Macros_1.scala index 1f753c7e47ee..5977d5c4e498 100644 --- a/tests/run-macros/xml-interpolation-7/Macros_1.scala +++ b/tests/run-macros/xml-interpolation-7/Macros_1.scala @@ -17,17 +17,17 @@ object XmlQuote { // } object XMLOps { opaque type StringContext = scala.StringContext - def (ctx: scala.StringContext).xml: StringContext = ctx + extension (ctx: scala.StringContext) def xml: StringContext = ctx } - inline def (inline ctx: XMLOps.StringContext).apply(inline args: Any*): Xml = + extension (inline ctx: XMLOps.StringContext) inline def apply(inline args: Any*): Xml = ${XmlQuote.impl('ctx, 'args)} - // inline def (inline ctx: SCOps.StringContext).unapplySeq(...): Xml = ... + // extension (inline ctx: SCOps.StringContext) inline def unapplySeq(...): Xml = ... def impl(receiver: Expr[XMLOps.StringContext], args: Expr[Seq[Any]])(using QuoteContext): Expr[Xml] = { val string = receiver match { - case '{ XMLOps.xml(${Unlifted(sc)}) } => sc.parts.mkString("??") + case '{ XMLOps.extension_xml(${Unlifted(sc)}) } => sc.parts.mkString("??") } '{new Xml(${string}, $args.toList)} } diff --git a/tests/run/LazyLists.scala b/tests/run/LazyLists.scala index c13949ce8454..6cf8c8cf8e50 100644 --- a/tests/run/LazyLists.scala +++ b/tests/run/LazyLists.scala @@ -55,7 +55,7 @@ package xcollections: if it.hasNext then set(it.next, fromIterator (it)) else empty - extension on [T, U >: T](xs: LazyList[T]): + extension [T, U >: T](xs: LazyList[T]) def #::(x: U): LazyList[U] = new: protected def force(): LazyList[U] = set(x, xs) @@ -65,7 +65,7 @@ package xcollections: if xs.isEmpty then ys.forced() else set(xs.head, xs.tail ++ ys) - extension on [T, U](xs: LazyList[T]): + extension [T, U](xs: LazyList[T]) def map(f: T => U): LazyList[U] = new: protected def force() = if xs.isEmpty then empty @@ -80,7 +80,7 @@ package xcollections: if xs.isEmpty then z else xs.tail.foldLeft(f(z, xs.head))(f) - extension on [T](xs: LazyList[T]): + extension [T](xs: LazyList[T]) def filter(p: T => Boolean): LazyList[T] = new: protected def force(): LazyList[T] = if xs.isEmpty then empty diff --git a/tests/run/Pouring.scala b/tests/run/Pouring.scala index 9097901f97de..6f4611af8bfc 100644 --- a/tests/run/Pouring.scala +++ b/tests/run/Pouring.scala @@ -8,7 +8,7 @@ class Pouring(capacity: Vector[Int]): case Fill(g) => content.updated(g, capacity(g)) case Pour(from, to) => val amount = content(from) min (capacity(to) - content(to)) - def (s: Content).adjust(g: Glass, delta: Int) = s.updated(g, s(g) + delta) + extension (s: Content) def adjust(g: Glass, delta: Int) = s.updated(g, s(g) + delta) content.adjust(from, -amount).adjust(to, amount) case Empty(glass: Glass) diff --git a/tests/run/collective-extensions.scala b/tests/run/collective-extensions.scala index 9c8c2d4de04d..c76dda6d2a1b 100644 --- a/tests/run/collective-extensions.scala +++ b/tests/run/collective-extensions.scala @@ -1,10 +1,10 @@ -extension on (x: String): +extension (x: String) def foo(y: String): String = x ++ y def bar(y: String): String = foo(y) def baz(y: String): String = val x = y bar(x) - def bam(y: String): String = this.baz(x)(y) + def bam(y: String): String = this.extension_baz(x)(y) def ban(foo: String): String = x + foo def bao(y: String): String = val bam = "ABC" diff --git a/tests/run/eq-xmethod.scala b/tests/run/eq-xmethod.scala index 5231063bf62b..86805092e41a 100644 --- a/tests/run/eq-xmethod.scala +++ b/tests/run/eq-xmethod.scala @@ -6,7 +6,7 @@ object Test extends App { class N object N extends N - def (x: N)._eq(y: R | N) = y eq N + extension (x: N) def _eq(y: R | N) = y eq N val r1, r2 = new R assert(r1 _eq r1) diff --git a/tests/run/exports.scala b/tests/run/exports.scala index c796f330b9c7..c82143192152 100644 --- a/tests/run/exports.scala +++ b/tests/run/exports.scala @@ -12,7 +12,7 @@ object Test extends App { class Scanner { def scan() = println("scanning") - def (x: Any).scanned = scan() + extension (x: Any) def scanned = scan() } object Scanner extends Scanner diff --git a/tests/run/extension-methods.scala b/tests/run/extension-methods.scala index ae5a9c4c2d2c..649c2c6aea93 100644 --- a/tests/run/extension-methods.scala +++ b/tests/run/extension-methods.scala @@ -1,35 +1,36 @@ object Test extends App { - def (x: Int).em: Boolean = x > 0 + extension (x: Int) def em: Boolean = x > 0 - assert(1.em == em(1)) + assert(1.em == extension_em(1)) case class Circle(x: Double, y: Double, radius: Double) - def (c: Circle).circumference: Double = c.radius * math.Pi * 2 + extension (c: Circle) def circumference: Double = c.radius * math.Pi * 2 val circle = new Circle(1, 1, 2.0) - assert(circle.circumference == circumference(circle)) + assert(circle.circumference == extension_circumference(circle)) - def (xs: Seq[String]).longestStrings: Seq[String] = { + extension (xs: Seq[String]) def longestStrings: Seq[String] = { val maxLength = xs.map(_.length).max xs.filter(_.length == maxLength) } val names = List("hi", "hello", "world") assert(names.longestStrings == List("hello", "world")) - def [T](xs: Seq[T]) second = xs.tail.head + extension [T](xs: Seq[T]) def second = xs.tail.head assert(names.longestStrings.second == "world") - def [T](xs: List[List[T]]) flattened = xs.foldLeft[List[T]](Nil)(_ ++ _) + extension [T](xs: List[List[T]]) + def flattened = xs.foldLeft[List[T]](Nil)(_ ++ _) assert(List(names, List("!")).flattened == names :+ "!") assert(Nil.flattened == Nil) trait SemiGroup[T] { - def (x: T).combine(y: T): T + extension (x: T) def combine(y: T): T } trait Monoid[T] extends SemiGroup[T] { def unit: T @@ -37,7 +38,7 @@ object Test extends App { // An instance declaration: implicit object StringMonoid extends Monoid[String] { - def (x: String).combine(y: String): String = x.concat(y) + extension (x: String) def combine(y: String): String = x.concat(y) def unit: String = "" } @@ -48,14 +49,14 @@ object Test extends App { println(sum(names)) trait Ord[T] { - def (x: T).compareTo(y: T): Int - def (x: T) < (y: T) = x.compareTo(y) < 0 - def (x: T) > (y: T) = x.compareTo(y) > 0 + extension (x: T) def compareTo(y: T): Int + extension (x: T) def < (y: T) = x.compareTo(y) < 0 + extension (x: T) def > (y: T) = x.compareTo(y) > 0 val minimum: T } implicit object IntOrd extends Ord[Int] { - def (x: Int).compareTo(y: Int) = + extension (x: Int) def compareTo(y: Int) = if (x < y) -1 else if (x > y) +1 else 0 val minimum = Int.MinValue } @@ -84,7 +85,7 @@ object Test extends App { println(max(List(1, 2, 3), List(2))) trait Functor[F[_]] { - def [A, B](x: F[A]) map (f: A => B): F[B] + extension [A, B](x: F[A]) def map (f: A => B): F[B] } trait Monad[F[_]] extends Functor[F] { @@ -98,14 +99,14 @@ object Test extends App { } implicit object ListMonad extends Monad[List] { - def [A, B](xs: List[A]) flatMap (f: A => List[B]): List[B] = + extension [A, B](xs: List[A]) def flatMap (f: A => List[B]): List[B] = xs.flatMap(f) def pure[A](x: A): List[A] = List(x) } class ReaderMonad[Ctx] extends Monad[[X] =>> Ctx => X] { - def [A, B](r: Ctx => A) flatMap (f: A => Ctx => B): Ctx => B = + extension [A, B](r: Ctx => A) def flatMap (f: A => Ctx => B): Ctx => B = ctx => f(r(ctx))(ctx) def pure[A](x: A): Ctx => A = ctx => x @@ -114,7 +115,7 @@ object Test extends App { def mappAll[F[_]: Monad, T](x: T, fs: List[T => T]): F[T] = fs.foldLeft(implicitly[Monad[F]].pure(x))((x: F[T], f: T => T) => - if (true) implicitly[Monad[F]].map(x)(f) + if (true) implicitly[Monad[F]].extension_map(x)(f) else if (true) x.map(f) else x.map[T, T](f) ) diff --git a/tests/run/extension-specificity.scala b/tests/run/extension-specificity.scala index e60f22637ce8..19a7d5d01b44 100644 --- a/tests/run/extension-specificity.scala +++ b/tests/run/extension-specificity.scala @@ -1,10 +1,10 @@ class A class B extends A -extension a on (x: A): +extension (x: A) def foo: Int = 1 -extension b on (x: B): +extension (x: B) def foo: Int = 2 @main def Test = diff --git a/tests/run/extmethod-overload.scala b/tests/run/extmethod-overload.scala index 7ae67268957c..c3d0ddbf2c20 100644 --- a/tests/run/extmethod-overload.scala +++ b/tests/run/extmethod-overload.scala @@ -22,13 +22,13 @@ object Test extends App { // Test with extension methods in given object object test1 { - extension Foo: - def (x: Int) |+| (y: Int) = x + y - def (x: Int) |+| (y: String) = x + y.length + extension (x: Int) + def |+| (y: Int) = x + y + def |+| (y: String) = x + y.length - def [T](xs: List[T]) +++ (ys: List[T]): List[T] = xs ++ ys ++ ys - def [T](xs: List[T]) +++ (ys: Iterator[T]): List[T] = xs ++ ys ++ ys - end Foo + extension [T](xs: List[T]) + def +++ (ys: List[T]): List[T] = xs ++ ys ++ ys + def +++ (ys: Iterator[T]): List[T] = xs ++ ys ++ ys assert((1 |+| 2) == 3) assert((1 |+| "2") == 2) @@ -41,7 +41,7 @@ object Test extends App { // Test with imported extension methods object test2 { - import test1.Foo._ + import test1._ assert((1 |+| 2) == 3) assert((1 |+| "2") == 2) @@ -55,11 +55,11 @@ object Test extends App { // Test with given extension methods coming from base class object test3 { class Foo { - def (x: Int) |+| (y: Int) = x + y - def (x: Int) |+| (y: String) = x + y.length + extension (x: Int) def |+| (y: Int) = x + y + extension (x: Int) def |+| (y: String) = x + y.length - def [T](xs: List[T]) +++ (ys: List[T]): List[T] = xs ++ ys ++ ys - def [T](xs: List[T]) +++ (ys: Iterator[T]): List[T] = xs ++ ys ++ ys + extension [T](xs: List[T]) def +++ (ys: List[T]): List[T] = xs ++ ys ++ ys + extension [T](xs: List[T]) def +++ (ys: Iterator[T]): List[T] = xs ++ ys ++ ys } given Bar as Foo @@ -88,18 +88,19 @@ object Test extends App { class C { def xx (x: Any) = 2 } - def (c: C).xx(x: Int) = 1 + extension (c: C) def xx(x: Int) = 1 val c = new C assert(c.xx(1) == 2) // member method takes precedence object D { - def (x: Int).yy(y: Int) = x + y + extension (x: Int) def yy(y: Int) = x + y } - extension on (x: Int) { - def yy (y: Int) = x - y - } + given AnyRef: + extension (x: Int) { + def yy (y: Int) = x - y + } import D._ assert((1 yy 2) == 3) // imported extension method takes precedence @@ -114,8 +115,8 @@ object Test extends App { def b: Long = a } - def (rectangle: Rectangle).area: Long = 0 - def (square: Square).area: Long = square.a * square.a + extension (rectangle: Rectangle) def area: Long = 0 + extension (square: Square) def area: Long = square.a * square.a val rectangles = List(GenericRectangle(2, 3), Square(5)) val areas = rectangles.map(_.area) assert(areas.sum == 0) diff --git a/tests/run/extmethods2.scala b/tests/run/extmethods2.scala index f454612762ea..61766a4c3a79 100644 --- a/tests/run/extmethods2.scala +++ b/tests/run/extmethods2.scala @@ -4,8 +4,8 @@ object Test extends App { given stringListOps(using TC) as Object { type T = List[String] - def (x: T).foo(y: T) = (x ++ y, summon[TC]) - def (x: T).bar(y: Int) = (x(0)(y), summon[TC]) + extension (x: T) def foo(y: T) = (x ++ y, summon[TC]) + extension (x: T) def bar(y: Int) = (x(0)(y), summon[TC]) } def test(using TC) = { @@ -16,26 +16,26 @@ object Test extends App { test(using TC()) object A { - extension listOps on [T](xs: List[T]) { + extension [T](xs: List[T]) { def second: T = xs.tail.head def third: T = xs.tail.tail.head def concat(ys: List[T]) = xs ++ ys } - extension polyListOps on [T, U](xs: List[T]) { + extension [T, U](xs: List[T]) { def zipp(ys: List[U]): List[(T, U)] = xs.zip(ys) } - extension on (xs: List[Int]) { + extension (xs: List[Int]) { def prod = (1 /: xs)(_ * _) } } object B { - import A.{given _} + import A._ val xs = List(1, 2, 3) assert(xs.second[Int] == 2) assert(xs.third == 3) - assert(A.listOps.second[Int](xs) == 2) - assert(A.listOps.third(xs) == 3) + assert(A.extension_second[Int](xs) == 2) + assert(A.extension_third(xs) == 3) assert(xs.prod == 6) assert(xs.concat(xs).length == 6) assert(xs.zipp(xs).map(_ + _).prod == 36) diff --git a/tests/run/fragables-extension.scala b/tests/run/fragables-extension.scala new file mode 100644 index 000000000000..9476d1efd191 --- /dev/null +++ b/tests/run/fragables-extension.scala @@ -0,0 +1,28 @@ +trait Frag + +case class IntFrag(x: Int) extends Frag +case class StringFrag(x: String) extends Frag + +trait Fragable[T]: + extension (x: T) + def toFrags: List[Frag] + +given Fragable[Int] = + x => List(IntFrag(x)) +given Fragable[String] = + x => List(StringFrag(x)) +given [A: Fragable] as Fragable[List[A]] = + x => x.flatMap(_.toFrags) +given Fragable[EmptyTuple] = + x => Nil +given [A: Fragable, B <: Tuple: Fragable] as Fragable[A *: B] = + x => x.head.toFrags ++ x.tail.toFrags + +def f[T: Fragable](x: T) = + println(s"got: ${x.toFrags.mkString(", ")}") + +@main def Test = + f(1) + f("abc") + f(List(1, 2, 3)) + f(1, "a", List("c", "d")) diff --git a/tests/run/i5110/quoted_1.scala b/tests/run/i5110/quoted_1.scala index 9be1181f3dcf..3648a9f38a38 100644 --- a/tests/run/i5110/quoted_1.scala +++ b/tests/run/i5110/quoted_1.scala @@ -8,9 +8,9 @@ object Macro { throw new Boom } - inline def (boom: Bomb) foo(): Unit = () + extension (boom: Bomb) inline def foo(): Unit = () // By name Boom is used to elide the evaluation of the prefix - inline def (boom: => Bomb) bar(): Unit = () + extension (boom: => Bomb) inline def bar(): Unit = () } diff --git a/tests/run/i5606.scala b/tests/run/i5606.scala index f84c752e9b90..aedfa0617dfc 100644 --- a/tests/run/i5606.scala +++ b/tests/run/i5606.scala @@ -1,6 +1,6 @@ object Test extends App { - def [A, B](f: A => B) `$` (a: A): B = f(a) + extension [A, B](f: A => B) def `$` (a: A): B = f(a) assert((((a: Int) => a.toString()) `$` 10) == "10") diff --git a/tests/run/i6902.scala b/tests/run/i6902.scala index 18729d1df937..1cfadd58108f 100644 --- a/tests/run/i6902.scala +++ b/tests/run/i6902.scala @@ -1,6 +1,6 @@ object Test { - extension on [A](a: A) { def <<< : A = a } - extension on (b: Int) { def <<<< : Int = b } + extension [A](a: A) { def <<< : A = a } + extension (b: Int) { def <<<< : Int = b } def main(args: Array[String]): Unit = { 1.<<< diff --git a/tests/run/i8058.scala b/tests/run/i8058.scala index b9ad34ba7282..ad5343126af3 100644 --- a/tests/run/i8058.scala +++ b/tests/run/i8058.scala @@ -1,4 +1,4 @@ -extension on (x: Array[Char]): +extension (x: Array[Char]) inline def swap(i: Int, j: Int) : Unit = val v = x(i) x(i) = x(j) diff --git a/tests/run/inline-override-num.scala b/tests/run/inline-override-num.scala index 2a5acf1c2553..9770cba7f2da 100644 --- a/tests/run/inline-override-num.scala +++ b/tests/run/inline-override-num.scala @@ -8,12 +8,12 @@ object Num { } given IntNum - extension Extension on [T](inline x: T)(using inline num: Num[T]) { + extension [T](inline x: T)(using inline num: Num[T]) { inline def +(inline y: T): T = num.plus(x, y) } } -import Num.Extension._ +import Num.+ inline def twiceInlined[T: Num](x : T): T = x + x def twice[T: Num](x : T): T = x + x diff --git a/tests/run/instances-anonymous.scala b/tests/run/instances-anonymous.scala index b71d2e775ddf..d2df14152cd4 100644 --- a/tests/run/instances-anonymous.scala +++ b/tests/run/instances-anonymous.scala @@ -1,14 +1,14 @@ object Test extends App { implicit object O { - def (x: Int).em: Boolean = x > 0 + extension (x: Int) def em: Boolean = x > 0 } - assert(1.em == O.em(1)) + assert(1.em == O.extension_em(1)) case class Circle(x: Double, y: Double, radius: Double) - extension on (c: Circle) { + extension (c: Circle) { def circumference: Double = c.radius * math.Pi * 2 } @@ -17,7 +17,7 @@ object Test extends App { println(circle.circumference) given AnyRef { - def (xs: Seq[String]).longestStrings: Seq[String] = { + extension (xs: Seq[String]) def longestStrings: Seq[String] = { val maxLength = xs.map(_.length).max xs.filter(_.length == maxLength) } @@ -25,13 +25,13 @@ object Test extends App { val names = List("hi", "hello", "world") assert(names.longestStrings == List("hello", "world")) - extension on [T](xs: Seq[T]) { + extension [T](xs: Seq[T]) { def second = xs.tail.head } assert(names.longestStrings.second == "world") - extension on [T](xs: List[List[T]]) { + extension [T](xs: List[List[T]]) { def flattened = xs.foldLeft[List[T]](Nil)(_ ++ _) } @@ -39,14 +39,14 @@ object Test extends App { assert(Nil.flattened == Nil) trait SemiGroup[T] { - def (x: T).combine(y: T): T + extension (x: T) def combine(y: T): T } trait Monoid[T] extends SemiGroup[T] { def unit: T } given Monoid[String] { - def (x: String).combine(y: String): String = x.concat(y) + extension (x: String) def combine(y: String): String = x.concat(y) def unit: String = "" } @@ -57,20 +57,20 @@ object Test extends App { println(sum(names)) trait Ord[T] { - def (x: T).compareTo(y: T): Int - def (x: T) < (y: T) = x.compareTo(y) < 0 - def (x: T) > (y: T) = x.compareTo(y) > 0 + extension (x: T) def compareTo(y: T): Int + extension (x: T) def < (y: T) = x.compareTo(y) < 0 + extension (x: T) def > (y: T) = x.compareTo(y) > 0 val minimum: T } given as Ord[Int] { - def (x: Int).compareTo(y: Int) = + extension (x: Int) def compareTo(y: Int) = if (x < y) -1 else if (x > y) +1 else 0 val minimum = Int.MinValue } given [T: Ord] as Ord[List[T]] { - def (xs: List[T]).compareTo(ys: List[T]): Int = (xs, ys).match { + extension (xs: List[T]) def compareTo(ys: List[T]): Int = (xs, ys).match { case (Nil, Nil) => 0 case (Nil, _) => -1 case (_, Nil) => +1 @@ -91,25 +91,25 @@ object Test extends App { println(max(List(1, 2, 3), List(2))) trait Functor[F[_]] { - def [A, B](x: F[A]) map (f: A => B): F[B] + extension [A, B](x: F[A]) def map (f: A => B): F[B] } trait Monad[F[_]] extends Functor[F] { - def [A, B](x: F[A]) flatMap (f: A => F[B]): F[B] - def [A, B](x: F[A]) map (f: A => B) = x.flatMap(f `andThen` pure) + extension [A, B](x: F[A]) def flatMap (f: A => F[B]): F[B] + extension [A, B](x: F[A]) def map (f: A => B) = x.flatMap(f `andThen` pure) def pure[A](x: A): F[A] } given Monad[List] { - def [A, B](xs: List[A]) flatMap (f: A => List[B]): List[B] = + extension [A, B](xs: List[A]) def flatMap (f: A => List[B]): List[B] = xs.flatMap(f) def pure[A](x: A): List[A] = List(x) } given [Ctx] as Monad[[X] =>> Ctx => X] { - def [A, B](r: Ctx => A) flatMap (f: A => Ctx => B): Ctx => B = + extension [A, B](r: Ctx => A) def flatMap (f: A => Ctx => B): Ctx => B = ctx => f(r(ctx))(ctx) def pure[A](x: A): Ctx => A = ctx => x @@ -117,7 +117,7 @@ object Test extends App { def mappAll[F[_]: Monad, T](x: T, fs: List[T => T]): F[T] = fs.foldLeft(implicitly[Monad[F]].pure(x))((x: F[T], f: T => T) => - if (true) implicitly[Monad[F]].map(x)(f) + if (true) implicitly[Monad[F]].extension_map(x)(f) else if (true) x.map(f) else x.map[T, T](f) ) diff --git a/tests/run/instances.scala b/tests/run/instances.scala index c56054aeeb06..fd120fa2dabe 100644 --- a/tests/run/instances.scala +++ b/tests/run/instances.scala @@ -1,21 +1,21 @@ object Test extends App { implicit object O { - def (x: Int).em: Boolean = x > 0 + extension (x: Int) def em: Boolean = x > 0 } - assert(1.em == O.em(1)) + assert(1.em == O.extension_em(1)) case class Circle(x: Double, y: Double, radius: Double) - extension circleOps on (c: Circle): + extension (c: Circle) def circumference: Double = c.radius * math.Pi * 2 val circle = new Circle(1, 1, 2.0) - assert(circle.circumference == circleOps.circumference(circle)) + assert(circle.circumference == Test.extension_circumference(circle)) - extension stringOps on (xs: Seq[String]): + extension (xs: Seq[String]) def longestStrings: Seq[String] = val maxLength = xs.map(_.length).max xs.filter(_.length == maxLength) @@ -23,16 +23,15 @@ object Test extends App { val names = List("hi", "hello", "world") assert(names.longestStrings == List("hello", "world")) - extension on [T](xs: Seq[T]): + extension [T](xs: Seq[T]) def second = xs.tail.head assert(names.longestStrings.second == "world") - extension listListOps on [T](xs: List[List[T]]): + extension [T](xs: List[List[T]]) def flattened = xs.foldLeft[List[T]](Nil)(_ ++ _) - extension prepend: - def [T](x: T) :: (xs: Seq[T]) = x +: xs + extension [T](x: T) def :: (xs: Seq[T]) = x +: xs val ss: Seq[Int] = List(1, 2, 3) val ss1 = 0 :: ss @@ -42,13 +41,13 @@ object Test extends App { assert(Nil.flattened == Nil) trait SemiGroup[T]: - def (x: T).combine(y: T): T + extension (x: T) def combine(y: T): T trait Monoid[T] extends SemiGroup[T]: def unit: T given StringMonoid as Monoid[String]: - def (x: String).combine(y: String): String = x.concat(y) + extension (x: String) def combine(y: String): String = x.concat(y) def unit: String = "" // Abstracting over a type class with a context bound: @@ -58,19 +57,19 @@ object Test extends App { println(sum(names)) trait Ord[T]: - def (x: T).compareTo(y: T): Int - def (x: T) < (y: T) = x.compareTo(y) < 0 - def (x: T) > (y: T) = x.compareTo(y) > 0 + extension (x: T) def compareTo(y: T): Int + extension (x: T) def < (y: T) = x.compareTo(y) < 0 + extension (x: T) def > (y: T) = x.compareTo(y) > 0 val minimum: T end Ord given Ord[Int]: - def (x: Int).compareTo(y: Int) = + extension (x: Int) def compareTo(y: Int) = if (x < y) -1 else if (x > y) +1 else 0 val minimum = Int.MinValue given listOrd[T: Ord] as Ord[List[T]]: - def (xs: List[T]).compareTo(ys: List[T]): Int = (xs, ys).match + extension (xs: List[T]) def compareTo(ys: List[T]): Int = (xs, ys).match case (Nil, Nil) => 0 case (Nil, _) => -1 case (_, Nil) => +1 @@ -90,31 +89,31 @@ object Test extends App { println(max(List(1, 2, 3), List(2))) trait Functor[F[_]]: - def [A, B](x: F[A]) map (f: A => B): F[B] + extension [A, B](x: F[A]) def map (f: A => B): F[B] end Functor trait Monad[F[_]] extends Functor[F]: - def [A, B](x: F[A]) flatMap (f: A => F[B]): F[B] - def [A, B](x: F[A]) map (f: A => B) = x.flatMap(f `andThen` pure) + extension [A, B](x: F[A]) def flatMap (f: A => F[B]): F[B] + extension [A, B](x: F[A]) def map (f: A => B) = x.flatMap(f `andThen` pure) def pure[A](x: A): F[A] end Monad given listMonad as Monad[List]: - def [A, B](xs: List[A]) flatMap (f: A => List[B]): List[B] = + extension [A, B](xs: List[A]) def flatMap (f: A => List[B]): List[B] = xs.flatMap(f) def pure[A](x: A): List[A] = List(x) given readerMonad[Ctx] as Monad[[X] =>> Ctx => X]: - def [A, B](r: Ctx => A) flatMap (f: A => Ctx => B): Ctx => B = + extension [A, B](r: Ctx => A) def flatMap (f: A => Ctx => B): Ctx => B = ctx => f(r(ctx))(ctx) def pure[A](x: A): Ctx => A = ctx => x def mapAll[F[_]: Monad, T](x: T, fs: List[T => T]): F[T] = fs.foldLeft(summon[Monad[F]].pure(x))((x: F[T], f: T => T) => - if true then summon[Monad[F]].map(x)(f) + if true then summon[Monad[F]].extension_map(x)(f) else if true then x.map(f) else x.map[T, T](f) ) diff --git a/tests/run/opaque-immutable-array-xm.scala b/tests/run/opaque-immutable-array-xm.scala index 4f90a982c9d1..49e131afcf00 100644 --- a/tests/run/opaque-immutable-array-xm.scala +++ b/tests/run/opaque-immutable-array-xm.scala @@ -11,8 +11,8 @@ object Test extends App { // These should be inline but that does not work currently. Try again // once inliner is moved to ReifyQuotes - def [A](ia: IArray[A]) length: Int = (ia: Array[A]).length - def [A](ia: IArray[A]) apply (i: Int): A = (ia: Array[A])(i) + extension [A](ia: IArray[A]) def length: Int = (ia: Array[A]).length + extension [A](ia: IArray[A]) def apply (i: Int): A = (ia: Array[A])(i) // return a sorted copy of the array def sorted[A <: AnyRef : math.Ordering](ia: IArray[A]): IArray[A] = { diff --git a/tests/run/rescue.scala b/tests/run/rescue.scala index 5ae9a78e63ee..a2b2e33c2cde 100644 --- a/tests/run/rescue.scala +++ b/tests/run/rescue.scala @@ -2,13 +2,13 @@ import scala.util.control.NonFatal import scala.util.control.NonLocalReturns._ object lib { - inline def [T](op: => T) rescue (fallback: => T) = + extension [T](op: => T) inline def rescue (fallback: => T) = try op catch { case NonFatal(_) => fallback // ReturnThrowable is fatal error, thus ignored } - inline def [T, E <: Throwable](op: => T) rescue (fallback: PartialFunction[E, T]) = + extension [T, E <: Throwable](op: => T) inline def rescue (fallback: PartialFunction[E, T]) = try op catch { // case ex: ReturnThrowable[_] => throw ex // bug #7041 diff --git a/tests/run/singleton-ops-flags.scala b/tests/run/singleton-ops-flags.scala index eaef14927b84..66fcc69e21b3 100644 --- a/tests/run/singleton-ops-flags.scala +++ b/tests/run/singleton-ops-flags.scala @@ -22,7 +22,7 @@ package example { type LastFlag = Open.idx.type - def (s: FlagSet).debug: String = + extension (s: FlagSet) def debug: String = if s == EmptyFlags then "EmptyFlags" else s.toSingletonSets[LastFlag].map ( [n <: Int] => (flag: SingletonFlagSet[n]) => flag match { case Erased => "Erased" @@ -48,18 +48,18 @@ package example { opaque type SingletonSets[N <: Int] = Int - private def [N <: Int](n: N).shift: 1 << N = ( 1 << n ).asInstanceOf - private def [N <: Int](n: N).succ : S[N] = ( n + 1 ).asInstanceOf + extension [N <: Int](n: N) private def shift: 1 << N = ( 1 << n ).asInstanceOf + extension [N <: Int](n: N) private def succ : S[N] = ( n + 1 ).asInstanceOf final val baseFlags: EmptyFlagSet = 0 - def (s: EmptyFlagSet).next: SingletonFlagSet[0] = 1 - def [N <: Int: ValueOf](s: SingletonFlagSet[N]).next: SingletonFlagSet[S[N]] = valueOf[N].succ.shift - def [N <: Int: ValueOf](s: SingletonFlagSet[N]).idx: N = valueOf[N] - def [N <: Int](s: FlagSet).toSingletonSets: SingletonSets[N] = s - def (s: FlagSet) | (t: FlagSet): FlagSet = s | t + extension (s: EmptyFlagSet) def next: SingletonFlagSet[0] = 1 + extension [N <: Int: ValueOf](s: SingletonFlagSet[N]) def next: SingletonFlagSet[S[N]] = valueOf[N].succ.shift + extension [N <: Int: ValueOf](s: SingletonFlagSet[N]) def idx: N = valueOf[N] + extension [N <: Int](s: FlagSet) def toSingletonSets: SingletonSets[N] = s + extension (s: FlagSet) def | (t: FlagSet): FlagSet = s | t - def [A, N <: Int: ValueOf](ss: SingletonSets[N]).map(f: [t <: Int] => (s: SingletonFlagSet[t]) => A): List[A] = + extension [A, N <: Int: ValueOf](ss: SingletonSets[N]) def map(f: [t <: Int] => (s: SingletonFlagSet[t]) => A): List[A] = val maxFlag = valueOf[N] val buf = List.newBuilder[A] var current = 0 diff --git a/tests/run/string-context-implicits-with-conversion.scala b/tests/run/string-context-implicits-with-conversion.scala index c3ec5af884ff..54e52ebdda8d 100644 --- a/tests/run/string-context-implicits-with-conversion.scala +++ b/tests/run/string-context-implicits-with-conversion.scala @@ -1,6 +1,6 @@ object Lib { - def (sc: StringContext).showMe(args: Showed*): String = sc.s(args: _*) + extension (sc: StringContext) def showMe(args: Showed*): String = sc.s(args: _*) opaque type Showed = String diff --git a/tests/run/toplevel-implicits/a.b.scala b/tests/run/toplevel-implicits/a.b.scala index a360f4d6e491..4811080ef78c 100644 --- a/tests/run/toplevel-implicits/a.b.scala +++ b/tests/run/toplevel-implicits/a.b.scala @@ -3,7 +3,7 @@ package implicits case class C() implicit object Cops { - def (x: C).pair(y: C) = (x, y) + extension (x: C) def pair(y: C) = (x, y) } class D { diff --git a/tests/run/tupled-function-andThen.scala b/tests/run/tupled-function-andThen.scala index 8bc7fa941a47..9080b8966522 100644 --- a/tests/run/tupled-function-andThen.scala +++ b/tests/run/tupled-function-andThen.scala @@ -32,7 +32,7 @@ object Test { * @tparam GArgs the tuple type with the same types as the function arguments of G and return type of F * @tparam R the return type of G */ - def [F, G, FArgs <: Tuple, GArgs <: Tuple, R](f: F) andThen (g: G)(using tf: TupledFunction[F, FArgs => GArgs], tg: TupledFunction[G, GArgs => R]): FArgs => R = { + extension [F, G, FArgs <: Tuple, GArgs <: Tuple, R](f: F) def andThen (g: G)(using tf: TupledFunction[F, FArgs => GArgs], tg: TupledFunction[G, GArgs => R]): FArgs => R = { x => tg.tupled(g)(tf.tupled(f)(x)) } diff --git a/tests/run/tupled-function-apply.scala b/tests/run/tupled-function-apply.scala index 16c90e952f6c..d37dba0a2f01 100644 --- a/tests/run/tupled-function-apply.scala +++ b/tests/run/tupled-function-apply.scala @@ -113,6 +113,6 @@ object Test { * @tparam Args the tuple type with the same types as the function arguments of F * @tparam R the return type of F */ - def [F, Args <: Tuple, R](f: F) apply (args: Args)(using tf: TupledFunction[F, Args => R]): R = + extension [F, Args <: Tuple, R](f: F) def apply (args: Args)(using tf: TupledFunction[F, Args => R]): R = tf.tupled(f)(args) } \ No newline at end of file diff --git a/tests/run/tupled-function-compose.scala b/tests/run/tupled-function-compose.scala index d929e9bb2956..71a918f1115c 100644 --- a/tests/run/tupled-function-compose.scala +++ b/tests/run/tupled-function-compose.scala @@ -33,7 +33,7 @@ object Test { * @tparam GArgs the tuple type with the same types as the function arguments of G * @tparam R the return type of F */ - def [F, G, FArgs <: Tuple, GArgs <: Tuple, R](f: F) compose (g: G)(using tg: TupledFunction[G, GArgs => FArgs], tf: TupledFunction[F, FArgs => R]): GArgs => R = { + extension [F, G, FArgs <: Tuple, GArgs <: Tuple, R](f: F) def compose (g: G)(using tg: TupledFunction[G, GArgs => FArgs], tf: TupledFunction[F, FArgs => R]): GArgs => R = { x => tf.tupled(f)(tg.tupled(g)(x)) } diff --git a/tests/run/tupled-function-extension-method.scala b/tests/run/tupled-function-extension-method.scala index 1281a5275511..2f5a0c4e8e67 100644 --- a/tests/run/tupled-function-extension-method.scala +++ b/tests/run/tupled-function-extension-method.scala @@ -34,14 +34,17 @@ object Test { class Expr[T](val x: T) // Specialized only for arity 0 and one as auto tupling will not provide the disired effect - def [R](e: Expr[() => R]) apply (): R = e.x() - def [Arg, R](e: Expr[Arg => R]) apply (arg: Arg): R = e.x(arg) - def [Arg, R](e: Expr[Arg ?=> R]) applyGiven(arg: Arg): R = e.x(using arg) + extension [R](e: Expr[() => R]) + def apply (): R = e.x() + extension [Arg, R](e: Expr[Arg => R]) + def apply (arg: Arg): R = e.x(arg) + extension [Arg, R](e: Expr[Arg ?=> R]) + def applyGiven(arg: Arg): R = e.x(using arg) // Applied to all funtions of arity 2 or more (including more than 22 parameters) - def [F, Args <: Tuple, R](e: Expr[F]) apply (args: Args)(using tf: TupledFunction[F, Args => R]): R = + extension [F, Args <: Tuple, R](e: Expr[F]) def apply (args: Args)(using tf: TupledFunction[F, Args => R]): R = tf.tupled(e.x)(args) - def [F, Args <: Tuple, R](e: Expr[F]) applyGiven (args: Args)(using tf: TupledFunction[F, Args ?=> R]): R = + extension [F, Args <: Tuple, R](e: Expr[F]) def applyGiven (args: Args)(using tf: TupledFunction[F, Args ?=> R]): R = tf.tupled(e.x)(using args) } \ No newline at end of file diff --git a/tests/run/tupled-function-tupled.scala b/tests/run/tupled-function-tupled.scala index ce42c13beb32..ce80ef983808 100644 --- a/tests/run/tupled-function-tupled.scala +++ b/tests/run/tupled-function-tupled.scala @@ -24,5 +24,5 @@ object Test { * @tparam Args the tuple type with the same types as the function arguments of F * @tparam R the return type of F */ - def [F, Args <: Tuple, R](f: F) tupled (using tf: TupledFunction[F, Args => R]): Args => R = tf.tupled(f) + extension [F, Args <: Tuple, R](f: F) def tupled (using tf: TupledFunction[F, Args => R]): Args => R = tf.tupled(f) } diff --git a/tests/run/tupled-function-untupled.scala b/tests/run/tupled-function-untupled.scala index 605836f16909..2d50169c0bd3 100644 --- a/tests/run/tupled-function-untupled.scala +++ b/tests/run/tupled-function-untupled.scala @@ -104,5 +104,5 @@ object Test { * @tparam Args the tuple type with the same types as the function arguments of F * @tparam R the return type of F */ - def [F, Args <: Tuple, R](f: Args => R) untupled(using tf: TupledFunction[F, Args => R]): F = tf.untupled(f) + extension [F, Args <: Tuple, R](f: Args => R) def untupled(using tf: TupledFunction[F, Args => R]): F = tf.untupled(f) } diff --git a/tests/semanticdb/expect/Enums.expect.scala b/tests/semanticdb/expect/Enums.expect.scala index 3baff23aa49c..0539adc8866e 100644 --- a/tests/semanticdb/expect/Enums.expect.scala +++ b/tests/semanticdb/expect/Enums.expect.scala @@ -12,10 +12,10 @@ object Enums/*<-_empty_::Enums.*/: case Hearts/*<-_empty_::Enums.Suits.Hearts.*/, Spades/*<-_empty_::Enums.Suits.Spades.*/, Clubs/*<-_empty_::Enums.Suits.Clubs.*/, Diamonds/*<-_empty_::Enums.Suits.Diamonds.*/ object Suits/*<-_empty_::Enums.Suits.*/: - def (suit/*<-_empty_::Enums.Suits.isRed().*//*<-_empty_::Enums.Suits.isRed().(suit)*/: Suits/*->_empty_::Enums.Suits#*/).isRed: Boolean/*->scala::Boolean#*/ = - suit/*->_empty_::Enums.Suits.isRed().(suit)*/ ==/*->scala::Any#`==`().*/ Hearts/*->_empty_::Enums.Suits.Hearts.*/ ||/*->scala::Boolean#`||`().*/ suit/*->_empty_::Enums.Suits.isRed().(suit)*/ ==/*->scala::Any#`==`().*/ Diamonds/*->_empty_::Enums.Suits.Diamonds.*/ + extension (suit/*<-_empty_::Enums.Suits.extension_isRed().(suit)*/: Suits/*->_empty_::Enums.Suits#*/) def isRed: Boolean /*<-_empty_::Enums.Suits.extension_isRed().*//*->scala::Boolean#*/= + suit/*->_empty_::Enums.Suits.extension_isRed().(suit)*/ ==/*->scala::Any#`==`().*/ Hearts/*->_empty_::Enums.Suits.Hearts.*/ ||/*->scala::Boolean#`||`().*/ suit/*->_empty_::Enums.Suits.extension_isRed().(suit)*/ ==/*->scala::Any#`==`().*/ Diamonds/*->_empty_::Enums.Suits.Diamonds.*/ - def (suit: /*<-_empty_::Enums.Suits.isBlack().*//*<-_empty_::Enums.Suits.isBlack().(suit)*/Suits/*->_empty_::Enums.Suits#*/).isBlack: Boolean/*->scala::Boolean#*/ = suit/*->_empty_::Enums.Suits.isBlack().(suit)*/ match + extension (suit/*<-_empty_::Enums.Suits.extension_isBlack().(suit)*/: Suits/*->_empty_::Enums.Suits#*/) def isBlack: Boolean /*<-_empty_::Enums.Suits.extension_isBlack().*//*->scala::Boolean#*/= suit/*->_empty_::Enums.Suits.extension_isBlack().(suit)*/ match case Spades/*->_empty_::Enums.Suits.Spades.*/ | Clubs/*->_empty_::Enums.Suits.Clubs.*/ => true case _ => false @@ -49,10 +49,10 @@ object Enums/*<-_empty_::Enums.*/: object <:_empty_::Enums.`<:<`.given_T().[T]*/ <:_empty_::Enums.`<:<`#*/ T/*->_empty_::Enums.`<:<`.given_T().[T]*/) = Refl/*->_empty_::Enums.`<:<`.Refl.*//*->_empty_::Enums.`<:<`.Refl.apply().*/() - def [A, B]/*<-_empty_::Enums.unwrap().*//*<-_empty_::Enums.unwrap().[A]*//*<-_empty_::Enums.unwrap().[B]*/(opt/*<-_empty_::Enums.unwrap().(opt)*/: Option/*->scala::Option#*/[A/*->_empty_::Enums.unwrap().[A]*/]) unwrap(using ev/*<-_empty_::Enums.unwrap().(ev)*/: A/*->_empty_::Enums.unwrap().[A]*/ <:_empty_::Enums.`<:<`#*/ Option/*->scala::Option#*/[B/*->_empty_::Enums.unwrap().[B]*/]): Option/*->scala::Option#*/[B/*->_empty_::Enums.unwrap().[B]*/] = ev/*->_empty_::Enums.unwrap().(ev)*/ match - case Refl/*->_empty_::Enums.`<:<`.Refl.*//*->_empty_::Enums.`<:<`.Refl.unapply().*/() => opt/*->_empty_::Enums.unwrap().(opt)*/.flatMap/*->scala::Option#flatMap().*/(identity/*->scala::Predef.identity().*//*->local0*/[Option/*->scala::Option#*/[B/*->_empty_::Enums.unwrap().[B]*/]]) + extension [A/*<-_empty_::Enums.extension_unwrap().[A]*/, B/*<-_empty_::Enums.extension_unwrap().[B]*/](opt/*<-_empty_::Enums.extension_unwrap().(opt)*/: Option/*->scala::Option#*/[A/*->_empty_::Enums.extension_unwrap().[A]*/]) def unwrap(using ev:/*<-_empty_::Enums.extension_unwrap().*//*<-_empty_::Enums.extension_unwrap().(ev)*/ A/*->_empty_::Enums.extension_unwrap().[A]*/ <:_empty_::Enums.`<:<`#*/ Option/*->scala::Option#*/[B/*->_empty_::Enums.extension_unwrap().[B]*/]): Option/*->scala::Option#*/[B/*->_empty_::Enums.extension_unwrap().[B]*/] = ev/*->_empty_::Enums.extension_unwrap().(ev)*/ match + case Refl/*->_empty_::Enums.`<:<`.Refl.*//*->_empty_::Enums.`<:<`.Refl.unapply().*/() => opt/*->_empty_::Enums.extension_unwrap().(opt)*/.flatMap/*->scala::Option#flatMap().*/(identity/*->scala::Predef.identity().*//*->local0*/[Option/*->scala::Option#*/[B/*->_empty_::Enums.extension_unwrap().[B]*/]]) - val some1/*<-_empty_::Enums.some1.*/ = /*->_empty_::Enums.unwrap().*/Some/*->scala::Some.*//*->scala::Some.apply().*/(Some/*->scala::Some.*//*->scala::Some.apply().*/(1))/*->_empty_::Enums.`<:<`.given_T().*/.unwrap + val some1/*<-_empty_::Enums.some1.*/ = /*->_empty_::Enums.extension_unwrap().*/Some/*->scala::Some.*//*->scala::Some.apply().*/(Some/*->scala::Some.*//*->scala::Some.apply().*/(1))/*->_empty_::Enums.`<:<`.given_T().*/.unwrap enum Planet/*<-_empty_::Enums.Planet#*/(mass/*<-_empty_::Enums.Planet#mass.*/: Double/*->scala::Double#*/, radius/*<-_empty_::Enums.Planet#radius.*/: Double/*->scala::Double#*/) extends java.lang.Enum/*->java::lang::Enum#*/[Planet/*->_empty_::Enums.Planet#*/]/*->java::lang::Enum#``().*/: private final val G/*<-_empty_::Enums.Planet#G.*/ = 6.67300E-11 diff --git a/tests/semanticdb/expect/Enums.scala b/tests/semanticdb/expect/Enums.scala index d32b8823c98f..f781e0878145 100644 --- a/tests/semanticdb/expect/Enums.scala +++ b/tests/semanticdb/expect/Enums.scala @@ -12,10 +12,10 @@ object Enums: case Hearts, Spades, Clubs, Diamonds object Suits: - def (suit: Suits).isRed: Boolean = + extension (suit: Suits) def isRed: Boolean = suit == Hearts || suit == Diamonds - def (suit: Suits).isBlack: Boolean = suit match + extension (suit: Suits) def isBlack: Boolean = suit match case Spades | Clubs => true case _ => false @@ -49,7 +49,7 @@ object Enums: object <:< : given [T] as (T <:< T) = Refl() - def [A, B](opt: Option[A]) unwrap(using ev: A <:< Option[B]): Option[B] = ev match + extension [A, B](opt: Option[A]) def unwrap(using ev: A <:< Option[B]): Option[B] = ev match case Refl() => opt.flatMap(identity[Option[B]]) val some1 = Some(Some(1)).unwrap diff --git a/tests/semanticdb/expect/Givens.expect.scala b/tests/semanticdb/expect/Givens.expect.scala index 7c3c543c1bc1..ecc6addedeba 100644 --- a/tests/semanticdb/expect/Givens.expect.scala +++ b/tests/semanticdb/expect/Givens.expect.scala @@ -3,25 +3,25 @@ package b object Givens/*<-a::b::Givens.*/: - extension on [A](any: A): - /*<-a::b::Givens.extension_sayHello_A.*//*<-a::b::Givens.extension_sayHello_A.sayHello().[A]*//*<-a::b::Givens.extension_sayHello_A.sayHello().(any)*//*->a::b::Givens.extension_sayHello_A.sayHello().[A]*/def sayHello/*<-a::b::Givens.extension_sayHello_A.sayHello().*/ = s"/*->scala::StringContext.apply().*/Hello, I am $any/*->a::b::Givens.extension_sayHello_A.sayHello().(any)*/"/*->scala::StringContext#s().*/ + extension [A/*<-a::b::Givens.extension_sayHello().[A]*/](any/*<-a::b::Givens.extension_sayHello().(any)*/: A/*->a::b::Givens.extension_sayHello().[A]*/) + def sayHello = s"Hello/*<-a::b::Givens.extension_sayHello().*//*->scala::StringContext.apply().*/, I am $any/*->a::b::Givens.extension_sayHello().(any)*/"/*->scala::StringContext#s().*/ - extension on [B](any: B): - de/*<-a::b::Givens.extension_sayGoodbye_B.*//*<-a::b::Givens.extension_sayGoodbye_B.sayGoodbye().[B]*//*<-a::b::Givens.extension_sayGoodbye_B.saySoLong().[B]*//*<-a::b::Givens.extension_sayGoodbye_B.sayGoodbye().(any)*//*<-a::b::Givens.extension_sayGoodbye_B.saySoLong().(any)*//*->a::b::Givens.extension_sayGoodbye_B.sayGoodbye().[B]*//*->a::b::Givens.extension_sayGoodbye_B.saySoLong().[B]*/f sayGoodbye/*<-a::b::Givens.extension_sayGoodbye_B.sayGoodbye().*/ = s"/*->scala::StringContext.apply().*/Goodbye, from $any/*->a::b::Givens.extension_sayGoodbye_B.sayGoodbye().(any)*/"/*->scala::StringContext#s().*/ - def saySoLong/*<-a::b::Givens.extension_sayGoodbye_B.saySoLong().*/ = s"/*->scala::StringContext.apply().*/So Long, from $any/*->a::b::Givens.extension_sayGoodbye_B.saySoLong().(any)*/"/*->scala::StringContext#s().*/ + extension [B/*<-a::b::Givens.extension_sayGoodbye().[B]*//*<-a::b::Givens.extension_saySoLong().[B]*/](any/*<-a::b::Givens.extension_sayGoodbye().(any)*//*<-a::b::Givens.extension_saySoLong().(any)*/: B/*->a::b::Givens.extension_sayGoodbye().[B]*//*->a::b::Givens.extension_saySoLong().[B]*/) + def sayGoodbye = s"Goodb/*<-a::b::Givens.extension_sayGoodbye().*//*->scala::StringContext.apply().*/ye, from $any/*->a::b::Givens.extension_sayGoodbye().(any)*/"/*->scala::StringContext#s().*/ + def saySoLong = s"So Lo/*<-a::b::Givens.extension_saySoLong().*//*->scala::StringContext.apply().*/ng, from $any/*->a::b::Givens.extension_saySoLong().(any)*/"/*->scala::StringContext#s().*/ - val hello1/*<-a::b::Givens.hello1.*/ = /*->a::b::Givens.extension_sayHello_A.sayHello().*/1.sayHello - val goodbye1/*<-a::b::Givens.goodbye1.*/ = /*->a::b::Givens.extension_sayGoodbye_B.sayGoodbye().*/1.sayGoodbye - val soLong1/*<-a::b::Givens.soLong1.*/ = /*->a::b::Givens.extension_sayGoodbye_B.saySoLong().*/1.saySoLong + val hello1/*<-a::b::Givens.hello1.*/ = /*->a::b::Givens.extension_sayHello().*/1.sayHello + val goodbye1/*<-a::b::Givens.goodbye1.*/ = /*->a::b::Givens.extension_sayGoodbye().*/1.sayGoodbye + val soLong1/*<-a::b::Givens.soLong1.*/ = /*->a::b::Givens.extension_saySoLong().*/1.saySoLong trait Monoid/*<-a::b::Givens.Monoid#*/[A/*<-a::b::Givens.Monoid#[A]*/]: def empty/*<-a::b::Givens.Monoid#empty().*/: A/*->a::b::Givens.Monoid#[A]*/ - def (x: A)./*<-a::b::Givens.Monoid#combine().*//*<-a::b::Givens.Monoid#combine().(x)*//*->a::b::Givens.Monoid#[A]*/combine(y/*<-a::b::Givens.Monoid#combine().(y)*/: A/*->a::b::Givens.Monoid#[A]*/): A/*->a::b::Givens.Monoid#[A]*/ - + extension (x/*<-a::b::Givens.Monoid#extension_combine().(x)*/: A/*->a::b::Givens.Monoid#[A]*/) def combine(y: A): A +/*<-a::b::Givens.Monoid#extension_combine().*//*<-a::b::Givens.Monoid#extension_combine().(y)*//*->a::b::Givens.Monoid#[A]*//*->a::b::Givens.Monoid#[A]*/ given Monoid[String]: /*<-a::b::Givens.given_Monoid_String.*//*->a::b::Givens.Monoid#*//*->scala::Predef.String#*/ def empty/*<-a::b::Givens.given_Monoid_String.empty().*/ = "" - def (x: Str/*<-a::b::Givens.given_Monoid_String.combine().*//*<-a::b::Givens.given_Monoid_String.combine().(x)*/ing/*->scala::Predef.String#*/).combine(y/*<-a::b::Givens.given_Monoid_String.combine().(y)*/: String/*->scala::Predef.String#*/) = x/*->a::b::Givens.given_Monoid_String.combine().(x)*/ +/*->java::lang::String#`+`().*/ y/*->a::b::Givens.given_Monoid_String.combine().(y)*/ + extension (x/*<-a::b::Givens.given_Monoid_String.extension_combine().(x)*/: String/*->scala::Predef.String#*/) def combine(y: String/*<-a::b::Givens.given_Monoid_String.extension_combine().*//*<-a::b::Givens.given_Monoid_String.extension_combine().(y)*//*->scala::Predef.String#*/) = x/*->a::b::Givens.given_Monoid_String.extension_combine().(x)*/ +/*->java::lang::String#`+`().*/ y/*->a::b::Givens.given_Monoid_String.extension_combine().(y)*/ inline given int2String/*<-a::b::Givens.int2String().*/ as Conversion/*->scala::Conversion#*/[Int/*->scala::Int#*/, String/*->scala::Predef.String#*/] = _.toString/*->scala::Any#toString().*/ - def foo/*<-a::b::Givens.foo().*/[A/*<-a::b::Givens.foo().[A]*/](using A/*<-a::b::Givens.foo().(A)*/: Monoid/*->a::b::Givens.Monoid#*/[A/*->a::b::Givens.foo().[A]*/]): A/*->a::b::Givens.foo().[A]*/ = A/*->a::b::Givens.foo().(A)*/.combine/*->a::b::Givens.Monoid#combine().*/(A/*->a::b::Givens.foo().(A)*/.empty/*->a::b::Givens.Monoid#empty().*/)(A/*->a::b::Givens.foo().(A)*/.empty/*->a::b::Givens.Monoid#empty().*/) + def foo/*<-a::b::Givens.foo().*/[A/*<-a::b::Givens.foo().[A]*/](using A/*<-a::b::Givens.foo().(A)*/: Monoid/*->a::b::Givens.Monoid#*/[A/*->a::b::Givens.foo().[A]*/]): A/*->a::b::Givens.foo().[A]*/ = A/*->a::b::Givens.foo().(A)*/.extension_combine/*->a::b::Givens.Monoid#extension_combine().*/(A/*->a::b::Givens.foo().(A)*/.empty/*->a::b::Givens.Monoid#empty().*/)(A/*->a::b::Givens.foo().(A)*/.empty/*->a::b::Givens.Monoid#empty().*/) diff --git a/tests/semanticdb/expect/Givens.scala b/tests/semanticdb/expect/Givens.scala index 54a905a51579..2f6add1e12e1 100644 --- a/tests/semanticdb/expect/Givens.scala +++ b/tests/semanticdb/expect/Givens.scala @@ -3,10 +3,10 @@ package b object Givens: - extension on [A](any: A): + extension [A](any: A) def sayHello = s"Hello, I am $any" - extension on [B](any: B): + extension [B](any: B) def sayGoodbye = s"Goodbye, from $any" def saySoLong = s"So Long, from $any" @@ -16,12 +16,12 @@ object Givens: trait Monoid[A]: def empty: A - def (x: A).combine(y: A): A + extension (x: A) def combine(y: A): A given Monoid[String]: def empty = "" - def (x: String).combine(y: String) = x + y + extension (x: String) def combine(y: String) = x + y inline given int2String as Conversion[Int, String] = _.toString - def foo[A](using A: Monoid[A]): A = A.combine(A.empty)(A.empty) + def foo[A](using A: Monoid[A]): A = A.extension_combine(A.empty)(A.empty) diff --git a/tests/semanticdb/expect/toplevel.expect.scala b/tests/semanticdb/expect/toplevel.expect.scala index b61e882fef36..612bff8b0054 100644 --- a/tests/semanticdb/expect/toplevel.expect.scala +++ b/tests/semanticdb/expect/toplevel.expect.scala @@ -1,5 +1,5 @@ inline val a = "/*<-_empty_::toplevel$package.*//*<-_empty_::toplevel$package.a.*/" -def (x: Int/*<-_empty_::toplevel$package.combine().*//*<-_empty_::toplevel$package.combine().(x)*//*->scala::Int#*/) combine (y/*<-_empty_::toplevel$package.combine().(y)*/: Int/*->scala::Int#*/) = x/*->_empty_::toplevel$package.combine().(x)*/ +/*->scala::Int#`+`(+4).*/ y/*->_empty_::toplevel$package.combine().(y)*/ -def combine/*<-_empty_::toplevel$package.combine(+1).*/(x/*<-_empty_::toplevel$package.combine(+1).(x)*/: Int/*->scala::Int#*/, y/*<-_empty_::toplevel$package.combine(+1).(y)*/: Int/*->scala::Int#*/, z/*<-_empty_::toplevel$package.combine(+1).(z)*/: Int/*->scala::Int#*/) = x/*->_empty_::toplevel$package.combine(+1).(x)*/ +/*->scala::Int#`+`(+4).*/ y/*->_empty_::toplevel$package.combine(+1).(y)*/ +/*->scala::Int#`+`(+4).*/ z/*->_empty_::toplevel$package.combine(+1).(z)*/ -def combine/*<-_empty_::toplevel$package.combine(+2).*/ = 0 +extension (x/*<-_empty_::toplevel$package.extension_combine().(x)*/: Int/*->scala::Int#*/) def combine (y: Int) /*<-_empty_::toplevel$package.extension_combine().*//*<-_empty_::toplevel$package.extension_combine().(y)*//*->scala::Int#*/= x/*->_empty_::toplevel$package.extension_combine().(x)*/ +/*->scala::Int#`+`(+4).*/ y/*->_empty_::toplevel$package.extension_combine().(y)*/ +def combine/*<-_empty_::toplevel$package.combine().*/(x/*<-_empty_::toplevel$package.combine().(x)*/: Int/*->scala::Int#*/, y/*<-_empty_::toplevel$package.combine().(y)*/: Int/*->scala::Int#*/, z/*<-_empty_::toplevel$package.combine().(z)*/: Int/*->scala::Int#*/) = x/*->_empty_::toplevel$package.combine().(x)*/ +/*->scala::Int#`+`(+4).*/ y/*->_empty_::toplevel$package.combine().(y)*/ +/*->scala::Int#`+`(+4).*/ z/*->_empty_::toplevel$package.combine().(z)*/ +def combine/*<-_empty_::toplevel$package.combine(+1).*/ = 0 def foo/*<-_empty_::toplevel$package.foo().*/ = "foo" diff --git a/tests/semanticdb/expect/toplevel.scala b/tests/semanticdb/expect/toplevel.scala index 37ff0172e2aa..a99265bc630e 100644 --- a/tests/semanticdb/expect/toplevel.scala +++ b/tests/semanticdb/expect/toplevel.scala @@ -1,5 +1,5 @@ inline val a = "" -def (x: Int) combine (y: Int) = x + y +extension (x: Int) def combine (y: Int) = x + y def combine(x: Int, y: Int, z: Int) = x + y + z def combine = 0 def foo = "foo" diff --git a/tests/semanticdb/metac.expect b/tests/semanticdb/metac.expect index 5bb0ed61903c..afb28b83d02f 100644 --- a/tests/semanticdb/metac.expect +++ b/tests/semanticdb/metac.expect @@ -743,10 +743,10 @@ _empty_/Enums.Suits.Diamonds. => case val static enum method Diamonds _empty_/Enums.Suits.Hearts. => case val static enum method Hearts _empty_/Enums.Suits.Spades. => case val static enum method Spades _empty_/Enums.Suits.derived$Eql(). => implicit method derived$Eql -_empty_/Enums.Suits.isBlack(). => method isBlack -_empty_/Enums.Suits.isBlack().(suit) => param suit -_empty_/Enums.Suits.isRed(). => method isRed -_empty_/Enums.Suits.isRed().(suit) => param suit +_empty_/Enums.Suits.extension_isBlack(). => method extension_isBlack +_empty_/Enums.Suits.extension_isBlack().(suit) => param suit +_empty_/Enums.Suits.extension_isRed(). => method extension_isRed +_empty_/Enums.Suits.extension_isRed().(suit) => param suit _empty_/Enums.Suits.valueOf(). => method valueOf _empty_/Enums.Suits.valueOf().($name) => param $name _empty_/Enums.Suits.values(). => method values @@ -792,12 +792,12 @@ _empty_/Enums.`<:<`.Refl.unapply().(x$1) => param x$1 _empty_/Enums.`<:<`.Refl.unapply().[C] => typeparam C _empty_/Enums.`<:<`.given_T(). => final implicit method given_T _empty_/Enums.`<:<`.given_T().[T] => typeparam T +_empty_/Enums.extension_unwrap(). => method extension_unwrap +_empty_/Enums.extension_unwrap().(ev) => implicit param ev +_empty_/Enums.extension_unwrap().(opt) => param opt +_empty_/Enums.extension_unwrap().[A] => typeparam A +_empty_/Enums.extension_unwrap().[B] => typeparam B _empty_/Enums.some1. => val method some1 -_empty_/Enums.unwrap(). => method unwrap -_empty_/Enums.unwrap().(ev) => implicit param ev -_empty_/Enums.unwrap().(opt) => param opt -_empty_/Enums.unwrap().[A] => typeparam A -_empty_/Enums.unwrap().[B] => typeparam B local0 => param x Occurrences: @@ -824,22 +824,22 @@ Occurrences: [11:25..11:30): Clubs <- _empty_/Enums.Suits.Clubs. [11:32..11:40): Diamonds <- _empty_/Enums.Suits.Diamonds. [13:9..13:14): Suits <- _empty_/Enums.Suits. -[14:8..14:13): (suit <- _empty_/Enums.Suits.isRed(). -[14:9..14:13): suit <- _empty_/Enums.Suits.isRed().(suit) -[14:15..14:20): Suits -> _empty_/Enums.Suits# -[14:29..14:36): Boolean -> scala/Boolean# -[15:6..15:10): suit -> _empty_/Enums.Suits.isRed().(suit) +[14:15..14:19): suit <- _empty_/Enums.Suits.extension_isRed().(suit) +[14:21..14:26): Suits -> _empty_/Enums.Suits# +[14:32..14:47): isRed: Boolean <- _empty_/Enums.Suits.extension_isRed(). +[14:39..14:46): Boolean -> scala/Boolean# +[15:6..15:10): suit -> _empty_/Enums.Suits.extension_isRed().(suit) [15:11..15:13): == -> scala/Any#`==`(). [15:14..15:20): Hearts -> _empty_/Enums.Suits.Hearts. [15:21..15:23): || -> scala/Boolean#`||`(). -[15:24..15:28): suit -> _empty_/Enums.Suits.isRed().(suit) +[15:24..15:28): suit -> _empty_/Enums.Suits.extension_isRed().(suit) [15:29..15:31): == -> scala/Any#`==`(). [15:32..15:40): Diamonds -> _empty_/Enums.Suits.Diamonds. -[17:8..17:15): (suit: <- _empty_/Enums.Suits.isBlack(). -[17:9..17:13): suit <- _empty_/Enums.Suits.isBlack().(suit) -[17:15..17:20): Suits -> _empty_/Enums.Suits# -[17:31..17:38): Boolean -> scala/Boolean# -[17:41..17:45): suit -> _empty_/Enums.Suits.isBlack().(suit) +[17:15..17:19): suit <- _empty_/Enums.Suits.extension_isBlack().(suit) +[17:21..17:26): Suits -> _empty_/Enums.Suits# +[17:32..17:49): isBlack: Boolean <- _empty_/Enums.Suits.extension_isBlack(). +[17:41..17:48): Boolean -> scala/Boolean# +[17:51..17:55): suit -> _empty_/Enums.Suits.extension_isBlack().(suit) [18:11..18:17): Spades -> _empty_/Enums.Suits.Spades. [18:20..18:25): Clubs -> _empty_/Enums.Suits.Clubs. [21:7..21:15): WeekDays <- _empty_/Enums.WeekDays# @@ -917,30 +917,30 @@ Occurrences: [49:24..49:25): T -> _empty_/Enums.`<:<`.given_T().[T] [49:29..49:33): Refl -> _empty_/Enums.`<:<`.Refl. [49:33..49:33): -> _empty_/Enums.`<:<`.Refl.apply(). -[51:6..51:12): [A, B] <- _empty_/Enums.unwrap(). -[51:7..51:8): A <- _empty_/Enums.unwrap().[A] -[51:10..51:11): B <- _empty_/Enums.unwrap().[B] -[51:13..51:16): opt <- _empty_/Enums.unwrap().(opt) -[51:18..51:24): Option -> scala/Option# -[51:25..51:26): A -> _empty_/Enums.unwrap().[A] -[51:42..51:44): ev <- _empty_/Enums.unwrap().(ev) -[51:46..51:47): A -> _empty_/Enums.unwrap().[A] -[51:48..51:51): <:< -> _empty_/Enums.`<:<`# -[51:52..51:58): Option -> scala/Option# -[51:59..51:60): B -> _empty_/Enums.unwrap().[B] -[51:64..51:70): Option -> scala/Option# -[51:71..51:72): B -> _empty_/Enums.unwrap().[B] -[51:76..51:78): ev -> _empty_/Enums.unwrap().(ev) +[51:13..51:14): A <- _empty_/Enums.extension_unwrap().[A] +[51:16..51:17): B <- _empty_/Enums.extension_unwrap().[B] +[51:19..51:22): opt <- _empty_/Enums.extension_unwrap().(opt) +[51:24..51:30): Option -> scala/Option# +[51:31..51:32): A -> _empty_/Enums.extension_unwrap().[A] +[51:39..51:55): unwrap(using ev: <- _empty_/Enums.extension_unwrap(). +[51:52..51:54): ev <- _empty_/Enums.extension_unwrap().(ev) +[51:56..51:57): A -> _empty_/Enums.extension_unwrap().[A] +[51:58..51:61): <:< -> _empty_/Enums.`<:<`# +[51:62..51:68): Option -> scala/Option# +[51:69..51:70): B -> _empty_/Enums.extension_unwrap().[B] +[51:74..51:80): Option -> scala/Option# +[51:81..51:82): B -> _empty_/Enums.extension_unwrap().[B] +[51:86..51:88): ev -> _empty_/Enums.extension_unwrap().(ev) [52:9..52:13): Refl -> _empty_/Enums.`<:<`.Refl. [52:13..52:13): -> _empty_/Enums.`<:<`.Refl.unapply(). -[52:19..52:22): opt -> _empty_/Enums.unwrap().(opt) +[52:19..52:22): opt -> _empty_/Enums.extension_unwrap().(opt) [52:23..52:30): flatMap -> scala/Option#flatMap(). [52:31..52:39): identity -> scala/Predef.identity(). [52:31..52:31): -> local0 [52:40..52:46): Option -> scala/Option# -[52:47..52:48): B -> _empty_/Enums.unwrap().[B] +[52:47..52:48): B -> _empty_/Enums.extension_unwrap().[B] [54:6..54:11): some1 <- _empty_/Enums.some1. -[54:14..54:14): -> _empty_/Enums.unwrap(). +[54:14..54:14): -> _empty_/Enums.extension_unwrap(). [54:14..54:18): Some -> scala/Some. [54:18..54:18): -> scala/Some.apply(). [54:19..54:23): Some -> scala/Some. @@ -1204,37 +1204,35 @@ Schema => SemanticDB v4 Uri => Givens.scala Text => empty Language => Scala -Symbols => 31 entries -Occurrences => 72 entries +Symbols => 29 entries +Occurrences => 70 entries Symbols: a/b/Givens. => final object Givens a/b/Givens.Monoid# => trait Monoid a/b/Givens.Monoid#[A] => typeparam A a/b/Givens.Monoid#``(). => primary ctor -a/b/Givens.Monoid#combine(). => abstract method combine -a/b/Givens.Monoid#combine().(x) => param x -a/b/Givens.Monoid#combine().(y) => param y a/b/Givens.Monoid#empty(). => abstract method empty -a/b/Givens.extension_sayGoodbye_B. => final implicit object extension_sayGoodbye_B -a/b/Givens.extension_sayGoodbye_B.sayGoodbye(). => method sayGoodbye -a/b/Givens.extension_sayGoodbye_B.sayGoodbye().(any) => param any -a/b/Givens.extension_sayGoodbye_B.sayGoodbye().[B] => typeparam B -a/b/Givens.extension_sayGoodbye_B.saySoLong(). => method saySoLong -a/b/Givens.extension_sayGoodbye_B.saySoLong().(any) => param any -a/b/Givens.extension_sayGoodbye_B.saySoLong().[B] => typeparam B -a/b/Givens.extension_sayHello_A. => final implicit object extension_sayHello_A -a/b/Givens.extension_sayHello_A.sayHello(). => method sayHello -a/b/Givens.extension_sayHello_A.sayHello().(any) => param any -a/b/Givens.extension_sayHello_A.sayHello().[A] => typeparam A +a/b/Givens.Monoid#extension_combine(). => abstract method extension_combine +a/b/Givens.Monoid#extension_combine().(x) => param x +a/b/Givens.Monoid#extension_combine().(y) => param y +a/b/Givens.extension_sayGoodbye(). => method extension_sayGoodbye +a/b/Givens.extension_sayGoodbye().(any) => param any +a/b/Givens.extension_sayGoodbye().[B] => typeparam B +a/b/Givens.extension_sayHello(). => method extension_sayHello +a/b/Givens.extension_sayHello().(any) => param any +a/b/Givens.extension_sayHello().[A] => typeparam A +a/b/Givens.extension_saySoLong(). => method extension_saySoLong +a/b/Givens.extension_saySoLong().(any) => param any +a/b/Givens.extension_saySoLong().[B] => typeparam B a/b/Givens.foo(). => method foo a/b/Givens.foo().(A) => implicit param A a/b/Givens.foo().[A] => typeparam A a/b/Givens.given_Monoid_String. => final implicit object given_Monoid_String -a/b/Givens.given_Monoid_String.combine(). => method combine -a/b/Givens.given_Monoid_String.combine().(x) => param x -a/b/Givens.given_Monoid_String.combine().(y) => param y a/b/Givens.given_Monoid_String.empty(). => method empty +a/b/Givens.given_Monoid_String.extension_combine(). => method extension_combine +a/b/Givens.given_Monoid_String.extension_combine().(x) => param x +a/b/Givens.given_Monoid_String.extension_combine().(y) => param y a/b/Givens.goodbye1. => val method goodbye1 a/b/Givens.hello1. => val method hello1 a/b/Givens.int2String(). => final implicit macro int2String @@ -1244,58 +1242,56 @@ Occurrences: [0:8..0:9): a <- a/ [1:8..1:9): b <- a/b/ [3:7..3:13): Givens <- a/b/Givens. -[5:12..6:4): <- a/b/Givens.extension_sayHello_A. -[5:16..5:17): A <- a/b/Givens.extension_sayHello_A.sayHello().[A] -[5:19..5:22): any <- a/b/Givens.extension_sayHello_A.sayHello().(any) -[5:24..5:25): A -> a/b/Givens.extension_sayHello_A.sayHello().[A] -[6:8..6:16): sayHello <- a/b/Givens.extension_sayHello_A.sayHello(). +[5:13..5:14): A <- a/b/Givens.extension_sayHello().[A] +[5:16..5:19): any <- a/b/Givens.extension_sayHello().(any) +[5:21..5:22): A -> a/b/Givens.extension_sayHello().[A] +[6:8..6:26): sayHello = s"Hello <- a/b/Givens.extension_sayHello(). [6:21..6:21): -> scala/StringContext.apply(). -[6:34..6:37): any -> a/b/Givens.extension_sayHello_A.sayHello().(any) +[6:34..6:37): any -> a/b/Givens.extension_sayHello().(any) [6:37..6:38): " -> scala/StringContext#s(). -[8:12..9:6): <- a/b/Givens.extension_sayGoodbye_B. -[8:16..8:17): B <- a/b/Givens.extension_sayGoodbye_B.sayGoodbye().[B] -[8:16..8:17): B <- a/b/Givens.extension_sayGoodbye_B.saySoLong().[B] -[8:19..8:22): any <- a/b/Givens.extension_sayGoodbye_B.sayGoodbye().(any) -[8:19..8:22): any <- a/b/Givens.extension_sayGoodbye_B.saySoLong().(any) -[8:24..8:25): B -> a/b/Givens.extension_sayGoodbye_B.sayGoodbye().[B] -[8:24..8:25): B -> a/b/Givens.extension_sayGoodbye_B.saySoLong().[B] -[9:8..9:18): sayGoodbye <- a/b/Givens.extension_sayGoodbye_B.sayGoodbye(). +[8:13..8:14): B <- a/b/Givens.extension_sayGoodbye().[B] +[8:13..8:14): B <- a/b/Givens.extension_saySoLong().[B] +[8:16..8:19): any <- a/b/Givens.extension_sayGoodbye().(any) +[8:16..8:19): any <- a/b/Givens.extension_saySoLong().(any) +[8:21..8:22): B -> a/b/Givens.extension_sayGoodbye().[B] +[8:21..8:22): B -> a/b/Givens.extension_saySoLong().[B] +[9:8..9:28): sayGoodbye = s"Goodb <- a/b/Givens.extension_sayGoodbye(). [9:23..9:23): -> scala/StringContext.apply(). -[9:38..9:41): any -> a/b/Givens.extension_sayGoodbye_B.sayGoodbye().(any) +[9:38..9:41): any -> a/b/Givens.extension_sayGoodbye().(any) [9:41..9:42): " -> scala/StringContext#s(). -[10:8..10:17): saySoLong <- a/b/Givens.extension_sayGoodbye_B.saySoLong(). +[10:8..10:27): saySoLong = s"So Lo <- a/b/Givens.extension_saySoLong(). [10:22..10:22): -> scala/StringContext.apply(). -[10:37..10:40): any -> a/b/Givens.extension_sayGoodbye_B.saySoLong().(any) +[10:37..10:40): any -> a/b/Givens.extension_saySoLong().(any) [10:40..10:41): " -> scala/StringContext#s(). [12:6..12:12): hello1 <- a/b/Givens.hello1. -[12:15..12:15): -> a/b/Givens.extension_sayHello_A.sayHello(). +[12:15..12:15): -> a/b/Givens.extension_sayHello(). [13:6..13:14): goodbye1 <- a/b/Givens.goodbye1. -[13:17..13:17): -> a/b/Givens.extension_sayGoodbye_B.sayGoodbye(). +[13:17..13:17): -> a/b/Givens.extension_sayGoodbye(). [14:6..14:13): soLong1 <- a/b/Givens.soLong1. -[14:16..14:16): -> a/b/Givens.extension_sayGoodbye_B.saySoLong(). +[14:16..14:16): -> a/b/Givens.extension_saySoLong(). [16:8..16:14): Monoid <- a/b/Givens.Monoid# [16:14..16:17): <- a/b/Givens.Monoid#``(). [16:15..16:16): A <- a/b/Givens.Monoid#[A] [17:8..17:13): empty <- a/b/Givens.Monoid#empty(). [17:15..17:16): A -> a/b/Givens.Monoid#[A] -[18:8..18:15): (x: A). <- a/b/Givens.Monoid#combine(). -[18:9..18:10): x <- a/b/Givens.Monoid#combine().(x) -[18:12..18:13): A -> a/b/Givens.Monoid#[A] -[18:23..18:24): y <- a/b/Givens.Monoid#combine().(y) -[18:26..18:27): A -> a/b/Givens.Monoid#[A] -[18:30..18:31): A -> a/b/Givens.Monoid#[A] +[18:15..18:16): x <- a/b/Givens.Monoid#extension_combine().(x) +[18:18..18:19): A -> a/b/Givens.Monoid#[A] +[18:25..19:0): <- a/b/Givens.Monoid#extension_combine(). +[18:33..18:34): y <- a/b/Givens.Monoid#extension_combine().(y) +[18:36..18:37): A -> a/b/Givens.Monoid#[A] +[18:40..18:41): A -> a/b/Givens.Monoid#[A] [20:8..21:3): <- a/b/Givens.given_Monoid_String. [20:8..20:14): Monoid -> a/b/Givens.Monoid# [20:15..20:21): String -> scala/Predef.String# [21:8..21:13): empty <- a/b/Givens.given_Monoid_String.empty(). -[22:8..22:15): (x: Str <- a/b/Givens.given_Monoid_String.combine(). -[22:9..22:10): x <- a/b/Givens.given_Monoid_String.combine().(x) -[22:12..22:18): String -> scala/Predef.String# -[22:28..22:29): y <- a/b/Givens.given_Monoid_String.combine().(y) -[22:31..22:37): String -> scala/Predef.String# -[22:41..22:42): x -> a/b/Givens.given_Monoid_String.combine().(x) -[22:43..22:44): + -> java/lang/String#`+`(). -[22:45..22:46): y -> a/b/Givens.given_Monoid_String.combine().(y) +[22:15..22:16): x <- a/b/Givens.given_Monoid_String.extension_combine().(x) +[22:18..22:24): String -> scala/Predef.String# +[22:30..22:47): combine(y: String <- a/b/Givens.given_Monoid_String.extension_combine(). +[22:38..22:39): y <- a/b/Givens.given_Monoid_String.extension_combine().(y) +[22:41..22:47): String -> scala/Predef.String# +[22:51..22:52): x -> a/b/Givens.given_Monoid_String.extension_combine().(x) +[22:53..22:54): + -> java/lang/String#`+`(). +[22:55..22:56): y -> a/b/Givens.given_Monoid_String.extension_combine().(y) [24:15..24:25): int2String <- a/b/Givens.int2String(). [24:29..24:39): Conversion -> scala/Conversion# [24:40..24:43): Int -> scala/Int# @@ -1308,11 +1304,11 @@ Occurrences: [26:29..26:30): A -> a/b/Givens.foo().[A] [26:34..26:35): A -> a/b/Givens.foo().[A] [26:38..26:39): A -> a/b/Givens.foo().(A) -[26:40..26:47): combine -> a/b/Givens.Monoid#combine(). -[26:48..26:49): A -> a/b/Givens.foo().(A) -[26:50..26:55): empty -> a/b/Givens.Monoid#empty(). -[26:57..26:58): A -> a/b/Givens.foo().(A) -[26:59..26:64): empty -> a/b/Givens.Monoid#empty(). +[26:40..26:57): extension_combine -> a/b/Givens.Monoid#extension_combine(). +[26:58..26:59): A -> a/b/Givens.foo().(A) +[26:60..26:65): empty -> a/b/Givens.Monoid#empty(). +[26:67..26:68): A -> a/b/Givens.foo().(A) +[26:69..26:74): empty -> a/b/Givens.Monoid#empty(). expect/ImplicitConversion.scala ------------------------------- @@ -3865,36 +3861,36 @@ _empty_/toplevel$package.a. => val method a _empty_/toplevel$package.combine(). => method combine _empty_/toplevel$package.combine().(x) => param x _empty_/toplevel$package.combine().(y) => param y +_empty_/toplevel$package.combine().(z) => param z _empty_/toplevel$package.combine(+1). => method combine -_empty_/toplevel$package.combine(+1).(x) => param x -_empty_/toplevel$package.combine(+1).(y) => param y -_empty_/toplevel$package.combine(+1).(z) => param z -_empty_/toplevel$package.combine(+2). => method combine +_empty_/toplevel$package.extension_combine(). => method extension_combine +_empty_/toplevel$package.extension_combine().(x) => param x +_empty_/toplevel$package.extension_combine().(y) => param y _empty_/toplevel$package.foo(). => method foo Occurrences: [0:0..0:16): inline val a = " <- _empty_/toplevel$package. [0:11..0:12): a <- _empty_/toplevel$package.a. -[1:4..1:11): (x: Int <- _empty_/toplevel$package.combine(). -[1:5..1:6): x <- _empty_/toplevel$package.combine().(x) -[1:8..1:11): Int -> scala/Int# -[1:22..1:23): y <- _empty_/toplevel$package.combine().(y) -[1:25..1:28): Int -> scala/Int# -[1:32..1:33): x -> _empty_/toplevel$package.combine().(x) -[1:34..1:35): + -> scala/Int#`+`(+4). -[1:36..1:37): y -> _empty_/toplevel$package.combine().(y) -[2:4..2:11): combine <- _empty_/toplevel$package.combine(+1). -[2:12..2:13): x <- _empty_/toplevel$package.combine(+1).(x) +[1:11..1:12): x <- _empty_/toplevel$package.extension_combine().(x) +[1:14..1:17): Int -> scala/Int# +[1:23..1:40): combine (y: Int) <- _empty_/toplevel$package.extension_combine(). +[1:32..1:33): y <- _empty_/toplevel$package.extension_combine().(y) +[1:35..1:38): Int -> scala/Int# +[1:42..1:43): x -> _empty_/toplevel$package.extension_combine().(x) +[1:44..1:45): + -> scala/Int#`+`(+4). +[1:46..1:47): y -> _empty_/toplevel$package.extension_combine().(y) +[2:4..2:11): combine <- _empty_/toplevel$package.combine(). +[2:12..2:13): x <- _empty_/toplevel$package.combine().(x) [2:15..2:18): Int -> scala/Int# -[2:20..2:21): y <- _empty_/toplevel$package.combine(+1).(y) +[2:20..2:21): y <- _empty_/toplevel$package.combine().(y) [2:23..2:26): Int -> scala/Int# -[2:28..2:29): z <- _empty_/toplevel$package.combine(+1).(z) +[2:28..2:29): z <- _empty_/toplevel$package.combine().(z) [2:31..2:34): Int -> scala/Int# -[2:38..2:39): x -> _empty_/toplevel$package.combine(+1).(x) +[2:38..2:39): x -> _empty_/toplevel$package.combine().(x) [2:40..2:41): + -> scala/Int#`+`(+4). -[2:42..2:43): y -> _empty_/toplevel$package.combine(+1).(y) +[2:42..2:43): y -> _empty_/toplevel$package.combine().(y) [2:44..2:45): + -> scala/Int#`+`(+4). -[2:46..2:47): z -> _empty_/toplevel$package.combine(+1).(z) -[3:4..3:11): combine <- _empty_/toplevel$package.combine(+2). +[2:46..2:47): z -> _empty_/toplevel$package.combine().(z) +[3:4..3:11): combine <- _empty_/toplevel$package.combine(+1). [4:4..4:7): foo <- _empty_/toplevel$package.foo().