Skip to content

Commit

Permalink
[c++1z] Most of N4268 (allow constant evaluation for non-type templat…
Browse files Browse the repository at this point in the history
…e arguments).

We don't yet support pointer-to-member template arguments that have undergone
pointer-to-member conversions, mostly because we don't have a mangling for them yet.

llvm-svn: 222807
  • Loading branch information
zygoloid committed Nov 26, 2014
1 parent 1686e4e commit 410cc89
Show file tree
Hide file tree
Showing 12 changed files with 397 additions and 87 deletions.
10 changes: 10 additions & 0 deletions clang/include/clang/Basic/DiagnosticSemaKinds.td
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,9 @@ def err_typecheck_converted_constant_expression : Error<
"value of type %0 is not implicitly convertible to %1">;
def err_typecheck_converted_constant_expression_disallowed : Error<
"conversion from %0 to %1 is not allowed in a converted constant expression">;
def err_typecheck_converted_constant_expression_indirect : Error<
"conversion from %0 to %1 in converted constant expression would "
"bind reference to a temporary">;
def err_expr_not_cce : Error<
"%select{case value|enumerator value|non-type template argument|array size}0 "
"is not a constant expression">;
Expand Down Expand Up @@ -3293,6 +3296,10 @@ def err_template_arg_wrongtype_null_constant : Error<
def err_deduced_non_type_template_arg_type_mismatch : Error<
"deduced non-type template argument does not have the same type as the "
"its corresponding template parameter%diff{ ($ vs $)|}0,1">;
def err_non_type_template_arg_subobject : Error<
"non-type template argument refers to subobject '%0'">;
def err_non_type_template_arg_addr_label_diff : Error<
"template argument / label address difference / what did you expect?">;
def err_template_arg_not_convertible : Error<
"non-type template argument of type %0 cannot be converted to a value "
"of type %1">;
Expand Down Expand Up @@ -3344,6 +3351,9 @@ def err_template_arg_not_object_or_func : Error<
"non-type template argument does not refer to an object or function">;
def err_template_arg_not_pointer_to_member_form : Error<
"non-type template argument is not a pointer to member constant">;
def err_template_arg_member_ptr_base_derived_not_supported : Error<
"sorry, non-type template argument of pointer-to-member type %1 that refers "
"to member %q0 of a different class is not supported yet">;
def ext_template_arg_extra_parens : ExtWarn<
"address non-type template argument cannot be surrounded by parentheses">;
def warn_cxx98_compat_template_arg_extra_parens : Warning<
Expand Down
2 changes: 2 additions & 0 deletions clang/include/clang/Sema/Sema.h
Original file line number Diff line number Diff line change
Expand Up @@ -2148,6 +2148,8 @@ class Sema {
};
ExprResult CheckConvertedConstantExpression(Expr *From, QualType T,
llvm::APSInt &Value, CCEKind CCE);
ExprResult CheckConvertedConstantExpression(Expr *From, QualType T,
APValue &Value, CCEKind CCE);

/// \brief Abstract base class used to perform a contextual implicit
/// conversion from an expression to any type passing a filter.
Expand Down
27 changes: 23 additions & 4 deletions clang/lib/Sema/SemaExprCXX.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2780,10 +2780,29 @@ Sema::PerformImplicitConversion(Expr *From, QualType ToType,
// Perform the second implicit conversion
switch (SCS.Second) {
case ICK_Identity:
// If both sides are functions (or pointers/references to them), there could
// be incompatible exception declarations.
if (CheckExceptionSpecCompatibility(From, ToType))
return ExprError();
// C++ [except.spec]p5:
// [For] assignment to and initialization of pointers to functions,
// pointers to member functions, and references to functions: the
// target entity shall allow at least the exceptions allowed by the
// source value in the assignment or initialization.
switch (Action) {
case AA_Assigning:
case AA_Initializing:
// Note, function argument passing and returning are initialization.
case AA_Passing:
case AA_Returning:
case AA_Sending:
case AA_Passing_CFAudited:
if (CheckExceptionSpecCompatibility(From, ToType))
return ExprError();
break;

case AA_Casting:
case AA_Converting:
// Casts and implicit conversions are not initialization, so are not
// checked for exception specification mismatches.
break;
}
// Nothing else to do.
break;

Expand Down
159 changes: 97 additions & 62 deletions clang/lib/Sema/SemaOverload.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4894,41 +4894,51 @@ static bool CheckConvertedConstantConversions(Sema &S,
// conversions are fine.
switch (SCS.Second) {
case ICK_Identity:
case ICK_NoReturn_Adjustment:
case ICK_Integral_Promotion:
case ICK_Integral_Conversion:
case ICK_Zero_Event_Conversion:
case ICK_Integral_Conversion: // Narrowing conversions are checked elsewhere.
return true;

case ICK_Boolean_Conversion:
// Conversion from an integral or unscoped enumeration type to bool is
// classified as ICK_Boolean_Conversion, but it's also an integral
// conversion, so it's permitted in a converted constant expression.
// classified as ICK_Boolean_Conversion, but it's also arguably an integral
// conversion, so we allow it in a converted constant expression.
//
// FIXME: Per core issue 1407, we should not allow this, but that breaks
// a lot of popular code. We should at least add a warning for this
// (non-conforming) extension.
return SCS.getFromType()->isIntegralOrUnscopedEnumerationType() &&
SCS.getToType(2)->isBooleanType();

case ICK_Pointer_Conversion:
case ICK_Pointer_Member:
// C++1z: null pointer conversions and null member pointer conversions are
// only permitted if the source type is std::nullptr_t.
return SCS.getFromType()->isNullPtrType();

case ICK_Floating_Promotion:
case ICK_Complex_Promotion:
case ICK_Floating_Conversion:
case ICK_Complex_Conversion:
case ICK_Floating_Integral:
case ICK_Compatible_Conversion:
case ICK_Derived_To_Base:
case ICK_Vector_Conversion:
case ICK_Vector_Splat:
case ICK_Complex_Real:
case ICK_Block_Pointer_Conversion:
case ICK_TransparentUnionConversion:
case ICK_Writeback_Conversion:
case ICK_Zero_Event_Conversion:
return false;

case ICK_Lvalue_To_Rvalue:
case ICK_Array_To_Pointer:
case ICK_Function_To_Pointer:
case ICK_NoReturn_Adjustment:
llvm_unreachable("found a first conversion kind in Second");

case ICK_Qualification:
case ICK_Compatible_Conversion:
case ICK_Vector_Conversion:
case ICK_Vector_Splat:
case ICK_Derived_To_Base:
case ICK_Pointer_Conversion:
case ICK_Pointer_Member:
case ICK_Block_Pointer_Conversion:
case ICK_Writeback_Conversion:
case ICK_Floating_Promotion:
case ICK_Complex_Promotion:
case ICK_Complex_Conversion:
case ICK_Floating_Conversion:
case ICK_TransparentUnionConversion:
llvm_unreachable("unexpected second conversion kind");
llvm_unreachable("found a third conversion kind in Second");

case ICK_Num_Conversion_Kinds:
break;
Expand All @@ -4940,67 +4950,71 @@ static bool CheckConvertedConstantConversions(Sema &S,
/// CheckConvertedConstantExpression - Check that the expression From is a
/// converted constant expression of type T, perform the conversion and produce
/// the converted expression, per C++11 [expr.const]p3.
ExprResult Sema::CheckConvertedConstantExpression(Expr *From, QualType T,
llvm::APSInt &Value,
CCEKind CCE) {
assert(LangOpts.CPlusPlus11 && "converted constant expression outside C++11");
assert(T->isIntegralOrEnumerationType() && "unexpected converted const type");

if (checkPlaceholderForOverload(*this, From))
static ExprResult CheckConvertedConstantExpression(Sema &S, Expr *From,
QualType T, APValue &Value,
Sema::CCEKind CCE,
bool RequireInt) {
assert(S.getLangOpts().CPlusPlus11 &&
"converted constant expression outside C++11");

if (checkPlaceholderForOverload(S, From))
return ExprError();

// C++11 [expr.const]p3 with proposed wording fixes:
// A converted constant expression of type T is a core constant expression,
// implicitly converted to a prvalue of type T, where the converted
// expression is a literal constant expression and the implicit conversion
// sequence contains only user-defined conversions, lvalue-to-rvalue
// conversions, integral promotions, and integral conversions other than
// narrowing conversions.
// C++1z [expr.const]p3:
// A converted constant expression of type T is an expression,
// implicitly converted to type T, where the converted
// expression is a constant expression and the implicit conversion
// sequence contains only [... list of conversions ...].
ImplicitConversionSequence ICS =
TryImplicitConversion(From, T,
TryCopyInitialization(S, From, T,
/*SuppressUserConversions=*/false,
/*AllowExplicit=*/false,
/*InOverloadResolution=*/false,
/*CStyle=*/false,
/*AllowObjcWritebackConversion=*/false);
/*AllowObjcWritebackConversion=*/false,
/*AllowExplicit=*/false);
StandardConversionSequence *SCS = nullptr;
switch (ICS.getKind()) {
case ImplicitConversionSequence::StandardConversion:
if (!CheckConvertedConstantConversions(*this, ICS.Standard))
return Diag(From->getLocStart(),
diag::err_typecheck_converted_constant_expression_disallowed)
<< From->getType() << From->getSourceRange() << T;
SCS = &ICS.Standard;
break;
case ImplicitConversionSequence::UserDefinedConversion:
// We are converting from class type to an integral or enumeration type, so
// the Before sequence must be trivial.
if (!CheckConvertedConstantConversions(*this, ICS.UserDefined.After))
return Diag(From->getLocStart(),
diag::err_typecheck_converted_constant_expression_disallowed)
<< From->getType() << From->getSourceRange() << T;
// We are converting to a non-class type, so the Before sequence
// must be trivial.
SCS = &ICS.UserDefined.After;
break;
case ImplicitConversionSequence::AmbiguousConversion:
case ImplicitConversionSequence::BadConversion:
if (!DiagnoseMultipleUserDefinedConversion(From, T))
return Diag(From->getLocStart(),
diag::err_typecheck_converted_constant_expression)
<< From->getType() << From->getSourceRange() << T;
if (!S.DiagnoseMultipleUserDefinedConversion(From, T))
return S.Diag(From->getLocStart(),
diag::err_typecheck_converted_constant_expression)
<< From->getType() << From->getSourceRange() << T;
return ExprError();

case ImplicitConversionSequence::EllipsisConversion:
llvm_unreachable("ellipsis conversion in converted constant expression");
}

ExprResult Result = PerformImplicitConversion(From, T, ICS, AA_Converting);
// Check that we would only use permitted conversions.
if (!CheckConvertedConstantConversions(S, *SCS)) {
return S.Diag(From->getLocStart(),
diag::err_typecheck_converted_constant_expression_disallowed)
<< From->getType() << From->getSourceRange() << T;
}
// [...] and where the reference binding (if any) binds directly.
if (SCS->ReferenceBinding && !SCS->DirectBinding) {
return S.Diag(From->getLocStart(),
diag::err_typecheck_converted_constant_expression_indirect)
<< From->getType() << From->getSourceRange() << T;
}

ExprResult Result =
S.PerformImplicitConversion(From, T, ICS, Sema::AA_Converting);
if (Result.isInvalid())
return Result;

// Check for a narrowing implicit conversion.
APValue PreNarrowingValue;
QualType PreNarrowingType;
switch (SCS->getNarrowingKind(Context, Result.get(), PreNarrowingValue,
switch (SCS->getNarrowingKind(S.Context, Result.get(), PreNarrowingValue,
PreNarrowingType)) {
case NK_Variable_Narrowing:
// Implicit conversion to a narrower type, and the value is not a constant
Expand All @@ -5009,13 +5023,13 @@ ExprResult Sema::CheckConvertedConstantExpression(Expr *From, QualType T,
break;

case NK_Constant_Narrowing:
Diag(From->getLocStart(), diag::ext_cce_narrowing)
S.Diag(From->getLocStart(), diag::ext_cce_narrowing)
<< CCE << /*Constant*/1
<< PreNarrowingValue.getAsString(Context, PreNarrowingType) << T;
<< PreNarrowingValue.getAsString(S.Context, PreNarrowingType) << T;
break;

case NK_Type_Narrowing:
Diag(From->getLocStart(), diag::ext_cce_narrowing)
S.Diag(From->getLocStart(), diag::ext_cce_narrowing)
<< CCE << /*Constant*/0 << From->getType() << T;
break;
}
Expand All @@ -5025,12 +5039,15 @@ ExprResult Sema::CheckConvertedConstantExpression(Expr *From, QualType T,
Expr::EvalResult Eval;
Eval.Diag = &Notes;

if (!Result.get()->EvaluateAsRValue(Eval, Context) || !Eval.Val.isInt()) {
if ((T->isReferenceType()
? !Result.get()->EvaluateAsLValue(Eval, S.Context)
: !Result.get()->EvaluateAsRValue(Eval, S.Context)) ||
(RequireInt && !Eval.Val.isInt())) {
// The expression can't be folded, so we can't keep it at this position in
// the AST.
Result = ExprError();
} else {
Value = Eval.Val.getInt();
Value = Eval.Val;

if (Notes.empty()) {
// It's a constant expression.
Expand All @@ -5041,16 +5058,34 @@ ExprResult Sema::CheckConvertedConstantExpression(Expr *From, QualType T,
// It's not a constant expression. Produce an appropriate diagnostic.
if (Notes.size() == 1 &&
Notes[0].second.getDiagID() == diag::note_invalid_subexpr_in_const_expr)
Diag(Notes[0].first, diag::err_expr_not_cce) << CCE;
S.Diag(Notes[0].first, diag::err_expr_not_cce) << CCE;
else {
Diag(From->getLocStart(), diag::err_expr_not_cce)
S.Diag(From->getLocStart(), diag::err_expr_not_cce)
<< CCE << From->getSourceRange();
for (unsigned I = 0; I < Notes.size(); ++I)
Diag(Notes[I].first, Notes[I].second);
S.Diag(Notes[I].first, Notes[I].second);
}
return Result;
return ExprError();
}

ExprResult Sema::CheckConvertedConstantExpression(Expr *From, QualType T,
APValue &Value, CCEKind CCE) {
return ::CheckConvertedConstantExpression(*this, From, T, Value, CCE, false);
}

ExprResult Sema::CheckConvertedConstantExpression(Expr *From, QualType T,
llvm::APSInt &Value,
CCEKind CCE) {
assert(T->isIntegralOrEnumerationType() && "unexpected converted const type");

APValue V;
auto R = ::CheckConvertedConstantExpression(*this, From, T, V, CCE, true);
if (!R.isInvalid())
Value = V.getInt();
return R;
}


/// dropPointerConversions - If the given standard conversion sequence
/// involves any pointer conversions, remove them. This may change
/// the result type of the conversion sequence.
Expand Down

0 comments on commit 410cc89

Please sign in to comment.