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

Nathan Sidwell via cfe-commits cfe-commits at lists.llvm.org
Fri Dec 1 15:13:50 PST 2023


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

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

>From 89c05618bb75fd073343046f3b54bde5f2254719 Mon Sep 17 00:00:00 2001
From: Nathan Sidwell <nathan at acm.org>
Date: Wed, 15 Nov 2023 15:27:16 -0500
Subject: [PATCH] [clang] Strict aliasing warning ala GCC [PR50066]

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
---
 clang/include/clang/AST/ASTConsumer.h         |   25 +
 clang/include/clang/Basic/DiagnosticGroups.td |    6 -
 clang/include/clang/Basic/DiagnosticOptions.h |    4 +
 .../clang/Basic/DiagnosticSemaKinds.td        |    8 +
 clang/include/clang/Driver/Options.td         |    6 +
 clang/include/clang/Sema/Sema.h               |   11 +
 clang/lib/CodeGen/BackendConsumer.h           |    1 +
 clang/lib/CodeGen/CodeGenAction.cpp           |    4 +
 clang/lib/CodeGen/CodeGenModule.h             |    1 +
 clang/lib/CodeGen/CodeGenTBAA.cpp             |  134 ++
 clang/lib/CodeGen/CodeGenTBAA.h               |    5 +-
 clang/lib/CodeGen/ModuleBuilder.cpp           |    2 +
 clang/lib/Frontend/CompilerInvocation.cpp     |    3 +
 clang/lib/Frontend/FrontendAction.cpp         |    8 +
 clang/lib/Sema/SemaCast.cpp                   |  139 ++
 clang/lib/Sema/SemaExpr.cpp                   |   17 +-
 clang/lib/Sema/SemaExprMember.cpp             |    2 +
 clang/lib/Sema/SemaInit.cpp                   |    5 +
 clang/test/Misc/warning-flags-tree.c          |    4 -
 clang/test/Sema/strict-aliasing-warn.c        |  192 +++
 clang/test/SemaCXX/strict-aliasing-warn.cpp   | 1375 +++++++++++++++++
 21 files changed, 1939 insertions(+), 13 deletions(-)
 create mode 100644 clang/test/Sema/strict-aliasing-warn.c
 create mode 100644 clang/test/SemaCXX/strict-aliasing-warn.cpp

diff --git a/clang/include/clang/AST/ASTConsumer.h b/clang/include/clang/AST/ASTConsumer.h
index ebcd8059284d8d4..d5731ed6adf631a 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 ff028bbbf74261e..b349228cd7113c5 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 099982c3bdd5a00..1c6b9d1fb82b195 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 6dfb2d7195203a3..33c0d05ef63676f 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 19d04e82aed4d68..33d9cd26b2b83b0 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 8b2ed6f7cd8cd8e..c00023c22c6fa0f 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 72a814cd43d738d..06943c17d8abd27 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 bb6b1a3bc228cf9..6833863cef9cc0c 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 ec34680fd3f7e6e..7af12acea5ed6be 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 8705d3d65f1a573..def339cdaeedef8 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 a65963596fe9def..0fcad5d481050d9 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 3594f4c66e67745..9fb0f4480a9bf40 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 a6188cb45e1787f..afd20e244f552a2 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 eff785b99a09a4f..c5f709bae822b3d 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 9d85568d97b2d27..f47f99319ec12ca 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 object we were looking for.
+  }
+
+  QualType SrcTy = SrcExpr->getType();
+  if (!IsLValueCast)
+    SrcTy = SrcTy->getPointeeType();
+  if (SrcTy->isVoidType())
+    return; // From void type.
+  if (SrcTy->isFunctionType())
+    return; // From Fn type, meaningless.
+
+  auto Aliasing =
+      getASTConsumer().getTypeAliasing()->getAliasingKind(DstTy, SrcTy);
+
+  if (Aliasing != ASTConsumer::TypeAliasing::AK_Ok &&
+      (Diag(Range.getBegin(), diag::warn_strict_aliasing)
+       << IsLValueCast
+       << (Aliasing == ASTConsumer::TypeAliasing::AK_ToIncomplete)
+       << (Aliasing == ASTConsumer::TypeAliasing::AK_KnownDisjoint &&
+           Diags.getDiagnosticOptions().StrictAliasing >= 2)
+       << Range))
+    Diag(Range.getBegin(),
+         Aliasing == ASTConsumer::TypeAliasing::AK_ToIncomplete
+             ? diag::note_incomplete_aliasing
+             : diag::note_incompatible_aliasing)
+        << DstTy << SrcTy;
+}
+
 // Checks for undefined behavior in reinterpret_cast.
 // The cases that is checked for is:
 // *reinterpret_cast<T*>(&a)
@@ -2587,6 +2719,10 @@ static TryCastResult TryReinterpretCast(Sema &Self, ExprResult &SrcExpr,
     SrcPtee = SrcPtee->getPointeeType();
   }
 
+  if (Kind == (IsLValueCast ? CK_LValueBitCast : CK_BitCast))
+    Self.CheckStrictAliasingCast(SrcExpr.get(), DestType, IsLValueCast,
+                                 OpRange);
+
   // C++ 5.2.10p7: A pointer to an object can be explicitly converted to
   //   a pointer to an object of different type.
   // Void pointers are not specified, but supported by every compiler out there.
@@ -3236,6 +3372,9 @@ void CastOperation::CheckCStyleCast() {
       SrcExpr = ExprError();
       return;
     }
+
+    if (!SrcTy->isVoidType())
+      Self.CheckStrictAliasingCast(SrcExpr.get(), DestType, false, OpRange);
   }
 
   DiagnoseCastOfObjCSEL(Self, SrcExpr, DestType);
diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp
index b204cb01a0deffd..3d4d9048f8104d5 100644
--- a/clang/lib/Sema/SemaExpr.cpp
+++ b/clang/lib/Sema/SemaExpr.cpp
@@ -736,7 +736,12 @@ ExprResult Sema::DefaultLvalueConversion(Expr *E) {
 
   // C++ [conv.lval]p3:
   //   If T is cv std::nullptr_t, the result is a null pointer constant.
-  CastKind CK = T->isNullPtrType() ? CK_NullToPointer : CK_LValueToRValue;
+  CastKind CK = CK_NullToPointer;
+  if (!T->isNullPtrType()) {
+    // We're going to deref, check aliasing.
+    CheckStrictAliasingDeref(E, true);
+    CK = CK_LValueToRValue;
+  }
   Res = ImplicitCastExpr::Create(Context, T, CK, E, nullptr, VK_PRValue,
                                  CurFPFeatureOverrides());
 
@@ -5213,8 +5218,10 @@ ExprResult Sema::ActOnArraySubscriptExpr(Scope *S, Expr *base,
   ExprResult Res =
       CreateBuiltinArraySubscriptExpr(base, lbLoc, ArgExprs.front(), rbLoc);
 
-  if (!Res.isInvalid() && isa<ArraySubscriptExpr>(Res.get()))
+  if (!Res.isInvalid() && isa<ArraySubscriptExpr>(Res.get())) {
+    CheckStrictAliasingDeref(base, !base->getType()->isAnyPointerType());
     CheckSubscriptAccessOfNoDeref(cast<ArraySubscriptExpr>(Res.get()));
+  }
 
   return Res;
 }
@@ -14696,6 +14703,7 @@ QualType Sema::CheckAssignmentOperands(Expr *LHSExpr, ExprResult &RHS,
                                RHS.get(), AA_Assigning))
     return QualType();
 
+  CheckStrictAliasingDeref(LHSExpr, true);
   CheckForNullPointerDereference(*this, LHSExpr);
 
   if (getLangOpts().CPlusPlus20 && LHSType.isVolatileQualified()) {
@@ -14906,6 +14914,9 @@ static QualType CheckIncrementDecrementOperand(Sema &S, Expr *Op,
     S.Diag(OpLoc, diag::warn_deprecated_increment_decrement_volatile)
         << IsInc << ResType;
   }
+
+  S.CheckStrictAliasingDeref(Op, true);
+
   // In C++, a prefix increment is the same type as the operand. Otherwise
   // (in C or with postfix), the increment is the unqualified type of the
   // operand.
@@ -15322,6 +15333,8 @@ static QualType CheckIndirectionOperand(Sema &S, Expr *Op, ExprValueKind &VK,
           << OpTy << Op->getSourceRange();
   }
 
+  S.CheckStrictAliasingDeref(Op, false);
+
   // Dereferences are usually l-values...
   VK = VK_LValue;
 
diff --git a/clang/lib/Sema/SemaExprMember.cpp b/clang/lib/Sema/SemaExprMember.cpp
index 473eea55bb6b19e..8ab45110d4a93e8 100644
--- a/clang/lib/Sema/SemaExprMember.cpp
+++ b/clang/lib/Sema/SemaExprMember.cpp
@@ -1903,6 +1903,8 @@ Sema::BuildFieldReferenceExpr(Expr *BaseExpr, bool IsArrow,
     }
   }
 
