Commit ffa214ef authored by Saar Raz's avatar Saar Raz
Browse files

[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
parent a1881820
Loading
Loading
Loading
Loading
+80 −0
Original line number Diff line number Diff line
//===--- ASTConcept.h - 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 provides AST data structures related to concepts.
///
//===----------------------------------------------------------------------===//

#ifndef LLVM_CLANG_AST_ASTCONCEPT_H
#define LLVM_CLANG_AST_ASTCONCEPT_H
#include "clang/AST/Expr.h"
#include "clang/Basic/SourceLocation.h"
#include "llvm/ADT/PointerUnion.h"
#include "llvm/ADT/SmallVector.h"
#include <string>
#include <utility>
namespace clang {

/// \brief The result of a constraint satisfaction check, containing the
/// necessary information to diagnose an unsatisfied constraint.
struct ConstraintSatisfaction {
  using SubstitutionDiagnostic = std::pair<SourceLocation, std::string>;
  using Detail = llvm::PointerUnion<Expr *, SubstitutionDiagnostic *>;

  bool IsSatisfied = false;

  /// \brief Pairs of unsatisfied atomic constraint expressions along with the
  /// substituted constraint expr, if the template arguments could be
  /// substituted into them, or a diagnostic if substitution resulted in an
  /// invalid expression.
  llvm::SmallVector<std::pair<const Expr *, Detail>, 4> Details;

  // This can leak if used in an AST node, use ASTConstraintSatisfaction
  // instead.
  void *operator new(size_t bytes, ASTContext &C) = delete;
};

/// Pairs of unsatisfied atomic constraint expressions along with the
/// substituted constraint expr, if the template arguments could be
/// substituted into them, or a diagnostic if substitution resulted in
/// an invalid expression.
using UnsatisfiedConstraintRecord =
    std::pair<const Expr *,
              llvm::PointerUnion<Expr *,
                                 std::pair<SourceLocation, StringRef> *>>;

/// \brief The result of a constraint satisfaction check, containing the
/// necessary information to diagnose an unsatisfied constraint.
///
/// This is safe to store in an AST node, as opposed to ConstraintSatisfaction.
struct ASTConstraintSatisfaction final :
    llvm::TrailingObjects<ASTConstraintSatisfaction,
                          UnsatisfiedConstraintRecord> {
  std::size_t NumRecords;
  bool IsSatisfied : 1;

  const UnsatisfiedConstraintRecord *begin() const {
    return getTrailingObjects<UnsatisfiedConstraintRecord>();
  }

  const UnsatisfiedConstraintRecord *end() const {
    return getTrailingObjects<UnsatisfiedConstraintRecord>() + NumRecords;
  }

  ASTConstraintSatisfaction(const ASTContext &C,
                            const ConstraintSatisfaction &Satisfaction);

  static ASTConstraintSatisfaction *
  Create(const ASTContext &C, const ConstraintSatisfaction &Satisfaction);
};

} // clang

#endif // LLVM_CLANG_AST_ASTCONCEPT_H
 No newline at end of file
+28 −11
Original line number Diff line number Diff line
@@ -14,6 +14,7 @@
#ifndef LLVM_CLANG_AST_EXPRCXX_H
#define LLVM_CLANG_AST_EXPRCXX_H

#include "clang/AST/ASTConcept.h"
#include "clang/AST/Decl.h"
#include "clang/AST/DeclBase.h"
#include "clang/AST/DeclCXX.h"
@@ -4851,6 +4852,10 @@ class ConceptSpecializationExpr final : public Expr,
                                    TemplateArgument> {
  friend class ASTStmtReader;
  friend TrailingObjects;
public:
  using SubstitutionDiagnostic = std::pair<SourceLocation, std::string>;

protected:

  // \brief The optional nested name specifier used when naming the concept.
  NestedNameSpecifierLoc NestedNameSpec;
@@ -4868,11 +4873,8 @@ class ConceptSpecializationExpr final : public Expr,
  /// through a UsingShadowDecl.
  NamedDecl *FoundDecl;

  /// \brief The concept named, and whether or not the concept with the given
  /// arguments was satisfied when the expression was created.
  /// If any of the template arguments are dependent (this expr would then be
  /// isValueDependent()), this bit is to be ignored.
  llvm::PointerIntPair<ConceptDecl *, 1, bool> NamedConcept;
  /// \brief The concept named.
  ConceptDecl *NamedConcept;

  /// \brief The template argument list source info used to specialize the
  /// concept.
@@ -4882,13 +4884,18 @@ class ConceptSpecializationExpr final : public Expr,
  /// converted template arguments.
  unsigned NumTemplateArgs;

  /// \brief Information about the satisfaction of the named concept with the
  /// given arguments. If this expression is value dependent, this is to be
  /// ignored.
  ASTConstraintSatisfaction *Satisfaction;

  ConceptSpecializationExpr(ASTContext &C, NestedNameSpecifierLoc NNS,
                            SourceLocation TemplateKWLoc,
                            SourceLocation ConceptNameLoc, NamedDecl *FoundDecl,
                            ConceptDecl *NamedConcept,
                            const ASTTemplateArgumentListInfo *ArgsAsWritten,
                            ArrayRef<TemplateArgument> ConvertedArgs,
                            Optional<bool> IsSatisfied);
                            const ConstraintSatisfaction *Satisfaction);

  ConceptSpecializationExpr(EmptyShell Empty, unsigned NumTemplateArgs);

@@ -4899,7 +4906,8 @@ public:
         SourceLocation TemplateKWLoc, SourceLocation ConceptNameLoc,
         NamedDecl *FoundDecl, ConceptDecl *NamedConcept,
         const ASTTemplateArgumentListInfo *ArgsAsWritten,
         ArrayRef<TemplateArgument> ConvertedArgs, Optional<bool> IsSatisfied);
         ArrayRef<TemplateArgument> ConvertedArgs,
         const ConstraintSatisfaction *Satisfaction);

  static ConceptSpecializationExpr *
  Create(ASTContext &C, EmptyShell Empty, unsigned NumTemplateArgs);
@@ -4913,7 +4921,7 @@ public:
  }

  ConceptDecl *getNamedConcept() const {
    return NamedConcept.getPointer();
    return NamedConcept;
  }

  ArrayRef<TemplateArgument> getTemplateArguments() const {
@@ -4930,12 +4938,21 @@ public:
                            ArrayRef<TemplateArgument> Converted);

  /// \brief Whether or not the concept with the given arguments was satisfied
  /// when the expression was created. This method assumes that the expression
  /// is not dependent!
  /// when the expression was created.
  /// The expression must not be dependent.
  bool isSatisfied() const {
    assert(!isValueDependent()
           && "isSatisfied called on a dependent ConceptSpecializationExpr");
    return NamedConcept.getInt();
    return Satisfaction->IsSatisfied;
  }

  /// \brief Get elaborated satisfaction info about the template arguments'
  /// satisfaction of the named concept.
  /// The expression must not be dependent.
  const ASTConstraintSatisfaction &getSatisfaction() const {
    assert(!isValueDependent()
           && "getSatisfaction called on dependent ConceptSpecializationExpr");
    return *Satisfaction;
  }

  SourceLocation getConceptNameLoc() const { return ConceptNameLoc; }
+30 −0
Original line number Diff line number Diff line
@@ -2542,6 +2542,26 @@ def err_non_constant_constraint_expression : Error<
  "expression">;
def err_non_bool_atomic_constraint : Error<
  "atomic constraint must be of type 'bool' (found %0)">;
def err_template_arg_list_constraints_not_satisfied : Error<
  "constraints not satisfied for %select{class template|function template|variable template|alias template|"
  "template template parameter|template}0 %1%2">;
def note_constraints_not_satisfied : Note<
  "constraints not satisfied">;
def note_substituted_constraint_expr_is_ill_formed : Note<
  "because substituted constraint expression is ill-formed%0">;
def note_atomic_constraint_evaluated_to_false : Note<
  "%select{and |because }0'%1' evaluated to false">;
def note_concept_specialization_constraint_evaluated_to_false : Note<
  "%select{and |because }0'%1' evaluated to false">;
def note_single_arg_concept_specialization_constraint_evaluated_to_false : Note<
  "%select{and |because }0%1 does not satisfy %2">;
def note_atomic_constraint_evaluated_to_false_elaborated : Note<
  "%select{and |because }0'%1' (%2 %3 %4) evaluated to false">;
def err_could_not_normalize_ill_formed_constraint : Error<
  "required expansion of concept specialization %0 failed, substituted "
  "expression would be illegal">;
def note_could_not_normalize_ill_formed_constraint_reason : Note<
  "because: %0">;

def err_template_different_requires_clause : Error<
  "requires clause differs in template redeclaration">;
@@ -3820,6 +3840,8 @@ def note_ovl_candidate_inconsistent_deduction_types : Note<
def note_ovl_candidate_explicit_arg_mismatch_named : Note<
    "candidate template ignored: invalid explicitly-specified argument "
    "for template parameter %0">;
def note_ovl_candidate_unsatisfied_constraints : Note<
    "candidate template ignored: constraints not satisfied%0">;
def note_ovl_candidate_explicit_arg_mismatch_unnamed : Note<
    "candidate template ignored: invalid explicitly-specified argument "
    "for %ordinal0 template parameter">;
@@ -4512,6 +4534,14 @@ def note_template_default_arg_checking : Note<
  "while checking a default template argument used here">;
def note_concept_specialization_here : Note<
  "while checking the satisfaction of concept '%0' requested here">;
def note_checking_constraints_for_template_id_here : Note<
  "while checking constraint satisfaction for template '%0' required here">;
def note_checking_constraints_for_var_spec_id_here : Note<
  "while checking constraint satisfaction for variable template "
  "partial specialization '%0' required here">;
def note_checking_constraints_for_class_spec_id_here : Note<
  "while checking constraint satisfaction for class template partial "
  "specialization '%0' required here">;
def note_constraint_substitution_here : Note<
  "while substituting template arguments into constraint expression here">;
def note_instantiation_contexts_suppressed : Note<
+83 −7
Original line number Diff line number Diff line
@@ -14,6 +14,7 @@
#ifndef LLVM_CLANG_SEMA_SEMA_H
#define LLVM_CLANG_SEMA_SEMA_H
#include "clang/AST/ASTConcept.h"
#include "clang/AST/Attr.h"
#include "clang/AST/Availability.h"
#include "clang/AST/ComparisonCategories.h"
@@ -6131,10 +6132,45 @@ public:
  /// A diagnostic is emitted if it is not, and false is returned.
  bool CheckConstraintExpression(Expr *CE);
  bool CalculateConstraintSatisfaction(ConceptDecl *NamedConcept,
                                       MultiLevelTemplateArgumentList &MLTAL,
                                       Expr *ConstraintExpr,
                                       bool &IsSatisfied);
  /// \brief Check whether the given list of constraint expressions are
  /// satisfied (as if in a 'conjunction') given template arguments.
  /// \param ConstraintExprs a list of constraint expressions, treated as if
  /// they were 'AND'ed together.
  /// \param TemplateArgs the list of template arguments to substitute into the
  /// constraint expression.
  /// \param TemplateIDRange The source range of the template id that
  /// caused the constraints check.
  /// \param Satisfaction if true is returned, will contain details of the
  /// satisfaction, with enough information to diagnose an unsatisfied
  /// expression.
  /// \returns true if an error occurred and satisfaction could not be checked,
  /// false otherwise.
  bool CheckConstraintSatisfaction(TemplateDecl *Template,
                                   ArrayRef<const Expr *> ConstraintExprs,
                                   ArrayRef<TemplateArgument> TemplateArgs,
                                   SourceRange TemplateIDRange,
                                   ConstraintSatisfaction &Satisfaction);
  bool CheckConstraintSatisfaction(ClassTemplatePartialSpecializationDecl *TD,
                                   ArrayRef<const Expr *> ConstraintExprs,
                                   ArrayRef<TemplateArgument> TemplateArgs,
                                   SourceRange TemplateIDRange,
                                   ConstraintSatisfaction &Satisfaction);
  bool CheckConstraintSatisfaction(VarTemplatePartialSpecializationDecl *TD,
                                   ArrayRef<const Expr *> ConstraintExprs,
                                   ArrayRef<TemplateArgument> TemplateArgs,
                                   SourceRange TemplateIDRange,
                                   ConstraintSatisfaction &Satisfaction);
  /// \brief Check whether the given non-dependent constraint expression is
  /// satisfied. Returns false and updates Satisfaction with the satisfaction
  /// verdict if successful, emits a diagnostic and returns true if an error
  /// occured and satisfaction could not be determined.
  ///
  /// \returns true if an error occurred, false otherwise.
  bool CheckConstraintSatisfaction(const Expr *ConstraintExpr,
                                   ConstraintSatisfaction &Satisfaction);
  /// Check that the associated constraints of a template declaration match the
  /// associated constraints of an older declaration of which it is a
@@ -6142,6 +6178,38 @@ public:
  bool CheckRedeclarationConstraintMatch(TemplateParameterList *Old,
                                         TemplateParameterList *New);
  /// \brief Ensure that the given template arguments satisfy the constraints
  /// associated with the given template, emitting a diagnostic if they do not.
  ///
  /// \param Template The template to which the template arguments are being
  /// provided.
  ///
  /// \param TemplateArgs The converted, canonicalized template arguments.
  ///
  /// \param TemplateIDRange The source range of the template id that
  /// caused the constraints check.
  ///
  /// \returns true if the constrains are not satisfied or could not be checked
  /// for satisfaction, false if the constraints are satisfied.
  bool EnsureTemplateArgumentListConstraints(TemplateDecl *Template,
                                       ArrayRef<TemplateArgument> TemplateArgs,
                                             SourceRange TemplateIDRange);
  /// \brief Emit diagnostics explaining why a constraint expression was deemed
  /// unsatisfied.
  void
  DiagnoseUnsatisfiedConstraint(const ConstraintSatisfaction& Satisfaction);
  /// \brief Emit diagnostics explaining why a constraint expression was deemed
  /// unsatisfied.
  void
  DiagnoseUnsatisfiedConstraint(const ASTConstraintSatisfaction& Satisfaction);
  /// \brief Emit diagnostics explaining why a constraint expression was deemed
  /// unsatisfied because it was ill-formed.
  void DiagnoseUnsatisfiedIllFormedConstraint(SourceLocation DiagnosticLocation,
                                              StringRef Diagnostic);
  // ParseObjCStringLiteral - Parse Objective-C string literals.
  ExprResult ParseObjCStringLiteral(SourceLocation *AtLocs,
                                    ArrayRef<Expr *> Strings);
@@ -6957,13 +7025,18 @@ public:
  /// contain the converted forms of the template arguments as written.
  /// Otherwise, \p TemplateArgs will not be modified.
  ///
  /// \param ConstraintsNotSatisfied If provided, and an error occured, will
  /// receive true if the cause for the error is the associated constraints of
  /// the template not being satisfied by the template arguments.
  ///
  /// \returns true if an error occurred, false otherwise.
  bool CheckTemplateArgumentList(TemplateDecl *Template,
                                 SourceLocation TemplateLoc,
                                 TemplateArgumentListInfo &TemplateArgs,
                                 bool PartialTemplateArgs,
                                 SmallVectorImpl<TemplateArgument> &Converted,
                                 bool UpdateArgsWithConversions = true);
                                 bool UpdateArgsWithConversions = true,
                                 bool *ConstraintsNotSatisfied = nullptr);
  bool CheckTemplateTypeArgument(TemplateTypeParmDecl *Param,
                                 TemplateArgumentLoc &Arg,
@@ -7505,6 +7578,9 @@ public:
    TDK_InvalidExplicitArguments,
    /// Checking non-dependent argument conversions failed.
    TDK_NonDependentConversionFailure,
    /// The deduced arguments did not satisfy the constraints associated
    /// with the template.
    TDK_ConstraintsNotSatisfied,
    /// Deduction failed; that's all we know.
    TDK_MiscellaneousDeductionFailure,
    /// CUDA Target attributes do not match.
@@ -8017,7 +8093,7 @@ public:
    /// constrained entity (a concept declaration or a template with associated
    /// constraints).
    InstantiatingTemplate(Sema &SemaRef, SourceLocation PointOfInstantiation,
                          ConstraintsCheck, TemplateDecl *Template,
                          ConstraintsCheck, NamedDecl *Template,
                          ArrayRef<TemplateArgument> TemplateArgs,
                          SourceRange InstantiationRange);
@@ -8026,7 +8102,7 @@ public:
    /// with a template declaration or as part of the satisfaction check of a
    /// concept.
    InstantiatingTemplate(Sema &SemaRef, SourceLocation PointOfInstantiation,
                          ConstraintSubstitution, TemplateDecl *Template,
                          ConstraintSubstitution, NamedDecl *Template,
                          sema::TemplateDeductionInfo &DeductionInfo,
                          SourceRange InstantiationRange);
+6 −0
Original line number Diff line number Diff line
@@ -14,6 +14,8 @@
#ifndef LLVM_CLANG_SEMA_TEMPLATEDEDUCTION_H
#define LLVM_CLANG_SEMA_TEMPLATEDEDUCTION_H

#include "clang/Sema/Ownership.h"
#include "clang/AST/ASTConcept.h"
#include "clang/AST/DeclAccessPair.h"
#include "clang/AST/DeclTemplate.h"
#include "clang/AST/TemplateBase.h"
@@ -218,6 +220,10 @@ public:
  ///
  /// FIXME: This should be kept internal to SemaTemplateDeduction.
  SmallVector<DeducedPack *, 8> PendingDeducedPacks;

  /// \brief The constraint satisfaction details resulting from the associated
  /// constraints satisfaction tests.
  ConstraintSatisfaction AssociatedConstraintsSatisfaction;
};

} // namespace sema
Loading