-
Notifications
You must be signed in to change notification settings - Fork 13.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
[C2y] Implement WG14 N3369 and N3469 (_Countof) #133125
Conversation
C2y adds the _Countof operator which returns the number of elements in an array. As with sizeof, _Countof either accepts a parenthesized type name or an expression. Its operand must be (of) an array type. When passed a constant-size array operand, the operator is a constant expression which is valid for use as an integer constant expression. Fixes llvm#102836
@llvm/pr-subscribers-clang-codegen @llvm/pr-subscribers-clang Author: Aaron Ballman (AaronBallman) ChangesC2y adds the _Countof operator which returns the number of elements in an array. As with sizeof, _Countof either accepts a parenthesized type name or an expression. Its operand must be (of) an array type. When passed a constant-size array operand, the operator is a constant expression which is valid for use as an integer constant expression. This is being exposed as an extension in earlier C language modes, but not in C++. C++ already has Fixes #102836 Patch is 25.31 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/133125.diff 15 Files Affected:
diff --git a/clang/docs/LanguageExtensions.rst b/clang/docs/LanguageExtensions.rst
index d4771775c9739..8b5707ce2acac 100644
--- a/clang/docs/LanguageExtensions.rst
+++ b/clang/docs/LanguageExtensions.rst
@@ -1653,6 +1653,7 @@ Array & element qualification (N2607) C
Attributes (N2335) C23 C89
``#embed`` (N3017) C23 C89, C++
Octal literals prefixed with ``0o`` or ``0O`` C2y C89, C++
+``_Countof`` (N3369, N3469) C2y C89
============================================= ================================ ============= =============
Builtin type aliases
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 04ec2cfef679c..b82e79c092c4e 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -141,6 +141,12 @@ C2y Feature Support
paper also introduced octal and hexadecimal delimited escape sequences (e.g.,
``"\x{12}\o{12}"``) which are also supported as an extension in older C
language modes.
+- Implemented `WG14 N3369 <https://www.open-std.org/jtc1/sc22/wg14/www/docs/n3369.pdf>`_
+ which introduces the ``_Lengthof`` operator, and `WG14 N3469 <https://www.open-std.org/jtc1/sc22/wg14/www/docs/n3469.htm>`_
+ which renamed ``_Lengthof`` to ``_Countof``. This feature is implemented as
+ a conforming extension in earlier C language modes, but not in C++ language
+ modes (``std::extent`` and ``std::rank`` already provide the same
+ functionality but with more granularity).
C23 Feature Support
^^^^^^^^^^^^^^^^^^^
diff --git a/clang/include/clang/Basic/DiagnosticParseKinds.td b/clang/include/clang/Basic/DiagnosticParseKinds.td
index 4dc956f7ae6f7..86c361b4dbcf7 100644
--- a/clang/include/clang/Basic/DiagnosticParseKinds.td
+++ b/clang/include/clang/Basic/DiagnosticParseKinds.td
@@ -171,12 +171,17 @@ def ext_c99_feature : Extension<
"'%0' is a C99 extension">, InGroup<C99>;
def ext_c11_feature : Extension<
"'%0' is a C11 extension">, InGroup<C11>;
+def ext_c2y_feature : Extension<
+ "'%0' is a C2y extension">, InGroup<C2y>;
def warn_c11_compat_keyword : Warning<
"'%0' is incompatible with C standards before C11">,
InGroup<CPre11Compat>, DefaultIgnore;
def warn_c23_compat_keyword : Warning<
"'%0' is incompatible with C standards before C23">,
InGroup<CPre23Compat>, DefaultIgnore;
+def warn_c2y_compat_keyword : Warning<
+ "'%0' is incompatible with C standards before C2y">,
+ InGroup<CPre2yCompat>, DefaultIgnore;
def err_c11_noreturn_misplaced : Error<
"'_Noreturn' keyword must precede function declarator">;
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index c77cde297dc32..1e900437d41ce 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -7022,6 +7022,8 @@ def err_sizeof_alignof_typeof_bitfield : Error<
"bit-field">;
def err_alignof_member_of_incomplete_type : Error<
"invalid application of 'alignof' to a field of a class still being defined">;
+def err_countof_arg_not_array_type : Error<
+ "'_Countof' requires an argument of array type; %0 invalid">;
def err_vecstep_non_scalar_vector_type : Error<
"'vec_step' requires built-in scalar or vector type, %0 invalid">;
def err_offsetof_incomplete_type : Error<
diff --git a/clang/include/clang/Basic/TokenKinds.def b/clang/include/clang/Basic/TokenKinds.def
index 1bf9f43f80986..880928ae0447d 100644
--- a/clang/include/clang/Basic/TokenKinds.def
+++ b/clang/include/clang/Basic/TokenKinds.def
@@ -349,6 +349,8 @@ KEYWORD(__func__ , KEYALL)
KEYWORD(__objc_yes , KEYALL)
KEYWORD(__objc_no , KEYALL)
+// C2y
+UNARY_EXPR_OR_TYPE_TRAIT(_Countof, CountOf, KEYNOCXX)
// C++ 2.11p1: Keywords.
KEYWORD(asm , KEYCXX|KEYGNU)
diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp
index 95da7b067b459..92b1f41bf2fab 100644
--- a/clang/lib/AST/ExprConstant.cpp
+++ b/clang/lib/AST/ExprConstant.cpp
@@ -14926,6 +14926,23 @@ bool IntExprEvaluator::VisitUnaryExprOrTypeTraitExpr(
return false;
}
+ case UETT_CountOf: {
+ QualType Ty = E->getTypeOfArgument();
+ assert(Ty->isArrayType());
+
+ // We don't need to worry about array element qualifiers, so getting the
+ // unsafe array type is fine.
+ if (const auto *CAT =
+ dyn_cast<ConstantArrayType>(Ty->getAsArrayTypeUnsafe())) {
+ return Success(CAT->getSize(), E);
+ }
+
+ // If it wasn't a constant array, it's not a valid constant expression.
+ assert(!Ty->isConstantSizeType());
+ // FIXME: Better diagnostic.
+ Info.FFDiag(E->getBeginLoc());
+ return false;
+ }
}
llvm_unreachable("unknown expr/type trait");
@@ -17425,7 +17442,7 @@ static ICEDiag CheckICE(const Expr* E, const ASTContext &Ctx) {
}
case Expr::UnaryExprOrTypeTraitExprClass: {
const UnaryExprOrTypeTraitExpr *Exp = cast<UnaryExprOrTypeTraitExpr>(E);
- if ((Exp->getKind() == UETT_SizeOf) &&
+ if ((Exp->getKind() == UETT_SizeOf || Exp->getKind() == UETT_CountOf) &&
Exp->getTypeOfArgument()->isVariableArrayType())
return ICEDiag(IK_NotICE, E->getBeginLoc());
return NoDiag();
diff --git a/clang/lib/AST/ItaniumMangle.cpp b/clang/lib/AST/ItaniumMangle.cpp
index 917402544d4f6..981cdb3c806b1 100644
--- a/clang/lib/AST/ItaniumMangle.cpp
+++ b/clang/lib/AST/ItaniumMangle.cpp
@@ -5367,6 +5367,7 @@ void CXXNameMangler::mangleExpression(const Expr *E, unsigned Arity,
MangleAlignofSizeofArg();
break;
+ case UETT_CountOf:
case UETT_VectorElements:
case UETT_OpenMPRequiredSimdAlign:
case UETT_VecStep:
diff --git a/clang/lib/CodeGen/CGExprScalar.cpp b/clang/lib/CodeGen/CGExprScalar.cpp
index eccdcdb497f84..e858de7a4f6d2 100644
--- a/clang/lib/CodeGen/CGExprScalar.cpp
+++ b/clang/lib/CodeGen/CGExprScalar.cpp
@@ -3477,7 +3477,7 @@ ScalarExprEmitter::VisitUnaryExprOrTypeTraitExpr(
const UnaryExprOrTypeTraitExpr *E) {
QualType TypeToSize = E->getTypeOfArgument();
if (auto Kind = E->getKind();
- Kind == UETT_SizeOf || Kind == UETT_DataSizeOf) {
+ Kind == UETT_SizeOf || Kind == UETT_DataSizeOf || Kind == UETT_CountOf) {
if (const VariableArrayType *VAT =
CGF.getContext().getAsVariableArrayType(TypeToSize)) {
if (E->isArgumentType()) {
@@ -3492,10 +3492,15 @@ ScalarExprEmitter::VisitUnaryExprOrTypeTraitExpr(
auto VlaSize = CGF.getVLASize(VAT);
llvm::Value *size = VlaSize.NumElts;
- // Scale the number of non-VLA elements by the non-VLA element size.
- CharUnits eltSize = CGF.getContext().getTypeSizeInChars(VlaSize.Type);
- if (!eltSize.isOne())
- size = CGF.Builder.CreateNUWMul(CGF.CGM.getSize(eltSize), size);
+ // For sizeof and __datasizeof, we need to scale the number of elements
+ // by the size of the array element type. For _Countof, we just want to
+ // return the size directly.
+ if (Kind != UETT_CountOf) {
+ // Scale the number of non-VLA elements by the non-VLA element size.
+ CharUnits eltSize = CGF.getContext().getTypeSizeInChars(VlaSize.Type);
+ if (!eltSize.isOne())
+ size = CGF.Builder.CreateNUWMul(CGF.CGM.getSize(eltSize), size);
+ }
return size;
}
diff --git a/clang/lib/Parse/ParseExpr.cpp b/clang/lib/Parse/ParseExpr.cpp
index 0c28972d6ed8f..0a22f7372a9f9 100644
--- a/clang/lib/Parse/ParseExpr.cpp
+++ b/clang/lib/Parse/ParseExpr.cpp
@@ -904,6 +904,8 @@ ExprResult Parser::ParseBuiltinPtrauthTypeDiscriminator() {
/// [GNU] '__alignof' '(' type-name ')'
/// [C11] '_Alignof' '(' type-name ')'
/// [C++11] 'alignof' '(' type-id ')'
+/// [C2y] '_Countof' unary-expression
+/// [C2y] '_Countof' '(' type-name ')'
/// [GNU] '&&' identifier
/// [C++11] 'noexcept' '(' expression ')' [C++11 5.3.7]
/// [C++] new-expression
@@ -1544,6 +1546,7 @@ ExprResult Parser::ParseCastExpression(CastParseKind ParseKind,
// unary-expression: '__builtin_omp_required_simd_align' '(' type-name ')'
case tok::kw___builtin_omp_required_simd_align:
case tok::kw___builtin_vectorelements:
+ case tok::kw__Countof:
if (NotPrimaryExpression)
*NotPrimaryExpression = true;
AllowSuffix = false;
@@ -2463,7 +2466,7 @@ Parser::ParseExprAfterUnaryExprOrTypeTrait(const Token &OpTok,
tok::kw___datasizeof, tok::kw___alignof, tok::kw_alignof,
tok::kw__Alignof, tok::kw_vec_step,
tok::kw___builtin_omp_required_simd_align,
- tok::kw___builtin_vectorelements) &&
+ tok::kw___builtin_vectorelements, tok::kw__Countof) &&
"Not a typeof/sizeof/alignof/vec_step expression!");
ExprResult Operand;
@@ -2510,9 +2513,9 @@ Parser::ParseExprAfterUnaryExprOrTypeTrait(const Token &OpTok,
// is not going to help when the nesting is too deep. In this corner case
// we continue to parse with sufficient stack space to avoid crashing.
if (OpTok.isOneOf(tok::kw_sizeof, tok::kw___datasizeof, tok::kw___alignof,
- tok::kw_alignof, tok::kw__Alignof) &&
+ tok::kw_alignof, tok::kw__Alignof, tok::kw__Countof) &&
Tok.isOneOf(tok::kw_sizeof, tok::kw___datasizeof, tok::kw___alignof,
- tok::kw_alignof, tok::kw__Alignof))
+ tok::kw_alignof, tok::kw__Alignof, tok::kw__Countof))
Actions.runWithSufficientStackSpace(Tok.getLocation(), [&] {
Operand = ParseCastExpression(UnaryExprOnly);
});
@@ -2594,12 +2597,14 @@ ExprResult Parser::ParseSYCLUniqueStableNameExpression() {
/// [GNU] '__alignof' '(' type-name ')'
/// [C11] '_Alignof' '(' type-name ')'
/// [C++11] 'alignof' '(' type-id ')'
+/// [C2y] '_Countof' unary-expression
+/// [C2y] '_Countof' '(' type-name ')'
/// \endverbatim
ExprResult Parser::ParseUnaryExprOrTypeTraitExpression() {
assert(Tok.isOneOf(tok::kw_sizeof, tok::kw___datasizeof, tok::kw___alignof,
tok::kw_alignof, tok::kw__Alignof, tok::kw_vec_step,
tok::kw___builtin_omp_required_simd_align,
- tok::kw___builtin_vectorelements) &&
+ tok::kw___builtin_vectorelements, tok::kw__Countof) &&
"Not a sizeof/alignof/vec_step expression!");
Token OpTok = Tok;
ConsumeToken();
@@ -2656,6 +2661,8 @@ ExprResult Parser::ParseUnaryExprOrTypeTraitExpression() {
Diag(OpTok, diag::warn_cxx98_compat_alignof);
else if (getLangOpts().C23 && OpTok.is(tok::kw_alignof))
Diag(OpTok, diag::warn_c23_compat_keyword) << OpTok.getName();
+ else if (getLangOpts().C2y && OpTok.is(tok::kw__Countof))
+ Diag(OpTok, diag::warn_c2y_compat_keyword) << OpTok.getName();
EnterExpressionEvaluationContext Unevaluated(
Actions, Sema::ExpressionEvaluationContext::Unevaluated,
@@ -2690,6 +2697,12 @@ ExprResult Parser::ParseUnaryExprOrTypeTraitExpression() {
case tok::kw___builtin_vectorelements:
ExprKind = UETT_VectorElements;
break;
+ case tok::kw__Countof:
+ ExprKind = UETT_CountOf;
+ assert(!getLangOpts().CPlusPlus && "_Countof in C++ mode?");
+ if (!getLangOpts().C2y)
+ Diag(OpTok, diag::ext_c2y_feature) << OpTok.getName();
+ break;
default:
break;
}
diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp
index 3af6d6c23438f..f7554d90ee4c5 100644
--- a/clang/lib/Sema/SemaExpr.cpp
+++ b/clang/lib/Sema/SemaExpr.cpp
@@ -4266,7 +4266,7 @@ bool Sema::CheckUnaryExprOrTypeTraitOperand(Expr *E,
bool IsUnevaluatedOperand =
(ExprKind == UETT_SizeOf || ExprKind == UETT_DataSizeOf ||
ExprKind == UETT_AlignOf || ExprKind == UETT_PreferredAlignOf ||
- ExprKind == UETT_VecStep);
+ ExprKind == UETT_VecStep || ExprKind == UETT_CountOf);
if (IsUnevaluatedOperand) {
ExprResult Result = CheckUnevaluatedOperand(E);
if (Result.isInvalid())
@@ -4338,6 +4338,21 @@ bool Sema::CheckUnaryExprOrTypeTraitOperand(Expr *E,
E->getSourceRange(), ExprKind))
return true;
+ if (ExprKind == UETT_CountOf) {
+ // The type has to be an array type. We already checked for incomplete
+ // types above.
+ QualType ExprType = E->IgnoreParens()->getType();
+ if (!ExprType->isArrayType()) {
+ Diag(E->getExprLoc(), diag::err_countof_arg_not_array_type) << ExprType;
+ return true;
+ }
+ // FIXME: warn on _Countof on an array parameter. Not warning on it
+ // currently because there are papers in WG14 about array types which do
+ // not decay that could impact this behavior, so we want to see if anything
+ // changes here before coming up with a warning group for _Countof-related
+ // diagnostics.
+ }
+
if (ExprKind == UETT_SizeOf) {
if (const auto *DeclRef = dyn_cast<DeclRefExpr>(E->IgnoreParens())) {
if (const auto *PVD = dyn_cast<ParmVarDecl>(DeclRef->getFoundDecl())) {
@@ -4608,6 +4623,15 @@ bool Sema::CheckUnaryExprOrTypeTraitOperand(QualType ExprType,
return true;
}
+ if (ExprKind == UETT_CountOf) {
+ // The type has to be an array type. We already checked for incomplete
+ // types above.
+ if (!ExprType->isArrayType()) {
+ Diag(OpLoc, diag::err_countof_arg_not_array_type) << ExprType;
+ return true;
+ }
+ }
+
// WebAssembly tables are always illegal operands to unary expressions and
// type traits.
if (Context.getTargetInfo().getTriple().isWasm() &&
@@ -4666,7 +4690,8 @@ ExprResult Sema::CreateUnaryExprOrTypeTraitExpr(TypeSourceInfo *TInfo,
// properly deal with VLAs in nested calls of sizeof and typeof.
if (currentEvaluationContext().isUnevaluated() &&
currentEvaluationContext().InConditionallyConstantEvaluateContext &&
- ExprKind == UETT_SizeOf && TInfo->getType()->isVariablyModifiedType())
+ (ExprKind == UETT_SizeOf || ExprKind == UETT_CountOf) &&
+ TInfo->getType()->isVariablyModifiedType())
TInfo = TransformToPotentiallyEvaluated(TInfo);
// C99 6.5.3.4p4: the type (an unsigned integer type) is size_t.
@@ -4697,16 +4722,16 @@ Sema::CreateUnaryExprOrTypeTraitExpr(Expr *E, SourceLocation OpLoc,
} else if (E->refersToBitField()) { // C99 6.5.3.4p1.
Diag(E->getExprLoc(), diag::err_sizeof_alignof_typeof_bitfield) << 0;
isInvalid = true;
- } else if (ExprKind == UETT_VectorElements) {
- isInvalid = CheckUnaryExprOrTypeTraitOperand(E, UETT_VectorElements);
- } else {
- isInvalid = CheckUnaryExprOrTypeTraitOperand(E, UETT_SizeOf);
+ } else if (ExprKind == UETT_VectorElements || ExprKind == UETT_SizeOf ||
+ ExprKind == UETT_CountOf) { // FIXME: __datasizeof?
+ isInvalid = CheckUnaryExprOrTypeTraitOperand(E, ExprKind);
}
if (isInvalid)
return ExprError();
- if (ExprKind == UETT_SizeOf && E->getType()->isVariableArrayType()) {
+ if ((ExprKind == UETT_SizeOf || ExprKind == UETT_CountOf) &&
+ E->getType()->isVariableArrayType()) {
PE = TransformToPotentiallyEvaluated(E);
if (PE.isInvalid()) return ExprError();
E = PE.get();
diff --git a/clang/test/C/C2y/n3369.c b/clang/test/C/C2y/n3369.c
new file mode 100644
index 0000000000000..c199feb7f9d54
--- /dev/null
+++ b/clang/test/C/C2y/n3369.c
@@ -0,0 +1,61 @@
+// RUN: %clang_cc1 -fsyntax-only -std=c2y -pedantic -Wall -Wno-comment -verify %s
+
+/* WG14 N3369: Clang 21
+ * _Lengthof operator
+ *
+ * Adds an operator to get the length of an array. Note that WG14 N3469 renamed
+ * this operator to _Countof.
+ */
+
+int global_array[12];
+
+void test_parsing_failures() {
+ (void)_Countof; // expected-error {{expected expression}}
+ (void)_Countof(; // expected-error {{expected expression}}
+ (void)_Countof(); // expected-error {{expected expression}}
+ (void)_Countof int; // expected-error {{expected expression}}
+}
+
+void test_semantic_failures() {
+ (void)_Countof(1); // expected-error {{'_Countof' requires an argument of array type; 'int' invalid}}
+ int non_array;
+ (void)_Countof non_array; // expected-error {{'_Countof' requires an argument of array type; 'int' invalid}}
+ (void)_Countof(int); // expected-error {{'_Countof' requires an argument of array type; 'int' invalid}}
+}
+
+void test_constant_expression_behavior(int n) {
+ static_assert(_Countof(global_array) == 12);
+ static_assert(_Countof global_array == 12);
+ static_assert(_Countof(int[12]) == 12);
+
+ // Use of a VLA makes it not a constant expression, same as with sizeof.
+ int array[n];
+ static_assert(_Countof(array)); // expected-error {{static assertion expression is not an integral constant expression}}
+ static_assert(sizeof(array)); // expected-error {{static assertion expression is not an integral constant expression}}
+ static_assert(_Countof(int[n]));// expected-error {{static assertion expression is not an integral constant expression}}
+ static_assert(sizeof(int[n])); // expected-error {{static assertion expression is not an integral constant expression}}
+
+ // Constant folding works the same way as sizeof, too.
+ const int m = 12;
+ int other_array[m];
+ static_assert(sizeof(other_array)); // expected-error {{static assertion expression is not an integral constant expression}}
+ static_assert(_Countof(other_array)); // expected-error {{static assertion expression is not an integral constant expression}}
+ static_assert(sizeof(int[m])); // expected-error {{static assertion expression is not an integral constant expression}}
+ static_assert(_Countof(int[m])); // expected-error {{static assertion expression is not an integral constant expression}}
+
+ // Note that this applies to each array dimension.
+ int another_array[n][7];
+ static_assert(_Countof(another_array)); // expected-error {{static assertion expression is not an integral constant expression}}
+ static_assert(_Countof(*another_array) == 7);
+}
+
+void test_with_function_param(int array[12], int (*array_ptr)[12]) {
+ (void)_Countof(array); // expected-error {{'_Countof' requires an argument of array type; 'int *' invalid}}
+ static_assert(_Countof(*array_ptr) == 12);
+}
+
+void test_multidimensional_arrays() {
+ int array[12][7];
+ static_assert(_Countof(array) == 12);
+ static_assert(_Countof(*array) == 7);
+}
diff --git a/clang/test/C/C2y/n3369_1.c b/clang/test/C/C2y/n3369_1.c
new file mode 100644
index 0000000000000..b4e75151a5404
--- /dev/null
+++ b/clang/test/C/C2y/n3369_1.c
@@ -0,0 +1,25 @@
+/* RUN: %clang_cc1 -fsyntax-only -std=c2y -pedantic -Wpre-c2y-compat -verify=compat %s
+ RUN: %clang_cc1 -fsyntax-only -std=c23 -pedantic -verify %s
+ RUN: %clang_cc1 -fsyntax-only -std=c89 -pedantic -verify=expected,static-assert %s
+ RUN: %clang_cc1 -fsyntax-only -pedantic -verify=cpp,static-assert -x c++ %s
+ */
+
+/* This tests the extension behavior for _Countof in language modes before C2y.
+ * It also tests the behavior of the precompat warning. And it tests the
+ * behavior in C++ mode where the extension is not supported.
+ */
+int array[12];
+int x = _Countof(array); /* expected-warning {{'_Countof' is a C2y extension}}
+ compat-warning {{'_Countof' is incompatible with C standards before C2y}}
+ cpp-error {{use of undeclared identifier '_Countof'}}
+ */
+int y = _Countof(int[12]); /* expected-warning {{'_Countof' is a C2y extension}}
+ compat-warning {{'_Countof' is incompatible with C standards before C2y}}
+ cpp-error {{expected '(' for function-style cast or type construction}}
+ */
+
+_Static_assert(_Countof(int[12]) == 12, ""); /* expected-warning {{'_Countof' is a C2y extension}}
+ compat-warning {{'_Countof' is incompatible with C standards before C2y}}
+ cpp-error {{expected '(' for function-style cast or type construction}}
+ static-a...
[truncated]
|
You can test this locally with the following command:git-clang-format --diff 15f5a7a3ec71c624cea0cbdf02e3c5205ba81d9d b50ac64688338b7c21df9434c3cd5b075c3832ca --extensions cpp,h,c -- clang/test/C/C2y/n3369.c clang/test/C/C2y/n3369_1.c clang/test/C/C2y/n3369_2.c clang/test/C/C2y/n3469.c clang/include/clang/AST/Stmt.h clang/include/clang/AST/Type.h clang/lib/AST/ByteCode/Compiler.cpp clang/lib/AST/ExprConstant.cpp clang/lib/AST/ItaniumMangle.cpp clang/lib/CodeGen/CGExprScalar.cpp clang/lib/Parse/ParseExpr.cpp clang/lib/Sema/SemaExpr.cpp View the diff from clang-format here.diff --git a/clang/lib/Parse/ParseExpr.cpp b/clang/lib/Parse/ParseExpr.cpp
index 0a22f7372a..14c9b59926 100644
--- a/clang/lib/Parse/ParseExpr.cpp
+++ b/clang/lib/Parse/ParseExpr.cpp
@@ -953,20 +953,18 @@ ExprResult Parser::ParseBuiltinPtrauthTypeDiscriminator() {
/// [OBJC] '\@encode' '(' type-name ')'
/// [OBJC] objc-string-literal
/// [C++] simple-type-specifier '(' expression-list[opt] ')' [C++ 5.2.3]
-/// [C++11] simple-type-specifier braced-init-list [C++11 5.2.3]
-/// [C++] typename-specifier '(' expression-list[opt] ')' [C++ 5.2.3]
-/// [C++11] typename-specifier braced-init-list [C++11 5.2.3]
-/// [C++] 'const_cast' '<' type-name '>' '(' expression ')' [C++ 5.2p1]
-/// [C++] 'dynamic_cast' '<' type-name '>' '(' expression ')' [C++ 5.2p1]
-/// [C++] 'reinterpret_cast' '<' type-name '>' '(' expression ')' [C++ 5.2p1]
-/// [C++] 'static_cast' '<' type-name '>' '(' expression ')' [C++ 5.2p1]
-/// [C++] 'typeid' '(' expression ')' [C++ 5.2p1]
-/// [C++] 'typeid' '(' type-id ')' [C++ 5.2p1]
-/// [C++] 'this' [C++ 9.3.2]
-/// [G++] unary-type-trait '(' type-id ')'
-/// [G++] binary-type-trait '(' type-id ',' type-id ')' [TODO]
-/// [EMBT] array-type-trait '(' type-id ',' integer ')'
-/// [clang] '^' block-literal
+/// [C++11] simple-type-specifier braced-init-list [C++11 5.2.3] [C++]
+/// typename-specifier '(' expression-list[opt] ')' [C++ 5.2.3] [C++11]
+/// typename-specifier braced-init-list [C++11 5.2.3] [C++]
+/// 'const_cast' '<' type-name '>' '(' expression ')' [C++ 5.2p1] [C++]
+/// 'dynamic_cast' '<' type-name '>' '(' expression ')' [C++ 5.2p1] [C++]
+/// 'reinterpret_cast' '<' type-name '>' '(' expression ')' [C++ 5.2p1] [C++]
+/// 'static_cast' '<' type-name '>' '(' expression ')' [C++ 5.2p1] [C++]
+/// 'typeid' '(' expression ')' [C++ 5.2p1] [C++]
+/// 'typeid' '(' type-id ')' [C++ 5.2p1] [C++]
+/// 'this' [C++ 9.3.2] [G++] unary-type-trait '(' type-id ')' [G++]
+/// binary-type-trait '(' type-id ',' type-id ')' [TODO] [EMBT]
+/// array-type-trait '(' type-id ',' integer ')' [clang] '^' block-literal
///
/// constant: [C99 6.4.4]
/// integer-constant
|
Although C++ might have good alternatives, that's not so helpful for C headers being included from C++ that might end up using this (e.g. some static inline function in a system header). (i.e. if it's not too much effort to do, I would encourage exposing it in C++) |
We can contrive reasons to expose this in C++, and it would be a conforming extension there, but I'm still not convinced that's a good idea. Historically, many of the things we expose from C into C++ have some really rough edges we mostly ignore, like VLAs, _Complex vs std::complex, etc and I'd like to break that cycle by not exposing everything to both languages just because we can. I'm not strongly opposed in this case, but I'd still rather wait for a real world use case beyond "it could be used there" before we expose it to C++. |
Good catch on the VLA test cases @jyknight! It turns out we model VLAs very oddly in the type system. Given:
We make a VariableArrayType whose element type is a VariableArrayType rather than a ConstantArrayType of size 7 whose element type is a VariableArrayType. This made |
Yea. Up till now I think there was no observable difference between the outer array having a constant or variable bound when the inner array is already a VLA, so it's perhaps not surprising it was modeled that way. |
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.
Did WG14 considered some sort of macros for this feature?
Otherwise I'd be tempted to ask you to add something to __has_feature
. I can see user wanting to use _Countof
when available and fallback to something else when not.
There is weak consensus to add |
Then I'd really like a feature.
|
So you want this to be a |
Presumably yes.
Are most users going to include My assumption is that they would not, so having the feature thingy is useful (especially in a library that may be concerned about not defining |
Hmmm okay, that seems reasonable enough. |
I've added that now. |
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.
Thanks!
Thanks @AaronBallman !! I am still working on my patch, so just for learning about Clang internals, I'll try to finish mine, and compare it to yours. :-) Sorry for being so slow! Cheers, |
Sorry for stealing this one out from under you! It turns out it was a bit more involved than I expected because I didn't realize how we represented multidimensional VLAs in the type system.
No worries! I wasn't certain if you had gotten busy or not, and I had some spare bandwidth and wanted to write code for a bit. |
No problem. :-)
Yep, the GCC patches were also non-trivial; at least for some corner cases. I had to wait for Martin to patch something unrelated to make it work. BTW, I'll check all the tests I wrote for my implementation, and contribute anything that might be missing here (if any).
:) |
Link: <llvm#102836> Link: <llvm#133125> Signed-off-by: Alejandro Colomar <alx@kernel.org>
This comment was marked as resolved.
This comment was marked as resolved.
Link: <llvm#102836> Link: <llvm#133125> Cc: Aaron Ballman <aaron@aaronballman.com> Signed-off-by: Alejandro Colomar <alx@kernel.org>
Link: <llvm#102836> Link: <llvm#133125> Cc: Aaron Ballman <aaron@aaronballman.com> Signed-off-by: Alejandro Colomar <alx@kernel.org>
C2y adds the
_Countof
operator which returns the number of elements in an array. As withsizeof
,_Countof
either accepts a parenthesized type name or an expression. Its operand must be (of) an array type. When passed a constant-size array operand, the operator is a constant expression which is valid for use as an integer constant expression.This is being exposed as an extension in earlier C language modes, but not in C++. C++ already has
std::extent
andstd::rank
to cover these needs, so the operator doesn't seem to get the user enough benefit to warrant carrying this as an extension.Fixes #102836