Skip to content

Commit

Permalink
Merge pull request scala#10885 from som-snytt/issue/12127-patmat-erro…
Browse files Browse the repository at this point in the history
…r-emitting

Don't narrow a constant while stabiliszing [ci: last-only]
  • Loading branch information
lrytz authored Oct 16, 2024
2 parents a10532a + c079462 commit 5f4087b
Show file tree
Hide file tree
Showing 4 changed files with 36 additions and 33 deletions.
42 changes: 16 additions & 26 deletions src/compiler/scala/tools/nsc/typechecker/Typers.scala
Original file line number Diff line number Diff line change
Expand Up @@ -550,7 +550,10 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
|| mode.inQualMode && !tree.symbol.isConstant
|| !(tree.tpe <:< pt) && (ptSym.isAbstractType && pt.lowerBound.isStable || ptSym.isRefinementClass)
)

def isNarrowable(tpe: Type): Boolean = unwrapWrapperTypes(tpe) match {
case TypeRef(_, _, _) | RefinedType(_, _) => true
case tpe => !isConstantType(tpe) && !phase.erasedTypes
}
( isNarrowable(tree.tpe)
&& mode.typingExprNotLhs
&& expectsStable
Expand Down Expand Up @@ -601,15 +604,13 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
*/
protected def stabilize(tree: Tree, pre: Type, mode: Mode, pt: Type): Tree = {

// Side effect time! Don't be an idiot like me and think you
// can move "val sym = tree.symbol" before this line, because
// inferExprAlternative side-effects the tree's symbol.
if (tree.symbol.isOverloaded && !mode.inFunMode)
inferExprAlternative(tree, pt)

val sym = tree.symbol
val sym = {
// inferExprAlternative side-effects the tree's symbol.
if (tree.symbol.isOverloaded && !mode.inFunMode)
inferExprAlternative(tree, pt)
tree.symbol
}
val isStableIdPattern = mode.typingPatternNotConstructor && tree.isTerm

def isModuleTypedExpr = (
treeInfo.admitsTypeSelection(tree)
&& (isStableContext(tree, mode, pt) || sym.isModuleNotMethod)
Expand All @@ -626,33 +627,22 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
// this so for now it requires the type symbol be public.
def isGetClassCall = isGetClass(sym) && pre.typeSymbol.isPublic

def narrowIf(tree: Tree, condition: Boolean) =
if (condition) tree setType singleType(pre, sym) else tree

def checkStable(tree: Tree): Tree =
if (treeInfo.isStableIdentifierPattern(tree)) tree
else UnstableTreeError(tree)

if (tree.isErrorTyped)
tree
else if (!sym.isValue && isStableValueRequired) // (2)
NotAValueError(tree, sym)
else if (isStableIdPattern) // (1)
// A module reference in a pattern has type Foo.type, not "object Foo"
narrowIf(checkStable(tree), sym.isModuleNotMethod)
if (!treeInfo.isStableIdentifierPattern(tree)) UnstableTreeError(tree)
else if (sym.isModuleNotMethod) tree.setType(singleType(pre, sym))
else tree
else if (isModuleTypedExpr) // (3)
narrowIf(tree, condition = true)
tree.setType(singleType(pre, sym))
else if (isGetClassCall) // (4)
tree setType MethodType(Nil, getClassReturnType(pre))
tree.setType(MethodType(Nil, getClassReturnType(pre)))
else
tree
}

private def isNarrowable(tpe: Type): Boolean = unwrapWrapperTypes(tpe) match {
case TypeRef(_, _, _) | RefinedType(_, _) => true
case _ => !phase.erasedTypes
}

def stabilizeFun(tree: Tree, mode: Mode, pt: Type): Tree = {
val sym = tree.symbol
val pre = tree match {
Expand Down Expand Up @@ -1055,7 +1045,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
if (sym != null && !context.unit.isJava && sym.isDeprecated)
context.deprecationWarning(tree.pos, sym)
tree match {
case Literal(`value`) => tree
case Literal(`value`) /*| Bind(_, _)*/ => tree
case _ =>
// If the original tree is not a literal, make it available to plugins in an attachment
treeCopy.Literal(tree, value).updateAttachment(OriginalTreeAttachment(tree))
Expand Down
2 changes: 1 addition & 1 deletion test/files/neg/t1503.scala
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ class Test {

def h(x: Any): String = x match { case s @ "hello, world" => s }
def h2(x: Any): String = x match { case s @ (_: "hello, world") => s }
//def h3(x: Any): "hello, world" = x match { case s @ "hello, world" => s } // crash
def h3(x: Any): "hello, world" = x match { case s @ "hello, world" => s }

//def j(x: Any): Array[Int] = x match { case xs @ Array(42) => xs } // found Array[T] required Array[Int]
}
10 changes: 10 additions & 0 deletions test/files/pos/t12127.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@

//> using options -Werror -Wunused

class C {
def f(x: Any): "hi" = x match { case s @ "hi" => s } // was: Error while emitting (in backend)
def g(x: Any): String = x match { case s @ "hi" => s }
def h(x: Any): String = x match { case s: String => s }

def check(x: Any): "bi" = x match { case s @ "hi" => "bi" }
}
15 changes: 9 additions & 6 deletions test/junit/scala/tools/nsc/typechecker/TypedTreeTest.scala
Original file line number Diff line number Diff line change
Expand Up @@ -60,19 +60,22 @@ class TypedTreeTest extends BytecodeTesting {
import compiler.global._
val code =
"""object O {
| final val x = 42
| final val x = 42 // accessor gets inlined constant
| def f(x: Int) = x
| def f(x: Boolean) = x
| f(O.x)
| f(O.x) // arg is inlined constant
|}
""".stripMargin
val run = compiler.newRun()
run.compileSources(List(BytecodeTesting.makeSourceFile(code, "UnitTestSource.scala")))
val tree = run.units.next().body
val attached = tree.filter(_.hasAttachment[analyzer.OriginalTreeAttachment]).toList
assertEquals(1, attached.length)
val List(t) = attached
assertEquals("42:Set(OriginalTreeAttachment(O.x))", s"$t:${t.attachments.all}")
tree.filter(_.hasAttachment[analyzer.OriginalTreeAttachment])
.sortBy(_.pos.start)
.toList
.map(t => s"$t:${t.attachments.all}") match {
case "42:Set(OriginalTreeAttachment(O.this.x))" :: "42:Set(OriginalTreeAttachment(O.x))" :: Nil =>
case wrong => throw new MatchError(wrong)
}
}


Expand Down

0 comments on commit 5f4087b

Please sign in to comment.