-
Notifications
You must be signed in to change notification settings - Fork 1.1k
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
SIP-53 - Quote pattern explicit type variable syntax #17362
Conversation
Could you add a test case related to semicolon inference? scala/improvement-proposals#59 (comment) |
14cf508
to
a726494
Compare
This comment was marked as off-topic.
This comment was marked as off-topic.
This is a binary-compatible change. Now that it is also experimental, it seems it no longer needs a minor release. |
val untpdTypeVariables = stats.takeWhile { | ||
case tdef @ untpd.TypeDef(name, _) => name.isVarPattern | ||
case _ => false | ||
}.asInstanceOf[List[untpd.TypeDef]] | ||
val otherStats = stats.dropWhile { | ||
case tdef @ untpd.TypeDef(name, _) => name.isVarPattern | ||
case _ => false | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Isn't this equivalent to:
val untpdTypeVariables = stats.takeWhile { | |
case tdef @ untpd.TypeDef(name, _) => name.isVarPattern | |
case _ => false | |
}.asInstanceOf[List[untpd.TypeDef]] | |
val otherStats = stats.dropWhile { | |
case tdef @ untpd.TypeDef(name, _) => name.isVarPattern | |
case _ => false | |
} | |
val (untpdTypeVariables, otherStats) = stats.partition: | |
case tdef @ untpd.TypeDef(name, _) => name.isVarPattern | |
case _ => false |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In the case of quoted type patterns, it might be equivalent. But this might not be the case with expression quote patterns. Consider this ill-formed pattern '{ type t; $x; type u; $y }
, in this case we only want to desugar type t
as a type variable. When checking the pattern tpye u
will chase an error. Changing the way we partition would the way we emit errors in those ill-formed patterns.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
in this case we only want to desugar type t as a type variable.
Right, this should be achievable by using .span
instead of .partition
.
if in.token == SEMI then in.nextToken() | ||
if in.isNewLine then in.nextToken() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could this be:
if in.token == SEMI then in.nextToken() | |
if in.isNewLine then in.nextToken() | |
acceptStatSep() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That does not work.
for untpdTypeVariable <- untpdTypeVariables do untpdTypeVariable.rhs match | ||
case _: TypeBoundsTree => // ok | ||
case LambdaTypeTree(_, body: TypeBoundsTree) => // ok | ||
case _ => report.error("Quote type variable definition cannot be an alias", untpdTypeVariable.srcPos) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why not?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If we declare a type variable with the same as an alias, then we know the type of that variable. It would be useless to solve the type of that type variable at runtime when pattern matching. This could also be seen as a missed optimization.
if !(typeSymInfo =:= TypeBounds.empty) && !(typeSym.info <:< typeSymInfo) then | ||
report.warning(em"Ignored bound$typeSymInfo\n\nConsider defining bounds explicitly `'{ $typeSym$typeSymInfo; ... }`", tree.srcPos) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What's an example where this warning triggers?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
import scala.quoted.*
type F[x <: Int, y <: Double]
def test(x: Type[?])(using Quotes) =
x match
case '[ F[t, t] ] => // warn // error
-- Warning: t/Test.scala:5:17 --------------------------------------------------
5 | case '[ F[t, t] ] => // warn // error
| ^
| Ignored bound <: Double
|
| Consider defining bounds explicitly `'{ type t <: Int & Double; ... }`
-- [E057] Type Mismatch Error: t/Test.scala:5:15 -------------------------------
5 | case '[ F[t, t] ] => // warn // error
| ^
| Type argument t does not conform to upper bound Double
|
| longer explanation available when compiling with `-explain`
The winning serves as extra information for the failure that will happen afterward. The warning complements the error message. We could make it an error, but I am not sure how to re-phrase the message in that case. This version seemed to be the simplest with the clearest hint.
I added a test case for this in tests/neg-macros/quote-type-variable-no-inference.scala
.
*/ | ||
def quote(inPattern: Boolean): Tree = | ||
atSpan(in.skipToken()) { | ||
withinStaged(StageKind.Quoted | (if (inPattern) StageKind.QuotedPattern else 0)) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is it possible to be StageKind.QuotedPattern but not StageKind.Quoted?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No. It would be possible to make all StageKind
disjoint if we want.
@@ -1761,6 +1758,20 @@ object Parsers { | |||
if in.isNewLine then in.nextToken() | |||
tdef | |||
|
|||
/** Quoted ::= ‘'’ ‘{’ Block ‘}’ | |||
* | ‘'’ ‘[’ Type ‘]’ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Couldn't the grammar be refactored so that we can drop this case and only have Block and TypeBlock?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should just be TypeBlock
. Type
is already one of the cases of TypeBlock
.
In syntax.md
we have the correct one
Quoted ::= ‘'’ ‘{’ Block ‘}’
| ‘'’ ‘[’ TypeBlock ‘]’
6c10549
to
f3157b9
Compare
Rebased |
With this change we can support references to quote pattern type variables without backticks. ```scala case '{ type t; ... : F[t] } ``` SIP: https://github.com/scala/improvement-proposals/blob/main/content/quote-pattern-type-variable-syntax.md?plain=1#L66-L70 ```scala case '{ ... : F[t, t] } ``` SIP: https://github.com/scala/improvement-proposals/blob/main/content/quote-pattern-type-variable-syntax.md?plain=1#L72-L78
Support explicit type variable definition in quoted patterns. This allows users to set explicit bounds or use the binding twice. Previously this was only possible on quoted expression patterns case '{ ... }. ```scala case '[type x; x] => case '[type x; Map[x, x]] => case '[type x <: List[Any]; x] => case '[type f[X]; f] => case '[type f <: AnyKind; f] => ``` Fixes scala#10864 Fixes scala#11738
Rebased |
SIP-53: https://github.com/scala/improvement-proposals/blob/main/content/quote-pattern-type-variable-syntax.md
This PR implements the feature under experimental mode.