diff options
| author | Dimitry Andric <dim@FreeBSD.org> | 2020-01-17 20:45:01 +0000 | 
|---|---|---|
| committer | Dimitry Andric <dim@FreeBSD.org> | 2020-01-17 20:45:01 +0000 | 
| commit | 706b4fc47bbc608932d3b491ae19a3b9cde9497b (patch) | |
| tree | 4adf86a776049cbf7f69a1929c4babcbbef925eb /clang/lib/Sema/SemaConcept.cpp | |
| parent | 7cc9cf2bf09f069cb2dd947ead05d0b54301fb71 (diff) | |
Notes
Diffstat (limited to 'clang/lib/Sema/SemaConcept.cpp')
| -rw-r--r-- | clang/lib/Sema/SemaConcept.cpp | 821 | 
1 files changed, 762 insertions, 59 deletions
| diff --git a/clang/lib/Sema/SemaConcept.cpp b/clang/lib/Sema/SemaConcept.cpp index 848ccf543445..018ac2d7dc9d 100644 --- a/clang/lib/Sema/SemaConcept.cpp +++ b/clang/lib/Sema/SemaConcept.cpp @@ -11,15 +11,24 @@  //  //===----------------------------------------------------------------------===// +#include "clang/Sema/SemaConcept.h"  #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 "clang/AST/RecursiveASTVisitor.h" +#include "clang/Basic/OperatorPrecedence.h" +#include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/PointerUnion.h"  using namespace clang;  using namespace sema; -bool Sema::CheckConstraintExpression(Expr *ConstraintExpression) { +bool +Sema::CheckConstraintExpression(Expr *ConstraintExpression, Token NextToken, +                                bool *PossibleNonPrimary, +                                bool IsTrailingRequiresClause) {    // C++2a [temp.constr.atomic]p1    // ..E shall be a constant expression of type bool. @@ -27,99 +36,793 @@ bool Sema::CheckConstraintExpression(Expr *ConstraintExpression) {    if (auto *BinOp = dyn_cast<BinaryOperator>(ConstraintExpression)) {      if (BinOp->getOpcode() == BO_LAnd || BinOp->getOpcode() == BO_LOr) -      return CheckConstraintExpression(BinOp->getLHS()) && -             CheckConstraintExpression(BinOp->getRHS()); +      return CheckConstraintExpression(BinOp->getLHS(), NextToken, +                                       PossibleNonPrimary) && +             CheckConstraintExpression(BinOp->getRHS(), NextToken, +                                       PossibleNonPrimary);    } else if (auto *C = dyn_cast<ExprWithCleanups>(ConstraintExpression)) -    return CheckConstraintExpression(C->getSubExpr()); +    return CheckConstraintExpression(C->getSubExpr(), NextToken, +                                     PossibleNonPrimary); + +  QualType Type = ConstraintExpression->getType(); + +  auto CheckForNonPrimary = [&] { +    if (PossibleNonPrimary) +      *PossibleNonPrimary = +          // We have the following case: +          // template<typename> requires func(0) struct S { }; +          // The user probably isn't aware of the parentheses required around +          // the function call, and we're only going to parse 'func' as the +          // primary-expression, and complain that it is of non-bool type. +          (NextToken.is(tok::l_paren) && +           (IsTrailingRequiresClause || +            (Type->isDependentType() && +             IsDependentFunctionNameExpr(ConstraintExpression)) || +            Type->isFunctionType() || +            Type->isSpecificBuiltinType(BuiltinType::Overload))) || +          // We have the following case: +          // template<typename T> requires size_<T> == 0 struct S { }; +          // The user probably isn't aware of the parentheses required around +          // the binary operator, and we're only going to parse 'func' as the +          // first operand, and complain that it is of non-bool type. +          getBinOpPrecedence(NextToken.getKind(), +                             /*GreaterThanIsOperator=*/true, +                             getLangOpts().CPlusPlus11) > prec::LogicalAnd; +  };    // An atomic constraint! -  if (ConstraintExpression->isTypeDependent()) +  if (ConstraintExpression->isTypeDependent()) { +    CheckForNonPrimary();      return true; +  } -  QualType Type = ConstraintExpression->getType();    if (!Context.hasSameUnqualifiedType(Type, Context.BoolTy)) {      Diag(ConstraintExpression->getExprLoc(),           diag::err_non_bool_atomic_constraint) << Type          << ConstraintExpression->getSourceRange(); +    CheckForNonPrimary();      return false;    } + +  if (PossibleNonPrimary) +      *PossibleNonPrimary = false;    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); +  // An atomic constraint expression +  ExprResult SubstitutedAtomicExpr = Evaluator(ConstraintExpr); -  // 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); +  if (SubstitutedAtomicExpr.isInvalid()) +    return true; + +  if (!SubstitutedAtomicExpr.isUsable()) +    // Evaluator has decided satisfaction without yielding an expression. +    return false; -    E = SubstExpr(ConstraintExpr, MLTAL); -    if (E.isInvalid() || Trap.hasErrorOccurred()) { +  EnterExpressionEvaluationContext ConstantEvaluated( +      S, Sema::ExpressionEvaluationContext::ConstantEvaluated); +  SmallVector<PartialDiagnosticAt, 2> EvaluationDiags; +  Expr::EvalResult EvalResult; +  EvalResult.Diag = &EvaluationDiags; +  if (!SubstitutedAtomicExpr.get()->EvaluateAsRValue(EvalResult, S.Context)) {        // C++2a [temp.constr.atomic]p1 -      //   ...If substitution results in an invalid type or expression, the -      //   constraint is not satisfied. -      IsSatisfied = false; +      //   ...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) +      S.Diag(PDiag.first, PDiag.second); +    return true; +  } + +  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); +} -  if (!CheckConstraintExpression(E.get())) +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; -  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); +  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; +  } +} + +const NormalizedConstraint * +Sema::getNormalizedAssociatedConstraints( +    NamedDecl *ConstrainedDecl, ArrayRef<const Expr *> AssociatedConstraints) { +  auto CacheEntry = NormalizationCache.find(ConstrainedDecl); +  if (CacheEntry == NormalizationCache.end()) { +    auto Normalized = +        NormalizedConstraint::fromConstraintExprs(*this, ConstrainedDecl, +                                                  AssociatedConstraints); +    CacheEntry = +        NormalizationCache +            .try_emplace(ConstrainedDecl, +                         Normalized +                             ? new (Context) NormalizedConstraint( +                                 std::move(*Normalized)) +                             : nullptr) +            .first; +  } +  return CacheEntry->second; +} + +static bool substituteParameterMappings(Sema &S, NormalizedConstraint &N, +    ConceptDecl *Concept, ArrayRef<TemplateArgument> TemplateArgs, +    const ASTTemplateArgumentListInfo *ArgsAsWritten) { +  if (!N.isAtomic()) { +    if (substituteParameterMappings(S, N.getLHS(), Concept, TemplateArgs, +                                    ArgsAsWritten)) +      return true; +    return substituteParameterMappings(S, N.getRHS(), Concept, TemplateArgs, +                                       ArgsAsWritten); +  } +  TemplateParameterList *TemplateParams = Concept->getTemplateParameters(); + +  AtomicConstraint &Atomic = *N.getAtomicConstraint(); +  TemplateArgumentListInfo SubstArgs; +  MultiLevelTemplateArgumentList MLTAL; +  MLTAL.addOuterTemplateArguments(TemplateArgs); +  if (!Atomic.ParameterMapping) { +    llvm::SmallBitVector OccurringIndices(TemplateParams->size()); +    S.MarkUsedTemplateParameters(Atomic.ConstraintExpr, /*OnlyDeduced=*/false, +                                 /*Depth=*/0, OccurringIndices); +    Atomic.ParameterMapping.emplace( +        MutableArrayRef<TemplateArgumentLoc>( +            new (S.Context) TemplateArgumentLoc[OccurringIndices.count()], +            OccurringIndices.count())); +    for (unsigned I = 0, J = 0, C = TemplateParams->size(); I != C; ++I) +      if (OccurringIndices[I]) +        new (&(*Atomic.ParameterMapping)[J++]) TemplateArgumentLoc( +            S.getIdentityTemplateArgumentLoc(TemplateParams->begin()[I], +                // Here we assume we do not support things like +                // template<typename A, typename B> +                // concept C = ...; +                // +                // template<typename... Ts> requires C<Ts...> +                // struct S { }; +                // The above currently yields a diagnostic. +                // We still might have default arguments for concept parameters. +                ArgsAsWritten->NumTemplateArgs > I ? +                ArgsAsWritten->arguments()[I].getLocation() : +                SourceLocation())); +  } +  Sema::InstantiatingTemplate Inst( +      S, ArgsAsWritten->arguments().front().getSourceRange().getBegin(), +      Sema::InstantiatingTemplate::ParameterMappingSubstitution{}, Concept, +      SourceRange(ArgsAsWritten->arguments()[0].getSourceRange().getBegin(), +                  ArgsAsWritten->arguments().back().getSourceRange().getEnd())); +  if (S.SubstTemplateArguments(*Atomic.ParameterMapping, MLTAL, SubstArgs)) +    return true; +  std::copy(SubstArgs.arguments().begin(), SubstArgs.arguments().end(), +            N.getAtomicConstraint()->ParameterMapping->begin()); +  return false; +} + +Optional<NormalizedConstraint> +NormalizedConstraint::fromConstraintExprs(Sema &S, NamedDecl *D, +                                          ArrayRef<const Expr *> E) { +  assert(E.size() != 0); +  auto First = fromConstraintExpr(S, D, E[0]); +  if (E.size() == 1) +    return First; +  auto Second = fromConstraintExpr(S, D, E[1]); +  if (!Second) +    return None; +  llvm::Optional<NormalizedConstraint> Conjunction; +  Conjunction.emplace(S.Context, std::move(*First), std::move(*Second), +                      CCK_Conjunction); +  for (unsigned I = 2; I < E.size(); ++I) { +    auto Next = fromConstraintExpr(S, D, E[I]); +    if (!Next) +      return llvm::Optional<NormalizedConstraint>{}; +    NormalizedConstraint NewConjunction(S.Context, std::move(*Conjunction), +                                        std::move(*Next), CCK_Conjunction); +    *Conjunction = std::move(NewConjunction); +  } +  return Conjunction; +} + +llvm::Optional<NormalizedConstraint> +NormalizedConstraint::fromConstraintExpr(Sema &S, NamedDecl *D, const Expr *E) { +  assert(E != nullptr); + +  // C++ [temp.constr.normal]p1.1 +  // [...] +  // - The normal form of an expression (E) is the normal form of E. +  // [...] +  E = E->IgnoreParenImpCasts(); +  if (auto *BO = dyn_cast<const BinaryOperator>(E)) { +    if (BO->getOpcode() == BO_LAnd || BO->getOpcode() == BO_LOr) { +      auto LHS = fromConstraintExpr(S, D, BO->getLHS()); +      if (!LHS) +        return None; +      auto RHS = fromConstraintExpr(S, D, BO->getRHS()); +      if (!RHS) +        return None; + +      return NormalizedConstraint( +          S.Context, std::move(*LHS), std::move(*RHS), +          BO->getOpcode() == BO_LAnd ? CCK_Conjunction : CCK_Disjunction); +    } +  } else if (auto *CSE = dyn_cast<const ConceptSpecializationExpr>(E)) { +    const NormalizedConstraint *SubNF; +    { +      Sema::InstantiatingTemplate Inst( +          S, CSE->getExprLoc(), +          Sema::InstantiatingTemplate::ConstraintNormalization{}, D, +          CSE->getSourceRange()); +      // C++ [temp.constr.normal]p1.1 +      // [...] +      // The normal form of an id-expression of the form C<A1, A2, ..., AN>, +      // where C names a concept, is the normal form of the +      // constraint-expression of C, after substituting A1, A2, ..., AN for C’s +      // respective template parameters in the parameter mappings in each atomic +      // constraint. If any such substitution results in an invalid type or +      // expression, the program is ill-formed; no diagnostic is required. +      // [...] +      ConceptDecl *CD = CSE->getNamedConcept(); +      SubNF = S.getNormalizedAssociatedConstraints(CD, +                                                   {CD->getConstraintExpr()}); +      if (!SubNF) +        return None; +    } + +    Optional<NormalizedConstraint> New; +    New.emplace(S.Context, *SubNF); + +    if (substituteParameterMappings( +            S, *New, CSE->getNamedConcept(), +            CSE->getTemplateArguments(), CSE->getTemplateArgsAsWritten())) +      return None; + +    return New; +  } +  return NormalizedConstraint{new (S.Context) AtomicConstraint(S, E)}; +} + +using NormalForm = +    llvm::SmallVector<llvm::SmallVector<AtomicConstraint *, 2>, 4>; + +static NormalForm makeCNF(const NormalizedConstraint &Normalized) { +  if (Normalized.isAtomic()) +    return {{Normalized.getAtomicConstraint()}}; + +  NormalForm LCNF = makeCNF(Normalized.getLHS()); +  NormalForm RCNF = makeCNF(Normalized.getRHS()); +  if (Normalized.getCompoundKind() == NormalizedConstraint::CCK_Conjunction) { +    LCNF.reserve(LCNF.size() + RCNF.size()); +    while (!RCNF.empty()) +      LCNF.push_back(RCNF.pop_back_val()); +    return LCNF; +  } + +  // Disjunction +  NormalForm Res; +  Res.reserve(LCNF.size() * RCNF.size()); +  for (auto &LDisjunction : LCNF) +    for (auto &RDisjunction : RCNF) { +      NormalForm::value_type Combined; +      Combined.reserve(LDisjunction.size() + RDisjunction.size()); +      std::copy(LDisjunction.begin(), LDisjunction.end(), +                std::back_inserter(Combined)); +      std::copy(RDisjunction.begin(), RDisjunction.end(), +                std::back_inserter(Combined)); +      Res.emplace_back(Combined); +    } +  return Res; +} + +static NormalForm makeDNF(const NormalizedConstraint &Normalized) { +  if (Normalized.isAtomic()) +    return {{Normalized.getAtomicConstraint()}}; + +  NormalForm LDNF = makeDNF(Normalized.getLHS()); +  NormalForm RDNF = makeDNF(Normalized.getRHS()); +  if (Normalized.getCompoundKind() == NormalizedConstraint::CCK_Disjunction) { +    LDNF.reserve(LDNF.size() + RDNF.size()); +    while (!RDNF.empty()) +      LDNF.push_back(RDNF.pop_back_val()); +    return LDNF; +  } + +  // Conjunction +  NormalForm Res; +  Res.reserve(LDNF.size() * RDNF.size()); +  for (auto &LConjunction : LDNF) { +    for (auto &RConjunction : RDNF) { +      NormalForm::value_type Combined; +      Combined.reserve(LConjunction.size() + RConjunction.size()); +      std::copy(LConjunction.begin(), LConjunction.end(), +                std::back_inserter(Combined)); +      std::copy(RConjunction.begin(), RConjunction.end(), +                std::back_inserter(Combined)); +      Res.emplace_back(Combined); +    } +  } +  return Res; +} + +template<typename AtomicSubsumptionEvaluator> +static bool subsumes(NormalForm PDNF, NormalForm QCNF, +                     AtomicSubsumptionEvaluator E) { +  // C++ [temp.constr.order] p2 +  //   Then, P subsumes Q if and only if, for every disjunctive clause Pi in the +  //   disjunctive normal form of P, Pi subsumes every conjunctive clause Qj in +  //   the conjuctive normal form of Q, where [...] +  for (const auto &Pi : PDNF) { +    for (const auto &Qj : QCNF) { +      // C++ [temp.constr.order] p2 +      //   - [...] a disjunctive clause Pi subsumes a conjunctive clause Qj if +      //     and only if there exists an atomic constraint Pia in Pi for which +      //     there exists an atomic constraint, Qjb, in Qj such that Pia +      //     subsumes Qjb. +      bool Found = false; +      for (const AtomicConstraint *Pia : Pi) { +        for (const AtomicConstraint *Qjb : Qj) { +          if (E(*Pia, *Qjb)) { +            Found = true; +            break; +          } +        } +        if (Found) +          break; +      } +      if (!Found) +        return false; +    } +  } +  return true; +} + +template<typename AtomicSubsumptionEvaluator> +static bool subsumes(Sema &S, NamedDecl *DP, ArrayRef<const Expr *> P, +                     NamedDecl *DQ, ArrayRef<const Expr *> Q, bool &Subsumes, +                     AtomicSubsumptionEvaluator E) { +  // C++ [temp.constr.order] p2 +  //   In order to determine if a constraint P subsumes a constraint Q, P is +  //   transformed into disjunctive normal form, and Q is transformed into +  //   conjunctive normal form. [...] +  auto *PNormalized = S.getNormalizedAssociatedConstraints(DP, P); +  if (!PNormalized) +    return true; +  const NormalForm PDNF = makeDNF(*PNormalized); + +  auto *QNormalized = S.getNormalizedAssociatedConstraints(DQ, Q); +  if (!QNormalized)      return true; +  const NormalForm QCNF = makeCNF(*QNormalized); + +  Subsumes = subsumes(PDNF, QCNF, E); +  return false; +} + +bool Sema::IsAtLeastAsConstrained(NamedDecl *D1, ArrayRef<const Expr *> AC1, +                                  NamedDecl *D2, ArrayRef<const Expr *> AC2, +                                  bool &Result) { +  if (AC1.empty()) { +    Result = AC2.empty(); +    return false; +  } +  if (AC2.empty()) { +    // TD1 has associated constraints and TD2 does not. +    Result = true; +    return false;    } -  IsSatisfied = EvalResult.Val.getInt().getBoolValue(); +  std::pair<NamedDecl *, NamedDecl *> Key{D1, D2}; +  auto CacheEntry = SubsumptionCache.find(Key); +  if (CacheEntry != SubsumptionCache.end()) { +    Result = CacheEntry->second; +    return false; +  } +  if (subsumes(*this, D1, AC1, D2, AC2, Result, +        [this] (const AtomicConstraint &A, const AtomicConstraint &B) { +          return A.subsumes(Context, B); +        })) +    return true; +  SubsumptionCache.try_emplace(Key, Result);    return false; -}
\ No newline at end of file +} + +bool Sema::MaybeEmitAmbiguousAtomicConstraintsDiagnostic(NamedDecl *D1, +    ArrayRef<const Expr *> AC1, NamedDecl *D2, ArrayRef<const Expr *> AC2) { +  if (isSFINAEContext()) +    // No need to work here because our notes would be discarded. +    return false; + +  if (AC1.empty() || AC2.empty()) +    return false; + +  auto NormalExprEvaluator = +      [this] (const AtomicConstraint &A, const AtomicConstraint &B) { +        return A.subsumes(Context, B); +      }; + +  const Expr *AmbiguousAtomic1 = nullptr, *AmbiguousAtomic2 = nullptr; +  auto IdenticalExprEvaluator = +      [&] (const AtomicConstraint &A, const AtomicConstraint &B) { +        if (!A.hasMatchingParameterMapping(Context, B)) +          return false; +        const Expr *EA = A.ConstraintExpr, *EB = B.ConstraintExpr; +        if (EA == EB) +          return true; + +        // Not the same source level expression - are the expressions +        // identical? +        llvm::FoldingSetNodeID IDA, IDB; +        EA->Profile(IDA, Context, /*Cannonical=*/true); +        EB->Profile(IDB, Context, /*Cannonical=*/true); +        if (IDA != IDB) +          return false; + +        AmbiguousAtomic1 = EA; +        AmbiguousAtomic2 = EB; +        return true; +      }; + +  { +    // The subsumption checks might cause diagnostics +    SFINAETrap Trap(*this); +    auto *Normalized1 = getNormalizedAssociatedConstraints(D1, AC1); +    if (!Normalized1) +      return false; +    const NormalForm DNF1 = makeDNF(*Normalized1); +    const NormalForm CNF1 = makeCNF(*Normalized1); + +    auto *Normalized2 = getNormalizedAssociatedConstraints(D2, AC2); +    if (!Normalized2) +      return false; +    const NormalForm DNF2 = makeDNF(*Normalized2); +    const NormalForm CNF2 = makeCNF(*Normalized2); + +    bool Is1AtLeastAs2Normally = subsumes(DNF1, CNF2, NormalExprEvaluator); +    bool Is2AtLeastAs1Normally = subsumes(DNF2, CNF1, NormalExprEvaluator); +    bool Is1AtLeastAs2 = subsumes(DNF1, CNF2, IdenticalExprEvaluator); +    bool Is2AtLeastAs1 = subsumes(DNF2, CNF1, IdenticalExprEvaluator); +    if (Is1AtLeastAs2 == Is1AtLeastAs2Normally && +        Is2AtLeastAs1 == Is2AtLeastAs1Normally) +      // Same result - no ambiguity was caused by identical atomic expressions. +      return false; +  } + +  // A different result! Some ambiguous atomic constraint(s) caused a difference +  assert(AmbiguousAtomic1 && AmbiguousAtomic2); + +  Diag(AmbiguousAtomic1->getBeginLoc(), diag::note_ambiguous_atomic_constraints) +      << AmbiguousAtomic1->getSourceRange(); +  Diag(AmbiguousAtomic2->getBeginLoc(), +       diag::note_ambiguous_atomic_constraints_similar_expression) +      << AmbiguousAtomic2->getSourceRange(); +  return true; +} | 
