// These are possible in C++17 due to mandatory copy elision.
isa<ReturnedValueConstructionContext>(C) ||
isa<VariableConstructionContext>(C) ||
- isa<ConstructorInitializerConstructionContext>(C)));
+ isa<ConstructorInitializerConstructionContext>(C) ||
+ isa<ArgumentConstructionContext>(C)));
Data2.setPointer(const_cast<ConstructionContext *>(C));
}
/// new-expression triggers construction of the newly allocated object(s).
TriggerTy Trigger;
+ /// If a single trigger statement triggers multiple constructors, they are
+ /// usually being enumerated. This covers function argument constructors
+ /// triggered by a call-expression and items in an initializer list triggered
+ /// by an init-list-expression.
+ unsigned Index;
+
/// Sometimes a single trigger is not enough to describe the construction
/// site. In this case we'd have a chain of "partial" construction context
/// layers.
/// Not all of these are currently supported.
const ConstructionContextLayer *Parent = nullptr;
- ConstructionContextLayer(TriggerTy Trigger,
- const ConstructionContextLayer *Parent)
- : Trigger(Trigger), Parent(Parent) {}
+ ConstructionContextLayer(TriggerTy Trigger, unsigned Index,
+ const ConstructionContextLayer *Parent)
+ : Trigger(Trigger), Index(Index), Parent(Parent) {}
public:
static const ConstructionContextLayer *
- create(BumpVectorContext &C, TriggerTy Trigger,
+ create(BumpVectorContext &C, TriggerTy Trigger, unsigned Index = 0,
const ConstructionContextLayer *Parent = nullptr);
const ConstructionContextLayer *getParent() const { return Parent; }
return Trigger.dyn_cast<CXXCtorInitializer *>();
}
+ unsigned getIndex() const { return Index; }
+
/// Returns true if these layers are equal as individual layers, even if
/// their parents are different.
bool isSameLayer(const ConstructionContextLayer *Other) const {
assert(Other);
- return (Trigger == Other->Trigger);
+ return (Trigger == Other->Trigger && Index == Other->Index);
}
/// See if Other is a proper initial segment of this construction context
SimpleReturnedValueKind,
CXX17ElidedCopyReturnedValueKind,
RETURNED_VALUE_BEGIN = SimpleReturnedValueKind,
- RETURNED_VALUE_END = CXX17ElidedCopyReturnedValueKind
+ RETURNED_VALUE_END = CXX17ElidedCopyReturnedValueKind,
+ ArgumentKind
};
protected:
}
};
+class ArgumentConstructionContext : public ConstructionContext {
+ const Expr *CE; // The call of which the context is an argument.
+ unsigned Index; // Which argument we're constructing.
+ const CXXBindTemporaryExpr *BTE; // Whether the object needs to be destroyed.
+
+ friend class ConstructionContext; // Allows to create<>() itself.
+
+ explicit ArgumentConstructionContext(const Expr *CE, unsigned Index,
+ const CXXBindTemporaryExpr *BTE)
+ : ConstructionContext(ArgumentKind), CE(CE),
+ Index(Index), BTE(BTE) {
+ assert(isa<CallExpr>(CE) || isa<CXXConstructExpr>(CE) ||
+ isa<ObjCMessageExpr>(CE));
+ // BTE is optional.
+ }
+
+public:
+ const Expr *getCallLikeExpr() const { return CE; }
+ unsigned getIndex() const { return Index; }
+ const CXXBindTemporaryExpr *getCXXBindTemporaryExpr() const { return BTE; }
+
+ static bool classof(const ConstructionContext *CC) {
+ return CC->getKind() == ArgumentKind;
+ }
+};
+
} // end namespace clang
#endif // LLVM_CLANG_ANALYSIS_CONSTRUCTIONCONTEXT_H
std::is_same<CallLikeExpr, CXXConstructExpr>::value ||
std::is_same<CallLikeExpr, ObjCMessageExpr>::value>>
void findConstructionContextsForArguments(CallLikeExpr *E) {
- // A stub for the code that'll eventually be used for finding construction
- // contexts for constructors of C++ object-type arguments passed into
- // call-like expression E.
- // FIXME: Once actually implemented, this construction context layer should
- // include the index of the argument as well.
- for (auto Arg : E->arguments())
+ for (unsigned i = 0, e = E->getNumArgs(); i != e; ++i) {
+ Expr *Arg = E->getArg(i);
if (Arg->getType()->getAsCXXRecordDecl() && !Arg->isGLValue())
findConstructionContexts(
- ConstructionContextLayer::create(cfg->getBumpVectorContext(), E),
+ ConstructionContextLayer::create(cfg->getBumpVectorContext(), E, i),
Arg);
+ }
}
// Unset the construction context after consuming it. This is done immediately
if (!Child)
return;
- auto withExtraLayer = [this, Layer](Stmt *S) {
+ auto withExtraLayer = [this, Layer](Stmt *S, unsigned Index = 0) {
return ConstructionContextLayer::create(cfg->getBumpVectorContext(), S,
- Layer);
+ Index, Layer);
};
switch(Child->getStmtClass()) {
OS << ", ";
const auto *SICC = cast<SimpleConstructorInitializerConstructionContext>(CC);
print_initializer(OS, Helper, SICC->getCXXCtorInitializer());
- break;
+ return;
}
case ConstructionContext::CXX17ElidedCopyConstructorInitializerKind: {
OS << ", ";
Stmts.push_back(TOCC->getConstructorAfterElision());
break;
}
+ case ConstructionContext::ArgumentKind: {
+ const auto *ACC = cast<ArgumentConstructionContext>(CC);
+ if (const Stmt *BTE = ACC->getCXXBindTemporaryExpr()) {
+ OS << ", ";
+ Helper.handledStmt(const_cast<Stmt *>(BTE), OS);
+ }
+ OS << ", ";
+ Helper.handledStmt(const_cast<Expr *>(ACC->getCallLikeExpr()), OS);
+ OS << "+" << ACC->getIndex();
+ return;
+ }
}
for (auto I: Stmts)
if (I) {
const ConstructionContextLayer *
ConstructionContextLayer::create(BumpVectorContext &C, TriggerTy Trigger,
+ unsigned Index,
const ConstructionContextLayer *Parent) {
ConstructionContextLayer *CC =
C.getAllocator().Allocate<ConstructionContextLayer>();
- return new (CC) ConstructionContextLayer(Trigger, Parent);
+ return new (CC) ConstructionContextLayer(Trigger, Index, Parent);
}
bool ConstructionContextLayer::isStrictlyMoreSpecificThan(
}
assert(ParentLayer->isLast());
- // This is a constructor into a function argument. Not implemented yet.
+ // This is a constructor into a function argument.
if (isa<CallExpr>(ParentLayer->getTriggerStmt()) ||
isa<CXXConstructExpr>(ParentLayer->getTriggerStmt()) ||
- isa<ObjCMessageExpr>(ParentLayer->getTriggerStmt()))
- return nullptr;
+ isa<ObjCMessageExpr>(ParentLayer->getTriggerStmt())) {
+ return create<ArgumentConstructionContext>(
+ C, cast<Expr>(ParentLayer->getTriggerStmt()),
+ ParentLayer->getIndex(), BTE);
+ }
// This is C++17 copy-elided construction into return statement.
if (auto *RS = dyn_cast<ReturnStmt>(ParentLayer->getTriggerStmt())) {
assert(!RS->getRetValue()->getType().getCanonicalType()
assert(TopLayer->isLast());
return create<SimpleReturnedValueConstructionContext>(C, RS);
}
- // This is a constructor into a function argument. Not implemented yet.
+ // This is a constructor into a function argument.
if (isa<CallExpr>(TopLayer->getTriggerStmt()) ||
isa<CXXConstructExpr>(TopLayer->getTriggerStmt()) ||
- isa<ObjCMessageExpr>(TopLayer->getTriggerStmt()))
- return nullptr;
+ isa<ObjCMessageExpr>(TopLayer->getTriggerStmt())) {
+ assert(TopLayer->isLast());
+ return create<ArgumentConstructionContext>(
+ C, cast<Expr>(TopLayer->getTriggerStmt()), TopLayer->getIndex(),
+ /*BTE=*/nullptr);
+ }
llvm_unreachable("Unexpected construction context with statement!");
} else if (const CXXCtorInitializer *I = TopLayer->getTriggerInit()) {
assert(TopLayer->isLast());
// and construct directly into the final object. This call
// also sets the CallOpts flags for us.
SVal V;
+ // If the elided copy/move constructor is not supported, there's still
+ // benefit in trying to model the non-elided constructor.
+ // Stash our state before trying to elide, as it'll get overwritten.
+ ProgramStateRef PreElideState = State;
+ EvalCallOptions PreElideCallOpts = CallOpts;
+
std::tie(State, V) = prepareForObjectConstruction(
CE, State, LCtx, TCC->getConstructionContextAfterElision(), CallOpts);
- // Remember that we've elided the constructor.
- State = addObjectUnderConstruction(State, CE, LCtx, V);
+ // FIXME: This definition of "copy elision has not failed" is unreliable.
+ // It doesn't indicate that the constructor will actually be inlined
+ // later; it is still up to evalCall() to decide.
+ if (!CallOpts.IsCtorOrDtorWithImproperlyModeledTargetRegion) {
+ // Remember that we've elided the constructor.
+ State = addObjectUnderConstruction(State, CE, LCtx, V);
- // Remember that we've elided the destructor.
- if (BTE)
- State = elideDestructor(State, BTE, LCtx);
+ // Remember that we've elided the destructor.
+ if (BTE)
+ State = elideDestructor(State, BTE, LCtx);
- // Instead of materialization, shamelessly return
- // the final object destination.
- if (MTE)
- State = addObjectUnderConstruction(State, MTE, LCtx, V);
+ // Instead of materialization, shamelessly return
+ // the final object destination.
+ if (MTE)
+ State = addObjectUnderConstruction(State, MTE, LCtx, V);
- return std::make_pair(State, V);
+ return std::make_pair(State, V);
+ } else {
+ // Copy elision failed. Revert the changes and proceed as if we have
+ // a simple temporary.
+ State = PreElideState;
+ CallOpts = PreElideCallOpts;
+ }
+ // FALL-THROUGH
}
case ConstructionContext::SimpleTemporaryObjectKind: {
- const auto *TCC = cast<SimpleTemporaryObjectConstructionContext>(CC);
+ const auto *TCC = cast<TemporaryObjectConstructionContext>(CC);
const CXXBindTemporaryExpr *BTE = TCC->getCXXBindTemporaryExpr();
const MaterializeTemporaryExpr *MTE = TCC->getMaterializedTemporaryExpr();
SVal V = UnknownVal();
CallOpts.IsTemporaryCtorOrDtor = true;
return std::make_pair(State, V);
}
+ case ConstructionContext::ArgumentKind: {
+ // Function argument constructors. Not implemented yet.
+ break;
+ }
}
}
// If we couldn't find an existing region to construct into, assume we're
void useCByReference(const C &c);
void useD(D d);
void useDByReference(const D &d);
+void useCAndD(C c, D d);
-// FIXME: Find construction context for the argument.
// CHECK: void passArgument()
// CHECK: 1: useC
// CHECK-NEXT: 2: [B1.1] (ImplicitCastExpr, FunctionToPointerDecay, void (*)(class C))
-// CXX11-NEXT: 3: C() (CXXConstructExpr, [B1.4], class C)
+// CXX11-ELIDE-NEXT: 3: C() (CXXConstructExpr, [B1.4], [B1.5], class C)
+// CXX11-NOELIDE-NEXT: 3: C() (CXXConstructExpr, [B1.4], class C)
// CXX11-NEXT: 4: [B1.3]
-// CXX11-NEXT: 5: [B1.4] (CXXConstructExpr, class C)
+// CXX11-NEXT: 5: [B1.4] (CXXConstructExpr, [B1.6]+0, class C)
// CXX11-NEXT: 6: [B1.2]([B1.5])
-// CXX17-NEXT: 3: C() (CXXConstructExpr, class C)
+// CXX17-NEXT: 3: C() (CXXConstructExpr, [B1.4]+0, class C)
// CXX17-NEXT: 4: [B1.2]([B1.3])
void passArgument() {
useC(C());
}
+// CHECK: void passTwoArguments()
+// CHECK: [B1]
+// CHECK-NEXT: 1: useCAndD
+// CHECK-NEXT: 2: [B1.1] (ImplicitCastExpr, FunctionToPointerDecay, void (*)(class C, class argument_constructors::D))
+// CXX11-ELIDE-NEXT: 3: C() (CXXConstructExpr, [B1.4], [B1.5], class C)
+// CXX11-NOELIDE-NEXT: 3: C() (CXXConstructExpr, [B1.4], class C)
+// CXX11-NEXT: 4: [B1.3]
+// CXX11-NEXT: 5: [B1.4] (CXXConstructExpr, [B1.12]+0, class C)
+// CXX11-ELIDE-NEXT: 6: argument_constructors::D() (CXXConstructExpr, [B1.7], [B1.9], [B1.10], class argument_constructors::D)
+// CXX11-NOELIDE-NEXT: 6: argument_constructors::D() (CXXConstructExpr, [B1.7], [B1.9], class argument_constructors::D)
+// CXX11-NEXT: 7: [B1.6] (BindTemporary)
+// CXX11-NEXT: 8: [B1.7] (ImplicitCastExpr, NoOp, const class argument_constructors::D)
+// CXX11-NEXT: 9: [B1.8]
+// CXX11-NEXT: 10: [B1.9] (CXXConstructExpr, [B1.11], [B1.12]+1, class argument_constructors::D)
+// CXX11-NEXT: 11: [B1.10] (BindTemporary)
+// CXX11-NEXT: 12: [B1.2]([B1.5], [B1.11])
+// CXX11-NEXT: 13: ~argument_constructors::D() (Temporary object destructor)
+// CXX11-NEXT: 14: ~argument_constructors::D() (Temporary object destructor)
+// CXX17-NEXT: 3: C() (CXXConstructExpr, [B1.6]+0, class C)
+// CXX17-NEXT: 4: argument_constructors::D() (CXXConstructExpr, [B1.5], [B1.6]+1, class argument_co
+// CXX17-NEXT: 5: [B1.4] (BindTemporary)
+// CXX17-NEXT: 6: [B1.2]([B1.3], [B1.5])
+// CXX17-NEXT: 7: ~argument_constructors::D() (Temporary object destructor)
+void passTwoArguments() {
+ useCAndD(C(), D());
+}
+
// CHECK: void passArgumentByReference()
// CHECK: 1: useCByReference
// CHECK-NEXT: 2: [B1.1] (ImplicitCastExpr, FunctionToPointerDecay, void (*)(const class C &))
useCByReference(C());
}
-// FIXME: Find construction context for the argument.
// CHECK: void passArgumentWithDestructor()
// CHECK: 1: useD
// CHECK-NEXT: 2: [B1.1] (ImplicitCastExpr, FunctionToPointerDecay, void (*)(class argument_constructors::D))
-// CXX11-NEXT: 3: argument_constructors::D() (CXXConstructExpr, [B1.4], [B1.6], class argument_constructors::D)
+// CXX11-ELIDE-NEXT: 3: argument_constructors::D() (CXXConstructExpr, [B1.4], [B1.6], [B1.7], class argument_constructors::D)
+// CXX11-NOELIDE-NEXT: 3: argument_constructors::D() (CXXConstructExpr, [B1.4], [B1.6], class argument_constructors::D)
// CXX11-NEXT: 4: [B1.3] (BindTemporary)
// CXX11-NEXT: 5: [B1.4] (ImplicitCastExpr, NoOp, const class argument_constructors::D)
// CXX11-NEXT: 6: [B1.5]
-// CXX11-NEXT: 7: [B1.6] (CXXConstructExpr, class argument_constructors::D)
+// CXX11-NEXT: 7: [B1.6] (CXXConstructExpr, [B1.8], [B1.9]+0, class argument_constructors::D)
// CXX11-NEXT: 8: [B1.7] (BindTemporary)
// CXX11-NEXT: 9: [B1.2]([B1.8])
// CXX11-NEXT: 10: ~argument_constructors::D() (Temporary object destructor)
// CXX11-NEXT: 11: ~argument_constructors::D() (Temporary object destructor)
-// CXX17-NEXT: 3: argument_constructors::D() (CXXConstructExpr, class argument_constructors::D)
+// CXX17-NEXT: 3: argument_constructors::D() (CXXConstructExpr, [B1.4], [B1.5]+0, class argument_constructors::D)
// CXX17-NEXT: 4: [B1.3] (BindTemporary)
// CXX17-NEXT: 5: [B1.2]([B1.4])
// CXX17-NEXT: 6: ~argument_constructors::D() (Temporary object destructor)
useDByReference(D());
}
-// FIXME: Find construction context for the argument.
// CHECK: void passArgumentIntoAnotherConstructor()
-// CXX11: 1: argument_constructors::D() (CXXConstructExpr, [B1.2], [B1.4], class argument_constructors::D)
+// CXX11-ELIDE: 1: argument_constructors::D() (CXXConstructExpr, [B1.2], [B1.4], [B1.5], class argument_constructors::D)
+// CXX11-NOELIDE: 1: argument_constructors::D() (CXXConstructExpr, [B1.2], [B1.4], class argument_constructors::D)
// CXX11-NEXT: 2: [B1.1] (BindTemporary)
// CXX11-NEXT: 3: [B1.2] (ImplicitCastExpr, NoOp, const class argument_constructors::D)
// CXX11-NEXT: 4: [B1.3]
-// CXX11-NEXT: 5: [B1.4] (CXXConstructExpr, class argument_constructors::D)
+// CXX11-NEXT: 5: [B1.4] (CXXConstructExpr, [B1.6], [B1.7]+0, class argument_constructors::D)
// CXX11-NEXT: 6: [B1.5] (BindTemporary)
// CXX11-ELIDE-NEXT: 7: [B1.6] (CXXConstructExpr, [B1.9], [B1.10], class argument_constructors::E)
// CXX11-NOELIDE-NEXT: 7: [B1.6] (CXXConstructExpr, [B1.9], class argument_constructors::E)
// CXX11-NEXT: 11: argument_constructors::E e = argument_constructors::E(argument_constructors::D());
// CXX11-NEXT: 12: ~argument_constructors::D() (Temporary object destructor)
// CXX11-NEXT: 13: ~argument_constructors::D() (Temporary object destructor)
-// CXX17: 1: argument_constructors::D() (CXXConstructExpr, class argument_constructors::D)
+// CXX17: 1: argument_constructors::D() (CXXConstructExpr, [B1.2], [B1.3]+0, class argument_constructors::D)
// CXX17-NEXT: 2: [B1.1] (BindTemporary)
// CXX17-NEXT: 3: [B1.2] (CXXConstructExpr, [B1.5], class argument_constructors::E)
// CXX17-NEXT: 4: argument_constructors::E([B1.3]) (CXXFunctionalCastExpr, ConstructorConversion, class argument_constructors::E)
-(D) bar;
@end
-// FIXME: Find construction context for the argument.
// CHECK: void passArgumentIntoMessage(E *e)
// CHECK: 1: e
// CHECK-NEXT: 2: [B1.1] (ImplicitCastExpr, LValueToRValue, E *)
-// CXX11-NEXT: 3: D() (CXXConstructExpr, [B1.4], [B1.6], class D)
+// CXX11-ELIDE-NEXT: 3: D() (CXXConstructExpr, [B1.4], [B1.6], [B1.7], class D)
+// CXX11-NOELIDE-NEXT: 3: D() (CXXConstructExpr, [B1.4], [B1.6], class D)
// CXX11-NEXT: 4: [B1.3] (BindTemporary)
// CXX11-NEXT: 5: [B1.4] (ImplicitCastExpr, NoOp, const class D)
// CXX11-NEXT: 6: [B1.5]
-// CXX11-NEXT: 7: [B1.6] (CXXConstructExpr, class D)
+// CXX11-NEXT: 7: [B1.6] (CXXConstructExpr, [B1.8], [B1.9]+0, class D)
// CXX11-NEXT: 8: [B1.7] (BindTemporary)
// Double brackets trigger FileCheck variables, escape.
// CXX11-NEXT: 9: {{\[}}[B1.2] foo:[B1.8]]
// CXX11-NEXT: 10: ~D() (Temporary object destructor)
// CXX11-NEXT: 11: ~D() (Temporary object destructor)
-// CXX17-NEXT: 3: D() (CXXConstructExpr, class D)
+// CXX17-NEXT: 3: D() (CXXConstructExpr, [B1.4], [B1.5]+0, class D)
// CXX17-NEXT: 4: [B1.3] (BindTemporary)
// Double brackets trigger FileCheck variables, escape.
// CXX17-NEXT: 5: {{\[}}[B1.2] foo:[B1.4]]