[clang] [clang] Strict aliasing warning ala GCC [PR50066] (PR #74155)

via cfe-commits cfe-commits at lists.llvm.org
Sat Dec 2 08:59:48 PST 2023


llvmbot wrote:


<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-clang

@llvm/pr-subscribers-clang-codegen

Author: Nathan Sidwell (urnathan)

<details>
<summary>Changes</summary>

This implements -Wstrict-aliasing(=[123])? along the same lines as GCC. It's not 100% the same for reasons expanded on below. The default is level 3, and I have verified that bootstrapping does not trigger any warnings (just like building with GCC).

As with GCC, higher levels result in fewer warnings, reducing the number of false positives at the cost of missing (some) potential cases. Unlike GCC, this is entirely in the FE, we do not propagate any checking into the IR (so there are cases GCC will detect we do not, I have not encountered any). GCC's documentation is not very specific about which cases are detected. I examined GCC's source code to reverse engineer the algorithm[*], and as can be seen from the testcases, noted GCC's behaviour.

The strict aliasing check relies on TBAA. LLVM's representation is semantically different to GCCs for structured types. I have tried to keep with the spirit of the warning.

The warning checks reinterpret_casts that are CK_BitCast or CK_LValueBitCast. It also checks C-style casts that are equivalent (to the extent available, as a C-style bitcast could be a well-formed static cast).

level=1 looks for reinterpret casts from T * to U * (or an lvalue of type T to U &), where T and U are not TBAA compatible.

level=2 requires the src expression be an lvalue something of known(ish) static type. I.e a variable, array dereference or member access.

level=3 requires level 2 and that the resultant pointer is actually referenced (lvalue->rvalue or lvalue written). Here we can do better than GCC, which doesn't represent this in the IR -- we merely get a dereference (and reference types are modeled as pointers with auto-dereference semantics). There is one exception to this, which is by-value aggregate arguments. These end up invoking a copy constructor (passing a reference of course), but are IMHO morally an rvalue -- so should trigger.

The warning hooks into clang's code-generator's TBAA machinery. For scalar types the TBAA is straight forwards, comparing LLVM's MDNode representaion. For record & union types we check if one of the types is (recursively) the same (or TBAA compatible) as the first direct base or a field(s) of the record at offset zero (i.e. the first member of a record, or any members of a union). This covers both C++ and C-Style inheritance. Also. the member maybe alias_any, or in ubiquitous-char's alias set, which is also permissible.

The warning is similar to the alignment check that CheckCompatibleReinterpretCast does, perhaps some merging could occur if this is accepted?

[*] I implemented what turned into GCC's level=1 way back when.

WIP: adjust tests

---

Patch is 77.54 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/74155.diff


21 Files Affected:

- (modified) clang/include/clang/AST/ASTConsumer.h (+25) 
- (modified) clang/include/clang/Basic/DiagnosticGroups.td (-6) 
- (modified) clang/include/clang/Basic/DiagnosticOptions.h (+4) 
- (modified) clang/include/clang/Basic/DiagnosticSemaKinds.td (+8) 
- (modified) clang/include/clang/Driver/Options.td (+6) 
- (modified) clang/include/clang/Sema/Sema.h (+11) 
- (modified) clang/lib/CodeGen/BackendConsumer.h (+1) 
- (modified) clang/lib/CodeGen/CodeGenAction.cpp (+4) 
- (modified) clang/lib/CodeGen/CodeGenModule.h (+1) 
- (modified) clang/lib/CodeGen/CodeGenTBAA.cpp (+134) 
- (modified) clang/lib/CodeGen/CodeGenTBAA.h (+4-1) 
- (modified) clang/lib/CodeGen/ModuleBuilder.cpp (+2) 
- (modified) clang/lib/Frontend/CompilerInvocation.cpp (+3) 
- (modified) clang/lib/Frontend/FrontendAction.cpp (+8) 
- (modified) clang/lib/Sema/SemaCast.cpp (+139) 
- (modified) clang/lib/Sema/SemaExpr.cpp (+15-2) 
- (modified) clang/lib/Sema/SemaExprMember.cpp (+2) 
- (modified) clang/lib/Sema/SemaInit.cpp (+5) 
- (modified) clang/test/Misc/warning-flags-tree.c (-4) 
- (added) clang/test/Sema/strict-aliasing-warn.c (+192) 
- (added) clang/test/SemaCXX/strict-aliasing-warn.cpp (+1375) 


``````````diff
diff --git a/clang/include/clang/AST/ASTConsumer.h b/clang/include/clang/AST/ASTConsumer.h
index ebcd8059284d8..d5731ed6adf63 100644
--- a/clang/include/clang/AST/ASTConsumer.h
+++ b/clang/include/clang/AST/ASTConsumer.h
@@ -21,6 +21,7 @@ namespace clang {
   class DeclGroupRef;
   class ASTMutationListener;
   class ASTDeserializationListener; // layering violation because void* is ugly
+  class QualType;
   class SemaConsumer; // layering violation required for safe SemaConsumer
   class TagDecl;
   class VarDecl;
@@ -37,6 +38,27 @@ class ASTConsumer {
 
   friend class SemaConsumer;
 
+public:
+  /// Allow type-based aliasing information to be interrogated by the AST
+  /// producer (for diagnostics).
+  class TypeAliasing {
+  public:
+    TypeAliasing() = default;
+    virtual ~TypeAliasing(){};
+
+  public:
+    enum AliasingKind {
+      AK_Ok,            // Alias sets are compatible.
+      AK_ToIncomplete,  // Converting to an incomplete type
+      AK_KnownDisjoint, // The alias sets are known to be disjoint.
+      AK_MaybeDisjoint, // The alias sets might be disjoint.
+    };
+
+    // Return aliasing kind of reinterpreting the representation of a Src type
+    // to a Dst type.
+    virtual AliasingKind getAliasingKind(QualType &Dst, QualType &Src) = 0;
+  };
+
 public:
   ASTConsumer() = default;
 
@@ -143,6 +165,9 @@ class ASTConsumer {
   /// body may be parsed anyway if it is needed (for instance, if it contains
   /// the code completion point or is constexpr).
   virtual bool shouldSkipFunctionBody(Decl *D) { return true; }
+
+  /// Return a type aliasing object that the frontend can interrogate.
+  virtual TypeAliasing *getTypeAliasing() { return nullptr; }
 };
 
 } // end namespace clang.
diff --git a/clang/include/clang/Basic/DiagnosticGroups.td b/clang/include/clang/Basic/DiagnosticGroups.td
index ff028bbbf7426..b349228cd7113 100644
--- a/clang/include/clang/Basic/DiagnosticGroups.td
+++ b/clang/include/clang/Basic/DiagnosticGroups.td
@@ -713,12 +713,6 @@ def ReservedIdAsMacroAlias : DiagGroup<"reserved-id-macro", [ReservedIdAsMacro]>
 def RestrictExpansionMacro : DiagGroup<"restrict-expansion">;
 def FinalMacro : DiagGroup<"final-macro">;
 
-// Just silence warnings about -Wstrict-aliasing for now.
-def : DiagGroup<"strict-aliasing=0">;
-def : DiagGroup<"strict-aliasing=1">;
-def : DiagGroup<"strict-aliasing=2">;
-def : DiagGroup<"strict-aliasing">;
-
 // Just silence warnings about -Wstrict-overflow for now.
 def : DiagGroup<"strict-overflow=0">;
 def : DiagGroup<"strict-overflow=1">;
diff --git a/clang/include/clang/Basic/DiagnosticOptions.h b/clang/include/clang/Basic/DiagnosticOptions.h
index 099982c3bdd5a..1c6b9d1fb82b1 100644
--- a/clang/include/clang/Basic/DiagnosticOptions.h
+++ b/clang/include/clang/Basic/DiagnosticOptions.h
@@ -128,6 +128,10 @@ class DiagnosticOptions : public RefCountedBase<DiagnosticOptions>{
   /// whether -Wsystem-headers is enabled on a per-module basis.
   std::vector<std::string> SystemHeaderWarningsModules;
 
+  /// Level of scrutiny reinterpret_casts get for type-unsafe aliasing
+  /// checks. Requires an ASTConsumer that provides TBAA information.
+  unsigned StrictAliasing;
+
 public:
   // Define accessors/mutators for diagnostic options of enumeration type.
 #define DIAGOPT(Name, Bits, Default)
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index 6dfb2d7195203..33c0d05ef6367 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -17,6 +17,14 @@ def note_entity_declared_at : Note<"%0 declared here">;
 def note_callee_decl : Note<"%0 declared here">;
 def note_defined_here : Note<"%0 defined here">;
 
+def warn_strict_aliasing : Warning<
+  "dereferencing type-punned %select{pointer|reference}0 "
+  "%select{|to incomplete type }1%select{might break|breaks}2 strict aliasing">,
+  InGroup<DiagGroup<"strict-aliasing">>;
+def note_incompatible_aliasing : Note<
+  "%0 and %1 are not alias compatible">;
+def note_incomplete_aliasing : Note<"%0 is incomplete">;
+
 // For loop analysis
 def warn_variables_not_in_loop_body : Warning<
   "variable%select{s| %1|s %1 and %2|s %1, %2, and %3|s %1, %2, %3, and %4}0 "
diff --git a/clang/include/clang/Driver/Options.td b/clang/include/clang/Driver/Options.td
index 19d04e82aed4d..33d9cd26b2b83 100644
--- a/clang/include/clang/Driver/Options.td
+++ b/clang/include/clang/Driver/Options.td
@@ -930,6 +930,12 @@ def Wnonportable_cfstrings : Joined<["-"], "Wnonportable-cfstrings">, Group<W_Gr
 def Wp_COMMA : CommaJoined<["-"], "Wp,">,
   HelpText<"Pass the comma separated arguments in <arg> to the preprocessor">,
   MetaVarName<"<arg>">, Group<Preprocessor_Group>;
+def Wstrict_aliasing_EQ : Joined<["-"], "Wstrict-aliasing=">,
+  Group<W_value_Group>, Flags<[HelpHidden]>,
+  Visibility<[ClangOption, CC1Option]>,
+  MetaVarName<"<arg>">,
+  HelpText<"Warning level for type-based aliasing-unsafe reinterpret casts">,
+  MarshallingInfoInt<DiagnosticOpts<"StrictAliasing">, "3">;
 def Wundef_prefix_EQ : CommaJoined<["-"], "Wundef-prefix=">, Group<W_value_Group>,
   Flags<[HelpHidden]>,
   Visibility<[ClangOption, CC1Option, CLOption, DXCOption]>,
diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index 8b2ed6f7cd8cd..c00023c22c6fa 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -6650,6 +6650,17 @@ class Sema final {
   ParsedType getDestructorTypeForDecltype(const DeclSpec &DS,
                                           ParsedType ObjectType);
 
+  // Check aliasing issues bit-converting Op to DestType.
+  void CheckStrictAliasing(Expr const *Op, QualType DestType, bool IsLValue,
+                           SourceRange Range);
+
+  // Check aliasing issues in the memory reference E, which is being accessed.
+  void CheckStrictAliasingDeref(Expr const *E, bool IsLValue);
+
+  // Check aliasing issues constructing a bitcast from Op to DestType.
+  void CheckStrictAliasingCast(Expr const *Op, QualType DestType, bool IsLValue,
+                               SourceRange Range);
+
   // Checks that reinterpret casts don't have undefined behavior.
   void CheckCompatibleReinterpretCast(QualType SrcType, QualType DestType,
                                       bool IsDereference, SourceRange Range);
diff --git a/clang/lib/CodeGen/BackendConsumer.h b/clang/lib/CodeGen/BackendConsumer.h
index 72a814cd43d73..06943c17d8abd 100644
--- a/clang/lib/CodeGen/BackendConsumer.h
+++ b/clang/lib/CodeGen/BackendConsumer.h
@@ -97,6 +97,7 @@ class BackendConsumer : public ASTConsumer {
   std::unique_ptr<llvm::Module> takeModule();
 
   CodeGenerator *getCodeGenerator();
+  TypeAliasing *getTypeAliasing() override;
 
   void HandleCXXStaticMemberVarInstantiation(VarDecl *VD) override;
   void Initialize(ASTContext &Ctx) override;
diff --git a/clang/lib/CodeGen/CodeGenAction.cpp b/clang/lib/CodeGen/CodeGenAction.cpp
index bb6b1a3bc228c..6833863cef9cc 100644
--- a/clang/lib/CodeGen/CodeGenAction.cpp
+++ b/clang/lib/CodeGen/CodeGenAction.cpp
@@ -173,6 +173,10 @@ CodeGenerator* BackendConsumer::getCodeGenerator() {
   return Gen.get();
 }
 
+ASTConsumer::TypeAliasing *BackendConsumer::getTypeAliasing() {
+  return Gen->getTypeAliasing();
+}
+
 void BackendConsumer::HandleCXXStaticMemberVarInstantiation(VarDecl *VD) {
   Gen->HandleCXXStaticMemberVarInstantiation(VD);
 }
diff --git a/clang/lib/CodeGen/CodeGenModule.h b/clang/lib/CodeGen/CodeGenModule.h
index ec34680fd3f7e..7af12acea5ed6 100644
--- a/clang/lib/CodeGen/CodeGenModule.h
+++ b/clang/lib/CodeGen/CodeGenModule.h
@@ -760,6 +760,7 @@ class CodeGenModule : public CodeGenTypeCache {
   llvm::LLVMContext &getLLVMContext() { return VMContext; }
 
   bool shouldUseTBAA() const { return TBAA != nullptr; }
+  CodeGenTBAA *getTBAA() const { return TBAA.get(); }
 
   const TargetCodeGenInfo &getTargetCodeGenInfo();
 
diff --git a/clang/lib/CodeGen/CodeGenTBAA.cpp b/clang/lib/CodeGen/CodeGenTBAA.cpp
index 8705d3d65f1a5..def339cdaeede 100644
--- a/clang/lib/CodeGen/CodeGenTBAA.cpp
+++ b/clang/lib/CodeGen/CodeGenTBAA.cpp
@@ -498,3 +498,137 @@ CodeGenTBAA::mergeTBAAInfoForMemoryTransfer(TBAAAccessInfo DestInfo,
   // access type regardless of their base types.
   return TBAAAccessInfo::getMayAliasInfo();
 }
+
+// Determine the aliasing kind bit-converting from type Src to type Dst.
+CodeGenTBAA::AliasingKind CodeGenTBAA::getAliasingKind(QualType &Dst,
+                                                       QualType &Src) {
+  assert(!Src->isVoidType() && !Dst->isVoidType());
+  if (TypeHasMayAlias(Src) || TypeHasMayAlias(Dst))
+    return AK_Ok;
+
+  Src = QualType{Src->getBaseElementTypeUnsafe(), 0};
+  Dst = QualType{Dst->getBaseElementTypeUnsafe(), 0};
+
+  auto *SrcDecl = Src->getAsRecordDecl();
+  auto *DstDecl = Dst->getAsRecordDecl();
+
+  const llvm::MDNode *AnyTBAA = getChar();
+  const llvm::MDNode *SrcTBAA = nullptr;
+  const llvm::MDNode *DstTBAA = nullptr;
+
+  if (!SrcDecl) {
+    SrcTBAA = getTypeInfo(Src);
+    if (!SrcTBAA || SrcTBAA == AnyTBAA)
+      return AK_Ok;
+  }
+  if (!DstDecl) {
+    DstTBAA = getTypeInfo(Dst);
+    if (!DstTBAA || DstTBAA == AnyTBAA)
+      return AK_Ok;
+  }
+
+  auto IsAncestor = [](const llvm::MDNode *Ancestor,
+                       const llvm::MDNode *Descendant) {
+    assert(Ancestor != Descendant && "Identical TBAA");
+    while (Descendant->getNumOperands() != 1) {
+      Descendant = cast<llvm::MDNode>(Descendant->getOperand(1));
+      if (Descendant == Ancestor)
+        return true;
+    }
+    return false;
+  };
+
+  assert(SrcTBAA != AnyTBAA && DstTBAA != AnyTBAA &&
+         "AnyTBAA should already be handled");
+
+  if (!SrcDecl && !DstDecl) {
+    // Neither is a record.
+    assert(!Src->isIncompleteType() && !Dst->isIncompleteType());
+
+    if (SrcTBAA == DstTBAA || IsAncestor(SrcTBAA, DstTBAA) ||
+        IsAncestor(DstTBAA, SrcTBAA))
+      return AK_Ok;
+    return AK_KnownDisjoint;
+  }
+
+  // Is InnerTy (recursively) a field(s) or base of Outer at offset zero? Or is
+  // InnerTBAA alias compatible with such an entity?  This is C++ or C-like
+  // 'inheritance'. InnerTBAA is non-null iff InnerTy is not a record type.
+  auto Contains = [&](const RecordDecl *Outer, QualType InnerTy,
+                      const llvm::MDNode *InnerTBAA, auto &Self) {
+    assert(InnerTy->isRecordType() == !InnerTBAA &&
+           "TBAA and record typeness mismatch");
+
+    if (Outer->isLambda())
+      return false;
+
+    const ASTRecordLayout &Layout = Context.getASTRecordLayout(Outer);
+
+    if (auto *CRD = dyn_cast<CXXRecordDecl>(Outer)) {
+      // Try first direct base.
+      const auto Bases = CRD->bases();
+      if (!Bases.empty()) {
+        QualType BTy = Bases.begin()->getType();
+        const auto *BRD = BTy->getAsCXXRecordDecl();
+        if (Layout.getBaseClassOffset(BRD).isZero()) {
+          if (Context.hasSameType(BTy, InnerTy))
+            return true;
+          if (Self(BRD->getDefinition(), InnerTy, InnerTBAA, Self))
+            return true;
+        }
+      }
+    }
+
+    for (auto Field : Outer->fields()) {
+      if (Layout.getFieldOffset(Field->getFieldIndex()) != 0)
+        return false; // Not at offset zero.
+
+      if (Field->hasAttr<MayAliasAttr>())
+        return true; // Can alias anything.
+
+      QualType FTy = QualType{Field->getType()->getBaseElementTypeUnsafe(), 0};
+
+      if (auto *RD = FTy->getAsRecordDecl()) {
+        if (Context.hasSameType(FTy, InnerTy))
+          // The record-type field is the inner's type (C-like 'inheritance').
+          return true;
+        if (Self(RD->getDefinition(), InnerTy, InnerTBAA, Self))
+          return true;
+      } else if (const llvm::MDNode *FTBAA = getTypeInfo(FTy)) {
+        if (FTBAA == AnyTBAA)
+          // The scalar field can alias anything.
+          return true;
+
+        if (FTBAA == InnerTBAA || (InnerTBAA && (IsAncestor(FTBAA, InnerTBAA) ||
+                                                 IsAncestor(InnerTBAA, FTBAA))))
+          // The scalar field is alias-compatible with inner.
+          return true;
+      }
+
+      if (!Outer->isUnion())
+        // Only repeat for unions.
+        break;
+    };
+
+    return false;
+  };
+
+  if (SrcDecl) {
+    const RecordDecl *SrcDef = SrcDecl->getDefinition();
+    if (!SrcDef || Contains(SrcDef, Dst, DstTBAA, Contains))
+      // From incomplete, or container of Dst
+      return AK_Ok;
+  }
+
+  if (DstDecl) {
+    const RecordDecl *DstDef = DstDecl->getDefinition();
+    if (!DstDef)
+      return AK_ToIncomplete;
+    if (Contains(DstDef, Src, SrcTBAA, Contains))
+      // To container of Src.
+      return AK_Ok;
+  }
+
+  // Both are complete and we've not found a relationship.
+  return AK_MaybeDisjoint;
+}
diff --git a/clang/lib/CodeGen/CodeGenTBAA.h b/clang/lib/CodeGen/CodeGenTBAA.h
index a65963596fe9d..0fcad5d481050 100644
--- a/clang/lib/CodeGen/CodeGenTBAA.h
+++ b/clang/lib/CodeGen/CodeGenTBAA.h
@@ -14,6 +14,7 @@
 #ifndef LLVM_CLANG_LIB_CODEGEN_CODEGENTBAA_H
 #define LLVM_CLANG_LIB_CODEGEN_CODEGENTBAA_H
 
+#include "clang/AST/ASTConsumer.h"
 #include "clang/AST/Type.h"
 #include "clang/Basic/LLVM.h"
 #include "llvm/ADT/DenseMap.h"
@@ -113,7 +114,7 @@ struct TBAAAccessInfo {
 
 /// CodeGenTBAA - This class organizes the cross-module state that is used
 /// while lowering AST types to LLVM types.
-class CodeGenTBAA {
+class CodeGenTBAA : public ASTConsumer::TypeAliasing {
   ASTContext &Context;
   llvm::Module &Module;
   const CodeGenOptions &CodeGenOpts;
@@ -208,6 +209,8 @@ class CodeGenTBAA {
   /// purpose of memory transfer calls.
   TBAAAccessInfo mergeTBAAInfoForMemoryTransfer(TBAAAccessInfo DestInfo,
                                                 TBAAAccessInfo SrcInfo);
+
+  AliasingKind getAliasingKind(QualType &Dst, QualType &Src) override;
 };
 
 }  // end namespace CodeGen
diff --git a/clang/lib/CodeGen/ModuleBuilder.cpp b/clang/lib/CodeGen/ModuleBuilder.cpp
index 3594f4c66e677..9fb0f4480a9bf 100644
--- a/clang/lib/CodeGen/ModuleBuilder.cpp
+++ b/clang/lib/CodeGen/ModuleBuilder.cpp
@@ -171,6 +171,8 @@ namespace {
         Builder->AppendLinkerOptions(Opt);
     }
 
+    TypeAliasing *getTypeAliasing() override { return CGM().getTBAA(); }
+
     void HandleCXXStaticMemberVarInstantiation(VarDecl *VD) override {
       if (Diags.hasErrorOccurred())
         return;
diff --git a/clang/lib/Frontend/CompilerInvocation.cpp b/clang/lib/Frontend/CompilerInvocation.cpp
index a6188cb45e178..afd20e244f552 100644
--- a/clang/lib/Frontend/CompilerInvocation.cpp
+++ b/clang/lib/Frontend/CompilerInvocation.cpp
@@ -2413,6 +2413,9 @@ void CompilerInvocationBase::GenerateDiagnosticArgs(
     // This option is automatically generated from UndefPrefixes.
     if (Warning == "undef-prefix")
       continue;
+    // This option is automatically generated from StrictAliasing.
+    if (Warning == "strict-aliasing")
+      continue;
     Consumer(StringRef("-W") + Warning);
   }
 
diff --git a/clang/lib/Frontend/FrontendAction.cpp b/clang/lib/Frontend/FrontendAction.cpp
index eff785b99a09a..c5f709bae822b 100644
--- a/clang/lib/Frontend/FrontendAction.cpp
+++ b/clang/lib/Frontend/FrontendAction.cpp
@@ -31,6 +31,7 @@
 #include "clang/Parse/ParseAST.h"
 #include "clang/Sema/HLSLExternalSemaSource.h"
 #include "clang/Sema/MultiplexExternalSemaSource.h"
+#include "clang/Sema/SemaDiagnostic.h"
 #include "clang/Serialization/ASTDeserializationListener.h"
 #include "clang/Serialization/ASTReader.h"
 #include "clang/Serialization/GlobalModuleIndex.h"
@@ -1006,6 +1007,13 @@ bool FrontendAction::BeginSourceFile(CompilerInstance &CI,
       return false;
   }
 
+  // If we have no TypeAliasing, or the diagnostic is disabled, turn off the
+  // strict aliasing warning.
+  if (!CI.hasASTConsumer() || !CI.getASTConsumer().getTypeAliasing() ||
+      CI.getDiagnostics().isIgnored(diag::warn_strict_aliasing,
+                                    SourceLocation()))
+    CI.getDiagnostics().getDiagnosticOptions().StrictAliasing = 0;
+
   // Initialize built-in info as long as we aren't using an external AST
   // source.
   if (CI.getLangOpts().Modules || !CI.hasASTContext() ||
diff --git a/clang/lib/Sema/SemaCast.cpp b/clang/lib/Sema/SemaCast.cpp
index 9d85568d97b2d..f47f99319ec12 100644
--- a/clang/lib/Sema/SemaCast.cpp
+++ b/clang/lib/Sema/SemaCast.cpp
@@ -13,6 +13,7 @@
 //
 //===----------------------------------------------------------------------===//
 
+#include "clang/AST/ASTConsumer.h"
 #include "clang/AST/ASTContext.h"
 #include "clang/AST/ASTStructuralEquivalence.h"
 #include "clang/AST/CXXInheritance.h"
@@ -2026,6 +2027,137 @@ static TryCastResult TryConstCast(Sema &Self, ExprResult &SrcExpr,
   return TC_Success;
 }
 
+// We're dereferencing E, either by turning into an RValue, or by dereferencing
+// it. Check whether it's a deref of a reinterpret cast that has aliasing
+// issues.
+void Sema::CheckStrictAliasingDeref(Expr const *E, bool IsLValue) {
+  if (Diags.getDiagnosticOptions().StrictAliasing < 3)
+    return;
+
+  assert(IsLValue || E->getType()->isAnyPointerType());
+  CastExpr const *CE = nullptr;
+  for (;;) {
+    CE = dyn_cast<CastExpr>(E->IgnoreParens());
+    if (!CE)
+      return;
+
+    if (IsLValue || CE->getCastKind() != CK_ArrayToPointerDecay)
+      break;
+
+    E = CE->getSubExpr();
+    IsLValue = true;
+  }
+
+  if (CE->getCastKind() != (IsLValue ? CK_LValueBitCast : CK_BitCast))
+    return;
+
+  if (CE->getSubExpr()->getType()->isVoidPointerType())
+    return;
+
+  QualType DestTy = CE->getType();
+  if (!IsLValue)
+    DestTy = DestTy->getPointeeType();
+
+  CheckStrictAliasing(CE->getSubExpr(), DestTy, IsLValue, CE->getSourceRange());
+}
+
+/// We're building a cast from E to pointer type DestType. If ISLValueCast is
+/// true, DestType is the pointer equivalent of the reference type we're casting
+/// to.
+void Sema::CheckStrictAliasingCast(Expr const *E, QualType DestType,
+                                   bool IsLValueCast, SourceRange Range) {
+  if (Diags.getDiagnosticOptions().StrictAliasing < 1 ||
+      Diags.getDiagnosticOptions().StrictAliasing > 2)
+    return;
+
+  CheckStrictAliasing(E, DestType->getPointeeType(), IsLValueCast, Range);
+}
+
+/// Check for strict-aliasing issues in a bitcast-like conversion of E to
+/// DstTy. DstTy is the non-reference, non-pointer type of the result,
+/// IsLValueCast specifies whether it was a reference or pointer.
+void Sema::CheckStrictAliasing(Expr const *E, QualType DstTy, bool IsLValueCast,
+                               SourceRange Range) {
+  assert(Diags.getDiagnosticOptions().StrictAliasing);
+
+  if (Diags.isIgnored(diag::warn_strict_aliasing, Range.getBegin()))
+    return;
+
+  if (DstTy->isDependentType() || E->getType()->isDependentType() ||
+      E->isValueDependent())
+    return;
+
+  if (DstTy->isVoidType())
+    return; // To void, Ok.
+  if (DstTy->isFunctionType())
+    return; // To Fn type, meaningless.
+
+  Expr const *SrcExpr = E->IgnoreParenNoopCasts(Context);
+  if (Diags.getDiagnosticOptions().StrictAliasing >= 2) {
+    // Require an object of plausible static type.
+    // This (approximately) matches GCC's algorithm.
+    Expr const *IE = SrcExpr;
+
+    if (!IsLValueCast) {
+      // Require AddrOf(Entity) or ArrayDecay(Entity)
+      if (auto *UO = dyn_cast<UnaryOperator>(IE)) {
+        if (UO->getOpcode() != UO_AddrOf)
+          return;
+        IE = UO->getSubExpr();
+      } else if (auto *CE = dyn_cast<ImplicitCastExpr>(IE)) {
+        if (CE->getCastKind() != CK_ArrayToPointerDecay)
+          return;
+        IE = CE->getSubExpr();
+      } else
+        return;
+      IE = IE->IgnoreParens();
+    }
+
+    ValueDecl const *Dcl = nullptr;
+    if (auto const *DRE = dyn_cast<DeclRefExpr>(IE)) {
+      Dcl = DRE->getDecl();
+    } else if (auto const *ME = dyn_cast<MemberExpr>(IE)) {
+      Dcl = ME->getMemberDecl();
+    } else if (auto const *UE = dyn_cast<UnaryOperator>(IE)) {
+      if (UE->getOpcode() != UO_Imag && UE->getOpcode() != UO_Real)
+        return;
+    } else if (auto const *CE = dyn_cast<CastExpr>(IE)) {
+      if (CE->getCastKind() != CK_LValueBitCast)
+        return;
+    } else if (isa<ArraySubscriptExpr>(IE))
+      ; // OK
+    else
+      return;
+
+    if (Dcl && Dcl->getType()->isReferenceType())
+      return; // This is not the...
[truncated]

``````````

</details>


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


More information about the cfe-commits mailing list