@@ -3289,8 +3289,13 @@ final class CParser(AST) : Parser!AST
32893289 }
32903290
32913291 /* **********************************
3292- * C11 6.7.10
3292+ * C11 6.7.10 / C23 6.7.12
32933293 * _Static_assert ( constant-expression , string-literal ) ;
3294+ * _Static_assert ( constant-expression ) ;
3295+ *
3296+ * C23 makes the message operand optional. Accepting the single-argument
3297+ * form cannot change the meaning of any valid C11 program, so it is
3298+ * enabled unconditionally.
32943299 */
32953300 private AST .StaticAssert cparseStaticAssert ()
32963301 {
@@ -3300,13 +3305,22 @@ final class CParser(AST) : Parser!AST
33003305 nextToken();
33013306 check(TOK .leftParenthesis);
33023307 auto exp = cparseConstantExp();
3303- check(TOK .comma);
3304- if (token.value != TOK .string_)
3305- error(" string literal expected" );
3306- auto msg = cparsePrimaryExp();
3308+ if (token.value == TOK .comma) // C23 6.7.12
3309+ {
3310+ nextToken();
3311+ if (token.value != TOK .string_)
3312+ error(" string literal expected" );
3313+ auto msg = cparsePrimaryExp();
3314+ check(TOK .rightParenthesis);
3315+ check(TOK .semicolon);
3316+ return new AST .StaticAssert(loc, exp, msg);
3317+ }
33073318 check(TOK .rightParenthesis);
33083319 check(TOK .semicolon);
3309- return new AST .StaticAssert(loc, exp, msg);
3320+ // Use the Expressions* overload so a missing message stays null
3321+ // rather than a one-element array of null (matches D's parseStaticAssert).
3322+ AST .Expressions* msgs = null ;
3323+ return new AST .StaticAssert(loc, exp, msgs);
33103324 }
33113325
33123326 /* ************************
0 commit comments