diff --git a/clang/include/clang/Parse/Parser.h b/clang/include/clang/Parse/Parser.h --- a/clang/include/clang/Parse/Parser.h +++ b/clang/include/clang/Parse/Parser.h @@ -21,6 +21,7 @@ #include "clang/Lex/CodeCompletionHandler.h" #include "clang/Lex/Preprocessor.h" #include "clang/Sema/DeclSpec.h" +#include "clang/Sema/DelayedDiagnostic.h" #include "clang/Sema/Sema.h" #include "llvm/ADT/SmallVector.h" #include "llvm/Frontend/OpenMP/OMPContext.h" @@ -3486,6 +3487,7 @@ bool isGNUAsmQualifier(const Token &TokAfterAsm) const; GNUAsmQualifiers::AQ getGNUAsmQualifier(const Token &Tok) const; bool parseGNUAsmQualifierListOpt(GNUAsmQualifiers &AQ); + void RemoveDiagnosticsFromPool(clang::sema::DelayedDiagnostic::DDKind K); }; } // end namespace clang diff --git a/clang/include/clang/Sema/DelayedDiagnostic.h b/clang/include/clang/Sema/DelayedDiagnostic.h --- a/clang/include/clang/Sema/DelayedDiagnostic.h +++ b/clang/include/clang/Sema/DelayedDiagnostic.h @@ -312,6 +312,14 @@ pool.Diagnostics.clear(); } + void removeAllOfKind(DelayedDiagnostic::DDKind Kind) { + Diagnostics.erase(std::remove_if(Diagnostics.begin(), Diagnostics.end(), + [Kind](const DelayedDiagnostic &DD) { + return DD.Kind == Kind; + }), + Diagnostics.end()); + } + using pool_iterator = SmallVectorImpl::const_iterator; pool_iterator pool_begin() const { return Diagnostics.begin(); } diff --git a/clang/lib/Parse/ParseDecl.cpp b/clang/lib/Parse/ParseDecl.cpp --- a/clang/lib/Parse/ParseDecl.cpp +++ b/clang/lib/Parse/ParseDecl.cpp @@ -3071,6 +3071,19 @@ SourceLocation Loc = Tok.getLocation(); + // Turn off usual access checking for template specializations and + // instantiations. + // We don't use `SuppressAccessChecks` here + // because it suppresses ALL kinds of diagnostics + // but we need to retain other kinds. + // For instance this marked as unavailable: + // class __attribute((unavailable)) UnavailableClass;` + auto RemoveAccessCheckingDiagnostics = [&TemplateInfo, this]() { + if (TemplateInfo.Kind == ParsedTemplateInfo::ExplicitInstantiation || + TemplateInfo.Kind == ParsedTemplateInfo::ExplicitSpecialization) + RemoveDiagnosticsFromPool(sema::DelayedDiagnostic::DDKind::Access); + }; + switch (Tok.getKind()) { default: DoneWithDeclSpec: @@ -3248,6 +3261,12 @@ /*WantNontrivialTypeSourceInfo=*/true, isClassTemplateDeductionContext(DSContext)); + // C++20 [temp.spec] 13.9/6. + // This disables the access checking rules for function template explicit + // instantiation and explicit specialization: + // - `return type`. + RemoveAccessCheckingDiagnostics(); + // If the referenced identifier is not a type, then this declspec is // erroneous: We already checked about that it has no type specifier, and // C++ doesn't have implicit int. Diagnose it as a typo w.r.t. to the @@ -3361,6 +3380,13 @@ DS.SetTypeSpecError(); goto DoneWithDeclSpec; } + + // C++20 [temp.spec] 13.9/6. + // This disables the access checking rules for function template + // explicit instantiation and explicit specialization: + // - `return type`. + RemoveAccessCheckingDiagnostics(); + if (!Tok.is(tok::identifier)) continue; } diff --git a/clang/lib/Parse/ParseDeclCXX.cpp b/clang/lib/Parse/ParseDeclCXX.cpp --- a/clang/lib/Parse/ParseDeclCXX.cpp +++ b/clang/lib/Parse/ParseDeclCXX.cpp @@ -1438,19 +1438,15 @@ return; } - // C++03 [temp.explicit] 14.7.2/8: - // The usual access checking rules do not apply to names used to specify - // explicit instantiations. - // - // As an extension we do not perform access checking on the names used to - // specify explicit specializations either. This is important to allow - // specializing traits classes for private types. - // - // Note that we don't suppress if this turns out to be an elaborated - // type specifier. - bool shouldDelayDiagsInTag = - (TemplateInfo.Kind == ParsedTemplateInfo::ExplicitInstantiation || - TemplateInfo.Kind == ParsedTemplateInfo::ExplicitSpecialization); + // C++20 [temp.class.spec] 13.7.5/10 + // The usual access checking rules do not apply to non-dependent names + // used to specify template arguments of the simple-template-id of the + // partial specialization. + // C++20 [temp.spec] 13.9/6: + // The usual access checking rules do not apply to names in a declaration + // of an explicit instantiation or explicit specialization... + const bool shouldDelayDiagsInTag = + (TemplateInfo.Kind != ParsedTemplateInfo::NonTemplate); SuppressAccessChecks diagsFromTag(*this, shouldDelayDiagsInTag); ParsedAttributesWithRange attrs(AttrFactory); @@ -1794,14 +1790,6 @@ } } - // If this is an elaborated type specifier, and we delayed - // diagnostics before, just merge them into the current pool. - if (shouldDelayDiagsInTag) { - diagsFromTag.done(); - if (TUK == Sema::TUK_Reference) - diagsFromTag.redelay(); - } - if (!Name && !TemplateId && (DS.getTypeSpecType() == DeclSpec::TST_error || TUK != Sema::TUK_Definition)) { if (DS.getTypeSpecType() != DeclSpec::TST_error) { @@ -1978,6 +1966,16 @@ } } + // If this is an elaborated type specifier in function template, + // and we delayed diagnostics before, + // just merge them into the current pool. + if (shouldDelayDiagsInTag) { + diagsFromTag.done(); + if (TUK == Sema::TUK_Reference && + TemplateInfo.Kind == ParsedTemplateInfo::Template) + diagsFromTag.redelay(); + } + // If there is a body, parse it and inform the actions module. if (TUK == Sema::TUK_Definition) { assert(Tok.is(tok::l_brace) || @@ -2654,6 +2652,19 @@ ParseDeclarationSpecifiers(DS, TemplateInfo, AS, DeclSpecContext::DSC_class, &CommonLateParsedAttrs); + // Turn off usual access checking for templates explicit specialization + // and instantiation. + auto RemoveAccessChecksDiagnostics = [&TemplateInfo, this]() { + if (TemplateInfo.Kind == ParsedTemplateInfo::ExplicitInstantiation || + TemplateInfo.Kind == ParsedTemplateInfo::ExplicitSpecialization) + RemoveDiagnosticsFromPool(sema::DelayedDiagnostic::DDKind::Access); + }; + + // C++20 [temp.spec] 13.9/6. + // This disables the access checking rules for member function template + // explicit instantiation and explicit specialization. + RemoveAccessChecksDiagnostics(); + // Turn off colon protection that was set for declspec. X.restore(); @@ -2792,6 +2803,11 @@ DS.ClearStorageClassSpecs(); } + // C++20 [temp.spec] 13.9/6. + // This disables the access checking rules for member function template + // explicit instantiation and explicit specialization. + RemoveAccessChecksDiagnostics(); + Decl *FunDecl = ParseCXXInlineMethodDef(AS, AccessAttrs, DeclaratorInfo, TemplateInfo, VS, PureSpecLoc); diff --git a/clang/lib/Parse/ParseTemplate.cpp b/clang/lib/Parse/ParseTemplate.cpp --- a/clang/lib/Parse/ParseTemplate.cpp +++ b/clang/lib/Parse/ParseTemplate.cpp @@ -258,6 +258,26 @@ return nullptr; } + // Turn off usual access checking for template specializations and + // instantiations. + // We don't use `SuppressAccessChecks` here + // because it suppresses ALL kinds of diagnostics + // but we need to retain other kinds. + // For instance this marked as unavailable: + // class __attribute((unavailable)) UnavailableClass;` + // + // C++20 [temp.spec] 13.9/6. + // This disables the access checking rules for function template explicit + // instantiation and explicit specialization: + // - parameter-list; + // - template-argument-list; + // - noexcept-specifier; + // - dynamic-exception-specifications (deprecated in C++11, removed since + // C++17). + if (TemplateInfo.Kind == ParsedTemplateInfo::ExplicitInstantiation || + TemplateInfo.Kind == ParsedTemplateInfo::ExplicitSpecialization) + RemoveDiagnosticsFromPool(sema::DelayedDiagnostic::DDKind::Access); + llvm::TimeTraceScope TimeScope("ParseTemplate", [&]() { return std::string(DeclaratorInfo.getIdentifier() != nullptr ? DeclaratorInfo.getIdentifier()->getName() @@ -1826,3 +1846,13 @@ AngleBrackets.clear(*this); return false; } + +/// Remove delayed diagnostics of given kind from the pool to avoid unwanted +/// diagnostic messages. +/// +/// Note: Removing doesn't touch parent pools. +void Parser::RemoveDiagnosticsFromPool(sema::DelayedDiagnostic::DDKind K) { + if (sema::DelayedDiagnosticPool *Pool = + Actions.DelayedDiagnostics.getCurrentPool()) + Pool->removeAllOfKind(K); +} \ No newline at end of file diff --git a/clang/test/CXX/class.access/class.friend/p1.cpp b/clang/test/CXX/class.access/class.friend/p1.cpp --- a/clang/test/CXX/class.access/class.friend/p1.cpp +++ b/clang/test/CXX/class.access/class.friend/p1.cpp @@ -272,7 +272,7 @@ // Return types, parameters and default arguments to friend functions. namespace test8 { class A { - typedef int I; // expected-note 4 {{declared private here}} + typedef int I; // expected-note 6 {{declared private here}} static const I x = 0; // expected-note {{implicitly declared private here}} friend I f(I i); template friend I g(I i); @@ -289,7 +289,16 @@ template A::I g2(A::I i) { // expected-error 2 {{is a private member of}} T t; } - template A::I g2(A::I i); + template <> A::I g2(A::I i) { return 0; } // OK + template A::I g2(A::I i); // OK + template <> A::I g2(A::I i); // OK + template <> A::I g2(A::I i); // OK + template A::I g2(A::I i); // OK + template int g2(int i); // OK + template A::I g2(A::I i); // OK + + template A::I g3(A::I i) { return 0; } // expected-error 2 {{is a private member of}} + template <> int g3(int i); // OK } // PR6885 diff --git a/clang/test/CXX/drs/dr1xx.cpp b/clang/test/CXX/drs/dr1xx.cpp --- a/clang/test/CXX/drs/dr1xx.cpp +++ b/clang/test/CXX/drs/dr1xx.cpp @@ -934,12 +934,12 @@ template void C::g() {} class A { - class B {}; // expected-note {{here}} + class B {}; void f(); }; template void C::f(); - template <> void C::g(); // expected-error {{private}} + template <> void C::g(); void A::f() { C cb; diff --git a/clang/test/CXX/temp/temp.spec/func.spec.cpp b/clang/test/CXX/temp/temp.spec/func.spec.cpp new file mode 100644 --- /dev/null +++ b/clang/test/CXX/temp/temp.spec/func.spec.cpp @@ -0,0 +1,247 @@ +// RUN: %clang_cc1 -std=c++17 -fsyntax-only -verify %s + +// C++20 [temp.spec] 13.9/6: +// The usual access checking rules do not apply to names in a declaration +// of an explicit instantiation or explicit specialization, with the +// exception of names appearing in a function body, default argument, +// base-clause, member-specification, enumerator-list, or static data member +// or variable template initializer. +// [Note : In particular, the template arguments and names used in the +// function declarator(including parameter types, return types and exception +// specifications) may be private types or objects that would normally not be +// accessible. — end note] + +class A { + // expected-note@+1 17{{implicitly declared private here}} + template class B {}; + // expected-note@+1 3{{implicitly declared private here}} + static constexpr int num1 = 42; + +protected: + // expected-note@+1 13{{declared protected here}} + class C {}; + // expected-note@+1 2{{declared protected here}} + static constexpr int num2 = 43; + static int num4; + +public: + template class D {}; + static constexpr int num3 = 44; +}; +int A::num4 = 44; + +class E : public A { + + // Declarations + + // expected-error@+1 {{is a private member of}} + template > void func1(); + template void func2(); + template > void func3(); + // expected-error@+1 {{is a private member of}} + template A::B func4(); + // expected-error@+1 {{is a private member of}} + template A::B func5(); + template class A::C func6(); + template class A::D func7(); + // expected-error@+1 2{{is a private member of}} + template void func8(class A::B, int x = A::num1); + template void func9(A::C, A::D, int = A::num3); + + // Specializations inside class declaration + template <> void func1>() {} + template <> void func2>() { + } template <> void func3() { + } + template <> class A::B func4>() { return {}; } template <> A::B> func5>() { + return {}; + } + template <> class A::C func6() { return {}; } template <> A::D func7() { + return {}; + } + template <> void func8(class A::B, int) {} + template <> void func9>(A::C, A::D>, int) {} + + // FIXME: Instantiations inside class declaration. + // don't work correctly. +}; + +// Definitions + +template void E::func1() {} +template void E::func2() {} +template void E::func3() {} +// expected-error@+1 {{is a private member of}} +template A::B E::func4() { return {}; } +// expected-error@+1 {{is a private member of}} +template A::B E::func5() { return {}; } +template A::C E::func6() { return {}; } +template A::D E::func7() { return {}; } +// expected-error@+1 {{is a private member of}} +template void E::func8(A::B, int) {} +template void E::func9(A::C, A::D, int) {} + +// Specializations + +template <> void E::func1>() {} +template <> void E::func2() {} +template <> void E::func3>() { +} template <> class A::B E::func4>() { + return {}; +} template <> A::B E::func5() { + return {}; +} +template <> class A::C E::func6>() { return {}; } template <> A::D E::func7() { + return {}; +} +template <> void E::func8(class A::B, int) {} +template <> void E::func9(A::C, A::D, int) {} + +// Instantiations + +template <> void E::func1>(); +template <> void E::func2(); +template <> void E::func3>(); +template <> class A::B E::func4>(); +template <> A::B E::func5(); +template <> class A::C E::func6>(); +template <> A::D E::func7(); +template <> void E::func8(class A::B, int); +template <> void E::func9(A::C, A::D, int); + +//----------------------------------------------------------// + +// forward declarations + +// expected-error@+1 {{is a protected member of}} +template class A::C func1(); +// expected-error@+1 {{is a private member of}} +template A::B func2(); +template A::D func3(); +// expected-error@+1 {{is a private member of}} +template class A::B func4(); +template void func5(); +// expected-error@+1 {{is a private member of}} +template void func6(); +// expected-error@+1 {{is a protected member of}} +template void func7(); +// expected-error@+1 {{is a protected member of}} +template void func8(int x = sizeof(A::C)); +// expected-error@+1 {{is a private member of}} +template void func9(int x = A::num1); +// expected-error@+2 {{is a private member of}} +// expected-error@+1 {{is a protected member of}} +template void func10(class A::B, int x = A::num2); +// expected-error@+1 {{is a protected member of}} +template void func11(class A::C, A::D, int = A::num3); +template void func12(); +template void func13(); +template void func14(); +template