(`#50244 <https://github.com/llvm/llvm-project/issues/50244>_`).
- Apply ``-fmacro-prefix-map`` to anonymous tags in template arguments
(`#63219 <https://github.com/llvm/llvm-project/issues/63219>`_).
+- Clang now properly diagnoses format string mismatches involving scoped
+ enumeration types. A scoped enumeration type is not promoted to an integer
+ type by the default argument promotions, and thus this is UB. Clang's
+ behavior now matches GCC's behavior in C++.
+ (`#38717 <https://github.com/llvm/llvm-project/issues/38717>_`).
Bug Fixes to Compiler Builtins
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
case AnyCharTy: {
if (const auto *ETy = argTy->getAs<EnumType>()) {
// If the enum is incomplete we know nothing about the underlying type.
- // Assume that it's 'int'.
+ // Assume that it's 'int'. Do not use the underlying type for a scoped
+ // enumeration.
if (!ETy->getDecl()->isComplete())
return NoMatch;
- argTy = ETy->getDecl()->getIntegerType();
+ if (ETy->isUnscopedEnumerationType())
+ argTy = ETy->getDecl()->getIntegerType();
}
if (const auto *BT = argTy->getAs<BuiltinType>()) {
case SpecificTy: {
if (const EnumType *ETy = argTy->getAs<EnumType>()) {
// If the enum is incomplete we know nothing about the underlying type.
- // Assume that it's 'int'.
+ // Assume that it's 'int'. Do not use the underlying type for a scoped
+ // enumeration as that needs an exact match.
if (!ETy->getDecl()->isComplete())
argTy = C.IntTy;
- else
+ else if (ETy->isUnscopedEnumerationType())
argTy = ETy->getDecl()->getIntegerType();
}
argTy = C.getCanonicalType(argTy).getUnqualifiedType();
ImplicitMatch == ArgType::NoMatchTypeConfusion)
Match = ImplicitMatch;
assert(Match != ArgType::MatchPromotion);
- // Look through enums to their underlying type.
+ // Look through unscoped enums to their underlying type.
bool IsEnum = false;
if (auto EnumTy = ExprTy->getAs<EnumType>()) {
- ExprTy = EnumTy->getDecl()->getIntegerType();
- IsEnum = true;
+ if (EnumTy->isUnscopedEnumerationType()) {
+ ExprTy = EnumTy->getDecl()->getIntegerType();
+ // This controls whether we're talking about the underlying type or not,
+ // which we only want to do when it's an unscoped enum.
+ IsEnum = true;
+ }
}
// %C in an Objective-C context prints a unichar, not a wchar_t.
}
+
+namespace ScopedEnumerations {
+enum class Scoped1 { One };
+enum class Scoped2 : unsigned short { Two };
+
+void f(Scoped1 S1, Scoped2 S2) {
+ printf("%hhd", S1); // expected-warning {{format specifies type 'char' but the argument has type 'Scoped1'}}
+ printf("%hd", S1); // expected-warning {{format specifies type 'short' but the argument has type 'Scoped1'}}
+ printf("%d", S1); // expected-warning {{format specifies type 'int' but the argument has type 'Scoped1'}}
+
+ printf("%hhd", S2); // expected-warning {{format specifies type 'char' but the argument has type 'Scoped2'}}
+ printf("%hd", S2); // expected-warning {{format specifies type 'short' but the argument has type 'Scoped2'}}
+ printf("%d", S2); // expected-warning {{format specifies type 'int' but the argument has type 'Scoped2'}}
+
+ scanf("%hhd", &S1); // expected-warning {{format specifies type 'char *' but the argument has type 'Scoped1 *'}}
+ scanf("%hd", &S1); // expected-warning {{format specifies type 'short *' but the argument has type 'Scoped1 *'}}
+ scanf("%d", &S1); // expected-warning {{format specifies type 'int *' but the argument has type 'Scoped1 *'}}
+
+ scanf("%hhd", &S2); // expected-warning {{format specifies type 'char *' but the argument has type 'Scoped2 *'}}
+ scanf("%hd", &S2); // expected-warning {{format specifies type 'short *' but the argument has type 'Scoped2 *'}}
+ scanf("%d", &S2); // expected-warning {{format specifies type 'int *' but the argument has type 'Scoped2 *'}}
+}
+}
+
#endif