saar.raz updated this revision to Diff 204764.
saar.raz added a comment.
Add support for CSE mangling
Repository:
rC Clang
CHANGES SINCE LAST ACTION
https://reviews.llvm.org/D41217/new/
https://reviews.llvm.org/D41217
Files:
include/clang/AST/ExprCXX.h
include/clang/AST/RecursiveASTVisitor.h
include/clang/Basic/DiagnosticSemaKinds.td
include/clang/Basic/StmtNodes.td
include/clang/Sema/Sema.h
include/clang/Serialization/ASTBitCodes.h
lib/AST/Expr.cpp
lib/AST/ExprCXX.cpp
lib/AST/ExprClassification.cpp
lib/AST/ExprConstant.cpp
lib/AST/ItaniumMangle.cpp
lib/AST/StmtPrinter.cpp
lib/AST/StmtProfile.cpp
lib/CodeGen/CGExprScalar.cpp
lib/Frontend/FrontendActions.cpp
lib/Parse/ParseExpr.cpp
lib/Sema/CMakeLists.txt
lib/Sema/SemaConcept.cpp
lib/Sema/SemaExceptionSpec.cpp
lib/Sema/SemaTemplate.cpp
lib/Sema/SemaTemplateInstantiate.cpp
lib/Sema/TreeTransform.h
lib/Serialization/ASTReaderStmt.cpp
lib/Serialization/ASTWriterStmt.cpp
lib/StaticAnalyzer/Core/ExprEngine.cpp
test/CXX/concepts-ts/expr/expr.prim/expr.prim.id/p3.cpp
test/CodeGenCXX/mangle-concept.cpp
test/Parser/cxx2a-concept-declaration.cpp
tools/libclang/CXCursor.cpp
Index: tools/libclang/CXCursor.cpp
===================================================================
--- tools/libclang/CXCursor.cpp
+++ tools/libclang/CXCursor.cpp
@@ -256,6 +256,7 @@
case Stmt::BinaryConditionalOperatorClass:
case Stmt::TypeTraitExprClass:
case Stmt::CoawaitExprClass:
+ case Stmt::ConceptSpecializationExprClass:
case Stmt::DependentCoawaitExprClass:
case Stmt::CoyieldExprClass:
case Stmt::CXXBindTemporaryExprClass:
Index: test/Parser/cxx2a-concept-declaration.cpp
===================================================================
--- test/Parser/cxx2a-concept-declaration.cpp
+++ test/Parser/cxx2a-concept-declaration.cpp
@@ -14,8 +14,6 @@
// expected-error@-2{{template template parameter requires 'class' after the parameter list}}
// expected-error@-3{{concept template parameter list must have at least one parameter; explicit specialization of concepts is not allowed}}
-template<typename T> concept C2 = 0.f; // expected-error {{constraint expression must be of type 'bool' but is of type 'float'}}
-
struct S1 {
template<typename T> concept C1 = true; // expected-error {{concept declarations may only appear in global or namespace scope}}
};
@@ -26,15 +24,15 @@
template<typename A>
template<typename B>
-concept C4 = true; // expected-error {{extraneous template parameter list in concept definition}}
+concept C2 = true; // expected-error {{extraneous template parameter list in concept definition}}
-template<typename T> concept C5 = true; // expected-note {{previous}} expected-note {{previous}}
-int C5; // expected-error {{redefinition}}
-struct C5 {}; // expected-error {{redefinition}}
+template<typename T> concept C3 = true; // expected-note {{previous}} expected-note {{previous}}
+int C3; // expected-error {{redefinition}}
+struct C3 {}; // expected-error {{redefinition}}
-struct C6 {}; // expected-note{{previous definition is here}}
-template<typename T> concept C6 = true;
-// expected-error@-1{{redefinition of 'C6' as different kind of symbol}}
+struct C4 {}; // expected-note{{previous definition is here}}
+template<typename T> concept C4 = true;
+// expected-error@-1{{redefinition of 'C4' as different kind of symbol}}
// TODO: Add test to prevent explicit specialization, partial specialization
// and explicit instantiation of concepts.
@@ -43,31 +41,55 @@
struct integral_constant { static constexpr T value = v; };
namespace N {
- template<typename T> concept C7 = true;
+ template<typename T> concept C5 = true;
}
-using N::C7;
+using N::C5;
-template <bool word> concept C8 = integral_constant<bool, wor>::value;
+template <bool word> concept C6 = integral_constant<bool, wor>::value;
// expected-error@-1{{use of undeclared identifier 'wor'; did you mean 'word'?}}
// expected-note@-2{{'word' declared here}}
-template<typename T> concept bool C9 = true;
+template<typename T> concept bool C7 = true;
// expected-warning@-1{{ISO C++2a does not permit the 'bool' keyword after 'concept'}}
-template<> concept C10 = false;
+template<> concept C8 = false;
// expected-error@-1{{concept template parameter list must have at least one parameter; explicit specialization of concepts is not allowed}}
-template<> concept C9<int> = false;
+template<> concept C7<int> = false;
// expected-error@-1{{name defined in concept definition must be an identifier}}
-template<typename T> concept N::C11 = false;
+template<typename T> concept N::C9 = false;
// expected-error@-1{{name defined in concept definition must be an identifier}}
class A { };
// expected-note@-1{{'A' declared here}}
-template<typename T> concept A::C12 = false;
+template<typename T> concept A::C10 = false;
// expected-error@-1{{expected namespace name}}
template<typename T> concept operator int = false;
// expected-error@-1{{name defined in concept definition must be an identifier}}
+
+template<bool x> concept C11 = 2; // expected-error {{atomic constraint must be of type 'bool' (found 'int')}}
+template<bool x> concept C12 = 2 && x; // expected-error {{atomic constraint must be of type 'bool' (found 'int')}}
+template<bool x> concept C13 = x || 2 || x; // expected-error {{atomic constraint must be of type 'bool' (found 'int')}}
+template<bool x> concept C14 = 8ull && x || x; // expected-error {{atomic constraint must be of type 'bool' (found 'unsigned long long')}}
+template<typename T> concept C15 = sizeof(T); // expected-error {{atomic constraint must be of type 'bool' (found 'unsigned long')}}
+template<typename T> concept C16 = T{};
+static_assert(!C16<bool>);
+template<typename T> concept C17 = (bool&&)true;
+static_assert(C17<int>);
+template<typename T> concept C18 = (const bool&)true;
+static_assert(C18<int>);
+template<typename T> concept C19 = (const bool)true;
+static_assert(C19<int>);
+template <bool c> concept C20 = integral_constant<bool, c>::value && true;
+static_assert(C20<true>);
+static_assert(!C20<false>);
+template <bool c> concept C21 = integral_constant<bool, c>::value;
+static_assert(C21<true>);
+static_assert(!C21<false>);
+
+template <bool word> concept C22 = integral_constant<bool, wor>::value;
+// expected-error@-1{{use of undeclared identifier 'wor'; did you mean 'word'?}}
+// expected-note@-2{{'word' declared here}}
Index: test/CodeGenCXX/mangle-concept.cpp
===================================================================
--- /dev/null
+++ test/CodeGenCXX/mangle-concept.cpp
@@ -0,0 +1,16 @@
+// RUN: %clang_cc1 -verify -Wno-return-type -Wno-main -std=c++2a -fconcepts-ts -emit-llvm -triple %itanium_abi_triple -o - %s | FileCheck %s
+// expected-no-diagnostics
+
+namespace test1 {
+template <bool> struct S {};
+template <typename> concept C = true;
+template <typename T = int> S<C<T>> f0() { return S<C<T>>{}; }
+template S<C<int>> f0<>();
+// CHECK: void @_ZN5test12f0IiEENS_1SIXL_ZNS_1CIT_EEEEEEv()
+}
+
+template <bool> struct S {};
+template <typename> concept C = true;
+template <typename T = int> S<C<T>> f0() { return S<C<T>>{}; }
+template S<C<int>> f0<>();
+// CHECK: void @_Z2f0IiE1SIXL_Z1CIT_EEEEv()
\ No newline at end of file
Index: test/CXX/concepts-ts/expr/expr.prim/expr.prim.id/p3.cpp
===================================================================
--- test/CXX/concepts-ts/expr/expr.prim/expr.prim.id/p3.cpp
+++ test/CXX/concepts-ts/expr/expr.prim/expr.prim.id/p3.cpp
@@ -1,4 +1,153 @@
// RUN: %clang_cc1 -std=c++2a -fconcepts-ts -verify %s
-template<typename T> concept C = true;
-static_assert(C<int>); // expected-error{{sorry, unimplemented concepts feature concept specialization used}}
+template<typename T> concept C1 = true; // expected-note{{template is declared here}}
+static_assert(C1<int>);
+static_assert(C1);
+// expected-error@-1{{use of concept 'C1' requires template arguments}}
+
+template<typename T> concept C2 = sizeof(T) == 4;
+static_assert(C2<int>);
+static_assert(!C2<long long int>);
+static_assert(C2<char[4]>);
+static_assert(!C2<char[5]>);
+
+template<typename T> concept C3 = sizeof(*T{}) == 4;
+static_assert(C3<int*>);
+static_assert(!C3<long long int>);
+
+struct A {
+ static constexpr int add(int a, int b) {
+ return a + b;
+ }
+};
+struct B {
+ static int add(int a, int b) { // expected-note{{declared here}}
+ return a + b;
+ }
+};
+template<typename U>
+concept C4 = U::add(1, 2) == 3;
+// expected-error@-1{{substitution into constraint expression resulted in a non-constant expression}}
+// expected-note@-2{{non-constexpr function 'add' cannot be used in a constant expression}}
+static_assert(C4<A>);
+static_assert(!C4<B>); // expected-note {{while checking the satisfaction of concept 'C4<B>' requested here}}
+
+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>;
+
+static_assert(Same<int, int>);
+static_assert(Same<int, decltype(1)>);
+static_assert(!Same<int, unsigned int>);
+static_assert(!Same<A, B>);
+static_assert(Same<A, A>);
+
+static_assert(Same<bool, decltype(C1<int>)>);
+static_assert(Same<bool, decltype(C2<int>)>);
+static_assert(Same<bool, decltype(C3<int*>)>);
+static_assert(Same<bool, decltype(C4<A>)>);
+
+template<typename T> concept C5 = T{}; // expected-error {{atomic constraint must be of type 'bool' (found 'int')}}
+constexpr bool x = C5<int>; // expected-note {{while checking the satisfaction of concept 'C5<int>' requested here}}
+
+template<int x>
+concept IsEven = (x % 2) == 0;
+
+static_assert(IsEven<20>);
+static_assert(!IsEven<11>);
+
+template<template<typename T> typename P>
+concept IsTypePredicate = is_same_v<decltype(P<bool>::value), const bool>
+ && is_same_v<decltype(P<int>::value), const bool>
+ && is_same_v<decltype(P<long long>::value), const bool>;
+
+template<typename T> struct T1 {};
+template<typename T> struct T2 { static constexpr bool value = sizeof(T) == 2; };
+
+static_assert(IsTypePredicate<T2>);
+static_assert(!IsTypePredicate<T1>);
+
+namespace piecewise_substitution {
+ template <typename T>
+ concept True = true;
+
+ template <typename T>
+ concept A = True<T> || T::value;
+
+ template <typename T>
+ concept B = (True<T> || T::value);
+
+ template <typename T>
+ concept C = !True<T> && T::value || true;
+ // expected-warning@-1{{'&&' within '||'}}
+ // expected-note@-2{{place parentheses around the '&&' expression to silence this warning}}
+
+ template <typename T>
+ concept D = (!True<T> && T::value) || true;
+
+ template <typename T>
+ concept E = T::value || True<T>;
+
+ template <typename T>
+ concept F = (T::value || True<T>);
+
+ template <typename T>
+ concept G = T::value && !True<T> || true;
+ // expected-warning@-1{{'&&' within '||'}}
+ // expected-note@-2{{place parentheses around the '&&' expression to silence this warning}}
+
+ template <typename T>
+ concept H = (T::value && !True<T>) || true;
+
+ template <typename T>
+ concept I = T::value;
+
+ static_assert(A<int>);
+ static_assert(B<int>);
+ static_assert(C<int>);
+ static_assert(D<int>);
+ static_assert(E<int>);
+ static_assert(F<int>);
+ static_assert(G<int>);
+ static_assert(H<int>);
+ static_assert(!I<int>);
+}
+
+// Short ciruiting
+
+template<typename T> struct T3 { using type = typename T::type; };
+// expected-error@-1{{type 'char' cannot be used prior to '::' because it has no members}}
+// expected-error@-2{{type 'short' cannot be used prior to '::' because it has no members}}
+
+template<typename T>
+concept C6 = sizeof(T) == 1 && sizeof(typename T3<T>::type) == 1;
+// expected-note@-1{{while substituting template arguments into constraint expression here}}
+// expected-note@-2{{in instantiation of template class 'T3<char>' requested here}}
+
+template<typename T>
+concept C7 = sizeof(T) == 1 || sizeof(
+// expected-note@-1{{while substituting template arguments into constraint expression here}}
+ typename
+ T3<T>
+// expected-note@-1{{in instantiation of template class 'T3<short>' requested here}}
+ ::type) == 1;
+
+static_assert(!C6<short>);
+static_assert(!C6<char>); // expected-note{{while checking the satisfaction of concept 'C6<char>' requested here}}
+static_assert(C7<char>);
+static_assert(!C7<short>); // expected-note{{while checking the satisfaction of concept 'C7<short>' requested here}}
+
+// Make sure argument list is converted when instantiating a CSE.
+
+template<typename T, typename U = int>
+concept SameSize = sizeof(T) == sizeof(U);
+
+template<typename T>
+struct X { static constexpr bool a = SameSize<T>; };
+
+static_assert(X<unsigned>::a);
Index: lib/StaticAnalyzer/Core/ExprEngine.cpp
===================================================================
--- lib/StaticAnalyzer/Core/ExprEngine.cpp
+++ lib/StaticAnalyzer/Core/ExprEngine.cpp
@@ -1320,6 +1320,7 @@
case Stmt::CUDAKernelCallExprClass:
case Stmt::OpaqueValueExprClass:
case Stmt::AsTypeExprClass:
+ case Stmt::ConceptSpecializationExprClass:
// Fall through.
// Cases we intentionally don't evaluate, since they don't need
Index: lib/Serialization/ASTWriterStmt.cpp
===================================================================
--- lib/Serialization/ASTWriterStmt.cpp
+++ lib/Serialization/ASTWriterStmt.cpp
@@ -384,6 +384,24 @@
Code = serialization::EXPR_DEPENDENT_COAWAIT;
}
+void ASTStmtWriter::VisitConceptSpecializationExpr(
+ ConceptSpecializationExpr *E) {
+ VisitExpr(E);
+ ArrayRef<TemplateArgument> TemplateArgs = E->getTemplateArguments();
+ Record.push_back(TemplateArgs.size());
+ Record.AddNestedNameSpecifierLoc(E->getNestedNameSpecifierLoc());
+ Record.AddSourceLocation(E->getTemplateKWLoc());
+ Record.AddSourceLocation(E->getConceptNameLoc());
+ Record.AddDeclRef(E->getFoundDecl());
+ Record.AddDeclRef(E->getNamedConcept());
+ Record.AddASTTemplateArgumentListInfo(E->getTemplateArgsAsWritten());
+ for (const TemplateArgument &Arg : TemplateArgs)
+ Record.AddTemplateArgument(Arg);
+ Record.push_back(E->isSatisfied());
+ Code = serialization::EXPR_CONCEPT_SPECIALIZATION;
+}
+
+
void ASTStmtWriter::VisitCapturedStmt(CapturedStmt *S) {
VisitStmt(S);
// NumCaptures
Index: lib/Serialization/ASTReaderStmt.cpp
===================================================================
--- lib/Serialization/ASTReaderStmt.cpp
+++ lib/Serialization/ASTReaderStmt.cpp
@@ -713,6 +713,24 @@
E->setRParenLoc(ReadSourceLocation());
}
+void ASTStmtReader::VisitConceptSpecializationExpr(
+ ConceptSpecializationExpr *E) {
+ VisitExpr(E);
+ unsigned NumTemplateArgs = Record.readInt();
+ E->NestedNameSpec = Record.readNestedNameSpecifierLoc();
+ E->TemplateKWLoc = Record.readSourceLocation();
+ E->ConceptNameLoc = Record.readSourceLocation();
+ E->FoundDecl = ReadDeclAs<NamedDecl>();
+ E->NamedConcept.setPointer(ReadDeclAs<ConceptDecl>());
+ const ASTTemplateArgumentListInfo *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->NamedConcept.setInt(Record.readInt() == 1);
+}
+
void ASTStmtReader::VisitArraySubscriptExpr(ArraySubscriptExpr *E) {
VisitExpr(E);
E->setLHS(Record.readSubExpr());
@@ -3399,6 +3417,12 @@
case EXPR_DEPENDENT_COAWAIT:
S = new (Context) DependentCoawaitExpr(Empty);
break;
+
+ case EXPR_CONCEPT_SPECIALIZATION:
+ unsigned numTemplateArgs = Record[ASTStmtReader::NumExprFields];
+ S = ConceptSpecializationExpr::Create(Context, Empty, numTemplateArgs);
+ break;
+
}
// We hit a STMT_STOP, so we're done with this expression.
Index: lib/Sema/TreeTransform.h
===================================================================
--- lib/Sema/TreeTransform.h
+++ lib/Sema/TreeTransform.h
@@ -2977,6 +2977,25 @@
///
/// By default, performs semantic analysis to build the new expression.
/// Subclasses may override this routine to provide different behavior.
+ ExprResult RebuildConceptSpecializationExpr(NestedNameSpecifierLoc NNS,
+ SourceLocation TemplateKWLoc, SourceLocation ConceptNameLoc,
+ NamedDecl *FoundDecl, ConceptDecl *NamedConcept,
+ TemplateArgumentListInfo *TALI) {
+ CXXScopeSpec SS;
+ SS.Adopt(NNS);
+ ExprResult Result = getSema().CheckConceptTemplateId(SS, TemplateKWLoc,
+ ConceptNameLoc,
+ FoundDecl,
+ NamedConcept, TALI);
+ if (Result.isInvalid())
+ return ExprError();
+ return Result;
+ }
+
+ /// \brief Build a new Objective-C boxed expression.
+ ///
+ /// By default, performs semantic analysis to build the new expression.
+ /// Subclasses may override this routine to provide different behavior.
ExprResult RebuildObjCBoxedExpr(SourceRange SR, Expr *ValueExpr) {
return getSema().BuildObjCBoxedExpr(SR, ValueExpr);
}
@@ -10850,6 +10869,23 @@
E->getEndLoc());
}
+template<typename Derived>
+ExprResult
+TreeTransform<Derived>::TransformConceptSpecializationExpr(
+ ConceptSpecializationExpr *E) {
+ const ASTTemplateArgumentListInfo *Old = E->getTemplateArgsAsWritten();
+ TemplateArgumentListInfo TransArgs(Old->LAngleLoc, Old->RAngleLoc);
+ if (getDerived().TransformTemplateArguments(Old->getTemplateArgs(),
+ Old->NumTemplateArgs, TransArgs))
+ return ExprError();
+
+ return getDerived().RebuildConceptSpecializationExpr(
+ E->getNestedNameSpecifierLoc(), E->getTemplateKWLoc(),
+ E->getConceptNameLoc(), E->getFoundDecl(), E->getNamedConcept(),
+ &TransArgs);
+}
+
+
template<typename Derived>
ExprResult
TreeTransform<Derived>::TransformArrayTypeTraitExpr(ArrayTypeTraitExpr *E) {
Index: lib/Sema/SemaTemplateInstantiate.cpp
===================================================================
--- lib/Sema/SemaTemplateInstantiate.cpp
+++ lib/Sema/SemaTemplateInstantiate.cpp
@@ -194,12 +194,14 @@
case ExplicitTemplateArgumentSubstitution:
case DeducedTemplateArgumentSubstitution:
case PriorTemplateArgumentSubstitution:
+ case ConstraintsCheck:
return true;
case DefaultTemplateArgumentChecking:
case DeclaringSpecialMember:
case DefiningSynthesizedFunction:
case ExceptionSpecEvaluation:
+ case ConstraintSubstitution:
return false;
// This function should never be called when Kind's value is Memoization.
@@ -354,6 +356,24 @@
PointOfInstantiation, InstantiationRange, Param, Template,
TemplateArgs) {}
+Sema::InstantiatingTemplate::InstantiatingTemplate(
+ Sema &SemaRef, SourceLocation PointOfInstantiation,
+ ConstraintsCheck, TemplateDecl *Template,
+ ArrayRef<TemplateArgument> TemplateArgs, SourceRange InstantiationRange)
+ : InstantiatingTemplate(
+ SemaRef, CodeSynthesisContext::ConstraintsCheck,
+ PointOfInstantiation, InstantiationRange, Template, nullptr,
+ TemplateArgs) {}
+
+Sema::InstantiatingTemplate::InstantiatingTemplate(
+ Sema &SemaRef, SourceLocation PointOfInstantiation,
+ ConstraintSubstitution, TemplateDecl *Template,
+ sema::TemplateDeductionInfo &DeductionInfo, SourceRange InstantiationRange)
+ : InstantiatingTemplate(
+ SemaRef, CodeSynthesisContext::ConstraintSubstitution,
+ PointOfInstantiation, InstantiationRange, Template, nullptr,
+ {}, &DeductionInfo) {}
+
void Sema::pushCodeSynthesisContext(CodeSynthesisContext Ctx) {
Ctx.SavedInNonInstantiationSFINAEContext = InNonInstantiationSFINAEContext;
InNonInstantiationSFINAEContext = false;
@@ -491,11 +511,12 @@
Diags.Report(Active->PointOfInstantiation,
diag::note_template_nsdmi_here)
<< FD << Active->InstantiationRange;
- } else {
+ } else if (auto *AD = dyn_cast<TypeAliasTemplateDecl>(D)) {
Diags.Report(Active->PointOfInstantiation,
diag::note_template_type_alias_instantiation_here)
- << cast<TypeAliasTemplateDecl>(D)
+ << AD
<< Active->InstantiationRange;
+ } else {
}
break;
}
@@ -655,6 +676,30 @@
case CodeSynthesisContext::Memoization:
break;
+
+ case CodeSynthesisContext::ConstraintsCheck:
+ if (auto *CD = dyn_cast<ConceptDecl>(Active->Entity)) {
+ SmallVector<char, 128> TemplateArgsStr;
+ llvm::raw_svector_ostream OS(TemplateArgsStr);
+ CD->printName(OS);
+ printTemplateArgumentList(OS, Active->template_arguments(),
+ getPrintingPolicy());
+ Diags.Report(Active->PointOfInstantiation,
+ diag::note_concept_specialization_here)
+ << OS.str()
+ << Active->InstantiationRange;
+ break;
+ }
+ // TODO: Concepts - implement this for constrained templates and partial
+ // specializations.
+ llvm_unreachable("only concept constraints are supported right now");
+ break;
+
+ case CodeSynthesisContext::ConstraintSubstitution:
+ Diags.Report(Active->PointOfInstantiation,
+ diag::note_constraint_substitution_here)
+ << Active->InstantiationRange;
+ break;
}
}
}
@@ -678,6 +723,7 @@
LLVM_FALLTHROUGH;
case CodeSynthesisContext::DefaultFunctionArgumentInstantiation:
case CodeSynthesisContext::ExceptionSpecInstantiation:
+ case CodeSynthesisContext::ConstraintsCheck:
// This is a template instantiation, so there is no SFINAE.
return None;
@@ -691,8 +737,10 @@
case CodeSynthesisContext::ExplicitTemplateArgumentSubstitution:
case CodeSynthesisContext::DeducedTemplateArgumentSubstitution:
- // We're either substitution explicitly-specified template arguments
- // or deduced template arguments, so SFINAE applies.
+ case CodeSynthesisContext::ConstraintSubstitution:
+ // We're either substituting explicitly-specified template arguments
+ // or deduced template arguments or a constraint expression, so SFINAE
+ // applies.
assert(Active->DeductionInfo && "Missing deduction info pointer");
return Active->DeductionInfo;
Index: lib/Sema/SemaTemplate.cpp
===================================================================
--- lib/Sema/SemaTemplate.cpp
+++ lib/Sema/SemaTemplate.cpp
@@ -4137,14 +4137,45 @@
ExprResult
Sema::CheckConceptTemplateId(const CXXScopeSpec &SS,
- const DeclarationNameInfo &NameInfo,
- ConceptDecl *Template,
- SourceLocation TemplateLoc,
+ SourceLocation TemplateKWLoc,
+ SourceLocation ConceptNameLoc,
+ NamedDecl *FoundDecl,
+ ConceptDecl *NamedConcept,
const TemplateArgumentListInfo *TemplateArgs) {
- // TODO: Do concept specialization here.
- Diag(NameInfo.getBeginLoc(), diag::err_concept_not_implemented) <<
- "concept specialization";
- return ExprError();
+ assert(NamedConcept && "A concept template id without a template?");
+
+ llvm::SmallVector<TemplateArgument, 4> Converted;
+ if (CheckTemplateArgumentList(NamedConcept, ConceptNameLoc,
+ const_cast<TemplateArgumentListInfo&>(*TemplateArgs),
+ /*PartialTemplateArgs=*/false, Converted,
+ /*UpdateArgsWithConversion=*/false))
+ return ExprError();
+
+ bool IsSatisfied = true;
+ bool IsInstantiationDependent = false;
+ for (TemplateArgument &Arg : Converted) {
+ if (Arg.isInstantiationDependent()) {
+ IsInstantiationDependent = true;
+ break;
+ }
+ }
+ if (!IsInstantiationDependent) {
+ InstantiatingTemplate Inst(*this, ConceptNameLoc,
+ InstantiatingTemplate::ConstraintsCheck{}, NamedConcept, Converted,
+ SourceRange(SS.isSet() ? SS.getBeginLoc() : ConceptNameLoc,
+ TemplateArgs->getRAngleLoc()));
+ MultiLevelTemplateArgumentList MLTAL;
+ MLTAL.addOuterTemplateArguments(Converted);
+ if (CalculateConstraintSatisfaction(NamedConcept, MLTAL,
+ NamedConcept->getConstraintExpr(),
+ IsSatisfied))
+ return ExprError();
+ }
+ return ConceptSpecializationExpr::Create(Context,
+ SS.isSet() ? SS.getWithLocInContext(Context) : NestedNameSpecifierLoc{},
+ TemplateKWLoc, ConceptNameLoc, FoundDecl, NamedConcept,
+ ASTTemplateArgumentListInfo::Create(Context, *TemplateArgs), Converted,
+ IsSatisfied);
}
ExprResult Sema::BuildTemplateIdExpr(const CXXScopeSpec &SS,
@@ -4189,9 +4220,10 @@
}
if (R.getAsSingle<ConceptDecl>() && !AnyDependentArguments()) {
- return CheckConceptTemplateId(SS, R.getLookupNameInfo(),
- R.getAsSingle<ConceptDecl>(),
- TemplateKWLoc, TemplateArgs);
+ return CheckConceptTemplateId(SS, TemplateKWLoc,
+ R.getLookupNameInfo().getBeginLoc(),
+ R.getFoundDecl(),
+ R.getAsSingle<ConceptDecl>(), TemplateArgs);
}
// We don't want lookup warnings at this point.
@@ -7887,20 +7919,7 @@
ConceptDecl *NewDecl = ConceptDecl::Create(Context, DC, NameLoc, Name,
TemplateParameterLists.front(),
ConstraintExpr);
-
- if (!ConstraintExpr->isTypeDependent() &&
- ConstraintExpr->getType() != Context.BoolTy) {
- // C++2a [temp.constr.atomic]p3:
- // E shall be a constant expression of type bool.
- // TODO: Do this check for individual atomic constraints
- // and not the constraint expression. Probably should do it in
- // ParseConstraintExpression.
- Diag(ConstraintExpr->getSourceRange().getBegin(),
- diag::err_concept_initialized_with_non_bool_type)
- << ConstraintExpr->getType();
- NewDecl->setInvalidDecl();
- }
-
+
if (NewDecl->getAssociatedConstraints()) {
// C++2a [temp.concept]p4:
// A concept shall not have associated constraints.
Index: lib/Sema/SemaExceptionSpec.cpp
===================================================================
--- lib/Sema/SemaExceptionSpec.cpp
+++ lib/Sema/SemaExceptionSpec.cpp
@@ -1289,6 +1289,7 @@
case Expr::PredefinedExprClass:
case Expr::SizeOfPackExprClass:
case Expr::StringLiteralClass:
+ case Expr::ConceptSpecializationExprClass:
// These expressions can never throw.
return CT_Cannot;
Index: lib/Sema/SemaConcept.cpp
===================================================================
--- /dev/null
+++ lib/Sema/SemaConcept.cpp
@@ -0,0 +1,122 @@
+//===-- SemaConcept.cpp - Semantic Analysis for Constraints and Concepts --===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file implements semantic analysis for C++ constraints and concepts.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/Sema/Sema.h"
+#include "clang/Sema/SemaDiagnostic.h"
+#include "clang/Sema/TemplateDeduction.h"
+#include "clang/Sema/Template.h"
+#include "clang/AST/ExprCXX.h"
+using namespace clang;
+using namespace sema;
+
+bool Sema::CheckConstraintExpression(Expr *ConstraintExpression) {
+ // C++2a [temp.constr.atomic]p1
+ // ..E shall be a constant expression of type bool.
+
+ if (auto *BinOp = dyn_cast<BinaryOperator>(ConstraintExpression))
+ if (BinOp->getOpcode() == BO_LAnd || BinOp->getOpcode() == BO_LOr)
+ return CheckConstraintExpression(BinOp->getLHS()) &&
+ CheckConstraintExpression(BinOp->getRHS());
+
+ // An atomic constraint!
+ if (ConstraintExpression->isTypeDependent())
+ return true;
+
+ QualType Type = ConstraintExpression->IgnoreParenImpCasts()->getType()
+ .getNonReferenceType().getUnqualifiedType();
+ if (!Context.hasSameType(Type, Context.BoolTy)) {
+ Diag(ConstraintExpression->getExprLoc(),
+ diag::err_non_bool_atomic_constraint) << Type
+ << ConstraintExpression->getSourceRange();
+ return false;
+ }
+ return true;
+}
+
+bool
+Sema::CalculateConstraintSatisfaction(ConceptDecl *NamedConcept,
+ MultiLevelTemplateArgumentList &MLTAL,
+ Expr *ConstraintExpr,
+ bool &IsSatisfied) {
+ if (auto *BO = dyn_cast<BinaryOperator>(ConstraintExpr)) {
+ if (BO->getOpcode() == BO_LAnd) {
+ if (CalculateConstraintSatisfaction(NamedConcept, MLTAL, BO->getLHS(),
+ IsSatisfied))
+ return true;
+ if (!IsSatisfied)
+ return false;
+ return CalculateConstraintSatisfaction(NamedConcept, MLTAL, BO->getRHS(),
+ IsSatisfied);
+ } else if (BO->getOpcode() == BO_LOr) {
+ if (CalculateConstraintSatisfaction(NamedConcept, MLTAL, BO->getLHS(),
+ IsSatisfied))
+ return true;
+ if (IsSatisfied)
+ return false;
+ return CalculateConstraintSatisfaction(NamedConcept, MLTAL, BO->getRHS(),
+ IsSatisfied);
+ }
+ } else if (auto *PO = dyn_cast<ParenExpr>(ConstraintExpr))
+ return CalculateConstraintSatisfaction(NamedConcept, MLTAL,
+ PO->getSubExpr(), IsSatisfied);
+ else if (auto *C = dyn_cast<ExprWithCleanups>(ConstraintExpr))
+ return CalculateConstraintSatisfaction(NamedConcept, MLTAL, C->getSubExpr(),
+ IsSatisfied);
+
+ EnterExpressionEvaluationContext ConstantEvaluated(
+ *this, Sema::ExpressionEvaluationContext::ConstantEvaluated);
+
+ // Atomic constraint - substitute arguments and check satisfaction.
+ ExprResult E;
+ {
+ TemplateDeductionInfo Info(ConstraintExpr->getBeginLoc());
+ InstantiatingTemplate Inst(*this, ConstraintExpr->getBeginLoc(),
+ InstantiatingTemplate::ConstraintSubstitution{},
+ NamedConcept, Info,
+ ConstraintExpr->getSourceRange());
+ if (Inst.isInvalid())
+ return true;
+ // We do not want error diagnostics escaping here.
+ Sema::SFINAETrap Trap(*this);
+
+ E = SubstExpr(ConstraintExpr, MLTAL);
+ if (E.isInvalid() || Trap.hasErrorOccurred()) {
+ // C++2a [temp.constr.atomic]p1
+ // ...If substitution results in an invalid type or expression, the
+ // constraint is not satisfied.
+ IsSatisfied = false;
+ return false;
+ }
+ }
+
+ if (!CheckConstraintExpression(E.get()))
+ return true;
+
+ SmallVector<PartialDiagnosticAt, 2> EvaluationDiags;
+ Expr::EvalResult EvalResult;
+ EvalResult.Diag = &EvaluationDiags;
+ if (!E.get()->EvaluateAsRValue(EvalResult, Context)) {
+ // C++2a [temp.constr.atomic]p1
+ // ...E shall be a constant expression of type bool.
+ Diag(E.get()->getBeginLoc(),
+ diag::err_non_constant_constraint_expression)
+ << E.get()->getSourceRange();
+ for (const PartialDiagnosticAt &PDiag : EvaluationDiags)
+ Diag(PDiag.first, PDiag.second);
+ return true;
+ }
+
+ IsSatisfied = EvalResult.Val.getInt().getBoolValue();
+
+ return false;
+}
Index: lib/Sema/CMakeLists.txt
===================================================================
--- lib/Sema/CMakeLists.txt
+++ lib/Sema/CMakeLists.txt
@@ -27,6 +27,7 @@
SemaCast.cpp
SemaChecking.cpp
SemaCodeComplete.cpp
+ SemaConcept.cpp
SemaConsumer.cpp
SemaCoroutine.cpp
SemaCUDA.cpp
Index: lib/Parse/ParseExpr.cpp
===================================================================
--- lib/Parse/ParseExpr.cpp
+++ lib/Parse/ParseExpr.cpp
@@ -228,18 +228,16 @@
/// Parse a constraint-expression.
///
/// \verbatim
-/// constraint-expression: [Concepts TS temp.constr.decl p1]
+/// constraint-expression: C++2a[temp.constr.decl]p1
/// logical-or-expression
/// \endverbatim
ExprResult Parser::ParseConstraintExpression() {
- // FIXME: this may erroneously consume a function-body as the braced
- // initializer list of a compound literal
- //
- // FIXME: this may erroneously consume a parenthesized rvalue reference
- // declarator as a parenthesized address-of-label expression
+ EnterExpressionEvaluationContext ConstantEvaluated(
+ Actions, Sema::ExpressionEvaluationContext::ConstantEvaluated);
ExprResult LHS(ParseCastExpression(/*isUnaryExpression=*/false));
ExprResult Res(ParseRHSOfBinaryExpression(LHS, prec::LogicalOr));
-
+ if (Res.isUsable() && !Actions.CheckConstraintExpression(Res.get()))
+ return ExprError();
return Res;
}
Index: lib/Frontend/FrontendActions.cpp
===================================================================
--- lib/Frontend/FrontendActions.cpp
+++ lib/Frontend/FrontendActions.cpp
@@ -416,6 +416,10 @@
return "DefiningSynthesizedFunction";
case CodeSynthesisContext::Memoization:
return "Memoization";
+ case CodeSynthesisContext::ConstraintsCheck:
+ return "ConstraintsCheck";
+ case CodeSynthesisContext::ConstraintSubstitution:
+ return "ConstraintSubstitution";
}
return "";
}
Index: lib/CodeGen/CGExprScalar.cpp
===================================================================
--- lib/CodeGen/CGExprScalar.cpp
+++ lib/CodeGen/CGExprScalar.cpp
@@ -665,6 +665,10 @@
return llvm::ConstantInt::get(ConvertType(E->getType()), E->getValue());
}
+ Value *VisitConceptSpecializationExpr(const ConceptSpecializationExpr *E) {
+ return Builder.getInt1(E->isSatisfied());
+ }
+
Value *VisitArrayTypeTraitExpr(const ArrayTypeTraitExpr *E) {
return llvm::ConstantInt::get(Builder.getInt32Ty(), E->getValue());
}
Index: lib/AST/StmtProfile.cpp
===================================================================
--- lib/AST/StmtProfile.cpp
+++ lib/AST/StmtProfile.cpp
@@ -1294,6 +1294,14 @@
ID.AddInteger(S->getOp());
}
+void StmtProfiler::VisitConceptSpecializationExpr(
+ const ConceptSpecializationExpr *S) {
+ VisitExpr(S);
+ VisitDecl(S->getNamedConcept());
+ VisitTemplateArguments(S->getTemplateArgsAsWritten()->getTemplateArgs(),
+ S->getTemplateArgsAsWritten()->NumTemplateArgs);
+}
+
static Stmt::StmtClass DecodeOperatorCall(const CXXOperatorCallExpr *S,
UnaryOperatorKind &UnaryOp,
BinaryOperatorKind &BinaryOp) {
Index: lib/AST/StmtPrinter.cpp
===================================================================
--- lib/AST/StmtPrinter.cpp
+++ lib/AST/StmtPrinter.cpp
@@ -2187,6 +2187,17 @@
OS << ")";
}
+void StmtPrinter::VisitConceptSpecializationExpr(ConceptSpecializationExpr *E) {
+ NestedNameSpecifierLoc NNS = E->getNestedNameSpecifierLoc();
+ if (NNS)
+ NNS.getNestedNameSpecifier()->print(OS, Policy);
+ if (E->getTemplateKWLoc().isValid())
+ OS << "template ";
+ OS << E->getFoundDecl()->getName();
+ printTemplateArgumentList(OS, E->getTemplateArgsAsWritten()->arguments(),
+ Policy);
+}
+
// C++ Coroutines TS
void StmtPrinter::VisitCoroutineBodyStmt(CoroutineBodyStmt *S) {
Index: lib/AST/ItaniumMangle.cpp
===================================================================
--- lib/AST/ItaniumMangle.cpp
+++ lib/AST/ItaniumMangle.cpp
@@ -964,7 +964,7 @@
assert(!AdditionalAbiTags &&
"template template param cannot have abi tags");
mangleTemplateParameter(TTP->getIndex());
- } else if (isa<BuiltinTemplateDecl>(ND)) {
+ } else if (isa<BuiltinTemplateDecl>(ND) || isa<ConceptDecl>(ND)) {
mangleUnscopedName(ND, AdditionalAbiTags);
} else {
mangleUnscopedName(ND->getTemplatedDecl(), AdditionalAbiTags);
@@ -1832,7 +1832,7 @@
mangleTemplateParameter(TTP->getIndex());
} else {
manglePrefix(getEffectiveDeclContext(ND), NoFunction);
- if (isa<BuiltinTemplateDecl>(ND))
+ if (isa<BuiltinTemplateDecl>(ND) || isa<ConceptDecl>(ND))
mangleUnqualifiedName(ND, nullptr);
else
mangleUnqualifiedName(ND->getTemplatedDecl(), nullptr);
@@ -4063,6 +4063,18 @@
mangleExpression(cast<ParenExpr>(E)->getSubExpr(), Arity);
break;
+
+ case Expr::ConceptSpecializationExprClass: {
+ // <expr-primary> ::= L <mangled-name> E # external name
+ Out << "L_Z";
+ auto *CSE = cast<ConceptSpecializationExpr>(E);
+ mangleTemplateName(CSE->getNamedConcept(),
+ CSE->getTemplateArguments().data(),
+ CSE->getTemplateArguments().size());
+ Out << 'E';
+ break;
+ }
+
case Expr::DeclRefExprClass: {
const NamedDecl *D = cast<DeclRefExpr>(E)->getDecl();
Index: lib/AST/ExprConstant.cpp
===================================================================
--- lib/AST/ExprConstant.cpp
+++ lib/AST/ExprConstant.cpp
@@ -7491,6 +7491,7 @@
bool VisitCXXNoexceptExpr(const CXXNoexceptExpr *E);
bool VisitSizeOfPackExpr(const SizeOfPackExpr *E);
+ bool VisitConceptSpecializationExpr(const ConceptSpecializationExpr *E);
// FIXME: Missing: array subscript of vector, member of vector
};
@@ -9942,6 +9943,12 @@
return Success(E->getValue(), E);
}
+bool IntExprEvaluator::VisitConceptSpecializationExpr(
+ const ConceptSpecializationExpr *E) {
+ return Success(E->isSatisfied(), E);
+}
+
+
bool FixedPointExprEvaluator::VisitUnaryOperator(const UnaryOperator *E) {
switch (E->getOpcode()) {
default:
@@ -11411,6 +11418,7 @@
case Expr::CXXBoolLiteralExprClass:
case Expr::CXXScalarValueInitExprClass:
case Expr::TypeTraitExprClass:
+ case Expr::ConceptSpecializationExprClass:
case Expr::ArrayTypeTraitExprClass:
case Expr::ExpressionTraitExprClass:
case Expr::CXXNoexceptExprClass:
Index: lib/AST/ExprClassification.cpp
===================================================================
--- lib/AST/ExprClassification.cpp
+++ lib/AST/ExprClassification.cpp
@@ -191,6 +191,7 @@
case Expr::ArrayInitIndexExprClass:
case Expr::NoInitExprClass:
case Expr::DesignatedInitUpdateExprClass:
+ case Expr::ConceptSpecializationExprClass:
return Cl::CL_PRValue;
case Expr::ConstantExprClass:
Index: lib/AST/ExprCXX.cpp
===================================================================
--- lib/AST/ExprCXX.cpp
+++ lib/AST/ExprCXX.cpp
@@ -28,6 +28,9 @@
#include "clang/Basic/OperatorKinds.h"
#include "clang/Basic/SourceLocation.h"
#include "clang/Basic/Specifiers.h"
+#include "clang/Sema/Template.h"
+#include "clang/Sema/SemaDiagnostic.h"
+#include "clang/Sema/Sema.h"
#include "llvm/ADT/ArrayRef.h"
#include "llvm/Support/Casting.h"
#include "llvm/Support/ErrorHandling.h"
@@ -1657,3 +1660,83 @@
alignof(CUDAKernelCallExpr));
return new (Mem) CUDAKernelCallExpr(NumArgs, Empty);
}
+
+ConceptSpecializationExpr::ConceptSpecializationExpr(ASTContext &C,
+ NestedNameSpecifierLoc NNS, SourceLocation TemplateKWLoc,
+ SourceLocation ConceptNameLoc, NamedDecl *FoundDecl,
+ ConceptDecl *NamedConcept, const ASTTemplateArgumentListInfo *ArgsAsWritten,
+ ArrayRef<TemplateArgument> ConvertedArgs, bool IsSatisfied)
+ : Expr(ConceptSpecializationExprClass, C.BoolTy, VK_RValue, OK_Ordinary,
+ /*TypeDependent=*/false,
+ // All the flags below are set in setTemplateArguments.
+ /*ValueDependent=*/false, /*InstantiationDependent=*/false,
+ /*ContainsUnexpandedParameterPacks=*/false),
+ NestedNameSpec(NNS), TemplateKWLoc(TemplateKWLoc),
+ ConceptNameLoc(ConceptNameLoc), FoundDecl(FoundDecl),
+ NamedConcept(NamedConcept, IsSatisfied),
+ NumTemplateArgs(ConvertedArgs.size()) {
+ setTemplateArguments(ArgsAsWritten, ConvertedArgs);
+}
+
+ConceptSpecializationExpr::ConceptSpecializationExpr(EmptyShell Empty,
+ unsigned NumTemplateArgs)
+ : Expr(ConceptSpecializationExprClass, Empty),
+ 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 IsDependent = false;
+ bool IsInstantiationDependent = false;
+ bool ContainsUnexpandedParameterPack = false;
+ for (const TemplateArgumentLoc& LocInfo : ArgsAsWritten->arguments()) {
+ if (LocInfo.getArgument().isDependent()) {
+ IsDependent = true;
+ if (ContainsUnexpandedParameterPack && IsInstantiationDependent)
+ break;
+ }
+ if (LocInfo.getArgument().isInstantiationDependent()) {
+ IsInstantiationDependent = true;
+ if (ContainsUnexpandedParameterPack && IsDependent)
+ break;
+ }
+ if (LocInfo.getArgument().containsUnexpandedParameterPack()) {
+ ContainsUnexpandedParameterPack = true;
+ if (IsDependent && IsInstantiationDependent)
+ break;
+ }
+ }
+ setValueDependent(IsDependent);
+ setInstantiationDependent(IsInstantiationDependent);
+ setContainsUnexpandedParameterPack(ContainsUnexpandedParameterPack);
+}
+
+ConceptSpecializationExpr *
+ConceptSpecializationExpr::Create(ASTContext &C, NestedNameSpecifierLoc NNS,
+ SourceLocation TemplateKWLoc,
+ SourceLocation ConceptNameLoc,
+ NamedDecl *FoundDecl,
+ ConceptDecl *NamedConcept,
+ const ASTTemplateArgumentListInfo *ArgsAsWritten,
+ ArrayRef<TemplateArgument> ConvertedArgs,
+ bool IsSatisfied) {
+ void *Buffer = C.Allocate(totalSizeToAlloc<TemplateArgument>(
+ ConvertedArgs.size()));
+ return new (Buffer) ConceptSpecializationExpr(C, NNS, TemplateKWLoc,
+ ConceptNameLoc, FoundDecl,
+ NamedConcept, ArgsAsWritten,
+ ConvertedArgs, IsSatisfied);
+}
+
+ConceptSpecializationExpr *
+ConceptSpecializationExpr::Create(ASTContext &C, EmptyShell Empty,
+ unsigned NumTemplateArgs) {
+ void *Buffer = C.Allocate(totalSizeToAlloc<TemplateArgument>(
+ NumTemplateArgs));
+ return new (Buffer) ConceptSpecializationExpr(Empty, NumTemplateArgs);
+}
\ No newline at end of file
Index: lib/AST/Expr.cpp
===================================================================
--- lib/AST/Expr.cpp
+++ lib/AST/Expr.cpp
@@ -3155,6 +3155,7 @@
case ObjCAvailabilityCheckExprClass:
case CXXUuidofExprClass:
case OpaqueValueExprClass:
+ case ConceptSpecializationExprClass:
// These never have a side-effect.
return false;
Index: include/clang/Serialization/ASTBitCodes.h
===================================================================
--- include/clang/Serialization/ASTBitCodes.h
+++ include/clang/Serialization/ASTBitCodes.h
@@ -1906,6 +1906,7 @@
EXPR_FUNCTION_PARM_PACK, // FunctionParmPackExpr
EXPR_MATERIALIZE_TEMPORARY, // MaterializeTemporaryExpr
EXPR_CXX_FOLD, // CXXFoldExpr
+ EXPR_CONCEPT_SPECIALIZATION,// ConceptSpecializationExpr
// CUDA
EXPR_CUDA_KERNEL_CALL, // CUDAKernelCallExpr
Index: include/clang/Sema/Sema.h
===================================================================
--- include/clang/Sema/Sema.h
+++ include/clang/Sema/Sema.h
@@ -5760,7 +5760,16 @@
CXXConversionDecl *Conv,
Expr *Src);
- // ParseObjCStringLiteral - Parse Objective-C string literals.
+ /// Check whether the given expression is a valid constraint expression.
+ /// A diagnostic is emitted if it is not, and false is returned.
+ bool CheckConstraintExpression(Expr *CE);
+
+ bool CalculateConstraintSatisfaction(ConceptDecl *NamedConcept,
+ MultiLevelTemplateArgumentList &MLTAL,
+ Expr *ConstraintExpr,
+ bool &IsSatisfied);
+
+ // ParseObjCStringLiteral - Parse Objective-C string literals.
ExprResult ParseObjCStringLiteral(SourceLocation *AtLocs,
ArrayRef<Expr *> Strings);
@@ -6400,9 +6409,9 @@
ExprResult
CheckConceptTemplateId(const CXXScopeSpec &SS,
- const DeclarationNameInfo &NameInfo,
- ConceptDecl *Template,
- SourceLocation TemplateLoc,
+ SourceLocation TemplateKWLoc,
+ SourceLocation ConceptNameLoc, NamedDecl *FoundDecl,
+ ConceptDecl *NamedConcept,
const TemplateArgumentListInfo *TemplateArgs);
void diagnoseMissingTemplateArguments(TemplateName Name, SourceLocation Loc);
@@ -7321,6 +7330,13 @@
/// member).
DefiningSynthesizedFunction,
+ // We are checking the constraints associated with a constrained entity or
+ // the constraint expression of a concept.
+ ConstraintsCheck,
+
+ // We are substituting template arguments into a constraint expression.
+ ConstraintSubstitution,
+
/// Added for Template instantiation observation.
/// Memoization means we are _not_ instantiating a template because
/// it is already instantiated (but we entered a context where we
@@ -7579,6 +7595,23 @@
ArrayRef<TemplateArgument> TemplateArgs,
SourceRange InstantiationRange);
+ struct ConstraintsCheck {};
+ /// \brief Note that we are checking the constraints associated with some
+ /// constrained entity (a concept declaration or a template with associated
+ /// constraints).
+ InstantiatingTemplate(Sema &SemaRef, SourceLocation PointOfInstantiation,
+ ConstraintsCheck, TemplateDecl *Template,
+ ArrayRef<TemplateArgument> TemplateArgs,
+ SourceRange InstantiationRange);
+
+ struct ConstraintSubstitution {};
+ /// \brief Note that we are checking a constraint expression associated
+ /// with a template declaration or as part of the satisfaction check of a
+ /// concept.
+ InstantiatingTemplate(Sema &SemaRef, SourceLocation PointOfInstantiation,
+ ConstraintSubstitution, TemplateDecl *Template,
+ sema::TemplateDeductionInfo &DeductionInfo,
+ SourceRange InstantiationRange);
/// Note that we have finished instantiating this template.
void Clear();
Index: include/clang/Basic/StmtNodes.td
===================================================================
--- include/clang/Basic/StmtNodes.td
+++ include/clang/Basic/StmtNodes.td
@@ -162,6 +162,9 @@
def DependentCoawaitExpr : DStmt<Expr>;
def CoyieldExpr : DStmt<CoroutineSuspendExpr>;
+// C++2a Concepts expressions
+def ConceptSpecializationExpr : DStmt<Expr>;
+
// Obj-C Expressions.
def ObjCStringLiteral : DStmt<Expr>;
def ObjCBoxedExpr : DStmt<Expr>;
Index: include/clang/Basic/DiagnosticSemaKinds.td
===================================================================
--- include/clang/Basic/DiagnosticSemaKinds.td
+++ include/clang/Basic/DiagnosticSemaKinds.td
@@ -2434,8 +2434,6 @@
"use __attribute__((visibility(\"hidden\"))) attribute instead">;
// C++ Concepts
-def err_concept_initialized_with_non_bool_type : Error<
- "constraint expression must be of type 'bool' but is of type %0">;
def err_concept_decls_may_only_appear_in_global_namespace_scope : Error<
"concept declarations may only appear in global or namespace scope">;
def err_concept_no_parameters : Error<
@@ -2447,6 +2445,11 @@
"concept cannot have associated constraints">;
def err_concept_not_implemented : Error<
"sorry, unimplemented concepts feature %0 used">;
+def err_non_constant_constraint_expression : Error<
+ "substitution into constraint expression resulted in a non-constant "
+ "expression">;
+def err_non_bool_atomic_constraint : Error<
+ "atomic constraint must be of type 'bool' (found %0)">;
def err_template_different_associated_constraints : Error<
"associated constraints differ in template redeclaration">;
@@ -4322,6 +4325,10 @@
" template parameter%1 %2">;
def note_template_default_arg_checking : Note<
"while checking a default template argument used here">;
+def note_concept_specialization_here : Note<
+ "while checking the satisfaction of concept '%0' requested here">;
+def note_constraint_substitution_here : Note<
+ "while substituting template arguments into constraint expression here">;
def note_instantiation_contexts_suppressed : Note<
"(skipping %0 context%s0 in backtrace; use -ftemplate-backtrace-limit=0 to "
"see all)">;
Index: include/clang/AST/RecursiveASTVisitor.h
===================================================================
--- include/clang/AST/RecursiveASTVisitor.h
+++ include/clang/AST/RecursiveASTVisitor.h
@@ -2623,6 +2623,12 @@
}
})
+DEF_TRAVERSE_STMT(ConceptSpecializationExpr, {
+ TRY_TO(TraverseTemplateArgumentLocsHelper(
+ S->getTemplateArgsAsWritten()->getTemplateArgs(),
+ S->getTemplateArgsAsWritten()->NumTemplateArgs));
+})
+
// These literals (all of them) do not need any action.
DEF_TRAVERSE_STMT(IntegerLiteral, {})
DEF_TRAVERSE_STMT(FixedPointLiteral, {})
Index: include/clang/AST/ExprCXX.h
===================================================================
--- include/clang/AST/ExprCXX.h
+++ include/clang/AST/ExprCXX.h
@@ -17,6 +17,7 @@
#include "clang/AST/Decl.h"
#include "clang/AST/DeclBase.h"
#include "clang/AST/DeclCXX.h"
+#include "clang/AST/DeclTemplate.h"
#include "clang/AST/DeclarationName.h"
#include "clang/AST/Expr.h"
#include "clang/AST/NestedNameSpecifier.h"
@@ -56,6 +57,7 @@
class LambdaCapture;
class NonTypeTemplateParmDecl;
class TemplateParameterList;
+class Sema;
//===--------------------------------------------------------------------===//
// C++ Expressions.
@@ -4521,6 +4523,122 @@
}
};
+/// \brief Represents the specialization of a concept - evaluates to a prvalue
+/// of type bool.
+///
+/// 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,
+ private llvm::TrailingObjects<ConceptSpecializationExpr,
+ TemplateArgument> {
+ friend class ASTStmtReader;
+ friend TrailingObjects;
+
+ // \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 differ from NamedConcept when, for example, the concept was found
+ /// through a UsingShadowDecl.
+ NamedDecl *FoundDecl;
+
+ /// \brief The concept named, and whether or not the concept with the given
+ /// arguments was satisfied when the expression was created.
+ /// If any of the template arguments are dependent (this expr would then be
+ /// isValueDependent()), this bit is to be ignored.
+ llvm::PointerIntPair<ConceptDecl *, 1, bool> 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;
+
+ ConceptSpecializationExpr(ASTContext &C, NestedNameSpecifierLoc NNS,
+ SourceLocation TemplateKWLoc,
+ SourceLocation ConceptNameLoc, NamedDecl *FoundDecl,
+ ConceptDecl *NamedConcept,
+ const ASTTemplateArgumentListInfo *ArgsAsWritten,
+ ArrayRef<TemplateArgument> ConvertedArgs,
+ bool IsSatisfied);
+
+ ConceptSpecializationExpr(EmptyShell Empty, unsigned NumTemplateArgs);
+
+public:
+
+ static ConceptSpecializationExpr *
+ Create(ASTContext &C, NestedNameSpecifierLoc NNS,
+ SourceLocation TemplateKWLoc, SourceLocation ConceptNameLoc,
+ NamedDecl *FoundDecl, ConceptDecl *NamedConcept,
+ const ASTTemplateArgumentListInfo *ArgsAsWritten,
+ ArrayRef<TemplateArgument> ConvertedArgs, bool IsSatisfied);
+
+ 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.getPointer();
+ }
+
+ 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);
+
+ /// \brief Whether or not the concept with the given arguments was satisfied
+ /// when the expression was created. This method assumes that the expression
+ /// is not dependent!
+ bool isSatisfied() const {
+ assert(!isValueDependent()
+ && "isSatisfied called on a dependent ConceptSpecializationExpr");
+ return NamedConcept.getInt();
+ }
+
+ 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 getEndLoc() const LLVM_READONLY {
+ return ArgsAsWritten->RAngleLoc;
+ }
+
+ // Iterators
+ child_range children() {
+ return child_range(child_iterator(), child_iterator());
+ }
+};
+
} // namespace clang
#endif // LLVM_CLANG_AST_EXPRCXX_H
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits