return SubstitutedAtomicExpr;
}
+ if (SubstitutedAtomicExpr.get()->isValueDependent())
+ return SubstitutedAtomicExpr;
+
EnterExpressionEvaluationContext ConstantEvaluated(
S, Sema::ExpressionEvaluationContext::ConstantEvaluated);
SmallVector<PartialDiagnosticAt, 2> EvaluationDiags;
};
} // namespace
+static const Expr *SubstituteConstraintExpression(Sema &S, const NamedDecl *ND,
+ const Expr *ConstrExpr) {
+ MultiLevelTemplateArgumentList MLTAL = S.getTemplateInstantiationArgs(
+ ND, /*Final=*/false, /*Innermost=*/nullptr,
+ /*RelativeToPrimary=*/true,
+ /*Pattern=*/nullptr,
+ /*ForConstraintInstantiation=*/true, /*SkipForSpecialization*/ false);
+ if (MLTAL.getNumSubstitutedLevels() == 0)
+ return ConstrExpr;
+ Sema::SFINAETrap SFINAE(S, /*AccessCheckingSFINAE=*/false);
+ std::optional<Sema::CXXThisScopeRAII> ThisScope;
+ if (auto *RD = dyn_cast<CXXRecordDecl>(ND->getDeclContext()))
+ ThisScope.emplace(S, const_cast<CXXRecordDecl *>(RD), Qualifiers());
+ ExprResult SubstConstr =
+ S.SubstConstraintExpr(const_cast<clang::Expr *>(ConstrExpr), MLTAL);
+ if (SFINAE.hasErrorOccurred() || !SubstConstr.isUsable())
+ return nullptr;
+ return SubstConstr.get();
+}
+
bool Sema::AreConstraintExpressionsEqual(const NamedDecl *Old,
const Expr *OldConstr,
const NamedDecl *New,
const Expr *NewConstr) {
+ if (OldConstr == NewConstr)
+ return true;
if (Old && New && Old != New) {
- unsigned Depth1 = CalculateTemplateDepthForConstraints(
- *this, Old);
- unsigned Depth2 = CalculateTemplateDepthForConstraints(
- *this, New);
-
- // Adjust the 'shallowest' verison of this to increase the depth to match
- // the 'other'.
- if (Depth2 > Depth1) {
- OldConstr = AdjustConstraintDepth(*this, Depth2 - Depth1)
- .TransformExpr(const_cast<Expr *>(OldConstr))
- .get();
- } else if (Depth1 > Depth2) {
- NewConstr = AdjustConstraintDepth(*this, Depth1 - Depth2)
- .TransformExpr(const_cast<Expr *>(NewConstr))
- .get();
- }
+ if (const Expr *SubstConstr =
+ SubstituteConstraintExpression(*this, Old, OldConstr))
+ OldConstr = SubstConstr;
+ else
+ return false;
+ if (const Expr *SubstConstr =
+ SubstituteConstraintExpression(*this, New, NewConstr))
+ NewConstr = SubstConstr;
+ else
+ return false;
}
llvm::FoldingSetNodeID ID1, ID2;
// We check the return type and template parameter lists for function
// templates first; the remaining checks follow.
bool SameTemplateParameterList = TemplateParameterListsAreEqual(
- NewTemplate->getTemplateParameters(),
+ NewTemplate, NewTemplate->getTemplateParameters(), OldTemplate,
OldTemplate->getTemplateParameters(), false, TPL_TemplateMatch);
bool SameReturnType = Context.hasSameType(Old->getDeclaredReturnType(),
New->getDeclaredReturnType());
return Response::UseNextDecl(Function);
}
+Response HandleFunctionTemplateDecl(const FunctionTemplateDecl *FTD) {
+ return Response::ChangeDecl(FTD->getLexicalDeclContext());
+}
+
Response HandleRecordDecl(const CXXRecordDecl *Rec,
MultiLevelTemplateArgumentList &Result,
ASTContext &Context,
} else if (const auto *CSD =
dyn_cast<ImplicitConceptSpecializationDecl>(CurDecl)) {
R = HandleImplicitConceptSpecializationDecl(CSD, Result);
+ } else if (const auto *FTD = dyn_cast<FunctionTemplateDecl>(CurDecl)) {
+ R = HandleFunctionTemplateDecl(FTD);
} else if (!isa<DeclContext>(CurDecl)) {
R = Response::DontClearRelativeToPrimaryNextDecl(CurDecl);
if (CurDecl->getDeclContext()->isTranslationUnit()) {
<< QualifierLoc.getSourceRange();
return nullptr;
}
-
- if (PrevClassTemplate) {
- const ClassTemplateDecl *MostRecentPrevCT =
- PrevClassTemplate->getMostRecentDecl();
- TemplateParameterList *PrevParams =
- MostRecentPrevCT->getTemplateParameters();
-
- // Make sure the parameter lists match.
- if (!SemaRef.TemplateParameterListsAreEqual(
- D->getTemplatedDecl(), InstParams,
- MostRecentPrevCT->getTemplatedDecl(), PrevParams, true,
- Sema::TPL_TemplateMatch))
- return nullptr;
-
- // Do some additional validation, then merge default arguments
- // from the existing declarations.
- if (SemaRef.CheckTemplateParameterList(InstParams, PrevParams,
- Sema::TPC_ClassTemplate))
- return nullptr;
- }
}
CXXRecordDecl *RecordInst = CXXRecordDecl::Create(
SemaRef.Context, Pattern->getTagKind(), DC, Pattern->getBeginLoc(),
Pattern->getLocation(), Pattern->getIdentifier(), PrevDecl,
/*DelayTypeCreation=*/true);
-
if (QualifierLoc)
RecordInst->setQualifierInfo(QualifierLoc);
ClassTemplateDecl *Inst
= ClassTemplateDecl::Create(SemaRef.Context, DC, D->getLocation(),
D->getIdentifier(), InstParams, RecordInst);
- assert(!(isFriend && Owner->isDependentContext()));
- Inst->setPreviousDecl(PrevClassTemplate);
-
RecordInst->setDescribedClassTemplate(Inst);
if (isFriend) {
- if (PrevClassTemplate)
+ assert(!Owner->isDependentContext());
+ Inst->setLexicalDeclContext(Owner);
+ RecordInst->setLexicalDeclContext(Owner);
+
+ if (PrevClassTemplate) {
+ RecordInst->setTypeForDecl(
+ PrevClassTemplate->getTemplatedDecl()->getTypeForDecl());
+ const ClassTemplateDecl *MostRecentPrevCT =
+ PrevClassTemplate->getMostRecentDecl();
+ TemplateParameterList *PrevParams =
+ MostRecentPrevCT->getTemplateParameters();
+
+ // Make sure the parameter lists match.
+ if (!SemaRef.TemplateParameterListsAreEqual(
+ RecordInst, InstParams, MostRecentPrevCT->getTemplatedDecl(),
+ PrevParams, true, Sema::TPL_TemplateMatch))
+ return nullptr;
+
+ // Do some additional validation, then merge default arguments
+ // from the existing declarations.
+ if (SemaRef.CheckTemplateParameterList(InstParams, PrevParams,
+ Sema::TPC_ClassTemplate))
+ return nullptr;
+
Inst->setAccess(PrevClassTemplate->getAccess());
- else
+ } else {
Inst->setAccess(D->getAccess());
+ }
Inst->setObjectOfFriendDecl();
// TODO: do we want to track the instantiation progeny of this
Inst->setInstantiatedFromMemberTemplate(D);
}
+ Inst->setPreviousDecl(PrevClassTemplate);
+
// Trigger creation of the type for the instantiation.
- SemaRef.Context.getInjectedClassNameType(RecordInst,
- Inst->getInjectedClassNameSpecialization());
+ SemaRef.Context.getInjectedClassNameType(
+ RecordInst, Inst->getInjectedClassNameSpecialization());
// Finish handling of friends.
if (isFriend) {
DC->makeDeclVisibleInContext(Inst);
- Inst->setLexicalDeclContext(Owner);
- RecordInst->setLexicalDeclContext(Owner);
return Inst;
}
static_assert(S<int>::specialization("str") == SPECIALIZATION_REQUIRES);
} // namespace multiple_template_parameter_lists
+
+static constexpr int CONSTRAINED_METHOD_1 = 1;
+static constexpr int CONSTRAINED_METHOD_2 = 2;
+
+namespace constrained_members {
+
+template <int>
+struct S {
+ template <Concept C>
+ static constexpr int constrained_method();
+};
+
+template <>
+template <Concept C>
+constexpr int S<1>::constrained_method() { return CONSTRAINED_METHOD_1; }
+
+template <>
+template <Concept C>
+constexpr int S<2>::constrained_method() { return CONSTRAINED_METHOD_2; }
+
+static_assert(S<1>::constrained_method<XY>() == CONSTRAINED_METHOD_1);
+static_assert(S<2>::constrained_method<XY>() == CONSTRAINED_METHOD_2);
+
+
+template <class T1, class T2>
+concept ConceptT1T2 = true;
+
+template<typename T3>
+struct S12 {
+ template<ConceptT1T2<T3> T4>
+ static constexpr int constrained_method();
+};
+
+template<>
+template<ConceptT1T2<int> T5>
+constexpr int S12<int>::constrained_method() { return CONSTRAINED_METHOD_1; }
+
+template<>
+template<ConceptT1T2<double> T5>
+constexpr int S12<double>::constrained_method() { return CONSTRAINED_METHOD_2; }
+
+static_assert(S12<int>::constrained_method<XY>() == CONSTRAINED_METHOD_1);
+static_assert(S12<double>::constrained_method<XY>() == CONSTRAINED_METHOD_2);
+
+} // namespace constrained members
+
+namespace constrained_members_of_nested_types {
+
+template <int>
+struct S {
+ struct Inner0 {
+ struct Inner1 {
+ template <Concept C>
+ static constexpr int constrained_method();
+ };
+ };
+};
+
+template <>
+template <Concept C>
+constexpr int S<1>::Inner0::Inner1::constrained_method() { return CONSTRAINED_METHOD_1; }
+
+template <>
+template <Concept C>
+constexpr int S<2>::Inner0::Inner1::constrained_method() { return CONSTRAINED_METHOD_2; }
+
+static_assert(S<1>::Inner0::Inner1::constrained_method<XY>() == CONSTRAINED_METHOD_1);
+static_assert(S<2>::Inner0::Inner1::constrained_method<XY>() == CONSTRAINED_METHOD_2);
+
+
+template <class T1, class T2>
+concept ConceptT1T2 = true;
+
+template<typename T3>
+struct S12 {
+ struct Inner0 {
+ struct Inner1 {
+ template<ConceptT1T2<T3> T4>
+ static constexpr int constrained_method();
+ };
+ };
+};
+
+template<>
+template<ConceptT1T2<int> T5>
+constexpr int S12<int>::Inner0::Inner1::constrained_method() { return CONSTRAINED_METHOD_1; }
+
+template<>
+template<ConceptT1T2<double> T5>
+constexpr int S12<double>::Inner0::Inner1::constrained_method() { return CONSTRAINED_METHOD_2; }
+
+static_assert(S12<int>::Inner0::Inner1::constrained_method<XY>() == CONSTRAINED_METHOD_1);
+static_assert(S12<double>::Inner0::Inner1::constrained_method<XY>() == CONSTRAINED_METHOD_2);
+
+} // namespace constrained_members_of_nested_types
+
+namespace constrained_member_sfinae {
+
+template<int N> struct S {
+ template<class T>
+ static constexpr int constrained_method() requires (sizeof(int[N * 1073741824 + 4]) == 16) {
+ return CONSTRAINED_METHOD_1;
+ }
+
+ template<class T>
+ static constexpr int constrained_method() requires (sizeof(int[N]) == 16);
+};
+
+template<>
+template<typename T>
+constexpr int S<4>::constrained_method() requires (sizeof(int[4]) == 16) {
+ return CONSTRAINED_METHOD_2;
+}
+
+// Verify that there is no amiguity in this case.
+static_assert(S<4>::constrained_method<double>() == CONSTRAINED_METHOD_2);
+
+} // namespace constrained_member_sfinae
+
+namespace requires_expression_references_members {
+
+void accept1(int x);
+void accept2(XY xy);
+
+template <class T> struct S {
+ T Field = T();
+
+ constexpr int constrained_method()
+ requires requires { accept1(Field); };
+
+ constexpr int constrained_method()
+ requires requires { accept2(Field); };
+};
+
+template <class T>
+constexpr int S<T>::constrained_method()
+ requires requires { accept1(Field); } {
+ return CONSTRAINED_METHOD_1;
+}
+
+template <class T>
+constexpr int S<T>::constrained_method()
+ requires requires { accept2(Field); } {
+ return CONSTRAINED_METHOD_2;
+}
+
+static_assert(S<int>().constrained_method() == CONSTRAINED_METHOD_1);
+static_assert(S<XY>().constrained_method() == CONSTRAINED_METHOD_2);
+
+} // namespace requires_expression_references_members
static_assert(Parent<int>::TakesBinary<int, 0ULL>::i == 0);
}
+namespace TemplateInsideNonTemplateClass {
+template<typename T, typename U> concept C = true;
+
+template<typename T> auto L = []<C<T> U>() {};
+
+struct Q {
+ template<C<int> U> friend constexpr auto decltype(L<int>)::operator()() const;
+};
+} // namespace TemplateInsideNonTemplateClass
<tr>
<td><a href="https://wg21.link/p1980r0">P1980R0</a></td>
</tr>
- <tr> <!-- from Prague -->
- <td><a href="https://wg21.link/p2103r0">P2103R0</a></td>
- </tr>
<tr> <!-- from February 2022 -->
<td><a href="https://wg21.link/p2493r0">P2493R0</a></td>
</tr>
<td><a href="https://wg21.link/p2113r0">P2113R0</a></td>
<td rowspan="1" class="full" align="center">Clang 16</td>
</tr>
+ <tr>
+ <td><a href="https://wg21.link/p2103r0">P2103R0</a></td>
+ </tr>
<!-- Albuquerque papers -->
<tr>
<td>Range-based for statements with initializer</td>