Skip to content

Have checkNotPrivate skip over private aliases. #1171

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Mar 13, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 7 additions & 2 deletions src/dotty/tools/dotc/transform/PostTyper.scala
Original file line number Diff line number Diff line change
Expand Up @@ -131,8 +131,13 @@ class PostTyper extends MacroTransform with IdentityDenotTransformer { thisTran
annot.derivedAnnotation(transformAnnot(annot.tree))

private def transformMemberDef(tree: MemberDef)(implicit ctx: Context): Unit = {
tree.symbol.transformAnnotations(transformAnnot)
Checking.checkNoPrivateLeaks(tree)
val sym = tree.symbol
sym.transformAnnotations(transformAnnot)
if (!sym.is(SyntheticOrPrivate) && sym.owner.isClass) {
val info1 = Checking.checkNoPrivateLeaks(sym, tree.pos)
if (info1 ne sym.info)
sym.copySymDenotation(info = info1).installAfter(thisTransformer)
}
}

private def transformSelect(tree: Select, targs: List[Tree])(implicit ctx: Context): Tree = {
Expand Down
55 changes: 34 additions & 21 deletions src/dotty/tools/dotc/typer/Checking.scala
Original file line number Diff line number Diff line change
Expand Up @@ -327,40 +327,53 @@ object Checking {
* to a private type or value which is invisible at a point where `M` is still
* visible. As an exception, we allow references to type aliases if the underlying
* type of the alias is not a leak. So type aliases are transparent as far as
* leak testing is concerned. See 997.scala for tests.
* leak testing is concerned.
* @return The `info` of `sym`, with problematic aliases expanded away.
* See i997.scala for tests, i1130.scala for a case where it matters that we
* transform leaky aliases away.
*/
def checkNoPrivateLeaks(tree: MemberDef)(implicit ctx: Context): Unit = {
type Errors = List[(String, Position)]
val sym = tree.symbol
val notPrivate = new TypeAccumulator[Errors] {
def checkNoPrivateLeaks(sym: Symbol, pos: Position)(implicit ctx: Context): Type = {
class NotPrivate extends TypeMap {
type Errors = List[(String, Position)]
var errors: Errors = Nil
def accessBoundary(sym: Symbol): Symbol =
if (sym.is(Private)) sym.owner
else if (sym.privateWithin.exists) sym.privateWithin
else if (sym.is(Package)) sym
else accessBoundary(sym.owner)
def apply(errors: Errors, tp: Type): Errors = tp match {
def apply(tp: Type): Type = tp match {
case tp: NamedType =>
val errors1 =
val prevErrors = errors
var tp1 =
if (tp.symbol.is(Private) &&
!accessBoundary(sym).isContainedIn(tp.symbol.owner)) {
(d"non-private $sym refers to private ${tp.symbol}\n in its type signature ${sym.info}", tree.pos) :: errors
} else foldOver(errors, tp)
if ((errors1 ne errors) && tp.info.isAlias) {
!accessBoundary(sym).isContainedIn(tp.symbol.owner)) {
errors = (d"non-private $sym refers to private ${tp.symbol}\n in its type signature ${sym.info}",
pos) :: errors
tp
}
else mapOver(tp)
if ((errors ne prevErrors) && tp.info.isAlias) {
// try to dealias to avoid a leak error
val errors2 = apply(errors, tp.info.bounds.hi)
if (errors2 eq errors) errors2
else errors1
} else errors1
val savedErrors = errors
errors = prevErrors
val tp2 = apply(tp.info.bounds.hi)
if (errors eq prevErrors) tp1 = tp2
else errors = savedErrors
}
tp1
case tp: ClassInfo =>
(apply(errors, tp.prefix) /: tp.parentsWithArgs)(apply)
tp.derivedClassInfo(
prefix = apply(tp.prefix),
classParents = tp.parentsWithArgs.map(p =>
apply(p).underlyingClassRef(refinementOK = false).asInstanceOf[TypeRef]))
case _ =>
foldOver(errors, tp)
mapOver(tp)
}
}
if (!sym.is(SyntheticOrPrivate) && sym.owner.isClass) {
val errors = notPrivate(Nil, sym.info)
errors.foreach { case (msg, pos) => ctx.errorOrMigrationWarning(msg, pos) }
}
val notPrivate = new NotPrivate
val info = notPrivate(sym.info)
notPrivate.errors.foreach { case (msg, pos) => ctx.errorOrMigrationWarning(msg, pos) }
info
}
}

Expand Down
6 changes: 6 additions & 0 deletions tests/pos/i1130.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
trait A {
private type Foo = Int

def foo: Foo = 1
}
class B extends A