[Concepts] Requires Expressions
Implement support for C++2a requires-expressions.
Re-commit after compilation failure on some platforms due to alignment issues with PointerIntPair.
Differential Revision: https://reviews.llvm.org/D50360
diff --git a/clang/lib/Sema/SemaExprCXX.cpp b/clang/lib/Sema/SemaExprCXX.cpp
index 938420d..192c237 100644
--- a/clang/lib/Sema/SemaExprCXX.cpp
+++ b/clang/lib/Sema/SemaExprCXX.cpp
@@ -11,6 +11,7 @@
///
//===----------------------------------------------------------------------===//
+#include "clang/Sema/Template.h"
#include "clang/Sema/SemaInternal.h"
#include "TreeTransform.h"
#include "TypeLocBuilder.h"
@@ -8331,3 +8332,215 @@
return CheckMicrosoftIfExistsSymbol(S, SS, TargetNameInfo);
}
+
+concepts::Requirement *Sema::ActOnSimpleRequirement(Expr *E) {
+ return BuildExprRequirement(E, /*IsSimple=*/true,
+ /*NoexceptLoc=*/SourceLocation(),
+ /*ReturnTypeRequirement=*/{});
+}
+
+concepts::Requirement *
+Sema::ActOnTypeRequirement(SourceLocation TypenameKWLoc, CXXScopeSpec &SS,
+ SourceLocation NameLoc, IdentifierInfo *TypeName,
+ TemplateIdAnnotation *TemplateId) {
+ assert(((!TypeName && TemplateId) || (TypeName && !TemplateId)) &&
+ "Exactly one of TypeName and TemplateId must be specified.");
+ TypeSourceInfo *TSI = nullptr;
+ if (TypeName) {
+ QualType T = CheckTypenameType(ETK_Typename, TypenameKWLoc,
+ SS.getWithLocInContext(Context), *TypeName,
+ NameLoc, &TSI, /*DeducedTypeContext=*/false);
+ if (T.isNull())
+ return nullptr;
+ } else {
+ ASTTemplateArgsPtr ArgsPtr(TemplateId->getTemplateArgs(),
+ TemplateId->NumArgs);
+ TypeResult T = ActOnTypenameType(CurScope, TypenameKWLoc, SS,
+ TemplateId->TemplateKWLoc,
+ TemplateId->Template, TemplateId->Name,
+ TemplateId->TemplateNameLoc,
+ TemplateId->LAngleLoc, ArgsPtr,
+ TemplateId->RAngleLoc);
+ if (T.isInvalid())
+ return nullptr;
+ if (GetTypeFromParser(T.get(), &TSI).isNull())
+ return nullptr;
+ }
+ return BuildTypeRequirement(TSI);
+}
+
+concepts::Requirement *
+Sema::ActOnCompoundRequirement(Expr *E, SourceLocation NoexceptLoc) {
+ return BuildExprRequirement(E, /*IsSimple=*/false, NoexceptLoc,
+ /*ReturnTypeRequirement=*/{});
+}
+
+concepts::Requirement *
+Sema::ActOnCompoundRequirement(
+ Expr *E, SourceLocation NoexceptLoc, CXXScopeSpec &SS,
+ TemplateIdAnnotation *TypeConstraint, unsigned Depth) {
+ // C++2a [expr.prim.req.compound] p1.3.3
+ // [..] the expression is deduced against an invented function template
+ // F [...] F is a void function template with a single type template
+ // parameter T declared with the constrained-parameter. Form a new
+ // cv-qualifier-seq cv by taking the union of const and volatile specifiers
+ // around the constrained-parameter. F has a single parameter whose
+ // type-specifier is cv T followed by the abstract-declarator. [...]
+ //
+ // The cv part is done in the calling function - we get the concept with
+ // arguments and the abstract declarator with the correct CV qualification and
+ // have to synthesize T and the single parameter of F.
+ auto &II = Context.Idents.get("expr-type");
+ auto *TParam = TemplateTypeParmDecl::Create(Context, CurContext,
+ SourceLocation(),
+ SourceLocation(), Depth,
+ /*Index=*/0, &II,
+ /*Typename=*/true,
+ /*ParameterPack=*/false,
+ /*HasTypeConstraint=*/true);
+
+ if (ActOnTypeConstraint(SS, TypeConstraint, TParam,
+ /*EllpsisLoc=*/SourceLocation()))
+ // Just produce a requirement with no type requirements.
+ return BuildExprRequirement(E, /*IsSimple=*/false, NoexceptLoc, {});
+
+ auto *TPL = TemplateParameterList::Create(Context, SourceLocation(),
+ SourceLocation(),
+ ArrayRef<NamedDecl *>(TParam),
+ SourceLocation(),
+ /*RequiresClause=*/nullptr);
+ return BuildExprRequirement(
+ E, /*IsSimple=*/false, NoexceptLoc,
+ concepts::ExprRequirement::ReturnTypeRequirement(TPL));
+}
+
+concepts::ExprRequirement *
+Sema::BuildExprRequirement(
+ Expr *E, bool IsSimple, SourceLocation NoexceptLoc,
+ concepts::ExprRequirement::ReturnTypeRequirement ReturnTypeRequirement) {
+ auto Status = concepts::ExprRequirement::SS_Satisfied;
+ ConceptSpecializationExpr *SubstitutedConstraintExpr = nullptr;
+ if (E->isInstantiationDependent() || ReturnTypeRequirement.isDependent())
+ Status = concepts::ExprRequirement::SS_Dependent;
+ else if (NoexceptLoc.isValid() && canThrow(E) == CanThrowResult::CT_Can)
+ Status = concepts::ExprRequirement::SS_NoexceptNotMet;
+ else if (ReturnTypeRequirement.isSubstitutionFailure())
+ Status = concepts::ExprRequirement::SS_TypeRequirementSubstitutionFailure;
+ else if (ReturnTypeRequirement.isTypeConstraint()) {
+ // C++2a [expr.prim.req]p1.3.3
+ // The immediately-declared constraint ([temp]) of decltype((E)) shall
+ // be satisfied.
+ TemplateParameterList *TPL =
+ ReturnTypeRequirement.getTypeConstraintTemplateParameterList();
+ QualType MatchedType =
+ BuildDecltypeType(E, E->getBeginLoc()).getCanonicalType();
+ llvm::SmallVector<TemplateArgument, 1> Args;
+ Args.push_back(TemplateArgument(MatchedType));
+ TemplateArgumentList TAL(TemplateArgumentList::OnStack, Args);
+ MultiLevelTemplateArgumentList MLTAL(TAL);
+ for (unsigned I = 0; I < TPL->getDepth(); ++I)
+ MLTAL.addOuterRetainedLevel();
+ Expr *IDC =
+ cast<TemplateTypeParmDecl>(TPL->getParam(0))->getTypeConstraint()
+ ->getImmediatelyDeclaredConstraint();
+ ExprResult Constraint = SubstExpr(IDC, MLTAL);
+ assert(!Constraint.isInvalid() &&
+ "Substitution cannot fail as it is simply putting a type template "
+ "argument into a concept specialization expression's parameter.");
+
+ SubstitutedConstraintExpr =
+ cast<ConceptSpecializationExpr>(Constraint.get());
+ if (!SubstitutedConstraintExpr->isSatisfied())
+ Status = concepts::ExprRequirement::SS_ConstraintsNotSatisfied;
+ }
+ return new (Context) concepts::ExprRequirement(E, IsSimple, NoexceptLoc,
+ ReturnTypeRequirement, Status,
+ SubstitutedConstraintExpr);
+}
+
+concepts::ExprRequirement *
+Sema::BuildExprRequirement(
+ concepts::Requirement::SubstitutionDiagnostic *ExprSubstitutionDiagnostic,
+ bool IsSimple, SourceLocation NoexceptLoc,
+ concepts::ExprRequirement::ReturnTypeRequirement ReturnTypeRequirement) {
+ return new (Context) concepts::ExprRequirement(ExprSubstitutionDiagnostic,
+ IsSimple, NoexceptLoc,
+ ReturnTypeRequirement);
+}
+
+concepts::TypeRequirement *
+Sema::BuildTypeRequirement(TypeSourceInfo *Type) {
+ return new (Context) concepts::TypeRequirement(Type);
+}
+
+concepts::TypeRequirement *
+Sema::BuildTypeRequirement(
+ concepts::Requirement::SubstitutionDiagnostic *SubstDiag) {
+ return new (Context) concepts::TypeRequirement(SubstDiag);
+}
+
+concepts::Requirement *Sema::ActOnNestedRequirement(Expr *Constraint) {
+ return BuildNestedRequirement(Constraint);
+}
+
+concepts::NestedRequirement *
+Sema::BuildNestedRequirement(Expr *Constraint) {
+ ConstraintSatisfaction Satisfaction;
+ if (!Constraint->isInstantiationDependent() &&
+ CheckConstraintSatisfaction(Constraint, Satisfaction))
+ return nullptr;
+ return new (Context) concepts::NestedRequirement(Context, Constraint,
+ Satisfaction);
+}
+
+concepts::NestedRequirement *
+Sema::BuildNestedRequirement(
+ concepts::Requirement::SubstitutionDiagnostic *SubstDiag) {
+ return new (Context) concepts::NestedRequirement(SubstDiag);
+}
+
+RequiresExprBodyDecl *
+Sema::ActOnStartRequiresExpr(SourceLocation RequiresKWLoc,
+ ArrayRef<ParmVarDecl *> LocalParameters,
+ Scope *BodyScope) {
+ assert(BodyScope);
+
+ RequiresExprBodyDecl *Body = RequiresExprBodyDecl::Create(Context, CurContext,
+ RequiresKWLoc);
+
+ PushDeclContext(BodyScope, Body);
+
+ for (ParmVarDecl *Param : LocalParameters) {
+ if (Param->hasDefaultArg())
+ // C++2a [expr.prim.req] p4
+ // [...] A local parameter of a requires-expression shall not have a
+ // default argument. [...]
+ Diag(Param->getDefaultArgRange().getBegin(),
+ diag::err_requires_expr_local_parameter_default_argument);
+ // Ignore default argument and move on
+
+ Param->setDeclContext(Body);
+ // If this has an identifier, add it to the scope stack.
+ if (Param->getIdentifier()) {
+ CheckShadow(BodyScope, Param);
+ PushOnScopeChains(Param, BodyScope);
+ }
+ }
+ return Body;
+}
+
+void Sema::ActOnFinishRequiresExpr() {
+ assert(CurContext && "DeclContext imbalance!");
+ CurContext = CurContext->getLexicalParent();
+ assert(CurContext && "Popped translation unit!");
+}
+
+ExprResult
+Sema::ActOnRequiresExpr(SourceLocation RequiresKWLoc,
+ RequiresExprBodyDecl *Body,
+ ArrayRef<ParmVarDecl *> LocalParameters,
+ ArrayRef<concepts::Requirement *> Requirements,
+ SourceLocation ClosingBraceLoc) {
+ return RequiresExpr::Create(Context, RequiresKWLoc, Body, LocalParameters,
+ Requirements, ClosingBraceLoc);
+}