From f952a812340db7bc11f45b45f46e4b8ce7d6fb49 Mon Sep 17 00:00:00 2001 From: Som Snytt Date: Wed, 4 May 2016 11:34:54 -0700 Subject: SI-9045 Refactor to abuse of match Collapse conditionals into match for legible. Yes, guards have scary eval order. --- .../scala/tools/nsc/typechecker/Typers.scala | 64 ++++++++++------------ 1 file changed, 28 insertions(+), 36 deletions(-) diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala index 8f5c4b9f6d..65c6e09fd3 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala @@ -2992,43 +2992,35 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper def includesTargetPos(tree: Tree) = tree.pos.isRange && context.unit.exists && (tree.pos includes context.unit.targetPos) val localTarget = stats exists includesTargetPos - def typedStat(stat: Tree): Tree = { - if (context.owner.isRefinementClass && !treeInfo.isDeclarationOrTypeDef(stat)) - OnlyDeclarationsError(stat) - else - stat match { - case imp @ Import(_, _) => - imp.symbol.initialize - if (!imp.symbol.isError) { - context = context.make(imp) - typedImport(imp) - } else EmptyTree - case _ => - if (localTarget && !includesTargetPos(stat)) { - // skip typechecking of statements in a sequence where some other statement includes - // the targetposition - stat - } else { - val localTyper = if (inBlock || (stat.isDef && !stat.isInstanceOf[LabelDef])) { - this - } else newTyper(context.make(stat, exprOwner)) - // XXX this creates a spurious dead code warning if an exception is thrown - // in a constructor, even if it is the only thing in the constructor. - val result = checkDead(localTyper.typedByValueExpr(stat)) - - if (treeInfo.isSelfOrSuperConstrCall(result)) { - context.inConstructorSuffix = true - if (treeInfo.isSelfConstrCall(result) && result.symbol.pos.pointOrElse(0) >= exprOwner.enclMethod.pos.pointOrElse(0)) - ConstructorsOrderError(stat) - } - - if (!isPastTyper && treeInfo.isPureExprForWarningPurposes(result)) context.warning(stat.pos, - "a pure expression does nothing in statement position; " + - "you may be omitting necessary parentheses" - ) - result - } + def typedStat(stat: Tree): Tree = stat match { + case s if context.owner.isRefinementClass && !treeInfo.isDeclarationOrTypeDef(s) => OnlyDeclarationsError(s) + case imp @ Import(_, _) => + imp.symbol.initialize + if (!imp.symbol.isError) { + context = context.make(imp) + typedImport(imp) + } else EmptyTree + // skip typechecking of statements in a sequence where some other statement includes the targetposition + case s if localTarget && !includesTargetPos(s) => s + case _ => + val localTyper = if (inBlock || (stat.isDef && !stat.isInstanceOf[LabelDef])) { + this + } else newTyper(context.make(stat, exprOwner)) + // XXX this creates a spurious dead code warning if an exception is thrown + // in a constructor, even if it is the only thing in the constructor. + val result = checkDead(localTyper.typedByValueExpr(stat)) + + if (treeInfo.isSelfOrSuperConstrCall(result)) { + context.inConstructorSuffix = true + if (treeInfo.isSelfConstrCall(result) && result.symbol.pos.pointOrElse(0) >= exprOwner.enclMethod.pos.pointOrElse(0)) + ConstructorsOrderError(stat) } + + if (!isPastTyper && treeInfo.isPureExprForWarningPurposes(result)) context.warning(stat.pos, + "a pure expression does nothing in statement position; " + + "you may be omitting necessary parentheses" + ) + result } /* 'accessor' and 'accessed' are so similar it becomes very difficult to -- cgit v1.2.3 From 6379b70d952cf0eea96d205e14a291b441f9cd45 Mon Sep 17 00:00:00 2001 From: Som Snytt Date: Wed, 4 May 2016 12:44:40 -0700 Subject: SI-9045 Error on recursive ctor If the constructor invokes itself, say so. --- .../scala/tools/nsc/typechecker/ContextErrors.scala | 5 +++++ src/compiler/scala/tools/nsc/typechecker/Typers.scala | 17 +++++++++-------- test/files/neg/constrs.check | 2 +- test/files/neg/t4460a.check | 2 +- test/files/neg/t4460b.check | 2 +- test/files/neg/t9045.check | 7 +++++++ test/files/neg/t9045.scala | 8 ++++++++ 7 files changed, 32 insertions(+), 11 deletions(-) create mode 100644 test/files/neg/t9045.check create mode 100644 test/files/neg/t9045.scala diff --git a/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala b/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala index ccdff5c9a1..e190b57017 100644 --- a/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala +++ b/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala @@ -469,6 +469,11 @@ trait ContextErrors { setError(tree) } + def ConstructorRecursesError(tree: Tree) = { + issueNormalTypeError(tree, "constructor invokes itself") + setError(tree) + } + def OnlyDeclarationsError(tree: Tree) = { issueNormalTypeError(tree, "only declarations allowed here") setError(tree) diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala index 65c6e09fd3..329ce8c23b 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala @@ -3003,22 +3003,23 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper // skip typechecking of statements in a sequence where some other statement includes the targetposition case s if localTarget && !includesTargetPos(s) => s case _ => - val localTyper = if (inBlock || (stat.isDef && !stat.isInstanceOf[LabelDef])) { - this - } else newTyper(context.make(stat, exprOwner)) + val localTyper = if (inBlock || (stat.isDef && !stat.isInstanceOf[LabelDef])) this + else newTyper(context.make(stat, exprOwner)) // XXX this creates a spurious dead code warning if an exception is thrown // in a constructor, even if it is the only thing in the constructor. val result = checkDead(localTyper.typedByValueExpr(stat)) if (treeInfo.isSelfOrSuperConstrCall(result)) { context.inConstructorSuffix = true - if (treeInfo.isSelfConstrCall(result) && result.symbol.pos.pointOrElse(0) >= exprOwner.enclMethod.pos.pointOrElse(0)) - ConstructorsOrderError(stat) + if (treeInfo.isSelfConstrCall(result)) { + if (result.symbol == exprOwner.enclMethod) + ConstructorRecursesError(stat) + else if (result.symbol.pos.pointOrElse(0) >= exprOwner.enclMethod.pos.pointOrElse(0)) + ConstructorsOrderError(stat) + } } - if (!isPastTyper && treeInfo.isPureExprForWarningPurposes(result)) context.warning(stat.pos, - "a pure expression does nothing in statement position; " + - "you may be omitting necessary parentheses" + "a pure expression does nothing in statement position; you may be omitting necessary parentheses" ) result } diff --git a/test/files/neg/constrs.check b/test/files/neg/constrs.check index 4f4a12bc13..8a5bd97ae3 100644 --- a/test/files/neg/constrs.check +++ b/test/files/neg/constrs.check @@ -7,7 +7,7 @@ constrs.scala:6: error: value u is not a member of object test constrs.scala:10: error: called constructor's definition must precede calling constructor's definition def this() = this("abc") ^ -constrs.scala:12: error: called constructor's definition must precede calling constructor's definition +constrs.scala:12: error: constructor invokes itself def this(x: Boolean) = this(x) ^ constrs.scala:16: error: type mismatch; diff --git a/test/files/neg/t4460a.check b/test/files/neg/t4460a.check index b711e7acb1..7a7618a114 100644 --- a/test/files/neg/t4460a.check +++ b/test/files/neg/t4460a.check @@ -1,4 +1,4 @@ -t4460a.scala:6: error: called constructor's definition must precede calling constructor's definition +t4460a.scala:6: error: constructor invokes itself def this() = this() // was binding to Predef. !! ^ one error found diff --git a/test/files/neg/t4460b.check b/test/files/neg/t4460b.check index f0e703fd10..9a621dbd5c 100644 --- a/test/files/neg/t4460b.check +++ b/test/files/neg/t4460b.check @@ -1,4 +1,4 @@ -t4460b.scala:7: error: called constructor's definition must precede calling constructor's definition +t4460b.scala:7: error: constructor invokes itself def this() = this() // was binding to Predef. !! ^ one error found diff --git a/test/files/neg/t9045.check b/test/files/neg/t9045.check new file mode 100644 index 0000000000..07d0e2dd74 --- /dev/null +++ b/test/files/neg/t9045.check @@ -0,0 +1,7 @@ +t9045.scala:3: error: constructor invokes itself + def this(axes: Array[Int]) = this(axes) + ^ +t9045.scala:6: error: called constructor's definition must precede calling constructor's definition + def this(d: Double) = this(d.toLong) + ^ +two errors found diff --git a/test/files/neg/t9045.scala b/test/files/neg/t9045.scala new file mode 100644 index 0000000000..e6710ab324 --- /dev/null +++ b/test/files/neg/t9045.scala @@ -0,0 +1,8 @@ + +case class AffineImageShape(axes: Seq[Int]) { + def this(axes: Array[Int]) = this(axes) +} +class X(i: Int) { + def this(d: Double) = this(d.toLong) + def this(n: Long) = this(n.toInt) +} -- cgit v1.2.3