diff --git a/compiler/src/dotty/tools/dotc/ast/Trees.scala b/compiler/src/dotty/tools/dotc/ast/Trees.scala index 59a439e4bb21..2835d3c06ddf 100644 --- a/compiler/src/dotty/tools/dotc/ast/Trees.scala +++ b/compiler/src/dotty/tools/dotc/ast/Trees.scala @@ -669,6 +669,11 @@ object Trees { type ThisTree[-T >: Untyped] = LambdaTypeTree[T] } + case class TermLambdaTypeTree[-T >: Untyped] private[ast] (params: List[ValDef[T]], body: Tree[T])(implicit @constructorOnly src: SourceFile) + extends TypTree[T] { + type ThisTree[-T >: Untyped] = TermLambdaTypeTree[T] + } + /** [bound] selector match { cases } */ case class MatchTypeTree[-T >: Untyped] private[ast] (bound: Tree[T], selector: Tree[T], cases: List[CaseDef[T]])(implicit @constructorOnly src: SourceFile) extends TypTree[T] { @@ -964,6 +969,7 @@ object Trees { type RefinedTypeTree = Trees.RefinedTypeTree[T] type AppliedTypeTree = Trees.AppliedTypeTree[T] type LambdaTypeTree = Trees.LambdaTypeTree[T] + type TermLambdaTypeTree = Trees.TermLambdaTypeTree[T] type MatchTypeTree = Trees.MatchTypeTree[T] type ByNameTypeTree = Trees.ByNameTypeTree[T] type TypeBoundsTree = Trees.TypeBoundsTree[T] @@ -1135,6 +1141,10 @@ object Trees { case tree: LambdaTypeTree if (tparams eq tree.tparams) && (body eq tree.body) => tree case _ => finalize(tree, untpd.LambdaTypeTree(tparams, body)(sourceFile(tree))) } + def TermLambdaTypeTree(tree: Tree)(params: List[ValDef], body: Tree)(implicit ctx: Context): TermLambdaTypeTree = tree match { + case tree: TermLambdaTypeTree if (params eq tree.params) && (body eq tree.body) => tree + case _ => finalize(tree, untpd.TermLambdaTypeTree(params, body)(sourceFile(tree))) + } def MatchTypeTree(tree: Tree)(bound: Tree, selector: Tree, cases: List[CaseDef])(implicit ctx: Context): MatchTypeTree = tree match { case tree: MatchTypeTree if (bound eq tree.bound) && (selector eq tree.selector) && (cases eq tree.cases) => tree case _ => finalize(tree, untpd.MatchTypeTree(bound, selector, cases)(sourceFile(tree))) @@ -1293,6 +1303,10 @@ object Trees { inContext(localCtx) { cpy.LambdaTypeTree(tree)(transformSub(tparams), transform(body)) } + case TermLambdaTypeTree(params, body) => + inContext(localCtx) { + cpy.TermLambdaTypeTree(tree)(transformSub(params), transform(body)) + } case MatchTypeTree(bound, selector, cases) => cpy.MatchTypeTree(tree)(transform(bound), transform(selector), transformSub(cases)) case ByNameTypeTree(result) => @@ -1422,6 +1436,10 @@ object Trees { inContext(localCtx) { this(this(x, tparams), body) } + case TermLambdaTypeTree(params, body) => + inContext(localCtx) { + this(this(x, params), body) + } case MatchTypeTree(bound, selector, cases) => this(this(this(x, bound), selector), cases) case ByNameTypeTree(result) => diff --git a/compiler/src/dotty/tools/dotc/ast/untpd.scala b/compiler/src/dotty/tools/dotc/ast/untpd.scala index bb8da0d59d72..45a67971b36a 100644 --- a/compiler/src/dotty/tools/dotc/ast/untpd.scala +++ b/compiler/src/dotty/tools/dotc/ast/untpd.scala @@ -377,6 +377,7 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo { def RefinedTypeTree(tpt: Tree, refinements: List[Tree])(implicit src: SourceFile): RefinedTypeTree = new RefinedTypeTree(tpt, refinements) def AppliedTypeTree(tpt: Tree, args: List[Tree])(implicit src: SourceFile): AppliedTypeTree = new AppliedTypeTree(tpt, args) def LambdaTypeTree(tparams: List[TypeDef], body: Tree)(implicit src: SourceFile): LambdaTypeTree = new LambdaTypeTree(tparams, body) + def TermLambdaTypeTree(params: List[ValDef], body: Tree)(implicit src: SourceFile): TermLambdaTypeTree = new TermLambdaTypeTree(params, body) def MatchTypeTree(bound: Tree, selector: Tree, cases: List[CaseDef])(implicit src: SourceFile): MatchTypeTree = new MatchTypeTree(bound, selector, cases) def ByNameTypeTree(result: Tree)(implicit src: SourceFile): ByNameTypeTree = new ByNameTypeTree(result) def TypeBoundsTree(lo: Tree, hi: Tree, alias: Tree = EmptyTree)(implicit src: SourceFile): TypeBoundsTree = new TypeBoundsTree(lo, hi, alias) @@ -485,8 +486,14 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo { ValDef(nme.syntheticParamName(n), if (tpt == null) TypeTree() else tpt, EmptyTree) .withFlags(flags) - def lambdaAbstract(tparams: List[TypeDef], tpt: Tree)(implicit ctx: Context): Tree = - if (tparams.isEmpty) tpt else LambdaTypeTree(tparams, tpt) + def lambdaAbstract(params: List[ValDef] | List[TypeDef], tpt: Tree)(using Context): Tree = + params match + case Nil => tpt + case (vd: ValDef) :: _ => TermLambdaTypeTree(params.asInstanceOf[List[ValDef]], tpt) + case _ => LambdaTypeTree(params.asInstanceOf[List[TypeDef]], tpt) + + def lambdaAbstractAll(paramss: List[List[ValDef] | List[TypeDef]], tpt: Tree)(using Context): Tree = + paramss.foldRight(tpt)(lambdaAbstract) /** A reference to given definition. If definition is a repeated * parameter, the reference will be a repeated argument. diff --git a/compiler/src/dotty/tools/dotc/config/Feature.scala b/compiler/src/dotty/tools/dotc/config/Feature.scala index 3b9d03773f6a..933fd0f372c4 100644 --- a/compiler/src/dotty/tools/dotc/config/Feature.scala +++ b/compiler/src/dotty/tools/dotc/config/Feature.scala @@ -3,7 +3,7 @@ package dotc package config import core._ -import Contexts._, Symbols._, Names._ +import Contexts._, Symbols._, Names._, NameOps._ import StdNames.nme import Decorators.{given _} import util.SourcePosition @@ -22,7 +22,7 @@ object Feature: def enabledBySetting(feature: TermName, owner: Symbol = NoSymbol)(using Context): Boolean = def toPrefix(sym: Symbol): String = if !sym.exists || sym == defn.LanguageModule.moduleClass then "" - else toPrefix(sym.owner) + sym.name + "." + else toPrefix(sym.owner) + sym.name.stripModuleClassSuffix + "." val prefix = if owner.exists then toPrefix(owner) else "" ctx.base.settings.language.value.contains(prefix + feature) @@ -38,7 +38,7 @@ object Feature: def enabledByImport(feature: TermName, owner: Symbol = NoSymbol)(using Context): Boolean = ctx.atPhase(ctx.typerPhase) { ctx.importInfo != null - && ctx.importInfo.featureImported(feature.toTermName, + && ctx.importInfo.featureImported(feature, if owner.exists then owner else defn.LanguageModule.moduleClass) } @@ -57,6 +57,9 @@ object Feature: def dynamicsEnabled(using Context): Boolean = enabled(nme.dynamics) + def dependentEnabled(using Context) = + enabled(nme.dependent, defn.LanguageExperimentalModule.moduleClass) + def sourceVersionSetting(using Context): SourceVersion = SourceVersion.valueOf(ctx.settings.source.value) diff --git a/compiler/src/dotty/tools/dotc/core/Definitions.scala b/compiler/src/dotty/tools/dotc/core/Definitions.scala index 65a53c19dece..fa5354d4f0aa 100644 --- a/compiler/src/dotty/tools/dotc/core/Definitions.scala +++ b/compiler/src/dotty/tools/dotc/core/Definitions.scala @@ -664,6 +664,7 @@ class Definitions { @tu lazy val Mirror_SingletonProxyClass: ClassSymbol = ctx.requiredClass("scala.deriving.Mirror.SingletonProxy") @tu lazy val LanguageModule: Symbol = ctx.requiredModule("scala.language") + @tu lazy val LanguageExperimentalModule: Symbol = ctx.requiredModule("scala.language.experimental") @tu lazy val NonLocalReturnControlClass: ClassSymbol = ctx.requiredClass("scala.runtime.NonLocalReturnControl") @tu lazy val SelectableClass: ClassSymbol = ctx.requiredClass("scala.Selectable") diff --git a/compiler/src/dotty/tools/dotc/core/StdNames.scala b/compiler/src/dotty/tools/dotc/core/StdNames.scala index 45e23e9d4512..799565dcbdcd 100644 --- a/compiler/src/dotty/tools/dotc/core/StdNames.scala +++ b/compiler/src/dotty/tools/dotc/core/StdNames.scala @@ -441,6 +441,7 @@ object StdNames { val definitions: N = "definitions" val delayedInit: N = "delayedInit" val delayedInitArg: N = "delayedInit$body" + val dependent: N = "dependent" val derived: N = "derived" val derives: N = "derives" val drop: N = "drop" diff --git a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala index 7c1b2c522895..a41912e212a2 100644 --- a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala +++ b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala @@ -192,7 +192,9 @@ object Parsers { def isIdent = in.isIdent def isIdent(name: Name) = in.isIdent(name) - def isSimpleLiteral = simpleLiteralTokens contains in.token + def isSimpleLiteral = + simpleLiteralTokens.contains(in.token) + || isIdent(nme.raw.MINUS) && numericLitTokens.contains(in.lookahead.token) def isLiteral = literalTokens contains in.token def isNumericLit = numericLitTokens contains in.token def isTemplateIntro = templateIntroTokens contains in.token @@ -206,7 +208,9 @@ object Parsers { def isBindingIntro: Boolean = { in.token match { case USCORE => true - case IDENTIFIER | BACKQUOTED_IDENT => in.lookaheadIn(BitSet(ARROW, CTXARROW)) + case IDENTIFIER | BACKQUOTED_IDENT => + val nxt = in.lookahead.token + nxt == ARROW || nxt == CTXARROW case LPAREN => val lookahead = in.LookaheadScanner() lookahead.skipParens() @@ -235,7 +239,7 @@ object Parsers { */ def isSplice: Boolean = in.token == IDENTIFIER && in.name(0) == '$' && { - if (in.name.length == 1) in.lookaheadIn(BitSet(LBRACE)) + if in.name.length == 1 then in.lookahead.token == LBRACE else (staged & StageKind.Quoted) != 0 } @@ -1038,71 +1042,55 @@ object Parsers { atSpan(accept(USCORE)) { Ident(nme.WILDCARD) } /** Accept identifier or match clause acting as a selector on given tree `t` */ - def selector(t: Tree): Tree = + def selectorOrMatch(t: Tree): Tree = atSpan(startOffset(t), in.offset) { if in.token == MATCH then matchClause(t) else Select(t, ident()) } - /** Selectors ::= id { `.' id } - * - * Accept `.' separated identifiers acting as a selectors on given tree `t`. - * @param finish An alternative parse in case the next token is not an identifier. - * If the alternative does not apply, its tree argument is returned unchanged. - */ - def selectors(t: Tree, finish: Tree => Tree): Tree = { - val t1 = finish(t) - if (t1 ne t) t1 else dotSelectors(selector(t), finish) - } + def selector(t: Tree): Tree = + atSpan(startOffset(t), in.offset) { Select(t, ident()) } - /** DotSelectors ::= { `.' id } - * - * Accept `.' separated identifiers acting as a selectors on given tree `t`. - * @param finish An alternative parse in case the token following a `.' is not an identifier. - * If the alternative does not apply, its tree argument is returned unchanged. - */ - def dotSelectors(t: Tree, finish: Tree => Tree = id): Tree = - if (in.token == DOT) { in.nextToken(); selectors(t, finish) } + /** DotSelectors ::= { `.' id } */ + def dotSelectors(t: Tree): Tree = + if (in.token == DOT) { in.nextToken(); dotSelectors(selector(t)) } else t private val id: Tree => Tree = x => x - /** Path ::= StableId - * | [id `.'] this - * - * @param thisOK If true, the path can end with the keyword `this`. - * If false, another selection is required after the `this`. - * @param finish An alternative parse in case the token following a `.' is not an identifier. - * If the alternative does not apply, its tree argument is returned unchanged. + /** SimpleRef ::= id + * | [id ‘.’] ‘this’ + * | [id ‘.’] ‘super’ [ClassQualifier] ‘.’ id */ - def path(thisOK: Boolean, finish: Tree => Tree = id): Tree = { + def simpleRef(): Tree = val start = in.offset - def handleThis(qual: Ident) = { + + def handleThis(qual: Ident) = in.nextToken() - val t = atSpan(start) { This(qual) } - if (!thisOK && in.token != DOT) syntaxError(DanglingThisInPath(), t.span) - dotSelectors(t, finish) - } - def handleSuper(qual: Ident) = { + atSpan(start) { This(qual) } + + def handleSuper(qual: Ident) = in.nextToken() val mix = mixinQualifierOpt() val t = atSpan(start) { Super(This(qual), mix) } accept(DOT) - dotSelectors(selector(t), finish) - } - if (in.token == THIS) handleThis(EmptyTypeIdent) - else if (in.token == SUPER) handleSuper(EmptyTypeIdent) - else { + selector(t) + + if in.token == THIS then handleThis(EmptyTypeIdent) + else if in.token == SUPER then handleSuper(EmptyTypeIdent) + else val t = termIdent() - if (in.token == DOT) { + if in.token == DOT then def qual = cpy.Ident(t)(t.name.toTypeName) - in.nextToken() - if (in.token == THIS) handleThis(qual) - else if (in.token == SUPER) handleSuper(qual) - else selectors(t, finish) - } + in.lookahead.token match + case THIS => + in.nextToken() + handleThis(qual) + case SUPER => + in.nextToken() + handleSuper(qual) + case _ => t else t - } - } + end simpleRef /** MixinQualifier ::= `[' id `]' */ @@ -1110,29 +1098,46 @@ object Parsers { if (in.token == LBRACKET) inBrackets(atSpan(in.offset) { typeIdent() }) else EmptyTypeIdent - /** StableId ::= id - * | Path `.' id - * | [id '.'] super [`[' id `]']`.' id - */ - def stableId(): Tree = - path(thisOK = false) - /** QualId ::= id {`.' id} */ def qualId(): Tree = dotSelectors(termIdent()) - /** SimpleExpr ::= literal - * | 'id | 'this | 'true | 'false | 'null - * | null + /** Singleton ::= SimpleRef + * | SimpleLiteral + * | Singleton ‘.’ id + */ + def singleton(): Tree = + if isSimpleLiteral then simpleLiteral() + else dotSelectors(simpleRef()) + + /** SimpleLiteral ::= [‘-’] integerLiteral + * | [‘-’] floatingPointLiteral + * | booleanLiteral + * | characterLiteral + * | stringLiteral + */ + def simpleLiteral(): Tree = + if isIdent(nme.raw.MINUS) then + val start = in.offset + in.nextToken() + literal(negOffset = start, inTypeOrSingleton = true) + else + literal(inTypeOrSingleton = true) + + /** Literal ::= SimpleLiteral + * | processedStringLiteral + * | symbolLiteral + * | ‘null’ + * * @param negOffset The offset of a preceding `-' sign, if any. - * If the literal is not negated, negOffset = in.offset. + * If the literal is not negated, negOffset == in.offset. */ - def literal(negOffset: Int = in.offset, inPattern: Boolean = false, inType: Boolean = false, inStringInterpolation: Boolean = false): Tree = { + def literal(negOffset: Int = in.offset, inPattern: Boolean = false, inTypeOrSingleton: Boolean = false, inStringInterpolation: Boolean = false): Tree = { def literalOf(token: Token): Tree = { val isNegated = negOffset < in.offset def digits0 = in.removeNumberSeparators(in.strVal) def digits = if (isNegated) "-" + digits0 else digits0 - if (!inType) + if !inTypeOrSingleton then token match { case INTLIT => return Number(digits, NumberKind.Whole(in.base)) case DECILIT => return Number(digits, NumberKind.Decimal) @@ -1353,22 +1358,33 @@ object Parsers { case _ => false } - /** Type ::= FunType - * | HkTypeParamClause ‘=>>’ Type - * | MatchType - * | InfixType - * FunType ::= (MonoFunType | PolyFunType) - * MonoFunType ::= FunArgTypes (‘=>’ | ‘?=>’) Type - * PolyFunType ::= HKTypeParamClause '=>' Type - * FunArgTypes ::= InfixType - * | `(' [ [ ‘[using]’ ‘['erased'] FunArgType {`,' FunArgType } ] `)' - * | '(' [ ‘[using]’ ‘['erased'] TypedFunParam {',' TypedFunParam } ')' + /** Type ::= FunType + * | HkTypeParamClause ‘=>>’ Type + * | FunParamClause ‘=>>’ Type + * | MatchType + * | InfixType + * FunType ::= (MonoFunType | PolyFunType) + * MonoFunType ::= FunArgTypes (‘=>’ | ‘?=>’) Type + * PolyFunType ::= HKTypeParamClause '=>' Type + * FunArgTypes ::= InfixType + * | `(' [ [ ‘[using]’ ‘['erased'] FunArgType {`,' FunArgType } ] `)' + * | '(' [ ‘[using]’ ‘['erased'] TypedFunParam {',' TypedFunParam } ')' */ def typ(): Tree = { val start = in.offset var imods = Modifiers() def functionRest(params: List[Tree]): Tree = atSpan(start, in.offset) { + if in.token == TLARROW then + if !imods.flags.isEmpty || params.isEmpty then + syntaxError(em"illegal parameter list for type lambda", start) + in.token = ARROW + else + for case ValDef(_, tpt: ByNameTypeTree, _) <- params do + syntaxError(em"parameter of type lambda may not be call-by-name", tpt.span) + in.nextToken() + return TermLambdaTypeTree(params.asInstanceOf[List[ValDef]], typ()) + if in.token == CTXARROW then in.nextToken() imods |= Given @@ -1495,10 +1511,19 @@ object Parsers { Span(start, start + nme.IMPLICITkw.asSimpleName.length) /** TypedFunParam ::= id ':' Type */ - def typedFunParam(start: Offset, name: TermName, mods: Modifiers = EmptyModifiers): Tree = atSpan(start) { - accept(COLON) - makeParameter(name, typ(), mods | Param) - } + def typedFunParam(start: Offset, name: TermName, mods: Modifiers = EmptyModifiers): ValDef = + atSpan(start) { + accept(COLON) + makeParameter(name, typ(), mods | Param) + } + + /** FunParamClause ::= ‘(’ TypedFunParam {‘,’ TypedFunParam } ‘)’ + */ + def funParamClause(): List[ValDef] = + inParens(commaSeparated(() => typedFunParam(in.offset, ident()))) + + def funParamClauses(): List[List[ValDef]] = + if in.token == LPAREN then funParamClause() :: funParamClauses() else Nil /** InfixType ::= RefinedType {id [nl] RefinedType} */ @@ -1506,7 +1531,10 @@ object Parsers { /** Is current ident a `*`, and is it followed by a `)` or `,`? */ def isPostfixStar: Boolean = - in.name == nme.raw.STAR && in.lookaheadIn(BitSet(RPAREN, COMMA)) + in.name == nme.raw.STAR && { + val nxt = in.lookahead.token + nxt == RPAREN || nxt == COMMA + } def infixTypeRest(t: Tree): Tree = infixOps(t, canStartTypeTokens, refinedType, isType = true, isOperator = !isPostfixStar) @@ -1570,58 +1598,64 @@ object Parsers { if (isType) TypSplice(expr) else Splice(expr) } - /** SimpleType ::= SimpleType TypeArgs - * | SimpleType `#' id - * | StableId - * | Path `.' type - * | `(' ArgTypes `)' - * | `_' TypeBounds - * | Refinement - * | Literal - * | ‘$’ ‘{’ Block ‘}’ + /** SimpleType ::= SimpleLiteral + * | ‘?’ SubtypeBounds + * | SimpleType1 + * | SimpeType ‘(’ Singletons ‘)’ -- under language.experimental.dependent, checked in Typer + * Singletons ::= Singleton {‘,’ Singleton} */ - def simpleType(): Tree = simpleTypeRest { - if (in.token == LPAREN) - atSpan(in.offset) { - makeTupleOrParens(inParens(argTypes(namedOK = false, wildOK = true))) - } - else if (in.token == LBRACE) - atSpan(in.offset) { RefinedTypeTree(EmptyTree, refinement()) } - else if (isSimpleLiteral) { SingletonTypeTree(literal(inType = true)) } - else if (isIdent(nme.raw.MINUS) && in.lookaheadIn(numericLitTokens)) { - val start = in.offset - in.nextToken() - SingletonTypeTree(literal(negOffset = start, inType = true)) - } - else if (in.token == USCORE) { + def simpleType(): Tree = + if isSimpleLiteral then + SingletonTypeTree(simpleLiteral()) + else if in.token == USCORE then if sourceVersion.isAtLeast(`3.1`) then deprecationWarning(em"`_` is deprecated for wildcard arguments of types: use `?` instead") patch(source, Span(in.offset, in.offset + 1), "?") val start = in.skipToken() typeBounds().withSpan(Span(start, in.lastOffset, start)) - } - else if (isIdent(nme.?)) { + else if isIdent(nme.?) then val start = in.skipToken() typeBounds().withSpan(Span(start, in.lastOffset, start)) - } - else if (isIdent(nme.*) && ctx.settings.YkindProjector.value) { + else if isIdent(nme.*) && ctx.settings.YkindProjector.value then typeIdent() - } + else + def singletonArgs(t: Tree): Tree = + if in.token == LPAREN + then singletonArgs(AppliedTypeTree(t, inParens(commaSeparated(singleton)))) + else t + singletonArgs(simpleType1()) + + /** SimpleType1 ::= id + * | Singleton `.' id + * | Singleton `.' type + * | ‘(’ ArgTypes ‘)’ + * | Refinement + * | ‘$’ ‘{’ Block ‘}’ + * | SimpleType1 TypeArgs + * | SimpleType1 `#' id + */ + def simpleType1() = simpleTypeRest { + if in.token == LPAREN then + atSpan(in.offset) { + makeTupleOrParens(inParens(argTypes(namedOK = false, wildOK = true))) + } + else if in.token == LBRACE then + atSpan(in.offset) { RefinedTypeTree(EmptyTree, refinement()) } else if (isSplice) splice(isType = true) - else path(thisOK = false, handleSingletonType) match { - case r @ SingletonTypeTree(_) => r - case r => convertToTypeId(r) - } + else + def singletonCompletion(t: Tree): Tree = + if in.token == DOT then + in.nextToken() + if in.token == TYPE then + in.nextToken() + atSpan(startOffset(t)) { SingletonTypeTree(t) } + else + singletonCompletion(selector(t)) + else convertToTypeId(t) + singletonCompletion(simpleRef()) } - val handleSingletonType: Tree => Tree = t => - if (in.token == TYPE) { - in.nextToken() - atSpan(startOffset(t)) { SingletonTypeTree(t) } - } - else t - private def simpleTypeRest(t: Tree): Tree = in.token match { case HASH => simpleTypeRest(typeProjection(t)) case LBRACKET => simpleTypeRest(atSpan(startOffset(t)) { @@ -2012,7 +2046,7 @@ object Parsers { case _ => if isIdent(nme.inline) && !in.inModifierPosition() - && in.lookaheadIn(in.canStartExprTokens) + && in.canStartExprTokens.contains(in.lookahead.token) then val start = in.skipToken() in.token match @@ -2203,7 +2237,7 @@ object Parsers { * | SimpleExpr1 [`_`] * SimpleExpr1 ::= literal * | xmlLiteral - * | Path + * | SimpleRef * | `(` [ExprsInParens] `)` * | SimpleExpr `.` id * | SimpleExpr `.` MatchClause @@ -2219,9 +2253,9 @@ object Parsers { xmlLiteral() case IDENTIFIER => if (isSplice) splice(isType = false) - else path(thisOK = true) + else simpleRef() case BACKQUOTED_IDENT | THIS | SUPER => - path(thisOK = true) + simpleRef() case USCORE => val start = in.skipToken() val pname = WildcardParamName.fresh() @@ -2276,7 +2310,7 @@ object Parsers { in.token match { case DOT => in.nextToken() - simpleExprRest(selector(t), canApply = true) + simpleExprRest(selectorOrMatch(t), canApply = true) case LBRACKET => val tapp = atSpan(startOffset(t), in.offset) { TypeApply(t, typeArgs(namedOK = true, wildOK = false)) } simpleExprRest(tapp, canApply = true) @@ -2654,17 +2688,16 @@ object Parsers { * | XmlPattern * | `(' [Patterns] `)' * | SimplePattern1 [TypeArgs] [ArgumentPatterns] - * SimplePattern1 ::= Path + * SimplePattern1 ::= SimpleRef * | SimplePattern1 `.' id * PatVar ::= id * | `_' */ val simplePattern: () => Tree = () => in.token match { - case IDENTIFIER | BACKQUOTED_IDENT | THIS => - path(thisOK = true) match { + case IDENTIFIER | BACKQUOTED_IDENT | THIS | SUPER => + simpleRef() match case id @ Ident(nme.raw.MINUS) if isNumericLit => literal(startOffset(id)) case t => simplePatternRest(t) - } case USCORE => val wildIdent = wildcardIdent() @@ -2690,14 +2723,17 @@ object Parsers { } } - def simplePatternRest(t: Tree): Tree = { - var p = t - if (in.token == LBRACKET) - p = atSpan(startOffset(t), in.offset) { TypeApply(p, typeArgs(namedOK = false, wildOK = false)) } - if (in.token == LPAREN) - p = atSpan(startOffset(t), in.offset) { Apply(p, argumentPatterns()) } - p - } + def simplePatternRest(t: Tree): Tree = + if in.token == DOT then + in.nextToken() + simplePatternRest(selector(t)) + else + var p = t + if (in.token == LBRACKET) + p = atSpan(startOffset(t), in.offset) { TypeApply(p, typeArgs(namedOK = false, wildOK = false)) } + if (in.token == LPAREN) + p = atSpan(startOffset(t), in.offset) { Apply(p, argumentPatterns()) } + p /** Patterns ::= Pattern [`,' Pattern] */ @@ -2830,11 +2866,11 @@ object Parsers { else tree1 } - /** Annotation ::= `@' SimpleType {ParArgumentExprs} + /** Annotation ::= `@' SimpleType1 {ParArgumentExprs} */ def annot(): Tree = adjustStart(accept(AT)) { - ensureApplied(parArgumentExprss(wrapNew(simpleType()))) + ensureApplied(parArgumentExprss(wrapNew(simpleType1()))) } def annotations(skipNewLines: Boolean = false): List[Tree] = { @@ -3005,7 +3041,7 @@ object Parsers { val isParams = !impliedMods.is(Given) || startParamTokens.contains(in.token) - || isIdent && (in.name == nme.inline || in.lookaheadIn(BitSet(COLON))) + || isIdent && (in.name == nme.inline || in.lookahead.token == COLON) if isParams then commaSeparated(() => param()) else contextTypes(ofClass, nparams) checkVarArgsRules(clause) @@ -3084,7 +3120,7 @@ object Parsers { ctx.compilationUnit.sourceVersion = Some(SourceVersion.valueOf(imported.toString)) Import(tree, selectors) - /** ImportExpr ::= StableId ‘.’ ImportSpec + /** ImportExpr ::= SimpleRef {‘.’ id} ‘.’ ImportSpec * ImportSpec ::= id * | ‘_’ * | ‘{’ ImportSelectors) ‘}’ @@ -3131,26 +3167,24 @@ object Parsers { Nil selector :: rest - val handleImport: Tree => Tree = tree => + def importSelection(qual: Tree): Tree = + accept(DOT) in.token match case USCORE => - mkTree(tree, ImportSelector(wildcardSelectorId()) :: Nil) + mkTree(qual, ImportSelector(wildcardSelectorId()) :: Nil) case LBRACE => - mkTree(tree, inBraces(importSelectors(idOK = true))) + mkTree(qual, inBraces(importSelectors(idOK = true))) case _ => - tree + val start = in.offset + val name = ident() + if in.token == DOT then + importSelection(atSpan(startOffset(qual), start) { Select(qual, name) }) + else + atSpan(startOffset(qual)) { + mkTree(qual, ImportSelector(atSpan(start) { Ident(name) }) :: Nil) + } - () => { - val p = path(thisOK = false, handleImport) - p match - case _: Import | _: Export => p - case sel @ Select(qual, name) => - val selector = ImportSelector(atSpan(pointOffset(sel)) { Ident(name) }) - mkTree(qual, selector :: Nil).withSpan(sel.span) - case t => - accept(DOT) - mkTree(t, ImportSelector(Ident(nme.WILDCARD)) :: Nil) - } + () => importSelection(simpleRef()) } def posMods(start: Int, mods: Modifiers): Modifiers = { @@ -3369,15 +3403,16 @@ object Parsers { argumentExprss(mkApply(Ident(nme.CONSTRUCTOR), argumentExprs())) } - /** TypeDcl ::= id [TypeParamClause] TypeBounds [‘=’ Type] + /** TypeDcl ::= id [TypeParamClause] {FunParamClause} TypeBounds [‘=’ Type] */ def typeDefOrDcl(start: Offset, mods: Modifiers): Tree = { newLinesOpt() atSpan(start, nameStart) { val nameIdent = typeIdent() val tparams = typeParamClauseOpt(ParamOwner.Type) + val vparamss = funParamClauses() def makeTypeDef(rhs: Tree): Tree = { - val rhs1 = lambdaAbstract(tparams, rhs) + val rhs1 = lambdaAbstractAll(tparams :: vparamss, rhs) val tdef = TypeDef(nameIdent.name.toTypeName, rhs1) if (nameIdent.isBackquoted) tdef.pushAttachment(Backquoted, ()) @@ -3567,7 +3602,7 @@ object Parsers { val tparams = typeParamClauseOpt(ParamOwner.Def) newLineOpt() val vparamss = - if in.token == LPAREN && in.lookaheadIn(nme.using) + if in.token == LPAREN && in.lookahead.isIdent(nme.using) then paramClauses(givenOnly = true) else Nil newLinesOpt() @@ -3625,13 +3660,13 @@ object Parsers { /* -------- TEMPLATES ------------------------------------------- */ - /** SimpleConstrApp ::= AnnotType {ParArgumentExprs} + /** ConstrApp ::= SimpleType1 {Annotation} {ParArgumentExprs} */ - val constrApp: () => Tree = () => { - val t = rejectWildcardType(annotType(), fallbackTree = Ident(nme.ERROR)) - // Using Ident(nme.ERROR) to avoid causing cascade errors on non-user-written code + val constrApp: () => Tree = () => + val t = rejectWildcardType(annotTypeRest(simpleType1()), + fallbackTree = Ident(tpnme.ERROR)) + // Using Ident(tpnme.ERROR) to avoid causing cascade errors on non-user-written code if in.token == LPAREN then parArgumentExprss(wrapNew(t)) else t - } /** ConstrApps ::= ConstrApp {(‘,’ | ‘with’) ConstrApp} */ diff --git a/compiler/src/dotty/tools/dotc/parsing/Scanners.scala b/compiler/src/dotty/tools/dotc/parsing/Scanners.scala index 85894324aac4..55d99b0bf0da 100644 --- a/compiler/src/dotty/tools/dotc/parsing/Scanners.scala +++ b/compiler/src/dotty/tools/dotc/parsing/Scanners.scala @@ -98,6 +98,13 @@ object Scanners { this.strVal = td.strVal this.base = td.base } + + def isNewLine = token == NEWLINE || token == NEWLINES + def isIdent = token == IDENTIFIER || token == BACKQUOTED_IDENT + def isIdent(name: Name) = token == IDENTIFIER && this.name == name + + def isNestedStart = token == LBRACE || token == INDENT + def isNestedEnd = token == RBRACE || token == OUTDENT } abstract class ScannerCommon(source: SourceFile)(implicit ctx: Context) extends CharArrayReader with TokenData { @@ -535,7 +542,7 @@ object Scanners { def observeColonEOL(): Unit = if token == COLON then - lookahead() + lookAhead() val atEOL = isAfterLineEnd || token == EOF reset() if atEOL then token = COLONEOL @@ -562,7 +569,7 @@ object Scanners { insert(OUTDENT, offset) case _ => - def lookahead() = { + def lookAhead() = { prev.copyFrom(this) lastOffset = lastCharOffset fetchToken() @@ -591,12 +598,12 @@ object Scanners { } token match { case CASE => - lookahead() + lookAhead() if (token == CLASS) fuse(CASECLASS) else if (token == OBJECT) fuse(CASEOBJECT) else reset() case SEMI => - lookahead() + lookAhead() if (token != ELSE) reset() case COMMA => def isEnclosedInParens(r: Region): Boolean = r match @@ -608,7 +615,7 @@ object Scanners { insert(OUTDENT, offset) currentRegion = r.outer case _ => - lookahead() + lookAhead() if isAfterLineEnd && (token == RPAREN || token == RBRACKET || token == RBRACE || token == OUTDENT) then @@ -892,6 +899,18 @@ object Scanners { // Lookahead --------------------------------------------------------------- + /** The next token after this one. + * The token is computed via fetchToken, so complex two word + * tokens such as CASECLASS are not recognized. + * Newlines and indent/unindent tokens are skipped. + * + */ + def lookahead: TokenData = + if next.token == EMPTY then + lookAhead() + reset() + next + class LookaheadScanner() extends Scanner(source, offset) /** Skip matching pairs of `(...)` or `[...]` parentheses. @@ -904,17 +923,6 @@ object Scanners { if token == opening && multiple then skipParens() else nextToken() nextToken() - /** Is the token following the current one in `tokens`? */ - def lookaheadIn(follow: BitSet | TermName): Boolean = - val lookahead = LookaheadScanner() - while - lookahead.nextToken() - lookahead.isNewLine - do () - follow match - case tokens: BitSet => tokens.contains(lookahead.token) - case name: TermName => lookahead.token == IDENTIFIER && lookahead.name == name - /** Is the current token in a position where a modifier is allowed? */ def inModifierPosition(): Boolean = { val lookahead = LookaheadScanner() @@ -1010,14 +1018,7 @@ object Scanners { isSoftModifier && inModifierPosition() def isSoftModifierInParamModifierPosition: Boolean = - isSoftModifier && !lookaheadIn(BitSet(COLON)) - - def isNewLine = token == NEWLINE || token == NEWLINES - def isIdent = token == IDENTIFIER || token == BACKQUOTED_IDENT - def isIdent(name: Name) = token == IDENTIFIER && this.name == name - - def isNestedStart = token == LBRACE || token == INDENT - def isNestedEnd = token == RBRACE || token == OUTDENT + isSoftModifier && lookahead.token != COLON def canStartStatTokens = if migrateTo3 then canStartStatTokens2 else canStartStatTokens3 diff --git a/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala b/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala index 98d231de643c..fbf7f24b3a0e 100644 --- a/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala +++ b/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala @@ -477,12 +477,19 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) { changePrec(OrTypePrec) { toText(args(0)) ~ " | " ~ atPrec(OrTypePrec + 1) { toText(args(1)) } } else if (tpt.symbol == defn.andType && args.length == 2) changePrec(AndTypePrec) { toText(args(0)) ~ " & " ~ atPrec(AndTypePrec + 1) { toText(args(1)) } } - else - toTextLocal(tpt) ~ "[" ~ Text(args map argText, ", ") ~ "]" + else args match + case arg :: _ if arg.isTerm => + toTextLocal(tpt) ~ "(" ~ Text(args.map(argText), ", ") ~ ")" + case _ => + toTextLocal(tpt) ~ "[" ~ Text(args.map(argText), ", ") ~ "]" case LambdaTypeTree(tparams, body) => changePrec(GlobalPrec) { tparamsText(tparams) ~ " =>> " ~ toText(body) } + case TermLambdaTypeTree(params, body) => + changePrec(GlobalPrec) { + paramsText(params) ~ " =>> " ~ toText(body) + } case MatchTypeTree(bound, sel, cases) => changePrec(GlobalPrec) { toText(sel) ~ keywordStr(" match ") ~ blockText(cases) ~ diff --git a/compiler/src/dotty/tools/dotc/typer/ErrorReporting.scala b/compiler/src/dotty/tools/dotc/typer/ErrorReporting.scala index d075e49247f8..55f5cb18baff 100644 --- a/compiler/src/dotty/tools/dotc/typer/ErrorReporting.scala +++ b/compiler/src/dotty/tools/dotc/typer/ErrorReporting.scala @@ -146,5 +146,9 @@ object ErrorReporting { else "" } + def dependentStr = + """Term-dependent types are experimental, + |they must be enabled with a `experimental.dependent` language import or setting""".stripMargin + def err(using Context): Errors = new Errors } diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index fdd19deb1e1e..4892aba6034a 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -1578,6 +1578,14 @@ class Typer extends Namer } def typedAppliedTypeTree(tree: untpd.AppliedTypeTree)(using Context): Tree = { + tree.args match + case arg :: _ if arg.isTerm => + if dependentEnabled then + return errorTree(tree, i"Not yet implemented: T(...)") + else + return errorTree(tree, dependentStr) + case _ => + val tpt1 = typed(tree.tpt, AnyTypeConstructorProto)(using ctx.retractMode(Mode.Pattern)) val tparams = tpt1.tpe.typeParams if (tparams.isEmpty) { @@ -1654,6 +1662,12 @@ class Typer extends Namer assignType(cpy.LambdaTypeTree(tree)(tparams1, body1), tparams1, body1) } + def typedTermLambdaTypeTree(tree: untpd.TermLambdaTypeTree)(using Context): Tree = + if dependentEnabled then + errorTree(tree, i"Not yet implemented: (...) =>> ...") + else + errorTree(tree, dependentStr) + def typedMatchTypeTree(tree: untpd.MatchTypeTree, pt: Type)(using Context): Tree = { val bound1 = if (tree.bound.isEmpty && isFullyDefined(pt, ForceDegree.none)) TypeTree(pt) @@ -2369,6 +2383,7 @@ class Typer extends Namer case tree: untpd.RefinedTypeTree => typedRefinedTypeTree(tree) case tree: untpd.AppliedTypeTree => typedAppliedTypeTree(tree) case tree: untpd.LambdaTypeTree => typedLambdaTypeTree(tree)(using ctx.localContext(tree, NoSymbol).setNewScope) + case tree: untpd.TermLambdaTypeTree => typedTermLambdaTypeTree(tree)(using ctx.localContext(tree, NoSymbol).setNewScope) case tree: untpd.MatchTypeTree => typedMatchTypeTree(tree, pt) case tree: untpd.ByNameTypeTree => typedByNameTypeTree(tree) case tree: untpd.TypeBoundsTree => typedTypeBoundsTree(tree, pt) diff --git a/compiler/test/dotty/tools/repl/TabcompleteTests.scala b/compiler/test/dotty/tools/repl/TabcompleteTests.scala index 373b625aca26..4e6bfbddf102 100644 --- a/compiler/test/dotty/tools/repl/TabcompleteTests.scala +++ b/compiler/test/dotty/tools/repl/TabcompleteTests.scala @@ -100,7 +100,7 @@ class TabcompleteTests extends ReplTest { @Test def importScala = fromInitialState { implicit s => val comp = tabComplete("import scala.") // check that there are no special symbols leaked: , , ... - assertEquals(comp.find(_.startsWith("<")), Some("<:<")) + assertEquals(Some("<:<"), comp.find(_.startsWith("<"))) assert(!comp.contains("package")) } diff --git a/docs/docs/internals/syntax.md b/docs/docs/internals/syntax.md index 88c49035696c..acf02c91f040 100644 --- a/docs/docs/internals/syntax.md +++ b/docs/docs/internals/syntax.md @@ -128,42 +128,51 @@ Literal ::= SimpleLiteral QualId ::= id {‘.’ id} ids ::= id {‘,’ id} -Path ::= StableId +SimpleRef ::= id | [id ‘.’] ‘this’ -StableId ::= id - | Path ‘.’ id | [id ‘.’] ‘super’ [ClassQualifier] ‘.’ id + ClassQualifier ::= ‘[’ id ‘]’ ``` ### Types ```ebnf Type ::= FunType - | HkTypeParamClause ‘=>>’ Type TypeLambda(ps, t) + | HkTypeParamClause ‘=>>’ Type LambdaTypeTree(ps, t) + | FunParamClause ‘=>>’ Type TermLambdaTypeTree(ps, t) | MatchType | InfixType FunType ::= FunArgTypes (‘=>’ | ‘?=>’) Type Function(ts, t) | HKTypeParamClause '=>' Type PolyFunction(ps, t) FunArgTypes ::= InfixType | ‘(’ [ FunArgType {‘,’ FunArgType } ] ‘)’ - | ‘(’ TypedFunParam {‘,’ TypedFunParam } ‘)’ + | FunParamClause +FunParamClause ::= ‘(’ TypedFunParam {‘,’ TypedFunParam } ‘)’ TypedFunParam ::= id ‘:’ Type MatchType ::= InfixType `match` ‘{’ TypeCaseClauses ‘}’ InfixType ::= RefinedType {id [nl] RefinedType} InfixOp(t1, op, t2) RefinedType ::= WithType {[nl | colonEol] Refinement} RefinedTypeTree(t, ds) WithType ::= AnnotType {‘with’ AnnotType} (deprecated) AnnotType ::= SimpleType {Annotation} Annotated(t, annot) -SimpleType ::= SimpleType TypeArgs AppliedTypeTree(t, args) - | SimpleType ‘#’ id Select(t, name) - | StableId - | Path ‘.’ ‘type’ SingletonTypeTree(p) - | ‘(’ ArgTypes ‘)’ Tuple(ts) + +SimpleType ::= SimpleLiteral SingletonTypeTree(l) | ‘?’ SubtypeBounds + | SimpleType1 { ‘(’ Singletons ‘)’ } +SimpleType1 ::= id Ident(name) + | Singleton ‘.’ id Select(t, name) + | Singleton ‘.’ ‘type’ SingletonTypeTree(p) + | ‘(’ ArgTypes ‘)’ Tuple(ts) | Refinement RefinedTypeTree(EmptyTree, refinement) - | SimpleLiteral SingletonTypeTree(l) | ‘$’ ‘{’ Block ‘}’ -ArgTypes ::= Type {‘,’ Type} - | NamedTypeArg {‘,’ NamedTypeArg} + | SimpleType1 TypeArgs AppliedTypeTree(t, args) + | SimpleType1 ‘#’ id Select(t, name) +Singleton ::= SimpleRef + | SimpleLiteral + | Singleton ‘.’ id +-- not yet | Singleton ‘(’ Singletons ‘)’ +-- not yet | Singleton ‘[’ ArgTypes ‘]’ +Singletons ::= Singleton { ‘,’ Singleton } +ArgTypes ::= Types FunArgType ::= Type | ‘=>’ Type PrefixOp(=>, t) ParamType ::= [‘=>’] ParamValueType @@ -209,7 +218,7 @@ InfixExpr ::= PrefixExpr | InfixExpr MatchClause MatchClause ::= ‘match’ ‘{’ CaseClauses ‘}’ Match(expr, cases) PrefixExpr ::= [‘-’ | ‘+’ | ‘~’ | ‘!’] SimpleExpr PrefixOp(expr, op) -SimpleExpr ::= Path +SimpleExpr ::= SimpleRef | Literal | ‘_’ | BlockExpr @@ -271,7 +280,7 @@ SimplePattern ::= PatVar | Quoted | XmlPattern | SimplePattern1 [TypeArgs] [ArgumentPatterns] -SimplePattern1 ::= Path +SimplePattern1 ::= SimpleRef | SimplePattern1 ‘.’ id PatVar ::= varid | ‘_’ @@ -332,10 +341,10 @@ LocalModifier ::= ‘abstract’ AccessModifier ::= (‘private’ | ‘protected’) [AccessQualifier] AccessQualifier ::= ‘[’ id ‘]’ -Annotation ::= ‘@’ SimpleType {ParArgumentExprs} Apply(tpe, args) +Annotation ::= ‘@’ SimpleType1 {ParArgumentExprs} Apply(tpe, args) Import ::= ‘import’ ImportExpr {‘,’ ImportExpr} -ImportExpr ::= StableId ‘.’ ImportSpec Import(expr, sels) +ImportExpr ::= SimpleRef {‘.’ id} ‘.’ ImportSpec Import(expr, sels) ImportSpec ::= id | ‘_’ | ‘{’ ImportSelectors) ‘}’ @@ -363,7 +372,8 @@ VarDcl ::= ids ‘:’ Type DefDcl ::= DefSig ‘:’ Type DefDef(_, name, tparams, vparamss, tpe, EmptyTree) DefSig ::= id [DefTypeParamClause] DefParamClauses | ExtParamClause {nl} [‘.’] id DefParamClauses -TypeDcl ::= id [TypeParamClause] SubtypeBounds [‘=’ Type] TypeDefTree(_, name, tparams, bound +TypeDcl ::= id [TypeParamClause] {FunParamClause} SubtypeBounds TypeDefTree(_, name, tparams, bound + [‘=’ Type] Def ::= ‘val’ PatDef | ‘var’ VarDef @@ -398,7 +408,7 @@ ExtParamClause ::= [DefTypeParamClause] ‘(’ DefParam ‘)’ Template ::= InheritClauses [TemplateBody] Template(constr, parents, self, stats) InheritClauses ::= [‘extends’ ConstrApps] [‘derives’ QualId {‘,’ QualId}] ConstrApps ::= ConstrApp {(‘,’ | ‘with’) ConstrApp} -ConstrApp ::= AnnotType {ParArgumentExprs} Apply(tp, args) +ConstrApp ::= SimpleType1 {Annotation} {ParArgumentExprs} Apply(tp, args) ConstrExpr ::= SelfInvocation | ‘{’ SelfInvocation {semi BlockStat} ‘}’ SelfInvocation ::= ‘this’ ArgumentExprs {ArgumentExprs} diff --git a/library/src/scalaShadowing/language.scala b/library/src/scalaShadowing/language.scala index 0dc00b4029d0..3c2ab45c2b0d 100644 --- a/library/src/scalaShadowing/language.scala +++ b/library/src/scalaShadowing/language.scala @@ -210,6 +210,9 @@ object language { * to debug and understand. */ implicit lazy val macros: macros = languageFeature.experimental.macros + + /** Experimental support for richer dependent types */ + object dependent } /** Where imported, a backwards compatibility mode for Scala2 is enabled */ @@ -239,6 +242,9 @@ object language { */ object adhocExtensions + /** Experimental support for richer dependent types */ + object dependent + /** Source version */ object `3.0-migration` object `3.0` diff --git a/tests/neg/deptypes-badsyntax.scala b/tests/neg/deptypes-badsyntax.scala new file mode 100644 index 000000000000..88c49d6203cc --- /dev/null +++ b/tests/neg/deptypes-badsyntax.scala @@ -0,0 +1,2 @@ + +type Bad1[T] = () =>> Array[T] // error diff --git a/tests/neg/deptypes.scala b/tests/neg/deptypes.scala new file mode 100644 index 000000000000..ee6d1c95c0f2 --- /dev/null +++ b/tests/neg/deptypes.scala @@ -0,0 +1,10 @@ + +type Vec[T] = (n: Int) =>> Array[T] // error: needs to be enabled + +type Matrix[T](m: Int, n: Int) = Vec[Vec[T](n)](m) // error: needs to be enabled + +type Tensor2[T](m: Int)(n: Int) = Matrix[T](m, n) // error: needs to be enabled + +val x: Vec[Int](10) = ??? // error: needs to be enabled +val n = 10 +type T = Vec[String](n) // error: needs to be enabled \ No newline at end of file diff --git a/tests/neg/i4373.scala b/tests/neg/i4373.scala index 20a6c2595c9b..d3a7bce51436 100644 --- a/tests/neg/i4373.scala +++ b/tests/neg/i4373.scala @@ -9,13 +9,13 @@ class X5[A >: _ with X5[_]] // error class X6[A >: X6[_] with _] // error class A1 extends _ // error -class A2 extends _ with _ // error // error +class A2 extends _ with _ // error class A3 extends Base with _ // error class A4 extends _ with Base // error object Test { type T1 = _ // error - type T2 = _[Int] // error + type T2 = _[Int] // error // error type T3 = _ { type S } // error type T4 = [X] =>> _ // error diff --git a/tests/neg/i4373c.scala b/tests/neg/i4373c.scala index 44d744856f3f..c14d69da65d8 100644 --- a/tests/neg/i4373c.scala +++ b/tests/neg/i4373c.scala @@ -1,4 +1,4 @@ // ==> 18b253a4a89a84c5674165c6fc3efafad535eee3.scala <== object x0 { - def x1[x2 <:_[ // error // error + def x1[x2 <:_[ // error // error \ No newline at end of file