Files
clang-r547379/include/clang/Sema/SemaConcept.h
Ryan Prichard 6024e5c395 Update prebuilt Clang to r547379 (20.0.0).
clang 20.0.0 (based on r547379) from build 12806354.

Bug: http://b/379133546
Test: N/A
Change-Id: I2eb8938af55d809de674be63cb30cf27e801862b

Upstream-Commit: ad834e67b1105d15ef907f6255d4c96e8e733f57
2025-11-26 14:59:46 -05:00

284 lines
10 KiB
C++
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

//===-- SemaConcept.h - Semantic Analysis for Constraints and Concepts ----===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
///
// This file provides semantic analysis for C++ constraints and concepts.
///
//===----------------------------------------------------------------------===//
#ifndef LLVM_CLANG_SEMA_SEMACONCEPT_H
#define LLVM_CLANG_SEMA_SEMACONCEPT_H
#include "clang/AST/ASTConcept.h"
#include "clang/AST/ASTContext.h"
#include "clang/AST/Expr.h"
#include "clang/AST/DeclTemplate.h"
#include "clang/Basic/SourceLocation.h"
#include "llvm/ADT/PointerUnion.h"
#include "llvm/ADT/SmallVector.h"
#include <optional>
#include <string>
#include <utility>
namespace clang {
class Sema;
enum { ConstraintAlignment = 8 };
struct alignas(ConstraintAlignment) AtomicConstraint {
const Expr *ConstraintExpr;
NamedDecl *ConstraintDecl;
std::optional<ArrayRef<TemplateArgumentLoc>> ParameterMapping;
AtomicConstraint(const Expr *ConstraintExpr, NamedDecl *ConstraintDecl)
: ConstraintExpr(ConstraintExpr), ConstraintDecl(ConstraintDecl) {};
bool hasMatchingParameterMapping(ASTContext &C,
const AtomicConstraint &Other) const {
if (!ParameterMapping != !Other.ParameterMapping)
return false;
if (!ParameterMapping)
return true;
if (ParameterMapping->size() != Other.ParameterMapping->size())
return false;
for (unsigned I = 0, S = ParameterMapping->size(); I < S; ++I) {
llvm::FoldingSetNodeID IDA, IDB;
C.getCanonicalTemplateArgument((*ParameterMapping)[I].getArgument())
.Profile(IDA, C);
C.getCanonicalTemplateArgument((*Other.ParameterMapping)[I].getArgument())
.Profile(IDB, C);
if (IDA != IDB)
return false;
}
return true;
}
bool subsumes(ASTContext &C, const AtomicConstraint &Other) const {
// C++ [temp.constr.order] p2
// - an atomic constraint A subsumes another atomic constraint B
// if and only if the A and B are identical [...]
//
// C++ [temp.constr.atomic] p2
// Two atomic constraints are identical if they are formed from the
// same expression and the targets of the parameter mappings are
// equivalent according to the rules for expressions [...]
// We do not actually substitute the parameter mappings into the
// constraint expressions, therefore the constraint expressions are
// the originals, and comparing them will suffice.
if (ConstraintExpr != Other.ConstraintExpr)
return false;
// Check that the parameter lists are identical
return hasMatchingParameterMapping(C, Other);
}
};
struct alignas(ConstraintAlignment) FoldExpandedConstraint;
using NormalFormConstraint =
llvm::PointerUnion<AtomicConstraint *, FoldExpandedConstraint *>;
struct NormalizedConstraint;
using NormalForm =
llvm::SmallVector<llvm::SmallVector<NormalFormConstraint, 2>, 4>;
// A constraint is in conjunctive normal form when it is a conjunction of
// clauses where each clause is a disjunction of atomic constraints. For atomic
// constraints A, B, and C, the constraint A  ∧ (B C) is in conjunctive
// normal form.
NormalForm makeCNF(const NormalizedConstraint &Normalized);
// A constraint is in disjunctive normal form when it is a disjunction of
// clauses where each clause is a conjunction of atomic constraints. For atomic
// constraints A, B, and C, the disjunctive normal form of the constraint A
//  ∧ (B C) is (A B) (A C).
NormalForm makeDNF(const NormalizedConstraint &Normalized);
struct alignas(ConstraintAlignment) NormalizedConstraintPair;
/// \brief A normalized constraint, as defined in C++ [temp.constr.normal], is
/// either an atomic constraint, a conjunction of normalized constraints or a
/// disjunction of normalized constraints.
struct NormalizedConstraint {
friend class Sema;
enum CompoundConstraintKind { CCK_Conjunction, CCK_Disjunction };
using CompoundConstraint = llvm::PointerIntPair<NormalizedConstraintPair *, 1,
CompoundConstraintKind>;
llvm::PointerUnion<AtomicConstraint *, FoldExpandedConstraint *,
CompoundConstraint>
Constraint;
NormalizedConstraint(AtomicConstraint *C): Constraint{C} { };
NormalizedConstraint(FoldExpandedConstraint *C) : Constraint{C} {};
NormalizedConstraint(ASTContext &C, NormalizedConstraint LHS,
NormalizedConstraint RHS, CompoundConstraintKind Kind);
NormalizedConstraint(ASTContext &C, const NormalizedConstraint &Other);
NormalizedConstraint(NormalizedConstraint &&Other):
Constraint(Other.Constraint) {
Other.Constraint = nullptr;
}
NormalizedConstraint &operator=(const NormalizedConstraint &Other) = delete;
NormalizedConstraint &operator=(NormalizedConstraint &&Other) {
if (&Other != this) {
NormalizedConstraint Temp(std::move(Other));
std::swap(Constraint, Temp.Constraint);
}
return *this;
}
bool isAtomic() const { return Constraint.is<AtomicConstraint *>(); }
bool isFoldExpanded() const {
return Constraint.is<FoldExpandedConstraint *>();
}
bool isCompound() const { return Constraint.is<CompoundConstraint>(); }
CompoundConstraintKind getCompoundKind() const {
assert(isCompound() && "getCompoundKind on a non-compound constraint..");
return Constraint.get<CompoundConstraint>().getInt();
}
NormalizedConstraint &getLHS() const;
NormalizedConstraint &getRHS() const;
AtomicConstraint *getAtomicConstraint() const {
assert(isAtomic() &&
"getAtomicConstraint called on non-atomic constraint.");
return Constraint.get<AtomicConstraint *>();
}
FoldExpandedConstraint *getFoldExpandedConstraint() const {
assert(isFoldExpanded() &&
"getFoldExpandedConstraint called on non-fold-expanded constraint.");
return Constraint.get<FoldExpandedConstraint *>();
}
private:
static std::optional<NormalizedConstraint>
fromConstraintExprs(Sema &S, NamedDecl *D, ArrayRef<const Expr *> E);
static std::optional<NormalizedConstraint>
fromConstraintExpr(Sema &S, NamedDecl *D, const Expr *E);
};
struct alignas(ConstraintAlignment) NormalizedConstraintPair {
NormalizedConstraint LHS, RHS;
};
struct alignas(ConstraintAlignment) FoldExpandedConstraint {
enum class FoldOperatorKind { And, Or } Kind;
NormalizedConstraint Constraint;
const Expr *Pattern;
FoldExpandedConstraint(FoldOperatorKind K, NormalizedConstraint C,
const Expr *Pattern)
: Kind(K), Constraint(std::move(C)), Pattern(Pattern) {};
template <typename AtomicSubsumptionEvaluator>
bool subsumes(const FoldExpandedConstraint &Other,
const AtomicSubsumptionEvaluator &E) const;
static bool AreCompatibleForSubsumption(const FoldExpandedConstraint &A,
const FoldExpandedConstraint &B);
};
const NormalizedConstraint *getNormalizedAssociatedConstraints(
Sema &S, NamedDecl *ConstrainedDecl,
ArrayRef<const Expr *> AssociatedConstraints);
template <typename AtomicSubsumptionEvaluator>
bool subsumes(const NormalForm &PDNF, const NormalForm &QCNF,
const 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 (NormalFormConstraint Pia : Pi) {
for (NormalFormConstraint Qjb : Qj) {
if (Pia.is<FoldExpandedConstraint *>() &&
Qjb.is<FoldExpandedConstraint *>()) {
if (Pia.get<FoldExpandedConstraint *>()->subsumes(
*Qjb.get<FoldExpandedConstraint *>(), E)) {
Found = true;
break;
}
} else if (Pia.is<AtomicConstraint *>() &&
Qjb.is<AtomicConstraint *>()) {
if (E(*Pia.get<AtomicConstraint *>(),
*Qjb.get<AtomicConstraint *>())) {
Found = true;
break;
}
}
}
if (Found)
break;
}
if (!Found)
return false;
}
}
return true;
}
template <typename AtomicSubsumptionEvaluator>
bool subsumes(Sema &S, NamedDecl *DP, ArrayRef<const Expr *> P, NamedDecl *DQ,
ArrayRef<const Expr *> Q, bool &Subsumes,
const 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. [...]
const NormalizedConstraint *PNormalized =
getNormalizedAssociatedConstraints(S, DP, P);
if (!PNormalized)
return true;
NormalForm PDNF = makeDNF(*PNormalized);
const NormalizedConstraint *QNormalized =
getNormalizedAssociatedConstraints(S, DQ, Q);
if (!QNormalized)
return true;
NormalForm QCNF = makeCNF(*QNormalized);
Subsumes = subsumes(PDNF, QCNF, E);
return false;
}
template <typename AtomicSubsumptionEvaluator>
bool FoldExpandedConstraint::subsumes(
const FoldExpandedConstraint &Other,
const AtomicSubsumptionEvaluator &E) const {
// [C++26] [temp.constr.order]
// a fold expanded constraint A subsumes another fold expanded constraint B if
// they are compatible for subsumption, have the same fold-operator, and the
// constraint of A subsumes that of B
if (Kind != Other.Kind || !AreCompatibleForSubsumption(*this, Other))
return false;
NormalForm PDNF = makeDNF(this->Constraint);
NormalForm QCNF = makeCNF(Other.Constraint);
return clang::subsumes(PDNF, QCNF, E);
}
} // clang
#endif // LLVM_CLANG_SEMA_SEMACONCEPT_H