-
Notifications
You must be signed in to change notification settings - Fork 13.4k
Accepts-invalid with dependent name not prefixed with typename #17283
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Comments
This can also cause assertion failures or generate invalid IR: template<typename T> struct A {
template<typename U> struct B {};
};
struct C {};
template<typename T> A<T>::B<T> begin(const T &);
void test() {
auto __begin = begin(5); // asserts because we didn't deduce the type of __begin
for (auto &&x : C()); // without assertions, will produce invalid LLVM IR
} |
This is also tracked as rdar://problem/19438432. Ben proposed a patch: |
Diagnostics is emitted since Clang 5: https://godbolt.org/z/1K786Tan4 |
The following still crashes: template<typename T> struct A {
template<typename U> struct B {};
};
struct C {};
template<typename T> A<T>::B<T> begin(const T &);
void test() {
auto __begin = begin(5); // asserts because we didn't deduce the type of __begin
} |
Crash appears to be fixed in Clang 5: https://godbolt.org/z/bbsvE68MT |
@llvm/issue-subscribers-clang-frontend |
I think what happened is that we added a check to reject this... but when we implemented the C++20 typename rules in https://reviews.llvm.org/D53847, the check was relaxed, so this issue showed up again. |
@sdkrystian Could you look at this one? |
@cor3ntin So, the problem here is that we parse & annotate the nested-name-specifier |
FWIW, this issue still happens on trunk: https://godbolt.org/z/6jhGacv5n CC @sdkrystian |
I tried the "obvious" thing, following the lead of #96364, but it doesn't really work... I mean, it kind of works, but there's a big issue: if we parse twice, that means we also generate all the diagnostics twice. Which is not what we want. diff --git a/clang/lib/Parse/ParseDecl.cpp b/clang/lib/Parse/ParseDecl.cpp
index 9ca3e2b..61019a9 100644
--- a/clang/lib/Parse/ParseDecl.cpp
+++ b/clang/lib/Parse/ParseDecl.cpp
@@ -4035,27 +4035,45 @@ void Parser::ParseDeclarationSpecifiers(
}
}
- // In C++, check to see if this is a scope specifier like foo::bar::, if
- // so handle it as such. This is important for ctor parsing.
- if (getLangOpts().CPlusPlus) {
- // C++20 [temp.spec] 13.9/6.
- // This disables the access checking rules for function template
- // explicit instantiation and explicit specialization:
- // - `return type`.
- SuppressAccessChecks SAC(*this, IsTemplateSpecOrInst);
-
- const bool Success = TryAnnotateCXXScopeToken(EnteringContext);
-
- if (IsTemplateSpecOrInst)
- SAC.done();
-
- if (Success) {
- if (IsTemplateSpecOrInst)
- SAC.redelay();
+ // In C++, this could be a scope specifier followed by an identifier
+ // naming a constructor. But we need to be careful because it could
+ // also just be a dependent type.
+ if (getLangOpts().CPlusPlus && (DSContext == DeclSpecContext::DSC_top_level ||
+ DSContext == DeclSpecContext::DSC_class)) {
+ TentativeParsingAction TPA(*this, /*Unannotated=*/true);
+ CXXScopeSpec SS;
+
+ if (ParseOptionalCXXScopeSpecifier(SS, /*ObjectType=*/nullptr,
+ /*ObjectHasErrors=*/false,
+ /*EnteringContext*/true,
+ /*MayBePseudoDestructor=*/nullptr,
+ /*IsTypename=*/false, /*LastII=*/nullptr,
+ /*OnlyNamespace=*/false,
+ /*InUsingDeclaration=*/false,
+ /*Disambiguation=*/true)) {
DS.SetTypeSpecError();
+ TPA.Commit();
goto DoneWithDeclSpec;
}
+ if (!SS.isEmpty()) {
+ AnnotateScopeToken(SS, true);
+ if (NextToken().is(tok::identifier) &&
+ Actions.isCurrentClassName(*NextToken().getIdentifierInfo(), getCurScope(),
+ &SS) &&
+ isConstructorDeclarator(/*Unqualified=*/false,
+ /*DeductionGuide=*/false,
+ DS.isFriendSpecified(),
+ &TemplateInfo)) {
+ TPA.Commit();
+ goto DoneWithDeclSpec;
+ }
+ }
+ TPA.Revert();
+ }
+
+ if (getLangOpts().CPlusPlus) {
+ TryAnnotateCXXScopeToken(/*EnteringContext*/false);
if (!Tok.is(tok::identifier))
continue;
} |
Related testcases: template<typename T> class A { class B; };
template<typename T> class A<T>::B::C f();
template<typename T> void f(enum A<T>::B::C); |
Extended Description
Testcase:
We should print an error about the missing
typename
and template keywords.The text was updated successfully, but these errors were encountered: