mirror of
https://github.com/capstone-engine/llvm-capstone.git
synced 2025-01-11 10:26:44 +00:00
[Concepts] Requires Expressions
Implement support for C++2a requires-expressions. Re-commit after compilation failure on some platforms due to alignment issues with PointerIntPair. Differential Revision: https://reviews.llvm.org/D50360
This commit is contained in:
parent
ed9cc6404e
commit
a0f50d7316
@ -22,6 +22,7 @@
|
|||||||
#include <utility>
|
#include <utility>
|
||||||
namespace clang {
|
namespace clang {
|
||||||
class ConceptDecl;
|
class ConceptDecl;
|
||||||
|
class ConceptSpecializationExpr;
|
||||||
|
|
||||||
/// \brief The result of a constraint satisfaction check, containing the
|
/// \brief The result of a constraint satisfaction check, containing the
|
||||||
/// necessary information to diagnose an unsatisfied constraint.
|
/// necessary information to diagnose an unsatisfied constraint.
|
||||||
|
@ -1893,6 +1893,37 @@ public:
|
|||||||
static bool classofKind(Kind K) { return K == CXXDeductionGuide; }
|
static bool classofKind(Kind K) { return K == CXXDeductionGuide; }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// \brief Represents the body of a requires-expression.
|
||||||
|
///
|
||||||
|
/// This decl exists merely to serve as the DeclContext for the local
|
||||||
|
/// parameters of the requires expression as well as other declarations inside
|
||||||
|
/// it.
|
||||||
|
///
|
||||||
|
/// \code
|
||||||
|
/// template<typename T> requires requires (T t) { {t++} -> regular; }
|
||||||
|
/// \endcode
|
||||||
|
///
|
||||||
|
/// In this example, a RequiresExpr object will be generated for the expression,
|
||||||
|
/// and a RequiresExprBodyDecl will be created to hold the parameter t and the
|
||||||
|
/// template argument list imposed by the compound requirement.
|
||||||
|
class RequiresExprBodyDecl : public Decl, public DeclContext {
|
||||||
|
RequiresExprBodyDecl(ASTContext &C, DeclContext *DC, SourceLocation StartLoc)
|
||||||
|
: Decl(RequiresExprBody, DC, StartLoc), DeclContext(RequiresExprBody) {}
|
||||||
|
|
||||||
|
public:
|
||||||
|
friend class ASTDeclReader;
|
||||||
|
friend class ASTDeclWriter;
|
||||||
|
|
||||||
|
static RequiresExprBodyDecl *Create(ASTContext &C, DeclContext *DC,
|
||||||
|
SourceLocation StartLoc);
|
||||||
|
|
||||||
|
static RequiresExprBodyDecl *CreateDeserialized(ASTContext &C, unsigned ID);
|
||||||
|
|
||||||
|
// Implement isa/cast/dyncast/etc.
|
||||||
|
static bool classof(const Decl *D) { return classofKind(D->getKind()); }
|
||||||
|
static bool classofKind(Kind K) { return K == RequiresExprBody; }
|
||||||
|
};
|
||||||
|
|
||||||
/// Represents a static or instance method of a struct/union/class.
|
/// Represents a static or instance method of a struct/union/class.
|
||||||
///
|
///
|
||||||
/// In the terminology of the C++ Standard, these are the (static and
|
/// In the terminology of the C++ Standard, these are the (static and
|
||||||
|
@ -14,7 +14,6 @@
|
|||||||
#ifndef LLVM_CLANG_AST_EXPRCXX_H
|
#ifndef LLVM_CLANG_AST_EXPRCXX_H
|
||||||
#define LLVM_CLANG_AST_EXPRCXX_H
|
#define LLVM_CLANG_AST_EXPRCXX_H
|
||||||
|
|
||||||
#include "clang/AST/ASTConcept.h"
|
|
||||||
#include "clang/AST/Decl.h"
|
#include "clang/AST/Decl.h"
|
||||||
#include "clang/AST/DeclBase.h"
|
#include "clang/AST/DeclBase.h"
|
||||||
#include "clang/AST/DeclCXX.h"
|
#include "clang/AST/DeclCXX.h"
|
||||||
@ -4836,99 +4835,6 @@ 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, public ConceptReference,
|
|
||||||
private llvm::TrailingObjects<ConceptSpecializationExpr,
|
|
||||||
TemplateArgument> {
|
|
||||||
friend class ASTStmtReader;
|
|
||||||
friend TrailingObjects;
|
|
||||||
public:
|
|
||||||
using SubstitutionDiagnostic = std::pair<SourceLocation, std::string>;
|
|
||||||
|
|
||||||
protected:
|
|
||||||
/// \brief The number of template arguments in the tail-allocated list of
|
|
||||||
/// converted template arguments.
|
|
||||||
unsigned NumTemplateArgs;
|
|
||||||
|
|
||||||
/// \brief Information about the satisfaction of the named concept with the
|
|
||||||
/// given arguments. If this expression is value dependent, this is to be
|
|
||||||
/// ignored.
|
|
||||||
ASTConstraintSatisfaction *Satisfaction;
|
|
||||||
|
|
||||||
ConceptSpecializationExpr(const ASTContext &C, NestedNameSpecifierLoc NNS,
|
|
||||||
SourceLocation TemplateKWLoc,
|
|
||||||
DeclarationNameInfo ConceptNameInfo,
|
|
||||||
NamedDecl *FoundDecl, ConceptDecl *NamedConcept,
|
|
||||||
const ASTTemplateArgumentListInfo *ArgsAsWritten,
|
|
||||||
ArrayRef<TemplateArgument> ConvertedArgs,
|
|
||||||
const ConstraintSatisfaction *Satisfaction);
|
|
||||||
|
|
||||||
ConceptSpecializationExpr(EmptyShell Empty, unsigned NumTemplateArgs);
|
|
||||||
|
|
||||||
public:
|
|
||||||
|
|
||||||
static ConceptSpecializationExpr *
|
|
||||||
Create(const ASTContext &C, NestedNameSpecifierLoc NNS,
|
|
||||||
SourceLocation TemplateKWLoc, DeclarationNameInfo ConceptNameInfo,
|
|
||||||
NamedDecl *FoundDecl, ConceptDecl *NamedConcept,
|
|
||||||
const ASTTemplateArgumentListInfo *ArgsAsWritten,
|
|
||||||
ArrayRef<TemplateArgument> ConvertedArgs,
|
|
||||||
const ConstraintSatisfaction *Satisfaction);
|
|
||||||
|
|
||||||
static ConceptSpecializationExpr *
|
|
||||||
Create(ASTContext &C, EmptyShell Empty, unsigned NumTemplateArgs);
|
|
||||||
|
|
||||||
ArrayRef<TemplateArgument> getTemplateArguments() const {
|
|
||||||
return ArrayRef<TemplateArgument>(getTrailingObjects<TemplateArgument>(),
|
|
||||||
NumTemplateArgs);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// \brief Set new template arguments for this concept specialization.
|
|
||||||
void setTemplateArguments(ArrayRef<TemplateArgument> Converted);
|
|
||||||
|
|
||||||
/// \brief Whether or not the concept with the given arguments was satisfied
|
|
||||||
/// when the expression was created.
|
|
||||||
/// The expression must not be dependent.
|
|
||||||
bool isSatisfied() const {
|
|
||||||
assert(!isValueDependent()
|
|
||||||
&& "isSatisfied called on a dependent ConceptSpecializationExpr");
|
|
||||||
return Satisfaction->IsSatisfied;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// \brief Get elaborated satisfaction info about the template arguments'
|
|
||||||
/// satisfaction of the named concept.
|
|
||||||
/// The expression must not be dependent.
|
|
||||||
const ASTConstraintSatisfaction &getSatisfaction() const {
|
|
||||||
assert(!isValueDependent()
|
|
||||||
&& "getSatisfaction called on dependent ConceptSpecializationExpr");
|
|
||||||
return *Satisfaction;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool classof(const Stmt *T) {
|
|
||||||
return T->getStmtClass() == ConceptSpecializationExprClass;
|
|
||||||
}
|
|
||||||
|
|
||||||
SourceLocation getBeginLoc() const LLVM_READONLY {
|
|
||||||
return ConceptName.getBeginLoc();
|
|
||||||
}
|
|
||||||
|
|
||||||
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
|
} // namespace clang
|
||||||
|
|
||||||
#endif // LLVM_CLANG_AST_EXPRCXX_H
|
#endif // LLVM_CLANG_AST_EXPRCXX_H
|
||||||
|
540
clang/include/clang/AST/ExprConcepts.h
Normal file
540
clang/include/clang/AST/ExprConcepts.h
Normal file
@ -0,0 +1,540 @@
|
|||||||
|
//===- ExprConcepts.h - C++2a Concepts expressions --------------*- C++ -*-===//
|
||||||
|
//
|
||||||
|
// 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
|
||||||
|
//
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
//
|
||||||
|
/// \file
|
||||||
|
/// Defines Expressions and AST nodes for C++2a concepts.
|
||||||
|
//
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
|
||||||
|
#ifndef LLVM_CLANG_AST_EXPRCONCEPTS_H
|
||||||
|
#define LLVM_CLANG_AST_EXPRCONCEPTS_H
|
||||||
|
|
||||||
|
#include "clang/AST/ASTContext.h"
|
||||||
|
#include "clang/AST/ASTConcept.h"
|
||||||
|
#include "clang/AST/Decl.h"
|
||||||
|
#include "clang/AST/DeclarationName.h"
|
||||||
|
#include "clang/AST/DeclTemplate.h"
|
||||||
|
#include "clang/AST/Expr.h"
|
||||||
|
#include "clang/AST/NestedNameSpecifier.h"
|
||||||
|
#include "clang/AST/TemplateBase.h"
|
||||||
|
#include "clang/AST/Type.h"
|
||||||
|
#include "clang/Basic/SourceLocation.h"
|
||||||
|
#include "llvm/Support/TrailingObjects.h"
|
||||||
|
#include <utility>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace clang {
|
||||||
|
class ASTStmtReader;
|
||||||
|
class ASTStmtWriter;
|
||||||
|
|
||||||
|
/// \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, public ConceptReference,
|
||||||
|
private llvm::TrailingObjects<ConceptSpecializationExpr,
|
||||||
|
TemplateArgument> {
|
||||||
|
friend class ASTStmtReader;
|
||||||
|
friend TrailingObjects;
|
||||||
|
public:
|
||||||
|
using SubstitutionDiagnostic = std::pair<SourceLocation, std::string>;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
/// \brief The number of template arguments in the tail-allocated list of
|
||||||
|
/// converted template arguments.
|
||||||
|
unsigned NumTemplateArgs;
|
||||||
|
|
||||||
|
/// \brief Information about the satisfaction of the named concept with the
|
||||||
|
/// given arguments. If this expression is value dependent, this is to be
|
||||||
|
/// ignored.
|
||||||
|
ASTConstraintSatisfaction *Satisfaction;
|
||||||
|
|
||||||
|
ConceptSpecializationExpr(const ASTContext &C, NestedNameSpecifierLoc NNS,
|
||||||
|
SourceLocation TemplateKWLoc,
|
||||||
|
DeclarationNameInfo ConceptNameInfo,
|
||||||
|
NamedDecl *FoundDecl, ConceptDecl *NamedConcept,
|
||||||
|
const ASTTemplateArgumentListInfo *ArgsAsWritten,
|
||||||
|
ArrayRef<TemplateArgument> ConvertedArgs,
|
||||||
|
const ConstraintSatisfaction *Satisfaction);
|
||||||
|
|
||||||
|
ConceptSpecializationExpr(EmptyShell Empty, unsigned NumTemplateArgs);
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
static ConceptSpecializationExpr *
|
||||||
|
Create(const ASTContext &C, NestedNameSpecifierLoc NNS,
|
||||||
|
SourceLocation TemplateKWLoc, DeclarationNameInfo ConceptNameInfo,
|
||||||
|
NamedDecl *FoundDecl, ConceptDecl *NamedConcept,
|
||||||
|
const ASTTemplateArgumentListInfo *ArgsAsWritten,
|
||||||
|
ArrayRef<TemplateArgument> ConvertedArgs,
|
||||||
|
const ConstraintSatisfaction *Satisfaction);
|
||||||
|
|
||||||
|
static ConceptSpecializationExpr *
|
||||||
|
Create(ASTContext &C, EmptyShell Empty, unsigned NumTemplateArgs);
|
||||||
|
|
||||||
|
ArrayRef<TemplateArgument> getTemplateArguments() const {
|
||||||
|
return ArrayRef<TemplateArgument>(getTrailingObjects<TemplateArgument>(),
|
||||||
|
NumTemplateArgs);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// \brief Set new template arguments for this concept specialization.
|
||||||
|
void setTemplateArguments(ArrayRef<TemplateArgument> Converted);
|
||||||
|
|
||||||
|
/// \brief Whether or not the concept with the given arguments was satisfied
|
||||||
|
/// when the expression was created.
|
||||||
|
/// The expression must not be dependent.
|
||||||
|
bool isSatisfied() const {
|
||||||
|
assert(!isValueDependent()
|
||||||
|
&& "isSatisfied called on a dependent ConceptSpecializationExpr");
|
||||||
|
return Satisfaction->IsSatisfied;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// \brief Get elaborated satisfaction info about the template arguments'
|
||||||
|
/// satisfaction of the named concept.
|
||||||
|
/// The expression must not be dependent.
|
||||||
|
const ASTConstraintSatisfaction &getSatisfaction() const {
|
||||||
|
assert(!isValueDependent()
|
||||||
|
&& "getSatisfaction called on dependent ConceptSpecializationExpr");
|
||||||
|
return *Satisfaction;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool classof(const Stmt *T) {
|
||||||
|
return T->getStmtClass() == ConceptSpecializationExprClass;
|
||||||
|
}
|
||||||
|
|
||||||
|
SourceLocation getBeginLoc() const LLVM_READONLY {
|
||||||
|
return ConceptName.getBeginLoc();
|
||||||
|
}
|
||||||
|
|
||||||
|
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 concepts {
|
||||||
|
|
||||||
|
/// \brief A static requirement that can be used in a requires-expression to
|
||||||
|
/// check properties of types and expression.
|
||||||
|
class Requirement {
|
||||||
|
public:
|
||||||
|
// Note - simple and compound requirements are both represented by the same
|
||||||
|
// class (ExprRequirement).
|
||||||
|
enum RequirementKind { RK_Type, RK_Simple, RK_Compound, RK_Nested };
|
||||||
|
private:
|
||||||
|
const RequirementKind Kind;
|
||||||
|
bool Dependent : 1;
|
||||||
|
bool ContainsUnexpandedParameterPack : 1;
|
||||||
|
bool Satisfied : 1;
|
||||||
|
public:
|
||||||
|
struct SubstitutionDiagnostic {
|
||||||
|
StringRef SubstitutedEntity;
|
||||||
|
// FIXME: Store diagnostics semantically and not as prerendered strings.
|
||||||
|
// Fixing this probably requires serialization of PartialDiagnostic
|
||||||
|
// objects.
|
||||||
|
SourceLocation DiagLoc;
|
||||||
|
StringRef DiagMessage;
|
||||||
|
};
|
||||||
|
|
||||||
|
Requirement(RequirementKind Kind, bool IsDependent,
|
||||||
|
bool ContainsUnexpandedParameterPack, bool IsSatisfied = true) :
|
||||||
|
Kind(Kind), Dependent(IsDependent),
|
||||||
|
ContainsUnexpandedParameterPack(ContainsUnexpandedParameterPack),
|
||||||
|
Satisfied(IsSatisfied) {}
|
||||||
|
|
||||||
|
RequirementKind getKind() const { return Kind; }
|
||||||
|
|
||||||
|
bool isSatisfied() const {
|
||||||
|
assert(!Dependent &&
|
||||||
|
"isSatisfied can only be called on non-dependent requirements.");
|
||||||
|
return Satisfied;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setSatisfied(bool IsSatisfied) {
|
||||||
|
assert(!Dependent &&
|
||||||
|
"setSatisfied can only be called on non-dependent requirements.");
|
||||||
|
Satisfied = IsSatisfied;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setDependent(bool IsDependent) { Dependent = IsDependent; }
|
||||||
|
bool isDependent() const { return Dependent; }
|
||||||
|
|
||||||
|
void setContainsUnexpandedParameterPack(bool Contains) {
|
||||||
|
ContainsUnexpandedParameterPack = Contains;
|
||||||
|
}
|
||||||
|
bool containsUnexpandedParameterPack() const {
|
||||||
|
return ContainsUnexpandedParameterPack;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/// \brief A requires-expression requirement which queries the existence of a
|
||||||
|
/// type name or type template specialization ('type' requirements).
|
||||||
|
class TypeRequirement : public Requirement {
|
||||||
|
public:
|
||||||
|
enum SatisfactionStatus {
|
||||||
|
SS_Dependent,
|
||||||
|
SS_SubstitutionFailure,
|
||||||
|
SS_Satisfied
|
||||||
|
};
|
||||||
|
private:
|
||||||
|
llvm::PointerUnion<SubstitutionDiagnostic *, TypeSourceInfo *> Value;
|
||||||
|
SatisfactionStatus Status;
|
||||||
|
public:
|
||||||
|
friend ASTStmtReader;
|
||||||
|
friend ASTStmtWriter;
|
||||||
|
|
||||||
|
/// \brief Construct a type requirement from a type. If the given type is not
|
||||||
|
/// dependent, this indicates that the type exists and the requirement will be
|
||||||
|
/// satisfied. Otherwise, the SubstitutionDiagnostic constructor is to be
|
||||||
|
/// used.
|
||||||
|
TypeRequirement(TypeSourceInfo *T);
|
||||||
|
|
||||||
|
/// \brief Construct a type requirement when the nested name specifier is
|
||||||
|
/// invalid due to a bad substitution. The requirement is unsatisfied.
|
||||||
|
TypeRequirement(SubstitutionDiagnostic *Diagnostic) :
|
||||||
|
Requirement(RK_Type, false, false, false), Value(Diagnostic),
|
||||||
|
Status(SS_SubstitutionFailure) {}
|
||||||
|
|
||||||
|
SatisfactionStatus getSatisfactionStatus() const { return Status; }
|
||||||
|
void setSatisfactionStatus(SatisfactionStatus Status) {
|
||||||
|
this->Status = Status;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isSubstitutionFailure() const {
|
||||||
|
return Status == SS_SubstitutionFailure;
|
||||||
|
}
|
||||||
|
|
||||||
|
SubstitutionDiagnostic *getSubstitutionDiagnostic() const {
|
||||||
|
assert(Status == SS_SubstitutionFailure &&
|
||||||
|
"Attempted to get substitution diagnostic when there has been no "
|
||||||
|
"substitution failure.");
|
||||||
|
return Value.get<SubstitutionDiagnostic *>();
|
||||||
|
}
|
||||||
|
|
||||||
|
TypeSourceInfo *getType() const {
|
||||||
|
assert(!isSubstitutionFailure() &&
|
||||||
|
"Attempted to get type when there has been a substitution failure.");
|
||||||
|
return Value.get<TypeSourceInfo *>();
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool classof(const Requirement *R) {
|
||||||
|
return R->getKind() == RK_Type;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/// \brief A requires-expression requirement which queries the validity and
|
||||||
|
/// properties of an expression ('simple' and 'compound' requirements).
|
||||||
|
class ExprRequirement : public Requirement {
|
||||||
|
public:
|
||||||
|
enum SatisfactionStatus {
|
||||||
|
SS_Dependent,
|
||||||
|
SS_ExprSubstitutionFailure,
|
||||||
|
SS_NoexceptNotMet,
|
||||||
|
SS_TypeRequirementSubstitutionFailure,
|
||||||
|
SS_ConstraintsNotSatisfied,
|
||||||
|
SS_Satisfied
|
||||||
|
};
|
||||||
|
class ReturnTypeRequirement {
|
||||||
|
llvm::PointerIntPair<
|
||||||
|
llvm::PointerUnion<TemplateParameterList *, SubstitutionDiagnostic *>,
|
||||||
|
1, bool>
|
||||||
|
TypeConstraintInfo;
|
||||||
|
public:
|
||||||
|
friend ASTStmtReader;
|
||||||
|
friend ASTStmtWriter;
|
||||||
|
|
||||||
|
/// \brief No return type requirement was specified.
|
||||||
|
ReturnTypeRequirement() : TypeConstraintInfo(nullptr, 0) {}
|
||||||
|
|
||||||
|
/// \brief A return type requirement was specified but it was a
|
||||||
|
/// substitution failure.
|
||||||
|
ReturnTypeRequirement(SubstitutionDiagnostic *SubstDiag) :
|
||||||
|
TypeConstraintInfo(SubstDiag, 0) {}
|
||||||
|
|
||||||
|
/// \brief A 'type constraint' style return type requirement.
|
||||||
|
/// \param TPL an invented template parameter list containing a single
|
||||||
|
/// type parameter with a type-constraint.
|
||||||
|
// TODO: Can we maybe not save the whole template parameter list and just
|
||||||
|
// the type constraint? Saving the whole TPL makes it easier to handle in
|
||||||
|
// serialization but is less elegant.
|
||||||
|
ReturnTypeRequirement(TemplateParameterList *TPL);
|
||||||
|
|
||||||
|
bool isDependent() const {
|
||||||
|
return TypeConstraintInfo.getInt();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool containsUnexpandedParameterPack() const {
|
||||||
|
if (!isTypeConstraint())
|
||||||
|
return false;
|
||||||
|
return getTypeConstraintTemplateParameterList()
|
||||||
|
->containsUnexpandedParameterPack();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isEmpty() const {
|
||||||
|
return TypeConstraintInfo.getPointer().isNull();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isSubstitutionFailure() const {
|
||||||
|
return !isEmpty() &&
|
||||||
|
TypeConstraintInfo.getPointer().is<SubstitutionDiagnostic *>();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isTypeConstraint() const {
|
||||||
|
return !isEmpty() &&
|
||||||
|
TypeConstraintInfo.getPointer().is<TemplateParameterList *>();
|
||||||
|
}
|
||||||
|
|
||||||
|
SubstitutionDiagnostic *getSubstitutionDiagnostic() const {
|
||||||
|
assert(isSubstitutionFailure());
|
||||||
|
return TypeConstraintInfo.getPointer().get<SubstitutionDiagnostic *>();
|
||||||
|
}
|
||||||
|
|
||||||
|
const TypeConstraint *getTypeConstraint() const;
|
||||||
|
|
||||||
|
TemplateParameterList *getTypeConstraintTemplateParameterList() const {
|
||||||
|
assert(isTypeConstraint());
|
||||||
|
return TypeConstraintInfo.getPointer().get<TemplateParameterList *>();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
private:
|
||||||
|
llvm::PointerUnion<Expr *, SubstitutionDiagnostic *> Value;
|
||||||
|
SourceLocation NoexceptLoc; // May be empty if noexcept wasn't specified.
|
||||||
|
ReturnTypeRequirement TypeReq;
|
||||||
|
ConceptSpecializationExpr *SubstitutedConstraintExpr;
|
||||||
|
SatisfactionStatus Status;
|
||||||
|
public:
|
||||||
|
friend ASTStmtReader;
|
||||||
|
friend ASTStmtWriter;
|
||||||
|
|
||||||
|
/// \brief Construct a compound requirement.
|
||||||
|
/// \param E the expression which is checked by this requirement.
|
||||||
|
/// \param IsSimple whether this was a simple requirement in source.
|
||||||
|
/// \param NoexceptLoc the location of the noexcept keyword, if it was
|
||||||
|
/// specified, otherwise an empty location.
|
||||||
|
/// \param Req the requirement for the type of the checked expression.
|
||||||
|
/// \param Status the satisfaction status of this requirement.
|
||||||
|
ExprRequirement(
|
||||||
|
Expr *E, bool IsSimple, SourceLocation NoexceptLoc,
|
||||||
|
ReturnTypeRequirement Req, SatisfactionStatus Status,
|
||||||
|
ConceptSpecializationExpr *SubstitutedConstraintExpr = nullptr);
|
||||||
|
|
||||||
|
/// \brief Construct a compound requirement whose expression was a
|
||||||
|
/// substitution failure. The requirement is not satisfied.
|
||||||
|
/// \param E the diagnostic emitted while instantiating the original
|
||||||
|
/// expression.
|
||||||
|
/// \param IsSimple whether this was a simple requirement in source.
|
||||||
|
/// \param NoexceptLoc the location of the noexcept keyword, if it was
|
||||||
|
/// specified, otherwise an empty location.
|
||||||
|
/// \param Req the requirement for the type of the checked expression (omit
|
||||||
|
/// if no requirement was specified).
|
||||||
|
ExprRequirement(SubstitutionDiagnostic *E, bool IsSimple,
|
||||||
|
SourceLocation NoexceptLoc, ReturnTypeRequirement Req = {});
|
||||||
|
|
||||||
|
bool isSimple() const { return getKind() == RK_Simple; }
|
||||||
|
bool isCompound() const { return getKind() == RK_Compound; }
|
||||||
|
|
||||||
|
bool hasNoexceptRequirement() const { return NoexceptLoc.isValid(); }
|
||||||
|
SourceLocation getNoexceptLoc() const { return NoexceptLoc; }
|
||||||
|
|
||||||
|
SatisfactionStatus getSatisfactionStatus() const { return Status; }
|
||||||
|
|
||||||
|
bool isExprSubstitutionFailure() const {
|
||||||
|
return Status == SS_ExprSubstitutionFailure;
|
||||||
|
}
|
||||||
|
|
||||||
|
const ReturnTypeRequirement &getReturnTypeRequirement() const {
|
||||||
|
return TypeReq;
|
||||||
|
}
|
||||||
|
|
||||||
|
ConceptSpecializationExpr *
|
||||||
|
getReturnTypeRequirementSubstitutedConstraintExpr() const {
|
||||||
|
assert(Status >= SS_TypeRequirementSubstitutionFailure);
|
||||||
|
return SubstitutedConstraintExpr;
|
||||||
|
}
|
||||||
|
|
||||||
|
SubstitutionDiagnostic *getExprSubstitutionDiagnostic() const {
|
||||||
|
assert(isExprSubstitutionFailure() &&
|
||||||
|
"Attempted to get expression substitution diagnostic when there has "
|
||||||
|
"been no expression substitution failure");
|
||||||
|
return Value.get<SubstitutionDiagnostic *>();
|
||||||
|
}
|
||||||
|
|
||||||
|
Expr *getExpr() const {
|
||||||
|
assert(!isExprSubstitutionFailure() &&
|
||||||
|
"ExprRequirement has no expression because there has been a "
|
||||||
|
"substitution failure.");
|
||||||
|
return Value.get<Expr *>();
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool classof(const Requirement *R) {
|
||||||
|
return R->getKind() == RK_Compound || R->getKind() == RK_Simple;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/// \brief A requires-expression requirement which is satisfied when a general
|
||||||
|
/// constraint expression is satisfied ('nested' requirements).
|
||||||
|
class NestedRequirement : public Requirement {
|
||||||
|
llvm::PointerUnion<Expr *, SubstitutionDiagnostic *> Value;
|
||||||
|
const ASTConstraintSatisfaction *Satisfaction = nullptr;
|
||||||
|
|
||||||
|
public:
|
||||||
|
friend ASTStmtReader;
|
||||||
|
friend ASTStmtWriter;
|
||||||
|
|
||||||
|
NestedRequirement(SubstitutionDiagnostic *SubstDiag) :
|
||||||
|
Requirement(RK_Nested, /*Dependent=*/false,
|
||||||
|
/*ContainsUnexpandedParameterPack*/false,
|
||||||
|
/*Satisfied=*/false), Value(SubstDiag) {}
|
||||||
|
|
||||||
|
NestedRequirement(Expr *Constraint) :
|
||||||
|
Requirement(RK_Nested, /*Dependent=*/true,
|
||||||
|
Constraint->containsUnexpandedParameterPack()),
|
||||||
|
Value(Constraint) {
|
||||||
|
assert(Constraint->isInstantiationDependent() &&
|
||||||
|
"Nested requirement with non-dependent constraint must be "
|
||||||
|
"constructed with a ConstraintSatisfaction object");
|
||||||
|
}
|
||||||
|
|
||||||
|
NestedRequirement(ASTContext &C, Expr *Constraint,
|
||||||
|
const ConstraintSatisfaction &Satisfaction) :
|
||||||
|
Requirement(RK_Nested, Constraint->isInstantiationDependent(),
|
||||||
|
Constraint->containsUnexpandedParameterPack(),
|
||||||
|
Satisfaction.IsSatisfied),
|
||||||
|
Value(Constraint),
|
||||||
|
Satisfaction(ASTConstraintSatisfaction::Create(C, Satisfaction)) {}
|
||||||
|
|
||||||
|
bool isSubstitutionFailure() const {
|
||||||
|
return Value.is<SubstitutionDiagnostic *>();
|
||||||
|
}
|
||||||
|
|
||||||
|
SubstitutionDiagnostic *getSubstitutionDiagnostic() const {
|
||||||
|
assert(isSubstitutionFailure() &&
|
||||||
|
"getSubstitutionDiagnostic() may not be called when there was no "
|
||||||
|
"substitution failure.");
|
||||||
|
return Value.get<SubstitutionDiagnostic *>();
|
||||||
|
}
|
||||||
|
|
||||||
|
Expr *getConstraintExpr() const {
|
||||||
|
assert(!isSubstitutionFailure() && "getConstraintExpr() may not be called "
|
||||||
|
"on nested requirements with "
|
||||||
|
"substitution failures.");
|
||||||
|
return Value.get<Expr *>();
|
||||||
|
}
|
||||||
|
|
||||||
|
const ASTConstraintSatisfaction &getConstraintSatisfaction() const {
|
||||||
|
assert(!isSubstitutionFailure() && "getConstraintSatisfaction() may not be "
|
||||||
|
"called on nested requirements with "
|
||||||
|
"substitution failures.");
|
||||||
|
return *Satisfaction;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool classof(const Requirement *R) {
|
||||||
|
return R->getKind() == RK_Nested;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace concepts
|
||||||
|
|
||||||
|
/// C++2a [expr.prim.req]:
|
||||||
|
/// A requires-expression provides a concise way to express requirements on
|
||||||
|
/// template arguments. A requirement is one that can be checked by name
|
||||||
|
/// lookup (6.4) or by checking properties of types and expressions.
|
||||||
|
/// [...]
|
||||||
|
/// A requires-expression is a prvalue of type bool [...]
|
||||||
|
class RequiresExpr final : public Expr,
|
||||||
|
llvm::TrailingObjects<RequiresExpr, ParmVarDecl *,
|
||||||
|
concepts::Requirement *> {
|
||||||
|
friend TrailingObjects;
|
||||||
|
friend class ASTStmtReader;
|
||||||
|
|
||||||
|
unsigned NumLocalParameters;
|
||||||
|
unsigned NumRequirements;
|
||||||
|
RequiresExprBodyDecl *Body;
|
||||||
|
SourceLocation RBraceLoc;
|
||||||
|
|
||||||
|
unsigned numTrailingObjects(OverloadToken<ParmVarDecl *>) const {
|
||||||
|
return NumLocalParameters;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned numTrailingObjects(OverloadToken<concepts::Requirement *>) const {
|
||||||
|
return NumRequirements;
|
||||||
|
}
|
||||||
|
|
||||||
|
RequiresExpr(ASTContext &C, SourceLocation RequiresKWLoc,
|
||||||
|
RequiresExprBodyDecl *Body,
|
||||||
|
ArrayRef<ParmVarDecl *> LocalParameters,
|
||||||
|
ArrayRef<concepts::Requirement *> Requirements,
|
||||||
|
SourceLocation RBraceLoc);
|
||||||
|
RequiresExpr(ASTContext &C, EmptyShell Empty, unsigned NumLocalParameters,
|
||||||
|
unsigned NumRequirements);
|
||||||
|
|
||||||
|
public:
|
||||||
|
static RequiresExpr *
|
||||||
|
Create(ASTContext &C, SourceLocation RequiresKWLoc,
|
||||||
|
RequiresExprBodyDecl *Body, ArrayRef<ParmVarDecl *> LocalParameters,
|
||||||
|
ArrayRef<concepts::Requirement *> Requirements,
|
||||||
|
SourceLocation RBraceLoc);
|
||||||
|
static RequiresExpr *
|
||||||
|
Create(ASTContext &C, EmptyShell Empty, unsigned NumLocalParameters,
|
||||||
|
unsigned NumRequirements);
|
||||||
|
|
||||||
|
ArrayRef<ParmVarDecl *> getLocalParameters() const {
|
||||||
|
return {getTrailingObjects<ParmVarDecl *>(), NumLocalParameters};
|
||||||
|
}
|
||||||
|
|
||||||
|
RequiresExprBodyDecl *getBody() const { return Body; }
|
||||||
|
|
||||||
|
ArrayRef<concepts::Requirement *> getRequirements() const {
|
||||||
|
return {getTrailingObjects<concepts::Requirement *>(), NumRequirements};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// \brief Whether or not the requires clause is satisfied.
|
||||||
|
/// The expression must not be dependent.
|
||||||
|
bool isSatisfied() const {
|
||||||
|
assert(!isValueDependent()
|
||||||
|
&& "isSatisfied called on a dependent RequiresExpr");
|
||||||
|
return RequiresExprBits.IsSatisfied;
|
||||||
|
}
|
||||||
|
|
||||||
|
SourceLocation getRequiresKWLoc() const {
|
||||||
|
return RequiresExprBits.RequiresKWLoc;
|
||||||
|
}
|
||||||
|
|
||||||
|
SourceLocation getRBraceLoc() const { return RBraceLoc; }
|
||||||
|
|
||||||
|
static bool classof(const Stmt *T) {
|
||||||
|
return T->getStmtClass() == RequiresExprClass;
|
||||||
|
}
|
||||||
|
|
||||||
|
SourceLocation getBeginLoc() const LLVM_READONLY {
|
||||||
|
return RequiresExprBits.RequiresKWLoc;
|
||||||
|
}
|
||||||
|
SourceLocation getEndLoc() const LLVM_READONLY {
|
||||||
|
return RBraceLoc;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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_EXPRCONCEPTS_H
|
@ -23,6 +23,7 @@
|
|||||||
#include "clang/AST/DeclOpenMP.h"
|
#include "clang/AST/DeclOpenMP.h"
|
||||||
#include "clang/AST/DeclTemplate.h"
|
#include "clang/AST/DeclTemplate.h"
|
||||||
#include "clang/AST/Expr.h"
|
#include "clang/AST/Expr.h"
|
||||||
|
#include "clang/AST/ExprConcepts.h"
|
||||||
#include "clang/AST/ExprCXX.h"
|
#include "clang/AST/ExprCXX.h"
|
||||||
#include "clang/AST/ExprObjC.h"
|
#include "clang/AST/ExprObjC.h"
|
||||||
#include "clang/AST/ExprOpenMP.h"
|
#include "clang/AST/ExprOpenMP.h"
|
||||||
@ -2138,6 +2139,8 @@ DEF_TRAVERSE_DECL(ParmVarDecl, {
|
|||||||
TRY_TO(TraverseStmt(D->getDefaultArg()));
|
TRY_TO(TraverseStmt(D->getDefaultArg()));
|
||||||
})
|
})
|
||||||
|
|
||||||
|
DEF_TRAVERSE_DECL(RequiresExprBodyDecl, {})
|
||||||
|
|
||||||
#undef DEF_TRAVERSE_DECL
|
#undef DEF_TRAVERSE_DECL
|
||||||
|
|
||||||
// ----------------- Stmt traversal -----------------
|
// ----------------- Stmt traversal -----------------
|
||||||
@ -2709,6 +2712,28 @@ DEF_TRAVERSE_STMT(ConceptSpecializationExpr, {
|
|||||||
TRY_TO(TraverseConceptReference(*S));
|
TRY_TO(TraverseConceptReference(*S));
|
||||||
})
|
})
|
||||||
|
|
||||||
|
DEF_TRAVERSE_STMT(RequiresExpr, {
|
||||||
|
TRY_TO(TraverseDecl(S->getBody()));
|
||||||
|
for (ParmVarDecl *Parm : S->getLocalParameters())
|
||||||
|
TRY_TO(TraverseDecl(Parm));
|
||||||
|
for (concepts::Requirement *Req : S->getRequirements())
|
||||||
|
if (auto *TypeReq = dyn_cast<concepts::TypeRequirement>(Req)) {
|
||||||
|
if (!TypeReq->isSubstitutionFailure())
|
||||||
|
TRY_TO(TraverseTypeLoc(TypeReq->getType()->getTypeLoc()));
|
||||||
|
} else if (auto *ExprReq = dyn_cast<concepts::ExprRequirement>(Req)) {
|
||||||
|
if (!ExprReq->isExprSubstitutionFailure())
|
||||||
|
TRY_TO(TraverseStmt(ExprReq->getExpr()));
|
||||||
|
auto &RetReq = ExprReq->getReturnTypeRequirement();
|
||||||
|
if (RetReq.isTypeConstraint())
|
||||||
|
TRY_TO(TraverseTemplateParameterListHelper(
|
||||||
|
RetReq.getTypeConstraintTemplateParameterList()));
|
||||||
|
} else {
|
||||||
|
auto *NestedReq = cast<concepts::NestedRequirement>(Req);
|
||||||
|
if (!NestedReq->isSubstitutionFailure())
|
||||||
|
TRY_TO(TraverseStmt(NestedReq->getConstraintExpr()));
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
// These literals (all of them) do not need any action.
|
// These literals (all of them) do not need any action.
|
||||||
DEF_TRAVERSE_STMT(IntegerLiteral, {})
|
DEF_TRAVERSE_STMT(IntegerLiteral, {})
|
||||||
DEF_TRAVERSE_STMT(FixedPointLiteral, {})
|
DEF_TRAVERSE_STMT(FixedPointLiteral, {})
|
||||||
|
@ -910,6 +910,17 @@ protected:
|
|||||||
SourceLocation NameLoc;
|
SourceLocation NameLoc;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class RequiresExprBitfields {
|
||||||
|
friend class ASTStmtReader;
|
||||||
|
friend class ASTStmtWriter;
|
||||||
|
friend class RequiresExpr;
|
||||||
|
|
||||||
|
unsigned : NumExprBits;
|
||||||
|
|
||||||
|
unsigned IsSatisfied : 1;
|
||||||
|
SourceLocation RequiresKWLoc;
|
||||||
|
};
|
||||||
|
|
||||||
//===--- C++ Coroutines TS bitfields classes ---===//
|
//===--- C++ Coroutines TS bitfields classes ---===//
|
||||||
|
|
||||||
class CoawaitExprBitfields {
|
class CoawaitExprBitfields {
|
||||||
@ -1008,6 +1019,7 @@ protected:
|
|||||||
UnresolvedMemberExprBitfields UnresolvedMemberExprBits;
|
UnresolvedMemberExprBitfields UnresolvedMemberExprBits;
|
||||||
CXXNoexceptExprBitfields CXXNoexceptExprBits;
|
CXXNoexceptExprBitfields CXXNoexceptExprBits;
|
||||||
SubstNonTypeTemplateParmExprBitfields SubstNonTypeTemplateParmExprBits;
|
SubstNonTypeTemplateParmExprBitfields SubstNonTypeTemplateParmExprBits;
|
||||||
|
RequiresExprBitfields RequiresExprBits;
|
||||||
|
|
||||||
// C++ Coroutines TS expressions
|
// C++ Coroutines TS expressions
|
||||||
CoawaitExprBitfields CoawaitBits;
|
CoawaitExprBitfields CoawaitBits;
|
||||||
|
@ -13,6 +13,7 @@
|
|||||||
#ifndef LLVM_CLANG_AST_STMTVISITOR_H
|
#ifndef LLVM_CLANG_AST_STMTVISITOR_H
|
||||||
#define LLVM_CLANG_AST_STMTVISITOR_H
|
#define LLVM_CLANG_AST_STMTVISITOR_H
|
||||||
|
|
||||||
|
#include "clang/AST/ExprConcepts.h"
|
||||||
#include "clang/AST/ExprCXX.h"
|
#include "clang/AST/ExprCXX.h"
|
||||||
#include "clang/AST/ExprObjC.h"
|
#include "clang/AST/ExprObjC.h"
|
||||||
#include "clang/AST/ExprOpenMP.h"
|
#include "clang/AST/ExprOpenMP.h"
|
||||||
|
@ -100,5 +100,6 @@ def OMPThreadPrivate : DeclNode<Decl>;
|
|||||||
def OMPAllocate : DeclNode<Decl>;
|
def OMPAllocate : DeclNode<Decl>;
|
||||||
def OMPRequires : DeclNode<Decl>;
|
def OMPRequires : DeclNode<Decl>;
|
||||||
def Empty : DeclNode<Decl>;
|
def Empty : DeclNode<Decl>;
|
||||||
|
def RequiresExprBody : DeclNode<Decl>, DeclContext;
|
||||||
def LifetimeExtendedTemporary : DeclNode<Decl>;
|
def LifetimeExtendedTemporary : DeclNode<Decl>;
|
||||||
|
|
||||||
|
@ -744,6 +744,33 @@ def err_friend_explicit_instantiation : Error<
|
|||||||
def err_explicit_instantiation_enum : Error<
|
def err_explicit_instantiation_enum : Error<
|
||||||
"enumerations cannot be explicitly instantiated">;
|
"enumerations cannot be explicitly instantiated">;
|
||||||
def err_expected_template_parameter : Error<"expected template parameter">;
|
def err_expected_template_parameter : Error<"expected template parameter">;
|
||||||
|
def note_ill_formed_requires_expression_outside_template : Note<
|
||||||
|
"requires expression outside a template declaration may not contain invalid "
|
||||||
|
"types or expressions">;
|
||||||
|
def err_empty_requires_expr : Error<
|
||||||
|
"a requires expression must contain at least one requirement">;
|
||||||
|
def err_requires_expr_parameter_list_ellipsis : Error<
|
||||||
|
"varargs not allowed in requires expression">;
|
||||||
|
def err_requires_expr_type_req_illegal_identifier : Error<
|
||||||
|
"expected identifier or template-id in type requirement">;
|
||||||
|
def err_requires_expr_type_req_template_args_on_non_template : Error<
|
||||||
|
"template arguments provided for non-template '%0'">;
|
||||||
|
def err_expected_semi_requirement : Error<
|
||||||
|
"expected ';' at end of requirement">;
|
||||||
|
def err_requires_expr_missing_arrow : Error<
|
||||||
|
"expected '->' before expression type requirement">;
|
||||||
|
def err_requires_expr_expected_type_constraint : Error<
|
||||||
|
"expected concept name with optional arguments">;
|
||||||
|
def err_requires_expr_simple_requirement_noexcept : Error<
|
||||||
|
"'noexcept' can only be used in a compound requirement (with '{' '}' around "
|
||||||
|
"the expression)">;
|
||||||
|
def err_requires_expr_simple_requirement_unexpected_tok : Error<
|
||||||
|
"unexpected %0 after expression; did you intend to use a compound "
|
||||||
|
"requirement (with '{' '}' around the expression)?">;
|
||||||
|
def warn_requires_expr_in_simple_requirement : Warning<
|
||||||
|
"this requires expression will only be checked for syntactic validity; did "
|
||||||
|
"you intend to place it in a nested requirement? (add another 'requires' "
|
||||||
|
"before the expression)">, InGroup<DiagGroup<"requires-expression">>;
|
||||||
|
|
||||||
def err_missing_dependent_template_keyword : Error<
|
def err_missing_dependent_template_keyword : Error<
|
||||||
"use 'template' keyword to treat '%0' as a dependent template name">;
|
"use 'template' keyword to treat '%0' as a dependent template name">;
|
||||||
|
@ -2102,12 +2102,21 @@ def err_auto_not_allowed : Error<
|
|||||||
"|in template argument|in typedef|in type alias|in function return type"
|
"|in template argument|in typedef|in type alias|in function return type"
|
||||||
"|in conversion function type|here|in lambda parameter"
|
"|in conversion function type|here|in lambda parameter"
|
||||||
"|in type allocated by 'new'|in K&R-style function parameter"
|
"|in type allocated by 'new'|in K&R-style function parameter"
|
||||||
"|in template parameter|in friend declaration}1">;
|
"|in template parameter|in friend declaration"
|
||||||
|
"|in requires expression parameter}1">;
|
||||||
|
def err_auto_not_allowed_in_return_type_requirement : Error<
|
||||||
|
"%select{'auto'|'decltype(auto)'|'__auto_type'}0 not allowed in expression "
|
||||||
|
"type requirement">;
|
||||||
def err_dependent_deduced_tst : Error<
|
def err_dependent_deduced_tst : Error<
|
||||||
"typename specifier refers to "
|
"typename specifier refers to "
|
||||||
"%select{class template|function template|variable template|alias template|"
|
"%select{class template|function template|variable template|alias template|"
|
||||||
"template template parameter|template}0 member in %1; "
|
"template template parameter|template}0 member in %1; "
|
||||||
"argument deduction not allowed here">;
|
"argument deduction not allowed here">;
|
||||||
|
def err_deduced_tst : Error<
|
||||||
|
"typename specifier refers to "
|
||||||
|
"%select{class template|function template|variable template|alias template|"
|
||||||
|
"template template parameter|template}0; argument deduction not allowed "
|
||||||
|
"here">;
|
||||||
def err_auto_not_allowed_var_inst : Error<
|
def err_auto_not_allowed_var_inst : Error<
|
||||||
"'auto' variable template instantiation is not allowed">;
|
"'auto' variable template instantiation is not allowed">;
|
||||||
def err_auto_var_requires_init : Error<
|
def err_auto_var_requires_init : Error<
|
||||||
@ -2590,19 +2599,56 @@ def note_constraints_not_satisfied : Note<
|
|||||||
def note_substituted_constraint_expr_is_ill_formed : Note<
|
def note_substituted_constraint_expr_is_ill_formed : Note<
|
||||||
"because substituted constraint expression is ill-formed%0">;
|
"because substituted constraint expression is ill-formed%0">;
|
||||||
def note_atomic_constraint_evaluated_to_false : Note<
|
def note_atomic_constraint_evaluated_to_false : Note<
|
||||||
"%select{and |because }0'%1' evaluated to false">;
|
"%select{and|because}0 '%1' evaluated to false">;
|
||||||
def note_concept_specialization_constraint_evaluated_to_false : Note<
|
def note_concept_specialization_constraint_evaluated_to_false : Note<
|
||||||
"%select{and |because }0'%1' evaluated to false">;
|
"%select{and|because}0 '%1' evaluated to false">;
|
||||||
def note_single_arg_concept_specialization_constraint_evaluated_to_false : Note<
|
def note_single_arg_concept_specialization_constraint_evaluated_to_false : Note<
|
||||||
"%select{and |because }0%1 does not satisfy %2">;
|
"%select{and|because}0 %1 does not satisfy %2">;
|
||||||
def note_atomic_constraint_evaluated_to_false_elaborated : Note<
|
def note_atomic_constraint_evaluated_to_false_elaborated : Note<
|
||||||
"%select{and |because }0'%1' (%2 %3 %4) evaluated to false">;
|
"%select{and|because}0 '%1' (%2 %3 %4) evaluated to false">;
|
||||||
def err_constrained_virtual_method : Error<
|
def err_constrained_virtual_method : Error<
|
||||||
"virtual function cannot have a requires clause">;
|
"virtual function cannot have a requires clause">;
|
||||||
def err_trailing_requires_clause_on_deduction_guide : Error<
|
def err_trailing_requires_clause_on_deduction_guide : Error<
|
||||||
"deduction guide cannot have a requires clause">;
|
"deduction guide cannot have a requires clause">;
|
||||||
def err_reference_to_function_with_unsatisfied_constraints : Error<
|
def err_reference_to_function_with_unsatisfied_constraints : Error<
|
||||||
"invalid reference to function %0: constraints not satisfied">;
|
"invalid reference to function %0: constraints not satisfied">;
|
||||||
|
def note_requires_expr_ill_formed_expr : Note<
|
||||||
|
"expression is invalid: %0">;
|
||||||
|
def note_requires_expr_no_implicit_conversion : Note<
|
||||||
|
"no implicit conversion exists between expression type %0 and expected type "
|
||||||
|
"%1">;
|
||||||
|
def err_requires_expr_local_parameter_default_argument : Error<
|
||||||
|
"default arguments not allowed for parameters of a requires expression">;
|
||||||
|
def err_requires_expr_parameter_referenced_in_evaluated_context : Error<
|
||||||
|
"constraint variable %0 cannot be used in an evaluated context">;
|
||||||
|
def note_expr_requirement_expr_substitution_error : Note<
|
||||||
|
"%select{and|because}0 '%1' would be invalid: %2">;
|
||||||
|
def note_expr_requirement_expr_unknown_substitution_error : Note<
|
||||||
|
"%select{and|because}0 '%1' would be invalid">;
|
||||||
|
def note_expr_requirement_noexcept_not_met : Note<
|
||||||
|
"%select{and|because}0 '%1' may throw an exception">;
|
||||||
|
def note_expr_requirement_type_requirement_substitution_error : Note<
|
||||||
|
"%select{and|because}0 '%1' would be invalid: %2">;
|
||||||
|
def note_expr_requirement_type_requirement_unknown_substitution_error : Note<
|
||||||
|
"%select{and|because}0 '%1' would be invalid">;
|
||||||
|
def note_expr_requirement_constraints_not_satisfied : Note<
|
||||||
|
"%select{and|because}0 type constraint '%1' was not satisfied:">;
|
||||||
|
def note_expr_requirement_constraints_not_satisfied_simple : Note<
|
||||||
|
"%select{and|because}0 %1 does not satisfy %2:">;
|
||||||
|
def note_type_requirement_substitution_error : Note<
|
||||||
|
"%select{and|because}0 '%1' would be invalid: %2">;
|
||||||
|
def note_type_requirement_unknown_substitution_error : Note<
|
||||||
|
"%select{and|because}0 '%1' would be invalid">;
|
||||||
|
def err_type_requirement_non_type_template : Error<
|
||||||
|
"'%0' refers to a %select{class template|function template|"
|
||||||
|
"variable template|alias template|template template parameter|template}1, "
|
||||||
|
"not a type template">;
|
||||||
|
def err_type_requirement_no_such_type : Error<
|
||||||
|
"'%0' does not name a type">;
|
||||||
|
def note_nested_requirement_substitution_error : Note<
|
||||||
|
"%select{and|because}0 '%1' would be invalid: %2">;
|
||||||
|
def note_nested_requirement_unknown_substitution_error : Note<
|
||||||
|
"%select{and|because}0 '%1' would be invalid">;
|
||||||
def note_ambiguous_atomic_constraints : Note<
|
def note_ambiguous_atomic_constraints : Note<
|
||||||
"similar constraint expressions not considered equivalent; constraint "
|
"similar constraint expressions not considered equivalent; constraint "
|
||||||
"expressions cannot be considered equivalent unless they originate from the "
|
"expressions cannot be considered equivalent unless they originate from the "
|
||||||
@ -4588,6 +4634,8 @@ def note_template_type_alias_instantiation_here : Note<
|
|||||||
"in instantiation of template type alias %0 requested here">;
|
"in instantiation of template type alias %0 requested here">;
|
||||||
def note_template_exception_spec_instantiation_here : Note<
|
def note_template_exception_spec_instantiation_here : Note<
|
||||||
"in instantiation of exception specification for %0 requested here">;
|
"in instantiation of exception specification for %0 requested here">;
|
||||||
|
def note_template_requirement_instantiation_here : Note<
|
||||||
|
"in instantiation of requirement here">;
|
||||||
def warn_var_template_missing : Warning<"instantiation of variable %q0 "
|
def warn_var_template_missing : Warning<"instantiation of variable %q0 "
|
||||||
"required here, but no definition is available">,
|
"required here, but no definition is available">,
|
||||||
InGroup<UndefinedVarTemplate>;
|
InGroup<UndefinedVarTemplate>;
|
||||||
@ -4623,6 +4671,8 @@ def note_template_default_arg_checking : Note<
|
|||||||
"while checking a default template argument used here">;
|
"while checking a default template argument used here">;
|
||||||
def note_concept_specialization_here : Note<
|
def note_concept_specialization_here : Note<
|
||||||
"while checking the satisfaction of concept '%0' requested here">;
|
"while checking the satisfaction of concept '%0' requested here">;
|
||||||
|
def note_nested_requirement_here : Note<
|
||||||
|
"while checking the satisfaction of nested requirement requested here">;
|
||||||
def note_checking_constraints_for_template_id_here : Note<
|
def note_checking_constraints_for_template_id_here : Note<
|
||||||
"while checking constraint satisfaction for template '%0' required here">;
|
"while checking constraint satisfaction for template '%0' required here">;
|
||||||
def note_checking_constraints_for_var_spec_id_here : Note<
|
def note_checking_constraints_for_var_spec_id_here : Note<
|
||||||
@ -4756,8 +4806,12 @@ def err_typename_nested_not_found_requirement : Error<
|
|||||||
"declaration">;
|
"declaration">;
|
||||||
def err_typename_nested_not_type : Error<
|
def err_typename_nested_not_type : Error<
|
||||||
"typename specifier refers to non-type member %0 in %1">;
|
"typename specifier refers to non-type member %0 in %1">;
|
||||||
def note_typename_refers_here : Note<
|
def err_typename_not_type : Error<
|
||||||
|
"typename specifier refers to non-type %0">;
|
||||||
|
def note_typename_member_refers_here : Note<
|
||||||
"referenced member %0 is declared here">;
|
"referenced member %0 is declared here">;
|
||||||
|
def note_typename_refers_here : Note<
|
||||||
|
"referenced %0 is declared here">;
|
||||||
def err_typename_missing : Error<
|
def err_typename_missing : Error<
|
||||||
"missing 'typename' prior to dependent type name '%0%1'">;
|
"missing 'typename' prior to dependent type name '%0%1'">;
|
||||||
def err_typename_missing_template : Error<
|
def err_typename_missing_template : Error<
|
||||||
|
@ -164,6 +164,7 @@ def CoyieldExpr : StmtNode<CoroutineSuspendExpr>;
|
|||||||
|
|
||||||
// C++2a Concepts expressions
|
// C++2a Concepts expressions
|
||||||
def ConceptSpecializationExpr : StmtNode<Expr>;
|
def ConceptSpecializationExpr : StmtNode<Expr>;
|
||||||
|
def RequiresExpr : StmtNode<Expr>;
|
||||||
|
|
||||||
// Obj-C Expressions.
|
// Obj-C Expressions.
|
||||||
def ObjCStringLiteral : StmtNode<Expr>;
|
def ObjCStringLiteral : StmtNode<Expr>;
|
||||||
|
@ -1933,6 +1933,7 @@ private:
|
|||||||
//===--------------------------------------------------------------------===//
|
//===--------------------------------------------------------------------===//
|
||||||
// C++ Concepts
|
// C++ Concepts
|
||||||
|
|
||||||
|
ExprResult ParseRequiresExpression();
|
||||||
void ParseTrailingRequiresClause(Declarator &D);
|
void ParseTrailingRequiresClause(Declarator &D);
|
||||||
|
|
||||||
//===--------------------------------------------------------------------===//
|
//===--------------------------------------------------------------------===//
|
||||||
@ -2771,7 +2772,7 @@ private:
|
|||||||
Declarator &D,
|
Declarator &D,
|
||||||
SmallVectorImpl<DeclaratorChunk::ParamInfo> &ParamInfo);
|
SmallVectorImpl<DeclaratorChunk::ParamInfo> &ParamInfo);
|
||||||
void ParseParameterDeclarationClause(
|
void ParseParameterDeclarationClause(
|
||||||
Declarator &D,
|
DeclaratorContext DeclaratorContext,
|
||||||
ParsedAttributes &attrs,
|
ParsedAttributes &attrs,
|
||||||
SmallVectorImpl<DeclaratorChunk::ParamInfo> &ParamInfo,
|
SmallVectorImpl<DeclaratorChunk::ParamInfo> &ParamInfo,
|
||||||
SourceLocation &EllipsisLoc);
|
SourceLocation &EllipsisLoc);
|
||||||
|
@ -1757,7 +1757,8 @@ enum class DeclaratorContext {
|
|||||||
TemplateArgContext, // Any template argument (in template argument list).
|
TemplateArgContext, // Any template argument (in template argument list).
|
||||||
TemplateTypeArgContext, // Template type argument (in default argument).
|
TemplateTypeArgContext, // Template type argument (in default argument).
|
||||||
AliasDeclContext, // C++11 alias-declaration.
|
AliasDeclContext, // C++11 alias-declaration.
|
||||||
AliasTemplateContext // C++11 alias-declaration template.
|
AliasTemplateContext, // C++11 alias-declaration template.
|
||||||
|
RequiresExprContext // C++2a requires-expression.
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
@ -1981,6 +1982,7 @@ public:
|
|||||||
case DeclaratorContext::TemplateTypeArgContext:
|
case DeclaratorContext::TemplateTypeArgContext:
|
||||||
case DeclaratorContext::TrailingReturnContext:
|
case DeclaratorContext::TrailingReturnContext:
|
||||||
case DeclaratorContext::TrailingReturnVarContext:
|
case DeclaratorContext::TrailingReturnVarContext:
|
||||||
|
case DeclaratorContext::RequiresExprContext:
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
llvm_unreachable("unknown context kind!");
|
llvm_unreachable("unknown context kind!");
|
||||||
@ -2003,6 +2005,7 @@ public:
|
|||||||
case DeclaratorContext::TemplateParamContext:
|
case DeclaratorContext::TemplateParamContext:
|
||||||
case DeclaratorContext::CXXCatchContext:
|
case DeclaratorContext::CXXCatchContext:
|
||||||
case DeclaratorContext::ObjCCatchContext:
|
case DeclaratorContext::ObjCCatchContext:
|
||||||
|
case DeclaratorContext::RequiresExprContext:
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
case DeclaratorContext::TypeNameContext:
|
case DeclaratorContext::TypeNameContext:
|
||||||
@ -2039,6 +2042,7 @@ public:
|
|||||||
case DeclaratorContext::MemberContext:
|
case DeclaratorContext::MemberContext:
|
||||||
case DeclaratorContext::PrototypeContext:
|
case DeclaratorContext::PrototypeContext:
|
||||||
case DeclaratorContext::TemplateParamContext:
|
case DeclaratorContext::TemplateParamContext:
|
||||||
|
case DeclaratorContext::RequiresExprContext:
|
||||||
// Maybe one day...
|
// Maybe one day...
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
@ -2116,6 +2120,7 @@ public:
|
|||||||
case DeclaratorContext::TemplateArgContext:
|
case DeclaratorContext::TemplateArgContext:
|
||||||
case DeclaratorContext::TemplateTypeArgContext:
|
case DeclaratorContext::TemplateTypeArgContext:
|
||||||
case DeclaratorContext::TrailingReturnContext:
|
case DeclaratorContext::TrailingReturnContext:
|
||||||
|
case DeclaratorContext::RequiresExprContext:
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
llvm_unreachable("unknown context kind!");
|
llvm_unreachable("unknown context kind!");
|
||||||
@ -2337,6 +2342,7 @@ public:
|
|||||||
case DeclaratorContext::TemplateTypeArgContext:
|
case DeclaratorContext::TemplateTypeArgContext:
|
||||||
case DeclaratorContext::TrailingReturnContext:
|
case DeclaratorContext::TrailingReturnContext:
|
||||||
case DeclaratorContext::TrailingReturnVarContext:
|
case DeclaratorContext::TrailingReturnVarContext:
|
||||||
|
case DeclaratorContext::RequiresExprContext:
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
llvm_unreachable("unknown context kind!");
|
llvm_unreachable("unknown context kind!");
|
||||||
@ -2370,6 +2376,7 @@ public:
|
|||||||
case DeclaratorContext::TrailingReturnContext:
|
case DeclaratorContext::TrailingReturnContext:
|
||||||
case DeclaratorContext::TrailingReturnVarContext:
|
case DeclaratorContext::TrailingReturnVarContext:
|
||||||
case DeclaratorContext::TemplateTypeArgContext:
|
case DeclaratorContext::TemplateTypeArgContext:
|
||||||
|
case DeclaratorContext::RequiresExprContext:
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
case DeclaratorContext::BlockContext:
|
case DeclaratorContext::BlockContext:
|
||||||
|
@ -21,6 +21,7 @@
|
|||||||
#include "clang/AST/DeclTemplate.h"
|
#include "clang/AST/DeclTemplate.h"
|
||||||
#include "clang/AST/DeclarationName.h"
|
#include "clang/AST/DeclarationName.h"
|
||||||
#include "clang/AST/Expr.h"
|
#include "clang/AST/Expr.h"
|
||||||
|
#include "clang/AST/ExprConcepts.h"
|
||||||
#include "clang/AST/ExprCXX.h"
|
#include "clang/AST/ExprCXX.h"
|
||||||
#include "clang/AST/ExprObjC.h"
|
#include "clang/AST/ExprObjC.h"
|
||||||
#include "clang/AST/ExternalASTSource.h"
|
#include "clang/AST/ExternalASTSource.h"
|
||||||
@ -6282,13 +6283,17 @@ public:
|
|||||||
|
|
||||||
/// \brief Emit diagnostics explaining why a constraint expression was deemed
|
/// \brief Emit diagnostics explaining why a constraint expression was deemed
|
||||||
/// unsatisfied.
|
/// unsatisfied.
|
||||||
|
/// \param First whether this is the first time an unsatisfied constraint is
|
||||||
|
/// diagnosed for this error.
|
||||||
void
|
void
|
||||||
DiagnoseUnsatisfiedConstraint(const ConstraintSatisfaction& Satisfaction);
|
DiagnoseUnsatisfiedConstraint(const ConstraintSatisfaction &Satisfaction,
|
||||||
|
bool First = true);
|
||||||
|
|
||||||
/// \brief Emit diagnostics explaining why a constraint expression was deemed
|
/// \brief Emit diagnostics explaining why a constraint expression was deemed
|
||||||
/// unsatisfied.
|
/// unsatisfied.
|
||||||
void
|
void
|
||||||
DiagnoseUnsatisfiedConstraint(const ASTConstraintSatisfaction& Satisfaction);
|
DiagnoseUnsatisfiedConstraint(const ASTConstraintSatisfaction &Satisfaction,
|
||||||
|
bool First = true);
|
||||||
|
|
||||||
/// \brief Emit diagnostics explaining why a constraint expression was deemed
|
/// \brief Emit diagnostics explaining why a constraint expression was deemed
|
||||||
/// unsatisfied because it was ill-formed.
|
/// unsatisfied because it was ill-formed.
|
||||||
@ -7262,7 +7267,17 @@ public:
|
|||||||
SourceLocation KeywordLoc,
|
SourceLocation KeywordLoc,
|
||||||
NestedNameSpecifierLoc QualifierLoc,
|
NestedNameSpecifierLoc QualifierLoc,
|
||||||
const IdentifierInfo &II,
|
const IdentifierInfo &II,
|
||||||
SourceLocation IILoc);
|
SourceLocation IILoc,
|
||||||
|
TypeSourceInfo **TSI,
|
||||||
|
bool DeducedTSTContext);
|
||||||
|
|
||||||
|
QualType CheckTypenameType(ElaboratedTypeKeyword Keyword,
|
||||||
|
SourceLocation KeywordLoc,
|
||||||
|
NestedNameSpecifierLoc QualifierLoc,
|
||||||
|
const IdentifierInfo &II,
|
||||||
|
SourceLocation IILoc,
|
||||||
|
bool DeducedTSTContext = true);
|
||||||
|
|
||||||
|
|
||||||
TypeSourceInfo *RebuildTypeInCurrentInstantiation(TypeSourceInfo *T,
|
TypeSourceInfo *RebuildTypeInCurrentInstantiation(TypeSourceInfo *T,
|
||||||
SourceLocation Loc,
|
SourceLocation Loc,
|
||||||
@ -7282,11 +7297,52 @@ public:
|
|||||||
const TemplateArgument *Args,
|
const TemplateArgument *Args,
|
||||||
unsigned NumArgs);
|
unsigned NumArgs);
|
||||||
|
|
||||||
// Concepts
|
//===--------------------------------------------------------------------===//
|
||||||
|
// C++ Concepts
|
||||||
|
//===--------------------------------------------------------------------===//
|
||||||
Decl *ActOnConceptDefinition(
|
Decl *ActOnConceptDefinition(
|
||||||
Scope *S, MultiTemplateParamsArg TemplateParameterLists,
|
Scope *S, MultiTemplateParamsArg TemplateParameterLists,
|
||||||
IdentifierInfo *Name, SourceLocation NameLoc, Expr *ConstraintExpr);
|
IdentifierInfo *Name, SourceLocation NameLoc, Expr *ConstraintExpr);
|
||||||
|
|
||||||
|
RequiresExprBodyDecl *
|
||||||
|
ActOnStartRequiresExpr(SourceLocation RequiresKWLoc,
|
||||||
|
ArrayRef<ParmVarDecl *> LocalParameters,
|
||||||
|
Scope *BodyScope);
|
||||||
|
void ActOnFinishRequiresExpr();
|
||||||
|
concepts::Requirement *ActOnSimpleRequirement(Expr *E);
|
||||||
|
concepts::Requirement *ActOnTypeRequirement(
|
||||||
|
SourceLocation TypenameKWLoc, CXXScopeSpec &SS, SourceLocation NameLoc,
|
||||||
|
IdentifierInfo *TypeName, TemplateIdAnnotation *TemplateId);
|
||||||
|
concepts::Requirement *ActOnCompoundRequirement(Expr *E,
|
||||||
|
SourceLocation NoexceptLoc);
|
||||||
|
concepts::Requirement *
|
||||||
|
ActOnCompoundRequirement(
|
||||||
|
Expr *E, SourceLocation NoexceptLoc, CXXScopeSpec &SS,
|
||||||
|
TemplateIdAnnotation *TypeConstraint, unsigned Depth);
|
||||||
|
concepts::Requirement *ActOnNestedRequirement(Expr *Constraint);
|
||||||
|
concepts::ExprRequirement *
|
||||||
|
BuildExprRequirement(
|
||||||
|
Expr *E, bool IsSatisfied, SourceLocation NoexceptLoc,
|
||||||
|
concepts::ExprRequirement::ReturnTypeRequirement ReturnTypeRequirement);
|
||||||
|
concepts::ExprRequirement *
|
||||||
|
BuildExprRequirement(
|
||||||
|
concepts::Requirement::SubstitutionDiagnostic *ExprSubstDiag,
|
||||||
|
bool IsSatisfied, SourceLocation NoexceptLoc,
|
||||||
|
concepts::ExprRequirement::ReturnTypeRequirement ReturnTypeRequirement);
|
||||||
|
concepts::TypeRequirement *BuildTypeRequirement(TypeSourceInfo *Type);
|
||||||
|
concepts::TypeRequirement *
|
||||||
|
BuildTypeRequirement(
|
||||||
|
concepts::Requirement::SubstitutionDiagnostic *SubstDiag);
|
||||||
|
concepts::NestedRequirement *BuildNestedRequirement(Expr *E);
|
||||||
|
concepts::NestedRequirement *
|
||||||
|
BuildNestedRequirement(
|
||||||
|
concepts::Requirement::SubstitutionDiagnostic *SubstDiag);
|
||||||
|
ExprResult ActOnRequiresExpr(SourceLocation RequiresKWLoc,
|
||||||
|
RequiresExprBodyDecl *Body,
|
||||||
|
ArrayRef<ParmVarDecl *> LocalParameters,
|
||||||
|
ArrayRef<concepts::Requirement *> Requirements,
|
||||||
|
SourceLocation ClosingBraceLoc);
|
||||||
|
|
||||||
//===--------------------------------------------------------------------===//
|
//===--------------------------------------------------------------------===//
|
||||||
// C++ Variadic Templates (C++0x [temp.variadic])
|
// C++ Variadic Templates (C++0x [temp.variadic])
|
||||||
//===--------------------------------------------------------------------===//
|
//===--------------------------------------------------------------------===//
|
||||||
@ -7933,6 +7989,13 @@ public:
|
|||||||
/// template which was deferred until it was needed.
|
/// template which was deferred until it was needed.
|
||||||
ExceptionSpecInstantiation,
|
ExceptionSpecInstantiation,
|
||||||
|
|
||||||
|
/// We are instantiating a requirement of a requires expression.
|
||||||
|
RequirementInstantiation,
|
||||||
|
|
||||||
|
/// We are checking the satisfaction of a nested requirement of a requires
|
||||||
|
/// expression.
|
||||||
|
NestedRequirementConstraintsCheck,
|
||||||
|
|
||||||
/// We are declaring an implicit special member function.
|
/// We are declaring an implicit special member function.
|
||||||
DeclaringSpecialMember,
|
DeclaringSpecialMember,
|
||||||
|
|
||||||
@ -8254,6 +8317,19 @@ public:
|
|||||||
ParameterMappingSubstitution, NamedDecl *Template,
|
ParameterMappingSubstitution, NamedDecl *Template,
|
||||||
SourceRange InstantiationRange);
|
SourceRange InstantiationRange);
|
||||||
|
|
||||||
|
/// \brief Note that we are substituting template arguments into a part of
|
||||||
|
/// a requirement of a requires expression.
|
||||||
|
InstantiatingTemplate(Sema &SemaRef, SourceLocation PointOfInstantiation,
|
||||||
|
concepts::Requirement *Req,
|
||||||
|
sema::TemplateDeductionInfo &DeductionInfo,
|
||||||
|
SourceRange InstantiationRange = SourceRange());
|
||||||
|
|
||||||
|
/// \brief Note that we are checking the satisfaction of the constraint
|
||||||
|
/// expression inside of a nested requirement.
|
||||||
|
InstantiatingTemplate(Sema &SemaRef, SourceLocation PointOfInstantiation,
|
||||||
|
concepts::NestedRequirement *Req, ConstraintsCheck,
|
||||||
|
SourceRange InstantiationRange = SourceRange());
|
||||||
|
|
||||||
/// Note that we have finished instantiating this template.
|
/// Note that we have finished instantiating this template.
|
||||||
void Clear();
|
void Clear();
|
||||||
|
|
||||||
|
@ -13,10 +13,17 @@
|
|||||||
|
|
||||||
#ifndef LLVM_CLANG_SEMA_SEMACONCEPT_H
|
#ifndef LLVM_CLANG_SEMA_SEMACONCEPT_H
|
||||||
#define LLVM_CLANG_SEMA_SEMACONCEPT_H
|
#define LLVM_CLANG_SEMA_SEMACONCEPT_H
|
||||||
|
#include "clang/AST/ASTConcept.h"
|
||||||
#include "clang/AST/ASTContext.h"
|
#include "clang/AST/ASTContext.h"
|
||||||
#include "clang/AST/Expr.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/Optional.h"
|
||||||
#include "llvm/ADT/SmallVector.h"
|
#include "llvm/ADT/SmallVector.h"
|
||||||
|
#include <string>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
namespace clang {
|
namespace clang {
|
||||||
class Sema;
|
class Sema;
|
||||||
|
|
||||||
|
@ -1403,6 +1403,9 @@ namespace serialization {
|
|||||||
/// An LifetimeExtendedTemporaryDecl record.
|
/// An LifetimeExtendedTemporaryDecl record.
|
||||||
DECL_LIFETIME_EXTENDED_TEMPORARY,
|
DECL_LIFETIME_EXTENDED_TEMPORARY,
|
||||||
|
|
||||||
|
/// A RequiresExprBodyDecl record.
|
||||||
|
DECL_REQUIRES_EXPR_BODY,
|
||||||
|
|
||||||
/// An ObjCTypeParamDecl record.
|
/// An ObjCTypeParamDecl record.
|
||||||
DECL_OBJC_TYPE_PARAM,
|
DECL_OBJC_TYPE_PARAM,
|
||||||
|
|
||||||
@ -1785,6 +1788,7 @@ namespace serialization {
|
|||||||
EXPR_MATERIALIZE_TEMPORARY, // MaterializeTemporaryExpr
|
EXPR_MATERIALIZE_TEMPORARY, // MaterializeTemporaryExpr
|
||||||
EXPR_CXX_FOLD, // CXXFoldExpr
|
EXPR_CXX_FOLD, // CXXFoldExpr
|
||||||
EXPR_CONCEPT_SPECIALIZATION,// ConceptSpecializationExpr
|
EXPR_CONCEPT_SPECIALIZATION,// ConceptSpecializationExpr
|
||||||
|
EXPR_REQUIRES, // RequiresExpr
|
||||||
|
|
||||||
// CUDA
|
// CUDA
|
||||||
EXPR_CUDA_KERNEL_CALL, // CUDAKernelCallExpr
|
EXPR_CUDA_KERNEL_CALL, // CUDAKernelCallExpr
|
||||||
|
@ -46,6 +46,7 @@ add_clang_library(clangAST
|
|||||||
DeclTemplate.cpp
|
DeclTemplate.cpp
|
||||||
Expr.cpp
|
Expr.cpp
|
||||||
ExprClassification.cpp
|
ExprClassification.cpp
|
||||||
|
ExprConcepts.cpp
|
||||||
ExprConstant.cpp
|
ExprConstant.cpp
|
||||||
ExprCXX.cpp
|
ExprCXX.cpp
|
||||||
ExprObjC.cpp
|
ExprObjC.cpp
|
||||||
|
@ -804,6 +804,7 @@ unsigned Decl::getIdentifierNamespaceForKind(Kind DeclKind) {
|
|||||||
case OMPCapturedExpr:
|
case OMPCapturedExpr:
|
||||||
case Empty:
|
case Empty:
|
||||||
case LifetimeExtendedTemporary:
|
case LifetimeExtendedTemporary:
|
||||||
|
case RequiresExprBody:
|
||||||
// Never looked up by name.
|
// Never looked up by name.
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -1177,6 +1178,7 @@ DeclContext *DeclContext::getPrimaryContext() {
|
|||||||
case Decl::Captured:
|
case Decl::Captured:
|
||||||
case Decl::OMPDeclareReduction:
|
case Decl::OMPDeclareReduction:
|
||||||
case Decl::OMPDeclareMapper:
|
case Decl::OMPDeclareMapper:
|
||||||
|
case Decl::RequiresExprBody:
|
||||||
// There is only one DeclContext for these entities.
|
// There is only one DeclContext for these entities.
|
||||||
return this;
|
return this;
|
||||||
|
|
||||||
|
@ -1968,6 +1968,16 @@ CXXDeductionGuideDecl *CXXDeductionGuideDecl::CreateDeserialized(ASTContext &C,
|
|||||||
QualType(), nullptr, SourceLocation());
|
QualType(), nullptr, SourceLocation());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
RequiresExprBodyDecl *RequiresExprBodyDecl::Create(
|
||||||
|
ASTContext &C, DeclContext *DC, SourceLocation StartLoc) {
|
||||||
|
return new (C, DC) RequiresExprBodyDecl(C, DC, StartLoc);
|
||||||
|
}
|
||||||
|
|
||||||
|
RequiresExprBodyDecl *RequiresExprBodyDecl::CreateDeserialized(ASTContext &C,
|
||||||
|
unsigned ID) {
|
||||||
|
return new (C, ID) RequiresExprBodyDecl(C, nullptr, SourceLocation());
|
||||||
|
}
|
||||||
|
|
||||||
void CXXMethodDecl::anchor() {}
|
void CXXMethodDecl::anchor() {}
|
||||||
|
|
||||||
bool CXXMethodDecl::isStatic() const {
|
bool CXXMethodDecl::isStatic() const {
|
||||||
|
@ -3457,6 +3457,7 @@ bool Expr::HasSideEffects(const ASTContext &Ctx,
|
|||||||
case OpaqueValueExprClass:
|
case OpaqueValueExprClass:
|
||||||
case SourceLocExprClass:
|
case SourceLocExprClass:
|
||||||
case ConceptSpecializationExprClass:
|
case ConceptSpecializationExprClass:
|
||||||
|
case RequiresExprClass:
|
||||||
// These never have a side-effect.
|
// These never have a side-effect.
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
@ -17,6 +17,7 @@
|
|||||||
#include "clang/AST/DeclAccessPair.h"
|
#include "clang/AST/DeclAccessPair.h"
|
||||||
#include "clang/AST/DeclBase.h"
|
#include "clang/AST/DeclBase.h"
|
||||||
#include "clang/AST/DeclCXX.h"
|
#include "clang/AST/DeclCXX.h"
|
||||||
|
#include "clang/AST/DeclTemplate.h"
|
||||||
#include "clang/AST/DeclarationName.h"
|
#include "clang/AST/DeclarationName.h"
|
||||||
#include "clang/AST/Expr.h"
|
#include "clang/AST/Expr.h"
|
||||||
#include "clang/AST/LambdaCapture.h"
|
#include "clang/AST/LambdaCapture.h"
|
||||||
@ -1764,81 +1765,3 @@ CUDAKernelCallExpr *CUDAKernelCallExpr::CreateEmpty(const ASTContext &Ctx,
|
|||||||
alignof(CUDAKernelCallExpr));
|
alignof(CUDAKernelCallExpr));
|
||||||
return new (Mem) CUDAKernelCallExpr(NumArgs, Empty);
|
return new (Mem) CUDAKernelCallExpr(NumArgs, Empty);
|
||||||
}
|
}
|
||||||
|
|
||||||
ConceptSpecializationExpr::ConceptSpecializationExpr(const ASTContext &C,
|
|
||||||
NestedNameSpecifierLoc NNS, SourceLocation TemplateKWLoc,
|
|
||||||
DeclarationNameInfo ConceptNameInfo, NamedDecl *FoundDecl,
|
|
||||||
ConceptDecl *NamedConcept, const ASTTemplateArgumentListInfo *ArgsAsWritten,
|
|
||||||
ArrayRef<TemplateArgument> ConvertedArgs,
|
|
||||||
const ConstraintSatisfaction *Satisfaction)
|
|
||||||
: Expr(ConceptSpecializationExprClass, C.BoolTy, VK_RValue, OK_Ordinary,
|
|
||||||
/*TypeDependent=*/false,
|
|
||||||
// All the flags below are set in setTemplateArguments.
|
|
||||||
/*ValueDependent=*/!Satisfaction, /*InstantiationDependent=*/false,
|
|
||||||
/*ContainsUnexpandedParameterPacks=*/false),
|
|
||||||
ConceptReference(NNS, TemplateKWLoc, ConceptNameInfo, FoundDecl,
|
|
||||||
NamedConcept, ArgsAsWritten),
|
|
||||||
NumTemplateArgs(ConvertedArgs.size()),
|
|
||||||
Satisfaction(Satisfaction ?
|
|
||||||
ASTConstraintSatisfaction::Create(C, *Satisfaction) :
|
|
||||||
nullptr) {
|
|
||||||
setTemplateArguments(ConvertedArgs);
|
|
||||||
}
|
|
||||||
|
|
||||||
ConceptSpecializationExpr::ConceptSpecializationExpr(EmptyShell Empty,
|
|
||||||
unsigned NumTemplateArgs)
|
|
||||||
: Expr(ConceptSpecializationExprClass, Empty), ConceptReference(),
|
|
||||||
NumTemplateArgs(NumTemplateArgs) { }
|
|
||||||
|
|
||||||
void ConceptSpecializationExpr::setTemplateArguments(
|
|
||||||
ArrayRef<TemplateArgument> Converted) {
|
|
||||||
assert(Converted.size() == NumTemplateArgs);
|
|
||||||
std::uninitialized_copy(Converted.begin(), Converted.end(),
|
|
||||||
getTrailingObjects<TemplateArgument>());
|
|
||||||
bool IsInstantiationDependent = false;
|
|
||||||
bool ContainsUnexpandedParameterPack = false;
|
|
||||||
for (const TemplateArgument& Arg : Converted) {
|
|
||||||
if (Arg.isInstantiationDependent())
|
|
||||||
IsInstantiationDependent = true;
|
|
||||||
if (Arg.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(const ASTContext &C,
|
|
||||||
NestedNameSpecifierLoc NNS,
|
|
||||||
SourceLocation TemplateKWLoc,
|
|
||||||
DeclarationNameInfo ConceptNameInfo,
|
|
||||||
NamedDecl *FoundDecl,
|
|
||||||
ConceptDecl *NamedConcept,
|
|
||||||
const ASTTemplateArgumentListInfo *ArgsAsWritten,
|
|
||||||
ArrayRef<TemplateArgument> ConvertedArgs,
|
|
||||||
const ConstraintSatisfaction *Satisfaction) {
|
|
||||||
void *Buffer = C.Allocate(totalSizeToAlloc<TemplateArgument>(
|
|
||||||
ConvertedArgs.size()));
|
|
||||||
return new (Buffer) ConceptSpecializationExpr(C, NNS, TemplateKWLoc,
|
|
||||||
ConceptNameInfo, FoundDecl,
|
|
||||||
NamedConcept, ArgsAsWritten,
|
|
||||||
ConvertedArgs, Satisfaction);
|
|
||||||
}
|
|
||||||
|
|
||||||
ConceptSpecializationExpr *
|
|
||||||
ConceptSpecializationExpr::Create(ASTContext &C, EmptyShell Empty,
|
|
||||||
unsigned NumTemplateArgs) {
|
|
||||||
void *Buffer = C.Allocate(totalSizeToAlloc<TemplateArgument>(
|
|
||||||
NumTemplateArgs));
|
|
||||||
return new (Buffer) ConceptSpecializationExpr(Empty, NumTemplateArgs);
|
|
||||||
}
|
|
||||||
|
@ -193,6 +193,7 @@ static Cl::Kinds ClassifyInternal(ASTContext &Ctx, const Expr *E) {
|
|||||||
case Expr::DesignatedInitUpdateExprClass:
|
case Expr::DesignatedInitUpdateExprClass:
|
||||||
case Expr::SourceLocExprClass:
|
case Expr::SourceLocExprClass:
|
||||||
case Expr::ConceptSpecializationExprClass:
|
case Expr::ConceptSpecializationExprClass:
|
||||||
|
case Expr::RequiresExprClass:
|
||||||
return Cl::CL_PRValue;
|
return Cl::CL_PRValue;
|
||||||
|
|
||||||
case Expr::ConstantExprClass:
|
case Expr::ConstantExprClass:
|
||||||
|
185
clang/lib/AST/ExprConcepts.cpp
Normal file
185
clang/lib/AST/ExprConcepts.cpp
Normal file
@ -0,0 +1,185 @@
|
|||||||
|
//===- ExprCXX.cpp - (C++) Expression AST Node Implementation -------------===//
|
||||||
|
//
|
||||||
|
// 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 implements the subclesses of Expr class declared in ExprCXX.h
|
||||||
|
//
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
|
||||||
|
#include "clang/AST/ExprConcepts.h"
|
||||||
|
#include "clang/AST/ASTContext.h"
|
||||||
|
#include "clang/AST/ASTConcept.h"
|
||||||
|
#include "clang/AST/Decl.h"
|
||||||
|
#include "clang/AST/DeclarationName.h"
|
||||||
|
#include "clang/AST/DeclTemplate.h"
|
||||||
|
#include "clang/AST/Expr.h"
|
||||||
|
#include "clang/AST/NestedNameSpecifier.h"
|
||||||
|
#include "clang/AST/TemplateBase.h"
|
||||||
|
#include "clang/AST/Type.h"
|
||||||
|
#include "clang/Basic/SourceLocation.h"
|
||||||
|
#include "llvm/Support/TrailingObjects.h"
|
||||||
|
#include <algorithm>
|
||||||
|
#include <utility>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
using namespace clang;
|
||||||
|
|
||||||
|
ConceptSpecializationExpr::ConceptSpecializationExpr(const ASTContext &C,
|
||||||
|
NestedNameSpecifierLoc NNS, SourceLocation TemplateKWLoc,
|
||||||
|
DeclarationNameInfo ConceptNameInfo, NamedDecl *FoundDecl,
|
||||||
|
ConceptDecl *NamedConcept, const ASTTemplateArgumentListInfo *ArgsAsWritten,
|
||||||
|
ArrayRef<TemplateArgument> ConvertedArgs,
|
||||||
|
const ConstraintSatisfaction *Satisfaction)
|
||||||
|
: Expr(ConceptSpecializationExprClass, C.BoolTy, VK_RValue, OK_Ordinary,
|
||||||
|
/*TypeDependent=*/false,
|
||||||
|
// All the flags below are set in setTemplateArguments.
|
||||||
|
/*ValueDependent=*/!Satisfaction, /*InstantiationDependent=*/false,
|
||||||
|
/*ContainsUnexpandedParameterPacks=*/false),
|
||||||
|
ConceptReference(NNS, TemplateKWLoc, ConceptNameInfo, FoundDecl,
|
||||||
|
NamedConcept, ArgsAsWritten),
|
||||||
|
NumTemplateArgs(ConvertedArgs.size()),
|
||||||
|
Satisfaction(Satisfaction ?
|
||||||
|
ASTConstraintSatisfaction::Create(C, *Satisfaction) :
|
||||||
|
nullptr) {
|
||||||
|
setTemplateArguments(ConvertedArgs);
|
||||||
|
}
|
||||||
|
|
||||||
|
ConceptSpecializationExpr::ConceptSpecializationExpr(EmptyShell Empty,
|
||||||
|
unsigned NumTemplateArgs)
|
||||||
|
: Expr(ConceptSpecializationExprClass, Empty), ConceptReference(),
|
||||||
|
NumTemplateArgs(NumTemplateArgs) { }
|
||||||
|
|
||||||
|
void ConceptSpecializationExpr::setTemplateArguments(
|
||||||
|
ArrayRef<TemplateArgument> Converted) {
|
||||||
|
assert(Converted.size() == NumTemplateArgs);
|
||||||
|
std::uninitialized_copy(Converted.begin(), Converted.end(),
|
||||||
|
getTrailingObjects<TemplateArgument>());
|
||||||
|
bool IsInstantiationDependent = false;
|
||||||
|
bool ContainsUnexpandedParameterPack = false;
|
||||||
|
for (const TemplateArgument& Arg : Converted) {
|
||||||
|
if (Arg.isInstantiationDependent())
|
||||||
|
IsInstantiationDependent = true;
|
||||||
|
if (Arg.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(const ASTContext &C,
|
||||||
|
NestedNameSpecifierLoc NNS,
|
||||||
|
SourceLocation TemplateKWLoc,
|
||||||
|
DeclarationNameInfo ConceptNameInfo,
|
||||||
|
NamedDecl *FoundDecl,
|
||||||
|
ConceptDecl *NamedConcept,
|
||||||
|
const ASTTemplateArgumentListInfo *ArgsAsWritten,
|
||||||
|
ArrayRef<TemplateArgument> ConvertedArgs,
|
||||||
|
const ConstraintSatisfaction *Satisfaction) {
|
||||||
|
void *Buffer = C.Allocate(totalSizeToAlloc<TemplateArgument>(
|
||||||
|
ConvertedArgs.size()));
|
||||||
|
return new (Buffer) ConceptSpecializationExpr(C, NNS, TemplateKWLoc,
|
||||||
|
ConceptNameInfo, FoundDecl,
|
||||||
|
NamedConcept, ArgsAsWritten,
|
||||||
|
ConvertedArgs, Satisfaction);
|
||||||
|
}
|
||||||
|
|
||||||
|
ConceptSpecializationExpr *
|
||||||
|
ConceptSpecializationExpr::Create(ASTContext &C, EmptyShell Empty,
|
||||||
|
unsigned NumTemplateArgs) {
|
||||||
|
void *Buffer = C.Allocate(totalSizeToAlloc<TemplateArgument>(
|
||||||
|
NumTemplateArgs));
|
||||||
|
return new (Buffer) ConceptSpecializationExpr(Empty, NumTemplateArgs);
|
||||||
|
}
|
||||||
|
|
||||||
|
const TypeConstraint *
|
||||||
|
concepts::ExprRequirement::ReturnTypeRequirement::getTypeConstraint() const {
|
||||||
|
assert(isTypeConstraint());
|
||||||
|
auto TPL =
|
||||||
|
TypeConstraintInfo.getPointer().get<TemplateParameterList *>();
|
||||||
|
return cast<TemplateTypeParmDecl>(TPL->getParam(0))
|
||||||
|
->getTypeConstraint();
|
||||||
|
}
|
||||||
|
|
||||||
|
RequiresExpr::RequiresExpr(ASTContext &C, SourceLocation RequiresKWLoc,
|
||||||
|
RequiresExprBodyDecl *Body,
|
||||||
|
ArrayRef<ParmVarDecl *> LocalParameters,
|
||||||
|
ArrayRef<concepts::Requirement *> Requirements,
|
||||||
|
SourceLocation RBraceLoc)
|
||||||
|
: Expr(RequiresExprClass, C.BoolTy, VK_RValue, OK_Ordinary,
|
||||||
|
/*TD=*/false, /*VD=*/false, /*ID=*/false,
|
||||||
|
/*ContainsUnexpandedParameterPack=*/false),
|
||||||
|
NumLocalParameters(LocalParameters.size()),
|
||||||
|
NumRequirements(Requirements.size()), Body(Body), RBraceLoc(RBraceLoc) {
|
||||||
|
RequiresExprBits.IsSatisfied = false;
|
||||||
|
RequiresExprBits.RequiresKWLoc = RequiresKWLoc;
|
||||||
|
bool Dependent = false;
|
||||||
|
bool ContainsUnexpandedParameterPack = false;
|
||||||
|
for (ParmVarDecl *P : LocalParameters) {
|
||||||
|
Dependent |= P->getType()->isInstantiationDependentType();
|
||||||
|
ContainsUnexpandedParameterPack |=
|
||||||
|
P->getType()->containsUnexpandedParameterPack();
|
||||||
|
}
|
||||||
|
RequiresExprBits.IsSatisfied = true;
|
||||||
|
for (concepts::Requirement *R : Requirements) {
|
||||||
|
Dependent |= R->isDependent();
|
||||||
|
ContainsUnexpandedParameterPack |= R->containsUnexpandedParameterPack();
|
||||||
|
if (!Dependent) {
|
||||||
|
RequiresExprBits.IsSatisfied = R->isSatisfied();
|
||||||
|
if (!RequiresExprBits.IsSatisfied)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
std::copy(LocalParameters.begin(), LocalParameters.end(),
|
||||||
|
getTrailingObjects<ParmVarDecl *>());
|
||||||
|
std::copy(Requirements.begin(), Requirements.end(),
|
||||||
|
getTrailingObjects<concepts::Requirement *>());
|
||||||
|
RequiresExprBits.IsSatisfied |= Dependent;
|
||||||
|
setValueDependent(Dependent);
|
||||||
|
setInstantiationDependent(Dependent);
|
||||||
|
setContainsUnexpandedParameterPack(ContainsUnexpandedParameterPack);
|
||||||
|
}
|
||||||
|
|
||||||
|
RequiresExpr::RequiresExpr(ASTContext &C, EmptyShell Empty,
|
||||||
|
unsigned NumLocalParameters,
|
||||||
|
unsigned NumRequirements)
|
||||||
|
: Expr(RequiresExprClass, Empty), NumLocalParameters(NumLocalParameters),
|
||||||
|
NumRequirements(NumRequirements) { }
|
||||||
|
|
||||||
|
RequiresExpr *
|
||||||
|
RequiresExpr::Create(ASTContext &C, SourceLocation RequiresKWLoc,
|
||||||
|
RequiresExprBodyDecl *Body,
|
||||||
|
ArrayRef<ParmVarDecl *> LocalParameters,
|
||||||
|
ArrayRef<concepts::Requirement *> Requirements,
|
||||||
|
SourceLocation RBraceLoc) {
|
||||||
|
void *Mem =
|
||||||
|
C.Allocate(totalSizeToAlloc<ParmVarDecl *, concepts::Requirement *>(
|
||||||
|
LocalParameters.size(), Requirements.size()),
|
||||||
|
alignof(RequiresExpr));
|
||||||
|
return new (Mem) RequiresExpr(C, RequiresKWLoc, Body, LocalParameters,
|
||||||
|
Requirements, RBraceLoc);
|
||||||
|
}
|
||||||
|
|
||||||
|
RequiresExpr *
|
||||||
|
RequiresExpr::Create(ASTContext &C, EmptyShell Empty,
|
||||||
|
unsigned NumLocalParameters, unsigned NumRequirements) {
|
||||||
|
void *Mem =
|
||||||
|
C.Allocate(totalSizeToAlloc<ParmVarDecl *, concepts::Requirement *>(
|
||||||
|
NumLocalParameters, NumRequirements),
|
||||||
|
alignof(RequiresExpr));
|
||||||
|
return new (Mem) RequiresExpr(C, Empty, NumLocalParameters, NumRequirements);
|
||||||
|
}
|
@ -9912,6 +9912,7 @@ public:
|
|||||||
bool VisitSizeOfPackExpr(const SizeOfPackExpr *E);
|
bool VisitSizeOfPackExpr(const SizeOfPackExpr *E);
|
||||||
bool VisitSourceLocExpr(const SourceLocExpr *E);
|
bool VisitSourceLocExpr(const SourceLocExpr *E);
|
||||||
bool VisitConceptSpecializationExpr(const ConceptSpecializationExpr *E);
|
bool VisitConceptSpecializationExpr(const ConceptSpecializationExpr *E);
|
||||||
|
bool VisitRequiresExpr(const RequiresExpr *E);
|
||||||
// FIXME: Missing: array subscript of vector, member of vector
|
// FIXME: Missing: array subscript of vector, member of vector
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -12524,6 +12525,9 @@ bool IntExprEvaluator::VisitConceptSpecializationExpr(
|
|||||||
return Success(E->isSatisfied(), E);
|
return Success(E->isSatisfied(), E);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool IntExprEvaluator::VisitRequiresExpr(const RequiresExpr *E) {
|
||||||
|
return Success(E->isSatisfied(), E);
|
||||||
|
}
|
||||||
|
|
||||||
bool FixedPointExprEvaluator::VisitUnaryOperator(const UnaryOperator *E) {
|
bool FixedPointExprEvaluator::VisitUnaryOperator(const UnaryOperator *E) {
|
||||||
switch (E->getOpcode()) {
|
switch (E->getOpcode()) {
|
||||||
@ -14182,6 +14186,7 @@ static ICEDiag CheckICE(const Expr* E, const ASTContext &Ctx) {
|
|||||||
case Expr::CXXScalarValueInitExprClass:
|
case Expr::CXXScalarValueInitExprClass:
|
||||||
case Expr::TypeTraitExprClass:
|
case Expr::TypeTraitExprClass:
|
||||||
case Expr::ConceptSpecializationExprClass:
|
case Expr::ConceptSpecializationExprClass:
|
||||||
|
case Expr::RequiresExprClass:
|
||||||
case Expr::ArrayTypeTraitExprClass:
|
case Expr::ArrayTypeTraitExprClass:
|
||||||
case Expr::ExpressionTraitExprClass:
|
case Expr::ExpressionTraitExprClass:
|
||||||
case Expr::CXXNoexceptExprClass:
|
case Expr::CXXNoexceptExprClass:
|
||||||
|
@ -22,6 +22,7 @@
|
|||||||
#include "clang/AST/DeclOpenMP.h"
|
#include "clang/AST/DeclOpenMP.h"
|
||||||
#include "clang/AST/DeclTemplate.h"
|
#include "clang/AST/DeclTemplate.h"
|
||||||
#include "clang/AST/Expr.h"
|
#include "clang/AST/Expr.h"
|
||||||
|
#include "clang/AST/ExprConcepts.h"
|
||||||
#include "clang/AST/ExprCXX.h"
|
#include "clang/AST/ExprCXX.h"
|
||||||
#include "clang/AST/ExprObjC.h"
|
#include "clang/AST/ExprObjC.h"
|
||||||
#include "clang/AST/TypeLoc.h"
|
#include "clang/AST/TypeLoc.h"
|
||||||
@ -3668,6 +3669,7 @@ recurse:
|
|||||||
case Expr::ConvertVectorExprClass:
|
case Expr::ConvertVectorExprClass:
|
||||||
case Expr::StmtExprClass:
|
case Expr::StmtExprClass:
|
||||||
case Expr::TypeTraitExprClass:
|
case Expr::TypeTraitExprClass:
|
||||||
|
case Expr::RequiresExprClass:
|
||||||
case Expr::ArrayTypeTraitExprClass:
|
case Expr::ArrayTypeTraitExprClass:
|
||||||
case Expr::ExpressionTraitExprClass:
|
case Expr::ExpressionTraitExprClass:
|
||||||
case Expr::VAArgExprClass:
|
case Expr::VAArgExprClass:
|
||||||
|
@ -16,6 +16,7 @@
|
|||||||
#include "clang/AST/Decl.h"
|
#include "clang/AST/Decl.h"
|
||||||
#include "clang/AST/DeclGroup.h"
|
#include "clang/AST/DeclGroup.h"
|
||||||
#include "clang/AST/Expr.h"
|
#include "clang/AST/Expr.h"
|
||||||
|
#include "clang/AST/ExprConcepts.h"
|
||||||
#include "clang/AST/ExprCXX.h"
|
#include "clang/AST/ExprCXX.h"
|
||||||
#include "clang/AST/ExprObjC.h"
|
#include "clang/AST/ExprObjC.h"
|
||||||
#include "clang/AST/ExprOpenMP.h"
|
#include "clang/AST/ExprOpenMP.h"
|
||||||
|
@ -2269,6 +2269,60 @@ void StmtPrinter::VisitConceptSpecializationExpr(ConceptSpecializationExpr *E) {
|
|||||||
Policy);
|
Policy);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void StmtPrinter::VisitRequiresExpr(RequiresExpr *E) {
|
||||||
|
OS << "requires ";
|
||||||
|
auto LocalParameters = E->getLocalParameters();
|
||||||
|
if (!LocalParameters.empty()) {
|
||||||
|
OS << "(";
|
||||||
|
for (ParmVarDecl *LocalParam : LocalParameters) {
|
||||||
|
PrintRawDecl(LocalParam);
|
||||||
|
if (LocalParam != LocalParameters.back())
|
||||||
|
OS << ", ";
|
||||||
|
}
|
||||||
|
|
||||||
|
OS << ") ";
|
||||||
|
}
|
||||||
|
OS << "{ ";
|
||||||
|
auto Requirements = E->getRequirements();
|
||||||
|
for (concepts::Requirement *Req : Requirements) {
|
||||||
|
if (auto *TypeReq = dyn_cast<concepts::TypeRequirement>(Req)) {
|
||||||
|
if (TypeReq->isSubstitutionFailure())
|
||||||
|
OS << "<<error-type>>";
|
||||||
|
else
|
||||||
|
TypeReq->getType()->getType().print(OS, Policy);
|
||||||
|
} else if (auto *ExprReq = dyn_cast<concepts::ExprRequirement>(Req)) {
|
||||||
|
if (ExprReq->isCompound())
|
||||||
|
OS << "{ ";
|
||||||
|
if (ExprReq->isExprSubstitutionFailure())
|
||||||
|
OS << "<<error-expression>>";
|
||||||
|
else
|
||||||
|
PrintExpr(ExprReq->getExpr());
|
||||||
|
if (ExprReq->isCompound()) {
|
||||||
|
OS << " }";
|
||||||
|
if (ExprReq->getNoexceptLoc().isValid())
|
||||||
|
OS << " noexcept";
|
||||||
|
const auto &RetReq = ExprReq->getReturnTypeRequirement();
|
||||||
|
if (!RetReq.isEmpty()) {
|
||||||
|
OS << " -> ";
|
||||||
|
if (RetReq.isSubstitutionFailure())
|
||||||
|
OS << "<<error-type>>";
|
||||||
|
else if (RetReq.isTypeConstraint())
|
||||||
|
RetReq.getTypeConstraint()->print(OS, Policy);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
auto *NestedReq = cast<concepts::NestedRequirement>(Req);
|
||||||
|
OS << "requires ";
|
||||||
|
if (NestedReq->isSubstitutionFailure())
|
||||||
|
OS << "<<error-expression>>";
|
||||||
|
else
|
||||||
|
PrintExpr(NestedReq->getConstraintExpr());
|
||||||
|
}
|
||||||
|
OS << "; ";
|
||||||
|
}
|
||||||
|
OS << "}";
|
||||||
|
}
|
||||||
|
|
||||||
// C++ Coroutines TS
|
// C++ Coroutines TS
|
||||||
|
|
||||||
void StmtPrinter::VisitCoroutineBodyStmt(CoroutineBodyStmt *S) {
|
void StmtPrinter::VisitCoroutineBodyStmt(CoroutineBodyStmt *S) {
|
||||||
|
@ -1340,6 +1340,49 @@ void StmtProfiler::VisitConceptSpecializationExpr(
|
|||||||
VisitTemplateArgument(Arg);
|
VisitTemplateArgument(Arg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void StmtProfiler::VisitRequiresExpr(const RequiresExpr *S) {
|
||||||
|
VisitExpr(S);
|
||||||
|
ID.AddInteger(S->getLocalParameters().size());
|
||||||
|
for (ParmVarDecl *LocalParam : S->getLocalParameters())
|
||||||
|
VisitDecl(LocalParam);
|
||||||
|
ID.AddInteger(S->getRequirements().size());
|
||||||
|
for (concepts::Requirement *Req : S->getRequirements()) {
|
||||||
|
if (auto *TypeReq = dyn_cast<concepts::TypeRequirement>(Req)) {
|
||||||
|
ID.AddInteger(concepts::Requirement::RK_Type);
|
||||||
|
ID.AddBoolean(TypeReq->isSubstitutionFailure());
|
||||||
|
if (!TypeReq->isSubstitutionFailure())
|
||||||
|
VisitType(TypeReq->getType()->getType());
|
||||||
|
} else if (auto *ExprReq = dyn_cast<concepts::ExprRequirement>(Req)) {
|
||||||
|
ID.AddInteger(concepts::Requirement::RK_Compound);
|
||||||
|
ID.AddBoolean(ExprReq->isExprSubstitutionFailure());
|
||||||
|
if (!ExprReq->isExprSubstitutionFailure())
|
||||||
|
Visit(ExprReq->getExpr());
|
||||||
|
// C++2a [expr.prim.req.compound]p1 Example:
|
||||||
|
// [...] The compound-requirement in C1 requires that x++ is a valid
|
||||||
|
// expression. It is equivalent to the simple-requirement x++; [...]
|
||||||
|
// We therefore do not profile isSimple() here.
|
||||||
|
ID.AddBoolean(ExprReq->getNoexceptLoc().isValid());
|
||||||
|
const concepts::ExprRequirement::ReturnTypeRequirement &RetReq =
|
||||||
|
ExprReq->getReturnTypeRequirement();
|
||||||
|
if (RetReq.isEmpty()) {
|
||||||
|
ID.AddInteger(0);
|
||||||
|
} else if (RetReq.isTypeConstraint()) {
|
||||||
|
ID.AddInteger(1);
|
||||||
|
Visit(RetReq.getTypeConstraint()->getImmediatelyDeclaredConstraint());
|
||||||
|
} else {
|
||||||
|
assert(RetReq.isSubstitutionFailure());
|
||||||
|
ID.AddInteger(2);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ID.AddInteger(concepts::Requirement::RK_Nested);
|
||||||
|
auto *NestedReq = cast<concepts::NestedRequirement>(Req);
|
||||||
|
ID.AddBoolean(NestedReq->isSubstitutionFailure());
|
||||||
|
if (!NestedReq->isSubstitutionFailure())
|
||||||
|
Visit(NestedReq->getConstraintExpr());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static Stmt::StmtClass DecodeOperatorCall(const CXXOperatorCallExpr *S,
|
static Stmt::StmtClass DecodeOperatorCall(const CXXOperatorCallExpr *S,
|
||||||
UnaryOperatorKind &UnaryOp,
|
UnaryOperatorKind &UnaryOp,
|
||||||
BinaryOperatorKind &BinaryOp) {
|
BinaryOperatorKind &BinaryOp) {
|
||||||
|
@ -111,6 +111,7 @@ void CodeGenFunction::EmitDecl(const Decl &D) {
|
|||||||
case Decl::Empty:
|
case Decl::Empty:
|
||||||
case Decl::Concept:
|
case Decl::Concept:
|
||||||
case Decl::LifetimeExtendedTemporary:
|
case Decl::LifetimeExtendedTemporary:
|
||||||
|
case Decl::RequiresExprBody:
|
||||||
// None of these decls require codegen support.
|
// None of these decls require codegen support.
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
@ -680,6 +680,10 @@ public:
|
|||||||
return Builder.getInt1(E->isSatisfied());
|
return Builder.getInt1(E->isSatisfied());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Value *VisitRequiresExpr(const RequiresExpr *E) {
|
||||||
|
return Builder.getInt1(E->isSatisfied());
|
||||||
|
}
|
||||||
|
|
||||||
Value *VisitArrayTypeTraitExpr(const ArrayTypeTraitExpr *E) {
|
Value *VisitArrayTypeTraitExpr(const ArrayTypeTraitExpr *E) {
|
||||||
return llvm::ConstantInt::get(Builder.getInt32Ty(), E->getValue());
|
return llvm::ConstantInt::get(Builder.getInt32Ty(), E->getValue());
|
||||||
}
|
}
|
||||||
|
@ -429,6 +429,10 @@ private:
|
|||||||
return "ConstraintNormalization";
|
return "ConstraintNormalization";
|
||||||
case CodeSynthesisContext::ParameterMappingSubstitution:
|
case CodeSynthesisContext::ParameterMappingSubstitution:
|
||||||
return "ParameterMappingSubstitution";
|
return "ParameterMappingSubstitution";
|
||||||
|
case CodeSynthesisContext::RequirementInstantiation:
|
||||||
|
return "RequirementInstantiation";
|
||||||
|
case CodeSynthesisContext::NestedRequirementConstraintsCheck:
|
||||||
|
return "NestedRequirementConstraintsCheck";
|
||||||
}
|
}
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
@ -385,6 +385,9 @@ static void InitializeStandardPredefinedMacros(const TargetInfo &TI,
|
|||||||
else
|
else
|
||||||
Builder.defineMacro("__cplusplus", "199711L");
|
Builder.defineMacro("__cplusplus", "199711L");
|
||||||
|
|
||||||
|
if (LangOpts.ConceptsTS)
|
||||||
|
Builder.defineMacro("__cpp_concepts", "201707L");
|
||||||
|
|
||||||
// C++1z [cpp.predefined]p1:
|
// C++1z [cpp.predefined]p1:
|
||||||
// An integer literal of type std::size_t whose value is the alignment
|
// An integer literal of type std::size_t whose value is the alignment
|
||||||
// guaranteed by a call to operator new(std::size_t)
|
// guaranteed by a call to operator new(std::size_t)
|
||||||
|
@ -6366,7 +6366,7 @@ void Parser::ParseFunctionDeclarator(Declarator &D,
|
|||||||
ProhibitAttributes(FnAttrs);
|
ProhibitAttributes(FnAttrs);
|
||||||
} else {
|
} else {
|
||||||
if (Tok.isNot(tok::r_paren))
|
if (Tok.isNot(tok::r_paren))
|
||||||
ParseParameterDeclarationClause(D, FirstArgAttrs, ParamInfo,
|
ParseParameterDeclarationClause(D.getContext(), FirstArgAttrs, ParamInfo,
|
||||||
EllipsisLoc);
|
EllipsisLoc);
|
||||||
else if (RequiresArg)
|
else if (RequiresArg)
|
||||||
Diag(Tok, diag::err_argument_required_after_attribute);
|
Diag(Tok, diag::err_argument_required_after_attribute);
|
||||||
@ -6584,9 +6584,9 @@ void Parser::ParseFunctionDeclaratorIdentifierList(
|
|||||||
/// after the opening parenthesis. This function will not parse a K&R-style
|
/// after the opening parenthesis. This function will not parse a K&R-style
|
||||||
/// identifier list.
|
/// identifier list.
|
||||||
///
|
///
|
||||||
/// D is the declarator being parsed. If FirstArgAttrs is non-null, then the
|
/// DeclContext is the context of the declarator being parsed. If FirstArgAttrs
|
||||||
/// caller parsed those arguments immediately after the open paren - they should
|
/// is non-null, then the caller parsed those attributes immediately after the
|
||||||
/// be considered to be part of the first parameter.
|
/// open paren - they should be considered to be part of the first parameter.
|
||||||
///
|
///
|
||||||
/// After returning, ParamInfo will hold the parsed parameters. EllipsisLoc will
|
/// After returning, ParamInfo will hold the parsed parameters. EllipsisLoc will
|
||||||
/// be the location of the ellipsis, if any was parsed.
|
/// be the location of the ellipsis, if any was parsed.
|
||||||
@ -6612,7 +6612,7 @@ void Parser::ParseFunctionDeclaratorIdentifierList(
|
|||||||
/// [C++11] attribute-specifier-seq parameter-declaration
|
/// [C++11] attribute-specifier-seq parameter-declaration
|
||||||
///
|
///
|
||||||
void Parser::ParseParameterDeclarationClause(
|
void Parser::ParseParameterDeclarationClause(
|
||||||
Declarator &D,
|
DeclaratorContext DeclaratorContext,
|
||||||
ParsedAttributes &FirstArgAttrs,
|
ParsedAttributes &FirstArgAttrs,
|
||||||
SmallVectorImpl<DeclaratorChunk::ParamInfo> &ParamInfo,
|
SmallVectorImpl<DeclaratorChunk::ParamInfo> &ParamInfo,
|
||||||
SourceLocation &EllipsisLoc) {
|
SourceLocation &EllipsisLoc) {
|
||||||
@ -6661,9 +6661,11 @@ void Parser::ParseParameterDeclarationClause(
|
|||||||
// "LambdaExprParameterContext", because we must accept either
|
// "LambdaExprParameterContext", because we must accept either
|
||||||
// 'declarator' or 'abstract-declarator' here.
|
// 'declarator' or 'abstract-declarator' here.
|
||||||
Declarator ParmDeclarator(
|
Declarator ParmDeclarator(
|
||||||
DS, D.getContext() == DeclaratorContext::LambdaExprContext
|
DS, DeclaratorContext == DeclaratorContext::RequiresExprContext
|
||||||
? DeclaratorContext::LambdaExprParameterContext
|
? DeclaratorContext::RequiresExprContext
|
||||||
: DeclaratorContext::PrototypeContext);
|
: DeclaratorContext == DeclaratorContext::LambdaExprContext
|
||||||
|
? DeclaratorContext::LambdaExprParameterContext
|
||||||
|
: DeclaratorContext::PrototypeContext);
|
||||||
ParseDeclarator(ParmDeclarator);
|
ParseDeclarator(ParmDeclarator);
|
||||||
|
|
||||||
// Parse GNU attributes, if present.
|
// Parse GNU attributes, if present.
|
||||||
@ -6717,7 +6719,7 @@ void Parser::ParseParameterDeclarationClause(
|
|||||||
SourceLocation EqualLoc = Tok.getLocation();
|
SourceLocation EqualLoc = Tok.getLocation();
|
||||||
|
|
||||||
// Parse the default argument
|
// Parse the default argument
|
||||||
if (D.getContext() == DeclaratorContext::MemberContext) {
|
if (DeclaratorContext == DeclaratorContext::MemberContext) {
|
||||||
// If we're inside a class definition, cache the tokens
|
// If we're inside a class definition, cache the tokens
|
||||||
// corresponding to the default argument. We'll actually parse
|
// corresponding to the default argument. We'll actually parse
|
||||||
// them when we see the end of the class definition.
|
// them when we see the end of the class definition.
|
||||||
|
@ -756,6 +756,7 @@ class CastExpressionIdValidator final : public CorrectionCandidateCallback {
|
|||||||
/// [C++11] user-defined-literal
|
/// [C++11] user-defined-literal
|
||||||
/// '(' expression ')'
|
/// '(' expression ')'
|
||||||
/// [C11] generic-selection
|
/// [C11] generic-selection
|
||||||
|
/// [C++2a] requires-expression
|
||||||
/// '__func__' [C99 6.4.2.2]
|
/// '__func__' [C99 6.4.2.2]
|
||||||
/// [GNU] '__FUNCTION__'
|
/// [GNU] '__FUNCTION__'
|
||||||
/// [MS] '__FUNCDNAME__'
|
/// [MS] '__FUNCDNAME__'
|
||||||
@ -1601,6 +1602,9 @@ ExprResult Parser::ParseCastExpression(CastParseKind ParseKind,
|
|||||||
*NotPrimaryExpression = true;
|
*NotPrimaryExpression = true;
|
||||||
return ParseCXXDeleteExpression(false, Tok.getLocation());
|
return ParseCXXDeleteExpression(false, Tok.getLocation());
|
||||||
|
|
||||||
|
case tok::kw_requires: // [C++2a] requires-expression
|
||||||
|
return ParseRequiresExpression();
|
||||||
|
|
||||||
case tok::kw_noexcept: { // [C++0x] 'noexcept' '(' expression ')'
|
case tok::kw_noexcept: { // [C++0x] 'noexcept' '(' expression ')'
|
||||||
if (NotPrimaryExpression)
|
if (NotPrimaryExpression)
|
||||||
*NotPrimaryExpression = true;
|
*NotPrimaryExpression = true;
|
||||||
|
@ -11,7 +11,9 @@
|
|||||||
//===----------------------------------------------------------------------===//
|
//===----------------------------------------------------------------------===//
|
||||||
#include "clang/Parse/Parser.h"
|
#include "clang/Parse/Parser.h"
|
||||||
#include "clang/AST/ASTContext.h"
|
#include "clang/AST/ASTContext.h"
|
||||||
|
#include "clang/AST/Decl.h"
|
||||||
#include "clang/AST/DeclTemplate.h"
|
#include "clang/AST/DeclTemplate.h"
|
||||||
|
#include "clang/AST/ExprCXX.h"
|
||||||
#include "clang/Basic/PrettyStackTrace.h"
|
#include "clang/Basic/PrettyStackTrace.h"
|
||||||
#include "clang/Lex/LiteralSupport.h"
|
#include "clang/Lex/LiteralSupport.h"
|
||||||
#include "clang/Parse/ParseDiagnostic.h"
|
#include "clang/Parse/ParseDiagnostic.h"
|
||||||
@ -1299,9 +1301,9 @@ ExprResult Parser::ParseLambdaExpressionAfterIntroducer(
|
|||||||
Actions.RecordParsingTemplateParameterDepth(
|
Actions.RecordParsingTemplateParameterDepth(
|
||||||
CurTemplateDepthTracker.getOriginalDepth());
|
CurTemplateDepthTracker.getOriginalDepth());
|
||||||
|
|
||||||
ParseParameterDeclarationClause(D, Attr, ParamInfo, EllipsisLoc);
|
ParseParameterDeclarationClause(D.getContext(), Attr, ParamInfo,
|
||||||
|
EllipsisLoc);
|
||||||
// For a generic lambda, each 'auto' within the parameter declaration
|
// For a generic lambda, each 'auto' within the parameter declaration
|
||||||
// clause creates a template type parameter, so increment the depth.
|
// clause creates a template type parameter, so increment the depth.
|
||||||
// If we've parsed any explicit template parameters, then the depth will
|
// If we've parsed any explicit template parameters, then the depth will
|
||||||
// have already been incremented. So we make sure that at most a single
|
// have already been incremented. So we make sure that at most a single
|
||||||
@ -3255,6 +3257,324 @@ Parser::ParseCXXDeleteExpression(bool UseGlobal, SourceLocation Start) {
|
|||||||
return Actions.ActOnCXXDelete(Start, UseGlobal, ArrayDelete, Operand.get());
|
return Actions.ActOnCXXDelete(Start, UseGlobal, ArrayDelete, Operand.get());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// ParseRequiresExpression - Parse a C++2a requires-expression.
|
||||||
|
/// C++2a [expr.prim.req]p1
|
||||||
|
/// A requires-expression provides a concise way to express requirements on
|
||||||
|
/// template arguments. A requirement is one that can be checked by name
|
||||||
|
/// lookup (6.4) or by checking properties of types and expressions.
|
||||||
|
///
|
||||||
|
/// requires-expression:
|
||||||
|
/// 'requires' requirement-parameter-list[opt] requirement-body
|
||||||
|
///
|
||||||
|
/// requirement-parameter-list:
|
||||||
|
/// '(' parameter-declaration-clause[opt] ')'
|
||||||
|
///
|
||||||
|
/// requirement-body:
|
||||||
|
/// '{' requirement-seq '}'
|
||||||
|
///
|
||||||
|
/// requirement-seq:
|
||||||
|
/// requirement
|
||||||
|
/// requirement-seq requirement
|
||||||
|
///
|
||||||
|
/// requirement:
|
||||||
|
/// simple-requirement
|
||||||
|
/// type-requirement
|
||||||
|
/// compound-requirement
|
||||||
|
/// nested-requirement
|
||||||
|
ExprResult Parser::ParseRequiresExpression() {
|
||||||
|
assert(Tok.is(tok::kw_requires) && "Expected 'requires' keyword");
|
||||||
|
SourceLocation RequiresKWLoc = ConsumeToken(); // Consume 'requires'
|
||||||
|
|
||||||
|
llvm::SmallVector<ParmVarDecl *, 2> LocalParameterDecls;
|
||||||
|
if (Tok.is(tok::l_paren)) {
|
||||||
|
// requirement parameter list is present.
|
||||||
|
ParseScope LocalParametersScope(this, Scope::FunctionPrototypeScope |
|
||||||
|
Scope::DeclScope);
|
||||||
|
BalancedDelimiterTracker Parens(*this, tok::l_paren);
|
||||||
|
Parens.consumeOpen();
|
||||||
|
if (!Tok.is(tok::r_paren)) {
|
||||||
|
ParsedAttributes FirstArgAttrs(getAttrFactory());
|
||||||
|
SourceLocation EllipsisLoc;
|
||||||
|
llvm::SmallVector<DeclaratorChunk::ParamInfo, 2> LocalParameters;
|
||||||
|
DiagnosticErrorTrap Trap(Diags);
|
||||||
|
ParseParameterDeclarationClause(DeclaratorContext::RequiresExprContext,
|
||||||
|
FirstArgAttrs, LocalParameters,
|
||||||
|
EllipsisLoc);
|
||||||
|
if (EllipsisLoc.isValid())
|
||||||
|
Diag(EllipsisLoc, diag::err_requires_expr_parameter_list_ellipsis);
|
||||||
|
for (auto &ParamInfo : LocalParameters)
|
||||||
|
LocalParameterDecls.push_back(cast<ParmVarDecl>(ParamInfo.Param));
|
||||||
|
if (Trap.hasErrorOccurred())
|
||||||
|
SkipUntil(tok::r_paren, StopBeforeMatch);
|
||||||
|
}
|
||||||
|
Parens.consumeClose();
|
||||||
|
}
|
||||||
|
|
||||||
|
BalancedDelimiterTracker Braces(*this, tok::l_brace);
|
||||||
|
if (Braces.expectAndConsume())
|
||||||
|
return ExprError();
|
||||||
|
|
||||||
|
// Start of requirement list
|
||||||
|
llvm::SmallVector<concepts::Requirement *, 2> Requirements;
|
||||||
|
|
||||||
|
// C++2a [expr.prim.req]p2
|
||||||
|
// Expressions appearing within a requirement-body are unevaluated operands.
|
||||||
|
EnterExpressionEvaluationContext Ctx(
|
||||||
|
Actions, Sema::ExpressionEvaluationContext::Unevaluated);
|
||||||
|
|
||||||
|
ParseScope BodyScope(this, Scope::DeclScope);
|
||||||
|
RequiresExprBodyDecl *Body = Actions.ActOnStartRequiresExpr(
|
||||||
|
RequiresKWLoc, LocalParameterDecls, getCurScope());
|
||||||
|
|
||||||
|
if (Tok.is(tok::r_brace)) {
|
||||||
|
// Grammar does not allow an empty body.
|
||||||
|
// requirement-body:
|
||||||
|
// { requirement-seq }
|
||||||
|
// requirement-seq:
|
||||||
|
// requirement
|
||||||
|
// requirement-seq requirement
|
||||||
|
Diag(Tok, diag::err_empty_requires_expr);
|
||||||
|
// Continue anyway and produce a requires expr with no requirements.
|
||||||
|
} else {
|
||||||
|
while (!Tok.is(tok::r_brace)) {
|
||||||
|
switch (Tok.getKind()) {
|
||||||
|
case tok::l_brace: {
|
||||||
|
// Compound requirement
|
||||||
|
// C++ [expr.prim.req.compound]
|
||||||
|
// compound-requirement:
|
||||||
|
// '{' expression '}' 'noexcept'[opt]
|
||||||
|
// return-type-requirement[opt] ';'
|
||||||
|
// return-type-requirement:
|
||||||
|
// trailing-return-type
|
||||||
|
// '->' cv-qualifier-seq[opt] constrained-parameter
|
||||||
|
// cv-qualifier-seq[opt] abstract-declarator[opt]
|
||||||
|
BalancedDelimiterTracker ExprBraces(*this, tok::l_brace);
|
||||||
|
ExprBraces.consumeOpen();
|
||||||
|
ExprResult Expression =
|
||||||
|
Actions.CorrectDelayedTyposInExpr(ParseExpression());
|
||||||
|
if (!Expression.isUsable()) {
|
||||||
|
ExprBraces.skipToEnd();
|
||||||
|
SkipUntil(tok::semi, tok::r_brace, SkipUntilFlags::StopBeforeMatch);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (ExprBraces.consumeClose())
|
||||||
|
ExprBraces.skipToEnd();
|
||||||
|
|
||||||
|
concepts::Requirement *Req = nullptr;
|
||||||
|
SourceLocation NoexceptLoc;
|
||||||
|
TryConsumeToken(tok::kw_noexcept, NoexceptLoc);
|
||||||
|
if (Tok.is(tok::semi)) {
|
||||||
|
Req = Actions.ActOnCompoundRequirement(Expression.get(), NoexceptLoc);
|
||||||
|
if (Req)
|
||||||
|
Requirements.push_back(Req);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (!TryConsumeToken(tok::arrow))
|
||||||
|
// User probably forgot the arrow, remind them and try to continue.
|
||||||
|
Diag(Tok, diag::err_requires_expr_missing_arrow)
|
||||||
|
<< FixItHint::CreateInsertion(Tok.getLocation(), "->");
|
||||||
|
// Try to parse a 'type-constraint'
|
||||||
|
CXXScopeSpec SS;
|
||||||
|
if (ParseOptionalCXXScopeSpecifier(SS, ParsedType(),
|
||||||
|
/*EnteringContext=*/false,
|
||||||
|
/*MayBePseudoDestructor=*/nullptr,
|
||||||
|
// If this is not a type-constraint,
|
||||||
|
// then this scope-spec is part of
|
||||||
|
// the typename of a non-type
|
||||||
|
// template parameter
|
||||||
|
/*IsTypename=*/true,
|
||||||
|
/*LastII=*/nullptr,
|
||||||
|
// We won't find concepts in
|
||||||
|
// non-namespaces anyway, so might as
|
||||||
|
// well parse this correctly for
|
||||||
|
// possible type names.
|
||||||
|
/*OnlyNamespace=*/false,
|
||||||
|
/*SuppressDiagnostic=*/true)) {
|
||||||
|
SkipUntil(tok::semi, tok::r_brace, SkipUntilFlags::StopBeforeMatch);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (TryAnnotateTypeConstraint()) {
|
||||||
|
SkipUntil(tok::semi, tok::r_brace, SkipUntilFlags::StopBeforeMatch);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (!isTypeConstraintAnnotation()) {
|
||||||
|
Diag(Tok, diag::err_requires_expr_expected_type_constraint);
|
||||||
|
SkipUntil(tok::semi, tok::r_brace, SkipUntilFlags::StopBeforeMatch);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (Tok.is(tok::annot_cxxscope))
|
||||||
|
ConsumeAnnotationToken();
|
||||||
|
|
||||||
|
Req = Actions.ActOnCompoundRequirement(
|
||||||
|
Expression.get(), NoexceptLoc, SS, takeTemplateIdAnnotation(Tok),
|
||||||
|
TemplateParameterDepth);
|
||||||
|
ConsumeAnnotationToken();
|
||||||
|
if (Req)
|
||||||
|
Requirements.push_back(Req);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default: {
|
||||||
|
bool PossibleRequiresExprInSimpleRequirement = false;
|
||||||
|
if (Tok.is(tok::kw_requires)) {
|
||||||
|
auto IsNestedRequirement = [&] {
|
||||||
|
RevertingTentativeParsingAction TPA(*this);
|
||||||
|
ConsumeToken(); // 'requires'
|
||||||
|
if (Tok.is(tok::l_brace))
|
||||||
|
// This is a requires expression
|
||||||
|
// requires (T t) {
|
||||||
|
// requires { t++; };
|
||||||
|
// ... ^
|
||||||
|
// }
|
||||||
|
return false;
|
||||||
|
if (Tok.is(tok::l_paren)) {
|
||||||
|
// This might be the parameter list of a requires expression
|
||||||
|
ConsumeParen();
|
||||||
|
auto Res = TryParseParameterDeclarationClause();
|
||||||
|
if (Res != TPResult::False) {
|
||||||
|
// Skip to the closing parenthesis
|
||||||
|
// FIXME: Don't traverse these tokens twice (here and in
|
||||||
|
// TryParseParameterDeclarationClause).
|
||||||
|
unsigned Depth = 1;
|
||||||
|
while (Depth != 0) {
|
||||||
|
if (Tok.is(tok::l_paren))
|
||||||
|
Depth++;
|
||||||
|
else if (Tok.is(tok::r_paren))
|
||||||
|
Depth--;
|
||||||
|
ConsumeAnyToken();
|
||||||
|
}
|
||||||
|
// requires (T t) {
|
||||||
|
// requires () ?
|
||||||
|
// ... ^
|
||||||
|
// - OR -
|
||||||
|
// requires (int x) ?
|
||||||
|
// ... ^
|
||||||
|
// }
|
||||||
|
if (Tok.is(tok::l_brace))
|
||||||
|
// requires (...) {
|
||||||
|
// ^ - a requires expression as a
|
||||||
|
// simple-requirement.
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
if (IsNestedRequirement()) {
|
||||||
|
ConsumeToken();
|
||||||
|
// Nested requirement
|
||||||
|
// C++ [expr.prim.req.nested]
|
||||||
|
// nested-requirement:
|
||||||
|
// 'requires' constraint-expression ';'
|
||||||
|
ExprResult ConstraintExpr =
|
||||||
|
Actions.CorrectDelayedTyposInExpr(ParseConstraintExpression());
|
||||||
|
if (ConstraintExpr.isInvalid() || !ConstraintExpr.isUsable()) {
|
||||||
|
SkipUntil(tok::semi, tok::r_brace,
|
||||||
|
SkipUntilFlags::StopBeforeMatch);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (auto *Req =
|
||||||
|
Actions.ActOnNestedRequirement(ConstraintExpr.get()))
|
||||||
|
Requirements.push_back(Req);
|
||||||
|
else {
|
||||||
|
SkipUntil(tok::semi, tok::r_brace,
|
||||||
|
SkipUntilFlags::StopBeforeMatch);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
} else
|
||||||
|
PossibleRequiresExprInSimpleRequirement = true;
|
||||||
|
} else if (Tok.is(tok::kw_typename)) {
|
||||||
|
// This might be 'typename T::value_type;' (a type requirement) or
|
||||||
|
// 'typename T::value_type{};' (a simple requirement).
|
||||||
|
TentativeParsingAction TPA(*this);
|
||||||
|
|
||||||
|
// We need to consume the typename to allow 'requires { typename a; }'
|
||||||
|
SourceLocation TypenameKWLoc = ConsumeToken();
|
||||||
|
if (TryAnnotateCXXScopeToken()) {
|
||||||
|
SkipUntil(tok::semi, tok::r_brace, SkipUntilFlags::StopBeforeMatch);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
CXXScopeSpec SS;
|
||||||
|
if (Tok.is(tok::annot_cxxscope)) {
|
||||||
|
Actions.RestoreNestedNameSpecifierAnnotation(
|
||||||
|
Tok.getAnnotationValue(), Tok.getAnnotationRange(), SS);
|
||||||
|
ConsumeAnnotationToken();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Tok.isOneOf(tok::identifier, tok::annot_template_id) &&
|
||||||
|
!NextToken().isOneOf(tok::l_brace, tok::l_paren)) {
|
||||||
|
TPA.Commit();
|
||||||
|
SourceLocation NameLoc = Tok.getLocation();
|
||||||
|
IdentifierInfo *II = nullptr;
|
||||||
|
TemplateIdAnnotation *TemplateId = nullptr;
|
||||||
|
if (Tok.is(tok::identifier)) {
|
||||||
|
II = Tok.getIdentifierInfo();
|
||||||
|
ConsumeToken();
|
||||||
|
} else {
|
||||||
|
TemplateId = takeTemplateIdAnnotation(Tok);
|
||||||
|
ConsumeAnnotationToken();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (auto *Req = Actions.ActOnTypeRequirement(TypenameKWLoc, SS,
|
||||||
|
NameLoc, II,
|
||||||
|
TemplateId)) {
|
||||||
|
Requirements.push_back(Req);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
TPA.Revert();
|
||||||
|
}
|
||||||
|
// Simple requirement
|
||||||
|
// C++ [expr.prim.req.simple]
|
||||||
|
// simple-requirement:
|
||||||
|
// expression ';'
|
||||||
|
SourceLocation StartLoc = Tok.getLocation();
|
||||||
|
ExprResult Expression =
|
||||||
|
Actions.CorrectDelayedTyposInExpr(ParseExpression());
|
||||||
|
if (!Expression.isUsable()) {
|
||||||
|
SkipUntil(tok::semi, tok::r_brace, SkipUntilFlags::StopBeforeMatch);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (!Expression.isInvalid() && PossibleRequiresExprInSimpleRequirement)
|
||||||
|
Diag(StartLoc, diag::warn_requires_expr_in_simple_requirement)
|
||||||
|
<< FixItHint::CreateInsertion(StartLoc, "requires");
|
||||||
|
if (auto *Req = Actions.ActOnSimpleRequirement(Expression.get()))
|
||||||
|
Requirements.push_back(Req);
|
||||||
|
else {
|
||||||
|
SkipUntil(tok::semi, tok::r_brace, SkipUntilFlags::StopBeforeMatch);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
// User may have tried to put some compound requirement stuff here
|
||||||
|
if (Tok.is(tok::kw_noexcept)) {
|
||||||
|
Diag(Tok, diag::err_requires_expr_simple_requirement_noexcept)
|
||||||
|
<< FixItHint::CreateInsertion(StartLoc, "{")
|
||||||
|
<< FixItHint::CreateInsertion(Tok.getLocation(), "}");
|
||||||
|
SkipUntil(tok::semi, tok::r_brace, SkipUntilFlags::StopBeforeMatch);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (ExpectAndConsumeSemi(diag::err_expected_semi_requirement)) {
|
||||||
|
SkipUntil(tok::semi, tok::r_brace, SkipUntilFlags::StopBeforeMatch);
|
||||||
|
TryConsumeToken(tok::semi);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (Requirements.empty()) {
|
||||||
|
// Don't emit an empty requires expr here to avoid confusing the user with
|
||||||
|
// other diagnostics quoting an empty requires expression they never
|
||||||
|
// wrote.
|
||||||
|
Braces.consumeClose();
|
||||||
|
Actions.ActOnFinishRequiresExpr();
|
||||||
|
return ExprError();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Braces.consumeClose();
|
||||||
|
Actions.ActOnFinishRequiresExpr();
|
||||||
|
return Actions.ActOnRequiresExpr(RequiresKWLoc, Body, LocalParameterDecls,
|
||||||
|
Requirements, Braces.getCloseLocation());
|
||||||
|
}
|
||||||
|
|
||||||
static TypeTrait TypeTraitFromTokKind(tok::TokenKind kind) {
|
static TypeTrait TypeTraitFromTokKind(tok::TokenKind kind) {
|
||||||
switch (kind) {
|
switch (kind) {
|
||||||
default: llvm_unreachable("Not a known type trait");
|
default: llvm_unreachable("Not a known type trait");
|
||||||
|
@ -1261,7 +1261,8 @@ DeclContext *Sema::getFunctionLevelDeclContext() {
|
|||||||
DeclContext *DC = CurContext;
|
DeclContext *DC = CurContext;
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
if (isa<BlockDecl>(DC) || isa<EnumDecl>(DC) || isa<CapturedDecl>(DC)) {
|
if (isa<BlockDecl>(DC) || isa<EnumDecl>(DC) || isa<CapturedDecl>(DC) ||
|
||||||
|
isa<RequiresExprBodyDecl>(DC)) {
|
||||||
DC = DC->getParent();
|
DC = DC->getParent();
|
||||||
} else if (isa<CXXMethodDecl>(DC) &&
|
} else if (isa<CXXMethodDecl>(DC) &&
|
||||||
cast<CXXMethodDecl>(DC)->getOverloadedOperator() == OO_Call &&
|
cast<CXXMethodDecl>(DC)->getOverloadedOperator() == OO_Call &&
|
||||||
|
@ -17,7 +17,10 @@
|
|||||||
#include "clang/Sema/SemaDiagnostic.h"
|
#include "clang/Sema/SemaDiagnostic.h"
|
||||||
#include "clang/Sema/TemplateDeduction.h"
|
#include "clang/Sema/TemplateDeduction.h"
|
||||||
#include "clang/Sema/Template.h"
|
#include "clang/Sema/Template.h"
|
||||||
#include "clang/AST/ExprCXX.h"
|
#include "clang/Sema/Overload.h"
|
||||||
|
#include "clang/Sema/Initialization.h"
|
||||||
|
#include "clang/Sema/SemaInternal.h"
|
||||||
|
#include "clang/AST/ExprConcepts.h"
|
||||||
#include "clang/AST/RecursiveASTVisitor.h"
|
#include "clang/AST/RecursiveASTVisitor.h"
|
||||||
#include "clang/Basic/OperatorPrecedence.h"
|
#include "clang/Basic/OperatorPrecedence.h"
|
||||||
#include "llvm/ADT/DenseMap.h"
|
#include "llvm/ADT/DenseMap.h"
|
||||||
@ -336,6 +339,118 @@ bool Sema::EnsureTemplateArgumentListConstraints(
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void diagnoseUnsatisfiedRequirement(Sema &S,
|
||||||
|
concepts::ExprRequirement *Req,
|
||||||
|
bool First) {
|
||||||
|
assert(!Req->isSatisfied()
|
||||||
|
&& "Diagnose() can only be used on an unsatisfied requirement");
|
||||||
|
switch (Req->getSatisfactionStatus()) {
|
||||||
|
case concepts::ExprRequirement::SS_Dependent:
|
||||||
|
llvm_unreachable("Diagnosing a dependent requirement");
|
||||||
|
break;
|
||||||
|
case concepts::ExprRequirement::SS_ExprSubstitutionFailure: {
|
||||||
|
auto *SubstDiag = Req->getExprSubstitutionDiagnostic();
|
||||||
|
if (!SubstDiag->DiagMessage.empty())
|
||||||
|
S.Diag(SubstDiag->DiagLoc,
|
||||||
|
diag::note_expr_requirement_expr_substitution_error)
|
||||||
|
<< (int)First << SubstDiag->SubstitutedEntity
|
||||||
|
<< SubstDiag->DiagMessage;
|
||||||
|
else
|
||||||
|
S.Diag(SubstDiag->DiagLoc,
|
||||||
|
diag::note_expr_requirement_expr_unknown_substitution_error)
|
||||||
|
<< (int)First << SubstDiag->SubstitutedEntity;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case concepts::ExprRequirement::SS_NoexceptNotMet:
|
||||||
|
S.Diag(Req->getNoexceptLoc(),
|
||||||
|
diag::note_expr_requirement_noexcept_not_met)
|
||||||
|
<< (int)First << Req->getExpr();
|
||||||
|
break;
|
||||||
|
case concepts::ExprRequirement::SS_TypeRequirementSubstitutionFailure: {
|
||||||
|
auto *SubstDiag =
|
||||||
|
Req->getReturnTypeRequirement().getSubstitutionDiagnostic();
|
||||||
|
if (!SubstDiag->DiagMessage.empty())
|
||||||
|
S.Diag(SubstDiag->DiagLoc,
|
||||||
|
diag::note_expr_requirement_type_requirement_substitution_error)
|
||||||
|
<< (int)First << SubstDiag->SubstitutedEntity
|
||||||
|
<< SubstDiag->DiagMessage;
|
||||||
|
else
|
||||||
|
S.Diag(SubstDiag->DiagLoc,
|
||||||
|
diag::note_expr_requirement_type_requirement_unknown_substitution_error)
|
||||||
|
<< (int)First << SubstDiag->SubstitutedEntity;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case concepts::ExprRequirement::SS_ConstraintsNotSatisfied: {
|
||||||
|
ConceptSpecializationExpr *ConstraintExpr =
|
||||||
|
Req->getReturnTypeRequirementSubstitutedConstraintExpr();
|
||||||
|
if (ConstraintExpr->getTemplateArgsAsWritten()->NumTemplateArgs == 1)
|
||||||
|
// A simple case - expr type is the type being constrained and the concept
|
||||||
|
// was not provided arguments.
|
||||||
|
S.Diag(ConstraintExpr->getBeginLoc(),
|
||||||
|
diag::note_expr_requirement_constraints_not_satisfied_simple)
|
||||||
|
<< (int)First << S.BuildDecltypeType(Req->getExpr(),
|
||||||
|
Req->getExpr()->getBeginLoc())
|
||||||
|
<< ConstraintExpr->getNamedConcept();
|
||||||
|
else
|
||||||
|
S.Diag(ConstraintExpr->getBeginLoc(),
|
||||||
|
diag::note_expr_requirement_constraints_not_satisfied)
|
||||||
|
<< (int)First << ConstraintExpr;
|
||||||
|
S.DiagnoseUnsatisfiedConstraint(ConstraintExpr->getSatisfaction());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case concepts::ExprRequirement::SS_Satisfied:
|
||||||
|
llvm_unreachable("We checked this above");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void diagnoseUnsatisfiedRequirement(Sema &S,
|
||||||
|
concepts::TypeRequirement *Req,
|
||||||
|
bool First) {
|
||||||
|
assert(!Req->isSatisfied()
|
||||||
|
&& "Diagnose() can only be used on an unsatisfied requirement");
|
||||||
|
switch (Req->getSatisfactionStatus()) {
|
||||||
|
case concepts::TypeRequirement::SS_Dependent:
|
||||||
|
llvm_unreachable("Diagnosing a dependent requirement");
|
||||||
|
return;
|
||||||
|
case concepts::TypeRequirement::SS_SubstitutionFailure: {
|
||||||
|
auto *SubstDiag = Req->getSubstitutionDiagnostic();
|
||||||
|
if (!SubstDiag->DiagMessage.empty())
|
||||||
|
S.Diag(SubstDiag->DiagLoc,
|
||||||
|
diag::note_type_requirement_substitution_error) << (int)First
|
||||||
|
<< SubstDiag->SubstitutedEntity << SubstDiag->DiagMessage;
|
||||||
|
else
|
||||||
|
S.Diag(SubstDiag->DiagLoc,
|
||||||
|
diag::note_type_requirement_unknown_substitution_error)
|
||||||
|
<< (int)First << SubstDiag->SubstitutedEntity;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
llvm_unreachable("Unknown satisfaction status");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void diagnoseUnsatisfiedRequirement(Sema &S,
|
||||||
|
concepts::NestedRequirement *Req,
|
||||||
|
bool First) {
|
||||||
|
if (Req->isSubstitutionFailure()) {
|
||||||
|
concepts::Requirement::SubstitutionDiagnostic *SubstDiag =
|
||||||
|
Req->getSubstitutionDiagnostic();
|
||||||
|
if (!SubstDiag->DiagMessage.empty())
|
||||||
|
S.Diag(SubstDiag->DiagLoc,
|
||||||
|
diag::note_nested_requirement_substitution_error)
|
||||||
|
<< (int)First << SubstDiag->SubstitutedEntity
|
||||||
|
<< SubstDiag->DiagMessage;
|
||||||
|
else
|
||||||
|
S.Diag(SubstDiag->DiagLoc,
|
||||||
|
diag::note_nested_requirement_unknown_substitution_error)
|
||||||
|
<< (int)First << SubstDiag->SubstitutedEntity;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
S.DiagnoseUnsatisfiedConstraint(Req->getConstraintSatisfaction(), First);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
static void diagnoseWellFormedUnsatisfiedConstraintExpr(Sema &S,
|
static void diagnoseWellFormedUnsatisfiedConstraintExpr(Sema &S,
|
||||||
Expr *SubstExpr,
|
Expr *SubstExpr,
|
||||||
bool First = true) {
|
bool First = true) {
|
||||||
@ -412,6 +527,19 @@ static void diagnoseWellFormedUnsatisfiedConstraintExpr(Sema &S,
|
|||||||
}
|
}
|
||||||
S.DiagnoseUnsatisfiedConstraint(CSE->getSatisfaction());
|
S.DiagnoseUnsatisfiedConstraint(CSE->getSatisfaction());
|
||||||
return;
|
return;
|
||||||
|
} else if (auto *RE = dyn_cast<RequiresExpr>(SubstExpr)) {
|
||||||
|
for (concepts::Requirement *Req : RE->getRequirements())
|
||||||
|
if (!Req->isDependent() && !Req->isSatisfied()) {
|
||||||
|
if (auto *E = dyn_cast<concepts::ExprRequirement>(Req))
|
||||||
|
diagnoseUnsatisfiedRequirement(S, E, First);
|
||||||
|
else if (auto *T = dyn_cast<concepts::TypeRequirement>(Req))
|
||||||
|
diagnoseUnsatisfiedRequirement(S, T, First);
|
||||||
|
else
|
||||||
|
diagnoseUnsatisfiedRequirement(
|
||||||
|
S, cast<concepts::NestedRequirement>(Req), First);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
S.Diag(SubstExpr->getSourceRange().getBegin(),
|
S.Diag(SubstExpr->getSourceRange().getBegin(),
|
||||||
@ -434,11 +562,11 @@ static void diagnoseUnsatisfiedConstraintExpr(
|
|||||||
Record.template get<Expr *>(), First);
|
Record.template get<Expr *>(), First);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Sema::DiagnoseUnsatisfiedConstraint(
|
void
|
||||||
const ConstraintSatisfaction& Satisfaction) {
|
Sema::DiagnoseUnsatisfiedConstraint(const ConstraintSatisfaction& Satisfaction,
|
||||||
|
bool First) {
|
||||||
assert(!Satisfaction.IsSatisfied &&
|
assert(!Satisfaction.IsSatisfied &&
|
||||||
"Attempted to diagnose a satisfied constraint");
|
"Attempted to diagnose a satisfied constraint");
|
||||||
bool First = true;
|
|
||||||
for (auto &Pair : Satisfaction.Details) {
|
for (auto &Pair : Satisfaction.Details) {
|
||||||
diagnoseUnsatisfiedConstraintExpr(*this, Pair.first, Pair.second, First);
|
diagnoseUnsatisfiedConstraintExpr(*this, Pair.first, Pair.second, First);
|
||||||
First = false;
|
First = false;
|
||||||
@ -446,10 +574,10 @@ void Sema::DiagnoseUnsatisfiedConstraint(
|
|||||||
}
|
}
|
||||||
|
|
||||||
void Sema::DiagnoseUnsatisfiedConstraint(
|
void Sema::DiagnoseUnsatisfiedConstraint(
|
||||||
const ASTConstraintSatisfaction &Satisfaction) {
|
const ASTConstraintSatisfaction &Satisfaction,
|
||||||
|
bool First) {
|
||||||
assert(!Satisfaction.IsSatisfied &&
|
assert(!Satisfaction.IsSatisfied &&
|
||||||
"Attempted to diagnose a satisfied constraint");
|
"Attempted to diagnose a satisfied constraint");
|
||||||
bool First = true;
|
|
||||||
for (auto &Pair : Satisfaction) {
|
for (auto &Pair : Satisfaction) {
|
||||||
diagnoseUnsatisfiedConstraintExpr(*this, Pair.first, Pair.second, First);
|
diagnoseUnsatisfiedConstraintExpr(*this, Pair.first, Pair.second, First);
|
||||||
First = false;
|
First = false;
|
||||||
@ -826,3 +954,67 @@ bool Sema::MaybeEmitAmbiguousAtomicConstraintsDiagnostic(NamedDecl *D1,
|
|||||||
<< AmbiguousAtomic2->getSourceRange();
|
<< AmbiguousAtomic2->getSourceRange();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
concepts::ExprRequirement::ExprRequirement(
|
||||||
|
Expr *E, bool IsSimple, SourceLocation NoexceptLoc,
|
||||||
|
ReturnTypeRequirement Req, SatisfactionStatus Status,
|
||||||
|
ConceptSpecializationExpr *SubstitutedConstraintExpr) :
|
||||||
|
Requirement(IsSimple ? RK_Simple : RK_Compound, Status == SS_Dependent,
|
||||||
|
Status == SS_Dependent &&
|
||||||
|
(E->containsUnexpandedParameterPack() ||
|
||||||
|
Req.containsUnexpandedParameterPack()),
|
||||||
|
Status == SS_Satisfied), Value(E), NoexceptLoc(NoexceptLoc),
|
||||||
|
TypeReq(Req), SubstitutedConstraintExpr(SubstitutedConstraintExpr),
|
||||||
|
Status(Status) {
|
||||||
|
assert((!IsSimple || (Req.isEmpty() && NoexceptLoc.isInvalid())) &&
|
||||||
|
"Simple requirement must not have a return type requirement or a "
|
||||||
|
"noexcept specification");
|
||||||
|
assert((Status > SS_TypeRequirementSubstitutionFailure && Req.isTypeConstraint()) ==
|
||||||
|
(SubstitutedConstraintExpr != nullptr));
|
||||||
|
}
|
||||||
|
|
||||||
|
concepts::ExprRequirement::ExprRequirement(
|
||||||
|
SubstitutionDiagnostic *ExprSubstDiag, bool IsSimple,
|
||||||
|
SourceLocation NoexceptLoc, ReturnTypeRequirement Req) :
|
||||||
|
Requirement(IsSimple ? RK_Simple : RK_Compound, Req.isDependent(),
|
||||||
|
Req.containsUnexpandedParameterPack(), /*IsSatisfied=*/false),
|
||||||
|
Value(ExprSubstDiag), NoexceptLoc(NoexceptLoc), TypeReq(Req),
|
||||||
|
Status(SS_ExprSubstitutionFailure) {
|
||||||
|
assert((!IsSimple || (Req.isEmpty() && NoexceptLoc.isInvalid())) &&
|
||||||
|
"Simple requirement must not have a return type requirement or a "
|
||||||
|
"noexcept specification");
|
||||||
|
}
|
||||||
|
|
||||||
|
concepts::ExprRequirement::ReturnTypeRequirement::
|
||||||
|
ReturnTypeRequirement(TemplateParameterList *TPL) :
|
||||||
|
TypeConstraintInfo(TPL, 0) {
|
||||||
|
assert(TPL->size() == 1);
|
||||||
|
const TypeConstraint *TC =
|
||||||
|
cast<TemplateTypeParmDecl>(TPL->getParam(0))->getTypeConstraint();
|
||||||
|
assert(TC &&
|
||||||
|
"TPL must have a template type parameter with a type constraint");
|
||||||
|
auto *Constraint =
|
||||||
|
cast_or_null<ConceptSpecializationExpr>(
|
||||||
|
TC->getImmediatelyDeclaredConstraint());
|
||||||
|
bool Dependent = false;
|
||||||
|
if (Constraint->getTemplateArgsAsWritten()) {
|
||||||
|
for (auto &ArgLoc :
|
||||||
|
Constraint->getTemplateArgsAsWritten()->arguments().drop_front(1)) {
|
||||||
|
if (ArgLoc.getArgument().isDependent()) {
|
||||||
|
Dependent = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
TypeConstraintInfo.setInt(Dependent ? 1 : 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
concepts::TypeRequirement::TypeRequirement(TypeSourceInfo *T) :
|
||||||
|
Requirement(RK_Type, T->getType()->isDependentType(),
|
||||||
|
T->getType()->containsUnexpandedParameterPack(),
|
||||||
|
// We reach this ctor with either dependent types (in which
|
||||||
|
// IsSatisfied doesn't matter) or with non-dependent type in
|
||||||
|
// which the existence of the type indicates satisfaction.
|
||||||
|
/*IsSatisfied=*/true
|
||||||
|
), Value(T),
|
||||||
|
Status(T->getType()->isDependentType() ? SS_Dependent : SS_Satisfied) {}
|
||||||
|
@ -6468,6 +6468,8 @@ static bool shouldConsiderLinkage(const VarDecl *VD) {
|
|||||||
return true;
|
return true;
|
||||||
if (DC->isRecord())
|
if (DC->isRecord())
|
||||||
return false;
|
return false;
|
||||||
|
if (isa<RequiresExprBodyDecl>(DC))
|
||||||
|
return false;
|
||||||
llvm_unreachable("Unexpected context");
|
llvm_unreachable("Unexpected context");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1386,6 +1386,7 @@ CanThrowResult Sema::canThrow(const Stmt *S) {
|
|||||||
case Expr::StringLiteralClass:
|
case Expr::StringLiteralClass:
|
||||||
case Expr::SourceLocExprClass:
|
case Expr::SourceLocExprClass:
|
||||||
case Expr::ConceptSpecializationExprClass:
|
case Expr::ConceptSpecializationExprClass:
|
||||||
|
case Expr::RequiresExprClass:
|
||||||
// These expressions can never throw.
|
// These expressions can never throw.
|
||||||
return CT_Cannot;
|
return CT_Cannot;
|
||||||
|
|
||||||
|
@ -350,6 +350,17 @@ bool Sema::DiagnoseUseOfDecl(NamedDecl *D, ArrayRef<SourceLocation> Locs,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (isa<ParmVarDecl>(D) && isa<RequiresExprBodyDecl>(D->getDeclContext()) &&
|
||||||
|
!isUnevaluatedContext()) {
|
||||||
|
// C++ [expr.prim.req.nested] p3
|
||||||
|
// A local parameter shall only appear as an unevaluated operand
|
||||||
|
// (Clause 8) within the constraint-expression.
|
||||||
|
Diag(Loc, diag::err_requires_expr_parameter_referenced_in_evaluated_context)
|
||||||
|
<< D;
|
||||||
|
Diag(D->getLocation(), diag::note_entity_declared_at) << D;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1904,7 +1915,7 @@ Sema::BuildDeclRefExpr(ValueDecl *D, QualType Ty, ExprValueKind VK,
|
|||||||
bool RefersToCapturedVariable =
|
bool RefersToCapturedVariable =
|
||||||
isa<VarDecl>(D) &&
|
isa<VarDecl>(D) &&
|
||||||
NeedToCaptureVariable(cast<VarDecl>(D), NameInfo.getLoc());
|
NeedToCaptureVariable(cast<VarDecl>(D), NameInfo.getLoc());
|
||||||
|
|
||||||
DeclRefExpr *E = DeclRefExpr::Create(
|
DeclRefExpr *E = DeclRefExpr::Create(
|
||||||
Context, NNS, TemplateKWLoc, D, RefersToCapturedVariable, NameInfo, Ty,
|
Context, NNS, TemplateKWLoc, D, RefersToCapturedVariable, NameInfo, Ty,
|
||||||
VK, FoundD, TemplateArgs, getNonOdrUseReasonInCurrentContext(D));
|
VK, FoundD, TemplateArgs, getNonOdrUseReasonInCurrentContext(D));
|
||||||
|
@ -11,6 +11,7 @@
|
|||||||
///
|
///
|
||||||
//===----------------------------------------------------------------------===//
|
//===----------------------------------------------------------------------===//
|
||||||
|
|
||||||
|
#include "clang/Sema/Template.h"
|
||||||
#include "clang/Sema/SemaInternal.h"
|
#include "clang/Sema/SemaInternal.h"
|
||||||
#include "TreeTransform.h"
|
#include "TreeTransform.h"
|
||||||
#include "TypeLocBuilder.h"
|
#include "TypeLocBuilder.h"
|
||||||
@ -8331,3 +8332,215 @@ Sema::CheckMicrosoftIfExistsSymbol(Scope *S, SourceLocation KeywordLoc,
|
|||||||
|
|
||||||
return CheckMicrosoftIfExistsSymbol(S, SS, TargetNameInfo);
|
return CheckMicrosoftIfExistsSymbol(S, SS, TargetNameInfo);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
concepts::Requirement *Sema::ActOnSimpleRequirement(Expr *E) {
|
||||||
|
return BuildExprRequirement(E, /*IsSimple=*/true,
|
||||||
|
/*NoexceptLoc=*/SourceLocation(),
|
||||||
|
/*ReturnTypeRequirement=*/{});
|
||||||
|
}
|
||||||
|
|
||||||
|
concepts::Requirement *
|
||||||
|
Sema::ActOnTypeRequirement(SourceLocation TypenameKWLoc, CXXScopeSpec &SS,
|
||||||
|
SourceLocation NameLoc, IdentifierInfo *TypeName,
|
||||||
|
TemplateIdAnnotation *TemplateId) {
|
||||||
|
assert(((!TypeName && TemplateId) || (TypeName && !TemplateId)) &&
|
||||||
|
"Exactly one of TypeName and TemplateId must be specified.");
|
||||||
|
TypeSourceInfo *TSI = nullptr;
|
||||||
|
if (TypeName) {
|
||||||
|
QualType T = CheckTypenameType(ETK_Typename, TypenameKWLoc,
|
||||||
|
SS.getWithLocInContext(Context), *TypeName,
|
||||||
|
NameLoc, &TSI, /*DeducedTypeContext=*/false);
|
||||||
|
if (T.isNull())
|
||||||
|
return nullptr;
|
||||||
|
} else {
|
||||||
|
ASTTemplateArgsPtr ArgsPtr(TemplateId->getTemplateArgs(),
|
||||||
|
TemplateId->NumArgs);
|
||||||
|
TypeResult T = ActOnTypenameType(CurScope, TypenameKWLoc, SS,
|
||||||
|
TemplateId->TemplateKWLoc,
|
||||||
|
TemplateId->Template, TemplateId->Name,
|
||||||
|
TemplateId->TemplateNameLoc,
|
||||||
|
TemplateId->LAngleLoc, ArgsPtr,
|
||||||
|
TemplateId->RAngleLoc);
|
||||||
|
if (T.isInvalid())
|
||||||
|
return nullptr;
|
||||||
|
if (GetTypeFromParser(T.get(), &TSI).isNull())
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
return BuildTypeRequirement(TSI);
|
||||||
|
}
|
||||||
|
|
||||||
|
concepts::Requirement *
|
||||||
|
Sema::ActOnCompoundRequirement(Expr *E, SourceLocation NoexceptLoc) {
|
||||||
|
return BuildExprRequirement(E, /*IsSimple=*/false, NoexceptLoc,
|
||||||
|
/*ReturnTypeRequirement=*/{});
|
||||||
|
}
|
||||||
|
|
||||||
|
concepts::Requirement *
|
||||||
|
Sema::ActOnCompoundRequirement(
|
||||||
|
Expr *E, SourceLocation NoexceptLoc, CXXScopeSpec &SS,
|
||||||
|
TemplateIdAnnotation *TypeConstraint, unsigned Depth) {
|
||||||
|
// C++2a [expr.prim.req.compound] p1.3.3
|
||||||
|
// [..] the expression is deduced against an invented function template
|
||||||
|
// F [...] F is a void function template with a single type template
|
||||||
|
// parameter T declared with the constrained-parameter. Form a new
|
||||||
|
// cv-qualifier-seq cv by taking the union of const and volatile specifiers
|
||||||
|
// around the constrained-parameter. F has a single parameter whose
|
||||||
|
// type-specifier is cv T followed by the abstract-declarator. [...]
|
||||||
|
//
|
||||||
|
// The cv part is done in the calling function - we get the concept with
|
||||||
|
// arguments and the abstract declarator with the correct CV qualification and
|
||||||
|
// have to synthesize T and the single parameter of F.
|
||||||
|
auto &II = Context.Idents.get("expr-type");
|
||||||
|
auto *TParam = TemplateTypeParmDecl::Create(Context, CurContext,
|
||||||
|
SourceLocation(),
|
||||||
|
SourceLocation(), Depth,
|
||||||
|
/*Index=*/0, &II,
|
||||||
|
/*Typename=*/true,
|
||||||
|
/*ParameterPack=*/false,
|
||||||
|
/*HasTypeConstraint=*/true);
|
||||||
|
|
||||||
|
if (ActOnTypeConstraint(SS, TypeConstraint, TParam,
|
||||||
|
/*EllpsisLoc=*/SourceLocation()))
|
||||||
|
// Just produce a requirement with no type requirements.
|
||||||
|
return BuildExprRequirement(E, /*IsSimple=*/false, NoexceptLoc, {});
|
||||||
|
|
||||||
|
auto *TPL = TemplateParameterList::Create(Context, SourceLocation(),
|
||||||
|
SourceLocation(),
|
||||||
|
ArrayRef<NamedDecl *>(TParam),
|
||||||
|
SourceLocation(),
|
||||||
|
/*RequiresClause=*/nullptr);
|
||||||
|
return BuildExprRequirement(
|
||||||
|
E, /*IsSimple=*/false, NoexceptLoc,
|
||||||
|
concepts::ExprRequirement::ReturnTypeRequirement(TPL));
|
||||||
|
}
|
||||||
|
|
||||||
|
concepts::ExprRequirement *
|
||||||
|
Sema::BuildExprRequirement(
|
||||||
|
Expr *E, bool IsSimple, SourceLocation NoexceptLoc,
|
||||||
|
concepts::ExprRequirement::ReturnTypeRequirement ReturnTypeRequirement) {
|
||||||
|
auto Status = concepts::ExprRequirement::SS_Satisfied;
|
||||||
|
ConceptSpecializationExpr *SubstitutedConstraintExpr = nullptr;
|
||||||
|
if (E->isInstantiationDependent() || ReturnTypeRequirement.isDependent())
|
||||||
|
Status = concepts::ExprRequirement::SS_Dependent;
|
||||||
|
else if (NoexceptLoc.isValid() && canThrow(E) == CanThrowResult::CT_Can)
|
||||||
|
Status = concepts::ExprRequirement::SS_NoexceptNotMet;
|
||||||
|
else if (ReturnTypeRequirement.isSubstitutionFailure())
|
||||||
|
Status = concepts::ExprRequirement::SS_TypeRequirementSubstitutionFailure;
|
||||||
|
else if (ReturnTypeRequirement.isTypeConstraint()) {
|
||||||
|
// C++2a [expr.prim.req]p1.3.3
|
||||||
|
// The immediately-declared constraint ([temp]) of decltype((E)) shall
|
||||||
|
// be satisfied.
|
||||||
|
TemplateParameterList *TPL =
|
||||||
|
ReturnTypeRequirement.getTypeConstraintTemplateParameterList();
|
||||||
|
QualType MatchedType =
|
||||||
|
BuildDecltypeType(E, E->getBeginLoc()).getCanonicalType();
|
||||||
|
llvm::SmallVector<TemplateArgument, 1> Args;
|
||||||
|
Args.push_back(TemplateArgument(MatchedType));
|
||||||
|
TemplateArgumentList TAL(TemplateArgumentList::OnStack, Args);
|
||||||
|
MultiLevelTemplateArgumentList MLTAL(TAL);
|
||||||
|
for (unsigned I = 0; I < TPL->getDepth(); ++I)
|
||||||
|
MLTAL.addOuterRetainedLevel();
|
||||||
|
Expr *IDC =
|
||||||
|
cast<TemplateTypeParmDecl>(TPL->getParam(0))->getTypeConstraint()
|
||||||
|
->getImmediatelyDeclaredConstraint();
|
||||||
|
ExprResult Constraint = SubstExpr(IDC, MLTAL);
|
||||||
|
assert(!Constraint.isInvalid() &&
|
||||||
|
"Substitution cannot fail as it is simply putting a type template "
|
||||||
|
"argument into a concept specialization expression's parameter.");
|
||||||
|
|
||||||
|
SubstitutedConstraintExpr =
|
||||||
|
cast<ConceptSpecializationExpr>(Constraint.get());
|
||||||
|
if (!SubstitutedConstraintExpr->isSatisfied())
|
||||||
|
Status = concepts::ExprRequirement::SS_ConstraintsNotSatisfied;
|
||||||
|
}
|
||||||
|
return new (Context) concepts::ExprRequirement(E, IsSimple, NoexceptLoc,
|
||||||
|
ReturnTypeRequirement, Status,
|
||||||
|
SubstitutedConstraintExpr);
|
||||||
|
}
|
||||||
|
|
||||||
|
concepts::ExprRequirement *
|
||||||
|
Sema::BuildExprRequirement(
|
||||||
|
concepts::Requirement::SubstitutionDiagnostic *ExprSubstitutionDiagnostic,
|
||||||
|
bool IsSimple, SourceLocation NoexceptLoc,
|
||||||
|
concepts::ExprRequirement::ReturnTypeRequirement ReturnTypeRequirement) {
|
||||||
|
return new (Context) concepts::ExprRequirement(ExprSubstitutionDiagnostic,
|
||||||
|
IsSimple, NoexceptLoc,
|
||||||
|
ReturnTypeRequirement);
|
||||||
|
}
|
||||||
|
|
||||||
|
concepts::TypeRequirement *
|
||||||
|
Sema::BuildTypeRequirement(TypeSourceInfo *Type) {
|
||||||
|
return new (Context) concepts::TypeRequirement(Type);
|
||||||
|
}
|
||||||
|
|
||||||
|
concepts::TypeRequirement *
|
||||||
|
Sema::BuildTypeRequirement(
|
||||||
|
concepts::Requirement::SubstitutionDiagnostic *SubstDiag) {
|
||||||
|
return new (Context) concepts::TypeRequirement(SubstDiag);
|
||||||
|
}
|
||||||
|
|
||||||
|
concepts::Requirement *Sema::ActOnNestedRequirement(Expr *Constraint) {
|
||||||
|
return BuildNestedRequirement(Constraint);
|
||||||
|
}
|
||||||
|
|
||||||
|
concepts::NestedRequirement *
|
||||||
|
Sema::BuildNestedRequirement(Expr *Constraint) {
|
||||||
|
ConstraintSatisfaction Satisfaction;
|
||||||
|
if (!Constraint->isInstantiationDependent() &&
|
||||||
|
CheckConstraintSatisfaction(Constraint, Satisfaction))
|
||||||
|
return nullptr;
|
||||||
|
return new (Context) concepts::NestedRequirement(Context, Constraint,
|
||||||
|
Satisfaction);
|
||||||
|
}
|
||||||
|
|
||||||
|
concepts::NestedRequirement *
|
||||||
|
Sema::BuildNestedRequirement(
|
||||||
|
concepts::Requirement::SubstitutionDiagnostic *SubstDiag) {
|
||||||
|
return new (Context) concepts::NestedRequirement(SubstDiag);
|
||||||
|
}
|
||||||
|
|
||||||
|
RequiresExprBodyDecl *
|
||||||
|
Sema::ActOnStartRequiresExpr(SourceLocation RequiresKWLoc,
|
||||||
|
ArrayRef<ParmVarDecl *> LocalParameters,
|
||||||
|
Scope *BodyScope) {
|
||||||
|
assert(BodyScope);
|
||||||
|
|
||||||
|
RequiresExprBodyDecl *Body = RequiresExprBodyDecl::Create(Context, CurContext,
|
||||||
|
RequiresKWLoc);
|
||||||
|
|
||||||
|
PushDeclContext(BodyScope, Body);
|
||||||
|
|
||||||
|
for (ParmVarDecl *Param : LocalParameters) {
|
||||||
|
if (Param->hasDefaultArg())
|
||||||
|
// C++2a [expr.prim.req] p4
|
||||||
|
// [...] A local parameter of a requires-expression shall not have a
|
||||||
|
// default argument. [...]
|
||||||
|
Diag(Param->getDefaultArgRange().getBegin(),
|
||||||
|
diag::err_requires_expr_local_parameter_default_argument);
|
||||||
|
// Ignore default argument and move on
|
||||||
|
|
||||||
|
Param->setDeclContext(Body);
|
||||||
|
// If this has an identifier, add it to the scope stack.
|
||||||
|
if (Param->getIdentifier()) {
|
||||||
|
CheckShadow(BodyScope, Param);
|
||||||
|
PushOnScopeChains(Param, BodyScope);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Body;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Sema::ActOnFinishRequiresExpr() {
|
||||||
|
assert(CurContext && "DeclContext imbalance!");
|
||||||
|
CurContext = CurContext->getLexicalParent();
|
||||||
|
assert(CurContext && "Popped translation unit!");
|
||||||
|
}
|
||||||
|
|
||||||
|
ExprResult
|
||||||
|
Sema::ActOnRequiresExpr(SourceLocation RequiresKWLoc,
|
||||||
|
RequiresExprBodyDecl *Body,
|
||||||
|
ArrayRef<ParmVarDecl *> LocalParameters,
|
||||||
|
ArrayRef<concepts::Requirement *> Requirements,
|
||||||
|
SourceLocation ClosingBraceLoc) {
|
||||||
|
return RequiresExpr::Create(Context, RequiresKWLoc, Body, LocalParameters,
|
||||||
|
Requirements, ClosingBraceLoc);
|
||||||
|
}
|
||||||
|
@ -1575,7 +1575,9 @@ llvm::DenseSet<Module*> &Sema::getLookupModules() {
|
|||||||
unsigned N = CodeSynthesisContexts.size();
|
unsigned N = CodeSynthesisContexts.size();
|
||||||
for (unsigned I = CodeSynthesisContextLookupModules.size();
|
for (unsigned I = CodeSynthesisContextLookupModules.size();
|
||||||
I != N; ++I) {
|
I != N; ++I) {
|
||||||
Module *M = getDefiningModule(*this, CodeSynthesisContexts[I].Entity);
|
Module *M = CodeSynthesisContexts[I].Entity ?
|
||||||
|
getDefiningModule(*this, CodeSynthesisContexts[I].Entity) :
|
||||||
|
nullptr;
|
||||||
if (M && !LookupModulesCache.insert(M).second)
|
if (M && !LookupModulesCache.insert(M).second)
|
||||||
M = nullptr;
|
M = nullptr;
|
||||||
CodeSynthesisContextLookupModules.push_back(M);
|
CodeSynthesisContextLookupModules.push_back(M);
|
||||||
|
@ -10010,24 +10010,12 @@ Sema::ActOnTypenameType(Scope *S, SourceLocation TypenameLoc,
|
|||||||
<< FixItHint::CreateRemoval(TypenameLoc);
|
<< FixItHint::CreateRemoval(TypenameLoc);
|
||||||
|
|
||||||
NestedNameSpecifierLoc QualifierLoc = SS.getWithLocInContext(Context);
|
NestedNameSpecifierLoc QualifierLoc = SS.getWithLocInContext(Context);
|
||||||
|
TypeSourceInfo *TSI = nullptr;
|
||||||
QualType T = CheckTypenameType(TypenameLoc.isValid()? ETK_Typename : ETK_None,
|
QualType T = CheckTypenameType(TypenameLoc.isValid()? ETK_Typename : ETK_None,
|
||||||
TypenameLoc, QualifierLoc, II, IdLoc);
|
TypenameLoc, QualifierLoc, II, IdLoc, &TSI,
|
||||||
|
/*DeducedTSTContext=*/true);
|
||||||
if (T.isNull())
|
if (T.isNull())
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
TypeSourceInfo *TSI = Context.CreateTypeSourceInfo(T);
|
|
||||||
if (isa<DependentNameType>(T)) {
|
|
||||||
DependentNameTypeLoc TL = TSI->getTypeLoc().castAs<DependentNameTypeLoc>();
|
|
||||||
TL.setElaboratedKeywordLoc(TypenameLoc);
|
|
||||||
TL.setQualifierLoc(QualifierLoc);
|
|
||||||
TL.setNameLoc(IdLoc);
|
|
||||||
} else {
|
|
||||||
ElaboratedTypeLoc TL = TSI->getTypeLoc().castAs<ElaboratedTypeLoc>();
|
|
||||||
TL.setElaboratedKeywordLoc(TypenameLoc);
|
|
||||||
TL.setQualifierLoc(QualifierLoc);
|
|
||||||
TL.getNamedTypeLoc().castAs<TypeSpecTypeLoc>().setNameLoc(IdLoc);
|
|
||||||
}
|
|
||||||
|
|
||||||
return CreateParsedType(T, TSI);
|
return CreateParsedType(T, TSI);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -10164,6 +10152,35 @@ static bool isEnableIf(NestedNameSpecifierLoc NNS, const IdentifierInfo &II,
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QualType
|
||||||
|
Sema::CheckTypenameType(ElaboratedTypeKeyword Keyword,
|
||||||
|
SourceLocation KeywordLoc,
|
||||||
|
NestedNameSpecifierLoc QualifierLoc,
|
||||||
|
const IdentifierInfo &II,
|
||||||
|
SourceLocation IILoc,
|
||||||
|
TypeSourceInfo **TSI,
|
||||||
|
bool DeducedTSTContext) {
|
||||||
|
QualType T = CheckTypenameType(Keyword, KeywordLoc, QualifierLoc, II, IILoc,
|
||||||
|
DeducedTSTContext);
|
||||||
|
if (T.isNull())
|
||||||
|
return QualType();
|
||||||
|
|
||||||
|
*TSI = Context.CreateTypeSourceInfo(T);
|
||||||
|
if (isa<DependentNameType>(T)) {
|
||||||
|
DependentNameTypeLoc TL =
|
||||||
|
(*TSI)->getTypeLoc().castAs<DependentNameTypeLoc>();
|
||||||
|
TL.setElaboratedKeywordLoc(KeywordLoc);
|
||||||
|
TL.setQualifierLoc(QualifierLoc);
|
||||||
|
TL.setNameLoc(IILoc);
|
||||||
|
} else {
|
||||||
|
ElaboratedTypeLoc TL = (*TSI)->getTypeLoc().castAs<ElaboratedTypeLoc>();
|
||||||
|
TL.setElaboratedKeywordLoc(KeywordLoc);
|
||||||
|
TL.setQualifierLoc(QualifierLoc);
|
||||||
|
TL.getNamedTypeLoc().castAs<TypeSpecTypeLoc>().setNameLoc(IILoc);
|
||||||
|
}
|
||||||
|
return T;
|
||||||
|
}
|
||||||
|
|
||||||
/// Build the type that describes a C++ typename specifier,
|
/// Build the type that describes a C++ typename specifier,
|
||||||
/// e.g., "typename T::type".
|
/// e.g., "typename T::type".
|
||||||
QualType
|
QualType
|
||||||
@ -10171,32 +10188,38 @@ Sema::CheckTypenameType(ElaboratedTypeKeyword Keyword,
|
|||||||
SourceLocation KeywordLoc,
|
SourceLocation KeywordLoc,
|
||||||
NestedNameSpecifierLoc QualifierLoc,
|
NestedNameSpecifierLoc QualifierLoc,
|
||||||
const IdentifierInfo &II,
|
const IdentifierInfo &II,
|
||||||
SourceLocation IILoc) {
|
SourceLocation IILoc, bool DeducedTSTContext) {
|
||||||
CXXScopeSpec SS;
|
CXXScopeSpec SS;
|
||||||
SS.Adopt(QualifierLoc);
|
SS.Adopt(QualifierLoc);
|
||||||
|
|
||||||
DeclContext *Ctx = computeDeclContext(SS);
|
DeclContext *Ctx = nullptr;
|
||||||
if (!Ctx) {
|
if (QualifierLoc) {
|
||||||
// If the nested-name-specifier is dependent and couldn't be
|
Ctx = computeDeclContext(SS);
|
||||||
// resolved to a type, build a typename type.
|
if (!Ctx) {
|
||||||
assert(QualifierLoc.getNestedNameSpecifier()->isDependent());
|
// If the nested-name-specifier is dependent and couldn't be
|
||||||
return Context.getDependentNameType(Keyword,
|
// resolved to a type, build a typename type.
|
||||||
QualifierLoc.getNestedNameSpecifier(),
|
assert(QualifierLoc.getNestedNameSpecifier()->isDependent());
|
||||||
&II);
|
return Context.getDependentNameType(Keyword,
|
||||||
|
QualifierLoc.getNestedNameSpecifier(),
|
||||||
|
&II);
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the nested-name-specifier refers to the current instantiation,
|
||||||
|
// the "typename" keyword itself is superfluous. In C++03, the
|
||||||
|
// program is actually ill-formed. However, DR 382 (in C++0x CD1)
|
||||||
|
// allows such extraneous "typename" keywords, and we retroactively
|
||||||
|
// apply this DR to C++03 code with only a warning. In any case we continue.
|
||||||
|
|
||||||
|
if (RequireCompleteDeclContext(SS, Ctx))
|
||||||
|
return QualType();
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the nested-name-specifier refers to the current instantiation,
|
|
||||||
// the "typename" keyword itself is superfluous. In C++03, the
|
|
||||||
// program is actually ill-formed. However, DR 382 (in C++0x CD1)
|
|
||||||
// allows such extraneous "typename" keywords, and we retroactively
|
|
||||||
// apply this DR to C++03 code with only a warning. In any case we continue.
|
|
||||||
|
|
||||||
if (RequireCompleteDeclContext(SS, Ctx))
|
|
||||||
return QualType();
|
|
||||||
|
|
||||||
DeclarationName Name(&II);
|
DeclarationName Name(&II);
|
||||||
LookupResult Result(*this, Name, IILoc, LookupOrdinaryName);
|
LookupResult Result(*this, Name, IILoc, LookupOrdinaryName);
|
||||||
LookupQualifiedName(Result, Ctx, SS);
|
if (Ctx)
|
||||||
|
LookupQualifiedName(Result, Ctx, SS);
|
||||||
|
else
|
||||||
|
LookupName(Result, CurScope);
|
||||||
unsigned DiagID = 0;
|
unsigned DiagID = 0;
|
||||||
Decl *Referenced = nullptr;
|
Decl *Referenced = nullptr;
|
||||||
switch (Result.getResultKind()) {
|
switch (Result.getResultKind()) {
|
||||||
@ -10205,7 +10228,7 @@ Sema::CheckTypenameType(ElaboratedTypeKeyword Keyword,
|
|||||||
// a more specific diagnostic.
|
// a more specific diagnostic.
|
||||||
SourceRange CondRange;
|
SourceRange CondRange;
|
||||||
Expr *Cond = nullptr;
|
Expr *Cond = nullptr;
|
||||||
if (isEnableIf(QualifierLoc, II, CondRange, Cond)) {
|
if (Ctx && isEnableIf(QualifierLoc, II, CondRange, Cond)) {
|
||||||
// If we have a condition, narrow it down to the specific failed
|
// If we have a condition, narrow it down to the specific failed
|
||||||
// condition.
|
// condition.
|
||||||
if (Cond) {
|
if (Cond) {
|
||||||
@ -10221,12 +10244,14 @@ Sema::CheckTypenameType(ElaboratedTypeKeyword Keyword,
|
|||||||
return QualType();
|
return QualType();
|
||||||
}
|
}
|
||||||
|
|
||||||
Diag(CondRange.getBegin(), diag::err_typename_nested_not_found_enable_if)
|
Diag(CondRange.getBegin(),
|
||||||
|
diag::err_typename_nested_not_found_enable_if)
|
||||||
<< Ctx << CondRange;
|
<< Ctx << CondRange;
|
||||||
return QualType();
|
return QualType();
|
||||||
}
|
}
|
||||||
|
|
||||||
DiagID = diag::err_typename_nested_not_found;
|
DiagID = Ctx ? diag::err_typename_nested_not_found
|
||||||
|
: diag::err_unknown_typename;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -10292,6 +10317,19 @@ Sema::CheckTypenameType(ElaboratedTypeKeyword Keyword,
|
|||||||
// is a placeholder for a deduced class type [...].
|
// is a placeholder for a deduced class type [...].
|
||||||
if (getLangOpts().CPlusPlus17) {
|
if (getLangOpts().CPlusPlus17) {
|
||||||
if (auto *TD = getAsTypeTemplateDecl(Result.getFoundDecl())) {
|
if (auto *TD = getAsTypeTemplateDecl(Result.getFoundDecl())) {
|
||||||
|
if (!DeducedTSTContext) {
|
||||||
|
QualType T(QualifierLoc
|
||||||
|
? QualifierLoc.getNestedNameSpecifier()->getAsType()
|
||||||
|
: nullptr, 0);
|
||||||
|
if (!T.isNull())
|
||||||
|
Diag(IILoc, diag::err_dependent_deduced_tst)
|
||||||
|
<< (int)getTemplateNameKindForDiagnostics(TemplateName(TD)) << T;
|
||||||
|
else
|
||||||
|
Diag(IILoc, diag::err_deduced_tst)
|
||||||
|
<< (int)getTemplateNameKindForDiagnostics(TemplateName(TD));
|
||||||
|
Diag(TD->getLocation(), diag::note_template_decl_here);
|
||||||
|
return QualType();
|
||||||
|
}
|
||||||
return Context.getElaboratedType(
|
return Context.getElaboratedType(
|
||||||
Keyword, QualifierLoc.getNestedNameSpecifier(),
|
Keyword, QualifierLoc.getNestedNameSpecifier(),
|
||||||
Context.getDeducedTemplateSpecializationType(TemplateName(TD),
|
Context.getDeducedTemplateSpecializationType(TemplateName(TD),
|
||||||
@ -10299,12 +10337,14 @@ Sema::CheckTypenameType(ElaboratedTypeKeyword Keyword,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
DiagID = diag::err_typename_nested_not_type;
|
DiagID = Ctx ? diag::err_typename_nested_not_type
|
||||||
|
: diag::err_typename_not_type;
|
||||||
Referenced = Result.getFoundDecl();
|
Referenced = Result.getFoundDecl();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case LookupResult::FoundOverloaded:
|
case LookupResult::FoundOverloaded:
|
||||||
DiagID = diag::err_typename_nested_not_type;
|
DiagID = Ctx ? diag::err_typename_nested_not_type
|
||||||
|
: diag::err_typename_not_type;
|
||||||
Referenced = *Result.begin();
|
Referenced = *Result.begin();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@ -10316,9 +10356,14 @@ Sema::CheckTypenameType(ElaboratedTypeKeyword Keyword,
|
|||||||
// type. Emit an appropriate diagnostic and return an error.
|
// type. Emit an appropriate diagnostic and return an error.
|
||||||
SourceRange FullRange(KeywordLoc.isValid() ? KeywordLoc : SS.getBeginLoc(),
|
SourceRange FullRange(KeywordLoc.isValid() ? KeywordLoc : SS.getBeginLoc(),
|
||||||
IILoc);
|
IILoc);
|
||||||
Diag(IILoc, DiagID) << FullRange << Name << Ctx;
|
if (Ctx)
|
||||||
|
Diag(IILoc, DiagID) << FullRange << Name << Ctx;
|
||||||
|
else
|
||||||
|
Diag(IILoc, DiagID) << FullRange << Name;
|
||||||
if (Referenced)
|
if (Referenced)
|
||||||
Diag(Referenced->getLocation(), diag::note_typename_refers_here)
|
Diag(Referenced->getLocation(),
|
||||||
|
Ctx ? diag::note_typename_member_refers_here
|
||||||
|
: diag::note_typename_refers_here)
|
||||||
<< Name;
|
<< Name;
|
||||||
return QualType();
|
return QualType();
|
||||||
}
|
}
|
||||||
|
@ -26,6 +26,7 @@
|
|||||||
#include "clang/Sema/Template.h"
|
#include "clang/Sema/Template.h"
|
||||||
#include "clang/Sema/TemplateDeduction.h"
|
#include "clang/Sema/TemplateDeduction.h"
|
||||||
#include "clang/Sema/TemplateInstCallback.h"
|
#include "clang/Sema/TemplateInstCallback.h"
|
||||||
|
#include "clang/Sema/SemaConcept.h"
|
||||||
#include "llvm/Support/TimeProfiler.h"
|
#include "llvm/Support/TimeProfiler.h"
|
||||||
|
|
||||||
using namespace clang;
|
using namespace clang;
|
||||||
@ -199,8 +200,10 @@ bool Sema::CodeSynthesisContext::isInstantiationRecord() const {
|
|||||||
case DeducedTemplateArgumentSubstitution:
|
case DeducedTemplateArgumentSubstitution:
|
||||||
case PriorTemplateArgumentSubstitution:
|
case PriorTemplateArgumentSubstitution:
|
||||||
case ConstraintsCheck:
|
case ConstraintsCheck:
|
||||||
|
case NestedRequirementConstraintsCheck:
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
|
case RequirementInstantiation:
|
||||||
case DefaultTemplateArgumentChecking:
|
case DefaultTemplateArgumentChecking:
|
||||||
case DeclaringSpecialMember:
|
case DeclaringSpecialMember:
|
||||||
case DeclaringImplicitEqualityComparison:
|
case DeclaringImplicitEqualityComparison:
|
||||||
@ -247,7 +250,7 @@ Sema::InstantiatingTemplate::InstantiatingTemplate(
|
|||||||
Inst.InstantiationRange = InstantiationRange;
|
Inst.InstantiationRange = InstantiationRange;
|
||||||
SemaRef.pushCodeSynthesisContext(Inst);
|
SemaRef.pushCodeSynthesisContext(Inst);
|
||||||
|
|
||||||
AlreadyInstantiating =
|
AlreadyInstantiating = !Inst.Entity ? false :
|
||||||
!SemaRef.InstantiatingSpecializations
|
!SemaRef.InstantiatingSpecializations
|
||||||
.insert(std::make_pair(Inst.Entity->getCanonicalDecl(), Inst.Kind))
|
.insert(std::make_pair(Inst.Entity->getCanonicalDecl(), Inst.Kind))
|
||||||
.second;
|
.second;
|
||||||
@ -364,6 +367,26 @@ Sema::InstantiatingTemplate::InstantiatingTemplate(
|
|||||||
PointOfInstantiation, InstantiationRange, Param, Template,
|
PointOfInstantiation, InstantiationRange, Param, Template,
|
||||||
TemplateArgs) {}
|
TemplateArgs) {}
|
||||||
|
|
||||||
|
Sema::InstantiatingTemplate::InstantiatingTemplate(
|
||||||
|
Sema &SemaRef, SourceLocation PointOfInstantiation,
|
||||||
|
concepts::Requirement *Req, sema::TemplateDeductionInfo &DeductionInfo,
|
||||||
|
SourceRange InstantiationRange)
|
||||||
|
: InstantiatingTemplate(
|
||||||
|
SemaRef, CodeSynthesisContext::RequirementInstantiation,
|
||||||
|
PointOfInstantiation, InstantiationRange, /*Entity=*/nullptr,
|
||||||
|
/*Template=*/nullptr, /*TemplateArgs=*/None, &DeductionInfo) {}
|
||||||
|
|
||||||
|
|
||||||
|
Sema::InstantiatingTemplate::InstantiatingTemplate(
|
||||||
|
Sema &SemaRef, SourceLocation PointOfInstantiation,
|
||||||
|
concepts::NestedRequirement *Req, ConstraintsCheck,
|
||||||
|
SourceRange InstantiationRange)
|
||||||
|
: InstantiatingTemplate(
|
||||||
|
SemaRef, CodeSynthesisContext::NestedRequirementConstraintsCheck,
|
||||||
|
PointOfInstantiation, InstantiationRange, /*Entity=*/nullptr,
|
||||||
|
/*Template=*/nullptr, /*TemplateArgs=*/None) {}
|
||||||
|
|
||||||
|
|
||||||
Sema::InstantiatingTemplate::InstantiatingTemplate(
|
Sema::InstantiatingTemplate::InstantiatingTemplate(
|
||||||
Sema &SemaRef, SourceLocation PointOfInstantiation,
|
Sema &SemaRef, SourceLocation PointOfInstantiation,
|
||||||
ConstraintsCheck, NamedDecl *Template,
|
ConstraintsCheck, NamedDecl *Template,
|
||||||
@ -446,8 +469,9 @@ void Sema::InstantiatingTemplate::Clear() {
|
|||||||
if (!Invalid) {
|
if (!Invalid) {
|
||||||
if (!AlreadyInstantiating) {
|
if (!AlreadyInstantiating) {
|
||||||
auto &Active = SemaRef.CodeSynthesisContexts.back();
|
auto &Active = SemaRef.CodeSynthesisContexts.back();
|
||||||
SemaRef.InstantiatingSpecializations.erase(
|
if (Active.Entity)
|
||||||
std::make_pair(Active.Entity, Active.Kind));
|
SemaRef.InstantiatingSpecializations.erase(
|
||||||
|
std::make_pair(Active.Entity, Active.Kind));
|
||||||
}
|
}
|
||||||
|
|
||||||
atTemplateEnd(SemaRef.TemplateInstCallbacks, SemaRef,
|
atTemplateEnd(SemaRef.TemplateInstCallbacks, SemaRef,
|
||||||
@ -684,6 +708,18 @@ void Sema::PrintInstantiationStack() {
|
|||||||
<< Active->InstantiationRange;
|
<< Active->InstantiationRange;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case CodeSynthesisContext::RequirementInstantiation:
|
||||||
|
Diags.Report(Active->PointOfInstantiation,
|
||||||
|
diag::note_template_requirement_instantiation_here)
|
||||||
|
<< Active->InstantiationRange;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case CodeSynthesisContext::NestedRequirementConstraintsCheck:
|
||||||
|
Diags.Report(Active->PointOfInstantiation,
|
||||||
|
diag::note_nested_requirement_here)
|
||||||
|
<< Active->InstantiationRange;
|
||||||
|
break;
|
||||||
|
|
||||||
case CodeSynthesisContext::DeclaringSpecialMember:
|
case CodeSynthesisContext::DeclaringSpecialMember:
|
||||||
Diags.Report(Active->PointOfInstantiation,
|
Diags.Report(Active->PointOfInstantiation,
|
||||||
diag::note_in_declaration_of_implicit_special_member)
|
diag::note_in_declaration_of_implicit_special_member)
|
||||||
@ -788,6 +824,7 @@ Optional<TemplateDeductionInfo *> Sema::isSFINAEContext() const {
|
|||||||
case CodeSynthesisContext::ConstraintsCheck:
|
case CodeSynthesisContext::ConstraintsCheck:
|
||||||
case CodeSynthesisContext::ParameterMappingSubstitution:
|
case CodeSynthesisContext::ParameterMappingSubstitution:
|
||||||
case CodeSynthesisContext::ConstraintNormalization:
|
case CodeSynthesisContext::ConstraintNormalization:
|
||||||
|
case CodeSynthesisContext::NestedRequirementConstraintsCheck:
|
||||||
// This is a template instantiation, so there is no SFINAE.
|
// This is a template instantiation, so there is no SFINAE.
|
||||||
return None;
|
return None;
|
||||||
|
|
||||||
@ -802,9 +839,10 @@ Optional<TemplateDeductionInfo *> Sema::isSFINAEContext() const {
|
|||||||
case CodeSynthesisContext::ExplicitTemplateArgumentSubstitution:
|
case CodeSynthesisContext::ExplicitTemplateArgumentSubstitution:
|
||||||
case CodeSynthesisContext::DeducedTemplateArgumentSubstitution:
|
case CodeSynthesisContext::DeducedTemplateArgumentSubstitution:
|
||||||
case CodeSynthesisContext::ConstraintSubstitution:
|
case CodeSynthesisContext::ConstraintSubstitution:
|
||||||
// We're either substituting explicitly-specified template arguments
|
case CodeSynthesisContext::RequirementInstantiation:
|
||||||
// or deduced template arguments or a constraint expression, so SFINAE
|
// We're either substituting explicitly-specified template arguments,
|
||||||
// applies.
|
// deduced template arguments, a constraint expression or a requirement
|
||||||
|
// in a requires expression, so SFINAE applies.
|
||||||
assert(Active->DeductionInfo && "Missing deduction info pointer");
|
assert(Active->DeductionInfo && "Missing deduction info pointer");
|
||||||
return Active->DeductionInfo;
|
return Active->DeductionInfo;
|
||||||
|
|
||||||
@ -1056,6 +1094,41 @@ namespace {
|
|||||||
return TreeTransform<TemplateInstantiator>::TransformLambdaExpr(E);
|
return TreeTransform<TemplateInstantiator>::TransformLambdaExpr(E);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ExprResult TransformRequiresExpr(RequiresExpr *E) {
|
||||||
|
LocalInstantiationScope Scope(SemaRef, /*CombineWithOuterScope=*/true);
|
||||||
|
return TreeTransform<TemplateInstantiator>::TransformRequiresExpr(E);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool TransformRequiresExprRequirements(
|
||||||
|
ArrayRef<concepts::Requirement *> Reqs,
|
||||||
|
SmallVectorImpl<concepts::Requirement *> &Transformed) {
|
||||||
|
bool SatisfactionDetermined = false;
|
||||||
|
for (concepts::Requirement *Req : Reqs) {
|
||||||
|
concepts::Requirement *TransReq = nullptr;
|
||||||
|
if (!SatisfactionDetermined) {
|
||||||
|
if (auto *TypeReq = dyn_cast<concepts::TypeRequirement>(Req))
|
||||||
|
TransReq = TransformTypeRequirement(TypeReq);
|
||||||
|
else if (auto *ExprReq = dyn_cast<concepts::ExprRequirement>(Req))
|
||||||
|
TransReq = TransformExprRequirement(ExprReq);
|
||||||
|
else
|
||||||
|
TransReq = TransformNestedRequirement(
|
||||||
|
cast<concepts::NestedRequirement>(Req));
|
||||||
|
if (!TransReq)
|
||||||
|
return true;
|
||||||
|
if (!TransReq->isDependent() && !TransReq->isSatisfied())
|
||||||
|
// [expr.prim.req]p6
|
||||||
|
// [...] The substitution and semantic constraint checking
|
||||||
|
// proceeds in lexical order and stops when a condition that
|
||||||
|
// determines the result of the requires-expression is
|
||||||
|
// encountered. [..]
|
||||||
|
SatisfactionDetermined = true;
|
||||||
|
} else
|
||||||
|
TransReq = Req;
|
||||||
|
Transformed.push_back(TransReq);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
TemplateParameterList *TransformTemplateParameterList(
|
TemplateParameterList *TransformTemplateParameterList(
|
||||||
TemplateParameterList *OrigTPL) {
|
TemplateParameterList *OrigTPL) {
|
||||||
if (!OrigTPL || !OrigTPL->size()) return OrigTPL;
|
if (!OrigTPL || !OrigTPL->size()) return OrigTPL;
|
||||||
@ -1065,6 +1138,14 @@ namespace {
|
|||||||
/* DeclContext *Owner */ Owner, TemplateArgs);
|
/* DeclContext *Owner */ Owner, TemplateArgs);
|
||||||
return DeclInstantiator.SubstTemplateParams(OrigTPL);
|
return DeclInstantiator.SubstTemplateParams(OrigTPL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
concepts::TypeRequirement *
|
||||||
|
TransformTypeRequirement(concepts::TypeRequirement *Req);
|
||||||
|
concepts::ExprRequirement *
|
||||||
|
TransformExprRequirement(concepts::ExprRequirement *Req);
|
||||||
|
concepts::NestedRequirement *
|
||||||
|
TransformNestedRequirement(concepts::NestedRequirement *Req);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
ExprResult transformNonTypeTemplateParmRef(NonTypeTemplateParmDecl *parm,
|
ExprResult transformNonTypeTemplateParmRef(NonTypeTemplateParmDecl *parm,
|
||||||
SourceLocation loc,
|
SourceLocation loc,
|
||||||
@ -1669,6 +1750,163 @@ TemplateInstantiator::TransformSubstTemplateTypeParmPackType(
|
|||||||
return Result;
|
return Result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template<typename EntityPrinter>
|
||||||
|
static concepts::Requirement::SubstitutionDiagnostic *
|
||||||
|
createSubstDiag(Sema &S, TemplateDeductionInfo &Info, EntityPrinter Printer) {
|
||||||
|
SmallString<128> Message;
|
||||||
|
SourceLocation ErrorLoc;
|
||||||
|
if (Info.hasSFINAEDiagnostic()) {
|
||||||
|
PartialDiagnosticAt PDA(SourceLocation(),
|
||||||
|
PartialDiagnostic::NullDiagnostic{});
|
||||||
|
Info.takeSFINAEDiagnostic(PDA);
|
||||||
|
PDA.second.EmitToString(S.getDiagnostics(), Message);
|
||||||
|
ErrorLoc = PDA.first;
|
||||||
|
} else {
|
||||||
|
ErrorLoc = Info.getLocation();
|
||||||
|
}
|
||||||
|
char *MessageBuf = new (S.Context) char[Message.size()];
|
||||||
|
std::copy(Message.begin(), Message.end(), MessageBuf);
|
||||||
|
SmallString<128> Entity;
|
||||||
|
llvm::raw_svector_ostream OS(Entity);
|
||||||
|
Printer(OS);
|
||||||
|
char *EntityBuf = new (S.Context) char[Entity.size()];
|
||||||
|
std::copy(Entity.begin(), Entity.end(), EntityBuf);
|
||||||
|
return new (S.Context) concepts::Requirement::SubstitutionDiagnostic{
|
||||||
|
StringRef(EntityBuf, Entity.size()), ErrorLoc,
|
||||||
|
StringRef(MessageBuf, Message.size())};
|
||||||
|
}
|
||||||
|
|
||||||
|
concepts::TypeRequirement *
|
||||||
|
TemplateInstantiator::TransformTypeRequirement(concepts::TypeRequirement *Req) {
|
||||||
|
if (!Req->isDependent() && !AlwaysRebuild())
|
||||||
|
return Req;
|
||||||
|
if (Req->isSubstitutionFailure()) {
|
||||||
|
if (AlwaysRebuild())
|
||||||
|
return RebuildTypeRequirement(
|
||||||
|
Req->getSubstitutionDiagnostic());
|
||||||
|
return Req;
|
||||||
|
}
|
||||||
|
|
||||||
|
Sema::SFINAETrap Trap(SemaRef);
|
||||||
|
TemplateDeductionInfo Info(Req->getType()->getTypeLoc().getBeginLoc());
|
||||||
|
Sema::InstantiatingTemplate TypeInst(SemaRef,
|
||||||
|
Req->getType()->getTypeLoc().getBeginLoc(), Req, Info,
|
||||||
|
Req->getType()->getTypeLoc().getSourceRange());
|
||||||
|
if (TypeInst.isInvalid())
|
||||||
|
return nullptr;
|
||||||
|
TypeSourceInfo *TransType = TransformType(Req->getType());
|
||||||
|
if (!TransType || Trap.hasErrorOccurred())
|
||||||
|
return RebuildTypeRequirement(createSubstDiag(SemaRef, Info,
|
||||||
|
[&] (llvm::raw_ostream& OS) {
|
||||||
|
Req->getType()->getType().print(OS, SemaRef.getPrintingPolicy());
|
||||||
|
}));
|
||||||
|
return RebuildTypeRequirement(TransType);
|
||||||
|
}
|
||||||
|
|
||||||
|
concepts::ExprRequirement *
|
||||||
|
TemplateInstantiator::TransformExprRequirement(concepts::ExprRequirement *Req) {
|
||||||
|
if (!Req->isDependent() && !AlwaysRebuild())
|
||||||
|
return Req;
|
||||||
|
|
||||||
|
Sema::SFINAETrap Trap(SemaRef);
|
||||||
|
TemplateDeductionInfo Info(Req->getExpr()->getBeginLoc());
|
||||||
|
|
||||||
|
llvm::PointerUnion<Expr *, concepts::Requirement::SubstitutionDiagnostic *>
|
||||||
|
TransExpr;
|
||||||
|
if (Req->isExprSubstitutionFailure())
|
||||||
|
TransExpr = Req->getExprSubstitutionDiagnostic();
|
||||||
|
else {
|
||||||
|
Sema::InstantiatingTemplate ExprInst(SemaRef, Req->getExpr()->getBeginLoc(),
|
||||||
|
Req, Info,
|
||||||
|
Req->getExpr()->getSourceRange());
|
||||||
|
if (ExprInst.isInvalid())
|
||||||
|
return nullptr;
|
||||||
|
ExprResult TransExprRes = TransformExpr(Req->getExpr());
|
||||||
|
if (TransExprRes.isInvalid() || Trap.hasErrorOccurred())
|
||||||
|
TransExpr = createSubstDiag(SemaRef, Info,
|
||||||
|
[&] (llvm::raw_ostream& OS) {
|
||||||
|
Req->getExpr()->printPretty(OS, nullptr,
|
||||||
|
SemaRef.getPrintingPolicy());
|
||||||
|
});
|
||||||
|
else
|
||||||
|
TransExpr = TransExprRes.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
llvm::Optional<concepts::ExprRequirement::ReturnTypeRequirement> TransRetReq;
|
||||||
|
const auto &RetReq = Req->getReturnTypeRequirement();
|
||||||
|
if (RetReq.isEmpty())
|
||||||
|
TransRetReq.emplace();
|
||||||
|
else if (RetReq.isSubstitutionFailure())
|
||||||
|
TransRetReq.emplace(RetReq.getSubstitutionDiagnostic());
|
||||||
|
else if (RetReq.isTypeConstraint()) {
|
||||||
|
TemplateParameterList *OrigTPL =
|
||||||
|
RetReq.getTypeConstraintTemplateParameterList();
|
||||||
|
Sema::InstantiatingTemplate TPLInst(SemaRef, OrigTPL->getTemplateLoc(),
|
||||||
|
Req, Info, OrigTPL->getSourceRange());
|
||||||
|
if (TPLInst.isInvalid())
|
||||||
|
return nullptr;
|
||||||
|
TemplateParameterList *TPL =
|
||||||
|
TransformTemplateParameterList(OrigTPL);
|
||||||
|
if (!TPL)
|
||||||
|
TransRetReq.emplace(createSubstDiag(SemaRef, Info,
|
||||||
|
[&] (llvm::raw_ostream& OS) {
|
||||||
|
RetReq.getTypeConstraint()->getImmediatelyDeclaredConstraint()
|
||||||
|
->printPretty(OS, nullptr, SemaRef.getPrintingPolicy());
|
||||||
|
}));
|
||||||
|
else {
|
||||||
|
TPLInst.Clear();
|
||||||
|
TransRetReq.emplace(TPL);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
assert(TransRetReq.hasValue() &&
|
||||||
|
"All code paths leading here must set TransRetReq");
|
||||||
|
if (Expr *E = TransExpr.dyn_cast<Expr *>())
|
||||||
|
return RebuildExprRequirement(E, Req->isSimple(), Req->getNoexceptLoc(),
|
||||||
|
std::move(*TransRetReq));
|
||||||
|
return RebuildExprRequirement(
|
||||||
|
TransExpr.get<concepts::Requirement::SubstitutionDiagnostic *>(),
|
||||||
|
Req->isSimple(), Req->getNoexceptLoc(), std::move(*TransRetReq));
|
||||||
|
}
|
||||||
|
|
||||||
|
concepts::NestedRequirement *
|
||||||
|
TemplateInstantiator::TransformNestedRequirement(
|
||||||
|
concepts::NestedRequirement *Req) {
|
||||||
|
if (!Req->isDependent() && !AlwaysRebuild())
|
||||||
|
return Req;
|
||||||
|
if (Req->isSubstitutionFailure()) {
|
||||||
|
if (AlwaysRebuild())
|
||||||
|
return RebuildNestedRequirement(
|
||||||
|
Req->getSubstitutionDiagnostic());
|
||||||
|
return Req;
|
||||||
|
}
|
||||||
|
Sema::InstantiatingTemplate ReqInst(SemaRef,
|
||||||
|
Req->getConstraintExpr()->getBeginLoc(), Req,
|
||||||
|
Sema::InstantiatingTemplate::ConstraintsCheck{},
|
||||||
|
Req->getConstraintExpr()->getSourceRange());
|
||||||
|
|
||||||
|
ExprResult TransConstraint;
|
||||||
|
TemplateDeductionInfo Info(Req->getConstraintExpr()->getBeginLoc());
|
||||||
|
{
|
||||||
|
EnterExpressionEvaluationContext ContextRAII(
|
||||||
|
SemaRef, Sema::ExpressionEvaluationContext::ConstantEvaluated);
|
||||||
|
Sema::SFINAETrap Trap(SemaRef);
|
||||||
|
Sema::InstantiatingTemplate ConstrInst(SemaRef,
|
||||||
|
Req->getConstraintExpr()->getBeginLoc(), Req, Info,
|
||||||
|
Req->getConstraintExpr()->getSourceRange());
|
||||||
|
if (ConstrInst.isInvalid())
|
||||||
|
return nullptr;
|
||||||
|
TransConstraint = TransformExpr(Req->getConstraintExpr());
|
||||||
|
if (TransConstraint.isInvalid() || Trap.hasErrorOccurred())
|
||||||
|
return RebuildNestedRequirement(createSubstDiag(SemaRef, Info,
|
||||||
|
[&] (llvm::raw_ostream& OS) {
|
||||||
|
Req->getConstraintExpr()->printPretty(OS, nullptr,
|
||||||
|
SemaRef.getPrintingPolicy());
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
return RebuildNestedRequirement(TransConstraint.get());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/// Perform substitution on the type T with a given set of template
|
/// Perform substitution on the type T with a given set of template
|
||||||
/// arguments.
|
/// arguments.
|
||||||
///
|
///
|
||||||
|
@ -3600,6 +3600,12 @@ Decl *TemplateDeclInstantiator::VisitConceptDecl(ConceptDecl *D) {
|
|||||||
llvm_unreachable("Concept definitions cannot reside inside a template");
|
llvm_unreachable("Concept definitions cannot reside inside a template");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Decl *
|
||||||
|
TemplateDeclInstantiator::VisitRequiresExprBodyDecl(RequiresExprBodyDecl *D) {
|
||||||
|
return RequiresExprBodyDecl::Create(SemaRef.Context, D->getDeclContext(),
|
||||||
|
D->getBeginLoc());
|
||||||
|
}
|
||||||
|
|
||||||
Decl *TemplateDeclInstantiator::VisitDecl(Decl *D) {
|
Decl *TemplateDeclInstantiator::VisitDecl(Decl *D) {
|
||||||
llvm_unreachable("Unexpected decl");
|
llvm_unreachable("Unexpected decl");
|
||||||
}
|
}
|
||||||
|
@ -2994,6 +2994,9 @@ static QualType GetDeclSpecTypeForDeclarator(TypeProcessingState &state,
|
|||||||
case DeclaratorContext::PrototypeContext:
|
case DeclaratorContext::PrototypeContext:
|
||||||
Error = 0;
|
Error = 0;
|
||||||
break;
|
break;
|
||||||
|
case DeclaratorContext::RequiresExprContext:
|
||||||
|
Error = 21;
|
||||||
|
break;
|
||||||
case DeclaratorContext::LambdaExprParameterContext:
|
case DeclaratorContext::LambdaExprParameterContext:
|
||||||
// In C++14, generic lambdas allow 'auto' in their parameters.
|
// In C++14, generic lambdas allow 'auto' in their parameters.
|
||||||
if (!SemaRef.getLangOpts().CPlusPlus14 ||
|
if (!SemaRef.getLangOpts().CPlusPlus14 ||
|
||||||
@ -3221,6 +3224,7 @@ static QualType GetDeclSpecTypeForDeclarator(TypeProcessingState &state,
|
|||||||
case DeclaratorContext::ObjCParameterContext:
|
case DeclaratorContext::ObjCParameterContext:
|
||||||
case DeclaratorContext::ObjCResultContext:
|
case DeclaratorContext::ObjCResultContext:
|
||||||
case DeclaratorContext::KNRTypeListContext:
|
case DeclaratorContext::KNRTypeListContext:
|
||||||
|
case DeclaratorContext::RequiresExprContext:
|
||||||
// C++ [dcl.fct]p6:
|
// C++ [dcl.fct]p6:
|
||||||
// Types shall not be defined in return or parameter types.
|
// Types shall not be defined in return or parameter types.
|
||||||
DiagID = diag::err_type_defined_in_param_type;
|
DiagID = diag::err_type_defined_in_param_type;
|
||||||
@ -4279,6 +4283,7 @@ static TypeSourceInfo *GetFullTypeForDeclarator(TypeProcessingState &state,
|
|||||||
case DeclaratorContext::TemplateTypeArgContext:
|
case DeclaratorContext::TemplateTypeArgContext:
|
||||||
case DeclaratorContext::TypeNameContext:
|
case DeclaratorContext::TypeNameContext:
|
||||||
case DeclaratorContext::FunctionalCastContext:
|
case DeclaratorContext::FunctionalCastContext:
|
||||||
|
case DeclaratorContext::RequiresExprContext:
|
||||||
// Don't infer in these contexts.
|
// Don't infer in these contexts.
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -5227,6 +5232,7 @@ static TypeSourceInfo *GetFullTypeForDeclarator(TypeProcessingState &state,
|
|||||||
switch (D.getContext()) {
|
switch (D.getContext()) {
|
||||||
case DeclaratorContext::PrototypeContext:
|
case DeclaratorContext::PrototypeContext:
|
||||||
case DeclaratorContext::LambdaExprParameterContext:
|
case DeclaratorContext::LambdaExprParameterContext:
|
||||||
|
case DeclaratorContext::RequiresExprContext:
|
||||||
// C++0x [dcl.fct]p13:
|
// C++0x [dcl.fct]p13:
|
||||||
// [...] When it is part of a parameter-declaration-clause, the
|
// [...] When it is part of a parameter-declaration-clause, the
|
||||||
// parameter pack is a function parameter pack (14.5.3). The type T
|
// parameter pack is a function parameter pack (14.5.3). The type T
|
||||||
|
@ -19,6 +19,7 @@
|
|||||||
#include "clang/AST/DeclObjC.h"
|
#include "clang/AST/DeclObjC.h"
|
||||||
#include "clang/AST/DeclTemplate.h"
|
#include "clang/AST/DeclTemplate.h"
|
||||||
#include "clang/AST/Expr.h"
|
#include "clang/AST/Expr.h"
|
||||||
|
#include "clang/AST/ExprConcepts.h"
|
||||||
#include "clang/AST/ExprCXX.h"
|
#include "clang/AST/ExprCXX.h"
|
||||||
#include "clang/AST/ExprObjC.h"
|
#include "clang/AST/ExprObjC.h"
|
||||||
#include "clang/AST/ExprOpenMP.h"
|
#include "clang/AST/ExprOpenMP.h"
|
||||||
@ -509,6 +510,15 @@ public:
|
|||||||
DeclarationNameInfo
|
DeclarationNameInfo
|
||||||
TransformDeclarationNameInfo(const DeclarationNameInfo &NameInfo);
|
TransformDeclarationNameInfo(const DeclarationNameInfo &NameInfo);
|
||||||
|
|
||||||
|
bool TransformRequiresExprRequirements(ArrayRef<concepts::Requirement *> Reqs,
|
||||||
|
llvm::SmallVectorImpl<concepts::Requirement *> &Transformed);
|
||||||
|
concepts::TypeRequirement *
|
||||||
|
TransformTypeRequirement(concepts::TypeRequirement *Req);
|
||||||
|
concepts::ExprRequirement *
|
||||||
|
TransformExprRequirement(concepts::ExprRequirement *Req);
|
||||||
|
concepts::NestedRequirement *
|
||||||
|
TransformNestedRequirement(concepts::NestedRequirement *Req);
|
||||||
|
|
||||||
/// Transform the given template name.
|
/// Transform the given template name.
|
||||||
///
|
///
|
||||||
/// \param SS The nested-name-specifier that qualifies the template
|
/// \param SS The nested-name-specifier that qualifies the template
|
||||||
@ -1056,23 +1066,8 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (Keyword == ETK_None || Keyword == ETK_Typename) {
|
if (Keyword == ETK_None || Keyword == ETK_Typename) {
|
||||||
QualType T = SemaRef.CheckTypenameType(Keyword, KeywordLoc, QualifierLoc,
|
return SemaRef.CheckTypenameType(Keyword, KeywordLoc, QualifierLoc,
|
||||||
*Id, IdLoc);
|
*Id, IdLoc, DeducedTSTContext);
|
||||||
// If a dependent name resolves to a deduced template specialization type,
|
|
||||||
// check that we're in one of the syntactic contexts permitting it.
|
|
||||||
if (!DeducedTSTContext) {
|
|
||||||
if (auto *Deduced = dyn_cast_or_null<DeducedTemplateSpecializationType>(
|
|
||||||
T.isNull() ? nullptr : T->getContainedDeducedType())) {
|
|
||||||
SemaRef.Diag(IdLoc, diag::err_dependent_deduced_tst)
|
|
||||||
<< (int)SemaRef.getTemplateNameKindForDiagnostics(
|
|
||||||
Deduced->getTemplateName())
|
|
||||||
<< QualType(QualifierLoc.getNestedNameSpecifier()->getAsType(), 0);
|
|
||||||
if (auto *TD = Deduced->getTemplateName().getAsTemplateDecl())
|
|
||||||
SemaRef.Diag(TD->getLocation(), diag::note_template_decl_here);
|
|
||||||
return QualType();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return T;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TagTypeKind Kind = TypeWithKeyword::getTagTypeKindForKeyword(Keyword);
|
TagTypeKind Kind = TypeWithKeyword::getTagTypeKindForKeyword(Keyword);
|
||||||
@ -3078,7 +3073,56 @@ public:
|
|||||||
return Result;
|
return Result;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// \brief Build a new Objective-C boxed expression.
|
/// \brief Build a new requires expression.
|
||||||
|
///
|
||||||
|
/// By default, performs semantic analysis to build the new expression.
|
||||||
|
/// Subclasses may override this routine to provide different behavior.
|
||||||
|
ExprResult RebuildRequiresExpr(SourceLocation RequiresKWLoc,
|
||||||
|
RequiresExprBodyDecl *Body,
|
||||||
|
ArrayRef<ParmVarDecl *> LocalParameters,
|
||||||
|
ArrayRef<concepts::Requirement *> Requirements,
|
||||||
|
SourceLocation ClosingBraceLoc) {
|
||||||
|
return RequiresExpr::Create(SemaRef.Context, RequiresKWLoc, Body,
|
||||||
|
LocalParameters, Requirements, ClosingBraceLoc);
|
||||||
|
}
|
||||||
|
|
||||||
|
concepts::TypeRequirement *
|
||||||
|
RebuildTypeRequirement(
|
||||||
|
concepts::Requirement::SubstitutionDiagnostic *SubstDiag) {
|
||||||
|
return SemaRef.BuildTypeRequirement(SubstDiag);
|
||||||
|
}
|
||||||
|
|
||||||
|
concepts::TypeRequirement *RebuildTypeRequirement(TypeSourceInfo *T) {
|
||||||
|
return SemaRef.BuildTypeRequirement(T);
|
||||||
|
}
|
||||||
|
|
||||||
|
concepts::ExprRequirement *
|
||||||
|
RebuildExprRequirement(
|
||||||
|
concepts::Requirement::SubstitutionDiagnostic *SubstDiag, bool IsSimple,
|
||||||
|
SourceLocation NoexceptLoc,
|
||||||
|
concepts::ExprRequirement::ReturnTypeRequirement Ret) {
|
||||||
|
return SemaRef.BuildExprRequirement(SubstDiag, IsSimple, NoexceptLoc,
|
||||||
|
std::move(Ret));
|
||||||
|
}
|
||||||
|
|
||||||
|
concepts::ExprRequirement *
|
||||||
|
RebuildExprRequirement(Expr *E, bool IsSimple, SourceLocation NoexceptLoc,
|
||||||
|
concepts::ExprRequirement::ReturnTypeRequirement Ret) {
|
||||||
|
return SemaRef.BuildExprRequirement(E, IsSimple, NoexceptLoc,
|
||||||
|
std::move(Ret));
|
||||||
|
}
|
||||||
|
|
||||||
|
concepts::NestedRequirement *
|
||||||
|
RebuildNestedRequirement(
|
||||||
|
concepts::Requirement::SubstitutionDiagnostic *SubstDiag) {
|
||||||
|
return SemaRef.BuildNestedRequirement(SubstDiag);
|
||||||
|
}
|
||||||
|
|
||||||
|
concepts::NestedRequirement *RebuildNestedRequirement(Expr *Constraint) {
|
||||||
|
return SemaRef.BuildNestedRequirement(Constraint);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// \brief Build a new Objective-C boxed expression.
|
||||||
///
|
///
|
||||||
/// By default, performs semantic analysis to build the new expression.
|
/// By default, performs semantic analysis to build the new expression.
|
||||||
/// Subclasses may override this routine to provide different behavior.
|
/// Subclasses may override this routine to provide different behavior.
|
||||||
@ -11179,6 +11223,146 @@ TreeTransform<Derived>::TransformConceptSpecializationExpr(
|
|||||||
&TransArgs);
|
&TransArgs);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template<typename Derived>
|
||||||
|
ExprResult
|
||||||
|
TreeTransform<Derived>::TransformRequiresExpr(RequiresExpr *E) {
|
||||||
|
SmallVector<ParmVarDecl*, 4> TransParams;
|
||||||
|
SmallVector<QualType, 4> TransParamTypes;
|
||||||
|
Sema::ExtParameterInfoBuilder ExtParamInfos;
|
||||||
|
|
||||||
|
// C++2a [expr.prim.req]p2
|
||||||
|
// Expressions appearing within a requirement-body are unevaluated operands.
|
||||||
|
EnterExpressionEvaluationContext Ctx(
|
||||||
|
SemaRef, Sema::ExpressionEvaluationContext::Unevaluated);
|
||||||
|
|
||||||
|
RequiresExprBodyDecl *Body = RequiresExprBodyDecl::Create(
|
||||||
|
getSema().Context, E->getBody()->getDeclContext(),
|
||||||
|
E->getBody()->getBeginLoc());
|
||||||
|
|
||||||
|
Sema::ContextRAII SavedContext(getSema(), Body, /*NewThisContext*/false);
|
||||||
|
|
||||||
|
if (getDerived().TransformFunctionTypeParams(E->getRequiresKWLoc(),
|
||||||
|
E->getLocalParameters(),
|
||||||
|
/*ParamTypes=*/nullptr,
|
||||||
|
/*ParamInfos=*/nullptr,
|
||||||
|
TransParamTypes, &TransParams,
|
||||||
|
ExtParamInfos))
|
||||||
|
return ExprError();
|
||||||
|
|
||||||
|
for (ParmVarDecl *Param : TransParams)
|
||||||
|
Param->setDeclContext(Body);
|
||||||
|
|
||||||
|
SmallVector<concepts::Requirement *, 4> TransReqs;
|
||||||
|
if (getDerived().TransformRequiresExprRequirements(E->getRequirements(),
|
||||||
|
TransReqs))
|
||||||
|
return ExprError();
|
||||||
|
|
||||||
|
for (concepts::Requirement *Req : TransReqs) {
|
||||||
|
if (auto *ER = dyn_cast<concepts::ExprRequirement>(Req)) {
|
||||||
|
if (ER->getReturnTypeRequirement().isTypeConstraint()) {
|
||||||
|
ER->getReturnTypeRequirement()
|
||||||
|
.getTypeConstraintTemplateParameterList()->getParam(0)
|
||||||
|
->setDeclContext(Body);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return getDerived().RebuildRequiresExpr(E->getRequiresKWLoc(), Body,
|
||||||
|
TransParams, TransReqs,
|
||||||
|
E->getRBraceLoc());
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename Derived>
|
||||||
|
bool TreeTransform<Derived>::TransformRequiresExprRequirements(
|
||||||
|
ArrayRef<concepts::Requirement *> Reqs,
|
||||||
|
SmallVectorImpl<concepts::Requirement *> &Transformed) {
|
||||||
|
for (concepts::Requirement *Req : Reqs) {
|
||||||
|
concepts::Requirement *TransReq = nullptr;
|
||||||
|
if (auto *TypeReq = dyn_cast<concepts::TypeRequirement>(Req))
|
||||||
|
TransReq = getDerived().TransformTypeRequirement(TypeReq);
|
||||||
|
else if (auto *ExprReq = dyn_cast<concepts::ExprRequirement>(Req))
|
||||||
|
TransReq = getDerived().TransformExprRequirement(ExprReq);
|
||||||
|
else
|
||||||
|
TransReq = getDerived().TransformNestedRequirement(
|
||||||
|
cast<concepts::NestedRequirement>(Req));
|
||||||
|
if (!TransReq)
|
||||||
|
return true;
|
||||||
|
Transformed.push_back(TransReq);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename Derived>
|
||||||
|
concepts::TypeRequirement *
|
||||||
|
TreeTransform<Derived>::TransformTypeRequirement(
|
||||||
|
concepts::TypeRequirement *Req) {
|
||||||
|
if (Req->isSubstitutionFailure()) {
|
||||||
|
if (getDerived().AlwaysRebuild())
|
||||||
|
return getDerived().RebuildTypeRequirement(
|
||||||
|
Req->getSubstitutionDiagnostic());
|
||||||
|
return Req;
|
||||||
|
}
|
||||||
|
TypeSourceInfo *TransType = getDerived().TransformType(Req->getType());
|
||||||
|
if (!TransType)
|
||||||
|
return nullptr;
|
||||||
|
return getDerived().RebuildTypeRequirement(TransType);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename Derived>
|
||||||
|
concepts::ExprRequirement *
|
||||||
|
TreeTransform<Derived>::TransformExprRequirement(concepts::ExprRequirement *Req) {
|
||||||
|
llvm::PointerUnion<Expr *, concepts::Requirement::SubstitutionDiagnostic *> TransExpr;
|
||||||
|
if (Req->isExprSubstitutionFailure())
|
||||||
|
TransExpr = Req->getExprSubstitutionDiagnostic();
|
||||||
|
else {
|
||||||
|
ExprResult TransExprRes = getDerived().TransformExpr(Req->getExpr());
|
||||||
|
if (TransExprRes.isInvalid())
|
||||||
|
return nullptr;
|
||||||
|
TransExpr = TransExprRes.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
llvm::Optional<concepts::ExprRequirement::ReturnTypeRequirement> TransRetReq;
|
||||||
|
const auto &RetReq = Req->getReturnTypeRequirement();
|
||||||
|
if (RetReq.isEmpty())
|
||||||
|
TransRetReq.emplace();
|
||||||
|
else if (RetReq.isSubstitutionFailure())
|
||||||
|
TransRetReq.emplace(RetReq.getSubstitutionDiagnostic());
|
||||||
|
else if (RetReq.isTypeConstraint()) {
|
||||||
|
TemplateParameterList *OrigTPL =
|
||||||
|
RetReq.getTypeConstraintTemplateParameterList();
|
||||||
|
TemplateParameterList *TPL =
|
||||||
|
getDerived().TransformTemplateParameterList(OrigTPL);
|
||||||
|
if (!TPL)
|
||||||
|
return nullptr;
|
||||||
|
TransRetReq.emplace(TPL);
|
||||||
|
}
|
||||||
|
assert(TransRetReq.hasValue() &&
|
||||||
|
"All code paths leading here must set TransRetReq");
|
||||||
|
if (Expr *E = TransExpr.dyn_cast<Expr *>())
|
||||||
|
return getDerived().RebuildExprRequirement(E, Req->isSimple(),
|
||||||
|
Req->getNoexceptLoc(),
|
||||||
|
std::move(*TransRetReq));
|
||||||
|
return getDerived().RebuildExprRequirement(
|
||||||
|
TransExpr.get<concepts::Requirement::SubstitutionDiagnostic *>(),
|
||||||
|
Req->isSimple(), Req->getNoexceptLoc(), std::move(*TransRetReq));
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename Derived>
|
||||||
|
concepts::NestedRequirement *
|
||||||
|
TreeTransform<Derived>::TransformNestedRequirement(
|
||||||
|
concepts::NestedRequirement *Req) {
|
||||||
|
if (Req->isSubstitutionFailure()) {
|
||||||
|
if (getDerived().AlwaysRebuild())
|
||||||
|
return getDerived().RebuildNestedRequirement(
|
||||||
|
Req->getSubstitutionDiagnostic());
|
||||||
|
return Req;
|
||||||
|
}
|
||||||
|
ExprResult TransConstraint =
|
||||||
|
getDerived().TransformExpr(Req->getConstraintExpr());
|
||||||
|
if (TransConstraint.isInvalid())
|
||||||
|
return nullptr;
|
||||||
|
return getDerived().RebuildNestedRequirement(TransConstraint.get());
|
||||||
|
}
|
||||||
|
|
||||||
template<typename Derived>
|
template<typename Derived>
|
||||||
ExprResult
|
ExprResult
|
||||||
|
@ -402,6 +402,7 @@ bool serialization::isRedeclarableDeclKind(unsigned Kind) {
|
|||||||
case Decl::Binding:
|
case Decl::Binding:
|
||||||
case Decl::Concept:
|
case Decl::Concept:
|
||||||
case Decl::LifetimeExtendedTemporary:
|
case Decl::LifetimeExtendedTemporary:
|
||||||
|
case Decl::RequiresExprBody:
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
// These indirectly derive from Redeclarable<T> but are not actually
|
// These indirectly derive from Redeclarable<T> but are not actually
|
||||||
|
@ -375,6 +375,7 @@ namespace clang {
|
|||||||
void VisitNonTypeTemplateParmDecl(NonTypeTemplateParmDecl *D);
|
void VisitNonTypeTemplateParmDecl(NonTypeTemplateParmDecl *D);
|
||||||
DeclID VisitTemplateDecl(TemplateDecl *D);
|
DeclID VisitTemplateDecl(TemplateDecl *D);
|
||||||
void VisitConceptDecl(ConceptDecl *D);
|
void VisitConceptDecl(ConceptDecl *D);
|
||||||
|
void VisitRequiresExprBodyDecl(RequiresExprBodyDecl *D);
|
||||||
RedeclarableResult VisitRedeclarableTemplateDecl(RedeclarableTemplateDecl *D);
|
RedeclarableResult VisitRedeclarableTemplateDecl(RedeclarableTemplateDecl *D);
|
||||||
void VisitClassTemplateDecl(ClassTemplateDecl *D);
|
void VisitClassTemplateDecl(ClassTemplateDecl *D);
|
||||||
void VisitBuiltinTemplateDecl(BuiltinTemplateDecl *D);
|
void VisitBuiltinTemplateDecl(BuiltinTemplateDecl *D);
|
||||||
@ -2037,6 +2038,9 @@ void ASTDeclReader::VisitConceptDecl(ConceptDecl *D) {
|
|||||||
mergeMergeable(D);
|
mergeMergeable(D);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ASTDeclReader::VisitRequiresExprBodyDecl(RequiresExprBodyDecl *D) {
|
||||||
|
}
|
||||||
|
|
||||||
ASTDeclReader::RedeclarableResult
|
ASTDeclReader::RedeclarableResult
|
||||||
ASTDeclReader::VisitRedeclarableTemplateDecl(RedeclarableTemplateDecl *D) {
|
ASTDeclReader::VisitRedeclarableTemplateDecl(RedeclarableTemplateDecl *D) {
|
||||||
RedeclarableResult Redecl = VisitRedeclarable(D);
|
RedeclarableResult Redecl = VisitRedeclarable(D);
|
||||||
@ -3839,6 +3843,9 @@ Decl *ASTReader::ReadDeclRecord(DeclID ID) {
|
|||||||
case DECL_CONCEPT:
|
case DECL_CONCEPT:
|
||||||
D = ConceptDecl::CreateDeserialized(Context, ID);
|
D = ConceptDecl::CreateDeserialized(Context, ID);
|
||||||
break;
|
break;
|
||||||
|
case DECL_REQUIRES_EXPR_BODY:
|
||||||
|
D = RequiresExprBodyDecl::CreateDeserialized(Context, ID);
|
||||||
|
break;
|
||||||
case DECL_STATIC_ASSERT:
|
case DECL_STATIC_ASSERT:
|
||||||
D = StaticAssertDecl::CreateDeserialized(Context, ID);
|
D = StaticAssertDecl::CreateDeserialized(Context, ID);
|
||||||
break;
|
break;
|
||||||
|
@ -724,6 +724,28 @@ void ASTStmtReader::VisitUnaryExprOrTypeTraitExpr(UnaryExprOrTypeTraitExpr *E) {
|
|||||||
E->setRParenLoc(readSourceLocation());
|
E->setRParenLoc(readSourceLocation());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static ConstraintSatisfaction
|
||||||
|
readConstraintSatisfaction(ASTRecordReader &Record) {
|
||||||
|
ConstraintSatisfaction Satisfaction;
|
||||||
|
Satisfaction.IsSatisfied = Record.readInt();
|
||||||
|
if (!Satisfaction.IsSatisfied) {
|
||||||
|
unsigned NumDetailRecords = Record.readInt();
|
||||||
|
for (unsigned i = 0; i != NumDetailRecords; ++i) {
|
||||||
|
Expr *ConstraintExpr = Record.readExpr();
|
||||||
|
if (bool IsDiagnostic = Record.readInt()) {
|
||||||
|
SourceLocation DiagLocation = Record.readSourceLocation();
|
||||||
|
std::string DiagMessage = Record.readString();
|
||||||
|
Satisfaction.Details.emplace_back(
|
||||||
|
ConstraintExpr, new (Record.getContext())
|
||||||
|
ConstraintSatisfaction::SubstitutionDiagnostic{
|
||||||
|
DiagLocation, DiagMessage});
|
||||||
|
} else
|
||||||
|
Satisfaction.Details.emplace_back(ConstraintExpr, Record.readExpr());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Satisfaction;
|
||||||
|
}
|
||||||
|
|
||||||
void ASTStmtReader::VisitConceptSpecializationExpr(
|
void ASTStmtReader::VisitConceptSpecializationExpr(
|
||||||
ConceptSpecializationExpr *E) {
|
ConceptSpecializationExpr *E) {
|
||||||
VisitExpr(E);
|
VisitExpr(E);
|
||||||
@ -737,26 +759,121 @@ void ASTStmtReader::VisitConceptSpecializationExpr(
|
|||||||
for (unsigned I = 0; I < NumTemplateArgs; ++I)
|
for (unsigned I = 0; I < NumTemplateArgs; ++I)
|
||||||
Args.push_back(Record.readTemplateArgument());
|
Args.push_back(Record.readTemplateArgument());
|
||||||
E->setTemplateArguments(Args);
|
E->setTemplateArguments(Args);
|
||||||
ConstraintSatisfaction Satisfaction;
|
E->Satisfaction = E->isValueDependent() ? nullptr :
|
||||||
Satisfaction.IsSatisfied = Record.readInt();
|
ASTConstraintSatisfaction::Create(Record.getContext(),
|
||||||
if (!Satisfaction.IsSatisfied) {
|
readConstraintSatisfaction(Record));
|
||||||
unsigned NumDetailRecords = Record.readInt();
|
}
|
||||||
for (unsigned i = 0; i != NumDetailRecords; ++i) {
|
|
||||||
Expr *ConstraintExpr = Record.readExpr();
|
static concepts::Requirement::SubstitutionDiagnostic *
|
||||||
bool IsDiagnostic = Record.readInt();
|
readSubstitutionDiagnostic(ASTRecordReader &Record) {
|
||||||
if (IsDiagnostic) {
|
std::string SubstitutedEntity = Record.readString();
|
||||||
SourceLocation DiagLocation = Record.readSourceLocation();
|
SourceLocation DiagLoc = Record.readSourceLocation();
|
||||||
std::string DiagMessage = Record.readString();
|
std::string DiagMessage = Record.readString();
|
||||||
Satisfaction.Details.emplace_back(
|
return new (Record.getContext())
|
||||||
ConstraintExpr, new (Record.getContext())
|
concepts::Requirement::SubstitutionDiagnostic{SubstitutedEntity, DiagLoc,
|
||||||
ConstraintSatisfaction::SubstitutionDiagnostic{
|
DiagMessage};
|
||||||
DiagLocation, DiagMessage});
|
}
|
||||||
} else
|
|
||||||
Satisfaction.Details.emplace_back(ConstraintExpr, Record.readExpr());
|
void ASTStmtReader::VisitRequiresExpr(RequiresExpr *E) {
|
||||||
|
VisitExpr(E);
|
||||||
|
unsigned NumLocalParameters = Record.readInt();
|
||||||
|
unsigned NumRequirements = Record.readInt();
|
||||||
|
E->RequiresExprBits.RequiresKWLoc = Record.readSourceLocation();
|
||||||
|
E->RequiresExprBits.IsSatisfied = Record.readInt();
|
||||||
|
E->Body = Record.readDeclAs<RequiresExprBodyDecl>();
|
||||||
|
llvm::SmallVector<ParmVarDecl *, 4> LocalParameters;
|
||||||
|
for (unsigned i = 0; i < NumLocalParameters; ++i)
|
||||||
|
LocalParameters.push_back(cast<ParmVarDecl>(Record.readDecl()));
|
||||||
|
std::copy(LocalParameters.begin(), LocalParameters.end(),
|
||||||
|
E->getTrailingObjects<ParmVarDecl *>());
|
||||||
|
llvm::SmallVector<concepts::Requirement *, 4> Requirements;
|
||||||
|
for (unsigned i = 0; i < NumRequirements; ++i) {
|
||||||
|
auto RK =
|
||||||
|
static_cast<concepts::Requirement::RequirementKind>(Record.readInt());
|
||||||
|
concepts::Requirement *R = nullptr;
|
||||||
|
switch (RK) {
|
||||||
|
case concepts::Requirement::RK_Type: {
|
||||||
|
auto Status =
|
||||||
|
static_cast<concepts::TypeRequirement::SatisfactionStatus>(
|
||||||
|
Record.readInt());
|
||||||
|
if (Status == concepts::TypeRequirement::SS_SubstitutionFailure)
|
||||||
|
R = new (Record.getContext())
|
||||||
|
concepts::TypeRequirement(readSubstitutionDiagnostic(Record));
|
||||||
|
else
|
||||||
|
R = new (Record.getContext())
|
||||||
|
concepts::TypeRequirement(Record.readTypeSourceInfo());
|
||||||
|
} break;
|
||||||
|
case concepts::Requirement::RK_Simple:
|
||||||
|
case concepts::Requirement::RK_Compound: {
|
||||||
|
auto Status =
|
||||||
|
static_cast<concepts::ExprRequirement::SatisfactionStatus>(
|
||||||
|
Record.readInt());
|
||||||
|
llvm::PointerUnion<concepts::Requirement::SubstitutionDiagnostic *,
|
||||||
|
Expr *> E;
|
||||||
|
if (Status == concepts::ExprRequirement::SS_ExprSubstitutionFailure) {
|
||||||
|
E = readSubstitutionDiagnostic(Record);
|
||||||
|
} else
|
||||||
|
E = Record.readExpr();
|
||||||
|
|
||||||
|
llvm::Optional<concepts::ExprRequirement::ReturnTypeRequirement> Req;
|
||||||
|
ConceptSpecializationExpr *SubstitutedConstraintExpr = nullptr;
|
||||||
|
SourceLocation NoexceptLoc;
|
||||||
|
if (RK == concepts::Requirement::RK_Simple) {
|
||||||
|
Req.emplace();
|
||||||
|
} else {
|
||||||
|
NoexceptLoc = Record.readSourceLocation();
|
||||||
|
switch (auto returnTypeRequirementKind = Record.readInt()) {
|
||||||
|
case 0:
|
||||||
|
// No return type requirement.
|
||||||
|
Req.emplace();
|
||||||
|
break;
|
||||||
|
case 1: {
|
||||||
|
// type-constraint
|
||||||
|
TemplateParameterList *TPL = Record.readTemplateParameterList();
|
||||||
|
if (Status >=
|
||||||
|
concepts::ExprRequirement::SS_ConstraintsNotSatisfied)
|
||||||
|
SubstitutedConstraintExpr =
|
||||||
|
cast<ConceptSpecializationExpr>(Record.readExpr());
|
||||||
|
Req.emplace(TPL);
|
||||||
|
} break;
|
||||||
|
case 2:
|
||||||
|
// Substitution failure
|
||||||
|
Req.emplace(readSubstitutionDiagnostic(Record));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (Expr *Ex = E.dyn_cast<Expr *>())
|
||||||
|
R = new (Record.getContext()) concepts::ExprRequirement(
|
||||||
|
Ex, RK == concepts::Requirement::RK_Simple, NoexceptLoc,
|
||||||
|
std::move(*Req), Status, SubstitutedConstraintExpr);
|
||||||
|
else
|
||||||
|
R = new (Record.getContext()) concepts::ExprRequirement(
|
||||||
|
E.get<concepts::Requirement::SubstitutionDiagnostic *>(),
|
||||||
|
RK == concepts::Requirement::RK_Simple, NoexceptLoc,
|
||||||
|
std::move(*Req));
|
||||||
|
} break;
|
||||||
|
case concepts::Requirement::RK_Nested: {
|
||||||
|
if (bool IsSubstitutionDiagnostic = Record.readInt()) {
|
||||||
|
R = new (Record.getContext()) concepts::NestedRequirement(
|
||||||
|
readSubstitutionDiagnostic(Record));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
Expr *E = Record.readExpr();
|
||||||
|
if (E->isInstantiationDependent())
|
||||||
|
R = new (Record.getContext()) concepts::NestedRequirement(E);
|
||||||
|
else
|
||||||
|
R = new (Record.getContext())
|
||||||
|
concepts::NestedRequirement(Record.getContext(), E,
|
||||||
|
readConstraintSatisfaction(Record));
|
||||||
|
} break;
|
||||||
}
|
}
|
||||||
|
if (!R)
|
||||||
|
continue;
|
||||||
|
Requirements.push_back(R);
|
||||||
}
|
}
|
||||||
E->Satisfaction = ASTConstraintSatisfaction::Create(Record.getContext(),
|
std::copy(Requirements.begin(), Requirements.end(),
|
||||||
Satisfaction);
|
E->getTrailingObjects<concepts::Requirement *>());
|
||||||
|
E->RBraceLoc = Record.readSourceLocation();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ASTStmtReader::VisitArraySubscriptExpr(ArraySubscriptExpr *E) {
|
void ASTStmtReader::VisitArraySubscriptExpr(ArraySubscriptExpr *E) {
|
||||||
@ -3566,11 +3683,18 @@ Stmt *ASTReader::ReadStmtFromStream(ModuleFile &F) {
|
|||||||
S = new (Context) DependentCoawaitExpr(Empty);
|
S = new (Context) DependentCoawaitExpr(Empty);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case EXPR_CONCEPT_SPECIALIZATION:
|
case EXPR_CONCEPT_SPECIALIZATION: {
|
||||||
unsigned numTemplateArgs = Record[ASTStmtReader::NumExprFields];
|
unsigned numTemplateArgs = Record[ASTStmtReader::NumExprFields];
|
||||||
S = ConceptSpecializationExpr::Create(Context, Empty, numTemplateArgs);
|
S = ConceptSpecializationExpr::Create(Context, Empty, numTemplateArgs);
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case EXPR_REQUIRES:
|
||||||
|
unsigned numLocalParameters = Record[ASTStmtReader::NumExprFields];
|
||||||
|
unsigned numRequirement = Record[ASTStmtReader::NumExprFields + 1];
|
||||||
|
S = RequiresExpr::Create(Context, Empty, numLocalParameters,
|
||||||
|
numRequirement);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// We hit a STMT_STOP, so we're done with this expression.
|
// We hit a STMT_STOP, so we're done with this expression.
|
||||||
|
@ -885,6 +885,7 @@ void ASTWriter::WriteBlockInfoBlock() {
|
|||||||
RECORD(DECL_NON_TYPE_TEMPLATE_PARM);
|
RECORD(DECL_NON_TYPE_TEMPLATE_PARM);
|
||||||
RECORD(DECL_TEMPLATE_TEMPLATE_PARM);
|
RECORD(DECL_TEMPLATE_TEMPLATE_PARM);
|
||||||
RECORD(DECL_CONCEPT);
|
RECORD(DECL_CONCEPT);
|
||||||
|
RECORD(DECL_REQUIRES_EXPR_BODY);
|
||||||
RECORD(DECL_TYPE_ALIAS_TEMPLATE);
|
RECORD(DECL_TYPE_ALIAS_TEMPLATE);
|
||||||
RECORD(DECL_STATIC_ASSERT);
|
RECORD(DECL_STATIC_ASSERT);
|
||||||
RECORD(DECL_CXX_BASE_SPECIFIERS);
|
RECORD(DECL_CXX_BASE_SPECIFIERS);
|
||||||
|
@ -104,6 +104,7 @@ namespace clang {
|
|||||||
void VisitNonTypeTemplateParmDecl(NonTypeTemplateParmDecl *D);
|
void VisitNonTypeTemplateParmDecl(NonTypeTemplateParmDecl *D);
|
||||||
void VisitTemplateDecl(TemplateDecl *D);
|
void VisitTemplateDecl(TemplateDecl *D);
|
||||||
void VisitConceptDecl(ConceptDecl *D);
|
void VisitConceptDecl(ConceptDecl *D);
|
||||||
|
void VisitRequiresExprBodyDecl(RequiresExprBodyDecl *D);
|
||||||
void VisitRedeclarableTemplateDecl(RedeclarableTemplateDecl *D);
|
void VisitRedeclarableTemplateDecl(RedeclarableTemplateDecl *D);
|
||||||
void VisitClassTemplateDecl(ClassTemplateDecl *D);
|
void VisitClassTemplateDecl(ClassTemplateDecl *D);
|
||||||
void VisitVarTemplateDecl(VarTemplateDecl *D);
|
void VisitVarTemplateDecl(VarTemplateDecl *D);
|
||||||
@ -1481,6 +1482,10 @@ void ASTDeclWriter::VisitConceptDecl(ConceptDecl *D) {
|
|||||||
Code = serialization::DECL_CONCEPT;
|
Code = serialization::DECL_CONCEPT;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ASTDeclWriter::VisitRequiresExprBodyDecl(RequiresExprBodyDecl *D) {
|
||||||
|
Code = serialization::DECL_REQUIRES_EXPR_BODY;
|
||||||
|
}
|
||||||
|
|
||||||
void ASTDeclWriter::VisitRedeclarableTemplateDecl(RedeclarableTemplateDecl *D) {
|
void ASTDeclWriter::VisitRedeclarableTemplateDecl(RedeclarableTemplateDecl *D) {
|
||||||
VisitRedeclarable(D);
|
VisitRedeclarable(D);
|
||||||
|
|
||||||
|
@ -12,6 +12,7 @@
|
|||||||
//===----------------------------------------------------------------------===//
|
//===----------------------------------------------------------------------===//
|
||||||
|
|
||||||
#include "clang/Serialization/ASTRecordWriter.h"
|
#include "clang/Serialization/ASTRecordWriter.h"
|
||||||
|
#include "clang/Sema/DeclSpec.h"
|
||||||
#include "clang/AST/ASTContext.h"
|
#include "clang/AST/ASTContext.h"
|
||||||
#include "clang/AST/DeclCXX.h"
|
#include "clang/AST/DeclCXX.h"
|
||||||
#include "clang/AST/DeclObjC.h"
|
#include "clang/AST/DeclObjC.h"
|
||||||
@ -388,19 +389,9 @@ void ASTStmtWriter::VisitDependentCoawaitExpr(DependentCoawaitExpr *E) {
|
|||||||
Code = serialization::EXPR_DEPENDENT_COAWAIT;
|
Code = serialization::EXPR_DEPENDENT_COAWAIT;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ASTStmtWriter::VisitConceptSpecializationExpr(
|
static void
|
||||||
ConceptSpecializationExpr *E) {
|
addConstraintSatisfaction(ASTRecordWriter &Record,
|
||||||
VisitExpr(E);
|
const ASTConstraintSatisfaction &Satisfaction) {
|
||||||
ArrayRef<TemplateArgument> TemplateArgs = E->getTemplateArguments();
|
|
||||||
Record.push_back(TemplateArgs.size());
|
|
||||||
Record.AddNestedNameSpecifierLoc(E->getNestedNameSpecifierLoc());
|
|
||||||
Record.AddSourceLocation(E->getTemplateKWLoc());
|
|
||||||
Record.AddDeclarationNameInfo(E->getConceptNameInfo());
|
|
||||||
Record.AddDeclRef(E->getNamedConcept());
|
|
||||||
Record.AddASTTemplateArgumentListInfo(E->getTemplateArgsAsWritten());
|
|
||||||
for (const TemplateArgument &Arg : TemplateArgs)
|
|
||||||
Record.AddTemplateArgument(Arg);
|
|
||||||
const ASTConstraintSatisfaction &Satisfaction = E->getSatisfaction();
|
|
||||||
Record.push_back(Satisfaction.IsSatisfied);
|
Record.push_back(Satisfaction.IsSatisfied);
|
||||||
if (!Satisfaction.IsSatisfied) {
|
if (!Satisfaction.IsSatisfied) {
|
||||||
Record.push_back(Satisfaction.NumRecords);
|
Record.push_back(Satisfaction.NumRecords);
|
||||||
@ -418,10 +409,98 @@ void ASTStmtWriter::VisitConceptSpecializationExpr(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
addSubstitutionDiagnostic(
|
||||||
|
ASTRecordWriter &Record,
|
||||||
|
const concepts::Requirement::SubstitutionDiagnostic *D) {
|
||||||
|
Record.AddString(D->SubstitutedEntity);
|
||||||
|
Record.AddSourceLocation(D->DiagLoc);
|
||||||
|
Record.AddString(D->DiagMessage);
|
||||||
|
}
|
||||||
|
|
||||||
|
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.AddDeclarationNameInfo(E->getConceptNameInfo());
|
||||||
|
Record.AddDeclRef(E->getNamedConcept());
|
||||||
|
Record.AddASTTemplateArgumentListInfo(E->getTemplateArgsAsWritten());
|
||||||
|
for (const TemplateArgument &Arg : TemplateArgs)
|
||||||
|
Record.AddTemplateArgument(Arg);
|
||||||
|
if (!E->isValueDependent())
|
||||||
|
addConstraintSatisfaction(Record, E->getSatisfaction());
|
||||||
|
|
||||||
Code = serialization::EXPR_CONCEPT_SPECIALIZATION;
|
Code = serialization::EXPR_CONCEPT_SPECIALIZATION;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ASTStmtWriter::VisitRequiresExpr(RequiresExpr *E) {
|
||||||
|
VisitExpr(E);
|
||||||
|
Record.push_back(E->getLocalParameters().size());
|
||||||
|
Record.push_back(E->getRequirements().size());
|
||||||
|
Record.AddSourceLocation(E->RequiresExprBits.RequiresKWLoc);
|
||||||
|
Record.push_back(E->RequiresExprBits.IsSatisfied);
|
||||||
|
Record.AddDeclRef(E->getBody());
|
||||||
|
for (ParmVarDecl *P : E->getLocalParameters())
|
||||||
|
Record.AddDeclRef(P);
|
||||||
|
for (concepts::Requirement *R : E->getRequirements()) {
|
||||||
|
if (auto *TypeReq = dyn_cast<concepts::TypeRequirement>(R)) {
|
||||||
|
Record.push_back(concepts::Requirement::RK_Type);
|
||||||
|
Record.push_back(TypeReq->Status);
|
||||||
|
if (TypeReq->Status == concepts::TypeRequirement::SS_SubstitutionFailure)
|
||||||
|
addSubstitutionDiagnostic(Record, TypeReq->getSubstitutionDiagnostic());
|
||||||
|
else
|
||||||
|
Record.AddTypeSourceInfo(TypeReq->getType());
|
||||||
|
} else if (auto *ExprReq = dyn_cast<concepts::ExprRequirement>(R)) {
|
||||||
|
Record.push_back(ExprReq->getKind());
|
||||||
|
Record.push_back(ExprReq->Status);
|
||||||
|
if (ExprReq->isExprSubstitutionFailure()) {
|
||||||
|
addSubstitutionDiagnostic(Record,
|
||||||
|
ExprReq->Value.get<concepts::Requirement::SubstitutionDiagnostic *>());
|
||||||
|
} else
|
||||||
|
Record.AddStmt(ExprReq->Value.get<Expr *>());
|
||||||
|
if (ExprReq->getKind() == concepts::Requirement::RK_Compound) {
|
||||||
|
Record.AddSourceLocation(ExprReq->NoexceptLoc);
|
||||||
|
const auto &RetReq = ExprReq->getReturnTypeRequirement();
|
||||||
|
if (RetReq.isSubstitutionFailure()) {
|
||||||
|
Record.push_back(2);
|
||||||
|
addSubstitutionDiagnostic(Record, RetReq.getSubstitutionDiagnostic());
|
||||||
|
} else if (RetReq.isTypeConstraint()) {
|
||||||
|
Record.push_back(1);
|
||||||
|
Record.AddTemplateParameterList(
|
||||||
|
RetReq.getTypeConstraintTemplateParameterList());
|
||||||
|
if (ExprReq->Status >=
|
||||||
|
concepts::ExprRequirement::SS_ConstraintsNotSatisfied)
|
||||||
|
Record.AddStmt(
|
||||||
|
ExprReq->getReturnTypeRequirementSubstitutedConstraintExpr());
|
||||||
|
} else {
|
||||||
|
assert(RetReq.isEmpty());
|
||||||
|
Record.push_back(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
auto *NestedReq = cast<concepts::NestedRequirement>(R);
|
||||||
|
Record.push_back(concepts::Requirement::RK_Nested);
|
||||||
|
Record.push_back(NestedReq->isSubstitutionFailure());
|
||||||
|
if (NestedReq->isSubstitutionFailure()){
|
||||||
|
addSubstitutionDiagnostic(Record,
|
||||||
|
NestedReq->getSubstitutionDiagnostic());
|
||||||
|
} else {
|
||||||
|
Record.AddStmt(NestedReq->Value.get<Expr *>());
|
||||||
|
if (!NestedReq->isDependent())
|
||||||
|
addConstraintSatisfaction(Record, *NestedReq->Satisfaction);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Record.AddSourceLocation(E->getEndLoc());
|
||||||
|
|
||||||
|
Code = serialization::EXPR_REQUIRES;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void ASTStmtWriter::VisitCapturedStmt(CapturedStmt *S) {
|
void ASTStmtWriter::VisitCapturedStmt(CapturedStmt *S) {
|
||||||
VisitStmt(S);
|
VisitStmt(S);
|
||||||
|
@ -1386,6 +1386,7 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred,
|
|||||||
case Stmt::AsTypeExprClass:
|
case Stmt::AsTypeExprClass:
|
||||||
case Stmt::ConceptSpecializationExprClass:
|
case Stmt::ConceptSpecializationExprClass:
|
||||||
case Stmt::CXXRewrittenBinaryOperatorClass:
|
case Stmt::CXXRewrittenBinaryOperatorClass:
|
||||||
|
case Stmt::RequiresExprClass:
|
||||||
// Fall through.
|
// Fall through.
|
||||||
|
|
||||||
// Cases we intentionally don't evaluate, since they don't need
|
// Cases we intentionally don't evaluate, since they don't need
|
||||||
|
@ -0,0 +1,175 @@
|
|||||||
|
// RUN: %clang_cc1 -std=c++2a -fconcepts-ts -x c++ %s -verify
|
||||||
|
|
||||||
|
static_assert(requires { { 0 }; });
|
||||||
|
static_assert(requires { { "aaaa" }; });
|
||||||
|
static_assert(requires { { (0).da }; }); // expected-error{{member reference base type 'int' is not a structure or union}}
|
||||||
|
|
||||||
|
void foo() {}
|
||||||
|
static_assert(requires { { foo() }; });
|
||||||
|
|
||||||
|
// Substitution failure in expression
|
||||||
|
|
||||||
|
struct A {};
|
||||||
|
struct B {
|
||||||
|
B operator+(const B &other) const { return other; }
|
||||||
|
};
|
||||||
|
struct C {
|
||||||
|
C operator+(C &other) const { return other; }
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename T> requires requires (T a, const T& b) { { a + b }; } // expected-note{{because 'a + b' would be invalid: invalid operands to binary expression ('A' and 'const A')}} expected-note{{because 'a + b' would be invalid: invalid operands to binary expression ('C' and 'const C')}}
|
||||||
|
struct r1 {};
|
||||||
|
|
||||||
|
using r1i1 = r1<int>;
|
||||||
|
using r1i2 = r1<A>; // expected-error{{constraints not satisfied for class template 'r1' [with T = A]}}
|
||||||
|
using r1i3 = r1<B>;
|
||||||
|
using r1i4 = r1<C>; // expected-error{{constraints not satisfied for class template 'r1' [with T = C]}}
|
||||||
|
|
||||||
|
struct D { void foo() {} };
|
||||||
|
|
||||||
|
template<typename T> requires requires (T a) { { a.foo() }; } // expected-note{{because 'a.foo()' would be invalid: no member named 'foo' in 'A'}} expected-note{{because 'a.foo()' would be invalid: member reference base type 'int' is not a structure or union}} expected-note{{because 'a.foo()' would be invalid: 'this' argument to member function 'foo' has type 'const D', but function is not marked const}}
|
||||||
|
struct r2 {};
|
||||||
|
|
||||||
|
using r2i1 = r2<int>; // expected-error{{constraints not satisfied for class template 'r2' [with T = int]}}
|
||||||
|
using r2i2 = r2<A>; // expected-error{{constraints not satisfied for class template 'r2' [with T = A]}}
|
||||||
|
using r2i3 = r2<D>;
|
||||||
|
using r2i4 = r2<const D>; // expected-error{{constraints not satisfied for class template 'r2' [with T = const D]}}
|
||||||
|
|
||||||
|
template<typename T> requires requires { { sizeof(T) }; } // expected-note{{because 'sizeof(T)' would be invalid: invalid application of 'sizeof' to an incomplete type 'void'}} expected-note{{because 'sizeof(T)' would be invalid: invalid application of 'sizeof' to an incomplete type 'nonexistent'}}
|
||||||
|
struct r3 {};
|
||||||
|
|
||||||
|
using r3i1 = r3<int>;
|
||||||
|
using r3i2 = r3<A>;
|
||||||
|
using r3i3 = r3<A &>;
|
||||||
|
using r3i4 = r3<void>; // expected-error{{constraints not satisfied for class template 'r3' [with T = void]}}
|
||||||
|
using r3i4 = r3<class nonexistent>; // expected-error{{constraints not satisfied for class template 'r3' [with T = nonexistent]}}
|
||||||
|
|
||||||
|
// Non-dependent expressions
|
||||||
|
|
||||||
|
template<typename T> requires requires (T t) { { 0 }; { "a" }; { (void)'a' }; }
|
||||||
|
struct r4 {};
|
||||||
|
|
||||||
|
using r4i1 = r4<int>;
|
||||||
|
using r4i2 = r4<int[10]>;
|
||||||
|
using r4i3 = r4<int(int)>;
|
||||||
|
|
||||||
|
// Noexcept requirement
|
||||||
|
void maythrow() { }
|
||||||
|
static_assert(!requires { { maythrow() } noexcept; });
|
||||||
|
static_assert(requires { { 1 } noexcept; });
|
||||||
|
|
||||||
|
struct E { void operator++(int) noexcept; };
|
||||||
|
struct F { void operator++(int); };
|
||||||
|
|
||||||
|
template<typename T> requires requires (T t) { { t++ } noexcept; } // expected-note{{because 't ++' may throw an exception}}
|
||||||
|
struct r5 {};
|
||||||
|
|
||||||
|
using r5i1 = r5<int>;
|
||||||
|
using r5i2 = r5<E>;
|
||||||
|
using r5i2 = r5<F>; // expected-error{{constraints not satisfied for class template 'r5' [with T = F]}}
|
||||||
|
|
||||||
|
template<typename T> requires requires (T t) { { t.foo() } noexcept; } // expected-note{{because 't.foo()' would be invalid: no member named 'foo' in 'E'}}
|
||||||
|
struct r6 {};
|
||||||
|
|
||||||
|
using r6i = r6<E>; // expected-error{{constraints not satisfied for class template 'r6' [with T = E]}}
|
||||||
|
|
||||||
|
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>;
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
concept Large = sizeof(T) >= 4; // expected-note{{because 'sizeof(short) >= 4' (2 >= 4) evaluated to false}}
|
||||||
|
|
||||||
|
template<typename T> requires requires (T t) { { t } -> Large; } // expected-note{{because 'decltype(t)' (aka 'short') does not satisfy 'Large':}}
|
||||||
|
struct r7 {};
|
||||||
|
|
||||||
|
using r7i1 = r7<int>;
|
||||||
|
using r7i2 = r7<short>; // expected-error{{constraints not satisfied for class template 'r7' [with T = short]}}
|
||||||
|
|
||||||
|
template<typename T> requires requires (T t) { { t } -> Same<T>; }
|
||||||
|
struct r8 {};
|
||||||
|
|
||||||
|
using r8i1 = r8<int>;
|
||||||
|
using r8i2 = r8<short*>;
|
||||||
|
|
||||||
|
// Substitution failure in type constraint
|
||||||
|
|
||||||
|
template<typename T> requires requires (T t) { { t } -> Same<typename T::type>; } // expected-note{{because 'Same<expr-type, typename T::type>' would be invalid: type 'int' cannot be used prior to '::' because it has no members}}
|
||||||
|
struct r9 {};
|
||||||
|
|
||||||
|
struct M { using type = M; };
|
||||||
|
|
||||||
|
using r9i1 = r9<M>;
|
||||||
|
using r9i2 = r9<int>; // expected-error{{constraints not satisfied for class template 'r9' [with T = int]}}
|
||||||
|
|
||||||
|
// Substitution failure in both expression and return type requirement
|
||||||
|
|
||||||
|
template<typename T> requires requires (T t) { { t.foo() } -> Same<typename T::type>; } // expected-note{{because 't.foo()' would be invalid: member reference base type 'int' is not a structure or union}}
|
||||||
|
struct r10 {};
|
||||||
|
|
||||||
|
using r10i = r10<int>; // expected-error{{constraints not satisfied for class template 'r10' [with T = int]}}
|
||||||
|
|
||||||
|
// Non-type concept in type constraint
|
||||||
|
|
||||||
|
template<int T>
|
||||||
|
concept IsEven = (T % 2) == 0;
|
||||||
|
|
||||||
|
template<typename T> requires requires (T t) { { t } -> IsEven; } // expected-error{{concept named in type constraint is not a type concept}}
|
||||||
|
struct r11 {};
|
||||||
|
|
||||||
|
// C++ [expr.prim.req.compound] Example
|
||||||
|
namespace std_example {
|
||||||
|
template<typename T> concept C1 =
|
||||||
|
requires(T x) {
|
||||||
|
{x++};
|
||||||
|
};
|
||||||
|
|
||||||
|
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_as = is_same_v<T, U>;
|
||||||
|
// expected-note@-1 {{because 'is_same_v<int, int *>' evaluated to false}}
|
||||||
|
|
||||||
|
static_assert(C1<int>);
|
||||||
|
static_assert(C1<int*>);
|
||||||
|
template<C1 T> struct C1_check {};
|
||||||
|
using c1c1 = C1_check<int>;
|
||||||
|
using c1c2 = C1_check<int[10]>;
|
||||||
|
|
||||||
|
template<typename T> concept C2 =
|
||||||
|
requires(T x) {
|
||||||
|
{*x} -> same_as<typename T::inner>;
|
||||||
|
// expected-note@-1{{because type constraint 'same_as<int, typename T2::inner>' was not satisfied:}}
|
||||||
|
// expected-note@-2{{because '*x' would be invalid: indirection requires pointer operand ('int' invalid)}}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct T1 {
|
||||||
|
using inner = int;
|
||||||
|
inner operator *() { return 0; }
|
||||||
|
};
|
||||||
|
struct T2 {
|
||||||
|
using inner = int *;
|
||||||
|
int operator *() { return 0; }
|
||||||
|
};
|
||||||
|
static_assert(C2<T1>);
|
||||||
|
template<C2 T> struct C2_check {}; // expected-note{{because 'int' does not satisfy 'C2'}} expected-note{{because 'std_example::T2' does not satisfy 'C2'}}
|
||||||
|
using c2c1 = C2_check<int>; // expected-error{{constraints not satisfied for class template 'C2_check' [with T = int]}}
|
||||||
|
using c2c2 = C2_check<T2>; // expected-error{{constraints not satisfied for class template 'C2_check' [with T = std_example::T2]}}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
void g(T t) noexcept(sizeof(T) == 1) {}
|
||||||
|
|
||||||
|
template<typename T> concept C5 =
|
||||||
|
requires(T x) {
|
||||||
|
{g(x)} noexcept; // expected-note{{because 'g(x)' may throw an exception}}
|
||||||
|
};
|
||||||
|
|
||||||
|
static_assert(C5<char>);
|
||||||
|
template<C5 T> struct C5_check {}; // expected-note{{because 'short' does not satisfy 'C5'}}
|
||||||
|
using c5 = C5_check<short>; // expected-error{{constraints not satisfied for class template 'C5_check' [with T = short]}}
|
||||||
|
}
|
125
clang/test/CXX/expr/expr.prim/expr.prim.req/equivalence.cpp
Normal file
125
clang/test/CXX/expr/expr.prim/expr.prim.req/equivalence.cpp
Normal file
@ -0,0 +1,125 @@
|
|||||||
|
// RUN: %clang_cc1 -std=c++2a -fconcepts-ts -x c++ %s -verify
|
||||||
|
|
||||||
|
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> struct identity { using type = T; };
|
||||||
|
template<typename T> using identity_t = T;
|
||||||
|
|
||||||
|
// Type requirements
|
||||||
|
template<typename T> requires requires { typename identity_t<T>; }
|
||||||
|
struct r1;
|
||||||
|
template<typename U> requires requires { typename identity_t<U>; } // expected-note{{previous template declaration is here}}
|
||||||
|
struct r1;
|
||||||
|
template<typename T> requires requires { typename identity_t<T*>; } // expected-error{{requires clause differs in template redeclaration}}
|
||||||
|
struct r1;
|
||||||
|
template<typename T> requires requires { typename ::identity_t<T>; }
|
||||||
|
struct r1;
|
||||||
|
|
||||||
|
template<typename Y> requires requires { typename identity<Y>::type; }
|
||||||
|
struct r2;
|
||||||
|
template<typename U> requires requires { typename identity<U>::type; }
|
||||||
|
struct r2;
|
||||||
|
template<typename T> requires requires { typename ::identity<T>::type; } // expected-note 2{{previous template declaration is here}}
|
||||||
|
struct r2;
|
||||||
|
template<typename T> requires requires { typename identity<T>::typr; } // expected-error{{requires clause differs in template redeclaration}}
|
||||||
|
struct r2;
|
||||||
|
namespace ns {
|
||||||
|
template<typename T> struct identity { using type = T; };
|
||||||
|
}
|
||||||
|
template<typename T> requires requires { typename ns::identity<T>::type; } // expected-error{{requires clause differs in template redeclaration}}
|
||||||
|
struct r2;
|
||||||
|
|
||||||
|
template<typename T> requires requires { typename T::template identity<T>::type; }
|
||||||
|
struct r3;
|
||||||
|
template<typename U> requires requires { typename U::template identity<U>::type; } // expected-note{{previous template declaration is here}}
|
||||||
|
struct r3;
|
||||||
|
template<typename T> requires requires { typename T::template identitr<T>::type; } // expected-error{{requires clause differs in template redeclaration}}
|
||||||
|
struct r3;
|
||||||
|
|
||||||
|
template<typename T> requires requires { typename T::template temp<>; }
|
||||||
|
struct r4;
|
||||||
|
template<typename U> requires requires { typename U::template temp<>; }
|
||||||
|
struct r4;
|
||||||
|
|
||||||
|
// Expr requirements
|
||||||
|
template<typename T> requires requires { 0; } // expected-note{{previous template declaration is here}}
|
||||||
|
struct r5;
|
||||||
|
template<typename T> requires requires { 1; } // expected-error{{requires clause differs in template redeclaration}}
|
||||||
|
struct r5;
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
concept C1 = true;
|
||||||
|
|
||||||
|
template<typename T> requires requires { sizeof(T); }
|
||||||
|
struct r6;
|
||||||
|
template<typename U> requires requires { sizeof(U); } // expected-note{{previous template declaration is here}}
|
||||||
|
struct r6;
|
||||||
|
template<typename U> requires requires { sizeof(U) - 1; } // expected-error{{requires clause differs in template redeclaration}}
|
||||||
|
struct r6;
|
||||||
|
template<typename T> requires requires { { sizeof(T) }; } // expected-note 2{{previous template declaration is here}}
|
||||||
|
struct r6;
|
||||||
|
template<typename T> requires requires { { sizeof(T) } noexcept; } // expected-error{{requires clause differs in template redeclaration}}
|
||||||
|
struct r6;
|
||||||
|
template<typename T> requires requires { { sizeof(T) } -> C1; } // expected-error{{requires clause differs in template redeclaration}}
|
||||||
|
struct r6;
|
||||||
|
|
||||||
|
template<typename T> requires requires { { sizeof(T) } -> C1; }
|
||||||
|
struct r7;
|
||||||
|
template<typename U> requires requires { { sizeof(U) } -> C1; }
|
||||||
|
struct r7;
|
||||||
|
template<typename T> requires requires { { sizeof(T) } -> C1<>; } // expected-note {{previous template declaration is here}}
|
||||||
|
struct r7;
|
||||||
|
template<typename U> requires requires { { sizeof(U) }; } // expected-error{{requires clause differs in template redeclaration}}
|
||||||
|
struct r7;
|
||||||
|
|
||||||
|
template<typename T, typename U>
|
||||||
|
concept C2 = true;
|
||||||
|
|
||||||
|
template<typename T> requires requires { { sizeof(T) } -> C2<T>; }
|
||||||
|
struct r8;
|
||||||
|
template<typename U> requires requires { { sizeof(U) } -> C2<U>; } // expected-note{{previous template declaration is here}}
|
||||||
|
struct r8;
|
||||||
|
template<typename T> requires requires { { sizeof(T) } -> C2<T*>; } // expected-error{{requires clause differs in template redeclaration}}
|
||||||
|
struct r8;
|
||||||
|
|
||||||
|
// Nested requirements
|
||||||
|
template<typename T> requires requires { requires sizeof(T) == 0; }
|
||||||
|
struct r9;
|
||||||
|
template<typename U> requires requires { requires sizeof(U) == 0; } // expected-note{{previous template declaration is here}}
|
||||||
|
struct r9;
|
||||||
|
template<typename T> requires requires { requires sizeof(T) == 1; } // expected-error{{requires clause differs in template redeclaration}}
|
||||||
|
struct r9;
|
||||||
|
|
||||||
|
// Parameter list
|
||||||
|
template<typename T> requires requires { requires true; }
|
||||||
|
struct r10;
|
||||||
|
template<typename T> requires requires() { requires true; } // expected-note{{previous template declaration is here}}
|
||||||
|
struct r10;
|
||||||
|
template<typename T> requires requires(T i) { requires true; } // expected-error{{requires clause differs in template redeclaration}}
|
||||||
|
struct r10;
|
||||||
|
|
||||||
|
template<typename T> requires requires(T i, T *j) { requires true; } // expected-note 2{{previous template declaration is here}}
|
||||||
|
struct r11;
|
||||||
|
template<typename T> requires requires(T i) { requires true; } // expected-error{{requires clause differs in template redeclaration}}
|
||||||
|
struct r11;
|
||||||
|
template<typename T> requires requires(T i, T *j, T &k) { requires true; } // expected-error{{requires clause differs in template redeclaration}}
|
||||||
|
struct r11;
|
||||||
|
|
||||||
|
// Parameter names
|
||||||
|
template<typename T> requires requires(int i) { requires sizeof(i) == 1; }
|
||||||
|
struct r12;
|
||||||
|
template<typename T> requires requires(int j) { requires sizeof(j) == 1; } // expected-note 2{{previous template declaration is here}}
|
||||||
|
struct r12;
|
||||||
|
template<typename T> requires requires(int k) { requires sizeof(k) == 2; } // expected-error{{requires clause differs in template redeclaration}}
|
||||||
|
struct r12;
|
||||||
|
template<typename T> requires requires(const int k) { requires sizeof(k) == 1; } // expected-error{{requires clause differs in template redeclaration}}
|
||||||
|
struct r12;
|
||||||
|
|
||||||
|
// Order of requirements
|
||||||
|
template<typename T> requires requires { requires true; 0; }
|
||||||
|
struct r13;
|
||||||
|
template<typename T> requires requires { requires true; 0; } // expected-note{{previous template declaration is here}}
|
||||||
|
struct r13;
|
||||||
|
template<typename T> requires requires { 0; requires true; } // expected-error{{requires clause differs in template redeclaration}}
|
||||||
|
struct r13;
|
@ -0,0 +1,46 @@
|
|||||||
|
// RUN: %clang_cc1 -std=c++2a -fconcepts-ts -x c++ %s -verify
|
||||||
|
|
||||||
|
static_assert(requires { requires true; });
|
||||||
|
|
||||||
|
template<typename T> requires requires { requires false; } // expected-note{{because 'false' evaluated to false}}
|
||||||
|
struct r1 {};
|
||||||
|
|
||||||
|
using r1i = r1<int>; // expected-error{{constraints not satisfied for class template 'r1' [with T = int]}}
|
||||||
|
|
||||||
|
template<typename T> requires requires { requires sizeof(T) == 0; } // expected-note{{because 'sizeof(int) == 0' (4 == 0) evaluated to false}}
|
||||||
|
struct r2 {};
|
||||||
|
|
||||||
|
using r2i = r2<int>; // expected-error{{constraints not satisfied for class template 'r2' [with T = int]}}
|
||||||
|
|
||||||
|
template<typename T> requires requires (T t) { requires sizeof(t) == 0; } // expected-note{{because 'sizeof (t) == 0' (4 == 0) evaluated to false}}
|
||||||
|
struct r3 {};
|
||||||
|
|
||||||
|
using r3i = r3<int>; // expected-error{{constraints not satisfied for class template 'r3' [with T = int]}}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
struct X {
|
||||||
|
template<typename U> requires requires (U u) { requires sizeof(u) == sizeof(T); } // expected-note{{because 'sizeof (u) == sizeof(T)' would be invalid: invalid application of 'sizeof' to an incomplete type 'void'}}
|
||||||
|
struct r4 {};
|
||||||
|
};
|
||||||
|
|
||||||
|
using r4i = X<void>::r4<int>; // expected-error{{constraints not satisfied for class template 'r4' [with U = int]}}
|
||||||
|
|
||||||
|
// C++ [expr.prim.req.nested] Examples
|
||||||
|
namespace std_example {
|
||||||
|
template<typename U> concept C1 = sizeof(U) == 1; // expected-note{{because 'sizeof(int) == 1' (4 == 1) evaluated to false}}
|
||||||
|
template<typename T> concept D =
|
||||||
|
requires (T t) {
|
||||||
|
requires C1<decltype (+t)>; // expected-note{{because 'decltype(+t)' (aka 'int') does not satisfy 'C1'}}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct T1 { char operator+() { return 'a'; } };
|
||||||
|
static_assert(D<T1>);
|
||||||
|
template<D T> struct D_check {}; // expected-note{{because 'short' does not satisfy 'D'}}
|
||||||
|
using dc1 = D_check<short>; // expected-error{{constraints not satisfied for class template 'D_check' [with T = short]}}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
concept C2 = requires (T a) { // expected-note{{'a' declared here}}
|
||||||
|
requires sizeof(a) == 4; // OK
|
||||||
|
requires a == 0; // expected-error{{constraint variable 'a' cannot be used in an evaluated context}}
|
||||||
|
};
|
||||||
|
}
|
37
clang/test/CXX/expr/expr.prim/expr.prim.req/p3.cpp
Normal file
37
clang/test/CXX/expr/expr.prim/expr.prim.req/p3.cpp
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
// RUN: %clang_cc1 -std=c++2a -fconcepts-ts -x c++ %s -verify
|
||||||
|
|
||||||
|
// Examples from standard
|
||||||
|
|
||||||
|
template<typename T, typename U>
|
||||||
|
concept convertible_to = requires(T t) { U(t); };
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
concept R = requires (T i) {
|
||||||
|
typename T::type;
|
||||||
|
{*i} -> convertible_to<const typename T::type&>;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename T> requires R<T> struct S {};
|
||||||
|
|
||||||
|
struct T {
|
||||||
|
using type = int;
|
||||||
|
type i;
|
||||||
|
const type &operator*() { return i; }
|
||||||
|
};
|
||||||
|
|
||||||
|
using si = S<T>;
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
requires requires (T x) { x + x; } // expected-note{{because 'x + x' would be invalid: invalid operands to binary expression ('T' and 'T')}}
|
||||||
|
T add(T a, T b) { return a + b; } // expected-note{{candidate template ignored: constraints not satisfied [with T = T]}}
|
||||||
|
|
||||||
|
int x = add(1, 2);
|
||||||
|
int y = add(T{}, T{}); // expected-error{{no matching function for call to 'add'}}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
concept C = requires (T x) { x + x; }; // expected-note{{because 'x + x' would be invalid: invalid operands to binary expression ('T' and 'T')}}
|
||||||
|
template<typename T> requires C<T> // expected-note{{because 'T' does not satisfy 'C'}}
|
||||||
|
T add2(T a, T b) { return a + b; } // expected-note{{candidate template ignored: constraints not satisfied [with T = T]}}
|
||||||
|
|
||||||
|
int z = add2(1, 2);
|
||||||
|
int w = add2(T{}, T{}); // expected-error{{no matching function for call to 'add2'}}
|
@ -0,0 +1,68 @@
|
|||||||
|
// RUN: %clang_cc1 -std=c++2a -fconcepts-ts -x c++ %s -verify
|
||||||
|
|
||||||
|
using A = int;
|
||||||
|
|
||||||
|
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_as = is_same_v<T, U>;
|
||||||
|
|
||||||
|
static_assert(requires { requires true; 0; typename A;
|
||||||
|
{ 0 } -> same_as<int>; });
|
||||||
|
static_assert(is_same_v<bool, decltype(requires { requires false; })>);
|
||||||
|
|
||||||
|
// Check that requires expr is an unevaluated context.
|
||||||
|
struct Y {
|
||||||
|
int i;
|
||||||
|
static constexpr bool r = requires { i; };
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename T> requires requires (T t) {
|
||||||
|
requires false; // expected-note{{because 'false' evaluated to false}}
|
||||||
|
requires false;
|
||||||
|
requires requires {
|
||||||
|
requires false;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
struct r1 { };
|
||||||
|
|
||||||
|
using r1i = r1<int>;
|
||||||
|
// expected-error@-1 {{constraints not satisfied for class template 'r1' [with T = int]}}
|
||||||
|
|
||||||
|
template<typename T> requires requires (T t) {
|
||||||
|
requires requires {
|
||||||
|
requires false; // expected-note{{because 'false' evaluated to false}}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
struct r2 { };
|
||||||
|
|
||||||
|
using r2i = r2<int>;
|
||||||
|
// expected-error@-1 {{constraints not satisfied for class template 'r2' [with T = int]}}
|
||||||
|
|
||||||
|
template<typename T> requires requires (T t) {
|
||||||
|
requires requires {
|
||||||
|
requires true;
|
||||||
|
};
|
||||||
|
requires true;
|
||||||
|
requires requires {
|
||||||
|
requires false; // expected-note{{because 'false' evaluated to false}}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
struct r3 { };
|
||||||
|
|
||||||
|
using r3i = r3<int>;
|
||||||
|
// expected-error@-1 {{constraints not satisfied for class template 'r3' [with T = int]}}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
struct S { static const int s = T::value; };
|
||||||
|
|
||||||
|
template<typename T> requires requires { T::value; S<T>::s; }
|
||||||
|
// expected-note@-1 {{because 'T::value' would be invalid: type 'int' cannot be used prior to '::' because it has no members}}
|
||||||
|
struct r4 { };
|
||||||
|
|
||||||
|
using r4i = r4<int>;
|
||||||
|
// expected-error@-1 {{constraints not satisfied for class template 'r4' [with T = int]}}
|
@ -0,0 +1,106 @@
|
|||||||
|
// RUN: %clang_cc1 %s -I%S -std=c++2a -fconcepts-ts -verify
|
||||||
|
|
||||||
|
namespace std { struct type_info; }
|
||||||
|
|
||||||
|
static_assert(requires { 0; });
|
||||||
|
static_assert(requires { "aaaa"; });
|
||||||
|
static_assert(requires { (0).da; }); // expected-error{{member reference base type 'int' is not a structure or union}}
|
||||||
|
|
||||||
|
struct A {};
|
||||||
|
struct B {
|
||||||
|
B operator+(const B &other) const { return other; }
|
||||||
|
};
|
||||||
|
struct C {
|
||||||
|
C operator+(C &other) const { return other; }
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename T> requires requires (T a, const T& b) { a + b; }
|
||||||
|
// expected-note@-1{{because 'a + b' would be invalid: invalid operands to binary expression ('A' and 'const A')}}
|
||||||
|
// expected-note@-2{{because 'a + b' would be invalid: invalid operands to binary expression ('C' and 'const C')}}
|
||||||
|
struct r1 {};
|
||||||
|
|
||||||
|
using r1i1 = r1<int>;
|
||||||
|
using r1i2 = r1<A>; // expected-error{{constraints not satisfied for class template 'r1' [with T = A]}}
|
||||||
|
using r1i3 = r1<B>;
|
||||||
|
using r1i4 = r1<C>; // expected-error{{constraints not satisfied for class template 'r1' [with T = C]}}
|
||||||
|
|
||||||
|
struct D { void foo() {} };
|
||||||
|
|
||||||
|
template<typename T> requires requires (T a) { a.foo(); }
|
||||||
|
// expected-note@-1{{because 'a.foo()' would be invalid: no member named 'foo' in 'A'}}
|
||||||
|
// expected-note@-2{{because 'a.foo()' would be invalid: member reference base type 'int' is not a structure or union}}
|
||||||
|
// expected-note@-3{{because 'a.foo()' would be invalid: 'this' argument to member function 'foo' has type 'const D', but function is not marked const}}
|
||||||
|
struct r2 {};
|
||||||
|
|
||||||
|
using r2i1 = r2<int>; // expected-error{{constraints not satisfied for class template 'r2' [with T = int]}}
|
||||||
|
using r2i2 = r2<A>; // expected-error{{constraints not satisfied for class template 'r2' [with T = A]}}
|
||||||
|
using r2i3 = r2<D>;
|
||||||
|
using r2i4 = r2<const D>; // expected-error{{constraints not satisfied for class template 'r2' [with T = const D]}}
|
||||||
|
|
||||||
|
template<typename T> requires requires { sizeof(T); }
|
||||||
|
// expected-note@-1{{because 'sizeof(T)' would be invalid: invalid application of 'sizeof' to an incomplete type 'void'}}
|
||||||
|
// expected-note@-2{{because 'sizeof(T)' would be invalid: invalid application of 'sizeof' to an incomplete type 'nonexistent'}}
|
||||||
|
struct r3 {};
|
||||||
|
|
||||||
|
using r3i1 = r3<int>;
|
||||||
|
using r3i2 = r3<A>;
|
||||||
|
using r3i3 = r3<A &>;
|
||||||
|
using r3i4 = r3<void>; // expected-error{{constraints not satisfied for class template 'r3' [with T = void]}}
|
||||||
|
using r3i4 = r3<class nonexistent>; // expected-error{{constraints not satisfied for class template 'r3' [with T = nonexistent]}}
|
||||||
|
|
||||||
|
template<typename T> requires requires (T t) { 0; "a"; (void)'a'; }
|
||||||
|
struct r4 {};
|
||||||
|
|
||||||
|
using r4i1 = r4<int>;
|
||||||
|
using r4i2 = r4<int[10]>;
|
||||||
|
using r4i3 = r4<int(int)>;
|
||||||
|
|
||||||
|
template<class T> void f(T) = delete;
|
||||||
|
template<class T> requires (sizeof(T) == 1) void f(T) { }
|
||||||
|
|
||||||
|
template<typename T> requires requires(T t) { f(t); }
|
||||||
|
// expected-note@-1{{because 'f(t)' would be invalid: call to deleted function 'f'}}
|
||||||
|
struct r5 {};
|
||||||
|
|
||||||
|
using r5i1 = r5<int>;
|
||||||
|
// expected-error@-1 {{constraints not satisfied for class template 'r5' [with T = int]}}
|
||||||
|
using r5i2 = r5<char>;
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
struct E {
|
||||||
|
struct non_default_constructible { non_default_constructible(T t) { } };
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename T> requires requires(T t) { typename E<T>::non_default_constructible{}; }
|
||||||
|
// expected-note@-1 {{because 'typename E<T>::non_default_constructible({})' would be invalid: no matching constructor for initialization of 'typename E<int>::non_default_constructible'}}
|
||||||
|
struct r6 {};
|
||||||
|
|
||||||
|
using r6i1 = r6<int>;
|
||||||
|
// expected-error@-1 {{constraints not satisfied for class template 'r6' [with T = int]}}
|
||||||
|
|
||||||
|
template<typename T> requires requires(T t) { typename E<T>::non_default_constructible(); }
|
||||||
|
// expected-note@-1 {{because 'typename E<T>::non_default_constructible()' would be invalid: no matching constructor for initialization of 'typename E<int>::non_default_constructible'}}
|
||||||
|
struct r7 {};
|
||||||
|
|
||||||
|
using r7i1 = r7<int>;
|
||||||
|
// expected-error@-1 {{constraints not satisfied for class template 'r7' [with T = int]}}
|
||||||
|
|
||||||
|
// C++ [expr.prim.req.simple] Example
|
||||||
|
namespace std_example {
|
||||||
|
template<typename T> concept C =
|
||||||
|
requires (T a, T b) { // expected-note{{because substituted constraint expression is ill-formed: argument may not have 'void' type}}
|
||||||
|
a + b; // expected-note{{because 'a + b' would be invalid: invalid operands to binary expression ('int *' and 'int *')}}
|
||||||
|
};
|
||||||
|
|
||||||
|
static_assert(C<int>);
|
||||||
|
template<C T> struct C_check {}; // expected-note{{because 'void' does not satisfy 'C'}} expected-note{{because 'int *' does not satisfy 'C'}}
|
||||||
|
using c1c1 = C_check<void>; // expected-error{{constraints not satisfied for class template 'C_check' [with T = void]}}
|
||||||
|
using c1c2 = C_check<int *>; // expected-error{{constraints not satisfied for class template 'C_check' [with T = int *]}}
|
||||||
|
}
|
||||||
|
|
||||||
|
// typeid() of an expression becomes potentially evaluated if the expression is
|
||||||
|
// of a polymorphic type.
|
||||||
|
class X { virtual ~X(); };
|
||||||
|
constexpr bool b = requires (X &x) { static_cast<int(*)[(typeid(x), 0)]>(nullptr); };
|
||||||
|
// expected-error@-1{{constraint variable 'x' cannot be used in an evaluated context}}
|
||||||
|
// expected-note@-2{{'x' declared here}}
|
194
clang/test/CXX/expr/expr.prim/expr.prim.req/type-requirement.cpp
Normal file
194
clang/test/CXX/expr/expr.prim/expr.prim.req/type-requirement.cpp
Normal file
@ -0,0 +1,194 @@
|
|||||||
|
// RUN: %clang_cc1 -std=c++2a -fconcepts-ts -x c++ %s -verify
|
||||||
|
|
||||||
|
using A = int;
|
||||||
|
|
||||||
|
template<typename T> using identity_t = T; // expected-note 4{{template is declared here}}}
|
||||||
|
|
||||||
|
template<typename T> struct identity { using type = T; };
|
||||||
|
// expected-note@-1 2{{template is declared here}}
|
||||||
|
|
||||||
|
struct C {};
|
||||||
|
|
||||||
|
struct D { static int type; }; // expected-note{{referenced member 'type' is declared here}}
|
||||||
|
|
||||||
|
// Basic unqualified and global-qualified lookups
|
||||||
|
|
||||||
|
static_assert(requires { typename A; typename ::A; });
|
||||||
|
static_assert(requires { typename identity_t<A>; typename ::identity_t<A>; });
|
||||||
|
static_assert(!requires { typename identity_t<A, A>; }); // expected-error{{too many template arguments for alias template 'identity_t'}}
|
||||||
|
static_assert(!requires { typename ::identity_t<A, A>; }); // expected-error{{too many template arguments for alias template 'identity_t'}}
|
||||||
|
static_assert(requires { typename identity<A>; });
|
||||||
|
static_assert(!requires { typename identity; });
|
||||||
|
// expected-error@-1 {{typename specifier refers to class template; argument deduction not allowed here}}
|
||||||
|
static_assert(!requires { typename ::identity; });
|
||||||
|
// expected-error@-1 {{typename specifier refers to class template; argument deduction not allowed here}}
|
||||||
|
static_assert(!requires { typename identity_t; });
|
||||||
|
// expected-error@-1 {{typename specifier refers to alias template; argument deduction not allowed here}}
|
||||||
|
static_assert(!requires { typename ::identity_t; });
|
||||||
|
// expected-error@-1 {{typename specifier refers to alias template; argument deduction not allowed here}}
|
||||||
|
|
||||||
|
namespace ns {
|
||||||
|
using B = int;
|
||||||
|
int C = 0;
|
||||||
|
// expected-note@-1 {{referenced 'C' is declared here}}
|
||||||
|
static_assert(requires { typename A; typename B; typename ::A; });
|
||||||
|
static_assert(!requires { typename ns::A; }); // expected-error{{no type named 'A' in namespace 'ns'}}
|
||||||
|
static_assert(!requires { typename ::B; }); // expected-error{{no type named 'B' in the global namespace}}
|
||||||
|
static_assert(requires { typename C; });
|
||||||
|
// expected-error@-1 {{typename specifier refers to non-type 'C'}}
|
||||||
|
}
|
||||||
|
|
||||||
|
// member type lookups
|
||||||
|
|
||||||
|
static_assert(requires { typename identity<int>::type; typename ::identity<int>::type; });
|
||||||
|
static_assert(!requires { typename identity<int>::typr; }); // expected-error{{no type named 'typr' in 'identity<int>'}}
|
||||||
|
static_assert(!requires { typename ::identity<int>::typr; }); // expected-error{{no type named 'typr' in 'identity<int>'}}
|
||||||
|
|
||||||
|
template<typename T> requires requires { typename T::type; }
|
||||||
|
// expected-note@-1 {{because 'typename T::type' would be invalid: type 'int' cannot be used prior to '::' because it has no members}}
|
||||||
|
// expected-note@-2 {{because 'typename T::type' would be invalid: no type named 'type' in 'C'}}
|
||||||
|
// expected-note@-3 {{because 'typename T::type' would be invalid: typename specifier refers to non-type member 'type' in 'D'}}
|
||||||
|
// expected-note@-4 {{in instantiation of template class 'invalid<D>' requested here}}
|
||||||
|
// expected-note@-5 {{in instantiation of requirement here}}
|
||||||
|
// expected-note@-6 {{while substituting template arguments into constraint expression here}}
|
||||||
|
// expected-note@-7 {{because 'typename T::type' would be invalid}}
|
||||||
|
struct r1 {};
|
||||||
|
|
||||||
|
using r1i1 = r1<identity<int>>;
|
||||||
|
using r1i2 = r1<int>; // expected-error{{constraints not satisfied for class template 'r1' [with T = int]}}
|
||||||
|
using r1i3 = r1<C>; // expected-error{{constraints not satisfied for class template 'r1' [with T = C]}}
|
||||||
|
using r1i4 = r1<D>; // expected-error{{constraints not satisfied for class template 'r1' [with T = D]}}
|
||||||
|
|
||||||
|
template<typename T> struct invalid { typename T::type x; };
|
||||||
|
// expected-error@-1 {{typename specifier refers to non-type member 'type' in 'D'}}
|
||||||
|
using r1i5 = r1<invalid<D>>;
|
||||||
|
// expected-error@-1 {{constraints not satisfied for class template 'r1' [with T = invalid<D>]}}
|
||||||
|
// expected-note@-2 {{while checking constraint satisfaction for template 'r1<invalid<D> >' required here}}
|
||||||
|
|
||||||
|
// mismatching template arguments
|
||||||
|
|
||||||
|
template<typename... Ts> requires requires { typename identity<Ts...>; } // expected-note{{because 'typename identity<Ts...>' would be invalid: too many template arguments for class template 'identity'}}
|
||||||
|
struct r2 {};
|
||||||
|
|
||||||
|
using r2i1 = r2<int>;
|
||||||
|
using r2i2 = r2<void>;
|
||||||
|
using r2i3 = r2<int, int>; // expected-error{{constraints not satisfied for class template 'r2' [with Ts = <int, int>]}}
|
||||||
|
|
||||||
|
namespace ns2 {
|
||||||
|
template<typename T, typename U> struct identity {};
|
||||||
|
|
||||||
|
template<typename... Ts> requires requires { typename identity<Ts...>; } // expected-note 2{{because 'typename identity<Ts...>' would be invalid: too few template arguments for class template 'identity'}}
|
||||||
|
struct r4 {};
|
||||||
|
|
||||||
|
using r4i1 = r4<int>; // expected-error{{constraints not satisfied for class template 'r4' [with Ts = <int>]}}
|
||||||
|
}
|
||||||
|
|
||||||
|
using r4i2 = ns2::r4<int>; // expected-error{{constraints not satisfied for class template 'r4' [with Ts = <int>]}}
|
||||||
|
|
||||||
|
using E = int;
|
||||||
|
template<typename T> requires requires { typename E<T>; } // expected-error{{expected ';' at end of requirement}}
|
||||||
|
struct r5v1 {};
|
||||||
|
template<typename T> requires requires { typename ::E<T>; } // expected-error{{expected ';' at end of requirement}}
|
||||||
|
struct r5v2 {};
|
||||||
|
|
||||||
|
template<typename T> requires (sizeof(T) == 1)
|
||||||
|
struct chars_only {};
|
||||||
|
|
||||||
|
template<typename T> requires requires { typename chars_only<T>; } // expected-note{{because 'typename chars_only<T>' would be invalid: constraints not satisfied for class template 'chars_only' [with T = int]}}
|
||||||
|
struct r6 {};
|
||||||
|
|
||||||
|
using r6i = r6<int>; // expected-error{{constraints not satisfied for class template 'r6' [with T = int]}}
|
||||||
|
|
||||||
|
template<typename T> int F = 0; // expected-note 2{{variable template 'F' declared here}}
|
||||||
|
|
||||||
|
static_assert(!requires { typename F<int>; });
|
||||||
|
// expected-error@-1{{template name refers to non-type template 'F'}}
|
||||||
|
static_assert(!requires { typename ::F<int>; });
|
||||||
|
// expected-error@-1{{template name refers to non-type template '::F'}}
|
||||||
|
|
||||||
|
struct G { template<typename T> static T temp; };
|
||||||
|
|
||||||
|
template<typename T> requires requires { typename T::template temp<int>; }
|
||||||
|
// expected-note@-1{{because 'typename T::temp<int>' would be invalid: type 'int' cannot be used prior to '::' because it has no members}}
|
||||||
|
// expected-note@-2{{because 'typename T::temp<int>' would be invalid: no member named 'temp' in 'D'}}
|
||||||
|
// expected-note@-3{{because 'typename T::temp<int>' would be invalid: template name refers to non-type template 'G::template temp'}}
|
||||||
|
struct r7 {};
|
||||||
|
|
||||||
|
using r7i1 = r7<int>; // expected-error{{constraints not satisfied for class template 'r7' [with T = int]}}
|
||||||
|
using r7i2 = r7<D>; // expected-error{{constraints not satisfied for class template 'r7' [with T = D]}}
|
||||||
|
using r7i3 = r7<G>; // expected-error{{constraints not satisfied for class template 'r7' [with T = G]}}
|
||||||
|
|
||||||
|
template<typename T> struct H;
|
||||||
|
|
||||||
|
template<typename T> requires requires { typename H<T>; }
|
||||||
|
struct r8 {};
|
||||||
|
|
||||||
|
using r8i = r8<int>;
|
||||||
|
|
||||||
|
template<typename T> struct I { struct incomplete; }; // expected-note{{member is declared here}}
|
||||||
|
|
||||||
|
static_assert(!requires { I<int>::incomplete::inner; }); // expected-error{{implicit instantiation of undefined member 'I<int>::incomplete'}}
|
||||||
|
|
||||||
|
template<typename T> requires requires { typename I<T>::incomplete::inner; } // expected-note{{because 'typename I<T>::incomplete::inner' would be invalid: implicit instantiation of undefined member 'I<int>::incomplete'}}
|
||||||
|
struct r9 {};
|
||||||
|
|
||||||
|
using r9i = r9<int>; // expected-error{{constraints not satisfied for class template 'r9' [with T = int]}}
|
||||||
|
|
||||||
|
namespace ns3 {
|
||||||
|
struct X { }; // expected-note 2{{candidate found by name lookup is 'ns3::X'}}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct X { using inner = int; }; // expected-note 2{{candidate found by name lookup is 'X'}}
|
||||||
|
|
||||||
|
using namespace ns3;
|
||||||
|
static_assert(requires { typename X; }); // expected-error{{reference to 'X' is ambiguous}}
|
||||||
|
static_assert(requires { typename X::inner; }); // expected-error{{reference to 'X' is ambiguous}}
|
||||||
|
// expected-error@-1{{unknown type name 'inner'}}
|
||||||
|
|
||||||
|
// naming a type template specialization in a type requirement does not require
|
||||||
|
// it to be complete and should not care about partial specializations.
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
struct Z;
|
||||||
|
|
||||||
|
template<typename T> requires (sizeof(T) >= 1)
|
||||||
|
struct Z<T> {}; // expected-note{{partial specialization matches [with T = int]}}
|
||||||
|
|
||||||
|
template<typename T> requires (sizeof(T) <= 4)
|
||||||
|
struct Z<T> {}; // expected-note{{partial specialization matches [with T = int]}}
|
||||||
|
|
||||||
|
Z<int> x; // expected-error{{ambiguous partial specializations of 'Z<int>'}}
|
||||||
|
|
||||||
|
static_assert(requires { typename Z<int>; });
|
||||||
|
|
||||||
|
// C++ [expr.prim.req.type] Example
|
||||||
|
namespace std_example {
|
||||||
|
template<typename T, typename T::type = 0> struct S;
|
||||||
|
// expected-note@-1 {{because 'typename S<T>' would be invalid: no type named 'type' in 'std_example::has_inner}}
|
||||||
|
template<typename T> using Ref = T&; // expected-note{{because 'typename Ref<T>' would be invalid: cannot form a reference to 'void'}}
|
||||||
|
template<typename T> concept C1 =
|
||||||
|
requires {
|
||||||
|
typename T::inner;
|
||||||
|
// expected-note@-1 {{because 'typename T::inner' would be invalid: type 'int' cannot be used prior to '::' because it has no members}}
|
||||||
|
// expected-note@-2 {{because 'typename T::inner' would be invalid: no type named 'inner' in 'std_example::has_type'}}
|
||||||
|
};
|
||||||
|
template<typename T> concept C2 = requires { typename S<T>; };
|
||||||
|
template<typename T> concept C3 = requires { typename Ref<T>; };
|
||||||
|
|
||||||
|
struct has_inner { using inner = int;};
|
||||||
|
struct has_type { using type = int; };
|
||||||
|
struct has_inner_and_type { using inner = int; using type = int; };
|
||||||
|
|
||||||
|
static_assert(C1<has_inner_and_type> && C2<has_inner_and_type> && C3<has_inner_and_type>);
|
||||||
|
template<C1 T> struct C1_check {};
|
||||||
|
// expected-note@-1 {{because 'int' does not satisfy 'C1'}}
|
||||||
|
// expected-note@-2 {{because 'std_example::has_type' does not satisfy 'C1'}}
|
||||||
|
template<C2 T> struct C2_check {};
|
||||||
|
// expected-note@-1 {{because 'std_example::has_inner' does not satisfy 'C2'}}
|
||||||
|
template<C3 T> struct C3_check {};
|
||||||
|
// expected-note@-1 {{because 'void' does not satisfy 'C3'}}
|
||||||
|
using c1 = C1_check<int>; // expected-error{{constraints not satisfied for class template 'C1_check' [with T = int]}}
|
||||||
|
using c2 = C1_check<has_type>; // expected-error{{constraints not satisfied for class template 'C1_check' [with T = std_example::has_type]}}
|
||||||
|
using c3 = C2_check<has_inner>; // expected-error{{constraints not satisfied for class template 'C2_check' [with T = std_example::has_inner]}}
|
||||||
|
using c4 = C3_check<void>; // expected-error{{constraints not satisfied for class template 'C3_check' [with T = void]}}
|
||||||
|
}
|
20
clang/test/PCH/cxx2a-requires-expr.cpp
Normal file
20
clang/test/PCH/cxx2a-requires-expr.cpp
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
// RUN: %clang_cc1 -emit-pch -std=c++2a -fconcepts-ts -o %t %s
|
||||||
|
// RUN: %clang_cc1 -std=c++2a -fconcepts-ts -x ast -ast-print %t | FileCheck %s
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
concept C = true;
|
||||||
|
|
||||||
|
template<typename T, typename U>
|
||||||
|
concept C2 = true;
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
bool f() {
|
||||||
|
// CHECK: requires (T t) { t++; { t++ } noexcept -> C; { t++ } -> C2<int>; typename T::a; requires T::val; };
|
||||||
|
return requires (T t) {
|
||||||
|
t++;
|
||||||
|
{ t++ } noexcept -> C;
|
||||||
|
{ t++ } -> C2<int>;
|
||||||
|
typename T::a;
|
||||||
|
requires T::val;
|
||||||
|
};
|
||||||
|
}
|
141
clang/test/Parser/cxx2a-concepts-requires-expr.cpp
Normal file
141
clang/test/Parser/cxx2a-concepts-requires-expr.cpp
Normal file
@ -0,0 +1,141 @@
|
|||||||
|
// RUN: %clang_cc1 -std=c++2a -fconcepts-ts -x c++ %s -verify
|
||||||
|
|
||||||
|
bool r1 = requires () {};
|
||||||
|
// expected-error@-1 {{a requires expression must contain at least one requirement}}
|
||||||
|
|
||||||
|
bool r2 = requires { requires true; };
|
||||||
|
|
||||||
|
bool r3 = requires (int a, ...) { requires true; };
|
||||||
|
// expected-error@-1 {{varargs not allowed in requires expression}}
|
||||||
|
|
||||||
|
template<typename... T>
|
||||||
|
bool r4 = requires (T... ts) { requires true; };
|
||||||
|
|
||||||
|
bool r5 = requires (bool c, int d) { c; d; };
|
||||||
|
|
||||||
|
bool r6 = requires (bool c, int d) { c; d; } && decltype(d){};
|
||||||
|
// expected-error@-1 {{use of undeclared identifier 'd'}}
|
||||||
|
|
||||||
|
bool r7 = requires (bool c) { c; (requires (int d) { c; d; }); d; } && decltype(c){} && decltype(d){};
|
||||||
|
// expected-error@-1 2{{use of undeclared identifier 'd'}}
|
||||||
|
// expected-error@-2 {{use of undeclared identifier 'c'}}
|
||||||
|
|
||||||
|
bool r8 = requires (bool, int) { requires true; };
|
||||||
|
|
||||||
|
bool r9 = requires (bool a, int a) { requires true; };
|
||||||
|
// expected-error@-1 {{redefinition of parameter 'a'}}
|
||||||
|
// expected-note@-2 {{previous declaration is here}}
|
||||||
|
|
||||||
|
bool r10 = requires (struct new_struct { int x; } s) { requires true; };
|
||||||
|
// expected-error@-1 {{'new_struct' cannot be defined in a parameter type}}
|
||||||
|
|
||||||
|
bool r11 = requires (int x(1)) { requires true; };
|
||||||
|
// expected-error@-1 {{expected parameter declarator}}
|
||||||
|
// expected-error@-2 {{expected ')'}}
|
||||||
|
// expected-note@-3 {{to match this '('}}
|
||||||
|
|
||||||
|
bool r12 = requires (int x = 10) { requires true; };
|
||||||
|
// expected-error@-1 {{default arguments not allowed for parameters of a requires expression}}
|
||||||
|
|
||||||
|
bool r13 = requires (int f(int)) { requires true; };
|
||||||
|
|
||||||
|
bool r14 = requires (int (*f)(int)) { requires true; };
|
||||||
|
|
||||||
|
bool r15 = requires (10) { requires true; };
|
||||||
|
// expected-error@-1 {{expected parameter declarator}}
|
||||||
|
|
||||||
|
bool r16 = requires (auto x) { requires true; };
|
||||||
|
// expected-error@-1 {{'auto' not allowed in requires expression parameter}}
|
||||||
|
|
||||||
|
bool r17 = requires (auto [x, y]) { requires true; };
|
||||||
|
// expected-error@-1 {{'auto' not allowed in requires expression parameter}}
|
||||||
|
// expected-error@-2 {{use of undeclared identifier 'x'}}
|
||||||
|
|
||||||
|
using a = int;
|
||||||
|
|
||||||
|
bool r18 = requires { typename a; };
|
||||||
|
|
||||||
|
bool r19 = requires { typename ::a; };
|
||||||
|
|
||||||
|
template<typename T> struct identity { using type = T; };
|
||||||
|
|
||||||
|
template<typename T> using identity_t = T;
|
||||||
|
|
||||||
|
bool r20 = requires {
|
||||||
|
typename identity<int>::type;
|
||||||
|
typename identity<int>;
|
||||||
|
typename ::identity_t<int>;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct s { bool operator==(const s&); ~s(); };
|
||||||
|
|
||||||
|
bool r21 = requires { typename s::operator==; };
|
||||||
|
// expected-error@-1 {{expected an identifier or template-id after '::'}}
|
||||||
|
|
||||||
|
bool r22 = requires { typename s::~s; };
|
||||||
|
// expected-error@-1 {{expected an identifier or template-id after '::'}}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
bool r23 = requires { typename identity<T>::temp<T>; };
|
||||||
|
// expected-error@-1 {{use 'template' keyword to treat 'temp' as a dependent template name}}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
bool r24 = requires {
|
||||||
|
typename identity<T>::template temp<T>;
|
||||||
|
typename identity<T>::template temp; // expected-error{{expected an identifier or template-id after '::'}}
|
||||||
|
};
|
||||||
|
|
||||||
|
bool r25 = requires { ; };
|
||||||
|
// expected-error@-1 {{expected expression}}
|
||||||
|
|
||||||
|
bool r26 = requires { {}; };
|
||||||
|
// expected-error@-1 {{expected expression}}
|
||||||
|
|
||||||
|
bool r27 = requires { { 0 } noexcept; };
|
||||||
|
|
||||||
|
bool r28 = requires { { 0 } noexcept noexcept; };
|
||||||
|
// expected-error@-1 {{expected '->' before expression type requirement}}
|
||||||
|
// expected-error@-2 {{expected concept name with optional arguments}}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
concept C1 = true;
|
||||||
|
|
||||||
|
template<typename T, typename U>
|
||||||
|
concept C2 = true;
|
||||||
|
|
||||||
|
bool r29 = requires { { 0 } noexcept C1; };
|
||||||
|
// expected-error@-1 {{expected '->' before expression type requirement}}
|
||||||
|
|
||||||
|
bool r30 = requires { { 0 } noexcept -> C2<int>; };
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
T i1 = 0;
|
||||||
|
|
||||||
|
bool r31 = requires { requires false, 1; };
|
||||||
|
// expected-error@-1 {{expected ';' at end of requirement}}
|
||||||
|
|
||||||
|
bool r32 = requires { 0 noexcept; };
|
||||||
|
// expected-error@-1 {{'noexcept' can only be used in a compound requirement (with '{' '}' around the expression)}}
|
||||||
|
|
||||||
|
bool r33 = requires { 0 int; };
|
||||||
|
// expected-error@-1 {{expected ';' at end of requirement}}
|
||||||
|
|
||||||
|
bool r34 = requires { requires true };
|
||||||
|
// expected-error@-1 {{expected ';' at end of requirement}}
|
||||||
|
|
||||||
|
bool r35 = requires (bool b) { requires sizeof(b) == 1; };
|
||||||
|
|
||||||
|
void r36(bool b) requires requires { 1 } {}
|
||||||
|
// expected-error@-1 {{expected ';' at end of requirement}}
|
||||||
|
|
||||||
|
bool r37 = requires { requires { 1; }; };
|
||||||
|
// expected-warning@-1 {{this requires expression will only be checked for syntactic validity; did you intend to place it in a nested requirement? (add another 'requires' before the expression)}}
|
||||||
|
|
||||||
|
bool r38 = requires { requires () { 1; }; };
|
||||||
|
// expected-warning@-1 {{this requires expression will only be checked for syntactic validity; did you intend to place it in a nested requirement? (add another 'requires' before the expression)}}
|
||||||
|
|
||||||
|
bool r39 = requires { requires (int i) { i; }; };
|
||||||
|
// expected-warning@-1 {{this requires expression will only be checked for syntactic validity; did you intend to place it in a nested requirement? (add another 'requires' before the expression)}}
|
||||||
|
|
||||||
|
bool r40 = requires { requires (); };
|
||||||
|
// expected-error@-1 {{expected expression}}
|
216
clang/test/SemaTemplate/instantiate-requires-expr.cpp
Normal file
216
clang/test/SemaTemplate/instantiate-requires-expr.cpp
Normal file
@ -0,0 +1,216 @@
|
|||||||
|
// RUN: %clang_cc1 -std=c++2a -fconcepts-ts -x c++ %s -verify -Wno-unused-value
|
||||||
|
|
||||||
|
template<typename T, typename U>
|
||||||
|
constexpr bool is_same_v = false;
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
constexpr bool is_same_v<T, T> = true;
|
||||||
|
|
||||||
|
// We use a hack in this file to make the compiler print out the requires
|
||||||
|
// expression after it has been instantiated - we put false_v<requires {...}> as
|
||||||
|
// the requires clause of a class template, then instantiate the template.
|
||||||
|
// The requirement will not be satisfied, and the explaining diagnostic will
|
||||||
|
// print out false_v<requires {...}> in its raw form (the false_v serves to
|
||||||
|
// prevent the diagnostic from elaborating on why the requires expr wasn't
|
||||||
|
// satisfied).
|
||||||
|
|
||||||
|
template<bool v>
|
||||||
|
constexpr bool false_v = false;
|
||||||
|
|
||||||
|
template<typename... Ts>
|
||||||
|
using void_t = void;
|
||||||
|
|
||||||
|
// Check that requires parameters are instantiated correctly.
|
||||||
|
|
||||||
|
template<typename T> requires
|
||||||
|
false_v<requires (T t) { requires is_same_v<decltype(t), int>; }>
|
||||||
|
// expected-note@-1 {{because 'false_v<requires (int t) { requires is_same_v<decltype(t), int>; }>' evaluated to false}}
|
||||||
|
// expected-note@-2 {{because 'false_v<requires (char t) { requires is_same_v<decltype(t), int>; }>' evaluated to false}}
|
||||||
|
struct r1 {};
|
||||||
|
|
||||||
|
using r1i1 = r1<int>; // expected-error {{constraints not satisfied for class template 'r1' [with T = int]}}
|
||||||
|
using r1i2 = r1<char>; // expected-error {{constraints not satisfied for class template 'r1' [with T = char]}}
|
||||||
|
|
||||||
|
// Check that parameter packs work.
|
||||||
|
|
||||||
|
template<typename... Ts> requires
|
||||||
|
false_v<requires (Ts... ts) {requires ((sizeof(ts) == 2) && ...);}>
|
||||||
|
// expected-note@-1 {{because 'false_v<requires (short ts, unsigned short ts) { requires (sizeof (ts) == 2) && (sizeof (ts) == 2); }>'}}
|
||||||
|
// expected-note@-2 {{because 'false_v<requires (short ts) { requires (sizeof (ts) == 2); }>' evaluated to false}}
|
||||||
|
struct r2 {};
|
||||||
|
|
||||||
|
using r2i1 = r2<short, unsigned short>; // expected-error {{constraints not satisfied for class template 'r2' [with Ts = <short, unsigned short>]}}
|
||||||
|
using r2i2 = r2<short>; // expected-error {{constraints not satisfied for class template 'r2' [with Ts = <short>]}}
|
||||||
|
|
||||||
|
template<typename... Ts> requires
|
||||||
|
false_v<(requires (Ts ts) {requires sizeof(ts) != 0;} && ...)>
|
||||||
|
// expected-note@-1 {{because 'false_v<requires (short ts) { requires sizeof (ts) != 0; } && requires (unsigned short ts) { requires sizeof (ts) != 0; }>' evaluated to false}}
|
||||||
|
// expected-note@-2 {{because 'false_v<requires (short ts) { requires sizeof (ts) != 0; }>' evaluated to false}}
|
||||||
|
struct r3 {};
|
||||||
|
|
||||||
|
using r3i1 = r3<short, unsigned short>; // expected-error {{constraints not satisfied for class template 'r3' [with Ts = <short, unsigned short>]}}
|
||||||
|
using r3i2 = r3<short>; // expected-error {{constraints not satisfied for class template 'r3' [with Ts = <short>]}}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
struct identity { using type = T; };
|
||||||
|
|
||||||
|
namespace type_requirement {
|
||||||
|
struct A {};
|
||||||
|
|
||||||
|
// check that nested name specifier is instantiated correctly.
|
||||||
|
template<typename T> requires false_v<requires { typename T::type; }> // expected-note{{because 'false_v<requires { typename identity<int>::type; }>' evaluated to false}}
|
||||||
|
struct r1 {};
|
||||||
|
|
||||||
|
using r1i = r1<identity<int>>; // expected-error{{constraints not satisfied for class template 'r1' [with T = identity<int>]}}
|
||||||
|
|
||||||
|
// check that template argument list is instantiated correctly.
|
||||||
|
template<typename T>
|
||||||
|
struct contains_template {
|
||||||
|
template<typename U> requires is_same_v<contains_template<T>, U>
|
||||||
|
using temp = int;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename T> requires
|
||||||
|
false_v<requires { typename T::template temp<T>; }>
|
||||||
|
// expected-note@-1 {{because 'false_v<requires { typename contains_template<int>::temp<contains_template<int> >; }>' evaluated to false}}
|
||||||
|
// expected-note@-2 {{because 'false_v<requires { typename contains_template<short>::temp<contains_template<short> >; }>' evaluated to false}}
|
||||||
|
struct r2 {};
|
||||||
|
|
||||||
|
using r2i1 = r2<contains_template<int>>; // expected-error{{constraints not satisfied for class template 'r2' [with T = type_requirement::contains_template<int>]}}
|
||||||
|
using r2i2 = r2<contains_template<short>>; // expected-error{{constraints not satisfied for class template 'r2' [with T = type_requirement::contains_template<short>]}}
|
||||||
|
|
||||||
|
// substitution error occurs, then requires expr is instantiated again
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
struct a {
|
||||||
|
template<typename U> requires (requires { typename T::a::a; }, false)
|
||||||
|
// expected-note@-1{{because 'requires { <<error-type>>; } , false' evaluated to false}}
|
||||||
|
struct r {};
|
||||||
|
};
|
||||||
|
|
||||||
|
using ari = a<int>::r<short>; // expected-error{{constraints not satisfied for class template 'r' [with U = short]}}
|
||||||
|
|
||||||
|
// Parameter pack inside expr
|
||||||
|
template<typename... Ts> requires
|
||||||
|
false_v<(requires { typename Ts::type; } && ...)>
|
||||||
|
// expected-note@-1 {{because 'false_v<requires { typename identity<short>::type; } && requires { typename identity<int>::type; } && requires { <<error-type>>; }>' evaluated to false}}
|
||||||
|
struct r5 {};
|
||||||
|
|
||||||
|
using r5i = r5<identity<short>, identity<int>, short>; // expected-error{{constraints not satisfied for class template 'r5' [with Ts = <identity<short>, identity<int>, short>]}}
|
||||||
|
template<typename... Ts> requires
|
||||||
|
false_v<(requires { typename void_t<Ts>; } && ...)> // expected-note{{because 'false_v<requires { typename void_t<int>; } && requires { typename void_t<short>; }>' evaluated to false}}
|
||||||
|
struct r6 {};
|
||||||
|
|
||||||
|
using r6i = r6<int, short>; // expected-error{{constraints not satisfied for class template 'r6' [with Ts = <int, short>]}}
|
||||||
|
|
||||||
|
template<typename... Ts> requires
|
||||||
|
false_v<(requires { typename Ts::template aaa<Ts>; } && ...)>
|
||||||
|
// expected-note@-1 {{because 'false_v<requires { <<error-type>>; } && requires { <<error-type>>; }>' evaluated to false}}
|
||||||
|
struct r7 {};
|
||||||
|
|
||||||
|
using r7i = r7<int, A>; // expected-error{{constraints not satisfied for class template 'r7' [with Ts = <int, type_requirement::A>]}}
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace expr_requirement {
|
||||||
|
// check that compound/simple requirements are instantiated correctly.
|
||||||
|
|
||||||
|
template<typename T> requires false_v<requires { sizeof(T); { sizeof(T) }; }>
|
||||||
|
// expected-note@-1 {{because 'false_v<requires { sizeof(int); { sizeof(int) }; }>' evaluated to false}}
|
||||||
|
// expected-note@-2 {{because 'false_v<requires { <<error-expression>>; { sizeof(T) }; }>' evaluated to false}}
|
||||||
|
struct r1 {};
|
||||||
|
|
||||||
|
using r1i1 = r1<int>; // expected-error{{constraints not satisfied for class template 'r1' [with T = int]}}
|
||||||
|
using r1i2 = r1<void>; // expected-error{{constraints not satisfied for class template 'r1' [with T = void]}}
|
||||||
|
|
||||||
|
// substitution error occurs in expr, then expr is instantiated again.
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
struct a {
|
||||||
|
template<typename U> requires (requires { sizeof(T::a); }, false) // expected-note{{because 'requires { <<error-expression>>; } , false' evaluated to false}}
|
||||||
|
struct r {};
|
||||||
|
};
|
||||||
|
|
||||||
|
using ari = a<int>::r<short>; // expected-error{{constraints not satisfied for class template 'r' [with U = short]}}
|
||||||
|
|
||||||
|
// check that the return-type-requirement is instantiated correctly.
|
||||||
|
|
||||||
|
template<typename T, typename U = int>
|
||||||
|
concept C1 = is_same_v<T, U>;
|
||||||
|
|
||||||
|
template<typename T> requires false_v<requires(T t) { { t } -> C1<T>; }>
|
||||||
|
// expected-note@-1 {{because 'false_v<requires (int t) { { t } -> C1<int>; }>' evaluated to false}}
|
||||||
|
// expected-note@-2 {{because 'false_v<requires (double t) { { t } -> C1<double>; }>' evaluated to false}}
|
||||||
|
struct r2 {};
|
||||||
|
|
||||||
|
using r2i1 = r2<int>; // expected-error{{constraints not satisfied for class template 'r2' [with T = int]}}
|
||||||
|
using r2i2 = r2<double>; // expected-error{{constraints not satisfied for class template 'r2' [with T = double]}}
|
||||||
|
|
||||||
|
|
||||||
|
// substitution error occurs in return type requirement, then requires expr is
|
||||||
|
// instantiated again.
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
struct b {
|
||||||
|
template<typename U> requires (requires { { 0 } -> C1<typename T::a>; }, false) // expected-note{{because 'requires { { 0 } -> <<error-type>>; } , false' evaluated to false}}
|
||||||
|
struct r {};
|
||||||
|
};
|
||||||
|
|
||||||
|
using bri = b<int>::r<short>; // expected-error{{constraints not satisfied for class template 'r' [with U = short]}}
|
||||||
|
|
||||||
|
|
||||||
|
template<typename... Ts> requires
|
||||||
|
false_v<(requires { { 0 } noexcept -> C1<Ts>; } && ...)>
|
||||||
|
// expected-note@-1 {{because 'false_v<requires { { 0 } noexcept -> C1<int>; } && requires { { 0 } noexcept -> C1<unsigned int>; }>' evaluated to false}}
|
||||||
|
struct r3 {};
|
||||||
|
|
||||||
|
using r3i = r3<int, unsigned int>; // expected-error{{constraints not satisfied for class template 'r3' [with Ts = <int, unsigned int>]}}
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace nested_requirement {
|
||||||
|
// check that constraint expression is instantiated correctly
|
||||||
|
template<typename T> requires false_v<requires { requires sizeof(T) == 2; }> // expected-note{{because 'false_v<requires { requires sizeof(int) == 2; }>' evaluated to false}}
|
||||||
|
struct r1 {};
|
||||||
|
|
||||||
|
using r1i = r1<int>; // expected-error{{constraints not satisfied for class template 'r1' [with T = int]}}
|
||||||
|
|
||||||
|
// substitution error occurs in expr, then expr is instantiated again.
|
||||||
|
template<typename T>
|
||||||
|
struct a {
|
||||||
|
template<typename U> requires
|
||||||
|
(requires { requires sizeof(T::a) == 0; }, false) // expected-note{{because 'requires { requires <<error-expression>>; } , false' evaluated to false}}
|
||||||
|
struct r {};
|
||||||
|
};
|
||||||
|
|
||||||
|
using ari = a<int>::r<short>; // expected-error{{constraints not satisfied for class template 'r' [with U = short]}}
|
||||||
|
|
||||||
|
// Parameter pack inside expr
|
||||||
|
template<typename... Ts> requires
|
||||||
|
false_v<(requires { requires sizeof(Ts) == 0; } && ...)>
|
||||||
|
// expected-note@-1 {{because 'false_v<requires { requires sizeof(int) == 0; } && requires { requires sizeof(short) == 0; }>' evaluated to false}}
|
||||||
|
struct r2 {};
|
||||||
|
|
||||||
|
using r2i = r2<int, short>; // expected-error{{constraints not satisfied for class template 'r2' [with Ts = <int, short>]}}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parameter pack inside multiple requirements
|
||||||
|
template<typename... Ts> requires
|
||||||
|
false_v<(requires { requires sizeof(Ts) == 0; sizeof(Ts); } && ...)>
|
||||||
|
// expected-note@-1 {{because 'false_v<requires { requires sizeof(int) == 0; sizeof(Ts); } && requires { requires sizeof(short) == 0; sizeof(Ts); }>' evaluated to false}}
|
||||||
|
struct r4 {};
|
||||||
|
|
||||||
|
using r4i = r4<int, short>; // expected-error{{constraints not satisfied for class template 'r4' [with Ts = <int, short>]}}
|
||||||
|
|
||||||
|
template<typename... Ts> requires
|
||||||
|
false_v<(requires(Ts t) { requires sizeof(t) == 0; t++; } && ...)>
|
||||||
|
// expected-note@-1 {{because 'false_v<requires (int t) { requires sizeof (t) == 0; t++; } && requires (short t) { requires sizeof (t) == 0; t++; }>' evaluated to false}}
|
||||||
|
struct r5 {};
|
||||||
|
|
||||||
|
using r5i = r5<int, short>; // expected-error{{constraints not satisfied for class template 'r5' [with Ts = <int, short>]}}
|
||||||
|
|
||||||
|
template<typename T> requires
|
||||||
|
false_v<(requires(T t) { T{t}; })> // T{t} creates an "UnevaluatedList" context.
|
||||||
|
// expected-note@-1 {{because 'false_v<(requires (int t) { int{t}; })>' evaluated to false}}
|
||||||
|
struct r6 {};
|
||||||
|
|
||||||
|
using r6i = r6<int>;
|
||||||
|
// expected-error@-1 {{constraints not satisfied for class template 'r6' [with T = int]}}
|
@ -6340,6 +6340,7 @@ CXCursor clang_getCursorDefinition(CXCursor C) {
|
|||||||
case Decl::UsingPack:
|
case Decl::UsingPack:
|
||||||
case Decl::Concept:
|
case Decl::Concept:
|
||||||
case Decl::LifetimeExtendedTemporary:
|
case Decl::LifetimeExtendedTemporary:
|
||||||
|
case Decl::RequiresExprBody:
|
||||||
return C;
|
return C;
|
||||||
|
|
||||||
// Declaration kinds that don't make any sense here, but are
|
// Declaration kinds that don't make any sense here, but are
|
||||||
|
@ -257,6 +257,7 @@ CXCursor cxcursor::MakeCXCursor(const Stmt *S, const Decl *Parent,
|
|||||||
case Stmt::TypeTraitExprClass:
|
case Stmt::TypeTraitExprClass:
|
||||||
case Stmt::CoawaitExprClass:
|
case Stmt::CoawaitExprClass:
|
||||||
case Stmt::ConceptSpecializationExprClass:
|
case Stmt::ConceptSpecializationExprClass:
|
||||||
|
case Stmt::RequiresExprClass:
|
||||||
case Stmt::DependentCoawaitExprClass:
|
case Stmt::DependentCoawaitExprClass:
|
||||||
case Stmt::CoyieldExprClass:
|
case Stmt::CoyieldExprClass:
|
||||||
case Stmt::CXXBindTemporaryExprClass:
|
case Stmt::CXXBindTemporaryExprClass:
|
||||||
|
Loading…
x
Reference in New Issue
Block a user