+  CheckStrictAliasingDeref(Base.get(), !IsArrow);
+
   return BuildMemberExpr(Base.get(), IsArrow, OpLoc, &SS,
                          /*TemplateKWLoc=*/SourceLocation(), Field, FoundDecl,
                          /*HadMultipleCandidates=*/false, MemberNameInfo,
diff --git a/clang/lib/Sema/SemaInit.cpp b/clang/lib/Sema/SemaInit.cpp
index 50ee0a5acb5586a..b9fe4e96381b230 100644
--- a/clang/lib/Sema/SemaInit.cpp
+++ b/clang/lib/Sema/SemaInit.cpp
@@ -10536,6 +10536,11 @@ Sema::PerformCopyInitialization(const InitializedEntity &Entity,
     CurrentParameterCopyTypes.push_back(Entity.getType());
   }
 
+  if (Seq.isConstructorInitialization())
+    // We'll be passing by reference to the constructor, but check as-if passing
+    // by value, as that's what's morally happening.
+    CheckStrictAliasingDeref(InitE, true);
+
   ExprResult Result = Seq.Perform(*this, Entity, Kind, InitE);
 
   if (ShouldTrackCopy)
diff --git a/clang/test/Misc/warning-flags-tree.c b/clang/test/Misc/warning-flags-tree.c
index 01ba4971ee06563..ff6b3e7686ff54d 100644
--- a/clang/test/Misc/warning-flags-tree.c
+++ b/clang/test/Misc/warning-flags-tree.c
@@ -14,10 +14,6 @@
 // CHECK:   -Wmost
 
 // These flags are currently unimplemented; test that we output them anyway.
-// CHECK: -Wstrict-aliasing
-// CHECK-NEXT: -Wstrict-aliasing=0
-// CHECK-NEXT: -Wstrict-aliasing=1
-// CHECK-NEXT: -Wstrict-aliasing=2
 // CHECK: -Wstrict-overflow
 // CHECK-NEXT: -Wstrict-overflow=0
 // CHECK-NEXT: -Wstrict-overflow=1
diff --git a/clang/test/Sema/strict-aliasing-warn.c b/clang/test/Sema/strict-aliasing-warn.c
new file mode 100644
index 000000000000000..ad561939eeda18d
--- /dev/null
+++ b/clang/test/Sema/strict-aliasing-warn.c
@@ -0,0 +1,192 @@
+// RUN: %clang_cc1 %s -O0 -Wstrict-aliasing -S -o %t -verify=quiet
+// RUN: %clang_cc1 %s -O2 -Wstrict-aliasing=0 -S -o %t -verify=quiet
+// RUN: %clang_cc1 %s -O2 -Wno-strict-aliasing -S -o %t -verify=quiet
+// RUN: %clang_cc1 %s -O2 -Wstrict-aliasing=1 -S -o %t -verify=level1,level12,level123
+// RUN: %clang_cc1 %s -O2 -Wstrict-aliasing=2 -S -o %t -verify=level2,level23,level12,level123
+// RUN: %clang_cc1 %s -O2 -Wstrict-aliasing=3 -S -o %t -verify=level23,level123
+// RUN: %clang_cc1 %s -O2 -Wstrict-aliasing -S -o %t -verify=level23,level123
+// RUN: %clang_cc1 %s -O2 -S -o %t -verify=level23,level123
+
+// quiet-no-diagnostics
+
+#if _LP64
+// These names make more sense on an ilp32 machine
+typedef long INT;
+typedef long long LONG;
+typedef unsigned long UINT;
+#else
+typedef int INT;
+typedef long LONG;
+typedef unsigned int UINT;
+#endif
+typedef short SHORT;
+
+INT ScalarINT;
+INT Ary[2];
+struct {int m;} Struct;
+
+_Complex int CPLx;
+
+void ByVal(long long);
+void ByPtr(void *);
+
+void VarPtr(INT *Ptr) {
+  // GCC: 1
+  // level1-warning at +1{{type-punned pointer might break}}
+  ByPtr((LONG *)(Ptr));
+  // level1-note at -1{{not alias compatible}}
+
+  // GCC:
+  ByPtr((LONG *)((void *)(Ptr)));
+
+  // GCC: 1
+  // level1-warning at +1{{type-punned pointer might break}}
+  ByVal(*(LONG *)(Ptr));
+  // level1-note at -1{{not alias compatible}}
+}
+
+void Object() {
+  // GCC: 1, 2
+  // level2-warning at +2{{type-punned pointer breaks}}
+  // level1-warning at +1{{type-punned pointer might break}}
+  ByPtr((LONG *)(&ScalarINT));
+  // level12-note at -1{{not alias compatible}}
+
+  // GCC:
+  ByPtr((LONG *)((void *)(&ScalarINT)));
+
+  // GCC: 1, 2, 3
+  // level23-warning at +2{{type-punned pointer breaks}}
+  // level1-warning at +1{{type-punned pointer might break}}
+  ByVal(*(LONG *)(&ScalarINT));
+  // level123-note at -1{{not alias compatible}}
+}
+
+// Level 1, 2, 3 - 1, 2, 3
+void DetectedVariants() {
+  // GCC: 1, 2, 3
+  // level23-warning at +2{{type-punned pointer breaks}}
+  // level1-warning at +1{{type-punned pointer might break}}
+  ByVal(*(LONG *)(&Ary[1]));
+  // level123-note at -1{{not alias compatible}}
+
+  // GCC: 1, 2, 3
+  // level23-warning at +2{{type-punned pointer breaks}}
+  // level1-warning at +1{{type-punned pointer might break}}
+  ByVal(*(LONG *)(&Struct.m));
+  // level123-note at -1{{not alias compatible}}
+
+  // GCC: 1, 2, 3
+  // level23-warning at +2{{type-punned pointer breaks}}
+  // level1-warning at +1{{type-punned pointer might break}}
+  ByVal(*(LONG *)(&(&Struct)->m));
+  // level123-note at -1{{not alias compatible}}
+
+  // GCC: 1, 2, 3
+  // level23-warning at +2{{type-punned pointer breaks}}
+  // level1-warning at +1{{type-punned pointer might break}}
+  ByVal(*(LONG *)(&__real__(CPLx)));
+  // level123-note at -1{{not alias compatible}}
+
+  // GCC: 1, 2, 3
+  // level23-warning at +2{{type-punned pointer breaks}}
+  // level1-warning at +1{{type-punned pointer might break}}
+  ByVal(*(LONG *)(&__imag__(CPLx)));
+  // level123-note at -1{{not alias compatible}}
+}
+
+void Ok() {
+  // GCC:
+  ByPtr((UINT *)(&ScalarINT));
+  // GCC:
+  ByPtr((UINT *)((void *)(&ScalarINT)));
+  // GCC:
+  ByVal(*(UINT *)(&ScalarINT));
+}
+
+// Level 1, 2, 3 - 1, 2, 3
+void Parens() {
+  // GCC: 1, 2, 3
+  // level23-warning at +2{{type-punned pointer breaks}}
+  // level1-warning at +1{{type-punned pointer might break}}
+  ByVal(*((LONG *)((&(ScalarINT)))));
+  // level123-note at -1{{not alias compatible}}
+
+  // GCC: 1, 2, 3
+  // level23-warning at +2{{type-punned pointer breaks}}
+  // level1-warning at +1{{type-punned pointer might break}}
+  ByVal(*((LONG *)((&(Ary[1])))));
+  // level123-note at -1{{not alias compatible}}
+}
+
+// Clang models may_alias as a decl attribute, not a type attribute.
+
+typedef int MA __attribute__((may_alias));
+
+void Frob(MA *a) {
+  ByPtr((short *)(a));
+  ByVal(*(short *)(a));
+}
+
+struct Inner { int m; };
+struct Outer1 { struct Inner i; };
+struct Outer2 { struct Outer1 o; };
+struct Inner i;
+struct Outer2 o;
+
+void ByValInner (struct Inner);
+void ByValOuter2 (struct Outer2);
+
+void Inherit() {
+  // Check we see through multiple levels
+  int in;
+
+  ByValOuter2(*(struct Outer2 *)&in);
+  ByValOuter2(*(struct Outer2 *)&i);
+  ByValInner(*(struct Inner *)&o.o);
+  ByValInner(*(struct Inner *)&o);
+  ByVal(*(int *)&o);
+}
+
+// PR 50066
+typedef unsigned char uchar;
+
+void Double(double);
+
+int main() {
+  double d = 2.34;
+  int i[2];
+  Double(d);
+
+  // GCC: 1, 2, 3
+  // level23-warning at +2{{type-punned pointer breaks}}
+  // level1-warning at +1{{type-punned pointer might break}}
+  *(long long *)i =
+  // level123-note at -1{{not alias compatible}}
+
+      // GCC: 1, 2, 3
+      // level23-warning at +2{{type-punned pointer breaks}}
+      // level1-warning at +1{{type-punned pointer might break}}
+      *(long long *)&d;
+      // level123-note at -1{{not alias compatible}}
+
+  // GCC: 1, 2, 3
+  // level23-warning at +2{{type-punned pointer breaks}}
+  // level1-warning at +1{{type-punned pointer might break}}
+  ((int *)&d)[0] = i[0];
+  // level123-note at -1{{not alias compatible}}
+
+  // GCC: 1, 2
+  // level23-warning at +2{{type-punned pointer breaks}}
+  // level1-warning at +1{{type-punned pointer might break}}
+  ((int *)&d)[1] = i[1];
+  // level123-note at -1{{not alias compatible}}
+
+  Double(d);
+  ((uchar *)&d)[2] += 123;
+  Double(d);
+  return 0;
+}
+
+// GCC gets (cast)[0], but not (cast)[1] because it represents the first as
+// *(cast), and so it falls into the indirect operator path.
diff --git a/clang/test/SemaCXX/strict-aliasing-warn.cpp b/clang/test/SemaCXX/strict-aliasing-warn.cpp
new file mode 100644
index 000000000000000..aedeef847378fee
--- /dev/null
+++ b/clang/test/SemaCXX/strict-aliasing-warn.cpp
@@ -0,0 +1,1375 @@
+// RUN: %clang_cc1 -triple x86_64-unknown-unknown %s -O0 -Wstrict-aliasing -S -o %t -verify=quiet
+// RUN: %clang_cc1 -triple x86_64-unknown-unknown %s -O2 -Wstrict-aliasing=0 -S -o %t -verify=quiet
+// RUN: %clang_cc1 -triple x86_64-unknown-unknown %s -O2 -Wno-strict-aliasing -S -o %t -verify=quiet
+// RUN: %clang_cc1 -triple x86_64-unknown-unknown %s -O2 -Wstrict-aliasing=1 -S -o %t -verify=level1,level12,level123
+// RUN: %clang_cc1 -triple x86_64-unknown-unknown %s -O2 -Wstrict-aliasing=2 -S -o %t -verify=level2,level23,level12,level123
+// RUN: %clang_cc1 -triple x86_64-unknown-unknown %s -O2 -Wstrict-aliasing=3 -S -o %t -verify=level23,level123
+// RUN: %clang_cc1 -triple x86_64-unknown-unknown %s -O2 -Wstrict-aliasing -S -o %t -verify=level23,level123
+// RUN: %clang_cc1 -triple x86_64-unknown-unknown %s -O2 -S -o %t -verify=level23,level123
+
+// quiet-no-diagnostics
+
+// There seems to be a diagnostic bug in that elaborated type names are
+// sometimes printed with a nested-name and sometime not.
+
+long Scalar;
+long Ary[2];
+struct Struct_t {int m;} Struct;
+struct Struct2_t {float f; int m;};
+
+_Complex int CPLx;
+
+template<typename T> void LValue(T &);
+template<typename T> void RValue(T);
+
+namespace scalar {
+void VarPtr(long *Ptr) {
+  // GCC: 1
+  // level1-warning at +1{{type-punned pointer might break}}
+  RValue(reinterpret_cast<long long *>(Ptr));
+  // level1-note at -1{{'long long' and 'long' are not alias compatible}}
+
+  // GCC: 1
+  // level1-warning at +1{{type-punned reference might break}}
+  RValue(reinterpret_cast<long long &>(*Ptr));
+  // level1-note at -1{{'long long' and 'long' are not alias compatible}}
+
+  // GCC:
+  RValue(static_cast<long long *>(static_cast<void *>(Ptr)));
+
+  // GCC: 1
+  // level1-warning at +1{{type-punned pointer might break}}
+  RValue(reinterpret_cast<long long *>(static_cast<void *>(Ptr)));
+  // level1-note at -1{{'long long' and 'long' are not alias compatible}}
+
+  // GCC:
+  RValue(static_cast<long long *>(reinterpret_cast<void *>(Ptr)));
+
+  // GCC: 1
+  // level1-warning at +1{{type-punned reference might break}}
+  RValue(reinterpret_cast<long long &>(*Ptr));
+  // level1-note at -1{{'long long' and 'long' are not alias compatible}}
+
+  // GCC: 1
+  // level1-warning at +1{{type-punned pointer might break}}
+  RValue(*reinterpret_cast<long long *>(Ptr));
+  // level1-note at -1{{'long long' and 'long' are not alias compatible}}
+
+  // GCC: 1
+  // level1-warning at +1{{type-punned reference might break}}
+  LValue(reinterpret_cast<long long &>(*Ptr));
+  // level1-note at -1{{'long long' and 'long' are not alias compatible}}
+
+  // GCC: 1
+  // level1-warning at +1{{type-punned pointer might break}}
+  LValue(*reinterpret_cast<long long *>(Ptr));
+  // level1-note at -1{{'long long' and 'long' are not alias compatible}}
+}
+
+void VarRef(long &Ref) {
+  // GCC: 1
+  // level1-warning at +1{{type-punned pointer might break}}
+  RValue(reinterpret_cast<long long *>(&Ref));
+  // level1-note at -1{{'long long' and 'long' are not alias compatible}}
+
+  // GCC: 1
+  // level1-warning at +1{{type-punned reference might break}}
+  RValue(reinterpret_cast<long long &>(Ref));
+  // level1-note at -1{{'long long' and 'long' are not alias compatible}}
+
+  // GCC:
+  RValue(static_cast<long long *>(static_cast<void *>(&Ref)));
+
+  // GCC: 1
+  // level1-warning at +1{{type-punned pointer might break}}
+  RValue(reinterpret_cast<long long *>(static_cast<void *>(&Ref)));
+  // level1-note at -1{{'long long' and 'long' are not alias compatible}}
+
+  // GCC:
+  RValue(static_cast<long long *>(reinterpret_cast<void *>(&Ref)));
+
+  // GCC: 1
+  // level1-warning at +1{{type-punned reference might break}}
+  RValue(reinterpret_cast<long long &>(Ref));
+  // level1-note at -1{{'long long' and 'long' are not alias compatible}}
+
+  // GCC: 1
+  // level1-warning at +1{{type-punned pointer might break}}
+  RValue(*reinterpret_cast<long long *>(&Ref));
+  // level1-note at -1{{'long long' and 'long' are not alias compatible}}
+
+  // GCC: 1
+  // level1-warning at +1{{type-punned reference might break}}
+  LValue(reinterpret_cast<long long &>(Ref));
+  // level1-note at -1{{'long long' and 'long' are not alias compatible}}
+
+  // GCC: 1
+  // level1-warning at +1{{type-punned reference might break}}
+  LValue(reinterpret_cast<long long &>(Ref));
+  // level1-note at -1{{'long long' and 'long' are not alias compatible}}
+
+  // GCC: 1
+  // level1-warning at +1{{type-punned pointer might break}}
+  LValue(*reinterpret_cast<long long *>(&Ref));
+  // level1-note at -1{{'long long' and 'long' are not alias compatible}}
+}
+
+void Object() {
+  // GCC: 1, 2
+  // level2-warning at +2{{type-punned pointer breaks}}
+  // level1-warning at +1{{type-punned pointer might break}}
+  RValue(reinterpret_cast<long long *>(&Scalar));
+  // level12-note at -1{{'long long' and 'long' are not alias compatible}}
+
+  // GCC: 1, 2, 3
+  // level23-warning at +2{{type-punned reference breaks}}
+  // level1-warning at +1{{type-punned reference might break}}
+  RValue(reinterpret_cast<long long &>(Scalar));
+  // level123-note at -1{{'long long' and 'long' are not alias compatible}}
+
+  // GCC:
+  RValue(static_cast<long long *>(static_cast<void *>(&Scalar)));
+
+  // GCC: 1, 2
+  // level2-warning at +2{{type-punned pointer breaks}}
+  // level1-warning at +1{{type-punned pointer might break}}
+  RValue(reinterpret_cast<long long *>(static_cast<void *>(&Scalar)));
+  // level12-note at -1{{'long long' and 'long' are not alias compatible}}
+
+  // GCC:
+  RValue(static_cast<long long *>(reinterpret_cast<void *>(&Scalar)));
+
+  // GCC: 1, 2, 3
+  // level23-warning at +2{{type-punned reference breaks}}
+  // level1-warning at +1{{type-punned reference might break}}
+  RValue(reinterpret_cast<long long &>(Scalar));
+  // level123-note at -1{{'long long' and 'long' are not alias compatible}}
+
+  // GCC: 1, 2, 3
+  // level23-warning at +2{{type-punned pointer breaks}}
+  // level1-warning at +1{{type-punned pointer might break}}
+  RValue(*reinterpret_cast<long long *>(&Scalar));
+  // level123-note at -1{{'long long' and 'long' are not alias compatible}}
+
+  // FIXME: no level 3, No actual deref
+  // GCC: 1, 2, 3
+  // level2-warning at +2{{type-punned reference breaks}}
+  // level1-warning at +1{{type-punned reference might break}}
+  LValue(reinterpret_cast<long long &>(Scalar));
+  // level12-note at -1{{'long long' and 'long' are not alias compatible}}
+  
+  // GCC: 1, 2, 3
+  // level23-warning at +2{{type-punned pointer breaks}}
+  // level1-warning at +1{{type-punned pointer might break}}
+  LValue(*reinterpret_cast<long long *>(&Scalar));
+  // level123-note at -1{{'long long' and 'long' are not alias compatible}}
+}
+
+// Level 1, 2, 3 - 1, 2, 3
+void DetectedVariants() {
+  // GCC: 1, 2, 3
+  // level23-warning at +2{{type-punned reference breaks}}
+  // level1-warning at +1{{type-punned reference might break}}
+  RValue(reinterpret_cast<long long &>(Ary[1]));
+  // level123-note at -1{{'long long' and 'long' are not alias compatible}}
+
+  // GCC: 1, 2, 3
+  // level23-warning at +2{{type-punned pointer breaks}}
+  // level1-warning at +1{{type-punned pointer might break}}
+  RValue(*reinterpret_cast<long long *>(&Ary[1]));
+  // level123-note at -1{{'long long' and 'long' are not alias compatible}}
+
+  // GCC: 1, 2, 3
+  // level23-warning at +2{{type-punned reference breaks}}
+  // level1-warning at +1{{type-punned reference might break}}
+  RValue(reinterpret_cast<long long &>(Struct.m));
+  // level123-note at -1{{'long long' and 'int' are not alias compatible}}
+
+  // GCC: 1, 2, 3
+  // level23-warning at +2{{type-punned pointer breaks}}
+  // level1-warning at +1{{type-punned pointer might break}}
+  RValue(*reinterpret_cast<long long *>(&Struct.m));
+  // level123-note at -1{{'long long' and 'int' are not alias compatible}}
+
+  // GCC: 1, 2, 3
+  // level23-warning at +2{{type-punned reference breaks}}
+  // level1-warning at +1{{type-punned reference might break}}
+  RValue(reinterpret_cast<long long &>((&Struct)->m));
+  // level123-note at -1{{'long long' and 'int' are not alias compatible}}
+
+  // GCC: 1, 2, 3
+  // level23-warning at +2{{type-punned pointer breaks}}
+  // level1-warning at +1{{type-punned pointer might break}}
+  RValue(*reinterpret_cast<long long *>(&(&Struct)->m));
+  // level123-note at -1{{'long long' and 'int' are not alias compatible}}
+
+  // GCC: 1, 2, 3
+  // level23-warning at +2{{type-punned reference breaks}}
+  // level1-warning at +1{{type-punned reference might break}}
+  RValue(reinterpret_cast<long long &>(__real__(CPLx)));
+  // level123-note at -1{{'long long' and 'int' are not alias compatible}}
+
+  // GCC: 1, 2, 3
+  // level23-warning at +2{{type-punned pointer breaks}}
+  // level1-warning at +1{{type-punned pointer might break}}
+  RValue(*reinterpret_cast<long long *>(&__real__(CPLx)));
+  // level123-note at -1{{'long long' and 'int' are not alias compatible}}
+
+  // GCC: 1, 2, 3
+  // level23-warning at +2{{type-punned reference breaks}}
+  // level1-warning at +1{{type-punned reference might break}}
+  RValue(reinterpret_cast<long long &>(__imag__(CPLx)));
+  // level123-note at -1{{'long long' and 'int' are not alias compatible}}
+
+  // GCC: 1, 2, 3
+  // level23-warning at +2{{type-punned pointer breaks}}
+  // level1-warning at +1{{type-punned pointer might break}}
+  RValue(*reinterpret_cast<long long *>(&__imag__(CPLx)));
+  // level123-note at -1{{'long long' and 'int' are not alias compatible}}
+}
+
+void Ok() {
+  // GCC:
+  RValue(reinterpret_cast<unsigned long *>(&Scalar));
+  // GCC:
+  RValue(reinterpret_cast<unsigned long &>(Scalar));
+  // GCC:
+  RValue(static_cast<unsigned long *>(static_cast<void *>(&Scalar)));
+  // GCC:
+  RValue(reinterpret_cast<unsigned long *>(static_cast<void *>(&Scalar)));
+  // GCC:
+  RValue(static_cast<unsigned long *>(reinterpret_cast<void *>(&Scalar)));
+  // GCC:
+  RValue(reinterpret_cast<unsigned long &>(Scalar));
+  // GCC:
+  RValue(*reinterpret_cast<unsigned long *>(&Scalar));
+
+  // GCC:
+  LValue(reinterpret_cast<unsigned long &>(Scalar));
+  // GCC:
+  LValue(reinterpret_cast<unsigned long &>(Scalar));
+  // GCC:
+  LValue(*reinterpret_cast<unsigned long *>(&Scalar));
+}
+
+// Level 1, 2, 3 - 1, 2, 3
+void Parens() {
+  // GCC: 1, 2, 3
+  // level23-warning at +2{{type-punned reference breaks}}
+  // level1-warning at +1{{type-punned reference might break}}
+  RValue(reinterpret_cast<long long &>((Scalar)));
+  // level123-note at -1{{'long long' and 'long' are not alias compatible}}
+
+  // GCC: 1, 2, 3
+  // level23-warning at +2{{type-punned pointer breaks}}
+  // level1-warning at +1{{type-punned pointer might break}}
+  RValue(*(reinterpret_cast<long long *>((&(Scalar)))));
+  // level123-note at -1{{'long long' and 'long' are not alias compatible}}
+
+  // GCC: 1, 2, 3
+  // level23-warning at +2{{type-punned pointer breaks}}
+  // level1-warning at +1{{type-punned pointer might break}}
+  RValue(*(reinterpret_cast<long long *>((&(Ary[1])))));
+  // level123-note at -1{{'long long' and 'long' are not alias compatible}}
+}
+
+// Clang models may_alias as a decl attribute, not a type attribute.
+
+using MA __attribute__((may_alias)) = int;
+
+void Frob(MA *a) {
+  RValue(reinterpret_cast<short *>(a));
+  RValue(*reinterpret_cast<short *>(a));
+  LValue(reinterpret_cast<short &>(*a));
+  RValue(reinterpret_cast<short &>(*a));
+}
+
+}
+
+namespace record {
+
+struct A {
+  char a[4];
+};
+
+struct NotA {
+  float a;
+};
+union U {
+  char a[2];
+  int b;
+};
+union NotU {
+  int b;
+  char a[2];
+};
+
+struct B {
+  int a;
+};
+
+struct C {
+  float a;
+};
+
+struct D : B {};
+
+struct E;
+
+template<typename T>
+struct Wrapper {
+  T t;
+};
+
+int ScalarINT;
+A a;
+B b;
+C c;
+D d;
+
+void Frob(A *aptr, B *bptr, C *cptr) {
+  // GCC: 
+  RValue(reinterpret_cast<B *>(aptr));
+
+  // GCC: 
+  LValue(reinterpret_cast<B &>(*aptr));
+
+  // GCC: 
+  RValue(reinterpret_cast<A *>(bptr));
+
+  // GCC: 
+  LValue(reinterpret_cast<A &>(*bptr));
+
+  // GCC: 1
+  // level1-warning at +1{{type-punned pointer might break}}
+  RValue(reinterpret_cast<B *>(cptr));
+  // level1-note at -1{{'record::B' and 'C' are not alias compatible}}
+
+  // GCC: 1
+  // level1-warning at +1{{type-punned pointer might break}}
+  RValue(*reinterpret_cast<B *>(cptr));
+  // level1-note at -1{{'record::B' and 'C' are not alias compatible}}
+
+  // GCC: 
+  RValue(*reinterpret_cast<B *>(aptr));
+
+  // GCC: 
+  RValue(reinterpret_cast<B &>(*aptr));
+
+  // GCC: 
+  RValue(*reinterpret_cast<A *>(bptr));
+
+  // GCC: 
+  RValue(reinterpret_cast<A &>(*bptr));
+
+  // GCC: 1
+  // level1-warning at +1{{type-punned reference might break}}
+  LValue(reinterpret_cast<B &>(*cptr));
+  // level1-note at -1{{'record::B' and 'C' are not alias compatible}}
+
+  // GCC: 1
+  // level1-warning at +1{{type-punned reference might break}}
+  RValue(reinterpret_cast<B &>(*cptr));
+  // level1-note at -1{{'record::B' and 'C' are not alias compatible}}
+}
+
+void Frob(Wrapper<A> *awptr, Wrapper<C> *cwptr)
+{
+  // GCC: 
+  RValue(reinterpret_cast<B *>(&awptr->t));
+
+  // GCC: 
+  LValue(reinterpret_cast<B &>(awptr->t));
+
+  // GCC: 
+  RValue(*reinterpret_cast<B *>(&awptr->t));
+
+  // GCC: 
+  RValue(reinterpret_cast<B &>(awptr->t));
+
+  // GCC: 1, 2
+  // level12-warning at +1{{type-punned pointer might break}}
+  RValue(reinterpret_cast<B *>(&cwptr->t));
+  // level12-note at -1{{'record::B' and 'record::C' are not alias compatible}}
+
+  // GCC: 1, 2, 3
+  // level123-warning at +1{{type-punned pointer might break}}
+  RValue(*reinterpret_cast<B *>(&cwptr->t));
+  // level123-note-re at -1{{'{{(record::)?}}B' and '{{(record::)?}}C' are not alias compatible}}
+
+  // FIXME: no level 3, No actual deref
+  // GCC: 1, 2, 3
+  // level12-warning at +1{{type-punned reference might break}}
+  LValue(reinterpret_cast<B &>(cwptr->t));
+  // level12-note-re at -1{{'{{(record::)?}}B' and '{{(record::)?}}C' are not alias compatible}}
+
+  // GCC: 1, 2, 3
+  // level123-warning at +1{{type-punned reference might break}}
+  RValue(reinterpret_cast<B &>(cwptr->t));
+  // level123-note-re at -1{{'{{(record::)?}}B' and '{{(record::)?}}C' are not alias compatible}}
+}
+
+void Frob() {
+  // GCC:
+  RValue(reinterpret_cast<B *>(&a));
+
+  // GCC:
+  LValue(reinterpret_cast<B &>(a));
+
+  // GCC:
+  RValue(*reinterpret_cast<B *>(&a));
+
+  // GCC:
+  RValue(reinterpret_cast<B &>(a));
+
+  // GCC: 1, 2
+  // level12-warning at +1{{type-punned pointer might break}}
+  RValue(reinterpret_cast<B *>(&c));
+  // level12-note-re at -1{{'{{(record::)?}}B' and '{{(record::)?}}C' are not alias compatible}}
+
+  // GCC: 1, 2, 3
+  // level123-warning at +1{{type-punned pointer might break}}
+  RValue(*reinterpret_cast<B *>(&c));
+  // level123-note-re at -1{{'{{(record::)?}}B' and '{{(record::)?}}C' are not alias compatible}}
+
+  // FIXME: no level 3, No actual deref
+  // GCC: 1, 2, 3
+  // level12-warning at +1{{type-punned reference might break}}
+  LValue(reinterpret_cast<B &>(c));
+  // level12-note-re at -1{{'{{(record::)?}}B' and '{{(record::)?}}C' are not alias compatible}}
+
+  // GCC: 1, 2, 3
+  // level123-warning at +1{{type-punned reference might break}}
+  RValue(reinterpret_cast<B &>(c));
+  // level123-note-re at -1{{'{{(record::)?}}B' and '{{(record::)?}}C' are not alias compatible}}
+}
+
+void Not(Wrapper<A> * a, Wrapper<NotA> *na, Wrapper<U> *u, Wrapper<NotU> *nu) {
+  // GCC: 
+  RValue(reinterpret_cast<B *>(&a->t));
+
+  // GCC: 
+  RValue(reinterpret_cast<B *>(&u->t));
+
+  // The differences below are expected as GCC considers record types containing
+  // at least one aliases-all field to be alias all.  Clang's heuristic requires
+  // the first field of a union or the only field of a struct to be so.
+  
+  // GCC: 
+  // level12-warning at +1{{type-punned pointer might break}}
+  RValue(reinterpret_cast<B *>(&na->t));
+  // level12-note-re at -1{{'{{(record::)?}}B' and '{{(record::)?}}NotA' are not alias compatible}}
+
+  // GCC: 
+  RValue(reinterpret_cast<B *>(&nu->t));
+}
+
+void Base(D d, Wrapper<D> *dw) {
+  // GCC: 
+  RValue(*static_cast<B *>(&d));
+  // GCC: 
+  RValue(*static_cast<B *>(&dw->t));
+
+  RValue(reinterpret_cast<D *>(dw));
+
+  // We permit inheritance, whether GCC does depends on the member types.
+  // GCC: 1, 2
+  RValue(*reinterpret_cast<B *>(&d));
+
+  // GCC: 1, 2
+  RValue(*reinterpret_cast<B *>(&dw->t));
+}
+
+// Clang models may_alias as a decl attribute, not a type attribute.
+
+struct __attribute__((may_alias)) MA {
+  int m;
+};
+
+void Frob(MA *a) {
+  RValue(reinterpret_cast<short *>(a));
+  RValue(*reinterpret_cast<short *>(a));
+  LValue(reinterpret_cast<short &>(*a));
+  RValue(reinterpret_cast<short &>(*a));
+}
+
+struct MM {
+  int __attribute__((may_alias)) m;
+};
+
+void Frob(MM *a) {
+  RValue(reinterpret_cast<short *>(a));
+  RValue(*reinterpret_cast<short *>(a));
+  LValue(reinterpret_cast<short &>(*a));
+  RValue(reinterpret_cast<short &>(*a));
+}
+
+void Inc(E *ep, B *bp) {
+  // GCC: 1
+  // level1-warning at +1{{type-punned pointer to incomplete type might}}
+  RValue(reinterpret_cast<E *>(bp));
+  // level1-note at -1{{is incomplete}}
+
+  // GCC:
+  RValue(reinterpret_cast<B *>(ep));
+
+  // GCC: 1, 2
+  // level12-warning at +1{{type-punned pointer to incomplete type might}}
+  RValue(reinterpret_cast<E *>(&b));
+  // level12-note at -1{{is incomplete}}
+}
+
+}
+
+namespace ccast {
+
+void VarPtr(long *Ptr) {
+  // GCC: 1
+  // level1-warning at +1{{type-punned pointer might break}}
+  RValue((long long *)(Ptr));
+  // level1-note at -1{{'long long' and 'long' are not alias compatible}}
+
+  // GCC: 1
+  // level1-warning at +1{{type-punned reference might break}}
+  RValue((long long &)(*Ptr));
+  // level1-note at -1{{'long long' and 'long' are not alias compatible}}
+
+  // GCC:
+  RValue((long long *)((void *)(Ptr)));
+
+  // GCC: 1
+  // level1-warning at +1{{type-punned reference might break}}
+  RValue((long long &)(*Ptr));
+  // level1-note at -1{{'long long' and 'long' are not alias compatible}}
+
+  // GCC: 1
+  // level1-warning at +1{{type-punned pointer might break}}
+  RValue(*(long long *)(Ptr));
+  // level1-note at -1{{'long long' and 'long' are not alias compatible}}
+
+  // GCC: 1
+  // level1-warning at +1{{type-punned reference might break}}
+  LValue((long long &)(*Ptr));
+  // level1-note at -1{{'long long' and 'long' are not alias compatible}}
+
+  // GCC: 1
+  // level1-warning at +1{{type-punned pointer might break}}
+  LValue(*(long long *)(Ptr));
+  // level1-note at -1{{'long long' and 'long' are not alias compatible}}
+}
+
+void VarRef(long &Ref) {
+  // GCC: 1
+  // level1-warning at +1{{type-punned pointer might break}}
+  RValue((long long *)(&Ref));
+  // level1-note at -1{{'long long' and 'long' are not alias compatible}}
+
+  // GCC: 1
+  // level1-warning at +1{{type-punned reference might break}}
+  RValue((long long &)(Ref));
+  // level1-note at -1{{'long long' and 'long' are not alias compatible}}
+
+  // GCC:
+  RValue((long long *)((void *)(&Ref)));
+
+  // GCC: 1
+  // level1-warning at +1{{type-punned reference might break}}
+  RValue((long long &)(Ref));
+  // level1-note at -1{{'long long' and 'long' are not alias compatible}}
+
+  // GCC: 1
+  // level1-warning at +1{{type-punned pointer might break}}
+  RValue(*(long long *)(&Ref));
+  // level1-note at -1{{'long long' and 'long' are not alias compatible}}
+
+  // GCC: 1
+  // level1-warning at +1{{type-punned reference might break}}
+  LValue((long long &)(Ref));
+  // level1-note at -1{{'long long' and 'long' are not alias compatible}}
+
+  // GCC: 1
+  // level1-warning at +1{{type-punned reference might break}}
+  LValue((long long &)(Ref));
+  // level1-note at -1{{'long long' and 'long' are not alias compatible}}
+
+  // GCC: 1
+  // level1-warning at +1{{type-punned pointer might break}}
+  LValue(*(long long *)(&Ref));
+  // level1-note at -1{{'long long' and 'long' are not alias compatible}}
+}
+
+void Object() {
+  // GCC: 1, 2
+  // level2-warning at +2{{type-punned pointer breaks}}
+  // level1-warning at +1{{type-punned pointer might break}}
+  RValue((long long *)(&Scalar));
+  // level12-note at -1{{'long long' and 'long' are not alias compatible}}
+
+  // GCC: 1, 2, 3
+  // level23-warning at +2{{type-punned reference breaks}}
+  // level1-warning at +1{{type-punned reference might break}}
+  RValue((long long &)(Scalar));
+  // level123-note at -1{{'long long' and 'long' are not alias compatible}}
+
+  // GCC:
+  RValue((long long *)((void *)(&Scalar)));
+
+  // GCC: 1, 2, 3
+  // level23-warning at +2{{type-punned reference breaks}}
+  // level1-warning at +1{{type-punned reference might break}}
+  RValue((long long &)(Scalar));
+  // level123-note at -1{{'long long' and 'long' are not alias compatible}}
+
+  // GCC: 1, 2, 3
+  // level23-warning at +2{{type-punned pointer breaks}}
+  // level1-warning at +1{{type-punned pointer might break}}
+  RValue(*(long long *)(&Scalar));
+  // level123-note at -1{{'long long' and 'long' are not alias compatible}}
+
+  // FIXME: no level 3, No actual deref
+  // GCC: 1, 2, 3
+  // level2-warning at +2{{type-punned reference breaks}}
+  // level1-warning at +1{{type-punned reference might break}}
+  LValue((long long &)(Scalar));
+  // level12-note at -1{{'long long' and 'long' are not alias compatible}}
+  
+  // GCC: 1, 2, 3
+  // level23-warning at +2{{type-punned pointer breaks}}
+  // level1-warning at +1{{type-punned pointer might break}}
+  LValue(*(long long *)(&Scalar));
+  // level123-note at -1{{'long long' and 'long' are not alias compatible}}
+}
+
+// Level 1, 2, 3 - 1, 2, 3
+void DetectedVariants() {
+  // GCC: 1, 2, 3
+  // level23-warning at +2{{type-punned reference breaks}}
+  // level1-warning at +1{{type-punned reference might break}}
+  RValue((long long &)(Ary[1]));
+  // level123-note at -1{{'long long' and 'long' are not alias compatible}}
+
+  // GCC: 1, 2, 3
+  // level23-warning at +2{{type-punned pointer breaks}}
+  // level1-warning at +1{{type-punned pointer might break}}
+  RValue(*(long long *)(&Ary[1]));
+  // level123-note at -1{{'long long' and 'long' are not alias compatible}}
+
+  // GCC: 1, 2, 3
+  // level23-warning at +2{{type-punned reference breaks}}
+  // level1-warning at +1{{type-punned reference might break}}
+  RValue((long long &)(Struct.m));
+  // level123-note at -1{{'long long' and 'int' are not alias compatible}}
+
+  // GCC: 1, 2, 3
+  // level23-warning at +2{{type-punned pointer breaks}}
+  // level1-warning at +1{{type-punned pointer might break}}
+  RValue(*(long long *)(&Struct.m));
+  // level123-note at -1{{'long long' and 'int' are not alias compatible}}
+
+  // GCC: 1, 2, 3
+  // level23-warning at +2{{type-punned reference breaks}}
+  // level1-warning at +1{{type-punned reference might break}}
+  RValue((long long &)((&Struct)->m));
+  // level123-note at -1{{'long long' and 'int' are not alias compatible}}
+
+  // GCC: 1, 2, 3
+  // level23-warning at +2{{type-punned pointer breaks}}
+  // level1-warning at +1{{type-punned pointer might break}}
+  RValue(*(long long *)(&(&Struct)->m));
+  // level123-note at -1{{'long long' and 'int' are not alias compatible}}
+
+  // GCC: 1, 2, 3
+  // level23-warning at +2{{type-punned reference breaks}}
+  // level1-warning at +1{{type-punned reference might break}}
+  RValue((long long &)(__real__(CPLx)));
+  // level123-note at -1{{'long long' and 'int' are not alias compatible}}
+
+  // GCC: 1, 2, 3
+  // level23-warning at +2{{type-punned pointer breaks}}
+  // level1-warning at +1{{type-punned pointer might break}}
+  RValue(*(long long *)(&__real__(CPLx)));
+  // level123-note at -1{{'long long' and 'int' are not alias compatible}}
+
+  // GCC: 1, 2, 3
+  // level23-warning at +2{{type-punned reference breaks}}
+  // level1-warning at +1{{type-punned reference might break}}
+  RValue((long long &)(__imag__(CPLx)));
+  // level123-note at -1{{'long long' and 'int' are not alias compatible}}
+
+  // GCC: 1, 2, 3
+  // level23-warning at +2{{type-punned pointer breaks}}
+  // level1-warning at +1{{type-punned pointer might break}}
+  RValue(*(long long *)(&__imag__(CPLx)));
+  // level123-note at -1{{'long long' and 'int' are not alias compatible}}
+}
+
+void Ok() {
+  // GCC:
+  RValue((unsigned long *)(&Scalar));
+  // GCC:
+  RValue((unsigned long &)(Scalar));
+  // GCC:
+  RValue((unsigned long *)((void *)(&Scalar)));
+  // GCC:
+  RValue((unsigned long &)(Scalar));
+  // GCC:
+  RValue(*(unsigned long *)(&Scalar));
+
+  // GCC:
+  LValue((unsigned long &)(Scalar));
+  // GCC:
+  LValue((unsigned long &)(Scalar));
+  // GCC:
+  LValue(*(unsigned long *)(&Scalar));
+}
+
+// Level 1, 2, 3 - 1, 2, 3
+void Parens() {
+  // GCC: 1, 2, 3
+  // level23-warning at +2{{type-punned reference breaks}}
+  // level1-warning at +1{{type-punned reference might break}}
+  RValue((long long &)((Scalar)));
+  // level123-note at -1{{'long long' and 'long' are not alias compatible}}
+
+  // GCC: 1, 2, 3
+  // level23-warning at +2{{type-punned pointer breaks}}
+  // level1-warning at +1{{type-punned pointer might break}}
+  RValue(*((long long *)((&(Scalar)))));
+  // level123-note at -1{{'long long' and 'long' are not alias compatible}}
+
+  // GCC: 1, 2, 3
+  // level23-warning at +2{{type-punned pointer breaks}}
+  // level1-warning at +1{{type-punned pointer might break}}
+  RValue(*((long long *)((&(Ary[1])))));
+  // level123-note at -1{{'long long' and 'long' are not alias compatible}}
+}
+
+// Clang models may_alias as a decl attribute, not a type attribute.
+
+using MA __attribute__((may_alias)) = int;
+
+void Frob(MA *a) {
+  RValue((short *)(a));
+  RValue(*(short *)(a));
+  LValue((short &)(*a));
+  RValue((short &)(*a));
+}
+
+}
+
+namespace fcast {
+
+// Functional casts, GCC does not get any of these -- comments are for
+// c casts, which are functionally the same.
+
+using LongLongPtr = long long *;
+using LongLongRef = long long &;
+using VoidPtr = void *;
+
+void VarPtr(int *Ptr) {
+  // GCC: 1
+  // level1-warning at +1{{type-punned pointer might break}}
+  RValue(LongLongPtr(Ptr));
+  // level1-note at -1{{'long long' and 'int' are not alias compatible}}
+
+  // GCC: 1
+  // level1-warning at +1{{type-punned reference might break}}
+  RValue(LongLongRef(*Ptr));
+  // level1-note at -1{{'long long' and 'int' are not alias compatible}}
+
+  // GCC:
+  RValue(LongLongPtr(VoidPtr(Ptr)));
+
+  // GCC: 1
+  // level1-warning at +1{{type-punned reference might break}}
+  RValue(LongLongRef(*Ptr));
+  // level1-note at -1{{'long long' and 'int' are not alias compatible}}
+
+  // GCC: 1
+  // level1-warning at +1{{type-punned pointer might break}}
+  RValue(*LongLongPtr(Ptr));
+  // level1-note at -1{{'long long' and 'int' are not alias compatible}}
+
+  // GCC: 1
+  // level1-warning at +1{{type-punned reference might break}}
+  LValue(LongLongRef(*Ptr));
+  // level1-note at -1{{'long long' and 'int' are not alias compatible}}
+
+  // GCC: 1
+  // level1-warning at +1{{type-punned pointer might break}}
+  LValue(*LongLongPtr(Ptr));
+  // level1-note at -1{{'long long' and 'int' are not alias compatible}}
+}
+
+void VarRef(int &Ref) {
+  // GCC: 1
+  // level1-warning at +1{{type-punned pointer might break}}
+  RValue(LongLongPtr(&Ref));
+  // level1-note at -1{{'long long' and 'int' are not alias compatible}}
+
+  // GCC: 1
+  // level1-warning at +1{{type-punned reference might break}}
+  RValue(LongLongRef(Ref));
+  // level1-note at -1{{'long long' and 'int' are not alias compatible}}
+
+  // GCC:
+  RValue(LongLongPtr(VoidPtr(&Ref)));
+
+  // GCC: 1
+  // level1-warning at +1{{type-punned reference might break}}
+  RValue(LongLongRef(Ref));
+  // level1-note at -1{{'long long' and 'int' are not alias compatible}}
+
+  // GCC: 1
+  // level1-warning at +1{{type-punned pointer might break}}
+  RValue(*LongLongPtr(&Ref));
+  // level1-note at -1{{'long long' and 'int' are not alias compatible}}
+
+  // GCC: 1
+  // level1-warning at +1{{type-punned reference might break}}
+  LValue(LongLongRef(Ref));
+  // level1-note at -1{{'long long' and 'int' are not alias compatible}}
+
+  // GCC: 1
+  // level1-warning at +1{{type-punned reference might break}}
+  LValue(LongLongRef(Ref));
+  // level1-note at -1{{'long long' and 'int' are not alias compatible}}
+
+  // GCC: 1
+  // level1-warning at +1{{type-punned pointer might break}}
+  LValue(*LongLongPtr(&Ref));
+  // level1-note at -1{{'long long' and 'int' are not alias compatible}}
+}
+
+void Object() {
+  // GCC: 1, 2
+  // level2-warning at +2{{type-punned pointer breaks}}
+  // level1-warning at +1{{type-punned pointer might break}}
+  RValue(LongLongPtr(&Scalar));
+  // level12-note at -1{{'long long' and 'long' are not alias compatible}}
+
+  // GCC: 1, 2, 3
+  // level23-warning at +2{{type-punned reference breaks}}
+  // level1-warning at +1{{type-punned reference might break}}
+  RValue(LongLongRef(Scalar));
+  // level123-note at -1{{'long long' and 'long' are not alias compatible}}
+
+  // GCC:
+  RValue(LongLongPtr(VoidPtr(&Scalar)));
+
+  // GCC: 1, 2, 3
+  // level23-warning at +2{{type-punned reference breaks}}
+  // level1-warning at +1{{type-punned reference might break}}
+  RValue(LongLongRef(Scalar));
+  // level123-note at -1{{'long long' and 'long' are not alias compatible}}
+
+  // GCC: 1, 2, 3
+  // level23-warning at +2{{type-punned pointer breaks}}
+  // level1-warning at +1{{type-punned pointer might break}}
+  RValue(*LongLongPtr(&Scalar));
+  // level123-note at -1{{'long long' and 'long' are not alias compatible}}
+
+  // FIXME: no level 3, No actual deref
+  // GCC: 1, 2, 3
+  // level2-warning at +2{{type-punned reference breaks}}
+  // level1-warning at +1{{type-punned reference might break}}
+  LValue(LongLongRef(Scalar));
+  // level12-note at -1{{'long long' and 'long' are not alias compatible}}
+
+  // GCC: 1, 2, 3
+  // level23-warning at +2{{type-punned pointer breaks}}
+  // level1-warning at +1{{type-punned pointer might break}}
+  LValue(*LongLongPtr(&Scalar));
+  // level123-note at -1{{'long long' and 'long' are not alias compatible}}
+}
+
+// Level 1, 2, 3 - 1, 2, 3
+void DetectedVariants() {
+  // GCC: 1, 2, 3
+  // level23-warning at +2{{type-punned reference breaks}}
+  // level1-warning at +1{{type-punned reference might break}}
+  RValue(LongLongRef(Ary[1]));
+  // level123-note at -1{{'long long' and 'long' are not alias compatible}}
+
+  // GCC: 1, 2, 3
+  // level23-warning at +2{{type-punned pointer breaks}}
+  // level1-warning at +1{{type-punned pointer might break}}
+  RValue(*LongLongPtr(&Ary[1]));
+  // level123-note at -1{{'long long' and 'long' are not alias compatible}}
+
+  // GCC: 1, 2, 3
+  // level23-warning at +2{{type-punned reference breaks}}
+  // level1-warning at +1{{type-punned reference might break}}
+  RValue(LongLongRef(Struct.m));
+  // level123-note at -1{{'long long' and 'int' are not alias compatible}}
+
+  // GCC: 1, 2, 3
+  // level23-warning at +2{{type-punned pointer breaks}}
+  // level1-warning at +1{{type-punned pointer might break}}
+  RValue(*LongLongPtr(&Struct.m));
+  // level123-note at -1{{'long long' and 'int' are not alias compatible}}
+
+  // GCC: 1, 2, 3
+  // level23-warning at +2{{type-punned reference breaks}}
+  // level1-warning at +1{{type-punned reference might break}}
+  RValue(LongLongRef((&Struct)->m));
+  // level123-note at -1{{'long long' and 'int' are not alias compatible}}
+
+  // GCC: 1, 2, 3
+  // level23-warning at +2{{type-punned pointer breaks}}
+  // level1-warning at +1{{type-punned pointer might break}}
+  RValue(*LongLongPtr(&(&Struct)->m));
+  // level123-note at -1{{'long long' and 'int' are not alias compatible}}
+
+  // GCC: 1, 2, 3
+  // level23-warning at +2{{type-punned reference breaks}}
+  // level1-warning at +1{{type-punned reference might break}}
+  RValue(LongLongRef(__real__(CPLx)));
+  // level123-note at -1{{'long long' and 'int' are not alias compatible}}
+
+  // GCC: 1, 2, 3
+  // level23-warning at +2{{type-punned pointer breaks}}
+  // level1-warning at +1{{type-punned pointer might break}}
+  RValue(*LongLongPtr(&__real__(CPLx)));
+  // level123-note at -1{{'long long' and 'int' are not alias compatible}}
+
+  // GCC: 1, 2, 3
+  // level23-warning at +2{{type-punned reference breaks}}
+  // level1-warning at +1{{type-punned reference might break}}
+  RValue(LongLongRef(__imag__(CPLx)));
+  // level123-note at -1{{'long long' and 'int' are not alias compatible}}
+
+  // GCC: 1, 2, 3
+  // level23-warning at +2{{type-punned pointer breaks}}
+  // level1-warning at +1{{type-punned pointer might break}}
+  RValue(*LongLongPtr(&__imag__(CPLx)));
+  // level123-note at -1{{'long long' and 'int' are not alias compatible}}
+}
+
+using UnsignedLongLongPtr = unsigned long *;
+using UnsignedLongLongRef = unsigned long &;
+
+void Ok() {
+  // GCC:
+  RValue(UnsignedLongLongPtr(&Scalar));
+  // GCC:
+  RValue(UnsignedLongLongRef(Scalar));
+  // GCC:
+  RValue(UnsignedLongLongPtr(VoidPtr(&Scalar)));
+  // GCC:
+  RValue(UnsignedLongLongRef(Scalar));
+  // GCC:
+  RValue(*UnsignedLongLongPtr(&Scalar));
+
+  // GCC:
+  LValue(UnsignedLongLongRef(Scalar));
+  // GCC:
+  LValue(UnsignedLongLongRef(Scalar));
+  // GCC:
+  LValue(*UnsignedLongLongPtr(&Scalar));
+}
+
+// Level 1, 2, 3 - 1, 2, 3
+void Parens() {
+  // GCC: 1, 2, 3
+  // level23-warning at +2{{type-punned reference breaks}}
+  // level1-warning at +1{{type-punned reference might break}}
+  RValue(LongLongRef((Scalar)));
+  // level123-note at -1{{'long long' and 'long' are not alias compatible}}
+
+  // GCC: 1, 2, 3
+  // level23-warning at +2{{type-punned pointer breaks}}
+  // level1-warning at +1{{type-punned pointer might break}}
+  RValue(*(LongLongPtr((&(Scalar)))));
+  // level123-note at -1{{'long long' and 'long' are not alias compatible}}
+
+  // GCC: 1, 2, 3
+  // level23-warning at +2{{type-punned pointer breaks}}
+  // level1-warning at +1{{type-punned pointer might break}}
+  RValue(*(LongLongPtr((&(Ary[1])))));
+  // level123-note at -1{{'long long' and 'long' are not alias compatible}}
+}
+
+// Clang models may_alias as a decl attribute, not a type attribute.
+
+using MA __attribute__((may_alias)) = int;
+
+using ShortPtr = short *;
+using ShortRef = short &;
+
+void Frob(MA *a) {
+  RValue(ShortPtr(a));
+  RValue(*ShortPtr(a));
+  LValue(ShortRef(*a));
+  RValue(ShortRef(*a));
+}
+
+}
+
+namespace puns {
+
+// quiet-no-diagnostics
+
+void Foo () {
+  float d = 0.0;
+  int i[2];
+
+  // level23-warning at +2{{type-punned pointer breaks}}
+  // level1-warning at +1{{type-punned pointer might break}}
+  i[0] = reinterpret_cast<int *>(&d)[0];
+  // level123-note at -1{{'int' and 'float' are not alias compatible}}
+
+  // level23-warning at +2{{type-punned reference breaks}}
+  // level1-warning at +1{{type-punned reference might break}}
+  i[0] = reinterpret_cast<int (&)[2]>(d)[0];
+  // level123-note at -1{{'int' and 'float' are not alias compatible}}
+
+  // level23-warning at +2{{type-punned reference breaks}}
+  // level1-warning at +1{{type-punned reference might break}}
+  i[0] = reinterpret_cast<int &>(d);
+  // level123-note at -1{{'int' and 'float' are not alias compatible}}
+
+  // level23-warning at +2{{type-punned pointer breaks}}
+  // level1-warning at +1{{type-punned pointer might break}}
+  reinterpret_cast<int *>(&d)[0] = 1;
+  // level123-note at -1{{'int' and 'float' are not alias compatible}}
+
+  // level23-warning at +2{{type-punned reference breaks}}
+  // level1-warning at +1{{type-punned reference might break}}
+  reinterpret_cast<int (&)[2]>(d)[0] = 1;
+  // level123-note at -1{{'int' and 'float' are not alias compatible}}
+
+  // level23-warning at +2{{type-punned reference breaks}}
+  // level1-warning at +1{{type-punned reference might break}}
+  reinterpret_cast<int &>(d) = 1;
+  // level123-note at -1{{'int' and 'float' are not alias compatible}}
+
+  // level23-warning at +2{{type-punned pointer breaks}}
+  // level1-warning at +1{{type-punned pointer might break}}
+  ++*reinterpret_cast<int *>(&d);
+  // level123-note at -1{{'int' and 'float' are not alias compatible}}
+
+  // level23-warning at +2{{type-punned pointer breaks}}
+  // level1-warning at +1{{type-punned pointer might break}}
+  --*reinterpret_cast<int *>(&d);
+  // level123-note at -1{{'int' and 'float' are not alias compatible}}
+
+  // level23-warning at +2{{type-punned pointer breaks}}
+  // level1-warning at +1{{type-punned pointer might break}}
+  (*reinterpret_cast<int *>(&d))++;
+  // level123-note at -1{{'int' and 'float' are not alias compatible}}
+
+  // level23-warning at +2{{type-punned pointer breaks}}
+  // level1-warning at +1{{type-punned pointer might break}}
+  (*reinterpret_cast<int *>(&d))--;
+  // level123-note at -1{{'int' and 'float' are not alias compatible}}
+
+  // level23-warning at +2{{type-punned pointer breaks}}
+  // level1-warning at +1{{type-punned pointer might break}}
+  *reinterpret_cast<int *>(&d) += 1;
+  // level123-note at -1{{'int' and 'float' are not alias compatible}}
+
+  // level23-warning at +2{{type-punned pointer breaks}}
+  // level1-warning at +1{{type-punned pointer might break}}
+  *reinterpret_cast<int *>(&d) -= 1;
+  // level123-note at -1{{'int' and 'float' are not alias compatible}}
+  
+  // level23-warning at +2{{type-punned reference breaks}}
+  // level1-warning at +1{{type-punned reference might break}}
+  ++reinterpret_cast<int &>(d);
+  // level123-note at -1{{'int' and 'float' are not alias compatible}}
+
+  // level23-warning at +2{{type-punned reference breaks}}
+  // level1-warning at +1{{type-punned reference might break}}
+  --reinterpret_cast<int &>(d);
+  // level123-note at -1{{'int' and 'float' are not alias compatible}}
+
+  // level23-warning at +2{{type-punned reference breaks}}
+  // level1-warning at +1{{type-punned reference might break}}
+  reinterpret_cast<int &>(d)++;
+  // level123-note at -1{{'int' and 'float' are not alias compatible}}
+
+  // level23-warning at +2{{type-punned reference breaks}}
+  // level1-warning at +1{{type-punned reference might break}}
+  reinterpret_cast<int &>(d)--;
+  // level123-note at -1{{'int' and 'float' are not alias compatible}}
+
+  // level23-warning at +2{{type-punned reference breaks}}
+  // level1-warning at +1{{type-punned reference might break}}
+  reinterpret_cast<int &>(d) += 1;
+  // level123-note at -1{{'int' and 'float' are not alias compatible}}
+
+  // level23-warning at +2{{type-punned reference breaks}}
+  // level1-warning at +1{{type-punned reference might break}}
+  reinterpret_cast<int &>(d) -= 1;
+  // level123-note at -1{{'int' and 'float' are not alias compatible}}
+
+  // level23-warning at +2{{type-punned pointer breaks}}
+  // level1-warning at +1{{type-punned pointer might break}}
+  d = reinterpret_cast<float *>(&i)[0];
+  // level123-note at -1{{'float' and 'int' are not alias compatible}}
+
+  // level23-warning at +2{{type-punned pointer breaks}}
+  // level1-warning at +1{{type-punned pointer might break}}
+  d = *reinterpret_cast<float *>(&i[0]);
+  // level123-note at -1{{'float' and 'int' are not alias compatible}}
+
+  // level23-warning at +2{{type-punned pointer breaks}}
+  // level1-warning at +1{{type-punned pointer might break}}
+  d = *reinterpret_cast<float *>(i);
+  // level123-note at -1{{'float' and 'int' are not alias compatible}}
+
+  // level23-warning at +2{{type-punned pointer breaks}}
+  // level1-warning at +1{{type-punned pointer might break}}
+  d = *reinterpret_cast<float *>(&i[0]);
+  // level123-note at -1{{'float' and 'int' are not alias compatible}}
+
+  // level23-warning at +2{{type-punned reference breaks}}
+  // level1-warning at +1{{type-punned reference might break}}
+  d = reinterpret_cast<float &>(i);
+  // level123-note at -1{{'float' and 'int' are not alias compatible}}
+
+  // level23-warning at +2{{type-punned reference breaks}}
+  // level1-warning at +1{{type-punned reference might break}}
+  d = reinterpret_cast<float &>(i[0]);
+  // level123-note at -1{{'float' and 'int' are not alias compatible}}
+
+  // level23-warning at +2{{type-punned reference breaks}}
+  // level1-warning at +1{{type-punned reference might break}}
+  d = reinterpret_cast<float &>(i);
+  // level123-note at -1{{'float' and 'int' are not alias compatible}}
+
+  // level23-warning at +2{{type-punned reference breaks}}
+  // level1-warning at +1{{type-punned reference might break}}
+  d = reinterpret_cast<float &>(i[0]);
+  // level123-note at -1{{'float' and 'int' are not alias compatible}}
+
+  reinterpret_cast<Struct_t &>(i).m = 1;
+  reinterpret_cast<Struct_t *>(&i)->m = 1;
+
+  // level123-warning at +1{{type-punned reference might break}}
+  reinterpret_cast<Struct2_t &>(i).m = 1;
+  // level123-note at -1{{'Struct2_t' and 'int' are not alias compatible}}
+
+  // level123-warning at +1{{type-punned pointer might break}}
+  reinterpret_cast<Struct2_t *>(&i)->m = 1;
+  // level123-note at -1{{'Struct2_t' and 'int' are not alias compatible}}
+}
+
+
+void pr50066() {
+  double d = 2.34;
+  int i[2];
+
+  RValue(d);
+
+  // GCC: 1, 2, 3
+  // level23-warning at +2{{type-punned pointer breaks}}
+  // level1-warning at +1{{type-punned pointer might break}}
+  *(long long *)i =
+  // level123-note at -1{{'long long' and 'int' are not alias compatible}}
+
+      // GCC: 1, 2, 3
+      // level23-warning at +2{{type-punned pointer breaks}}
+      // level1-warning at +1{{type-punned pointer might break}}
+      *(long long *)&d;
+      // level123-note at -1{{'long long' and 'double' are not alias compatible}}
+
+  // GCC: 1, 2, 3
+  // level23-warning at +2{{type-punned pointer breaks}}
+  // level1-warning at +1{{type-punned pointer might break}}
+  ((int *)&d)[0] = i[0];
+  // level123-note at -1{{'int' and 'double' are not alias compatible}}
+
+  // GCC: 1, 2
+  // GCC misses this at level 3, because it represents (cast)[0] as
+  // *(cast), but (cast)[1] as *((cast) + 1), which doesn't trigger the
+  // indirection case that levl 3 requires.
+
+  // level23-warning at +2{{type-punned pointer breaks}}
+  // level1-warning at +1{{type-punned pointer might break}}
+  ((int *)&d)[1] = i[1];
+  // level123-note at -1{{'int' and 'double' are not alias compatible}}
+
+  RValue(d);
+
+  ((char *)&d)[2] += 123;
+
+  RValue(d);
+}
+}
+
+namespace tpl {
+
+int vi;
+float vf;
+
+template<typename T>
+void Frob0(int *a) {
+  // GCC 1
+  // level1-warning at +1 2{{type-punned pointer might break}}
+  RValue(*reinterpret_cast<float *>(a));
+  // level1-note at -1 2{{'float' and 'int' are not alias compatible}}
+
+  // GCC 1
+  // level1-warning at +1 2{{type-punned pointer might break}}
+  RValue(*reinterpret_cast<float *>(static_cast<void *>(a)));
+  // level1-note at -1 2{{'float' and 'int' are not alias compatible}}
+}
+
+template<typename T>
+void Frob0() {
+  // GCC 1, 2, 3
+  // level23-warning at +2{{type-punned pointer breaks}}
+  // level1-warning at +1{{type-punned pointer might break}}
+  RValue(*reinterpret_cast<int *>(&vf));
+  // level123-note at -1{{'int' and 'float' are not alias compatible}}
+
+  // GCC 1, 2, 3
+  // level2-warning at +2 2{{type-punned pointer breaks}}
+  // level1-warning at +1 2{{type-punned pointer might break}}
+  RValue(*reinterpret_cast<int *>(static_cast<void *>(&vf)));
+  // level12-note at -1 2{{'int' and 'float' are not alias compatible}}
+}
+
+template<typename T>
+void Frob1(T *b) {
+  // GCC 1
+  // level1-warning at +1{{type-punned pointer might break}}
+  RValue(*reinterpret_cast<float *>(b));
+  // level1-note at -1{{'float' and 'long' are not alias compatible}}
+
+  // GCC 1
+  // level1-warning at +1{{type-punned pointer might break}}
+  RValue(*reinterpret_cast<float *>(static_cast<void *>(b)));
+  // level1-note at -1{{'float' and 'long' are not alias compatible}}
+}
+
+template<typename T>
+void Frob1() {
+  // GCC 1, 2, 3
+  // level23-warning at +2{{type-punned pointer breaks}}
+  // level1-warning at +1{{type-punned pointer might break}}
+  RValue(*reinterpret_cast<T *>(&vf));
+  // level123-note at -1{{'long' and 'float' are not alias compatible}}
+
+  // GCC 1, 2, 3
+  // level2-warning at +2{{type-punned pointer breaks}}
+  // level1-warning at +1{{type-punned pointer might break}}
+  RValue(*reinterpret_cast<T *>(static_cast<void *>(&vf)));
+  // level12-note at -1{{'long' and 'float' are not alias compatible}}
+}
+
+void Call(){
+  int i;
+  // level1-note at +1{{'tpl::Frob0<float>' requested here}}
+  Frob0<float>(&i);
+  // level12-note at +1{{'tpl::Frob0<float>' requested here}}
+  Frob0<float>();
+
+  float f;
+  Frob1(&f);
+  Frob1<float>();
+
+  long l;
+  // level1-note at +1{{'tpl::Frob1<long>' requested here}}
+  Frob1(&l);
+  // level123-note at +1{{'tpl::Frob1<long>' requested here}}
+  Frob1<long>();
+}
+
+}
+
+namespace embedding {
+// Check embedding as first member is accepted
+
+struct Ints { int a, b; };
+struct Floats { float a, b; };
+struct Other { short a, b, c, d; };
+
+template<typename T>
+struct Wrapper { T m; void *other;};
+
+template<typename T>
+struct Allocator { alignas(T) char buffer[sizeof(T)]; };
+
+union Union {
+  void *random;
+  Ints ints;
+  Floats floats;
+};
+
+template<typename T>
+struct NotWrapper { void *other; T m; };
+
+void FromContainer (Wrapper<Ints> wrapint,
+                    Allocator<Ints> allocint,
+                    Union u,
+                    NotWrapper<Ints> notwrapint) {
+
+  RValue (*reinterpret_cast<Ints *> (&wrapint));
+  RValue (*reinterpret_cast<Ints *> (&allocint));
+  RValue (*reinterpret_cast<Ints *> (&u));
+  RValue (*reinterpret_cast<Floats *> (&u));
+
+  // level123-warning at +1{{type-punned pointer might break}}
+  RValue (*reinterpret_cast<Ints *> (&notwrapint));
+  // level123-note-re at -1{{'{{(embedding::)?}}Ints' and 'NotWrapper<Ints>' are not alias compatible}}
+}
+
+void ToContainer (Ints ints, Floats floats, Other others) {
+  RValue (*reinterpret_cast<Wrapper<Ints> *>(&ints));
+
+  // level123-warning at +1{{type-punned pointer to incomplete type might break}}
+  RValue (*reinterpret_cast<Wrapper<Floats> *>(&floats));
+  // level123-note-re at -1{{'{{(embedding::)?}}Wrapper<{{(embedding::)?}}Floats>' is incomplete}}
+
+  // This second cast occurs after Wrapper<Floats> has been instantiated, so no
+  // warning.
+  RValue (*reinterpret_cast<Wrapper<Floats> *>(&floats));
+
+  RValue (*reinterpret_cast<Allocator<Ints> *>(&ints));
+  RValue (*reinterpret_cast<Union *>(&ints));
+  RValue (*reinterpret_cast<Union *>(&floats));
+
+  // level123-warning at +1{{type-punned pointer might break}}
+  RValue (*reinterpret_cast<Wrapper<Floats> *>(&ints));
+  // level123-note-re at -1{{'{{(embedding::)?}}Wrapper<{{(embedding::)?}}Floats>' and 'Ints' are not alias compatible}}
+
+  // level123-warning at +1{{type-punned pointer might break}}
+  RValue (*reinterpret_cast<NotWrapper<Ints> *>(&ints));
+  // level123-note-re at -1{{'{{(embedding::)?}}NotWrapper<{{(embedding::)?}}Ints>' and 'Ints' are not alias compatible}}
+
+  // level123-warning at +1{{type-punned pointer might break}}
+  RValue (*reinterpret_cast<Union *>(&others));
+  // level123-note-re at -1{{'{{(embedding::)?}}Union' and 'Other' are not alias compatible}}
+}
+
+struct Inner { int m; };
+struct Outer1 { Inner i; };
+struct Outer2 { Outer1 o; };
+struct Inherit1 : Inner {};
+struct Inherit2 : Inherit1 {};
+
+void Inherit() {
+  // Check we see through multiple levels
+  Inner i;
+  Outer2 o;
+  Inherit2 i2;
+  int in;
+
+  // Containership is ok
+  RValue(*reinterpret_cast<Outer2 *>(&i));
+  RValue(*reinterpret_cast<Inner *>(&o.o));
+  RValue(*reinterpret_cast<Inner *>(&o));
+  RValue(*reinterpret_cast<Outer2 *>(&in));
+  RValue(*reinterpret_cast<int *>(&o));
+
+  // Inheritance is also ok -- but why not static cast here?
+  RValue(*reinterpret_cast<Inherit2 *>(&i));
+  RValue(*reinterpret_cast<Inner *>(&i2));
+  RValue(*reinterpret_cast<Inherit2 *>(&in));
+  RValue(*reinterpret_cast<int *>(&i2));
+}
+
+}



More information about the cfe-commits mailing list