diff options
author | Saar Raz <saar@raz.email> | 2019-12-06 01:30:21 +0200 |
---|---|---|
committer | Saar Raz <saar@raz.email> | 2019-12-06 01:34:20 +0200 |
commit | fdf80e86a52849813d05da4b6c25884c06ba9e98 (patch) | |
tree | f08ca57be85567d41a118bedb588a8152763c360 /clang/lib | |
parent | 7faa8440440f280912c33a27c14c0dc4031532de (diff) | |
download | bcm5719-llvm-fdf80e86a52849813d05da4b6c25884c06ba9e98.tar.gz bcm5719-llvm-fdf80e86a52849813d05da4b6c25884c06ba9e98.zip |
[Concepts] Constraint Enforcement & Diagnostics
Part of the C++20 concepts implementation effort.
- Associated constraints (requires clauses, currently) are now enforced when instantiating/specializing templates and when considering partial specializations and function overloads.
- Elaborated diagnostics give helpful insight as to why the constraints were not satisfied.
Phabricator: D41569
Re-commit, after fixing some memory bugs.
Diffstat (limited to 'clang/lib')
-rw-r--r-- | clang/lib/AST/ASTConcept.cpp | 55 | ||||
-rw-r--r-- | clang/lib/AST/ASTContext.cpp | 1 | ||||
-rw-r--r-- | clang/lib/AST/CMakeLists.txt | 1 | ||||
-rw-r--r-- | clang/lib/AST/Decl.cpp | 2 | ||||
-rw-r--r-- | clang/lib/AST/ExprCXX.cpp | 17 | ||||
-rw-r--r-- | clang/lib/Sema/SemaConcept.cpp | 400 | ||||
-rw-r--r-- | clang/lib/Sema/SemaDeclCXX.cpp | 12 | ||||
-rw-r--r-- | clang/lib/Sema/SemaOverload.cpp | 47 | ||||
-rw-r--r-- | clang/lib/Sema/SemaTemplate.cpp | 60 | ||||
-rw-r--r-- | clang/lib/Sema/SemaTemplateDeduction.cpp | 31 | ||||
-rw-r--r-- | clang/lib/Sema/SemaTemplateInstantiate.cpp | 39 | ||||
-rw-r--r-- | clang/lib/Sema/SemaTemplateInstantiateDecl.cpp | 6 | ||||
-rw-r--r-- | clang/lib/Serialization/ASTReaderStmt.cpp | 24 | ||||
-rw-r--r-- | clang/lib/Serialization/ASTWriterStmt.cpp | 20 |
14 files changed, 600 insertions, 115 deletions
diff --git a/clang/lib/AST/ASTConcept.cpp b/clang/lib/AST/ASTConcept.cpp new file mode 100644 index 00000000000..b38b0952145 --- /dev/null +++ b/clang/lib/AST/ASTConcept.cpp @@ -0,0 +1,55 @@ +//===--- ASTConcept.cpp - Concepts Related AST Data Structures --*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// \brief This file defines AST data structures related to concepts. +/// +//===----------------------------------------------------------------------===// + +#include "clang/AST/ASTConcept.h" +#include "clang/AST/ASTContext.h" +using namespace clang; + +ASTConstraintSatisfaction::ASTConstraintSatisfaction(const ASTContext &C, + const ConstraintSatisfaction &Satisfaction): + NumRecords{Satisfaction.Details.size()}, + IsSatisfied{Satisfaction.IsSatisfied} { + for (unsigned I = 0; I < NumRecords; ++I) { + auto &Detail = Satisfaction.Details[I]; + if (Detail.second.is<Expr *>()) + new (getTrailingObjects<UnsatisfiedConstraintRecord>() + I) + UnsatisfiedConstraintRecord{Detail.first, + UnsatisfiedConstraintRecord::second_type( + Detail.second.get<Expr *>())}; + else { + auto &SubstitutionDiagnostic = + *Detail.second.get<std::pair<SourceLocation, std::string> *>(); + unsigned MessageSize = SubstitutionDiagnostic.second.size(); + char *Mem = new (C) char[MessageSize]; + memcpy(Mem, SubstitutionDiagnostic.second.c_str(), MessageSize); + auto *NewSubstDiag = new (C) std::pair<SourceLocation, StringRef>( + SubstitutionDiagnostic.first, StringRef(Mem, MessageSize)); + new (getTrailingObjects<UnsatisfiedConstraintRecord>() + I) + UnsatisfiedConstraintRecord{Detail.first, + UnsatisfiedConstraintRecord::second_type( + NewSubstDiag)}; + } + } +} + + +ASTConstraintSatisfaction * +ASTConstraintSatisfaction::Create(const ASTContext &C, + const ConstraintSatisfaction &Satisfaction) { + std::size_t size = + totalSizeToAlloc<UnsatisfiedConstraintRecord>( + Satisfaction.Details.size()); + void *Mem = C.Allocate(size, alignof(ASTConstraintSatisfaction)); + return new (Mem) ASTConstraintSatisfaction(C, Satisfaction); +} diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp index 006eb1e0def..10b6718c89b 100644 --- a/clang/lib/AST/ASTContext.cpp +++ b/clang/lib/AST/ASTContext.cpp @@ -14,6 +14,7 @@ #include "CXXABI.h" #include "Interp/Context.h" #include "clang/AST/APValue.h" +#include "clang/AST/ASTConcept.h" #include "clang/AST/ASTMutationListener.h" #include "clang/AST/ASTTypeTraits.h" #include "clang/AST/Attr.h" diff --git a/clang/lib/AST/CMakeLists.txt b/clang/lib/AST/CMakeLists.txt index 5bae40c8653..1ede58cc9ed 100644 --- a/clang/lib/AST/CMakeLists.txt +++ b/clang/lib/AST/CMakeLists.txt @@ -14,6 +14,7 @@ clang_tablegen(Opcodes.inc add_clang_library(clangAST APValue.cpp + ASTConcept.cpp ASTConsumer.cpp ASTContext.cpp ASTDiagnostic.cpp diff --git a/clang/lib/AST/Decl.cpp b/clang/lib/AST/Decl.cpp index 0301110b706..a3ba6ebbd0f 100644 --- a/clang/lib/AST/Decl.cpp +++ b/clang/lib/AST/Decl.cpp @@ -4606,7 +4606,7 @@ LabelDecl *LabelDecl::CreateDeserialized(ASTContext &C, unsigned ID) { } void LabelDecl::setMSAsmLabel(StringRef Name) { - char *Buffer = new (getASTContext(), 1) char[Name.size() + 1]; +char *Buffer = new (getASTContext(), 1) char[Name.size() + 1]; memcpy(Buffer, Name.data(), Name.size()); Buffer[Name.size()] = '\0'; MSAsmName = Buffer; diff --git a/clang/lib/AST/ExprCXX.cpp b/clang/lib/AST/ExprCXX.cpp index 0fb132dbe3f..bef181ed4cf 100644 --- a/clang/lib/AST/ExprCXX.cpp +++ b/clang/lib/AST/ExprCXX.cpp @@ -1769,18 +1769,19 @@ ConceptSpecializationExpr::ConceptSpecializationExpr(ASTContext &C, NestedNameSpecifierLoc NNS, SourceLocation TemplateKWLoc, SourceLocation ConceptNameLoc, NamedDecl *FoundDecl, ConceptDecl *NamedConcept, const ASTTemplateArgumentListInfo *ArgsAsWritten, - ArrayRef<TemplateArgument> ConvertedArgs, Optional<bool> IsSatisfied) + ArrayRef<TemplateArgument> ConvertedArgs, + const ConstraintSatisfaction *Satisfaction) : Expr(ConceptSpecializationExprClass, C.BoolTy, VK_RValue, OK_Ordinary, /*TypeDependent=*/false, // All the flags below are set in setTemplateArguments. - /*ValueDependent=*/!IsSatisfied.hasValue(), - /*InstantiationDependent=*/false, + /*ValueDependent=*/!Satisfaction, /*InstantiationDependent=*/false, /*ContainsUnexpandedParameterPacks=*/false), NestedNameSpec(NNS), TemplateKWLoc(TemplateKWLoc), ConceptNameLoc(ConceptNameLoc), FoundDecl(FoundDecl), - NamedConcept(NamedConcept, IsSatisfied ? *IsSatisfied : true), - NumTemplateArgs(ConvertedArgs.size()) { - + NamedConcept(NamedConcept), NumTemplateArgs(ConvertedArgs.size()), + Satisfaction(Satisfaction ? + ASTConstraintSatisfaction::Create(C, *Satisfaction) : + nullptr) { setTemplateArguments(ArgsAsWritten, ConvertedArgs); } @@ -1827,13 +1828,13 @@ ConceptSpecializationExpr::Create(ASTContext &C, NestedNameSpecifierLoc NNS, ConceptDecl *NamedConcept, const ASTTemplateArgumentListInfo *ArgsAsWritten, ArrayRef<TemplateArgument> ConvertedArgs, - Optional<bool> IsSatisfied) { + const ConstraintSatisfaction *Satisfaction) { void *Buffer = C.Allocate(totalSizeToAlloc<TemplateArgument>( ConvertedArgs.size())); return new (Buffer) ConceptSpecializationExpr(C, NNS, TemplateKWLoc, ConceptNameLoc, FoundDecl, NamedConcept, ArgsAsWritten, - ConvertedArgs, IsSatisfied); + ConvertedArgs, Satisfaction); } ConceptSpecializationExpr * diff --git a/clang/lib/Sema/SemaConcept.cpp b/clang/lib/Sema/SemaConcept.cpp index 848ccf54344..f917d9c7e5a 100644 --- a/clang/lib/Sema/SemaConcept.cpp +++ b/clang/lib/Sema/SemaConcept.cpp @@ -12,10 +12,13 @@ //===----------------------------------------------------------------------===// #include "clang/Sema/Sema.h" +#include "clang/Sema/SemaInternal.h" #include "clang/Sema/SemaDiagnostic.h" #include "clang/Sema/TemplateDeduction.h" #include "clang/Sema/Template.h" #include "clang/AST/ExprCXX.h" +#include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/PointerUnion.h" using namespace clang; using namespace sema; @@ -46,80 +49,369 @@ bool Sema::CheckConstraintExpression(Expr *ConstraintExpression) { return true; } -bool -Sema::CalculateConstraintSatisfaction(ConceptDecl *NamedConcept, - MultiLevelTemplateArgumentList &MLTAL, - Expr *ConstraintExpr, - bool &IsSatisfied) { +template <typename AtomicEvaluator> +static bool +calculateConstraintSatisfaction(Sema &S, const Expr *ConstraintExpr, + ConstraintSatisfaction &Satisfaction, + AtomicEvaluator &&Evaluator) { ConstraintExpr = ConstraintExpr->IgnoreParenImpCasts(); if (auto *BO = dyn_cast<BinaryOperator>(ConstraintExpr)) { - if (BO->getOpcode() == BO_LAnd) { - if (CalculateConstraintSatisfaction(NamedConcept, MLTAL, BO->getLHS(), - IsSatisfied)) + if (BO->getOpcode() == BO_LAnd || BO->getOpcode() == BO_LOr) { + if (calculateConstraintSatisfaction(S, BO->getLHS(), Satisfaction, + Evaluator)) return true; - if (!IsSatisfied) + + bool IsLHSSatisfied = Satisfaction.IsSatisfied; + + if (BO->getOpcode() == BO_LOr && IsLHSSatisfied) + // [temp.constr.op] p3 + // A disjunction is a constraint taking two operands. To determine if + // a disjunction is satisfied, the satisfaction of the first operand + // is checked. If that is satisfied, the disjunction is satisfied. + // Otherwise, the disjunction is satisfied if and only if the second + // operand is satisfied. 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) + + if (BO->getOpcode() == BO_LAnd && !IsLHSSatisfied) + // [temp.constr.op] p2 + // A conjunction is a constraint taking two operands. To determine if + // a conjunction is satisfied, the satisfaction of the first operand + // is checked. If that is not satisfied, the conjunction is not + // satisfied. Otherwise, the conjunction is satisfied if and only if + // the second operand is satisfied. return false; - return CalculateConstraintSatisfaction(NamedConcept, MLTAL, BO->getRHS(), - IsSatisfied); + + return calculateConstraintSatisfaction(S, BO->getRHS(), Satisfaction, + std::forward<AtomicEvaluator>(Evaluator)); } } else if (auto *C = dyn_cast<ExprWithCleanups>(ConstraintExpr)) - return CalculateConstraintSatisfaction(NamedConcept, MLTAL, C->getSubExpr(), - IsSatisfied); + return calculateConstraintSatisfaction(S, C->getSubExpr(), Satisfaction, + std::forward<AtomicEvaluator>(Evaluator)); - 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); + // An atomic constraint expression + ExprResult SubstitutedAtomicExpr = Evaluator(ConstraintExpr); - 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())) + if (SubstitutedAtomicExpr.isInvalid()) return true; + if (!SubstitutedAtomicExpr.isUsable()) + // Evaluator has decided satisfaction without yielding an expression. + return false; + + EnterExpressionEvaluationContext ConstantEvaluated( + S, Sema::ExpressionEvaluationContext::ConstantEvaluated); 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(); + if (!SubstitutedAtomicExpr.get()->EvaluateAsRValue(EvalResult, S.Context)) { + // C++2a [temp.constr.atomic]p1 + // ...E shall be a constant expression of type bool. + S.Diag(SubstitutedAtomicExpr.get()->getBeginLoc(), + diag::err_non_constant_constraint_expression) + << SubstitutedAtomicExpr.get()->getSourceRange(); for (const PartialDiagnosticAt &PDiag : EvaluationDiags) - Diag(PDiag.first, PDiag.second); + S.Diag(PDiag.first, PDiag.second); return true; } - IsSatisfied = EvalResult.Val.getInt().getBoolValue(); + Satisfaction.IsSatisfied = EvalResult.Val.getInt().getBoolValue(); + if (!Satisfaction.IsSatisfied) + Satisfaction.Details.emplace_back(ConstraintExpr, + SubstitutedAtomicExpr.get()); + + return false; +} + +template <typename TemplateDeclT> +static bool calculateConstraintSatisfaction( + Sema &S, TemplateDeclT *Template, ArrayRef<TemplateArgument> TemplateArgs, + SourceLocation TemplateNameLoc, MultiLevelTemplateArgumentList &MLTAL, + const Expr *ConstraintExpr, ConstraintSatisfaction &Satisfaction) { + return calculateConstraintSatisfaction( + S, ConstraintExpr, Satisfaction, [&](const Expr *AtomicExpr) { + EnterExpressionEvaluationContext ConstantEvaluated( + S, Sema::ExpressionEvaluationContext::ConstantEvaluated); + + // Atomic constraint - substitute arguments and check satisfaction. + ExprResult SubstitutedExpression; + { + TemplateDeductionInfo Info(TemplateNameLoc); + Sema::InstantiatingTemplate Inst(S, AtomicExpr->getBeginLoc(), + Sema::InstantiatingTemplate::ConstraintSubstitution{}, Template, + Info, AtomicExpr->getSourceRange()); + if (Inst.isInvalid()) + return ExprError(); + // We do not want error diagnostics escaping here. + Sema::SFINAETrap Trap(S); + SubstitutedExpression = S.SubstExpr(const_cast<Expr *>(AtomicExpr), + MLTAL); + if (SubstitutedExpression.isInvalid() || Trap.hasErrorOccurred()) { + // C++2a [temp.constr.atomic]p1 + // ...If substitution results in an invalid type or expression, the + // constraint is not satisfied. + if (!Trap.hasErrorOccurred()) + // A non-SFINAE error has occured as a result of this + // substitution. + return ExprError(); + + PartialDiagnosticAt SubstDiag{SourceLocation(), + PartialDiagnostic::NullDiagnostic()}; + Info.takeSFINAEDiagnostic(SubstDiag); + // FIXME: Concepts: This is an unfortunate consequence of there + // being no serialization code for PartialDiagnostics and the fact + // that serializing them would likely take a lot more storage than + // just storing them as strings. We would still like, in the + // future, to serialize the proper PartialDiagnostic as serializing + // it as a string defeats the purpose of the diagnostic mechanism. + SmallString<128> DiagString; + DiagString = ": "; + SubstDiag.second.EmitToString(S.getDiagnostics(), DiagString); + unsigned MessageSize = DiagString.size(); + char *Mem = new (S.Context) char[MessageSize]; + memcpy(Mem, DiagString.c_str(), MessageSize); + Satisfaction.Details.emplace_back( + AtomicExpr, + new (S.Context) ConstraintSatisfaction::SubstitutionDiagnostic{ + SubstDiag.first, StringRef(Mem, MessageSize)}); + Satisfaction.IsSatisfied = false; + return ExprEmpty(); + } + } + + if (!S.CheckConstraintExpression(SubstitutedExpression.get())) + return ExprError(); + + return SubstitutedExpression; + }); +} + +template<typename TemplateDeclT> +static bool CheckConstraintSatisfaction(Sema &S, TemplateDeclT *Template, + ArrayRef<const Expr *> ConstraintExprs, + ArrayRef<TemplateArgument> TemplateArgs, + SourceRange TemplateIDRange, + ConstraintSatisfaction &Satisfaction) { + if (ConstraintExprs.empty()) { + Satisfaction.IsSatisfied = true; + return false; + } + + for (auto& Arg : TemplateArgs) + if (Arg.isInstantiationDependent()) { + // No need to check satisfaction for dependent constraint expressions. + Satisfaction.IsSatisfied = true; + return false; + } + + Sema::InstantiatingTemplate Inst(S, TemplateIDRange.getBegin(), + Sema::InstantiatingTemplate::ConstraintsCheck{}, Template, TemplateArgs, + TemplateIDRange); + if (Inst.isInvalid()) + return true; + + MultiLevelTemplateArgumentList MLTAL; + MLTAL.addOuterTemplateArguments(TemplateArgs); + + for (const Expr *ConstraintExpr : ConstraintExprs) { + if (calculateConstraintSatisfaction(S, Template, TemplateArgs, + TemplateIDRange.getBegin(), MLTAL, + ConstraintExpr, Satisfaction)) + return true; + if (!Satisfaction.IsSatisfied) + // [temp.constr.op] p2 + // [...] To determine if a conjunction is satisfied, the satisfaction + // of the first operand is checked. If that is not satisfied, the + // conjunction is not satisfied. [...] + return false; + } + return false; +} + +bool Sema::CheckConstraintSatisfaction(TemplateDecl *Template, + ArrayRef<const Expr *> ConstraintExprs, + ArrayRef<TemplateArgument> TemplateArgs, + SourceRange TemplateIDRange, + ConstraintSatisfaction &Satisfaction) { + return ::CheckConstraintSatisfaction(*this, Template, ConstraintExprs, + TemplateArgs, TemplateIDRange, + Satisfaction); +} +bool +Sema::CheckConstraintSatisfaction(ClassTemplatePartialSpecializationDecl* Part, + ArrayRef<const Expr *> ConstraintExprs, + ArrayRef<TemplateArgument> TemplateArgs, + SourceRange TemplateIDRange, + ConstraintSatisfaction &Satisfaction) { + return ::CheckConstraintSatisfaction(*this, Part, ConstraintExprs, + TemplateArgs, TemplateIDRange, + Satisfaction); +} + +bool +Sema::CheckConstraintSatisfaction(VarTemplatePartialSpecializationDecl* Partial, + ArrayRef<const Expr *> ConstraintExprs, + ArrayRef<TemplateArgument> TemplateArgs, + SourceRange TemplateIDRange, + ConstraintSatisfaction &Satisfaction) { + return ::CheckConstraintSatisfaction(*this, Partial, ConstraintExprs, + TemplateArgs, TemplateIDRange, + Satisfaction); +} + +bool Sema::CheckConstraintSatisfaction(const Expr *ConstraintExpr, + ConstraintSatisfaction &Satisfaction) { + return calculateConstraintSatisfaction( + *this, ConstraintExpr, Satisfaction, + [](const Expr *AtomicExpr) -> ExprResult { + return ExprResult(const_cast<Expr *>(AtomicExpr)); + }); +} + +bool Sema::EnsureTemplateArgumentListConstraints( + TemplateDecl *TD, ArrayRef<TemplateArgument> TemplateArgs, + SourceRange TemplateIDRange) { + ConstraintSatisfaction Satisfaction; + llvm::SmallVector<const Expr *, 3> AssociatedConstraints; + TD->getAssociatedConstraints(AssociatedConstraints); + if (CheckConstraintSatisfaction(TD, AssociatedConstraints, TemplateArgs, + TemplateIDRange, Satisfaction)) + return true; + + if (!Satisfaction.IsSatisfied) { + SmallString<128> TemplateArgString; + TemplateArgString = " "; + TemplateArgString += getTemplateArgumentBindingsText( + TD->getTemplateParameters(), TemplateArgs.data(), TemplateArgs.size()); + + Diag(TemplateIDRange.getBegin(), + diag::err_template_arg_list_constraints_not_satisfied) + << (int)getTemplateNameKindForDiagnostics(TemplateName(TD)) << TD + << TemplateArgString << TemplateIDRange; + DiagnoseUnsatisfiedConstraint(Satisfaction); + return true; + } return false; +} + +static void diagnoseWellFormedUnsatisfiedConstraintExpr(Sema &S, + Expr *SubstExpr, + bool First = true) { + SubstExpr = SubstExpr->IgnoreParenImpCasts(); + if (BinaryOperator *BO = dyn_cast<BinaryOperator>(SubstExpr)) { + switch (BO->getOpcode()) { + // These two cases will in practice only be reached when using fold + // expressions with || and &&, since otherwise the || and && will have been + // broken down into atomic constraints during satisfaction checking. + case BO_LOr: + // Or evaluated to false - meaning both RHS and LHS evaluated to false. + diagnoseWellFormedUnsatisfiedConstraintExpr(S, BO->getLHS(), First); + diagnoseWellFormedUnsatisfiedConstraintExpr(S, BO->getRHS(), + /*First=*/false); + return; + case BO_LAnd: + bool LHSSatisfied; + BO->getLHS()->EvaluateAsBooleanCondition(LHSSatisfied, S.Context); + if (LHSSatisfied) { + // LHS is true, so RHS must be false. + diagnoseWellFormedUnsatisfiedConstraintExpr(S, BO->getRHS(), First); + return; + } + // LHS is false + diagnoseWellFormedUnsatisfiedConstraintExpr(S, BO->getLHS(), First); + + // RHS might also be false + bool RHSSatisfied; + BO->getRHS()->EvaluateAsBooleanCondition(RHSSatisfied, S.Context); + if (!RHSSatisfied) + diagnoseWellFormedUnsatisfiedConstraintExpr(S, BO->getRHS(), + /*First=*/false); + return; + case BO_GE: + case BO_LE: + case BO_GT: + case BO_LT: + case BO_EQ: + case BO_NE: + if (BO->getLHS()->getType()->isIntegerType() && + BO->getRHS()->getType()->isIntegerType()) { + Expr::EvalResult SimplifiedLHS; + Expr::EvalResult SimplifiedRHS; + BO->getLHS()->EvaluateAsInt(SimplifiedLHS, S.Context); + BO->getRHS()->EvaluateAsInt(SimplifiedRHS, S.Context); + if (!SimplifiedLHS.Diag && ! SimplifiedRHS.Diag) { + S.Diag(SubstExpr->getBeginLoc(), + diag::note_atomic_constraint_evaluated_to_false_elaborated) + << (int)First << SubstExpr + << SimplifiedLHS.Val.getInt().toString(10) + << BinaryOperator::getOpcodeStr(BO->getOpcode()) + << SimplifiedRHS.Val.getInt().toString(10); + return; + } + } + break; + + default: + break; + } + } else if (auto *CSE = dyn_cast<ConceptSpecializationExpr>(SubstExpr)) { + if (CSE->getTemplateArgsAsWritten()->NumTemplateArgs == 1) { + S.Diag( + CSE->getSourceRange().getBegin(), + diag:: + note_single_arg_concept_specialization_constraint_evaluated_to_false) + << (int)First + << CSE->getTemplateArgsAsWritten()->arguments()[0].getArgument() + << CSE->getNamedConcept(); + } else { + S.Diag(SubstExpr->getSourceRange().getBegin(), + diag::note_concept_specialization_constraint_evaluated_to_false) + << (int)First << CSE; + } + S.DiagnoseUnsatisfiedConstraint(CSE->getSatisfaction()); + return; + } + + S.Diag(SubstExpr->getSourceRange().getBegin(), + diag::note_atomic_constraint_evaluated_to_false) + << (int)First << SubstExpr; +} + +template<typename SubstitutionDiagnostic> +static void diagnoseUnsatisfiedConstraintExpr( + Sema &S, const Expr *E, + const llvm::PointerUnion<Expr *, SubstitutionDiagnostic *> &Record, + bool First = true) { + if (auto *Diag = Record.template dyn_cast<SubstitutionDiagnostic *>()){ + S.Diag(Diag->first, diag::note_substituted_constraint_expr_is_ill_formed) + << Diag->second; + return; + } + + diagnoseWellFormedUnsatisfiedConstraintExpr(S, + Record.template get<Expr *>(), First); +} + +void Sema::DiagnoseUnsatisfiedConstraint( + const ConstraintSatisfaction& Satisfaction) { + assert(!Satisfaction.IsSatisfied && + "Attempted to diagnose a satisfied constraint"); + bool First = true; + for (auto &Pair : Satisfaction.Details) { + diagnoseUnsatisfiedConstraintExpr(*this, Pair.first, Pair.second, First); + First = false; + } +} + +void Sema::DiagnoseUnsatisfiedConstraint( + const ASTConstraintSatisfaction &Satisfaction) { + assert(!Satisfaction.IsSatisfied && + "Attempted to diagnose a satisfied constraint"); + bool First = true; + for (auto &Pair : Satisfaction) { + diagnoseUnsatisfiedConstraintExpr(*this, Pair.first, Pair.second, First); + First = false; + } }
\ No newline at end of file diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp index cc3510b0d47..1561960c99d 100644 --- a/clang/lib/Sema/SemaDeclCXX.cpp +++ b/clang/lib/Sema/SemaDeclCXX.cpp @@ -14486,8 +14486,16 @@ Decl *Sema::BuildStaticAssertDeclaration(SourceLocation StaticAssertLoc, std::string InnerCondDescription; std::tie(InnerCond, InnerCondDescription) = findFailedBooleanCondition(Converted.get()); - if (InnerCond && !isa<CXXBoolLiteralExpr>(InnerCond) - && !isa<IntegerLiteral>(InnerCond)) { + if (InnerCond && isa<ConceptSpecializationExpr>(InnerCond)) { + // Drill down into concept specialization expressions to see why they + // weren't satisfied. + Diag(StaticAssertLoc, diag::err_static_assert_failed) + << !AssertMessage << Msg.str() << AssertExpr->getSourceRange(); + ConstraintSatisfaction Satisfaction; + if (!CheckConstraintSatisfaction(InnerCond, Satisfaction)) + DiagnoseUnsatisfiedConstraint(Satisfaction); + } else if (InnerCond && !isa<CXXBoolLiteralExpr>(InnerCond) + && !isa<IntegerLiteral>(InnerCond)) { Diag(StaticAssertLoc, diag::err_static_assert_requirement_failed) << InnerCondDescription << !AssertMessage << Msg.str() << InnerCond->getSourceRange(); diff --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp index a97847461e3..d733563c98d 100644 --- a/clang/lib/Sema/SemaOverload.cpp +++ b/clang/lib/Sema/SemaOverload.cpp @@ -591,6 +591,12 @@ namespace { TemplateArgumentList *TemplateArgs; unsigned CallArgIndex; }; + // Structure used by DeductionFailureInfo to store information about + // unsatisfied constraints. + struct CNSInfo { + TemplateArgumentList *TemplateArgs; + ConstraintSatisfaction Satisfaction; + }; } /// Convert from Sema's representation of template deduction information @@ -661,6 +667,14 @@ clang::MakeDeductionFailureInfo(ASTContext &Context, } break; + case Sema::TDK_ConstraintsNotSatisfied: { + CNSInfo *Saved = new (Context) CNSInfo; + Saved->TemplateArgs = Info.take(); + Saved->Satisfaction = Info.AssociatedConstraintsSatisfaction; + Result.Data = Saved; + break; + } + case Sema::TDK_Success: case Sema::TDK_NonDependentConversionFailure: llvm_unreachable("not a deduction failure"); @@ -701,6 +715,15 @@ void DeductionFailureInfo::Destroy() { } break; + case Sema::TDK_ConstraintsNotSatisfied: + // FIXME: Destroy the template argument list? + Data = nullptr; + if (PartialDiagnosticAt *Diag = getSFINAEDiagnostic()) { + Diag->~PartialDiagnosticAt(); + HasDiagnostic = false; + } + break; + // Unhandled case Sema::TDK_MiscellaneousDeductionFailure: break; @@ -726,6 +749,7 @@ TemplateParameter DeductionFailureInfo::getTemplateParameter() { case Sema::TDK_NonDeducedMismatch: case Sema::TDK_CUDATargetMismatch: case Sema::TDK_NonDependentConversionFailure: + case Sema::TDK_ConstraintsNotSatisfied: return TemplateParameter(); case Sema::TDK_Incomplete: @@ -769,6 +793,9 @@ TemplateArgumentList *DeductionFailureInfo::getTemplateArgumentList() { case Sema::TDK_SubstitutionFailure: return static_cast<TemplateArgumentList*>(Data); + case Sema::TDK_ConstraintsNotSatisfied: + return static_cast<CNSInfo*>(Data)->TemplateArgs; + // Unhandled case Sema::TDK_MiscellaneousDeductionFailure: break; @@ -789,6 +816,7 @@ const TemplateArgument *DeductionFailureInfo::getFirstArg() { case Sema::TDK_SubstitutionFailure: case Sema::TDK_CUDATargetMismatch: case Sema::TDK_NonDependentConversionFailure: + case Sema::TDK_ConstraintsNotSatisfied: return nullptr; case Sema::TDK_IncompletePack: @@ -820,6 +848,7 @@ const TemplateArgument *DeductionFailureInfo::getSecondArg() { case Sema::TDK_SubstitutionFailure: case Sema::TDK_CUDATargetMismatch: case Sema::TDK_NonDependentConversionFailure: + case Sema::TDK_ConstraintsNotSatisfied: return nullptr; case Sema::TDK_Inconsistent: @@ -1255,6 +1284,8 @@ bool Sema::IsOverload(FunctionDecl *New, FunctionDecl *Old, return NewTarget != OldTarget; } + // TODO: Concepts: Check function trailing requires clauses here. + // The signatures match; this is not an overload. return false; } @@ -10352,6 +10383,21 @@ static void DiagnoseBadDeduction(Sema &S, NamedDecl *Found, Decl *Templated, MaybeEmitInheritedConstructorNote(S, Found); return; + case Sema::TDK_ConstraintsNotSatisfied: { + // Format the template argument list into the argument string. + SmallString<128> TemplateArgString; + TemplateArgumentList *Args = DeductionFailure.getTemplateArgumentList(); + TemplateArgString = " "; + TemplateArgString += S.getTemplateArgumentBindingsText( + getDescribedTemplate(Templated)->getTemplateParameters(), *Args); + S.Diag(Templated->getLocation(), + diag::note_ovl_candidate_unsatisfied_constraints) + << TemplateArgString; + + S.DiagnoseUnsatisfiedConstraint( + static_cast<CNSInfo*>(DeductionFailure.Data)->Satisfaction); + return; + } case Sema::TDK_TooManyArguments: case Sema::TDK_TooFewArguments: DiagnoseArityMismatch(S, Found, Templated, NumArgs); @@ -10804,6 +10850,7 @@ static unsigned RankDeductionFailure(const DeductionFailureInfo &DFI) { case Sema::TDK_SubstitutionFailure: case Sema::TDK_DeducedMismatch: + case Sema::TDK_ConstraintsNotSatisfied: case Sema::TDK_DeducedMismatchNested: case Sema::TDK_NonDeducedMismatch: case Sema::TDK_MiscellaneousDeductionFailure: diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp index 7dd1e9075c1..699895568b7 100644 --- a/clang/lib/Sema/SemaTemplate.cpp +++ b/clang/lib/Sema/SemaTemplate.cpp @@ -3213,8 +3213,7 @@ QualType Sema::CheckTemplateIdType(TemplateName Name, TemplateDecl *Template = Name.getAsTemplateDecl(); if (!Template || isa<FunctionTemplateDecl>(Template) || - isa<VarTemplateDecl>(Template) || - isa<ConceptDecl>(Template)) { + isa<VarTemplateDecl>(Template) || isa<ConceptDecl>(Template)) { // We might have a substituted template template parameter pack. If so, // build a template specialization type for it. if (Name.getAsSubstTemplateTemplateParmPack()) @@ -3230,7 +3229,8 @@ QualType Sema::CheckTemplateIdType(TemplateName Name, // template. SmallVector<TemplateArgument, 4> Converted; if (CheckTemplateArgumentList(Template, TemplateLoc, TemplateArgs, - false, Converted)) + false, Converted, + /*UpdateArgsWithConversion=*/true)) return QualType(); QualType CanonType; @@ -3238,6 +3238,7 @@ QualType Sema::CheckTemplateIdType(TemplateName Name, bool InstantiationDependent = false; if (TypeAliasTemplateDecl *AliasTemplate = dyn_cast<TypeAliasTemplateDecl>(Template)) { + // Find the canonical type for this type alias template specialization. TypeAliasDecl *Pattern = AliasTemplate->getTemplatedDecl(); if (Pattern->isInvalidDecl()) @@ -3875,7 +3876,8 @@ DeclResult Sema::ActOnVarTemplateSpecialization( // template. SmallVector<TemplateArgument, 4> Converted; if (CheckTemplateArgumentList(VarTemplate, TemplateNameLoc, TemplateArgs, - false, Converted)) + false, Converted, + /*UpdateArgsWithConversion=*/true)) return true; // Find the variable template (partial) specialization declaration that @@ -4046,7 +4048,7 @@ Sema::CheckVarTemplateId(VarTemplateDecl *Template, SourceLocation TemplateLoc, if (CheckTemplateArgumentList( Template, TemplateNameLoc, const_cast<TemplateArgumentListInfo &>(TemplateArgs), false, - Converted)) + Converted, /*UpdateArgsWithConversion=*/true)) return true; // Find the variable template specialization declaration that @@ -4237,7 +4239,7 @@ Sema::CheckConceptTemplateId(const CXXScopeSpec &SS, /*UpdateArgsWithConversion=*/false)) return ExprError(); - Optional<bool> IsSatisfied; + ConstraintSatisfaction Satisfaction; bool AreArgsDependent = false; for (TemplateArgument &Arg : Converted) { if (Arg.isDependent()) { @@ -4245,25 +4247,21 @@ Sema::CheckConceptTemplateId(const CXXScopeSpec &SS, 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)) + if (!AreArgsDependent && + CheckConstraintSatisfaction(NamedConcept, + {NamedConcept->getConstraintExpr()}, + Converted, + SourceRange(SS.isSet() ? SS.getBeginLoc() : + ConceptNameLoc, + TemplateArgs->getRAngleLoc()), + Satisfaction)) return ExprError(); - IsSatisfied = Satisfied; - } + return ConceptSpecializationExpr::Create(Context, SS.isSet() ? SS.getWithLocInContext(Context) : NestedNameSpecifierLoc{}, TemplateKWLoc, ConceptNameLoc, FoundDecl, NamedConcept, ASTTemplateArgumentListInfo::Create(Context, *TemplateArgs), Converted, - IsSatisfied); + AreArgsDependent ? nullptr : &Satisfaction); } ExprResult Sema::BuildTemplateIdExpr(const CXXScopeSpec &SS, @@ -5209,7 +5207,11 @@ bool Sema::CheckTemplateArgumentList( TemplateDecl *Template, SourceLocation TemplateLoc, TemplateArgumentListInfo &TemplateArgs, bool PartialTemplateArgs, SmallVectorImpl<TemplateArgument> &Converted, - bool UpdateArgsWithConversions) { + bool UpdateArgsWithConversions, bool *ConstraintsNotSatisfied) { + + if (ConstraintsNotSatisfied) + *ConstraintsNotSatisfied = false; + // Make a copy of the template arguments for processing. Only make the // changes at the end when successful in matching the arguments to the // template. @@ -5324,7 +5326,6 @@ bool Sema::CheckTemplateArgumentList( if ((*Param)->isTemplateParameterPack() && !ArgumentPack.empty()) Converted.push_back( TemplateArgument::CreatePackCopy(Context, ArgumentPack)); - return false; } @@ -5463,6 +5464,15 @@ bool Sema::CheckTemplateArgumentList( if (UpdateArgsWithConversions) TemplateArgs = std::move(NewArgs); + if (!PartialTemplateArgs && + EnsureTemplateArgumentListConstraints( + Template, Converted, SourceRange(TemplateLoc, + TemplateArgs.getRAngleLoc()))) { + if (ConstraintsNotSatisfied) + *ConstraintsNotSatisfied = true; + return true; + } + return false; } @@ -7776,7 +7786,8 @@ DeclResult Sema::ActOnClassTemplateSpecialization( // template. SmallVector<TemplateArgument, 4> Converted; if (CheckTemplateArgumentList(ClassTemplate, TemplateNameLoc, - TemplateArgs, false, Converted)) + TemplateArgs, false, Converted, + /*UpdateArgsWithConversion=*/true)) return true; // Find the class template (partial) specialization declaration that @@ -9022,7 +9033,8 @@ DeclResult Sema::ActOnExplicitInstantiation( // template. SmallVector<TemplateArgument, 4> Converted; if (CheckTemplateArgumentList(ClassTemplate, TemplateNameLoc, - TemplateArgs, false, Converted)) + TemplateArgs, false, Converted, + /*UpdateArgsWithConversion=*/true)) return true; // Find the class template specialization declaration that diff --git a/clang/lib/Sema/SemaTemplateDeduction.cpp b/clang/lib/Sema/SemaTemplateDeduction.cpp index 64ef819e30d..327447746c3 100644 --- a/clang/lib/Sema/SemaTemplateDeduction.cpp +++ b/clang/lib/Sema/SemaTemplateDeduction.cpp @@ -2591,6 +2591,23 @@ ConvertDeducedTemplateArgument(Sema &S, NamedDecl *Param, return ConvertArg(Arg, 0); } +template<typename TemplateDeclT> +static Sema::TemplateDeductionResult +CheckDeducedArgumentConstraints(Sema& S, TemplateDeclT *Template, + ArrayRef<TemplateArgument> DeducedArgs, + TemplateDeductionInfo &Info) { + llvm::SmallVector<const Expr *, 3> AssociatedConstraints; + Template->getAssociatedConstraints(AssociatedConstraints); + if (S.CheckConstraintSatisfaction(Template, AssociatedConstraints, + DeducedArgs, Info.getLocation(), + Info.AssociatedConstraintsSatisfaction) || + !Info.AssociatedConstraintsSatisfaction.IsSatisfied) { + Info.reset(TemplateArgumentList::CreateCopy(S.Context, DeducedArgs)); + return Sema::TDK_ConstraintsNotSatisfied; + } + return Sema::TDK_Success; +} + // FIXME: This should not be a template, but // ClassTemplatePartialSpecializationDecl sadly does not derive from // TemplateDecl. @@ -2688,6 +2705,10 @@ static Sema::TemplateDeductionResult ConvertDeducedTemplateArguments( // If we get here, we successfully used the default template argument. } + if (Sema::TemplateDeductionResult Result + = CheckDeducedArgumentConstraints(S, Template, Builder, Info)) + return Result; + return Sema::TDK_Success; } @@ -2767,10 +2788,14 @@ FinishTemplateArgumentDeduction( return Sema::TDK_SubstitutionFailure; } + bool ConstraintsNotSatisfied; SmallVector<TemplateArgument, 4> ConvertedInstArgs; if (S.CheckTemplateArgumentList(Template, Partial->getLocation(), InstArgs, - false, ConvertedInstArgs)) - return Sema::TDK_SubstitutionFailure; + false, ConvertedInstArgs, + /*UpdateArgsWithConversions=*/true, + &ConstraintsNotSatisfied)) + return ConstraintsNotSatisfied ? Sema::TDK_ConstraintsNotSatisfied : + Sema::TDK_SubstitutionFailure; TemplateParameterList *TemplateParams = Template->getTemplateParameters(); for (unsigned I = 0, E = TemplateParams->size(); I != E; ++I) { @@ -2831,7 +2856,6 @@ static Sema::TemplateDeductionResult FinishTemplateArgumentDeduction( return Sema::TDK_Success; } - /// Perform template argument deduction to determine whether /// the given template arguments match the given class template /// partial specialization per C++ [temp.class.spec.match]. @@ -5049,6 +5073,7 @@ template<typename TemplateLikeDecl> static bool isAtLeastAsSpecializedAs(Sema &S, QualType T1, QualType T2, TemplateLikeDecl *P2, TemplateDeductionInfo &Info) { + // TODO: Concepts: Regard constraints // C++ [temp.class.order]p1: // For two class template partial specializations, the first is at least as // specialized as the second if, given the following rewrite to two diff --git a/clang/lib/Sema/SemaTemplateInstantiate.cpp b/clang/lib/Sema/SemaTemplateInstantiate.cpp index 701be0411f7..2496c919311 100644 --- a/clang/lib/Sema/SemaTemplateInstantiate.cpp +++ b/clang/lib/Sema/SemaTemplateInstantiate.cpp @@ -363,7 +363,7 @@ Sema::InstantiatingTemplate::InstantiatingTemplate( Sema::InstantiatingTemplate::InstantiatingTemplate( Sema &SemaRef, SourceLocation PointOfInstantiation, - ConstraintsCheck, TemplateDecl *Template, + ConstraintsCheck, NamedDecl *Template, ArrayRef<TemplateArgument> TemplateArgs, SourceRange InstantiationRange) : InstantiatingTemplate( SemaRef, CodeSynthesisContext::ConstraintsCheck, @@ -372,7 +372,7 @@ Sema::InstantiatingTemplate::InstantiatingTemplate( Sema::InstantiatingTemplate::InstantiatingTemplate( Sema &SemaRef, SourceLocation PointOfInstantiation, - ConstraintSubstitution, TemplateDecl *Template, + ConstraintSubstitution, NamedDecl *Template, sema::TemplateDeductionInfo &DeductionInfo, SourceRange InstantiationRange) : InstantiatingTemplate( SemaRef, CodeSynthesisContext::ConstraintSubstitution, @@ -691,24 +691,27 @@ 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; + case CodeSynthesisContext::ConstraintsCheck: { + unsigned DiagID = 0; + if (isa<ConceptDecl>(Active->Entity)) + DiagID = diag::note_concept_specialization_here; + else if (isa<TemplateDecl>(Active->Entity)) + DiagID = diag::note_checking_constraints_for_template_id_here; + else if (isa<VarTemplatePartialSpecializationDecl>(Active->Entity)) + DiagID = diag::note_checking_constraints_for_var_spec_id_here; + else { + assert(isa<ClassTemplatePartialSpecializationDecl>(Active->Entity)); + DiagID = diag::note_checking_constraints_for_class_spec_id_here; } - // TODO: Concepts - implement this for constrained templates and partial - // specializations. - llvm_unreachable("only concept constraints are supported right now"); + SmallVector<char, 128> TemplateArgsStr; + llvm::raw_svector_ostream OS(TemplateArgsStr); + cast<NamedDecl>(Active->Entity)->printName(OS); + printTemplateArgumentList(OS, Active->template_arguments(), + getPrintingPolicy()); + Diags.Report(Active->PointOfInstantiation, DiagID) << OS.str() + << Active->InstantiationRange; break; - + } case CodeSynthesisContext::ConstraintSubstitution: Diags.Report(Active->PointOfInstantiation, diag::note_constraint_substitution_here) diff --git a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp index e9cb9f89e0a..2ab282e05e3 100644 --- a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp +++ b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp @@ -3296,7 +3296,8 @@ TemplateDeclInstantiator::VisitClassTemplateSpecializationDecl( D->getLocation(), InstTemplateArgs, false, - Converted)) + Converted, + /*UpdateArgsWithConversion=*/true)) return nullptr; // Figure out where to insert this class template explicit specialization @@ -3417,7 +3418,8 @@ Decl *TemplateDeclInstantiator::VisitVarTemplateSpecializationDecl( // Check that the template argument list is well-formed for this template. SmallVector<TemplateArgument, 4> Converted; if (SemaRef.CheckTemplateArgumentList(InstVarTemplate, D->getLocation(), - VarTemplateArgsInfo, false, Converted)) + VarTemplateArgsInfo, false, Converted, + /*UpdateArgsWithConversion=*/true)) return nullptr; // Check whether we've already seen a declaration of this specialization. diff --git a/clang/lib/Serialization/ASTReaderStmt.cpp b/clang/lib/Serialization/ASTReaderStmt.cpp index f426ed3b8d3..815a9744c5b 100644 --- a/clang/lib/Serialization/ASTReaderStmt.cpp +++ b/clang/lib/Serialization/ASTReaderStmt.cpp @@ -12,6 +12,7 @@ //===----------------------------------------------------------------------===// #include "clang/Serialization/ASTReader.h" +#include "clang/AST/ASTConcept.h" #include "clang/AST/ASTContext.h" #include "clang/AST/AttrIterator.h" #include "clang/AST/Decl.h" @@ -742,14 +743,33 @@ void ASTStmtReader::VisitConceptSpecializationExpr( E->TemplateKWLoc = Record.readSourceLocation(); E->ConceptNameLoc = Record.readSourceLocation(); E->FoundDecl = ReadDeclAs<NamedDecl>(); - E->NamedConcept.setPointer(ReadDeclAs<ConceptDecl>()); + E->NamedConcept = 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); + ConstraintSatisfaction Satisfaction; + Satisfaction.IsSatisfied = Record.readInt(); + if (!Satisfaction.IsSatisfied) { + unsigned NumDetailRecords = Record.readInt(); + for (unsigned i = 0; i != NumDetailRecords; ++i) { + Expr *ConstraintExpr = Record.readExpr(); + bool IsDiagnostic = Record.readInt(); + if (IsDiagnostic) { + SourceLocation DiagLocation = Record.readSourceLocation(); + std::string DiagMessage = Record.readString(); + Satisfaction.Details.emplace_back( + ConstraintExpr, new (Record.getContext()) + ConstraintSatisfaction::SubstitutionDiagnostic{ + DiagLocation, DiagMessage}); + } else + Satisfaction.Details.emplace_back(ConstraintExpr, Record.readExpr()); + } + } + E->Satisfaction = ASTConstraintSatisfaction::Create(Record.getContext(), + Satisfaction); } void ASTStmtReader::VisitArraySubscriptExpr(ArraySubscriptExpr *E) { diff --git a/clang/lib/Serialization/ASTWriterStmt.cpp b/clang/lib/Serialization/ASTWriterStmt.cpp index 2b331a97985..1e801d02afc 100644 --- a/clang/lib/Serialization/ASTWriterStmt.cpp +++ b/clang/lib/Serialization/ASTWriterStmt.cpp @@ -401,7 +401,25 @@ void ASTStmtWriter::VisitConceptSpecializationExpr( Record.AddASTTemplateArgumentListInfo(E->getTemplateArgsAsWritten()); for (const TemplateArgument &Arg : TemplateArgs) Record.AddTemplateArgument(Arg); - Record.push_back(E->isSatisfied()); + const ASTConstraintSatisfaction &Satisfaction = E->getSatisfaction(); + Record.push_back(Satisfaction.IsSatisfied); + if (!Satisfaction.IsSatisfied) { + Record.push_back(Satisfaction.NumRecords); + for (const auto &DetailRecord : Satisfaction) { + Record.AddStmt(const_cast<Expr *>(DetailRecord.first)); + auto *E = DetailRecord.second.dyn_cast<Expr *>(); + Record.push_back(E == nullptr); + if (E) + Record.AddStmt(E); + else { + auto *Diag = DetailRecord.second.get<std::pair<SourceLocation, + StringRef> *>(); + Record.AddSourceLocation(Diag->first); + Record.AddString(Diag->second); + } + } + } + Code = serialization::EXPR_CONCEPT_SPECIALIZATION; } |