You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
While exploring macro quotes, an incorrect code snippet seems to exhibit inconsistent compilation and may provide a possible opportunity to detect and flag an error. The code is below. Also available in scastie.
importscala.quoted.*transparentinlinedefpower5(a: Int, b:Int):Any=${ power5Impl('a,'b) }
defpower5Impl(a: Expr[Int], b: Expr[Int])(usingq1: Quotes):Expr[Int] =importquotes.reflect.*
b matchcase'{0} =>
println(s"b(0) : ${b.show} = ${b.value}")
'{1}
case'{1} =>
println(s"b(1) : ${b.show} = ${b.value}")
a
case'{ $expr: tpe } =>
println(s"b(a) : ${b.show} = ${b.value}")
println(s"b(b) : ${Type.show[tpe]} = ${expr.value}")
valtest:Expr[Int] ='{$b -1}
println(s"test : ${test.show} = ${test.value}")
// No terminal match on 0 or 1. Stack overflow// val testOut: Expr[Int] = power5Impl(a, test)// println(s"testOut : ${testOut.show} = ${testOut.value}")'{
valnb= $b -1// Incorrect exponent value. Always None. No terminal match on 0 or 1. Stack overflow// This is correct. nb has not been calculatedvalnext=${ power5Impl(a, 'nb)}
// No terminal match on 0 or 1// Matches and stops ok// val next = ${ power5Impl(a, Expr(1))}// Matches and stops ok// val next = ${ power5Impl(a, '{1})}// Correct exponent value. No terminal match on 0 or 1. Stack overflow// val next = ${ power5Impl(a, test)}
$a * next
}
Output
As is, the code will, as it should, cause stack overflow. The output is:
b(a) :2=Some(2)
b(b) :2=Some(2)
test :2.-(1) =Some(1)
b(a) : nb =None
b(b) : nb.type=None
test : nb.-(1) =None
b(a) : nb =None
b(b) : nb.type=None
test : nb.-(1) =None
b(a) : nb =None
b(b) : nb.type=None
test : nb.-(1) =None
The issue is that the value for nb is not available when we splice the call to power5Impl(a, 'nb). Hence the value None. If we comment this line and remove the comment from the line:
// val next = ${ power5Impl(a, test)}
then we also get a stack overflow. This time however, the value is updated correctly. The overflow occurs because matching fails and may be due to another issue (see #16521).
Expectation
In the case of the nb variable being used when unavailable, maybe this can be detected and flagged by compiler? If so a compilation may issue an error or at least a warning.
On this next issue, I am unsure.
In regards to the next that uses the test value. Intuitively it seems correct to expect this to be available when splicing. However, after seeing what the issue is with nb, I would expect it to behave just like the nb. After all both nb and test are the same quote. It seems like the compiler calculates test and makes it available, unlike the nb. Maybe the nb should also be made available?
The text was updated successfully, but these errors were encountered:
By generating val nb: Int = 4 - 1; power5Impl(a, nb) we ger into a situation where b will never be reduced and we always. Furthermore, having this b will force the match into the case '{ $expr: tpe } => as '{ nb: Int } and into an infinite loop. The other cases work because the value is directly placed as the b argument. This kind of bug in the user code is too hard (maybe impossible) to detect in the compiler.
Also, try to avoid partially evaluating values waiting generated code as this is slower and does not guarantee that the value will be reduced. The best practice is to use .value and then you will have all the control over how the code is unrolled. This also reduces the number of ways you could accidentally make a bug in the macro implementation. The ideal code would have been:
importscala.quoted.*transparentinlinedefpower5(a: Int, b:Int):Any=${ power5Impl('a,'b) }
defpower5Impl(a: Expr[Int], b: Expr[Int])(usingQuotes):Expr[Int] =
b.value matchcaseSome(n) => power5Impl(a, n)
caseNone=>// Not handled in the original example and source of infinite recursion???defpower5Impl(a: Expr[Int], n: Int)(usingQuotes):Expr[Int] =
n matchcase0=>'{1}
case1=> a
case _ =>valnb= n -1'{
valnext=${ power5Impl(a, nb) }
$a * next
}
// or just '{ $a * ${ power5Impl(a, n-1) } }
Compiler version
Scala version 3.2.1
Minimized code
While exploring macro quotes, an incorrect code snippet seems to exhibit inconsistent compilation and may provide a possible opportunity to detect and flag an error. The code is below. Also available in scastie.
Output
As is, the code will, as it should, cause stack overflow. The output is:
The issue is that the value for
nb
is not available when we splice the call topower5Impl(a, 'nb)
. Hence the valueNone
. If we comment this line and remove the comment from the line:// val next = ${ power5Impl(a, test)}
then we also get a stack overflow. This time however, the value is updated correctly. The overflow occurs because matching fails and may be due to another issue (see #16521).
Expectation
In the case of the
nb
variable being used when unavailable, maybe this can be detected and flagged by compiler? If so a compilation may issue an error or at least a warning.On this next issue, I am unsure.
In regards to the
next
that uses thetest
value. Intuitively it seems correct to expect this to be available when splicing. However, after seeing what the issue is withnb
, I would expect it to behave just like thenb
. After all bothnb
andtest
are the same quote. It seems like the compiler calculatestest
and makes it available, unlike thenb
. Maybe thenb
should also be made available?The text was updated successfully, but these errors were encountered: