Skip to content

Commit

Permalink
Re-apply "Deferred Concept Instantiation Implementation"
Browse files Browse the repository at this point in the history
This reverts commit 95d94a6.

This implements the deferred concepts instantiation, which should allow
the libstdc++ ranges to properly compile, and for the CRTP to work for
constrained functions.

Since the last attempt, this has fixed the issues from @wlei and
@mordante.

Differential Revision: https://reviews.llvm.org/D126907
  • Loading branch information
Erich Keane committed Sep 22, 2022
1 parent e0cdafe commit babdef2
Show file tree
Hide file tree
Showing 31 changed files with 1,816 additions and 243 deletions.
4 changes: 4 additions & 0 deletions clang/docs/ReleaseNotes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -345,6 +345,10 @@ C++20 Feature Support
`Issue 50455 <https://github.com/llvm/llvm-project/issues/50455>`_,
`Issue 54872 <https://github.com/llvm/llvm-project/issues/54872>`_,
`Issue 54587 <https://github.com/llvm/llvm-project/issues/54587>`_.
- Clang now correctly delays the instantiation of function constraints until
the time of checking, which should now allow the libstdc++ ranges implementation
to work for at least trivial examples. This fixes
`Issue 44178 <https://github.com/llvm/llvm-project/issues/44178>`_.

C++2b Feature Support
^^^^^^^^^^^^^^^^^^^^^
Expand Down
5 changes: 5 additions & 0 deletions clang/include/clang/AST/ASTContext.h
Original file line number Diff line number Diff line change
Expand Up @@ -2669,6 +2669,11 @@ class ASTContext : public RefCountedBase<ASTContext> {
/// template.
bool hasSameTemplateName(const TemplateName &X, const TemplateName &Y) const;

/// Determine whether two Friend functions are different because constraints
/// that refer to an enclosing template, according to [temp.friend] p9.
bool FriendsDifferByConstraints(const FunctionDecl *X,
const FunctionDecl *Y) const;

/// Determine whether the two declarations refer to the same entity.
bool isSameEntity(const NamedDecl *X, const NamedDecl *Y) const;

Expand Down
13 changes: 13 additions & 0 deletions clang/include/clang/AST/Decl.h
Original file line number Diff line number Diff line change
Expand Up @@ -2474,6 +2474,19 @@ class FunctionDecl : public DeclaratorDecl,
getCanonicalDecl()->FunctionDeclBits.IsMultiVersion = V;
}

// Sets that this is a constrained friend where the constraint refers to an
// enclosing template.
void setFriendConstraintRefersToEnclosingTemplate(bool V = true) {
getCanonicalDecl()
->FunctionDeclBits.FriendConstraintRefersToEnclosingTemplate = V;
}
// Indicates this function is a constrained friend, where the constraint
// refers to an enclosing template for hte purposes of [temp.friend]p9.
bool FriendConstraintRefersToEnclosingTemplate() const {
return getCanonicalDecl()
->FunctionDeclBits.FriendConstraintRefersToEnclosingTemplate;
}

/// Gets the kind of multiversioning attribute this declaration has. Note that
/// this can return a value even if the function is not multiversion, such as
/// the case of 'target'.
Expand Down
10 changes: 7 additions & 3 deletions clang/include/clang/AST/DeclBase.h
Original file line number Diff line number Diff line change
Expand Up @@ -1664,10 +1664,14 @@ class DeclContext {

/// Indicates if the function uses Floating Point Constrained Intrinsics
uint64_t UsesFPIntrin : 1;

// Indicates this function is a constrained friend, where the constraint
// refers to an enclosing template for hte purposes of [temp.friend]p9.
uint64_t FriendConstraintRefersToEnclosingTemplate : 1;
};

/// Number of non-inherited bits in FunctionDeclBitfields.
enum { NumFunctionDeclBits = 28 };
enum { NumFunctionDeclBits = 29 };

