[clang] [clang][ThreadSafety] Instantiate a parameter's attribute naming a later parameter (PR #232784)

Aaron Puchert via cfe-commits cfe-commits at lists.llvm.org
Sun Oct 25 17:18:30 PDT 2026


================
@@ -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.
----------------
aaronpuchert wrote:

> 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.

I wonder whether that's related to the fact that calling conventions also survive canonicalization, unlike most static analysis attributes. Types have to be canonicalized for example when it comes to deciding which overloads are the same, or which template instantiations are the same.

```c++
void f(int (*)()) {}
void f(int (*)() [[gnu::ms_abi]]) {} // Ok.
void f(int (* _Nullable)()) {}       // error: redefinition
```

We don't want behavioral changes relative to a compiler that doesn't understand the attributes. (Unlike calling conventions, which change the ABI, static analysis attributes are just an add-on.) So they shouldn't affect overloading etc., decisions about that have be the independent of the attributes.

https://github.com/llvm/llvm-project/pull/232784


More information about the cfe-commits mailing list