From 353a4d9f17b91d09dea3c9090c7a21e267372abe Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Sat, 4 Mar 2017 17:27:10 +0100 Subject: Drop named type parameters in classes Drop the [type T] syntax, and what's associated to make it work. Motivation: It's an alternative way of doing things for which there seems to be little need. The implementation was provisional and bitrotted during the various iterations to introduce higher-kinded types. So in the end the complxity-cost for language and compiler was not worth the added benefit that [type T] parameters provide. Noe that we still accept _named arguments_ [A = T] in expressions; these are useful for specifying some parameters and letting others be inferred. --- .../dotty/tools/dotc/core/ConstraintHandling.scala | 3 -- compiler/src/dotty/tools/dotc/core/Flags.scala | 3 -- .../src/dotty/tools/dotc/core/SymDenotations.scala | 17 +----- .../dotty/tools/dotc/core/TypeApplications.scala | 61 ---------------------- .../src/dotty/tools/dotc/core/TypeComparer.scala | 14 +---- compiler/src/dotty/tools/dotc/core/TypeOps.scala | 14 ++--- compiler/src/dotty/tools/dotc/core/Types.scala | 8 +-- .../src/dotty/tools/dotc/parsing/Parsers.scala | 34 +++++------- compiler/src/dotty/tools/dotc/typer/Namer.scala | 54 ++----------------- .../src/dotty/tools/dotc/typer/RefChecks.scala | 5 -- .../src/dotty/tools/dotc/typer/TypeAssigner.scala | 22 ++------ compiler/src/dotty/tools/dotc/typer/Typer.scala | 30 +++++------ docs/docs/internals/syntax.md | 4 +- tests/neg/namedTypeParams.scala | 12 +++++ 14 files changed, 56 insertions(+), 225 deletions(-) create mode 100644 tests/neg/namedTypeParams.scala diff --git a/compiler/src/dotty/tools/dotc/core/ConstraintHandling.scala b/compiler/src/dotty/tools/dotc/core/ConstraintHandling.scala index 3aa20f15b..b3c50fb71 100644 --- a/compiler/src/dotty/tools/dotc/core/ConstraintHandling.scala +++ b/compiler/src/dotty/tools/dotc/core/ConstraintHandling.scala @@ -253,9 +253,6 @@ trait ConstraintHandling { if (fromBelow && isOrType(inst) && isFullyDefined(inst) && !isOrType(upperBound)) inst = ctx.harmonizeUnion(inst) - // 3. If instance is from below, and upper bound has open named parameters - // make sure the instance has all named parameters of the bound. - if (fromBelow) inst = inst.widenToNamedTypeParams(param.namedTypeParams) inst } diff --git a/compiler/src/dotty/tools/dotc/core/Flags.scala b/compiler/src/dotty/tools/dotc/core/Flags.scala index 63fbc98dc..d2a1c58f5 100644 --- a/compiler/src/dotty/tools/dotc/core/Flags.scala +++ b/compiler/src/dotty/tools/dotc/core/Flags.scala @@ -613,9 +613,6 @@ object Flags { /** A private parameter accessor */ final val PrivateParamAccessor = allOf(Private, ParamAccessor) - /** A type parameter introduced with [type ... ] */ - final val NamedTypeParam = allOf(TypeParam, ParamAccessor) - /** A local parameter */ final val ParamAndLocal = allOf(Param, Local) diff --git a/compiler/src/dotty/tools/dotc/core/SymDenotations.scala b/compiler/src/dotty/tools/dotc/core/SymDenotations.scala index a3475e14c..9b9caf8e7 100644 --- a/compiler/src/dotty/tools/dotc/core/SymDenotations.scala +++ b/compiler/src/dotty/tools/dotc/core/SymDenotations.scala @@ -1086,9 +1086,6 @@ object SymDenotations { /** The type parameters of a class symbol, Nil for all other symbols */ def typeParams(implicit ctx: Context): List[TypeSymbol] = Nil - /** The named type parameters declared or inherited by this symbol */ - def namedTypeParams(implicit ctx: Context): Set[TypeSymbol] = Set() - /** The type This(cls), where cls is this class, NoPrefix for all other symbols */ def thisType(implicit ctx: Context): Type = NoPrefix @@ -1226,11 +1223,9 @@ object SymDenotations { /** TODO: Document why caches are supposedly safe to use */ private[this] var myTypeParams: List[TypeSymbol] = _ - private[this] var myNamedTypeParams: Set[TypeSymbol] = _ - /** The type parameters in this class, in the order they appear in the current * scope `decls`. This might be temporarily the incorrect order when - * reading Scala2 pickled info. The problem is fixed by `updateTypeParams` + * reading Scala2 pickled info. The problem is fixed by `ensureTypeParamsInCorrectOrder`, * which is called once an unpickled symbol has been completed. */ private def typeParamsFromDecls(implicit ctx: Context) = @@ -1253,16 +1248,6 @@ object SymDenotations { myTypeParams } - /** The named type parameters declared or inherited by this class */ - override final def namedTypeParams(implicit ctx: Context): Set[TypeSymbol] = { - def computeNamedTypeParams: Set[TypeSymbol] = - if (ctx.erasedTypes || is(Module)) Set() // fast return for modules to avoid scanning package decls - else memberNames(abstractTypeNameFilter).map(name => - info.member(name).symbol.asType).filter(_.is(TypeParam, butNot = ExpandedName)).toSet - if (myNamedTypeParams == null) myNamedTypeParams = computeNamedTypeParams - myNamedTypeParams - } - override protected[dotc] final def info_=(tp: Type) = { super.info_=(tp) myTypeParams = null // changing the info might change decls, and with it typeParams diff --git a/compiler/src/dotty/tools/dotc/core/TypeApplications.scala b/compiler/src/dotty/tools/dotc/core/TypeApplications.scala index 70819e590..c713cd542 100644 --- a/compiler/src/dotty/tools/dotc/core/TypeApplications.scala +++ b/compiler/src/dotty/tools/dotc/core/TypeApplications.scala @@ -246,67 +246,6 @@ class TypeApplications(val self: Type) extends AnyVal { case _ => Nil } - /** The named type parameters declared or inherited by this type. - * These are all uninstantiated named type parameters of this type or one - * of its base types. - */ - final def namedTypeParams(implicit ctx: Context): Set[TypeSymbol] = self match { - case self: ClassInfo => - self.cls.namedTypeParams - case self: RefinedType => - self.parent.namedTypeParams.filterNot(_.name == self.refinedName) - case self: SingletonType => - Set() - case self: TypeProxy => - self.underlying.namedTypeParams - case _ => - Set() - } - - /** The smallest supertype of this type that instantiated none of the named type parameters - * in `params`. That is, for each named type parameter `p` in `params`, either there is - * no type field named `p` in this type, or `p` is a named type parameter of this type. - * The first case is important for the recursive case of AndTypes, because some of their operands might - * be missing the named parameter altogether, but the AndType as a whole can still - * contain it. - */ - final def widenToNamedTypeParams(params: Set[TypeSymbol])(implicit ctx: Context): Type = { - - /** Is widening not needed for `tp`? */ - def isOK(tp: Type) = { - val ownParams = tp.namedTypeParams - def isMissingOrOpen(param: TypeSymbol) = { - val ownParam = tp.nonPrivateMember(param.name).symbol - !ownParam.exists || ownParams.contains(ownParam.asType) - } - params.forall(isMissingOrOpen) - } - - /** Widen type by forming the intersection of its widened parents */ - def widenToParents(tp: Type) = { - val parents = tp.parents.map(p => - tp.baseTypeWithArgs(p.symbol).widenToNamedTypeParams(params)) - parents.reduceLeft(ctx.typeComparer.andType(_, _)) - } - - if (isOK(self)) self - else self match { - case self @ AppliedType(tycon, args) if !isOK(tycon) => - widenToParents(self) - case self: TypeRef if self.symbol.isClass => - widenToParents(self) - case self: RefinedType => - val parent1 = self.parent.widenToNamedTypeParams(params) - if (params.exists(_.name == self.refinedName)) parent1 - else self.derivedRefinedType(parent1, self.refinedName, self.refinedInfo) - case self: TypeProxy => - self.superType.widenToNamedTypeParams(params) - case self: AndOrType => - self.derivedAndOrType( - self.tp1.widenToNamedTypeParams(params), self.tp2.widenToNamedTypeParams(params)) - } - } - /** Is self type higher-kinded (i.e. of kind != "*")? */ def isHK(implicit ctx: Context): Boolean = self.dealias match { case self: TypeRef => self.info.isHK diff --git a/compiler/src/dotty/tools/dotc/core/TypeComparer.scala b/compiler/src/dotty/tools/dotc/core/TypeComparer.scala index fca111702..b97dfe684 100644 --- a/compiler/src/dotty/tools/dotc/core/TypeComparer.scala +++ b/compiler/src/dotty/tools/dotc/core/TypeComparer.scala @@ -1264,22 +1264,10 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling { } } - /** `op(tp1, tp2)` unless `tp1` and `tp2` are type-constructors with at least - * some unnamed type parameters. + /** `op(tp1, tp2)` unless `tp1` and `tp2` are type-constructors. * In the latter case, combine `tp1` and `tp2` under a type lambda like this: * * [X1, ..., Xn] -> op(tp1[X1, ..., Xn], tp2[X1, ..., Xn]) - * - * Note: There is a tension between named and positional parameters here, which - * is impossible to resolve completely. Say you have - * - * C[type T], D[type U] - * - * Then do you expand `C & D` to `[T] -> C[T] & D[T]` or not? Under the named - * type parameter interpretation, this would be wrong whereas under the traditional - * higher-kinded interpretation this would be required. The problem arises from - * allowing both interpretations. A possible remedy is to be somehow stricter - * in where we allow which interpretation. */ private def liftIfHK(tp1: Type, tp2: Type, op: (Type, Type) => Type, original: (Type, Type) => Type) = { val tparams1 = tp1.typeParams diff --git a/compiler/src/dotty/tools/dotc/core/TypeOps.scala b/compiler/src/dotty/tools/dotc/core/TypeOps.scala index 308e6e306..6c40794e3 100644 --- a/compiler/src/dotty/tools/dotc/core/TypeOps.scala +++ b/compiler/src/dotty/tools/dotc/core/TypeOps.scala @@ -428,16 +428,10 @@ trait TypeOps { this: Context => // TODO: Make standalone object. case tp: TypeRef => tp case tp @ RefinedType(tp1, name: TypeName, rinfo) => - rinfo match { - case TypeAlias(TypeRef(pre, name1)) if name1 == name && (pre =:= cls.thisType) => - // Don't record refinements of the form X = this.X (These can arise using named parameters). - typr.println(s"dropping refinement $tp") - case _ => - val prevInfo = refinements(name) - refinements = refinements.updated(name, - if (prevInfo == null) tp.refinedInfo else prevInfo & tp.refinedInfo) - formals = formals.updated(name, tp1.typeParamNamed(name)) - } + val prevInfo = refinements(name) + refinements = refinements.updated(name, + if (prevInfo == null) tp.refinedInfo else prevInfo & tp.refinedInfo) + formals = formals.updated(name, tp1.typeParamNamed(name)) normalizeToRef(tp1) case _: ErrorType => defn.AnyType diff --git a/compiler/src/dotty/tools/dotc/core/Types.scala b/compiler/src/dotty/tools/dotc/core/Types.scala index f56c6a286..9dd767a8e 100644 --- a/compiler/src/dotty/tools/dotc/core/Types.scala +++ b/compiler/src/dotty/tools/dotc/core/Types.scala @@ -549,13 +549,7 @@ object Types { def goThis(tp: ThisType) = { val d = go(tp.underlying) - if (d.exists) - if ((pre eq tp) && d.symbol.is(NamedTypeParam) && (d.symbol.owner eq tp.cls)) - // If we look for a named type parameter `P` in `C.this.P`, looking up - // the fully applied self type of `C` will give as an info the alias type - // `P = this.P`. We need to return a denotation with the underlying bounds instead. - d.symbol.denot - else d + if (d.exists) d else // There is a special case to handle: // trait Super { this: Sub => private class Inner {} println(this.Inner) } diff --git a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala index b27bff37a..65c7a290d 100644 --- a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala +++ b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala @@ -805,7 +805,7 @@ object Parsers { private def simpleTypeRest(t: Tree): Tree = in.token match { case HASH => simpleTypeRest(typeProjection(t)) case LBRACKET => simpleTypeRest(atPos(startOffset(t)) { - AppliedTypeTree(t, typeArgs(namedOK = true, wildOK = true)) }) + AppliedTypeTree(t, typeArgs(namedOK = false, wildOK = true)) }) case _ => t } @@ -1664,7 +1664,7 @@ object Parsers { /* -------- PARAMETERS ------------------------------------------- */ /** ClsTypeParamClause::= `[' ClsTypeParam {`,' ClsTypeParam} `]' - * ClsTypeParam ::= {Annotation} [{Modifier} type] [`+' | `-'] + * ClsTypeParam ::= {Annotation} [`+' | `-'] * id [HkTypeParamClause] TypeParamBounds * * DefTypeParamClause::= `[' DefTypeParam {`,' DefTypeParam} `]' @@ -1680,25 +1680,17 @@ object Parsers { def typeParam(): TypeDef = { val isConcreteOwner = ownerKind == ParamOwner.Class || ownerKind == ParamOwner.Def val start = in.offset - var mods = annotsAsMods() - if (ownerKind == ParamOwner.Class) { - mods = modifiers(start = mods) - mods = - atPos(start, in.offset) { - if (in.token == TYPE) { - val mod = atPos(in.skipToken()) { Mod.Type() } - (mods | Param | ParamAccessor).withAddedMod(mod) - } else { - if (mods.hasFlags) syntaxError(TypeParamsTypeExpected(mods, ident())) - mods | Param | PrivateLocal - } - } - } - else mods = atPos(start) (mods | Param) - if (ownerKind != ParamOwner.Def) { - if (isIdent(nme.raw.PLUS)) mods |= Covariant - else if (isIdent(nme.raw.MINUS)) mods |= Contravariant - if (mods is VarianceFlags) in.nextToken() + val mods = atPos(start) { + annotsAsMods() | { + if (ownerKind == ParamOwner.Class) Param | PrivateLocal + else Param + } | { + if (ownerKind != ParamOwner.Def) + if (isIdent(nme.raw.PLUS)) { in.nextToken(); Covariant } + else if (isIdent(nme.raw.MINUS)) { in.nextToken(); Contravariant } + else EmptyFlags + else EmptyFlags + } } atPos(start, nameStart) { val name = diff --git a/compiler/src/dotty/tools/dotc/typer/Namer.scala b/compiler/src/dotty/tools/dotc/typer/Namer.scala index d5f171fe3..96660f15c 100644 --- a/compiler/src/dotty/tools/dotc/typer/Namer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Namer.scala @@ -753,11 +753,10 @@ class Namer { typer: Typer => /* Check parent type tree `parent` for the following well-formedness conditions: * (1) It must be a class type with a stable prefix (@see checkClassTypeWithStablePrefix) * (2) If may not derive from itself - * (3) Overriding type parameters must be correctly forwarded. (@see checkTypeParamOverride) - * (4) The class is not final - * (5) If the class is sealed, it is defined in the same compilation unit as the current class + * (3) The class is not final + * (4) If the class is sealed, it is defined in the same compilation unit as the current class */ - def checkedParentType(parent: untpd.Tree, paramAccessors: List[Symbol]): Type = { + def checkedParentType(parent: untpd.Tree): Type = { val ptype = parentType(parent)(ctx.superCallContext) if (cls.isRefinementClass) ptype else { @@ -772,8 +771,6 @@ class Namer { typer: Typer => ctx.error(i"cyclic inheritance: $cls extends itself$addendum", parent.pos) defn.ObjectType } - else if (!paramAccessors.forall(checkTypeParamOverride(pt, _))) - defn.ObjectType else { val pclazz = pt.typeSymbol if (pclazz.is(Final)) @@ -785,47 +782,7 @@ class Namer { typer: Typer => } } - /* Check that every parameter with the same name as a visible named parameter in the parent - * class satisfies the following two conditions: - * (1) The overriding parameter is also named (i.e. not local/name mangled). - * (2) The overriding parameter is passed on directly to the parent parameter, or the - * parent parameter is not fully defined. - * @return true if conditions are satisfied, false otherwise. - */ - def checkTypeParamOverride(parent: Type, paramAccessor: Symbol): Boolean = { - var ok = true - val pname = paramAccessor.name - - def illegal(how: String): Unit = { - ctx.error(em"Illegal override of public type parameter $pname in $parent$how", paramAccessor.pos) - ok = false - } - - def checkAlias(tp: Type): Unit = tp match { - case tp: RefinedType => - if (tp.refinedName == pname) - tp.refinedInfo match { - case TypeAlias(alias) => - alias match { - case TypeRef(pre, name1) if name1 == pname && (pre =:= cls.thisType) => - // OK, parameter is passed on directly - case _ => - illegal(em".\nParameter is both redeclared and instantiated with $alias.") - } - case _ => // OK, argument is not fully defined - } - else checkAlias(tp.parent) - case _ => - } - if (parent.nonPrivateMember(paramAccessor.name).symbol.is(Param)) - if (paramAccessor is Private) - illegal("\nwith private parameter. Parameter definition needs to be prefixed with `type'.") - else - checkAlias(parent) - ok - } - - addAnnotations(denot.symbol, original) + addAnnotations(denot.symbol, original) val selfInfo = if (self.isEmpty) NoType @@ -853,8 +810,7 @@ class Namer { typer: Typer => indexAndAnnotate(rest)(inClassContext(selfInfo)) - val tparamAccessors = decls.filter(_ is TypeParamAccessor).toList - val parentTypes = ensureFirstIsClass(parents.map(checkedParentType(_, tparamAccessors))) + val parentTypes = ensureFirstIsClass(parents.map(checkedParentType(_))) val parentRefs = ctx.normalizeToClassRefs(parentTypes, cls, decls) typr.println(s"completing $denot, parents = $parents, parentTypes = $parentTypes, parentRefs = $parentRefs") diff --git a/compiler/src/dotty/tools/dotc/typer/RefChecks.scala b/compiler/src/dotty/tools/dotc/typer/RefChecks.scala index 7c573d23c..23d05e087 100644 --- a/compiler/src/dotty/tools/dotc/typer/RefChecks.scala +++ b/compiler/src/dotty/tools/dotc/typer/RefChecks.scala @@ -241,8 +241,6 @@ object RefChecks { isDefaultGetter(member.name) || // default getters are not checked for compatibility memberTp.overrides(otherTp) - def domain(sym: Symbol): Set[Name] = sym.info.namedTypeParams.map(_.name) - //Console.println(infoString(member) + " overrides " + infoString(other) + " in " + clazz);//DEBUG // return if we already checked this combination elsewhere @@ -344,9 +342,6 @@ object RefChecks { overrideError("cannot be used here - only term macros can override term macros") } else if (!compatibleTypes) { overrideError("has incompatible type" + err.whyNoMatchStr(memberTp, otherTp)) - } else if (member.isType && domain(member) != domain(other)) { - overrideError("has different named type parameters: "+ - i"[${domain(member).toList}%, %] instead of [${domain(other).toList}%, %]") } else { checkOverrideDeprecated() } diff --git a/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala b/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala index 53ce5555b..6bf8dcbbc 100644 --- a/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala +++ b/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala @@ -199,10 +199,7 @@ trait TypeAssigner { } } else if (d.symbol is TypeParamAccessor) - if (d.info.isAlias) - ensureAccessible(d.info.bounds.hi, superAccess, pos) - else // It's a named parameter, use the non-symbolic representation to pick up inherited versions as well - d.symbol.owner.thisType.select(d.symbol.name) + ensureAccessible(d.info.bounds.hi, superAccess, pos) else ctx.makePackageObjPrefixExplicit(tpe withDenot d) case _ => @@ -452,23 +449,10 @@ trait TypeAssigner { } def assignType(tree: untpd.AppliedTypeTree, tycon: Tree, args: List[Tree])(implicit ctx: Context) = { + assert(!hasNamedArg(args)) val tparams = tycon.tpe.typeParams - lazy val ntparams = tycon.tpe.namedTypeParams - def refineNamed(tycon: Type, arg: Tree) = arg match { - case ast.Trees.NamedArg(name, argtpt) => - // Dotty deviation: importing ast.Trees._ and matching on NamedArg gives a cyclic ref error - val tparam = tparams.find(_.paramName == name) match { - case Some(tparam) => tparam - case none => ntparams.find(_.name == name).getOrElse(NoSymbol) - } - if (tparam.isTypeParam) RefinedType(tycon, name, argtpt.tpe.toBounds(tparam)) - else errorType(i"$tycon does not have a parameter or abstract type member named $name", arg.pos) - case _ => - errorType(s"named and positional type arguments may not be mixed", arg.pos) - } val ownType = - if (hasNamedArg(args)) (tycon.tpe /: args)(refineNamed) - else if (sameLength(tparams, args)) tycon.tpe.appliedTo(args.tpes) + if (sameLength(tparams, args)) tycon.tpe.appliedTo(args.tpes) else wrongNumberOfTypeArgs(tycon.tpe, tparams, args, tree.pos) tree.withType(ownType) } diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index 57e3c1b88..b2e9d639d 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -1064,23 +1064,21 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit } else { var args = tree.args - val args1 = - if (hasNamedArg(args)) typedNamedArgs(args) - else { - if (args.length != tparams.length) { - wrongNumberOfTypeArgs(tpt1.tpe, tparams, args, tree.pos) - args = args.take(tparams.length) - } - def typedArg(arg: untpd.Tree, tparam: TypeParamInfo) = { - val (desugaredArg, argPt) = - if (ctx.mode is Mode.Pattern) - (if (isVarPattern(arg)) desugar.patternVar(arg) else arg, tparam.paramBounds) - else - (arg, WildcardType) - typed(desugaredArg, argPt) - } - args.zipWithConserve(tparams)(typedArg(_, _)).asInstanceOf[List[Tree]] + val args1 = { + if (args.length != tparams.length) { + wrongNumberOfTypeArgs(tpt1.tpe, tparams, args, tree.pos) + args = args.take(tparams.length) + } + def typedArg(arg: untpd.Tree, tparam: TypeParamInfo) = { + val (desugaredArg, argPt) = + if (ctx.mode is Mode.Pattern) + (if (isVarPattern(arg)) desugar.patternVar(arg) else arg, tparam.paramBounds) + else + (arg, WildcardType) + typed(desugaredArg, argPt) } + args.zipWithConserve(tparams)(typedArg(_, _)).asInstanceOf[List[Tree]] + } // check that arguments conform to bounds is done in phase PostTyper assignType(cpy.AppliedTypeTree(tree)(tpt1, args1), tpt1, args1) } diff --git a/docs/docs/internals/syntax.md b/docs/docs/internals/syntax.md index b7d5936c2..7f06cdc2a 100644 --- a/docs/docs/internals/syntax.md +++ b/docs/docs/internals/syntax.md @@ -126,7 +126,7 @@ InfixType ::= RefinedType {id [nl] RefinedType} RefinedType ::= WithType {[nl] Refinement} RefinedTypeTree(t, ds) WithType ::= AnnotType {‘with’ AnnotType} (deprecated) AnnotType ::= SimpleType {Annotation} Annotated(t, annot) -SimpleType ::= SimpleType (TypeArgs | NamedTypeArgs) AppliedTypeTree(t, args) +SimpleType ::= SimpleType TypeArgs AppliedTypeTree(t, args) | SimpleType ‘#’ id Select(t, name) | StableId | Path ‘.’ ‘type’ SingletonTypeTree(p) @@ -240,7 +240,7 @@ ArgumentPatterns ::= ‘(’ [Patterns] ‘)’ ### Type and Value Parameters ```ebnf ClsTypeParamClause::= ‘[’ ClsTypeParam {‘,’ ClsTypeParam} ‘]’ -ClsTypeParam ::= {Annotation} [{Modifier} type] [‘+’ | ‘-’] TypeDef(Modifiers, name, tparams, bounds) +ClsTypeParam ::= {Annotation} [‘+’ | ‘-’] TypeDef(Modifiers, name, tparams, bounds) id [HkTypeParamClause] TypeParamBounds Bound(below, above, context) DefTypeParamClause::= ‘[’ DefTypeParam {‘,’ DefTypeParam} ‘]’ diff --git a/tests/neg/namedTypeParams.scala b/tests/neg/namedTypeParams.scala new file mode 100644 index 000000000..75bb1cd7e --- /dev/null +++ b/tests/neg/namedTypeParams.scala @@ -0,0 +1,12 @@ +class C[T] +class D[type T] // error: identifier expected, but `type` found + +object Test { + + val x: C[T = Int] = // error: ']' expected, but `=` found // error + new C[T = Int] // error: ']' expected, but `=` found // error + + class E extends C[T = Int] // error: ']' expected, but `=` found // error + class F extends C[T = Int]() // error: ']' expected, but `=` found // error + +} -- cgit v1.2.3