From 6a32c0589cd2b60bf599b8aede33c484ec359e7a Mon Sep 17 00:00:00 2001 From: Richard Smith Date: Mon, 23 Jul 2018 21:21:24 +0000 Subject: [PATCH] Do not try to perform lifetime-extension through conditional expressions. CodeGen can't cope with that yet. Instead, produce a "not supported" warning for now and don't extend lifetime. llvm-svn: 337744 --- clang/include/clang/Basic/DiagnosticSemaKinds.td | 15 +++--- clang/lib/Sema/SemaInit.cpp | 59 ++++++++++++++++-------- clang/test/SemaCXX/conditional-expr.cpp | 22 ++++++++- 3 files changed, 69 insertions(+), 27 deletions(-) diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index 0d000e7..9877093 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -7895,13 +7895,16 @@ def warn_new_dangling_initializer_list : Warning< "the allocated initializer list}0 " "will be destroyed at the end of the full-expression">, InGroup; -def warn_default_member_init_temporary_not_extended : Warning< - "sorry, lifetime extension of temporary created by aggregate initialization " - "using default member initializer is not supported; lifetime of temporary " +def warn_unsupported_temporary_not_extended : Warning< + "sorry, lifetime extension of temporary created " + "%select{by aggregate initialization using default member initializer|" + "within conditional expression}0 " + "is not supported; lifetime of temporary " "will end at the end of the full-expression">, InGroup; -def warn_default_member_init_init_list_not_extended : Warning< - "sorry, lifetime extension of backing array of initializer list " - "created by aggregate initialization using default member initializer " +def warn_unsupported_init_list_not_extended : Warning< + "sorry, lifetime extension of backing array of initializer list created " + "%select{by aggregate initialization using default member initializer|" + "within conditional expression}0 " "is not supported; lifetime of backing array will end at the end of the " "full-expression">, InGroup; diff --git a/clang/lib/Sema/SemaInit.cpp b/clang/lib/Sema/SemaInit.cpp index 5c891be..208576e 100644 --- a/clang/lib/Sema/SemaInit.cpp +++ b/clang/lib/Sema/SemaInit.cpp @@ -6362,6 +6362,7 @@ using Local = Expr*; struct IndirectLocalPathEntry { enum EntryKind { DefaultInit, + Conditional, AddressOf, VarInit, LValToRVal, @@ -6497,6 +6498,7 @@ static void visitLocalsRetainedByReferenceBinding(IndirectLocalPath &Path, case Stmt::ConditionalOperatorClass: case Stmt::BinaryConditionalOperatorClass: { + Path.push_back({IndirectLocalPathEntry::Conditional, Init}); auto *C = cast(Init); if (!C->getTrueExpr()->getType()->isVoidType()) visitLocalsRetainedByReferenceBinding(Path, C->getTrueExpr(), RK, Visit); @@ -6683,6 +6685,7 @@ static void visitLocalsRetainedByInitializer(IndirectLocalPath &Path, case Stmt::ConditionalOperatorClass: case Stmt::BinaryConditionalOperatorClass: { + Path.push_back({IndirectLocalPathEntry::Conditional, Init}); auto *C = cast(Init); // In C++, we can have a throw-expression operand, which has 'void' type // and isn't interesting from a lifetime perspective. @@ -6714,12 +6717,33 @@ static void visitLocalsRetainedByInitializer(IndirectLocalPath &Path, /// supposed to lifetime-extend along (but don't). static bool shouldLifetimeExtendThroughPath(const IndirectLocalPath &Path) { for (auto Elem : Path) { - if (Elem.Kind != IndirectLocalPathEntry::DefaultInit) + if (Elem.Kind != IndirectLocalPathEntry::DefaultInit && + Elem.Kind != IndirectLocalPathEntry::Conditional) return false; } return true; } +/// Find the range for the first interesting entry in the path at or after I. +static SourceRange nextPathEntryRange(const IndirectLocalPath &Path, unsigned I, + Expr *E) { + for (unsigned N = Path.size(); I != N; ++I) { + switch (Path[I].Kind) { + case IndirectLocalPathEntry::AddressOf: + case IndirectLocalPathEntry::LValToRVal: + case IndirectLocalPathEntry::Conditional: + // These exist primarily to mark the path as not permitting or + // supporting lifetime extension. + break; + + case IndirectLocalPathEntry::DefaultInit: + case IndirectLocalPathEntry::VarInit: + return Path[I].E->getSourceRange(); + } + } + return E->getSourceRange(); +} + void Sema::checkInitializerLifetime(const InitializedEntity &Entity, Expr *Init) { LifetimeResult LR = getEntityLifetime(&Entity); @@ -6733,9 +6757,8 @@ void Sema::checkInitializerLifetime(const InitializedEntity &Entity, auto TemporaryVisitor = [&](IndirectLocalPath &Path, Local L, ReferenceKind RK) -> bool { - Expr *First = Path.empty() ? L : Path.front().E; - SourceLocation DiagLoc = First->getLocStart(); - SourceRange DiagRange = First->getSourceRange(); + SourceRange DiagRange = nextPathEntryRange(Path, 0, L); + SourceLocation DiagLoc = DiagRange.getBegin(); switch (LK) { case LK_FullExpression: @@ -6761,13 +6784,14 @@ void Sema::checkInitializerLifetime(const InitializedEntity &Entity, // We're supposed to lifetime-extend the temporary along this path (per // the resolution of DR1815), but we don't support that yet. // - // FIXME: Properly handle this situation. Perhaps the easiest approach + // FIXME: Properly handle these situations. + // For the default member initializer case, perhaps the easiest approach // would be to clone the initializer expression on each use that would // lifetime extend its temporaries. - Diag(DiagLoc, - RK == RK_ReferenceBinding - ? diag::warn_default_member_init_temporary_not_extended - : diag::warn_default_member_init_init_list_not_extended) + Diag(DiagLoc, RK == RK_ReferenceBinding + ? diag::warn_unsupported_temporary_not_extended + : diag::warn_unsupported_init_list_not_extended) + << (Path.front().Kind == IndirectLocalPathEntry::Conditional) << DiagRange; } else { // FIXME: Warn on this. @@ -6871,31 +6895,26 @@ void Sema::checkInitializerLifetime(const InitializedEntity &Entity, for (unsigned I = 0; I != Path.size(); ++I) { auto Elem = Path[I]; - // Highlight the range of the next step within this path element. - SourceRange Range; - if (I < Path.size() - 1) - Range = Path[I + 1].E->getSourceRange(); - else - Range = L->getSourceRange(); - switch (Elem.Kind) { case IndirectLocalPathEntry::AddressOf: case IndirectLocalPathEntry::LValToRVal: - // These exist primarily to mark the path as not permitting lifetime - // extension. + case IndirectLocalPathEntry::Conditional: + // These exist primarily to mark the path as not permitting or + // supporting lifetime extension. break; case IndirectLocalPathEntry::DefaultInit: { auto *FD = cast(Elem.D); Diag(FD->getLocation(), diag::note_init_with_default_member_initalizer) - << FD << Range; + << FD << nextPathEntryRange(Path, I + 1, L); break; } case IndirectLocalPathEntry::VarInit: const VarDecl *VD = cast(Elem.D); Diag(VD->getLocation(), diag::note_local_var_initializer) - << VD->getType()->isReferenceType() << VD->getDeclName() << Range; + << VD->getType()->isReferenceType() << VD->getDeclName() + << nextPathEntryRange(Path, I + 1, L); break; } } diff --git a/clang/test/SemaCXX/conditional-expr.cpp b/clang/test/SemaCXX/conditional-expr.cpp index 7e6b0ce..ab19ce5 100644 --- a/clang/test/SemaCXX/conditional-expr.cpp +++ b/clang/test/SemaCXX/conditional-expr.cpp @@ -1,4 +1,5 @@ // RUN: %clang_cc1 -fcxx-exceptions -fexceptions -fsyntax-only -verify -std=c++11 -Wsign-conversion %s +// RUN: %clang_cc1 -fcxx-exceptions -fexceptions -fsyntax-only -verify -std=c++17 -Wsign-conversion %s // C++ rules for ?: are a lot stricter than C rules, and have to take into // account more conversion options. @@ -228,7 +229,7 @@ void test() // be properly tested at runtime, though. const Abstract &abstract1 = true ? static_cast(Derived1()) : Derived2(); // expected-error {{allocating an object of abstract class type 'const Abstract'}} - const Abstract &abstract2 = true ? static_cast(Derived1()) : throw 3; // ok + const Abstract &abstract2 = true ? static_cast(Derived1()) : throw 3; // expected-warning-re {{sorry, lifetime extension {{.*}} not supported}} } namespace PR6595 { @@ -393,3 +394,22 @@ Derived d; typedef decltype(true ? static_cast(b) : static_cast(d)) x; typedef Base &&x; } + +namespace lifetime_extension { + struct A {}; + struct B : A { B(); ~B(); }; + struct C : A { C(); ~C(); }; + + void f(bool b) { + // expected-warning@+1 2{{sorry, lifetime extension of temporary created within conditional expression is not supported}} + A &&r = b ? static_cast(B()) : static_cast(C()); + } + + struct D { A &&a; }; + void f_indirect(bool b) { +#if __cplusplus >= 201702L + // expected-warning@+2 2{{sorry, lifetime extension of temporary created within conditional expression is not supported}} +#endif + D d = b ? D{B()} : D{C()}; + } +} -- 2.7.4