https://github.com/kevinwkt updated 
https://github.com/llvm/llvm-project/pull/207429

>From ae7336d03baf6a77e7a02f2d67b8c28c8964fe04 Mon Sep 17 00:00:00 2001
From: Kyungtak Woo <[email protected]>
Date: Fri, 3 Jul 2026 17:05:36 +0000
Subject: [PATCH 1/3] [Clang][Sema] Ensure explicitly defaulted functions
 respect FP pragmas from their declaration site

---
 clang/include/clang/AST/Decl.h                |  10 +-
 clang/lib/AST/Decl.cpp                        |   3 +-
 clang/lib/Sema/SemaDeclCXX.cpp                |  45 +++++++-
 .../lib/Sema/SemaTemplateInstantiateDecl.cpp  |   5 +-
 clang/lib/Serialization/ASTReaderDecl.cpp     |   4 +-
 clang/lib/Serialization/ASTWriterDecl.cpp     |   2 +
 .../defaulted-function-fp-features.cpp        | 101 ++++++++++++++++++
 7 files changed, 160 insertions(+), 10 deletions(-)
 create mode 100644 clang/test/CodeGenCXX/defaulted-function-fp-features.cpp

diff --git a/clang/include/clang/AST/Decl.h b/clang/include/clang/AST/Decl.h
index 88c900203630a..7f4ea059f699e 100644
--- a/clang/include/clang/AST/Decl.h
+++ b/clang/include/clang/AST/Decl.h
@@ -2049,13 +2049,16 @@ class FunctionDecl : public DeclaratorDecl,
 
   };
 
-  /// Stashed information about a defaulted/deleted function body.
+  /// Stashed information about a defaulted/deleted function body, including
+  /// the active FP pragma overrides (FPOptionsOverride) from the declaration
+  /// site. These overrides are required to correctly synthesize the function 
body.
   class DefaultedOrDeletedFunctionInfo final
       : llvm::TrailingObjects<DefaultedOrDeletedFunctionInfo, DeclAccessPair,
                               StringLiteral *> {
     friend TrailingObjects;
     unsigned NumLookups;
     bool HasDeletedMessage;
+    uint64_t FPFeatures;
 
     size_t numTrailingObjects(OverloadToken<DeclAccessPair>) const {
       return NumLookups;
@@ -2064,7 +2067,10 @@ class FunctionDecl : public DeclaratorDecl,
   public:
     static DefaultedOrDeletedFunctionInfo *
     Create(ASTContext &Context, ArrayRef<DeclAccessPair> Lookups,
-           StringLiteral *DeletedMessage = nullptr);
+           StringLiteral *DeletedMessage = nullptr,
+           uint64_t FPFeatures = 0);
+
+    uint64_t getFPFeatures() const { return FPFeatures; }
 
     /// Get the unqualified lookup results that should be used in this
     /// defaulted function definition.
diff --git a/clang/lib/AST/Decl.cpp b/clang/lib/AST/Decl.cpp
index b23bf73ae803c..1b3976172db80 100644
--- a/clang/lib/AST/Decl.cpp
+++ b/clang/lib/AST/Decl.cpp
@@ -3116,7 +3116,7 @@ bool FunctionDecl::isVariadic() const {
 FunctionDecl::DefaultedOrDeletedFunctionInfo *
 FunctionDecl::DefaultedOrDeletedFunctionInfo::Create(
     ASTContext &Context, ArrayRef<DeclAccessPair> Lookups,
-    StringLiteral *DeletedMessage) {
+    StringLiteral *DeletedMessage, uint64_t FPFeatures) {
   static constexpr size_t Alignment =
       std::max({alignof(DefaultedOrDeletedFunctionInfo),
                 alignof(DeclAccessPair), alignof(StringLiteral *)});
@@ -3127,6 +3127,7 @@ FunctionDecl::DefaultedOrDeletedFunctionInfo::Create(
       new (Context.Allocate(Size, Alignment)) DefaultedOrDeletedFunctionInfo;
   Info->NumLookups = Lookups.size();
   Info->HasDeletedMessage = DeletedMessage != nullptr;
+  Info->FPFeatures = FPFeatures;
 
   llvm::uninitialized_copy(Lookups, 
Info->getTrailingObjects<DeclAccessPair>());
   if (DeletedMessage)
diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp
index c1caf9a58d650..5969af4da3043 100644
--- a/clang/lib/Sema/SemaDeclCXX.cpp
+++ b/clang/lib/Sema/SemaDeclCXX.cpp
@@ -6906,6 +6906,25 @@ Sema::getDefaultedFunctionKind(const FunctionDecl *FD) {
   return DefaultedFunctionKind();
 }
 
+namespace {
+/// RAII object to restore the floating-point (FP) features active at the time
+/// a defaulted function was declared. This ensures that the synthesized body
+/// of the function respects the FP pragmas (e.g., #pragma STDC FENV_ACCESS)
+/// that were in effect when the function was explicitly defaulted.
+struct DefaultedFunctionFPFeaturesRAII {
+  Sema::FPFeaturesStateRAII SavedFPFeatures;
+  DefaultedFunctionFPFeaturesRAII(Sema &S, FunctionDecl *FD)
+      : SavedFPFeatures(S) {
+    auto *Info = FD->getDefaultedOrDeletedInfo();
+    FPOptionsOverride FPO =
+        Info ? FPOptionsOverride::getFromOpaqueInt(Info->getFPFeatures())
+             : FPOptionsOverride();
+    S.CurFPFeatures = FPO.applyOverrides(S.LangOpts);
+    S.FpPragmaStack.CurrentValue = FPO;
+  }
+};
+} // namespace
+
 static void DefineDefaultedFunction(Sema &S, FunctionDecl *FD,
                                     SourceLocation DefaultLoc) {
   Sema::DefaultedFunctionKind DFK = S.getDefaultedFunctionKind(FD);
@@ -9027,9 +9046,10 @@ bool Sema::CheckExplicitlyDefaultedComparison(Scope *S, 
FunctionDecl *FD,
     UnresolvedSet<32> Operators;
     lookupOperatorsForDefaultedComparison(*this, S, Operators,
                                           FD->getOverloadedOperator());
-    FD->setDefaultedOrDeletedInfo(
-        FunctionDecl::DefaultedOrDeletedFunctionInfo::Create(
-            Context, Operators.pairs()));
+    auto *Info = FunctionDecl::DefaultedOrDeletedFunctionInfo::Create(
+        Context, Operators.pairs(), /*DeletedMessage=*/nullptr,
+        CurFPFeatureOverrides().getAsOpaqueInt());
+    FD->setDefaultedOrDeletedInfo(Info);
   }
 
   // C++2a [class.compare.default]p1:
@@ -9374,6 +9394,8 @@ void Sema::DefineDefaultedComparison(SourceLocation 
UseLoc, FunctionDecl *FD,
   // Add a context note for diagnostics produced after this point.
   Scope.addContextNote(UseLoc);
 
+  DefaultedFunctionFPFeaturesRAII RestoreFP(*this, FD);
+
   {
     // Build and set up the function body.
     // The first parameter has type maybe-ref-to maybe-const T, use that to get
@@ -14366,6 +14388,7 @@ CXXConstructorDecl 
*Sema::DeclareImplicitDefaultConstructor(
 
 void Sema::DefineImplicitDefaultConstructor(SourceLocation CurrentLocation,
                                             CXXConstructorDecl *Constructor) {
+  DefaultedFunctionFPFeaturesRAII RestoreFP(*this, Constructor);
   assert((Constructor->isDefaulted() && Constructor->isDefaultConstructor() &&
           !Constructor->doesThisDeclarationHaveABody() &&
           !Constructor->isDeleted()) &&
@@ -14665,6 +14688,7 @@ CXXDestructorDecl 
*Sema::DeclareImplicitDestructor(CXXRecordDecl *ClassDecl) {
 
 void Sema::DefineImplicitDestructor(SourceLocation CurrentLocation,
                                     CXXDestructorDecl *Destructor) {
+  DefaultedFunctionFPFeaturesRAII RestoreFP(*this, Destructor);
   assert((Destructor->isDefaulted() &&
           !Destructor->doesThisDeclarationHaveABody() &&
           !Destructor->isDeleted()) &&
@@ -15355,6 +15379,7 @@ static void diagnoseDeprecatedCopyOperation(Sema &S, 
CXXMethodDecl *CopyOp) {
 
 void Sema::DefineImplicitCopyAssignment(SourceLocation CurrentLocation,
                                         CXXMethodDecl *CopyAssignOperator) {
+  DefaultedFunctionFPFeaturesRAII RestoreFP(*this, CopyAssignOperator);
   assert((CopyAssignOperator->isDefaulted() &&
           CopyAssignOperator->isOverloadedOperator() &&
           CopyAssignOperator->getOverloadedOperator() == OO_Equal &&
@@ -15742,6 +15767,7 @@ static void checkMoveAssignmentForRepeatedMove(Sema &S, 
CXXRecordDecl *Class,
 
 void Sema::DefineImplicitMoveAssignment(SourceLocation CurrentLocation,
                                         CXXMethodDecl *MoveAssignOperator) {
+  DefaultedFunctionFPFeaturesRAII RestoreFP(*this, MoveAssignOperator);
   assert((MoveAssignOperator->isDefaulted() &&
           MoveAssignOperator->isOverloadedOperator() &&
           MoveAssignOperator->getOverloadedOperator() == OO_Equal &&
@@ -16075,6 +16101,7 @@ CXXConstructorDecl 
*Sema::DeclareImplicitCopyConstructor(
 
 void Sema::DefineImplicitCopyConstructor(SourceLocation CurrentLocation,
                                          CXXConstructorDecl *CopyConstructor) {
+  DefaultedFunctionFPFeaturesRAII RestoreFP(*this, CopyConstructor);
   assert((CopyConstructor->isDefaulted() &&
           CopyConstructor->isCopyConstructor() &&
           !CopyConstructor->doesThisDeclarationHaveABody() &&
@@ -16213,6 +16240,7 @@ CXXConstructorDecl 
*Sema::DeclareImplicitMoveConstructor(
 
 void Sema::DefineImplicitMoveConstructor(SourceLocation CurrentLocation,
                                          CXXConstructorDecl *MoveConstructor) {
+  DefaultedFunctionFPFeaturesRAII RestoreFP(*this, MoveConstructor);
   assert((MoveConstructor->isDefaulted() &&
           MoveConstructor->isMoveConstructor() &&
           !MoveConstructor->doesThisDeclarationHaveABody() &&
@@ -18775,6 +18803,17 @@ void Sema::SetDeclDefaulted(Decl *Dcl, SourceLocation 
DefaultLoc) {
       return;
   }
 
+  // Only allocate DefaultedOrDeletedFunctionInfo if we actually have
+  // non-default FP features to stash. This avoids memory overhead for
+  // the vast majority of defaulted functions.
+  if (!FD->getDefaultedOrDeletedInfo() &&
+      CurFPFeatureOverrides().requiresTrailingStorage()) {
+    FD->setDefaultedOrDeletedInfo(
+        FunctionDecl::DefaultedOrDeletedFunctionInfo::Create(
+            Context, /*Lookups=*/{}, /*DeletedMessage=*/nullptr,
+            CurFPFeatureOverrides().getAsOpaqueInt()));
+  }
+
   if (DefKind.isComparison()) {
     if (CheckExplicitlyDefaultedComparison(nullptr, FD, 
DefKind.asComparison()))
       FD->setInvalidDecl();
diff --git a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp 
b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
index c56203f10ac3c..d4fc17073d2e9 100644
--- a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
+++ b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
@@ -5515,11 +5515,10 @@ bool 
TemplateDeclInstantiator::SubstDefaultedFunction(FunctionDecl *New,
       Lookups.push_back(DeclAccessPair::make(D, DA.getAccess()));
     }
 
-    // It's unlikely that substitution will change any declarations. Don't
-    // store an unnecessary copy in that case.
     New->setDefaultedOrDeletedInfo(
         AnyChanged ? FunctionDecl::DefaultedOrDeletedFunctionInfo::Create(
-                         SemaRef.Context, Lookups)
+                         SemaRef.Context, Lookups, /*DeletedMessage=*/nullptr,
+                         DFI->getFPFeatures())
                    : DFI);
   }
 
diff --git a/clang/lib/Serialization/ASTReaderDecl.cpp 
b/clang/lib/Serialization/ASTReaderDecl.cpp
index a25e58e434bf2..b3ebbc752abf8 100644
--- a/clang/lib/Serialization/ASTReaderDecl.cpp
+++ b/clang/lib/Serialization/ASTReaderDecl.cpp
@@ -1094,6 +1094,8 @@ void ASTDeclReader::VisitFunctionDecl(FunctionDecl *FD) {
       StringLiteral *DeletedMessage =
           HasMessage ? cast<StringLiteral>(Record.readExpr()) : nullptr;
 
+      uint64_t FPFeatures = Record.readInt();
+
       unsigned NumLookups = Record.readInt();
       SmallVector<DeclAccessPair, 8> Lookups;
       for (unsigned I = 0; I != NumLookups; ++I) {
@@ -1104,7 +1106,7 @@ void ASTDeclReader::VisitFunctionDecl(FunctionDecl *FD) {
 
       FD->setDefaultedOrDeletedInfo(
           FunctionDecl::DefaultedOrDeletedFunctionInfo::Create(
-              Reader.getContext(), Lookups, DeletedMessage));
+              Reader.getContext(), Lookups, DeletedMessage, FPFeatures));
     }
   }
 
diff --git a/clang/lib/Serialization/ASTWriterDecl.cpp 
b/clang/lib/Serialization/ASTWriterDecl.cpp
index f271769d8edf6..52bd5bc88848f 100644
--- a/clang/lib/Serialization/ASTWriterDecl.cpp
+++ b/clang/lib/Serialization/ASTWriterDecl.cpp
@@ -898,6 +898,8 @@ void ASTDeclWriter::VisitFunctionDecl(FunctionDecl *D) {
       if (DeletedMessage)
         Record.AddStmt(DeletedMessage);
 
+      Record.push_back(FDI->getFPFeatures());
+
       Record.push_back(FDI->getUnqualifiedLookups().size());
       for (DeclAccessPair P : FDI->getUnqualifiedLookups()) {
         Record.AddDeclRef(P.getDecl());
diff --git a/clang/test/CodeGenCXX/defaulted-function-fp-features.cpp 
b/clang/test/CodeGenCXX/defaulted-function-fp-features.cpp
new file mode 100644
index 0000000000000..809c1fc864618
--- /dev/null
+++ b/clang/test/CodeGenCXX/defaulted-function-fp-features.cpp
@@ -0,0 +1,101 @@
+// RUN: %clang_cc1 -triple x86_64-unknown-linux -std=c++20 -emit-llvm -o - %s 
| FileCheck %s
+
+// CHECK-DAG: define {{.*}} @_ZeqRK6TargetS1_({{.*}}) [[NORMAL_ATTRS:#[0-9]+]]
+// CHECK-DAG: define {{.*}} @_ZeqRK12StrictTargetS1_({{.*}}) 
[[STRICT_ATTRS:#[0-9]+]]
+// CHECK-DAG: define {{.*}} @_ZN12AssignTargetaSERKS_({{.*}}) [[NORMAL_ATTRS]]
+// CHECK-DAG: define {{.*}} @_ZN18StrictAssignTargetaSERKS_({{.*}}) 
[[STRICT_ATTRS]]
+
+// Templates
+// CHECK-DAG: define {{.*}} @_ZeqRK14TemplateTargetIdES2_({{.*}}) 
[[NORMAL_ATTRS]]
+// CHECK-DAG: define {{.*}} @_ZeqRK14TemplateTargetIfES2_({{.*}}) 
[[NORMAL_ATTRS]]
+// CHECK-DAG: define {{.*}} @_ZeqRK20StrictTemplateTargetIdES2_({{.*}}) 
[[STRICT_ATTRS]]
+
+// --- NON-STRICT DECLARATIONS (at top of file, default FP is non-strict) ---
+
+struct Target {
+  double d;
+  friend bool operator==(const Target&, const Target&) = default;
+};
+
+struct Member {
+  double d;
+  Member& operator=(const Member& Other) {
+    d = Other.d + 1.0;
+    return *this;
+  }
+};
+
+struct AssignTarget {
+  Member m;
+};
+
+template <typename T>
+struct TemplateTarget {
+  T d;
+  friend bool operator==(const TemplateTarget&, const TemplateTarget&) = 
default;
+};
+
+// Trigger instantiation of TemplateTarget<double>::operator== in non-strict 
context.
+bool test_template_non_strict(TemplateTarget<double> a, TemplateTarget<double> 
b) {
+  return a == b;
+}
+
+
+// --- STRICT CONTEXT (pragmas enabled) ---
+#pragma STDC FENV_ACCESS ON
+
+// Use-sites triggering synthesis of non-strict defaulted functions in strict 
context.
+
+bool test_non_strict_cmp(Target a, Target b) {
+  return a == b;
+}
+
+void test_non_strict_assign(AssignTarget& a, AssignTarget& b) {
+  a = b;
+}
+
+// Trigger instantiation of TemplateTarget<float>::operator== in strict 
context.
+// It should still be non-strict because the template was defined in 
non-strict context.
+bool test_template_strict(TemplateTarget<float> a, TemplateTarget<float> b) {
+  return a == b;
+}
+
+// Strict declarations (must get strictfp)
+
+struct StrictTarget {
+  double d;
+  friend bool operator==(const StrictTarget&, const StrictTarget&) = default;
+};
+
+bool test_strict_cmp(StrictTarget a, StrictTarget b) {
+  return a == b;
+}
+
+struct StrictAssignTarget {
+  Member m;
+};
+
+void test_strict_assign(StrictAssignTarget& a, StrictAssignTarget& b) {
+  a = b;
+}
+
+template <typename T>
+struct StrictTemplateTarget {
+  T d;
+  friend bool operator==(const StrictTemplateTarget&, const 
StrictTemplateTarget&) = default;
+};
+
+
+// --- NON-STRICT CONTEXT AGAIN ---
+#pragma STDC FENV_ACCESS OFF
+
+// Trigger instantiation of StrictTemplateTarget<double>::operator== in 
non-strict context.
+// It should be strict because the template was defined in strict context.
+bool test_strict_template_non_strict(StrictTemplateTarget<double> a, 
StrictTemplateTarget<double> b) {
+  return a == b;
+}
+
+
+// CHECK-DAG: attributes [[STRICT_ATTRS]] = { {{.*}}strictfp{{.*}} }
+// CHECK-DAG: attributes [[NORMAL_ATTRS]] = { {{.*}} }
+// CHECK-NOT: attributes [[NORMAL_ATTRS]] = { {{.*}}strictfp

>From 08a1731ffb88ebb3f7f736fffb73fe8275b9cfaa Mon Sep 17 00:00:00 2001
From: Kyungtak Woo <[email protected]>
Date: Fri, 3 Jul 2026 17:45:21 +0000
Subject: [PATCH 2/3] fix: run clang tidy

---
 clang/include/clang/AST/Decl.h | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/clang/include/clang/AST/Decl.h b/clang/include/clang/AST/Decl.h
index 7f4ea059f699e..fb896390a8bea 100644
--- a/clang/include/clang/AST/Decl.h
+++ b/clang/include/clang/AST/Decl.h
@@ -2051,7 +2051,8 @@ class FunctionDecl : public DeclaratorDecl,
 
   /// Stashed information about a defaulted/deleted function body, including
   /// the active FP pragma overrides (FPOptionsOverride) from the declaration
-  /// site. These overrides are required to correctly synthesize the function 
body.
+  /// site. These overrides are required to correctly synthesize the function
+  /// body.
   class DefaultedOrDeletedFunctionInfo final
       : llvm::TrailingObjects<DefaultedOrDeletedFunctionInfo, DeclAccessPair,
                               StringLiteral *> {
@@ -2067,8 +2068,7 @@ class FunctionDecl : public DeclaratorDecl,
   public:
     static DefaultedOrDeletedFunctionInfo *
     Create(ASTContext &Context, ArrayRef<DeclAccessPair> Lookups,
-           StringLiteral *DeletedMessage = nullptr,
-           uint64_t FPFeatures = 0);
+           StringLiteral *DeletedMessage = nullptr, uint64_t FPFeatures = 0);
 
     uint64_t getFPFeatures() const { return FPFeatures; }
 

>From b08153fbd1c6e107c70cf14f4c1b710b1cc99651 Mon Sep 17 00:00:00 2001
From: Kyungtak Woo <[email protected]>
Date: Fri, 3 Jul 2026 21:57:56 +0000
Subject: [PATCH 3/3] feat: add default destructor

---
 clang/lib/Sema/SemaDeclCXX.cpp | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp
index 5969af4da3043..1cf7e4cfe07ac 100644
--- a/clang/lib/Sema/SemaDeclCXX.cpp
+++ b/clang/lib/Sema/SemaDeclCXX.cpp
@@ -6922,6 +6922,8 @@ struct DefaultedFunctionFPFeaturesRAII {
     S.CurFPFeatures = FPO.applyOverrides(S.LangOpts);
     S.FpPragmaStack.CurrentValue = FPO;
   }
+
+  ~DefaultedFunctionFPFeaturesRAII() = default;
 };
 } // namespace
 

_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to