[clang] [clang] Strict aliasing warning ala GCC [PR50066] (PR #74155)
Nathan Sidwell via cfe-commits
cfe-commits at lists.llvm.org
Wed Dec 6 04:12:26 PST 2023
https://github.com/urnathan updated https://github.com/llvm/llvm-project/pull/74155
>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 1/5] [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 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 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 b204cb01a0def..3d4d9048f8104 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 473eea55bb6b1..8ab45110d4a93 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 50ee0a5acb558..b9fe4e96381b2 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 01ba4971ee065..ff6b3e7686ff5 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 0000000000000..ad561939eeda1
--- /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 0000000000000..aedeef847378f
--- /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 *> (¬wrapint));
+ // 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));
+}
+
+}
>From 4dbce70420633abdfce665d247591ebed434d24f Mon Sep 17 00:00:00 2001
From: Nathan Sidwell <nathan at acm.org>
Date: Mon, 4 Dec 2023 08:34:57 -0500
Subject: [PATCH 2/5] Use scoped enum for AliasingKind
---
clang/include/clang/AST/ASTConsumer.h | 10 +++++-----
clang/lib/CodeGen/CodeGenTBAA.cpp | 18 +++++++++---------
clang/lib/Sema/SemaCast.cpp | 8 ++++----
3 files changed, 18 insertions(+), 18 deletions(-)
diff --git a/clang/include/clang/AST/ASTConsumer.h b/clang/include/clang/AST/ASTConsumer.h
index d5731ed6adf63..e635ad0827ccf 100644
--- a/clang/include/clang/AST/ASTConsumer.h
+++ b/clang/include/clang/AST/ASTConsumer.h
@@ -47,11 +47,11 @@ class ASTConsumer {
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.
+ enum class AliasingKind {
+ Ok, // Alias sets are compatible.
+ ToIncomplete, // Converting to an incomplete type
+ KnownDisjoint, // The alias sets are known to be disjoint.
+ MaybeDisjoint, // The alias sets might be disjoint.
};
// Return aliasing kind of reinterpreting the representation of a Src type
diff --git a/clang/lib/CodeGen/CodeGenTBAA.cpp b/clang/lib/CodeGen/CodeGenTBAA.cpp
index def339cdaeede..a117493de51bb 100644
--- a/clang/lib/CodeGen/CodeGenTBAA.cpp
+++ b/clang/lib/CodeGen/CodeGenTBAA.cpp
@@ -504,7 +504,7 @@ CodeGenTBAA::AliasingKind CodeGenTBAA::getAliasingKind(QualType &Dst,
QualType &Src) {
assert(!Src->isVoidType() && !Dst->isVoidType());
if (TypeHasMayAlias(Src) || TypeHasMayAlias(Dst))
- return AK_Ok;
+ return AliasingKind::Ok;
Src = QualType{Src->getBaseElementTypeUnsafe(), 0};
Dst = QualType{Dst->getBaseElementTypeUnsafe(), 0};
@@ -519,12 +519,12 @@ CodeGenTBAA::AliasingKind CodeGenTBAA::getAliasingKind(QualType &Dst,
if (!SrcDecl) {
SrcTBAA = getTypeInfo(Src);
if (!SrcTBAA || SrcTBAA == AnyTBAA)
- return AK_Ok;
+ return AliasingKind::Ok;
}
if (!DstDecl) {
DstTBAA = getTypeInfo(Dst);
if (!DstTBAA || DstTBAA == AnyTBAA)
- return AK_Ok;
+ return AliasingKind::Ok;
}
auto IsAncestor = [](const llvm::MDNode *Ancestor,
@@ -547,8 +547,8 @@ CodeGenTBAA::AliasingKind CodeGenTBAA::getAliasingKind(QualType &Dst,
if (SrcTBAA == DstTBAA || IsAncestor(SrcTBAA, DstTBAA) ||
IsAncestor(DstTBAA, SrcTBAA))
- return AK_Ok;
- return AK_KnownDisjoint;
+ return AliasingKind::Ok;
+ return AliasingKind::KnownDisjoint;
}
// Is InnerTy (recursively) a field(s) or base of Outer at offset zero? Or is
@@ -617,18 +617,18 @@ CodeGenTBAA::AliasingKind CodeGenTBAA::getAliasingKind(QualType &Dst,
const RecordDecl *SrcDef = SrcDecl->getDefinition();
if (!SrcDef || Contains(SrcDef, Dst, DstTBAA, Contains))
// From incomplete, or container of Dst
- return AK_Ok;
+ return AliasingKind::Ok;
}
if (DstDecl) {
const RecordDecl *DstDef = DstDecl->getDefinition();
if (!DstDef)
- return AK_ToIncomplete;
+ return AliasingKind::ToIncomplete;
if (Contains(DstDef, Src, SrcTBAA, Contains))
// To container of Src.
- return AK_Ok;
+ return AliasingKind::Ok;
}
// Both are complete and we've not found a relationship.
- return AK_MaybeDisjoint;
+ return AliasingKind::MaybeDisjoint;
}
diff --git a/clang/lib/Sema/SemaCast.cpp b/clang/lib/Sema/SemaCast.cpp
index f47f99319ec12..9f5232829987f 100644
--- a/clang/lib/Sema/SemaCast.cpp
+++ b/clang/lib/Sema/SemaCast.cpp
@@ -2144,15 +2144,15 @@ void Sema::CheckStrictAliasing(Expr const *E, QualType DstTy, bool IsLValueCast,
auto Aliasing =
getASTConsumer().getTypeAliasing()->getAliasingKind(DstTy, SrcTy);
- if (Aliasing != ASTConsumer::TypeAliasing::AK_Ok &&
+ if (Aliasing != ASTConsumer::TypeAliasing::AliasingKind::Ok &&
(Diag(Range.getBegin(), diag::warn_strict_aliasing)
<< IsLValueCast
- << (Aliasing == ASTConsumer::TypeAliasing::AK_ToIncomplete)
- << (Aliasing == ASTConsumer::TypeAliasing::AK_KnownDisjoint &&
+ << (Aliasing == ASTConsumer::TypeAliasing::AliasingKind::ToIncomplete)
+ << (Aliasing == ASTConsumer::TypeAliasing::AliasingKind::KnownDisjoint &&
Diags.getDiagnosticOptions().StrictAliasing >= 2)
<< Range))
Diag(Range.getBegin(),
- Aliasing == ASTConsumer::TypeAliasing::AK_ToIncomplete
+ Aliasing == ASTConsumer::TypeAliasing::AliasingKind::ToIncomplete
? diag::note_incomplete_aliasing
: diag::note_incompatible_aliasing)
<< DstTy << SrcTy;
>From 0458a3450c1d442a95fb7845762eef23bbd714f1 Mon Sep 17 00:00:00 2001
From: Nathan Sidwell <nathan at acm.org>
Date: Mon, 4 Dec 2023 16:19:58 -0500
Subject: [PATCH 3/5] Add ubiquitous char test
---
clang/test/SemaCXX/strict-aliasing-warn.cpp | 25 +++++++++++++++++----
1 file changed, 21 insertions(+), 4 deletions(-)
diff --git a/clang/test/SemaCXX/strict-aliasing-warn.cpp b/clang/test/SemaCXX/strict-aliasing-warn.cpp
index aedeef847378f..e8b7161f734d4 100644
--- a/clang/test/SemaCXX/strict-aliasing-warn.cpp
+++ b/clang/test/SemaCXX/strict-aliasing-warn.cpp
@@ -286,6 +286,27 @@ void Frob(MA *a) {
}
+namespace std {
+enum byte : unsigned char {};
+enum my_byte : unsigned char {};
+}
+
+namespace ubiquitous_char {
+void Frob(int a) {
+ // ubiquitous char
+ RValue(*reinterpret_cast<char *>(&a));
+ RValue(*reinterpret_cast<unsigned char *>(&a));
+ RValue(*reinterpret_cast<signed char *>(&a));
+ RValue(*reinterpret_cast<std::byte *>(&a));
+
+ // Not ubiquitous char
+ // level23-warning at +2{{type-punned pointer breaks}}
+ // level1-warning at +1{{type-punned pointer might break}}
+ RValue(*reinterpret_cast<std::my_byte *>(&a));
+ // level123-note at -1{{'std::my_byte' and 'int' are not alias compatible}}
+}
+}
+
namespace record {
struct A {
@@ -397,7 +418,6 @@ void Frob(Wrapper<A> *awptr, Wrapper<C> *cwptr)
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));
@@ -432,7 +452,6 @@ void Frob() {
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));
@@ -626,7 +645,6 @@ void Object() {
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}}
@@ -869,7 +887,6 @@ void Object() {
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}}
>From d20658f5144a6fe8bb38cbaca2ccff9a368a91f2 Mon Sep 17 00:00:00 2001
From: Nathan Sidwell <nathan at acm.org>
Date: Mon, 4 Dec 2023 14:38:42 -0500
Subject: [PATCH 4/5] Adjust diagnostic options
---
clang/include/clang/Basic/DiagnosticOptions.def | 3 +++
clang/include/clang/Basic/DiagnosticOptions.h | 4 ----
clang/lib/Frontend/FrontendAction.cpp | 7 -------
clang/lib/Sema/SemaCast.cpp | 7 +++++--
4 files changed, 8 insertions(+), 13 deletions(-)
diff --git a/clang/include/clang/Basic/DiagnosticOptions.def b/clang/include/clang/Basic/DiagnosticOptions.def
index 6d0c1b14acc12..a6a90495f6a02 100644
--- a/clang/include/clang/Basic/DiagnosticOptions.def
+++ b/clang/include/clang/Basic/DiagnosticOptions.def
@@ -99,6 +99,9 @@ VALUE_DIAGOPT(MessageLength, 32, 0)
DIAGOPT(ShowSafeBufferUsageSuggestions, 1, 0)
+/// Level of scrutiny reinterpret_cast gets for type safetyness.
+VALUE_DIAGOPT(StrictAliasing, 2, 3)
+
#undef DIAGOPT
#undef ENUM_DIAGOPT
#undef VALUE_DIAGOPT
diff --git a/clang/include/clang/Basic/DiagnosticOptions.h b/clang/include/clang/Basic/DiagnosticOptions.h
index 1c6b9d1fb82b1..099982c3bdd5a 100644
--- a/clang/include/clang/Basic/DiagnosticOptions.h
+++ b/clang/include/clang/Basic/DiagnosticOptions.h
@@ -128,10 +128,6 @@ 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/lib/Frontend/FrontendAction.cpp b/clang/lib/Frontend/FrontendAction.cpp
index c5f709bae822b..9692e43f818ec 100644
--- a/clang/lib/Frontend/FrontendAction.cpp
+++ b/clang/lib/Frontend/FrontendAction.cpp
@@ -1007,13 +1007,6 @@ 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 9f5232829987f..3b911cbf55e55 100644
--- a/clang/lib/Sema/SemaCast.cpp
+++ b/clang/lib/Sema/SemaCast.cpp
@@ -2080,6 +2080,10 @@ void Sema::CheckStrictAliasing(Expr const *E, QualType DstTy, bool IsLValueCast,
SourceRange Range) {
assert(Diags.getDiagnosticOptions().StrictAliasing);
+ auto *TBAA = getASTConsumer().getTypeAliasing();
+ if (!TBAA)
+ return;
+
if (Diags.isIgnored(diag::warn_strict_aliasing, Range.getBegin()))
return;
@@ -2141,8 +2145,7 @@ void Sema::CheckStrictAliasing(Expr const *E, QualType DstTy, bool IsLValueCast,
if (SrcTy->isFunctionType())
return; // From Fn type, meaningless.
- auto Aliasing =
- getASTConsumer().getTypeAliasing()->getAliasingKind(DstTy, SrcTy);
+ auto Aliasing = TBAA->getAliasingKind(DstTy, SrcTy);
if (Aliasing != ASTConsumer::TypeAliasing::AliasingKind::Ok &&
(Diag(Range.getBegin(), diag::warn_strict_aliasing)
>From 5c53352a0b9b1c4d0837ff2dca7f36d6bd32ca22 Mon Sep 17 00:00:00 2001
From: Nathan Sidwell <nathan at acm.org>
Date: Tue, 5 Dec 2023 11:11:32 -0500
Subject: [PATCH 5/5] Release note
---
clang/docs/ReleaseNotes.rst | 9 +++++++++
1 file changed, 9 insertions(+)
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 8733bb93f5708..99e608d723e14 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -360,6 +360,15 @@ Improvements to Clang's diagnostics
- Clang constexpr evaluator now diagnoses compound assignment operators against
uninitialized variables as a read of uninitialized object.
(`#51536 <https://github.com/llvm/llvm-project/issues/51536>`_)
+- Clang now implements ``-Wstrict-aliasing=$level``, ``$level=[0-3]``
+ with ``-Wstrict-aliasing`` defaulint to level 3. This warns about
+ and potentially type-based aliasing-unsafe reinterpret_casts. Lower
+ levels warn about more case but with more false positives, higher
+ levels have fewer false positives at the risk of more false
+ negatives. The warning requires require optimization to be
+ enabled. Previously these were accepted, but ignored, for GCC
+ compatibility (the preciese semantics are similar-to, but not
+ identical with, GCC).
- Clang's ``-Wformat-truncation`` now diagnoses ``snprintf`` call that is known to
result in string truncation.
(`#64871: <https://github.com/llvm/llvm-project/issues/64871>`_).
More information about the cfe-commits
mailing list