/// Stores the bits used by CXXConstructorDecl. If modified
/// NumCXXConstructorDeclBits and the accessor
Expand All @@ -1679,12 +1683,12 @@ class DeclContext {
/// For the bits in FunctionDeclBitfields.
uint64_t : NumFunctionDeclBits;

/// 23 bits to fit in the remaining available space.
/// 22 bits to fit in the remaining available space.
/// Note that this makes CXXConstructorDeclBitfields take
/// exactly 64 bits and thus the width of NumCtorInitializers
/// will need to be shrunk if some bit is added to NumDeclContextBitfields,
/// NumFunctionDeclBitfields or CXXConstructorDeclBitfields.
uint64_t NumCtorInitializers : 20;
uint64_t NumCtorInitializers : 19;
uint64_t IsInheritingConstructor : 1;

/// Whether this constructor has a trail-allocated explicit specifier.
Expand Down
142 changes: 120 additions & 22 deletions clang/include/clang/Sema/Sema.h
Original file line number Diff line number Diff line change
Expand Up @@ -3673,6 +3673,31 @@ class Sema final {
bool ConsiderCudaAttrs = true,
bool ConsiderRequiresClauses = true);

// Calculates whether the expression Constraint depends on an enclosing
// template, for the purposes of [temp.friend] p9.
// TemplateDepth is the 'depth' of the friend function, which is used to
// compare whether a declaration reference is referring to a containing
// template, or just the current friend function. A 'lower' TemplateDepth in
// the AST refers to a 'containing' template. As the constraint is
// uninstantiated, this is relative to the 'top' of the TU.
bool ConstraintExpressionDependsOnEnclosingTemplate(unsigned TemplateDepth,
const Expr *Constraint);

// Calculates whether the friend function depends on an enclosing template for
// the purposes of [temp.friend] p9.
bool FriendConstraintsDependOnEnclosingTemplate(const FunctionDecl *FD);

// Calculates whether two constraint expressions are equal irrespective of a
// difference in 'depth'. This takes a pair of optional 'NamedDecl's 'Old' and
// 'New', which are the "source" of the constraint, since this is necessary
// for figuring out the relative 'depth' of the constraint. The depth of the
// 'primary template' and the 'instantiated from' templates aren't necessarily
// the same, such as a case when one is a 'friend' defined in a class.
bool AreConstraintExpressionsEqual(const NamedDecl *Old,
const Expr *OldConstr,
const NamedDecl *New,
const Expr *NewConstr);

enum class AllowedExplicit {
/// Allow no explicit functions to be used.
None,
Expand Down Expand Up @@ -7152,6 +7177,21 @@ class Sema final {
LocalInstantiationScope &Scope,
const MultiLevelTemplateArgumentList &TemplateArgs);

/// used by SetupConstraintCheckingTemplateArgumentsAndScope to recursively(in
/// the case of lambdas) set up the LocalInstantiationScope of the current
/// function.
bool SetupConstraintScope(
FunctionDecl *FD, llvm::Optional<ArrayRef<TemplateArgument>> TemplateArgs,
MultiLevelTemplateArgumentList MLTAL, LocalInstantiationScope &Scope);

/// Used during constraint checking, sets up the constraint template arguemnt
/// lists, and calls SetupConstraintScope to set up the
/// LocalInstantiationScope to have the proper set of ParVarDecls configured.
llvm::Optional<MultiLevelTemplateArgumentList>
SetupConstraintCheckingTemplateArgumentsAndScope(
FunctionDecl *FD, llvm::Optional<ArrayRef<TemplateArgument>> TemplateArgs,
LocalInstantiationScope &Scope);

public:
const NormalizedConstraint *
getNormalizedAssociatedConstraints(
Expand Down Expand Up @@ -7194,6 +7234,39 @@ class Sema final {
bool CheckConstraintSatisfaction(
const NamedDecl *Template, ArrayRef<const Expr *> ConstraintExprs,
const MultiLevelTemplateArgumentList &TemplateArgLists,
SourceRange TemplateIDRange, ConstraintSatisfaction &Satisfaction) {
llvm::SmallVector<Expr *, 4> Converted;
return CheckConstraintSatisfaction(Template, ConstraintExprs, Converted,
TemplateArgLists, TemplateIDRange,
Satisfaction);
}

/// \brief Check whether the given list of constraint expressions are
/// satisfied (as if in a 'conjunction') given template arguments.
/// Additionally, takes an empty list of Expressions which is populated with
/// the instantiated versions of the ConstraintExprs.
/// \param Template the template-like entity that triggered the constraints
/// check (either a concept or a constrained entity).
/// \param ConstraintExprs a list of constraint expressions, treated as if
/// they were 'AND'ed together.
/// \param ConvertedConstraints a out parameter that will get populated with
/// the instantiated version of the ConstraintExprs if we successfully checked
/// satisfaction.
/// \param TemplateArgList the multi-level list of template arguments to
/// substitute into the constraint expression. This should be relative to the
/// top-level (hence multi-level), since we need to instantiate fully at the
/// time of checking.
/// \param TemplateIDRange The source range of the template id that
/// caused the constraints check.
/// \param Satisfaction if true is returned, will contain details of the
/// satisfaction, with enough information to diagnose an unsatisfied
/// expression.
/// \returns true if an error occurred and satisfaction could not be checked,
/// false otherwise.
bool CheckConstraintSatisfaction(
const NamedDecl *Template, ArrayRef<const Expr *> ConstraintExprs,
llvm::SmallVectorImpl<Expr *> &ConvertedConstraints,
const MultiLevelTemplateArgumentList &TemplateArgList,
SourceRange TemplateIDRange, ConstraintSatisfaction &Satisfaction);

/// \brief Check whether the given non-dependent constraint expression is
Expand All @@ -7213,8 +7286,8 @@ class Sema final {
/// \returns true if an error occurred, false otherwise.
bool CheckFunctionConstraints(const FunctionDecl *FD,
ConstraintSatisfaction &Satisfaction,
SourceLocation UsageLoc = SourceLocation());

SourceLocation UsageLoc = SourceLocation(),
bool ForOverloadResolution = false);

/// \brief Ensure that the given template arguments satisfy the constraints
/// associated with the given template, emitting a diagnostic if they do not.
Expand Down Expand Up @@ -8222,12 +8295,19 @@ class Sema final {
TPL_TemplateTemplateArgumentMatch
};

bool TemplateParameterListsAreEqual(TemplateParameterList *New,
TemplateParameterList *Old,
bool Complain,
TemplateParameterListEqualKind Kind,
SourceLocation TemplateArgLoc
= SourceLocation());
bool TemplateParameterListsAreEqual(
const NamedDecl *NewInstFrom, TemplateParameterList *New,
const NamedDecl *OldInstFrom, TemplateParameterList *Old, bool Complain,
TemplateParameterListEqualKind Kind,
SourceLocation TemplateArgLoc = SourceLocation());

bool TemplateParameterListsAreEqual(
TemplateParameterList *New, TemplateParameterList *Old, bool Complain,
TemplateParameterListEqualKind Kind,
SourceLocation TemplateArgLoc = SourceLocation()) {
return TemplateParameterListsAreEqual(nullptr, New, nullptr, Old, Complain,
Kind, TemplateArgLoc);
}

bool CheckTemplateDeclScope(Scope *S, TemplateParameterList *TemplateParams);

Expand Down Expand Up @@ -8962,7 +9042,8 @@ class Sema final {

MultiLevelTemplateArgumentList getTemplateInstantiationArgs(
const NamedDecl *D, const TemplateArgumentList *Innermost = nullptr,
bool RelativeToPrimary = false, const FunctionDecl *Pattern = nullptr);
bool RelativeToPrimary = false, const FunctionDecl *Pattern = nullptr,
bool LookBeyondLambda = false, bool IncludeContainingStruct = false);

/// A context in which code is being synthesized (where a source location
/// alone is not sufficient to identify the context). This covers template
Expand Down Expand Up @@ -9670,23 +9751,21 @@ class Sema final {
const MultiLevelTemplateArgumentList &TemplateArgs,
SourceLocation Loc, DeclarationName Entity);

TypeSourceInfo *SubstFunctionDeclType(TypeSourceInfo *T,
const MultiLevelTemplateArgumentList &TemplateArgs,
SourceLocation Loc,
DeclarationName Entity,
CXXRecordDecl *ThisContext,
Qualifiers ThisTypeQuals);
TypeSourceInfo *SubstFunctionDeclType(
TypeSourceInfo *T, const MultiLevelTemplateArgumentList &TemplateArgs,
SourceLocation Loc, DeclarationName Entity, CXXRecordDecl *ThisContext,
Qualifiers ThisTypeQuals, bool EvaluateConstraints = true);
void SubstExceptionSpec(FunctionDecl *New, const FunctionProtoType *Proto,
const MultiLevelTemplateArgumentList &Args);
bool SubstExceptionSpec(SourceLocation Loc,
FunctionProtoType::ExceptionSpecInfo &ESI,
SmallVectorImpl<QualType> &ExceptionStorage,
const MultiLevelTemplateArgumentList &Args);
ParmVarDecl *SubstParmVarDecl(ParmVarDecl *D,
const MultiLevelTemplateArgumentList &TemplateArgs,
int indexAdjustment,
Optional<unsigned> NumExpansions,
bool ExpectParameterPack);
ParmVarDecl *
SubstParmVarDecl(ParmVarDecl *D,
const MultiLevelTemplateArgumentList &TemplateArgs,
int indexAdjustment, Optional<unsigned> NumExpansions,
bool ExpectParameterPack, bool EvaluateConstraints = true);
bool SubstParmTypes(SourceLocation Loc, ArrayRef<ParmVarDecl *> Params,
const FunctionProtoType::ExtParameterInfo *ExtParamInfos,
const MultiLevelTemplateArgumentList &TemplateArgs,
Expand All @@ -9696,6 +9775,25 @@ class Sema final {
ExprResult SubstExpr(Expr *E,
const MultiLevelTemplateArgumentList &TemplateArgs);

// A RAII type used by the TemplateDeclInstantiator and TemplateInstantiator
// to disable constraint evaluation, then restore the state.
template <typename InstTy> struct ConstraintEvalRAII {
InstTy &TI;
bool OldValue;

ConstraintEvalRAII(InstTy &TI)
: TI(TI), OldValue(TI.getEvaluateConstraints()) {
TI.setEvaluateConstraints(false);
}
~ConstraintEvalRAII() { TI.setEvaluateConstraints(OldValue); }
};

// Unlike the above, this evaluates constraints, which should only happen at
// 'constraint checking' time.
ExprResult
SubstConstraintExpr(Expr *E,
const MultiLevelTemplateArgumentList &TemplateArgs);

/// Substitute the given template arguments into a list of
/// expressions, expanding pack expansions if required.
///
Expand Down Expand Up @@ -9725,7 +9823,6 @@ class Sema final {
const MultiLevelTemplateArgumentList &TemplateArgs,
TemplateArgumentListInfo &Outputs);


Decl *SubstDecl(Decl *D, DeclContext *Owner,
const MultiLevelTemplateArgumentList &TemplateArgs);

Expand Down Expand Up @@ -9816,7 +9913,8 @@ class Sema final {
const MultiLevelTemplateArgumentList &TemplateArgs);

bool SubstTypeConstraint(TemplateTypeParmDecl *Inst, const TypeConstraint *TC,
const MultiLevelTemplateArgumentList &TemplateArgs);
const MultiLevelTemplateArgumentList &TemplateArgs,
bool EvaluateConstraint);

bool InstantiateDefaultArgument(SourceLocation CallLoc, FunctionDecl *FD,
ParmVarDecl *Param);
Expand Down
8 changes: 8 additions & 0 deletions clang/include/clang/Sema/Template.h
Original file line number Diff line number Diff line change
Expand Up @@ -503,6 +503,7 @@ enum class TemplateSubstitutionKind : char {
const MultiLevelTemplateArgumentList &TemplateArgs;
Sema::LateInstantiatedAttrVec* LateAttrs = nullptr;
LocalInstantiationScope *StartingScope = nullptr;
bool EvaluateConstraints = true;

/// A list of out-of-line class template partial
/// specializations that will need to be instantiated after the
Expand All @@ -526,6 +527,13 @@ enum class TemplateSubstitutionKind : char {
SubstIndex(SemaRef, SemaRef.ArgumentPackSubstitutionIndex),
Owner(Owner), TemplateArgs(TemplateArgs) {}

void setEvaluateConstraints(bool B) {
EvaluateConstraints = B;
}
bool getEvaluateConstraints() {
return EvaluateConstraints;
}

// Define all the decl visitors using DeclNodes.inc
#define DECL(DERIVED, BASE) \
Decl *Visit ## DERIVED ## Decl(DERIVED ## Decl *D);
Expand Down
29 changes: 29 additions & 0 deletions clang/lib/AST/ASTContext.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6452,6 +6452,31 @@ static bool hasSameOverloadableAttrs(const FunctionDecl *A,
return true;
}

bool ASTContext::FriendsDifferByConstraints(const FunctionDecl *X,
const FunctionDecl *Y) const {
// If these aren't friends, then they aren't friends that differ by
// constraints.
if (!X->getFriendObjectKind() || !Y->getFriendObjectKind())
return false;

// If the the two functions share lexical declaration context, they are not in
// separate instantations, and thus in the same scope.
if (X->getLexicalDeclContext() == Y->getLexicalDeclContext())
return false;

if (!X->getDescribedFunctionTemplate()) {
assert(!Y->getDescribedFunctionTemplate() &&
"How would these be the same if they aren't both templates?");

// If these friends don't have constraints, they aren't constrained, and
// thus don't fall under temp.friend p9. Else the simple presence of a
// constraint makes them unique.
return X->getTrailingRequiresClause();
}

return X->FriendConstraintRefersToEnclosingTemplate();
}

bool ASTContext::isSameEntity(const NamedDecl *X, const NamedDecl *Y) const {
if (X == Y)
return true;
Expand Down Expand Up @@ -6532,6 +6557,10 @@ bool ASTContext::isSameEntity(const NamedDecl *X, const NamedDecl *Y) const {
FuncY->getTrailingRequiresClause()))
return false;

// Constrained friends are different in certain cases, see: [temp.friend]p9.
if (FriendsDifferByConstraints(FuncX, FuncY))
return false;

auto GetTypeAsWritten = [](const FunctionDecl *FD) {
// Map to the first declaration that we've already merged into this one.
// The TSI of redeclarations might not match (due to calling conventions
Expand Down
2 changes: 2 additions & 0 deletions clang/lib/AST/ASTImporter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3701,6 +3701,8 @@ ExpectedDecl ASTNodeImporter::VisitFunctionDecl(FunctionDecl *D) {
ToFunction->setDefaulted(D->isDefaulted());
ToFunction->setExplicitlyDefaulted(D->isExplicitlyDefaulted());
ToFunction->setDeletedAsWritten(D->isDeletedAsWritten());
ToFunction->setFriendConstraintRefersToEnclosingTemplate(
D->FriendConstraintRefersToEnclosingTemplate());
ToFunction->setRangeEnd(ToEndLoc);

// Set the parameters.
Expand Down
1 change: 1 addition & 0 deletions clang/lib/AST/Decl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2970,6 +2970,7 @@ FunctionDecl::FunctionDecl(Kind DK, ASTContext &C, DeclContext *DC,
FunctionDeclBits.IsMultiVersion = false;
FunctionDeclBits.IsCopyDeductionCandidate = false;
FunctionDeclBits.HasODRHash = false;
FunctionDeclBits.FriendConstraintRefersToEnclosingTemplate = false;
if (TrailingRequiresClause)
setTrailingRequiresClause(TrailingRequiresClause);
}
Expand Down
Loading

0 comments on commit babdef2

Please sign in to comment.