Skip to content

Commit

Permalink
Merge pull request #735 from retronym/ticket/4842-2
Browse files Browse the repository at this point in the history
SI-4842 Forbid access to in-construction this in self-constructor args
  • Loading branch information
adriaanm committed Jun 21, 2012
2 parents 6aea0ae + 6aa5762 commit 320a32b
Show file tree
Hide file tree
Showing 7 changed files with 94 additions and 16 deletions.
8 changes: 7 additions & 1 deletion src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala
Expand Up @@ -222,7 +222,13 @@ trait ContextErrors {
NormalTypeError(tree, "super constructor cannot be passed a self reference unless parameter is declared by-name")

def SuperConstrArgsThisReferenceError(tree: Tree) =
NormalTypeError(tree, "super constructor arguments cannot reference unconstructed `this`")
ConstrArgsThisReferenceError("super", tree)

def SelfConstrArgsThisReferenceError(tree: Tree) =
ConstrArgsThisReferenceError("self", tree)

private def ConstrArgsThisReferenceError(prefix: String, tree: Tree) =
NormalTypeError(tree, s"$prefix constructor arguments cannot reference unconstructed `this`")

def TooManyArgumentListsForConstructor(tree: Tree) = {
issueNormalTypeError(tree, "too many argument lists for constructor invocation")
Expand Down
62 changes: 47 additions & 15 deletions src/compiler/scala/tools/nsc/typechecker/Typers.scala
Expand Up @@ -1846,16 +1846,13 @@ trait Typers extends Modes with Adaptations with Tags {
val pending = ListBuffer[AbsTypeError]()
// an object cannot be allowed to pass a reference to itself to a superconstructor
// because of initialization issues; bug #473
for (arg <- superArgs ; tree <- arg) {
val sym = tree.symbol
if (sym != null && (sym.info.baseClasses contains clazz)) {
if (sym.isModule)
pending += SuperConstrReferenceError(tree)
tree match {
case This(qual) =>
pending += SuperConstrArgsThisReferenceError(tree)
case _ => ()
}
foreachSubTreeBoundTo(superArgs, clazz) { tree =>
if (tree.symbol.isModule)
pending += SuperConstrReferenceError(tree)
tree match {
case This(qual) =>
pending += SuperConstrArgsThisReferenceError(tree)
case _ => ()
}
}

Expand Down Expand Up @@ -1889,7 +1886,39 @@ trait Typers extends Modes with Adaptations with Tags {
pending.foreach(ErrorUtils.issueTypeError)
}

/** Check if a structurally defined method violates implementation restrictions.
// Check for SI-4842.
private def checkSelfConstructorArgs(ddef: DefDef, clazz: Symbol) {
val pending = ListBuffer[AbsTypeError]()
ddef.rhs match {
case Block(stats, expr) =>
val selfConstructorCall = stats.headOption.getOrElse(expr)
foreachSubTreeBoundTo(List(selfConstructorCall), clazz) {
case tree @ This(qual) =>
pending += SelfConstrArgsThisReferenceError(tree)
case _ => ()
}
case _ =>
}
pending.foreach(ErrorUtils.issueTypeError)
}

/**
* Run the provided function for each sub tree of `trees` that
* are bound to a symbol with `clazz` as a base class.
*
* @param f This function can assume that `tree.symbol` is non null
*/
private def foreachSubTreeBoundTo[A](trees: List[Tree], clazz: Symbol)(f: Tree => Unit): Unit =
for {
tree <- trees
subTree <- tree
} {
val sym = subTree.symbol
if (sym != null && sym.info.baseClasses.contains(clazz))
f(subTree)
}

/** Check if a structurally defined method violates implementation restrictions.
* A method cannot be called if it is a non-private member of a refinement type
* and if its parameter's types are any of:
* - the self-type of the refinement
Expand Down Expand Up @@ -2007,11 +2036,14 @@ trait Typers extends Modes with Adaptations with Tags {
transformedOrTyped(ddef.rhs, EXPRmode, tpt1.tpe)
}

if (meth.isPrimaryConstructor && meth.isClassConstructor && !isPastTyper && !reporter.hasErrors && !meth.owner.isSubClass(AnyValClass)) {
// At this point in AnyVal there is no supercall, which will blow up
// in computeParamAliases; there's nothing to be computed for Anyval anyway.
if (meth.isClassConstructor && !isPastTyper && !reporter.hasErrors && !meth.owner.isSubClass(AnyValClass)) {
// At this point in AnyVal there is no supercall, which will blow up
// in computeParamAliases; there's nothing to be computed for Anyval anyway.
if (meth.isPrimaryConstructor)
computeParamAliases(meth.owner, vparamss1, rhs1)
}
else
checkSelfConstructorArgs(ddef, meth.owner)
}

if (tpt1.tpe.typeSymbol != NothingClass && !context.returnsSeen && rhs1.tpe.typeSymbol != NothingClass)
rhs1 = checkDead(rhs1)
Expand Down
4 changes: 4 additions & 0 deletions test/files/neg/t4842a.check
@@ -0,0 +1,4 @@
t4842a.scala:2: error: self constructor arguments cannot reference unconstructed `this`
def this(x: Int) = this(new { println(Foo.this)}) // error
^
one error found
3 changes: 3 additions & 0 deletions test/files/neg/t4842a.scala
@@ -0,0 +1,3 @@
class Foo (x: AnyRef) {
def this(x: Int) = this(new { println(Foo.this)}) // error
}
4 changes: 4 additions & 0 deletions test/files/neg/t4842b.check
@@ -0,0 +1,4 @@
t4842b.scala:2: error: self constructor arguments cannot reference unconstructed `this`
def this() = { this(???)(new { println(TypeArg.this.x) } ); println("next") } // error
^
one error found
3 changes: 3 additions & 0 deletions test/files/neg/t4842b.scala
@@ -0,0 +1,3 @@
class TypeArg[X](val x: X)(a: AnyRef) {
def this() = { this(???)(new { println(TypeArg.this.x) } ); println("next") } // error
}
26 changes: 26 additions & 0 deletions test/files/pos/t4842.scala
@@ -0,0 +1,26 @@
class Foo (x: AnyRef) {
def this() = {
this(new { } ) // okay
}
}


class Blerg (x: AnyRef) {
def this() = {
this(new { class Bar { println(Bar.this); new { println(Bar.this) } }; new Bar } ) // okay
}
}


class Outer {
class Inner (x: AnyRef) {
def this() = {
this(new { class Bar { println(Bar.this); new { println(Bar.this) } }; new Bar } ) // okay
}

def this(x: Boolean) = {
this(new { println(Outer.this) } ) // okay
}
}
}

0 comments on commit 320a32b

Please sign in to comment.