summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAbdoulaye Walsimou Gaye <awg@embtoolkit.org>2012-11-19 23:49:41 +0100
committerAbdoulaye Walsimou Gaye <awg@embtoolkit.org>2012-11-19 23:49:41 +0100
commit42128c4030d44bcbaf318c94a57b606b7b981362 (patch)
tree0ca7b51ec45bf0590e0e75983f0e8e4b117b8fb2
parentf3c381988e41f748e7ab0f2109ff10a9e4491ea3 (diff)
parent0d7bc297f7165a7dbefdcad3b4043c275d35c13e (diff)
downloadclang-42128c4030d44bcbaf318c94a57b606b7b981362.tar.gz
clang-42128c4030d44bcbaf318c94a57b606b7b981362.tar.bz2
clang-42128c4030d44bcbaf318c94a57b606b7b981362.tar.xz
Merge branch 'release-3.2' into embtk-support-release-3.2
-rw-r--r--lib/AST/DeclCXX.cpp19
-rw-r--r--lib/Basic/Targets.cpp7
-rw-r--r--lib/CodeGen/CGExpr.cpp9
-rw-r--r--lib/Sema/SemaPseudoObject.cpp22
-rw-r--r--lib/StaticAnalyzer/Checkers/MallocChecker.cpp96
-rw-r--r--test/Analysis/malloc.mm56
-rw-r--r--test/CodeGen/ppc-atomics.c35
-rw-r--r--test/CodeGenCXX/cxx11-special-members.cpp32
-rw-r--r--test/CodeGenObjCXX/property-objects.mm22
-rw-r--r--test/SemaObjCXX/properties.mm23
10 files changed, 277 insertions, 44 deletions
diff --git a/lib/AST/DeclCXX.cpp b/lib/AST/DeclCXX.cpp
index 9db33357a6..82e630acef 100644
--- a/lib/AST/DeclCXX.cpp
+++ b/lib/AST/DeclCXX.cpp
@@ -240,10 +240,13 @@ CXXRecordDecl::setBases(CXXBaseSpecifier const * const *Bases,
// -- the constructor selected to copy/move each direct base class
// subobject is trivial, and
// FIXME: C++0x: We need to only consider the selected constructor
- // instead of all of them.
+ // instead of all of them. For now, we treat a move constructor as being
+ // non-trivial if it calls anything other than a trivial move constructor.
if (!BaseClassDecl->hasTrivialCopyConstructor())
data().HasTrivialCopyConstructor = false;
- if (!BaseClassDecl->hasTrivialMoveConstructor())
+ if (!BaseClassDecl->hasTrivialMoveConstructor() ||
+ !(BaseClassDecl->hasDeclaredMoveConstructor() ||
+ BaseClassDecl->needsImplicitMoveConstructor()))
data().HasTrivialMoveConstructor = false;
// C++0x [class.copy]p27:
@@ -255,7 +258,9 @@ CXXRecordDecl::setBases(CXXBaseSpecifier const * const *Bases,
// of all of them.
if (!BaseClassDecl->hasTrivialCopyAssignment())
data().HasTrivialCopyAssignment = false;
- if (!BaseClassDecl->hasTrivialMoveAssignment())
+ if (!BaseClassDecl->hasTrivialMoveAssignment() ||
+ !(BaseClassDecl->hasDeclaredMoveAssignment() ||
+ BaseClassDecl->needsImplicitMoveAssignment()))
data().HasTrivialMoveAssignment = false;
// C++11 [class.ctor]p6:
@@ -830,7 +835,9 @@ NotASpecialMember:;
// FIXME: C++0x: We don't correctly model 'selected' constructors.
if (!FieldRec->hasTrivialCopyConstructor())
data().HasTrivialCopyConstructor = false;
- if (!FieldRec->hasTrivialMoveConstructor())
+ if (!FieldRec->hasTrivialMoveConstructor() ||
+ !(FieldRec->hasDeclaredMoveConstructor() ||
+ FieldRec->needsImplicitMoveConstructor()))
data().HasTrivialMoveConstructor = false;
// C++0x [class.copy]p27:
@@ -842,7 +849,9 @@ NotASpecialMember:;
// FIXME: C++0x: We don't correctly model 'selected' operators.
if (!FieldRec->hasTrivialCopyAssignment())
data().HasTrivialCopyAssignment = false;
- if (!FieldRec->hasTrivialMoveAssignment())
+ if (!FieldRec->hasTrivialMoveAssignment() ||
+ !(FieldRec->hasDeclaredMoveAssignment() ||
+ FieldRec->needsImplicitMoveAssignment()))
data().HasTrivialMoveAssignment = false;
if (!FieldRec->hasTrivialDestructor())
diff --git a/lib/Basic/Targets.cpp b/lib/Basic/Targets.cpp
index 26a4f41442..ab02e6f3b5 100644
--- a/lib/Basic/Targets.cpp
+++ b/lib/Basic/Targets.cpp
@@ -1037,6 +1037,9 @@ public:
LongDoubleWidth = LongDoubleAlign = 64;
LongDoubleFormat = &llvm::APFloat::IEEEdouble;
}
+
+ // PPC32 supports atomics up to 4 bytes.
+ MaxAtomicPromoteWidth = MaxAtomicInlineWidth = 32;
}
virtual BuiltinVaListKind getBuiltinVaListKind() const {
@@ -1065,7 +1068,9 @@ public:
DescriptionString = "E-p:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-"
"i64:64:64-f32:32:32-f64:64:64-f128:128:128-"
"v128:128:128-n32:64";
-
+
+ // PPC64 supports atomics up to 8 bytes.
+ MaxAtomicPromoteWidth = MaxAtomicInlineWidth = 64;
}
virtual BuiltinVaListKind getBuiltinVaListKind() const {
return TargetInfo::CharPtrBuiltinVaList;
diff --git a/lib/CodeGen/CGExpr.cpp b/lib/CodeGen/CGExpr.cpp
index d1a2889f9a..63cc5b515d 100644
--- a/lib/CodeGen/CGExpr.cpp
+++ b/lib/CodeGen/CGExpr.cpp
@@ -3164,11 +3164,10 @@ RValue CodeGenFunction::EmitAtomicExpr(AtomicExpr *E, llvm::Value *Dest) {
uint64_t Size = sizeChars.getQuantity();
CharUnits alignChars = getContext().getTypeAlignInChars(AtomicTy);
unsigned Align = alignChars.getQuantity();
- unsigned MaxInlineWidth =
- getContext().getTargetInfo().getMaxAtomicInlineWidth();
- bool UseLibcall = (Size != Align || Size > MaxInlineWidth);
-
-
+ unsigned MaxInlineWidthInBits =
+ getContext().getTargetInfo().getMaxAtomicInlineWidth();
+ bool UseLibcall = (Size != Align ||
+ getContext().toBits(sizeChars) > MaxInlineWidthInBits);
llvm::Value *Ptr, *Order, *OrderFail = 0, *Val1 = 0, *Val2 = 0;
Ptr = EmitScalarExpr(E->getPtr());
diff --git a/lib/Sema/SemaPseudoObject.cpp b/lib/Sema/SemaPseudoObject.cpp
index 37d9e77548..a8d75b290f 100644
--- a/lib/Sema/SemaPseudoObject.cpp
+++ b/lib/Sema/SemaPseudoObject.cpp
@@ -198,7 +198,14 @@ namespace {
}
/// Return true if assignments have a non-void result.
- virtual bool assignmentsHaveResult() { return true; }
+ bool CanCaptureValueOfType(QualType ty) {
+ assert(!ty->isIncompleteType());
+ assert(!ty->isDependentType());
+
+ if (const CXXRecordDecl *ClassDecl = ty->getAsCXXRecordDecl())
+ return ClassDecl->isTriviallyCopyable();
+ return true;
+ }
virtual Expr *rebuildAndCaptureObject(Expr *) = 0;
virtual ExprResult buildGet() = 0;
@@ -380,7 +387,7 @@ PseudoOpBuilder::buildAssignmentOperation(Scope *Sc, SourceLocation opcLoc,
// The result of the assignment, if not void, is the value set into
// the l-value.
- result = buildSet(result.take(), opcLoc, assignmentsHaveResult());
+ result = buildSet(result.take(), opcLoc, /*captureSetValueAsResult*/ true);
if (result.isInvalid()) return ExprError();
addSemanticExpr(result.take());
@@ -404,7 +411,7 @@ PseudoOpBuilder::buildIncDecOperation(Scope *Sc, SourceLocation opcLoc,
QualType resultType = result.get()->getType();
// That's the postfix result.
- if (UnaryOperator::isPostfix(opcode) && assignmentsHaveResult()) {
+ if (UnaryOperator::isPostfix(opcode) && CanCaptureValueOfType(resultType)) {
result = capture(result.take());
setResultToLastSemantic();
}
@@ -423,8 +430,7 @@ PseudoOpBuilder::buildIncDecOperation(Scope *Sc, SourceLocation opcLoc,
// Store that back into the result. The value stored is the result
// of a prefix operation.
- result = buildSet(result.take(), opcLoc,
- UnaryOperator::isPrefix(opcode) && assignmentsHaveResult());
+ result = buildSet(result.take(), opcLoc, UnaryOperator::isPrefix(opcode));
if (result.isInvalid()) return ExprError();
addSemanticExpr(result.take());
@@ -696,7 +702,8 @@ ExprResult ObjCPropertyOpBuilder::buildSet(Expr *op, SourceLocation opcLoc,
ObjCMessageExpr *msgExpr =
cast<ObjCMessageExpr>(msg.get()->IgnoreImplicit());
Expr *arg = msgExpr->getArg(0);
- msgExpr->setArg(0, captureValueAsResult(arg));
+ if (CanCaptureValueOfType(arg->getType()))
+ msgExpr->setArg(0, captureValueAsResult(arg));
}
return msg;
@@ -1312,7 +1319,8 @@ ExprResult ObjCSubscriptOpBuilder::buildSet(Expr *op, SourceLocation opcLoc,
ObjCMessageExpr *msgExpr =
cast<ObjCMessageExpr>(msg.get()->IgnoreImplicit());
Expr *arg = msgExpr->getArg(0);
- msgExpr->setArg(0, captureValueAsResult(arg));
+ if (CanCaptureValueOfType(arg->getType()))
+ msgExpr->setArg(0, captureValueAsResult(arg));
}
return msg;
diff --git a/lib/StaticAnalyzer/Checkers/MallocChecker.cpp b/lib/StaticAnalyzer/Checkers/MallocChecker.cpp
index b1cce85f99..caf70ca370 100644
--- a/lib/StaticAnalyzer/Checkers/MallocChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/MallocChecker.cpp
@@ -107,7 +107,7 @@ class MallocChecker : public Checker<check::DeadSymbols,
check::PreStmt<CallExpr>,
check::PostStmt<CallExpr>,
check::PostStmt<BlockExpr>,
- check::PreObjCMessage,
+ check::PostObjCMessage,
check::Location,
check::Bind,
eval::Assume,
@@ -135,7 +135,7 @@ public:
void checkPreStmt(const CallExpr *S, CheckerContext &C) const;
void checkPostStmt(const CallExpr *CE, CheckerContext &C) const;
- void checkPreObjCMessage(const ObjCMethodCall &Call, CheckerContext &C) const;
+ void checkPostObjCMessage(const ObjCMethodCall &Call, CheckerContext &C) const;
void checkPostStmt(const BlockExpr *BE, CheckerContext &C) const;
void checkDeadSymbols(SymbolReaper &SymReaper, CheckerContext &C) const;
void checkEndPath(CheckerContext &C) const;
@@ -193,12 +193,14 @@ private:
ProgramStateRef FreeMemAux(CheckerContext &C, const CallExpr *CE,
ProgramStateRef state, unsigned Num,
bool Hold,
- bool &ReleasedAllocated) const;
+ bool &ReleasedAllocated,
+ bool ReturnsNullOnFailure = false) const;
ProgramStateRef FreeMemAux(CheckerContext &C, const Expr *Arg,
const Expr *ParentExpr,
- ProgramStateRef state,
+ ProgramStateRef State,
bool Hold,
- bool &ReleasedAllocated) const;
+ bool &ReleasedAllocated,
+ bool ReturnsNullOnFailure = false) const;
ProgramStateRef ReallocMem(CheckerContext &C, const CallExpr *CE,
bool FreesMemOnFailure) const;
@@ -341,6 +343,10 @@ private:
REGISTER_MAP_WITH_PROGRAMSTATE(RegionState, SymbolRef, RefState)
REGISTER_MAP_WITH_PROGRAMSTATE(ReallocPairs, SymbolRef, ReallocPair)
+// A map from the freed symbol to the symbol representing the return value of
+// the free function.
+REGISTER_MAP_WITH_PROGRAMSTATE(FreeReturnValue, SymbolRef, SymbolRef)
+
namespace {
class StopTrackingCallback : public SymbolVisitor {
ProgramStateRef state;
@@ -492,8 +498,8 @@ static bool isFreeWhenDoneSetToZero(const ObjCMethodCall &Call) {
return false;
}
-void MallocChecker::checkPreObjCMessage(const ObjCMethodCall &Call,
- CheckerContext &C) const {
+void MallocChecker::checkPostObjCMessage(const ObjCMethodCall &Call,
+ CheckerContext &C) const {
// If the first selector is dataWithBytesNoCopy, assume that the memory will
// be released with 'free' by the new object.
// Ex: [NSData dataWithBytesNoCopy:bytes length:10];
@@ -506,9 +512,12 @@ void MallocChecker::checkPreObjCMessage(const ObjCMethodCall &Call,
S.getNameForSlot(0) == "initWithCharactersNoCopy") &&
!isFreeWhenDoneSetToZero(Call)){
unsigned int argIdx = 0;
- C.addTransition(FreeMemAux(C, Call.getArgExpr(argIdx),
- Call.getOriginExpr(), C.getState(), true,
- ReleasedAllocatedMemory));
+ ProgramStateRef State = FreeMemAux(C, Call.getArgExpr(argIdx),
+ Call.getOriginExpr(), C.getState(), true,
+ ReleasedAllocatedMemory,
+ /* RetNullOnFailure*/ true);
+
+ C.addTransition(State);
}
}
@@ -609,21 +618,39 @@ ProgramStateRef MallocChecker::FreeMemAux(CheckerContext &C,
ProgramStateRef state,
unsigned Num,
bool Hold,
- bool &ReleasedAllocated) const {
+ bool &ReleasedAllocated,
+ bool ReturnsNullOnFailure) const {
if (CE->getNumArgs() < (Num + 1))
return 0;
- return FreeMemAux(C, CE->getArg(Num), CE, state, Hold, ReleasedAllocated);
+ return FreeMemAux(C, CE->getArg(Num), CE, state, Hold,
+ ReleasedAllocated, ReturnsNullOnFailure);
+}
+
+/// Checks if the previous call to free on the given symbol failed - if free
+/// failed, returns true. Also, returns the corresponding return value symbol.
+bool didPreviousFreeFail(ProgramStateRef State,
+ SymbolRef Sym, SymbolRef &RetStatusSymbol) {
+ const SymbolRef *Ret = State->get<FreeReturnValue>(Sym);
+ if (Ret) {
+ assert(*Ret && "We should not store the null return symbol");
+ ConstraintManager &CMgr = State->getConstraintManager();
+ ConditionTruthVal FreeFailed = CMgr.isNull(State, *Ret);
+ RetStatusSymbol = *Ret;
+ return FreeFailed.isConstrainedTrue();
+ }
+ return false;
}
ProgramStateRef MallocChecker::FreeMemAux(CheckerContext &C,
const Expr *ArgExpr,
const Expr *ParentExpr,
- ProgramStateRef state,
+ ProgramStateRef State,
bool Hold,
- bool &ReleasedAllocated) const {
+ bool &ReleasedAllocated,
+ bool ReturnsNullOnFailure) const {
- SVal ArgVal = state->getSVal(ArgExpr, C.getLocationContext());
+ SVal ArgVal = State->getSVal(ArgExpr, C.getLocationContext());
if (!isa<DefinedOrUnknownSVal>(ArgVal))
return 0;
DefinedOrUnknownSVal location = cast<DefinedOrUnknownSVal>(ArgVal);
@@ -634,7 +661,7 @@ ProgramStateRef MallocChecker::FreeMemAux(CheckerContext &C,
// The explicit NULL case, no operation is performed.
ProgramStateRef notNullState, nullState;
- llvm::tie(notNullState, nullState) = state->assume(location);
+ llvm::tie(notNullState, nullState) = State->assume(location);
if (nullState && !notNullState)
return 0;
@@ -683,10 +710,14 @@ ProgramStateRef MallocChecker::FreeMemAux(CheckerContext &C,
return 0;
SymbolRef Sym = SR->getSymbol();
- const RefState *RS = state->get<RegionState>(Sym);
+ const RefState *RS = State->get<RegionState>(Sym);
+ SymbolRef PreviousRetStatusSymbol = 0;
// Check double free.
- if (RS && (RS->isReleased() || RS->isRelinquished())) {
+ if (RS &&
+ (RS->isReleased() || RS->isRelinquished()) &&
+ !didPreviousFreeFail(State, Sym, PreviousRetStatusSymbol)) {
+
if (ExplodedNode *N = C.generateSink()) {
if (!BT_DoubleFree)
BT_DoubleFree.reset(
@@ -696,6 +727,8 @@ ProgramStateRef MallocChecker::FreeMemAux(CheckerContext &C,
"Attempt to free non-owned memory"), N);
R->addRange(ArgExpr->getSourceRange());
R->markInteresting(Sym);
+ if (PreviousRetStatusSymbol)
+ R->markInteresting(PreviousRetStatusSymbol);
R->addVisitor(new MallocBugVisitor(Sym));
C.emitReport(R);
}
@@ -704,10 +737,24 @@ ProgramStateRef MallocChecker::FreeMemAux(CheckerContext &C,
ReleasedAllocated = (RS != 0);
+ // Clean out the info on previous call to free return info.
+ State = State->remove<FreeReturnValue>(Sym);
+
+ // Keep track of the return value. If it is NULL, we will know that free
+ // failed.
+ if (ReturnsNullOnFailure) {
+ SVal RetVal = C.getSVal(ParentExpr);
+ SymbolRef RetStatusSymbol = RetVal.getAsSymbol();
+ if (RetStatusSymbol) {
+ C.getSymbolManager().addSymbolDependency(Sym, RetStatusSymbol);
+ State = State->set<FreeReturnValue>(Sym, RetStatusSymbol);
+ }
+ }
+
// Normal free.
if (Hold)
- return state->set<RegionState>(Sym, RefState::getRelinquished(ParentExpr));
- return state->set<RegionState>(Sym, RefState::getReleased(ParentExpr));
+ return State->set<RegionState>(Sym, RefState::getRelinquished(ParentExpr));
+ return State->set<RegionState>(Sym, RefState::getReleased(ParentExpr));
}
bool MallocChecker::SummarizeValue(raw_ostream &os, SVal V) {
@@ -1064,6 +1111,15 @@ void MallocChecker::checkDeadSymbols(SymbolReaper &SymReaper,
}
}
+ // Cleanup the FreeReturnValue Map.
+ FreeReturnValueTy FR = state->get<FreeReturnValue>();
+ for (FreeReturnValueTy::iterator I = FR.begin(), E = FR.end(); I != E; ++I) {
+ if (SymReaper.isDead(I->first) ||
+ SymReaper.isDead(I->second)) {
+ state = state->remove<FreeReturnValue>(I->first);
+ }
+ }
+
// Generate leak node.
ExplodedNode *N = C.getPredecessor();
if (!Errors.empty()) {
diff --git a/test/Analysis/malloc.mm b/test/Analysis/malloc.mm
index b5a1aeb12b..c92c966459 100644
--- a/test/Analysis/malloc.mm
+++ b/test/Analysis/malloc.mm
@@ -222,3 +222,59 @@ void noCrashOnVariableArgumentSelector() {
NSMutableString *myString = [NSMutableString stringWithString:@"some text"];
[myString appendFormat:@"some text = %d", 3];
}
+
+void test12365078_check() {
+ unichar *characters = (unichar*)malloc(12);
+ NSString *string = [[NSString alloc] initWithCharactersNoCopy:characters length:12 freeWhenDone:1];
+ if (!string) free(characters); // no-warning
+}
+
+void test12365078_nocheck() {
+ unichar *characters = (unichar*)malloc(12);
+ NSString *string = [[NSString alloc] initWithCharactersNoCopy:characters length:12 freeWhenDone:1];
+}
+
+void test12365078_false_negative() {
+ unichar *characters = (unichar*)malloc(12);
+ NSString *string = [[NSString alloc] initWithCharactersNoCopy:characters length:12 freeWhenDone:1];
+ if (!string) {;}
+}
+
+void test12365078_no_malloc(unichar *characters) {
+ NSString *string = [[NSString alloc] initWithCharactersNoCopy:characters length:12 freeWhenDone:1];
+ if (!string) {free(characters);}
+}
+
+NSString *test12365078_no_malloc_returnValue(unichar *characters) {
+ NSString *string = [[NSString alloc] initWithCharactersNoCopy:characters length:12 freeWhenDone:1];
+ if (!string) {
+ return 0; // no-warning
+ }
+ return string;
+}
+
+void test12365078_nocheck_nomalloc(unichar *characters) {
+ NSString *string = [[NSString alloc] initWithCharactersNoCopy:characters length:12 freeWhenDone:1];
+ free(characters); // expected-warning {{Attempt to free non-owned memory}}
+}
+
+void test12365078_nested(unichar *characters) {
+ NSString *string = [[NSString alloc] initWithCharactersNoCopy:characters length:12 freeWhenDone:1];
+ if (!string) {
+ NSString *string2 = [[NSString alloc] initWithCharactersNoCopy:characters length:12 freeWhenDone:1];
+ if (!string2) {
+ NSString *string3 = [[NSString alloc] initWithCharactersNoCopy:characters length:12 freeWhenDone:1];
+ if (!string3) {
+ NSString *string4 = [[NSString alloc] initWithCharactersNoCopy:characters length:12 freeWhenDone:1];
+ if (!string4)
+ free(characters);
+ }
+ }
+ }
+}
+
+void test12365078_check_positive() {
+ unichar *characters = (unichar*)malloc(12);
+ NSString *string = [[NSString alloc] initWithCharactersNoCopy:characters length:12 freeWhenDone:1];
+ if (string) free(characters); // expected-warning{{Attempt to free non-owned memory}}
+}
diff --git a/test/CodeGen/ppc-atomics.c b/test/CodeGen/ppc-atomics.c
new file mode 100644
index 0000000000..3fcb0fbec9
--- /dev/null
+++ b/test/CodeGen/ppc-atomics.c
@@ -0,0 +1,35 @@
+// RUN: %clang_cc1 -triple powerpc-linux-gnu -emit-llvm %s -o - | FileCheck %s -check-prefix=32
+// RUN: %clang_cc1 -triple powerpc64-linux-gnu -emit-llvm %s -o - | FileCheck %s -check-prefix=64
+
+unsigned char c1, c2;
+unsigned short s1, s2;
+unsigned int i1, i2;
+unsigned long long ll1, ll2;
+
+enum memory_order {
+ memory_order_relaxed,
+ memory_order_consume,
+ memory_order_acquire,
+ memory_order_release,
+ memory_order_acq_rel,
+ memory_order_seq_cst
+};
+
+void test1(void) {
+ (void)__atomic_load(&c1, &c2, memory_order_seq_cst);
+ (void)__atomic_load(&s1, &s2, memory_order_seq_cst);
+ (void)__atomic_load(&i1, &i2, memory_order_seq_cst);
+ (void)__atomic_load(&ll1, &ll2, memory_order_seq_cst);
+
+// 32: define void @test1
+// 32: load atomic i8* @c1 seq_cst
+// 32: load atomic i16* @s1 seq_cst
+// 32: load atomic i32* @i1 seq_cst
+// 32: call void @__atomic_load(i32 8, i8* bitcast (i64* @ll1 to i8*)
+
+// 64: define void @test1
+// 64: load atomic i8* @c1 seq_cst
+// 64: load atomic i16* @s1 seq_cst
+// 64: load atomic i32* @i1 seq_cst
+// 64: load atomic i64* @ll1 seq_cst
+}
diff --git a/test/CodeGenCXX/cxx11-special-members.cpp b/test/CodeGenCXX/cxx11-special-members.cpp
new file mode 100644
index 0000000000..59461f9e2f
--- /dev/null
+++ b/test/CodeGenCXX/cxx11-special-members.cpp
@@ -0,0 +1,32 @@
+// RUN: %clang_cc1 %s -std=c++11 -emit-llvm -o - -triple=i686-linux-gnu | FileCheck %s
+
+struct A {
+ A(const A&);
+ A &operator=(const A&);
+};
+
+struct B {
+ A a;
+ B(B&&) = default;
+ B &operator=(B&&) = default;
+};
+
+// CHECK: define {{.*}} @_Z2f1
+void f1(B &x) {
+ // CHECK-NOT: memcpy
+ // CHECK: call {{.*}} @_ZN1BC1EOS_(
+ B b(static_cast<B&&>(x));
+}
+
+// CHECK: define {{.*}} @_Z2f2
+void f2(B &x, B &y) {
+ // CHECK-NOT: memcpy
+ // CHECK: call {{.*}} @_ZN1BaSEOS_(
+ x = static_cast<B&&>(y);
+}
+
+// CHECK: define {{.*}} @_ZN1BaSEOS_(
+// CHECK: call {{.*}} @_ZN1AaSERKS_(
+
+// CHECK: define {{.*}} @_ZN1BC2EOS_(
+// CHECK: call {{.*}} @_ZN1AC1ERKS_(
diff --git a/test/CodeGenObjCXX/property-objects.mm b/test/CodeGenObjCXX/property-objects.mm
index 6dfcc27f19..a3c2ed3746 100644
--- a/test/CodeGenObjCXX/property-objects.mm
+++ b/test/CodeGenObjCXX/property-objects.mm
@@ -1,8 +1,4 @@
// RUN: %clang_cc1 %s -triple=x86_64-apple-darwin10 -emit-llvm -o - | FileCheck %s
-// CHECK-NOT: callq _objc_msgSend_stret
-// CHECK: call void @_ZN1SC1ERKS_
-// CHECK: call %class.S* @_ZN1SaSERKS_
-// CHECK: call %struct.CGRect* @_ZN6CGRectaSERKS_
class S {
public:
@@ -19,13 +15,14 @@ struct CGRect {
S position;
CGRect bounds;
}
+
@property(assign, nonatomic) S position;
@property CGRect bounds;
@property CGRect frame;
- (void)setFrame:(CGRect)frameRect;
- (CGRect)frame;
- (void) initWithOwner;
-- (struct CGRect)extent;
+- (CGRect)extent;
- (void)dealloc;
@end
@@ -33,6 +30,11 @@ struct CGRect {
@synthesize position;
@synthesize bounds;
@synthesize frame;
+
+// CHECK: define internal void @"\01-[I setPosition:]"
+// CHECK: call %class.S* @_ZN1SaSERKS_
+// CHECK-NEXT: ret void
+
- (void)setFrame:(CGRect)frameRect {}
- (CGRect)frame {return bounds;}
@@ -42,14 +44,20 @@ struct CGRect {
labelLayerFrame = self.bounds;
_labelLayer.frame = labelLayerFrame;
}
+
// rdar://8366604
- (void)dealloc
{
CGRect cgrect = self.extent;
}
- (struct CGRect)extent {return bounds;}
+
@end
+// CHECK: define i32 @main
+// CHECK: call void @_ZN1SC1ERKS_(%class.S* [[AGGTMP:%[a-zA-Z0-9\.]+]], %class.S* {{%[a-zA-Z0-9\.]+}})
+// CHECK: call void bitcast (i8* (i8*, i8*, ...)* @objc_msgSend to void (i8*, i8*, %class.S*)*)(i8* {{%[a-zA-Z0-9\.]+}}, i8* {{%[a-zA-Z0-9\.]+}}, %class.S* [[AGGTMP]])
+// CHECK-NEXT: ret i32 0
int main() {
I *i;
S s1;
@@ -59,7 +67,9 @@ int main() {
// rdar://8379892
// CHECK: define void @_Z1fP1A
-// CHECK: @objc_msgSend to void
+// CHECK: call void @_ZN1XC1Ev(%struct.X* [[LVTEMP:%[a-zA-Z0-9\.]+]])
+// CHECK: call void @_ZN1XC1ERKS_(%struct.X* [[AGGTMP:%[a-zA-Z0-9\.]+]], %struct.X* [[LVTEMP]])
+// CHECK: call void bitcast (i8* (i8*, i8*, ...)* @objc_msgSend to void (i8*, i8*, %struct.X*)*)({{.*}} %struct.X* [[AGGTMP]])
struct X {
X();
X(const X&);
diff --git a/test/SemaObjCXX/properties.mm b/test/SemaObjCXX/properties.mm
index 3c6b138586..0783eebc11 100644
--- a/test/SemaObjCXX/properties.mm
+++ b/test/SemaObjCXX/properties.mm
@@ -106,3 +106,26 @@ void test7(Test7 *ptr) {
delete ptr.implicit_struct_property;
delete ptr.explicit_struct_property;
}
+
+// Make sure the returned value from property assignment is void,
+// because there isn't any other viable way to handle it for
+// non-trivial classes.
+class NonTrivial1 {
+public:
+ ~NonTrivial1();
+};
+class NonTrivial2 {
+public:
+ NonTrivial2();
+ NonTrivial2(const NonTrivial2&);
+};
+@interface TestNonTrivial
+@property(assign, nonatomic) NonTrivial1 p1;
+@property(assign, nonatomic) NonTrivial2 p2;
+@end
+TestNonTrivial *TestNonTrivialObj;
+
+extern void* VoidType;
+extern decltype(TestNonTrivialObj.p1 = NonTrivial1())* VoidType;
+extern decltype(TestNonTrivialObj.p2 = NonTrivial2())* VoidType;
+