diff options
Diffstat (limited to 'clang/lib/Sema')
-rw-r--r-- | clang/lib/Sema/CMakeLists.txt | 1 | ||||
-rw-r--r-- | clang/lib/Sema/SemaConcept.cpp | 125 | ||||
-rw-r--r-- | clang/lib/Sema/SemaExceptionSpec.cpp | 1 | ||||
-rw-r--r-- | clang/lib/Sema/SemaTemplate.cpp | 69 | ||||
-rw-r--r-- | clang/lib/Sema/SemaTemplateInstantiate.cpp | 51 | ||||
-rw-r--r-- | clang/lib/Sema/TreeTransform.h | 36 |
6 files changed, 257 insertions, 26 deletions
diff --git a/clang/lib/Sema/CMakeLists.txt b/clang/lib/Sema/CMakeLists.txt index 742343583d1..89c3f6c47b4 100644 --- a/clang/lib/Sema/CMakeLists.txt +++ b/clang/lib/Sema/CMakeLists.txt @@ -32,6 +32,7 @@ add_clang_library(clangSema SemaCast.cpp SemaChecking.cpp SemaCodeComplete.cpp + SemaConcept.cpp SemaConsumer.cpp SemaCoroutine.cpp SemaCUDA.cpp diff --git a/clang/lib/Sema/SemaConcept.cpp b/clang/lib/Sema/SemaConcept.cpp new file mode 100644 index 00000000000..3131609390a --- /dev/null +++ b/clang/lib/Sema/SemaConcept.cpp @@ -0,0 +1,125 @@ +//===-- 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. + + ConstraintExpression = ConstraintExpression->IgnoreParenImpCasts(); + + if (auto *BinOp = dyn_cast<BinaryOperator>(ConstraintExpression)) { + if (BinOp->getOpcode() == BO_LAnd || BinOp->getOpcode() == BO_LOr) + return CheckConstraintExpression(BinOp->getLHS()) && + CheckConstraintExpression(BinOp->getRHS()); + } else if (auto *C = dyn_cast<ExprWithCleanups>(ConstraintExpression)) + return CheckConstraintExpression(C->getSubExpr()); + + // An atomic constraint! + if (ConstraintExpression->isTypeDependent()) + return true; + + QualType Type = ConstraintExpression->getType(); + if (!Context.hasSameUnqualifiedType(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) { + ConstraintExpr = ConstraintExpr->IgnoreParenImpCasts(); + + 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 *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; +} diff --git a/clang/lib/Sema/SemaExceptionSpec.cpp b/clang/lib/Sema/SemaExceptionSpec.cpp index 0f3a27233ee..76fd10d2e63 100644 --- a/clang/lib/Sema/SemaExceptionSpec.cpp +++ b/clang/lib/Sema/SemaExceptionSpec.cpp @@ -1314,6 +1314,7 @@ CanThrowResult Sema::canThrow(const Expr *E) { case Expr::SizeOfPackExprClass: case Expr::StringLiteralClass: case Expr::SourceLocExprClass: + case Expr::ConceptSpecializationExprClass: // These expressions can never throw. return CT_Cannot; diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp index 284962f3e07..cb756eb30f6 100644 --- a/clang/lib/Sema/SemaTemplate.cpp +++ b/clang/lib/Sema/SemaTemplate.cpp @@ -4271,14 +4271,47 @@ void Sema::diagnoseMissingTemplateArguments(TemplateName Name, 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(); + + Optional<bool> IsSatisfied; + bool AreArgsDependent = false; + for (TemplateArgument &Arg : Converted) { + if (Arg.isDependent()) { + AreArgsDependent = true; + break; + } + } + if (!AreArgsDependent) { + InstantiatingTemplate Inst(*this, ConceptNameLoc, + InstantiatingTemplate::ConstraintsCheck{}, NamedConcept, Converted, + SourceRange(SS.isSet() ? SS.getBeginLoc() : ConceptNameLoc, + TemplateArgs->getRAngleLoc())); + MultiLevelTemplateArgumentList MLTAL; + MLTAL.addOuterTemplateArguments(Converted); + bool Satisfied; + if (CalculateConstraintSatisfaction(NamedConcept, MLTAL, + NamedConcept->getConstraintExpr(), + Satisfied)) + return ExprError(); + IsSatisfied = Satisfied; + } + 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, @@ -4322,9 +4355,10 @@ ExprResult Sema::BuildTemplateIdExpr(const CXXScopeSpec &SS, } 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. @@ -8054,20 +8088,7 @@ Decl *Sema::ActOnConceptDefinition(Scope *S, 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. diff --git a/clang/lib/Sema/SemaTemplateInstantiate.cpp b/clang/lib/Sema/SemaTemplateInstantiate.cpp index 9091bc5b80d..42411c9c33f 100644 --- a/clang/lib/Sema/SemaTemplateInstantiate.cpp +++ b/clang/lib/Sema/SemaTemplateInstantiate.cpp @@ -198,12 +198,14 @@ bool Sema::CodeSynthesisContext::isInstantiationRecord() const { 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. @@ -358,6 +360,24 @@ Sema::InstantiatingTemplate::InstantiatingTemplate( 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; @@ -664,6 +684,30 @@ void Sema::PrintInstantiationStack() { 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; } } } @@ -687,6 +731,7 @@ Optional<TemplateDeductionInfo *> Sema::isSFINAEContext() const { LLVM_FALLTHROUGH; case CodeSynthesisContext::DefaultFunctionArgumentInstantiation: case CodeSynthesisContext::ExceptionSpecInstantiation: + case CodeSynthesisContext::ConstraintsCheck: // This is a template instantiation, so there is no SFINAE. return None; @@ -700,8 +745,10 @@ Optional<TemplateDeductionInfo *> Sema::isSFINAEContext() const { 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; diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h index 59cac787876..8184d4c09b4 100644 --- a/clang/lib/Sema/TreeTransform.h +++ b/clang/lib/Sema/TreeTransform.h @@ -3019,6 +3019,25 @@ public: /// /// 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); } @@ -11016,6 +11035,23 @@ TreeTransform<Derived>::TransformTypeTraitExpr(TypeTraitExpr *E) { 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) { TypeSourceInfo *T = getDerived().TransformType(E->getQueriedTypeSourceInfo()); if (!T) |