mirror of
https://github.com/capstone-engine/llvm-capstone.git
synced 2024-12-22 15:31:00 +00:00
[Concepts] Concept Specialization Expressions
Part of C++20 Concepts implementation effort. Added Concept Specialization Expressions that are created when a concept is refe$ D41217 on Phabricator. (recommit after fixing failing Parser test on windows) llvm-svn: 374903
This commit is contained in:
parent
d545c9056e
commit
5d98ba6077
@ -17,6 +17,7 @@
|
||||
#include "clang/AST/Decl.h"
|
||||
#include "clang/AST/DeclBase.h"
|
||||
#include "clang/AST/DeclCXX.h"
|
||||
#include "clang/AST/DeclTemplate.h"
|
||||
#include "clang/AST/DeclarationName.h"
|
||||
#include "clang/AST/Expr.h"
|
||||
#include "clang/AST/NestedNameSpecifier.h"
|
||||
@ -56,6 +57,7 @@ class IdentifierInfo;
|
||||
class LambdaCapture;
|
||||
class NonTypeTemplateParmDecl;
|
||||
class TemplateParameterList;
|
||||
class Sema;
|
||||
|
||||
//===--------------------------------------------------------------------===//
|
||||
// C++ Expressions.
|
||||
@ -4750,6 +4752,125 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
/// \brief Represents the specialization of a concept - evaluates to a prvalue
|
||||
/// of type bool.
|
||||
///
|
||||
/// According to C++2a [expr.prim.id]p3 an id-expression that denotes the
|
||||
/// specialization of a concept results in a prvalue of type bool.
|
||||
class ConceptSpecializationExpr final : public Expr,
|
||||
private llvm::TrailingObjects<ConceptSpecializationExpr,
|
||||
TemplateArgument> {
|
||||
friend class ASTStmtReader;
|
||||
friend TrailingObjects;
|
||||
|
||||
// \brief The optional nested name specifier used when naming the concept.
|
||||
NestedNameSpecifierLoc NestedNameSpec;
|
||||
|
||||
/// \brief The location of the template keyword, if specified when naming the
|
||||
/// concept.
|
||||
SourceLocation TemplateKWLoc;
|
||||
|
||||
/// \brief The location of the concept name in the expression.
|
||||
SourceLocation ConceptNameLoc;
|
||||
|
||||
/// \brief The declaration found by name lookup when the expression was
|
||||
/// created.
|
||||
/// Can differ from NamedConcept when, for example, the concept was found
|
||||
/// 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 template argument list source info used to specialize the
|
||||
/// concept.
|
||||
const ASTTemplateArgumentListInfo *ArgsAsWritten = nullptr;
|
||||
|
||||
/// \brief The number of template arguments in the tail-allocated list of
|
||||
/// converted template arguments.
|
||||
unsigned NumTemplateArgs;
|
||||
|
||||
ConceptSpecializationExpr(ASTContext &C, NestedNameSpecifierLoc NNS,
|
||||
SourceLocation TemplateKWLoc,
|
||||
SourceLocation ConceptNameLoc, NamedDecl *FoundDecl,
|
||||
ConceptDecl *NamedConcept,
|
||||
const ASTTemplateArgumentListInfo *ArgsAsWritten,
|
||||
ArrayRef<TemplateArgument> ConvertedArgs,
|
||||
Optional<bool> IsSatisfied);
|
||||
|
||||
ConceptSpecializationExpr(EmptyShell Empty, unsigned NumTemplateArgs);
|
||||
|
||||
public:
|
||||
|
||||
static ConceptSpecializationExpr *
|
||||
Create(ASTContext &C, NestedNameSpecifierLoc NNS,
|
||||
SourceLocation TemplateKWLoc, SourceLocation ConceptNameLoc,
|
||||
NamedDecl *FoundDecl, ConceptDecl *NamedConcept,
|
||||
const ASTTemplateArgumentListInfo *ArgsAsWritten,
|
||||
ArrayRef<TemplateArgument> ConvertedArgs, Optional<bool> IsSatisfied);
|
||||
|
||||
static ConceptSpecializationExpr *
|
||||
Create(ASTContext &C, EmptyShell Empty, unsigned NumTemplateArgs);
|
||||
|
||||
const NestedNameSpecifierLoc &getNestedNameSpecifierLoc() const {
|
||||
return NestedNameSpec;
|
||||
}
|
||||
|
||||
NamedDecl *getFoundDecl() const {
|
||||
return FoundDecl;
|
||||
}
|
||||
|
||||
ConceptDecl *getNamedConcept() const {
|
||||
return NamedConcept.getPointer();
|
||||
}
|
||||
|
||||
ArrayRef<TemplateArgument> getTemplateArguments() const {
|
||||
return ArrayRef<TemplateArgument>(getTrailingObjects<TemplateArgument>(),
|
||||
NumTemplateArgs);
|
||||
}
|
||||
|
||||
const ASTTemplateArgumentListInfo *getTemplateArgsAsWritten() const {
|
||||
return ArgsAsWritten;
|
||||
}
|
||||
|
||||
/// \brief Set new template arguments for this concept specialization.
|
||||
void setTemplateArguments(const ASTTemplateArgumentListInfo *ArgsAsWritten,
|
||||
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!
|
||||
bool isSatisfied() const {
|
||||
assert(!isValueDependent()
|
||||
&& "isSatisfied called on a dependent ConceptSpecializationExpr");
|
||||
return NamedConcept.getInt();
|
||||
}
|
||||
|
||||
SourceLocation getConceptNameLoc() const { return ConceptNameLoc; }
|
||||
|
||||
SourceLocation getTemplateKWLoc() const { return TemplateKWLoc; }
|
||||
|
||||
static bool classof(const Stmt *T) {
|
||||
return T->getStmtClass() == ConceptSpecializationExprClass;
|
||||
}
|
||||
|
||||
SourceLocation getBeginLoc() const LLVM_READONLY { return ConceptNameLoc; }
|
||||
SourceLocation getEndLoc() const LLVM_READONLY {
|
||||
return ArgsAsWritten->RAngleLoc;
|
||||
}
|
||||
|
||||
// Iterators
|
||||
child_range children() {
|
||||
return child_range(child_iterator(), child_iterator());
|
||||
}
|
||||
const_child_range children() const {
|
||||
return const_child_range(const_child_iterator(), const_child_iterator());
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace clang
|
||||
|
||||
#endif // LLVM_CLANG_AST_EXPRCXX_H
|
||||
|
@ -2659,6 +2659,12 @@ DEF_TRAVERSE_STMT(CoyieldExpr, {
|
||||
}
|
||||
})
|
||||
|
||||
DEF_TRAVERSE_STMT(ConceptSpecializationExpr, {
|
||||
TRY_TO(TraverseTemplateArgumentLocsHelper(
|
||||
S->getTemplateArgsAsWritten()->getTemplateArgs(),
|
||||
S->getTemplateArgsAsWritten()->NumTemplateArgs));
|
||||
})
|
||||
|
||||
// These literals (all of them) do not need any action.
|
||||
DEF_TRAVERSE_STMT(IntegerLiteral, {})
|
||||
DEF_TRAVERSE_STMT(FixedPointLiteral, {})
|
||||
|
@ -2526,8 +2526,6 @@ def note_private_extern : Note<
|
||||
"use __attribute__((visibility(\"hidden\"))) attribute instead">;
|
||||
|
||||
// C++ Concepts
|
||||
def err_concept_initialized_with_non_bool_type : Error<
|
||||
"constraint expression must be of type 'bool' but is of type %0">;
|
||||
def err_concept_decls_may_only_appear_in_global_namespace_scope : Error<
|
||||
"concept declarations may only appear in global or namespace scope">;
|
||||
def err_concept_no_parameters : Error<
|
||||
@ -2539,6 +2537,11 @@ def err_concept_no_associated_constraints : Error<
|
||||
"concept cannot have associated constraints">;
|
||||
def err_concept_not_implemented : Error<
|
||||
"sorry, unimplemented concepts feature %0 used">;
|
||||
def err_non_constant_constraint_expression : Error<
|
||||
"substitution into constraint expression resulted in a non-constant "
|
||||
"expression">;
|
||||
def err_non_bool_atomic_constraint : Error<
|
||||
"atomic constraint must be of type 'bool' (found %0)">;
|
||||
|
||||
def err_template_different_associated_constraints : Error<
|
||||
"associated constraints differ in template redeclaration">;
|
||||
@ -4496,6 +4499,10 @@ def note_prior_template_arg_substitution : Note<
|
||||
" template parameter%1 %2">;
|
||||
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_constraint_substitution_here : Note<
|
||||
"while substituting template arguments into constraint expression here">;
|
||||
def note_instantiation_contexts_suppressed : Note<
|
||||
"(skipping %0 context%s0 in backtrace; use -ftemplate-backtrace-limit=0 to "
|
||||
"see all)">;
|
||||
|
@ -163,6 +163,9 @@ def CoawaitExpr : DStmt<CoroutineSuspendExpr>;
|
||||
def DependentCoawaitExpr : DStmt<Expr>;
|
||||
def CoyieldExpr : DStmt<CoroutineSuspendExpr>;
|
||||
|
||||
// C++2a Concepts expressions
|
||||
def ConceptSpecializationExpr : DStmt<Expr>;
|
||||
|
||||
// Obj-C Expressions.
|
||||
def ObjCStringLiteral : DStmt<Expr>;
|
||||
def ObjCBoxedExpr : DStmt<Expr>;
|
||||
|
@ -6047,7 +6047,16 @@ public:
|
||||
CXXConversionDecl *Conv,
|
||||
Expr *Src);
|
||||
|
||||
// ParseObjCStringLiteral - Parse Objective-C string literals.
|
||||
/// Check whether the given expression is a valid constraint expression.
|
||||
/// 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);
|
||||
|
||||
// ParseObjCStringLiteral - Parse Objective-C string literals.
|
||||
ExprResult ParseObjCStringLiteral(SourceLocation *AtLocs,
|
||||
ArrayRef<Expr *> Strings);
|
||||
|
||||
@ -6718,9 +6727,9 @@ public:
|
||||
|
||||
ExprResult
|
||||
CheckConceptTemplateId(const CXXScopeSpec &SS,
|
||||
const DeclarationNameInfo &NameInfo,
|
||||
ConceptDecl *Template,
|
||||
SourceLocation TemplateLoc,
|
||||
SourceLocation TemplateKWLoc,
|
||||
SourceLocation ConceptNameLoc, NamedDecl *FoundDecl,
|
||||
ConceptDecl *NamedConcept,
|
||||
const TemplateArgumentListInfo *TemplateArgs);
|
||||
|
||||
void diagnoseMissingTemplateArguments(TemplateName Name, SourceLocation Loc);
|
||||
@ -7639,6 +7648,15 @@ public:
|
||||
/// member).
|
||||
DefiningSynthesizedFunction,
|
||||
|
||||
// We are checking the constraints associated with a constrained entity or
|
||||
// the constraint expression of a concept. This includes the checks that
|
||||
// atomic constraints have the type 'bool' and that they can be constant
|
||||
// evaluated.
|
||||
ConstraintsCheck,
|
||||
|
||||
// We are substituting template arguments into a constraint expression.
|
||||
ConstraintSubstitution,
|
||||
|
||||
/// Added for Template instantiation observation.
|
||||
/// Memoization means we are _not_ instantiating a template because
|
||||
/// it is already instantiated (but we entered a context where we
|
||||
@ -7899,6 +7917,23 @@ public:
|
||||
ArrayRef<TemplateArgument> TemplateArgs,
|
||||
SourceRange InstantiationRange);
|
||||
|
||||
struct ConstraintsCheck {};
|
||||
/// \brief Note that we are checking the constraints associated with some
|
||||
/// constrained entity (a concept declaration or a template with associated
|
||||
/// constraints).
|
||||
InstantiatingTemplate(Sema &SemaRef, SourceLocation PointOfInstantiation,
|
||||
ConstraintsCheck, TemplateDecl *Template,
|
||||
ArrayRef<TemplateArgument> TemplateArgs,
|
||||
SourceRange InstantiationRange);
|
||||
|
||||
struct ConstraintSubstitution {};
|
||||
/// \brief Note that we are checking a constraint expression associated
|
||||
/// with a template declaration or as part of the satisfaction check of a
|
||||
/// concept.
|
||||
InstantiatingTemplate(Sema &SemaRef, SourceLocation PointOfInstantiation,
|
||||
ConstraintSubstitution, TemplateDecl *Template,
|
||||
sema::TemplateDeductionInfo &DeductionInfo,
|
||||
SourceRange InstantiationRange);
|
||||
|
||||
/// Note that we have finished instantiating this template.
|
||||
void Clear();
|
||||
|
@ -1915,6 +1915,7 @@ namespace serialization {
|
||||
EXPR_FUNCTION_PARM_PACK, // FunctionParmPackExpr
|
||||
EXPR_MATERIALIZE_TEMPORARY, // MaterializeTemporaryExpr
|
||||
EXPR_CXX_FOLD, // CXXFoldExpr
|
||||
EXPR_CONCEPT_SPECIALIZATION,// ConceptSpecializationExpr
|
||||
|
||||
// CUDA
|
||||
EXPR_CUDA_KERNEL_CALL, // CUDAKernelCallExpr
|
||||
|
@ -3403,6 +3403,7 @@ bool Expr::HasSideEffects(const ASTContext &Ctx,
|
||||
case CXXUuidofExprClass:
|
||||
case OpaqueValueExprClass:
|
||||
case SourceLocExprClass:
|
||||
case ConceptSpecializationExprClass:
|
||||
// These never have a side-effect.
|
||||
return false;
|
||||
|
||||
|
@ -28,6 +28,9 @@
|
||||
#include "clang/Basic/OperatorKinds.h"
|
||||
#include "clang/Basic/SourceLocation.h"
|
||||
#include "clang/Basic/Specifiers.h"
|
||||
#include "clang/Sema/Template.h"
|
||||
#include "clang/Sema/SemaDiagnostic.h"
|
||||
#include "clang/Sema/Sema.h"
|
||||
#include "llvm/ADT/ArrayRef.h"
|
||||
#include "llvm/Support/Casting.h"
|
||||
#include "llvm/Support/ErrorHandling.h"
|
||||
@ -1680,3 +1683,82 @@ CUDAKernelCallExpr *CUDAKernelCallExpr::CreateEmpty(const ASTContext &Ctx,
|
||||
alignof(CUDAKernelCallExpr));
|
||||
return new (Mem) CUDAKernelCallExpr(NumArgs, Empty);
|
||||
}
|
||||
|
||||
ConceptSpecializationExpr::ConceptSpecializationExpr(ASTContext &C,
|
||||
NestedNameSpecifierLoc NNS, SourceLocation TemplateKWLoc,
|
||||
SourceLocation ConceptNameLoc, NamedDecl *FoundDecl,
|
||||
ConceptDecl *NamedConcept, const ASTTemplateArgumentListInfo *ArgsAsWritten,
|
||||
ArrayRef<TemplateArgument> ConvertedArgs, Optional<bool> IsSatisfied)
|
||||
: Expr(ConceptSpecializationExprClass, C.BoolTy, VK_RValue, OK_Ordinary,
|
||||
/*TypeDependent=*/false,
|
||||
// All the flags below are set in setTemplateArguments.
|
||||
/*ValueDependent=*/!IsSatisfied.hasValue(),
|
||||
/*InstantiationDependent=*/false,
|
||||
/*ContainsUnexpandedParameterPacks=*/false),
|
||||
NestedNameSpec(NNS), TemplateKWLoc(TemplateKWLoc),
|
||||
ConceptNameLoc(ConceptNameLoc), FoundDecl(FoundDecl),
|
||||
NamedConcept(NamedConcept, IsSatisfied ? *IsSatisfied : true),
|
||||
NumTemplateArgs(ConvertedArgs.size()) {
|
||||
|
||||
setTemplateArguments(ArgsAsWritten, ConvertedArgs);
|
||||
}
|
||||
|
||||
ConceptSpecializationExpr::ConceptSpecializationExpr(EmptyShell Empty,
|
||||
unsigned NumTemplateArgs)
|
||||
: Expr(ConceptSpecializationExprClass, Empty),
|
||||
NumTemplateArgs(NumTemplateArgs) { }
|
||||
|
||||
void ConceptSpecializationExpr::setTemplateArguments(
|
||||
const ASTTemplateArgumentListInfo *ArgsAsWritten,
|
||||
ArrayRef<TemplateArgument> Converted) {
|
||||
assert(Converted.size() == NumTemplateArgs);
|
||||
assert(!this->ArgsAsWritten && "setTemplateArguments can only be used once");
|
||||
this->ArgsAsWritten = ArgsAsWritten;
|
||||
std::uninitialized_copy(Converted.begin(), Converted.end(),
|
||||
getTrailingObjects<TemplateArgument>());
|
||||
bool IsInstantiationDependent = false;
|
||||
bool ContainsUnexpandedParameterPack = false;
|
||||
for (const TemplateArgumentLoc& LocInfo : ArgsAsWritten->arguments()) {
|
||||
if (LocInfo.getArgument().isInstantiationDependent())
|
||||
IsInstantiationDependent = true;
|
||||
if (LocInfo.getArgument().containsUnexpandedParameterPack())
|
||||
ContainsUnexpandedParameterPack = true;
|
||||
if (ContainsUnexpandedParameterPack && IsInstantiationDependent)
|
||||
break;
|
||||
}
|
||||
|
||||
// Currently guaranteed by the fact concepts can only be at namespace-scope.
|
||||
assert(!NestedNameSpec ||
|
||||
(!NestedNameSpec.getNestedNameSpecifier()->isInstantiationDependent() &&
|
||||
!NestedNameSpec.getNestedNameSpecifier()
|
||||
->containsUnexpandedParameterPack()));
|
||||
setInstantiationDependent(IsInstantiationDependent);
|
||||
setContainsUnexpandedParameterPack(ContainsUnexpandedParameterPack);
|
||||
assert((!isValueDependent() || isInstantiationDependent()) &&
|
||||
"should not be value-dependent");
|
||||
}
|
||||
|
||||
ConceptSpecializationExpr *
|
||||
ConceptSpecializationExpr::Create(ASTContext &C, NestedNameSpecifierLoc NNS,
|
||||
SourceLocation TemplateKWLoc,
|
||||
SourceLocation ConceptNameLoc,
|
||||
NamedDecl *FoundDecl,
|
||||
ConceptDecl *NamedConcept,
|
||||
const ASTTemplateArgumentListInfo *ArgsAsWritten,
|
||||
ArrayRef<TemplateArgument> ConvertedArgs,
|
||||
Optional<bool> IsSatisfied) {
|
||||
void *Buffer = C.Allocate(totalSizeToAlloc<TemplateArgument>(
|
||||
ConvertedArgs.size()));
|
||||
return new (Buffer) ConceptSpecializationExpr(C, NNS, TemplateKWLoc,
|
||||
ConceptNameLoc, FoundDecl,
|
||||
NamedConcept, ArgsAsWritten,
|
||||
ConvertedArgs, IsSatisfied);
|
||||
}
|
||||
|
||||
ConceptSpecializationExpr *
|
||||
ConceptSpecializationExpr::Create(ASTContext &C, EmptyShell Empty,
|
||||
unsigned NumTemplateArgs) {
|
||||
void *Buffer = C.Allocate(totalSizeToAlloc<TemplateArgument>(
|
||||
NumTemplateArgs));
|
||||
return new (Buffer) ConceptSpecializationExpr(Empty, NumTemplateArgs);
|
||||
}
|
@ -192,6 +192,7 @@ static Cl::Kinds ClassifyInternal(ASTContext &Ctx, const Expr *E) {
|
||||
case Expr::NoInitExprClass:
|
||||
case Expr::DesignatedInitUpdateExprClass:
|
||||
case Expr::SourceLocExprClass:
|
||||
case Expr::ConceptSpecializationExprClass:
|
||||
return Cl::CL_PRValue;
|
||||
|
||||
case Expr::ConstantExprClass:
|
||||
|
@ -9768,6 +9768,7 @@ public:
|
||||
bool VisitCXXNoexceptExpr(const CXXNoexceptExpr *E);
|
||||
bool VisitSizeOfPackExpr(const SizeOfPackExpr *E);
|
||||
bool VisitSourceLocExpr(const SourceLocExpr *E);
|
||||
bool VisitConceptSpecializationExpr(const ConceptSpecializationExpr *E);
|
||||
// FIXME: Missing: array subscript of vector, member of vector
|
||||
};
|
||||
|
||||
@ -12250,6 +12251,12 @@ bool IntExprEvaluator::VisitCXXNoexceptExpr(const CXXNoexceptExpr *E) {
|
||||
return Success(E->getValue(), E);
|
||||
}
|
||||
|
||||
bool IntExprEvaluator::VisitConceptSpecializationExpr(
|
||||
const ConceptSpecializationExpr *E) {
|
||||
return Success(E->isSatisfied(), E);
|
||||
}
|
||||
|
||||
|
||||
bool FixedPointExprEvaluator::VisitUnaryOperator(const UnaryOperator *E) {
|
||||
switch (E->getOpcode()) {
|
||||
default:
|
||||
@ -13923,6 +13930,7 @@ static ICEDiag CheckICE(const Expr* E, const ASTContext &Ctx) {
|
||||
case Expr::CXXBoolLiteralExprClass:
|
||||
case Expr::CXXScalarValueInitExprClass:
|
||||
case Expr::TypeTraitExprClass:
|
||||
case Expr::ConceptSpecializationExprClass:
|
||||
case Expr::ArrayTypeTraitExprClass:
|
||||
case Expr::ExpressionTraitExprClass:
|
||||
case Expr::CXXNoexceptExprClass:
|
||||
|
@ -3658,6 +3658,7 @@ recurse:
|
||||
case Expr::ConvertVectorExprClass:
|
||||
case Expr::StmtExprClass:
|
||||
case Expr::TypeTraitExprClass:
|
||||
case Expr::ConceptSpecializationExprClass:
|
||||
case Expr::ArrayTypeTraitExprClass:
|
||||
case Expr::ExpressionTraitExprClass:
|
||||
case Expr::VAArgExprClass:
|
||||
|
@ -2231,6 +2231,17 @@ void StmtPrinter::VisitCXXFoldExpr(CXXFoldExpr *E) {
|
||||
OS << ")";
|
||||
}
|
||||
|
||||
void StmtPrinter::VisitConceptSpecializationExpr(ConceptSpecializationExpr *E) {
|
||||
NestedNameSpecifierLoc NNS = E->getNestedNameSpecifierLoc();
|
||||
if (NNS)
|
||||
NNS.getNestedNameSpecifier()->print(OS, Policy);
|
||||
if (E->getTemplateKWLoc().isValid())
|
||||
OS << "template ";
|
||||
OS << E->getFoundDecl()->getName();
|
||||
printTemplateArgumentList(OS, E->getTemplateArgsAsWritten()->arguments(),
|
||||
Policy);
|
||||
}
|
||||
|
||||
// C++ Coroutines TS
|
||||
|
||||
void StmtPrinter::VisitCoroutineBodyStmt(CoroutineBodyStmt *S) {
|
||||
|
@ -1309,6 +1309,14 @@ void StmtProfiler::VisitAtomicExpr(const AtomicExpr *S) {
|
||||
ID.AddInteger(S->getOp());
|
||||
}
|
||||
|
||||
void StmtProfiler::VisitConceptSpecializationExpr(
|
||||
const ConceptSpecializationExpr *S) {
|
||||
VisitExpr(S);
|
||||
VisitDecl(S->getFoundDecl());
|
||||
VisitTemplateArguments(S->getTemplateArgsAsWritten()->getTemplateArgs(),
|
||||
S->getTemplateArgsAsWritten()->NumTemplateArgs);
|
||||
}
|
||||
|
||||
static Stmt::StmtClass DecodeOperatorCall(const CXXOperatorCallExpr *S,
|
||||
UnaryOperatorKind &UnaryOp,
|
||||
BinaryOperatorKind &BinaryOp) {
|
||||
|
@ -673,6 +673,10 @@ public:
|
||||
return llvm::ConstantInt::get(ConvertType(E->getType()), E->getValue());
|
||||
}
|
||||
|
||||
Value *VisitConceptSpecializationExpr(const ConceptSpecializationExpr *E) {
|
||||
return Builder.getInt1(E->isSatisfied());
|
||||
}
|
||||
|
||||
Value *VisitArrayTypeTraitExpr(const ArrayTypeTraitExpr *E) {
|
||||
return llvm::ConstantInt::get(Builder.getInt32Ty(), E->getValue());
|
||||
}
|
||||
|
@ -417,6 +417,10 @@ private:
|
||||
return "DefiningSynthesizedFunction";
|
||||
case CodeSynthesisContext::Memoization:
|
||||
return "Memoization";
|
||||
case CodeSynthesisContext::ConstraintsCheck:
|
||||
return "ConstraintsCheck";
|
||||
case CodeSynthesisContext::ConstraintSubstitution:
|
||||
return "ConstraintSubstitution";
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
@ -228,18 +228,16 @@ ExprResult Parser::ParseCaseExpression(SourceLocation CaseLoc) {
|
||||
/// Parse a constraint-expression.
|
||||
///
|
||||
/// \verbatim
|
||||
/// constraint-expression: [Concepts TS temp.constr.decl p1]
|
||||
/// constraint-expression: C++2a[temp.constr.decl]p1
|
||||
/// logical-or-expression
|
||||
/// \endverbatim
|
||||
ExprResult Parser::ParseConstraintExpression() {
|
||||
// FIXME: this may erroneously consume a function-body as the braced
|
||||
// initializer list of a compound literal
|
||||
//
|
||||
// FIXME: this may erroneously consume a parenthesized rvalue reference
|
||||
// declarator as a parenthesized address-of-label expression
|
||||
EnterExpressionEvaluationContext ConstantEvaluated(
|
||||
Actions, Sema::ExpressionEvaluationContext::ConstantEvaluated);
|
||||
ExprResult LHS(ParseCastExpression(/*isUnaryExpression=*/false));
|
||||
ExprResult Res(ParseRHSOfBinaryExpression(LHS, prec::LogicalOr));
|
||||
|
||||
if (Res.isUsable() && !Actions.CheckConstraintExpression(Res.get()))
|
||||
return ExprError();
|
||||
return Res;
|
||||
}
|
||||
|
||||
|
@ -32,6 +32,7 @@ add_clang_library(clangSema
|
||||
SemaCast.cpp
|
||||
SemaChecking.cpp
|
||||
SemaCodeComplete.cpp
|
||||
SemaConcept.cpp
|
||||
SemaConsumer.cpp
|
||||
SemaCoroutine.cpp
|
||||
SemaCUDA.cpp
|
||||
|
125
clang/lib/Sema/SemaConcept.cpp
Normal file
125
clang/lib/Sema/SemaConcept.cpp
Normal file
@ -0,0 +1,125 @@
|
||||
//===-- SemaConcept.cpp - Semantic Analysis for Constraints and Concepts --===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This file implements semantic analysis for C++ constraints and concepts.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "clang/Sema/Sema.h"
|
||||
#include "clang/Sema/SemaDiagnostic.h"
|
||||
#include "clang/Sema/TemplateDeduction.h"
|
||||
#include "clang/Sema/Template.h"
|
||||
#include "clang/AST/ExprCXX.h"
|
||||
using namespace clang;
|
||||
using namespace sema;
|
||||
|
||||
bool Sema::CheckConstraintExpression(Expr *ConstraintExpression) {
|
||||
// C++2a [temp.constr.atomic]p1
|
||||
// ..E shall be a constant expression of type bool.
|
||||
|
||||
ConstraintExpression = ConstraintExpression->IgnoreParenImpCasts();
|
||||
|
||||
if (auto *BinOp = dyn_cast<BinaryOperator>(ConstraintExpression)) {
|
||||
if (BinOp->getOpcode() == BO_LAnd || BinOp->getOpcode() == BO_LOr)
|
||||
return CheckConstraintExpression(BinOp->getLHS()) &&
|
||||
CheckConstraintExpression(BinOp->getRHS());
|
||||
} else if (auto *C = dyn_cast<ExprWithCleanups>(ConstraintExpression))
|
||||
return CheckConstraintExpression(C->getSubExpr());
|
||||
|
||||
// An atomic constraint!
|
||||
if (ConstraintExpression->isTypeDependent())
|
||||
return true;
|
||||
|
||||
QualType Type = ConstraintExpression->getType();
|
||||
if (!Context.hasSameUnqualifiedType(Type, Context.BoolTy)) {
|
||||
Diag(ConstraintExpression->getExprLoc(),
|
||||
diag::err_non_bool_atomic_constraint) << Type
|
||||
<< ConstraintExpression->getSourceRange();
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
Sema::CalculateConstraintSatisfaction(ConceptDecl *NamedConcept,
|
||||
MultiLevelTemplateArgumentList &MLTAL,
|
||||
Expr *ConstraintExpr,
|
||||
bool &IsSatisfied) {
|
||||
ConstraintExpr = ConstraintExpr->IgnoreParenImpCasts();
|
||||
|
||||
if (auto *BO = dyn_cast<BinaryOperator>(ConstraintExpr)) {
|
||||
if (BO->getOpcode() == BO_LAnd) {
|
||||
if (CalculateConstraintSatisfaction(NamedConcept, MLTAL, BO->getLHS(),
|
||||
IsSatisfied))
|
||||
return true;
|
||||
if (!IsSatisfied)
|
||||
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)
|
||||
return false;
|
||||
return CalculateConstraintSatisfaction(NamedConcept, MLTAL, BO->getRHS(),
|
||||
IsSatisfied);
|
||||
}
|
||||
}
|
||||
else if (auto *C = dyn_cast<ExprWithCleanups>(ConstraintExpr))
|
||||
return CalculateConstraintSatisfaction(NamedConcept, MLTAL, C->getSubExpr(),
|
||||
IsSatisfied);
|
||||
|
||||
EnterExpressionEvaluationContext ConstantEvaluated(
|
||||
*this, Sema::ExpressionEvaluationContext::ConstantEvaluated);
|
||||
|
||||
// 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);
|
||||
|
||||
E = SubstExpr(ConstraintExpr, MLTAL);
|
||||
if (E.isInvalid() || Trap.hasErrorOccurred()) {
|
||||
// C++2a [temp.constr.atomic]p1
|
||||
// ...If substitution results in an invalid type or expression, the
|
||||
// constraint is not satisfied.
|
||||
IsSatisfied = false;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!CheckConstraintExpression(E.get()))
|
||||
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);
|
||||
return true;
|
||||
}
|
||||
|
||||
IsSatisfied = EvalResult.Val.getInt().getBoolValue();
|
||||
|
||||
return false;
|
||||
}
|
@ -1314,6 +1314,7 @@ CanThrowResult Sema::canThrow(const Expr *E) {
|
||||
case Expr::SizeOfPackExprClass:
|
||||
case Expr::StringLiteralClass:
|
||||
case Expr::SourceLocExprClass:
|
||||
case Expr::ConceptSpecializationExprClass:
|
||||
// These expressions can never throw.
|
||||
return CT_Cannot;
|
||||
|
||||
|
@ -4271,14 +4271,47 @@ void Sema::diagnoseMissingTemplateArguments(TemplateName Name,
|
||||
|
||||
ExprResult
|
||||
Sema::CheckConceptTemplateId(const CXXScopeSpec &SS,
|
||||
const DeclarationNameInfo &NameInfo,
|
||||
ConceptDecl *Template,
|
||||
SourceLocation TemplateLoc,
|
||||
SourceLocation TemplateKWLoc,
|
||||
SourceLocation ConceptNameLoc,
|
||||
NamedDecl *FoundDecl,
|
||||
ConceptDecl *NamedConcept,
|
||||
const TemplateArgumentListInfo *TemplateArgs) {
|
||||
// TODO: Do concept specialization here.
|
||||
Diag(NameInfo.getBeginLoc(), diag::err_concept_not_implemented) <<
|
||||
"concept specialization";
|
||||
return ExprError();
|
||||
assert(NamedConcept && "A concept template id without a template?");
|
||||
|
||||
llvm::SmallVector<TemplateArgument, 4> Converted;
|
||||
if (CheckTemplateArgumentList(NamedConcept, ConceptNameLoc,
|
||||
const_cast<TemplateArgumentListInfo&>(*TemplateArgs),
|
||||
/*PartialTemplateArgs=*/false, Converted,
|
||||
/*UpdateArgsWithConversion=*/false))
|
||||
return ExprError();
|
||||
|
||||
Optional<bool> IsSatisfied;
|
||||
bool AreArgsDependent = false;
|
||||
for (TemplateArgument &Arg : Converted) {
|
||||
if (Arg.isDependent()) {
|
||||
AreArgsDependent = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!AreArgsDependent) {
|
||||
InstantiatingTemplate Inst(*this, ConceptNameLoc,
|
||||
InstantiatingTemplate::ConstraintsCheck{}, NamedConcept, Converted,
|
||||
SourceRange(SS.isSet() ? SS.getBeginLoc() : ConceptNameLoc,
|
||||
TemplateArgs->getRAngleLoc()));
|
||||
MultiLevelTemplateArgumentList MLTAL;
|
||||
MLTAL.addOuterTemplateArguments(Converted);
|
||||
bool Satisfied;
|
||||
if (CalculateConstraintSatisfaction(NamedConcept, MLTAL,
|
||||
NamedConcept->getConstraintExpr(),
|
||||
Satisfied))
|
||||
return ExprError();
|
||||
IsSatisfied = Satisfied;
|
||||
}
|
||||
return ConceptSpecializationExpr::Create(Context,
|
||||
SS.isSet() ? SS.getWithLocInContext(Context) : NestedNameSpecifierLoc{},
|
||||
TemplateKWLoc, ConceptNameLoc, FoundDecl, NamedConcept,
|
||||
ASTTemplateArgumentListInfo::Create(Context, *TemplateArgs), Converted,
|
||||
IsSatisfied);
|
||||
}
|
||||
|
||||
ExprResult Sema::BuildTemplateIdExpr(const CXXScopeSpec &SS,
|
||||
@ -4322,9 +4355,10 @@ ExprResult Sema::BuildTemplateIdExpr(const CXXScopeSpec &SS,
|
||||
}
|
||||
|
||||
if (R.getAsSingle<ConceptDecl>() && !AnyDependentArguments()) {
|
||||
return CheckConceptTemplateId(SS, R.getLookupNameInfo(),
|
||||
R.getAsSingle<ConceptDecl>(),
|
||||
TemplateKWLoc, TemplateArgs);
|
||||
return CheckConceptTemplateId(SS, TemplateKWLoc,
|
||||
R.getLookupNameInfo().getBeginLoc(),
|
||||
R.getFoundDecl(),
|
||||
R.getAsSingle<ConceptDecl>(), TemplateArgs);
|
||||
}
|
||||
|
||||
// We don't want lookup warnings at this point.
|
||||
@ -8054,20 +8088,7 @@ Decl *Sema::ActOnConceptDefinition(Scope *S,
|
||||
ConceptDecl *NewDecl = ConceptDecl::Create(Context, DC, NameLoc, Name,
|
||||
TemplateParameterLists.front(),
|
||||
ConstraintExpr);
|
||||
|
||||
if (!ConstraintExpr->isTypeDependent() &&
|
||||
ConstraintExpr->getType() != Context.BoolTy) {
|
||||
// C++2a [temp.constr.atomic]p3:
|
||||
// E shall be a constant expression of type bool.
|
||||
// TODO: Do this check for individual atomic constraints
|
||||
// and not the constraint expression. Probably should do it in
|
||||
// ParseConstraintExpression.
|
||||
Diag(ConstraintExpr->getSourceRange().getBegin(),
|
||||
diag::err_concept_initialized_with_non_bool_type)
|
||||
<< ConstraintExpr->getType();
|
||||
NewDecl->setInvalidDecl();
|
||||
}
|
||||
|
||||
|
||||
if (NewDecl->getAssociatedConstraints()) {
|
||||
// C++2a [temp.concept]p4:
|
||||
// A concept shall not have associated constraints.
|
||||
|
@ -198,12 +198,14 @@ bool Sema::CodeSynthesisContext::isInstantiationRecord() const {
|
||||
case ExplicitTemplateArgumentSubstitution:
|
||||
case DeducedTemplateArgumentSubstitution:
|
||||
case PriorTemplateArgumentSubstitution:
|
||||
case ConstraintsCheck:
|
||||
return true;
|
||||
|
||||
case DefaultTemplateArgumentChecking:
|
||||
case DeclaringSpecialMember:
|
||||
case DefiningSynthesizedFunction:
|
||||
case ExceptionSpecEvaluation:
|
||||
case ConstraintSubstitution:
|
||||
return false;
|
||||
|
||||
// This function should never be called when Kind's value is Memoization.
|
||||
@ -358,6 +360,24 @@ Sema::InstantiatingTemplate::InstantiatingTemplate(
|
||||
PointOfInstantiation, InstantiationRange, Param, Template,
|
||||
TemplateArgs) {}
|
||||
|
||||
Sema::InstantiatingTemplate::InstantiatingTemplate(
|
||||
Sema &SemaRef, SourceLocation PointOfInstantiation,
|
||||
ConstraintsCheck, TemplateDecl *Template,
|
||||
ArrayRef<TemplateArgument> TemplateArgs, SourceRange InstantiationRange)
|
||||
: InstantiatingTemplate(
|
||||
SemaRef, CodeSynthesisContext::ConstraintsCheck,
|
||||
PointOfInstantiation, InstantiationRange, Template, nullptr,
|
||||
TemplateArgs) {}
|
||||
|
||||
Sema::InstantiatingTemplate::InstantiatingTemplate(
|
||||
Sema &SemaRef, SourceLocation PointOfInstantiation,
|
||||
ConstraintSubstitution, TemplateDecl *Template,
|
||||
sema::TemplateDeductionInfo &DeductionInfo, SourceRange InstantiationRange)
|
||||
: InstantiatingTemplate(
|
||||
SemaRef, CodeSynthesisContext::ConstraintSubstitution,
|
||||
PointOfInstantiation, InstantiationRange, Template, nullptr,
|
||||
{}, &DeductionInfo) {}
|
||||
|
||||
void Sema::pushCodeSynthesisContext(CodeSynthesisContext Ctx) {
|
||||
Ctx.SavedInNonInstantiationSFINAEContext = InNonInstantiationSFINAEContext;
|
||||
InNonInstantiationSFINAEContext = false;
|
||||
@ -664,6 +684,30 @@ void Sema::PrintInstantiationStack() {
|
||||
|
||||
case CodeSynthesisContext::Memoization:
|
||||
break;
|
||||
|
||||
case CodeSynthesisContext::ConstraintsCheck:
|
||||
if (auto *CD = dyn_cast<ConceptDecl>(Active->Entity)) {
|
||||
SmallVector<char, 128> TemplateArgsStr;
|
||||
llvm::raw_svector_ostream OS(TemplateArgsStr);
|
||||
CD->printName(OS);
|
||||
printTemplateArgumentList(OS, Active->template_arguments(),
|
||||
getPrintingPolicy());
|
||||
Diags.Report(Active->PointOfInstantiation,
|
||||
diag::note_concept_specialization_here)
|
||||
<< OS.str()
|
||||
<< Active->InstantiationRange;
|
||||
break;
|
||||
}
|
||||
// TODO: Concepts - implement this for constrained templates and partial
|
||||
// specializations.
|
||||
llvm_unreachable("only concept constraints are supported right now");
|
||||
break;
|
||||
|
||||
case CodeSynthesisContext::ConstraintSubstitution:
|
||||
Diags.Report(Active->PointOfInstantiation,
|
||||
diag::note_constraint_substitution_here)
|
||||
<< Active->InstantiationRange;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -687,6 +731,7 @@ Optional<TemplateDeductionInfo *> Sema::isSFINAEContext() const {
|
||||
LLVM_FALLTHROUGH;
|
||||
case CodeSynthesisContext::DefaultFunctionArgumentInstantiation:
|
||||
case CodeSynthesisContext::ExceptionSpecInstantiation:
|
||||
case CodeSynthesisContext::ConstraintsCheck:
|
||||
// This is a template instantiation, so there is no SFINAE.
|
||||
return None;
|
||||
|
||||
@ -700,8 +745,10 @@ Optional<TemplateDeductionInfo *> Sema::isSFINAEContext() const {
|
||||
|
||||
case CodeSynthesisContext::ExplicitTemplateArgumentSubstitution:
|
||||
case CodeSynthesisContext::DeducedTemplateArgumentSubstitution:
|
||||
// We're either substitution explicitly-specified template arguments
|
||||
// or deduced template arguments, so SFINAE applies.
|
||||
case CodeSynthesisContext::ConstraintSubstitution:
|
||||
// We're either substituting explicitly-specified template arguments
|
||||
// or deduced template arguments or a constraint expression, so SFINAE
|
||||
// applies.
|
||||
assert(Active->DeductionInfo && "Missing deduction info pointer");
|
||||
return Active->DeductionInfo;
|
||||
|
||||
|
@ -3019,6 +3019,25 @@ public:
|
||||
///
|
||||
/// By default, performs semantic analysis to build the new expression.
|
||||
/// Subclasses may override this routine to provide different behavior.
|
||||
ExprResult RebuildConceptSpecializationExpr(NestedNameSpecifierLoc NNS,
|
||||
SourceLocation TemplateKWLoc, SourceLocation ConceptNameLoc,
|
||||
NamedDecl *FoundDecl, ConceptDecl *NamedConcept,
|
||||
TemplateArgumentListInfo *TALI) {
|
||||
CXXScopeSpec SS;
|
||||
SS.Adopt(NNS);
|
||||
ExprResult Result = getSema().CheckConceptTemplateId(SS, TemplateKWLoc,
|
||||
ConceptNameLoc,
|
||||
FoundDecl,
|
||||
NamedConcept, TALI);
|
||||
if (Result.isInvalid())
|
||||
return ExprError();
|
||||
return Result;
|
||||
}
|
||||
|
||||
/// \brief Build a new Objective-C boxed expression.
|
||||
///
|
||||
/// By default, performs semantic analysis to build the new expression.
|
||||
/// Subclasses may override this routine to provide different behavior.
|
||||
ExprResult RebuildObjCBoxedExpr(SourceRange SR, Expr *ValueExpr) {
|
||||
return getSema().BuildObjCBoxedExpr(SR, ValueExpr);
|
||||
}
|
||||
@ -11014,6 +11033,23 @@ TreeTransform<Derived>::TransformTypeTraitExpr(TypeTraitExpr *E) {
|
||||
E->getEndLoc());
|
||||
}
|
||||
|
||||
template<typename Derived>
|
||||
ExprResult
|
||||
TreeTransform<Derived>::TransformConceptSpecializationExpr(
|
||||
ConceptSpecializationExpr *E) {
|
||||
const ASTTemplateArgumentListInfo *Old = E->getTemplateArgsAsWritten();
|
||||
TemplateArgumentListInfo TransArgs(Old->LAngleLoc, Old->RAngleLoc);
|
||||
if (getDerived().TransformTemplateArguments(Old->getTemplateArgs(),
|
||||
Old->NumTemplateArgs, TransArgs))
|
||||
return ExprError();
|
||||
|
||||
return getDerived().RebuildConceptSpecializationExpr(
|
||||
E->getNestedNameSpecifierLoc(), E->getTemplateKWLoc(),
|
||||
E->getConceptNameLoc(), E->getFoundDecl(), E->getNamedConcept(),
|
||||
&TransArgs);
|
||||
}
|
||||
|
||||
|
||||
template<typename Derived>
|
||||
ExprResult
|
||||
TreeTransform<Derived>::TransformArrayTypeTraitExpr(ArrayTypeTraitExpr *E) {
|
||||
|
@ -734,6 +734,24 @@ void ASTStmtReader::VisitUnaryExprOrTypeTraitExpr(UnaryExprOrTypeTraitExpr *E) {
|
||||
E->setRParenLoc(ReadSourceLocation());
|
||||
}
|
||||
|
||||
void ASTStmtReader::VisitConceptSpecializationExpr(
|
||||
ConceptSpecializationExpr *E) {
|
||||
VisitExpr(E);
|
||||
unsigned NumTemplateArgs = Record.readInt();
|
||||
E->NestedNameSpec = Record.readNestedNameSpecifierLoc();
|
||||
E->TemplateKWLoc = Record.readSourceLocation();
|
||||
E->ConceptNameLoc = Record.readSourceLocation();
|
||||
E->FoundDecl = ReadDeclAs<NamedDecl>();
|
||||
E->NamedConcept.setPointer(ReadDeclAs<ConceptDecl>());
|
||||
const ASTTemplateArgumentListInfo *ArgsAsWritten =
|
||||
Record.readASTTemplateArgumentListInfo();
|
||||
llvm::SmallVector<TemplateArgument, 4> Args;
|
||||
for (unsigned I = 0; I < NumTemplateArgs; ++I)
|
||||
Args.push_back(Record.readTemplateArgument());
|
||||
E->setTemplateArguments(ArgsAsWritten, Args);
|
||||
E->NamedConcept.setInt(Record.readInt() == 1);
|
||||
}
|
||||
|
||||
void ASTStmtReader::VisitArraySubscriptExpr(ArraySubscriptExpr *E) {
|
||||
VisitExpr(E);
|
||||
E->setLHS(Record.readSubExpr());
|
||||
@ -3490,6 +3508,12 @@ Stmt *ASTReader::ReadStmtFromStream(ModuleFile &F) {
|
||||
case EXPR_DEPENDENT_COAWAIT:
|
||||
S = new (Context) DependentCoawaitExpr(Empty);
|
||||
break;
|
||||
|
||||
case EXPR_CONCEPT_SPECIALIZATION:
|
||||
unsigned numTemplateArgs = Record[ASTStmtReader::NumExprFields];
|
||||
S = ConceptSpecializationExpr::Create(Context, Empty, numTemplateArgs);
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
// We hit a STMT_STOP, so we're done with this expression.
|
||||
|
@ -388,6 +388,24 @@ void ASTStmtWriter::VisitDependentCoawaitExpr(DependentCoawaitExpr *E) {
|
||||
Code = serialization::EXPR_DEPENDENT_COAWAIT;
|
||||
}
|
||||
|
||||
void ASTStmtWriter::VisitConceptSpecializationExpr(
|
||||
ConceptSpecializationExpr *E) {
|
||||
VisitExpr(E);
|
||||
ArrayRef<TemplateArgument> TemplateArgs = E->getTemplateArguments();
|
||||
Record.push_back(TemplateArgs.size());
|
||||
Record.AddNestedNameSpecifierLoc(E->getNestedNameSpecifierLoc());
|
||||
Record.AddSourceLocation(E->getTemplateKWLoc());
|
||||
Record.AddSourceLocation(E->getConceptNameLoc());
|
||||
Record.AddDeclRef(E->getFoundDecl());
|
||||
Record.AddDeclRef(E->getNamedConcept());
|
||||
Record.AddASTTemplateArgumentListInfo(E->getTemplateArgsAsWritten());
|
||||
for (const TemplateArgument &Arg : TemplateArgs)
|
||||
Record.AddTemplateArgument(Arg);
|
||||
Record.push_back(E->isSatisfied());
|
||||
Code = serialization::EXPR_CONCEPT_SPECIALIZATION;
|
||||
}
|
||||
|
||||
|
||||
void ASTStmtWriter::VisitCapturedStmt(CapturedStmt *S) {
|
||||
VisitStmt(S);
|
||||
// NumCaptures
|
||||
|
@ -1370,6 +1370,7 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred,
|
||||
case Stmt::CUDAKernelCallExprClass:
|
||||
case Stmt::OpaqueValueExprClass:
|
||||
case Stmt::AsTypeExprClass:
|
||||
case Stmt::ConceptSpecializationExprClass:
|
||||
// Fall through.
|
||||
|
||||
// Cases we intentionally don't evaluate, since they don't need
|
||||
|
@ -1,61 +0,0 @@
|
||||
// RUN: %clang_cc1 -std=c++14 -fconcepts-ts -fcxx-exceptions -x c++ -verify %s
|
||||
|
||||
namespace A {
|
||||
template<typename T> concept bool C1() { return true; }
|
||||
|
||||
template<typename T> concept bool C2 = true;
|
||||
}
|
||||
|
||||
template<typename T> concept bool C3() { return (throw 0, true); }
|
||||
static_assert(noexcept(C3<int>()), "function concept should be treated as if noexcept(true) specified");
|
||||
|
||||
template<typename T> concept bool D1(); // expected-error {{function concept declaration must be a definition}}
|
||||
|
||||
struct B {
|
||||
template<typename T> concept bool D2() { return true; } // expected-error {{concept declarations may only appear in namespace scope}}
|
||||
};
|
||||
|
||||
struct C {
|
||||
template<typename T> static concept bool D3 = true; // expected-error {{concept declarations may only appear in namespace scope}}
|
||||
};
|
||||
|
||||
concept bool D4() { return true; } // expected-error {{'concept' can only appear on the definition of a function template or variable template}}
|
||||
|
||||
concept bool D5 = true; // expected-error {{'concept' can only appear on the definition of a function template or variable template}}
|
||||
|
||||
template<typename T>
|
||||
concept bool D6; // expected-error {{variable concept declaration must be initialized}}
|
||||
|
||||
template<typename T>
|
||||
concept bool D7() throw(int) { return true; } // expected-error {{function concept cannot have exception specification}}
|
||||
|
||||
// Tag
|
||||
|
||||
concept class CC1 {}; // expected-error {{'concept' can only appear on the definition of a function template or variable template}}
|
||||
concept struct CS1 {}; // expected-error {{'concept' can only appear on the definition of a function template or variable template}}
|
||||
concept union CU1 {}; // expected-error {{'concept' can only appear on the definition of a function template or variable template}}
|
||||
concept enum CE1 {}; // expected-error {{'concept' can only appear on the definition of a function template or variable template}}
|
||||
template <typename T> concept class TCC1 {}; // expected-error {{'concept' can only appear on the definition of a function template or variable template}}
|
||||
template <typename T> concept struct TCS1 {}; // expected-error {{'concept' can only appear on the definition of a function template or variable template}}
|
||||
template <typename T> concept union TCU1 {}; // expected-error {{'concept' can only appear on the definition of a function template or variable template}}
|
||||
typedef concept int CI; // expected-error {{'concept' can only appear on the definition of a function template or variable template}}
|
||||
void fpc(concept int i) {} // expected-error {{'concept' can only appear on the definition of a function template or variable template}}
|
||||
|
||||
concept bool; // expected-error {{'concept' can only appear on the definition of a function template or variable template}}
|
||||
|
||||
template <typename T> concept bool VCEI{ true };
|
||||
template concept bool VCEI<int>; // expected-error {{'concept' cannot be applied on an explicit instantiation}}
|
||||
extern template concept bool VCEI<int>; // expected-error {{'concept' cannot be applied on an explicit instantiation}}
|
||||
|
||||
template <typename T> concept bool VCPS{ true };
|
||||
template <typename T> concept bool VCPS<T *>{ true }; // expected-error {{'concept' cannot be applied on an partial specialization}}
|
||||
|
||||
template <typename T> concept bool VCES{ true };
|
||||
template <> concept bool VCES<int>{ true }; // expected-error {{'concept' cannot be applied on an explicit specialization}}
|
||||
|
||||
template <typename T> concept bool FCEI() { return true; }
|
||||
template concept bool FCEI<int>(); // expected-error {{'concept' cannot be applied on an explicit instantiation}}
|
||||
extern template concept bool FCEI<int>(); // expected-error {{'concept' cannot be applied on an explicit instantiation}}
|
||||
|
||||
template <typename T> concept bool FCES() { return true; }
|
||||
template <> concept bool FCES<bool>() { return true; } // expected-error {{'concept' cannot be applied on an explicit specialization}}
|
@ -1,13 +0,0 @@
|
||||
// RUN: %clang_cc1 -std=c++14 -fconcepts-ts -x c++ -verify %s
|
||||
|
||||
template<typename T> concept thread_local bool VCTL = true; // expected-error {{variable concept cannot be declared 'thread_local'}}
|
||||
|
||||
template<typename T> concept constexpr bool VCC = true; // expected-error {{variable concept cannot be declared 'constexpr'}}
|
||||
|
||||
template<typename T> concept inline bool FCI() { return true; } // expected-error {{function concept cannot be declared 'inline'}}
|
||||
|
||||
struct X {
|
||||
template<typename T> concept friend bool FCF() { return true; } // expected-error {{function concept cannot be declared 'friend'}}
|
||||
};
|
||||
|
||||
template<typename T> concept constexpr bool FCC() { return true; } // expected-error {{function concept cannot be declared 'constexpr'}}
|
@ -1,25 +0,0 @@
|
||||
// RUN: %clang_cc1 -std=c++14 -fconcepts-ts -x c++ -verify %s
|
||||
|
||||
template<typename T>
|
||||
concept bool fcpv(void) { return true; }
|
||||
|
||||
template<typename T>
|
||||
concept bool fcpi(int i = 0) { return true; } // expected-error {{function concept cannot have any parameters}}
|
||||
|
||||
template<typename... Ts>
|
||||
concept bool fcpp(Ts... ts) { return true; } // expected-error {{function concept cannot have any parameters}}
|
||||
|
||||
template<typename T>
|
||||
concept bool fcpva(...) { return true; } // expected-error {{function concept cannot have any parameters}}
|
||||
|
||||
template<typename T>
|
||||
concept const bool fcrtc() { return true; } // expected-error {{declared return type of function concept must be 'bool'}}
|
||||
|
||||
template<typename T>
|
||||
concept int fcrti() { return 5; } // expected-error {{declared return type of function concept must be 'bool'}}
|
||||
|
||||
template<typename T>
|
||||
concept float fcrtf() { return 5.5; } // expected-error {{declared return type of function concept must be 'bool'}}
|
||||
|
||||
template<typename T>
|
||||
concept decltype(auto) fcrtd(void) { return true; } // expected-error {{declared return type of function concept must be 'bool'}}
|
@ -1,25 +0,0 @@
|
||||
// RUN: %clang_cc1 -std=c++14 -fconcepts-ts -x c++ -verify %s
|
||||
|
||||
template<typename T>
|
||||
concept bool vc { true };
|
||||
|
||||
template<typename T>
|
||||
struct B { typedef bool Boolean; };
|
||||
|
||||
template<int N>
|
||||
B<void>::Boolean concept vctb(!0);
|
||||
|
||||
template<typename T>
|
||||
concept const bool vctc { true }; // expected-error {{declared type of variable concept must be 'bool'}}
|
||||
|
||||
template<typename T>
|
||||
concept int vcti { 5 }; // expected-error {{declared type of variable concept must be 'bool'}}
|
||||
|
||||
template<typename T>
|
||||
concept float vctf { 5.5 }; // expected-error {{declared type of variable concept must be 'bool'}}
|
||||
|
||||
template<typename T>
|
||||
concept auto vcta { true }; // expected-error {{declared type of variable concept must be 'bool'}}
|
||||
|
||||
template<typename T>
|
||||
concept decltype(auto) vctd { true }; // expected-error {{declared type of variable concept must be 'bool'}}
|
@ -1,18 +0,0 @@
|
||||
// RUN: %clang_cc1 -std=c++14 -fconcepts-ts -x c++ -verify %s
|
||||
|
||||
template <typename T> concept bool FCEI() { return true; } // expected-note {{previous declaration is here}} expected-note {{previous declaration is here}}
|
||||
template bool FCEI<int>(); // expected-error {{function concept cannot be explicitly instantiated}}
|
||||
extern template bool FCEI<double>(); // expected-error {{function concept cannot be explicitly instantiated}}
|
||||
|
||||
template <typename T> concept bool FCES() { return true; } // expected-note {{previous declaration is here}}
|
||||
template <> bool FCES<int>() { return true; } // expected-error {{function concept cannot be explicitly specialized}}
|
||||
|
||||
template <typename T> concept bool VC { true }; // expected-note {{previous declaration is here}} expected-note {{previous declaration is here}}
|
||||
template bool VC<int>; // expected-error {{variable concept cannot be explicitly instantiated}}
|
||||
extern template bool VC<double>; // expected-error {{variable concept cannot be explicitly instantiated}}
|
||||
|
||||
template <typename T> concept bool VCES { true }; // expected-note {{previous declaration is here}}
|
||||
template <> bool VCES<int> { true }; // expected-error {{variable concept cannot be explicitly specialized}}
|
||||
|
||||
template <typename T> concept bool VCPS { true }; // expected-note {{previous declaration is here}}
|
||||
template <typename T> bool VCPS<T *> { true }; // expected-error {{variable concept cannot be partially specialized}}
|
@ -1,26 +0,0 @@
|
||||
# -*- Python -*-
|
||||
|
||||
import os
|
||||
import lit.formats
|
||||
|
||||
from lit.llvm import llvm_config
|
||||
|
||||
# Configuration file for the 'lit' test runner.
|
||||
|
||||
# name: The name of this test suite.
|
||||
config.name = 'Clang-Concepts-TS-Unsupported'
|
||||
|
||||
# testFormat: The test format to use to interpret tests.
|
||||
#
|
||||
# For now we require '&&' between commands, until they get globally killed and
|
||||
# the test runner updated.
|
||||
config.test_format = lit.formats.ShTest(not llvm_config.use_lit_shell)
|
||||
|
||||
# suffixes: A list of file extensions to treat as test files.
|
||||
config.suffixes = ['.c', '.cpp', '.cppm', '.m', '.mm', '.cu',
|
||||
'.ll', '.cl', '.s', '.S', '.modulemap', '.test', '.rs']
|
||||
|
||||
config.unsupported = True
|
||||
|
||||
# test_source_root: The root path where tests are located.
|
||||
config.test_source_root = os.path.dirname(__file__)
|
@ -1,4 +0,0 @@
|
||||
// RUN: %clang_cc1 -std=c++2a -fconcepts-ts -verify %s
|
||||
|
||||
template<typename T> concept C = true;
|
||||
static_assert(C<int>); // expected-error{{sorry, unimplemented concepts feature concept specialization used}}
|
149
clang/test/CXX/expr/expr.prim/expr.prim.id/p3.cpp
Normal file
149
clang/test/CXX/expr/expr.prim/expr.prim.id/p3.cpp
Normal file
@ -0,0 +1,149 @@
|
||||
// RUN: %clang_cc1 -std=c++2a -verify -triple x86_64-linux-gnu %s
|
||||
|
||||
template<typename T> concept C1 = true; // expected-note{{template is declared here}}
|
||||
static_assert(C1<int>);
|
||||
static_assert(C1);
|
||||
// expected-error@-1{{use of concept 'C1' requires template arguments}}
|
||||
|
||||
template<typename T> concept C2 = sizeof(T) == 4;
|
||||
static_assert(C2<int>);
|
||||
static_assert(!C2<long long int>);
|
||||
static_assert(C2<char[4]>);
|
||||
static_assert(!C2<char[5]>);
|
||||
|
||||
template<typename T> concept C3 = sizeof(*T{}) == 4;
|
||||
static_assert(C3<int*>);
|
||||
static_assert(!C3<long long int>);
|
||||
|
||||
struct A {
|
||||
static constexpr int add(int a, int b) {
|
||||
return a + b;
|
||||
}
|
||||
};
|
||||
struct B {
|
||||
static int add(int a, int b) { // expected-note{{declared here}}
|
||||
return a + b;
|
||||
}
|
||||
};
|
||||
template<typename U>
|
||||
concept C4 = U::add(1, 2) == 3;
|
||||
// expected-error@-1{{substitution into constraint expression resulted in a non-constant expression}}
|
||||
// expected-note@-2{{non-constexpr function 'add' cannot be used in a constant expression}}
|
||||
static_assert(C4<A>);
|
||||
static_assert(!C4<B>); // expected-note {{while checking the satisfaction of concept 'C4<B>' requested here}}
|
||||
|
||||
template<typename T, typename U>
|
||||
constexpr bool is_same_v = false;
|
||||
|
||||
template<typename T>
|
||||
constexpr bool is_same_v<T, T> = true;
|
||||
|
||||
template<typename T, typename U>
|
||||
concept Same = is_same_v<T, U>;
|
||||
|
||||
static_assert(Same<int, int>);
|
||||
static_assert(Same<int, decltype(1)>);
|
||||
static_assert(!Same<int, unsigned int>);
|
||||
static_assert(!Same<A, B>);
|
||||
static_assert(Same<A, A>);
|
||||
|
||||
static_assert(Same<bool, decltype(C1<int>)>);
|
||||
static_assert(Same<bool, decltype(C2<int>)>);
|
||||
static_assert(Same<bool, decltype(C3<int*>)>);
|
||||
static_assert(Same<bool, decltype(C4<A>)>);
|
||||
|
||||
template<typename T> concept C5 = T{}; // expected-error {{atomic constraint must be of type 'bool' (found 'int')}}
|
||||
constexpr bool x = C5<int>; // expected-note {{while checking the satisfaction of concept 'C5<int>' requested here}}
|
||||
|
||||
template<int x>
|
||||
concept IsEven = (x % 2) == 0;
|
||||
|
||||
static_assert(IsEven<20>);
|
||||
static_assert(!IsEven<11>);
|
||||
|
||||
template<template<typename T> typename P>
|
||||
concept IsTypePredicate = is_same_v<decltype(P<bool>::value), const bool>
|
||||
&& is_same_v<decltype(P<int>::value), const bool>
|
||||
&& is_same_v<decltype(P<long long>::value), const bool>;
|
||||
|
||||
template<typename T> struct T1 {};
|
||||
template<typename T> struct T2 { static constexpr bool value = sizeof(T) == 2; };
|
||||
|
||||
static_assert(IsTypePredicate<T2>);
|
||||
static_assert(!IsTypePredicate<T1>);
|
||||
|
||||
namespace piecewise_substitution {
|
||||
template <typename T>
|
||||
concept True = true;
|
||||
|
||||
template <typename T>
|
||||
concept A = True<T> || T::value;
|
||||
|
||||
template <typename T>
|
||||
concept B = (True<T> || T::value);
|
||||
|
||||
template <typename T>
|
||||
concept C = !True<T> && T::value || true;
|
||||
|
||||
template <typename T>
|
||||
concept D = (!True<T> && T::value) || true;
|
||||
|
||||
template <typename T>
|
||||
concept E = T::value || True<T>;
|
||||
|
||||
template <typename T>
|
||||
concept F = (T::value || True<T>);
|
||||
|
||||
template <typename T>
|
||||
concept G = T::value && !True<T> || true;
|
||||
|
||||
template <typename T>
|
||||
concept H = (T::value && !True<T>) || true;
|
||||
|
||||
template <typename T>
|
||||
concept I = T::value;
|
||||
|
||||
static_assert(A<int>);
|
||||
static_assert(B<int>);
|
||||
static_assert(C<int>);
|
||||
static_assert(D<int>);
|
||||
static_assert(E<int>);
|
||||
static_assert(F<int>);
|
||||
static_assert(G<int>);
|
||||
static_assert(H<int>);
|
||||
static_assert(!I<int>);
|
||||
}
|
||||
|
||||
// Short ciruiting
|
||||
|
||||
template<typename T> struct T3 { using type = typename T::type; };
|
||||
// expected-error@-1{{type 'char' cannot be used prior to '::' because it has no members}}
|
||||
// expected-error@-2{{type 'short' cannot be used prior to '::' because it has no members}}
|
||||
|
||||
template<typename T>
|
||||
concept C6 = sizeof(T) == 1 && sizeof(typename T3<T>::type) == 1;
|
||||
// expected-note@-1{{while substituting template arguments into constraint expression here}}
|
||||
// expected-note@-2{{in instantiation of template class 'T3<char>' requested here}}
|
||||
|
||||
template<typename T>
|
||||
concept C7 = sizeof(T) == 1 || sizeof(
|
||||
// expected-note@-1{{while substituting template arguments into constraint expression here}}
|
||||
typename
|
||||
T3<T>
|
||||
// expected-note@-1{{in instantiation of template class 'T3<short>' requested here}}
|
||||
::type) == 1;
|
||||
|
||||
static_assert(!C6<short>);
|
||||
static_assert(!C6<char>); // expected-note{{while checking the satisfaction of concept 'C6<char>' requested here}}
|
||||
static_assert(C7<char>);
|
||||
static_assert(!C7<short>); // expected-note{{while checking the satisfaction of concept 'C7<short>' requested here}}
|
||||
|
||||
// Make sure argument list is converted when instantiating a CSE.
|
||||
|
||||
template<typename T, typename U = int>
|
||||
concept SameSize = sizeof(T) == sizeof(U);
|
||||
|
||||
template<typename T>
|
||||
struct X { static constexpr bool a = SameSize<T>; };
|
||||
|
||||
static_assert(X<unsigned>::a);
|
32
clang/test/PCH/cxx2a-concept-specialization-expr.cpp
Normal file
32
clang/test/PCH/cxx2a-concept-specialization-expr.cpp
Normal file
@ -0,0 +1,32 @@
|
||||
// RUN: %clang_cc1 -std=c++2a -emit-pch %s -o %t
|
||||
// RUN: %clang_cc1 -std=c++2a -include-pch %t -verify %s
|
||||
|
||||
// expected-no-diagnostics
|
||||
|
||||
#ifndef HEADER
|
||||
#define HEADER
|
||||
|
||||
template<typename... T>
|
||||
concept C = true;
|
||||
|
||||
namespace n {
|
||||
template<typename... T>
|
||||
concept C = true;
|
||||
}
|
||||
|
||||
void f() {
|
||||
(void)C<int>;
|
||||
(void)C<int, void>;
|
||||
(void)n::C<void>;
|
||||
}
|
||||
|
||||
#else /*included pch*/
|
||||
|
||||
int main() {
|
||||
(void)C<int>;
|
||||
(void)C<int, void>;
|
||||
(void)n::C<void>;
|
||||
f();
|
||||
}
|
||||
|
||||
#endif // HEADER
|
@ -14,8 +14,6 @@ template<template<typename> concept T> concept D2 = true;
|
||||
// expected-error@-2{{template template parameter requires 'class' after the parameter list}}
|
||||
// expected-error@-3{{concept template parameter list must have at least one parameter; explicit specialization of concepts is not allowed}}
|
||||
|
||||
template<typename T> concept C2 = 0.f; // expected-error {{constraint expression must be of type 'bool' but is of type 'float'}}
|
||||
|
||||
struct S1 {
|
||||
template<typename T> concept C1 = true; // expected-error {{concept declarations may only appear in global or namespace scope}}
|
||||
};
|
||||
@ -26,15 +24,15 @@ extern "C++" {
|
||||
|
||||
template<typename A>
|
||||
template<typename B>
|
||||
concept C4 = true; // expected-error {{extraneous template parameter list in concept definition}}
|
||||
concept C2 = true; // expected-error {{extraneous template parameter list in concept definition}}
|
||||
|
||||
template<typename T> concept C5 = true; // expected-note {{previous}} expected-note {{previous}}
|
||||
int C5; // expected-error {{redefinition}}
|
||||
struct C5 {}; // expected-error {{redefinition}}
|
||||
template<typename T> concept C3 = true; // expected-note {{previous}} expected-note {{previous}}
|
||||
int C3; // expected-error {{redefinition}}
|
||||
struct C3 {}; // expected-error {{redefinition}}
|
||||
|
||||
struct C6 {}; // expected-note{{previous definition is here}}
|
||||
template<typename T> concept C6 = true;
|
||||
// expected-error@-1{{redefinition of 'C6' as different kind of symbol}}
|
||||
struct C4 {}; // expected-note{{previous definition is here}}
|
||||
template<typename T> concept C4 = true;
|
||||
// expected-error@-1{{redefinition of 'C4' as different kind of symbol}}
|
||||
|
||||
// TODO: Add test to prevent explicit specialization, partial specialization
|
||||
// and explicit instantiation of concepts.
|
||||
@ -43,31 +41,60 @@ template<typename T, T v>
|
||||
struct integral_constant { static constexpr T value = v; };
|
||||
|
||||
namespace N {
|
||||
template<typename T> concept C7 = true;
|
||||
template<typename T> concept C5 = true;
|
||||
}
|
||||
using N::C7;
|
||||
using N::C5;
|
||||
|
||||
template <bool word> concept C8 = integral_constant<bool, wor>::value;
|
||||
template <bool word> concept C6 = integral_constant<bool, wor>::value;
|
||||
// expected-error@-1{{use of undeclared identifier 'wor'; did you mean 'word'?}}
|
||||
// expected-note@-2{{'word' declared here}}
|
||||
|
||||
template<typename T> concept bool C9 = true;
|
||||
template<typename T> concept bool C7 = true;
|
||||
// expected-warning@-1{{ISO C++2a does not permit the 'bool' keyword after 'concept'}}
|
||||
|
||||
template<> concept C10 = false;
|
||||
template<> concept C8 = false;
|
||||
// expected-error@-1{{concept template parameter list must have at least one parameter; explicit specialization of concepts is not allowed}}
|
||||
|
||||
template<> concept C9<int> = false;
|
||||
template<> concept C7<int> = false;
|
||||
// expected-error@-1{{name defined in concept definition must be an identifier}}
|
||||
|
||||
template<typename T> concept N::C11 = false;
|
||||
template<typename T> concept N::C9 = false;
|
||||
// expected-error@-1{{name defined in concept definition must be an identifier}}
|
||||
|
||||
class A { };
|
||||
// expected-note@-1{{'A' declared here}}
|
||||
|
||||
template<typename T> concept A::C12 = false;
|
||||
template<typename T> concept A::C10 = false;
|
||||
// expected-error@-1{{expected namespace name}}
|
||||
|
||||
template<typename T> concept operator int = false;
|
||||
// expected-error@-1{{name defined in concept definition must be an identifier}}
|
||||
|
||||
template<bool x> concept C11 = 2; // expected-error {{atomic constraint must be of type 'bool' (found 'int')}}
|
||||
template<bool x> concept C12 = 2 && x; // expected-error {{atomic constraint must be of type 'bool' (found 'int')}}
|
||||
template<bool x> concept C13 = x || 2 || x; // expected-error {{atomic constraint must be of type 'bool' (found 'int')}}
|
||||
template<bool x> concept C14 = 8ull && x || x; // expected-error {{atomic constraint must be of type 'bool' (found 'unsigned long long')}}
|
||||
template<typename T> concept C15 = sizeof(T); // expected-error {{atomic constraint must be of type 'bool'}}
|
||||
template<typename T> concept C16 = true && (0 && 0); // expected-error {{atomic constraint must be of type 'bool' (found 'int')}}
|
||||
// expected-warning@-1{{use of logical '&&' with constant operand}}
|
||||
// expected-note@-2{{use '&' for a bitwise operation}}
|
||||
// expected-note@-3{{remove constant to silence this warning}}
|
||||
template<typename T> concept C17 = T{};
|
||||
static_assert(!C17<bool>);
|
||||
template<typename T> concept C18 = (bool&&)true;
|
||||
static_assert(C18<int>);
|
||||
template<typename T> concept C19 = (const bool&)true;
|
||||
static_assert(C19<int>);
|
||||
template<typename T> concept C20 = (const bool)true;
|
||||
static_assert(C20<int>);
|
||||
template <bool c> concept C21 = integral_constant<bool, c>::value && true;
|
||||
static_assert(C21<true>);
|
||||
static_assert(!C21<false>);
|
||||
template <bool c> concept C22 = integral_constant<bool, c>::value;
|
||||
static_assert(C22<true>);
|
||||
static_assert(!C22<false>);
|
||||
|
||||
template <bool word> concept C23 = integral_constant<bool, wor>::value;
|
||||
// expected-error@-1{{use of undeclared identifier 'wor'; did you mean 'word'?}}
|
||||
// expected-note@-2{{'word' declared here}}
|
||||
|
||||
|
@ -256,6 +256,7 @@ CXCursor cxcursor::MakeCXCursor(const Stmt *S, const Decl *Parent,
|
||||
case Stmt::BinaryConditionalOperatorClass:
|
||||
case Stmt::TypeTraitExprClass:
|
||||
case Stmt::CoawaitExprClass:
|
||||
case Stmt::ConceptSpecializationExprClass:
|
||||
case Stmt::DependentCoawaitExprClass:
|
||||
case Stmt::CoyieldExprClass:
|
||||
case Stmt::CXXBindTemporaryExprClass:
|
||||
|
Loading…
Reference in New Issue
Block a user