mirror of
https://github.com/llvm/llvm-project.git
synced 2025-04-18 19:06:44 +00:00
[Clang][Sema] Substitute for the type aliases inside of a CTAD guide (#94740)
Similar to the approach of handling nested class templates when building a CTAD guide, we substitute the template parameters of a type alias declaration with the instantiating template arguments in order to ensure the guide eventually doesn't reference any outer template parameters. For example, ```cpp template <class T> struct Outer { using Alias = S<T>; template <class U> struct Inner { Inner(Alias); }; }; ``` we used to retain the reference to T accidentally because the TreeTransform does nothing on type alias Decls by default. Fixes https://github.com/llvm/llvm-project/issues/94614
This commit is contained in:
parent
d3923354a4
commit
869ac40648
@ -1000,6 +1000,7 @@ Bug Fixes to C++ Support
|
||||
evaluated to an integer. (#GH96670).
|
||||
- Fixed a bug where references to lambda capture inside a ``noexcept`` specifier were not correctly
|
||||
instantiated. (#GH95735).
|
||||
- Fixed a CTAD substitution bug involving type aliases that reference outer template parameters. (#GH94614).
|
||||
|
||||
Bug Fixes to AST Handling
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
@ -711,6 +711,7 @@ enum class TemplateSubstitutionKind : char {
|
||||
VarTemplateSpecializationDecl *PrevDecl = nullptr);
|
||||
|
||||
Decl *InstantiateTypedefNameDecl(TypedefNameDecl *D, bool IsTypeAlias);
|
||||
Decl *InstantiateTypeAliasTemplateDecl(TypeAliasTemplateDecl *D);
|
||||
ClassTemplatePartialSpecializationDecl *
|
||||
InstantiateClassTemplatePartialSpecialization(
|
||||
ClassTemplateDecl *ClassTemplate,
|
||||
|
@ -2178,23 +2178,110 @@ namespace {
|
||||
class ExtractTypeForDeductionGuide
|
||||
: public TreeTransform<ExtractTypeForDeductionGuide> {
|
||||
llvm::SmallVectorImpl<TypedefNameDecl *> &MaterializedTypedefs;
|
||||
ClassTemplateDecl *NestedPattern;
|
||||
const MultiLevelTemplateArgumentList *OuterInstantiationArgs;
|
||||
std::optional<TemplateDeclInstantiator> TypedefNameInstantiator;
|
||||
|
||||
public:
|
||||
typedef TreeTransform<ExtractTypeForDeductionGuide> Base;
|
||||
ExtractTypeForDeductionGuide(
|
||||
Sema &SemaRef,
|
||||
llvm::SmallVectorImpl<TypedefNameDecl *> &MaterializedTypedefs)
|
||||
: Base(SemaRef), MaterializedTypedefs(MaterializedTypedefs) {}
|
||||
llvm::SmallVectorImpl<TypedefNameDecl *> &MaterializedTypedefs,
|
||||
ClassTemplateDecl *NestedPattern,
|
||||
const MultiLevelTemplateArgumentList *OuterInstantiationArgs)
|
||||
: Base(SemaRef), MaterializedTypedefs(MaterializedTypedefs),
|
||||
NestedPattern(NestedPattern),
|
||||
OuterInstantiationArgs(OuterInstantiationArgs) {
|
||||
if (OuterInstantiationArgs)
|
||||
TypedefNameInstantiator.emplace(
|
||||
SemaRef, SemaRef.getASTContext().getTranslationUnitDecl(),
|
||||
*OuterInstantiationArgs);
|
||||
}
|
||||
|
||||
TypeSourceInfo *transform(TypeSourceInfo *TSI) { return TransformType(TSI); }
|
||||
|
||||
/// Returns true if it's safe to substitute \p Typedef with
|
||||
/// \p OuterInstantiationArgs.
|
||||
bool mightReferToOuterTemplateParameters(TypedefNameDecl *Typedef) {
|
||||
if (!NestedPattern)
|
||||
return false;
|
||||
|
||||
static auto WalkUp = [](DeclContext *DC, DeclContext *TargetDC) {
|
||||
if (DC->Equals(TargetDC))
|
||||
return true;
|
||||
while (DC->isRecord()) {
|
||||
if (DC->Equals(TargetDC))
|
||||
return true;
|
||||
DC = DC->getParent();
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
if (WalkUp(Typedef->getDeclContext(), NestedPattern->getTemplatedDecl()))
|
||||
return true;
|
||||
if (WalkUp(NestedPattern->getTemplatedDecl(), Typedef->getDeclContext()))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
QualType
|
||||
RebuildTemplateSpecializationType(TemplateName Template,
|
||||
SourceLocation TemplateNameLoc,
|
||||
TemplateArgumentListInfo &TemplateArgs) {
|
||||
if (!OuterInstantiationArgs ||
|
||||
!isa_and_present<TypeAliasTemplateDecl>(Template.getAsTemplateDecl()))
|
||||
return Base::RebuildTemplateSpecializationType(Template, TemplateNameLoc,
|
||||
TemplateArgs);
|
||||
|
||||
auto *TATD = cast<TypeAliasTemplateDecl>(Template.getAsTemplateDecl());
|
||||
auto *Pattern = TATD;
|
||||
while (Pattern->getInstantiatedFromMemberTemplate())
|
||||
Pattern = Pattern->getInstantiatedFromMemberTemplate();
|
||||
if (!mightReferToOuterTemplateParameters(Pattern->getTemplatedDecl()))
|
||||
return Base::RebuildTemplateSpecializationType(Template, TemplateNameLoc,
|
||||
TemplateArgs);
|
||||
|
||||
Decl *NewD =
|
||||
TypedefNameInstantiator->InstantiateTypeAliasTemplateDecl(TATD);
|
||||
if (!NewD)
|
||||
return QualType();
|
||||
|
||||
auto *NewTATD = cast<TypeAliasTemplateDecl>(NewD);
|
||||
MaterializedTypedefs.push_back(NewTATD->getTemplatedDecl());
|
||||
|
||||
return Base::RebuildTemplateSpecializationType(
|
||||
TemplateName(NewTATD), TemplateNameLoc, TemplateArgs);
|
||||
}
|
||||
|
||||
QualType TransformTypedefType(TypeLocBuilder &TLB, TypedefTypeLoc TL) {
|
||||
ASTContext &Context = SemaRef.getASTContext();
|
||||
TypedefNameDecl *OrigDecl = TL.getTypedefNameDecl();
|
||||
TypedefNameDecl *Decl = OrigDecl;
|
||||
// Transform the underlying type of the typedef and clone the Decl only if
|
||||
// the typedef has a dependent context.
|
||||
if (OrigDecl->getDeclContext()->isDependentContext()) {
|
||||
bool InDependentContext = OrigDecl->getDeclContext()->isDependentContext();
|
||||
|
||||
// A typedef/alias Decl within the NestedPattern may reference the outer
|
||||
// template parameters. They're substituted with corresponding instantiation
|
||||
// arguments here and in RebuildTemplateSpecializationType() above.
|
||||
// Otherwise, we would have a CTAD guide with "dangling" template
|
||||
// parameters.
|
||||
// For example,
|
||||
// template <class T> struct Outer {
|
||||
// using Alias = S<T>;
|
||||
// template <class U> struct Inner {
|
||||
// Inner(Alias);
|
||||
// };
|
||||
// };
|
||||
if (OuterInstantiationArgs && InDependentContext &&
|
||||
TL.getTypePtr()->isInstantiationDependentType()) {
|
||||
Decl = cast_if_present<TypedefNameDecl>(
|
||||
TypedefNameInstantiator->InstantiateTypedefNameDecl(
|
||||
OrigDecl, /*IsTypeAlias=*/isa<TypeAliasDecl>(OrigDecl)));
|
||||
if (!Decl)
|
||||
return QualType();
|
||||
MaterializedTypedefs.push_back(Decl);
|
||||
} else if (InDependentContext) {
|
||||
TypeLocBuilder InnerTLB;
|
||||
QualType Transformed =
|
||||
TransformType(InnerTLB, OrigDecl->getTypeSourceInfo()->getTypeLoc());
|
||||
@ -2541,8 +2628,9 @@ private:
|
||||
// defined outside of the surrounding class template. That is T in the
|
||||
// above example.
|
||||
if (NestedPattern) {
|
||||
NewParam = transformFunctionTypeParam(NewParam, OuterInstantiationArgs,
|
||||
MaterializedTypedefs);
|
||||
NewParam = transformFunctionTypeParam(
|
||||
NewParam, OuterInstantiationArgs, MaterializedTypedefs,
|
||||
/*TransformingOuterPatterns=*/true);
|
||||
if (!NewParam)
|
||||
return QualType();
|
||||
}
|
||||
@ -2550,7 +2638,8 @@ private:
|
||||
// defined at the class template and the constructor. In this example,
|
||||
// they're U and V, respectively.
|
||||
NewParam =
|
||||
transformFunctionTypeParam(NewParam, Args, MaterializedTypedefs);
|
||||
transformFunctionTypeParam(NewParam, Args, MaterializedTypedefs,
|
||||
/*TransformingOuterPatterns=*/false);
|
||||
if (!NewParam)
|
||||
return QualType();
|
||||
ParamTypes.push_back(NewParam->getType());
|
||||
@ -2594,7 +2683,8 @@ private:
|
||||
|
||||
ParmVarDecl *transformFunctionTypeParam(
|
||||
ParmVarDecl *OldParam, MultiLevelTemplateArgumentList &Args,
|
||||
llvm::SmallVectorImpl<TypedefNameDecl *> &MaterializedTypedefs) {
|
||||
llvm::SmallVectorImpl<TypedefNameDecl *> &MaterializedTypedefs,
|
||||
bool TransformingOuterPatterns) {
|
||||
TypeSourceInfo *OldDI = OldParam->getTypeSourceInfo();
|
||||
TypeSourceInfo *NewDI;
|
||||
if (auto PackTL = OldDI->getTypeLoc().getAs<PackExpansionTypeLoc>()) {
|
||||
@ -2617,7 +2707,9 @@ private:
|
||||
// members of the current instantiations with the definitions of those
|
||||
// typedefs, avoiding triggering instantiation of the deduced type during
|
||||
// deduction.
|
||||
NewDI = ExtractTypeForDeductionGuide(SemaRef, MaterializedTypedefs)
|
||||
NewDI = ExtractTypeForDeductionGuide(
|
||||
SemaRef, MaterializedTypedefs, NestedPattern,
|
||||
TransformingOuterPatterns ? &Args : nullptr)
|
||||
.transform(NewDI);
|
||||
|
||||
// Resolving a wording defect, we also inherit default arguments from the
|
||||
|
@ -1096,8 +1096,8 @@ Decl *TemplateDeclInstantiator::VisitTypeAliasDecl(TypeAliasDecl *D) {
|
||||
return Typedef;
|
||||
}
|
||||
|
||||
Decl *
|
||||
TemplateDeclInstantiator::VisitTypeAliasTemplateDecl(TypeAliasTemplateDecl *D) {
|
||||
Decl *TemplateDeclInstantiator::InstantiateTypeAliasTemplateDecl(
|
||||
TypeAliasTemplateDecl *D) {
|
||||
// Create a local instantiation scope for this type alias template, which
|
||||
// will contain the instantiations of the template parameters.
|
||||
LocalInstantiationScope Scope(SemaRef);
|
||||
@ -1143,7 +1143,14 @@ TemplateDeclInstantiator::VisitTypeAliasTemplateDecl(TypeAliasTemplateDecl *D) {
|
||||
if (!PrevAliasTemplate)
|
||||
Inst->setInstantiatedFromMemberTemplate(D);
|
||||
|
||||
Owner->addDecl(Inst);
|
||||
return Inst;
|
||||
}
|
||||
|
||||
Decl *
|
||||
TemplateDeclInstantiator::VisitTypeAliasTemplateDecl(TypeAliasTemplateDecl *D) {
|
||||
Decl *Inst = InstantiateTypeAliasTemplateDecl(D);
|
||||
if (Inst)
|
||||
Owner->addDecl(Inst);
|
||||
|
||||
return Inst;
|
||||
}
|
||||
|
@ -1,5 +1,4 @@
|
||||
// RUN: %clang_cc1 -std=c++17 -verify %s
|
||||
// expected-no-diagnostics
|
||||
|
||||
template<typename T> struct A {
|
||||
template<typename U> struct B {
|
||||
@ -16,3 +15,76 @@ using T = A<void>::B<int>;
|
||||
|
||||
using Copy = decltype(copy);
|
||||
using Copy = A<void>::B<int>;
|
||||
|
||||
namespace GH94614 {
|
||||
|
||||
template <class, class> struct S {};
|
||||
|
||||
struct trouble_1 {
|
||||
} constexpr t1;
|
||||
struct trouble_2 {
|
||||
} constexpr t2;
|
||||
struct trouble_3 {
|
||||
} constexpr t3;
|
||||
struct trouble_4 {
|
||||
} constexpr t4;
|
||||
struct trouble_5 {
|
||||
} constexpr t5;
|
||||
struct trouble_6 {
|
||||
} constexpr t6;
|
||||
struct trouble_7 {
|
||||
} constexpr t7;
|
||||
struct trouble_8 {
|
||||
} constexpr t8;
|
||||
struct trouble_9 {
|
||||
} constexpr t9;
|
||||
|
||||
template <class U, class... T> struct Unrelated {
|
||||
using Trouble = S<U, T...>;
|
||||
|
||||
template <class... V> using Trouble2 = S<V..., T...>;
|
||||
};
|
||||
|
||||
template <class T, class U> struct Outer {
|
||||
using Trouble = S<U, T>;
|
||||
|
||||
template <class V> using Trouble2 = S<V, T>;
|
||||
|
||||
template <class V> using Trouble3 = S<U, T>;
|
||||
|
||||
template <class V> struct Inner {
|
||||
template <class W> struct Paranoid {
|
||||
using Trouble4 = S<W, T>;
|
||||
|
||||
template <class... X> using Trouble5 = S<X..., T>;
|
||||
};
|
||||
|
||||
Inner(trouble_1, V v, Trouble trouble) {}
|
||||
Inner(trouble_2, V v, Trouble2<V> trouble) {}
|
||||
Inner(trouble_3, V v, Trouble3<V> trouble) {}
|
||||
Inner(trouble_4, V v, typename Unrelated<U, T>::template Trouble2<V> trouble) {}
|
||||
Inner(trouble_5, V v, typename Unrelated<U, T>::Trouble trouble) {}
|
||||
Inner(trouble_6, V v, typename Unrelated<V, T>::Trouble trouble) {}
|
||||
Inner(trouble_7, V v, typename Paranoid<V>::Trouble4 trouble) {}
|
||||
Inner(trouble_8, V v, typename Paranoid<V>::template Trouble5<V> trouble) {}
|
||||
template <class W>
|
||||
Inner(trouble_9, V v, W w, typename Paranoid<V>::template Trouble5<W> trouble) {}
|
||||
};
|
||||
};
|
||||
|
||||
S<int, char> s;
|
||||
|
||||
Outer<char, int>::Inner _1(t1, 42, s);
|
||||
Outer<char, int>::Inner _2(t2, 42, s);
|
||||
Outer<char, int>::Inner _3(t3, 42, s);
|
||||
Outer<char, int>::Inner _4(t4, 42, s);
|
||||
Outer<char, int>::Inner _5(t5, 42, s);
|
||||
Outer<char, int>::Inner _6(t6, 42, s);
|
||||
Outer<char, int>::Inner _7(t7, 42, s);
|
||||
Outer<char, int>::Inner _8(t8, 42, s);
|
||||
Outer<char, int>::Inner _9(t9, 42, 24, s);
|
||||
|
||||
// Make sure we don't accidentally inject the TypedefNameDecl into the TU.
|
||||
Trouble should_not_be_in_the_tu_decl; // expected-error {{unknown type name 'Trouble'}}
|
||||
|
||||
} // namespace GH94614
|
||||
|
Loading…
x
Reference in New Issue
Block a user