P0091R3: Improved syntactic checking of deduction-guides.

llvm-svn: 294395
This commit is contained in:
Richard Smith 2017-02-08 00:35:25 +00:00
parent 4b946916ac
commit f283fdcd50
7 changed files with 159 additions and 43 deletions

View File

@ -1966,6 +1966,8 @@ def err_deduction_guide_no_trailing_return_type : Error<
def err_deduction_guide_with_complex_decl : Error<
"cannot specify any part of a return type in the "
"declaration of a deduction guide">;
def err_deduction_guide_invalid_specifier : Error<
"deduction guide cannot be declared '%0'">;
def err_deduction_guide_name_not_class_template : Error<
"cannot specify deduction guide for "
"%select{<error>|function template|variable template|alias template|"
@ -5775,8 +5777,8 @@ def err_this_static_member_func : Error<
def err_invalid_member_use_in_static_method : Error<
"invalid use of member %0 in static member function">;
def err_invalid_qualified_function_type : Error<
"%select{static |non-}0member function %select{of type %2 |}1"
"cannot have '%3' qualifier">;
"%select{non-member function|static member function|deduction guide}0 "
"%select{of type %2 |}1cannot have '%3' qualifier">;
def err_compound_qualified_function_type : Error<
"%select{block pointer|pointer|reference}0 to function type %select{%2 |}1"
"cannot have '%3' qualifier">;

View File

