diff --git a/compiler/src/dotty/tools/dotc/ast/Desugar.scala b/compiler/src/dotty/tools/dotc/ast/Desugar.scala index cb117d286a02..445426ab4118 100644 --- a/compiler/src/dotty/tools/dotc/ast/Desugar.scala +++ b/compiler/src/dotty/tools/dotc/ast/Desugar.scala @@ -393,6 +393,16 @@ object desugar { vparam.withMods(mods & (GivenOrImplicit | Erased | hasDefault) | Param) } + def mkApply(fn: Tree, paramss: List[ParamClause])(using Context): Tree = + paramss.foldLeft(fn) { (fn, params) => params match + case TypeDefs(params) => + TypeApply(fn, params.map(refOfDef)) + case (vparam: ValDef) :: _ if vparam.mods.is(Given) => + Apply(fn, params.map(refOfDef)).setApplyKind(ApplyKind.Using) + case _ => + Apply(fn, params.map(refOfDef)) + } + /** The expansion of a class definition. See inline comments for what is involved */ def classDef(cdef: TypeDef)(using Context): Tree = { val impl @ Template(constr0, _, self, _) = cdef.rhs @@ -588,7 +598,7 @@ object desugar { } // new C[Ts](paramss) - lazy val creatorExpr = { + lazy val creatorExpr = val vparamss = constrVparamss match case (vparam :: _) :: _ if vparam.mods.is(Implicit) => // add a leading () to match class parameters Nil :: constrVparamss @@ -607,7 +617,6 @@ object desugar { } } ensureApplied(nu) - } val copiedAccessFlags = if migrateTo3 then EmptyFlags else AccessFlags @@ -892,48 +901,50 @@ object desugar { } } + def extMethod(mdef: DefDef, extParamss: List[ParamClause])(using Context): DefDef = + cpy.DefDef(mdef)( + name = normalizeName(mdef, mdef.tpt).asTermName, + paramss = + if mdef.name.isRightAssocOperatorName then + val (typaramss, paramss) = mdef.paramss.span(isTypeParamClause) // first extract type parameters + + paramss match + case params :: paramss1 => // `params` must have a single parameter and without `given` flag + + def badRightAssoc(problem: String) = + report.error(i"right-associative extension method $problem", mdef.srcPos) + extParamss ++ mdef.paramss + + params match + case ValDefs(vparam :: Nil) => + if !vparam.mods.is(Given) then + // we merge the extension parameters with the method parameters, + // swapping the operator arguments: + // e.g. + // extension [A](using B)(c: C)(using D) + // def %:[E](f: F)(g: G)(using H): Res = ??? + // will be encoded as + // def %:[A](using B)[E](f: F)(c: C)(using D)(g: G)(using H): Res = ??? + val (leadingUsing, otherExtParamss) = extParamss.span(isUsingOrTypeParamClause) + leadingUsing ::: typaramss ::: params :: otherExtParamss ::: paramss1 + else + badRightAssoc("cannot start with using clause") + case _ => + badRightAssoc("must start with a single parameter") + case _ => + // no value parameters, so not an infix operator. + extParamss ++ mdef.paramss + else + extParamss ++ mdef.paramss + ).withMods(mdef.mods | ExtensionMethod) + /** Transform extension construct to list of extension methods */ def extMethods(ext: ExtMethods)(using Context): Tree = flatTree { - for mdef <- ext.methods yield - defDef( - cpy.DefDef(mdef)( - name = normalizeName(mdef, ext).asTermName, - paramss = - if mdef.name.isRightAssocOperatorName then - val (typaramss, paramss) = mdef.paramss.span(isTypeParamClause) // first extract type parameters - - paramss match - case params :: paramss1 => // `params` must have a single parameter and without `given` flag - - def badRightAssoc(problem: String) = - report.error(i"right-associative extension method $problem", mdef.srcPos) - ext.paramss ++ mdef.paramss - - params match - case ValDefs(vparam :: Nil) => - if !vparam.mods.is(Given) then - // we merge the extension parameters with the method parameters, - // swapping the operator arguments: - // e.g. - // extension [A](using B)(c: C)(using D) - // def %:[E](f: F)(g: G)(using H): Res = ??? - // will be encoded as - // def %:[A](using B)[E](f: F)(c: C)(using D)(g: G)(using H): Res = ??? - val (leadingUsing, otherExtParamss) = ext.paramss.span(isUsingOrTypeParamClause) - leadingUsing ::: typaramss ::: params :: otherExtParamss ::: paramss1 - else - badRightAssoc("cannot start with using clause") - case _ => - badRightAssoc("must start with a single parameter") - case _ => - // no value parameters, so not an infix operator. - ext.paramss ++ mdef.paramss - else - ext.paramss ++ mdef.paramss - ).withMods(mdef.mods | ExtensionMethod) - ) + ext.methods map { + case exp: Export => exp + case mdef: DefDef => defDef(extMethod(mdef, ext.paramss)) + } } - /** Transforms * * type t >: Low <: Hi diff --git a/compiler/src/dotty/tools/dotc/ast/untpd.scala b/compiler/src/dotty/tools/dotc/ast/untpd.scala index 365b969faffc..7d417b023c14 100644 --- a/compiler/src/dotty/tools/dotc/ast/untpd.scala +++ b/compiler/src/dotty/tools/dotc/ast/untpd.scala @@ -117,7 +117,7 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo { case class GenAlias(pat: Tree, expr: Tree)(implicit @constructorOnly src: SourceFile) extends Tree 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 ExtMethods(paramss: List[ParamClause], methods: List[DefDef])(implicit @constructorOnly src: SourceFile) extends Tree + case class ExtMethods(paramss: List[ParamClause], methods: List[Tree])(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 { @@ -640,7 +640,7 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo { case tree: PatDef if (mods eq tree.mods) && (pats eq tree.pats) && (tpt eq tree.tpt) && (rhs eq tree.rhs) => tree case _ => finalize(tree, untpd.PatDef(mods, pats, tpt, rhs)(tree.source)) } - def ExtMethods(tree: Tree)(paramss: List[ParamClause], methods: List[DefDef])(using Context): Tree = tree match + def ExtMethods(tree: Tree)(paramss: List[ParamClause], methods: List[Tree])(using Context): Tree = tree match case tree: ExtMethods if (paramss eq tree.paramss) && (methods == tree.methods) => tree case _ => finalize(tree, untpd.ExtMethods(paramss, methods)(tree.source)) def ImportSelector(tree: Tree)(imported: Ident, renamed: Tree, bound: Tree)(using Context): Tree = tree match { diff --git a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala index 643cf8c9caca..6cf1d4884dc6 100644 --- a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala +++ b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala @@ -3123,7 +3123,7 @@ object Parsers { /** Import ::= `import' ImportExpr {‘,’ ImportExpr} * Export ::= `export' ImportExpr {‘,’ ImportExpr} */ - def importClause(leading: Token, mkTree: ImportConstr): List[Tree] = { + def importOrExportClause(leading: Token, mkTree: ImportConstr): List[Tree] = { val offset = accept(leading) commaSeparated(importExpr(mkTree)) match { case t :: rest => @@ -3136,6 +3136,12 @@ object Parsers { } } + def exportClause() = + importOrExportClause(EXPORT, Export(_,_)) + + def importClause(outermost: Boolean = false) = + importOrExportClause(IMPORT, mkImport(outermost)) + /** Create an import node and handle source version imports */ def mkImport(outermost: Boolean = false): ImportConstr = (tree, selectors) => val imp = Import(tree, selectors) @@ -3685,8 +3691,10 @@ object Parsers { if in.isColon() then syntaxError("no `:` expected here") in.nextToken() - val methods = - if isDefIntro(modifierTokens) then + val methods: List[Tree] = + if in.token == EXPORT then + exportClause() + else if isDefIntro(modifierTokens) then extMethod(nparams) :: Nil else in.observeIndented() @@ -3696,12 +3704,13 @@ object Parsers { val result = atSpan(start)(ExtMethods(joinParams(tparams, leadParamss.toList), methods)) val comment = in.getDocComment(start) if comment.isDefined then - for meth <- methods do + for case meth: DefDef <- methods do if !meth.rawComment.isDefined then meth.setComment(comment) result end extension /** ExtMethod ::= {Annotation [nl]} {Modifier} ‘def’ DefDef + * | Export */ def extMethod(numLeadParams: Int): DefDef = val start = in.offset @@ -3711,16 +3720,18 @@ object Parsers { /** ExtMethods ::= ExtMethod | [nl] ‘{’ ExtMethod {semi ExtMethod ‘}’ */ - def extMethods(numLeadParams: Int): List[DefDef] = checkNoEscapingPlaceholders { - val meths = new ListBuffer[DefDef] + def extMethods(numLeadParams: Int): List[Tree] = checkNoEscapingPlaceholders { + val meths = new ListBuffer[Tree] while val start = in.offset - val mods = defAnnotsMods(modifierTokens) - in.token != EOF && { - accept(DEF) - meths += defDefOrDcl(start, mods, numLeadParams) - in.token != EOF && statSepOrEnd(meths, what = "extension method") - } + if in.token == EXPORT then + meths ++= exportClause() + else + val mods = defAnnotsMods(modifierTokens) + if in.token != EOF then + accept(DEF) + meths += defDefOrDcl(start, mods, numLeadParams) + in.token != EOF && statSepOrEnd(meths, what = "extension method") do () if meths.isEmpty then syntaxErrorOrIncomplete("`def` expected") meths.toList @@ -3868,9 +3879,9 @@ object Parsers { else stats += packaging(start) } else if (in.token == IMPORT) - stats ++= importClause(IMPORT, mkImport(outermost)) + stats ++= importClause(outermost) else if (in.token == EXPORT) - stats ++= importClause(EXPORT, Export(_,_)) + stats ++= exportClause() else if isIdent(nme.extension) && followingIsExtension() then stats += extension() else if isDefIntro(modifierTokens) then @@ -3916,9 +3927,9 @@ object Parsers { while var empty = false if (in.token == IMPORT) - stats ++= importClause(IMPORT, mkImport()) + stats ++= importClause() else if (in.token == EXPORT) - stats ++= importClause(EXPORT, Export(_,_)) + stats ++= exportClause() else if isIdent(nme.extension) && followingIsExtension() then stats += extension() else if (isDefIntro(modifierTokensOrCase)) @@ -3994,7 +4005,7 @@ object Parsers { while var empty = false if (in.token == IMPORT) - stats ++= importClause(IMPORT, mkImport()) + stats ++= importClause() else if (isExprIntro) stats += expr(Location.InBlock) else if in.token == IMPLICIT && !in.inModifierPosition() then diff --git a/compiler/src/dotty/tools/dotc/typer/Namer.scala b/compiler/src/dotty/tools/dotc/typer/Namer.scala index 07ac87d5455a..79e015e0015c 100644 --- a/compiler/src/dotty/tools/dotc/typer/Namer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Namer.scala @@ -1070,16 +1070,26 @@ class Namer { typer: Typer => def init(): Context = index(params) - /** The forwarders defined by export `exp` */ - private def exportForwarders(exp: Export)(using Context): List[tpd.MemberDef] = + /** The forwarders defined by export `exp` + * @param pathMethod If it exists, the symbol referenced by the path of an export + * in an extension clause. That symbol is always a companion + * extension method. + */ + private def exportForwarders(exp: Export, pathMethod: Symbol)(using Context): List[tpd.MemberDef] = val buf = new mutable.ListBuffer[tpd.MemberDef] val Export(expr, selectors) = exp if expr.isEmpty then report.error(em"Export selector must have prefix and `.`", exp.srcPos) return Nil - val path = typedAheadExpr(expr, AnySelectionProto) - checkLegalExportPath(path, selectors) + val (path, pathType) = + if pathMethod.exists then + val path = typedAhead(expr, _.withType(pathMethod.termRef)) + (path, pathMethod.info.finalResultType) + else + val path = typedAheadExpr(expr, AnySelectionProto) + checkLegalExportPath(path, selectors) + (path, path.tpe) lazy val wildcardBound = importBound(selectors, isGiven = false) lazy val givenBound = importBound(selectors, isGiven = true) @@ -1087,12 +1097,16 @@ class Namer { typer: Typer => def canForward(mbr: SingleDenotation, alias: TermName): CanForward = { import CanForward.* val sym = mbr.symbol - if !sym.isAccessibleFrom(path.tpe) then + if !sym.isAccessibleFrom(pathType) then No("is not accessible") - else if sym.isConstructor || sym.is(ModuleClass) || sym.is(Bridge) || sym.is(ConstructorProxy) then + else if sym.isConstructor || sym.is(ModuleClass) || sym.is(Bridge) || sym.is(ConstructorProxy) || sym.isAllOf(JavaModule) then Skip else if cls.derivesFrom(sym.owner) && (sym.owner == cls || !sym.is(Deferred)) then No(i"is already a member of $cls") + else if pathMethod.exists && mbr.isType then + No("is a type, so it cannot be exported as extension method") + else if pathMethod.exists && sym.is(ExtensionMethod) then + No("is already an extension method, cannot be exported into another one") else if targets.contains(alias) then No(i"clashes with another export in the same export clause") else if sym.is(Override) then @@ -1101,9 +1115,8 @@ class Namer { typer: Typer => ) match case Some(other) => No(i"overrides ${other.showLocated}, which is already a member of $cls") case None => Yes - else if sym.isAllOf(JavaModule) then - Skip - else Yes + else + Yes } def foreachDefaultGetterOf(sym: TermSymbol, op: TermSymbol => Unit): Unit = @@ -1112,7 +1125,7 @@ class Namer { typer: Typer => if param.isTerm then if param.is(HasDefault) then val getterName = DefaultGetterName(sym.name, n) - val getter = path.tpe.member(DefaultGetterName(sym.name, n)).symbol + val getter = pathType.member(DefaultGetterName(sym.name, n)).symbol assert(getter.exists, i"$path does not have a default getter named $getterName") op(getter.asTerm) n += 1 @@ -1143,7 +1156,7 @@ class Namer { typer: Typer => val forwarder = if mbr.isType then val forwarderName = checkNoConflict(alias.toTypeName, isPrivate = false, span) - var target = path.tpe.select(sym) + var target = pathType.select(sym) if target.typeParams.nonEmpty then target = target.EtaExpand(target.typeParams) newSymbol( @@ -1160,14 +1173,45 @@ class Namer { typer: Typer => case tp: TermRef => tp.termSymbol.is(Private) || refersToPrivate(tp.prefix) case _ => false val (maybeStable, mbrInfo) = - if sym.isStableMember && sym.isPublic && !refersToPrivate(path.tpe) then - (StableRealizable, ExprType(path.tpe.select(sym))) + if sym.isStableMember + && sym.isPublic + && pathType.isStable + && !refersToPrivate(pathType) + then + (StableRealizable, ExprType(pathType.select(sym))) else - (EmptyFlags, mbr.info.ensureMethodic) + def addPathMethodParams(pathType: Type, info: Type): Type = + def defines(pt: Type, pname: Name): Boolean = pt match + case pt: MethodOrPoly => + pt.paramNames.contains(pname) || defines(pt.resType, pname) + case _ => + false + def avoidNameClashes(info: Type): Type = info match + case info: MethodOrPoly => + info.derivedLambdaType( + paramNames = info.paramNames.mapConserve { + pname => if defines(pathType, pname) then pname.freshened else pname + }, + resType = avoidNameClashes(info.resType)) + case info => + info + def wrap(pt: Type, info: Type): Type = pt match + case pt: MethodOrPoly => + pt.derivedLambdaType(resType = wrap(pt.resType, info)) + case _ => + info + wrap(pathType, avoidNameClashes(info)) + + val mbrInfo = + if pathMethod.exists + then addPathMethodParams(pathMethod.info, mbr.info.widenExpr) + else mbr.info.ensureMethodic + (EmptyFlags, mbrInfo) var flagMask = RetainedExportFlags if sym.isTerm then flagMask |= HasDefaultParams | NoDefaultParams var mbrFlags = Exported | Method | Final | maybeStable | sym.flags & flagMask - if sym.is(ExtensionMethod) then mbrFlags |= ExtensionMethod + if sym.is(ExtensionMethod) || pathMethod.exists then + mbrFlags |= ExtensionMethod val forwarderName = checkNoConflict(alias, isPrivate = false, span) newSymbol(cls, forwarderName, mbrFlags, mbrInfo, coord = span) @@ -1178,9 +1222,14 @@ class Namer { typer: Typer => buf += tpd.TypeDef(forwarder.asType).withSpan(span) else import tpd._ - val ref = path.select(sym.asTerm) - val ddef = tpd.DefDef(forwarder.asTerm, prefss => - ref.appliedToArgss(adaptForwarderParams(Nil, sym.info, prefss))) + def extensionParamsCount(pt: Type): Int = pt match + case pt: MethodOrPoly => 1 + extensionParamsCount(pt.resType) + case _ => 0 + val ddef = tpd.DefDef(forwarder.asTerm, prefss => { + val (pathRefss, methRefss) = prefss.splitAt(extensionParamsCount(path.tpe.widen)) + val ref = path.appliedToArgss(pathRefss).select(sym.asTerm) + ref.appliedToArgss(adaptForwarderParams(Nil, sym.info, methRefss)) + }) if forwarder.isInlineMethod then PrepareInlineable.registerInlineInfo(forwarder, ddef.rhs) buf += ddef.withSpan(span) @@ -1192,26 +1241,27 @@ 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).flatMap(pathType.member(_).alternatives) mbrs.foreach(addForwarder(alias, _, span)) - targets += alias if buf.size == size then val reason = mbrs.map(canForward(_, alias)).collect { case CanForward.No(whyNot) => i"\n$path.$name cannot be exported because it $whyNot" }.headOption.getOrElse("") report.error(i"""no eligible member $name at $path$reason""", ctx.source.atSpan(span)) + else + targets += alias def addWildcardForwardersNamed(name: TermName, span: Span): Unit = List(name, name.toTypeName) - .flatMap(path.tpe.memberBasedOnFlags(_, excluded = Private|Given|ConstructorProxy).alternatives) + .flatMap(pathType.memberBasedOnFlags(_, excluded = Private|Given|ConstructorProxy).alternatives) .foreach(addForwarder(name, _, span)) // ignore if any are not added def addWildcardForwarders(seen: List[TermName], span: Span): Unit = val nonContextual = mutable.HashSet(seen: _*) - val fromCaseClass = path.tpe.widen.classSymbols.exists(_.is(Case)) + val fromCaseClass = pathType.widen.classSymbols.exists(_.is(Case)) def isCaseClassSynthesized(mbr: Symbol) = fromCaseClass && defn.caseClassSynthesized.contains(mbr) - for mbr <- path.tpe.membersBasedOnFlags(required = EmptyFlags, excluded = PrivateOrSynthetic) do + for mbr <- pathType.membersBasedOnFlags(required = EmptyFlags, excluded = PrivateOrSynthetic) do if !mbr.symbol.isSuperAccessor // Scala 2 superaccessors have neither Synthetic nor Artfact set, so we // need to filter them out here (by contrast, Scala 3 superaccessors are Artifacts) @@ -1304,20 +1354,49 @@ class Namer { typer: Typer => /** Add forwarders as required by the export statements in this class */ private def processExports(using Context): Unit = + def processExport(exp: Export, pathSym: Symbol)(using Context): Unit = + for forwarder <- exportForwarders(exp, pathSym) do + forwarder.symbol.entered + + def exportPathSym(path: Tree, ext: ExtMethods)(using Context): Symbol = + def fail(msg: String): Symbol = + report.error(em"export qualifier $msg", path.srcPos) + NoSymbol + path match + case Ident(name) => + def matches(cand: Tree) = cand match + case meth: DefDef => meth.name == name && meth.paramss.hasSameLengthAs(ext.paramss) + case _ => false + expanded(ext).toList.filter(matches) match + case cand :: Nil => symbolOfTree(cand) + case Nil => fail(i"$name is not a parameterless companion extension method") + case _ => fail(i"$name cannot be overloaded") + case _ => + fail("must be a simple reference to a companion extension method") + def process(stats: List[Tree])(using Context): Unit = stats match case (stat: Export) :: stats1 => - for forwarder <- exportForwarders(stat) do - forwarder.symbol.entered + processExport(stat, NoSymbol) process(stats1) case (stat: Import) :: stats1 => process(stats1)(using ctx.importContext(stat, symbolOfTree(stat))) + case (stat: ExtMethods) :: stats1 => + for case exp: Export <- stat.methods do + val pathSym = exportPathSym(exp.expr, stat) + if pathSym.exists then processExport(exp, pathSym) + process(stats1) case stat :: stats1 => process(stats1) case Nil => + def hasExport(stats: List[Tree]): Boolean = stats.exists { + case _: Export => true + case ExtMethods(_, stats1) => hasExport(stats1) + case _ => false + } // Do a quick scan whether we need to process at all. This avoids creating // import contexts for nothing. - if rest.exists(_.isInstanceOf[Export]) then + if hasExport(rest) then process(rest) end processExports diff --git a/compiler/src/dotty/tools/dotc/typer/ReTyper.scala b/compiler/src/dotty/tools/dotc/typer/ReTyper.scala index 4798563fab43..385d71b570ca 100644 --- a/compiler/src/dotty/tools/dotc/typer/ReTyper.scala +++ b/compiler/src/dotty/tools/dotc/typer/ReTyper.scala @@ -73,6 +73,9 @@ class ReTyper(nestingLevel: Int = 0) extends Typer(nestingLevel) with ReChecking override def typedRefinedTypeTree(tree: untpd.RefinedTypeTree)(using Context): TypTree = promote(TypeTree(tree.tpe).withSpan(tree.span)) + override def typedExport(exp: untpd.Export)(using Context): Export = + promote(exp) + override def typedBind(tree: untpd.Bind, pt: Type)(using Context): Bind = { assertTyped(tree) val body1 = typed(tree.body, pt) diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index 0af8ee8dc624..659b1342698e 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -2616,11 +2616,13 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer val selectors1 = typedSelectors(imp.selectors) assignType(cpy.Import(imp)(expr1, selectors1), sym) - def typedExport(exp: untpd.Export)(using Context): Export = - val expr1 = typedExpr(exp.expr, AnySelectionProto) - // already called `checkLegalExportPath` in Namer - val selectors1 = typedSelectors(exp.selectors) - assignType(cpy.Export(exp)(expr1, selectors1)) + def typedExport(exp: untpd.Export)(using Context): Tree = + exp.expr.removeAttachment(TypedAhead) match + case Some(expr1) => + val selectors1 = typedSelectors(exp.selectors) + assignType(cpy.Export(exp)(expr1, selectors1)) + case _ => + errorTree(exp, em"exports are only allowed from objects and classes, they can not belong to local blocks") def typedPackageDef(tree: untpd.PackageDef)(using Context): Tree = val pid1 = withMode(Mode.InPackageClauseName)(typedExpr(tree.pid, AnySelectionProto)) diff --git a/docs/_docs/internals/syntax.md b/docs/_docs/internals/syntax.md index 7eba33f29ef1..cae2ebae7af2 100644 --- a/docs/_docs/internals/syntax.md +++ b/docs/_docs/internals/syntax.md @@ -10,8 +10,8 @@ _Unicode escapes_ are used to represent the [Unicode character](https://www.w3.o hexadecimal code: ```ebnf -UnicodeEscape ::= ‘\’ ‘u’ {‘u’} hexDigit hexDigit hexDigit hexDigit ; -hexDigit ::= ‘0’ | … | ‘9’ | ‘A’ | … | ‘F’ | ‘a’ | … | ‘f’ ; +UnicodeEscape ::= ‘\’ ‘u’ {‘u’} hexDigit hexDigit hexDigit hexDigit +hexDigit ::= ‘0’ | … | ‘9’ | ‘A’ | … | ‘F’ | ‘a’ | … | ‘f’ ``` Informal descriptions are typeset as `“some comment”`. @@ -22,69 +22,69 @@ The lexical syntax of Scala is given by the following grammar in EBNF form. ```ebnf -whiteSpace ::= ‘\u0020’ | ‘\u0009’ | ‘\u000D’ | ‘\u000A’ ; -upper ::= ‘A’ | … | ‘Z’ | ‘\$’ | ‘_’ “… and Unicode category Lu” ; -lower ::= ‘a’ | … | ‘z’ “… and Unicode category Ll” ; -letter ::= upper | lower “… and Unicode categories Lo, Lt, Lm, Nl” ; -digit ::= ‘0’ | … | ‘9’ ; -paren ::= ‘(’ | ‘)’ | ‘[’ | ‘]’ | ‘{’ | ‘}’ | ‘'(’ | ‘'[’ | ‘'{’ ; -delim ::= ‘`’ | ‘'’ | ‘"’ | ‘.’ | ‘;’ | ‘,’ ; +whiteSpace ::= ‘\u0020’ | ‘\u0009’ | ‘\u000D’ | ‘\u000A’ +upper ::= ‘A’ | … | ‘Z’ | ‘\$’ | ‘_’ “… and Unicode category Lu” +lower ::= ‘a’ | … | ‘z’ “… and Unicode category Ll” +letter ::= upper | lower “… and Unicode categories Lo, Lt, Lm, Nl” +digit ::= ‘0’ | … | ‘9’ +paren ::= ‘(’ | ‘)’ | ‘[’ | ‘]’ | ‘{’ | ‘}’ | ‘'(’ | ‘'[’ | ‘'{’ +delim ::= ‘`’ | ‘'’ | ‘"’ | ‘.’ | ‘;’ | ‘,’ opchar ::= ‘!’ | ‘#’ | ‘%’ | ‘&’ | ‘*’ | ‘+’ | ‘-’ | ‘/’ | ‘:’ | ‘<’ | ‘=’ | ‘>’ | ‘?’ | ‘@’ | ‘\’ | ‘^’ | ‘|’ | ‘~’ - “… and Unicode categories Sm, So” ; -printableChar ::= “all characters in [\u0020, \u007E] inclusive” ; -charEscapeSeq ::= ‘\’ (‘b’ | ‘t’ | ‘n’ | ‘f’ | ‘r’ | ‘"’ | ‘'’ | ‘\’) ; + “… and Unicode categories Sm, So” +printableChar ::= “all characters in [\u0020, \u007E] inclusive” +charEscapeSeq ::= ‘\’ (‘b’ | ‘t’ | ‘n’ | ‘f’ | ‘r’ | ‘"’ | ‘'’ | ‘\’) -op ::= opchar {opchar} ; -varid ::= lower idrest ; +op ::= opchar {opchar} +varid ::= lower idrest alphaid ::= upper idrest - | varid ; + | varid plainid ::= alphaid - | op ; + | op id ::= plainid - | ‘`’ { charNoBackQuoteOrNewline | UnicodeEscape | charEscapeSeq } ‘`’ ; -idrest ::= {letter | digit} [‘_’ op] ; -quoteId ::= ‘'’ alphaid ; + | ‘`’ { charNoBackQuoteOrNewline | UnicodeEscape | charEscapeSeq } ‘`’ +idrest ::= {letter | digit} [‘_’ op] +quoteId ::= ‘'’ alphaid -integerLiteral ::= (decimalNumeral | hexNumeral) [‘L’ | ‘l’] ; -decimalNumeral ::= ‘0’ | nonZeroDigit [{digit | ‘_’} digit] ; -hexNumeral ::= ‘0’ (‘x’ | ‘X’) hexDigit [{hexDigit | ‘_’} hexDigit] ; -nonZeroDigit ::= ‘1’ | … | ‘9’ ; +integerLiteral ::= (decimalNumeral | hexNumeral) [‘L’ | ‘l’] +decimalNumeral ::= ‘0’ | nonZeroDigit [{digit | ‘_’} digit] +hexNumeral ::= ‘0’ (‘x’ | ‘X’) hexDigit [{hexDigit | ‘_’} hexDigit] +nonZeroDigit ::= ‘1’ | … | ‘9’ floatingPointLiteral ::= [decimalNumeral] ‘.’ digit [{digit | ‘_’} digit] [exponentPart] [floatType] | decimalNumeral exponentPart [floatType] - | decimalNumeral floatType ; -exponentPart ::= (‘E’ | ‘e’) [‘+’ | ‘-’] digit [{digit | ‘_’} digit] ; -floatType ::= ‘F’ | ‘f’ | ‘D’ | ‘d’ ; + | decimalNumeral floatType +exponentPart ::= (‘E’ | ‘e’) [‘+’ | ‘-’] digit [{digit | ‘_’} digit] +floatType ::= ‘F’ | ‘f’ | ‘D’ | ‘d’ -booleanLiteral ::= ‘true’ | ‘false’ ; +booleanLiteral ::= ‘true’ | ‘false’ -characterLiteral ::= ‘'’ (printableChar | charEscapeSeq) ‘'’ ; +characterLiteral ::= ‘'’ (printableChar | charEscapeSeq) ‘'’ stringLiteral ::= ‘"’ {stringElement} ‘"’ - | ‘"""’ multiLineChars ‘"""’ ; + | ‘"""’ multiLineChars ‘"""’ stringElement ::= printableChar \ (‘"’ | ‘\’) | UnicodeEscape - | charEscapeSeq ; -multiLineChars ::= {[‘"’] [‘"’] char \ ‘"’} {‘"’} ; + | charEscapeSeq +multiLineChars ::= {[‘"’] [‘"’] char \ ‘"’} {‘"’} processedStringLiteral ::= alphaid ‘"’ {[‘\’] processedStringPart | ‘\\’ | ‘\"’} ‘"’ - | alphaid ‘"""’ {[‘"’] [‘"’] char \ (‘"’ | ‘$’) | escape} {‘"’} ‘"""’ ; + | alphaid ‘"""’ {[‘"’] [‘"’] char \ (‘"’ | ‘$’) | escape} {‘"’} ‘"""’ processedStringPart - ::= printableChar \ (‘"’ | ‘$’ | ‘\’) | escape ; + ::= printableChar \ (‘"’ | ‘$’ | ‘\’) | escape escape ::= ‘$$’ | ‘$’ letter { letter | digit } - | ‘{’ Block [‘;’ whiteSpace stringFormat whiteSpace] ‘}’ ; -stringFormat ::= {printableChar \ (‘"’ | ‘}’ | ‘ ’ | ‘\t’ | ‘\n’)} ; + | ‘{’ Block [‘;’ whiteSpace stringFormat whiteSpace] ‘}’ +stringFormat ::= {printableChar \ (‘"’ | ‘}’ | ‘ ’ | ‘\t’ | ‘\n’)} -symbolLiteral ::= ‘'’ plainid // until 2.13 ; +symbolLiteral ::= ‘'’ plainid // until 2.13 comment ::= ‘/*’ “any sequence of characters; nested comments are allowed” ‘*/’ - | ‘//’ “any sequence of characters up to end of line” ; + | ‘//’ “any sequence of characters up to end of line” -nl ::= “new line character” ; -semi ::= ‘;’ | nl {nl} ; +nl ::= “new line character” +semi ::= ‘;’ | nl {nl} ``` @@ -100,9 +100,9 @@ a `:` at the end of a line. ``` <<< ts >>> ::= ‘{’ ts ‘}’ - | indent ts outdent ; + | indent ts outdent :<<< ts >>> ::= [nl] ‘{’ ts ‘}’ - | `:` indent ts outdent ; + | `:` indent ts outdent ``` ## Keywords @@ -140,20 +140,20 @@ SimpleLiteral ::= [‘-’] integerLiteral | [‘-’] floatingPointLiteral | booleanLiteral | characterLiteral - | stringLiteral ; + | stringLiteral Literal ::= SimpleLiteral | processedStringLiteral | symbolLiteral - | ‘null’ ; + | ‘null’ -QualId ::= id {‘.’ id} ; -ids ::= id {‘,’ id} ; +QualId ::= id {‘.’ id} +ids ::= id {‘,’ id} SimpleRef ::= id | [id ‘.’] ‘this’ - | [id ‘.’] ‘super’ [ClassQualifier] ‘.’ id ; + | [id ‘.’] ‘super’ [ClassQualifier] ‘.’ id -ClassQualifier ::= ‘[’ id ‘]’ ; +ClassQualifier ::= ‘[’ id ‘]’ ``` ### Types @@ -162,22 +162,22 @@ Type ::= FunType | HkTypeParamClause ‘=>>’ Type LambdaTypeTree(ps, t) | FunParamClause ‘=>>’ Type TermLambdaTypeTree(ps, t) | MatchType - | InfixType ; + | InfixType FunType ::= FunTypeArgs (‘=>’ | ‘?=>’) Type Function(ts, t) - | HKTypeParamClause '=>' Type PolyFunction(ps, t) ; + | HKTypeParamClause '=>' Type PolyFunction(ps, t) FunTypeArgs ::= InfixType | ‘(’ [ FunArgTypes ] ‘)’ - | FunParamClause ; -FunParamClause ::= ‘(’ TypedFunParam {‘,’ TypedFunParam } ‘)’ ; -TypedFunParam ::= id ‘:’ Type ; -MatchType ::= InfixType `match` <<< TypeCaseClauses >>> ; -InfixType ::= RefinedType {id [nl] RefinedType} InfixOp(t1, op, t2) ; -RefinedType ::= AnnotType {[nl] Refinement} RefinedTypeTree(t, ds) ; -AnnotType ::= SimpleType {Annotation} Annotated(t, annot) ; + | FunParamClause +FunParamClause ::= ‘(’ TypedFunParam {‘,’ TypedFunParam } ‘)’ +TypedFunParam ::= id ‘:’ Type +MatchType ::= InfixType `match` <<< TypeCaseClauses >>> +InfixType ::= RefinedType {id [nl] RefinedType} InfixOp(t1, op, t2) +RefinedType ::= AnnotType {[nl] Refinement} RefinedTypeTree(t, ds) +AnnotType ::= SimpleType {Annotation} Annotated(t, annot) SimpleType ::= SimpleLiteral SingletonTypeTree(l) | ‘?’ TypeBounds - | SimpleType1 ; + | SimpleType1 SimpleType1 ::= id Ident(name) | Singleton ‘.’ id Select(t, name) | Singleton ‘.’ ‘type’ SingletonTypeTree(p) @@ -186,34 +186,34 @@ SimpleType1 ::= id | ‘$’ ‘{’ Block ‘}’ -- unless inside quoted pattern | ‘$’ ‘{’ Pattern ‘}’ -- only inside quoted pattern | SimpleType1 TypeArgs AppliedTypeTree(t, args) - | SimpleType1 ‘#’ id Select(t, name) ; + | SimpleType1 ‘#’ id Select(t, name) Singleton ::= SimpleRef | SimpleLiteral - | Singleton ‘.’ id ; -Singletons ::= Singleton { ‘,’ Singleton } ; + | Singleton ‘.’ id +Singletons ::= Singleton { ‘,’ Singleton } FunArgType ::= Type - | ‘=>’ Type PrefixOp(=>, t) ; -FunArgTypes ::= FunArgType { ‘,’ FunArgType } ; -ParamType ::= [‘=>’] ParamValueType ; -ParamValueType ::= Type [‘*’] PostfixOp(t, "*") ; -TypeArgs ::= ‘[’ Types ‘]’ ts ; -Refinement ::= ‘{’ [RefineDcl] {semi [RefineDcl]} ‘}’ ds ; -TypeBounds ::= [‘>:’ Type] [‘<:’ Type] TypeBoundsTree(lo, hi) ; -TypeParamBounds ::= TypeBounds {‘:’ Type} ContextBounds(typeBounds, tps) ; -Types ::= Type {‘,’ Type} ; + | ‘=>’ Type PrefixOp(=>, t) +FunArgTypes ::= FunArgType { ‘,’ FunArgType } +ParamType ::= [‘=>’] ParamValueType +ParamValueType ::= Type [‘*’] PostfixOp(t, "*") +TypeArgs ::= ‘[’ Types ‘]’ ts +Refinement ::= ‘{’ [RefineDcl] {semi [RefineDcl]} ‘}’ ds +TypeBounds ::= [‘>:’ Type] [‘<:’ Type] TypeBoundsTree(lo, hi) +TypeParamBounds ::= TypeBounds {‘:’ Type} ContextBounds(typeBounds, tps) +Types ::= Type {‘,’ Type} ``` ### Expressions ```ebnf Expr ::= FunParams (‘=>’ | ‘?=>’) Expr Function(args, expr), Function(ValDef([implicit], id, TypeTree(), EmptyTree), expr) | HkTypeParamClause ‘=>’ Expr PolyFunction(ts, expr) - | Expr1 ; + | Expr1 BlockResult ::= FunParams (‘=>’ | ‘?=>’) Block | HkTypeParamClause ‘=>’ Block - | Expr1 ; + | Expr1 FunParams ::= Bindings | id - | ‘_’ ; + | ‘_’ Expr1 ::= [‘inline’] ‘if’ ‘(’ Expr ‘)’ {nl} Expr [[semi] ‘else’ Expr] If(Parens(cond), thenp, elsep?) | [‘inline’] ‘if’ Expr ‘then’ Expr [[semi] ‘else’ Expr] If(cond, thenp, elsep?) | ‘while’ ‘(’ Expr ‘)’ {nl} Expr WhileDo(Parens(cond), body) @@ -227,18 +227,18 @@ Expr1 ::= [‘inline’] ‘if’ ‘(’ Expr ‘)’ {nl} Expr [[ | PrefixOperator SimpleExpr ‘=’ Expr Assign(expr, expr) | SimpleExpr ArgumentExprs ‘=’ Expr Assign(expr, expr) | PostfixExpr [Ascription] - | ‘inline’ InfixExpr MatchClause ; + | ‘inline’ InfixExpr MatchClause Ascription ::= ‘:’ InfixType Typed(expr, tp) - | ‘:’ Annotation {Annotation} Typed(expr, Annotated(EmptyTree, annot)*) ; -Catches ::= ‘catch’ (Expr | ExprCaseClause) ; -PostfixExpr ::= InfixExpr [id] PostfixOp(expr, op) ; + | ‘:’ Annotation {Annotation} Typed(expr, Annotated(EmptyTree, annot)*) +Catches ::= ‘catch’ (Expr | ExprCaseClause) +PostfixExpr ::= InfixExpr [id] PostfixOp(expr, op) InfixExpr ::= PrefixExpr | InfixExpr id [nl] InfixExpr InfixOp(expr, op, expr) | InfixExpr id ‘:’ IndentedExpr - | InfixExpr MatchClause ; -MatchClause ::= ‘match’ <<< CaseClauses >>> Match(expr, cases) ; -PrefixExpr ::= [PrefixOperator] SimpleExpr PrefixOp(expr, op) ; -PrefixOperator ::= ‘-’ | ‘+’ | ‘~’ | ‘!’ ; + | InfixExpr MatchClause +MatchClause ::= ‘match’ <<< CaseClauses >>> Match(expr, cases) +PrefixExpr ::= [PrefixOperator] SimpleExpr PrefixOp(expr, op) +PrefixOperator ::= ‘-’ | ‘+’ | ‘~’ | ‘!’ SimpleExpr ::= SimpleRef | Literal | ‘_’ @@ -257,180 +257,181 @@ SimpleExpr ::= SimpleRef | SimpleExpr ‘:’ IndentedExpr -- under language.experimental.fewerBraces | SimpleExpr FunParams (‘=>’ | ‘?=>’) IndentedExpr -- under language.experimental.fewerBraces | SimpleExpr ‘_’ PostfixOp(expr, _) (to be dropped) - | XmlExpr -- to be dropped ; -IndentedExpr ::= indent CaseClauses | Block outdent ; -Quoted ::= ‘'’ ‘{’ Block ‘}’ - | ‘'’ ‘[’ Type ‘]’ ; -ExprsInParens ::= ExprInParens {‘,’ ExprInParens} ; -ExprInParens ::= PostfixExpr ‘:’ Type -- normal Expr allows only RefinedType here ; + | XmlExpr -- to be dropped +IndentedExpr ::= indent CaseClauses | Block outdent +Quoted ::= ‘'’ ‘{’ Block ‘}’ + | ‘'’ ‘[’ Type ‘]’ +ExprsInParens ::= ExprInParens {‘,’ ExprInParens} +ExprInParens ::= PostfixExpr ‘:’ Type -- normal Expr allows only RefinedType here | Expr ParArgumentExprs ::= ‘(’ [‘using’] ExprsInParens ‘)’ exprs - | ‘(’ [ExprsInParens ‘,’] PostfixExpr ‘*’ ‘)’ exprs :+ Typed(expr, Ident(wildcardStar)) ; + | ‘(’ [ExprsInParens ‘,’] PostfixExpr ‘*’ ‘)’ exprs :+ Typed(expr, Ident(wildcardStar)) ArgumentExprs ::= ParArgumentExprs - | BlockExpr ; -BlockExpr ::= <<< CaseClauses | Block >>> ; -Block ::= {BlockStat semi} [BlockResult] Block(stats, expr?) ; + | BlockExpr +BlockExpr ::= <<< CaseClauses | Block >>> +Block ::= {BlockStat semi} [BlockResult] Block(stats, expr?) BlockStat ::= Import | {Annotation {nl}} {LocalModifier} Def | Extension | Expr1 - | EndMarker ; + | EndMarker ForExpr ::= ‘for’ ‘(’ Enumerators0 ‘)’ {nl} [‘do‘ | ‘yield’] Expr ForYield(enums, expr) / ForDo(enums, expr) | ‘for’ ‘{’ Enumerators0 ‘}’ {nl} [‘do‘ | ‘yield’] Expr - | ‘for’ Enumerators0 (‘do‘ | ‘yield’) Expr ; -Enumerators0 ::= {nl} Enumerators [semi] ; -Enumerators ::= Generator {semi Enumerator | Guard} ; + | ‘for’ Enumerators0 (‘do‘ | ‘yield’) Expr +Enumerators0 ::= {nl} Enumerators [semi] +Enumerators ::= Generator {semi Enumerator | Guard} Enumerator ::= Generator | Guard {Guard} - | Pattern1 ‘=’ Expr GenAlias(pat, expr) ; -Generator ::= [‘case’] Pattern1 ‘<-’ Expr GenFrom(pat, expr) ; -Guard ::= ‘if’ PostfixExpr ; - -CaseClauses ::= CaseClause { CaseClause } Match(EmptyTree, cases) ; -CaseClause ::= ‘case’ Pattern [Guard] ‘=>’ Block CaseDef(pat, guard?, block) // block starts at => ; -ExprCaseClause ::= ‘case’ Pattern [Guard] ‘=>’ Expr ; -TypeCaseClauses ::= TypeCaseClause { TypeCaseClause } ; -TypeCaseClause ::= ‘case’ (InfixType | ‘_’) ‘=>’ Type [semi] ; - -Pattern ::= Pattern1 { ‘|’ Pattern1 } Alternative(pats) ; -Pattern1 ::= Pattern2 [‘:’ RefinedType] Bind(name, Typed(Ident(wildcard), tpe)) ; -Pattern2 ::= [id ‘@’] InfixPattern Bind(name, pat) ; -InfixPattern ::= SimplePattern { id [nl] SimplePattern } InfixOp(pat, op, pat) ; + | Pattern1 ‘=’ Expr GenAlias(pat, expr) +Generator ::= [‘case’] Pattern1 ‘<-’ Expr GenFrom(pat, expr) +Guard ::= ‘if’ PostfixExpr + +CaseClauses ::= CaseClause { CaseClause } Match(EmptyTree, cases) +CaseClause ::= ‘case’ Pattern [Guard] ‘=>’ Block CaseDef(pat, guard?, block) // block starts at => +ExprCaseClause ::= ‘case’ Pattern [Guard] ‘=>’ Expr +TypeCaseClauses ::= TypeCaseClause { TypeCaseClause } +TypeCaseClause ::= ‘case’ (InfixType | ‘_’) ‘=>’ Type [semi] + +Pattern ::= Pattern1 { ‘|’ Pattern1 } Alternative(pats) +Pattern1 ::= Pattern2 [‘:’ RefinedType] Bind(name, Typed(Ident(wildcard), tpe)) +Pattern2 ::= [id ‘@’] InfixPattern Bind(name, pat) +InfixPattern ::= SimplePattern { id [nl] SimplePattern } InfixOp(pat, op, pat) SimplePattern ::= PatVar Ident(wildcard) | Literal Bind(name, Ident(wildcard)) | ‘(’ [Patterns] ‘)’ Parens(pats) Tuple(pats) | Quoted | XmlPattern (to be dropped) | SimplePattern1 [TypeArgs] [ArgumentPatterns] - | ‘given’ RefinedType ; + | ‘given’ RefinedType SimplePattern1 ::= SimpleRef - | SimplePattern1 ‘.’ id ; + | SimplePattern1 ‘.’ id PatVar ::= varid - | ‘_’ ; -Patterns ::= Pattern {‘,’ Pattern} ; + | ‘_’ +Patterns ::= Pattern {‘,’ Pattern} ArgumentPatterns ::= ‘(’ [Patterns] ‘)’ Apply(fn, pats) - | ‘(’ [Patterns ‘,’] PatVar ‘*’ ‘)’ ; + | ‘(’ [Patterns ‘,’] PatVar ‘*’ ‘)’ ``` ### Type and Value Parameters ```ebnf -ClsTypeParamClause::= ‘[’ ClsTypeParam {‘,’ ClsTypeParam} ‘]’ ; +ClsTypeParamClause::= ‘[’ ClsTypeParam {‘,’ ClsTypeParam} ‘]’ ClsTypeParam ::= {Annotation} [‘+’ | ‘-’] TypeDef(Modifiers, name, tparams, bounds) - id [HkTypeParamClause] TypeParamBounds Bound(below, above, context) ; + id [HkTypeParamClause] TypeParamBounds Bound(below, above, context) -DefTypeParamClause::= ‘[’ DefTypeParam {‘,’ DefTypeParam} ‘]’ ; -DefTypeParam ::= {Annotation} id [HkTypeParamClause] TypeParamBounds ; +DefTypeParamClause::= ‘[’ DefTypeParam {‘,’ DefTypeParam} ‘]’ +DefTypeParam ::= {Annotation} id [HkTypeParamClause] TypeParamBounds -TypTypeParamClause::= ‘[’ TypTypeParam {‘,’ TypTypeParam} ‘]’ ; -TypTypeParam ::= {Annotation} id [HkTypeParamClause] TypeBounds ; +TypTypeParamClause::= ‘[’ TypTypeParam {‘,’ TypTypeParam} ‘]’ +TypTypeParam ::= {Annotation} id [HkTypeParamClause] TypeBounds -HkTypeParamClause ::= ‘[’ HkTypeParam {‘,’ HkTypeParam} ‘]’ ; +HkTypeParamClause ::= ‘[’ HkTypeParam {‘,’ HkTypeParam} ‘]’ HkTypeParam ::= {Annotation} [‘+’ | ‘-’] (id [HkTypeParamClause] | ‘_’) - TypeBounds ; + TypeBounds -ClsParamClauses ::= {ClsParamClause} [[nl] ‘(’ [‘implicit’] ClsParams ‘)’] ; +ClsParamClauses ::= {ClsParamClause} [[nl] ‘(’ [‘implicit’] ClsParams ‘)’] ClsParamClause ::= [nl] ‘(’ ClsParams ‘)’ - | [nl] ‘(’ ‘using’ (ClsParams | FunArgTypes) ‘)’ ; -ClsParams ::= ClsParam {‘,’ ClsParam} ; + | [nl] ‘(’ ‘using’ (ClsParams | FunArgTypes) ‘)’ +ClsParams ::= ClsParam {‘,’ ClsParam} ClsParam ::= {Annotation} ValDef(mods, id, tpe, expr) -- point of mods on val/var - [{Modifier} (‘val’ | ‘var’) | ‘inline’] Param ; -Param ::= id ‘:’ ParamType [‘=’ Expr] ; - -DefParamClauses ::= {DefParamClause} [[nl] ‘(’ [‘implicit’] DefParams ‘)’] ; -DefParamClause ::= [nl] ‘(’ DefParams ‘)’ | UsingParamClause ; -UsingParamClause ::= [nl] ‘(’ ‘using’ (DefParams | FunArgTypes) ‘)’ ; -DefParams ::= DefParam {‘,’ DefParam} ; -DefParam ::= {Annotation} [‘inline’] Param ValDef(mods, id, tpe, expr) -- point of mods at id. ; + [{Modifier} (‘val’ | ‘var’) | ‘inline’] Param +Param ::= id ‘:’ ParamType [‘=’ Expr] + +DefParamClauses ::= {DefParamClause} [[nl] ‘(’ [‘implicit’] DefParams ‘)’] +DefParamClause ::= [nl] ‘(’ DefParams ‘)’ | UsingParamClause +UsingParamClause ::= [nl] ‘(’ ‘using’ (DefParams | FunArgTypes) ‘)’ +DefParams ::= DefParam {‘,’ DefParam} +DefParam ::= {Annotation} [‘inline’] Param ValDef(mods, id, tpe, expr) -- point of mods at id. ``` ### Bindings and Imports ```ebnf -Bindings ::= ‘(’ [Binding {‘,’ Binding}] ‘)’ ; -Binding ::= (id | ‘_’) [‘:’ Type] ValDef(_, id, tpe, EmptyTree) ; +Bindings ::= ‘(’ [Binding {‘,’ Binding}] ‘)’ +Binding ::= (id | ‘_’) [‘:’ Type] ValDef(_, id, tpe, EmptyTree) Modifier ::= LocalModifier | AccessModifier | ‘override’ - | ‘opaque’ ; + | ‘opaque’ LocalModifier ::= ‘abstract’ | ‘final’ | ‘sealed’ | ‘open’ | ‘implicit’ | ‘lazy’ - | ‘inline’ ; -AccessModifier ::= (‘private’ | ‘protected’) [AccessQualifier] ; -AccessQualifier ::= ‘[’ id ‘]’ ; + | ‘inline’ +AccessModifier ::= (‘private’ | ‘protected’) [AccessQualifier] +AccessQualifier ::= ‘[’ id ‘]’ -Annotation ::= ‘@’ SimpleType1 {ParArgumentExprs} Apply(tpe, args) ; +Annotation ::= ‘@’ SimpleType1 {ParArgumentExprs} Apply(tpe, args) -Import ::= ‘import’ ImportExpr {‘,’ ImportExpr} ; -Export ::= ‘export’ ImportExpr {‘,’ ImportExpr} ; +Import ::= ‘import’ ImportExpr {‘,’ ImportExpr} +Export ::= ‘export’ ImportExpr {‘,’ ImportExpr} ImportExpr ::= SimpleRef {‘.’ id} ‘.’ ImportSpec Import(expr, sels) - | SimpleRef ‘as’ id Import(EmptyTree, ImportSelector(ref, id)) ; + | SimpleRef ‘as’ id Import(EmptyTree, ImportSelector(ref, id)) ImportSpec ::= NamedSelector | WildcardSelector - | ‘{’ ImportSelectors) ‘}’ ; -NamedSelector ::= id [‘as’ (id | ‘_’)] ; -WildCardSelector ::= ‘*' | ‘given’ [InfixType] ; + | ‘{’ ImportSelectors) ‘}’ +NamedSelector ::= id [‘as’ (id | ‘_’)] +WildCardSelector ::= ‘*' | ‘given’ [InfixType] ImportSelectors ::= NamedSelector [‘,’ ImportSelectors] - | WildCardSelector {‘,’ WildCardSelector} ; + | WildCardSelector {‘,’ WildCardSelector} -EndMarker ::= ‘end’ EndMarkerTag -- when followed by EOL ; +EndMarker ::= ‘end’ EndMarkerTag -- when followed by EOL EndMarkerTag ::= id | ‘if’ | ‘while’ | ‘for’ | ‘match’ | ‘try’ - | ‘new’ | ‘this’ | ‘given’ | ‘extension’ | ‘val’ ; + | ‘new’ | ‘this’ | ‘given’ | ‘extension’ | ‘val’ ``` ### Declarations and Definitions ```ebnf RefineDcl ::= ‘val’ ValDcl | ‘def’ DefDcl - | ‘type’ {nl} TypeDcl ; + | ‘type’ {nl} TypeDcl Dcl ::= RefineDcl - | ‘var’ VarDcl ; -ValDcl ::= ids ‘:’ Type PatDef(_, ids, tpe, EmptyTree) ; -VarDcl ::= ids ‘:’ Type PatDef(_, ids, tpe, EmptyTree) ; -DefDcl ::= DefSig ‘:’ Type DefDef(_, name, tparams, vparamss, tpe, EmptyTree) ; -DefSig ::= id [DefTypeParamClause] DefParamClauses ; + | ‘var’ VarDcl +ValDcl ::= ids ‘:’ Type PatDef(_, ids, tpe, EmptyTree) +VarDcl ::= ids ‘:’ Type PatDef(_, ids, tpe, EmptyTree) +DefDcl ::= DefSig ‘:’ Type DefDef(_, name, tparams, vparamss, tpe, EmptyTree) +DefSig ::= id [DefTypeParamClause] DefParamClauses TypeDcl ::= id [TypeParamClause] {FunParamClause} TypeBounds TypeDefTree(_, name, tparams, bound - [‘=’ Type] ; + [‘=’ Type] Def ::= ‘val’ PatDef | ‘var’ PatDef | ‘def’ DefDef | ‘type’ {nl} TypeDcl - | TmplDef ; + | TmplDef PatDef ::= ids [‘:’ Type] ‘=’ Expr - | Pattern2 [‘:’ Type] ‘=’ Expr PatDef(_, pats, tpe?, expr) ; + | Pattern2 [‘:’ Type] ‘=’ Expr PatDef(_, pats, tpe?, expr) DefDef ::= DefSig [‘:’ Type] ‘=’ Expr DefDef(_, name, tparams, vparamss, tpe, expr) - | ‘this’ DefParamClause DefParamClauses ‘=’ ConstrExpr DefDef(_, , Nil, vparamss, EmptyTree, expr | Block) ; + | ‘this’ DefParamClause DefParamClauses ‘=’ ConstrExpr DefDef(_, , Nil, vparamss, EmptyTree, expr | Block) TmplDef ::= ([‘case’] ‘class’ | ‘trait’) ClassDef | [‘case’] ‘object’ ObjectDef | ‘enum’ EnumDef - | ‘given’ GivenDef ; -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] ; -ObjectDef ::= id [Template] ModuleDef(mods, name, template) // no constructor ; -EnumDef ::= id ClassConstr InheritClauses EnumBody ; -GivenDef ::= [GivenSig] (AnnotType [‘=’ Expr] | StructuralInstance) ; -GivenSig ::= [id] [DefTypeParamClause] {UsingParamClause} ‘:’ -- one of `id`, `DefParamClause`, `UsingParamClause` must be present ; -StructuralInstance ::= ConstrApp {‘with’ ConstrApp} [‘with’ TemplateBody] ; + | ‘given’ GivenDef +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] +ObjectDef ::= id [Template] ModuleDef(mods, name, template) // no constructor +EnumDef ::= id ClassConstr InheritClauses EnumBody +GivenDef ::= [GivenSig] (AnnotType [‘=’ Expr] | StructuralInstance) +GivenSig ::= [id] [DefTypeParamClause] {UsingParamClause} ‘:’ -- one of `id`, `DefParamClause`, `UsingParamClause` must be present +StructuralInstance ::= ConstrApp {‘with’ ConstrApp} [‘with’ TemplateBody] Extension ::= ‘extension’ [DefTypeParamClause] {UsingParamClause} - ‘(’ DefParam ‘)’ {UsingParamClause} ExtMethods ; -ExtMethods ::= ExtMethod | [nl] <<< ExtMethod {semi ExtMethod} >>> ; -ExtMethod ::= {Annotation [nl]} {Modifier} ‘def’ DefDef ; -Template ::= InheritClauses [TemplateBody] ; -InheritClauses ::= [‘extends’ ConstrApps] [‘derives’ QualId {‘,’ QualId}] ; -ConstrApps ::= ConstrApp ({‘,’ ConstrApp} | {‘with’ ConstrApp}) ; -ConstrApp ::= SimpleType1 {Annotation} {ParArgumentExprs} ; + ‘(’ DefParam ‘)’ {UsingParamClause} ExtMethods +ExtMethods ::= ExtMethod | [nl] <<< ExtMethod {semi ExtMethod} >>> +ExtMethod ::= {Annotation [nl]} {Modifier} ‘def’ DefDef + | Export +Template ::= InheritClauses [TemplateBody] +InheritClauses ::= [‘extends’ ConstrApps] [‘derives’ QualId {‘,’ QualId}] +ConstrApps ::= ConstrApp ({‘,’ ConstrApp} | {‘with’ ConstrApp}) +ConstrApp ::= SimpleType1 {Annotation} {ParArgumentExprs} ConstrExpr ::= SelfInvocation - | <<< SelfInvocation {semi BlockStat} >>> ; -SelfInvocation ::= ‘this’ ArgumentExprs {ArgumentExprs} ; + | <<< SelfInvocation {semi BlockStat} >>> +SelfInvocation ::= ‘this’ ArgumentExprs {ArgumentExprs} -TemplateBody ::= :<<< [SelfType] TemplateStat {semi TemplateStat} >>> ; +TemplateBody ::= :<<< [SelfType] TemplateStat {semi TemplateStat} >>> TemplateStat ::= Import | Export | {Annotation [nl]} {Modifier} Def @@ -438,16 +439,16 @@ TemplateStat ::= Import | Extension | Expr1 | EndMarker - | ; + | SelfType ::= id [‘:’ InfixType] ‘=>’ ValDef(_, name, tpt, _) - | ‘this’ ‘:’ InfixType ‘=>’ ; + | ‘this’ ‘:’ InfixType ‘=>’ -EnumBody ::= :<<< [SelfType] EnumStat {semi EnumStat} >>> ; +EnumBody ::= :<<< [SelfType] EnumStat {semi EnumStat} >>> EnumStat ::= TemplateStat - | {Annotation [nl]} {Modifier} EnumCase ; -EnumCase ::= ‘case’ (id ClassConstr [‘extends’ ConstrApps]] | ids) ; + | {Annotation [nl]} {Modifier} EnumCase +EnumCase ::= ‘case’ (id ClassConstr [‘extends’ ConstrApps]] | ids) -TopStats ::= TopStat {semi TopStat} ; +TopStats ::= TopStat {semi TopStat} TopStat ::= Import | Export | {Annotation [nl]} {Modifier} Def @@ -455,9 +456,9 @@ TopStat ::= Import | Packaging | PackageObject | EndMarker - | ; -Packaging ::= ‘package’ QualId :<<< TopStats >>> ; -PackageObject ::= ‘package’ ‘object’ ObjectDef ; + | +Packaging ::= ‘package’ QualId :<<< TopStats >>> +PackageObject ::= ‘package’ ‘object’ ObjectDef -CompilationUnit ::= {‘package’ QualId semi} TopStats ; +CompilationUnit ::= {‘package’ QualId semi} TopStats ``` diff --git a/docs/_docs/reference/other-new-features/export.md b/docs/_docs/reference/other-new-features/export.md index 85f03de4104e..bd2d42e653a1 100644 --- a/docs/_docs/reference/other-new-features/export.md +++ b/docs/_docs/reference/other-new-features/export.md @@ -75,9 +75,23 @@ A member is _eligible_ if all of the following holds: - it is not a constructor, nor the (synthetic) class part of an object, - it is a given instance (declared with `given`) if and only if the export is from a _given selector_. +It is a compile-time error if a simple or renaming selector does not identify +any eligible members. + It is a compile-time error if a simple or renaming selector does not identify any eligible members. -Type members are aliased by type definitions, and term members are aliased by method definitions. Export aliases copy the type and value parameters of the members they refer to. +Type members are aliased by type definitions, and term members are aliased by method definitions. For instance: +```scala +object O: + class C(val x: Int) + def m(c: C): Int = c.x + 1 +export O.* + // generates + // type C = O.C + // def m(c: O.C): Int = O.m(c) +``` + +Export aliases copy the type and value parameters of the members they refer to. Export aliases are always `final`. Aliases of given instances are again defined as givens (and aliases of old-style implicits are `implicit`). Aliases of extensions are again defined as extensions. Aliases of inline methods or values are again defined `inline`. There are no other modifiers that can be given to an alias. This has the following consequences for overriding: - Export aliases cannot be overridden, since they are final. @@ -132,6 +146,34 @@ Export clauses also fill a gap opened by the shift from package objects to top-l of internal compositions available to users of a package. Top-level definitions are not wrapped in a user-defined object, so they can't inherit anything. However, top-level definitions can be export clauses, which supports the facade design pattern in a safer and more flexible way. +## Export Clauses in Extensions + +An export clause may also appear in an extension. + +Example: +```scala +class StringOps(x: String): + def *(n: Int): String = ... + def capitalize: String = ... + +extension (x: String) + def take(n: Int): String = x.substring(0, n) + def drop(n: Int): String = x.substring(n) + private def moreOps = new StringOps(x) + export moreOps.* +``` +In this case the qualifier expression must be an identifier that refers to a unique parameterless extension method in the same extension clause. The export will create +extension methods for all accessible term members +in the result of the qualifier path. For instance, the extension above would be expanded to +```scala +extension (x: String) + def take(n: Int): String = x.substring(0, n) + def drop(n: Int): String = x.substring(n) + private def moreOps = StringOps(x) + def *(n: Int): String = moreOps.*(n) + def capitalize: String = moreOps.capitalize +``` + ## Syntax changes: ``` @@ -139,6 +181,8 @@ TemplateStat ::= ... | Export TopStat ::= ... | Export +ExtMethod ::= ... + | Export Export ::= ‘export’ ImportExpr {‘,’ ImportExpr} ImportExpr ::= SimpleRef {‘.’ id} ‘.’ ImportSpec ImportSpec ::= NamedSelector diff --git a/docs/_docs/reference/syntax.md b/docs/_docs/reference/syntax.md index 57b21659b7c4..d04a7a848062 100644 --- a/docs/_docs/reference/syntax.md +++ b/docs/_docs/reference/syntax.md @@ -10,9 +10,9 @@ referring to the ASCII fragment `\u0000` – `\u007F`. _Unicode escapes_ are used to represent the [Unicode character](https://www.w3.org/International/articles/definitions-characters/) with the given hexadecimal code: -```ebnf -UnicodeEscape ::= ‘\’ ‘u’ {‘u’} hexDigit hexDigit hexDigit hexDigit ; -hexDigit ::= ‘0’ | … | ‘9’ | ‘A’ | … | ‘F’ | ‘a’ | … | ‘f’ ; +``` +UnicodeEscape ::= ‘\’ ‘u’ {‘u’} hexDigit hexDigit hexDigit hexDigit +hexDigit ::= ‘0’ | … | ‘9’ | ‘A’ | … | ‘F’ | ‘a’ | … | ‘f’ ``` Informal descriptions are typeset as `“some comment”`. @@ -22,70 +22,70 @@ Informal descriptions are typeset as `“some comment”`. The lexical syntax of Scala is given by the following grammar in EBNF form. -```ebnf -whiteSpace ::= ‘\u0020’ | ‘\u0009’ | ‘\u000D’ | ‘\u000A’ ; -upper ::= ‘A’ | … | ‘Z’ | ‘\$’ | ‘_’ “… and Unicode category Lu” ; -lower ::= ‘a’ | … | ‘z’ “… and Unicode category Ll” ; -letter ::= upper | lower “… and Unicode categories Lo, Lt, Nl” ; -digit ::= ‘0’ | … | ‘9’ ; -paren ::= ‘(’ | ‘)’ | ‘[’ | ‘]’ | ‘{’ | ‘}’ | ‘'(’ | ‘'[’ | ‘'{’ ; -delim ::= ‘`’ | ‘'’ | ‘"’ | ‘.’ | ‘;’ | ‘,’ ; +``` +whiteSpace ::= ‘\u0020’ | ‘\u0009’ | ‘\u000D’ | ‘\u000A’ +upper ::= ‘A’ | … | ‘Z’ | ‘\$’ | ‘_’ “… and Unicode category Lu” +lower ::= ‘a’ | … | ‘z’ “… and Unicode category Ll” +letter ::= upper | lower “… and Unicode categories Lo, Lt, Nl” +digit ::= ‘0’ | … | ‘9’ +paren ::= ‘(’ | ‘)’ | ‘[’ | ‘]’ | ‘{’ | ‘}’ | ‘'(’ | ‘'[’ | ‘'{’ +delim ::= ‘`’ | ‘'’ | ‘"’ | ‘.’ | ‘;’ | ‘,’ opchar ::= “printableChar not matched by (whiteSpace | upper | lower | letter | digit | paren | delim | opchar | - Unicode_Sm | Unicode_So)” ; -printableChar ::= “all characters in [\u0020, \u007F] inclusive” ; -charEscapeSeq ::= ‘\’ (‘b’ | ‘t’ | ‘n’ | ‘f’ | ‘r’ | ‘"’ | ‘'’ | ‘\’) ; + Unicode_Sm | Unicode_So)” +printableChar ::= “all characters in [\u0020, \u007F] inclusive” +charEscapeSeq ::= ‘\’ (‘b’ | ‘t’ | ‘n’ | ‘f’ | ‘r’ | ‘"’ | ‘'’ | ‘\’) -op ::= opchar {opchar} ; -varid ::= lower idrest ; +op ::= opchar {opchar} +varid ::= lower idrest alphaid ::= upper idrest - | varid ; + | varid plainid ::= alphaid - | op ; + | op id ::= plainid - | ‘`’ { charNoBackQuoteOrNewline | UnicodeEscape | charEscapeSeq } ‘`’ ; -idrest ::= {letter | digit} [‘_’ op] ; -quoteId ::= ‘'’ alphaid ; + | ‘`’ { charNoBackQuoteOrNewline | UnicodeEscape | charEscapeSeq } ‘`’ +idrest ::= {letter | digit} [‘_’ op] +quoteId ::= ‘'’ alphaid -integerLiteral ::= (decimalNumeral | hexNumeral) [‘L’ | ‘l’] ; -decimalNumeral ::= ‘0’ | nonZeroDigit [{digit | ‘_’} digit] ; -hexNumeral ::= ‘0’ (‘x’ | ‘X’) hexDigit [{hexDigit | ‘_’} hexDigit] ; -nonZeroDigit ::= ‘1’ | … | ‘9’ ; +integerLiteral ::= (decimalNumeral | hexNumeral) [‘L’ | ‘l’] +decimalNumeral ::= ‘0’ | nonZeroDigit [{digit | ‘_’} digit] +hexNumeral ::= ‘0’ (‘x’ | ‘X’) hexDigit [{hexDigit | ‘_’} hexDigit] +nonZeroDigit ::= ‘1’ | … | ‘9’ floatingPointLiteral ::= [decimalNumeral] ‘.’ digit [{digit | ‘_’} digit] [exponentPart] [floatType] | decimalNumeral exponentPart [floatType] - | decimalNumeral floatType ; -exponentPart ::= (‘E’ | ‘e’) [‘+’ | ‘-’] digit [{digit | ‘_’} digit] ; -floatType ::= ‘F’ | ‘f’ | ‘D’ | ‘d’ ; + | decimalNumeral floatType +exponentPart ::= (‘E’ | ‘e’) [‘+’ | ‘-’] digit [{digit | ‘_’} digit] +floatType ::= ‘F’ | ‘f’ | ‘D’ | ‘d’ -booleanLiteral ::= ‘true’ | ‘false’ ; +booleanLiteral ::= ‘true’ | ‘false’ -characterLiteral ::= ‘'’ (printableChar | charEscapeSeq) ‘'’ ; +characterLiteral ::= ‘'’ (printableChar | charEscapeSeq) ‘'’ stringLiteral ::= ‘"’ {stringElement} ‘"’ - | ‘"""’ multiLineChars ‘"""’ ; + | ‘"""’ multiLineChars ‘"""’ stringElement ::= printableChar \ (‘"’ | ‘\’) | UnicodeEscape - | charEscapeSeq ; -multiLineChars ::= {[‘"’] [‘"’] char \ ‘"’} {‘"’} ; + | charEscapeSeq +multiLineChars ::= {[‘"’] [‘"’] char \ ‘"’} {‘"’} processedStringLiteral ::= alphaid ‘"’ {[‘\’] processedStringPart | ‘\\’ | ‘\"’} ‘"’ - | alphaid ‘"""’ {[‘"’] [‘"’] char \ (‘"’ | ‘$’) | escape} {‘"’} ‘"""’ ; + | alphaid ‘"""’ {[‘"’] [‘"’] char \ (‘"’ | ‘$’) | escape} {‘"’} ‘"""’ processedStringPart - ::= printableChar \ (‘"’ | ‘$’ | ‘\’) | escape ; + ::= printableChar \ (‘"’ | ‘$’ | ‘\’) | escape escape ::= ‘$$’ | ‘$’ letter { letter | digit } - | ‘{’ Block [‘;’ whiteSpace stringFormat whiteSpace] ‘}’ ; -stringFormat ::= {printableChar \ (‘"’ | ‘}’ | ‘ ’ | ‘\t’ | ‘\n’)} ; + | ‘{’ Block [‘;’ whiteSpace stringFormat whiteSpace] ‘}’ +stringFormat ::= {printableChar \ (‘"’ | ‘}’ | ‘ ’ | ‘\t’ | ‘\n’)} -symbolLiteral ::= ‘'’ plainid // until 2.13 ; +symbolLiteral ::= ‘'’ plainid // until 2.13 comment ::= ‘/*’ “any sequence of characters; nested comments are allowed” ‘*/’ - | ‘//’ “any sequence of characters up to end of line” ; + | ‘//’ “any sequence of characters up to end of line” -nl ::= “new line character” ; -semi ::= ‘;’ | nl {nl} ; +nl ::= “new line character” +semi ::= ‘;’ | nl {nl} ``` ## Optional Braces @@ -97,31 +97,31 @@ to indicate a token sequence `ts` that is either enclosed in a pair of braces `{ notation `:<<< ts >>>` indicates a token sequence `ts` that is either enclosed in a pair of braces `{ ts }` or that constitutes an indented region `indent ts outdent` that follows a `:` at the end of a line. -```ebnf +``` <<< ts >>> ::= ‘{’ ts ‘}’ - | indent ts outdent ; + | indent ts outdent :<<< ts >>> ::= [nl] ‘{’ ts ‘}’ - | `:` indent ts outdent ; + | `:` indent ts outdent ``` ## Keywords ### Regular keywords -```ebnf +``` abstract case catch class def do else enum export extends false final finally for given if implicit import lazy match new null object override package private protected return sealed super then throw trait true try type val var while with yield -: = <- => <: >: # +: = <- => <: :> # @ =>> ?=> ``` ### Soft keywords -```ebnf +``` as derives end extension infix inline opaque open throws transparent using | * + - ``` @@ -135,45 +135,45 @@ The context-free syntax of Scala is given by the following EBNF grammar: ### Literals and Paths -```ebnf +``` SimpleLiteral ::= [‘-’] integerLiteral | [‘-’] floatingPointLiteral | booleanLiteral | characterLiteral - | stringLiteral ; + | stringLiteral Literal ::= SimpleLiteral | processedStringLiteral | symbolLiteral - | ‘null’ ; + | ‘null’ -QualId ::= id {‘.’ id} ; -ids ::= id {‘,’ id} ; +QualId ::= id {‘.’ id} +ids ::= id {‘,’ id} SimpleRef ::= id | [id ‘.’] ‘this’ - | [id ‘.’] ‘super’ [ClassQualifier] ‘.’ id ; + | [id ‘.’] ‘super’ [ClassQualifier] ‘.’ id -ClassQualifier ::= ‘[’ id ‘]’ ; +ClassQualifier ::= ‘[’ id ‘]’ ``` ### Types -```ebnf +``` Type ::= FunType | HkTypeParamClause ‘=>>’ Type | FunParamClause ‘=>>’ Type | MatchType - | InfixType ; + | InfixType FunType ::= FunTypeArgs (‘=>’ | ‘?=>’) Type - | HKTypeParamClause '=>' Type ; + | HKTypeParamClause '=>' Type FunTypeArgs ::= InfixType | ‘(’ [ FunArgTypes ] ‘)’ - | FunParamClause ; -FunParamClause ::= ‘(’ TypedFunParam {‘,’ TypedFunParam } ‘)’ ; -TypedFunParam ::= id ‘:’ Type ; -MatchType ::= InfixType `match` <<< TypeCaseClauses >>> ; -InfixType ::= RefinedType {id [nl] RefinedType} ; -RefinedType ::= AnnotType {[nl] Refinement} ; -AnnotType ::= SimpleType {Annotation} ; + | FunParamClause +FunParamClause ::= ‘(’ TypedFunParam {‘,’ TypedFunParam } ‘)’ +TypedFunParam ::= id ‘:’ Type +MatchType ::= InfixType `match` <<< TypeCaseClauses >>> +InfixType ::= RefinedType {id [nl] RefinedType} +RefinedType ::= AnnotType {[nl] Refinement} +AnnotType ::= SimpleType {Annotation} SimpleType ::= SimpleLiteral | ‘?’ TypeBounds @@ -185,34 +185,34 @@ SimpleType ::= SimpleLiteral | ‘$’ ‘{’ Block ‘}’ -- unless inside quoted pattern | ‘$’ ‘{’ Pattern ‘}’ -- only inside quoted pattern | SimpleType1 TypeArgs - | SimpleType1 ‘#’ id ; + | SimpleType1 ‘#’ id Singleton ::= SimpleRef | SimpleLiteral - | Singleton ‘.’ id ; + | Singleton ‘.’ id FunArgType ::= Type - | ‘=>’ Type ; -FunArgTypes ::= FunArgType { ‘,’ FunArgType } ; -ParamType ::= [‘=>’] ParamValueType ; -ParamValueType ::= Type [‘*’] ; -TypeArgs ::= ‘[’ Types ‘]’ ; -Refinement ::= ‘{’ [RefineDcl] {semi [RefineDcl]} ‘}’ ; -TypeBounds ::= [‘>:’ Type] [‘<:’ Type] ; -TypeParamBounds ::= TypeBounds {‘:’ Type} ; -Types ::= Type {‘,’ Type} ; + | ‘=>’ Type +FunArgTypes ::= FunArgType { ‘,’ FunArgType } +ParamType ::= [‘=>’] ParamValueType +ParamValueType ::= Type [‘*’] +TypeArgs ::= ‘[’ Types ‘]’ +Refinement ::= ‘{’ [RefineDcl] {semi [RefineDcl]} ‘}’ +TypeBounds ::= [‘>:’ Type] [‘<:’ Type] +TypeParamBounds ::= TypeBounds {‘:’ Type} +Types ::= Type {‘,’ Type} ``` ### Expressions -```ebnf +``` Expr ::= FunParams (‘=>’ | ‘?=>’) Expr | HkTypeParamClause ‘=>’ Expr - | Expr1 ; + | Expr1 BlockResult ::= FunParams (‘=>’ | ‘?=>’) Block | HkTypeParamClause ‘=>’ Block - | Expr1 ; + | Expr1 FunParams ::= Bindings | id - | ‘_’ ; + | ‘_’ Expr1 ::= [‘inline’] ‘if’ ‘(’ Expr ‘)’ {nl} Expr [[semi] ‘else’ Expr] | [‘inline’] ‘if’ Expr ‘then’ Expr [[semi] ‘else’ Expr] | ‘while’ ‘(’ Expr ‘)’ {nl} Expr @@ -226,17 +226,17 @@ Expr1 ::= [‘inline’] ‘if’ ‘(’ Expr ‘)’ {nl} Expr [[ | PrefixOperator SimpleExpr ‘=’ Expr | SimpleExpr ArgumentExprs ‘=’ Expr | PostfixExpr [Ascription] - | ‘inline’ InfixExpr MatchClause ; + | ‘inline’ InfixExpr MatchClause Ascription ::= ‘:’ InfixType - | ‘:’ Annotation {Annotation} ; -Catches ::= ‘catch’ (Expr | ExprCaseClause) ; -PostfixExpr ::= InfixExpr [id] -- only if language.postfixOperators is enabled ; + | ‘:’ Annotation {Annotation} +Catches ::= ‘catch’ (Expr | ExprCaseClause) +PostfixExpr ::= InfixExpr [id] -- only if language.postfixOperators is enabled InfixExpr ::= PrefixExpr | InfixExpr id [nl] InfixExpr - | InfixExpr MatchClause ; -MatchClause ::= ‘match’ <<< CaseClauses >>> ; -PrefixExpr ::= [PrefixOperator] SimpleExpr ; -PrefixOperator ::= ‘-’ | ‘+’ | ‘~’ | ‘!’ ; + | InfixExpr MatchClause +MatchClause ::= ‘match’ <<< CaseClauses >>> +PrefixExpr ::= [PrefixOperator] SimpleExpr +PrefixOperator ::= ‘-’ | ‘+’ | ‘~’ | ‘!’ SimpleExpr ::= SimpleRef | Literal | ‘_’ @@ -251,174 +251,175 @@ SimpleExpr ::= SimpleRef | SimpleExpr ‘.’ id | SimpleExpr ‘.’ MatchClause | SimpleExpr TypeArgs - | SimpleExpr ArgumentExprs ; + | SimpleExpr ArgumentExprs Quoted ::= ‘'’ ‘{’ Block ‘}’ - | ‘'’ ‘[’ Type ‘]’ ; -ExprsInParens ::= ExprInParens {‘,’ ExprInParens} ; + | ‘'’ ‘[’ Type ‘]’ +ExprsInParens ::= ExprInParens {‘,’ ExprInParens} ExprInParens ::= PostfixExpr ‘:’ Type - | Expr ; + | Expr ParArgumentExprs ::= ‘(’ [‘using’] ExprsInParens ‘)’ - | ‘(’ [ExprsInParens ‘,’] PostfixExpr ‘*’ ‘)’ ; + | ‘(’ [ExprsInParens ‘,’] PostfixExpr ‘*’ ‘)’ ArgumentExprs ::= ParArgumentExprs - | BlockExpr ; -BlockExpr ::= <<< (CaseClauses | Block) >>> ; -Block ::= {BlockStat semi} [BlockResult] ; + | BlockExpr +BlockExpr ::= <<< (CaseClauses | Block) >>> +Block ::= {BlockStat semi} [BlockResult] BlockStat ::= Import | {Annotation {nl}} {LocalModifier} Def | Extension | Expr1 - | EndMarker ; + | EndMarker ForExpr ::= ‘for’ ‘(’ Enumerators0 ‘)’ {nl} [‘do‘ | ‘yield’] Expr | ‘for’ ‘{’ Enumerators0 ‘}’ {nl} [‘do‘ | ‘yield’] Expr - | ‘for’ Enumerators0 (‘do‘ | ‘yield’) Expr ; -Enumerators0 ::= {nl} Enumerators [semi] ; -Enumerators ::= Generator {semi Enumerator | Guard} ; + | ‘for’ Enumerators0 (‘do‘ | ‘yield’) Expr +Enumerators0 ::= {nl} Enumerators [semi] +Enumerators ::= Generator {semi Enumerator | Guard} Enumerator ::= Generator | Guard {Guard} - | Pattern1 ‘=’ Expr ; -Generator ::= [‘case’] Pattern1 ‘<-’ Expr ; -Guard ::= ‘if’ PostfixExpr ; - -CaseClauses ::= CaseClause { CaseClause } ; -CaseClause ::= ‘case’ Pattern [Guard] ‘=>’ Block ; -ExprCaseClause ::= ‘case’ Pattern [Guard] ‘=>’ Expr ; -TypeCaseClauses ::= TypeCaseClause { TypeCaseClause } ; -TypeCaseClause ::= ‘case’ InfixType ‘=>’ Type [semi] ; - -Pattern ::= Pattern1 { ‘|’ Pattern1 } ; -Pattern1 ::= Pattern2 [‘:’ RefinedType] ; -Pattern2 ::= [id ‘@’] InfixPattern [‘*’] ; -InfixPattern ::= SimplePattern { id [nl] SimplePattern } ; + | Pattern1 ‘=’ Expr +Generator ::= [‘case’] Pattern1 ‘<-’ Expr +Guard ::= ‘if’ PostfixExpr + +CaseClauses ::= CaseClause { CaseClause } +CaseClause ::= ‘case’ Pattern [Guard] ‘=>’ Block +ExprCaseClause ::= ‘case’ Pattern [Guard] ‘=>’ Expr +TypeCaseClauses ::= TypeCaseClause { TypeCaseClause } +TypeCaseClause ::= ‘case’ InfixType ‘=>’ Type [semi] + +Pattern ::= Pattern1 { ‘|’ Pattern1 } +Pattern1 ::= Pattern2 [‘:’ RefinedType] +Pattern2 ::= [id ‘@’] InfixPattern [‘*’] +InfixPattern ::= SimplePattern { id [nl] SimplePattern } SimplePattern ::= PatVar | Literal | ‘(’ [Patterns] ‘)’ | Quoted | SimplePattern1 [TypeArgs] [ArgumentPatterns] - | ‘given’ RefinedType ; + | ‘given’ RefinedType SimplePattern1 ::= SimpleRef - | SimplePattern1 ‘.’ id ; + | SimplePattern1 ‘.’ id PatVar ::= varid - | ‘_’ ; -Patterns ::= Pattern {‘,’ Pattern} ; + | ‘_’ +Patterns ::= Pattern {‘,’ Pattern} ArgumentPatterns ::= ‘(’ [Patterns] ‘)’ - | ‘(’ [Patterns ‘,’] PatVar ‘*’ ‘)’ ; + | ‘(’ [Patterns ‘,’] PatVar ‘*’ ‘)’ ``` ### Type and Value Parameters -```ebnf -ClsTypeParamClause::= ‘[’ ClsTypeParam {‘,’ ClsTypeParam} ‘]’ ; -ClsTypeParam ::= {Annotation} [‘+’ | ‘-’] id [HkTypeParamClause] TypeParamBounds ; +``` +ClsTypeParamClause::= ‘[’ ClsTypeParam {‘,’ ClsTypeParam} ‘]’ +ClsTypeParam ::= {Annotation} [‘+’ | ‘-’] id [HkTypeParamClause] TypeParamBounds -DefTypeParamClause::= ‘[’ DefTypeParam {‘,’ DefTypeParam} ‘]’ ; -DefTypeParam ::= {Annotation} id [HkTypeParamClause] TypeParamBounds ; +DefTypeParamClause::= ‘[’ DefTypeParam {‘,’ DefTypeParam} ‘]’ +DefTypeParam ::= {Annotation} id [HkTypeParamClause] TypeParamBounds -TypTypeParamClause::= ‘[’ TypTypeParam {‘,’ TypTypeParam} ‘]’ ; -TypTypeParam ::= {Annotation} id [HkTypeParamClause] TypeBounds ; +TypTypeParamClause::= ‘[’ TypTypeParam {‘,’ TypTypeParam} ‘]’ +TypTypeParam ::= {Annotation} id [HkTypeParamClause] TypeBounds -HkTypeParamClause ::= ‘[’ HkTypeParam {‘,’ HkTypeParam} ‘]’ ; -HkTypeParam ::= {Annotation} [‘+’ | ‘-’] (id [HkTypeParamClause] | ‘_’) TypeBounds ; +HkTypeParamClause ::= ‘[’ HkTypeParam {‘,’ HkTypeParam} ‘]’ +HkTypeParam ::= {Annotation} [‘+’ | ‘-’] (id [HkTypeParamClause] | ‘_’) TypeBounds -ClsParamClauses ::= {ClsParamClause} [[nl] ‘(’ [‘implicit’] ClsParams ‘)’] ; +ClsParamClauses ::= {ClsParamClause} [[nl] ‘(’ [‘implicit’] ClsParams ‘)’] ClsParamClause ::= [nl] ‘(’ ClsParams ‘)’ - | [nl] ‘(’ ‘using’ (ClsParams | FunArgTypes) ‘)’ ; -ClsParams ::= ClsParam {‘,’ ClsParam} ; -ClsParam ::= {Annotation} [{Modifier} (‘val’ | ‘var’) | ‘inline’] Param ; -Param ::= id ‘:’ ParamType [‘=’ Expr] ; - -DefParamClauses ::= {DefParamClause} [[nl] ‘(’ [‘implicit’] DefParams ‘)’] ; -DefParamClause ::= [nl] ‘(’ DefParams ‘)’ | UsingParamClause ; -UsingParamClause ::= [nl] ‘(’ ‘using’ (DefParams | FunArgTypes) ‘)’ ; -DefParams ::= DefParam {‘,’ DefParam} ; -DefParam ::= {Annotation} [‘inline’] Param ; + | [nl] ‘(’ ‘using’ (ClsParams | FunArgTypes) ‘)’ +ClsParams ::= ClsParam {‘,’ ClsParam} +ClsParam ::= {Annotation} [{Modifier} (‘val’ | ‘var’) | ‘inline’] Param +Param ::= id ‘:’ ParamType [‘=’ Expr] + +DefParamClauses ::= {DefParamClause} [[nl] ‘(’ [‘implicit’] DefParams ‘)’] +DefParamClause ::= [nl] ‘(’ DefParams ‘)’ | UsingParamClause +UsingParamClause ::= [nl] ‘(’ ‘using’ (DefParams | FunArgTypes) ‘)’ +DefParams ::= DefParam {‘,’ DefParam} +DefParam ::= {Annotation} [‘inline’] Param ``` ### Bindings and Imports -```ebnf -Bindings ::= ‘(’ [Binding {‘,’ Binding}] ‘)’ ; -Binding ::= (id | ‘_’) [‘:’ Type] ; +``` +Bindings ::= ‘(’ [Binding {‘,’ Binding}] ‘)’ +Binding ::= (id | ‘_’) [‘:’ Type] Modifier ::= LocalModifier | AccessModifier | ‘override’ - | ‘opaque’ ; + | ‘opaque’ LocalModifier ::= ‘abstract’ | ‘final’ | ‘sealed’ | ‘open’ | ‘implicit’ | ‘lazy’ - | ‘inline’ ; -AccessModifier ::= (‘private’ | ‘protected’) [AccessQualifier] ; -AccessQualifier ::= ‘[’ id ‘]’ ; + | ‘inline’ +AccessModifier ::= (‘private’ | ‘protected’) [AccessQualifier] +AccessQualifier ::= ‘[’ id ‘]’ -Annotation ::= ‘@’ SimpleType1 {ParArgumentExprs} ; +Annotation ::= ‘@’ SimpleType1 {ParArgumentExprs} -Import ::= ‘import’ ImportExpr {‘,’ ImportExpr} ; -Export ::= ‘export’ ImportExpr {‘,’ ImportExpr} ; +Import ::= ‘import’ ImportExpr {‘,’ ImportExpr} +Export ::= ‘export’ ImportExpr {‘,’ ImportExpr} ImportExpr ::= SimpleRef {‘.’ id} ‘.’ ImportSpec - | SimpleRef ‘as’ id ; + | SimpleRef ‘as’ id ImportSpec ::= NamedSelector | WildcardSelector - | ‘{’ ImportSelectors) ‘}’ ; -NamedSelector ::= id [‘as’ (id | ‘_’)] ; -WildCardSelector ::= ‘*' | ‘given’ [InfixType] ; + | ‘{’ ImportSelectors) ‘}’ +NamedSelector ::= id [‘as’ (id | ‘_’)] +WildCardSelector ::= ‘*' | ‘given’ [InfixType] ImportSelectors ::= NamedSelector [‘,’ ImportSelectors] - | WildCardSelector {‘,’ WildCardSelector} ; + | WildCardSelector {‘,’ WildCardSelector} -EndMarker ::= ‘end’ EndMarkerTag -- when followed by EOL ; +EndMarker ::= ‘end’ EndMarkerTag -- when followed by EOL EndMarkerTag ::= id | ‘if’ | ‘while’ | ‘for’ | ‘match’ | ‘try’ - | ‘new’ | ‘this’ | ‘given’ | ‘extension’ | ‘val’ ; + | ‘new’ | ‘this’ | ‘given’ | ‘extension’ | ‘val’ ``` ### Declarations and Definitions -```ebnf +``` RefineDcl ::= ‘val’ ValDcl | ‘def’ DefDcl - | ‘type’ {nl} TypeDcl ; + | ‘type’ {nl} TypeDcl Dcl ::= RefineDcl - | ‘var’ VarDcl ; -ValDcl ::= ids ‘:’ Type ; -VarDcl ::= ids ‘:’ Type ; -DefDcl ::= DefSig ‘:’ Type ; -DefSig ::= id [DefTypeParamClause] DefParamClauses ; -TypeDcl ::= id [TypeParamClause] {FunParamClause} TypeBounds [‘=’ Type] ; + | ‘var’ VarDcl +ValDcl ::= ids ‘:’ Type +VarDcl ::= ids ‘:’ Type +DefDcl ::= DefSig ‘:’ Type +DefSig ::= id [DefTypeParamClause] DefParamClauses +TypeDcl ::= id [TypeParamClause] {FunParamClause} TypeBounds [‘=’ Type] Def ::= ‘val’ PatDef | ‘var’ PatDef | ‘def’ DefDef | ‘type’ {nl} TypeDcl - | TmplDef ; + | TmplDef PatDef ::= ids [‘:’ Type] ‘=’ Expr - | Pattern2 [‘:’ Type] ‘=’ Expr ; + | Pattern2 [‘:’ Type] ‘=’ Expr DefDef ::= DefSig [‘:’ Type] ‘=’ Expr - | ‘this’ DefParamClause DefParamClauses ‘=’ ConstrExpr ; + | ‘this’ DefParamClause DefParamClauses ‘=’ ConstrExpr TmplDef ::= ([‘case’] ‘class’ | ‘trait’) ClassDef | [‘case’] ‘object’ ObjectDef | ‘enum’ EnumDef - | ‘given’ GivenDef ; -ClassDef ::= id ClassConstr [Template] ; -ClassConstr ::= [ClsTypeParamClause] [ConstrMods] ClsParamClauses ; -ConstrMods ::= {Annotation} [AccessModifier] ; -ObjectDef ::= id [Template] ; -EnumDef ::= id ClassConstr InheritClauses EnumBody ; -GivenDef ::= [GivenSig] (AnnotType [‘=’ Expr] | StructuralInstance) ; -GivenSig ::= [id] [DefTypeParamClause] {UsingParamClause} ‘:’ -- one of `id`, `DefParamClause`, `UsingParamClause` must be present ; -StructuralInstance ::= ConstrApp {‘with’ ConstrApp} [‘with’ TemplateBody] ; + | ‘given’ GivenDef +ClassDef ::= id ClassConstr [Template] +ClassConstr ::= [ClsTypeParamClause] [ConstrMods] ClsParamClauses +ConstrMods ::= {Annotation} [AccessModifier] +ObjectDef ::= id [Template] +EnumDef ::= id ClassConstr InheritClauses EnumBody +GivenDef ::= [GivenSig] (AnnotType [‘=’ Expr] | StructuralInstance) +GivenSig ::= [id] [DefTypeParamClause] {UsingParamClause} ‘:’ -- one of `id`, `DefParamClause`, `UsingParamClause` must be present +StructuralInstance ::= ConstrApp {‘with’ ConstrApp} [‘with’ TemplateBody] Extension ::= ‘extension’ [DefTypeParamClause] {UsingParamClause} - ‘(’ DefParam ‘)’ {UsingParamClause} ExtMethods ; -ExtMethods ::= ExtMethod | [nl] <<< ExtMethod {semi ExtMethod} >>> ; -ExtMethod ::= {Annotation [nl]} {Modifier} ‘def’ DefDef ; -Template ::= InheritClauses [TemplateBody] ; -InheritClauses ::= [‘extends’ ConstrApps] [‘derives’ QualId {‘,’ QualId}] ; -ConstrApps ::= ConstrApp ({‘,’ ConstrApp} | {‘with’ ConstrApp}) ; -ConstrApp ::= SimpleType1 {Annotation} {ParArgumentExprs} ; + ‘(’ DefParam ‘)’ {UsingParamClause} ExtMethods +ExtMethods ::= ExtMethod | [nl] <<< ExtMethod {semi ExtMethod} >>> +ExtMethod ::= {Annotation [nl]} {Modifier} ‘def’ DefDef + | Export +Template ::= InheritClauses [TemplateBody] +InheritClauses ::= [‘extends’ ConstrApps] [‘derives’ QualId {‘,’ QualId}] +ConstrApps ::= ConstrApp ({‘,’ ConstrApp} | {‘with’ ConstrApp}) +ConstrApp ::= SimpleType1 {Annotation} {ParArgumentExprs} ConstrExpr ::= SelfInvocation - | <<< SelfInvocation {semi BlockStat} >>> ; -SelfInvocation ::= ‘this’ ArgumentExprs {ArgumentExprs} ; + | <<< SelfInvocation {semi BlockStat} >>> +SelfInvocation ::= ‘this’ ArgumentExprs {ArgumentExprs} -TemplateBody ::= :<<< [SelfType] TemplateStat {semi TemplateStat} >>> ; +TemplateBody ::= :<<< [SelfType] TemplateStat {semi TemplateStat} >>> TemplateStat ::= Import | Export | {Annotation [nl]} {Modifier} Def @@ -426,16 +427,16 @@ TemplateStat ::= Import | Extension | Expr1 | EndMarker - | ; + | SelfType ::= id [‘:’ InfixType] ‘=>’ - | ‘this’ ‘:’ InfixType ‘=>’ ; + | ‘this’ ‘:’ InfixType ‘=>’ -EnumBody ::= :<<< [SelfType] EnumStat {semi EnumStat} >>> ; +EnumBody ::= :<<< [SelfType] EnumStat {semi EnumStat} >>> EnumStat ::= TemplateStat - | {Annotation [nl]} {Modifier} EnumCase ; -EnumCase ::= ‘case’ (id ClassConstr [‘extends’ ConstrApps]] | ids) ; + | {Annotation [nl]} {Modifier} EnumCase +EnumCase ::= ‘case’ (id ClassConstr [‘extends’ ConstrApps]] | ids) -TopStats ::= TopStat {semi TopStat} ; +TopStats ::= TopStat {semi TopStat} TopStat ::= Import | Export | {Annotation [nl]} {Modifier} Def @@ -443,9 +444,9 @@ TopStat ::= Import | Packaging | PackageObject | EndMarker - | ; -Packaging ::= ‘package’ QualId :<<< TopStats >>> ; -PackageObject ::= ‘package’ ‘object’ ObjectDef ; + | +Packaging ::= ‘package’ QualId :<<< TopStats >>> +PackageObject ::= ‘package’ ‘object’ ObjectDef -CompilationUnit ::= {‘package’ QualId semi} TopStats ; +CompilationUnit ::= {‘package’ QualId semi} TopStats ``` diff --git a/tests/neg/export-extension-in-extension.scala b/tests/neg/export-extension-in-extension.scala new file mode 100644 index 000000000000..83c281687d3f --- /dev/null +++ b/tests/neg/export-extension-in-extension.scala @@ -0,0 +1,16 @@ +class StringOps: + extension (x: String) + def capitalize: String = ??? + def foo: String = ??? + + def foo: String = ??? + + +extension (s: String) + private def moreOps = new StringOps() + export moreOps.capitalize // error: no eligible member capitalize at moreOps + +extension (s: String) + private def moreOps2= new StringOps() + export moreOps2.foo // OK + diff --git a/tests/neg/export-in-extension.check b/tests/neg/export-in-extension.check new file mode 100644 index 000000000000..3f510291b304 --- /dev/null +++ b/tests/neg/export-in-extension.check @@ -0,0 +1,29 @@ +-- Error: tests/neg/export-in-extension.scala:14:13 -------------------------------------------------------------------- +14 | export c1.* // error + | ^^ + | export qualifier c1 is not a parameterless companion extension method +-- Error: tests/neg/export-in-extension.scala:19:22 -------------------------------------------------------------------- +19 | export cm.{bar, D} // error + | ^ + | no eligible member D at O.O2.cm + | O.O2.cm.D cannot be exported because it is a type, so it cannot be exported as extension method +-- Error: tests/neg/export-in-extension.scala:20:18 -------------------------------------------------------------------- +20 | export this.cm.baz // error + | ^^^^^^^ + | export qualifier must be a simple reference to a companion extension method +-- Error: tests/neg/export-in-extension.scala:24:13 -------------------------------------------------------------------- +24 | export missing.* // error + | ^^^^^^^ + | export qualifier missing is not a parameterless companion extension method +-- Error: tests/neg/export-in-extension.scala:28:13 -------------------------------------------------------------------- +28 | export cm.* // error + | ^^ + | export qualifier cm is not a parameterless companion extension method +-- Error: tests/neg/export-in-extension.scala:33:13 -------------------------------------------------------------------- +33 | export cm.* // error + | ^^ + | export qualifier cm is not a parameterless companion extension method +-- Error: tests/neg/export-in-extension.scala:38:13 -------------------------------------------------------------------- +38 | export cm.* // error + | ^^^^^^^^^^^ + | exports are only allowed from objects and classes, they can not belong to local blocks diff --git a/tests/neg/export-in-extension.scala b/tests/neg/export-in-extension.scala new file mode 100644 index 000000000000..8bcab0e5ac8d --- /dev/null +++ b/tests/neg/export-in-extension.scala @@ -0,0 +1,40 @@ +object O: + + class C(x: Int): + def bar = x + def baz(y: Int) = x + y + val bam = x * x + def :: (y: Int) = x - y + class D + + val c1 = C(1) + + object O1: + extension (x: Int) + export c1.* // error + + object O2: + extension (x: Int) + private def cm = new C(x) + export cm.{bar, D} // error + export this.cm.baz // error + + object O3: + extension (x: Int) + export missing.* // error + + object O4: + extension (x: Int) + export cm.* // error + + object O5: + extension (x: Int) + private def cm(y: C) = C + export cm.* // error + + { + extension (x: Int) + private def cm = new C(x) + export cm.* // error + } + diff --git a/tests/neg/exports.check b/tests/neg/exports.check index 577f9e6b47ce..49d8cdf0654b 100644 --- a/tests/neg/exports.check +++ b/tests/neg/exports.check @@ -58,3 +58,9 @@ | Double definition: | val bar: Bar in class Baz at line 45 and | final def bar: (Baz.this.bar.bar : => (Baz.this.bar.baz.bar : Bar)) in class Baz at line 46 +-- [E083] Type Error: tests/neg/exports.scala:57:11 -------------------------------------------------------------------- +57 | export printer.* // error: not stable + | ^^^^^^^ + | (No.printer : => Printer) is not a valid export prefix, since it is not an immutable path + | + | longer explanation available when compiling with `-explain` diff --git a/tests/neg/exports.scala b/tests/neg/exports.scala index e0d834e39403..c187582c940d 100644 --- a/tests/neg/exports.scala +++ b/tests/neg/exports.scala @@ -49,3 +49,9 @@ val baz: Baz = new Baz export baz._ } + + object No: + def printer = + println("new Printer") + new Printer + export printer.* // error: not stable diff --git a/tests/pos/export-in-extension-rename.scala b/tests/pos/export-in-extension-rename.scala new file mode 100644 index 000000000000..6fb64f392c7a --- /dev/null +++ b/tests/pos/export-in-extension-rename.scala @@ -0,0 +1,7 @@ +class Ops[A](xs: List[A]): + def map[B](x: A => B): List[B] = ??? + +extension [B](x: List[B]) + private def ops = new Ops[B](x) + export ops.map // `x` and `B` should not appear twice as a parameter + diff --git a/tests/pos/forwardCompat-exportInExtension/Lib_1_r3.0.scala b/tests/pos/forwardCompat-exportInExtension/Lib_1_r3.0.scala new file mode 100644 index 000000000000..099782b74c8a --- /dev/null +++ b/tests/pos/forwardCompat-exportInExtension/Lib_1_r3.0.scala @@ -0,0 +1,13 @@ +case class ShortString(value: String) + +class StringOps(x: ShortString): + def *(n: Int): ShortString = ??? + def capitalize: ShortString = ??? + +object stringsyntax: + + extension (x: ShortString) + def take(n: Int): ShortString = ??? + def drop(n: Int): ShortString = ??? + private def moreOps = StringOps(x) + export moreOps.* diff --git a/tests/pos/forwardCompat-exportInExtension/Test_2_c3.0.2.scala b/tests/pos/forwardCompat-exportInExtension/Test_2_c3.0.2.scala new file mode 100644 index 000000000000..0abe6283609f --- /dev/null +++ b/tests/pos/forwardCompat-exportInExtension/Test_2_c3.0.2.scala @@ -0,0 +1,7 @@ +import stringsyntax.* + +def test = + ShortString("Hello").take(2) + ShortString("Hello").drop(3) + ShortString("Hello") * 5 + ShortString("Hello").capitalize diff --git a/tests/pos/reference/exports.scala b/tests/pos/reference/exports.scala index fe924c57e489..8588a2b4cd9d 100644 --- a/tests/pos/reference/exports.scala +++ b/tests/pos/reference/exports.scala @@ -1,3 +1,4 @@ +object exports: class BitMap class InkJet @@ -22,7 +23,22 @@ def status: List[String] = printUnit.status ++ scanUnit.status } - class C22 { type T } - object O22 { val c: C22 = ??? } - export O22.c - def f22: c.T = ??? + class C { type T } + object O { val c: C = ??? } + export O.c + def f: c.T = ??? + + class StringOps(x: String): + def *(n: Int): String = ??? + def ::(c: Char) = c.toString + x + def capitalize: String = ??? + + extension (x: String) + def take(n: Int): String = x.substring(0, n) + def drop(n: Int): String = x.substring(n) + private def moreOps = new StringOps(x) + export moreOps.* + + val s = "abc" + val t1 = (s.take(1) + s.drop(1)).capitalize * 2 + val t2 = 'a' :: s diff --git a/tests/run/export-in-extension.scala b/tests/run/export-in-extension.scala new file mode 100644 index 000000000000..26becc280ff3 --- /dev/null +++ b/tests/run/export-in-extension.scala @@ -0,0 +1,32 @@ +object O: + + class C(x: Int): + def bar = x + def baz(y: Int) = x + y + val bam = x * x + def :: (y: Int) = x - y + + extension (x: Int) + private def cm = new C(x) + export cm.* + def succ: Int = x + 1 + def succ2: Int = succ + 1 + def ::: (y: Int) = x - y + +object O2: + import O.C + extension (x: Int) + private def cm = new C(x) + export cm.{bar, baz, bam, ::} + def succ: Int = x + 1 + def succ2: Int = succ + 1 + def ::: (y: Int) = x - y + +@main def Test = + import O.* + assert(3.succ2 == 5) + assert(3.bar == 3) + assert(3.baz(3) == 6) + assert(3.bam == 9) + assert(3 :: 2 :: 10 == (10 - 2) - 3) // same as for implicit class C + assert(3 ::: 2 ::: 10 == 3 - (2 - 10))