The lvalue-to-rvalue on structs in C++ is actually part

of default argument promotion and needs to happen unconditionally.
This is particularly semantically important in C++0x.

llvm-svn: 138691
This commit is contained in:
John McCall 2011-08-27 01:09:30 +00:00
parent 79cfba180a
commit 29ad95b232
5 changed files with 116 additions and 28 deletions

View File

@ -389,7 +389,7 @@ BUILTIN(__builtin_constant_p, "i.", "nct")
BUILTIN(__builtin_classify_type, "i.", "nct")
BUILTIN(__builtin___CFStringMakeConstantString, "FC*cC*", "nc")
BUILTIN(__builtin___NSStringMakeConstantString, "FC*cC*", "nc")
BUILTIN(__builtin_va_start, "vA.", "n")
BUILTIN(__builtin_va_start, "vA.", "nt")
BUILTIN(__builtin_va_end, "vA", "n")
BUILTIN(__builtin_va_copy, "vAA", "n")
BUILTIN(__builtin_stdarg_start, "vA.", "n")

View File

@ -12,6 +12,7 @@
//
//===----------------------------------------------------------------------===//
#include "clang/Sema/Initialization.h"
#include "clang/Sema/Sema.h"
#include "clang/Sema/SemaInternal.h"
#include "clang/Sema/ScopeInfo.h"
@ -396,6 +397,30 @@ bool Sema::CheckBlockCall(NamedDecl *NDecl, CallExpr *TheCall) {
return false;
}
/// checkBuiltinArgument - Given a call to a builtin function, perform
/// normal type-checking on the given argument, updating the call in
/// place. This is useful when a builtin function requires custom
/// type-checking for some of its arguments but not necessarily all of
/// them.
///
/// Returns true on error.
static bool checkBuiltinArgument(Sema &S, CallExpr *E, unsigned ArgIndex) {
FunctionDecl *Fn = E->getDirectCallee();
assert(Fn && "builtin call without direct callee!");
ParmVarDecl *Param = Fn->getParamDecl(ArgIndex);
InitializedEntity Entity =
InitializedEntity::InitializeParameter(S.Context, Param);
ExprResult Arg = E->getArg(0);
Arg = S.PerformCopyInitialization(Entity, SourceLocation(), Arg);
if (Arg.isInvalid())
return true;
E->setArg(ArgIndex, Arg.take());
return false;
}
/// SemaBuiltinAtomicOverloaded - We have a call to a function like
/// __sync_fetch_and_add, which is an overloaded function based on the pointer
/// type of its first argument. The main ActOnCallExpr routines have already
@ -661,6 +686,10 @@ bool Sema::SemaBuiltinVAStart(CallExpr *TheCall) {
<< 0 /*function call*/ << 2 << TheCall->getNumArgs();
}
// Type-check the first argument normally.
if (checkBuiltinArgument(*this, TheCall, 0))
return true;
// Determine whether the current function is variadic or not.
BlockScopeInfo *CurBlock = getCurBlock();
bool isVariadic;

View File

@ -443,6 +443,18 @@ 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()) {
ExprResult Temp = PerformCopyInitialization(
InitializedEntity::InitializeTemporary(E->getType()),
E->getExprLoc(),
Owned(E));
if (Temp.isInvalid())
return ExprError();
E = Temp.get();
}
return Owned(E);
}
@ -460,19 +472,13 @@ ExprResult Sema::DefaultVariadicArgumentPromotion(Expr *E, VariadicCallType CT,
return ExprError();
E = ExprRes.take();
// __builtin_va_start takes the second argument as a "varargs" argument, but
// it doesn't actually do anything with it. It doesn't need to be non-pod
// etc.
if (FDecl && FDecl->getBuiltinID() == Builtin::BI__builtin_va_start)
return Owned(E);
// Don't allow one to pass an Objective-C interface to a vararg.
if (E->getType()->isObjCObjectType() &&
DiagRuntimeBehavior(E->getLocStart(), 0,
PDiag(diag::err_cannot_pass_objc_interface_to_vararg)
<< E->getType() << CT))
return ExprError();
if (!E->getType().isPODType(Context)) {
// C++0x [expr.call]p7:
// Passing a potentially-evaluated argument of class type (Clause 9)
@ -519,16 +525,6 @@ ExprResult Sema::DefaultVariadicArgumentPromotion(Expr *E, VariadicCallType CT,
if (Comma.isInvalid())
return ExprError();
E = Comma.get();
// Use that to initialize a temporary, or else we might get an
// l-value in a varargs position.
ExprResult Temp = PerformCopyInitialization(
InitializedEntity::InitializeTemporary(E->getType()),
E->getLocStart(),
Owned(E));
if (Temp.isInvalid())
return ExprError();
E = Temp.get();
}
}

View File

@ -0,0 +1,37 @@
// RUN: %clang_cc1 -std=c++0x -fsyntax-only -verify %s
namespace test0 {
struct A {
A() = default;
int x;
int y;
A(const A&) = delete; // expected-note {{function has been explicitly marked deleted here}}
};
void foo(...);
void test() {
A a;
foo(a); // expected-error {{call to deleted constructor of 'test0::A'}}
}
}
namespace test1 {
struct A {
A() = default;
int x;
int y;
private:
A(const A&) = default; // expected-note {{declared private here}}
};
void foo(...);
void test() {
A a;
// FIXME: this error about variadics is bogus
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}}
}
}

View File

@ -1,17 +1,43 @@
// RUN: %clang_cc1 -emit-llvm %s -o - | FileCheck %s
// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -emit-llvm %s -o - | FileCheck %s
// rdar://7309675
// PR4678
namespace test0 {
// test1 should be compmiled to be a varargs function in the IR even
// though there is no way to do a va_begin. Otherwise, the optimizer
// will warn about 'dropped arguments' at the call site.
// test1 should be compmiled to be a varargs function in the IR even
// though there is no way to do a va_begin. Otherwise, the optimizer
// will warn about 'dropped arguments' at the call site.
// CHECK: define i32 @_ZN5test05test1Ez(...)
int test1(...) {
return -1;
}
// CHECK: define i32 @_Z5test1z(...)
int test1(...) {
return -1;
// CHECK: call i32 (...)* @_ZN5test05test1Ez(i32 0)
void test() {
test1(0);
}
}
// CHECK: call i32 (...)* @_Z5test1z(i32 0)
void test() {
test1(0);
namespace test1 {
struct A {
int x;
int y;
};
void foo(...);
void test() {
A x;
foo(x);
}
// CHECK: define void @_ZN5test14testEv()
// CHECK: [[X:%.*]] = alloca [[A:%.*]], align 4
// CHECK-NEXT: [[TMP:%.*]] = alloca [[A]], align 4
// CHECK-NEXT: [[T0:%.*]] = bitcast [[A]]* [[TMP]] to i8*
// CHECK-NEXT: [[T1:%.*]] = bitcast [[A]]* [[X]] to i8*
// CHECK-NEXT: call void @llvm.memcpy.p0i8.p0i8.i64(i8* [[T0]], i8* [[T1]], i64 8, i32 4, i1 false)
// CHECK-NEXT: [[T0:%.*]] = bitcast [[A]]* [[TMP]] to i64*
// CHECK-NEXT: [[T1:%.*]] = load i64* [[T0]], align 1
// CHECK-NEXT: call void (...)* @_ZN5test13fooEz(i64 [[T1]])
// CHECK-NEXT: ret void
}