diff --git a/clang/lib/AST/Interp/Interp.h b/clang/lib/AST/Interp/Interp.h index e0cbf56e19695..4058d43c0bced 100644 --- a/clang/lib/AST/Interp/Interp.h +++ b/clang/lib/AST/Interp/Interp.h @@ -181,6 +181,17 @@ template ::T> bool Ret(InterpState &S, CodePtr &PC, APValue &Result) { const T &Ret = S.Stk.pop(); + // Make sure returned pointers are live. We might be trying to return a + // pointer or reference to a local variable. + // Just return false, since a diagnostic has already been emitted in Sema. + if constexpr (std::is_same_v) { + // FIXME: We could be calling isLive() here, but the emitted diagnostics + // seem a little weird, at least if the returned expression is of + // pointer type. + if (!Ret.isLive()) + return false; + } + assert(S.Current->getFrameOffset() == S.Stk.size() && "Invalid frame"); if (!S.checkingPotentialConstantExpression() || S.Current->Caller) S.Current->popArgs(); diff --git a/clang/test/AST/Interp/functions.cpp b/clang/test/AST/Interp/functions.cpp index 629d0323e1d2e..4bb8791de8f4e 100644 --- a/clang/test/AST/Interp/functions.cpp +++ b/clang/test/AST/Interp/functions.cpp @@ -265,3 +265,29 @@ namespace CallWithArgs { g(0); } } + +namespace ReturnLocalPtr { + constexpr int *p() { + int a = 12; + return &a; // ref-warning {{address of stack memory}} \ + // expected-warning {{address of stack memory}} + } + + /// GCC rejects the expression below, just like the new interpreter. The current interpreter + /// however accepts it and only warns about the function above returning an address to stack + /// memory. If we change the condition to 'p() != nullptr', it even succeeds. + static_assert(p() == nullptr, ""); // ref-error {{static assertion failed}} \ + // expected-error {{not an integral constant expression}} + + /// FIXME: The current interpreter emits diagnostics in the reference case below, but the + /// new one does not. + constexpr const int &p2() { + int a = 12; // ref-note {{declared here}} + return a; // ref-warning {{reference to stack memory associated with local variable}} \ + // expected-warning {{reference to stack memory associated with local variable}} + } + + static_assert(p2() == 12, ""); // ref-error {{not an integral constant expression}} \ + // ref-note {{read of variable whose lifetime has ended}} \ + // expected-error {{not an integral constant expression}} +}