================
@@ -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

Reply via email to