diff options
| author | Richard Smith <richard-llvm@metafoo.co.uk> | 2011-09-29 19:11:37 +0000 |
|---|---|---|
| committer | Richard Smith <richard-llvm@metafoo.co.uk> | 2011-09-29 19:11:37 +0000 |
| commit | 2316cd8b798be3163878e7397ec68a8890063098 (patch) | |
| tree | e89a2675b03c27b64defcc69ac004ff8d0ef6329 /clang/lib | |
| parent | d71061298cc290668b9640a6a15456ea9b3a937c (diff) | |
| download | bcm5719-llvm-2316cd8b798be3163878e7397ec68a8890063098.tar.gz bcm5719-llvm-2316cd8b798be3163878e7397ec68a8890063098.zip | |
constexpr: semantic checking for constexpr variables.
We had an extension which allowed const static class members of floating-point type to have in-class initializers, 'as a C++0x extension'. However, C++0x does not allow this. The extension has been kept, and extended to all literal types in C++0x mode (with a fixit to add the 'constexpr' specifier).
llvm-svn: 140801
Diffstat (limited to 'clang/lib')
| -rw-r--r-- | clang/lib/Sema/SemaDecl.cpp | 119 | ||||
| -rw-r--r-- | clang/lib/Sema/SemaDeclCXX.cpp | 27 |
2 files changed, 111 insertions, 35 deletions
diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp index f6bfb3a2f20..e56d09f6a77 100644 --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -3833,8 +3833,36 @@ Sema::ActOnVariableDeclarator(Scope *S, Declarator &D, DeclContext *DC, } if (D.getDeclSpec().isConstexprSpecified()) { - // FIXME: check this is a valid use of constexpr. - NewVD->setConstexpr(true); + // FIXME: once we know whether there's an initializer, apply this to + // static data members too. + if (!NewVD->isStaticDataMember() && + !NewVD->isThisDeclarationADefinition()) { + // 'constexpr' is redundant and ill-formed on a non-defining declaration + // of a variable. Suggest replacing it with 'const' if appropriate. + SourceLocation ConstexprLoc = D.getDeclSpec().getConstexprSpecLoc(); + SourceRange ConstexprRange(ConstexprLoc, ConstexprLoc); + // If the declarator is complex, we need to move the keyword to the + // innermost chunk as we switch it from 'constexpr' to 'const'. + int Kind = DeclaratorChunk::Paren; + for (unsigned I = 0, E = D.getNumTypeObjects(); I != E; ++I) { + Kind = D.getTypeObject(I).Kind; + if (Kind != DeclaratorChunk::Paren) + break; + } + if ((D.getDeclSpec().getTypeQualifiers() & DeclSpec::TQ_const) || + Kind == DeclaratorChunk::Reference) + Diag(ConstexprLoc, diag::err_invalid_constexpr_var_decl) + << FixItHint::CreateRemoval(ConstexprRange); + else if (Kind == DeclaratorChunk::Paren) + Diag(ConstexprLoc, diag::err_invalid_constexpr_var_decl) + << FixItHint::CreateReplacement(ConstexprRange, "const"); + else + Diag(ConstexprLoc, diag::err_invalid_constexpr_var_decl) + << FixItHint::CreateRemoval(ConstexprRange) + << FixItHint::CreateInsertion(D.getIdentifierLoc(), "const "); + } else { + NewVD->setConstexpr(true); + } } } @@ -5796,11 +5824,26 @@ void Sema::AddInitializerToDecl(Decl *RealDecl, Expr *Init, // A member-declarator can contain a constant-initializer only // if it declares a static member (9.4) of const integral or // const enumeration type, see 9.4.2. + // + // C++0x [class.static.data]p3: + // If a non-volatile const static data member is of integral or + // enumeration type, its declaration in the class definition can + // specify a brace-or-equal-initializer in which every initalizer-clause + // that is an assignment-expression is a constant expression. A static + // data member of literal type can be declared in the class definition + // with the constexpr specifier; if so, its declaration shall specify a + // brace-or-equal-initializer in which every initializer-clause that is + // an assignment-expression is a constant expression. QualType T = VDecl->getType(); // Do nothing on dependent types. if (T->isDependentType()) { + // Allow any 'static constexpr' members, whether or not they are of literal + // type. We separately check that the initializer is a constant expression, + // which implicitly requires the member to be of literal type. + } else if (VDecl->isConstexpr()) { + // Require constness. } else if (!T.isConstQualified()) { Diag(VDecl->getLocation(), diag::err_in_class_initializer_non_const) @@ -5809,6 +5852,9 @@ void Sema::AddInitializerToDecl(Decl *RealDecl, Expr *Init, // We allow integer constant expressions in all cases. } else if (T->isIntegralOrEnumerationType()) { + // FIXME: In C++0x, a non-constexpr const static data member with an + // in-class initializer cannot be volatile. + // Check whether the expression is a constant expression. SourceLocation Loc; if (Init->isValueDependent()) @@ -5828,31 +5874,28 @@ void Sema::AddInitializerToDecl(Decl *RealDecl, Expr *Init, VDecl->setInvalidDecl(); } - // We allow floating-point constants as an extension in C++03, and - // C++0x has far more complicated rules that we don't really - // implement fully. - } else { - bool Allowed = false; - if (getLangOptions().CPlusPlus0x) { - Allowed = T->isLiteralType(); - } else if (T->isFloatingType()) { // also permits complex, which is ok - Diag(VDecl->getLocation(), diag::ext_in_class_initializer_float_type) - << T << Init->getSourceRange(); - Allowed = true; - } + // Suggest adding 'constexpr' in C++0x for literal types. + } else if (getLangOptions().CPlusPlus0x && T->isLiteralType()) { + Diag(VDecl->getLocation(), diag::ext_in_class_initializer_literal_type) + << T << Init->getSourceRange() + << FixItHint::CreateInsertion(VDecl->getLocStart(), "constexpr "); + VDecl->setConstexpr(true); - if (!Allowed) { - Diag(VDecl->getLocation(), diag::err_in_class_initializer_bad_type) - << T << Init->getSourceRange(); - VDecl->setInvalidDecl(); + // We allow floating-point constants as an extension. + } else if (T->isFloatingType()) { // also permits complex, which is ok + Diag(VDecl->getLocation(), diag::ext_in_class_initializer_float_type) + << T << Init->getSourceRange(); - // TODO: there are probably expressions that pass here that shouldn't. - } else if (!Init->isValueDependent() && - !Init->isConstantInitializer(Context, false)) { + if (!Init->isValueDependent() && + !Init->isConstantInitializer(Context, false)) { Diag(Init->getExprLoc(), diag::err_in_class_initializer_non_constant) << Init->getSourceRange(); VDecl->setInvalidDecl(); } + } else { + Diag(VDecl->getLocation(), diag::err_in_class_initializer_bad_type) + << T << Init->getSourceRange(); + VDecl->setInvalidDecl(); } } else if (VDecl->isFileVarDecl()) { if (VDecl->getStorageClassAsWritten() == SC_Extern && @@ -5893,6 +5936,17 @@ void Sema::AddInitializerToDecl(Decl *RealDecl, Expr *Init, if (!VDecl->isInvalidDecl()) checkUnsafeAssigns(VDecl->getLocation(), VDecl->getType(), Init); + + if (VDecl->isConstexpr() && !VDecl->isInvalidDecl() && + !VDecl->getType()->isDependentType() && + !Init->isTypeDependent() && !Init->isValueDependent() && + !Init->isConstantInitializer(Context, + VDecl->getType()->isReferenceType())) { + // FIXME: Improve this diagnostic to explain why the initializer is not + // a constant expression. + Diag(VDecl->getLocation(), diag::err_constexpr_var_requires_const_init) + << VDecl << Init->getSourceRange(); + } Init = MaybeCreateExprWithCleanups(Init); // Attach the initializer to the decl. @@ -5958,6 +6012,24 @@ void Sema::ActOnUninitializedDecl(Decl *RealDecl, return; } + // C++0x [dcl.constexpr]p9: An object or reference declared constexpr must + // have an initializer. + // C++0x [class.static.data]p3: A static data member can be declared with + // the constexpr specifier; if so, its declaration shall specify + // a brace-or-equal-initializer. + if (Var->isConstexpr()) { + // FIXME: Provide fix-its to convert the constexpr to const. + if (Var->isStaticDataMember() && Var->getAnyInitializer()) { + Diag(Var->getLocation(), diag::err_constexpr_initialized_static_member) + << Var->getDeclName(); + } else { + Diag(Var->getLocation(), diag::err_constexpr_var_requires_init) + << Var->getDeclName(); + } + Var->setInvalidDecl(); + return; + } + switch (Var->isThisDeclarationADefinition()) { case VarDecl::Definition: if (!Var->isStaticDataMember() || !Var->getAnyInitializer()) @@ -6151,9 +6223,8 @@ void Sema::ActOnCXXForRangeDecl(Decl *D) { case SC_OpenCLWorkGroupLocal: llvm_unreachable("Unexpected storage class"); } - // FIXME: constexpr isn't allowed here. - //if (DS.isConstexprSpecified()) - // Error = 5; + if (VD->isConstexpr()) + Error = 5; if (Error != -1) { Diag(VD->getOuterLocStart(), diag::err_for_range_storage_class) << VD->getDeclName() << Error; diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp index e8892fd323f..ae31e61c437 100644 --- a/clang/lib/Sema/SemaDeclCXX.cpp +++ b/clang/lib/Sema/SemaDeclCXX.cpp @@ -1239,14 +1239,8 @@ Sema::ActOnCXXMemberDeclarator(Scope *S, AccessSpecifier AS, Declarator &D, if (Init) AddInitializerToDecl(Member, Init, false, DS.getTypeSpecType() == DeclSpec::TST_auto); - else if (DS.getTypeSpecType() == DeclSpec::TST_auto && - DS.getStorageClassSpec() == DeclSpec::SCS_static) { - // C++0x [dcl.spec.auto]p4: 'auto' can only be used in the type of a static - // data member if a brace-or-equal-initializer is provided. - Diag(Loc, diag::err_auto_var_requires_init) - << Name << cast<ValueDecl>(Member)->getType(); - Member->setInvalidDecl(); - } + else if (DS.getStorageClassSpec() == DeclSpec::SCS_static) + ActOnUninitializedDecl(Member, DS.getTypeSpecType() == DeclSpec::TST_auto); FinalizeDeclaration(Member); @@ -8727,10 +8721,21 @@ void Sema::AddCXXDirectInitializerToDecl(Decl *RealDecl, return; } - CheckImplicitConversions(Result.get(), LParenLoc); + Expr *Init = Result.get(); + CheckImplicitConversions(Init, LParenLoc); - Result = MaybeCreateExprWithCleanups(Result); - VDecl->setInit(Result.takeAs<Expr>()); + if (VDecl->isConstexpr() && !VDecl->isInvalidDecl() && + !Init->isValueDependent() && + !Init->isConstantInitializer(Context, + VDecl->getType()->isReferenceType())) { + // FIXME: Improve this diagnostic to explain why the initializer is not + // a constant expression. + Diag(VDecl->getLocation(), diag::err_constexpr_var_requires_const_init) + << VDecl << Init->getSourceRange(); + } + + Init = MaybeCreateExprWithCleanups(Init); + VDecl->setInit(Init); VDecl->setCXXDirectInitializer(true); CheckCompleteVariableDeclaration(VDecl); |

