blob: dc5f0ec97e857588d0663b44d4b5edc427e38536 [file] [log] [blame]
//===-- 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/Optional.h"
#include "llvm/ADT/SmallVector.h"
#include <string>
#include <utility>
namespace clang {
class Sema;
struct AtomicConstraint {
const Expr *ConstraintExpr;
Optional<MutableArrayRef<TemplateArgumentLoc>> ParameterMapping;
AtomicConstraint(Sema &S, const Expr *ConstraintExpr) :
ConstraintExpr(ConstraintExpr) { };
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);
}
};
/// \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<
std::pair<NormalizedConstraint, NormalizedConstraint> *, 1,
CompoundConstraintKind>;
llvm::PointerUnion<AtomicConstraint *, CompoundConstraint> Constraint;
NormalizedConstraint(AtomicConstraint *C): Constraint{C} { };
NormalizedConstraint(ASTContext &C, NormalizedConstraint LHS,
NormalizedConstraint RHS, CompoundConstraintKind Kind)
: Constraint{CompoundConstraint{
new (C) std::pair<NormalizedConstraint, NormalizedConstraint>{
std::move(LHS), std::move(RHS)}, Kind}} { };
NormalizedConstraint(ASTContext &C, const NormalizedConstraint &Other) {
if (Other.isAtomic()) {
Constraint = new (C) AtomicConstraint(*Other.getAtomicConstraint());
} else {
Constraint = CompoundConstraint(
new (C) std::pair<NormalizedConstraint, NormalizedConstraint>{
NormalizedConstraint(C, Other.getLHS()),
NormalizedConstraint(C, Other.getRHS())},
Other.getCompoundKind());
}
}
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;
}
CompoundConstraintKind getCompoundKind() const {
assert(!isAtomic() && "getCompoundKind called on atomic constraint.");
return Constraint.get<CompoundConstraint>().getInt();
}
bool isAtomic() const { return Constraint.is<AtomicConstraint *>(); }
NormalizedConstraint &getLHS() const {
assert(!isAtomic() && "getLHS called on atomic constraint.");
return Constraint.get<CompoundConstraint>().getPointer()->first;
}
NormalizedConstraint &getRHS() const {
assert(!isAtomic() && "getRHS called on atomic constraint.");
return Constraint.get<CompoundConstraint>().getPointer()->second;
}
AtomicConstraint *getAtomicConstraint() const {
assert(isAtomic() &&
"getAtomicConstraint called on non-atomic constraint.");
return Constraint.get<AtomicConstraint *>();
}
private:
static Optional<NormalizedConstraint>
fromConstraintExprs(Sema &S, NamedDecl *D, ArrayRef<const Expr *> E);
static Optional<NormalizedConstraint>
fromConstraintExpr(Sema &S, NamedDecl *D, const Expr *E);
};
} // clang
#endif //LLVM_CLANG_SEMA_SEMACONCEPT_H