diff --git a/.github/new-prs-labeler.yml b/.github/new-prs-labeler.yml index 8cc6c36fa945b..cef4782331510 100644 --- a/.github/new-prs-labeler.yml +++ b/.github/new-prs-labeler.yml @@ -69,7 +69,7 @@ PGO: - llvm/**/llvm-profdata/**/* - llvm/**/llvm-profgen/**/* -vectorization: +vectorizers: - llvm/lib/Transforms/Vectorize/**/* - llvm/include/llvm/Transforms/Vectorize/**/* diff --git a/.github/workflows/release-binaries.yml b/.github/workflows/release-binaries.yml index f24e25879b96b..1cde628d3f66c 100644 --- a/.github/workflows/release-binaries.yml +++ b/.github/workflows/release-binaries.yml @@ -328,7 +328,7 @@ jobs: run: | # Build some of the mlir tools that take a long time to link if [ "${{ needs.prepare.outputs.build-flang }}" = "true" ]; then - ninja -C ${{ steps.setup-stage.outputs.build-prefix }}/build/tools/clang/stage2-bins/ -j2 flang-new bbc + ninja -C ${{ steps.setup-stage.outputs.build-prefix }}/build/tools/clang/stage2-bins/ -j2 flang bbc fi ninja -C ${{ steps.setup-stage.outputs.build-prefix }}/build/tools/clang/stage2-bins/ \ mlir-bytecode-parser-fuzzer \ diff --git a/bolt/lib/Profile/YAMLProfileReader.cpp b/bolt/lib/Profile/YAMLProfileReader.cpp index 67ed32017667d..fe0fcfdcd42f9 100644 --- a/bolt/lib/Profile/YAMLProfileReader.cpp +++ b/bolt/lib/Profile/YAMLProfileReader.cpp @@ -643,11 +643,7 @@ size_t YAMLProfileReader::matchWithNameSimilarity(BinaryContext &BC) { // equal number of blocks. if (NamespaceToProfiledBFSizesIt->second.count(BF->size()) == 0) continue; - auto NamespaceToBFsIt = NamespaceToBFs.find(Namespace); - if (NamespaceToBFsIt == NamespaceToBFs.end()) - NamespaceToBFs[Namespace] = {BF}; - else - NamespaceToBFsIt->second.push_back(BF); + NamespaceToBFs[Namespace].push_back(BF); } // Iterates through all profiled functions and binary functions belonging to diff --git a/clang-tools-extra/clang-tidy/cppcoreguidelines/ProTypeMemberInitCheck.cpp b/clang-tools-extra/clang-tidy/cppcoreguidelines/ProTypeMemberInitCheck.cpp index 9c3c7cc70c187..225e867c9b24f 100644 --- a/clang-tools-extra/clang-tidy/cppcoreguidelines/ProTypeMemberInitCheck.cpp +++ b/clang-tools-extra/clang-tidy/cppcoreguidelines/ProTypeMemberInitCheck.cpp @@ -474,10 +474,8 @@ void ProTypeMemberInitCheck::checkMissingMemberInitializer( // It only includes fields that have not been fixed SmallPtrSet AllFieldsToInit; forEachField(ClassDecl, FieldsToInit, [&](const FieldDecl *F) { - if (!HasRecordClassMemberSet.contains(F)) { + if (HasRecordClassMemberSet.insert(F).second) AllFieldsToInit.insert(F); - HasRecordClassMemberSet.insert(F); - } }); if (FieldsToInit.empty()) return; diff --git a/clang-tools-extra/clang-tidy/performance/MoveConstArgCheck.cpp b/clang-tools-extra/clang-tidy/performance/MoveConstArgCheck.cpp index d29b9e91f2e35..421ce003975bc 100644 --- a/clang-tools-extra/clang-tidy/performance/MoveConstArgCheck.cpp +++ b/clang-tools-extra/clang-tidy/performance/MoveConstArgCheck.cpp @@ -209,8 +209,9 @@ void MoveConstArgCheck::check(const MatchFinder::MatchResult &Result) { } if (const CXXRecordDecl *RecordDecl = ArgType->getAsCXXRecordDecl(); - RecordDecl && !(RecordDecl->hasMoveConstructor() && - RecordDecl->hasMoveAssignment())) { + RecordDecl && RecordDecl->hasDefinition() && + !(RecordDecl->hasMoveConstructor() && + RecordDecl->hasMoveAssignment())) { const bool MissingMoveAssignment = !RecordDecl->hasMoveAssignment(); const bool MissingMoveConstructor = !RecordDecl->hasMoveConstructor(); const bool MissingBoth = MissingMoveAssignment && MissingMoveConstructor; diff --git a/clang-tools-extra/clang-tidy/rename_check.py b/clang-tools-extra/clang-tidy/rename_check.py index bf9c886699cb2..5f3295b23ba72 100755 --- a/clang-tools-extra/clang-tidy/rename_check.py +++ b/clang-tools-extra/clang-tidy/rename_check.py @@ -8,16 +8,16 @@ # # ===-----------------------------------------------------------------------===# -from __future__ import unicode_literals - import argparse import glob import io import os import re +import sys +from typing import List -def replaceInFileRegex(fileName, sFrom, sTo): +def replaceInFileRegex(fileName: str, sFrom: str, sTo: str) -> None: if sFrom == sTo: return @@ -35,7 +35,7 @@ def replaceInFileRegex(fileName, sFrom, sTo): f.write(txt) -def replaceInFile(fileName, sFrom, sTo): +def replaceInFile(fileName: str, sFrom: str, sTo: str) -> None: if sFrom == sTo: return txt = None @@ -51,7 +51,7 @@ def replaceInFile(fileName, sFrom, sTo): f.write(txt) -def generateCommentLineHeader(filename): +def generateCommentLineHeader(filename: str) -> str: return "".join( [ "//===--- ", @@ -63,7 +63,7 @@ def generateCommentLineHeader(filename): ) -def generateCommentLineSource(filename): +def generateCommentLineSource(filename: str) -> str: return "".join( [ "//===--- ", @@ -75,7 +75,7 @@ def generateCommentLineSource(filename): ) -def fileRename(fileName, sFrom, sTo): +def fileRename(fileName: str, sFrom: str, sTo: str) -> str: if sFrom not in fileName or sFrom == sTo: return fileName newFileName = fileName.replace(sFrom, sTo) @@ -84,7 +84,7 @@ def fileRename(fileName, sFrom, sTo): return newFileName -def deleteMatchingLines(fileName, pattern): +def deleteMatchingLines(fileName: str, pattern: str) -> bool: lines = None with io.open(fileName, "r", encoding="utf8") as f: lines = f.readlines() @@ -101,7 +101,7 @@ def deleteMatchingLines(fileName, pattern): return True -def getListOfFiles(clang_tidy_path): +def getListOfFiles(clang_tidy_path: str) -> List[str]: files = glob.glob(os.path.join(clang_tidy_path, "**"), recursive=True) files += [ os.path.normpath(os.path.join(clang_tidy_path, "../docs/ReleaseNotes.rst")) @@ -124,7 +124,7 @@ def getListOfFiles(clang_tidy_path): # Adapts the module's CMakelist file. Returns 'True' if it could add a new # entry and 'False' if the entry already existed. -def adapt_cmake(module_path, check_name_camel): +def adapt_cmake(module_path: str, check_name_camel: str) -> bool: filename = os.path.join(module_path, "CMakeLists.txt") with io.open(filename, "r", encoding="utf8") as f: lines = f.readlines() @@ -153,7 +153,9 @@ def adapt_cmake(module_path, check_name_camel): # Modifies the module to include the new check. -def adapt_module(module_path, module, check_name, check_name_camel): +def adapt_module( + module_path: str, module: str, check_name: str, check_name_camel: str +) -> None: modulecpp = next( iter( filter( @@ -204,7 +206,9 @@ def adapt_module(module_path, module, check_name, check_name_camel): # Adds a release notes entry. -def add_release_notes(clang_tidy_path, old_check_name, new_check_name): +def add_release_notes( + clang_tidy_path: str, old_check_name: str, new_check_name: str +) -> None: filename = os.path.normpath( os.path.join(clang_tidy_path, "../docs/ReleaseNotes.rst") ) @@ -262,7 +266,7 @@ def add_release_notes(clang_tidy_path, old_check_name, new_check_name): f.write(line) -def main(): +def main() -> None: parser = argparse.ArgumentParser(description="Rename clang-tidy check.") parser.add_argument("old_check_name", type=str, help="Old check name.") parser.add_argument("new_check_name", type=str, help="New check name.") @@ -311,7 +315,7 @@ def main(): "Check name '%s' not found in %s. Exiting." % (check_name_camel, cmake_lists) ) - return 1 + sys.exit(1) modulecpp = next( iter( diff --git a/clang-tools-extra/docs/ReleaseNotes.rst b/clang-tools-extra/docs/ReleaseNotes.rst index 5110028672458..3c4652e3c2377 100644 --- a/clang-tools-extra/docs/ReleaseNotes.rst +++ b/clang-tools-extra/docs/ReleaseNotes.rst @@ -210,6 +210,10 @@ Changes in existing checks ` check to use ``std::endl`` as placeholder when lexer cannot get source text. +- Improved :doc:`performance-move-const-arg + ` check to fix a crash when + an argument type is declared but not defined. + - Improved :doc:`readability-container-contains ` check to let it work on any class that has a ``contains`` method. diff --git a/clang-tools-extra/test/clang-tidy/checkers/performance/move-const-arg.cpp b/clang-tools-extra/test/clang-tidy/checkers/performance/move-const-arg.cpp index 4505eef6df24b..8e325b0ae6ca3 100644 --- a/clang-tools-extra/test/clang-tidy/checkers/performance/move-const-arg.cpp +++ b/clang-tools-extra/test/clang-tidy/checkers/performance/move-const-arg.cpp @@ -546,3 +546,17 @@ void testAlsoNonMoveable() { } } // namespace issue_62550 + +namespace GH111450 { +struct Status; + +struct Error { + Error(const Status& S); +}; + +struct Result { + Error E; + Result(Status&& S) : E(std::move(S)) {} + // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: passing result of std::move() as a const reference argument; no move will actually happen [performance-move-const-arg] +}; +} // namespace GH111450 diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index 583c1e6b4215c..c0019cfe4658d 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -174,6 +174,10 @@ C++23 Feature Support C++20 Feature Support ^^^^^^^^^^^^^^^^^^^^^ +C++17 Feature Support +^^^^^^^^^^^^^^^^^^^^^ +- The implementation of the relaxed template template argument matching rules is + more complete and reliable, and should provide more accurate diagnostics. Resolutions to C++ Defect Reports ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -200,7 +204,8 @@ Resolutions to C++ Defect Reports (`CWG2351: void{} `_). - Clang now has improved resolution to CWG2398, allowing class templates to have - default arguments deduced when partial ordering. + default arguments deduced when partial ordering, and better backwards compatibility + in overload resolution. - Clang now allows comparing unequal object pointers that have been cast to ``void *`` in constant expressions. These comparisons always worked in non-constant expressions. @@ -331,6 +336,10 @@ Improvements to Clang's diagnostics - Clang now diagnoses when the result of a [[nodiscard]] function is discarded after being cast in C. Fixes #GH104391. +- Clang now properly explains the reason a template template argument failed to + match a template template parameter, in terms of the C++17 relaxed matching rules + instead of the old ones. + - Don't emit duplicated dangling diagnostics. (#GH93386). - Improved diagnostic when trying to befriend a concept. (#GH45182). @@ -440,6 +449,8 @@ Bug Fixes to C++ Support - Correctly check constraints of explicit instantiations of member functions. (#GH46029) - When performing partial ordering of function templates, clang now checks that the deduction was consistent. Fixes (#GH18291). +- Fixes to several issues in partial ordering of template template parameters, which + were documented in the test suite. - Fixed an assertion failure about a constraint of a friend function template references to a value with greater template depth than the friend function template. (#GH98258) - Clang now rebuilds the template parameters of out-of-line declarations and specializations in the context @@ -468,7 +479,6 @@ Bug Fixes to C++ Support - Fixed an assertion failure in debug mode, and potential crashes in release mode, when diagnosing a failed cast caused indirectly by a failed implicit conversion to the type of the constructor parameter. - Fixed an assertion failure by adjusting integral to boolean vector conversions (#GH108326) -- Clang is now better at keeping track of friend function template instance contexts. (#GH55509) - Fixed an issue deducing non-type template arguments of reference type. (#GH73460) - Fixed an issue in constraint evaluation, where type constraints on the lambda expression containing outer unexpanded parameters were not correctly expanded. (#GH101754) @@ -478,9 +488,9 @@ Bug Fixes to C++ Support in certain friend declarations. (#GH93099) - Clang now instantiates the correct lambda call operator when a lambda's class type is merged across modules. (#GH110401) -- Clang now uses the correct set of template argument lists when comparing the constraints of - out-of-line definitions and member templates explicitly specialized for a given implicit instantiation of - a class template. (#GH102320) +- Fix a crash when parsing a pseudo destructor involving an invalid type. (#GH111460) +- Fixed an assertion failure when invoking recovery call expressions with explicit attributes + and undeclared templates. (#GH107047, #GH49093) Bug Fixes to AST Handling ^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/clang/include/clang/AST/Decl.h b/clang/include/clang/AST/Decl.h index 6afc86710a813..7ff35d73df599 100644 --- a/clang/include/clang/AST/Decl.h +++ b/clang/include/clang/AST/Decl.h @@ -2299,13 +2299,6 @@ class FunctionDecl : public DeclaratorDecl, FunctionDeclBits.IsLateTemplateParsed = ILT; } - bool isInstantiatedFromMemberTemplate() const { - return FunctionDeclBits.IsInstantiatedFromMemberTemplate; - } - void setInstantiatedFromMemberTemplate(bool Val = true) { - FunctionDeclBits.IsInstantiatedFromMemberTemplate = Val; - } - /// Whether this function is "trivial" in some specialized C++ senses. /// Can only be true for default constructors, copy constructors, /// copy assignment operators, and destructors. Not meaningful until diff --git a/clang/include/clang/AST/DeclBase.h b/clang/include/clang/AST/DeclBase.h index eb67dc03157e6..a3447d1990975 100644 --- a/clang/include/clang/AST/DeclBase.h +++ b/clang/include/clang/AST/DeclBase.h @@ -687,6 +687,9 @@ class alignas(8) Decl { /// Whether this declaration comes from a named module. bool isInNamedModule() const; + /// Whether this declaration comes from a header unit. + bool isFromHeaderUnit() const; + /// Return true if this declaration has an attribute which acts as /// definition of the entity, such as 'alias' or 'ifunc'. bool hasDefiningAttr() const; @@ -1763,8 +1766,6 @@ class DeclContext { uint64_t HasImplicitReturnZero : 1; LLVM_PREFERRED_TYPE(bool) uint64_t IsLateTemplateParsed : 1; - LLVM_PREFERRED_TYPE(bool) - uint64_t IsInstantiatedFromMemberTemplate : 1; /// Kind of contexpr specifier as defined by ConstexprSpecKind. LLVM_PREFERRED_TYPE(ConstexprSpecKind) @@ -1815,7 +1816,7 @@ class DeclContext { }; /// Number of inherited and non-inherited bits in FunctionDeclBitfields. - enum { NumFunctionDeclBits = NumDeclContextBits + 32 }; + enum { NumFunctionDeclBits = NumDeclContextBits + 31 }; /// Stores the bits used by CXXConstructorDecl. If modified /// NumCXXConstructorDeclBits and the accessor @@ -1826,12 +1827,12 @@ class DeclContext { LLVM_PREFERRED_TYPE(FunctionDeclBitfields) uint64_t : NumFunctionDeclBits; - /// 19 bits to fit in the remaining available space. + /// 20 bits to fit in the remaining available space. /// Note that this makes CXXConstructorDeclBitfields take /// exactly 64 bits and thus the width of NumCtorInitializers /// will need to be shrunk if some bit is added to NumDeclContextBitfields, /// NumFunctionDeclBitfields or CXXConstructorDeclBitfields. - uint64_t NumCtorInitializers : 16; + uint64_t NumCtorInitializers : 17; LLVM_PREFERRED_TYPE(bool) uint64_t IsInheritingConstructor : 1; @@ -1845,7 +1846,7 @@ class DeclContext { }; /// Number of inherited and non-inherited bits in CXXConstructorDeclBitfields. - enum { NumCXXConstructorDeclBits = NumFunctionDeclBits + 19 }; + enum { NumCXXConstructorDeclBits = NumFunctionDeclBits + 20 }; /// Stores the bits used by ObjCMethodDecl. /// If modified NumObjCMethodDeclBits and the accessor diff --git a/clang/include/clang/AST/DeclTemplate.h b/clang/include/clang/AST/DeclTemplate.h index 2fb49ec1aea0d..687715a22e9fd 100644 --- a/clang/include/clang/AST/DeclTemplate.h +++ b/clang/include/clang/AST/DeclTemplate.h @@ -781,11 +781,15 @@ class RedeclarableTemplateDecl : public TemplateDecl, EntryType *Entry, void *InsertPos); struct CommonBase { - CommonBase() {} + CommonBase() : InstantiatedFromMember(nullptr, false) {} /// The template from which this was most /// directly instantiated (or null). - RedeclarableTemplateDecl *InstantiatedFromMember = nullptr; + /// + /// The boolean value indicates whether this template + /// was explicitly specialized. + llvm::PointerIntPair + InstantiatedFromMember; /// If non-null, points to an array of specializations (including /// partial specializations) known only by their external declaration IDs. @@ -805,19 +809,14 @@ class RedeclarableTemplateDecl : public TemplateDecl, }; /// Pointer to the common data shared by all declarations of this - /// template, and a flag indicating if the template is a member - /// specialization. - mutable llvm::PointerIntPair Common; - - CommonBase *getCommonPtrInternal() const { return Common.getPointer(); } + /// template. + mutable CommonBase *Common = nullptr; /// Retrieves the "common" pointer shared by all (re-)declarations of /// the same template. Calling this routine may implicitly allocate memory /// for the common pointer. CommonBase *getCommonPtr() const; - void setCommonPtr(CommonBase *C) const { Common.setPointer(C); } - virtual CommonBase *newCommon(ASTContext &C) const = 0; // Construct a template decl with name, parameters, and templated element. @@ -858,12 +857,15 @@ class RedeclarableTemplateDecl : public TemplateDecl, /// template<> template /// struct X::Inner { /* ... */ }; /// \endcode - bool isMemberSpecialization() const { return Common.getInt(); } + bool isMemberSpecialization() const { + return getCommonPtr()->InstantiatedFromMember.getInt(); + } /// Note that this member template is a specialization. void setMemberSpecialization() { - assert(!isMemberSpecialization() && "already a member specialization"); - Common.setInt(true); + assert(getCommonPtr()->InstantiatedFromMember.getPointer() && + "Only member templates can be member template specializations"); + getCommonPtr()->InstantiatedFromMember.setInt(true); } /// Retrieve the member template from which this template was @@ -903,12 +905,12 @@ class RedeclarableTemplateDecl : public TemplateDecl, /// void X::f(T, U); /// \endcode RedeclarableTemplateDecl *getInstantiatedFromMemberTemplate() const { - return getCommonPtr()->InstantiatedFromMember; + return getCommonPtr()->InstantiatedFromMember.getPointer(); } void setInstantiatedFromMemberTemplate(RedeclarableTemplateDecl *TD) { - assert(!getCommonPtr()->InstantiatedFromMember); - getCommonPtr()->InstantiatedFromMember = TD; + assert(!getCommonPtr()->InstantiatedFromMember.getPointer()); + getCommonPtr()->InstantiatedFromMember.setPointer(TD); } /// Retrieve the "injected" template arguments that correspond to the @@ -1008,15 +1010,6 @@ class FunctionTemplateDecl : public RedeclarableTemplateDecl { return getTemplatedDecl()->isThisDeclarationADefinition(); } - bool isCompatibleWithDefinition() const { - return getTemplatedDecl()->isInstantiatedFromMemberTemplate() || - isThisDeclarationADefinition(); - } - void setInstantiatedFromMemberTemplate(FunctionTemplateDecl *D) { - getTemplatedDecl()->setInstantiatedFromMemberTemplate(); - RedeclarableTemplateDecl::setInstantiatedFromMemberTemplate(D); - } - /// Return the specialization with the provided arguments if it exists, /// otherwise return the insertion point. FunctionDecl *findSpecialization(ArrayRef Args, @@ -1996,8 +1989,6 @@ class ClassTemplateSpecializationDecl : public CXXRecordDecl, /// template arguments have been deduced. void setInstantiationOf(ClassTemplatePartialSpecializationDecl *PartialSpec, const TemplateArgumentList *TemplateArgs) { - assert(!isa(this) && - "A partial specialization cannot be instantiated from a template"); assert(!SpecializedTemplate.is() && "Already set to a class template partial specialization!"); auto *PS = new (getASTContext()) SpecializedPartialSpecialization(); @@ -2009,8 +2000,6 @@ class ClassTemplateSpecializationDecl : public CXXRecordDecl, /// Note that this class template specialization is an instantiation /// of the given class template. void setInstantiationOf(ClassTemplateDecl *TemplDecl) { - assert(!isa(this) && - "A partial specialization cannot be instantiated from a template"); assert(!SpecializedTemplate.is() && "Previously set to a class template partial specialization!"); SpecializedTemplate = TemplDecl; @@ -2198,11 +2187,18 @@ class ClassTemplatePartialSpecializationDecl /// struct X::Inner { /* ... */ }; /// \endcode bool isMemberSpecialization() const { - return InstantiatedFromMember.getInt(); + const auto *First = + cast(getFirstDecl()); + return First->InstantiatedFromMember.getInt(); } /// Note that this member template is a specialization. - void setMemberSpecialization() { return InstantiatedFromMember.setInt(true); } + void setMemberSpecialization() { + auto *First = cast(getFirstDecl()); + assert(First->InstantiatedFromMember.getPointer() && + "Only member templates can be member template specializations"); + return First->InstantiatedFromMember.setInt(true); + } /// Retrieves the injected specialization type for this partial /// specialization. This is not the same as the type-decl-type for @@ -2272,6 +2268,10 @@ class ClassTemplateDecl : public RedeclarableTemplateDecl { return static_cast(RedeclarableTemplateDecl::getCommonPtr()); } + void setCommonPtr(Common *C) { + RedeclarableTemplateDecl::Common = C; + } + public: friend class ASTDeclReader; @@ -2754,8 +2754,6 @@ class VarTemplateSpecializationDecl : public VarDecl, /// template arguments have been deduced. void setInstantiationOf(VarTemplatePartialSpecializationDecl *PartialSpec, const TemplateArgumentList *TemplateArgs) { - assert(!isa(this) && - "A partial specialization cannot be instantiated from a template"); assert(!SpecializedTemplate.is() && "Already set to a variable template partial specialization!"); auto *PS = new (getASTContext()) SpecializedPartialSpecialization(); @@ -2767,8 +2765,6 @@ class VarTemplateSpecializationDecl : public VarDecl, /// Note that this variable template specialization is an instantiation /// of the given variable template. void setInstantiationOf(VarTemplateDecl *TemplDecl) { - assert(!isa(this) && - "A partial specialization cannot be instantiated from a template"); assert(!SpecializedTemplate.is() && "Previously set to a variable template partial specialization!"); SpecializedTemplate = TemplDecl; @@ -2953,11 +2949,18 @@ class VarTemplatePartialSpecializationDecl /// U* X::Inner = (T*)(0) + 1; /// \endcode bool isMemberSpecialization() const { - return InstantiatedFromMember.getInt(); + const auto *First = + cast(getFirstDecl()); + return First->InstantiatedFromMember.getInt(); } /// Note that this member template is a specialization. - void setMemberSpecialization() { return InstantiatedFromMember.setInt(true); } + void setMemberSpecialization() { + auto *First = cast(getFirstDecl()); + assert(First->InstantiatedFromMember.getPointer() && + "Only member templates can be member template specializations"); + return First->InstantiatedFromMember.setInt(true); + } SourceRange getSourceRange() const override LLVM_READONLY; diff --git a/clang/include/clang/AST/OpenMPClause.h b/clang/include/clang/AST/OpenMPClause.h index 3a1d6852d2a70..2e48c1c3c72c8 100644 --- a/clang/include/clang/AST/OpenMPClause.h +++ b/clang/include/clang/AST/OpenMPClause.h @@ -930,6 +930,105 @@ class OMPSizesClause final } }; +/// This class represents the 'permutation' clause in the +/// '#pragma omp interchange' directive. +/// +/// \code{.c} +/// #pragma omp interchange permutation(2,1) +/// for (int i = 0; i < 64; ++i) +/// for (int j = 0; j < 64; ++j) +/// \endcode +class OMPPermutationClause final + : public OMPClause, + private llvm::TrailingObjects { + friend class OMPClauseReader; + friend class llvm::TrailingObjects; + + /// Location of '('. + SourceLocation LParenLoc; + + /// Number of arguments in the clause, and hence also the number of loops to + /// be permuted. + unsigned NumLoops; + + /// Sets the permutation index expressions. + void setArgRefs(ArrayRef VL) { + assert(VL.size() == NumLoops && "Expecting one expression per loop"); + llvm::copy(VL, static_cast(this) + ->template getTrailingObjects()); + } + + /// Build an empty clause. + explicit OMPPermutationClause(int NumLoops) + : OMPClause(llvm::omp::OMPC_permutation, SourceLocation(), + SourceLocation()), + NumLoops(NumLoops) {} + +public: + /// Build a 'permutation' clause AST node. + /// + /// \param C Context of the AST. + /// \param StartLoc Location of the 'permutation' identifier. + /// \param LParenLoc Location of '('. + /// \param EndLoc Location of ')'. + /// \param Args Content of the clause. + static OMPPermutationClause * + Create(const ASTContext &C, SourceLocation StartLoc, SourceLocation LParenLoc, + SourceLocation EndLoc, ArrayRef Args); + + /// Build an empty 'permutation' AST node for deserialization. + /// + /// \param C Context of the AST. + /// \param NumLoops Number of arguments in the clause. + static OMPPermutationClause *CreateEmpty(const ASTContext &C, + unsigned NumLoops); + + /// Sets the location of '('. + void setLParenLoc(SourceLocation Loc) { LParenLoc = Loc; } + + /// Returns the location of '('. + SourceLocation getLParenLoc() const { return LParenLoc; } + + /// Returns the number of list items. + unsigned getNumLoops() const { return NumLoops; } + + /// Returns the permutation index expressions. + ///@{ + MutableArrayRef getArgsRefs() { + return MutableArrayRef(static_cast(this) + ->template getTrailingObjects(), + NumLoops); + } + ArrayRef getArgsRefs() const { + return ArrayRef(static_cast(this) + ->template getTrailingObjects(), + NumLoops); + } + ///@} + + child_range children() { + MutableArrayRef Args = getArgsRefs(); + return child_range(reinterpret_cast(Args.begin()), + reinterpret_cast(Args.end())); + } + const_child_range children() const { + ArrayRef Args = getArgsRefs(); + return const_child_range(reinterpret_cast(Args.begin()), + reinterpret_cast(Args.end())); + } + + child_range used_children() { + return child_range(child_iterator(), child_iterator()); + } + const_child_range used_children() const { + return const_child_range(const_child_iterator(), const_child_iterator()); + } + + static bool classof(const OMPClause *T) { + return T->getClauseKind() == llvm::omp::OMPC_permutation; + } +}; + /// Representation of the 'full' clause of the '#pragma omp unroll' directive. /// /// \code diff --git a/clang/include/clang/AST/RecursiveASTVisitor.h b/clang/include/clang/AST/RecursiveASTVisitor.h index cbbba9e88b7f5..b2dd51319ba9e 100644 --- a/clang/include/clang/AST/RecursiveASTVisitor.h +++ b/clang/include/clang/AST/RecursiveASTVisitor.h @@ -3348,6 +3348,14 @@ bool RecursiveASTVisitor::VisitOMPSizesClause(OMPSizesClause *C) { return true; } +template +bool RecursiveASTVisitor::VisitOMPPermutationClause( + OMPPermutationClause *C) { + for (Expr *E : C->getArgsRefs()) + TRY_TO(TraverseStmt(E)); + return true; +} + template bool RecursiveASTVisitor::VisitOMPFullClause(OMPFullClause *C) { return true; diff --git a/clang/include/clang/Basic/DiagnosticDriverKinds.td b/clang/include/clang/Basic/DiagnosticDriverKinds.td index 97573fcf20c1f..68722ad963312 100644 --- a/clang/include/clang/Basic/DiagnosticDriverKinds.td +++ b/clang/include/clang/Basic/DiagnosticDriverKinds.td @@ -147,6 +147,9 @@ def warn_drv_unsupported_option_for_processor : Warning< def warn_drv_unsupported_openmp_library : Warning< "the library '%0=%1' is not supported, OpenMP will not be enabled">, InGroup; +def warn_openmp_experimental : Warning< + "OpenMP support in flang is still experimental">, + InGroup; def err_drv_invalid_thread_model_for_target : Error< "invalid thread model '%0' in '%1' for this target">; diff --git a/clang/include/clang/Basic/DiagnosticGroups.td b/clang/include/clang/Basic/DiagnosticGroups.td index 41e719d4d5781..8273701e7b096 100644 --- a/clang/include/clang/Basic/DiagnosticGroups.td +++ b/clang/include/clang/Basic/DiagnosticGroups.td @@ -1583,3 +1583,7 @@ def ExtractAPIMisuse : DiagGroup<"extractapi-misuse">; // Warnings about using the non-standard extension having an explicit specialization // with a storage class specifier. def ExplicitSpecializationStorageClass : DiagGroup<"explicit-specialization-storage-class">; + +// A warning for options that enable a feature that is not yet complete +def ExperimentalOption : DiagGroup<"experimental-option">; + diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index 536211a6da335..f4a2d4a3f0656 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -5262,6 +5262,13 @@ def note_template_arg_refers_here_func : Note< def err_template_arg_template_params_mismatch : Error< "template template argument has different template parameters than its " "corresponding template template parameter">; +def note_template_arg_template_params_mismatch : Note< + "template template argument has different template parameters than its " + "corresponding template template parameter">; +def err_non_deduced_mismatch : Error< + "could not match %diff{$ against $|types}0,1">; +def err_inconsistent_deduction : Error< + "conflicting deduction %diff{$ against $|types}0,1 for parameter">; def err_template_arg_not_integral_or_enumeral : Error< "non-type template argument of type %0 must have an integral or enumeration" " type">; @@ -11702,6 +11709,10 @@ def err_omp_dispatch_statement_call " to a target function or an assignment to one">; def err_omp_unroll_full_variable_trip_count : Error< "loop to be fully unrolled must have a constant trip count">; +def err_omp_interchange_permutation_value_range : Error< + "permutation index must be at least 1 and at most %0">; +def err_omp_interchange_permutation_value_repeated : Error< + "index %0 must appear exactly once in the permutation clause">; def note_omp_directive_here : Note<"'%0' directive found here">; def err_omp_instantiation_not_supported : Error<"instantiation of '%0' not supported yet">; diff --git a/clang/include/clang/CIR/CIRGenerator.h b/clang/include/clang/CIR/CIRGenerator.h new file mode 100644 index 0000000000000..9a8930ac46ea9 --- /dev/null +++ b/clang/include/clang/CIR/CIRGenerator.h @@ -0,0 +1,60 @@ +//===- CIRGenerator.h - CIR Generation from Clang AST ---------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file declares a simple interface to perform CIR generation from Clang +// AST +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_CIR_CIRGENERATOR_H +#define LLVM_CLANG_CIR_CIRGENERATOR_H + +#include "clang/AST/ASTConsumer.h" +#include "clang/Basic/CodeGenOptions.h" + +#include "llvm/ADT/IntrusiveRefCntPtr.h" +#include "llvm/Support/VirtualFileSystem.h" + +#include + +namespace clang { +class DeclGroupRef; +class DiagnosticsEngine; +} // namespace clang + +namespace mlir { +class MLIRContext; +} // namespace mlir +namespace cir { +class CIRGenModule; + +class CIRGenerator : public clang::ASTConsumer { + virtual void anchor(); + clang::DiagnosticsEngine &diags; + clang::ASTContext *astCtx; + // Only used for debug info. + llvm::IntrusiveRefCntPtr fs; + + const clang::CodeGenOptions &codeGenOpts; + +protected: + std::unique_ptr mlirCtx; + std::unique_ptr cgm; + +public: + CIRGenerator(clang::DiagnosticsEngine &diags, + llvm::IntrusiveRefCntPtr fs, + const clang::CodeGenOptions &cgo); + ~CIRGenerator() override; + void Initialize(clang::ASTContext &astCtx) override; + bool HandleTopLevelDecl(clang::DeclGroupRef group) override; +}; + +} // namespace cir + +#endif // LLVM_CLANG_CIR_CIRGENERATOR_H diff --git a/clang/include/clang/CIR/FrontendAction/CIRGenAction.h b/clang/include/clang/CIR/FrontendAction/CIRGenAction.h new file mode 100644 index 0000000000000..2ab612613b73d --- /dev/null +++ b/clang/include/clang/CIR/FrontendAction/CIRGenAction.h @@ -0,0 +1,60 @@ +//===---- CIRGenAction.h - CIR Code Generation Frontend Action -*- C++ -*--===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_CIR_CIRGENACTION_H +#define LLVM_CLANG_CIR_CIRGENACTION_H + +#include "clang/Frontend/FrontendAction.h" + +#include "mlir/IR/BuiltinOps.h" +#include "mlir/IR/OwningOpRef.h" + +namespace mlir { +class MLIRContext; +class ModuleOp; +} // namespace mlir + +namespace cir { +class CIRGenConsumer; + +class CIRGenAction : public clang::ASTFrontendAction { +public: + enum class OutputType { + EmitCIR, + }; + +private: + friend class CIRGenConsumer; + + mlir::OwningOpRef MLIRMod; + + mlir::MLIRContext *MLIRCtx; + +protected: + CIRGenAction(OutputType Action, mlir::MLIRContext *MLIRCtx = nullptr); + + std::unique_ptr + CreateASTConsumer(clang::CompilerInstance &CI, + llvm::StringRef InFile) override; + +public: + ~CIRGenAction() override; + + OutputType Action; +}; + +class EmitCIRAction : public CIRGenAction { + virtual void anchor(); + +public: + EmitCIRAction(mlir::MLIRContext *MLIRCtx = nullptr); +}; + +} // namespace cir + +#endif diff --git a/clang/include/clang/Driver/Options.td b/clang/include/clang/Driver/Options.td index 90f0c4f2df213..d306c751505e9 100644 --- a/clang/include/clang/Driver/Options.td +++ b/clang/include/clang/Driver/Options.td @@ -2996,7 +2996,7 @@ defm clangir : BoolFOption<"clangir", PosFlag, NegFlag LLVM pipeline to compile">, BothFlags<[], [ClangOption, CC1Option], "">>; -def emit_cir : Flag<["-"], "emit-cir">, Visibility<[CC1Option]>, +def emit_cir : Flag<["-"], "emit-cir">, Visibility<[ClangOption, CC1Option]>, Group, HelpText<"Build ASTs and then lower to ClangIR">; /// ClangIR-specific options - END @@ -6077,7 +6077,7 @@ def _sysroot_EQ : Joined<["--"], "sysroot=">, Visibility<[ClangOption, FlangOpti def _sysroot : Separate<["--"], "sysroot">, Alias<_sysroot_EQ>; //===----------------------------------------------------------------------===// -// pie/pic options (clang + flang-new) +// pie/pic options (clang + flang) //===----------------------------------------------------------------------===// let Visibility = [ClangOption, FlangOption] in { @@ -6093,7 +6093,7 @@ def fno_pie : Flag<["-"], "fno-pie">, Group; } // let Vis = [Default, FlangOption] //===----------------------------------------------------------------------===// -// Target Options (clang + flang-new) +// Target Options (clang + flang) //===----------------------------------------------------------------------===// let Flags = [TargetSpecific] in { let Visibility = [ClangOption, FlangOption] in { diff --git a/clang/include/clang/Parse/Parser.h b/clang/include/clang/Parse/Parser.h index 93e49d395388a..dbcb545058a02 100644 --- a/clang/include/clang/Parse/Parser.h +++ b/clang/include/clang/Parse/Parser.h @@ -3595,6 +3595,9 @@ class Parser : public CodeCompletionHandler { /// Parses the 'sizes' clause of a '#pragma omp tile' directive. OMPClause *ParseOpenMPSizesClause(); + /// Parses the 'permutation' clause of a '#pragma omp interchange' directive. + OMPClause *ParseOpenMPPermutationClause(); + /// Parses clause without any additional arguments. /// /// \param Kind Kind of current clause. diff --git a/clang/include/clang/Sema/Overload.h b/clang/include/clang/Sema/Overload.h index c716a25bb673b..d38278c504111 100644 --- a/clang/include/clang/Sema/Overload.h +++ b/clang/include/clang/Sema/Overload.h @@ -925,6 +925,11 @@ class Sema; bool TookAddressOfOverload : 1; + /// Have we matched any packs on the parameter side, versus any non-packs on + /// the argument side, in a context where the opposite matching is also + /// allowed? + bool HasMatchedPackOnParmToNonPackOnArg : 1; + /// True if the candidate was found using ADL. CallExpr::ADLCallKind IsADLCandidate : 1; @@ -999,8 +1004,9 @@ class Sema; friend class OverloadCandidateSet; OverloadCandidate() : IsSurrogate(false), IgnoreObjectArgument(false), - TookAddressOfOverload(false), IsADLCandidate(CallExpr::NotADL), - RewriteKind(CRK_None) {} + TookAddressOfOverload(false), + HasMatchedPackOnParmToNonPackOnArg(false), + IsADLCandidate(CallExpr::NotADL), RewriteKind(CRK_None) {} }; /// OverloadCandidateSet - A set of overload candidates, used in C++ diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index 86053bd7da172..ef010fafb1573 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -4453,9 +4453,10 @@ class Sema final : public SemaBase { SourceLocation *ArgLocation = nullptr); /// Determine if type T is a valid subject for a nonnull and similar - /// attributes. By default, we look through references (the behavior used by - /// nonnull), but if the second parameter is true, then we treat a reference - /// type as valid. + /// attributes. Dependent types are considered valid so they can be checked + /// during instantiation time. By default, we look through references (the + /// behavior used by nonnull), but if the second parameter is true, then we + /// treat a reference type as valid. bool isValidPointerAttrType(QualType T, bool RefOkay = false); /// AddAssumeAlignedAttr - Adds an assume_aligned attribute to a particular @@ -10132,7 +10133,8 @@ class Sema final : public SemaBase { ADLCallKind IsADLCandidate = ADLCallKind::NotADL, ConversionSequenceList EarlyConversions = std::nullopt, OverloadCandidateParamOrder PO = {}, - bool AggregateCandidateDeduction = false); + bool AggregateCandidateDeduction = false, + bool HasMatchedPackOnParmToNonPackOnArg = false); /// Add all of the function declarations in the given function set to /// the overload candidate set. @@ -10167,7 +10169,8 @@ class Sema final : public SemaBase { bool SuppressUserConversions = false, bool PartialOverloading = false, ConversionSequenceList EarlyConversions = std::nullopt, - OverloadCandidateParamOrder PO = {}); + OverloadCandidateParamOrder PO = {}, + bool HasMatchedPackOnParmToNonPackOnArg = false); /// Add a C++ member function template as a candidate to the candidate /// set, using template argument deduction to produce an appropriate member @@ -10213,7 +10216,8 @@ class Sema final : public SemaBase { CXXConversionDecl *Conversion, DeclAccessPair FoundDecl, CXXRecordDecl *ActingContext, Expr *From, QualType ToType, OverloadCandidateSet &CandidateSet, bool AllowObjCConversionOnExplicit, - bool AllowExplicit, bool AllowResultConversion = true); + bool AllowExplicit, bool AllowResultConversion = true, + bool HasMatchedPackOnParmToNonPackOnArg = false); /// Adds a conversion function template specialization /// candidate to the overload set, using template argument deduction @@ -11325,9 +11329,9 @@ class Sema final : public SemaBase { CXXScopeSpec &SS, IdentifierInfo *Name, SourceLocation NameLoc, const ParsedAttributesView &Attr, TemplateParameterList *TemplateParams, AccessSpecifier AS, SourceLocation ModulePrivateLoc, - SourceLocation FriendLoc, - ArrayRef OuterTemplateParamLists, - bool IsMemberSpecialization, SkipBodyInfo *SkipBody = nullptr); + SourceLocation FriendLoc, unsigned NumOuterTemplateParamLists, + TemplateParameterList **OuterTemplateParamLists, + SkipBodyInfo *SkipBody = nullptr); /// Translates template arguments as provided by the parser /// into template arguments used by semantic analysis. @@ -11366,8 +11370,7 @@ class Sema final : public SemaBase { DeclResult ActOnVarTemplateSpecialization( Scope *S, Declarator &D, TypeSourceInfo *DI, LookupResult &Previous, SourceLocation TemplateKWLoc, TemplateParameterList *TemplateParams, - StorageClass SC, bool IsPartialSpecialization, - bool IsMemberSpecialization); + StorageClass SC, bool IsPartialSpecialization); /// Get the specialization of the given variable template corresponding to /// the specified argument list, or a null-but-valid result if the arguments @@ -11637,7 +11640,8 @@ class Sema final : public SemaBase { SourceLocation RAngleLoc, unsigned ArgumentPackIndex, SmallVectorImpl &SugaredConverted, SmallVectorImpl &CanonicalConverted, - CheckTemplateArgumentKind CTAK); + CheckTemplateArgumentKind CTAK, bool PartialOrdering, + bool *MatchedPackOnParmToNonPackOnArg); /// Check that the given template arguments can be provided to /// the given template, converting the arguments along the way. @@ -11684,7 +11688,8 @@ class Sema final : public SemaBase { SmallVectorImpl &SugaredConverted, SmallVectorImpl &CanonicalConverted, bool UpdateArgsWithConversions = true, - bool *ConstraintsNotSatisfied = nullptr, bool PartialOrderingTTP = false); + bool *ConstraintsNotSatisfied = nullptr, bool PartialOrderingTTP = false, + bool *MatchedPackOnParmToNonPackOnArg = nullptr); bool CheckTemplateTypeArgument( TemplateTypeParmDecl *Param, TemplateArgumentLoc &Arg, @@ -11718,7 +11723,9 @@ class Sema final : public SemaBase { /// It returns true if an error occurred, and false otherwise. bool CheckTemplateTemplateArgument(TemplateTemplateParmDecl *Param, TemplateParameterList *Params, - TemplateArgumentLoc &Arg, bool IsDeduced); + TemplateArgumentLoc &Arg, + bool PartialOrdering, + bool *MatchedPackOnParmToNonPackOnArg); void NoteTemplateLocation(const NamedDecl &Decl, std::optional ParamRange = {}); @@ -12229,8 +12236,8 @@ class Sema final : public SemaBase { SmallVectorImpl &Deduced, unsigned NumExplicitlySpecified, FunctionDecl *&Specialization, sema::TemplateDeductionInfo &Info, - SmallVectorImpl const *OriginalCallArgs = nullptr, - bool PartialOverloading = false, + SmallVectorImpl const *OriginalCallArgs, + bool PartialOverloading, bool PartialOrdering, llvm::function_ref CheckNonDependent = [] { return false; }); /// Perform template argument deduction from a function call @@ -12264,7 +12271,8 @@ class Sema final : public SemaBase { TemplateArgumentListInfo *ExplicitTemplateArgs, ArrayRef Args, FunctionDecl *&Specialization, sema::TemplateDeductionInfo &Info, bool PartialOverloading, bool AggregateDeductionCandidate, - QualType ObjectType, Expr::Classification ObjectClassification, + bool PartialOrdering, QualType ObjectType, + Expr::Classification ObjectClassification, llvm::function_ref)> CheckNonDependent); /// Deduce template arguments when taking the address of a function @@ -12417,8 +12425,9 @@ class Sema final : public SemaBase { sema::TemplateDeductionInfo &Info); bool isTemplateTemplateParameterAtLeastAsSpecializedAs( - TemplateParameterList *PParam, TemplateDecl *AArg, - const DefaultArguments &DefaultArgs, SourceLocation Loc, bool IsDeduced); + TemplateParameterList *PParam, TemplateDecl *PArg, TemplateDecl *AArg, + const DefaultArguments &DefaultArgs, SourceLocation ArgLoc, + bool PartialOrdering, bool *MatchedPackOnParmToNonPackOnArg); /// Mark which template parameters are used in a given expression. /// @@ -12727,6 +12736,9 @@ class Sema final : public SemaBase { /// We are instantiating a type alias template declaration. TypeAliasTemplateInstantiation, + + /// We are performing partial ordering for template template parameters. + PartialOrderingTTP, } Kind; /// Was the enclosing context a non-instantiation SFINAE context? @@ -12948,6 +12960,12 @@ class Sema final : public SemaBase { TemplateDecl *Entity, BuildingDeductionGuidesTag, SourceRange InstantiationRange = SourceRange()); + struct PartialOrderingTTP {}; + /// \brief Note that we are partial ordering template template parameters. + InstantiatingTemplate(Sema &SemaRef, SourceLocation ArgLoc, + PartialOrderingTTP, TemplateDecl *PArg, + SourceRange InstantiationRange = SourceRange()); + /// Note that we have finished instantiating this template. void Clear(); @@ -13008,20 +13026,28 @@ class Sema final : public SemaBase { /// dealing with a specialization. This is only relevant for function /// template specializations. /// + /// \param Pattern If non-NULL, indicates the pattern from which we will be + /// instantiating the definition of the given declaration, \p ND. This is + /// used to determine the proper set of template instantiation arguments for + /// friend function template specializations. + /// /// \param ForConstraintInstantiation when collecting arguments, /// ForConstraintInstantiation indicates we should continue looking when /// encountering a lambda generic call operator, and continue looking for /// arguments on an enclosing class template. + /// + /// \param SkipForSpecialization when specified, any template specializations + /// in a traversal would be ignored. + /// \param ForDefaultArgumentSubstitution indicates we should continue looking + /// when encountering a specialized member function template, rather than + /// returning immediately. MultiLevelTemplateArgumentList getTemplateInstantiationArgs( const NamedDecl *D, const DeclContext *DC = nullptr, bool Final = false, std::optional> Innermost = std::nullopt, - bool RelativeToPrimary = false, bool ForConstraintInstantiation = false); - - void getTemplateInstantiationArgs( - MultiLevelTemplateArgumentList &Result, const NamedDecl *D, - const DeclContext *DC = nullptr, bool Final = false, - std::optional> Innermost = std::nullopt, - bool RelativeToPrimary = false, bool ForConstraintInstantiation = false); + bool RelativeToPrimary = false, const FunctionDecl *Pattern = nullptr, + bool ForConstraintInstantiation = false, + bool SkipForSpecialization = false, + bool ForDefaultArgumentSubstitution = false); /// RAII object to handle the state changes required to synthesize /// a function body. @@ -13400,7 +13426,8 @@ class Sema final : public SemaBase { bool InstantiateClassTemplateSpecialization( SourceLocation PointOfInstantiation, ClassTemplateSpecializationDecl *ClassTemplateSpec, - TemplateSpecializationKind TSK, bool Complain = true); + TemplateSpecializationKind TSK, bool Complain = true, + bool PrimaryHasMatchedPackOnParmToNonPackOnArg = false); /// Instantiates the definitions of all of the member /// of the given class, which is an instantiation of a class template diff --git a/clang/include/clang/Sema/SemaOpenMP.h b/clang/include/clang/Sema/SemaOpenMP.h index 53191e7bb4272..80ad30b0f99ef 100644 --- a/clang/include/clang/Sema/SemaOpenMP.h +++ b/clang/include/clang/Sema/SemaOpenMP.h @@ -891,6 +891,11 @@ class SemaOpenMP : public SemaBase { SourceLocation StartLoc, SourceLocation LParenLoc, SourceLocation EndLoc); + /// Called on well-form 'permutation' clause after parsing its arguments. + OMPClause *ActOnOpenMPPermutationClause(ArrayRef PermExprs, + SourceLocation StartLoc, + SourceLocation LParenLoc, + SourceLocation EndLoc); /// Called on well-form 'full' clauses. OMPClause *ActOnOpenMPFullClause(SourceLocation StartLoc, SourceLocation EndLoc); diff --git a/clang/include/clang/Sema/TemplateDeduction.h b/clang/include/clang/Sema/TemplateDeduction.h index 28b014fd84e4b..9c12eef5c42a0 100644 --- a/clang/include/clang/Sema/TemplateDeduction.h +++ b/clang/include/clang/Sema/TemplateDeduction.h @@ -51,6 +51,11 @@ class TemplateDeductionInfo { /// Have we suppressed an error during deduction? bool HasSFINAEDiagnostic = false; + /// Have we matched any packs on the parameter side, versus any non-packs on + /// the argument side, in a context where the opposite matching is also + /// allowed? + bool MatchedPackOnParmToNonPackOnArg = false; + /// The template parameter depth for which we're performing deduction. unsigned DeducedDepth; @@ -87,6 +92,14 @@ class TemplateDeductionInfo { return DeducedDepth; } + bool hasMatchedPackOnParmToNonPackOnArg() const { + return MatchedPackOnParmToNonPackOnArg; + } + + void setMatchedPackOnParmToNonPackOnArg() { + MatchedPackOnParmToNonPackOnArg = true; + } + /// Get the number of explicitly-specified arguments. unsigned getNumExplicitArgs() const { return ExplicitArgs; diff --git a/clang/include/clang/Serialization/ASTReader.h b/clang/include/clang/Serialization/ASTReader.h index aa88560b259a3..ee4e897b24888 100644 --- a/clang/include/clang/Serialization/ASTReader.h +++ b/clang/include/clang/Serialization/ASTReader.h @@ -2527,7 +2527,7 @@ class BitsUnpacker { inline bool shouldSkipCheckingODR(const Decl *D) { return D->getASTContext().getLangOpts().SkipODRCheckInGMF && - D->isFromGlobalModule(); + (D->isFromGlobalModule() || D->isFromHeaderUnit()); } } // namespace clang diff --git a/clang/lib/AST/ByteCode/Compiler.cpp b/clang/lib/AST/ByteCode/Compiler.cpp index f270de1054c61..fe44238ea1186 100644 --- a/clang/lib/AST/ByteCode/Compiler.cpp +++ b/clang/lib/AST/ByteCode/Compiler.cpp @@ -3406,7 +3406,7 @@ bool Compiler::VisitCXXDeleteExpr(const CXXDeleteExpr *E) { if (!this->visit(Arg)) return false; - return this->emitFree(E->isArrayForm(), E); + return this->emitFree(E->isArrayForm(), E->isGlobalDelete(), E); } template diff --git a/clang/lib/AST/ByteCode/EvaluationResult.cpp b/clang/lib/AST/ByteCode/EvaluationResult.cpp index 627d4b2f65be9..c0d116cdf26c4 100644 --- a/clang/lib/AST/ByteCode/EvaluationResult.cpp +++ b/clang/lib/AST/ByteCode/EvaluationResult.cpp @@ -130,8 +130,9 @@ static bool CheckFieldsInitialized(InterpState &S, SourceLocation Loc, const Descriptor *Desc = BasePtr.getDeclDesc(); if (const auto *CD = dyn_cast_if_present(R->getDecl())) { const auto &BS = *std::next(CD->bases_begin(), I); - S.FFDiag(BS.getBaseTypeLoc(), diag::note_constexpr_uninitialized_base) - << B.Desc->getType() << BS.getSourceRange(); + SourceLocation TypeBeginLoc = BS.getBaseTypeLoc(); + S.FFDiag(TypeBeginLoc, diag::note_constexpr_uninitialized_base) + << B.Desc->getType() << SourceRange(TypeBeginLoc, BS.getEndLoc()); } else { S.FFDiag(Desc->getLocation(), diag::note_constexpr_uninitialized_base) << B.Desc->getType(); diff --git a/clang/lib/AST/ByteCode/Function.h b/clang/lib/AST/ByteCode/Function.h index 640bfa65644f0..7fe9aeb110120 100644 --- a/clang/lib/AST/ByteCode/Function.h +++ b/clang/lib/AST/ByteCode/Function.h @@ -222,6 +222,10 @@ class Function final { return ParamOffsets[ParamIndex]; } + PrimType getParamType(unsigned ParamIndex) const { + return ParamTypes[ParamIndex]; + } + private: /// Construct a function representing an actual function. Function(Program &P, FunctionDeclTy Source, unsigned ArgSize, diff --git a/clang/lib/AST/ByteCode/Interp.cpp b/clang/lib/AST/ByteCode/Interp.cpp index 050de67c2e77d..95715655cc9bb 100644 --- a/clang/lib/AST/ByteCode/Interp.cpp +++ b/clang/lib/AST/ByteCode/Interp.cpp @@ -22,6 +22,7 @@ #include "clang/AST/Expr.h" #include "clang/AST/ExprCXX.h" #include "clang/Basic/DiagnosticSema.h" +#include "clang/Basic/TargetInfo.h" #include "llvm/ADT/APSInt.h" #include "llvm/ADT/StringExtras.h" #include @@ -963,7 +964,7 @@ static bool runRecordDestructor(InterpState &S, CodePtr OpPC, return true; } -bool RunDestructors(InterpState &S, CodePtr OpPC, const Block *B) { +static bool RunDestructors(InterpState &S, CodePtr OpPC, const Block *B) { assert(B); const Descriptor *Desc = B->getDescriptor(); @@ -988,6 +989,89 @@ bool RunDestructors(InterpState &S, CodePtr OpPC, const Block *B) { return runRecordDestructor(S, OpPC, Pointer(const_cast(B)), Desc); } +bool Free(InterpState &S, CodePtr OpPC, bool DeleteIsArrayForm, + bool IsGlobalDelete) { + if (!CheckDynamicMemoryAllocation(S, OpPC)) + return false; + + const Expr *Source = nullptr; + const Block *BlockToDelete = nullptr; + { + // Extra scope for this so the block doesn't have this pointer + // pointing to it when we destroy it. + Pointer Ptr = S.Stk.pop(); + + // Deleteing nullptr is always fine. + if (Ptr.isZero()) + return true; + + // Remove base casts. + while (Ptr.isBaseClass()) + Ptr = Ptr.getBase(); + + if (!Ptr.isRoot() || Ptr.isOnePastEnd() || Ptr.isArrayElement()) { + const SourceInfo &Loc = S.Current->getSource(OpPC); + S.FFDiag(Loc, diag::note_constexpr_delete_subobject) + << Ptr.toDiagnosticString(S.getASTContext()) << Ptr.isOnePastEnd(); + return false; + } + + Source = Ptr.getDeclDesc()->asExpr(); + BlockToDelete = Ptr.block(); + + if (!CheckDeleteSource(S, OpPC, Source, Ptr)) + return false; + + // For a class type with a virtual destructor, the selected operator delete + // is the one looked up when building the destructor. + QualType AllocType = Ptr.getType(); + if (!DeleteIsArrayForm && !IsGlobalDelete) { + auto getVirtualOperatorDelete = [](QualType T) -> const FunctionDecl * { + if (const CXXRecordDecl *RD = T->getAsCXXRecordDecl()) + if (const CXXDestructorDecl *DD = RD->getDestructor()) + return DD->isVirtual() ? DD->getOperatorDelete() : nullptr; + return nullptr; + }; + + AllocType->dump(); + if (const FunctionDecl *VirtualDelete = + getVirtualOperatorDelete(AllocType); + VirtualDelete && + !VirtualDelete->isReplaceableGlobalAllocationFunction()) { + S.FFDiag(S.Current->getSource(OpPC), + diag::note_constexpr_new_non_replaceable) + << isa(VirtualDelete) << VirtualDelete; + return false; + } + } + } + assert(Source); + assert(BlockToDelete); + + // Invoke destructors before deallocating the memory. + if (!RunDestructors(S, OpPC, BlockToDelete)) + return false; + + DynamicAllocator &Allocator = S.getAllocator(); + const Descriptor *BlockDesc = BlockToDelete->getDescriptor(); + std::optional AllocForm = + Allocator.getAllocationForm(Source); + + if (!Allocator.deallocate(Source, BlockToDelete, S)) { + // Nothing has been deallocated, this must be a double-delete. + const SourceInfo &Loc = S.Current->getSource(OpPC); + S.FFDiag(Loc, diag::note_constexpr_double_delete); + return false; + } + + assert(AllocForm); + DynamicAllocator::Form DeleteForm = DeleteIsArrayForm + ? DynamicAllocator::Form::Array + : DynamicAllocator::Form::NonArray; + return CheckNewDeleteForms(S, OpPC, *AllocForm, DeleteForm, BlockDesc, + Source); +} + void diagnoseEnumValue(InterpState &S, CodePtr OpPC, const EnumDecl *ED, const APSInt &Value) { llvm::APInt Min; @@ -1415,6 +1499,46 @@ bool InvalidShuffleVectorIndex(InterpState &S, CodePtr OpPC, uint32_t Index) { return false; } +bool CheckPointerToIntegralCast(InterpState &S, CodePtr OpPC, + const Pointer &Ptr, unsigned BitWidth) { + if (Ptr.isDummy()) + return false; + + const SourceInfo &E = S.Current->getSource(OpPC); + S.CCEDiag(E, diag::note_constexpr_invalid_cast) + << 2 << S.getLangOpts().CPlusPlus << S.Current->getRange(OpPC); + + if (Ptr.isBlockPointer() && !Ptr.isZero()) { + // Only allow based lvalue casts if they are lossless. + if (S.getASTContext().getTargetInfo().getPointerWidth(LangAS::Default) != + BitWidth) + return Invalid(S, OpPC); + } + return true; +} + +bool CastPointerIntegralAP(InterpState &S, CodePtr OpPC, uint32_t BitWidth) { + const Pointer &Ptr = S.Stk.pop(); + + if (!CheckPointerToIntegralCast(S, OpPC, Ptr, BitWidth)) + return false; + + S.Stk.push>( + IntegralAP::from(Ptr.getIntegerRepresentation(), BitWidth)); + return true; +} + +bool CastPointerIntegralAPS(InterpState &S, CodePtr OpPC, uint32_t BitWidth) { + const Pointer &Ptr = S.Stk.pop(); + + if (!CheckPointerToIntegralCast(S, OpPC, Ptr, BitWidth)) + return false; + + S.Stk.push>( + IntegralAP::from(Ptr.getIntegerRepresentation(), BitWidth)); + return true; +} + // https://github.com/llvm/llvm-project/issues/102513 #if defined(_WIN32) && !defined(__clang__) && !defined(NDEBUG) #pragma optimize("", off) diff --git a/clang/lib/AST/ByteCode/Interp.h b/clang/lib/AST/ByteCode/Interp.h index 2c5538d221bf0..dece95971b761 100644 --- a/clang/lib/AST/ByteCode/Interp.h +++ b/clang/lib/AST/ByteCode/Interp.h @@ -2289,53 +2289,22 @@ static inline bool CastFloatingIntegralAPS(InterpState &S, CodePtr OpPC, return CheckFloatResult(S, OpPC, F, Status, FPO); } +bool CheckPointerToIntegralCast(InterpState &S, CodePtr OpPC, + const Pointer &Ptr, unsigned BitWidth); +bool CastPointerIntegralAP(InterpState &S, CodePtr OpPC, uint32_t BitWidth); +bool CastPointerIntegralAPS(InterpState &S, CodePtr OpPC, uint32_t BitWidth); + template ::T> bool CastPointerIntegral(InterpState &S, CodePtr OpPC) { const Pointer &Ptr = S.Stk.pop(); - if (Ptr.isDummy()) + if (!CheckPointerToIntegralCast(S, OpPC, Ptr, T::bitWidth())) return false; - const SourceInfo &E = S.Current->getSource(OpPC); - S.CCEDiag(E, diag::note_constexpr_invalid_cast) - << 2 << S.getLangOpts().CPlusPlus << S.Current->getRange(OpPC); - S.Stk.push(T::from(Ptr.getIntegerRepresentation())); return true; } -static inline bool CastPointerIntegralAP(InterpState &S, CodePtr OpPC, - uint32_t BitWidth) { - const Pointer &Ptr = S.Stk.pop(); - - if (Ptr.isDummy()) - return false; - - const SourceInfo &E = S.Current->getSource(OpPC); - S.CCEDiag(E, diag::note_constexpr_invalid_cast) - << 2 << S.getLangOpts().CPlusPlus << S.Current->getRange(OpPC); - - S.Stk.push>( - IntegralAP::from(Ptr.getIntegerRepresentation(), BitWidth)); - return true; -} - -static inline bool CastPointerIntegralAPS(InterpState &S, CodePtr OpPC, - uint32_t BitWidth) { - const Pointer &Ptr = S.Stk.pop(); - - if (Ptr.isDummy()) - return false; - - const SourceInfo &E = S.Current->getSource(OpPC); - S.CCEDiag(E, diag::note_constexpr_invalid_cast) - << 2 << S.getLangOpts().CPlusPlus << S.Current->getRange(OpPC); - - S.Stk.push>( - IntegralAP::from(Ptr.getIntegerRepresentation(), BitWidth)); - return true; -} - template ::T> static inline bool CastIntegralFixedPoint(InterpState &S, CodePtr OpPC, uint32_t FPS) { @@ -3007,61 +2976,8 @@ inline bool AllocCN(InterpState &S, CodePtr OpPC, const Descriptor *ElementDesc, return true; } -bool RunDestructors(InterpState &S, CodePtr OpPC, const Block *B); -static inline bool Free(InterpState &S, CodePtr OpPC, bool DeleteIsArrayForm) { - if (!CheckDynamicMemoryAllocation(S, OpPC)) - return false; - - const Expr *Source = nullptr; - const Block *BlockToDelete = nullptr; - { - // Extra scope for this so the block doesn't have this pointer - // pointing to it when we destroy it. - const Pointer &Ptr = S.Stk.pop(); - - // Deleteing nullptr is always fine. - if (Ptr.isZero()) - return true; - - if (!Ptr.isRoot() || Ptr.isOnePastEnd() || Ptr.isArrayElement()) { - const SourceInfo &Loc = S.Current->getSource(OpPC); - S.FFDiag(Loc, diag::note_constexpr_delete_subobject) - << Ptr.toDiagnosticString(S.getASTContext()) << Ptr.isOnePastEnd(); - return false; - } - - Source = Ptr.getDeclDesc()->asExpr(); - BlockToDelete = Ptr.block(); - - if (!CheckDeleteSource(S, OpPC, Source, Ptr)) - return false; - } - assert(Source); - assert(BlockToDelete); - - // Invoke destructors before deallocating the memory. - if (!RunDestructors(S, OpPC, BlockToDelete)) - return false; - - DynamicAllocator &Allocator = S.getAllocator(); - const Descriptor *BlockDesc = BlockToDelete->getDescriptor(); - std::optional AllocForm = - Allocator.getAllocationForm(Source); - - if (!Allocator.deallocate(Source, BlockToDelete, S)) { - // Nothing has been deallocated, this must be a double-delete. - const SourceInfo &Loc = S.Current->getSource(OpPC); - S.FFDiag(Loc, diag::note_constexpr_double_delete); - return false; - } - - assert(AllocForm); - DynamicAllocator::Form DeleteForm = DeleteIsArrayForm - ? DynamicAllocator::Form::Array - : DynamicAllocator::Form::NonArray; - return CheckNewDeleteForms(S, OpPC, *AllocForm, DeleteForm, BlockDesc, - Source); -} +bool Free(InterpState &S, CodePtr OpPC, bool DeleteIsArrayForm, + bool IsGlobalDelete); static inline bool IsConstantContext(InterpState &S, CodePtr OpPC) { S.Stk.push(Boolean::from(S.inConstantContext())); diff --git a/clang/lib/AST/ByteCode/InterpBuiltin.cpp b/clang/lib/AST/ByteCode/InterpBuiltin.cpp index 98381254886e2..1765193f5bebb 100644 --- a/clang/lib/AST/ByteCode/InterpBuiltin.cpp +++ b/clang/lib/AST/ByteCode/InterpBuiltin.cpp @@ -38,6 +38,15 @@ static T getParam(const InterpFrame *Frame, unsigned Index) { return Frame->getParam(Offset); } +// static APSInt getAPSIntParam(InterpStack &Stk, size_t Offset = 0) { +static APSInt getAPSIntParam(const InterpFrame *Frame, unsigned Index) { + APSInt R; + unsigned Offset = Frame->getFunction()->getParamOffset(Index); + INT_TYPE_SWITCH(Frame->getFunction()->getParamType(Index), + R = Frame->getParam(Offset).toAPSInt()); + return R; +} + PrimType getIntPrimType(const InterpState &S) { const TargetInfo &TI = S.getASTContext().getTargetInfo(); unsigned IntWidth = TI.getIntWidth(); @@ -1273,6 +1282,44 @@ static bool interp__builtin_ia32_pext(InterpState &S, CodePtr OpPC, return true; } +static bool interp__builtin_ia32_addcarry_subborrow(InterpState &S, + CodePtr OpPC, + const InterpFrame *Frame, + const Function *Func, + const CallExpr *Call) { + if (!Call->getArg(0)->getType()->isIntegerType() || + !Call->getArg(1)->getType()->isIntegerType() || + !Call->getArg(2)->getType()->isIntegerType()) + return false; + + unsigned BuiltinOp = Func->getBuiltinID(); + APSInt CarryIn = getAPSIntParam(Frame, 0); + APSInt LHS = getAPSIntParam(Frame, 1); + APSInt RHS = getAPSIntParam(Frame, 2); + + bool IsAdd = BuiltinOp == clang::X86::BI__builtin_ia32_addcarryx_u32 || + BuiltinOp == clang::X86::BI__builtin_ia32_addcarryx_u64; + + unsigned BitWidth = LHS.getBitWidth(); + unsigned CarryInBit = CarryIn.ugt(0) ? 1 : 0; + APInt ExResult = + IsAdd ? (LHS.zext(BitWidth + 1) + (RHS.zext(BitWidth + 1) + CarryInBit)) + : (LHS.zext(BitWidth + 1) - (RHS.zext(BitWidth + 1) + CarryInBit)); + + APInt Result = ExResult.extractBits(BitWidth, 0); + APSInt CarryOut = + APSInt(ExResult.extractBits(1, BitWidth), /*IsUnsigned=*/true); + + Pointer &CarryOutPtr = S.Stk.peek(); + QualType CarryOutType = Call->getArg(3)->getType()->getPointeeType(); + PrimType CarryOutT = *S.getContext().classify(CarryOutType); + assignInteger(CarryOutPtr, CarryOutT, APSInt(Result, true)); + + pushInteger(S, CarryOut, Call->getType()); + + return true; +} + static bool interp__builtin_os_log_format_buffer_size(InterpState &S, CodePtr OpPC, const InterpFrame *Frame, @@ -1898,6 +1945,14 @@ bool InterpretBuiltin(InterpState &S, CodePtr OpPC, const Function *F, return false; break; + case clang::X86::BI__builtin_ia32_addcarryx_u32: + case clang::X86::BI__builtin_ia32_addcarryx_u64: + case clang::X86::BI__builtin_ia32_subborrow_u32: + case clang::X86::BI__builtin_ia32_subborrow_u64: + if (!interp__builtin_ia32_addcarry_subborrow(S, OpPC, Frame, F, Call)) + return false; + break; + case Builtin::BI__builtin_os_log_format_buffer_size: if (!interp__builtin_os_log_format_buffer_size(S, OpPC, Frame, F, Call)) return false; diff --git a/clang/lib/AST/ByteCode/Opcodes.td b/clang/lib/AST/ByteCode/Opcodes.td index 7b65138e5a3c9..4fa9b6d61d5ab 100644 --- a/clang/lib/AST/ByteCode/Opcodes.td +++ b/clang/lib/AST/ByteCode/Opcodes.td @@ -818,7 +818,7 @@ def AllocCN : Opcode { } def Free : Opcode { - let Args = [ArgBool]; + let Args = [ArgBool, ArgBool]; } def CheckNewTypeMismatch : Opcode { diff --git a/clang/lib/AST/Decl.cpp b/clang/lib/AST/Decl.cpp index 8f54b5f1589d4..84ef9f74582ef 100644 --- a/clang/lib/AST/Decl.cpp +++ b/clang/lib/AST/Decl.cpp @@ -2696,27 +2696,21 @@ VarDecl *VarDecl::getTemplateInstantiationPattern() const { if (isTemplateInstantiation(VDTemplSpec->getTemplateSpecializationKind())) { auto From = VDTemplSpec->getInstantiatedFrom(); if (auto *VTD = From.dyn_cast()) { - while (true) { - VTD = VTD->getMostRecentDecl(); - if (VTD->isMemberSpecialization()) - break; - if (auto *NewVTD = VTD->getInstantiatedFromMemberTemplate()) - VTD = NewVTD; - else + while (!VTD->isMemberSpecialization()) { + auto *NewVTD = VTD->getInstantiatedFromMemberTemplate(); + if (!NewVTD) break; + VTD = NewVTD; } return getDefinitionOrSelf(VTD->getTemplatedDecl()); } if (auto *VTPSD = From.dyn_cast()) { - while (true) { - VTPSD = VTPSD->getMostRecentDecl(); - if (VTPSD->isMemberSpecialization()) - break; - if (auto *NewVTPSD = VTPSD->getInstantiatedFromMember()) - VTPSD = NewVTPSD; - else + while (!VTPSD->isMemberSpecialization()) { + auto *NewVTPSD = VTPSD->getInstantiatedFromMember(); + if (!NewVTPSD) break; + VTPSD = NewVTPSD; } return getDefinitionOrSelf(VTPSD); } @@ -2725,17 +2719,15 @@ VarDecl *VarDecl::getTemplateInstantiationPattern() const { // If this is the pattern of a variable template, find where it was // instantiated from. FIXME: Is this necessary? - if (VarTemplateDecl *VTD = VD->getDescribedVarTemplate()) { - while (true) { - VTD = VTD->getMostRecentDecl(); - if (VTD->isMemberSpecialization()) - break; - if (auto *NewVTD = VTD->getInstantiatedFromMemberTemplate()) - VTD = NewVTD; - else + if (VarTemplateDecl *VarTemplate = VD->getDescribedVarTemplate()) { + while (!VarTemplate->isMemberSpecialization()) { + auto *NewVT = VarTemplate->getInstantiatedFromMemberTemplate(); + if (!NewVT) break; + VarTemplate = NewVT; } - return getDefinitionOrSelf(VTD->getTemplatedDecl()); + + return getDefinitionOrSelf(VarTemplate->getTemplatedDecl()); } if (VD == this) @@ -3067,7 +3059,6 @@ FunctionDecl::FunctionDecl(Kind DK, ASTContext &C, DeclContext *DC, FunctionDeclBits.IsIneligibleOrNotSelected = false; FunctionDeclBits.HasImplicitReturnZero = false; FunctionDeclBits.IsLateTemplateParsed = false; - FunctionDeclBits.IsInstantiatedFromMemberTemplate = false; FunctionDeclBits.ConstexprKind = static_cast(ConstexprKind); FunctionDeclBits.BodyContainsImmediateEscalatingExpression = false; FunctionDeclBits.InstantiationIsPending = false; @@ -4151,14 +4142,11 @@ FunctionDecl::getTemplateInstantiationPattern(bool ForDefinition) const { if (FunctionTemplateDecl *Primary = getPrimaryTemplate()) { // If we hit a point where the user provided a specialization of this // template, we're done looking. - while (true) { - Primary = Primary->getMostRecentDecl(); - if (ForDefinition && Primary->isMemberSpecialization()) - break; - if (auto *NewPrimary = Primary->getInstantiatedFromMemberTemplate()) - Primary = NewPrimary; - else + while (!ForDefinition || !Primary->isMemberSpecialization()) { + auto *NewPrimary = Primary->getInstantiatedFromMemberTemplate(); + if (!NewPrimary) break; + Primary = NewPrimary; } return getDefinitionOrSelf(Primary->getTemplatedDecl()); diff --git a/clang/lib/AST/DeclBase.cpp b/clang/lib/AST/DeclBase.cpp index f42857f20efc4..48b91dca1f6d9 100644 --- a/clang/lib/AST/DeclBase.cpp +++ b/clang/lib/AST/DeclBase.cpp @@ -1168,6 +1168,10 @@ bool Decl::isInNamedModule() const { return getOwningModule() && getOwningModule()->isNamedModule(); } +bool Decl::isFromHeaderUnit() const { + return getOwningModule() && getOwningModule()->isHeaderUnit(); +} + static Decl::Kind getKind(const Decl *D) { return D->getKind(); } static Decl::Kind getKind(const DeclContext *DC) { return DC->getDeclKind(); } diff --git a/clang/lib/AST/DeclCXX.cpp b/clang/lib/AST/DeclCXX.cpp index cfc7a9a218f25..1364ccc745ba0 100644 --- a/clang/lib/AST/DeclCXX.cpp +++ b/clang/lib/AST/DeclCXX.cpp @@ -2023,27 +2023,19 @@ const CXXRecordDecl *CXXRecordDecl::getTemplateInstantiationPattern() const { if (auto *TD = dyn_cast(this)) { auto From = TD->getInstantiatedFrom(); if (auto *CTD = From.dyn_cast()) { - while (true) { - CTD = CTD->getMostRecentDecl(); - if (CTD->isMemberSpecialization()) - break; - if (auto *NewCTD = CTD->getInstantiatedFromMemberTemplate()) - CTD = NewCTD; - else + while (auto *NewCTD = CTD->getInstantiatedFromMemberTemplate()) { + if (NewCTD->isMemberSpecialization()) break; + CTD = NewCTD; } return GetDefinitionOrSelf(CTD->getTemplatedDecl()); } if (auto *CTPSD = From.dyn_cast()) { - while (true) { - CTPSD = CTPSD->getMostRecentDecl(); - if (CTPSD->isMemberSpecialization()) - break; - if (auto *NewCTPSD = CTPSD->getInstantiatedFromMemberTemplate()) - CTPSD = NewCTPSD; - else + while (auto *NewCTPSD = CTPSD->getInstantiatedFromMember()) { + if (NewCTPSD->isMemberSpecialization()) break; + CTPSD = NewCTPSD; } return GetDefinitionOrSelf(CTPSD); } diff --git a/clang/lib/AST/DeclTemplate.cpp b/clang/lib/AST/DeclTemplate.cpp index d9b67b7bedf5a..6fe817c5ef1c6 100644 --- a/clang/lib/AST/DeclTemplate.cpp +++ b/clang/lib/AST/DeclTemplate.cpp @@ -309,16 +309,16 @@ bool TemplateDecl::isTypeAlias() const { void RedeclarableTemplateDecl::anchor() {} RedeclarableTemplateDecl::CommonBase *RedeclarableTemplateDecl::getCommonPtr() const { - if (CommonBase *C = getCommonPtrInternal()) - return C; + if (Common) + return Common; // Walk the previous-declaration chain until we either find a declaration // with a common pointer or we run out of previous declarations. SmallVector PrevDecls; for (const RedeclarableTemplateDecl *Prev = getPreviousDecl(); Prev; Prev = Prev->getPreviousDecl()) { - if (CommonBase *C = Prev->getCommonPtrInternal()) { - setCommonPtr(C); + if (Prev->Common) { + Common = Prev->Common; break; } @@ -326,18 +326,18 @@ RedeclarableTemplateDecl::CommonBase *RedeclarableTemplateDecl::getCommonPtr() c } // If we never found a common pointer, allocate one now. - if (!getCommonPtrInternal()) { + if (!Common) { // FIXME: If any of the declarations is from an AST file, we probably // need an update record to add the common data. - setCommonPtr(newCommon(getASTContext())); + Common = newCommon(getASTContext()); } // Update any previous declarations we saw with the common pointer. for (const RedeclarableTemplateDecl *Prev : PrevDecls) - Prev->setCommonPtr(getCommonPtrInternal()); + Prev->Common = Common; - return getCommonPtrInternal(); + return Common; } void RedeclarableTemplateDecl::loadLazySpecializationsImpl() const { @@ -463,17 +463,19 @@ void FunctionTemplateDecl::addSpecialization( } void FunctionTemplateDecl::mergePrevDecl(FunctionTemplateDecl *Prev) { + using Base = RedeclarableTemplateDecl; + // If we haven't created a common pointer yet, then it can just be created // with the usual method. - if (!getCommonPtrInternal()) + if (!Base::Common) return; - Common *ThisCommon = static_cast(getCommonPtrInternal()); + Common *ThisCommon = static_cast(Base::Common); Common *PrevCommon = nullptr; SmallVector PreviousDecls; for (; Prev; Prev = Prev->getPreviousDecl()) { - if (CommonBase *C = Prev->getCommonPtrInternal()) { - PrevCommon = static_cast(C); + if (Prev->Base::Common) { + PrevCommon = static_cast(Prev->Base::Common); break; } PreviousDecls.push_back(Prev); @@ -483,7 +485,7 @@ void FunctionTemplateDecl::mergePrevDecl(FunctionTemplateDecl *Prev) { // use this common pointer. if (!PrevCommon) { for (auto *D : PreviousDecls) - D->setCommonPtr(ThisCommon); + D->Base::Common = ThisCommon; return; } @@ -491,7 +493,7 @@ void FunctionTemplateDecl::mergePrevDecl(FunctionTemplateDecl *Prev) { assert(ThisCommon->Specializations.size() == 0 && "Can't merge incompatible declarations!"); - setCommonPtr(PrevCommon); + Base::Common = PrevCommon; } //===----------------------------------------------------------------------===// diff --git a/clang/lib/AST/OpenMPClause.cpp b/clang/lib/AST/OpenMPClause.cpp index eb15aa8440690..985c844362d95 100644 --- a/clang/lib/AST/OpenMPClause.cpp +++ b/clang/lib/AST/OpenMPClause.cpp @@ -971,6 +971,25 @@ OMPSizesClause *OMPSizesClause::CreateEmpty(const ASTContext &C, return new (Mem) OMPSizesClause(NumSizes); } +OMPPermutationClause *OMPPermutationClause::Create(const ASTContext &C, + SourceLocation StartLoc, + SourceLocation LParenLoc, + SourceLocation EndLoc, + ArrayRef Args) { + OMPPermutationClause *Clause = CreateEmpty(C, Args.size()); + Clause->setLocStart(StartLoc); + Clause->setLParenLoc(LParenLoc); + Clause->setLocEnd(EndLoc); + Clause->setArgRefs(Args); + return Clause; +} + +OMPPermutationClause *OMPPermutationClause::CreateEmpty(const ASTContext &C, + unsigned NumLoops) { + void *Mem = C.Allocate(totalSizeToAlloc(NumLoops)); + return new (Mem) OMPPermutationClause(NumLoops); +} + OMPFullClause *OMPFullClause::Create(const ASTContext &C, SourceLocation StartLoc, SourceLocation EndLoc) { @@ -1841,6 +1860,14 @@ void OMPClausePrinter::VisitOMPSizesClause(OMPSizesClause *Node) { OS << ")"; } +void OMPClausePrinter::VisitOMPPermutationClause(OMPPermutationClause *Node) { + OS << "permutation("; + llvm::interleaveComma(Node->getArgsRefs(), OS, [&](const Expr *E) { + E->printPretty(OS, nullptr, Policy, 0); + }); + OS << ")"; +} + void OMPClausePrinter::VisitOMPFullClause(OMPFullClause *Node) { OS << "full"; } void OMPClausePrinter::VisitOMPPartialClause(OMPPartialClause *Node) { diff --git a/clang/lib/AST/StmtProfile.cpp b/clang/lib/AST/StmtProfile.cpp index 299ac005c7fdb..4d177fd6c5968 100644 --- a/clang/lib/AST/StmtProfile.cpp +++ b/clang/lib/AST/StmtProfile.cpp @@ -493,6 +493,13 @@ void OMPClauseProfiler::VisitOMPSizesClause(const OMPSizesClause *C) { Profiler->VisitExpr(E); } +void OMPClauseProfiler::VisitOMPPermutationClause( + const OMPPermutationClause *C) { + for (Expr *E : C->getArgsRefs()) + if (E) + Profiler->VisitExpr(E); +} + void OMPClauseProfiler::VisitOMPFullClause(const OMPFullClause *C) {} void OMPClauseProfiler::VisitOMPPartialClause(const OMPPartialClause *C) { diff --git a/clang/lib/Basic/OpenMPKinds.cpp b/clang/lib/Basic/OpenMPKinds.cpp index 630a8898aa229..8d2460bc74fa3 100644 --- a/clang/lib/Basic/OpenMPKinds.cpp +++ b/clang/lib/Basic/OpenMPKinds.cpp @@ -188,6 +188,7 @@ unsigned clang::getOpenMPSimpleClauseType(OpenMPClauseKind Kind, StringRef Str, case OMPC_safelen: case OMPC_simdlen: case OMPC_sizes: + case OMPC_permutation: case OMPC_allocator: case OMPC_allocate: case OMPC_collapse: @@ -512,6 +513,7 @@ const char *clang::getOpenMPSimpleClauseTypeName(OpenMPClauseKind Kind, case OMPC_safelen: case OMPC_simdlen: case OMPC_sizes: + case OMPC_permutation: case OMPC_allocator: case OMPC_allocate: case OMPC_collapse: diff --git a/clang/lib/CIR/CMakeLists.txt b/clang/lib/CIR/CMakeLists.txt index d2ff200e0da5f..11cca734808df 100644 --- a/clang/lib/CIR/CMakeLists.txt +++ b/clang/lib/CIR/CMakeLists.txt @@ -2,3 +2,5 @@ include_directories(${LLVM_MAIN_SRC_DIR}/../mlir/include) include_directories(${CMAKE_BINARY_DIR}/tools/mlir/include) add_subdirectory(Dialect) +add_subdirectory(CodeGen) +add_subdirectory(FrontendAction) diff --git a/clang/lib/CIR/CodeGen/CIRGenModule.cpp b/clang/lib/CIR/CodeGen/CIRGenModule.cpp new file mode 100644 index 0000000000000..95e62326939fc --- /dev/null +++ b/clang/lib/CIR/CodeGen/CIRGenModule.cpp @@ -0,0 +1,32 @@ +//===- CIRGenModule.cpp - Per-Module state for CIR generation -------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This is the internal per-translation-unit state used for CIR translation. +// +//===----------------------------------------------------------------------===// + +#include "CIRGenModule.h" + +#include "clang/AST/ASTContext.h" +#include "clang/AST/DeclBase.h" + +#include "mlir/IR/BuiltinOps.h" +#include "mlir/IR/Location.h" +#include "mlir/IR/MLIRContext.h" + +using namespace cir; +CIRGenModule::CIRGenModule(mlir::MLIRContext &context, + clang::ASTContext &astctx, + const clang::CodeGenOptions &cgo, + DiagnosticsEngine &diags) + : astCtx(astctx), langOpts(astctx.getLangOpts()), + theModule{mlir::ModuleOp::create(mlir::UnknownLoc())}, + target(astCtx.getTargetInfo()) {} + +// Emit code for a single top level declaration. +void CIRGenModule::buildTopLevelDecl(Decl *decl) {} diff --git a/clang/lib/CIR/CodeGen/CIRGenModule.h b/clang/lib/CIR/CodeGen/CIRGenModule.h new file mode 100644 index 0000000000000..ab2a1d8864659 --- /dev/null +++ b/clang/lib/CIR/CodeGen/CIRGenModule.h @@ -0,0 +1,62 @@ +//===--- CIRGenModule.h - Per-Module state for CIR gen ----------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This is the internal per-translation-unit state used for CIR translation. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_LIB_CIR_CODEGEN_CIRGENMODULE_H +#define LLVM_CLANG_LIB_CIR_CODEGEN_CIRGENMODULE_H + +#include "CIRGenTypeCache.h" + +#include "mlir/IR/BuiltinOps.h" +#include "mlir/IR/MLIRContext.h" + +namespace clang { +class ASTContext; +class CodeGenOptions; +class Decl; +class DiagnosticsEngine; +class LangOptions; +class TargetInfo; +} // namespace clang + +using namespace clang; +namespace cir { + +/// This class organizes the cross-function state that is used while generating +/// CIR code. +class CIRGenModule : public CIRGenTypeCache { + CIRGenModule(CIRGenModule &) = delete; + CIRGenModule &operator=(CIRGenModule &) = delete; + +public: + CIRGenModule(mlir::MLIRContext &context, clang::ASTContext &astctx, + const clang::CodeGenOptions &cgo, + clang::DiagnosticsEngine &diags); + + ~CIRGenModule() = default; + +private: + /// Hold Clang AST information. + clang::ASTContext &astCtx; + + const clang::LangOptions &langOpts; + + /// A "module" matches a c/cpp source file: containing a list of functions. + mlir::ModuleOp theModule; + + const clang::TargetInfo ⌖ + +public: + void buildTopLevelDecl(clang::Decl *decl); +}; +} // namespace cir + +#endif // LLVM_CLANG_LIB_CIR_CODEGEN_CIRGENMODULE_H diff --git a/clang/lib/CIR/CodeGen/CIRGenTypeCache.h b/clang/lib/CIR/CodeGen/CIRGenTypeCache.h new file mode 100644 index 0000000000000..6478e0a078099 --- /dev/null +++ b/clang/lib/CIR/CodeGen/CIRGenTypeCache.h @@ -0,0 +1,27 @@ +//===--- CIRGenTypeCache.h - Commonly used LLVM types and info -*- C++ --*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This structure provides a set of common types useful during CIR emission. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_LIB_CIR_CIRGENTYPECACHE_H +#define LLVM_CLANG_LIB_CIR_CIRGENTYPECACHE_H + +namespace cir { + +/// This structure provides a set of types that are commonly used +/// during IR emission. It's initialized once in CodeGenModule's +/// constructor and then copied around into new CIRGenFunction's. +struct CIRGenTypeCache { + CIRGenTypeCache() = default; +}; + +} // namespace cir + +#endif // LLVM_CLANG_LIB_CIR_CODEGEN_CIRGENTYPECACHE_H diff --git a/clang/lib/CIR/CodeGen/CIRGenerator.cpp b/clang/lib/CIR/CodeGen/CIRGenerator.cpp new file mode 100644 index 0000000000000..159355a99ece8 --- /dev/null +++ b/clang/lib/CIR/CodeGen/CIRGenerator.cpp @@ -0,0 +1,43 @@ +//===--- CIRGenerator.cpp - Emit CIR from ASTs ----------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This builds an AST and converts it to CIR. +// +//===----------------------------------------------------------------------===// + +#include "CIRGenModule.h" + +#include "clang/AST/DeclGroup.h" +#include "clang/CIR/CIRGenerator.h" + +using namespace cir; +using namespace clang; + +void CIRGenerator::anchor() {} + +CIRGenerator::CIRGenerator(clang::DiagnosticsEngine &diags, + llvm::IntrusiveRefCntPtr vfs, + const CodeGenOptions &cgo) + : diags(diags), fs(std::move(vfs)), codeGenOpts{cgo} {} +CIRGenerator::~CIRGenerator() = default; + +void CIRGenerator::Initialize(ASTContext &astCtx) { + using namespace llvm; + + this->astCtx = &astCtx; + + cgm = std::make_unique(*mlirCtx, astCtx, codeGenOpts, diags); +} + +bool CIRGenerator::HandleTopLevelDecl(DeclGroupRef group) { + + for (Decl *decl : group) + cgm->buildTopLevelDecl(decl); + + return true; +} diff --git a/clang/lib/CIR/CodeGen/CMakeLists.txt b/clang/lib/CIR/CodeGen/CMakeLists.txt new file mode 100644 index 0000000000000..17a3aabfbd7f0 --- /dev/null +++ b/clang/lib/CIR/CodeGen/CMakeLists.txt @@ -0,0 +1,23 @@ +set( + LLVM_LINK_COMPONENTS + Core + Support +) + +get_property(dialect_libs GLOBAL PROPERTY MLIR_DIALECT_LIBS) + +add_clang_library(clangCIR + CIRGenerator.cpp + CIRGenModule.cpp + + DEPENDS + MLIRCIR + ${dialect_libs} + + LINK_LIBS + clangAST + clangBasic + clangLex + ${dialect_libs} + MLIRCIR +) diff --git a/clang/lib/CIR/FrontendAction/CIRGenAction.cpp b/clang/lib/CIR/FrontendAction/CIRGenAction.cpp new file mode 100644 index 0000000000000..72b9fa0c13c59 --- /dev/null +++ b/clang/lib/CIR/FrontendAction/CIRGenAction.cpp @@ -0,0 +1,72 @@ +//===--- CIRGenAction.cpp - LLVM Code generation Frontend Action ---------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "clang/CIR/FrontendAction/CIRGenAction.h" +#include "clang/CIR/CIRGenerator.h" +#include "clang/Frontend/CompilerInstance.h" + +#include "mlir/IR/MLIRContext.h" +#include "mlir/IR/OwningOpRef.h" + +using namespace cir; +using namespace clang; + +namespace cir { + +class CIRGenConsumer : public clang::ASTConsumer { + + virtual void anchor(); + + std::unique_ptr OutputStream; + + IntrusiveRefCntPtr FS; + std::unique_ptr Gen; + +public: + CIRGenConsumer(CIRGenAction::OutputType Action, + DiagnosticsEngine &DiagnosticsEngine, + IntrusiveRefCntPtr VFS, + const HeaderSearchOptions &HeaderSearchOptions, + const CodeGenOptions &CodeGenOptions, + const TargetOptions &TargetOptions, + const LangOptions &LangOptions, + const FrontendOptions &FEOptions, + std::unique_ptr OS) + : OutputStream(std::move(OS)), FS(VFS), + Gen(std::make_unique(DiagnosticsEngine, std::move(VFS), + CodeGenOptions)) {} + + bool HandleTopLevelDecl(DeclGroupRef D) override { + Gen->HandleTopLevelDecl(D); + return true; + } +}; +} // namespace cir + +void CIRGenConsumer::anchor() {} + +CIRGenAction::CIRGenAction(OutputType Act, mlir::MLIRContext *MLIRCtx) + : MLIRCtx(MLIRCtx ? MLIRCtx : new mlir::MLIRContext), Action(Act) {} + +CIRGenAction::~CIRGenAction() { MLIRMod.release(); } + +std::unique_ptr +CIRGenAction::CreateASTConsumer(CompilerInstance &CI, StringRef InFile) { + std::unique_ptr Out = CI.takeOutputStream(); + + auto Result = std::make_unique( + Action, CI.getDiagnostics(), &CI.getVirtualFileSystem(), + CI.getHeaderSearchOpts(), CI.getCodeGenOpts(), CI.getTargetOpts(), + CI.getLangOpts(), CI.getFrontendOpts(), std::move(Out)); + + return Result; +} + +void EmitCIRAction::anchor() {} +EmitCIRAction::EmitCIRAction(mlir::MLIRContext *MLIRCtx) + : CIRGenAction(OutputType::EmitCIR, MLIRCtx) {} diff --git a/clang/lib/CIR/FrontendAction/CMakeLists.txt b/clang/lib/CIR/FrontendAction/CMakeLists.txt new file mode 100644 index 0000000000000..b0616ab5d64b0 --- /dev/null +++ b/clang/lib/CIR/FrontendAction/CMakeLists.txt @@ -0,0 +1,17 @@ +set(LLVM_LINK_COMPONENTS + Core + Support + ) + +get_property(dialect_libs GLOBAL PROPERTY MLIR_DIALECT_LIBS) + +add_clang_library(clangCIRFrontendAction + CIRGenAction.cpp + + LINK_LIBS + clangAST + clangFrontend + clangCIR + MLIRCIR + MLIRIR + ) diff --git a/clang/lib/CodeGen/BackendUtil.cpp b/clang/lib/CodeGen/BackendUtil.cpp index abc936f2c686d..f018130807519 100644 --- a/clang/lib/CodeGen/BackendUtil.cpp +++ b/clang/lib/CodeGen/BackendUtil.cpp @@ -1321,10 +1321,11 @@ static void runThinLTOBackend( Conf.CGFileType = getCodeGenFileType(Action); break; } - if (Error E = thinBackend( - Conf, -1, AddStream, *M, *CombinedIndex, ImportList, - ModuleToDefinedGVSummaries[M->getModuleIdentifier()], - /* ModuleMap */ nullptr, Conf.CodeGenOnly, CGOpts.CmdArgs)) { + if (Error E = + thinBackend(Conf, -1, AddStream, *M, *CombinedIndex, ImportList, + ModuleToDefinedGVSummaries[M->getModuleIdentifier()], + /*ModuleMap=*/nullptr, Conf.CodeGenOnly, + /*IRAddStream=*/nullptr, CGOpts.CmdArgs)) { handleAllErrors(std::move(E), [&](ErrorInfoBase &EIB) { errs() << "Error running ThinLTO backend: " << EIB.message() << '\n'; }); diff --git a/clang/lib/CodeGen/CGBuiltin.cpp b/clang/lib/CodeGen/CGBuiltin.cpp index 57705f2d2d042..2449b90a0e790 100644 --- a/clang/lib/CodeGen/CGBuiltin.cpp +++ b/clang/lib/CodeGen/CGBuiltin.cpp @@ -18876,18 +18876,25 @@ case Builtin::BI__builtin_hlsl_elementwise_isinf: { return EmitRuntimeCall(Intrinsic::getDeclaration(&CGM.getModule(), ID)); } case Builtin::BI__builtin_hlsl_elementwise_sign: { - Value *Op0 = EmitScalarExpr(E->getArg(0)); + auto *Arg0 = E->getArg(0); + Value *Op0 = EmitScalarExpr(Arg0); llvm::Type *Xty = Op0->getType(); llvm::Type *retType = llvm::Type::getInt32Ty(this->getLLVMContext()); if (Xty->isVectorTy()) { - auto *XVecTy = E->getArg(0)->getType()->getAs(); + auto *XVecTy = Arg0->getType()->getAs(); retType = llvm::VectorType::get( retType, ElementCount::getFixed(XVecTy->getNumElements())); } - assert((E->getArg(0)->getType()->hasFloatingRepresentation() || - E->getArg(0)->getType()->hasSignedIntegerRepresentation()) && + assert((Arg0->getType()->hasFloatingRepresentation() || + Arg0->getType()->hasIntegerRepresentation()) && "sign operand must have a float or int representation"); + if (Arg0->getType()->hasUnsignedIntegerRepresentation()) { + Value *Cmp = Builder.CreateICmpEQ(Op0, ConstantInt::get(Xty, 0)); + return Builder.CreateSelect(Cmp, ConstantInt::get(retType, 0), + ConstantInt::get(retType, 1), "hlsl.sign"); + } + return Builder.CreateIntrinsic( retType, CGM.getHLSLRuntime().getSignIntrinsic(), ArrayRef{Op0}, nullptr, "hlsl.sign"); diff --git a/clang/lib/Driver/Driver.cpp b/clang/lib/Driver/Driver.cpp index a5d43bdac2373..ba850cf3803e9 100644 --- a/clang/lib/Driver/Driver.cpp +++ b/clang/lib/Driver/Driver.cpp @@ -2029,7 +2029,7 @@ void Driver::PrintHelp(bool ShowHidden) const { void Driver::PrintVersion(const Compilation &C, raw_ostream &OS) const { if (IsFlangMode()) { - OS << getClangToolFullVersion("flang-new") << '\n'; + OS << getClangToolFullVersion("flang") << '\n'; } else { // FIXME: The following handlers should use a callback mechanism, we don't // know what the client would like to do. diff --git a/clang/lib/Driver/ToolChain.cpp b/clang/lib/Driver/ToolChain.cpp index de250322b3b34..4df3177095085 100644 --- a/clang/lib/Driver/ToolChain.cpp +++ b/clang/lib/Driver/ToolChain.cpp @@ -386,6 +386,9 @@ static const DriverSuffix *FindDriverSuffix(StringRef ProgName, size_t &Pos) { {"cl", "--driver-mode=cl"}, {"++", "--driver-mode=g++"}, {"flang", "--driver-mode=flang"}, + // For backwards compatibility, we create a symlink for `flang` called + // `flang-new`. This will be removed in the future. + {"flang-new", "--driver-mode=flang"}, {"clang-dxc", "--driver-mode=dxc"}, }; diff --git a/clang/lib/Driver/ToolChains/Clang.cpp b/clang/lib/Driver/ToolChains/Clang.cpp index 66ec0a7fd32f9..49b07322a21a5 100644 --- a/clang/lib/Driver/ToolChains/Clang.cpp +++ b/clang/lib/Driver/ToolChains/Clang.cpp @@ -5139,6 +5139,9 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA, } } + if (Args.hasArg(options::OPT_fclangir)) + CmdArgs.push_back("-fclangir"); + if (IsOpenMPDevice) { // We have to pass the triple of the host if compiling for an OpenMP device. std::string NormalizedTriple = @@ -9107,13 +9110,6 @@ void OffloadPackager::ConstructJob(Compilation &C, const JobAction &JA, llvm::copy_if(Features, std::back_inserter(FeatureArgs), [](StringRef Arg) { return !Arg.starts_with("-target"); }); - if (TC->getTriple().isAMDGPU()) { - for (StringRef Feature : llvm::split(Arch.split(':').second, ':')) { - FeatureArgs.emplace_back( - Args.MakeArgString(Feature.take_back() + Feature.drop_back())); - } - } - // TODO: We need to pass in the full target-id and handle it properly in the // linker wrapper. SmallVector Parts{ @@ -9123,7 +9119,7 @@ void OffloadPackager::ConstructJob(Compilation &C, const JobAction &JA, "kind=" + Kind.str(), }; - if (TC->getDriver().isUsingOffloadLTO() || TC->getTriple().isAMDGPU()) + if (TC->getDriver().isUsingOffloadLTO()) for (StringRef Feature : FeatureArgs) Parts.emplace_back("feature=" + Feature.str()); diff --git a/clang/lib/Driver/ToolChains/Cuda.cpp b/clang/lib/Driver/ToolChains/Cuda.cpp index 509cd87b28c37..7a70cf1c5694f 100644 --- a/clang/lib/Driver/ToolChains/Cuda.cpp +++ b/clang/lib/Driver/ToolChains/Cuda.cpp @@ -848,6 +848,10 @@ void CudaToolChain::addClangTargetOptions( if (CudaInstallation.version() >= CudaVersion::CUDA_90) CC1Args.push_back("-fcuda-allow-variadic-functions"); + if (DriverArgs.hasFlag(options::OPT_fcuda_short_ptr, + options::OPT_fno_cuda_short_ptr, false)) + CC1Args.append({"-mllvm", "--nvptx-short-ptr"}); + if (DriverArgs.hasArg(options::OPT_nogpulib)) return; @@ -873,10 +877,6 @@ void CudaToolChain::addClangTargetOptions( clang::CudaVersion CudaInstallationVersion = CudaInstallation.version(); - if (DriverArgs.hasFlag(options::OPT_fcuda_short_ptr, - options::OPT_fno_cuda_short_ptr, false)) - CC1Args.append({"-mllvm", "--nvptx-short-ptr"}); - if (CudaInstallationVersion >= CudaVersion::UNKNOWN) CC1Args.push_back( DriverArgs.MakeArgString(Twine("-target-sdk-version=") + diff --git a/clang/lib/Driver/ToolChains/Flang.cpp b/clang/lib/Driver/ToolChains/Flang.cpp index 98350690f8d20..19b43594b0081 100644 --- a/clang/lib/Driver/ToolChains/Flang.cpp +++ b/clang/lib/Driver/ToolChains/Flang.cpp @@ -787,6 +787,9 @@ void Flang::ConstructJob(Compilation &C, const JobAction &JA, if (Args.hasArg(options::OPT_fopenmp_force_usm)) CmdArgs.push_back("-fopenmp-force-usm"); + // TODO: OpenMP support isn't "done" yet, so for now we warn that it + // is experimental. + D.Diag(diag::warn_openmp_experimental); // FIXME: Clang supports a whole bunch more flags here. break; @@ -881,14 +884,12 @@ void Flang::ConstructJob(Compilation &C, const JobAction &JA, CmdArgs.push_back(Input.getFilename()); - // TODO: Replace flang-new with flang once the new driver replaces the - // throwaway driver - const char *Exec = Args.MakeArgString(D.GetProgramPath("flang-new", TC)); + const char *Exec = Args.MakeArgString(D.GetProgramPath("flang", TC)); C.addCommand(std::make_unique(JA, *this, ResponseFileSupport::AtFileUTF8(), Exec, CmdArgs, Inputs, Output)); } -Flang::Flang(const ToolChain &TC) : Tool("flang-new", "flang frontend", TC) {} +Flang::Flang(const ToolChain &TC) : Tool("flang", "flang frontend", TC) {} Flang::~Flang() {} diff --git a/clang/lib/Frontend/FrontendActions.cpp b/clang/lib/Frontend/FrontendActions.cpp index 64f90c493c105..e4b462b9b0fd8 100644 --- a/clang/lib/Frontend/FrontendActions.cpp +++ b/clang/lib/Frontend/FrontendActions.cpp @@ -457,6 +457,8 @@ class DefaultTemplateInstCallback : public TemplateInstantiationCallback { return "BuildingDeductionGuides"; case CodeSynthesisContext::TypeAliasTemplateInstantiation: return "TypeAliasTemplateInstantiation"; + case CodeSynthesisContext::PartialOrderingTTP: + return "PartialOrderingTTP"; } return ""; } diff --git a/clang/lib/Frontend/TextDiagnostic.cpp b/clang/lib/Frontend/TextDiagnostic.cpp index a264836a54398..4119ce6048d45 100644 --- a/clang/lib/Frontend/TextDiagnostic.cpp +++ b/clang/lib/Frontend/TextDiagnostic.cpp @@ -1252,10 +1252,10 @@ highlightLines(StringRef FileData, unsigned StartLineNumber, for (unsigned I = 0; I <= Spelling.size(); ++I) { // This line is done. if (I == Spelling.size() || isVerticalWhitespace(Spelling[I])) { - SmallVector &LineRanges = - SnippetRanges[L - StartLineNumber]; - if (L >= StartLineNumber) { + SmallVector &LineRanges = + SnippetRanges[L - StartLineNumber]; + if (L == TokenStartLine) // First line appendStyle(LineRanges, T, StartCol, LineLength); else if (L == TokenEndLine) // Last line diff --git a/clang/lib/FrontendTool/CMakeLists.txt b/clang/lib/FrontendTool/CMakeLists.txt index 51c379ade2704..bfc7652b4c118 100644 --- a/clang/lib/FrontendTool/CMakeLists.txt +++ b/clang/lib/FrontendTool/CMakeLists.txt @@ -12,6 +12,15 @@ set(link_libs clangRewriteFrontend ) +set(deps) + +if(CLANG_ENABLE_CIR) + list(APPEND link_libs + clangCIRFrontendAction + MLIRIR + ) +endif() + if(CLANG_ENABLE_ARCMT) list(APPEND link_libs clangARCMigrate @@ -29,7 +38,13 @@ add_clang_library(clangFrontendTool DEPENDS ClangDriverOptions + ${deps} LINK_LIBS ${link_libs} ) + +if(CLANG_ENABLE_CIR) + target_include_directories(clangFrontendTool PRIVATE ${LLVM_MAIN_SRC_DIR}/../mlir/include) + target_include_directories(clangFrontendTool PRIVATE ${CMAKE_BINARY_DIR}/tools/mlir/include) +endif() diff --git a/clang/lib/FrontendTool/ExecuteCompilerInvocation.cpp b/clang/lib/FrontendTool/ExecuteCompilerInvocation.cpp index 7476b1076d103..60fde03289cf3 100644 --- a/clang/lib/FrontendTool/ExecuteCompilerInvocation.cpp +++ b/clang/lib/FrontendTool/ExecuteCompilerInvocation.cpp @@ -31,6 +31,11 @@ #include "llvm/Support/BuryPointer.h" #include "llvm/Support/DynamicLibrary.h" #include "llvm/Support/ErrorHandling.h" + +#if CLANG_ENABLE_CIR +#include "clang/CIR/FrontendAction/CIRGenAction.h" +#endif + using namespace clang; using namespace llvm::opt; @@ -42,6 +47,13 @@ CreateFrontendBaseAction(CompilerInstance &CI) { StringRef Action("unknown"); (void)Action; + unsigned UseCIR = CI.getFrontendOpts().UseClangIRPipeline; + frontend::ActionKind Act = CI.getFrontendOpts().ProgramAction; + bool EmitsCIR = Act == EmitCIR; + + if (!UseCIR && EmitsCIR) + llvm::report_fatal_error("-emit-cir and only valid when using -fclangir"); + switch (CI.getFrontendOpts().ProgramAction) { case ASTDeclList: return std::make_unique(); case ASTDump: return std::make_unique(); @@ -54,7 +66,11 @@ CreateFrontendBaseAction(CompilerInstance &CI) { case EmitAssembly: return std::make_unique(); case EmitBC: return std::make_unique(); case EmitCIR: +#if CLANG_ENABLE_CIR + return std::make_unique<::cir::EmitCIRAction>(); +#else llvm_unreachable("CIR suppport not built into clang"); +#endif case EmitHTML: return std::make_unique(); case EmitLLVM: return std::make_unique(); case EmitLLVMOnly: return std::make_unique(); diff --git a/clang/lib/Headers/hlsl/hlsl_intrinsics.h b/clang/lib/Headers/hlsl/hlsl_intrinsics.h index f39a68ba847e9..813f8a317bf6b 100644 --- a/clang/lib/Headers/hlsl/hlsl_intrinsics.h +++ b/clang/lib/Headers/hlsl/hlsl_intrinsics.h @@ -2088,6 +2088,19 @@ int3 sign(int16_t3); _HLSL_AVAILABILITY(shadermodel, 6.2) _HLSL_BUILTIN_ALIAS(__builtin_hlsl_elementwise_sign) int4 sign(int16_t4); + +_HLSL_AVAILABILITY(shadermodel, 6.2) +_HLSL_BUILTIN_ALIAS(__builtin_hlsl_elementwise_sign) +int sign(uint16_t); +_HLSL_AVAILABILITY(shadermodel, 6.2) +_HLSL_BUILTIN_ALIAS(__builtin_hlsl_elementwise_sign) +int2 sign(uint16_t2); +_HLSL_AVAILABILITY(shadermodel, 6.2) +_HLSL_BUILTIN_ALIAS(__builtin_hlsl_elementwise_sign) +int3 sign(uint16_t3); +_HLSL_AVAILABILITY(shadermodel, 6.2) +_HLSL_BUILTIN_ALIAS(__builtin_hlsl_elementwise_sign) +int4 sign(uint16_t4); #endif _HLSL_16BIT_AVAILABILITY(shadermodel, 6.2) @@ -2112,6 +2125,15 @@ int3 sign(int3); _HLSL_BUILTIN_ALIAS(__builtin_hlsl_elementwise_sign) int4 sign(int4); +_HLSL_BUILTIN_ALIAS(__builtin_hlsl_elementwise_sign) +int sign(uint); +_HLSL_BUILTIN_ALIAS(__builtin_hlsl_elementwise_sign) +int2 sign(uint2); +_HLSL_BUILTIN_ALIAS(__builtin_hlsl_elementwise_sign) +int3 sign(uint3); +_HLSL_BUILTIN_ALIAS(__builtin_hlsl_elementwise_sign) +int4 sign(uint4); + _HLSL_BUILTIN_ALIAS(__builtin_hlsl_elementwise_sign) int sign(float); _HLSL_BUILTIN_ALIAS(__builtin_hlsl_elementwise_sign) @@ -2130,6 +2152,15 @@ int3 sign(int64_t3); _HLSL_BUILTIN_ALIAS(__builtin_hlsl_elementwise_sign) int4 sign(int64_t4); +_HLSL_BUILTIN_ALIAS(__builtin_hlsl_elementwise_sign) +int sign(uint64_t); +_HLSL_BUILTIN_ALIAS(__builtin_hlsl_elementwise_sign) +int2 sign(uint64_t2); +_HLSL_BUILTIN_ALIAS(__builtin_hlsl_elementwise_sign) +int3 sign(uint64_t3); +_HLSL_BUILTIN_ALIAS(__builtin_hlsl_elementwise_sign) +int4 sign(uint64_t4); + _HLSL_BUILTIN_ALIAS(__builtin_hlsl_elementwise_sign) int sign(double); _HLSL_BUILTIN_ALIAS(__builtin_hlsl_elementwise_sign) diff --git a/clang/lib/Parse/ParseOpenMP.cpp b/clang/lib/Parse/ParseOpenMP.cpp index 64dfcd4729699..108b532be4168 100644 --- a/clang/lib/Parse/ParseOpenMP.cpp +++ b/clang/lib/Parse/ParseOpenMP.cpp @@ -3080,6 +3080,18 @@ OMPClause *Parser::ParseOpenMPSizesClause() { OpenLoc, CloseLoc); } +OMPClause *Parser::ParseOpenMPPermutationClause() { + SourceLocation ClauseNameLoc, OpenLoc, CloseLoc; + SmallVector ArgExprs; + if (ParseOpenMPExprListClause(OMPC_permutation, ClauseNameLoc, OpenLoc, + CloseLoc, ArgExprs, + /*ReqIntConst=*/true)) + return nullptr; + + return Actions.OpenMP().ActOnOpenMPPermutationClause(ArgExprs, ClauseNameLoc, + OpenLoc, CloseLoc); +} + OMPClause *Parser::ParseOpenMPUsesAllocatorClause(OpenMPDirectiveKind DKind) { SourceLocation Loc = Tok.getLocation(); ConsumeAnyToken(); @@ -3377,6 +3389,14 @@ OMPClause *Parser::ParseOpenMPClause(OpenMPDirectiveKind DKind, Clause = ParseOpenMPSizesClause(); break; + case OMPC_permutation: + if (!FirstClause) { + Diag(Tok, diag::err_omp_more_one_clause) + << getOpenMPDirectiveName(DKind) << getOpenMPClauseName(CKind) << 0; + ErrorFound = true; + } + Clause = ParseOpenMPPermutationClause(); + break; case OMPC_uses_allocators: Clause = ParseOpenMPUsesAllocatorClause(DKind); break; diff --git a/clang/lib/Parse/ParseTemplate.cpp b/clang/lib/Parse/ParseTemplate.cpp index de29652abbfd9..0953cfc3c017e 100644 --- a/clang/lib/Parse/ParseTemplate.cpp +++ b/clang/lib/Parse/ParseTemplate.cpp @@ -331,6 +331,8 @@ Parser::ParseConceptDefinition(const ParsedTemplateInfo &TemplateInfo, if (!TryConsumeToken(tok::equal)) { Diag(Tok.getLocation(), diag::err_expected) << tok::equal; SkipUntil(tok::semi); + if (D) + D->setInvalidDecl(); return nullptr; } @@ -338,6 +340,8 @@ Parser::ParseConceptDefinition(const ParsedTemplateInfo &TemplateInfo, Actions.CorrectDelayedTyposInExpr(ParseConstraintExpression()); if (ConstraintExprResult.isInvalid()) { SkipUntil(tok::semi); + if (D) + D->setInvalidDecl(); return nullptr; } diff --git a/clang/lib/Sema/SemaAvailability.cpp b/clang/lib/Sema/SemaAvailability.cpp index e04cbeec16555..798cabaa31a47 100644 --- a/clang/lib/Sema/SemaAvailability.cpp +++ b/clang/lib/Sema/SemaAvailability.cpp @@ -1005,25 +1005,54 @@ bool DiagnoseUnguardedAvailability::VisitTypeLoc(TypeLoc Ty) { return true; } +struct ExtractedAvailabilityExpr { + const ObjCAvailabilityCheckExpr *E = nullptr; + bool isNegated = false; +}; + +ExtractedAvailabilityExpr extractAvailabilityExpr(const Expr *IfCond) { + const auto *E = IfCond; + bool IsNegated = false; + while (true) { + E = E->IgnoreParens(); + if (const auto *AE = dyn_cast(E)) { + return ExtractedAvailabilityExpr{AE, IsNegated}; + } + + const auto *UO = dyn_cast(E); + if (!UO || UO->getOpcode() != UO_LNot) { + return ExtractedAvailabilityExpr{}; + } + E = UO->getSubExpr(); + IsNegated = !IsNegated; + } +} + bool DiagnoseUnguardedAvailability::TraverseIfStmt(IfStmt *If) { - VersionTuple CondVersion; - if (auto *E = dyn_cast(If->getCond())) { - CondVersion = E->getVersion(); - - // If we're using the '*' case here or if this check is redundant, then we - // use the enclosing version to check both branches. - if (CondVersion.empty() || CondVersion <= AvailabilityStack.back()) - return TraverseStmt(If->getThen()) && TraverseStmt(If->getElse()); - } else { + ExtractedAvailabilityExpr IfCond = extractAvailabilityExpr(If->getCond()); + if (!IfCond.E) { // This isn't an availability checking 'if', we can just continue. return Base::TraverseIfStmt(If); } + VersionTuple CondVersion = IfCond.E->getVersion(); + // If we're using the '*' case here or if this check is redundant, then we + // use the enclosing version to check both branches. + if (CondVersion.empty() || CondVersion <= AvailabilityStack.back()) { + return TraverseStmt(If->getThen()) && TraverseStmt(If->getElse()); + } + + auto *Guarded = If->getThen(); + auto *Unguarded = If->getElse(); + if (IfCond.isNegated) { + std::swap(Guarded, Unguarded); + } + AvailabilityStack.push_back(CondVersion); - bool ShouldContinue = TraverseStmt(If->getThen()); + bool ShouldContinue = TraverseStmt(Guarded); AvailabilityStack.pop_back(); - return ShouldContinue && TraverseStmt(If->getElse()); + return ShouldContinue && TraverseStmt(Unguarded); } } // end anonymous namespace diff --git a/clang/lib/Sema/SemaConcept.cpp b/clang/lib/Sema/SemaConcept.cpp index e36ee06221371..998a148a7d24a 100644 --- a/clang/lib/Sema/SemaConcept.cpp +++ b/clang/lib/Sema/SemaConcept.cpp @@ -585,8 +585,8 @@ static bool CheckConstraintSatisfaction( ArrayRef TemplateArgs = TemplateArgsLists.getNumSubstitutedLevels() > 0 - ? TemplateArgsLists.getInnermost() - : ArrayRef{}; + ? TemplateArgsLists.getOutermost() + : ArrayRef {}; Sema::InstantiatingTemplate Inst(S, TemplateIDRange.getBegin(), Sema::InstantiatingTemplate::ConstraintsCheck{}, const_cast(Template), TemplateArgs, TemplateIDRange); @@ -834,6 +834,7 @@ Sema::SetupConstraintCheckingTemplateArgumentsAndScope( getTemplateInstantiationArgs(FD, FD->getLexicalDeclContext(), /*Final=*/false, /*Innermost=*/std::nullopt, /*RelativeToPrimary=*/true, + /*Pattern=*/nullptr, /*ForConstraintInstantiation=*/true); if (SetupConstraintScope(FD, TemplateArgs, MLTAL, Scope)) return std::nullopt; @@ -909,13 +910,15 @@ bool Sema::CheckFunctionConstraints(const FunctionDecl *FD, // Figure out the to-translation-unit depth for this function declaration for // the purpose of seeing if they differ by constraints. This isn't the same as // getTemplateDepth, because it includes already instantiated parents. -static unsigned CalculateTemplateDepthForConstraints(Sema &S, - const NamedDecl *ND) { +static unsigned +CalculateTemplateDepthForConstraints(Sema &S, const NamedDecl *ND, + bool SkipForSpecialization = false) { MultiLevelTemplateArgumentList MLTAL = S.getTemplateInstantiationArgs( ND, ND->getLexicalDeclContext(), /*Final=*/false, /*Innermost=*/std::nullopt, /*RelativeToPrimary=*/true, - /*ForConstraintInstantiation=*/true); + /*Pattern=*/nullptr, + /*ForConstraintInstantiation=*/true, SkipForSpecialization); return MLTAL.getNumLevels(); } @@ -954,7 +957,8 @@ static const Expr *SubstituteConstraintExpressionWithoutSatisfaction( DeclInfo.getDecl(), DeclInfo.getLexicalDeclContext(), /*Final=*/false, /*Innermost=*/std::nullopt, /*RelativeToPrimary=*/true, - /*ForConstraintInstantiation=*/true); + /*Pattern=*/nullptr, /*ForConstraintInstantiation=*/true, + /*SkipForSpecialization*/ false); if (MLTAL.getNumSubstitutedLevels() == 0) return ConstrExpr; @@ -1064,16 +1068,16 @@ bool Sema::AreConstraintExpressionsEqual(const NamedDecl *Old, bool Sema::FriendConstraintsDependOnEnclosingTemplate(const FunctionDecl *FD) { assert(FD->getFriendObjectKind() && "Must be a friend!"); - FunctionTemplateDecl *FTD = FD->getDescribedFunctionTemplate(); // The logic for non-templates is handled in ASTContext::isSameEntity, so we // don't have to bother checking 'DependsOnEnclosingTemplate' for a // non-function-template. - assert(FTD && "Non-function templates don't need to be checked"); + assert(FD->getDescribedFunctionTemplate() && + "Non-function templates don't need to be checked"); SmallVector ACs; - FTD->getAssociatedConstraints(ACs); + FD->getDescribedFunctionTemplate()->getAssociatedConstraints(ACs); - unsigned OldTemplateDepth = FTD->getTemplateParameters()->getDepth(); + unsigned OldTemplateDepth = CalculateTemplateDepthForConstraints(*this, FD); for (const Expr *Constraint : ACs) if (ConstraintExpressionDependsOnEnclosingTemplate(FD, OldTemplateDepth, Constraint)) @@ -1520,6 +1524,7 @@ static bool substituteParameterMappings(Sema &S, NormalizedConstraint &N, CSE->getNamedConcept(), CSE->getNamedConcept()->getLexicalDeclContext(), /*Final=*/false, CSE->getTemplateArguments(), /*RelativeToPrimary=*/true, + /*Pattern=*/nullptr, /*ForConstraintInstantiation=*/true); return substituteParameterMappings(S, N, CSE->getNamedConcept(), MLTAL, @@ -1800,8 +1805,8 @@ bool Sema::IsAtLeastAsConstrained(NamedDecl *D1, return false; } - unsigned Depth1 = CalculateTemplateDepthForConstraints(*this, D1); - unsigned Depth2 = CalculateTemplateDepthForConstraints(*this, D2); + unsigned Depth1 = CalculateTemplateDepthForConstraints(*this, D1, true); + unsigned Depth2 = CalculateTemplateDepthForConstraints(*this, D2, true); for (size_t I = 0; I != AC1.size() && I != AC2.size(); ++I) { if (Depth2 > Depth1) { diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp index 072f43d360ee1..118873bc93ad4 100644 --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -4510,10 +4510,10 @@ void Sema::MergeVarDecl(VarDecl *New, LookupResult &Previous) { adjustDeclContextForDeclaratorDecl(New, Old); // Ensure the template parameters are compatible. - if (NewTemplate && !TemplateParameterListsAreEqual( - NewTemplate, NewTemplate->getTemplateParameters(), - OldTemplate, OldTemplate->getTemplateParameters(), - /*Complain=*/true, TPL_TemplateMatch)) + if (NewTemplate && + !TemplateParameterListsAreEqual(NewTemplate->getTemplateParameters(), + OldTemplate->getTemplateParameters(), + /*Complain=*/true, TPL_TemplateMatch)) return New->setInvalidDecl(); // C++ [class.mem]p1: @@ -7663,7 +7663,7 @@ NamedDecl *Sema::ActOnVariableDeclarator( : SourceLocation(); DeclResult Res = ActOnVarTemplateSpecialization( S, D, TInfo, Previous, TemplateKWLoc, TemplateParams, SC, - IsPartialSpecialization, IsMemberSpecialization); + IsPartialSpecialization); if (Res.isInvalid()) return nullptr; NewVD = cast(Res.get()); @@ -7682,10 +7682,6 @@ NamedDecl *Sema::ActOnVariableDeclarator( VarTemplateDecl::Create(Context, DC, D.getIdentifierLoc(), Name, TemplateParams, NewVD); NewVD->setDescribedVarTemplate(NewTemplate); - // If we are providing an explicit specialization of a static variable - // template, make a note of that. - if (IsMemberSpecialization) - NewTemplate->setMemberSpecialization(); } // If this decl has an auto type in need of deduction, make a note of the @@ -8063,6 +8059,12 @@ NamedDecl *Sema::ActOnVariableDeclarator( ? TPC_ClassTemplateMember : TPC_VarTemplate)) NewVD->setInvalidDecl(); + + // If we are providing an explicit specialization of a static variable + // template, make a note of that. + if (PrevVarTemplate && + PrevVarTemplate->getInstantiatedFromMemberTemplate()) + PrevVarTemplate->setMemberSpecialization(); } } @@ -9869,8 +9871,6 @@ Sema::ActOnFunctionDeclarator(Scope *S, Declarator &D, DeclContext *DC, NewFD); FunctionTemplate->setLexicalDeclContext(CurContext); NewFD->setDescribedFunctionTemplate(FunctionTemplate); - if (isMemberSpecialization) - FunctionTemplate->setMemberSpecialization(); // For source fidelity, store the other template param lists. if (TemplateParamLists.size() > 1) { @@ -12028,7 +12028,10 @@ bool Sema::CheckFunctionDeclaration(Scope *S, FunctionDecl *NewFD, // If this is an explicit specialization of a member that is a function // template, mark it as a member specialization. - if (IsMemberSpecialization) { + if (IsMemberSpecialization && + NewTemplateDecl->getInstantiatedFromMemberTemplate()) { + NewTemplateDecl->setMemberSpecialization(); + assert(OldTemplateDecl->isMemberSpecialization()); // Explicit specializations of a member template do not inherit deleted // status from the parent member template that they are specializing. if (OldFD->isDeleted()) { @@ -17090,8 +17093,8 @@ Sema::ActOnTag(Scope *S, unsigned TagSpec, TagUseKind TUK, SourceLocation KWLoc, DeclResult Result = CheckClassTemplate( S, TagSpec, TUK, KWLoc, SS, Name, NameLoc, Attrs, TemplateParams, AS, ModulePrivateLoc, - /*FriendLoc*/ SourceLocation(), TemplateParameterLists.drop_back(), - isMemberSpecialization, SkipBody); + /*FriendLoc*/ SourceLocation(), TemplateParameterLists.size() - 1, + TemplateParameterLists.data(), SkipBody); return Result.get(); } else { // The "template<>" header is extraneous. diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp index af983349a89b5..e2174ba926f17 100644 --- a/clang/lib/Sema/SemaDeclAttr.cpp +++ b/clang/lib/Sema/SemaDeclAttr.cpp @@ -1216,6 +1216,8 @@ static void handlePreferredName(Sema &S, Decl *D, const ParsedAttr &AL) { } bool Sema::isValidPointerAttrType(QualType T, bool RefOkay) { + if (T->isDependentType()) + return true; if (RefOkay) { if (T->isReferenceType()) return true; @@ -1284,7 +1286,7 @@ static void handleNonNullAttr(Sema &S, Decl *D, const ParsedAttr &AL) { for (unsigned I = 0, E = getFunctionOrMethodNumParams(D); I != E && !AnyPointers; ++I) { QualType T = getFunctionOrMethodParamType(D, I); - if (T->isDependentType() || S.isValidPointerAttrType(T)) + if (S.isValidPointerAttrType(T)) AnyPointers = true; } @@ -1409,8 +1411,7 @@ void Sema::AddAllocAlignAttr(Decl *D, const AttributeCommonInfo &CI, AllocAlignAttr TmpAttr(Context, CI, ParamIdx()); SourceLocation AttrLoc = CI.getLoc(); - if (!ResultType->isDependentType() && - !isValidPointerAttrType(ResultType, /* RefOkay */ true)) { + if (!isValidPointerAttrType(ResultType, /* RefOkay */ true)) { Diag(AttrLoc, diag::warn_attribute_return_pointers_refs_only) << &TmpAttr << CI.getRange() << getFunctionOrMethodResultSourceRange(D); return; diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp index 75d82c12e0c1f..9cb2ed02a3f76 100644 --- a/clang/lib/Sema/SemaDeclCXX.cpp +++ b/clang/lib/Sema/SemaDeclCXX.cpp @@ -17416,8 +17416,8 @@ DeclResult Sema::ActOnTemplatedFriendTag( return CheckClassTemplate(S, TagSpec, TagUseKind::Friend, TagLoc, SS, Name, NameLoc, Attr, TemplateParams, AS_public, /*ModulePrivateLoc=*/SourceLocation(), - FriendLoc, TempParamLists.drop_back(), - IsMemberSpecialization) + FriendLoc, TempParamLists.size() - 1, + TempParamLists.data()) .get(); } else { // The "template<>" header is extraneous. diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp index 7401e4cc8f092..e2141e03ca423 100644 --- a/clang/lib/Sema/SemaExpr.cpp +++ b/clang/lib/Sema/SemaExpr.cpp @@ -2470,7 +2470,15 @@ bool Sema::DiagnoseEmptyLookup(Scope *S, CXXScopeSpec &SS, LookupResult &R, LookupCtx ? LookupCtx : (SS.isEmpty() ? CurContext : nullptr); while (DC) { if (isa(DC)) { - LookupQualifiedName(R, DC); + if (ExplicitTemplateArgs) { + if (LookupTemplateName( + R, S, SS, Context.getRecordType(cast(DC)), + /*EnteringContext*/ false, TemplateNameIsRequired, + /*RequiredTemplateKind*/ nullptr, /*AllowTypoCorrection*/ true)) + return true; + } else { + LookupQualifiedName(R, DC); + } if (!R.empty()) { // Don't give errors about ambiguities in this lookup. diff --git a/clang/lib/Sema/SemaExprCXX.cpp b/clang/lib/Sema/SemaExprCXX.cpp index d490452e91c3b..8e9bcb10a80b4 100644 --- a/clang/lib/Sema/SemaExprCXX.cpp +++ b/clang/lib/Sema/SemaExprCXX.cpp @@ -8429,7 +8429,8 @@ ExprResult Sema::ActOnPseudoDestructorExpr(Scope *S, Expr *Base, QualType ObjectType; QualType T; TypeLocBuilder TLB; - if (CheckArrow(*this, ObjectType, Base, OpKind, OpLoc)) + if (CheckArrow(*this, ObjectType, Base, OpKind, OpLoc) || + DS.getTypeSpecType() == DeclSpec::TST_error) return ExprError(); switch (DS.getTypeSpecType()) { diff --git a/clang/lib/Sema/SemaHLSL.cpp b/clang/lib/Sema/SemaHLSL.cpp index d2d2df829e7b1..05e6e7800112d 100644 --- a/clang/lib/Sema/SemaHLSL.cpp +++ b/clang/lib/Sema/SemaHLSL.cpp @@ -1708,9 +1708,9 @@ static bool CheckNoDoubleVectors(Sema *S, CallExpr *TheCall) { return CheckArgsTypesAreCorrect(S, TheCall, S->Context.FloatTy, checkDoubleVector); } -static bool CheckFloatingOrSignedIntRepresentation(Sema *S, CallExpr *TheCall) { +static bool CheckFloatingOrIntRepresentation(Sema *S, CallExpr *TheCall) { auto checkAllSignedTypes = [](clang::QualType PassedType) -> bool { - return !PassedType->hasSignedIntegerRepresentation() && + return !PassedType->hasIntegerRepresentation() && !PassedType->hasFloatingRepresentation(); }; return CheckArgsTypesAreCorrect(S, TheCall, S->Context.IntTy, @@ -1966,7 +1966,7 @@ bool SemaHLSL::CheckBuiltinFunctionCall(unsigned BuiltinID, CallExpr *TheCall) { break; } case Builtin::BI__builtin_hlsl_elementwise_sign: { - if (CheckFloatingOrSignedIntRepresentation(&SemaRef, TheCall)) + if (CheckFloatingOrIntRepresentation(&SemaRef, TheCall)) return true; if (SemaRef.PrepareBuiltinElementwiseMathOneArgCall(TheCall)) return true; diff --git a/clang/lib/Sema/SemaLookup.cpp b/clang/lib/Sema/SemaLookup.cpp index f3f62474d0644..60fa195221c93 100644 --- a/clang/lib/Sema/SemaLookup.cpp +++ b/clang/lib/Sema/SemaLookup.cpp @@ -3666,7 +3666,9 @@ Sema::LookupLiteralOperator(Scope *S, LookupResult &R, TemplateArgumentLoc Arg(TemplateArgument(StringLit), StringLit); if (CheckTemplateArgument( Params->getParam(0), Arg, FD, R.getNameLoc(), R.getNameLoc(), - 0, SugaredChecked, CanonicalChecked, CTAK_Specified) || + 0, SugaredChecked, CanonicalChecked, CTAK_Specified, + /*PartialOrdering=*/false, + /*MatchedPackOnParmToNonPackOnArg=*/nullptr) || Trap.hasErrorOccurred()) IsTemplate = false; } diff --git a/clang/lib/Sema/SemaOpenMP.cpp b/clang/lib/Sema/SemaOpenMP.cpp index 74ab52106e323..d3e696a79b94f 100644 --- a/clang/lib/Sema/SemaOpenMP.cpp +++ b/clang/lib/Sema/SemaOpenMP.cpp @@ -14956,7 +14956,9 @@ StmtResult SemaOpenMP::ActOnOpenMPInterchangeDirective( return StmtError(); // interchange without permutation clause swaps two loops. - constexpr size_t NumLoops = 2; + const OMPPermutationClause *PermutationClause = + OMPExecutableDirective::getSingleClause(Clauses); + size_t NumLoops = PermutationClause ? PermutationClause->getNumLoops() : 2; // Verify and diagnose loop nest. SmallVector LoopHelpers(NumLoops); @@ -14971,6 +14973,12 @@ StmtResult SemaOpenMP::ActOnOpenMPInterchangeDirective( return OMPInterchangeDirective::Create(Context, StartLoc, EndLoc, Clauses, NumLoops, AStmt, nullptr, nullptr); + // An invalid expression in the permutation clause is set to nullptr in + // ActOnOpenMPPermutationClause. + if (PermutationClause && + llvm::is_contained(PermutationClause->getArgsRefs(), nullptr)) + return StmtError(); + assert(LoopHelpers.size() == NumLoops && "Expecting loop iteration space dimensionaly to match number of " "affected loops"); @@ -14979,7 +14987,44 @@ StmtResult SemaOpenMP::ActOnOpenMPInterchangeDirective( "affected loops"); // Decode the permutation clause. - constexpr uint64_t Permutation[] = {1, 0}; + SmallVector Permutation; + if (!PermutationClause) { + Permutation = {1, 0}; + } else { + ArrayRef PermArgs = PermutationClause->getArgsRefs(); + llvm::BitVector Flags(PermArgs.size()); + for (Expr *PermArg : PermArgs) { + std::optional PermCstExpr = + PermArg->getIntegerConstantExpr(Context); + if (!PermCstExpr) + continue; + uint64_t PermInt = PermCstExpr->getZExtValue(); + assert(1 <= PermInt && PermInt <= NumLoops && + "Must be a permutation; diagnostic emitted in " + "ActOnOpenMPPermutationClause"); + if (Flags[PermInt - 1]) { + SourceRange ExprRange(PermArg->getBeginLoc(), PermArg->getEndLoc()); + Diag(PermArg->getExprLoc(), + diag::err_omp_interchange_permutation_value_repeated) + << PermInt << ExprRange; + continue; + } + Flags[PermInt - 1] = true; + + Permutation.push_back(PermInt - 1); + } + + if (Permutation.size() != NumLoops) + return StmtError(); + } + + // Nothing to transform with trivial permutation. + if (NumLoops <= 1 || llvm::all_of(llvm::enumerate(Permutation), [](auto P) { + auto [Idx, Arg] = P; + return Idx == Arg; + })) + return OMPInterchangeDirective::Create(Context, StartLoc, EndLoc, Clauses, + NumLoops, AStmt, AStmt, nullptr); // Find the affected loops. SmallVector LoopStmts(NumLoops, nullptr); @@ -16111,6 +16156,44 @@ OMPClause *SemaOpenMP::ActOnOpenMPSizesClause(ArrayRef SizeExprs, SanitizedSizeExprs); } +OMPClause *SemaOpenMP::ActOnOpenMPPermutationClause(ArrayRef PermExprs, + SourceLocation StartLoc, + SourceLocation LParenLoc, + SourceLocation EndLoc) { + size_t NumLoops = PermExprs.size(); + SmallVector SanitizedPermExprs; + llvm::append_range(SanitizedPermExprs, PermExprs); + + for (Expr *&PermExpr : SanitizedPermExprs) { + // Skip if template-dependent or already sanitized, e.g. during a partial + // template instantiation. + if (!PermExpr || PermExpr->isInstantiationDependent()) + continue; + + llvm::APSInt PermVal; + ExprResult PermEvalExpr = SemaRef.VerifyIntegerConstantExpression( + PermExpr, &PermVal, Sema::AllowFold); + bool IsValid = PermEvalExpr.isUsable(); + if (IsValid) + PermExpr = PermEvalExpr.get(); + + if (IsValid && (PermVal < 1 || NumLoops < PermVal)) { + SourceRange ExprRange(PermEvalExpr.get()->getBeginLoc(), + PermEvalExpr.get()->getEndLoc()); + Diag(PermEvalExpr.get()->getExprLoc(), + diag::err_omp_interchange_permutation_value_range) + << NumLoops << ExprRange; + IsValid = false; + } + + if (!PermExpr->isInstantiationDependent() && !IsValid) + PermExpr = nullptr; + } + + return OMPPermutationClause::Create(getASTContext(), StartLoc, LParenLoc, + EndLoc, SanitizedPermExprs); +} + OMPClause *SemaOpenMP::ActOnOpenMPFullClause(SourceLocation StartLoc, SourceLocation EndLoc) { return OMPFullClause::Create(getASTContext(), StartLoc, EndLoc); diff --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp index 2cde8131108fb..f545e9341e1ae 100644 --- a/clang/lib/Sema/SemaOverload.cpp +++ b/clang/lib/Sema/SemaOverload.cpp @@ -6864,7 +6864,8 @@ void Sema::AddOverloadCandidate( OverloadCandidateSet &CandidateSet, bool SuppressUserConversions, bool PartialOverloading, bool AllowExplicit, bool AllowExplicitConversions, ADLCallKind IsADLCandidate, ConversionSequenceList EarlyConversions, - OverloadCandidateParamOrder PO, bool AggregateCandidateDeduction) { + OverloadCandidateParamOrder PO, bool AggregateCandidateDeduction, + bool HasMatchedPackOnParmToNonPackOnArg) { const FunctionProtoType *Proto = dyn_cast(Function->getType()->getAs()); assert(Proto && "Functions without a prototype cannot be overloaded"); @@ -6883,7 +6884,8 @@ void Sema::AddOverloadCandidate( AddMethodCandidate(Method, FoundDecl, Method->getParent(), QualType(), Expr::Classification::makeSimpleLValue(), Args, CandidateSet, SuppressUserConversions, - PartialOverloading, EarlyConversions, PO); + PartialOverloading, EarlyConversions, PO, + HasMatchedPackOnParmToNonPackOnArg); return; } // We treat a constructor like a non-member function, since its object @@ -6926,6 +6928,8 @@ void Sema::AddOverloadCandidate( CandidateSet.getRewriteInfo().getRewriteKind(Function, PO); Candidate.IsADLCandidate = IsADLCandidate; Candidate.ExplicitCallArguments = Args.size(); + Candidate.HasMatchedPackOnParmToNonPackOnArg = + HasMatchedPackOnParmToNonPackOnArg; // Explicit functions are not actually candidates at all if we're not // allowing them in this context, but keep them around so we can point @@ -7453,16 +7457,13 @@ void Sema::AddMethodCandidate(DeclAccessPair FoundDecl, QualType ObjectType, } } -void -Sema::AddMethodCandidate(CXXMethodDecl *Method, DeclAccessPair FoundDecl, - CXXRecordDecl *ActingContext, QualType ObjectType, - Expr::Classification ObjectClassification, - ArrayRef Args, - OverloadCandidateSet &CandidateSet, - bool SuppressUserConversions, - bool PartialOverloading, - ConversionSequenceList EarlyConversions, - OverloadCandidateParamOrder PO) { +void Sema::AddMethodCandidate( + CXXMethodDecl *Method, DeclAccessPair FoundDecl, + CXXRecordDecl *ActingContext, QualType ObjectType, + Expr::Classification ObjectClassification, ArrayRef Args, + OverloadCandidateSet &CandidateSet, bool SuppressUserConversions, + bool PartialOverloading, ConversionSequenceList EarlyConversions, + OverloadCandidateParamOrder PO, bool HasMatchedPackOnParmToNonPackOnArg) { const FunctionProtoType *Proto = dyn_cast(Method->getType()->getAs()); assert(Proto && "Methods without a prototype cannot be overloaded"); @@ -7493,6 +7494,8 @@ Sema::AddMethodCandidate(CXXMethodDecl *Method, DeclAccessPair FoundDecl, Candidate.TookAddressOfOverload = CandidateSet.getKind() == OverloadCandidateSet::CSK_AddressOfOverloadSet; Candidate.ExplicitCallArguments = Args.size(); + Candidate.HasMatchedPackOnParmToNonPackOnArg = + HasMatchedPackOnParmToNonPackOnArg; bool IgnoreExplicitObject = (Method->isExplicitObjectMemberFunction() && @@ -7663,8 +7666,8 @@ void Sema::AddMethodTemplateCandidate( ConversionSequenceList Conversions; if (TemplateDeductionResult Result = DeduceTemplateArguments( MethodTmpl, ExplicitTemplateArgs, Args, Specialization, Info, - PartialOverloading, /*AggregateDeductionCandidate=*/false, ObjectType, - ObjectClassification, + PartialOverloading, /*AggregateDeductionCandidate=*/false, + /*PartialOrdering=*/false, ObjectType, ObjectClassification, [&](ArrayRef ParamTypes) { return CheckNonDependentConversions( MethodTmpl, ParamTypes, Args, CandidateSet, Conversions, @@ -7702,7 +7705,8 @@ void Sema::AddMethodTemplateCandidate( AddMethodCandidate(cast(Specialization), FoundDecl, ActingContext, ObjectType, ObjectClassification, Args, CandidateSet, SuppressUserConversions, PartialOverloading, - Conversions, PO); + Conversions, PO, + Info.hasMatchedPackOnParmToNonPackOnArg()); } /// Determine whether a given function template has a simple explicit specifier @@ -7748,6 +7752,7 @@ void Sema::AddTemplateOverloadCandidate( if (TemplateDeductionResult Result = DeduceTemplateArguments( FunctionTemplate, ExplicitTemplateArgs, Args, Specialization, Info, PartialOverloading, AggregateCandidateDeduction, + /*PartialOrdering=*/false, /*ObjectType=*/QualType(), /*ObjectClassification=*/Expr::Classification(), [&](ArrayRef ParamTypes) { @@ -7788,7 +7793,8 @@ void Sema::AddTemplateOverloadCandidate( Specialization, FoundDecl, Args, CandidateSet, SuppressUserConversions, PartialOverloading, AllowExplicit, /*AllowExplicitConversions=*/false, IsADLCandidate, Conversions, PO, - Info.AggregateDeductionCandidateHasMismatchedArity); + Info.AggregateDeductionCandidateHasMismatchedArity, + Info.hasMatchedPackOnParmToNonPackOnArg()); } bool Sema::CheckNonDependentConversions( @@ -7910,7 +7916,8 @@ void Sema::AddConversionCandidate( CXXConversionDecl *Conversion, DeclAccessPair FoundDecl, CXXRecordDecl *ActingContext, Expr *From, QualType ToType, OverloadCandidateSet &CandidateSet, bool AllowObjCConversionOnExplicit, - bool AllowExplicit, bool AllowResultConversion) { + bool AllowExplicit, bool AllowResultConversion, + bool HasMatchedPackOnParmToNonPackOnArg) { assert(!Conversion->getDescribedFunctionTemplate() && "Conversion function templates use AddTemplateConversionCandidate"); QualType ConvType = Conversion->getConversionType().getNonReferenceType(); @@ -7955,6 +7962,8 @@ void Sema::AddConversionCandidate( Candidate.FinalConversion.setAllToTypes(ToType); Candidate.Viable = true; Candidate.ExplicitCallArguments = 1; + Candidate.HasMatchedPackOnParmToNonPackOnArg = + HasMatchedPackOnParmToNonPackOnArg; // Explicit functions are not actually candidates at all if we're not // allowing them in this context, but keep them around so we can point @@ -8156,7 +8165,8 @@ void Sema::AddTemplateConversionCandidate( assert(Specialization && "Missing function template specialization?"); AddConversionCandidate(Specialization, FoundDecl, ActingDC, From, ToType, CandidateSet, AllowObjCConversionOnExplicit, - AllowExplicit, AllowResultConversion); + AllowExplicit, AllowResultConversion, + Info.hasMatchedPackOnParmToNonPackOnArg()); } void Sema::AddSurrogateCandidate(CXXConversionDecl *Conversion, @@ -10509,6 +10519,10 @@ bool clang::isBetterOverloadCandidate( isa(Cand2.Function)) return isa(Cand1.Function); + if (Cand1.HasMatchedPackOnParmToNonPackOnArg != + Cand2.HasMatchedPackOnParmToNonPackOnArg) + return Cand2.HasMatchedPackOnParmToNonPackOnArg; + // -- F1 is a non-template function and F2 is a function template // specialization, or, if not that, bool Cand1IsSpecialization = Cand1.Function && diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp index dfd56debc75e9..62d0d0914fa30 100644 --- a/clang/lib/Sema/SemaTemplate.cpp +++ b/clang/lib/Sema/SemaTemplate.cpp @@ -1795,9 +1795,8 @@ DeclResult Sema::CheckClassTemplate( CXXScopeSpec &SS, IdentifierInfo *Name, SourceLocation NameLoc, const ParsedAttributesView &Attr, TemplateParameterList *TemplateParams, AccessSpecifier AS, SourceLocation ModulePrivateLoc, - SourceLocation FriendLoc, - ArrayRef OuterTemplateParamLists, - bool IsMemberSpecialization, SkipBodyInfo *SkipBody) { + SourceLocation FriendLoc, unsigned NumOuterTemplateParamLists, + TemplateParameterList **OuterTemplateParamLists, SkipBodyInfo *SkipBody) { assert(TemplateParams && TemplateParams->size() > 0 && "No template parameters"); assert(TUK != TagUseKind::Reference && @@ -1985,6 +1984,19 @@ DeclResult Sema::CheckClassTemplate( } if (PrevClassTemplate) { + // Ensure that the template parameter lists are compatible. Skip this check + // for a friend in a dependent context: the template parameter list itself + // could be dependent. + if (!(TUK == TagUseKind::Friend && CurContext->isDependentContext()) && + !TemplateParameterListsAreEqual( + TemplateCompareNewDeclInfo(SemanticContext ? SemanticContext + : CurContext, + CurContext, KWLoc), + TemplateParams, PrevClassTemplate, + PrevClassTemplate->getTemplateParameters(), /*Complain=*/true, + TPL_TemplateMatch)) + return true; + // C++ [temp.class]p4: // In a redeclaration, partial specialization, explicit // specialization or explicit instantiation of a class template, @@ -1999,6 +2011,30 @@ DeclResult Sema::CheckClassTemplate( Diag(PrevRecordDecl->getLocation(), diag::note_previous_use); Kind = PrevRecordDecl->getTagKind(); } + + // Check for redefinition of this class template. + if (TUK == TagUseKind::Definition) { + if (TagDecl *Def = PrevRecordDecl->getDefinition()) { + // If we have a prior definition that is not visible, treat this as + // simply making that previous definition visible. + NamedDecl *Hidden = nullptr; + if (SkipBody && !hasVisibleDefinition(Def, &Hidden)) { + SkipBody->ShouldSkip = true; + SkipBody->Previous = Def; + auto *Tmpl = cast(Hidden)->getDescribedClassTemplate(); + assert(Tmpl && "original definition of a class template is not a " + "class template?"); + makeMergedDefinitionVisible(Hidden); + makeMergedDefinitionVisible(Tmpl); + } else { + Diag(NameLoc, diag::err_redefinition) << Name; + Diag(Def->getLocation(), diag::note_previous_definition); + // FIXME: Would it make sense to try to "forget" the previous + // definition, as part of error recovery? + return true; + } + } + } } else if (PrevDecl) { // C++ [temp]p5: // A class template shall not have the same name as any other @@ -2010,6 +2046,23 @@ DeclResult Sema::CheckClassTemplate( return true; } + // Check the template parameter list of this declaration, possibly + // merging in the template parameter list from the previous class + // template declaration. Skip this check for a friend in a dependent + // context, because the template parameter list might be dependent. + if (!(TUK == TagUseKind::Friend && CurContext->isDependentContext()) && + CheckTemplateParameterList( + TemplateParams, + PrevClassTemplate ? GetTemplateParameterList(PrevClassTemplate) + : nullptr, + (SS.isSet() && SemanticContext && SemanticContext->isRecord() && + SemanticContext->isDependentContext()) + ? TPC_ClassTemplateMember + : TUK == TagUseKind::Friend ? TPC_FriendClassTemplate + : TPC_ClassTemplate, + SkipBody)) + Invalid = true; + if (SS.isSet()) { // If the name of the template was qualified, we must be defining the // template out-of-line. @@ -2036,8 +2089,10 @@ DeclResult Sema::CheckClassTemplate( PrevClassTemplate->getTemplatedDecl() : nullptr, /*DelayTypeCreation=*/true); SetNestedNameSpecifier(*this, NewClass, SS); - if (!OuterTemplateParamLists.empty()) - NewClass->setTemplateParameterListsInfo(Context, OuterTemplateParamLists); + if (NumOuterTemplateParamLists > 0) + NewClass->setTemplateParameterListsInfo( + Context, + llvm::ArrayRef(OuterTemplateParamLists, NumOuterTemplateParamLists)); // Add alignment attributes if necessary; these attributes are checked when // the ASTContext lays out the structure. @@ -2050,10 +2105,7 @@ DeclResult Sema::CheckClassTemplate( = ClassTemplateDecl::Create(Context, SemanticContext, NameLoc, DeclarationName(Name), TemplateParams, NewClass); - // If we are providing an explicit specialization of a member that is a - // class template, make a note of that. - if (IsMemberSpecialization) - NewTemplate->setMemberSpecialization(); + if (ShouldAddRedecl) NewTemplate->setPreviousDecl(PrevClassTemplate); @@ -2068,6 +2120,12 @@ DeclResult Sema::CheckClassTemplate( assert(T->isDependentType() && "Class template type is not dependent?"); (void)T; + // If we are providing an explicit specialization of a member that is a + // class template, make a note of that. + if (PrevClassTemplate && + PrevClassTemplate->getInstantiatedFromMemberTemplate()) + PrevClassTemplate->setMemberSpecialization(); + // Set the access specifier. if (!Invalid && TUK != TagUseKind::Friend && NewTemplate->getDeclContext()->isRecord()) @@ -2077,62 +2135,8 @@ DeclResult Sema::CheckClassTemplate( NewClass->setLexicalDeclContext(CurContext); NewTemplate->setLexicalDeclContext(CurContext); - // Ensure that the template parameter lists are compatible. Skip this check - // for a friend in a dependent context: the template parameter list itself - // could be dependent. - if (ShouldAddRedecl && PrevClassTemplate && - !TemplateParameterListsAreEqual( - NewTemplate, TemplateParams, PrevClassTemplate, - PrevClassTemplate->getTemplateParameters(), - /*Complain=*/true, TPL_TemplateMatch)) - return true; - - // Check the template parameter list of this declaration, possibly - // merging in the template parameter list from the previous class - // template declaration. Skip this check for a friend in a dependent - // context, because the template parameter list might be dependent. - if (ShouldAddRedecl && - CheckTemplateParameterList( - TemplateParams, - PrevClassTemplate ? PrevClassTemplate->getTemplateParameters() - : nullptr, - (SS.isSet() && SemanticContext && SemanticContext->isRecord() && - SemanticContext->isDependentContext()) - ? TPC_ClassTemplateMember - : TUK == TagUseKind::Friend ? TPC_FriendClassTemplate - : TPC_ClassTemplate, - SkipBody)) - Invalid = true; - - if (TUK == TagUseKind::Definition) { - if (PrevClassTemplate) { - // Check for redefinition of this class template. - if (TagDecl *Def = - PrevClassTemplate->getTemplatedDecl()->getDefinition()) { - // If we have a prior definition that is not visible, treat this as - // simply making that previous definition visible. - NamedDecl *Hidden = nullptr; - if (SkipBody && !hasVisibleDefinition(Def, &Hidden)) { - SkipBody->ShouldSkip = true; - SkipBody->Previous = Def; - auto *Tmpl = cast(Hidden)->getDescribedClassTemplate(); - assert(Tmpl && "original definition of a class template is not a " - "class template?"); - makeMergedDefinitionVisible(Hidden); - makeMergedDefinitionVisible(Tmpl); - } else { - Diag(NameLoc, diag::err_redefinition) << Name; - Diag(Def->getLocation(), diag::note_previous_definition); - // FIXME: Would it make sense to try to "forget" the previous - // definition, as part of error recovery? - return true; - } - } - } - - if (!SkipBody || !SkipBody->ShouldSkip) - NewClass->startDefinition(); - } + if (TUK == TagUseKind::Definition && (!SkipBody || !SkipBody->ShouldSkip)) + NewClass->startDefinition(); ProcessDeclAttributeList(S, NewClass, Attr); ProcessAPINotes(NewClass); @@ -4129,8 +4133,7 @@ void Sema::CheckDeductionGuideTemplate(FunctionTemplateDecl *TD) { DeclResult Sema::ActOnVarTemplateSpecialization( Scope *S, Declarator &D, TypeSourceInfo *DI, LookupResult &Previous, SourceLocation TemplateKWLoc, TemplateParameterList *TemplateParams, - StorageClass SC, bool IsPartialSpecialization, - bool IsMemberSpecialization) { + StorageClass SC, bool IsPartialSpecialization) { // D must be variable template id. assert(D.getName().getKind() == UnqualifiedIdKind::IK_TemplateId && "Variable template specialization is declared with a template id."); @@ -4248,16 +4251,17 @@ DeclResult Sema::ActOnVarTemplateSpecialization( Context, VarTemplate->getDeclContext(), TemplateKWLoc, TemplateNameLoc, TemplateParams, VarTemplate, DI->getType(), DI, SC, CanonicalConverted); - // If we are providing an explicit specialization of a member variable - // template specialization, make a note of that. - if (IsMemberSpecialization) - Partial->setMemberSpecialization(); Partial->setTemplateArgsAsWritten(TemplateArgs); if (!PrevPartial) VarTemplate->AddPartialSpecialization(Partial, InsertPos); Specialization = Partial; + // If we are providing an explicit specialization of a member variable + // template specialization, make a note of that. + if (PrevPartial && PrevPartial->getInstantiatedFromMember()) + PrevPartial->setMemberSpecialization(); + CheckTemplatePartialSpecialization(Partial); } else { // Create a new class template specialization declaration node for @@ -5175,7 +5179,8 @@ bool Sema::CheckTemplateArgument( unsigned ArgumentPackIndex, SmallVectorImpl &SugaredConverted, SmallVectorImpl &CanonicalConverted, - CheckTemplateArgumentKind CTAK) { + CheckTemplateArgumentKind CTAK, bool PartialOrdering, + bool *MatchedPackOnParmToNonPackOnArg) { // Check template type parameters. if (TemplateTypeParmDecl *TTP = dyn_cast(Param)) return CheckTemplateTypeArgument(TTP, Arg, SugaredConverted, @@ -5390,8 +5395,8 @@ bool Sema::CheckTemplateArgument( case TemplateArgument::Template: case TemplateArgument::TemplateExpansion: - if (CheckTemplateTemplateArgument(TempParm, Params, Arg, - /*IsDeduced=*/CTAK != CTAK_Specified)) + if (CheckTemplateTemplateArgument(TempParm, Params, Arg, PartialOrdering, + MatchedPackOnParmToNonPackOnArg)) return true; SugaredConverted.push_back(Arg.getArgument()); @@ -5465,7 +5470,7 @@ bool Sema::CheckTemplateArgumentList( SmallVectorImpl &SugaredConverted, SmallVectorImpl &CanonicalConverted, bool UpdateArgsWithConversions, bool *ConstraintsNotSatisfied, - bool PartialOrderingTTP) { + bool PartialOrderingTTP, bool *MatchedPackOnParmToNonPackOnArg) { if (ConstraintsNotSatisfied) *ConstraintsNotSatisfied = false; @@ -5498,8 +5503,7 @@ bool Sema::CheckTemplateArgumentList( DefaultArgs && ParamIdx >= DefaultArgs.StartPos) { // All written arguments should have been consumed by this point. assert(ArgIdx == NumArgs && "bad default argument deduction"); - // FIXME: Don't ignore parameter packs. - if (ParamIdx == DefaultArgs.StartPos && !(*Param)->isParameterPack()) { + if (ParamIdx == DefaultArgs.StartPos) { assert(Param + DefaultArgs.Args.size() <= ParamEnd); // Default arguments from a DeducedTemplateName are already converted. for (const TemplateArgument &DefArg : DefaultArgs.Args) { @@ -5545,7 +5549,8 @@ bool Sema::CheckTemplateArgumentList( if (CheckTemplateArgument(*Param, NewArgs[ArgIdx], Template, TemplateLoc, RAngleLoc, SugaredArgumentPack.size(), SugaredConverted, CanonicalConverted, - CTAK_Specified)) + CTAK_Specified, /*PartialOrdering=*/false, + MatchedPackOnParmToNonPackOnArg)) return true; CanonicalConverted.back().setIsDefaulted( @@ -5703,7 +5708,8 @@ bool Sema::CheckTemplateArgumentList( // Check the default template argument. if (CheckTemplateArgument(*Param, Arg, Template, TemplateLoc, RAngleLoc, 0, SugaredConverted, CanonicalConverted, - CTAK_Specified)) + CTAK_Specified, /*PartialOrdering=*/false, + /*MatchedPackOnParmToNonPackOnArg=*/nullptr)) return true; SugaredConverted.back().setIsDefaulted(true); @@ -5724,8 +5730,9 @@ bool Sema::CheckTemplateArgumentList( // pack expansions; they might be empty. This can happen even if // PartialTemplateArgs is false (the list of arguments is complete but // still dependent). - if (ArgIdx < NumArgs && CurrentInstantiationScope && - CurrentInstantiationScope->getPartiallySubstitutedPack()) { + if (PartialOrderingTTP || + (CurrentInstantiationScope && + CurrentInstantiationScope->getPartiallySubstitutedPack())) { while (ArgIdx < NumArgs && NewArgs[ArgIdx].getArgument().isPackExpansion()) { const TemplateArgument &Arg = NewArgs[ArgIdx++].getArgument(); @@ -5772,7 +5779,9 @@ bool Sema::CheckTemplateArgumentList( MultiLevelTemplateArgumentList MLTAL = getTemplateInstantiationArgs( Template, NewContext, /*Final=*/false, CanonicalConverted, - /*RelativeToPrimary=*/true, /*ForConceptInstantiation=*/true); + /*RelativeToPrimary=*/true, + /*Pattern=*/nullptr, + /*ForConceptInstantiation=*/true); if (EnsureTemplateArgumentListConstraints( Template, MLTAL, SourceRange(TemplateLoc, TemplateArgs.getRAngleLoc()))) { @@ -7283,10 +7292,10 @@ static void DiagnoseTemplateParameterListArityMismatch( Sema &S, TemplateParameterList *New, TemplateParameterList *Old, Sema::TemplateParameterListEqualKind Kind, SourceLocation TemplateArgLoc); -bool Sema::CheckTemplateTemplateArgument(TemplateTemplateParmDecl *Param, - TemplateParameterList *Params, - TemplateArgumentLoc &Arg, - bool IsDeduced) { +bool Sema::CheckTemplateTemplateArgument( + TemplateTemplateParmDecl *Param, TemplateParameterList *Params, + TemplateArgumentLoc &Arg, bool PartialOrdering, + bool *MatchedPackOnParmToNonPackOnArg) { TemplateName Name = Arg.getArgument().getAsTemplateOrTemplatePattern(); auto [Template, DefaultArgs] = Name.getTemplateDeclAndDefaultArgs(); if (!Template) { @@ -7321,64 +7330,47 @@ bool Sema::CheckTemplateTemplateArgument(TemplateTemplateParmDecl *Param, << Template; } + if (!getLangOpts().RelaxedTemplateTemplateArgs) + return !TemplateParameterListsAreEqual( + Template->getTemplateParameters(), Params, /*Complain=*/true, + TPL_TemplateTemplateArgumentMatch, Arg.getLocation()); + // C++1z [temp.arg.template]p3: (DR 150) // A template-argument matches a template template-parameter P when P // is at least as specialized as the template-argument A. - if (getLangOpts().RelaxedTemplateTemplateArgs) { - // Quick check for the common case: - // If P contains a parameter pack, then A [...] matches P if each of A's - // template parameters matches the corresponding template parameter in - // the template-parameter-list of P. - if (TemplateParameterListsAreEqual( - Template->getTemplateParameters(), Params, false, - TPL_TemplateTemplateArgumentMatch, Arg.getLocation()) && - // If the argument has no associated constraints, then the parameter is - // definitely at least as specialized as the argument. - // Otherwise - we need a more thorough check. - !Template->hasAssociatedConstraints()) - return false; - - if (isTemplateTemplateParameterAtLeastAsSpecializedAs( - Params, Template, DefaultArgs, Arg.getLocation(), IsDeduced)) { - // P2113 - // C++20[temp.func.order]p2 - // [...] If both deductions succeed, the partial ordering selects the - // more constrained template (if one exists) as determined below. - SmallVector ParamsAC, TemplateAC; - Params->getAssociatedConstraints(ParamsAC); - // C++2a[temp.arg.template]p3 - // [...] In this comparison, if P is unconstrained, the constraints on A - // are not considered. - if (ParamsAC.empty()) - return false; + if (!isTemplateTemplateParameterAtLeastAsSpecializedAs( + Params, Param, Template, DefaultArgs, Arg.getLocation(), + PartialOrdering, MatchedPackOnParmToNonPackOnArg)) + return true; + // P2113 + // C++20[temp.func.order]p2 + // [...] If both deductions succeed, the partial ordering selects the + // more constrained template (if one exists) as determined below. + SmallVector ParamsAC, TemplateAC; + Params->getAssociatedConstraints(ParamsAC); + // C++20[temp.arg.template]p3 + // [...] In this comparison, if P is unconstrained, the constraints on A + // are not considered. + if (ParamsAC.empty()) + return false; - Template->getAssociatedConstraints(TemplateAC); + Template->getAssociatedConstraints(TemplateAC); - bool IsParamAtLeastAsConstrained; - if (IsAtLeastAsConstrained(Param, ParamsAC, Template, TemplateAC, - IsParamAtLeastAsConstrained)) - return true; - if (!IsParamAtLeastAsConstrained) { - Diag(Arg.getLocation(), - diag::err_template_template_parameter_not_at_least_as_constrained) - << Template << Param << Arg.getSourceRange(); - Diag(Param->getLocation(), diag::note_entity_declared_at) << Param; - Diag(Template->getLocation(), diag::note_entity_declared_at) - << Template; - MaybeEmitAmbiguousAtomicConstraintsDiagnostic(Param, ParamsAC, Template, - TemplateAC); - return true; - } - return false; - } - // FIXME: Produce better diagnostics for deduction failures. + bool IsParamAtLeastAsConstrained; + if (IsAtLeastAsConstrained(Param, ParamsAC, Template, TemplateAC, + IsParamAtLeastAsConstrained)) + return true; + if (!IsParamAtLeastAsConstrained) { + Diag(Arg.getLocation(), + diag::err_template_template_parameter_not_at_least_as_constrained) + << Template << Param << Arg.getSourceRange(); + Diag(Param->getLocation(), diag::note_entity_declared_at) << Param; + Diag(Template->getLocation(), diag::note_entity_declared_at) << Template; + MaybeEmitAmbiguousAtomicConstraintsDiagnostic(Param, ParamsAC, Template, + TemplateAC); + return true; } - - return !TemplateParameterListsAreEqual(Template->getTemplateParameters(), - Params, - true, - TPL_TemplateTemplateArgumentMatch, - Arg.getLocation()); + return false; } static Sema::SemaDiagnosticBuilder noteLocation(Sema &S, const NamedDecl &Decl, @@ -8461,12 +8453,15 @@ DeclResult Sema::ActOnClassTemplateSpecialization( Diag(TemplateNameLoc, diag::err_partial_spec_args_match_primary_template) << /*class template*/ 0 << (TUK == TagUseKind::Definition) << FixItHint::CreateRemoval(SourceRange(LAngleLoc, RAngleLoc)); - return CheckClassTemplate( - S, TagSpec, TUK, KWLoc, SS, ClassTemplate->getIdentifier(), - TemplateNameLoc, Attr, TemplateParams, AS_none, - /*ModulePrivateLoc=*/SourceLocation(), - /*FriendLoc*/ SourceLocation(), TemplateParameterLists.drop_back(), - isMemberSpecialization); + return CheckClassTemplate(S, TagSpec, TUK, KWLoc, SS, + ClassTemplate->getIdentifier(), + TemplateNameLoc, + Attr, + TemplateParams, + AS_none, /*ModulePrivateLoc=*/SourceLocation(), + /*FriendLoc*/SourceLocation(), + TemplateParameterLists.size() - 1, + TemplateParameterLists.data()); } // Create a new class template partial specialization declaration node. @@ -8476,11 +8471,6 @@ DeclResult Sema::ActOnClassTemplateSpecialization( ClassTemplatePartialSpecializationDecl::Create( Context, Kind, DC, KWLoc, TemplateNameLoc, TemplateParams, ClassTemplate, CanonicalConverted, CanonType, PrevPartial); - - // If we are providing an explicit specialization of a member class - // template specialization, make a note of that. - if (isMemberSpecialization) - Partial->setMemberSpecialization(); Partial->setTemplateArgsAsWritten(TemplateArgs); SetNestedNameSpecifier(*this, Partial, SS); if (TemplateParameterLists.size() > 1 && SS.isSet()) { @@ -8492,6 +8482,11 @@ DeclResult Sema::ActOnClassTemplateSpecialization( ClassTemplate->AddPartialSpecialization(Partial, InsertPos); Specialization = Partial; + // If we are providing an explicit specialization of a member class + // template specialization, make a note of that. + if (PrevPartial && PrevPartial->getInstantiatedFromMember()) + PrevPartial->setMemberSpecialization(); + CheckTemplatePartialSpecialization(Partial); } else { // Create a new class template specialization declaration node for @@ -9763,11 +9758,14 @@ DeclResult Sema::ActOnExplicitInstantiation( // Check that the template argument list is well-formed for this // template. + bool PrimaryHasMatchedPackOnParmToNonPackOnArg = false; SmallVector SugaredConverted, CanonicalConverted; - if (CheckTemplateArgumentList(ClassTemplate, TemplateNameLoc, TemplateArgs, - /*DefaultArgs=*/{}, false, SugaredConverted, - CanonicalConverted, - /*UpdateArgsWithConversions=*/true)) + if (CheckTemplateArgumentList( + ClassTemplate, TemplateNameLoc, TemplateArgs, + /*DefaultArgs=*/{}, false, SugaredConverted, CanonicalConverted, + /*UpdateArgsWithConversions=*/true, + /*ConstraintsNotSatisfied=*/nullptr, /*PartialOrderingTTP=*/false, + &PrimaryHasMatchedPackOnParmToNonPackOnArg)) return true; // Find the class template specialization declaration that @@ -9888,7 +9886,9 @@ DeclResult Sema::ActOnExplicitInstantiation( = cast_or_null( Specialization->getDefinition()); if (!Def) - InstantiateClassTemplateSpecialization(TemplateNameLoc, Specialization, TSK); + InstantiateClassTemplateSpecialization( + TemplateNameLoc, Specialization, TSK, + /*Complain=*/true, PrimaryHasMatchedPackOnParmToNonPackOnArg); else if (TSK == TSK_ExplicitInstantiationDefinition) { MarkVTableUsed(TemplateNameLoc, Specialization, true); Specialization->setPointOfInstantiation(Def->getPointOfInstantiation()); @@ -11290,8 +11290,8 @@ class ExplicitSpecializationVisibilityChecker { template void checkTemplate(TemplDecl *TD) { - if (TD->getMostRecentDecl()->isMemberSpecialization()) { - if (!CheckMemberSpecialization(TD->getMostRecentDecl())) + if (TD->isMemberSpecialization()) { + if (!CheckMemberSpecialization(TD)) diagnose(TD->getMostRecentDecl(), false); } } diff --git a/clang/lib/Sema/SemaTemplateDeduction.cpp b/clang/lib/Sema/SemaTemplateDeduction.cpp index dfae0d6cda0d9..e49d315f7186b 100644 --- a/clang/lib/Sema/SemaTemplateDeduction.cpp +++ b/clang/lib/Sema/SemaTemplateDeduction.cpp @@ -145,7 +145,9 @@ static TemplateDeductionResult DeduceTemplateArgumentsByTypeMatch( PartialOrderingKind POK, bool DeducedFromArrayBound, bool *HasDeducedAnyParam); -enum class PackFold { ParameterToArgument, ArgumentToParameter }; +/// What directions packs are allowed to match non-packs. +enum class PackFold { ParameterToArgument, ArgumentToParameter, Both }; + static TemplateDeductionResult DeduceTemplateArguments(Sema &S, TemplateParameterList *TemplateParams, ArrayRef Ps, @@ -1711,7 +1713,21 @@ static TemplateDeductionResult DeduceTemplateArgumentsByTypeMatch( DeducedTemplateArgument Result = checkDeducedTemplateArguments(S.Context, Deduced[Index], NewDeduced); if (Result.isNull()) { - Info.Param = cast(TemplateParams->getParam(Index)); + // We can also get inconsistencies when matching NTTP type. + switch (NamedDecl *Param = TemplateParams->getParam(Index); + Param->getKind()) { + case Decl::TemplateTypeParm: + Info.Param = cast(Param); + break; + case Decl::NonTypeTemplateParm: + Info.Param = cast(Param); + break; + case Decl::TemplateTemplateParm: + Info.Param = cast(Param); + break; + default: + llvm_unreachable("unexpected kind"); + } Info.FirstArg = Deduced[Index]; Info.SecondArg = NewDeduced; return TemplateDeductionResult::Inconsistent; @@ -2549,8 +2565,31 @@ DeduceTemplateArguments(Sema &S, TemplateParameterList *TemplateParams, if (const NonTypeTemplateParmDecl *NTTP = getDeducedParameterFromExpr(Info, P.getAsExpr())) { switch (A.getKind()) { + case TemplateArgument::Expression: { + const Expr *E = A.getAsExpr(); + // When checking NTTP, if either the parameter or the argument is + // dependent, as there would be otherwise nothing to deduce, we force + // the argument to the parameter type using this dependent implicit + // cast, in order to maintain invariants. Now we can deduce the + // resulting type from the original type, and deduce the original type + // against the parameter we are checking. + if (const auto *ICE = dyn_cast(E); + ICE && ICE->getCastKind() == clang::CK_Dependent) { + E = ICE->getSubExpr(); + if (auto Result = DeduceTemplateArgumentsByTypeMatch( + S, TemplateParams, ICE->getType(), E->getType(), Info, + Deduced, TDF_SkipNonDependent, + PartialOrdering ? PartialOrderingKind::NonCall + : PartialOrderingKind::None, + /*DeducedFromArrayBound=*/false, HasDeducedAnyParam); + Result != TemplateDeductionResult::Success) + return Result; + } + return DeduceNonTypeTemplateArgument( + S, TemplateParams, NTTP, DeducedTemplateArgument(A), E->getType(), + Info, PartialOrdering, Deduced, HasDeducedAnyParam); + } case TemplateArgument::Integral: - case TemplateArgument::Expression: case TemplateArgument::StructuralValue: return DeduceNonTypeTemplateArgument( S, TemplateParams, NTTP, DeducedTemplateArgument(A), @@ -2639,50 +2678,75 @@ DeduceTemplateArguments(Sema &S, TemplateParameterList *TemplateParams, SmallVectorImpl &Deduced, bool NumberOfArgumentsMustMatch, bool PartialOrdering, PackFold PackFold, bool *HasDeducedAnyParam) { - if (PackFold == PackFold::ArgumentToParameter) - std::swap(Ps, As); + bool FoldPackParameter = PackFold == PackFold::ParameterToArgument || + PackFold == PackFold::Both, + FoldPackArgument = PackFold == PackFold::ArgumentToParameter || + PackFold == PackFold::Both; + // C++0x [temp.deduct.type]p9: // If the template argument list of P contains a pack expansion that is not // the last template argument, the entire template argument list is a // non-deduced context. - if (hasPackExpansionBeforeEnd(Ps)) + if (FoldPackParameter && hasPackExpansionBeforeEnd(Ps)) + return TemplateDeductionResult::Success; + + if (FoldPackArgument && hasPackExpansionBeforeEnd(As)) return TemplateDeductionResult::Success; // C++0x [temp.deduct.type]p9: // If P has a form that contains or , then each argument Pi of the // respective template argument list P is compared with the corresponding // argument Ai of the corresponding template argument list of A. - unsigned ArgIdx = 0, ParamIdx = 0; - for (; hasTemplateArgumentForDeduction(Ps, ParamIdx); ++ParamIdx) { - const TemplateArgument &P = Ps[ParamIdx]; - if (!P.isPackExpansion()) { + for (unsigned ArgIdx = 0, ParamIdx = 0; /**/; /**/) { + if (!hasTemplateArgumentForDeduction(Ps, ParamIdx)) + return !FoldPackParameter && hasTemplateArgumentForDeduction(As, ArgIdx) + ? TemplateDeductionResult::MiscellaneousDeductionFailure + : TemplateDeductionResult::Success; + + if (!Ps[ParamIdx].isPackExpansion()) { // The simple case: deduce template arguments by matching Pi and Ai. // Check whether we have enough arguments. if (!hasTemplateArgumentForDeduction(As, ArgIdx)) - return NumberOfArgumentsMustMatch + return !FoldPackArgument && NumberOfArgumentsMustMatch ? TemplateDeductionResult::MiscellaneousDeductionFailure : TemplateDeductionResult::Success; - // C++1z [temp.deduct.type]p9: - // During partial ordering, if Ai was originally a pack expansion [and] - // Pi is not a pack expansion, template argument deduction fails. - if (As[ArgIdx].isPackExpansion()) - return TemplateDeductionResult::MiscellaneousDeductionFailure; + if (As[ArgIdx].isPackExpansion()) { + // C++1z [temp.deduct.type]p9: + // During partial ordering, if Ai was originally a pack expansion + // [and] Pi is not a pack expansion, template argument deduction + // fails. + if (!FoldPackArgument) + return TemplateDeductionResult::MiscellaneousDeductionFailure; + + TemplateArgument Pattern = As[ArgIdx].getPackExpansionPattern(); + for (;;) { + // Deduce template parameters from the pattern. + if (auto Result = DeduceTemplateArguments( + S, TemplateParams, Ps[ParamIdx], Pattern, Info, + PartialOrdering, Deduced, HasDeducedAnyParam); + Result != TemplateDeductionResult::Success) + return Result; - // Perform deduction for this Pi/Ai pair. - TemplateArgument Pi = P, Ai = As[ArgIdx]; - if (PackFold == PackFold::ArgumentToParameter) - std::swap(Pi, Ai); - if (auto Result = DeduceTemplateArguments(S, TemplateParams, Pi, Ai, Info, - PartialOrdering, Deduced, - HasDeducedAnyParam); - Result != TemplateDeductionResult::Success) - return Result; + ++ParamIdx; + if (!hasTemplateArgumentForDeduction(Ps, ParamIdx)) + return TemplateDeductionResult::Success; + if (Ps[ParamIdx].isPackExpansion()) + break; + } + } else { + // Perform deduction for this Pi/Ai pair. + if (auto Result = DeduceTemplateArguments( + S, TemplateParams, Ps[ParamIdx], As[ArgIdx], Info, + PartialOrdering, Deduced, HasDeducedAnyParam); + Result != TemplateDeductionResult::Success) + return Result; - // Move to the next argument. - ++ArgIdx; - continue; + ++ArgIdx; + ++ParamIdx; + continue; + } } // The parameter is a pack expansion. @@ -2692,7 +2756,7 @@ DeduceTemplateArguments(Sema &S, TemplateParameterList *TemplateParams, // each remaining argument in the template argument list of A. Each // comparison deduces template arguments for subsequent positions in the // template parameter packs expanded by Pi. - TemplateArgument Pattern = P.getPackExpansionPattern(); + TemplateArgument Pattern = Ps[ParamIdx].getPackExpansionPattern(); // Prepare to deduce the packs within the pattern. PackDeductionScope PackScope(S, TemplateParams, Deduced, Info, Pattern); @@ -2703,13 +2767,16 @@ DeduceTemplateArguments(Sema &S, TemplateParameterList *TemplateParams, for (; hasTemplateArgumentForDeduction(As, ArgIdx) && PackScope.hasNextElement(); ++ArgIdx) { - TemplateArgument Pi = Pattern, Ai = As[ArgIdx]; - if (PackFold == PackFold::ArgumentToParameter) - std::swap(Pi, Ai); + if (!As[ArgIdx].isPackExpansion()) { + if (!FoldPackParameter) + return TemplateDeductionResult::MiscellaneousDeductionFailure; + if (FoldPackArgument) + Info.setMatchedPackOnParmToNonPackOnArg(); + } // Deduce template arguments from the pattern. - if (auto Result = DeduceTemplateArguments(S, TemplateParams, Pi, Ai, Info, - PartialOrdering, Deduced, - HasDeducedAnyParam); + if (auto Result = DeduceTemplateArguments( + S, TemplateParams, Pattern, As[ArgIdx], Info, PartialOrdering, + Deduced, HasDeducedAnyParam); Result != TemplateDeductionResult::Success) return Result; @@ -2718,12 +2785,8 @@ DeduceTemplateArguments(Sema &S, TemplateParameterList *TemplateParams, // Build argument packs for each of the parameter packs expanded by this // pack expansion. - if (auto Result = PackScope.finish(); - Result != TemplateDeductionResult::Success) - return Result; + return PackScope.finish(); } - - return TemplateDeductionResult::Success; } TemplateDeductionResult Sema::DeduceTemplateArguments( @@ -2892,7 +2955,7 @@ Sema::getIdentityTemplateArgumentLoc(NamedDecl *TemplateParm, /// fully-converted template arguments. static bool ConvertDeducedTemplateArgument( Sema &S, NamedDecl *Param, DeducedTemplateArgument Arg, NamedDecl *Template, - TemplateDeductionInfo &Info, bool IsDeduced, + TemplateDeductionInfo &Info, bool IsDeduced, bool PartialOrdering, SmallVectorImpl &SugaredOutput, SmallVectorImpl &CanonicalOutput) { auto ConvertArg = [&](DeducedTemplateArgument Arg, @@ -2903,15 +2966,20 @@ static bool ConvertDeducedTemplateArgument( TemplateArgumentLoc ArgLoc = S.getTrivialTemplateArgumentLoc( Arg, QualType(), Info.getLocation(), Param); + bool MatchedPackOnParmToNonPackOnArg = false; // Check the template argument, converting it as necessary. - return S.CheckTemplateArgument( + auto Res = S.CheckTemplateArgument( Param, ArgLoc, Template, Template->getLocation(), Template->getSourceRange().getEnd(), ArgumentPackIndex, SugaredOutput, CanonicalOutput, IsDeduced ? (Arg.wasDeducedFromArrayBound() ? Sema::CTAK_DeducedFromArrayBound : Sema::CTAK_Deduced) - : Sema::CTAK_Specified); + : Sema::CTAK_Specified, + PartialOrdering, &MatchedPackOnParmToNonPackOnArg); + if (MatchedPackOnParmToNonPackOnArg) + Info.setMatchedPackOnParmToNonPackOnArg(); + return Res; }; if (Arg.getKind() == TemplateArgument::Pack) { @@ -2994,9 +3062,9 @@ static TemplateDeductionResult ConvertDeducedTemplateArguments( SmallVectorImpl &Deduced, TemplateDeductionInfo &Info, SmallVectorImpl &SugaredBuilder, - SmallVectorImpl &CanonicalBuilder, - LocalInstantiationScope *CurrentInstantiationScope = nullptr, - unsigned NumAlreadyConverted = 0, bool *IsIncomplete = nullptr) { + SmallVectorImpl &CanonicalBuilder, bool PartialOrdering, + LocalInstantiationScope *CurrentInstantiationScope, + unsigned NumAlreadyConverted, bool *IsIncomplete) { TemplateParameterList *TemplateParams = Template->getTemplateParameters(); for (unsigned I = 0, N = TemplateParams->size(); I != N; ++I) { @@ -3039,8 +3107,8 @@ static TemplateDeductionResult ConvertDeducedTemplateArguments( // We may have deduced this argument, so it still needs to be // checked and converted. if (ConvertDeducedTemplateArgument(S, Param, Deduced[I], Template, Info, - IsDeduced, SugaredBuilder, - CanonicalBuilder)) { + IsDeduced, PartialOrdering, + SugaredBuilder, CanonicalBuilder)) { Info.Param = makeTemplateParameter(Param); // FIXME: These template arguments are temporary. Free them! Info.reset( @@ -3106,7 +3174,9 @@ static TemplateDeductionResult ConvertDeducedTemplateArguments( // Check whether we can actually use the default argument. if (S.CheckTemplateArgument( Param, DefArg, TD, TD->getLocation(), TD->getSourceRange().getEnd(), - 0, SugaredBuilder, CanonicalBuilder, Sema::CTAK_Specified)) { + /*ArgumentPackIndex=*/0, SugaredBuilder, CanonicalBuilder, + Sema::CTAK_Specified, /*PartialOrdering=*/false, + /*MatchedPackOnParmToNonPackOnArg=*/nullptr)) { Info.Param = makeTemplateParameter( const_cast(TemplateParams->getParam(I))); // FIXME: These template arguments are temporary. Free them! @@ -3138,6 +3208,20 @@ template<> struct IsPartialSpecialization { static constexpr bool value = true; }; +template +static bool DeducedArgsNeedReplacement(TemplateDeclT *Template) { + return false; +} +template <> +bool DeducedArgsNeedReplacement( + VarTemplatePartialSpecializationDecl *Spec) { + return !Spec->isClassScopeExplicitSpecialization(); +} +template <> +bool DeducedArgsNeedReplacement( + ClassTemplatePartialSpecializationDecl *Spec) { + return !Spec->isClassScopeExplicitSpecialization(); +} template static TemplateDeductionResult @@ -3148,10 +3232,23 @@ CheckDeducedArgumentConstraints(Sema &S, TemplateDeclT *Template, llvm::SmallVector AssociatedConstraints; Template->getAssociatedConstraints(AssociatedConstraints); + std::optional> Innermost; + // If we don't need to replace the deduced template arguments, + // we can add them immediately as the inner-most argument list. + if (!DeducedArgsNeedReplacement(Template)) + Innermost = CanonicalDeducedArgs; + MultiLevelTemplateArgumentList MLTAL = S.getTemplateInstantiationArgs( - Template, Template->getDeclContext(), /*Final=*/false, - /*Innermost=*/CanonicalDeducedArgs, /*RelativeToPrimary=*/true, - /*ForConstraintInstantiation=*/true); + Template, Template->getDeclContext(), /*Final=*/false, Innermost, + /*RelativeToPrimary=*/true, /*Pattern=*/ + nullptr, /*ForConstraintInstantiation=*/true); + + // getTemplateInstantiationArgs picks up the non-deduced version of the + // template args when this is a variable template partial specialization and + // not class-scope explicit specialization, so replace with Deduced Args + // instead of adding to inner-most. + if (!Innermost) + MLTAL.replaceInnermostTemplateArguments(Template, CanonicalDeducedArgs); if (S.CheckConstraintSatisfaction(Template, AssociatedConstraints, MLTAL, Info.getLocation(), @@ -3187,7 +3284,9 @@ FinishTemplateArgumentDeduction( SmallVector SugaredBuilder, CanonicalBuilder; if (auto Result = ConvertDeducedTemplateArguments( S, Partial, IsPartialOrdering, Deduced, Info, SugaredBuilder, - CanonicalBuilder); + CanonicalBuilder, IsPartialOrdering, + /*CurrentInstantiationScope=*/nullptr, /*NumAlreadyConverted=*/0, + /*IsIncomplete=*/nullptr); Result != TemplateDeductionResult::Success) return Result; @@ -3228,16 +3327,20 @@ FinishTemplateArgumentDeduction( return TemplateDeductionResult::SubstitutionFailure; } + bool MatchedPackOnParmToNonPackOnArg = false; bool ConstraintsNotSatisfied; SmallVector SugaredConvertedInstArgs, CanonicalConvertedInstArgs; if (S.CheckTemplateArgumentList( Template, Partial->getLocation(), InstArgs, /*DefaultArgs=*/{}, false, SugaredConvertedInstArgs, CanonicalConvertedInstArgs, - /*UpdateArgsWithConversions=*/true, &ConstraintsNotSatisfied)) + /*UpdateArgsWithConversions=*/true, &ConstraintsNotSatisfied, + /*PartialOrderingTTP=*/false, &MatchedPackOnParmToNonPackOnArg)) return ConstraintsNotSatisfied ? TemplateDeductionResult::ConstraintsNotSatisfied : TemplateDeductionResult::SubstitutionFailure; + if (MatchedPackOnParmToNonPackOnArg) + Info.setMatchedPackOnParmToNonPackOnArg(); TemplateParameterList *TemplateParams = Template->getTemplateParameters(); for (unsigned I = 0, E = TemplateParams->size(); I != E; ++I) { @@ -3275,7 +3378,6 @@ static TemplateDeductionResult FinishTemplateArgumentDeduction( // Unevaluated SFINAE context. EnterExpressionEvaluationContext Unevaluated( S, Sema::ExpressionEvaluationContext::Unevaluated); - Sema::SFINAETrap Trap(S); Sema::ContextRAII SavedContext(S, getAsDeclContextOrEnclosing(Template)); @@ -3284,29 +3386,45 @@ static TemplateDeductionResult FinishTemplateArgumentDeduction( // explicitly specified, template argument deduction fails. SmallVector SugaredBuilder, CanonicalBuilder; if (auto Result = ConvertDeducedTemplateArguments( - S, Template, /*IsDeduced*/ PartialOrdering, Deduced, Info, - SugaredBuilder, CanonicalBuilder, + S, Template, /*IsDeduced=*/PartialOrdering, Deduced, Info, + SugaredBuilder, CanonicalBuilder, PartialOrdering, /*CurrentInstantiationScope=*/nullptr, - /*NumAlreadyConverted=*/0U); + /*NumAlreadyConverted=*/0U, /*IsIncomplete=*/nullptr); Result != TemplateDeductionResult::Success) return Result; // Check that we produced the correct argument list. TemplateParameterList *TemplateParams = Template->getTemplateParameters(); + auto isSame = [&](unsigned I, const TemplateArgument &P, + const TemplateArgument &A) { + if (isSameTemplateArg(S.Context, P, A, PartialOrdering, + /*PackExpansionMatchesPack=*/true)) + return true; + Info.Param = makeTemplateParameter(TemplateParams->getParam(I)); + Info.FirstArg = P; + Info.SecondArg = A; + return false; + }; for (unsigned I = 0, E = TemplateParams->size(); I != E; ++I) { - TemplateArgument InstArg = CanonicalBuilder[I]; - if (!isSameTemplateArg(S.Context, TemplateArgs[I], InstArg, PartialOrdering, - /*PackExpansionMatchesPack=*/true)) { - Info.Param = makeTemplateParameter(TemplateParams->getParam(I)); - Info.FirstArg = TemplateArgs[I]; - Info.SecondArg = InstArg; - return TemplateDeductionResult::NonDeducedMismatch; + const TemplateArgument &P = TemplateArgs[I]; + if (P.isPackExpansion()) { + assert(I == TemplateArgs.size() - 1); + for (/**/; I != E; ++I) { + const TemplateArgument &A = CanonicalBuilder[I]; + if (A.getKind() == TemplateArgument::Pack) { + for (const TemplateArgument &Ai : A.getPackAsArray()) + if (!isSame(I, P, Ai)) + return TemplateDeductionResult::NonDeducedMismatch; + } else if (!isSame(I, P, A)) { + return TemplateDeductionResult::NonDeducedMismatch; + } + } + break; } + if (!isSame(I, P, CanonicalBuilder[I])) + return TemplateDeductionResult::NonDeducedMismatch; } - if (Trap.hasErrorOccurred()) - return TemplateDeductionResult::SubstitutionFailure; - if (!PartialOrdering) { if (auto Result = CheckDeducedArgumentConstraints( S, Template, SugaredBuilder, CanonicalBuilder, Info); @@ -3327,7 +3445,6 @@ static TemplateDeductionResult FinishTemplateArgumentDeduction( // Unevaluated SFINAE context. EnterExpressionEvaluationContext Unevaluated( S, Sema::ExpressionEvaluationContext::Unevaluated); - Sema::SFINAETrap Trap(S); Sema::ContextRAII SavedContext(S, getAsDeclContextOrEnclosing(TD)); @@ -3336,20 +3453,15 @@ static TemplateDeductionResult FinishTemplateArgumentDeduction( // explicitly specified, template argument deduction fails. SmallVector SugaredBuilder, CanonicalBuilder; if (auto Result = ConvertDeducedTemplateArguments( - S, TD, /*IsPartialOrdering=*/false, Deduced, Info, SugaredBuilder, - CanonicalBuilder); - Result != TemplateDeductionResult::Success) - return Result; - - if (Trap.hasErrorOccurred()) - return TemplateDeductionResult::SubstitutionFailure; - - if (auto Result = CheckDeducedArgumentConstraints(S, TD, SugaredBuilder, - CanonicalBuilder, Info); + S, TD, /*IsDeduced=*/false, Deduced, Info, SugaredBuilder, + CanonicalBuilder, /*PartialOrdering=*/false, + /*CurrentInstantiationScope=*/nullptr, /*NumAlreadyConverted=*/0, + /*IsIncomplete=*/nullptr); Result != TemplateDeductionResult::Success) return Result; - return TemplateDeductionResult::Success; + return ::CheckDeducedArgumentConstraints(S, TD, SugaredBuilder, + CanonicalBuilder, Info); } /// Perform template argument deduction to determine whether the given template @@ -3396,16 +3508,20 @@ DeduceTemplateArguments(Sema &S, T *Partial, if (Inst.isInvalid()) return TemplateDeductionResult::InstantiationDepth; - if (Trap.hasErrorOccurred()) - return TemplateDeductionResult::SubstitutionFailure; - TemplateDeductionResult Result; S.runWithSufficientStackSpace(Info.getLocation(), [&] { Result = ::FinishTemplateArgumentDeduction(S, Partial, /*IsPartialOrdering=*/false, TemplateArgs, Deduced, Info); }); - return Result; + + if (Result != TemplateDeductionResult::Success) + return Result; + + if (Trap.hasErrorOccurred()) + return TemplateDeductionResult::SubstitutionFailure; + + return TemplateDeductionResult::Success; } TemplateDeductionResult @@ -3461,14 +3577,18 @@ Sema::DeduceTemplateArgumentsFromType(TemplateDecl *TD, QualType FromType, if (Inst.isInvalid()) return TemplateDeductionResult::InstantiationDepth; - if (Trap.hasErrorOccurred()) - return TemplateDeductionResult::SubstitutionFailure; - TemplateDeductionResult Result; runWithSufficientStackSpace(Info.getLocation(), [&] { Result = ::FinishTemplateArgumentDeduction(*this, TD, Deduced, Info); }); - return Result; + + if (Result != TemplateDeductionResult::Success) + return Result; + + if (Trap.hasErrorOccurred()) + return TemplateDeductionResult::SubstitutionFailure; + + return TemplateDeductionResult::Success; } /// Determine whether the given type T is a simple-template-id type. @@ -3874,7 +3994,8 @@ TemplateDeductionResult Sema::FinishTemplateArgumentDeduction( unsigned NumExplicitlySpecified, FunctionDecl *&Specialization, TemplateDeductionInfo &Info, SmallVectorImpl const *OriginalCallArgs, - bool PartialOverloading, llvm::function_ref CheckNonDependent) { + bool PartialOverloading, bool PartialOrdering, + llvm::function_ref CheckNonDependent) { // Unevaluated SFINAE context. EnterExpressionEvaluationContext Unevaluated( *this, Sema::ExpressionEvaluationContext::Unevaluated); @@ -3897,9 +4018,10 @@ TemplateDeductionResult Sema::FinishTemplateArgumentDeduction( bool IsIncomplete = false; SmallVector SugaredBuilder, CanonicalBuilder; if (auto Result = ConvertDeducedTemplateArguments( - *this, FunctionTemplate, /*IsDeduced*/ true, Deduced, Info, - SugaredBuilder, CanonicalBuilder, CurrentInstantiationScope, - NumExplicitlySpecified, PartialOverloading ? &IsIncomplete : nullptr); + *this, FunctionTemplate, /*IsDeduced=*/true, Deduced, Info, + SugaredBuilder, CanonicalBuilder, PartialOrdering, + CurrentInstantiationScope, NumExplicitlySpecified, + PartialOverloading ? &IsIncomplete : nullptr); Result != TemplateDeductionResult::Success) return Result; @@ -3928,7 +4050,22 @@ TemplateDeductionResult Sema::FinishTemplateArgumentDeduction( if (FunctionTemplate->getFriendObjectKind()) Owner = FunctionTemplate->getLexicalDeclContext(); FunctionDecl *FD = FunctionTemplate->getTemplatedDecl(); - + // additional check for inline friend, + // ``` + // template int foo(F1 X); + // template struct A { + // template friend int foo(F1 X) { return A1; } + // }; + // template struct A<1>; + // int a = foo(1.0); + // ``` + const FunctionDecl *FDFriend; + if (FD->getFriendObjectKind() == Decl::FriendObjectKind::FOK_None && + FD->isDefined(FDFriend, /*CheckForPendingFriendDefinition*/ true) && + FDFriend->getFriendObjectKind() != Decl::FriendObjectKind::FOK_None) { + FD = const_cast(FDFriend); + Owner = FD->getLexicalDeclContext(); + } MultiLevelTemplateArgumentList SubstArgs( FunctionTemplate, CanonicalDeducedArgumentList->asArray(), /*Final=*/false); @@ -4416,7 +4553,8 @@ TemplateDeductionResult Sema::DeduceTemplateArguments( TemplateArgumentListInfo *ExplicitTemplateArgs, ArrayRef Args, FunctionDecl *&Specialization, TemplateDeductionInfo &Info, bool PartialOverloading, bool AggregateDeductionCandidate, - QualType ObjectType, Expr::Classification ObjectClassification, + bool PartialOrdering, QualType ObjectType, + Expr::Classification ObjectClassification, llvm::function_ref)> CheckNonDependent) { if (FunctionTemplate->isInvalidDecl()) return TemplateDeductionResult::Invalid; @@ -4631,7 +4769,8 @@ TemplateDeductionResult Sema::DeduceTemplateArguments( runWithSufficientStackSpace(Info.getLocation(), [&] { Result = FinishTemplateArgumentDeduction( FunctionTemplate, Deduced, NumExplicitlySpecified, Specialization, Info, - &OriginalCallArgs, PartialOverloading, [&, CallingCtx]() { + &OriginalCallArgs, PartialOverloading, PartialOrdering, + [&, CallingCtx]() { ContextRAII SavedContext(*this, CallingCtx); return CheckNonDependent(ParamTypesForArgChecking); }); @@ -4743,9 +4882,10 @@ TemplateDeductionResult Sema::DeduceTemplateArguments( TemplateDeductionResult Result; runWithSufficientStackSpace(Info.getLocation(), [&] { - Result = FinishTemplateArgumentDeduction(FunctionTemplate, Deduced, - NumExplicitlySpecified, - Specialization, Info); + Result = FinishTemplateArgumentDeduction( + FunctionTemplate, Deduced, NumExplicitlySpecified, Specialization, Info, + /*OriginalCallArgs=*/nullptr, /*PartialOverloading=*/false, + /*PartialOrdering=*/true); }); if (Result != TemplateDeductionResult::Success) return Result; @@ -4925,9 +5065,10 @@ TemplateDeductionResult Sema::DeduceTemplateArguments( FunctionDecl *ConversionSpecialized = nullptr; TemplateDeductionResult Result; runWithSufficientStackSpace(Info.getLocation(), [&] { - Result = FinishTemplateArgumentDeduction(ConversionTemplate, Deduced, 0, - ConversionSpecialized, Info, - &OriginalCallArgs); + Result = FinishTemplateArgumentDeduction( + ConversionTemplate, Deduced, 0, ConversionSpecialized, Info, + &OriginalCallArgs, /*PartialOverloading=*/false, + /*PartialOrdering=*/false); }); Specialization = cast_or_null(ConversionSpecialized); return Result; @@ -5504,7 +5645,8 @@ static TemplateDeductionResult FinishTemplateArgumentDeduction( SmallVector SugaredBuilder, CanonicalBuilder; if (auto Result = ConvertDeducedTemplateArguments( S, FTD, /*IsDeduced=*/true, Deduced, Info, SugaredBuilder, - CanonicalBuilder, /*CurrentInstantiationScope=*/nullptr, + CanonicalBuilder, /*PartialOrdering=*/true, + /*CurrentInstantiationScope=*/nullptr, /*NumAlreadyConverted=*/0, &IsIncomplete); Result != TemplateDeductionResult::Success) return Result; @@ -6094,14 +6236,23 @@ static bool isAtLeastAsSpecializedAs(Sema &S, QualType T1, QualType T2, return false; const auto *TST1 = cast(T1); - bool AtLeastAsSpecialized; + + Sema::SFINAETrap Trap(S); + + TemplateDeductionResult Result; S.runWithSufficientStackSpace(Info.getLocation(), [&] { - AtLeastAsSpecialized = - FinishTemplateArgumentDeduction( - S, P2, /*IsPartialOrdering=*/true, TST1->template_arguments(), - Deduced, Info) == TemplateDeductionResult::Success; + Result = ::FinishTemplateArgumentDeduction( + S, P2, /*IsPartialOrdering=*/true, TST1->template_arguments(), Deduced, + Info); }); - return AtLeastAsSpecialized; + + if (Result != TemplateDeductionResult::Success) + return false; + + if (Trap.hasErrorOccurred()) + return false; + + return true; } namespace { @@ -6339,8 +6490,9 @@ bool Sema::isMoreSpecializedThanPrimary( } bool Sema::isTemplateTemplateParameterAtLeastAsSpecializedAs( - TemplateParameterList *P, TemplateDecl *AArg, - const DefaultArguments &DefaultArgs, SourceLocation Loc, bool IsDeduced) { + TemplateParameterList *P, TemplateDecl *PArg, TemplateDecl *AArg, + const DefaultArguments &DefaultArgs, SourceLocation ArgLoc, + bool PartialOrdering, bool *MatchedPackOnParmToNonPackOnArg) { // C++1z [temp.arg.template]p4: (DR 150) // A template template-parameter P is at least as specialized as a // template template-argument A if, given the following rewrite to two @@ -6352,6 +6504,12 @@ bool Sema::isTemplateTemplateParameterAtLeastAsSpecializedAs( // TemplateParameterList *A = AArg->getTemplateParameters(); + Sema::InstantiatingTemplate Inst( + *this, ArgLoc, Sema::InstantiatingTemplate::PartialOrderingTTP(), PArg, + SourceRange(P->getTemplateLoc(), P->getRAngleLoc())); + if (Inst.isInvalid()) + return false; + // Given an invented class template X with the template parameter list of // A (including default arguments): // - Each function template has a single function parameter whose type is @@ -6366,8 +6524,6 @@ bool Sema::isTemplateTemplateParameterAtLeastAsSpecializedAs( // templates. SmallVector PArgs; { - SFINAETrap Trap(*this); - Context.getInjectedTemplateArgs(P, PArgs); TemplateArgumentListInfo PArgList(P->getLAngleLoc(), P->getRAngleLoc()); @@ -6387,18 +6543,17 @@ bool Sema::isTemplateTemplateParameterAtLeastAsSpecializedAs( // C++1z [temp.arg.template]p3: // If the rewrite produces an invalid type, then P is not at least as // specialized as A. - SmallVector SugaredPArgs; - if (CheckTemplateArgumentList(AArg, Loc, PArgList, DefaultArgs, false, - SugaredPArgs, PArgs, - /*UpdateArgsWithConversions=*/true, - /*ConstraintsNotSatisfied=*/nullptr, - /*PartialOrderTTP=*/true) || - Trap.hasErrorOccurred()) + SmallVector CanonicalPArgs; + if (CheckTemplateArgumentList( + AArg, ArgLoc, PArgList, DefaultArgs, false, PArgs, CanonicalPArgs, + /*UpdateArgsWithConversions=*/true, + /*ConstraintsNotSatisfied=*/nullptr, + /*PartialOrderingTTP=*/true, MatchedPackOnParmToNonPackOnArg)) return false; } // Determine whether P1 is at least as specialized as P2. - TemplateDeductionInfo Info(Loc, A->getDepth()); + TemplateDeductionInfo Info(ArgLoc, A->getDepth()); SmallVector Deduced; Deduced.resize(A->size()); @@ -6413,29 +6568,92 @@ bool Sema::isTemplateTemplateParameterAtLeastAsSpecializedAs( // be inverted between Ps and As. On non-deduced context, matching needs to // happen both ways, according to [temp.arg.template]p3, but this is // currently implemented as a special case elsewhere. - if (::DeduceTemplateArguments(*this, A, AArgs, PArgs, Info, Deduced, - /*NumberOfArgumentsMustMatch=*/false, - /*PartialOrdering=*/true, - IsDeduced ? PackFold::ArgumentToParameter - : PackFold::ParameterToArgument, - /*HasDeducedAnyParam=*/nullptr) != - TemplateDeductionResult::Success) + switch (::DeduceTemplateArguments( + *this, A, AArgs, PArgs, Info, Deduced, + /*NumberOfArgumentsMustMatch=*/false, /*PartialOrdering=*/true, + PartialOrdering ? PackFold::ArgumentToParameter : PackFold::Both, + /*HasDeducedAnyParam=*/nullptr)) { + case clang::TemplateDeductionResult::Success: + if (MatchedPackOnParmToNonPackOnArg && + Info.hasMatchedPackOnParmToNonPackOnArg()) + *MatchedPackOnParmToNonPackOnArg = true; + break; + + case TemplateDeductionResult::MiscellaneousDeductionFailure: + Diag(AArg->getLocation(), diag::err_template_param_list_different_arity) + << (A->size() > P->size()) << /*isTemplateTemplateParameter=*/true + << SourceRange(A->getTemplateLoc(), P->getRAngleLoc()); + return false; + case TemplateDeductionResult::NonDeducedMismatch: + Diag(AArg->getLocation(), diag::err_non_deduced_mismatch) + << Info.FirstArg << Info.SecondArg; + return false; + case TemplateDeductionResult::Inconsistent: + Diag(getAsNamedDecl(Info.Param)->getLocation(), + diag::err_inconsistent_deduction) + << Info.FirstArg << Info.SecondArg; + return false; + case TemplateDeductionResult::AlreadyDiagnosed: return false; + // None of these should happen for a plain deduction. + case TemplateDeductionResult::Invalid: + case TemplateDeductionResult::InstantiationDepth: + case TemplateDeductionResult::Incomplete: + case TemplateDeductionResult::IncompletePack: + case TemplateDeductionResult::Underqualified: + case TemplateDeductionResult::SubstitutionFailure: + case TemplateDeductionResult::DeducedMismatch: + case TemplateDeductionResult::DeducedMismatchNested: + case TemplateDeductionResult::TooManyArguments: + case TemplateDeductionResult::TooFewArguments: + case TemplateDeductionResult::InvalidExplicitArguments: + case TemplateDeductionResult::NonDependentConversionFailure: + case TemplateDeductionResult::ConstraintsNotSatisfied: + case TemplateDeductionResult::CUDATargetMismatch: + llvm_unreachable("Unexpected Result"); + } + SmallVector DeducedArgs(Deduced.begin(), Deduced.end()); - Sema::InstantiatingTemplate Inst(*this, Info.getLocation(), AArg, DeducedArgs, - Info); - if (Inst.isInvalid()) - return false; - bool AtLeastAsSpecialized; + TemplateDeductionResult TDK; runWithSufficientStackSpace(Info.getLocation(), [&] { - AtLeastAsSpecialized = - ::FinishTemplateArgumentDeduction( - *this, AArg, /*IsPartialOrdering=*/true, PArgs, Deduced, Info) == - TemplateDeductionResult::Success; + TDK = ::FinishTemplateArgumentDeduction( + *this, AArg, /*IsPartialOrdering=*/true, PArgs, Deduced, Info); }); - return AtLeastAsSpecialized; + switch (TDK) { + case TemplateDeductionResult::Success: + return true; + + // It doesn't seem possible to get a non-deduced mismatch when partial + // ordering TTPs. + case TemplateDeductionResult::NonDeducedMismatch: + llvm_unreachable("Unexpected NonDeducedMismatch"); + + // Substitution failures should have already been diagnosed. + case TemplateDeductionResult::AlreadyDiagnosed: + case TemplateDeductionResult::SubstitutionFailure: + case TemplateDeductionResult::InstantiationDepth: + return false; + + // None of these should happen when just converting deduced arguments. + case TemplateDeductionResult::Invalid: + case TemplateDeductionResult::Incomplete: + case TemplateDeductionResult::IncompletePack: + case TemplateDeductionResult::Inconsistent: + case TemplateDeductionResult::Underqualified: + case TemplateDeductionResult::DeducedMismatch: + case TemplateDeductionResult::DeducedMismatchNested: + case TemplateDeductionResult::TooManyArguments: + case TemplateDeductionResult::TooFewArguments: + case TemplateDeductionResult::InvalidExplicitArguments: + case TemplateDeductionResult::NonDependentConversionFailure: + case TemplateDeductionResult::ConstraintsNotSatisfied: + case TemplateDeductionResult::MiscellaneousDeductionFailure: + case TemplateDeductionResult::CUDATargetMismatch: + llvm_unreachable("Unexpected Result"); + } + llvm_unreachable("Unexpected TDK"); } namespace { diff --git a/clang/lib/Sema/SemaTemplateDeductionGuide.cpp b/clang/lib/Sema/SemaTemplateDeductionGuide.cpp index ca93c840f0321..545da21183c3c 100644 --- a/clang/lib/Sema/SemaTemplateDeductionGuide.cpp +++ b/clang/lib/Sema/SemaTemplateDeductionGuide.cpp @@ -765,7 +765,7 @@ buildAssociatedConstraints(Sema &SemaRef, FunctionTemplateDecl *F, } // Template arguments used to transform the template arguments in // DeducedResults. - SmallVector InnerArgsForBuildingRC( + SmallVector TemplateArgsForBuildingRC( F->getTemplateParameters()->size()); // Transform the transformed template args MultiLevelTemplateArgumentList Args; @@ -778,30 +778,33 @@ buildAssociatedConstraints(Sema &SemaRef, FunctionTemplateDecl *F, NamedDecl *TP = F->getTemplateParameters()->getParam(Index); MultiLevelTemplateArgumentList Args; Args.setKind(TemplateSubstitutionKind::Rewrite); - Args.addOuterTemplateArguments(InnerArgsForBuildingRC); + Args.addOuterTemplateArguments(TemplateArgsForBuildingRC); // Rebuild the template parameter with updated depth and index. NamedDecl *NewParam = transformTemplateParameter(SemaRef, F->getDeclContext(), TP, Args, /*NewIndex=*/FirstUndeducedParamIdx, getDepthAndIndex(TP).first + AdjustDepth); FirstUndeducedParamIdx += 1; - assert(InnerArgsForBuildingRC[Index].isNull()); - InnerArgsForBuildingRC[Index] = Context.getInjectedTemplateArg(NewParam); + assert(TemplateArgsForBuildingRC[Index].isNull()); + TemplateArgsForBuildingRC[Index] = + Context.getInjectedTemplateArg(NewParam); continue; } TemplateArgumentLoc Input = SemaRef.getTrivialTemplateArgumentLoc(D, QualType(), SourceLocation{}); TemplateArgumentLoc Output; if (!SemaRef.SubstTemplateArgument(Input, Args, Output)) { - assert(InnerArgsForBuildingRC[Index].isNull() && + assert(TemplateArgsForBuildingRC[Index].isNull() && "InstantiatedArgs must be null before setting"); - InnerArgsForBuildingRC[Index] = Output.getArgument(); + TemplateArgsForBuildingRC[Index] = Output.getArgument(); } } - // A list of template arguments for transforming the require-clause using - // the transformed template arguments as the template argument list of F. - // + // A list of template arguments for transforming the require-clause of F. + // It must contain the entire set of template argument lists. + MultiLevelTemplateArgumentList ArgsForBuildingRC; + ArgsForBuildingRC.setKind(clang::TemplateSubstitutionKind::Rewrite); + ArgsForBuildingRC.addOuterTemplateArguments(TemplateArgsForBuildingRC); // For 2), if the underlying deduction guide F is nested in a class template, // we need the entire template argument list, as the constraint AST in the // require-clause of F remains completely uninstantiated. @@ -824,15 +827,25 @@ buildAssociatedConstraints(Sema &SemaRef, FunctionTemplateDecl *F, // - The occurrence of U in the function parameter is [depth:0, index:0] // - The template parameter of U is [depth:0, index:0] // + // We add the outer template arguments which is [int] to the multi-level arg + // list to ensure that the occurrence U in `C` will be replaced with int + // during the substitution. + // // NOTE: The underlying deduction guide F is instantiated -- either from an // explicitly-written deduction guide member, or from a constructor. - MultiLevelTemplateArgumentList ArgsForBuildingRC = - SemaRef.getTemplateInstantiationArgs(F, F->getLexicalDeclContext(), - /*Final=*/false, - /*Innermost=*/InnerArgsForBuildingRC, - /*RelativeToPrimary=*/true, - /*ForConstraintInstantiation=*/true); - ArgsForBuildingRC.setKind(clang::TemplateSubstitutionKind::Rewrite); + // getInstantiatedFromMemberTemplate() can only handle the former case, so we + // check the DeclContext kind. + if (F->getLexicalDeclContext()->getDeclKind() == + clang::Decl::ClassTemplateSpecialization) { + auto OuterLevelArgs = SemaRef.getTemplateInstantiationArgs( + F, F->getLexicalDeclContext(), + /*Final=*/false, /*Innermost=*/std::nullopt, + /*RelativeToPrimary=*/true, + /*Pattern=*/nullptr, + /*ForConstraintInstantiation=*/true); + for (auto It : OuterLevelArgs) + ArgsForBuildingRC.addOuterTemplateArguments(It.Args); + } ExprResult E = SemaRef.SubstExpr(RC, ArgsForBuildingRC); if (E.isInvalid()) diff --git a/clang/lib/Sema/SemaTemplateInstantiate.cpp b/clang/lib/Sema/SemaTemplateInstantiate.cpp index 261ef4edf1759..74252bd7513cd 100644 --- a/clang/lib/Sema/SemaTemplateInstantiate.cpp +++ b/clang/lib/Sema/SemaTemplateInstantiate.cpp @@ -52,6 +52,38 @@ using namespace sema; //===----------------------------------------------------------------------===/ namespace { +namespace TemplateInstArgsHelpers { +struct Response { + const Decl *NextDecl = nullptr; + bool IsDone = false; + bool ClearRelativeToPrimary = true; + static Response Done() { + Response R; + R.IsDone = true; + return R; + } + static Response ChangeDecl(const Decl *ND) { + Response R; + R.NextDecl = ND; + return R; + } + static Response ChangeDecl(const DeclContext *Ctx) { + Response R; + R.NextDecl = Decl::castFromDeclContext(Ctx); + return R; + } + + static Response UseNextDecl(const Decl *CurDecl) { + return ChangeDecl(CurDecl->getDeclContext()); + } + + static Response DontClearRelativeToPrimaryNextDecl(const Decl *CurDecl) { + Response R = Response::UseNextDecl(CurDecl); + R.ClearRelativeToPrimary = false; + return R; + } +}; + // Retrieve the primary template for a lambda call operator. It's // unfortunate that we only have the mappings of call operators rather // than lambda classes. @@ -139,407 +171,374 @@ bool isLambdaEnclosedByTypeAliasDecl( .TraverseType(Underlying); } -struct TemplateInstantiationArgumentCollecter - : DeclVisitor { - Sema &S; - MultiLevelTemplateArgumentList &Result; - std::optional> Innermost; - bool RelativeToPrimary; - bool ForConstraintInstantiation; - - TemplateInstantiationArgumentCollecter( - Sema &S, MultiLevelTemplateArgumentList &Result, - std::optional> Innermost, - bool RelativeToPrimary, bool ForConstraintInstantiation) - : S(S), Result(Result), Innermost(Innermost), - RelativeToPrimary(RelativeToPrimary), - ForConstraintInstantiation(ForConstraintInstantiation) {} - - Decl *Done() { return nullptr; } - - Decl *ChangeDecl(const Decl *D) { - RelativeToPrimary = false; - return const_cast(D); - } - - Decl *ChangeDecl(const DeclContext *DC) { - return ChangeDecl(Decl::castFromDeclContext(DC)); - } - - Decl *UseNextDecl(const Decl *D) { return ChangeDecl(D->getDeclContext()); } - - void AddInnermostTemplateArguments(const Decl *D) { - assert(Innermost); - Result.addOuterTemplateArguments(const_cast(D), *Innermost, - /*Final=*/false); - Innermost.reset(); - } - - void AddOuterTemplateArguments(const Decl *D, ArrayRef Args, - bool Final) { - Result.addOuterTemplateArguments(const_cast(D), Args, Final); +// Add template arguments from a variable template instantiation. +Response +HandleVarTemplateSpec(const VarTemplateSpecializationDecl *VarTemplSpec, + MultiLevelTemplateArgumentList &Result, + bool SkipForSpecialization) { + // For a class-scope explicit specialization, there are no template arguments + // at this level, but there may be enclosing template arguments. + if (VarTemplSpec->isClassScopeExplicitSpecialization()) + return Response::DontClearRelativeToPrimaryNextDecl(VarTemplSpec); + + // We're done when we hit an explicit specialization. + if (VarTemplSpec->getSpecializationKind() == TSK_ExplicitSpecialization && + !isa(VarTemplSpec)) + return Response::Done(); + + // If this variable template specialization was instantiated from a + // specialized member that is a variable template, we're done. + assert(VarTemplSpec->getSpecializedTemplate() && "No variable template?"); + llvm::PointerUnion + Specialized = VarTemplSpec->getSpecializedTemplateOrPartial(); + if (VarTemplatePartialSpecializationDecl *Partial = + Specialized.dyn_cast()) { + if (!SkipForSpecialization) + Result.addOuterTemplateArguments( + Partial, VarTemplSpec->getTemplateInstantiationArgs().asArray(), + /*Final=*/false); + if (Partial->isMemberSpecialization()) + return Response::Done(); + } else { + VarTemplateDecl *Tmpl = Specialized.get(); + if (!SkipForSpecialization) + Result.addOuterTemplateArguments( + Tmpl, VarTemplSpec->getTemplateInstantiationArgs().asArray(), + /*Final=*/false); + if (Tmpl->isMemberSpecialization()) + return Response::Done(); } + return Response::DontClearRelativeToPrimaryNextDecl(VarTemplSpec); +} - Decl *VisitTemplateTemplateParmDecl(TemplateTemplateParmDecl *TTPD) { - if (Innermost) - AddInnermostTemplateArguments(TTPD); - else if (ForConstraintInstantiation) - AddOuterTemplateArguments(nullptr, std::nullopt, /*Final=*/false); - - for (unsigned Depth = TTPD->getDepth() + 1; Depth--;) - AddOuterTemplateArguments(nullptr, std::nullopt, /*Final=*/false); - - return Done(); - } +// If we have a template template parameter with translation unit context, +// then we're performing substitution into a default template argument of +// this template template parameter before we've constructed the template +// that will own this template template parameter. In this case, we +// use empty template parameter lists for all of the outer templates +// to avoid performing any substitutions. +Response +HandleDefaultTempArgIntoTempTempParam(const TemplateTemplateParmDecl *TTP, + MultiLevelTemplateArgumentList &Result) { + for (unsigned I = 0, N = TTP->getDepth() + 1; I != N; ++I) + Result.addOuterTemplateArguments(std::nullopt); + return Response::Done(); +} - Decl *VisitFunctionTemplateDecl(FunctionTemplateDecl *FTD) { - assert( - (ForConstraintInstantiation || Result.getNumSubstitutedLevels() == 0) && - "outer template not instantiated?"); +Response HandlePartialClassTemplateSpec( + const ClassTemplatePartialSpecializationDecl *PartialClassTemplSpec, + MultiLevelTemplateArgumentList &Result, bool SkipForSpecialization) { + if (!SkipForSpecialization) + Result.addOuterRetainedLevels(PartialClassTemplSpec->getTemplateDepth()); + return Response::Done(); +} - if (Innermost) - AddInnermostTemplateArguments(FTD); - else if (ForConstraintInstantiation) - AddOuterTemplateArguments(FTD, FTD->getInjectedTemplateArgs(), - /*Final=*/false); +// Add template arguments from a class template instantiation. +Response +HandleClassTemplateSpec(const ClassTemplateSpecializationDecl *ClassTemplSpec, + MultiLevelTemplateArgumentList &Result, + bool SkipForSpecialization) { + if (!ClassTemplSpec->isClassScopeExplicitSpecialization()) { + // We're done when we hit an explicit specialization. + if (ClassTemplSpec->getSpecializationKind() == TSK_ExplicitSpecialization && + !isa(ClassTemplSpec)) + return Response::Done(); - if (FTD->isMemberSpecialization()) - return Done(); + if (!SkipForSpecialization) + Result.addOuterTemplateArguments( + const_cast(ClassTemplSpec), + ClassTemplSpec->getTemplateInstantiationArgs().asArray(), + /*Final=*/false); - if (FTD->getFriendObjectKind()) - return ChangeDecl(FTD->getLexicalDeclContext()); - return UseNextDecl(FTD); + // If this class template specialization was instantiated from a + // specialized member that is a class template, we're done. + assert(ClassTemplSpec->getSpecializedTemplate() && "No class template?"); + if (ClassTemplSpec->getSpecializedTemplate()->isMemberSpecialization()) + return Response::Done(); + + // If this was instantiated from a partial template specialization, we need + // to get the next level of declaration context from the partial + // specialization, as the ClassTemplateSpecializationDecl's + // DeclContext/LexicalDeclContext will be for the primary template. + if (auto *InstFromPartialTempl = ClassTemplSpec->getSpecializedTemplateOrPartial() + .dyn_cast()) + return Response::ChangeDecl(InstFromPartialTempl->getLexicalDeclContext()); } + return Response::UseNextDecl(ClassTemplSpec); +} - Decl *VisitVarTemplateDecl(VarTemplateDecl *VTD) { - assert( - (ForConstraintInstantiation || Result.getNumSubstitutedLevels() == 0) && - "outer template not instantiated?"); - - if (Innermost) - AddInnermostTemplateArguments(VTD); - else if (ForConstraintInstantiation) - AddOuterTemplateArguments(VTD, VTD->getInjectedTemplateArgs(), - /*Final=*/false); - - if (VTD->isMemberSpecialization()) - return Done(); - - return UseNextDecl(VTD); - } +Response HandleFunction(Sema &SemaRef, const FunctionDecl *Function, + MultiLevelTemplateArgumentList &Result, + const FunctionDecl *Pattern, bool RelativeToPrimary, + bool ForConstraintInstantiation, + bool ForDefaultArgumentSubstitution) { + // Add template arguments from a function template specialization. + if (!RelativeToPrimary && + Function->getTemplateSpecializationKindForInstantiation() == + TSK_ExplicitSpecialization) + return Response::Done(); + + if (!RelativeToPrimary && + Function->getTemplateSpecializationKind() == TSK_ExplicitSpecialization) { + // This is an implicit instantiation of an explicit specialization. We + // don't get any template arguments from this function but might get + // some from an enclosing template. + return Response::UseNextDecl(Function); + } else if (const TemplateArgumentList *TemplateArgs = + Function->getTemplateSpecializationArgs()) { + // Add the template arguments for this specialization. + Result.addOuterTemplateArguments(const_cast(Function), + TemplateArgs->asArray(), + /*Final=*/false); - Decl *VisitVarTemplatePartialSpecializationDecl( - VarTemplatePartialSpecializationDecl *VTPSD) { + if (RelativeToPrimary && + (Function->getTemplateSpecializationKind() == + TSK_ExplicitSpecialization || + (Function->getFriendObjectKind() && + !Function->getPrimaryTemplate()->getFriendObjectKind()))) + return Response::UseNextDecl(Function); + + // If this function was instantiated from a specialized member that is + // a function template, we're done. + assert(Function->getPrimaryTemplate() && "No function template?"); + if (!ForDefaultArgumentSubstitution && + Function->getPrimaryTemplate()->isMemberSpecialization()) + return Response::Done(); + + // If this function is a generic lambda specialization, we are done. + if (!ForConstraintInstantiation && + isGenericLambdaCallOperatorOrStaticInvokerSpecialization(Function)) + return Response::Done(); + + } else if (Function->getDescribedFunctionTemplate()) { assert( (ForConstraintInstantiation || Result.getNumSubstitutedLevels() == 0) && - "outer template not instantiated?"); - - if (Innermost) - AddInnermostTemplateArguments(VTPSD); - else if (ForConstraintInstantiation) - AddOuterTemplateArguments(VTPSD, VTPSD->getTemplateArgs().asArray(), - /*Final=*/false); - - if (VTPSD->isMemberSpecialization()) - return Done(); - - return UseNextDecl(VTPSD); + "Outer template not instantiated?"); } - - Decl *VisitClassTemplateDecl(ClassTemplateDecl *CTD) { - assert( - (ForConstraintInstantiation || Result.getNumSubstitutedLevels() == 0) && - "outer template not instantiated?"); - - if (Innermost) - AddInnermostTemplateArguments(CTD); - else if (ForConstraintInstantiation) - AddOuterTemplateArguments(CTD, CTD->getInjectedTemplateArgs(), - /*Final=*/false); - - if (CTD->isMemberSpecialization()) - return Done(); - - if (CTD->getFriendObjectKind()) - return ChangeDecl(CTD->getLexicalDeclContext()); - return UseNextDecl(CTD); + // If this is a friend or local declaration and it declares an entity at + // namespace scope, take arguments from its lexical parent + // instead of its semantic parent, unless of course the pattern we're + // instantiating actually comes from the file's context! + if ((Function->getFriendObjectKind() || Function->isLocalExternDecl()) && + Function->getNonTransparentDeclContext()->isFileContext() && + (!Pattern || !Pattern->getLexicalDeclContext()->isFileContext())) { + return Response::ChangeDecl(Function->getLexicalDeclContext()); } - Decl *VisitClassTemplatePartialSpecializationDecl( - ClassTemplatePartialSpecializationDecl *CTPSD) { - assert( - (ForConstraintInstantiation || Result.getNumSubstitutedLevels() == 0) && - "outer template not instantiated?"); - - if (Innermost) - AddInnermostTemplateArguments(CTPSD); - else if (ForConstraintInstantiation) - AddOuterTemplateArguments(CTPSD, CTPSD->getTemplateArgs().asArray(), - /*Final=*/false); + if (ForConstraintInstantiation && Function->getFriendObjectKind()) + return Response::ChangeDecl(Function->getLexicalDeclContext()); + return Response::UseNextDecl(Function); +} - if (CTPSD->isMemberSpecialization()) - return Done(); +Response HandleFunctionTemplateDecl(const FunctionTemplateDecl *FTD, + MultiLevelTemplateArgumentList &Result) { + if (!isa(FTD->getDeclContext())) { + Result.addOuterTemplateArguments( + const_cast(FTD), + const_cast(FTD)->getInjectedTemplateArgs(), + /*Final=*/false); + + NestedNameSpecifier *NNS = FTD->getTemplatedDecl()->getQualifier(); + + while (const Type *Ty = NNS ? NNS->getAsType() : nullptr) { + if (NNS->isInstantiationDependent()) { + if (const auto *TSTy = Ty->getAs()) { + ArrayRef Arguments = TSTy->template_arguments(); + // Prefer template arguments from the injected-class-type if possible. + // For example, + // ```cpp + // template struct S { + // template void foo(); + // }; + // template template + // ^^^^^^^^^^^^^ InjectedTemplateArgs + // They're of kind TemplateArgument::Pack, not of + // TemplateArgument::Type. + // void S::foo() {} + // ^^^^^^^ + // TSTy->template_arguments() (which are of PackExpansionType) + // ``` + // This meets the contract in + // TreeTransform::TryExpandParameterPacks that the template arguments + // for unexpanded parameters should be of a Pack kind. + if (TSTy->isCurrentInstantiation()) { + auto *RD = TSTy->getCanonicalTypeInternal()->getAsCXXRecordDecl(); + if (ClassTemplateDecl *CTD = RD->getDescribedClassTemplate()) + Arguments = CTD->getInjectedTemplateArgs(); + else if (auto *Specialization = + dyn_cast(RD)) + Arguments = + Specialization->getTemplateInstantiationArgs().asArray(); + } + Result.addOuterTemplateArguments( + TSTy->getTemplateName().getAsTemplateDecl(), Arguments, + /*Final=*/false); + } + } - return UseNextDecl(CTPSD); + NNS = NNS->getPrefix(); + } } - Decl *VisitTypeAliasTemplateDecl(TypeAliasTemplateDecl *TATD) { - assert( - (ForConstraintInstantiation || Result.getNumSubstitutedLevels() == 0) && - "outer template not instantiated?"); - if (Innermost) - AddInnermostTemplateArguments(TATD); - else if (ForConstraintInstantiation) - AddOuterTemplateArguments(TATD, TATD->getInjectedTemplateArgs(), - /*Final=*/false); - - return UseNextDecl(TATD); - } + return Response::ChangeDecl(FTD->getLexicalDeclContext()); +} - Decl *VisitConceptDecl(ConceptDecl *CD) { +Response HandleRecordDecl(Sema &SemaRef, const CXXRecordDecl *Rec, + MultiLevelTemplateArgumentList &Result, + ASTContext &Context, + bool ForConstraintInstantiation) { + if (ClassTemplateDecl *ClassTemplate = Rec->getDescribedClassTemplate()) { assert( (ForConstraintInstantiation || Result.getNumSubstitutedLevels() == 0) && - "outer template not instantiated?"); - if (Innermost) - AddInnermostTemplateArguments(CD); - - return UseNextDecl(CD); + "Outer template not instantiated?"); + if (ClassTemplate->isMemberSpecialization()) + return Response::Done(); + if (ForConstraintInstantiation) + Result.addOuterTemplateArguments(const_cast(Rec), + ClassTemplate->getInjectedTemplateArgs(), + /*Final=*/false); } - Decl *VisitFunctionDecl(FunctionDecl *FD) { - assert(!FD->getDescribedFunctionTemplate() && - "not for templated declarations"); - - if (!RelativeToPrimary) { - // Add template arguments from a function template specialization. - if (const MemberSpecializationInfo *MSI = - FD->getMemberSpecializationInfo(); - MSI && - MSI->getTemplateSpecializationKind() == TSK_ExplicitSpecialization) - return Done(); - - // This is an implicit instantiation of an explicit specialization. We - // don't get any template arguments from this function but might get - // some from an enclosing template. - if (FD->getTemplateSpecializationKind() == TSK_ExplicitSpecialization) - return UseNextDecl(FD); - } - - if (const TemplateArgumentList *TemplateArgs = - FD->getTemplateSpecializationArgs()) { - // Add the template arguments for this specialization. - if (Innermost) - AddInnermostTemplateArguments(FD); - else - AddOuterTemplateArguments(FD, TemplateArgs->asArray(), /*Final=*/false); - - if (FD->getTemplateSpecializationKind() == TSK_ExplicitSpecialization || - (FD->getFriendObjectKind() && - !FD->getPrimaryTemplate()->getFriendObjectKind())) - return UseNextDecl(FD); - - // If this function was instantiated from a specialized member that is - // a function template, we're done. - assert(FD->getPrimaryTemplate() && "No function template?"); - if (FD->getPrimaryTemplate()->isMemberSpecialization()) - return Done(); - - // If this function is a generic lambda specialization, we are done. - if (!ForConstraintInstantiation && - isGenericLambdaCallOperatorOrStaticInvokerSpecialization(FD)) - return Done(); - } - - // If this is a friend or local declaration and it declares an entity at - // namespace scope, take arguments from its lexical parent - // instead of its semantic parent, unless of course the pattern we're - // instantiating actually comes from the file's context! - if ((FD->getFriendObjectKind() || FD->isLocalExternDecl()) && - FD->getNonTransparentDeclContext()->isFileContext()) { - return ChangeDecl(FD->getLexicalDeclContext()); - } - - if (ForConstraintInstantiation && FD->getFriendObjectKind()) - return ChangeDecl(FD->getLexicalDeclContext()); - return UseNextDecl(FD); + if (const MemberSpecializationInfo *MSInfo = + Rec->getMemberSpecializationInfo()) + if (MSInfo->getTemplateSpecializationKind() == TSK_ExplicitSpecialization) + return Response::Done(); + + bool IsFriend = Rec->getFriendObjectKind() || + (Rec->getDescribedClassTemplate() && + Rec->getDescribedClassTemplate()->getFriendObjectKind()); + if (ForConstraintInstantiation && IsFriend && + Rec->getNonTransparentDeclContext()->isFileContext()) { + return Response::ChangeDecl(Rec->getLexicalDeclContext()); } - Decl *VisitCXXRecordDecl(CXXRecordDecl *RD) { - assert(!RD->getDescribedClassTemplate() && - "not for templated declarations"); - - if (const MemberSpecializationInfo *MSI = RD->getMemberSpecializationInfo(); - MSI && - MSI->getTemplateSpecializationKind() == TSK_ExplicitSpecialization) - return Done(); - - if (ForConstraintInstantiation && RD->getFriendObjectKind() && - RD->getNonTransparentDeclContext()->isFileContext()) { - return ChangeDecl(RD->getLexicalDeclContext()); - } - - // This is to make sure we pick up the VarTemplateSpecializationDecl or the - // TypeAliasTemplateDecl that this lambda is defined inside of. - if (RD->isLambda()) { - if (Decl *LCD = RD->getLambdaContextDecl()) - return ChangeDecl(LCD); - // Retrieve the template arguments for a using alias declaration. - // This is necessary for constraint checking, since we always keep - // constraints relative to the primary template. - if (auto TypeAlias = getEnclosingTypeAliasTemplateDecl(S); - ForConstraintInstantiation && TypeAlias) { - if (isLambdaEnclosedByTypeAliasDecl(RD->getLambdaCallOperator(), - TypeAlias.PrimaryTypeAliasDecl)) { - AddOuterTemplateArguments(TypeAlias.Template, - TypeAlias.AssociatedTemplateArguments, - /*Final=*/false); - // Visit the parent of the current type alias declaration rather than - // the lambda thereof. - // E.g., in the following example: - // struct S { - // template using T = decltype([] {} ()); - // }; - // void foo() { - // S::T var; - // } - // The instantiated lambda expression (which we're visiting at 'var') - // has a function DeclContext 'foo' rather than the Record DeclContext - // S. This seems to be an oversight to me that we may want to set a - // Sema Context from the CXXScopeSpec before substituting into T. - return ChangeDecl(TypeAlias.Template->getDeclContext()); - } + // This is to make sure we pick up the VarTemplateSpecializationDecl or the + // TypeAliasTemplateDecl that this lambda is defined inside of. + if (Rec->isLambda()) { + if (const Decl *LCD = Rec->getLambdaContextDecl()) + return Response::ChangeDecl(LCD); + // Retrieve the template arguments for a using alias declaration. + // This is necessary for constraint checking, since we always keep + // constraints relative to the primary template. + if (auto TypeAlias = getEnclosingTypeAliasTemplateDecl(SemaRef); + ForConstraintInstantiation && TypeAlias) { + if (isLambdaEnclosedByTypeAliasDecl(Rec->getLambdaCallOperator(), + TypeAlias.PrimaryTypeAliasDecl)) { + Result.addOuterTemplateArguments(TypeAlias.Template, + TypeAlias.AssociatedTemplateArguments, + /*Final=*/false); + // Visit the parent of the current type alias declaration rather than + // the lambda thereof. + // E.g., in the following example: + // struct S { + // template using T = decltype([] {} ()); + // }; + // void foo() { + // S::T var; + // } + // The instantiated lambda expression (which we're visiting at 'var') + // has a function DeclContext 'foo' rather than the Record DeclContext + // S. This seems to be an oversight to me that we may want to set a + // Sema Context from the CXXScopeSpec before substituting into T. + return Response::ChangeDecl(TypeAlias.Template->getDeclContext()); } } - - return UseNextDecl(RD); - } - - Decl * - VisitClassTemplateSpecializationDecl(ClassTemplateSpecializationDecl *CTSD) { - // For a class-scope explicit specialization, there are no template - // arguments at this level, but there may be enclosing template arguments. - if (CTSD->isClassScopeExplicitSpecialization()) - return UseNextDecl(CTSD); - - // We're done when we hit an explicit specialization. - if (CTSD->getSpecializationKind() == TSK_ExplicitSpecialization) - return Done(); - - if (Innermost) - AddInnermostTemplateArguments(CTSD); - else - AddOuterTemplateArguments(CTSD, - CTSD->getTemplateInstantiationArgs().asArray(), - /*Final=*/false); - - // If this class template specialization was instantiated from a - // specialized member that is a class template, we're done. - assert(CTSD->getSpecializedTemplate() && "No class template?"); - llvm::PointerUnion - Specialized = CTSD->getSpecializedTemplateOrPartial(); - if (auto *CTPSD = - Specialized.dyn_cast()) { - if (CTPSD->isMemberSpecialization()) - return Done(); - } else { - auto *CTD = Specialized.get(); - if (CTD->isMemberSpecialization()) - return Done(); - } - return UseNextDecl(CTSD); } - Decl * - VisitVarTemplateSpecializationDecl(VarTemplateSpecializationDecl *VTSD) { - // For a class-scope explicit specialization, there are no template - // arguments at this level, but there may be enclosing template arguments. - if (VTSD->isClassScopeExplicitSpecialization()) - return UseNextDecl(VTSD); - - // We're done when we hit an explicit specialization. - if (VTSD->getSpecializationKind() == TSK_ExplicitSpecialization) - return Done(); - - if (Innermost) - AddInnermostTemplateArguments(VTSD); - else - AddOuterTemplateArguments(VTSD, - VTSD->getTemplateInstantiationArgs().asArray(), - /*Final=*/false); - - // If this variable template specialization was instantiated from a - // specialized member that is a variable template, we're done. - assert(VTSD->getSpecializedTemplate() && "No variable template?"); - llvm::PointerUnion - Specialized = VTSD->getSpecializedTemplateOrPartial(); - if (auto *VTPSD = - Specialized.dyn_cast()) { - if (VTPSD->isMemberSpecialization()) - return Done(); - } else { - auto *VTD = Specialized.get(); - if (VTD->isMemberSpecialization()) - return Done(); - } - return UseNextDecl(VTSD); - } - - Decl *VisitImplicitConceptSpecializationDecl( - ImplicitConceptSpecializationDecl *ICSD) { - AddOuterTemplateArguments(ICSD, ICSD->getTemplateArguments(), - /*Final=*/false); - return UseNextDecl(ICSD); - } - - Decl *VisitDecl(Decl *D) { - if (D->isFileContextDecl()) - return Done(); - - if (isa(D)) - RelativeToPrimary = false; - - return UseNextDecl(D); - } + return Response::UseNextDecl(Rec); +} - Decl *Visit(Decl *D) { - if (TemplateDecl *TD = D->getDescribedTemplate()) - D = TD; - return DeclVisitor::Visit(D); - } -}; +Response HandleImplicitConceptSpecializationDecl( + const ImplicitConceptSpecializationDecl *CSD, + MultiLevelTemplateArgumentList &Result) { + Result.addOuterTemplateArguments( + const_cast(CSD), + CSD->getTemplateArguments(), + /*Final=*/false); + return Response::UseNextDecl(CSD); +} +Response HandleGenericDeclContext(const Decl *CurDecl) { + return Response::UseNextDecl(CurDecl); +} +} // namespace TemplateInstArgsHelpers } // namespace -void Sema::getTemplateInstantiationArgs( - MultiLevelTemplateArgumentList &Result, const NamedDecl *ND, - const DeclContext *DC, bool Final, +MultiLevelTemplateArgumentList Sema::getTemplateInstantiationArgs( + const NamedDecl *ND, const DeclContext *DC, bool Final, std::optional> Innermost, bool RelativeToPrimary, - bool ForConstraintInstantiation) { + const FunctionDecl *Pattern, bool ForConstraintInstantiation, + bool SkipForSpecialization, bool ForDefaultArgumentSubstitution) { assert((ND || DC) && "Can't find arguments for a decl if one isn't provided"); // Accumulate the set of template argument lists in this structure. + MultiLevelTemplateArgumentList Result; + + using namespace TemplateInstArgsHelpers; const Decl *CurDecl = ND; if (!CurDecl) CurDecl = Decl::castFromDeclContext(DC); - TemplateInstantiationArgumentCollecter Collecter( - *this, Result, Innermost, RelativeToPrimary, ForConstraintInstantiation); - do { - CurDecl = Collecter.Visit(const_cast(CurDecl)); - } while (CurDecl); -} + if (Innermost) { + Result.addOuterTemplateArguments(const_cast(ND), *Innermost, + Final); + // Populate placeholder template arguments for TemplateTemplateParmDecls. + // This is essential for the case e.g. + // + // template concept Concept = false; + // template