aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJohn McCall <rjmccall@apple.com>2011-08-27 22:06:17 +0000
committerJohn McCall <rjmccall@apple.com>2011-08-27 22:06:17 +0000
commit96a914a50cb8c01be8a3b7481cc4791e19c4285b (patch)
tree47f97a09ee8727cb1389d5543fb661429a3674f1
parentb8607398a5e1e552f82a86d1d8c3a4031ac4c946 (diff)
Disable the l-value to r-value conversion on C++ class types passed
to varargs functions in unevaluated contexts. AFAICT, there is no standards justification for this, but it matches what other compilers do and therefore preserves compatibility with certain template metaprogramming idioms. Should fix self-host. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@138715 91177308-0d34-0410-b5e6-96231b3b80d8
-rw-r--r--lib/Sema/SemaExpr.cpp12
-rw-r--r--test/CXX/special/class.temporary/p1.cpp21
2 files changed, 30 insertions, 3 deletions
diff --git a/lib/Sema/SemaExpr.cpp b/lib/Sema/SemaExpr.cpp
index 65c9cccaf1..6327ee71db 100644
--- a/lib/Sema/SemaExpr.cpp
+++ b/lib/Sema/SemaExpr.cpp
@@ -443,9 +443,15 @@ ExprResult Sema::DefaultArgumentPromotion(Expr *E) {
if (Ty->isSpecificBuiltinType(BuiltinType::Float))
E = ImpCastExprToType(E, Context.DoubleTy, CK_FloatingCast).take();
- // C++ includes lvalue-to-rvalue conversion as a default argument
- // promotion. If we have a gl-value, initialize a temporary.
- if (getLangOptions().CPlusPlus && E->isGLValue()) {
+ // C++ performs lvalue-to-rvalue conversion as a default argument
+ // promotion. If we still have a gl-value after usual unary
+ // conversion, we must have an l-value of class type, so we need to
+ // initialize a temporary. For compatibility reasons, however, we
+ // don't want to do this in unevaluated contexts; otherwise we
+ // reject metaprograms which work by passing uncopyable l-values to
+ // variadic functions.
+ if (getLangOptions().CPlusPlus && E->isGLValue() &&
+ ExprEvalContexts.back().Context != Unevaluated) {
ExprResult Temp = PerformCopyInitialization(
InitializedEntity::InitializeTemporary(E->getType()),
E->getExprLoc(),
diff --git a/test/CXX/special/class.temporary/p1.cpp b/test/CXX/special/class.temporary/p1.cpp
index e3b8f9ce66..07890eb297 100644
--- a/test/CXX/special/class.temporary/p1.cpp
+++ b/test/CXX/special/class.temporary/p1.cpp
@@ -35,3 +35,24 @@ namespace test1 {
foo(a); // expected-error {{calling a private constructor of class 'test1::A'}} expected-error {{cannot pass object of non-trivial type 'test1::A' through variadic function}}
}
}
+
+// Don't enforce this in an unevaluated context.
+namespace test2 {
+ struct A {
+ A(const A&) = delete; // expected-note {{marked deleted here}}
+ };
+
+ typedef char one[1];
+ typedef char two[2];
+
+ one &meta(bool);
+ two &meta(...);
+
+ void a(A &a) {
+ char check[sizeof(meta(a)) == 2 ? 1 : -1];
+ }
+
+ void b(A &a) {
+ meta(a); // expected-error {{call to deleted constructor}}
+ }
+}