2

Here is a snippet. When pattern matching, compiler emits no warning. Do you know any workaround ?

I would like the compiler to emit warning when I forget a case when pattern matching against SimpleExpr.Expr and OtherExpr.Expr. This construct allows me to factor Nodes that are common to both Expression Trees (like If)

trait Hierarchy {
  sealed trait Expr
}
trait If {
  this: Hierarchy =>
  case class If(cond: Expr, yes: Expr, no: Expr) extends Expr
}
trait Word {
  this: Hierarchy =>
  case class Word(name: String) extends Expr
}

object SimpleExpr extends Hierarchy with If with Word
//object OtherExpr extends Hierarchy with If with Integer

object Demo extends App {
  import SimpleExpr._
  def func(expr: Expr) = expr match {
    case If(cond, yes, no) => cond
    // compiler should emit warning
  }
}
Julien__
  • 1,962
  • 1
  • 15
  • 25

2 Answers2

1

Because sealed isn't transitive, it's not clear to me whether the lack of a compile error is a bug or not.

I noticed that adding another case to the match expression causes the compiler to issue an "unreachable code" warning. Here's my modified version of your code:

#!/usr/bin/env scala
Demo.main(args)

sealed trait Hierarchy {
  sealed trait Expr
}
trait If {
  this: Hierarchy =>
  case class If(cond: Expr, yes: Expr, no: Expr) extends Expr
}
trait Word {
  this: Hierarchy =>
  case class Word(name: String) extends Expr
}

object SimpleExpr extends Hierarchy with If with Word
//object OtherExpr extends Hierarchy with If with Integer

object Demo extends App {
  import SimpleExpr._
  def func(expr: Expr) = expr match {
    case If(cond, yes, no) => cond
    // compiler should emit warning
    case Word(name) => printf("word[%s]\n",name)
  }
  func(Word("yo!"))
}

Here's what I get when I run it:

warning: unreachable code
case Word(name) => printf("word[%s]\n",name)
one warning found
word[yo!]

The warning is incorrect, the unreachable code is being executed.

When the case Word line is commented out, here's what I get:

scala.MatchError: Word(yo!) (of class Main$$anon$1$Word$Word)
    at Main$$anon$1$Demo$.func(demo.sc:21)

The following, however, does issue the desired warning:

#!/usr/bin/env scala
Demo.main(args)

sealed trait Expr
case class Word(name: String) extends Expr
case class If(cond: Expr, yes: Expr, no: Expr) extends Expr

trait Hierarchy
trait IfExpr {
  this: Hierarchy =>
}
trait WordExpr {
  this: Hierarchy =>
}

object SimpleExpr extends Hierarchy with IfExpr with WordExpr
//object OtherExpr extends Hierarchy with If with Integer

object Demo extends App {
  import SimpleExpr._
  def func(expr: Expr) = expr match {
    case If(cond, yes, no) => cond
    // compiler should emit warning
    // case Word(name) => printf("word[%s]\n",name)
  }
  // func(Word("yo!"))
}

Here's the warning I get:

demo.sc:22: warning: match may not be exhaustive.
It would fail on the following input: Word(_)
  def func(expr: Expr) = expr match {
                     ^
philwalk
  • 634
  • 1
  • 7
  • 15
  • so, (to make sure) do you agree that it's a bug ? – Julien__ Feb 08 '16 at 00:04
  • The warning would seem to be a bug. The other apparently is not. – philwalk Feb 09 '16 at 15:31
  • I tried to seal Hierarchy, but still no "sealed behaviour". How did you manage to get one ? – Julien__ Feb 09 '16 at 21:08
  • Thank you for your interest and your time. I upvoted your answer. (I'm still looking for a way to achieve the desired effect so I'm not accepting it yet... Perhaps someone will have a creative idea) – Julien__ Feb 10 '16 at 17:34
  • I discovered that commenting out the definition "case class Word" doesn't cause a compile error, so it seems that defining a trait within Hierarchy does not obligate implementing classes in any way. See my revised answer for a version that might do what you want. – philwalk Feb 10 '16 at 20:59
0

I finally found a solution that achieves the desired effect :

trait Hierarchy {
  sealed trait Expr
  case class Unit() extends Expr
}
trait AddIf extends Hierarchy {
  case class If(cond: Expr) extends Expr
}
trait AddWord extends Hierarchy {
  case class Word(name: String) extends Expr
}
trait AddSymb extends Hierarchy {
  case class Symb(name: String) extends Expr
}


object AST1 extends Hierarchy with AddIf
object AST2 extends Hierarchy with AddSymb with AddIf


object TestMatch extends App {
  def match1(e: AST1.Expr) = e match {
    case AST1.If(cond) => 1
  }
  def match2(e: AST2.Expr) = e match {
    case AST2.If(cond) => 1
    case AST2.Unit() => 1
  }
}
Julien__
  • 1,962
  • 1
  • 15
  • 25