Rakete1111 updated this revision to Diff 172147.
Rakete1111 marked an inline comment as done.
Rakete1111 added a comment.
Remove spaces to be consistent.
Repository:
rC Clang
https://reviews.llvm.org/D53847
Files:
include/clang/Basic/DiagnosticSemaKinds.td
include/clang/Parse/Parser.h
include/clang/Sema/Scope.h
include/clang/Sema/Sema.h
lib/Parse/ParseDecl.cpp
lib/Parse/ParseDeclCXX.cpp
lib/Parse/ParseExprCXX.cpp
lib/Parse/ParseTentative.cpp
lib/Parse/Parser.cpp
lib/Sema/SemaDecl.cpp
lib/Sema/SemaLookup.cpp
lib/Sema/SemaTemplate.cpp
test/CXX/drs/dr1xx.cpp
test/CXX/drs/dr2xx.cpp
test/CXX/drs/dr4xx.cpp
test/CXX/drs/dr5xx.cpp
test/CXX/temp/temp.res/p5.cpp
test/CXX/temp/temp.res/temp.dep/temp.dep.type/p1.cpp
test/FixIt/fixit.cpp
test/SemaCXX/MicrosoftCompatibility.cpp
test/SemaCXX/MicrosoftExtensions.cpp
test/SemaCXX/MicrosoftSuper.cpp
test/SemaCXX/unknown-type-name.cpp
Index: test/SemaCXX/unknown-type-name.cpp
===================================================================
--- test/SemaCXX/unknown-type-name.cpp
+++ test/SemaCXX/unknown-type-name.cpp
@@ -36,39 +36,39 @@
static int n;
static type m;
- static int h(T::type, int); // expected-error{{missing 'typename'}}
- static int h(T::type x, char); // expected-error{{missing 'typename'}}
+ static int h(T::type, int); // expected-warning{{implicit 'typename' is a C++2a extension}}
+ static int h(T::type x, char); // expected-warning{{implicit 'typename' is a C++2a extension}}
};
template<typename T>
-A<T>::type g(T t) { return t; } // expected-error{{missing 'typename'}}
+A<T>::type g(T t) { return t; } // expected-warning{{implicit 'typename' is a C++2a extension}}
template<typename T>
-A<T>::type A<T>::f() { return type(); } // expected-error{{missing 'typename'}}
+A<T>::type A<T>::f() { return type(); } // expected-warning{{implicit 'typename' is a C++2a extension}}
template<typename T>
-void f(T::type) { } // expected-error{{missing 'typename'}}
+void f(T::type) { } // expected-warning{{implicit 'typename' is a C++2a extension}}
template<typename T>
-void g(T::type x) { } // expected-error{{missing 'typename'}}
+void g(T::type x) { } // expected-warning{{implicit 'typename' is a C++2a extension}}
template<typename T>
-void f(T::type, int) { } // expected-error{{missing 'typename'}}
+void f(T::type, int) { } // expected-warning{{implicit 'typename' is a C++2a extension}}
template<typename T>
-void f(T::type x, char) { } // expected-error{{missing 'typename'}}
+void f(T::type x, char) { } // expected-warning{{implicit 'typename' is a C++2a extension}}
template<typename T>
-void f(int, T::type) { } // expected-error{{missing 'typename'}}
+void f(int, T::type) { } // expected-warning{{implicit 'typename' is a C++2a extension}}
template<typename T>
-void f(char, T::type x) { } // expected-error{{missing 'typename'}}
+void f(char, T::type x) { } // expected-warning{{implicit 'typename' is a C++2a extension}}
template<typename T>
-void f(int, T::type, int) { } // expected-error{{missing 'typename'}}
+void f(int, T::type, int) { } // expected-warning{{implicit 'typename' is a C++2a extension}}
template<typename T>
-void f(int, T::type x, char) { } // expected-error{{missing 'typename'}}
+void f(int, T::type x, char) { } // expected-warning{{implicit 'typename' is a C++2a extension}}
int *p;
@@ -86,11 +86,11 @@
template<typename T> int A<T>::n(T::value); // ok
template<typename T>
-A<T>::type // expected-error{{missing 'typename'}}
+A<T>::type // expected-warning {{implicit 'typename' is a C++2a extension}}
A<T>::m(T::value, 0); // ok
-template<typename T> int A<T>::h(T::type, int) {} // expected-error{{missing 'typename'}}
-template<typename T> int A<T>::h(T::type x, char) {} // expected-error{{missing 'typename'}}
+template<typename T> int A<T>::h(T::type, int) {} // expected-warning{{implicit 'typename' is a C++2a extension}}
+template<typename T> int A<T>::h(T::type x, char) {} // expected-warning{{implicit 'typename' is a C++2a extension}}
template<typename T> int h(T::type, int); // expected-error{{missing 'typename'}}
template<typename T> int h(T::type x, char); // expected-error{{missing 'typename'}}
@@ -100,7 +100,7 @@
// expected-warning@-2 {{variable templates are a C++14 extension}}
#endif
template<typename T> int junk2(T::junk) throw(); // expected-error{{missing 'typename'}}
-template<typename T> int junk3(T::junk) = delete; // expected-error{{missing 'typename'}}
+template<typename T> int junk3(T::junk) = delete; // expected-warning{{implicit 'typename' is a C++2a extension}}
#if __cplusplus <= 199711L
//expected-warning@-2 {{deleted function definitions are a C++11 extension}}
#endif
@@ -118,4 +118,5 @@
// FIXME: We know which type specifier should have been specified here. Provide
// a fix-it to add 'typename A<T>::type'
template<typename T>
-A<T>::g() { } // expected-error{{requires a type specifier}}
+A<T>::g() { } // expected-error{{expected unqualified-id}}
+// expected-warning@-1{{implicit 'typename' is a C++2a extension}}
Index: test/SemaCXX/MicrosoftSuper.cpp
===================================================================
--- test/SemaCXX/MicrosoftSuper.cpp
+++ test/SemaCXX/MicrosoftSuper.cpp
@@ -108,8 +108,8 @@
typename __super::XXX a;
typedef typename __super::XXX b;
- __super::XXX c; // expected-error {{missing 'typename'}}
- typedef __super::XXX d; // expected-error {{missing 'typename'}}
+ __super::XXX c; // expected-warning {{implicit 'typename' is a C++2a extension}}
+ typedef __super::XXX d; // expected-warning {{implicit 'typename' is a C++2a extension}}
void foo() {
typename __super::XXX e;
@@ -127,8 +127,8 @@
typename __super::XXX a;
typedef typename __super::XXX b;
- __super::XXX c; // expected-error {{missing 'typename'}}
- typedef __super::XXX d; // expected-error {{missing 'typename'}}
+ __super::XXX c; // expected-warning {{implicit 'typename' is a C++2a extension}}
+ typedef __super::XXX d; // expected-warning {{implicit 'typename' is a C++2a extension}}
void foo() {
typename __super::XXX e;
Index: test/SemaCXX/MicrosoftExtensions.cpp
===================================================================
--- test/SemaCXX/MicrosoftExtensions.cpp
+++ test/SemaCXX/MicrosoftExtensions.cpp
@@ -526,7 +526,7 @@
namespace PR32750 {
template<typename T> struct A {};
-template<typename T> struct B : A<A<T>> { A<T>::C::D d; }; // expected-error {{missing 'typename' prior to dependent type name 'A<T>::C::D'}}
+template<typename T> struct B : A<A<T>> { A<T>::C::D d; }; // expected-warning {{implicit 'typename' is a C++2a extension}}
}
#else
Index: test/SemaCXX/MicrosoftCompatibility.cpp
===================================================================
--- test/SemaCXX/MicrosoftCompatibility.cpp
+++ test/SemaCXX/MicrosoftCompatibility.cpp
@@ -199,25 +199,25 @@
typedef B<U> Base2;
typedef A<U> Base3;
- A<T>::TYPE a1; // expected-warning {{missing 'typename' prior to dependent type name}}
- Base1::TYPE a2; // expected-warning {{missing 'typename' prior to dependent type name}}
+ A<T>::TYPE a1; // expected-warning {{implicit 'typename' is a C++2a extension}}
+ Base1::TYPE a2; // expected-warning {{implicit 'typename' is a C++2a extension}}
- B<U>::TYPE a3; // expected-warning {{missing 'typename' prior to dependent type name}}
- Base2::TYPE a4; // expected-warning {{missing 'typename' prior to dependent type name}}
+ B<U>::TYPE a3; // expected-warning {{implicit 'typename' is a C++2a extension}}
+ Base2::TYPE a4; // expected-warning {{implicit 'typename' is a C++2a extension}}
- A<U>::TYPE a5; // expected-error {{missing 'typename' prior to dependent type name}}
- Base3::TYPE a6; // expected-error {{missing 'typename' prior to dependent type name}}
+ A<U>::TYPE a5; // expected-warning {{implicit 'typename' is a C++2a extension}}
+ Base3::TYPE a6; // expected-warning {{implicit 'typename' is a C++2a extension}}
};
class D {
public:
typedef int Type;
};
template <class T>
-void function_missing_typename(const T::Type param)// expected-warning {{missing 'typename' prior to dependent type name}}
+void function_missing_typename(const T::Type param)// expected-warning {{implicit 'typename' is a C++2a extension}}
{
- const T::Type var = 2; // expected-warning {{missing 'typename' prior to dependent type name}}
+ const T::Type var = 2; // expected-warning {{missing 'typename'}}
}
template void function_missing_typename<D>(const D::Type param);
Index: test/FixIt/fixit.cpp
===================================================================
--- test/FixIt/fixit.cpp
+++ test/FixIt/fixit.cpp
@@ -211,7 +211,7 @@
template<class T> struct Mystery;
template<class T> typedef Mystery<T>::type getMysteriousThing() { // \
expected-error {{function definition declared 'typedef'}} \
- expected-error {{missing 'typename' prior to dependent}}
+ expected-warning {{implicit 'typename' is a C++2a extension}}
return Mystery<T>::get();
}
Index: test/CXX/temp/temp.res/temp.dep/temp.dep.type/p1.cpp
===================================================================
--- test/CXX/temp/temp.res/temp.dep/temp.dep.type/p1.cpp
+++ test/CXX/temp/temp.res/temp.dep/temp.dep.type/p1.cpp
@@ -17,7 +17,7 @@
template<class T> struct A<A<A<T>>> {
struct C {};
- B<B<T>>::C bc; // expected-error {{missing 'typename'}}
+ B<B<T>>::C bc; // expected-warning {{implicit 'typename' is a C++2a extension}}
};
}
Index: test/CXX/temp/temp.res/p5.cpp
===================================================================
--- /dev/null
+++ test/CXX/temp/temp.res/p5.cpp
@@ -0,0 +1,141 @@
+// RUN: %clang_cc1 -std=c++2a -pedantic -verify %s
+
+struct X {
+ using type = int;
+ static constexpr int value = 1;
+ class tclass {};
+};
+
+// [temp.res]p5
+// A qualified-id is assumed to name a type if
+
+template <typename T>
+void f() {
+ // it is a qualified name in a type-id-only context (see below), or
+ // [its smallest enclosing [/new/defining/]-type-id is]:
+ // - a new-type-id
+ auto *Ptr = new T::type();
+ // - a defining-type-id
+ class T::tclass Empty1;
+ T::tclass Empty2; // expected-error{{missing 'typename'}}
+ // - a trailing-return-type
+ auto f()->T::type;
+ // - default argument of a type-parameter of a template [see below]
+
+ // - type-id of a
+ // static_cast,
+ auto StaticCast = static_cast<T::type>(1.2);
+ // const_cast,
+ const auto *ConstCast = const_cast<const T::type *>(Ptr);
+ // reinterpret_cast,
+ int ReinterpretCast = reinterpret_cast<T::type>(4);
+ // dynamic_cast
+ struct B {
+ virtual ~B() = default;
+ };
+ struct D : T::tclass {};
+ auto *Base = dynamic_cast<T::tclass *>(new B);
+
+ T::type Invalid; // expected-error{{missing 'typename'}}
+}
+
+template void f<X>();
+
+// As default argument.
+template <typename T, typename = T::type>
+struct DefaultArg {};
+
+template struct DefaultArg<X>;
+
+// it is a decl-specifier of the decl-specifier-seq of a
+// - simple-declaration or a function-definition in namespace scope
+template <typename T>
+T::type VarTemp = 1;
+
+template int VarTemp<X>;
+
+template <typename T>
+T::type FuncDef() { return 1; }
+
+template int FuncDef<X>();
+
+template <typename T>
+T::type funcDecl();
+
+template <typename T>
+void FuncParam(T::type); // ok, but variable template
+// expected-error@-1{{variable has incomplete type 'void'}}
+
+template <typename T>
+void FuncParam2(const T::type, int); // expected-error{{missing 'typename'}}
+
+template <typename T>
+struct MemberDecl {
+ // member-declaration,
+ T::type Member;
+
+ // parameter-declaration in a member-declaration, unless that
+ // parameter-declaration appears in a default argument
+ void NoDefault(T::type);
+ void Default(int A = T::value);
+};
+
+template struct MemberDecl<X>;
+
+// parameter-declaration in a declarator of a function or function template
+// declaration where the declarator-id is qualified, unless that
+// parameter-declaration appears in a default argument,
+struct QualifiedFunc {
+ template <typename T>
+ void foo(typename T::type);
+ template <typename T>
+ void bar(T::type);
+};
+
+template <typename T>
+void QualifiedFunc::foo(T::type) {}
+template <typename T>
+void QualifiedFunc::bar(typename T::type) {}
+
+template <typename T>
+void g() {
+ // parameter-declaration in a lambda-declarator, unless that
+ // parameter-declaration appears in a default argument, or
+ auto Lambda1 = [](T::type) {};
+ auto Lambda2 = [](int A = T::value) {};
+}
+
+template void g<X>();
+
+// parameter-declaration of a (non-type) template-parameter.
+template <typename T, T::type>
+void NonTypeArg() {}
+
+template void NonTypeArg<X, 0>();
+
+template <typename T>
+void f(T::type) {}
+
+namespace N {
+ template <typename T>
+ int f(typename T::type);
+ template <typename T>
+ extern int Var;
+}
+
+template <typename T>
+int N::f(T::type); // ok, function
+template <typename T>
+int N::Var(T::value); // ok, variable
+
+int h() {
+ return N::f<X>(10) + N::Var<X>;
+}
+
+namespace NN {
+ inline namespace A { template <typename T> int f(typename T::type); } // expected-note{{previous definition is here}}
+ inline namespace B { template <typename T> int f(T::type); }
+}
+
+template <typename T>
+int NN::f(T::type); // expected-error{{redefinition of 'f' as different kind of symbol}}
Index: test/CXX/drs/dr5xx.cpp
===================================================================
--- test/CXX/drs/dr5xx.cpp
+++ test/CXX/drs/dr5xx.cpp
@@ -228,8 +228,8 @@
template<int N> struct X {
typedef int type;
X<N>::type v1;
- X<(N)>::type v2; // expected-error {{missing 'typename'}}
- X<+N>::type v3; // expected-error {{missing 'typename'}}
+ X<(N)>::type v2; // expected-error {{implicit 'typename' is a C++2a extension}}
+ X<+N>::type v3; // expected-error {{implicit 'typename' is a C++2a extension}}
};
}
Index: test/CXX/drs/dr4xx.cpp
===================================================================
--- test/CXX/drs/dr4xx.cpp
+++ test/CXX/drs/dr4xx.cpp
@@ -173,7 +173,7 @@
B b1;
A::B b2;
A<T>::B b3;
- A<T*>::B b4; // expected-error {{missing 'typename'}}
+ A<T*>::B b4; // expected-error {{implicit 'typename' is a C++2a extension}}
};
}
Index: test/CXX/drs/dr2xx.cpp
===================================================================
--- test/CXX/drs/dr2xx.cpp
+++ test/CXX/drs/dr2xx.cpp
@@ -242,53 +242,53 @@
typedef int type;
A::type a;
A<T>::type b;
- A<T*>::type c; // expected-error {{missing 'typename'}}
+ A<T*>::type c; // expected-error {{implicit 'typename' is a C++2a extension}}
::dr224::example1::A<T>::type d;
class B {
typedef int type;
A::type a;
A<T>::type b;
- A<T*>::type c; // expected-error {{missing 'typename'}}
+ A<T*>::type c; // expected-error {{implicit 'typename' is a C++2a extension}}
::dr224::example1::A<T>::type d;
B::type e;
A<T>::B::type f;
- A<T*>::B::type g; // expected-error {{missing 'typename'}}
+ A<T*>::B::type g; // expected-error {{implicit 'typename' is a C++2a extension}}
typename A<T*>::B::type h;
};
};
template <class T> class A<T*> {
typedef int type;
A<T*>::type a;
- A<T>::type b; // expected-error {{missing 'typename'}}
+ A<T>::type b; // expected-error {{implicit 'typename' is a C++2a extension}}
};
template <class T1, class T2, int I> struct B {
typedef int type;
B<T1, T2, I>::type b1;
- B<T2, T1, I>::type b2; // expected-error {{missing 'typename'}}
+ B<T2, T1, I>::type b2; // expected-error {{implicit 'typename' is a C++2a extension}}
typedef T1 my_T1;
static const int my_I = I;
static const int my_I2 = I+0;
static const int my_I3 = my_I;
- B<my_T1, T2, my_I>::type b3; // FIXME: expected-error {{missing 'typename'}}
- B<my_T1, T2, my_I2>::type b4; // expected-error {{missing 'typename'}}
- B<my_T1, T2, my_I3>::type b5; // FIXME: expected-error {{missing 'typename'}}
+ B<my_T1, T2, my_I>::type b3; // FIXME: expected-error {{implicit 'typename' is a C++2a extension}}
+ B<my_T1, T2, my_I2>::type b4; // expected-error {{implicit 'typename' is a C++2a extension}}
+ B<my_T1, T2, my_I3>::type b5; // FIXME: expected-error {{implicit 'typename' is a C++2a extension}}
};
}
namespace example2 {
template <int, typename T> struct X { typedef T type; };
template <class T> class A {
static const int i = 5;
- X<i, int>::type w; // FIXME: expected-error {{missing 'typename'}}
- X<A::i, char>::type x; // FIXME: expected-error {{missing 'typename'}}
- X<A<T>::i, double>::type y; // FIXME: expected-error {{missing 'typename'}}
- X<A<T*>::i, long>::type z; // expected-error {{missing 'typename'}}
+ X<i, int>::type w; // FIXME: expected-error {{implicit 'typename' is a C++2a extension}}
+ X<A::i, char>::type x; // FIXME: expected-error {{implicit 'typename' is a C++2a extension}}
+ X<A<T>::i, double>::type y; // FIXME: expected-error {{implicit 'typename' is a C++2a extension}}
+ X<A<T*>::i, long>::type z; // expected-error {{implicit 'typename' is a C++2a extension}}
int f();
};
template <class T> int A<T>::f() {
Index: test/CXX/drs/dr1xx.cpp
===================================================================
--- test/CXX/drs/dr1xx.cpp
+++ test/CXX/drs/dr1xx.cpp
@@ -58,7 +58,7 @@
namespace dr108 { // dr108: yes
template<typename T> struct A {
struct B { typedef int X; };
- B::X x; // expected-error {{missing 'typename'}}
+ B::X x; // expected-error {{implicit 'typename' is a C++2a extension}}
struct C : B { X x; }; // expected-error {{unknown type name}}
};
template<> struct A<int>::B { int X; };
Index: lib/Sema/SemaTemplate.cpp
===================================================================
--- lib/Sema/SemaTemplate.cpp
+++ lib/Sema/SemaTemplate.cpp
@@ -9413,7 +9413,7 @@
TypeResult
Sema::ActOnTypenameType(Scope *S, SourceLocation TypenameLoc,
const CXXScopeSpec &SS, const IdentifierInfo &II,
- SourceLocation IdLoc) {
+ SourceLocation IdLoc, bool IsImplicitTypename) {
if (SS.isInvalid())
return true;
@@ -9425,8 +9425,9 @@
<< FixItHint::CreateRemoval(TypenameLoc);
NestedNameSpecifierLoc QualifierLoc = SS.getWithLocInContext(Context);
- QualType T = CheckTypenameType(TypenameLoc.isValid()? ETK_Typename : ETK_None,
- TypenameLoc, QualifierLoc, II, IdLoc);
+ QualType T = CheckTypenameType(
+ TypenameLoc.isValid() || IsImplicitTypename ? ETK_Typename : ETK_None,
+ TypenameLoc, QualifierLoc, II, IdLoc);
if (T.isNull())
return true;
Index: lib/Sema/SemaLookup.cpp
===================================================================
--- lib/Sema/SemaLookup.cpp
+++ lib/Sema/SemaLookup.cpp
@@ -950,12 +950,6 @@
return Found;
}
-static bool isNamespaceOrTranslationUnitScope(Scope *S) {
- if (DeclContext *Ctx = S->getEntity())
- return Ctx->isFileContext();
- return false;
-}
-
// Find the next outer declaration context from this scope. This
// routine actually returns the semantic outer context, which may
// differ from the lexical context (encoded directly in the Scope
@@ -1094,7 +1088,7 @@
// When performing a scope lookup, we want to find local extern decls.
FindLocalExternScope FindLocals(R);
- for (; S && !isNamespaceOrTranslationUnitScope(S); S = S->getParent()) {
+ for (; S && !S->isNamespaceOrTranslationUnitScope(); S = S->getParent()) {
DeclContext *Ctx = S->getEntity();
bool SearchNamespaceScope = true;
// Check whether the IdResolver has anything in this scope.
@@ -1208,7 +1202,7 @@
// from local scopes.
Scope *InnermostFileScope = S;
while (InnermostFileScope &&
- !isNamespaceOrTranslationUnitScope(InnermostFileScope))
+ !InnermostFileScope->isNamespaceOrTranslationUnitScope())
InnermostFileScope = InnermostFileScope->getParent();
UDirs.visitScopeChain(Initial, InnermostFileScope);
@@ -1790,7 +1784,7 @@
// If the scope containing the declaration is the translation unit,
// then we'll need to perform our checks based on the matching
// DeclContexts rather than matching scopes.
- if (S && isNamespaceOrTranslationUnitScope(S))
+ if (S && S->isNamespaceOrTranslationUnitScope())
S = nullptr;
// Compute the DeclContext, if we need it.
@@ -3782,7 +3776,7 @@
UnqualUsingDirectiveSet UDirs(*this);
if (getLangOpts().CPlusPlus) {
// Find the first namespace or translation-unit scope.
- while (S && !isNamespaceOrTranslationUnitScope(S))
+ while (S && !S->isNamespaceOrTranslationUnitScope())
S = S->getParent();
UDirs.visitScopeChain(Initial, S);
Index: lib/Sema/SemaDecl.cpp
===================================================================
--- lib/Sema/SemaDecl.cpp
+++ lib/Sema/SemaDecl.cpp
@@ -281,6 +281,7 @@
bool IsCtorOrDtorName,
bool WantNontrivialTypeSourceInfo,
bool IsClassTemplateDeductionContext,
+ bool AllowImplicitTypename,
IdentifierInfo **CorrectedII) {
// FIXME: Consider allowing this outside C++1z mode as an extension.
bool AllowDeducedTemplate = IsClassTemplateDeductionContext &&
@@ -307,17 +308,37 @@
//
// We therefore do not perform any name lookup if the result would
// refer to a member of an unknown specialization.
- if (!isClassName && !IsCtorOrDtorName)
+ // In C++2a, in several contexts a 'typename' is not required. Also
+ // allow this as an extension.
+ if (!AllowImplicitTypename && !isClassName && !IsCtorOrDtorName)
return nullptr;
+ bool IsImplicitTypename = !isClassName && !IsCtorOrDtorName;
+
+ // We need to delay this diagnostic for function parameters, because
+ // at that point we don't know whether we have a function-definition
+ // (where implicit typename is allowed) or a unqualified function
+ // declaration (where implicit typename is not allowed).
+ if (IsImplicitTypename && !S->isFunctionPrototypeScope()) {
+ SourceLocation QualifiedLoc = SS->getRange().getBegin();
+ if (getLangOpts().CPlusPlus2a)
+ Diag(QualifiedLoc,
+ diag::warn_cxx17_compat_implicit_typename);
+ else
+ Diag(QualifiedLoc, diag::ext_implicit_typename)
+ << FixItHint::CreateInsertion(QualifiedLoc, "typename ");
+ }
// We know from the grammar that this name refers to a type,
// so build a dependent node to describe the type.
if (WantNontrivialTypeSourceInfo)
- return ActOnTypenameType(S, SourceLocation(), *SS, II, NameLoc).get();
+ return ActOnTypenameType(S, SourceLocation(), *SS, II, NameLoc,
+ IsImplicitTypename)
+ .get();
NestedNameSpecifierLoc QualifierLoc = SS->getWithLocInContext(Context);
- QualType T = CheckTypenameType(ETK_None, SourceLocation(), QualifierLoc,
- II, NameLoc);
+ QualType T =
+ CheckTypenameType(IsImplicitTypename ? ETK_Typename : ETK_None,
+ SourceLocation(), QualifierLoc, II, NameLoc);
return ParsedType::make(T);
}
@@ -395,7 +416,8 @@
isClassName, HasTrailingDot, ObjectTypePtr,
IsCtorOrDtorName,
WantNontrivialTypeSourceInfo,
- IsClassTemplateDeductionContext);
+ IsClassTemplateDeductionContext,
+ AllowImplicitTypename);
if (Ty) {
diagnoseTypo(Correction,
PDiag(diag::err_unknown_type_or_class_name_suggest)
@@ -9108,11 +9130,71 @@
// marking the function.
AddCFAuditedAttribute(NewFD);
- // If this is a function definition, check if we have to apply optnone due to
- // a pragma.
- if(D.isFunctionDefinition())
+ auto HasParamImplicitTypename = [this](ParmVarDecl *Param, bool Diagnostics) {
+ if (const auto *TSI = Param->getTypeSourceInfo()) {
+ if (const auto DLoc = TSI->getTypeLoc()
+ .getUnqualifiedLoc()
+ .getAs<DependentNameTypeLoc>()) {
+ auto *DependentName =
+ cast<DependentNameType>(Param->getType().getTypePtr());
+ if (DependentName->getKeyword() == ETK_Typename &&
+ DLoc.getElaboratedKeywordLoc().isInvalid()) {
+ if (Diagnostics)
+ Diag(Param->getBeginLoc(), diag::err_typename_missing)
+ << DependentName->getQualifier()
+ << DependentName->getIdentifier()->getName();
+ return true;
+ }
+ }
+ }
+ return false;
+ };
+ auto DiagnoseImplicitTypename =
+ [this, &HasParamImplicitTypename](
+ const DeclaratorChunk::FunctionTypeInfo &FuncInfo) {
+ for (std::size_t I = 0, Size = FuncInfo.NumParams; I < Size; ++I) {
+ if (HasParamImplicitTypename(
+ cast<ParmVarDecl>(FuncInfo.Params[I].Param),
+ /*Diagnostics=*/false)) {
+ SourceLocation NameLoc = FuncInfo.Params[I].Param->getBeginLoc();
+ if (getLangOpts().CPlusPlus2a)
+ Diag(NameLoc,
+ diag::warn_cxx17_compat_implicit_typename);
+ else
+ Diag(NameLoc, diag::ext_implicit_typename)
+ << FixItHint::CreateInsertion(NameLoc, "typename ");
+ }
+ }
+ };
+
+ // If this is a function definition, check if we have to apply optnone due
+ // to a pragma.
+ if (D.isFunctionDefinition()) {
AddRangeBasedOptnone(NewFD);
+ DeclaratorChunk::FunctionTypeInfo FuncInfo = D.getFunctionTypeInfo();
+ // We delayed the diagnostic for implicit typename. Do it now.
+ DiagnoseImplicitTypename(FuncInfo);
+ } else if (D.isFunctionDeclarator()) {
+ // We also need to diagnose a missing 'typename' here.
+ // template <typename T> void f(T::type, int);
+ // But only if 1) the function is unqualified and 2) it's at namespace
+ // scope.
+ DeclaratorChunk::FunctionTypeInfo FuncInfo = D.getFunctionTypeInfo();
+ if (!D.getCXXScopeSpec().isSet() &&
+ S->isNamespaceOrTranslationUnitScope()) {
+ // If it finds an implicit typename, it gets diagnosed.
+ std::any_of(
+ FuncInfo.Params, FuncInfo.Params + FuncInfo.NumParams,
+ [&HasParamImplicitTypename](const DeclaratorChunk::ParamInfo &PInfo) {
+ return PInfo.Param &&
+ HasParamImplicitTypename(cast<ParmVarDecl>(PInfo.Param),
+ /*Diagnostics=*/true);
+ });
+ } else if (S->isClassScope())
+ DiagnoseImplicitTypename(FuncInfo);
+ }
+
// If this is the first declaration of an extern C variable, update
// the map of such variables.
if (NewFD->isFirstDecl() && !NewFD->isInvalidDecl() &&
Index: lib/Parse/Parser.cpp
===================================================================
--- lib/Parse/Parser.cpp
+++ lib/Parse/Parser.cpp
@@ -1773,8 +1773,9 @@
*Tok.getIdentifierInfo(), Tok.getLocation(), getCurScope(), &SS,
false, NextToken().is(tok::period), nullptr,
/*IsCtorOrDtorName=*/false,
- /*NonTrivialTypeSourceInfo*/true,
- /*IsClassTemplateDeductionContext*/true)) {
+ /*NonTrivialTypeSourceInfo*/ true,
+ /*IsClassTemplateDeductionContext*/ true,
+ /*AllowImplicitTypename*/false)) {
SourceLocation BeginLoc = Tok.getLocation();
if (SS.isNotEmpty()) // it was a C++ qualified type name.
BeginLoc = SS.getBeginLoc();
Index: lib/Parse/ParseTentative.cpp
===================================================================
--- lib/Parse/ParseTentative.cpp
+++ lib/Parse/ParseTentative.cpp
@@ -1788,9 +1788,14 @@
// and can appear next in a function definition. This must be a function
// declarator.
TPR = TPResult::True;
- else if (InvalidAsDeclaration)
+ else if (InvalidAsDeclaration) {
// Use the absence of 'typename' as a tie-breaker.
TPR = TPResult::False;
+ // But still mark the declaration as ambiguous, as the tie-breakter is
+ // not perfect.
+ if (IsAmbiguous)
+ *IsAmbiguous = true;
+ }
}
}
Index: lib/Parse/ParseExprCXX.cpp
===================================================================
--- lib/Parse/ParseExprCXX.cpp
+++ lib/Parse/ParseExprCXX.cpp
@@ -1351,7 +1351,8 @@
// Parse the common declaration-specifiers piece.
DeclSpec DS(AttrFactory);
- ParseSpecifierQualifierList(DS);
+ ParseSpecifierQualifierList(DS, /*AccessSpecifier=*/AS_none,
+ DeclSpecContext::DSC_type_specifier);
// Parse the abstract-declarator, if present.
Declarator DeclaratorInfo(DS, DeclaratorContext::TypeNameContext);
Index: lib/Parse/ParseDeclCXX.cpp
===================================================================
--- lib/Parse/ParseDeclCXX.cpp
+++ lib/Parse/ParseDeclCXX.cpp
@@ -1175,7 +1175,8 @@
*Id, IdLoc, getCurScope(), &SS, /*IsClassName=*/true, false, nullptr,
/*IsCtorOrDtorName=*/false,
/*NonTrivialTypeSourceInfo=*/true,
- /*IsClassTemplateDeductionContext*/ false, &CorrectedII);
+ /*IsClassTemplateDeductionContext*/ false,
+ /*AllowImplicitTypename*/true, &CorrectedII);
if (!Type) {
Diag(IdLoc, diag::err_expected_class_name);
return true;
Index: lib/Parse/ParseDecl.cpp
===================================================================
--- lib/Parse/ParseDecl.cpp
+++ lib/Parse/ParseDecl.cpp
@@ -2691,6 +2691,16 @@
if (Context == DeclaratorContext::AliasDeclContext ||
Context == DeclaratorContext::AliasTemplateContext)
return DeclSpecContext::DSC_alias_declaration;
+ if (Context == DeclaratorContext::BlockContext ||
+ Context == DeclaratorContext::ForContext ||
+ Context == DeclaratorContext::InitStmtContext ||
+ Context == DeclaratorContext::LambdaExprContext ||
+ Context == DeclaratorContext::CXXCatchContext)
+ return DeclSpecContext::DSC_block;
+ if (Context == DeclaratorContext::TypeNameContext)
+ return DeclSpecContext::DSC_type_specifier;
+ if (Context == DeclaratorContext::ConditionContext)
+ return DeclSpecContext::DSC_condition;
return DeclSpecContext::DSC_normal;
}
@@ -3101,7 +3111,8 @@
getCurScope(), &SS, false, false, nullptr,
/*IsCtorOrDtorName=*/false,
/*WantNonTrivialSourceInfo=*/true,
- isClassTemplateDeductionContext(DSContext));
+ isClassTemplateDeductionContext(DSContext),
+ isImplicitTypenameContext(DSContext));
// If the referenced identifier is not a type, then this declspec is
// erroneous: We already checked about that it has no type specifier, and
@@ -3231,7 +3242,6 @@
if (DSContext == DeclSpecContext::DSC_objc_method_result &&
isObjCInstancetype()) {
ParsedType TypeRep = Actions.ActOnObjCInstanceType(Loc);
- assert(TypeRep);
isInvalid = DS.SetTypeSpecType(DeclSpec::TST_typename, Loc, PrevSpec,
DiagID, TypeRep, Policy);
if (isInvalid)
@@ -5828,6 +5838,24 @@
TentativelyDeclaredIdentifiers.push_back(D.getIdentifier());
bool IsFunctionDecl = isCXXFunctionDeclarator(&IsAmbiguous);
TentativelyDeclaredIdentifiers.pop_back();
+
+ if (!IsFunctionDecl && IsAmbiguous) {
+ // We have an ambiguity between a function and a variable:
+ // template <typename T> void N::f(T::type) [...]
+ // but only for qualified names.
+ if (D.getCXXScopeSpec().isSet()) {
+ LookupResult LR(Actions, D.getIdentifier(), D.getBeginLoc(),
+ Sema::LookupOrdinaryName,
+ Sema::ForVisibleRedeclaration);
+ DeclContext *DC = Actions.computeDeclContext(D.getCXXScopeSpec());
+ assert(DC && "couldn't find decl context of qualified name");
+ Actions.LookupQualifiedName(LR, DC);
+
+ for (Decl *D : LR)
+ IsFunctionDecl =
+ isa<FunctionDecl>(D) || isa<FunctionTemplateDecl>(D);
+ }
+ }
if (!IsFunctionDecl)
break;
}
Index: include/clang/Sema/Sema.h
===================================================================
--- include/clang/Sema/Sema.h
+++ include/clang/Sema/Sema.h
@@ -1698,6 +1698,7 @@
bool IsCtorOrDtorName = false,
bool WantNontrivialTypeSourceInfo = false,
bool IsClassTemplateDeductionContext = true,
+ bool AllowImplicitTypename = false,
IdentifierInfo **CorrectedII = nullptr);
TypeSpecifierType isTagName(IdentifierInfo &II, Scope *S);
bool isMicrosoftMissingTypename(const CXXScopeSpec *SS, Scope *S);
@@ -6486,10 +6487,11 @@
/// \param SS the nested-name-specifier following the typename (e.g., 'T::').
/// \param II the identifier we're retrieving (e.g., 'type' in the example).
/// \param IdLoc the location of the identifier.
- TypeResult
- ActOnTypenameType(Scope *S, SourceLocation TypenameLoc,
- const CXXScopeSpec &SS, const IdentifierInfo &II,
- SourceLocation IdLoc);
+ /// \param IsImplicitTypename whether the 'typename' is implicit.
+ TypeResult ActOnTypenameType(Scope *S, SourceLocation TypenameLoc,
+ const CXXScopeSpec &SS, const IdentifierInfo &II,
+ SourceLocation IdLoc,
+ bool IsImplicitTypename = false);
/// Called when the parser has parsed a C++ typename
/// specifier that ends in a template-id, e.g.,
Index: include/clang/Sema/Scope.h
===================================================================
--- include/clang/Sema/Scope.h
+++ include/clang/Sema/Scope.h
@@ -14,6 +14,7 @@
#ifndef LLVM_CLANG_SEMA_SCOPE_H
#define LLVM_CLANG_SEMA_SCOPE_H
+#include "clang/AST/DeclBase.h"
#include "clang/Basic/Diagnostic.h"
#include "llvm/ADT/PointerIntPair.h"
#include "llvm/ADT/SmallPtrSet.h"
@@ -30,7 +31,6 @@
namespace clang {
class Decl;
-class DeclContext;
class UsingDirectiveDecl;
class VarDecl;
@@ -444,6 +444,13 @@
return getFlags() & Scope::CompoundStmtScope;
}
+ /// Determine whether this scope is a namespace (or TU) scope.
+ bool isNamespaceOrTranslationUnitScope() const {
+ if (Entity)
+ return Entity->isFileContext();
+ return false;
+ }
+
/// Returns if rhs has a higher scope depth than this.
///
/// The caller is responsible for calling this only if one of the two scopes
Index: include/clang/Parse/Parser.h
===================================================================
--- include/clang/Parse/Parser.h
+++ include/clang/Parse/Parser.h
@@ -1994,7 +1994,8 @@
DSC_template_param, // template parameter context
DSC_template_type_arg, // template type argument context
DSC_objc_method_result, // ObjC method result context, enables 'instancetype'
- DSC_condition // condition declaration context
+ DSC_condition, // condition declaration context
+ DSC_block, // declaration within a block
};
/// Is this a context in which we are parsing just a type-specifier (or
@@ -2007,6 +2008,7 @@
case DeclSpecContext::DSC_top_level:
case DeclSpecContext::DSC_objc_method_result:
case DeclSpecContext::DSC_condition:
+ case DeclSpecContext::DSC_block:
return false;
case DeclSpecContext::DSC_template_type_arg:
@@ -2028,6 +2030,7 @@
case DeclSpecContext::DSC_top_level:
case DeclSpecContext::DSC_condition:
case DeclSpecContext::DSC_type_specifier:
+ case DeclSpecContext::DSC_block:
return true;
case DeclSpecContext::DSC_objc_method_result:
@@ -2039,6 +2042,27 @@
llvm_unreachable("Missing DeclSpecContext case");
}
+ /// Context where an implicit typename is allowed.
+ static bool isImplicitTypenameContext(DeclSpecContext DSC) {
+ switch (DSC) {
+ case DeclSpecContext::DSC_class:
+ case DeclSpecContext::DSC_type_specifier:
+ case DeclSpecContext::DSC_trailing:
+ case DeclSpecContext::DSC_alias_declaration:
+ case DeclSpecContext::DSC_top_level:
+ case DeclSpecContext::DSC_template_param:
+ case DeclSpecContext::DSC_template_type_arg:
+ case DeclSpecContext::DSC_normal:
+ return true;
+
+ case DeclSpecContext::DSC_objc_method_result:
+ case DeclSpecContext::DSC_condition:
+ case DeclSpecContext::DSC_block:
+ return false;
+ }
+ llvm_unreachable("Missing DeclSpecContext case");
+ }
+
/// Information on a C++0x for-range-initializer found while parsing a
/// declaration which turns out to be a for-range-declaration.
struct ForRangeInit {
Index: include/clang/Basic/DiagnosticSemaKinds.td
===================================================================
--- include/clang/Basic/DiagnosticSemaKinds.td
+++ include/clang/Basic/DiagnosticSemaKinds.td
@@ -4456,6 +4456,11 @@
"%0 in %1">;
def note_using_value_decl_missing_typename : Note<
"add 'typename' to treat this using declaration as a type">;
+def warn_cxx17_compat_implicit_typename : Warning<"use of implicit 'typename' is "
+ "incompatible with C++ standards before C++2a">, InGroup<CXX2aCompat>,
+ DefaultIgnore;
+def ext_implicit_typename : ExtWarn<"implicit 'typename' is a C++2a extension">,
+ InGroup<CXX2a>;
def err_template_kw_refers_to_non_template : Error<
"%0 following the 'template' keyword does not refer to a template">;
_______________________________________________
cfe-commits mailing list
[email protected]
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits