Skip to content

Commit 6b7c51c

Browse files
committed
Merge pull request #834 from dotty-staging/fix-#825
Fix #825
2 parents 9ad85c3 + 30e5f32 commit 6b7c51c

File tree

15 files changed

+222
-63
lines changed

15 files changed

+222
-63
lines changed

src/dotty/tools/dotc/ast/Desugar.scala

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -380,18 +380,22 @@ object desugar {
380380
// synthetic implicit C[Ts](p11: T11, ..., p1N: T1N) ... (pM1: TM1, ..., pMN: TMN): C[Ts] =
381381
// new C[Ts](p11, ..., p1N) ... (pM1, ..., pMN) =
382382
val implicitWrappers =
383-
if (mods is Implicit) {
384-
if (ctx.owner is Package)
385-
ctx.error("implicit classes may not be toplevel", cdef.pos)
386-
if (mods is Case)
387-
ctx.error("implicit classes may not case classes", cdef.pos)
388-
383+
if (!mods.is(Implicit))
384+
Nil
385+
else if (ctx.owner is Package) {
386+
ctx.error("implicit classes may not be toplevel", cdef.pos)
387+
Nil
388+
}
389+
else if (mods is Case) {
390+
ctx.error("implicit classes may not be case classes", cdef.pos)
391+
Nil
392+
}
393+
else
389394
// implicit wrapper is typechecked in same scope as constructor, so
390395
// we can reuse the constructor parameters; no derived params are needed.
391396
DefDef(name.toTermName, constrTparams, constrVparamss, classTypeRef, creatorExpr)
392397
.withFlags(Synthetic | Implicit) :: Nil
393-
}
394-
else Nil
398+
395399

396400
val self1 = {
397401
val selfType = if (self.tpt.isEmpty) classTypeRef else self.tpt

src/dotty/tools/dotc/core/TypeErasure.scala

Lines changed: 42 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -401,10 +401,10 @@ class TypeErasure(isJava: Boolean, semiEraseVCs: Boolean, isConstructor: Boolean
401401
private def eraseDerivedValueClassRef(tref: TypeRef)(implicit ctx: Context): Type = {
402402
val cls = tref.symbol.asClass
403403
val underlying = underlyingOfValueClass(cls)
404-
ErasedValueType(cls, valueErasure(underlying))
404+
if (underlying.exists) ErasedValueType(cls, valueErasure(underlying))
405+
else NoType
405406
}
406407

407-
408408
private def eraseNormalClassRef(tref: TypeRef)(implicit ctx: Context): Type = {
409409
val cls = tref.symbol.asClass
410410
(if (cls.owner is Package) normalizeClass(cls) else cls).typeRef
@@ -439,36 +439,45 @@ class TypeErasure(isJava: Boolean, semiEraseVCs: Boolean, isConstructor: Boolean
439439
/** The name of the type as it is used in `Signature`s.
440440
* Need to ensure correspondence with erasure!
441441
*/
442-
private def sigName(tp: Type)(implicit ctx: Context): TypeName = tp match {
443-
case ErasedValueType(_, underlying) =>
444-
sigName(underlying)
445-
case tp: TypeRef =>
446-
if (!tp.denot.exists) throw new MissingType(tp.prefix, tp.name)
447-
val sym = tp.symbol
448-
if (!sym.isClass) {
449-
val info = tp.info
450-
if (!info.exists) assert(false, "undefined: $tp with symbol $sym")
451-
sigName(info)
452-
}
453-
else if (isDerivedValueClass(sym)) sigName(eraseDerivedValueClassRef(tp))
454-
else normalizeClass(sym.asClass).fullName.asTypeName
455-
case defn.ArrayType(elem) =>
456-
sigName(this(tp))
457-
case JavaArrayType(elem) =>
458-
sigName(elem) ++ "[]"
459-
case tp: TermRef =>
460-
sigName(tp.widen)
461-
case ExprType(rt) =>
462-
sigName(defn.FunctionType(Nil, rt))
463-
case tp: TypeProxy =>
464-
sigName(tp.underlying)
465-
case ErrorType | WildcardType =>
466-
tpnme.WILDCARD
467-
case tp: WildcardType =>
468-
sigName(tp.optBounds)
469-
case _ =>
470-
val erased = this(tp)
471-
assert(erased ne tp, tp)
472-
sigName(erased)
442+
private def sigName(tp: Type)(implicit ctx: Context): TypeName = try {
443+
tp match {
444+
case ErasedValueType(_, underlying) =>
445+
sigName(underlying)
446+
case tp: TypeRef =>
447+
if (!tp.denot.exists) throw new MissingType(tp.prefix, tp.name)
448+
val sym = tp.symbol
449+
if (!sym.isClass) {
450+
val info = tp.info
451+
if (!info.exists) assert(false, "undefined: $tp with symbol $sym")
452+
return sigName(info)
453+
}
454+
if (isDerivedValueClass(sym)) {
455+
val erasedVCRef = eraseDerivedValueClassRef(tp)
456+
if (erasedVCRef.exists) return sigName(erasedVCRef)
457+
}
458+
normalizeClass(sym.asClass).fullName.asTypeName
459+
case defn.ArrayType(elem) =>
460+
sigName(this(tp))
461+
case JavaArrayType(elem) =>
462+
sigName(elem) ++ "[]"
463+
case tp: TermRef =>
464+
sigName(tp.widen)
465+
case ExprType(rt) =>
466+
sigName(defn.FunctionType(Nil, rt))
467+
case tp: TypeProxy =>
468+
sigName(tp.underlying)
469+
case ErrorType | WildcardType =>
470+
tpnme.WILDCARD
471+
case tp: WildcardType =>
472+
sigName(tp.optBounds)
473+
case _ =>
474+
val erased = this(tp)
475+
assert(erased ne tp, tp)
476+
sigName(erased)
477+
}
478+
} catch {
479+
case ex: AssertionError =>
480+
println(s"no sig for $tp")
481+
throw ex
473482
}
474483
}

src/dotty/tools/dotc/parsing/Parsers.scala

Lines changed: 19 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1343,7 +1343,7 @@ object Parsers {
13431343
private def flagOfToken(tok: Int): FlagSet = tok match {
13441344
case ABSTRACT => Abstract
13451345
case FINAL => Final
1346-
case IMPLICIT => Implicit
1346+
case IMPLICIT => ImplicitCommon
13471347
case LAZY => Lazy
13481348
case OVERRIDE => Override
13491349
case PRIVATE => Private
@@ -1377,12 +1377,21 @@ object Parsers {
13771377
|| flags1.isTypeFlags && flags2.isTypeFlags
13781378
)
13791379

1380-
def addFlag(mods: Modifiers, flag: FlagSet): Modifiers =
1380+
def addFlag(mods: Modifiers, flag: FlagSet): Modifiers = {
1381+
def incompatible(kind: String) = {
1382+
syntaxError(s"modifier(s) `${mods.flags}' not allowed for $kind")
1383+
Modifiers(flag)
1384+
}
13811385
if (compatible(mods.flags, flag)) mods | flag
1382-
else {
1383-
syntaxError(s"illegal modifier combination: ${mods.flags} and $flag")
1384-
mods
1386+
else flag match {
1387+
case Trait => incompatible("trait")
1388+
case Method => incompatible("method")
1389+
case Mutable => incompatible("variable")
1390+
case _ =>
1391+
syntaxError(s"illegal modifier combination: ${mods.flags} and $flag")
1392+
mods
13851393
}
1394+
}
13861395

13871396
/** AccessQualifier ::= "[" (Id | this) "]"
13881397
*/
@@ -1726,6 +1735,7 @@ object Parsers {
17261735
}
17271736
makeConstructor(Nil, vparamss, rhs).withMods(mods)
17281737
} else {
1738+
val mods1 = addFlag(mods, Method)
17291739
val name = ident()
17301740
val tparams = typeParamClauseOpt(ParamOwner.Def)
17311741
val vparamss = paramClauses(name)
@@ -1735,7 +1745,7 @@ object Parsers {
17351745
if (atScala2Brace) tpt = scalaUnit else accept(EQUALS)
17361746
expr()
17371747
} else EmptyTree
1738-
DefDef(name, tparams, vparamss, tpt, rhs).withMods(mods | Method)
1748+
DefDef(name, tparams, vparamss, tpt, rhs).withMods(mods1)
17391749
}
17401750
}
17411751

@@ -1792,7 +1802,7 @@ object Parsers {
17921802
*/
17931803
def tmplDef(start: Int, mods: Modifiers): Tree = in.token match {
17941804
case TRAIT =>
1795-
classDef(posMods(start, mods | Trait))
1805+
classDef(posMods(start, addFlag(mods, Trait)))
17961806
case CLASS =>
17971807
classDef(posMods(start, mods))
17981808
case CASECLASS =>
@@ -2009,7 +2019,7 @@ object Parsers {
20092019
}
20102020

20112021
def localDef(start: Int, implicitFlag: FlagSet): Tree =
2012-
defOrDcl(start, defAnnotsMods(localModifierTokens) | implicitFlag)
2022+
defOrDcl(start, addFlag(defAnnotsMods(localModifierTokens), implicitFlag))
20132023

20142024
/** BlockStatSeq ::= { BlockStat semi } [ResultExpr]
20152025
* BlockStat ::= Import
@@ -2038,7 +2048,7 @@ object Parsers {
20382048
if (in.token == IMPLICIT) {
20392049
val start = in.skipToken()
20402050
if (isIdent) stats += implicitClosure(start, Location.InBlock)
2041-
else stats += localDef(start, Implicit)
2051+
else stats += localDef(start, ImplicitCommon)
20422052
} else {
20432053
stats += localDef(in.offset, EmptyFlags)
20442054
}

src/dotty/tools/dotc/typer/Checking.scala

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -230,6 +230,50 @@ object Checking {
230230
}
231231
checkTree((), refinement)
232232
}
233+
234+
/** Check that symbol's definition is well-formed. */
235+
def checkWellFormed(sym: Symbol)(implicit ctx: Context): Unit = {
236+
//println(i"check wf $sym with flags ${sym.flags}")
237+
def fail(msg: String) = ctx.error(msg, sym.pos)
238+
def varNote =
239+
if (sym.is(Mutable)) "\n(Note that variables need to be initialized to be defined)"
240+
else ""
241+
242+
def checkWithDeferred(flag: FlagSet) =
243+
if (sym.is(flag))
244+
fail(i"abstract member may not have `$flag' modifier")
245+
def checkNoConflict(flag1: FlagSet, flag2: FlagSet) =
246+
if (sym.is(allOf(flag1, flag2)))
247+
fail(i"illegal combination of modifiers: $flag1 and $flag2 for: $sym")
248+
249+
if (sym.is(ImplicitCommon)) {
250+
if (sym.owner.is(Package))
251+
fail(i"`implicit' modifier cannot be used for top-level definitions")
252+
if (sym.isType)
253+
fail(i"`implicit' modifier cannot be used for types or traits")
254+
}
255+
if (!sym.isClass && sym.is(Abstract))
256+
fail(i"`abstract' modifier can be used only for classes; it should be omitted for abstract members")
257+
if (sym.is(AbsOverride) && !sym.owner.is(Trait))
258+
fail(i"`abstract override' modifier only allowed for members of traits")
259+
if (sym.is(Trait) && sym.is(Final))
260+
fail(i"$sym may not be `final'")
261+
if (sym.hasAnnotation(defn.NativeAnnot)) {
262+
if (!sym.is(Deferred))
263+
fail(i"`@native' members may not have implementation")
264+
}
265+
else if (sym.is(Deferred, butNot = Param) && !sym.isSelfSym) {
266+
if (!sym.owner.isClass || sym.owner.is(Module) || sym.owner.isAnonymousClass)
267+
fail(i"only classes can have declared but undefined members$varNote")
268+
checkWithDeferred(Private)
269+
checkWithDeferred(Final)
270+
}
271+
if (sym.isValueClass && sym.is(Trait) && !sym.isRefinementClass)
272+
fail(i"$sym cannot extend AnyVal")
273+
checkNoConflict(Final, Sealed)
274+
checkNoConflict(Private, Protected)
275+
checkNoConflict(Abstract, Override)
276+
}
233277
}
234278

235279
trait Checking {

src/dotty/tools/dotc/typer/Namer.scala

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -234,6 +234,18 @@ class Namer { typer: Typer =>
234234
sym
235235
}
236236

237+
def checkFlags(flags: FlagSet) =
238+
if (flags.isEmpty) flags
239+
else {
240+
val (ok, adapted, kind) = tree match {
241+
case tree: TypeDef => (flags.isTypeFlags, flags.toTypeFlags, "type")
242+
case _ => (flags.isTermFlags, flags.toTermFlags, "value")
243+
}
244+
if (!ok)
245+
ctx.error(i"modifier(s) `$flags' incompatible with $kind definition", tree.pos)
246+
adapted
247+
}
248+
237249
/** Add moduleClass/sourceModule to completer if it is for a module val or class */
238250
def adjustIfModule(completer: LazyType, tree: MemberDef) =
239251
if (tree.mods is Module) ctx.adjustModuleCompleter(completer, tree.name.encode)
@@ -261,14 +273,16 @@ class Namer { typer: Typer =>
261273
tree match {
262274
case tree: TypeDef if tree.isClassDef =>
263275
val name = checkNoConflict(tree.name.encode).asTypeName
276+
val flags = checkFlags(tree.mods.flags &~ Implicit)
264277
val cls = record(ctx.newClassSymbol(
265-
ctx.owner, name, tree.mods.flags | inSuperCall,
278+
ctx.owner, name, flags | inSuperCall,
266279
cls => adjustIfModule(new ClassCompleter(cls, tree)(ctx), tree),
267280
privateWithinClass(tree.mods), tree.pos, ctx.source.file))
268281
cls.completer.asInstanceOf[ClassCompleter].init()
269282
cls
270283
case tree: MemberDef =>
271284
val name = checkNoConflict(tree.name.encode)
285+
val flags = checkFlags(tree.mods.flags)
272286
val isDeferred = lacksDefinition(tree)
273287
val deferred = if (isDeferred) Deferred else EmptyFlags
274288
val method = if (tree.isInstanceOf[DefDef]) Method else EmptyFlags
@@ -291,7 +305,7 @@ class Namer { typer: Typer =>
291305
val cctx = if (tree.name == nme.CONSTRUCTOR && !(tree.mods is JavaDefined)) ctx.outer else ctx
292306

293307
record(ctx.newSymbol(
294-
ctx.owner, name, tree.mods.flags | deferred | method | higherKinded | inSuperCall1,
308+
ctx.owner, name, flags | deferred | method | higherKinded | inSuperCall1,
295309
adjustIfModule(new Completer(tree)(cctx), tree),
296310
privateWithinClass(tree.mods), tree.pos))
297311
case tree: Import =>
@@ -517,6 +531,7 @@ class Namer { typer: Typer =>
517531
def completeInCreationContext(denot: SymDenotation): Unit = {
518532
denot.info = typeSig(denot.symbol)
519533
addAnnotations(denot)
534+
Checking.checkWellFormed(denot.symbol)
520535
}
521536
}
522537

@@ -585,6 +600,7 @@ class Namer { typer: Typer =>
585600
index(rest)(inClassContext(selfInfo))
586601
denot.info = ClassInfo(cls.owner.thisType, cls, parentRefs, decls, selfInfo)
587602
addAnnotations(denot)
603+
Checking.checkWellFormed(cls)
588604
if (isDerivedValueClass(cls)) cls.setFlag(Final)
589605
cls.setApplicableFlags(
590606
(NoInitsInterface /: impl.body)((fs, stat) => fs & defKind(stat)))

test/dotc/tests.scala

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,7 @@ class tests extends CompilerTest {
105105
@Test def neg_typedapply() = compileFile(negDir, "typedapply", xerrors = 4)
106106
@Test def neg_typedidents() = compileFile(negDir, "typedIdents", xerrors = 2)
107107
@Test def neg_assignments() = compileFile(negDir, "assignments", xerrors = 3)
108-
@Test def neg_typers() = compileFile(negDir, "typers", xerrors = 13)(allowDoubleBindings)
108+
@Test def neg_typers() = compileFile(negDir, "typers", xerrors = 14)(allowDoubleBindings)
109109
@Test def neg_privates() = compileFile(negDir, "privates", xerrors = 2)
110110
@Test def neg_rootImports = compileFile(negDir, "rootImplicits", xerrors = 2)
111111
@Test def neg_templateParents() = compileFile(negDir, "templateParents", xerrors = 3)
@@ -135,7 +135,7 @@ class tests extends CompilerTest {
135135
@Test def neg_badAuxConstr = compileFile(negDir, "badAuxConstr", xerrors = 2)
136136
@Test def neg_typetest = compileFile(negDir, "typetest", xerrors = 1)
137137
@Test def neg_t1569_failedAvoid = compileFile(negDir, "t1569-failedAvoid", xerrors = 1)
138-
@Test def neg_clashes = compileFile(negDir, "clashes", xerrors = 2)
138+
@Test def neg_clashes = compileFile(negDir, "clashes", xerrors = 3)
139139
@Test def neg_cycles = compileFile(negDir, "cycles", xerrors = 8)
140140
@Test def neg_boundspropagation = compileFile(negDir, "boundspropagation", xerrors = 5)
141141
@Test def neg_refinedSubtyping = compileFile(negDir, "refinedSubtyping", xerrors = 2)
@@ -150,13 +150,16 @@ class tests extends CompilerTest {
150150
@Test def neg_instantiateAbstract = compileFile(negDir, "instantiateAbstract", xerrors = 8)
151151
@Test def neg_selfInheritance = compileFile(negDir, "selfInheritance", xerrors = 6)
152152
@Test def neg_selfreq = compileFile(negDir, "selfreq", xerrors = 4)
153-
@Test def neg_singletons = compileFile(negDir, "singletons", xerrors = 5)
153+
@Test def neg_singletons = compileFile(negDir, "singletons", xerrors = 8)
154154
@Test def neg_shadowedImplicits = compileFile(negDir, "arrayclone-new", xerrors = 2)
155155
@Test def neg_traitParamsTyper = compileFile(negDir, "traitParamsTyper", xerrors = 5)
156156
@Test def neg_traitParamsMixin = compileFile(negDir, "traitParamsMixin", xerrors = 2)
157157
@Test def neg_firstError = compileFile(negDir, "firstError", xerrors = 3)
158158
@Test def neg_implicitLowerBound = compileFile(negDir, "implicit-lower-bound", xerrors = 1)
159159
@Test def neg_partialApplications = compileFile(negDir, "partialApplications", xerrors = 8)
160+
@Test def neg_validate = compileFile(negDir, "validate", xerrors = 18)
161+
@Test def neg_validateParsing = compileFile(negDir, "validate-parsing", xerrors = 7)
162+
@Test def neg_validateRefchecks = compileFile(negDir, "validate-refchecks", xerrors = 2)
160163

161164
@Test def run_all = runFiles(runDir)
162165

tests/neg/i50-volatile.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
object Test {
1+
class Test {
22
class Base {
33
class Inner
44
}

tests/neg/singletons.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ object Test {
55

66
val n: null = null // error: Null is not a legal singleton type
77

8-
val sym: 'sym = 'sym // error: Symbol is a legal singleton type
8+
val sym: 'sym = 'sym // error: Symbol is not a legal singleton type
99

1010
val foo: s"abc" = "abc" // error: not a legal singleton type
1111
}

tests/neg/typers.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ object typers {
3030
}
3131

3232
type L[X] = scala.collection.immutable.List[X]
33-
type M[X, Y] <: scala.collection.immutable.Map[X, Y]
33+
type M[X, Y] <: scala.collection.immutable.Map[X, Y] // error: only classes can have declared but undefined members
3434

3535
object hk {
3636
def f(x: L) // error: missing type parameter

tests/neg/validate-parsing.scala

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
object A {
2+
sealed def y: Int = 1 // error: modifier(s) `sealed' not allowed for method
3+
sealed var x = 1 // error: modifier(s) `sealed' not allowed for variable
4+
lazy trait T // error: modifier(s) `lazy' not allowed for trait
5+
}
6+
7+
class C () {
8+
implicit this() = this() // error: ';' expected but 'implicit' found.
9+
override this() = this() // error: ';' expected but 'override' found.
10+
}
11+
class D override() // error: ';' expected but 'override' found.
12+
13+
case class ByName(x: => Int) // error: `val' parameters may not be call-by-name

0 commit comments

Comments
 (0)