[libcxx-commits] [clang] [libcxx] [Clang] Implement CWG2369 "Ordering between constraints and substitution" (PR #102857)

Younan Zhang via libcxx-commits libcxx-commits at lists.llvm.org
Sat Aug 17 18:26:26 PDT 2024


================
@@ -1122,6 +1122,154 @@ bool Sema::CheckInstantiatedFunctionTemplateConstraints(
                                      PointOfInstantiation, Satisfaction);
 }
 
+namespace {
+
+// We employ a TreeTransform because RAV couldn't recurse into a bunch of
+// Exprs e.g. SizeOfPackExpr, CXXFoldExpr, etc.
+// FIXME: Could we do the Decl instantiation as we substitute into
+// the constraint expressions?
+class InstantiateReferencedParameter
+    : public TreeTransform<InstantiateReferencedParameter> {
+  const MultiLevelTemplateArgumentList &TemplateArgs;
+
+  llvm::SmallPtrSet<ParmVarDecl *, 4> InstantiatedDecls;
+
+  FunctionDecl *PrimaryTemplatedFunction;
+
+  using inherited = TreeTransform<InstantiateReferencedParameter>;
+
+  bool instantiateParameterToScope(ParmVarDecl *OldParm,
+                                   LocalInstantiationScope &Scope) {
+    // The current context might have been changed by lambda expressions. So
+    // resume it before we substitute into parameters.
+    Sema::ContextRAII Context(SemaRef, PrimaryTemplatedFunction);
+    std::optional<unsigned> NumExpansions;
+    ParmVarDecl *NewParm = nullptr;
+    unsigned IndexAdjustment = 0;
+    if (OldParm->isParameterPack()) {
+      SmallVector<UnexpandedParameterPack, 2> Unexpanded;
+      TypeLoc TL = OldParm->getTypeSourceInfo()->getTypeLoc();
+      PackExpansionTypeLoc ExpansionTL = TL.castAs<PackExpansionTypeLoc>();
+      TypeLoc Pattern = ExpansionTL.getPatternLoc();
+      SemaRef.collectUnexpandedParameterPacks(Pattern, Unexpanded);
+
+      assert(!Unexpanded.empty() &&
+             "A pack Decl doesn't contain anything unexpanded?");
+
+      bool ShouldExpand = false;
+      bool RetainExpansion = false;
+      std::optional<unsigned> OrigNumExpansions =
+          ExpansionTL.getTypePtr()->getNumExpansions();
+      NumExpansions = OrigNumExpansions;
+      if (SemaRef.CheckParameterPacksForExpansion(
+              ExpansionTL.getEllipsisLoc(), Pattern.getSourceRange(),
+              Unexpanded, TemplateArgs, ShouldExpand, RetainExpansion,
+              NumExpansions))
+        return true;
+
+      assert(ShouldExpand && !RetainExpansion &&
+             "Shouldn't retain an expansion here!");
+      Scope.MakeInstantiatedLocalArgPack(OldParm);
+
+      for (unsigned I = 0; I != *NumExpansions; ++I) {
+        Sema::ArgumentPackSubstitutionIndexRAII SubstIndex(SemaRef, I);
+        ParmVarDecl *NewParm = SemaRef.SubstParmVarDecl(
+            OldParm, TemplateArgs, /*indexAdjustment=*/IndexAdjustment++,
+            NumExpansions, /*ExpectParameterPack=*/false,
+            /*EvaluateConstraints=*/false);
+        if (!NewParm)
+          return true;
+      }
+
+      return false;
+    }
+    NewParm = SemaRef.SubstParmVarDecl(OldParm, TemplateArgs,
+                                       /*indexAdjustment=*/IndexAdjustment,
+                                       std::nullopt,
+                                       /*ExpectParameterPack=*/false);
+    if (!NewParm)
+      return true;
+    Scope.InstantiatedLocal(OldParm, NewParm);
+    return false;
+  }
+
+public:
+  InstantiateReferencedParameter(
+      Sema &SemaRef, const MultiLevelTemplateArgumentList &TemplateArgs,
+      FunctionDecl *PrimaryTemplatedFunction)
+      : inherited(SemaRef), TemplateArgs(TemplateArgs),
+        PrimaryTemplatedFunction(PrimaryTemplatedFunction) {}
+
+  Decl *TransformDecl(SourceLocation Loc, Decl *D) {
+    if (auto *PVD = dyn_cast_if_present<ParmVarDecl>(D);
+        PVD && PVD->getDeclContext() == PrimaryTemplatedFunction &&
+        !InstantiatedDecls.contains(PVD)) {
+      instantiateParameterToScope(PVD, *SemaRef.CurrentInstantiationScope);
----------------
zyn0217 wrote:

That would cause an implementation divergence with GCC: https://gcc.godbolt.org/z/7MobE9b61
If we instantiate all parameters, we wouldn't accept such a case that GCC currently accepts.

The standard seems *vague* ([It](https://cplusplus.github.io/CWG/issues/2369.html) just said `function type` shouldn't be replaced before the constraint evaluation) about this, but I presume the intent was to instantiate parameters on demand.

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


More information about the libcxx-commits mailing list