Skip to content

Commit ffa214e

Browse files
committed
[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
1 parent a188182 commit ffa214e

23 files changed

+1059
-133
lines changed

clang/include/clang/AST/ASTConcept.h

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
//===--- ASTConcept.h - Concepts Related AST Data Structures ----*- C++ -*-===//
2+
//
3+
// The LLVM Compiler Infrastructure
4+
//
5+
// This file is distributed under the University of Illinois Open Source
6+
// License. See LICENSE.TXT for details.
7+
//
8+
//===----------------------------------------------------------------------===//
9+
///
10+
/// \file
11+
/// \brief This file provides AST data structures related to concepts.
12+
///
13+
//===----------------------------------------------------------------------===//
14+
15+
#ifndef LLVM_CLANG_AST_ASTCONCEPT_H
16+
#define LLVM_CLANG_AST_ASTCONCEPT_H
17+
#include "clang/AST/Expr.h"
18+
#include "clang/Basic/SourceLocation.h"
19+
#include "llvm/ADT/PointerUnion.h"
20+
#include "llvm/ADT/SmallVector.h"
21+
#include <string>
22+
#include <utility>
23+
namespace clang {
24+
25+
/// \brief The result of a constraint satisfaction check, containing the
26+
/// necessary information to diagnose an unsatisfied constraint.
27+
struct ConstraintSatisfaction {
28+
using SubstitutionDiagnostic = std::pair<SourceLocation, std::string>;
29+
using Detail = llvm::PointerUnion<Expr *, SubstitutionDiagnostic *>;
30+
31+
bool IsSatisfied = false;
32+
33+
/// \brief Pairs of unsatisfied atomic constraint expressions along with the
34+
/// substituted constraint expr, if the template arguments could be
35+
/// substituted into them, or a diagnostic if substitution resulted in an
36+
/// invalid expression.
37+
llvm::SmallVector<std::pair<const Expr *, Detail>, 4> Details;
38+
39+
// This can leak if used in an AST node, use ASTConstraintSatisfaction
40+
// instead.
41+
void *operator new(size_t bytes, ASTContext &C) = delete;
42+
};
43+
44+
/// Pairs of unsatisfied atomic constraint expressions along with the
45+
/// substituted constraint expr, if the template arguments could be
46+
/// substituted into them, or a diagnostic if substitution resulted in
47+
/// an invalid expression.
48+
using UnsatisfiedConstraintRecord =
49+
std::pair<const Expr *,
50+
llvm::PointerUnion<Expr *,
51+
std::pair<SourceLocation, StringRef> *>>;
52+
53+
/// \brief The result of a constraint satisfaction check, containing the
54+
/// necessary information to diagnose an unsatisfied constraint.
55+
///
56+
/// This is safe to store in an AST node, as opposed to ConstraintSatisfaction.
57+
struct ASTConstraintSatisfaction final :
58+
llvm::TrailingObjects<ASTConstraintSatisfaction,
59+
UnsatisfiedConstraintRecord> {
60+
std::size_t NumRecords;
61+
bool IsSatisfied : 1;
62+
63+
const UnsatisfiedConstraintRecord *begin() const {
64+
return getTrailingObjects<UnsatisfiedConstraintRecord>();
65+
}
66+
67+
const UnsatisfiedConstraintRecord *end() const {
68+
return getTrailingObjects<UnsatisfiedConstraintRecord>() + NumRecords;
69+
}
70+
71+
ASTConstraintSatisfaction(const ASTContext &C,
72+
const ConstraintSatisfaction &Satisfaction);
73+
74+
static ASTConstraintSatisfaction *
75+
Create(const ASTContext &C, const ConstraintSatisfaction &Satisfaction);
76+
};
77+
78+
} // clang
79+
80+
#endif // LLVM_CLANG_AST_ASTCONCEPT_H

clang/include/clang/AST/ExprCXX.h

Lines changed: 28 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
#ifndef LLVM_CLANG_AST_EXPRCXX_H
1515
#define LLVM_CLANG_AST_EXPRCXX_H
1616

17+
#include "clang/AST/ASTConcept.h"
1718
#include "clang/AST/Decl.h"
1819
#include "clang/AST/DeclBase.h"
1920
#include "clang/AST/DeclCXX.h"
@@ -4851,6 +4852,10 @@ class ConceptSpecializationExpr final : public Expr,
48514852
TemplateArgument> {
48524853
friend class ASTStmtReader;
48534854
friend TrailingObjects;
4855+
public:
4856+
using SubstitutionDiagnostic = std::pair<SourceLocation, std::string>;
4857+
4858+
protected:
48544859

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

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

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

4887+
/// \brief Information about the satisfaction of the named concept with the
4888+
/// given arguments. If this expression is value dependent, this is to be
4889+
/// ignored.
4890+
ASTConstraintSatisfaction *Satisfaction;
4891+
48854892
ConceptSpecializationExpr(ASTContext &C, NestedNameSpecifierLoc NNS,
48864893
SourceLocation TemplateKWLoc,
48874894
SourceLocation ConceptNameLoc, NamedDecl *FoundDecl,
48884895
ConceptDecl *NamedConcept,
48894896
const ASTTemplateArgumentListInfo *ArgsAsWritten,
48904897
ArrayRef<TemplateArgument> ConvertedArgs,
4891-
Optional<bool> IsSatisfied);
4898+
const ConstraintSatisfaction *Satisfaction);
48924899

48934900
ConceptSpecializationExpr(EmptyShell Empty, unsigned NumTemplateArgs);
48944901

@@ -4899,7 +4906,8 @@ class ConceptSpecializationExpr final : public Expr,
48994906
SourceLocation TemplateKWLoc, SourceLocation ConceptNameLoc,
49004907
NamedDecl *FoundDecl, ConceptDecl *NamedConcept,
49014908
const ASTTemplateArgumentListInfo *ArgsAsWritten,
4902-
ArrayRef<TemplateArgument> ConvertedArgs, Optional<bool> IsSatisfied);
4909+
ArrayRef<TemplateArgument> ConvertedArgs,
4910+
const ConstraintSatisfaction *Satisfaction);
49034911

