diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -1359,6 +1359,10 @@ ExpressionEvaluationContext::ImmediateFunctionContext && InDiscardedStatement); } + + bool isPotentiallyEvaluatedIfUsed() const { + return Context == ExpressionEvaluationContext::PotentiallyEvaluatedIfUsed; + } }; /// A stack of expression evaluation contexts. @@ -9406,6 +9410,12 @@ return ExprEvalContexts.back().isImmediateFunctionContext(); } + bool isPotentiallyEvaluatedIfUsedContext() const { + assert(!ExprEvalContexts.empty() && + "Must be in an expression evaluation context"); + return ExprEvalContexts.back().isPotentiallyEvaluatedIfUsed(); + } + /// RAII class used to determine whether SFINAE has /// trapped any errors that occur during template argument /// deduction. diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp --- a/clang/lib/Sema/SemaExpr.cpp +++ b/clang/lib/Sema/SemaExpr.cpp @@ -17500,7 +17500,8 @@ /*IsImmediateInvocation*/ true); /// Value-dependent constant expressions should not be immediately /// evaluated until they are instantiated. - if (!Res->isValueDependent()) + /// Default arguments must be evaluated in the context of their call. + if (!Res->isValueDependent() && !isPotentiallyEvaluatedIfUsedContext()) ExprEvalContexts.back().ImmediateInvocationCandidates.emplace_back(Res, 0); return Res; } diff --git a/clang/test/SemaCXX/source_location.cpp b/clang/test/SemaCXX/source_location.cpp --- a/clang/test/SemaCXX/source_location.cpp +++ b/clang/test/SemaCXX/source_location.cpp @@ -364,8 +364,8 @@ template void func_template_tests() { constexpr auto P = test_func_template(42); - //static_assert(is_equal(P.first.function(), __func__), ""); - //static_assert(!is_equal(P.second.function(), __func__), ""); + static_assert(is_equal(P.first.function(), __PRETTY_FUNCTION__), ""); + static_assert(!is_equal(P.second.function(), __PRETTY_FUNCTION__), ""); } template void func_template_tests(); diff --git a/clang/test/SemaCXX/source_location_consteval.cpp b/clang/test/SemaCXX/source_location_consteval.cpp new file mode 100644 --- /dev/null +++ b/clang/test/SemaCXX/source_location_consteval.cpp @@ -0,0 +1,59 @@ +// RUN: %clang_cc1 -std=c++20 -verify %s +// expected-no-diagnostics + +namespace std { +class source_location { + struct __impl; + +public: + static consteval source_location current(const __impl *__p = __builtin_source_location()) noexcept { + source_location __loc; + __loc.__m_impl = __p; + return __loc; + } + constexpr source_location() = default; + constexpr source_location(source_location const &) = default; + constexpr unsigned int line() const noexcept { return __m_impl ? __m_impl->_M_line : 0; } + constexpr unsigned int column() const noexcept { return __m_impl ? __m_impl->_M_column : 0; } + constexpr const char *file() const noexcept { return __m_impl ? __m_impl->_M_file_name : ""; } + constexpr const char *function() const noexcept { return __m_impl ? __m_impl->_M_function_name : ""; } + +private: + // Note: The type name "std::source_location::__impl", and its constituent + // field-names are required by __builtin_source_location(). + struct __impl { + const char *_M_file_name; + const char *_M_function_name; + unsigned _M_line; + unsigned _M_column; + }; + const __impl *__m_impl = nullptr; + +public: + using public_impl_alias = __impl; +}; +} // namespace std + +using SL = std::source_location; + +constexpr bool is_equal(const char *LHS, const char *RHS) { + while (*LHS != 0 && *RHS != 0) { + if (*LHS != *RHS) + return false; + ++LHS; + ++RHS; + } + return *LHS == 0 && *RHS == 0; +} + +constexpr SL get_sl(SL l = SL::current()) { return l; } + +#line 700 "CheckDefaultArg.h" +constexpr SL l = get_sl(); +static_assert(l.line() == 700); +static_assert(is_equal(l.file(), "CheckDefaultArg.h")); + +void test() { + static_assert(is_equal(get_sl().function(), __PRETTY_FUNCTION__)); + static_assert(get_sl().line() == __LINE__); +}