Thread overview
[Issue 18146] A case expression of final switch allows to pass wrong enum value
Dec 31, 2017
ag0aep6g@gmail.com
Jan 02, 2018
ARAI Kohei
May 31, 2018
ARAI Kohei
Jan 21, 2023
Nick Treleaven
Feb 11, 2023
Basile-z
December 31, 2017
https://issues.dlang.org/show_bug.cgi?id=18146

ag0aep6g@gmail.com changed:

           What    |Removed                     |Added
----------------------------------------------------------------------------
                 CC|                            |ag0aep6g@gmail.com

--- Comment #1 from ag0aep6g@gmail.com ---
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.

--
January 02, 2018
https://issues.dlang.org/show_bug.cgi?id=18146

ARAI Kohei <kohei-coco@jcom.home.ne.jp> changed:

           What    |Removed                     |Added
----------------------------------------------------------------------------
             Status|NEW                         |RESOLVED
         Resolution|---                         |FIXED

--- Comment #2 from ARAI Kohei <kohei-coco@jcom.home.ne.jp> ---
I updated dmd to version 2.078.0(beta), the issue is resolved.

--
May 31, 2018
https://issues.dlang.org/show_bug.cgi?id=18146

ARAI Kohei <kohei-coco@jcom.home.ne.jp> changed:

           What    |Removed                     |Added
----------------------------------------------------------------------------
             Status|RESOLVED                    |REOPENED
         Resolution|FIXED                       |---

--- Comment #3 from ARAI Kohei <kohei-coco@jcom.home.ne.jp> ---
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.

--
January 21, 2023
https://issues.dlang.org/show_bug.cgi?id=18146

Nick Treleaven <nick@geany.org> changed:

           What    |Removed                     |Added
----------------------------------------------------------------------------
                 CC|                            |nick@geany.org

--- Comment #4 from Nick Treleaven <nick@geany.org> ---
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.

--
February 11, 2023
https://issues.dlang.org/show_bug.cgi?id=18146

Basile-z <b2.temp@gmx.com> changed:

           What    |Removed                     |Added
----------------------------------------------------------------------------
                 CC|                            |b2.temp@gmx.com
           See Also|                            |https://issues.dlang.org/sh
                   |                            |ow_bug.cgi?id=21460

--- Comment #5 from Basile-z <b2.temp@gmx.com> ---
isnt that the same problem as https://issues.dlang.org/show_bug.cgi?id=21460#c1 ?

--