49044912
static ConceptSpecializationExpr *
49054913
Create(ASTContext &C, EmptyShell Empty, unsigned NumTemplateArgs);
@@ -4913,7 +4921,7 @@ class ConceptSpecializationExpr final : public Expr,
49134921
}
49144922

49154923
ConceptDecl *getNamedConcept() const {
4916-
return NamedConcept.getPointer();
4924+
return NamedConcept;
49174925
}
49184926

49194927
ArrayRef<TemplateArgument> getTemplateArguments() const {
@@ -4930,12 +4938,21 @@ class ConceptSpecializationExpr final : public Expr,
49304938
ArrayRef<TemplateArgument> Converted);
49314939

49324940
/// \brief Whether or not the concept with the given arguments was satisfied
4933-
/// when the expression was created. This method assumes that the expression
4934-
/// is not dependent!
4941+
/// when the expression was created.
4942+
/// The expression must not be dependent.
49354943
bool isSatisfied() const {
49364944
assert(!isValueDependent()
49374945
&& "isSatisfied called on a dependent ConceptSpecializationExpr");
4938-
return NamedConcept.getInt();
4946+
return Satisfaction->IsSatisfied;
4947+
}
4948+
4949+
/// \brief Get elaborated satisfaction info about the template arguments'
4950+
/// satisfaction of the named concept.
4951+
/// The expression must not be dependent.
4952+
const ASTConstraintSatisfaction &getSatisfaction() const {
4953+
assert(!isValueDependent()
4954+
&& "getSatisfaction called on dependent ConceptSpecializationExpr");
4955+
return *Satisfaction;
49394956
}
49404957

49414958
SourceLocation getConceptNameLoc() const { return ConceptNameLoc; }

clang/include/clang/Basic/DiagnosticSemaKinds.td

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2542,6 +2542,26 @@ def err_non_constant_constraint_expression : Error<
25422542
"expression">;
25432543
def err_non_bool_atomic_constraint : Error<
25442544
"atomic constraint must be of type 'bool' (found %0)">;
2545+
def err_template_arg_list_constraints_not_satisfied : Error<
2546+
"constraints not satisfied for %select{class template|function template|variable template|alias template|"
2547+
"template template parameter|template}0 %1%2">;
2548+
def note_constraints_not_satisfied : Note<
2549+
"constraints not satisfied">;
2550+
def note_substituted_constraint_expr_is_ill_formed : Note<
2551+
"because substituted constraint expression is ill-formed%0">;
2552+
def note_atomic_constraint_evaluated_to_false : Note<
2553+
"%select{and |because }0'%1' evaluated to false">;
2554+
def note_concept_specialization_constraint_evaluated_to_false : Note<
2555+
"%select{and |because }0'%1' evaluated to false">;
2556+
def note_single_arg_concept_specialization_constraint_evaluated_to_false : Note<
2557+
"%select{and |because }0%1 does not satisfy %2">;
2558+
def note_atomic_constraint_evaluated_to_false_elaborated : Note<
2559+
"%select{and |because }0'%1' (%2 %3 %4) evaluated to false">;
2560+
def err_could_not_normalize_ill_formed_constraint : Error<
2561+
"required expansion of concept specialization %0 failed, substituted "
2562+
"expression would be illegal">;
2563+
def note_could_not_normalize_ill_formed_constraint_reason : Note<
2564+
"because: %0">;
25452565

25462566
def err_template_different_requires_clause : Error<
25472567
"requires clause differs in template redeclaration">;
@@ -3820,6 +3840,8 @@ def note_ovl_candidate_inconsistent_deduction_types : Note<
38203840
def note_ovl_candidate_explicit_arg_mismatch_named : Note<
38213841
"candidate template ignored: invalid explicitly-specified argument "
38223842
"for template parameter %0">;
3843+
def note_ovl_candidate_unsatisfied_constraints : Note<
3844+
"candidate template ignored: constraints not satisfied%0">;
38233845
def note_ovl_candidate_explicit_arg_mismatch_unnamed : Note<
38243846
"candidate template ignored: invalid explicitly-specified argument "
38253847
"for %ordinal0 template parameter">;
@@ -4512,6 +4534,14 @@ def note_template_default_arg_checking : Note<
45124534
"while checking a default template argument used here">;
45134535
def note_concept_specialization_here : Note<
45144536
"while checking the satisfaction of concept '%0' requested here">;
4537+
def note_checking_constraints_for_template_id_here : Note<
4538+
"while checking constraint satisfaction for template '%0' required here">;
4539+
def note_checking_constraints_for_var_spec_id_here : Note<
4540+
"while checking constraint satisfaction for variable template "
4541+
"partial specialization '%0' required here">;
4542+
def note_checking_constraints_for_class_spec_id_here : Note<
4543+
"while checking constraint satisfaction for class template partial "
4544+
"specialization '%0' required here">;
45154545
def note_constraint_substitution_here : Note<
45164546
"while substituting template arguments into constraint expression here">;
45174547
def note_instantiation_contexts_suppressed : Note<

clang/include/clang/Sema/Sema.h

Lines changed: 83 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
#ifndef LLVM_CLANG_SEMA_SEMA_H
1515
#define LLVM_CLANG_SEMA_SEMA_H
1616

17+
#include "clang/AST/ASTConcept.h"
1718
#include "clang/AST/Attr.h"
1819
#include "clang/AST/Availability.h"
1920
#include "clang/AST/ComparisonCategories.h"
@@ -6131,17 +6132,84 @@ class Sema {
61316132
/// A diagnostic is emitted if it is not, and false is returned.
61326133
bool CheckConstraintExpression(Expr *CE);
61336134

6134-
bool CalculateConstraintSatisfaction(ConceptDecl *NamedConcept,
6135-
MultiLevelTemplateArgumentList &MLTAL,
6136-
Expr *ConstraintExpr,
6137-
bool &IsSatisfied);
6135+
/// \brief Check whether the given list of constraint expressions are
6136+
/// satisfied (as if in a 'conjunction') given template arguments.
6137+
/// \param ConstraintExprs a list of constraint expressions, treated as if
6138+
/// they were 'AND'ed together.
6139+
/// \param TemplateArgs the list of template arguments to substitute into the
6140+
/// constraint expression.
6141+
/// \param TemplateIDRange The source range of the template id that
6142+
/// caused the constraints check.
6143+
/// \param Satisfaction if true is returned, will contain details of the
6144+
/// satisfaction, with enough information to diagnose an unsatisfied
6145+
/// expression.
6146+
/// \returns true if an error occurred and satisfaction could not be checked,
6147+
/// false otherwise.
6148+
bool CheckConstraintSatisfaction(TemplateDecl *Template,
6149+
ArrayRef<const Expr *> ConstraintExprs,
6150+
ArrayRef<TemplateArgument> TemplateArgs,
6151+
SourceRange TemplateIDRange,
6152+
ConstraintSatisfaction &Satisfaction);
6153+
6154+
bool CheckConstraintSatisfaction(ClassTemplatePartialSpecializationDecl *TD,
6155+
ArrayRef<const Expr *> ConstraintExprs,
6156+
ArrayRef<TemplateArgument> TemplateArgs,
6157+
SourceRange TemplateIDRange,
6158+
ConstraintSatisfaction &Satisfaction);
6159+
6160+
bool CheckConstraintSatisfaction(VarTemplatePartialSpecializationDecl *TD,
6161+
ArrayRef<const Expr *> ConstraintExprs,
6162+
ArrayRef<TemplateArgument> TemplateArgs,
6163+
SourceRange TemplateIDRange,
6164+
ConstraintSatisfaction &Satisfaction);
6165+
6166+
/// \brief Check whether the given non-dependent constraint expression is
6167+
/// satisfied. Returns false and updates Satisfaction with the satisfaction
6168+
/// verdict if successful, emits a diagnostic and returns true if an error
6169+
/// occured and satisfaction could not be determined.
6170+
///
6171+
/// \returns true if an error occurred, false otherwise.
6172+
bool CheckConstraintSatisfaction(const Expr *ConstraintExpr,
6173+
ConstraintSatisfaction &Satisfaction);
61386174

61396175
/// Check that the associated constraints of a template declaration match the
61406176
/// associated constraints of an older declaration of which it is a
61416177
/// redeclaration.
61426178
bool CheckRedeclarationConstraintMatch(TemplateParameterList *Old,
61436179
TemplateParameterList *New);
61446180

6181+
/// \brief Ensure that the given template arguments satisfy the constraints
6182+
/// associated with the given template, emitting a diagnostic if they do not.
6183+
///
6184+
/// \param Template The template to which the template arguments are being
6185+
/// provided.
6186+
///
6187+
/// \param TemplateArgs The converted, canonicalized template arguments.
6188+
///
6189+
/// \param TemplateIDRange The source range of the template id that
6190+
/// caused the constraints check.
6191+
///
6192+
/// \returns true if the constrains are not satisfied or could not be checked
6193+
/// for satisfaction, false if the constraints are satisfied.
6194+
bool EnsureTemplateArgumentListConstraints(TemplateDecl *Template,
6195+
ArrayRef<TemplateArgument> TemplateArgs,
6196+
SourceRange TemplateIDRange);
6197+
6198+
/// \brief Emit diagnostics explaining why a constraint expression was deemed
6199+
/// unsatisfied.
6200+
void
6201+
DiagnoseUnsatisfiedConstraint(const ConstraintSatisfaction& Satisfaction);
6202+
6203+
/// \brief Emit diagnostics explaining why a constraint expression was deemed
6204+
/// unsatisfied.
6205+
void
6206+
DiagnoseUnsatisfiedConstraint(const ASTConstraintSatisfaction& Satisfaction);
6207+
6208+
/// \brief Emit diagnostics explaining why a constraint expression was deemed
6209+
/// unsatisfied because it was ill-formed.
6210+
void DiagnoseUnsatisfiedIllFormedConstraint(SourceLocation DiagnosticLocation,
6211+
StringRef Diagnostic);
6212+
61456213
// ParseObjCStringLiteral - Parse Objective-C string literals.
61466214
ExprResult ParseObjCStringLiteral(SourceLocation *AtLocs,
61476215
ArrayRef<Expr *> Strings);
@@ -6957,13 +7025,18 @@ class Sema {
69577025
/// contain the converted forms of the template arguments as written.
69587026
/// Otherwise, \p TemplateArgs will not be modified.
69597027
///
7028+
/// \param ConstraintsNotSatisfied If provided, and an error occured, will
7029+
/// receive true if the cause for the error is the associated constraints of
7030+
/// the template not being satisfied by the template arguments.
7031+
///
69607032
/// \returns true if an error occurred, false otherwise.
69617033
bool CheckTemplateArgumentList(TemplateDecl *Template,
69627034
SourceLocation TemplateLoc,
69637035
TemplateArgumentListInfo &TemplateArgs,
69647036
bool PartialTemplateArgs,
69657037
SmallVectorImpl<TemplateArgument> &Converted,
6966-
bool UpdateArgsWithConversions = true);
7038+
bool UpdateArgsWithConversions = true,
7039+
bool *ConstraintsNotSatisfied = nullptr);
69677040

69687041
bool CheckTemplateTypeArgument(TemplateTypeParmDecl *Param,
69697042
TemplateArgumentLoc &Arg,
@@ -7505,6 +7578,9 @@ class Sema {
75057578
TDK_InvalidExplicitArguments,
75067579
/// Checking non-dependent argument conversions failed.
75077580
TDK_NonDependentConversionFailure,
7581+
/// The deduced arguments did not satisfy the constraints associated
7582+
/// with the template.
7583+
TDK_ConstraintsNotSatisfied,
75087584
/// Deduction failed; that's all we know.
75097585
TDK_MiscellaneousDeductionFailure,
75107586
/// CUDA Target attributes do not match.
@@ -8017,7 +8093,7 @@ class Sema {
80178093
/// constrained entity (a concept declaration or a template with associated
80188094
/// constraints).
80198095
InstantiatingTemplate(Sema &SemaRef, SourceLocation PointOfInstantiation,
8020-
ConstraintsCheck, TemplateDecl *Template,
8096+
ConstraintsCheck, NamedDecl *Template,
80218097
ArrayRef<TemplateArgument> TemplateArgs,
80228098
SourceRange InstantiationRange);
80238099

@@ -8026,7 +8102,7 @@ class Sema {
80268102
/// with a template declaration or as part of the satisfaction check of a
80278103
/// concept.
80288104
InstantiatingTemplate(Sema &SemaRef, SourceLocation PointOfInstantiation,
8029-
ConstraintSubstitution, TemplateDecl *Template,
8105+
ConstraintSubstitution, NamedDecl *Template,
80308106
sema::TemplateDeductionInfo &DeductionInfo,
80318107
SourceRange InstantiationRange);
80328108

clang/include/clang/Sema/TemplateDeduction.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@
1414
#ifndef LLVM_CLANG_SEMA_TEMPLATEDEDUCTION_H
1515
#define LLVM_CLANG_SEMA_TEMPLATEDEDUCTION_H
1616

17+
#include "clang/Sema/Ownership.h"
18+
#include "clang/AST/ASTConcept.h"
1719
#include "clang/AST/DeclAccessPair.h"
1820
#include "clang/AST/DeclTemplate.h"
1921
#include "clang/AST/TemplateBase.h"
@@ -218,6 +220,10 @@ class TemplateDeductionInfo {
218220
///
219221
/// FIXME: This should be kept internal to SemaTemplateDeduction.
220222
SmallVector<DeducedPack *, 8> PendingDeducedPacks;
223+
224+
/// \brief The constraint satisfaction details resulting from the associated
225+
/// constraints satisfaction tests.
226+
ConstraintSatisfaction AssociatedConstraintsSatisfaction;
221227
};
222228

223229
} // namespace sema

0 commit comments

Comments
 (0)