[clang] ff1e0fc - [Concepts] Type Constraints
Saar Raz via cfe-commits
cfe-commits at lists.llvm.org
Tue Jan 14 18:02:59 PST 2020
Author: Saar Raz
Date: 2020-01-15T04:02:39+02:00
New Revision: ff1e0fce817e01f0288fad6a2607dd173180aabd
URL: https://github.com/llvm/llvm-project/commit/ff1e0fce817e01f0288fad6a2607dd173180aabd
DIFF: https://github.com/llvm/llvm-project/commit/ff1e0fce817e01f0288fad6a2607dd173180aabd.diff
LOG: [Concepts] Type Constraints
Add support for type-constraints in template type parameters.
Also add support for template type parameters as pack expansions (where the type constraint can now contain an unexpanded parameter pack).
Differential Revision: https://reviews.llvm.org/D44352
Added:
clang/test/CXX/temp/temp.arg/temp.arg.template/p3-2a.cpp
clang/test/CXX/temp/temp.constr/temp.constr.decl/p3.cpp
clang/test/CXX/temp/temp.param/p10-2a.cpp
clang/test/Parser/cxx2a-constrained-template-param-with-partial-id.cpp
clang/test/Parser/cxx2a-constrained-template-param.cpp
clang/test/SemaTemplate/instantiate-expanded-type-constraint.cpp
Modified:
clang/include/clang/AST/ASTConcept.h
clang/include/clang/AST/ASTContext.h
clang/include/clang/AST/ASTNodeTraverser.h
clang/include/clang/AST/DeclTemplate.h
clang/include/clang/AST/ExprCXX.h
clang/include/clang/AST/RecursiveASTVisitor.h
clang/include/clang/Basic/DiagnosticSemaKinds.td
clang/include/clang/Basic/TokenKinds.def
clang/include/clang/Parse/Parser.h
clang/include/clang/Sema/ParsedTemplate.h
clang/include/clang/Sema/Sema.h
clang/lib/AST/ASTContext.cpp
clang/lib/AST/ASTImporter.cpp
clang/lib/AST/DeclPrinter.cpp
clang/lib/AST/DeclTemplate.cpp
clang/lib/AST/ExprCXX.cpp
clang/lib/AST/ODRHash.cpp
clang/lib/AST/TextNodeDumper.cpp
clang/lib/Parse/ParseDecl.cpp
clang/lib/Parse/ParseExprCXX.cpp
clang/lib/Parse/ParseTemplate.cpp
clang/lib/Sema/SemaCodeComplete.cpp
clang/lib/Sema/SemaTemplate.cpp
clang/lib/Sema/SemaTemplateDeduction.cpp
clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
clang/lib/Sema/SemaType.cpp
clang/lib/Sema/TreeTransform.h
clang/lib/Serialization/ASTReaderDecl.cpp
clang/lib/Serialization/ASTReaderStmt.cpp
clang/lib/Serialization/ASTWriterDecl.cpp
clang/lib/Serialization/ASTWriterStmt.cpp
clang/test/CXX/temp/temp.constr/temp.constr.decl/class-template-decl.cpp
clang/test/SemaTemplate/ms-delayed-default-template-args.cpp
clang/tools/libclang/CIndex.cpp
Removed:
################################################################################
diff --git a/clang/include/clang/AST/ASTConcept.h b/clang/include/clang/AST/ASTConcept.h
index 937a8a9b345e..896d857d8c96 100644
--- a/clang/include/clang/AST/ASTConcept.h
+++ b/clang/include/clang/AST/ASTConcept.h
@@ -21,6 +21,7 @@
#include <string>
#include <utility>
namespace clang {
+class ConceptDecl;
/// \brief The result of a constraint satisfaction check, containing the
/// necessary information to diagnose an unsatisfied constraint.
@@ -75,6 +76,102 @@ struct ASTConstraintSatisfaction final :
Create(const ASTContext &C, const ConstraintSatisfaction &Satisfaction);
};
+/// \brief Common data class for constructs that reference concepts with
+/// template arguments.
+class ConceptReference {
+protected:
+ // \brief The optional nested name specifier used when naming the concept.
+ NestedNameSpecifierLoc NestedNameSpec;
+
+ /// \brief The location of the template keyword, if specified when naming the
+ /// concept.
+ SourceLocation TemplateKWLoc;
+
+ /// \brief The concept name used.
+ DeclarationNameInfo ConceptName;
+
+ /// \brief The declaration found by name lookup when the expression was
+ /// created.
+ /// Can
diff er from NamedConcept when, for example, the concept was found
+ /// through a UsingShadowDecl.
+ NamedDecl *FoundDecl;
+
+ /// \brief The concept named.
+ ConceptDecl *NamedConcept;
+
+ /// \brief The template argument list source info used to specialize the
+ /// concept.
+ const ASTTemplateArgumentListInfo *ArgsAsWritten;
+
+public:
+
+ ConceptReference(NestedNameSpecifierLoc NNS, SourceLocation TemplateKWLoc,
+ DeclarationNameInfo ConceptNameInfo, NamedDecl *FoundDecl,
+ ConceptDecl *NamedConcept,
+ const ASTTemplateArgumentListInfo *ArgsAsWritten) :
+ NestedNameSpec(NNS), TemplateKWLoc(TemplateKWLoc),
+ ConceptName(ConceptNameInfo), FoundDecl(FoundDecl),
+ NamedConcept(NamedConcept), ArgsAsWritten(ArgsAsWritten) {}
+
+ ConceptReference() : NestedNameSpec(), TemplateKWLoc(), ConceptName(),
+ FoundDecl(nullptr), NamedConcept(nullptr), ArgsAsWritten(nullptr) {}
+
+ const NestedNameSpecifierLoc &getNestedNameSpecifierLoc() const {
+ return NestedNameSpec;
+ }
+
+ const DeclarationNameInfo &getConceptNameInfo() const { return ConceptName; }
+
+ SourceLocation getConceptNameLoc() const {
+ return getConceptNameInfo().getLoc();
+ }
+
+ SourceLocation getTemplateKWLoc() const { return TemplateKWLoc; }
+
+ NamedDecl *getFoundDecl() const {
+ return FoundDecl;
+ }
+
+ ConceptDecl *getNamedConcept() const {
+ return NamedConcept;
+ }
+
+ const ASTTemplateArgumentListInfo *getTemplateArgsAsWritten() const {
+ return ArgsAsWritten;
+ }
+
+ /// \brief Whether or not template arguments were explicitly specified in the
+ /// concept reference (they might not be in type constraints, for example)
+ bool hasExplicitTemplateArgs() const {
+ return ArgsAsWritten != nullptr;
+ }
+};
+
+class TypeConstraint : public ConceptReference {
+ /// \brief The immediately-declared constraint expression introduced by this
+ /// type-constraint.
+ Expr *ImmediatelyDeclaredConstraint = nullptr;
+
+public:
+ TypeConstraint(NestedNameSpecifierLoc NNS,
+ DeclarationNameInfo ConceptNameInfo, NamedDecl *FoundDecl,
+ ConceptDecl *NamedConcept,
+ const ASTTemplateArgumentListInfo *ArgsAsWritten,
+ Expr *ImmediatelyDeclaredConstraint) :
+ ConceptReference(NNS, /*TemplateKWLoc=*/SourceLocation(), ConceptNameInfo,
+ FoundDecl, NamedConcept, ArgsAsWritten),
+ ImmediatelyDeclaredConstraint(ImmediatelyDeclaredConstraint) {}
+
+ /// \brief Get the immediately-declared constraint expression introduced by
+ /// this type-constraint, that is - the constraint expression that is added to
+ /// the associated constraints of the enclosing declaration in practice.
+ Expr *getImmediatelyDeclaredConstraint() const {
+ return ImmediatelyDeclaredConstraint;
+ }
+
+ void print(llvm::raw_ostream &OS, PrintingPolicy Policy) const;
+};
+
} // clang
#endif // LLVM_CLANG_AST_ASTCONCEPT_H
\ No newline at end of file
diff --git a/clang/include/clang/AST/ASTContext.h b/clang/include/clang/AST/ASTContext.h
index d5ade9340c8e..fb269cef1ce8 100644
--- a/clang/include/clang/AST/ASTContext.h
+++ b/clang/include/clang/AST/ASTContext.h
@@ -283,12 +283,16 @@ class ASTContext : public RefCountedBase<ASTContext> {
TemplateTemplateParmDecl *getParam() const { return Parm; }
- void Profile(llvm::FoldingSetNodeID &ID) { Profile(ID, Parm); }
+ void Profile(llvm::FoldingSetNodeID &ID, const ASTContext &C) {
+ Profile(ID, C, Parm);
+ }
static void Profile(llvm::FoldingSetNodeID &ID,
+ const ASTContext &C,
TemplateTemplateParmDecl *Parm);
};
- mutable llvm::FoldingSet<CanonicalTemplateTemplateParm>
+ mutable llvm::ContextualFoldingSet<CanonicalTemplateTemplateParm,
+ const ASTContext&>
CanonTemplateTemplateParms;
TemplateTemplateParmDecl *
diff --git a/clang/include/clang/AST/ASTNodeTraverser.h b/clang/include/clang/AST/ASTNodeTraverser.h
index a672d92695da..e0ebb020e697 100644
--- a/clang/include/clang/AST/ASTNodeTraverser.h
+++ b/clang/include/clang/AST/ASTNodeTraverser.h
@@ -537,6 +537,10 @@ class ASTNodeTraverser
}
void VisitTemplateTypeParmDecl(const TemplateTypeParmDecl *D) {
+ if (const auto *TC = D->getTypeConstraint())
+ if (TC->hasExplicitTemplateArgs())
+ for (const auto &ArgLoc : TC->getTemplateArgsAsWritten()->arguments())
+ dumpTemplateArgumentLoc(ArgLoc);
if (D->hasDefaultArgument())
Visit(D->getDefaultArgument(), SourceRange(),
D->getDefaultArgStorage().getInheritedFrom(),
@@ -544,6 +548,8 @@ class ASTNodeTraverser
}
void VisitNonTypeTemplateParmDecl(const NonTypeTemplateParmDecl *D) {
+ if (const auto *TC = D->getPlaceholderTypeConstraint())
+ Visit(TC->getImmediatelyDeclaredConstraint());
if (D->hasDefaultArgument())
Visit(D->getDefaultArgument(), SourceRange(),
D->getDefaultArgStorage().getInheritedFrom(),
diff --git a/clang/include/clang/AST/DeclTemplate.h b/clang/include/clang/AST/DeclTemplate.h
index 23905d12c8b2..7a55d04a0f35 100755
--- a/clang/include/clang/AST/DeclTemplate.h
+++ b/clang/include/clang/AST/DeclTemplate.h
@@ -14,6 +14,7 @@
#ifndef LLVM_CLANG_AST_DECLTEMPLATE_H
#define LLVM_CLANG_AST_DECLTEMPLATE_H
+#include "clang/AST/ASTConcept.h"
#include "clang/AST/Decl.h"
#include "clang/AST/DeclBase.h"
#include "clang/AST/DeclCXX.h"
@@ -51,6 +52,7 @@ class NonTypeTemplateParmDecl;
class TemplateDecl;
class TemplateTemplateParmDecl;
class TemplateTypeParmDecl;
+class ConceptDecl;
class UnresolvedSetImpl;
class VarTemplateDecl;
class VarTemplatePartialSpecializationDecl;
@@ -81,20 +83,24 @@ class TemplateParameterList final
/// pack.
unsigned ContainsUnexpandedParameterPack : 1;
- /// Whether this template parameter list has an associated requires-clause
+ /// Whether this template parameter list has a requires clause.
unsigned HasRequiresClause : 1;
+ /// Whether any of the template parameters has constrained-parameter
+ /// constraint-expression.
+ unsigned HasConstrainedParameters : 1;
+
protected:
- TemplateParameterList(SourceLocation TemplateLoc, SourceLocation LAngleLoc,
- ArrayRef<NamedDecl *> Params, SourceLocation RAngleLoc,
- Expr *RequiresClause);
+ TemplateParameterList(const ASTContext& C, SourceLocation TemplateLoc,
+ SourceLocation LAngleLoc, ArrayRef<NamedDecl *> Params,
+ SourceLocation RAngleLoc, Expr *RequiresClause);
size_t numTrailingObjects(OverloadToken<NamedDecl *>) const {
return NumParams;
}
size_t numTrailingObjects(OverloadToken<Expr *>) const {
- return HasRequiresClause;
+ return HasRequiresClause ? 1 : 0;
}
public:
@@ -158,14 +164,22 @@ class TemplateParameterList final
return ContainsUnexpandedParameterPack;
}
+ /// Determine whether this template parameter list contains a parameter pack.
+ bool hasParameterPack() const {
+ for (const NamedDecl *P : asArray())
+ if (P->isParameterPack())
+ return true;
+ return false;
+ }
+
/// The constraint-expression of the associated requires-clause.
Expr *getRequiresClause() {
- return HasRequiresClause ? *getTrailingObjects<Expr *>() : nullptr;
+ return HasRequiresClause ? getTrailingObjects<Expr *>()[0] : nullptr;
}
/// The constraint-expression of the associated requires-clause.
const Expr *getRequiresClause() const {
- return HasRequiresClause ? *getTrailingObjects<Expr *>() : nullptr;
+ return HasRequiresClause ? getTrailingObjects<Expr *>()[0] : nullptr;
}
/// \brief All associated constraints derived from this template parameter
@@ -208,15 +222,16 @@ class FixedSizeTemplateParameterListStorage
>::type storage;
public:
- FixedSizeTemplateParameterListStorage(SourceLocation TemplateLoc,
+ FixedSizeTemplateParameterListStorage(const ASTContext &C,
+ SourceLocation TemplateLoc,
SourceLocation LAngleLoc,
ArrayRef<NamedDecl *> Params,
SourceLocation RAngleLoc,
Expr *RequiresClause)
: FixedSizeStorageOwner(
(assert(N == Params.size()),
- assert(HasRequiresClause == static_cast<bool>(RequiresClause)),
- new (static_cast<void *>(&storage)) TemplateParameterList(
+ assert(HasRequiresClause == (RequiresClause != nullptr)),
+ new (static_cast<void *>(&storage)) TemplateParameterList(C,
TemplateLoc, LAngleLoc, Params, RAngleLoc, RequiresClause))) {}
};
@@ -1148,9 +1163,12 @@ class TemplateParmPosition {
/// \code
/// template<typename T> class vector;
/// \endcode
-class TemplateTypeParmDecl : public TypeDecl {
+class TemplateTypeParmDecl final : public TypeDecl,
+ private llvm::TrailingObjects<TemplateTypeParmDecl, TypeConstraint> {
/// Sema creates these on the stack during auto type deduction.
friend class Sema;
+ friend TrailingObjects;
+ friend class ASTDeclReader;
/// Whether this template type parameter was declaration with
/// the 'typename' keyword.
@@ -1158,6 +1176,22 @@ class TemplateTypeParmDecl : public TypeDecl {
/// If false, it was declared with the 'class' keyword.
bool Typename : 1;
+ /// Whether this template type parameter has a type-constraint construct.
+ bool HasTypeConstraint : 1;
+
+ /// Whether the type constraint has been initialized. This can be false if the
+ /// constraint was not initialized yet or if there was an error forming the
+ /// type constriant.
+ bool TypeConstraintInitialized : 1;
+
+ /// Whether this non-type template parameter is an "expanded"
+ /// parameter pack, meaning that its type is a pack expansion and we
+ /// already know the set of types that expansion expands to.
+ bool ExpandedParameterPack : 1;
+
+ /// The number of type parameters in an expanded parameter pack.
+ unsigned NumExpanded = 0;
+
/// The default template argument, if any.
using DefArgStorage =
DefaultArgStorage<TemplateTypeParmDecl, TypeSourceInfo *>;
@@ -1165,8 +1199,12 @@ class TemplateTypeParmDecl : public TypeDecl {
TemplateTypeParmDecl(DeclContext *DC, SourceLocation KeyLoc,
SourceLocation IdLoc, IdentifierInfo *Id,
- bool Typename)
- : TypeDecl(TemplateTypeParm, DC, IdLoc, Id, KeyLoc), Typename(Typename) {}
+ bool Typename, bool HasTypeConstraint,
+ Optional<unsigned> NumExpanded)
+ : TypeDecl(TemplateTypeParm, DC, IdLoc, Id, KeyLoc), Typename(Typename),
+ HasTypeConstraint(HasTypeConstraint), TypeConstraintInitialized(false),
+ ExpandedParameterPack(NumExpanded),
+ NumExpanded(NumExpanded ? *NumExpanded : 0) {}
public:
static TemplateTypeParmDecl *Create(const ASTContext &C, DeclContext *DC,
@@ -1174,15 +1212,24 @@ class TemplateTypeParmDecl : public TypeDecl {
SourceLocation NameLoc,
unsigned D, unsigned P,
IdentifierInfo *Id, bool Typename,
- bool ParameterPack);
+ bool ParameterPack,
+ bool HasTypeConstraint = false,
+ Optional<unsigned> NumExpanded = None);
+
static TemplateTypeParmDecl *CreateDeserialized(const ASTContext &C,
unsigned ID);
+ static TemplateTypeParmDecl *CreateDeserialized(const ASTContext &C,
+ unsigned ID,
+ bool HasTypeConstraint);
/// Whether this template type parameter was declared with
/// the 'typename' keyword.
///
- /// If not, it was declared with the 'class' keyword.
- bool wasDeclaredWithTypename() const { return Typename; }
+ /// If not, it was either declared with the 'class' keyword or with a
+ /// type-constraint (see hasTypeConstraint()).
+ bool wasDeclaredWithTypename() const {
+ return Typename && !HasTypeConstraint;
+ }
const DefArgStorage &getDefaultArgStorage() const { return DefaultArgument; }
@@ -1239,6 +1286,78 @@ class TemplateTypeParmDecl : public TypeDecl {
/// Returns whether this is a parameter pack.
bool isParameterPack() const;
+ /// Whether this parameter pack is a pack expansion.
+ ///
+ /// A template type template parameter pack can be a pack expansion if its
+ /// type-constraint contains an unexpanded parameter pack.
+ bool isPackExpansion() const {
+ if (!isParameterPack())
+ return false;
+ if (const TypeConstraint *TC = getTypeConstraint())
+ if (TC->hasExplicitTemplateArgs())
+ for (const auto &ArgLoc : TC->getTemplateArgsAsWritten()->arguments())
+ if (ArgLoc.getArgument().containsUnexpandedParameterPack())
+ return true;
+ return false;
+ }
+
+ /// Whether this parameter is a template type parameter pack that has a known
+ /// list of
diff erent type-constraints at
diff erent positions.
+ ///
+ /// A parameter pack is an expanded parameter pack when the original
+ /// parameter pack's type-constraint was itself a pack expansion, and that
+ /// expansion has already been expanded. For example, given:
+ ///
+ /// \code
+ /// template<typename ...Types>
+ /// struct X {
+ /// template<convertible_to<Types> ...Convertibles>
+ /// struct Y { /* ... */ };
+ /// };
+ /// \endcode
+ ///
+ /// The parameter pack \c Convertibles has (convertible_to<Types> && ...) as
+ /// its type-constraint. When \c Types is supplied with template arguments by
+ /// instantiating \c X, the instantiation of \c Convertibles becomes an
+ /// expanded parameter pack. For example, instantiating
+ /// \c X<int, unsigned int> results in \c Convertibles being an expanded
+ /// parameter pack of size 2 (use getNumExpansionTypes() to get this number).
+ bool isExpandedParameterPack() const { return ExpandedParameterPack; }
+
+ /// Retrieves the number of parameters in an expanded parameter pack.
+ unsigned getNumExpansionParameters() const {
+ assert(ExpandedParameterPack && "Not an expansion parameter pack");
+ return NumExpanded;
+ }
+
+ /// Returns the type constraint associated with this template parameter (if
+ /// any).
+ const TypeConstraint *getTypeConstraint() const {
+ return TypeConstraintInitialized ? getTrailingObjects<TypeConstraint>() :
+ nullptr;
+ }
+
+ void setTypeConstraint(NestedNameSpecifierLoc NNS,
+ DeclarationNameInfo NameInfo, NamedDecl *FoundDecl,
+ ConceptDecl *CD,
+ const ASTTemplateArgumentListInfo *ArgsAsWritten,
+ Expr *ImmediatelyDeclaredConstraint);
+
+ /// Determine whether this template parameter has a type-constraint.
+ bool hasTypeConstraint() const {
+ return HasTypeConstraint;
+ }
+
+ /// \brief Get the associated-constraints of this template parameter.
+ /// This will either be the immediately-introduced constraint or empty.
+ ///
+ /// Use this instead of getConstraintExpression for concepts APIs that
+ /// accept an ArrayRef of constraint expressions.
+ void getAssociatedConstraints(llvm::SmallVectorImpl<const Expr *> &AC) const {
+ if (HasTypeConstraint)
+ AC.push_back(getTypeConstraint()->getImmediatelyDeclaredConstraint());
+ }
+
SourceRange getSourceRange() const override LLVM_READONLY;
// Implement isa/cast/dyncast/etc.
@@ -1424,6 +1543,33 @@ class NonTypeTemplateParmDecl final
return TypesAndInfos[I].second;
}
+ /// Return the type-constraint in the placeholder type of this non-type
+ /// template parameter (if any).
+ TypeConstraint *getPlaceholderTypeConstraint() const {
+ // TODO: Concepts: Implement once we have actual placeholders with type
+ // constraints.
+ return nullptr;
+ }
+
+ /// Determine whether this non-type template parameter's type has a
+ /// placeholder with a type-constraint.
+ bool hasPlaceholderTypeConstraint() const {
+ // TODO: Concepts: Implement once we have actual placeholders with type
+ // constraints.
+ return false;
+ }
+
+ /// \brief Get the associated-constraints of this template parameter.
+ /// This will either be a vector of size 1 containing the immediately-declared
+ /// constraint introduced by the placeholder type, or an empty vector.
+ ///
+ /// Use this instead of getPlaceholderImmediatelyDeclaredConstraint for
+ /// concepts APIs that accept an ArrayRef of constraint expressions.
+ void getAssociatedConstraints(llvm::SmallVectorImpl<const Expr *> &AC) const {
+ if (TypeConstraint *TC = getPlaceholderTypeConstraint())
+ AC.push_back(TC->getImmediatelyDeclaredConstraint());
+ }
+
// Implement isa/cast/dyncast/etc.
static bool classof(const Decl *D) { return classofKind(D->getKind()); }
static bool classofKind(Kind K) { return K == NonTypeTemplateParm; }
@@ -3086,6 +3232,10 @@ class ConceptDecl : public TemplateDecl, public Mergeable<ConceptDecl> {
ConstraintExpr->getEndLoc());
}
+ bool isTypeConcept() const {
+ return isa<TemplateTypeParmDecl>(getTemplateParameters()->getParam(0));
+ }
+
// Implement isa/cast/dyncast/etc.
static bool classof(const Decl *D) { return classofKind(D->getKind()); }
static bool classofKind(Kind K) { return K == Concept; }
diff --git a/clang/include/clang/AST/ExprCXX.h b/clang/include/clang/AST/ExprCXX.h
index 1a16aa7aacec..2c29409e0ca5 100644
--- a/clang/include/clang/AST/ExprCXX.h
+++ b/clang/include/clang/AST/ExprCXX.h
@@ -4841,7 +4841,7 @@ class BuiltinBitCastExpr final
///
/// According to C++2a [expr.prim.id]p3 an id-expression that denotes the
/// specialization of a concept results in a prvalue of type bool.
-class ConceptSpecializationExpr final : public Expr,
+class ConceptSpecializationExpr final : public Expr, public ConceptReference,
private llvm::TrailingObjects<ConceptSpecializationExpr,
TemplateArgument> {
friend class ASTStmtReader;
@@ -4850,30 +4850,6 @@ class ConceptSpecializationExpr final : public Expr,
using SubstitutionDiagnostic = std::pair<SourceLocation, std::string>;
protected:
-
- // \brief The optional nested name specifier used when naming the concept.
- NestedNameSpecifierLoc NestedNameSpec;
-
- /// \brief The location of the template keyword, if specified when naming the
- /// concept.
- SourceLocation TemplateKWLoc;
-
- /// \brief The location of the concept name in the expression.
- SourceLocation ConceptNameLoc;
-
- /// \brief The declaration found by name lookup when the expression was
- /// created.
- /// Can
diff er from NamedConcept when, for example, the concept was found
- /// through a UsingShadowDecl.
- NamedDecl *FoundDecl;
-
- /// \brief The concept named.
- ConceptDecl *NamedConcept;
-
- /// \brief The template argument list source info used to specialize the
- /// concept.
- const ASTTemplateArgumentListInfo *ArgsAsWritten = nullptr;
-
/// \brief The number of template arguments in the tail-allocated list of
/// converted template arguments.
unsigned NumTemplateArgs;
@@ -4883,10 +4859,10 @@ class ConceptSpecializationExpr final : public Expr,
/// ignored.
ASTConstraintSatisfaction *Satisfaction;
- ConceptSpecializationExpr(ASTContext &C, NestedNameSpecifierLoc NNS,
+ ConceptSpecializationExpr(const ASTContext &C, NestedNameSpecifierLoc NNS,
SourceLocation TemplateKWLoc,
- SourceLocation ConceptNameLoc, NamedDecl *FoundDecl,
- ConceptDecl *NamedConcept,
+ DeclarationNameInfo ConceptNameInfo,
+ NamedDecl *FoundDecl, ConceptDecl *NamedConcept,
const ASTTemplateArgumentListInfo *ArgsAsWritten,
ArrayRef<TemplateArgument> ConvertedArgs,
const ConstraintSatisfaction *Satisfaction);
@@ -4896,8 +4872,8 @@ class ConceptSpecializationExpr final : public Expr,
public:
static ConceptSpecializationExpr *
- Create(ASTContext &C, NestedNameSpecifierLoc NNS,
- SourceLocation TemplateKWLoc, SourceLocation ConceptNameLoc,
+ Create(const ASTContext &C, NestedNameSpecifierLoc NNS,
+ SourceLocation TemplateKWLoc, DeclarationNameInfo ConceptNameInfo,
NamedDecl *FoundDecl, ConceptDecl *NamedConcept,
const ASTTemplateArgumentListInfo *ArgsAsWritten,
ArrayRef<TemplateArgument> ConvertedArgs,
@@ -4906,30 +4882,13 @@ class ConceptSpecializationExpr final : public Expr,
static ConceptSpecializationExpr *
Create(ASTContext &C, EmptyShell Empty, unsigned NumTemplateArgs);
- const NestedNameSpecifierLoc &getNestedNameSpecifierLoc() const {
- return NestedNameSpec;
- }
-
- NamedDecl *getFoundDecl() const {
- return FoundDecl;
- }
-
- ConceptDecl *getNamedConcept() const {
- return NamedConcept;
- }
-
ArrayRef<TemplateArgument> getTemplateArguments() const {
return ArrayRef<TemplateArgument>(getTrailingObjects<TemplateArgument>(),
NumTemplateArgs);
}
- const ASTTemplateArgumentListInfo *getTemplateArgsAsWritten() const {
- return ArgsAsWritten;
- }
-
/// \brief Set new template arguments for this concept specialization.
- void setTemplateArguments(const ASTTemplateArgumentListInfo *ArgsAsWritten,
- ArrayRef<TemplateArgument> Converted);
+ void setTemplateArguments(ArrayRef<TemplateArgument> Converted);
/// \brief Whether or not the concept with the given arguments was satisfied
/// when the expression was created.
@@ -4949,15 +4908,14 @@ class ConceptSpecializationExpr final : public Expr,
return *Satisfaction;
}
- SourceLocation getConceptNameLoc() const { return ConceptNameLoc; }
-
- SourceLocation getTemplateKWLoc() const { return TemplateKWLoc; }
-
static bool classof(const Stmt *T) {
return T->getStmtClass() == ConceptSpecializationExprClass;
}
- SourceLocation getBeginLoc() const LLVM_READONLY { return ConceptNameLoc; }
+ SourceLocation getBeginLoc() const LLVM_READONLY {
+ return ConceptName.getBeginLoc();
+ }
+
SourceLocation getEndLoc() const LLVM_READONLY {
return ArgsAsWritten->RAngleLoc;
}
diff --git a/clang/include/clang/AST/RecursiveASTVisitor.h b/clang/include/clang/AST/RecursiveASTVisitor.h
index 144ef221d692..f8ab8e451d8c 100644
--- a/clang/include/clang/AST/RecursiveASTVisitor.h
+++ b/clang/include/clang/AST/RecursiveASTVisitor.h
@@ -304,6 +304,11 @@ template <typename Derived> class RecursiveASTVisitor {
bool TraverseSynOrSemInitListExpr(InitListExpr *S,
DataRecursionQueue *Queue = nullptr);
+ /// Recursively visit a reference to a concept with potential arguments.
+ ///
+ /// \returns false if the visitation was terminated early, true otherwise.
+ bool TraverseConceptReference(const ConceptReference &C);
+
// ---- Methods on Attrs ----
// Visit an attribute.
@@ -1773,9 +1778,8 @@ DEF_TRAVERSE_DECL(TemplateTemplateParmDecl, {
// D is the "T" in something like
// template <template <typename> class T> class container { };
TRY_TO(TraverseDecl(D->getTemplatedDecl()));
- if (D->hasDefaultArgument() && !D->defaultArgumentWasInherited()) {
+ if (D->hasDefaultArgument() && !D->defaultArgumentWasInherited())
TRY_TO(TraverseTemplateArgumentLoc(D->getDefaultArgument()));
- }
TRY_TO(TraverseTemplateParameterListHelper(D->getTemplateParameters()));
})
@@ -1787,6 +1791,8 @@ DEF_TRAVERSE_DECL(TemplateTypeParmDecl, {
// D is the "T" in something like "template<typename T> class vector;"
if (D->getTypeForDecl())
TRY_TO(TraverseType(QualType(D->getTypeForDecl(), 0)));
+ if (const auto *TC = D->getTypeConstraint())
+ TRY_TO(TraverseConceptReference(*TC));
if (D->hasDefaultArgument() && !D->defaultArgumentWasInherited())
TRY_TO(TraverseTypeLoc(D->getDefaultArgumentInfo()->getTypeLoc()));
})
@@ -2330,6 +2336,18 @@ bool RecursiveASTVisitor<Derived>::TraverseSynOrSemInitListExpr(
return true;
}
+template<typename Derived>
+bool RecursiveASTVisitor<Derived>::TraverseConceptReference(
+ const ConceptReference &C) {
+ TRY_TO(TraverseNestedNameSpecifierLoc(C.getNestedNameSpecifierLoc()));
+ TRY_TO(TraverseDeclarationNameInfo(C.getConceptNameInfo()));
+ if (C.hasExplicitTemplateArgs())
+ TRY_TO(TraverseTemplateArgumentLocsHelper(
+ C.getTemplateArgsAsWritten()->getTemplateArgs(),
+ C.getTemplateArgsAsWritten()->NumTemplateArgs));
+ return true;
+}
+
// If shouldVisitImplicitCode() returns false, this method traverses only the
// syntactic form of InitListExpr.
// If shouldVisitImplicitCode() return true, this method is called once for
@@ -2688,9 +2706,7 @@ DEF_TRAVERSE_STMT(CoyieldExpr, {
})
DEF_TRAVERSE_STMT(ConceptSpecializationExpr, {
- TRY_TO(TraverseTemplateArgumentLocsHelper(
- S->getTemplateArgsAsWritten()->getTemplateArgs(),
- S->getTemplateArgsAsWritten()->NumTemplateArgs));
+ TRY_TO(TraverseConceptReference(*S));
})
// These literals (all of them) do not need any action.
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index d27691d2112d..7d8231d140e4 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -2612,6 +2612,17 @@ def note_ambiguous_atomic_constraints_similar_expression : Note<
def err_template_
diff erent_requires_clause : Error<
"requires clause
diff ers in template redeclaration">;
+def err_template_
diff erent_type_constraint : Error<
+ "type constraint
diff ers in template redeclaration">;
+def err_template_template_parameter_not_at_least_as_constrained : Error<
+ "template template argument %0 is more constrained than template template "
+ "parameter %1">;
+
+def err_type_constraint_non_type_concept : Error<
+ "concept named in type constraint is not a type concept">;
+def err_type_constraint_missing_arguments : Error<
+ "%0 requires more than 1 template argument; provide the remaining arguments "
+ "explicitly to use it here">;
// C++11 char16_t/char32_t
def warn_cxx98_compat_unicode_type : Warning<
@@ -4819,8 +4830,8 @@ def err_unexpanded_parameter_pack : Error<
"size|static assertion|fixed underlying type|enumerator value|"
"using declaration|friend declaration|qualifier|initializer|default argument|"
"non-type template parameter type|exception type|partial specialization|"
- "__if_exists name|__if_not_exists name|lambda|block}0 contains"
- "%plural{0: an|:}1 unexpanded parameter pack"
+ "__if_exists name|__if_not_exists name|lambda|block|type constraint}0 "
+ "contains%plural{0: an|:}1 unexpanded parameter pack"
"%plural{0:|1: %2|2:s %2 and %3|:s %2, %3, ...}1">;
def err_pack_expansion_without_parameter_packs : Error<
diff --git a/clang/include/clang/Basic/TokenKinds.def b/clang/include/clang/Basic/TokenKinds.def
index ae908bbdf3a8..d58b0d987d71 100644
--- a/clang/include/clang/Basic/TokenKinds.def
+++ b/clang/include/clang/Basic/TokenKinds.def
@@ -729,7 +729,9 @@ ANNOTATION(typename) // annotation for a C typedef name, a C++ (possibly
// template-id that names a type ("std::vector<int>")
ANNOTATION(template_id) // annotation for a C++ template-id that names a
// function template specialization (not a type),
- // e.g., "std::swap<int>"
+ // e.g., "std::swap<int>", or a type-constraint (which
+ // might not have explicit template arguments),
+ // e.g. "C", "C<int>".
ANNOTATION(non_type) // annotation for a single non-type declaration
ANNOTATION(non_type_undeclared) // annotation for an undeclared identifier that
// was assumed to be an ADL-only function name
diff --git a/clang/include/clang/Parse/Parser.h b/clang/include/clang/Parse/Parser.h
index eb2f4152e72c..e320c9647818 100644
--- a/clang/include/clang/Parse/Parser.h
+++ b/clang/include/clang/Parse/Parser.h
@@ -3064,11 +3064,15 @@ class Parser : public CodeCompletionHandler {
SourceLocation &RAngleLoc);
bool ParseTemplateParameterList(unsigned Depth,
SmallVectorImpl<NamedDecl*> &TemplateParams);
- bool isStartOfTemplateTypeParameter();
+ bool isStartOfTemplateTypeParameter(bool &ScopeError);
NamedDecl *ParseTemplateParameter(unsigned Depth, unsigned Position);
NamedDecl *ParseTypeParameter(unsigned Depth, unsigned Position);
NamedDecl *ParseTemplateTemplateParameter(unsigned Depth, unsigned Position);
NamedDecl *ParseNonTypeTemplateParameter(unsigned Depth, unsigned Position);
+ bool isTypeConstraintAnnotation();
+ bool TryAnnotateTypeConstraint(CXXScopeSpec &SS);
+ NamedDecl *
+ ParseConstrainedTemplateTypeParameter(unsigned Depth, unsigned Position);
void DiagnoseMisplacedEllipsis(SourceLocation EllipsisLoc,
SourceLocation CorrectLoc,
bool AlreadyHasEllipsis,
@@ -3090,7 +3094,8 @@ class Parser : public CodeCompletionHandler {
CXXScopeSpec &SS,
SourceLocation TemplateKWLoc,
UnqualifiedId &TemplateName,
- bool AllowTypeAnnotation = true);
+ bool AllowTypeAnnotation = true,
+ bool TypeConstraint = false);
void AnnotateTemplateIdTokenAsType(bool IsClassName = false);
bool ParseTemplateArgumentList(TemplateArgList &TemplateArgs);
ParsedTemplateArgument ParseTemplateTemplateArgument();
diff --git a/clang/include/clang/Sema/ParsedTemplate.h b/clang/include/clang/Sema/ParsedTemplate.h
index 2eed301e8aeb..0874905b38a5 100644
--- a/clang/include/clang/Sema/ParsedTemplate.h
+++ b/clang/include/clang/Sema/ParsedTemplate.h
@@ -144,6 +144,10 @@ namespace clang {
/// expressions, or template names, and the source locations for important
/// tokens. All of the information about template arguments is allocated
/// directly after this structure.
+ /// A template-id annotation token can also be generated by a type-constraint
+ /// construct with no explicit template arguments, e.g. "template<C T>" would
+ /// annotate C as a TemplateIdAnnotation with no template arguments (the angle
+ /// locations would be invalid in this case).
struct TemplateIdAnnotation final
: private llvm::TrailingObjects<TemplateIdAnnotation,
ParsedTemplateArgument> {
diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index 5aea6102c0e6..22063cbe965e 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -5923,6 +5923,8 @@ class Sema final {
///
/// \param OnlyNamespace If true, only considers namespaces in lookup.
///
+ /// \param SuppressDiagnostic If true, suppress diagnostic on error.
+ ///
/// \returns true if an error occurred, false otherwise.
bool ActOnCXXNestedNameSpecifier(Scope *S,
NestedNameSpecInfo &IdInfo,
@@ -6263,12 +6265,6 @@ class Sema final {
bool CheckConstraintSatisfaction(const Expr *ConstraintExpr,
ConstraintSatisfaction &Satisfaction);
- /// Check that the associated constraints of a template declaration match the
- /// associated constraints of an older declaration of which it is a
- /// redeclaration.
- bool CheckRedeclarationConstraintMatch(TemplateParameterList *Old,
- TemplateParameterList *New);
-
/// \brief Ensure that the given template arguments satisfy the constraints
/// associated with the given template, emitting a diagnostic if they do not.
///
@@ -6301,9 +6297,8 @@ class Sema final {
void DiagnoseUnsatisfiedIllFormedConstraint(SourceLocation DiagnosticLocation,
StringRef Diagnostic);
- void
- DiagnoseRedeclarationConstraintMismatch(const TemplateParameterList *Old,
- const TemplateParameterList *New);
+ void DiagnoseRedeclarationConstraintMismatch(SourceLocation Old,
+ SourceLocation New);
// ParseObjCStringLiteral - Parse Objective-C string literals.
ExprResult ParseObjCStringLiteral(SourceLocation *AtLocs,
@@ -6872,13 +6867,24 @@ class Sema final {
TemplateDecl *AdjustDeclIfTemplate(Decl *&Decl);
NamedDecl *ActOnTypeParameter(Scope *S, bool Typename,
- SourceLocation EllipsisLoc,
- SourceLocation KeyLoc,
- IdentifierInfo *ParamName,
- SourceLocation ParamNameLoc,
- unsigned Depth, unsigned Position,
- SourceLocation EqualLoc,
- ParsedType DefaultArg);
+ SourceLocation EllipsisLoc,
+ SourceLocation KeyLoc,
+ IdentifierInfo *ParamName,
+ SourceLocation ParamNameLoc,
+ unsigned Depth, unsigned Position,
+ SourceLocation EqualLoc,
+ ParsedType DefaultArg, bool HasTypeConstraint);
+
+ bool ActOnTypeConstraint(TemplateIdAnnotation *TypeConstraint,
+ TemplateTypeParmDecl *ConstrainedParameter,
+ SourceLocation EllipsisLoc);
+
+ bool AttachTypeConstraint(NestedNameSpecifierLoc NS,
+ DeclarationNameInfo NameInfo,
+ ConceptDecl *NamedConcept,
+ const TemplateArgumentListInfo *TemplateArgs,
+ TemplateTypeParmDecl *ConstrainedParameter,
+ SourceLocation EllipsisLoc);
QualType CheckNonTypeTemplateParameterType(TypeSourceInfo *&TSI,
SourceLocation Loc);
@@ -7000,8 +7006,8 @@ class Sema final {
ExprResult
CheckConceptTemplateId(const CXXScopeSpec &SS,
SourceLocation TemplateKWLoc,
- SourceLocation ConceptNameLoc, NamedDecl *FoundDecl,
- ConceptDecl *NamedConcept,
+ const DeclarationNameInfo &ConceptNameInfo,
+ NamedDecl *FoundDecl, ConceptDecl *NamedConcept,
const TemplateArgumentListInfo *TemplateArgs);
void diagnoseMissingTemplateArguments(TemplateName Name, SourceLocation Loc);
@@ -7160,7 +7166,8 @@ class Sema final {
QualType InstantiatedParamType, Expr *Arg,
TemplateArgument &Converted,
CheckTemplateArgumentKind CTAK = CTAK_Specified);
- bool CheckTemplateTemplateArgument(TemplateParameterList *Params,
+ bool CheckTemplateTemplateArgument(TemplateTemplateParmDecl *Param,
+ TemplateParameterList *Params,
TemplateArgumentLoc &Arg);
ExprResult
@@ -7353,7 +7360,10 @@ class Sema final {
UPPC_Lambda,
/// Block expression,
- UPPC_Block
+ UPPC_Block,
+
+ /// A type constraint,
+ UPPC_TypeConstraint
};
/// Diagnose unexpanded parameter packs.
@@ -7846,7 +7856,7 @@ class Sema final {
sema::TemplateDeductionInfo &Info);
bool isTemplateTemplateParameterAtLeastAsSpecializedAs(
- TemplateParameterList *P, TemplateDecl *AArg, SourceLocation Loc);
+ TemplateParameterList *PParam, TemplateDecl *AArg, SourceLocation Loc);
void MarkUsedTemplateParameters(const Expr *E, bool OnlyDeduced,
unsigned Depth, llvm::SmallBitVector &Used);
diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp
index 26e76d450424..a51429264dbe 100644
--- a/clang/lib/AST/ASTContext.cpp
+++ b/clang/lib/AST/ASTContext.cpp
@@ -661,8 +661,9 @@ comments::FullComment *ASTContext::getCommentForDecl(
return FC;
}
-void
+void
ASTContext::CanonicalTemplateTemplateParm::Profile(llvm::FoldingSetNodeID &ID,
+ const ASTContext &C,
TemplateTemplateParmDecl *Parm) {
ID.AddInteger(Parm->getDepth());
ID.AddInteger(Parm->getPosition());
@@ -676,6 +677,16 @@ ASTContext::CanonicalTemplateTemplateParm::Profile(llvm::FoldingSetNodeID &ID,
if (const auto *TTP = dyn_cast<TemplateTypeParmDecl>(*P)) {
ID.AddInteger(0);
ID.AddBoolean(TTP->isParameterPack());
+ const TypeConstraint *TC = TTP->getTypeConstraint();
+ ID.AddBoolean(TC != nullptr);
+ if (TC)
+ TC->getImmediatelyDeclaredConstraint()->Profile(ID, C,
+ /*Canonical=*/true);
+ if (TTP->isExpandedParameterPack()) {
+ ID.AddBoolean(true);
+ ID.AddInteger(TTP->getNumExpansionParameters());
+ } else
+ ID.AddBoolean(false);
continue;
}
@@ -697,8 +708,12 @@ ASTContext::CanonicalTemplateTemplateParm::Profile(llvm::FoldingSetNodeID &ID,
auto *TTP = cast<TemplateTemplateParmDecl>(*P);
ID.AddInteger(2);
- Profile(ID, TTP);
+ Profile(ID, C, TTP);
}
+ Expr *RequiresClause = Parm->getTemplateParameters()->getRequiresClause();
+ ID.AddBoolean(RequiresClause != nullptr);
+ if (RequiresClause)
+ RequiresClause->Profile(ID, C, /*Canonical=*/true);
}
TemplateTemplateParmDecl *
@@ -706,7 +721,7 @@ ASTContext::getCanonicalTemplateTemplateParmDecl(
TemplateTemplateParmDecl *TTP) const {
// Check if we already have a canonical template template parameter.
llvm::FoldingSetNodeID ID;
- CanonicalTemplateTemplateParm::Profile(ID, TTP);
+ CanonicalTemplateTemplateParm::Profile(ID, *this, TTP);
void *InsertPos = nullptr;
CanonicalTemplateTemplateParm *Canonical
= CanonTemplateTemplateParms.FindNodeOrInsertPos(ID, InsertPos);
@@ -720,15 +735,79 @@ ASTContext::getCanonicalTemplateTemplateParmDecl(
for (TemplateParameterList::const_iterator P = Params->begin(),
PEnd = Params->end();
P != PEnd; ++P) {
- if (const auto *TTP = dyn_cast<TemplateTypeParmDecl>(*P))
- CanonParams.push_back(
- TemplateTypeParmDecl::Create(*this, getTranslationUnitDecl(),
- SourceLocation(),
- SourceLocation(),
- TTP->getDepth(),
- TTP->getIndex(), nullptr, false,
- TTP->isParameterPack()));
- else if (const auto *NTTP = dyn_cast<NonTypeTemplateParmDecl>(*P)) {
+ if (const auto *TTP = dyn_cast<TemplateTypeParmDecl>(*P)) {
+ TemplateTypeParmDecl *NewTTP = TemplateTypeParmDecl::Create(*this,
+ getTranslationUnitDecl(), SourceLocation(), SourceLocation(),
+ TTP->getDepth(), TTP->getIndex(), nullptr, false,
+ TTP->isParameterPack(), TTP->hasTypeConstraint(),
+ TTP->isExpandedParameterPack() ?
+ llvm::Optional<unsigned>(TTP->getNumExpansionParameters()) : None);
+ if (const auto *TC = TTP->getTypeConstraint()) {
+ // This is a bit ugly - we need to form a new immediately-declared
+ // constraint that references the new parameter; this would ideally
+ // require semantic analysis (e.g. template<C T> struct S {}; - the
+ // converted arguments of C<T> could be an argument pack if C is
+ // declared as template<typename... T> concept C = ...).
+ // We don't have semantic analysis here so we dig deep into the
+ // ready-made constraint expr and change the thing manually.
+ Expr *IDC = TC->getImmediatelyDeclaredConstraint();
+ ConceptSpecializationExpr *CSE;
+ if (const auto *Fold = dyn_cast<CXXFoldExpr>(IDC))
+ CSE = cast<ConceptSpecializationExpr>(Fold->getLHS());
+ else
+ CSE = cast<ConceptSpecializationExpr>(IDC);
+ ArrayRef<TemplateArgument> OldConverted = CSE->getTemplateArguments();
+ SmallVector<TemplateArgument, 3> NewConverted;
+ NewConverted.reserve(OldConverted.size());
+
+ QualType ParamAsArgument(NewTTP->getTypeForDecl(), 0);
+ if (OldConverted.front().getKind() == TemplateArgument::Pack) {
+ // The case:
+ // template<typename... T> concept C = true;
+ // template<C<int> T> struct S; -> constraint is C<{T, int}>
+ NewConverted.push_back(ParamAsArgument);
+ for (auto &Arg : OldConverted.front().pack_elements().drop_front(1))
+ NewConverted.push_back(Arg);
+ TemplateArgument NewPack(NewConverted);
+
+ NewConverted.clear();
+ NewConverted.push_back(NewPack);
+ assert(OldConverted.size() == 1 &&
+ "Template parameter pack should be the last parameter");
+ } else {
+ assert(OldConverted.front().getKind() == TemplateArgument::Type &&
+ "Unexpected first argument kind for immediately-declared "
+ "constraint");
+ NewConverted.push_back(ParamAsArgument);
+ for (auto &Arg : OldConverted.drop_front(1))
+ NewConverted.push_back(Arg);
+ }
+ Expr *NewIDC = ConceptSpecializationExpr::Create(*this,
+ NestedNameSpecifierLoc(), /*TemplateKWLoc=*/SourceLocation(),
+ CSE->getConceptNameInfo(), /*FoundDecl=*/CSE->getNamedConcept(),
+ CSE->getNamedConcept(),
+ // Actually canonicalizing a TemplateArgumentLoc is
diff icult so we
+ // simply omit the ArgsAsWritten
+ /*ArgsAsWritten=*/nullptr, NewConverted, nullptr);
+
+ if (auto *OrigFold = dyn_cast<CXXFoldExpr>(IDC))
+ NewIDC = new (*this) CXXFoldExpr(OrigFold->getType(),
+ SourceLocation(), NewIDC,
+ BinaryOperatorKind::BO_LAnd,
+ SourceLocation(), /*RHS=*/nullptr,
+ SourceLocation(),
+ /*NumExpansions=*/None);
+
+ NewTTP->setTypeConstraint(
+ NestedNameSpecifierLoc(),
+ DeclarationNameInfo(TC->getNamedConcept()->getDeclName(),
+ SourceLocation()), /*FoundDecl=*/nullptr,
+ // Actually canonicalizing a TemplateArgumentLoc is
diff icult so we
+ // simply omit the ArgsAsWritten
+ CSE->getNamedConcept(), /*ArgsAsWritten=*/nullptr, NewIDC);
+ }
+ CanonParams.push_back(NewTTP);
+ } else if (const auto *NTTP = dyn_cast<NonTypeTemplateParmDecl>(*P)) {
QualType T = getCanonicalType(NTTP->getType());
TypeSourceInfo *TInfo = getTrivialTypeSourceInfo(T);
NonTypeTemplateParmDecl *Param;
@@ -767,9 +846,9 @@ ASTContext::getCanonicalTemplateTemplateParmDecl(
cast<TemplateTemplateParmDecl>(*P)));
}
- assert(!TTP->getTemplateParameters()->getRequiresClause() &&
- "Unexpected requires-clause on template template-parameter");
- Expr *const CanonRequiresClause = nullptr;
+ Expr *CanonRequiresClause = nullptr;
+ if (Expr *RequiresClause = TTP->getTemplateParameters()->getRequiresClause())
+ CanonRequiresClause = RequiresClause;
TemplateTemplateParmDecl *CanonTTP
= TemplateTemplateParmDecl::Create(*this, getTranslationUnitDecl(),
@@ -865,7 +944,8 @@ ASTContext::ASTContext(LangOptions &LOpts, SourceManager &SM,
: ConstantArrayTypes(this_()), FunctionProtoTypes(this_()),
TemplateSpecializationTypes(this_()),
DependentTemplateSpecializationTypes(this_()),
- SubstTemplateTemplateParmPacks(this_()), SourceMgr(SM), LangOpts(LOpts),
+ SubstTemplateTemplateParmPacks(this_()),
+ CanonTemplateTemplateParms(this_()), SourceMgr(SM), LangOpts(LOpts),
SanitizerBL(new SanitizerBlacklist(LangOpts.SanitizerBlacklistFiles, SM)),
XRayFilter(new XRayFunctionFilter(LangOpts.XRayAlwaysInstrumentFiles,
LangOpts.XRayNeverInstrumentFiles,
diff --git a/clang/lib/AST/ASTImporter.cpp b/clang/lib/AST/ASTImporter.cpp
index 8eb58b32ae17..22fb67478c96 100644
--- a/clang/lib/AST/ASTImporter.cpp
+++ b/clang/lib/AST/ASTImporter.cpp
@@ -5097,7 +5097,7 @@ ASTNodeImporter::VisitTemplateTypeParmDecl(TemplateTypeParmDecl *D) {
// context. This context will be fixed when the actual template declaration
// is created.
- // FIXME: Import default argument.
+ // FIXME: Import default argument and constraint expression.
ExpectedSLoc BeginLocOrErr = import(D->getBeginLoc());
if (!BeginLocOrErr)
@@ -5108,12 +5108,47 @@ ASTNodeImporter::VisitTemplateTypeParmDecl(TemplateTypeParmDecl *D) {
return LocationOrErr.takeError();
TemplateTypeParmDecl *ToD = nullptr;
- (void)GetImportedOrCreateDecl(
+ if (GetImportedOrCreateDecl(
ToD, D, Importer.getToContext(),
Importer.getToContext().getTranslationUnitDecl(),
*BeginLocOrErr, *LocationOrErr,
D->getDepth(), D->getIndex(), Importer.Import(D->getIdentifier()),
- D->wasDeclaredWithTypename(), D->isParameterPack());
+ D->wasDeclaredWithTypename(), D->isParameterPack(),
+ D->hasTypeConstraint()))
+ return ToD;
+
+ // Import the type-constraint
+ if (const TypeConstraint *TC = D->getTypeConstraint()) {
+ NestedNameSpecifierLoc ToNNS;
+ DeclarationName ToName;
+ SourceLocation ToNameLoc;
+ NamedDecl *ToFoundDecl;
+ ConceptDecl *ToNamedConcept;
+ Expr *ToIDC;
+ if (auto Imp = importSeq(TC->getNestedNameSpecifierLoc(),
+ TC->getConceptNameInfo().getName(), TC->getConceptNameInfo().getLoc(),
+ TC->getFoundDecl(), TC->getNamedConcept(),
+ TC->getImmediatelyDeclaredConstraint()))
+ std::tie(ToNNS, ToName, ToNameLoc, ToFoundDecl, ToNamedConcept,
+ ToIDC) = *Imp;
+ else
+ return Imp.takeError();
+
+ TemplateArgumentListInfo ToTAInfo;
+ const auto *ASTTemplateArgs = TC->getTemplateArgsAsWritten();
+ if (ASTTemplateArgs)
+ if (Error Err = ImportTemplateArgumentListInfo(*ASTTemplateArgs,
+ ToTAInfo))
+ return std::move(Err);
+
+ ToD->setTypeConstraint(ToNNS, DeclarationNameInfo(ToName, ToNameLoc),
+ ToFoundDecl, ToNamedConcept,
+ ASTTemplateArgs ?
+ ASTTemplateArgumentListInfo::Create(Importer.getToContext(),
+ ToTAInfo) : nullptr,
+ ToIDC);
+ }
+
return ToD;
}
diff --git a/clang/lib/AST/DeclPrinter.cpp b/clang/lib/AST/DeclPrinter.cpp
index 68cc8b7a0eb9..4cedcbed0644 100644
--- a/clang/lib/AST/DeclPrinter.cpp
+++ b/clang/lib/AST/DeclPrinter.cpp
@@ -1053,7 +1053,9 @@ void DeclPrinter::printTemplateParameters(const TemplateParameterList *Params,
if (auto TTP = dyn_cast<TemplateTypeParmDecl>(Param)) {
- if (TTP->wasDeclaredWithTypename())
+ if (const TypeConstraint *TC = TTP->getTypeConstraint())
+ TC->print(Out, Policy);
+ else if (TTP->wasDeclaredWithTypename())
Out << "typename";
else
Out << "class";
diff --git a/clang/lib/AST/DeclTemplate.cpp b/clang/lib/AST/DeclTemplate.cpp
index 59fa7faad927..95a2e26e0df8 100755
--- a/clang/lib/AST/DeclTemplate.cpp
+++ b/clang/lib/AST/DeclTemplate.cpp
@@ -43,35 +43,46 @@ using namespace clang;
// TemplateParameterList Implementation
//===----------------------------------------------------------------------===//
-TemplateParameterList::TemplateParameterList(SourceLocation TemplateLoc,
+
+TemplateParameterList::TemplateParameterList(const ASTContext& C,
+ SourceLocation TemplateLoc,
SourceLocation LAngleLoc,
ArrayRef<NamedDecl *> Params,
SourceLocation RAngleLoc,
Expr *RequiresClause)
: TemplateLoc(TemplateLoc), LAngleLoc(LAngleLoc), RAngleLoc(RAngleLoc),
NumParams(Params.size()), ContainsUnexpandedParameterPack(false),
- HasRequiresClause(static_cast<bool>(RequiresClause)) {
+ HasRequiresClause(RequiresClause != nullptr),
+ HasConstrainedParameters(false) {
for (unsigned Idx = 0; Idx < NumParams; ++Idx) {
NamedDecl *P = Params[Idx];
begin()[Idx] = P;
- if (!P->isTemplateParameterPack()) {
- if (const auto *NTTP = dyn_cast<NonTypeTemplateParmDecl>(P))
- if (NTTP->getType()->containsUnexpandedParameterPack())
- ContainsUnexpandedParameterPack = true;
-
- if (const auto *TTP = dyn_cast<TemplateTemplateParmDecl>(P))
- if (TTP->getTemplateParameters()->containsUnexpandedParameterPack())
- ContainsUnexpandedParameterPack = true;
-
- // FIXME: If a default argument contains an unexpanded parameter pack, the
- // template parameter list does too.
+ bool IsPack = P->isTemplateParameterPack();
+ if (const auto *NTTP = dyn_cast<NonTypeTemplateParmDecl>(P)) {
+ if (!IsPack && NTTP->getType()->containsUnexpandedParameterPack())
+ ContainsUnexpandedParameterPack = true;
+ if (NTTP->hasPlaceholderTypeConstraint())
+ HasConstrainedParameters = true;
+ } else if (const auto *TTP = dyn_cast<TemplateTemplateParmDecl>(P)) {
+ if (!IsPack &&
+ TTP->getTemplateParameters()->containsUnexpandedParameterPack())
+ ContainsUnexpandedParameterPack = true;
+ } else if (const TypeConstraint *TC =
+ cast<TemplateTypeParmDecl>(P)->getTypeConstraint()) {
+ if (TC->getImmediatelyDeclaredConstraint()
+ ->containsUnexpandedParameterPack())
+ ContainsUnexpandedParameterPack = true;
+ HasConstrainedParameters = true;
}
+ // FIXME: If a default argument contains an unexpanded parameter pack, the
+ // template parameter list does too.
}
- if (RequiresClause) {
- *getTrailingObjects<Expr *>() = RequiresClause;
+
+ if (HasRequiresClause) {
if (RequiresClause->containsUnexpandedParameterPack())
ContainsUnexpandedParameterPack = true;
+ *getTrailingObjects<Expr *>() = RequiresClause;
}
}
@@ -83,7 +94,7 @@ TemplateParameterList::Create(const ASTContext &C, SourceLocation TemplateLoc,
void *Mem = C.Allocate(totalSizeToAlloc<NamedDecl *, Expr *>(
Params.size(), RequiresClause ? 1u : 0u),
alignof(TemplateParameterList));
- return new (Mem) TemplateParameterList(TemplateLoc, LAngleLoc, Params,
+ return new (Mem) TemplateParameterList(C, TemplateLoc, LAngleLoc, Params,
RAngleLoc, RequiresClause);
}
@@ -91,11 +102,23 @@ unsigned TemplateParameterList::getMinRequiredArguments() const {
unsigned NumRequiredArgs = 0;
for (const NamedDecl *P : asArray()) {
if (P->isTemplateParameterPack()) {
- if (const auto *NTTP = dyn_cast<NonTypeTemplateParmDecl>(P))
+ if (const auto *NTTP = dyn_cast<NonTypeTemplateParmDecl>(P)) {
if (NTTP->isExpandedParameterPack()) {
NumRequiredArgs += NTTP->getNumExpansionTypes();
continue;
}
+ } else if (const auto *TTP = dyn_cast<TemplateTypeParmDecl>(P)) {
+ if (TTP->isExpandedParameterPack()) {
+ NumRequiredArgs += TTP->getNumExpansionParameters();
+ continue;
+ }
+ } else {
+ const auto *TP = cast<TemplateTemplateParmDecl>(P);
+ if (TP->isExpandedParameterPack()) {
+ NumRequiredArgs += TP->getNumExpansionTemplateParameters();
+ continue;
+ }
+ }
break;
}
@@ -140,14 +163,17 @@ static void AdoptTemplateParameterList(TemplateParameterList *Params,
void TemplateParameterList::
getAssociatedConstraints(llvm::SmallVectorImpl<const Expr *> &AC) const {
- // TODO: Concepts: Collect immediately-introduced constraints.
+ if (HasConstrainedParameters)
+ for (const NamedDecl *Param : *this)
+ if (const auto *TTP = dyn_cast<TemplateTypeParmDecl>(Param))
+ if (const auto *TC = TTP->getTypeConstraint())
+ AC.push_back(TC->getImmediatelyDeclaredConstraint());
if (HasRequiresClause)
AC.push_back(getRequiresClause());
}
bool TemplateParameterList::hasAssociatedConstraints() const {
- // TODO: Concepts: Regard immediately-introduced constraints.
- return HasRequiresClause;
+ return HasRequiresClause || HasConstrainedParameters;
}
namespace clang {
@@ -558,9 +584,14 @@ TemplateTypeParmDecl *
TemplateTypeParmDecl::Create(const ASTContext &C, DeclContext *DC,
SourceLocation KeyLoc, SourceLocation NameLoc,
unsigned D, unsigned P, IdentifierInfo *Id,
- bool Typename, bool ParameterPack) {
+ bool Typename, bool ParameterPack,
+ bool HasTypeConstraint,
+ Optional<unsigned> NumExpanded) {
auto *TTPDecl =
- new (C, DC) TemplateTypeParmDecl(DC, KeyLoc, NameLoc, Id, Typename);
+ new (C, DC,
+ additionalSizeToAlloc<TypeConstraint>(HasTypeConstraint ? 1 : 0))
+ TemplateTypeParmDecl(DC, KeyLoc, NameLoc, Id, Typename,
+ HasTypeConstraint, NumExpanded);
QualType TTPType = C.getTemplateTypeParmType(D, P, ParameterPack, TTPDecl);
TTPDecl->setTypeForDecl(TTPType.getTypePtr());
return TTPDecl;
@@ -569,7 +600,17 @@ TemplateTypeParmDecl::Create(const ASTContext &C, DeclContext *DC,
TemplateTypeParmDecl *
TemplateTypeParmDecl::CreateDeserialized(const ASTContext &C, unsigned ID) {
return new (C, ID) TemplateTypeParmDecl(nullptr, SourceLocation(),
- SourceLocation(), nullptr, false);
+ SourceLocation(), nullptr, false,
+ false, None);
+}
+
+TemplateTypeParmDecl *
+TemplateTypeParmDecl::CreateDeserialized(const ASTContext &C, unsigned ID,
+ bool HasTypeConstraint) {
+ return new (C, ID,
+ additionalSizeToAlloc<TypeConstraint>(HasTypeConstraint ? 1 : 0))
+ TemplateTypeParmDecl(nullptr, SourceLocation(), SourceLocation(),
+ nullptr, false, HasTypeConstraint, None);
}
SourceLocation TemplateTypeParmDecl::getDefaultArgumentLoc() const {
@@ -602,6 +643,20 @@ bool TemplateTypeParmDecl::isParameterPack() const {
return getTypeForDecl()->castAs<TemplateTypeParmType>()->isParameterPack();
}
+void TemplateTypeParmDecl::setTypeConstraint(NestedNameSpecifierLoc NNS,
+ DeclarationNameInfo NameInfo, NamedDecl *FoundDecl, ConceptDecl *CD,
+ const ASTTemplateArgumentListInfo *ArgsAsWritten,
+ Expr *ImmediatelyDeclaredConstraint) {
+ assert(HasTypeConstraint &&
+ "HasTypeConstraint=true must be passed at construction in order to "
+ "call setTypeConstraint");
+ assert(!TypeConstraintInitialized &&
+ "TypeConstraint was already initialized!");
+ new (getTrailingObjects<TypeConstraint>()) TypeConstraint(NNS, NameInfo,
+ FoundDecl, CD, ArgsAsWritten, ImmediatelyDeclaredConstraint);
+ TypeConstraintInitialized = true;
+}
+
//===----------------------------------------------------------------------===//
// NonTypeTemplateParmDecl Method Implementations
//===----------------------------------------------------------------------===//
@@ -1256,7 +1311,8 @@ createMakeIntegerSeqParameterList(const ASTContext &C, DeclContext *DC) {
// typename T
auto *T = TemplateTypeParmDecl::Create(
C, DC, SourceLocation(), SourceLocation(), /*Depth=*/1, /*Position=*/0,
- /*Id=*/nullptr, /*Typename=*/true, /*ParameterPack=*/false);
+ /*Id=*/nullptr, /*Typename=*/true, /*ParameterPack=*/false,
+ /*HasTypeConstraint=*/false);
T->setImplicit(true);
// T ...Ints
@@ -1281,7 +1337,8 @@ createMakeIntegerSeqParameterList(const ASTContext &C, DeclContext *DC) {
// typename T
auto *TemplateTypeParm = TemplateTypeParmDecl::Create(
C, DC, SourceLocation(), SourceLocation(), /*Depth=*/0, /*Position=*/1,
- /*Id=*/nullptr, /*Typename=*/true, /*ParameterPack=*/false);
+ /*Id=*/nullptr, /*Typename=*/true, /*ParameterPack=*/false,
+ /*HasTypeConstraint=*/false);
TemplateTypeParm->setImplicit(true);
// T N
@@ -1309,7 +1366,8 @@ createTypePackElementParameterList(const ASTContext &C, DeclContext *DC) {
// typename ...T
auto *Ts = TemplateTypeParmDecl::Create(
C, DC, SourceLocation(), SourceLocation(), /*Depth=*/0, /*Position=*/1,
- /*Id=*/nullptr, /*Typename=*/true, /*ParameterPack=*/true);
+ /*Id=*/nullptr, /*Typename=*/true, /*ParameterPack=*/true,
+ /*HasTypeConstraint=*/false);
Ts->setImplicit(true);
// template <std::size_t Index, typename ...T>
@@ -1339,3 +1397,15 @@ BuiltinTemplateDecl::BuiltinTemplateDecl(const ASTContext &C, DeclContext *DC,
: TemplateDecl(BuiltinTemplate, DC, SourceLocation(), Name,
createBuiltinTemplateParameterList(C, DC, BTK)),
BTK(BTK) {}
+
+void TypeConstraint::print(llvm::raw_ostream &OS, PrintingPolicy Policy) const {
+ if (NestedNameSpec)
+ NestedNameSpec.getNestedNameSpecifier()->print(OS, Policy);
+ ConceptName.printName(OS, Policy);
+ if (hasExplicitTemplateArgs()) {
+ OS << "<";
+ for (auto &ArgLoc : ArgsAsWritten->arguments())
+ ArgLoc.getArgument().print(Policy, OS);
+ OS << ">";
+ }
+}
\ No newline at end of file
diff --git a/clang/lib/AST/ExprCXX.cpp b/clang/lib/AST/ExprCXX.cpp
index deb76820afe5..422227d787b1 100644
--- a/clang/lib/AST/ExprCXX.cpp
+++ b/clang/lib/AST/ExprCXX.cpp
@@ -1765,9 +1765,9 @@ CUDAKernelCallExpr *CUDAKernelCallExpr::CreateEmpty(const ASTContext &Ctx,
return new (Mem) CUDAKernelCallExpr(NumArgs, Empty);
}
-ConceptSpecializationExpr::ConceptSpecializationExpr(ASTContext &C,
+ConceptSpecializationExpr::ConceptSpecializationExpr(const ASTContext &C,
NestedNameSpecifierLoc NNS, SourceLocation TemplateKWLoc,
- SourceLocation ConceptNameLoc, NamedDecl *FoundDecl,
+ DeclarationNameInfo ConceptNameInfo, NamedDecl *FoundDecl,
ConceptDecl *NamedConcept, const ASTTemplateArgumentListInfo *ArgsAsWritten,
ArrayRef<TemplateArgument> ConvertedArgs,
const ConstraintSatisfaction *Satisfaction)
@@ -1776,34 +1776,31 @@ ConceptSpecializationExpr::ConceptSpecializationExpr(ASTContext &C,
// All the flags below are set in setTemplateArguments.
/*ValueDependent=*/!Satisfaction, /*InstantiationDependent=*/false,
/*ContainsUnexpandedParameterPacks=*/false),
- NestedNameSpec(NNS), TemplateKWLoc(TemplateKWLoc),
- ConceptNameLoc(ConceptNameLoc), FoundDecl(FoundDecl),
- NamedConcept(NamedConcept), NumTemplateArgs(ConvertedArgs.size()),
+ ConceptReference(NNS, TemplateKWLoc, ConceptNameInfo, FoundDecl,
+ NamedConcept, ArgsAsWritten),
+ NumTemplateArgs(ConvertedArgs.size()),
Satisfaction(Satisfaction ?
ASTConstraintSatisfaction::Create(C, *Satisfaction) :
nullptr) {
- setTemplateArguments(ArgsAsWritten, ConvertedArgs);
+ setTemplateArguments(ConvertedArgs);
}
ConceptSpecializationExpr::ConceptSpecializationExpr(EmptyShell Empty,
unsigned NumTemplateArgs)
- : Expr(ConceptSpecializationExprClass, Empty),
+ : Expr(ConceptSpecializationExprClass, Empty), ConceptReference(),
NumTemplateArgs(NumTemplateArgs) { }
void ConceptSpecializationExpr::setTemplateArguments(
- const ASTTemplateArgumentListInfo *ArgsAsWritten,
ArrayRef<TemplateArgument> Converted) {
assert(Converted.size() == NumTemplateArgs);
- assert(!this->ArgsAsWritten && "setTemplateArguments can only be used once");
- this->ArgsAsWritten = ArgsAsWritten;
std::uninitialized_copy(Converted.begin(), Converted.end(),
getTrailingObjects<TemplateArgument>());
bool IsInstantiationDependent = false;
bool ContainsUnexpandedParameterPack = false;
- for (const TemplateArgumentLoc& LocInfo : ArgsAsWritten->arguments()) {
- if (LocInfo.getArgument().isInstantiationDependent())
+ for (const TemplateArgument& Arg : Converted) {
+ if (Arg.isInstantiationDependent())
IsInstantiationDependent = true;
- if (LocInfo.getArgument().containsUnexpandedParameterPack())
+ if (Arg.containsUnexpandedParameterPack())
ContainsUnexpandedParameterPack = true;
if (ContainsUnexpandedParameterPack && IsInstantiationDependent)
break;
@@ -1821,9 +1818,10 @@ void ConceptSpecializationExpr::setTemplateArguments(
}
ConceptSpecializationExpr *
-ConceptSpecializationExpr::Create(ASTContext &C, NestedNameSpecifierLoc NNS,
+ConceptSpecializationExpr::Create(const ASTContext &C,
+ NestedNameSpecifierLoc NNS,
SourceLocation TemplateKWLoc,
- SourceLocation ConceptNameLoc,
+ DeclarationNameInfo ConceptNameInfo,
NamedDecl *FoundDecl,
ConceptDecl *NamedConcept,
const ASTTemplateArgumentListInfo *ArgsAsWritten,
@@ -1832,7 +1830,7 @@ ConceptSpecializationExpr::Create(ASTContext &C, NestedNameSpecifierLoc NNS,
void *Buffer = C.Allocate(totalSizeToAlloc<TemplateArgument>(
ConvertedArgs.size()));
return new (Buffer) ConceptSpecializationExpr(C, NNS, TemplateKWLoc,
- ConceptNameLoc, FoundDecl,
+ ConceptNameInfo, FoundDecl,
NamedConcept, ArgsAsWritten,
ConvertedArgs, Satisfaction);
}
diff --git a/clang/lib/AST/ODRHash.cpp b/clang/lib/AST/ODRHash.cpp
index 3b89c630b451..27fdca1c4b9c 100644
--- a/clang/lib/AST/ODRHash.cpp
+++ b/clang/lib/AST/ODRHash.cpp
@@ -380,6 +380,11 @@ class ODRDeclVisitor : public ConstDeclVisitor<ODRDeclVisitor> {
}
Hash.AddBoolean(D->isParameterPack());
+ const TypeConstraint *TC = D->getTypeConstraint();
+ Hash.AddBoolean(TC != nullptr);
+ if (TC)
+ AddStmt(TC->getImmediatelyDeclaredConstraint());
+
Inherited::VisitTemplateTypeParmDecl(D);
}
diff --git a/clang/lib/AST/TextNodeDumper.cpp b/clang/lib/AST/TextNodeDumper.cpp
index fa7f4130b761..965ad17fcfa5 100644
--- a/clang/lib/AST/TextNodeDumper.cpp
+++ b/clang/lib/AST/TextNodeDumper.cpp
@@ -1694,7 +1694,16 @@ void TextNodeDumper::VisitBuiltinTemplateDecl(const BuiltinTemplateDecl *D) {
}
void TextNodeDumper::VisitTemplateTypeParmDecl(const TemplateTypeParmDecl *D) {
- if (D->wasDeclaredWithTypename())
+ if (const auto *TC = D->getTypeConstraint()) {
+ OS << " ";
+ dumpBareDeclRef(TC->getNamedConcept());
+ if (TC->getNamedConcept() != TC->getFoundDecl()) {
+ OS << " (";
+ dumpBareDeclRef(TC->getFoundDecl());
+ OS << ")";
+ }
+ Visit(TC->getImmediatelyDeclaredConstraint());
+ } else if (D->wasDeclaredWithTypename())
OS << " typename";
else
OS << " class";
diff --git a/clang/lib/Parse/ParseDecl.cpp b/clang/lib/Parse/ParseDecl.cpp
index c94ccee60287..69a3ed9cbad7 100644
--- a/clang/lib/Parse/ParseDecl.cpp
+++ b/clang/lib/Parse/ParseDecl.cpp
@@ -5121,6 +5121,13 @@ bool Parser::isDeclarationSpecifier(bool DisambiguatingWithExpression) {
return !DisambiguatingWithExpression ||
!isStartOfObjCClassMessageMissingOpenBracket();
+ // placeholder-type-specifier
+ case tok::annot_template_id: {
+ TemplateIdAnnotation *TemplateId = takeTemplateIdAnnotation(Tok);
+ return TemplateId->Kind == TNK_Concept_template &&
+ (NextToken().is(tok::kw_auto) || NextToken().is(tok::kw_decltype));
+ }
+
case tok::kw___declspec:
case tok::kw___cdecl:
case tok::kw___stdcall:
diff --git a/clang/lib/Parse/ParseExprCXX.cpp b/clang/lib/Parse/ParseExprCXX.cpp
index f4ffa08b2a1b..e685d5ea8a9c 100644
--- a/clang/lib/Parse/ParseExprCXX.cpp
+++ b/clang/lib/Parse/ParseExprCXX.cpp
@@ -142,11 +142,16 @@ void Parser::CheckForTemplateAndDigraph(Token &Next, ParsedType ObjectType,
///
/// \param OnlyNamespace If true, only considers namespaces in lookup.
///
+///
/// \returns true if there was an error parsing a scope specifier
-bool Parser::ParseOptionalCXXScopeSpecifier(
- CXXScopeSpec &SS, ParsedType ObjectType, bool EnteringContext,
- bool *MayBePseudoDestructor, bool IsTypename, IdentifierInfo **LastII,
- bool OnlyNamespace, bool InUsingDeclaration) {
+bool Parser::ParseOptionalCXXScopeSpecifier(CXXScopeSpec &SS,
+ ParsedType ObjectType,
+ bool EnteringContext,
+ bool *MayBePseudoDestructor,
+ bool IsTypename,
+ IdentifierInfo **LastII,
+ bool OnlyNamespace,
+ bool InUsingDeclaration) {
assert(getLangOpts().CPlusPlus &&
"Call sites of this function should be guarded by checking for C++");
@@ -476,6 +481,7 @@ bool Parser::ParseOptionalCXXScopeSpecifier(
// nested-name-specifier:
// type-name '<'
if (Next.is(tok::less)) {
+
TemplateTy Template;
UnqualifiedId TemplateName;
TemplateName.setIdentifier(&II, Tok.getLocation());
@@ -500,7 +506,8 @@ bool Parser::ParseOptionalCXXScopeSpecifier(
// template-id to be translated into a type annotation,
// because some clients (e.g., the parsing of class template
// specializations) still want to see the original template-id
- // token.
+ // token, and it might not be a type at all (e.g. a concept name in a
+ // type-constraint).
ConsumeToken();
if (AnnotateTemplateIdToken(Template, TNK, SS, SourceLocation(),
TemplateName, false))
diff --git a/clang/lib/Parse/ParseTemplate.cpp b/clang/lib/Parse/ParseTemplate.cpp
index 35cee596bb01..1b9301b6591d 100644
--- a/clang/lib/Parse/ParseTemplate.cpp
+++ b/clang/lib/Parse/ParseTemplate.cpp
@@ -12,6 +12,7 @@
#include "clang/AST/ASTContext.h"
#include "clang/AST/DeclTemplate.h"
+#include "clang/AST/ExprCXX.h"
#include "clang/Parse/ParseDiagnostic.h"
#include "clang/Parse/Parser.h"
#include "clang/Parse/RAIIObjectsForParser.h"
@@ -498,7 +499,10 @@ Parser::ParseTemplateParameterList(const unsigned Depth,
/// Determine whether the parser is at the start of a template
/// type parameter.
-bool Parser::isStartOfTemplateTypeParameter() {
+/// \param ScopeError will receive true if there was an error parsing a
+/// scope specifier at the current location.
+bool Parser::isStartOfTemplateTypeParameter(bool &ScopeError) {
+ ScopeError = false;
if (Tok.is(tok::kw_class)) {
// "class" may be the start of an elaborated-type-specifier or a
// type-parameter. Per C++ [temp.param]p3, we prefer the type-parameter.
@@ -531,6 +535,40 @@ bool Parser::isStartOfTemplateTypeParameter() {
}
}
+ bool WasScopeAnnotation = Tok.is(tok::annot_cxxscope);
+ CXXScopeSpec SS;
+ ScopeError =
+ ParseOptionalCXXScopeSpecifier(SS, ParsedType(),
+ /*EnteringContext=*/false,
+ /*MayBePseudoDestructor=*/nullptr,
+ // If this is not a type-constraint, then
+ // this scope-spec is part of the typename
+ // of a non-type template parameter
+ /*IsTypename=*/true, /*LastII=*/nullptr,
+ // We won't find concepts in
+ // non-namespaces anyway, so might as well
+ // parse this correctly for possible type
+ // names.
+ /*OnlyNamespace=*/false);
+ if (ScopeError)
+ return false;
+ if (TryAnnotateTypeConstraint(SS))
+ return false;
+ bool IsTypeConstraint = isTypeConstraintAnnotation();
+ if (!IsTypeConstraint && SS.isNotEmpty()) {
+ // This isn't a type-constraint but we've already parsed this scope
+ // specifier - annotate it.
+ AnnotateScopeToken(SS, /*isNewAnnotation=*/!WasScopeAnnotation);
+ return false;
+ }
+
+ if (IsTypeConstraint &&
+ // Next token might be 'auto' or 'decltype', indicating that this
+ // type-constraint is in fact part of a placeholder-type-specifier of a
+ // non-type template parameter.
+ !NextToken().isOneOf(tok::kw_auto, tok::kw_decltype))
+ return true;
+
// 'typedef' is a reasonably-common typo/thinko for 'typename', and is
// ill-formed otherwise.
if (Tok.isNot(tok::kw_typename) && Tok.isNot(tok::kw_typedef))
@@ -574,24 +612,37 @@ bool Parser::isStartOfTemplateTypeParameter() {
/// type-parameter
/// parameter-declaration
///
-/// type-parameter: (see below)
-/// 'class' ...[opt] identifier[opt]
-/// 'class' identifier[opt] '=' type-id
-/// 'typename' ...[opt] identifier[opt]
-/// 'typename' identifier[opt] '=' type-id
-/// 'template' '<' template-parameter-list '>'
-/// 'class' ...[opt] identifier[opt]
-/// 'template' '<' template-parameter-list '>' 'class' identifier[opt]
-/// = id-expression
+/// type-parameter: (See below)
+/// type-parameter-key ...[opt] identifier[opt]
+/// type-parameter-key identifier[opt] = type-id
+/// (C++2a) type-constraint ...[opt] identifier[opt]
+/// (C++2a) type-constraint identifier[opt] = type-id
+/// 'template' '<' template-parameter-list '>' type-parameter-key
+/// ...[opt] identifier[opt]
+/// 'template' '<' template-parameter-list '>' type-parameter-key
+/// identifier[opt] '=' id-expression
+///
+/// type-parameter-key:
+/// class
+/// typename
+///
NamedDecl *Parser::ParseTemplateParameter(unsigned Depth, unsigned Position) {
- if (isStartOfTemplateTypeParameter()) {
- // Is there just a typo in the input code? ('typedef' instead of 'typename')
+ // We could be facing a type-constraint, which (could) start a type parameter.
+ // Annotate it now (we might end up not using it if we determine this
+ // type-constraint is in fact part of a placeholder-type-specifier of a
+ // non-type template parameter.
+
+ bool ScopeError;
+ if (isStartOfTemplateTypeParameter(ScopeError)) {
+ // Is there just a typo in the input code? ('typedef' instead of
+ // 'typename')
if (Tok.is(tok::kw_typedef)) {
Diag(Tok.getLocation(), diag::err_expected_template_parameter);
Diag(Tok.getLocation(), diag::note_meant_to_use_typename)
<< FixItHint::CreateReplacement(CharSourceRange::getCharRange(
- Tok.getLocation(), Tok.getEndLoc()),
+ Tok.getLocation(),
+ Tok.getEndLoc()),
"typename");
Tok.setKind(tok::kw_typename);
@@ -599,7 +650,26 @@ NamedDecl *Parser::ParseTemplateParameter(unsigned Depth, unsigned Position) {
return ParseTypeParameter(Depth, Position);
}
-
+ if (ScopeError) {
+ // We return an invalid parameter as opposed to null to avoid having bogus
+ // diagnostics about an empty template parameter list.
+ // FIXME: Fix ParseTemplateParameterList to better handle nullptr results
+ // from here.
+ // Return a NTTP as if there was an error in a scope specifier, the user
+ // probably meant to write the type of a NTTP.
+ DeclSpec DS(getAttrFactory());
+ DS.SetTypeSpecError();
+ Declarator D(DS, DeclaratorContext::TemplateParamContext);
+ D.SetIdentifier(nullptr, Tok.getLocation());
+ D.setInvalidType(true);
+ NamedDecl *ErrorParam = Actions.ActOnNonTypeTemplateParameter(
+ getCurScope(), D, Depth, Position, /*EqualLoc=*/SourceLocation(),
+ /*DefaultArg=*/nullptr);
+ ErrorParam->setInvalidDecl(true);
+ SkipUntil(tok::comma, tok::greater, tok::greatergreater,
+ StopAtSemi | StopBeforeMatch);
+ return ErrorParam;
+ }
if (Tok.is(tok::kw_template))
return ParseTemplateTemplateParameter(Depth, Position);
@@ -609,6 +679,56 @@ NamedDecl *Parser::ParseTemplateParameter(unsigned Depth, unsigned Position) {
return ParseNonTypeTemplateParameter(Depth, Position);
}
+/// Check whether the current token is a template-id annotation denoting a
+/// type-constraint.
+bool Parser::isTypeConstraintAnnotation() {
+ if (Tok.isNot(tok::annot_template_id))
+ return false;
+ const auto *ExistingAnnot =
+ static_cast<TemplateIdAnnotation *>(Tok.getAnnotationValue());
+ return ExistingAnnot->Kind == TNK_Concept_template;
+}
+
+/// Try parsing a type-constraint construct at the current location, after the
+/// optional scope specifier.
+///
+/// type-constraint:
+/// nested-name-specifier[opt] concept-name
+/// nested-name-specifier[opt] concept-name
+/// '<' template-argument-list[opt] '>'[opt]
+///
+/// \returns true if an error occurred, and false otherwise.
+bool Parser::TryAnnotateTypeConstraint(CXXScopeSpec &SS) {
+ if (!getLangOpts().ConceptsTS || Tok.isNot(tok::identifier))
+ return false;
+
+ UnqualifiedId PossibleConceptName;
+ PossibleConceptName.setIdentifier(Tok.getIdentifierInfo(),
+ Tok.getLocation());
+
+ TemplateTy PossibleConcept;
+ bool MemberOfUnknownSpecialization = false;
+ auto TNK = Actions.isTemplateName(getCurScope(), SS,
+ /*hasTemplateKeyword=*/false,
+ PossibleConceptName,
+ /*ObjectType=*/ParsedType(),
+ /*EnteringContext=*/false,
+ PossibleConcept,
+ MemberOfUnknownSpecialization);
+ assert(!MemberOfUnknownSpecialization
+ && "Member when we only allowed namespace scope qualifiers??");
+ if (!PossibleConcept || TNK != TNK_Concept_template)
+ return false;
+
+ // At this point we're sure we're dealing with a constrained parameter. It
+ // may or may not have a template parameter list following the concept name.
+ return AnnotateTemplateIdToken(PossibleConcept, TNK, SS,
+ /*TemplateKWLoc=*/SourceLocation(),
+ PossibleConceptName,
+ /*AllowTypeAnnotation=*/false,
+ /*TypeConstraint=*/true);
+}
+
/// ParseTypeParameter - Parse a template type parameter (C++ [temp.param]).
/// Other kinds of template parameters are parsed in
/// ParseTemplateTemplateParameter and ParseNonTypeTemplateParameter.
@@ -619,12 +739,25 @@ NamedDecl *Parser::ParseTemplateParameter(unsigned Depth, unsigned Position) {
/// 'typename' ...[opt][C++0x] identifier[opt]
/// 'typename' identifier[opt] '=' type-id
NamedDecl *Parser::ParseTypeParameter(unsigned Depth, unsigned Position) {
- assert(Tok.isOneOf(tok::kw_class, tok::kw_typename) &&
- "A type-parameter starts with 'class' or 'typename'");
-
- // Consume the 'class' or 'typename' keyword.
- bool TypenameKeyword = Tok.is(tok::kw_typename);
- SourceLocation KeyLoc = ConsumeToken();
+ assert(Tok.isOneOf(tok::kw_class, tok::kw_typename, tok::annot_template_id) &&
+ "A type-parameter starts with 'class', 'typename' or a "
+ "type-constraint");
+
+ TemplateIdAnnotation *TypeConstraint = nullptr;
+ bool TypenameKeyword = false;
+ SourceLocation KeyLoc;
+ if (Tok.is(tok::annot_template_id)) {
+ // Consume the 'type-constraint'.
+ TypeConstraint =
+ static_cast<TemplateIdAnnotation *>(Tok.getAnnotationValue());
+ assert(TypeConstraint->Kind == TNK_Concept_template &&
+ "stray non-concept template-id annotation");
+ KeyLoc = ConsumeAnnotationToken();
+ } else {
+ // Consume the 'class' or 'typename' keyword.
+ TypenameKeyword = Tok.is(tok::kw_typename);
+ KeyLoc = ConsumeToken();
+ }
// Grab the ellipsis (if given).
SourceLocation EllipsisLoc;
@@ -664,9 +797,19 @@ NamedDecl *Parser::ParseTypeParameter(unsigned Depth, unsigned Position) {
DefaultArg = ParseTypeName(/*Range=*/nullptr,
DeclaratorContext::TemplateTypeArgContext).get();
- return Actions.ActOnTypeParameter(getCurScope(), TypenameKeyword, EllipsisLoc,
- KeyLoc, ParamName, NameLoc, Depth, Position,
- EqualLoc, DefaultArg);
+ NamedDecl *NewDecl = Actions.ActOnTypeParameter(getCurScope(),
+ TypenameKeyword, EllipsisLoc,
+ KeyLoc, ParamName, NameLoc,
+ Depth, Position, EqualLoc,
+ DefaultArg,
+ TypeConstraint != nullptr);
+
+ if (TypeConstraint)
+ Actions.ActOnTypeConstraint(TypeConstraint,
+ cast<TemplateTypeParmDecl>(NewDecl),
+ EllipsisLoc);
+
+ return NewDecl;
}
/// ParseTemplateTemplateParameter - Handle the parsing of template
@@ -829,7 +972,7 @@ Parser::ParseNonTypeTemplateParameter(unsigned Depth, unsigned Position) {
// Create the parameter.
return Actions.ActOnNonTypeTemplateParameter(getCurScope(), ParamDecl,
- Depth, Position, EqualLoc,
+ Depth, Position, EqualLoc,
DefaultArg.get());
}
@@ -1105,6 +1248,10 @@ Parser::ParseTemplateIdAfterTemplateName(bool ConsumeLastToken,
/// simple-template-id is always replaced with a template-id
/// annotation token.
///
+/// \param TypeConstraint if true, then this is actually a type-constraint,
+/// meaning that the template argument list can be omitted (and the template in
+/// question must be a concept).
+///
/// If an unrecoverable parse error occurs and no annotation token can be
/// formed, this function returns true.
///
@@ -1112,10 +1259,15 @@ bool Parser::AnnotateTemplateIdToken(TemplateTy Template, TemplateNameKind TNK,
CXXScopeSpec &SS,
SourceLocation TemplateKWLoc,
UnqualifiedId &TemplateName,
- bool AllowTypeAnnotation) {
+ bool AllowTypeAnnotation,
+ bool TypeConstraint) {
assert(getLangOpts().CPlusPlus && "Can only annotate template-ids in C++");
- assert(Template && Tok.is(tok::less) &&
+ assert(Template && (Tok.is(tok::less) || TypeConstraint) &&
"Parser isn't at the beginning of a template-id");
+ assert(!(TypeConstraint && AllowTypeAnnotation) && "type-constraint can't be "
+ "a type annotation");
+ assert((!TypeConstraint || TNK == TNK_Concept_template) && "type-constraint "
+ "must accompany a concept name");
// Consume the template-name.
SourceLocation TemplateNameLoc = TemplateName.getSourceRange().getBegin();
@@ -1123,17 +1275,19 @@ bool Parser::AnnotateTemplateIdToken(TemplateTy Template, TemplateNameKind TNK,
// Parse the enclosed template argument list.
SourceLocation LAngleLoc, RAngleLoc;
TemplateArgList TemplateArgs;
- bool Invalid = ParseTemplateIdAfterTemplateName(false, LAngleLoc,
- TemplateArgs,
- RAngleLoc);
-
- if (Invalid) {
- // If we failed to parse the template ID but skipped ahead to a >, we're not
- // going to be able to form a token annotation. Eat the '>' if present.
- TryConsumeToken(tok::greater);
- // FIXME: Annotate the token stream so we don't produce the same errors
- // again if we're doing this annotation as part of a tentative parse.
- return true;
+ if (!TypeConstraint || Tok.is(tok::less)) {
+ bool Invalid = ParseTemplateIdAfterTemplateName(false, LAngleLoc,
+ TemplateArgs,
+ RAngleLoc);
+
+ if (Invalid) {
+ // If we failed to parse the template ID but skipped ahead to a >, we're not
+ // going to be able to form a token annotation. Eat the '>' if present.
+ TryConsumeToken(tok::greater);
+ // FIXME: Annotate the token stream so we don't produce the same errors
+ // again if we're doing this annotation as part of a tentative parse.
+ return true;
+ }
}
ASTTemplateArgsPtr TemplateArgsPtr(TemplateArgs);
@@ -1371,8 +1525,9 @@ ParsedTemplateArgument Parser::ParseTemplateArgument() {
// Parse a non-type template argument.
SourceLocation Loc = Tok.getLocation();
ExprResult ExprArg = ParseConstantExpressionInExprEvalContext(MaybeTypeCast);
- if (ExprArg.isInvalid() || !ExprArg.get())
+ if (ExprArg.isInvalid() || !ExprArg.get()) {
return ParsedTemplateArgument();
+ }
return ParsedTemplateArgument(ParsedTemplateArgument::NonType,
ExprArg.get(), Loc);
diff --git a/clang/lib/Sema/SemaCodeComplete.cpp b/clang/lib/Sema/SemaCodeComplete.cpp
index e14c63d557ad..7260977c634d 100644
--- a/clang/lib/Sema/SemaCodeComplete.cpp
+++ b/clang/lib/Sema/SemaCodeComplete.cpp
@@ -2986,7 +2986,11 @@ static void AddTemplateParameterChunks(
if (TemplateTypeParmDecl *TTP = dyn_cast<TemplateTypeParmDecl>(*P)) {
if (TTP->wasDeclaredWithTypename())
PlaceholderStr = "typename";
- else
+ else if (const auto *TC = TTP->getTypeConstraint()) {
+ llvm::raw_string_ostream OS(PlaceholderStr);
+ TC->print(OS, Policy);
+ OS.flush();
+ } else
PlaceholderStr = "class";
if (TTP->getIdentifier()) {
diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp
index 6f22326e8fc5..1184446796eb 100755
--- a/clang/lib/Sema/SemaTemplate.cpp
+++ b/clang/lib/Sema/SemaTemplate.cpp
@@ -976,20 +976,23 @@ ParsedTemplateArgument Sema::ActOnTemplateTypeArgument(TypeResult ParsedType) {
/// If the type parameter has a default argument, it will be added
/// later via ActOnTypeParameterDefault.
NamedDecl *Sema::ActOnTypeParameter(Scope *S, bool Typename,
- SourceLocation EllipsisLoc,
- SourceLocation KeyLoc,
- IdentifierInfo *ParamName,
- SourceLocation ParamNameLoc,
- unsigned Depth, unsigned Position,
- SourceLocation EqualLoc,
- ParsedType DefaultArg) {
+ SourceLocation EllipsisLoc,
+ SourceLocation KeyLoc,
+ IdentifierInfo *ParamName,
+ SourceLocation ParamNameLoc,
+ unsigned Depth, unsigned Position,
+ SourceLocation EqualLoc,
+ ParsedType DefaultArg,
+ bool HasTypeConstraint) {
assert(S->isTemplateParamScope() &&
"Template type parameter not in template parameter scope!");
bool IsParameterPack = EllipsisLoc.isValid();
- TemplateTypeParmDecl *Param = TemplateTypeParmDecl::Create(
- Context, Context.getTranslationUnitDecl(), KeyLoc, ParamNameLoc, Depth,
- Position, ParamName, Typename, IsParameterPack);
+ TemplateTypeParmDecl *Param
+ = TemplateTypeParmDecl::Create(Context, Context.getTranslationUnitDecl(),
+ KeyLoc, ParamNameLoc, Depth, Position,
+ ParamName, Typename, IsParameterPack,
+ HasTypeConstraint);
Param->setAccess(AS_public);
if (Param->isParameterPack())
@@ -1036,6 +1039,125 @@ NamedDecl *Sema::ActOnTypeParameter(Scope *S, bool Typename,
return Param;
}
+/// Convert the parser's template argument list representation into our form.
+static TemplateArgumentListInfo
+makeTemplateArgumentListInfo(Sema &S, TemplateIdAnnotation &TemplateId) {
+ TemplateArgumentListInfo TemplateArgs(TemplateId.LAngleLoc,
+ TemplateId.RAngleLoc);
+ ASTTemplateArgsPtr TemplateArgsPtr(TemplateId.getTemplateArgs(),
+ TemplateId.NumArgs);
+ S.translateTemplateArguments(TemplateArgsPtr, TemplateArgs);
+ return TemplateArgs;
+}
+
+bool Sema::ActOnTypeConstraint(TemplateIdAnnotation *TypeConstr,
+ TemplateTypeParmDecl *ConstrainedParameter,
+ SourceLocation EllipsisLoc) {
+ ConceptDecl *CD =
+ cast<ConceptDecl>(TypeConstr->Template.get().getAsTemplateDecl());
+
+ // C++2a [temp.param]p4:
+ // [...] The concept designated by a type-constraint shall be a type
+ // concept ([temp.concept]).
+ if (!CD->isTypeConcept()) {
+ Diag(TypeConstr->TemplateNameLoc,
+ diag::err_type_constraint_non_type_concept);
+ return true;
+ }
+
+ bool WereArgsSpecified = TypeConstr->LAngleLoc.isValid();
+
+ if (!WereArgsSpecified &&
+ CD->getTemplateParameters()->getMinRequiredArguments() > 1) {
+ Diag(TypeConstr->TemplateNameLoc,
+ diag::err_type_constraint_missing_arguments) << CD;
+ return true;
+ }
+
+ TemplateArgumentListInfo TemplateArgs;
+ if (TypeConstr->LAngleLoc.isValid()) {
+ TemplateArgs =
+ makeTemplateArgumentListInfo(*this, *TypeConstr);
+ }
+ return AttachTypeConstraint(
+ TypeConstr->SS.isSet() ? TypeConstr->SS.getWithLocInContext(Context) :
+ NestedNameSpecifierLoc(),
+ DeclarationNameInfo(DeclarationName(TypeConstr->Name),
+ TypeConstr->TemplateNameLoc), CD,
+ TypeConstr->LAngleLoc.isValid() ? &TemplateArgs : nullptr,
+ ConstrainedParameter, EllipsisLoc);
+}
+
+/// Attach a type-constraint to a template parameter.
+/// \returns true if an error occured. This can happen if the
+/// immediately-declared constraint could not be formed (e.g. incorrect number
+/// of arguments for the named concept).
+bool Sema::AttachTypeConstraint(NestedNameSpecifierLoc NS,
+ DeclarationNameInfo NameInfo,
+ ConceptDecl *NamedConcept,
+ const TemplateArgumentListInfo *TemplateArgs,
+ TemplateTypeParmDecl *ConstrainedParameter,
+ SourceLocation EllipsisLoc) {
+ // C++2a [temp.param]p4:
+ // [...] If Q is of the form C<A1, ..., An>, then let E' be
+ // C<T, A1, ..., An>. Otherwise, let E' be C<T>. [...]
+ const ASTTemplateArgumentListInfo *ArgsAsWritten =
+ TemplateArgs ? ASTTemplateArgumentListInfo::Create(Context,
+ *TemplateArgs) : nullptr;
+
+ QualType ParamAsArgument(ConstrainedParameter->getTypeForDecl(), 0);
+ TemplateArgumentListInfo ConstraintArgs;
+ ConstraintArgs.addArgument(
+ TemplateArgumentLoc(
+ TemplateArgument(ParamAsArgument),
+ TemplateArgumentLocInfo(
+ Context.getTrivialTypeSourceInfo(ParamAsArgument,
+ ConstrainedParameter->getLocation()))));
+ if (TemplateArgs) {
+ ConstraintArgs.setRAngleLoc(TemplateArgs->getRAngleLoc());
+ ConstraintArgs.setLAngleLoc(TemplateArgs->getLAngleLoc());
+ for (const TemplateArgumentLoc &ArgLoc : TemplateArgs->arguments())
+ ConstraintArgs.addArgument(ArgLoc);
+ }
+
+ // C++2a [temp.param]p4:
+ // [...] This constraint-expression E is called the immediately-declared
+ // constraint of T. [...]
+ CXXScopeSpec SS;
+ SS.Adopt(NS);
+ ExprResult ImmediatelyDeclaredConstraint = CheckConceptTemplateId(SS,
+ /*TemplateKWLoc=*/SourceLocation(), NameInfo, /*FoundDecl=*/NamedConcept,
+ NamedConcept, &ConstraintArgs);
+ if (ImmediatelyDeclaredConstraint.isInvalid())
+ return true;
+
+ if (ConstrainedParameter->isParameterPack()) {
+ // C++2a [temp.param]p4:
+ // [...] If T is not a pack, then E is E', otherwise E is (E' && ...).
+ //
+ // We have the following case:
+ //
+ // template<typename T> concept C1 = true;
+ // template<C1... T> struct s1;
+ //
+ // The constraint: (C1<T> && ...)
+ ImmediatelyDeclaredConstraint =
+ BuildCXXFoldExpr(/*LParenLoc=*/SourceLocation(),
+ ImmediatelyDeclaredConstraint.get(), BO_LAnd,
+ EllipsisLoc, /*RHS=*/nullptr,
+ /*RParenLoc=*/SourceLocation(),
+ /*NumExpansions=*/None).get();
+ if (ImmediatelyDeclaredConstraint.isInvalid())
+ return true;
+ }
+
+ ConstrainedParameter->setTypeConstraint(NS, NameInfo,
+ /*FoundDecl=*/NamedConcept,
+ NamedConcept, ArgsAsWritten,
+ ImmediatelyDeclaredConstraint.get());
+ return false;
+}
+
/// Check that the type of a non-type template parameter is
/// well-formed.
///
@@ -1868,7 +1990,23 @@ struct ConvertConstructorToDeductionGuideTransform {
SemaRef.Context, DC, TTP->getBeginLoc(), TTP->getLocation(),
/*Depth*/ 0, Depth1IndexAdjustment + TTP->getIndex(),
TTP->getIdentifier(), TTP->wasDeclaredWithTypename(),
- TTP->isParameterPack());
+ TTP->isParameterPack(), TTP->hasTypeConstraint(),
+ TTP->isExpandedParameterPack() ?
+ llvm::Optional<unsigned>(TTP->getNumExpansionParameters()) : None);
+ if (const auto *TC = TTP->getTypeConstraint()) {
+ TemplateArgumentListInfo TransformedArgs;
+ const auto *ArgsAsWritten = TC->getTemplateArgsAsWritten();
+ if (SemaRef.Subst(ArgsAsWritten->getTemplateArgs(),
+ ArgsAsWritten->NumTemplateArgs, TransformedArgs,
+ Args))
+ SemaRef.AttachTypeConstraint(
+ TC->getNestedNameSpecifierLoc(), TC->getConceptNameInfo(),
+ TC->getNamedConcept(), &TransformedArgs, NewTTP,
+ NewTTP->isParameterPack()
+ ? cast<CXXFoldExpr>(TC->getImmediatelyDeclaredConstraint())
+ ->getEllipsisLoc()
+ : SourceLocation());
+ }
if (TTP->hasDefaultArgument()) {
TypeSourceInfo *InstantiatedDefaultArg =
SemaRef.SubstType(TTP->getDefaultArgumentInfo(), Args,
@@ -2177,6 +2315,17 @@ static bool DiagnoseUnexpandedParameterPacks(Sema &S,
TemplateParameterList *Params = TTP->getTemplateParameters();
for (unsigned I = 0, N = Params->size(); I != N; ++I) {
NamedDecl *P = Params->getParam(I);
+ if (TemplateTypeParmDecl *TTP = dyn_cast<TemplateTypeParmDecl>(P)) {
+ if (!TTP->isParameterPack())
+ if (const TypeConstraint *TC = TTP->getTypeConstraint())
+ if (TC->hasExplicitTemplateArgs())
+ for (auto &ArgLoc : TC->getTemplateArgsAsWritten()->arguments())
+ if (S.DiagnoseUnexpandedParameterPack(ArgLoc,
+ Sema::UPPC_TypeConstraint))
+ return true;
+ continue;
+ }
+
if (NonTypeTemplateParmDecl *NTTP = dyn_cast<NonTypeTemplateParmDecl>(P)) {
if (!NTTP->isParameterPack() &&
S.DiagnoseUnexpandedParameterPack(NTTP->getLocation(),
@@ -3711,17 +3860,6 @@ static bool isSameAsPrimaryTemplate(TemplateParameterList *Params,
return true;
}
-/// Convert the parser's template argument list representation into our form.
-static TemplateArgumentListInfo
-makeTemplateArgumentListInfo(Sema &S, TemplateIdAnnotation &TemplateId) {
- TemplateArgumentListInfo TemplateArgs(TemplateId.LAngleLoc,
- TemplateId.RAngleLoc);
- ASTTemplateArgsPtr TemplateArgsPtr(TemplateId.getTemplateArgs(),
- TemplateId.NumArgs);
- S.translateTemplateArguments(TemplateArgsPtr, TemplateArgs);
- return TemplateArgs;
-}
-
template<typename PartialSpecDecl>
static void checkMoreSpecializedThanPrimary(Sema &S, PartialSpecDecl *Partial) {
if (Partial->getDeclContext()->isDependentContext())
@@ -4233,14 +4371,14 @@ void Sema::diagnoseMissingTemplateArguments(TemplateName Name,
ExprResult
Sema::CheckConceptTemplateId(const CXXScopeSpec &SS,
SourceLocation TemplateKWLoc,
- SourceLocation ConceptNameLoc,
+ const DeclarationNameInfo &ConceptNameInfo,
NamedDecl *FoundDecl,
ConceptDecl *NamedConcept,
const TemplateArgumentListInfo *TemplateArgs) {
assert(NamedConcept && "A concept template id without a template?");
llvm::SmallVector<TemplateArgument, 4> Converted;
- if (CheckTemplateArgumentList(NamedConcept, ConceptNameLoc,
+ if (CheckTemplateArgumentList(NamedConcept, ConceptNameInfo.getLoc(),
const_cast<TemplateArgumentListInfo&>(*TemplateArgs),
/*PartialTemplateArgs=*/false, Converted,
/*UpdateArgsWithConversion=*/false))
@@ -4259,14 +4397,14 @@ Sema::CheckConceptTemplateId(const CXXScopeSpec &SS,
{NamedConcept->getConstraintExpr()},
Converted,
SourceRange(SS.isSet() ? SS.getBeginLoc() :
- ConceptNameLoc,
- TemplateArgs->getRAngleLoc()),
- Satisfaction))
+ ConceptNameInfo.getLoc(),
+ TemplateArgs->getRAngleLoc()),
+ Satisfaction))
return ExprError();
return ConceptSpecializationExpr::Create(Context,
SS.isSet() ? SS.getWithLocInContext(Context) : NestedNameSpecifierLoc{},
- TemplateKWLoc, ConceptNameLoc, FoundDecl, NamedConcept,
+ TemplateKWLoc, ConceptNameInfo, FoundDecl, NamedConcept,
ASTTemplateArgumentListInfo::Create(Context, *TemplateArgs), Converted,
AreArgsDependent ? nullptr : &Satisfaction);
}
@@ -4312,8 +4450,7 @@ ExprResult Sema::BuildTemplateIdExpr(const CXXScopeSpec &SS,
}
if (R.getAsSingle<ConceptDecl>()) {
- return CheckConceptTemplateId(SS, TemplateKWLoc,
- R.getLookupNameInfo().getBeginLoc(),
+ return CheckConceptTemplateId(SS, TemplateKWLoc, R.getLookupNameInfo(),
R.getFoundDecl(),
R.getAsSingle<ConceptDecl>(), TemplateArgs);
}
@@ -5116,7 +5253,7 @@ bool Sema::CheckTemplateArgument(NamedDecl *Param,
case TemplateArgument::Template:
case TemplateArgument::TemplateExpansion:
- if (CheckTemplateTemplateArgument(Params, Arg))
+ if (CheckTemplateTemplateArgument(TempParm, Params, Arg))
return true;
Converted.push_back(Arg.getArgument());
@@ -5156,6 +5293,12 @@ bool Sema::CheckTemplateArgument(NamedDecl *Param,
/// In \c A<int,int>::B, \c NTs and \c TTs have expanded pack size 2, and \c Us
/// is not a pack expansion, so returns an empty Optional.
static Optional<unsigned> getExpandedPackSize(NamedDecl *Param) {
+ if (TemplateTypeParmDecl *TTP
+ = dyn_cast<TemplateTypeParmDecl>(Param)) {
+ if (TTP->isExpandedParameterPack())
+ return TTP->getNumExpansionParameters();
+ }
+
if (NonTypeTemplateParmDecl *NTTP
= dyn_cast<NonTypeTemplateParmDecl>(Param)) {
if (NTTP->isExpandedParameterPack())
@@ -6894,7 +7037,8 @@ static void DiagnoseTemplateParameterListArityMismatch(
///
/// This routine implements the semantics of C++ [temp.arg.template].
/// It returns true if an error occurred, and false otherwise.
-bool Sema::CheckTemplateTemplateArgument(TemplateParameterList *Params,
+bool Sema::CheckTemplateTemplateArgument(TemplateTemplateParmDecl *Param,
+ TemplateParameterList *Params,
TemplateArgumentLoc &Arg) {
TemplateName Name = Arg.getArgument().getAsTemplateOrTemplatePattern();
TemplateDecl *Template = Name.getAsTemplateDecl();
@@ -6933,6 +7077,9 @@ bool Sema::CheckTemplateTemplateArgument(TemplateParameterList *Params,
// 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.
+ // FIXME: We should enable RelaxedTemplateTemplateArgs by default as it is a
+ // defect report resolution from C++17 and shouldn't be introduced by
+ // concepts.
if (getLangOpts().RelaxedTemplateTemplateArgs) {
// Quick check for the common case:
// If P contains a parameter pack, then A [...] matches P if each of A's
@@ -6940,12 +7087,39 @@ bool Sema::CheckTemplateTemplateArgument(TemplateParameterList *Params,
// the template-parameter-list of P.
if (TemplateParameterListsAreEqual(
Template->getTemplateParameters(), Params, false,
- TPL_TemplateTemplateArgumentMatch, Arg.getLocation()))
+ 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,
- Arg.getLocation()))
+ Arg.getLocation())) {
+ // C++2a[temp.func.order]p2
+ // [...] If both deductions succeed, the partial ordering selects the
+ // more constrained template as described by the rules in
+ // [temp.constr.order].
+ SmallVector<const Expr *, 3> ParamsAC, TemplateAC;
+ Params->getAssociatedConstraints(ParamsAC);
+ 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.
}
@@ -7123,7 +7297,6 @@ static bool MatchTemplateParameterKind(Sema &S, NamedDecl *New, NamedDecl *Old,
bool Complain,
Sema::TemplateParameterListEqualKind Kind,
SourceLocation TemplateArgLoc) {
- // TODO: Concepts: Check constrained-parameter constraints here.
// Check the actual kind (type, non-type, template).
if (Old->getKind() != New->getKind()) {
if (Complain) {
@@ -7177,50 +7350,73 @@ static bool MatchTemplateParameterKind(Sema &S, NamedDecl *New, NamedDecl *Old,
// template parameter and one of the non-type template parameter types
// is dependent, then we must wait until template instantiation time
// to actually compare the arguments.
- if (Kind == Sema::TPL_TemplateTemplateArgumentMatch &&
- (OldNTTP->getType()->isDependentType() ||
- NewNTTP->getType()->isDependentType()))
- return true;
-
- if (!S.Context.hasSameType(OldNTTP->getType(), NewNTTP->getType())) {
- if (Complain) {
- unsigned NextDiag = diag::err_template_nontype_parm_
diff erent_type;
- if (TemplateArgLoc.isValid()) {
- S.Diag(TemplateArgLoc,
- diag::err_template_arg_template_params_mismatch);
- NextDiag = diag::note_template_nontype_parm_
diff erent_type;
+ if (Kind != Sema::TPL_TemplateTemplateArgumentMatch ||
+ (!OldNTTP->getType()->isDependentType() &&
+ !NewNTTP->getType()->isDependentType()))
+ if (!S.Context.hasSameType(OldNTTP->getType(), NewNTTP->getType())) {
+ if (Complain) {
+ unsigned NextDiag = diag::err_template_nontype_parm_
diff erent_type;
+ if (TemplateArgLoc.isValid()) {
+ S.Diag(TemplateArgLoc,
+ diag::err_template_arg_template_params_mismatch);
+ NextDiag = diag::note_template_nontype_parm_
diff erent_type;
+ }
+ S.Diag(NewNTTP->getLocation(), NextDiag)
+ << NewNTTP->getType()
+ << (Kind != Sema::TPL_TemplateMatch);
+ S.Diag(OldNTTP->getLocation(),
+ diag::note_template_nontype_parm_prev_declaration)
+ << OldNTTP->getType();
}
- S.Diag(NewNTTP->getLocation(), NextDiag)
- << NewNTTP->getType()
- << (Kind != Sema::TPL_TemplateMatch);
- S.Diag(OldNTTP->getLocation(),
- diag::note_template_nontype_parm_prev_declaration)
- << OldNTTP->getType();
- }
- return false;
- }
-
- return true;
+ return false;
+ }
}
-
// For template template parameters, check the template parameter types.
// The template parameter lists of template template
// parameters must agree.
- if (TemplateTemplateParmDecl *OldTTP
+ else if (TemplateTemplateParmDecl *OldTTP
= dyn_cast<TemplateTemplateParmDecl>(Old)) {
TemplateTemplateParmDecl *NewTTP = cast<TemplateTemplateParmDecl>(New);
- return S.TemplateParameterListsAreEqual(NewTTP->getTemplateParameters(),
- OldTTP->getTemplateParameters(),
- Complain,
+ if (!S.TemplateParameterListsAreEqual(NewTTP->getTemplateParameters(),
+ OldTTP->getTemplateParameters(),
+ Complain,
(Kind == Sema::TPL_TemplateMatch
? Sema::TPL_TemplateTemplateParmMatch
: Kind),
- TemplateArgLoc);
- }
+ TemplateArgLoc))
+ return false;
+ } else if (Kind != Sema::TPL_TemplateTemplateArgumentMatch) {
+ const Expr *NewC = nullptr, *OldC = nullptr;
+ if (const auto *TC = cast<TemplateTypeParmDecl>(New)->getTypeConstraint())
+ NewC = TC->getImmediatelyDeclaredConstraint();
+ if (const auto *TC = cast<TemplateTypeParmDecl>(Old)->getTypeConstraint())
+ OldC = TC->getImmediatelyDeclaredConstraint();
+
+ auto Diagnose = [&] {
+ S.Diag(NewC ? NewC->getBeginLoc() : New->getBeginLoc(),
+ diag::err_template_
diff erent_type_constraint);
+ S.Diag(OldC ? OldC->getBeginLoc() : Old->getBeginLoc(),
+ diag::note_template_prev_declaration) << /*declaration*/0;
+ };
- // TODO: Concepts: Match immediately-introduced-constraint for type
- // constraints
+ if (!NewC != !OldC) {
+ if (Complain)
+ Diagnose();
+ return false;
+ }
+
+ if (NewC) {
+ llvm::FoldingSetNodeID OldCID, NewCID;
+ OldC->Profile(OldCID, S.Context, /*Canonical=*/true);
+ NewC->Profile(NewCID, S.Context, /*Canonical=*/true);
+ if (OldCID != NewCID) {
+ if (Complain)
+ Diagnose();
+ return false;
+ }
+ }
+ }
return true;
}
@@ -7247,15 +7443,6 @@ void DiagnoseTemplateParameterListArityMismatch(Sema &S,
<< SourceRange(Old->getTemplateLoc(), Old->getRAngleLoc());
}
-static void
-DiagnoseTemplateParameterListRequiresClauseMismatch(Sema &S,
- TemplateParameterList *New,
- TemplateParameterList *Old){
- S.Diag(New->getTemplateLoc(), diag::err_template_
diff erent_requires_clause);
- S.Diag(Old->getTemplateLoc(), diag::note_template_prev_declaration)
- << /*declaration*/0;
-}
-
/// Determine whether the given template parameter lists are
/// equivalent.
///
@@ -7348,9 +7535,17 @@ Sema::TemplateParameterListsAreEqual(TemplateParameterList *New,
if (Kind != TPL_TemplateTemplateArgumentMatch) {
const Expr *NewRC = New->getRequiresClause();
const Expr *OldRC = Old->getRequiresClause();
+
+ auto Diagnose = [&] {
+ Diag(NewRC ? NewRC->getBeginLoc() : New->getTemplateLoc(),
+ diag::err_template_
diff erent_requires_clause);
+ Diag(OldRC ? OldRC->getBeginLoc() : Old->getTemplateLoc(),
+ diag::note_template_prev_declaration) << /*declaration*/0;
+ };
+
if (!NewRC != !OldRC) {
if (Complain)
- DiagnoseTemplateParameterListRequiresClauseMismatch(*this, New, Old);
+ Diagnose();
return false;
}
@@ -7360,7 +7555,7 @@ Sema::TemplateParameterListsAreEqual(TemplateParameterList *New,
NewRC->Profile(NewRCID, Context, /*Canonical=*/true);
if (OldRCID != NewRCID) {
if (Complain)
- DiagnoseTemplateParameterListRequiresClauseMismatch(*this, New, Old);
+ Diagnose();
return false;
}
}
diff --git a/clang/lib/Sema/SemaTemplateDeduction.cpp b/clang/lib/Sema/SemaTemplateDeduction.cpp
index e626948cb5d4..1b9f1b2144d1 100644
--- a/clang/lib/Sema/SemaTemplateDeduction.cpp
+++ b/clang/lib/Sema/SemaTemplateDeduction.cpp
@@ -643,6 +643,10 @@ static TemplateParameter makeTemplateParameter(Decl *D) {
/// If \p Param is an expanded parameter pack, get the number of expansions.
static Optional<unsigned> getExpandedPackSize(NamedDecl *Param) {
+ if (auto *TTP = dyn_cast<TemplateTypeParmDecl>(Param))
+ if (TTP->isExpandedParameterPack())
+ return TTP->getNumExpansionParameters();
+
if (auto *NTTP = dyn_cast<NonTypeTemplateParmDecl>(Param))
if (NTTP->isExpandedParameterPack())
return NTTP->getNumExpansionTypes();
@@ -4542,11 +4546,12 @@ Sema::DeduceAutoType(TypeLoc Type, Expr *&Init, QualType &Result,
// Build template<class TemplParam> void Func(FuncParam);
TemplateTypeParmDecl *TemplParam = TemplateTypeParmDecl::Create(
- Context, nullptr, SourceLocation(), Loc, Depth, 0, nullptr, false, false);
+ Context, nullptr, SourceLocation(), Loc, Depth, 0, nullptr, false, false,
+ false);
QualType TemplArg = QualType(TemplParam->getTypeForDecl(), 0);
NamedDecl *TemplParamPtr = TemplParam;
FixedSizeTemplateParameterListStorage<1, false> TemplateParamsSt(
- Loc, Loc, TemplParamPtr, Loc, nullptr);
+ Context, Loc, Loc, TemplParamPtr, Loc, nullptr);
QualType FuncParam =
SubstituteDeducedTypeTransform(*this, TemplArg, /*UseTypeSugar*/false)
@@ -5276,9 +5281,8 @@ Sema::getMoreSpecializedPartialSpecialization(
return nullptr;
if (IsAtLeastAsConstrained(PS2, AC2, PS1, AC1, AtLeastAsConstrained2))
return nullptr;
- if (AtLeastAsConstrained1 == AtLeastAsConstrained2) {
+ if (AtLeastAsConstrained1 == AtLeastAsConstrained2)
return nullptr;
- }
return AtLeastAsConstrained1 ? PS1 : PS2;
}
@@ -5352,7 +5356,8 @@ bool Sema::isTemplateTemplateParameterAtLeastAsSpecializedAs(
SFINAETrap Trap(*this);
Context.getInjectedTemplateArgs(P, PArgs);
- TemplateArgumentListInfo PArgList(P->getLAngleLoc(), P->getRAngleLoc());
+ TemplateArgumentListInfo PArgList(P->getLAngleLoc(),
+ P->getRAngleLoc());
for (unsigned I = 0, N = P->size(); I != N; ++I) {
// Unwrap packs that getInjectedTemplateArgs wrapped around pack
// expansions, to form an "as written" argument list.
diff --git a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
index ce94c036eb14..64500d0a26d5 100755
--- a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
+++ b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
@@ -2479,13 +2479,68 @@ Decl *TemplateDeclInstantiator::VisitTemplateTypeParmDecl(
// TODO: don't always clone when decls are refcounted.
assert(D->getTypeForDecl()->isTemplateTypeParmType());
+ Optional<unsigned> NumExpanded;
+
+ if (const TypeConstraint *TC = D->getTypeConstraint()) {
+ if (D->isPackExpansion() && !D->isExpandedParameterPack()) {
+ assert(TC->getTemplateArgsAsWritten() &&
+ "type parameter can only be an expansion when explicit arguments "
+ "are specified");
+ // The template type parameter pack's type is a pack expansion of types.
+ // Determine whether we need to expand this parameter pack into separate
+ // types.
+ SmallVector<UnexpandedParameterPack, 2> Unexpanded;
+ for (auto &ArgLoc : TC->getTemplateArgsAsWritten()->arguments())
+ SemaRef.collectUnexpandedParameterPacks(ArgLoc, Unexpanded);
+
+ // Determine whether the set of unexpanded parameter packs can and should
+ // be expanded.
+ bool Expand = true;
+ bool RetainExpansion = false;
+ if (SemaRef.CheckParameterPacksForExpansion(
+ cast<CXXFoldExpr>(TC->getImmediatelyDeclaredConstraint())
+ ->getEllipsisLoc(),
+ SourceRange(TC->getConceptNameLoc(),
+ TC->hasExplicitTemplateArgs() ?
+ TC->getTemplateArgsAsWritten()->getRAngleLoc() :
+ TC->getConceptNameInfo().getEndLoc()),
+ Unexpanded, TemplateArgs, Expand, RetainExpansion, NumExpanded))
+ return nullptr;
+ }
+ }
+
TemplateTypeParmDecl *Inst = TemplateTypeParmDecl::Create(
SemaRef.Context, Owner, D->getBeginLoc(), D->getLocation(),
D->getDepth() - TemplateArgs.getNumSubstitutedLevels(), D->getIndex(),
- D->getIdentifier(), D->wasDeclaredWithTypename(), D->isParameterPack());
+ D->getIdentifier(), D->wasDeclaredWithTypename(), D->isParameterPack(),
+ D->hasTypeConstraint(), NumExpanded);
+
Inst->setAccess(AS_public);
Inst->setImplicit(D->isImplicit());
-
+ if (auto *TC = D->getTypeConstraint()) {
+ // TODO: Concepts: do not instantiate the constraint (delayed constraint
+ // substitution)
+ const ASTTemplateArgumentListInfo *TemplArgInfo
+ = TC->getTemplateArgsAsWritten();
+ TemplateArgumentListInfo InstArgs;
+
+ if (TemplArgInfo) {
+ InstArgs.setLAngleLoc(TemplArgInfo->LAngleLoc);
+ InstArgs.setRAngleLoc(TemplArgInfo->RAngleLoc);
+ if (SemaRef.Subst(TemplArgInfo->getTemplateArgs(),
+ TemplArgInfo->NumTemplateArgs,
+ InstArgs, TemplateArgs))
+ return nullptr;
+ }
+ if (SemaRef.AttachTypeConstraint(
+ TC->getNestedNameSpecifierLoc(), TC->getConceptNameInfo(),
+ TC->getNamedConcept(), &InstArgs, Inst,
+ D->isParameterPack()
+ ? cast<CXXFoldExpr>(TC->getImmediatelyDeclaredConstraint())
+ ->getEllipsisLoc()
+ : SourceLocation()))
+ return nullptr;
+ }
if (D->hasDefaultArgument() && !D->defaultArgumentWasInherited()) {
TypeSourceInfo *InstantiatedDefaultArg =
SemaRef.SubstType(D->getDefaultArgumentInfo(), TemplateArgs,
diff --git a/clang/lib/Sema/SemaType.cpp b/clang/lib/Sema/SemaType.cpp
index d77542be53fb..3884fdae8fe7 100644
--- a/clang/lib/Sema/SemaType.cpp
+++ b/clang/lib/Sema/SemaType.cpp
@@ -3016,7 +3016,8 @@ static QualType GetDeclSpecTypeForDeclarator(TypeProcessingState &state,
SemaRef.Context, SemaRef.Context.getTranslationUnitDecl(),
/*KeyLoc*/ SourceLocation(), /*NameLoc*/ D.getBeginLoc(),
TemplateParameterDepth, AutoParameterPosition,
- /*Identifier*/ nullptr, false, IsParameterPack);
+ /*Identifier*/ nullptr, false, IsParameterPack,
+ /*HasTypeConstraint=*/false);
CorrespondingTemplateParam->setImplicit();
LSI->TemplateParams.push_back(CorrespondingTemplateParam);
// Replace the 'auto' in the function parameter with this invented
diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h
index 1d775dab67be..3b827fbc950b 100644
--- a/clang/lib/Sema/TreeTransform.h
+++ b/clang/lib/Sema/TreeTransform.h
@@ -3064,13 +3064,13 @@ class TreeTransform {
/// By default, performs semantic analysis to build the new expression.
/// Subclasses may override this routine to provide
diff erent behavior.
ExprResult RebuildConceptSpecializationExpr(NestedNameSpecifierLoc NNS,
- SourceLocation TemplateKWLoc, SourceLocation ConceptNameLoc,
+ SourceLocation TemplateKWLoc, DeclarationNameInfo ConceptNameInfo,
NamedDecl *FoundDecl, ConceptDecl *NamedConcept,
TemplateArgumentListInfo *TALI) {
CXXScopeSpec SS;
SS.Adopt(NNS);
ExprResult Result = getSema().CheckConceptTemplateId(SS, TemplateKWLoc,
- ConceptNameLoc,
+ ConceptNameInfo,
FoundDecl,
NamedConcept, TALI);
if (Result.isInvalid())
@@ -11175,7 +11175,7 @@ TreeTransform<Derived>::TransformConceptSpecializationExpr(
return getDerived().RebuildConceptSpecializationExpr(
E->getNestedNameSpecifierLoc(), E->getTemplateKWLoc(),
- E->getConceptNameLoc(), E->getFoundDecl(), E->getNamedConcept(),
+ E->getConceptNameInfo(), E->getFoundDecl(), E->getNamedConcept(),
&TransArgs);
}
diff --git a/clang/lib/Serialization/ASTReaderDecl.cpp b/clang/lib/Serialization/ASTReaderDecl.cpp
index 3bcc9ad58053..96a7d5ae0a31 100644
--- a/clang/lib/Serialization/ASTReaderDecl.cpp
+++ b/clang/lib/Serialization/ASTReaderDecl.cpp
@@ -2313,7 +2313,20 @@ void ASTDeclReader::VisitTemplateTypeParmDecl(TemplateTypeParmDecl *D) {
D->setDeclaredWithTypename(Record.readInt());
- // TODO: Concepts: Immediately introduced constraint
+ if (Record.readInt()) {
+ NestedNameSpecifierLoc NNS = Record.readNestedNameSpecifierLoc();
+ DeclarationNameInfo DN = Record.readDeclarationNameInfo();
+ ConceptDecl *NamedConcept = cast<ConceptDecl>(Record.readDecl());
+ const ASTTemplateArgumentListInfo *ArgsAsWritten = nullptr;
+ if (Record.readInt())
+ ArgsAsWritten = Record.readASTTemplateArgumentListInfo();
+ Expr *ImmediatelyDeclaredConstraint = Record.readExpr();
+ D->setTypeConstraint(NNS, DN, /*FoundDecl=*/nullptr, NamedConcept,
+ ArgsAsWritten, ImmediatelyDeclaredConstraint);
+ if ((D->ExpandedParameterPack = Record.readInt()))
+ D->NumExpanded = Record.readInt();
+ }
+
if (Record.readInt())
D->setDefaultArgument(readTypeSourceInfo());
}
@@ -3800,9 +3813,12 @@ Decl *ASTReader::ReadDeclRecord(DeclID ID) {
case DECL_FUNCTION_TEMPLATE:
D = FunctionTemplateDecl::CreateDeserialized(Context, ID);
break;
- case DECL_TEMPLATE_TYPE_PARM:
- D = TemplateTypeParmDecl::CreateDeserialized(Context, ID);
+ case DECL_TEMPLATE_TYPE_PARM: {
+ bool HasTypeConstraint = Record.readInt();
+ D = TemplateTypeParmDecl::CreateDeserialized(Context, ID,
+ HasTypeConstraint);
break;
+ }
case DECL_NON_TYPE_TEMPLATE_PARM:
D = NonTypeTemplateParmDecl::CreateDeserialized(Context, ID);
break;
diff --git a/clang/lib/Serialization/ASTReaderStmt.cpp b/clang/lib/Serialization/ASTReaderStmt.cpp
index 533502116ed7..f558c26b5f1e 100644
--- a/clang/lib/Serialization/ASTReaderStmt.cpp
+++ b/clang/lib/Serialization/ASTReaderStmt.cpp
@@ -730,15 +730,13 @@ void ASTStmtReader::VisitConceptSpecializationExpr(
unsigned NumTemplateArgs = Record.readInt();
E->NestedNameSpec = Record.readNestedNameSpecifierLoc();
E->TemplateKWLoc = Record.readSourceLocation();
- E->ConceptNameLoc = Record.readSourceLocation();
- E->FoundDecl = readDeclAs<NamedDecl>();
+ E->ConceptName = Record.readDeclarationNameInfo();
E->NamedConcept = readDeclAs<ConceptDecl>();
- const ASTTemplateArgumentListInfo *ArgsAsWritten =
- Record.readASTTemplateArgumentListInfo();
+ E->ArgsAsWritten = Record.readASTTemplateArgumentListInfo();
llvm::SmallVector<TemplateArgument, 4> Args;
for (unsigned I = 0; I < NumTemplateArgs; ++I)
Args.push_back(Record.readTemplateArgument());
- E->setTemplateArguments(ArgsAsWritten, Args);
+ E->setTemplateArguments(Args);
ConstraintSatisfaction Satisfaction;
Satisfaction.IsSatisfied = Record.readInt();
if (!Satisfaction.IsSatisfied) {
diff --git a/clang/lib/Serialization/ASTWriterDecl.cpp b/clang/lib/Serialization/ASTWriterDecl.cpp
index 6b8a9d9719d4..b2a8c118d401 100644
--- a/clang/lib/Serialization/ASTWriterDecl.cpp
+++ b/clang/lib/Serialization/ASTWriterDecl.cpp
@@ -1637,10 +1637,26 @@ void ASTDeclWriter::VisitFunctionTemplateDecl(FunctionTemplateDecl *D) {
}
void ASTDeclWriter::VisitTemplateTypeParmDecl(TemplateTypeParmDecl *D) {
+ Record.push_back(D->hasTypeConstraint());
VisitTypeDecl(D);
Record.push_back(D->wasDeclaredWithTypename());
- // TODO: Concepts - constrained parameters.
+
+ const TypeConstraint *TC = D->getTypeConstraint();
+ Record.push_back(TC != nullptr);
+ if (TC) {
+ Record.AddNestedNameSpecifierLoc(TC->getNestedNameSpecifierLoc());
+ Record.AddDeclarationNameInfo(TC->getConceptNameInfo());
+ Record.AddDeclRef(TC->getNamedConcept());
+ Record.push_back(TC->getTemplateArgsAsWritten() != nullptr);
+ if (TC->getTemplateArgsAsWritten())
+ Record.AddASTTemplateArgumentListInfo(TC->getTemplateArgsAsWritten());
+ Record.AddStmt(TC->getImmediatelyDeclaredConstraint());
+ Record.push_back(D->isExpandedParameterPack());
+ if (D->isExpandedParameterPack())
+ Record.push_back(D->getNumExpansionParameters());
+ }
+
bool OwnsDefaultArg = D->hasDefaultArgument() &&
!D->defaultArgumentWasInherited();
Record.push_back(OwnsDefaultArg);
@@ -1670,7 +1686,6 @@ void ASTDeclWriter::VisitNonTypeTemplateParmDecl(NonTypeTemplateParmDecl *D) {
Code = serialization::DECL_EXPANDED_NON_TYPE_TEMPLATE_PARM_PACK;
} else {
- // TODO: Concepts - constrained parameters.
// Rest of NonTypeTemplateParmDecl.
Record.push_back(D->isParameterPack());
bool OwnsDefaultArg = D->hasDefaultArgument() &&
@@ -1700,7 +1715,6 @@ void ASTDeclWriter::VisitTemplateTemplateParmDecl(TemplateTemplateParmDecl *D) {
Record.AddTemplateParameterList(D->getExpansionTemplateParameters(I));
Code = serialization::DECL_EXPANDED_TEMPLATE_TEMPLATE_PARM_PACK;
} else {
- // TODO: Concepts - constrained parameters.
// Rest of TemplateTemplateParmDecl.
Record.push_back(D->isParameterPack());
bool OwnsDefaultArg = D->hasDefaultArgument() &&
diff --git a/clang/lib/Serialization/ASTWriterStmt.cpp b/clang/lib/Serialization/ASTWriterStmt.cpp
index e671c78fe97b..9231f3b2b9ba 100644
--- a/clang/lib/Serialization/ASTWriterStmt.cpp
+++ b/clang/lib/Serialization/ASTWriterStmt.cpp
@@ -395,8 +395,7 @@ void ASTStmtWriter::VisitConceptSpecializationExpr(
Record.push_back(TemplateArgs.size());
Record.AddNestedNameSpecifierLoc(E->getNestedNameSpecifierLoc());
Record.AddSourceLocation(E->getTemplateKWLoc());
- Record.AddSourceLocation(E->getConceptNameLoc());
- Record.AddDeclRef(E->getFoundDecl());
+ Record.AddDeclarationNameInfo(E->getConceptNameInfo());
Record.AddDeclRef(E->getNamedConcept());
Record.AddASTTemplateArgumentListInfo(E->getTemplateArgsAsWritten());
for (const TemplateArgument &Arg : TemplateArgs)
diff --git a/clang/test/CXX/temp/temp.arg/temp.arg.template/p3-2a.cpp b/clang/test/CXX/temp/temp.arg/temp.arg.template/p3-2a.cpp
new file mode 100644
index 000000000000..593336163fa1
--- /dev/null
+++ b/clang/test/CXX/temp/temp.arg/temp.arg.template/p3-2a.cpp
@@ -0,0 +1,34 @@
+// RUN: %clang_cc1 -std=c++2a -fconcepts-ts -frelaxed-template-template-args -verify %s
+
+template<typename T> concept C = T::f();
+// expected-note at -1{{similar constraint}}
+template<typename T> concept D = C<T> && T::g();
+template<typename T> concept F = T::f();
+// expected-note at -1{{similar constraint expressions not considered equivalent}}
+template<template<C> class P> struct S1 { }; // expected-note 2{{'P' declared here}}
+
+template<C> struct X { }; // expected-note{{'X' declared here}}
+
+template<D> struct Y { }; // expected-note 2{{'Y' declared here}}
+template<typename T> struct Z { };
+template<F> struct W { }; // expected-note{{'W' declared here}}
+
+S1<X> s11;
+S1<Y> s12; // expected-error{{template template argument 'Y' is more constrained than template template parameter 'P'}}
+S1<Z> s13;
+S1<W> s14; // expected-error{{template template argument 'W' is more constrained than template template parameter 'P'}}
+
+template<template<typename> class P> struct S2 { }; // expected-note 2{{'P' declared here}}
+
+S2<X> s21; // expected-error{{template template argument 'X' is more constrained than template template parameter 'P'}}
+S2<Y> s22; // expected-error{{template template argument 'Y' is more constrained than template template parameter 'P'}}
+S2<Z> s23;
+
+template <template <typename...> class C>
+struct S3;
+
+template <C T>
+using N = typename T::type;
+
+using s31 = S3<N>;
+using s32 = S3<Z>;
diff --git a/clang/test/CXX/temp/temp.constr/temp.constr.decl/class-template-decl.cpp b/clang/test/CXX/temp/temp.constr/temp.constr.decl/class-template-decl.cpp
index 6f7b80e26a66..2a1a4e1b7ff7 100644
--- a/clang/test/CXX/temp/temp.constr/temp.constr.decl/class-template-decl.cpp
+++ b/clang/test/CXX/temp/temp.constr/temp.constr.decl/class-template-decl.cpp
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -std=c++2a -x c++ -verify %s
+// RUN: %clang_cc1 -std=c++2a -fconcepts-ts -x c++ -verify %s
namespace nodiag {
@@ -7,6 +7,14 @@ struct A;
template <typename U> requires (bool(U()))
struct A;
+template<typename T>
+concept C1 = true;
+
+template <C1 T> requires (bool(T()))
+struct B;
+template <C1 U> requires (bool(U()))
+struct B;
+
} // end namespace nodiag
namespace diag {
@@ -24,6 +32,14 @@ struct C;
template <typename T> requires (!0) // expected-error{{requires clause
diff ers in template redeclaration}}
struct C;
+template<typename T>
+concept C1 = true;
+
+template <C1 T> // expected-note{{previous template declaration is here}}
+struct D;
+template <typename T> requires C1<T> // expected-error{{type constraint
diff ers in template redeclaration}}
+struct D;
+
} // end namespace diag
namespace nodiag {
diff --git a/clang/test/CXX/temp/temp.constr/temp.constr.decl/p3.cpp b/clang/test/CXX/temp/temp.constr/temp.constr.decl/p3.cpp
new file mode 100644
index 000000000000..c6fe82d8de45
--- /dev/null
+++ b/clang/test/CXX/temp/temp.constr/temp.constr.decl/p3.cpp
@@ -0,0 +1,30 @@
+// RUN: %clang_cc1 -std=c++2a -fconcepts-ts -x c++ -verify %s
+
+template<typename T>
+struct X {
+ using Y = typename T::invalid;
+};
+
+template<typename T>
+concept Invalid = X<T>{};
+
+template<typename T>
+concept False = false; // expected-note{{because 'false' evaluated to false}}
+
+template<typename T>
+concept True = true;
+
+// TODO: Concepts: Uncomment trailing requires clauses here when we have correct substitution.
+//template<True T>
+// requires False<T>
+//void g1() requires Invalid<T>;
+//
+//using g1i = decltype(g1<int>());
+
+template<False T> // expected-note{{because 'int' does not satisfy 'False'}}
+ requires Invalid<T>
+void g2(); // requires Invalid<T>;
+// expected-note at -1{{candidate template ignored: constraints not satisfied [with T = int]}}
+
+using g2i = decltype(g2<int>());
+// expected-error at -1{{no matching function for call to 'g2'}}
\ No newline at end of file
diff --git a/clang/test/CXX/temp/temp.param/p10-2a.cpp b/clang/test/CXX/temp/temp.param/p10-2a.cpp
new file mode 100644
index 000000000000..de40f8cca5d5
--- /dev/null
+++ b/clang/test/CXX/temp/temp.param/p10-2a.cpp
@@ -0,0 +1,117 @@
+// RUN: %clang_cc1 -std=c++2a -fconcepts-ts -x c++ -verify %s
+
+template<typename T>
+concept C1 = sizeof(T) == 1;
+// expected-note at -1 2{{because 'sizeof(short) == 1' (2 == 1) evaluated to false}}
+// expected-note at -2 {{because 'sizeof(int) == 1' (4 == 1) evaluated to false}}
+
+template<C1 T> // expected-note {{because 'int' does not satisfy 'C1'}}
+using A = T;
+
+using a1 = A<int>; // expected-error {{constraints not satisfied for alias template 'A' [with T = int]}}
+using a2 = A<char>;
+
+template<typename T>
+concept C2 = sizeof(T) == 2;
+// expected-note at -1 {{because 'sizeof(char) == 2' (1 == 2) evaluated to false}}
+
+template<C1 T1, C2 T2>
+// expected-note at -1 2{{because 'short' does not satisfy 'C1'}}
+// expected-note at -2 {{because 'char' does not satisfy 'C2'}}
+using B = T1;
+
+using b1 = B<char, short>;
+using b2 = B<char, char>; // expected-error {{constraints not satisfied for alias template 'B' [with T1 = char, T2 = char]}}
+using b3 = B<short, short>; // expected-error {{constraints not satisfied for alias template 'B' [with T1 = short, T2 = short]}}
+using b4 = B<short, char>; // expected-error {{constraints not satisfied for alias template 'B' [with T1 = short, T2 = char]}}
+
+template<typename... T>
+concept C3 = (sizeof(T) + ...) == 12;
+// expected-note at -1 {{because 'sizeof(char [11]) == 12' (11 == 12) evaluated to false}}
+// expected-note at -2 {{because 'sizeof(char [10]) == 12' (10 == 12) evaluated to false}}
+// expected-note at -3 3{{because 'sizeof(int) == 12' (4 == 12) evaluated to false}}
+// expected-note at -4 6{{because 'sizeof(short) == 12' (2 == 12) evaluated to false}}
+
+template<C3 T1, C3 T2, C3 T3>
+// expected-note at -1 {{because 'char [11]' does not satisfy 'C3'}}
+// expected-note at -2 {{because 'char [10]' does not satisfy 'C3'}}
+using C = T2;
+
+using c1 = C<char[12], int[3], short[6]>;
+using c2 = C<char[12], char[11], char[10]>;
+// expected-error at -1 {{constraints not satisfied for alias template 'C' [with T1 = char [12], T2 = char [11], T3 = char [10]]}}
+using c3 = C<char[12], char[12], char[10]>;
+// expected-error at -1 {{constraints not satisfied for alias template 'C' [with T1 = char [12], T2 = char [12], T3 = char [10]]}}
+
+template<C3... Ts>
+// expected-note at -1 {{because 'int' does not satisfy 'C3'}}
+// expected-note at -2 2{{and 'int' does not satisfy 'C3'}}
+// expected-note at -3 {{because 'short' does not satisfy 'C3'}}
+// expected-note at -4 5{{and 'short' does not satisfy 'C3'}}
+using D = int;
+
+using d1 = D<char[12], int[3], short[6]>;
+using d2 = D<int, int, int>;
+// expected-error at -1 {{constraints not satisfied for alias template 'D' [with Ts = <int, int, int>}}
+using d3 = D<short, short, short, short, short, short>;
+// expected-error at -1 {{constraints not satisfied for alias template 'D' [with Ts = <short, short, short, short, short, short>}}
+
+template<typename T>
+concept C4 = sizeof(T) == 4;
+// expected-note at -1 3{{because 'sizeof(char) == 4' (1 == 4) evaluated to false}}
+
+template<C4... Ts>
+// expected-note at -1 2{{because 'char' does not satisfy 'C4'}}
+// expected-note at -2 {{and 'char' does not satisfy 'C4'}}
+using E = int;
+
+using e1 = E<int>;
+using e2 = E<char, int>; // expected-error {{constraints not satisfied for alias template 'E' [with Ts = <char, int>]}}
+using e3 = E<char, char>; // expected-error {{constraints not satisfied for alias template 'E' [with Ts = <char, char>]}}
+using e4 = E<>;
+
+template<typename T, typename U>
+constexpr bool is_same_v = false;
+
+template<typename T>
+constexpr bool is_same_v<T, T> = true;
+
+template<typename T, typename U>
+concept Same = is_same_v<T, U>; // expected-note {{because 'is_same_v<long, int>' evaluated to false}}
+
+template<Same<int> T> // expected-note {{because 'Same<long, int>' evaluated to false}}
+using F = T;
+
+using f1 = F<int>;
+using f2 = F<long>; // expected-error {{constraints not satisfied for alias template 'F' [with T = long]}}
+
+template<typename T, typename... Ts>
+concept OneOf = (is_same_v<T, Ts> || ...);
+// expected-note at -1 2{{because 'is_same_v<char, char [1]>' evaluated to false}}
+// expected-note at -2 2{{and 'is_same_v<char, char [2]>' evaluated to false}}
+// expected-note at -3 {{because 'is_same_v<short, int>' evaluated to false}}
+// expected-note at -4 {{and 'is_same_v<short, long>' evaluated to false}}
+// expected-note at -5 {{and 'is_same_v<short, char>' evaluated to false}}
+// expected-note at -6 3{{because 'is_same_v<int, char [1]>' evaluated to false}}
+// expected-note at -7 3{{and 'is_same_v<int, char [2]>' evaluated to false}}
+
+template<OneOf<char[1], char[2]> T, OneOf<int, long, char> U>
+// expected-note at -1 2{{because 'OneOf<char, char [1], char [2]>' evaluated to false}}
+// expected-note at -2 {{because 'OneOf<short, int, long, char>' evaluated to false}}
+using G = T;
+
+using g1 = G<char[1], int>;
+using g2 = G<char, int>; // expected-error{{constraints not satisfied for alias template 'G' [with T = char, U = int]}}
+using g3 = G<char[1], short>; // expected-error{{constraints not satisfied for alias template 'G' [with T = char [1], U = short]}}
+using g4 = G<char, short>; // expected-error{{constraints not satisfied for alias template 'G' [with T = char, U = short]}}
+
+template<OneOf<char[1], char[2]>... Ts>
+// expected-note at -1 2{{because 'OneOf<int, char [1], char [2]>' evaluated to false}}
+// expected-note at -2 {{and 'OneOf<int, char [1], char [2]>' evaluated to false}}
+using H = int;
+
+using h1 = H<char[1], int>;
+// expected-error at -1 {{constraints not satisfied for alias template 'H' [with Ts = <char [1], int>]}}
+using h2 = H<int, int>;
+// expected-error at -1 {{constraints not satisfied for alias template 'H' [with Ts = <int, int>]}}
+using h3 = H<char[1], char[2]>;
\ No newline at end of file
diff --git a/clang/test/Parser/cxx2a-constrained-template-param-with-partial-id.cpp b/clang/test/Parser/cxx2a-constrained-template-param-with-partial-id.cpp
new file mode 100644
index 000000000000..2d80475e1ebf
--- /dev/null
+++ b/clang/test/Parser/cxx2a-constrained-template-param-with-partial-id.cpp
@@ -0,0 +1,39 @@
+// RUN: %clang_cc1 -std=c++2a -fconcepts-ts -x c++ %s -verify
+
+template<typename T, int a>
+concept C1 = true;
+
+template<C1 T>
+// expected-error at -1 {{'C1' requires more than 1 template argument; provide the remaining arguments explicitly to use it here}}
+using badA = T[10];
+
+template<C1<0> T>
+using A = T[10];
+
+using a = A<int>;
+
+namespace ns {
+ template<typename T, typename U, typename... X>
+ concept C2 = true;
+}
+
+template<ns::C2 T1, ::ns::C2 T2>
+// expected-error at -1 2{{'C2' requires more than 1 template argument; provide the remaining arguments explicitly to use it here}}
+requires (sizeof(T1) <= sizeof(T2))
+struct badB { };
+
+template<ns::C2<int> T1, ::ns::C2<char, T1> T2>
+ requires (sizeof(T1) <= sizeof(T2))
+struct B { };
+
+using b = B<int, int>;
+
+template<ns::C2... T1>
+// expected-error at -1 {{'C2' requires more than 1 template argument; provide the remaining arguments explicitly to use it here}}
+struct badC { };
+
+template<ns::C2<int>... T1>
+struct C { };
+
+using c1 = C<char, char, char>;
+using c2 = C<char, char, char, char>;
\ No newline at end of file
diff --git a/clang/test/Parser/cxx2a-constrained-template-param.cpp b/clang/test/Parser/cxx2a-constrained-template-param.cpp
new file mode 100644
index 000000000000..2ee544e970bd
--- /dev/null
+++ b/clang/test/Parser/cxx2a-constrained-template-param.cpp
@@ -0,0 +1,52 @@
+// RUN: %clang_cc1 -std=c++2a -fconcepts-ts -x c++ %s -verify
+
+namespace type
+{
+ template<typename T>
+ concept C1 = true;
+
+ template<C1 T, C1 U = int>
+ using A = T[10];
+
+ using a = A<int>;
+
+ namespace ns {
+ template<typename T, int a = 0>
+ concept C2 = true;
+ }
+
+ template<ns::C2 T1, ::type::ns::C2 T2> requires (sizeof(T1) <= sizeof(T2))
+ struct B { };
+
+ using b = B<int, int>;
+
+ template<ns::C2... T1>
+ struct C { };
+
+ using c1 = C<char, char, char>;
+ using c2 = C<char, char, char, char>;
+}
+
+namespace non_type
+{
+ template<int v>
+ concept C1 = true;
+
+ template<C1 v, C1 u = 0> // expected-error{{expected a type}} // expected-note{{declared here}}
+ // expected-error at -1 2{{concept named in type constraint is not a type concept}}
+ // expected-error at -2 {{expected ',' or '>' in template-parameter-list}}
+ int A = v; // expected-error{{'v' does not refer to a value}}
+}
+
+namespace temp
+{
+ template<typename>
+ struct test1 { }; // expected-note{{template is declared here}}
+
+ template<template<typename> typename T>
+ concept C1 = true;
+
+ template<C1 TT, C1 UU = test1> // expected-error{{use of class template 'test1' requires template arguments}}
+ // expected-error at -1 2{{concept named in type constraint is not a type concept}}
+ using A = TT<int>; // expected-error{{expected ';' after alias declaration}}
+}
\ No newline at end of file
diff --git a/clang/test/SemaTemplate/instantiate-expanded-type-constraint.cpp b/clang/test/SemaTemplate/instantiate-expanded-type-constraint.cpp
new file mode 100644
index 000000000000..4daf31a292c2
--- /dev/null
+++ b/clang/test/SemaTemplate/instantiate-expanded-type-constraint.cpp
@@ -0,0 +1,36 @@
+// RUN: %clang_cc1 -std=c++2a -fconcepts-ts -verify %s
+
+template<typename T, typename U>
+constexpr bool is_same_v = false;
+
+template<typename T>
+constexpr bool is_same_v<T, T> = true;
+
+template<typename T, typename U>
+concept same_as = is_same_v<T, U>;
+// expected-note at -1{{because 'is_same_v<int, _Bool>' evaluated to false}}
+
+template<typename T, typename... Us>
+concept either = (is_same_v<T, Us> || ...);
+
+template<typename... Ts>
+struct T {
+ template<same_as<Ts>... Us>
+ // expected-note at -1{{because 'same_as<int, _Bool>' evaluated to false}}
+ static void foo(Us... u, int x) { };
+ // expected-note at -1{{candidate template ignored: deduced too few arguments}}
+ // expected-note at -2{{candidate template ignored: constraints not satisfied}}
+
+ template<typename... Us>
+ struct S {
+ template<either<Ts, Us...>... Vs>
+ static void foo(Vs... v);
+ };
+};
+
+int main() {
+ T<int, bool>::foo(1); // expected-error{{no matching function for call to 'foo'}}
+ T<int, bool>::foo(1, 2, 3); // expected-error{{no matching function for call to 'foo'}}
+ T<int, bool>::S<char>::foo(1, 'a');
+ T<int, bool>::S<char>::foo('a', true);
+}
diff --git a/clang/test/SemaTemplate/ms-delayed-default-template-args.cpp b/clang/test/SemaTemplate/ms-delayed-default-template-args.cpp
index 0c0546942475..beb64f8273da 100644
--- a/clang/test/SemaTemplate/ms-delayed-default-template-args.cpp
+++ b/clang/test/SemaTemplate/ms-delayed-default-template-args.cpp
@@ -94,12 +94,11 @@ namespace test_undeclared_nontype_parm_arg {
template <typename T> struct Bar { T x; };
template <Bar<Xylophone> *P> // expected-error {{use of undeclared identifier 'Xylophone'}}
-// expected-note at -1 {{template parameter is declared here}}
struct Foo { };
typedef int Xylophone;
Bar<Xylophone> g;
-template struct Foo<&g>; // expected-error {{cannot be converted}}
+template struct Foo<&g>; // expected-error {{value of type}}
}
#endif
diff --git a/clang/tools/libclang/CIndex.cpp b/clang/tools/libclang/CIndex.cpp
index 94084e169faf..6f3490471de5 100644
--- a/clang/tools/libclang/CIndex.cpp
+++ b/clang/tools/libclang/CIndex.cpp
@@ -757,6 +757,11 @@ bool CursorVisitor::VisitClassTemplatePartialSpecializationDecl(
}
bool CursorVisitor::VisitTemplateTypeParmDecl(TemplateTypeParmDecl *D) {
+ if (const auto *TC = D->getTypeConstraint())
+ if (Visit(MakeCXCursor(TC->getImmediatelyDeclaredConstraint(), StmtParent,
+ TU, RegionOfInterest)))
+ return true;
+
// Visit the default argument.
if (D->hasDefaultArgument() && !D->defaultArgumentWasInherited())
if (TypeSourceInfo *DefArg = D->getDefaultArgumentInfo())
@@ -5050,7 +5055,12 @@ CXString clang_getCursorDisplayName(CXCursor C) {
// There is no parameter name, which makes this tricky. Try to come up
// with something useful that isn't too long.
if (TemplateTypeParmDecl *TTP = dyn_cast<TemplateTypeParmDecl>(Param))
- OS << (TTP->wasDeclaredWithTypename()? "typename" : "class");
+ if (const auto *TC = TTP->getTypeConstraint()) {
+ TC->getConceptNameInfo().printName(OS, Policy);
+ if (TC->hasExplicitTemplateArgs())
+ OS << "<...>";
+ } else
+ OS << (TTP->wasDeclaredWithTypename()? "typename" : "class");
else if (NonTypeTemplateParmDecl *NTTP
= dyn_cast<NonTypeTemplateParmDecl>(Param))
OS << NTTP->getType().getAsString(Policy);
More information about the cfe-commits
mailing list