[llvm] [libcxx] [clang-tools-extra] [clang] [lld] [mlir] [libc] [libunwind] [libcxxabi] [openmp] [flang] [C23] Implement N3018: The constexpr specifier for object definitions (PR #73099)
Mariya Podchishchaeva via cfe-commits
cfe-commits at lists.llvm.org
Thu Nov 30 02:10:40 PST 2023
================
@@ -14240,6 +14294,114 @@ StmtResult Sema::ActOnCXXForRangeIdentifier(Scope *S, SourceLocation IdentLoc,
: IdentLoc);
}
+static ImplicitConversionKind getConversionKind(QualType FromType,
+ QualType ToType) {
+ if (ToType->isIntegerType()) {
+ if (FromType->isComplexType())
+ return ICK_Complex_Real;
+ if (FromType->isFloatingType())
+ return ICK_Floating_Integral;
+ if (FromType->isIntegerType())
+ return ICK_Integral_Conversion;
+ }
+
+ if (ToType->isFloatingType()) {
+ if (FromType->isComplexType())
+ return ICK_Complex_Real;
+ if (FromType->isFloatingType())
+ return ICK_Floating_Conversion;
+ if (FromType->isIntegerType())
+ return ICK_Floating_Integral;
+ }
+
+ return ICK_Identity;
+}
+
+static bool checkC23ConstexprInitConversion(Sema &S, const Expr *Init) {
+ assert(S.getLangOpts().C23);
+ const Expr *InitNoCast = Init->IgnoreImpCasts();
+ StandardConversionSequence SCS;
+ SCS.setAsIdentityConversion();
+ auto FromType = InitNoCast->getType();
+ auto ToType = Init->getType();
+ SCS.setToType(0, FromType);
+ SCS.setToType(1, ToType);
+ SCS.Second = getConversionKind(FromType, ToType);
+
+ APValue Value;
+ QualType PreNarrowingType;
+ // Reuse C++ narrowing check.
+ switch (SCS.getNarrowingKind(S.Context, Init, Value, PreNarrowingType,
+ /*IgnoreFloatToIntegralConversion*/ false)) {
+ // The value doesn't fit.
+ case NK_Constant_Narrowing:
+ S.Diag(Init->getBeginLoc(), diag::err_c23_constexpr_init_not_representable)
+ << Value.getAsString(S.Context, PreNarrowingType) << ToType;
+ return true;
+
+ // Conversion to a narrower type.
+ case NK_Type_Narrowing:
+ S.Diag(Init->getBeginLoc(), diag::err_c23_constexpr_init_type_mismatch)
+ << ToType << FromType;
+ return true;
+
+ // Since we only reuse narrowing check for C23 constexpr variables here, we're
+ // not really interested in these cases.
+ case NK_Dependent_Narrowing:
+ case NK_Variable_Narrowing:
+ case NK_Not_Narrowing:
+ return false;
+ }
+ llvm_unreachable("unhandled case in switch");
+}
+
+static bool checkC23ConstexprInitStringLiteral(const StringLiteral *SE,
+ Sema &SemaRef,
+ SourceLocation Loc) {
+ assert(SemaRef.getLangOpts().C23);
+ // String literals have the target type attached but underneath may contain
+ // values that don't really fit into the target type. Check that every
+ // character fits.
+ const ConstantArrayType *CAT =
+ SemaRef.Context.getAsConstantArrayType(SE->getType());
+ QualType CharType = CAT->getElementType();
+ uint32_t BitWidth = SemaRef.Context.getTypeSize(CharType);
+ bool isUnsigned = CharType->isUnsignedIntegerType();
+ llvm::APSInt Value(BitWidth, isUnsigned);
+ const StringRef S = SE->getBytes();
+ for (unsigned I = 0, N = SE->getLength(); I != N; ++I) {
+ Value = S[I];
+ if (Value != S[I]) {
+ SemaRef.Diag(Loc, diag::err_c23_constexpr_init_not_representable)
+ << S[I] << CharType;
----------------
Fznamznon wrote:
What I'm trying to implement is 6.7.1 p5 of C (looking at N3096 draft):
> An object declared with storage-class specifier constexpr or any of its members, even recursively,
shall not have an atomic type, or a variably modified type, or a type that is volatile or restrict
qualified. An initializer of floating type shall be evaluated with the translation-time floating-point
environment. The declaration shall be a definition and shall have an initializer.139) **The value of
any constant expressions or of any character in a string literal of the initializer shall be exactly
representable in the corresponding target type**; no change of value shall be applied
There is also an illustrating example in p18:
> Similarly, implementation-defined behavior related to the char type of the elements of the string literal "\xFF"
may cause constraint violations at translation time:
```
constexpr char string[] = { "\xFF", }; // ok
constexpr char8_t u8string[] = { u8"\xFF", }; // ok
constexpr unsigned char ucstring[] = { "\xFF", }; // possible constraint
// violation
```
> In both the string and ucstring initializers, the initializer is a (brace-enclosed) string literal of type char. If the type char is
capable of representing negative values and its width is 8, then the code above is equivalent to:
```
constexpr char string[] = { -1, 0, }; // ok
constexpr char8_t u8string[] = { 255, 0, }; // ok
constexpr unsigned char ucstring[] = { -1, 0, }; // constraint violation
```
About
> StringLiterals ought to be representable per constructions
https://eel.is/c++draft/lex.string#10.2.4
https://eel.is/c++draft/lex.string#10.1.sentence-2
My understanding that it is not the same for C. At least I fail to find somewhat same in C draft.
https://github.com/llvm/llvm-project/pull/73099
More information about the cfe-commits
mailing list