Implement https://cplusplus.github.io/CWG/issues/2631.html. Immediate calls in default arguments and defaults members are not evaluated. Instead, we evaluate them when constructing a `CXXDefaultArgExpr`/`BuildCXXDefaultInitExpr`. The immediate calls are executed by doing a transform on the initializing expression. Note that lambdas are not considering subexpressions so we do not need to transform them. As a result of this patch, unused default member initializers are not considered odr-used, and errors about members binding to local variables in an outer scope only surface at the point where a constructor is defined. Reviewed By: aaron.ballman, #clang-language-wg, rupprecht Differential Revision: https://reviews.llvm.org/D136554
82 lines
2.8 KiB
C++
82 lines
2.8 KiB
C++
// RUN: %clang_cc1 -fsyntax-only -verify -std=c++20 %s
|
|
// RUN: %clang_cc1 -fsyntax-only -verify -std=c++2b %s
|
|
|
|
consteval int undefined(); // expected-note 4 {{declared here}}
|
|
|
|
void check_lambdas_unused(
|
|
int a = []
|
|
{
|
|
// The body of a lambda is not a subexpression of the lambda
|
|
// so this is immediately evaluated even if the parameter
|
|
// is never used.
|
|
return undefined(); // expected-error {{not a constant expression}} \
|
|
// expected-note {{undefined function 'undefined'}}
|
|
}(),
|
|
int b = [](int no_error = undefined()) {
|
|
return no_error;
|
|
}(0),
|
|
int c = [](int defaulted = undefined()) {
|
|
return defaulted;
|
|
}()
|
|
) {}
|
|
|
|
int check_lambdas_used(
|
|
int b = [](int no_error = undefined()) {
|
|
return no_error;
|
|
}(0),
|
|
int c = [](int defaulted = undefined()) { // expected-error {{not a constant expression}} \
|
|
// expected-note {{declared here}} \
|
|
// expected-note {{undefined function 'undefined'}}
|
|
return defaulted;
|
|
}(), // expected-note {{in the default initalizer of 'defaulted'}}
|
|
int d = [](int defaulted = sizeof(undefined())) {
|
|
return defaulted;
|
|
}()
|
|
) {
|
|
return 0;
|
|
}
|
|
|
|
int test_check_lambdas_used = check_lambdas_used();
|
|
|
|
struct UnusedInitWithLambda {
|
|
int a = [] {
|
|
return undefined(); // expected-error {{not a constant expression}} \
|
|
// expected-note {{undefined function 'undefined'}}
|
|
}();
|
|
// UnusedInitWithLambda is never constructed, so the initializer
|
|
// of b and undefined() are never evaluated.
|
|
int b = [](int no_error = undefined()) {
|
|
return no_error;
|
|
}();
|
|
};
|
|
|
|
consteval int ub(int n) {
|
|
return 0/n; // expected-note {{division}}
|
|
}
|
|
|
|
struct InitWithLambda {
|
|
int b = [](int error = undefined()) { // expected-error {{not a constant expression}} \
|
|
// expected-note {{declared here}} \
|
|
// expected-note {{undefined function 'undefined'}}
|
|
return error;
|
|
}(); // expected-note {{in the default initalizer of 'error'}}
|
|
int c = [](int error = sizeof(undefined()) + ub(0)) { // expected-error {{'ub' is not a constant expression}} \
|
|
// expected-note {{declared here}} \
|
|
// expected-note {{in call to 'ub(0)}}
|
|
return error;
|
|
}(); // expected-note {{in the default initalizer of 'error'}}
|
|
} i; // expected-note {{in implicit default constructor}}
|
|
|
|
namespace ShouldNotCrash {
|
|
template<typename T>
|
|
struct F {
|
|
template<typename U>
|
|
F(const U&) {}
|
|
};
|
|
struct A {
|
|
static constexpr auto x = [] {};
|
|
F<int> f = x;
|
|
};
|
|
void f(A a = A()) { }
|
|
}
|