forked from OSchip/llvm-project
Disallow narrowing conversions to bool in noexcept specififers
Completes the support for P1401R5.
This commit is contained in:
parent
3189dd205a
commit
3c8e94bc20
|
@ -82,11 +82,11 @@ def err_typecheck_converted_constant_expression_indirect : Error<
|
||||||
"bind reference to a temporary">;
|
"bind reference to a temporary">;
|
||||||
def err_expr_not_cce : Error<
|
def err_expr_not_cce : Error<
|
||||||
"%select{case value|enumerator value|non-type template argument|"
|
"%select{case value|enumerator value|non-type template argument|"
|
||||||
"array size|explicit specifier argument}0 "
|
"array size|explicit specifier argument|noexcept specifier argument}0 "
|
||||||
"is not a constant expression">;
|
"is not a constant expression">;
|
||||||
def ext_cce_narrowing : ExtWarn<
|
def ext_cce_narrowing : ExtWarn<
|
||||||
"%select{case value|enumerator value|non-type template argument|"
|
"%select{case value|enumerator value|non-type template argument|"
|
||||||
"array size|explicit specifier argument}0 "
|
"array size|explicit specifier argument|noexcept specifier argument}0 "
|
||||||
"%select{cannot be narrowed from type %2 to %3|"
|
"%select{cannot be narrowed from type %2 to %3|"
|
||||||
"evaluates to %2, which cannot be narrowed to type %3}1">,
|
"evaluates to %2, which cannot be narrowed to type %3}1">,
|
||||||
InGroup<CXX11Narrowing>, DefaultError, SFINAEFailure;
|
InGroup<CXX11Narrowing>, DefaultError, SFINAEFailure;
|
||||||
|
|
|
@ -3499,11 +3499,12 @@ public:
|
||||||
|
|
||||||
/// Contexts in which a converted constant expression is required.
|
/// Contexts in which a converted constant expression is required.
|
||||||
enum CCEKind {
|
enum CCEKind {
|
||||||
CCEK_CaseValue, ///< Expression in a case label.
|
CCEK_CaseValue, ///< Expression in a case label.
|
||||||
CCEK_Enumerator, ///< Enumerator value with fixed underlying type.
|
CCEK_Enumerator, ///< Enumerator value with fixed underlying type.
|
||||||
CCEK_TemplateArg, ///< Value of a non-type template parameter.
|
CCEK_TemplateArg, ///< Value of a non-type template parameter.
|
||||||
CCEK_ArrayBound, ///< Array bound in array declarator or new-expression.
|
CCEK_ArrayBound, ///< Array bound in array declarator or new-expression.
|
||||||
CCEK_ExplicitBool ///< Condition in an explicit(bool) specifier.
|
CCEK_ExplicitBool, ///< Condition in an explicit(bool) specifier.
|
||||||
|
CCEK_Noexcept ///< Condition in a noexcept(bool) specifier.
|
||||||
};
|
};
|
||||||
ExprResult CheckConvertedConstantExpression(Expr *From, QualType T,
|
ExprResult CheckConvertedConstantExpression(Expr *From, QualType T,
|
||||||
llvm::APSInt &Value, CCEKind CCE);
|
llvm::APSInt &Value, CCEKind CCE);
|
||||||
|
@ -5904,7 +5905,7 @@ public:
|
||||||
|
|
||||||
/// Check the given noexcept-specifier, convert its expression, and compute
|
/// Check the given noexcept-specifier, convert its expression, and compute
|
||||||
/// the appropriate ExceptionSpecificationType.
|
/// the appropriate ExceptionSpecificationType.
|
||||||
ExprResult ActOnNoexceptSpec(SourceLocation NoexceptLoc, Expr *NoexceptExpr,
|
ExprResult ActOnNoexceptSpec(Expr *NoexceptExpr,
|
||||||
ExceptionSpecificationType &EST);
|
ExceptionSpecificationType &EST);
|
||||||
|
|
||||||
/// Check the given exception-specification and update the
|
/// Check the given exception-specification and update the
|
||||||
|
|
|
@ -3837,7 +3837,7 @@ Parser::tryParseExceptionSpecification(bool Delayed,
|
||||||
NoexceptExpr = ParseConstantExpression();
|
NoexceptExpr = ParseConstantExpression();
|
||||||
T.consumeClose();
|
T.consumeClose();
|
||||||
if (!NoexceptExpr.isInvalid()) {
|
if (!NoexceptExpr.isInvalid()) {
|
||||||
NoexceptExpr = Actions.ActOnNoexceptSpec(KeywordLoc, NoexceptExpr.get(),
|
NoexceptExpr = Actions.ActOnNoexceptSpec(NoexceptExpr.get(),
|
||||||
NoexceptType);
|
NoexceptType);
|
||||||
NoexceptRange = SourceRange(KeywordLoc, T.getCloseLocation());
|
NoexceptRange = SourceRange(KeywordLoc, T.getCloseLocation());
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -78,14 +78,21 @@ bool Sema::isLibstdcxxEagerExceptionSpecHack(const Declarator &D) {
|
||||||
.Default(false);
|
.Default(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
ExprResult Sema::ActOnNoexceptSpec(SourceLocation NoexceptLoc,
|
ExprResult Sema::ActOnNoexceptSpec(Expr *NoexceptExpr,
|
||||||
Expr *NoexceptExpr,
|
|
||||||
ExceptionSpecificationType &EST) {
|
ExceptionSpecificationType &EST) {
|
||||||
// FIXME: This is bogus, a noexcept expression is not a condition.
|
|
||||||
ExprResult Converted = CheckBooleanCondition(NoexceptLoc, NoexceptExpr);
|
if (NoexceptExpr->isTypeDependent() ||
|
||||||
|
NoexceptExpr->containsUnexpandedParameterPack()) {
|
||||||
|
EST = EST_DependentNoexcept;
|
||||||
|
return NoexceptExpr;
|
||||||
|
}
|
||||||
|
|
||||||
|
llvm::APSInt Result;
|
||||||
|
ExprResult Converted = CheckConvertedConstantExpression(
|
||||||
|
NoexceptExpr, Context.BoolTy, Result, CCEK_Noexcept);
|
||||||
|
|
||||||
if (Converted.isInvalid()) {
|
if (Converted.isInvalid()) {
|
||||||
EST = EST_NoexceptFalse;
|
EST = EST_NoexceptFalse;
|
||||||
|
|
||||||
// Fill in an expression of 'false' as a fixup.
|
// Fill in an expression of 'false' as a fixup.
|
||||||
auto *BoolExpr = new (Context)
|
auto *BoolExpr = new (Context)
|
||||||
CXXBoolLiteralExpr(false, Context.BoolTy, NoexceptExpr->getBeginLoc());
|
CXXBoolLiteralExpr(false, Context.BoolTy, NoexceptExpr->getBeginLoc());
|
||||||
|
@ -99,9 +106,6 @@ ExprResult Sema::ActOnNoexceptSpec(SourceLocation NoexceptLoc,
|
||||||
return Converted;
|
return Converted;
|
||||||
}
|
}
|
||||||
|
|
||||||
llvm::APSInt Result;
|
|
||||||
Converted = VerifyIntegerConstantExpression(
|
|
||||||
Converted.get(), &Result, diag::err_noexcept_needs_constant_expression);
|
|
||||||
if (!Converted.isInvalid())
|
if (!Converted.isInvalid())
|
||||||
EST = !Result ? EST_NoexceptFalse : EST_NoexceptTrue;
|
EST = !Result ? EST_NoexceptFalse : EST_NoexceptTrue;
|
||||||
return Converted;
|
return Converted;
|
||||||
|
|
|
@ -5635,7 +5635,7 @@ static ExprResult CheckConvertedConstantExpression(Sema &S, Expr *From,
|
||||||
// expression is a constant expression and the implicit conversion
|
// expression is a constant expression and the implicit conversion
|
||||||
// sequence contains only [... list of conversions ...].
|
// sequence contains only [... list of conversions ...].
|
||||||
ImplicitConversionSequence ICS =
|
ImplicitConversionSequence ICS =
|
||||||
CCE == Sema::CCEK_ExplicitBool
|
(CCE == Sema::CCEK_ExplicitBool || CCE == Sema::CCEK_Noexcept)
|
||||||
? TryContextuallyConvertToBool(S, From)
|
? TryContextuallyConvertToBool(S, From)
|
||||||
: TryCopyInitialization(S, From, T,
|
: TryCopyInitialization(S, From, T,
|
||||||
/*SuppressUserConversions=*/false,
|
/*SuppressUserConversions=*/false,
|
||||||
|
|
|
@ -5945,7 +5945,7 @@ bool TreeTransform<Derived>::TransformExceptionSpec(
|
||||||
|
|
||||||
ExceptionSpecificationType EST = ESI.Type;
|
ExceptionSpecificationType EST = ESI.Type;
|
||||||
NoexceptExpr =
|
NoexceptExpr =
|
||||||
getSema().ActOnNoexceptSpec(Loc, NoexceptExpr.get(), EST);
|
getSema().ActOnNoexceptSpec(NoexceptExpr.get(), EST);
|
||||||
if (NoexceptExpr.isInvalid())
|
if (NoexceptExpr.isInvalid())
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
|
|
|
@ -54,9 +54,8 @@ namespace noex {
|
||||||
|
|
||||||
struct A {};
|
struct A {};
|
||||||
|
|
||||||
void g1() noexcept(A()); // expected-error {{not contextually convertible}}
|
void g1() noexcept(A()); // expected-error {{value of type 'noex::A' is not implicitly convertible to 'bool'}}
|
||||||
void g2(bool b) noexcept(b); // expected-error {{argument to noexcept specifier must be a constant expression}} expected-note {{function parameter 'b' with unknown value}} expected-note {{here}}
|
void g2(bool b) noexcept(b); // expected-error {{noexcept specifier argument is not a constant expression}} expected-note {{function parameter 'b' with unknown value}} expected-note {{here}}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace noexcept_unevaluated {
|
namespace noexcept_unevaluated {
|
||||||
|
@ -73,12 +72,12 @@ namespace noexcept_unevaluated {
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace PR11084 {
|
namespace PR11084 {
|
||||||
template<int X> struct A {
|
template <int X> struct A {
|
||||||
static int f() noexcept(1/X) { return 10; } // expected-error{{argument to noexcept specifier must be a constant expression}} expected-note{{division by zero}}
|
static int f() noexcept(1 / X) { return 10; } // expected-error{{noexcept specifier argument is not a constant expression}} expected-note{{division by zero}}
|
||||||
};
|
};
|
||||||
|
|
||||||
template<int X> void f() {
|
template<int X> void f() {
|
||||||
int (*p)() noexcept(1/X); // expected-error{{argument to noexcept specifier must be a constant expression}} expected-note{{division by zero}}
|
int (*p)() noexcept(1 / X); // expected-error{{noexcept specifier argument is not a constant expression}} expected-note{{division by zero}}
|
||||||
};
|
};
|
||||||
|
|
||||||
void g() {
|
void g() {
|
||||||
|
@ -89,7 +88,7 @@ namespace PR11084 {
|
||||||
|
|
||||||
namespace FuncTmplNoexceptError {
|
namespace FuncTmplNoexceptError {
|
||||||
int a = 0;
|
int a = 0;
|
||||||
// expected-error@+1{{argument to noexcept specifier must be a constant expression}}
|
// expected-error@+1{{noexcept specifier argument is not a constant expression}}
|
||||||
template <class T> T f() noexcept(a++){ return {};}
|
template <class T> T f() noexcept(a++){ return {};}
|
||||||
void g(){
|
void g(){
|
||||||
f<int>();
|
f<int>();
|
||||||
|
|
|
@ -77,6 +77,17 @@ void vla(bool b) {
|
||||||
}
|
}
|
||||||
|
|
||||||
struct pr_44514 {
|
struct pr_44514 {
|
||||||
// expected-error@+1{{value of type 'void' is not contextually convertible to 'bool'}}
|
// expected-error@+1{{value of type 'void' is not implicitly convertible to 'bool'}}
|
||||||
void foo(void) const &noexcept(f());
|
void foo(void) const &noexcept(f());
|
||||||
};
|
};
|
||||||
|
|
||||||
|
namespace P1401 {
|
||||||
|
const int *ptr = nullptr;
|
||||||
|
void f() noexcept(sizeof(char[2])); // expected-error {{noexcept specifier argument evaluates to 2, which cannot be narrowed to type 'bool'}}
|
||||||
|
void g() noexcept(sizeof(char));
|
||||||
|
void h() noexcept(ptr); // expected-error {{conversion from 'const int *' to 'bool' is not allowed in a converted constant expression}}
|
||||||
|
void i() noexcept(nullptr); // expected-error {{conversion from 'nullptr_t' to 'bool' is not allowed in a converted constant expression}}
|
||||||
|
void j() noexcept(0);
|
||||||
|
void k() noexcept(1);
|
||||||
|
void l() noexcept(2); // expected-error {{noexcept specifier argument evaluates to 2, which cannot be narrowed to type 'bool'}}
|
||||||
|
} // namespace P1401
|
||||||
|
|
|
@ -727,3 +727,18 @@ Str a = "short";
|
||||||
Str b = "not so short";// expected-error {{no viable conversion}}
|
Str b = "not so short";// expected-error {{no viable conversion}}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
namespace P1401 {
|
||||||
|
|
||||||
|
const int *ptr;
|
||||||
|
|
||||||
|
struct S {
|
||||||
|
explicit(sizeof(char[2])) S(char); // expected-error {{explicit specifier argument evaluates to 2, which cannot be narrowed to type 'bool'}}
|
||||||
|
explicit(ptr) S(long); // expected-error {{conversion from 'const int *' to 'bool' is not allowed in a converted constant expression}}
|
||||||
|
explicit(nullptr) S(int); // expected-error {{conversion from 'nullptr_t' to 'bool' is not allowed in a converted constant expression}}
|
||||||
|
explicit(42L) S(int, int); // expected-error {{explicit specifier argument evaluates to 42, which cannot be narrowed to type 'bool'}}
|
||||||
|
explicit(sizeof(char)) S();
|
||||||
|
explicit(0) S(char, char);
|
||||||
|
explicit(1L) S(char, char, char);
|
||||||
|
};
|
||||||
|
} // namespace P1401
|
||||||
|
|
|
@ -0,0 +1,21 @@
|
||||||
|
// RUN: %clang_cc1 %s -std=c++11 -Wignored-qualifiers -Wno-ignored-reference-qualifiers -verify=both
|
||||||
|
// RUN: %clang_cc1 %s -std=c++11 -Wignored-qualifiers -verify=both,qual
|
||||||
|
|
||||||
|
const int scalar_c(); // both-warning{{'const' type qualifier on return type has no effect}}
|
||||||
|
volatile int scalar_v(); // both-warning{{'volatile' type qualifier on return type has no effect}}
|
||||||
|
const volatile int scalar_cv(); // both-warning{{'const volatile' type qualifiers on return type have no effect}}
|
||||||
|
|
||||||
|
typedef int& IntRef;
|
||||||
|
|
||||||
|
const IntRef ref_c(); // qual-warning{{'const' qualifier on reference type 'IntRef' (aka 'int &') has no effect}}
|
||||||
|
volatile IntRef ref_v(); // qual-warning{{'volatile' qualifier on reference type 'IntRef' (aka 'int &') has no effect}}
|
||||||
|
const volatile IntRef ref_cv(); // qual-warning{{'const' qualifier on reference type 'IntRef' (aka 'int &') has no effect}} \
|
||||||
|
qual-warning{{'volatile' qualifier on reference type 'IntRef' (aka 'int &') has no effect}}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
class container {
|
||||||
|
using value_type = T;
|
||||||
|
using reference = value_type&;
|
||||||
|
reference get();
|
||||||
|
const reference get() const; // qual-warning{{'const' qualifier on reference type 'container::reference' (aka 'T &') has no effect}}
|
||||||
|
};
|
|
@ -876,7 +876,7 @@ TEST_P(ASTMatchersTest, Matcher_NoexceptExpression) {
|
||||||
EXPECT_TRUE(
|
EXPECT_TRUE(
|
||||||
matches("void foo() noexcept; bool bar = noexcept(foo());", NoExcept));
|
matches("void foo() noexcept; bool bar = noexcept(foo());", NoExcept));
|
||||||
EXPECT_TRUE(notMatches("void foo() noexcept;", NoExcept));
|
EXPECT_TRUE(notMatches("void foo() noexcept;", NoExcept));
|
||||||
EXPECT_TRUE(notMatches("void foo() noexcept(1+1);", NoExcept));
|
EXPECT_TRUE(notMatches("void foo() noexcept(0+1);", NoExcept));
|
||||||
EXPECT_TRUE(matches("void foo() noexcept(noexcept(1+1));", NoExcept));
|
EXPECT_TRUE(matches("void foo() noexcept(noexcept(1+1));", NoExcept));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1298,7 +1298,7 @@ C++20, informally referred to as C++2b.</p>
|
||||||
<tr>
|
<tr>
|
||||||
<td>Narrowing contextual conversions to bool</td>
|
<td>Narrowing contextual conversions to bool</td>
|
||||||
<td><a href="https://wg21.link/P1401R5">P1401R5</a></td>
|
<td><a href="https://wg21.link/P1401R5">P1401R5</a></td>
|
||||||
<td class="partial" align="center">Clang 13</td>
|
<td class="unreleased" align="center">Clang 14</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>Trimming whitespaces before line splicing</td>
|
<td>Trimming whitespaces before line splicing</td>
|
||||||
|
|
Loading…
Reference in New Issue