Skip to content

Commit dfa3ec8

Browse files
committed
Merge pull request #1066 from dotty-staging/fix-#997
Fix #997
2 parents 2217a4e + 6f382a5 commit dfa3ec8

File tree

13 files changed

+140
-16
lines changed

13 files changed

+140
-16
lines changed

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

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -232,6 +232,7 @@ object desugar {
232232
def classDef(cdef: TypeDef)(implicit ctx: Context): Tree = {
233233
val TypeDef(name, impl @ Template(constr0, parents, self, _)) = cdef
234234
val mods = cdef.mods
235+
val accessFlags = (mods.flags & AccessFlags).toCommonFlags
235236

236237
val (constr1, defaultGetters) = defDef(constr0, isPrimaryConstructor = true) match {
237238
case meth: DefDef => (meth, Nil)
@@ -312,6 +313,7 @@ object desugar {
312313
case ValDef(_, tpt, _) => isRepeated(tpt)
313314
case _ => false
314315
})
316+
315317
val copyMeths =
316318
if (mods.is(Abstract) || hasRepeatedParam) Nil // cannot have default arguments for repeated parameters, hence copy method is not issued
317319
else {
@@ -346,7 +348,7 @@ object desugar {
346348
moduleDef(
347349
ModuleDef(
348350
name.toTermName, Template(emptyConstructor, parentTpt :: Nil, EmptyValDef, defs))
349-
.withMods(synthetic))
351+
.withFlags(Synthetic | accessFlags))
350352
.withPos(cdef.pos).toList
351353

352354
// The companion object definitions, if a companion is needed, Nil otherwise.
@@ -371,7 +373,7 @@ object desugar {
371373
if (mods is Abstract) Nil
372374
else
373375
DefDef(nme.apply, derivedTparams, derivedVparamss, TypeTree(), creatorExpr)
374-
.withMods(synthetic | (constr1.mods.flags & DefaultParameterized)) :: Nil
376+
.withFlags(Synthetic | (constr1.mods.flags & DefaultParameterized)) :: Nil
375377
val unapplyMeth = {
376378
val unapplyParam = makeSyntheticParameter(tpt = classTypeRef)
377379
val unapplyRHS = if (arity == 0) Literal(Constant(true)) else Ident(unapplyParam.name)
@@ -403,7 +405,7 @@ object desugar {
403405
// implicit wrapper is typechecked in same scope as constructor, so
404406
// we can reuse the constructor parameters; no derived params are needed.
405407
DefDef(name.toTermName, constrTparams, constrVparamss, classTypeRef, creatorExpr)
406-
.withFlags(Synthetic | Implicit)
408+
.withFlags(Synthetic | Implicit | accessFlags)
407409
.withPos(cdef.pos) :: Nil
408410

409411

@@ -453,7 +455,7 @@ object desugar {
453455
val clsName = name.moduleClassName
454456
val clsRef = Ident(clsName)
455457
val modul = ValDef(name, clsRef, New(clsRef, Nil))
456-
.withMods(mods | ModuleCreationFlags)
458+
.withMods(mods | ModuleCreationFlags | mods.flags & AccessFlags)
457459
.withPos(mdef.pos)
458460
val ValDef(selfName, selfTpt, _) = tmpl.self
459461
val selfMods = tmpl.self.mods
@@ -515,7 +517,7 @@ object desugar {
515517
derivedValDef(named, tpt, matchExpr, mods)
516518
case _ =>
517519
val tmpName = ctx.freshName().toTermName
518-
val patFlags = PrivateLocal | Synthetic | (mods.flags & Lazy)
520+
val patFlags = mods.flags & AccessFlags | Synthetic | (mods.flags & Lazy)
519521
val firstDef = ValDef(tmpName, TypeTree(), matchExpr).withFlags(patFlags)
520522
def selector(n: Int) = Select(Ident(tmpName), nme.selectorName(n))
521523
val restDefs =

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -543,6 +543,9 @@ object Flags {
543543
/** A lazy or deferred value */
544544
final val LazyOrDeferred = Lazy | Deferred
545545

546+
/** A synthetic or private definition */
547+
final val SyntheticOrPrivate = Synthetic | Private
548+
546549
/** A type parameter or type parameter accessor */
547550
final val TypeParamOrAccessor = TypeParam | TypeParamAccessor
548551

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

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1018,6 +1018,12 @@ object Types {
10181018
case _ => List()
10191019
}
10201020

1021+
/** The full parent types, including all type arguments */
1022+
def parentsWithArgs(implicit ctx: Context): List[Type] = this match {
1023+
case tp: TypeProxy => tp.underlying.parentsWithArgs
1024+
case _ => List()
1025+
}
1026+
10211027
/** The first parent of this type, AnyRef if list of parents is empty */
10221028
def firstParent(implicit ctx: Context): TypeRef = parents match {
10231029
case p :: _ => p
@@ -2785,7 +2791,7 @@ object Types {
27852791
}
27862792

27872793
/** The parent types with all type arguments */
2788-
def instantiatedParents(implicit ctx: Context): List[Type] =
2794+
override def parentsWithArgs(implicit ctx: Context): List[Type] =
27892795
parents mapConserve { pref =>
27902796
((pref: Type) /: pref.classSymbol.typeParams) { (parent, tparam) =>
27912797
val targSym = decls.lookup(tparam.name)

src/dotty/tools/dotc/printing/Disambiguation.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,12 @@ object Disambiguation {
1414
val variants = new mutable.HashMap[String, mutable.ListBuffer[Symbol]]
1515
}
1616

17-
def newPrinter: Context => Printer = {
17+
def newPrinter: Context => RefinedPrinter = {
1818
val state = new State
1919
new Printer(state)(_)
2020
}
2121

22-
class Printer(state: State)(_ctx: Context) extends RefinedPrinter(_ctx) {
22+
private class Printer(state: State)(_ctx: Context) extends RefinedPrinter(_ctx) {
2323
import state._
2424

2525
override def simpleNameString(sym: Symbol): String = {

src/dotty/tools/dotc/printing/RefinedPrinter.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -160,7 +160,7 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) {
160160
case ErasedValueType(clazz, underlying) =>
161161
return "ErasedValueType(" ~ toText(clazz.typeRef) ~ ", " ~ toText(underlying) ~ ")"
162162
case tp: ClassInfo =>
163-
return toTextParents(tp.instantiatedParents) ~ "{...}"
163+
return toTextParents(tp.parentsWithArgs) ~ "{...}"
164164
case JavaArrayType(elemtp) =>
165165
return toText(elemtp) ~ "[]"
166166
case tp: SelectionProto =>

src/dotty/tools/dotc/reporting/Reporter.scala

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,9 @@ trait Reporting { this: Context =>
106106
reporter.report(new Error(msg, pos))
107107
}
108108

109+
def errorOrMigrationWarning(msg: => String, pos: SourcePosition = NoSourcePosition): Unit =
110+
if (ctx.scala2Mode) migrationWarning(msg, pos) else error(msg, pos)
111+
109112
def restrictionError(msg: => String, pos: SourcePosition = NoSourcePosition): Unit =
110113
error(s"Implementation restriction: $msg", pos)
111114

src/dotty/tools/dotc/transform/PostTyper.scala

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -130,8 +130,10 @@ class PostTyper extends MacroTransform with IdentityDenotTransformer { thisTran
130130
private def transformAnnot(annot: Annotation)(implicit ctx: Context): Annotation =
131131
annot.derivedAnnotation(transformAnnot(annot.tree))
132132

133-
private def transformAnnots(tree: MemberDef)(implicit ctx: Context): Unit =
133+
private def transformMemberDef(tree: MemberDef)(implicit ctx: Context): Unit = {
134134
tree.symbol.transformAnnotations(transformAnnot)
135+
Checking.checkNoPrivateLeaks(tree)
136+
}
135137

136138
private def transformSelect(tree: Select, targs: List[Tree])(implicit ctx: Context): Tree = {
137139
val qual = tree.qualifier
@@ -211,10 +213,10 @@ class PostTyper extends MacroTransform with IdentityDenotTransformer { thisTran
211213
}
212214
finally parentNews = saved
213215
case tree: DefDef =>
214-
transformAnnots(tree)
216+
transformMemberDef(tree)
215217
superAcc.wrapDefDef(tree)(super.transform(tree).asInstanceOf[DefDef])
216218
case tree: TypeDef =>
217-
transformAnnots(tree)
219+
transformMemberDef(tree)
218220
val sym = tree.symbol
219221
val tree1 =
220222
if (sym.isClass) tree
@@ -224,7 +226,7 @@ class PostTyper extends MacroTransform with IdentityDenotTransformer { thisTran
224226
}
225227
super.transform(tree1)
226228
case tree: MemberDef =>
227-
transformAnnots(tree)
229+
transformMemberDef(tree)
228230
super.transform(tree)
229231
case tree: New if !inJavaAnnot && !parentNews.contains(tree) =>
230232
Checking.checkInstantiable(tree.tpe, tree.pos)

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

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -322,6 +322,46 @@ object Checking {
322322
checkNoConflict(Private, Protected)
323323
checkNoConflict(Abstract, Override)
324324
}
325+
326+
/** Check the type signature of the symbol `M` defined by `tree` does not refer
327+
* to a private type or value which is invisible at a point where `M` is still
328+
* visible. As an exception, we allow references to type aliases if the underlying
329+
* type of the alias is not a leak. So type aliases are transparent as far as
330+
* leak testing is concerned. See 997.scala for tests.
331+
*/
332+
def checkNoPrivateLeaks(tree: MemberDef)(implicit ctx: Context): Unit = {
333+
type Errors = List[(String, Position)]
334+
val sym = tree.symbol
335+
val notPrivate = new TypeAccumulator[Errors] {
336+
def accessBoundary(sym: Symbol): Symbol =
337+
if (sym.is(Private)) sym.owner
338+
else if (sym.privateWithin.exists) sym.privateWithin
339+
else if (sym.is(Package)) sym
340+
else accessBoundary(sym.owner)
341+
def apply(errors: Errors, tp: Type): Errors = tp match {
342+
case tp: NamedType =>
343+
val errors1 =
344+
if (tp.symbol.is(Private) &&
345+
!accessBoundary(sym).isContainedIn(tp.symbol.owner)) {
346+
(d"non-private $sym refers to private ${tp.symbol}\n in its type signature ${sym.info}", tree.pos) :: errors
347+
} else foldOver(errors, tp)
348+
if ((errors1 ne errors) && tp.info.isAlias) {
349+
// try to dealias to avoid a leak error
350+
val errors2 = apply(errors, tp.info.bounds.hi)
351+
if (errors2 eq errors) errors2
352+
else errors1
353+
} else errors1
354+
case tp: ClassInfo =>
355+
(apply(errors, tp.prefix) /: tp.parentsWithArgs)(apply)
356+
case _ =>
357+
foldOver(errors, tp)
358+
}
359+
}
360+
if (!sym.is(SyntheticOrPrivate) && sym.owner.isClass) {
361+
val errors = notPrivate(Nil, sym.info)
362+
errors.foreach { case (msg, pos) => ctx.errorOrMigrationWarning(msg, pos) }
363+
}
364+
}
325365
}
326366

327367
trait Checking {

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ trait TypeAssigner {
7171
if (tp1.typeSymbol.exists)
7272
return tp1
7373
}
74-
val parentType = info.instantiatedParents.reduceLeft(ctx.typeComparer.andType(_, _))
74+
val parentType = info.parentsWithArgs.reduceLeft(ctx.typeComparer.andType(_, _))
7575
def addRefinement(parent: Type, decl: Symbol) = {
7676
val inherited =
7777
parentType.findMember(decl.name, info.cls.thisType, Private)
@@ -292,7 +292,7 @@ trait TypeAssigner {
292292
else if (!mix.isEmpty) findMixinSuper(cls.info)
293293
else if (inConstrCall || ctx.erasedTypes) cls.info.firstParent
294294
else {
295-
val ps = cls.classInfo.instantiatedParents
295+
val ps = cls.classInfo.parentsWithArgs
296296
if (ps.isEmpty) defn.AnyType else ps.reduceLeft((x: Type, y: Type) => x & y)
297297
}
298298
tree.withType(SuperType(cls.thisType, owntype))

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import config.Printers.variances
1313
* The method should be invoked once for each Template.
1414
*/
1515
object VarianceChecker {
16-
private case class VarianceError(tvar: Symbol, required: Variance)
16+
case class VarianceError(tvar: Symbol, required: Variance)
1717
def check(tree: tpd.Tree)(implicit ctx: Context) =
1818
new VarianceChecker()(ctx).Traverser.traverse(tree)
1919
}

test/dotc/tests.scala

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,8 @@ class tests extends CompilerTest {
166166
@Test def neg_i803 = compileFile(negDir, "i803", xerrors = 2)
167167
@Test def neg_i866 = compileFile(negDir, "i866", xerrors = 2)
168168
@Test def neg_i974 = compileFile(negDir, "i974", xerrors = 2)
169+
@Test def neg_i997 = compileFile(negDir, "i997", xerrors = 15)
170+
@Test def neg_i997a = compileFile(negDir, "i997a", xerrors = 2)
169171
@Test def neg_i1050 = compileFile(negDir, "i1050", List("-strict"), xerrors = 11)
170172
@Test def neg_i1050a = compileFile(negDir, "i1050a", xerrors = 2)
171173
@Test def neg_i1050c = compileFile(negDir, "i1050c", xerrors = 8)

tests/neg/i997.scala

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
class C {
2+
3+
private type T = E
4+
private type Tok = D
5+
private val p: C = new C
6+
7+
def f1(x: T): Unit = () // error
8+
def f1(x: Tok): Unit = () // ok
9+
def f2(x: p.D): Unit = () // error
10+
11+
val v1: T = ??? // error
12+
val v2: p.D = ??? // error
13+
14+
type U1[X <: T] // error
15+
type U2 = T // error
16+
17+
private class E {
18+
def f1ok(x: T): Unit = () // ok
19+
def f2ok(x: p.D): Unit = () // ok
20+
21+
val v1ok: T = ??? // ok
22+
val v2ok: p.D = ??? // ok
23+
24+
type U1ok[X <: T] //ok
25+
type U2ok = T //ok
26+
}
27+
28+
class D extends E { // error
29+
def f1(x: T): Unit = () // error
30+
def f2(x: p.D): Unit = () // error
31+
32+
val v1: T = ??? // error
33+
val v2: p.D = ??? // error
34+
35+
type U1[X <: T] // error
36+
type U2 = T // error
37+
}
38+
39+
class F(x: T) // error
40+
41+
class G private (x: T) // ok
42+
43+
private trait U
44+
45+
class H extends U // error
46+
47+
}

tests/neg/i997a.scala

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
class C {
2+
3+
class Super
4+
5+
object O {
6+
7+
private case class CC(x: Int)
8+
9+
private implicit class D(x: Int) extends Super
10+
}
11+
12+
import O._
13+
14+
println(O.CC(1)) // error: CC cannot be accessed
15+
16+
val s: Super = 1 // error: type mismatch
17+
18+
19+
}

0 commit comments

Comments
 (0)