================
@@ -5493,23 +5520,25 @@ TypeSourceInfo
*TemplateDeclInstantiator::SubstFunctionType(
// the function parameters themselves.
const FunctionProtoType *OldProto =
cast<FunctionProtoType>(OldProtoLoc.getType());
- for (unsigned i = 0, i_end = OldProtoLoc.getNumParams(); i != i_end;
- ++i) {
- ParmVarDecl *OldParam = OldProtoLoc.getParam(i);
- if (!OldParam) {
- Params.push_back(SemaRef.BuildParmVarDeclForTypedef(
- D, D->getLocation(), OldProto->getParamType(i)));
- continue;
- }
-
- ParmVarDecl *Parm = SemaRef.SubstParmVarDecl(
- OldParam, TemplateArgs, /*indexAdjustment=*/0,
- /*NumExpansions=*/std::nullopt,
- /*ExpectParameterPack=*/false, EvaluateConstraints);
- if (!Parm)
- return nullptr;
- Params.push_back(Parm);
- }
+ SmallVector<QualType, 4> ParamTypes;
+ Sema::ExtParameterInfoBuilder ExtParamInfos;
+ if (SemaRef.SubstParmTypes(D->getLocation(), OldProtoLoc.getParams(),
+ nullptr, TemplateArgs, ParamTypes, &Params,
+ ExtParamInfos, OldProto->param_type_begin(),
+ EvaluateConstraints))
+ return nullptr;
+ // Declare a parameter that was only a type in the prototype. A function
+ // declared through a typedef with an attribute that rewrites the
+ // function type in place, such as noreturn, gets a prototype whose
+ // parameters have no ParmVarDecl, so no attribute of the typedef's
+ // parameters reaches it; see TypeSpecLocFiller in SemaType.
+ // FIXME: Keep the typedef's parameters there instead. The dependent
+ // branch above returns null for such a prototype, dropping the member.
----------------
vtjnash wrote:
reviewer comment: The FIXME's last sentence refers to pre-existing bugs which
needs redesign elsewhere, and then this branch goes away.
---
More details, mostly synthesized by Claude, since they aren't actually relevant
to the PR, just for context on why this logic is needed:
Since d462aa5a, when the typedef is dependent, this same member would take the
dependent path, and the added early return in that commit makes this member
vanish silently:
template <typename T> struct X {
typedef void functype(T);
__attribute__((noreturn)) functype nr;
};
void use(X<int> x) { x.nr(0); } // error: no member named 'nr' in 'X<int>'
It is a pre-existing split in how SemaType applies function-type attributes,
and the type-location builder has a FIXME where the split lands.
Two families of function-type attributes. handleFunctionTypeAttr in
clang/lib/Sema/SemaType.cpp applies calling conventions and ns_returns_retained
by wrapping the written type in an AttributedType, so the typedef sugar
survives as the modified type and the type location still mirrors the
declarator. The other family rewrites the function type in place through
FunctionTypeUnwrapper, which desugars through the typedef and rebuilds a bare
prototype: noreturn, regparm, nocf_check, cmse_nonsecure_call,
no_caller_saved_registers, cfi_unchecked_callee, cfi_salt, and the Arm
streaming family. On a typedef-declared method all of those produce the same
shape. A quick dump of a typedef-declared method with each shows it:
```
┌──────────────────────────────────┬───────────────────────────────────────────────────┐
│ Attribute │ Declared type after Sema
│
├──────────────────────────────────┼───────────────────────────────────────────────────┤
│ noreturn, regparm(1), nocf_check │ int (int, int) __attribute__((...)),
typedef gone │
├──────────────────────────────────┼───────────────────────────────────────────────────┤
│ cdecl │ functype __attribute__((cdecl)), typedef
kept │
├──────────────────────────────────┼───────────────────────────────────────────────────┤
│ none │ functype
│
└──────────────────────────────────┴───────────────────────────────────────────────────┘
```
Where the null entries come from. GetTypeSourceInfoForDeclarator walks the
declarator chunks and then hands the remaining type location to
TypeSpecLocFiller to fill from the decl-specifier. That filler has cases for
the type-specifier kinds it expects, and a default that just calls initialize
with a FIXME saying other kinds should be added and the default turned into an
assert. A bare prototype coming from a typedef specifier hits that default, and
initialize leaves every parameter entry null. SubstFunctionType's second path
is the consumer that has to cope.
**Should SemaType route it differently?** Arguably yes, as a separate cleanup:
the in-place family could preserve sugar the way the calling-convention family
does, or the filler could learn the case. The visible cost today is small,
diagnostics print the desugared type and the parameters have no source ranges,
which is why nobody has fixed it since 2013. It is orthogonal to attribute
deferral, so for this branch the right handling is what the tree does: build
the missing declarations from the substituted type and treat them as having no
attributes to defer.
https://github.com/llvm/llvm-project/pull/232784
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits