[PATCH] D103611: Correct the behavior of va_arg checking in C++
Aaron Ballman via Phabricator via cfe-commits
cfe-commits at lists.llvm.org
Sat Jun 5 09:17:32 PDT 2021
aaron.ballman updated this revision to Diff 350060.
aaron.ballman added a comment.
Added some more logic and new tests.
The CI pipeline pointed out a valid issue which has now been corrected -- C++ was not properly handling the case where one type was `int` and the other was `unsigned int`, which are compatible types in C but not the same type in C++. The test adds some triples and a new test case to ensure that we are able to consistently test this property in both directions.
@efriedma, would you mind taking a second look just to be sure you're still happy with the fix?
CHANGES SINCE LAST ACTION
https://reviews.llvm.org/D103611/new/
https://reviews.llvm.org/D103611
Files:
clang/lib/Sema/SemaExpr.cpp
clang/test/SemaCXX/varargs.cpp
Index: clang/test/SemaCXX/varargs.cpp
===================================================================
--- clang/test/SemaCXX/varargs.cpp
+++ clang/test/SemaCXX/varargs.cpp
@@ -1,5 +1,5 @@
-// RUN: %clang_cc1 -std=c++03 -verify %s
-// RUN: %clang_cc1 -std=c++11 -verify %s
+// RUN: %clang_cc1 -std=c++03 -Wno-c++11-extensions -triple i386-pc-unknown -verify %s
+// RUN: %clang_cc1 -std=c++11 -triple x86_64-apple-darwin9 -verify %s
__builtin_va_list ap;
@@ -28,6 +28,33 @@
};
}
+// Ensure the correct behavior for promotable type UB checking.
+void promotable(int a, ...) {
+ enum Unscoped1 { One = 0x7FFFFFFF };
+ (void)__builtin_va_arg(ap, Unscoped1); // ok
+
+ enum Unscoped2 { Two = 0xFFFFFFFF };
+ (void)__builtin_va_arg(ap, Unscoped2); // ok
+
+ enum class Scoped { Three };
+ (void)__builtin_va_arg(ap, Scoped); // ok
+
+ enum Fixed : int { Four };
+ (void)__builtin_va_arg(ap, Fixed); // ok
+
+ enum FixedSmall : char { Five };
+ (void)__builtin_va_arg(ap, FixedSmall); // expected-warning {{second argument to 'va_arg' is of promotable type 'FixedSmall'; this va_arg has undefined behavior because arguments will be promoted to 'int'}}
+
+ enum FixedLarge : long long { Six };
+ (void)__builtin_va_arg(ap, FixedLarge); // ok
+
+ // Ensure that qualifiers are ignored.
+ (void)__builtin_va_arg(ap, const volatile int); // ok
+
+ // Ensure that signed vs unsigned doesn't matter either.
+ (void)__builtin_va_arg(ap, unsigned int);
+}
+
#if __cplusplus >= 201103L
// We used to have bugs identifying the correct enclosing function scope in a
// lambda.
Index: clang/lib/Sema/SemaExpr.cpp
===================================================================
--- clang/lib/Sema/SemaExpr.cpp
+++ clang/lib/Sema/SemaExpr.cpp
@@ -15750,8 +15750,48 @@
QualType PromoteType;
if (TInfo->getType()->isPromotableIntegerType()) {
PromoteType = Context.getPromotedIntegerType(TInfo->getType());
- if (Context.typesAreCompatible(PromoteType, TInfo->getType()))
+ // [cstdarg.syn]p1 defers the C++ behavior to what the C standard says,
+ // and C2x 7.16.1.1p2 says, in part:
+ // If type is not compatible with the type of the actual next argument
+ // (as promoted according to the default argument promotions), the
+ // behavior is undefined, except for the following cases:
+ // - both types are pointers to qualified or unqualified versions of
+ // compatible types;
+ // - one type is a signed integer type, the other type is the
+ // corresponding unsigned integer type, and the value is
+ // representable in both types;
+ // - one type is pointer to qualified or unqualified void and the
+ // other is a pointer to a qualified or unqualified character type.
+ // Given that type compatibility is the primary requirement (ignoring
+ // qualifications), you would think we could call typesAreCompatible()
+ // directly to test this. However, in C++, that checks for *same type*,
+ // which causes false positives when passing an enumeration type to
+ // va_arg. Instead, get the underlying type of the enumeration and pass
+ // that.
+ QualType UnderlyingType = TInfo->getType();
+ if (const auto *ET = UnderlyingType->getAs<EnumType>())
+ UnderlyingType = ET->getDecl()->getIntegerType();
+ if (Context.typesAreCompatible(PromoteType, UnderlyingType,
+ /*CompareUnqualified*/ true))
PromoteType = QualType();
+
+ // If the types are still not compatible, in C++ we need to test whether
+ // the promoted type and the underlying type are the same except for
+ // signedness. Ask the AST for the correctly corresponding type and see
+ // if that's compatible. We don't need to do this in C because the above
+ // test for typesAreCompatible() will already properly consider those to
+ // be compatible types.
+ if (Context.getLangOpts().CPlusPlus && !PromoteType.isNull() &&
+ PromoteType->isUnsignedIntegerType() !=
+ UnderlyingType->isUnsignedIntegerType()) {
+ UnderlyingType =
+ UnderlyingType->isUnsignedIntegerType()
+ ? Context.getCorrespondingSignedType(UnderlyingType)
+ : Context.getCorrespondingUnsignedType(UnderlyingType);
+ if (Context.typesAreCompatible(PromoteType, UnderlyingType,
+ /*CompareUnqualified*/ true))
+ PromoteType = QualType();
+ }
}
if (TInfo->getType()->isSpecificBuiltinType(BuiltinType::Float))
PromoteType = Context.DoubleTy;
-------------- next part --------------
A non-text attachment was scrubbed...
Name: D103611.350060.patch
Type: text/x-patch
Size: 4693 bytes
Desc: not available
URL: <http://lists.llvm.org/pipermail/cfe-commits/attachments/20210605/0cb48555/attachment-0001.bin>
More information about the cfe-commits
mailing list