@ -203,7 +203,10 @@ void DeclarationName::print(raw_ostream &OS, const PrintingPolicy &Policy) {
}
case DeclarationName::CXXDeductionGuideName:
return getCXXDeductionGuideTemplate()->getDeclName().print(OS, Policy);
OS << "<deduction guide for ";
getCXXDeductionGuideTemplate()->getDeclName().print(OS, Policy);
OS << '>';
return;
case DeclarationName::CXXOperatorName: {
static const char* const OperatorNames[NUM_OVERLOADED_OPERATORS] = {

View File

@ -5463,8 +5463,13 @@ Sema::ActOnTypedefDeclarator(Scope* S, Declarator& D, DeclContext* DC,
diag::err_concept_wrong_decl_kind);
if (D.getName().Kind != UnqualifiedId::IK_Identifier) {
Diag(D.getName().StartLocation, diag::err_typedef_not_identifier)
<< D.getName().getSourceRange();
if (D.getName().Kind == UnqualifiedId::IK_DeductionGuideName)
Diag(D.getName().StartLocation,
diag::err_deduction_guide_invalid_specifier)
<< "typedef";
else
Diag(D.getName().StartLocation, diag::err_typedef_not_identifier)
<< D.getName().getSourceRange();
return nullptr;
}
@ -5989,8 +5994,7 @@ NamedDecl *Sema::ActOnVariableDeclarator(
Name = II;
}
} else if (!II) {
Diag(D.getIdentifierLoc(), diag::err_bad_variable_name)
<< Name;
Diag(D.getIdentifierLoc(), diag::err_bad_variable_name) << Name;
return nullptr;
}
@ -7517,6 +7521,7 @@ static StorageClass getFunctionStorageClass(Sema &SemaRef, Declarator &D) {
case DeclSpec::SCS_mutable:
SemaRef.Diag(D.getDeclSpec().getStorageClassSpecLoc(),
diag::err_typecheck_sclass_func);
D.getMutableDeclSpec().ClearStorageClassSpecs();
D.setInvalidType();
break;
case DeclSpec::SCS_unspecified: break;

View File

@ -8033,13 +8033,102 @@ Decl *Sema::ActOnConversionDeclarator(CXXConversionDecl *Conversion) {
return Conversion;
}
namespace {
/// Utility class to accumulate and print a diagnostic listing the invalid
/// specifier(s) on a declaration.
struct BadSpecifierDiagnoser {
BadSpecifierDiagnoser(Sema &S, SourceLocation Loc, unsigned DiagID)
: S(S), Diagnostic(S.Diag(Loc, DiagID)) {}
~BadSpecifierDiagnoser() {
Diagnostic << Specifiers;
}
template<typename T> void check(SourceLocation SpecLoc, T Spec) {
return check(SpecLoc, DeclSpec::getSpecifierName(Spec));
}
void check(SourceLocation SpecLoc, DeclSpec::TST Spec) {
return check(SpecLoc,
DeclSpec::getSpecifierName(Spec, S.getPrintingPolicy()));
}
void check(SourceLocation SpecLoc, const char *Spec) {
if (SpecLoc.isInvalid()) return;
Diagnostic << SourceRange(SpecLoc, SpecLoc);
if (!Specifiers.empty()) Specifiers += " ";
Specifiers += Spec;
}
Sema &S;
Sema::SemaDiagnosticBuilder Diagnostic;
std::string Specifiers;
};
}
/// Check the validity of a declarator that we parsed for a deduction-guide.
/// These aren't actually declarators in the grammar, so we need to check that
/// the user didn't specify any pieces that are not part of the deduction-guide
/// grammar.
void Sema::CheckDeductionGuideDeclarator(Declarator &D, QualType &R,
StorageClass &SC) {
// FIXME: Implement
auto &DS = D.getMutableDeclSpec();
// We leave 'friend' and 'virtual' to be rejected in the normal way.
if (DS.hasTypeSpecifier() || DS.getTypeQualifiers() ||
DS.getStorageClassSpecLoc().isValid() || DS.isInlineSpecified() ||
DS.isNoreturnSpecified() || DS.isConstexprSpecified() ||
DS.isConceptSpecified()) {
BadSpecifierDiagnoser Diagnoser(
*this, D.getIdentifierLoc(),
diag::err_deduction_guide_invalid_specifier);
Diagnoser.check(DS.getStorageClassSpecLoc(), DS.getStorageClassSpec());
DS.ClearStorageClassSpecs();
SC = SC_None;
// 'explicit' is permitted.
Diagnoser.check(DS.getInlineSpecLoc(), "inline");
Diagnoser.check(DS.getNoreturnSpecLoc(), "_Noreturn");
Diagnoser.check(DS.getConstexprSpecLoc(), "constexpr");
Diagnoser.check(DS.getConceptSpecLoc(), "concept");
DS.ClearConstexprSpec();
DS.ClearConceptSpec();
Diagnoser.check(DS.getConstSpecLoc(), "const");
Diagnoser.check(DS.getRestrictSpecLoc(), "__restrict");
Diagnoser.check(DS.getVolatileSpecLoc(), "volatile");
Diagnoser.check(DS.getAtomicSpecLoc(), "_Atomic");
Diagnoser.check(DS.getUnalignedSpecLoc(), "__unaligned");
DS.ClearTypeQualifiers();
Diagnoser.check(DS.getTypeSpecComplexLoc(), DS.getTypeSpecComplex());
Diagnoser.check(DS.getTypeSpecSignLoc(), DS.getTypeSpecSign());
Diagnoser.check(DS.getTypeSpecWidthLoc(), DS.getTypeSpecWidth());
Diagnoser.check(DS.getTypeSpecTypeLoc(), DS.getTypeSpecType());
DS.ClearTypeSpecType();
}
if (D.isInvalidType())
return;
// Check the declarator is simple enough.
bool FoundFunction = false;
for (const DeclaratorChunk &Chunk : llvm::reverse(D.type_objects())) {
if (Chunk.Kind == DeclaratorChunk::Paren)
continue;
if (Chunk.Kind != DeclaratorChunk::Function || FoundFunction) {
Diag(D.getDeclSpec().getLocStart(),
diag::err_deduction_guide_with_complex_decl)
<< D.getSourceRange();
break;
}
if (!Chunk.Fun.hasTrailingReturnType()) {
Diag(D.getName().getLocStart(),
diag::err_deduction_guide_no_trailing_return_type);
break;
}
FoundFunction = true;
}
// FIXME: Check that the return type can instantiate to a specialization of
// the template specified as the deduction-guide's name.
}
//===----------------------------------------------------------------------===//

View File

@ -2735,13 +2735,8 @@ static QualType GetDeclSpecTypeForDeclarator(TypeProcessingState &state,
case UnqualifiedId::IK_DeductionGuideName:
// Deduction guides have a trailing return type and no type in their
// decl-specifier sequence.
T = SemaRef.Context.getAutoDeductType();
if (!D.hasTrailingReturnType()) {
SemaRef.Diag(D.getName().getLocStart(),
diag::err_deduction_guide_no_trailing_return_type);
D.setInvalidType(true);
}
// decl-specifier sequence. Use a placeholder return type for now.
T = SemaRef.Context.DependentTy;
break;
case UnqualifiedId::IK_ConversionFunctionId:
@ -4181,18 +4176,21 @@ static TypeSourceInfo *GetFullTypeForDeclarator(TypeProcessingState &state,
diag::err_trailing_return_in_parens)
<< T << D.getSourceRange();
D.setInvalidType(true);
} else if (D.getName().getKind() ==
UnqualifiedId::IK_DeductionGuideName) {
if (T != Context.DependentTy) {
S.Diag(D.getDeclSpec().getLocStart(),
diag::err_deduction_guide_with_complex_decl)
<< D.getSourceRange();
D.setInvalidType(true);
}
} else if (D.getContext() != Declarator::LambdaExprContext &&
(T.hasQualifiers() || !isa<AutoType>(T) ||
cast<AutoType>(T)->getKeyword() !=
AutoTypeKeyword::Auto)) {
if (D.getName().getKind() == UnqualifiedId::IK_DeductionGuideName)
S.Diag(D.getDeclSpec().getLocStart(),
diag::err_deduction_guide_with_complex_decl)
<< D.getSourceRange();
else
S.Diag(D.getDeclSpec().getTypeSpecTypeLoc(),
diag::err_trailing_return_without_auto)
<< T << D.getDeclSpec().getSourceRange();
S.Diag(D.getDeclSpec().getTypeSpecTypeLoc(),
diag::err_trailing_return_without_auto)
<< T << D.getDeclSpec().getSourceRange();
D.setInvalidType(true);
}
T = S.GetTypeFromParser(FTI.getTrailingReturnType(), &TInfo);
@ -4206,7 +4204,7 @@ static TypeSourceInfo *GetFullTypeForDeclarator(TypeProcessingState &state,
// C99 6.7.5.3p1: The return type may not be a function or array type.
// For conversion functions, we'll diagnose this particular error later.
if ((T->isArrayType() || T->isFunctionType()) &&
if (!D.isInvalidType() && (T->isArrayType() || T->isFunctionType()) &&
(D.getName().getKind() != UnqualifiedId::IK_ConversionFunctionId)) {
unsigned diagID = diag::err_func_returning_array_function;
// Last processing chunk in block context means this function chunk
@ -4622,14 +4620,18 @@ static TypeSourceInfo *GetFullTypeForDeclarator(TypeProcessingState &state,
//
// Core issue 547 also allows cv-qualifiers on function types that are
// top-level template type arguments.
bool FreeFunction;
if (!D.getCXXScopeSpec().isSet()) {
FreeFunction = ((D.getContext() != Declarator::MemberContext &&
D.getContext() != Declarator::LambdaExprContext) ||
D.getDeclSpec().isFriendSpecified());
enum { NonMember, Member, DeductionGuide } Kind = NonMember;
if (D.getName().getKind() == UnqualifiedId::IK_DeductionGuideName)
Kind = DeductionGuide;
else if (!D.getCXXScopeSpec().isSet()) {
if ((D.getContext() == Declarator::MemberContext ||
D.getContext() == Declarator::LambdaExprContext) &&
!D.getDeclSpec().isFriendSpecified())
Kind = Member;
} else {
DeclContext *DC = S.computeDeclContext(D.getCXXScopeSpec());
FreeFunction = (DC && !DC->isRecord());
if (!DC || DC->isRecord())
Kind = Member;
}
// C++11 [dcl.fct]p6 (w/DR1417):
@ -4649,7 +4651,7 @@ static TypeSourceInfo *GetFullTypeForDeclarator(TypeProcessingState &state,
//
// ... for instance.
if (IsQualifiedFunction &&
!(!FreeFunction &&
!(Kind == Member &&
D.getDeclSpec().getStorageClassSpec() != DeclSpec::SCS_static) &&
!IsTypedefName &&
D.getContext() != Declarator::TemplateTypeArgContext) {
@ -4677,7 +4679,7 @@ static TypeSourceInfo *GetFullTypeForDeclarator(TypeProcessingState &state,
}
S.Diag(Loc, diag::err_invalid_qualified_function_type)
<< FreeFunction << D.isFunctionDeclarator() << T
<< Kind << D.isFunctionDeclarator() << T
<< getFunctionQualifiersAsString(FnTy)
<< FixItHint::CreateRemoval(RemovalRange);

View File

@ -51,7 +51,7 @@ decltype(auto) *f3(); // expected-error {{cannot form pointer to 'decltype(auto)
const decltype(auto) f4(); // expected-error {{'decltype(auto)' cannot be combined with other type specifiers}}
typedef decltype(auto) f5(); // expected-error {{'decltype(auto)' not allowed in typedef}}
decltype(auto) ((((((f6))))())); // ok
decltype(auto) f7()(); // expected-error {{'decltype(auto)' can only be used as a return type in a function declaration}} expected-error {{function cannot return function type}}
decltype(auto) f7()(); // expected-error {{'decltype(auto)' can only be used as a return type in a function declaration}}
decltype(auto) (S::*f8)(); // expected-error {{'decltype(auto)' can only be used as a return type in a function declaration}} expected-error {{requires an initializer}}
decltype(auto) &f9(); // expected-error {{cannot form reference to 'decltype(auto)'}}
decltype(auto) (&f10())[10]; // expected-error {{cannot form array of 'decltype(auto)'}}

View File

@ -16,11 +16,19 @@ explicit A(int(&)[2]) -> A<int>;
&A(int(&)[4]) -> A<int>; // expected-error {{cannot specify any part of a return type in the declaration of a deduction guide}}
A(int(&)[5])[3] -> A<int>;
#ifdef CLASS // FIXME: These diagnostics are both pretty bad.
// expected-error@-2 {{deduction guide declaration without trailing return type}} expected-error@-2 {{array of 'auto'}} expected-error@-2 {{';'}}
// expected-error@-2 {{function cannot return array type}} expected-error@-2 {{';'}}
#else
// expected-error@-4 {{expected function body after function declarator}}
#endif
(A[3])(int(&)[5][1]) -> A<int>; // expected-error {{'<deduction guide for A>' cannot be the name of a variable}}
#ifndef CLASS
// expected-error@-2 {{declared as array of functions}}
#endif
(*A)(int(&)[5][2]) -> A<int>; // expected-error {{'<deduction guide for A>' cannot be the name of a variable}}
(&A)(int(&)[5][3]) -> A<int>; // expected-error {{'<deduction guide for A>' cannot be the name of a variable}}
(*A(int))(int(&)[5][4]) -> A<int>; // expected-error {{cannot specify any part of a return type in the declaration of a deduction guide}}
// (Pending DR) attributes and parens around the declarator-id are OK.
[[deprecated]] A(int(&)[6]) [[]] -> A<int> [[]];
A [[]] (int(&)[7]) -> A<int>; // FIXME: expected-error 2{{expected}} expected-note {{to match}}
@ -43,30 +51,37 @@ int A(int) -> A<int>; // expected-error {{function with trailing return type mus
template<typename T> struct B {}; // expected-note {{here}}
auto B(int) -> B<int>; // expected-error {{redefinition of 'B' as different kind of symbol}}
// FIXME: No storage class specifier, function specifier, ...
// No storage class specifier, function specifier, ...
friend A(int(&)[20]) -> A<int>;
#ifdef CLASS
// expected-error@-2 {{cannot declare a deduction guide as a friend}}
#else
// expected-error@-4 {{'friend' used outside of class}}
#endif
typedef A(int(&)[21]) -> A<int>; // FIXME: Bad diagnostic: expected-error {{typedef name must be an identifier}}
constexpr A(int(&)[22]) -> A<int>;
inline A(int(&)[23]) -> A<int>;
static A(int(&)[24]) -> A<int>;
typedef A(int(&)[21]) -> A<int>; // expected-error {{deduction guide cannot be declared 'typedef'}}
constexpr A(int(&)[22]) -> A<int>; // expected-error {{deduction guide cannot be declared 'constexpr'}}
inline A(int(&)[23]) -> A<int>; // expected-error {{deduction guide cannot be declared 'inline'}}
static A(int(&)[24]) -> A<int>; // expected-error {{deduction guide cannot be declared 'static'}}
thread_local A(int(&)[25]) -> A<int>; // expected-error {{'thread_local' is only allowed on variable declarations}}
extern A(int(&)[26]) -> A<int>;
#ifdef CLASS
// expected-error@-2 {{storage class specified for a member}}
#else
// expected-error@-4 {{deduction guide cannot be declared 'extern'}}
#endif
mutable A(int(&)[27]) -> A<int>; // expected-error-re {{{{'mutable' cannot be applied to|illegal storage class on}} function}}
virtual A(int(&)[28]) -> A<int>; // expected-error {{'virtual' can only appear on non-static member functions}}
const A(int(&)[28]) -> A<int>; // expected-error {{deduction guide cannot be declared 'const'}}
const volatile static constexpr inline A(int(&)[29]) -> A<int>; // expected-error {{deduction guide cannot be declared 'static inline constexpr const volatile'}}
A(int(&)[30]) const -> A<int>; // expected-error {{deduction guide cannot have 'const' qualifier}}
// FIXME: No definition is allowed.
A(int(&)[30]) -> A<int> {}
A(int(&)[31]) -> A<int> = default; // expected-error {{only special member functions may be defaulted}}
A(int(&)[32]) -> A<int> = delete;
A(int(&)[33]) -> A<int> try {} catch (...) {}
A(int(&)[40]) -> A<int> {}
A(int(&)[41]) -> A<int> = default; // expected-error {{only special member functions may be defaulted}}
A(int(&)[42]) -> A<int> = delete;
A(int(&)[43]) -> A<int> try {} catch (...) {}
#ifdef CLASS
};