r294395 - P0091R3: Improved syntactic checking of deduction-guides.
Richard Smith via cfe-commits
cfe-commits at lists.llvm.org
Tue Feb 7 16:35:26 PST 2017
Author: rsmith
Date: Tue Feb 7 18:35:25 2017
New Revision: 294395
URL: http://llvm.org/viewvc/llvm-project?rev=294395&view=rev
Log:
P0091R3: Improved syntactic checking of deduction-guides.
Modified:
cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td
cfe/trunk/lib/AST/DeclarationName.cpp
cfe/trunk/lib/Sema/SemaDecl.cpp
cfe/trunk/lib/Sema/SemaDeclCXX.cpp
cfe/trunk/lib/Sema/SemaType.cpp
cfe/trunk/test/CXX/dcl.dcl/dcl.spec/dcl.type/dcl.spec.auto/p7-1y.cpp
cfe/trunk/test/CXX/temp/temp.deduct.guide/p1.cpp
Modified: cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td?rev=294395&r1=294394&r2=294395&view=diff
==============================================================================
--- cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td (original)
+++ cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td Tue Feb 7 18:35:25 2017
@@ -1966,6 +1966,8 @@ def err_deduction_guide_no_trailing_retu
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">;
Modified: cfe/trunk/lib/AST/DeclarationName.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/AST/DeclarationName.cpp?rev=294395&r1=294394&r2=294395&view=diff
==============================================================================
--- cfe/trunk/lib/AST/DeclarationName.cpp (original)
+++ cfe/trunk/lib/AST/DeclarationName.cpp Tue Feb 7 18:35:25 2017
@@ -203,7 +203,10 @@ void DeclarationName::print(raw_ostream
}
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] = {
Modified: cfe/trunk/lib/Sema/SemaDecl.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaDecl.cpp?rev=294395&r1=294394&r2=294395&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/SemaDecl.cpp (original)
+++ cfe/trunk/lib/Sema/SemaDecl.cpp Tue Feb 7 18:35:25 2017
@@ -5463,8 +5463,13 @@ Sema::ActOnTypedefDeclarator(Scope* S, D
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 getFunctionStorageCl
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;
Modified: cfe/trunk/lib/Sema/SemaDeclCXX.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaDeclCXX.cpp?rev=294395&r1=294394&r2=294395&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/SemaDeclCXX.cpp (original)
+++ cfe/trunk/lib/Sema/SemaDeclCXX.cpp Tue Feb 7 18:35:25 2017
@@ -8033,13 +8033,102 @@ Decl *Sema::ActOnConversionDeclarator(CX
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.
}
//===----------------------------------------------------------------------===//
Modified: cfe/trunk/lib/Sema/SemaType.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaType.cpp?rev=294395&r1=294394&r2=294395&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/SemaType.cpp (original)
+++ cfe/trunk/lib/Sema/SemaType.cpp Tue Feb 7 18:35:25 2017
@@ -2735,13 +2735,8 @@ static QualType GetDeclSpecTypeForDeclar
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 *GetFullTypeForDec
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 *GetFullTypeForDec
// 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 *GetFullTypeForDec
//
// 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 *GetFullTypeForDec
//
// ... for instance.
if (IsQualifiedFunction &&
- !(!FreeFunction &&
+ !(Kind == Member &&
D.getDeclSpec().getStorageClassSpec() != DeclSpec::SCS_static) &&
!IsTypedefName &&
D.getContext() != Declarator::TemplateTypeArgContext) {
@@ -4677,7 +4679,7 @@ static TypeSourceInfo *GetFullTypeForDec
}
S.Diag(Loc, diag::err_invalid_qualified_function_type)
- << FreeFunction << D.isFunctionDeclarator() << T
+ << Kind << D.isFunctionDeclarator() << T
<< getFunctionQualifiersAsString(FnTy)
<< FixItHint::CreateRemoval(RemovalRange);
Modified: cfe/trunk/test/CXX/dcl.dcl/dcl.spec/dcl.type/dcl.spec.auto/p7-1y.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CXX/dcl.dcl/dcl.spec/dcl.type/dcl.spec.auto/p7-1y.cpp?rev=294395&r1=294394&r2=294395&view=diff
==============================================================================
--- cfe/trunk/test/CXX/dcl.dcl/dcl.spec/dcl.type/dcl.spec.auto/p7-1y.cpp (original)
+++ cfe/trunk/test/CXX/dcl.dcl/dcl.spec/dcl.type/dcl.spec.auto/p7-1y.cpp Tue Feb 7 18:35:25 2017
@@ -51,7 +51,7 @@ decltype(auto) *f3(); // expected-error
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)'}}
Modified: cfe/trunk/test/CXX/temp/temp.deduct.guide/p1.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CXX/temp/temp.deduct.guide/p1.cpp?rev=294395&r1=294394&r2=294395&view=diff
==============================================================================
--- cfe/trunk/test/CXX/temp/temp.deduct.guide/p1.cpp (original)
+++ cfe/trunk/test/CXX/temp/temp.deduct.guide/p1.cpp Tue Feb 7 18:35:25 2017
@@ -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 at -2 {{deduction guide declaration without trailing return type}} expected-error at -2 {{array of 'auto'}} expected-error at -2 {{';'}}
+// expected-error at -2 {{function cannot return array type}} expected-error at -2 {{';'}}
#else
// expected-error at -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 at -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
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 at -2 {{cannot declare a deduction guide as a friend}}
#else
// expected-error at -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 at -2 {{storage class specified for a member}}
+#else
+// expected-error at -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
};
More information about the cfe-commits
mailing list