Skip to content
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

Multiple assignment does not do correct typing #140

Closed
scabug opened this issue Sep 27, 2007 · 16 comments
Closed

Multiple assignment does not do correct typing #140

scabug opened this issue Sep 27, 2007 · 16 comments
Assignees

Comments

@scabug
Copy link

scabug commented Sep 27, 2007

The following code should not compile:

val (is: Option[String], not: Option[String]) = ("a", "b") match {
  case ("b", _) => (Some("Dog"), None)
  case _ => (Some((1,2)), Some("Dog"))
}

is.foreach(v => println(v.length))

Note that 'is' is typed as an Option[String], but it is assigned either Option[String] or Option[(Int, Int)]

This leads to a runtime error.

@scabug
Copy link
Author

scabug commented Sep 27, 2007

Imported From: https://issues.scala-lang.org/browse/SI-140?orig=1
Reporter: David Pollak (dpp)

@scabug
Copy link
Author

scabug commented Sep 28, 2007

@odersky said:
This is as speced. The `val' is a pattern matching definition and is: Option[String]
is a typed pattern, which can fail. I agree it's surprising, though. Let's think about it, whether we can change the spec.

@scabug
Copy link
Author

scabug commented Sep 28, 2007

David Pollak (dpp) said:
Sorry to be a pest about this... but we've been bitten a number of times by this issue.

I scanned through the spec for mention of this form of assignment, but could not find anything. A pointer would be appreciated.

If I rewrite the code to:

val (tis, not: Option[String]) = ("a", "b") match {
  case ("b", _) => (Some("Dog"), None)
  case _ => (Some((1,2)), Some("Dog"))
}

val is: Option[String] = tis

The compiler picks up the error.

Thanks!

@scabug
Copy link
Author

scabug commented Sep 28, 2007

@odersky said:
It's in the section on value definitions. The paragraphs following:

Value definitions can alternatively have a pattern
(\sref{sec:patterns}) as left-hand side. If $$p$$ is some pattern other
than a simple name or a name followed by a colon and a type, then the
value definition \lstinline@val $$p$$ = $$e$$@ is expanded as follows:

...

-- Martin

@scabug
Copy link
Author

scabug commented Nov 9, 2009

@odersky said:
My previous comments still apply. This is as speced. It's not at all clear what a changed rule would be, and how much coded such a change would break. Note that for for-expressions a lot of people a clamoring for the reverse: Even simple definitions like x: T <- foo should be treated as a pattern match which can fail (and which will be translated to a filter). So without a SID I believe the best course of action is do nothing.

@scabug
Copy link
Author

scabug commented Jan 13, 2010

@dcsobral said:
While this has been compared to #900, I don't think they are really about the same issue. I see this much in the same way as match statement error to the effect the match is impossible.

The semantics I'd personally like to see is: if the inferred/ascribed type cannot satisfy the pre-erasure type of pattern match statements (valid for val, for and match), return an error.

Just to compare and combine with #900, these are things I'd expect:

for (i: Int <- List('symbol, "string", true)) yield i // compiles
for (i: Int <- List(false, true, false)) yield i // error

@scabug
Copy link
Author

scabug commented Jan 13, 2010

@dcsobral said:
Reformatted the code:

for (i: Int <- List('symbol, "string", true)) yield i // compiles 
for (i: Int <- List(false, true, false)) yield i // error

@scabug
Copy link
Author

scabug commented May 10, 2011

@soc said:
The first two examples crash the compiler:

scala> val (is: Option[String], not: Option[String]) = ("a", "b") match {
     |   case ("b", _) => (Some("Dog"), None)
     |   case _ => (Some((1,2)), Some("Dog"))
     | }
java.lang.Error: symbol value x$$1 does not exist in <init>
	at scala.tools.nsc.symtab.SymbolTable.abort(SymbolTable.scala:35)
	at scala.tools.nsc.backend.icode.GenICode$$ICodePhase.scala$$tools$$nsc$$backend$$icode$$GenICode$$ICodePhase$$$$genLoad(GenICode.scala:1027)
	at scala.tools.nsc.backend.icode.GenICode$$ICodePhase.genLoadQualifier(GenICode.scala:1175)
	at scala.tools.nsc.backend.icode.GenICode$$ICodePhase.scala$$tools$$nsc$$backend$$icode$$GenICode$$ICodePhase$$$$genLoad(GenICode.scala:921)
	at scala.tools.nsc.backend.icode.GenICode$$ICodePhase.genLoadQualifier(GenICode.scala:1175)
	at scala.tools.nsc.backend.icode.GenICode$$ICodePhase.scala$$tools$$nsc$$backend$$icode$$GenICode$$ICodePhase$$$$genLoad(GenICode.scala:738)
	at scala.tools.nsc.backend.icode.GenICode$$ICodePhase.scala$$tools$$nsc$$backend$$icode$$GenICode$$ICodePhase$$$$genStat(GenICode.scala:163)
	at scala.tools.nsc.backend.icode.GenICode$$ICodePhase$$$$anonfun$$genStat$$1.apply(GenICode.scala:143)
	at scala.tools.nsc.backend.icode.GenICode$$ICodePhase$$$$anonfun$$genStat$$1.apply(GenICode.scala:143)
	at scala.collection.LinearSeqOptimized$$class.foldLeft(LinearSeqOptimized.scala:111)
	at scala.collection.immutable.List.foldLeft(List.scala:45)
	at scala.tools.nsc.backend.icode.GenICode$$ICodePhase.genStat(GenICode.scala:143)
	at scala.tools.nsc.backend.icode.GenICode$$ICodePhase.scala$$tools$$nsc$$backend$$icode$$GenICode$$ICodePhase$$$$genLoad(GenICode.scala:1052)
	at scala.tools.nsc.backend.icode.GenICode$$ICodePhase.gen(GenICode.scala:114)
	at scala.tools.nsc.backend.icode.GenICode$$ICodePhase$$$$anonfun$$gen$$1.apply(GenICode.scala:69)
	at scala.tools.nsc.backend.icode.GenICode$$ICodePhase$$$$anonfun$$gen$$1.apply(GenICode.scala:69)
	at scala.collection.LinearSeqOptimized$$class.foreach(LinearSeqOptimized.scala:59)
	at scala.collection.immutable.List.foreach(List.scala:45)
	at scala.tools.nsc.backend.icode.GenICode$$ICodePhase.gen(GenICode.scala:69)
	at scala.tools.nsc.backend.icode.GenICode$$ICodePhase.gen(GenICode.scala:136)
	at scala.tools.nsc.backend.icode.GenICode$$ICodePhase.gen(GenICode.scala:88)
	at scala.tools.nsc.backend.icode.GenICode$$ICodePhase$$$$anonfun$$gen$$1.apply(GenICode.scala:69)
	at scala.tools.nsc.backend.icode.GenICode$$ICodePhase$$$$anonfun$$gen$$1.apply(GenICode.scala:69)
	at scala.collection.LinearSeqOptimized$$class.foreach(LinearSeqOptimized.scala:59)
	at scala.collection.immutable.List.foreach(List.scala:45)
scala> val (tis, not: Option[String]) = ("a", "b") match {
     |   case ("b", _) => (Some("Dog"), None)
     |   case _ => (Some((1,2)), Some("Dog"))
     | }
java.lang.Error: symbol value x$$1 does not exist in <init>
	at scala.tools.nsc.symtab.SymbolTable.abort(SymbolTable.scala:35)
	at scala.tools.nsc.backend.icode.GenICode$$ICodePhase.scala$$tools$$nsc$$backend$$icode$$GenICode$$ICodePhase$$$$genLoad(GenICode.scala:1027)
	at scala.tools.nsc.backend.icode.GenICode$$ICodePhase.genLoadQualifier(GenICode.scala:1175)
	at scala.tools.nsc.backend.icode.GenICode$$ICodePhase.scala$$tools$$nsc$$backend$$icode$$GenICode$$ICodePhase$$$$genLoad(GenICode.scala:921)
	at scala.tools.nsc.backend.icode.GenICode$$ICodePhase.genLoadQualifier(GenICode.scala:1175)
	at scala.tools.nsc.backend.icode.GenICode$$ICodePhase.scala$$tools$$nsc$$backend$$icode$$GenICode$$ICodePhase$$$$genLoad(GenICode.scala:738)
	at scala.tools.nsc.backend.icode.GenICode$$ICodePhase.scala$$tools$$nsc$$backend$$icode$$GenICode$$ICodePhase$$$$genStat(GenICode.scala:163)
	at scala.tools.nsc.backend.icode.GenICode$$ICodePhase$$$$anonfun$$genStat$$1.apply(GenICode.scala:143)
	at scala.tools.nsc.backend.icode.GenICode$$ICodePhase$$$$anonfun$$genStat$$1.apply(GenICode.scala:143)
	at scala.collection.LinearSeqOptimized$$class.foldLeft(LinearSeqOptimized.scala:111)
	at scala.collection.immutable.List.foldLeft(List.scala:45)
	at scala.tools.nsc.backend.icode.GenICode$$ICodePhase.genStat(GenICode.scala:143)
	at scala.tools.nsc.backend.icode.GenICode$$ICodePhase.scala$$tools$$nsc$$backend$$icode$$GenICode$$ICodePhase$$$$genLoad(GenICode.scala:1052)
	at scala.tools.nsc.backend.icode.GenICode$$ICodePhase.gen(GenICode.scala:114)
	at scala.tools.nsc.backend.icode.GenICode$$ICodePhase$$$$anonfun$$gen$$1.apply(GenICode.scala:69)
	at scala.tools.nsc.backend.icode.GenICode$$ICodePhase$$$$anonfun$$gen$$1.apply(GenICode.scala:69)
	at scala.collection.LinearSeqOptimized$$class.foreach(LinearSeqOptimized.scala:59)
	at scala.collection.immutable.List.foreach(List.scala:45)
	at scala.tools.nsc.backend.icode.GenICode$$ICodePhase.gen(GenICode.scala:69)
	at scala.tools.nsc.backend.icode.GenICode$$ICodePhase.gen(GenICode.scala:136)
	at scala.tools.nsc.backend.icode.GenICode$$ICodePhase.gen(GenICode.scala:88)
	at scala.tools.nsc.backend.icode.GenICode$$ICodePhase$$$$anonfun$$gen$$1.apply(GenICode.scala:69)
	at scala.tools.nsc.backend.icode.GenICode$$ICodePhase$$$$anonfun$$gen$$1.apply(GenICode.scala:69)
	at scala.collection.LinearSeqOptimized$$class.foreach(LinearSeqOptimized.scala:59)
	at scala.collection.immutable.List.foreach(List.scala:45)

and the last two don't compile:

scala> for (i: Int <- List('symbol, "string", true)) yield i // compiles
<console>:8: error: type mismatch;
 found   : (Int) => Int
 required: (Any) => ?
       for (i: Int <- List('symbol, "string", true)) yield i // compiles
                   ^
scala> for (i: Int <- List(false, true, false)) yield i // error
<console>:8: error: type mismatch;
 found   : (Int) => Int
 required: (Boolean) => ?
       for (i: Int <- List(false, true, false)) yield i // error
                   ^

@scabug
Copy link
Author

scabug commented Jun 2, 2013

@som-snytt said:
For those cats not killed by curiosity,

scala> val (is: Option[String], not: Option[String]) = ("a", "b") match {
  case ("b", _) => (Some("Dog"), None)
  case _ => (Some((1,2)), Some("Dog"))
}
<console>:7: warning: non-variable type argument String in type pattern Option[String] is unchecked since it is eliminated by erasure
       val (is: Option[String], not: Option[String]) = ("a", "b") match {
                ^
<console>:7: error: pattern type is incompatible with expected type;
 found   : Option[String]
 required: Some[java.io.Serializable]
       val (is: Option[String], not: Option[String]) = ("a", "b") match {
                ^

@dwijnand
Copy link
Member

dwijnand commented May 3, 2017

Given @som-snytt's has shown the code block no longer compiles (still the behaviour today in 2.12.2), can't this closed now?

@SethTisue
Copy link
Member

to be sure, we have to alter Som's code a bit:

scala> val (is: Option[String], not) = 0 match { case 1 => (Some("foo"), 0); case _ => (Option(1), 0) }
<console>:13: warning: non-variable type argument String in type pattern Option[String] is unchecked since it is eliminated by erasure
       val (is: Option[String], not) = 0 match { case 1 => (Some("foo"), 0); case _ => (Option(1), 0) }
                ^
is: Option[String] = Some(1)

but this is covered by the erasure warning; the overall pattern match is correctly typed as (Option[Any], Int)

@SethTisue SethTisue removed the patmat label May 3, 2017
@dwijnand
Copy link
Member

dwijnand commented May 3, 2017

is: Option[String] = Some(1)

😨 here be dragons

@SethTisue
Copy link
Member

SethTisue commented May 3, 2017

yeah that type erasure warning should probably have skulls and knives in it... pretty common to see beginners just ignoring it

@som-snytt
Copy link

Consider adding a compiler option scalac -XYZZY to unlock special features.

@SethTisue
Copy link
Member

should probably have skulls and knives in it

more seriously: perhaps we ought to break with tradition here and make the absence of @unchecked an error, not just a warning

@dwijnand
Copy link
Member

dwijnand commented Apr 2, 2020

perhaps we ought to break with tradition here and make the absence of @unchecked an error, not just a warning

https://dotty.epfl.ch/docs/reference/changed-features/pattern-bindings.html#bindings-in-pattern-definitions 🎉 :shipit:

@scala scala deleted a comment from scabug Apr 2, 2020
@scala scala deleted a comment from scabug Apr 2, 2020
@SethTisue SethTisue removed this from the Backlog milestone Jul 17, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants