This issue relates enum and final switch.
The situation is shown roughly below.
An enum `Hoge' is defined in file `/foo/bar.d'.
---
enum Hoge: ubyte{Val0, Val1, Val2, Val3, Val4}
---
Then, other file `/foo/baz.d' imports `bar.d'.
template tmp(T){
class Abcd{...}
class Efgh(uint tv0, uint tv1, Hoge typ){
@safe pure nothrow @nogc{
this(){...}
this(in T[] wxyz){
static if( tv0 == tv1 ){
final switch(typ){
case Hoge.Val2:
static assert(typ == Hoge.Val2); // <- this expression throws an assert exception!
// then, `typ' is equal to Hoge.Val1
break;
case Hoge.Val1:
...;
break;
.
.
.
}
} // End of `static if' statement
} // End of a constructor
}
} // End of class
} // End of template
Comment #1 by ag0aep6g — 2017-12-31T17:52:07Z
Please post a complete, self-contained example, without omissions. And specify where you think the compiler misbehaves.
As it is, your code doesn't do anything, because the templates are never instantiated. When I add the instantiations, the code doesn't compile because of the omissions. When I fill the omissions, it seems to me that the static assert correctly aborts compilation.
Comment #2 by kohei-coco — 2018-01-02T05:54:45Z
I updated dmd to version 2.078.0(beta), the issue is resolved.
Comment #3 by kohei-coco — 2018-05-31T14:10:55Z
Title:
A problem and an its temporary solution about case statements allows to pass through the wrong enum value.
Overview & background:
I encountered a problem of final switch statement: when an argument of enum type passed to final switch statement, rarely, wrong case statements allows to path through the processing flow. The simplified code is shown below.
--- graphic/bezier.d ---
module graphic.bezier;
import numeric.sekitk.qvm; // linear algebraic types and operations
enum BezierOrder: ubyte{Line= 1u, Quadratic, Cubic}
class Bezier(BezierOrder TYP){
enum ubyte N= cast(ubyte)TYP+1u;
this() @safe pure nothrow{. . .}
// some methods are defined here
// this method generates two parallel curves of “this”
typeof(this)[2] offsetCurve(in double width) @safe const
in{
// “in” contract is implemented here
}
do{
typeof(return) curves;
final switch(TYP){
case BezierOrder.Line:
static assert(N == 2u); // compile-time error is generated here
// implementation for straight Bezier lines
break;
case BezierOrder.Quadratic:
static assert(N == 3u);
// implementation for quadratic Bezier curves
break;
case BezierOrder.Cubic:
static assert(N == 4u);
// implementation for cubic Bezier curves
}
return curves;
}
private:
Vector2[N] node;
}
--- test.d ---
void main(){
import std.math: PI_2;
import numeric.sekitk.qvm;
import graphic.bezier;
import graphic.shapes; // unitArc(in double angle) is defined here
Bezier!(BezierType.Cubic) arc= unitArc(PI_2);
Bezier!(BezierType.Cubic) side= arc.offsetCurve(31.0); // the error can occur
}
---
This problem have ever been occurred another my project, then, I reported it as this page (Issue 18146). However, I turned the status of the report to “resolved”, because the problem had not occurred after the immediately update. Despite it, recently, I've encountered the same problem in another my project. So, I was wrong. Therefore, I tried to find the solution, and fortunately, a temporary solution has found. The solution and its evidence are shown below.
Trial-1:
In order to avoid the problem, I tried to use static if statements instead of a final switch statement.
At first, the equality expressions are used for the comparison between the template argument (lhs value) and case argument (rhs value), the same error occurred.
---
static if(TYP == BezierOrder.Line){
static assert(N == 2u); // compile-time error is generated here
// implementation for straight Bezier lines
}
else static if(TYP == BezierOrder.Quadratic){
static assert(N == 3u);
// implementation for quadratic Bezier curves
}
else static if(TYP == BezierOrder.Cubic){
static assert(N == 4u);
// implementation for cubic Bezier curves
}
else static assert(false); // unreachable
---
Trial-2:
Next, the identity expressions are used, the correct result is obtained. So, this is the temporary solution.
---
static if(TYP is BezierOrder.Line){
static assert(N == 2u); // error is NOT generated here
// implementation for Bezier line segments
}
else static if(TYP is BezierOrder.Quadratic){
static assert(N == 3u);
// implementation for quadratic Bezier curves
}
else static if(TYP is BezierOrder.Cubic){
static assert(N == 4u);
// implementation for cubic Bezier curves
}
else static assert(false); // unreachable
---
Conclusion:
According to Trial 1 and Trial 2, the following result and thinkings are obtained.
(1) In order to avoid the error, the usage of static if statements with identity expression seems to be effective.
(2) Probably, a final switch statement uses some equality expression internally for comparison; this is just my guess, I've never read the source codes of DMD.
(3) If (2) is correct, in the implementation of final switch statements with enum type argument should use identity expressions instead of equality expressions.
P.S.
When I reported [Issue 18146], I was required to submit a self-contained codes. However, I couldn't verify the request: I was busy, I don't have any GitHub account, and my English ability is so poor. If necessary, please tell me how to submit my codes. I can open my codes for implementation of dlang.
Comment #4 by nick — 2023-01-21T16:24:10Z
The problem in comment #3 seems to be these lines:
class Bezier(BezierOrder TYP){
enum ubyte N= cast(ubyte)TYP+1u;
typeof(this)[2] offsetCurve(in double width) @safe const
do{
final switch(TYP){
case BezierOrder.Line:
static assert(N == 2u); // compile-time error is generated here
break;
case BezierOrder.Quadratic:
static assert(N == 3u);
*All* the code inside `final switch` will be *compiled* regardless of the value of `TYP`. `final switch` works at runtime even when given a compile-time argument. So the compiler will try to compile *both* `static asserts` in the `final switch`.
> In order to avoid the problem, I tried to use static if statements instead of a final switch statement.
Yes, because `static if` branches are only compiled if the compile-time condition is true.