[clang] Add check in SemaChecking for multiple unsequenced volatile accesses (PR #180955)
Nikita Terentev via cfe-commits
cfe-commits at lists.llvm.org
Fri Aug 21 05:13:10 PDT 2026
https://github.com/Seraphimt updated https://github.com/llvm/llvm-project/pull/180955
>From 8411b532a145c3b5a39a3785a1ab6c3dbaf446e8 Mon Sep 17 00:00:00 2001
From: Seraphimt <svet58585 at mail.ru>
Date: Wed, 11 Feb 2026 12:18:06 +0300
Subject: [PATCH 01/12] Add warning to Sema for multi unseq use volatile.
---
clang/lib/Sema/SemaChecking.cpp | 42 ++++++++++++++++++-------
clang/test/SemaCXX/warn-unsequenced.cpp | 26 +++++++++++++++
2 files changed, 56 insertions(+), 12 deletions(-)
diff --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp
index 89171246d0bcb..b9d1ae9240923 100644
--- a/clang/lib/Sema/SemaChecking.cpp
+++ b/clang/lib/Sema/SemaChecking.cpp
@@ -14015,7 +14015,7 @@ class SequenceChecker : public ConstEvaluatedExprVisitor<SequenceChecker> {
};
/// An object for which we can track unsequenced uses.
- using Object = const NamedDecl *;
+ using Object = const ValueDecl *;
/// Different flavors of object usage which we track. We only track the
/// least-sequenced usage of each kind.
@@ -14034,6 +14034,12 @@ class SequenceChecker : public ConstEvaluatedExprVisitor<SequenceChecker> {
UK_Count = UK_ModAsSideEffect + 1
};
+ enum WarningKind {
+ WK_UseAndMod,
+ WK_ModAndMod,
+ WK_Volatile_UseAndUse
+ };
+
/// Bundle together a sequencing region and the expression corresponding
/// to a specific usage. One Usage is stored for each usage kind in UsageInfo.
struct Usage {
@@ -14179,7 +14185,7 @@ class SequenceChecker : public ConstEvaluatedExprVisitor<SequenceChecker> {
/// \p IsModMod is true when we are checking for a mod-mod unsequenced
/// usage and false we are checking for a mod-use unsequenced usage.
void checkUsage(Object O, UsageInfo &UI, const Expr *UsageExpr,
- UsageKind OtherKind, bool IsModMod) {
+ UsageKind OtherKind, WarningKind WarnKind) {
if (UI.Diagnosed)
return;
@@ -14192,11 +14198,22 @@ class SequenceChecker : public ConstEvaluatedExprVisitor<SequenceChecker> {
if (OtherKind == UK_Use)
std::swap(Mod, ModOrUse);
+ unsigned DiagID = 0;
+ switch (WarnKind) {
+ case WK_UseAndMod:
+ DiagID = diag::warn_unsequenced_mod_use;
+ break;
+ case WK_ModAndMod:
+ DiagID = diag::warn_unsequenced_mod_mod;
+ break;
+ case WK_Volatile_UseAndUse:
+ DiagID = diag::warn_unsequenced_use_use_volatile;
+ break;
+ }
+
SemaRef.DiagRuntimeBehavior(
Mod->getExprLoc(), {Mod, ModOrUse},
- SemaRef.PDiag(IsModMod ? diag::warn_unsequenced_mod_mod
- : diag::warn_unsequenced_mod_use)
- << O << SourceRange(ModOrUse->getExprLoc()));
+ SemaRef.PDiag(DiagID)<< O << SourceRange(ModOrUse->getExprLoc()));
UI.Diagnosed = true;
}
@@ -14229,27 +14246,28 @@ class SequenceChecker : public ConstEvaluatedExprVisitor<SequenceChecker> {
void notePreUse(Object O, const Expr *UseExpr) {
UsageInfo &UI = UsageMap[O];
// Uses conflict with other modifications.
- checkUsage(O, UI, UseExpr, /*OtherKind=*/UK_ModAsValue, /*IsModMod=*/false);
+ checkUsage(O, UI, UseExpr, /*OtherKind=*/UK_ModAsValue, WK_UseAndMod);
+ // Volatile uses conflict with other uses.
+ if (O->getType().isVolatileQualified())
+ checkUsage(O, UI, UseExpr, /*OtherKind=*/UK_Use, WK_Volatile_UseAndUse);
}
void notePostUse(Object O, const Expr *UseExpr) {
UsageInfo &UI = UsageMap[O];
- checkUsage(O, UI, UseExpr, /*OtherKind=*/UK_ModAsSideEffect,
- /*IsModMod=*/false);
+ checkUsage(O, UI, UseExpr, /*OtherKind=*/UK_ModAsSideEffect, WK_UseAndMod);
addUsage(O, UI, UseExpr, /*UsageKind=*/UK_Use);
}
void notePreMod(Object O, const Expr *ModExpr) {
UsageInfo &UI = UsageMap[O];
// Modifications conflict with other modifications and with uses.
- checkUsage(O, UI, ModExpr, /*OtherKind=*/UK_ModAsValue, /*IsModMod=*/true);
- checkUsage(O, UI, ModExpr, /*OtherKind=*/UK_Use, /*IsModMod=*/false);
+ checkUsage(O, UI, ModExpr, /*OtherKind=*/UK_ModAsValue, WK_ModAndMod);
+ checkUsage(O, UI, ModExpr, /*OtherKind=*/UK_Use, WK_UseAndMod);
}
void notePostMod(Object O, const Expr *ModExpr, UsageKind UK) {
UsageInfo &UI = UsageMap[O];
- checkUsage(O, UI, ModExpr, /*OtherKind=*/UK_ModAsSideEffect,
- /*IsModMod=*/true);
+ checkUsage(O, UI, ModExpr, /*OtherKind=*/UK_ModAsSideEffect, WK_ModAndMod);
addUsage(O, UI, ModExpr, /*UsageKind=*/UK);
}
diff --git a/clang/test/SemaCXX/warn-unsequenced.cpp b/clang/test/SemaCXX/warn-unsequenced.cpp
index 50dde8f3a5789..6019eb3929304 100644
--- a/clang/test/SemaCXX/warn-unsequenced.cpp
+++ b/clang/test/SemaCXX/warn-unsequenced.cpp
@@ -815,3 +815,29 @@ void test_var() {
}
} // namespace templates
+
+namespace muliple_read_volatile {
+ volatile int v1;
+
+ void PositiveTest(){
+ int x = 0;
+ int y = 0;
+ x = v1 + v1; // cxx11-warning {{unsequenced accesses to volatile qualified 'v1'}}
+ // cxx17-warning at -1 {{unsequenced accesses to volatile qualified 'v1'}}
+ v1 = v1 * v1; // cxx11-warning {{unsequenced accesses to volatile qualified 'v1'}}
+ // cxx17-warning at -1 {{unsequenced accesses to volatile qualified 'v1'}}
+ x = v1 + (y++, v1); // cxx11-warning {{unsequenced accesses to volatile qualified 'v1'}}
+ // cxx17-warning at -1 {{unsequenced accesses to volatile qualified 'v1'}}
+ x = v1 + y || y; // cxx11-warning {{unsequenced accesses to volatile qualified 'v1'}}
+ // cxx17-warning at -1 {{unsequenced accesses to volatile qualified 'v1'}}
+ }
+
+ void NegativeTest(){
+ int x = 0;
+ int y = 0;
+ x = v1 + y; // no-warning
+ v1 = v1 * y; // no-warning
+ x = (v1, v1); // no-warning
+ x = v1 || v1; // no-warning
+ }
+} // namespace volatiles
\ No newline at end of file
>From 4889b198e22806080f7b602b29526858135f4f02 Mon Sep 17 00:00:00 2001
From: Seraphimt <svet58585 at mail.ru>
Date: Wed, 11 Feb 2026 15:58:17 +0300
Subject: [PATCH 02/12] Minor fix.
---
clang/include/clang/Basic/DiagnosticSemaKinds.td | 2 ++
clang/test/SemaCXX/warn-unsequenced.cpp | 4 ++--
2 files changed, 4 insertions(+), 2 deletions(-)
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index f12677ac11600..defd168b2c629 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -2581,6 +2581,8 @@ def warn_unsequenced_mod_mod : Warning<
"multiple unsequenced modifications to %0">, InGroup<Unsequenced>;
def warn_unsequenced_mod_use : Warning<
"unsequenced modification and access to %0">, InGroup<Unsequenced>;
+def warn_unsequenced_use_use_volatile : Warning<
+ "unsequenced volatile accesses to %0">, InGroup<Unsequenced>;
def select_initialized_entity_kind : TextSubstitution<
"%select{copying variable|copying parameter|initializing template parameter|"
diff --git a/clang/test/SemaCXX/warn-unsequenced.cpp b/clang/test/SemaCXX/warn-unsequenced.cpp
index 6019eb3929304..3b4a8ec9380d0 100644
--- a/clang/test/SemaCXX/warn-unsequenced.cpp
+++ b/clang/test/SemaCXX/warn-unsequenced.cpp
@@ -828,7 +828,7 @@ namespace muliple_read_volatile {
// cxx17-warning at -1 {{unsequenced accesses to volatile qualified 'v1'}}
x = v1 + (y++, v1); // cxx11-warning {{unsequenced accesses to volatile qualified 'v1'}}
// cxx17-warning at -1 {{unsequenced accesses to volatile qualified 'v1'}}
- x = v1 + y || y; // cxx11-warning {{unsequenced accesses to volatile qualified 'v1'}}
+ x = v1 + v1 || y; // cxx11-warning {{unsequenced accesses to volatile qualified 'v1'}}
// cxx17-warning at -1 {{unsequenced accesses to volatile qualified 'v1'}}
}
@@ -840,4 +840,4 @@ namespace muliple_read_volatile {
x = (v1, v1); // no-warning
x = v1 || v1; // no-warning
}
-} // namespace volatiles
\ No newline at end of file
+} // namespace muliple_read_volatile
\ No newline at end of file
>From 204bd16181542dc2724cb9c1ab358d218de73d77 Mon Sep 17 00:00:00 2001
From: Seraphimt <svet58585 at mail.ru>
Date: Wed, 11 Feb 2026 20:47:24 +0300
Subject: [PATCH 03/12] Fix clang-format.
---
clang/lib/Sema/SemaChecking.cpp | 10 +++-------
1 file changed, 3 insertions(+), 7 deletions(-)
diff --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp
index b9d1ae9240923..4c2eaad980394 100644
--- a/clang/lib/Sema/SemaChecking.cpp
+++ b/clang/lib/Sema/SemaChecking.cpp
@@ -14034,11 +14034,7 @@ class SequenceChecker : public ConstEvaluatedExprVisitor<SequenceChecker> {
UK_Count = UK_ModAsSideEffect + 1
};
- enum WarningKind {
- WK_UseAndMod,
- WK_ModAndMod,
- WK_Volatile_UseAndUse
- };
+ enum WarningKind { WK_UseAndMod, WK_ModAndMod, WK_Volatile_UseAndUse };
/// Bundle together a sequencing region and the expression corresponding
/// to a specific usage. One Usage is stored for each usage kind in UsageInfo.
@@ -14203,7 +14199,7 @@ class SequenceChecker : public ConstEvaluatedExprVisitor<SequenceChecker> {
case WK_UseAndMod:
DiagID = diag::warn_unsequenced_mod_use;
break;
- case WK_ModAndMod:
+ case WK_ModAndMod:
DiagID = diag::warn_unsequenced_mod_mod;
break;
case WK_Volatile_UseAndUse:
@@ -14213,7 +14209,7 @@ class SequenceChecker : public ConstEvaluatedExprVisitor<SequenceChecker> {
SemaRef.DiagRuntimeBehavior(
Mod->getExprLoc(), {Mod, ModOrUse},
- SemaRef.PDiag(DiagID)<< O << SourceRange(ModOrUse->getExprLoc()));
+ SemaRef.PDiag(DiagID) << O << SourceRange(ModOrUse->getExprLoc()));
UI.Diagnosed = true;
}
>From 36f0b43070bfb20ed86f190f9ba4d5f3cf297fc2 Mon Sep 17 00:00:00 2001
From: Seraphimt <svet58585 at mail.ru>
Date: Thu, 12 Feb 2026 20:39:06 +0300
Subject: [PATCH 04/12] Add new line in end tests file.
---
clang/test/SemaCXX/warn-unsequenced.cpp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/clang/test/SemaCXX/warn-unsequenced.cpp b/clang/test/SemaCXX/warn-unsequenced.cpp
index 3b4a8ec9380d0..73b1e3718b211 100644
--- a/clang/test/SemaCXX/warn-unsequenced.cpp
+++ b/clang/test/SemaCXX/warn-unsequenced.cpp
@@ -840,4 +840,4 @@ namespace muliple_read_volatile {
x = (v1, v1); // no-warning
x = v1 || v1; // no-warning
}
-} // namespace muliple_read_volatile
\ No newline at end of file
+} // namespace muliple_read_volatile
>From 0fd7b2ed0e4715f642f760783368a46ee9a46ce0 Mon Sep 17 00:00:00 2001
From: Seraphimt <svet58585 at mail.ru>
Date: Fri, 13 Feb 2026 22:14:25 +0300
Subject: [PATCH 05/12] Fix message.
---
clang/include/clang/Basic/DiagnosticSemaKinds.td | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index defd168b2c629..90f455611536b 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -2582,7 +2582,7 @@ def warn_unsequenced_mod_mod : Warning<
def warn_unsequenced_mod_use : Warning<
"unsequenced modification and access to %0">, InGroup<Unsequenced>;
def warn_unsequenced_use_use_volatile : Warning<
- "unsequenced volatile accesses to %0">, InGroup<Unsequenced>;
+ "unsequenced accesses to volatile qualified %0">, InGroup<Unsequenced>;
def select_initialized_entity_kind : TextSubstitution<
"%select{copying variable|copying parameter|initializing template parameter|"
>From d8edb3d1f48db3da764da0d46791dd79e110befd Mon Sep 17 00:00:00 2001
From: Seraphimt <svet58585 at mail.ru>
Date: Sat, 14 Feb 2026 17:03:24 +0300
Subject: [PATCH 06/12] Add Wno-sequence-point in tests.
---
clang/test/CodeGen/PowerPC/builtins-ppc-fma.c | 2 +-
clang/test/CodeGen/SystemZ/builtins-systemz-i128.c | 2 +-
clang/test/CodeGen/SystemZ/builtins-systemz-vector.c | 2 +-
.../CodeGen/SystemZ/builtins-systemz-vector2-constrained.c | 2 +-
clang/test/CodeGen/SystemZ/builtins-systemz-vector2.c | 2 +-
clang/test/CodeGen/SystemZ/builtins-systemz-vector3.c | 2 +-
clang/test/CodeGen/SystemZ/builtins-systemz-vector4.c | 2 +-
clang/test/CodeGen/SystemZ/builtins-systemz-vector5.c | 2 +-
.../CodeGen/SystemZ/builtins-systemz-zvector-constrained.c | 4 ++--
clang/test/CodeGen/SystemZ/builtins-systemz-zvector-error.c | 2 +-
clang/test/CodeGen/SystemZ/builtins-systemz-zvector.c | 4 ++--
.../CodeGen/SystemZ/builtins-systemz-zvector2-constrained.c | 4 ++--
clang/test/CodeGen/SystemZ/builtins-systemz-zvector2.c | 4 ++--
clang/test/CodeGen/SystemZ/builtins-systemz-zvector3.c | 4 ++--
clang/test/CodeGen/SystemZ/builtins-systemz-zvector4.c | 4 ++--
clang/test/CodeGen/SystemZ/builtins-systemz.c | 4 ++--
16 files changed, 23 insertions(+), 23 deletions(-)
diff --git a/clang/test/CodeGen/PowerPC/builtins-ppc-fma.c b/clang/test/CodeGen/PowerPC/builtins-ppc-fma.c
index 61421fbf2fecd..4dd7295d8efe1 100644
--- a/clang/test/CodeGen/PowerPC/builtins-ppc-fma.c
+++ b/clang/test/CodeGen/PowerPC/builtins-ppc-fma.c
@@ -1,5 +1,5 @@
// RUN: %clang_cc1 -triple powerpc64le-gnu-linux \
-// RUN: -target-feature +vsx -Wall -Wno-unused -Werror -emit-llvm %s -o - | FileCheck \
+// RUN: -target-feature +vsx -Wall -Wno-sequence-point -Werror -emit-llvm %s -o - | FileCheck \
// RUN: %s
typedef __attribute__((vector_size(4 * sizeof(float)))) float vec_float;
diff --git a/clang/test/CodeGen/SystemZ/builtins-systemz-i128.c b/clang/test/CodeGen/SystemZ/builtins-systemz-i128.c
index 2a20607123af9..436d6a0039f9b 100644
--- a/clang/test/CodeGen/SystemZ/builtins-systemz-i128.c
+++ b/clang/test/CodeGen/SystemZ/builtins-systemz-i128.c
@@ -2,7 +2,7 @@
// REQUIRES: systemz-registered-target
// RUN: %clang_cc1 -target-cpu z14 -triple s390x-linux-gnu \
// RUN: -O2 -fzvector -flax-vector-conversions=none \
-// RUN: -Wall -Wno-unused -Werror -emit-llvm %s -o - | FileCheck %s
+// RUN: -Wall -Wno-unused -Wno-sequence-point -Werror -emit-llvm %s -o - | FileCheck %s
#include <vecintrin.h>
diff --git a/clang/test/CodeGen/SystemZ/builtins-systemz-vector.c b/clang/test/CodeGen/SystemZ/builtins-systemz-vector.c
index ae3b08a1b67eb..0cf7e3732b8c4 100644
--- a/clang/test/CodeGen/SystemZ/builtins-systemz-vector.c
+++ b/clang/test/CodeGen/SystemZ/builtins-systemz-vector.c
@@ -1,6 +1,6 @@
// REQUIRES: systemz-registered-target
// RUN: %clang_cc1 -target-cpu z13 -triple s390x-ibm-linux -flax-vector-conversions=none \
-// RUN: -Wall -Wno-unused -Werror -emit-llvm %s -o - | FileCheck %s
+// RUN: -Wall -Wno-sequence-point -Wno-unused -Werror -emit-llvm %s -o - | FileCheck %s
typedef __attribute__((vector_size(16))) signed char vec_schar;
typedef __attribute__((vector_size(16))) signed short vec_sshort;
diff --git a/clang/test/CodeGen/SystemZ/builtins-systemz-vector2-constrained.c b/clang/test/CodeGen/SystemZ/builtins-systemz-vector2-constrained.c
index 12c675041af76..682e6f613f44c 100644
--- a/clang/test/CodeGen/SystemZ/builtins-systemz-vector2-constrained.c
+++ b/clang/test/CodeGen/SystemZ/builtins-systemz-vector2-constrained.c
@@ -1,6 +1,6 @@
// REQUIRES: systemz-registered-target
// RUN: %clang_cc1 -target-cpu z14 -triple s390x-ibm-linux -flax-vector-conversions=none \
-// RUN: -ffp-exception-behavior=strict -Wall -Wno-unused -Werror -emit-llvm %s -o - | FileCheck %s
+// RUN: -ffp-exception-behavior=strict -Wall -Wno-sequence-point -Wno-unused -Werror -emit-llvm %s -o - | FileCheck %s
typedef __attribute__((vector_size(16))) double vec_double;
typedef __attribute__((vector_size(16))) float vec_float;
diff --git a/clang/test/CodeGen/SystemZ/builtins-systemz-vector2.c b/clang/test/CodeGen/SystemZ/builtins-systemz-vector2.c
index 8f9e164ebef8c..3af63f225afad 100644
--- a/clang/test/CodeGen/SystemZ/builtins-systemz-vector2.c
+++ b/clang/test/CodeGen/SystemZ/builtins-systemz-vector2.c
@@ -1,6 +1,6 @@
// REQUIRES: systemz-registered-target
// RUN: %clang_cc1 -target-cpu z14 -triple s390x-ibm-linux -flax-vector-conversions=none \
-// RUN: -Wall -Wno-unused -Werror -emit-llvm %s -o - | FileCheck %s
+// RUN: -Wall -Wno-sequence-point -Wno-unused -Werror -emit-llvm %s -o - | FileCheck %s
typedef __attribute__((vector_size(16))) signed char vec_schar;
typedef __attribute__((vector_size(16))) signed short vec_sshort;
diff --git a/clang/test/CodeGen/SystemZ/builtins-systemz-vector3.c b/clang/test/CodeGen/SystemZ/builtins-systemz-vector3.c
index e4af1c1e54b86..0a04affb9c445 100644
--- a/clang/test/CodeGen/SystemZ/builtins-systemz-vector3.c
+++ b/clang/test/CodeGen/SystemZ/builtins-systemz-vector3.c
@@ -1,6 +1,6 @@
// REQUIRES: systemz-registered-target
// RUN: %clang_cc1 -target-cpu z15 -triple s390x-ibm-linux -flax-vector-conversions=none \
-// RUN: -Wall -Wno-unused -Werror -emit-llvm %s -o - | FileCheck %s
+// RUN: -Wall -Wno-sequence-point -Wno-unused -Werror -emit-llvm %s -o - | FileCheck %s
typedef __attribute__((vector_size(16))) signed char vec_schar;
typedef __attribute__((vector_size(16))) signed short vec_sshort;
diff --git a/clang/test/CodeGen/SystemZ/builtins-systemz-vector4.c b/clang/test/CodeGen/SystemZ/builtins-systemz-vector4.c
index c5ce47cea2ea8..09ab10fac5116 100644
--- a/clang/test/CodeGen/SystemZ/builtins-systemz-vector4.c
+++ b/clang/test/CodeGen/SystemZ/builtins-systemz-vector4.c
@@ -1,6 +1,6 @@
// REQUIRES: systemz-registered-target
// RUN: %clang_cc1 -target-cpu z16 -triple s390x-ibm-linux -flax-vector-conversions=none \
-// RUN: -Wall -Wno-unused -Werror -emit-llvm %s -o - | FileCheck %s
+// RUN: -Wall -Wno-sequence-point -Wno-unused -Werror -emit-llvm %s -o - | FileCheck %s
typedef __attribute__((vector_size(16))) signed char vec_schar;
typedef __attribute__((vector_size(16))) signed short vec_sshort;
diff --git a/clang/test/CodeGen/SystemZ/builtins-systemz-vector5.c b/clang/test/CodeGen/SystemZ/builtins-systemz-vector5.c
index b765fa64b33d4..95fe321d606b1 100644
--- a/clang/test/CodeGen/SystemZ/builtins-systemz-vector5.c
+++ b/clang/test/CodeGen/SystemZ/builtins-systemz-vector5.c
@@ -1,6 +1,6 @@
// REQUIRES: systemz-registered-target
// RUN: %clang_cc1 -target-cpu z17 -triple s390x-ibm-linux -flax-vector-conversions=none \
-// RUN: -Wall -Wno-unused -Werror -emit-llvm %s -o - | FileCheck %s
+// RUN: -Wall -Wno-sequence-point -Wno-unused -Werror -emit-llvm %s -o - | FileCheck %s
typedef __attribute__((vector_size(16))) signed char vec_schar;
typedef __attribute__((vector_size(16))) signed short vec_sshort;
diff --git a/clang/test/CodeGen/SystemZ/builtins-systemz-zvector-constrained.c b/clang/test/CodeGen/SystemZ/builtins-systemz-zvector-constrained.c
index 4993df20df143..af750a24767c6 100644
--- a/clang/test/CodeGen/SystemZ/builtins-systemz-zvector-constrained.c
+++ b/clang/test/CodeGen/SystemZ/builtins-systemz-zvector-constrained.c
@@ -2,11 +2,11 @@
// RUN: %clang_cc1 -target-cpu z13 -triple s390x-linux-gnu \
// RUN: -O2 -fzvector -flax-vector-conversions=none \
// RUN: -ffp-exception-behavior=strict \
-// RUN: -Wall -Wno-unused -Werror -emit-llvm %s -o - | FileCheck %s
+// RUN: -Wall -Wno-sequence-point -Wno-unused -Werror -emit-llvm %s -o - | FileCheck %s
// RUN: %clang_cc1 -target-cpu z13 -triple s390x-linux-gnu \
// RUN: -O2 -fzvector -flax-vector-conversions=none \
// RUN: -ffp-exception-behavior=strict \
-// RUN: -Wall -Wno-unused -Werror -S %s -o - | FileCheck %s --check-prefix=CHECK-ASM
+// RUN: -Wall -Wno-sequence-point -Wno-unused -Werror -S %s -o - | FileCheck %s --check-prefix=CHECK-ASM
#include <vecintrin.h>
diff --git a/clang/test/CodeGen/SystemZ/builtins-systemz-zvector-error.c b/clang/test/CodeGen/SystemZ/builtins-systemz-zvector-error.c
index 2ec1d960aa5bb..d82cbf0c51b2b 100644
--- a/clang/test/CodeGen/SystemZ/builtins-systemz-zvector-error.c
+++ b/clang/test/CodeGen/SystemZ/builtins-systemz-zvector-error.c
@@ -1,7 +1,7 @@
// REQUIRES: systemz-registered-target
// RUN: %clang_cc1 -target-cpu z13 -triple s390x-linux-gnu \
// RUN: -fzvector -flax-vector-conversions=none \
-// RUN: -Wall -Wno-unused -Werror -fsyntax-only -verify %s
+// RUN: -Wall -Wno-sequence-point -Wno-unused -Werror -fsyntax-only -verify %s
#include <vecintrin.h>
diff --git a/clang/test/CodeGen/SystemZ/builtins-systemz-zvector.c b/clang/test/CodeGen/SystemZ/builtins-systemz-zvector.c
index 42298339733d4..9f1283724201c 100644
--- a/clang/test/CodeGen/SystemZ/builtins-systemz-zvector.c
+++ b/clang/test/CodeGen/SystemZ/builtins-systemz-zvector.c
@@ -1,10 +1,10 @@
// REQUIRES: systemz-registered-target
// RUN: %clang_cc1 -target-cpu z13 -triple s390x-linux-gnu \
// RUN: -O2 -fzvector -flax-vector-conversions=none \
-// RUN: -Wall -Wno-unused -Werror -emit-llvm %s -o - | FileCheck %s
+// RUN: -Wall -Wno-sequence-point -Wno-unused -Werror -emit-llvm %s -o - | FileCheck %s
// RUN: %clang_cc1 -target-cpu z13 -triple s390x-linux-gnu \
// RUN: -O2 -fzvector -flax-vector-conversions=none \
-// RUN: -Wall -Wno-unused -Werror -S %s -o - | FileCheck %s --check-prefix=CHECK-ASM
+// RUN: -Wall -Wno-sequence-point -Wno-unused -Werror -S %s -o - | FileCheck %s --check-prefix=CHECK-ASM
#include <vecintrin.h>
diff --git a/clang/test/CodeGen/SystemZ/builtins-systemz-zvector2-constrained.c b/clang/test/CodeGen/SystemZ/builtins-systemz-zvector2-constrained.c
index 25b3e0b68cd02..6dea4f6bf6600 100644
--- a/clang/test/CodeGen/SystemZ/builtins-systemz-zvector2-constrained.c
+++ b/clang/test/CodeGen/SystemZ/builtins-systemz-zvector2-constrained.c
@@ -2,11 +2,11 @@
// RUN: %clang_cc1 -target-cpu z14 -triple s390x-linux-gnu \
// RUN: -O2 -fzvector -flax-vector-conversions=none \
// RUN: -ffp-exception-behavior=strict \
-// RUN: -Wall -Wno-unused -Werror -emit-llvm %s -o - | FileCheck %s
+// RUN: -Wall -Wno-sequence-point -Wno-unused -Werror -emit-llvm %s -o - | FileCheck %s
// RUN: %clang_cc1 -target-cpu z14 -triple s390x-linux-gnu \
// RUN: -O2 -fzvector -flax-vector-conversions=none \
// RUN: -ffp-exception-behavior=strict \
-// RUN: -Wall -Wno-unused -Werror -S %s -o - | FileCheck %s --check-prefix=CHECK-ASM
+// RUN: -Wall -Wno-sequence-point -Wno-unused -Werror -S %s -o - | FileCheck %s --check-prefix=CHECK-ASM
#include <vecintrin.h>
diff --git a/clang/test/CodeGen/SystemZ/builtins-systemz-zvector2.c b/clang/test/CodeGen/SystemZ/builtins-systemz-zvector2.c
index c1ef178fcfaa9..aedd7f734edb8 100644
--- a/clang/test/CodeGen/SystemZ/builtins-systemz-zvector2.c
+++ b/clang/test/CodeGen/SystemZ/builtins-systemz-zvector2.c
@@ -1,10 +1,10 @@
// REQUIRES: systemz-registered-target
// RUN: %clang_cc1 -target-cpu z14 -triple s390x-linux-gnu \
// RUN: -O2 -fzvector -flax-vector-conversions=none \
-// RUN: -Wall -Wno-unused -Werror -emit-llvm %s -o - | FileCheck %s
+// RUN: -Wall -Wno-sequence-point -Wno-unused -Werror -emit-llvm %s -o - | FileCheck %s
// RUN: %clang_cc1 -target-cpu z14 -triple s390x-linux-gnu \
// RUN: -O2 -fzvector -flax-vector-conversions=none \
-// RUN: -Wall -Wno-unused -Werror -S %s -o - | FileCheck %s --check-prefix=CHECK-ASM
+// RUN: -Wall -Wno-sequence-point -Wno-unused -Werror -S %s -o - | FileCheck %s --check-prefix=CHECK-ASM
#include <vecintrin.h>
diff --git a/clang/test/CodeGen/SystemZ/builtins-systemz-zvector3.c b/clang/test/CodeGen/SystemZ/builtins-systemz-zvector3.c
index ce8b315127237..dd7ec19b2412f 100644
--- a/clang/test/CodeGen/SystemZ/builtins-systemz-zvector3.c
+++ b/clang/test/CodeGen/SystemZ/builtins-systemz-zvector3.c
@@ -1,10 +1,10 @@
// REQUIRES: systemz-registered-target
// RUN: %clang_cc1 -target-cpu z15 -triple s390x-linux-gnu \
// RUN: -O2 -fzvector -flax-vector-conversions=none \
-// RUN: -Wall -Wno-unused -Werror -emit-llvm %s -o - | FileCheck %s
+// RUN: -Wall -Wno-sequence-point -Wno-unused -Werror -emit-llvm %s -o - | FileCheck %s
// RUN: %clang_cc1 -target-cpu z15 -triple s390x-linux-gnu \
// RUN: -O2 -fzvector -flax-vector-conversions=none \
-// RUN: -Wall -Wno-unused -Werror -S %s -o - | FileCheck %s --check-prefix=CHECK-ASM
+// RUN: -Wall -Wno-sequence-point -Wno-unused -Werror -S %s -o - | FileCheck %s --check-prefix=CHECK-ASM
#include <vecintrin.h>
diff --git a/clang/test/CodeGen/SystemZ/builtins-systemz-zvector4.c b/clang/test/CodeGen/SystemZ/builtins-systemz-zvector4.c
index adc55927ce366..23685cdff8994 100644
--- a/clang/test/CodeGen/SystemZ/builtins-systemz-zvector4.c
+++ b/clang/test/CodeGen/SystemZ/builtins-systemz-zvector4.c
@@ -1,10 +1,10 @@
// REQUIRES: systemz-registered-target
// RUN: %clang_cc1 -target-cpu z16 -triple s390x-linux-gnu \
// RUN: -O2 -fzvector -flax-vector-conversions=none \
-// RUN: -Wall -Wno-unused -Werror -emit-llvm %s -o - | FileCheck %s
+// RUN: -Wall -Wno-sequence-point -Wno-unused -Werror -emit-llvm %s -o - | FileCheck %s
// RUN: %clang_cc1 -target-cpu z16 -triple s390x-linux-gnu \
// RUN: -O2 -fzvector -flax-vector-conversions=none \
-// RUN: -Wall -Wno-unused -Werror -S %s -o - | FileCheck %s --check-prefix=CHECK-ASM
+// RUN: -Wall -Wno-sequence-point -Wno-unused -Werror -S %s -o - | FileCheck %s --check-prefix=CHECK-ASM
#include <vecintrin.h>
diff --git a/clang/test/CodeGen/SystemZ/builtins-systemz.c b/clang/test/CodeGen/SystemZ/builtins-systemz.c
index 4848bef4b4aad..61c7cd934f7c4 100644
--- a/clang/test/CodeGen/SystemZ/builtins-systemz.c
+++ b/clang/test/CodeGen/SystemZ/builtins-systemz.c
@@ -1,6 +1,6 @@
// REQUIRES: systemz-registered-target
-// RUN: %clang_cc1 -target-cpu zEC12 -triple s390x-ibm-linux -Wall -Wno-unused -Werror -emit-llvm %s -o - | FileCheck %s
-// RUN: %clang_cc1 -target-cpu zEC12 -triple s390x-ibm-linux -Wall -Wno-unused -Werror -emit-llvm -x c++ %s -o - | FileCheck %s
+// RUN: %clang_cc1 -target-cpu zEC12 -triple s390x-ibm-linux -Wall -Wno-unused -Wno-sequence-point -Werror -emit-llvm %s -o - | FileCheck %s
+// RUN: %clang_cc1 -target-cpu zEC12 -triple s390x-ibm-linux -Wall -Wno-unused -Wno-sequence-point -Werror -emit-llvm -x c++ %s -o - | FileCheck %s
#include <stdint.h>
#include <htmintrin.h>
>From da20fc4992ed4912c071d76041dc7ee7a612d0f2 Mon Sep 17 00:00:00 2001
From: Seraphimt <svet58585 at mail.ru>
Date: Sat, 14 Feb 2026 18:14:56 +0300
Subject: [PATCH 07/12] Add Wno-sequence-point in tests x2.
---
clang/test/CodeGen/PowerPC/builtins-ppc-fma.c | 2 +-
.../CodeGen/SystemZ/builtins-systemz-vector-constrained.c | 2 +-
clang/test/CodeGen/SystemZ/builtins-systemz-zvector5.c | 4 ++--
3 files changed, 4 insertions(+), 4 deletions(-)
diff --git a/clang/test/CodeGen/PowerPC/builtins-ppc-fma.c b/clang/test/CodeGen/PowerPC/builtins-ppc-fma.c
index 4dd7295d8efe1..e26d7793ffb2b 100644
--- a/clang/test/CodeGen/PowerPC/builtins-ppc-fma.c
+++ b/clang/test/CodeGen/PowerPC/builtins-ppc-fma.c
@@ -1,5 +1,5 @@
// RUN: %clang_cc1 -triple powerpc64le-gnu-linux \
-// RUN: -target-feature +vsx -Wall -Wno-sequence-point -Werror -emit-llvm %s -o - | FileCheck \
+// RUN: -target-feature +vsx -Wall -Wno-sequence-point -Wno-unused -Werror -emit-llvm %s -o - | FileCheck \
// RUN: %s
typedef __attribute__((vector_size(4 * sizeof(float)))) float vec_float;
diff --git a/clang/test/CodeGen/SystemZ/builtins-systemz-vector-constrained.c b/clang/test/CodeGen/SystemZ/builtins-systemz-vector-constrained.c
index ff24ef9a091b7..0877067a1fdcd 100644
--- a/clang/test/CodeGen/SystemZ/builtins-systemz-vector-constrained.c
+++ b/clang/test/CodeGen/SystemZ/builtins-systemz-vector-constrained.c
@@ -1,6 +1,6 @@
// REQUIRES: systemz-registered-target
// RUN: %clang_cc1 -target-cpu z13 -triple s390x-ibm-linux -flax-vector-conversions=none \
-// RUN: -ffp-exception-behavior=strict -Wall -Wno-unused -Werror -emit-llvm %s -o - | FileCheck %s
+// RUN: -ffp-exception-behavior=strict -Wall -Wno-sequence-point -Wno-unused -Werror -emit-llvm %s -o - | FileCheck %s
typedef __attribute__((vector_size(16))) signed long long vec_slong;
typedef __attribute__((vector_size(16))) double vec_double;
diff --git a/clang/test/CodeGen/SystemZ/builtins-systemz-zvector5.c b/clang/test/CodeGen/SystemZ/builtins-systemz-zvector5.c
index cd0fafdb7435f..b354219e817f0 100644
--- a/clang/test/CodeGen/SystemZ/builtins-systemz-zvector5.c
+++ b/clang/test/CodeGen/SystemZ/builtins-systemz-zvector5.c
@@ -1,10 +1,10 @@
// REQUIRES: systemz-registered-target
// RUN: %clang_cc1 -target-cpu z17 -triple s390x-linux-gnu \
// RUN: -O2 -fzvector -flax-vector-conversions=none \
-// RUN: -Wall -Wno-unused -Werror -emit-llvm %s -o - | FileCheck %s
+// RUN: -Wall -Wno-sequence-point -Wno-unused -Werror -emit-llvm %s -o - | FileCheck %s
// RUN: %clang_cc1 -target-cpu z17 -triple s390x-linux-gnu \
// RUN: -O2 -fzvector -flax-vector-conversions=none \
-// RUN: -Wall -Wno-unused -Werror -S %s -o - | FileCheck %s --check-prefix=CHECK-ASM
+// RUN: -Wall -Wno-sequence-point -Wno-unused -Werror -S %s -o - | FileCheck %s --check-prefix=CHECK-ASM
#include <vecintrin.h>
>From 852668f1bc6dbf39d7137f510a8f864169664c3e Mon Sep 17 00:00:00 2001
From: Seraphimt <svet58585 at mail.ru>
Date: Thu, 20 Aug 2026 19:37:24 +0300
Subject: [PATCH 08/12] Corrections based on review
---
clang/docs/ReleaseNotes.rst | 854 +-
clang/lib/Sema/SemaChecking.cpp | 33921 +++++++++++-----------
clang/test/SemaCXX/warn-unsequenced.cpp | 1689 +-
3 files changed, 18239 insertions(+), 18225 deletions(-)
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 59fdbc80e8bed..d227c16c1f257 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -1,426 +1,428 @@
-.. If you want to modify sections/contents permanently, you should modify both
- ReleaseNotes.rst and ReleaseNotesTemplate.txt.
-
-===========================================
-Clang |release| |ReleaseNotesTitle|
-===========================================
-
-.. contents::
- :local:
- :depth: 2
-
-Written by the `LLVM Team <https://llvm.org/>`_
-
-.. only:: PreRelease
-
- .. warning::
- These are in-progress notes for the upcoming Clang |version| release.
- Release notes for previous releases can be found on
- `the Releases Page <https://llvm.org/releases/>`_.
-
-Introduction
-============
-
-This document contains the release notes for the Clang C/C++/Objective-C
-frontend, part of the LLVM Compiler Infrastructure, release |release|. Here we
-describe the status of Clang in some detail, including major
-improvements from the previous release and new feature work. For the
-general LLVM release notes, see `the LLVM
-documentation <https://llvm.org/docs/ReleaseNotes.html>`_. For the libc++ release notes,
-see `this page <https://libcxx.llvm.org/ReleaseNotes.html>`_. All LLVM releases
-may be downloaded from the `LLVM releases web site <https://llvm.org/releases/>`_.
-
-For more information about Clang or LLVM, including information about the
-latest release, please see the `Clang Web Site <https://clang.llvm.org>`_ or the
-`LLVM Web Site <https://llvm.org>`_.
-
-Potentially Breaking Changes
-============================
-
-C/C++ Language Potentially Breaking Changes
--------------------------------------------
-
-C++ Specific Potentially Breaking Changes
------------------------------------------
-
-- Clang now more aggressively optimizes away stores to objects after they are
- dead. This behavior can be disabled with ``-fno-lifetime-dse``.
-
-ABI Changes in This Version
----------------------------
-
-AST Dumping Potentially Breaking Changes
-----------------------------------------
-
-- The JSON AST dump now includes all fields from ``AvailabilityAttr``: ``platform``,
- ``introduced``, ``deprecated``, ``obsoleted``, ``unavailable``, ``message``,
- ``strict``, ``replacement``, ``priority``, and ``environment``. Previously, these
- fields were missing from the JSON output.
-
-Clang Frontend Potentially Breaking Changes
--------------------------------------------
-
-- HIPSPV toolchain: `--offload-targets=spirv{32,64}` option is
- deprecated and will be removed when the new offload driver becomes
- default. The replacement for the option is
- `--offload-targets=spirv{32,64}-unknown-chipstar` when using the new
- offload driver (`--offload-new-driver`).
-
-
-Clang Python Bindings Potentially Breaking Changes
---------------------------------------------------
-- Remove ``CompletionString.Availability``. No libclang interfaces returned instances of it.
-- ``CompletionString.availability`` now returns instances of ``CompletionString.AvailabilityKindCompat``.
-
- Instances of ``AvailabilityKindCompat`` have the same ``__str__`` representation
- as the previous ``CompletionChunk.Kind`` and are equality-comparable with
- the existing ``AvailabilityKind`` enum. It will be replaced by ``AvailabilityKind``
- in a future release. When this happens, the return type of ``CompletionString.availability``
- will change to ``AvailabilityKind``, so it is recommended to use ``AvailabilityKind``
- to compare with the return values of ``CompletionString.availability``.
-- Remove ``availabilityKinds``. In this release, uses of ``availabilityKinds``
- need to be replaced by ``CompletionString.AvailabilityKind``.
-- ``CompletionChunk.kind`` now returns instances of ``CompletionChunkKind``.
-
- Instances of ``CompletionChunkKind`` have the same ``__str__`` representation
- as the previous ``CompletionChunk.Kind`` for compatibility.
- These representations will be changed in a future release to match other enums.
-- Remove ``completionChunkKindMap``. In this release, uses of ``completionChunkKindMap``
- need to be replaced by ``CompletionChunkKind``.
-- Move ``SPELLING_CACHE`` into ``CompletionChunk`` and change it to use
- ``CompletionChunkKind`` instances as keys, instead of the enum values.
- An alias is kept in the form of a ``SPELLING_CACHE`` variable, but it only supports
- ``__getitem__`` and ``__contains__``. It will be removed in a future release.
- Please migrate to using ``CompletionChunk.SPELLING_CACHE`` instead.
-- ``SourceLocation`` and ``SourceRange`` now use ``NotImplemented`` to delegate
- equality checks (``__eq__``) to the other object they are compared with when
- they are of different classes. They previously returned ``False`` when compared
- with objects of other classes.
-
-What's New in Clang |release|?
-==============================
-
-C++ Language Changes
---------------------
-
-C++2c Feature Support
-^^^^^^^^^^^^^^^^^^^^^
-
-C++23 Feature Support
-^^^^^^^^^^^^^^^^^^^^^
-
-C++20 Feature Support
-^^^^^^^^^^^^^^^^^^^^^
-
-- Clang now supports `P1857R3 <https://wg21.link/p1857r3>`_ Modules Dependency Discovery. (#GH54047)
-
-C++17 Feature Support
-^^^^^^^^^^^^^^^^^^^^^
-
-Resolutions to C++ Defect Reports
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-C Language Changes
-------------------
-
-C2y Feature Support
-^^^^^^^^^^^^^^^^^^^
-
-C23 Feature Support
-^^^^^^^^^^^^^^^^^^^
-
-Non-comprehensive list of changes in this release
--------------------------------------------------
-
-- Added ``__builtin_stdc_rotate_left`` and ``__builtin_stdc_rotate_right``
- for bit rotation of unsigned integers including ``_BitInt`` types. Rotation
- counts are normalized modulo the bit-width and support negative values.
- Usable in constant expressions. Implicit conversion is supported for
- class/struct types with conversion operators.
-
-New Compiler Flags
-------------------
-- New option ``-fms-anonymous-structs`` / ``-fno-ms-anonymous-structs`` added
- to enable or disable Microsoft's anonymous struct/union extension without
- enabling other ``-fms-extensions`` features (#GH177607).
-
-Deprecated Compiler Flags
--------------------------
-
-Modified Compiler Flags
------------------------
-- The `-mno-outline` and `-moutline` compiler flags are now allowed on RISC-V and X86, which both support the machine outliner.
-
-Removed Compiler Flags
-----------------------
-
-Attribute Changes in Clang
---------------------------
-
-- Added new attribute ``stack_protector_ignore`` to opt specific local variables out of
- the analysis which determines if a function should get a stack protector. A function
- will still generate a stack protector if other local variables or command line flags
- require it.
-
-Improvements to Clang's diagnostics
------------------------------------
-- Added ``-Wlifetime-safety`` to enable lifetime safety analysis,
- a CFG-based intra-procedural analysis that detects use-after-free and related
- temporal safety bugs. See the
- `RFC <https://discourse.llvm.org/t/rfc-intra-procedural-lifetime-analysis-in-clang/86291>`_
- for more details. By design, this warning is enabled in ``-Weverything``. To disable
- the analysis, use ``-Wno-lifetime-safety`` or ``-fno-lifetime-safety``.
-
-- Added ``-Wlifetime-safety-suggestions`` to enable lifetime annotation suggestions.
- This provides suggestions for function parameters that
- should be marked ``[[clang::lifetimebound]]`` based on lifetime analysis. For
- example, for the following function:
-
- .. code-block:: c++
-
- int* p(int *in) { return in; }
-
- Clang will suggest:
-
- .. code-block:: c++
-
- warning: parameter in intra-TU function should be marked [[clang::lifetimebound]]
- int* p(int *in) { return in; }
- ^~~~~~~
- [[clang::lifetimebound]]
- note: param returned here
- int* p(int *in) { return in; }
- ^~
-
-- Added ``-Wlifetime-safety-noescape`` to detect misuse of ``[[clang::noescape]]``
- annotation where the parameter escapes through return. For example:
-
- .. code-block:: c++
-
- int* p(int *in [[clang::noescape]]) { return in; }
-
- Clang will warn:
-
- .. code-block:: c++
-
- warning: parameter is marked [[clang::noescape]] but escapes
- int* p(int *in [[clang::noescape]]) { return in; }
- ^~~~~~~
- note: returned here
- int* p(int *in [[clang::noescape]]) { return in; }
- ^~
-
-- Added ``-Wlifetime-safety-dangling-field`` to detect dangling field references
- when stack memory escapes to class fields. This is part of ``-Wlifetime-safety``
- and detects cases where local variables or parameters are stored in fields but
- outlive their scope. For example:
-
- .. code-block:: c++
-
- struct DanglingView {
- std::string_view view;
- DanglingView(std::string s) : view(s) {} // warning: address of stack memory escapes to a field
- };
-
-- Improved ``-Wassign-enum`` performance by caching enum enumerator values. (#GH176454)
-
-- Fixed a false negative in ``-Warray-bounds`` where the warning was suppressed
- when accessing a member function on a past-the-end array element.
- (#GH179128)
-
-Improvements to Clang's time-trace
-----------------------------------
-
-Improvements to Coverage Mapping
---------------------------------
-
-- [MC/DC] Nested expressions are handled as individual MC/DC expressions.
-- "Single byte coverage" now supports branch coverage and can be used
- together with ``-fcoverage-mcdc``.
-
-Bug Fixes in This Version
--------------------------
-
-- Fixed atomic boolean compound assignment; the conversion back to atomic bool would be miscompiled. (#GH33210)
-
-- Fixed a failed assertion in the preprocessor when ``__has_embed`` parameters are missing parentheses. (#GH175088)
-
-- Fix lifetime extension of temporaries in for-range-initializers in templates. (#GH165182)
-- Fixed a preprocessor crash in ``__has_cpp_attribute`` on incomplete scoped attributes. (#GH178098)
-- Fixes an assertion failure when evaluating ``__underlying_type`` on enum redeclarations. (#GH177943)
-
-- Clang now outputs relative paths of embeds for dependency output. (#GH161950)
-
-Bug Fixes to Compiler Builtins
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-Bug Fixes to Attribute Support
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-- Fixed a behavioral discrepancy between deleted functions and private members when checking the ``enable_if`` attribute. (#GH175895)
-
-Bug Fixes to C++ Support
-^^^^^^^^^^^^^^^^^^^^^^^^
-- Fixed a crash when instantiating ``requires`` expressions involving substitution failures in C++ concepts. (#GH176402)
-- Fixed a crash when a default argument is passed to an explicit object parameter. (#GH176639)
-- Fixed a crash when diagnosing an invalid static member function with an explicit object parameter (#GH177741)
-
-Bug Fixes to AST Handling
-^^^^^^^^^^^^^^^^^^^^^^^^^
-- Fixed a bug where explicit nullability property attributes were not stored in AST nodes in Objective-C. (#GH179703)
-
-Miscellaneous Bug Fixes
-^^^^^^^^^^^^^^^^^^^^^^^
-- Fixed the arguments of the format attribute on ``__builtin_os_log_format``. Previously, they were off by 1.
-
-Miscellaneous Clang Crashes Fixed
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-- Fixed a crash when attempting to jump over initialization of a variable with variably modified type. (#GH175540)
-- Fixed a crash when using loop hint with a value dependent argument inside a
- generic lambda. (#GH172289)
-- Fixed a crash in C++ overload resolution with ``_Atomic``-qualified argument types. (#GH170433)
-
-OpenACC Specific Changes
-------------------------
-
-Target Specific Changes
------------------------
-
-AMDGPU Support
-^^^^^^^^^^^^^^
-
-- Initial support for gfx1310
-
-NVPTX Support
-^^^^^^^^^^^^^^
-
-X86 Support
-^^^^^^^^^^^
-- ``march=znver6`` is now supported.
-
-Arm and AArch64 Support
-^^^^^^^^^^^^^^^^^^^^^^^
-
-Android Support
-^^^^^^^^^^^^^^^
-
-Windows Support
-^^^^^^^^^^^^^^^
-
-LoongArch Support
-^^^^^^^^^^^^^^^^^
-
-- DWARF fission is now compatible with linker relaxations, allowing `-gsplit-dwarf` and `-mrelax`
- to be used together when building for the LoongArch platform.
-
-RISC-V Support
-^^^^^^^^^^^^^^
-
-- Tenstorrent Ascalon D8 was renamed to Ascalon X. Use `tt-ascalon-x` with `-mcpu` or `-mtune`.
-
-CUDA/HIP Language Changes
-^^^^^^^^^^^^^^^^^^^^^^^^^
-
-CUDA Support
-^^^^^^^^^^^^
-
-AIX Support
-^^^^^^^^^^^
-
-NetBSD Support
-^^^^^^^^^^^^^^
-
-WebAssembly Support
-^^^^^^^^^^^^^^^^^^^
-
-- Fixed a crash when ``__funcref`` is applied to a non-function pointer type.
- (#GH118233)
-
-AVR Support
-^^^^^^^^^^^
-
-SystemZ Support
-^^^^^^^^^^^^^^^
-
-- Add support for `#pragma export` for z/OS. This is a pragma used to export functions and variables
- with external linkage from shared libraries. It provides compatibility with the IBM XL C/C++
- compiler.
-
-DWARF Support in Clang
-----------------------
-
-Floating Point Support in Clang
--------------------------------
-
-Fixed Point Support in Clang
-----------------------------
-
-AST Matchers
-------------
-- Add ``functionTypeLoc`` matcher for matching ``FunctionTypeLoc``.
-
-clang-format
-------------
-- Add ``ObjCSpaceAfterMethodDeclarationPrefix`` option to control space between the
- '-'/'+' and the return type in Objective-C method declarations
-
-libclang
---------
-
-Code Completion
----------------
-
-Static Analyzer
----------------
-
-.. comment:
- This is for the Static Analyzer.
- Using the caret `^^^` underlining for subsections:
- - Crash and bug fixes
- - New checkers and features
- - Improvements
- - Moved checkers
-
-.. _release-notes-sanitizers:
-
-Sanitizers
-----------
-
-Python Binding Changes
-----------------------
-- Add deprecation warnings to ``CompletionChunk.isKind...`` methods.
- These will be removed in a future release. Existing uses should be adapted
- to directly compare equality of the ``CompletionChunk`` kind with
- the corresponding ``CompletionChunkKind`` variant.
-
- Affected methods: ``isKindOptional``, ``isKindTypedText``, ``isKindPlaceHolder``,
- ``isKindInformative`` and ``isKindResultType``.
-- Add a deprecation warning to ``CodeCompletionResults.results``.
- This property will become an implementation detail with changed behavior in a
- future release and should not be used directly.. Existing uses of
- ``CodeCompletionResults.results`` should be changed to directly use
- ``CodeCompletionResults``: it nows supports ``__len__`` and ``__getitem__``,
- so it can be used the same as ``CodeCompletionResults.results``.
-
-OpenMP Support
---------------
-- Added support for ``transparent`` clause in task and taskloop directives.
-- Added support for ``use_device_ptr`` clause to accept an optional
- ``fallback`` modifier (``fb_nullify`` or ``fb_preserve``) with OpenMP >= 61.
-
-Improvements
-^^^^^^^^^^^^
-
-Additional Information
-======================
-
-A wide variety of additional information is available on the `Clang web
-page <https://clang.llvm.org/>`_. The web page contains versions of the
-API documentation which are up-to-date with the Git version of
-the source code. You can access versions of these documents specific to
-this release by going into the "``clang/docs/``" directory in the Clang
-tree.
-
-If you have any questions or comments about Clang, please feel free to
-contact us on the `Discourse forums (Clang Frontend category)
-<https://discourse.llvm.org/c/clang/6>`_.
+.. If you want to modify sections/contents permanently, you should modify both
+ ReleaseNotes.rst and ReleaseNotesTemplate.txt.
+
+===========================================
+Clang |release| |ReleaseNotesTitle|
+===========================================
+
+.. contents::
+ :local:
+ :depth: 2
+
+Written by the `LLVM Team <https://llvm.org/>`_
+
+.. only:: PreRelease
+
+ .. warning::
+ These are in-progress notes for the upcoming Clang |version| release.
+ Release notes for previous releases can be found on
+ `the Releases Page <https://llvm.org/releases/>`_.
+
+Introduction
+============
+
+This document contains the release notes for the Clang C/C++/Objective-C
+frontend, part of the LLVM Compiler Infrastructure, release |release|. Here we
+describe the status of Clang in some detail, including major
+improvements from the previous release and new feature work. For the
+general LLVM release notes, see `the LLVM
+documentation <https://llvm.org/docs/ReleaseNotes.html>`_. For the libc++ release notes,
+see `this page <https://libcxx.llvm.org/ReleaseNotes.html>`_. All LLVM releases
+may be downloaded from the `LLVM releases web site <https://llvm.org/releases/>`_.
+
+For more information about Clang or LLVM, including information about the
+latest release, please see the `Clang Web Site <https://clang.llvm.org>`_ or the
+`LLVM Web Site <https://llvm.org>`_.
+
+Potentially Breaking Changes
+============================
+
+C/C++ Language Potentially Breaking Changes
+-------------------------------------------
+
+C++ Specific Potentially Breaking Changes
+-----------------------------------------
+
+- Clang now more aggressively optimizes away stores to objects after they are
+ dead. This behavior can be disabled with ``-fno-lifetime-dse``.
+
+ABI Changes in This Version
+---------------------------
+
+AST Dumping Potentially Breaking Changes
+----------------------------------------
+
+- The JSON AST dump now includes all fields from ``AvailabilityAttr``: ``platform``,
+ ``introduced``, ``deprecated``, ``obsoleted``, ``unavailable``, ``message``,
+ ``strict``, ``replacement``, ``priority``, and ``environment``. Previously, these
+ fields were missing from the JSON output.
+
+Clang Frontend Potentially Breaking Changes
+-------------------------------------------
+
+- HIPSPV toolchain: `--offload-targets=spirv{32,64}` option is
+ deprecated and will be removed when the new offload driver becomes
+ default. The replacement for the option is
+ `--offload-targets=spirv{32,64}-unknown-chipstar` when using the new
+ offload driver (`--offload-new-driver`).
+
+
+Clang Python Bindings Potentially Breaking Changes
+--------------------------------------------------
+- Remove ``CompletionString.Availability``. No libclang interfaces returned instances of it.
+- ``CompletionString.availability`` now returns instances of ``CompletionString.AvailabilityKindCompat``.
+
+ Instances of ``AvailabilityKindCompat`` have the same ``__str__`` representation
+ as the previous ``CompletionChunk.Kind`` and are equality-comparable with
+ the existing ``AvailabilityKind`` enum. It will be replaced by ``AvailabilityKind``
+ in a future release. When this happens, the return type of ``CompletionString.availability``
+ will change to ``AvailabilityKind``, so it is recommended to use ``AvailabilityKind``
+ to compare with the return values of ``CompletionString.availability``.
+- Remove ``availabilityKinds``. In this release, uses of ``availabilityKinds``
+ need to be replaced by ``CompletionString.AvailabilityKind``.
+- ``CompletionChunk.kind`` now returns instances of ``CompletionChunkKind``.
+
+ Instances of ``CompletionChunkKind`` have the same ``__str__`` representation
+ as the previous ``CompletionChunk.Kind`` for compatibility.
+ These representations will be changed in a future release to match other enums.
+- Remove ``completionChunkKindMap``. In this release, uses of ``completionChunkKindMap``
+ need to be replaced by ``CompletionChunkKind``.
+- Move ``SPELLING_CACHE`` into ``CompletionChunk`` and change it to use
+ ``CompletionChunkKind`` instances as keys, instead of the enum values.
+ An alias is kept in the form of a ``SPELLING_CACHE`` variable, but it only supports
+ ``__getitem__`` and ``__contains__``. It will be removed in a future release.
+ Please migrate to using ``CompletionChunk.SPELLING_CACHE`` instead.
+- ``SourceLocation`` and ``SourceRange`` now use ``NotImplemented`` to delegate
+ equality checks (``__eq__``) to the other object they are compared with when
+ they are of different classes. They previously returned ``False`` when compared
+ with objects of other classes.
+
+What's New in Clang |release|?
+==============================
+
+C++ Language Changes
+--------------------
+
+C++2c Feature Support
+^^^^^^^^^^^^^^^^^^^^^
+
+C++23 Feature Support
+^^^^^^^^^^^^^^^^^^^^^
+
+C++20 Feature Support
+^^^^^^^^^^^^^^^^^^^^^
+
+- Clang now supports `P1857R3 <https://wg21.link/p1857r3>`_ Modules Dependency Discovery. (#GH54047)
+
+C++17 Feature Support
+^^^^^^^^^^^^^^^^^^^^^
+
+Resolutions to C++ Defect Reports
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+C Language Changes
+------------------
+
+C2y Feature Support
+^^^^^^^^^^^^^^^^^^^
+
+C23 Feature Support
+^^^^^^^^^^^^^^^^^^^
+
+Non-comprehensive list of changes in this release
+-------------------------------------------------
+
+- Added ``__builtin_stdc_rotate_left`` and ``__builtin_stdc_rotate_right``
+ for bit rotation of unsigned integers including ``_BitInt`` types. Rotation
+ counts are normalized modulo the bit-width and support negative values.
+ Usable in constant expressions. Implicit conversion is supported for
+ class/struct types with conversion operators.
+
+New Compiler Flags
+------------------
+- New option ``-fms-anonymous-structs`` / ``-fno-ms-anonymous-structs`` added
+ to enable or disable Microsoft's anonymous struct/union extension without
+ enabling other ``-fms-extensions`` features (#GH177607).
+
+Deprecated Compiler Flags
+-------------------------
+
+Modified Compiler Flags
+-----------------------
+- The `-mno-outline` and `-moutline` compiler flags are now allowed on RISC-V and X86, which both support the machine outliner.
+
+Removed Compiler Flags
+----------------------
+
+Attribute Changes in Clang
+--------------------------
+
+- Added new attribute ``stack_protector_ignore`` to opt specific local variables out of
+ the analysis which determines if a function should get a stack protector. A function
+ will still generate a stack protector if other local variables or command line flags
+ require it.
+
+Improvements to Clang's diagnostics
+-----------------------------------
+- Added ``-Wlifetime-safety`` to enable lifetime safety analysis,
+ a CFG-based intra-procedural analysis that detects use-after-free and related
+ temporal safety bugs. See the
+ `RFC <https://discourse.llvm.org/t/rfc-intra-procedural-lifetime-analysis-in-clang/86291>`_
+ for more details. By design, this warning is enabled in ``-Weverything``. To disable
+ the analysis, use ``-Wno-lifetime-safety`` or ``-fno-lifetime-safety``.
+
+- Added ``-Wlifetime-safety-suggestions`` to enable lifetime annotation suggestions.
+ This provides suggestions for function parameters that
+ should be marked ``[[clang::lifetimebound]]`` based on lifetime analysis. For
+ example, for the following function:
+
+ .. code-block:: c++
+
+ int* p(int *in) { return in; }
+
+ Clang will suggest:
+
+ .. code-block:: c++
+
+ warning: parameter in intra-TU function should be marked [[clang::lifetimebound]]
+ int* p(int *in) { return in; }
+ ^~~~~~~
+ [[clang::lifetimebound]]
+ note: param returned here
+ int* p(int *in) { return in; }
+ ^~
+
+- Added ``-Wlifetime-safety-noescape`` to detect misuse of ``[[clang::noescape]]``
+ annotation where the parameter escapes through return. For example:
+
+ .. code-block:: c++
+
+ int* p(int *in [[clang::noescape]]) { return in; }
+
+ Clang will warn:
+
+ .. code-block:: c++
+
+ warning: parameter is marked [[clang::noescape]] but escapes
+ int* p(int *in [[clang::noescape]]) { return in; }
+ ^~~~~~~
+ note: returned here
+ int* p(int *in [[clang::noescape]]) { return in; }
+ ^~
+
+- Added ``-Wlifetime-safety-dangling-field`` to detect dangling field references
+ when stack memory escapes to class fields. This is part of ``-Wlifetime-safety``
+ and detects cases where local variables or parameters are stored in fields but
+ outlive their scope. For example:
+
+ .. code-block:: c++
+
+ struct DanglingView {
+ std::string_view view;
+ DanglingView(std::string s) : view(s) {} // warning: address of stack memory escapes to a field
+ };
+
+- Improved ``-Wassign-enum`` performance by caching enum enumerator values. (#GH176454)
+
+- Fixed a false negative in ``-Warray-bounds`` where the warning was suppressed
+ when accessing a member function on a past-the-end array element.
+ (#GH179128)
+
+- Improved ``-Wunsequenced`` by taking into account the volatile qualifier.
+
+Improvements to Clang's time-trace
+----------------------------------
+
+Improvements to Coverage Mapping
+--------------------------------
+
+- [MC/DC] Nested expressions are handled as individual MC/DC expressions.
+- "Single byte coverage" now supports branch coverage and can be used
+ together with ``-fcoverage-mcdc``.
+
+Bug Fixes in This Version
+-------------------------
+
+- Fixed atomic boolean compound assignment; the conversion back to atomic bool would be miscompiled. (#GH33210)
+
+- Fixed a failed assertion in the preprocessor when ``__has_embed`` parameters are missing parentheses. (#GH175088)
+
+- Fix lifetime extension of temporaries in for-range-initializers in templates. (#GH165182)
+- Fixed a preprocessor crash in ``__has_cpp_attribute`` on incomplete scoped attributes. (#GH178098)
+- Fixes an assertion failure when evaluating ``__underlying_type`` on enum redeclarations. (#GH177943)
+
+- Clang now outputs relative paths of embeds for dependency output. (#GH161950)
+
+Bug Fixes to Compiler Builtins
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Bug Fixes to Attribute Support
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+- Fixed a behavioral discrepancy between deleted functions and private members when checking the ``enable_if`` attribute. (#GH175895)
+
+Bug Fixes to C++ Support
+^^^^^^^^^^^^^^^^^^^^^^^^
+- Fixed a crash when instantiating ``requires`` expressions involving substitution failures in C++ concepts. (#GH176402)
+- Fixed a crash when a default argument is passed to an explicit object parameter. (#GH176639)
+- Fixed a crash when diagnosing an invalid static member function with an explicit object parameter (#GH177741)
+
+Bug Fixes to AST Handling
+^^^^^^^^^^^^^^^^^^^^^^^^^
+- Fixed a bug where explicit nullability property attributes were not stored in AST nodes in Objective-C. (#GH179703)
+
+Miscellaneous Bug Fixes
+^^^^^^^^^^^^^^^^^^^^^^^
+- Fixed the arguments of the format attribute on ``__builtin_os_log_format``. Previously, they were off by 1.
+
+Miscellaneous Clang Crashes Fixed
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+- Fixed a crash when attempting to jump over initialization of a variable with variably modified type. (#GH175540)
+- Fixed a crash when using loop hint with a value dependent argument inside a
+ generic lambda. (#GH172289)
+- Fixed a crash in C++ overload resolution with ``_Atomic``-qualified argument types. (#GH170433)
+
+OpenACC Specific Changes
+------------------------
+
+Target Specific Changes
+-----------------------
+
+AMDGPU Support
+^^^^^^^^^^^^^^
+
+- Initial support for gfx1310
+
+NVPTX Support
+^^^^^^^^^^^^^^
+
+X86 Support
+^^^^^^^^^^^
+- ``march=znver6`` is now supported.
+
+Arm and AArch64 Support
+^^^^^^^^^^^^^^^^^^^^^^^
+
+Android Support
+^^^^^^^^^^^^^^^
+
+Windows Support
+^^^^^^^^^^^^^^^
+
+LoongArch Support
+^^^^^^^^^^^^^^^^^
+
+- DWARF fission is now compatible with linker relaxations, allowing `-gsplit-dwarf` and `-mrelax`
+ to be used together when building for the LoongArch platform.
+
+RISC-V Support
+^^^^^^^^^^^^^^
+
+- Tenstorrent Ascalon D8 was renamed to Ascalon X. Use `tt-ascalon-x` with `-mcpu` or `-mtune`.
+
+CUDA/HIP Language Changes
+^^^^^^^^^^^^^^^^^^^^^^^^^
+
+CUDA Support
+^^^^^^^^^^^^
+
+AIX Support
+^^^^^^^^^^^
+
+NetBSD Support
+^^^^^^^^^^^^^^
+
+WebAssembly Support
+^^^^^^^^^^^^^^^^^^^
+
+- Fixed a crash when ``__funcref`` is applied to a non-function pointer type.
+ (#GH118233)
+
+AVR Support
+^^^^^^^^^^^
+
+SystemZ Support
+^^^^^^^^^^^^^^^
+
+- Add support for `#pragma export` for z/OS. This is a pragma used to export functions and variables
+ with external linkage from shared libraries. It provides compatibility with the IBM XL C/C++
+ compiler.
+
+DWARF Support in Clang
+----------------------
+
+Floating Point Support in Clang
+-------------------------------
+
+Fixed Point Support in Clang
+----------------------------
+
+AST Matchers
+------------
+- Add ``functionTypeLoc`` matcher for matching ``FunctionTypeLoc``.
+
+clang-format
+------------
+- Add ``ObjCSpaceAfterMethodDeclarationPrefix`` option to control space between the
+ '-'/'+' and the return type in Objective-C method declarations
+
+libclang
+--------
+
+Code Completion
+---------------
+
+Static Analyzer
+---------------
+
+.. comment:
+ This is for the Static Analyzer.
+ Using the caret `^^^` underlining for subsections:
+ - Crash and bug fixes
+ - New checkers and features
+ - Improvements
+ - Moved checkers
+
+.. _release-notes-sanitizers:
+
+Sanitizers
+----------
+
+Python Binding Changes
+----------------------
+- Add deprecation warnings to ``CompletionChunk.isKind...`` methods.
+ These will be removed in a future release. Existing uses should be adapted
+ to directly compare equality of the ``CompletionChunk`` kind with
+ the corresponding ``CompletionChunkKind`` variant.
+
+ Affected methods: ``isKindOptional``, ``isKindTypedText``, ``isKindPlaceHolder``,
+ ``isKindInformative`` and ``isKindResultType``.
+- Add a deprecation warning to ``CodeCompletionResults.results``.
+ This property will become an implementation detail with changed behavior in a
+ future release and should not be used directly.. Existing uses of
+ ``CodeCompletionResults.results`` should be changed to directly use
+ ``CodeCompletionResults``: it nows supports ``__len__`` and ``__getitem__``,
+ so it can be used the same as ``CodeCompletionResults.results``.
+
+OpenMP Support
+--------------
+- Added support for ``transparent`` clause in task and taskloop directives.
+- Added support for ``use_device_ptr`` clause to accept an optional
+ ``fallback`` modifier (``fb_nullify`` or ``fb_preserve``) with OpenMP >= 61.
+
+Improvements
+^^^^^^^^^^^^
+
+Additional Information
+======================
+
+A wide variety of additional information is available on the `Clang web
+page <https://clang.llvm.org/>`_. The web page contains versions of the
+API documentation which are up-to-date with the Git version of
+the source code. You can access versions of these documents specific to
+this release by going into the "``clang/docs/``" directory in the Clang
+tree.
+
+If you have any questions or comments about Clang, please feel free to
+contact us on the `Discourse forums (Clang Frontend category)
+<https://discourse.llvm.org/c/clang/6>`_.
diff --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp
index 4c2eaad980394..618f678728287 100644
--- a/clang/lib/Sema/SemaChecking.cpp
+++ b/clang/lib/Sema/SemaChecking.cpp
@@ -1,16956 +1,16965 @@
-//===- SemaChecking.cpp - Extra Semantic Checking -------------------------===//
-//
-// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
-// See https://llvm.org/LICENSE.txt for license information.
-// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
-//
-//===----------------------------------------------------------------------===//
-//
-// This file implements extra semantic analysis beyond what is enforced
-// by the C type system.
-//
-//===----------------------------------------------------------------------===//
-
-#include "CheckExprLifetime.h"
-#include "clang/AST/APValue.h"
-#include "clang/AST/ASTContext.h"
-#include "clang/AST/ASTDiagnostic.h"
-#include "clang/AST/Attr.h"
-#include "clang/AST/AttrIterator.h"
-#include "clang/AST/CharUnits.h"
-#include "clang/AST/Decl.h"
-#include "clang/AST/DeclBase.h"
-#include "clang/AST/DeclCXX.h"
-#include "clang/AST/DeclObjC.h"
-#include "clang/AST/DeclarationName.h"
-#include "clang/AST/EvaluatedExprVisitor.h"
-#include "clang/AST/Expr.h"
-#include "clang/AST/ExprCXX.h"
-#include "clang/AST/ExprObjC.h"
-#include "clang/AST/FormatString.h"
-#include "clang/AST/IgnoreExpr.h"
-#include "clang/AST/NSAPI.h"
-#include "clang/AST/NonTrivialTypeVisitor.h"
-#include "clang/AST/OperationKinds.h"
-#include "clang/AST/RecordLayout.h"
-#include "clang/AST/Stmt.h"
-#include "clang/AST/TemplateBase.h"
-#include "clang/AST/TemplateName.h"
-#include "clang/AST/Type.h"
-#include "clang/AST/TypeBase.h"
-#include "clang/AST/TypeLoc.h"
-#include "clang/AST/UnresolvedSet.h"
-#include "clang/Basic/AddressSpaces.h"
-#include "clang/Basic/Diagnostic.h"
-#include "clang/Basic/DiagnosticSema.h"
-#include "clang/Basic/IdentifierTable.h"
-#include "clang/Basic/LLVM.h"
-#include "clang/Basic/LangOptions.h"
-#include "clang/Basic/OpenCLOptions.h"
-#include "clang/Basic/OperatorKinds.h"
-#include "clang/Basic/PartialDiagnostic.h"
-#include "clang/Basic/SourceLocation.h"
-#include "clang/Basic/SourceManager.h"
-#include "clang/Basic/Specifiers.h"
-#include "clang/Basic/SyncScope.h"
-#include "clang/Basic/TargetInfo.h"
-#include "clang/Basic/TypeTraits.h"
-#include "clang/Lex/Lexer.h" // TODO: Extract static functions to fix layering.
-#include "clang/Sema/Initialization.h"
-#include "clang/Sema/Lookup.h"
-#include "clang/Sema/Ownership.h"
-#include "clang/Sema/Scope.h"
-#include "clang/Sema/ScopeInfo.h"
-#include "clang/Sema/Sema.h"
-#include "clang/Sema/SemaAMDGPU.h"
-#include "clang/Sema/SemaARM.h"
-#include "clang/Sema/SemaBPF.h"
-#include "clang/Sema/SemaDirectX.h"
-#include "clang/Sema/SemaHLSL.h"
-#include "clang/Sema/SemaHexagon.h"
-#include "clang/Sema/SemaLoongArch.h"
-#include "clang/Sema/SemaMIPS.h"
-#include "clang/Sema/SemaNVPTX.h"
-#include "clang/Sema/SemaObjC.h"
-#include "clang/Sema/SemaOpenCL.h"
-#include "clang/Sema/SemaPPC.h"
-#include "clang/Sema/SemaRISCV.h"
-#include "clang/Sema/SemaSPIRV.h"
-#include "clang/Sema/SemaSystemZ.h"
-#include "clang/Sema/SemaWasm.h"
-#include "clang/Sema/SemaX86.h"
-#include "llvm/ADT/APFloat.h"
-#include "llvm/ADT/APInt.h"
-#include "llvm/ADT/APSInt.h"
-#include "llvm/ADT/ArrayRef.h"
-#include "llvm/ADT/DenseMap.h"
-#include "llvm/ADT/FoldingSet.h"
-#include "llvm/ADT/STLExtras.h"
-#include "llvm/ADT/STLForwardCompat.h"
-#include "llvm/ADT/SmallBitVector.h"
-#include "llvm/ADT/SmallPtrSet.h"
-#include "llvm/ADT/SmallString.h"
-#include "llvm/ADT/SmallVector.h"
-#include "llvm/ADT/StringExtras.h"
-#include "llvm/ADT/StringRef.h"
-#include "llvm/ADT/StringSet.h"
-#include "llvm/ADT/StringSwitch.h"
-#include "llvm/Support/AtomicOrdering.h"
-#include "llvm/Support/Compiler.h"
-#include "llvm/Support/ConvertUTF.h"
-#include "llvm/Support/ErrorHandling.h"
-#include "llvm/Support/Format.h"
-#include "llvm/Support/Locale.h"
-#include "llvm/Support/MathExtras.h"
-#include "llvm/Support/SaveAndRestore.h"
-#include "llvm/Support/raw_ostream.h"
-#include "llvm/TargetParser/RISCVTargetParser.h"
-#include "llvm/TargetParser/Triple.h"
-#include <algorithm>
-#include <cassert>
-#include <cctype>
-#include <cstddef>
-#include <cstdint>
-#include <functional>
-#include <limits>
-#include <optional>
-#include <string>
-#include <tuple>
-#include <utility>
-
-using namespace clang;
-using namespace sema;
-
-SourceLocation Sema::getLocationOfStringLiteralByte(const StringLiteral *SL,
- unsigned ByteNo) const {
- return SL->getLocationOfByte(ByteNo, getSourceManager(), LangOpts,
- Context.getTargetInfo());
-}
-
-static constexpr unsigned short combineFAPK(Sema::FormatArgumentPassingKind A,
- Sema::FormatArgumentPassingKind B) {
- return (A << 8) | B;
-}
-
-bool Sema::checkArgCountAtLeast(CallExpr *Call, unsigned MinArgCount) {
- unsigned ArgCount = Call->getNumArgs();
- if (ArgCount >= MinArgCount)
- return false;
-
- return Diag(Call->getEndLoc(), diag::err_typecheck_call_too_few_args)
- << 0 /*function call*/ << MinArgCount << ArgCount
- << /*is non object*/ 0 << Call->getSourceRange();
-}
-
-bool Sema::checkArgCountAtMost(CallExpr *Call, unsigned MaxArgCount) {
- unsigned ArgCount = Call->getNumArgs();
- if (ArgCount <= MaxArgCount)
- return false;
- return Diag(Call->getEndLoc(), diag::err_typecheck_call_too_many_args_at_most)
- << 0 /*function call*/ << MaxArgCount << ArgCount
- << /*is non object*/ 0 << Call->getSourceRange();
-}
-
-bool Sema::checkArgCountRange(CallExpr *Call, unsigned MinArgCount,
- unsigned MaxArgCount) {
- return checkArgCountAtLeast(Call, MinArgCount) ||
- checkArgCountAtMost(Call, MaxArgCount);
-}
-
-bool Sema::checkArgCount(CallExpr *Call, unsigned DesiredArgCount) {
- unsigned ArgCount = Call->getNumArgs();
- if (ArgCount == DesiredArgCount)
- return false;
-
- if (checkArgCountAtLeast(Call, DesiredArgCount))
- return true;
- assert(ArgCount > DesiredArgCount && "should have diagnosed this");
-
- // Highlight all the excess arguments.
- SourceRange Range(Call->getArg(DesiredArgCount)->getBeginLoc(),
- Call->getArg(ArgCount - 1)->getEndLoc());
-
- return Diag(Range.getBegin(), diag::err_typecheck_call_too_many_args)
- << 0 /*function call*/ << DesiredArgCount << ArgCount
- << /*is non object*/ 0 << Range;
-}
-
-static bool checkBuiltinVerboseTrap(CallExpr *Call, Sema &S) {
- bool HasError = false;
-
- for (const Expr *Arg : Call->arguments()) {
- if (Arg->isValueDependent())
- continue;
-
- std::optional<std::string> ArgString = Arg->tryEvaluateString(S.Context);
- int DiagMsgKind = -1;
- // Arguments must be pointers to constant strings and cannot use '$'.
- if (!ArgString.has_value())
- DiagMsgKind = 0;
- else if (ArgString->find('$') != std::string::npos)
- DiagMsgKind = 1;
-
- if (DiagMsgKind >= 0) {
- S.Diag(Arg->getBeginLoc(), diag::err_builtin_verbose_trap_arg)
- << DiagMsgKind << Arg->getSourceRange();
- HasError = true;
- }
- }
-
- return !HasError;
-}
-
-static bool convertArgumentToType(Sema &S, Expr *&Value, QualType Ty) {
- if (Value->isTypeDependent())
- return false;
-
- InitializedEntity Entity =
- InitializedEntity::InitializeParameter(S.Context, Ty, false);
- ExprResult Result =
- S.PerformCopyInitialization(Entity, SourceLocation(), Value);
- if (Result.isInvalid())
- return true;
- Value = Result.get();
- return false;
-}
-
-/// Check that the first argument to __builtin_annotation is an integer
-/// and the second argument is a non-wide string literal.
-static bool BuiltinAnnotation(Sema &S, CallExpr *TheCall) {
- if (S.checkArgCount(TheCall, 2))
- return true;
-
- // First argument should be an integer.
- Expr *ValArg = TheCall->getArg(0);
- QualType Ty = ValArg->getType();
- if (!Ty->isIntegerType()) {
- S.Diag(ValArg->getBeginLoc(), diag::err_builtin_annotation_first_arg)
- << ValArg->getSourceRange();
- return true;
- }
-
- // Second argument should be a constant string.
- Expr *StrArg = TheCall->getArg(1)->IgnoreParenCasts();
- StringLiteral *Literal = dyn_cast<StringLiteral>(StrArg);
- if (!Literal || !Literal->isOrdinary()) {
- S.Diag(StrArg->getBeginLoc(), diag::err_builtin_annotation_second_arg)
- << StrArg->getSourceRange();
- return true;
- }
-
- TheCall->setType(Ty);
- return false;
-}
-
-static bool BuiltinMSVCAnnotation(Sema &S, CallExpr *TheCall) {
- // We need at least one argument.
- if (TheCall->getNumArgs() < 1) {
- S.Diag(TheCall->getEndLoc(), diag::err_typecheck_call_too_few_args_at_least)
- << 0 << 1 << TheCall->getNumArgs() << /*is non object*/ 0
- << TheCall->getCallee()->getSourceRange();
- return true;
- }
-
- // All arguments should be wide string literals.
- for (Expr *Arg : TheCall->arguments()) {
- auto *Literal = dyn_cast<StringLiteral>(Arg->IgnoreParenCasts());
- if (!Literal || !Literal->isWide()) {
- S.Diag(Arg->getBeginLoc(), diag::err_msvc_annotation_wide_str)
- << Arg->getSourceRange();
- return true;
- }
- }
-
- return false;
-}
-
-/// Check that the argument to __builtin_addressof is a glvalue, and set the
-/// result type to the corresponding pointer type.
-static bool BuiltinAddressof(Sema &S, CallExpr *TheCall) {
- if (S.checkArgCount(TheCall, 1))
- return true;
-
- ExprResult Arg(TheCall->getArg(0));
- QualType ResultType = S.CheckAddressOfOperand(Arg, TheCall->getBeginLoc());
- if (ResultType.isNull())
- return true;
-
- TheCall->setArg(0, Arg.get());
- TheCall->setType(ResultType);
- return false;
-}
-
-/// Check that the argument to __builtin_function_start is a function.
-static bool BuiltinFunctionStart(Sema &S, CallExpr *TheCall) {
- if (S.checkArgCount(TheCall, 1))
- return true;
-
- if (TheCall->getArg(0)->containsErrors())
- return true;
-
- ExprResult Arg = S.DefaultFunctionArrayLvalueConversion(TheCall->getArg(0));
- if (Arg.isInvalid())
- return true;
-
- TheCall->setArg(0, Arg.get());
- const FunctionDecl *FD = dyn_cast_or_null<FunctionDecl>(
- Arg.get()->getAsBuiltinConstantDeclRef(S.getASTContext()));
-
- if (!FD) {
- S.Diag(TheCall->getBeginLoc(), diag::err_function_start_invalid_type)
- << TheCall->getSourceRange();
- return true;
- }
-
- return !S.checkAddressOfFunctionIsAvailable(FD, /*Complain=*/true,
- TheCall->getBeginLoc());
-}
-
-/// Check the number of arguments and set the result type to
-/// the argument type.
-static bool BuiltinPreserveAI(Sema &S, CallExpr *TheCall) {
- if (S.checkArgCount(TheCall, 1))
- return true;
-
- TheCall->setType(TheCall->getArg(0)->getType());
- return false;
-}
-
-/// Check that the value argument for __builtin_is_aligned(value, alignment) and
-/// __builtin_aligned_{up,down}(value, alignment) is an integer or a pointer
-/// type (but not a function pointer) and that the alignment is a power-of-two.
-static bool BuiltinAlignment(Sema &S, CallExpr *TheCall, unsigned ID) {
- if (S.checkArgCount(TheCall, 2))
- return true;
-
- clang::Expr *Source = TheCall->getArg(0);
- bool IsBooleanAlignBuiltin = ID == Builtin::BI__builtin_is_aligned;
-
- auto IsValidIntegerType = [](QualType Ty) {
- return Ty->isIntegerType() && !Ty->isEnumeralType() && !Ty->isBooleanType();
- };
- QualType SrcTy = Source->getType();
- // We should also be able to use it with arrays (but not functions!).
- if (SrcTy->canDecayToPointerType() && SrcTy->isArrayType()) {
- SrcTy = S.Context.getDecayedType(SrcTy);
- }
- if ((!SrcTy->isPointerType() && !IsValidIntegerType(SrcTy)) ||
- SrcTy->isFunctionPointerType()) {
- // FIXME: this is not quite the right error message since we don't allow
- // floating point types, or member pointers.
- S.Diag(Source->getExprLoc(), diag::err_typecheck_expect_scalar_operand)
- << SrcTy;
- return true;
- }
-
- clang::Expr *AlignOp = TheCall->getArg(1);
- if (!IsValidIntegerType(AlignOp->getType())) {
- S.Diag(AlignOp->getExprLoc(), diag::err_typecheck_expect_int)
- << AlignOp->getType();
- return true;
- }
- Expr::EvalResult AlignResult;
- unsigned MaxAlignmentBits = S.Context.getIntWidth(SrcTy) - 1;
- // We can't check validity of alignment if it is value dependent.
- if (!AlignOp->isValueDependent() &&
- AlignOp->EvaluateAsInt(AlignResult, S.Context,
- Expr::SE_AllowSideEffects)) {
- llvm::APSInt AlignValue = AlignResult.Val.getInt();
- llvm::APSInt MaxValue(
- llvm::APInt::getOneBitSet(MaxAlignmentBits + 1, MaxAlignmentBits));
- if (AlignValue < 1) {
- S.Diag(AlignOp->getExprLoc(), diag::err_alignment_too_small) << 1;
- return true;
- }
- if (llvm::APSInt::compareValues(AlignValue, MaxValue) > 0) {
- S.Diag(AlignOp->getExprLoc(), diag::err_alignment_too_big)
- << toString(MaxValue, 10);
- return true;
- }
- if (!AlignValue.isPowerOf2()) {
- S.Diag(AlignOp->getExprLoc(), diag::err_alignment_not_power_of_two);
- return true;
- }
- if (AlignValue == 1) {
- S.Diag(AlignOp->getExprLoc(), diag::warn_alignment_builtin_useless)
- << IsBooleanAlignBuiltin;
- }
- }
-
- ExprResult SrcArg = S.PerformCopyInitialization(
- InitializedEntity::InitializeParameter(S.Context, SrcTy, false),
- SourceLocation(), Source);
- if (SrcArg.isInvalid())
- return true;
- TheCall->setArg(0, SrcArg.get());
- ExprResult AlignArg =
- S.PerformCopyInitialization(InitializedEntity::InitializeParameter(
- S.Context, AlignOp->getType(), false),
- SourceLocation(), AlignOp);
- if (AlignArg.isInvalid())
- return true;
- TheCall->setArg(1, AlignArg.get());
- // For align_up/align_down, the return type is the same as the (potentially
- // decayed) argument type including qualifiers. For is_aligned(), the result
- // is always bool.
- TheCall->setType(IsBooleanAlignBuiltin ? S.Context.BoolTy : SrcTy);
- return false;
-}
-
-static bool BuiltinOverflow(Sema &S, CallExpr *TheCall, unsigned BuiltinID) {
- if (S.checkArgCount(TheCall, 3))
- return true;
-
- std::pair<unsigned, const char *> Builtins[] = {
- { Builtin::BI__builtin_add_overflow, "ckd_add" },
- { Builtin::BI__builtin_sub_overflow, "ckd_sub" },
- { Builtin::BI__builtin_mul_overflow, "ckd_mul" },
- };
-
- bool CkdOperation = llvm::any_of(Builtins, [&](const std::pair<unsigned,
- const char *> &P) {
- return BuiltinID == P.first && TheCall->getExprLoc().isMacroID() &&
- Lexer::getImmediateMacroName(TheCall->getExprLoc(),
- S.getSourceManager(), S.getLangOpts()) == P.second;
- });
-
- auto ValidCkdIntType = [](QualType QT) {
- // A valid checked integer type is an integer type other than a plain char,
- // bool, a bit-precise type, or an enumeration type.
- if (const auto *BT = QT.getCanonicalType()->getAs<BuiltinType>())
- return (BT->getKind() >= BuiltinType::Short &&
- BT->getKind() <= BuiltinType::Int128) || (
- BT->getKind() >= BuiltinType::UShort &&
- BT->getKind() <= BuiltinType::UInt128) ||
- BT->getKind() == BuiltinType::UChar ||
- BT->getKind() == BuiltinType::SChar;
- return false;
- };
-
- // First two arguments should be integers.
- for (unsigned I = 0; I < 2; ++I) {
- ExprResult Arg = S.DefaultFunctionArrayLvalueConversion(TheCall->getArg(I));
- if (Arg.isInvalid()) return true;
- TheCall->setArg(I, Arg.get());
-
- QualType Ty = Arg.get()->getType();
- bool IsValid = CkdOperation ? ValidCkdIntType(Ty) : Ty->isIntegerType();
- if (!IsValid) {
- S.Diag(Arg.get()->getBeginLoc(), diag::err_overflow_builtin_must_be_int)
- << CkdOperation << Ty << Arg.get()->getSourceRange();
- return true;
- }
- }
-
- // Third argument should be a pointer to a non-const integer.
- // IRGen correctly handles volatile, restrict, and address spaces, and
- // the other qualifiers aren't possible.
- {
- ExprResult Arg = S.DefaultFunctionArrayLvalueConversion(TheCall->getArg(2));
- if (Arg.isInvalid()) return true;
- TheCall->setArg(2, Arg.get());
-
- QualType Ty = Arg.get()->getType();
- const auto *PtrTy = Ty->getAs<PointerType>();
- if (!PtrTy ||
- !PtrTy->getPointeeType()->isIntegerType() ||
- (!ValidCkdIntType(PtrTy->getPointeeType()) && CkdOperation) ||
- PtrTy->getPointeeType().isConstQualified()) {
- S.Diag(Arg.get()->getBeginLoc(),
- diag::err_overflow_builtin_must_be_ptr_int)
- << CkdOperation << Ty << Arg.get()->getSourceRange();
- return true;
- }
- }
-
- // Disallow signed bit-precise integer args larger than 128 bits to mul
- // function until we improve backend support.
- if (BuiltinID == Builtin::BI__builtin_mul_overflow) {
- for (unsigned I = 0; I < 3; ++I) {
- const auto Arg = TheCall->getArg(I);
- // Third argument will be a pointer.
- auto Ty = I < 2 ? Arg->getType() : Arg->getType()->getPointeeType();
- if (Ty->isBitIntType() && Ty->isSignedIntegerType() &&
- S.getASTContext().getIntWidth(Ty) > 128)
- return S.Diag(Arg->getBeginLoc(),
- diag::err_overflow_builtin_bit_int_max_size)
- << 128;
- }
- }
-
- return false;
-}
-
-namespace {
-struct BuiltinDumpStructGenerator {
- Sema &S;
- CallExpr *TheCall;
- SourceLocation Loc = TheCall->getBeginLoc();
- SmallVector<Expr *, 32> Actions;
- DiagnosticErrorTrap ErrorTracker;
- PrintingPolicy Policy;
-
- BuiltinDumpStructGenerator(Sema &S, CallExpr *TheCall)
- : S(S), TheCall(TheCall), ErrorTracker(S.getDiagnostics()),
- Policy(S.Context.getPrintingPolicy()) {
- Policy.AnonymousTagLocations = false;
- }
-
- Expr *makeOpaqueValueExpr(Expr *Inner) {
- auto *OVE = new (S.Context)
- OpaqueValueExpr(Loc, Inner->getType(), Inner->getValueKind(),
- Inner->getObjectKind(), Inner);
- Actions.push_back(OVE);
- return OVE;
- }
-
- Expr *getStringLiteral(llvm::StringRef Str) {
- Expr *Lit = S.Context.getPredefinedStringLiteralFromCache(Str);
- // Wrap the literal in parentheses to attach a source location.
- return new (S.Context) ParenExpr(Loc, Loc, Lit);
- }
-
- bool callPrintFunction(llvm::StringRef Format,
- llvm::ArrayRef<Expr *> Exprs = {}) {
- SmallVector<Expr *, 8> Args;
- assert(TheCall->getNumArgs() >= 2);
- Args.reserve((TheCall->getNumArgs() - 2) + /*Format*/ 1 + Exprs.size());
- Args.assign(TheCall->arg_begin() + 2, TheCall->arg_end());
- Args.push_back(getStringLiteral(Format));
- llvm::append_range(Args, Exprs);
-
- // Register a note to explain why we're performing the call.
- Sema::CodeSynthesisContext Ctx;
- Ctx.Kind = Sema::CodeSynthesisContext::BuildingBuiltinDumpStructCall;
- Ctx.PointOfInstantiation = Loc;
- Ctx.CallArgs = Args.data();
- Ctx.NumCallArgs = Args.size();
- S.pushCodeSynthesisContext(Ctx);
-
- ExprResult RealCall =
- S.BuildCallExpr(/*Scope=*/nullptr, TheCall->getArg(1),
- TheCall->getBeginLoc(), Args, TheCall->getRParenLoc());
-
- S.popCodeSynthesisContext();
- if (!RealCall.isInvalid())
- Actions.push_back(RealCall.get());
- // Bail out if we've hit any errors, even if we managed to build the
- // call. We don't want to produce more than one error.
- return RealCall.isInvalid() || ErrorTracker.hasErrorOccurred();
- }
-
- Expr *getIndentString(unsigned Depth) {
- if (!Depth)
- return nullptr;
-
- llvm::SmallString<32> Indent;
- Indent.resize(Depth * Policy.Indentation, ' ');
- return getStringLiteral(Indent);
- }
-
- Expr *getTypeString(QualType T) {
- return getStringLiteral(T.getAsString(Policy));
- }
-
- bool appendFormatSpecifier(QualType T, llvm::SmallVectorImpl<char> &Str) {
- llvm::raw_svector_ostream OS(Str);
-
- // Format 'bool', 'char', 'signed char', 'unsigned char' as numbers, rather
- // than trying to print a single character.
- if (auto *BT = T->getAs<BuiltinType>()) {
- switch (BT->getKind()) {
- case BuiltinType::Bool:
- OS << "%d";
- return true;
- case BuiltinType::Char_U:
- case BuiltinType::UChar:
- OS << "%hhu";
- return true;
- case BuiltinType::Char_S:
- case BuiltinType::SChar:
- OS << "%hhd";
- return true;
- default:
- break;
- }
- }
-
- analyze_printf::PrintfSpecifier Specifier;
- if (Specifier.fixType(T, S.getLangOpts(), S.Context, /*IsObjCLiteral=*/false)) {
- // We were able to guess how to format this.
- if (Specifier.getConversionSpecifier().getKind() ==
- analyze_printf::PrintfConversionSpecifier::sArg) {
- // Wrap double-quotes around a '%s' specifier and limit its maximum
- // length. Ideally we'd also somehow escape special characters in the
- // contents but printf doesn't support that.
- // FIXME: '%s' formatting is not safe in general.
- OS << '"';
- Specifier.setPrecision(analyze_printf::OptionalAmount(32u));
- Specifier.toString(OS);
- OS << '"';
- // FIXME: It would be nice to include a '...' if the string doesn't fit
- // in the length limit.
- } else {
- Specifier.toString(OS);
- }
- return true;
- }
-
- if (T->isPointerType()) {
- // Format all pointers with '%p'.
- OS << "%p";
- return true;
- }
-
- return false;
- }
-
- bool dumpUnnamedRecord(const RecordDecl *RD, Expr *E, unsigned Depth) {
- Expr *IndentLit = getIndentString(Depth);
- Expr *TypeLit = getTypeString(S.Context.getCanonicalTagType(RD));
- if (IndentLit ? callPrintFunction("%s%s", {IndentLit, TypeLit})
- : callPrintFunction("%s", {TypeLit}))
- return true;
-
- return dumpRecordValue(RD, E, IndentLit, Depth);
- }
-
- // Dump a record value. E should be a pointer or lvalue referring to an RD.
- bool dumpRecordValue(const RecordDecl *RD, Expr *E, Expr *RecordIndent,
- unsigned Depth) {
- // FIXME: Decide what to do if RD is a union. At least we should probably
- // turn off printing `const char*` members with `%s`, because that is very
- // likely to crash if that's not the active member. Whatever we decide, we
- // should document it.
-
- // Build an OpaqueValueExpr so we can refer to E more than once without
- // triggering re-evaluation.
- Expr *RecordArg = makeOpaqueValueExpr(E);
- bool RecordArgIsPtr = RecordArg->getType()->isPointerType();
-
- if (callPrintFunction(" {\n"))
- return true;
-
- // Dump each base class, regardless of whether they're aggregates.
- if (const auto *CXXRD = dyn_cast<CXXRecordDecl>(RD)) {
- for (const auto &Base : CXXRD->bases()) {
- QualType BaseType =
- RecordArgIsPtr ? S.Context.getPointerType(Base.getType())
- : S.Context.getLValueReferenceType(Base.getType());
- ExprResult BasePtr = S.BuildCStyleCastExpr(
- Loc, S.Context.getTrivialTypeSourceInfo(BaseType, Loc), Loc,
- RecordArg);
- if (BasePtr.isInvalid() ||
- dumpUnnamedRecord(Base.getType()->getAsRecordDecl(), BasePtr.get(),
- Depth + 1))
- return true;
- }
- }
-
- Expr *FieldIndentArg = getIndentString(Depth + 1);
-
- // Dump each field.
- for (auto *D : RD->decls()) {
- auto *IFD = dyn_cast<IndirectFieldDecl>(D);
- auto *FD = IFD ? IFD->getAnonField() : dyn_cast<FieldDecl>(D);
- if (!FD || FD->isUnnamedBitField() || FD->isAnonymousStructOrUnion())
- continue;
-
- llvm::SmallString<20> Format = llvm::StringRef("%s%s %s ");
- llvm::SmallVector<Expr *, 5> Args = {FieldIndentArg,
- getTypeString(FD->getType()),
- getStringLiteral(FD->getName())};
-
- if (FD->isBitField()) {
- Format += ": %zu ";
- QualType SizeT = S.Context.getSizeType();
- llvm::APInt BitWidth(S.Context.getIntWidth(SizeT),
- FD->getBitWidthValue());
- Args.push_back(IntegerLiteral::Create(S.Context, BitWidth, SizeT, Loc));
- }
-
- Format += "=";
-
- ExprResult Field =
- IFD ? S.BuildAnonymousStructUnionMemberReference(
- CXXScopeSpec(), Loc, IFD,
- DeclAccessPair::make(IFD, AS_public), RecordArg, Loc)
- : S.BuildFieldReferenceExpr(
- RecordArg, RecordArgIsPtr, Loc, CXXScopeSpec(), FD,
- DeclAccessPair::make(FD, AS_public),
- DeclarationNameInfo(FD->getDeclName(), Loc));
- if (Field.isInvalid())
- return true;
-
- auto *InnerRD = FD->getType()->getAsRecordDecl();
- auto *InnerCXXRD = dyn_cast_or_null<CXXRecordDecl>(InnerRD);
- if (InnerRD && (!InnerCXXRD || InnerCXXRD->isAggregate())) {
- // Recursively print the values of members of aggregate record type.
- if (callPrintFunction(Format, Args) ||
- dumpRecordValue(InnerRD, Field.get(), FieldIndentArg, Depth + 1))
- return true;
- } else {
- Format += " ";
- if (appendFormatSpecifier(FD->getType(), Format)) {
- // We know how to print this field.
- Args.push_back(Field.get());
- } else {
- // We don't know how to print this field. Print out its address
- // with a format specifier that a smart tool will be able to
- // recognize and treat specially.
- Format += "*%p";
- ExprResult FieldAddr =
- S.BuildUnaryOp(nullptr, Loc, UO_AddrOf, Field.get());
- if (FieldAddr.isInvalid())
- return true;
- Args.push_back(FieldAddr.get());
- }
- Format += "\n";
- if (callPrintFunction(Format, Args))
- return true;
- }
- }
-
- return RecordIndent ? callPrintFunction("%s}\n", RecordIndent)
- : callPrintFunction("}\n");
- }
-
- Expr *buildWrapper() {
- auto *Wrapper = PseudoObjectExpr::Create(S.Context, TheCall, Actions,
- PseudoObjectExpr::NoResult);
- TheCall->setType(Wrapper->getType());
- TheCall->setValueKind(Wrapper->getValueKind());
- return Wrapper;
- }
-};
-} // namespace
-
-static ExprResult BuiltinDumpStruct(Sema &S, CallExpr *TheCall) {
- if (S.checkArgCountAtLeast(TheCall, 2))
- return ExprError();
-
- ExprResult PtrArgResult = S.DefaultLvalueConversion(TheCall->getArg(0));
- if (PtrArgResult.isInvalid())
- return ExprError();
- TheCall->setArg(0, PtrArgResult.get());
-
- // First argument should be a pointer to a struct.
- QualType PtrArgType = PtrArgResult.get()->getType();
- if (!PtrArgType->isPointerType() ||
- !PtrArgType->getPointeeType()->isRecordType()) {
- S.Diag(PtrArgResult.get()->getBeginLoc(),
- diag::err_expected_struct_pointer_argument)
- << 1 << TheCall->getDirectCallee() << PtrArgType;
- return ExprError();
- }
- QualType Pointee = PtrArgType->getPointeeType();
- const RecordDecl *RD = Pointee->getAsRecordDecl();
- // Try to instantiate the class template as appropriate; otherwise, access to
- // its data() may lead to a crash.
- if (S.RequireCompleteType(PtrArgResult.get()->getBeginLoc(), Pointee,
- diag::err_incomplete_type))
- return ExprError();
- // Second argument is a callable, but we can't fully validate it until we try
- // calling it.
- QualType FnArgType = TheCall->getArg(1)->getType();
- if (!FnArgType->isFunctionType() && !FnArgType->isFunctionPointerType() &&
- !FnArgType->isBlockPointerType() &&
- !(S.getLangOpts().CPlusPlus && FnArgType->isRecordType())) {
- auto *BT = FnArgType->getAs<BuiltinType>();
- switch (BT ? BT->getKind() : BuiltinType::Void) {
- case BuiltinType::Dependent:
- case BuiltinType::Overload:
- case BuiltinType::BoundMember:
- case BuiltinType::PseudoObject:
- case BuiltinType::UnknownAny:
- case BuiltinType::BuiltinFn:
- // This might be a callable.
- break;
-
- default:
- S.Diag(TheCall->getArg(1)->getBeginLoc(),
- diag::err_expected_callable_argument)
- << 2 << TheCall->getDirectCallee() << FnArgType;
- return ExprError();
- }
- }
-
- BuiltinDumpStructGenerator Generator(S, TheCall);
-
- // Wrap parentheses around the given pointer. This is not necessary for
- // correct code generation, but it means that when we pretty-print the call
- // arguments in our diagnostics we will produce '(&s)->n' instead of the
- // incorrect '&s->n'.
- Expr *PtrArg = PtrArgResult.get();
- PtrArg = new (S.Context)
- ParenExpr(PtrArg->getBeginLoc(),
- S.getLocForEndOfToken(PtrArg->getEndLoc()), PtrArg);
- if (Generator.dumpUnnamedRecord(RD, PtrArg, 0))
- return ExprError();
-
- return Generator.buildWrapper();
-}
-
-static bool BuiltinCallWithStaticChain(Sema &S, CallExpr *BuiltinCall) {
- if (S.checkArgCount(BuiltinCall, 2))
- return true;
-
- SourceLocation BuiltinLoc = BuiltinCall->getBeginLoc();
- Expr *Builtin = BuiltinCall->getCallee()->IgnoreImpCasts();
- Expr *Call = BuiltinCall->getArg(0);
- Expr *Chain = BuiltinCall->getArg(1);
-
- if (Call->getStmtClass() != Stmt::CallExprClass) {
- S.Diag(BuiltinLoc, diag::err_first_argument_to_cwsc_not_call)
- << Call->getSourceRange();
- return true;
- }
-
- auto CE = cast<CallExpr>(Call);
- if (CE->getCallee()->getType()->isBlockPointerType()) {
- S.Diag(BuiltinLoc, diag::err_first_argument_to_cwsc_block_call)
- << Call->getSourceRange();
- return true;
- }
-
- const Decl *TargetDecl = CE->getCalleeDecl();
- if (const FunctionDecl *FD = dyn_cast_or_null<FunctionDecl>(TargetDecl))
- if (FD->getBuiltinID()) {
- S.Diag(BuiltinLoc, diag::err_first_argument_to_cwsc_builtin_call)
- << Call->getSourceRange();
- return true;
- }
-
- if (isa<CXXPseudoDestructorExpr>(CE->getCallee()->IgnoreParens())) {
- S.Diag(BuiltinLoc, diag::err_first_argument_to_cwsc_pdtor_call)
- << Call->getSourceRange();
- return true;
- }
-
- ExprResult ChainResult = S.UsualUnaryConversions(Chain);
- if (ChainResult.isInvalid())
- return true;
- if (!ChainResult.get()->getType()->isPointerType()) {
- S.Diag(BuiltinLoc, diag::err_second_argument_to_cwsc_not_pointer)
- << Chain->getSourceRange();
- return true;
- }
-
- QualType ReturnTy = CE->getCallReturnType(S.Context);
- QualType ArgTys[2] = { ReturnTy, ChainResult.get()->getType() };
- QualType BuiltinTy = S.Context.getFunctionType(
- ReturnTy, ArgTys, FunctionProtoType::ExtProtoInfo());
- QualType BuiltinPtrTy = S.Context.getPointerType(BuiltinTy);
-
- Builtin =
- S.ImpCastExprToType(Builtin, BuiltinPtrTy, CK_BuiltinFnToFnPtr).get();
-
- BuiltinCall->setType(CE->getType());
- BuiltinCall->setValueKind(CE->getValueKind());
- BuiltinCall->setObjectKind(CE->getObjectKind());
- BuiltinCall->setCallee(Builtin);
- BuiltinCall->setArg(1, ChainResult.get());
-
- return false;
-}
-
-namespace {
-
-class ScanfDiagnosticFormatHandler
- : public analyze_format_string::FormatStringHandler {
- // Accepts the argument index (relative to the first destination index) of the
- // argument whose size we want.
- using ComputeSizeFunction =
- llvm::function_ref<std::optional<llvm::APSInt>(unsigned)>;
-
- // Accepts the argument index (relative to the first destination index), the
- // destination size, and the source size).
- using DiagnoseFunction =
- llvm::function_ref<void(unsigned, unsigned, unsigned)>;
-
- ComputeSizeFunction ComputeSizeArgument;
- DiagnoseFunction Diagnose;
-
-public:
- ScanfDiagnosticFormatHandler(ComputeSizeFunction ComputeSizeArgument,
- DiagnoseFunction Diagnose)
- : ComputeSizeArgument(ComputeSizeArgument), Diagnose(Diagnose) {}
-
- bool HandleScanfSpecifier(const analyze_scanf::ScanfSpecifier &FS,
- const char *StartSpecifier,
- unsigned specifierLen) override {
- if (!FS.consumesDataArgument())
- return true;
-
- unsigned NulByte = 0;
- switch ((FS.getConversionSpecifier().getKind())) {
- default:
- return true;
- case analyze_format_string::ConversionSpecifier::sArg:
- case analyze_format_string::ConversionSpecifier::ScanListArg:
- NulByte = 1;
- break;
- case analyze_format_string::ConversionSpecifier::cArg:
- break;
- }
-
- analyze_format_string::OptionalAmount FW = FS.getFieldWidth();
- if (FW.getHowSpecified() !=
- analyze_format_string::OptionalAmount::HowSpecified::Constant)
- return true;
-
- unsigned SourceSize = FW.getConstantAmount() + NulByte;
-
- std::optional<llvm::APSInt> DestSizeAPS =
- ComputeSizeArgument(FS.getArgIndex());
- if (!DestSizeAPS)
- return true;
-
- unsigned DestSize = DestSizeAPS->getZExtValue();
-
- if (DestSize < SourceSize)
- Diagnose(FS.getArgIndex(), DestSize, SourceSize);
-
- return true;
- }
-};
-
-class EstimateSizeFormatHandler
- : public analyze_format_string::FormatStringHandler {
- size_t Size;
- /// Whether the format string contains Linux kernel's format specifier
- /// extension.
- bool IsKernelCompatible = true;
-
-public:
- EstimateSizeFormatHandler(StringRef Format)
- : Size(std::min(Format.find(0), Format.size()) +
- 1 /* null byte always written by sprintf */) {}
-
- bool HandlePrintfSpecifier(const analyze_printf::PrintfSpecifier &FS,
- const char *, unsigned SpecifierLen,
- const TargetInfo &) override {
-
- const size_t FieldWidth = computeFieldWidth(FS);
- const size_t Precision = computePrecision(FS);
-
- // The actual format.
- switch (FS.getConversionSpecifier().getKind()) {
- // Just a char.
- case analyze_format_string::ConversionSpecifier::cArg:
- case analyze_format_string::ConversionSpecifier::CArg:
- Size += std::max(FieldWidth, (size_t)1);
- break;
- // Just an integer.
- case analyze_format_string::ConversionSpecifier::dArg:
- case analyze_format_string::ConversionSpecifier::DArg:
- case analyze_format_string::ConversionSpecifier::iArg:
- case analyze_format_string::ConversionSpecifier::oArg:
- case analyze_format_string::ConversionSpecifier::OArg:
- case analyze_format_string::ConversionSpecifier::uArg:
- case analyze_format_string::ConversionSpecifier::UArg:
- case analyze_format_string::ConversionSpecifier::xArg:
- case analyze_format_string::ConversionSpecifier::XArg:
- Size += std::max(FieldWidth, Precision);
- break;
-
- // %g style conversion switches between %f or %e style dynamically.
- // %g removes trailing zeros, and does not print decimal point if there are
- // no digits that follow it. Thus %g can print a single digit.
- // FIXME: If it is alternative form:
- // For g and G conversions, trailing zeros are not removed from the result.
- case analyze_format_string::ConversionSpecifier::gArg:
- case analyze_format_string::ConversionSpecifier::GArg:
- Size += 1;
- break;
-
- // Floating point number in the form '[+]ddd.ddd'.
- case analyze_format_string::ConversionSpecifier::fArg:
- case analyze_format_string::ConversionSpecifier::FArg:
- Size += std::max(FieldWidth, 1 /* integer part */ +
- (Precision ? 1 + Precision
- : 0) /* period + decimal */);
- break;
-
- // Floating point number in the form '[-]d.ddde[+-]dd'.
- case analyze_format_string::ConversionSpecifier::eArg:
- case analyze_format_string::ConversionSpecifier::EArg:
- Size +=
- std::max(FieldWidth,
- 1 /* integer part */ +
- (Precision ? 1 + Precision : 0) /* period + decimal */ +
- 1 /* e or E letter */ + 2 /* exponent */);
- break;
-
- // Floating point number in the form '[-]0xh.hhhhp±dd'.
- case analyze_format_string::ConversionSpecifier::aArg:
- case analyze_format_string::ConversionSpecifier::AArg:
- Size +=
- std::max(FieldWidth,
- 2 /* 0x */ + 1 /* integer part */ +
- (Precision ? 1 + Precision : 0) /* period + decimal */ +
- 1 /* p or P letter */ + 1 /* + or - */ + 1 /* value */);
- break;
-
- // Just a string.
- case analyze_format_string::ConversionSpecifier::sArg:
- case analyze_format_string::ConversionSpecifier::SArg:
- Size += FieldWidth;
- break;
-
- // Just a pointer in the form '0xddd'.
- case analyze_format_string::ConversionSpecifier::pArg:
- // Linux kernel has its own extesion for `%p` specifier.
- // Kernel Document:
- // https://docs.kernel.org/core-api/printk-formats.html#pointer-types
- IsKernelCompatible = false;
- Size += std::max(FieldWidth, 2 /* leading 0x */ + Precision);
- break;
-
- // A plain percent.
- case analyze_format_string::ConversionSpecifier::PercentArg:
- Size += 1;
- break;
-
- default:
- break;
- }
-
- // If field width is specified, the sign/space is already accounted for
- // within the field width, so no additional size is needed.
- if ((FS.hasPlusPrefix() || FS.hasSpacePrefix()) && FieldWidth == 0)
- Size += 1;
-
- if (FS.hasAlternativeForm()) {
- switch (FS.getConversionSpecifier().getKind()) {
- // For o conversion, it increases the precision, if and only if necessary,
- // to force the first digit of the result to be a zero
- // (if the value and precision are both 0, a single 0 is printed)
- case analyze_format_string::ConversionSpecifier::oArg:
- // For b conversion, a nonzero result has 0b prefixed to it.
- case analyze_format_string::ConversionSpecifier::bArg:
- // For x (or X) conversion, a nonzero result has 0x (or 0X) prefixed to
- // it.
- case analyze_format_string::ConversionSpecifier::xArg:
- case analyze_format_string::ConversionSpecifier::XArg:
- // Note: even when the prefix is added, if
- // (prefix_width <= FieldWidth - formatted_length) holds,
- // the prefix does not increase the format
- // size. e.g.(("%#3x", 0xf) is "0xf")
-
- // If the result is zero, o, b, x, X adds nothing.
- break;
- // For a, A, e, E, f, F, g, and G conversions,
- // the result of converting a floating-point number always contains a
- // decimal-point
- case analyze_format_string::ConversionSpecifier::aArg:
- case analyze_format_string::ConversionSpecifier::AArg:
- case analyze_format_string::ConversionSpecifier::eArg:
- case analyze_format_string::ConversionSpecifier::EArg:
- case analyze_format_string::ConversionSpecifier::fArg:
- case analyze_format_string::ConversionSpecifier::FArg:
- case analyze_format_string::ConversionSpecifier::gArg:
- case analyze_format_string::ConversionSpecifier::GArg:
- Size += (Precision ? 0 : 1);
- break;
- // For other conversions, the behavior is undefined.
- default:
- break;
- }
- }
- assert(SpecifierLen <= Size && "no underflow");
- Size -= SpecifierLen;
- return true;
- }
-
- size_t getSizeLowerBound() const { return Size; }
- bool isKernelCompatible() const { return IsKernelCompatible; }
-
-private:
- static size_t computeFieldWidth(const analyze_printf::PrintfSpecifier &FS) {
- const analyze_format_string::OptionalAmount &FW = FS.getFieldWidth();
- size_t FieldWidth = 0;
- if (FW.getHowSpecified() == analyze_format_string::OptionalAmount::Constant)
- FieldWidth = FW.getConstantAmount();
- return FieldWidth;
- }
-
- static size_t computePrecision(const analyze_printf::PrintfSpecifier &FS) {
- const analyze_format_string::OptionalAmount &FW = FS.getPrecision();
- size_t Precision = 0;
-
- // See man 3 printf for default precision value based on the specifier.
- switch (FW.getHowSpecified()) {
- case analyze_format_string::OptionalAmount::NotSpecified:
- switch (FS.getConversionSpecifier().getKind()) {
- default:
- break;
- case analyze_format_string::ConversionSpecifier::dArg: // %d
- case analyze_format_string::ConversionSpecifier::DArg: // %D
- case analyze_format_string::ConversionSpecifier::iArg: // %i
- Precision = 1;
- break;
- case analyze_format_string::ConversionSpecifier::oArg: // %d
- case analyze_format_string::ConversionSpecifier::OArg: // %D
- case analyze_format_string::ConversionSpecifier::uArg: // %d
- case analyze_format_string::ConversionSpecifier::UArg: // %D
- case analyze_format_string::ConversionSpecifier::xArg: // %d
- case analyze_format_string::ConversionSpecifier::XArg: // %D
- Precision = 1;
- break;
- case analyze_format_string::ConversionSpecifier::fArg: // %f
- case analyze_format_string::ConversionSpecifier::FArg: // %F
- case analyze_format_string::ConversionSpecifier::eArg: // %e
- case analyze_format_string::ConversionSpecifier::EArg: // %E
- case analyze_format_string::ConversionSpecifier::gArg: // %g
- case analyze_format_string::ConversionSpecifier::GArg: // %G
- Precision = 6;
- break;
- case analyze_format_string::ConversionSpecifier::pArg: // %d
- Precision = 1;
- break;
- }
- break;
- case analyze_format_string::OptionalAmount::Constant:
- Precision = FW.getConstantAmount();
- break;
- default:
- break;
- }
- return Precision;
- }
-};
-
-} // namespace
-
-static bool ProcessFormatStringLiteral(const Expr *FormatExpr,
- StringRef &FormatStrRef, size_t &StrLen,
- ASTContext &Context) {
- if (const auto *Format = dyn_cast<StringLiteral>(FormatExpr);
- Format && (Format->isOrdinary() || Format->isUTF8())) {
- FormatStrRef = Format->getString();
- const ConstantArrayType *T =
- Context.getAsConstantArrayType(Format->getType());
- assert(T && "String literal not of constant array type!");
- size_t TypeSize = T->getZExtSize();
- // In case there's a null byte somewhere.
- StrLen = std::min(std::max(TypeSize, size_t(1)) - 1, FormatStrRef.find(0));
- return true;
- }
- return false;
-}
-
-void Sema::checkFortifiedBuiltinMemoryFunction(FunctionDecl *FD,
- CallExpr *TheCall) {
- if (TheCall->isValueDependent() || TheCall->isTypeDependent() ||
- isConstantEvaluatedContext())
- return;
-
- bool UseDABAttr = false;
- const FunctionDecl *UseDecl = FD;
-
- const auto *DABAttr = FD->getAttr<DiagnoseAsBuiltinAttr>();
- if (DABAttr) {
- UseDecl = DABAttr->getFunction();
- assert(UseDecl && "Missing FunctionDecl in DiagnoseAsBuiltin attribute!");
- UseDABAttr = true;
- }
-
- unsigned BuiltinID = UseDecl->getBuiltinID(/*ConsiderWrappers=*/true);
-
- if (!BuiltinID)
- return;
-
- const TargetInfo &TI = getASTContext().getTargetInfo();
- unsigned SizeTypeWidth = TI.getTypeWidth(TI.getSizeType());
-
- auto TranslateIndex = [&](unsigned Index) -> std::optional<unsigned> {
- // If we refer to a diagnose_as_builtin attribute, we need to change the
- // argument index to refer to the arguments of the called function. Unless
- // the index is out of bounds, which presumably means it's a variadic
- // function.
- if (!UseDABAttr)
- return Index;
- unsigned DABIndices = DABAttr->argIndices_size();
- unsigned NewIndex = Index < DABIndices
- ? DABAttr->argIndices_begin()[Index]
- : Index - DABIndices + FD->getNumParams();
- if (NewIndex >= TheCall->getNumArgs())
- return std::nullopt;
- return NewIndex;
- };
-
- auto ComputeExplicitObjectSizeArgument =
- [&](unsigned Index) -> std::optional<llvm::APSInt> {
- std::optional<unsigned> IndexOptional = TranslateIndex(Index);
- if (!IndexOptional)
- return std::nullopt;
- unsigned NewIndex = *IndexOptional;
- Expr::EvalResult Result;
- Expr *SizeArg = TheCall->getArg(NewIndex);
- if (!SizeArg->EvaluateAsInt(Result, getASTContext()))
- return std::nullopt;
- llvm::APSInt Integer = Result.Val.getInt();
- Integer.setIsUnsigned(true);
- return Integer;
- };
-
- auto ComputeSizeArgument =
- [&](unsigned Index) -> std::optional<llvm::APSInt> {
- // If the parameter has a pass_object_size attribute, then we should use its
- // (potentially) more strict checking mode. Otherwise, conservatively assume
- // type 0.
- int BOSType = 0;
- // This check can fail for variadic functions.
- if (Index < FD->getNumParams()) {
- if (const auto *POS =
- FD->getParamDecl(Index)->getAttr<PassObjectSizeAttr>())
- BOSType = POS->getType();
- }
-
- std::optional<unsigned> IndexOptional = TranslateIndex(Index);
- if (!IndexOptional)
- return std::nullopt;
- unsigned NewIndex = *IndexOptional;
-
- if (NewIndex >= TheCall->getNumArgs())
- return std::nullopt;
-
- const Expr *ObjArg = TheCall->getArg(NewIndex);
- if (std::optional<uint64_t> ObjSize =
- ObjArg->tryEvaluateObjectSize(getASTContext(), BOSType)) {
- // Get the object size in the target's size_t width.
- return llvm::APSInt::getUnsigned(*ObjSize).extOrTrunc(SizeTypeWidth);
- }
- return std::nullopt;
- };
-
- auto ComputeStrLenArgument =
- [&](unsigned Index) -> std::optional<llvm::APSInt> {
- std::optional<unsigned> IndexOptional = TranslateIndex(Index);
- if (!IndexOptional)
- return std::nullopt;
- unsigned NewIndex = *IndexOptional;
-
- const Expr *ObjArg = TheCall->getArg(NewIndex);
-
- if (std::optional<uint64_t> Result =
- ObjArg->tryEvaluateStrLen(getASTContext())) {
- // Add 1 for null byte.
- return llvm::APSInt::getUnsigned(*Result + 1).extOrTrunc(SizeTypeWidth);
- }
- return std::nullopt;
- };
-
- std::optional<llvm::APSInt> SourceSize;
- std::optional<llvm::APSInt> DestinationSize;
- unsigned DiagID = 0;
- bool IsChkVariant = false;
-
- auto GetFunctionName = [&]() {
- std::string FunctionNameStr =
- getASTContext().BuiltinInfo.getName(BuiltinID);
- llvm::StringRef FunctionName = FunctionNameStr;
- // Skim off the details of whichever builtin was called to produce a better
- // diagnostic, as it's unlikely that the user wrote the __builtin
- // explicitly.
- if (IsChkVariant) {
- FunctionName = FunctionName.drop_front(std::strlen("__builtin___"));
- FunctionName = FunctionName.drop_back(std::strlen("_chk"));
- } else {
- FunctionName.consume_front("__builtin_");
- }
- return FunctionName.str();
- };
-
- switch (BuiltinID) {
- default:
- return;
- case Builtin::BI__builtin_strcat:
- case Builtin::BIstrcat:
- case Builtin::BI__builtin_stpcpy:
- case Builtin::BIstpcpy:
- case Builtin::BI__builtin_strcpy:
- case Builtin::BIstrcpy: {
- DiagID = diag::warn_fortify_strlen_overflow;
- SourceSize = ComputeStrLenArgument(1);
- DestinationSize = ComputeSizeArgument(0);
- break;
- }
-
- case Builtin::BI__builtin___strcat_chk:
- case Builtin::BI__builtin___stpcpy_chk:
- case Builtin::BI__builtin___strcpy_chk: {
- DiagID = diag::warn_fortify_strlen_overflow;
- SourceSize = ComputeStrLenArgument(1);
- DestinationSize = ComputeExplicitObjectSizeArgument(2);
- IsChkVariant = true;
- break;
- }
-
- case Builtin::BIscanf:
- case Builtin::BIfscanf:
- case Builtin::BIsscanf: {
- unsigned FormatIndex = 1;
- unsigned DataIndex = 2;
- if (BuiltinID == Builtin::BIscanf) {
- FormatIndex = 0;
- DataIndex = 1;
- }
-
- const auto *FormatExpr =
- TheCall->getArg(FormatIndex)->IgnoreParenImpCasts();
-
- StringRef FormatStrRef;
- size_t StrLen;
- if (!ProcessFormatStringLiteral(FormatExpr, FormatStrRef, StrLen, Context))
- return;
-
- auto Diagnose = [&](unsigned ArgIndex, unsigned DestSize,
- unsigned SourceSize) {
- DiagID = diag::warn_fortify_scanf_overflow;
- unsigned Index = ArgIndex + DataIndex;
- std::string FunctionName = GetFunctionName();
- DiagRuntimeBehavior(TheCall->getArg(Index)->getBeginLoc(), TheCall,
- PDiag(DiagID) << FunctionName << (Index + 1)
- << DestSize << SourceSize);
- };
-
- auto ShiftedComputeSizeArgument = [&](unsigned Index) {
- return ComputeSizeArgument(Index + DataIndex);
- };
- ScanfDiagnosticFormatHandler H(ShiftedComputeSizeArgument, Diagnose);
- const char *FormatBytes = FormatStrRef.data();
- analyze_format_string::ParseScanfString(H, FormatBytes,
- FormatBytes + StrLen, getLangOpts(),
- Context.getTargetInfo());
-
- // Unlike the other cases, in this one we have already issued the diagnostic
- // here, so no need to continue (because unlike the other cases, here the
- // diagnostic refers to the argument number).
- return;
- }
-
- case Builtin::BIsprintf:
- case Builtin::BI__builtin___sprintf_chk: {
- size_t FormatIndex = BuiltinID == Builtin::BIsprintf ? 1 : 3;
- auto *FormatExpr = TheCall->getArg(FormatIndex)->IgnoreParenImpCasts();
-
- StringRef FormatStrRef;
- size_t StrLen;
- if (ProcessFormatStringLiteral(FormatExpr, FormatStrRef, StrLen, Context)) {
- EstimateSizeFormatHandler H(FormatStrRef);
- const char *FormatBytes = FormatStrRef.data();
- if (!analyze_format_string::ParsePrintfString(
- H, FormatBytes, FormatBytes + StrLen, getLangOpts(),
- Context.getTargetInfo(), false)) {
- DiagID = H.isKernelCompatible()
- ? diag::warn_format_overflow
- : diag::warn_format_overflow_non_kprintf;
- SourceSize = llvm::APSInt::getUnsigned(H.getSizeLowerBound())
- .extOrTrunc(SizeTypeWidth);
- if (BuiltinID == Builtin::BI__builtin___sprintf_chk) {
- DestinationSize = ComputeExplicitObjectSizeArgument(2);
- IsChkVariant = true;
- } else {
- DestinationSize = ComputeSizeArgument(0);
- }
- break;
- }
- }
- return;
- }
- case Builtin::BI__builtin___memcpy_chk:
- case Builtin::BI__builtin___memmove_chk:
- case Builtin::BI__builtin___memset_chk:
- case Builtin::BI__builtin___strlcat_chk:
- case Builtin::BI__builtin___strlcpy_chk:
- case Builtin::BI__builtin___strncat_chk:
- case Builtin::BI__builtin___strncpy_chk:
- case Builtin::BI__builtin___stpncpy_chk:
- case Builtin::BI__builtin___memccpy_chk:
- case Builtin::BI__builtin___mempcpy_chk: {
- DiagID = diag::warn_builtin_chk_overflow;
- SourceSize = ComputeExplicitObjectSizeArgument(TheCall->getNumArgs() - 2);
- DestinationSize =
- ComputeExplicitObjectSizeArgument(TheCall->getNumArgs() - 1);
- IsChkVariant = true;
- break;
- }
-
- case Builtin::BI__builtin___snprintf_chk:
- case Builtin::BI__builtin___vsnprintf_chk: {
- DiagID = diag::warn_builtin_chk_overflow;
- SourceSize = ComputeExplicitObjectSizeArgument(1);
- DestinationSize = ComputeExplicitObjectSizeArgument(3);
- IsChkVariant = true;
- break;
- }
-
- case Builtin::BIstrncat:
- case Builtin::BI__builtin_strncat:
- case Builtin::BIstrncpy:
- case Builtin::BI__builtin_strncpy:
- case Builtin::BIstpncpy:
- case Builtin::BI__builtin_stpncpy: {
- // Whether these functions overflow depends on the runtime strlen of the
- // string, not just the buffer size, so emitting the "always overflow"
- // diagnostic isn't quite right. We should still diagnose passing a buffer
- // size larger than the destination buffer though; this is a runtime abort
- // in _FORTIFY_SOURCE mode, and is quite suspicious otherwise.
- DiagID = diag::warn_fortify_source_size_mismatch;
- SourceSize = ComputeExplicitObjectSizeArgument(TheCall->getNumArgs() - 1);
- DestinationSize = ComputeSizeArgument(0);
- break;
- }
-
- case Builtin::BImemcpy:
- case Builtin::BI__builtin_memcpy:
- case Builtin::BImemmove:
- case Builtin::BI__builtin_memmove:
- case Builtin::BImemset:
- case Builtin::BI__builtin_memset:
- case Builtin::BImempcpy:
- case Builtin::BI__builtin_mempcpy: {
- DiagID = diag::warn_fortify_source_overflow;
- SourceSize = ComputeExplicitObjectSizeArgument(TheCall->getNumArgs() - 1);
- DestinationSize = ComputeSizeArgument(0);
- break;
- }
- case Builtin::BIsnprintf:
- case Builtin::BI__builtin_snprintf:
- case Builtin::BIvsnprintf:
- case Builtin::BI__builtin_vsnprintf: {
- DiagID = diag::warn_fortify_source_size_mismatch;
- SourceSize = ComputeExplicitObjectSizeArgument(1);
- const auto *FormatExpr = TheCall->getArg(2)->IgnoreParenImpCasts();
- StringRef FormatStrRef;
- size_t StrLen;
- if (SourceSize &&
- ProcessFormatStringLiteral(FormatExpr, FormatStrRef, StrLen, Context)) {
- EstimateSizeFormatHandler H(FormatStrRef);
- const char *FormatBytes = FormatStrRef.data();
- if (!analyze_format_string::ParsePrintfString(
- H, FormatBytes, FormatBytes + StrLen, getLangOpts(),
- Context.getTargetInfo(), /*isFreeBSDKPrintf=*/false)) {
- llvm::APSInt FormatSize =
- llvm::APSInt::getUnsigned(H.getSizeLowerBound())
- .extOrTrunc(SizeTypeWidth);
- if (FormatSize > *SourceSize && *SourceSize != 0) {
- unsigned TruncationDiagID =
- H.isKernelCompatible() ? diag::warn_format_truncation
- : diag::warn_format_truncation_non_kprintf;
- SmallString<16> SpecifiedSizeStr;
- SmallString<16> FormatSizeStr;
- SourceSize->toString(SpecifiedSizeStr, /*Radix=*/10);
- FormatSize.toString(FormatSizeStr, /*Radix=*/10);
- DiagRuntimeBehavior(TheCall->getBeginLoc(), TheCall,
- PDiag(TruncationDiagID)
- << GetFunctionName() << SpecifiedSizeStr
- << FormatSizeStr);
- }
- }
- }
- DestinationSize = ComputeSizeArgument(0);
- }
- }
-
- if (!SourceSize || !DestinationSize ||
- llvm::APSInt::compareValues(*SourceSize, *DestinationSize) <= 0)
- return;
-
- std::string FunctionName = GetFunctionName();
-
- SmallString<16> DestinationStr;
- SmallString<16> SourceStr;
- DestinationSize->toString(DestinationStr, /*Radix=*/10);
- SourceSize->toString(SourceStr, /*Radix=*/10);
- DiagRuntimeBehavior(TheCall->getBeginLoc(), TheCall,
- PDiag(DiagID)
- << FunctionName << DestinationStr << SourceStr);
-}
-
-static bool BuiltinSEHScopeCheck(Sema &SemaRef, CallExpr *TheCall,
- Scope::ScopeFlags NeededScopeFlags,
- unsigned DiagID) {
- // Scopes aren't available during instantiation. Fortunately, builtin
- // functions cannot be template args so they cannot be formed through template
- // instantiation. Therefore checking once during the parse is sufficient.
- if (SemaRef.inTemplateInstantiation())
- return false;
-
- Scope *S = SemaRef.getCurScope();
- while (S && !S->isSEHExceptScope())
- S = S->getParent();
- if (!S || !(S->getFlags() & NeededScopeFlags)) {
- auto *DRE = cast<DeclRefExpr>(TheCall->getCallee()->IgnoreParenCasts());
- SemaRef.Diag(TheCall->getExprLoc(), DiagID)
- << DRE->getDecl()->getIdentifier();
- return true;
- }
-
- return false;
-}
-
-// In OpenCL, __builtin_alloca_* should return a pointer to address space
-// that corresponds to the stack address space i.e private address space.
-static void builtinAllocaAddrSpace(Sema &S, CallExpr *TheCall) {
- QualType RT = TheCall->getType();
- assert((RT->isPointerType() && !(RT->getPointeeType().hasAddressSpace())) &&
- "__builtin_alloca has invalid address space");
-
- RT = RT->getPointeeType();
- RT = S.Context.getAddrSpaceQualType(RT, LangAS::opencl_private);
- TheCall->setType(S.Context.getPointerType(RT));
-}
-
-static bool checkBuiltinInferAllocToken(Sema &S, CallExpr *TheCall) {
- if (S.checkArgCountAtLeast(TheCall, 1))
- return true;
-
- for (Expr *Arg : TheCall->arguments()) {
- // If argument is dependent on a template parameter, we can't resolve now.
- if (Arg->isTypeDependent() || Arg->isValueDependent())
- continue;
- // Reject void types.
- QualType ArgTy = Arg->IgnoreParenImpCasts()->getType();
- if (ArgTy->isVoidType())
- return S.Diag(Arg->getBeginLoc(), diag::err_param_with_void_type);
- }
-
- TheCall->setType(S.Context.getSizeType());
- return false;
-}
-
-namespace {
-enum PointerAuthOpKind {
- PAO_Strip,
- PAO_Sign,
- PAO_Auth,
- PAO_SignGeneric,
- PAO_Discriminator,
- PAO_BlendPointer,
- PAO_BlendInteger
-};
-}
-
-bool Sema::checkPointerAuthEnabled(SourceLocation Loc, SourceRange Range) {
- if (getLangOpts().PointerAuthIntrinsics)
- return false;
-
- Diag(Loc, diag::err_ptrauth_disabled) << Range;
- return true;
-}
-
-static bool checkPointerAuthEnabled(Sema &S, Expr *E) {
- return S.checkPointerAuthEnabled(E->getExprLoc(), E->getSourceRange());
-}
-
-static bool checkPointerAuthKey(Sema &S, Expr *&Arg) {
- // Convert it to type 'int'.
- if (convertArgumentToType(S, Arg, S.Context.IntTy))
- return true;
-
- // Value-dependent expressions are okay; wait for template instantiation.
- if (Arg->isValueDependent())
- return false;
-
- unsigned KeyValue;
- return S.checkConstantPointerAuthKey(Arg, KeyValue);
-}
-
-bool Sema::checkConstantPointerAuthKey(Expr *Arg, unsigned &Result) {
- // Attempt to constant-evaluate the expression.
- std::optional<llvm::APSInt> KeyValue = Arg->getIntegerConstantExpr(Context);
- if (!KeyValue) {
- Diag(Arg->getExprLoc(), diag::err_expr_not_ice)
- << 0 << Arg->getSourceRange();
- return true;
- }
-
- // Ask the target to validate the key parameter.
- if (!Context.getTargetInfo().validatePointerAuthKey(*KeyValue)) {
- llvm::SmallString<32> Value;
- {
- llvm::raw_svector_ostream Str(Value);
- Str << *KeyValue;
- }
-
- Diag(Arg->getExprLoc(), diag::err_ptrauth_invalid_key)
- << Value << Arg->getSourceRange();
- return true;
- }
-
- Result = KeyValue->getZExtValue();
- return false;
-}
-
-bool Sema::checkPointerAuthDiscriminatorArg(Expr *Arg,
- PointerAuthDiscArgKind Kind,
- unsigned &IntVal) {
- if (!Arg) {
- IntVal = 0;
- return true;
- }
-
- std::optional<llvm::APSInt> Result = Arg->getIntegerConstantExpr(Context);
- if (!Result) {
- Diag(Arg->getExprLoc(), diag::err_ptrauth_arg_not_ice);
- return false;
- }
-
- unsigned Max;
- bool IsAddrDiscArg = false;
-
- switch (Kind) {
- case PointerAuthDiscArgKind::Addr:
- Max = 1;
- IsAddrDiscArg = true;
- break;
- case PointerAuthDiscArgKind::Extra:
- Max = PointerAuthQualifier::MaxDiscriminator;
- break;
- };
-
- if (*Result < 0 || *Result > Max) {
- if (IsAddrDiscArg)
- Diag(Arg->getExprLoc(), diag::err_ptrauth_address_discrimination_invalid)
- << Result->getExtValue();
- else
- Diag(Arg->getExprLoc(), diag::err_ptrauth_extra_discriminator_invalid)
- << Result->getExtValue() << Max;
-
- return false;
- };
-
- IntVal = Result->getZExtValue();
- return true;
-}
-
-static std::pair<const ValueDecl *, CharUnits>
-findConstantBaseAndOffset(Sema &S, Expr *E) {
- // Must evaluate as a pointer.
- Expr::EvalResult Result;
- if (!E->EvaluateAsRValue(Result, S.Context) || !Result.Val.isLValue())
- return {nullptr, CharUnits()};
-
- const auto *BaseDecl =
- Result.Val.getLValueBase().dyn_cast<const ValueDecl *>();
- if (!BaseDecl)
- return {nullptr, CharUnits()};
-
- return {BaseDecl, Result.Val.getLValueOffset()};
-}
-
-static bool checkPointerAuthValue(Sema &S, Expr *&Arg, PointerAuthOpKind OpKind,
- bool RequireConstant = false) {
- if (Arg->hasPlaceholderType()) {
- ExprResult R = S.CheckPlaceholderExpr(Arg);
- if (R.isInvalid())
- return true;
- Arg = R.get();
- }
-
- auto AllowsPointer = [](PointerAuthOpKind OpKind) {
- return OpKind != PAO_BlendInteger;
- };
- auto AllowsInteger = [](PointerAuthOpKind OpKind) {
- return OpKind == PAO_Discriminator || OpKind == PAO_BlendInteger ||
- OpKind == PAO_SignGeneric;
- };
-
- // Require the value to have the right range of type.
- QualType ExpectedTy;
- if (AllowsPointer(OpKind) && Arg->getType()->isPointerType()) {
- ExpectedTy = Arg->getType().getUnqualifiedType();
- } else if (AllowsPointer(OpKind) && Arg->getType()->isNullPtrType()) {
- ExpectedTy = S.Context.VoidPtrTy;
- } else if (AllowsInteger(OpKind) &&
- Arg->getType()->isIntegralOrUnscopedEnumerationType()) {
- ExpectedTy = S.Context.getUIntPtrType();
-
- } else {
- // Diagnose the failures.
- S.Diag(Arg->getExprLoc(), diag::err_ptrauth_value_bad_type)
- << unsigned(OpKind == PAO_Discriminator ? 1
- : OpKind == PAO_BlendPointer ? 2
- : OpKind == PAO_BlendInteger ? 3
- : 0)
- << unsigned(AllowsInteger(OpKind) ? (AllowsPointer(OpKind) ? 2 : 1) : 0)
- << Arg->getType() << Arg->getSourceRange();
- return true;
- }
-
- // Convert to that type. This should just be an lvalue-to-rvalue
- // conversion.
- if (convertArgumentToType(S, Arg, ExpectedTy))
- return true;
-
- if (!RequireConstant) {
- // Warn about null pointers for non-generic sign and auth operations.
- if ((OpKind == PAO_Sign || OpKind == PAO_Auth) &&
- Arg->isNullPointerConstant(S.Context, Expr::NPC_ValueDependentIsNull)) {
- S.Diag(Arg->getExprLoc(), OpKind == PAO_Sign
- ? diag::warn_ptrauth_sign_null_pointer
- : diag::warn_ptrauth_auth_null_pointer)
- << Arg->getSourceRange();
- }
-
- return false;
- }
-
- // Perform special checking on the arguments to ptrauth_sign_constant.
-
- // The main argument.
- if (OpKind == PAO_Sign) {
- // Require the value we're signing to have a special form.
- auto [BaseDecl, Offset] = findConstantBaseAndOffset(S, Arg);
- bool Invalid;
-
- // Must be rooted in a declaration reference.
- if (!BaseDecl)
- Invalid = true;
-
- // If it's a function declaration, we can't have an offset.
- else if (isa<FunctionDecl>(BaseDecl))
- Invalid = !Offset.isZero();
-
- // Otherwise we're fine.
- else
- Invalid = false;
-
- if (Invalid)
- S.Diag(Arg->getExprLoc(), diag::err_ptrauth_bad_constant_pointer);
- return Invalid;
- }
-
- // The discriminator argument.
- assert(OpKind == PAO_Discriminator);
-
- // Must be a pointer or integer or blend thereof.
- Expr *Pointer = nullptr;
- Expr *Integer = nullptr;
- if (auto *Call = dyn_cast<CallExpr>(Arg->IgnoreParens())) {
- if (Call->getBuiltinCallee() ==
- Builtin::BI__builtin_ptrauth_blend_discriminator) {
- Pointer = Call->getArg(0);
- Integer = Call->getArg(1);
- }
- }
- if (!Pointer && !Integer) {
- if (Arg->getType()->isPointerType())
- Pointer = Arg;
- else
- Integer = Arg;
- }
-
- // Check the pointer.
- bool Invalid = false;
- if (Pointer) {
- assert(Pointer->getType()->isPointerType());
-
- // TODO: if we're initializing a global, check that the address is
- // somehow related to what we're initializing. This probably will
- // never really be feasible and we'll have to catch it at link-time.
- auto [BaseDecl, Offset] = findConstantBaseAndOffset(S, Pointer);
- if (!BaseDecl || !isa<VarDecl>(BaseDecl))
- Invalid = true;
- }
-
- // Check the integer.
- if (Integer) {
- assert(Integer->getType()->isIntegerType());
- if (!Integer->isEvaluatable(S.Context))
- Invalid = true;
- }
-
- if (Invalid)
- S.Diag(Arg->getExprLoc(), diag::err_ptrauth_bad_constant_discriminator);
- return Invalid;
-}
-
-static ExprResult PointerAuthStrip(Sema &S, CallExpr *Call) {
- if (S.checkArgCount(Call, 2))
- return ExprError();
- if (checkPointerAuthEnabled(S, Call))
- return ExprError();
- if (checkPointerAuthValue(S, Call->getArgs()[0], PAO_Strip) ||
- checkPointerAuthKey(S, Call->getArgs()[1]))
- return ExprError();
-
- Call->setType(Call->getArgs()[0]->getType());
- return Call;
-}
-
-static ExprResult PointerAuthBlendDiscriminator(Sema &S, CallExpr *Call) {
- if (S.checkArgCount(Call, 2))
- return ExprError();
- if (checkPointerAuthEnabled(S, Call))
- return ExprError();
- if (checkPointerAuthValue(S, Call->getArgs()[0], PAO_BlendPointer) ||
- checkPointerAuthValue(S, Call->getArgs()[1], PAO_BlendInteger))
- return ExprError();
-
- Call->setType(S.Context.getUIntPtrType());
- return Call;
-}
-
-static ExprResult PointerAuthSignGenericData(Sema &S, CallExpr *Call) {
- if (S.checkArgCount(Call, 2))
- return ExprError();
- if (checkPointerAuthEnabled(S, Call))
- return ExprError();
- if (checkPointerAuthValue(S, Call->getArgs()[0], PAO_SignGeneric) ||
- checkPointerAuthValue(S, Call->getArgs()[1], PAO_Discriminator))
- return ExprError();
-
- Call->setType(S.Context.getUIntPtrType());
- return Call;
-}
-
-static ExprResult PointerAuthSignOrAuth(Sema &S, CallExpr *Call,
- PointerAuthOpKind OpKind,
- bool RequireConstant) {
- if (S.checkArgCount(Call, 3))
- return ExprError();
- if (checkPointerAuthEnabled(S, Call))
- return ExprError();
- if (checkPointerAuthValue(S, Call->getArgs()[0], OpKind, RequireConstant) ||
- checkPointerAuthKey(S, Call->getArgs()[1]) ||
- checkPointerAuthValue(S, Call->getArgs()[2], PAO_Discriminator,
- RequireConstant))
- return ExprError();
-
- Call->setType(Call->getArgs()[0]->getType());
- return Call;
-}
-
-static ExprResult PointerAuthAuthAndResign(Sema &S, CallExpr *Call) {
- if (S.checkArgCount(Call, 5))
- return ExprError();
- if (checkPointerAuthEnabled(S, Call))
- return ExprError();
- if (checkPointerAuthValue(S, Call->getArgs()[0], PAO_Auth) ||
- checkPointerAuthKey(S, Call->getArgs()[1]) ||
- checkPointerAuthValue(S, Call->getArgs()[2], PAO_Discriminator) ||
- checkPointerAuthKey(S, Call->getArgs()[3]) ||
- checkPointerAuthValue(S, Call->getArgs()[4], PAO_Discriminator))
- return ExprError();
-
- Call->setType(Call->getArgs()[0]->getType());
- return Call;
-}
-
-static ExprResult PointerAuthAuthLoadRelativeAndSign(Sema &S, CallExpr *Call) {
- if (S.checkArgCount(Call, 6))
- return ExprError();
- if (checkPointerAuthEnabled(S, Call))
- return ExprError();
- const Expr *AddendExpr = Call->getArg(5);
- bool AddendIsConstInt = AddendExpr->isIntegerConstantExpr(S.Context);
- if (!AddendIsConstInt) {
- const Expr *Arg = Call->getArg(5)->IgnoreParenImpCasts();
- DeclRefExpr *DRE = cast<DeclRefExpr>(Call->getCallee()->IgnoreParenCasts());
- FunctionDecl *FDecl = cast<FunctionDecl>(DRE->getDecl());
- S.Diag(Arg->getBeginLoc(), diag::err_constant_integer_last_arg_type)
- << FDecl->getDeclName() << Arg->getSourceRange();
- }
- if (checkPointerAuthValue(S, Call->getArgs()[0], PAO_Auth) ||
- checkPointerAuthKey(S, Call->getArgs()[1]) ||
- checkPointerAuthValue(S, Call->getArgs()[2], PAO_Discriminator) ||
- checkPointerAuthKey(S, Call->getArgs()[3]) ||
- checkPointerAuthValue(S, Call->getArgs()[4], PAO_Discriminator) ||
- !AddendIsConstInt)
- return ExprError();
-
- Call->setType(Call->getArgs()[0]->getType());
- return Call;
-}
-
-static ExprResult PointerAuthStringDiscriminator(Sema &S, CallExpr *Call) {
- if (checkPointerAuthEnabled(S, Call))
- return ExprError();
-
- // We've already performed normal call type-checking.
- const Expr *Arg = Call->getArg(0)->IgnoreParenImpCasts();
-
- // Operand must be an ordinary or UTF-8 string literal.
- const auto *Literal = dyn_cast<StringLiteral>(Arg);
- if (!Literal || Literal->getCharByteWidth() != 1) {
- S.Diag(Arg->getExprLoc(), diag::err_ptrauth_string_not_literal)
- << (Literal ? 1 : 0) << Arg->getSourceRange();
- return ExprError();
- }
-
- return Call;
-}
-
-static ExprResult GetVTablePointer(Sema &S, CallExpr *Call) {
- if (S.checkArgCount(Call, 1))
- return ExprError();
- Expr *FirstArg = Call->getArg(0);
- ExprResult FirstValue = S.DefaultFunctionArrayLvalueConversion(FirstArg);
- if (FirstValue.isInvalid())
- return ExprError();
- Call->setArg(0, FirstValue.get());
- QualType FirstArgType = FirstArg->getType();
- if (FirstArgType->canDecayToPointerType() && FirstArgType->isArrayType())
- FirstArgType = S.Context.getDecayedType(FirstArgType);
-
- const CXXRecordDecl *FirstArgRecord = FirstArgType->getPointeeCXXRecordDecl();
- if (!FirstArgRecord) {
- S.Diag(FirstArg->getBeginLoc(), diag::err_get_vtable_pointer_incorrect_type)
- << /*isPolymorphic=*/0 << FirstArgType;
- return ExprError();
- }
- if (S.RequireCompleteType(
- FirstArg->getBeginLoc(), FirstArgType->getPointeeType(),
- diag::err_get_vtable_pointer_requires_complete_type)) {
- return ExprError();
- }
-
- if (!FirstArgRecord->isPolymorphic()) {
- S.Diag(FirstArg->getBeginLoc(), diag::err_get_vtable_pointer_incorrect_type)
- << /*isPolymorphic=*/1 << FirstArgRecord;
- return ExprError();
- }
- QualType ReturnType = S.Context.getPointerType(S.Context.VoidTy.withConst());
- Call->setType(ReturnType);
- return Call;
-}
-
-static ExprResult BuiltinLaunder(Sema &S, CallExpr *TheCall) {
- if (S.checkArgCount(TheCall, 1))
- return ExprError();
-
- // Compute __builtin_launder's parameter type from the argument.
- // The parameter type is:
- // * The type of the argument if it's not an array or function type,
- // Otherwise,
- // * The decayed argument type.
- QualType ParamTy = [&]() {
- QualType ArgTy = TheCall->getArg(0)->getType();
- if (const ArrayType *Ty = ArgTy->getAsArrayTypeUnsafe())
- return S.Context.getPointerType(Ty->getElementType());
- if (ArgTy->isFunctionType()) {
- return S.Context.getPointerType(ArgTy);
- }
- return ArgTy;
- }();
-
- TheCall->setType(ParamTy);
-
- auto DiagSelect = [&]() -> std::optional<unsigned> {
- if (!ParamTy->isPointerType())
- return 0;
- if (ParamTy->isFunctionPointerType())
- return 1;
- if (ParamTy->isVoidPointerType())
- return 2;
- return std::optional<unsigned>{};
- }();
- if (DiagSelect) {
- S.Diag(TheCall->getBeginLoc(), diag::err_builtin_launder_invalid_arg)
- << *DiagSelect << TheCall->getSourceRange();
- return ExprError();
- }
-
- // We either have an incomplete class type, or we have a class template
- // whose instantiation has not been forced. Example:
- //
- // template <class T> struct Foo { T value; };
- // Foo<int> *p = nullptr;
- // auto *d = __builtin_launder(p);
- if (S.RequireCompleteType(TheCall->getBeginLoc(), ParamTy->getPointeeType(),
- diag::err_incomplete_type))
- return ExprError();
-
- assert(ParamTy->getPointeeType()->isObjectType() &&
- "Unhandled non-object pointer case");
-
- InitializedEntity Entity =
- InitializedEntity::InitializeParameter(S.Context, ParamTy, false);
- ExprResult Arg =
- S.PerformCopyInitialization(Entity, SourceLocation(), TheCall->getArg(0));
- if (Arg.isInvalid())
- return ExprError();
- TheCall->setArg(0, Arg.get());
-
- return TheCall;
-}
-
-static ExprResult BuiltinIsWithinLifetime(Sema &S, CallExpr *TheCall) {
- if (S.checkArgCount(TheCall, 1))
- return ExprError();
-
- ExprResult Arg = S.DefaultFunctionArrayLvalueConversion(TheCall->getArg(0));
- if (Arg.isInvalid())
- return ExprError();
- QualType ParamTy = Arg.get()->getType();
- TheCall->setArg(0, Arg.get());
- TheCall->setType(S.Context.BoolTy);
-
- // Only accept pointers to objects as arguments, which should have object
- // pointer or void pointer types.
- if (const auto *PT = ParamTy->getAs<PointerType>()) {
- // LWG4138: Function pointer types not allowed
- if (PT->getPointeeType()->isFunctionType()) {
- S.Diag(TheCall->getArg(0)->getExprLoc(),
- diag::err_builtin_is_within_lifetime_invalid_arg)
- << 1;
- return ExprError();
- }
- // Disallow VLAs too since those shouldn't be able to
- // be a template parameter for `std::is_within_lifetime`
- if (PT->getPointeeType()->isVariableArrayType()) {
- S.Diag(TheCall->getArg(0)->getExprLoc(), diag::err_vla_unsupported)
- << 1 << "__builtin_is_within_lifetime";
- return ExprError();
- }
- } else {
- S.Diag(TheCall->getArg(0)->getExprLoc(),
- diag::err_builtin_is_within_lifetime_invalid_arg)
- << 0;
- return ExprError();
- }
- return TheCall;
-}
-
-static ExprResult BuiltinTriviallyRelocate(Sema &S, CallExpr *TheCall) {
- if (S.checkArgCount(TheCall, 3))
- return ExprError();
-
- QualType Dest = TheCall->getArg(0)->getType();
- if (!Dest->isPointerType() || Dest.getCVRQualifiers() != 0) {
- S.Diag(TheCall->getArg(0)->getExprLoc(),
- diag::err_builtin_trivially_relocate_invalid_arg_type)
- << /*a pointer*/ 0;
- return ExprError();
- }
-
- QualType T = Dest->getPointeeType();
- if (S.RequireCompleteType(TheCall->getBeginLoc(), T,
- diag::err_incomplete_type))
- return ExprError();
-
- if (T.isConstQualified() || !S.IsCXXTriviallyRelocatableType(T) ||
- T->isIncompleteArrayType()) {
- S.Diag(TheCall->getArg(0)->getExprLoc(),
- diag::err_builtin_trivially_relocate_invalid_arg_type)
- << (T.isConstQualified() ? /*non-const*/ 1 : /*relocatable*/ 2);
- return ExprError();
- }
-
- TheCall->setType(Dest);
-
- QualType Src = TheCall->getArg(1)->getType();
- if (Src.getCanonicalType() != Dest.getCanonicalType()) {
- S.Diag(TheCall->getArg(1)->getExprLoc(),
- diag::err_builtin_trivially_relocate_invalid_arg_type)
- << /*the same*/ 3;
- return ExprError();
- }
-
- Expr *SizeExpr = TheCall->getArg(2);
- ExprResult Size = S.DefaultLvalueConversion(SizeExpr);
- if (Size.isInvalid())
- return ExprError();
-
- Size = S.tryConvertExprToType(Size.get(), S.getASTContext().getSizeType());
- if (Size.isInvalid())
- return ExprError();
- SizeExpr = Size.get();
- TheCall->setArg(2, SizeExpr);
-
- return TheCall;
-}
-
-// Emit an error and return true if the current object format type is in the
-// list of unsupported types.
-static bool CheckBuiltinTargetNotInUnsupported(
- Sema &S, unsigned BuiltinID, CallExpr *TheCall,
- ArrayRef<llvm::Triple::ObjectFormatType> UnsupportedObjectFormatTypes) {
- llvm::Triple::ObjectFormatType CurObjFormat =
- S.getASTContext().getTargetInfo().getTriple().getObjectFormat();
- if (llvm::is_contained(UnsupportedObjectFormatTypes, CurObjFormat)) {
- S.Diag(TheCall->getBeginLoc(), diag::err_builtin_target_unsupported)
- << TheCall->getSourceRange();
- return true;
- }
- return false;
-}
-
-// Emit an error and return true if the current architecture is not in the list
-// of supported architectures.
-static bool
-CheckBuiltinTargetInSupported(Sema &S, CallExpr *TheCall,
- ArrayRef<llvm::Triple::ArchType> SupportedArchs) {
- llvm::Triple::ArchType CurArch =
- S.getASTContext().getTargetInfo().getTriple().getArch();
- if (llvm::is_contained(SupportedArchs, CurArch))
- return false;
- S.Diag(TheCall->getBeginLoc(), diag::err_builtin_target_unsupported)
- << TheCall->getSourceRange();
- return true;
-}
-
-static void CheckNonNullArgument(Sema &S, const Expr *ArgExpr,
- SourceLocation CallSiteLoc);
-
-bool Sema::CheckTSBuiltinFunctionCall(const TargetInfo &TI, unsigned BuiltinID,
- CallExpr *TheCall) {
- switch (TI.getTriple().getArch()) {
- default:
- // Some builtins don't require additional checking, so just consider these
- // acceptable.
- return false;
- case llvm::Triple::arm:
- case llvm::Triple::armeb:
- case llvm::Triple::thumb:
- case llvm::Triple::thumbeb:
- return ARM().CheckARMBuiltinFunctionCall(TI, BuiltinID, TheCall);
- case llvm::Triple::aarch64:
- case llvm::Triple::aarch64_32:
- case llvm::Triple::aarch64_be:
- return ARM().CheckAArch64BuiltinFunctionCall(TI, BuiltinID, TheCall);
- case llvm::Triple::bpfeb:
- case llvm::Triple::bpfel:
- return BPF().CheckBPFBuiltinFunctionCall(BuiltinID, TheCall);
- case llvm::Triple::dxil:
- return DirectX().CheckDirectXBuiltinFunctionCall(BuiltinID, TheCall);
- case llvm::Triple::hexagon:
- return Hexagon().CheckHexagonBuiltinFunctionCall(BuiltinID, TheCall);
- case llvm::Triple::mips:
- case llvm::Triple::mipsel:
- case llvm::Triple::mips64:
- case llvm::Triple::mips64el:
- return MIPS().CheckMipsBuiltinFunctionCall(TI, BuiltinID, TheCall);
- case llvm::Triple::spirv:
- case llvm::Triple::spirv32:
- case llvm::Triple::spirv64:
- if (TI.getTriple().getOS() != llvm::Triple::OSType::AMDHSA)
- return SPIRV().CheckSPIRVBuiltinFunctionCall(TI, BuiltinID, TheCall);
- return false;
- case llvm::Triple::systemz:
- return SystemZ().CheckSystemZBuiltinFunctionCall(BuiltinID, TheCall);
- case llvm::Triple::x86:
- case llvm::Triple::x86_64:
- return X86().CheckBuiltinFunctionCall(TI, BuiltinID, TheCall);
- case llvm::Triple::ppc:
- case llvm::Triple::ppcle:
- case llvm::Triple::ppc64:
- case llvm::Triple::ppc64le:
- return PPC().CheckPPCBuiltinFunctionCall(TI, BuiltinID, TheCall);
- case llvm::Triple::amdgcn:
- return AMDGPU().CheckAMDGCNBuiltinFunctionCall(BuiltinID, TheCall);
- case llvm::Triple::riscv32:
- case llvm::Triple::riscv64:
- case llvm::Triple::riscv32be:
- case llvm::Triple::riscv64be:
- return RISCV().CheckBuiltinFunctionCall(TI, BuiltinID, TheCall);
- case llvm::Triple::loongarch32:
- case llvm::Triple::loongarch64:
- return LoongArch().CheckLoongArchBuiltinFunctionCall(TI, BuiltinID,
- TheCall);
- case llvm::Triple::wasm32:
- case llvm::Triple::wasm64:
- return Wasm().CheckWebAssemblyBuiltinFunctionCall(TI, BuiltinID, TheCall);
- case llvm::Triple::nvptx:
- case llvm::Triple::nvptx64:
- return NVPTX().CheckNVPTXBuiltinFunctionCall(TI, BuiltinID, TheCall);
- }
-}
-
-static bool isValidMathElementType(QualType T) {
- return T->isDependentType() ||
- (T->isRealType() && !T->isBooleanType() && !T->isEnumeralType());
-}
-
-// Check if \p Ty is a valid type for the elementwise math builtins. If it is
-// not a valid type, emit an error message and return true. Otherwise return
-// false.
-static bool
-checkMathBuiltinElementType(Sema &S, SourceLocation Loc, QualType ArgTy,
- Sema::EltwiseBuiltinArgTyRestriction ArgTyRestr,
- int ArgOrdinal) {
- QualType EltTy = ArgTy;
- if (auto *VecTy = EltTy->getAs<VectorType>())
- EltTy = VecTy->getElementType();
-
- switch (ArgTyRestr) {
- case Sema::EltwiseBuiltinArgTyRestriction::None:
- if (!ArgTy->getAs<VectorType>() && !isValidMathElementType(ArgTy)) {
- return S.Diag(Loc, diag::err_builtin_invalid_arg_type)
- << ArgOrdinal << /* vector */ 2 << /* integer */ 1 << /* fp */ 1
- << ArgTy;
- }
- break;
- case Sema::EltwiseBuiltinArgTyRestriction::FloatTy:
- if (!EltTy->isRealFloatingType()) {
- return S.Diag(Loc, diag::err_builtin_invalid_arg_type)
- << ArgOrdinal << /* scalar or vector */ 5 << /* no int */ 0
- << /* floating-point */ 1 << ArgTy;
- }
- break;
- case Sema::EltwiseBuiltinArgTyRestriction::IntegerTy:
- if (!EltTy->isIntegerType()) {
- return S.Diag(Loc, diag::err_builtin_invalid_arg_type)
- << ArgOrdinal << /* scalar or vector */ 5 << /* integer */ 1
- << /* no fp */ 0 << ArgTy;
- }
- break;
- case Sema::EltwiseBuiltinArgTyRestriction::SignedIntOrFloatTy:
- if (!EltTy->isSignedIntegerType() && !EltTy->isRealFloatingType()) {
- return S.Diag(Loc, diag::err_builtin_invalid_arg_type)
- << 1 << /* scalar or vector */ 5 << /* signed int */ 2
- << /* or fp */ 1 << ArgTy;
- }
- break;
- }
-
- return false;
-}
-
-/// BuiltinCpu{Supports|Is} - Handle __builtin_cpu_{supports|is}(char *).
-/// This checks that the target supports the builtin and that the string
-/// argument is constant and valid.
-static bool BuiltinCpu(Sema &S, const TargetInfo &TI, CallExpr *TheCall,
- const TargetInfo *AuxTI, unsigned BuiltinID) {
- assert((BuiltinID == Builtin::BI__builtin_cpu_supports ||
- BuiltinID == Builtin::BI__builtin_cpu_is) &&
- "Expecting __builtin_cpu_...");
-
- bool IsCPUSupports = BuiltinID == Builtin::BI__builtin_cpu_supports;
- const TargetInfo *TheTI = &TI;
- auto SupportsBI = [=](const TargetInfo *TInfo) {
- return TInfo && ((IsCPUSupports && TInfo->supportsCpuSupports()) ||
- (!IsCPUSupports && TInfo->supportsCpuIs()));
- };
- if (!SupportsBI(&TI) && SupportsBI(AuxTI))
- TheTI = AuxTI;
-
- if ((!IsCPUSupports && !TheTI->supportsCpuIs()) ||
- (IsCPUSupports && !TheTI->supportsCpuSupports()))
- return S.Diag(TheCall->getBeginLoc(),
- TI.getTriple().isOSAIX()
- ? diag::err_builtin_aix_os_unsupported
- : diag::err_builtin_target_unsupported)
- << SourceRange(TheCall->getBeginLoc(), TheCall->getEndLoc());
-
- Expr *Arg = TheCall->getArg(0)->IgnoreParenImpCasts();
- // Check if the argument is a string literal.
- if (!isa<StringLiteral>(Arg))
- return S.Diag(TheCall->getBeginLoc(), diag::err_expr_not_string_literal)
- << Arg->getSourceRange();
-
- // Check the contents of the string.
- StringRef Feature = cast<StringLiteral>(Arg)->getString();
- if (IsCPUSupports && !TheTI->validateCpuSupports(Feature)) {
- S.Diag(TheCall->getBeginLoc(), diag::warn_invalid_cpu_supports)
- << Arg->getSourceRange();
- return false;
- }
- if (!IsCPUSupports && !TheTI->validateCpuIs(Feature))
- return S.Diag(TheCall->getBeginLoc(), diag::err_invalid_cpu_is)
- << Arg->getSourceRange();
- return false;
-}
-
-/// Checks that __builtin_bswapg was called with a single argument, which is an
-/// unsigned integer, and overrides the return value type to the integer type.
-static bool BuiltinBswapg(Sema &S, CallExpr *TheCall) {
- if (S.checkArgCount(TheCall, 1))
- return true;
- ExprResult ArgRes = S.DefaultLvalueConversion(TheCall->getArg(0));
- if (ArgRes.isInvalid())
- return true;
-
- Expr *Arg = ArgRes.get();
- TheCall->setArg(0, Arg);
- if (Arg->isTypeDependent())
- return false;
-
- QualType ArgTy = Arg->getType();
-
- if (!ArgTy->isIntegerType()) {
- S.Diag(Arg->getBeginLoc(), diag::err_builtin_invalid_arg_type)
- << 1 << /*scalar=*/1 << /*unsigned integer=*/1 << /*floating point=*/0
- << ArgTy;
- return true;
- }
- if (const auto *BT = dyn_cast<BitIntType>(ArgTy)) {
- if (BT->getNumBits() % 16 != 0 && BT->getNumBits() != 8 &&
- BT->getNumBits() != 1) {
- S.Diag(Arg->getBeginLoc(), diag::err_bswapg_invalid_bit_width)
- << ArgTy << BT->getNumBits();
- return true;
- }
- }
- TheCall->setType(ArgTy);
- return false;
-}
-
-/// Checks that __builtin_popcountg was called with a single argument, which is
-/// an unsigned integer.
-static bool BuiltinPopcountg(Sema &S, CallExpr *TheCall) {
- if (S.checkArgCount(TheCall, 1))
- return true;
-
- ExprResult ArgRes = S.DefaultLvalueConversion(TheCall->getArg(0));
- if (ArgRes.isInvalid())
- return true;
-
- Expr *Arg = ArgRes.get();
- TheCall->setArg(0, Arg);
-
- QualType ArgTy = Arg->getType();
-
- if (!ArgTy->isUnsignedIntegerType() && !ArgTy->isExtVectorBoolType()) {
- S.Diag(Arg->getBeginLoc(), diag::err_builtin_invalid_arg_type)
- << 1 << /* scalar */ 1 << /* unsigned integer ty */ 3 << /* no fp */ 0
- << ArgTy;
- return true;
- }
- return false;
-}
-
-/// Checks that __builtin_{clzg,ctzg} was called with a first argument, which is
-/// an unsigned integer, and an optional second argument, which is promoted to
-/// an 'int'.
-static bool BuiltinCountZeroBitsGeneric(Sema &S, CallExpr *TheCall) {
- if (S.checkArgCountRange(TheCall, 1, 2))
- return true;
-
- ExprResult Arg0Res = S.DefaultLvalueConversion(TheCall->getArg(0));
- if (Arg0Res.isInvalid())
- return true;
-
- Expr *Arg0 = Arg0Res.get();
- TheCall->setArg(0, Arg0);
-
- QualType Arg0Ty = Arg0->getType();
-
- if (!Arg0Ty->isUnsignedIntegerType() && !Arg0Ty->isExtVectorBoolType()) {
- S.Diag(Arg0->getBeginLoc(), diag::err_builtin_invalid_arg_type)
- << 1 << /* scalar */ 1 << /* unsigned integer ty */ 3 << /* no fp */ 0
- << Arg0Ty;
- return true;
- }
-
- if (TheCall->getNumArgs() > 1) {
- ExprResult Arg1Res = S.UsualUnaryConversions(TheCall->getArg(1));
- if (Arg1Res.isInvalid())
- return true;
-
- Expr *Arg1 = Arg1Res.get();
- TheCall->setArg(1, Arg1);
-
- QualType Arg1Ty = Arg1->getType();
-
- if (!Arg1Ty->isSpecificBuiltinType(BuiltinType::Int)) {
- S.Diag(Arg1->getBeginLoc(), diag::err_builtin_invalid_arg_type)
- << 2 << /* scalar */ 1 << /* 'int' ty */ 4 << /* no fp */ 0 << Arg1Ty;
- return true;
- }
- }
-
- return false;
-}
-
-class RotateIntegerConverter : public Sema::ContextualImplicitConverter {
- unsigned ArgIndex;
- bool OnlyUnsigned;
-
- Sema::SemaDiagnosticBuilder emitError(Sema &S, SourceLocation Loc,
- QualType T) {
- return S.Diag(Loc, diag::err_builtin_invalid_arg_type)
- << ArgIndex << /*scalar*/ 1
- << (OnlyUnsigned ? /*unsigned integer*/ 3 : /*integer*/ 1)
- << /*no fp*/ 0 << T;
- }
-
-public:
- RotateIntegerConverter(unsigned ArgIndex, bool OnlyUnsigned)
- : ContextualImplicitConverter(/*Suppress=*/false,
- /*SuppressConversion=*/true),
- ArgIndex(ArgIndex), OnlyUnsigned(OnlyUnsigned) {}
-
- bool match(QualType T) override {
- return OnlyUnsigned ? T->isUnsignedIntegerType() : T->isIntegerType();
- }
-
- Sema::SemaDiagnosticBuilder diagnoseNoMatch(Sema &S, SourceLocation Loc,
- QualType T) override {
- return emitError(S, Loc, T);
- }
-
- Sema::SemaDiagnosticBuilder diagnoseIncomplete(Sema &S, SourceLocation Loc,
- QualType T) override {
- return emitError(S, Loc, T);
- }
-
- Sema::SemaDiagnosticBuilder diagnoseExplicitConv(Sema &S, SourceLocation Loc,
- QualType T,
- QualType ConvTy) override {
- return emitError(S, Loc, T);
- }
-
- Sema::SemaDiagnosticBuilder noteExplicitConv(Sema &S, CXXConversionDecl *Conv,
- QualType ConvTy) override {
- return S.Diag(Conv->getLocation(), diag::note_conv_function_declared_at);
- }
-
- Sema::SemaDiagnosticBuilder diagnoseAmbiguous(Sema &S, SourceLocation Loc,
- QualType T) override {
- return emitError(S, Loc, T);
- }
-
- Sema::SemaDiagnosticBuilder noteAmbiguous(Sema &S, CXXConversionDecl *Conv,
- QualType ConvTy) override {
- return S.Diag(Conv->getLocation(), diag::note_conv_function_declared_at);
- }
-
- Sema::SemaDiagnosticBuilder diagnoseConversion(Sema &S, SourceLocation Loc,
- QualType T,
- QualType ConvTy) override {
- llvm_unreachable("conversion functions are permitted");
- }
-};
-
-/// Checks that __builtin_stdc_rotate_{left,right} was called with two
-/// arguments, that the first argument is an unsigned integer type, and that
-/// the second argument is an integer type.
-static bool BuiltinRotateGeneric(Sema &S, CallExpr *TheCall) {
- if (S.checkArgCount(TheCall, 2))
- return true;
-
- // First argument (value to rotate) must be unsigned integer type.
- RotateIntegerConverter Arg0Converter(1, /*OnlyUnsigned=*/true);
- ExprResult Arg0Res = S.PerformContextualImplicitConversion(
- TheCall->getArg(0)->getBeginLoc(), TheCall->getArg(0), Arg0Converter);
- if (Arg0Res.isInvalid())
- return true;
-
- Expr *Arg0 = Arg0Res.get();
- TheCall->setArg(0, Arg0);
-
- QualType Arg0Ty = Arg0->getType();
- if (!Arg0Ty->isUnsignedIntegerType())
- return true;
-
- // Second argument (rotation count) must be integer type.
- RotateIntegerConverter Arg1Converter(2, /*OnlyUnsigned=*/false);
- ExprResult Arg1Res = S.PerformContextualImplicitConversion(
- TheCall->getArg(1)->getBeginLoc(), TheCall->getArg(1), Arg1Converter);
- if (Arg1Res.isInvalid())
- return true;
-
- Expr *Arg1 = Arg1Res.get();
- TheCall->setArg(1, Arg1);
-
- QualType Arg1Ty = Arg1->getType();
- if (!Arg1Ty->isIntegerType())
- return true;
-
- TheCall->setType(Arg0Ty);
- return false;
-}
-
-static bool CheckMaskedBuiltinArgs(Sema &S, Expr *MaskArg, Expr *PtrArg,
- unsigned Pos, bool AllowConst,
- bool AllowAS) {
- QualType MaskTy = MaskArg->getType();
- if (!MaskTy->isExtVectorBoolType())
- return S.Diag(MaskArg->getBeginLoc(), diag::err_builtin_invalid_arg_type)
- << 1 << /* vector of */ 4 << /* booleans */ 6 << /* no fp */ 0
- << MaskTy;
-
- QualType PtrTy = PtrArg->getType();
- if (!PtrTy->isPointerType() || PtrTy->getPointeeType()->isVectorType())
- return S.Diag(PtrArg->getExprLoc(), diag::err_vec_masked_load_store_ptr)
- << Pos << "scalar pointer";
-
- QualType PointeeTy = PtrTy->getPointeeType();
- if (PointeeTy.isVolatileQualified() || PointeeTy->isAtomicType() ||
- (!AllowConst && PointeeTy.isConstQualified()) ||
- (!AllowAS && PointeeTy.hasAddressSpace())) {
- QualType Target =
- S.Context.getPointerType(PointeeTy.getAtomicUnqualifiedType());
- return S.Diag(PtrArg->getExprLoc(),
- diag::err_typecheck_convert_incompatible)
- << PtrTy << Target << /*different qualifiers=*/5
- << /*qualifier difference=*/0 << /*parameter mismatch=*/3 << 2
- << PtrTy << Target;
- }
- return false;
-}
-
-static bool ConvertMaskedBuiltinArgs(Sema &S, CallExpr *TheCall) {
- bool TypeDependent = false;
- for (unsigned Arg = 0, E = TheCall->getNumArgs(); Arg != E; ++Arg) {
- ExprResult Converted =
- S.DefaultFunctionArrayLvalueConversion(TheCall->getArg(Arg));
- if (Converted.isInvalid())
- return true;
- TheCall->setArg(Arg, Converted.get());
- TypeDependent |= Converted.get()->isTypeDependent();
- }
-
- if (TypeDependent)
- TheCall->setType(S.Context.DependentTy);
- return false;
-}
-
-static ExprResult BuiltinMaskedLoad(Sema &S, CallExpr *TheCall) {
- if (S.checkArgCountRange(TheCall, 2, 3))
- return ExprError();
-
- if (ConvertMaskedBuiltinArgs(S, TheCall))
- return ExprError();
-
- Expr *MaskArg = TheCall->getArg(0);
- Expr *PtrArg = TheCall->getArg(1);
- if (TheCall->isTypeDependent())
- return TheCall;
-
- if (CheckMaskedBuiltinArgs(S, MaskArg, PtrArg, 2, /*AllowConst=*/true,
- TheCall->getBuiltinCallee() ==
- Builtin::BI__builtin_masked_load))
- return ExprError();
-
- QualType MaskTy = MaskArg->getType();
- QualType PtrTy = PtrArg->getType();
- QualType PointeeTy = PtrTy->getPointeeType();
- const VectorType *MaskVecTy = MaskTy->getAs<VectorType>();
-
- QualType RetTy = S.Context.getExtVectorType(PointeeTy.getUnqualifiedType(),
- MaskVecTy->getNumElements());
- if (TheCall->getNumArgs() == 3) {
- Expr *PassThruArg = TheCall->getArg(2);
- QualType PassThruTy = PassThruArg->getType();
- if (!S.Context.hasSameType(PassThruTy, RetTy))
- return S.Diag(PtrArg->getExprLoc(), diag::err_vec_masked_load_store_ptr)
- << /* third argument */ 3 << RetTy;
- }
-
- TheCall->setType(RetTy);
- return TheCall;
-}
-
-static ExprResult BuiltinMaskedStore(Sema &S, CallExpr *TheCall) {
- if (S.checkArgCount(TheCall, 3))
- return ExprError();
-
- if (ConvertMaskedBuiltinArgs(S, TheCall))
- return ExprError();
-
- Expr *MaskArg = TheCall->getArg(0);
- Expr *ValArg = TheCall->getArg(1);
- Expr *PtrArg = TheCall->getArg(2);
- if (TheCall->isTypeDependent())
- return TheCall;
-
- if (CheckMaskedBuiltinArgs(S, MaskArg, PtrArg, 3, /*AllowConst=*/false,
- TheCall->getBuiltinCallee() ==
- Builtin::BI__builtin_masked_store))
- return ExprError();
-
- QualType MaskTy = MaskArg->getType();
- QualType PtrTy = PtrArg->getType();
- QualType ValTy = ValArg->getType();
- if (!ValTy->isVectorType())
- return ExprError(
- S.Diag(ValArg->getExprLoc(), diag::err_vec_masked_load_store_ptr)
- << 2 << "vector");
-
- QualType PointeeTy = PtrTy->getPointeeType();
- const VectorType *MaskVecTy = MaskTy->getAs<VectorType>();
- QualType MemoryTy = S.Context.getExtVectorType(PointeeTy.getUnqualifiedType(),
- MaskVecTy->getNumElements());
- if (!S.Context.hasSameType(ValTy.getUnqualifiedType(),
- MemoryTy.getUnqualifiedType()))
- return ExprError(S.Diag(TheCall->getBeginLoc(),
- diag::err_vec_builtin_incompatible_vector)
- << TheCall->getDirectCallee() << /*isMorethantwoArgs*/ 2
- << SourceRange(TheCall->getArg(1)->getBeginLoc(),
- TheCall->getArg(1)->getEndLoc()));
-
- TheCall->setType(S.Context.VoidTy);
- return TheCall;
-}
-
-static ExprResult BuiltinMaskedGather(Sema &S, CallExpr *TheCall) {
- if (S.checkArgCountRange(TheCall, 3, 4))
- return ExprError();
-
- if (ConvertMaskedBuiltinArgs(S, TheCall))
- return ExprError();
-
- Expr *MaskArg = TheCall->getArg(0);
- Expr *IdxArg = TheCall->getArg(1);
- Expr *PtrArg = TheCall->getArg(2);
- if (TheCall->isTypeDependent())
- return TheCall;
-
- if (CheckMaskedBuiltinArgs(S, MaskArg, PtrArg, 3, /*AllowConst=*/true,
- /*AllowAS=*/true))
- return ExprError();
-
- QualType IdxTy = IdxArg->getType();
- const VectorType *IdxVecTy = IdxTy->getAs<VectorType>();
- if (!IdxTy->isExtVectorType() || !IdxVecTy->getElementType()->isIntegerType())
- return S.Diag(MaskArg->getBeginLoc(), diag::err_builtin_invalid_arg_type)
- << 1 << /* vector of */ 4 << /* integer */ 1 << /* no fp */ 0
- << IdxTy;
-
- QualType MaskTy = MaskArg->getType();
- QualType PtrTy = PtrArg->getType();
- QualType PointeeTy = PtrTy->getPointeeType();
- const VectorType *MaskVecTy = MaskTy->getAs<VectorType>();
- if (MaskVecTy->getNumElements() != IdxVecTy->getNumElements())
- return ExprError(
- S.Diag(TheCall->getBeginLoc(), diag::err_vec_masked_load_store_size)
- << S.getASTContext().BuiltinInfo.getQuotedName(
- TheCall->getBuiltinCallee())
- << MaskTy << IdxTy);
-
- QualType RetTy = S.Context.getExtVectorType(PointeeTy.getUnqualifiedType(),
- MaskVecTy->getNumElements());
- if (TheCall->getNumArgs() == 4) {
- Expr *PassThruArg = TheCall->getArg(3);
- QualType PassThruTy = PassThruArg->getType();
- if (!S.Context.hasSameType(PassThruTy, RetTy))
- return S.Diag(PassThruArg->getExprLoc(),
- diag::err_vec_masked_load_store_ptr)
- << /* fourth argument */ 4 << RetTy;
- }
-
- TheCall->setType(RetTy);
- return TheCall;
-}
-
-static ExprResult BuiltinMaskedScatter(Sema &S, CallExpr *TheCall) {
- if (S.checkArgCount(TheCall, 4))
- return ExprError();
-
- if (ConvertMaskedBuiltinArgs(S, TheCall))
- return ExprError();
-
- Expr *MaskArg = TheCall->getArg(0);
- Expr *IdxArg = TheCall->getArg(1);
- Expr *ValArg = TheCall->getArg(2);
- Expr *PtrArg = TheCall->getArg(3);
- if (TheCall->isTypeDependent())
- return TheCall;
-
- if (CheckMaskedBuiltinArgs(S, MaskArg, PtrArg, 4, /*AllowConst=*/false,
- /*AllowAS=*/true))
- return ExprError();
-
- QualType IdxTy = IdxArg->getType();
- const VectorType *IdxVecTy = IdxTy->getAs<VectorType>();
- if (!IdxTy->isExtVectorType() || !IdxVecTy->getElementType()->isIntegerType())
- return S.Diag(MaskArg->getBeginLoc(), diag::err_builtin_invalid_arg_type)
- << 2 << /* vector of */ 4 << /* integer */ 1 << /* no fp */ 0
- << IdxTy;
-
- QualType ValTy = ValArg->getType();
- QualType MaskTy = MaskArg->getType();
- QualType PtrTy = PtrArg->getType();
- QualType PointeeTy = PtrTy->getPointeeType();
-
- const VectorType *MaskVecTy = MaskTy->castAs<VectorType>();
- const VectorType *ValVecTy = ValTy->castAs<VectorType>();
- if (MaskVecTy->getNumElements() != IdxVecTy->getNumElements())
- return ExprError(
- S.Diag(TheCall->getBeginLoc(), diag::err_vec_masked_load_store_size)
- << S.getASTContext().BuiltinInfo.getQuotedName(
- TheCall->getBuiltinCallee())
- << MaskTy << IdxTy);
- if (MaskVecTy->getNumElements() != ValVecTy->getNumElements())
- return ExprError(
- S.Diag(TheCall->getBeginLoc(), diag::err_vec_masked_load_store_size)
- << S.getASTContext().BuiltinInfo.getQuotedName(
- TheCall->getBuiltinCallee())
- << MaskTy << ValTy);
-
- QualType ArgTy = S.Context.getExtVectorType(PointeeTy.getUnqualifiedType(),
- MaskVecTy->getNumElements());
- if (!S.Context.hasSameType(ValTy.getUnqualifiedType(), ArgTy))
- return ExprError(S.Diag(TheCall->getBeginLoc(),
- diag::err_vec_builtin_incompatible_vector)
- << TheCall->getDirectCallee() << /*isMoreThanTwoArgs*/ 2
- << SourceRange(TheCall->getArg(1)->getBeginLoc(),
- TheCall->getArg(1)->getEndLoc()));
-
- TheCall->setType(S.Context.VoidTy);
- return TheCall;
-}
-
-static ExprResult BuiltinInvoke(Sema &S, CallExpr *TheCall) {
- SourceLocation Loc = TheCall->getBeginLoc();
- MutableArrayRef Args(TheCall->getArgs(), TheCall->getNumArgs());
- assert(llvm::none_of(Args, [](Expr *Arg) { return Arg->isTypeDependent(); }));
-
- if (Args.size() == 0) {
- S.Diag(TheCall->getBeginLoc(),
- diag::err_typecheck_call_too_few_args_at_least)
- << /*callee_type=*/0 << /*min_arg_count=*/1 << /*actual_arg_count=*/0
- << /*is_non_object=*/0 << TheCall->getSourceRange();
- return ExprError();
- }
-
- QualType FuncT = Args[0]->getType();
-
- if (const auto *MPT = FuncT->getAs<MemberPointerType>()) {
- if (Args.size() < 2) {
- S.Diag(TheCall->getBeginLoc(),
- diag::err_typecheck_call_too_few_args_at_least)
- << /*callee_type=*/0 << /*min_arg_count=*/2 << /*actual_arg_count=*/1
- << /*is_non_object=*/0 << TheCall->getSourceRange();
- return ExprError();
- }
-
- const Type *MemPtrClass = MPT->getQualifier().getAsType();
- QualType ObjectT = Args[1]->getType();
-
- if (MPT->isMemberDataPointer() && S.checkArgCount(TheCall, 2))
- return ExprError();
-
- ExprResult ObjectArg = [&]() -> ExprResult {
- // (1.1): (t1.*f)(t2, ..., tN) when f is a pointer to a member function of
- // a class T and is_same_v<T, remove_cvref_t<decltype(t1)>> ||
- // is_base_of_v<T, remove_cvref_t<decltype(t1)>> is true;
- // (1.4): t1.*f when N=1 and f is a pointer to data member of a class T
- // and is_same_v<T, remove_cvref_t<decltype(t1)>> ||
- // is_base_of_v<T, remove_cvref_t<decltype(t1)>> is true;
- if (S.Context.hasSameType(QualType(MemPtrClass, 0),
- S.BuiltinRemoveCVRef(ObjectT, Loc)) ||
- S.BuiltinIsBaseOf(Args[1]->getBeginLoc(), QualType(MemPtrClass, 0),
- S.BuiltinRemoveCVRef(ObjectT, Loc))) {
- return Args[1];
- }
-
- // (t1.get().*f)(t2, ..., tN) when f is a pointer to a member function of
- // a class T and remove_cvref_t<decltype(t1)> is a specialization of
- // reference_wrapper;
- if (const auto *RD = ObjectT->getAsCXXRecordDecl()) {
- if (RD->isInStdNamespace() &&
- RD->getDeclName().getAsString() == "reference_wrapper") {
- CXXScopeSpec SS;
- IdentifierInfo *GetName = &S.Context.Idents.get("get");
- UnqualifiedId GetID;
- GetID.setIdentifier(GetName, Loc);
-
- ExprResult MemExpr = S.ActOnMemberAccessExpr(
- S.getCurScope(), Args[1], Loc, tok::period, SS,
- /*TemplateKWLoc=*/SourceLocation(), GetID, nullptr);
-
- if (MemExpr.isInvalid())
- return ExprError();
-
- return S.ActOnCallExpr(S.getCurScope(), MemExpr.get(), Loc, {}, Loc);
- }
- }
-
- // ((*t1).*f)(t2, ..., tN) when f is a pointer to a member function of a
- // class T and t1 does not satisfy the previous two items;
-
- return S.ActOnUnaryOp(S.getCurScope(), Loc, tok::star, Args[1]);
- }();
-
- if (ObjectArg.isInvalid())
- return ExprError();
-
- ExprResult BinOp = S.ActOnBinOp(S.getCurScope(), TheCall->getBeginLoc(),
- tok::periodstar, ObjectArg.get(), Args[0]);
- if (BinOp.isInvalid())
- return ExprError();
-
- if (MPT->isMemberDataPointer())
- return BinOp;
-
- auto *MemCall = new (S.Context)
- ParenExpr(SourceLocation(), SourceLocation(), BinOp.get());
-
- return S.ActOnCallExpr(S.getCurScope(), MemCall, TheCall->getBeginLoc(),
- Args.drop_front(2), TheCall->getRParenLoc());
- }
- return S.ActOnCallExpr(S.getCurScope(), Args.front(), TheCall->getBeginLoc(),
- Args.drop_front(), TheCall->getRParenLoc());
-}
-
-// Performs a similar job to Sema::UsualUnaryConversions, but without any
-// implicit promotion of integral/enumeration types.
-static ExprResult BuiltinVectorMathConversions(Sema &S, Expr *E) {
- // First, convert to an r-value.
- ExprResult Res = S.DefaultFunctionArrayLvalueConversion(E);
- if (Res.isInvalid())
- return ExprError();
-
- // Promote floating-point types.
- return S.UsualUnaryFPConversions(Res.get());
-}
-
-ExprResult
-Sema::CheckBuiltinFunctionCall(FunctionDecl *FDecl, unsigned BuiltinID,
- CallExpr *TheCall) {
- ExprResult TheCallResult(TheCall);
-
- // Find out if any arguments are required to be integer constant expressions.
- unsigned ICEArguments = 0;
- ASTContext::GetBuiltinTypeError Error;
- Context.GetBuiltinType(BuiltinID, Error, &ICEArguments);
- if (Error != ASTContext::GE_None)
- ICEArguments = 0; // Don't diagnose previously diagnosed errors.
-
- // If any arguments are required to be ICE's, check and diagnose.
- for (unsigned ArgNo = 0; ICEArguments != 0; ++ArgNo) {
- // Skip arguments not required to be ICE's.
- if ((ICEArguments & (1 << ArgNo)) == 0) continue;
-
- llvm::APSInt Result;
- // If we don't have enough arguments, continue so we can issue better
- // diagnostic in checkArgCount(...)
- if (ArgNo < TheCall->getNumArgs() &&
- BuiltinConstantArg(TheCall, ArgNo, Result))
- return true;
- ICEArguments &= ~(1 << ArgNo);
- }
-
- FPOptions FPO;
- switch (BuiltinID) {
- case Builtin::BI__builtin_cpu_supports:
- case Builtin::BI__builtin_cpu_is:
- if (BuiltinCpu(*this, Context.getTargetInfo(), TheCall,
- Context.getAuxTargetInfo(), BuiltinID))
- return ExprError();
- break;
- case Builtin::BI__builtin_cpu_init:
- if (!Context.getTargetInfo().supportsCpuInit()) {
- Diag(TheCall->getBeginLoc(), diag::err_builtin_target_unsupported)
- << SourceRange(TheCall->getBeginLoc(), TheCall->getEndLoc());
- return ExprError();
- }
- break;
- case Builtin::BI__builtin___CFStringMakeConstantString:
- // CFStringMakeConstantString is currently not implemented for GOFF (i.e.,
- // on z/OS) and for XCOFF (i.e., on AIX). Emit unsupported
- if (CheckBuiltinTargetNotInUnsupported(
- *this, BuiltinID, TheCall,
- {llvm::Triple::GOFF, llvm::Triple::XCOFF}))
- return ExprError();
- assert(TheCall->getNumArgs() == 1 &&
- "Wrong # arguments to builtin CFStringMakeConstantString");
- if (ObjC().CheckObjCString(TheCall->getArg(0)))
- return ExprError();
- break;
- case Builtin::BI__builtin_ms_va_start:
- case Builtin::BI__builtin_stdarg_start:
- case Builtin::BI__builtin_va_start:
- case Builtin::BI__builtin_c23_va_start:
- if (BuiltinVAStart(BuiltinID, TheCall))
- return ExprError();
- break;
- case Builtin::BI__va_start: {
- switch (Context.getTargetInfo().getTriple().getArch()) {
- case llvm::Triple::aarch64:
- case llvm::Triple::arm:
- case llvm::Triple::thumb:
- if (BuiltinVAStartARMMicrosoft(TheCall))
- return ExprError();
- break;
- default:
- if (BuiltinVAStart(BuiltinID, TheCall))
- return ExprError();
- break;
- }
- break;
- }
-
- // The acquire, release, and no fence variants are ARM and AArch64 only.
- case Builtin::BI_interlockedbittestandset_acq:
- case Builtin::BI_interlockedbittestandset_rel:
- case Builtin::BI_interlockedbittestandset_nf:
- case Builtin::BI_interlockedbittestandreset_acq:
- case Builtin::BI_interlockedbittestandreset_rel:
- case Builtin::BI_interlockedbittestandreset_nf:
- if (CheckBuiltinTargetInSupported(
- *this, TheCall,
- {llvm::Triple::arm, llvm::Triple::thumb, llvm::Triple::aarch64}))
- return ExprError();
- break;
-
- // The 64-bit bittest variants are x64, ARM, and AArch64 only.
- case Builtin::BI_bittest64:
- case Builtin::BI_bittestandcomplement64:
- case Builtin::BI_bittestandreset64:
- case Builtin::BI_bittestandset64:
- case Builtin::BI_interlockedbittestandreset64:
- case Builtin::BI_interlockedbittestandset64:
- if (CheckBuiltinTargetInSupported(
- *this, TheCall,
- {llvm::Triple::x86_64, llvm::Triple::arm, llvm::Triple::thumb,
- llvm::Triple::aarch64, llvm::Triple::amdgcn}))
- return ExprError();
- break;
-
- // The 64-bit acquire, release, and no fence variants are AArch64 only.
- case Builtin::BI_interlockedbittestandreset64_acq:
- case Builtin::BI_interlockedbittestandreset64_rel:
- case Builtin::BI_interlockedbittestandreset64_nf:
- case Builtin::BI_interlockedbittestandset64_acq:
- case Builtin::BI_interlockedbittestandset64_rel:
- case Builtin::BI_interlockedbittestandset64_nf:
- if (CheckBuiltinTargetInSupported(*this, TheCall, {llvm::Triple::aarch64}))
- return ExprError();
- break;
-
- case Builtin::BI__builtin_set_flt_rounds:
- if (CheckBuiltinTargetInSupported(
- *this, TheCall,
- {llvm::Triple::x86, llvm::Triple::x86_64, llvm::Triple::arm,
- llvm::Triple::thumb, llvm::Triple::aarch64, llvm::Triple::amdgcn,
- llvm::Triple::ppc, llvm::Triple::ppc64, llvm::Triple::ppcle,
- llvm::Triple::ppc64le}))
- return ExprError();
- break;
-
- case Builtin::BI__builtin_isgreater:
- case Builtin::BI__builtin_isgreaterequal:
- case Builtin::BI__builtin_isless:
- case Builtin::BI__builtin_islessequal:
- case Builtin::BI__builtin_islessgreater:
- case Builtin::BI__builtin_isunordered:
- if (BuiltinUnorderedCompare(TheCall, BuiltinID))
- return ExprError();
- break;
- case Builtin::BI__builtin_fpclassify:
- if (BuiltinFPClassification(TheCall, 6, BuiltinID))
- return ExprError();
- break;
- case Builtin::BI__builtin_isfpclass:
- if (BuiltinFPClassification(TheCall, 2, BuiltinID))
- return ExprError();
- break;
- case Builtin::BI__builtin_isfinite:
- case Builtin::BI__builtin_isinf:
- case Builtin::BI__builtin_isinf_sign:
- case Builtin::BI__builtin_isnan:
- case Builtin::BI__builtin_issignaling:
- case Builtin::BI__builtin_isnormal:
- case Builtin::BI__builtin_issubnormal:
- case Builtin::BI__builtin_iszero:
- case Builtin::BI__builtin_signbit:
- case Builtin::BI__builtin_signbitf:
- case Builtin::BI__builtin_signbitl:
- if (BuiltinFPClassification(TheCall, 1, BuiltinID))
- return ExprError();
- break;
- case Builtin::BI__builtin_shufflevector:
- return BuiltinShuffleVector(TheCall);
- // TheCall will be freed by the smart pointer here, but that's fine, since
- // BuiltinShuffleVector guts it, but then doesn't release it.
- case Builtin::BI__builtin_masked_load:
- case Builtin::BI__builtin_masked_expand_load:
- return BuiltinMaskedLoad(*this, TheCall);
- case Builtin::BI__builtin_masked_store:
- case Builtin::BI__builtin_masked_compress_store:
- return BuiltinMaskedStore(*this, TheCall);
- case Builtin::BI__builtin_masked_gather:
- return BuiltinMaskedGather(*this, TheCall);
- case Builtin::BI__builtin_masked_scatter:
- return BuiltinMaskedScatter(*this, TheCall);
- case Builtin::BI__builtin_invoke:
- return BuiltinInvoke(*this, TheCall);
- case Builtin::BI__builtin_prefetch:
- if (BuiltinPrefetch(TheCall))
- return ExprError();
- break;
- case Builtin::BI__builtin_alloca_with_align:
- case Builtin::BI__builtin_alloca_with_align_uninitialized:
- if (BuiltinAllocaWithAlign(TheCall))
- return ExprError();
- [[fallthrough]];
- case Builtin::BI__builtin_alloca:
- case Builtin::BI__builtin_alloca_uninitialized:
- Diag(TheCall->getBeginLoc(), diag::warn_alloca)
- << TheCall->getDirectCallee();
- if (getLangOpts().OpenCL) {
- builtinAllocaAddrSpace(*this, TheCall);
- }
- break;
- case Builtin::BI__builtin_infer_alloc_token:
- if (checkBuiltinInferAllocToken(*this, TheCall))
- return ExprError();
- break;
- case Builtin::BI__arithmetic_fence:
- if (BuiltinArithmeticFence(TheCall))
- return ExprError();
- break;
- case Builtin::BI__assume:
- case Builtin::BI__builtin_assume:
- if (BuiltinAssume(TheCall))
- return ExprError();
- break;
- case Builtin::BI__builtin_assume_aligned:
- if (BuiltinAssumeAligned(TheCall))
- return ExprError();
- break;
- case Builtin::BI__builtin_dynamic_object_size:
- case Builtin::BI__builtin_object_size:
- if (BuiltinConstantArgRange(TheCall, 1, 0, 3))
- return ExprError();
- break;
- case Builtin::BI__builtin_longjmp:
- if (BuiltinLongjmp(TheCall))
- return ExprError();
- break;
- case Builtin::BI__builtin_setjmp:
- if (BuiltinSetjmp(TheCall))
- return ExprError();
- break;
- case Builtin::BI__builtin_classify_type:
- if (checkArgCount(TheCall, 1))
- return true;
- TheCall->setType(Context.IntTy);
- break;
- case Builtin::BI__builtin_complex:
- if (BuiltinComplex(TheCall))
- return ExprError();
- break;
- case Builtin::BI__builtin_constant_p: {
- if (checkArgCount(TheCall, 1))
- return true;
- ExprResult Arg = DefaultFunctionArrayLvalueConversion(TheCall->getArg(0));
- if (Arg.isInvalid()) return true;
- TheCall->setArg(0, Arg.get());
- TheCall->setType(Context.IntTy);
- break;
- }
- case Builtin::BI__builtin_launder:
- return BuiltinLaunder(*this, TheCall);
- case Builtin::BI__builtin_is_within_lifetime:
- return BuiltinIsWithinLifetime(*this, TheCall);
- case Builtin::BI__builtin_trivially_relocate:
- return BuiltinTriviallyRelocate(*this, TheCall);
-
- case Builtin::BI__sync_fetch_and_add:
- case Builtin::BI__sync_fetch_and_add_1:
- case Builtin::BI__sync_fetch_and_add_2:
- case Builtin::BI__sync_fetch_and_add_4:
- case Builtin::BI__sync_fetch_and_add_8:
- case Builtin::BI__sync_fetch_and_add_16:
- case Builtin::BI__sync_fetch_and_sub:
- case Builtin::BI__sync_fetch_and_sub_1:
- case Builtin::BI__sync_fetch_and_sub_2:
- case Builtin::BI__sync_fetch_and_sub_4:
- case Builtin::BI__sync_fetch_and_sub_8:
- case Builtin::BI__sync_fetch_and_sub_16:
- case Builtin::BI__sync_fetch_and_or:
- case Builtin::BI__sync_fetch_and_or_1:
- case Builtin::BI__sync_fetch_and_or_2:
- case Builtin::BI__sync_fetch_and_or_4:
- case Builtin::BI__sync_fetch_and_or_8:
- case Builtin::BI__sync_fetch_and_or_16:
- case Builtin::BI__sync_fetch_and_and:
- case Builtin::BI__sync_fetch_and_and_1:
- case Builtin::BI__sync_fetch_and_and_2:
- case Builtin::BI__sync_fetch_and_and_4:
- case Builtin::BI__sync_fetch_and_and_8:
- case Builtin::BI__sync_fetch_and_and_16:
- case Builtin::BI__sync_fetch_and_xor:
- case Builtin::BI__sync_fetch_and_xor_1:
- case Builtin::BI__sync_fetch_and_xor_2:
- case Builtin::BI__sync_fetch_and_xor_4:
- case Builtin::BI__sync_fetch_and_xor_8:
- case Builtin::BI__sync_fetch_and_xor_16:
- case Builtin::BI__sync_fetch_and_nand:
- case Builtin::BI__sync_fetch_and_nand_1:
- case Builtin::BI__sync_fetch_and_nand_2:
- case Builtin::BI__sync_fetch_and_nand_4:
- case Builtin::BI__sync_fetch_and_nand_8:
- case Builtin::BI__sync_fetch_and_nand_16:
- case Builtin::BI__sync_add_and_fetch:
- case Builtin::BI__sync_add_and_fetch_1:
- case Builtin::BI__sync_add_and_fetch_2:
- case Builtin::BI__sync_add_and_fetch_4:
- case Builtin::BI__sync_add_and_fetch_8:
- case Builtin::BI__sync_add_and_fetch_16:
- case Builtin::BI__sync_sub_and_fetch:
- case Builtin::BI__sync_sub_and_fetch_1:
- case Builtin::BI__sync_sub_and_fetch_2:
- case Builtin::BI__sync_sub_and_fetch_4:
- case Builtin::BI__sync_sub_and_fetch_8:
- case Builtin::BI__sync_sub_and_fetch_16:
- case Builtin::BI__sync_and_and_fetch:
- case Builtin::BI__sync_and_and_fetch_1:
- case Builtin::BI__sync_and_and_fetch_2:
- case Builtin::BI__sync_and_and_fetch_4:
- case Builtin::BI__sync_and_and_fetch_8:
- case Builtin::BI__sync_and_and_fetch_16:
- case Builtin::BI__sync_or_and_fetch:
- case Builtin::BI__sync_or_and_fetch_1:
- case Builtin::BI__sync_or_and_fetch_2:
- case Builtin::BI__sync_or_and_fetch_4:
- case Builtin::BI__sync_or_and_fetch_8:
- case Builtin::BI__sync_or_and_fetch_16:
- case Builtin::BI__sync_xor_and_fetch:
- case Builtin::BI__sync_xor_and_fetch_1:
- case Builtin::BI__sync_xor_and_fetch_2:
- case Builtin::BI__sync_xor_and_fetch_4:
- case Builtin::BI__sync_xor_and_fetch_8:
- case Builtin::BI__sync_xor_and_fetch_16:
- case Builtin::BI__sync_nand_and_fetch:
- case Builtin::BI__sync_nand_and_fetch_1:
- case Builtin::BI__sync_nand_and_fetch_2:
- case Builtin::BI__sync_nand_and_fetch_4:
- case Builtin::BI__sync_nand_and_fetch_8:
- case Builtin::BI__sync_nand_and_fetch_16:
- case Builtin::BI__sync_val_compare_and_swap:
- case Builtin::BI__sync_val_compare_and_swap_1:
- case Builtin::BI__sync_val_compare_and_swap_2:
- case Builtin::BI__sync_val_compare_and_swap_4:
- case Builtin::BI__sync_val_compare_and_swap_8:
- case Builtin::BI__sync_val_compare_and_swap_16:
- case Builtin::BI__sync_bool_compare_and_swap:
- case Builtin::BI__sync_bool_compare_and_swap_1:
- case Builtin::BI__sync_bool_compare_and_swap_2:
- case Builtin::BI__sync_bool_compare_and_swap_4:
- case Builtin::BI__sync_bool_compare_and_swap_8:
- case Builtin::BI__sync_bool_compare_and_swap_16:
- case Builtin::BI__sync_lock_test_and_set:
- case Builtin::BI__sync_lock_test_and_set_1:
- case Builtin::BI__sync_lock_test_and_set_2:
- case Builtin::BI__sync_lock_test_and_set_4:
- case Builtin::BI__sync_lock_test_and_set_8:
- case Builtin::BI__sync_lock_test_and_set_16:
- case Builtin::BI__sync_lock_release:
- case Builtin::BI__sync_lock_release_1:
- case Builtin::BI__sync_lock_release_2:
- case Builtin::BI__sync_lock_release_4:
- case Builtin::BI__sync_lock_release_8:
- case Builtin::BI__sync_lock_release_16:
- case Builtin::BI__sync_swap:
- case Builtin::BI__sync_swap_1:
- case Builtin::BI__sync_swap_2:
- case Builtin::BI__sync_swap_4:
- case Builtin::BI__sync_swap_8:
- case Builtin::BI__sync_swap_16:
- return BuiltinAtomicOverloaded(TheCallResult);
- case Builtin::BI__sync_synchronize:
- Diag(TheCall->getBeginLoc(), diag::warn_atomic_implicit_seq_cst)
- << TheCall->getCallee()->getSourceRange();
- break;
- case Builtin::BI__builtin_nontemporal_load:
- case Builtin::BI__builtin_nontemporal_store:
- return BuiltinNontemporalOverloaded(TheCallResult);
- case Builtin::BI__builtin_memcpy_inline: {
- clang::Expr *SizeOp = TheCall->getArg(2);
- // We warn about copying to or from `nullptr` pointers when `size` is
- // greater than 0. When `size` is value dependent we cannot evaluate its
- // value so we bail out.
- if (SizeOp->isValueDependent())
- break;
- if (!SizeOp->EvaluateKnownConstInt(Context).isZero()) {
- CheckNonNullArgument(*this, TheCall->getArg(0), TheCall->getExprLoc());
- CheckNonNullArgument(*this, TheCall->getArg(1), TheCall->getExprLoc());
- }
- break;
- }
- case Builtin::BI__builtin_memset_inline: {
- clang::Expr *SizeOp = TheCall->getArg(2);
- // We warn about filling to `nullptr` pointers when `size` is greater than
- // 0. When `size` is value dependent we cannot evaluate its value so we bail
- // out.
- if (SizeOp->isValueDependent())
- break;
- if (!SizeOp->EvaluateKnownConstInt(Context).isZero())
- CheckNonNullArgument(*this, TheCall->getArg(0), TheCall->getExprLoc());
- break;
- }
-#define ATOMIC_BUILTIN(ID, TYPE, ATTRS) \
- case Builtin::BI##ID: \
- return AtomicOpsOverloaded(TheCallResult, AtomicExpr::AO##ID);
-#include "clang/Basic/Builtins.inc"
- case Builtin::BI__annotation:
- if (BuiltinMSVCAnnotation(*this, TheCall))
- return ExprError();
- break;
- case Builtin::BI__builtin_annotation:
- if (BuiltinAnnotation(*this, TheCall))
- return ExprError();
- break;
- case Builtin::BI__builtin_addressof:
- if (BuiltinAddressof(*this, TheCall))
- return ExprError();
- break;
- case Builtin::BI__builtin_function_start:
- if (BuiltinFunctionStart(*this, TheCall))
- return ExprError();
- break;
- case Builtin::BI__builtin_is_aligned:
- case Builtin::BI__builtin_align_up:
- case Builtin::BI__builtin_align_down:
- if (BuiltinAlignment(*this, TheCall, BuiltinID))
- return ExprError();
- break;
- case Builtin::BI__builtin_add_overflow:
- case Builtin::BI__builtin_sub_overflow:
- case Builtin::BI__builtin_mul_overflow:
- if (BuiltinOverflow(*this, TheCall, BuiltinID))
- return ExprError();
- break;
- case Builtin::BI__builtin_operator_new:
- case Builtin::BI__builtin_operator_delete: {
- bool IsDelete = BuiltinID == Builtin::BI__builtin_operator_delete;
- ExprResult Res =
- BuiltinOperatorNewDeleteOverloaded(TheCallResult, IsDelete);
- return Res;
- }
- case Builtin::BI__builtin_dump_struct:
- return BuiltinDumpStruct(*this, TheCall);
- case Builtin::BI__builtin_expect_with_probability: {
- // We first want to ensure we are called with 3 arguments
- if (checkArgCount(TheCall, 3))
- return ExprError();
- // then check probability is constant float in range [0.0, 1.0]
- const Expr *ProbArg = TheCall->getArg(2);
- SmallVector<PartialDiagnosticAt, 8> Notes;
- Expr::EvalResult Eval;
- Eval.Diag = &Notes;
- if ((!ProbArg->EvaluateAsConstantExpr(Eval, Context)) ||
- !Eval.Val.isFloat()) {
- Diag(ProbArg->getBeginLoc(), diag::err_probability_not_constant_float)
- << ProbArg->getSourceRange();
- for (const PartialDiagnosticAt &PDiag : Notes)
- Diag(PDiag.first, PDiag.second);
- return ExprError();
- }
- llvm::APFloat Probability = Eval.Val.getFloat();
- bool LoseInfo = false;
- Probability.convert(llvm::APFloat::IEEEdouble(),
- llvm::RoundingMode::Dynamic, &LoseInfo);
- if (!(Probability >= llvm::APFloat(0.0) &&
- Probability <= llvm::APFloat(1.0))) {
- Diag(ProbArg->getBeginLoc(), diag::err_probability_out_of_range)
- << ProbArg->getSourceRange();
- return ExprError();
- }
- break;
- }
- case Builtin::BI__builtin_preserve_access_index:
- if (BuiltinPreserveAI(*this, TheCall))
- return ExprError();
- break;
- case Builtin::BI__builtin_call_with_static_chain:
- if (BuiltinCallWithStaticChain(*this, TheCall))
- return ExprError();
- break;
- case Builtin::BI__exception_code:
- case Builtin::BI_exception_code:
- if (BuiltinSEHScopeCheck(*this, TheCall, Scope::SEHExceptScope,
- diag::err_seh___except_block))
- return ExprError();
- break;
- case Builtin::BI__exception_info:
- case Builtin::BI_exception_info:
- if (BuiltinSEHScopeCheck(*this, TheCall, Scope::SEHFilterScope,
- diag::err_seh___except_filter))
- return ExprError();
- break;
- case Builtin::BI__GetExceptionInfo:
- if (checkArgCount(TheCall, 1))
- return ExprError();
-
- if (CheckCXXThrowOperand(
- TheCall->getBeginLoc(),
- Context.getExceptionObjectType(FDecl->getParamDecl(0)->getType()),
- TheCall))
- return ExprError();
-
- TheCall->setType(Context.VoidPtrTy);
- break;
- case Builtin::BIaddressof:
- case Builtin::BI__addressof:
- case Builtin::BIforward:
- case Builtin::BIforward_like:
- case Builtin::BImove:
- case Builtin::BImove_if_noexcept:
- case Builtin::BIas_const: {
- // These are all expected to be of the form
- // T &/&&/* f(U &/&&)
- // where T and U only differ in qualification.
- if (checkArgCount(TheCall, 1))
- return ExprError();
- QualType Param = FDecl->getParamDecl(0)->getType();
- QualType Result = FDecl->getReturnType();
- bool ReturnsPointer = BuiltinID == Builtin::BIaddressof ||
- BuiltinID == Builtin::BI__addressof;
- if (!(Param->isReferenceType() &&
- (ReturnsPointer ? Result->isAnyPointerType()
- : Result->isReferenceType()) &&
- Context.hasSameUnqualifiedType(Param->getPointeeType(),
- Result->getPointeeType()))) {
- Diag(TheCall->getBeginLoc(), diag::err_builtin_move_forward_unsupported)
- << FDecl;
- return ExprError();
- }
- break;
- }
- case Builtin::BI__builtin_ptrauth_strip:
- return PointerAuthStrip(*this, TheCall);
- case Builtin::BI__builtin_ptrauth_blend_discriminator:
- return PointerAuthBlendDiscriminator(*this, TheCall);
- case Builtin::BI__builtin_ptrauth_sign_constant:
- return PointerAuthSignOrAuth(*this, TheCall, PAO_Sign,
- /*RequireConstant=*/true);
- case Builtin::BI__builtin_ptrauth_sign_unauthenticated:
- return PointerAuthSignOrAuth(*this, TheCall, PAO_Sign,
- /*RequireConstant=*/false);
- case Builtin::BI__builtin_ptrauth_auth:
- return PointerAuthSignOrAuth(*this, TheCall, PAO_Auth,
- /*RequireConstant=*/false);
- case Builtin::BI__builtin_ptrauth_sign_generic_data:
- return PointerAuthSignGenericData(*this, TheCall);
- case Builtin::BI__builtin_ptrauth_auth_and_resign:
- return PointerAuthAuthAndResign(*this, TheCall);
- case Builtin::BI__builtin_ptrauth_auth_load_relative_and_sign:
- return PointerAuthAuthLoadRelativeAndSign(*this, TheCall);
- case Builtin::BI__builtin_ptrauth_string_discriminator:
- return PointerAuthStringDiscriminator(*this, TheCall);
-
- case Builtin::BI__builtin_get_vtable_pointer:
- return GetVTablePointer(*this, TheCall);
-
- // OpenCL v2.0, s6.13.16 - Pipe functions
- case Builtin::BIread_pipe:
- case Builtin::BIwrite_pipe:
- // Since those two functions are declared with var args, we need a semantic
- // check for the argument.
- if (OpenCL().checkBuiltinRWPipe(TheCall))
- return ExprError();
- break;
- case Builtin::BIreserve_read_pipe:
- case Builtin::BIreserve_write_pipe:
- case Builtin::BIwork_group_reserve_read_pipe:
- case Builtin::BIwork_group_reserve_write_pipe:
- if (OpenCL().checkBuiltinReserveRWPipe(TheCall))
- return ExprError();
- break;
- case Builtin::BIsub_group_reserve_read_pipe:
- case Builtin::BIsub_group_reserve_write_pipe:
- if (OpenCL().checkSubgroupExt(TheCall) ||
- OpenCL().checkBuiltinReserveRWPipe(TheCall))
- return ExprError();
- break;
- case Builtin::BIcommit_read_pipe:
- case Builtin::BIcommit_write_pipe:
- case Builtin::BIwork_group_commit_read_pipe:
- case Builtin::BIwork_group_commit_write_pipe:
- if (OpenCL().checkBuiltinCommitRWPipe(TheCall))
- return ExprError();
- break;
- case Builtin::BIsub_group_commit_read_pipe:
- case Builtin::BIsub_group_commit_write_pipe:
- if (OpenCL().checkSubgroupExt(TheCall) ||
- OpenCL().checkBuiltinCommitRWPipe(TheCall))
- return ExprError();
- break;
- case Builtin::BIget_pipe_num_packets:
- case Builtin::BIget_pipe_max_packets:
- if (OpenCL().checkBuiltinPipePackets(TheCall))
- return ExprError();
- break;
- case Builtin::BIto_global:
- case Builtin::BIto_local:
- case Builtin::BIto_private:
- if (OpenCL().checkBuiltinToAddr(BuiltinID, TheCall))
- return ExprError();
- break;
- // OpenCL v2.0, s6.13.17 - Enqueue kernel functions.
- case Builtin::BIenqueue_kernel:
- if (OpenCL().checkBuiltinEnqueueKernel(TheCall))
- return ExprError();
- break;
- case Builtin::BIget_kernel_work_group_size:
- case Builtin::BIget_kernel_preferred_work_group_size_multiple:
- if (OpenCL().checkBuiltinKernelWorkGroupSize(TheCall))
- return ExprError();
- break;
- case Builtin::BIget_kernel_max_sub_group_size_for_ndrange:
- case Builtin::BIget_kernel_sub_group_count_for_ndrange:
- if (OpenCL().checkBuiltinNDRangeAndBlock(TheCall))
- return ExprError();
- break;
- case Builtin::BI__builtin_os_log_format:
- Cleanup.setExprNeedsCleanups(true);
- [[fallthrough]];
- case Builtin::BI__builtin_os_log_format_buffer_size:
- if (BuiltinOSLogFormat(TheCall))
- return ExprError();
- break;
- case Builtin::BI__builtin_frame_address:
- case Builtin::BI__builtin_return_address: {
- if (BuiltinConstantArgRange(TheCall, 0, 0, 0xFFFF))
- return ExprError();
-
- // -Wframe-address warning if non-zero passed to builtin
- // return/frame address.
- Expr::EvalResult Result;
- if (!TheCall->getArg(0)->isValueDependent() &&
- TheCall->getArg(0)->EvaluateAsInt(Result, getASTContext()) &&
- Result.Val.getInt() != 0)
- Diag(TheCall->getBeginLoc(), diag::warn_frame_address)
- << ((BuiltinID == Builtin::BI__builtin_return_address)
- ? "__builtin_return_address"
- : "__builtin_frame_address")
- << TheCall->getSourceRange();
- break;
- }
-
- case Builtin::BI__builtin_nondeterministic_value: {
- if (BuiltinNonDeterministicValue(TheCall))
- return ExprError();
- break;
- }
-
- // __builtin_elementwise_abs restricts the element type to signed integers or
- // floating point types only.
- case Builtin::BI__builtin_elementwise_abs:
- if (PrepareBuiltinElementwiseMathOneArgCall(
- TheCall, EltwiseBuiltinArgTyRestriction::SignedIntOrFloatTy))
- return ExprError();
- break;
-
- // These builtins restrict the element type to floating point
- // types only.
- case Builtin::BI__builtin_elementwise_acos:
- case Builtin::BI__builtin_elementwise_asin:
- case Builtin::BI__builtin_elementwise_atan:
- case Builtin::BI__builtin_elementwise_ceil:
- case Builtin::BI__builtin_elementwise_cos:
- case Builtin::BI__builtin_elementwise_cosh:
- case Builtin::BI__builtin_elementwise_exp:
- case Builtin::BI__builtin_elementwise_exp2:
- case Builtin::BI__builtin_elementwise_exp10:
- case Builtin::BI__builtin_elementwise_floor:
- case Builtin::BI__builtin_elementwise_log:
- case Builtin::BI__builtin_elementwise_log2:
- case Builtin::BI__builtin_elementwise_log10:
- case Builtin::BI__builtin_elementwise_roundeven:
- case Builtin::BI__builtin_elementwise_round:
- case Builtin::BI__builtin_elementwise_rint:
- case Builtin::BI__builtin_elementwise_nearbyint:
- case Builtin::BI__builtin_elementwise_sin:
- case Builtin::BI__builtin_elementwise_sinh:
- case Builtin::BI__builtin_elementwise_sqrt:
- case Builtin::BI__builtin_elementwise_tan:
- case Builtin::BI__builtin_elementwise_tanh:
- case Builtin::BI__builtin_elementwise_trunc:
- case Builtin::BI__builtin_elementwise_canonicalize:
- if (PrepareBuiltinElementwiseMathOneArgCall(
- TheCall, EltwiseBuiltinArgTyRestriction::FloatTy))
- return ExprError();
- break;
- case Builtin::BI__builtin_elementwise_fma:
- if (BuiltinElementwiseTernaryMath(TheCall))
- return ExprError();
- break;
-
- case Builtin::BI__builtin_elementwise_ldexp: {
- if (checkArgCount(TheCall, 2))
- return ExprError();
-
- ExprResult A = BuiltinVectorMathConversions(*this, TheCall->getArg(0));
- if (A.isInvalid())
- return ExprError();
- QualType TyA = A.get()->getType();
- if (checkMathBuiltinElementType(*this, A.get()->getBeginLoc(), TyA,
- EltwiseBuiltinArgTyRestriction::FloatTy, 1))
- return ExprError();
-
- ExprResult Exp = UsualUnaryConversions(TheCall->getArg(1));
- if (Exp.isInvalid())
- return ExprError();
- QualType TyExp = Exp.get()->getType();
- if (checkMathBuiltinElementType(*this, Exp.get()->getBeginLoc(), TyExp,
- EltwiseBuiltinArgTyRestriction::IntegerTy,
- 2))
- return ExprError();
-
- // Check the two arguments are either scalars or vectors of equal length.
- const auto *Vec0 = TyA->getAs<VectorType>();
- const auto *Vec1 = TyExp->getAs<VectorType>();
- unsigned Arg0Length = Vec0 ? Vec0->getNumElements() : 0;
- unsigned Arg1Length = Vec1 ? Vec1->getNumElements() : 0;
- if (Arg0Length != Arg1Length) {
- Diag(Exp.get()->getBeginLoc(),
- diag::err_typecheck_vector_lengths_not_equal)
- << TyA << TyExp << A.get()->getSourceRange()
- << Exp.get()->getSourceRange();
- return ExprError();
- }
-
- TheCall->setArg(0, A.get());
- TheCall->setArg(1, Exp.get());
- TheCall->setType(TyA);
- break;
- }
-
- // These builtins restrict the element type to floating point
- // types only, and take in two arguments.
- case Builtin::BI__builtin_elementwise_minnum:
- case Builtin::BI__builtin_elementwise_maxnum:
- case Builtin::BI__builtin_elementwise_minimum:
- case Builtin::BI__builtin_elementwise_maximum:
- case Builtin::BI__builtin_elementwise_minimumnum:
- case Builtin::BI__builtin_elementwise_maximumnum:
- case Builtin::BI__builtin_elementwise_atan2:
- case Builtin::BI__builtin_elementwise_fmod:
- case Builtin::BI__builtin_elementwise_pow:
- if (BuiltinElementwiseMath(TheCall,
- EltwiseBuiltinArgTyRestriction::FloatTy))
- return ExprError();
- break;
- // These builtins restrict the element type to integer
- // types only.
- case Builtin::BI__builtin_elementwise_add_sat:
- case Builtin::BI__builtin_elementwise_sub_sat:
- if (BuiltinElementwiseMath(TheCall,
- EltwiseBuiltinArgTyRestriction::IntegerTy))
- return ExprError();
- break;
- case Builtin::BI__builtin_elementwise_fshl:
- case Builtin::BI__builtin_elementwise_fshr:
- if (BuiltinElementwiseTernaryMath(
- TheCall, EltwiseBuiltinArgTyRestriction::IntegerTy))
- return ExprError();
- break;
- case Builtin::BI__builtin_elementwise_min:
- case Builtin::BI__builtin_elementwise_max:
- if (BuiltinElementwiseMath(TheCall))
- return ExprError();
- break;
- case Builtin::BI__builtin_elementwise_popcount:
- case Builtin::BI__builtin_elementwise_bitreverse:
- if (PrepareBuiltinElementwiseMathOneArgCall(
- TheCall, EltwiseBuiltinArgTyRestriction::IntegerTy))
- return ExprError();
- break;
- case Builtin::BI__builtin_elementwise_copysign: {
- if (checkArgCount(TheCall, 2))
- return ExprError();
-
- ExprResult Magnitude = UsualUnaryConversions(TheCall->getArg(0));
- ExprResult Sign = UsualUnaryConversions(TheCall->getArg(1));
- if (Magnitude.isInvalid() || Sign.isInvalid())
- return ExprError();
-
- QualType MagnitudeTy = Magnitude.get()->getType();
- QualType SignTy = Sign.get()->getType();
- if (checkMathBuiltinElementType(
- *this, TheCall->getArg(0)->getBeginLoc(), MagnitudeTy,
- EltwiseBuiltinArgTyRestriction::FloatTy, 1) ||
- checkMathBuiltinElementType(
- *this, TheCall->getArg(1)->getBeginLoc(), SignTy,
- EltwiseBuiltinArgTyRestriction::FloatTy, 2)) {
- return ExprError();
- }
-
- if (MagnitudeTy.getCanonicalType() != SignTy.getCanonicalType()) {
- return Diag(Sign.get()->getBeginLoc(),
- diag::err_typecheck_call_different_arg_types)
- << MagnitudeTy << SignTy;
- }
-
- TheCall->setArg(0, Magnitude.get());
- TheCall->setArg(1, Sign.get());
- TheCall->setType(Magnitude.get()->getType());
- break;
- }
- case Builtin::BI__builtin_elementwise_clzg:
- case Builtin::BI__builtin_elementwise_ctzg:
- // These builtins can be unary or binary. Note for empty calls we call the
- // unary checker in order to not emit an error that says the function
- // expects 2 arguments, which would be misleading.
- if (TheCall->getNumArgs() <= 1) {
- if (PrepareBuiltinElementwiseMathOneArgCall(
- TheCall, EltwiseBuiltinArgTyRestriction::IntegerTy))
- return ExprError();
- } else if (BuiltinElementwiseMath(
- TheCall, EltwiseBuiltinArgTyRestriction::IntegerTy))
- return ExprError();
- break;
- case Builtin::BI__builtin_reduce_max:
- case Builtin::BI__builtin_reduce_min: {
- if (PrepareBuiltinReduceMathOneArgCall(TheCall))
- return ExprError();
-
- const Expr *Arg = TheCall->getArg(0);
- const auto *TyA = Arg->getType()->getAs<VectorType>();
-
- QualType ElTy;
- if (TyA)
- ElTy = TyA->getElementType();
- else if (Arg->getType()->isSizelessVectorType())
- ElTy = Arg->getType()->getSizelessVectorEltType(Context);
-
- if (ElTy.isNull()) {
- Diag(Arg->getBeginLoc(), diag::err_builtin_invalid_arg_type)
- << 1 << /* vector ty */ 2 << /* no int */ 0 << /* no fp */ 0
- << Arg->getType();
- return ExprError();
- }
-
- TheCall->setType(ElTy);
- break;
- }
- case Builtin::BI__builtin_reduce_maximum:
- case Builtin::BI__builtin_reduce_minimum: {
- if (PrepareBuiltinReduceMathOneArgCall(TheCall))
- return ExprError();
-
- const Expr *Arg = TheCall->getArg(0);
- const auto *TyA = Arg->getType()->getAs<VectorType>();
-
- QualType ElTy;
- if (TyA)
- ElTy = TyA->getElementType();
- else if (Arg->getType()->isSizelessVectorType())
- ElTy = Arg->getType()->getSizelessVectorEltType(Context);
-
- if (ElTy.isNull() || !ElTy->isFloatingType()) {
- Diag(Arg->getBeginLoc(), diag::err_builtin_invalid_arg_type)
- << 1 << /* vector of */ 4 << /* no int */ 0 << /* fp */ 1
- << Arg->getType();
- return ExprError();
- }
-
- TheCall->setType(ElTy);
- break;
- }
-
- // These builtins support vectors of integers only.
- // TODO: ADD/MUL should support floating-point types.
- case Builtin::BI__builtin_reduce_add:
- case Builtin::BI__builtin_reduce_mul:
- case Builtin::BI__builtin_reduce_xor:
- case Builtin::BI__builtin_reduce_or:
- case Builtin::BI__builtin_reduce_and: {
- if (PrepareBuiltinReduceMathOneArgCall(TheCall))
- return ExprError();
-
- const Expr *Arg = TheCall->getArg(0);
- const auto *TyA = Arg->getType()->getAs<VectorType>();
-
- QualType ElTy;
- if (TyA)
- ElTy = TyA->getElementType();
- else if (Arg->getType()->isSizelessVectorType())
- ElTy = Arg->getType()->getSizelessVectorEltType(Context);
-
- if (ElTy.isNull() || !ElTy->isIntegerType()) {
- Diag(Arg->getBeginLoc(), diag::err_builtin_invalid_arg_type)
- << 1 << /* vector of */ 4 << /* int */ 1 << /* no fp */ 0
- << Arg->getType();
- return ExprError();
- }
-
- TheCall->setType(ElTy);
- break;
- }
-
- case Builtin::BI__builtin_matrix_transpose:
- return BuiltinMatrixTranspose(TheCall, TheCallResult);
-
- case Builtin::BI__builtin_matrix_column_major_load:
- return BuiltinMatrixColumnMajorLoad(TheCall, TheCallResult);
-
- case Builtin::BI__builtin_matrix_column_major_store:
- return BuiltinMatrixColumnMajorStore(TheCall, TheCallResult);
-
- case Builtin::BI__builtin_verbose_trap:
- if (!checkBuiltinVerboseTrap(TheCall, *this))
- return ExprError();
- break;
-
- case Builtin::BI__builtin_get_device_side_mangled_name: {
- auto Check = [](CallExpr *TheCall) {
- if (TheCall->getNumArgs() != 1)
- return false;
- auto *DRE = dyn_cast<DeclRefExpr>(TheCall->getArg(0)->IgnoreImpCasts());
- if (!DRE)
- return false;
- auto *D = DRE->getDecl();
- if (!isa<FunctionDecl>(D) && !isa<VarDecl>(D))
- return false;
- return D->hasAttr<CUDAGlobalAttr>() || D->hasAttr<CUDADeviceAttr>() ||
- D->hasAttr<CUDAConstantAttr>() || D->hasAttr<HIPManagedAttr>();
- };
- if (!Check(TheCall)) {
- Diag(TheCall->getBeginLoc(),
- diag::err_hip_invalid_args_builtin_mangled_name);
- return ExprError();
- }
- break;
- }
- case Builtin::BI__builtin_bswapg:
- if (BuiltinBswapg(*this, TheCall))
- return ExprError();
- break;
- case Builtin::BI__builtin_popcountg:
- if (BuiltinPopcountg(*this, TheCall))
- return ExprError();
- break;
- case Builtin::BI__builtin_clzg:
- case Builtin::BI__builtin_ctzg:
- if (BuiltinCountZeroBitsGeneric(*this, TheCall))
- return ExprError();
- break;
-
- case Builtin::BI__builtin_stdc_rotate_left:
- case Builtin::BI__builtin_stdc_rotate_right:
- if (BuiltinRotateGeneric(*this, TheCall))
- return ExprError();
- break;
-
- case Builtin::BI__builtin_allow_runtime_check: {
- Expr *Arg = TheCall->getArg(0);
- // Check if the argument is a string literal.
- if (!isa<StringLiteral>(Arg->IgnoreParenImpCasts())) {
- Diag(TheCall->getBeginLoc(), diag::err_expr_not_string_literal)
- << Arg->getSourceRange();
- return ExprError();
- }
- break;
- }
-
- case Builtin::BI__builtin_allow_sanitize_check: {
- Expr *Arg = TheCall->getArg(0);
- // Check if the argument is a string literal.
- const StringLiteral *SanitizerName =
- dyn_cast<StringLiteral>(Arg->IgnoreParenImpCasts());
- if (!SanitizerName) {
- Diag(TheCall->getBeginLoc(), diag::err_expr_not_string_literal)
- << Arg->getSourceRange();
- return ExprError();
- }
- // Validate the sanitizer name.
- if (!llvm::StringSwitch<bool>(SanitizerName->getString())
- .Cases({"address", "thread", "memory", "hwaddress",
- "kernel-address", "kernel-memory", "kernel-hwaddress"},
- true)
- .Default(false)) {
- Diag(TheCall->getBeginLoc(), diag::err_invalid_builtin_argument)
- << SanitizerName->getString() << "__builtin_allow_sanitize_check"
- << Arg->getSourceRange();
- return ExprError();
- }
- break;
- }
- case Builtin::BI__builtin_counted_by_ref:
- if (BuiltinCountedByRef(TheCall))
- return ExprError();
- break;
- }
-
- if (getLangOpts().HLSL && HLSL().CheckBuiltinFunctionCall(BuiltinID, TheCall))
- return ExprError();
-
- // Since the target specific builtins for each arch overlap, only check those
- // of the arch we are compiling for.
- if (Context.BuiltinInfo.isTSBuiltin(BuiltinID)) {
- if (Context.BuiltinInfo.isAuxBuiltinID(BuiltinID)) {
- assert(Context.getAuxTargetInfo() &&
- "Aux Target Builtin, but not an aux target?");
-
- if (CheckTSBuiltinFunctionCall(
- *Context.getAuxTargetInfo(),
- Context.BuiltinInfo.getAuxBuiltinID(BuiltinID), TheCall))
- return ExprError();
- } else {
- if (CheckTSBuiltinFunctionCall(Context.getTargetInfo(), BuiltinID,
- TheCall))
- return ExprError();
- }
- }
-
- return TheCallResult;
-}
-
-bool Sema::ValueIsRunOfOnes(CallExpr *TheCall, unsigned ArgNum) {
- llvm::APSInt Result;
- // We can't check the value of a dependent argument.
- Expr *Arg = TheCall->getArg(ArgNum);
- if (Arg->isTypeDependent() || Arg->isValueDependent())
- return false;
-
- // Check constant-ness first.
- if (BuiltinConstantArg(TheCall, ArgNum, Result))
- return true;
-
- // Check contiguous run of 1s, 0xFF0000FF is also a run of 1s.
- if (Result.isShiftedMask() || (~Result).isShiftedMask())
- return false;
-
- return Diag(TheCall->getBeginLoc(),
- diag::err_argument_not_contiguous_bit_field)
- << ArgNum << Arg->getSourceRange();
-}
-
-bool Sema::getFormatStringInfo(const Decl *D, unsigned FormatIdx,
- unsigned FirstArg, FormatStringInfo *FSI) {
- bool HasImplicitThisParam = hasImplicitObjectParameter(D);
- bool IsVariadic = false;
- if (const FunctionType *FnTy = D->getFunctionType())
- IsVariadic = cast<FunctionProtoType>(FnTy)->isVariadic();
- else if (const auto *BD = dyn_cast<BlockDecl>(D))
- IsVariadic = BD->isVariadic();
- else if (const auto *OMD = dyn_cast<ObjCMethodDecl>(D))
- IsVariadic = OMD->isVariadic();
-
- return getFormatStringInfo(FormatIdx, FirstArg, HasImplicitThisParam,
- IsVariadic, FSI);
-}
-
-bool Sema::getFormatStringInfo(unsigned FormatIdx, unsigned FirstArg,
- bool HasImplicitThisParam, bool IsVariadic,
- FormatStringInfo *FSI) {
- if (FirstArg == 0)
- FSI->ArgPassingKind = FAPK_VAList;
- else if (IsVariadic)
- FSI->ArgPassingKind = FAPK_Variadic;
- else
- FSI->ArgPassingKind = FAPK_Fixed;
- FSI->FormatIdx = FormatIdx - 1;
- FSI->FirstDataArg = FSI->ArgPassingKind == FAPK_VAList ? 0 : FirstArg - 1;
-
- // The way the format attribute works in GCC, the implicit this argument
- // of member functions is counted. However, it doesn't appear in our own
- // lists, so decrement format_idx in that case.
- if (HasImplicitThisParam) {
- if(FSI->FormatIdx == 0)
- return false;
- --FSI->FormatIdx;
- if (FSI->FirstDataArg != 0)
- --FSI->FirstDataArg;
- }
- return true;
-}
-
-/// Checks if a the given expression evaluates to null.
-///
-/// Returns true if the value evaluates to null.
-static bool CheckNonNullExpr(Sema &S, const Expr *Expr) {
- // Treat (smart) pointers constructed from nullptr as null, whether we can
- // const-evaluate them or not.
- // This must happen first: the smart pointer expr might have _Nonnull type!
- if (isa<CXXNullPtrLiteralExpr>(
- IgnoreExprNodes(Expr, IgnoreImplicitAsWrittenSingleStep,
- IgnoreElidableImplicitConstructorSingleStep)))
- return true;
-
- // If the expression has non-null type, it doesn't evaluate to null.
- if (auto nullability = Expr->IgnoreImplicit()->getType()->getNullability()) {
- if (*nullability == NullabilityKind::NonNull)
- return false;
- }
-
- // As a special case, transparent unions initialized with zero are
- // considered null for the purposes of the nonnull attribute.
- if (const RecordType *UT = Expr->getType()->getAsUnionType();
- UT &&
- UT->getDecl()->getMostRecentDecl()->hasAttr<TransparentUnionAttr>()) {
- if (const auto *CLE = dyn_cast<CompoundLiteralExpr>(Expr))
- if (const auto *ILE = dyn_cast<InitListExpr>(CLE->getInitializer()))
- Expr = ILE->getInit(0);
- }
-
- bool Result;
- return (!Expr->isValueDependent() &&
- Expr->EvaluateAsBooleanCondition(Result, S.Context) &&
- !Result);
-}
-
-static void CheckNonNullArgument(Sema &S,
- const Expr *ArgExpr,
- SourceLocation CallSiteLoc) {
- if (CheckNonNullExpr(S, ArgExpr))
- S.DiagRuntimeBehavior(CallSiteLoc, ArgExpr,
- S.PDiag(diag::warn_null_arg)
- << ArgExpr->getSourceRange());
-}
-
-/// Determine whether the given type has a non-null nullability annotation.
-static bool isNonNullType(QualType type) {
- if (auto nullability = type->getNullability())
- return *nullability == NullabilityKind::NonNull;
-
- return false;
-}
-
-static void CheckNonNullArguments(Sema &S,
- const NamedDecl *FDecl,
- const FunctionProtoType *Proto,
- ArrayRef<const Expr *> Args,
- SourceLocation CallSiteLoc) {
- assert((FDecl || Proto) && "Need a function declaration or prototype");
-
- // Already checked by constant evaluator.
- if (S.isConstantEvaluatedContext())
- return;
- // Check the attributes attached to the method/function itself.
- llvm::SmallBitVector NonNullArgs;
- if (FDecl) {
- // Handle the nonnull attribute on the function/method declaration itself.
- for (const auto *NonNull : FDecl->specific_attrs<NonNullAttr>()) {
- if (!NonNull->args_size()) {
- // Easy case: all pointer arguments are nonnull.
- for (const auto *Arg : Args)
- if (S.isValidPointerAttrType(Arg->getType()))
- CheckNonNullArgument(S, Arg, CallSiteLoc);
- return;
- }
-
- for (const ParamIdx &Idx : NonNull->args()) {
- unsigned IdxAST = Idx.getASTIndex();
- if (IdxAST >= Args.size())
- continue;
- if (NonNullArgs.empty())
- NonNullArgs.resize(Args.size());
- NonNullArgs.set(IdxAST);
- }
- }
- }
-
- if (FDecl && (isa<FunctionDecl>(FDecl) || isa<ObjCMethodDecl>(FDecl))) {
- // Handle the nonnull attribute on the parameters of the
- // function/method.
- ArrayRef<ParmVarDecl*> parms;
- if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(FDecl))
- parms = FD->parameters();
- else
- parms = cast<ObjCMethodDecl>(FDecl)->parameters();
-
- unsigned ParamIndex = 0;
- for (ArrayRef<ParmVarDecl*>::iterator I = parms.begin(), E = parms.end();
- I != E; ++I, ++ParamIndex) {
- const ParmVarDecl *PVD = *I;
- if (PVD->hasAttr<NonNullAttr>() || isNonNullType(PVD->getType())) {
- if (NonNullArgs.empty())
- NonNullArgs.resize(Args.size());
-
- NonNullArgs.set(ParamIndex);
- }
- }
- } else {
- // If we have a non-function, non-method declaration but no
- // function prototype, try to dig out the function prototype.
- if (!Proto) {
- if (const ValueDecl *VD = dyn_cast<ValueDecl>(FDecl)) {
- QualType type = VD->getType().getNonReferenceType();
- if (auto pointerType = type->getAs<PointerType>())
- type = pointerType->getPointeeType();
- else if (auto blockType = type->getAs<BlockPointerType>())
- type = blockType->getPointeeType();
- // FIXME: data member pointers?
-
- // Dig out the function prototype, if there is one.
- Proto = type->getAs<FunctionProtoType>();
- }
- }
-
- // Fill in non-null argument information from the nullability
- // information on the parameter types (if we have them).
- if (Proto) {
- unsigned Index = 0;
- for (auto paramType : Proto->getParamTypes()) {
- if (isNonNullType(paramType)) {
- if (NonNullArgs.empty())
- NonNullArgs.resize(Args.size());
-
- NonNullArgs.set(Index);
- }
-
- ++Index;
- }
- }
- }
-
- // Check for non-null arguments.
- for (unsigned ArgIndex = 0, ArgIndexEnd = NonNullArgs.size();
- ArgIndex != ArgIndexEnd; ++ArgIndex) {
- if (NonNullArgs[ArgIndex])
- CheckNonNullArgument(S, Args[ArgIndex], Args[ArgIndex]->getExprLoc());
- }
-}
-
-void Sema::CheckArgAlignment(SourceLocation Loc, NamedDecl *FDecl,
- StringRef ParamName, QualType ArgTy,
- QualType ParamTy) {
-
- // If a function accepts a pointer or reference type
- if (!ParamTy->isPointerType() && !ParamTy->isReferenceType())
- return;
-
- // If the parameter is a pointer type, get the pointee type for the
- // argument too. If the parameter is a reference type, don't try to get
- // the pointee type for the argument.
- if (ParamTy->isPointerType())
- ArgTy = ArgTy->getPointeeType();
-
- // Remove reference or pointer
- ParamTy = ParamTy->getPointeeType();
-
- // Find expected alignment, and the actual alignment of the passed object.
- // getTypeAlignInChars requires complete types
- if (ArgTy.isNull() || ParamTy->isDependentType() ||
- ParamTy->isIncompleteType() || ArgTy->isIncompleteType() ||
- ParamTy->isUndeducedType() || ArgTy->isUndeducedType())
- return;
-
- CharUnits ParamAlign = Context.getTypeAlignInChars(ParamTy);
- CharUnits ArgAlign = Context.getTypeAlignInChars(ArgTy);
-
- // If the argument is less aligned than the parameter, there is a
- // potential alignment issue.
- if (ArgAlign < ParamAlign)
- Diag(Loc, diag::warn_param_mismatched_alignment)
- << (int)ArgAlign.getQuantity() << (int)ParamAlign.getQuantity()
- << ParamName << (FDecl != nullptr) << FDecl;
-}
-
-void Sema::checkLifetimeCaptureBy(FunctionDecl *FD, bool IsMemberFunction,
- const Expr *ThisArg,
- ArrayRef<const Expr *> Args) {
- if (!FD || Args.empty())
- return;
- auto GetArgAt = [&](int Idx) -> const Expr * {
- if (Idx == LifetimeCaptureByAttr::Global ||
- Idx == LifetimeCaptureByAttr::Unknown)
- return nullptr;
- if (IsMemberFunction && Idx == 0)
- return ThisArg;
- return Args[Idx - IsMemberFunction];
- };
- auto HandleCaptureByAttr = [&](const LifetimeCaptureByAttr *Attr,
- unsigned ArgIdx) {
- if (!Attr)
- return;
-
- Expr *Captured = const_cast<Expr *>(GetArgAt(ArgIdx));
- for (int CapturingParamIdx : Attr->params()) {
- // lifetime_capture_by(this) case is handled in the lifetimebound expr
- // initialization codepath.
- if (CapturingParamIdx == LifetimeCaptureByAttr::This &&
- isa<CXXConstructorDecl>(FD))
- continue;
- Expr *Capturing = const_cast<Expr *>(GetArgAt(CapturingParamIdx));
- CapturingEntity CE{Capturing};
- // Ensure that 'Captured' outlives the 'Capturing' entity.
- checkCaptureByLifetime(*this, CE, Captured);
- }
- };
- for (unsigned I = 0; I < FD->getNumParams(); ++I)
- HandleCaptureByAttr(FD->getParamDecl(I)->getAttr<LifetimeCaptureByAttr>(),
- I + IsMemberFunction);
- // Check when the implicit object param is captured.
- if (IsMemberFunction) {
- TypeSourceInfo *TSI = FD->getTypeSourceInfo();
- if (!TSI)
- return;
- AttributedTypeLoc ATL;
- for (TypeLoc TL = TSI->getTypeLoc();
- (ATL = TL.getAsAdjusted<AttributedTypeLoc>());
- TL = ATL.getModifiedLoc())
- HandleCaptureByAttr(ATL.getAttrAs<LifetimeCaptureByAttr>(), 0);
- }
-}
-
-void Sema::checkCall(NamedDecl *FDecl, const FunctionProtoType *Proto,
- const Expr *ThisArg, ArrayRef<const Expr *> Args,
- bool IsMemberFunction, SourceLocation Loc,
- SourceRange Range, VariadicCallType CallType) {
- // FIXME: We should check as much as we can in the template definition.
- if (CurContext->isDependentContext())
- return;
-
- // Printf and scanf checking.
- llvm::SmallBitVector CheckedVarArgs;
- if (FDecl) {
- for (const auto *I : FDecl->specific_attrs<FormatMatchesAttr>()) {
- // Only create vector if there are format attributes.
- CheckedVarArgs.resize(Args.size());
- CheckFormatString(I, Args, IsMemberFunction, CallType, Loc, Range,
- CheckedVarArgs);
- }
-
- for (const auto *I : FDecl->specific_attrs<FormatAttr>()) {
- CheckedVarArgs.resize(Args.size());
- CheckFormatArguments(I, Args, IsMemberFunction, CallType, Loc, Range,
- CheckedVarArgs);
- }
- }
-
- // Refuse POD arguments that weren't caught by the format string
- // checks above.
- auto *FD = dyn_cast_or_null<FunctionDecl>(FDecl);
- if (CallType != VariadicCallType::DoesNotApply &&
- (!FD || FD->getBuiltinID() != Builtin::BI__noop)) {
- unsigned NumParams = Proto ? Proto->getNumParams()
- : isa_and_nonnull<FunctionDecl>(FDecl)
- ? cast<FunctionDecl>(FDecl)->getNumParams()
- : isa_and_nonnull<ObjCMethodDecl>(FDecl)
- ? cast<ObjCMethodDecl>(FDecl)->param_size()
- : 0;
-
- for (unsigned ArgIdx = NumParams; ArgIdx < Args.size(); ++ArgIdx) {
- // Args[ArgIdx] can be null in malformed code.
- if (const Expr *Arg = Args[ArgIdx]) {
- if (CheckedVarArgs.empty() || !CheckedVarArgs[ArgIdx])
- checkVariadicArgument(Arg, CallType);
- }
- }
- }
- if (FD)
- checkLifetimeCaptureBy(FD, IsMemberFunction, ThisArg, Args);
- if (FDecl || Proto) {
- CheckNonNullArguments(*this, FDecl, Proto, Args, Loc);
-
- // Type safety checking.
- if (FDecl) {
- for (const auto *I : FDecl->specific_attrs<ArgumentWithTypeTagAttr>())
- CheckArgumentWithTypeTag(I, Args, Loc);
- }
- }
-
- // Check that passed arguments match the alignment of original arguments.
- // Try to get the missing prototype from the declaration.
- if (!Proto && FDecl) {
- const auto *FT = FDecl->getFunctionType();
- if (isa_and_nonnull<FunctionProtoType>(FT))
- Proto = cast<FunctionProtoType>(FDecl->getFunctionType());
- }
- if (Proto) {
- // For variadic functions, we may have more args than parameters.
- // For some K&R functions, we may have less args than parameters.
- const auto N = std::min<unsigned>(Proto->getNumParams(), Args.size());
- bool IsScalableRet = Proto->getReturnType()->isSizelessVectorType();
- bool IsScalableArg = false;
- for (unsigned ArgIdx = 0; ArgIdx < N; ++ArgIdx) {
- // Args[ArgIdx] can be null in malformed code.
- if (const Expr *Arg = Args[ArgIdx]) {
- if (Arg->containsErrors())
- continue;
-
- if (Context.getTargetInfo().getTriple().isOSAIX() && FDecl && Arg &&
- FDecl->hasLinkage() &&
- FDecl->getFormalLinkage() != Linkage::Internal &&
- CallType == VariadicCallType::DoesNotApply)
- PPC().checkAIXMemberAlignment((Arg->getExprLoc()), Arg);
-
- QualType ParamTy = Proto->getParamType(ArgIdx);
- if (ParamTy->isSizelessVectorType())
- IsScalableArg = true;
- QualType ArgTy = Arg->getType();
- CheckArgAlignment(Arg->getExprLoc(), FDecl, std::to_string(ArgIdx + 1),
- ArgTy, ParamTy);
- }
- }
-
- // If the callee has an AArch64 SME attribute to indicate that it is an
- // __arm_streaming function, then the caller requires SME to be available.
- FunctionProtoType::ExtProtoInfo ExtInfo = Proto->getExtProtoInfo();
- if (ExtInfo.AArch64SMEAttributes & FunctionType::SME_PStateSMEnabledMask) {
- if (auto *CallerFD = dyn_cast<FunctionDecl>(CurContext)) {
- llvm::StringMap<bool> CallerFeatureMap;
- Context.getFunctionFeatureMap(CallerFeatureMap, CallerFD);
- if (!CallerFeatureMap.contains("sme"))
- Diag(Loc, diag::err_sme_call_in_non_sme_target);
- } else if (!Context.getTargetInfo().hasFeature("sme")) {
- Diag(Loc, diag::err_sme_call_in_non_sme_target);
- }
- }
-
- // If the call requires a streaming-mode change and has scalable vector
- // arguments or return values, then warn the user that the streaming and
- // non-streaming vector lengths may be different.
- // When both streaming and non-streaming vector lengths are defined and
- // mismatched, produce an error.
- const auto *CallerFD = dyn_cast<FunctionDecl>(CurContext);
- if (CallerFD && (!FD || !FD->getBuiltinID()) &&
- (IsScalableArg || IsScalableRet)) {
- bool IsCalleeStreaming =
- ExtInfo.AArch64SMEAttributes & FunctionType::SME_PStateSMEnabledMask;
- bool IsCalleeStreamingCompatible =
- ExtInfo.AArch64SMEAttributes &
- FunctionType::SME_PStateSMCompatibleMask;
- SemaARM::ArmStreamingType CallerFnType = getArmStreamingFnType(CallerFD);
- if (!IsCalleeStreamingCompatible &&
- (CallerFnType == SemaARM::ArmStreamingCompatible ||
- ((CallerFnType == SemaARM::ArmStreaming) ^ IsCalleeStreaming))) {
- const LangOptions &LO = getLangOpts();
- unsigned VL = LO.VScaleMin * 128;
- unsigned SVL = LO.VScaleStreamingMin * 128;
- bool IsVLMismatch = VL && SVL && VL != SVL;
-
- auto EmitDiag = [&](bool IsArg) {
- if (IsVLMismatch) {
- if (CallerFnType == SemaARM::ArmStreamingCompatible)
- // Emit warning for streaming-compatible callers
- Diag(Loc, diag::warn_sme_streaming_compatible_vl_mismatch)
- << IsArg << IsCalleeStreaming << SVL << VL;
- else
- // Emit error otherwise
- Diag(Loc, diag::err_sme_streaming_transition_vl_mismatch)
- << IsArg << SVL << VL;
- } else
- Diag(Loc, diag::warn_sme_streaming_pass_return_vl_to_non_streaming)
- << IsArg;
- };
-
- if (IsScalableArg)
- EmitDiag(true);
- if (IsScalableRet)
- EmitDiag(false);
- }
- }
-
- FunctionType::ArmStateValue CalleeArmZAState =
- FunctionType::getArmZAState(ExtInfo.AArch64SMEAttributes);
- FunctionType::ArmStateValue CalleeArmZT0State =
- FunctionType::getArmZT0State(ExtInfo.AArch64SMEAttributes);
- if (CalleeArmZAState != FunctionType::ARM_None ||
- CalleeArmZT0State != FunctionType::ARM_None) {
- bool CallerHasZAState = false;
- bool CallerHasZT0State = false;
- if (CallerFD) {
- auto *Attr = CallerFD->getAttr<ArmNewAttr>();
- if (Attr && Attr->isNewZA())
- CallerHasZAState = true;
- if (Attr && Attr->isNewZT0())
- CallerHasZT0State = true;
- if (const auto *FPT = CallerFD->getType()->getAs<FunctionProtoType>()) {
- CallerHasZAState |=
- FunctionType::getArmZAState(
- FPT->getExtProtoInfo().AArch64SMEAttributes) !=
- FunctionType::ARM_None;
- CallerHasZT0State |=
- FunctionType::getArmZT0State(
- FPT->getExtProtoInfo().AArch64SMEAttributes) !=
- FunctionType::ARM_None;
- }
- }
-
- if (CalleeArmZAState != FunctionType::ARM_None && !CallerHasZAState)
- Diag(Loc, diag::err_sme_za_call_no_za_state);
-
- if (CalleeArmZT0State != FunctionType::ARM_None && !CallerHasZT0State)
- Diag(Loc, diag::err_sme_zt0_call_no_zt0_state);
-
- if (CallerHasZAState && CalleeArmZAState == FunctionType::ARM_None &&
- CalleeArmZT0State != FunctionType::ARM_None) {
- Diag(Loc, diag::err_sme_unimplemented_za_save_restore);
- Diag(Loc, diag::note_sme_use_preserves_za);
- }
- }
- }
-
- if (FDecl && FDecl->hasAttr<AllocAlignAttr>()) {
- auto *AA = FDecl->getAttr<AllocAlignAttr>();
- const Expr *Arg = Args[AA->getParamIndex().getASTIndex()];
- if (!Arg->isValueDependent()) {
- Expr::EvalResult Align;
- if (Arg->EvaluateAsInt(Align, Context)) {
- const llvm::APSInt &I = Align.Val.getInt();
- if (!I.isPowerOf2())
- Diag(Arg->getExprLoc(), diag::warn_alignment_not_power_of_two)
- << Arg->getSourceRange();
-
- if (I > Sema::MaximumAlignment)
- Diag(Arg->getExprLoc(), diag::warn_assume_aligned_too_great)
- << Arg->getSourceRange() << Sema::MaximumAlignment;
- }
- }
- }
-
- if (FD)
- diagnoseArgDependentDiagnoseIfAttrs(FD, ThisArg, Args, Loc);
-}
-
-void Sema::CheckConstrainedAuto(const AutoType *AutoT, SourceLocation Loc) {
- if (TemplateDecl *Decl = AutoT->getTypeConstraintConcept()) {
- DiagnoseUseOfDecl(Decl, Loc);
- }
-}
-
-void Sema::CheckConstructorCall(FunctionDecl *FDecl, QualType ThisType,
- ArrayRef<const Expr *> Args,
- const FunctionProtoType *Proto,
- SourceLocation Loc) {
- VariadicCallType CallType = Proto->isVariadic()
- ? VariadicCallType::Constructor
- : VariadicCallType::DoesNotApply;
-
- auto *Ctor = cast<CXXConstructorDecl>(FDecl);
- CheckArgAlignment(
- Loc, FDecl, "'this'", Context.getPointerType(ThisType),
- Context.getPointerType(Ctor->getFunctionObjectParameterType()));
-
- checkCall(FDecl, Proto, /*ThisArg=*/nullptr, Args, /*IsMemberFunction=*/true,
- Loc, SourceRange(), CallType);
-}
-
-bool Sema::CheckFunctionCall(FunctionDecl *FDecl, CallExpr *TheCall,
- const FunctionProtoType *Proto) {
- bool IsMemberOperatorCall = isa<CXXOperatorCallExpr>(TheCall) &&
- isa<CXXMethodDecl>(FDecl);
- bool IsMemberFunction = isa<CXXMemberCallExpr>(TheCall) ||
- IsMemberOperatorCall;
- VariadicCallType CallType = getVariadicCallType(FDecl, Proto,
- TheCall->getCallee());
- Expr** Args = TheCall->getArgs();
- unsigned NumArgs = TheCall->getNumArgs();
-
- Expr *ImplicitThis = nullptr;
- if (IsMemberOperatorCall && !FDecl->hasCXXExplicitFunctionObjectParameter()) {
- // If this is a call to a member operator, hide the first
- // argument from checkCall.
- // FIXME: Our choice of AST representation here is less than ideal.
- ImplicitThis = Args[0];
- ++Args;
- --NumArgs;
- } else if (IsMemberFunction && !FDecl->isStatic() &&
- !FDecl->hasCXXExplicitFunctionObjectParameter())
- ImplicitThis =
- cast<CXXMemberCallExpr>(TheCall)->getImplicitObjectArgument();
-
- if (ImplicitThis) {
- // ImplicitThis may or may not be a pointer, depending on whether . or -> is
- // used.
- QualType ThisType = ImplicitThis->getType();
- if (!ThisType->isPointerType()) {
- assert(!ThisType->isReferenceType());
- ThisType = Context.getPointerType(ThisType);
- }
-
- QualType ThisTypeFromDecl = Context.getPointerType(
- cast<CXXMethodDecl>(FDecl)->getFunctionObjectParameterType());
-
- CheckArgAlignment(TheCall->getRParenLoc(), FDecl, "'this'", ThisType,
- ThisTypeFromDecl);
- }
-
- checkCall(FDecl, Proto, ImplicitThis, llvm::ArrayRef(Args, NumArgs),
- IsMemberFunction, TheCall->getRParenLoc(),
- TheCall->getCallee()->getSourceRange(), CallType);
-
- IdentifierInfo *FnInfo = FDecl->getIdentifier();
- // None of the checks below are needed for functions that don't have
- // simple names (e.g., C++ conversion functions).
- if (!FnInfo)
- return false;
-
- // Enforce TCB except for builtin calls, which are always allowed.
- if (FDecl->getBuiltinID() == 0)
- CheckTCBEnforcement(TheCall->getExprLoc(), FDecl);
-
- CheckAbsoluteValueFunction(TheCall, FDecl);
- CheckMaxUnsignedZero(TheCall, FDecl);
- CheckInfNaNFunction(TheCall, FDecl);
-
- if (getLangOpts().ObjC)
- ObjC().DiagnoseCStringFormatDirectiveInCFAPI(FDecl, Args, NumArgs);
-
- unsigned CMId = FDecl->getMemoryFunctionKind();
-
- // Handle memory setting and copying functions.
- switch (CMId) {
- case 0:
- return false;
- case Builtin::BIstrlcpy: // fallthrough
- case Builtin::BIstrlcat:
- CheckStrlcpycatArguments(TheCall, FnInfo);
- break;
- case Builtin::BIstrncat:
- CheckStrncatArguments(TheCall, FnInfo);
- break;
- case Builtin::BIfree:
- CheckFreeArguments(TheCall);
- break;
- default:
- CheckMemaccessArguments(TheCall, CMId, FnInfo);
- }
-
- return false;
-}
-
-bool Sema::CheckPointerCall(NamedDecl *NDecl, CallExpr *TheCall,
- const FunctionProtoType *Proto) {
- QualType Ty;
- if (const auto *V = dyn_cast<VarDecl>(NDecl))
- Ty = V->getType().getNonReferenceType();
- else if (const auto *F = dyn_cast<FieldDecl>(NDecl))
- Ty = F->getType().getNonReferenceType();
- else
- return false;
-
- if (!Ty->isBlockPointerType() && !Ty->isFunctionPointerType() &&
- !Ty->isFunctionProtoType())
- return false;
-
- VariadicCallType CallType;
- if (!Proto || !Proto->isVariadic()) {
- CallType = VariadicCallType::DoesNotApply;
- } else if (Ty->isBlockPointerType()) {
- CallType = VariadicCallType::Block;
- } else { // Ty->isFunctionPointerType()
- CallType = VariadicCallType::Function;
- }
-
- checkCall(NDecl, Proto, /*ThisArg=*/nullptr,
- llvm::ArrayRef(TheCall->getArgs(), TheCall->getNumArgs()),
- /*IsMemberFunction=*/false, TheCall->getRParenLoc(),
- TheCall->getCallee()->getSourceRange(), CallType);
-
- return false;
-}
-
-bool Sema::CheckOtherCall(CallExpr *TheCall, const FunctionProtoType *Proto) {
- VariadicCallType CallType = getVariadicCallType(/*FDecl=*/nullptr, Proto,
- TheCall->getCallee());
- checkCall(/*FDecl=*/nullptr, Proto, /*ThisArg=*/nullptr,
- llvm::ArrayRef(TheCall->getArgs(), TheCall->getNumArgs()),
- /*IsMemberFunction=*/false, TheCall->getRParenLoc(),
- TheCall->getCallee()->getSourceRange(), CallType);
-
- return false;
-}
-
-static bool isValidOrderingForOp(int64_t Ordering, AtomicExpr::AtomicOp Op) {
- if (!llvm::isValidAtomicOrderingCABI(Ordering))
- return false;
-
- auto OrderingCABI = (llvm::AtomicOrderingCABI)Ordering;
- switch (Op) {
- case AtomicExpr::AO__c11_atomic_init:
- case AtomicExpr::AO__opencl_atomic_init:
- llvm_unreachable("There is no ordering argument for an init");
-
- case AtomicExpr::AO__c11_atomic_load:
- case AtomicExpr::AO__opencl_atomic_load:
- case AtomicExpr::AO__hip_atomic_load:
- case AtomicExpr::AO__atomic_load_n:
- case AtomicExpr::AO__atomic_load:
- case AtomicExpr::AO__scoped_atomic_load_n:
- case AtomicExpr::AO__scoped_atomic_load:
- return OrderingCABI != llvm::AtomicOrderingCABI::release &&
- OrderingCABI != llvm::AtomicOrderingCABI::acq_rel;
-
- case AtomicExpr::AO__c11_atomic_store:
- case AtomicExpr::AO__opencl_atomic_store:
- case AtomicExpr::AO__hip_atomic_store:
- case AtomicExpr::AO__atomic_store:
- case AtomicExpr::AO__atomic_store_n:
- case AtomicExpr::AO__scoped_atomic_store:
- case AtomicExpr::AO__scoped_atomic_store_n:
- case AtomicExpr::AO__atomic_clear:
- return OrderingCABI != llvm::AtomicOrderingCABI::consume &&
- OrderingCABI != llvm::AtomicOrderingCABI::acquire &&
- OrderingCABI != llvm::AtomicOrderingCABI::acq_rel;
-
- default:
- return true;
- }
-}
-
-ExprResult Sema::AtomicOpsOverloaded(ExprResult TheCallResult,
- AtomicExpr::AtomicOp Op) {
- CallExpr *TheCall = cast<CallExpr>(TheCallResult.get());
- DeclRefExpr *DRE =cast<DeclRefExpr>(TheCall->getCallee()->IgnoreParenCasts());
- MultiExprArg Args{TheCall->getArgs(), TheCall->getNumArgs()};
- return BuildAtomicExpr({TheCall->getBeginLoc(), TheCall->getEndLoc()},
- DRE->getSourceRange(), TheCall->getRParenLoc(), Args,
- Op);
-}
-
-ExprResult Sema::BuildAtomicExpr(SourceRange CallRange, SourceRange ExprRange,
- SourceLocation RParenLoc, MultiExprArg Args,
- AtomicExpr::AtomicOp Op,
- AtomicArgumentOrder ArgOrder) {
- // All the non-OpenCL operations take one of the following forms.
- // The OpenCL operations take the __c11 forms with one extra argument for
- // synchronization scope.
- enum {
- // C __c11_atomic_init(A *, C)
- Init,
-
- // C __c11_atomic_load(A *, int)
- Load,
-
- // void __atomic_load(A *, CP, int)
- LoadCopy,
-
- // void __atomic_store(A *, CP, int)
- Copy,
-
- // C __c11_atomic_add(A *, M, int)
- Arithmetic,
-
- // C __atomic_exchange_n(A *, CP, int)
- Xchg,
-
- // void __atomic_exchange(A *, C *, CP, int)
- GNUXchg,
-
- // bool __c11_atomic_compare_exchange_strong(A *, C *, CP, int, int)
- C11CmpXchg,
-
- // bool __atomic_compare_exchange(A *, C *, CP, bool, int, int)
- GNUCmpXchg,
-
- // bool __atomic_test_and_set(A *, int)
- TestAndSetByte,
-
- // void __atomic_clear(A *, int)
- ClearByte,
- } Form = Init;
-
- const unsigned NumForm = ClearByte + 1;
- const unsigned NumArgs[] = {2, 2, 3, 3, 3, 3, 4, 5, 6, 2, 2};
- const unsigned NumVals[] = {1, 0, 1, 1, 1, 1, 2, 2, 3, 0, 0};
- // where:
- // C is an appropriate type,
- // A is volatile _Atomic(C) for __c11 builtins and is C for GNU builtins,
- // CP is C for __c11 builtins and GNU _n builtins and is C * otherwise,
- // M is C if C is an integer, and ptrdiff_t if C is a pointer, and
- // the int parameters are for orderings.
-
- static_assert(sizeof(NumArgs)/sizeof(NumArgs[0]) == NumForm
- && sizeof(NumVals)/sizeof(NumVals[0]) == NumForm,
- "need to update code for modified forms");
- static_assert(AtomicExpr::AO__atomic_add_fetch == 0 &&
- AtomicExpr::AO__atomic_xor_fetch + 1 ==
- AtomicExpr::AO__c11_atomic_compare_exchange_strong,
- "need to update code for modified C11 atomics");
- bool IsOpenCL = Op >= AtomicExpr::AO__opencl_atomic_compare_exchange_strong &&
- Op <= AtomicExpr::AO__opencl_atomic_store;
- bool IsHIP = Op >= AtomicExpr::AO__hip_atomic_compare_exchange_strong &&
- Op <= AtomicExpr::AO__hip_atomic_store;
- bool IsScoped = Op >= AtomicExpr::AO__scoped_atomic_add_fetch &&
- Op <= AtomicExpr::AO__scoped_atomic_xor_fetch;
- bool IsC11 = (Op >= AtomicExpr::AO__c11_atomic_compare_exchange_strong &&
- Op <= AtomicExpr::AO__c11_atomic_store) ||
- IsOpenCL;
- bool IsN = Op == AtomicExpr::AO__atomic_load_n ||
- Op == AtomicExpr::AO__atomic_store_n ||
- Op == AtomicExpr::AO__atomic_exchange_n ||
- Op == AtomicExpr::AO__atomic_compare_exchange_n ||
- Op == AtomicExpr::AO__scoped_atomic_load_n ||
- Op == AtomicExpr::AO__scoped_atomic_store_n ||
- Op == AtomicExpr::AO__scoped_atomic_exchange_n ||
- Op == AtomicExpr::AO__scoped_atomic_compare_exchange_n;
- // Bit mask for extra allowed value types other than integers for atomic
- // arithmetic operations. Add/sub allow pointer and floating point. Min/max
- // allow floating point.
- enum ArithOpExtraValueType {
- AOEVT_None = 0,
- AOEVT_Pointer = 1,
- AOEVT_FP = 2,
- };
- unsigned ArithAllows = AOEVT_None;
-
- switch (Op) {
- case AtomicExpr::AO__c11_atomic_init:
- case AtomicExpr::AO__opencl_atomic_init:
- Form = Init;
- break;
-
- case AtomicExpr::AO__c11_atomic_load:
- case AtomicExpr::AO__opencl_atomic_load:
- case AtomicExpr::AO__hip_atomic_load:
- case AtomicExpr::AO__atomic_load_n:
- case AtomicExpr::AO__scoped_atomic_load_n:
- Form = Load;
- break;
-
- case AtomicExpr::AO__atomic_load:
- case AtomicExpr::AO__scoped_atomic_load:
- Form = LoadCopy;
- break;
-
- case AtomicExpr::AO__c11_atomic_store:
- case AtomicExpr::AO__opencl_atomic_store:
- case AtomicExpr::AO__hip_atomic_store:
- case AtomicExpr::AO__atomic_store:
- case AtomicExpr::AO__atomic_store_n:
- case AtomicExpr::AO__scoped_atomic_store:
- case AtomicExpr::AO__scoped_atomic_store_n:
- Form = Copy;
- break;
- case AtomicExpr::AO__atomic_fetch_add:
- case AtomicExpr::AO__atomic_fetch_sub:
- case AtomicExpr::AO__atomic_add_fetch:
- case AtomicExpr::AO__atomic_sub_fetch:
- case AtomicExpr::AO__scoped_atomic_fetch_add:
- case AtomicExpr::AO__scoped_atomic_fetch_sub:
- case AtomicExpr::AO__scoped_atomic_add_fetch:
- case AtomicExpr::AO__scoped_atomic_sub_fetch:
- case AtomicExpr::AO__c11_atomic_fetch_add:
- case AtomicExpr::AO__c11_atomic_fetch_sub:
- case AtomicExpr::AO__opencl_atomic_fetch_add:
- case AtomicExpr::AO__opencl_atomic_fetch_sub:
- case AtomicExpr::AO__hip_atomic_fetch_add:
- case AtomicExpr::AO__hip_atomic_fetch_sub:
- ArithAllows = AOEVT_Pointer | AOEVT_FP;
- Form = Arithmetic;
- break;
- case AtomicExpr::AO__atomic_fetch_max:
- case AtomicExpr::AO__atomic_fetch_min:
- case AtomicExpr::AO__atomic_max_fetch:
- case AtomicExpr::AO__atomic_min_fetch:
- case AtomicExpr::AO__scoped_atomic_fetch_max:
- case AtomicExpr::AO__scoped_atomic_fetch_min:
- case AtomicExpr::AO__scoped_atomic_max_fetch:
- case AtomicExpr::AO__scoped_atomic_min_fetch:
- case AtomicExpr::AO__c11_atomic_fetch_max:
- case AtomicExpr::AO__c11_atomic_fetch_min:
- case AtomicExpr::AO__opencl_atomic_fetch_max:
- case AtomicExpr::AO__opencl_atomic_fetch_min:
- case AtomicExpr::AO__hip_atomic_fetch_max:
- case AtomicExpr::AO__hip_atomic_fetch_min:
- ArithAllows = AOEVT_FP;
- Form = Arithmetic;
- break;
- case AtomicExpr::AO__c11_atomic_fetch_and:
- case AtomicExpr::AO__c11_atomic_fetch_or:
- case AtomicExpr::AO__c11_atomic_fetch_xor:
- case AtomicExpr::AO__hip_atomic_fetch_and:
- case AtomicExpr::AO__hip_atomic_fetch_or:
- case AtomicExpr::AO__hip_atomic_fetch_xor:
- case AtomicExpr::AO__c11_atomic_fetch_nand:
- case AtomicExpr::AO__opencl_atomic_fetch_and:
- case AtomicExpr::AO__opencl_atomic_fetch_or:
- case AtomicExpr::AO__opencl_atomic_fetch_xor:
- case AtomicExpr::AO__atomic_fetch_and:
- case AtomicExpr::AO__atomic_fetch_or:
- case AtomicExpr::AO__atomic_fetch_xor:
- case AtomicExpr::AO__atomic_fetch_nand:
- case AtomicExpr::AO__atomic_and_fetch:
- case AtomicExpr::AO__atomic_or_fetch:
- case AtomicExpr::AO__atomic_xor_fetch:
- case AtomicExpr::AO__atomic_nand_fetch:
- case AtomicExpr::AO__atomic_fetch_uinc:
- case AtomicExpr::AO__atomic_fetch_udec:
- case AtomicExpr::AO__scoped_atomic_fetch_and:
- case AtomicExpr::AO__scoped_atomic_fetch_or:
- case AtomicExpr::AO__scoped_atomic_fetch_xor:
- case AtomicExpr::AO__scoped_atomic_fetch_nand:
- case AtomicExpr::AO__scoped_atomic_and_fetch:
- case AtomicExpr::AO__scoped_atomic_or_fetch:
- case AtomicExpr::AO__scoped_atomic_xor_fetch:
- case AtomicExpr::AO__scoped_atomic_nand_fetch:
- case AtomicExpr::AO__scoped_atomic_fetch_uinc:
- case AtomicExpr::AO__scoped_atomic_fetch_udec:
- Form = Arithmetic;
- break;
-
- case AtomicExpr::AO__c11_atomic_exchange:
- case AtomicExpr::AO__hip_atomic_exchange:
- case AtomicExpr::AO__opencl_atomic_exchange:
- case AtomicExpr::AO__atomic_exchange_n:
- case AtomicExpr::AO__scoped_atomic_exchange_n:
- Form = Xchg;
- break;
-
- case AtomicExpr::AO__atomic_exchange:
- case AtomicExpr::AO__scoped_atomic_exchange:
- Form = GNUXchg;
- break;
-
- case AtomicExpr::AO__c11_atomic_compare_exchange_strong:
- case AtomicExpr::AO__c11_atomic_compare_exchange_weak:
- case AtomicExpr::AO__hip_atomic_compare_exchange_strong:
- case AtomicExpr::AO__opencl_atomic_compare_exchange_strong:
- case AtomicExpr::AO__opencl_atomic_compare_exchange_weak:
- case AtomicExpr::AO__hip_atomic_compare_exchange_weak:
- Form = C11CmpXchg;
- break;
-
- case AtomicExpr::AO__atomic_compare_exchange:
- case AtomicExpr::AO__atomic_compare_exchange_n:
- case AtomicExpr::AO__scoped_atomic_compare_exchange:
- case AtomicExpr::AO__scoped_atomic_compare_exchange_n:
- Form = GNUCmpXchg;
- break;
-
- case AtomicExpr::AO__atomic_test_and_set:
- Form = TestAndSetByte;
- break;
-
- case AtomicExpr::AO__atomic_clear:
- Form = ClearByte;
- break;
- }
-
- unsigned AdjustedNumArgs = NumArgs[Form];
- if ((IsOpenCL || IsHIP || IsScoped) &&
- Op != AtomicExpr::AO__opencl_atomic_init)
- ++AdjustedNumArgs;
- // Check we have the right number of arguments.
- if (Args.size() < AdjustedNumArgs) {
- Diag(CallRange.getEnd(), diag::err_typecheck_call_too_few_args)
- << 0 << AdjustedNumArgs << static_cast<unsigned>(Args.size())
- << /*is non object*/ 0 << ExprRange;
- return ExprError();
- } else if (Args.size() > AdjustedNumArgs) {
- Diag(Args[AdjustedNumArgs]->getBeginLoc(),
- diag::err_typecheck_call_too_many_args)
- << 0 << AdjustedNumArgs << static_cast<unsigned>(Args.size())
- << /*is non object*/ 0 << ExprRange;
- return ExprError();
- }
-
- // Inspect the first argument of the atomic operation.
- Expr *Ptr = Args[0];
- ExprResult ConvertedPtr = DefaultFunctionArrayLvalueConversion(Ptr);
- if (ConvertedPtr.isInvalid())
- return ExprError();
-
- Ptr = ConvertedPtr.get();
- const PointerType *pointerType = Ptr->getType()->getAs<PointerType>();
- if (!pointerType) {
- Diag(ExprRange.getBegin(), diag::err_atomic_builtin_must_be_pointer)
- << Ptr->getType() << 0 << Ptr->getSourceRange();
- return ExprError();
- }
-
- // For a __c11 builtin, this should be a pointer to an _Atomic type.
- QualType AtomTy = pointerType->getPointeeType(); // 'A'
- QualType ValType = AtomTy; // 'C'
- if (IsC11) {
- if (!AtomTy->isAtomicType()) {
- Diag(ExprRange.getBegin(), diag::err_atomic_op_needs_atomic)
- << Ptr->getType() << Ptr->getSourceRange();
- return ExprError();
- }
- if ((Form != Load && Form != LoadCopy && AtomTy.isConstQualified()) ||
- AtomTy.getAddressSpace() == LangAS::opencl_constant) {
- Diag(ExprRange.getBegin(), diag::err_atomic_op_needs_non_const_atomic)
- << (AtomTy.isConstQualified() ? 0 : 1) << Ptr->getType()
- << Ptr->getSourceRange();
- return ExprError();
- }
- ValType = AtomTy->castAs<AtomicType>()->getValueType();
- } else if (Form != Load && Form != LoadCopy) {
- if (ValType.isConstQualified()) {
- Diag(ExprRange.getBegin(), diag::err_atomic_op_needs_non_const_pointer)
- << Ptr->getType() << Ptr->getSourceRange();
- return ExprError();
- }
- }
-
- if (Form != TestAndSetByte && Form != ClearByte) {
- // Pointer to object of size zero is not allowed.
- if (RequireCompleteType(Ptr->getBeginLoc(), AtomTy,
- diag::err_incomplete_type))
- return ExprError();
-
- if (Context.getTypeInfoInChars(AtomTy).Width.isZero()) {
- Diag(ExprRange.getBegin(), diag::err_atomic_builtin_must_be_pointer)
- << Ptr->getType() << 1 << Ptr->getSourceRange();
- return ExprError();
- }
- } else {
- // The __atomic_clear and __atomic_test_and_set intrinsics accept any
- // non-const pointer type, including void* and pointers to incomplete
- // structs, but only access the first byte.
- AtomTy = Context.CharTy;
- AtomTy = AtomTy.withCVRQualifiers(
- pointerType->getPointeeType().getCVRQualifiers());
- QualType PointerQT = Context.getPointerType(AtomTy);
- pointerType = PointerQT->getAs<PointerType>();
- Ptr = ImpCastExprToType(Ptr, PointerQT, CK_BitCast).get();
- ValType = AtomTy;
- }
-
- PointerAuthQualifier PointerAuth = AtomTy.getPointerAuth();
- if (PointerAuth && PointerAuth.isAddressDiscriminated()) {
- Diag(ExprRange.getBegin(),
- diag::err_atomic_op_needs_non_address_discriminated_pointer)
- << 0 << Ptr->getType() << Ptr->getSourceRange();
- return ExprError();
- }
-
- // For an arithmetic operation, the implied arithmetic must be well-formed.
- if (Form == Arithmetic) {
- // GCC does not enforce these rules for GNU atomics, but we do to help catch
- // trivial type errors.
- auto IsAllowedValueType = [&](QualType ValType,
- unsigned AllowedType) -> bool {
- if (ValType->isIntegerType())
- return true;
- if (ValType->isPointerType())
- return AllowedType & AOEVT_Pointer;
- if (!(ValType->isFloatingType() && (AllowedType & AOEVT_FP)))
- return false;
- // LLVM Parser does not allow atomicrmw with x86_fp80 type.
- if (ValType->isSpecificBuiltinType(BuiltinType::LongDouble) &&
- &Context.getTargetInfo().getLongDoubleFormat() ==
- &llvm::APFloat::x87DoubleExtended())
- return false;
- return true;
- };
- if (!IsAllowedValueType(ValType, ArithAllows)) {
- auto DID = ArithAllows & AOEVT_FP
- ? (ArithAllows & AOEVT_Pointer
- ? diag::err_atomic_op_needs_atomic_int_ptr_or_fp
- : diag::err_atomic_op_needs_atomic_int_or_fp)
- : diag::err_atomic_op_needs_atomic_int;
- Diag(ExprRange.getBegin(), DID)
- << IsC11 << Ptr->getType() << Ptr->getSourceRange();
- return ExprError();
- }
- if (IsC11 && ValType->isPointerType() &&
- RequireCompleteType(Ptr->getBeginLoc(), ValType->getPointeeType(),
- diag::err_incomplete_type)) {
- return ExprError();
- }
- } else if (IsN && !ValType->isIntegerType() && !ValType->isPointerType()) {
- // For __atomic_*_n operations, the value type must be a scalar integral or
- // pointer type which is 1, 2, 4, 8 or 16 bytes in length.
- Diag(ExprRange.getBegin(), diag::err_atomic_op_needs_atomic_int_or_ptr)
- << IsC11 << Ptr->getType() << Ptr->getSourceRange();
- return ExprError();
- }
-
- if (!IsC11 && !AtomTy.isTriviallyCopyableType(Context) &&
- !AtomTy->isScalarType()) {
- // For GNU atomics, require a trivially-copyable type. This is not part of
- // the GNU atomics specification but we enforce it for consistency with
- // other atomics which generally all require a trivially-copyable type. This
- // is because atomics just copy bits.
- Diag(ExprRange.getBegin(), diag::err_atomic_op_needs_trivial_copy)
- << Ptr->getType() << Ptr->getSourceRange();
- return ExprError();
- }
-
- switch (ValType.getObjCLifetime()) {
- case Qualifiers::OCL_None:
- case Qualifiers::OCL_ExplicitNone:
- // okay
- break;
-
- case Qualifiers::OCL_Weak:
- case Qualifiers::OCL_Strong:
- case Qualifiers::OCL_Autoreleasing:
- // FIXME: Can this happen? By this point, ValType should be known
- // to be trivially copyable.
- Diag(ExprRange.getBegin(), diag::err_arc_atomic_ownership)
- << ValType << Ptr->getSourceRange();
- return ExprError();
- }
-
- // All atomic operations have an overload which takes a pointer to a volatile
- // 'A'. We shouldn't let the volatile-ness of the pointee-type inject itself
- // into the result or the other operands. Similarly atomic_load takes a
- // pointer to a const 'A'.
- ValType.removeLocalVolatile();
- ValType.removeLocalConst();
- QualType ResultType = ValType;
- if (Form == Copy || Form == LoadCopy || Form == GNUXchg || Form == Init ||
- Form == ClearByte)
- ResultType = Context.VoidTy;
- else if (Form == C11CmpXchg || Form == GNUCmpXchg || Form == TestAndSetByte)
- ResultType = Context.BoolTy;
-
- // The type of a parameter passed 'by value'. In the GNU atomics, such
- // arguments are actually passed as pointers.
- QualType ByValType = ValType; // 'CP'
- bool IsPassedByAddress = false;
- if (!IsC11 && !IsHIP && !IsN) {
- ByValType = Ptr->getType();
- IsPassedByAddress = true;
- }
-
- SmallVector<Expr *, 5> APIOrderedArgs;
- if (ArgOrder == Sema::AtomicArgumentOrder::AST) {
- APIOrderedArgs.push_back(Args[0]);
- switch (Form) {
- case Init:
- case Load:
- APIOrderedArgs.push_back(Args[1]); // Val1/Order
- break;
- case LoadCopy:
- case Copy:
- case Arithmetic:
- case Xchg:
- APIOrderedArgs.push_back(Args[2]); // Val1
- APIOrderedArgs.push_back(Args[1]); // Order
- break;
- case GNUXchg:
- APIOrderedArgs.push_back(Args[2]); // Val1
- APIOrderedArgs.push_back(Args[3]); // Val2
- APIOrderedArgs.push_back(Args[1]); // Order
- break;
- case C11CmpXchg:
- APIOrderedArgs.push_back(Args[2]); // Val1
- APIOrderedArgs.push_back(Args[4]); // Val2
- APIOrderedArgs.push_back(Args[1]); // Order
- APIOrderedArgs.push_back(Args[3]); // OrderFail
- break;
- case GNUCmpXchg:
- APIOrderedArgs.push_back(Args[2]); // Val1
- APIOrderedArgs.push_back(Args[4]); // Val2
- APIOrderedArgs.push_back(Args[5]); // Weak
- APIOrderedArgs.push_back(Args[1]); // Order
- APIOrderedArgs.push_back(Args[3]); // OrderFail
- break;
- case TestAndSetByte:
- case ClearByte:
- APIOrderedArgs.push_back(Args[1]); // Order
- break;
- }
- } else
- APIOrderedArgs.append(Args.begin(), Args.end());
-
- // The first argument's non-CV pointer type is used to deduce the type of
- // subsequent arguments, except for:
- // - weak flag (always converted to bool)
- // - memory order (always converted to int)
- // - scope (always converted to int)
- for (unsigned i = 0; i != APIOrderedArgs.size(); ++i) {
- QualType Ty;
- if (i < NumVals[Form] + 1) {
- switch (i) {
- case 0:
- // The first argument is always a pointer. It has a fixed type.
- // It is always dereferenced, a nullptr is undefined.
- CheckNonNullArgument(*this, APIOrderedArgs[i], ExprRange.getBegin());
- // Nothing else to do: we already know all we want about this pointer.
- continue;
- case 1:
- // The second argument is the non-atomic operand. For arithmetic, this
- // is always passed by value, and for a compare_exchange it is always
- // passed by address. For the rest, GNU uses by-address and C11 uses
- // by-value.
- assert(Form != Load);
- if (Form == Arithmetic && ValType->isPointerType())
- Ty = Context.getPointerDiffType();
- else if (Form == Init || Form == Arithmetic)
- Ty = ValType;
- else if (Form == Copy || Form == Xchg) {
- if (IsPassedByAddress) {
- // The value pointer is always dereferenced, a nullptr is undefined.
- CheckNonNullArgument(*this, APIOrderedArgs[i],
- ExprRange.getBegin());
- }
- Ty = ByValType;
- } else {
- Expr *ValArg = APIOrderedArgs[i];
- // The value pointer is always dereferenced, a nullptr is undefined.
- CheckNonNullArgument(*this, ValArg, ExprRange.getBegin());
- LangAS AS = LangAS::Default;
- // Keep address space of non-atomic pointer type.
- if (const PointerType *PtrTy =
- ValArg->getType()->getAs<PointerType>()) {
- AS = PtrTy->getPointeeType().getAddressSpace();
- }
- Ty = Context.getPointerType(
- Context.getAddrSpaceQualType(ValType.getUnqualifiedType(), AS));
- }
- break;
- case 2:
- // The third argument to compare_exchange / GNU exchange is the desired
- // value, either by-value (for the C11 and *_n variant) or as a pointer.
- if (IsPassedByAddress)
- CheckNonNullArgument(*this, APIOrderedArgs[i], ExprRange.getBegin());
- Ty = ByValType;
- break;
- case 3:
- // The fourth argument to GNU compare_exchange is a 'weak' flag.
- Ty = Context.BoolTy;
- break;
- }
- } else {
- // The order(s) and scope are always converted to int.
- Ty = Context.IntTy;
- }
-
- InitializedEntity Entity =
- InitializedEntity::InitializeParameter(Context, Ty, false);
- ExprResult Arg = APIOrderedArgs[i];
- Arg = PerformCopyInitialization(Entity, SourceLocation(), Arg);
- if (Arg.isInvalid())
- return true;
- APIOrderedArgs[i] = Arg.get();
- }
-
- // Permute the arguments into a 'consistent' order.
- SmallVector<Expr*, 5> SubExprs;
- SubExprs.push_back(Ptr);
- switch (Form) {
- case Init:
- // Note, AtomicExpr::getVal1() has a special case for this atomic.
- SubExprs.push_back(APIOrderedArgs[1]); // Val1
- break;
- case Load:
- case TestAndSetByte:
- case ClearByte:
- SubExprs.push_back(APIOrderedArgs[1]); // Order
- break;
- case LoadCopy:
- case Copy:
- case Arithmetic:
- case Xchg:
- SubExprs.push_back(APIOrderedArgs[2]); // Order
- SubExprs.push_back(APIOrderedArgs[1]); // Val1
- break;
- case GNUXchg:
- // Note, AtomicExpr::getVal2() has a special case for this atomic.
- SubExprs.push_back(APIOrderedArgs[3]); // Order
- SubExprs.push_back(APIOrderedArgs[1]); // Val1
- SubExprs.push_back(APIOrderedArgs[2]); // Val2
- break;
- case C11CmpXchg:
- SubExprs.push_back(APIOrderedArgs[3]); // Order
- SubExprs.push_back(APIOrderedArgs[1]); // Val1
- SubExprs.push_back(APIOrderedArgs[4]); // OrderFail
- SubExprs.push_back(APIOrderedArgs[2]); // Val2
- break;
- case GNUCmpXchg:
- SubExprs.push_back(APIOrderedArgs[4]); // Order
- SubExprs.push_back(APIOrderedArgs[1]); // Val1
- SubExprs.push_back(APIOrderedArgs[5]); // OrderFail
- SubExprs.push_back(APIOrderedArgs[2]); // Val2
- SubExprs.push_back(APIOrderedArgs[3]); // Weak
- break;
- }
-
- // If the memory orders are constants, check they are valid.
- if (SubExprs.size() >= 2 && Form != Init) {
- std::optional<llvm::APSInt> Success =
- SubExprs[1]->getIntegerConstantExpr(Context);
- if (Success && !isValidOrderingForOp(Success->getSExtValue(), Op)) {
- Diag(SubExprs[1]->getBeginLoc(),
- diag::warn_atomic_op_has_invalid_memory_order)
- << /*success=*/(Form == C11CmpXchg || Form == GNUCmpXchg)
- << SubExprs[1]->getSourceRange();
- }
- if (SubExprs.size() >= 5) {
- if (std::optional<llvm::APSInt> Failure =
- SubExprs[3]->getIntegerConstantExpr(Context)) {
- if (!llvm::is_contained(
- {llvm::AtomicOrderingCABI::relaxed,
- llvm::AtomicOrderingCABI::consume,
- llvm::AtomicOrderingCABI::acquire,
- llvm::AtomicOrderingCABI::seq_cst},
- (llvm::AtomicOrderingCABI)Failure->getSExtValue())) {
- Diag(SubExprs[3]->getBeginLoc(),
- diag::warn_atomic_op_has_invalid_memory_order)
- << /*failure=*/2 << SubExprs[3]->getSourceRange();
- }
- }
- }
- }
-
- if (auto ScopeModel = AtomicExpr::getScopeModel(Op)) {
- auto *Scope = Args[Args.size() - 1];
- if (std::optional<llvm::APSInt> Result =
- Scope->getIntegerConstantExpr(Context)) {
- if (!ScopeModel->isValid(Result->getZExtValue()))
- Diag(Scope->getBeginLoc(), diag::err_atomic_op_has_invalid_sync_scope)
- << Scope->getSourceRange();
- }
- SubExprs.push_back(Scope);
- }
-
- AtomicExpr *AE = new (Context)
- AtomicExpr(ExprRange.getBegin(), SubExprs, ResultType, Op, RParenLoc);
-
- if ((Op == AtomicExpr::AO__c11_atomic_load ||
- Op == AtomicExpr::AO__c11_atomic_store ||
- Op == AtomicExpr::AO__opencl_atomic_load ||
- Op == AtomicExpr::AO__hip_atomic_load ||
- Op == AtomicExpr::AO__opencl_atomic_store ||
- Op == AtomicExpr::AO__hip_atomic_store) &&
- Context.AtomicUsesUnsupportedLibcall(AE))
- Diag(AE->getBeginLoc(), diag::err_atomic_load_store_uses_lib)
- << ((Op == AtomicExpr::AO__c11_atomic_load ||
- Op == AtomicExpr::AO__opencl_atomic_load ||
- Op == AtomicExpr::AO__hip_atomic_load)
- ? 0
- : 1);
-
- if (ValType->isBitIntType()) {
- Diag(Ptr->getExprLoc(), diag::err_atomic_builtin_bit_int_prohibit);
- return ExprError();
- }
-
- return AE;
-}
-
-/// checkBuiltinArgument - Given a call to a builtin function, perform
-/// normal type-checking on the given argument, updating the call in
-/// place. This is useful when a builtin function requires custom
-/// type-checking for some of its arguments but not necessarily all of
-/// them.
-///
-/// Returns true on error.
-static bool checkBuiltinArgument(Sema &S, CallExpr *E, unsigned ArgIndex) {
- FunctionDecl *Fn = E->getDirectCallee();
- assert(Fn && "builtin call without direct callee!");
-
- ParmVarDecl *Param = Fn->getParamDecl(ArgIndex);
- InitializedEntity Entity =
- InitializedEntity::InitializeParameter(S.Context, Param);
-
- ExprResult Arg = E->getArg(ArgIndex);
- Arg = S.PerformCopyInitialization(Entity, SourceLocation(), Arg);
- if (Arg.isInvalid())
- return true;
-
- E->setArg(ArgIndex, Arg.get());
- return false;
-}
-
-ExprResult Sema::BuiltinAtomicOverloaded(ExprResult TheCallResult) {
- CallExpr *TheCall = static_cast<CallExpr *>(TheCallResult.get());
- Expr *Callee = TheCall->getCallee();
- DeclRefExpr *DRE = cast<DeclRefExpr>(Callee->IgnoreParenCasts());
- FunctionDecl *FDecl = cast<FunctionDecl>(DRE->getDecl());
-
- // Ensure that we have at least one argument to do type inference from.
- if (TheCall->getNumArgs() < 1) {
- Diag(TheCall->getEndLoc(), diag::err_typecheck_call_too_few_args_at_least)
- << 0 << 1 << TheCall->getNumArgs() << /*is non object*/ 0
- << Callee->getSourceRange();
- return ExprError();
- }
-
- // Inspect the first argument of the atomic builtin. This should always be
- // a pointer type, whose element is an integral scalar or pointer type.
- // Because it is a pointer type, we don't have to worry about any implicit
- // casts here.
- // FIXME: We don't allow floating point scalars as input.
- Expr *FirstArg = TheCall->getArg(0);
- ExprResult FirstArgResult = DefaultFunctionArrayLvalueConversion(FirstArg);
- if (FirstArgResult.isInvalid())
- return ExprError();
- FirstArg = FirstArgResult.get();
- TheCall->setArg(0, FirstArg);
-
- const PointerType *pointerType = FirstArg->getType()->getAs<PointerType>();
- if (!pointerType) {
- Diag(DRE->getBeginLoc(), diag::err_atomic_builtin_must_be_pointer)
- << FirstArg->getType() << 0 << FirstArg->getSourceRange();
- return ExprError();
- }
-
- QualType ValType = pointerType->getPointeeType();
- if (!ValType->isIntegerType() && !ValType->isAnyPointerType() &&
- !ValType->isBlockPointerType()) {
- Diag(DRE->getBeginLoc(), diag::err_atomic_builtin_must_be_pointer_intptr)
- << FirstArg->getType() << 0 << FirstArg->getSourceRange();
- return ExprError();
- }
- PointerAuthQualifier PointerAuth = ValType.getPointerAuth();
- if (PointerAuth && PointerAuth.isAddressDiscriminated()) {
- Diag(FirstArg->getBeginLoc(),
- diag::err_atomic_op_needs_non_address_discriminated_pointer)
- << 1 << ValType << FirstArg->getSourceRange();
- return ExprError();
- }
-
- if (ValType.isConstQualified()) {
- Diag(DRE->getBeginLoc(), diag::err_atomic_builtin_cannot_be_const)
- << FirstArg->getType() << FirstArg->getSourceRange();
- return ExprError();
- }
-
- switch (ValType.getObjCLifetime()) {
- case Qualifiers::OCL_None:
- case Qualifiers::OCL_ExplicitNone:
- // okay
- break;
-
- case Qualifiers::OCL_Weak:
- case Qualifiers::OCL_Strong:
- case Qualifiers::OCL_Autoreleasing:
- Diag(DRE->getBeginLoc(), diag::err_arc_atomic_ownership)
- << ValType << FirstArg->getSourceRange();
- return ExprError();
- }
-
- // Strip any qualifiers off ValType.
- ValType = ValType.getUnqualifiedType();
-
- // The majority of builtins return a value, but a few have special return
- // types, so allow them to override appropriately below.
- QualType ResultType = ValType;
-
- // We need to figure out which concrete builtin this maps onto. For example,
- // __sync_fetch_and_add with a 2 byte object turns into
- // __sync_fetch_and_add_2.
-#define BUILTIN_ROW(x) \
- { Builtin::BI##x##_1, Builtin::BI##x##_2, Builtin::BI##x##_4, \
- Builtin::BI##x##_8, Builtin::BI##x##_16 }
-
- static const unsigned BuiltinIndices[][5] = {
- BUILTIN_ROW(__sync_fetch_and_add),
- BUILTIN_ROW(__sync_fetch_and_sub),
- BUILTIN_ROW(__sync_fetch_and_or),
- BUILTIN_ROW(__sync_fetch_and_and),
- BUILTIN_ROW(__sync_fetch_and_xor),
- BUILTIN_ROW(__sync_fetch_and_nand),
-
- BUILTIN_ROW(__sync_add_and_fetch),
- BUILTIN_ROW(__sync_sub_and_fetch),
- BUILTIN_ROW(__sync_and_and_fetch),
- BUILTIN_ROW(__sync_or_and_fetch),
- BUILTIN_ROW(__sync_xor_and_fetch),
- BUILTIN_ROW(__sync_nand_and_fetch),
-
- BUILTIN_ROW(__sync_val_compare_and_swap),
- BUILTIN_ROW(__sync_bool_compare_and_swap),
- BUILTIN_ROW(__sync_lock_test_and_set),
- BUILTIN_ROW(__sync_lock_release),
- BUILTIN_ROW(__sync_swap)
- };
-#undef BUILTIN_ROW
-
- // Determine the index of the size.
- unsigned SizeIndex;
- switch (Context.getTypeSizeInChars(ValType).getQuantity()) {
- case 1: SizeIndex = 0; break;
- case 2: SizeIndex = 1; break;
- case 4: SizeIndex = 2; break;
- case 8: SizeIndex = 3; break;
- case 16: SizeIndex = 4; break;
- default:
- Diag(DRE->getBeginLoc(), diag::err_atomic_builtin_pointer_size)
- << FirstArg->getType() << FirstArg->getSourceRange();
- return ExprError();
- }
-
- // Each of these builtins has one pointer argument, followed by some number of
- // values (0, 1 or 2) followed by a potentially empty varags list of stuff
- // that we ignore. Find out which row of BuiltinIndices to read from as well
- // as the number of fixed args.
- unsigned BuiltinID = FDecl->getBuiltinID();
- unsigned BuiltinIndex, NumFixed = 1;
- bool WarnAboutSemanticsChange = false;
- switch (BuiltinID) {
- default: llvm_unreachable("Unknown overloaded atomic builtin!");
- case Builtin::BI__sync_fetch_and_add:
- case Builtin::BI__sync_fetch_and_add_1:
- case Builtin::BI__sync_fetch_and_add_2:
- case Builtin::BI__sync_fetch_and_add_4:
- case Builtin::BI__sync_fetch_and_add_8:
- case Builtin::BI__sync_fetch_and_add_16:
- BuiltinIndex = 0;
- break;
-
- case Builtin::BI__sync_fetch_and_sub:
- case Builtin::BI__sync_fetch_and_sub_1:
- case Builtin::BI__sync_fetch_and_sub_2:
- case Builtin::BI__sync_fetch_and_sub_4:
- case Builtin::BI__sync_fetch_and_sub_8:
- case Builtin::BI__sync_fetch_and_sub_16:
- BuiltinIndex = 1;
- break;
-
- case Builtin::BI__sync_fetch_and_or:
- case Builtin::BI__sync_fetch_and_or_1:
- case Builtin::BI__sync_fetch_and_or_2:
- case Builtin::BI__sync_fetch_and_or_4:
- case Builtin::BI__sync_fetch_and_or_8:
- case Builtin::BI__sync_fetch_and_or_16:
- BuiltinIndex = 2;
- break;
-
- case Builtin::BI__sync_fetch_and_and:
- case Builtin::BI__sync_fetch_and_and_1:
- case Builtin::BI__sync_fetch_and_and_2:
- case Builtin::BI__sync_fetch_and_and_4:
- case Builtin::BI__sync_fetch_and_and_8:
- case Builtin::BI__sync_fetch_and_and_16:
- BuiltinIndex = 3;
- break;
-
- case Builtin::BI__sync_fetch_and_xor:
- case Builtin::BI__sync_fetch_and_xor_1:
- case Builtin::BI__sync_fetch_and_xor_2:
- case Builtin::BI__sync_fetch_and_xor_4:
- case Builtin::BI__sync_fetch_and_xor_8:
- case Builtin::BI__sync_fetch_and_xor_16:
- BuiltinIndex = 4;
- break;
-
- case Builtin::BI__sync_fetch_and_nand:
- case Builtin::BI__sync_fetch_and_nand_1:
- case Builtin::BI__sync_fetch_and_nand_2:
- case Builtin::BI__sync_fetch_and_nand_4:
- case Builtin::BI__sync_fetch_and_nand_8:
- case Builtin::BI__sync_fetch_and_nand_16:
- BuiltinIndex = 5;
- WarnAboutSemanticsChange = true;
- break;
-
- case Builtin::BI__sync_add_and_fetch:
- case Builtin::BI__sync_add_and_fetch_1:
- case Builtin::BI__sync_add_and_fetch_2:
- case Builtin::BI__sync_add_and_fetch_4:
- case Builtin::BI__sync_add_and_fetch_8:
- case Builtin::BI__sync_add_and_fetch_16:
- BuiltinIndex = 6;
- break;
-
- case Builtin::BI__sync_sub_and_fetch:
- case Builtin::BI__sync_sub_and_fetch_1:
- case Builtin::BI__sync_sub_and_fetch_2:
- case Builtin::BI__sync_sub_and_fetch_4:
- case Builtin::BI__sync_sub_and_fetch_8:
- case Builtin::BI__sync_sub_and_fetch_16:
- BuiltinIndex = 7;
- break;
-
- case Builtin::BI__sync_and_and_fetch:
- case Builtin::BI__sync_and_and_fetch_1:
- case Builtin::BI__sync_and_and_fetch_2:
- case Builtin::BI__sync_and_and_fetch_4:
- case Builtin::BI__sync_and_and_fetch_8:
- case Builtin::BI__sync_and_and_fetch_16:
- BuiltinIndex = 8;
- break;
-
- case Builtin::BI__sync_or_and_fetch:
- case Builtin::BI__sync_or_and_fetch_1:
- case Builtin::BI__sync_or_and_fetch_2:
- case Builtin::BI__sync_or_and_fetch_4:
- case Builtin::BI__sync_or_and_fetch_8:
- case Builtin::BI__sync_or_and_fetch_16:
- BuiltinIndex = 9;
- break;
-
- case Builtin::BI__sync_xor_and_fetch:
- case Builtin::BI__sync_xor_and_fetch_1:
- case Builtin::BI__sync_xor_and_fetch_2:
- case Builtin::BI__sync_xor_and_fetch_4:
- case Builtin::BI__sync_xor_and_fetch_8:
- case Builtin::BI__sync_xor_and_fetch_16:
- BuiltinIndex = 10;
- break;
-
- case Builtin::BI__sync_nand_and_fetch:
- case Builtin::BI__sync_nand_and_fetch_1:
- case Builtin::BI__sync_nand_and_fetch_2:
- case Builtin::BI__sync_nand_and_fetch_4:
- case Builtin::BI__sync_nand_and_fetch_8:
- case Builtin::BI__sync_nand_and_fetch_16:
- BuiltinIndex = 11;
- WarnAboutSemanticsChange = true;
- break;
-
- case Builtin::BI__sync_val_compare_and_swap:
- case Builtin::BI__sync_val_compare_and_swap_1:
- case Builtin::BI__sync_val_compare_and_swap_2:
- case Builtin::BI__sync_val_compare_and_swap_4:
- case Builtin::BI__sync_val_compare_and_swap_8:
- case Builtin::BI__sync_val_compare_and_swap_16:
- BuiltinIndex = 12;
- NumFixed = 2;
- break;
-
- case Builtin::BI__sync_bool_compare_and_swap:
- case Builtin::BI__sync_bool_compare_and_swap_1:
- case Builtin::BI__sync_bool_compare_and_swap_2:
- case Builtin::BI__sync_bool_compare_and_swap_4:
- case Builtin::BI__sync_bool_compare_and_swap_8:
- case Builtin::BI__sync_bool_compare_and_swap_16:
- BuiltinIndex = 13;
- NumFixed = 2;
- ResultType = Context.BoolTy;
- break;
-
- case Builtin::BI__sync_lock_test_and_set:
- case Builtin::BI__sync_lock_test_and_set_1:
- case Builtin::BI__sync_lock_test_and_set_2:
- case Builtin::BI__sync_lock_test_and_set_4:
- case Builtin::BI__sync_lock_test_and_set_8:
- case Builtin::BI__sync_lock_test_and_set_16:
- BuiltinIndex = 14;
- break;
-
- case Builtin::BI__sync_lock_release:
- case Builtin::BI__sync_lock_release_1:
- case Builtin::BI__sync_lock_release_2:
- case Builtin::BI__sync_lock_release_4:
- case Builtin::BI__sync_lock_release_8:
- case Builtin::BI__sync_lock_release_16:
- BuiltinIndex = 15;
- NumFixed = 0;
- ResultType = Context.VoidTy;
- break;
-
- case Builtin::BI__sync_swap:
- case Builtin::BI__sync_swap_1:
- case Builtin::BI__sync_swap_2:
- case Builtin::BI__sync_swap_4:
- case Builtin::BI__sync_swap_8:
- case Builtin::BI__sync_swap_16:
- BuiltinIndex = 16;
- break;
- }
-
- // Now that we know how many fixed arguments we expect, first check that we
- // have at least that many.
- if (TheCall->getNumArgs() < 1+NumFixed) {
- Diag(TheCall->getEndLoc(), diag::err_typecheck_call_too_few_args_at_least)
- << 0 << 1 + NumFixed << TheCall->getNumArgs() << /*is non object*/ 0
- << Callee->getSourceRange();
- return ExprError();
- }
-
- Diag(TheCall->getEndLoc(), diag::warn_atomic_implicit_seq_cst)
- << Callee->getSourceRange();
-
- if (WarnAboutSemanticsChange) {
- Diag(TheCall->getEndLoc(), diag::warn_sync_fetch_and_nand_semantics_change)
- << Callee->getSourceRange();
- }
-
- // Get the decl for the concrete builtin from this, we can tell what the
- // concrete integer type we should convert to is.
- unsigned NewBuiltinID = BuiltinIndices[BuiltinIndex][SizeIndex];
- std::string NewBuiltinName = Context.BuiltinInfo.getName(NewBuiltinID);
- FunctionDecl *NewBuiltinDecl;
- if (NewBuiltinID == BuiltinID)
- NewBuiltinDecl = FDecl;
- else {
- // Perform builtin lookup to avoid redeclaring it.
- DeclarationName DN(&Context.Idents.get(NewBuiltinName));
- LookupResult Res(*this, DN, DRE->getBeginLoc(), LookupOrdinaryName);
- LookupName(Res, TUScope, /*AllowBuiltinCreation=*/true);
- assert(Res.getFoundDecl());
- NewBuiltinDecl = dyn_cast<FunctionDecl>(Res.getFoundDecl());
- if (!NewBuiltinDecl)
- return ExprError();
- }
-
- // The first argument --- the pointer --- has a fixed type; we
- // deduce the types of the rest of the arguments accordingly. Walk
- // the remaining arguments, converting them to the deduced value type.
- for (unsigned i = 0; i != NumFixed; ++i) {
- ExprResult Arg = TheCall->getArg(i+1);
-
- // GCC does an implicit conversion to the pointer or integer ValType. This
- // can fail in some cases (1i -> int**), check for this error case now.
- // Initialize the argument.
- InitializedEntity Entity = InitializedEntity::InitializeParameter(Context,
- ValType, /*consume*/ false);
- Arg = PerformCopyInitialization(Entity, SourceLocation(), Arg);
- if (Arg.isInvalid())
- return ExprError();
-
- // Okay, we have something that *can* be converted to the right type. Check
- // to see if there is a potentially weird extension going on here. This can
- // happen when you do an atomic operation on something like an char* and
- // pass in 42. The 42 gets converted to char. This is even more strange
- // for things like 45.123 -> char, etc.
- // FIXME: Do this check.
- TheCall->setArg(i+1, Arg.get());
- }
-
- // Create a new DeclRefExpr to refer to the new decl.
- DeclRefExpr *NewDRE = DeclRefExpr::Create(
- Context, DRE->getQualifierLoc(), SourceLocation(), NewBuiltinDecl,
- /*enclosing*/ false, DRE->getLocation(), Context.BuiltinFnTy,
- DRE->getValueKind(), nullptr, nullptr, DRE->isNonOdrUse());
-
- // Set the callee in the CallExpr.
- // FIXME: This loses syntactic information.
- QualType CalleePtrTy = Context.getPointerType(NewBuiltinDecl->getType());
- ExprResult PromotedCall = ImpCastExprToType(NewDRE, CalleePtrTy,
- CK_BuiltinFnToFnPtr);
- TheCall->setCallee(PromotedCall.get());
-
- // Change the result type of the call to match the original value type. This
- // is arbitrary, but the codegen for these builtins ins design to handle it
- // gracefully.
- TheCall->setType(ResultType);
-
- // Prohibit problematic uses of bit-precise integer types with atomic
- // builtins. The arguments would have already been converted to the first
- // argument's type, so only need to check the first argument.
- const auto *BitIntValType = ValType->getAs<BitIntType>();
- if (BitIntValType && !llvm::isPowerOf2_64(BitIntValType->getNumBits())) {
- Diag(FirstArg->getExprLoc(), diag::err_atomic_builtin_ext_int_size);
- return ExprError();
- }
-
- return TheCallResult;
-}
-
-ExprResult Sema::BuiltinNontemporalOverloaded(ExprResult TheCallResult) {
- CallExpr *TheCall = (CallExpr *)TheCallResult.get();
- DeclRefExpr *DRE =
- cast<DeclRefExpr>(TheCall->getCallee()->IgnoreParenCasts());
- FunctionDecl *FDecl = cast<FunctionDecl>(DRE->getDecl());
- unsigned BuiltinID = FDecl->getBuiltinID();
- assert((BuiltinID == Builtin::BI__builtin_nontemporal_store ||
- BuiltinID == Builtin::BI__builtin_nontemporal_load) &&
- "Unexpected nontemporal load/store builtin!");
- bool isStore = BuiltinID == Builtin::BI__builtin_nontemporal_store;
- unsigned numArgs = isStore ? 2 : 1;
-
- // Ensure that we have the proper number of arguments.
- if (checkArgCount(TheCall, numArgs))
- return ExprError();
-
- // Inspect the last argument of the nontemporal builtin. This should always
- // be a pointer type, from which we imply the type of the memory access.
- // Because it is a pointer type, we don't have to worry about any implicit
- // casts here.
- Expr *PointerArg = TheCall->getArg(numArgs - 1);
- ExprResult PointerArgResult =
- DefaultFunctionArrayLvalueConversion(PointerArg);
-
- if (PointerArgResult.isInvalid())
- return ExprError();
- PointerArg = PointerArgResult.get();
- TheCall->setArg(numArgs - 1, PointerArg);
-
- const PointerType *pointerType = PointerArg->getType()->getAs<PointerType>();
- if (!pointerType) {
- Diag(DRE->getBeginLoc(), diag::err_nontemporal_builtin_must_be_pointer)
- << PointerArg->getType() << PointerArg->getSourceRange();
- return ExprError();
- }
-
- QualType ValType = pointerType->getPointeeType();
-
- // Strip any qualifiers off ValType.
- ValType = ValType.getUnqualifiedType();
- if (!ValType->isIntegerType() && !ValType->isAnyPointerType() &&
- !ValType->isBlockPointerType() && !ValType->isFloatingType() &&
- !ValType->isVectorType()) {
- Diag(DRE->getBeginLoc(),
- diag::err_nontemporal_builtin_must_be_pointer_intfltptr_or_vector)
- << PointerArg->getType() << PointerArg->getSourceRange();
- return ExprError();
- }
-
- if (!isStore) {
- TheCall->setType(ValType);
- return TheCallResult;
- }
-
- ExprResult ValArg = TheCall->getArg(0);
- InitializedEntity Entity = InitializedEntity::InitializeParameter(
- Context, ValType, /*consume*/ false);
- ValArg = PerformCopyInitialization(Entity, SourceLocation(), ValArg);
- if (ValArg.isInvalid())
- return ExprError();
-
- TheCall->setArg(0, ValArg.get());
- TheCall->setType(Context.VoidTy);
- return TheCallResult;
-}
-
-/// CheckObjCString - Checks that the format string argument to the os_log()
-/// and os_trace() functions is correct, and converts it to const char *.
-ExprResult Sema::CheckOSLogFormatStringArg(Expr *Arg) {
- Arg = Arg->IgnoreParenCasts();
- auto *Literal = dyn_cast<StringLiteral>(Arg);
- if (!Literal) {
- if (auto *ObjcLiteral = dyn_cast<ObjCStringLiteral>(Arg)) {
- Literal = ObjcLiteral->getString();
- }
- }
-
- if (!Literal || (!Literal->isOrdinary() && !Literal->isUTF8())) {
- return ExprError(
- Diag(Arg->getBeginLoc(), diag::err_os_log_format_not_string_constant)
- << Arg->getSourceRange());
- }
-
- ExprResult Result(Literal);
- QualType ResultTy = Context.getPointerType(Context.CharTy.withConst());
- InitializedEntity Entity =
- InitializedEntity::InitializeParameter(Context, ResultTy, false);
- Result = PerformCopyInitialization(Entity, SourceLocation(), Result);
- return Result;
-}
-
-/// Check that the user is calling the appropriate va_start builtin for the
-/// target and calling convention.
-static bool checkVAStartABI(Sema &S, unsigned BuiltinID, Expr *Fn) {
- const llvm::Triple &TT = S.Context.getTargetInfo().getTriple();
- bool IsX64 = TT.getArch() == llvm::Triple::x86_64;
- bool IsAArch64 = (TT.getArch() == llvm::Triple::aarch64 ||
- TT.getArch() == llvm::Triple::aarch64_32);
- bool IsWindowsOrUEFI = TT.isOSWindows() || TT.isUEFI();
- bool IsMSVAStart = BuiltinID == Builtin::BI__builtin_ms_va_start;
- if (IsX64 || IsAArch64) {
- CallingConv CC = CC_C;
- if (const FunctionDecl *FD = S.getCurFunctionDecl())
- CC = FD->getType()->castAs<FunctionType>()->getCallConv();
- if (IsMSVAStart) {
- // Don't allow this in System V ABI functions.
- if (CC == CC_X86_64SysV || (!IsWindowsOrUEFI && CC != CC_Win64))
- return S.Diag(Fn->getBeginLoc(),
- diag::err_ms_va_start_used_in_sysv_function);
- } else {
- // On x86-64/AArch64 Unix, don't allow this in Win64 ABI functions.
- // On x64 Windows, don't allow this in System V ABI functions.
- // (Yes, that means there's no corresponding way to support variadic
- // System V ABI functions on Windows.)
- if ((IsWindowsOrUEFI && CC == CC_X86_64SysV) ||
- (!IsWindowsOrUEFI && CC == CC_Win64))
- return S.Diag(Fn->getBeginLoc(),
- diag::err_va_start_used_in_wrong_abi_function)
- << !IsWindowsOrUEFI;
- }
- return false;
- }
-
- if (IsMSVAStart)
- return S.Diag(Fn->getBeginLoc(), diag::err_builtin_x64_aarch64_only);
- return false;
-}
-
-static bool checkVAStartIsInVariadicFunction(Sema &S, Expr *Fn,
- ParmVarDecl **LastParam = nullptr) {
- // Determine whether the current function, block, or obj-c method is variadic
- // and get its parameter list.
- bool IsVariadic = false;
- ArrayRef<ParmVarDecl *> Params;
- DeclContext *Caller = S.CurContext;
- if (auto *Block = dyn_cast<BlockDecl>(Caller)) {
- IsVariadic = Block->isVariadic();
- Params = Block->parameters();
- } else if (auto *FD = dyn_cast<FunctionDecl>(Caller)) {
- IsVariadic = FD->isVariadic();
- Params = FD->parameters();
- } else if (auto *MD = dyn_cast<ObjCMethodDecl>(Caller)) {
- IsVariadic = MD->isVariadic();
- // FIXME: This isn't correct for methods (results in bogus warning).
- Params = MD->parameters();
- } else if (isa<CapturedDecl>(Caller)) {
- // We don't support va_start in a CapturedDecl.
- S.Diag(Fn->getBeginLoc(), diag::err_va_start_captured_stmt);
- return true;
- } else {
- // This must be some other declcontext that parses exprs.
- S.Diag(Fn->getBeginLoc(), diag::err_va_start_outside_function);
- return true;
- }
-
- if (!IsVariadic) {
- S.Diag(Fn->getBeginLoc(), diag::err_va_start_fixed_function);
- return true;
- }
-
- if (LastParam)
- *LastParam = Params.empty() ? nullptr : Params.back();
-
- return false;
-}
-
-bool Sema::BuiltinVAStart(unsigned BuiltinID, CallExpr *TheCall) {
- Expr *Fn = TheCall->getCallee();
- if (checkVAStartABI(*this, BuiltinID, Fn))
- return true;
-
- if (BuiltinID == Builtin::BI__builtin_c23_va_start) {
- // This builtin requires one argument (the va_list), allows two arguments,
- // but diagnoses more than two arguments. e.g.,
- // __builtin_c23_va_start(); // error
- // __builtin_c23_va_start(list); // ok
- // __builtin_c23_va_start(list, param); // ok
- // __builtin_c23_va_start(list, anything, anything); // error
- // This differs from the GCC behavior in that they accept the last case
- // with a warning, but it doesn't seem like a useful behavior to allow.
- if (checkArgCountRange(TheCall, 1, 2))
- return true;
- } else {
- // In C23 mode, va_start only needs one argument. However, the builtin still
- // requires two arguments (which matches the behavior of the GCC builtin),
- // <stdarg.h> passes `0` as the second argument in C23 mode.
- if (checkArgCount(TheCall, 2))
- return true;
- }
-
- // Type-check the first argument normally.
- if (checkBuiltinArgument(*this, TheCall, 0))
- return true;
-
- // Check that the current function is variadic, and get its last parameter.
- ParmVarDecl *LastParam;
- if (checkVAStartIsInVariadicFunction(*this, Fn, &LastParam))
- return true;
-
- // Verify that the second argument to the builtin is the last non-variadic
- // argument of the current function or method. In C23 mode, if the call is
- // not to __builtin_c23_va_start, and the second argument is an integer
- // constant expression with value 0, then we don't bother with this check.
- // For __builtin_c23_va_start, we only perform the check for the second
- // argument being the last argument to the current function if there is a
- // second argument present.
- if (BuiltinID == Builtin::BI__builtin_c23_va_start &&
- TheCall->getNumArgs() < 2) {
- Diag(TheCall->getExprLoc(), diag::warn_c17_compat_va_start_one_arg);
- return false;
- }
-
- const Expr *Arg = TheCall->getArg(1)->IgnoreParenCasts();
- if (std::optional<llvm::APSInt> Val =
- TheCall->getArg(1)->getIntegerConstantExpr(Context);
- Val && LangOpts.C23 && *Val == 0 &&
- BuiltinID != Builtin::BI__builtin_c23_va_start) {
- Diag(TheCall->getExprLoc(), diag::warn_c17_compat_va_start_one_arg);
- return false;
- }
-
- // These are valid if SecondArgIsLastNonVariadicArgument is false after the
- // next block.
- QualType Type;
- SourceLocation ParamLoc;
- bool IsCRegister = false;
- bool SecondArgIsLastNonVariadicArgument = false;
- if (const DeclRefExpr *DR = dyn_cast<DeclRefExpr>(Arg)) {
- if (const ParmVarDecl *PV = dyn_cast<ParmVarDecl>(DR->getDecl())) {
- SecondArgIsLastNonVariadicArgument = PV == LastParam;
-
- Type = PV->getType();
- ParamLoc = PV->getLocation();
- IsCRegister =
- PV->getStorageClass() == SC_Register && !getLangOpts().CPlusPlus;
- }
- }
-
- if (!SecondArgIsLastNonVariadicArgument)
- Diag(TheCall->getArg(1)->getBeginLoc(),
- diag::warn_second_arg_of_va_start_not_last_non_variadic_param);
- else if (IsCRegister || Type->isReferenceType() ||
- Type->isSpecificBuiltinType(BuiltinType::Float) || [=] {
- // Promotable integers are UB, but enumerations need a bit of
- // extra checking to see what their promotable type actually is.
- if (!Context.isPromotableIntegerType(Type))
- return false;
- const auto *ED = Type->getAsEnumDecl();
- if (!ED)
- return true;
- return !Context.typesAreCompatible(ED->getPromotionType(), Type);
- }()) {
- unsigned Reason = 0;
- if (Type->isReferenceType()) Reason = 1;
- else if (IsCRegister) Reason = 2;
- Diag(Arg->getBeginLoc(), diag::warn_va_start_type_is_undefined) << Reason;
- Diag(ParamLoc, diag::note_parameter_type) << Type;
- }
-
- return false;
-}
-
-bool Sema::BuiltinVAStartARMMicrosoft(CallExpr *Call) {
- auto IsSuitablyTypedFormatArgument = [this](const Expr *Arg) -> bool {
- const LangOptions &LO = getLangOpts();
-
- if (LO.CPlusPlus)
- return Arg->getType()
- .getCanonicalType()
- .getTypePtr()
- ->getPointeeType()
- .withoutLocalFastQualifiers() == Context.CharTy;
-
- // In C, allow aliasing through `char *`, this is required for AArch64 at
- // least.
- return true;
- };
-
- // void __va_start(va_list *ap, const char *named_addr, size_t slot_size,
- // const char *named_addr);
-
- Expr *Func = Call->getCallee();
-
- if (Call->getNumArgs() < 3)
- return Diag(Call->getEndLoc(),
- diag::err_typecheck_call_too_few_args_at_least)
- << 0 /*function call*/ << 3 << Call->getNumArgs()
- << /*is non object*/ 0;
-
- // Type-check the first argument normally.
- if (checkBuiltinArgument(*this, Call, 0))
- return true;
-
- // Check that the current function is variadic.
- if (checkVAStartIsInVariadicFunction(*this, Func))
- return true;
-
- // __va_start on Windows does not validate the parameter qualifiers
-
- const Expr *Arg1 = Call->getArg(1)->IgnoreParens();
- const Type *Arg1Ty = Arg1->getType().getCanonicalType().getTypePtr();
-
- const Expr *Arg2 = Call->getArg(2)->IgnoreParens();
- const Type *Arg2Ty = Arg2->getType().getCanonicalType().getTypePtr();
-
- const QualType &ConstCharPtrTy =
- Context.getPointerType(Context.CharTy.withConst());
- if (!Arg1Ty->isPointerType() || !IsSuitablyTypedFormatArgument(Arg1))
- Diag(Arg1->getBeginLoc(), diag::err_typecheck_convert_incompatible)
- << Arg1->getType() << ConstCharPtrTy << 1 /* different class */
- << 0 /* qualifier difference */
- << 3 /* parameter mismatch */
- << 2 << Arg1->getType() << ConstCharPtrTy;
-
- const QualType SizeTy = Context.getSizeType();
- if (!Context.hasSameType(
- Arg2Ty->getCanonicalTypeInternal().withoutLocalFastQualifiers(),
- SizeTy))
- Diag(Arg2->getBeginLoc(), diag::err_typecheck_convert_incompatible)
- << Arg2->getType() << SizeTy << 1 /* different class */
- << 0 /* qualifier difference */
- << 3 /* parameter mismatch */
- << 3 << Arg2->getType() << SizeTy;
-
- return false;
-}
-
-bool Sema::BuiltinUnorderedCompare(CallExpr *TheCall, unsigned BuiltinID) {
- if (checkArgCount(TheCall, 2))
- return true;
-
- if (BuiltinID == Builtin::BI__builtin_isunordered &&
- TheCall->getFPFeaturesInEffect(getLangOpts()).getNoHonorNaNs())
- Diag(TheCall->getBeginLoc(), diag::warn_fp_nan_inf_when_disabled)
- << 1 << 0 << TheCall->getSourceRange();
-
- ExprResult OrigArg0 = TheCall->getArg(0);
- ExprResult OrigArg1 = TheCall->getArg(1);
-
- // Do standard promotions between the two arguments, returning their common
- // type.
- QualType Res = UsualArithmeticConversions(
- OrigArg0, OrigArg1, TheCall->getExprLoc(), ArithConvKind::Comparison);
- if (OrigArg0.isInvalid() || OrigArg1.isInvalid())
- return true;
-
- // Make sure any conversions are pushed back into the call; this is
- // type safe since unordered compare builtins are declared as "_Bool
- // foo(...)".
- TheCall->setArg(0, OrigArg0.get());
- TheCall->setArg(1, OrigArg1.get());
-
- if (OrigArg0.get()->isTypeDependent() || OrigArg1.get()->isTypeDependent())
- return false;
-
- // If the common type isn't a real floating type, then the arguments were
- // invalid for this operation.
- if (Res.isNull() || !Res->isRealFloatingType())
- return Diag(OrigArg0.get()->getBeginLoc(),
- diag::err_typecheck_call_invalid_ordered_compare)
- << OrigArg0.get()->getType() << OrigArg1.get()->getType()
- << SourceRange(OrigArg0.get()->getBeginLoc(),
- OrigArg1.get()->getEndLoc());
-
- return false;
-}
-
-bool Sema::BuiltinFPClassification(CallExpr *TheCall, unsigned NumArgs,
- unsigned BuiltinID) {
- if (checkArgCount(TheCall, NumArgs))
- return true;
-
- FPOptions FPO = TheCall->getFPFeaturesInEffect(getLangOpts());
- if (FPO.getNoHonorInfs() && (BuiltinID == Builtin::BI__builtin_isfinite ||
- BuiltinID == Builtin::BI__builtin_isinf ||
- BuiltinID == Builtin::BI__builtin_isinf_sign))
- Diag(TheCall->getBeginLoc(), diag::warn_fp_nan_inf_when_disabled)
- << 0 << 0 << TheCall->getSourceRange();
-
- if (FPO.getNoHonorNaNs() && (BuiltinID == Builtin::BI__builtin_isnan ||
- BuiltinID == Builtin::BI__builtin_isunordered))
- Diag(TheCall->getBeginLoc(), diag::warn_fp_nan_inf_when_disabled)
- << 1 << 0 << TheCall->getSourceRange();
-
- bool IsFPClass = NumArgs == 2;
-
- // Find out position of floating-point argument.
- unsigned FPArgNo = IsFPClass ? 0 : NumArgs - 1;
-
- // We can count on all parameters preceding the floating-point just being int.
- // Try all of those.
- for (unsigned i = 0; i < FPArgNo; ++i) {
- Expr *Arg = TheCall->getArg(i);
-
- if (Arg->isTypeDependent())
- return false;
-
- ExprResult Res = PerformImplicitConversion(Arg, Context.IntTy,
- AssignmentAction::Passing);
-
- if (Res.isInvalid())
- return true;
- TheCall->setArg(i, Res.get());
- }
-
- Expr *OrigArg = TheCall->getArg(FPArgNo);
-
- if (OrigArg->isTypeDependent())
- return false;
-
- // Usual Unary Conversions will convert half to float, which we want for
- // machines that use fp16 conversion intrinsics. Else, we wnat to leave the
- // type how it is, but do normal L->Rvalue conversions.
- if (Context.getTargetInfo().useFP16ConversionIntrinsics()) {
- ExprResult Res = UsualUnaryConversions(OrigArg);
-
- if (!Res.isUsable())
- return true;
- OrigArg = Res.get();
- } else {
- ExprResult Res = DefaultFunctionArrayLvalueConversion(OrigArg);
-
- if (!Res.isUsable())
- return true;
- OrigArg = Res.get();
- }
- TheCall->setArg(FPArgNo, OrigArg);
-
- QualType VectorResultTy;
- QualType ElementTy = OrigArg->getType();
- // TODO: When all classification function are implemented with is_fpclass,
- // vector argument can be supported in all of them.
- if (ElementTy->isVectorType() && IsFPClass) {
- VectorResultTy = GetSignedVectorType(ElementTy);
- ElementTy = ElementTy->castAs<VectorType>()->getElementType();
- }
-
- // This operation requires a non-_Complex floating-point number.
- if (!ElementTy->isRealFloatingType())
- return Diag(OrigArg->getBeginLoc(),
- diag::err_typecheck_call_invalid_unary_fp)
- << OrigArg->getType() << OrigArg->getSourceRange();
-
- // __builtin_isfpclass has integer parameter that specify test mask. It is
- // passed in (...), so it should be analyzed completely here.
- if (IsFPClass)
- if (BuiltinConstantArgRange(TheCall, 1, 0, llvm::fcAllFlags))
- return true;
-
- // TODO: enable this code to all classification functions.
- if (IsFPClass) {
- QualType ResultTy;
- if (!VectorResultTy.isNull())
- ResultTy = VectorResultTy;
- else
- ResultTy = Context.IntTy;
- TheCall->setType(ResultTy);
- }
-
- return false;
-}
-
-bool Sema::BuiltinComplex(CallExpr *TheCall) {
- if (checkArgCount(TheCall, 2))
- return true;
-
- bool Dependent = false;
- for (unsigned I = 0; I != 2; ++I) {
- Expr *Arg = TheCall->getArg(I);
- QualType T = Arg->getType();
- if (T->isDependentType()) {
- Dependent = true;
- continue;
- }
-
- // Despite supporting _Complex int, GCC requires a real floating point type
- // for the operands of __builtin_complex.
- if (!T->isRealFloatingType()) {
- return Diag(Arg->getBeginLoc(), diag::err_typecheck_call_requires_real_fp)
- << Arg->getType() << Arg->getSourceRange();
- }
-
- ExprResult Converted = DefaultLvalueConversion(Arg);
- if (Converted.isInvalid())
- return true;
- TheCall->setArg(I, Converted.get());
- }
-
- if (Dependent) {
- TheCall->setType(Context.DependentTy);
- return false;
- }
-
- Expr *Real = TheCall->getArg(0);
- Expr *Imag = TheCall->getArg(1);
- if (!Context.hasSameType(Real->getType(), Imag->getType())) {
- return Diag(Real->getBeginLoc(),
- diag::err_typecheck_call_different_arg_types)
- << Real->getType() << Imag->getType()
- << Real->getSourceRange() << Imag->getSourceRange();
- }
-
- TheCall->setType(Context.getComplexType(Real->getType()));
- return false;
-}
-
-/// BuiltinShuffleVector - Handle __builtin_shufflevector.
-// This is declared to take (...), so we have to check everything.
-ExprResult Sema::BuiltinShuffleVector(CallExpr *TheCall) {
- unsigned NumArgs = TheCall->getNumArgs();
- if (NumArgs < 2)
- return ExprError(Diag(TheCall->getEndLoc(),
- diag::err_typecheck_call_too_few_args_at_least)
- << 0 /*function call*/ << 2 << NumArgs
- << /*is non object*/ 0 << TheCall->getSourceRange());
-
- // Determine which of the following types of shufflevector we're checking:
- // 1) unary, vector mask: (lhs, mask)
- // 2) binary, scalar mask: (lhs, rhs, index, ..., index)
- QualType ResType = TheCall->getArg(0)->getType();
- unsigned NumElements = 0;
-
- if (!TheCall->getArg(0)->isTypeDependent() &&
- !TheCall->getArg(1)->isTypeDependent()) {
- QualType LHSType = TheCall->getArg(0)->getType();
- QualType RHSType = TheCall->getArg(1)->getType();
-
- if (!LHSType->isVectorType() || !RHSType->isVectorType())
- return ExprError(
- Diag(TheCall->getBeginLoc(), diag::err_vec_builtin_non_vector)
- << TheCall->getDirectCallee() << /*isMoreThanTwoArgs*/ false
- << SourceRange(TheCall->getArg(0)->getBeginLoc(),
- TheCall->getArg(1)->getEndLoc()));
-
- NumElements = LHSType->castAs<VectorType>()->getNumElements();
- unsigned NumResElements = NumArgs - 2;
-
- // Check to see if we have a call with 2 vector arguments, the unary shuffle
- // with mask. If so, verify that RHS is an integer vector type with the
- // same number of elts as lhs.
- if (NumArgs == 2) {
- if (!RHSType->hasIntegerRepresentation() ||
- RHSType->castAs<VectorType>()->getNumElements() != NumElements)
- return ExprError(Diag(TheCall->getBeginLoc(),
- diag::err_vec_builtin_incompatible_vector)
- << TheCall->getDirectCallee()
- << /*isMoreThanTwoArgs*/ false
- << SourceRange(TheCall->getArg(1)->getBeginLoc(),
- TheCall->getArg(1)->getEndLoc()));
- } else if (!Context.hasSameUnqualifiedType(LHSType, RHSType)) {
- return ExprError(Diag(TheCall->getBeginLoc(),
- diag::err_vec_builtin_incompatible_vector)
- << TheCall->getDirectCallee()
- << /*isMoreThanTwoArgs*/ false
- << SourceRange(TheCall->getArg(0)->getBeginLoc(),
- TheCall->getArg(1)->getEndLoc()));
- } else if (NumElements != NumResElements) {
- QualType EltType = LHSType->castAs<VectorType>()->getElementType();
- ResType = ResType->isExtVectorType()
- ? Context.getExtVectorType(EltType, NumResElements)
- : Context.getVectorType(EltType, NumResElements,
- VectorKind::Generic);
- }
- }
-
- for (unsigned I = 2; I != NumArgs; ++I) {
- Expr *Arg = TheCall->getArg(I);
- if (Arg->isTypeDependent() || Arg->isValueDependent())
- continue;
-
- std::optional<llvm::APSInt> Result = Arg->getIntegerConstantExpr(Context);
- if (!Result)
- return ExprError(Diag(TheCall->getBeginLoc(),
- diag::err_shufflevector_nonconstant_argument)
- << Arg->getSourceRange());
-
- // Allow -1 which will be translated to undef in the IR.
- if (Result->isSigned() && Result->isAllOnes())
- ;
- else if (Result->getActiveBits() > 64 ||
- Result->getZExtValue() >= NumElements * 2)
- return ExprError(Diag(TheCall->getBeginLoc(),
- diag::err_shufflevector_argument_too_large)
- << Arg->getSourceRange());
-
- TheCall->setArg(I, ConstantExpr::Create(Context, Arg, APValue(*Result)));
- }
-
- auto *Result = new (Context) ShuffleVectorExpr(
- Context, ArrayRef(TheCall->getArgs(), NumArgs), ResType,
- TheCall->getCallee()->getBeginLoc(), TheCall->getRParenLoc());
-
- // All moved to Result.
- TheCall->shrinkNumArgs(0);
- return Result;
-}
-
-ExprResult Sema::ConvertVectorExpr(Expr *E, TypeSourceInfo *TInfo,
- SourceLocation BuiltinLoc,
- SourceLocation RParenLoc) {
- ExprValueKind VK = VK_PRValue;
- ExprObjectKind OK = OK_Ordinary;
- QualType DstTy = TInfo->getType();
- QualType SrcTy = E->getType();
-
- if (!SrcTy->isVectorType() && !SrcTy->isDependentType())
- return ExprError(Diag(BuiltinLoc,
- diag::err_convertvector_non_vector)
- << E->getSourceRange());
- if (!DstTy->isVectorType() && !DstTy->isDependentType())
- return ExprError(Diag(BuiltinLoc, diag::err_builtin_non_vector_type)
- << "second"
- << "__builtin_convertvector");
-
- if (!SrcTy->isDependentType() && !DstTy->isDependentType()) {
- unsigned SrcElts = SrcTy->castAs<VectorType>()->getNumElements();
- unsigned DstElts = DstTy->castAs<VectorType>()->getNumElements();
- if (SrcElts != DstElts)
- return ExprError(Diag(BuiltinLoc,
- diag::err_convertvector_incompatible_vector)
- << E->getSourceRange());
- }
-
- return ConvertVectorExpr::Create(Context, E, TInfo, DstTy, VK, OK, BuiltinLoc,
- RParenLoc, CurFPFeatureOverrides());
-}
-
-bool Sema::BuiltinPrefetch(CallExpr *TheCall) {
- unsigned NumArgs = TheCall->getNumArgs();
-
- if (NumArgs > 3)
- return Diag(TheCall->getEndLoc(),
- diag::err_typecheck_call_too_many_args_at_most)
- << 0 /*function call*/ << 3 << NumArgs << /*is non object*/ 0
- << TheCall->getSourceRange();
-
- // Argument 0 is checked for us and the remaining arguments must be
- // constant integers.
- for (unsigned i = 1; i != NumArgs; ++i)
- if (BuiltinConstantArgRange(TheCall, i, 0, i == 1 ? 1 : 3))
- return true;
-
- return false;
-}
-
-bool Sema::BuiltinArithmeticFence(CallExpr *TheCall) {
- if (!Context.getTargetInfo().checkArithmeticFenceSupported())
- return Diag(TheCall->getBeginLoc(), diag::err_builtin_target_unsupported)
- << SourceRange(TheCall->getBeginLoc(), TheCall->getEndLoc());
- if (checkArgCount(TheCall, 1))
- return true;
- Expr *Arg = TheCall->getArg(0);
- if (Arg->isInstantiationDependent())
- return false;
-
- QualType ArgTy = Arg->getType();
- if (!ArgTy->hasFloatingRepresentation())
- return Diag(TheCall->getEndLoc(), diag::err_typecheck_expect_flt_or_vector)
- << ArgTy;
- if (Arg->isLValue()) {
- ExprResult FirstArg = DefaultLvalueConversion(Arg);
- TheCall->setArg(0, FirstArg.get());
- }
- TheCall->setType(TheCall->getArg(0)->getType());
- return false;
-}
-
-bool Sema::BuiltinAssume(CallExpr *TheCall) {
- Expr *Arg = TheCall->getArg(0);
- if (Arg->isInstantiationDependent()) return false;
-
- if (Arg->HasSideEffects(Context))
- Diag(Arg->getBeginLoc(), diag::warn_assume_side_effects)
- << Arg->getSourceRange()
- << cast<FunctionDecl>(TheCall->getCalleeDecl())->getIdentifier();
-
- return false;
-}
-
-bool Sema::BuiltinAllocaWithAlign(CallExpr *TheCall) {
- // The alignment must be a constant integer.
- Expr *Arg = TheCall->getArg(1);
-
- // We can't check the value of a dependent argument.
- if (!Arg->isTypeDependent() && !Arg->isValueDependent()) {
- if (const auto *UE =
- dyn_cast<UnaryExprOrTypeTraitExpr>(Arg->IgnoreParenImpCasts()))
- if (UE->getKind() == UETT_AlignOf ||
- UE->getKind() == UETT_PreferredAlignOf)
- Diag(TheCall->getBeginLoc(), diag::warn_alloca_align_alignof)
- << Arg->getSourceRange();
-
- llvm::APSInt Result = Arg->EvaluateKnownConstInt(Context);
-
- if (!Result.isPowerOf2())
- return Diag(TheCall->getBeginLoc(), diag::err_alignment_not_power_of_two)
- << Arg->getSourceRange();
-
- if (Result < Context.getCharWidth())
- return Diag(TheCall->getBeginLoc(), diag::err_alignment_too_small)
- << (unsigned)Context.getCharWidth() << Arg->getSourceRange();
-
- if (Result > std::numeric_limits<int32_t>::max())
- return Diag(TheCall->getBeginLoc(), diag::err_alignment_too_big)
- << std::numeric_limits<int32_t>::max() << Arg->getSourceRange();
- }
-
- return false;
-}
-
-bool Sema::BuiltinAssumeAligned(CallExpr *TheCall) {
- if (checkArgCountRange(TheCall, 2, 3))
- return true;
-
- unsigned NumArgs = TheCall->getNumArgs();
- Expr *FirstArg = TheCall->getArg(0);
-
- {
- ExprResult FirstArgResult =
- DefaultFunctionArrayLvalueConversion(FirstArg);
- if (!FirstArgResult.get()->getType()->isPointerType()) {
- Diag(TheCall->getBeginLoc(), diag::err_builtin_assume_aligned_invalid_arg)
- << TheCall->getSourceRange();
- return true;
- }
- TheCall->setArg(0, FirstArgResult.get());
- }
-
- // The alignment must be a constant integer.
- Expr *SecondArg = TheCall->getArg(1);
-
- // We can't check the value of a dependent argument.
- if (!SecondArg->isValueDependent()) {
- llvm::APSInt Result;
- if (BuiltinConstantArg(TheCall, 1, Result))
- return true;
-
- if (!Result.isPowerOf2())
- return Diag(TheCall->getBeginLoc(), diag::err_alignment_not_power_of_two)
- << SecondArg->getSourceRange();
-
- if (Result > Sema::MaximumAlignment)
- Diag(TheCall->getBeginLoc(), diag::warn_assume_aligned_too_great)
- << SecondArg->getSourceRange() << Sema::MaximumAlignment;
-
- TheCall->setArg(1,
- ConstantExpr::Create(Context, SecondArg, APValue(Result)));
- }
-
- if (NumArgs > 2) {
- Expr *ThirdArg = TheCall->getArg(2);
- if (convertArgumentToType(*this, ThirdArg, Context.getSizeType()))
- return true;
- TheCall->setArg(2, ThirdArg);
- }
-
- return false;
-}
-
-bool Sema::BuiltinOSLogFormat(CallExpr *TheCall) {
- unsigned BuiltinID =
- cast<FunctionDecl>(TheCall->getCalleeDecl())->getBuiltinID();
- bool IsSizeCall = BuiltinID == Builtin::BI__builtin_os_log_format_buffer_size;
-
- unsigned NumArgs = TheCall->getNumArgs();
- unsigned NumRequiredArgs = IsSizeCall ? 1 : 2;
- if (NumArgs < NumRequiredArgs) {
- return Diag(TheCall->getEndLoc(), diag::err_typecheck_call_too_few_args)
- << 0 /* function call */ << NumRequiredArgs << NumArgs
- << /*is non object*/ 0 << TheCall->getSourceRange();
- }
- if (NumArgs >= NumRequiredArgs + 0x100) {
- return Diag(TheCall->getEndLoc(),
- diag::err_typecheck_call_too_many_args_at_most)
- << 0 /* function call */ << (NumRequiredArgs + 0xff) << NumArgs
- << /*is non object*/ 0 << TheCall->getSourceRange();
- }
- unsigned i = 0;
-
- // For formatting call, check buffer arg.
- if (!IsSizeCall) {
- ExprResult Arg(TheCall->getArg(i));
- InitializedEntity Entity = InitializedEntity::InitializeParameter(
- Context, Context.VoidPtrTy, false);
- Arg = PerformCopyInitialization(Entity, SourceLocation(), Arg);
- if (Arg.isInvalid())
- return true;
- TheCall->setArg(i, Arg.get());
- i++;
- }
-
- // Check string literal arg.
- unsigned FormatIdx = i;
- {
- ExprResult Arg = CheckOSLogFormatStringArg(TheCall->getArg(i));
- if (Arg.isInvalid())
- return true;
- TheCall->setArg(i, Arg.get());
- i++;
- }
-
- // Make sure variadic args are scalar.
- unsigned FirstDataArg = i;
- while (i < NumArgs) {
- ExprResult Arg = DefaultVariadicArgumentPromotion(
- TheCall->getArg(i), VariadicCallType::Function, nullptr);
- if (Arg.isInvalid())
- return true;
- CharUnits ArgSize = Context.getTypeSizeInChars(Arg.get()->getType());
- if (ArgSize.getQuantity() >= 0x100) {
- return Diag(Arg.get()->getEndLoc(), diag::err_os_log_argument_too_big)
- << i << (int)ArgSize.getQuantity() << 0xff
- << TheCall->getSourceRange();
- }
- TheCall->setArg(i, Arg.get());
- i++;
- }
-
- // Check formatting specifiers. NOTE: We're only doing this for the non-size
- // call to avoid duplicate diagnostics.
- if (!IsSizeCall) {
- llvm::SmallBitVector CheckedVarArgs(NumArgs, false);
- ArrayRef<const Expr *> Args(TheCall->getArgs(), TheCall->getNumArgs());
- bool Success = CheckFormatArguments(
- Args, FAPK_Variadic, nullptr, FormatIdx, FirstDataArg,
- FormatStringType::OSLog, VariadicCallType::Function,
- TheCall->getBeginLoc(), SourceRange(), CheckedVarArgs);
- if (!Success)
- return true;
- }
-
- if (IsSizeCall) {
- TheCall->setType(Context.getSizeType());
- } else {
- TheCall->setType(Context.VoidPtrTy);
- }
- return false;
-}
-
-bool Sema::BuiltinConstantArg(CallExpr *TheCall, unsigned ArgNum,
- llvm::APSInt &Result) {
- Expr *Arg = TheCall->getArg(ArgNum);
-
- if (Arg->isTypeDependent() || Arg->isValueDependent())
- return false;
-
- std::optional<llvm::APSInt> R = Arg->getIntegerConstantExpr(Context);
- if (!R) {
- auto *DRE = cast<DeclRefExpr>(TheCall->getCallee()->IgnoreParenCasts());
- auto *FDecl = cast<FunctionDecl>(DRE->getDecl());
- return Diag(TheCall->getBeginLoc(), diag::err_constant_integer_arg_type)
- << FDecl->getDeclName() << Arg->getSourceRange();
- }
- Result = *R;
-
- return false;
-}
-
-bool Sema::BuiltinConstantArgRange(CallExpr *TheCall, unsigned ArgNum, int Low,
- int High, bool RangeIsError) {
- if (isConstantEvaluatedContext())
- return false;
- llvm::APSInt Result;
-
- // We can't check the value of a dependent argument.
- Expr *Arg = TheCall->getArg(ArgNum);
- if (Arg->isTypeDependent() || Arg->isValueDependent())
- return false;
-
- // Check constant-ness first.
- if (BuiltinConstantArg(TheCall, ArgNum, Result))
- return true;
-
- if (Result.getSExtValue() < Low || Result.getSExtValue() > High) {
- if (RangeIsError)
- return Diag(TheCall->getBeginLoc(), diag::err_argument_invalid_range)
- << toString(Result, 10) << Low << High << Arg->getSourceRange();
- else
- // Defer the warning until we know if the code will be emitted so that
- // dead code can ignore this.
- DiagRuntimeBehavior(TheCall->getBeginLoc(), TheCall,
- PDiag(diag::warn_argument_invalid_range)
- << toString(Result, 10) << Low << High
- << Arg->getSourceRange());
- }
-
- return false;
-}
-
-bool Sema::BuiltinConstantArgMultiple(CallExpr *TheCall, unsigned ArgNum,
- unsigned Num) {
- llvm::APSInt Result;
-
- // We can't check the value of a dependent argument.
- Expr *Arg = TheCall->getArg(ArgNum);
- if (Arg->isTypeDependent() || Arg->isValueDependent())
- return false;
-
- // Check constant-ness first.
- if (BuiltinConstantArg(TheCall, ArgNum, Result))
- return true;
-
- if (Result.getSExtValue() % Num != 0)
- return Diag(TheCall->getBeginLoc(), diag::err_argument_not_multiple)
- << Num << Arg->getSourceRange();
-
- return false;
-}
-
-bool Sema::BuiltinConstantArgPower2(CallExpr *TheCall, unsigned ArgNum) {
- llvm::APSInt Result;
-
- // We can't check the value of a dependent argument.
- Expr *Arg = TheCall->getArg(ArgNum);
- if (Arg->isTypeDependent() || Arg->isValueDependent())
- return false;
-
- // Check constant-ness first.
- if (BuiltinConstantArg(TheCall, ArgNum, Result))
- return true;
-
- if (Result.isPowerOf2())
- return false;
-
- return Diag(TheCall->getBeginLoc(), diag::err_argument_not_power_of_2)
- << Arg->getSourceRange();
-}
-
-static bool IsShiftedByte(llvm::APSInt Value) {
- if (Value.isNegative())
- return false;
-
- // Check if it's a shifted byte, by shifting it down
- while (true) {
- // If the value fits in the bottom byte, the check passes.
- if (Value < 0x100)
- return true;
-
- // Otherwise, if the value has _any_ bits in the bottom byte, the check
- // fails.
- if ((Value & 0xFF) != 0)
- return false;
-
- // If the bottom 8 bits are all 0, but something above that is nonzero,
- // then shifting the value right by 8 bits won't affect whether it's a
- // shifted byte or not. So do that, and go round again.
- Value >>= 8;
- }
-}
-
-bool Sema::BuiltinConstantArgShiftedByte(CallExpr *TheCall, unsigned ArgNum,
- unsigned ArgBits) {
- llvm::APSInt Result;
-
- // We can't check the value of a dependent argument.
- Expr *Arg = TheCall->getArg(ArgNum);
- if (Arg->isTypeDependent() || Arg->isValueDependent())
- return false;
-
- // Check constant-ness first.
- if (BuiltinConstantArg(TheCall, ArgNum, Result))
- return true;
-
- // Truncate to the given size.
- Result = Result.getLoBits(ArgBits);
- Result.setIsUnsigned(true);
-
- if (IsShiftedByte(Result))
- return false;
-
- return Diag(TheCall->getBeginLoc(), diag::err_argument_not_shifted_byte)
- << Arg->getSourceRange();
-}
-
-bool Sema::BuiltinConstantArgShiftedByteOrXXFF(CallExpr *TheCall,
- unsigned ArgNum,
- unsigned ArgBits) {
- llvm::APSInt Result;
-
- // We can't check the value of a dependent argument.
- Expr *Arg = TheCall->getArg(ArgNum);
- if (Arg->isTypeDependent() || Arg->isValueDependent())
- return false;
-
- // Check constant-ness first.
- if (BuiltinConstantArg(TheCall, ArgNum, Result))
- return true;
-
- // Truncate to the given size.
- Result = Result.getLoBits(ArgBits);
- Result.setIsUnsigned(true);
-
- // Check to see if it's in either of the required forms.
- if (IsShiftedByte(Result) ||
- (Result > 0 && Result < 0x10000 && (Result & 0xFF) == 0xFF))
- return false;
-
- return Diag(TheCall->getBeginLoc(),
- diag::err_argument_not_shifted_byte_or_xxff)
- << Arg->getSourceRange();
-}
-
-bool Sema::BuiltinLongjmp(CallExpr *TheCall) {
- if (!Context.getTargetInfo().hasSjLjLowering())
- return Diag(TheCall->getBeginLoc(), diag::err_builtin_longjmp_unsupported)
- << SourceRange(TheCall->getBeginLoc(), TheCall->getEndLoc());
-
- Expr *Arg = TheCall->getArg(1);
- llvm::APSInt Result;
-
- // TODO: This is less than ideal. Overload this to take a value.
- if (BuiltinConstantArg(TheCall, 1, Result))
- return true;
-
- if (Result != 1)
- return Diag(TheCall->getBeginLoc(), diag::err_builtin_longjmp_invalid_val)
- << SourceRange(Arg->getBeginLoc(), Arg->getEndLoc());
-
- return false;
-}
-
-bool Sema::BuiltinSetjmp(CallExpr *TheCall) {
- if (!Context.getTargetInfo().hasSjLjLowering())
- return Diag(TheCall->getBeginLoc(), diag::err_builtin_setjmp_unsupported)
- << SourceRange(TheCall->getBeginLoc(), TheCall->getEndLoc());
- return false;
-}
-
-bool Sema::BuiltinCountedByRef(CallExpr *TheCall) {
- if (checkArgCount(TheCall, 1))
- return true;
-
- ExprResult ArgRes = UsualUnaryConversions(TheCall->getArg(0));
- if (ArgRes.isInvalid())
- return true;
-
- // For simplicity, we support only limited expressions for the argument.
- // Specifically a flexible array member or a pointer with counted_by:
- // 'ptr->array' or 'ptr->pointer'. This allows us to reject arguments with
- // complex casting, which really shouldn't be a huge problem.
- const Expr *Arg = ArgRes.get()->IgnoreParenImpCasts();
- if (!Arg->getType()->isPointerType() && !Arg->getType()->isArrayType())
- return Diag(Arg->getBeginLoc(),
- diag::err_builtin_counted_by_ref_invalid_arg)
- << Arg->getSourceRange();
-
- if (Arg->HasSideEffects(Context))
- return Diag(Arg->getBeginLoc(),
- diag::err_builtin_counted_by_ref_has_side_effects)
- << Arg->getSourceRange();
-
- if (const auto *ME = dyn_cast<MemberExpr>(Arg)) {
- const auto *CATy =
- ME->getMemberDecl()->getType()->getAs<CountAttributedType>();
-
- if (CATy && CATy->getKind() == CountAttributedType::CountedBy) {
- // Member has counted_by attribute - return pointer to count field
- const auto *MemberDecl = cast<FieldDecl>(ME->getMemberDecl());
- if (const FieldDecl *CountFD = MemberDecl->findCountedByField()) {
- TheCall->setType(Context.getPointerType(CountFD->getType()));
- return false;
- }
- }
-
- // FAMs and pointers without counted_by return void*
- QualType MemberTy = ME->getMemberDecl()->getType();
- if (!MemberTy->isArrayType() && !MemberTy->isPointerType())
- return Diag(Arg->getBeginLoc(),
- diag::err_builtin_counted_by_ref_invalid_arg)
- << Arg->getSourceRange();
- } else {
- return Diag(Arg->getBeginLoc(),
- diag::err_builtin_counted_by_ref_invalid_arg)
- << Arg->getSourceRange();
- }
-
- TheCall->setType(Context.getPointerType(Context.VoidTy));
- return false;
-}
-
-/// The result of __builtin_counted_by_ref cannot be assigned to a variable.
-/// It allows leaking and modification of bounds safety information.
-bool Sema::CheckInvalidBuiltinCountedByRef(const Expr *E,
- BuiltinCountedByRefKind K) {
- const CallExpr *CE =
- E ? dyn_cast<CallExpr>(E->IgnoreParenImpCasts()) : nullptr;
- if (!CE || CE->getBuiltinCallee() != Builtin::BI__builtin_counted_by_ref)
- return false;
-
- switch (K) {
- case BuiltinCountedByRefKind::Assignment:
- case BuiltinCountedByRefKind::Initializer:
- Diag(E->getExprLoc(),
- diag::err_builtin_counted_by_ref_cannot_leak_reference)
- << 0 << E->getSourceRange();
- break;
- case BuiltinCountedByRefKind::FunctionArg:
- Diag(E->getExprLoc(),
- diag::err_builtin_counted_by_ref_cannot_leak_reference)
- << 1 << E->getSourceRange();
- break;
- case BuiltinCountedByRefKind::ReturnArg:
- Diag(E->getExprLoc(),
- diag::err_builtin_counted_by_ref_cannot_leak_reference)
- << 2 << E->getSourceRange();
- break;
- case BuiltinCountedByRefKind::ArraySubscript:
- Diag(E->getExprLoc(), diag::err_builtin_counted_by_ref_invalid_use)
- << 0 << E->getSourceRange();
- break;
- case BuiltinCountedByRefKind::BinaryExpr:
- Diag(E->getExprLoc(), diag::err_builtin_counted_by_ref_invalid_use)
- << 1 << E->getSourceRange();
- break;
- }
-
- return true;
-}
-
-namespace {
-
-class UncoveredArgHandler {
- enum { Unknown = -1, AllCovered = -2 };
-
- signed FirstUncoveredArg = Unknown;
- SmallVector<const Expr *, 4> DiagnosticExprs;
-
-public:
- UncoveredArgHandler() = default;
-
- bool hasUncoveredArg() const {
- return (FirstUncoveredArg >= 0);
- }
-
- unsigned getUncoveredArg() const {
- assert(hasUncoveredArg() && "no uncovered argument");
- return FirstUncoveredArg;
- }
-
- void setAllCovered() {
- // A string has been found with all arguments covered, so clear out
- // the diagnostics.
- DiagnosticExprs.clear();
- FirstUncoveredArg = AllCovered;
- }
-
- void Update(signed NewFirstUncoveredArg, const Expr *StrExpr) {
- assert(NewFirstUncoveredArg >= 0 && "Outside range");
-
- // Don't update if a previous string covers all arguments.
- if (FirstUncoveredArg == AllCovered)
- return;
-
- // UncoveredArgHandler tracks the highest uncovered argument index
- // and with it all the strings that match this index.
- if (NewFirstUncoveredArg == FirstUncoveredArg)
- DiagnosticExprs.push_back(StrExpr);
- else if (NewFirstUncoveredArg > FirstUncoveredArg) {
- DiagnosticExprs.clear();
- DiagnosticExprs.push_back(StrExpr);
- FirstUncoveredArg = NewFirstUncoveredArg;
- }
- }
-
- void Diagnose(Sema &S, bool IsFunctionCall, const Expr *ArgExpr);
-};
-
-enum StringLiteralCheckType {
- SLCT_NotALiteral,
- SLCT_UncheckedLiteral,
- SLCT_CheckedLiteral
-};
-
-} // namespace
-
-static void sumOffsets(llvm::APSInt &Offset, llvm::APSInt Addend,
- BinaryOperatorKind BinOpKind,
- bool AddendIsRight) {
- unsigned BitWidth = Offset.getBitWidth();
- unsigned AddendBitWidth = Addend.getBitWidth();
- // There might be negative interim results.
- if (Addend.isUnsigned()) {
- Addend = Addend.zext(++AddendBitWidth);
- Addend.setIsSigned(true);
- }
- // Adjust the bit width of the APSInts.
- if (AddendBitWidth > BitWidth) {
- Offset = Offset.sext(AddendBitWidth);
- BitWidth = AddendBitWidth;
- } else if (BitWidth > AddendBitWidth) {
- Addend = Addend.sext(BitWidth);
- }
-
- bool Ov = false;
- llvm::APSInt ResOffset = Offset;
- if (BinOpKind == BO_Add)
- ResOffset = Offset.sadd_ov(Addend, Ov);
- else {
- assert(AddendIsRight && BinOpKind == BO_Sub &&
- "operator must be add or sub with addend on the right");
- ResOffset = Offset.ssub_ov(Addend, Ov);
- }
-
- // We add an offset to a pointer here so we should support an offset as big as
- // possible.
- if (Ov) {
- assert(BitWidth <= std::numeric_limits<unsigned>::max() / 2 &&
- "index (intermediate) result too big");
- Offset = Offset.sext(2 * BitWidth);
- sumOffsets(Offset, Addend, BinOpKind, AddendIsRight);
- return;
- }
-
- Offset = ResOffset;
-}
-
-namespace {
-
-// This is a wrapper class around StringLiteral to support offsetted string
-// literals as format strings. It takes the offset into account when returning
-// the string and its length or the source locations to display notes correctly.
-class FormatStringLiteral {
- const StringLiteral *FExpr;
- int64_t Offset;
-
-public:
- FormatStringLiteral(const StringLiteral *fexpr, int64_t Offset = 0)
- : FExpr(fexpr), Offset(Offset) {}
-
- const StringLiteral *getFormatString() const { return FExpr; }
-
- StringRef getString() const { return FExpr->getString().drop_front(Offset); }
-
- unsigned getByteLength() const {
- return FExpr->getByteLength() - getCharByteWidth() * Offset;
- }
-
- unsigned getLength() const { return FExpr->getLength() - Offset; }
- unsigned getCharByteWidth() const { return FExpr->getCharByteWidth(); }
-
- StringLiteralKind getKind() const { return FExpr->getKind(); }
-
- QualType getType() const { return FExpr->getType(); }
-
- bool isAscii() const { return FExpr->isOrdinary(); }
- bool isWide() const { return FExpr->isWide(); }
- bool isUTF8() const { return FExpr->isUTF8(); }
- bool isUTF16() const { return FExpr->isUTF16(); }
- bool isUTF32() const { return FExpr->isUTF32(); }
- bool isPascal() const { return FExpr->isPascal(); }
-
- SourceLocation getLocationOfByte(
- unsigned ByteNo, const SourceManager &SM, const LangOptions &Features,
- const TargetInfo &Target, unsigned *StartToken = nullptr,
- unsigned *StartTokenByteOffset = nullptr) const {
- return FExpr->getLocationOfByte(ByteNo + Offset, SM, Features, Target,
- StartToken, StartTokenByteOffset);
- }
-
- SourceLocation getBeginLoc() const LLVM_READONLY {
- return FExpr->getBeginLoc().getLocWithOffset(Offset);
- }
-
- SourceLocation getEndLoc() const LLVM_READONLY { return FExpr->getEndLoc(); }
-};
-
-} // namespace
-
-static void CheckFormatString(
- Sema &S, const FormatStringLiteral *FExpr,
- const StringLiteral *ReferenceFormatString, const Expr *OrigFormatExpr,
- ArrayRef<const Expr *> Args, Sema::FormatArgumentPassingKind APK,
- unsigned format_idx, unsigned firstDataArg, FormatStringType Type,
- bool inFunctionCall, VariadicCallType CallType,
- llvm::SmallBitVector &CheckedVarArgs, UncoveredArgHandler &UncoveredArg,
- bool IgnoreStringsWithoutSpecifiers);
-
-static const Expr *maybeConstEvalStringLiteral(ASTContext &Context,
- const Expr *E);
-
-// Determine if an expression is a string literal or constant string.
-// If this function returns false on the arguments to a function expecting a
-// format string, we will usually need to emit a warning.
-// True string literals are then checked by CheckFormatString.
-static StringLiteralCheckType
-checkFormatStringExpr(Sema &S, const StringLiteral *ReferenceFormatString,
- const Expr *E, ArrayRef<const Expr *> Args,
- Sema::FormatArgumentPassingKind APK, unsigned format_idx,
- unsigned firstDataArg, FormatStringType Type,
- VariadicCallType CallType, bool InFunctionCall,
- llvm::SmallBitVector &CheckedVarArgs,
- UncoveredArgHandler &UncoveredArg, llvm::APSInt Offset,
- std::optional<unsigned> *CallerFormatParamIdx = nullptr,
- bool IgnoreStringsWithoutSpecifiers = false) {
- if (S.isConstantEvaluatedContext())
- return SLCT_NotALiteral;
-tryAgain:
- assert(Offset.isSigned() && "invalid offset");
-
- if (E->isTypeDependent() || E->isValueDependent())
- return SLCT_NotALiteral;
-
- E = E->IgnoreParenCasts();
-
- if (E->isNullPointerConstant(S.Context, Expr::NPC_ValueDependentIsNotNull))
- // Technically -Wformat-nonliteral does not warn about this case.
- // The behavior of printf and friends in this case is implementation
- // dependent. Ideally if the format string cannot be null then
- // it should have a 'nonnull' attribute in the function prototype.
- return SLCT_UncheckedLiteral;
-
- switch (E->getStmtClass()) {
- case Stmt::InitListExprClass:
- // Handle expressions like {"foobar"}.
- if (const clang::Expr *SLE = maybeConstEvalStringLiteral(S.Context, E)) {
- return checkFormatStringExpr(S, ReferenceFormatString, SLE, Args, APK,
- format_idx, firstDataArg, Type, CallType,
- /*InFunctionCall*/ false, CheckedVarArgs,
- UncoveredArg, Offset, CallerFormatParamIdx,
- IgnoreStringsWithoutSpecifiers);
- }
- return SLCT_NotALiteral;
- case Stmt::BinaryConditionalOperatorClass:
- case Stmt::ConditionalOperatorClass: {
- // The expression is a literal if both sub-expressions were, and it was
- // completely checked only if both sub-expressions were checked.
- const AbstractConditionalOperator *C =
- cast<AbstractConditionalOperator>(E);
-
- // Determine whether it is necessary to check both sub-expressions, for
- // example, because the condition expression is a constant that can be
- // evaluated at compile time.
- bool CheckLeft = true, CheckRight = true;
-
- bool Cond;
- if (C->getCond()->EvaluateAsBooleanCondition(
- Cond, S.getASTContext(), S.isConstantEvaluatedContext())) {
- if (Cond)
- CheckRight = false;
- else
- CheckLeft = false;
- }
-
- // We need to maintain the offsets for the right and the left hand side
- // separately to check if every possible indexed expression is a valid
- // string literal. They might have different offsets for different string
- // literals in the end.
- StringLiteralCheckType Left;
- if (!CheckLeft)
- Left = SLCT_UncheckedLiteral;
- else {
- Left = checkFormatStringExpr(S, ReferenceFormatString, C->getTrueExpr(),
- Args, APK, format_idx, firstDataArg, Type,
- CallType, InFunctionCall, CheckedVarArgs,
- UncoveredArg, Offset, CallerFormatParamIdx,
- IgnoreStringsWithoutSpecifiers);
- if (Left == SLCT_NotALiteral || !CheckRight) {
- return Left;
- }
- }
-
- StringLiteralCheckType Right = checkFormatStringExpr(
- S, ReferenceFormatString, C->getFalseExpr(), Args, APK, format_idx,
- firstDataArg, Type, CallType, InFunctionCall, CheckedVarArgs,
- UncoveredArg, Offset, CallerFormatParamIdx,
- IgnoreStringsWithoutSpecifiers);
-
- return (CheckLeft && Left < Right) ? Left : Right;
- }
-
- case Stmt::ImplicitCastExprClass:
- E = cast<ImplicitCastExpr>(E)->getSubExpr();
- goto tryAgain;
-
- case Stmt::OpaqueValueExprClass:
- if (const Expr *src = cast<OpaqueValueExpr>(E)->getSourceExpr()) {
- E = src;
- goto tryAgain;
- }
- return SLCT_NotALiteral;
-
- case Stmt::PredefinedExprClass:
- // While __func__, etc., are technically not string literals, they
- // cannot contain format specifiers and thus are not a security
- // liability.
- return SLCT_UncheckedLiteral;
-
- case Stmt::DeclRefExprClass: {
- const DeclRefExpr *DR = cast<DeclRefExpr>(E);
-
- // As an exception, do not flag errors for variables binding to
- // const string literals.
- if (const VarDecl *VD = dyn_cast<VarDecl>(DR->getDecl())) {
- bool isConstant = false;
- QualType T = DR->getType();
-
- if (const ArrayType *AT = S.Context.getAsArrayType(T)) {
- isConstant = AT->getElementType().isConstant(S.Context);
- } else if (const PointerType *PT = T->getAs<PointerType>()) {
- isConstant = T.isConstant(S.Context) &&
- PT->getPointeeType().isConstant(S.Context);
- } else if (T->isObjCObjectPointerType()) {
- // In ObjC, there is usually no "const ObjectPointer" type,
- // so don't check if the pointee type is constant.
- isConstant = T.isConstant(S.Context);
- }
-
- if (isConstant) {
- if (const Expr *Init = VD->getAnyInitializer()) {
- // Look through initializers like const char c[] = { "foo" }
- if (const InitListExpr *InitList = dyn_cast<InitListExpr>(Init)) {
- if (InitList->isStringLiteralInit())
- Init = InitList->getInit(0)->IgnoreParenImpCasts();
- }
- return checkFormatStringExpr(
- S, ReferenceFormatString, Init, Args, APK, format_idx,
- firstDataArg, Type, CallType, /*InFunctionCall=*/false,
- CheckedVarArgs, UncoveredArg, Offset, CallerFormatParamIdx);
- }
- }
-
- // When the format argument is an argument of this function, and this
- // function also has the format attribute, there are several interactions
- // for which there shouldn't be a warning. For instance, when calling
- // v*printf from a function that has the printf format attribute, we
- // should not emit a warning about using `fmt`, even though it's not
- // constant, because the arguments have already been checked for the
- // caller of `logmessage`:
- //
- // __attribute__((format(printf, 1, 2)))
- // void logmessage(char const *fmt, ...) {
- // va_list ap;
- // va_start(ap, fmt);
- // vprintf(fmt, ap); /* do not emit a warning about "fmt" */
- // ...
- // }
- //
- // Another interaction that we need to support is using a format string
- // specified by the format_matches attribute:
- //
- // __attribute__((format_matches(printf, 1, "%s %d")))
- // void logmessage(char const *fmt, const char *a, int b) {
- // printf(fmt, a, b); /* do not emit a warning about "fmt" */
- // printf(fmt, 123.4); /* emit warnings that "%s %d" is incompatible */
- // ...
- // }
- //
- // Yet another interaction that we need to support is calling a variadic
- // format function from a format function that has fixed arguments. For
- // instance:
- //
- // __attribute__((format(printf, 1, 2)))
- // void logstring(char const *fmt, char const *str) {
- // printf(fmt, str); /* do not emit a warning about "fmt" */
- // }
- //
- // Same (and perhaps more relatably) for the variadic template case:
- //
- // template<typename... Args>
- // __attribute__((format(printf, 1, 2)))
- // void log(const char *fmt, Args&&... args) {
- // printf(fmt, forward<Args>(args)...);
- // /* do not emit a warning about "fmt" */
- // }
- //
- // Due to implementation difficulty, we only check the format, not the
- // format arguments, in all cases.
- //
- if (const auto *PV = dyn_cast<ParmVarDecl>(VD)) {
- if (CallerFormatParamIdx)
- *CallerFormatParamIdx = PV->getFunctionScopeIndex();
- if (const auto *D = dyn_cast<Decl>(PV->getDeclContext())) {
- for (const auto *PVFormatMatches :
- D->specific_attrs<FormatMatchesAttr>()) {
- Sema::FormatStringInfo CalleeFSI;
- if (!Sema::getFormatStringInfo(D, PVFormatMatches->getFormatIdx(),
- 0, &CalleeFSI))
- continue;
- if (PV->getFunctionScopeIndex() == CalleeFSI.FormatIdx) {
- // If using the wrong type of format string, emit a diagnostic
- // here and stop checking to avoid irrelevant diagnostics.
- if (Type != S.GetFormatStringType(PVFormatMatches)) {
- S.Diag(Args[format_idx]->getBeginLoc(),
- diag::warn_format_string_type_incompatible)
- << PVFormatMatches->getType()->getName()
- << S.GetFormatStringTypeName(Type);
- if (!InFunctionCall) {
- S.Diag(PVFormatMatches->getFormatString()->getBeginLoc(),
- diag::note_format_string_defined);
- }
- return SLCT_UncheckedLiteral;
- }
- return checkFormatStringExpr(
- S, ReferenceFormatString, PVFormatMatches->getFormatString(),
- Args, APK, format_idx, firstDataArg, Type, CallType,
- /*InFunctionCall*/ false, CheckedVarArgs, UncoveredArg,
- Offset, CallerFormatParamIdx, IgnoreStringsWithoutSpecifiers);
- }
- }
-
- for (const auto *PVFormat : D->specific_attrs<FormatAttr>()) {
- Sema::FormatStringInfo CallerFSI;
- if (!Sema::getFormatStringInfo(D, PVFormat->getFormatIdx(),
- PVFormat->getFirstArg(), &CallerFSI))
- continue;
- if (PV->getFunctionScopeIndex() == CallerFSI.FormatIdx) {
- // We also check if the formats are compatible.
- // We can't pass a 'scanf' string to a 'printf' function.
- if (Type != S.GetFormatStringType(PVFormat)) {
- S.Diag(Args[format_idx]->getBeginLoc(),
- diag::warn_format_string_type_incompatible)
- << PVFormat->getType()->getName()
- << S.GetFormatStringTypeName(Type);
- if (!InFunctionCall) {
- S.Diag(E->getBeginLoc(), diag::note_format_string_defined);
- }
- return SLCT_UncheckedLiteral;
- }
- // Lastly, check that argument passing kinds transition in a
- // way that makes sense:
- // from a caller with FAPK_VAList, allow FAPK_VAList
- // from a caller with FAPK_Fixed, allow FAPK_Fixed
- // from a caller with FAPK_Fixed, allow FAPK_Variadic
- // from a caller with FAPK_Variadic, allow FAPK_VAList
- switch (combineFAPK(CallerFSI.ArgPassingKind, APK)) {
- case combineFAPK(Sema::FAPK_VAList, Sema::FAPK_VAList):
- case combineFAPK(Sema::FAPK_Fixed, Sema::FAPK_Fixed):
- case combineFAPK(Sema::FAPK_Fixed, Sema::FAPK_Variadic):
- case combineFAPK(Sema::FAPK_Variadic, Sema::FAPK_VAList):
- return SLCT_UncheckedLiteral;
- }
- }
- }
- }
- }
- }
-
- return SLCT_NotALiteral;
- }
-
- case Stmt::CallExprClass:
- case Stmt::CXXMemberCallExprClass: {
- const CallExpr *CE = cast<CallExpr>(E);
- if (const NamedDecl *ND = dyn_cast_or_null<NamedDecl>(CE->getCalleeDecl())) {
- bool IsFirst = true;
- StringLiteralCheckType CommonResult;
- for (const auto *FA : ND->specific_attrs<FormatArgAttr>()) {
- const Expr *Arg = CE->getArg(FA->getFormatIdx().getASTIndex());
- StringLiteralCheckType Result = checkFormatStringExpr(
- S, ReferenceFormatString, Arg, Args, APK, format_idx, firstDataArg,
- Type, CallType, InFunctionCall, CheckedVarArgs, UncoveredArg,
- Offset, CallerFormatParamIdx, IgnoreStringsWithoutSpecifiers);
- if (IsFirst) {
- CommonResult = Result;
- IsFirst = false;
- }
- }
- if (!IsFirst)
- return CommonResult;
-
- if (const auto *FD = dyn_cast<FunctionDecl>(ND)) {
- unsigned BuiltinID = FD->getBuiltinID();
- if (BuiltinID == Builtin::BI__builtin___CFStringMakeConstantString ||
- BuiltinID == Builtin::BI__builtin___NSStringMakeConstantString) {
- const Expr *Arg = CE->getArg(0);
- return checkFormatStringExpr(
- S, ReferenceFormatString, Arg, Args, APK, format_idx,
- firstDataArg, Type, CallType, InFunctionCall, CheckedVarArgs,
- UncoveredArg, Offset, CallerFormatParamIdx,
- IgnoreStringsWithoutSpecifiers);
- }
- }
- }
- if (const Expr *SLE = maybeConstEvalStringLiteral(S.Context, E))
- return checkFormatStringExpr(S, ReferenceFormatString, SLE, Args, APK,
- format_idx, firstDataArg, Type, CallType,
- /*InFunctionCall*/ false, CheckedVarArgs,
- UncoveredArg, Offset, CallerFormatParamIdx,
- IgnoreStringsWithoutSpecifiers);
- return SLCT_NotALiteral;
- }
- case Stmt::ObjCMessageExprClass: {
- const auto *ME = cast<ObjCMessageExpr>(E);
- if (const auto *MD = ME->getMethodDecl()) {
- if (const auto *FA = MD->getAttr<FormatArgAttr>()) {
- // As a special case heuristic, if we're using the method -[NSBundle
- // localizedStringForKey:value:table:], ignore any key strings that lack
- // format specifiers. The idea is that if the key doesn't have any
- // format specifiers then its probably just a key to map to the
- // localized strings. If it does have format specifiers though, then its
- // likely that the text of the key is the format string in the
- // programmer's language, and should be checked.
- const ObjCInterfaceDecl *IFace;
- if (MD->isInstanceMethod() && (IFace = MD->getClassInterface()) &&
- IFace->getIdentifier()->isStr("NSBundle") &&
- MD->getSelector().isKeywordSelector(
- {"localizedStringForKey", "value", "table"})) {
- IgnoreStringsWithoutSpecifiers = true;
- }
-
- const Expr *Arg = ME->getArg(FA->getFormatIdx().getASTIndex());
- return checkFormatStringExpr(
- S, ReferenceFormatString, Arg, Args, APK, format_idx, firstDataArg,
- Type, CallType, InFunctionCall, CheckedVarArgs, UncoveredArg,
- Offset, CallerFormatParamIdx, IgnoreStringsWithoutSpecifiers);
- }
- }
-
- return SLCT_NotALiteral;
- }
- case Stmt::ObjCStringLiteralClass:
- case Stmt::StringLiteralClass: {
- const StringLiteral *StrE = nullptr;
-
- if (const ObjCStringLiteral *ObjCFExpr = dyn_cast<ObjCStringLiteral>(E))
- StrE = ObjCFExpr->getString();
- else
- StrE = cast<StringLiteral>(E);
-
- if (StrE) {
- if (Offset.isNegative() || Offset > StrE->getLength()) {
- // TODO: It would be better to have an explicit warning for out of
- // bounds literals.
- return SLCT_NotALiteral;
- }
- FormatStringLiteral FStr(StrE, Offset.sextOrTrunc(64).getSExtValue());
- CheckFormatString(S, &FStr, ReferenceFormatString, E, Args, APK,
- format_idx, firstDataArg, Type, InFunctionCall,
- CallType, CheckedVarArgs, UncoveredArg,
- IgnoreStringsWithoutSpecifiers);
- return SLCT_CheckedLiteral;
- }
-
- return SLCT_NotALiteral;
- }
- case Stmt::BinaryOperatorClass: {
- const BinaryOperator *BinOp = cast<BinaryOperator>(E);
-
- // A string literal + an int offset is still a string literal.
- if (BinOp->isAdditiveOp()) {
- Expr::EvalResult LResult, RResult;
-
- bool LIsInt = BinOp->getLHS()->EvaluateAsInt(
- LResult, S.Context, Expr::SE_NoSideEffects,
- S.isConstantEvaluatedContext());
- bool RIsInt = BinOp->getRHS()->EvaluateAsInt(
- RResult, S.Context, Expr::SE_NoSideEffects,
- S.isConstantEvaluatedContext());
-
- if (LIsInt != RIsInt) {
- BinaryOperatorKind BinOpKind = BinOp->getOpcode();
-
- if (LIsInt) {
- if (BinOpKind == BO_Add) {
- sumOffsets(Offset, LResult.Val.getInt(), BinOpKind, RIsInt);
- E = BinOp->getRHS();
- goto tryAgain;
- }
- } else {
- sumOffsets(Offset, RResult.Val.getInt(), BinOpKind, RIsInt);
- E = BinOp->getLHS();
- goto tryAgain;
- }
- }
- }
-
- return SLCT_NotALiteral;
- }
- case Stmt::UnaryOperatorClass: {
- const UnaryOperator *UnaOp = cast<UnaryOperator>(E);
- auto ASE = dyn_cast<ArraySubscriptExpr>(UnaOp->getSubExpr());
- if (UnaOp->getOpcode() == UO_AddrOf && ASE) {
- Expr::EvalResult IndexResult;
- if (ASE->getRHS()->EvaluateAsInt(IndexResult, S.Context,
- Expr::SE_NoSideEffects,
- S.isConstantEvaluatedContext())) {
- sumOffsets(Offset, IndexResult.Val.getInt(), BO_Add,
- /*RHS is int*/ true);
- E = ASE->getBase();
- goto tryAgain;
- }
- }
-
- return SLCT_NotALiteral;
- }
-
- default:
- return SLCT_NotALiteral;
- }
-}
-
-// If this expression can be evaluated at compile-time,
-// check if the result is a StringLiteral and return it
-// otherwise return nullptr
-static const Expr *maybeConstEvalStringLiteral(ASTContext &Context,
- const Expr *E) {
- Expr::EvalResult Result;
- if (E->EvaluateAsRValue(Result, Context) && Result.Val.isLValue()) {
- const auto *LVE = Result.Val.getLValueBase().dyn_cast<const Expr *>();
- if (isa_and_nonnull<StringLiteral>(LVE))
- return LVE;
- }
- return nullptr;
-}
-
-StringRef Sema::GetFormatStringTypeName(FormatStringType FST) {
- switch (FST) {
- case FormatStringType::Scanf:
- return "scanf";
- case FormatStringType::Printf:
- return "printf";
- case FormatStringType::NSString:
- return "NSString";
- case FormatStringType::Strftime:
- return "strftime";
- case FormatStringType::Strfmon:
- return "strfmon";
- case FormatStringType::Kprintf:
- return "kprintf";
- case FormatStringType::FreeBSDKPrintf:
- return "freebsd_kprintf";
- case FormatStringType::OSLog:
- return "os_log";
- default:
- return "<unknown>";
- }
-}
-
-FormatStringType Sema::GetFormatStringType(StringRef Flavor) {
- return llvm::StringSwitch<FormatStringType>(Flavor)
- .Cases({"gnu_scanf", "scanf"}, FormatStringType::Scanf)
- .Cases({"gnu_printf", "printf", "printf0", "syslog"},
- FormatStringType::Printf)
- .Cases({"NSString", "CFString"}, FormatStringType::NSString)
- .Cases({"gnu_strftime", "strftime"}, FormatStringType::Strftime)
- .Cases({"gnu_strfmon", "strfmon"}, FormatStringType::Strfmon)
- .Cases({"kprintf", "cmn_err", "vcmn_err", "zcmn_err"},
- FormatStringType::Kprintf)
- .Case("freebsd_kprintf", FormatStringType::FreeBSDKPrintf)
- .Case("os_trace", FormatStringType::OSLog)
- .Case("os_log", FormatStringType::OSLog)
- .Default(FormatStringType::Unknown);
-}
-
-FormatStringType Sema::GetFormatStringType(const FormatAttr *Format) {
- return GetFormatStringType(Format->getType()->getName());
-}
-
-FormatStringType Sema::GetFormatStringType(const FormatMatchesAttr *Format) {
- return GetFormatStringType(Format->getType()->getName());
-}
-
-bool Sema::CheckFormatArguments(const FormatAttr *Format,
- ArrayRef<const Expr *> Args, bool IsCXXMember,
- VariadicCallType CallType, SourceLocation Loc,
- SourceRange Range,
- llvm::SmallBitVector &CheckedVarArgs) {
- FormatStringInfo FSI;
- if (getFormatStringInfo(Format->getFormatIdx(), Format->getFirstArg(),
- IsCXXMember,
- CallType != VariadicCallType::DoesNotApply, &FSI))
- return CheckFormatArguments(
- Args, FSI.ArgPassingKind, nullptr, FSI.FormatIdx, FSI.FirstDataArg,
- GetFormatStringType(Format), CallType, Loc, Range, CheckedVarArgs);
- return false;
-}
-
-bool Sema::CheckFormatString(const FormatMatchesAttr *Format,
- ArrayRef<const Expr *> Args, bool IsCXXMember,
- VariadicCallType CallType, SourceLocation Loc,
- SourceRange Range,
- llvm::SmallBitVector &CheckedVarArgs) {
- FormatStringInfo FSI;
- if (getFormatStringInfo(Format->getFormatIdx(), 0, IsCXXMember, false,
- &FSI)) {
- FSI.ArgPassingKind = Sema::FAPK_Elsewhere;
- return CheckFormatArguments(Args, FSI.ArgPassingKind,
- Format->getFormatString(), FSI.FormatIdx,
- FSI.FirstDataArg, GetFormatStringType(Format),
- CallType, Loc, Range, CheckedVarArgs);
- }
- return false;
-}
-
-static bool CheckMissingFormatAttribute(
- Sema *S, ArrayRef<const Expr *> Args, Sema::FormatArgumentPassingKind APK,
- StringLiteral *ReferenceFormatString, unsigned FormatIdx,
- unsigned FirstDataArg, FormatStringType FormatType, unsigned CallerParamIdx,
- SourceLocation Loc) {
- if (S->getDiagnostics().isIgnored(diag::warn_missing_format_attribute, Loc))
- return false;
-
- DeclContext *DC = S->CurContext;
- if (!isa<ObjCMethodDecl>(DC) && !isa<FunctionDecl>(DC) && !isa<BlockDecl>(DC))
- return false;
- Decl *Caller = cast<Decl>(DC)->getCanonicalDecl();
-
- unsigned NumCallerParams = getFunctionOrMethodNumParams(Caller);
-
- // Find the offset to convert between attribute and parameter indexes.
- unsigned CallerArgumentIndexOffset =
- hasImplicitObjectParameter(Caller) ? 2 : 1;
-
- unsigned FirstArgumentIndex = -1;
- switch (APK) {
- case Sema::FormatArgumentPassingKind::FAPK_Fixed:
- case Sema::FormatArgumentPassingKind::FAPK_Variadic: {
- // As an extension, clang allows the format attribute on non-variadic
- // functions.
- // Caller must have fixed arguments to pass them to a fixed or variadic
- // function. Try to match caller and callee arguments. If successful, then
- // emit a diag with the caller idx, otherwise we can't determine the callee
- // arguments.
- unsigned NumCalleeArgs = Args.size() - FirstDataArg;
- if (NumCalleeArgs == 0 || NumCallerParams < NumCalleeArgs) {
- // There aren't enough arguments in the caller to pass to callee.
- return false;
- }
- for (unsigned CalleeIdx = Args.size() - 1, CallerIdx = NumCallerParams - 1;
- CalleeIdx >= FirstDataArg; --CalleeIdx, --CallerIdx) {
- const auto *Arg =
- dyn_cast<DeclRefExpr>(Args[CalleeIdx]->IgnoreParenCasts());
- if (!Arg)
- return false;
- const auto *Param = dyn_cast<ParmVarDecl>(Arg->getDecl());
- if (!Param || Param->getFunctionScopeIndex() != CallerIdx)
- return false;
- }
- FirstArgumentIndex =
- NumCallerParams + CallerArgumentIndexOffset - NumCalleeArgs;
- break;
- }
- case Sema::FormatArgumentPassingKind::FAPK_VAList:
- // Caller arguments are either variadic or a va_list.
- FirstArgumentIndex = isFunctionOrMethodVariadic(Caller)
- ? (NumCallerParams + CallerArgumentIndexOffset)
- : 0;
- break;
- case Sema::FormatArgumentPassingKind::FAPK_Elsewhere:
- // The callee has a format_matches attribute. We will emit that instead.
- if (!ReferenceFormatString)
- return false;
- break;
- }
-
- // Emit the diagnostic and fixit.
- unsigned FormatStringIndex = CallerParamIdx + CallerArgumentIndexOffset;
- StringRef FormatTypeName = S->GetFormatStringTypeName(FormatType);
- NamedDecl *ND = dyn_cast<NamedDecl>(Caller);
- do {
- std::string Attr, Fixit;
- llvm::raw_string_ostream AttrOS(Attr);
- if (APK != Sema::FormatArgumentPassingKind::FAPK_Elsewhere) {
- AttrOS << "format(" << FormatTypeName << ", " << FormatStringIndex << ", "
- << FirstArgumentIndex << ")";
- } else {
- AttrOS << "format_matches(" << FormatTypeName << ", " << FormatStringIndex
- << ", \"";
- AttrOS.write_escaped(ReferenceFormatString->getString());
- AttrOS << "\")";
- }
- AttrOS.flush();
- auto DB = S->Diag(Loc, diag::warn_missing_format_attribute) << Attr;
- if (ND)
- DB << ND;
- else
- DB << "block";
-
- // Blocks don't provide a correct end loc, so skip emitting a fixit.
- if (isa<BlockDecl>(Caller))
- break;
-
- SourceLocation SL;
- llvm::raw_string_ostream IS(Fixit);
- // The attribute goes at the start of the declaration in C/C++ functions
- // and methods, but after the declaration for Objective-C methods.
- if (isa<ObjCMethodDecl>(Caller)) {
- IS << ' ';
- SL = Caller->getEndLoc();
- }
- const LangOptions &LO = S->getLangOpts();
- if (LO.C23 || LO.CPlusPlus11)
- IS << "[[gnu::" << Attr << "]]";
- else if (LO.ObjC || LO.GNUMode)
- IS << "__attribute__((" << Attr << "))";
- else
- break;
- if (!isa<ObjCMethodDecl>(Caller)) {
- IS << ' ';
- SL = Caller->getBeginLoc();
- }
- IS.flush();
-
- DB << FixItHint::CreateInsertion(SL, Fixit);
- } while (false);
-
- // Add implicit format or format_matches attribute.
- if (APK != Sema::FormatArgumentPassingKind::FAPK_Elsewhere) {
- Caller->addAttr(FormatAttr::CreateImplicit(
- S->getASTContext(), &S->getASTContext().Idents.get(FormatTypeName),
- FormatStringIndex, FirstArgumentIndex));
- } else {
- Caller->addAttr(FormatMatchesAttr::CreateImplicit(
- S->getASTContext(), &S->getASTContext().Idents.get(FormatTypeName),
- FormatStringIndex, ReferenceFormatString));
- }
-
- {
- auto DB = S->Diag(Caller->getLocation(), diag::note_entity_declared_at);
- if (ND)
- DB << ND;
- else
- DB << "block";
- }
- return true;
-}
-
-bool Sema::CheckFormatArguments(ArrayRef<const Expr *> Args,
- Sema::FormatArgumentPassingKind APK,
- StringLiteral *ReferenceFormatString,
- unsigned format_idx, unsigned firstDataArg,
- FormatStringType Type,
- VariadicCallType CallType, SourceLocation Loc,
- SourceRange Range,
- llvm::SmallBitVector &CheckedVarArgs) {
- // CHECK: printf/scanf-like function is called with no format string.
- if (format_idx >= Args.size()) {
- Diag(Loc, diag::warn_missing_format_string) << Range;
- return false;
- }
-
- const Expr *OrigFormatExpr = Args[format_idx]->IgnoreParenCasts();
-
- // CHECK: format string is not a string literal.
- //
- // Dynamically generated format strings are difficult to
- // automatically vet at compile time. Requiring that format strings
- // are string literals: (1) permits the checking of format strings by
- // the compiler and thereby (2) can practically remove the source of
- // many format string exploits.
-
- // Format string can be either ObjC string (e.g. @"%d") or
- // C string (e.g. "%d")
- // ObjC string uses the same format specifiers as C string, so we can use
- // the same format string checking logic for both ObjC and C strings.
- UncoveredArgHandler UncoveredArg;
- std::optional<unsigned> CallerParamIdx;
- StringLiteralCheckType CT = checkFormatStringExpr(
- *this, ReferenceFormatString, OrigFormatExpr, Args, APK, format_idx,
- firstDataArg, Type, CallType,
- /*IsFunctionCall*/ true, CheckedVarArgs, UncoveredArg,
- /*no string offset*/ llvm::APSInt(64, false) = 0, &CallerParamIdx);
-
- // Generate a diagnostic where an uncovered argument is detected.
- if (UncoveredArg.hasUncoveredArg()) {
- unsigned ArgIdx = UncoveredArg.getUncoveredArg() + firstDataArg;
- assert(ArgIdx < Args.size() && "ArgIdx outside bounds");
- UncoveredArg.Diagnose(*this, /*IsFunctionCall*/true, Args[ArgIdx]);
- }
-
- if (CT != SLCT_NotALiteral)
- // Literal format string found, check done!
- return CT == SLCT_CheckedLiteral;
-
- // Do not emit diag when the string param is a macro expansion and the
- // format is either NSString or CFString. This is a hack to prevent
- // diag when using the NSLocalizedString and CFCopyLocalizedString macros
- // which are usually used in place of NS and CF string literals.
- SourceLocation FormatLoc = Args[format_idx]->getBeginLoc();
- if (Type == FormatStringType::NSString &&
- SourceMgr.isInSystemMacro(FormatLoc))
- return false;
-
- if (CallerParamIdx && CheckMissingFormatAttribute(
- this, Args, APK, ReferenceFormatString, format_idx,
- firstDataArg, Type, *CallerParamIdx, Loc))
- return false;
-
- // Strftime is particular as it always uses a single 'time' argument,
- // so it is safe to pass a non-literal string.
- if (Type == FormatStringType::Strftime)
- return false;
-
- // If there are no arguments specified, warn with -Wformat-security, otherwise
- // warn only with -Wformat-nonliteral.
- if (Args.size() == firstDataArg) {
- Diag(FormatLoc, diag::warn_format_nonliteral_noargs)
- << OrigFormatExpr->getSourceRange();
- switch (Type) {
- default:
- break;
- case FormatStringType::Kprintf:
- case FormatStringType::FreeBSDKPrintf:
- case FormatStringType::Printf:
- Diag(FormatLoc, diag::note_format_security_fixit)
- << FixItHint::CreateInsertion(FormatLoc, "\"%s\", ");
- break;
- case FormatStringType::NSString:
- Diag(FormatLoc, diag::note_format_security_fixit)
- << FixItHint::CreateInsertion(FormatLoc, "@\"%@\", ");
- break;
- }
- } else {
- Diag(FormatLoc, diag::warn_format_nonliteral)
- << OrigFormatExpr->getSourceRange();
- }
- return false;
-}
-
-namespace {
-
-class CheckFormatHandler : public analyze_format_string::FormatStringHandler {
-protected:
- Sema &S;
- const FormatStringLiteral *FExpr;
- const Expr *OrigFormatExpr;
- const FormatStringType FSType;
- const unsigned FirstDataArg;
- const unsigned NumDataArgs;
- const char *Beg; // Start of format string.
- const Sema::FormatArgumentPassingKind ArgPassingKind;
- ArrayRef<const Expr *> Args;
- unsigned FormatIdx;
- llvm::SmallBitVector CoveredArgs;
- bool usesPositionalArgs = false;
- bool atFirstArg = true;
- bool inFunctionCall;
- VariadicCallType CallType;
- llvm::SmallBitVector &CheckedVarArgs;
- UncoveredArgHandler &UncoveredArg;
-
-public:
- CheckFormatHandler(Sema &s, const FormatStringLiteral *fexpr,
- const Expr *origFormatExpr, const FormatStringType type,
- unsigned firstDataArg, unsigned numDataArgs,
- const char *beg, Sema::FormatArgumentPassingKind APK,
- ArrayRef<const Expr *> Args, unsigned formatIdx,
- bool inFunctionCall, VariadicCallType callType,
- llvm::SmallBitVector &CheckedVarArgs,
- UncoveredArgHandler &UncoveredArg)
- : S(s), FExpr(fexpr), OrigFormatExpr(origFormatExpr), FSType(type),
- FirstDataArg(firstDataArg), NumDataArgs(numDataArgs), Beg(beg),
- ArgPassingKind(APK), Args(Args), FormatIdx(formatIdx),
- inFunctionCall(inFunctionCall), CallType(callType),
- CheckedVarArgs(CheckedVarArgs), UncoveredArg(UncoveredArg) {
- CoveredArgs.resize(numDataArgs);
- CoveredArgs.reset();
- }
-
- bool HasFormatArguments() const {
- return ArgPassingKind == Sema::FAPK_Fixed ||
- ArgPassingKind == Sema::FAPK_Variadic;
- }
-
- void DoneProcessing();
-
- void HandleIncompleteSpecifier(const char *startSpecifier,
- unsigned specifierLen) override;
-
- void HandleInvalidLengthModifier(
- const analyze_format_string::FormatSpecifier &FS,
- const analyze_format_string::ConversionSpecifier &CS,
- const char *startSpecifier, unsigned specifierLen,
- unsigned DiagID);
-
- void HandleNonStandardLengthModifier(
- const analyze_format_string::FormatSpecifier &FS,
- const char *startSpecifier, unsigned specifierLen);
-
- void HandleNonStandardConversionSpecifier(
- const analyze_format_string::ConversionSpecifier &CS,
- const char *startSpecifier, unsigned specifierLen);
-
- void HandlePosition(const char *startPos, unsigned posLen) override;
-
- void HandleInvalidPosition(const char *startSpecifier,
- unsigned specifierLen,
- analyze_format_string::PositionContext p) override;
-
- void HandleZeroPosition(const char *startPos, unsigned posLen) override;
-
- void HandleNullChar(const char *nullCharacter) override;
-
- template <typename Range>
- static void
- EmitFormatDiagnostic(Sema &S, bool inFunctionCall, const Expr *ArgumentExpr,
- const PartialDiagnostic &PDiag, SourceLocation StringLoc,
- bool IsStringLocation, Range StringRange,
- ArrayRef<FixItHint> Fixit = {});
-
-protected:
- bool HandleInvalidConversionSpecifier(unsigned argIndex, SourceLocation Loc,
- const char *startSpec,
- unsigned specifierLen,
- const char *csStart, unsigned csLen);
-
- void HandlePositionalNonpositionalArgs(SourceLocation Loc,
- const char *startSpec,
- unsigned specifierLen);
-
- SourceRange getFormatStringRange();
- CharSourceRange getSpecifierRange(const char *startSpecifier,
- unsigned specifierLen);
- SourceLocation getLocationOfByte(const char *x);
-
- const Expr *getDataArg(unsigned i) const;
-
- bool CheckNumArgs(const analyze_format_string::FormatSpecifier &FS,
- const analyze_format_string::ConversionSpecifier &CS,
- const char *startSpecifier, unsigned specifierLen,
- unsigned argIndex);
-
- template <typename Range>
- void EmitFormatDiagnostic(PartialDiagnostic PDiag, SourceLocation StringLoc,
- bool IsStringLocation, Range StringRange,
- ArrayRef<FixItHint> Fixit = {});
-};
-
-} // namespace
-
-SourceRange CheckFormatHandler::getFormatStringRange() {
- return OrigFormatExpr->getSourceRange();
-}
-
-CharSourceRange CheckFormatHandler::
-getSpecifierRange(const char *startSpecifier, unsigned specifierLen) {
- SourceLocation Start = getLocationOfByte(startSpecifier);
- SourceLocation End = getLocationOfByte(startSpecifier + specifierLen - 1);
-
- // Advance the end SourceLocation by one due to half-open ranges.
- End = End.getLocWithOffset(1);
-
- return CharSourceRange::getCharRange(Start, End);
-}
-
-SourceLocation CheckFormatHandler::getLocationOfByte(const char *x) {
- return FExpr->getLocationOfByte(x - Beg, S.getSourceManager(),
- S.getLangOpts(), S.Context.getTargetInfo());
-}
-
-void CheckFormatHandler::HandleIncompleteSpecifier(const char *startSpecifier,
- unsigned specifierLen){
- EmitFormatDiagnostic(S.PDiag(diag::warn_printf_incomplete_specifier),
- getLocationOfByte(startSpecifier),
- /*IsStringLocation*/true,
- getSpecifierRange(startSpecifier, specifierLen));
-}
-
-void CheckFormatHandler::HandleInvalidLengthModifier(
- const analyze_format_string::FormatSpecifier &FS,
- const analyze_format_string::ConversionSpecifier &CS,
- const char *startSpecifier, unsigned specifierLen, unsigned DiagID) {
- using namespace analyze_format_string;
-
- const LengthModifier &LM = FS.getLengthModifier();
- CharSourceRange LMRange = getSpecifierRange(LM.getStart(), LM.getLength());
-
- // See if we know how to fix this length modifier.
- std::optional<LengthModifier> FixedLM = FS.getCorrectedLengthModifier();
- if (FixedLM) {
- EmitFormatDiagnostic(S.PDiag(DiagID) << LM.toString() << CS.toString(),
- getLocationOfByte(LM.getStart()),
- /*IsStringLocation*/true,
- getSpecifierRange(startSpecifier, specifierLen));
-
- S.Diag(getLocationOfByte(LM.getStart()), diag::note_format_fix_specifier)
- << FixedLM->toString()
- << FixItHint::CreateReplacement(LMRange, FixedLM->toString());
-
- } else {
- FixItHint Hint;
- if (DiagID == diag::warn_format_nonsensical_length)
- Hint = FixItHint::CreateRemoval(LMRange);
-
- EmitFormatDiagnostic(S.PDiag(DiagID) << LM.toString() << CS.toString(),
- getLocationOfByte(LM.getStart()),
- /*IsStringLocation*/true,
- getSpecifierRange(startSpecifier, specifierLen),
- Hint);
- }
-}
-
-void CheckFormatHandler::HandleNonStandardLengthModifier(
- const analyze_format_string::FormatSpecifier &FS,
- const char *startSpecifier, unsigned specifierLen) {
- using namespace analyze_format_string;
-
- const LengthModifier &LM = FS.getLengthModifier();
- CharSourceRange LMRange = getSpecifierRange(LM.getStart(), LM.getLength());
-
- // See if we know how to fix this length modifier.
- std::optional<LengthModifier> FixedLM = FS.getCorrectedLengthModifier();
- if (FixedLM) {
- EmitFormatDiagnostic(S.PDiag(diag::warn_format_non_standard)
- << LM.toString() << 0,
- getLocationOfByte(LM.getStart()),
- /*IsStringLocation*/true,
- getSpecifierRange(startSpecifier, specifierLen));
-
- S.Diag(getLocationOfByte(LM.getStart()), diag::note_format_fix_specifier)
- << FixedLM->toString()
- << FixItHint::CreateReplacement(LMRange, FixedLM->toString());
-
- } else {
- EmitFormatDiagnostic(S.PDiag(diag::warn_format_non_standard)
- << LM.toString() << 0,
- getLocationOfByte(LM.getStart()),
- /*IsStringLocation*/true,
- getSpecifierRange(startSpecifier, specifierLen));
- }
-}
-
-void CheckFormatHandler::HandleNonStandardConversionSpecifier(
- const analyze_format_string::ConversionSpecifier &CS,
- const char *startSpecifier, unsigned specifierLen) {
- using namespace analyze_format_string;
-
- // See if we know how to fix this conversion specifier.
- std::optional<ConversionSpecifier> FixedCS = CS.getStandardSpecifier();
- if (FixedCS) {
- EmitFormatDiagnostic(S.PDiag(diag::warn_format_non_standard)
- << CS.toString() << /*conversion specifier*/1,
- getLocationOfByte(CS.getStart()),
- /*IsStringLocation*/true,
- getSpecifierRange(startSpecifier, specifierLen));
-
- CharSourceRange CSRange = getSpecifierRange(CS.getStart(), CS.getLength());
- S.Diag(getLocationOfByte(CS.getStart()), diag::note_format_fix_specifier)
- << FixedCS->toString()
- << FixItHint::CreateReplacement(CSRange, FixedCS->toString());
- } else {
- EmitFormatDiagnostic(S.PDiag(diag::warn_format_non_standard)
- << CS.toString() << /*conversion specifier*/1,
- getLocationOfByte(CS.getStart()),
- /*IsStringLocation*/true,
- getSpecifierRange(startSpecifier, specifierLen));
- }
-}
-
-void CheckFormatHandler::HandlePosition(const char *startPos,
- unsigned posLen) {
- if (!S.getDiagnostics().isIgnored(
- diag::warn_format_non_standard_positional_arg, SourceLocation()))
- EmitFormatDiagnostic(S.PDiag(diag::warn_format_non_standard_positional_arg),
- getLocationOfByte(startPos),
- /*IsStringLocation*/ true,
- getSpecifierRange(startPos, posLen));
-}
-
-void CheckFormatHandler::HandleInvalidPosition(
- const char *startSpecifier, unsigned specifierLen,
- analyze_format_string::PositionContext p) {
- if (!S.getDiagnostics().isIgnored(
- diag::warn_format_invalid_positional_specifier, SourceLocation()))
- EmitFormatDiagnostic(
- S.PDiag(diag::warn_format_invalid_positional_specifier) << (unsigned)p,
- getLocationOfByte(startSpecifier), /*IsStringLocation*/ true,
- getSpecifierRange(startSpecifier, specifierLen));
-}
-
-void CheckFormatHandler::HandleZeroPosition(const char *startPos,
- unsigned posLen) {
- if (!S.getDiagnostics().isIgnored(diag::warn_format_zero_positional_specifier,
- SourceLocation()))
- EmitFormatDiagnostic(S.PDiag(diag::warn_format_zero_positional_specifier),
- getLocationOfByte(startPos),
- /*IsStringLocation*/ true,
- getSpecifierRange(startPos, posLen));
-}
-
-void CheckFormatHandler::HandleNullChar(const char *nullCharacter) {
- if (!isa<ObjCStringLiteral>(OrigFormatExpr)) {
- // The presence of a null character is likely an error.
- EmitFormatDiagnostic(
- S.PDiag(diag::warn_printf_format_string_contains_null_char),
- getLocationOfByte(nullCharacter), /*IsStringLocation*/true,
- getFormatStringRange());
- }
-}
-
-// Note that this may return NULL if there was an error parsing or building
-// one of the argument expressions.
-const Expr *CheckFormatHandler::getDataArg(unsigned i) const {
- return Args[FirstDataArg + i];
-}
-
-void CheckFormatHandler::DoneProcessing() {
- // Does the number of data arguments exceed the number of
- // format conversions in the format string?
- if (HasFormatArguments()) {
- // Find any arguments that weren't covered.
- CoveredArgs.flip();
- signed notCoveredArg = CoveredArgs.find_first();
- if (notCoveredArg >= 0) {
- assert((unsigned)notCoveredArg < NumDataArgs);
- UncoveredArg.Update(notCoveredArg, OrigFormatExpr);
- } else {
- UncoveredArg.setAllCovered();
- }
- }
-}
-
-void UncoveredArgHandler::Diagnose(Sema &S, bool IsFunctionCall,
- const Expr *ArgExpr) {
- assert(hasUncoveredArg() && !DiagnosticExprs.empty() &&
- "Invalid state");
-
- if (!ArgExpr)
- return;
-
- SourceLocation Loc = ArgExpr->getBeginLoc();
-
- if (S.getSourceManager().isInSystemMacro(Loc))
- return;
-
- PartialDiagnostic PDiag = S.PDiag(diag::warn_printf_data_arg_not_used);
- for (auto E : DiagnosticExprs)
- PDiag << E->getSourceRange();
-
- CheckFormatHandler::EmitFormatDiagnostic(
- S, IsFunctionCall, DiagnosticExprs[0],
- PDiag, Loc, /*IsStringLocation*/false,
- DiagnosticExprs[0]->getSourceRange());
-}
-
-bool
-CheckFormatHandler::HandleInvalidConversionSpecifier(unsigned argIndex,
- SourceLocation Loc,
- const char *startSpec,
- unsigned specifierLen,
- const char *csStart,
- unsigned csLen) {
- bool keepGoing = true;
- if (argIndex < NumDataArgs) {
- // Consider the argument coverered, even though the specifier doesn't
- // make sense.
- CoveredArgs.set(argIndex);
- }
- else {
- // If argIndex exceeds the number of data arguments we
- // don't issue a warning because that is just a cascade of warnings (and
- // they may have intended '%%' anyway). We don't want to continue processing
- // the format string after this point, however, as we will like just get
- // gibberish when trying to match arguments.
- keepGoing = false;
- }
-
- StringRef Specifier(csStart, csLen);
-
- // If the specifier in non-printable, it could be the first byte of a UTF-8
- // sequence. In that case, print the UTF-8 code point. If not, print the byte
- // hex value.
- std::string CodePointStr;
- if (!llvm::sys::locale::isPrint(*csStart)) {
- llvm::UTF32 CodePoint;
- const llvm::UTF8 **B = reinterpret_cast<const llvm::UTF8 **>(&csStart);
- const llvm::UTF8 *E =
- reinterpret_cast<const llvm::UTF8 *>(csStart + csLen);
- llvm::ConversionResult Result =
- llvm::convertUTF8Sequence(B, E, &CodePoint, llvm::strictConversion);
-
- if (Result != llvm::conversionOK) {
- unsigned char FirstChar = *csStart;
- CodePoint = (llvm::UTF32)FirstChar;
- }
-
- llvm::raw_string_ostream OS(CodePointStr);
- if (CodePoint < 256)
- OS << "\\x" << llvm::format("%02x", CodePoint);
- else if (CodePoint <= 0xFFFF)
- OS << "\\u" << llvm::format("%04x", CodePoint);
- else
- OS << "\\U" << llvm::format("%08x", CodePoint);
- Specifier = CodePointStr;
- }
-
- EmitFormatDiagnostic(
- S.PDiag(diag::warn_format_invalid_conversion) << Specifier, Loc,
- /*IsStringLocation*/ true, getSpecifierRange(startSpec, specifierLen));
-
- return keepGoing;
-}
-
-void
-CheckFormatHandler::HandlePositionalNonpositionalArgs(SourceLocation Loc,
- const char *startSpec,
- unsigned specifierLen) {
- EmitFormatDiagnostic(
- S.PDiag(diag::warn_format_mix_positional_nonpositional_args),
- Loc, /*isStringLoc*/true, getSpecifierRange(startSpec, specifierLen));
-}
-
-bool
-CheckFormatHandler::CheckNumArgs(
- const analyze_format_string::FormatSpecifier &FS,
- const analyze_format_string::ConversionSpecifier &CS,
- const char *startSpecifier, unsigned specifierLen, unsigned argIndex) {
-
- if (HasFormatArguments() && argIndex >= NumDataArgs) {
- PartialDiagnostic PDiag = FS.usesPositionalArg()
- ? (S.PDiag(diag::warn_printf_positional_arg_exceeds_data_args)
- << (argIndex+1) << NumDataArgs)
- : S.PDiag(diag::warn_printf_insufficient_data_args);
- EmitFormatDiagnostic(
- PDiag, getLocationOfByte(CS.getStart()), /*IsStringLocation*/true,
- getSpecifierRange(startSpecifier, specifierLen));
-
- // Since more arguments than conversion tokens are given, by extension
- // all arguments are covered, so mark this as so.
- UncoveredArg.setAllCovered();
- return false;
- }
- return true;
-}
-
-template<typename Range>
-void CheckFormatHandler::EmitFormatDiagnostic(PartialDiagnostic PDiag,
- SourceLocation Loc,
- bool IsStringLocation,
- Range StringRange,
- ArrayRef<FixItHint> FixIt) {
- EmitFormatDiagnostic(S, inFunctionCall, Args[FormatIdx], PDiag,
- Loc, IsStringLocation, StringRange, FixIt);
-}
-
-/// If the format string is not within the function call, emit a note
-/// so that the function call and string are in diagnostic messages.
-///
-/// \param InFunctionCall if true, the format string is within the function
-/// call and only one diagnostic message will be produced. Otherwise, an
-/// extra note will be emitted pointing to location of the format string.
-///
-/// \param ArgumentExpr the expression that is passed as the format string
-/// argument in the function call. Used for getting locations when two
-/// diagnostics are emitted.
-///
-/// \param PDiag the callee should already have provided any strings for the
-/// diagnostic message. This function only adds locations and fixits
-/// to diagnostics.
-///
-/// \param Loc primary location for diagnostic. If two diagnostics are
-/// required, one will be at Loc and a new SourceLocation will be created for
-/// the other one.
-///
-/// \param IsStringLocation if true, Loc points to the format string should be
-/// used for the note. Otherwise, Loc points to the argument list and will
-/// be used with PDiag.
-///
-/// \param StringRange some or all of the string to highlight. This is
-/// templated so it can accept either a CharSourceRange or a SourceRange.
-///
-/// \param FixIt optional fix it hint for the format string.
-template <typename Range>
-void CheckFormatHandler::EmitFormatDiagnostic(
- Sema &S, bool InFunctionCall, const Expr *ArgumentExpr,
- const PartialDiagnostic &PDiag, SourceLocation Loc, bool IsStringLocation,
- Range StringRange, ArrayRef<FixItHint> FixIt) {
- if (InFunctionCall) {
- const Sema::SemaDiagnosticBuilder &D = S.Diag(Loc, PDiag);
- D << StringRange;
- D << FixIt;
- } else {
- S.Diag(IsStringLocation ? ArgumentExpr->getExprLoc() : Loc, PDiag)
- << ArgumentExpr->getSourceRange();
-
- const Sema::SemaDiagnosticBuilder &Note =
- S.Diag(IsStringLocation ? Loc : StringRange.getBegin(),
- diag::note_format_string_defined);
-
- Note << StringRange;
- Note << FixIt;
- }
-}
-
-//===--- CHECK: Printf format string checking -----------------------------===//
-
-namespace {
-
-class CheckPrintfHandler : public CheckFormatHandler {
-public:
- CheckPrintfHandler(Sema &s, const FormatStringLiteral *fexpr,
- const Expr *origFormatExpr, const FormatStringType type,
- unsigned firstDataArg, unsigned numDataArgs, bool isObjC,
- const char *beg, Sema::FormatArgumentPassingKind APK,
- ArrayRef<const Expr *> Args, unsigned formatIdx,
- bool inFunctionCall, VariadicCallType CallType,
- llvm::SmallBitVector &CheckedVarArgs,
- UncoveredArgHandler &UncoveredArg)
- : CheckFormatHandler(s, fexpr, origFormatExpr, type, firstDataArg,
- numDataArgs, beg, APK, Args, formatIdx,
- inFunctionCall, CallType, CheckedVarArgs,
- UncoveredArg) {}
-
- bool isObjCContext() const { return FSType == FormatStringType::NSString; }
-
- /// Returns true if '%@' specifiers are allowed in the format string.
- bool allowsObjCArg() const {
- return FSType == FormatStringType::NSString ||
- FSType == FormatStringType::OSLog ||
- FSType == FormatStringType::OSTrace;
- }
-
- bool HandleInvalidPrintfConversionSpecifier(
- const analyze_printf::PrintfSpecifier &FS,
- const char *startSpecifier,
- unsigned specifierLen) override;
-
- void handleInvalidMaskType(StringRef MaskType) override;
-
- bool HandlePrintfSpecifier(const analyze_printf::PrintfSpecifier &FS,
- const char *startSpecifier, unsigned specifierLen,
- const TargetInfo &Target) override;
- bool checkFormatExpr(const analyze_printf::PrintfSpecifier &FS,
- const char *StartSpecifier,
- unsigned SpecifierLen,
- const Expr *E);
-
- bool HandleAmount(const analyze_format_string::OptionalAmount &Amt, unsigned k,
- const char *startSpecifier, unsigned specifierLen);
- void HandleInvalidAmount(const analyze_printf::PrintfSpecifier &FS,
- const analyze_printf::OptionalAmount &Amt,
- unsigned type,
- const char *startSpecifier, unsigned specifierLen);
- void HandleFlag(const analyze_printf::PrintfSpecifier &FS,
- const analyze_printf::OptionalFlag &flag,
- const char *startSpecifier, unsigned specifierLen);
- void HandleIgnoredFlag(const analyze_printf::PrintfSpecifier &FS,
- const analyze_printf::OptionalFlag &ignoredFlag,
- const analyze_printf::OptionalFlag &flag,
- const char *startSpecifier, unsigned specifierLen);
- bool checkForCStrMembers(const analyze_printf::ArgType &AT,
- const Expr *E);
-
- void HandleEmptyObjCModifierFlag(const char *startFlag,
- unsigned flagLen) override;
-
- void HandleInvalidObjCModifierFlag(const char *startFlag,
- unsigned flagLen) override;
-
- void
- HandleObjCFlagsWithNonObjCConversion(const char *flagsStart,
- const char *flagsEnd,
- const char *conversionPosition) override;
-};
-
-/// Keeps around the information needed to verify that two specifiers are
-/// compatible.
-class EquatableFormatArgument {
-public:
- enum SpecifierSensitivity : unsigned {
- SS_None,
- SS_Private,
- SS_Public,
- SS_Sensitive
- };
-
- enum FormatArgumentRole : unsigned {
- FAR_Data,
- FAR_FieldWidth,
- FAR_Precision,
- FAR_Auxiliary, // FreeBSD kernel %b and %D
- };
-
-private:
- analyze_format_string::ArgType ArgType;
- analyze_format_string::LengthModifier::Kind LengthMod;
- StringRef SpecifierLetter;
- CharSourceRange Range;
- SourceLocation ElementLoc;
- FormatArgumentRole Role : 2;
- SpecifierSensitivity Sensitivity : 2; // only set for FAR_Data
- unsigned Position : 14;
- unsigned ModifierFor : 14; // not set for FAR_Data
-
- void EmitDiagnostic(Sema &S, PartialDiagnostic PDiag, const Expr *FmtExpr,
- bool InFunctionCall) const;
-
-public:
- EquatableFormatArgument(CharSourceRange Range, SourceLocation ElementLoc,
- analyze_format_string::LengthModifier::Kind LengthMod,
- StringRef SpecifierLetter,
- analyze_format_string::ArgType ArgType,
- FormatArgumentRole Role,
- SpecifierSensitivity Sensitivity, unsigned Position,
- unsigned ModifierFor)
- : ArgType(ArgType), LengthMod(LengthMod),
- SpecifierLetter(SpecifierLetter), Range(Range), ElementLoc(ElementLoc),
- Role(Role), Sensitivity(Sensitivity), Position(Position),
- ModifierFor(ModifierFor) {}
-
- unsigned getPosition() const { return Position; }
- SourceLocation getSourceLocation() const { return ElementLoc; }
- CharSourceRange getSourceRange() const { return Range; }
- analyze_format_string::LengthModifier getLengthModifier() const {
- return analyze_format_string::LengthModifier(nullptr, LengthMod);
- }
- void setModifierFor(unsigned V) { ModifierFor = V; }
-
- std::string buildFormatSpecifier() const {
- std::string result;
- llvm::raw_string_ostream(result)
- << getLengthModifier().toString() << SpecifierLetter;
- return result;
- }
-
- bool VerifyCompatible(Sema &S, const EquatableFormatArgument &Other,
- const Expr *FmtExpr, bool InFunctionCall) const;
-};
-
-/// Turns format strings into lists of EquatableSpecifier objects.
-class DecomposePrintfHandler : public CheckPrintfHandler {
- llvm::SmallVectorImpl<EquatableFormatArgument> &Specs;
- bool HadError;
-
- DecomposePrintfHandler(Sema &s, const FormatStringLiteral *fexpr,
- const Expr *origFormatExpr,
- const FormatStringType type, unsigned firstDataArg,
- unsigned numDataArgs, bool isObjC, const char *beg,
- Sema::FormatArgumentPassingKind APK,
- ArrayRef<const Expr *> Args, unsigned formatIdx,
- bool inFunctionCall, VariadicCallType CallType,
- llvm::SmallBitVector &CheckedVarArgs,
- UncoveredArgHandler &UncoveredArg,
- llvm::SmallVectorImpl<EquatableFormatArgument> &Specs)
- : CheckPrintfHandler(s, fexpr, origFormatExpr, type, firstDataArg,
- numDataArgs, isObjC, beg, APK, Args, formatIdx,
- inFunctionCall, CallType, CheckedVarArgs,
- UncoveredArg),
- Specs(Specs), HadError(false) {}
-
-public:
- static bool
- GetSpecifiers(Sema &S, const FormatStringLiteral *FSL, const Expr *FmtExpr,
- FormatStringType type, bool IsObjC, bool InFunctionCall,
- llvm::SmallVectorImpl<EquatableFormatArgument> &Args);
-
- virtual bool HandlePrintfSpecifier(const analyze_printf::PrintfSpecifier &FS,
- const char *startSpecifier,
- unsigned specifierLen,
- const TargetInfo &Target) override;
-};
-
-} // namespace
-
-bool CheckPrintfHandler::HandleInvalidPrintfConversionSpecifier(
- const analyze_printf::PrintfSpecifier &FS, const char *startSpecifier,
- unsigned specifierLen) {
- const analyze_printf::PrintfConversionSpecifier &CS =
- FS.getConversionSpecifier();
-
- return HandleInvalidConversionSpecifier(FS.getArgIndex(),
- getLocationOfByte(CS.getStart()),
- startSpecifier, specifierLen,
- CS.getStart(), CS.getLength());
-}
-
-void CheckPrintfHandler::handleInvalidMaskType(StringRef MaskType) {
- S.Diag(getLocationOfByte(MaskType.data()), diag::err_invalid_mask_type_size);
-}
-
-// Error out if struct or complex type argments are passed to os_log.
-static bool isInvalidOSLogArgTypeForCodeGen(FormatStringType FSType,
- QualType T) {
- if (FSType != FormatStringType::OSLog)
- return false;
- return T->isRecordType() || T->isComplexType();
-}
-
-bool CheckPrintfHandler::HandleAmount(
- const analyze_format_string::OptionalAmount &Amt, unsigned k,
- const char *startSpecifier, unsigned specifierLen) {
- if (Amt.hasDataArgument()) {
- if (HasFormatArguments()) {
- unsigned argIndex = Amt.getArgIndex();
- if (argIndex >= NumDataArgs) {
- EmitFormatDiagnostic(S.PDiag(diag::warn_printf_asterisk_missing_arg)
- << k,
- getLocationOfByte(Amt.getStart()),
- /*IsStringLocation*/ true,
- getSpecifierRange(startSpecifier, specifierLen));
- // Don't do any more checking. We will just emit
- // spurious errors.
- return false;
- }
-
- // Type check the data argument. It should be an 'int'.
- // Although not in conformance with C99, we also allow the argument to be
- // an 'unsigned int' as that is a reasonably safe case. GCC also
- // doesn't emit a warning for that case.
- CoveredArgs.set(argIndex);
- const Expr *Arg = getDataArg(argIndex);
- if (!Arg)
- return false;
-
- QualType T = Arg->getType();
-
- const analyze_printf::ArgType &AT = Amt.getArgType(S.Context);
- assert(AT.isValid());
-
- if (!AT.matchesType(S.Context, T)) {
- unsigned DiagID = isInvalidOSLogArgTypeForCodeGen(FSType, T)
- ? diag::err_printf_asterisk_wrong_type
- : diag::warn_printf_asterisk_wrong_type;
- EmitFormatDiagnostic(S.PDiag(DiagID)
- << k << AT.getRepresentativeTypeName(S.Context)
- << T << Arg->getSourceRange(),
- getLocationOfByte(Amt.getStart()),
- /*IsStringLocation*/ true,
- getSpecifierRange(startSpecifier, specifierLen));
- // Don't do any more checking. We will just emit
- // spurious errors.
- return false;
- }
- }
- }
- return true;
-}
-
-void CheckPrintfHandler::HandleInvalidAmount(
- const analyze_printf::PrintfSpecifier &FS,
- const analyze_printf::OptionalAmount &Amt,
- unsigned type,
- const char *startSpecifier,
- unsigned specifierLen) {
- const analyze_printf::PrintfConversionSpecifier &CS =
- FS.getConversionSpecifier();
-
- FixItHint fixit =
- Amt.getHowSpecified() == analyze_printf::OptionalAmount::Constant
- ? FixItHint::CreateRemoval(getSpecifierRange(Amt.getStart(),
- Amt.getConstantLength()))
- : FixItHint();
-
- EmitFormatDiagnostic(S.PDiag(diag::warn_printf_nonsensical_optional_amount)
- << type << CS.toString(),
- getLocationOfByte(Amt.getStart()),
- /*IsStringLocation*/true,
- getSpecifierRange(startSpecifier, specifierLen),
- fixit);
-}
-
-void CheckPrintfHandler::HandleFlag(const analyze_printf::PrintfSpecifier &FS,
- const analyze_printf::OptionalFlag &flag,
- const char *startSpecifier,
- unsigned specifierLen) {
- // Warn about pointless flag with a fixit removal.
- const analyze_printf::PrintfConversionSpecifier &CS =
- FS.getConversionSpecifier();
- EmitFormatDiagnostic(S.PDiag(diag::warn_printf_nonsensical_flag)
- << flag.toString() << CS.toString(),
- getLocationOfByte(flag.getPosition()),
- /*IsStringLocation*/true,
- getSpecifierRange(startSpecifier, specifierLen),
- FixItHint::CreateRemoval(
- getSpecifierRange(flag.getPosition(), 1)));
-}
-
-void CheckPrintfHandler::HandleIgnoredFlag(
- const analyze_printf::PrintfSpecifier &FS,
- const analyze_printf::OptionalFlag &ignoredFlag,
- const analyze_printf::OptionalFlag &flag,
- const char *startSpecifier,
- unsigned specifierLen) {
- // Warn about ignored flag with a fixit removal.
- EmitFormatDiagnostic(S.PDiag(diag::warn_printf_ignored_flag)
- << ignoredFlag.toString() << flag.toString(),
- getLocationOfByte(ignoredFlag.getPosition()),
- /*IsStringLocation*/true,
- getSpecifierRange(startSpecifier, specifierLen),
- FixItHint::CreateRemoval(
- getSpecifierRange(ignoredFlag.getPosition(), 1)));
-}
-
-void CheckPrintfHandler::HandleEmptyObjCModifierFlag(const char *startFlag,
- unsigned flagLen) {
- // Warn about an empty flag.
- EmitFormatDiagnostic(S.PDiag(diag::warn_printf_empty_objc_flag),
- getLocationOfByte(startFlag),
- /*IsStringLocation*/true,
- getSpecifierRange(startFlag, flagLen));
-}
-
-void CheckPrintfHandler::HandleInvalidObjCModifierFlag(const char *startFlag,
- unsigned flagLen) {
- // Warn about an invalid flag.
- auto Range = getSpecifierRange(startFlag, flagLen);
- StringRef flag(startFlag, flagLen);
- EmitFormatDiagnostic(S.PDiag(diag::warn_printf_invalid_objc_flag) << flag,
- getLocationOfByte(startFlag),
- /*IsStringLocation*/true,
- Range, FixItHint::CreateRemoval(Range));
-}
-
-void CheckPrintfHandler::HandleObjCFlagsWithNonObjCConversion(
- const char *flagsStart, const char *flagsEnd, const char *conversionPosition) {
- // Warn about using '[...]' without a '@' conversion.
- auto Range = getSpecifierRange(flagsStart, flagsEnd - flagsStart + 1);
- auto diag = diag::warn_printf_ObjCflags_without_ObjCConversion;
- EmitFormatDiagnostic(S.PDiag(diag) << StringRef(conversionPosition, 1),
- getLocationOfByte(conversionPosition),
- /*IsStringLocation*/ true, Range,
- FixItHint::CreateRemoval(Range));
-}
-
-void EquatableFormatArgument::EmitDiagnostic(Sema &S, PartialDiagnostic PDiag,
- const Expr *FmtExpr,
- bool InFunctionCall) const {
- CheckFormatHandler::EmitFormatDiagnostic(S, InFunctionCall, FmtExpr, PDiag,
- ElementLoc, true, Range);
-}
-
-bool EquatableFormatArgument::VerifyCompatible(
- Sema &S, const EquatableFormatArgument &Other, const Expr *FmtExpr,
- bool InFunctionCall) const {
- using MK = analyze_format_string::ArgType::MatchKind;
- if (Role != Other.Role) {
- // diagnose and stop
- EmitDiagnostic(
- S, S.PDiag(diag::warn_format_cmp_role_mismatch) << Role << Other.Role,
- FmtExpr, InFunctionCall);
- S.Diag(Other.ElementLoc, diag::note_format_cmp_with) << 0 << Other.Range;
- return false;
- }
-
- if (Role != FAR_Data) {
- if (ModifierFor != Other.ModifierFor) {
- // diagnose and stop
- EmitDiagnostic(S,
- S.PDiag(diag::warn_format_cmp_modifierfor_mismatch)
- << (ModifierFor + 1) << (Other.ModifierFor + 1),
- FmtExpr, InFunctionCall);
- S.Diag(Other.ElementLoc, diag::note_format_cmp_with) << 0 << Other.Range;
- return false;
- }
- return true;
- }
-
- bool HadError = false;
- if (Sensitivity != Other.Sensitivity) {
- // diagnose and continue
- EmitDiagnostic(S,
- S.PDiag(diag::warn_format_cmp_sensitivity_mismatch)
- << Sensitivity << Other.Sensitivity,
- FmtExpr, InFunctionCall);
- HadError = S.Diag(Other.ElementLoc, diag::note_format_cmp_with)
- << 0 << Other.Range;
- }
-
- switch (ArgType.matchesArgType(S.Context, Other.ArgType)) {
- case MK::Match:
- break;
-
- case MK::MatchPromotion:
- // Per consensus reached at https://discourse.llvm.org/t/-/83076/12,
- // MatchPromotion is treated as a failure by format_matches.
- case MK::NoMatch:
- case MK::NoMatchTypeConfusion:
- case MK::NoMatchPromotionTypeConfusion:
- EmitDiagnostic(S,
- S.PDiag(diag::warn_format_cmp_specifier_mismatch)
- << buildFormatSpecifier()
- << Other.buildFormatSpecifier(),
- FmtExpr, InFunctionCall);
- HadError = S.Diag(Other.ElementLoc, diag::note_format_cmp_with)
- << 0 << Other.Range;
- break;
-
- case MK::NoMatchPedantic:
- EmitDiagnostic(S,
- S.PDiag(diag::warn_format_cmp_specifier_mismatch_pedantic)
- << buildFormatSpecifier()
- << Other.buildFormatSpecifier(),
- FmtExpr, InFunctionCall);
- HadError = S.Diag(Other.ElementLoc, diag::note_format_cmp_with)
- << 0 << Other.Range;
- break;
-
- case MK::NoMatchSignedness:
- EmitDiagnostic(S,
- S.PDiag(diag::warn_format_cmp_specifier_sign_mismatch)
- << buildFormatSpecifier()
- << Other.buildFormatSpecifier(),
- FmtExpr, InFunctionCall);
- HadError = S.Diag(Other.ElementLoc, diag::note_format_cmp_with)
- << 0 << Other.Range;
- break;
- }
- return !HadError;
-}
-
-bool DecomposePrintfHandler::GetSpecifiers(
- Sema &S, const FormatStringLiteral *FSL, const Expr *FmtExpr,
- FormatStringType Type, bool IsObjC, bool InFunctionCall,
- llvm::SmallVectorImpl<EquatableFormatArgument> &Args) {
- StringRef Data = FSL->getString();
- const char *Str = Data.data();
- llvm::SmallBitVector BV;
- UncoveredArgHandler UA;
- const Expr *PrintfArgs[] = {FSL->getFormatString()};
- DecomposePrintfHandler H(S, FSL, FSL->getFormatString(), Type, 0, 0, IsObjC,
- Str, Sema::FAPK_Elsewhere, PrintfArgs, 0,
- InFunctionCall, VariadicCallType::DoesNotApply, BV,
- UA, Args);
-
- if (!analyze_format_string::ParsePrintfString(
- H, Str, Str + Data.size(), S.getLangOpts(), S.Context.getTargetInfo(),
- Type == FormatStringType::FreeBSDKPrintf))
- H.DoneProcessing();
- if (H.HadError)
- return false;
-
- llvm::stable_sort(Args, [](const EquatableFormatArgument &A,
- const EquatableFormatArgument &B) {
- return A.getPosition() < B.getPosition();
- });
- return true;
-}
-
-bool DecomposePrintfHandler::HandlePrintfSpecifier(
- const analyze_printf::PrintfSpecifier &FS, const char *startSpecifier,
- unsigned specifierLen, const TargetInfo &Target) {
- if (!CheckPrintfHandler::HandlePrintfSpecifier(FS, startSpecifier,
- specifierLen, Target)) {
- HadError = true;
- return false;
- }
-
- // Do not add any specifiers to the list for %%. This is possibly incorrect
- // if using a precision/width with a data argument, but that combination is
- // meaningless and we wouldn't know which format to attach the
- // precision/width to.
- const auto &CS = FS.getConversionSpecifier();
- if (CS.getKind() == analyze_format_string::ConversionSpecifier::PercentArg)
- return true;
-
- // have to patch these to have the right ModifierFor if they are used
- const unsigned Unset = ~0;
- unsigned FieldWidthIndex = Unset;
- unsigned PrecisionIndex = Unset;
-
- // field width?
- const auto &FieldWidth = FS.getFieldWidth();
- if (!FieldWidth.isInvalid() && FieldWidth.hasDataArgument()) {
- FieldWidthIndex = Specs.size();
- Specs.emplace_back(getSpecifierRange(startSpecifier, specifierLen),
- getLocationOfByte(FieldWidth.getStart()),
- analyze_format_string::LengthModifier::None, "*",
- FieldWidth.getArgType(S.Context),
- EquatableFormatArgument::FAR_FieldWidth,
- EquatableFormatArgument::SS_None,
- FieldWidth.usesPositionalArg()
- ? FieldWidth.getPositionalArgIndex() - 1
- : FieldWidthIndex,
- 0);
- }
- // precision?
- const auto &Precision = FS.getPrecision();
- if (!Precision.isInvalid() && Precision.hasDataArgument()) {
- PrecisionIndex = Specs.size();
- Specs.emplace_back(
- getSpecifierRange(startSpecifier, specifierLen),
- getLocationOfByte(Precision.getStart()),
- analyze_format_string::LengthModifier::None, ".*",
- Precision.getArgType(S.Context), EquatableFormatArgument::FAR_Precision,
- EquatableFormatArgument::SS_None,
- Precision.usesPositionalArg() ? Precision.getPositionalArgIndex() - 1
- : PrecisionIndex,
- 0);
- }
-
- // this specifier
- unsigned SpecIndex =
- FS.usesPositionalArg() ? FS.getPositionalArgIndex() - 1 : Specs.size();
- if (FieldWidthIndex != Unset)
- Specs[FieldWidthIndex].setModifierFor(SpecIndex);
- if (PrecisionIndex != Unset)
- Specs[PrecisionIndex].setModifierFor(SpecIndex);
-
- EquatableFormatArgument::SpecifierSensitivity Sensitivity;
- if (FS.isPrivate())
- Sensitivity = EquatableFormatArgument::SS_Private;
- else if (FS.isPublic())
- Sensitivity = EquatableFormatArgument::SS_Public;
- else if (FS.isSensitive())
- Sensitivity = EquatableFormatArgument::SS_Sensitive;
- else
- Sensitivity = EquatableFormatArgument::SS_None;
-
- Specs.emplace_back(
- getSpecifierRange(startSpecifier, specifierLen),
- getLocationOfByte(CS.getStart()), FS.getLengthModifier().getKind(),
- CS.getCharacters(), FS.getArgType(S.Context, isObjCContext()),
- EquatableFormatArgument::FAR_Data, Sensitivity, SpecIndex, 0);
-
- // auxiliary argument?
- if (CS.getKind() == analyze_format_string::ConversionSpecifier::FreeBSDbArg ||
- CS.getKind() == analyze_format_string::ConversionSpecifier::FreeBSDDArg) {
- Specs.emplace_back(getSpecifierRange(startSpecifier, specifierLen),
- getLocationOfByte(CS.getStart()),
- analyze_format_string::LengthModifier::None,
- CS.getCharacters(),
- analyze_format_string::ArgType::CStrTy,
- EquatableFormatArgument::FAR_Auxiliary, Sensitivity,
- SpecIndex + 1, SpecIndex);
- }
- return true;
-}
-
-// Determines if the specified is a C++ class or struct containing
-// a member with the specified name and kind (e.g. a CXXMethodDecl named
-// "c_str()").
-template<typename MemberKind>
-static llvm::SmallPtrSet<MemberKind*, 1>
-CXXRecordMembersNamed(StringRef Name, Sema &S, QualType Ty) {
- auto *RD = Ty->getAsCXXRecordDecl();
- llvm::SmallPtrSet<MemberKind*, 1> Results;
-
- if (!RD || !(RD->isBeingDefined() || RD->isCompleteDefinition()))
- return Results;
-
- LookupResult R(S, &S.Context.Idents.get(Name), SourceLocation(),
- Sema::LookupMemberName);
- R.suppressDiagnostics();
-
- // We just need to include all members of the right kind turned up by the
- // filter, at this point.
- if (S.LookupQualifiedName(R, RD))
- for (LookupResult::iterator I = R.begin(), E = R.end(); I != E; ++I) {
- NamedDecl *decl = (*I)->getUnderlyingDecl();
- if (MemberKind *FK = dyn_cast<MemberKind>(decl))
- Results.insert(FK);
- }
- return Results;
-}
-
-/// Check if we could call '.c_str()' on an object.
-///
-/// FIXME: This returns the wrong results in some cases (if cv-qualifiers don't
-/// allow the call, or if it would be ambiguous).
-bool Sema::hasCStrMethod(const Expr *E) {
- using MethodSet = llvm::SmallPtrSet<CXXMethodDecl *, 1>;
-
- MethodSet Results =
- CXXRecordMembersNamed<CXXMethodDecl>("c_str", *this, E->getType());
- for (MethodSet::iterator MI = Results.begin(), ME = Results.end();
- MI != ME; ++MI)
- if ((*MI)->getMinRequiredArguments() == 0)
- return true;
- return false;
-}
-
-// Check if a (w)string was passed when a (w)char* was needed, and offer a
-// better diagnostic if so. AT is assumed to be valid.
-// Returns true when a c_str() conversion method is found.
-bool CheckPrintfHandler::checkForCStrMembers(
- const analyze_printf::ArgType &AT, const Expr *E) {
- using MethodSet = llvm::SmallPtrSet<CXXMethodDecl *, 1>;
-
- MethodSet Results =
- CXXRecordMembersNamed<CXXMethodDecl>("c_str", S, E->getType());
-
- for (MethodSet::iterator MI = Results.begin(), ME = Results.end();
- MI != ME; ++MI) {
- const CXXMethodDecl *Method = *MI;
- if (Method->getMinRequiredArguments() == 0 &&
- AT.matchesType(S.Context, Method->getReturnType())) {
- // FIXME: Suggest parens if the expression needs them.
- SourceLocation EndLoc = S.getLocForEndOfToken(E->getEndLoc());
- S.Diag(E->getBeginLoc(), diag::note_printf_c_str)
- << "c_str()" << FixItHint::CreateInsertion(EndLoc, ".c_str()");
- return true;
- }
- }
-
- return false;
-}
-
-bool CheckPrintfHandler::HandlePrintfSpecifier(
- const analyze_printf::PrintfSpecifier &FS, const char *startSpecifier,
- unsigned specifierLen, const TargetInfo &Target) {
- using namespace analyze_format_string;
- using namespace analyze_printf;
-
- const PrintfConversionSpecifier &CS = FS.getConversionSpecifier();
-
- if (FS.consumesDataArgument()) {
- if (atFirstArg) {
- atFirstArg = false;
- usesPositionalArgs = FS.usesPositionalArg();
- }
- else if (usesPositionalArgs != FS.usesPositionalArg()) {
- HandlePositionalNonpositionalArgs(getLocationOfByte(CS.getStart()),
- startSpecifier, specifierLen);
- return false;
- }
- }
-
- // First check if the field width, precision, and conversion specifier
- // have matching data arguments.
- if (!HandleAmount(FS.getFieldWidth(), /* field width */ 0,
- startSpecifier, specifierLen)) {
- return false;
- }
-
- if (!HandleAmount(FS.getPrecision(), /* precision */ 1,
- startSpecifier, specifierLen)) {
- return false;
- }
-
- if (!CS.consumesDataArgument()) {
- // FIXME: Technically specifying a precision or field width here
- // makes no sense. Worth issuing a warning at some point.
- return true;
- }
-
- // Consume the argument.
- unsigned argIndex = FS.getArgIndex();
- if (argIndex < NumDataArgs) {
- // The check to see if the argIndex is valid will come later.
- // We set the bit here because we may exit early from this
- // function if we encounter some other error.
- CoveredArgs.set(argIndex);
- }
-
- // FreeBSD kernel extensions.
- if (CS.getKind() == ConversionSpecifier::FreeBSDbArg ||
- CS.getKind() == ConversionSpecifier::FreeBSDDArg) {
- // We need at least two arguments.
- if (!CheckNumArgs(FS, CS, startSpecifier, specifierLen, argIndex + 1))
- return false;
-
- if (HasFormatArguments()) {
- // Claim the second argument.
- CoveredArgs.set(argIndex + 1);
-
- // Type check the first argument (int for %b, pointer for %D)
- const Expr *Ex = getDataArg(argIndex);
- const analyze_printf::ArgType &AT =
- (CS.getKind() == ConversionSpecifier::FreeBSDbArg)
- ? ArgType(S.Context.IntTy)
- : ArgType::CPointerTy;
- if (AT.isValid() && !AT.matchesType(S.Context, Ex->getType()))
- EmitFormatDiagnostic(
- S.PDiag(diag::warn_format_conversion_argument_type_mismatch)
- << AT.getRepresentativeTypeName(S.Context) << Ex->getType()
- << false << Ex->getSourceRange(),
- Ex->getBeginLoc(), /*IsStringLocation*/ false,
- getSpecifierRange(startSpecifier, specifierLen));
-
- // Type check the second argument (char * for both %b and %D)
- Ex = getDataArg(argIndex + 1);
- const analyze_printf::ArgType &AT2 = ArgType::CStrTy;
- if (AT2.isValid() && !AT2.matchesType(S.Context, Ex->getType()))
- EmitFormatDiagnostic(
- S.PDiag(diag::warn_format_conversion_argument_type_mismatch)
- << AT2.getRepresentativeTypeName(S.Context) << Ex->getType()
- << false << Ex->getSourceRange(),
- Ex->getBeginLoc(), /*IsStringLocation*/ false,
- getSpecifierRange(startSpecifier, specifierLen));
- }
- return true;
- }
-
- // Check for using an Objective-C specific conversion specifier
- // in a non-ObjC literal.
- if (!allowsObjCArg() && CS.isObjCArg()) {
- return HandleInvalidPrintfConversionSpecifier(FS, startSpecifier,
- specifierLen);
- }
-
- // %P can only be used with os_log.
- if (FSType != FormatStringType::OSLog &&
- CS.getKind() == ConversionSpecifier::PArg) {
- return HandleInvalidPrintfConversionSpecifier(FS, startSpecifier,
- specifierLen);
- }
-
- // %n is not allowed with os_log.
- if (FSType == FormatStringType::OSLog &&
- CS.getKind() == ConversionSpecifier::nArg) {
- EmitFormatDiagnostic(S.PDiag(diag::warn_os_log_format_narg),
- getLocationOfByte(CS.getStart()),
- /*IsStringLocation*/ false,
- getSpecifierRange(startSpecifier, specifierLen));
-
- return true;
- }
-
- // Only scalars are allowed for os_trace.
- if (FSType == FormatStringType::OSTrace &&
- (CS.getKind() == ConversionSpecifier::PArg ||
- CS.getKind() == ConversionSpecifier::sArg ||
- CS.getKind() == ConversionSpecifier::ObjCObjArg)) {
- return HandleInvalidPrintfConversionSpecifier(FS, startSpecifier,
- specifierLen);
- }
-
- // Check for use of public/private annotation outside of os_log().
- if (FSType != FormatStringType::OSLog) {
- if (FS.isPublic().isSet()) {
- EmitFormatDiagnostic(S.PDiag(diag::warn_format_invalid_annotation)
- << "public",
- getLocationOfByte(FS.isPublic().getPosition()),
- /*IsStringLocation*/ false,
- getSpecifierRange(startSpecifier, specifierLen));
- }
- if (FS.isPrivate().isSet()) {
- EmitFormatDiagnostic(S.PDiag(diag::warn_format_invalid_annotation)
- << "private",
- getLocationOfByte(FS.isPrivate().getPosition()),
- /*IsStringLocation*/ false,
- getSpecifierRange(startSpecifier, specifierLen));
- }
- }
-
- const llvm::Triple &Triple = Target.getTriple();
- if (CS.getKind() == ConversionSpecifier::nArg &&
- (Triple.isAndroid() || Triple.isOSFuchsia())) {
- EmitFormatDiagnostic(S.PDiag(diag::warn_printf_narg_not_supported),
- getLocationOfByte(CS.getStart()),
- /*IsStringLocation*/ false,
- getSpecifierRange(startSpecifier, specifierLen));
- }
-
- // Check for invalid use of field width
- if (!FS.hasValidFieldWidth()) {
- HandleInvalidAmount(FS, FS.getFieldWidth(), /* field width */ 0,
- startSpecifier, specifierLen);
- }
-
- // Check for invalid use of precision
- if (!FS.hasValidPrecision()) {
- HandleInvalidAmount(FS, FS.getPrecision(), /* precision */ 1,
- startSpecifier, specifierLen);
- }
-
- // Precision is mandatory for %P specifier.
- if (CS.getKind() == ConversionSpecifier::PArg &&
- FS.getPrecision().getHowSpecified() == OptionalAmount::NotSpecified) {
- EmitFormatDiagnostic(S.PDiag(diag::warn_format_P_no_precision),
- getLocationOfByte(startSpecifier),
- /*IsStringLocation*/ false,
- getSpecifierRange(startSpecifier, specifierLen));
- }
-
- // Check each flag does not conflict with any other component.
- if (!FS.hasValidThousandsGroupingPrefix())
- HandleFlag(FS, FS.hasThousandsGrouping(), startSpecifier, specifierLen);
- if (!FS.hasValidLeadingZeros())
- HandleFlag(FS, FS.hasLeadingZeros(), startSpecifier, specifierLen);
- if (!FS.hasValidPlusPrefix())
- HandleFlag(FS, FS.hasPlusPrefix(), startSpecifier, specifierLen);
- if (!FS.hasValidSpacePrefix())
- HandleFlag(FS, FS.hasSpacePrefix(), startSpecifier, specifierLen);
- if (!FS.hasValidAlternativeForm())
- HandleFlag(FS, FS.hasAlternativeForm(), startSpecifier, specifierLen);
- if (!FS.hasValidLeftJustified())
- HandleFlag(FS, FS.isLeftJustified(), startSpecifier, specifierLen);
-
- // Check that flags are not ignored by another flag
- if (FS.hasSpacePrefix() && FS.hasPlusPrefix()) // ' ' ignored by '+'
- HandleIgnoredFlag(FS, FS.hasSpacePrefix(), FS.hasPlusPrefix(),
- startSpecifier, specifierLen);
- if (FS.hasLeadingZeros() && FS.isLeftJustified()) // '0' ignored by '-'
- HandleIgnoredFlag(FS, FS.hasLeadingZeros(), FS.isLeftJustified(),
- startSpecifier, specifierLen);
-
- // Check the length modifier is valid with the given conversion specifier.
- if (!FS.hasValidLengthModifier(S.getASTContext().getTargetInfo(),
- S.getLangOpts()))
- HandleInvalidLengthModifier(FS, CS, startSpecifier, specifierLen,
- diag::warn_format_nonsensical_length);
- else if (!FS.hasStandardLengthModifier())
- HandleNonStandardLengthModifier(FS, startSpecifier, specifierLen);
- else if (!FS.hasStandardLengthConversionCombination())
- HandleInvalidLengthModifier(FS, CS, startSpecifier, specifierLen,
- diag::warn_format_non_standard_conversion_spec);
-
- if (!FS.hasStandardConversionSpecifier(S.getLangOpts()))
- HandleNonStandardConversionSpecifier(CS, startSpecifier, specifierLen);
-
- // The remaining checks depend on the data arguments.
- if (!HasFormatArguments())
- return true;
-
- if (!CheckNumArgs(FS, CS, startSpecifier, specifierLen, argIndex))
- return false;
-
- const Expr *Arg = getDataArg(argIndex);
- if (!Arg)
- return true;
-
- return checkFormatExpr(FS, startSpecifier, specifierLen, Arg);
-}
-
-static bool requiresParensToAddCast(const Expr *E) {
- // FIXME: We should have a general way to reason about operator
- // precedence and whether parens are actually needed here.
- // Take care of a few common cases where they aren't.
- const Expr *Inside = E->IgnoreImpCasts();
- if (const PseudoObjectExpr *POE = dyn_cast<PseudoObjectExpr>(Inside))
- Inside = POE->getSyntacticForm()->IgnoreImpCasts();
-
- switch (Inside->getStmtClass()) {
- case Stmt::ArraySubscriptExprClass:
- case Stmt::CallExprClass:
- case Stmt::CharacterLiteralClass:
- case Stmt::CXXBoolLiteralExprClass:
- case Stmt::DeclRefExprClass:
- case Stmt::FloatingLiteralClass:
- case Stmt::IntegerLiteralClass:
- case Stmt::MemberExprClass:
- case Stmt::ObjCArrayLiteralClass:
- case Stmt::ObjCBoolLiteralExprClass:
- case Stmt::ObjCBoxedExprClass:
- case Stmt::ObjCDictionaryLiteralClass:
- case Stmt::ObjCEncodeExprClass:
- case Stmt::ObjCIvarRefExprClass:
- case Stmt::ObjCMessageExprClass:
- case Stmt::ObjCPropertyRefExprClass:
- case Stmt::ObjCStringLiteralClass:
- case Stmt::ObjCSubscriptRefExprClass:
- case Stmt::ParenExprClass:
- case Stmt::StringLiteralClass:
- case Stmt::UnaryOperatorClass:
- return false;
- default:
- return true;
- }
-}
-
-static std::pair<QualType, StringRef>
-shouldNotPrintDirectly(const ASTContext &Context,
- QualType IntendedTy,
- const Expr *E) {
- // Use a 'while' to peel off layers of typedefs.
- QualType TyTy = IntendedTy;
- while (const TypedefType *UserTy = TyTy->getAs<TypedefType>()) {
- StringRef Name = UserTy->getDecl()->getName();
- QualType CastTy = llvm::StringSwitch<QualType>(Name)
- .Case("CFIndex", Context.getNSIntegerType())
- .Case("NSInteger", Context.getNSIntegerType())
- .Case("NSUInteger", Context.getNSUIntegerType())
- .Case("SInt32", Context.IntTy)
- .Case("UInt32", Context.UnsignedIntTy)
- .Default(QualType());
-
- if (!CastTy.isNull())
- return std::make_pair(CastTy, Name);
-
- TyTy = UserTy->desugar();
- }
-
- // Strip parens if necessary.
- if (const ParenExpr *PE = dyn_cast<ParenExpr>(E))
- return shouldNotPrintDirectly(Context,
- PE->getSubExpr()->getType(),
- PE->getSubExpr());
-
- // If this is a conditional expression, then its result type is constructed
- // via usual arithmetic conversions and thus there might be no necessary
- // typedef sugar there. Recurse to operands to check for NSInteger &
- // Co. usage condition.
- if (const ConditionalOperator *CO = dyn_cast<ConditionalOperator>(E)) {
- QualType TrueTy, FalseTy;
- StringRef TrueName, FalseName;
-
- std::tie(TrueTy, TrueName) =
- shouldNotPrintDirectly(Context,
- CO->getTrueExpr()->getType(),
- CO->getTrueExpr());
- std::tie(FalseTy, FalseName) =
- shouldNotPrintDirectly(Context,
- CO->getFalseExpr()->getType(),
- CO->getFalseExpr());
-
- if (TrueTy == FalseTy)
- return std::make_pair(TrueTy, TrueName);
- else if (TrueTy.isNull())
- return std::make_pair(FalseTy, FalseName);
- else if (FalseTy.isNull())
- return std::make_pair(TrueTy, TrueName);
- }
-
- return std::make_pair(QualType(), StringRef());
-}
-
-/// Return true if \p ICE is an implicit argument promotion of an arithmetic
-/// type. Bit-field 'promotions' from a higher ranked type to a lower ranked
-/// type do not count.
-static bool
-isArithmeticArgumentPromotion(Sema &S, const ImplicitCastExpr *ICE) {
- QualType From = ICE->getSubExpr()->getType();
- QualType To = ICE->getType();
- // It's an integer promotion if the destination type is the promoted
- // source type.
- if (ICE->getCastKind() == CK_IntegralCast &&
- S.Context.isPromotableIntegerType(From) &&
- S.Context.getPromotedIntegerType(From) == To)
- return true;
- // Look through vector types, since we do default argument promotion for
- // those in OpenCL.
- if (const auto *VecTy = From->getAs<ExtVectorType>())
- From = VecTy->getElementType();
- if (const auto *VecTy = To->getAs<ExtVectorType>())
- To = VecTy->getElementType();
- // It's a floating promotion if the source type is a lower rank.
- return ICE->getCastKind() == CK_FloatingCast &&
- S.Context.getFloatingTypeOrder(From, To) < 0;
-}
-
-static analyze_format_string::ArgType::MatchKind
-handleFormatSignedness(analyze_format_string::ArgType::MatchKind Match,
- DiagnosticsEngine &Diags, SourceLocation Loc) {
- if (Match == analyze_format_string::ArgType::NoMatchSignedness) {
- if (Diags.isIgnored(
- diag::warn_format_conversion_argument_type_mismatch_signedness,
- Loc) ||
- Diags.isIgnored(
- // Arbitrary -Wformat diagnostic to detect -Wno-format:
- diag::warn_format_conversion_argument_type_mismatch, Loc)) {
- return analyze_format_string::ArgType::Match;
- }
- }
- return Match;
-}
-
-bool
-CheckPrintfHandler::checkFormatExpr(const analyze_printf::PrintfSpecifier &FS,
- const char *StartSpecifier,
- unsigned SpecifierLen,
- const Expr *E) {
- using namespace analyze_format_string;
- using namespace analyze_printf;
-
- // Now type check the data expression that matches the
- // format specifier.
- const analyze_printf::ArgType &AT = FS.getArgType(S.Context, isObjCContext());
- if (!AT.isValid())
- return true;
-
- QualType ExprTy = E->getType();
- while (const TypeOfExprType *TET = dyn_cast<TypeOfExprType>(ExprTy)) {
- ExprTy = TET->getUnderlyingExpr()->getType();
- }
-
- // When using the format attribute in C++, you can receive a function or an
- // array that will necessarily decay to a pointer when passed to the final
- // format consumer. Apply decay before type comparison.
- if (ExprTy->canDecayToPointerType())
- ExprTy = S.Context.getDecayedType(ExprTy);
-
- // Diagnose attempts to print a boolean value as a character. Unlike other
- // -Wformat diagnostics, this is fine from a type perspective, but it still
- // doesn't make sense.
- if (FS.getConversionSpecifier().getKind() == ConversionSpecifier::cArg &&
- E->isKnownToHaveBooleanValue()) {
- const CharSourceRange &CSR =
- getSpecifierRange(StartSpecifier, SpecifierLen);
- SmallString<4> FSString;
- llvm::raw_svector_ostream os(FSString);
- FS.toString(os);
- EmitFormatDiagnostic(S.PDiag(diag::warn_format_bool_as_character)
- << FSString,
- E->getExprLoc(), false, CSR);
- return true;
- }
-
- // Diagnose attempts to use '%P' with ObjC object types, which will result in
- // dumping raw class data (like is-a pointer), not actual data.
- if (FS.getConversionSpecifier().getKind() == ConversionSpecifier::PArg &&
- ExprTy->isObjCObjectPointerType()) {
- const CharSourceRange &CSR =
- getSpecifierRange(StartSpecifier, SpecifierLen);
- EmitFormatDiagnostic(S.PDiag(diag::warn_format_P_with_objc_pointer),
- E->getExprLoc(), false, CSR);
- return true;
- }
-
- ArgType::MatchKind ImplicitMatch = ArgType::NoMatch;
- ArgType::MatchKind Match = AT.matchesType(S.Context, ExprTy);
- ArgType::MatchKind OrigMatch = Match;
-
- Match = handleFormatSignedness(Match, S.getDiagnostics(), E->getExprLoc());
- if (Match == ArgType::Match)
- return true;
-
- // NoMatchPromotionTypeConfusion should be only returned in ImplictCastExpr
- assert(Match != ArgType::NoMatchPromotionTypeConfusion);
-
- // Look through argument promotions for our error message's reported type.
- // This includes the integral and floating promotions, but excludes array
- // and function pointer decay (seeing that an argument intended to be a
- // string has type 'char [6]' is probably more confusing than 'char *') and
- // certain bitfield promotions (bitfields can be 'demoted' to a lesser type).
- if (const ImplicitCastExpr *ICE = dyn_cast<ImplicitCastExpr>(E)) {
- if (isArithmeticArgumentPromotion(S, ICE)) {
- E = ICE->getSubExpr();
- ExprTy = E->getType();
-
- // Check if we didn't match because of an implicit cast from a 'char'
- // or 'short' to an 'int'. This is done because printf is a varargs
- // function.
- if (ICE->getType() == S.Context.IntTy ||
- ICE->getType() == S.Context.UnsignedIntTy) {
- // All further checking is done on the subexpression
- ImplicitMatch = AT.matchesType(S.Context, ExprTy);
- if (OrigMatch == ArgType::NoMatchSignedness &&
- ImplicitMatch != ArgType::NoMatchSignedness)
- // If the original match was a signedness match this match on the
- // implicit cast type also need to be signedness match otherwise we
- // might introduce new unexpected warnings from -Wformat-signedness.
- return true;
- ImplicitMatch = handleFormatSignedness(
- ImplicitMatch, S.getDiagnostics(), E->getExprLoc());
- if (ImplicitMatch == ArgType::Match)
- return true;
- }
- }
- } else if (const CharacterLiteral *CL = dyn_cast<CharacterLiteral>(E)) {
- // Special case for 'a', which has type 'int' in C.
- // Note, however, that we do /not/ want to treat multibyte constants like
- // 'MooV' as characters! This form is deprecated but still exists. In
- // addition, don't treat expressions as of type 'char' if one byte length
- // modifier is provided.
- if (ExprTy == S.Context.IntTy &&
- FS.getLengthModifier().getKind() != LengthModifier::AsChar)
- if (llvm::isUIntN(S.Context.getCharWidth(), CL->getValue())) {
- ExprTy = S.Context.CharTy;
- // To improve check results, we consider a character literal in C
- // to be a 'char' rather than an 'int'. 'printf("%hd", 'a');' is
- // more likely a type confusion situation, so we will suggest to
- // use '%hhd' instead by discarding the MatchPromotion.
- if (Match == ArgType::MatchPromotion)
- Match = ArgType::NoMatch;
- }
- }
- if (Match == ArgType::MatchPromotion) {
- // WG14 N2562 only clarified promotions in *printf
- // For NSLog in ObjC, just preserve -Wformat behavior
- if (!S.getLangOpts().ObjC &&
- ImplicitMatch != ArgType::NoMatchPromotionTypeConfusion &&
- ImplicitMatch != ArgType::NoMatchTypeConfusion)
- return true;
- Match = ArgType::NoMatch;
- }
- if (ImplicitMatch == ArgType::NoMatchPedantic ||
- ImplicitMatch == ArgType::NoMatchTypeConfusion)
- Match = ImplicitMatch;
- assert(Match != ArgType::MatchPromotion);
-
- // Look through unscoped enums to their underlying type.
- bool IsEnum = false;
- bool IsScopedEnum = false;
- QualType IntendedTy = ExprTy;
- if (const auto *ED = ExprTy->getAsEnumDecl()) {
- IntendedTy = ED->getIntegerType();
- if (!ED->isScoped()) {
- ExprTy = IntendedTy;
- // This controls whether we're talking about the underlying type or not,
- // which we only want to do when it's an unscoped enum.
- IsEnum = true;
- } else {
- IsScopedEnum = true;
- }
- }
-
- // %C in an Objective-C context prints a unichar, not a wchar_t.
- // If the argument is an integer of some kind, believe the %C and suggest
- // a cast instead of changing the conversion specifier.
- if (isObjCContext() &&
- FS.getConversionSpecifier().getKind() == ConversionSpecifier::CArg) {
- if (ExprTy->isIntegralOrUnscopedEnumerationType() &&
- !ExprTy->isCharType()) {
- // 'unichar' is defined as a typedef of unsigned short, but we should
- // prefer using the typedef if it is visible.
- IntendedTy = S.Context.UnsignedShortTy;
-
- // While we are here, check if the value is an IntegerLiteral that happens
- // to be within the valid range.
- if (const IntegerLiteral *IL = dyn_cast<IntegerLiteral>(E)) {
- const llvm::APInt &V = IL->getValue();
- if (V.getActiveBits() <= S.Context.getTypeSize(IntendedTy))
- return true;
- }
-
- LookupResult Result(S, &S.Context.Idents.get("unichar"), E->getBeginLoc(),
- Sema::LookupOrdinaryName);
- if (S.LookupName(Result, S.getCurScope())) {
- NamedDecl *ND = Result.getFoundDecl();
- if (TypedefNameDecl *TD = dyn_cast<TypedefNameDecl>(ND))
- if (TD->getUnderlyingType() == IntendedTy)
- IntendedTy =
- S.Context.getTypedefType(ElaboratedTypeKeyword::None,
- /*Qualifier=*/std::nullopt, TD);
- }
- }
- }
-
- // Special-case some of Darwin's platform-independence types by suggesting
- // casts to primitive types that are known to be large enough.
- bool ShouldNotPrintDirectly = false; StringRef CastTyName;
- if (S.Context.getTargetInfo().getTriple().isOSDarwin()) {
- QualType CastTy;
- std::tie(CastTy, CastTyName) = shouldNotPrintDirectly(S.Context, IntendedTy, E);
- if (!CastTy.isNull()) {
- // %zi/%zu and %td/%tu are OK to use for NSInteger/NSUInteger of type int
- // (long in ASTContext). Only complain to pedants or when they're the
- // underlying type of a scoped enum (which always needs a cast).
- if (!IsScopedEnum &&
- (CastTyName == "NSInteger" || CastTyName == "NSUInteger") &&
- (AT.isSizeT() || AT.isPtrdiffT()) &&
- AT.matchesType(S.Context, CastTy))
- Match = ArgType::NoMatchPedantic;
- IntendedTy = CastTy;
- ShouldNotPrintDirectly = true;
- }
- }
-
- // We may be able to offer a FixItHint if it is a supported type.
- PrintfSpecifier fixedFS = FS;
- bool Success =
- fixedFS.fixType(IntendedTy, S.getLangOpts(), S.Context, isObjCContext());
-
- if (Success) {
- // Get the fix string from the fixed format specifier
- SmallString<16> buf;
- llvm::raw_svector_ostream os(buf);
- fixedFS.toString(os);
-
- CharSourceRange SpecRange = getSpecifierRange(StartSpecifier, SpecifierLen);
-
- if (IntendedTy == ExprTy && !ShouldNotPrintDirectly && !IsScopedEnum) {
- unsigned Diag;
- switch (Match) {
- case ArgType::Match:
- case ArgType::MatchPromotion:
- case ArgType::NoMatchPromotionTypeConfusion:
- llvm_unreachable("expected non-matching");
- case ArgType::NoMatchSignedness:
- Diag = diag::warn_format_conversion_argument_type_mismatch_signedness;
- break;
- case ArgType::NoMatchPedantic:
- Diag = diag::warn_format_conversion_argument_type_mismatch_pedantic;
- break;
- case ArgType::NoMatchTypeConfusion:
- Diag = diag::warn_format_conversion_argument_type_mismatch_confusion;
- break;
- case ArgType::NoMatch:
- Diag = diag::warn_format_conversion_argument_type_mismatch;
- break;
- }
-
- // In this case, the specifier is wrong and should be changed to match
- // the argument.
- EmitFormatDiagnostic(S.PDiag(Diag)
- << AT.getRepresentativeTypeName(S.Context)
- << IntendedTy << IsEnum << E->getSourceRange(),
- E->getBeginLoc(),
- /*IsStringLocation*/ false, SpecRange,
- FixItHint::CreateReplacement(SpecRange, os.str()));
- } else {
- // The canonical type for formatting this value is different from the
- // actual type of the expression. (This occurs, for example, with Darwin's
- // NSInteger on 32-bit platforms, where it is typedef'd as 'int', but
- // should be printed as 'long' for 64-bit compatibility.)
- // Rather than emitting a normal format/argument mismatch, we want to
- // add a cast to the recommended type (and correct the format string
- // if necessary). We should also do so for scoped enumerations.
- SmallString<16> CastBuf;
- llvm::raw_svector_ostream CastFix(CastBuf);
- CastFix << (S.LangOpts.CPlusPlus ? "static_cast<" : "(");
- IntendedTy.print(CastFix, S.Context.getPrintingPolicy());
- CastFix << (S.LangOpts.CPlusPlus ? ">" : ")");
-
- SmallVector<FixItHint,4> Hints;
- ArgType::MatchKind IntendedMatch = AT.matchesType(S.Context, IntendedTy);
- IntendedMatch = handleFormatSignedness(IntendedMatch, S.getDiagnostics(),
- E->getExprLoc());
- if ((IntendedMatch != ArgType::Match) || ShouldNotPrintDirectly)
- Hints.push_back(FixItHint::CreateReplacement(SpecRange, os.str()));
-
- if (const CStyleCastExpr *CCast = dyn_cast<CStyleCastExpr>(E)) {
- // If there's already a cast present, just replace it.
- SourceRange CastRange(CCast->getLParenLoc(), CCast->getRParenLoc());
- Hints.push_back(FixItHint::CreateReplacement(CastRange, CastFix.str()));
-
- } else if (!requiresParensToAddCast(E) && !S.LangOpts.CPlusPlus) {
- // If the expression has high enough precedence,
- // just write the C-style cast.
- Hints.push_back(
- FixItHint::CreateInsertion(E->getBeginLoc(), CastFix.str()));
- } else {
- // Otherwise, add parens around the expression as well as the cast.
- CastFix << "(";
- Hints.push_back(
- FixItHint::CreateInsertion(E->getBeginLoc(), CastFix.str()));
-
- // We don't use getLocForEndOfToken because it returns invalid source
- // locations for macro expansions (by design).
- SourceLocation EndLoc = S.SourceMgr.getSpellingLoc(E->getEndLoc());
- SourceLocation After = EndLoc.getLocWithOffset(
- Lexer::MeasureTokenLength(EndLoc, S.SourceMgr, S.LangOpts));
- Hints.push_back(FixItHint::CreateInsertion(After, ")"));
- }
-
- if (ShouldNotPrintDirectly && !IsScopedEnum) {
- // The expression has a type that should not be printed directly.
- // We extract the name from the typedef because we don't want to show
- // the underlying type in the diagnostic.
- StringRef Name;
- if (const auto *TypedefTy = ExprTy->getAs<TypedefType>())
- Name = TypedefTy->getDecl()->getName();
- else
- Name = CastTyName;
- unsigned Diag = Match == ArgType::NoMatchPedantic
- ? diag::warn_format_argument_needs_cast_pedantic
- : diag::warn_format_argument_needs_cast;
- EmitFormatDiagnostic(S.PDiag(Diag) << Name << IntendedTy << IsEnum
- << E->getSourceRange(),
- E->getBeginLoc(), /*IsStringLocation=*/false,
- SpecRange, Hints);
- } else {
- // In this case, the expression could be printed using a different
- // specifier, but we've decided that the specifier is probably correct
- // and we should cast instead. Just use the normal warning message.
-
- unsigned Diag =
- IsScopedEnum
- ? diag::warn_format_conversion_argument_type_mismatch_pedantic
- : diag::warn_format_conversion_argument_type_mismatch;
-
- EmitFormatDiagnostic(
- S.PDiag(Diag) << AT.getRepresentativeTypeName(S.Context) << ExprTy
- << IsEnum << E->getSourceRange(),
- E->getBeginLoc(), /*IsStringLocation*/ false, SpecRange, Hints);
- }
- }
- } else {
- const CharSourceRange &CSR = getSpecifierRange(StartSpecifier,
- SpecifierLen);
- // Since the warning for passing non-POD types to variadic functions
- // was deferred until now, we emit a warning for non-POD
- // arguments here.
- bool EmitTypeMismatch = false;
- switch (S.isValidVarArgType(ExprTy)) {
- case VarArgKind::Valid:
- case VarArgKind::ValidInCXX11: {
- unsigned Diag;
- switch (Match) {
- case ArgType::Match:
- case ArgType::MatchPromotion:
- case ArgType::NoMatchPromotionTypeConfusion:
- llvm_unreachable("expected non-matching");
- case ArgType::NoMatchSignedness:
- Diag = diag::warn_format_conversion_argument_type_mismatch_signedness;
- break;
- case ArgType::NoMatchPedantic:
- Diag = diag::warn_format_conversion_argument_type_mismatch_pedantic;
- break;
- case ArgType::NoMatchTypeConfusion:
- Diag = diag::warn_format_conversion_argument_type_mismatch_confusion;
- break;
- case ArgType::NoMatch:
- Diag = isInvalidOSLogArgTypeForCodeGen(FSType, ExprTy)
- ? diag::err_format_conversion_argument_type_mismatch
- : diag::warn_format_conversion_argument_type_mismatch;
- break;
- }
-
- EmitFormatDiagnostic(
- S.PDiag(Diag) << AT.getRepresentativeTypeName(S.Context) << ExprTy
- << IsEnum << CSR << E->getSourceRange(),
- E->getBeginLoc(), /*IsStringLocation*/ false, CSR);
- break;
- }
- case VarArgKind::Undefined:
- case VarArgKind::MSVCUndefined:
- if (CallType == VariadicCallType::DoesNotApply) {
- EmitTypeMismatch = true;
- } else {
- EmitFormatDiagnostic(
- S.PDiag(diag::warn_non_pod_vararg_with_format_string)
- << S.getLangOpts().CPlusPlus11 << ExprTy << CallType
- << AT.getRepresentativeTypeName(S.Context) << CSR
- << E->getSourceRange(),
- E->getBeginLoc(), /*IsStringLocation*/ false, CSR);
- checkForCStrMembers(AT, E);
- }
- break;
-
- case VarArgKind::Invalid:
- if (CallType == VariadicCallType::DoesNotApply)
- EmitTypeMismatch = true;
- else if (ExprTy->isObjCObjectType())
- EmitFormatDiagnostic(
- S.PDiag(diag::err_cannot_pass_objc_interface_to_vararg_format)
- << S.getLangOpts().CPlusPlus11 << ExprTy << CallType
- << AT.getRepresentativeTypeName(S.Context) << CSR
- << E->getSourceRange(),
- E->getBeginLoc(), /*IsStringLocation*/ false, CSR);
- else
- // FIXME: If this is an initializer list, suggest removing the braces
- // or inserting a cast to the target type.
- S.Diag(E->getBeginLoc(), diag::err_cannot_pass_to_vararg_format)
- << isa<InitListExpr>(E) << ExprTy << CallType
- << AT.getRepresentativeTypeName(S.Context) << E->getSourceRange();
- break;
- }
-
- if (EmitTypeMismatch) {
- // The function is not variadic, so we do not generate warnings about
- // being allowed to pass that object as a variadic argument. Instead,
- // since there are inherently no printf specifiers for types which cannot
- // be passed as variadic arguments, emit a plain old specifier mismatch
- // argument.
- EmitFormatDiagnostic(
- S.PDiag(diag::warn_format_conversion_argument_type_mismatch)
- << AT.getRepresentativeTypeName(S.Context) << ExprTy << false
- << E->getSourceRange(),
- E->getBeginLoc(), false, CSR);
- }
-
- assert(FirstDataArg + FS.getArgIndex() < CheckedVarArgs.size() &&
- "format string specifier index out of range");
- CheckedVarArgs[FirstDataArg + FS.getArgIndex()] = true;
- }
-
- return true;
-}
-
-//===--- CHECK: Scanf format string checking ------------------------------===//
-
-namespace {
-
-class CheckScanfHandler : public CheckFormatHandler {
-public:
- CheckScanfHandler(Sema &s, const FormatStringLiteral *fexpr,
- const Expr *origFormatExpr, FormatStringType type,
- unsigned firstDataArg, unsigned numDataArgs,
- const char *beg, Sema::FormatArgumentPassingKind APK,
- ArrayRef<const Expr *> Args, unsigned formatIdx,
- bool inFunctionCall, VariadicCallType CallType,
- llvm::SmallBitVector &CheckedVarArgs,
- UncoveredArgHandler &UncoveredArg)
- : CheckFormatHandler(s, fexpr, origFormatExpr, type, firstDataArg,
- numDataArgs, beg, APK, Args, formatIdx,
- inFunctionCall, CallType, CheckedVarArgs,
- UncoveredArg) {}
-
- bool HandleScanfSpecifier(const analyze_scanf::ScanfSpecifier &FS,
- const char *startSpecifier,
- unsigned specifierLen) override;
-
- bool HandleInvalidScanfConversionSpecifier(
- const analyze_scanf::ScanfSpecifier &FS,
- const char *startSpecifier,
- unsigned specifierLen) override;
-
- void HandleIncompleteScanList(const char *start, const char *end) override;
-};
-
-} // namespace
-
-void CheckScanfHandler::HandleIncompleteScanList(const char *start,
- const char *end) {
- EmitFormatDiagnostic(S.PDiag(diag::warn_scanf_scanlist_incomplete),
- getLocationOfByte(end), /*IsStringLocation*/true,
- getSpecifierRange(start, end - start));
-}
-
-bool CheckScanfHandler::HandleInvalidScanfConversionSpecifier(
- const analyze_scanf::ScanfSpecifier &FS,
- const char *startSpecifier,
- unsigned specifierLen) {
- const analyze_scanf::ScanfConversionSpecifier &CS =
- FS.getConversionSpecifier();
-
- return HandleInvalidConversionSpecifier(FS.getArgIndex(),
- getLocationOfByte(CS.getStart()),
- startSpecifier, specifierLen,
- CS.getStart(), CS.getLength());
-}
-
-bool CheckScanfHandler::HandleScanfSpecifier(
- const analyze_scanf::ScanfSpecifier &FS,
- const char *startSpecifier,
- unsigned specifierLen) {
- using namespace analyze_scanf;
- using namespace analyze_format_string;
-
- const ScanfConversionSpecifier &CS = FS.getConversionSpecifier();
-
- // Handle case where '%' and '*' don't consume an argument. These shouldn't
- // be used to decide if we are using positional arguments consistently.
- if (FS.consumesDataArgument()) {
- if (atFirstArg) {
- atFirstArg = false;
- usesPositionalArgs = FS.usesPositionalArg();
- }
- else if (usesPositionalArgs != FS.usesPositionalArg()) {
- HandlePositionalNonpositionalArgs(getLocationOfByte(CS.getStart()),
- startSpecifier, specifierLen);
- return false;
- }
- }
-
- // Check if the field with is non-zero.
- const OptionalAmount &Amt = FS.getFieldWidth();
- if (Amt.getHowSpecified() == OptionalAmount::Constant) {
- if (Amt.getConstantAmount() == 0) {
- const CharSourceRange &R = getSpecifierRange(Amt.getStart(),
- Amt.getConstantLength());
- EmitFormatDiagnostic(S.PDiag(diag::warn_scanf_nonzero_width),
- getLocationOfByte(Amt.getStart()),
- /*IsStringLocation*/true, R,
- FixItHint::CreateRemoval(R));
- }
- }
-
- if (!FS.consumesDataArgument()) {
- // FIXME: Technically specifying a precision or field width here
- // makes no sense. Worth issuing a warning at some point.
- return true;
- }
-
- // Consume the argument.
- unsigned argIndex = FS.getArgIndex();
- if (argIndex < NumDataArgs) {
- // The check to see if the argIndex is valid will come later.
- // We set the bit here because we may exit early from this
- // function if we encounter some other error.
- CoveredArgs.set(argIndex);
- }
-
- // Check the length modifier is valid with the given conversion specifier.
- if (!FS.hasValidLengthModifier(S.getASTContext().getTargetInfo(),
- S.getLangOpts()))
- HandleInvalidLengthModifier(FS, CS, startSpecifier, specifierLen,
- diag::warn_format_nonsensical_length);
- else if (!FS.hasStandardLengthModifier())
- HandleNonStandardLengthModifier(FS, startSpecifier, specifierLen);
- else if (!FS.hasStandardLengthConversionCombination())
- HandleInvalidLengthModifier(FS, CS, startSpecifier, specifierLen,
- diag::warn_format_non_standard_conversion_spec);
-
- if (!FS.hasStandardConversionSpecifier(S.getLangOpts()))
- HandleNonStandardConversionSpecifier(CS, startSpecifier, specifierLen);
-
- // The remaining checks depend on the data arguments.
- if (!HasFormatArguments())
- return true;
-
- if (!CheckNumArgs(FS, CS, startSpecifier, specifierLen, argIndex))
- return false;
-
- // Check that the argument type matches the format specifier.
- const Expr *Ex = getDataArg(argIndex);
- if (!Ex)
- return true;
-
- const analyze_format_string::ArgType &AT = FS.getArgType(S.Context);
-
- if (!AT.isValid()) {
- return true;
- }
-
- analyze_format_string::ArgType::MatchKind Match =
- AT.matchesType(S.Context, Ex->getType());
- Match = handleFormatSignedness(Match, S.getDiagnostics(), Ex->getExprLoc());
- if (Match == analyze_format_string::ArgType::Match)
- return true;
- bool Pedantic = Match == analyze_format_string::ArgType::NoMatchPedantic;
- bool Signedness = Match == analyze_format_string::ArgType::NoMatchSignedness;
-
- ScanfSpecifier fixedFS = FS;
- bool Success = fixedFS.fixType(Ex->getType(), Ex->IgnoreImpCasts()->getType(),
- S.getLangOpts(), S.Context);
-
- unsigned Diag =
- Pedantic ? diag::warn_format_conversion_argument_type_mismatch_pedantic
- : Signedness
- ? diag::warn_format_conversion_argument_type_mismatch_signedness
- : diag::warn_format_conversion_argument_type_mismatch;
-
- if (Success) {
- // Get the fix string from the fixed format specifier.
- SmallString<128> buf;
- llvm::raw_svector_ostream os(buf);
- fixedFS.toString(os);
-
- EmitFormatDiagnostic(
- S.PDiag(Diag) << AT.getRepresentativeTypeName(S.Context)
- << Ex->getType() << false << Ex->getSourceRange(),
- Ex->getBeginLoc(),
- /*IsStringLocation*/ false,
- getSpecifierRange(startSpecifier, specifierLen),
- FixItHint::CreateReplacement(
- getSpecifierRange(startSpecifier, specifierLen), os.str()));
- } else {
- EmitFormatDiagnostic(S.PDiag(Diag)
- << AT.getRepresentativeTypeName(S.Context)
- << Ex->getType() << false << Ex->getSourceRange(),
- Ex->getBeginLoc(),
- /*IsStringLocation*/ false,
- getSpecifierRange(startSpecifier, specifierLen));
- }
-
- return true;
-}
-
-static bool CompareFormatSpecifiers(Sema &S, const StringLiteral *Ref,
- ArrayRef<EquatableFormatArgument> RefArgs,
- const StringLiteral *Fmt,
- ArrayRef<EquatableFormatArgument> FmtArgs,
- const Expr *FmtExpr, bool InFunctionCall) {
- bool HadError = false;
- auto FmtIter = FmtArgs.begin(), FmtEnd = FmtArgs.end();
- auto RefIter = RefArgs.begin(), RefEnd = RefArgs.end();
- while (FmtIter < FmtEnd && RefIter < RefEnd) {
- // In positional-style format strings, the same specifier can appear
- // multiple times (like %2$i %2$d). Specifiers in both RefArgs and FmtArgs
- // are sorted by getPosition(), and we process each range of equal
- // getPosition() values as one group.
- // RefArgs are taken from a string literal that was given to
- // attribute(format_matches), and if we got this far, we have already
- // verified that if it has positional specifiers that appear in multiple
- // locations, then they are all mutually compatible. What's left for us to
- // do is verify that all specifiers with the same position in FmtArgs are
- // compatible with the RefArgs specifiers. We check each specifier from
- // FmtArgs against the first member of the RefArgs group.
- for (; FmtIter < FmtEnd; ++FmtIter) {
- // Clang does not diagnose missing format specifiers in positional-style
- // strings (TODO: which it probably should do, as it is UB to skip over a
- // format argument). Skip specifiers if needed.
- if (FmtIter->getPosition() < RefIter->getPosition())
- continue;
-
- // Delimits a new getPosition() value.
- if (FmtIter->getPosition() > RefIter->getPosition())
- break;
-
- HadError |=
- !FmtIter->VerifyCompatible(S, *RefIter, FmtExpr, InFunctionCall);
- }
-
- // Jump RefIter to the start of the next group.
- RefIter = std::find_if(RefIter + 1, RefEnd, [=](const auto &Arg) {
- return Arg.getPosition() != RefIter->getPosition();
- });
- }
-
- if (FmtIter < FmtEnd) {
- CheckFormatHandler::EmitFormatDiagnostic(
- S, InFunctionCall, FmtExpr,
- S.PDiag(diag::warn_format_cmp_specifier_arity) << 1,
- FmtExpr->getBeginLoc(), false, FmtIter->getSourceRange());
- HadError = S.Diag(Ref->getBeginLoc(), diag::note_format_cmp_with) << 1;
- } else if (RefIter < RefEnd) {
- CheckFormatHandler::EmitFormatDiagnostic(
- S, InFunctionCall, FmtExpr,
- S.PDiag(diag::warn_format_cmp_specifier_arity) << 0,
- FmtExpr->getBeginLoc(), false, Fmt->getSourceRange());
- HadError = S.Diag(Ref->getBeginLoc(), diag::note_format_cmp_with)
- << 1 << RefIter->getSourceRange();
- }
- return !HadError;
-}
-
-static void CheckFormatString(
- Sema &S, const FormatStringLiteral *FExpr,
- const StringLiteral *ReferenceFormatString, const Expr *OrigFormatExpr,
- ArrayRef<const Expr *> Args, Sema::FormatArgumentPassingKind APK,
- unsigned format_idx, unsigned firstDataArg, FormatStringType Type,
- bool inFunctionCall, VariadicCallType CallType,
- llvm::SmallBitVector &CheckedVarArgs, UncoveredArgHandler &UncoveredArg,
- bool IgnoreStringsWithoutSpecifiers) {
- // CHECK: is the format string a wide literal?
- if (!FExpr->isAscii() && !FExpr->isUTF8()) {
- CheckFormatHandler::EmitFormatDiagnostic(
- S, inFunctionCall, Args[format_idx],
- S.PDiag(diag::warn_format_string_is_wide_literal), FExpr->getBeginLoc(),
- /*IsStringLocation*/ true, OrigFormatExpr->getSourceRange());
- return;
- }
-
- // Str - The format string. NOTE: this is NOT null-terminated!
- StringRef StrRef = FExpr->getString();
- const char *Str = StrRef.data();
- // Account for cases where the string literal is truncated in a declaration.
- const ConstantArrayType *T =
- S.Context.getAsConstantArrayType(FExpr->getType());
- assert(T && "String literal not of constant array type!");
- size_t TypeSize = T->getZExtSize();
- size_t StrLen = std::min(std::max(TypeSize, size_t(1)) - 1, StrRef.size());
- const unsigned numDataArgs = Args.size() - firstDataArg;
-
- if (IgnoreStringsWithoutSpecifiers &&
- !analyze_format_string::parseFormatStringHasFormattingSpecifiers(
- Str, Str + StrLen, S.getLangOpts(), S.Context.getTargetInfo()))
- return;
-
- // Emit a warning if the string literal is truncated and does not contain an
- // embedded null character.
- if (TypeSize <= StrRef.size() && !StrRef.substr(0, TypeSize).contains('\0')) {
- CheckFormatHandler::EmitFormatDiagnostic(
- S, inFunctionCall, Args[format_idx],
- S.PDiag(diag::warn_printf_format_string_not_null_terminated),
- FExpr->getBeginLoc(),
- /*IsStringLocation=*/true, OrigFormatExpr->getSourceRange());
- return;
- }
-
- // CHECK: empty format string?
- if (StrLen == 0 && numDataArgs > 0) {
- CheckFormatHandler::EmitFormatDiagnostic(
- S, inFunctionCall, Args[format_idx],
- S.PDiag(diag::warn_empty_format_string), FExpr->getBeginLoc(),
- /*IsStringLocation*/ true, OrigFormatExpr->getSourceRange());
- return;
- }
-
- if (Type == FormatStringType::Printf || Type == FormatStringType::NSString ||
- Type == FormatStringType::Kprintf ||
- Type == FormatStringType::FreeBSDKPrintf ||
- Type == FormatStringType::OSLog || Type == FormatStringType::OSTrace) {
- bool IsObjC =
- Type == FormatStringType::NSString || Type == FormatStringType::OSTrace;
- if (ReferenceFormatString == nullptr) {
- CheckPrintfHandler H(S, FExpr, OrigFormatExpr, Type, firstDataArg,
- numDataArgs, IsObjC, Str, APK, Args, format_idx,
- inFunctionCall, CallType, CheckedVarArgs,
- UncoveredArg);
-
- if (!analyze_format_string::ParsePrintfString(
- H, Str, Str + StrLen, S.getLangOpts(), S.Context.getTargetInfo(),
- Type == FormatStringType::Kprintf ||
- Type == FormatStringType::FreeBSDKPrintf))
- H.DoneProcessing();
- } else {
- S.CheckFormatStringsCompatible(
- Type, ReferenceFormatString, FExpr->getFormatString(),
- inFunctionCall ? nullptr : Args[format_idx]);
- }
- } else if (Type == FormatStringType::Scanf) {
- CheckScanfHandler H(S, FExpr, OrigFormatExpr, Type, firstDataArg,
- numDataArgs, Str, APK, Args, format_idx, inFunctionCall,
- CallType, CheckedVarArgs, UncoveredArg);
-
- if (!analyze_format_string::ParseScanfString(
- H, Str, Str + StrLen, S.getLangOpts(), S.Context.getTargetInfo()))
- H.DoneProcessing();
- } // TODO: handle other formats
-}
-
-bool Sema::CheckFormatStringsCompatible(
- FormatStringType Type, const StringLiteral *AuthoritativeFormatString,
- const StringLiteral *TestedFormatString, const Expr *FunctionCallArg) {
- if (Type != FormatStringType::Printf && Type != FormatStringType::NSString &&
- Type != FormatStringType::Kprintf &&
- Type != FormatStringType::FreeBSDKPrintf &&
- Type != FormatStringType::OSLog && Type != FormatStringType::OSTrace)
- return true;
-
- bool IsObjC =
- Type == FormatStringType::NSString || Type == FormatStringType::OSTrace;
- llvm::SmallVector<EquatableFormatArgument, 9> RefArgs, FmtArgs;
- FormatStringLiteral RefLit = AuthoritativeFormatString;
- FormatStringLiteral TestLit = TestedFormatString;
- const Expr *Arg;
- bool DiagAtStringLiteral;
- if (FunctionCallArg) {
- Arg = FunctionCallArg;
- DiagAtStringLiteral = false;
- } else {
- Arg = TestedFormatString;
- DiagAtStringLiteral = true;
- }
- if (DecomposePrintfHandler::GetSpecifiers(*this, &RefLit,
- AuthoritativeFormatString, Type,
- IsObjC, true, RefArgs) &&
- DecomposePrintfHandler::GetSpecifiers(*this, &TestLit, Arg, Type, IsObjC,
- DiagAtStringLiteral, FmtArgs)) {
- return CompareFormatSpecifiers(*this, AuthoritativeFormatString, RefArgs,
- TestedFormatString, FmtArgs, Arg,
- DiagAtStringLiteral);
- }
- return false;
-}
-
-bool Sema::ValidateFormatString(FormatStringType Type,
- const StringLiteral *Str) {
- if (Type != FormatStringType::Printf && Type != FormatStringType::NSString &&
- Type != FormatStringType::Kprintf &&
- Type != FormatStringType::FreeBSDKPrintf &&
- Type != FormatStringType::OSLog && Type != FormatStringType::OSTrace)
- return true;
-
- FormatStringLiteral RefLit = Str;
- llvm::SmallVector<EquatableFormatArgument, 9> Args;
- bool IsObjC =
- Type == FormatStringType::NSString || Type == FormatStringType::OSTrace;
- if (!DecomposePrintfHandler::GetSpecifiers(*this, &RefLit, Str, Type, IsObjC,
- true, Args))
- return false;
-
- // Group arguments by getPosition() value, and check that each member of the
- // group is compatible with the first member. This verifies that when
- // positional arguments are used multiple times (such as %2$i %2$d), all uses
- // are mutually compatible. As an optimization, don't test the first member
- // against itself.
- bool HadError = false;
- auto Iter = Args.begin();
- auto End = Args.end();
- while (Iter != End) {
- const auto &FirstInGroup = *Iter;
- for (++Iter;
- Iter != End && Iter->getPosition() == FirstInGroup.getPosition();
- ++Iter) {
- HadError |= !Iter->VerifyCompatible(*this, FirstInGroup, Str, true);
- }
- }
- return !HadError;
-}
-
-bool Sema::FormatStringHasSArg(const StringLiteral *FExpr) {
- // Str - The format string. NOTE: this is NOT null-terminated!
- StringRef StrRef = FExpr->getString();
- const char *Str = StrRef.data();
- // Account for cases where the string literal is truncated in a declaration.
- const ConstantArrayType *T = Context.getAsConstantArrayType(FExpr->getType());
- assert(T && "String literal not of constant array type!");
- size_t TypeSize = T->getZExtSize();
- size_t StrLen = std::min(std::max(TypeSize, size_t(1)) - 1, StrRef.size());
- return analyze_format_string::ParseFormatStringHasSArg(Str, Str + StrLen,
- getLangOpts(),
- Context.getTargetInfo());
-}
-
-//===--- CHECK: Warn on use of wrong absolute value function. -------------===//
-
-// Returns the related absolute value function that is larger, of 0 if one
-// does not exist.
-static unsigned getLargerAbsoluteValueFunction(unsigned AbsFunction) {
- switch (AbsFunction) {
- default:
- return 0;
-
- case Builtin::BI__builtin_abs:
- return Builtin::BI__builtin_labs;
- case Builtin::BI__builtin_labs:
- return Builtin::BI__builtin_llabs;
- case Builtin::BI__builtin_llabs:
- return 0;
-
- case Builtin::BI__builtin_fabsf:
- return Builtin::BI__builtin_fabs;
- case Builtin::BI__builtin_fabs:
- return Builtin::BI__builtin_fabsl;
- case Builtin::BI__builtin_fabsl:
- return 0;
-
- case Builtin::BI__builtin_cabsf:
- return Builtin::BI__builtin_cabs;
- case Builtin::BI__builtin_cabs:
- return Builtin::BI__builtin_cabsl;
- case Builtin::BI__builtin_cabsl:
- return 0;
-
- case Builtin::BIabs:
- return Builtin::BIlabs;
- case Builtin::BIlabs:
- return Builtin::BIllabs;
- case Builtin::BIllabs:
- return 0;
-
- case Builtin::BIfabsf:
- return Builtin::BIfabs;
- case Builtin::BIfabs:
- return Builtin::BIfabsl;
- case Builtin::BIfabsl:
- return 0;
-
- case Builtin::BIcabsf:
- return Builtin::BIcabs;
- case Builtin::BIcabs:
- return Builtin::BIcabsl;
- case Builtin::BIcabsl:
- return 0;
- }
-}
-
-// Returns the argument type of the absolute value function.
-static QualType getAbsoluteValueArgumentType(ASTContext &Context,
- unsigned AbsType) {
- if (AbsType == 0)
- return QualType();
-
- ASTContext::GetBuiltinTypeError Error = ASTContext::GE_None;
- QualType BuiltinType = Context.GetBuiltinType(AbsType, Error);
- if (Error != ASTContext::GE_None)
- return QualType();
-
- const FunctionProtoType *FT = BuiltinType->getAs<FunctionProtoType>();
- if (!FT)
- return QualType();
-
- if (FT->getNumParams() != 1)
- return QualType();
-
- return FT->getParamType(0);
-}
-
-// Returns the best absolute value function, or zero, based on type and
-// current absolute value function.
-static unsigned getBestAbsFunction(ASTContext &Context, QualType ArgType,
- unsigned AbsFunctionKind) {
- unsigned BestKind = 0;
- uint64_t ArgSize = Context.getTypeSize(ArgType);
- for (unsigned Kind = AbsFunctionKind; Kind != 0;
- Kind = getLargerAbsoluteValueFunction(Kind)) {
- QualType ParamType = getAbsoluteValueArgumentType(Context, Kind);
- if (Context.getTypeSize(ParamType) >= ArgSize) {
- if (BestKind == 0)
- BestKind = Kind;
- else if (Context.hasSameType(ParamType, ArgType)) {
- BestKind = Kind;
- break;
- }
- }
- }
- return BestKind;
-}
-
-enum AbsoluteValueKind {
- AVK_Integer,
- AVK_Floating,
- AVK_Complex
-};
-
-static AbsoluteValueKind getAbsoluteValueKind(QualType T) {
- if (T->isIntegralOrEnumerationType())
- return AVK_Integer;
- if (T->isRealFloatingType())
- return AVK_Floating;
- if (T->isAnyComplexType())
- return AVK_Complex;
-
- llvm_unreachable("Type not integer, floating, or complex");
-}
-
-// Changes the absolute value function to a different type. Preserves whether
-// the function is a builtin.
-static unsigned changeAbsFunction(unsigned AbsKind,
- AbsoluteValueKind ValueKind) {
- switch (ValueKind) {
- case AVK_Integer:
- switch (AbsKind) {
- default:
- return 0;
- case Builtin::BI__builtin_fabsf:
- case Builtin::BI__builtin_fabs:
- case Builtin::BI__builtin_fabsl:
- case Builtin::BI__builtin_cabsf:
- case Builtin::BI__builtin_cabs:
- case Builtin::BI__builtin_cabsl:
- return Builtin::BI__builtin_abs;
- case Builtin::BIfabsf:
- case Builtin::BIfabs:
- case Builtin::BIfabsl:
- case Builtin::BIcabsf:
- case Builtin::BIcabs:
- case Builtin::BIcabsl:
- return Builtin::BIabs;
- }
- case AVK_Floating:
- switch (AbsKind) {
- default:
- return 0;
- case Builtin::BI__builtin_abs:
- case Builtin::BI__builtin_labs:
- case Builtin::BI__builtin_llabs:
- case Builtin::BI__builtin_cabsf:
- case Builtin::BI__builtin_cabs:
- case Builtin::BI__builtin_cabsl:
- return Builtin::BI__builtin_fabsf;
- case Builtin::BIabs:
- case Builtin::BIlabs:
- case Builtin::BIllabs:
- case Builtin::BIcabsf:
- case Builtin::BIcabs:
- case Builtin::BIcabsl:
- return Builtin::BIfabsf;
- }
- case AVK_Complex:
- switch (AbsKind) {
- default:
- return 0;
- case Builtin::BI__builtin_abs:
- case Builtin::BI__builtin_labs:
- case Builtin::BI__builtin_llabs:
- case Builtin::BI__builtin_fabsf:
- case Builtin::BI__builtin_fabs:
- case Builtin::BI__builtin_fabsl:
- return Builtin::BI__builtin_cabsf;
- case Builtin::BIabs:
- case Builtin::BIlabs:
- case Builtin::BIllabs:
- case Builtin::BIfabsf:
- case Builtin::BIfabs:
- case Builtin::BIfabsl:
- return Builtin::BIcabsf;
- }
- }
- llvm_unreachable("Unable to convert function");
-}
-
-static unsigned getAbsoluteValueFunctionKind(const FunctionDecl *FDecl) {
- const IdentifierInfo *FnInfo = FDecl->getIdentifier();
- if (!FnInfo)
- return 0;
-
- switch (FDecl->getBuiltinID()) {
- default:
- return 0;
- case Builtin::BI__builtin_abs:
- case Builtin::BI__builtin_fabs:
- case Builtin::BI__builtin_fabsf:
- case Builtin::BI__builtin_fabsl:
- case Builtin::BI__builtin_labs:
- case Builtin::BI__builtin_llabs:
- case Builtin::BI__builtin_cabs:
- case Builtin::BI__builtin_cabsf:
- case Builtin::BI__builtin_cabsl:
- case Builtin::BIabs:
- case Builtin::BIlabs:
- case Builtin::BIllabs:
- case Builtin::BIfabs:
- case Builtin::BIfabsf:
- case Builtin::BIfabsl:
- case Builtin::BIcabs:
- case Builtin::BIcabsf:
- case Builtin::BIcabsl:
- return FDecl->getBuiltinID();
- }
- llvm_unreachable("Unknown Builtin type");
-}
-
-// If the replacement is valid, emit a note with replacement function.
-// Additionally, suggest including the proper header if not already included.
-static void emitReplacement(Sema &S, SourceLocation Loc, SourceRange Range,
- unsigned AbsKind, QualType ArgType) {
- bool EmitHeaderHint = true;
- const char *HeaderName = nullptr;
- std::string FunctionName;
- if (S.getLangOpts().CPlusPlus && !ArgType->isAnyComplexType()) {
- FunctionName = "std::abs";
- if (ArgType->isIntegralOrEnumerationType()) {
- HeaderName = "cstdlib";
- } else if (ArgType->isRealFloatingType()) {
- HeaderName = "cmath";
- } else {
- llvm_unreachable("Invalid Type");
- }
-
- // Lookup all std::abs
- if (NamespaceDecl *Std = S.getStdNamespace()) {
- LookupResult R(S, &S.Context.Idents.get("abs"), Loc, Sema::LookupAnyName);
- R.suppressDiagnostics();
- S.LookupQualifiedName(R, Std);
-
- for (const auto *I : R) {
- const FunctionDecl *FDecl = nullptr;
- if (const UsingShadowDecl *UsingD = dyn_cast<UsingShadowDecl>(I)) {
- FDecl = dyn_cast<FunctionDecl>(UsingD->getTargetDecl());
- } else {
- FDecl = dyn_cast<FunctionDecl>(I);
- }
- if (!FDecl)
- continue;
-
- // Found std::abs(), check that they are the right ones.
- if (FDecl->getNumParams() != 1)
- continue;
-
- // Check that the parameter type can handle the argument.
- QualType ParamType = FDecl->getParamDecl(0)->getType();
- if (getAbsoluteValueKind(ArgType) == getAbsoluteValueKind(ParamType) &&
- S.Context.getTypeSize(ArgType) <=
- S.Context.getTypeSize(ParamType)) {
- // Found a function, don't need the header hint.
- EmitHeaderHint = false;
- break;
- }
- }
- }
- } else {
- FunctionName = S.Context.BuiltinInfo.getName(AbsKind);
- HeaderName = S.Context.BuiltinInfo.getHeaderName(AbsKind);
-
- if (HeaderName) {
- DeclarationName DN(&S.Context.Idents.get(FunctionName));
- LookupResult R(S, DN, Loc, Sema::LookupAnyName);
- R.suppressDiagnostics();
- S.LookupName(R, S.getCurScope());
-
- if (R.isSingleResult()) {
- FunctionDecl *FD = dyn_cast<FunctionDecl>(R.getFoundDecl());
- if (FD && FD->getBuiltinID() == AbsKind) {
- EmitHeaderHint = false;
- } else {
- return;
- }
- } else if (!R.empty()) {
- return;
- }
- }
- }
-
- S.Diag(Loc, diag::note_replace_abs_function)
- << FunctionName << FixItHint::CreateReplacement(Range, FunctionName);
-
- if (!HeaderName)
- return;
-
- if (!EmitHeaderHint)
- return;
-
- S.Diag(Loc, diag::note_include_header_or_declare) << HeaderName
- << FunctionName;
-}
-
-template <std::size_t StrLen>
-static bool IsStdFunction(const FunctionDecl *FDecl,
- const char (&Str)[StrLen]) {
- if (!FDecl)
- return false;
- if (!FDecl->getIdentifier() || !FDecl->getIdentifier()->isStr(Str))
- return false;
- if (!FDecl->isInStdNamespace())
- return false;
-
- return true;
-}
-
-enum class MathCheck { NaN, Inf };
-static bool IsInfOrNanFunction(StringRef calleeName, MathCheck Check) {
- auto MatchesAny = [&](std::initializer_list<llvm::StringRef> names) {
- return llvm::is_contained(names, calleeName);
- };
-
- switch (Check) {
- case MathCheck::NaN:
- return MatchesAny({"__builtin_nan", "__builtin_nanf", "__builtin_nanl",
- "__builtin_nanf16", "__builtin_nanf128"});
- case MathCheck::Inf:
- return MatchesAny({"__builtin_inf", "__builtin_inff", "__builtin_infl",
- "__builtin_inff16", "__builtin_inff128"});
- }
- llvm_unreachable("unknown MathCheck");
-}
-
-static bool IsInfinityFunction(const FunctionDecl *FDecl) {
- if (FDecl->getName() != "infinity")
- return false;
-
- if (const CXXMethodDecl *MDecl = dyn_cast<CXXMethodDecl>(FDecl)) {
- const CXXRecordDecl *RDecl = MDecl->getParent();
- if (RDecl->getName() != "numeric_limits")
- return false;
-
- if (const NamespaceDecl *NSDecl =
- dyn_cast<NamespaceDecl>(RDecl->getDeclContext()))
- return NSDecl->isStdNamespace();
- }
-
- return false;
-}
-
-void Sema::CheckInfNaNFunction(const CallExpr *Call,
- const FunctionDecl *FDecl) {
- if (!FDecl->getIdentifier())
- return;
-
- FPOptions FPO = Call->getFPFeaturesInEffect(getLangOpts());
- if (FPO.getNoHonorNaNs() &&
- (IsStdFunction(FDecl, "isnan") || IsStdFunction(FDecl, "isunordered") ||
- IsInfOrNanFunction(FDecl->getName(), MathCheck::NaN))) {
- Diag(Call->getBeginLoc(), diag::warn_fp_nan_inf_when_disabled)
- << 1 << 0 << Call->getSourceRange();
- return;
- }
-
- if (FPO.getNoHonorInfs() &&
- (IsStdFunction(FDecl, "isinf") || IsStdFunction(FDecl, "isfinite") ||
- IsInfinityFunction(FDecl) ||
- IsInfOrNanFunction(FDecl->getName(), MathCheck::Inf))) {
- Diag(Call->getBeginLoc(), diag::warn_fp_nan_inf_when_disabled)
- << 0 << 0 << Call->getSourceRange();
- }
-}
-
-void Sema::CheckAbsoluteValueFunction(const CallExpr *Call,
- const FunctionDecl *FDecl) {
- if (Call->getNumArgs() != 1)
- return;
-
- unsigned AbsKind = getAbsoluteValueFunctionKind(FDecl);
- bool IsStdAbs = IsStdFunction(FDecl, "abs");
- if (AbsKind == 0 && !IsStdAbs)
- return;
-
- QualType ArgType = Call->getArg(0)->IgnoreParenImpCasts()->getType();
- QualType ParamType = Call->getArg(0)->getType();
-
- // Unsigned types cannot be negative. Suggest removing the absolute value
- // function call.
- if (ArgType->isUnsignedIntegerType()) {
- std::string FunctionName =
- IsStdAbs ? "std::abs" : Context.BuiltinInfo.getName(AbsKind);
- Diag(Call->getExprLoc(), diag::warn_unsigned_abs) << ArgType << ParamType;
- Diag(Call->getExprLoc(), diag::note_remove_abs)
- << FunctionName
- << FixItHint::CreateRemoval(Call->getCallee()->getSourceRange());
- return;
- }
-
- // Taking the absolute value of a pointer is very suspicious, they probably
- // wanted to index into an array, dereference a pointer, call a function, etc.
- if (ArgType->isPointerType() || ArgType->canDecayToPointerType()) {
- unsigned DiagType = 0;
- if (ArgType->isFunctionType())
- DiagType = 1;
- else if (ArgType->isArrayType())
- DiagType = 2;
-
- Diag(Call->getExprLoc(), diag::warn_pointer_abs) << DiagType << ArgType;
- return;
- }
-
- // std::abs has overloads which prevent most of the absolute value problems
- // from occurring.
- if (IsStdAbs)
- return;
-
- AbsoluteValueKind ArgValueKind = getAbsoluteValueKind(ArgType);
- AbsoluteValueKind ParamValueKind = getAbsoluteValueKind(ParamType);
-
- // The argument and parameter are the same kind. Check if they are the right
- // size.
- if (ArgValueKind == ParamValueKind) {
- if (Context.getTypeSize(ArgType) <= Context.getTypeSize(ParamType))
- return;
-
- unsigned NewAbsKind = getBestAbsFunction(Context, ArgType, AbsKind);
- Diag(Call->getExprLoc(), diag::warn_abs_too_small)
- << FDecl << ArgType << ParamType;
-
- if (NewAbsKind == 0)
- return;
-
- emitReplacement(*this, Call->getExprLoc(),
- Call->getCallee()->getSourceRange(), NewAbsKind, ArgType);
- return;
- }
-
- // ArgValueKind != ParamValueKind
- // The wrong type of absolute value function was used. Attempt to find the
- // proper one.
- unsigned NewAbsKind = changeAbsFunction(AbsKind, ArgValueKind);
- NewAbsKind = getBestAbsFunction(Context, ArgType, NewAbsKind);
- if (NewAbsKind == 0)
- return;
-
- Diag(Call->getExprLoc(), diag::warn_wrong_absolute_value_type)
- << FDecl << ParamValueKind << ArgValueKind;
-
- emitReplacement(*this, Call->getExprLoc(),
- Call->getCallee()->getSourceRange(), NewAbsKind, ArgType);
-}
-
-//===--- CHECK: Warn on use of std::max and unsigned zero. r---------------===//
-void Sema::CheckMaxUnsignedZero(const CallExpr *Call,
- const FunctionDecl *FDecl) {
- if (!Call || !FDecl) return;
-
- // Ignore template specializations and macros.
- if (inTemplateInstantiation()) return;
- if (Call->getExprLoc().isMacroID()) return;
-
- // Only care about the one template argument, two function parameter std::max
- if (Call->getNumArgs() != 2) return;
- if (!IsStdFunction(FDecl, "max")) return;
- const auto * ArgList = FDecl->getTemplateSpecializationArgs();
- if (!ArgList) return;
- if (ArgList->size() != 1) return;
-
- // Check that template type argument is unsigned integer.
- const auto& TA = ArgList->get(0);
- if (TA.getKind() != TemplateArgument::Type) return;
- QualType ArgType = TA.getAsType();
- if (!ArgType->isUnsignedIntegerType()) return;
-
- // See if either argument is a literal zero.
- auto IsLiteralZeroArg = [](const Expr* E) -> bool {
- const auto *MTE = dyn_cast<MaterializeTemporaryExpr>(E);
- if (!MTE) return false;
- const auto *Num = dyn_cast<IntegerLiteral>(MTE->getSubExpr());
- if (!Num) return false;
- if (Num->getValue() != 0) return false;
- return true;
- };
-
- const Expr *FirstArg = Call->getArg(0);
- const Expr *SecondArg = Call->getArg(1);
- const bool IsFirstArgZero = IsLiteralZeroArg(FirstArg);
- const bool IsSecondArgZero = IsLiteralZeroArg(SecondArg);
-
- // Only warn when exactly one argument is zero.
- if (IsFirstArgZero == IsSecondArgZero) return;
-
- SourceRange FirstRange = FirstArg->getSourceRange();
- SourceRange SecondRange = SecondArg->getSourceRange();
-
- SourceRange ZeroRange = IsFirstArgZero ? FirstRange : SecondRange;
-
- Diag(Call->getExprLoc(), diag::warn_max_unsigned_zero)
- << IsFirstArgZero << Call->getCallee()->getSourceRange() << ZeroRange;
-
- // Deduce what parts to remove so that "std::max(0u, foo)" becomes "(foo)".
- SourceRange RemovalRange;
- if (IsFirstArgZero) {
- RemovalRange = SourceRange(FirstRange.getBegin(),
- SecondRange.getBegin().getLocWithOffset(-1));
- } else {
- RemovalRange = SourceRange(getLocForEndOfToken(FirstRange.getEnd()),
- SecondRange.getEnd());
- }
-
- Diag(Call->getExprLoc(), diag::note_remove_max_call)
- << FixItHint::CreateRemoval(Call->getCallee()->getSourceRange())
- << FixItHint::CreateRemoval(RemovalRange);
-}
-
-//===--- CHECK: Standard memory functions ---------------------------------===//
-
-/// Takes the expression passed to the size_t parameter of functions
-/// such as memcmp, strncat, etc and warns if it's a comparison.
-///
-/// This is to catch typos like `if (memcmp(&a, &b, sizeof(a) > 0))`.
-static bool CheckMemorySizeofForComparison(Sema &S, const Expr *E,
- const IdentifierInfo *FnName,
- SourceLocation FnLoc,
- SourceLocation RParenLoc) {
- const auto *Size = dyn_cast<BinaryOperator>(E);
- if (!Size)
- return false;
-
- // if E is binop and op is <=>, >, <, >=, <=, ==, &&, ||:
- if (!Size->isComparisonOp() && !Size->isLogicalOp())
- return false;
-
- SourceRange SizeRange = Size->getSourceRange();
- S.Diag(Size->getOperatorLoc(), diag::warn_memsize_comparison)
- << SizeRange << FnName;
- S.Diag(FnLoc, diag::note_memsize_comparison_paren)
- << FnName
- << FixItHint::CreateInsertion(
- S.getLocForEndOfToken(Size->getLHS()->getEndLoc()), ")")
- << FixItHint::CreateRemoval(RParenLoc);
- S.Diag(SizeRange.getBegin(), diag::note_memsize_comparison_cast_silence)
- << FixItHint::CreateInsertion(SizeRange.getBegin(), "(size_t)(")
- << FixItHint::CreateInsertion(S.getLocForEndOfToken(SizeRange.getEnd()),
- ")");
-
- return true;
-}
-
-/// Determine whether the given type is or contains a dynamic class type
-/// (e.g., whether it has a vtable).
-static const CXXRecordDecl *getContainedDynamicClass(QualType T,
- bool &IsContained) {
- // Look through array types while ignoring qualifiers.
- const Type *Ty = T->getBaseElementTypeUnsafe();
- IsContained = false;
-
- const CXXRecordDecl *RD = Ty->getAsCXXRecordDecl();
- RD = RD ? RD->getDefinition() : nullptr;
- if (!RD || RD->isInvalidDecl())
- return nullptr;
-
- if (RD->isDynamicClass())
- return RD;
-
- // Check all the fields. If any bases were dynamic, the class is dynamic.
- // It's impossible for a class to transitively contain itself by value, so
- // infinite recursion is impossible.
- for (auto *FD : RD->fields()) {
- bool SubContained;
- if (const CXXRecordDecl *ContainedRD =
- getContainedDynamicClass(FD->getType(), SubContained)) {
- IsContained = true;
- return ContainedRD;
- }
- }
-
- return nullptr;
-}
-
-static const UnaryExprOrTypeTraitExpr *getAsSizeOfExpr(const Expr *E) {
- if (const auto *Unary = dyn_cast<UnaryExprOrTypeTraitExpr>(E))
- if (Unary->getKind() == UETT_SizeOf)
- return Unary;
- return nullptr;
-}
-
-/// If E is a sizeof expression, returns its argument expression,
-/// otherwise returns NULL.
-static const Expr *getSizeOfExprArg(const Expr *E) {
- if (const UnaryExprOrTypeTraitExpr *SizeOf = getAsSizeOfExpr(E))
- if (!SizeOf->isArgumentType())
- return SizeOf->getArgumentExpr()->IgnoreParenImpCasts();
- return nullptr;
-}
-
-/// If E is a sizeof expression, returns its argument type.
-static QualType getSizeOfArgType(const Expr *E) {
- if (const UnaryExprOrTypeTraitExpr *SizeOf = getAsSizeOfExpr(E))
- return SizeOf->getTypeOfArgument();
- return QualType();
-}
-
-namespace {
-
-struct SearchNonTrivialToInitializeField
- : DefaultInitializedTypeVisitor<SearchNonTrivialToInitializeField> {
- using Super =
- DefaultInitializedTypeVisitor<SearchNonTrivialToInitializeField>;
-
- SearchNonTrivialToInitializeField(const Expr *E, Sema &S) : E(E), S(S) {}
-
- void visitWithKind(QualType::PrimitiveDefaultInitializeKind PDIK, QualType FT,
- SourceLocation SL) {
- if (const auto *AT = asDerived().getContext().getAsArrayType(FT)) {
- asDerived().visitArray(PDIK, AT, SL);
- return;
- }
-
- Super::visitWithKind(PDIK, FT, SL);
- }
-
- void visitARCStrong(QualType FT, SourceLocation SL) {
- S.DiagRuntimeBehavior(SL, E, S.PDiag(diag::note_nontrivial_field) << 1);
- }
- void visitARCWeak(QualType FT, SourceLocation SL) {
- S.DiagRuntimeBehavior(SL, E, S.PDiag(diag::note_nontrivial_field) << 1);
- }
- void visitStruct(QualType FT, SourceLocation SL) {
- for (const FieldDecl *FD : FT->castAsRecordDecl()->fields())
- visit(FD->getType(), FD->getLocation());
- }
- void visitArray(QualType::PrimitiveDefaultInitializeKind PDIK,
- const ArrayType *AT, SourceLocation SL) {
- visit(getContext().getBaseElementType(AT), SL);
- }
- void visitTrivial(QualType FT, SourceLocation SL) {}
-
- static void diag(QualType RT, const Expr *E, Sema &S) {
- SearchNonTrivialToInitializeField(E, S).visitStruct(RT, SourceLocation());
- }
-
- ASTContext &getContext() { return S.getASTContext(); }
-
- const Expr *E;
- Sema &S;
-};
-
-struct SearchNonTrivialToCopyField
- : CopiedTypeVisitor<SearchNonTrivialToCopyField, false> {
- using Super = CopiedTypeVisitor<SearchNonTrivialToCopyField, false>;
-
- SearchNonTrivialToCopyField(const Expr *E, Sema &S) : E(E), S(S) {}
-
- void visitWithKind(QualType::PrimitiveCopyKind PCK, QualType FT,
- SourceLocation SL) {
- if (const auto *AT = asDerived().getContext().getAsArrayType(FT)) {
- asDerived().visitArray(PCK, AT, SL);
- return;
- }
-
- Super::visitWithKind(PCK, FT, SL);
- }
-
- void visitARCStrong(QualType FT, SourceLocation SL) {
- S.DiagRuntimeBehavior(SL, E, S.PDiag(diag::note_nontrivial_field) << 0);
- }
- void visitARCWeak(QualType FT, SourceLocation SL) {
- S.DiagRuntimeBehavior(SL, E, S.PDiag(diag::note_nontrivial_field) << 0);
- }
- void visitPtrAuth(QualType FT, SourceLocation SL) {
- S.DiagRuntimeBehavior(SL, E, S.PDiag(diag::note_nontrivial_field) << 0);
- }
- void visitStruct(QualType FT, SourceLocation SL) {
- for (const FieldDecl *FD : FT->castAsRecordDecl()->fields())
- visit(FD->getType(), FD->getLocation());
- }
- void visitArray(QualType::PrimitiveCopyKind PCK, const ArrayType *AT,
- SourceLocation SL) {
- visit(getContext().getBaseElementType(AT), SL);
- }
- void preVisit(QualType::PrimitiveCopyKind PCK, QualType FT,
- SourceLocation SL) {}
- void visitTrivial(QualType FT, SourceLocation SL) {}
- void visitVolatileTrivial(QualType FT, SourceLocation SL) {}
-
- static void diag(QualType RT, const Expr *E, Sema &S) {
- SearchNonTrivialToCopyField(E, S).visitStruct(RT, SourceLocation());
- }
-
- ASTContext &getContext() { return S.getASTContext(); }
-
- const Expr *E;
- Sema &S;
-};
-
-}
-
-/// Detect if \c SizeofExpr is likely to calculate the sizeof an object.
-static bool doesExprLikelyComputeSize(const Expr *SizeofExpr) {
- SizeofExpr = SizeofExpr->IgnoreParenImpCasts();
-
- if (const auto *BO = dyn_cast<BinaryOperator>(SizeofExpr)) {
- if (BO->getOpcode() != BO_Mul && BO->getOpcode() != BO_Add)
- return false;
-
- return doesExprLikelyComputeSize(BO->getLHS()) ||
- doesExprLikelyComputeSize(BO->getRHS());
- }
-
- return getAsSizeOfExpr(SizeofExpr) != nullptr;
-}
-
-/// Check if the ArgLoc originated from a macro passed to the call at CallLoc.
-///
-/// \code
-/// #define MACRO 0
-/// foo(MACRO);
-/// foo(0);
-/// \endcode
-///
-/// This should return true for the first call to foo, but not for the second
-/// (regardless of whether foo is a macro or function).
-static bool isArgumentExpandedFromMacro(SourceManager &SM,
- SourceLocation CallLoc,
- SourceLocation ArgLoc) {
- if (!CallLoc.isMacroID())
- return SM.getFileID(CallLoc) != SM.getFileID(ArgLoc);
-
- return SM.getFileID(SM.getImmediateMacroCallerLoc(CallLoc)) !=
- SM.getFileID(SM.getImmediateMacroCallerLoc(ArgLoc));
-}
-
-/// Diagnose cases like 'memset(buf, sizeof(buf), 0)', which should have the
-/// last two arguments transposed.
-static void CheckMemaccessSize(Sema &S, unsigned BId, const CallExpr *Call) {
- if (BId != Builtin::BImemset && BId != Builtin::BIbzero)
- return;
-
- const Expr *SizeArg =
- Call->getArg(BId == Builtin::BImemset ? 2 : 1)->IgnoreImpCasts();
-
- auto isLiteralZero = [](const Expr *E) {
- return (isa<IntegerLiteral>(E) &&
- cast<IntegerLiteral>(E)->getValue() == 0) ||
- (isa<CharacterLiteral>(E) &&
- cast<CharacterLiteral>(E)->getValue() == 0);
- };
-
- // If we're memsetting or bzeroing 0 bytes, then this is likely an error.
- SourceLocation CallLoc = Call->getRParenLoc();
- SourceManager &SM = S.getSourceManager();
- if (isLiteralZero(SizeArg) &&
- !isArgumentExpandedFromMacro(SM, CallLoc, SizeArg->getExprLoc())) {
-
- SourceLocation DiagLoc = SizeArg->getExprLoc();
-
- // Some platforms #define bzero to __builtin_memset. See if this is the
- // case, and if so, emit a better diagnostic.
- if (BId == Builtin::BIbzero ||
- (CallLoc.isMacroID() && Lexer::getImmediateMacroName(
- CallLoc, SM, S.getLangOpts()) == "bzero")) {
- S.Diag(DiagLoc, diag::warn_suspicious_bzero_size);
- S.Diag(DiagLoc, diag::note_suspicious_bzero_size_silence);
- } else if (!isLiteralZero(Call->getArg(1)->IgnoreImpCasts())) {
- S.Diag(DiagLoc, diag::warn_suspicious_sizeof_memset) << 0;
- S.Diag(DiagLoc, diag::note_suspicious_sizeof_memset_silence) << 0;
- }
- return;
- }
-
- // If the second argument to a memset is a sizeof expression and the third
- // isn't, this is also likely an error. This should catch
- // 'memset(buf, sizeof(buf), 0xff)'.
- if (BId == Builtin::BImemset &&
- doesExprLikelyComputeSize(Call->getArg(1)) &&
- !doesExprLikelyComputeSize(Call->getArg(2))) {
- SourceLocation DiagLoc = Call->getArg(1)->getExprLoc();
- S.Diag(DiagLoc, diag::warn_suspicious_sizeof_memset) << 1;
- S.Diag(DiagLoc, diag::note_suspicious_sizeof_memset_silence) << 1;
- return;
- }
-}
-
-void Sema::CheckMemaccessArguments(const CallExpr *Call,
- unsigned BId,
- IdentifierInfo *FnName) {
- assert(BId != 0);
-
- // It is possible to have a non-standard definition of memset. Validate
- // we have enough arguments, and if not, abort further checking.
- unsigned ExpectedNumArgs =
- (BId == Builtin::BIstrndup || BId == Builtin::BIbzero ? 2 : 3);
- if (Call->getNumArgs() < ExpectedNumArgs)
- return;
-
- unsigned LastArg = (BId == Builtin::BImemset || BId == Builtin::BIbzero ||
- BId == Builtin::BIstrndup ? 1 : 2);
- unsigned LenArg =
- (BId == Builtin::BIbzero || BId == Builtin::BIstrndup ? 1 : 2);
- const Expr *LenExpr = Call->getArg(LenArg)->IgnoreParenImpCasts();
-
- if (CheckMemorySizeofForComparison(*this, LenExpr, FnName,
- Call->getBeginLoc(), Call->getRParenLoc()))
- return;
-
- // Catch cases like 'memset(buf, sizeof(buf), 0)'.
- CheckMemaccessSize(*this, BId, Call);
-
- // We have special checking when the length is a sizeof expression.
- QualType SizeOfArgTy = getSizeOfArgType(LenExpr);
- const Expr *SizeOfArg = getSizeOfExprArg(LenExpr);
- llvm::FoldingSetNodeID SizeOfArgID;
-
- // Although widely used, 'bzero' is not a standard function. Be more strict
- // with the argument types before allowing diagnostics and only allow the
- // form bzero(ptr, sizeof(...)).
- QualType FirstArgTy = Call->getArg(0)->IgnoreParenImpCasts()->getType();
- if (BId == Builtin::BIbzero && !FirstArgTy->getAs<PointerType>())
- return;
-
- for (unsigned ArgIdx = 0; ArgIdx != LastArg; ++ArgIdx) {
- const Expr *Dest = Call->getArg(ArgIdx)->IgnoreParenImpCasts();
- SourceRange ArgRange = Call->getArg(ArgIdx)->getSourceRange();
-
- QualType DestTy = Dest->getType();
- QualType PointeeTy;
- if (const PointerType *DestPtrTy = DestTy->getAs<PointerType>()) {
- PointeeTy = DestPtrTy->getPointeeType();
-
- // Never warn about void type pointers. This can be used to suppress
- // false positives.
- if (PointeeTy->isVoidType())
- continue;
-
- // Catch "memset(p, 0, sizeof(p))" -- needs to be sizeof(*p). Do this by
- // actually comparing the expressions for equality. Because computing the
- // expression IDs can be expensive, we only do this if the diagnostic is
- // enabled.
- if (SizeOfArg &&
- !Diags.isIgnored(diag::warn_sizeof_pointer_expr_memaccess,
- SizeOfArg->getExprLoc())) {
- // We only compute IDs for expressions if the warning is enabled, and
- // cache the sizeof arg's ID.
- if (SizeOfArgID == llvm::FoldingSetNodeID())
- SizeOfArg->Profile(SizeOfArgID, Context, true);
- llvm::FoldingSetNodeID DestID;
- Dest->Profile(DestID, Context, true);
- if (DestID == SizeOfArgID) {
- // TODO: For strncpy() and friends, this could suggest sizeof(dst)
- // over sizeof(src) as well.
- unsigned ActionIdx = 0; // Default is to suggest dereferencing.
- StringRef ReadableName = FnName->getName();
-
- if (const UnaryOperator *UnaryOp = dyn_cast<UnaryOperator>(Dest))
- if (UnaryOp->getOpcode() == UO_AddrOf)
- ActionIdx = 1; // If its an address-of operator, just remove it.
- if (!PointeeTy->isIncompleteType() &&
- (Context.getTypeSize(PointeeTy) == Context.getCharWidth()))
- ActionIdx = 2; // If the pointee's size is sizeof(char),
- // suggest an explicit length.
-
- // If the function is defined as a builtin macro, do not show macro
- // expansion.
- SourceLocation SL = SizeOfArg->getExprLoc();
- SourceRange DSR = Dest->getSourceRange();
- SourceRange SSR = SizeOfArg->getSourceRange();
- SourceManager &SM = getSourceManager();
-
- if (SM.isMacroArgExpansion(SL)) {
- ReadableName = Lexer::getImmediateMacroName(SL, SM, LangOpts);
- SL = SM.getSpellingLoc(SL);
- DSR = SourceRange(SM.getSpellingLoc(DSR.getBegin()),
- SM.getSpellingLoc(DSR.getEnd()));
- SSR = SourceRange(SM.getSpellingLoc(SSR.getBegin()),
- SM.getSpellingLoc(SSR.getEnd()));
- }
-
- DiagRuntimeBehavior(SL, SizeOfArg,
- PDiag(diag::warn_sizeof_pointer_expr_memaccess)
- << ReadableName
- << PointeeTy
- << DestTy
- << DSR
- << SSR);
- DiagRuntimeBehavior(SL, SizeOfArg,
- PDiag(diag::warn_sizeof_pointer_expr_memaccess_note)
- << ActionIdx
- << SSR);
-
- break;
- }
- }
-
- // Also check for cases where the sizeof argument is the exact same
- // type as the memory argument, and where it points to a user-defined
- // record type.
- if (SizeOfArgTy != QualType()) {
- if (PointeeTy->isRecordType() &&
- Context.typesAreCompatible(SizeOfArgTy, DestTy)) {
- DiagRuntimeBehavior(LenExpr->getExprLoc(), Dest,
- PDiag(diag::warn_sizeof_pointer_type_memaccess)
- << FnName << SizeOfArgTy << ArgIdx
- << PointeeTy << Dest->getSourceRange()
- << LenExpr->getSourceRange());
- break;
- }
- }
- } else if (DestTy->isArrayType()) {
- PointeeTy = DestTy;
- }
-
- if (PointeeTy == QualType())
- continue;
-
- // Always complain about dynamic classes.
- bool IsContained;
- if (const CXXRecordDecl *ContainedRD =
- getContainedDynamicClass(PointeeTy, IsContained)) {
-
- unsigned OperationType = 0;
- const bool IsCmp = BId == Builtin::BImemcmp || BId == Builtin::BIbcmp;
- // "overwritten" if we're warning about the destination for any call
- // but memcmp; otherwise a verb appropriate to the call.
- if (ArgIdx != 0 || IsCmp) {
- if (BId == Builtin::BImemcpy)
- OperationType = 1;
- else if(BId == Builtin::BImemmove)
- OperationType = 2;
- else if (IsCmp)
- OperationType = 3;
- }
-
- DiagRuntimeBehavior(Dest->getExprLoc(), Dest,
- PDiag(diag::warn_dyn_class_memaccess)
- << (IsCmp ? ArgIdx + 2 : ArgIdx) << FnName
- << IsContained << ContainedRD << OperationType
- << Call->getCallee()->getSourceRange());
- } else if (PointeeTy.hasNonTrivialObjCLifetime() &&
- BId != Builtin::BImemset)
- DiagRuntimeBehavior(
- Dest->getExprLoc(), Dest,
- PDiag(diag::warn_arc_object_memaccess)
- << ArgIdx << FnName << PointeeTy
- << Call->getCallee()->getSourceRange());
- else if (const auto *RD = PointeeTy->getAsRecordDecl()) {
-
- // FIXME: Do not consider incomplete types even though they may be
- // completed later. GCC does not diagnose such code, but we may want to
- // consider diagnosing it in the future, perhaps under a different, but
- // related, diagnostic group.
- bool NonTriviallyCopyableCXXRecord =
- getLangOpts().CPlusPlus && RD->isCompleteDefinition() &&
- !PointeeTy.isTriviallyCopyableType(Context);
-
- if ((BId == Builtin::BImemset || BId == Builtin::BIbzero) &&
- RD->isNonTrivialToPrimitiveDefaultInitialize()) {
- DiagRuntimeBehavior(Dest->getExprLoc(), Dest,
- PDiag(diag::warn_cstruct_memaccess)
- << ArgIdx << FnName << PointeeTy << 0);
- SearchNonTrivialToInitializeField::diag(PointeeTy, Dest, *this);
- } else if ((BId == Builtin::BImemset || BId == Builtin::BIbzero) &&
- NonTriviallyCopyableCXXRecord && ArgIdx == 0) {
- // FIXME: Limiting this warning to dest argument until we decide
- // whether it's valid for source argument too.
- DiagRuntimeBehavior(Dest->getExprLoc(), Dest,
- PDiag(diag::warn_cxxstruct_memaccess)
- << FnName << PointeeTy);
- } else if ((BId == Builtin::BImemcpy || BId == Builtin::BImemmove) &&
- RD->isNonTrivialToPrimitiveCopy()) {
- DiagRuntimeBehavior(Dest->getExprLoc(), Dest,
- PDiag(diag::warn_cstruct_memaccess)
- << ArgIdx << FnName << PointeeTy << 1);
- SearchNonTrivialToCopyField::diag(PointeeTy, Dest, *this);
- } else if ((BId == Builtin::BImemcpy || BId == Builtin::BImemmove) &&
- NonTriviallyCopyableCXXRecord && ArgIdx == 0) {
- // FIXME: Limiting this warning to dest argument until we decide
- // whether it's valid for source argument too.
- DiagRuntimeBehavior(Dest->getExprLoc(), Dest,
- PDiag(diag::warn_cxxstruct_memaccess)
- << FnName << PointeeTy);
- } else {
- continue;
- }
- } else
- continue;
-
- DiagRuntimeBehavior(
- Dest->getExprLoc(), Dest,
- PDiag(diag::note_bad_memaccess_silence)
- << FixItHint::CreateInsertion(ArgRange.getBegin(), "(void*)"));
- break;
- }
-}
-
-// A little helper routine: ignore addition and subtraction of integer literals.
-// This intentionally does not ignore all integer constant expressions because
-// we don't want to remove sizeof().
-static const Expr *ignoreLiteralAdditions(const Expr *Ex, ASTContext &Ctx) {
- Ex = Ex->IgnoreParenCasts();
-
- while (true) {
- const BinaryOperator * BO = dyn_cast<BinaryOperator>(Ex);
- if (!BO || !BO->isAdditiveOp())
- break;
-
- const Expr *RHS = BO->getRHS()->IgnoreParenCasts();
- const Expr *LHS = BO->getLHS()->IgnoreParenCasts();
-
- if (isa<IntegerLiteral>(RHS))
- Ex = LHS;
- else if (isa<IntegerLiteral>(LHS))
- Ex = RHS;
- else
- break;
- }
-
- return Ex;
-}
-
-static bool isConstantSizeArrayWithMoreThanOneElement(QualType Ty,
- ASTContext &Context) {
- // Only handle constant-sized or VLAs, but not flexible members.
- if (const ConstantArrayType *CAT = Context.getAsConstantArrayType(Ty)) {
- // Only issue the FIXIT for arrays of size > 1.
- if (CAT->getZExtSize() <= 1)
- return false;
- } else if (!Ty->isVariableArrayType()) {
- return false;
- }
- return true;
-}
-
-void Sema::CheckStrlcpycatArguments(const CallExpr *Call,
- IdentifierInfo *FnName) {
-
- // Don't crash if the user has the wrong number of arguments
- unsigned NumArgs = Call->getNumArgs();
- if ((NumArgs != 3) && (NumArgs != 4))
- return;
-
- const Expr *SrcArg = ignoreLiteralAdditions(Call->getArg(1), Context);
- const Expr *SizeArg = ignoreLiteralAdditions(Call->getArg(2), Context);
- const Expr *CompareWithSrc = nullptr;
-
- if (CheckMemorySizeofForComparison(*this, SizeArg, FnName,
- Call->getBeginLoc(), Call->getRParenLoc()))
- return;
-
- // Look for 'strlcpy(dst, x, sizeof(x))'
- if (const Expr *Ex = getSizeOfExprArg(SizeArg))
- CompareWithSrc = Ex;
- else {
- // Look for 'strlcpy(dst, x, strlen(x))'
- if (const CallExpr *SizeCall = dyn_cast<CallExpr>(SizeArg)) {
- if (SizeCall->getBuiltinCallee() == Builtin::BIstrlen &&
- SizeCall->getNumArgs() == 1)
- CompareWithSrc = ignoreLiteralAdditions(SizeCall->getArg(0), Context);
- }
- }
-
- if (!CompareWithSrc)
- return;
-
- // Determine if the argument to sizeof/strlen is equal to the source
- // argument. In principle there's all kinds of things you could do
- // here, for instance creating an == expression and evaluating it with
- // EvaluateAsBooleanCondition, but this uses a more direct technique:
- const DeclRefExpr *SrcArgDRE = dyn_cast<DeclRefExpr>(SrcArg);
- if (!SrcArgDRE)
- return;
-
- const DeclRefExpr *CompareWithSrcDRE = dyn_cast<DeclRefExpr>(CompareWithSrc);
- if (!CompareWithSrcDRE ||
- SrcArgDRE->getDecl() != CompareWithSrcDRE->getDecl())
- return;
-
- const Expr *OriginalSizeArg = Call->getArg(2);
- Diag(CompareWithSrcDRE->getBeginLoc(), diag::warn_strlcpycat_wrong_size)
- << OriginalSizeArg->getSourceRange() << FnName;
-
- // Output a FIXIT hint if the destination is an array (rather than a
- // pointer to an array). This could be enhanced to handle some
- // pointers if we know the actual size, like if DstArg is 'array+2'
- // we could say 'sizeof(array)-2'.
- const Expr *DstArg = Call->getArg(0)->IgnoreParenImpCasts();
- if (!isConstantSizeArrayWithMoreThanOneElement(DstArg->getType(), Context))
- return;
-
- SmallString<128> sizeString;
- llvm::raw_svector_ostream OS(sizeString);
- OS << "sizeof(";
- DstArg->printPretty(OS, nullptr, getPrintingPolicy());
- OS << ")";
-
- Diag(OriginalSizeArg->getBeginLoc(), diag::note_strlcpycat_wrong_size)
- << FixItHint::CreateReplacement(OriginalSizeArg->getSourceRange(),
- OS.str());
-}
-
-/// Check if two expressions refer to the same declaration.
-static bool referToTheSameDecl(const Expr *E1, const Expr *E2) {
- if (const DeclRefExpr *D1 = dyn_cast_or_null<DeclRefExpr>(E1))
- if (const DeclRefExpr *D2 = dyn_cast_or_null<DeclRefExpr>(E2))
- return D1->getDecl() == D2->getDecl();
- return false;
-}
-
-static const Expr *getStrlenExprArg(const Expr *E) {
- if (const CallExpr *CE = dyn_cast<CallExpr>(E)) {
- const FunctionDecl *FD = CE->getDirectCallee();
- if (!FD || FD->getMemoryFunctionKind() != Builtin::BIstrlen)
- return nullptr;
- return CE->getArg(0)->IgnoreParenCasts();
- }
- return nullptr;
-}
-
-void Sema::CheckStrncatArguments(const CallExpr *CE,
- const IdentifierInfo *FnName) {
- // Don't crash if the user has the wrong number of arguments.
- if (CE->getNumArgs() < 3)
- return;
- const Expr *DstArg = CE->getArg(0)->IgnoreParenCasts();
- const Expr *SrcArg = CE->getArg(1)->IgnoreParenCasts();
- const Expr *LenArg = CE->getArg(2)->IgnoreParenCasts();
-
- if (CheckMemorySizeofForComparison(*this, LenArg, FnName, CE->getBeginLoc(),
- CE->getRParenLoc()))
- return;
-
- // Identify common expressions, which are wrongly used as the size argument
- // to strncat and may lead to buffer overflows.
- unsigned PatternType = 0;
- if (const Expr *SizeOfArg = getSizeOfExprArg(LenArg)) {
- // - sizeof(dst)
- if (referToTheSameDecl(SizeOfArg, DstArg))
- PatternType = 1;
- // - sizeof(src)
- else if (referToTheSameDecl(SizeOfArg, SrcArg))
- PatternType = 2;
- } else if (const BinaryOperator *BE = dyn_cast<BinaryOperator>(LenArg)) {
- if (BE->getOpcode() == BO_Sub) {
- const Expr *L = BE->getLHS()->IgnoreParenCasts();
- const Expr *R = BE->getRHS()->IgnoreParenCasts();
- // - sizeof(dst) - strlen(dst)
- if (referToTheSameDecl(DstArg, getSizeOfExprArg(L)) &&
- referToTheSameDecl(DstArg, getStrlenExprArg(R)))
- PatternType = 1;
- // - sizeof(src) - (anything)
- else if (referToTheSameDecl(SrcArg, getSizeOfExprArg(L)))
- PatternType = 2;
- }
- }
-
- if (PatternType == 0)
- return;
-
- // Generate the diagnostic.
- SourceLocation SL = LenArg->getBeginLoc();
- SourceRange SR = LenArg->getSourceRange();
- SourceManager &SM = getSourceManager();
-
- // If the function is defined as a builtin macro, do not show macro expansion.
- if (SM.isMacroArgExpansion(SL)) {
- SL = SM.getSpellingLoc(SL);
- SR = SourceRange(SM.getSpellingLoc(SR.getBegin()),
- SM.getSpellingLoc(SR.getEnd()));
- }
-
- // Check if the destination is an array (rather than a pointer to an array).
- QualType DstTy = DstArg->getType();
- bool isKnownSizeArray = isConstantSizeArrayWithMoreThanOneElement(DstTy,
- Context);
- if (!isKnownSizeArray) {
- if (PatternType == 1)
- Diag(SL, diag::warn_strncat_wrong_size) << SR;
- else
- Diag(SL, diag::warn_strncat_src_size) << SR;
- return;
- }
-
- if (PatternType == 1)
- Diag(SL, diag::warn_strncat_large_size) << SR;
- else
- Diag(SL, diag::warn_strncat_src_size) << SR;
-
- SmallString<128> sizeString;
- llvm::raw_svector_ostream OS(sizeString);
- OS << "sizeof(";
- DstArg->printPretty(OS, nullptr, getPrintingPolicy());
- OS << ") - ";
- OS << "strlen(";
- DstArg->printPretty(OS, nullptr, getPrintingPolicy());
- OS << ") - 1";
-
- Diag(SL, diag::note_strncat_wrong_size)
- << FixItHint::CreateReplacement(SR, OS.str());
-}
-
-namespace {
-void CheckFreeArgumentsOnLvalue(Sema &S, const std::string &CalleeName,
- const UnaryOperator *UnaryExpr, const Decl *D) {
- if (isa<FieldDecl, FunctionDecl, VarDecl>(D)) {
- S.Diag(UnaryExpr->getBeginLoc(), diag::warn_free_nonheap_object)
- << CalleeName << 0 /*object: */ << cast<NamedDecl>(D);
- return;
- }
-}
-
-void CheckFreeArgumentsAddressof(Sema &S, const std::string &CalleeName,
- const UnaryOperator *UnaryExpr) {
- if (const auto *Lvalue = dyn_cast<DeclRefExpr>(UnaryExpr->getSubExpr())) {
- const Decl *D = Lvalue->getDecl();
- if (const auto *DD = dyn_cast<DeclaratorDecl>(D)) {
- if (!DD->getType()->isReferenceType())
- return CheckFreeArgumentsOnLvalue(S, CalleeName, UnaryExpr, D);
- }
- }
-
- if (const auto *Lvalue = dyn_cast<MemberExpr>(UnaryExpr->getSubExpr()))
- return CheckFreeArgumentsOnLvalue(S, CalleeName, UnaryExpr,
- Lvalue->getMemberDecl());
-}
-
-void CheckFreeArgumentsPlus(Sema &S, const std::string &CalleeName,
- const UnaryOperator *UnaryExpr) {
- const auto *Lambda = dyn_cast<LambdaExpr>(
- UnaryExpr->getSubExpr()->IgnoreImplicitAsWritten()->IgnoreParens());
- if (!Lambda)
- return;
-
- S.Diag(Lambda->getBeginLoc(), diag::warn_free_nonheap_object)
- << CalleeName << 2 /*object: lambda expression*/;
-}
-
-void CheckFreeArgumentsStackArray(Sema &S, const std::string &CalleeName,
- const DeclRefExpr *Lvalue) {
- const auto *Var = dyn_cast<VarDecl>(Lvalue->getDecl());
- if (Var == nullptr)
- return;
-
- S.Diag(Lvalue->getBeginLoc(), diag::warn_free_nonheap_object)
- << CalleeName << 0 /*object: */ << Var;
-}
-
-void CheckFreeArgumentsCast(Sema &S, const std::string &CalleeName,
- const CastExpr *Cast) {
- SmallString<128> SizeString;
- llvm::raw_svector_ostream OS(SizeString);
-
- clang::CastKind Kind = Cast->getCastKind();
- if (Kind == clang::CK_BitCast &&
- !Cast->getSubExpr()->getType()->isFunctionPointerType())
- return;
- if (Kind == clang::CK_IntegralToPointer &&
- !isa<IntegerLiteral>(
- Cast->getSubExpr()->IgnoreParenImpCasts()->IgnoreParens()))
- return;
-
- switch (Cast->getCastKind()) {
- case clang::CK_BitCast:
- case clang::CK_IntegralToPointer:
- case clang::CK_FunctionToPointerDecay:
- OS << '\'';
- Cast->printPretty(OS, nullptr, S.getPrintingPolicy());
- OS << '\'';
- break;
- default:
- return;
- }
-
- S.Diag(Cast->getBeginLoc(), diag::warn_free_nonheap_object)
- << CalleeName << 0 /*object: */ << OS.str();
-}
-} // namespace
-
-void Sema::CheckFreeArguments(const CallExpr *E) {
- const std::string CalleeName =
- cast<FunctionDecl>(E->getCalleeDecl())->getQualifiedNameAsString();
-
- { // Prefer something that doesn't involve a cast to make things simpler.
- const Expr *Arg = E->getArg(0)->IgnoreParenCasts();
- if (const auto *UnaryExpr = dyn_cast<UnaryOperator>(Arg))
- switch (UnaryExpr->getOpcode()) {
- case UnaryOperator::Opcode::UO_AddrOf:
- return CheckFreeArgumentsAddressof(*this, CalleeName, UnaryExpr);
- case UnaryOperator::Opcode::UO_Plus:
- return CheckFreeArgumentsPlus(*this, CalleeName, UnaryExpr);
- default:
- break;
- }
-
- if (const auto *Lvalue = dyn_cast<DeclRefExpr>(Arg))
- if (Lvalue->getType()->isArrayType())
- return CheckFreeArgumentsStackArray(*this, CalleeName, Lvalue);
-
- if (const auto *Label = dyn_cast<AddrLabelExpr>(Arg)) {
- Diag(Label->getBeginLoc(), diag::warn_free_nonheap_object)
- << CalleeName << 0 /*object: */ << Label->getLabel()->getIdentifier();
- return;
- }
-
- if (isa<BlockExpr>(Arg)) {
- Diag(Arg->getBeginLoc(), diag::warn_free_nonheap_object)
- << CalleeName << 1 /*object: block*/;
- return;
- }
- }
- // Maybe the cast was important, check after the other cases.
- if (const auto *Cast = dyn_cast<CastExpr>(E->getArg(0)))
- return CheckFreeArgumentsCast(*this, CalleeName, Cast);
-}
-
-void
-Sema::CheckReturnValExpr(Expr *RetValExp, QualType lhsType,
- SourceLocation ReturnLoc,
- bool isObjCMethod,
- const AttrVec *Attrs,
- const FunctionDecl *FD) {
- // Check if the return value is null but should not be.
- if (((Attrs && hasSpecificAttr<ReturnsNonNullAttr>(*Attrs)) ||
- (!isObjCMethod && isNonNullType(lhsType))) &&
- CheckNonNullExpr(*this, RetValExp))
- Diag(ReturnLoc, diag::warn_null_ret)
- << (isObjCMethod ? 1 : 0) << RetValExp->getSourceRange();
-
- // C++11 [basic.stc.dynamic.allocation]p4:
- // If an allocation function declared with a non-throwing
- // exception-specification fails to allocate storage, it shall return
- // a null pointer. Any other allocation function that fails to allocate
- // storage shall indicate failure only by throwing an exception [...]
- if (FD) {
- OverloadedOperatorKind Op = FD->getOverloadedOperator();
- if (Op == OO_New || Op == OO_Array_New) {
- const FunctionProtoType *Proto
- = FD->getType()->castAs<FunctionProtoType>();
- if (!Proto->isNothrow(/*ResultIfDependent*/true) &&
- CheckNonNullExpr(*this, RetValExp))
- Diag(ReturnLoc, diag::warn_operator_new_returns_null)
- << FD << getLangOpts().CPlusPlus11;
- }
- }
-
- if (RetValExp && RetValExp->getType()->isWebAssemblyTableType()) {
- Diag(ReturnLoc, diag::err_wasm_table_art) << 1;
- }
-
- // PPC MMA non-pointer types are not allowed as return type. Checking the type
- // here prevent the user from using a PPC MMA type as trailing return type.
- if (Context.getTargetInfo().getTriple().isPPC64())
- PPC().CheckPPCMMAType(RetValExp->getType(), ReturnLoc);
-}
-
-void Sema::CheckFloatComparison(SourceLocation Loc, const Expr *LHS,
- const Expr *RHS, BinaryOperatorKind Opcode) {
- if (!BinaryOperator::isEqualityOp(Opcode))
- return;
-
- // Match and capture subexpressions such as "(float) X == 0.1".
- const FloatingLiteral *FPLiteral;
- const CastExpr *FPCast;
- auto getCastAndLiteral = [&FPLiteral, &FPCast](const Expr *L, const Expr *R) {
- FPLiteral = dyn_cast<FloatingLiteral>(L->IgnoreParens());
- FPCast = dyn_cast<CastExpr>(R->IgnoreParens());
- return FPLiteral && FPCast;
- };
-
- if (getCastAndLiteral(LHS, RHS) || getCastAndLiteral(RHS, LHS)) {
- auto *SourceTy = FPCast->getSubExpr()->getType()->getAs<BuiltinType>();
- auto *TargetTy = FPLiteral->getType()->getAs<BuiltinType>();
- if (SourceTy && TargetTy && SourceTy->isFloatingPoint() &&
- TargetTy->isFloatingPoint()) {
- bool Lossy;
- llvm::APFloat TargetC = FPLiteral->getValue();
- TargetC.convert(Context.getFloatTypeSemantics(QualType(SourceTy, 0)),
- llvm::APFloat::rmNearestTiesToEven, &Lossy);
- if (Lossy) {
- // If the literal cannot be represented in the source type, then a
- // check for == is always false and check for != is always true.
- Diag(Loc, diag::warn_float_compare_literal)
- << (Opcode == BO_EQ) << QualType(SourceTy, 0)
- << LHS->getSourceRange() << RHS->getSourceRange();
- return;
- }
- }
- }
-
- // Match a more general floating-point equality comparison (-Wfloat-equal).
- const Expr *LeftExprSansParen = LHS->IgnoreParenImpCasts();
- const Expr *RightExprSansParen = RHS->IgnoreParenImpCasts();
-
- // Special case: check for x == x (which is OK).
- // Do not emit warnings for such cases.
- if (const auto *DRL = dyn_cast<DeclRefExpr>(LeftExprSansParen))
- if (const auto *DRR = dyn_cast<DeclRefExpr>(RightExprSansParen))
- if (DRL->getDecl() == DRR->getDecl())
- return;
-
- // Special case: check for comparisons against literals that can be exactly
- // represented by APFloat. In such cases, do not emit a warning. This
- // is a heuristic: often comparison against such literals are used to
- // detect if a value in a variable has not changed. This clearly can
- // lead to false negatives.
- if (const auto *FLL = dyn_cast<FloatingLiteral>(LeftExprSansParen)) {
- if (FLL->isExact())
- return;
- } else if (const auto *FLR = dyn_cast<FloatingLiteral>(RightExprSansParen))
- if (FLR->isExact())
- return;
-
- // Check for comparisons with builtin types.
- if (const auto *CL = dyn_cast<CallExpr>(LeftExprSansParen);
- CL && CL->getBuiltinCallee())
- return;
-
- if (const auto *CR = dyn_cast<CallExpr>(RightExprSansParen);
- CR && CR->getBuiltinCallee())
- return;
-
- // Emit the diagnostic.
- Diag(Loc, diag::warn_floatingpoint_eq)
- << LHS->getSourceRange() << RHS->getSourceRange();
-}
-
-//===--- CHECK: Integer mixed-sign comparisons (-Wsign-compare) --------===//
-//===--- CHECK: Lossy implicit conversions (-Wconversion) --------------===//
-
-namespace {
-
-/// Structure recording the 'active' range of an integer-valued
-/// expression.
-struct IntRange {
- /// The number of bits active in the int. Note that this includes exactly one
- /// sign bit if !NonNegative.
- unsigned Width;
-
- /// True if the int is known not to have negative values. If so, all leading
- /// bits before Width are known zero, otherwise they are known to be the
- /// same as the MSB within Width.
- bool NonNegative;
-
- IntRange(unsigned Width, bool NonNegative)
- : Width(Width), NonNegative(NonNegative) {}
-
- /// Number of bits excluding the sign bit.
- unsigned valueBits() const {
- return NonNegative ? Width : Width - 1;
- }
-
- /// Returns the range of the bool type.
- static IntRange forBoolType() {
- return IntRange(1, true);
- }
-
- /// Returns the range of an opaque value of the given integral type.
- static IntRange forValueOfType(ASTContext &C, QualType T) {
- return forValueOfCanonicalType(C,
- T->getCanonicalTypeInternal().getTypePtr());
- }
-
- /// Returns the range of an opaque value of a canonical integral type.
- static IntRange forValueOfCanonicalType(ASTContext &C, const Type *T) {
- assert(T->isCanonicalUnqualified());
-
- if (const auto *VT = dyn_cast<VectorType>(T))
- T = VT->getElementType().getTypePtr();
- if (const auto *CT = dyn_cast<ComplexType>(T))
- T = CT->getElementType().getTypePtr();
- if (const auto *AT = dyn_cast<AtomicType>(T))
- T = AT->getValueType().getTypePtr();
-
- if (!C.getLangOpts().CPlusPlus) {
- // For enum types in C code, use the underlying datatype.
- if (const auto *ED = T->getAsEnumDecl())
- T = ED->getIntegerType().getDesugaredType(C).getTypePtr();
- } else if (auto *Enum = T->getAsEnumDecl()) {
- // For enum types in C++, use the known bit width of the enumerators.
- // In C++11, enums can have a fixed underlying type. Use this type to
- // compute the range.
- if (Enum->isFixed()) {
- return IntRange(C.getIntWidth(QualType(T, 0)),
- !Enum->getIntegerType()->isSignedIntegerType());
- }
-
- unsigned NumPositive = Enum->getNumPositiveBits();
- unsigned NumNegative = Enum->getNumNegativeBits();
-
- if (NumNegative == 0)
- return IntRange(NumPositive, true/*NonNegative*/);
- else
- return IntRange(std::max(NumPositive + 1, NumNegative),
- false/*NonNegative*/);
- }
-
- if (const auto *EIT = dyn_cast<BitIntType>(T))
- return IntRange(EIT->getNumBits(), EIT->isUnsigned());
-
- const BuiltinType *BT = cast<BuiltinType>(T);
- assert(BT->isInteger());
-
- return IntRange(C.getIntWidth(QualType(T, 0)), BT->isUnsignedInteger());
- }
-
- /// Returns the "target" range of a canonical integral type, i.e.
- /// the range of values expressible in the type.
- ///
- /// This matches forValueOfCanonicalType except that enums have the
- /// full range of their type, not the range of their enumerators.
- static IntRange forTargetOfCanonicalType(ASTContext &C, const Type *T) {
- assert(T->isCanonicalUnqualified());
-
- if (const VectorType *VT = dyn_cast<VectorType>(T))
- T = VT->getElementType().getTypePtr();
- if (const ComplexType *CT = dyn_cast<ComplexType>(T))
- T = CT->getElementType().getTypePtr();
- if (const AtomicType *AT = dyn_cast<AtomicType>(T))
- T = AT->getValueType().getTypePtr();
- if (const auto *ED = T->getAsEnumDecl())
- T = C.getCanonicalType(ED->getIntegerType()).getTypePtr();
-
- if (const auto *EIT = dyn_cast<BitIntType>(T))
- return IntRange(EIT->getNumBits(), EIT->isUnsigned());
-
- const BuiltinType *BT = cast<BuiltinType>(T);
- assert(BT->isInteger());
-
- return IntRange(C.getIntWidth(QualType(T, 0)), BT->isUnsignedInteger());
- }
-
- /// Returns the supremum of two ranges: i.e. their conservative merge.
- static IntRange join(IntRange L, IntRange R) {
- bool Unsigned = L.NonNegative && R.NonNegative;
- return IntRange(std::max(L.valueBits(), R.valueBits()) + !Unsigned,
- L.NonNegative && R.NonNegative);
- }
-
- /// Return the range of a bitwise-AND of the two ranges.
- static IntRange bit_and(IntRange L, IntRange R) {
- unsigned Bits = std::max(L.Width, R.Width);
- bool NonNegative = false;
- if (L.NonNegative) {
- Bits = std::min(Bits, L.Width);
- NonNegative = true;
- }
- if (R.NonNegative) {
- Bits = std::min(Bits, R.Width);
- NonNegative = true;
- }
- return IntRange(Bits, NonNegative);
- }
-
- /// Return the range of a sum of the two ranges.
- static IntRange sum(IntRange L, IntRange R) {
- bool Unsigned = L.NonNegative && R.NonNegative;
- return IntRange(std::max(L.valueBits(), R.valueBits()) + 1 + !Unsigned,
- Unsigned);
- }
-
- /// Return the range of a difference of the two ranges.
- static IntRange difference(IntRange L, IntRange R) {
- // We need a 1-bit-wider range if:
- // 1) LHS can be negative: least value can be reduced.
- // 2) RHS can be negative: greatest value can be increased.
- bool CanWiden = !L.NonNegative || !R.NonNegative;
- bool Unsigned = L.NonNegative && R.Width == 0;
- return IntRange(std::max(L.valueBits(), R.valueBits()) + CanWiden +
- !Unsigned,
- Unsigned);
- }
-
- /// Return the range of a product of the two ranges.
- static IntRange product(IntRange L, IntRange R) {
- // If both LHS and RHS can be negative, we can form
- // -2^L * -2^R = 2^(L + R)
- // which requires L + R + 1 value bits to represent.
- bool CanWiden = !L.NonNegative && !R.NonNegative;
- bool Unsigned = L.NonNegative && R.NonNegative;
- return IntRange(L.valueBits() + R.valueBits() + CanWiden + !Unsigned,
- Unsigned);
- }
-
- /// Return the range of a remainder operation between the two ranges.
- static IntRange rem(IntRange L, IntRange R) {
- // The result of a remainder can't be larger than the result of
- // either side. The sign of the result is the sign of the LHS.
- bool Unsigned = L.NonNegative;
- return IntRange(std::min(L.valueBits(), R.valueBits()) + !Unsigned,
- Unsigned);
- }
-};
-
-} // namespace
-
-static IntRange GetValueRange(llvm::APSInt &value, unsigned MaxWidth) {
- if (value.isSigned() && value.isNegative())
- return IntRange(value.getSignificantBits(), false);
-
- if (value.getBitWidth() > MaxWidth)
- value = value.trunc(MaxWidth);
-
- // isNonNegative() just checks the sign bit without considering
- // signedness.
- return IntRange(value.getActiveBits(), true);
-}
-
-static IntRange GetValueRange(APValue &result, QualType Ty, unsigned MaxWidth) {
- if (result.isInt())
- return GetValueRange(result.getInt(), MaxWidth);
-
- if (result.isVector()) {
- IntRange R = GetValueRange(result.getVectorElt(0), Ty, MaxWidth);
- for (unsigned i = 1, e = result.getVectorLength(); i != e; ++i) {
- IntRange El = GetValueRange(result.getVectorElt(i), Ty, MaxWidth);
- R = IntRange::join(R, El);
- }
- return R;
- }
-
- if (result.isComplexInt()) {
- IntRange R = GetValueRange(result.getComplexIntReal(), MaxWidth);
- IntRange I = GetValueRange(result.getComplexIntImag(), MaxWidth);
- return IntRange::join(R, I);
- }
-
- // This can happen with lossless casts to intptr_t of "based" lvalues.
- // Assume it might use arbitrary bits.
- // FIXME: The only reason we need to pass the type in here is to get
- // the sign right on this one case. It would be nice if APValue
- // preserved this.
- assert(result.isLValue() || result.isAddrLabelDiff());
- return IntRange(MaxWidth, Ty->isUnsignedIntegerOrEnumerationType());
-}
-
-static QualType GetExprType(const Expr *E) {
- QualType Ty = E->getType();
- if (const auto *AtomicRHS = Ty->getAs<AtomicType>())
- Ty = AtomicRHS->getValueType();
- return Ty;
-}
-
-/// Attempts to estimate an approximate range for the given integer expression.
-/// Returns a range if successful, otherwise it returns \c std::nullopt if a
-/// reliable estimation cannot be determined.
-///
-/// \param MaxWidth The width to which the value will be truncated.
-/// \param InConstantContext If \c true, interpret the expression within a
-/// constant context.
-/// \param Approximate If \c true, provide a likely range of values by assuming
-/// that arithmetic on narrower types remains within those types.
-/// If \c false, return a range that includes all possible values
-/// resulting from the expression.
-/// \returns A range of values that the expression might take, or
-/// std::nullopt if a reliable estimation cannot be determined.
-static std::optional<IntRange> TryGetExprRange(ASTContext &C, const Expr *E,
- unsigned MaxWidth,
- bool InConstantContext,
- bool Approximate) {
- E = E->IgnoreParens();
-
- // Try a full evaluation first.
- Expr::EvalResult result;
- if (E->EvaluateAsRValue(result, C, InConstantContext))
- return GetValueRange(result.Val, GetExprType(E), MaxWidth);
-
- // I think we only want to look through implicit casts here; if the
- // user has an explicit widening cast, we should treat the value as
- // being of the new, wider type.
- if (const auto *CE = dyn_cast<ImplicitCastExpr>(E)) {
- if (CE->getCastKind() == CK_NoOp || CE->getCastKind() == CK_LValueToRValue)
- return TryGetExprRange(C, CE->getSubExpr(), MaxWidth, InConstantContext,
- Approximate);
-
- IntRange OutputTypeRange = IntRange::forValueOfType(C, GetExprType(CE));
-
- bool isIntegerCast = CE->getCastKind() == CK_IntegralCast ||
- CE->getCastKind() == CK_BooleanToSignedIntegral;
-
- // Assume that non-integer casts can span the full range of the type.
- if (!isIntegerCast)
- return OutputTypeRange;
-
- std::optional<IntRange> SubRange = TryGetExprRange(
- C, CE->getSubExpr(), std::min(MaxWidth, OutputTypeRange.Width),
- InConstantContext, Approximate);
- if (!SubRange)
- return std::nullopt;
-
- // Bail out if the subexpr's range is as wide as the cast type.
- if (SubRange->Width >= OutputTypeRange.Width)
- return OutputTypeRange;
-
- // Otherwise, we take the smaller width, and we're non-negative if
- // either the output type or the subexpr is.
- return IntRange(SubRange->Width,
- SubRange->NonNegative || OutputTypeRange.NonNegative);
- }
-
- if (const auto *CO = dyn_cast<ConditionalOperator>(E)) {
- // If we can fold the condition, just take that operand.
- bool CondResult;
- if (CO->getCond()->EvaluateAsBooleanCondition(CondResult, C))
- return TryGetExprRange(
- C, CondResult ? CO->getTrueExpr() : CO->getFalseExpr(), MaxWidth,
- InConstantContext, Approximate);
-
- // Otherwise, conservatively merge.
- // TryGetExprRange requires an integer expression, but a throw expression
- // results in a void type.
- Expr *TrueExpr = CO->getTrueExpr();
- if (TrueExpr->getType()->isVoidType())
- return std::nullopt;
-
- std::optional<IntRange> L =
- TryGetExprRange(C, TrueExpr, MaxWidth, InConstantContext, Approximate);
- if (!L)
- return std::nullopt;
-
- Expr *FalseExpr = CO->getFalseExpr();
- if (FalseExpr->getType()->isVoidType())
- return std::nullopt;
-
- std::optional<IntRange> R =
- TryGetExprRange(C, FalseExpr, MaxWidth, InConstantContext, Approximate);
- if (!R)
- return std::nullopt;
-
- return IntRange::join(*L, *R);
- }
-
- if (const auto *BO = dyn_cast<BinaryOperator>(E)) {
- IntRange (*Combine)(IntRange, IntRange) = IntRange::join;
-
- switch (BO->getOpcode()) {
- case BO_Cmp:
- llvm_unreachable("builtin <=> should have class type");
-
- // Boolean-valued operations are single-bit and positive.
- case BO_LAnd:
- case BO_LOr:
- case BO_LT:
- case BO_GT:
- case BO_LE:
- case BO_GE:
- case BO_EQ:
- case BO_NE:
- return IntRange::forBoolType();
-
- // The type of the assignments is the type of the LHS, so the RHS
- // is not necessarily the same type.
- case BO_MulAssign:
- case BO_DivAssign:
- case BO_RemAssign:
- case BO_AddAssign:
- case BO_SubAssign:
- case BO_XorAssign:
- case BO_OrAssign:
- // TODO: bitfields?
- return IntRange::forValueOfType(C, GetExprType(E));
-
- // Simple assignments just pass through the RHS, which will have
- // been coerced to the LHS type.
- case BO_Assign:
- // TODO: bitfields?
- return TryGetExprRange(C, BO->getRHS(), MaxWidth, InConstantContext,
- Approximate);
-
- // Operations with opaque sources are black-listed.
- case BO_PtrMemD:
- case BO_PtrMemI:
- return IntRange::forValueOfType(C, GetExprType(E));
-
- // Bitwise-and uses the *infinum* of the two source ranges.
- case BO_And:
- case BO_AndAssign:
- Combine = IntRange::bit_and;
- break;
-
- // Left shift gets black-listed based on a judgement call.
- case BO_Shl:
- // ...except that we want to treat '1 << (blah)' as logically
- // positive. It's an important idiom.
- if (IntegerLiteral *I
- = dyn_cast<IntegerLiteral>(BO->getLHS()->IgnoreParenCasts())) {
- if (I->getValue() == 1) {
- IntRange R = IntRange::forValueOfType(C, GetExprType(E));
- return IntRange(R.Width, /*NonNegative*/ true);
- }
- }
- [[fallthrough]];
-
- case BO_ShlAssign:
- return IntRange::forValueOfType(C, GetExprType(E));
-
- // Right shift by a constant can narrow its left argument.
- case BO_Shr:
- case BO_ShrAssign: {
- std::optional<IntRange> L = TryGetExprRange(
- C, BO->getLHS(), MaxWidth, InConstantContext, Approximate);
- if (!L)
- return std::nullopt;
-
- // If the shift amount is a positive constant, drop the width by
- // that much.
- if (std::optional<llvm::APSInt> shift =
- BO->getRHS()->getIntegerConstantExpr(C)) {
- if (shift->isNonNegative()) {
- if (shift->uge(L->Width))
- L->Width = (L->NonNegative ? 0 : 1);
- else
- L->Width -= shift->getZExtValue();
- }
- }
-
- return L;
- }
-
- // Comma acts as its right operand.
- case BO_Comma:
- return TryGetExprRange(C, BO->getRHS(), MaxWidth, InConstantContext,
- Approximate);
-
- case BO_Add:
- if (!Approximate)
- Combine = IntRange::sum;
- break;
-
- case BO_Sub:
- if (BO->getLHS()->getType()->isPointerType())
- return IntRange::forValueOfType(C, GetExprType(E));
- if (!Approximate)
- Combine = IntRange::difference;
- break;
-
- case BO_Mul:
- if (!Approximate)
- Combine = IntRange::product;
- break;
-
- // The width of a division result is mostly determined by the size
- // of the LHS.
- case BO_Div: {
- // Don't 'pre-truncate' the operands.
- unsigned opWidth = C.getIntWidth(GetExprType(E));
- std::optional<IntRange> L = TryGetExprRange(
- C, BO->getLHS(), opWidth, InConstantContext, Approximate);
- if (!L)
- return std::nullopt;
-
- // If the divisor is constant, use that.
- if (std::optional<llvm::APSInt> divisor =
- BO->getRHS()->getIntegerConstantExpr(C)) {
- unsigned log2 = divisor->logBase2(); // floor(log_2(divisor))
- if (log2 >= L->Width)
- L->Width = (L->NonNegative ? 0 : 1);
- else
- L->Width = std::min(L->Width - log2, MaxWidth);
- return L;
- }
-
- // Otherwise, just use the LHS's width.
- // FIXME: This is wrong if the LHS could be its minimal value and the RHS
- // could be -1.
- std::optional<IntRange> R = TryGetExprRange(
- C, BO->getRHS(), opWidth, InConstantContext, Approximate);
- if (!R)
- return std::nullopt;
-
- return IntRange(L->Width, L->NonNegative && R->NonNegative);
- }
-
- case BO_Rem:
- Combine = IntRange::rem;
- break;
-
- // The default behavior is okay for these.
- case BO_Xor:
- case BO_Or:
- break;
- }
-
- // Combine the two ranges, but limit the result to the type in which we
- // performed the computation.
- QualType T = GetExprType(E);
- unsigned opWidth = C.getIntWidth(T);
- std::optional<IntRange> L = TryGetExprRange(C, BO->getLHS(), opWidth,
- InConstantContext, Approximate);
- if (!L)
- return std::nullopt;
-
- std::optional<IntRange> R = TryGetExprRange(C, BO->getRHS(), opWidth,
- InConstantContext, Approximate);
- if (!R)
- return std::nullopt;
-
- IntRange C = Combine(*L, *R);
- C.NonNegative |= T->isUnsignedIntegerOrEnumerationType();
- C.Width = std::min(C.Width, MaxWidth);
- return C;
- }
-
- if (const auto *UO = dyn_cast<UnaryOperator>(E)) {
- switch (UO->getOpcode()) {
- // Boolean-valued operations are white-listed.
- case UO_LNot:
- return IntRange::forBoolType();
-
- // Operations with opaque sources are black-listed.
- case UO_Deref:
- case UO_AddrOf: // should be impossible
- return IntRange::forValueOfType(C, GetExprType(E));
-
- case UO_Minus: {
- if (E->getType()->isUnsignedIntegerType()) {
- return TryGetExprRange(C, UO->getSubExpr(), MaxWidth, InConstantContext,
- Approximate);
- }
-
- std::optional<IntRange> SubRange = TryGetExprRange(
- C, UO->getSubExpr(), MaxWidth, InConstantContext, Approximate);
-
- if (!SubRange)
- return std::nullopt;
-
- // If the range was previously non-negative, we need an extra bit for the
- // sign bit. Otherwise, we need an extra bit because the negation of the
- // most-negative value is one bit wider than that value.
- return IntRange(std::min(SubRange->Width + 1, MaxWidth), false);
- }
-
- case UO_Not: {
- if (E->getType()->isUnsignedIntegerType()) {
- return TryGetExprRange(C, UO->getSubExpr(), MaxWidth, InConstantContext,
- Approximate);
- }
-
- std::optional<IntRange> SubRange = TryGetExprRange(
- C, UO->getSubExpr(), MaxWidth, InConstantContext, Approximate);
-
- if (!SubRange)
- return std::nullopt;
-
- // The width increments by 1 if the sub-expression cannot be negative
- // since it now can be.
- return IntRange(
- std::min(SubRange->Width + (int)SubRange->NonNegative, MaxWidth),
- false);
- }
-
- default:
- return TryGetExprRange(C, UO->getSubExpr(), MaxWidth, InConstantContext,
- Approximate);
- }
- }
-
- if (const auto *OVE = dyn_cast<OpaqueValueExpr>(E))
- return TryGetExprRange(C, OVE->getSourceExpr(), MaxWidth, InConstantContext,
- Approximate);
-
- if (const auto *BitField = E->getSourceBitField())
- return IntRange(BitField->getBitWidthValue(),
- BitField->getType()->isUnsignedIntegerOrEnumerationType());
-
- if (GetExprType(E)->isVoidType())
- return std::nullopt;
-
- return IntRange::forValueOfType(C, GetExprType(E));
-}
-
-static std::optional<IntRange> TryGetExprRange(ASTContext &C, const Expr *E,
- bool InConstantContext,
- bool Approximate) {
- return TryGetExprRange(C, E, C.getIntWidth(GetExprType(E)), InConstantContext,
- Approximate);
-}
-
-/// Checks whether the given value, which currently has the given
-/// source semantics, has the same value when coerced through the
-/// target semantics.
-static bool IsSameFloatAfterCast(const llvm::APFloat &value,
- const llvm::fltSemantics &Src,
- const llvm::fltSemantics &Tgt) {
- llvm::APFloat truncated = value;
-
- bool ignored;
- truncated.convert(Src, llvm::APFloat::rmNearestTiesToEven, &ignored);
- truncated.convert(Tgt, llvm::APFloat::rmNearestTiesToEven, &ignored);
-
- return truncated.bitwiseIsEqual(value);
-}
-
-/// Checks whether the given value, which currently has the given
-/// source semantics, has the same value when coerced through the
-/// target semantics.
-///
-/// The value might be a vector of floats (or a complex number).
-static bool IsSameFloatAfterCast(const APValue &value,
- const llvm::fltSemantics &Src,
- const llvm::fltSemantics &Tgt) {
- if (value.isFloat())
- return IsSameFloatAfterCast(value.getFloat(), Src, Tgt);
-
- if (value.isVector()) {
- for (unsigned i = 0, e = value.getVectorLength(); i != e; ++i)
- if (!IsSameFloatAfterCast(value.getVectorElt(i), Src, Tgt))
- return false;
- return true;
- }
-
- assert(value.isComplexFloat());
- return (IsSameFloatAfterCast(value.getComplexFloatReal(), Src, Tgt) &&
- IsSameFloatAfterCast(value.getComplexFloatImag(), Src, Tgt));
-}
-
-static void AnalyzeImplicitConversions(Sema &S, Expr *E, SourceLocation CC,
- bool IsListInit = false);
-
-static bool IsEnumConstOrFromMacro(Sema &S, const Expr *E) {
- // Suppress cases where we are comparing against an enum constant.
- if (const auto *DR = dyn_cast<DeclRefExpr>(E->IgnoreParenImpCasts()))
- if (isa<EnumConstantDecl>(DR->getDecl()))
- return true;
-
- // Suppress cases where the value is expanded from a macro, unless that macro
- // is how a language represents a boolean literal. This is the case in both C
- // and Objective-C.
- SourceLocation BeginLoc = E->getBeginLoc();
- if (BeginLoc.isMacroID()) {
- StringRef MacroName = Lexer::getImmediateMacroName(
- BeginLoc, S.getSourceManager(), S.getLangOpts());
- return MacroName != "YES" && MacroName != "NO" &&
- MacroName != "true" && MacroName != "false";
- }
-
- return false;
-}
-
-static bool isKnownToHaveUnsignedValue(const Expr *E) {
- return E->getType()->isIntegerType() &&
- (!E->getType()->isSignedIntegerType() ||
- !E->IgnoreParenImpCasts()->getType()->isSignedIntegerType());
-}
-
-namespace {
-/// The promoted range of values of a type. In general this has the
-/// following structure:
-///
-/// |-----------| . . . |-----------|
-/// ^ ^ ^ ^
-/// Min HoleMin HoleMax Max
-///
-/// ... where there is only a hole if a signed type is promoted to unsigned
-/// (in which case Min and Max are the smallest and largest representable
-/// values).
-struct PromotedRange {
- // Min, or HoleMax if there is a hole.
- llvm::APSInt PromotedMin;
- // Max, or HoleMin if there is a hole.
- llvm::APSInt PromotedMax;
-
- PromotedRange(IntRange R, unsigned BitWidth, bool Unsigned) {
- if (R.Width == 0)
- PromotedMin = PromotedMax = llvm::APSInt(BitWidth, Unsigned);
- else if (R.Width >= BitWidth && !Unsigned) {
- // Promotion made the type *narrower*. This happens when promoting
- // a < 32-bit unsigned / <= 32-bit signed bit-field to 'signed int'.
- // Treat all values of 'signed int' as being in range for now.
- PromotedMin = llvm::APSInt::getMinValue(BitWidth, Unsigned);
- PromotedMax = llvm::APSInt::getMaxValue(BitWidth, Unsigned);
- } else {
- PromotedMin = llvm::APSInt::getMinValue(R.Width, R.NonNegative)
- .extOrTrunc(BitWidth);
- PromotedMin.setIsUnsigned(Unsigned);
-
- PromotedMax = llvm::APSInt::getMaxValue(R.Width, R.NonNegative)
- .extOrTrunc(BitWidth);
- PromotedMax.setIsUnsigned(Unsigned);
- }
- }
-
- // Determine whether this range is contiguous (has no hole).
- bool isContiguous() const { return PromotedMin <= PromotedMax; }
-
- // Where a constant value is within the range.
- enum ComparisonResult {
- LT = 0x1,
- LE = 0x2,
- GT = 0x4,
- GE = 0x8,
- EQ = 0x10,
- NE = 0x20,
- InRangeFlag = 0x40,
-
- Less = LE | LT | NE,
- Min = LE | InRangeFlag,
- InRange = InRangeFlag,
- Max = GE | InRangeFlag,
- Greater = GE | GT | NE,
-
- OnlyValue = LE | GE | EQ | InRangeFlag,
- InHole = NE
- };
-
- ComparisonResult compare(const llvm::APSInt &Value) const {
- assert(Value.getBitWidth() == PromotedMin.getBitWidth() &&
- Value.isUnsigned() == PromotedMin.isUnsigned());
- if (!isContiguous()) {
- assert(Value.isUnsigned() && "discontiguous range for signed compare");
- if (Value.isMinValue()) return Min;
- if (Value.isMaxValue()) return Max;
- if (Value >= PromotedMin) return InRange;
- if (Value <= PromotedMax) return InRange;
- return InHole;
- }
-
- switch (llvm::APSInt::compareValues(Value, PromotedMin)) {
- case -1: return Less;
- case 0: return PromotedMin == PromotedMax ? OnlyValue : Min;
- case 1:
- switch (llvm::APSInt::compareValues(Value, PromotedMax)) {
- case -1: return InRange;
- case 0: return Max;
- case 1: return Greater;
- }
- }
-
- llvm_unreachable("impossible compare result");
- }
-
- static std::optional<StringRef>
- constantValue(BinaryOperatorKind Op, ComparisonResult R, bool ConstantOnRHS) {
- if (Op == BO_Cmp) {
- ComparisonResult LTFlag = LT, GTFlag = GT;
- if (ConstantOnRHS) std::swap(LTFlag, GTFlag);
-
- if (R & EQ) return StringRef("'std::strong_ordering::equal'");
- if (R & LTFlag) return StringRef("'std::strong_ordering::less'");
- if (R & GTFlag) return StringRef("'std::strong_ordering::greater'");
- return std::nullopt;
- }
-
- ComparisonResult TrueFlag, FalseFlag;
- if (Op == BO_EQ) {
- TrueFlag = EQ;
- FalseFlag = NE;
- } else if (Op == BO_NE) {
- TrueFlag = NE;
- FalseFlag = EQ;
- } else {
- if ((Op == BO_LT || Op == BO_GE) ^ ConstantOnRHS) {
- TrueFlag = LT;
- FalseFlag = GE;
- } else {
- TrueFlag = GT;
- FalseFlag = LE;
- }
- if (Op == BO_GE || Op == BO_LE)
- std::swap(TrueFlag, FalseFlag);
- }
- if (R & TrueFlag)
- return StringRef("true");
- if (R & FalseFlag)
- return StringRef("false");
- return std::nullopt;
- }
-};
-}
-
-static bool HasEnumType(const Expr *E) {
- // Strip off implicit integral promotions.
- while (const auto *ICE = dyn_cast<ImplicitCastExpr>(E)) {
- if (ICE->getCastKind() != CK_IntegralCast &&
- ICE->getCastKind() != CK_NoOp)
- break;
- E = ICE->getSubExpr();
- }
-
- return E->getType()->isEnumeralType();
-}
-
-static int classifyConstantValue(Expr *Constant) {
- // The values of this enumeration are used in the diagnostics
- // diag::warn_out_of_range_compare and diag::warn_tautological_bool_compare.
- enum ConstantValueKind {
- Miscellaneous = 0,
- LiteralTrue,
- LiteralFalse
- };
- if (auto *BL = dyn_cast<CXXBoolLiteralExpr>(Constant))
- return BL->getValue() ? ConstantValueKind::LiteralTrue
- : ConstantValueKind::LiteralFalse;
- return ConstantValueKind::Miscellaneous;
-}
-
-static bool CheckTautologicalComparison(Sema &S, BinaryOperator *E,
- Expr *Constant, Expr *Other,
- const llvm::APSInt &Value,
- bool RhsConstant) {
- if (S.inTemplateInstantiation())
- return false;
-
- Expr *OriginalOther = Other;
-
- Constant = Constant->IgnoreParenImpCasts();
- Other = Other->IgnoreParenImpCasts();
-
- // Suppress warnings on tautological comparisons between values of the same
- // enumeration type. There are only two ways we could warn on this:
- // - If the constant is outside the range of representable values of
- // the enumeration. In such a case, we should warn about the cast
- // to enumeration type, not about the comparison.
- // - If the constant is the maximum / minimum in-range value. For an
- // enumeratin type, such comparisons can be meaningful and useful.
- if (Constant->getType()->isEnumeralType() &&
- S.Context.hasSameUnqualifiedType(Constant->getType(), Other->getType()))
- return false;
-
- std::optional<IntRange> OtherValueRange = TryGetExprRange(
- S.Context, Other, S.isConstantEvaluatedContext(), /*Approximate=*/false);
- if (!OtherValueRange)
- return false;
-
- QualType OtherT = Other->getType();
- if (const auto *AT = OtherT->getAs<AtomicType>())
- OtherT = AT->getValueType();
- IntRange OtherTypeRange = IntRange::forValueOfType(S.Context, OtherT);
-
- // Special case for ObjC BOOL on targets where its a typedef for a signed char
- // (Namely, macOS). FIXME: IntRange::forValueOfType should do this.
- bool IsObjCSignedCharBool = S.getLangOpts().ObjC &&
- S.ObjC().NSAPIObj->isObjCBOOLType(OtherT) &&
- OtherT->isSpecificBuiltinType(BuiltinType::SChar);
-
- // Whether we're treating Other as being a bool because of the form of
- // expression despite it having another type (typically 'int' in C).
- bool OtherIsBooleanDespiteType =
- !OtherT->isBooleanType() && Other->isKnownToHaveBooleanValue();
- if (OtherIsBooleanDespiteType || IsObjCSignedCharBool)
- OtherTypeRange = *OtherValueRange = IntRange::forBoolType();
-
- // Check if all values in the range of possible values of this expression
- // lead to the same comparison outcome.
- PromotedRange OtherPromotedValueRange(*OtherValueRange, Value.getBitWidth(),
- Value.isUnsigned());
- auto Cmp = OtherPromotedValueRange.compare(Value);
- auto Result = PromotedRange::constantValue(E->getOpcode(), Cmp, RhsConstant);
- if (!Result)
- return false;
-
- // Also consider the range determined by the type alone. This allows us to
- // classify the warning under the proper diagnostic group.
- bool TautologicalTypeCompare = false;
- {
- PromotedRange OtherPromotedTypeRange(OtherTypeRange, Value.getBitWidth(),
- Value.isUnsigned());
- auto TypeCmp = OtherPromotedTypeRange.compare(Value);
- if (auto TypeResult = PromotedRange::constantValue(E->getOpcode(), TypeCmp,
- RhsConstant)) {
- TautologicalTypeCompare = true;
- Cmp = TypeCmp;
- Result = TypeResult;
- }
- }
-
- // Don't warn if the non-constant operand actually always evaluates to the
- // same value.
- if (!TautologicalTypeCompare && OtherValueRange->Width == 0)
- return false;
-
- // Suppress the diagnostic for an in-range comparison if the constant comes
- // from a macro or enumerator. We don't want to diagnose
- //
- // some_long_value <= INT_MAX
- //
- // when sizeof(int) == sizeof(long).
- bool InRange = Cmp & PromotedRange::InRangeFlag;
- if (InRange && IsEnumConstOrFromMacro(S, Constant))
- return false;
-
- // A comparison of an unsigned bit-field against 0 is really a type problem,
- // even though at the type level the bit-field might promote to 'signed int'.
- if (Other->refersToBitField() && InRange && Value == 0 &&
- Other->getType()->isUnsignedIntegerOrEnumerationType())
- TautologicalTypeCompare = true;
-
- // If this is a comparison to an enum constant, include that
- // constant in the diagnostic.
- const EnumConstantDecl *ED = nullptr;
- if (const auto *DR = dyn_cast<DeclRefExpr>(Constant))
- ED = dyn_cast<EnumConstantDecl>(DR->getDecl());
-
- // Should be enough for uint128 (39 decimal digits)
- SmallString<64> PrettySourceValue;
- llvm::raw_svector_ostream OS(PrettySourceValue);
- if (ED) {
- OS << '\'' << *ED << "' (" << Value << ")";
- } else if (auto *BL = dyn_cast<ObjCBoolLiteralExpr>(
- Constant->IgnoreParenImpCasts())) {
- OS << (BL->getValue() ? "YES" : "NO");
- } else {
- OS << Value;
- }
-
- if (!TautologicalTypeCompare) {
- S.Diag(E->getOperatorLoc(), diag::warn_tautological_compare_value_range)
- << RhsConstant << OtherValueRange->Width << OtherValueRange->NonNegative
- << E->getOpcodeStr() << OS.str() << *Result
- << E->getLHS()->getSourceRange() << E->getRHS()->getSourceRange();
- return true;
- }
-
- if (IsObjCSignedCharBool) {
- S.DiagRuntimeBehavior(E->getOperatorLoc(), E,
- S.PDiag(diag::warn_tautological_compare_objc_bool)
- << OS.str() << *Result);
- return true;
- }
-
- // FIXME: We use a somewhat different formatting for the in-range cases and
- // cases involving boolean values for historical reasons. We should pick a
- // consistent way of presenting these diagnostics.
- if (!InRange || Other->isKnownToHaveBooleanValue()) {
-
- S.DiagRuntimeBehavior(
- E->getOperatorLoc(), E,
- S.PDiag(!InRange ? diag::warn_out_of_range_compare
- : diag::warn_tautological_bool_compare)
- << OS.str() << classifyConstantValue(Constant) << OtherT
- << OtherIsBooleanDespiteType << *Result
- << E->getLHS()->getSourceRange() << E->getRHS()->getSourceRange());
- } else {
- bool IsCharTy = OtherT.withoutLocalFastQualifiers() == S.Context.CharTy;
- unsigned Diag =
- (isKnownToHaveUnsignedValue(OriginalOther) && Value == 0)
- ? (HasEnumType(OriginalOther)
- ? diag::warn_unsigned_enum_always_true_comparison
- : IsCharTy ? diag::warn_unsigned_char_always_true_comparison
- : diag::warn_unsigned_always_true_comparison)
- : diag::warn_tautological_constant_compare;
-
- S.Diag(E->getOperatorLoc(), Diag)
- << RhsConstant << OtherT << E->getOpcodeStr() << OS.str() << *Result
- << E->getLHS()->getSourceRange() << E->getRHS()->getSourceRange();
- }
-
- return true;
-}
-
-/// Analyze the operands of the given comparison. Implements the
-/// fallback case from AnalyzeComparison.
-static void AnalyzeImpConvsInComparison(Sema &S, BinaryOperator *E) {
- AnalyzeImplicitConversions(S, E->getLHS(), E->getOperatorLoc());
- AnalyzeImplicitConversions(S, E->getRHS(), E->getOperatorLoc());
-}
-
-/// Implements -Wsign-compare.
-///
-/// \param E the binary operator to check for warnings
-static void AnalyzeComparison(Sema &S, BinaryOperator *E) {
- // The type the comparison is being performed in.
- QualType T = E->getLHS()->getType();
-
- // Only analyze comparison operators where both sides have been converted to
- // the same type.
- if (!S.Context.hasSameUnqualifiedType(T, E->getRHS()->getType()))
- return AnalyzeImpConvsInComparison(S, E);
-
- // Don't analyze value-dependent comparisons directly.
- if (E->isValueDependent())
- return AnalyzeImpConvsInComparison(S, E);
-
- Expr *LHS = E->getLHS();
- Expr *RHS = E->getRHS();
-
- if (T->isIntegralType(S.Context)) {
- std::optional<llvm::APSInt> RHSValue =
- RHS->getIntegerConstantExpr(S.Context);
- std::optional<llvm::APSInt> LHSValue =
- LHS->getIntegerConstantExpr(S.Context);
-
- // We don't care about expressions whose result is a constant.
- if (RHSValue && LHSValue)
- return AnalyzeImpConvsInComparison(S, E);
-
- // We only care about expressions where just one side is literal
- if ((bool)RHSValue ^ (bool)LHSValue) {
- // Is the constant on the RHS or LHS?
- const bool RhsConstant = (bool)RHSValue;
- Expr *Const = RhsConstant ? RHS : LHS;
- Expr *Other = RhsConstant ? LHS : RHS;
- const llvm::APSInt &Value = RhsConstant ? *RHSValue : *LHSValue;
-
- // Check whether an integer constant comparison results in a value
- // of 'true' or 'false'.
- if (CheckTautologicalComparison(S, E, Const, Other, Value, RhsConstant))
- return AnalyzeImpConvsInComparison(S, E);
- }
- }
-
- if (!T->hasUnsignedIntegerRepresentation()) {
- // We don't do anything special if this isn't an unsigned integral
- // comparison: we're only interested in integral comparisons, and
- // signed comparisons only happen in cases we don't care to warn about.
- return AnalyzeImpConvsInComparison(S, E);
- }
-
- LHS = LHS->IgnoreParenImpCasts();
- RHS = RHS->IgnoreParenImpCasts();
-
- if (!S.getLangOpts().CPlusPlus) {
- // Avoid warning about comparison of integers with different signs when
- // RHS/LHS has a `typeof(E)` type whose sign is different from the sign of
- // the type of `E`.
- if (const auto *TET = dyn_cast<TypeOfExprType>(LHS->getType()))
- LHS = TET->getUnderlyingExpr()->IgnoreParenImpCasts();
- if (const auto *TET = dyn_cast<TypeOfExprType>(RHS->getType()))
- RHS = TET->getUnderlyingExpr()->IgnoreParenImpCasts();
- }
-
- // Check to see if one of the (unmodified) operands is of different
- // signedness.
- Expr *signedOperand, *unsignedOperand;
- if (LHS->getType()->hasSignedIntegerRepresentation()) {
- assert(!RHS->getType()->hasSignedIntegerRepresentation() &&
- "unsigned comparison between two signed integer expressions?");
- signedOperand = LHS;
- unsignedOperand = RHS;
- } else if (RHS->getType()->hasSignedIntegerRepresentation()) {
- signedOperand = RHS;
- unsignedOperand = LHS;
- } else {
- return AnalyzeImpConvsInComparison(S, E);
- }
-
- // Otherwise, calculate the effective range of the signed operand.
- std::optional<IntRange> signedRange =
- TryGetExprRange(S.Context, signedOperand, S.isConstantEvaluatedContext(),
- /*Approximate=*/true);
- if (!signedRange)
- return;
-
- // Go ahead and analyze implicit conversions in the operands. Note
- // that we skip the implicit conversions on both sides.
- AnalyzeImplicitConversions(S, LHS, E->getOperatorLoc());
- AnalyzeImplicitConversions(S, RHS, E->getOperatorLoc());
-
- // If the signed range is non-negative, -Wsign-compare won't fire.
- if (signedRange->NonNegative)
- return;
-
- // For (in)equality comparisons, if the unsigned operand is a
- // constant which cannot collide with a overflowed signed operand,
- // then reinterpreting the signed operand as unsigned will not
- // change the result of the comparison.
- if (E->isEqualityOp()) {
- unsigned comparisonWidth = S.Context.getIntWidth(T);
- std::optional<IntRange> unsignedRange = TryGetExprRange(
- S.Context, unsignedOperand, S.isConstantEvaluatedContext(),
- /*Approximate=*/true);
- if (!unsignedRange)
- return;
-
- // We should never be unable to prove that the unsigned operand is
- // non-negative.
- assert(unsignedRange->NonNegative && "unsigned range includes negative?");
-
- if (unsignedRange->Width < comparisonWidth)
- return;
- }
-
- S.DiagRuntimeBehavior(E->getOperatorLoc(), E,
- S.PDiag(diag::warn_mixed_sign_comparison)
- << LHS->getType() << RHS->getType()
- << LHS->getSourceRange() << RHS->getSourceRange());
-}
-
-/// Analyzes an attempt to assign the given value to a bitfield.
-///
-/// Returns true if there was something fishy about the attempt.
-static bool AnalyzeBitFieldAssignment(Sema &S, FieldDecl *Bitfield, Expr *Init,
- SourceLocation InitLoc) {
- assert(Bitfield->isBitField());
- if (Bitfield->isInvalidDecl())
- return false;
-
- // White-list bool bitfields.
- QualType BitfieldType = Bitfield->getType();
- if (BitfieldType->isBooleanType())
- return false;
-
- if (auto *BitfieldEnumDecl = BitfieldType->getAsEnumDecl()) {
- // If the underlying enum type was not explicitly specified as an unsigned
- // type and the enum contain only positive values, MSVC++ will cause an
- // inconsistency by storing this as a signed type.
- if (S.getLangOpts().CPlusPlus11 &&
- !BitfieldEnumDecl->getIntegerTypeSourceInfo() &&
- BitfieldEnumDecl->getNumPositiveBits() > 0 &&
- BitfieldEnumDecl->getNumNegativeBits() == 0) {
- S.Diag(InitLoc, diag::warn_no_underlying_type_specified_for_enum_bitfield)
- << BitfieldEnumDecl;
- }
- }
-
- // Ignore value- or type-dependent expressions.
- if (Bitfield->getBitWidth()->isValueDependent() ||
- Bitfield->getBitWidth()->isTypeDependent() ||
- Init->isValueDependent() ||
- Init->isTypeDependent())
- return false;
-
- Expr *OriginalInit = Init->IgnoreParenImpCasts();
- unsigned FieldWidth = Bitfield->getBitWidthValue();
-
- Expr::EvalResult Result;
- if (!OriginalInit->EvaluateAsInt(Result, S.Context,
- Expr::SE_AllowSideEffects)) {
- // The RHS is not constant. If the RHS has an enum type, make sure the
- // bitfield is wide enough to hold all the values of the enum without
- // truncation.
- const auto *ED = OriginalInit->getType()->getAsEnumDecl();
- const PreferredTypeAttr *PTAttr = nullptr;
- if (!ED) {
- PTAttr = Bitfield->getAttr<PreferredTypeAttr>();
- if (PTAttr)
- ED = PTAttr->getType()->getAsEnumDecl();
- }
- if (ED) {
- bool SignedBitfield = BitfieldType->isSignedIntegerOrEnumerationType();
-
- // Enum types are implicitly signed on Windows, so check if there are any
- // negative enumerators to see if the enum was intended to be signed or
- // not.
- bool SignedEnum = ED->getNumNegativeBits() > 0;
-
- // Check for surprising sign changes when assigning enum values to a
- // bitfield of different signedness. If the bitfield is signed and we
- // have exactly the right number of bits to store this unsigned enum,
- // suggest changing the enum to an unsigned type. This typically happens
- // on Windows where unfixed enums always use an underlying type of 'int'.
- unsigned DiagID = 0;
- if (SignedEnum && !SignedBitfield) {
- DiagID =
- PTAttr == nullptr
- ? diag::warn_unsigned_bitfield_assigned_signed_enum
- : diag::
- warn_preferred_type_unsigned_bitfield_assigned_signed_enum;
- } else if (SignedBitfield && !SignedEnum &&
- ED->getNumPositiveBits() == FieldWidth) {
- DiagID =
- PTAttr == nullptr
- ? diag::warn_signed_bitfield_enum_conversion
- : diag::warn_preferred_type_signed_bitfield_enum_conversion;
- }
- if (DiagID) {
- S.Diag(InitLoc, DiagID) << Bitfield << ED;
- TypeSourceInfo *TSI = Bitfield->getTypeSourceInfo();
- SourceRange TypeRange =
- TSI ? TSI->getTypeLoc().getSourceRange() : SourceRange();
- S.Diag(Bitfield->getTypeSpecStartLoc(), diag::note_change_bitfield_sign)
- << SignedEnum << TypeRange;
- if (PTAttr)
- S.Diag(PTAttr->getLocation(), diag::note_bitfield_preferred_type)
- << ED;
- }
-
- // Compute the required bitwidth. If the enum has negative values, we need
- // one more bit than the normal number of positive bits to represent the
- // sign bit.
- unsigned BitsNeeded = SignedEnum ? std::max(ED->getNumPositiveBits() + 1,
- ED->getNumNegativeBits())
- : ED->getNumPositiveBits();
-
- // Check the bitwidth.
- if (BitsNeeded > FieldWidth) {
- Expr *WidthExpr = Bitfield->getBitWidth();
- auto DiagID =
- PTAttr == nullptr
- ? diag::warn_bitfield_too_small_for_enum
- : diag::warn_preferred_type_bitfield_too_small_for_enum;
- S.Diag(InitLoc, DiagID) << Bitfield << ED;
- S.Diag(WidthExpr->getExprLoc(), diag::note_widen_bitfield)
- << BitsNeeded << ED << WidthExpr->getSourceRange();
- if (PTAttr)
- S.Diag(PTAttr->getLocation(), diag::note_bitfield_preferred_type)
- << ED;
- }
- }
-
- return false;
- }
-
- llvm::APSInt Value = Result.Val.getInt();
-
- unsigned OriginalWidth = Value.getBitWidth();
-
- // In C, the macro 'true' from stdbool.h will evaluate to '1'; To reduce
- // false positives where the user is demonstrating they intend to use the
- // bit-field as a Boolean, check to see if the value is 1 and we're assigning
- // to a one-bit bit-field to see if the value came from a macro named 'true'.
- bool OneAssignedToOneBitBitfield = FieldWidth == 1 && Value == 1;
- if (OneAssignedToOneBitBitfield && !S.LangOpts.CPlusPlus) {
- SourceLocation MaybeMacroLoc = OriginalInit->getBeginLoc();
- if (S.SourceMgr.isInSystemMacro(MaybeMacroLoc) &&
- S.findMacroSpelling(MaybeMacroLoc, "true"))
- return false;
- }
-
- if (!Value.isSigned() || Value.isNegative())
- if (UnaryOperator *UO = dyn_cast<UnaryOperator>(OriginalInit))
- if (UO->getOpcode() == UO_Minus || UO->getOpcode() == UO_Not)
- OriginalWidth = Value.getSignificantBits();
-
- if (OriginalWidth <= FieldWidth)
- return false;
-
- // Compute the value which the bitfield will contain.
- llvm::APSInt TruncatedValue = Value.trunc(FieldWidth);
- TruncatedValue.setIsSigned(BitfieldType->isSignedIntegerType());
-
- // Check whether the stored value is equal to the original value.
- TruncatedValue = TruncatedValue.extend(OriginalWidth);
- if (llvm::APSInt::isSameValue(Value, TruncatedValue))
- return false;
-
- std::string PrettyValue = toString(Value, 10);
- std::string PrettyTrunc = toString(TruncatedValue, 10);
-
- S.Diag(InitLoc, OneAssignedToOneBitBitfield
- ? diag::warn_impcast_single_bit_bitield_precision_constant
- : diag::warn_impcast_bitfield_precision_constant)
- << PrettyValue << PrettyTrunc << OriginalInit->getType()
- << Init->getSourceRange();
-
- return true;
-}
-
-/// Analyze the given simple or compound assignment for warning-worthy
-/// operations.
-static void AnalyzeAssignment(Sema &S, BinaryOperator *E) {
- // Just recurse on the LHS.
- AnalyzeImplicitConversions(S, E->getLHS(), E->getOperatorLoc());
-
- // We want to recurse on the RHS as normal unless we're assigning to
- // a bitfield.
- if (FieldDecl *Bitfield = E->getLHS()->getSourceBitField()) {
- if (AnalyzeBitFieldAssignment(S, Bitfield, E->getRHS(),
- E->getOperatorLoc())) {
- // Recurse, ignoring any implicit conversions on the RHS.
- return AnalyzeImplicitConversions(S, E->getRHS()->IgnoreParenImpCasts(),
- E->getOperatorLoc());
- }
- }
-
- AnalyzeImplicitConversions(S, E->getRHS(), E->getOperatorLoc());
-
- // Diagnose implicitly sequentially-consistent atomic assignment.
- if (E->getLHS()->getType()->isAtomicType())
- S.Diag(E->getRHS()->getBeginLoc(), diag::warn_atomic_implicit_seq_cst);
-}
-
-/// Diagnose an implicit cast; purely a helper for CheckImplicitConversion.
-static void DiagnoseImpCast(Sema &S, const Expr *E, QualType SourceType,
- QualType T, SourceLocation CContext, unsigned diag,
- bool PruneControlFlow = false) {
- // For languages like HLSL and OpenCL, implicit conversion diagnostics listing
- // address space annotations isn't really useful. The warnings aren't because
- // you're converting a `private int` to `unsigned int`, it is because you're
- // conerting `int` to `unsigned int`.
- if (SourceType.hasAddressSpace())
- SourceType = S.getASTContext().removeAddrSpaceQualType(SourceType);
- if (T.hasAddressSpace())
- T = S.getASTContext().removeAddrSpaceQualType(T);
- if (PruneControlFlow) {
- S.DiagRuntimeBehavior(E->getExprLoc(), E,
- S.PDiag(diag)
- << SourceType << T << E->getSourceRange()
- << SourceRange(CContext));
- return;
- }
- S.Diag(E->getExprLoc(), diag)
- << SourceType << T << E->getSourceRange() << SourceRange(CContext);
-}
-
-/// Diagnose an implicit cast; purely a helper for CheckImplicitConversion.
-static void DiagnoseImpCast(Sema &S, const Expr *E, QualType T,
- SourceLocation CContext, unsigned diag,
- bool PruneControlFlow = false) {
- DiagnoseImpCast(S, E, E->getType(), T, CContext, diag, PruneControlFlow);
-}
-
-/// Diagnose an implicit cast from a floating point value to an integer value.
-static void DiagnoseFloatingImpCast(Sema &S, const Expr *E, QualType T,
- SourceLocation CContext) {
- bool IsBool = T->isSpecificBuiltinType(BuiltinType::Bool);
- bool PruneWarnings = S.inTemplateInstantiation();
-
- const Expr *InnerE = E->IgnoreParenImpCasts();
- // We also want to warn on, e.g., "int i = -1.234"
- if (const auto *UOp = dyn_cast<UnaryOperator>(InnerE))
- if (UOp->getOpcode() == UO_Minus || UOp->getOpcode() == UO_Plus)
- InnerE = UOp->getSubExpr()->IgnoreParenImpCasts();
-
- bool IsLiteral = isa<FloatingLiteral>(E) || isa<FloatingLiteral>(InnerE);
-
- llvm::APFloat Value(0.0);
- bool IsConstant =
- E->EvaluateAsFloat(Value, S.Context, Expr::SE_AllowSideEffects);
- if (!IsConstant) {
- if (S.ObjC().isSignedCharBool(T)) {
- return S.ObjC().adornBoolConversionDiagWithTernaryFixit(
- E, S.Diag(CContext, diag::warn_impcast_float_to_objc_signed_char_bool)
- << E->getType());
- }
-
- return DiagnoseImpCast(S, E, T, CContext,
- diag::warn_impcast_float_integer, PruneWarnings);
- }
-
- bool isExact = false;
-
- llvm::APSInt IntegerValue(S.Context.getIntWidth(T),
- T->hasUnsignedIntegerRepresentation());
- llvm::APFloat::opStatus Result = Value.convertToInteger(
- IntegerValue, llvm::APFloat::rmTowardZero, &isExact);
-
- // FIXME: Force the precision of the source value down so we don't print
- // digits which are usually useless (we don't really care here if we
- // truncate a digit by accident in edge cases). Ideally, APFloat::toString
- // would automatically print the shortest representation, but it's a bit
- // tricky to implement.
- SmallString<16> PrettySourceValue;
- unsigned precision = llvm::APFloat::semanticsPrecision(Value.getSemantics());
- precision = (precision * 59 + 195) / 196;
- Value.toString(PrettySourceValue, precision);
-
- if (S.ObjC().isSignedCharBool(T) && IntegerValue != 0 && IntegerValue != 1) {
- return S.ObjC().adornBoolConversionDiagWithTernaryFixit(
- E, S.Diag(CContext, diag::warn_impcast_constant_value_to_objc_bool)
- << PrettySourceValue);
- }
-
- if (Result == llvm::APFloat::opOK && isExact) {
- if (IsLiteral) return;
- return DiagnoseImpCast(S, E, T, CContext, diag::warn_impcast_float_integer,
- PruneWarnings);
- }
-
- // Conversion of a floating-point value to a non-bool integer where the
- // integral part cannot be represented by the integer type is undefined.
- if (!IsBool && Result == llvm::APFloat::opInvalidOp)
- return DiagnoseImpCast(
- S, E, T, CContext,
- IsLiteral ? diag::warn_impcast_literal_float_to_integer_out_of_range
- : diag::warn_impcast_float_to_integer_out_of_range,
- PruneWarnings);
-
- unsigned DiagID = 0;
- if (IsLiteral) {
- // Warn on floating point literal to integer.
- DiagID = diag::warn_impcast_literal_float_to_integer;
- } else if (IntegerValue == 0) {
- if (Value.isZero()) { // Skip -0.0 to 0 conversion.
- return DiagnoseImpCast(S, E, T, CContext,
- diag::warn_impcast_float_integer, PruneWarnings);
- }
- // Warn on non-zero to zero conversion.
- DiagID = diag::warn_impcast_float_to_integer_zero;
- } else {
- if (IntegerValue.isUnsigned()) {
- if (!IntegerValue.isMaxValue()) {
- return DiagnoseImpCast(S, E, T, CContext,
- diag::warn_impcast_float_integer, PruneWarnings);
- }
- } else { // IntegerValue.isSigned()
- if (!IntegerValue.isMaxSignedValue() &&
- !IntegerValue.isMinSignedValue()) {
- return DiagnoseImpCast(S, E, T, CContext,
- diag::warn_impcast_float_integer, PruneWarnings);
- }
- }
- // Warn on evaluatable floating point expression to integer conversion.
- DiagID = diag::warn_impcast_float_to_integer;
- }
-
- SmallString<16> PrettyTargetValue;
- if (IsBool)
- PrettyTargetValue = Value.isZero() ? "false" : "true";
- else
- IntegerValue.toString(PrettyTargetValue);
-
- if (PruneWarnings) {
- S.DiagRuntimeBehavior(E->getExprLoc(), E,
- S.PDiag(DiagID)
- << E->getType() << T.getUnqualifiedType()
- << PrettySourceValue << PrettyTargetValue
- << E->getSourceRange() << SourceRange(CContext));
- } else {
- S.Diag(E->getExprLoc(), DiagID)
- << E->getType() << T.getUnqualifiedType() << PrettySourceValue
- << PrettyTargetValue << E->getSourceRange() << SourceRange(CContext);
- }
-}
-
-/// Analyze the given compound assignment for the possible losing of
-/// floating-point precision.
-static void AnalyzeCompoundAssignment(Sema &S, BinaryOperator *E) {
- assert(isa<CompoundAssignOperator>(E) &&
- "Must be compound assignment operation");
- // Recurse on the LHS and RHS in here
- AnalyzeImplicitConversions(S, E->getLHS(), E->getOperatorLoc());
- AnalyzeImplicitConversions(S, E->getRHS(), E->getOperatorLoc());
-
- if (E->getLHS()->getType()->isAtomicType())
- S.Diag(E->getOperatorLoc(), diag::warn_atomic_implicit_seq_cst);
-
- // Now check the outermost expression
- const auto *ResultBT = E->getLHS()->getType()->getAs<BuiltinType>();
- const auto *RBT = cast<CompoundAssignOperator>(E)
- ->getComputationResultType()
- ->getAs<BuiltinType>();
-
- // The below checks assume source is floating point.
- if (!ResultBT || !RBT || !RBT->isFloatingPoint()) return;
-
- // If source is floating point but target is an integer.
- if (ResultBT->isInteger())
- return DiagnoseImpCast(S, E, E->getRHS()->getType(), E->getLHS()->getType(),
- E->getExprLoc(), diag::warn_impcast_float_integer);
-
- if (!ResultBT->isFloatingPoint())
- return;
-
- // If both source and target are floating points, warn about losing precision.
- int Order = S.getASTContext().getFloatingTypeSemanticOrder(
- QualType(ResultBT, 0), QualType(RBT, 0));
- if (Order < 0 && !S.SourceMgr.isInSystemMacro(E->getOperatorLoc()))
- // warn about dropping FP rank.
- DiagnoseImpCast(S, E->getRHS(), E->getLHS()->getType(), E->getOperatorLoc(),
- diag::warn_impcast_float_result_precision);
-}
-
-static std::string PrettyPrintInRange(const llvm::APSInt &Value,
- IntRange Range) {
- if (!Range.Width) return "0";
-
- llvm::APSInt ValueInRange = Value;
- ValueInRange.setIsSigned(!Range.NonNegative);
- ValueInRange = ValueInRange.trunc(Range.Width);
- return toString(ValueInRange, 10);
-}
-
-static bool IsImplicitBoolFloatConversion(Sema &S, const Expr *Ex,
- bool ToBool) {
- if (!isa<ImplicitCastExpr>(Ex))
- return false;
-
- const Expr *InnerE = Ex->IgnoreParenImpCasts();
- const Type *Target = S.Context.getCanonicalType(Ex->getType()).getTypePtr();
- const Type *Source =
- S.Context.getCanonicalType(InnerE->getType()).getTypePtr();
- if (Target->isDependentType())
- return false;
-
- const auto *FloatCandidateBT =
- dyn_cast<BuiltinType>(ToBool ? Source : Target);
- const Type *BoolCandidateType = ToBool ? Target : Source;
-
- return (BoolCandidateType->isSpecificBuiltinType(BuiltinType::Bool) &&
- FloatCandidateBT && (FloatCandidateBT->isFloatingPoint()));
-}
-
-static void CheckImplicitArgumentConversions(Sema &S, const CallExpr *TheCall,
- SourceLocation CC) {
- for (unsigned I = 0, N = TheCall->getNumArgs(); I < N; ++I) {
- const Expr *CurrA = TheCall->getArg(I);
- if (!IsImplicitBoolFloatConversion(S, CurrA, true))
- continue;
-
- bool IsSwapped = ((I > 0) && IsImplicitBoolFloatConversion(
- S, TheCall->getArg(I - 1), false));
- IsSwapped |= ((I < (N - 1)) && IsImplicitBoolFloatConversion(
- S, TheCall->getArg(I + 1), false));
- if (IsSwapped) {
- // Warn on this floating-point to bool conversion.
- DiagnoseImpCast(S, CurrA->IgnoreParenImpCasts(),
- CurrA->getType(), CC,
- diag::warn_impcast_floating_point_to_bool);
- }
- }
-}
-
-static void DiagnoseNullConversion(Sema &S, Expr *E, QualType T,
- SourceLocation CC) {
- // Don't warn on functions which have return type nullptr_t.
- if (isa<CallExpr>(E))
- return;
-
- // Check for NULL (GNUNull) or nullptr (CXX11_nullptr).
- const Expr *NewE = E->IgnoreParenImpCasts();
- bool IsGNUNullExpr = isa<GNUNullExpr>(NewE);
- bool HasNullPtrType = NewE->getType()->isNullPtrType();
- if (!IsGNUNullExpr && !HasNullPtrType)
- return;
-
- // Return if target type is a safe conversion.
- if (T->isAnyPointerType() || T->isBlockPointerType() ||
- T->isMemberPointerType() || !T->isScalarType() || T->isNullPtrType())
- return;
-
- if (S.Diags.isIgnored(diag::warn_impcast_null_pointer_to_integer,
- E->getExprLoc()))
- return;
-
- SourceLocation Loc = E->getSourceRange().getBegin();
-
- // Venture through the macro stacks to get to the source of macro arguments.
- // The new location is a better location than the complete location that was
- // passed in.
- Loc = S.SourceMgr.getTopMacroCallerLoc(Loc);
- CC = S.SourceMgr.getTopMacroCallerLoc(CC);
-
- // __null is usually wrapped in a macro. Go up a macro if that is the case.
- if (IsGNUNullExpr && Loc.isMacroID()) {
- StringRef MacroName = Lexer::getImmediateMacroNameForDiagnostics(
- Loc, S.SourceMgr, S.getLangOpts());
- if (MacroName == "NULL")
- Loc = S.SourceMgr.getImmediateExpansionRange(Loc).getBegin();
- }
-
- // Only warn if the null and context location are in the same macro expansion.
- if (S.SourceMgr.getFileID(Loc) != S.SourceMgr.getFileID(CC))
- return;
-
- S.Diag(Loc, diag::warn_impcast_null_pointer_to_integer)
- << HasNullPtrType << T << SourceRange(CC)
- << FixItHint::CreateReplacement(Loc,
- S.getFixItZeroLiteralForType(T, Loc));
-}
-
-// Helper function to filter out cases for constant width constant conversion.
-// Don't warn on char array initialization or for non-decimal values.
-static bool isSameWidthConstantConversion(Sema &S, Expr *E, QualType T,
- SourceLocation CC) {
- // If initializing from a constant, and the constant starts with '0',
- // then it is a binary, octal, or hexadecimal. Allow these constants
- // to fill all the bits, even if there is a sign change.
- if (auto *IntLit = dyn_cast<IntegerLiteral>(E->IgnoreParenImpCasts())) {
- const char FirstLiteralCharacter =
- S.getSourceManager().getCharacterData(IntLit->getBeginLoc())[0];
- if (FirstLiteralCharacter == '0')
- return false;
- }
-
- // If the CC location points to a '{', and the type is char, then assume
- // assume it is an array initialization.
- if (CC.isValid() && T->isCharType()) {
- const char FirstContextCharacter =
- S.getSourceManager().getCharacterData(CC)[0];
- if (FirstContextCharacter == '{')
- return false;
- }
-
- return true;
-}
-
-static const IntegerLiteral *getIntegerLiteral(Expr *E) {
- const auto *IL = dyn_cast<IntegerLiteral>(E);
- if (!IL) {
- if (auto *UO = dyn_cast<UnaryOperator>(E)) {
- if (UO->getOpcode() == UO_Minus)
- return dyn_cast<IntegerLiteral>(UO->getSubExpr());
- }
- }
-
- return IL;
-}
-
-static void DiagnoseIntInBoolContext(Sema &S, Expr *E) {
- E = E->IgnoreParenImpCasts();
- SourceLocation ExprLoc = E->getExprLoc();
-
- if (const auto *BO = dyn_cast<BinaryOperator>(E)) {
- BinaryOperator::Opcode Opc = BO->getOpcode();
- Expr::EvalResult Result;
- // Do not diagnose unsigned shifts.
- if (Opc == BO_Shl) {
- const auto *LHS = getIntegerLiteral(BO->getLHS());
- const auto *RHS = getIntegerLiteral(BO->getRHS());
- if (LHS && LHS->getValue() == 0)
- S.Diag(ExprLoc, diag::warn_left_shift_always) << 0;
- else if (!E->isValueDependent() && LHS && RHS &&
- RHS->getValue().isNonNegative() &&
- E->EvaluateAsInt(Result, S.Context, Expr::SE_AllowSideEffects))
- S.Diag(ExprLoc, diag::warn_left_shift_always)
- << (Result.Val.getInt() != 0);
- else if (E->getType()->isSignedIntegerType())
- S.Diag(ExprLoc, diag::warn_left_shift_in_bool_context)
- << FixItHint::CreateInsertion(E->getBeginLoc(), "(")
- << FixItHint::CreateInsertion(S.getLocForEndOfToken(E->getEndLoc()),
- ") != 0");
- }
- }
-
- if (const auto *CO = dyn_cast<ConditionalOperator>(E)) {
- const auto *LHS = getIntegerLiteral(CO->getTrueExpr());
- const auto *RHS = getIntegerLiteral(CO->getFalseExpr());
- if (!LHS || !RHS)
- return;
- if ((LHS->getValue() == 0 || LHS->getValue() == 1) &&
- (RHS->getValue() == 0 || RHS->getValue() == 1))
- // Do not diagnose common idioms.
- return;
- if (LHS->getValue() != 0 && RHS->getValue() != 0)
- S.Diag(ExprLoc, diag::warn_integer_constants_in_conditional_always_true);
- }
-}
-
-static void DiagnoseMixedUnicodeImplicitConversion(Sema &S, const Type *Source,
- const Type *Target, Expr *E,
- QualType T,
- SourceLocation CC) {
- assert(Source->isUnicodeCharacterType() && Target->isUnicodeCharacterType() &&
- Source != Target);
-
- // Lone surrogates have a distinct representation in UTF-32.
- // Converting between UTF-16 and UTF-32 codepoints seems very widespread,
- // so don't warn on such conversion.
- if (Source->isChar16Type() && Target->isChar32Type())
- return;
-
- Expr::EvalResult Result;
- if (E->EvaluateAsInt(Result, S.getASTContext(), Expr::SE_AllowSideEffects,
- S.isConstantEvaluatedContext())) {
- llvm::APSInt Value(32);
- Value = Result.Val.getInt();
- bool IsASCII = Value <= 0x7F;
- bool IsBMP = Value <= 0xDFFF || (Value >= 0xE000 && Value <= 0xFFFF);
- bool ConversionPreservesSemantics =
- IsASCII || (!Source->isChar8Type() && !Target->isChar8Type() && IsBMP);
-
- if (!ConversionPreservesSemantics) {
- auto IsSingleCodeUnitCP = [](const QualType &T,
- const llvm::APSInt &Value) {
- if (T->isChar8Type())
- return llvm::IsSingleCodeUnitUTF8Codepoint(Value.getExtValue());
- if (T->isChar16Type())
- return llvm::IsSingleCodeUnitUTF16Codepoint(Value.getExtValue());
- assert(T->isChar32Type());
- return llvm::IsSingleCodeUnitUTF32Codepoint(Value.getExtValue());
- };
-
- S.Diag(CC, diag::warn_impcast_unicode_char_type_constant)
- << E->getType() << T
- << IsSingleCodeUnitCP(E->getType().getUnqualifiedType(), Value)
- << FormatUTFCodeUnitAsCodepoint(Value.getExtValue(), E->getType());
- }
- } else {
- bool LosesPrecision = S.getASTContext().getIntWidth(E->getType()) >
- S.getASTContext().getIntWidth(T);
- DiagnoseImpCast(S, E, T, CC,
- LosesPrecision ? diag::warn_impcast_unicode_precision
- : diag::warn_impcast_unicode_char_type);
- }
-}
-
-bool Sema::DiscardingCFIUncheckedCallee(QualType From, QualType To) const {
- From = Context.getCanonicalType(From);
- To = Context.getCanonicalType(To);
- QualType MaybePointee = From->getPointeeType();
- if (!MaybePointee.isNull() && MaybePointee->getAs<FunctionType>())
- From = MaybePointee;
- MaybePointee = To->getPointeeType();
- if (!MaybePointee.isNull() && MaybePointee->getAs<FunctionType>())
- To = MaybePointee;
-
- if (const auto *FromFn = From->getAs<FunctionType>()) {
- if (const auto *ToFn = To->getAs<FunctionType>()) {
- if (FromFn->getCFIUncheckedCalleeAttr() &&
- !ToFn->getCFIUncheckedCalleeAttr())
- return true;
- }
- }
- return false;
-}
-
-void Sema::CheckImplicitConversion(Expr *E, QualType T, SourceLocation CC,
- bool *ICContext, bool IsListInit) {
- if (E->isTypeDependent() || E->isValueDependent()) return;
-
- const Type *Source = Context.getCanonicalType(E->getType()).getTypePtr();
- const Type *Target = Context.getCanonicalType(T).getTypePtr();
- if (Source == Target) return;
- if (Target->isDependentType()) return;
-
- // If the conversion context location is invalid don't complain. We also
- // don't want to emit a warning if the issue occurs from the expansion of
- // a system macro. The problem is that 'getSpellingLoc()' is slow, so we
- // delay this check as long as possible. Once we detect we are in that
- // scenario, we just return.
- if (CC.isInvalid())
- return;
-
- if (Source->isAtomicType())
- Diag(E->getExprLoc(), diag::warn_atomic_implicit_seq_cst);
-
- // Diagnose implicit casts to bool.
- if (Target->isSpecificBuiltinType(BuiltinType::Bool)) {
- if (isa<StringLiteral>(E))
- // Warn on string literal to bool. Checks for string literals in logical
- // and expressions, for instance, assert(0 && "error here"), are
- // prevented by a check in AnalyzeImplicitConversions().
- return DiagnoseImpCast(*this, E, T, CC,
- diag::warn_impcast_string_literal_to_bool);
- if (isa<ObjCStringLiteral>(E) || isa<ObjCArrayLiteral>(E) ||
- isa<ObjCDictionaryLiteral>(E) || isa<ObjCBoxedExpr>(E)) {
- // This covers the literal expressions that evaluate to Objective-C
- // objects.
- return DiagnoseImpCast(*this, E, T, CC,
- diag::warn_impcast_objective_c_literal_to_bool);
- }
- if (Source->isPointerType() || Source->canDecayToPointerType()) {
- // Warn on pointer to bool conversion that is always true.
- DiagnoseAlwaysNonNullPointer(E, Expr::NPCK_NotNull, /*IsEqual*/ false,
- SourceRange(CC));
- }
- }
-
- // If the we're converting a constant to an ObjC BOOL on a platform where BOOL
- // is a typedef for signed char (macOS), then that constant value has to be 1
- // or 0.
- if (ObjC().isSignedCharBool(T) && Source->isIntegralType(Context)) {
- Expr::EvalResult Result;
- if (E->EvaluateAsInt(Result, getASTContext(), Expr::SE_AllowSideEffects)) {
- if (Result.Val.getInt() != 1 && Result.Val.getInt() != 0) {
- ObjC().adornBoolConversionDiagWithTernaryFixit(
- E, Diag(CC, diag::warn_impcast_constant_value_to_objc_bool)
- << toString(Result.Val.getInt(), 10));
- }
- return;
- }
- }
-
- // Check implicit casts from Objective-C collection literals to specialized
- // collection types, e.g., NSArray<NSString *> *.
- if (auto *ArrayLiteral = dyn_cast<ObjCArrayLiteral>(E))
- ObjC().checkArrayLiteral(QualType(Target, 0), ArrayLiteral);
- else if (auto *DictionaryLiteral = dyn_cast<ObjCDictionaryLiteral>(E))
- ObjC().checkDictionaryLiteral(QualType(Target, 0), DictionaryLiteral);
-
- // Strip vector types.
- if (isa<VectorType>(Source)) {
- if (Target->isSveVLSBuiltinType() &&
- (ARM().areCompatibleSveTypes(QualType(Target, 0),
- QualType(Source, 0)) ||
- ARM().areLaxCompatibleSveTypes(QualType(Target, 0),
- QualType(Source, 0))))
- return;
-
- if (Target->isRVVVLSBuiltinType() &&
- (Context.areCompatibleRVVTypes(QualType(Target, 0),
- QualType(Source, 0)) ||
- Context.areLaxCompatibleRVVTypes(QualType(Target, 0),
- QualType(Source, 0))))
- return;
-
- if (!isa<VectorType>(Target)) {
- if (SourceMgr.isInSystemMacro(CC))
- return;
- return DiagnoseImpCast(*this, E, T, CC, diag::warn_impcast_vector_scalar);
- }
- if (getLangOpts().HLSL &&
- Target->castAs<VectorType>()->getNumElements() <
- Source->castAs<VectorType>()->getNumElements()) {
- // Diagnose vector truncation but don't return. We may also want to
- // diagnose an element conversion.
- DiagnoseImpCast(*this, E, T, CC,
- diag::warn_hlsl_impcast_vector_truncation);
- }
-
- // If the vector cast is cast between two vectors of the same size, it is
- // a bitcast, not a conversion, except under HLSL where it is a conversion.
- if (!getLangOpts().HLSL &&
- Context.getTypeSize(Source) == Context.getTypeSize(Target))
- return;
-
- Source = cast<VectorType>(Source)->getElementType().getTypePtr();
- Target = cast<VectorType>(Target)->getElementType().getTypePtr();
- }
- if (const auto *VecTy = dyn_cast<VectorType>(Target))
- Target = VecTy->getElementType().getTypePtr();
-
- if (isa<ConstantMatrixType>(Source)) {
- if (Target->isScalarType())
- return DiagnoseImpCast(*this, E, T, CC, diag::warn_impcast_matrix_scalar);
-
- if (getLangOpts().HLSL &&
- Target->castAs<ConstantMatrixType>()->getNumElementsFlattened() <
- Source->castAs<ConstantMatrixType>()->getNumElementsFlattened()) {
- // Diagnose Matrix truncation but don't return. We may also want to
- // diagnose an element conversion.
- DiagnoseImpCast(*this, E, T, CC,
- diag::warn_hlsl_impcast_matrix_truncation);
- }
- }
- // Strip complex types.
- if (isa<ComplexType>(Source)) {
- if (!isa<ComplexType>(Target)) {
- if (SourceMgr.isInSystemMacro(CC) || Target->isBooleanType())
- return;
-
- return DiagnoseImpCast(*this, E, T, CC,
- getLangOpts().CPlusPlus
- ? diag::err_impcast_complex_scalar
- : diag::warn_impcast_complex_scalar);
- }
-
- Source = cast<ComplexType>(Source)->getElementType().getTypePtr();
- Target = cast<ComplexType>(Target)->getElementType().getTypePtr();
- }
-
- const BuiltinType *SourceBT = dyn_cast<BuiltinType>(Source);
- const BuiltinType *TargetBT = dyn_cast<BuiltinType>(Target);
-
- // Strip SVE vector types
- if (SourceBT && SourceBT->isSveVLSBuiltinType()) {
- // Need the original target type for vector type checks
- const Type *OriginalTarget = Context.getCanonicalType(T).getTypePtr();
- // Handle conversion from scalable to fixed when msve-vector-bits is
- // specified
- if (ARM().areCompatibleSveTypes(QualType(OriginalTarget, 0),
- QualType(Source, 0)) ||
- ARM().areLaxCompatibleSveTypes(QualType(OriginalTarget, 0),
- QualType(Source, 0)))
- return;
-
- // If the vector cast is cast between two vectors of the same size, it is
- // a bitcast, not a conversion.
- if (Context.getTypeSize(Source) == Context.getTypeSize(Target))
- return;
-
- Source = SourceBT->getSveEltType(Context).getTypePtr();
- }
-
- if (TargetBT && TargetBT->isSveVLSBuiltinType())
- Target = TargetBT->getSveEltType(Context).getTypePtr();
-
- // If the source is floating point...
- if (SourceBT && SourceBT->isFloatingPoint()) {
- // ...and the target is floating point...
- if (TargetBT && TargetBT->isFloatingPoint()) {
- // ...then warn if we're dropping FP rank.
-
- int Order = getASTContext().getFloatingTypeSemanticOrder(
- QualType(SourceBT, 0), QualType(TargetBT, 0));
- if (Order > 0) {
- // Don't warn about float constants that are precisely
- // representable in the target type.
- Expr::EvalResult result;
- if (E->EvaluateAsRValue(result, Context)) {
- // Value might be a float, a float vector, or a float complex.
- if (IsSameFloatAfterCast(
- result.Val,
- Context.getFloatTypeSemantics(QualType(TargetBT, 0)),
- Context.getFloatTypeSemantics(QualType(SourceBT, 0))))
- return;
- }
-
- if (SourceMgr.isInSystemMacro(CC))
- return;
-
- DiagnoseImpCast(*this, E, T, CC, diag::warn_impcast_float_precision);
- }
- // ... or possibly if we're increasing rank, too
- else if (Order < 0) {
- if (SourceMgr.isInSystemMacro(CC))
- return;
-
- DiagnoseImpCast(*this, E, T, CC, diag::warn_impcast_double_promotion);
- }
- return;
- }
-
- // If the target is integral, always warn.
- if (TargetBT && TargetBT->isInteger()) {
- if (SourceMgr.isInSystemMacro(CC))
- return;
-
- DiagnoseFloatingImpCast(*this, E, T, CC);
- }
-
- // Detect the case where a call result is converted from floating-point to
- // to bool, and the final argument to the call is converted from bool, to
- // discover this typo:
- //
- // bool b = fabs(x < 1.0); // should be "bool b = fabs(x) < 1.0;"
- //
- // FIXME: This is an incredibly special case; is there some more general
- // way to detect this class of misplaced-parentheses bug?
- if (Target->isBooleanType() && isa<CallExpr>(E)) {
- // Check last argument of function call to see if it is an
- // implicit cast from a type matching the type the result
- // is being cast to.
- CallExpr *CEx = cast<CallExpr>(E);
- if (unsigned NumArgs = CEx->getNumArgs()) {
- Expr *LastA = CEx->getArg(NumArgs - 1);
- Expr *InnerE = LastA->IgnoreParenImpCasts();
- if (isa<ImplicitCastExpr>(LastA) &&
- InnerE->getType()->isBooleanType()) {
- // Warn on this floating-point to bool conversion
- DiagnoseImpCast(*this, E, T, CC,
- diag::warn_impcast_floating_point_to_bool);
- }
- }
- }
- return;
- }
-
- // Valid casts involving fixed point types should be accounted for here.
- if (Source->isFixedPointType()) {
- if (Target->isUnsaturatedFixedPointType()) {
- Expr::EvalResult Result;
- if (E->EvaluateAsFixedPoint(Result, Context, Expr::SE_AllowSideEffects,
- isConstantEvaluatedContext())) {
- llvm::APFixedPoint Value = Result.Val.getFixedPoint();
- llvm::APFixedPoint MaxVal = Context.getFixedPointMax(T);
- llvm::APFixedPoint MinVal = Context.getFixedPointMin(T);
- if (Value > MaxVal || Value < MinVal) {
- DiagRuntimeBehavior(E->getExprLoc(), E,
- PDiag(diag::warn_impcast_fixed_point_range)
- << Value.toString() << T
- << E->getSourceRange()
- << clang::SourceRange(CC));
- return;
- }
- }
- } else if (Target->isIntegerType()) {
- Expr::EvalResult Result;
- if (!isConstantEvaluatedContext() &&
- E->EvaluateAsFixedPoint(Result, Context, Expr::SE_AllowSideEffects)) {
- llvm::APFixedPoint FXResult = Result.Val.getFixedPoint();
-
- bool Overflowed;
- llvm::APSInt IntResult = FXResult.convertToInt(
- Context.getIntWidth(T), Target->isSignedIntegerOrEnumerationType(),
- &Overflowed);
-
- if (Overflowed) {
- DiagRuntimeBehavior(E->getExprLoc(), E,
- PDiag(diag::warn_impcast_fixed_point_range)
- << FXResult.toString() << T
- << E->getSourceRange()
- << clang::SourceRange(CC));
- return;
- }
- }
- }
- } else if (Target->isUnsaturatedFixedPointType()) {
- if (Source->isIntegerType()) {
- Expr::EvalResult Result;
- if (!isConstantEvaluatedContext() &&
- E->EvaluateAsInt(Result, Context, Expr::SE_AllowSideEffects)) {
- llvm::APSInt Value = Result.Val.getInt();
-
- bool Overflowed;
- llvm::APFixedPoint IntResult = llvm::APFixedPoint::getFromIntValue(
- Value, Context.getFixedPointSemantics(T), &Overflowed);
-
- if (Overflowed) {
- DiagRuntimeBehavior(E->getExprLoc(), E,
- PDiag(diag::warn_impcast_fixed_point_range)
- << toString(Value, /*Radix=*/10) << T
- << E->getSourceRange()
- << clang::SourceRange(CC));
- return;
- }
- }
- }
- }
-
- // If we are casting an integer type to a floating point type without
- // initialization-list syntax, we might lose accuracy if the floating
- // point type has a narrower significand than the integer type.
- if (SourceBT && TargetBT && SourceBT->isIntegerType() &&
- TargetBT->isFloatingType() && !IsListInit) {
- // Determine the number of precision bits in the source integer type.
- std::optional<IntRange> SourceRange =
- TryGetExprRange(Context, E, isConstantEvaluatedContext(),
- /*Approximate=*/true);
- if (!SourceRange)
- return;
- unsigned int SourcePrecision = SourceRange->Width;
-
- // Determine the number of precision bits in the
- // target floating point type.
- unsigned int TargetPrecision = llvm::APFloatBase::semanticsPrecision(
- Context.getFloatTypeSemantics(QualType(TargetBT, 0)));
-
- if (SourcePrecision > 0 && TargetPrecision > 0 &&
- SourcePrecision > TargetPrecision) {
-
- if (std::optional<llvm::APSInt> SourceInt =
- E->getIntegerConstantExpr(Context)) {
- // If the source integer is a constant, convert it to the target
- // floating point type. Issue a warning if the value changes
- // during the whole conversion.
- llvm::APFloat TargetFloatValue(
- Context.getFloatTypeSemantics(QualType(TargetBT, 0)));
- llvm::APFloat::opStatus ConversionStatus =
- TargetFloatValue.convertFromAPInt(
- *SourceInt, SourceBT->isSignedInteger(),
- llvm::APFloat::rmNearestTiesToEven);
-
- if (ConversionStatus != llvm::APFloat::opOK) {
- SmallString<32> PrettySourceValue;
- SourceInt->toString(PrettySourceValue, 10);
- SmallString<32> PrettyTargetValue;
- TargetFloatValue.toString(PrettyTargetValue, TargetPrecision);
-
- DiagRuntimeBehavior(
- E->getExprLoc(), E,
- PDiag(diag::warn_impcast_integer_float_precision_constant)
- << PrettySourceValue << PrettyTargetValue << E->getType() << T
- << E->getSourceRange() << clang::SourceRange(CC));
- }
- } else {
- // Otherwise, the implicit conversion may lose precision.
- DiagnoseImpCast(*this, E, T, CC,
- diag::warn_impcast_integer_float_precision);
- }
- }
- }
-
- DiagnoseNullConversion(*this, E, T, CC);
-
- DiscardMisalignedMemberAddress(Target, E);
-
- if (Source->isUnicodeCharacterType() && Target->isUnicodeCharacterType()) {
- DiagnoseMixedUnicodeImplicitConversion(*this, Source, Target, E, T, CC);
- return;
- }
-
- if (Target->isBooleanType())
- DiagnoseIntInBoolContext(*this, E);
-
- if (DiscardingCFIUncheckedCallee(QualType(Source, 0), QualType(Target, 0))) {
- Diag(CC, diag::warn_cast_discards_cfi_unchecked_callee)
- << QualType(Source, 0) << QualType(Target, 0);
- }
-
- if (!Source->isIntegerType() || !Target->isIntegerType())
- return;
-
- // TODO: remove this early return once the false positives for constant->bool
- // in templates, macros, etc, are reduced or removed.
- if (Target->isSpecificBuiltinType(BuiltinType::Bool))
- return;
-
- if (ObjC().isSignedCharBool(T) && !Source->isCharType() &&
- !E->isKnownToHaveBooleanValue(/*Semantic=*/false)) {
- return ObjC().adornBoolConversionDiagWithTernaryFixit(
- E, Diag(CC, diag::warn_impcast_int_to_objc_signed_char_bool)
- << E->getType());
- }
- std::optional<IntRange> LikelySourceRange = TryGetExprRange(
- Context, E, isConstantEvaluatedContext(), /*Approximate=*/true);
- if (!LikelySourceRange)
- return;
-
- IntRange SourceTypeRange =
- IntRange::forTargetOfCanonicalType(Context, Source);
- IntRange TargetRange = IntRange::forTargetOfCanonicalType(Context, Target);
-
- if (LikelySourceRange->Width > TargetRange.Width) {
- // If the source is a constant, use a default-on diagnostic.
- // TODO: this should happen for bitfield stores, too.
- Expr::EvalResult Result;
- if (E->EvaluateAsInt(Result, Context, Expr::SE_AllowSideEffects,
- isConstantEvaluatedContext())) {
- llvm::APSInt Value(32);
- Value = Result.Val.getInt();
-
- if (SourceMgr.isInSystemMacro(CC))
- return;
-
- std::string PrettySourceValue = toString(Value, 10);
- std::string PrettyTargetValue = PrettyPrintInRange(Value, TargetRange);
-
- DiagRuntimeBehavior(E->getExprLoc(), E,
- PDiag(diag::warn_impcast_integer_precision_constant)
- << PrettySourceValue << PrettyTargetValue
- << E->getType() << T << E->getSourceRange()
- << SourceRange(CC));
- return;
- }
-
- // People want to build with -Wshorten-64-to-32 and not -Wconversion.
- if (SourceMgr.isInSystemMacro(CC))
- return;
-
- if (const auto *UO = dyn_cast<UnaryOperator>(E)) {
- if (UO->getOpcode() == UO_Minus)
- return DiagnoseImpCast(
- *this, E, T, CC, diag::warn_impcast_integer_precision_on_negation);
- }
-
- if (TargetRange.Width == 32 && Context.getIntWidth(E->getType()) == 64)
- return DiagnoseImpCast(*this, E, T, CC, diag::warn_impcast_integer_64_32,
- /* pruneControlFlow */ true);
- return DiagnoseImpCast(*this, E, T, CC,
- diag::warn_impcast_integer_precision);
- }
-
- if (TargetRange.Width > SourceTypeRange.Width) {
- if (auto *UO = dyn_cast<UnaryOperator>(E))
- if (UO->getOpcode() == UO_Minus)
- if (Source->isUnsignedIntegerType()) {
- if (Target->isUnsignedIntegerType())
- return DiagnoseImpCast(*this, E, T, CC,
- diag::warn_impcast_high_order_zero_bits);
- if (Target->isSignedIntegerType())
- return DiagnoseImpCast(*this, E, T, CC,
- diag::warn_impcast_nonnegative_result);
- }
- }
-
- if (TargetRange.Width == LikelySourceRange->Width &&
- !TargetRange.NonNegative && LikelySourceRange->NonNegative &&
- Source->isSignedIntegerType()) {
- // Warn when doing a signed to signed conversion, warn if the positive
- // source value is exactly the width of the target type, which will
- // cause a negative value to be stored.
-
- Expr::EvalResult Result;
- if (E->EvaluateAsInt(Result, Context, Expr::SE_AllowSideEffects) &&
- !SourceMgr.isInSystemMacro(CC)) {
- llvm::APSInt Value = Result.Val.getInt();
- if (isSameWidthConstantConversion(*this, E, T, CC)) {
- std::string PrettySourceValue = toString(Value, 10);
- std::string PrettyTargetValue = PrettyPrintInRange(Value, TargetRange);
-
- Diag(E->getExprLoc(),
- PDiag(diag::warn_impcast_integer_precision_constant)
- << PrettySourceValue << PrettyTargetValue << E->getType() << T
- << E->getSourceRange() << SourceRange(CC));
- return;
- }
- }
-
- // Fall through for non-constants to give a sign conversion warning.
- }
-
- if ((!isa<EnumType>(Target) || !isa<EnumType>(Source)) &&
- ((TargetRange.NonNegative && !LikelySourceRange->NonNegative) ||
- (!TargetRange.NonNegative && LikelySourceRange->NonNegative &&
- LikelySourceRange->Width == TargetRange.Width))) {
- if (SourceMgr.isInSystemMacro(CC))
- return;
-
- if (SourceBT && SourceBT->isInteger() && TargetBT &&
- TargetBT->isInteger() &&
- Source->isSignedIntegerType() == Target->isSignedIntegerType()) {
- return;
- }
-
- unsigned DiagID = diag::warn_impcast_integer_sign;
-
- // Traditionally, gcc has warned about this under -Wsign-compare.
- // We also want to warn about it in -Wconversion.
- // So if -Wconversion is off, use a completely identical diagnostic
- // in the sign-compare group.
- // The conditional-checking code will
- if (ICContext) {
- DiagID = diag::warn_impcast_integer_sign_conditional;
- *ICContext = true;
- }
-
- DiagnoseImpCast(*this, E, T, CC, DiagID);
- }
-
- // If we're implicitly converting from an integer into an enumeration, that
- // is valid in C but invalid in C++.
- QualType SourceType = E->getEnumCoercedType(Context);
- const BuiltinType *CoercedSourceBT = SourceType->getAs<BuiltinType>();
- if (CoercedSourceBT && CoercedSourceBT->isInteger() && isa<EnumType>(Target))
- return DiagnoseImpCast(*this, E, T, CC, diag::warn_impcast_int_to_enum);
-
- // Diagnose conversions between different enumeration types.
- // In C, we pretend that the type of an EnumConstantDecl is its enumeration
- // type, to give us better diagnostics.
- Source = Context.getCanonicalType(SourceType).getTypePtr();
-
- if (const EnumType *SourceEnum = Source->getAsCanonical<EnumType>())
- if (const EnumType *TargetEnum = Target->getAsCanonical<EnumType>())
- if (SourceEnum->getDecl()->hasNameForLinkage() &&
- TargetEnum->getDecl()->hasNameForLinkage() &&
- SourceEnum != TargetEnum) {
- if (SourceMgr.isInSystemMacro(CC))
- return;
-
- return DiagnoseImpCast(*this, E, SourceType, T, CC,
- diag::warn_impcast_different_enum_types);
- }
-}
-
-static void CheckConditionalOperator(Sema &S, AbstractConditionalOperator *E,
- SourceLocation CC, QualType T);
-
-static void CheckConditionalOperand(Sema &S, Expr *E, QualType T,
- SourceLocation CC, bool &ICContext) {
- E = E->IgnoreParenImpCasts();
- // Diagnose incomplete type for second or third operand in C.
- if (!S.getLangOpts().CPlusPlus && E->getType()->isRecordType())
- S.RequireCompleteExprType(E, diag::err_incomplete_type);
-
- if (auto *CO = dyn_cast<AbstractConditionalOperator>(E))
- return CheckConditionalOperator(S, CO, CC, T);
-
- AnalyzeImplicitConversions(S, E, CC);
- if (E->getType() != T)
- return S.CheckImplicitConversion(E, T, CC, &ICContext);
-}
-
-static void CheckConditionalOperator(Sema &S, AbstractConditionalOperator *E,
- SourceLocation CC, QualType T) {
- AnalyzeImplicitConversions(S, E->getCond(), E->getQuestionLoc());
-
- Expr *TrueExpr = E->getTrueExpr();
- if (auto *BCO = dyn_cast<BinaryConditionalOperator>(E))
- TrueExpr = BCO->getCommon();
-
- bool Suspicious = false;
- CheckConditionalOperand(S, TrueExpr, T, CC, Suspicious);
- CheckConditionalOperand(S, E->getFalseExpr(), T, CC, Suspicious);
-
- if (T->isBooleanType())
- DiagnoseIntInBoolContext(S, E);
-
- // If -Wconversion would have warned about either of the candidates
- // for a signedness conversion to the context type...
- if (!Suspicious) return;
-
- // ...but it's currently ignored...
- if (!S.Diags.isIgnored(diag::warn_impcast_integer_sign_conditional, CC))
- return;
-
- // ...then check whether it would have warned about either of the
- // candidates for a signedness conversion to the condition type.
- if (E->getType() == T) return;
-
- Suspicious = false;
- S.CheckImplicitConversion(TrueExpr->IgnoreParenImpCasts(), E->getType(), CC,
- &Suspicious);
- if (!Suspicious)
- S.CheckImplicitConversion(E->getFalseExpr()->IgnoreParenImpCasts(),
- E->getType(), CC, &Suspicious);
-}
-
-/// Check conversion of given expression to boolean.
-/// Input argument E is a logical expression.
-static void CheckBoolLikeConversion(Sema &S, Expr *E, SourceLocation CC) {
- // Run the bool-like conversion checks only for C since there bools are
- // still not used as the return type from "boolean" operators or as the input
- // type for conditional operators.
- if (S.getLangOpts().CPlusPlus)
- return;
- if (E->IgnoreParenImpCasts()->getType()->isAtomicType())
- return;
- S.CheckImplicitConversion(E->IgnoreParenImpCasts(), S.Context.BoolTy, CC);
-}
-
-namespace {
-struct AnalyzeImplicitConversionsWorkItem {
- Expr *E;
- SourceLocation CC;
- bool IsListInit;
-};
-}
-
-static void CheckCommaOperand(
- Sema &S, Expr *E, QualType T, SourceLocation CC,
- bool ExtraCheckForImplicitConversion,
- llvm::SmallVectorImpl<AnalyzeImplicitConversionsWorkItem> &WorkList) {
- E = E->IgnoreParenImpCasts();
- WorkList.push_back({E, CC, false});
-
- if (ExtraCheckForImplicitConversion && E->getType() != T)
- S.CheckImplicitConversion(E, T, CC);
-}
-
-/// Data recursive variant of AnalyzeImplicitConversions. Subexpressions
-/// that should be visited are added to WorkList.
-static void AnalyzeImplicitConversions(
- Sema &S, AnalyzeImplicitConversionsWorkItem Item,
- llvm::SmallVectorImpl<AnalyzeImplicitConversionsWorkItem> &WorkList) {
- Expr *OrigE = Item.E;
- SourceLocation CC = Item.CC;
-
- QualType T = OrigE->getType();
- Expr *E = OrigE->IgnoreParenImpCasts();
-
- // Propagate whether we are in a C++ list initialization expression.
- // If so, we do not issue warnings for implicit int-float conversion
- // precision loss, because C++11 narrowing already handles it.
- //
- // HLSL's initialization lists are special, so they shouldn't observe the C++
- // behavior here.
- bool IsListInit =
- Item.IsListInit || (isa<InitListExpr>(OrigE) &&
- S.getLangOpts().CPlusPlus && !S.getLangOpts().HLSL);
-
- if (E->isTypeDependent() || E->isValueDependent())
- return;
-
- Expr *SourceExpr = E;
- // Examine, but don't traverse into the source expression of an
- // OpaqueValueExpr, since it may have multiple parents and we don't want to
- // emit duplicate diagnostics. Its fine to examine the form or attempt to
- // evaluate it in the context of checking the specific conversion to T though.
- if (auto *OVE = dyn_cast<OpaqueValueExpr>(E))
- if (auto *Src = OVE->getSourceExpr())
- SourceExpr = Src;
-
- if (const auto *UO = dyn_cast<UnaryOperator>(SourceExpr))
- if (UO->getOpcode() == UO_Not &&
- UO->getSubExpr()->isKnownToHaveBooleanValue())
- S.Diag(UO->getBeginLoc(), diag::warn_bitwise_negation_bool)
- << OrigE->getSourceRange() << T->isBooleanType()
- << FixItHint::CreateReplacement(UO->getBeginLoc(), "!");
-
- if (auto *BO = dyn_cast<BinaryOperator>(SourceExpr)) {
- if ((BO->getOpcode() == BO_And || BO->getOpcode() == BO_Or) &&
- BO->getLHS()->isKnownToHaveBooleanValue() &&
- BO->getRHS()->isKnownToHaveBooleanValue() &&
- BO->getLHS()->HasSideEffects(S.Context) &&
- BO->getRHS()->HasSideEffects(S.Context)) {
- SourceManager &SM = S.getSourceManager();
- const LangOptions &LO = S.getLangOpts();
- SourceLocation BLoc = BO->getOperatorLoc();
- SourceLocation ELoc = Lexer::getLocForEndOfToken(BLoc, 0, SM, LO);
- StringRef SR = clang::Lexer::getSourceText(
- clang::CharSourceRange::getTokenRange(BLoc, ELoc), SM, LO);
- // To reduce false positives, only issue the diagnostic if the operator
- // is explicitly spelled as a punctuator. This suppresses the diagnostic
- // when using 'bitand' or 'bitor' either as keywords in C++ or as macros
- // in C, along with other macro spellings the user might invent.
- if (SR.str() == "&" || SR.str() == "|") {
-
- S.Diag(BO->getBeginLoc(), diag::warn_bitwise_instead_of_logical)
- << (BO->getOpcode() == BO_And ? "&" : "|")
- << OrigE->getSourceRange()
- << FixItHint::CreateReplacement(
- BO->getOperatorLoc(),
- (BO->getOpcode() == BO_And ? "&&" : "||"));
- S.Diag(BO->getBeginLoc(), diag::note_cast_operand_to_int);
- }
- } else if (BO->isCommaOp() && !S.getLangOpts().CPlusPlus) {
- /// Analyze the given comma operator. The basic idea behind the analysis
- /// is to analyze the left and right operands slightly differently. The
- /// left operand needs to check whether the operand itself has an implicit
- /// conversion, but not whether the left operand induces an implicit
- /// conversion for the entire comma expression itself. This is similar to
- /// how CheckConditionalOperand behaves; it's as-if the correct operand
- /// were directly used for the implicit conversion check.
- CheckCommaOperand(S, BO->getLHS(), T, BO->getOperatorLoc(),
- /*ExtraCheckForImplicitConversion=*/false, WorkList);
- CheckCommaOperand(S, BO->getRHS(), T, BO->getOperatorLoc(),
- /*ExtraCheckForImplicitConversion=*/true, WorkList);
- return;
- }
- }
-
- // For conditional operators, we analyze the arguments as if they
- // were being fed directly into the output.
- if (auto *CO = dyn_cast<AbstractConditionalOperator>(SourceExpr)) {
- CheckConditionalOperator(S, CO, CC, T);
- return;
- }
-
- // Check implicit argument conversions for function calls.
- if (const auto *Call = dyn_cast<CallExpr>(SourceExpr))
- CheckImplicitArgumentConversions(S, Call, CC);
-
- // Go ahead and check any implicit conversions we might have skipped.
- // The non-canonical typecheck is just an optimization;
- // CheckImplicitConversion will filter out dead implicit conversions.
- if (SourceExpr->getType() != T)
- S.CheckImplicitConversion(SourceExpr, T, CC, nullptr, IsListInit);
-
- // Now continue drilling into this expression.
-
- if (PseudoObjectExpr *POE = dyn_cast<PseudoObjectExpr>(E)) {
- // The bound subexpressions in a PseudoObjectExpr are not reachable
- // as transitive children.
- // FIXME: Use a more uniform representation for this.
- for (auto *SE : POE->semantics())
- if (auto *OVE = dyn_cast<OpaqueValueExpr>(SE))
- WorkList.push_back({OVE->getSourceExpr(), CC, IsListInit});
- }
-
- // Skip past explicit casts.
- if (auto *CE = dyn_cast<ExplicitCastExpr>(E)) {
- E = CE->getSubExpr();
- // In the special case of a C++ function-style cast with braces,
- // CXXFunctionalCastExpr has an InitListExpr as direct child with a single
- // initializer. This InitListExpr basically belongs to the cast itself, so
- // we skip it too. Specifically this is needed to silence -Wdouble-promotion
- if (isa<CXXFunctionalCastExpr>(CE)) {
- if (auto *InitListE = dyn_cast<InitListExpr>(E)) {
- if (InitListE->getNumInits() == 1) {
- E = InitListE->getInit(0);
- }
- }
- }
- E = E->IgnoreParenImpCasts();
- if (!CE->getType()->isVoidType() && E->getType()->isAtomicType())
- S.Diag(E->getBeginLoc(), diag::warn_atomic_implicit_seq_cst);
- WorkList.push_back({E, CC, IsListInit});
- return;
- }
-
- if (auto *OutArgE = dyn_cast<HLSLOutArgExpr>(E)) {
- WorkList.push_back({OutArgE->getArgLValue(), CC, IsListInit});
- // The base expression is only used to initialize the parameter for
- // arguments to `inout` parameters, so we only traverse down the base
- // expression for `inout` cases.
- if (OutArgE->isInOut())
- WorkList.push_back(
- {OutArgE->getCastedTemporary()->getSourceExpr(), CC, IsListInit});
- WorkList.push_back({OutArgE->getWritebackCast(), CC, IsListInit});
- return;
- }
-
- if (BinaryOperator *BO = dyn_cast<BinaryOperator>(E)) {
- // Do a somewhat different check with comparison operators.
- if (BO->isComparisonOp())
- return AnalyzeComparison(S, BO);
-
- // And with simple assignments.
- if (BO->getOpcode() == BO_Assign)
- return AnalyzeAssignment(S, BO);
- // And with compound assignments.
- if (BO->isAssignmentOp())
- return AnalyzeCompoundAssignment(S, BO);
- }
-
- // These break the otherwise-useful invariant below. Fortunately,
- // we don't really need to recurse into them, because any internal
- // expressions should have been analyzed already when they were
- // built into statements.
- if (isa<StmtExpr>(E)) return;
-
- // Don't descend into unevaluated contexts.
- if (isa<UnaryExprOrTypeTraitExpr>(E)) return;
-
- // Now just recurse over the expression's children.
- CC = E->getExprLoc();
- BinaryOperator *BO = dyn_cast<BinaryOperator>(E);
- bool IsLogicalAndOperator = BO && BO->getOpcode() == BO_LAnd;
- for (Stmt *SubStmt : E->children()) {
- Expr *ChildExpr = dyn_cast_or_null<Expr>(SubStmt);
- if (!ChildExpr)
- continue;
-
- if (auto *CSE = dyn_cast<CoroutineSuspendExpr>(E))
- if (ChildExpr == CSE->getOperand())
- // Do not recurse over a CoroutineSuspendExpr's operand.
- // The operand is also a subexpression of getCommonExpr(), and
- // recursing into it directly would produce duplicate diagnostics.
- continue;
-
- if (IsLogicalAndOperator &&
- isa<StringLiteral>(ChildExpr->IgnoreParenImpCasts()))
- // Ignore checking string literals that are in logical and operators.
- // This is a common pattern for asserts.
- continue;
- WorkList.push_back({ChildExpr, CC, IsListInit});
- }
-
- if (BO && BO->isLogicalOp()) {
- Expr *SubExpr = BO->getLHS()->IgnoreParenImpCasts();
- if (!IsLogicalAndOperator || !isa<StringLiteral>(SubExpr))
- ::CheckBoolLikeConversion(S, SubExpr, BO->getExprLoc());
-
- SubExpr = BO->getRHS()->IgnoreParenImpCasts();
- if (!IsLogicalAndOperator || !isa<StringLiteral>(SubExpr))
- ::CheckBoolLikeConversion(S, SubExpr, BO->getExprLoc());
- }
-
- if (const UnaryOperator *U = dyn_cast<UnaryOperator>(E)) {
- if (U->getOpcode() == UO_LNot) {
- ::CheckBoolLikeConversion(S, U->getSubExpr(), CC);
- } else if (U->getOpcode() != UO_AddrOf) {
- if (U->getSubExpr()->getType()->isAtomicType())
- S.Diag(U->getSubExpr()->getBeginLoc(),
- diag::warn_atomic_implicit_seq_cst);
- }
- }
-}
-
-/// AnalyzeImplicitConversions - Find and report any interesting
-/// implicit conversions in the given expression. There are a couple
-/// of competing diagnostics here, -Wconversion and -Wsign-compare.
-static void AnalyzeImplicitConversions(Sema &S, Expr *OrigE, SourceLocation CC,
- bool IsListInit/*= false*/) {
- llvm::SmallVector<AnalyzeImplicitConversionsWorkItem, 16> WorkList;
- WorkList.push_back({OrigE, CC, IsListInit});
- while (!WorkList.empty())
- AnalyzeImplicitConversions(S, WorkList.pop_back_val(), WorkList);
-}
-
-// Helper function for Sema::DiagnoseAlwaysNonNullPointer.
-// Returns true when emitting a warning about taking the address of a reference.
-static bool CheckForReference(Sema &SemaRef, const Expr *E,
- const PartialDiagnostic &PD) {
- E = E->IgnoreParenImpCasts();
-
- const FunctionDecl *FD = nullptr;
-
- if (const DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(E)) {
- if (!DRE->getDecl()->getType()->isReferenceType())
- return false;
- } else if (const MemberExpr *M = dyn_cast<MemberExpr>(E)) {
- if (!M->getMemberDecl()->getType()->isReferenceType())
- return false;
- } else if (const CallExpr *Call = dyn_cast<CallExpr>(E)) {
- if (!Call->getCallReturnType(SemaRef.Context)->isReferenceType())
- return false;
- FD = Call->getDirectCallee();
- } else {
- return false;
- }
-
- SemaRef.Diag(E->getExprLoc(), PD);
-
- // If possible, point to location of function.
- if (FD) {
- SemaRef.Diag(FD->getLocation(), diag::note_reference_is_return_value) << FD;
- }
-
- return true;
-}
-
-// Returns true if the SourceLocation is expanded from any macro body.
-// Returns false if the SourceLocation is invalid, is from not in a macro
-// expansion, or is from expanded from a top-level macro argument.
-static bool IsInAnyMacroBody(const SourceManager &SM, SourceLocation Loc) {
- if (Loc.isInvalid())
- return false;
-
- while (Loc.isMacroID()) {
- if (SM.isMacroBodyExpansion(Loc))
- return true;
- Loc = SM.getImmediateMacroCallerLoc(Loc);
- }
-
- return false;
-}
-
-void Sema::DiagnoseAlwaysNonNullPointer(Expr *E,
- Expr::NullPointerConstantKind NullKind,
- bool IsEqual, SourceRange Range) {
- if (!E)
- return;
-
- // Don't warn inside macros.
- if (E->getExprLoc().isMacroID()) {
- const SourceManager &SM = getSourceManager();
- if (IsInAnyMacroBody(SM, E->getExprLoc()) ||
- IsInAnyMacroBody(SM, Range.getBegin()))
- return;
- }
- E = E->IgnoreImpCasts();
-
- const bool IsCompare = NullKind != Expr::NPCK_NotNull;
-
- if (isa<CXXThisExpr>(E)) {
- unsigned DiagID = IsCompare ? diag::warn_this_null_compare
- : diag::warn_this_bool_conversion;
- Diag(E->getExprLoc(), DiagID) << E->getSourceRange() << Range << IsEqual;
- return;
- }
-
- bool IsAddressOf = false;
-
- if (auto *UO = dyn_cast<UnaryOperator>(E->IgnoreParens())) {
- if (UO->getOpcode() != UO_AddrOf)
- return;
- IsAddressOf = true;
- E = UO->getSubExpr();
- }
-
- if (IsAddressOf) {
- unsigned DiagID = IsCompare
- ? diag::warn_address_of_reference_null_compare
- : diag::warn_address_of_reference_bool_conversion;
- PartialDiagnostic PD = PDiag(DiagID) << E->getSourceRange() << Range
- << IsEqual;
- if (CheckForReference(*this, E, PD)) {
- return;
- }
- }
-
- auto ComplainAboutNonnullParamOrCall = [&](const Attr *NonnullAttr) {
- bool IsParam = isa<NonNullAttr>(NonnullAttr);
- std::string Str;
- llvm::raw_string_ostream S(Str);
- E->printPretty(S, nullptr, getPrintingPolicy());
- unsigned DiagID = IsCompare ? diag::warn_nonnull_expr_compare
- : diag::warn_cast_nonnull_to_bool;
- Diag(E->getExprLoc(), DiagID) << IsParam << S.str()
- << E->getSourceRange() << Range << IsEqual;
- Diag(NonnullAttr->getLocation(), diag::note_declared_nonnull) << IsParam;
- };
-
- // If we have a CallExpr that is tagged with returns_nonnull, we can complain.
- if (auto *Call = dyn_cast<CallExpr>(E->IgnoreParenImpCasts())) {
- if (auto *Callee = Call->getDirectCallee()) {
- if (const Attr *A = Callee->getAttr<ReturnsNonNullAttr>()) {
- ComplainAboutNonnullParamOrCall(A);
- return;
- }
- }
- }
-
- // Complain if we are converting a lambda expression to a boolean value
- // outside of instantiation.
- if (!inTemplateInstantiation()) {
- if (const auto *MCallExpr = dyn_cast<CXXMemberCallExpr>(E)) {
- if (const auto *MRecordDecl = MCallExpr->getRecordDecl();
- MRecordDecl && MRecordDecl->isLambda()) {
- Diag(E->getExprLoc(), diag::warn_impcast_pointer_to_bool)
- << /*LambdaPointerConversionOperatorType=*/3
- << MRecordDecl->getSourceRange() << Range << IsEqual;
- return;
- }
- }
- }
-
- // Expect to find a single Decl. Skip anything more complicated.
- ValueDecl *D = nullptr;
- if (DeclRefExpr *R = dyn_cast<DeclRefExpr>(E)) {
- D = R->getDecl();
- } else if (MemberExpr *M = dyn_cast<MemberExpr>(E)) {
- D = M->getMemberDecl();
- }
-
- // Weak Decls can be null.
- if (!D || D->isWeak())
- return;
-
- // Check for parameter decl with nonnull attribute
- if (const auto* PV = dyn_cast<ParmVarDecl>(D)) {
- if (getCurFunction() &&
- !getCurFunction()->ModifiedNonNullParams.count(PV)) {
- if (const Attr *A = PV->getAttr<NonNullAttr>()) {
- ComplainAboutNonnullParamOrCall(A);
- return;
- }
-
- if (const auto *FD = dyn_cast<FunctionDecl>(PV->getDeclContext())) {
- // Skip function template not specialized yet.
- if (FD->getTemplatedKind() == FunctionDecl::TK_FunctionTemplate)
- return;
- auto ParamIter = llvm::find(FD->parameters(), PV);
- assert(ParamIter != FD->param_end());
- unsigned ParamNo = std::distance(FD->param_begin(), ParamIter);
-
- for (const auto *NonNull : FD->specific_attrs<NonNullAttr>()) {
- if (!NonNull->args_size()) {
- ComplainAboutNonnullParamOrCall(NonNull);
- return;
- }
-
- for (const ParamIdx &ArgNo : NonNull->args()) {
- if (ArgNo.getASTIndex() == ParamNo) {
- ComplainAboutNonnullParamOrCall(NonNull);
- return;
- }
- }
- }
- }
- }
- }
-
- QualType T = D->getType();
- const bool IsArray = T->isArrayType();
- const bool IsFunction = T->isFunctionType();
-
- // Address of function is used to silence the function warning.
- if (IsAddressOf && IsFunction) {
- return;
- }
-
- // Found nothing.
- if (!IsAddressOf && !IsFunction && !IsArray)
- return;
-
- // Pretty print the expression for the diagnostic.
- std::string Str;
- llvm::raw_string_ostream S(Str);
- E->printPretty(S, nullptr, getPrintingPolicy());
-
- unsigned DiagID = IsCompare ? diag::warn_null_pointer_compare
- : diag::warn_impcast_pointer_to_bool;
- enum {
- AddressOf,
- FunctionPointer,
- ArrayPointer
- } DiagType;
- if (IsAddressOf)
- DiagType = AddressOf;
- else if (IsFunction)
- DiagType = FunctionPointer;
- else if (IsArray)
- DiagType = ArrayPointer;
- else
- llvm_unreachable("Could not determine diagnostic.");
- Diag(E->getExprLoc(), DiagID) << DiagType << S.str() << E->getSourceRange()
- << Range << IsEqual;
-
- if (!IsFunction)
- return;
-
- // Suggest '&' to silence the function warning.
- Diag(E->getExprLoc(), diag::note_function_warning_silence)
- << FixItHint::CreateInsertion(E->getBeginLoc(), "&");
-
- // Check to see if '()' fixit should be emitted.
- QualType ReturnType;
- UnresolvedSet<4> NonTemplateOverloads;
- tryExprAsCall(*E, ReturnType, NonTemplateOverloads);
- if (ReturnType.isNull())
- return;
-
- if (IsCompare) {
- // There are two cases here. If there is null constant, the only suggest
- // for a pointer return type. If the null is 0, then suggest if the return
- // type is a pointer or an integer type.
- if (!ReturnType->isPointerType()) {
- if (NullKind == Expr::NPCK_ZeroExpression ||
- NullKind == Expr::NPCK_ZeroLiteral) {
- if (!ReturnType->isIntegerType())
- return;
- } else {
- return;
- }
- }
- } else { // !IsCompare
- // For function to bool, only suggest if the function pointer has bool
- // return type.
- if (!ReturnType->isSpecificBuiltinType(BuiltinType::Bool))
- return;
- }
- Diag(E->getExprLoc(), diag::note_function_to_function_call)
- << FixItHint::CreateInsertion(getLocForEndOfToken(E->getEndLoc()), "()");
-}
-
-void Sema::CheckImplicitConversions(Expr *E, SourceLocation CC) {
- // Don't diagnose in unevaluated contexts.
- if (isUnevaluatedContext())
- return;
-
- // Don't diagnose for value- or type-dependent expressions.
- if (E->isTypeDependent() || E->isValueDependent())
- return;
-
- // Check for array bounds violations in cases where the check isn't triggered
- // elsewhere for other Expr types (like BinaryOperators), e.g. when an
- // ArraySubscriptExpr is on the RHS of a variable initialization.
- CheckArrayAccess(E);
-
- // This is not the right CC for (e.g.) a variable initialization.
- AnalyzeImplicitConversions(*this, E, CC);
-}
-
-void Sema::CheckBoolLikeConversion(Expr *E, SourceLocation CC) {
- ::CheckBoolLikeConversion(*this, E, CC);
-}
-
-void Sema::CheckForIntOverflow (const Expr *E) {
- // Use a work list to deal with nested struct initializers.
- SmallVector<const Expr *, 2> Exprs(1, E);
-
- do {
- const Expr *OriginalE = Exprs.pop_back_val();
- const Expr *E = OriginalE->IgnoreParenCasts();
-
- if (isa<BinaryOperator, UnaryOperator>(E)) {
- E->EvaluateForOverflow(Context);
- continue;
- }
-
- if (const auto *InitList = dyn_cast<InitListExpr>(OriginalE))
- Exprs.append(InitList->inits().begin(), InitList->inits().end());
- else if (isa<ObjCBoxedExpr>(OriginalE))
- E->EvaluateForOverflow(Context);
- else if (const auto *Call = dyn_cast<CallExpr>(E))
- Exprs.append(Call->arg_begin(), Call->arg_end());
- else if (const auto *Message = dyn_cast<ObjCMessageExpr>(E))
- Exprs.append(Message->arg_begin(), Message->arg_end());
- else if (const auto *Construct = dyn_cast<CXXConstructExpr>(E))
- Exprs.append(Construct->arg_begin(), Construct->arg_end());
- else if (const auto *Temporary = dyn_cast<CXXBindTemporaryExpr>(E))
- Exprs.push_back(Temporary->getSubExpr());
- else if (const auto *Array = dyn_cast<ArraySubscriptExpr>(E))
- Exprs.push_back(Array->getIdx());
- else if (const auto *Compound = dyn_cast<CompoundLiteralExpr>(E))
- Exprs.push_back(Compound->getInitializer());
- else if (const auto *New = dyn_cast<CXXNewExpr>(E);
- New && New->isArray()) {
- if (auto ArraySize = New->getArraySize())
- Exprs.push_back(*ArraySize);
- } else if (const auto *MTE = dyn_cast<MaterializeTemporaryExpr>(OriginalE))
- Exprs.push_back(MTE->getSubExpr());
- } while (!Exprs.empty());
-}
-
-namespace {
-
-/// Visitor for expressions which looks for unsequenced operations on the
-/// same object.
-class SequenceChecker : public ConstEvaluatedExprVisitor<SequenceChecker> {
- using Base = ConstEvaluatedExprVisitor<SequenceChecker>;
-
- /// A tree of sequenced regions within an expression. Two regions are
- /// unsequenced if one is an ancestor or a descendent of the other. When we
- /// finish processing an expression with sequencing, such as a comma
- /// expression, we fold its tree nodes into its parent, since they are
- /// unsequenced with respect to nodes we will visit later.
- class SequenceTree {
- struct Value {
- explicit Value(unsigned Parent) : Parent(Parent), Merged(false) {}
- unsigned Parent : 31;
- LLVM_PREFERRED_TYPE(bool)
- unsigned Merged : 1;
- };
- SmallVector<Value, 8> Values;
-
- public:
- /// A region within an expression which may be sequenced with respect
- /// to some other region.
- class Seq {
- friend class SequenceTree;
-
- unsigned Index;
-
- explicit Seq(unsigned N) : Index(N) {}
-
- public:
- Seq() : Index(0) {}
- };
-
- SequenceTree() { Values.push_back(Value(0)); }
- Seq root() const { return Seq(0); }
-
- /// Create a new sequence of operations, which is an unsequenced
- /// subset of \p Parent. This sequence of operations is sequenced with
- /// respect to other children of \p Parent.
- Seq allocate(Seq Parent) {
- Values.push_back(Value(Parent.Index));
- return Seq(Values.size() - 1);
- }
-
- /// Merge a sequence of operations into its parent.
- void merge(Seq S) {
- Values[S.Index].Merged = true;
- }
-
- /// Determine whether two operations are unsequenced. This operation
- /// is asymmetric: \p Cur should be the more recent sequence, and \p Old
- /// should have been merged into its parent as appropriate.
- bool isUnsequenced(Seq Cur, Seq Old) {
- unsigned C = representative(Cur.Index);
- unsigned Target = representative(Old.Index);
- while (C >= Target) {
- if (C == Target)
- return true;
- C = Values[C].Parent;
- }
- return false;
- }
-
- private:
- /// Pick a representative for a sequence.
- unsigned representative(unsigned K) {
- if (Values[K].Merged)
- // Perform path compression as we go.
- return Values[K].Parent = representative(Values[K].Parent);
- return K;
- }
- };
-
- /// An object for which we can track unsequenced uses.
- using Object = const ValueDecl *;
-
- /// Different flavors of object usage which we track. We only track the
- /// least-sequenced usage of each kind.
- enum UsageKind {
- /// A read of an object. Multiple unsequenced reads are OK.
- UK_Use,
-
- /// A modification of an object which is sequenced before the value
- /// computation of the expression, such as ++n in C++.
- UK_ModAsValue,
-
- /// A modification of an object which is not sequenced before the value
- /// computation of the expression, such as n++.
- UK_ModAsSideEffect,
-
- UK_Count = UK_ModAsSideEffect + 1
- };
-
- enum WarningKind { WK_UseAndMod, WK_ModAndMod, WK_Volatile_UseAndUse };
-
- /// Bundle together a sequencing region and the expression corresponding
- /// to a specific usage. One Usage is stored for each usage kind in UsageInfo.
- struct Usage {
- const Expr *UsageExpr = nullptr;
- SequenceTree::Seq Seq;
-
- Usage() = default;
- };
-
- struct UsageInfo {
- Usage Uses[UK_Count];
-
- /// Have we issued a diagnostic for this object already?
- bool Diagnosed = false;
-
- UsageInfo();
- };
- using UsageInfoMap = llvm::SmallDenseMap<Object, UsageInfo, 16>;
-
- Sema &SemaRef;
-
- /// Sequenced regions within the expression.
- SequenceTree Tree;
-
- /// Declaration modifications and references which we have seen.
- UsageInfoMap UsageMap;
-
- /// The region we are currently within.
- SequenceTree::Seq Region;
-
- /// Filled in with declarations which were modified as a side-effect
- /// (that is, post-increment operations).
- SmallVectorImpl<std::pair<Object, Usage>> *ModAsSideEffect = nullptr;
-
- /// Expressions to check later. We defer checking these to reduce
- /// stack usage.
- SmallVectorImpl<const Expr *> &WorkList;
-
- /// RAII object wrapping the visitation of a sequenced subexpression of an
- /// expression. At the end of this process, the side-effects of the evaluation
- /// become sequenced with respect to the value computation of the result, so
- /// we downgrade any UK_ModAsSideEffect within the evaluation to
- /// UK_ModAsValue.
- struct SequencedSubexpression {
- SequencedSubexpression(SequenceChecker &Self)
- : Self(Self), OldModAsSideEffect(Self.ModAsSideEffect) {
- Self.ModAsSideEffect = &ModAsSideEffect;
- }
-
- ~SequencedSubexpression() {
- for (const std::pair<Object, Usage> &M : llvm::reverse(ModAsSideEffect)) {
- // Add a new usage with usage kind UK_ModAsValue, and then restore
- // the previous usage with UK_ModAsSideEffect (thus clearing it if
- // the previous one was empty).
- UsageInfo &UI = Self.UsageMap[M.first];
- auto &SideEffectUsage = UI.Uses[UK_ModAsSideEffect];
- Self.addUsage(M.first, UI, SideEffectUsage.UsageExpr, UK_ModAsValue);
- SideEffectUsage = M.second;
- }
- Self.ModAsSideEffect = OldModAsSideEffect;
- }
-
- SequenceChecker &Self;
- SmallVector<std::pair<Object, Usage>, 4> ModAsSideEffect;
- SmallVectorImpl<std::pair<Object, Usage>> *OldModAsSideEffect;
- };
-
- /// RAII object wrapping the visitation of a subexpression which we might
- /// choose to evaluate as a constant. If any subexpression is evaluated and
- /// found to be non-constant, this allows us to suppress the evaluation of
- /// the outer expression.
- class EvaluationTracker {
- public:
- EvaluationTracker(SequenceChecker &Self)
- : Self(Self), Prev(Self.EvalTracker) {
- Self.EvalTracker = this;
- }
-
- ~EvaluationTracker() {
- Self.EvalTracker = Prev;
- if (Prev)
- Prev->EvalOK &= EvalOK;
- }
-
- bool evaluate(const Expr *E, bool &Result) {
- if (!EvalOK || E->isValueDependent())
- return false;
- EvalOK = E->EvaluateAsBooleanCondition(
- Result, Self.SemaRef.Context,
- Self.SemaRef.isConstantEvaluatedContext());
- return EvalOK;
- }
-
- private:
- SequenceChecker &Self;
- EvaluationTracker *Prev;
- bool EvalOK = true;
- } *EvalTracker = nullptr;
-
- /// Find the object which is produced by the specified expression,
- /// if any.
- Object getObject(const Expr *E, bool Mod) const {
- E = E->IgnoreParenCasts();
- if (const UnaryOperator *UO = dyn_cast<UnaryOperator>(E)) {
- if (Mod && (UO->getOpcode() == UO_PreInc || UO->getOpcode() == UO_PreDec))
- return getObject(UO->getSubExpr(), Mod);
- } else if (const BinaryOperator *BO = dyn_cast<BinaryOperator>(E)) {
- if (BO->getOpcode() == BO_Comma)
- return getObject(BO->getRHS(), Mod);
- if (Mod && BO->isAssignmentOp())
- return getObject(BO->getLHS(), Mod);
- } else if (const MemberExpr *ME = dyn_cast<MemberExpr>(E)) {
- // FIXME: Check for more interesting cases, like "x.n = ++x.n".
- if (isa<CXXThisExpr>(ME->getBase()->IgnoreParenCasts()))
- return ME->getMemberDecl();
- } else if (const DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(E))
- // FIXME: If this is a reference, map through to its value.
- return DRE->getDecl();
- return nullptr;
- }
-
- /// Note that an object \p O was modified or used by an expression
- /// \p UsageExpr with usage kind \p UK. \p UI is the \p UsageInfo for
- /// the object \p O as obtained via the \p UsageMap.
- void addUsage(Object O, UsageInfo &UI, const Expr *UsageExpr, UsageKind UK) {
- // Get the old usage for the given object and usage kind.
- Usage &U = UI.Uses[UK];
- if (!U.UsageExpr || !Tree.isUnsequenced(Region, U.Seq)) {
- // If we have a modification as side effect and are in a sequenced
- // subexpression, save the old Usage so that we can restore it later
- // in SequencedSubexpression::~SequencedSubexpression.
- if (UK == UK_ModAsSideEffect && ModAsSideEffect)
- ModAsSideEffect->push_back(std::make_pair(O, U));
- // Then record the new usage with the current sequencing region.
- U.UsageExpr = UsageExpr;
- U.Seq = Region;
- }
- }
-
- /// Check whether a modification or use of an object \p O in an expression
- /// \p UsageExpr conflicts with a prior usage of kind \p OtherKind. \p UI is
- /// the \p UsageInfo for the object \p O as obtained via the \p UsageMap.
- /// \p IsModMod is true when we are checking for a mod-mod unsequenced
- /// usage and false we are checking for a mod-use unsequenced usage.
- void checkUsage(Object O, UsageInfo &UI, const Expr *UsageExpr,
- UsageKind OtherKind, WarningKind WarnKind) {
- if (UI.Diagnosed)
- return;
-
- const Usage &U = UI.Uses[OtherKind];
- if (!U.UsageExpr || !Tree.isUnsequenced(Region, U.Seq))
- return;
-
- const Expr *Mod = U.UsageExpr;
- const Expr *ModOrUse = UsageExpr;
- if (OtherKind == UK_Use)
- std::swap(Mod, ModOrUse);
-
- unsigned DiagID = 0;
- switch (WarnKind) {
- case WK_UseAndMod:
- DiagID = diag::warn_unsequenced_mod_use;
- break;
- case WK_ModAndMod:
- DiagID = diag::warn_unsequenced_mod_mod;
- break;
- case WK_Volatile_UseAndUse:
- DiagID = diag::warn_unsequenced_use_use_volatile;
- break;
- }
-
- SemaRef.DiagRuntimeBehavior(
- Mod->getExprLoc(), {Mod, ModOrUse},
- SemaRef.PDiag(DiagID) << O << SourceRange(ModOrUse->getExprLoc()));
- UI.Diagnosed = true;
- }
-
- // A note on note{Pre, Post}{Use, Mod}:
- //
- // (It helps to follow the algorithm with an expression such as
- // "((++k)++, k) = k" or "k = (k++, k++)". Both contain unsequenced
- // operations before C++17 and both are well-defined in C++17).
- //
- // When visiting a node which uses/modify an object we first call notePreUse
- // or notePreMod before visiting its sub-expression(s). At this point the
- // children of the current node have not yet been visited and so the eventual
- // uses/modifications resulting from the children of the current node have not
- // been recorded yet.
- //
- // We then visit the children of the current node. After that notePostUse or
- // notePostMod is called. These will 1) detect an unsequenced modification
- // as side effect (as in "k++ + k") and 2) add a new usage with the
- // appropriate usage kind.
- //
- // We also have to be careful that some operation sequences modification as
- // side effect as well (for example: || or ,). To account for this we wrap
- // the visitation of such a sub-expression (for example: the LHS of || or ,)
- // with SequencedSubexpression. SequencedSubexpression is an RAII object
- // which record usages which are modifications as side effect, and then
- // downgrade them (or more accurately restore the previous usage which was a
- // modification as side effect) when exiting the scope of the sequenced
- // subexpression.
-
- void notePreUse(Object O, const Expr *UseExpr) {
- UsageInfo &UI = UsageMap[O];
- // Uses conflict with other modifications.
- checkUsage(O, UI, UseExpr, /*OtherKind=*/UK_ModAsValue, WK_UseAndMod);
- // Volatile uses conflict with other uses.
- if (O->getType().isVolatileQualified())
- checkUsage(O, UI, UseExpr, /*OtherKind=*/UK_Use, WK_Volatile_UseAndUse);
- }
-
- void notePostUse(Object O, const Expr *UseExpr) {
- UsageInfo &UI = UsageMap[O];
- checkUsage(O, UI, UseExpr, /*OtherKind=*/UK_ModAsSideEffect, WK_UseAndMod);
- addUsage(O, UI, UseExpr, /*UsageKind=*/UK_Use);
- }
-
- void notePreMod(Object O, const Expr *ModExpr) {
- UsageInfo &UI = UsageMap[O];
- // Modifications conflict with other modifications and with uses.
- checkUsage(O, UI, ModExpr, /*OtherKind=*/UK_ModAsValue, WK_ModAndMod);
- checkUsage(O, UI, ModExpr, /*OtherKind=*/UK_Use, WK_UseAndMod);
- }
-
- void notePostMod(Object O, const Expr *ModExpr, UsageKind UK) {
- UsageInfo &UI = UsageMap[O];
- checkUsage(O, UI, ModExpr, /*OtherKind=*/UK_ModAsSideEffect, WK_ModAndMod);
- addUsage(O, UI, ModExpr, /*UsageKind=*/UK);
- }
-
-public:
- SequenceChecker(Sema &S, const Expr *E,
- SmallVectorImpl<const Expr *> &WorkList)
- : Base(S.Context), SemaRef(S), Region(Tree.root()), WorkList(WorkList) {
- Visit(E);
- // Silence a -Wunused-private-field since WorkList is now unused.
- // TODO: Evaluate if it can be used, and if not remove it.
- (void)this->WorkList;
- }
-
- void VisitStmt(const Stmt *S) {
- // Skip all statements which aren't expressions for now.
- }
-
- void VisitExpr(const Expr *E) {
- // By default, just recurse to evaluated subexpressions.
- Base::VisitStmt(E);
- }
-
- void VisitCoroutineSuspendExpr(const CoroutineSuspendExpr *CSE) {
- for (auto *Sub : CSE->children()) {
- const Expr *ChildExpr = dyn_cast_or_null<Expr>(Sub);
- if (!ChildExpr)
- continue;
-
- if (ChildExpr == CSE->getOperand())
- // Do not recurse over a CoroutineSuspendExpr's operand.
- // The operand is also a subexpression of getCommonExpr(), and
- // recursing into it directly could confuse object management
- // for the sake of sequence tracking.
- continue;
-
- Visit(Sub);
- }
- }
-
- void VisitCastExpr(const CastExpr *E) {
- Object O = Object();
- if (E->getCastKind() == CK_LValueToRValue)
- O = getObject(E->getSubExpr(), false);
-
- if (O)
- notePreUse(O, E);
- VisitExpr(E);
- if (O)
- notePostUse(O, E);
- }
-
- void VisitSequencedExpressions(const Expr *SequencedBefore,
- const Expr *SequencedAfter) {
- SequenceTree::Seq BeforeRegion = Tree.allocate(Region);
- SequenceTree::Seq AfterRegion = Tree.allocate(Region);
- SequenceTree::Seq OldRegion = Region;
-
- {
- SequencedSubexpression SeqBefore(*this);
- Region = BeforeRegion;
- Visit(SequencedBefore);
- }
-
- Region = AfterRegion;
- Visit(SequencedAfter);
-
- Region = OldRegion;
-
- Tree.merge(BeforeRegion);
- Tree.merge(AfterRegion);
- }
-
- void VisitArraySubscriptExpr(const ArraySubscriptExpr *ASE) {
- // C++17 [expr.sub]p1:
- // The expression E1[E2] is identical (by definition) to *((E1)+(E2)). The
- // expression E1 is sequenced before the expression E2.
- if (SemaRef.getLangOpts().CPlusPlus17)
- VisitSequencedExpressions(ASE->getLHS(), ASE->getRHS());
- else {
- Visit(ASE->getLHS());
- Visit(ASE->getRHS());
- }
- }
-
- void VisitBinPtrMemD(const BinaryOperator *BO) { VisitBinPtrMem(BO); }
- void VisitBinPtrMemI(const BinaryOperator *BO) { VisitBinPtrMem(BO); }
- void VisitBinPtrMem(const BinaryOperator *BO) {
- // C++17 [expr.mptr.oper]p4:
- // Abbreviating pm-expression.*cast-expression as E1.*E2, [...]
- // the expression E1 is sequenced before the expression E2.
- if (SemaRef.getLangOpts().CPlusPlus17)
- VisitSequencedExpressions(BO->getLHS(), BO->getRHS());
- else {
- Visit(BO->getLHS());
- Visit(BO->getRHS());
- }
- }
-
- void VisitBinShl(const BinaryOperator *BO) { VisitBinShlShr(BO); }
- void VisitBinShr(const BinaryOperator *BO) { VisitBinShlShr(BO); }
- void VisitBinShlShr(const BinaryOperator *BO) {
- // C++17 [expr.shift]p4:
- // The expression E1 is sequenced before the expression E2.
- if (SemaRef.getLangOpts().CPlusPlus17)
- VisitSequencedExpressions(BO->getLHS(), BO->getRHS());
- else {
- Visit(BO->getLHS());
- Visit(BO->getRHS());
- }
- }
-
- void VisitBinComma(const BinaryOperator *BO) {
- // C++11 [expr.comma]p1:
- // Every value computation and side effect associated with the left
- // expression is sequenced before every value computation and side
- // effect associated with the right expression.
- VisitSequencedExpressions(BO->getLHS(), BO->getRHS());
- }
-
- void VisitBinAssign(const BinaryOperator *BO) {
- SequenceTree::Seq RHSRegion;
- SequenceTree::Seq LHSRegion;
- if (SemaRef.getLangOpts().CPlusPlus17) {
- RHSRegion = Tree.allocate(Region);
- LHSRegion = Tree.allocate(Region);
- } else {
- RHSRegion = Region;
- LHSRegion = Region;
- }
- SequenceTree::Seq OldRegion = Region;
-
- // C++11 [expr.ass]p1:
- // [...] the assignment is sequenced after the value computation
- // of the right and left operands, [...]
- //
- // so check it before inspecting the operands and update the
- // map afterwards.
- Object O = getObject(BO->getLHS(), /*Mod=*/true);
- if (O)
- notePreMod(O, BO);
-
- if (SemaRef.getLangOpts().CPlusPlus17) {
- // C++17 [expr.ass]p1:
- // [...] The right operand is sequenced before the left operand. [...]
- {
- SequencedSubexpression SeqBefore(*this);
- Region = RHSRegion;
- Visit(BO->getRHS());
- }
-
- Region = LHSRegion;
- Visit(BO->getLHS());
-
- if (O && isa<CompoundAssignOperator>(BO))
- notePostUse(O, BO);
-
- } else {
- // C++11 does not specify any sequencing between the LHS and RHS.
- Region = LHSRegion;
- Visit(BO->getLHS());
-
- if (O && isa<CompoundAssignOperator>(BO))
- notePostUse(O, BO);
-
- Region = RHSRegion;
- Visit(BO->getRHS());
- }
-
- // C++11 [expr.ass]p1:
- // the assignment is sequenced [...] before the value computation of the
- // assignment expression.
- // C11 6.5.16/3 has no such rule.
- Region = OldRegion;
- if (O)
- notePostMod(O, BO,
- SemaRef.getLangOpts().CPlusPlus ? UK_ModAsValue
- : UK_ModAsSideEffect);
- if (SemaRef.getLangOpts().CPlusPlus17) {
- Tree.merge(RHSRegion);
- Tree.merge(LHSRegion);
- }
- }
-
- void VisitCompoundAssignOperator(const CompoundAssignOperator *CAO) {
- VisitBinAssign(CAO);
- }
-
- void VisitUnaryPreInc(const UnaryOperator *UO) { VisitUnaryPreIncDec(UO); }
- void VisitUnaryPreDec(const UnaryOperator *UO) { VisitUnaryPreIncDec(UO); }
- void VisitUnaryPreIncDec(const UnaryOperator *UO) {
- Object O = getObject(UO->getSubExpr(), true);
- if (!O)
- return VisitExpr(UO);
-
- notePreMod(O, UO);
- Visit(UO->getSubExpr());
- // C++11 [expr.pre.incr]p1:
- // the expression ++x is equivalent to x+=1
- notePostMod(O, UO,
- SemaRef.getLangOpts().CPlusPlus ? UK_ModAsValue
- : UK_ModAsSideEffect);
- }
-
- void VisitUnaryPostInc(const UnaryOperator *UO) { VisitUnaryPostIncDec(UO); }
- void VisitUnaryPostDec(const UnaryOperator *UO) { VisitUnaryPostIncDec(UO); }
- void VisitUnaryPostIncDec(const UnaryOperator *UO) {
- Object O = getObject(UO->getSubExpr(), true);
- if (!O)
- return VisitExpr(UO);
-
- notePreMod(O, UO);
- Visit(UO->getSubExpr());
- notePostMod(O, UO, UK_ModAsSideEffect);
- }
-
- void VisitBinLOr(const BinaryOperator *BO) {
- // C++11 [expr.log.or]p2:
- // If the second expression is evaluated, every value computation and
- // side effect associated with the first expression is sequenced before
- // every value computation and side effect associated with the
- // second expression.
- SequenceTree::Seq LHSRegion = Tree.allocate(Region);
- SequenceTree::Seq RHSRegion = Tree.allocate(Region);
- SequenceTree::Seq OldRegion = Region;
-
- EvaluationTracker Eval(*this);
- {
- SequencedSubexpression Sequenced(*this);
- Region = LHSRegion;
- Visit(BO->getLHS());
- }
-
- // C++11 [expr.log.or]p1:
- // [...] the second operand is not evaluated if the first operand
- // evaluates to true.
- bool EvalResult = false;
- bool EvalOK = Eval.evaluate(BO->getLHS(), EvalResult);
- bool ShouldVisitRHS = !EvalOK || !EvalResult;
- if (ShouldVisitRHS) {
- Region = RHSRegion;
- Visit(BO->getRHS());
- }
-
- Region = OldRegion;
- Tree.merge(LHSRegion);
- Tree.merge(RHSRegion);
- }
-
- void VisitBinLAnd(const BinaryOperator *BO) {
- // C++11 [expr.log.and]p2:
- // If the second expression is evaluated, every value computation and
- // side effect associated with the first expression is sequenced before
- // every value computation and side effect associated with the
- // second expression.
- SequenceTree::Seq LHSRegion = Tree.allocate(Region);
- SequenceTree::Seq RHSRegion = Tree.allocate(Region);
- SequenceTree::Seq OldRegion = Region;
-
- EvaluationTracker Eval(*this);
- {
- SequencedSubexpression Sequenced(*this);
- Region = LHSRegion;
- Visit(BO->getLHS());
- }
-
- // C++11 [expr.log.and]p1:
- // [...] the second operand is not evaluated if the first operand is false.
- bool EvalResult = false;
- bool EvalOK = Eval.evaluate(BO->getLHS(), EvalResult);
- bool ShouldVisitRHS = !EvalOK || EvalResult;
- if (ShouldVisitRHS) {
- Region = RHSRegion;
- Visit(BO->getRHS());
- }
-
- Region = OldRegion;
- Tree.merge(LHSRegion);
- Tree.merge(RHSRegion);
- }
-
- void VisitAbstractConditionalOperator(const AbstractConditionalOperator *CO) {
- // C++11 [expr.cond]p1:
- // [...] Every value computation and side effect associated with the first
- // expression is sequenced before every value computation and side effect
- // associated with the second or third expression.
- SequenceTree::Seq ConditionRegion = Tree.allocate(Region);
-
- // No sequencing is specified between the true and false expression.
- // However since exactly one of both is going to be evaluated we can
- // consider them to be sequenced. This is needed to avoid warning on
- // something like "x ? y+= 1 : y += 2;" in the case where we will visit
- // both the true and false expressions because we can't evaluate x.
- // This will still allow us to detect an expression like (pre C++17)
- // "(x ? y += 1 : y += 2) = y".
- //
- // We don't wrap the visitation of the true and false expression with
- // SequencedSubexpression because we don't want to downgrade modifications
- // as side effect in the true and false expressions after the visition
- // is done. (for example in the expression "(x ? y++ : y++) + y" we should
- // not warn between the two "y++", but we should warn between the "y++"
- // and the "y".
- SequenceTree::Seq TrueRegion = Tree.allocate(Region);
- SequenceTree::Seq FalseRegion = Tree.allocate(Region);
- SequenceTree::Seq OldRegion = Region;
-
- EvaluationTracker Eval(*this);
- {
- SequencedSubexpression Sequenced(*this);
- Region = ConditionRegion;
- Visit(CO->getCond());
- }
-
- // C++11 [expr.cond]p1:
- // [...] The first expression is contextually converted to bool (Clause 4).
- // It is evaluated and if it is true, the result of the conditional
- // expression is the value of the second expression, otherwise that of the
- // third expression. Only one of the second and third expressions is
- // evaluated. [...]
- bool EvalResult = false;
- bool EvalOK = Eval.evaluate(CO->getCond(), EvalResult);
- bool ShouldVisitTrueExpr = !EvalOK || EvalResult;
- bool ShouldVisitFalseExpr = !EvalOK || !EvalResult;
- if (ShouldVisitTrueExpr) {
- Region = TrueRegion;
- Visit(CO->getTrueExpr());
- }
- if (ShouldVisitFalseExpr) {
- Region = FalseRegion;
- Visit(CO->getFalseExpr());
- }
-
- Region = OldRegion;
- Tree.merge(ConditionRegion);
- Tree.merge(TrueRegion);
- Tree.merge(FalseRegion);
- }
-
- void VisitCallExpr(const CallExpr *CE) {
- // FIXME: CXXNewExpr and CXXDeleteExpr implicitly call functions.
-
- if (CE->isUnevaluatedBuiltinCall(Context))
- return;
-
- // C++11 [intro.execution]p15:
- // When calling a function [...], every value computation and side effect
- // associated with any argument expression, or with the postfix expression
- // designating the called function, is sequenced before execution of every
- // expression or statement in the body of the function [and thus before
- // the value computation of its result].
- SequencedSubexpression Sequenced(*this);
- SemaRef.runWithSufficientStackSpace(CE->getExprLoc(), [&] {
- // C++17 [expr.call]p5
- // The postfix-expression is sequenced before each expression in the
- // expression-list and any default argument. [...]
- SequenceTree::Seq CalleeRegion;
- SequenceTree::Seq OtherRegion;
- if (SemaRef.getLangOpts().CPlusPlus17) {
- CalleeRegion = Tree.allocate(Region);
- OtherRegion = Tree.allocate(Region);
- } else {
- CalleeRegion = Region;
- OtherRegion = Region;
- }
- SequenceTree::Seq OldRegion = Region;
-
- // Visit the callee expression first.
- Region = CalleeRegion;
- if (SemaRef.getLangOpts().CPlusPlus17) {
- SequencedSubexpression Sequenced(*this);
- Visit(CE->getCallee());
- } else {
- Visit(CE->getCallee());
- }
-
- // Then visit the argument expressions.
- Region = OtherRegion;
- for (const Expr *Argument : CE->arguments())
- Visit(Argument);
-
- Region = OldRegion;
- if (SemaRef.getLangOpts().CPlusPlus17) {
- Tree.merge(CalleeRegion);
- Tree.merge(OtherRegion);
- }
- });
- }
-
- void VisitCXXOperatorCallExpr(const CXXOperatorCallExpr *CXXOCE) {
- // C++17 [over.match.oper]p2:
- // [...] the operator notation is first transformed to the equivalent
- // function-call notation as summarized in Table 12 (where @ denotes one
- // of the operators covered in the specified subclause). However, the
- // operands are sequenced in the order prescribed for the built-in
- // operator (Clause 8).
- //
- // From the above only overloaded binary operators and overloaded call
- // operators have sequencing rules in C++17 that we need to handle
- // separately.
- if (!SemaRef.getLangOpts().CPlusPlus17 ||
- (CXXOCE->getNumArgs() != 2 && CXXOCE->getOperator() != OO_Call))
- return VisitCallExpr(CXXOCE);
-
- enum {
- NoSequencing,
- LHSBeforeRHS,
- RHSBeforeLHS,
- LHSBeforeRest
- } SequencingKind;
- switch (CXXOCE->getOperator()) {
- case OO_Equal:
- case OO_PlusEqual:
- case OO_MinusEqual:
- case OO_StarEqual:
- case OO_SlashEqual:
- case OO_PercentEqual:
- case OO_CaretEqual:
- case OO_AmpEqual:
- case OO_PipeEqual:
- case OO_LessLessEqual:
- case OO_GreaterGreaterEqual:
- SequencingKind = RHSBeforeLHS;
- break;
-
- case OO_LessLess:
- case OO_GreaterGreater:
- case OO_AmpAmp:
- case OO_PipePipe:
- case OO_Comma:
- case OO_ArrowStar:
- case OO_Subscript:
- SequencingKind = LHSBeforeRHS;
- break;
-
- case OO_Call:
- SequencingKind = LHSBeforeRest;
- break;
-
- default:
- SequencingKind = NoSequencing;
- break;
- }
-
- if (SequencingKind == NoSequencing)
- return VisitCallExpr(CXXOCE);
-
- // This is a call, so all subexpressions are sequenced before the result.
- SequencedSubexpression Sequenced(*this);
-
- SemaRef.runWithSufficientStackSpace(CXXOCE->getExprLoc(), [&] {
- assert(SemaRef.getLangOpts().CPlusPlus17 &&
- "Should only get there with C++17 and above!");
- assert((CXXOCE->getNumArgs() == 2 || CXXOCE->getOperator() == OO_Call) &&
- "Should only get there with an overloaded binary operator"
- " or an overloaded call operator!");
-
- if (SequencingKind == LHSBeforeRest) {
- assert(CXXOCE->getOperator() == OO_Call &&
- "We should only have an overloaded call operator here!");
-
- // This is very similar to VisitCallExpr, except that we only have the
- // C++17 case. The postfix-expression is the first argument of the
- // CXXOperatorCallExpr. The expressions in the expression-list, if any,
- // are in the following arguments.
- //
- // Note that we intentionally do not visit the callee expression since
- // it is just a decayed reference to a function.
- SequenceTree::Seq PostfixExprRegion = Tree.allocate(Region);
- SequenceTree::Seq ArgsRegion = Tree.allocate(Region);
- SequenceTree::Seq OldRegion = Region;
-
- assert(CXXOCE->getNumArgs() >= 1 &&
- "An overloaded call operator must have at least one argument"
- " for the postfix-expression!");
- const Expr *PostfixExpr = CXXOCE->getArgs()[0];
- llvm::ArrayRef<const Expr *> Args(CXXOCE->getArgs() + 1,
- CXXOCE->getNumArgs() - 1);
-
- // Visit the postfix-expression first.
- {
- Region = PostfixExprRegion;
- SequencedSubexpression Sequenced(*this);
- Visit(PostfixExpr);
- }
-
- // Then visit the argument expressions.
- Region = ArgsRegion;
- for (const Expr *Arg : Args)
- Visit(Arg);
-
- Region = OldRegion;
- Tree.merge(PostfixExprRegion);
- Tree.merge(ArgsRegion);
- } else {
- assert(CXXOCE->getNumArgs() == 2 &&
- "Should only have two arguments here!");
- assert((SequencingKind == LHSBeforeRHS ||
- SequencingKind == RHSBeforeLHS) &&
- "Unexpected sequencing kind!");
-
- // We do not visit the callee expression since it is just a decayed
- // reference to a function.
- const Expr *E1 = CXXOCE->getArg(0);
- const Expr *E2 = CXXOCE->getArg(1);
- if (SequencingKind == RHSBeforeLHS)
- std::swap(E1, E2);
-
- return VisitSequencedExpressions(E1, E2);
- }
- });
- }
-
- void VisitCXXConstructExpr(const CXXConstructExpr *CCE) {
- // This is a call, so all subexpressions are sequenced before the result.
- SequencedSubexpression Sequenced(*this);
-
- if (!CCE->isListInitialization())
- return VisitExpr(CCE);
-
- // In C++11, list initializations are sequenced.
- SequenceExpressionsInOrder(
- llvm::ArrayRef(CCE->getArgs(), CCE->getNumArgs()));
- }
-
- void VisitInitListExpr(const InitListExpr *ILE) {
- if (!SemaRef.getLangOpts().CPlusPlus11)
- return VisitExpr(ILE);
-
- // In C++11, list initializations are sequenced.
- SequenceExpressionsInOrder(ILE->inits());
- }
-
- void VisitCXXParenListInitExpr(const CXXParenListInitExpr *PLIE) {
- // C++20 parenthesized list initializations are sequenced. See C++20
- // [decl.init.general]p16.5 and [decl.init.general]p16.6.2.2.
- SequenceExpressionsInOrder(PLIE->getInitExprs());
- }
-
-private:
- void SequenceExpressionsInOrder(ArrayRef<const Expr *> ExpressionList) {
- SmallVector<SequenceTree::Seq, 32> Elts;
- SequenceTree::Seq Parent = Region;
- for (const Expr *E : ExpressionList) {
- if (!E)
- continue;
- Region = Tree.allocate(Parent);
- Elts.push_back(Region);
- Visit(E);
- }
-
- // Forget that the initializers are sequenced.
- Region = Parent;
- for (unsigned I = 0; I < Elts.size(); ++I)
- Tree.merge(Elts[I]);
- }
-};
-
-SequenceChecker::UsageInfo::UsageInfo() = default;
-
-} // namespace
-
-void Sema::CheckUnsequencedOperations(const Expr *E) {
- SmallVector<const Expr *, 8> WorkList;
- WorkList.push_back(E);
- while (!WorkList.empty()) {
- const Expr *Item = WorkList.pop_back_val();
- SequenceChecker(*this, Item, WorkList);
- }
-}
-
-void Sema::CheckCompletedExpr(Expr *E, SourceLocation CheckLoc,
- bool IsConstexpr) {
- llvm::SaveAndRestore ConstantContext(isConstantEvaluatedOverride,
- IsConstexpr || isa<ConstantExpr>(E));
- CheckImplicitConversions(E, CheckLoc);
- if (!E->isInstantiationDependent())
- CheckUnsequencedOperations(E);
- if (!IsConstexpr && !E->isValueDependent())
- CheckForIntOverflow(E);
-}
-
-void Sema::CheckBitFieldInitialization(SourceLocation InitLoc,
- FieldDecl *BitField,
- Expr *Init) {
- (void) AnalyzeBitFieldAssignment(*this, BitField, Init, InitLoc);
-}
-
-static void diagnoseArrayStarInParamType(Sema &S, QualType PType,
- SourceLocation Loc) {
- if (!PType->isVariablyModifiedType())
- return;
- if (const auto *PointerTy = dyn_cast<PointerType>(PType)) {
- diagnoseArrayStarInParamType(S, PointerTy->getPointeeType(), Loc);
- return;
- }
- if (const auto *ReferenceTy = dyn_cast<ReferenceType>(PType)) {
- diagnoseArrayStarInParamType(S, ReferenceTy->getPointeeType(), Loc);
- return;
- }
- if (const auto *ParenTy = dyn_cast<ParenType>(PType)) {
- diagnoseArrayStarInParamType(S, ParenTy->getInnerType(), Loc);
- return;
- }
-
- const ArrayType *AT = S.Context.getAsArrayType(PType);
- if (!AT)
- return;
-
- if (AT->getSizeModifier() != ArraySizeModifier::Star) {
- diagnoseArrayStarInParamType(S, AT->getElementType(), Loc);
- return;
- }
-
- S.Diag(Loc, diag::err_array_star_in_function_definition);
-}
-
-bool Sema::CheckParmsForFunctionDef(ArrayRef<ParmVarDecl *> Parameters,
- bool CheckParameterNames) {
- bool HasInvalidParm = false;
- for (ParmVarDecl *Param : Parameters) {
- assert(Param && "null in a parameter list");
- // C99 6.7.5.3p4: the parameters in a parameter type list in a
- // function declarator that is part of a function definition of
- // that function shall not have incomplete type.
- //
- // C++23 [dcl.fct.def.general]/p2
- // The type of a parameter [...] for a function definition
- // shall not be a (possibly cv-qualified) class type that is incomplete
- // or abstract within the function body unless the function is deleted.
- if (!Param->isInvalidDecl() &&
- (RequireCompleteType(Param->getLocation(), Param->getType(),
- diag::err_typecheck_decl_incomplete_type) ||
- RequireNonAbstractType(Param->getBeginLoc(), Param->getOriginalType(),
- diag::err_abstract_type_in_decl,
- AbstractParamType))) {
- Param->setInvalidDecl();
- HasInvalidParm = true;
- }
-
- // C99 6.9.1p5: If the declarator includes a parameter type list, the
- // declaration of each parameter shall include an identifier.
- if (CheckParameterNames && Param->getIdentifier() == nullptr &&
- !Param->isImplicit() && !getLangOpts().CPlusPlus) {
- // Diagnose this as an extension in C17 and earlier.
- if (!getLangOpts().C23)
- Diag(Param->getLocation(), diag::ext_parameter_name_omitted_c23);
- }
-
- // C99 6.7.5.3p12:
- // If the function declarator is not part of a definition of that
- // function, parameters may have incomplete type and may use the [*]
- // notation in their sequences of declarator specifiers to specify
- // variable length array types.
- QualType PType = Param->getOriginalType();
- // FIXME: This diagnostic should point the '[*]' if source-location
- // information is added for it.
- diagnoseArrayStarInParamType(*this, PType, Param->getLocation());
-
- // If the parameter is a c++ class type and it has to be destructed in the
- // callee function, declare the destructor so that it can be called by the
- // callee function. Do not perform any direct access check on the dtor here.
- if (!Param->isInvalidDecl()) {
- if (CXXRecordDecl *ClassDecl = Param->getType()->getAsCXXRecordDecl()) {
- if (!ClassDecl->isInvalidDecl() &&
- !ClassDecl->hasIrrelevantDestructor() &&
- !ClassDecl->isDependentContext() &&
- ClassDecl->isParamDestroyedInCallee()) {
- CXXDestructorDecl *Destructor = LookupDestructor(ClassDecl);
- MarkFunctionReferenced(Param->getLocation(), Destructor);
- DiagnoseUseOfDecl(Destructor, Param->getLocation());
- }
- }
- }
-
- // Parameters with the pass_object_size attribute only need to be marked
- // constant at function definitions. Because we lack information about
- // whether we're on a declaration or definition when we're instantiating the
- // attribute, we need to check for constness here.
- if (const auto *Attr = Param->getAttr<PassObjectSizeAttr>())
- if (!Param->getType().isConstQualified())
- Diag(Param->getLocation(), diag::err_attribute_pointers_only)
- << Attr->getSpelling() << 1;
-
- // Check for parameter names shadowing fields from the class.
- if (LangOpts.CPlusPlus && !Param->isInvalidDecl()) {
- // The owning context for the parameter should be the function, but we
- // want to see if this function's declaration context is a record.
- DeclContext *DC = Param->getDeclContext();
- if (DC && DC->isFunctionOrMethod()) {
- if (auto *RD = dyn_cast<CXXRecordDecl>(DC->getParent()))
- CheckShadowInheritedFields(Param->getLocation(), Param->getDeclName(),
- RD, /*DeclIsField*/ false);
- }
- }
-
- if (!Param->isInvalidDecl() &&
- Param->getOriginalType()->isWebAssemblyTableType()) {
- Param->setInvalidDecl();
- HasInvalidParm = true;
- Diag(Param->getLocation(), diag::err_wasm_table_as_function_parameter);
- }
- }
-
- return HasInvalidParm;
-}
-
-std::optional<std::pair<
- CharUnits, CharUnits>> static getBaseAlignmentAndOffsetFromPtr(const Expr
- *E,
- ASTContext
- &Ctx);
-
-/// Compute the alignment and offset of the base class object given the
-/// derived-to-base cast expression and the alignment and offset of the derived
-/// class object.
-static std::pair<CharUnits, CharUnits>
-getDerivedToBaseAlignmentAndOffset(const CastExpr *CE, QualType DerivedType,
- CharUnits BaseAlignment, CharUnits Offset,
- ASTContext &Ctx) {
- for (auto PathI = CE->path_begin(), PathE = CE->path_end(); PathI != PathE;
- ++PathI) {
- const CXXBaseSpecifier *Base = *PathI;
- const CXXRecordDecl *BaseDecl = Base->getType()->getAsCXXRecordDecl();
- if (Base->isVirtual()) {
- // The complete object may have a lower alignment than the non-virtual
- // alignment of the base, in which case the base may be misaligned. Choose
- // the smaller of the non-virtual alignment and BaseAlignment, which is a
- // conservative lower bound of the complete object alignment.
- CharUnits NonVirtualAlignment =
- Ctx.getASTRecordLayout(BaseDecl).getNonVirtualAlignment();
- BaseAlignment = std::min(BaseAlignment, NonVirtualAlignment);
- Offset = CharUnits::Zero();
- } else {
- const ASTRecordLayout &RL =
- Ctx.getASTRecordLayout(DerivedType->getAsCXXRecordDecl());
- Offset += RL.getBaseClassOffset(BaseDecl);
- }
- DerivedType = Base->getType();
- }
-
- return std::make_pair(BaseAlignment, Offset);
-}
-
-/// Compute the alignment and offset of a binary additive operator.
-static std::optional<std::pair<CharUnits, CharUnits>>
-getAlignmentAndOffsetFromBinAddOrSub(const Expr *PtrE, const Expr *IntE,
- bool IsSub, ASTContext &Ctx) {
- QualType PointeeType = PtrE->getType()->getPointeeType();
-
- if (!PointeeType->isConstantSizeType())
- return std::nullopt;
-
- auto P = getBaseAlignmentAndOffsetFromPtr(PtrE, Ctx);
-
- if (!P)
- return std::nullopt;
-
- CharUnits EltSize = Ctx.getTypeSizeInChars(PointeeType);
- if (std::optional<llvm::APSInt> IdxRes = IntE->getIntegerConstantExpr(Ctx)) {
- CharUnits Offset = EltSize * IdxRes->getExtValue();
- if (IsSub)
- Offset = -Offset;
- return std::make_pair(P->first, P->second + Offset);
- }
-
- // If the integer expression isn't a constant expression, compute the lower
- // bound of the alignment using the alignment and offset of the pointer
- // expression and the element size.
- return std::make_pair(
- P->first.alignmentAtOffset(P->second).alignmentAtOffset(EltSize),
- CharUnits::Zero());
-}
-
-/// This helper function takes an lvalue expression and returns the alignment of
-/// a VarDecl and a constant offset from the VarDecl.
-std::optional<std::pair<
- CharUnits,
- CharUnits>> static getBaseAlignmentAndOffsetFromLValue(const Expr *E,
- ASTContext &Ctx) {
- E = E->IgnoreParens();
- switch (E->getStmtClass()) {
- default:
- break;
- case Stmt::CStyleCastExprClass:
- case Stmt::CXXStaticCastExprClass:
- case Stmt::ImplicitCastExprClass: {
- auto *CE = cast<CastExpr>(E);
- const Expr *From = CE->getSubExpr();
- switch (CE->getCastKind()) {
- default:
- break;
- case CK_NoOp:
- return getBaseAlignmentAndOffsetFromLValue(From, Ctx);
- case CK_UncheckedDerivedToBase:
- case CK_DerivedToBase: {
- auto P = getBaseAlignmentAndOffsetFromLValue(From, Ctx);
- if (!P)
- break;
- return getDerivedToBaseAlignmentAndOffset(CE, From->getType(), P->first,
- P->second, Ctx);
- }
- }
- break;
- }
- case Stmt::ArraySubscriptExprClass: {
- auto *ASE = cast<ArraySubscriptExpr>(E);
- return getAlignmentAndOffsetFromBinAddOrSub(ASE->getBase(), ASE->getIdx(),
- false, Ctx);
- }
- case Stmt::DeclRefExprClass: {
- if (auto *VD = dyn_cast<VarDecl>(cast<DeclRefExpr>(E)->getDecl())) {
- // FIXME: If VD is captured by copy or is an escaping __block variable,
- // use the alignment of VD's type.
- if (!VD->getType()->isReferenceType()) {
- // Dependent alignment cannot be resolved -> bail out.
- if (VD->hasDependentAlignment())
- break;
- return std::make_pair(Ctx.getDeclAlign(VD), CharUnits::Zero());
- }
- if (VD->hasInit())
- return getBaseAlignmentAndOffsetFromLValue(VD->getInit(), Ctx);
- }
- break;
- }
- case Stmt::MemberExprClass: {
- auto *ME = cast<MemberExpr>(E);
- auto *FD = dyn_cast<FieldDecl>(ME->getMemberDecl());
- if (!FD || FD->getType()->isReferenceType() ||
- FD->getParent()->isInvalidDecl())
- break;
- std::optional<std::pair<CharUnits, CharUnits>> P;
- if (ME->isArrow())
- P = getBaseAlignmentAndOffsetFromPtr(ME->getBase(), Ctx);
- else
- P = getBaseAlignmentAndOffsetFromLValue(ME->getBase(), Ctx);
- if (!P)
- break;
- const ASTRecordLayout &Layout = Ctx.getASTRecordLayout(FD->getParent());
- uint64_t Offset = Layout.getFieldOffset(FD->getFieldIndex());
- return std::make_pair(P->first,
- P->second + CharUnits::fromQuantity(Offset));
- }
- case Stmt::UnaryOperatorClass: {
- auto *UO = cast<UnaryOperator>(E);
- switch (UO->getOpcode()) {
- default:
- break;
- case UO_Deref:
- return getBaseAlignmentAndOffsetFromPtr(UO->getSubExpr(), Ctx);
- }
- break;
- }
- case Stmt::BinaryOperatorClass: {
- auto *BO = cast<BinaryOperator>(E);
- auto Opcode = BO->getOpcode();
- switch (Opcode) {
- default:
- break;
- case BO_Comma:
- return getBaseAlignmentAndOffsetFromLValue(BO->getRHS(), Ctx);
- }
- break;
- }
- }
- return std::nullopt;
-}
-
-/// This helper function takes a pointer expression and returns the alignment of
-/// a VarDecl and a constant offset from the VarDecl.
-std::optional<std::pair<
- CharUnits, CharUnits>> static getBaseAlignmentAndOffsetFromPtr(const Expr
- *E,
- ASTContext
- &Ctx) {
- E = E->IgnoreParens();
- switch (E->getStmtClass()) {
- default:
- break;
- case Stmt::CStyleCastExprClass:
- case Stmt::CXXStaticCastExprClass:
- case Stmt::ImplicitCastExprClass: {
- auto *CE = cast<CastExpr>(E);
- const Expr *From = CE->getSubExpr();
- switch (CE->getCastKind()) {
- default:
- break;
- case CK_NoOp:
- return getBaseAlignmentAndOffsetFromPtr(From, Ctx);
- case CK_ArrayToPointerDecay:
- return getBaseAlignmentAndOffsetFromLValue(From, Ctx);
- case CK_UncheckedDerivedToBase:
- case CK_DerivedToBase: {
- auto P = getBaseAlignmentAndOffsetFromPtr(From, Ctx);
- if (!P)
- break;
- return getDerivedToBaseAlignmentAndOffset(
- CE, From->getType()->getPointeeType(), P->first, P->second, Ctx);
- }
- }
- break;
- }
- case Stmt::CXXThisExprClass: {
- auto *RD = E->getType()->getPointeeType()->getAsCXXRecordDecl();
- CharUnits Alignment = Ctx.getASTRecordLayout(RD).getNonVirtualAlignment();
- return std::make_pair(Alignment, CharUnits::Zero());
- }
- case Stmt::UnaryOperatorClass: {
- auto *UO = cast<UnaryOperator>(E);
- if (UO->getOpcode() == UO_AddrOf)
- return getBaseAlignmentAndOffsetFromLValue(UO->getSubExpr(), Ctx);
- break;
- }
- case Stmt::BinaryOperatorClass: {
- auto *BO = cast<BinaryOperator>(E);
- auto Opcode = BO->getOpcode();
- switch (Opcode) {
- default:
- break;
- case BO_Add:
- case BO_Sub: {
- const Expr *LHS = BO->getLHS(), *RHS = BO->getRHS();
- if (Opcode == BO_Add && !RHS->getType()->isIntegralOrEnumerationType())
- std::swap(LHS, RHS);
- return getAlignmentAndOffsetFromBinAddOrSub(LHS, RHS, Opcode == BO_Sub,
- Ctx);
- }
- case BO_Comma:
- return getBaseAlignmentAndOffsetFromPtr(BO->getRHS(), Ctx);
- }
- break;
- }
- }
- return std::nullopt;
-}
-
-static CharUnits getPresumedAlignmentOfPointer(const Expr *E, Sema &S) {
- // See if we can compute the alignment of a VarDecl and an offset from it.
- std::optional<std::pair<CharUnits, CharUnits>> P =
- getBaseAlignmentAndOffsetFromPtr(E, S.Context);
-
- if (P)
- return P->first.alignmentAtOffset(P->second);
-
- // If that failed, return the type's alignment.
- return S.Context.getTypeAlignInChars(E->getType()->getPointeeType());
-}
-
-void Sema::CheckCastAlign(Expr *Op, QualType T, SourceRange TRange) {
- // This is actually a lot of work to potentially be doing on every
- // cast; don't do it if we're ignoring -Wcast_align (as is the default).
- if (getDiagnostics().isIgnored(diag::warn_cast_align, TRange.getBegin()))
- return;
-
- // Ignore dependent types.
- if (T->isDependentType() || Op->getType()->isDependentType())
- return;
-
- // Require that the destination be a pointer type.
- const PointerType *DestPtr = T->getAs<PointerType>();
- if (!DestPtr) return;
-
- // If the destination has alignment 1, we're done.
- QualType DestPointee = DestPtr->getPointeeType();
- if (DestPointee->isIncompleteType()) return;
- CharUnits DestAlign = Context.getTypeAlignInChars(DestPointee);
- if (DestAlign.isOne()) return;
-
- // Require that the source be a pointer type.
- const PointerType *SrcPtr = Op->getType()->getAs<PointerType>();
- if (!SrcPtr) return;
- QualType SrcPointee = SrcPtr->getPointeeType();
-
- // Explicitly allow casts from cv void*. We already implicitly
- // allowed casts to cv void*, since they have alignment 1.
- // Also allow casts involving incomplete types, which implicitly
- // includes 'void'.
- if (SrcPointee->isIncompleteType()) return;
-
- CharUnits SrcAlign = getPresumedAlignmentOfPointer(Op, *this);
-
- if (SrcAlign >= DestAlign) return;
-
- Diag(TRange.getBegin(), diag::warn_cast_align)
- << Op->getType() << T
- << static_cast<unsigned>(SrcAlign.getQuantity())
- << static_cast<unsigned>(DestAlign.getQuantity())
- << TRange << Op->getSourceRange();
-}
-
-void Sema::CheckArrayAccess(const Expr *BaseExpr, const Expr *IndexExpr,
- const ArraySubscriptExpr *ASE,
- bool AllowOnePastEnd, bool IndexNegated) {
- // Already diagnosed by the constant evaluator.
- if (isConstantEvaluatedContext())
- return;
-
- IndexExpr = IndexExpr->IgnoreParenImpCasts();
- if (IndexExpr->isValueDependent())
- return;
-
- const Type *EffectiveType =
- BaseExpr->getType()->getPointeeOrArrayElementType();
- BaseExpr = BaseExpr->IgnoreParenCasts();
- const ConstantArrayType *ArrayTy =
- Context.getAsConstantArrayType(BaseExpr->getType());
-
- LangOptions::StrictFlexArraysLevelKind
- StrictFlexArraysLevel = getLangOpts().getStrictFlexArraysLevel();
-
- const Type *BaseType =
- ArrayTy == nullptr ? nullptr : ArrayTy->getElementType().getTypePtr();
- bool IsUnboundedArray =
- BaseType == nullptr || BaseExpr->isFlexibleArrayMemberLike(
- Context, StrictFlexArraysLevel,
- /*IgnoreTemplateOrMacroSubstitution=*/true);
- if (EffectiveType->isDependentType() ||
- (!IsUnboundedArray && BaseType->isDependentType()))
- return;
-
- Expr::EvalResult Result;
- if (!IndexExpr->EvaluateAsInt(Result, Context, Expr::SE_AllowSideEffects))
- return;
-
- llvm::APSInt index = Result.Val.getInt();
- if (IndexNegated) {
- index.setIsUnsigned(false);
- index = -index;
- }
-
- if (IsUnboundedArray) {
- if (EffectiveType->isFunctionType())
- return;
- if (index.isUnsigned() || !index.isNegative()) {
- const auto &ASTC = getASTContext();
- unsigned AddrBits = ASTC.getTargetInfo().getPointerWidth(
- EffectiveType->getCanonicalTypeInternal().getAddressSpace());
- if (index.getBitWidth() < AddrBits)
- index = index.zext(AddrBits);
- std::optional<CharUnits> ElemCharUnits =
- ASTC.getTypeSizeInCharsIfKnown(EffectiveType);
- // PR50741 - If EffectiveType has unknown size (e.g., if it's a void
- // pointer) bounds-checking isn't meaningful.
- if (!ElemCharUnits || ElemCharUnits->isZero())
- return;
- llvm::APInt ElemBytes(index.getBitWidth(), ElemCharUnits->getQuantity());
- // If index has more active bits than address space, we already know
- // we have a bounds violation to warn about. Otherwise, compute
- // address of (index + 1)th element, and warn about bounds violation
- // only if that address exceeds address space.
- if (index.getActiveBits() <= AddrBits) {
- bool Overflow;
- llvm::APInt Product(index);
- Product += 1;
- Product = Product.umul_ov(ElemBytes, Overflow);
- if (!Overflow && Product.getActiveBits() <= AddrBits)
- return;
- }
-
- // Need to compute max possible elements in address space, since that
- // is included in diag message.
- llvm::APInt MaxElems = llvm::APInt::getMaxValue(AddrBits);
- MaxElems = MaxElems.zext(std::max(AddrBits + 1, ElemBytes.getBitWidth()));
- MaxElems += 1;
- ElemBytes = ElemBytes.zextOrTrunc(MaxElems.getBitWidth());
- MaxElems = MaxElems.udiv(ElemBytes);
-
- unsigned DiagID =
- ASE ? diag::warn_array_index_exceeds_max_addressable_bounds
- : diag::warn_ptr_arith_exceeds_max_addressable_bounds;
-
- // Diag message shows element size in bits and in "bytes" (platform-
- // dependent CharUnits)
- DiagRuntimeBehavior(BaseExpr->getBeginLoc(), BaseExpr,
- PDiag(DiagID) << index << AddrBits
- << (unsigned)ASTC.toBits(*ElemCharUnits)
- << ElemBytes << MaxElems
- << MaxElems.getZExtValue()
- << IndexExpr->getSourceRange());
-
- const NamedDecl *ND = nullptr;
- // Try harder to find a NamedDecl to point at in the note.
- while (const auto *ASE = dyn_cast<ArraySubscriptExpr>(BaseExpr))
- BaseExpr = ASE->getBase()->IgnoreParenCasts();
- if (const auto *DRE = dyn_cast<DeclRefExpr>(BaseExpr))
- ND = DRE->getDecl();
- if (const auto *ME = dyn_cast<MemberExpr>(BaseExpr))
- ND = ME->getMemberDecl();
-
- if (ND)
- DiagRuntimeBehavior(ND->getBeginLoc(), BaseExpr,
- PDiag(diag::note_array_declared_here) << ND);
- }
- return;
- }
-
- if (index.isUnsigned() || !index.isNegative()) {
- // It is possible that the type of the base expression after
- // IgnoreParenCasts is incomplete, even though the type of the base
- // expression before IgnoreParenCasts is complete (see PR39746 for an
- // example). In this case we have no information about whether the array
- // access exceeds the array bounds. However we can still diagnose an array
- // access which precedes the array bounds.
- if (BaseType->isIncompleteType())
- return;
-
- llvm::APInt size = ArrayTy->getSize();
-
- if (BaseType != EffectiveType) {
- // Make sure we're comparing apples to apples when comparing index to
- // size.
- uint64_t ptrarith_typesize = Context.getTypeSize(EffectiveType);
- uint64_t array_typesize = Context.getTypeSize(BaseType);
-
- // Handle ptrarith_typesize being zero, such as when casting to void*.
- // Use the size in bits (what "getTypeSize()" returns) rather than bytes.
- if (!ptrarith_typesize)
- ptrarith_typesize = Context.getCharWidth();
-
- if (ptrarith_typesize != array_typesize) {
- // There's a cast to a different size type involved.
- uint64_t ratio = array_typesize / ptrarith_typesize;
-
- // TODO: Be smarter about handling cases where array_typesize is not a
- // multiple of ptrarith_typesize.
- if (ptrarith_typesize * ratio == array_typesize)
- size *= llvm::APInt(size.getBitWidth(), ratio);
- }
- }
-
- if (size.getBitWidth() > index.getBitWidth())
- index = index.zext(size.getBitWidth());
- else if (size.getBitWidth() < index.getBitWidth())
- size = size.zext(index.getBitWidth());
-
- // For array subscripting the index must be less than size, but for pointer
- // arithmetic also allow the index (offset) to be equal to size since
- // computing the next address after the end of the array is legal and
- // commonly done e.g. in C++ iterators and range-based for loops.
- if (AllowOnePastEnd ? index.ule(size) : index.ult(size))
- return;
-
- // Suppress the warning if the subscript expression (as identified by the
- // ']' location) and the index expression are both from macro expansions
- // within a system header.
- if (ASE) {
- SourceLocation RBracketLoc = SourceMgr.getSpellingLoc(
- ASE->getRBracketLoc());
- if (SourceMgr.isInSystemHeader(RBracketLoc)) {
- SourceLocation IndexLoc =
- SourceMgr.getSpellingLoc(IndexExpr->getBeginLoc());
- if (SourceMgr.isWrittenInSameFile(RBracketLoc, IndexLoc))
- return;
- }
- }
-
- unsigned DiagID = ASE ? diag::warn_array_index_exceeds_bounds
- : diag::warn_ptr_arith_exceeds_bounds;
- unsigned CastMsg = (!ASE || BaseType == EffectiveType) ? 0 : 1;
- QualType CastMsgTy = ASE ? ASE->getLHS()->getType() : QualType();
-
- DiagRuntimeBehavior(BaseExpr->getBeginLoc(), BaseExpr,
- PDiag(DiagID)
- << index << ArrayTy->desugar() << CastMsg
- << CastMsgTy << IndexExpr->getSourceRange());
- } else {
- unsigned DiagID = diag::warn_array_index_precedes_bounds;
- if (!ASE) {
- DiagID = diag::warn_ptr_arith_precedes_bounds;
- if (index.isNegative()) index = -index;
- }
-
- DiagRuntimeBehavior(BaseExpr->getBeginLoc(), BaseExpr,
- PDiag(DiagID) << index << IndexExpr->getSourceRange());
- }
-
- const NamedDecl *ND = nullptr;
- // Try harder to find a NamedDecl to point at in the note.
- while (const auto *ASE = dyn_cast<ArraySubscriptExpr>(BaseExpr))
- BaseExpr = ASE->getBase()->IgnoreParenCasts();
- if (const auto *DRE = dyn_cast<DeclRefExpr>(BaseExpr))
- ND = DRE->getDecl();
- if (const auto *ME = dyn_cast<MemberExpr>(BaseExpr))
- ND = ME->getMemberDecl();
-
- if (ND)
- DiagRuntimeBehavior(ND->getBeginLoc(), BaseExpr,
- PDiag(diag::note_array_declared_here) << ND);
-}
-
-void Sema::CheckArrayAccess(const Expr *expr) {
- int AllowOnePastEnd = 0;
- while (expr) {
- expr = expr->IgnoreParenImpCasts();
- switch (expr->getStmtClass()) {
- case Stmt::ArraySubscriptExprClass: {
- const ArraySubscriptExpr *ASE = cast<ArraySubscriptExpr>(expr);
- CheckArrayAccess(ASE->getBase(), ASE->getIdx(), ASE,
- AllowOnePastEnd > 0);
- expr = ASE->getBase();
- break;
- }
- case Stmt::MemberExprClass: {
- expr = cast<MemberExpr>(expr)->getBase();
- break;
- }
- case Stmt::CXXMemberCallExprClass: {
- expr = cast<CXXMemberCallExpr>(expr)->getImplicitObjectArgument();
- break;
- }
- case Stmt::ArraySectionExprClass: {
- const ArraySectionExpr *ASE = cast<ArraySectionExpr>(expr);
- // FIXME: We should probably be checking all of the elements to the
- // 'length' here as well.
- if (ASE->getLowerBound())
- CheckArrayAccess(ASE->getBase(), ASE->getLowerBound(),
- /*ASE=*/nullptr, AllowOnePastEnd > 0);
- return;
- }
- case Stmt::UnaryOperatorClass: {
- // Only unwrap the * and & unary operators
- const UnaryOperator *UO = cast<UnaryOperator>(expr);
- expr = UO->getSubExpr();
- switch (UO->getOpcode()) {
- case UO_AddrOf:
- AllowOnePastEnd++;
- break;
- case UO_Deref:
- AllowOnePastEnd--;
- break;
- default:
- return;
- }
- break;
- }
- case Stmt::ConditionalOperatorClass: {
- const ConditionalOperator *cond = cast<ConditionalOperator>(expr);
- if (const Expr *lhs = cond->getLHS())
- CheckArrayAccess(lhs);
- if (const Expr *rhs = cond->getRHS())
- CheckArrayAccess(rhs);
- return;
- }
- case Stmt::CXXOperatorCallExprClass: {
- const auto *OCE = cast<CXXOperatorCallExpr>(expr);
- for (const auto *Arg : OCE->arguments())
- CheckArrayAccess(Arg);
- return;
- }
- default:
- return;
- }
- }
-}
-
-static bool checkUnsafeAssignLiteral(Sema &S, SourceLocation Loc,
- Expr *RHS, bool isProperty) {
- // Check if RHS is an Objective-C object literal, which also can get
- // immediately zapped in a weak reference. Note that we explicitly
- // allow ObjCStringLiterals, since those are designed to never really die.
- RHS = RHS->IgnoreParenImpCasts();
-
- // This enum needs to match with the 'select' in
- // warn_objc_arc_literal_assign (off-by-1).
- SemaObjC::ObjCLiteralKind Kind = S.ObjC().CheckLiteralKind(RHS);
- if (Kind == SemaObjC::LK_String || Kind == SemaObjC::LK_None)
- return false;
-
- S.Diag(Loc, diag::warn_arc_literal_assign)
- << (unsigned) Kind
- << (isProperty ? 0 : 1)
- << RHS->getSourceRange();
-
- return true;
-}
-
-static bool checkUnsafeAssignObject(Sema &S, SourceLocation Loc,
- Qualifiers::ObjCLifetime LT,
- Expr *RHS, bool isProperty) {
- // Strip off any implicit cast added to get to the one ARC-specific.
- while (ImplicitCastExpr *cast = dyn_cast<ImplicitCastExpr>(RHS)) {
- if (cast->getCastKind() == CK_ARCConsumeObject) {
- S.Diag(Loc, diag::warn_arc_retained_assign)
- << (LT == Qualifiers::OCL_ExplicitNone)
- << (isProperty ? 0 : 1)
- << RHS->getSourceRange();
- return true;
- }
- RHS = cast->getSubExpr();
- }
-
- if (LT == Qualifiers::OCL_Weak &&
- checkUnsafeAssignLiteral(S, Loc, RHS, isProperty))
- return true;
-
- return false;
-}
-
-bool Sema::checkUnsafeAssigns(SourceLocation Loc,
- QualType LHS, Expr *RHS) {
- Qualifiers::ObjCLifetime LT = LHS.getObjCLifetime();
-
- if (LT != Qualifiers::OCL_Weak && LT != Qualifiers::OCL_ExplicitNone)
- return false;
-
- if (checkUnsafeAssignObject(*this, Loc, LT, RHS, false))
- return true;
-
- return false;
-}
-
-void Sema::checkUnsafeExprAssigns(SourceLocation Loc,
- Expr *LHS, Expr *RHS) {
- QualType LHSType;
- // PropertyRef on LHS type need be directly obtained from
- // its declaration as it has a PseudoType.
- ObjCPropertyRefExpr *PRE
- = dyn_cast<ObjCPropertyRefExpr>(LHS->IgnoreParens());
- if (PRE && !PRE->isImplicitProperty()) {
- const ObjCPropertyDecl *PD = PRE->getExplicitProperty();
- if (PD)
- LHSType = PD->getType();
- }
-
- if (LHSType.isNull())
- LHSType = LHS->getType();
-
- Qualifiers::ObjCLifetime LT = LHSType.getObjCLifetime();
-
- if (LT == Qualifiers::OCL_Weak) {
- if (!Diags.isIgnored(diag::warn_arc_repeated_use_of_weak, Loc))
- getCurFunction()->markSafeWeakUse(LHS);
- }
-
- if (checkUnsafeAssigns(Loc, LHSType, RHS))
- return;
-
- // FIXME. Check for other life times.
- if (LT != Qualifiers::OCL_None)
- return;
-
- if (PRE) {
- if (PRE->isImplicitProperty())
- return;
- const ObjCPropertyDecl *PD = PRE->getExplicitProperty();
- if (!PD)
- return;
-
- unsigned Attributes = PD->getPropertyAttributes();
- if (Attributes & ObjCPropertyAttribute::kind_assign) {
- // when 'assign' attribute was not explicitly specified
- // by user, ignore it and rely on property type itself
- // for lifetime info.
- unsigned AsWrittenAttr = PD->getPropertyAttributesAsWritten();
- if (!(AsWrittenAttr & ObjCPropertyAttribute::kind_assign) &&
- LHSType->isObjCRetainableType())
- return;
-
- while (ImplicitCastExpr *cast = dyn_cast<ImplicitCastExpr>(RHS)) {
- if (cast->getCastKind() == CK_ARCConsumeObject) {
- Diag(Loc, diag::warn_arc_retained_property_assign)
- << RHS->getSourceRange();
- return;
- }
- RHS = cast->getSubExpr();
- }
- } else if (Attributes & ObjCPropertyAttribute::kind_weak) {
- if (checkUnsafeAssignObject(*this, Loc, Qualifiers::OCL_Weak, RHS, true))
- return;
- }
- }
-}
-
-//===--- CHECK: Empty statement body (-Wempty-body) ---------------------===//
-
-static bool ShouldDiagnoseEmptyStmtBody(const SourceManager &SourceMgr,
- SourceLocation StmtLoc,
- const NullStmt *Body) {
- // Do not warn if the body is a macro that expands to nothing, e.g:
- //
- // #define CALL(x)
- // if (condition)
- // CALL(0);
- if (Body->hasLeadingEmptyMacro())
- return false;
-
- // Get line numbers of statement and body.
- bool StmtLineInvalid;
- unsigned StmtLine = SourceMgr.getPresumedLineNumber(StmtLoc,
- &StmtLineInvalid);
- if (StmtLineInvalid)
- return false;
-
- bool BodyLineInvalid;
- unsigned BodyLine = SourceMgr.getSpellingLineNumber(Body->getSemiLoc(),
- &BodyLineInvalid);
- if (BodyLineInvalid)
- return false;
-
- // Warn if null statement and body are on the same line.
- if (StmtLine != BodyLine)
- return false;
-
- return true;
-}
-
-void Sema::DiagnoseEmptyStmtBody(SourceLocation StmtLoc,
- const Stmt *Body,
- unsigned DiagID) {
- // Since this is a syntactic check, don't emit diagnostic for template
- // instantiations, this just adds noise.
- if (CurrentInstantiationScope)
- return;
-
- // The body should be a null statement.
- const NullStmt *NBody = dyn_cast<NullStmt>(Body);
- if (!NBody)
- return;
-
- // Do the usual checks.
- if (!ShouldDiagnoseEmptyStmtBody(SourceMgr, StmtLoc, NBody))
- return;
-
- Diag(NBody->getSemiLoc(), DiagID);
- Diag(NBody->getSemiLoc(), diag::note_empty_body_on_separate_line);
-}
-
-void Sema::DiagnoseEmptyLoopBody(const Stmt *S,
- const Stmt *PossibleBody) {
- assert(!CurrentInstantiationScope); // Ensured by caller
-
- SourceLocation StmtLoc;
- const Stmt *Body;
- unsigned DiagID;
- if (const ForStmt *FS = dyn_cast<ForStmt>(S)) {
- StmtLoc = FS->getRParenLoc();
- Body = FS->getBody();
- DiagID = diag::warn_empty_for_body;
- } else if (const WhileStmt *WS = dyn_cast<WhileStmt>(S)) {
- StmtLoc = WS->getRParenLoc();
- Body = WS->getBody();
- DiagID = diag::warn_empty_while_body;
- } else
- return; // Neither `for' nor `while'.
-
- // The body should be a null statement.
- const NullStmt *NBody = dyn_cast<NullStmt>(Body);
- if (!NBody)
- return;
-
- // Skip expensive checks if diagnostic is disabled.
- if (Diags.isIgnored(DiagID, NBody->getSemiLoc()))
- return;
-
- // Do the usual checks.
- if (!ShouldDiagnoseEmptyStmtBody(SourceMgr, StmtLoc, NBody))
- return;
-
- // `for(...);' and `while(...);' are popular idioms, so in order to keep
- // noise level low, emit diagnostics only if for/while is followed by a
- // CompoundStmt, e.g.:
- // for (int i = 0; i < n; i++);
- // {
- // a(i);
- // }
- // or if for/while is followed by a statement with more indentation
- // than for/while itself:
- // for (int i = 0; i < n; i++);
- // a(i);
- bool ProbableTypo = isa<CompoundStmt>(PossibleBody);
- if (!ProbableTypo) {
- bool BodyColInvalid;
- unsigned BodyCol = SourceMgr.getPresumedColumnNumber(
- PossibleBody->getBeginLoc(), &BodyColInvalid);
- if (BodyColInvalid)
- return;
-
- bool StmtColInvalid;
- unsigned StmtCol =
- SourceMgr.getPresumedColumnNumber(S->getBeginLoc(), &StmtColInvalid);
- if (StmtColInvalid)
- return;
-
- if (BodyCol > StmtCol)
- ProbableTypo = true;
- }
-
- if (ProbableTypo) {
- Diag(NBody->getSemiLoc(), DiagID);
- Diag(NBody->getSemiLoc(), diag::note_empty_body_on_separate_line);
- }
-}
-
-//===--- CHECK: Warn on self move with std::move. -------------------------===//
-
-void Sema::DiagnoseSelfMove(const Expr *LHSExpr, const Expr *RHSExpr,
- SourceLocation OpLoc) {
- if (Diags.isIgnored(diag::warn_sizeof_pointer_expr_memaccess, OpLoc))
- return;
-
- if (inTemplateInstantiation())
- return;
-
- // Strip parens and casts away.
- LHSExpr = LHSExpr->IgnoreParenImpCasts();
- RHSExpr = RHSExpr->IgnoreParenImpCasts();
-
- // Check for a call to std::move or for a static_cast<T&&>(..) to an xvalue
- // which we can treat as an inlined std::move
- if (const auto *CE = dyn_cast<CallExpr>(RHSExpr);
- CE && CE->getNumArgs() == 1 && CE->isCallToStdMove())
- RHSExpr = CE->getArg(0);
- else if (const auto *CXXSCE = dyn_cast<CXXStaticCastExpr>(RHSExpr);
- CXXSCE && CXXSCE->isXValue())
- RHSExpr = CXXSCE->getSubExpr();
- else
- return;
-
- const DeclRefExpr *LHSDeclRef = dyn_cast<DeclRefExpr>(LHSExpr);
- const DeclRefExpr *RHSDeclRef = dyn_cast<DeclRefExpr>(RHSExpr);
-
- // Two DeclRefExpr's, check that the decls are the same.
- if (LHSDeclRef && RHSDeclRef) {
- if (!LHSDeclRef->getDecl() || !RHSDeclRef->getDecl())
- return;
- if (LHSDeclRef->getDecl()->getCanonicalDecl() !=
- RHSDeclRef->getDecl()->getCanonicalDecl())
- return;
-
- auto D = Diag(OpLoc, diag::warn_self_move)
- << LHSExpr->getType() << LHSExpr->getSourceRange()
- << RHSExpr->getSourceRange();
- if (const FieldDecl *F =
- getSelfAssignmentClassMemberCandidate(RHSDeclRef->getDecl()))
- D << 1 << F
- << FixItHint::CreateInsertion(LHSDeclRef->getBeginLoc(), "this->");
- else
- D << 0;
- return;
- }
-
- // Member variables require a different approach to check for self moves.
- // MemberExpr's are the same if every nested MemberExpr refers to the same
- // Decl and that the base Expr's are DeclRefExpr's with the same Decl or
- // the base Expr's are CXXThisExpr's.
- const Expr *LHSBase = LHSExpr;
- const Expr *RHSBase = RHSExpr;
- const MemberExpr *LHSME = dyn_cast<MemberExpr>(LHSExpr);
- const MemberExpr *RHSME = dyn_cast<MemberExpr>(RHSExpr);
- if (!LHSME || !RHSME)
- return;
-
- while (LHSME && RHSME) {
- if (LHSME->getMemberDecl()->getCanonicalDecl() !=
- RHSME->getMemberDecl()->getCanonicalDecl())
- return;
-
- LHSBase = LHSME->getBase();
- RHSBase = RHSME->getBase();
- LHSME = dyn_cast<MemberExpr>(LHSBase);
- RHSME = dyn_cast<MemberExpr>(RHSBase);
- }
-
- LHSDeclRef = dyn_cast<DeclRefExpr>(LHSBase);
- RHSDeclRef = dyn_cast<DeclRefExpr>(RHSBase);
- if (LHSDeclRef && RHSDeclRef) {
- if (!LHSDeclRef->getDecl() || !RHSDeclRef->getDecl())
- return;
- if (LHSDeclRef->getDecl()->getCanonicalDecl() !=
- RHSDeclRef->getDecl()->getCanonicalDecl())
- return;
-
- Diag(OpLoc, diag::warn_self_move)
- << LHSExpr->getType() << 0 << LHSExpr->getSourceRange()
- << RHSExpr->getSourceRange();
- return;
- }
-
- if (isa<CXXThisExpr>(LHSBase) && isa<CXXThisExpr>(RHSBase))
- Diag(OpLoc, diag::warn_self_move)
- << LHSExpr->getType() << 0 << LHSExpr->getSourceRange()
- << RHSExpr->getSourceRange();
-}
-
-//===--- Layout compatibility ----------------------------------------------//
-
-static bool isLayoutCompatible(const ASTContext &C, QualType T1, QualType T2);
-
-/// Check if two enumeration types are layout-compatible.
-static bool isLayoutCompatible(const ASTContext &C, const EnumDecl *ED1,
- const EnumDecl *ED2) {
- // C++11 [dcl.enum] p8:
- // Two enumeration types are layout-compatible if they have the same
- // underlying type.
- return ED1->isComplete() && ED2->isComplete() &&
- C.hasSameType(ED1->getIntegerType(), ED2->getIntegerType());
-}
-
-/// Check if two fields are layout-compatible.
-/// Can be used on union members, which are exempt from alignment requirement
-/// of common initial sequence.
-static bool isLayoutCompatible(const ASTContext &C, const FieldDecl *Field1,
- const FieldDecl *Field2,
- bool AreUnionMembers = false) {
-#ifndef NDEBUG
- CanQualType Field1Parent = C.getCanonicalTagType(Field1->getParent());
- CanQualType Field2Parent = C.getCanonicalTagType(Field2->getParent());
- assert(((Field1Parent->isStructureOrClassType() &&
- Field2Parent->isStructureOrClassType()) ||
- (Field1Parent->isUnionType() && Field2Parent->isUnionType())) &&
- "Can't evaluate layout compatibility between a struct field and a "
- "union field.");
- assert(((!AreUnionMembers && Field1Parent->isStructureOrClassType()) ||
- (AreUnionMembers && Field1Parent->isUnionType())) &&
- "AreUnionMembers should be 'true' for union fields (only).");
-#endif
-
- if (!isLayoutCompatible(C, Field1->getType(), Field2->getType()))
- return false;
-
- if (Field1->isBitField() != Field2->isBitField())
- return false;
-
- if (Field1->isBitField()) {
- // Make sure that the bit-fields are the same length.
- unsigned Bits1 = Field1->getBitWidthValue();
- unsigned Bits2 = Field2->getBitWidthValue();
-
- if (Bits1 != Bits2)
- return false;
- }
-
- if (Field1->hasAttr<clang::NoUniqueAddressAttr>() ||
- Field2->hasAttr<clang::NoUniqueAddressAttr>())
- return false;
-
- if (!AreUnionMembers &&
- Field1->getMaxAlignment() != Field2->getMaxAlignment())
- return false;
-
- return true;
-}
-
-/// Check if two standard-layout structs are layout-compatible.
-/// (C++11 [class.mem] p17)
-static bool isLayoutCompatibleStruct(const ASTContext &C, const RecordDecl *RD1,
- const RecordDecl *RD2) {
- // Get to the class where the fields are declared
- if (const CXXRecordDecl *D1CXX = dyn_cast<CXXRecordDecl>(RD1))
- RD1 = D1CXX->getStandardLayoutBaseWithFields();
-
- if (const CXXRecordDecl *D2CXX = dyn_cast<CXXRecordDecl>(RD2))
- RD2 = D2CXX->getStandardLayoutBaseWithFields();
-
- // Check the fields.
- return llvm::equal(RD1->fields(), RD2->fields(),
- [&C](const FieldDecl *F1, const FieldDecl *F2) -> bool {
- return isLayoutCompatible(C, F1, F2);
- });
-}
-
-/// Check if two standard-layout unions are layout-compatible.
-/// (C++11 [class.mem] p18)
-static bool isLayoutCompatibleUnion(const ASTContext &C, const RecordDecl *RD1,
- const RecordDecl *RD2) {
- llvm::SmallPtrSet<const FieldDecl *, 8> UnmatchedFields(llvm::from_range,
- RD2->fields());
-
- for (auto *Field1 : RD1->fields()) {
- auto I = UnmatchedFields.begin();
- auto E = UnmatchedFields.end();
-
- for ( ; I != E; ++I) {
- if (isLayoutCompatible(C, Field1, *I, /*IsUnionMember=*/true)) {
- bool Result = UnmatchedFields.erase(*I);
- (void) Result;
- assert(Result);
- break;
- }
- }
- if (I == E)
- return false;
- }
-
- return UnmatchedFields.empty();
-}
-
-static bool isLayoutCompatible(const ASTContext &C, const RecordDecl *RD1,
- const RecordDecl *RD2) {
- if (RD1->isUnion() != RD2->isUnion())
- return false;
-
- if (RD1->isUnion())
- return isLayoutCompatibleUnion(C, RD1, RD2);
- else
- return isLayoutCompatibleStruct(C, RD1, RD2);
-}
-
-/// Check if two types are layout-compatible in C++11 sense.
-static bool isLayoutCompatible(const ASTContext &C, QualType T1, QualType T2) {
- if (T1.isNull() || T2.isNull())
- return false;
-
- // C++20 [basic.types] p11:
- // Two types cv1 T1 and cv2 T2 are layout-compatible types
- // if T1 and T2 are the same type, layout-compatible enumerations (9.7.1),
- // or layout-compatible standard-layout class types (11.4).
- T1 = T1.getCanonicalType().getUnqualifiedType();
- T2 = T2.getCanonicalType().getUnqualifiedType();
-
- if (C.hasSameType(T1, T2))
- return true;
-
- const Type::TypeClass TC1 = T1->getTypeClass();
- const Type::TypeClass TC2 = T2->getTypeClass();
-
- if (TC1 != TC2)
- return false;
-
- if (TC1 == Type::Enum)
- return isLayoutCompatible(C, T1->castAsEnumDecl(), T2->castAsEnumDecl());
- if (TC1 == Type::Record) {
- if (!T1->isStandardLayoutType() || !T2->isStandardLayoutType())
- return false;
-
- return isLayoutCompatible(C, T1->castAsRecordDecl(),
- T2->castAsRecordDecl());
- }
-
- return false;
-}
-
-bool Sema::IsLayoutCompatible(QualType T1, QualType T2) const {
- return isLayoutCompatible(getASTContext(), T1, T2);
-}
-
-//===-------------- Pointer interconvertibility ----------------------------//
-
-bool Sema::IsPointerInterconvertibleBaseOf(const TypeSourceInfo *Base,
- const TypeSourceInfo *Derived) {
- QualType BaseT = Base->getType()->getCanonicalTypeUnqualified();
- QualType DerivedT = Derived->getType()->getCanonicalTypeUnqualified();
-
- if (BaseT->isStructureOrClassType() && DerivedT->isStructureOrClassType() &&
- getASTContext().hasSameType(BaseT, DerivedT))
- return true;
-
- if (!IsDerivedFrom(Derived->getTypeLoc().getBeginLoc(), DerivedT, BaseT))
- return false;
-
- // Per [basic.compound]/4.3, containing object has to be standard-layout.
- if (DerivedT->getAsCXXRecordDecl()->isStandardLayout())
- return true;
-
- return false;
-}
-
-//===--- CHECK: pointer_with_type_tag attribute: datatypes should match ----//
-
-/// Given a type tag expression find the type tag itself.
-///
-/// \param TypeExpr Type tag expression, as it appears in user's code.
-///
-/// \param VD Declaration of an identifier that appears in a type tag.
-///
-/// \param MagicValue Type tag magic value.
-///
-/// \param isConstantEvaluated whether the evalaution should be performed in
-
-/// constant context.
-static bool FindTypeTagExpr(const Expr *TypeExpr, const ASTContext &Ctx,
- const ValueDecl **VD, uint64_t *MagicValue,
- bool isConstantEvaluated) {
- while(true) {
- if (!TypeExpr)
- return false;
-
- TypeExpr = TypeExpr->IgnoreParenImpCasts()->IgnoreParenCasts();
-
- switch (TypeExpr->getStmtClass()) {
- case Stmt::UnaryOperatorClass: {
- const UnaryOperator *UO = cast<UnaryOperator>(TypeExpr);
- if (UO->getOpcode() == UO_AddrOf || UO->getOpcode() == UO_Deref) {
- TypeExpr = UO->getSubExpr();
- continue;
- }
- return false;
- }
-
- case Stmt::DeclRefExprClass: {
- const DeclRefExpr *DRE = cast<DeclRefExpr>(TypeExpr);
- *VD = DRE->getDecl();
- return true;
- }
-
- case Stmt::IntegerLiteralClass: {
- const IntegerLiteral *IL = cast<IntegerLiteral>(TypeExpr);
- llvm::APInt MagicValueAPInt = IL->getValue();
- if (MagicValueAPInt.getActiveBits() <= 64) {
- *MagicValue = MagicValueAPInt.getZExtValue();
- return true;
- } else
- return false;
- }
-
- case Stmt::BinaryConditionalOperatorClass:
- case Stmt::ConditionalOperatorClass: {
- const AbstractConditionalOperator *ACO =
- cast<AbstractConditionalOperator>(TypeExpr);
- bool Result;
- if (ACO->getCond()->EvaluateAsBooleanCondition(Result, Ctx,
- isConstantEvaluated)) {
- if (Result)
- TypeExpr = ACO->getTrueExpr();
- else
- TypeExpr = ACO->getFalseExpr();
- continue;
- }
- return false;
- }
-
- case Stmt::BinaryOperatorClass: {
- const BinaryOperator *BO = cast<BinaryOperator>(TypeExpr);
- if (BO->getOpcode() == BO_Comma) {
- TypeExpr = BO->getRHS();
- continue;
- }
- return false;
- }
-
- default:
- return false;
- }
- }
-}
-
-/// Retrieve the C type corresponding to type tag TypeExpr.
-///
-/// \param TypeExpr Expression that specifies a type tag.
-///
-/// \param MagicValues Registered magic values.
-///
-/// \param FoundWrongKind Set to true if a type tag was found, but of a wrong
-/// kind.
-///
-/// \param TypeInfo Information about the corresponding C type.
-///
-/// \param isConstantEvaluated whether the evalaution should be performed in
-/// constant context.
-///
-/// \returns true if the corresponding C type was found.
-static bool GetMatchingCType(
- const IdentifierInfo *ArgumentKind, const Expr *TypeExpr,
- const ASTContext &Ctx,
- const llvm::DenseMap<Sema::TypeTagMagicValue, Sema::TypeTagData>
- *MagicValues,
- bool &FoundWrongKind, Sema::TypeTagData &TypeInfo,
- bool isConstantEvaluated) {
- FoundWrongKind = false;
-
- // Variable declaration that has type_tag_for_datatype attribute.
- const ValueDecl *VD = nullptr;
-
- uint64_t MagicValue;
-
- if (!FindTypeTagExpr(TypeExpr, Ctx, &VD, &MagicValue, isConstantEvaluated))
- return false;
-
- if (VD) {
- if (TypeTagForDatatypeAttr *I = VD->getAttr<TypeTagForDatatypeAttr>()) {
- if (I->getArgumentKind() != ArgumentKind) {
- FoundWrongKind = true;
- return false;
- }
- TypeInfo.Type = I->getMatchingCType();
- TypeInfo.LayoutCompatible = I->getLayoutCompatible();
- TypeInfo.MustBeNull = I->getMustBeNull();
- return true;
- }
- return false;
- }
-
- if (!MagicValues)
- return false;
-
- llvm::DenseMap<Sema::TypeTagMagicValue,
- Sema::TypeTagData>::const_iterator I =
- MagicValues->find(std::make_pair(ArgumentKind, MagicValue));
- if (I == MagicValues->end())
- return false;
-
- TypeInfo = I->second;
- return true;
-}
-
-void Sema::RegisterTypeTagForDatatype(const IdentifierInfo *ArgumentKind,
- uint64_t MagicValue, QualType Type,
- bool LayoutCompatible,
- bool MustBeNull) {
- if (!TypeTagForDatatypeMagicValues)
- TypeTagForDatatypeMagicValues.reset(
- new llvm::DenseMap<TypeTagMagicValue, TypeTagData>);
-
- TypeTagMagicValue Magic(ArgumentKind, MagicValue);
- (*TypeTagForDatatypeMagicValues)[Magic] =
- TypeTagData(Type, LayoutCompatible, MustBeNull);
-}
-
-static bool IsSameCharType(QualType T1, QualType T2) {
- const BuiltinType *BT1 = T1->getAs<BuiltinType>();
- if (!BT1)
- return false;
-
- const BuiltinType *BT2 = T2->getAs<BuiltinType>();
- if (!BT2)
- return false;
-
- BuiltinType::Kind T1Kind = BT1->getKind();
- BuiltinType::Kind T2Kind = BT2->getKind();
-
- return (T1Kind == BuiltinType::SChar && T2Kind == BuiltinType::Char_S) ||
- (T1Kind == BuiltinType::UChar && T2Kind == BuiltinType::Char_U) ||
- (T1Kind == BuiltinType::Char_U && T2Kind == BuiltinType::UChar) ||
- (T1Kind == BuiltinType::Char_S && T2Kind == BuiltinType::SChar);
-}
-
-void Sema::CheckArgumentWithTypeTag(const ArgumentWithTypeTagAttr *Attr,
- const ArrayRef<const Expr *> ExprArgs,
- SourceLocation CallSiteLoc) {
- const IdentifierInfo *ArgumentKind = Attr->getArgumentKind();
- bool IsPointerAttr = Attr->getIsPointer();
-
- // Retrieve the argument representing the 'type_tag'.
- unsigned TypeTagIdxAST = Attr->getTypeTagIdx().getASTIndex();
- if (TypeTagIdxAST >= ExprArgs.size()) {
- Diag(CallSiteLoc, diag::err_tag_index_out_of_range)
- << 0 << Attr->getTypeTagIdx().getSourceIndex();
- return;
- }
- const Expr *TypeTagExpr = ExprArgs[TypeTagIdxAST];
- bool FoundWrongKind;
- TypeTagData TypeInfo;
- if (!GetMatchingCType(ArgumentKind, TypeTagExpr, Context,
- TypeTagForDatatypeMagicValues.get(), FoundWrongKind,
- TypeInfo, isConstantEvaluatedContext())) {
- if (FoundWrongKind)
- Diag(TypeTagExpr->getExprLoc(),
- diag::warn_type_tag_for_datatype_wrong_kind)
- << TypeTagExpr->getSourceRange();
- return;
- }
-
- // Retrieve the argument representing the 'arg_idx'.
- unsigned ArgumentIdxAST = Attr->getArgumentIdx().getASTIndex();
- if (ArgumentIdxAST >= ExprArgs.size()) {
- Diag(CallSiteLoc, diag::err_tag_index_out_of_range)
- << 1 << Attr->getArgumentIdx().getSourceIndex();
- return;
- }
- const Expr *ArgumentExpr = ExprArgs[ArgumentIdxAST];
- if (IsPointerAttr) {
- // Skip implicit cast of pointer to `void *' (as a function argument).
- if (const ImplicitCastExpr *ICE = dyn_cast<ImplicitCastExpr>(ArgumentExpr))
- if (ICE->getType()->isVoidPointerType() &&
- ICE->getCastKind() == CK_BitCast)
- ArgumentExpr = ICE->getSubExpr();
- }
- QualType ArgumentType = ArgumentExpr->getType();
-
- // Passing a `void*' pointer shouldn't trigger a warning.
- if (IsPointerAttr && ArgumentType->isVoidPointerType())
- return;
-
- if (TypeInfo.MustBeNull) {
- // Type tag with matching void type requires a null pointer.
- if (!ArgumentExpr->isNullPointerConstant(Context,
- Expr::NPC_ValueDependentIsNotNull)) {
- Diag(ArgumentExpr->getExprLoc(),
- diag::warn_type_safety_null_pointer_required)
- << ArgumentKind->getName()
- << ArgumentExpr->getSourceRange()
- << TypeTagExpr->getSourceRange();
- }
- return;
- }
-
- QualType RequiredType = TypeInfo.Type;
- if (IsPointerAttr)
- RequiredType = Context.getPointerType(RequiredType);
-
- bool mismatch = false;
- if (!TypeInfo.LayoutCompatible) {
- mismatch = !Context.hasSameType(ArgumentType, RequiredType);
-
- // C++11 [basic.fundamental] p1:
- // Plain char, signed char, and unsigned char are three distinct types.
- //
- // But we treat plain `char' as equivalent to `signed char' or `unsigned
- // char' depending on the current char signedness mode.
- if (mismatch)
- if ((IsPointerAttr && IsSameCharType(ArgumentType->getPointeeType(),
- RequiredType->getPointeeType())) ||
- (!IsPointerAttr && IsSameCharType(ArgumentType, RequiredType)))
- mismatch = false;
- } else
- if (IsPointerAttr)
- mismatch = !isLayoutCompatible(Context,
- ArgumentType->getPointeeType(),
- RequiredType->getPointeeType());
- else
- mismatch = !isLayoutCompatible(Context, ArgumentType, RequiredType);
-
- if (mismatch)
- Diag(ArgumentExpr->getExprLoc(), diag::warn_type_safety_type_mismatch)
- << ArgumentType << ArgumentKind
- << TypeInfo.LayoutCompatible << RequiredType
- << ArgumentExpr->getSourceRange()
- << TypeTagExpr->getSourceRange();
-}
-
-void Sema::AddPotentialMisalignedMembers(Expr *E, RecordDecl *RD, ValueDecl *MD,
- CharUnits Alignment) {
- currentEvaluationContext().MisalignedMembers.emplace_back(E, RD, MD,
- Alignment);
-}
-
-void Sema::DiagnoseMisalignedMembers() {
- for (MisalignedMember &m : currentEvaluationContext().MisalignedMembers) {
- const NamedDecl *ND = m.RD;
- if (ND->getName().empty()) {
- if (const TypedefNameDecl *TD = m.RD->getTypedefNameForAnonDecl())
- ND = TD;
- }
- Diag(m.E->getBeginLoc(), diag::warn_taking_address_of_packed_member)
- << m.MD << ND << m.E->getSourceRange();
- }
- currentEvaluationContext().MisalignedMembers.clear();
-}
-
-void Sema::DiscardMisalignedMemberAddress(const Type *T, Expr *E) {
- E = E->IgnoreParens();
- if (!T->isPointerType() && !T->isIntegerType() && !T->isDependentType())
- return;
- if (isa<UnaryOperator>(E) &&
- cast<UnaryOperator>(E)->getOpcode() == UO_AddrOf) {
- auto *Op = cast<UnaryOperator>(E)->getSubExpr()->IgnoreParens();
- if (isa<MemberExpr>(Op)) {
- auto &MisalignedMembersForExpr =
- currentEvaluationContext().MisalignedMembers;
- auto *MA = llvm::find(MisalignedMembersForExpr, MisalignedMember(Op));
- if (MA != MisalignedMembersForExpr.end() &&
- (T->isDependentType() || T->isIntegerType() ||
- (T->isPointerType() && (T->getPointeeType()->isIncompleteType() ||
- Context.getTypeAlignInChars(
- T->getPointeeType()) <= MA->Alignment))))
- MisalignedMembersForExpr.erase(MA);
- }
- }
-}
-
-void Sema::RefersToMemberWithReducedAlignment(
- Expr *E,
- llvm::function_ref<void(Expr *, RecordDecl *, FieldDecl *, CharUnits)>
- Action) {
- const auto *ME = dyn_cast<MemberExpr>(E);
- if (!ME)
- return;
-
- // No need to check expressions with an __unaligned-qualified type.
- if (E->getType().getQualifiers().hasUnaligned())
- return;
-
- // For a chain of MemberExpr like "a.b.c.d" this list
- // will keep FieldDecl's like [d, c, b].
- SmallVector<FieldDecl *, 4> ReverseMemberChain;
- const MemberExpr *TopME = nullptr;
- bool AnyIsPacked = false;
- do {
- QualType BaseType = ME->getBase()->getType();
- if (BaseType->isDependentType())
- return;
- if (ME->isArrow())
- BaseType = BaseType->getPointeeType();
- auto *RD = BaseType->castAsRecordDecl();
- if (RD->isInvalidDecl())
- return;
-
- ValueDecl *MD = ME->getMemberDecl();
- auto *FD = dyn_cast<FieldDecl>(MD);
- // We do not care about non-data members.
- if (!FD || FD->isInvalidDecl())
- return;
-
- AnyIsPacked =
- AnyIsPacked || (RD->hasAttr<PackedAttr>() || MD->hasAttr<PackedAttr>());
- ReverseMemberChain.push_back(FD);
-
- TopME = ME;
- ME = dyn_cast<MemberExpr>(ME->getBase()->IgnoreParens());
- } while (ME);
- assert(TopME && "We did not compute a topmost MemberExpr!");
-
- // Not the scope of this diagnostic.
- if (!AnyIsPacked)
- return;
-
- const Expr *TopBase = TopME->getBase()->IgnoreParenImpCasts();
- const auto *DRE = dyn_cast<DeclRefExpr>(TopBase);
- // TODO: The innermost base of the member expression may be too complicated.
- // For now, just disregard these cases. This is left for future
- // improvement.
- if (!DRE && !isa<CXXThisExpr>(TopBase))
- return;
-
- // Alignment expected by the whole expression.
- CharUnits ExpectedAlignment = Context.getTypeAlignInChars(E->getType());
-
- // No need to do anything else with this case.
- if (ExpectedAlignment.isOne())
- return;
-
- // Synthesize offset of the whole access.
- CharUnits Offset;
- for (const FieldDecl *FD : llvm::reverse(ReverseMemberChain))
- Offset += Context.toCharUnitsFromBits(Context.getFieldOffset(FD));
-
- // Compute the CompleteObjectAlignment as the alignment of the whole chain.
- CharUnits CompleteObjectAlignment = Context.getTypeAlignInChars(
- Context.getCanonicalTagType(ReverseMemberChain.back()->getParent()));
-
- // The base expression of the innermost MemberExpr may give
- // stronger guarantees than the class containing the member.
- if (DRE && !TopME->isArrow()) {
- const ValueDecl *VD = DRE->getDecl();
- if (!VD->getType()->isReferenceType())
- CompleteObjectAlignment =
- std::max(CompleteObjectAlignment, Context.getDeclAlign(VD));
- }
-
- // Check if the synthesized offset fulfills the alignment.
- if (!Offset.isMultipleOf(ExpectedAlignment) ||
- // It may fulfill the offset it but the effective alignment may still be
- // lower than the expected expression alignment.
- CompleteObjectAlignment < ExpectedAlignment) {
- // If this happens, we want to determine a sensible culprit of this.
- // Intuitively, watching the chain of member expressions from right to
- // left, we start with the required alignment (as required by the field
- // type) but some packed attribute in that chain has reduced the alignment.
- // It may happen that another packed structure increases it again. But if
- // we are here such increase has not been enough. So pointing the first
- // FieldDecl that either is packed or else its RecordDecl is,
- // seems reasonable.
- FieldDecl *FD = nullptr;
- CharUnits Alignment;
- for (FieldDecl *FDI : ReverseMemberChain) {
- if (FDI->hasAttr<PackedAttr>() ||
- FDI->getParent()->hasAttr<PackedAttr>()) {
- FD = FDI;
- Alignment = std::min(Context.getTypeAlignInChars(FD->getType()),
- Context.getTypeAlignInChars(
- Context.getCanonicalTagType(FD->getParent())));
- break;
- }
- }
- assert(FD && "We did not find a packed FieldDecl!");
- Action(E, FD->getParent(), FD, Alignment);
- }
-}
-
-void Sema::CheckAddressOfPackedMember(Expr *rhs) {
- using namespace std::placeholders;
-
- RefersToMemberWithReducedAlignment(
- rhs, std::bind(&Sema::AddPotentialMisalignedMembers, std::ref(*this), _1,
- _2, _3, _4));
-}
-
-bool Sema::PrepareBuiltinElementwiseMathOneArgCall(
- CallExpr *TheCall, EltwiseBuiltinArgTyRestriction ArgTyRestr) {
- if (checkArgCount(TheCall, 1))
- return true;
-
- ExprResult A = BuiltinVectorMathConversions(*this, TheCall->getArg(0));
- if (A.isInvalid())
- return true;
-
- TheCall->setArg(0, A.get());
- QualType TyA = A.get()->getType();
-
- if (checkMathBuiltinElementType(*this, A.get()->getBeginLoc(), TyA,
- ArgTyRestr, 1))
- return true;
-
- TheCall->setType(TyA);
- return false;
-}
-
-bool Sema::BuiltinElementwiseMath(CallExpr *TheCall,
- EltwiseBuiltinArgTyRestriction ArgTyRestr) {
- if (auto Res = BuiltinVectorMath(TheCall, ArgTyRestr); Res.has_value()) {
- TheCall->setType(*Res);
- return false;
- }
- return true;
-}
-
-bool Sema::BuiltinVectorToScalarMath(CallExpr *TheCall) {
- std::optional<QualType> Res = BuiltinVectorMath(TheCall);
- if (!Res)
- return true;
-
- if (auto *VecTy0 = (*Res)->getAs<VectorType>())
- TheCall->setType(VecTy0->getElementType());
- else
- TheCall->setType(*Res);
-
- return false;
-}
-
-static bool checkBuiltinVectorMathMixedEnums(Sema &S, Expr *LHS, Expr *RHS,
- SourceLocation Loc) {
- QualType L = LHS->getEnumCoercedType(S.Context),
- R = RHS->getEnumCoercedType(S.Context);
- if (L->isUnscopedEnumerationType() && R->isUnscopedEnumerationType() &&
- !S.Context.hasSameUnqualifiedType(L, R)) {
- return S.Diag(Loc, diag::err_conv_mixed_enum_types)
- << LHS->getSourceRange() << RHS->getSourceRange()
- << /*Arithmetic Between*/ 0 << L << R;
- }
- return false;
-}
-
-/// Check if all arguments have the same type. If the types don't match, emit an
-/// error message and return true. Otherwise return false.
-///
-/// For scalars we directly compare their unqualified types. But even if we
-/// compare unqualified vector types, a difference in qualifiers in the element
-/// types can make the vector types be considered not equal. For example,
-/// vector of 4 'const float' values vs vector of 4 'float' values.
-/// So we compare unqualified types of their elements and number of elements.
-static bool checkBuiltinVectorMathArgTypes(Sema &SemaRef,
- ArrayRef<Expr *> Args) {
- assert(!Args.empty() && "Should have at least one argument.");
-
- Expr *Arg0 = Args.front();
- QualType Ty0 = Arg0->getType();
-
- auto EmitError = [&](Expr *ArgI) {
- SemaRef.Diag(Arg0->getBeginLoc(),
- diag::err_typecheck_call_different_arg_types)
- << Arg0->getType() << ArgI->getType();
- };
-
- // Compare scalar types.
- if (!Ty0->isVectorType()) {
- for (Expr *ArgI : Args.drop_front())
- if (!SemaRef.Context.hasSameUnqualifiedType(Ty0, ArgI->getType())) {
- EmitError(ArgI);
- return true;
- }
-
- return false;
- }
-
- // Compare vector types.
- const auto *Vec0 = Ty0->castAs<VectorType>();
- for (Expr *ArgI : Args.drop_front()) {
- const auto *VecI = ArgI->getType()->getAs<VectorType>();
- if (!VecI ||
- !SemaRef.Context.hasSameUnqualifiedType(Vec0->getElementType(),
- VecI->getElementType()) ||
- Vec0->getNumElements() != VecI->getNumElements()) {
- EmitError(ArgI);
- return true;
- }
- }
-
- return false;
-}
-
-std::optional<QualType>
-Sema::BuiltinVectorMath(CallExpr *TheCall,
- EltwiseBuiltinArgTyRestriction ArgTyRestr) {
- if (checkArgCount(TheCall, 2))
- return std::nullopt;
-
- if (checkBuiltinVectorMathMixedEnums(
- *this, TheCall->getArg(0), TheCall->getArg(1), TheCall->getExprLoc()))
- return std::nullopt;
-
- Expr *Args[2];
- for (int I = 0; I < 2; ++I) {
- ExprResult Converted =
- BuiltinVectorMathConversions(*this, TheCall->getArg(I));
- if (Converted.isInvalid())
- return std::nullopt;
- Args[I] = Converted.get();
- }
-
- SourceLocation LocA = Args[0]->getBeginLoc();
- QualType TyA = Args[0]->getType();
-
- if (checkMathBuiltinElementType(*this, LocA, TyA, ArgTyRestr, 1))
- return std::nullopt;
-
- if (checkBuiltinVectorMathArgTypes(*this, Args))
- return std::nullopt;
-
- TheCall->setArg(0, Args[0]);
- TheCall->setArg(1, Args[1]);
- return TyA;
-}
-
-bool Sema::BuiltinElementwiseTernaryMath(
- CallExpr *TheCall, EltwiseBuiltinArgTyRestriction ArgTyRestr) {
- if (checkArgCount(TheCall, 3))
- return true;
-
- SourceLocation Loc = TheCall->getExprLoc();
- if (checkBuiltinVectorMathMixedEnums(*this, TheCall->getArg(0),
- TheCall->getArg(1), Loc) ||
- checkBuiltinVectorMathMixedEnums(*this, TheCall->getArg(1),
- TheCall->getArg(2), Loc))
- return true;
-
- Expr *Args[3];
- for (int I = 0; I < 3; ++I) {
- ExprResult Converted =
- BuiltinVectorMathConversions(*this, TheCall->getArg(I));
- if (Converted.isInvalid())
- return true;
- Args[I] = Converted.get();
- }
-
- int ArgOrdinal = 1;
- for (Expr *Arg : Args) {
- if (checkMathBuiltinElementType(*this, Arg->getBeginLoc(), Arg->getType(),
- ArgTyRestr, ArgOrdinal++))
- return true;
- }
-
- if (checkBuiltinVectorMathArgTypes(*this, Args))
- return true;
-
- for (int I = 0; I < 3; ++I)
- TheCall->setArg(I, Args[I]);
-
- TheCall->setType(Args[0]->getType());
- return false;
-}
-
-bool Sema::PrepareBuiltinReduceMathOneArgCall(CallExpr *TheCall) {
- if (checkArgCount(TheCall, 1))
- return true;
-
- ExprResult A = UsualUnaryConversions(TheCall->getArg(0));
- if (A.isInvalid())
- return true;
-
- TheCall->setArg(0, A.get());
- return false;
-}
-
-bool Sema::BuiltinNonDeterministicValue(CallExpr *TheCall) {
- if (checkArgCount(TheCall, 1))
- return true;
-
- ExprResult Arg = TheCall->getArg(0);
- QualType TyArg = Arg.get()->getType();
-
- if (!TyArg->isBuiltinType() && !TyArg->isVectorType())
- return Diag(TheCall->getArg(0)->getBeginLoc(),
- diag::err_builtin_invalid_arg_type)
- << 1 << /* vector */ 2 << /* integer */ 1 << /* fp */ 1 << TyArg;
-
- TheCall->setType(TyArg);
- return false;
-}
-
-ExprResult Sema::BuiltinMatrixTranspose(CallExpr *TheCall,
- ExprResult CallResult) {
- if (checkArgCount(TheCall, 1))
- return ExprError();
-
- ExprResult MatrixArg = DefaultLvalueConversion(TheCall->getArg(0));
- if (MatrixArg.isInvalid())
- return MatrixArg;
- Expr *Matrix = MatrixArg.get();
-
- auto *MType = Matrix->getType()->getAs<ConstantMatrixType>();
- if (!MType) {
- Diag(Matrix->getBeginLoc(), diag::err_builtin_invalid_arg_type)
- << 1 << /* matrix */ 3 << /* no int */ 0 << /* no fp */ 0
- << Matrix->getType();
- return ExprError();
- }
-
- // Create returned matrix type by swapping rows and columns of the argument
- // matrix type.
- QualType ResultType = Context.getConstantMatrixType(
- MType->getElementType(), MType->getNumColumns(), MType->getNumRows());
-
- // Change the return type to the type of the returned matrix.
- TheCall->setType(ResultType);
-
- // Update call argument to use the possibly converted matrix argument.
- TheCall->setArg(0, Matrix);
- return CallResult;
-}
-
-// Get and verify the matrix dimensions.
-static std::optional<unsigned>
-getAndVerifyMatrixDimension(Expr *Expr, StringRef Name, Sema &S) {
- std::optional<llvm::APSInt> Value = Expr->getIntegerConstantExpr(S.Context);
- if (!Value) {
- S.Diag(Expr->getBeginLoc(), diag::err_builtin_matrix_scalar_unsigned_arg)
- << Name;
- return {};
- }
- uint64_t Dim = Value->getZExtValue();
- if (Dim == 0 || Dim > S.Context.getLangOpts().MaxMatrixDimension) {
- S.Diag(Expr->getBeginLoc(), diag::err_builtin_matrix_invalid_dimension)
- << Name << S.Context.getLangOpts().MaxMatrixDimension;
- return {};
- }
- return Dim;
-}
-
-ExprResult Sema::BuiltinMatrixColumnMajorLoad(CallExpr *TheCall,
- ExprResult CallResult) {
- if (!getLangOpts().MatrixTypes) {
- Diag(TheCall->getBeginLoc(), diag::err_builtin_matrix_disabled);
- return ExprError();
- }
-
- if (getLangOpts().getDefaultMatrixMemoryLayout() !=
- LangOptions::MatrixMemoryLayout::MatrixColMajor) {
- Diag(TheCall->getBeginLoc(), diag::err_builtin_matrix_major_order_disabled)
- << /*column*/ 1 << /*load*/ 0;
- return ExprError();
- }
-
- if (checkArgCount(TheCall, 4))
- return ExprError();
-
- unsigned PtrArgIdx = 0;
- Expr *PtrExpr = TheCall->getArg(PtrArgIdx);
- Expr *RowsExpr = TheCall->getArg(1);
- Expr *ColumnsExpr = TheCall->getArg(2);
- Expr *StrideExpr = TheCall->getArg(3);
-
- bool ArgError = false;
-
- // Check pointer argument.
- {
- ExprResult PtrConv = DefaultFunctionArrayLvalueConversion(PtrExpr);
- if (PtrConv.isInvalid())
- return PtrConv;
- PtrExpr = PtrConv.get();
- TheCall->setArg(0, PtrExpr);
- if (PtrExpr->isTypeDependent()) {
- TheCall->setType(Context.DependentTy);
- return TheCall;
- }
- }
-
- auto *PtrTy = PtrExpr->getType()->getAs<PointerType>();
- QualType ElementTy;
- if (!PtrTy) {
- Diag(PtrExpr->getBeginLoc(), diag::err_builtin_invalid_arg_type)
- << PtrArgIdx + 1 << 0 << /* pointer to element ty */ 5 << /* no fp */ 0
- << PtrExpr->getType();
- ArgError = true;
- } else {
- ElementTy = PtrTy->getPointeeType().getUnqualifiedType();
-
- if (!ConstantMatrixType::isValidElementType(ElementTy, getLangOpts())) {
- Diag(PtrExpr->getBeginLoc(), diag::err_builtin_invalid_arg_type)
- << PtrArgIdx + 1 << 0 << /* pointer to element ty */ 5
- << /* no fp */ 0 << PtrExpr->getType();
- ArgError = true;
- }
- }
-
- // Apply default Lvalue conversions and convert the expression to size_t.
- auto ApplyArgumentConversions = [this](Expr *E) {
- ExprResult Conv = DefaultLvalueConversion(E);
- if (Conv.isInvalid())
- return Conv;
-
- return tryConvertExprToType(Conv.get(), Context.getSizeType());
- };
-
- // Apply conversion to row and column expressions.
- ExprResult RowsConv = ApplyArgumentConversions(RowsExpr);
- if (!RowsConv.isInvalid()) {
- RowsExpr = RowsConv.get();
- TheCall->setArg(1, RowsExpr);
- } else
- RowsExpr = nullptr;
-
- ExprResult ColumnsConv = ApplyArgumentConversions(ColumnsExpr);
- if (!ColumnsConv.isInvalid()) {
- ColumnsExpr = ColumnsConv.get();
- TheCall->setArg(2, ColumnsExpr);
- } else
- ColumnsExpr = nullptr;
-
- // If any part of the result matrix type is still pending, just use
- // Context.DependentTy, until all parts are resolved.
- if ((RowsExpr && RowsExpr->isTypeDependent()) ||
- (ColumnsExpr && ColumnsExpr->isTypeDependent())) {
- TheCall->setType(Context.DependentTy);
- return CallResult;
- }
-
- // Check row and column dimensions.
- std::optional<unsigned> MaybeRows;
- if (RowsExpr)
- MaybeRows = getAndVerifyMatrixDimension(RowsExpr, "row", *this);
-
- std::optional<unsigned> MaybeColumns;
- if (ColumnsExpr)
- MaybeColumns = getAndVerifyMatrixDimension(ColumnsExpr, "column", *this);
-
- // Check stride argument.
- ExprResult StrideConv = ApplyArgumentConversions(StrideExpr);
- if (StrideConv.isInvalid())
- return ExprError();
- StrideExpr = StrideConv.get();
- TheCall->setArg(3, StrideExpr);
-
- if (MaybeRows) {
- if (std::optional<llvm::APSInt> Value =
- StrideExpr->getIntegerConstantExpr(Context)) {
- uint64_t Stride = Value->getZExtValue();
- if (Stride < *MaybeRows) {
- Diag(StrideExpr->getBeginLoc(),
- diag::err_builtin_matrix_stride_too_small);
- ArgError = true;
- }
- }
- }
-
- if (ArgError || !MaybeRows || !MaybeColumns)
- return ExprError();
-
- TheCall->setType(
- Context.getConstantMatrixType(ElementTy, *MaybeRows, *MaybeColumns));
- return CallResult;
-}
-
-ExprResult Sema::BuiltinMatrixColumnMajorStore(CallExpr *TheCall,
- ExprResult CallResult) {
- if (!getLangOpts().MatrixTypes) {
- Diag(TheCall->getBeginLoc(), diag::err_builtin_matrix_disabled);
- return ExprError();
- }
-
- if (getLangOpts().getDefaultMatrixMemoryLayout() !=
- LangOptions::MatrixMemoryLayout::MatrixColMajor) {
- Diag(TheCall->getBeginLoc(), diag::err_builtin_matrix_major_order_disabled)
- << /*column*/ 1 << /*store*/ 1;
- return ExprError();
- }
-
- if (checkArgCount(TheCall, 3))
- return ExprError();
-
- unsigned PtrArgIdx = 1;
- Expr *MatrixExpr = TheCall->getArg(0);
- Expr *PtrExpr = TheCall->getArg(PtrArgIdx);
- Expr *StrideExpr = TheCall->getArg(2);
-
- bool ArgError = false;
-
- {
- ExprResult MatrixConv = DefaultLvalueConversion(MatrixExpr);
- if (MatrixConv.isInvalid())
- return MatrixConv;
- MatrixExpr = MatrixConv.get();
- TheCall->setArg(0, MatrixExpr);
- }
- if (MatrixExpr->isTypeDependent()) {
- TheCall->setType(Context.DependentTy);
- return TheCall;
- }
-
- auto *MatrixTy = MatrixExpr->getType()->getAs<ConstantMatrixType>();
- if (!MatrixTy) {
- Diag(MatrixExpr->getBeginLoc(), diag::err_builtin_invalid_arg_type)
- << 1 << /* matrix ty */ 3 << 0 << 0 << MatrixExpr->getType();
- ArgError = true;
- }
-
- {
- ExprResult PtrConv = DefaultFunctionArrayLvalueConversion(PtrExpr);
- if (PtrConv.isInvalid())
- return PtrConv;
- PtrExpr = PtrConv.get();
- TheCall->setArg(1, PtrExpr);
- if (PtrExpr->isTypeDependent()) {
- TheCall->setType(Context.DependentTy);
- return TheCall;
- }
- }
-
- // Check pointer argument.
- auto *PtrTy = PtrExpr->getType()->getAs<PointerType>();
- if (!PtrTy) {
- Diag(PtrExpr->getBeginLoc(), diag::err_builtin_invalid_arg_type)
- << PtrArgIdx + 1 << 0 << /* pointer to element ty */ 5 << 0
- << PtrExpr->getType();
- ArgError = true;
- } else {
- QualType ElementTy = PtrTy->getPointeeType();
- if (ElementTy.isConstQualified()) {
- Diag(PtrExpr->getBeginLoc(), diag::err_builtin_matrix_store_to_const);
- ArgError = true;
- }
- ElementTy = ElementTy.getUnqualifiedType().getCanonicalType();
- if (MatrixTy &&
- !Context.hasSameType(ElementTy, MatrixTy->getElementType())) {
- Diag(PtrExpr->getBeginLoc(),
- diag::err_builtin_matrix_pointer_arg_mismatch)
- << ElementTy << MatrixTy->getElementType();
- ArgError = true;
- }
- }
-
- // Apply default Lvalue conversions and convert the stride expression to
- // size_t.
- {
- ExprResult StrideConv = DefaultLvalueConversion(StrideExpr);
- if (StrideConv.isInvalid())
- return StrideConv;
-
- StrideConv = tryConvertExprToType(StrideConv.get(), Context.getSizeType());
- if (StrideConv.isInvalid())
- return StrideConv;
- StrideExpr = StrideConv.get();
- TheCall->setArg(2, StrideExpr);
- }
-
- // Check stride argument.
- if (MatrixTy) {
- if (std::optional<llvm::APSInt> Value =
- StrideExpr->getIntegerConstantExpr(Context)) {
- uint64_t Stride = Value->getZExtValue();
- if (Stride < MatrixTy->getNumRows()) {
- Diag(StrideExpr->getBeginLoc(),
- diag::err_builtin_matrix_stride_too_small);
- ArgError = true;
- }
- }
- }
-
- if (ArgError)
- return ExprError();
-
- return CallResult;
-}
-
-void Sema::CheckTCBEnforcement(const SourceLocation CallExprLoc,
- const NamedDecl *Callee) {
- // This warning does not make sense in code that has no runtime behavior.
- if (isUnevaluatedContext())
- return;
-
- const NamedDecl *Caller = getCurFunctionOrMethodDecl();
-
- if (!Caller || !Caller->hasAttr<EnforceTCBAttr>())
- return;
-
- // Search through the enforce_tcb and enforce_tcb_leaf attributes to find
- // all TCBs the callee is a part of.
- llvm::StringSet<> CalleeTCBs;
- for (const auto *A : Callee->specific_attrs<EnforceTCBAttr>())
- CalleeTCBs.insert(A->getTCBName());
- for (const auto *A : Callee->specific_attrs<EnforceTCBLeafAttr>())
- CalleeTCBs.insert(A->getTCBName());
-
- // Go through the TCBs the caller is a part of and emit warnings if Caller
- // is in a TCB that the Callee is not.
- for (const auto *A : Caller->specific_attrs<EnforceTCBAttr>()) {
- StringRef CallerTCB = A->getTCBName();
- if (CalleeTCBs.count(CallerTCB) == 0) {
- this->Diag(CallExprLoc, diag::warn_tcb_enforcement_violation)
- << Callee << CallerTCB;
- }
- }
-}
+//===- SemaChecking.cpp - Extra Semantic Checking -------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file implements extra semantic analysis beyond what is enforced
+// by the C type system.
+//
+//===----------------------------------------------------------------------===//
+
+#include "CheckExprLifetime.h"
+#include "clang/AST/APValue.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/AST/ASTDiagnostic.h"
+#include "clang/AST/Attr.h"
+#include "clang/AST/AttrIterator.h"
+#include "clang/AST/CharUnits.h"
+#include "clang/AST/Decl.h"
+#include "clang/AST/DeclBase.h"
+#include "clang/AST/DeclCXX.h"
+#include "clang/AST/DeclObjC.h"
+#include "clang/AST/DeclarationName.h"
+#include "clang/AST/EvaluatedExprVisitor.h"
+#include "clang/AST/Expr.h"
+#include "clang/AST/ExprCXX.h"
+#include "clang/AST/ExprObjC.h"
+#include "clang/AST/FormatString.h"
+#include "clang/AST/IgnoreExpr.h"
+#include "clang/AST/NSAPI.h"
+#include "clang/AST/NonTrivialTypeVisitor.h"
+#include "clang/AST/OperationKinds.h"
+#include "clang/AST/RecordLayout.h"
+#include "clang/AST/Stmt.h"
+#include "clang/AST/TemplateBase.h"
+#include "clang/AST/TemplateName.h"
+#include "clang/AST/Type.h"
+#include "clang/AST/TypeBase.h"
+#include "clang/AST/TypeLoc.h"
+#include "clang/AST/UnresolvedSet.h"
+#include "clang/Basic/AddressSpaces.h"
+#include "clang/Basic/Diagnostic.h"
+#include "clang/Basic/DiagnosticSema.h"
+#include "clang/Basic/IdentifierTable.h"
+#include "clang/Basic/LLVM.h"
+#include "clang/Basic/LangOptions.h"
+#include "clang/Basic/OpenCLOptions.h"
+#include "clang/Basic/OperatorKinds.h"
+#include "clang/Basic/PartialDiagnostic.h"
+#include "clang/Basic/SourceLocation.h"
+#include "clang/Basic/SourceManager.h"
+#include "clang/Basic/Specifiers.h"
+#include "clang/Basic/SyncScope.h"
+#include "clang/Basic/TargetInfo.h"
+#include "clang/Basic/TypeTraits.h"
+#include "clang/Lex/Lexer.h" // TODO: Extract static functions to fix layering.
+#include "clang/Sema/Initialization.h"
+#include "clang/Sema/Lookup.h"
+#include "clang/Sema/Ownership.h"
+#include "clang/Sema/Scope.h"
+#include "clang/Sema/ScopeInfo.h"
+#include "clang/Sema/Sema.h"
+#include "clang/Sema/SemaAMDGPU.h"
+#include "clang/Sema/SemaARM.h"
+#include "clang/Sema/SemaBPF.h"
+#include "clang/Sema/SemaDirectX.h"
+#include "clang/Sema/SemaHLSL.h"
+#include "clang/Sema/SemaHexagon.h"
+#include "clang/Sema/SemaLoongArch.h"
+#include "clang/Sema/SemaMIPS.h"
+#include "clang/Sema/SemaNVPTX.h"
+#include "clang/Sema/SemaObjC.h"
+#include "clang/Sema/SemaOpenCL.h"
+#include "clang/Sema/SemaPPC.h"
+#include "clang/Sema/SemaRISCV.h"
+#include "clang/Sema/SemaSPIRV.h"
+#include "clang/Sema/SemaSystemZ.h"
+#include "clang/Sema/SemaWasm.h"
+#include "clang/Sema/SemaX86.h"
+#include "llvm/ADT/APFloat.h"
+#include "llvm/ADT/APInt.h"
+#include "llvm/ADT/APSInt.h"
+#include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/DenseMap.h"
+#include "llvm/ADT/FoldingSet.h"
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/STLForwardCompat.h"
+#include "llvm/ADT/SmallBitVector.h"
+#include "llvm/ADT/SmallPtrSet.h"
+#include "llvm/ADT/SmallString.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/ADT/StringExtras.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/ADT/StringSet.h"
+#include "llvm/ADT/StringSwitch.h"
+#include "llvm/Support/AtomicOrdering.h"
+#include "llvm/Support/Compiler.h"
+#include "llvm/Support/ConvertUTF.h"
+#include "llvm/Support/ErrorHandling.h"
+#include "llvm/Support/Format.h"
+#include "llvm/Support/Locale.h"
+#include "llvm/Support/MathExtras.h"
+#include "llvm/Support/SaveAndRestore.h"
+#include "llvm/Support/raw_ostream.h"
+#include "llvm/TargetParser/RISCVTargetParser.h"
+#include "llvm/TargetParser/Triple.h"
+#include <algorithm>
+#include <cassert>
+#include <cctype>
+#include <cstddef>
+#include <cstdint>
+#include <functional>
+#include <limits>
+#include <optional>
+#include <string>
+#include <tuple>
+#include <utility>
+
+using namespace clang;
+using namespace sema;
+
+SourceLocation Sema::getLocationOfStringLiteralByte(const StringLiteral *SL,
+ unsigned ByteNo) const {
+ return SL->getLocationOfByte(ByteNo, getSourceManager(), LangOpts,
+ Context.getTargetInfo());
+}
+
+static constexpr unsigned short combineFAPK(Sema::FormatArgumentPassingKind A,
+ Sema::FormatArgumentPassingKind B) {
+ return (A << 8) | B;
+}
+
+bool Sema::checkArgCountAtLeast(CallExpr *Call, unsigned MinArgCount) {
+ unsigned ArgCount = Call->getNumArgs();
+ if (ArgCount >= MinArgCount)
+ return false;
+
+ return Diag(Call->getEndLoc(), diag::err_typecheck_call_too_few_args)
+ << 0 /*function call*/ << MinArgCount << ArgCount
+ << /*is non object*/ 0 << Call->getSourceRange();
+}
+
+bool Sema::checkArgCountAtMost(CallExpr *Call, unsigned MaxArgCount) {
+ unsigned ArgCount = Call->getNumArgs();
+ if (ArgCount <= MaxArgCount)
+ return false;
+ return Diag(Call->getEndLoc(), diag::err_typecheck_call_too_many_args_at_most)
+ << 0 /*function call*/ << MaxArgCount << ArgCount
+ << /*is non object*/ 0 << Call->getSourceRange();
+}
+
+bool Sema::checkArgCountRange(CallExpr *Call, unsigned MinArgCount,
+ unsigned MaxArgCount) {
+ return checkArgCountAtLeast(Call, MinArgCount) ||
+ checkArgCountAtMost(Call, MaxArgCount);
+}
+
+bool Sema::checkArgCount(CallExpr *Call, unsigned DesiredArgCount) {
+ unsigned ArgCount = Call->getNumArgs();
+ if (ArgCount == DesiredArgCount)
+ return false;
+
+ if (checkArgCountAtLeast(Call, DesiredArgCount))
+ return true;
+ assert(ArgCount > DesiredArgCount && "should have diagnosed this");
+
+ // Highlight all the excess arguments.
+ SourceRange Range(Call->getArg(DesiredArgCount)->getBeginLoc(),
+ Call->getArg(ArgCount - 1)->getEndLoc());
+
+ return Diag(Range.getBegin(), diag::err_typecheck_call_too_many_args)
+ << 0 /*function call*/ << DesiredArgCount << ArgCount
+ << /*is non object*/ 0 << Range;
+}
+
+static bool checkBuiltinVerboseTrap(CallExpr *Call, Sema &S) {
+ bool HasError = false;
+
+ for (const Expr *Arg : Call->arguments()) {
+ if (Arg->isValueDependent())
+ continue;
+
+ std::optional<std::string> ArgString = Arg->tryEvaluateString(S.Context);
+ int DiagMsgKind = -1;
+ // Arguments must be pointers to constant strings and cannot use '$'.
+ if (!ArgString.has_value())
+ DiagMsgKind = 0;
+ else if (ArgString->find('$') != std::string::npos)
+ DiagMsgKind = 1;
+
+ if (DiagMsgKind >= 0) {
+ S.Diag(Arg->getBeginLoc(), diag::err_builtin_verbose_trap_arg)
+ << DiagMsgKind << Arg->getSourceRange();
+ HasError = true;
+ }
+ }
+
+ return !HasError;
+}
+
+static bool convertArgumentToType(Sema &S, Expr *&Value, QualType Ty) {
+ if (Value->isTypeDependent())
+ return false;
+
+ InitializedEntity Entity =
+ InitializedEntity::InitializeParameter(S.Context, Ty, false);
+ ExprResult Result =
+ S.PerformCopyInitialization(Entity, SourceLocation(), Value);
+ if (Result.isInvalid())
+ return true;
+ Value = Result.get();
+ return false;
+}
+
+/// Check that the first argument to __builtin_annotation is an integer
+/// and the second argument is a non-wide string literal.
+static bool BuiltinAnnotation(Sema &S, CallExpr *TheCall) {
+ if (S.checkArgCount(TheCall, 2))
+ return true;
+
+ // First argument should be an integer.
+ Expr *ValArg = TheCall->getArg(0);
+ QualType Ty = ValArg->getType();
+ if (!Ty->isIntegerType()) {
+ S.Diag(ValArg->getBeginLoc(), diag::err_builtin_annotation_first_arg)
+ << ValArg->getSourceRange();
+ return true;
+ }
+
+ // Second argument should be a constant string.
+ Expr *StrArg = TheCall->getArg(1)->IgnoreParenCasts();
+ StringLiteral *Literal = dyn_cast<StringLiteral>(StrArg);
+ if (!Literal || !Literal->isOrdinary()) {
+ S.Diag(StrArg->getBeginLoc(), diag::err_builtin_annotation_second_arg)
+ << StrArg->getSourceRange();
+ return true;
+ }
+
+ TheCall->setType(Ty);
+ return false;
+}
+
+static bool BuiltinMSVCAnnotation(Sema &S, CallExpr *TheCall) {
+ // We need at least one argument.
+ if (TheCall->getNumArgs() < 1) {
+ S.Diag(TheCall->getEndLoc(), diag::err_typecheck_call_too_few_args_at_least)
+ << 0 << 1 << TheCall->getNumArgs() << /*is non object*/ 0
+ << TheCall->getCallee()->getSourceRange();
+ return true;
+ }
+
+ // All arguments should be wide string literals.
+ for (Expr *Arg : TheCall->arguments()) {
+ auto *Literal = dyn_cast<StringLiteral>(Arg->IgnoreParenCasts());
+ if (!Literal || !Literal->isWide()) {
+ S.Diag(Arg->getBeginLoc(), diag::err_msvc_annotation_wide_str)
+ << Arg->getSourceRange();
+ return true;
+ }
+ }
+
+ return false;
+}
+
+/// Check that the argument to __builtin_addressof is a glvalue, and set the
+/// result type to the corresponding pointer type.
+static bool BuiltinAddressof(Sema &S, CallExpr *TheCall) {
+ if (S.checkArgCount(TheCall, 1))
+ return true;
+
+ ExprResult Arg(TheCall->getArg(0));
+ QualType ResultType = S.CheckAddressOfOperand(Arg, TheCall->getBeginLoc());
+ if (ResultType.isNull())
+ return true;
+
+ TheCall->setArg(0, Arg.get());
+ TheCall->setType(ResultType);
+ return false;
+}
+
+/// Check that the argument to __builtin_function_start is a function.
+static bool BuiltinFunctionStart(Sema &S, CallExpr *TheCall) {
+ if (S.checkArgCount(TheCall, 1))
+ return true;
+
+ if (TheCall->getArg(0)->containsErrors())
+ return true;
+
+ ExprResult Arg = S.DefaultFunctionArrayLvalueConversion(TheCall->getArg(0));
+ if (Arg.isInvalid())
+ return true;
+
+ TheCall->setArg(0, Arg.get());
+ const FunctionDecl *FD = dyn_cast_or_null<FunctionDecl>(
+ Arg.get()->getAsBuiltinConstantDeclRef(S.getASTContext()));
+
+ if (!FD) {
+ S.Diag(TheCall->getBeginLoc(), diag::err_function_start_invalid_type)
+ << TheCall->getSourceRange();
+ return true;
+ }
+
+ return !S.checkAddressOfFunctionIsAvailable(FD, /*Complain=*/true,
+ TheCall->getBeginLoc());
+}
+
+/// Check the number of arguments and set the result type to
+/// the argument type.
+static bool BuiltinPreserveAI(Sema &S, CallExpr *TheCall) {
+ if (S.checkArgCount(TheCall, 1))
+ return true;
+
+ TheCall->setType(TheCall->getArg(0)->getType());
+ return false;
+}
+
+/// Check that the value argument for __builtin_is_aligned(value, alignment) and
+/// __builtin_aligned_{up,down}(value, alignment) is an integer or a pointer
+/// type (but not a function pointer) and that the alignment is a power-of-two.
+static bool BuiltinAlignment(Sema &S, CallExpr *TheCall, unsigned ID) {
+ if (S.checkArgCount(TheCall, 2))
+ return true;
+
+ clang::Expr *Source = TheCall->getArg(0);
+ bool IsBooleanAlignBuiltin = ID == Builtin::BI__builtin_is_aligned;
+
+ auto IsValidIntegerType = [](QualType Ty) {
+ return Ty->isIntegerType() && !Ty->isEnumeralType() && !Ty->isBooleanType();
+ };
+ QualType SrcTy = Source->getType();
+ // We should also be able to use it with arrays (but not functions!).
+ if (SrcTy->canDecayToPointerType() && SrcTy->isArrayType()) {
+ SrcTy = S.Context.getDecayedType(SrcTy);
+ }
+ if ((!SrcTy->isPointerType() && !IsValidIntegerType(SrcTy)) ||
+ SrcTy->isFunctionPointerType()) {
+ // FIXME: this is not quite the right error message since we don't allow
+ // floating point types, or member pointers.
+ S.Diag(Source->getExprLoc(), diag::err_typecheck_expect_scalar_operand)
+ << SrcTy;
+ return true;
+ }
+
+ clang::Expr *AlignOp = TheCall->getArg(1);
+ if (!IsValidIntegerType(AlignOp->getType())) {
+ S.Diag(AlignOp->getExprLoc(), diag::err_typecheck_expect_int)
+ << AlignOp->getType();
+ return true;
+ }
+ Expr::EvalResult AlignResult;
+ unsigned MaxAlignmentBits = S.Context.getIntWidth(SrcTy) - 1;
+ // We can't check validity of alignment if it is value dependent.
+ if (!AlignOp->isValueDependent() &&
+ AlignOp->EvaluateAsInt(AlignResult, S.Context,
+ Expr::SE_AllowSideEffects)) {
+ llvm::APSInt AlignValue = AlignResult.Val.getInt();
+ llvm::APSInt MaxValue(
+ llvm::APInt::getOneBitSet(MaxAlignmentBits + 1, MaxAlignmentBits));
+ if (AlignValue < 1) {
+ S.Diag(AlignOp->getExprLoc(), diag::err_alignment_too_small) << 1;
+ return true;
+ }
+ if (llvm::APSInt::compareValues(AlignValue, MaxValue) > 0) {
+ S.Diag(AlignOp->getExprLoc(), diag::err_alignment_too_big)
+ << toString(MaxValue, 10);
+ return true;
+ }
+ if (!AlignValue.isPowerOf2()) {
+ S.Diag(AlignOp->getExprLoc(), diag::err_alignment_not_power_of_two);
+ return true;
+ }
+ if (AlignValue == 1) {
+ S.Diag(AlignOp->getExprLoc(), diag::warn_alignment_builtin_useless)
+ << IsBooleanAlignBuiltin;
+ }
+ }
+
+ ExprResult SrcArg = S.PerformCopyInitialization(
+ InitializedEntity::InitializeParameter(S.Context, SrcTy, false),
+ SourceLocation(), Source);
+ if (SrcArg.isInvalid())
+ return true;
+ TheCall->setArg(0, SrcArg.get());
+ ExprResult AlignArg =
+ S.PerformCopyInitialization(InitializedEntity::InitializeParameter(
+ S.Context, AlignOp->getType(), false),
+ SourceLocation(), AlignOp);
+ if (AlignArg.isInvalid())
+ return true;
+ TheCall->setArg(1, AlignArg.get());
+ // For align_up/align_down, the return type is the same as the (potentially
+ // decayed) argument type including qualifiers. For is_aligned(), the result
+ // is always bool.
+ TheCall->setType(IsBooleanAlignBuiltin ? S.Context.BoolTy : SrcTy);
+ return false;
+}
+
+static bool BuiltinOverflow(Sema &S, CallExpr *TheCall, unsigned BuiltinID) {
+ if (S.checkArgCount(TheCall, 3))
+ return true;
+
+ std::pair<unsigned, const char *> Builtins[] = {
+ { Builtin::BI__builtin_add_overflow, "ckd_add" },
+ { Builtin::BI__builtin_sub_overflow, "ckd_sub" },
+ { Builtin::BI__builtin_mul_overflow, "ckd_mul" },
+ };
+
+ bool CkdOperation = llvm::any_of(Builtins, [&](const std::pair<unsigned,
+ const char *> &P) {
+ return BuiltinID == P.first && TheCall->getExprLoc().isMacroID() &&
+ Lexer::getImmediateMacroName(TheCall->getExprLoc(),
+ S.getSourceManager(), S.getLangOpts()) == P.second;
+ });
+
+ auto ValidCkdIntType = [](QualType QT) {
+ // A valid checked integer type is an integer type other than a plain char,
+ // bool, a bit-precise type, or an enumeration type.
+ if (const auto *BT = QT.getCanonicalType()->getAs<BuiltinType>())
+ return (BT->getKind() >= BuiltinType::Short &&
+ BT->getKind() <= BuiltinType::Int128) || (
+ BT->getKind() >= BuiltinType::UShort &&
+ BT->getKind() <= BuiltinType::UInt128) ||
+ BT->getKind() == BuiltinType::UChar ||
+ BT->getKind() == BuiltinType::SChar;
+ return false;
+ };
+
+ // First two arguments should be integers.
+ for (unsigned I = 0; I < 2; ++I) {
+ ExprResult Arg = S.DefaultFunctionArrayLvalueConversion(TheCall->getArg(I));
+ if (Arg.isInvalid()) return true;
+ TheCall->setArg(I, Arg.get());
+
+ QualType Ty = Arg.get()->getType();
+ bool IsValid = CkdOperation ? ValidCkdIntType(Ty) : Ty->isIntegerType();
+ if (!IsValid) {
+ S.Diag(Arg.get()->getBeginLoc(), diag::err_overflow_builtin_must_be_int)
+ << CkdOperation << Ty << Arg.get()->getSourceRange();
+ return true;
+ }
+ }
+
+ // Third argument should be a pointer to a non-const integer.
+ // IRGen correctly handles volatile, restrict, and address spaces, and
+ // the other qualifiers aren't possible.
+ {
+ ExprResult Arg = S.DefaultFunctionArrayLvalueConversion(TheCall->getArg(2));
+ if (Arg.isInvalid()) return true;
+ TheCall->setArg(2, Arg.get());
+
+ QualType Ty = Arg.get()->getType();
+ const auto *PtrTy = Ty->getAs<PointerType>();
+ if (!PtrTy ||
+ !PtrTy->getPointeeType()->isIntegerType() ||
+ (!ValidCkdIntType(PtrTy->getPointeeType()) && CkdOperation) ||
+ PtrTy->getPointeeType().isConstQualified()) {
+ S.Diag(Arg.get()->getBeginLoc(),
+ diag::err_overflow_builtin_must_be_ptr_int)
+ << CkdOperation << Ty << Arg.get()->getSourceRange();
+ return true;
+ }
+ }
+
+ // Disallow signed bit-precise integer args larger than 128 bits to mul
+ // function until we improve backend support.
+ if (BuiltinID == Builtin::BI__builtin_mul_overflow) {
+ for (unsigned I = 0; I < 3; ++I) {
+ const auto Arg = TheCall->getArg(I);
+ // Third argument will be a pointer.
+ auto Ty = I < 2 ? Arg->getType() : Arg->getType()->getPointeeType();
+ if (Ty->isBitIntType() && Ty->isSignedIntegerType() &&
+ S.getASTContext().getIntWidth(Ty) > 128)
+ return S.Diag(Arg->getBeginLoc(),
+ diag::err_overflow_builtin_bit_int_max_size)
+ << 128;
+ }
+ }
+
+ return false;
+}
+
+namespace {
+struct BuiltinDumpStructGenerator {
+ Sema &S;
+ CallExpr *TheCall;
+ SourceLocation Loc = TheCall->getBeginLoc();
+ SmallVector<Expr *, 32> Actions;
+ DiagnosticErrorTrap ErrorTracker;
+ PrintingPolicy Policy;
+
+ BuiltinDumpStructGenerator(Sema &S, CallExpr *TheCall)
+ : S(S), TheCall(TheCall), ErrorTracker(S.getDiagnostics()),
+ Policy(S.Context.getPrintingPolicy()) {
+ Policy.AnonymousTagLocations = false;
+ }
+
+ Expr *makeOpaqueValueExpr(Expr *Inner) {
+ auto *OVE = new (S.Context)
+ OpaqueValueExpr(Loc, Inner->getType(), Inner->getValueKind(),
+ Inner->getObjectKind(), Inner);
+ Actions.push_back(OVE);
+ return OVE;
+ }
+
+ Expr *getStringLiteral(llvm::StringRef Str) {
+ Expr *Lit = S.Context.getPredefinedStringLiteralFromCache(Str);
+ // Wrap the literal in parentheses to attach a source location.
+ return new (S.Context) ParenExpr(Loc, Loc, Lit);
+ }
+
+ bool callPrintFunction(llvm::StringRef Format,
+ llvm::ArrayRef<Expr *> Exprs = {}) {
+ SmallVector<Expr *, 8> Args;
+ assert(TheCall->getNumArgs() >= 2);
+ Args.reserve((TheCall->getNumArgs() - 2) + /*Format*/ 1 + Exprs.size());
+ Args.assign(TheCall->arg_begin() + 2, TheCall->arg_end());
+ Args.push_back(getStringLiteral(Format));
+ llvm::append_range(Args, Exprs);
+
+ // Register a note to explain why we're performing the call.
+ Sema::CodeSynthesisContext Ctx;
+ Ctx.Kind = Sema::CodeSynthesisContext::BuildingBuiltinDumpStructCall;
+ Ctx.PointOfInstantiation = Loc;
+ Ctx.CallArgs = Args.data();
+ Ctx.NumCallArgs = Args.size();
+ S.pushCodeSynthesisContext(Ctx);
+
+ ExprResult RealCall =
+ S.BuildCallExpr(/*Scope=*/nullptr, TheCall->getArg(1),
+ TheCall->getBeginLoc(), Args, TheCall->getRParenLoc());
+
+ S.popCodeSynthesisContext();
+ if (!RealCall.isInvalid())
+ Actions.push_back(RealCall.get());
+ // Bail out if we've hit any errors, even if we managed to build the
+ // call. We don't want to produce more than one error.
+ return RealCall.isInvalid() || ErrorTracker.hasErrorOccurred();
+ }
+
+ Expr *getIndentString(unsigned Depth) {
+ if (!Depth)
+ return nullptr;
+
+ llvm::SmallString<32> Indent;
+ Indent.resize(Depth * Policy.Indentation, ' ');
+ return getStringLiteral(Indent);
+ }
+
+ Expr *getTypeString(QualType T) {
+ return getStringLiteral(T.getAsString(Policy));
+ }
+
+ bool appendFormatSpecifier(QualType T, llvm::SmallVectorImpl<char> &Str) {
+ llvm::raw_svector_ostream OS(Str);
+
+ // Format 'bool', 'char', 'signed char', 'unsigned char' as numbers, rather
+ // than trying to print a single character.
+ if (auto *BT = T->getAs<BuiltinType>()) {
+ switch (BT->getKind()) {
+ case BuiltinType::Bool:
+ OS << "%d";
+ return true;
+ case BuiltinType::Char_U:
+ case BuiltinType::UChar:
+ OS << "%hhu";
+ return true;
+ case BuiltinType::Char_S:
+ case BuiltinType::SChar:
+ OS << "%hhd";
+ return true;
+ default:
+ break;
+ }
+ }
+
+ analyze_printf::PrintfSpecifier Specifier;
+ if (Specifier.fixType(T, S.getLangOpts(), S.Context, /*IsObjCLiteral=*/false)) {
+ // We were able to guess how to format this.
+ if (Specifier.getConversionSpecifier().getKind() ==
+ analyze_printf::PrintfConversionSpecifier::sArg) {
+ // Wrap double-quotes around a '%s' specifier and limit its maximum
+ // length. Ideally we'd also somehow escape special characters in the
+ // contents but printf doesn't support that.
+ // FIXME: '%s' formatting is not safe in general.
+ OS << '"';
+ Specifier.setPrecision(analyze_printf::OptionalAmount(32u));
+ Specifier.toString(OS);
+ OS << '"';
+ // FIXME: It would be nice to include a '...' if the string doesn't fit
+ // in the length limit.
+ } else {
+ Specifier.toString(OS);
+ }
+ return true;
+ }
+
+ if (T->isPointerType()) {
+ // Format all pointers with '%p'.
+ OS << "%p";
+ return true;
+ }
+
+ return false;
+ }
+
+ bool dumpUnnamedRecord(const RecordDecl *RD, Expr *E, unsigned Depth) {
+ Expr *IndentLit = getIndentString(Depth);
+ Expr *TypeLit = getTypeString(S.Context.getCanonicalTagType(RD));
+ if (IndentLit ? callPrintFunction("%s%s", {IndentLit, TypeLit})
+ : callPrintFunction("%s", {TypeLit}))
+ return true;
+
+ return dumpRecordValue(RD, E, IndentLit, Depth);
+ }
+
+ // Dump a record value. E should be a pointer or lvalue referring to an RD.
+ bool dumpRecordValue(const RecordDecl *RD, Expr *E, Expr *RecordIndent,
+ unsigned Depth) {
+ // FIXME: Decide what to do if RD is a union. At least we should probably
+ // turn off printing `const char*` members with `%s`, because that is very
+ // likely to crash if that's not the active member. Whatever we decide, we
+ // should document it.
+
+ // Build an OpaqueValueExpr so we can refer to E more than once without
+ // triggering re-evaluation.
+ Expr *RecordArg = makeOpaqueValueExpr(E);
+ bool RecordArgIsPtr = RecordArg->getType()->isPointerType();
+
+ if (callPrintFunction(" {\n"))
+ return true;
+
+ // Dump each base class, regardless of whether they're aggregates.
+ if (const auto *CXXRD = dyn_cast<CXXRecordDecl>(RD)) {
+ for (const auto &Base : CXXRD->bases()) {
+ QualType BaseType =
+ RecordArgIsPtr ? S.Context.getPointerType(Base.getType())
+ : S.Context.getLValueReferenceType(Base.getType());
+ ExprResult BasePtr = S.BuildCStyleCastExpr(
+ Loc, S.Context.getTrivialTypeSourceInfo(BaseType, Loc), Loc,
+ RecordArg);
+ if (BasePtr.isInvalid() ||
+ dumpUnnamedRecord(Base.getType()->getAsRecordDecl(), BasePtr.get(),
+ Depth + 1))
+ return true;
+ }
+ }
+
+ Expr *FieldIndentArg = getIndentString(Depth + 1);
+
+ // Dump each field.
+ for (auto *D : RD->decls()) {
+ auto *IFD = dyn_cast<IndirectFieldDecl>(D);
+ auto *FD = IFD ? IFD->getAnonField() : dyn_cast<FieldDecl>(D);
+ if (!FD || FD->isUnnamedBitField() || FD->isAnonymousStructOrUnion())
+ continue;
+
+ llvm::SmallString<20> Format = llvm::StringRef("%s%s %s ");
+ llvm::SmallVector<Expr *, 5> Args = {FieldIndentArg,
+ getTypeString(FD->getType()),
+ getStringLiteral(FD->getName())};
+
+ if (FD->isBitField()) {
+ Format += ": %zu ";
+ QualType SizeT = S.Context.getSizeType();
+ llvm::APInt BitWidth(S.Context.getIntWidth(SizeT),
+ FD->getBitWidthValue());
+ Args.push_back(IntegerLiteral::Create(S.Context, BitWidth, SizeT, Loc));
+ }
+
+ Format += "=";
+
+ ExprResult Field =
+ IFD ? S.BuildAnonymousStructUnionMemberReference(
+ CXXScopeSpec(), Loc, IFD,
+ DeclAccessPair::make(IFD, AS_public), RecordArg, Loc)
+ : S.BuildFieldReferenceExpr(
+ RecordArg, RecordArgIsPtr, Loc, CXXScopeSpec(), FD,
+ DeclAccessPair::make(FD, AS_public),
+ DeclarationNameInfo(FD->getDeclName(), Loc));
+ if (Field.isInvalid())
+ return true;
+
+ auto *InnerRD = FD->getType()->getAsRecordDecl();
+ auto *InnerCXXRD = dyn_cast_or_null<CXXRecordDecl>(InnerRD);
+ if (InnerRD && (!InnerCXXRD || InnerCXXRD->isAggregate())) {
+ // Recursively print the values of members of aggregate record type.
+ if (callPrintFunction(Format, Args) ||
+ dumpRecordValue(InnerRD, Field.get(), FieldIndentArg, Depth + 1))
+ return true;
+ } else {
+ Format += " ";
+ if (appendFormatSpecifier(FD->getType(), Format)) {
+ // We know how to print this field.
+ Args.push_back(Field.get());
+ } else {
+ // We don't know how to print this field. Print out its address
+ // with a format specifier that a smart tool will be able to
+ // recognize and treat specially.
+ Format += "*%p";
+ ExprResult FieldAddr =
+ S.BuildUnaryOp(nullptr, Loc, UO_AddrOf, Field.get());
+ if (FieldAddr.isInvalid())
+ return true;
+ Args.push_back(FieldAddr.get());
+ }
+ Format += "\n";
+ if (callPrintFunction(Format, Args))
+ return true;
+ }
+ }
+
+ return RecordIndent ? callPrintFunction("%s}\n", RecordIndent)
+ : callPrintFunction("}\n");
+ }
+
+ Expr *buildWrapper() {
+ auto *Wrapper = PseudoObjectExpr::Create(S.Context, TheCall, Actions,
+ PseudoObjectExpr::NoResult);
+ TheCall->setType(Wrapper->getType());
+ TheCall->setValueKind(Wrapper->getValueKind());
+ return Wrapper;
+ }
+};
+} // namespace
+
+static ExprResult BuiltinDumpStruct(Sema &S, CallExpr *TheCall) {
+ if (S.checkArgCountAtLeast(TheCall, 2))
+ return ExprError();
+
+ ExprResult PtrArgResult = S.DefaultLvalueConversion(TheCall->getArg(0));
+ if (PtrArgResult.isInvalid())
+ return ExprError();
+ TheCall->setArg(0, PtrArgResult.get());
+
+ // First argument should be a pointer to a struct.
+ QualType PtrArgType = PtrArgResult.get()->getType();
+ if (!PtrArgType->isPointerType() ||
+ !PtrArgType->getPointeeType()->isRecordType()) {
+ S.Diag(PtrArgResult.get()->getBeginLoc(),
+ diag::err_expected_struct_pointer_argument)
+ << 1 << TheCall->getDirectCallee() << PtrArgType;
+ return ExprError();
+ }
+ QualType Pointee = PtrArgType->getPointeeType();
+ const RecordDecl *RD = Pointee->getAsRecordDecl();
+ // Try to instantiate the class template as appropriate; otherwise, access to
+ // its data() may lead to a crash.
+ if (S.RequireCompleteType(PtrArgResult.get()->getBeginLoc(), Pointee,
+ diag::err_incomplete_type))
+ return ExprError();
+ // Second argument is a callable, but we can't fully validate it until we try
+ // calling it.
+ QualType FnArgType = TheCall->getArg(1)->getType();
+ if (!FnArgType->isFunctionType() && !FnArgType->isFunctionPointerType() &&
+ !FnArgType->isBlockPointerType() &&
+ !(S.getLangOpts().CPlusPlus && FnArgType->isRecordType())) {
+ auto *BT = FnArgType->getAs<BuiltinType>();
+ switch (BT ? BT->getKind() : BuiltinType::Void) {
+ case BuiltinType::Dependent:
+ case BuiltinType::Overload:
+ case BuiltinType::BoundMember:
+ case BuiltinType::PseudoObject:
+ case BuiltinType::UnknownAny:
+ case BuiltinType::BuiltinFn:
+ // This might be a callable.
+ break;
+
+ default:
+ S.Diag(TheCall->getArg(1)->getBeginLoc(),
+ diag::err_expected_callable_argument)
+ << 2 << TheCall->getDirectCallee() << FnArgType;
+ return ExprError();
+ }
+ }
+
+ BuiltinDumpStructGenerator Generator(S, TheCall);
+
+ // Wrap parentheses around the given pointer. This is not necessary for
+ // correct code generation, but it means that when we pretty-print the call
+ // arguments in our diagnostics we will produce '(&s)->n' instead of the
+ // incorrect '&s->n'.
+ Expr *PtrArg = PtrArgResult.get();
+ PtrArg = new (S.Context)
+ ParenExpr(PtrArg->getBeginLoc(),
+ S.getLocForEndOfToken(PtrArg->getEndLoc()), PtrArg);
+ if (Generator.dumpUnnamedRecord(RD, PtrArg, 0))
+ return ExprError();
+
+ return Generator.buildWrapper();
+}
+
+static bool BuiltinCallWithStaticChain(Sema &S, CallExpr *BuiltinCall) {
+ if (S.checkArgCount(BuiltinCall, 2))
+ return true;
+
+ SourceLocation BuiltinLoc = BuiltinCall->getBeginLoc();
+ Expr *Builtin = BuiltinCall->getCallee()->IgnoreImpCasts();
+ Expr *Call = BuiltinCall->getArg(0);
+ Expr *Chain = BuiltinCall->getArg(1);
+
+ if (Call->getStmtClass() != Stmt::CallExprClass) {
+ S.Diag(BuiltinLoc, diag::err_first_argument_to_cwsc_not_call)
+ << Call->getSourceRange();
+ return true;
+ }
+
+ auto CE = cast<CallExpr>(Call);
+ if (CE->getCallee()->getType()->isBlockPointerType()) {
+ S.Diag(BuiltinLoc, diag::err_first_argument_to_cwsc_block_call)
+ << Call->getSourceRange();
+ return true;
+ }
+
+ const Decl *TargetDecl = CE->getCalleeDecl();
+ if (const FunctionDecl *FD = dyn_cast_or_null<FunctionDecl>(TargetDecl))
+ if (FD->getBuiltinID()) {
+ S.Diag(BuiltinLoc, diag::err_first_argument_to_cwsc_builtin_call)
+ << Call->getSourceRange();
+ return true;
+ }
+
+ if (isa<CXXPseudoDestructorExpr>(CE->getCallee()->IgnoreParens())) {
+ S.Diag(BuiltinLoc, diag::err_first_argument_to_cwsc_pdtor_call)
+ << Call->getSourceRange();
+ return true;
+ }
+
+ ExprResult ChainResult = S.UsualUnaryConversions(Chain);
+ if (ChainResult.isInvalid())
+ return true;
+ if (!ChainResult.get()->getType()->isPointerType()) {
+ S.Diag(BuiltinLoc, diag::err_second_argument_to_cwsc_not_pointer)
+ << Chain->getSourceRange();
+ return true;
+ }
+
+ QualType ReturnTy = CE->getCallReturnType(S.Context);
+ QualType ArgTys[2] = { ReturnTy, ChainResult.get()->getType() };
+ QualType BuiltinTy = S.Context.getFunctionType(
+ ReturnTy, ArgTys, FunctionProtoType::ExtProtoInfo());
+ QualType BuiltinPtrTy = S.Context.getPointerType(BuiltinTy);
+
+ Builtin =
+ S.ImpCastExprToType(Builtin, BuiltinPtrTy, CK_BuiltinFnToFnPtr).get();
+
+ BuiltinCall->setType(CE->getType());
+ BuiltinCall->setValueKind(CE->getValueKind());
+ BuiltinCall->setObjectKind(CE->getObjectKind());
+ BuiltinCall->setCallee(Builtin);
+ BuiltinCall->setArg(1, ChainResult.get());
+
+ return false;
+}
+
+namespace {
+
+class ScanfDiagnosticFormatHandler
+ : public analyze_format_string::FormatStringHandler {
+ // Accepts the argument index (relative to the first destination index) of the
+ // argument whose size we want.
+ using ComputeSizeFunction =
+ llvm::function_ref<std::optional<llvm::APSInt>(unsigned)>;
+
+ // Accepts the argument index (relative to the first destination index), the
+ // destination size, and the source size).
+ using DiagnoseFunction =
+ llvm::function_ref<void(unsigned, unsigned, unsigned)>;
+
+ ComputeSizeFunction ComputeSizeArgument;
+ DiagnoseFunction Diagnose;
+
+public:
+ ScanfDiagnosticFormatHandler(ComputeSizeFunction ComputeSizeArgument,
+ DiagnoseFunction Diagnose)
+ : ComputeSizeArgument(ComputeSizeArgument), Diagnose(Diagnose) {}
+
+ bool HandleScanfSpecifier(const analyze_scanf::ScanfSpecifier &FS,
+ const char *StartSpecifier,
+ unsigned specifierLen) override {
+ if (!FS.consumesDataArgument())
+ return true;
+
+ unsigned NulByte = 0;
+ switch ((FS.getConversionSpecifier().getKind())) {
+ default:
+ return true;
+ case analyze_format_string::ConversionSpecifier::sArg:
+ case analyze_format_string::ConversionSpecifier::ScanListArg:
+ NulByte = 1;
+ break;
+ case analyze_format_string::ConversionSpecifier::cArg:
+ break;
+ }
+
+ analyze_format_string::OptionalAmount FW = FS.getFieldWidth();
+ if (FW.getHowSpecified() !=
+ analyze_format_string::OptionalAmount::HowSpecified::Constant)
+ return true;
+
+ unsigned SourceSize = FW.getConstantAmount() + NulByte;
+
+ std::optional<llvm::APSInt> DestSizeAPS =
+ ComputeSizeArgument(FS.getArgIndex());
+ if (!DestSizeAPS)
+ return true;
+
+ unsigned DestSize = DestSizeAPS->getZExtValue();
+
+ if (DestSize < SourceSize)
+ Diagnose(FS.getArgIndex(), DestSize, SourceSize);
+
+ return true;
+ }
+};
+
+class EstimateSizeFormatHandler
+ : public analyze_format_string::FormatStringHandler {
+ size_t Size;
+ /// Whether the format string contains Linux kernel's format specifier
+ /// extension.
+ bool IsKernelCompatible = true;
+
+public:
+ EstimateSizeFormatHandler(StringRef Format)
+ : Size(std::min(Format.find(0), Format.size()) +
+ 1 /* null byte always written by sprintf */) {}
+
+ bool HandlePrintfSpecifier(const analyze_printf::PrintfSpecifier &FS,
+ const char *, unsigned SpecifierLen,
+ const TargetInfo &) override {
+
+ const size_t FieldWidth = computeFieldWidth(FS);
+ const size_t Precision = computePrecision(FS);
+
+ // The actual format.
+ switch (FS.getConversionSpecifier().getKind()) {
+ // Just a char.
+ case analyze_format_string::ConversionSpecifier::cArg:
+ case analyze_format_string::ConversionSpecifier::CArg:
+ Size += std::max(FieldWidth, (size_t)1);
+ break;
+ // Just an integer.
+ case analyze_format_string::ConversionSpecifier::dArg:
+ case analyze_format_string::ConversionSpecifier::DArg:
+ case analyze_format_string::ConversionSpecifier::iArg:
+ case analyze_format_string::ConversionSpecifier::oArg:
+ case analyze_format_string::ConversionSpecifier::OArg:
+ case analyze_format_string::ConversionSpecifier::uArg:
+ case analyze_format_string::ConversionSpecifier::UArg:
+ case analyze_format_string::ConversionSpecifier::xArg:
+ case analyze_format_string::ConversionSpecifier::XArg:
+ Size += std::max(FieldWidth, Precision);
+ break;
+
+ // %g style conversion switches between %f or %e style dynamically.
+ // %g removes trailing zeros, and does not print decimal point if there are
+ // no digits that follow it. Thus %g can print a single digit.
+ // FIXME: If it is alternative form:
+ // For g and G conversions, trailing zeros are not removed from the result.
+ case analyze_format_string::ConversionSpecifier::gArg:
+ case analyze_format_string::ConversionSpecifier::GArg:
+ Size += 1;
+ break;
+
+ // Floating point number in the form '[+]ddd.ddd'.
+ case analyze_format_string::ConversionSpecifier::fArg:
+ case analyze_format_string::ConversionSpecifier::FArg:
+ Size += std::max(FieldWidth, 1 /* integer part */ +
+ (Precision ? 1 + Precision
+ : 0) /* period + decimal */);
+ break;
+
+ // Floating point number in the form '[-]d.ddde[+-]dd'.
+ case analyze_format_string::ConversionSpecifier::eArg:
+ case analyze_format_string::ConversionSpecifier::EArg:
+ Size +=
+ std::max(FieldWidth,
+ 1 /* integer part */ +
+ (Precision ? 1 + Precision : 0) /* period + decimal */ +
+ 1 /* e or E letter */ + 2 /* exponent */);
+ break;
+
+ // Floating point number in the form '[-]0xh.hhhhp±dd'.
+ case analyze_format_string::ConversionSpecifier::aArg:
+ case analyze_format_string::ConversionSpecifier::AArg:
+ Size +=
+ std::max(FieldWidth,
+ 2 /* 0x */ + 1 /* integer part */ +
+ (Precision ? 1 + Precision : 0) /* period + decimal */ +
+ 1 /* p or P letter */ + 1 /* + or - */ + 1 /* value */);
+ break;
+
+ // Just a string.
+ case analyze_format_string::ConversionSpecifier::sArg:
+ case analyze_format_string::ConversionSpecifier::SArg:
+ Size += FieldWidth;
+ break;
+
+ // Just a pointer in the form '0xddd'.
+ case analyze_format_string::ConversionSpecifier::pArg:
+ // Linux kernel has its own extesion for `%p` specifier.
+ // Kernel Document:
+ // https://docs.kernel.org/core-api/printk-formats.html#pointer-types
+ IsKernelCompatible = false;
+ Size += std::max(FieldWidth, 2 /* leading 0x */ + Precision);
+ break;
+
+ // A plain percent.
+ case analyze_format_string::ConversionSpecifier::PercentArg:
+ Size += 1;
+ break;
+
+ default:
+ break;
+ }
+
+ // If field width is specified, the sign/space is already accounted for
+ // within the field width, so no additional size is needed.
+ if ((FS.hasPlusPrefix() || FS.hasSpacePrefix()) && FieldWidth == 0)
+ Size += 1;
+
+ if (FS.hasAlternativeForm()) {
+ switch (FS.getConversionSpecifier().getKind()) {
+ // For o conversion, it increases the precision, if and only if necessary,
+ // to force the first digit of the result to be a zero
+ // (if the value and precision are both 0, a single 0 is printed)
+ case analyze_format_string::ConversionSpecifier::oArg:
+ // For b conversion, a nonzero result has 0b prefixed to it.
+ case analyze_format_string::ConversionSpecifier::bArg:
+ // For x (or X) conversion, a nonzero result has 0x (or 0X) prefixed to
+ // it.
+ case analyze_format_string::ConversionSpecifier::xArg:
+ case analyze_format_string::ConversionSpecifier::XArg:
+ // Note: even when the prefix is added, if
+ // (prefix_width <= FieldWidth - formatted_length) holds,
+ // the prefix does not increase the format
+ // size. e.g.(("%#3x", 0xf) is "0xf")
+
+ // If the result is zero, o, b, x, X adds nothing.
+ break;
+ // For a, A, e, E, f, F, g, and G conversions,
+ // the result of converting a floating-point number always contains a
+ // decimal-point
+ case analyze_format_string::ConversionSpecifier::aArg:
+ case analyze_format_string::ConversionSpecifier::AArg:
+ case analyze_format_string::ConversionSpecifier::eArg:
+ case analyze_format_string::ConversionSpecifier::EArg:
+ case analyze_format_string::ConversionSpecifier::fArg:
+ case analyze_format_string::ConversionSpecifier::FArg:
+ case analyze_format_string::ConversionSpecifier::gArg:
+ case analyze_format_string::ConversionSpecifier::GArg:
+ Size += (Precision ? 0 : 1);
+ break;
+ // For other conversions, the behavior is undefined.
+ default:
+ break;
+ }
+ }
+ assert(SpecifierLen <= Size && "no underflow");
+ Size -= SpecifierLen;
+ return true;
+ }
+
+ size_t getSizeLowerBound() const { return Size; }
+ bool isKernelCompatible() const { return IsKernelCompatible; }
+
+private:
+ static size_t computeFieldWidth(const analyze_printf::PrintfSpecifier &FS) {
+ const analyze_format_string::OptionalAmount &FW = FS.getFieldWidth();
+ size_t FieldWidth = 0;
+ if (FW.getHowSpecified() == analyze_format_string::OptionalAmount::Constant)
+ FieldWidth = FW.getConstantAmount();
+ return FieldWidth;
+ }
+
+ static size_t computePrecision(const analyze_printf::PrintfSpecifier &FS) {
+ const analyze_format_string::OptionalAmount &FW = FS.getPrecision();
+ size_t Precision = 0;
+
+ // See man 3 printf for default precision value based on the specifier.
+ switch (FW.getHowSpecified()) {
+ case analyze_format_string::OptionalAmount::NotSpecified:
+ switch (FS.getConversionSpecifier().getKind()) {
+ default:
+ break;
+ case analyze_format_string::ConversionSpecifier::dArg: // %d
+ case analyze_format_string::ConversionSpecifier::DArg: // %D
+ case analyze_format_string::ConversionSpecifier::iArg: // %i
+ Precision = 1;
+ break;
+ case analyze_format_string::ConversionSpecifier::oArg: // %d
+ case analyze_format_string::ConversionSpecifier::OArg: // %D
+ case analyze_format_string::ConversionSpecifier::uArg: // %d
+ case analyze_format_string::ConversionSpecifier::UArg: // %D
+ case analyze_format_string::ConversionSpecifier::xArg: // %d
+ case analyze_format_string::ConversionSpecifier::XArg: // %D
+ Precision = 1;
+ break;
+ case analyze_format_string::ConversionSpecifier::fArg: // %f
+ case analyze_format_string::ConversionSpecifier::FArg: // %F
+ case analyze_format_string::ConversionSpecifier::eArg: // %e
+ case analyze_format_string::ConversionSpecifier::EArg: // %E
+ case analyze_format_string::ConversionSpecifier::gArg: // %g
+ case analyze_format_string::ConversionSpecifier::GArg: // %G
+ Precision = 6;
+ break;
+ case analyze_format_string::ConversionSpecifier::pArg: // %d
+ Precision = 1;
+ break;
+ }
+ break;
+ case analyze_format_string::OptionalAmount::Constant:
+ Precision = FW.getConstantAmount();
+ break;
+ default:
+ break;
+ }
+ return Precision;
+ }
+};
+
+} // namespace
+
+static bool ProcessFormatStringLiteral(const Expr *FormatExpr,
+ StringRef &FormatStrRef, size_t &StrLen,
+ ASTContext &Context) {
+ if (const auto *Format = dyn_cast<StringLiteral>(FormatExpr);
+ Format && (Format->isOrdinary() || Format->isUTF8())) {
+ FormatStrRef = Format->getString();
+ const ConstantArrayType *T =
+ Context.getAsConstantArrayType(Format->getType());
+ assert(T && "String literal not of constant array type!");
+ size_t TypeSize = T->getZExtSize();
+ // In case there's a null byte somewhere.
+ StrLen = std::min(std::max(TypeSize, size_t(1)) - 1, FormatStrRef.find(0));
+ return true;
+ }
+ return false;
+}
+
+void Sema::checkFortifiedBuiltinMemoryFunction(FunctionDecl *FD,
+ CallExpr *TheCall) {
+ if (TheCall->isValueDependent() || TheCall->isTypeDependent() ||
+ isConstantEvaluatedContext())
+ return;
+
+ bool UseDABAttr = false;
+ const FunctionDecl *UseDecl = FD;
+
+ const auto *DABAttr = FD->getAttr<DiagnoseAsBuiltinAttr>();
+ if (DABAttr) {
+ UseDecl = DABAttr->getFunction();
+ assert(UseDecl && "Missing FunctionDecl in DiagnoseAsBuiltin attribute!");
+ UseDABAttr = true;
+ }
+
+ unsigned BuiltinID = UseDecl->getBuiltinID(/*ConsiderWrappers=*/true);
+
+ if (!BuiltinID)
+ return;
+
+ const TargetInfo &TI = getASTContext().getTargetInfo();
+ unsigned SizeTypeWidth = TI.getTypeWidth(TI.getSizeType());
+
+ auto TranslateIndex = [&](unsigned Index) -> std::optional<unsigned> {
+ // If we refer to a diagnose_as_builtin attribute, we need to change the
+ // argument index to refer to the arguments of the called function. Unless
+ // the index is out of bounds, which presumably means it's a variadic
+ // function.
+ if (!UseDABAttr)
+ return Index;
+ unsigned DABIndices = DABAttr->argIndices_size();
+ unsigned NewIndex = Index < DABIndices
+ ? DABAttr->argIndices_begin()[Index]
+ : Index - DABIndices + FD->getNumParams();
+ if (NewIndex >= TheCall->getNumArgs())
+ return std::nullopt;
+ return NewIndex;
+ };
+
+ auto ComputeExplicitObjectSizeArgument =
+ [&](unsigned Index) -> std::optional<llvm::APSInt> {
+ std::optional<unsigned> IndexOptional = TranslateIndex(Index);
+ if (!IndexOptional)
+ return std::nullopt;
+ unsigned NewIndex = *IndexOptional;
+ Expr::EvalResult Result;
+ Expr *SizeArg = TheCall->getArg(NewIndex);
+ if (!SizeArg->EvaluateAsInt(Result, getASTContext()))
+ return std::nullopt;
+ llvm::APSInt Integer = Result.Val.getInt();
+ Integer.setIsUnsigned(true);
+ return Integer;
+ };
+
+ auto ComputeSizeArgument =
+ [&](unsigned Index) -> std::optional<llvm::APSInt> {
+ // If the parameter has a pass_object_size attribute, then we should use its
+ // (potentially) more strict checking mode. Otherwise, conservatively assume
+ // type 0.
+ int BOSType = 0;
+ // This check can fail for variadic functions.
+ if (Index < FD->getNumParams()) {
+ if (const auto *POS =
+ FD->getParamDecl(Index)->getAttr<PassObjectSizeAttr>())
+ BOSType = POS->getType();
+ }
+
+ std::optional<unsigned> IndexOptional = TranslateIndex(Index);
+ if (!IndexOptional)
+ return std::nullopt;
+ unsigned NewIndex = *IndexOptional;
+
+ if (NewIndex >= TheCall->getNumArgs())
+ return std::nullopt;
+
+ const Expr *ObjArg = TheCall->getArg(NewIndex);
+ if (std::optional<uint64_t> ObjSize =
+ ObjArg->tryEvaluateObjectSize(getASTContext(), BOSType)) {
+ // Get the object size in the target's size_t width.
+ return llvm::APSInt::getUnsigned(*ObjSize).extOrTrunc(SizeTypeWidth);
+ }
+ return std::nullopt;
+ };
+
+ auto ComputeStrLenArgument =
+ [&](unsigned Index) -> std::optional<llvm::APSInt> {
+ std::optional<unsigned> IndexOptional = TranslateIndex(Index);
+ if (!IndexOptional)
+ return std::nullopt;
+ unsigned NewIndex = *IndexOptional;
+
+ const Expr *ObjArg = TheCall->getArg(NewIndex);
+
+ if (std::optional<uint64_t> Result =
+ ObjArg->tryEvaluateStrLen(getASTContext())) {
+ // Add 1 for null byte.
+ return llvm::APSInt::getUnsigned(*Result + 1).extOrTrunc(SizeTypeWidth);
+ }
+ return std::nullopt;
+ };
+
+ std::optional<llvm::APSInt> SourceSize;
+ std::optional<llvm::APSInt> DestinationSize;
+ unsigned DiagID = 0;
+ bool IsChkVariant = false;
+
+ auto GetFunctionName = [&]() {
+ std::string FunctionNameStr =
+ getASTContext().BuiltinInfo.getName(BuiltinID);
+ llvm::StringRef FunctionName = FunctionNameStr;
+ // Skim off the details of whichever builtin was called to produce a better
+ // diagnostic, as it's unlikely that the user wrote the __builtin
+ // explicitly.
+ if (IsChkVariant) {
+ FunctionName = FunctionName.drop_front(std::strlen("__builtin___"));
+ FunctionName = FunctionName.drop_back(std::strlen("_chk"));
+ } else {
+ FunctionName.consume_front("__builtin_");
+ }
+ return FunctionName.str();
+ };
+
+ switch (BuiltinID) {
+ default:
+ return;
+ case Builtin::BI__builtin_strcat:
+ case Builtin::BIstrcat:
+ case Builtin::BI__builtin_stpcpy:
+ case Builtin::BIstpcpy:
+ case Builtin::BI__builtin_strcpy:
+ case Builtin::BIstrcpy: {
+ DiagID = diag::warn_fortify_strlen_overflow;
+ SourceSize = ComputeStrLenArgument(1);
+ DestinationSize = ComputeSizeArgument(0);
+ break;
+ }
+
+ case Builtin::BI__builtin___strcat_chk:
+ case Builtin::BI__builtin___stpcpy_chk:
+ case Builtin::BI__builtin___strcpy_chk: {
+ DiagID = diag::warn_fortify_strlen_overflow;
+ SourceSize = ComputeStrLenArgument(1);
+ DestinationSize = ComputeExplicitObjectSizeArgument(2);
+ IsChkVariant = true;
+ break;
+ }
+
+ case Builtin::BIscanf:
+ case Builtin::BIfscanf:
+ case Builtin::BIsscanf: {
+ unsigned FormatIndex = 1;
+ unsigned DataIndex = 2;
+ if (BuiltinID == Builtin::BIscanf) {
+ FormatIndex = 0;
+ DataIndex = 1;
+ }
+
+ const auto *FormatExpr =
+ TheCall->getArg(FormatIndex)->IgnoreParenImpCasts();
+
+ StringRef FormatStrRef;
+ size_t StrLen;
+ if (!ProcessFormatStringLiteral(FormatExpr, FormatStrRef, StrLen, Context))
+ return;
+
+ auto Diagnose = [&](unsigned ArgIndex, unsigned DestSize,
+ unsigned SourceSize) {
+ DiagID = diag::warn_fortify_scanf_overflow;
+ unsigned Index = ArgIndex + DataIndex;
+ std::string FunctionName = GetFunctionName();
+ DiagRuntimeBehavior(TheCall->getArg(Index)->getBeginLoc(), TheCall,
+ PDiag(DiagID) << FunctionName << (Index + 1)
+ << DestSize << SourceSize);
+ };
+
+ auto ShiftedComputeSizeArgument = [&](unsigned Index) {
+ return ComputeSizeArgument(Index + DataIndex);
+ };
+ ScanfDiagnosticFormatHandler H(ShiftedComputeSizeArgument, Diagnose);
+ const char *FormatBytes = FormatStrRef.data();
+ analyze_format_string::ParseScanfString(H, FormatBytes,
+ FormatBytes + StrLen, getLangOpts(),
+ Context.getTargetInfo());
+
+ // Unlike the other cases, in this one we have already issued the diagnostic
+ // here, so no need to continue (because unlike the other cases, here the
+ // diagnostic refers to the argument number).
+ return;
+ }
+
+ case Builtin::BIsprintf:
+ case Builtin::BI__builtin___sprintf_chk: {
+ size_t FormatIndex = BuiltinID == Builtin::BIsprintf ? 1 : 3;
+ auto *FormatExpr = TheCall->getArg(FormatIndex)->IgnoreParenImpCasts();
+
+ StringRef FormatStrRef;
+ size_t StrLen;
+ if (ProcessFormatStringLiteral(FormatExpr, FormatStrRef, StrLen, Context)) {
+ EstimateSizeFormatHandler H(FormatStrRef);
+ const char *FormatBytes = FormatStrRef.data();
+ if (!analyze_format_string::ParsePrintfString(
+ H, FormatBytes, FormatBytes + StrLen, getLangOpts(),
+ Context.getTargetInfo(), false)) {
+ DiagID = H.isKernelCompatible()
+ ? diag::warn_format_overflow
+ : diag::warn_format_overflow_non_kprintf;
+ SourceSize = llvm::APSInt::getUnsigned(H.getSizeLowerBound())
+ .extOrTrunc(SizeTypeWidth);
+ if (BuiltinID == Builtin::BI__builtin___sprintf_chk) {
+ DestinationSize = ComputeExplicitObjectSizeArgument(2);
+ IsChkVariant = true;
+ } else {
+ DestinationSize = ComputeSizeArgument(0);
+ }
+ break;
+ }
+ }
+ return;
+ }
+ case Builtin::BI__builtin___memcpy_chk:
+ case Builtin::BI__builtin___memmove_chk:
+ case Builtin::BI__builtin___memset_chk:
+ case Builtin::BI__builtin___strlcat_chk:
+ case Builtin::BI__builtin___strlcpy_chk:
+ case Builtin::BI__builtin___strncat_chk:
+ case Builtin::BI__builtin___strncpy_chk:
+ case Builtin::BI__builtin___stpncpy_chk:
+ case Builtin::BI__builtin___memccpy_chk:
+ case Builtin::BI__builtin___mempcpy_chk: {
+ DiagID = diag::warn_builtin_chk_overflow;
+ SourceSize = ComputeExplicitObjectSizeArgument(TheCall->getNumArgs() - 2);
+ DestinationSize =
+ ComputeExplicitObjectSizeArgument(TheCall->getNumArgs() - 1);
+ IsChkVariant = true;
+ break;
+ }
+
+ case Builtin::BI__builtin___snprintf_chk:
+ case Builtin::BI__builtin___vsnprintf_chk: {
+ DiagID = diag::warn_builtin_chk_overflow;
+ SourceSize = ComputeExplicitObjectSizeArgument(1);
+ DestinationSize = ComputeExplicitObjectSizeArgument(3);
+ IsChkVariant = true;
+ break;
+ }
+
+ case Builtin::BIstrncat:
+ case Builtin::BI__builtin_strncat:
+ case Builtin::BIstrncpy:
+ case Builtin::BI__builtin_strncpy:
+ case Builtin::BIstpncpy:
+ case Builtin::BI__builtin_stpncpy: {
+ // Whether these functions overflow depends on the runtime strlen of the
+ // string, not just the buffer size, so emitting the "always overflow"
+ // diagnostic isn't quite right. We should still diagnose passing a buffer
+ // size larger than the destination buffer though; this is a runtime abort
+ // in _FORTIFY_SOURCE mode, and is quite suspicious otherwise.
+ DiagID = diag::warn_fortify_source_size_mismatch;
+ SourceSize = ComputeExplicitObjectSizeArgument(TheCall->getNumArgs() - 1);
+ DestinationSize = ComputeSizeArgument(0);
+ break;
+ }
+
+ case Builtin::BImemcpy:
+ case Builtin::BI__builtin_memcpy:
+ case Builtin::BImemmove:
+ case Builtin::BI__builtin_memmove:
+ case Builtin::BImemset:
+ case Builtin::BI__builtin_memset:
+ case Builtin::BImempcpy:
+ case Builtin::BI__builtin_mempcpy: {
+ DiagID = diag::warn_fortify_source_overflow;
+ SourceSize = ComputeExplicitObjectSizeArgument(TheCall->getNumArgs() - 1);
+ DestinationSize = ComputeSizeArgument(0);
+ break;
+ }
+ case Builtin::BIsnprintf:
+ case Builtin::BI__builtin_snprintf:
+ case Builtin::BIvsnprintf:
+ case Builtin::BI__builtin_vsnprintf: {
+ DiagID = diag::warn_fortify_source_size_mismatch;
+ SourceSize = ComputeExplicitObjectSizeArgument(1);
+ const auto *FormatExpr = TheCall->getArg(2)->IgnoreParenImpCasts();
+ StringRef FormatStrRef;
+ size_t StrLen;
+ if (SourceSize &&
+ ProcessFormatStringLiteral(FormatExpr, FormatStrRef, StrLen, Context)) {
+ EstimateSizeFormatHandler H(FormatStrRef);
+ const char *FormatBytes = FormatStrRef.data();
+ if (!analyze_format_string::ParsePrintfString(
+ H, FormatBytes, FormatBytes + StrLen, getLangOpts(),
+ Context.getTargetInfo(), /*isFreeBSDKPrintf=*/false)) {
+ llvm::APSInt FormatSize =
+ llvm::APSInt::getUnsigned(H.getSizeLowerBound())
+ .extOrTrunc(SizeTypeWidth);
+ if (FormatSize > *SourceSize && *SourceSize != 0) {
+ unsigned TruncationDiagID =
+ H.isKernelCompatible() ? diag::warn_format_truncation
+ : diag::warn_format_truncation_non_kprintf;
+ SmallString<16> SpecifiedSizeStr;
+ SmallString<16> FormatSizeStr;
+ SourceSize->toString(SpecifiedSizeStr, /*Radix=*/10);
+ FormatSize.toString(FormatSizeStr, /*Radix=*/10);
+ DiagRuntimeBehavior(TheCall->getBeginLoc(), TheCall,
+ PDiag(TruncationDiagID)
+ << GetFunctionName() << SpecifiedSizeStr
+ << FormatSizeStr);
+ }
+ }
+ }
+ DestinationSize = ComputeSizeArgument(0);
+ }
+ }
+
+ if (!SourceSize || !DestinationSize ||
+ llvm::APSInt::compareValues(*SourceSize, *DestinationSize) <= 0)
+ return;
+
+ std::string FunctionName = GetFunctionName();
+
+ SmallString<16> DestinationStr;
+ SmallString<16> SourceStr;
+ DestinationSize->toString(DestinationStr, /*Radix=*/10);
+ SourceSize->toString(SourceStr, /*Radix=*/10);
+ DiagRuntimeBehavior(TheCall->getBeginLoc(), TheCall,
+ PDiag(DiagID)
+ << FunctionName << DestinationStr << SourceStr);
+}
+
+static bool BuiltinSEHScopeCheck(Sema &SemaRef, CallExpr *TheCall,
+ Scope::ScopeFlags NeededScopeFlags,
+ unsigned DiagID) {
+ // Scopes aren't available during instantiation. Fortunately, builtin
+ // functions cannot be template args so they cannot be formed through template
+ // instantiation. Therefore checking once during the parse is sufficient.
+ if (SemaRef.inTemplateInstantiation())
+ return false;
+
+ Scope *S = SemaRef.getCurScope();
+ while (S && !S->isSEHExceptScope())
+ S = S->getParent();
+ if (!S || !(S->getFlags() & NeededScopeFlags)) {
+ auto *DRE = cast<DeclRefExpr>(TheCall->getCallee()->IgnoreParenCasts());
+ SemaRef.Diag(TheCall->getExprLoc(), DiagID)
+ << DRE->getDecl()->getIdentifier();
+ return true;
+ }
+
+ return false;
+}
+
+// In OpenCL, __builtin_alloca_* should return a pointer to address space
+// that corresponds to the stack address space i.e private address space.
+static void builtinAllocaAddrSpace(Sema &S, CallExpr *TheCall) {
+ QualType RT = TheCall->getType();
+ assert((RT->isPointerType() && !(RT->getPointeeType().hasAddressSpace())) &&
+ "__builtin_alloca has invalid address space");
+
+ RT = RT->getPointeeType();
+ RT = S.Context.getAddrSpaceQualType(RT, LangAS::opencl_private);
+ TheCall->setType(S.Context.getPointerType(RT));
+}
+
+static bool checkBuiltinInferAllocToken(Sema &S, CallExpr *TheCall) {
+ if (S.checkArgCountAtLeast(TheCall, 1))
+ return true;
+
+ for (Expr *Arg : TheCall->arguments()) {
+ // If argument is dependent on a template parameter, we can't resolve now.
+ if (Arg->isTypeDependent() || Arg->isValueDependent())
+ continue;
+ // Reject void types.
+ QualType ArgTy = Arg->IgnoreParenImpCasts()->getType();
+ if (ArgTy->isVoidType())
+ return S.Diag(Arg->getBeginLoc(), diag::err_param_with_void_type);
+ }
+
+ TheCall->setType(S.Context.getSizeType());
+ return false;
+}
+
+namespace {
+enum PointerAuthOpKind {
+ PAO_Strip,
+ PAO_Sign,
+ PAO_Auth,
+ PAO_SignGeneric,
+ PAO_Discriminator,
+ PAO_BlendPointer,
+ PAO_BlendInteger
+};
+}
+
+bool Sema::checkPointerAuthEnabled(SourceLocation Loc, SourceRange Range) {
+ if (getLangOpts().PointerAuthIntrinsics)
+ return false;
+
+ Diag(Loc, diag::err_ptrauth_disabled) << Range;
+ return true;
+}
+
+static bool checkPointerAuthEnabled(Sema &S, Expr *E) {
+ return S.checkPointerAuthEnabled(E->getExprLoc(), E->getSourceRange());
+}
+
+static bool checkPointerAuthKey(Sema &S, Expr *&Arg) {
+ // Convert it to type 'int'.
+ if (convertArgumentToType(S, Arg, S.Context.IntTy))
+ return true;
+
+ // Value-dependent expressions are okay; wait for template instantiation.
+ if (Arg->isValueDependent())
+ return false;
+
+ unsigned KeyValue;
+ return S.checkConstantPointerAuthKey(Arg, KeyValue);
+}
+
+bool Sema::checkConstantPointerAuthKey(Expr *Arg, unsigned &Result) {
+ // Attempt to constant-evaluate the expression.
+ std::optional<llvm::APSInt> KeyValue = Arg->getIntegerConstantExpr(Context);
+ if (!KeyValue) {
+ Diag(Arg->getExprLoc(), diag::err_expr_not_ice)
+ << 0 << Arg->getSourceRange();
+ return true;
+ }
+
+ // Ask the target to validate the key parameter.
+ if (!Context.getTargetInfo().validatePointerAuthKey(*KeyValue)) {
+ llvm::SmallString<32> Value;
+ {
+ llvm::raw_svector_ostream Str(Value);
+ Str << *KeyValue;
+ }
+
+ Diag(Arg->getExprLoc(), diag::err_ptrauth_invalid_key)
+ << Value << Arg->getSourceRange();
+ return true;
+ }
+
+ Result = KeyValue->getZExtValue();
+ return false;
+}
+
+bool Sema::checkPointerAuthDiscriminatorArg(Expr *Arg,
+ PointerAuthDiscArgKind Kind,
+ unsigned &IntVal) {
+ if (!Arg) {
+ IntVal = 0;
+ return true;
+ }
+
+ std::optional<llvm::APSInt> Result = Arg->getIntegerConstantExpr(Context);
+ if (!Result) {
+ Diag(Arg->getExprLoc(), diag::err_ptrauth_arg_not_ice);
+ return false;
+ }
+
+ unsigned Max;
+ bool IsAddrDiscArg = false;
+
+ switch (Kind) {
+ case PointerAuthDiscArgKind::Addr:
+ Max = 1;
+ IsAddrDiscArg = true;
+ break;
+ case PointerAuthDiscArgKind::Extra:
+ Max = PointerAuthQualifier::MaxDiscriminator;
+ break;
+ };
+
+ if (*Result < 0 || *Result > Max) {
+ if (IsAddrDiscArg)
+ Diag(Arg->getExprLoc(), diag::err_ptrauth_address_discrimination_invalid)
+ << Result->getExtValue();
+ else
+ Diag(Arg->getExprLoc(), diag::err_ptrauth_extra_discriminator_invalid)
+ << Result->getExtValue() << Max;
+
+ return false;
+ };
+
+ IntVal = Result->getZExtValue();
+ return true;
+}
+
+static std::pair<const ValueDecl *, CharUnits>
+findConstantBaseAndOffset(Sema &S, Expr *E) {
+ // Must evaluate as a pointer.
+ Expr::EvalResult Result;
+ if (!E->EvaluateAsRValue(Result, S.Context) || !Result.Val.isLValue())
+ return {nullptr, CharUnits()};
+
+ const auto *BaseDecl =
+ Result.Val.getLValueBase().dyn_cast<const ValueDecl *>();
+ if (!BaseDecl)
+ return {nullptr, CharUnits()};
+
+ return {BaseDecl, Result.Val.getLValueOffset()};
+}
+
+static bool checkPointerAuthValue(Sema &S, Expr *&Arg, PointerAuthOpKind OpKind,
+ bool RequireConstant = false) {
+ if (Arg->hasPlaceholderType()) {
+ ExprResult R = S.CheckPlaceholderExpr(Arg);
+ if (R.isInvalid())
+ return true;
+ Arg = R.get();
+ }
+
+ auto AllowsPointer = [](PointerAuthOpKind OpKind) {
+ return OpKind != PAO_BlendInteger;
+ };
+ auto AllowsInteger = [](PointerAuthOpKind OpKind) {
+ return OpKind == PAO_Discriminator || OpKind == PAO_BlendInteger ||
+ OpKind == PAO_SignGeneric;
+ };
+
+ // Require the value to have the right range of type.
+ QualType ExpectedTy;
+ if (AllowsPointer(OpKind) && Arg->getType()->isPointerType()) {
+ ExpectedTy = Arg->getType().getUnqualifiedType();
+ } else if (AllowsPointer(OpKind) && Arg->getType()->isNullPtrType()) {
+ ExpectedTy = S.Context.VoidPtrTy;
+ } else if (AllowsInteger(OpKind) &&
+ Arg->getType()->isIntegralOrUnscopedEnumerationType()) {
+ ExpectedTy = S.Context.getUIntPtrType();
+
+ } else {
+ // Diagnose the failures.
+ S.Diag(Arg->getExprLoc(), diag::err_ptrauth_value_bad_type)
+ << unsigned(OpKind == PAO_Discriminator ? 1
+ : OpKind == PAO_BlendPointer ? 2
+ : OpKind == PAO_BlendInteger ? 3
+ : 0)
+ << unsigned(AllowsInteger(OpKind) ? (AllowsPointer(OpKind) ? 2 : 1) : 0)
+ << Arg->getType() << Arg->getSourceRange();
+ return true;
+ }
+
+ // Convert to that type. This should just be an lvalue-to-rvalue
+ // conversion.
+ if (convertArgumentToType(S, Arg, ExpectedTy))
+ return true;
+
+ if (!RequireConstant) {
+ // Warn about null pointers for non-generic sign and auth operations.
+ if ((OpKind == PAO_Sign || OpKind == PAO_Auth) &&
+ Arg->isNullPointerConstant(S.Context, Expr::NPC_ValueDependentIsNull)) {
+ S.Diag(Arg->getExprLoc(), OpKind == PAO_Sign
+ ? diag::warn_ptrauth_sign_null_pointer
+ : diag::warn_ptrauth_auth_null_pointer)
+ << Arg->getSourceRange();
+ }
+
+ return false;
+ }
+
+ // Perform special checking on the arguments to ptrauth_sign_constant.
+
+ // The main argument.
+ if (OpKind == PAO_Sign) {
+ // Require the value we're signing to have a special form.
+ auto [BaseDecl, Offset] = findConstantBaseAndOffset(S, Arg);
+ bool Invalid;
+
+ // Must be rooted in a declaration reference.
+ if (!BaseDecl)
+ Invalid = true;
+
+ // If it's a function declaration, we can't have an offset.
+ else if (isa<FunctionDecl>(BaseDecl))
+ Invalid = !Offset.isZero();
+
+ // Otherwise we're fine.
+ else
+ Invalid = false;
+
+ if (Invalid)
+ S.Diag(Arg->getExprLoc(), diag::err_ptrauth_bad_constant_pointer);
+ return Invalid;
+ }
+
+ // The discriminator argument.
+ assert(OpKind == PAO_Discriminator);
+
+ // Must be a pointer or integer or blend thereof.
+ Expr *Pointer = nullptr;
+ Expr *Integer = nullptr;
+ if (auto *Call = dyn_cast<CallExpr>(Arg->IgnoreParens())) {
+ if (Call->getBuiltinCallee() ==
+ Builtin::BI__builtin_ptrauth_blend_discriminator) {
+ Pointer = Call->getArg(0);
+ Integer = Call->getArg(1);
+ }
+ }
+ if (!Pointer && !Integer) {
+ if (Arg->getType()->isPointerType())
+ Pointer = Arg;
+ else
+ Integer = Arg;
+ }
+
+ // Check the pointer.
+ bool Invalid = false;
+ if (Pointer) {
+ assert(Pointer->getType()->isPointerType());
+
+ // TODO: if we're initializing a global, check that the address is
+ // somehow related to what we're initializing. This probably will
+ // never really be feasible and we'll have to catch it at link-time.
+ auto [BaseDecl, Offset] = findConstantBaseAndOffset(S, Pointer);
+ if (!BaseDecl || !isa<VarDecl>(BaseDecl))
+ Invalid = true;
+ }
+
+ // Check the integer.
+ if (Integer) {
+ assert(Integer->getType()->isIntegerType());
+ if (!Integer->isEvaluatable(S.Context))
+ Invalid = true;
+ }
+
+ if (Invalid)
+ S.Diag(Arg->getExprLoc(), diag::err_ptrauth_bad_constant_discriminator);
+ return Invalid;
+}
+
+static ExprResult PointerAuthStrip(Sema &S, CallExpr *Call) {
+ if (S.checkArgCount(Call, 2))
+ return ExprError();
+ if (checkPointerAuthEnabled(S, Call))
+ return ExprError();
+ if (checkPointerAuthValue(S, Call->getArgs()[0], PAO_Strip) ||
+ checkPointerAuthKey(S, Call->getArgs()[1]))
+ return ExprError();
+
+ Call->setType(Call->getArgs()[0]->getType());
+ return Call;
+}
+
+static ExprResult PointerAuthBlendDiscriminator(Sema &S, CallExpr *Call) {
+ if (S.checkArgCount(Call, 2))
+ return ExprError();
+ if (checkPointerAuthEnabled(S, Call))
+ return ExprError();
+ if (checkPointerAuthValue(S, Call->getArgs()[0], PAO_BlendPointer) ||
+ checkPointerAuthValue(S, Call->getArgs()[1], PAO_BlendInteger))
+ return ExprError();
+
+ Call->setType(S.Context.getUIntPtrType());
+ return Call;
+}
+
+static ExprResult PointerAuthSignGenericData(Sema &S, CallExpr *Call) {
+ if (S.checkArgCount(Call, 2))
+ return ExprError();
+ if (checkPointerAuthEnabled(S, Call))
+ return ExprError();
+ if (checkPointerAuthValue(S, Call->getArgs()[0], PAO_SignGeneric) ||
+ checkPointerAuthValue(S, Call->getArgs()[1], PAO_Discriminator))
+ return ExprError();
+
+ Call->setType(S.Context.getUIntPtrType());
+ return Call;
+}
+
+static ExprResult PointerAuthSignOrAuth(Sema &S, CallExpr *Call,
+ PointerAuthOpKind OpKind,
+ bool RequireConstant) {
+ if (S.checkArgCount(Call, 3))
+ return ExprError();
+ if (checkPointerAuthEnabled(S, Call))
+ return ExprError();
+ if (checkPointerAuthValue(S, Call->getArgs()[0], OpKind, RequireConstant) ||
+ checkPointerAuthKey(S, Call->getArgs()[1]) ||
+ checkPointerAuthValue(S, Call->getArgs()[2], PAO_Discriminator,
+ RequireConstant))
+ return ExprError();
+
+ Call->setType(Call->getArgs()[0]->getType());
+ return Call;
+}
+
+static ExprResult PointerAuthAuthAndResign(Sema &S, CallExpr *Call) {
+ if (S.checkArgCount(Call, 5))
+ return ExprError();
+ if (checkPointerAuthEnabled(S, Call))
+ return ExprError();
+ if (checkPointerAuthValue(S, Call->getArgs()[0], PAO_Auth) ||
+ checkPointerAuthKey(S, Call->getArgs()[1]) ||
+ checkPointerAuthValue(S, Call->getArgs()[2], PAO_Discriminator) ||
+ checkPointerAuthKey(S, Call->getArgs()[3]) ||
+ checkPointerAuthValue(S, Call->getArgs()[4], PAO_Discriminator))
+ return ExprError();
+
+ Call->setType(Call->getArgs()[0]->getType());
+ return Call;
+}
+
+static ExprResult PointerAuthAuthLoadRelativeAndSign(Sema &S, CallExpr *Call) {
+ if (S.checkArgCount(Call, 6))
+ return ExprError();
+ if (checkPointerAuthEnabled(S, Call))
+ return ExprError();
+ const Expr *AddendExpr = Call->getArg(5);
+ bool AddendIsConstInt = AddendExpr->isIntegerConstantExpr(S.Context);
+ if (!AddendIsConstInt) {
+ const Expr *Arg = Call->getArg(5)->IgnoreParenImpCasts();
+ DeclRefExpr *DRE = cast<DeclRefExpr>(Call->getCallee()->IgnoreParenCasts());
+ FunctionDecl *FDecl = cast<FunctionDecl>(DRE->getDecl());
+ S.Diag(Arg->getBeginLoc(), diag::err_constant_integer_last_arg_type)
+ << FDecl->getDeclName() << Arg->getSourceRange();
+ }
+ if (checkPointerAuthValue(S, Call->getArgs()[0], PAO_Auth) ||
+ checkPointerAuthKey(S, Call->getArgs()[1]) ||
+ checkPointerAuthValue(S, Call->getArgs()[2], PAO_Discriminator) ||
+ checkPointerAuthKey(S, Call->getArgs()[3]) ||
+ checkPointerAuthValue(S, Call->getArgs()[4], PAO_Discriminator) ||
+ !AddendIsConstInt)
+ return ExprError();
+
+ Call->setType(Call->getArgs()[0]->getType());
+ return Call;
+}
+
+static ExprResult PointerAuthStringDiscriminator(Sema &S, CallExpr *Call) {
+ if (checkPointerAuthEnabled(S, Call))
+ return ExprError();
+
+ // We've already performed normal call type-checking.
+ const Expr *Arg = Call->getArg(0)->IgnoreParenImpCasts();
+
+ // Operand must be an ordinary or UTF-8 string literal.
+ const auto *Literal = dyn_cast<StringLiteral>(Arg);
+ if (!Literal || Literal->getCharByteWidth() != 1) {
+ S.Diag(Arg->getExprLoc(), diag::err_ptrauth_string_not_literal)
+ << (Literal ? 1 : 0) << Arg->getSourceRange();
+ return ExprError();
+ }
+
+ return Call;
+}
+
+static ExprResult GetVTablePointer(Sema &S, CallExpr *Call) {
+ if (S.checkArgCount(Call, 1))
+ return ExprError();
+ Expr *FirstArg = Call->getArg(0);
+ ExprResult FirstValue = S.DefaultFunctionArrayLvalueConversion(FirstArg);
+ if (FirstValue.isInvalid())
+ return ExprError();
+ Call->setArg(0, FirstValue.get());
+ QualType FirstArgType = FirstArg->getType();
+ if (FirstArgType->canDecayToPointerType() && FirstArgType->isArrayType())
+ FirstArgType = S.Context.getDecayedType(FirstArgType);
+
+ const CXXRecordDecl *FirstArgRecord = FirstArgType->getPointeeCXXRecordDecl();
+ if (!FirstArgRecord) {
+ S.Diag(FirstArg->getBeginLoc(), diag::err_get_vtable_pointer_incorrect_type)
+ << /*isPolymorphic=*/0 << FirstArgType;
+ return ExprError();
+ }
+ if (S.RequireCompleteType(
+ FirstArg->getBeginLoc(), FirstArgType->getPointeeType(),
+ diag::err_get_vtable_pointer_requires_complete_type)) {
+ return ExprError();
+ }
+
+ if (!FirstArgRecord->isPolymorphic()) {
+ S.Diag(FirstArg->getBeginLoc(), diag::err_get_vtable_pointer_incorrect_type)
+ << /*isPolymorphic=*/1 << FirstArgRecord;
+ return ExprError();
+ }
+ QualType ReturnType = S.Context.getPointerType(S.Context.VoidTy.withConst());
+ Call->setType(ReturnType);
+ return Call;
+}
+
+static ExprResult BuiltinLaunder(Sema &S, CallExpr *TheCall) {
+ if (S.checkArgCount(TheCall, 1))
+ return ExprError();
+
+ // Compute __builtin_launder's parameter type from the argument.
+ // The parameter type is:
+ // * The type of the argument if it's not an array or function type,
+ // Otherwise,
+ // * The decayed argument type.
+ QualType ParamTy = [&]() {
+ QualType ArgTy = TheCall->getArg(0)->getType();
+ if (const ArrayType *Ty = ArgTy->getAsArrayTypeUnsafe())
+ return S.Context.getPointerType(Ty->getElementType());
+ if (ArgTy->isFunctionType()) {
+ return S.Context.getPointerType(ArgTy);
+ }
+ return ArgTy;
+ }();
+
+ TheCall->setType(ParamTy);
+
+ auto DiagSelect = [&]() -> std::optional<unsigned> {
+ if (!ParamTy->isPointerType())
+ return 0;
+ if (ParamTy->isFunctionPointerType())
+ return 1;
+ if (ParamTy->isVoidPointerType())
+ return 2;
+ return std::optional<unsigned>{};
+ }();
+ if (DiagSelect) {
+ S.Diag(TheCall->getBeginLoc(), diag::err_builtin_launder_invalid_arg)
+ << *DiagSelect << TheCall->getSourceRange();
+ return ExprError();
+ }
+
+ // We either have an incomplete class type, or we have a class template
+ // whose instantiation has not been forced. Example:
+ //
+ // template <class T> struct Foo { T value; };
+ // Foo<int> *p = nullptr;
+ // auto *d = __builtin_launder(p);
+ if (S.RequireCompleteType(TheCall->getBeginLoc(), ParamTy->getPointeeType(),
+ diag::err_incomplete_type))
+ return ExprError();
+
+ assert(ParamTy->getPointeeType()->isObjectType() &&
+ "Unhandled non-object pointer case");
+
+ InitializedEntity Entity =
+ InitializedEntity::InitializeParameter(S.Context, ParamTy, false);
+ ExprResult Arg =
+ S.PerformCopyInitialization(Entity, SourceLocation(), TheCall->getArg(0));
+ if (Arg.isInvalid())
+ return ExprError();
+ TheCall->setArg(0, Arg.get());
+
+ return TheCall;
+}
+
+static ExprResult BuiltinIsWithinLifetime(Sema &S, CallExpr *TheCall) {
+ if (S.checkArgCount(TheCall, 1))
+ return ExprError();
+
+ ExprResult Arg = S.DefaultFunctionArrayLvalueConversion(TheCall->getArg(0));
+ if (Arg.isInvalid())
+ return ExprError();
+ QualType ParamTy = Arg.get()->getType();
+ TheCall->setArg(0, Arg.get());
+ TheCall->setType(S.Context.BoolTy);
+
+ // Only accept pointers to objects as arguments, which should have object
+ // pointer or void pointer types.
+ if (const auto *PT = ParamTy->getAs<PointerType>()) {
+ // LWG4138: Function pointer types not allowed
+ if (PT->getPointeeType()->isFunctionType()) {
+ S.Diag(TheCall->getArg(0)->getExprLoc(),
+ diag::err_builtin_is_within_lifetime_invalid_arg)
+ << 1;
+ return ExprError();
+ }
+ // Disallow VLAs too since those shouldn't be able to
+ // be a template parameter for `std::is_within_lifetime`
+ if (PT->getPointeeType()->isVariableArrayType()) {
+ S.Diag(TheCall->getArg(0)->getExprLoc(), diag::err_vla_unsupported)
+ << 1 << "__builtin_is_within_lifetime";
+ return ExprError();
+ }
+ } else {
+ S.Diag(TheCall->getArg(0)->getExprLoc(),
+ diag::err_builtin_is_within_lifetime_invalid_arg)
+ << 0;
+ return ExprError();
+ }
+ return TheCall;
+}
+
+static ExprResult BuiltinTriviallyRelocate(Sema &S, CallExpr *TheCall) {
+ if (S.checkArgCount(TheCall, 3))
+ return ExprError();
+
+ QualType Dest = TheCall->getArg(0)->getType();
+ if (!Dest->isPointerType() || Dest.getCVRQualifiers() != 0) {
+ S.Diag(TheCall->getArg(0)->getExprLoc(),
+ diag::err_builtin_trivially_relocate_invalid_arg_type)
+ << /*a pointer*/ 0;
+ return ExprError();
+ }
+
+ QualType T = Dest->getPointeeType();
+ if (S.RequireCompleteType(TheCall->getBeginLoc(), T,
+ diag::err_incomplete_type))
+ return ExprError();
+
+ if (T.isConstQualified() || !S.IsCXXTriviallyRelocatableType(T) ||
+ T->isIncompleteArrayType()) {
+ S.Diag(TheCall->getArg(0)->getExprLoc(),
+ diag::err_builtin_trivially_relocate_invalid_arg_type)
+ << (T.isConstQualified() ? /*non-const*/ 1 : /*relocatable*/ 2);
+ return ExprError();
+ }
+
+ TheCall->setType(Dest);
+
+ QualType Src = TheCall->getArg(1)->getType();
+ if (Src.getCanonicalType() != Dest.getCanonicalType()) {
+ S.Diag(TheCall->getArg(1)->getExprLoc(),
+ diag::err_builtin_trivially_relocate_invalid_arg_type)
+ << /*the same*/ 3;
+ return ExprError();
+ }
+
+ Expr *SizeExpr = TheCall->getArg(2);
+ ExprResult Size = S.DefaultLvalueConversion(SizeExpr);
+ if (Size.isInvalid())
+ return ExprError();
+
+ Size = S.tryConvertExprToType(Size.get(), S.getASTContext().getSizeType());
+ if (Size.isInvalid())
+ return ExprError();
+ SizeExpr = Size.get();
+ TheCall->setArg(2, SizeExpr);
+
+ return TheCall;
+}
+
+// Emit an error and return true if the current object format type is in the
+// list of unsupported types.
+static bool CheckBuiltinTargetNotInUnsupported(
+ Sema &S, unsigned BuiltinID, CallExpr *TheCall,
+ ArrayRef<llvm::Triple::ObjectFormatType> UnsupportedObjectFormatTypes) {
+ llvm::Triple::ObjectFormatType CurObjFormat =
+ S.getASTContext().getTargetInfo().getTriple().getObjectFormat();
+ if (llvm::is_contained(UnsupportedObjectFormatTypes, CurObjFormat)) {
+ S.Diag(TheCall->getBeginLoc(), diag::err_builtin_target_unsupported)
+ << TheCall->getSourceRange();
+ return true;
+ }
+ return false;
+}
+
+// Emit an error and return true if the current architecture is not in the list
+// of supported architectures.
+static bool
+CheckBuiltinTargetInSupported(Sema &S, CallExpr *TheCall,
+ ArrayRef<llvm::Triple::ArchType> SupportedArchs) {
+ llvm::Triple::ArchType CurArch =
+ S.getASTContext().getTargetInfo().getTriple().getArch();
+ if (llvm::is_contained(SupportedArchs, CurArch))
+ return false;
+ S.Diag(TheCall->getBeginLoc(), diag::err_builtin_target_unsupported)
+ << TheCall->getSourceRange();
+ return true;
+}
+
+static void CheckNonNullArgument(Sema &S, const Expr *ArgExpr,
+ SourceLocation CallSiteLoc);
+
+bool Sema::CheckTSBuiltinFunctionCall(const TargetInfo &TI, unsigned BuiltinID,
+ CallExpr *TheCall) {
+ switch (TI.getTriple().getArch()) {
+ default:
+ // Some builtins don't require additional checking, so just consider these
+ // acceptable.
+ return false;
+ case llvm::Triple::arm:
+ case llvm::Triple::armeb:
+ case llvm::Triple::thumb:
+ case llvm::Triple::thumbeb:
+ return ARM().CheckARMBuiltinFunctionCall(TI, BuiltinID, TheCall);
+ case llvm::Triple::aarch64:
+ case llvm::Triple::aarch64_32:
+ case llvm::Triple::aarch64_be:
+ return ARM().CheckAArch64BuiltinFunctionCall(TI, BuiltinID, TheCall);
+ case llvm::Triple::bpfeb:
+ case llvm::Triple::bpfel:
+ return BPF().CheckBPFBuiltinFunctionCall(BuiltinID, TheCall);
+ case llvm::Triple::dxil:
+ return DirectX().CheckDirectXBuiltinFunctionCall(BuiltinID, TheCall);
+ case llvm::Triple::hexagon:
+ return Hexagon().CheckHexagonBuiltinFunctionCall(BuiltinID, TheCall);
+ case llvm::Triple::mips:
+ case llvm::Triple::mipsel:
+ case llvm::Triple::mips64:
+ case llvm::Triple::mips64el:
+ return MIPS().CheckMipsBuiltinFunctionCall(TI, BuiltinID, TheCall);
+ case llvm::Triple::spirv:
+ case llvm::Triple::spirv32:
+ case llvm::Triple::spirv64:
+ if (TI.getTriple().getOS() != llvm::Triple::OSType::AMDHSA)
+ return SPIRV().CheckSPIRVBuiltinFunctionCall(TI, BuiltinID, TheCall);
+ return false;
+ case llvm::Triple::systemz:
+ return SystemZ().CheckSystemZBuiltinFunctionCall(BuiltinID, TheCall);
+ case llvm::Triple::x86:
+ case llvm::Triple::x86_64:
+ return X86().CheckBuiltinFunctionCall(TI, BuiltinID, TheCall);
+ case llvm::Triple::ppc:
+ case llvm::Triple::ppcle:
+ case llvm::Triple::ppc64:
+ case llvm::Triple::ppc64le:
+ return PPC().CheckPPCBuiltinFunctionCall(TI, BuiltinID, TheCall);
+ case llvm::Triple::amdgcn:
+ return AMDGPU().CheckAMDGCNBuiltinFunctionCall(BuiltinID, TheCall);
+ case llvm::Triple::riscv32:
+ case llvm::Triple::riscv64:
+ case llvm::Triple::riscv32be:
+ case llvm::Triple::riscv64be:
+ return RISCV().CheckBuiltinFunctionCall(TI, BuiltinID, TheCall);
+ case llvm::Triple::loongarch32:
+ case llvm::Triple::loongarch64:
+ return LoongArch().CheckLoongArchBuiltinFunctionCall(TI, BuiltinID,
+ TheCall);
+ case llvm::Triple::wasm32:
+ case llvm::Triple::wasm64:
+ return Wasm().CheckWebAssemblyBuiltinFunctionCall(TI, BuiltinID, TheCall);
+ case llvm::Triple::nvptx:
+ case llvm::Triple::nvptx64:
+ return NVPTX().CheckNVPTXBuiltinFunctionCall(TI, BuiltinID, TheCall);
+ }
+}
+
+static bool isValidMathElementType(QualType T) {
+ return T->isDependentType() ||
+ (T->isRealType() && !T->isBooleanType() && !T->isEnumeralType());
+}
+
+// Check if \p Ty is a valid type for the elementwise math builtins. If it is
+// not a valid type, emit an error message and return true. Otherwise return
+// false.
+static bool
+checkMathBuiltinElementType(Sema &S, SourceLocation Loc, QualType ArgTy,
+ Sema::EltwiseBuiltinArgTyRestriction ArgTyRestr,
+ int ArgOrdinal) {
+ QualType EltTy = ArgTy;
+ if (auto *VecTy = EltTy->getAs<VectorType>())
+ EltTy = VecTy->getElementType();
+
+ switch (ArgTyRestr) {
+ case Sema::EltwiseBuiltinArgTyRestriction::None:
+ if (!ArgTy->getAs<VectorType>() && !isValidMathElementType(ArgTy)) {
+ return S.Diag(Loc, diag::err_builtin_invalid_arg_type)
+ << ArgOrdinal << /* vector */ 2 << /* integer */ 1 << /* fp */ 1
+ << ArgTy;
+ }
+ break;
+ case Sema::EltwiseBuiltinArgTyRestriction::FloatTy:
+ if (!EltTy->isRealFloatingType()) {
+ return S.Diag(Loc, diag::err_builtin_invalid_arg_type)
+ << ArgOrdinal << /* scalar or vector */ 5 << /* no int */ 0
+ << /* floating-point */ 1 << ArgTy;
+ }
+ break;
+ case Sema::EltwiseBuiltinArgTyRestriction::IntegerTy:
+ if (!EltTy->isIntegerType()) {
+ return S.Diag(Loc, diag::err_builtin_invalid_arg_type)
+ << ArgOrdinal << /* scalar or vector */ 5 << /* integer */ 1
+ << /* no fp */ 0 << ArgTy;
+ }
+ break;
+ case Sema::EltwiseBuiltinArgTyRestriction::SignedIntOrFloatTy:
+ if (!EltTy->isSignedIntegerType() && !EltTy->isRealFloatingType()) {
+ return S.Diag(Loc, diag::err_builtin_invalid_arg_type)
+ << 1 << /* scalar or vector */ 5 << /* signed int */ 2
+ << /* or fp */ 1 << ArgTy;
+ }
+ break;
+ }
+
+ return false;
+}
+
+/// BuiltinCpu{Supports|Is} - Handle __builtin_cpu_{supports|is}(char *).
+/// This checks that the target supports the builtin and that the string
+/// argument is constant and valid.
+static bool BuiltinCpu(Sema &S, const TargetInfo &TI, CallExpr *TheCall,
+ const TargetInfo *AuxTI, unsigned BuiltinID) {
+ assert((BuiltinID == Builtin::BI__builtin_cpu_supports ||
+ BuiltinID == Builtin::BI__builtin_cpu_is) &&
+ "Expecting __builtin_cpu_...");
+
+ bool IsCPUSupports = BuiltinID == Builtin::BI__builtin_cpu_supports;
+ const TargetInfo *TheTI = &TI;
+ auto SupportsBI = [=](const TargetInfo *TInfo) {
+ return TInfo && ((IsCPUSupports && TInfo->supportsCpuSupports()) ||
+ (!IsCPUSupports && TInfo->supportsCpuIs()));
+ };
+ if (!SupportsBI(&TI) && SupportsBI(AuxTI))
+ TheTI = AuxTI;
+
+ if ((!IsCPUSupports && !TheTI->supportsCpuIs()) ||
+ (IsCPUSupports && !TheTI->supportsCpuSupports()))
+ return S.Diag(TheCall->getBeginLoc(),
+ TI.getTriple().isOSAIX()
+ ? diag::err_builtin_aix_os_unsupported
+ : diag::err_builtin_target_unsupported)
+ << SourceRange(TheCall->getBeginLoc(), TheCall->getEndLoc());
+
+ Expr *Arg = TheCall->getArg(0)->IgnoreParenImpCasts();
+ // Check if the argument is a string literal.
+ if (!isa<StringLiteral>(Arg))
+ return S.Diag(TheCall->getBeginLoc(), diag::err_expr_not_string_literal)
+ << Arg->getSourceRange();
+
+ // Check the contents of the string.
+ StringRef Feature = cast<StringLiteral>(Arg)->getString();
+ if (IsCPUSupports && !TheTI->validateCpuSupports(Feature)) {
+ S.Diag(TheCall->getBeginLoc(), diag::warn_invalid_cpu_supports)
+ << Arg->getSourceRange();
+ return false;
+ }
+ if (!IsCPUSupports && !TheTI->validateCpuIs(Feature))
+ return S.Diag(TheCall->getBeginLoc(), diag::err_invalid_cpu_is)
+ << Arg->getSourceRange();
+ return false;
+}
+
+/// Checks that __builtin_bswapg was called with a single argument, which is an
+/// unsigned integer, and overrides the return value type to the integer type.
+static bool BuiltinBswapg(Sema &S, CallExpr *TheCall) {
+ if (S.checkArgCount(TheCall, 1))
+ return true;
+ ExprResult ArgRes = S.DefaultLvalueConversion(TheCall->getArg(0));
+ if (ArgRes.isInvalid())
+ return true;
+
+ Expr *Arg = ArgRes.get();
+ TheCall->setArg(0, Arg);
+ if (Arg->isTypeDependent())
+ return false;
+
+ QualType ArgTy = Arg->getType();
+
+ if (!ArgTy->isIntegerType()) {
+ S.Diag(Arg->getBeginLoc(), diag::err_builtin_invalid_arg_type)
+ << 1 << /*scalar=*/1 << /*unsigned integer=*/1 << /*floating point=*/0
+ << ArgTy;
+ return true;
+ }
+ if (const auto *BT = dyn_cast<BitIntType>(ArgTy)) {
+ if (BT->getNumBits() % 16 != 0 && BT->getNumBits() != 8 &&
+ BT->getNumBits() != 1) {
+ S.Diag(Arg->getBeginLoc(), diag::err_bswapg_invalid_bit_width)
+ << ArgTy << BT->getNumBits();
+ return true;
+ }
+ }
+ TheCall->setType(ArgTy);
+ return false;
+}
+
+/// Checks that __builtin_popcountg was called with a single argument, which is
+/// an unsigned integer.
+static bool BuiltinPopcountg(Sema &S, CallExpr *TheCall) {
+ if (S.checkArgCount(TheCall, 1))
+ return true;
+
+ ExprResult ArgRes = S.DefaultLvalueConversion(TheCall->getArg(0));
+ if (ArgRes.isInvalid())
+ return true;
+
+ Expr *Arg = ArgRes.get();
+ TheCall->setArg(0, Arg);
+
+ QualType ArgTy = Arg->getType();
+
+ if (!ArgTy->isUnsignedIntegerType() && !ArgTy->isExtVectorBoolType()) {
+ S.Diag(Arg->getBeginLoc(), diag::err_builtin_invalid_arg_type)
+ << 1 << /* scalar */ 1 << /* unsigned integer ty */ 3 << /* no fp */ 0
+ << ArgTy;
+ return true;
+ }
+ return false;
+}
+
+/// Checks that __builtin_{clzg,ctzg} was called with a first argument, which is
+/// an unsigned integer, and an optional second argument, which is promoted to
+/// an 'int'.
+static bool BuiltinCountZeroBitsGeneric(Sema &S, CallExpr *TheCall) {
+ if (S.checkArgCountRange(TheCall, 1, 2))
+ return true;
+
+ ExprResult Arg0Res = S.DefaultLvalueConversion(TheCall->getArg(0));
+ if (Arg0Res.isInvalid())
+ return true;
+
+ Expr *Arg0 = Arg0Res.get();
+ TheCall->setArg(0, Arg0);
+
+ QualType Arg0Ty = Arg0->getType();
+
+ if (!Arg0Ty->isUnsignedIntegerType() && !Arg0Ty->isExtVectorBoolType()) {
+ S.Diag(Arg0->getBeginLoc(), diag::err_builtin_invalid_arg_type)
+ << 1 << /* scalar */ 1 << /* unsigned integer ty */ 3 << /* no fp */ 0
+ << Arg0Ty;
+ return true;
+ }
+
+ if (TheCall->getNumArgs() > 1) {
+ ExprResult Arg1Res = S.UsualUnaryConversions(TheCall->getArg(1));
+ if (Arg1Res.isInvalid())
+ return true;
+
+ Expr *Arg1 = Arg1Res.get();
+ TheCall->setArg(1, Arg1);
+
+ QualType Arg1Ty = Arg1->getType();
+
+ if (!Arg1Ty->isSpecificBuiltinType(BuiltinType::Int)) {
+ S.Diag(Arg1->getBeginLoc(), diag::err_builtin_invalid_arg_type)
+ << 2 << /* scalar */ 1 << /* 'int' ty */ 4 << /* no fp */ 0 << Arg1Ty;
+ return true;
+ }
+ }
+
+ return false;
+}
+
+class RotateIntegerConverter : public Sema::ContextualImplicitConverter {
+ unsigned ArgIndex;
+ bool OnlyUnsigned;
+
+ Sema::SemaDiagnosticBuilder emitError(Sema &S, SourceLocation Loc,
+ QualType T) {
+ return S.Diag(Loc, diag::err_builtin_invalid_arg_type)
+ << ArgIndex << /*scalar*/ 1
+ << (OnlyUnsigned ? /*unsigned integer*/ 3 : /*integer*/ 1)
+ << /*no fp*/ 0 << T;
+ }
+
+public:
+ RotateIntegerConverter(unsigned ArgIndex, bool OnlyUnsigned)
+ : ContextualImplicitConverter(/*Suppress=*/false,
+ /*SuppressConversion=*/true),
+ ArgIndex(ArgIndex), OnlyUnsigned(OnlyUnsigned) {}
+
+ bool match(QualType T) override {
+ return OnlyUnsigned ? T->isUnsignedIntegerType() : T->isIntegerType();
+ }
+
+ Sema::SemaDiagnosticBuilder diagnoseNoMatch(Sema &S, SourceLocation Loc,
+ QualType T) override {
+ return emitError(S, Loc, T);
+ }
+
+ Sema::SemaDiagnosticBuilder diagnoseIncomplete(Sema &S, SourceLocation Loc,
+ QualType T) override {
+ return emitError(S, Loc, T);
+ }
+
+ Sema::SemaDiagnosticBuilder diagnoseExplicitConv(Sema &S, SourceLocation Loc,
+ QualType T,
+ QualType ConvTy) override {
+ return emitError(S, Loc, T);
+ }
+
+ Sema::SemaDiagnosticBuilder noteExplicitConv(Sema &S, CXXConversionDecl *Conv,
+ QualType ConvTy) override {
+ return S.Diag(Conv->getLocation(), diag::note_conv_function_declared_at);
+ }
+
+ Sema::SemaDiagnosticBuilder diagnoseAmbiguous(Sema &S, SourceLocation Loc,
+ QualType T) override {
+ return emitError(S, Loc, T);
+ }
+
+ Sema::SemaDiagnosticBuilder noteAmbiguous(Sema &S, CXXConversionDecl *Conv,
+ QualType ConvTy) override {
+ return S.Diag(Conv->getLocation(), diag::note_conv_function_declared_at);
+ }
+
+ Sema::SemaDiagnosticBuilder diagnoseConversion(Sema &S, SourceLocation Loc,
+ QualType T,
+ QualType ConvTy) override {
+ llvm_unreachable("conversion functions are permitted");
+ }
+};
+
+/// Checks that __builtin_stdc_rotate_{left,right} was called with two
+/// arguments, that the first argument is an unsigned integer type, and that
+/// the second argument is an integer type.
+static bool BuiltinRotateGeneric(Sema &S, CallExpr *TheCall) {
+ if (S.checkArgCount(TheCall, 2))
+ return true;
+
+ // First argument (value to rotate) must be unsigned integer type.
+ RotateIntegerConverter Arg0Converter(1, /*OnlyUnsigned=*/true);
+ ExprResult Arg0Res = S.PerformContextualImplicitConversion(
+ TheCall->getArg(0)->getBeginLoc(), TheCall->getArg(0), Arg0Converter);
+ if (Arg0Res.isInvalid())
+ return true;
+
+ Expr *Arg0 = Arg0Res.get();
+ TheCall->setArg(0, Arg0);
+
+ QualType Arg0Ty = Arg0->getType();
+ if (!Arg0Ty->isUnsignedIntegerType())
+ return true;
+
+ // Second argument (rotation count) must be integer type.
+ RotateIntegerConverter Arg1Converter(2, /*OnlyUnsigned=*/false);
+ ExprResult Arg1Res = S.PerformContextualImplicitConversion(
+ TheCall->getArg(1)->getBeginLoc(), TheCall->getArg(1), Arg1Converter);
+ if (Arg1Res.isInvalid())
+ return true;
+
+ Expr *Arg1 = Arg1Res.get();
+ TheCall->setArg(1, Arg1);
+
+ QualType Arg1Ty = Arg1->getType();
+ if (!Arg1Ty->isIntegerType())
+ return true;
+
+ TheCall->setType(Arg0Ty);
+ return false;
+}
+
+static bool CheckMaskedBuiltinArgs(Sema &S, Expr *MaskArg, Expr *PtrArg,
+ unsigned Pos, bool AllowConst,
+ bool AllowAS) {
+ QualType MaskTy = MaskArg->getType();
+ if (!MaskTy->isExtVectorBoolType())
+ return S.Diag(MaskArg->getBeginLoc(), diag::err_builtin_invalid_arg_type)
+ << 1 << /* vector of */ 4 << /* booleans */ 6 << /* no fp */ 0
+ << MaskTy;
+
+ QualType PtrTy = PtrArg->getType();
+ if (!PtrTy->isPointerType() || PtrTy->getPointeeType()->isVectorType())
+ return S.Diag(PtrArg->getExprLoc(), diag::err_vec_masked_load_store_ptr)
+ << Pos << "scalar pointer";
+
+ QualType PointeeTy = PtrTy->getPointeeType();
+ if (PointeeTy.isVolatileQualified() || PointeeTy->isAtomicType() ||
+ (!AllowConst && PointeeTy.isConstQualified()) ||
+ (!AllowAS && PointeeTy.hasAddressSpace())) {
+ QualType Target =
+ S.Context.getPointerType(PointeeTy.getAtomicUnqualifiedType());
+ return S.Diag(PtrArg->getExprLoc(),
+ diag::err_typecheck_convert_incompatible)
+ << PtrTy << Target << /*different qualifiers=*/5
+ << /*qualifier difference=*/0 << /*parameter mismatch=*/3 << 2
+ << PtrTy << Target;
+ }
+ return false;
+}
+
+static bool ConvertMaskedBuiltinArgs(Sema &S, CallExpr *TheCall) {
+ bool TypeDependent = false;
+ for (unsigned Arg = 0, E = TheCall->getNumArgs(); Arg != E; ++Arg) {
+ ExprResult Converted =
+ S.DefaultFunctionArrayLvalueConversion(TheCall->getArg(Arg));
+ if (Converted.isInvalid())
+ return true;
+ TheCall->setArg(Arg, Converted.get());
+ TypeDependent |= Converted.get()->isTypeDependent();
+ }
+
+ if (TypeDependent)
+ TheCall->setType(S.Context.DependentTy);
+ return false;
+}
+
+static ExprResult BuiltinMaskedLoad(Sema &S, CallExpr *TheCall) {
+ if (S.checkArgCountRange(TheCall, 2, 3))
+ return ExprError();
+
+ if (ConvertMaskedBuiltinArgs(S, TheCall))
+ return ExprError();
+
+ Expr *MaskArg = TheCall->getArg(0);
+ Expr *PtrArg = TheCall->getArg(1);
+ if (TheCall->isTypeDependent())
+ return TheCall;
+
+ if (CheckMaskedBuiltinArgs(S, MaskArg, PtrArg, 2, /*AllowConst=*/true,
+ TheCall->getBuiltinCallee() ==
+ Builtin::BI__builtin_masked_load))
+ return ExprError();
+
+ QualType MaskTy = MaskArg->getType();
+ QualType PtrTy = PtrArg->getType();
+ QualType PointeeTy = PtrTy->getPointeeType();
+ const VectorType *MaskVecTy = MaskTy->getAs<VectorType>();
+
+ QualType RetTy = S.Context.getExtVectorType(PointeeTy.getUnqualifiedType(),
+ MaskVecTy->getNumElements());
+ if (TheCall->getNumArgs() == 3) {
+ Expr *PassThruArg = TheCall->getArg(2);
+ QualType PassThruTy = PassThruArg->getType();
+ if (!S.Context.hasSameType(PassThruTy, RetTy))
+ return S.Diag(PtrArg->getExprLoc(), diag::err_vec_masked_load_store_ptr)
+ << /* third argument */ 3 << RetTy;
+ }
+
+ TheCall->setType(RetTy);
+ return TheCall;
+}
+
+static ExprResult BuiltinMaskedStore(Sema &S, CallExpr *TheCall) {
+ if (S.checkArgCount(TheCall, 3))
+ return ExprError();
+
+ if (ConvertMaskedBuiltinArgs(S, TheCall))
+ return ExprError();
+
+ Expr *MaskArg = TheCall->getArg(0);
+ Expr *ValArg = TheCall->getArg(1);
+ Expr *PtrArg = TheCall->getArg(2);
+ if (TheCall->isTypeDependent())
+ return TheCall;
+
+ if (CheckMaskedBuiltinArgs(S, MaskArg, PtrArg, 3, /*AllowConst=*/false,
+ TheCall->getBuiltinCallee() ==
+ Builtin::BI__builtin_masked_store))
+ return ExprError();
+
+ QualType MaskTy = MaskArg->getType();
+ QualType PtrTy = PtrArg->getType();
+ QualType ValTy = ValArg->getType();
+ if (!ValTy->isVectorType())
+ return ExprError(
+ S.Diag(ValArg->getExprLoc(), diag::err_vec_masked_load_store_ptr)
+ << 2 << "vector");
+
+ QualType PointeeTy = PtrTy->getPointeeType();
+ const VectorType *MaskVecTy = MaskTy->getAs<VectorType>();
+ QualType MemoryTy = S.Context.getExtVectorType(PointeeTy.getUnqualifiedType(),
+ MaskVecTy->getNumElements());
+ if (!S.Context.hasSameType(ValTy.getUnqualifiedType(),
+ MemoryTy.getUnqualifiedType()))
+ return ExprError(S.Diag(TheCall->getBeginLoc(),
+ diag::err_vec_builtin_incompatible_vector)
+ << TheCall->getDirectCallee() << /*isMorethantwoArgs*/ 2
+ << SourceRange(TheCall->getArg(1)->getBeginLoc(),
+ TheCall->getArg(1)->getEndLoc()));
+
+ TheCall->setType(S.Context.VoidTy);
+ return TheCall;
+}
+
+static ExprResult BuiltinMaskedGather(Sema &S, CallExpr *TheCall) {
+ if (S.checkArgCountRange(TheCall, 3, 4))
+ return ExprError();
+
+ if (ConvertMaskedBuiltinArgs(S, TheCall))
+ return ExprError();
+
+ Expr *MaskArg = TheCall->getArg(0);
+ Expr *IdxArg = TheCall->getArg(1);
+ Expr *PtrArg = TheCall->getArg(2);
+ if (TheCall->isTypeDependent())
+ return TheCall;
+
+ if (CheckMaskedBuiltinArgs(S, MaskArg, PtrArg, 3, /*AllowConst=*/true,
+ /*AllowAS=*/true))
+ return ExprError();
+
+ QualType IdxTy = IdxArg->getType();
+ const VectorType *IdxVecTy = IdxTy->getAs<VectorType>();
+ if (!IdxTy->isExtVectorType() || !IdxVecTy->getElementType()->isIntegerType())
+ return S.Diag(MaskArg->getBeginLoc(), diag::err_builtin_invalid_arg_type)
+ << 1 << /* vector of */ 4 << /* integer */ 1 << /* no fp */ 0
+ << IdxTy;
+
+ QualType MaskTy = MaskArg->getType();
+ QualType PtrTy = PtrArg->getType();
+ QualType PointeeTy = PtrTy->getPointeeType();
+ const VectorType *MaskVecTy = MaskTy->getAs<VectorType>();
+ if (MaskVecTy->getNumElements() != IdxVecTy->getNumElements())
+ return ExprError(
+ S.Diag(TheCall->getBeginLoc(), diag::err_vec_masked_load_store_size)
+ << S.getASTContext().BuiltinInfo.getQuotedName(
+ TheCall->getBuiltinCallee())
+ << MaskTy << IdxTy);
+
+ QualType RetTy = S.Context.getExtVectorType(PointeeTy.getUnqualifiedType(),
+ MaskVecTy->getNumElements());
+ if (TheCall->getNumArgs() == 4) {
+ Expr *PassThruArg = TheCall->getArg(3);
+ QualType PassThruTy = PassThruArg->getType();
+ if (!S.Context.hasSameType(PassThruTy, RetTy))
+ return S.Diag(PassThruArg->getExprLoc(),
+ diag::err_vec_masked_load_store_ptr)
+ << /* fourth argument */ 4 << RetTy;
+ }
+
+ TheCall->setType(RetTy);
+ return TheCall;
+}
+
+static ExprResult BuiltinMaskedScatter(Sema &S, CallExpr *TheCall) {
+ if (S.checkArgCount(TheCall, 4))
+ return ExprError();
+
+ if (ConvertMaskedBuiltinArgs(S, TheCall))
+ return ExprError();
+
+ Expr *MaskArg = TheCall->getArg(0);
+ Expr *IdxArg = TheCall->getArg(1);
+ Expr *ValArg = TheCall->getArg(2);
+ Expr *PtrArg = TheCall->getArg(3);
+ if (TheCall->isTypeDependent())
+ return TheCall;
+
+ if (CheckMaskedBuiltinArgs(S, MaskArg, PtrArg, 4, /*AllowConst=*/false,
+ /*AllowAS=*/true))
+ return ExprError();
+
+ QualType IdxTy = IdxArg->getType();
+ const VectorType *IdxVecTy = IdxTy->getAs<VectorType>();
+ if (!IdxTy->isExtVectorType() || !IdxVecTy->getElementType()->isIntegerType())
+ return S.Diag(MaskArg->getBeginLoc(), diag::err_builtin_invalid_arg_type)
+ << 2 << /* vector of */ 4 << /* integer */ 1 << /* no fp */ 0
+ << IdxTy;
+
+ QualType ValTy = ValArg->getType();
+ QualType MaskTy = MaskArg->getType();
+ QualType PtrTy = PtrArg->getType();
+ QualType PointeeTy = PtrTy->getPointeeType();
+
+ const VectorType *MaskVecTy = MaskTy->castAs<VectorType>();
+ const VectorType *ValVecTy = ValTy->castAs<VectorType>();
+ if (MaskVecTy->getNumElements() != IdxVecTy->getNumElements())
+ return ExprError(
+ S.Diag(TheCall->getBeginLoc(), diag::err_vec_masked_load_store_size)
+ << S.getASTContext().BuiltinInfo.getQuotedName(
+ TheCall->getBuiltinCallee())
+ << MaskTy << IdxTy);
+ if (MaskVecTy->getNumElements() != ValVecTy->getNumElements())
+ return ExprError(
+ S.Diag(TheCall->getBeginLoc(), diag::err_vec_masked_load_store_size)
+ << S.getASTContext().BuiltinInfo.getQuotedName(
+ TheCall->getBuiltinCallee())
+ << MaskTy << ValTy);
+
+ QualType ArgTy = S.Context.getExtVectorType(PointeeTy.getUnqualifiedType(),
+ MaskVecTy->getNumElements());
+ if (!S.Context.hasSameType(ValTy.getUnqualifiedType(), ArgTy))
+ return ExprError(S.Diag(TheCall->getBeginLoc(),
+ diag::err_vec_builtin_incompatible_vector)
+ << TheCall->getDirectCallee() << /*isMoreThanTwoArgs*/ 2
+ << SourceRange(TheCall->getArg(1)->getBeginLoc(),
+ TheCall->getArg(1)->getEndLoc()));
+
+ TheCall->setType(S.Context.VoidTy);
+ return TheCall;
+}
+
+static ExprResult BuiltinInvoke(Sema &S, CallExpr *TheCall) {
+ SourceLocation Loc = TheCall->getBeginLoc();
+ MutableArrayRef Args(TheCall->getArgs(), TheCall->getNumArgs());
+ assert(llvm::none_of(Args, [](Expr *Arg) { return Arg->isTypeDependent(); }));
+
+ if (Args.size() == 0) {
+ S.Diag(TheCall->getBeginLoc(),
+ diag::err_typecheck_call_too_few_args_at_least)
+ << /*callee_type=*/0 << /*min_arg_count=*/1 << /*actual_arg_count=*/0
+ << /*is_non_object=*/0 << TheCall->getSourceRange();
+ return ExprError();
+ }
+
+ QualType FuncT = Args[0]->getType();
+
+ if (const auto *MPT = FuncT->getAs<MemberPointerType>()) {
+ if (Args.size() < 2) {
+ S.Diag(TheCall->getBeginLoc(),
+ diag::err_typecheck_call_too_few_args_at_least)
+ << /*callee_type=*/0 << /*min_arg_count=*/2 << /*actual_arg_count=*/1
+ << /*is_non_object=*/0 << TheCall->getSourceRange();
+ return ExprError();
+ }
+
+ const Type *MemPtrClass = MPT->getQualifier().getAsType();
+ QualType ObjectT = Args[1]->getType();
+
+ if (MPT->isMemberDataPointer() && S.checkArgCount(TheCall, 2))
+ return ExprError();
+
+ ExprResult ObjectArg = [&]() -> ExprResult {
+ // (1.1): (t1.*f)(t2, ..., tN) when f is a pointer to a member function of
+ // a class T and is_same_v<T, remove_cvref_t<decltype(t1)>> ||
+ // is_base_of_v<T, remove_cvref_t<decltype(t1)>> is true;
+ // (1.4): t1.*f when N=1 and f is a pointer to data member of a class T
+ // and is_same_v<T, remove_cvref_t<decltype(t1)>> ||
+ // is_base_of_v<T, remove_cvref_t<decltype(t1)>> is true;
+ if (S.Context.hasSameType(QualType(MemPtrClass, 0),
+ S.BuiltinRemoveCVRef(ObjectT, Loc)) ||
+ S.BuiltinIsBaseOf(Args[1]->getBeginLoc(), QualType(MemPtrClass, 0),
+ S.BuiltinRemoveCVRef(ObjectT, Loc))) {
+ return Args[1];
+ }
+
+ // (t1.get().*f)(t2, ..., tN) when f is a pointer to a member function of
+ // a class T and remove_cvref_t<decltype(t1)> is a specialization of
+ // reference_wrapper;
+ if (const auto *RD = ObjectT->getAsCXXRecordDecl()) {
+ if (RD->isInStdNamespace() &&
+ RD->getDeclName().getAsString() == "reference_wrapper") {
+ CXXScopeSpec SS;
+ IdentifierInfo *GetName = &S.Context.Idents.get("get");
+ UnqualifiedId GetID;
+ GetID.setIdentifier(GetName, Loc);
+
+ ExprResult MemExpr = S.ActOnMemberAccessExpr(
+ S.getCurScope(), Args[1], Loc, tok::period, SS,
+ /*TemplateKWLoc=*/SourceLocation(), GetID, nullptr);
+
+ if (MemExpr.isInvalid())
+ return ExprError();
+
+ return S.ActOnCallExpr(S.getCurScope(), MemExpr.get(), Loc, {}, Loc);
+ }
+ }
+
+ // ((*t1).*f)(t2, ..., tN) when f is a pointer to a member function of a
+ // class T and t1 does not satisfy the previous two items;
+
+ return S.ActOnUnaryOp(S.getCurScope(), Loc, tok::star, Args[1]);
+ }();
+
+ if (ObjectArg.isInvalid())
+ return ExprError();
+
+ ExprResult BinOp = S.ActOnBinOp(S.getCurScope(), TheCall->getBeginLoc(),
+ tok::periodstar, ObjectArg.get(), Args[0]);
+ if (BinOp.isInvalid())
+ return ExprError();
+
+ if (MPT->isMemberDataPointer())
+ return BinOp;
+
+ auto *MemCall = new (S.Context)
+ ParenExpr(SourceLocation(), SourceLocation(), BinOp.get());
+
+ return S.ActOnCallExpr(S.getCurScope(), MemCall, TheCall->getBeginLoc(),
+ Args.drop_front(2), TheCall->getRParenLoc());
+ }
+ return S.ActOnCallExpr(S.getCurScope(), Args.front(), TheCall->getBeginLoc(),
+ Args.drop_front(), TheCall->getRParenLoc());
+}
+
+// Performs a similar job to Sema::UsualUnaryConversions, but without any
+// implicit promotion of integral/enumeration types.
+static ExprResult BuiltinVectorMathConversions(Sema &S, Expr *E) {
+ // First, convert to an r-value.
+ ExprResult Res = S.DefaultFunctionArrayLvalueConversion(E);
+ if (Res.isInvalid())
+ return ExprError();
+
+ // Promote floating-point types.
+ return S.UsualUnaryFPConversions(Res.get());
+}
+
+ExprResult
+Sema::CheckBuiltinFunctionCall(FunctionDecl *FDecl, unsigned BuiltinID,
+ CallExpr *TheCall) {
+ ExprResult TheCallResult(TheCall);
+
+ // Find out if any arguments are required to be integer constant expressions.
+ unsigned ICEArguments = 0;
+ ASTContext::GetBuiltinTypeError Error;
+ Context.GetBuiltinType(BuiltinID, Error, &ICEArguments);
+ if (Error != ASTContext::GE_None)
+ ICEArguments = 0; // Don't diagnose previously diagnosed errors.
+
+ // If any arguments are required to be ICE's, check and diagnose.
+ for (unsigned ArgNo = 0; ICEArguments != 0; ++ArgNo) {
+ // Skip arguments not required to be ICE's.
+ if ((ICEArguments & (1 << ArgNo)) == 0) continue;
+
+ llvm::APSInt Result;
+ // If we don't have enough arguments, continue so we can issue better
+ // diagnostic in checkArgCount(...)
+ if (ArgNo < TheCall->getNumArgs() &&
+ BuiltinConstantArg(TheCall, ArgNo, Result))
+ return true;
+ ICEArguments &= ~(1 << ArgNo);
+ }
+
+ FPOptions FPO;
+ switch (BuiltinID) {
+ case Builtin::BI__builtin_cpu_supports:
+ case Builtin::BI__builtin_cpu_is:
+ if (BuiltinCpu(*this, Context.getTargetInfo(), TheCall,
+ Context.getAuxTargetInfo(), BuiltinID))
+ return ExprError();
+ break;
+ case Builtin::BI__builtin_cpu_init:
+ if (!Context.getTargetInfo().supportsCpuInit()) {
+ Diag(TheCall->getBeginLoc(), diag::err_builtin_target_unsupported)
+ << SourceRange(TheCall->getBeginLoc(), TheCall->getEndLoc());
+ return ExprError();
+ }
+ break;
+ case Builtin::BI__builtin___CFStringMakeConstantString:
+ // CFStringMakeConstantString is currently not implemented for GOFF (i.e.,
+ // on z/OS) and for XCOFF (i.e., on AIX). Emit unsupported
+ if (CheckBuiltinTargetNotInUnsupported(
+ *this, BuiltinID, TheCall,
+ {llvm::Triple::GOFF, llvm::Triple::XCOFF}))
+ return ExprError();
+ assert(TheCall->getNumArgs() == 1 &&
+ "Wrong # arguments to builtin CFStringMakeConstantString");
+ if (ObjC().CheckObjCString(TheCall->getArg(0)))
+ return ExprError();
+ break;
+ case Builtin::BI__builtin_ms_va_start:
+ case Builtin::BI__builtin_stdarg_start:
+ case Builtin::BI__builtin_va_start:
+ case Builtin::BI__builtin_c23_va_start:
+ if (BuiltinVAStart(BuiltinID, TheCall))
+ return ExprError();
+ break;
+ case Builtin::BI__va_start: {
+ switch (Context.getTargetInfo().getTriple().getArch()) {
+ case llvm::Triple::aarch64:
+ case llvm::Triple::arm:
+ case llvm::Triple::thumb:
+ if (BuiltinVAStartARMMicrosoft(TheCall))
+ return ExprError();
+ break;
+ default:
+ if (BuiltinVAStart(BuiltinID, TheCall))
+ return ExprError();
+ break;
+ }
+ break;
+ }
+
+ // The acquire, release, and no fence variants are ARM and AArch64 only.
+ case Builtin::BI_interlockedbittestandset_acq:
+ case Builtin::BI_interlockedbittestandset_rel:
+ case Builtin::BI_interlockedbittestandset_nf:
+ case Builtin::BI_interlockedbittestandreset_acq:
+ case Builtin::BI_interlockedbittestandreset_rel:
+ case Builtin::BI_interlockedbittestandreset_nf:
+ if (CheckBuiltinTargetInSupported(
+ *this, TheCall,
+ {llvm::Triple::arm, llvm::Triple::thumb, llvm::Triple::aarch64}))
+ return ExprError();
+ break;
+
+ // The 64-bit bittest variants are x64, ARM, and AArch64 only.
+ case Builtin::BI_bittest64:
+ case Builtin::BI_bittestandcomplement64:
+ case Builtin::BI_bittestandreset64:
+ case Builtin::BI_bittestandset64:
+ case Builtin::BI_interlockedbittestandreset64:
+ case Builtin::BI_interlockedbittestandset64:
+ if (CheckBuiltinTargetInSupported(
+ *this, TheCall,
+ {llvm::Triple::x86_64, llvm::Triple::arm, llvm::Triple::thumb,
+ llvm::Triple::aarch64, llvm::Triple::amdgcn}))
+ return ExprError();
+ break;
+
+ // The 64-bit acquire, release, and no fence variants are AArch64 only.
+ case Builtin::BI_interlockedbittestandreset64_acq:
+ case Builtin::BI_interlockedbittestandreset64_rel:
+ case Builtin::BI_interlockedbittestandreset64_nf:
+ case Builtin::BI_interlockedbittestandset64_acq:
+ case Builtin::BI_interlockedbittestandset64_rel:
+ case Builtin::BI_interlockedbittestandset64_nf:
+ if (CheckBuiltinTargetInSupported(*this, TheCall, {llvm::Triple::aarch64}))
+ return ExprError();
+ break;
+
+ case Builtin::BI__builtin_set_flt_rounds:
+ if (CheckBuiltinTargetInSupported(
+ *this, TheCall,
+ {llvm::Triple::x86, llvm::Triple::x86_64, llvm::Triple::arm,
+ llvm::Triple::thumb, llvm::Triple::aarch64, llvm::Triple::amdgcn,
+ llvm::Triple::ppc, llvm::Triple::ppc64, llvm::Triple::ppcle,
+ llvm::Triple::ppc64le}))
+ return ExprError();
+ break;
+
+ case Builtin::BI__builtin_isgreater:
+ case Builtin::BI__builtin_isgreaterequal:
+ case Builtin::BI__builtin_isless:
+ case Builtin::BI__builtin_islessequal:
+ case Builtin::BI__builtin_islessgreater:
+ case Builtin::BI__builtin_isunordered:
+ if (BuiltinUnorderedCompare(TheCall, BuiltinID))
+ return ExprError();
+ break;
+ case Builtin::BI__builtin_fpclassify:
+ if (BuiltinFPClassification(TheCall, 6, BuiltinID))
+ return ExprError();
+ break;
+ case Builtin::BI__builtin_isfpclass:
+ if (BuiltinFPClassification(TheCall, 2, BuiltinID))
+ return ExprError();
+ break;
+ case Builtin::BI__builtin_isfinite:
+ case Builtin::BI__builtin_isinf:
+ case Builtin::BI__builtin_isinf_sign:
+ case Builtin::BI__builtin_isnan:
+ case Builtin::BI__builtin_issignaling:
+ case Builtin::BI__builtin_isnormal:
+ case Builtin::BI__builtin_issubnormal:
+ case Builtin::BI__builtin_iszero:
+ case Builtin::BI__builtin_signbit:
+ case Builtin::BI__builtin_signbitf:
+ case Builtin::BI__builtin_signbitl:
+ if (BuiltinFPClassification(TheCall, 1, BuiltinID))
+ return ExprError();
+ break;
+ case Builtin::BI__builtin_shufflevector:
+ return BuiltinShuffleVector(TheCall);
+ // TheCall will be freed by the smart pointer here, but that's fine, since
+ // BuiltinShuffleVector guts it, but then doesn't release it.
+ case Builtin::BI__builtin_masked_load:
+ case Builtin::BI__builtin_masked_expand_load:
+ return BuiltinMaskedLoad(*this, TheCall);
+ case Builtin::BI__builtin_masked_store:
+ case Builtin::BI__builtin_masked_compress_store:
+ return BuiltinMaskedStore(*this, TheCall);
+ case Builtin::BI__builtin_masked_gather:
+ return BuiltinMaskedGather(*this, TheCall);
+ case Builtin::BI__builtin_masked_scatter:
+ return BuiltinMaskedScatter(*this, TheCall);
+ case Builtin::BI__builtin_invoke:
+ return BuiltinInvoke(*this, TheCall);
+ case Builtin::BI__builtin_prefetch:
+ if (BuiltinPrefetch(TheCall))
+ return ExprError();
+ break;
+ case Builtin::BI__builtin_alloca_with_align:
+ case Builtin::BI__builtin_alloca_with_align_uninitialized:
+ if (BuiltinAllocaWithAlign(TheCall))
+ return ExprError();
+ [[fallthrough]];
+ case Builtin::BI__builtin_alloca:
+ case Builtin::BI__builtin_alloca_uninitialized:
+ Diag(TheCall->getBeginLoc(), diag::warn_alloca)
+ << TheCall->getDirectCallee();
+ if (getLangOpts().OpenCL) {
+ builtinAllocaAddrSpace(*this, TheCall);
+ }
+ break;
+ case Builtin::BI__builtin_infer_alloc_token:
+ if (checkBuiltinInferAllocToken(*this, TheCall))
+ return ExprError();
+ break;
+ case Builtin::BI__arithmetic_fence:
+ if (BuiltinArithmeticFence(TheCall))
+ return ExprError();
+ break;
+ case Builtin::BI__assume:
+ case Builtin::BI__builtin_assume:
+ if (BuiltinAssume(TheCall))
+ return ExprError();
+ break;
+ case Builtin::BI__builtin_assume_aligned:
+ if (BuiltinAssumeAligned(TheCall))
+ return ExprError();
+ break;
+ case Builtin::BI__builtin_dynamic_object_size:
+ case Builtin::BI__builtin_object_size:
+ if (BuiltinConstantArgRange(TheCall, 1, 0, 3))
+ return ExprError();
+ break;
+ case Builtin::BI__builtin_longjmp:
+ if (BuiltinLongjmp(TheCall))
+ return ExprError();
+ break;
+ case Builtin::BI__builtin_setjmp:
+ if (BuiltinSetjmp(TheCall))
+ return ExprError();
+ break;
+ case Builtin::BI__builtin_classify_type:
+ if (checkArgCount(TheCall, 1))
+ return true;
+ TheCall->setType(Context.IntTy);
+ break;
+ case Builtin::BI__builtin_complex:
+ if (BuiltinComplex(TheCall))
+ return ExprError();
+ break;
+ case Builtin::BI__builtin_constant_p: {
+ if (checkArgCount(TheCall, 1))
+ return true;
+ ExprResult Arg = DefaultFunctionArrayLvalueConversion(TheCall->getArg(0));
+ if (Arg.isInvalid()) return true;
+ TheCall->setArg(0, Arg.get());
+ TheCall->setType(Context.IntTy);
+ break;
+ }
+ case Builtin::BI__builtin_launder:
+ return BuiltinLaunder(*this, TheCall);
+ case Builtin::BI__builtin_is_within_lifetime:
+ return BuiltinIsWithinLifetime(*this, TheCall);
+ case Builtin::BI__builtin_trivially_relocate:
+ return BuiltinTriviallyRelocate(*this, TheCall);
+
+ case Builtin::BI__sync_fetch_and_add:
+ case Builtin::BI__sync_fetch_and_add_1:
+ case Builtin::BI__sync_fetch_and_add_2:
+ case Builtin::BI__sync_fetch_and_add_4:
+ case Builtin::BI__sync_fetch_and_add_8:
+ case Builtin::BI__sync_fetch_and_add_16:
+ case Builtin::BI__sync_fetch_and_sub:
+ case Builtin::BI__sync_fetch_and_sub_1:
+ case Builtin::BI__sync_fetch_and_sub_2:
+ case Builtin::BI__sync_fetch_and_sub_4:
+ case Builtin::BI__sync_fetch_and_sub_8:
+ case Builtin::BI__sync_fetch_and_sub_16:
+ case Builtin::BI__sync_fetch_and_or:
+ case Builtin::BI__sync_fetch_and_or_1:
+ case Builtin::BI__sync_fetch_and_or_2:
+ case Builtin::BI__sync_fetch_and_or_4:
+ case Builtin::BI__sync_fetch_and_or_8:
+ case Builtin::BI__sync_fetch_and_or_16:
+ case Builtin::BI__sync_fetch_and_and:
+ case Builtin::BI__sync_fetch_and_and_1:
+ case Builtin::BI__sync_fetch_and_and_2:
+ case Builtin::BI__sync_fetch_and_and_4:
+ case Builtin::BI__sync_fetch_and_and_8:
+ case Builtin::BI__sync_fetch_and_and_16:
+ case Builtin::BI__sync_fetch_and_xor:
+ case Builtin::BI__sync_fetch_and_xor_1:
+ case Builtin::BI__sync_fetch_and_xor_2:
+ case Builtin::BI__sync_fetch_and_xor_4:
+ case Builtin::BI__sync_fetch_and_xor_8:
+ case Builtin::BI__sync_fetch_and_xor_16:
+ case Builtin::BI__sync_fetch_and_nand:
+ case Builtin::BI__sync_fetch_and_nand_1:
+ case Builtin::BI__sync_fetch_and_nand_2:
+ case Builtin::BI__sync_fetch_and_nand_4:
+ case Builtin::BI__sync_fetch_and_nand_8:
+ case Builtin::BI__sync_fetch_and_nand_16:
+ case Builtin::BI__sync_add_and_fetch:
+ case Builtin::BI__sync_add_and_fetch_1:
+ case Builtin::BI__sync_add_and_fetch_2:
+ case Builtin::BI__sync_add_and_fetch_4:
+ case Builtin::BI__sync_add_and_fetch_8:
+ case Builtin::BI__sync_add_and_fetch_16:
+ case Builtin::BI__sync_sub_and_fetch:
+ case Builtin::BI__sync_sub_and_fetch_1:
+ case Builtin::BI__sync_sub_and_fetch_2:
+ case Builtin::BI__sync_sub_and_fetch_4:
+ case Builtin::BI__sync_sub_and_fetch_8:
+ case Builtin::BI__sync_sub_and_fetch_16:
+ case Builtin::BI__sync_and_and_fetch:
+ case Builtin::BI__sync_and_and_fetch_1:
+ case Builtin::BI__sync_and_and_fetch_2:
+ case Builtin::BI__sync_and_and_fetch_4:
+ case Builtin::BI__sync_and_and_fetch_8:
+ case Builtin::BI__sync_and_and_fetch_16:
+ case Builtin::BI__sync_or_and_fetch:
+ case Builtin::BI__sync_or_and_fetch_1:
+ case Builtin::BI__sync_or_and_fetch_2:
+ case Builtin::BI__sync_or_and_fetch_4:
+ case Builtin::BI__sync_or_and_fetch_8:
+ case Builtin::BI__sync_or_and_fetch_16:
+ case Builtin::BI__sync_xor_and_fetch:
+ case Builtin::BI__sync_xor_and_fetch_1:
+ case Builtin::BI__sync_xor_and_fetch_2:
+ case Builtin::BI__sync_xor_and_fetch_4:
+ case Builtin::BI__sync_xor_and_fetch_8:
+ case Builtin::BI__sync_xor_and_fetch_16:
+ case Builtin::BI__sync_nand_and_fetch:
+ case Builtin::BI__sync_nand_and_fetch_1:
+ case Builtin::BI__sync_nand_and_fetch_2:
+ case Builtin::BI__sync_nand_and_fetch_4:
+ case Builtin::BI__sync_nand_and_fetch_8:
+ case Builtin::BI__sync_nand_and_fetch_16:
+ case Builtin::BI__sync_val_compare_and_swap:
+ case Builtin::BI__sync_val_compare_and_swap_1:
+ case Builtin::BI__sync_val_compare_and_swap_2:
+ case Builtin::BI__sync_val_compare_and_swap_4:
+ case Builtin::BI__sync_val_compare_and_swap_8:
+ case Builtin::BI__sync_val_compare_and_swap_16:
+ case Builtin::BI__sync_bool_compare_and_swap:
+ case Builtin::BI__sync_bool_compare_and_swap_1:
+ case Builtin::BI__sync_bool_compare_and_swap_2:
+ case Builtin::BI__sync_bool_compare_and_swap_4:
+ case Builtin::BI__sync_bool_compare_and_swap_8:
+ case Builtin::BI__sync_bool_compare_and_swap_16:
+ case Builtin::BI__sync_lock_test_and_set:
+ case Builtin::BI__sync_lock_test_and_set_1:
+ case Builtin::BI__sync_lock_test_and_set_2:
+ case Builtin::BI__sync_lock_test_and_set_4:
+ case Builtin::BI__sync_lock_test_and_set_8:
+ case Builtin::BI__sync_lock_test_and_set_16:
+ case Builtin::BI__sync_lock_release:
+ case Builtin::BI__sync_lock_release_1:
+ case Builtin::BI__sync_lock_release_2:
+ case Builtin::BI__sync_lock_release_4:
+ case Builtin::BI__sync_lock_release_8:
+ case Builtin::BI__sync_lock_release_16:
+ case Builtin::BI__sync_swap:
+ case Builtin::BI__sync_swap_1:
+ case Builtin::BI__sync_swap_2:
+ case Builtin::BI__sync_swap_4:
+ case Builtin::BI__sync_swap_8:
+ case Builtin::BI__sync_swap_16:
+ return BuiltinAtomicOverloaded(TheCallResult);
+ case Builtin::BI__sync_synchronize:
+ Diag(TheCall->getBeginLoc(), diag::warn_atomic_implicit_seq_cst)
+ << TheCall->getCallee()->getSourceRange();
+ break;
+ case Builtin::BI__builtin_nontemporal_load:
+ case Builtin::BI__builtin_nontemporal_store:
+ return BuiltinNontemporalOverloaded(TheCallResult);
+ case Builtin::BI__builtin_memcpy_inline: {
+ clang::Expr *SizeOp = TheCall->getArg(2);
+ // We warn about copying to or from `nullptr` pointers when `size` is
+ // greater than 0. When `size` is value dependent we cannot evaluate its
+ // value so we bail out.
+ if (SizeOp->isValueDependent())
+ break;
+ if (!SizeOp->EvaluateKnownConstInt(Context).isZero()) {
+ CheckNonNullArgument(*this, TheCall->getArg(0), TheCall->getExprLoc());
+ CheckNonNullArgument(*this, TheCall->getArg(1), TheCall->getExprLoc());
+ }
+ break;
+ }
+ case Builtin::BI__builtin_memset_inline: {
+ clang::Expr *SizeOp = TheCall->getArg(2);
+ // We warn about filling to `nullptr` pointers when `size` is greater than
+ // 0. When `size` is value dependent we cannot evaluate its value so we bail
+ // out.
+ if (SizeOp->isValueDependent())
+ break;
+ if (!SizeOp->EvaluateKnownConstInt(Context).isZero())
+ CheckNonNullArgument(*this, TheCall->getArg(0), TheCall->getExprLoc());
+ break;
+ }
+#define ATOMIC_BUILTIN(ID, TYPE, ATTRS) \
+ case Builtin::BI##ID: \
+ return AtomicOpsOverloaded(TheCallResult, AtomicExpr::AO##ID);
+#include "clang/Basic/Builtins.inc"
+ case Builtin::BI__annotation:
+ if (BuiltinMSVCAnnotation(*this, TheCall))
+ return ExprError();
+ break;
+ case Builtin::BI__builtin_annotation:
+ if (BuiltinAnnotation(*this, TheCall))
+ return ExprError();
+ break;
+ case Builtin::BI__builtin_addressof:
+ if (BuiltinAddressof(*this, TheCall))
+ return ExprError();
+ break;
+ case Builtin::BI__builtin_function_start:
+ if (BuiltinFunctionStart(*this, TheCall))
+ return ExprError();
+ break;
+ case Builtin::BI__builtin_is_aligned:
+ case Builtin::BI__builtin_align_up:
+ case Builtin::BI__builtin_align_down:
+ if (BuiltinAlignment(*this, TheCall, BuiltinID))
+ return ExprError();
+ break;
+ case Builtin::BI__builtin_add_overflow:
+ case Builtin::BI__builtin_sub_overflow:
+ case Builtin::BI__builtin_mul_overflow:
+ if (BuiltinOverflow(*this, TheCall, BuiltinID))
+ return ExprError();
+ break;
+ case Builtin::BI__builtin_operator_new:
+ case Builtin::BI__builtin_operator_delete: {
+ bool IsDelete = BuiltinID == Builtin::BI__builtin_operator_delete;
+ ExprResult Res =
+ BuiltinOperatorNewDeleteOverloaded(TheCallResult, IsDelete);
+ return Res;
+ }
+ case Builtin::BI__builtin_dump_struct:
+ return BuiltinDumpStruct(*this, TheCall);
+ case Builtin::BI__builtin_expect_with_probability: {
+ // We first want to ensure we are called with 3 arguments
+ if (checkArgCount(TheCall, 3))
+ return ExprError();
+ // then check probability is constant float in range [0.0, 1.0]
+ const Expr *ProbArg = TheCall->getArg(2);
+ SmallVector<PartialDiagnosticAt, 8> Notes;
+ Expr::EvalResult Eval;
+ Eval.Diag = &Notes;
+ if ((!ProbArg->EvaluateAsConstantExpr(Eval, Context)) ||
+ !Eval.Val.isFloat()) {
+ Diag(ProbArg->getBeginLoc(), diag::err_probability_not_constant_float)
+ << ProbArg->getSourceRange();
+ for (const PartialDiagnosticAt &PDiag : Notes)
+ Diag(PDiag.first, PDiag.second);
+ return ExprError();
+ }
+ llvm::APFloat Probability = Eval.Val.getFloat();
+ bool LoseInfo = false;
+ Probability.convert(llvm::APFloat::IEEEdouble(),
+ llvm::RoundingMode::Dynamic, &LoseInfo);
+ if (!(Probability >= llvm::APFloat(0.0) &&
+ Probability <= llvm::APFloat(1.0))) {
+ Diag(ProbArg->getBeginLoc(), diag::err_probability_out_of_range)
+ << ProbArg->getSourceRange();
+ return ExprError();
+ }
+ break;
+ }
+ case Builtin::BI__builtin_preserve_access_index:
+ if (BuiltinPreserveAI(*this, TheCall))
+ return ExprError();
+ break;
+ case Builtin::BI__builtin_call_with_static_chain:
+ if (BuiltinCallWithStaticChain(*this, TheCall))
+ return ExprError();
+ break;
+ case Builtin::BI__exception_code:
+ case Builtin::BI_exception_code:
+ if (BuiltinSEHScopeCheck(*this, TheCall, Scope::SEHExceptScope,
+ diag::err_seh___except_block))
+ return ExprError();
+ break;
+ case Builtin::BI__exception_info:
+ case Builtin::BI_exception_info:
+ if (BuiltinSEHScopeCheck(*this, TheCall, Scope::SEHFilterScope,
+ diag::err_seh___except_filter))
+ return ExprError();
+ break;
+ case Builtin::BI__GetExceptionInfo:
+ if (checkArgCount(TheCall, 1))
+ return ExprError();
+
+ if (CheckCXXThrowOperand(
+ TheCall->getBeginLoc(),
+ Context.getExceptionObjectType(FDecl->getParamDecl(0)->getType()),
+ TheCall))
+ return ExprError();
+
+ TheCall->setType(Context.VoidPtrTy);
+ break;
+ case Builtin::BIaddressof:
+ case Builtin::BI__addressof:
+ case Builtin::BIforward:
+ case Builtin::BIforward_like:
+ case Builtin::BImove:
+ case Builtin::BImove_if_noexcept:
+ case Builtin::BIas_const: {
+ // These are all expected to be of the form
+ // T &/&&/* f(U &/&&)
+ // where T and U only differ in qualification.
+ if (checkArgCount(TheCall, 1))
+ return ExprError();
+ QualType Param = FDecl->getParamDecl(0)->getType();
+ QualType Result = FDecl->getReturnType();
+ bool ReturnsPointer = BuiltinID == Builtin::BIaddressof ||
+ BuiltinID == Builtin::BI__addressof;
+ if (!(Param->isReferenceType() &&
+ (ReturnsPointer ? Result->isAnyPointerType()
+ : Result->isReferenceType()) &&
+ Context.hasSameUnqualifiedType(Param->getPointeeType(),
+ Result->getPointeeType()))) {
+ Diag(TheCall->getBeginLoc(), diag::err_builtin_move_forward_unsupported)
+ << FDecl;
+ return ExprError();
+ }
+ break;
+ }
+ case Builtin::BI__builtin_ptrauth_strip:
+ return PointerAuthStrip(*this, TheCall);
+ case Builtin::BI__builtin_ptrauth_blend_discriminator:
+ return PointerAuthBlendDiscriminator(*this, TheCall);
+ case Builtin::BI__builtin_ptrauth_sign_constant:
+ return PointerAuthSignOrAuth(*this, TheCall, PAO_Sign,
+ /*RequireConstant=*/true);
+ case Builtin::BI__builtin_ptrauth_sign_unauthenticated:
+ return PointerAuthSignOrAuth(*this, TheCall, PAO_Sign,
+ /*RequireConstant=*/false);
+ case Builtin::BI__builtin_ptrauth_auth:
+ return PointerAuthSignOrAuth(*this, TheCall, PAO_Auth,
+ /*RequireConstant=*/false);
+ case Builtin::BI__builtin_ptrauth_sign_generic_data:
+ return PointerAuthSignGenericData(*this, TheCall);
+ case Builtin::BI__builtin_ptrauth_auth_and_resign:
+ return PointerAuthAuthAndResign(*this, TheCall);
+ case Builtin::BI__builtin_ptrauth_auth_load_relative_and_sign:
+ return PointerAuthAuthLoadRelativeAndSign(*this, TheCall);
+ case Builtin::BI__builtin_ptrauth_string_discriminator:
+ return PointerAuthStringDiscriminator(*this, TheCall);
+
+ case Builtin::BI__builtin_get_vtable_pointer:
+ return GetVTablePointer(*this, TheCall);
+
+ // OpenCL v2.0, s6.13.16 - Pipe functions
+ case Builtin::BIread_pipe:
+ case Builtin::BIwrite_pipe:
+ // Since those two functions are declared with var args, we need a semantic
+ // check for the argument.
+ if (OpenCL().checkBuiltinRWPipe(TheCall))
+ return ExprError();
+ break;
+ case Builtin::BIreserve_read_pipe:
+ case Builtin::BIreserve_write_pipe:
+ case Builtin::BIwork_group_reserve_read_pipe:
+ case Builtin::BIwork_group_reserve_write_pipe:
+ if (OpenCL().checkBuiltinReserveRWPipe(TheCall))
+ return ExprError();
+ break;
+ case Builtin::BIsub_group_reserve_read_pipe:
+ case Builtin::BIsub_group_reserve_write_pipe:
+ if (OpenCL().checkSubgroupExt(TheCall) ||
+ OpenCL().checkBuiltinReserveRWPipe(TheCall))
+ return ExprError();
+ break;
+ case Builtin::BIcommit_read_pipe:
+ case Builtin::BIcommit_write_pipe:
+ case Builtin::BIwork_group_commit_read_pipe:
+ case Builtin::BIwork_group_commit_write_pipe:
+ if (OpenCL().checkBuiltinCommitRWPipe(TheCall))
+ return ExprError();
+ break;
+ case Builtin::BIsub_group_commit_read_pipe:
+ case Builtin::BIsub_group_commit_write_pipe:
+ if (OpenCL().checkSubgroupExt(TheCall) ||
+ OpenCL().checkBuiltinCommitRWPipe(TheCall))
+ return ExprError();
+ break;
+ case Builtin::BIget_pipe_num_packets:
+ case Builtin::BIget_pipe_max_packets:
+ if (OpenCL().checkBuiltinPipePackets(TheCall))
+ return ExprError();
+ break;
+ case Builtin::BIto_global:
+ case Builtin::BIto_local:
+ case Builtin::BIto_private:
+ if (OpenCL().checkBuiltinToAddr(BuiltinID, TheCall))
+ return ExprError();
+ break;
+ // OpenCL v2.0, s6.13.17 - Enqueue kernel functions.
+ case Builtin::BIenqueue_kernel:
+ if (OpenCL().checkBuiltinEnqueueKernel(TheCall))
+ return ExprError();
+ break;
+ case Builtin::BIget_kernel_work_group_size:
+ case Builtin::BIget_kernel_preferred_work_group_size_multiple:
+ if (OpenCL().checkBuiltinKernelWorkGroupSize(TheCall))
+ return ExprError();
+ break;
+ case Builtin::BIget_kernel_max_sub_group_size_for_ndrange:
+ case Builtin::BIget_kernel_sub_group_count_for_ndrange:
+ if (OpenCL().checkBuiltinNDRangeAndBlock(TheCall))
+ return ExprError();
+ break;
+ case Builtin::BI__builtin_os_log_format:
+ Cleanup.setExprNeedsCleanups(true);
+ [[fallthrough]];
+ case Builtin::BI__builtin_os_log_format_buffer_size:
+ if (BuiltinOSLogFormat(TheCall))
+ return ExprError();
+ break;
+ case Builtin::BI__builtin_frame_address:
+ case Builtin::BI__builtin_return_address: {
+ if (BuiltinConstantArgRange(TheCall, 0, 0, 0xFFFF))
+ return ExprError();
+
+ // -Wframe-address warning if non-zero passed to builtin
+ // return/frame address.
+ Expr::EvalResult Result;
+ if (!TheCall->getArg(0)->isValueDependent() &&
+ TheCall->getArg(0)->EvaluateAsInt(Result, getASTContext()) &&
+ Result.Val.getInt() != 0)
+ Diag(TheCall->getBeginLoc(), diag::warn_frame_address)
+ << ((BuiltinID == Builtin::BI__builtin_return_address)
+ ? "__builtin_return_address"
+ : "__builtin_frame_address")
+ << TheCall->getSourceRange();
+ break;
+ }
+
+ case Builtin::BI__builtin_nondeterministic_value: {
+ if (BuiltinNonDeterministicValue(TheCall))
+ return ExprError();
+ break;
+ }
+
+ // __builtin_elementwise_abs restricts the element type to signed integers or
+ // floating point types only.
+ case Builtin::BI__builtin_elementwise_abs:
+ if (PrepareBuiltinElementwiseMathOneArgCall(
+ TheCall, EltwiseBuiltinArgTyRestriction::SignedIntOrFloatTy))
+ return ExprError();
+ break;
+
+ // These builtins restrict the element type to floating point
+ // types only.
+ case Builtin::BI__builtin_elementwise_acos:
+ case Builtin::BI__builtin_elementwise_asin:
+ case Builtin::BI__builtin_elementwise_atan:
+ case Builtin::BI__builtin_elementwise_ceil:
+ case Builtin::BI__builtin_elementwise_cos:
+ case Builtin::BI__builtin_elementwise_cosh:
+ case Builtin::BI__builtin_elementwise_exp:
+ case Builtin::BI__builtin_elementwise_exp2:
+ case Builtin::BI__builtin_elementwise_exp10:
+ case Builtin::BI__builtin_elementwise_floor:
+ case Builtin::BI__builtin_elementwise_log:
+ case Builtin::BI__builtin_elementwise_log2:
+ case Builtin::BI__builtin_elementwise_log10:
+ case Builtin::BI__builtin_elementwise_roundeven:
+ case Builtin::BI__builtin_elementwise_round:
+ case Builtin::BI__builtin_elementwise_rint:
+ case Builtin::BI__builtin_elementwise_nearbyint:
+ case Builtin::BI__builtin_elementwise_sin:
+ case Builtin::BI__builtin_elementwise_sinh:
+ case Builtin::BI__builtin_elementwise_sqrt:
+ case Builtin::BI__builtin_elementwise_tan:
+ case Builtin::BI__builtin_elementwise_tanh:
+ case Builtin::BI__builtin_elementwise_trunc:
+ case Builtin::BI__builtin_elementwise_canonicalize:
+ if (PrepareBuiltinElementwiseMathOneArgCall(
+ TheCall, EltwiseBuiltinArgTyRestriction::FloatTy))
+ return ExprError();
+ break;
+ case Builtin::BI__builtin_elementwise_fma:
+ if (BuiltinElementwiseTernaryMath(TheCall))
+ return ExprError();
+ break;
+
+ case Builtin::BI__builtin_elementwise_ldexp: {
+ if (checkArgCount(TheCall, 2))
+ return ExprError();
+
+ ExprResult A = BuiltinVectorMathConversions(*this, TheCall->getArg(0));
+ if (A.isInvalid())
+ return ExprError();
+ QualType TyA = A.get()->getType();
+ if (checkMathBuiltinElementType(*this, A.get()->getBeginLoc(), TyA,
+ EltwiseBuiltinArgTyRestriction::FloatTy, 1))
+ return ExprError();
+
+ ExprResult Exp = UsualUnaryConversions(TheCall->getArg(1));
+ if (Exp.isInvalid())
+ return ExprError();
+ QualType TyExp = Exp.get()->getType();
+ if (checkMathBuiltinElementType(*this, Exp.get()->getBeginLoc(), TyExp,
+ EltwiseBuiltinArgTyRestriction::IntegerTy,
+ 2))
+ return ExprError();
+
+ // Check the two arguments are either scalars or vectors of equal length.
+ const auto *Vec0 = TyA->getAs<VectorType>();
+ const auto *Vec1 = TyExp->getAs<VectorType>();
+ unsigned Arg0Length = Vec0 ? Vec0->getNumElements() : 0;
+ unsigned Arg1Length = Vec1 ? Vec1->getNumElements() : 0;
+ if (Arg0Length != Arg1Length) {
+ Diag(Exp.get()->getBeginLoc(),
+ diag::err_typecheck_vector_lengths_not_equal)
+ << TyA << TyExp << A.get()->getSourceRange()
+ << Exp.get()->getSourceRange();
+ return ExprError();
+ }
+
+ TheCall->setArg(0, A.get());
+ TheCall->setArg(1, Exp.get());
+ TheCall->setType(TyA);
+ break;
+ }
+
+ // These builtins restrict the element type to floating point
+ // types only, and take in two arguments.
+ case Builtin::BI__builtin_elementwise_minnum:
+ case Builtin::BI__builtin_elementwise_maxnum:
+ case Builtin::BI__builtin_elementwise_minimum:
+ case Builtin::BI__builtin_elementwise_maximum:
+ case Builtin::BI__builtin_elementwise_minimumnum:
+ case Builtin::BI__builtin_elementwise_maximumnum:
+ case Builtin::BI__builtin_elementwise_atan2:
+ case Builtin::BI__builtin_elementwise_fmod:
+ case Builtin::BI__builtin_elementwise_pow:
+ if (BuiltinElementwiseMath(TheCall,
+ EltwiseBuiltinArgTyRestriction::FloatTy))
+ return ExprError();
+ break;
+ // These builtins restrict the element type to integer
+ // types only.
+ case Builtin::BI__builtin_elementwise_add_sat:
+ case Builtin::BI__builtin_elementwise_sub_sat:
+ if (BuiltinElementwiseMath(TheCall,
+ EltwiseBuiltinArgTyRestriction::IntegerTy))
+ return ExprError();
+ break;
+ case Builtin::BI__builtin_elementwise_fshl:
+ case Builtin::BI__builtin_elementwise_fshr:
+ if (BuiltinElementwiseTernaryMath(
+ TheCall, EltwiseBuiltinArgTyRestriction::IntegerTy))
+ return ExprError();
+ break;
+ case Builtin::BI__builtin_elementwise_min:
+ case Builtin::BI__builtin_elementwise_max:
+ if (BuiltinElementwiseMath(TheCall))
+ return ExprError();
+ break;
+ case Builtin::BI__builtin_elementwise_popcount:
+ case Builtin::BI__builtin_elementwise_bitreverse:
+ if (PrepareBuiltinElementwiseMathOneArgCall(
+ TheCall, EltwiseBuiltinArgTyRestriction::IntegerTy))
+ return ExprError();
+ break;
+ case Builtin::BI__builtin_elementwise_copysign: {
+ if (checkArgCount(TheCall, 2))
+ return ExprError();
+
+ ExprResult Magnitude = UsualUnaryConversions(TheCall->getArg(0));
+ ExprResult Sign = UsualUnaryConversions(TheCall->getArg(1));
+ if (Magnitude.isInvalid() || Sign.isInvalid())
+ return ExprError();
+
+ QualType MagnitudeTy = Magnitude.get()->getType();
+ QualType SignTy = Sign.get()->getType();
+ if (checkMathBuiltinElementType(
+ *this, TheCall->getArg(0)->getBeginLoc(), MagnitudeTy,
+ EltwiseBuiltinArgTyRestriction::FloatTy, 1) ||
+ checkMathBuiltinElementType(
+ *this, TheCall->getArg(1)->getBeginLoc(), SignTy,
+ EltwiseBuiltinArgTyRestriction::FloatTy, 2)) {
+ return ExprError();
+ }
+
+ if (MagnitudeTy.getCanonicalType() != SignTy.getCanonicalType()) {
+ return Diag(Sign.get()->getBeginLoc(),
+ diag::err_typecheck_call_different_arg_types)
+ << MagnitudeTy << SignTy;
+ }
+
+ TheCall->setArg(0, Magnitude.get());
+ TheCall->setArg(1, Sign.get());
+ TheCall->setType(Magnitude.get()->getType());
+ break;
+ }
+ case Builtin::BI__builtin_elementwise_clzg:
+ case Builtin::BI__builtin_elementwise_ctzg:
+ // These builtins can be unary or binary. Note for empty calls we call the
+ // unary checker in order to not emit an error that says the function
+ // expects 2 arguments, which would be misleading.
+ if (TheCall->getNumArgs() <= 1) {
+ if (PrepareBuiltinElementwiseMathOneArgCall(
+ TheCall, EltwiseBuiltinArgTyRestriction::IntegerTy))
+ return ExprError();
+ } else if (BuiltinElementwiseMath(
+ TheCall, EltwiseBuiltinArgTyRestriction::IntegerTy))
+ return ExprError();
+ break;
+ case Builtin::BI__builtin_reduce_max:
+ case Builtin::BI__builtin_reduce_min: {
+ if (PrepareBuiltinReduceMathOneArgCall(TheCall))
+ return ExprError();
+
+ const Expr *Arg = TheCall->getArg(0);
+ const auto *TyA = Arg->getType()->getAs<VectorType>();
+
+ QualType ElTy;
+ if (TyA)
+ ElTy = TyA->getElementType();
+ else if (Arg->getType()->isSizelessVectorType())
+ ElTy = Arg->getType()->getSizelessVectorEltType(Context);
+
+ if (ElTy.isNull()) {
+ Diag(Arg->getBeginLoc(), diag::err_builtin_invalid_arg_type)
+ << 1 << /* vector ty */ 2 << /* no int */ 0 << /* no fp */ 0
+ << Arg->getType();
+ return ExprError();
+ }
+
+ TheCall->setType(ElTy);
+ break;
+ }
+ case Builtin::BI__builtin_reduce_maximum:
+ case Builtin::BI__builtin_reduce_minimum: {
+ if (PrepareBuiltinReduceMathOneArgCall(TheCall))
+ return ExprError();
+
+ const Expr *Arg = TheCall->getArg(0);
+ const auto *TyA = Arg->getType()->getAs<VectorType>();
+
+ QualType ElTy;
+ if (TyA)
+ ElTy = TyA->getElementType();
+ else if (Arg->getType()->isSizelessVectorType())
+ ElTy = Arg->getType()->getSizelessVectorEltType(Context);
+
+ if (ElTy.isNull() || !ElTy->isFloatingType()) {
+ Diag(Arg->getBeginLoc(), diag::err_builtin_invalid_arg_type)
+ << 1 << /* vector of */ 4 << /* no int */ 0 << /* fp */ 1
+ << Arg->getType();
+ return ExprError();
+ }
+
+ TheCall->setType(ElTy);
+ break;
+ }
+
+ // These builtins support vectors of integers only.
+ // TODO: ADD/MUL should support floating-point types.
+ case Builtin::BI__builtin_reduce_add:
+ case Builtin::BI__builtin_reduce_mul:
+ case Builtin::BI__builtin_reduce_xor:
+ case Builtin::BI__builtin_reduce_or:
+ case Builtin::BI__builtin_reduce_and: {
+ if (PrepareBuiltinReduceMathOneArgCall(TheCall))
+ return ExprError();
+
+ const Expr *Arg = TheCall->getArg(0);
+ const auto *TyA = Arg->getType()->getAs<VectorType>();
+
+ QualType ElTy;
+ if (TyA)
+ ElTy = TyA->getElementType();
+ else if (Arg->getType()->isSizelessVectorType())
+ ElTy = Arg->getType()->getSizelessVectorEltType(Context);
+
+ if (ElTy.isNull() || !ElTy->isIntegerType()) {
+ Diag(Arg->getBeginLoc(), diag::err_builtin_invalid_arg_type)
+ << 1 << /* vector of */ 4 << /* int */ 1 << /* no fp */ 0
+ << Arg->getType();
+ return ExprError();
+ }
+
+ TheCall->setType(ElTy);
+ break;
+ }
+
+ case Builtin::BI__builtin_matrix_transpose:
+ return BuiltinMatrixTranspose(TheCall, TheCallResult);
+
+ case Builtin::BI__builtin_matrix_column_major_load:
+ return BuiltinMatrixColumnMajorLoad(TheCall, TheCallResult);
+
+ case Builtin::BI__builtin_matrix_column_major_store:
+ return BuiltinMatrixColumnMajorStore(TheCall, TheCallResult);
+
+ case Builtin::BI__builtin_verbose_trap:
+ if (!checkBuiltinVerboseTrap(TheCall, *this))
+ return ExprError();
+ break;
+
+ case Builtin::BI__builtin_get_device_side_mangled_name: {
+ auto Check = [](CallExpr *TheCall) {
+ if (TheCall->getNumArgs() != 1)
+ return false;
+ auto *DRE = dyn_cast<DeclRefExpr>(TheCall->getArg(0)->IgnoreImpCasts());
+ if (!DRE)
+ return false;
+ auto *D = DRE->getDecl();
+ if (!isa<FunctionDecl>(D) && !isa<VarDecl>(D))
+ return false;
+ return D->hasAttr<CUDAGlobalAttr>() || D->hasAttr<CUDADeviceAttr>() ||
+ D->hasAttr<CUDAConstantAttr>() || D->hasAttr<HIPManagedAttr>();
+ };
+ if (!Check(TheCall)) {
+ Diag(TheCall->getBeginLoc(),
+ diag::err_hip_invalid_args_builtin_mangled_name);
+ return ExprError();
+ }
+ break;
+ }
+ case Builtin::BI__builtin_bswapg:
+ if (BuiltinBswapg(*this, TheCall))
+ return ExprError();
+ break;
+ case Builtin::BI__builtin_popcountg:
+ if (BuiltinPopcountg(*this, TheCall))
+ return ExprError();
+ break;
+ case Builtin::BI__builtin_clzg:
+ case Builtin::BI__builtin_ctzg:
+ if (BuiltinCountZeroBitsGeneric(*this, TheCall))
+ return ExprError();
+ break;
+
+ case Builtin::BI__builtin_stdc_rotate_left:
+ case Builtin::BI__builtin_stdc_rotate_right:
+ if (BuiltinRotateGeneric(*this, TheCall))
+ return ExprError();
+ break;
+
+ case Builtin::BI__builtin_allow_runtime_check: {
+ Expr *Arg = TheCall->getArg(0);
+ // Check if the argument is a string literal.
+ if (!isa<StringLiteral>(Arg->IgnoreParenImpCasts())) {
+ Diag(TheCall->getBeginLoc(), diag::err_expr_not_string_literal)
+ << Arg->getSourceRange();
+ return ExprError();
+ }
+ break;
+ }
+
+ case Builtin::BI__builtin_allow_sanitize_check: {
+ Expr *Arg = TheCall->getArg(0);
+ // Check if the argument is a string literal.
+ const StringLiteral *SanitizerName =
+ dyn_cast<StringLiteral>(Arg->IgnoreParenImpCasts());
+ if (!SanitizerName) {
+ Diag(TheCall->getBeginLoc(), diag::err_expr_not_string_literal)
+ << Arg->getSourceRange();
+ return ExprError();
+ }
+ // Validate the sanitizer name.
+ if (!llvm::StringSwitch<bool>(SanitizerName->getString())
+ .Cases({"address", "thread", "memory", "hwaddress",
+ "kernel-address", "kernel-memory", "kernel-hwaddress"},
+ true)
+ .Default(false)) {
+ Diag(TheCall->getBeginLoc(), diag::err_invalid_builtin_argument)
+ << SanitizerName->getString() << "__builtin_allow_sanitize_check"
+ << Arg->getSourceRange();
+ return ExprError();
+ }
+ break;
+ }
+ case Builtin::BI__builtin_counted_by_ref:
+ if (BuiltinCountedByRef(TheCall))
+ return ExprError();
+ break;
+ }
+
+ if (getLangOpts().HLSL && HLSL().CheckBuiltinFunctionCall(BuiltinID, TheCall))
+ return ExprError();
+
+ // Since the target specific builtins for each arch overlap, only check those
+ // of the arch we are compiling for.
+ if (Context.BuiltinInfo.isTSBuiltin(BuiltinID)) {
+ if (Context.BuiltinInfo.isAuxBuiltinID(BuiltinID)) {
+ assert(Context.getAuxTargetInfo() &&
+ "Aux Target Builtin, but not an aux target?");
+
+ if (CheckTSBuiltinFunctionCall(
+ *Context.getAuxTargetInfo(),
+ Context.BuiltinInfo.getAuxBuiltinID(BuiltinID), TheCall))
+ return ExprError();
+ } else {
+ if (CheckTSBuiltinFunctionCall(Context.getTargetInfo(), BuiltinID,
+ TheCall))
+ return ExprError();
+ }
+ }
+
+ return TheCallResult;
+}
+
+bool Sema::ValueIsRunOfOnes(CallExpr *TheCall, unsigned ArgNum) {
+ llvm::APSInt Result;
+ // We can't check the value of a dependent argument.
+ Expr *Arg = TheCall->getArg(ArgNum);
+ if (Arg->isTypeDependent() || Arg->isValueDependent())
+ return false;
+
+ // Check constant-ness first.
+ if (BuiltinConstantArg(TheCall, ArgNum, Result))
+ return true;
+
+ // Check contiguous run of 1s, 0xFF0000FF is also a run of 1s.
+ if (Result.isShiftedMask() || (~Result).isShiftedMask())
+ return false;
+
+ return Diag(TheCall->getBeginLoc(),
+ diag::err_argument_not_contiguous_bit_field)
+ << ArgNum << Arg->getSourceRange();
+}
+
+bool Sema::getFormatStringInfo(const Decl *D, unsigned FormatIdx,
+ unsigned FirstArg, FormatStringInfo *FSI) {
+ bool HasImplicitThisParam = hasImplicitObjectParameter(D);
+ bool IsVariadic = false;
+ if (const FunctionType *FnTy = D->getFunctionType())
+ IsVariadic = cast<FunctionProtoType>(FnTy)->isVariadic();
+ else if (const auto *BD = dyn_cast<BlockDecl>(D))
+ IsVariadic = BD->isVariadic();
+ else if (const auto *OMD = dyn_cast<ObjCMethodDecl>(D))
+ IsVariadic = OMD->isVariadic();
+
+ return getFormatStringInfo(FormatIdx, FirstArg, HasImplicitThisParam,
+ IsVariadic, FSI);
+}
+
+bool Sema::getFormatStringInfo(unsigned FormatIdx, unsigned FirstArg,
+ bool HasImplicitThisParam, bool IsVariadic,
+ FormatStringInfo *FSI) {
+ if (FirstArg == 0)
+ FSI->ArgPassingKind = FAPK_VAList;
+ else if (IsVariadic)
+ FSI->ArgPassingKind = FAPK_Variadic;
+ else
+ FSI->ArgPassingKind = FAPK_Fixed;
+ FSI->FormatIdx = FormatIdx - 1;
+ FSI->FirstDataArg = FSI->ArgPassingKind == FAPK_VAList ? 0 : FirstArg - 1;
+
+ // The way the format attribute works in GCC, the implicit this argument
+ // of member functions is counted. However, it doesn't appear in our own
+ // lists, so decrement format_idx in that case.
+ if (HasImplicitThisParam) {
+ if(FSI->FormatIdx == 0)
+ return false;
+ --FSI->FormatIdx;
+ if (FSI->FirstDataArg != 0)
+ --FSI->FirstDataArg;
+ }
+ return true;
+}
+
+/// Checks if a the given expression evaluates to null.
+///
+/// Returns true if the value evaluates to null.
+static bool CheckNonNullExpr(Sema &S, const Expr *Expr) {
+ // Treat (smart) pointers constructed from nullptr as null, whether we can
+ // const-evaluate them or not.
+ // This must happen first: the smart pointer expr might have _Nonnull type!
+ if (isa<CXXNullPtrLiteralExpr>(
+ IgnoreExprNodes(Expr, IgnoreImplicitAsWrittenSingleStep,
+ IgnoreElidableImplicitConstructorSingleStep)))
+ return true;
+
+ // If the expression has non-null type, it doesn't evaluate to null.
+ if (auto nullability = Expr->IgnoreImplicit()->getType()->getNullability()) {
+ if (*nullability == NullabilityKind::NonNull)
+ return false;
+ }
+
+ // As a special case, transparent unions initialized with zero are
+ // considered null for the purposes of the nonnull attribute.
+ if (const RecordType *UT = Expr->getType()->getAsUnionType();
+ UT &&
+ UT->getDecl()->getMostRecentDecl()->hasAttr<TransparentUnionAttr>()) {
+ if (const auto *CLE = dyn_cast<CompoundLiteralExpr>(Expr))
+ if (const auto *ILE = dyn_cast<InitListExpr>(CLE->getInitializer()))
+ Expr = ILE->getInit(0);
+ }
+
+ bool Result;
+ return (!Expr->isValueDependent() &&
+ Expr->EvaluateAsBooleanCondition(Result, S.Context) &&
+ !Result);
+}
+
+static void CheckNonNullArgument(Sema &S,
+ const Expr *ArgExpr,
+ SourceLocation CallSiteLoc) {
+ if (CheckNonNullExpr(S, ArgExpr))
+ S.DiagRuntimeBehavior(CallSiteLoc, ArgExpr,
+ S.PDiag(diag::warn_null_arg)
+ << ArgExpr->getSourceRange());
+}
+
+/// Determine whether the given type has a non-null nullability annotation.
+static bool isNonNullType(QualType type) {
+ if (auto nullability = type->getNullability())
+ return *nullability == NullabilityKind::NonNull;
+
+ return false;
+}
+
+static void CheckNonNullArguments(Sema &S,
+ const NamedDecl *FDecl,
+ const FunctionProtoType *Proto,
+ ArrayRef<const Expr *> Args,
+ SourceLocation CallSiteLoc) {
+ assert((FDecl || Proto) && "Need a function declaration or prototype");
+
+ // Already checked by constant evaluator.
+ if (S.isConstantEvaluatedContext())
+ return;
+ // Check the attributes attached to the method/function itself.
+ llvm::SmallBitVector NonNullArgs;
+ if (FDecl) {
+ // Handle the nonnull attribute on the function/method declaration itself.
+ for (const auto *NonNull : FDecl->specific_attrs<NonNullAttr>()) {
+ if (!NonNull->args_size()) {
+ // Easy case: all pointer arguments are nonnull.
+ for (const auto *Arg : Args)
+ if (S.isValidPointerAttrType(Arg->getType()))
+ CheckNonNullArgument(S, Arg, CallSiteLoc);
+ return;
+ }
+
+ for (const ParamIdx &Idx : NonNull->args()) {
+ unsigned IdxAST = Idx.getASTIndex();
+ if (IdxAST >= Args.size())
+ continue;
+ if (NonNullArgs.empty())
+ NonNullArgs.resize(Args.size());
+ NonNullArgs.set(IdxAST);
+ }
+ }
+ }
+
+ if (FDecl && (isa<FunctionDecl>(FDecl) || isa<ObjCMethodDecl>(FDecl))) {
+ // Handle the nonnull attribute on the parameters of the
+ // function/method.
+ ArrayRef<ParmVarDecl*> parms;
+ if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(FDecl))
+ parms = FD->parameters();
+ else
+ parms = cast<ObjCMethodDecl>(FDecl)->parameters();
+
+ unsigned ParamIndex = 0;
+ for (ArrayRef<ParmVarDecl*>::iterator I = parms.begin(), E = parms.end();
+ I != E; ++I, ++ParamIndex) {
+ const ParmVarDecl *PVD = *I;
+ if (PVD->hasAttr<NonNullAttr>() || isNonNullType(PVD->getType())) {
+ if (NonNullArgs.empty())
+ NonNullArgs.resize(Args.size());
+
+ NonNullArgs.set(ParamIndex);
+ }
+ }
+ } else {
+ // If we have a non-function, non-method declaration but no
+ // function prototype, try to dig out the function prototype.
+ if (!Proto) {
+ if (const ValueDecl *VD = dyn_cast<ValueDecl>(FDecl)) {
+ QualType type = VD->getType().getNonReferenceType();
+ if (auto pointerType = type->getAs<PointerType>())
+ type = pointerType->getPointeeType();
+ else if (auto blockType = type->getAs<BlockPointerType>())
+ type = blockType->getPointeeType();
+ // FIXME: data member pointers?
+
+ // Dig out the function prototype, if there is one.
+ Proto = type->getAs<FunctionProtoType>();
+ }
+ }
+
+ // Fill in non-null argument information from the nullability
+ // information on the parameter types (if we have them).
+ if (Proto) {
+ unsigned Index = 0;
+ for (auto paramType : Proto->getParamTypes()) {
+ if (isNonNullType(paramType)) {
+ if (NonNullArgs.empty())
+ NonNullArgs.resize(Args.size());
+
+ NonNullArgs.set(Index);
+ }
+
+ ++Index;
+ }
+ }
+ }
+
+ // Check for non-null arguments.
+ for (unsigned ArgIndex = 0, ArgIndexEnd = NonNullArgs.size();
+ ArgIndex != ArgIndexEnd; ++ArgIndex) {
+ if (NonNullArgs[ArgIndex])
+ CheckNonNullArgument(S, Args[ArgIndex], Args[ArgIndex]->getExprLoc());
+ }
+}
+
+void Sema::CheckArgAlignment(SourceLocation Loc, NamedDecl *FDecl,
+ StringRef ParamName, QualType ArgTy,
+ QualType ParamTy) {
+
+ // If a function accepts a pointer or reference type
+ if (!ParamTy->isPointerType() && !ParamTy->isReferenceType())
+ return;
+
+ // If the parameter is a pointer type, get the pointee type for the
+ // argument too. If the parameter is a reference type, don't try to get
+ // the pointee type for the argument.
+ if (ParamTy->isPointerType())
+ ArgTy = ArgTy->getPointeeType();
+
+ // Remove reference or pointer
+ ParamTy = ParamTy->getPointeeType();
+
+ // Find expected alignment, and the actual alignment of the passed object.
+ // getTypeAlignInChars requires complete types
+ if (ArgTy.isNull() || ParamTy->isDependentType() ||
+ ParamTy->isIncompleteType() || ArgTy->isIncompleteType() ||
+ ParamTy->isUndeducedType() || ArgTy->isUndeducedType())
+ return;
+
+ CharUnits ParamAlign = Context.getTypeAlignInChars(ParamTy);
+ CharUnits ArgAlign = Context.getTypeAlignInChars(ArgTy);
+
+ // If the argument is less aligned than the parameter, there is a
+ // potential alignment issue.
+ if (ArgAlign < ParamAlign)
+ Diag(Loc, diag::warn_param_mismatched_alignment)
+ << (int)ArgAlign.getQuantity() << (int)ParamAlign.getQuantity()
+ << ParamName << (FDecl != nullptr) << FDecl;
+}
+
+void Sema::checkLifetimeCaptureBy(FunctionDecl *FD, bool IsMemberFunction,
+ const Expr *ThisArg,
+ ArrayRef<const Expr *> Args) {
+ if (!FD || Args.empty())
+ return;
+ auto GetArgAt = [&](int Idx) -> const Expr * {
+ if (Idx == LifetimeCaptureByAttr::Global ||
+ Idx == LifetimeCaptureByAttr::Unknown)
+ return nullptr;
+ if (IsMemberFunction && Idx == 0)
+ return ThisArg;
+ return Args[Idx - IsMemberFunction];
+ };
+ auto HandleCaptureByAttr = [&](const LifetimeCaptureByAttr *Attr,
+ unsigned ArgIdx) {
+ if (!Attr)
+ return;
+
+ Expr *Captured = const_cast<Expr *>(GetArgAt(ArgIdx));
+ for (int CapturingParamIdx : Attr->params()) {
+ // lifetime_capture_by(this) case is handled in the lifetimebound expr
+ // initialization codepath.
+ if (CapturingParamIdx == LifetimeCaptureByAttr::This &&
+ isa<CXXConstructorDecl>(FD))
+ continue;
+ Expr *Capturing = const_cast<Expr *>(GetArgAt(CapturingParamIdx));
+ CapturingEntity CE{Capturing};
+ // Ensure that 'Captured' outlives the 'Capturing' entity.
+ checkCaptureByLifetime(*this, CE, Captured);
+ }
+ };
+ for (unsigned I = 0; I < FD->getNumParams(); ++I)
+ HandleCaptureByAttr(FD->getParamDecl(I)->getAttr<LifetimeCaptureByAttr>(),
+ I + IsMemberFunction);
+ // Check when the implicit object param is captured.
+ if (IsMemberFunction) {
+ TypeSourceInfo *TSI = FD->getTypeSourceInfo();
+ if (!TSI)
+ return;
+ AttributedTypeLoc ATL;
+ for (TypeLoc TL = TSI->getTypeLoc();
+ (ATL = TL.getAsAdjusted<AttributedTypeLoc>());
+ TL = ATL.getModifiedLoc())
+ HandleCaptureByAttr(ATL.getAttrAs<LifetimeCaptureByAttr>(), 0);
+ }
+}
+
+void Sema::checkCall(NamedDecl *FDecl, const FunctionProtoType *Proto,
+ const Expr *ThisArg, ArrayRef<const Expr *> Args,
+ bool IsMemberFunction, SourceLocation Loc,
+ SourceRange Range, VariadicCallType CallType) {
+ // FIXME: We should check as much as we can in the template definition.
+ if (CurContext->isDependentContext())
+ return;
+
+ // Printf and scanf checking.
+ llvm::SmallBitVector CheckedVarArgs;
+ if (FDecl) {
+ for (const auto *I : FDecl->specific_attrs<FormatMatchesAttr>()) {
+ // Only create vector if there are format attributes.
+ CheckedVarArgs.resize(Args.size());
+ CheckFormatString(I, Args, IsMemberFunction, CallType, Loc, Range,
+ CheckedVarArgs);
+ }
+
+ for (const auto *I : FDecl->specific_attrs<FormatAttr>()) {
+ CheckedVarArgs.resize(Args.size());
+ CheckFormatArguments(I, Args, IsMemberFunction, CallType, Loc, Range,
+ CheckedVarArgs);
+ }
+ }
+
+ // Refuse POD arguments that weren't caught by the format string
+ // checks above.
+ auto *FD = dyn_cast_or_null<FunctionDecl>(FDecl);
+ if (CallType != VariadicCallType::DoesNotApply &&
+ (!FD || FD->getBuiltinID() != Builtin::BI__noop)) {
+ unsigned NumParams = Proto ? Proto->getNumParams()
+ : isa_and_nonnull<FunctionDecl>(FDecl)
+ ? cast<FunctionDecl>(FDecl)->getNumParams()
+ : isa_and_nonnull<ObjCMethodDecl>(FDecl)
+ ? cast<ObjCMethodDecl>(FDecl)->param_size()
+ : 0;
+
+ for (unsigned ArgIdx = NumParams; ArgIdx < Args.size(); ++ArgIdx) {
+ // Args[ArgIdx] can be null in malformed code.
+ if (const Expr *Arg = Args[ArgIdx]) {
+ if (CheckedVarArgs.empty() || !CheckedVarArgs[ArgIdx])
+ checkVariadicArgument(Arg, CallType);
+ }
+ }
+ }
+ if (FD)
+ checkLifetimeCaptureBy(FD, IsMemberFunction, ThisArg, Args);
+ if (FDecl || Proto) {
+ CheckNonNullArguments(*this, FDecl, Proto, Args, Loc);
+
+ // Type safety checking.
+ if (FDecl) {
+ for (const auto *I : FDecl->specific_attrs<ArgumentWithTypeTagAttr>())
+ CheckArgumentWithTypeTag(I, Args, Loc);
+ }
+ }
+
+ // Check that passed arguments match the alignment of original arguments.
+ // Try to get the missing prototype from the declaration.
+ if (!Proto && FDecl) {
+ const auto *FT = FDecl->getFunctionType();
+ if (isa_and_nonnull<FunctionProtoType>(FT))
+ Proto = cast<FunctionProtoType>(FDecl->getFunctionType());
+ }
+ if (Proto) {
+ // For variadic functions, we may have more args than parameters.
+ // For some K&R functions, we may have less args than parameters.
+ const auto N = std::min<unsigned>(Proto->getNumParams(), Args.size());
+ bool IsScalableRet = Proto->getReturnType()->isSizelessVectorType();
+ bool IsScalableArg = false;
+ for (unsigned ArgIdx = 0; ArgIdx < N; ++ArgIdx) {
+ // Args[ArgIdx] can be null in malformed code.
+ if (const Expr *Arg = Args[ArgIdx]) {
+ if (Arg->containsErrors())
+ continue;
+
+ if (Context.getTargetInfo().getTriple().isOSAIX() && FDecl && Arg &&
+ FDecl->hasLinkage() &&
+ FDecl->getFormalLinkage() != Linkage::Internal &&
+ CallType == VariadicCallType::DoesNotApply)
+ PPC().checkAIXMemberAlignment((Arg->getExprLoc()), Arg);
+
+ QualType ParamTy = Proto->getParamType(ArgIdx);
+ if (ParamTy->isSizelessVectorType())
+ IsScalableArg = true;
+ QualType ArgTy = Arg->getType();
+ CheckArgAlignment(Arg->getExprLoc(), FDecl, std::to_string(ArgIdx + 1),
+ ArgTy, ParamTy);
+ }
+ }
+
+ // If the callee has an AArch64 SME attribute to indicate that it is an
+ // __arm_streaming function, then the caller requires SME to be available.
+ FunctionProtoType::ExtProtoInfo ExtInfo = Proto->getExtProtoInfo();
+ if (ExtInfo.AArch64SMEAttributes & FunctionType::SME_PStateSMEnabledMask) {
+ if (auto *CallerFD = dyn_cast<FunctionDecl>(CurContext)) {
+ llvm::StringMap<bool> CallerFeatureMap;
+ Context.getFunctionFeatureMap(CallerFeatureMap, CallerFD);
+ if (!CallerFeatureMap.contains("sme"))
+ Diag(Loc, diag::err_sme_call_in_non_sme_target);
+ } else if (!Context.getTargetInfo().hasFeature("sme")) {
+ Diag(Loc, diag::err_sme_call_in_non_sme_target);
+ }
+ }
+
+ // If the call requires a streaming-mode change and has scalable vector
+ // arguments or return values, then warn the user that the streaming and
+ // non-streaming vector lengths may be different.
+ // When both streaming and non-streaming vector lengths are defined and
+ // mismatched, produce an error.
+ const auto *CallerFD = dyn_cast<FunctionDecl>(CurContext);
+ if (CallerFD && (!FD || !FD->getBuiltinID()) &&
+ (IsScalableArg || IsScalableRet)) {
+ bool IsCalleeStreaming =
+ ExtInfo.AArch64SMEAttributes & FunctionType::SME_PStateSMEnabledMask;
+ bool IsCalleeStreamingCompatible =
+ ExtInfo.AArch64SMEAttributes &
+ FunctionType::SME_PStateSMCompatibleMask;
+ SemaARM::ArmStreamingType CallerFnType = getArmStreamingFnType(CallerFD);
+ if (!IsCalleeStreamingCompatible &&
+ (CallerFnType == SemaARM::ArmStreamingCompatible ||
+ ((CallerFnType == SemaARM::ArmStreaming) ^ IsCalleeStreaming))) {
+ const LangOptions &LO = getLangOpts();
+ unsigned VL = LO.VScaleMin * 128;
+ unsigned SVL = LO.VScaleStreamingMin * 128;
+ bool IsVLMismatch = VL && SVL && VL != SVL;
+
+ auto EmitDiag = [&](bool IsArg) {
+ if (IsVLMismatch) {
+ if (CallerFnType == SemaARM::ArmStreamingCompatible)
+ // Emit warning for streaming-compatible callers
+ Diag(Loc, diag::warn_sme_streaming_compatible_vl_mismatch)
+ << IsArg << IsCalleeStreaming << SVL << VL;
+ else
+ // Emit error otherwise
+ Diag(Loc, diag::err_sme_streaming_transition_vl_mismatch)
+ << IsArg << SVL << VL;
+ } else
+ Diag(Loc, diag::warn_sme_streaming_pass_return_vl_to_non_streaming)
+ << IsArg;
+ };
+
+ if (IsScalableArg)
+ EmitDiag(true);
+ if (IsScalableRet)
+ EmitDiag(false);
+ }
+ }
+
+ FunctionType::ArmStateValue CalleeArmZAState =
+ FunctionType::getArmZAState(ExtInfo.AArch64SMEAttributes);
+ FunctionType::ArmStateValue CalleeArmZT0State =
+ FunctionType::getArmZT0State(ExtInfo.AArch64SMEAttributes);
+ if (CalleeArmZAState != FunctionType::ARM_None ||
+ CalleeArmZT0State != FunctionType::ARM_None) {
+ bool CallerHasZAState = false;
+ bool CallerHasZT0State = false;
+ if (CallerFD) {
+ auto *Attr = CallerFD->getAttr<ArmNewAttr>();
+ if (Attr && Attr->isNewZA())
+ CallerHasZAState = true;
+ if (Attr && Attr->isNewZT0())
+ CallerHasZT0State = true;
+ if (const auto *FPT = CallerFD->getType()->getAs<FunctionProtoType>()) {
+ CallerHasZAState |=
+ FunctionType::getArmZAState(
+ FPT->getExtProtoInfo().AArch64SMEAttributes) !=
+ FunctionType::ARM_None;
+ CallerHasZT0State |=
+ FunctionType::getArmZT0State(
+ FPT->getExtProtoInfo().AArch64SMEAttributes) !=
+ FunctionType::ARM_None;
+ }
+ }
+
+ if (CalleeArmZAState != FunctionType::ARM_None && !CallerHasZAState)
+ Diag(Loc, diag::err_sme_za_call_no_za_state);
+
+ if (CalleeArmZT0State != FunctionType::ARM_None && !CallerHasZT0State)
+ Diag(Loc, diag::err_sme_zt0_call_no_zt0_state);
+
+ if (CallerHasZAState && CalleeArmZAState == FunctionType::ARM_None &&
+ CalleeArmZT0State != FunctionType::ARM_None) {
+ Diag(Loc, diag::err_sme_unimplemented_za_save_restore);
+ Diag(Loc, diag::note_sme_use_preserves_za);
+ }
+ }
+ }
+
+ if (FDecl && FDecl->hasAttr<AllocAlignAttr>()) {
+ auto *AA = FDecl->getAttr<AllocAlignAttr>();
+ const Expr *Arg = Args[AA->getParamIndex().getASTIndex()];
+ if (!Arg->isValueDependent()) {
+ Expr::EvalResult Align;
+ if (Arg->EvaluateAsInt(Align, Context)) {
+ const llvm::APSInt &I = Align.Val.getInt();
+ if (!I.isPowerOf2())
+ Diag(Arg->getExprLoc(), diag::warn_alignment_not_power_of_two)
+ << Arg->getSourceRange();
+
+ if (I > Sema::MaximumAlignment)
+ Diag(Arg->getExprLoc(), diag::warn_assume_aligned_too_great)
+ << Arg->getSourceRange() << Sema::MaximumAlignment;
+ }
+ }
+ }
+
+ if (FD)
+ diagnoseArgDependentDiagnoseIfAttrs(FD, ThisArg, Args, Loc);
+}
+
+void Sema::CheckConstrainedAuto(const AutoType *AutoT, SourceLocation Loc) {
+ if (TemplateDecl *Decl = AutoT->getTypeConstraintConcept()) {
+ DiagnoseUseOfDecl(Decl, Loc);
+ }
+}
+
+void Sema::CheckConstructorCall(FunctionDecl *FDecl, QualType ThisType,
+ ArrayRef<const Expr *> Args,
+ const FunctionProtoType *Proto,
+ SourceLocation Loc) {
+ VariadicCallType CallType = Proto->isVariadic()
+ ? VariadicCallType::Constructor
+ : VariadicCallType::DoesNotApply;
+
+ auto *Ctor = cast<CXXConstructorDecl>(FDecl);
+ CheckArgAlignment(
+ Loc, FDecl, "'this'", Context.getPointerType(ThisType),
+ Context.getPointerType(Ctor->getFunctionObjectParameterType()));
+
+ checkCall(FDecl, Proto, /*ThisArg=*/nullptr, Args, /*IsMemberFunction=*/true,
+ Loc, SourceRange(), CallType);
+}
+
+bool Sema::CheckFunctionCall(FunctionDecl *FDecl, CallExpr *TheCall,
+ const FunctionProtoType *Proto) {
+ bool IsMemberOperatorCall = isa<CXXOperatorCallExpr>(TheCall) &&
+ isa<CXXMethodDecl>(FDecl);
+ bool IsMemberFunction = isa<CXXMemberCallExpr>(TheCall) ||
+ IsMemberOperatorCall;
+ VariadicCallType CallType = getVariadicCallType(FDecl, Proto,
+ TheCall->getCallee());
+ Expr** Args = TheCall->getArgs();
+ unsigned NumArgs = TheCall->getNumArgs();
+
+ Expr *ImplicitThis = nullptr;
+ if (IsMemberOperatorCall && !FDecl->hasCXXExplicitFunctionObjectParameter()) {
+ // If this is a call to a member operator, hide the first
+ // argument from checkCall.
+ // FIXME: Our choice of AST representation here is less than ideal.
+ ImplicitThis = Args[0];
+ ++Args;
+ --NumArgs;
+ } else if (IsMemberFunction && !FDecl->isStatic() &&
+ !FDecl->hasCXXExplicitFunctionObjectParameter())
+ ImplicitThis =
+ cast<CXXMemberCallExpr>(TheCall)->getImplicitObjectArgument();
+
+ if (ImplicitThis) {
+ // ImplicitThis may or may not be a pointer, depending on whether . or -> is
+ // used.
+ QualType ThisType = ImplicitThis->getType();
+ if (!ThisType->isPointerType()) {
+ assert(!ThisType->isReferenceType());
+ ThisType = Context.getPointerType(ThisType);
+ }
+
+ QualType ThisTypeFromDecl = Context.getPointerType(
+ cast<CXXMethodDecl>(FDecl)->getFunctionObjectParameterType());
+
+ CheckArgAlignment(TheCall->getRParenLoc(), FDecl, "'this'", ThisType,
+ ThisTypeFromDecl);
+ }
+
+ checkCall(FDecl, Proto, ImplicitThis, llvm::ArrayRef(Args, NumArgs),
+ IsMemberFunction, TheCall->getRParenLoc(),
+ TheCall->getCallee()->getSourceRange(), CallType);
+
+ IdentifierInfo *FnInfo = FDecl->getIdentifier();
+ // None of the checks below are needed for functions that don't have
+ // simple names (e.g., C++ conversion functions).
+ if (!FnInfo)
+ return false;
+
+ // Enforce TCB except for builtin calls, which are always allowed.
+ if (FDecl->getBuiltinID() == 0)
+ CheckTCBEnforcement(TheCall->getExprLoc(), FDecl);
+
+ CheckAbsoluteValueFunction(TheCall, FDecl);
+ CheckMaxUnsignedZero(TheCall, FDecl);
+ CheckInfNaNFunction(TheCall, FDecl);
+
+ if (getLangOpts().ObjC)
+ ObjC().DiagnoseCStringFormatDirectiveInCFAPI(FDecl, Args, NumArgs);
+
+ unsigned CMId = FDecl->getMemoryFunctionKind();
+
+ // Handle memory setting and copying functions.
+ switch (CMId) {
+ case 0:
+ return false;
+ case Builtin::BIstrlcpy: // fallthrough
+ case Builtin::BIstrlcat:
+ CheckStrlcpycatArguments(TheCall, FnInfo);
+ break;
+ case Builtin::BIstrncat:
+ CheckStrncatArguments(TheCall, FnInfo);
+ break;
+ case Builtin::BIfree:
+ CheckFreeArguments(TheCall);
+ break;
+ default:
+ CheckMemaccessArguments(TheCall, CMId, FnInfo);
+ }
+
+ return false;
+}
+
+bool Sema::CheckPointerCall(NamedDecl *NDecl, CallExpr *TheCall,
+ const FunctionProtoType *Proto) {
+ QualType Ty;
+ if (const auto *V = dyn_cast<VarDecl>(NDecl))
+ Ty = V->getType().getNonReferenceType();
+ else if (const auto *F = dyn_cast<FieldDecl>(NDecl))
+ Ty = F->getType().getNonReferenceType();
+ else
+ return false;
+
+ if (!Ty->isBlockPointerType() && !Ty->isFunctionPointerType() &&
+ !Ty->isFunctionProtoType())
+ return false;
+
+ VariadicCallType CallType;
+ if (!Proto || !Proto->isVariadic()) {
+ CallType = VariadicCallType::DoesNotApply;
+ } else if (Ty->isBlockPointerType()) {
+ CallType = VariadicCallType::Block;
+ } else { // Ty->isFunctionPointerType()
+ CallType = VariadicCallType::Function;
+ }
+
+ checkCall(NDecl, Proto, /*ThisArg=*/nullptr,
+ llvm::ArrayRef(TheCall->getArgs(), TheCall->getNumArgs()),
+ /*IsMemberFunction=*/false, TheCall->getRParenLoc(),
+ TheCall->getCallee()->getSourceRange(), CallType);
+
+ return false;
+}
+
+bool Sema::CheckOtherCall(CallExpr *TheCall, const FunctionProtoType *Proto) {
+ VariadicCallType CallType = getVariadicCallType(/*FDecl=*/nullptr, Proto,
+ TheCall->getCallee());
+ checkCall(/*FDecl=*/nullptr, Proto, /*ThisArg=*/nullptr,
+ llvm::ArrayRef(TheCall->getArgs(), TheCall->getNumArgs()),
+ /*IsMemberFunction=*/false, TheCall->getRParenLoc(),
+ TheCall->getCallee()->getSourceRange(), CallType);
+
+ return false;
+}
+
+static bool isValidOrderingForOp(int64_t Ordering, AtomicExpr::AtomicOp Op) {
+ if (!llvm::isValidAtomicOrderingCABI(Ordering))
+ return false;
+
+ auto OrderingCABI = (llvm::AtomicOrderingCABI)Ordering;
+ switch (Op) {
+ case AtomicExpr::AO__c11_atomic_init:
+ case AtomicExpr::AO__opencl_atomic_init:
+ llvm_unreachable("There is no ordering argument for an init");
+
+ case AtomicExpr::AO__c11_atomic_load:
+ case AtomicExpr::AO__opencl_atomic_load:
+ case AtomicExpr::AO__hip_atomic_load:
+ case AtomicExpr::AO__atomic_load_n:
+ case AtomicExpr::AO__atomic_load:
+ case AtomicExpr::AO__scoped_atomic_load_n:
+ case AtomicExpr::AO__scoped_atomic_load:
+ return OrderingCABI != llvm::AtomicOrderingCABI::release &&
+ OrderingCABI != llvm::AtomicOrderingCABI::acq_rel;
+
+ case AtomicExpr::AO__c11_atomic_store:
+ case AtomicExpr::AO__opencl_atomic_store:
+ case AtomicExpr::AO__hip_atomic_store:
+ case AtomicExpr::AO__atomic_store:
+ case AtomicExpr::AO__atomic_store_n:
+ case AtomicExpr::AO__scoped_atomic_store:
+ case AtomicExpr::AO__scoped_atomic_store_n:
+ case AtomicExpr::AO__atomic_clear:
+ return OrderingCABI != llvm::AtomicOrderingCABI::consume &&
+ OrderingCABI != llvm::AtomicOrderingCABI::acquire &&
+ OrderingCABI != llvm::AtomicOrderingCABI::acq_rel;
+
+ default:
+ return true;
+ }
+}
+
+ExprResult Sema::AtomicOpsOverloaded(ExprResult TheCallResult,
+ AtomicExpr::AtomicOp Op) {
+ CallExpr *TheCall = cast<CallExpr>(TheCallResult.get());
+ DeclRefExpr *DRE =cast<DeclRefExpr>(TheCall->getCallee()->IgnoreParenCasts());
+ MultiExprArg Args{TheCall->getArgs(), TheCall->getNumArgs()};
+ return BuildAtomicExpr({TheCall->getBeginLoc(), TheCall->getEndLoc()},
+ DRE->getSourceRange(), TheCall->getRParenLoc(), Args,
+ Op);
+}
+
+ExprResult Sema::BuildAtomicExpr(SourceRange CallRange, SourceRange ExprRange,
+ SourceLocation RParenLoc, MultiExprArg Args,
+ AtomicExpr::AtomicOp Op,
+ AtomicArgumentOrder ArgOrder) {
+ // All the non-OpenCL operations take one of the following forms.
+ // The OpenCL operations take the __c11 forms with one extra argument for
+ // synchronization scope.
+ enum {
+ // C __c11_atomic_init(A *, C)
+ Init,
+
+ // C __c11_atomic_load(A *, int)
+ Load,
+
+ // void __atomic_load(A *, CP, int)
+ LoadCopy,
+
+ // void __atomic_store(A *, CP, int)
+ Copy,
+
+ // C __c11_atomic_add(A *, M, int)
+ Arithmetic,
+
+ // C __atomic_exchange_n(A *, CP, int)
+ Xchg,
+
+ // void __atomic_exchange(A *, C *, CP, int)
+ GNUXchg,
+
+ // bool __c11_atomic_compare_exchange_strong(A *, C *, CP, int, int)
+ C11CmpXchg,
+
+ // bool __atomic_compare_exchange(A *, C *, CP, bool, int, int)
+ GNUCmpXchg,
+
+ // bool __atomic_test_and_set(A *, int)
+ TestAndSetByte,
+
+ // void __atomic_clear(A *, int)
+ ClearByte,
+ } Form = Init;
+
+ const unsigned NumForm = ClearByte + 1;
+ const unsigned NumArgs[] = {2, 2, 3, 3, 3, 3, 4, 5, 6, 2, 2};
+ const unsigned NumVals[] = {1, 0, 1, 1, 1, 1, 2, 2, 3, 0, 0};
+ // where:
+ // C is an appropriate type,
+ // A is volatile _Atomic(C) for __c11 builtins and is C for GNU builtins,
+ // CP is C for __c11 builtins and GNU _n builtins and is C * otherwise,
+ // M is C if C is an integer, and ptrdiff_t if C is a pointer, and
+ // the int parameters are for orderings.
+
+ static_assert(sizeof(NumArgs)/sizeof(NumArgs[0]) == NumForm
+ && sizeof(NumVals)/sizeof(NumVals[0]) == NumForm,
+ "need to update code for modified forms");
+ static_assert(AtomicExpr::AO__atomic_add_fetch == 0 &&
+ AtomicExpr::AO__atomic_xor_fetch + 1 ==
+ AtomicExpr::AO__c11_atomic_compare_exchange_strong,
+ "need to update code for modified C11 atomics");
+ bool IsOpenCL = Op >= AtomicExpr::AO__opencl_atomic_compare_exchange_strong &&
+ Op <= AtomicExpr::AO__opencl_atomic_store;
+ bool IsHIP = Op >= AtomicExpr::AO__hip_atomic_compare_exchange_strong &&
+ Op <= AtomicExpr::AO__hip_atomic_store;
+ bool IsScoped = Op >= AtomicExpr::AO__scoped_atomic_add_fetch &&
+ Op <= AtomicExpr::AO__scoped_atomic_xor_fetch;
+ bool IsC11 = (Op >= AtomicExpr::AO__c11_atomic_compare_exchange_strong &&
+ Op <= AtomicExpr::AO__c11_atomic_store) ||
+ IsOpenCL;
+ bool IsN = Op == AtomicExpr::AO__atomic_load_n ||
+ Op == AtomicExpr::AO__atomic_store_n ||
+ Op == AtomicExpr::AO__atomic_exchange_n ||
+ Op == AtomicExpr::AO__atomic_compare_exchange_n ||
+ Op == AtomicExpr::AO__scoped_atomic_load_n ||
+ Op == AtomicExpr::AO__scoped_atomic_store_n ||
+ Op == AtomicExpr::AO__scoped_atomic_exchange_n ||
+ Op == AtomicExpr::AO__scoped_atomic_compare_exchange_n;
+ // Bit mask for extra allowed value types other than integers for atomic
+ // arithmetic operations. Add/sub allow pointer and floating point. Min/max
+ // allow floating point.
+ enum ArithOpExtraValueType {
+ AOEVT_None = 0,
+ AOEVT_Pointer = 1,
+ AOEVT_FP = 2,
+ };
+ unsigned ArithAllows = AOEVT_None;
+
+ switch (Op) {
+ case AtomicExpr::AO__c11_atomic_init:
+ case AtomicExpr::AO__opencl_atomic_init:
+ Form = Init;
+ break;
+
+ case AtomicExpr::AO__c11_atomic_load:
+ case AtomicExpr::AO__opencl_atomic_load:
+ case AtomicExpr::AO__hip_atomic_load:
+ case AtomicExpr::AO__atomic_load_n:
+ case AtomicExpr::AO__scoped_atomic_load_n:
+ Form = Load;
+ break;
+
+ case AtomicExpr::AO__atomic_load:
+ case AtomicExpr::AO__scoped_atomic_load:
+ Form = LoadCopy;
+ break;
+
+ case AtomicExpr::AO__c11_atomic_store:
+ case AtomicExpr::AO__opencl_atomic_store:
+ case AtomicExpr::AO__hip_atomic_store:
+ case AtomicExpr::AO__atomic_store:
+ case AtomicExpr::AO__atomic_store_n:
+ case AtomicExpr::AO__scoped_atomic_store:
+ case AtomicExpr::AO__scoped_atomic_store_n:
+ Form = Copy;
+ break;
+ case AtomicExpr::AO__atomic_fetch_add:
+ case AtomicExpr::AO__atomic_fetch_sub:
+ case AtomicExpr::AO__atomic_add_fetch:
+ case AtomicExpr::AO__atomic_sub_fetch:
+ case AtomicExpr::AO__scoped_atomic_fetch_add:
+ case AtomicExpr::AO__scoped_atomic_fetch_sub:
+ case AtomicExpr::AO__scoped_atomic_add_fetch:
+ case AtomicExpr::AO__scoped_atomic_sub_fetch:
+ case AtomicExpr::AO__c11_atomic_fetch_add:
+ case AtomicExpr::AO__c11_atomic_fetch_sub:
+ case AtomicExpr::AO__opencl_atomic_fetch_add:
+ case AtomicExpr::AO__opencl_atomic_fetch_sub:
+ case AtomicExpr::AO__hip_atomic_fetch_add:
+ case AtomicExpr::AO__hip_atomic_fetch_sub:
+ ArithAllows = AOEVT_Pointer | AOEVT_FP;
+ Form = Arithmetic;
+ break;
+ case AtomicExpr::AO__atomic_fetch_max:
+ case AtomicExpr::AO__atomic_fetch_min:
+ case AtomicExpr::AO__atomic_max_fetch:
+ case AtomicExpr::AO__atomic_min_fetch:
+ case AtomicExpr::AO__scoped_atomic_fetch_max:
+ case AtomicExpr::AO__scoped_atomic_fetch_min:
+ case AtomicExpr::AO__scoped_atomic_max_fetch:
+ case AtomicExpr::AO__scoped_atomic_min_fetch:
+ case AtomicExpr::AO__c11_atomic_fetch_max:
+ case AtomicExpr::AO__c11_atomic_fetch_min:
+ case AtomicExpr::AO__opencl_atomic_fetch_max:
+ case AtomicExpr::AO__opencl_atomic_fetch_min:
+ case AtomicExpr::AO__hip_atomic_fetch_max:
+ case AtomicExpr::AO__hip_atomic_fetch_min:
+ ArithAllows = AOEVT_FP;
+ Form = Arithmetic;
+ break;
+ case AtomicExpr::AO__c11_atomic_fetch_and:
+ case AtomicExpr::AO__c11_atomic_fetch_or:
+ case AtomicExpr::AO__c11_atomic_fetch_xor:
+ case AtomicExpr::AO__hip_atomic_fetch_and:
+ case AtomicExpr::AO__hip_atomic_fetch_or:
+ case AtomicExpr::AO__hip_atomic_fetch_xor:
+ case AtomicExpr::AO__c11_atomic_fetch_nand:
+ case AtomicExpr::AO__opencl_atomic_fetch_and:
+ case AtomicExpr::AO__opencl_atomic_fetch_or:
+ case AtomicExpr::AO__opencl_atomic_fetch_xor:
+ case AtomicExpr::AO__atomic_fetch_and:
+ case AtomicExpr::AO__atomic_fetch_or:
+ case AtomicExpr::AO__atomic_fetch_xor:
+ case AtomicExpr::AO__atomic_fetch_nand:
+ case AtomicExpr::AO__atomic_and_fetch:
+ case AtomicExpr::AO__atomic_or_fetch:
+ case AtomicExpr::AO__atomic_xor_fetch:
+ case AtomicExpr::AO__atomic_nand_fetch:
+ case AtomicExpr::AO__atomic_fetch_uinc:
+ case AtomicExpr::AO__atomic_fetch_udec:
+ case AtomicExpr::AO__scoped_atomic_fetch_and:
+ case AtomicExpr::AO__scoped_atomic_fetch_or:
+ case AtomicExpr::AO__scoped_atomic_fetch_xor:
+ case AtomicExpr::AO__scoped_atomic_fetch_nand:
+ case AtomicExpr::AO__scoped_atomic_and_fetch:
+ case AtomicExpr::AO__scoped_atomic_or_fetch:
+ case AtomicExpr::AO__scoped_atomic_xor_fetch:
+ case AtomicExpr::AO__scoped_atomic_nand_fetch:
+ case AtomicExpr::AO__scoped_atomic_fetch_uinc:
+ case AtomicExpr::AO__scoped_atomic_fetch_udec:
+ Form = Arithmetic;
+ break;
+
+ case AtomicExpr::AO__c11_atomic_exchange:
+ case AtomicExpr::AO__hip_atomic_exchange:
+ case AtomicExpr::AO__opencl_atomic_exchange:
+ case AtomicExpr::AO__atomic_exchange_n:
+ case AtomicExpr::AO__scoped_atomic_exchange_n:
+ Form = Xchg;
+ break;
+
+ case AtomicExpr::AO__atomic_exchange:
+ case AtomicExpr::AO__scoped_atomic_exchange:
+ Form = GNUXchg;
+ break;
+
+ case AtomicExpr::AO__c11_atomic_compare_exchange_strong:
+ case AtomicExpr::AO__c11_atomic_compare_exchange_weak:
+ case AtomicExpr::AO__hip_atomic_compare_exchange_strong:
+ case AtomicExpr::AO__opencl_atomic_compare_exchange_strong:
+ case AtomicExpr::AO__opencl_atomic_compare_exchange_weak:
+ case AtomicExpr::AO__hip_atomic_compare_exchange_weak:
+ Form = C11CmpXchg;
+ break;
+
+ case AtomicExpr::AO__atomic_compare_exchange:
+ case AtomicExpr::AO__atomic_compare_exchange_n:
+ case AtomicExpr::AO__scoped_atomic_compare_exchange:
+ case AtomicExpr::AO__scoped_atomic_compare_exchange_n:
+ Form = GNUCmpXchg;
+ break;
+
+ case AtomicExpr::AO__atomic_test_and_set:
+ Form = TestAndSetByte;
+ break;
+
+ case AtomicExpr::AO__atomic_clear:
+ Form = ClearByte;
+ break;
+ }
+
+ unsigned AdjustedNumArgs = NumArgs[Form];
+ if ((IsOpenCL || IsHIP || IsScoped) &&
+ Op != AtomicExpr::AO__opencl_atomic_init)
+ ++AdjustedNumArgs;
+ // Check we have the right number of arguments.
+ if (Args.size() < AdjustedNumArgs) {
+ Diag(CallRange.getEnd(), diag::err_typecheck_call_too_few_args)
+ << 0 << AdjustedNumArgs << static_cast<unsigned>(Args.size())
+ << /*is non object*/ 0 << ExprRange;
+ return ExprError();
+ } else if (Args.size() > AdjustedNumArgs) {
+ Diag(Args[AdjustedNumArgs]->getBeginLoc(),
+ diag::err_typecheck_call_too_many_args)
+ << 0 << AdjustedNumArgs << static_cast<unsigned>(Args.size())
+ << /*is non object*/ 0 << ExprRange;
+ return ExprError();
+ }
+
+ // Inspect the first argument of the atomic operation.
+ Expr *Ptr = Args[0];
+ ExprResult ConvertedPtr = DefaultFunctionArrayLvalueConversion(Ptr);
+ if (ConvertedPtr.isInvalid())
+ return ExprError();
+
+ Ptr = ConvertedPtr.get();
+ const PointerType *pointerType = Ptr->getType()->getAs<PointerType>();
+ if (!pointerType) {
+ Diag(ExprRange.getBegin(), diag::err_atomic_builtin_must_be_pointer)
+ << Ptr->getType() << 0 << Ptr->getSourceRange();
+ return ExprError();
+ }
+
+ // For a __c11 builtin, this should be a pointer to an _Atomic type.
+ QualType AtomTy = pointerType->getPointeeType(); // 'A'
+ QualType ValType = AtomTy; // 'C'
+ if (IsC11) {
+ if (!AtomTy->isAtomicType()) {
+ Diag(ExprRange.getBegin(), diag::err_atomic_op_needs_atomic)
+ << Ptr->getType() << Ptr->getSourceRange();
+ return ExprError();
+ }
+ if ((Form != Load && Form != LoadCopy && AtomTy.isConstQualified()) ||
+ AtomTy.getAddressSpace() == LangAS::opencl_constant) {
+ Diag(ExprRange.getBegin(), diag::err_atomic_op_needs_non_const_atomic)
+ << (AtomTy.isConstQualified() ? 0 : 1) << Ptr->getType()
+ << Ptr->getSourceRange();
+ return ExprError();
+ }
+ ValType = AtomTy->castAs<AtomicType>()->getValueType();
+ } else if (Form != Load && Form != LoadCopy) {
+ if (ValType.isConstQualified()) {
+ Diag(ExprRange.getBegin(), diag::err_atomic_op_needs_non_const_pointer)
+ << Ptr->getType() << Ptr->getSourceRange();
+ return ExprError();
+ }
+ }
+
+ if (Form != TestAndSetByte && Form != ClearByte) {
+ // Pointer to object of size zero is not allowed.
+ if (RequireCompleteType(Ptr->getBeginLoc(), AtomTy,
+ diag::err_incomplete_type))
+ return ExprError();
+
+ if (Context.getTypeInfoInChars(AtomTy).Width.isZero()) {
+ Diag(ExprRange.getBegin(), diag::err_atomic_builtin_must_be_pointer)
+ << Ptr->getType() << 1 << Ptr->getSourceRange();
+ return ExprError();
+ }
+ } else {
+ // The __atomic_clear and __atomic_test_and_set intrinsics accept any
+ // non-const pointer type, including void* and pointers to incomplete
+ // structs, but only access the first byte.
+ AtomTy = Context.CharTy;
+ AtomTy = AtomTy.withCVRQualifiers(
+ pointerType->getPointeeType().getCVRQualifiers());
+ QualType PointerQT = Context.getPointerType(AtomTy);
+ pointerType = PointerQT->getAs<PointerType>();
+ Ptr = ImpCastExprToType(Ptr, PointerQT, CK_BitCast).get();
+ ValType = AtomTy;
+ }
+
+ PointerAuthQualifier PointerAuth = AtomTy.getPointerAuth();
+ if (PointerAuth && PointerAuth.isAddressDiscriminated()) {
+ Diag(ExprRange.getBegin(),
+ diag::err_atomic_op_needs_non_address_discriminated_pointer)
+ << 0 << Ptr->getType() << Ptr->getSourceRange();
+ return ExprError();
+ }
+
+ // For an arithmetic operation, the implied arithmetic must be well-formed.
+ if (Form == Arithmetic) {
+ // GCC does not enforce these rules for GNU atomics, but we do to help catch
+ // trivial type errors.
+ auto IsAllowedValueType = [&](QualType ValType,
+ unsigned AllowedType) -> bool {
+ if (ValType->isIntegerType())
+ return true;
+ if (ValType->isPointerType())
+ return AllowedType & AOEVT_Pointer;
+ if (!(ValType->isFloatingType() && (AllowedType & AOEVT_FP)))
+ return false;
+ // LLVM Parser does not allow atomicrmw with x86_fp80 type.
+ if (ValType->isSpecificBuiltinType(BuiltinType::LongDouble) &&
+ &Context.getTargetInfo().getLongDoubleFormat() ==
+ &llvm::APFloat::x87DoubleExtended())
+ return false;
+ return true;
+ };
+ if (!IsAllowedValueType(ValType, ArithAllows)) {
+ auto DID = ArithAllows & AOEVT_FP
+ ? (ArithAllows & AOEVT_Pointer
+ ? diag::err_atomic_op_needs_atomic_int_ptr_or_fp
+ : diag::err_atomic_op_needs_atomic_int_or_fp)
+ : diag::err_atomic_op_needs_atomic_int;
+ Diag(ExprRange.getBegin(), DID)
+ << IsC11 << Ptr->getType() << Ptr->getSourceRange();
+ return ExprError();
+ }
+ if (IsC11 && ValType->isPointerType() &&
+ RequireCompleteType(Ptr->getBeginLoc(), ValType->getPointeeType(),
+ diag::err_incomplete_type)) {
+ return ExprError();
+ }
+ } else if (IsN && !ValType->isIntegerType() && !ValType->isPointerType()) {
+ // For __atomic_*_n operations, the value type must be a scalar integral or
+ // pointer type which is 1, 2, 4, 8 or 16 bytes in length.
+ Diag(ExprRange.getBegin(), diag::err_atomic_op_needs_atomic_int_or_ptr)
+ << IsC11 << Ptr->getType() << Ptr->getSourceRange();
+ return ExprError();
+ }
+
+ if (!IsC11 && !AtomTy.isTriviallyCopyableType(Context) &&
+ !AtomTy->isScalarType()) {
+ // For GNU atomics, require a trivially-copyable type. This is not part of
+ // the GNU atomics specification but we enforce it for consistency with
+ // other atomics which generally all require a trivially-copyable type. This
+ // is because atomics just copy bits.
+ Diag(ExprRange.getBegin(), diag::err_atomic_op_needs_trivial_copy)
+ << Ptr->getType() << Ptr->getSourceRange();
+ return ExprError();
+ }
+
+ switch (ValType.getObjCLifetime()) {
+ case Qualifiers::OCL_None:
+ case Qualifiers::OCL_ExplicitNone:
+ // okay
+ break;
+
+ case Qualifiers::OCL_Weak:
+ case Qualifiers::OCL_Strong:
+ case Qualifiers::OCL_Autoreleasing:
+ // FIXME: Can this happen? By this point, ValType should be known
+ // to be trivially copyable.
+ Diag(ExprRange.getBegin(), diag::err_arc_atomic_ownership)
+ << ValType << Ptr->getSourceRange();
+ return ExprError();
+ }
+
+ // All atomic operations have an overload which takes a pointer to a volatile
+ // 'A'. We shouldn't let the volatile-ness of the pointee-type inject itself
+ // into the result or the other operands. Similarly atomic_load takes a
+ // pointer to a const 'A'.
+ ValType.removeLocalVolatile();
+ ValType.removeLocalConst();
+ QualType ResultType = ValType;
+ if (Form == Copy || Form == LoadCopy || Form == GNUXchg || Form == Init ||
+ Form == ClearByte)
+ ResultType = Context.VoidTy;
+ else if (Form == C11CmpXchg || Form == GNUCmpXchg || Form == TestAndSetByte)
+ ResultType = Context.BoolTy;
+
+ // The type of a parameter passed 'by value'. In the GNU atomics, such
+ // arguments are actually passed as pointers.
+ QualType ByValType = ValType; // 'CP'
+ bool IsPassedByAddress = false;
+ if (!IsC11 && !IsHIP && !IsN) {
+ ByValType = Ptr->getType();
+ IsPassedByAddress = true;
+ }
+
+ SmallVector<Expr *, 5> APIOrderedArgs;
+ if (ArgOrder == Sema::AtomicArgumentOrder::AST) {
+ APIOrderedArgs.push_back(Args[0]);
+ switch (Form) {
+ case Init:
+ case Load:
+ APIOrderedArgs.push_back(Args[1]); // Val1/Order
+ break;
+ case LoadCopy:
+ case Copy:
+ case Arithmetic:
+ case Xchg:
+ APIOrderedArgs.push_back(Args[2]); // Val1
+ APIOrderedArgs.push_back(Args[1]); // Order
+ break;
+ case GNUXchg:
+ APIOrderedArgs.push_back(Args[2]); // Val1
+ APIOrderedArgs.push_back(Args[3]); // Val2
+ APIOrderedArgs.push_back(Args[1]); // Order
+ break;
+ case C11CmpXchg:
+ APIOrderedArgs.push_back(Args[2]); // Val1
+ APIOrderedArgs.push_back(Args[4]); // Val2
+ APIOrderedArgs.push_back(Args[1]); // Order
+ APIOrderedArgs.push_back(Args[3]); // OrderFail
+ break;
+ case GNUCmpXchg:
+ APIOrderedArgs.push_back(Args[2]); // Val1
+ APIOrderedArgs.push_back(Args[4]); // Val2
+ APIOrderedArgs.push_back(Args[5]); // Weak
+ APIOrderedArgs.push_back(Args[1]); // Order
+ APIOrderedArgs.push_back(Args[3]); // OrderFail
+ break;
+ case TestAndSetByte:
+ case ClearByte:
+ APIOrderedArgs.push_back(Args[1]); // Order
+ break;
+ }
+ } else
+ APIOrderedArgs.append(Args.begin(), Args.end());
+
+ // The first argument's non-CV pointer type is used to deduce the type of
+ // subsequent arguments, except for:
+ // - weak flag (always converted to bool)
+ // - memory order (always converted to int)
+ // - scope (always converted to int)
+ for (unsigned i = 0; i != APIOrderedArgs.size(); ++i) {
+ QualType Ty;
+ if (i < NumVals[Form] + 1) {
+ switch (i) {
+ case 0:
+ // The first argument is always a pointer. It has a fixed type.
+ // It is always dereferenced, a nullptr is undefined.
+ CheckNonNullArgument(*this, APIOrderedArgs[i], ExprRange.getBegin());
+ // Nothing else to do: we already know all we want about this pointer.
+ continue;
+ case 1:
+ // The second argument is the non-atomic operand. For arithmetic, this
+ // is always passed by value, and for a compare_exchange it is always
+ // passed by address. For the rest, GNU uses by-address and C11 uses
+ // by-value.
+ assert(Form != Load);
+ if (Form == Arithmetic && ValType->isPointerType())
+ Ty = Context.getPointerDiffType();
+ else if (Form == Init || Form == Arithmetic)
+ Ty = ValType;
+ else if (Form == Copy || Form == Xchg) {
+ if (IsPassedByAddress) {
+ // The value pointer is always dereferenced, a nullptr is undefined.
+ CheckNonNullArgument(*this, APIOrderedArgs[i],
+ ExprRange.getBegin());
+ }
+ Ty = ByValType;
+ } else {
+ Expr *ValArg = APIOrderedArgs[i];
+ // The value pointer is always dereferenced, a nullptr is undefined.
+ CheckNonNullArgument(*this, ValArg, ExprRange.getBegin());
+ LangAS AS = LangAS::Default;
+ // Keep address space of non-atomic pointer type.
+ if (const PointerType *PtrTy =
+ ValArg->getType()->getAs<PointerType>()) {
+ AS = PtrTy->getPointeeType().getAddressSpace();
+ }
+ Ty = Context.getPointerType(
+ Context.getAddrSpaceQualType(ValType.getUnqualifiedType(), AS));
+ }
+ break;
+ case 2:
+ // The third argument to compare_exchange / GNU exchange is the desired
+ // value, either by-value (for the C11 and *_n variant) or as a pointer.
+ if (IsPassedByAddress)
+ CheckNonNullArgument(*this, APIOrderedArgs[i], ExprRange.getBegin());
+ Ty = ByValType;
+ break;
+ case 3:
+ // The fourth argument to GNU compare_exchange is a 'weak' flag.
+ Ty = Context.BoolTy;
+ break;
+ }
+ } else {
+ // The order(s) and scope are always converted to int.
+ Ty = Context.IntTy;
+ }
+
+ InitializedEntity Entity =
+ InitializedEntity::InitializeParameter(Context, Ty, false);
+ ExprResult Arg = APIOrderedArgs[i];
+ Arg = PerformCopyInitialization(Entity, SourceLocation(), Arg);
+ if (Arg.isInvalid())
+ return true;
+ APIOrderedArgs[i] = Arg.get();
+ }
+
+ // Permute the arguments into a 'consistent' order.
+ SmallVector<Expr*, 5> SubExprs;
+ SubExprs.push_back(Ptr);
+ switch (Form) {
+ case Init:
+ // Note, AtomicExpr::getVal1() has a special case for this atomic.
+ SubExprs.push_back(APIOrderedArgs[1]); // Val1
+ break;
+ case Load:
+ case TestAndSetByte:
+ case ClearByte:
+ SubExprs.push_back(APIOrderedArgs[1]); // Order
+ break;
+ case LoadCopy:
+ case Copy:
+ case Arithmetic:
+ case Xchg:
+ SubExprs.push_back(APIOrderedArgs[2]); // Order
+ SubExprs.push_back(APIOrderedArgs[1]); // Val1
+ break;
+ case GNUXchg:
+ // Note, AtomicExpr::getVal2() has a special case for this atomic.
+ SubExprs.push_back(APIOrderedArgs[3]); // Order
+ SubExprs.push_back(APIOrderedArgs[1]); // Val1
+ SubExprs.push_back(APIOrderedArgs[2]); // Val2
+ break;
+ case C11CmpXchg:
+ SubExprs.push_back(APIOrderedArgs[3]); // Order
+ SubExprs.push_back(APIOrderedArgs[1]); // Val1
+ SubExprs.push_back(APIOrderedArgs[4]); // OrderFail
+ SubExprs.push_back(APIOrderedArgs[2]); // Val2
+ break;
+ case GNUCmpXchg:
+ SubExprs.push_back(APIOrderedArgs[4]); // Order
+ SubExprs.push_back(APIOrderedArgs[1]); // Val1
+ SubExprs.push_back(APIOrderedArgs[5]); // OrderFail
+ SubExprs.push_back(APIOrderedArgs[2]); // Val2
+ SubExprs.push_back(APIOrderedArgs[3]); // Weak
+ break;
+ }
+
+ // If the memory orders are constants, check they are valid.
+ if (SubExprs.size() >= 2 && Form != Init) {
+ std::optional<llvm::APSInt> Success =
+ SubExprs[1]->getIntegerConstantExpr(Context);
+ if (Success && !isValidOrderingForOp(Success->getSExtValue(), Op)) {
+ Diag(SubExprs[1]->getBeginLoc(),
+ diag::warn_atomic_op_has_invalid_memory_order)
+ << /*success=*/(Form == C11CmpXchg || Form == GNUCmpXchg)
+ << SubExprs[1]->getSourceRange();
+ }
+ if (SubExprs.size() >= 5) {
+ if (std::optional<llvm::APSInt> Failure =
+ SubExprs[3]->getIntegerConstantExpr(Context)) {
+ if (!llvm::is_contained(
+ {llvm::AtomicOrderingCABI::relaxed,
+ llvm::AtomicOrderingCABI::consume,
+ llvm::AtomicOrderingCABI::acquire,
+ llvm::AtomicOrderingCABI::seq_cst},
+ (llvm::AtomicOrderingCABI)Failure->getSExtValue())) {
+ Diag(SubExprs[3]->getBeginLoc(),
+ diag::warn_atomic_op_has_invalid_memory_order)
+ << /*failure=*/2 << SubExprs[3]->getSourceRange();
+ }
+ }
+ }
+ }
+
+ if (auto ScopeModel = AtomicExpr::getScopeModel(Op)) {
+ auto *Scope = Args[Args.size() - 1];
+ if (std::optional<llvm::APSInt> Result =
+ Scope->getIntegerConstantExpr(Context)) {
+ if (!ScopeModel->isValid(Result->getZExtValue()))
+ Diag(Scope->getBeginLoc(), diag::err_atomic_op_has_invalid_sync_scope)
+ << Scope->getSourceRange();
+ }
+ SubExprs.push_back(Scope);
+ }
+
+ AtomicExpr *AE = new (Context)
+ AtomicExpr(ExprRange.getBegin(), SubExprs, ResultType, Op, RParenLoc);
+
+ if ((Op == AtomicExpr::AO__c11_atomic_load ||
+ Op == AtomicExpr::AO__c11_atomic_store ||
+ Op == AtomicExpr::AO__opencl_atomic_load ||
+ Op == AtomicExpr::AO__hip_atomic_load ||
+ Op == AtomicExpr::AO__opencl_atomic_store ||
+ Op == AtomicExpr::AO__hip_atomic_store) &&
+ Context.AtomicUsesUnsupportedLibcall(AE))
+ Diag(AE->getBeginLoc(), diag::err_atomic_load_store_uses_lib)
+ << ((Op == AtomicExpr::AO__c11_atomic_load ||
+ Op == AtomicExpr::AO__opencl_atomic_load ||
+ Op == AtomicExpr::AO__hip_atomic_load)
+ ? 0
+ : 1);
+
+ if (ValType->isBitIntType()) {
+ Diag(Ptr->getExprLoc(), diag::err_atomic_builtin_bit_int_prohibit);
+ return ExprError();
+ }
+
+ return AE;
+}
+
+/// checkBuiltinArgument - Given a call to a builtin function, perform
+/// normal type-checking on the given argument, updating the call in
+/// place. This is useful when a builtin function requires custom
+/// type-checking for some of its arguments but not necessarily all of
+/// them.
+///
+/// Returns true on error.
+static bool checkBuiltinArgument(Sema &S, CallExpr *E, unsigned ArgIndex) {
+ FunctionDecl *Fn = E->getDirectCallee();
+ assert(Fn && "builtin call without direct callee!");
+
+ ParmVarDecl *Param = Fn->getParamDecl(ArgIndex);
+ InitializedEntity Entity =
+ InitializedEntity::InitializeParameter(S.Context, Param);
+
+ ExprResult Arg = E->getArg(ArgIndex);
+ Arg = S.PerformCopyInitialization(Entity, SourceLocation(), Arg);
+ if (Arg.isInvalid())
+ return true;
+
+ E->setArg(ArgIndex, Arg.get());
+ return false;
+}
+
+ExprResult Sema::BuiltinAtomicOverloaded(ExprResult TheCallResult) {
+ CallExpr *TheCall = static_cast<CallExpr *>(TheCallResult.get());
+ Expr *Callee = TheCall->getCallee();
+ DeclRefExpr *DRE = cast<DeclRefExpr>(Callee->IgnoreParenCasts());
+ FunctionDecl *FDecl = cast<FunctionDecl>(DRE->getDecl());
+
+ // Ensure that we have at least one argument to do type inference from.
+ if (TheCall->getNumArgs() < 1) {
+ Diag(TheCall->getEndLoc(), diag::err_typecheck_call_too_few_args_at_least)
+ << 0 << 1 << TheCall->getNumArgs() << /*is non object*/ 0
+ << Callee->getSourceRange();
+ return ExprError();
+ }
+
+ // Inspect the first argument of the atomic builtin. This should always be
+ // a pointer type, whose element is an integral scalar or pointer type.
+ // Because it is a pointer type, we don't have to worry about any implicit
+ // casts here.
+ // FIXME: We don't allow floating point scalars as input.
+ Expr *FirstArg = TheCall->getArg(0);
+ ExprResult FirstArgResult = DefaultFunctionArrayLvalueConversion(FirstArg);
+ if (FirstArgResult.isInvalid())
+ return ExprError();
+ FirstArg = FirstArgResult.get();
+ TheCall->setArg(0, FirstArg);
+
+ const PointerType *pointerType = FirstArg->getType()->getAs<PointerType>();
+ if (!pointerType) {
+ Diag(DRE->getBeginLoc(), diag::err_atomic_builtin_must_be_pointer)
+ << FirstArg->getType() << 0 << FirstArg->getSourceRange();
+ return ExprError();
+ }
+
+ QualType ValType = pointerType->getPointeeType();
+ if (!ValType->isIntegerType() && !ValType->isAnyPointerType() &&
+ !ValType->isBlockPointerType()) {
+ Diag(DRE->getBeginLoc(), diag::err_atomic_builtin_must_be_pointer_intptr)
+ << FirstArg->getType() << 0 << FirstArg->getSourceRange();
+ return ExprError();
+ }
+ PointerAuthQualifier PointerAuth = ValType.getPointerAuth();
+ if (PointerAuth && PointerAuth.isAddressDiscriminated()) {
+ Diag(FirstArg->getBeginLoc(),
+ diag::err_atomic_op_needs_non_address_discriminated_pointer)
+ << 1 << ValType << FirstArg->getSourceRange();
+ return ExprError();
+ }
+
+ if (ValType.isConstQualified()) {
+ Diag(DRE->getBeginLoc(), diag::err_atomic_builtin_cannot_be_const)
+ << FirstArg->getType() << FirstArg->getSourceRange();
+ return ExprError();
+ }
+
+ switch (ValType.getObjCLifetime()) {
+ case Qualifiers::OCL_None:
+ case Qualifiers::OCL_ExplicitNone:
+ // okay
+ break;
+
+ case Qualifiers::OCL_Weak:
+ case Qualifiers::OCL_Strong:
+ case Qualifiers::OCL_Autoreleasing:
+ Diag(DRE->getBeginLoc(), diag::err_arc_atomic_ownership)
+ << ValType << FirstArg->getSourceRange();
+ return ExprError();
+ }
+
+ // Strip any qualifiers off ValType.
+ ValType = ValType.getUnqualifiedType();
+
+ // The majority of builtins return a value, but a few have special return
+ // types, so allow them to override appropriately below.
+ QualType ResultType = ValType;
+
+ // We need to figure out which concrete builtin this maps onto. For example,
+ // __sync_fetch_and_add with a 2 byte object turns into
+ // __sync_fetch_and_add_2.
+#define BUILTIN_ROW(x) \
+ { Builtin::BI##x##_1, Builtin::BI##x##_2, Builtin::BI##x##_4, \
+ Builtin::BI##x##_8, Builtin::BI##x##_16 }
+
+ static const unsigned BuiltinIndices[][5] = {
+ BUILTIN_ROW(__sync_fetch_and_add),
+ BUILTIN_ROW(__sync_fetch_and_sub),
+ BUILTIN_ROW(__sync_fetch_and_or),
+ BUILTIN_ROW(__sync_fetch_and_and),
+ BUILTIN_ROW(__sync_fetch_and_xor),
+ BUILTIN_ROW(__sync_fetch_and_nand),
+
+ BUILTIN_ROW(__sync_add_and_fetch),
+ BUILTIN_ROW(__sync_sub_and_fetch),
+ BUILTIN_ROW(__sync_and_and_fetch),
+ BUILTIN_ROW(__sync_or_and_fetch),
+ BUILTIN_ROW(__sync_xor_and_fetch),
+ BUILTIN_ROW(__sync_nand_and_fetch),
+
+ BUILTIN_ROW(__sync_val_compare_and_swap),
+ BUILTIN_ROW(__sync_bool_compare_and_swap),
+ BUILTIN_ROW(__sync_lock_test_and_set),
+ BUILTIN_ROW(__sync_lock_release),
+ BUILTIN_ROW(__sync_swap)
+ };
+#undef BUILTIN_ROW
+
+ // Determine the index of the size.
+ unsigned SizeIndex;
+ switch (Context.getTypeSizeInChars(ValType).getQuantity()) {
+ case 1: SizeIndex = 0; break;
+ case 2: SizeIndex = 1; break;
+ case 4: SizeIndex = 2; break;
+ case 8: SizeIndex = 3; break;
+ case 16: SizeIndex = 4; break;
+ default:
+ Diag(DRE->getBeginLoc(), diag::err_atomic_builtin_pointer_size)
+ << FirstArg->getType() << FirstArg->getSourceRange();
+ return ExprError();
+ }
+
+ // Each of these builtins has one pointer argument, followed by some number of
+ // values (0, 1 or 2) followed by a potentially empty varags list of stuff
+ // that we ignore. Find out which row of BuiltinIndices to read from as well
+ // as the number of fixed args.
+ unsigned BuiltinID = FDecl->getBuiltinID();
+ unsigned BuiltinIndex, NumFixed = 1;
+ bool WarnAboutSemanticsChange = false;
+ switch (BuiltinID) {
+ default: llvm_unreachable("Unknown overloaded atomic builtin!");
+ case Builtin::BI__sync_fetch_and_add:
+ case Builtin::BI__sync_fetch_and_add_1:
+ case Builtin::BI__sync_fetch_and_add_2:
+ case Builtin::BI__sync_fetch_and_add_4:
+ case Builtin::BI__sync_fetch_and_add_8:
+ case Builtin::BI__sync_fetch_and_add_16:
+ BuiltinIndex = 0;
+ break;
+
+ case Builtin::BI__sync_fetch_and_sub:
+ case Builtin::BI__sync_fetch_and_sub_1:
+ case Builtin::BI__sync_fetch_and_sub_2:
+ case Builtin::BI__sync_fetch_and_sub_4:
+ case Builtin::BI__sync_fetch_and_sub_8:
+ case Builtin::BI__sync_fetch_and_sub_16:
+ BuiltinIndex = 1;
+ break;
+
+ case Builtin::BI__sync_fetch_and_or:
+ case Builtin::BI__sync_fetch_and_or_1:
+ case Builtin::BI__sync_fetch_and_or_2:
+ case Builtin::BI__sync_fetch_and_or_4:
+ case Builtin::BI__sync_fetch_and_or_8:
+ case Builtin::BI__sync_fetch_and_or_16:
+ BuiltinIndex = 2;
+ break;
+
+ case Builtin::BI__sync_fetch_and_and:
+ case Builtin::BI__sync_fetch_and_and_1:
+ case Builtin::BI__sync_fetch_and_and_2:
+ case Builtin::BI__sync_fetch_and_and_4:
+ case Builtin::BI__sync_fetch_and_and_8:
+ case Builtin::BI__sync_fetch_and_and_16:
+ BuiltinIndex = 3;
+ break;
+
+ case Builtin::BI__sync_fetch_and_xor:
+ case Builtin::BI__sync_fetch_and_xor_1:
+ case Builtin::BI__sync_fetch_and_xor_2:
+ case Builtin::BI__sync_fetch_and_xor_4:
+ case Builtin::BI__sync_fetch_and_xor_8:
+ case Builtin::BI__sync_fetch_and_xor_16:
+ BuiltinIndex = 4;
+ break;
+
+ case Builtin::BI__sync_fetch_and_nand:
+ case Builtin::BI__sync_fetch_and_nand_1:
+ case Builtin::BI__sync_fetch_and_nand_2:
+ case Builtin::BI__sync_fetch_and_nand_4:
+ case Builtin::BI__sync_fetch_and_nand_8:
+ case Builtin::BI__sync_fetch_and_nand_16:
+ BuiltinIndex = 5;
+ WarnAboutSemanticsChange = true;
+ break;
+
+ case Builtin::BI__sync_add_and_fetch:
+ case Builtin::BI__sync_add_and_fetch_1:
+ case Builtin::BI__sync_add_and_fetch_2:
+ case Builtin::BI__sync_add_and_fetch_4:
+ case Builtin::BI__sync_add_and_fetch_8:
+ case Builtin::BI__sync_add_and_fetch_16:
+ BuiltinIndex = 6;
+ break;
+
+ case Builtin::BI__sync_sub_and_fetch:
+ case Builtin::BI__sync_sub_and_fetch_1:
+ case Builtin::BI__sync_sub_and_fetch_2:
+ case Builtin::BI__sync_sub_and_fetch_4:
+ case Builtin::BI__sync_sub_and_fetch_8:
+ case Builtin::BI__sync_sub_and_fetch_16:
+ BuiltinIndex = 7;
+ break;
+
+ case Builtin::BI__sync_and_and_fetch:
+ case Builtin::BI__sync_and_and_fetch_1:
+ case Builtin::BI__sync_and_and_fetch_2:
+ case Builtin::BI__sync_and_and_fetch_4:
+ case Builtin::BI__sync_and_and_fetch_8:
+ case Builtin::BI__sync_and_and_fetch_16:
+ BuiltinIndex = 8;
+ break;
+
+ case Builtin::BI__sync_or_and_fetch:
+ case Builtin::BI__sync_or_and_fetch_1:
+ case Builtin::BI__sync_or_and_fetch_2:
+ case Builtin::BI__sync_or_and_fetch_4:
+ case Builtin::BI__sync_or_and_fetch_8:
+ case Builtin::BI__sync_or_and_fetch_16:
+ BuiltinIndex = 9;
+ break;
+
+ case Builtin::BI__sync_xor_and_fetch:
+ case Builtin::BI__sync_xor_and_fetch_1:
+ case Builtin::BI__sync_xor_and_fetch_2:
+ case Builtin::BI__sync_xor_and_fetch_4:
+ case Builtin::BI__sync_xor_and_fetch_8:
+ case Builtin::BI__sync_xor_and_fetch_16:
+ BuiltinIndex = 10;
+ break;
+
+ case Builtin::BI__sync_nand_and_fetch:
+ case Builtin::BI__sync_nand_and_fetch_1:
+ case Builtin::BI__sync_nand_and_fetch_2:
+ case Builtin::BI__sync_nand_and_fetch_4:
+ case Builtin::BI__sync_nand_and_fetch_8:
+ case Builtin::BI__sync_nand_and_fetch_16:
+ BuiltinIndex = 11;
+ WarnAboutSemanticsChange = true;
+ break;
+
+ case Builtin::BI__sync_val_compare_and_swap:
+ case Builtin::BI__sync_val_compare_and_swap_1:
+ case Builtin::BI__sync_val_compare_and_swap_2:
+ case Builtin::BI__sync_val_compare_and_swap_4:
+ case Builtin::BI__sync_val_compare_and_swap_8:
+ case Builtin::BI__sync_val_compare_and_swap_16:
+ BuiltinIndex = 12;
+ NumFixed = 2;
+ break;
+
+ case Builtin::BI__sync_bool_compare_and_swap:
+ case Builtin::BI__sync_bool_compare_and_swap_1:
+ case Builtin::BI__sync_bool_compare_and_swap_2:
+ case Builtin::BI__sync_bool_compare_and_swap_4:
+ case Builtin::BI__sync_bool_compare_and_swap_8:
+ case Builtin::BI__sync_bool_compare_and_swap_16:
+ BuiltinIndex = 13;
+ NumFixed = 2;
+ ResultType = Context.BoolTy;
+ break;
+
+ case Builtin::BI__sync_lock_test_and_set:
+ case Builtin::BI__sync_lock_test_and_set_1:
+ case Builtin::BI__sync_lock_test_and_set_2:
+ case Builtin::BI__sync_lock_test_and_set_4:
+ case Builtin::BI__sync_lock_test_and_set_8:
+ case Builtin::BI__sync_lock_test_and_set_16:
+ BuiltinIndex = 14;
+ break;
+
+ case Builtin::BI__sync_lock_release:
+ case Builtin::BI__sync_lock_release_1:
+ case Builtin::BI__sync_lock_release_2:
+ case Builtin::BI__sync_lock_release_4:
+ case Builtin::BI__sync_lock_release_8:
+ case Builtin::BI__sync_lock_release_16:
+ BuiltinIndex = 15;
+ NumFixed = 0;
+ ResultType = Context.VoidTy;
+ break;
+
+ case Builtin::BI__sync_swap:
+ case Builtin::BI__sync_swap_1:
+ case Builtin::BI__sync_swap_2:
+ case Builtin::BI__sync_swap_4:
+ case Builtin::BI__sync_swap_8:
+ case Builtin::BI__sync_swap_16:
+ BuiltinIndex = 16;
+ break;
+ }
+
+ // Now that we know how many fixed arguments we expect, first check that we
+ // have at least that many.
+ if (TheCall->getNumArgs() < 1+NumFixed) {
+ Diag(TheCall->getEndLoc(), diag::err_typecheck_call_too_few_args_at_least)
+ << 0 << 1 + NumFixed << TheCall->getNumArgs() << /*is non object*/ 0
+ << Callee->getSourceRange();
+ return ExprError();
+ }
+
+ Diag(TheCall->getEndLoc(), diag::warn_atomic_implicit_seq_cst)
+ << Callee->getSourceRange();
+
+ if (WarnAboutSemanticsChange) {
+ Diag(TheCall->getEndLoc(), diag::warn_sync_fetch_and_nand_semantics_change)
+ << Callee->getSourceRange();
+ }
+
+ // Get the decl for the concrete builtin from this, we can tell what the
+ // concrete integer type we should convert to is.
+ unsigned NewBuiltinID = BuiltinIndices[BuiltinIndex][SizeIndex];
+ std::string NewBuiltinName = Context.BuiltinInfo.getName(NewBuiltinID);
+ FunctionDecl *NewBuiltinDecl;
+ if (NewBuiltinID == BuiltinID)
+ NewBuiltinDecl = FDecl;
+ else {
+ // Perform builtin lookup to avoid redeclaring it.
+ DeclarationName DN(&Context.Idents.get(NewBuiltinName));
+ LookupResult Res(*this, DN, DRE->getBeginLoc(), LookupOrdinaryName);
+ LookupName(Res, TUScope, /*AllowBuiltinCreation=*/true);
+ assert(Res.getFoundDecl());
+ NewBuiltinDecl = dyn_cast<FunctionDecl>(Res.getFoundDecl());
+ if (!NewBuiltinDecl)
+ return ExprError();
+ }
+
+ // The first argument --- the pointer --- has a fixed type; we
+ // deduce the types of the rest of the arguments accordingly. Walk
+ // the remaining arguments, converting them to the deduced value type.
+ for (unsigned i = 0; i != NumFixed; ++i) {
+ ExprResult Arg = TheCall->getArg(i+1);
+
+ // GCC does an implicit conversion to the pointer or integer ValType. This
+ // can fail in some cases (1i -> int**), check for this error case now.
+ // Initialize the argument.
+ InitializedEntity Entity = InitializedEntity::InitializeParameter(Context,
+ ValType, /*consume*/ false);
+ Arg = PerformCopyInitialization(Entity, SourceLocation(), Arg);
+ if (Arg.isInvalid())
+ return ExprError();
+
+ // Okay, we have something that *can* be converted to the right type. Check
+ // to see if there is a potentially weird extension going on here. This can
+ // happen when you do an atomic operation on something like an char* and
+ // pass in 42. The 42 gets converted to char. This is even more strange
+ // for things like 45.123 -> char, etc.
+ // FIXME: Do this check.
+ TheCall->setArg(i+1, Arg.get());
+ }
+
+ // Create a new DeclRefExpr to refer to the new decl.
+ DeclRefExpr *NewDRE = DeclRefExpr::Create(
+ Context, DRE->getQualifierLoc(), SourceLocation(), NewBuiltinDecl,
+ /*enclosing*/ false, DRE->getLocation(), Context.BuiltinFnTy,
+ DRE->getValueKind(), nullptr, nullptr, DRE->isNonOdrUse());
+
+ // Set the callee in the CallExpr.
+ // FIXME: This loses syntactic information.
+ QualType CalleePtrTy = Context.getPointerType(NewBuiltinDecl->getType());
+ ExprResult PromotedCall = ImpCastExprToType(NewDRE, CalleePtrTy,
+ CK_BuiltinFnToFnPtr);
+ TheCall->setCallee(PromotedCall.get());
+
+ // Change the result type of the call to match the original value type. This
+ // is arbitrary, but the codegen for these builtins ins design to handle it
+ // gracefully.
+ TheCall->setType(ResultType);
+
+ // Prohibit problematic uses of bit-precise integer types with atomic
+ // builtins. The arguments would have already been converted to the first
+ // argument's type, so only need to check the first argument.
+ const auto *BitIntValType = ValType->getAs<BitIntType>();
+ if (BitIntValType && !llvm::isPowerOf2_64(BitIntValType->getNumBits())) {
+ Diag(FirstArg->getExprLoc(), diag::err_atomic_builtin_ext_int_size);
+ return ExprError();
+ }
+
+ return TheCallResult;
+}
+
+ExprResult Sema::BuiltinNontemporalOverloaded(ExprResult TheCallResult) {
+ CallExpr *TheCall = (CallExpr *)TheCallResult.get();
+ DeclRefExpr *DRE =
+ cast<DeclRefExpr>(TheCall->getCallee()->IgnoreParenCasts());
+ FunctionDecl *FDecl = cast<FunctionDecl>(DRE->getDecl());
+ unsigned BuiltinID = FDecl->getBuiltinID();
+ assert((BuiltinID == Builtin::BI__builtin_nontemporal_store ||
+ BuiltinID == Builtin::BI__builtin_nontemporal_load) &&
+ "Unexpected nontemporal load/store builtin!");
+ bool isStore = BuiltinID == Builtin::BI__builtin_nontemporal_store;
+ unsigned numArgs = isStore ? 2 : 1;
+
+ // Ensure that we have the proper number of arguments.
+ if (checkArgCount(TheCall, numArgs))
+ return ExprError();
+
+ // Inspect the last argument of the nontemporal builtin. This should always
+ // be a pointer type, from which we imply the type of the memory access.
+ // Because it is a pointer type, we don't have to worry about any implicit
+ // casts here.
+ Expr *PointerArg = TheCall->getArg(numArgs - 1);
+ ExprResult PointerArgResult =
+ DefaultFunctionArrayLvalueConversion(PointerArg);
+
+ if (PointerArgResult.isInvalid())
+ return ExprError();
+ PointerArg = PointerArgResult.get();
+ TheCall->setArg(numArgs - 1, PointerArg);
+
+ const PointerType *pointerType = PointerArg->getType()->getAs<PointerType>();
+ if (!pointerType) {
+ Diag(DRE->getBeginLoc(), diag::err_nontemporal_builtin_must_be_pointer)
+ << PointerArg->getType() << PointerArg->getSourceRange();
+ return ExprError();
+ }
+
+ QualType ValType = pointerType->getPointeeType();
+
+ // Strip any qualifiers off ValType.
+ ValType = ValType.getUnqualifiedType();
+ if (!ValType->isIntegerType() && !ValType->isAnyPointerType() &&
+ !ValType->isBlockPointerType() && !ValType->isFloatingType() &&
+ !ValType->isVectorType()) {
+ Diag(DRE->getBeginLoc(),
+ diag::err_nontemporal_builtin_must_be_pointer_intfltptr_or_vector)
+ << PointerArg->getType() << PointerArg->getSourceRange();
+ return ExprError();
+ }
+
+ if (!isStore) {
+ TheCall->setType(ValType);
+ return TheCallResult;
+ }
+
+ ExprResult ValArg = TheCall->getArg(0);
+ InitializedEntity Entity = InitializedEntity::InitializeParameter(
+ Context, ValType, /*consume*/ false);
+ ValArg = PerformCopyInitialization(Entity, SourceLocation(), ValArg);
+ if (ValArg.isInvalid())
+ return ExprError();
+
+ TheCall->setArg(0, ValArg.get());
+ TheCall->setType(Context.VoidTy);
+ return TheCallResult;
+}
+
+/// CheckObjCString - Checks that the format string argument to the os_log()
+/// and os_trace() functions is correct, and converts it to const char *.
+ExprResult Sema::CheckOSLogFormatStringArg(Expr *Arg) {
+ Arg = Arg->IgnoreParenCasts();
+ auto *Literal = dyn_cast<StringLiteral>(Arg);
+ if (!Literal) {
+ if (auto *ObjcLiteral = dyn_cast<ObjCStringLiteral>(Arg)) {
+ Literal = ObjcLiteral->getString();
+ }
+ }
+
+ if (!Literal || (!Literal->isOrdinary() && !Literal->isUTF8())) {
+ return ExprError(
+ Diag(Arg->getBeginLoc(), diag::err_os_log_format_not_string_constant)
+ << Arg->getSourceRange());
+ }
+
+ ExprResult Result(Literal);
+ QualType ResultTy = Context.getPointerType(Context.CharTy.withConst());
+ InitializedEntity Entity =
+ InitializedEntity::InitializeParameter(Context, ResultTy, false);
+ Result = PerformCopyInitialization(Entity, SourceLocation(), Result);
+ return Result;
+}
+
+/// Check that the user is calling the appropriate va_start builtin for the
+/// target and calling convention.
+static bool checkVAStartABI(Sema &S, unsigned BuiltinID, Expr *Fn) {
+ const llvm::Triple &TT = S.Context.getTargetInfo().getTriple();
+ bool IsX64 = TT.getArch() == llvm::Triple::x86_64;
+ bool IsAArch64 = (TT.getArch() == llvm::Triple::aarch64 ||
+ TT.getArch() == llvm::Triple::aarch64_32);
+ bool IsWindowsOrUEFI = TT.isOSWindows() || TT.isUEFI();
+ bool IsMSVAStart = BuiltinID == Builtin::BI__builtin_ms_va_start;
+ if (IsX64 || IsAArch64) {
+ CallingConv CC = CC_C;
+ if (const FunctionDecl *FD = S.getCurFunctionDecl())
+ CC = FD->getType()->castAs<FunctionType>()->getCallConv();
+ if (IsMSVAStart) {
+ // Don't allow this in System V ABI functions.
+ if (CC == CC_X86_64SysV || (!IsWindowsOrUEFI && CC != CC_Win64))
+ return S.Diag(Fn->getBeginLoc(),
+ diag::err_ms_va_start_used_in_sysv_function);
+ } else {
+ // On x86-64/AArch64 Unix, don't allow this in Win64 ABI functions.
+ // On x64 Windows, don't allow this in System V ABI functions.
+ // (Yes, that means there's no corresponding way to support variadic
+ // System V ABI functions on Windows.)
+ if ((IsWindowsOrUEFI && CC == CC_X86_64SysV) ||
+ (!IsWindowsOrUEFI && CC == CC_Win64))
+ return S.Diag(Fn->getBeginLoc(),
+ diag::err_va_start_used_in_wrong_abi_function)
+ << !IsWindowsOrUEFI;
+ }
+ return false;
+ }
+
+ if (IsMSVAStart)
+ return S.Diag(Fn->getBeginLoc(), diag::err_builtin_x64_aarch64_only);
+ return false;
+}
+
+static bool checkVAStartIsInVariadicFunction(Sema &S, Expr *Fn,
+ ParmVarDecl **LastParam = nullptr) {
+ // Determine whether the current function, block, or obj-c method is variadic
+ // and get its parameter list.
+ bool IsVariadic = false;
+ ArrayRef<ParmVarDecl *> Params;
+ DeclContext *Caller = S.CurContext;
+ if (auto *Block = dyn_cast<BlockDecl>(Caller)) {
+ IsVariadic = Block->isVariadic();
+ Params = Block->parameters();
+ } else if (auto *FD = dyn_cast<FunctionDecl>(Caller)) {
+ IsVariadic = FD->isVariadic();
+ Params = FD->parameters();
+ } else if (auto *MD = dyn_cast<ObjCMethodDecl>(Caller)) {
+ IsVariadic = MD->isVariadic();
+ // FIXME: This isn't correct for methods (results in bogus warning).
+ Params = MD->parameters();
+ } else if (isa<CapturedDecl>(Caller)) {
+ // We don't support va_start in a CapturedDecl.
+ S.Diag(Fn->getBeginLoc(), diag::err_va_start_captured_stmt);
+ return true;
+ } else {
+ // This must be some other declcontext that parses exprs.
+ S.Diag(Fn->getBeginLoc(), diag::err_va_start_outside_function);
+ return true;
+ }
+
+ if (!IsVariadic) {
+ S.Diag(Fn->getBeginLoc(), diag::err_va_start_fixed_function);
+ return true;
+ }
+
+ if (LastParam)
+ *LastParam = Params.empty() ? nullptr : Params.back();
+
+ return false;
+}
+
+bool Sema::BuiltinVAStart(unsigned BuiltinID, CallExpr *TheCall) {
+ Expr *Fn = TheCall->getCallee();
+ if (checkVAStartABI(*this, BuiltinID, Fn))
+ return true;
+
+ if (BuiltinID == Builtin::BI__builtin_c23_va_start) {
+ // This builtin requires one argument (the va_list), allows two arguments,
+ // but diagnoses more than two arguments. e.g.,
+ // __builtin_c23_va_start(); // error
+ // __builtin_c23_va_start(list); // ok
+ // __builtin_c23_va_start(list, param); // ok
+ // __builtin_c23_va_start(list, anything, anything); // error
+ // This differs from the GCC behavior in that they accept the last case
+ // with a warning, but it doesn't seem like a useful behavior to allow.
+ if (checkArgCountRange(TheCall, 1, 2))
+ return true;
+ } else {
+ // In C23 mode, va_start only needs one argument. However, the builtin still
+ // requires two arguments (which matches the behavior of the GCC builtin),
+ // <stdarg.h> passes `0` as the second argument in C23 mode.
+ if (checkArgCount(TheCall, 2))
+ return true;
+ }
+
+ // Type-check the first argument normally.
+ if (checkBuiltinArgument(*this, TheCall, 0))
+ return true;
+
+ // Check that the current function is variadic, and get its last parameter.
+ ParmVarDecl *LastParam;
+ if (checkVAStartIsInVariadicFunction(*this, Fn, &LastParam))
+ return true;
+
+ // Verify that the second argument to the builtin is the last non-variadic
+ // argument of the current function or method. In C23 mode, if the call is
+ // not to __builtin_c23_va_start, and the second argument is an integer
+ // constant expression with value 0, then we don't bother with this check.
+ // For __builtin_c23_va_start, we only perform the check for the second
+ // argument being the last argument to the current function if there is a
+ // second argument present.
+ if (BuiltinID == Builtin::BI__builtin_c23_va_start &&
+ TheCall->getNumArgs() < 2) {
+ Diag(TheCall->getExprLoc(), diag::warn_c17_compat_va_start_one_arg);
+ return false;
+ }
+
+ const Expr *Arg = TheCall->getArg(1)->IgnoreParenCasts();
+ if (std::optional<llvm::APSInt> Val =
+ TheCall->getArg(1)->getIntegerConstantExpr(Context);
+ Val && LangOpts.C23 && *Val == 0 &&
+ BuiltinID != Builtin::BI__builtin_c23_va_start) {
+ Diag(TheCall->getExprLoc(), diag::warn_c17_compat_va_start_one_arg);
+ return false;
+ }
+
+ // These are valid if SecondArgIsLastNonVariadicArgument is false after the
+ // next block.
+ QualType Type;
+ SourceLocation ParamLoc;
+ bool IsCRegister = false;
+ bool SecondArgIsLastNonVariadicArgument = false;
+ if (const DeclRefExpr *DR = dyn_cast<DeclRefExpr>(Arg)) {
+ if (const ParmVarDecl *PV = dyn_cast<ParmVarDecl>(DR->getDecl())) {
+ SecondArgIsLastNonVariadicArgument = PV == LastParam;
+
+ Type = PV->getType();
+ ParamLoc = PV->getLocation();
+ IsCRegister =
+ PV->getStorageClass() == SC_Register && !getLangOpts().CPlusPlus;
+ }
+ }
+
+ if (!SecondArgIsLastNonVariadicArgument)
+ Diag(TheCall->getArg(1)->getBeginLoc(),
+ diag::warn_second_arg_of_va_start_not_last_non_variadic_param);
+ else if (IsCRegister || Type->isReferenceType() ||
+ Type->isSpecificBuiltinType(BuiltinType::Float) || [=] {
+ // Promotable integers are UB, but enumerations need a bit of
+ // extra checking to see what their promotable type actually is.
+ if (!Context.isPromotableIntegerType(Type))
+ return false;
+ const auto *ED = Type->getAsEnumDecl();
+ if (!ED)
+ return true;
+ return !Context.typesAreCompatible(ED->getPromotionType(), Type);
+ }()) {
+ unsigned Reason = 0;
+ if (Type->isReferenceType()) Reason = 1;
+ else if (IsCRegister) Reason = 2;
+ Diag(Arg->getBeginLoc(), diag::warn_va_start_type_is_undefined) << Reason;
+ Diag(ParamLoc, diag::note_parameter_type) << Type;
+ }
+
+ return false;
+}
+
+bool Sema::BuiltinVAStartARMMicrosoft(CallExpr *Call) {
+ auto IsSuitablyTypedFormatArgument = [this](const Expr *Arg) -> bool {
+ const LangOptions &LO = getLangOpts();
+
+ if (LO.CPlusPlus)
+ return Arg->getType()
+ .getCanonicalType()
+ .getTypePtr()
+ ->getPointeeType()
+ .withoutLocalFastQualifiers() == Context.CharTy;
+
+ // In C, allow aliasing through `char *`, this is required for AArch64 at
+ // least.
+ return true;
+ };
+
+ // void __va_start(va_list *ap, const char *named_addr, size_t slot_size,
+ // const char *named_addr);
+
+ Expr *Func = Call->getCallee();
+
+ if (Call->getNumArgs() < 3)
+ return Diag(Call->getEndLoc(),
+ diag::err_typecheck_call_too_few_args_at_least)
+ << 0 /*function call*/ << 3 << Call->getNumArgs()
+ << /*is non object*/ 0;
+
+ // Type-check the first argument normally.
+ if (checkBuiltinArgument(*this, Call, 0))
+ return true;
+
+ // Check that the current function is variadic.
+ if (checkVAStartIsInVariadicFunction(*this, Func))
+ return true;
+
+ // __va_start on Windows does not validate the parameter qualifiers
+
+ const Expr *Arg1 = Call->getArg(1)->IgnoreParens();
+ const Type *Arg1Ty = Arg1->getType().getCanonicalType().getTypePtr();
+
+ const Expr *Arg2 = Call->getArg(2)->IgnoreParens();
+ const Type *Arg2Ty = Arg2->getType().getCanonicalType().getTypePtr();
+
+ const QualType &ConstCharPtrTy =
+ Context.getPointerType(Context.CharTy.withConst());
+ if (!Arg1Ty->isPointerType() || !IsSuitablyTypedFormatArgument(Arg1))
+ Diag(Arg1->getBeginLoc(), diag::err_typecheck_convert_incompatible)
+ << Arg1->getType() << ConstCharPtrTy << 1 /* different class */
+ << 0 /* qualifier difference */
+ << 3 /* parameter mismatch */
+ << 2 << Arg1->getType() << ConstCharPtrTy;
+
+ const QualType SizeTy = Context.getSizeType();
+ if (!Context.hasSameType(
+ Arg2Ty->getCanonicalTypeInternal().withoutLocalFastQualifiers(),
+ SizeTy))
+ Diag(Arg2->getBeginLoc(), diag::err_typecheck_convert_incompatible)
+ << Arg2->getType() << SizeTy << 1 /* different class */
+ << 0 /* qualifier difference */
+ << 3 /* parameter mismatch */
+ << 3 << Arg2->getType() << SizeTy;
+
+ return false;
+}
+
+bool Sema::BuiltinUnorderedCompare(CallExpr *TheCall, unsigned BuiltinID) {
+ if (checkArgCount(TheCall, 2))
+ return true;
+
+ if (BuiltinID == Builtin::BI__builtin_isunordered &&
+ TheCall->getFPFeaturesInEffect(getLangOpts()).getNoHonorNaNs())
+ Diag(TheCall->getBeginLoc(), diag::warn_fp_nan_inf_when_disabled)
+ << 1 << 0 << TheCall->getSourceRange();
+
+ ExprResult OrigArg0 = TheCall->getArg(0);
+ ExprResult OrigArg1 = TheCall->getArg(1);
+
+ // Do standard promotions between the two arguments, returning their common
+ // type.
+ QualType Res = UsualArithmeticConversions(
+ OrigArg0, OrigArg1, TheCall->getExprLoc(), ArithConvKind::Comparison);
+ if (OrigArg0.isInvalid() || OrigArg1.isInvalid())
+ return true;
+
+ // Make sure any conversions are pushed back into the call; this is
+ // type safe since unordered compare builtins are declared as "_Bool
+ // foo(...)".
+ TheCall->setArg(0, OrigArg0.get());
+ TheCall->setArg(1, OrigArg1.get());
+
+ if (OrigArg0.get()->isTypeDependent() || OrigArg1.get()->isTypeDependent())
+ return false;
+
+ // If the common type isn't a real floating type, then the arguments were
+ // invalid for this operation.
+ if (Res.isNull() || !Res->isRealFloatingType())
+ return Diag(OrigArg0.get()->getBeginLoc(),
+ diag::err_typecheck_call_invalid_ordered_compare)
+ << OrigArg0.get()->getType() << OrigArg1.get()->getType()
+ << SourceRange(OrigArg0.get()->getBeginLoc(),
+ OrigArg1.get()->getEndLoc());
+
+ return false;
+}
+
+bool Sema::BuiltinFPClassification(CallExpr *TheCall, unsigned NumArgs,
+ unsigned BuiltinID) {
+ if (checkArgCount(TheCall, NumArgs))
+ return true;
+
+ FPOptions FPO = TheCall->getFPFeaturesInEffect(getLangOpts());
+ if (FPO.getNoHonorInfs() && (BuiltinID == Builtin::BI__builtin_isfinite ||
+ BuiltinID == Builtin::BI__builtin_isinf ||
+ BuiltinID == Builtin::BI__builtin_isinf_sign))
+ Diag(TheCall->getBeginLoc(), diag::warn_fp_nan_inf_when_disabled)
+ << 0 << 0 << TheCall->getSourceRange();
+
+ if (FPO.getNoHonorNaNs() && (BuiltinID == Builtin::BI__builtin_isnan ||
+ BuiltinID == Builtin::BI__builtin_isunordered))
+ Diag(TheCall->getBeginLoc(), diag::warn_fp_nan_inf_when_disabled)
+ << 1 << 0 << TheCall->getSourceRange();
+
+ bool IsFPClass = NumArgs == 2;
+
+ // Find out position of floating-point argument.
+ unsigned FPArgNo = IsFPClass ? 0 : NumArgs - 1;
+
+ // We can count on all parameters preceding the floating-point just being int.
+ // Try all of those.
+ for (unsigned i = 0; i < FPArgNo; ++i) {
+ Expr *Arg = TheCall->getArg(i);
+
+ if (Arg->isTypeDependent())
+ return false;
+
+ ExprResult Res = PerformImplicitConversion(Arg, Context.IntTy,
+ AssignmentAction::Passing);
+
+ if (Res.isInvalid())
+ return true;
+ TheCall->setArg(i, Res.get());
+ }
+
+ Expr *OrigArg = TheCall->getArg(FPArgNo);
+
+ if (OrigArg->isTypeDependent())
+ return false;
+
+ // Usual Unary Conversions will convert half to float, which we want for
+ // machines that use fp16 conversion intrinsics. Else, we wnat to leave the
+ // type how it is, but do normal L->Rvalue conversions.
+ if (Context.getTargetInfo().useFP16ConversionIntrinsics()) {
+ ExprResult Res = UsualUnaryConversions(OrigArg);
+
+ if (!Res.isUsable())
+ return true;
+ OrigArg = Res.get();
+ } else {
+ ExprResult Res = DefaultFunctionArrayLvalueConversion(OrigArg);
+
+ if (!Res.isUsable())
+ return true;
+ OrigArg = Res.get();
+ }
+ TheCall->setArg(FPArgNo, OrigArg);
+
+ QualType VectorResultTy;
+ QualType ElementTy = OrigArg->getType();
+ // TODO: When all classification function are implemented with is_fpclass,
+ // vector argument can be supported in all of them.
+ if (ElementTy->isVectorType() && IsFPClass) {
+ VectorResultTy = GetSignedVectorType(ElementTy);
+ ElementTy = ElementTy->castAs<VectorType>()->getElementType();
+ }
+
+ // This operation requires a non-_Complex floating-point number.
+ if (!ElementTy->isRealFloatingType())
+ return Diag(OrigArg->getBeginLoc(),
+ diag::err_typecheck_call_invalid_unary_fp)
+ << OrigArg->getType() << OrigArg->getSourceRange();
+
+ // __builtin_isfpclass has integer parameter that specify test mask. It is
+ // passed in (...), so it should be analyzed completely here.
+ if (IsFPClass)
+ if (BuiltinConstantArgRange(TheCall, 1, 0, llvm::fcAllFlags))
+ return true;
+
+ // TODO: enable this code to all classification functions.
+ if (IsFPClass) {
+ QualType ResultTy;
+ if (!VectorResultTy.isNull())
+ ResultTy = VectorResultTy;
+ else
+ ResultTy = Context.IntTy;
+ TheCall->setType(ResultTy);
+ }
+
+ return false;
+}
+
+bool Sema::BuiltinComplex(CallExpr *TheCall) {
+ if (checkArgCount(TheCall, 2))
+ return true;
+
+ bool Dependent = false;
+ for (unsigned I = 0; I != 2; ++I) {
+ Expr *Arg = TheCall->getArg(I);
+ QualType T = Arg->getType();
+ if (T->isDependentType()) {
+ Dependent = true;
+ continue;
+ }
+
+ // Despite supporting _Complex int, GCC requires a real floating point type
+ // for the operands of __builtin_complex.
+ if (!T->isRealFloatingType()) {
+ return Diag(Arg->getBeginLoc(), diag::err_typecheck_call_requires_real_fp)
+ << Arg->getType() << Arg->getSourceRange();
+ }
+
+ ExprResult Converted = DefaultLvalueConversion(Arg);
+ if (Converted.isInvalid())
+ return true;
+ TheCall->setArg(I, Converted.get());
+ }
+
+ if (Dependent) {
+ TheCall->setType(Context.DependentTy);
+ return false;
+ }
+
+ Expr *Real = TheCall->getArg(0);
+ Expr *Imag = TheCall->getArg(1);
+ if (!Context.hasSameType(Real->getType(), Imag->getType())) {
+ return Diag(Real->getBeginLoc(),
+ diag::err_typecheck_call_different_arg_types)
+ << Real->getType() << Imag->getType()
+ << Real->getSourceRange() << Imag->getSourceRange();
+ }
+
+ TheCall->setType(Context.getComplexType(Real->getType()));
+ return false;
+}
+
+/// BuiltinShuffleVector - Handle __builtin_shufflevector.
+// This is declared to take (...), so we have to check everything.
+ExprResult Sema::BuiltinShuffleVector(CallExpr *TheCall) {
+ unsigned NumArgs = TheCall->getNumArgs();
+ if (NumArgs < 2)
+ return ExprError(Diag(TheCall->getEndLoc(),
+ diag::err_typecheck_call_too_few_args_at_least)
+ << 0 /*function call*/ << 2 << NumArgs
+ << /*is non object*/ 0 << TheCall->getSourceRange());
+
+ // Determine which of the following types of shufflevector we're checking:
+ // 1) unary, vector mask: (lhs, mask)
+ // 2) binary, scalar mask: (lhs, rhs, index, ..., index)
+ QualType ResType = TheCall->getArg(0)->getType();
+ unsigned NumElements = 0;
+
+ if (!TheCall->getArg(0)->isTypeDependent() &&
+ !TheCall->getArg(1)->isTypeDependent()) {
+ QualType LHSType = TheCall->getArg(0)->getType();
+ QualType RHSType = TheCall->getArg(1)->getType();
+
+ if (!LHSType->isVectorType() || !RHSType->isVectorType())
+ return ExprError(
+ Diag(TheCall->getBeginLoc(), diag::err_vec_builtin_non_vector)
+ << TheCall->getDirectCallee() << /*isMoreThanTwoArgs*/ false
+ << SourceRange(TheCall->getArg(0)->getBeginLoc(),
+ TheCall->getArg(1)->getEndLoc()));
+
+ NumElements = LHSType->castAs<VectorType>()->getNumElements();
+ unsigned NumResElements = NumArgs - 2;
+
+ // Check to see if we have a call with 2 vector arguments, the unary shuffle
+ // with mask. If so, verify that RHS is an integer vector type with the
+ // same number of elts as lhs.
+ if (NumArgs == 2) {
+ if (!RHSType->hasIntegerRepresentation() ||
+ RHSType->castAs<VectorType>()->getNumElements() != NumElements)
+ return ExprError(Diag(TheCall->getBeginLoc(),
+ diag::err_vec_builtin_incompatible_vector)
+ << TheCall->getDirectCallee()
+ << /*isMoreThanTwoArgs*/ false
+ << SourceRange(TheCall->getArg(1)->getBeginLoc(),
+ TheCall->getArg(1)->getEndLoc()));
+ } else if (!Context.hasSameUnqualifiedType(LHSType, RHSType)) {
+ return ExprError(Diag(TheCall->getBeginLoc(),
+ diag::err_vec_builtin_incompatible_vector)
+ << TheCall->getDirectCallee()
+ << /*isMoreThanTwoArgs*/ false
+ << SourceRange(TheCall->getArg(0)->getBeginLoc(),
+ TheCall->getArg(1)->getEndLoc()));
+ } else if (NumElements != NumResElements) {
+ QualType EltType = LHSType->castAs<VectorType>()->getElementType();
+ ResType = ResType->isExtVectorType()
+ ? Context.getExtVectorType(EltType, NumResElements)
+ : Context.getVectorType(EltType, NumResElements,
+ VectorKind::Generic);
+ }
+ }
+
+ for (unsigned I = 2; I != NumArgs; ++I) {
+ Expr *Arg = TheCall->getArg(I);
+ if (Arg->isTypeDependent() || Arg->isValueDependent())
+ continue;
+
+ std::optional<llvm::APSInt> Result = Arg->getIntegerConstantExpr(Context);
+ if (!Result)
+ return ExprError(Diag(TheCall->getBeginLoc(),
+ diag::err_shufflevector_nonconstant_argument)
+ << Arg->getSourceRange());
+
+ // Allow -1 which will be translated to undef in the IR.
+ if (Result->isSigned() && Result->isAllOnes())
+ ;
+ else if (Result->getActiveBits() > 64 ||
+ Result->getZExtValue() >= NumElements * 2)
+ return ExprError(Diag(TheCall->getBeginLoc(),
+ diag::err_shufflevector_argument_too_large)
+ << Arg->getSourceRange());
+
+ TheCall->setArg(I, ConstantExpr::Create(Context, Arg, APValue(*Result)));
+ }
+
+ auto *Result = new (Context) ShuffleVectorExpr(
+ Context, ArrayRef(TheCall->getArgs(), NumArgs), ResType,
+ TheCall->getCallee()->getBeginLoc(), TheCall->getRParenLoc());
+
+ // All moved to Result.
+ TheCall->shrinkNumArgs(0);
+ return Result;
+}
+
+ExprResult Sema::ConvertVectorExpr(Expr *E, TypeSourceInfo *TInfo,
+ SourceLocation BuiltinLoc,
+ SourceLocation RParenLoc) {
+ ExprValueKind VK = VK_PRValue;
+ ExprObjectKind OK = OK_Ordinary;
+ QualType DstTy = TInfo->getType();
+ QualType SrcTy = E->getType();
+
+ if (!SrcTy->isVectorType() && !SrcTy->isDependentType())
+ return ExprError(Diag(BuiltinLoc,
+ diag::err_convertvector_non_vector)
+ << E->getSourceRange());
+ if (!DstTy->isVectorType() && !DstTy->isDependentType())
+ return ExprError(Diag(BuiltinLoc, diag::err_builtin_non_vector_type)
+ << "second"
+ << "__builtin_convertvector");
+
+ if (!SrcTy->isDependentType() && !DstTy->isDependentType()) {
+ unsigned SrcElts = SrcTy->castAs<VectorType>()->getNumElements();
+ unsigned DstElts = DstTy->castAs<VectorType>()->getNumElements();
+ if (SrcElts != DstElts)
+ return ExprError(Diag(BuiltinLoc,
+ diag::err_convertvector_incompatible_vector)
+ << E->getSourceRange());
+ }
+
+ return ConvertVectorExpr::Create(Context, E, TInfo, DstTy, VK, OK, BuiltinLoc,
+ RParenLoc, CurFPFeatureOverrides());
+}
+
+bool Sema::BuiltinPrefetch(CallExpr *TheCall) {
+ unsigned NumArgs = TheCall->getNumArgs();
+
+ if (NumArgs > 3)
+ return Diag(TheCall->getEndLoc(),
+ diag::err_typecheck_call_too_many_args_at_most)
+ << 0 /*function call*/ << 3 << NumArgs << /*is non object*/ 0
+ << TheCall->getSourceRange();
+
+ // Argument 0 is checked for us and the remaining arguments must be
+ // constant integers.
+ for (unsigned i = 1; i != NumArgs; ++i)
+ if (BuiltinConstantArgRange(TheCall, i, 0, i == 1 ? 1 : 3))
+ return true;
+
+ return false;
+}
+
+bool Sema::BuiltinArithmeticFence(CallExpr *TheCall) {
+ if (!Context.getTargetInfo().checkArithmeticFenceSupported())
+ return Diag(TheCall->getBeginLoc(), diag::err_builtin_target_unsupported)
+ << SourceRange(TheCall->getBeginLoc(), TheCall->getEndLoc());
+ if (checkArgCount(TheCall, 1))
+ return true;
+ Expr *Arg = TheCall->getArg(0);
+ if (Arg->isInstantiationDependent())
+ return false;
+
+ QualType ArgTy = Arg->getType();
+ if (!ArgTy->hasFloatingRepresentation())
+ return Diag(TheCall->getEndLoc(), diag::err_typecheck_expect_flt_or_vector)
+ << ArgTy;
+ if (Arg->isLValue()) {
+ ExprResult FirstArg = DefaultLvalueConversion(Arg);
+ TheCall->setArg(0, FirstArg.get());
+ }
+ TheCall->setType(TheCall->getArg(0)->getType());
+ return false;
+}
+
+bool Sema::BuiltinAssume(CallExpr *TheCall) {
+ Expr *Arg = TheCall->getArg(0);
+ if (Arg->isInstantiationDependent()) return false;
+
+ if (Arg->HasSideEffects(Context))
+ Diag(Arg->getBeginLoc(), diag::warn_assume_side_effects)
+ << Arg->getSourceRange()
+ << cast<FunctionDecl>(TheCall->getCalleeDecl())->getIdentifier();
+
+ return false;
+}
+
+bool Sema::BuiltinAllocaWithAlign(CallExpr *TheCall) {
+ // The alignment must be a constant integer.
+ Expr *Arg = TheCall->getArg(1);
+
+ // We can't check the value of a dependent argument.
+ if (!Arg->isTypeDependent() && !Arg->isValueDependent()) {
+ if (const auto *UE =
+ dyn_cast<UnaryExprOrTypeTraitExpr>(Arg->IgnoreParenImpCasts()))
+ if (UE->getKind() == UETT_AlignOf ||
+ UE->getKind() == UETT_PreferredAlignOf)
+ Diag(TheCall->getBeginLoc(), diag::warn_alloca_align_alignof)
+ << Arg->getSourceRange();
+
+ llvm::APSInt Result = Arg->EvaluateKnownConstInt(Context);
+
+ if (!Result.isPowerOf2())
+ return Diag(TheCall->getBeginLoc(), diag::err_alignment_not_power_of_two)
+ << Arg->getSourceRange();
+
+ if (Result < Context.getCharWidth())
+ return Diag(TheCall->getBeginLoc(), diag::err_alignment_too_small)
+ << (unsigned)Context.getCharWidth() << Arg->getSourceRange();
+
+ if (Result > std::numeric_limits<int32_t>::max())
+ return Diag(TheCall->getBeginLoc(), diag::err_alignment_too_big)
+ << std::numeric_limits<int32_t>::max() << Arg->getSourceRange();
+ }
+
+ return false;
+}
+
+bool Sema::BuiltinAssumeAligned(CallExpr *TheCall) {
+ if (checkArgCountRange(TheCall, 2, 3))
+ return true;
+
+ unsigned NumArgs = TheCall->getNumArgs();
+ Expr *FirstArg = TheCall->getArg(0);
+
+ {
+ ExprResult FirstArgResult =
+ DefaultFunctionArrayLvalueConversion(FirstArg);
+ if (!FirstArgResult.get()->getType()->isPointerType()) {
+ Diag(TheCall->getBeginLoc(), diag::err_builtin_assume_aligned_invalid_arg)
+ << TheCall->getSourceRange();
+ return true;
+ }
+ TheCall->setArg(0, FirstArgResult.get());
+ }
+
+ // The alignment must be a constant integer.
+ Expr *SecondArg = TheCall->getArg(1);
+
+ // We can't check the value of a dependent argument.
+ if (!SecondArg->isValueDependent()) {
+ llvm::APSInt Result;
+ if (BuiltinConstantArg(TheCall, 1, Result))
+ return true;
+
+ if (!Result.isPowerOf2())
+ return Diag(TheCall->getBeginLoc(), diag::err_alignment_not_power_of_two)
+ << SecondArg->getSourceRange();
+
+ if (Result > Sema::MaximumAlignment)
+ Diag(TheCall->getBeginLoc(), diag::warn_assume_aligned_too_great)
+ << SecondArg->getSourceRange() << Sema::MaximumAlignment;
+
+ TheCall->setArg(1,
+ ConstantExpr::Create(Context, SecondArg, APValue(Result)));
+ }
+
+ if (NumArgs > 2) {
+ Expr *ThirdArg = TheCall->getArg(2);
+ if (convertArgumentToType(*this, ThirdArg, Context.getSizeType()))
+ return true;
+ TheCall->setArg(2, ThirdArg);
+ }
+
+ return false;
+}
+
+bool Sema::BuiltinOSLogFormat(CallExpr *TheCall) {
+ unsigned BuiltinID =
+ cast<FunctionDecl>(TheCall->getCalleeDecl())->getBuiltinID();
+ bool IsSizeCall = BuiltinID == Builtin::BI__builtin_os_log_format_buffer_size;
+
+ unsigned NumArgs = TheCall->getNumArgs();
+ unsigned NumRequiredArgs = IsSizeCall ? 1 : 2;
+ if (NumArgs < NumRequiredArgs) {
+ return Diag(TheCall->getEndLoc(), diag::err_typecheck_call_too_few_args)
+ << 0 /* function call */ << NumRequiredArgs << NumArgs
+ << /*is non object*/ 0 << TheCall->getSourceRange();
+ }
+ if (NumArgs >= NumRequiredArgs + 0x100) {
+ return Diag(TheCall->getEndLoc(),
+ diag::err_typecheck_call_too_many_args_at_most)
+ << 0 /* function call */ << (NumRequiredArgs + 0xff) << NumArgs
+ << /*is non object*/ 0 << TheCall->getSourceRange();
+ }
+ unsigned i = 0;
+
+ // For formatting call, check buffer arg.
+ if (!IsSizeCall) {
+ ExprResult Arg(TheCall->getArg(i));
+ InitializedEntity Entity = InitializedEntity::InitializeParameter(
+ Context, Context.VoidPtrTy, false);
+ Arg = PerformCopyInitialization(Entity, SourceLocation(), Arg);
+ if (Arg.isInvalid())
+ return true;
+ TheCall->setArg(i, Arg.get());
+ i++;
+ }
+
+ // Check string literal arg.
+ unsigned FormatIdx = i;
+ {
+ ExprResult Arg = CheckOSLogFormatStringArg(TheCall->getArg(i));
+ if (Arg.isInvalid())
+ return true;
+ TheCall->setArg(i, Arg.get());
+ i++;
+ }
+
+ // Make sure variadic args are scalar.
+ unsigned FirstDataArg = i;
+ while (i < NumArgs) {
+ ExprResult Arg = DefaultVariadicArgumentPromotion(
+ TheCall->getArg(i), VariadicCallType::Function, nullptr);
+ if (Arg.isInvalid())
+ return true;
+ CharUnits ArgSize = Context.getTypeSizeInChars(Arg.get()->getType());
+ if (ArgSize.getQuantity() >= 0x100) {
+ return Diag(Arg.get()->getEndLoc(), diag::err_os_log_argument_too_big)
+ << i << (int)ArgSize.getQuantity() << 0xff
+ << TheCall->getSourceRange();
+ }
+ TheCall->setArg(i, Arg.get());
+ i++;
+ }
+
+ // Check formatting specifiers. NOTE: We're only doing this for the non-size
+ // call to avoid duplicate diagnostics.
+ if (!IsSizeCall) {
+ llvm::SmallBitVector CheckedVarArgs(NumArgs, false);
+ ArrayRef<const Expr *> Args(TheCall->getArgs(), TheCall->getNumArgs());
+ bool Success = CheckFormatArguments(
+ Args, FAPK_Variadic, nullptr, FormatIdx, FirstDataArg,
+ FormatStringType::OSLog, VariadicCallType::Function,
+ TheCall->getBeginLoc(), SourceRange(), CheckedVarArgs);
+ if (!Success)
+ return true;
+ }
+
+ if (IsSizeCall) {
+ TheCall->setType(Context.getSizeType());
+ } else {
+ TheCall->setType(Context.VoidPtrTy);
+ }
+ return false;
+}
+
+bool Sema::BuiltinConstantArg(CallExpr *TheCall, unsigned ArgNum,
+ llvm::APSInt &Result) {
+ Expr *Arg = TheCall->getArg(ArgNum);
+
+ if (Arg->isTypeDependent() || Arg->isValueDependent())
+ return false;
+
+ std::optional<llvm::APSInt> R = Arg->getIntegerConstantExpr(Context);
+ if (!R) {
+ auto *DRE = cast<DeclRefExpr>(TheCall->getCallee()->IgnoreParenCasts());
+ auto *FDecl = cast<FunctionDecl>(DRE->getDecl());
+ return Diag(TheCall->getBeginLoc(), diag::err_constant_integer_arg_type)
+ << FDecl->getDeclName() << Arg->getSourceRange();
+ }
+ Result = *R;
+
+ return false;
+}
+
+bool Sema::BuiltinConstantArgRange(CallExpr *TheCall, unsigned ArgNum, int Low,
+ int High, bool RangeIsError) {
+ if (isConstantEvaluatedContext())
+ return false;
+ llvm::APSInt Result;
+
+ // We can't check the value of a dependent argument.
+ Expr *Arg = TheCall->getArg(ArgNum);
+ if (Arg->isTypeDependent() || Arg->isValueDependent())
+ return false;
+
+ // Check constant-ness first.
+ if (BuiltinConstantArg(TheCall, ArgNum, Result))
+ return true;
+
+ if (Result.getSExtValue() < Low || Result.getSExtValue() > High) {
+ if (RangeIsError)
+ return Diag(TheCall->getBeginLoc(), diag::err_argument_invalid_range)
+ << toString(Result, 10) << Low << High << Arg->getSourceRange();
+ else
+ // Defer the warning until we know if the code will be emitted so that
+ // dead code can ignore this.
+ DiagRuntimeBehavior(TheCall->getBeginLoc(), TheCall,
+ PDiag(diag::warn_argument_invalid_range)
+ << toString(Result, 10) << Low << High
+ << Arg->getSourceRange());
+ }
+
+ return false;
+}
+
+bool Sema::BuiltinConstantArgMultiple(CallExpr *TheCall, unsigned ArgNum,
+ unsigned Num) {
+ llvm::APSInt Result;
+
+ // We can't check the value of a dependent argument.
+ Expr *Arg = TheCall->getArg(ArgNum);
+ if (Arg->isTypeDependent() || Arg->isValueDependent())
+ return false;
+
+ // Check constant-ness first.
+ if (BuiltinConstantArg(TheCall, ArgNum, Result))
+ return true;
+
+ if (Result.getSExtValue() % Num != 0)
+ return Diag(TheCall->getBeginLoc(), diag::err_argument_not_multiple)
+ << Num << Arg->getSourceRange();
+
+ return false;
+}
+
+bool Sema::BuiltinConstantArgPower2(CallExpr *TheCall, unsigned ArgNum) {
+ llvm::APSInt Result;
+
+ // We can't check the value of a dependent argument.
+ Expr *Arg = TheCall->getArg(ArgNum);
+ if (Arg->isTypeDependent() || Arg->isValueDependent())
+ return false;
+
+ // Check constant-ness first.
+ if (BuiltinConstantArg(TheCall, ArgNum, Result))
+ return true;
+
+ if (Result.isPowerOf2())
+ return false;
+
+ return Diag(TheCall->getBeginLoc(), diag::err_argument_not_power_of_2)
+ << Arg->getSourceRange();
+}
+
+static bool IsShiftedByte(llvm::APSInt Value) {
+ if (Value.isNegative())
+ return false;
+
+ // Check if it's a shifted byte, by shifting it down
+ while (true) {
+ // If the value fits in the bottom byte, the check passes.
+ if (Value < 0x100)
+ return true;
+
+ // Otherwise, if the value has _any_ bits in the bottom byte, the check
+ // fails.
+ if ((Value & 0xFF) != 0)
+ return false;
+
+ // If the bottom 8 bits are all 0, but something above that is nonzero,
+ // then shifting the value right by 8 bits won't affect whether it's a
+ // shifted byte or not. So do that, and go round again.
+ Value >>= 8;
+ }
+}
+
+bool Sema::BuiltinConstantArgShiftedByte(CallExpr *TheCall, unsigned ArgNum,
+ unsigned ArgBits) {
+ llvm::APSInt Result;
+
+ // We can't check the value of a dependent argument.
+ Expr *Arg = TheCall->getArg(ArgNum);
+ if (Arg->isTypeDependent() || Arg->isValueDependent())
+ return false;
+
+ // Check constant-ness first.
+ if (BuiltinConstantArg(TheCall, ArgNum, Result))
+ return true;
+
+ // Truncate to the given size.
+ Result = Result.getLoBits(ArgBits);
+ Result.setIsUnsigned(true);
+
+ if (IsShiftedByte(Result))
+ return false;
+
+ return Diag(TheCall->getBeginLoc(), diag::err_argument_not_shifted_byte)
+ << Arg->getSourceRange();
+}
+
+bool Sema::BuiltinConstantArgShiftedByteOrXXFF(CallExpr *TheCall,
+ unsigned ArgNum,
+ unsigned ArgBits) {
+ llvm::APSInt Result;
+
+ // We can't check the value of a dependent argument.
+ Expr *Arg = TheCall->getArg(ArgNum);
+ if (Arg->isTypeDependent() || Arg->isValueDependent())
+ return false;
+
+ // Check constant-ness first.
+ if (BuiltinConstantArg(TheCall, ArgNum, Result))
+ return true;
+
+ // Truncate to the given size.
+ Result = Result.getLoBits(ArgBits);
+ Result.setIsUnsigned(true);
+
+ // Check to see if it's in either of the required forms.
+ if (IsShiftedByte(Result) ||
+ (Result > 0 && Result < 0x10000 && (Result & 0xFF) == 0xFF))
+ return false;
+
+ return Diag(TheCall->getBeginLoc(),
+ diag::err_argument_not_shifted_byte_or_xxff)
+ << Arg->getSourceRange();
+}
+
+bool Sema::BuiltinLongjmp(CallExpr *TheCall) {
+ if (!Context.getTargetInfo().hasSjLjLowering())
+ return Diag(TheCall->getBeginLoc(), diag::err_builtin_longjmp_unsupported)
+ << SourceRange(TheCall->getBeginLoc(), TheCall->getEndLoc());
+
+ Expr *Arg = TheCall->getArg(1);
+ llvm::APSInt Result;
+
+ // TODO: This is less than ideal. Overload this to take a value.
+ if (BuiltinConstantArg(TheCall, 1, Result))
+ return true;
+
+ if (Result != 1)
+ return Diag(TheCall->getBeginLoc(), diag::err_builtin_longjmp_invalid_val)
+ << SourceRange(Arg->getBeginLoc(), Arg->getEndLoc());
+
+ return false;
+}
+
+bool Sema::BuiltinSetjmp(CallExpr *TheCall) {
+ if (!Context.getTargetInfo().hasSjLjLowering())
+ return Diag(TheCall->getBeginLoc(), diag::err_builtin_setjmp_unsupported)
+ << SourceRange(TheCall->getBeginLoc(), TheCall->getEndLoc());
+ return false;
+}
+
+bool Sema::BuiltinCountedByRef(CallExpr *TheCall) {
+ if (checkArgCount(TheCall, 1))
+ return true;
+
+ ExprResult ArgRes = UsualUnaryConversions(TheCall->getArg(0));
+ if (ArgRes.isInvalid())
+ return true;
+
+ // For simplicity, we support only limited expressions for the argument.
+ // Specifically a flexible array member or a pointer with counted_by:
+ // 'ptr->array' or 'ptr->pointer'. This allows us to reject arguments with
+ // complex casting, which really shouldn't be a huge problem.
+ const Expr *Arg = ArgRes.get()->IgnoreParenImpCasts();
+ if (!Arg->getType()->isPointerType() && !Arg->getType()->isArrayType())
+ return Diag(Arg->getBeginLoc(),
+ diag::err_builtin_counted_by_ref_invalid_arg)
+ << Arg->getSourceRange();
+
+ if (Arg->HasSideEffects(Context))
+ return Diag(Arg->getBeginLoc(),
+ diag::err_builtin_counted_by_ref_has_side_effects)
+ << Arg->getSourceRange();
+
+ if (const auto *ME = dyn_cast<MemberExpr>(Arg)) {
+ const auto *CATy =
+ ME->getMemberDecl()->getType()->getAs<CountAttributedType>();
+
+ if (CATy && CATy->getKind() == CountAttributedType::CountedBy) {
+ // Member has counted_by attribute - return pointer to count field
+ const auto *MemberDecl = cast<FieldDecl>(ME->getMemberDecl());
+ if (const FieldDecl *CountFD = MemberDecl->findCountedByField()) {
+ TheCall->setType(Context.getPointerType(CountFD->getType()));
+ return false;
+ }
+ }
+
+ // FAMs and pointers without counted_by return void*
+ QualType MemberTy = ME->getMemberDecl()->getType();
+ if (!MemberTy->isArrayType() && !MemberTy->isPointerType())
+ return Diag(Arg->getBeginLoc(),
+ diag::err_builtin_counted_by_ref_invalid_arg)
+ << Arg->getSourceRange();
+ } else {
+ return Diag(Arg->getBeginLoc(),
+ diag::err_builtin_counted_by_ref_invalid_arg)
+ << Arg->getSourceRange();
+ }
+
+ TheCall->setType(Context.getPointerType(Context.VoidTy));
+ return false;
+}
+
+/// The result of __builtin_counted_by_ref cannot be assigned to a variable.
+/// It allows leaking and modification of bounds safety information.
+bool Sema::CheckInvalidBuiltinCountedByRef(const Expr *E,
+ BuiltinCountedByRefKind K) {
+ const CallExpr *CE =
+ E ? dyn_cast<CallExpr>(E->IgnoreParenImpCasts()) : nullptr;
+ if (!CE || CE->getBuiltinCallee() != Builtin::BI__builtin_counted_by_ref)
+ return false;
+
+ switch (K) {
+ case BuiltinCountedByRefKind::Assignment:
+ case BuiltinCountedByRefKind::Initializer:
+ Diag(E->getExprLoc(),
+ diag::err_builtin_counted_by_ref_cannot_leak_reference)
+ << 0 << E->getSourceRange();
+ break;
+ case BuiltinCountedByRefKind::FunctionArg:
+ Diag(E->getExprLoc(),
+ diag::err_builtin_counted_by_ref_cannot_leak_reference)
+ << 1 << E->getSourceRange();
+ break;
+ case BuiltinCountedByRefKind::ReturnArg:
+ Diag(E->getExprLoc(),
+ diag::err_builtin_counted_by_ref_cannot_leak_reference)
+ << 2 << E->getSourceRange();
+ break;
+ case BuiltinCountedByRefKind::ArraySubscript:
+ Diag(E->getExprLoc(), diag::err_builtin_counted_by_ref_invalid_use)
+ << 0 << E->getSourceRange();
+ break;
+ case BuiltinCountedByRefKind::BinaryExpr:
+ Diag(E->getExprLoc(), diag::err_builtin_counted_by_ref_invalid_use)
+ << 1 << E->getSourceRange();
+ break;
+ }
+
+ return true;
+}
+
+namespace {
+
+class UncoveredArgHandler {
+ enum { Unknown = -1, AllCovered = -2 };
+
+ signed FirstUncoveredArg = Unknown;
+ SmallVector<const Expr *, 4> DiagnosticExprs;
+
+public:
+ UncoveredArgHandler() = default;
+
+ bool hasUncoveredArg() const {
+ return (FirstUncoveredArg >= 0);
+ }
+
+ unsigned getUncoveredArg() const {
+ assert(hasUncoveredArg() && "no uncovered argument");
+ return FirstUncoveredArg;
+ }
+
+ void setAllCovered() {
+ // A string has been found with all arguments covered, so clear out
+ // the diagnostics.
+ DiagnosticExprs.clear();
+ FirstUncoveredArg = AllCovered;
+ }
+
+ void Update(signed NewFirstUncoveredArg, const Expr *StrExpr) {
+ assert(NewFirstUncoveredArg >= 0 && "Outside range");
+
+ // Don't update if a previous string covers all arguments.
+ if (FirstUncoveredArg == AllCovered)
+ return;
+
+ // UncoveredArgHandler tracks the highest uncovered argument index
+ // and with it all the strings that match this index.
+ if (NewFirstUncoveredArg == FirstUncoveredArg)
+ DiagnosticExprs.push_back(StrExpr);
+ else if (NewFirstUncoveredArg > FirstUncoveredArg) {
+ DiagnosticExprs.clear();
+ DiagnosticExprs.push_back(StrExpr);
+ FirstUncoveredArg = NewFirstUncoveredArg;
+ }
+ }
+
+ void Diagnose(Sema &S, bool IsFunctionCall, const Expr *ArgExpr);
+};
+
+enum StringLiteralCheckType {
+ SLCT_NotALiteral,
+ SLCT_UncheckedLiteral,
+ SLCT_CheckedLiteral
+};
+
+} // namespace
+
+static void sumOffsets(llvm::APSInt &Offset, llvm::APSInt Addend,
+ BinaryOperatorKind BinOpKind,
+ bool AddendIsRight) {
+ unsigned BitWidth = Offset.getBitWidth();
+ unsigned AddendBitWidth = Addend.getBitWidth();
+ // There might be negative interim results.
+ if (Addend.isUnsigned()) {
+ Addend = Addend.zext(++AddendBitWidth);
+ Addend.setIsSigned(true);
+ }
+ // Adjust the bit width of the APSInts.
+ if (AddendBitWidth > BitWidth) {
+ Offset = Offset.sext(AddendBitWidth);
+ BitWidth = AddendBitWidth;
+ } else if (BitWidth > AddendBitWidth) {
+ Addend = Addend.sext(BitWidth);
+ }
+
+ bool Ov = false;
+ llvm::APSInt ResOffset = Offset;
+ if (BinOpKind == BO_Add)
+ ResOffset = Offset.sadd_ov(Addend, Ov);
+ else {
+ assert(AddendIsRight && BinOpKind == BO_Sub &&
+ "operator must be add or sub with addend on the right");
+ ResOffset = Offset.ssub_ov(Addend, Ov);
+ }
+
+ // We add an offset to a pointer here so we should support an offset as big as
+ // possible.
+ if (Ov) {
+ assert(BitWidth <= std::numeric_limits<unsigned>::max() / 2 &&
+ "index (intermediate) result too big");
+ Offset = Offset.sext(2 * BitWidth);
+ sumOffsets(Offset, Addend, BinOpKind, AddendIsRight);
+ return;
+ }
+
+ Offset = ResOffset;
+}
+
+namespace {
+
+// This is a wrapper class around StringLiteral to support offsetted string
+// literals as format strings. It takes the offset into account when returning
+// the string and its length or the source locations to display notes correctly.
+class FormatStringLiteral {
+ const StringLiteral *FExpr;
+ int64_t Offset;
+
+public:
+ FormatStringLiteral(const StringLiteral *fexpr, int64_t Offset = 0)
+ : FExpr(fexpr), Offset(Offset) {}
+
+ const StringLiteral *getFormatString() const { return FExpr; }
+
+ StringRef getString() const { return FExpr->getString().drop_front(Offset); }
+
+ unsigned getByteLength() const {
+ return FExpr->getByteLength() - getCharByteWidth() * Offset;
+ }
+
+ unsigned getLength() const { return FExpr->getLength() - Offset; }
+ unsigned getCharByteWidth() const { return FExpr->getCharByteWidth(); }
+
+ StringLiteralKind getKind() const { return FExpr->getKind(); }
+
+ QualType getType() const { return FExpr->getType(); }
+
+ bool isAscii() const { return FExpr->isOrdinary(); }
+ bool isWide() const { return FExpr->isWide(); }
+ bool isUTF8() const { return FExpr->isUTF8(); }
+ bool isUTF16() const { return FExpr->isUTF16(); }
+ bool isUTF32() const { return FExpr->isUTF32(); }
+ bool isPascal() const { return FExpr->isPascal(); }
+
+ SourceLocation getLocationOfByte(
+ unsigned ByteNo, const SourceManager &SM, const LangOptions &Features,
+ const TargetInfo &Target, unsigned *StartToken = nullptr,
+ unsigned *StartTokenByteOffset = nullptr) const {
+ return FExpr->getLocationOfByte(ByteNo + Offset, SM, Features, Target,
+ StartToken, StartTokenByteOffset);
+ }
+
+ SourceLocation getBeginLoc() const LLVM_READONLY {
+ return FExpr->getBeginLoc().getLocWithOffset(Offset);
+ }
+
+ SourceLocation getEndLoc() const LLVM_READONLY { return FExpr->getEndLoc(); }
+};
+
+} // namespace
+
+static void CheckFormatString(
+ Sema &S, const FormatStringLiteral *FExpr,
+ const StringLiteral *ReferenceFormatString, const Expr *OrigFormatExpr,
+ ArrayRef<const Expr *> Args, Sema::FormatArgumentPassingKind APK,
+ unsigned format_idx, unsigned firstDataArg, FormatStringType Type,
+ bool inFunctionCall, VariadicCallType CallType,
+ llvm::SmallBitVector &CheckedVarArgs, UncoveredArgHandler &UncoveredArg,
+ bool IgnoreStringsWithoutSpecifiers);
+
+static const Expr *maybeConstEvalStringLiteral(ASTContext &Context,
+ const Expr *E);
+
+// Determine if an expression is a string literal or constant string.
+// If this function returns false on the arguments to a function expecting a
+// format string, we will usually need to emit a warning.
+// True string literals are then checked by CheckFormatString.
+static StringLiteralCheckType
+checkFormatStringExpr(Sema &S, const StringLiteral *ReferenceFormatString,
+ const Expr *E, ArrayRef<const Expr *> Args,
+ Sema::FormatArgumentPassingKind APK, unsigned format_idx,
+ unsigned firstDataArg, FormatStringType Type,
+ VariadicCallType CallType, bool InFunctionCall,
+ llvm::SmallBitVector &CheckedVarArgs,
+ UncoveredArgHandler &UncoveredArg, llvm::APSInt Offset,
+ std::optional<unsigned> *CallerFormatParamIdx = nullptr,
+ bool IgnoreStringsWithoutSpecifiers = false) {
+ if (S.isConstantEvaluatedContext())
+ return SLCT_NotALiteral;
+tryAgain:
+ assert(Offset.isSigned() && "invalid offset");
+
+ if (E->isTypeDependent() || E->isValueDependent())
+ return SLCT_NotALiteral;
+
+ E = E->IgnoreParenCasts();
+
+ if (E->isNullPointerConstant(S.Context, Expr::NPC_ValueDependentIsNotNull))
+ // Technically -Wformat-nonliteral does not warn about this case.
+ // The behavior of printf and friends in this case is implementation
+ // dependent. Ideally if the format string cannot be null then
+ // it should have a 'nonnull' attribute in the function prototype.
+ return SLCT_UncheckedLiteral;
+
+ switch (E->getStmtClass()) {
+ case Stmt::InitListExprClass:
+ // Handle expressions like {"foobar"}.
+ if (const clang::Expr *SLE = maybeConstEvalStringLiteral(S.Context, E)) {
+ return checkFormatStringExpr(S, ReferenceFormatString, SLE, Args, APK,
+ format_idx, firstDataArg, Type, CallType,
+ /*InFunctionCall*/ false, CheckedVarArgs,
+ UncoveredArg, Offset, CallerFormatParamIdx,
+ IgnoreStringsWithoutSpecifiers);
+ }
+ return SLCT_NotALiteral;
+ case Stmt::BinaryConditionalOperatorClass:
+ case Stmt::ConditionalOperatorClass: {
+ // The expression is a literal if both sub-expressions were, and it was
+ // completely checked only if both sub-expressions were checked.
+ const AbstractConditionalOperator *C =
+ cast<AbstractConditionalOperator>(E);
+
+ // Determine whether it is necessary to check both sub-expressions, for
+ // example, because the condition expression is a constant that can be
+ // evaluated at compile time.
+ bool CheckLeft = true, CheckRight = true;
+
+ bool Cond;
+ if (C->getCond()->EvaluateAsBooleanCondition(
+ Cond, S.getASTContext(), S.isConstantEvaluatedContext())) {
+ if (Cond)
+ CheckRight = false;
+ else
+ CheckLeft = false;
+ }
+
+ // We need to maintain the offsets for the right and the left hand side
+ // separately to check if every possible indexed expression is a valid
+ // string literal. They might have different offsets for different string
+ // literals in the end.
+ StringLiteralCheckType Left;
+ if (!CheckLeft)
+ Left = SLCT_UncheckedLiteral;
+ else {
+ Left = checkFormatStringExpr(S, ReferenceFormatString, C->getTrueExpr(),
+ Args, APK, format_idx, firstDataArg, Type,
+ CallType, InFunctionCall, CheckedVarArgs,
+ UncoveredArg, Offset, CallerFormatParamIdx,
+ IgnoreStringsWithoutSpecifiers);
+ if (Left == SLCT_NotALiteral || !CheckRight) {
+ return Left;
+ }
+ }
+
+ StringLiteralCheckType Right = checkFormatStringExpr(
+ S, ReferenceFormatString, C->getFalseExpr(), Args, APK, format_idx,
+ firstDataArg, Type, CallType, InFunctionCall, CheckedVarArgs,
+ UncoveredArg, Offset, CallerFormatParamIdx,
+ IgnoreStringsWithoutSpecifiers);
+
+ return (CheckLeft && Left < Right) ? Left : Right;
+ }
+
+ case Stmt::ImplicitCastExprClass:
+ E = cast<ImplicitCastExpr>(E)->getSubExpr();
+ goto tryAgain;
+
+ case Stmt::OpaqueValueExprClass:
+ if (const Expr *src = cast<OpaqueValueExpr>(E)->getSourceExpr()) {
+ E = src;
+ goto tryAgain;
+ }
+ return SLCT_NotALiteral;
+
+ case Stmt::PredefinedExprClass:
+ // While __func__, etc., are technically not string literals, they
+ // cannot contain format specifiers and thus are not a security
+ // liability.
+ return SLCT_UncheckedLiteral;
+
+ case Stmt::DeclRefExprClass: {
+ const DeclRefExpr *DR = cast<DeclRefExpr>(E);
+
+ // As an exception, do not flag errors for variables binding to
+ // const string literals.
+ if (const VarDecl *VD = dyn_cast<VarDecl>(DR->getDecl())) {
+ bool isConstant = false;
+ QualType T = DR->getType();
+
+ if (const ArrayType *AT = S.Context.getAsArrayType(T)) {
+ isConstant = AT->getElementType().isConstant(S.Context);
+ } else if (const PointerType *PT = T->getAs<PointerType>()) {
+ isConstant = T.isConstant(S.Context) &&
+ PT->getPointeeType().isConstant(S.Context);
+ } else if (T->isObjCObjectPointerType()) {
+ // In ObjC, there is usually no "const ObjectPointer" type,
+ // so don't check if the pointee type is constant.
+ isConstant = T.isConstant(S.Context);
+ }
+
+ if (isConstant) {
+ if (const Expr *Init = VD->getAnyInitializer()) {
+ // Look through initializers like const char c[] = { "foo" }
+ if (const InitListExpr *InitList = dyn_cast<InitListExpr>(Init)) {
+ if (InitList->isStringLiteralInit())
+ Init = InitList->getInit(0)->IgnoreParenImpCasts();
+ }
+ return checkFormatStringExpr(
+ S, ReferenceFormatString, Init, Args, APK, format_idx,
+ firstDataArg, Type, CallType, /*InFunctionCall=*/false,
+ CheckedVarArgs, UncoveredArg, Offset, CallerFormatParamIdx);
+ }
+ }
+
+ // When the format argument is an argument of this function, and this
+ // function also has the format attribute, there are several interactions
+ // for which there shouldn't be a warning. For instance, when calling
+ // v*printf from a function that has the printf format attribute, we
+ // should not emit a warning about using `fmt`, even though it's not
+ // constant, because the arguments have already been checked for the
+ // caller of `logmessage`:
+ //
+ // __attribute__((format(printf, 1, 2)))
+ // void logmessage(char const *fmt, ...) {
+ // va_list ap;
+ // va_start(ap, fmt);
+ // vprintf(fmt, ap); /* do not emit a warning about "fmt" */
+ // ...
+ // }
+ //
+ // Another interaction that we need to support is using a format string
+ // specified by the format_matches attribute:
+ //
+ // __attribute__((format_matches(printf, 1, "%s %d")))
+ // void logmessage(char const *fmt, const char *a, int b) {
+ // printf(fmt, a, b); /* do not emit a warning about "fmt" */
+ // printf(fmt, 123.4); /* emit warnings that "%s %d" is incompatible */
+ // ...
+ // }
+ //
+ // Yet another interaction that we need to support is calling a variadic
+ // format function from a format function that has fixed arguments. For
+ // instance:
+ //
+ // __attribute__((format(printf, 1, 2)))
+ // void logstring(char const *fmt, char const *str) {
+ // printf(fmt, str); /* do not emit a warning about "fmt" */
+ // }
+ //
+ // Same (and perhaps more relatably) for the variadic template case:
+ //
+ // template<typename... Args>
+ // __attribute__((format(printf, 1, 2)))
+ // void log(const char *fmt, Args&&... args) {
+ // printf(fmt, forward<Args>(args)...);
+ // /* do not emit a warning about "fmt" */
+ // }
+ //
+ // Due to implementation difficulty, we only check the format, not the
+ // format arguments, in all cases.
+ //
+ if (const auto *PV = dyn_cast<ParmVarDecl>(VD)) {
+ if (CallerFormatParamIdx)
+ *CallerFormatParamIdx = PV->getFunctionScopeIndex();
+ if (const auto *D = dyn_cast<Decl>(PV->getDeclContext())) {
+ for (const auto *PVFormatMatches :
+ D->specific_attrs<FormatMatchesAttr>()) {
+ Sema::FormatStringInfo CalleeFSI;
+ if (!Sema::getFormatStringInfo(D, PVFormatMatches->getFormatIdx(),
+ 0, &CalleeFSI))
+ continue;
+ if (PV->getFunctionScopeIndex() == CalleeFSI.FormatIdx) {
+ // If using the wrong type of format string, emit a diagnostic
+ // here and stop checking to avoid irrelevant diagnostics.
+ if (Type != S.GetFormatStringType(PVFormatMatches)) {
+ S.Diag(Args[format_idx]->getBeginLoc(),
+ diag::warn_format_string_type_incompatible)
+ << PVFormatMatches->getType()->getName()
+ << S.GetFormatStringTypeName(Type);
+ if (!InFunctionCall) {
+ S.Diag(PVFormatMatches->getFormatString()->getBeginLoc(),
+ diag::note_format_string_defined);
+ }
+ return SLCT_UncheckedLiteral;
+ }
+ return checkFormatStringExpr(
+ S, ReferenceFormatString, PVFormatMatches->getFormatString(),
+ Args, APK, format_idx, firstDataArg, Type, CallType,
+ /*InFunctionCall*/ false, CheckedVarArgs, UncoveredArg,
+ Offset, CallerFormatParamIdx, IgnoreStringsWithoutSpecifiers);
+ }
+ }
+
+ for (const auto *PVFormat : D->specific_attrs<FormatAttr>()) {
+ Sema::FormatStringInfo CallerFSI;
+ if (!Sema::getFormatStringInfo(D, PVFormat->getFormatIdx(),
+ PVFormat->getFirstArg(), &CallerFSI))
+ continue;
+ if (PV->getFunctionScopeIndex() == CallerFSI.FormatIdx) {
+ // We also check if the formats are compatible.
+ // We can't pass a 'scanf' string to a 'printf' function.
+ if (Type != S.GetFormatStringType(PVFormat)) {
+ S.Diag(Args[format_idx]->getBeginLoc(),
+ diag::warn_format_string_type_incompatible)
+ << PVFormat->getType()->getName()
+ << S.GetFormatStringTypeName(Type);
+ if (!InFunctionCall) {
+ S.Diag(E->getBeginLoc(), diag::note_format_string_defined);
+ }
+ return SLCT_UncheckedLiteral;
+ }
+ // Lastly, check that argument passing kinds transition in a
+ // way that makes sense:
+ // from a caller with FAPK_VAList, allow FAPK_VAList
+ // from a caller with FAPK_Fixed, allow FAPK_Fixed
+ // from a caller with FAPK_Fixed, allow FAPK_Variadic
+ // from a caller with FAPK_Variadic, allow FAPK_VAList
+ switch (combineFAPK(CallerFSI.ArgPassingKind, APK)) {
+ case combineFAPK(Sema::FAPK_VAList, Sema::FAPK_VAList):
+ case combineFAPK(Sema::FAPK_Fixed, Sema::FAPK_Fixed):
+ case combineFAPK(Sema::FAPK_Fixed, Sema::FAPK_Variadic):
+ case combineFAPK(Sema::FAPK_Variadic, Sema::FAPK_VAList):
+ return SLCT_UncheckedLiteral;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ return SLCT_NotALiteral;
+ }
+
+ case Stmt::CallExprClass:
+ case Stmt::CXXMemberCallExprClass: {
+ const CallExpr *CE = cast<CallExpr>(E);
+ if (const NamedDecl *ND = dyn_cast_or_null<NamedDecl>(CE->getCalleeDecl())) {
+ bool IsFirst = true;
+ StringLiteralCheckType CommonResult;
+ for (const auto *FA : ND->specific_attrs<FormatArgAttr>()) {
+ const Expr *Arg = CE->getArg(FA->getFormatIdx().getASTIndex());
+ StringLiteralCheckType Result = checkFormatStringExpr(
+ S, ReferenceFormatString, Arg, Args, APK, format_idx, firstDataArg,
+ Type, CallType, InFunctionCall, CheckedVarArgs, UncoveredArg,
+ Offset, CallerFormatParamIdx, IgnoreStringsWithoutSpecifiers);
+ if (IsFirst) {
+ CommonResult = Result;
+ IsFirst = false;
+ }
+ }
+ if (!IsFirst)
+ return CommonResult;
+
+ if (const auto *FD = dyn_cast<FunctionDecl>(ND)) {
+ unsigned BuiltinID = FD->getBuiltinID();
+ if (BuiltinID == Builtin::BI__builtin___CFStringMakeConstantString ||
+ BuiltinID == Builtin::BI__builtin___NSStringMakeConstantString) {
+ const Expr *Arg = CE->getArg(0);
+ return checkFormatStringExpr(
+ S, ReferenceFormatString, Arg, Args, APK, format_idx,
+ firstDataArg, Type, CallType, InFunctionCall, CheckedVarArgs,
+ UncoveredArg, Offset, CallerFormatParamIdx,
+ IgnoreStringsWithoutSpecifiers);
+ }
+ }
+ }
+ if (const Expr *SLE = maybeConstEvalStringLiteral(S.Context, E))
+ return checkFormatStringExpr(S, ReferenceFormatString, SLE, Args, APK,
+ format_idx, firstDataArg, Type, CallType,
+ /*InFunctionCall*/ false, CheckedVarArgs,
+ UncoveredArg, Offset, CallerFormatParamIdx,
+ IgnoreStringsWithoutSpecifiers);
+ return SLCT_NotALiteral;
+ }
+ case Stmt::ObjCMessageExprClass: {
+ const auto *ME = cast<ObjCMessageExpr>(E);
+ if (const auto *MD = ME->getMethodDecl()) {
+ if (const auto *FA = MD->getAttr<FormatArgAttr>()) {
+ // As a special case heuristic, if we're using the method -[NSBundle
+ // localizedStringForKey:value:table:], ignore any key strings that lack
+ // format specifiers. The idea is that if the key doesn't have any
+ // format specifiers then its probably just a key to map to the
+ // localized strings. If it does have format specifiers though, then its
+ // likely that the text of the key is the format string in the
+ // programmer's language, and should be checked.
+ const ObjCInterfaceDecl *IFace;
+ if (MD->isInstanceMethod() && (IFace = MD->getClassInterface()) &&
+ IFace->getIdentifier()->isStr("NSBundle") &&
+ MD->getSelector().isKeywordSelector(
+ {"localizedStringForKey", "value", "table"})) {
+ IgnoreStringsWithoutSpecifiers = true;
+ }
+
+ const Expr *Arg = ME->getArg(FA->getFormatIdx().getASTIndex());
+ return checkFormatStringExpr(
+ S, ReferenceFormatString, Arg, Args, APK, format_idx, firstDataArg,
+ Type, CallType, InFunctionCall, CheckedVarArgs, UncoveredArg,
+ Offset, CallerFormatParamIdx, IgnoreStringsWithoutSpecifiers);
+ }
+ }
+
+ return SLCT_NotALiteral;
+ }
+ case Stmt::ObjCStringLiteralClass:
+ case Stmt::StringLiteralClass: {
+ const StringLiteral *StrE = nullptr;
+
+ if (const ObjCStringLiteral *ObjCFExpr = dyn_cast<ObjCStringLiteral>(E))
+ StrE = ObjCFExpr->getString();
+ else
+ StrE = cast<StringLiteral>(E);
+
+ if (StrE) {
+ if (Offset.isNegative() || Offset > StrE->getLength()) {
+ // TODO: It would be better to have an explicit warning for out of
+ // bounds literals.
+ return SLCT_NotALiteral;
+ }
+ FormatStringLiteral FStr(StrE, Offset.sextOrTrunc(64).getSExtValue());
+ CheckFormatString(S, &FStr, ReferenceFormatString, E, Args, APK,
+ format_idx, firstDataArg, Type, InFunctionCall,
+ CallType, CheckedVarArgs, UncoveredArg,
+ IgnoreStringsWithoutSpecifiers);
+ return SLCT_CheckedLiteral;
+ }
+
+ return SLCT_NotALiteral;
+ }
+ case Stmt::BinaryOperatorClass: {
+ const BinaryOperator *BinOp = cast<BinaryOperator>(E);
+
+ // A string literal + an int offset is still a string literal.
+ if (BinOp->isAdditiveOp()) {
+ Expr::EvalResult LResult, RResult;
+
+ bool LIsInt = BinOp->getLHS()->EvaluateAsInt(
+ LResult, S.Context, Expr::SE_NoSideEffects,
+ S.isConstantEvaluatedContext());
+ bool RIsInt = BinOp->getRHS()->EvaluateAsInt(
+ RResult, S.Context, Expr::SE_NoSideEffects,
+ S.isConstantEvaluatedContext());
+
+ if (LIsInt != RIsInt) {
+ BinaryOperatorKind BinOpKind = BinOp->getOpcode();
+
+ if (LIsInt) {
+ if (BinOpKind == BO_Add) {
+ sumOffsets(Offset, LResult.Val.getInt(), BinOpKind, RIsInt);
+ E = BinOp->getRHS();
+ goto tryAgain;
+ }
+ } else {
+ sumOffsets(Offset, RResult.Val.getInt(), BinOpKind, RIsInt);
+ E = BinOp->getLHS();
+ goto tryAgain;
+ }
+ }
+ }
+
+ return SLCT_NotALiteral;
+ }
+ case Stmt::UnaryOperatorClass: {
+ const UnaryOperator *UnaOp = cast<UnaryOperator>(E);
+ auto ASE = dyn_cast<ArraySubscriptExpr>(UnaOp->getSubExpr());
+ if (UnaOp->getOpcode() == UO_AddrOf && ASE) {
+ Expr::EvalResult IndexResult;
+ if (ASE->getRHS()->EvaluateAsInt(IndexResult, S.Context,
+ Expr::SE_NoSideEffects,
+ S.isConstantEvaluatedContext())) {
+ sumOffsets(Offset, IndexResult.Val.getInt(), BO_Add,
+ /*RHS is int*/ true);
+ E = ASE->getBase();
+ goto tryAgain;
+ }
+ }
+
+ return SLCT_NotALiteral;
+ }
+
+ default:
+ return SLCT_NotALiteral;
+ }
+}
+
+// If this expression can be evaluated at compile-time,
+// check if the result is a StringLiteral and return it
+// otherwise return nullptr
+static const Expr *maybeConstEvalStringLiteral(ASTContext &Context,
+ const Expr *E) {
+ Expr::EvalResult Result;
+ if (E->EvaluateAsRValue(Result, Context) && Result.Val.isLValue()) {
+ const auto *LVE = Result.Val.getLValueBase().dyn_cast<const Expr *>();
+ if (isa_and_nonnull<StringLiteral>(LVE))
+ return LVE;
+ }
+ return nullptr;
+}
+
+StringRef Sema::GetFormatStringTypeName(FormatStringType FST) {
+ switch (FST) {
+ case FormatStringType::Scanf:
+ return "scanf";
+ case FormatStringType::Printf:
+ return "printf";
+ case FormatStringType::NSString:
+ return "NSString";
+ case FormatStringType::Strftime:
+ return "strftime";
+ case FormatStringType::Strfmon:
+ return "strfmon";
+ case FormatStringType::Kprintf:
+ return "kprintf";
+ case FormatStringType::FreeBSDKPrintf:
+ return "freebsd_kprintf";
+ case FormatStringType::OSLog:
+ return "os_log";
+ default:
+ return "<unknown>";
+ }
+}
+
+FormatStringType Sema::GetFormatStringType(StringRef Flavor) {
+ return llvm::StringSwitch<FormatStringType>(Flavor)
+ .Cases({"gnu_scanf", "scanf"}, FormatStringType::Scanf)
+ .Cases({"gnu_printf", "printf", "printf0", "syslog"},
+ FormatStringType::Printf)
+ .Cases({"NSString", "CFString"}, FormatStringType::NSString)
+ .Cases({"gnu_strftime", "strftime"}, FormatStringType::Strftime)
+ .Cases({"gnu_strfmon", "strfmon"}, FormatStringType::Strfmon)
+ .Cases({"kprintf", "cmn_err", "vcmn_err", "zcmn_err"},
+ FormatStringType::Kprintf)
+ .Case("freebsd_kprintf", FormatStringType::FreeBSDKPrintf)
+ .Case("os_trace", FormatStringType::OSLog)
+ .Case("os_log", FormatStringType::OSLog)
+ .Default(FormatStringType::Unknown);
+}
+
+FormatStringType Sema::GetFormatStringType(const FormatAttr *Format) {
+ return GetFormatStringType(Format->getType()->getName());
+}
+
+FormatStringType Sema::GetFormatStringType(const FormatMatchesAttr *Format) {
+ return GetFormatStringType(Format->getType()->getName());
+}
+
+bool Sema::CheckFormatArguments(const FormatAttr *Format,
+ ArrayRef<const Expr *> Args, bool IsCXXMember,
+ VariadicCallType CallType, SourceLocation Loc,
+ SourceRange Range,
+ llvm::SmallBitVector &CheckedVarArgs) {
+ FormatStringInfo FSI;
+ if (getFormatStringInfo(Format->getFormatIdx(), Format->getFirstArg(),
+ IsCXXMember,
+ CallType != VariadicCallType::DoesNotApply, &FSI))
+ return CheckFormatArguments(
+ Args, FSI.ArgPassingKind, nullptr, FSI.FormatIdx, FSI.FirstDataArg,
+ GetFormatStringType(Format), CallType, Loc, Range, CheckedVarArgs);
+ return false;
+}
+
+bool Sema::CheckFormatString(const FormatMatchesAttr *Format,
+ ArrayRef<const Expr *> Args, bool IsCXXMember,
+ VariadicCallType CallType, SourceLocation Loc,
+ SourceRange Range,
+ llvm::SmallBitVector &CheckedVarArgs) {
+ FormatStringInfo FSI;
+ if (getFormatStringInfo(Format->getFormatIdx(), 0, IsCXXMember, false,
+ &FSI)) {
+ FSI.ArgPassingKind = Sema::FAPK_Elsewhere;
+ return CheckFormatArguments(Args, FSI.ArgPassingKind,
+ Format->getFormatString(), FSI.FormatIdx,
+ FSI.FirstDataArg, GetFormatStringType(Format),
+ CallType, Loc, Range, CheckedVarArgs);
+ }
+ return false;
+}
+
+static bool CheckMissingFormatAttribute(
+ Sema *S, ArrayRef<const Expr *> Args, Sema::FormatArgumentPassingKind APK,
+ StringLiteral *ReferenceFormatString, unsigned FormatIdx,
+ unsigned FirstDataArg, FormatStringType FormatType, unsigned CallerParamIdx,
+ SourceLocation Loc) {
+ if (S->getDiagnostics().isIgnored(diag::warn_missing_format_attribute, Loc))
+ return false;
+
+ DeclContext *DC = S->CurContext;
+ if (!isa<ObjCMethodDecl>(DC) && !isa<FunctionDecl>(DC) && !isa<BlockDecl>(DC))
+ return false;
+ Decl *Caller = cast<Decl>(DC)->getCanonicalDecl();
+
+ unsigned NumCallerParams = getFunctionOrMethodNumParams(Caller);
+
+ // Find the offset to convert between attribute and parameter indexes.
+ unsigned CallerArgumentIndexOffset =
+ hasImplicitObjectParameter(Caller) ? 2 : 1;
+
+ unsigned FirstArgumentIndex = -1;
+ switch (APK) {
+ case Sema::FormatArgumentPassingKind::FAPK_Fixed:
+ case Sema::FormatArgumentPassingKind::FAPK_Variadic: {
+ // As an extension, clang allows the format attribute on non-variadic
+ // functions.
+ // Caller must have fixed arguments to pass them to a fixed or variadic
+ // function. Try to match caller and callee arguments. If successful, then
+ // emit a diag with the caller idx, otherwise we can't determine the callee
+ // arguments.
+ unsigned NumCalleeArgs = Args.size() - FirstDataArg;
+ if (NumCalleeArgs == 0 || NumCallerParams < NumCalleeArgs) {
+ // There aren't enough arguments in the caller to pass to callee.
+ return false;
+ }
+ for (unsigned CalleeIdx = Args.size() - 1, CallerIdx = NumCallerParams - 1;
+ CalleeIdx >= FirstDataArg; --CalleeIdx, --CallerIdx) {
+ const auto *Arg =
+ dyn_cast<DeclRefExpr>(Args[CalleeIdx]->IgnoreParenCasts());
+ if (!Arg)
+ return false;
+ const auto *Param = dyn_cast<ParmVarDecl>(Arg->getDecl());
+ if (!Param || Param->getFunctionScopeIndex() != CallerIdx)
+ return false;
+ }
+ FirstArgumentIndex =
+ NumCallerParams + CallerArgumentIndexOffset - NumCalleeArgs;
+ break;
+ }
+ case Sema::FormatArgumentPassingKind::FAPK_VAList:
+ // Caller arguments are either variadic or a va_list.
+ FirstArgumentIndex = isFunctionOrMethodVariadic(Caller)
+ ? (NumCallerParams + CallerArgumentIndexOffset)
+ : 0;
+ break;
+ case Sema::FormatArgumentPassingKind::FAPK_Elsewhere:
+ // The callee has a format_matches attribute. We will emit that instead.
+ if (!ReferenceFormatString)
+ return false;
+ break;
+ }
+
+ // Emit the diagnostic and fixit.
+ unsigned FormatStringIndex = CallerParamIdx + CallerArgumentIndexOffset;
+ StringRef FormatTypeName = S->GetFormatStringTypeName(FormatType);
+ NamedDecl *ND = dyn_cast<NamedDecl>(Caller);
+ do {
+ std::string Attr, Fixit;
+ llvm::raw_string_ostream AttrOS(Attr);
+ if (APK != Sema::FormatArgumentPassingKind::FAPK_Elsewhere) {
+ AttrOS << "format(" << FormatTypeName << ", " << FormatStringIndex << ", "
+ << FirstArgumentIndex << ")";
+ } else {
+ AttrOS << "format_matches(" << FormatTypeName << ", " << FormatStringIndex
+ << ", \"";
+ AttrOS.write_escaped(ReferenceFormatString->getString());
+ AttrOS << "\")";
+ }
+ AttrOS.flush();
+ auto DB = S->Diag(Loc, diag::warn_missing_format_attribute) << Attr;
+ if (ND)
+ DB << ND;
+ else
+ DB << "block";
+
+ // Blocks don't provide a correct end loc, so skip emitting a fixit.
+ if (isa<BlockDecl>(Caller))
+ break;
+
+ SourceLocation SL;
+ llvm::raw_string_ostream IS(Fixit);
+ // The attribute goes at the start of the declaration in C/C++ functions
+ // and methods, but after the declaration for Objective-C methods.
+ if (isa<ObjCMethodDecl>(Caller)) {
+ IS << ' ';
+ SL = Caller->getEndLoc();
+ }
+ const LangOptions &LO = S->getLangOpts();
+ if (LO.C23 || LO.CPlusPlus11)
+ IS << "[[gnu::" << Attr << "]]";
+ else if (LO.ObjC || LO.GNUMode)
+ IS << "__attribute__((" << Attr << "))";
+ else
+ break;
+ if (!isa<ObjCMethodDecl>(Caller)) {
+ IS << ' ';
+ SL = Caller->getBeginLoc();
+ }
+ IS.flush();
+
+ DB << FixItHint::CreateInsertion(SL, Fixit);
+ } while (false);
+
+ // Add implicit format or format_matches attribute.
+ if (APK != Sema::FormatArgumentPassingKind::FAPK_Elsewhere) {
+ Caller->addAttr(FormatAttr::CreateImplicit(
+ S->getASTContext(), &S->getASTContext().Idents.get(FormatTypeName),
+ FormatStringIndex, FirstArgumentIndex));
+ } else {
+ Caller->addAttr(FormatMatchesAttr::CreateImplicit(
+ S->getASTContext(), &S->getASTContext().Idents.get(FormatTypeName),
+ FormatStringIndex, ReferenceFormatString));
+ }
+
+ {
+ auto DB = S->Diag(Caller->getLocation(), diag::note_entity_declared_at);
+ if (ND)
+ DB << ND;
+ else
+ DB << "block";
+ }
+ return true;
+}
+
+bool Sema::CheckFormatArguments(ArrayRef<const Expr *> Args,
+ Sema::FormatArgumentPassingKind APK,
+ StringLiteral *ReferenceFormatString,
+ unsigned format_idx, unsigned firstDataArg,
+ FormatStringType Type,
+ VariadicCallType CallType, SourceLocation Loc,
+ SourceRange Range,
+ llvm::SmallBitVector &CheckedVarArgs) {
+ // CHECK: printf/scanf-like function is called with no format string.
+ if (format_idx >= Args.size()) {
+ Diag(Loc, diag::warn_missing_format_string) << Range;
+ return false;
+ }
+
+ const Expr *OrigFormatExpr = Args[format_idx]->IgnoreParenCasts();
+
+ // CHECK: format string is not a string literal.
+ //
+ // Dynamically generated format strings are difficult to
+ // automatically vet at compile time. Requiring that format strings
+ // are string literals: (1) permits the checking of format strings by
+ // the compiler and thereby (2) can practically remove the source of
+ // many format string exploits.
+
+ // Format string can be either ObjC string (e.g. @"%d") or
+ // C string (e.g. "%d")
+ // ObjC string uses the same format specifiers as C string, so we can use
+ // the same format string checking logic for both ObjC and C strings.
+ UncoveredArgHandler UncoveredArg;
+ std::optional<unsigned> CallerParamIdx;
+ StringLiteralCheckType CT = checkFormatStringExpr(
+ *this, ReferenceFormatString, OrigFormatExpr, Args, APK, format_idx,
+ firstDataArg, Type, CallType,
+ /*IsFunctionCall*/ true, CheckedVarArgs, UncoveredArg,
+ /*no string offset*/ llvm::APSInt(64, false) = 0, &CallerParamIdx);
+
+ // Generate a diagnostic where an uncovered argument is detected.
+ if (UncoveredArg.hasUncoveredArg()) {
+ unsigned ArgIdx = UncoveredArg.getUncoveredArg() + firstDataArg;
+ assert(ArgIdx < Args.size() && "ArgIdx outside bounds");
+ UncoveredArg.Diagnose(*this, /*IsFunctionCall*/true, Args[ArgIdx]);
+ }
+
+ if (CT != SLCT_NotALiteral)
+ // Literal format string found, check done!
+ return CT == SLCT_CheckedLiteral;
+
+ // Do not emit diag when the string param is a macro expansion and the
+ // format is either NSString or CFString. This is a hack to prevent
+ // diag when using the NSLocalizedString and CFCopyLocalizedString macros
+ // which are usually used in place of NS and CF string literals.
+ SourceLocation FormatLoc = Args[format_idx]->getBeginLoc();
+ if (Type == FormatStringType::NSString &&
+ SourceMgr.isInSystemMacro(FormatLoc))
+ return false;
+
+ if (CallerParamIdx && CheckMissingFormatAttribute(
+ this, Args, APK, ReferenceFormatString, format_idx,
+ firstDataArg, Type, *CallerParamIdx, Loc))
+ return false;
+
+ // Strftime is particular as it always uses a single 'time' argument,
+ // so it is safe to pass a non-literal string.
+ if (Type == FormatStringType::Strftime)
+ return false;
+
+ // If there are no arguments specified, warn with -Wformat-security, otherwise
+ // warn only with -Wformat-nonliteral.
+ if (Args.size() == firstDataArg) {
+ Diag(FormatLoc, diag::warn_format_nonliteral_noargs)
+ << OrigFormatExpr->getSourceRange();
+ switch (Type) {
+ default:
+ break;
+ case FormatStringType::Kprintf:
+ case FormatStringType::FreeBSDKPrintf:
+ case FormatStringType::Printf:
+ Diag(FormatLoc, diag::note_format_security_fixit)
+ << FixItHint::CreateInsertion(FormatLoc, "\"%s\", ");
+ break;
+ case FormatStringType::NSString:
+ Diag(FormatLoc, diag::note_format_security_fixit)
+ << FixItHint::CreateInsertion(FormatLoc, "@\"%@\", ");
+ break;
+ }
+ } else {
+ Diag(FormatLoc, diag::warn_format_nonliteral)
+ << OrigFormatExpr->getSourceRange();
+ }
+ return false;
+}
+
+namespace {
+
+class CheckFormatHandler : public analyze_format_string::FormatStringHandler {
+protected:
+ Sema &S;
+ const FormatStringLiteral *FExpr;
+ const Expr *OrigFormatExpr;
+ const FormatStringType FSType;
+ const unsigned FirstDataArg;
+ const unsigned NumDataArgs;
+ const char *Beg; // Start of format string.
+ const Sema::FormatArgumentPassingKind ArgPassingKind;
+ ArrayRef<const Expr *> Args;
+ unsigned FormatIdx;
+ llvm::SmallBitVector CoveredArgs;
+ bool usesPositionalArgs = false;
+ bool atFirstArg = true;
+ bool inFunctionCall;
+ VariadicCallType CallType;
+ llvm::SmallBitVector &CheckedVarArgs;
+ UncoveredArgHandler &UncoveredArg;
+
+public:
+ CheckFormatHandler(Sema &s, const FormatStringLiteral *fexpr,
+ const Expr *origFormatExpr, const FormatStringType type,
+ unsigned firstDataArg, unsigned numDataArgs,
+ const char *beg, Sema::FormatArgumentPassingKind APK,
+ ArrayRef<const Expr *> Args, unsigned formatIdx,
+ bool inFunctionCall, VariadicCallType callType,
+ llvm::SmallBitVector &CheckedVarArgs,
+ UncoveredArgHandler &UncoveredArg)
+ : S(s), FExpr(fexpr), OrigFormatExpr(origFormatExpr), FSType(type),
+ FirstDataArg(firstDataArg), NumDataArgs(numDataArgs), Beg(beg),
+ ArgPassingKind(APK), Args(Args), FormatIdx(formatIdx),
+ inFunctionCall(inFunctionCall), CallType(callType),
+ CheckedVarArgs(CheckedVarArgs), UncoveredArg(UncoveredArg) {
+ CoveredArgs.resize(numDataArgs);
+ CoveredArgs.reset();
+ }
+
+ bool HasFormatArguments() const {
+ return ArgPassingKind == Sema::FAPK_Fixed ||
+ ArgPassingKind == Sema::FAPK_Variadic;
+ }
+
+ void DoneProcessing();
+
+ void HandleIncompleteSpecifier(const char *startSpecifier,
+ unsigned specifierLen) override;
+
+ void HandleInvalidLengthModifier(
+ const analyze_format_string::FormatSpecifier &FS,
+ const analyze_format_string::ConversionSpecifier &CS,
+ const char *startSpecifier, unsigned specifierLen,
+ unsigned DiagID);
+
+ void HandleNonStandardLengthModifier(
+ const analyze_format_string::FormatSpecifier &FS,
+ const char *startSpecifier, unsigned specifierLen);
+
+ void HandleNonStandardConversionSpecifier(
+ const analyze_format_string::ConversionSpecifier &CS,
+ const char *startSpecifier, unsigned specifierLen);
+
+ void HandlePosition(const char *startPos, unsigned posLen) override;
+
+ void HandleInvalidPosition(const char *startSpecifier,
+ unsigned specifierLen,
+ analyze_format_string::PositionContext p) override;
+
+ void HandleZeroPosition(const char *startPos, unsigned posLen) override;
+
+ void HandleNullChar(const char *nullCharacter) override;
+
+ template <typename Range>
+ static void
+ EmitFormatDiagnostic(Sema &S, bool inFunctionCall, const Expr *ArgumentExpr,
+ const PartialDiagnostic &PDiag, SourceLocation StringLoc,
+ bool IsStringLocation, Range StringRange,
+ ArrayRef<FixItHint> Fixit = {});
+
+protected:
+ bool HandleInvalidConversionSpecifier(unsigned argIndex, SourceLocation Loc,
+ const char *startSpec,
+ unsigned specifierLen,
+ const char *csStart, unsigned csLen);
+
+ void HandlePositionalNonpositionalArgs(SourceLocation Loc,
+ const char *startSpec,
+ unsigned specifierLen);
+
+ SourceRange getFormatStringRange();
+ CharSourceRange getSpecifierRange(const char *startSpecifier,
+ unsigned specifierLen);
+ SourceLocation getLocationOfByte(const char *x);
+
+ const Expr *getDataArg(unsigned i) const;
+
+ bool CheckNumArgs(const analyze_format_string::FormatSpecifier &FS,
+ const analyze_format_string::ConversionSpecifier &CS,
+ const char *startSpecifier, unsigned specifierLen,
+ unsigned argIndex);
+
+ template <typename Range>
+ void EmitFormatDiagnostic(PartialDiagnostic PDiag, SourceLocation StringLoc,
+ bool IsStringLocation, Range StringRange,
+ ArrayRef<FixItHint> Fixit = {});
+};
+
+} // namespace
+
+SourceRange CheckFormatHandler::getFormatStringRange() {
+ return OrigFormatExpr->getSourceRange();
+}
+
+CharSourceRange CheckFormatHandler::
+getSpecifierRange(const char *startSpecifier, unsigned specifierLen) {
+ SourceLocation Start = getLocationOfByte(startSpecifier);
+ SourceLocation End = getLocationOfByte(startSpecifier + specifierLen - 1);
+
+ // Advance the end SourceLocation by one due to half-open ranges.
+ End = End.getLocWithOffset(1);
+
+ return CharSourceRange::getCharRange(Start, End);
+}
+
+SourceLocation CheckFormatHandler::getLocationOfByte(const char *x) {
+ return FExpr->getLocationOfByte(x - Beg, S.getSourceManager(),
+ S.getLangOpts(), S.Context.getTargetInfo());
+}
+
+void CheckFormatHandler::HandleIncompleteSpecifier(const char *startSpecifier,
+ unsigned specifierLen){
+ EmitFormatDiagnostic(S.PDiag(diag::warn_printf_incomplete_specifier),
+ getLocationOfByte(startSpecifier),
+ /*IsStringLocation*/true,
+ getSpecifierRange(startSpecifier, specifierLen));
+}
+
+void CheckFormatHandler::HandleInvalidLengthModifier(
+ const analyze_format_string::FormatSpecifier &FS,
+ const analyze_format_string::ConversionSpecifier &CS,
+ const char *startSpecifier, unsigned specifierLen, unsigned DiagID) {
+ using namespace analyze_format_string;
+
+ const LengthModifier &LM = FS.getLengthModifier();
+ CharSourceRange LMRange = getSpecifierRange(LM.getStart(), LM.getLength());
+
+ // See if we know how to fix this length modifier.
+ std::optional<LengthModifier> FixedLM = FS.getCorrectedLengthModifier();
+ if (FixedLM) {
+ EmitFormatDiagnostic(S.PDiag(DiagID) << LM.toString() << CS.toString(),
+ getLocationOfByte(LM.getStart()),
+ /*IsStringLocation*/true,
+ getSpecifierRange(startSpecifier, specifierLen));
+
+ S.Diag(getLocationOfByte(LM.getStart()), diag::note_format_fix_specifier)
+ << FixedLM->toString()
+ << FixItHint::CreateReplacement(LMRange, FixedLM->toString());
+
+ } else {
+ FixItHint Hint;
+ if (DiagID == diag::warn_format_nonsensical_length)
+ Hint = FixItHint::CreateRemoval(LMRange);
+
+ EmitFormatDiagnostic(S.PDiag(DiagID) << LM.toString() << CS.toString(),
+ getLocationOfByte(LM.getStart()),
+ /*IsStringLocation*/true,
+ getSpecifierRange(startSpecifier, specifierLen),
+ Hint);
+ }
+}
+
+void CheckFormatHandler::HandleNonStandardLengthModifier(
+ const analyze_format_string::FormatSpecifier &FS,
+ const char *startSpecifier, unsigned specifierLen) {
+ using namespace analyze_format_string;
+
+ const LengthModifier &LM = FS.getLengthModifier();
+ CharSourceRange LMRange = getSpecifierRange(LM.getStart(), LM.getLength());
+
+ // See if we know how to fix this length modifier.
+ std::optional<LengthModifier> FixedLM = FS.getCorrectedLengthModifier();
+ if (FixedLM) {
+ EmitFormatDiagnostic(S.PDiag(diag::warn_format_non_standard)
+ << LM.toString() << 0,
+ getLocationOfByte(LM.getStart()),
+ /*IsStringLocation*/true,
+ getSpecifierRange(startSpecifier, specifierLen));
+
+ S.Diag(getLocationOfByte(LM.getStart()), diag::note_format_fix_specifier)
+ << FixedLM->toString()
+ << FixItHint::CreateReplacement(LMRange, FixedLM->toString());
+
+ } else {
+ EmitFormatDiagnostic(S.PDiag(diag::warn_format_non_standard)
+ << LM.toString() << 0,
+ getLocationOfByte(LM.getStart()),
+ /*IsStringLocation*/true,
+ getSpecifierRange(startSpecifier, specifierLen));
+ }
+}
+
+void CheckFormatHandler::HandleNonStandardConversionSpecifier(
+ const analyze_format_string::ConversionSpecifier &CS,
+ const char *startSpecifier, unsigned specifierLen) {
+ using namespace analyze_format_string;
+
+ // See if we know how to fix this conversion specifier.
+ std::optional<ConversionSpecifier> FixedCS = CS.getStandardSpecifier();
+ if (FixedCS) {
+ EmitFormatDiagnostic(S.PDiag(diag::warn_format_non_standard)
+ << CS.toString() << /*conversion specifier*/1,
+ getLocationOfByte(CS.getStart()),
+ /*IsStringLocation*/true,
+ getSpecifierRange(startSpecifier, specifierLen));
+
+ CharSourceRange CSRange = getSpecifierRange(CS.getStart(), CS.getLength());
+ S.Diag(getLocationOfByte(CS.getStart()), diag::note_format_fix_specifier)
+ << FixedCS->toString()
+ << FixItHint::CreateReplacement(CSRange, FixedCS->toString());
+ } else {
+ EmitFormatDiagnostic(S.PDiag(diag::warn_format_non_standard)
+ << CS.toString() << /*conversion specifier*/1,
+ getLocationOfByte(CS.getStart()),
+ /*IsStringLocation*/true,
+ getSpecifierRange(startSpecifier, specifierLen));
+ }
+}
+
+void CheckFormatHandler::HandlePosition(const char *startPos,
+ unsigned posLen) {
+ if (!S.getDiagnostics().isIgnored(
+ diag::warn_format_non_standard_positional_arg, SourceLocation()))
+ EmitFormatDiagnostic(S.PDiag(diag::warn_format_non_standard_positional_arg),
+ getLocationOfByte(startPos),
+ /*IsStringLocation*/ true,
+ getSpecifierRange(startPos, posLen));
+}
+
+void CheckFormatHandler::HandleInvalidPosition(
+ const char *startSpecifier, unsigned specifierLen,
+ analyze_format_string::PositionContext p) {
+ if (!S.getDiagnostics().isIgnored(
+ diag::warn_format_invalid_positional_specifier, SourceLocation()))
+ EmitFormatDiagnostic(
+ S.PDiag(diag::warn_format_invalid_positional_specifier) << (unsigned)p,
+ getLocationOfByte(startSpecifier), /*IsStringLocation*/ true,
+ getSpecifierRange(startSpecifier, specifierLen));
+}
+
+void CheckFormatHandler::HandleZeroPosition(const char *startPos,
+ unsigned posLen) {
+ if (!S.getDiagnostics().isIgnored(diag::warn_format_zero_positional_specifier,
+ SourceLocation()))
+ EmitFormatDiagnostic(S.PDiag(diag::warn_format_zero_positional_specifier),
+ getLocationOfByte(startPos),
+ /*IsStringLocation*/ true,
+ getSpecifierRange(startPos, posLen));
+}
+
+void CheckFormatHandler::HandleNullChar(const char *nullCharacter) {
+ if (!isa<ObjCStringLiteral>(OrigFormatExpr)) {
+ // The presence of a null character is likely an error.
+ EmitFormatDiagnostic(
+ S.PDiag(diag::warn_printf_format_string_contains_null_char),
+ getLocationOfByte(nullCharacter), /*IsStringLocation*/true,
+ getFormatStringRange());
+ }
+}
+
+// Note that this may return NULL if there was an error parsing or building
+// one of the argument expressions.
+const Expr *CheckFormatHandler::getDataArg(unsigned i) const {
+ return Args[FirstDataArg + i];
+}
+
+void CheckFormatHandler::DoneProcessing() {
+ // Does the number of data arguments exceed the number of
+ // format conversions in the format string?
+ if (HasFormatArguments()) {
+ // Find any arguments that weren't covered.
+ CoveredArgs.flip();
+ signed notCoveredArg = CoveredArgs.find_first();
+ if (notCoveredArg >= 0) {
+ assert((unsigned)notCoveredArg < NumDataArgs);
+ UncoveredArg.Update(notCoveredArg, OrigFormatExpr);
+ } else {
+ UncoveredArg.setAllCovered();
+ }
+ }
+}
+
+void UncoveredArgHandler::Diagnose(Sema &S, bool IsFunctionCall,
+ const Expr *ArgExpr) {
+ assert(hasUncoveredArg() && !DiagnosticExprs.empty() &&
+ "Invalid state");
+
+ if (!ArgExpr)
+ return;
+
+ SourceLocation Loc = ArgExpr->getBeginLoc();
+
+ if (S.getSourceManager().isInSystemMacro(Loc))
+ return;
+
+ PartialDiagnostic PDiag = S.PDiag(diag::warn_printf_data_arg_not_used);
+ for (auto E : DiagnosticExprs)
+ PDiag << E->getSourceRange();
+
+ CheckFormatHandler::EmitFormatDiagnostic(
+ S, IsFunctionCall, DiagnosticExprs[0],
+ PDiag, Loc, /*IsStringLocation*/false,
+ DiagnosticExprs[0]->getSourceRange());
+}
+
+bool
+CheckFormatHandler::HandleInvalidConversionSpecifier(unsigned argIndex,
+ SourceLocation Loc,
+ const char *startSpec,
+ unsigned specifierLen,
+ const char *csStart,
+ unsigned csLen) {
+ bool keepGoing = true;
+ if (argIndex < NumDataArgs) {
+ // Consider the argument coverered, even though the specifier doesn't
+ // make sense.
+ CoveredArgs.set(argIndex);
+ }
+ else {
+ // If argIndex exceeds the number of data arguments we
+ // don't issue a warning because that is just a cascade of warnings (and
+ // they may have intended '%%' anyway). We don't want to continue processing
+ // the format string after this point, however, as we will like just get
+ // gibberish when trying to match arguments.
+ keepGoing = false;
+ }
+
+ StringRef Specifier(csStart, csLen);
+
+ // If the specifier in non-printable, it could be the first byte of a UTF-8
+ // sequence. In that case, print the UTF-8 code point. If not, print the byte
+ // hex value.
+ std::string CodePointStr;
+ if (!llvm::sys::locale::isPrint(*csStart)) {
+ llvm::UTF32 CodePoint;
+ const llvm::UTF8 **B = reinterpret_cast<const llvm::UTF8 **>(&csStart);
+ const llvm::UTF8 *E =
+ reinterpret_cast<const llvm::UTF8 *>(csStart + csLen);
+ llvm::ConversionResult Result =
+ llvm::convertUTF8Sequence(B, E, &CodePoint, llvm::strictConversion);
+
+ if (Result != llvm::conversionOK) {
+ unsigned char FirstChar = *csStart;
+ CodePoint = (llvm::UTF32)FirstChar;
+ }
+
+ llvm::raw_string_ostream OS(CodePointStr);
+ if (CodePoint < 256)
+ OS << "\\x" << llvm::format("%02x", CodePoint);
+ else if (CodePoint <= 0xFFFF)
+ OS << "\\u" << llvm::format("%04x", CodePoint);
+ else
+ OS << "\\U" << llvm::format("%08x", CodePoint);
+ Specifier = CodePointStr;
+ }
+
+ EmitFormatDiagnostic(
+ S.PDiag(diag::warn_format_invalid_conversion) << Specifier, Loc,
+ /*IsStringLocation*/ true, getSpecifierRange(startSpec, specifierLen));
+
+ return keepGoing;
+}
+
+void
+CheckFormatHandler::HandlePositionalNonpositionalArgs(SourceLocation Loc,
+ const char *startSpec,
+ unsigned specifierLen) {
+ EmitFormatDiagnostic(
+ S.PDiag(diag::warn_format_mix_positional_nonpositional_args),
+ Loc, /*isStringLoc*/true, getSpecifierRange(startSpec, specifierLen));
+}
+
+bool
+CheckFormatHandler::CheckNumArgs(
+ const analyze_format_string::FormatSpecifier &FS,
+ const analyze_format_string::ConversionSpecifier &CS,
+ const char *startSpecifier, unsigned specifierLen, unsigned argIndex) {
+
+ if (HasFormatArguments() && argIndex >= NumDataArgs) {
+ PartialDiagnostic PDiag = FS.usesPositionalArg()
+ ? (S.PDiag(diag::warn_printf_positional_arg_exceeds_data_args)
+ << (argIndex+1) << NumDataArgs)
+ : S.PDiag(diag::warn_printf_insufficient_data_args);
+ EmitFormatDiagnostic(
+ PDiag, getLocationOfByte(CS.getStart()), /*IsStringLocation*/true,
+ getSpecifierRange(startSpecifier, specifierLen));
+
+ // Since more arguments than conversion tokens are given, by extension
+ // all arguments are covered, so mark this as so.
+ UncoveredArg.setAllCovered();
+ return false;
+ }
+ return true;
+}
+
+template<typename Range>
+void CheckFormatHandler::EmitFormatDiagnostic(PartialDiagnostic PDiag,
+ SourceLocation Loc,
+ bool IsStringLocation,
+ Range StringRange,
+ ArrayRef<FixItHint> FixIt) {
+ EmitFormatDiagnostic(S, inFunctionCall, Args[FormatIdx], PDiag,
+ Loc, IsStringLocation, StringRange, FixIt);
+}
+
+/// If the format string is not within the function call, emit a note
+/// so that the function call and string are in diagnostic messages.
+///
+/// \param InFunctionCall if true, the format string is within the function
+/// call and only one diagnostic message will be produced. Otherwise, an
+/// extra note will be emitted pointing to location of the format string.
+///
+/// \param ArgumentExpr the expression that is passed as the format string
+/// argument in the function call. Used for getting locations when two
+/// diagnostics are emitted.
+///
+/// \param PDiag the callee should already have provided any strings for the
+/// diagnostic message. This function only adds locations and fixits
+/// to diagnostics.
+///
+/// \param Loc primary location for diagnostic. If two diagnostics are
+/// required, one will be at Loc and a new SourceLocation will be created for
+/// the other one.
+///
+/// \param IsStringLocation if true, Loc points to the format string should be
+/// used for the note. Otherwise, Loc points to the argument list and will
+/// be used with PDiag.
+///
+/// \param StringRange some or all of the string to highlight. This is
+/// templated so it can accept either a CharSourceRange or a SourceRange.
+///
+/// \param FixIt optional fix it hint for the format string.
+template <typename Range>
+void CheckFormatHandler::EmitFormatDiagnostic(
+ Sema &S, bool InFunctionCall, const Expr *ArgumentExpr,
+ const PartialDiagnostic &PDiag, SourceLocation Loc, bool IsStringLocation,
+ Range StringRange, ArrayRef<FixItHint> FixIt) {
+ if (InFunctionCall) {
+ const Sema::SemaDiagnosticBuilder &D = S.Diag(Loc, PDiag);
+ D << StringRange;
+ D << FixIt;
+ } else {
+ S.Diag(IsStringLocation ? ArgumentExpr->getExprLoc() : Loc, PDiag)
+ << ArgumentExpr->getSourceRange();
+
+ const Sema::SemaDiagnosticBuilder &Note =
+ S.Diag(IsStringLocation ? Loc : StringRange.getBegin(),
+ diag::note_format_string_defined);
+
+ Note << StringRange;
+ Note << FixIt;
+ }
+}
+
+//===--- CHECK: Printf format string checking -----------------------------===//
+
+namespace {
+
+class CheckPrintfHandler : public CheckFormatHandler {
+public:
+ CheckPrintfHandler(Sema &s, const FormatStringLiteral *fexpr,
+ const Expr *origFormatExpr, const FormatStringType type,
+ unsigned firstDataArg, unsigned numDataArgs, bool isObjC,
+ const char *beg, Sema::FormatArgumentPassingKind APK,
+ ArrayRef<const Expr *> Args, unsigned formatIdx,
+ bool inFunctionCall, VariadicCallType CallType,
+ llvm::SmallBitVector &CheckedVarArgs,
+ UncoveredArgHandler &UncoveredArg)
+ : CheckFormatHandler(s, fexpr, origFormatExpr, type, firstDataArg,
+ numDataArgs, beg, APK, Args, formatIdx,
+ inFunctionCall, CallType, CheckedVarArgs,
+ UncoveredArg) {}
+
+ bool isObjCContext() const { return FSType == FormatStringType::NSString; }
+
+ /// Returns true if '%@' specifiers are allowed in the format string.
+ bool allowsObjCArg() const {
+ return FSType == FormatStringType::NSString ||
+ FSType == FormatStringType::OSLog ||
+ FSType == FormatStringType::OSTrace;
+ }
+
+ bool HandleInvalidPrintfConversionSpecifier(
+ const analyze_printf::PrintfSpecifier &FS,
+ const char *startSpecifier,
+ unsigned specifierLen) override;
+
+ void handleInvalidMaskType(StringRef MaskType) override;
+
+ bool HandlePrintfSpecifier(const analyze_printf::PrintfSpecifier &FS,
+ const char *startSpecifier, unsigned specifierLen,
+ const TargetInfo &Target) override;
+ bool checkFormatExpr(const analyze_printf::PrintfSpecifier &FS,
+ const char *StartSpecifier,
+ unsigned SpecifierLen,
+ const Expr *E);
+
+ bool HandleAmount(const analyze_format_string::OptionalAmount &Amt, unsigned k,
+ const char *startSpecifier, unsigned specifierLen);
+ void HandleInvalidAmount(const analyze_printf::PrintfSpecifier &FS,
+ const analyze_printf::OptionalAmount &Amt,
+ unsigned type,
+ const char *startSpecifier, unsigned specifierLen);
+ void HandleFlag(const analyze_printf::PrintfSpecifier &FS,
+ const analyze_printf::OptionalFlag &flag,
+ const char *startSpecifier, unsigned specifierLen);
+ void HandleIgnoredFlag(const analyze_printf::PrintfSpecifier &FS,
+ const analyze_printf::OptionalFlag &ignoredFlag,
+ const analyze_printf::OptionalFlag &flag,
+ const char *startSpecifier, unsigned specifierLen);
+ bool checkForCStrMembers(const analyze_printf::ArgType &AT,
+ const Expr *E);
+
+ void HandleEmptyObjCModifierFlag(const char *startFlag,
+ unsigned flagLen) override;
+
+ void HandleInvalidObjCModifierFlag(const char *startFlag,
+ unsigned flagLen) override;
+
+ void
+ HandleObjCFlagsWithNonObjCConversion(const char *flagsStart,
+ const char *flagsEnd,
+ const char *conversionPosition) override;
+};
+
+/// Keeps around the information needed to verify that two specifiers are
+/// compatible.
+class EquatableFormatArgument {
+public:
+ enum SpecifierSensitivity : unsigned {
+ SS_None,
+ SS_Private,
+ SS_Public,
+ SS_Sensitive
+ };
+
+ enum FormatArgumentRole : unsigned {
+ FAR_Data,
+ FAR_FieldWidth,
+ FAR_Precision,
+ FAR_Auxiliary, // FreeBSD kernel %b and %D
+ };
+
+private:
+ analyze_format_string::ArgType ArgType;
+ analyze_format_string::LengthModifier::Kind LengthMod;
+ StringRef SpecifierLetter;
+ CharSourceRange Range;
+ SourceLocation ElementLoc;
+ FormatArgumentRole Role : 2;
+ SpecifierSensitivity Sensitivity : 2; // only set for FAR_Data
+ unsigned Position : 14;
+ unsigned ModifierFor : 14; // not set for FAR_Data
+
+ void EmitDiagnostic(Sema &S, PartialDiagnostic PDiag, const Expr *FmtExpr,
+ bool InFunctionCall) const;
+
+public:
+ EquatableFormatArgument(CharSourceRange Range, SourceLocation ElementLoc,
+ analyze_format_string::LengthModifier::Kind LengthMod,
+ StringRef SpecifierLetter,
+ analyze_format_string::ArgType ArgType,
+ FormatArgumentRole Role,
+ SpecifierSensitivity Sensitivity, unsigned Position,
+ unsigned ModifierFor)
+ : ArgType(ArgType), LengthMod(LengthMod),
+ SpecifierLetter(SpecifierLetter), Range(Range), ElementLoc(ElementLoc),
+ Role(Role), Sensitivity(Sensitivity), Position(Position),
+ ModifierFor(ModifierFor) {}
+
+ unsigned getPosition() const { return Position; }
+ SourceLocation getSourceLocation() const { return ElementLoc; }
+ CharSourceRange getSourceRange() const { return Range; }
+ analyze_format_string::LengthModifier getLengthModifier() const {
+ return analyze_format_string::LengthModifier(nullptr, LengthMod);
+ }
+ void setModifierFor(unsigned V) { ModifierFor = V; }
+
+ std::string buildFormatSpecifier() const {
+ std::string result;
+ llvm::raw_string_ostream(result)
+ << getLengthModifier().toString() << SpecifierLetter;
+ return result;
+ }
+
+ bool VerifyCompatible(Sema &S, const EquatableFormatArgument &Other,
+ const Expr *FmtExpr, bool InFunctionCall) const;
+};
+
+/// Turns format strings into lists of EquatableSpecifier objects.
+class DecomposePrintfHandler : public CheckPrintfHandler {
+ llvm::SmallVectorImpl<EquatableFormatArgument> &Specs;
+ bool HadError;
+
+ DecomposePrintfHandler(Sema &s, const FormatStringLiteral *fexpr,
+ const Expr *origFormatExpr,
+ const FormatStringType type, unsigned firstDataArg,
+ unsigned numDataArgs, bool isObjC, const char *beg,
+ Sema::FormatArgumentPassingKind APK,
+ ArrayRef<const Expr *> Args, unsigned formatIdx,
+ bool inFunctionCall, VariadicCallType CallType,
+ llvm::SmallBitVector &CheckedVarArgs,
+ UncoveredArgHandler &UncoveredArg,
+ llvm::SmallVectorImpl<EquatableFormatArgument> &Specs)
+ : CheckPrintfHandler(s, fexpr, origFormatExpr, type, firstDataArg,
+ numDataArgs, isObjC, beg, APK, Args, formatIdx,
+ inFunctionCall, CallType, CheckedVarArgs,
+ UncoveredArg),
+ Specs(Specs), HadError(false) {}
+
+public:
+ static bool
+ GetSpecifiers(Sema &S, const FormatStringLiteral *FSL, const Expr *FmtExpr,
+ FormatStringType type, bool IsObjC, bool InFunctionCall,
+ llvm::SmallVectorImpl<EquatableFormatArgument> &Args);
+
+ virtual bool HandlePrintfSpecifier(const analyze_printf::PrintfSpecifier &FS,
+ const char *startSpecifier,
+ unsigned specifierLen,
+ const TargetInfo &Target) override;
+};
+
+} // namespace
+
+bool CheckPrintfHandler::HandleInvalidPrintfConversionSpecifier(
+ const analyze_printf::PrintfSpecifier &FS, const char *startSpecifier,
+ unsigned specifierLen) {
+ const analyze_printf::PrintfConversionSpecifier &CS =
+ FS.getConversionSpecifier();
+
+ return HandleInvalidConversionSpecifier(FS.getArgIndex(),
+ getLocationOfByte(CS.getStart()),
+ startSpecifier, specifierLen,
+ CS.getStart(), CS.getLength());
+}
+
+void CheckPrintfHandler::handleInvalidMaskType(StringRef MaskType) {
+ S.Diag(getLocationOfByte(MaskType.data()), diag::err_invalid_mask_type_size);
+}
+
+// Error out if struct or complex type argments are passed to os_log.
+static bool isInvalidOSLogArgTypeForCodeGen(FormatStringType FSType,
+ QualType T) {
+ if (FSType != FormatStringType::OSLog)
+ return false;
+ return T->isRecordType() || T->isComplexType();
+}
+
+bool CheckPrintfHandler::HandleAmount(
+ const analyze_format_string::OptionalAmount &Amt, unsigned k,
+ const char *startSpecifier, unsigned specifierLen) {
+ if (Amt.hasDataArgument()) {
+ if (HasFormatArguments()) {
+ unsigned argIndex = Amt.getArgIndex();
+ if (argIndex >= NumDataArgs) {
+ EmitFormatDiagnostic(S.PDiag(diag::warn_printf_asterisk_missing_arg)
+ << k,
+ getLocationOfByte(Amt.getStart()),
+ /*IsStringLocation*/ true,
+ getSpecifierRange(startSpecifier, specifierLen));
+ // Don't do any more checking. We will just emit
+ // spurious errors.
+ return false;
+ }
+
+ // Type check the data argument. It should be an 'int'.
+ // Although not in conformance with C99, we also allow the argument to be
+ // an 'unsigned int' as that is a reasonably safe case. GCC also
+ // doesn't emit a warning for that case.
+ CoveredArgs.set(argIndex);
+ const Expr *Arg = getDataArg(argIndex);
+ if (!Arg)
+ return false;
+
+ QualType T = Arg->getType();
+
+ const analyze_printf::ArgType &AT = Amt.getArgType(S.Context);
+ assert(AT.isValid());
+
+ if (!AT.matchesType(S.Context, T)) {
+ unsigned DiagID = isInvalidOSLogArgTypeForCodeGen(FSType, T)
+ ? diag::err_printf_asterisk_wrong_type
+ : diag::warn_printf_asterisk_wrong_type;
+ EmitFormatDiagnostic(S.PDiag(DiagID)
+ << k << AT.getRepresentativeTypeName(S.Context)
+ << T << Arg->getSourceRange(),
+ getLocationOfByte(Amt.getStart()),
+ /*IsStringLocation*/ true,
+ getSpecifierRange(startSpecifier, specifierLen));
+ // Don't do any more checking. We will just emit
+ // spurious errors.
+ return false;
+ }
+ }
+ }
+ return true;
+}
+
+void CheckPrintfHandler::HandleInvalidAmount(
+ const analyze_printf::PrintfSpecifier &FS,
+ const analyze_printf::OptionalAmount &Amt,
+ unsigned type,
+ const char *startSpecifier,
+ unsigned specifierLen) {
+ const analyze_printf::PrintfConversionSpecifier &CS =
+ FS.getConversionSpecifier();
+
+ FixItHint fixit =
+ Amt.getHowSpecified() == analyze_printf::OptionalAmount::Constant
+ ? FixItHint::CreateRemoval(getSpecifierRange(Amt.getStart(),
+ Amt.getConstantLength()))
+ : FixItHint();
+
+ EmitFormatDiagnostic(S.PDiag(diag::warn_printf_nonsensical_optional_amount)
+ << type << CS.toString(),
+ getLocationOfByte(Amt.getStart()),
+ /*IsStringLocation*/true,
+ getSpecifierRange(startSpecifier, specifierLen),
+ fixit);
+}
+
+void CheckPrintfHandler::HandleFlag(const analyze_printf::PrintfSpecifier &FS,
+ const analyze_printf::OptionalFlag &flag,
+ const char *startSpecifier,
+ unsigned specifierLen) {
+ // Warn about pointless flag with a fixit removal.
+ const analyze_printf::PrintfConversionSpecifier &CS =
+ FS.getConversionSpecifier();
+ EmitFormatDiagnostic(S.PDiag(diag::warn_printf_nonsensical_flag)
+ << flag.toString() << CS.toString(),
+ getLocationOfByte(flag.getPosition()),
+ /*IsStringLocation*/true,
+ getSpecifierRange(startSpecifier, specifierLen),
+ FixItHint::CreateRemoval(
+ getSpecifierRange(flag.getPosition(), 1)));
+}
+
+void CheckPrintfHandler::HandleIgnoredFlag(
+ const analyze_printf::PrintfSpecifier &FS,
+ const analyze_printf::OptionalFlag &ignoredFlag,
+ const analyze_printf::OptionalFlag &flag,
+ const char *startSpecifier,
+ unsigned specifierLen) {
+ // Warn about ignored flag with a fixit removal.
+ EmitFormatDiagnostic(S.PDiag(diag::warn_printf_ignored_flag)
+ << ignoredFlag.toString() << flag.toString(),
+ getLocationOfByte(ignoredFlag.getPosition()),
+ /*IsStringLocation*/true,
+ getSpecifierRange(startSpecifier, specifierLen),
+ FixItHint::CreateRemoval(
+ getSpecifierRange(ignoredFlag.getPosition(), 1)));
+}
+
+void CheckPrintfHandler::HandleEmptyObjCModifierFlag(const char *startFlag,
+ unsigned flagLen) {
+ // Warn about an empty flag.
+ EmitFormatDiagnostic(S.PDiag(diag::warn_printf_empty_objc_flag),
+ getLocationOfByte(startFlag),
+ /*IsStringLocation*/true,
+ getSpecifierRange(startFlag, flagLen));
+}
+
+void CheckPrintfHandler::HandleInvalidObjCModifierFlag(const char *startFlag,
+ unsigned flagLen) {
+ // Warn about an invalid flag.
+ auto Range = getSpecifierRange(startFlag, flagLen);
+ StringRef flag(startFlag, flagLen);
+ EmitFormatDiagnostic(S.PDiag(diag::warn_printf_invalid_objc_flag) << flag,
+ getLocationOfByte(startFlag),
+ /*IsStringLocation*/true,
+ Range, FixItHint::CreateRemoval(Range));
+}
+
+void CheckPrintfHandler::HandleObjCFlagsWithNonObjCConversion(
+ const char *flagsStart, const char *flagsEnd, const char *conversionPosition) {
+ // Warn about using '[...]' without a '@' conversion.
+ auto Range = getSpecifierRange(flagsStart, flagsEnd - flagsStart + 1);
+ auto diag = diag::warn_printf_ObjCflags_without_ObjCConversion;
+ EmitFormatDiagnostic(S.PDiag(diag) << StringRef(conversionPosition, 1),
+ getLocationOfByte(conversionPosition),
+ /*IsStringLocation*/ true, Range,
+ FixItHint::CreateRemoval(Range));
+}
+
+void EquatableFormatArgument::EmitDiagnostic(Sema &S, PartialDiagnostic PDiag,
+ const Expr *FmtExpr,
+ bool InFunctionCall) const {
+ CheckFormatHandler::EmitFormatDiagnostic(S, InFunctionCall, FmtExpr, PDiag,
+ ElementLoc, true, Range);
+}
+
+bool EquatableFormatArgument::VerifyCompatible(
+ Sema &S, const EquatableFormatArgument &Other, const Expr *FmtExpr,
+ bool InFunctionCall) const {
+ using MK = analyze_format_string::ArgType::MatchKind;
+ if (Role != Other.Role) {
+ // diagnose and stop
+ EmitDiagnostic(
+ S, S.PDiag(diag::warn_format_cmp_role_mismatch) << Role << Other.Role,
+ FmtExpr, InFunctionCall);
+ S.Diag(Other.ElementLoc, diag::note_format_cmp_with) << 0 << Other.Range;
+ return false;
+ }
+
+ if (Role != FAR_Data) {
+ if (ModifierFor != Other.ModifierFor) {
+ // diagnose and stop
+ EmitDiagnostic(S,
+ S.PDiag(diag::warn_format_cmp_modifierfor_mismatch)
+ << (ModifierFor + 1) << (Other.ModifierFor + 1),
+ FmtExpr, InFunctionCall);
+ S.Diag(Other.ElementLoc, diag::note_format_cmp_with) << 0 << Other.Range;
+ return false;
+ }
+ return true;
+ }
+
+ bool HadError = false;
+ if (Sensitivity != Other.Sensitivity) {
+ // diagnose and continue
+ EmitDiagnostic(S,
+ S.PDiag(diag::warn_format_cmp_sensitivity_mismatch)
+ << Sensitivity << Other.Sensitivity,
+ FmtExpr, InFunctionCall);
+ HadError = S.Diag(Other.ElementLoc, diag::note_format_cmp_with)
+ << 0 << Other.Range;
+ }
+
+ switch (ArgType.matchesArgType(S.Context, Other.ArgType)) {
+ case MK::Match:
+ break;
+
+ case MK::MatchPromotion:
+ // Per consensus reached at https://discourse.llvm.org/t/-/83076/12,
+ // MatchPromotion is treated as a failure by format_matches.
+ case MK::NoMatch:
+ case MK::NoMatchTypeConfusion:
+ case MK::NoMatchPromotionTypeConfusion:
+ EmitDiagnostic(S,
+ S.PDiag(diag::warn_format_cmp_specifier_mismatch)
+ << buildFormatSpecifier()
+ << Other.buildFormatSpecifier(),
+ FmtExpr, InFunctionCall);
+ HadError = S.Diag(Other.ElementLoc, diag::note_format_cmp_with)
+ << 0 << Other.Range;
+ break;
+
+ case MK::NoMatchPedantic:
+ EmitDiagnostic(S,
+ S.PDiag(diag::warn_format_cmp_specifier_mismatch_pedantic)
+ << buildFormatSpecifier()
+ << Other.buildFormatSpecifier(),
+ FmtExpr, InFunctionCall);
+ HadError = S.Diag(Other.ElementLoc, diag::note_format_cmp_with)
+ << 0 << Other.Range;
+ break;
+
+ case MK::NoMatchSignedness:
+ EmitDiagnostic(S,
+ S.PDiag(diag::warn_format_cmp_specifier_sign_mismatch)
+ << buildFormatSpecifier()
+ << Other.buildFormatSpecifier(),
+ FmtExpr, InFunctionCall);
+ HadError = S.Diag(Other.ElementLoc, diag::note_format_cmp_with)
+ << 0 << Other.Range;
+ break;
+ }
+ return !HadError;
+}
+
+bool DecomposePrintfHandler::GetSpecifiers(
+ Sema &S, const FormatStringLiteral *FSL, const Expr *FmtExpr,
+ FormatStringType Type, bool IsObjC, bool InFunctionCall,
+ llvm::SmallVectorImpl<EquatableFormatArgument> &Args) {
+ StringRef Data = FSL->getString();
+ const char *Str = Data.data();
+ llvm::SmallBitVector BV;
+ UncoveredArgHandler UA;
+ const Expr *PrintfArgs[] = {FSL->getFormatString()};
+ DecomposePrintfHandler H(S, FSL, FSL->getFormatString(), Type, 0, 0, IsObjC,
+ Str, Sema::FAPK_Elsewhere, PrintfArgs, 0,
+ InFunctionCall, VariadicCallType::DoesNotApply, BV,
+ UA, Args);
+
+ if (!analyze_format_string::ParsePrintfString(
+ H, Str, Str + Data.size(), S.getLangOpts(), S.Context.getTargetInfo(),
+ Type == FormatStringType::FreeBSDKPrintf))
+ H.DoneProcessing();
+ if (H.HadError)
+ return false;
+
+ llvm::stable_sort(Args, [](const EquatableFormatArgument &A,
+ const EquatableFormatArgument &B) {
+ return A.getPosition() < B.getPosition();
+ });
+ return true;
+}
+
+bool DecomposePrintfHandler::HandlePrintfSpecifier(
+ const analyze_printf::PrintfSpecifier &FS, const char *startSpecifier,
+ unsigned specifierLen, const TargetInfo &Target) {
+ if (!CheckPrintfHandler::HandlePrintfSpecifier(FS, startSpecifier,
+ specifierLen, Target)) {
+ HadError = true;
+ return false;
+ }
+
+ // Do not add any specifiers to the list for %%. This is possibly incorrect
+ // if using a precision/width with a data argument, but that combination is
+ // meaningless and we wouldn't know which format to attach the
+ // precision/width to.
+ const auto &CS = FS.getConversionSpecifier();
+ if (CS.getKind() == analyze_format_string::ConversionSpecifier::PercentArg)
+ return true;
+
+ // have to patch these to have the right ModifierFor if they are used
+ const unsigned Unset = ~0;
+ unsigned FieldWidthIndex = Unset;
+ unsigned PrecisionIndex = Unset;
+
+ // field width?
+ const auto &FieldWidth = FS.getFieldWidth();
+ if (!FieldWidth.isInvalid() && FieldWidth.hasDataArgument()) {
+ FieldWidthIndex = Specs.size();
+ Specs.emplace_back(getSpecifierRange(startSpecifier, specifierLen),
+ getLocationOfByte(FieldWidth.getStart()),
+ analyze_format_string::LengthModifier::None, "*",
+ FieldWidth.getArgType(S.Context),
+ EquatableFormatArgument::FAR_FieldWidth,
+ EquatableFormatArgument::SS_None,
+ FieldWidth.usesPositionalArg()
+ ? FieldWidth.getPositionalArgIndex() - 1
+ : FieldWidthIndex,
+ 0);
+ }
+ // precision?
+ const auto &Precision = FS.getPrecision();
+ if (!Precision.isInvalid() && Precision.hasDataArgument()) {
+ PrecisionIndex = Specs.size();
+ Specs.emplace_back(
+ getSpecifierRange(startSpecifier, specifierLen),
+ getLocationOfByte(Precision.getStart()),
+ analyze_format_string::LengthModifier::None, ".*",
+ Precision.getArgType(S.Context), EquatableFormatArgument::FAR_Precision,
+ EquatableFormatArgument::SS_None,
+ Precision.usesPositionalArg() ? Precision.getPositionalArgIndex() - 1
+ : PrecisionIndex,
+ 0);
+ }
+
+ // this specifier
+ unsigned SpecIndex =
+ FS.usesPositionalArg() ? FS.getPositionalArgIndex() - 1 : Specs.size();
+ if (FieldWidthIndex != Unset)
+ Specs[FieldWidthIndex].setModifierFor(SpecIndex);
+ if (PrecisionIndex != Unset)
+ Specs[PrecisionIndex].setModifierFor(SpecIndex);
+
+ EquatableFormatArgument::SpecifierSensitivity Sensitivity;
+ if (FS.isPrivate())
+ Sensitivity = EquatableFormatArgument::SS_Private;
+ else if (FS.isPublic())
+ Sensitivity = EquatableFormatArgument::SS_Public;
+ else if (FS.isSensitive())
+ Sensitivity = EquatableFormatArgument::SS_Sensitive;
+ else
+ Sensitivity = EquatableFormatArgument::SS_None;
+
+ Specs.emplace_back(
+ getSpecifierRange(startSpecifier, specifierLen),
+ getLocationOfByte(CS.getStart()), FS.getLengthModifier().getKind(),
+ CS.getCharacters(), FS.getArgType(S.Context, isObjCContext()),
+ EquatableFormatArgument::FAR_Data, Sensitivity, SpecIndex, 0);
+
+ // auxiliary argument?
+ if (CS.getKind() == analyze_format_string::ConversionSpecifier::FreeBSDbArg ||
+ CS.getKind() == analyze_format_string::ConversionSpecifier::FreeBSDDArg) {
+ Specs.emplace_back(getSpecifierRange(startSpecifier, specifierLen),
+ getLocationOfByte(CS.getStart()),
+ analyze_format_string::LengthModifier::None,
+ CS.getCharacters(),
+ analyze_format_string::ArgType::CStrTy,
+ EquatableFormatArgument::FAR_Auxiliary, Sensitivity,
+ SpecIndex + 1, SpecIndex);
+ }
+ return true;
+}
+
+// Determines if the specified is a C++ class or struct containing
+// a member with the specified name and kind (e.g. a CXXMethodDecl named
+// "c_str()").
+template<typename MemberKind>
+static llvm::SmallPtrSet<MemberKind*, 1>
+CXXRecordMembersNamed(StringRef Name, Sema &S, QualType Ty) {
+ auto *RD = Ty->getAsCXXRecordDecl();
+ llvm::SmallPtrSet<MemberKind*, 1> Results;
+
+ if (!RD || !(RD->isBeingDefined() || RD->isCompleteDefinition()))
+ return Results;
+
+ LookupResult R(S, &S.Context.Idents.get(Name), SourceLocation(),
+ Sema::LookupMemberName);
+ R.suppressDiagnostics();
+
+ // We just need to include all members of the right kind turned up by the
+ // filter, at this point.
+ if (S.LookupQualifiedName(R, RD))
+ for (LookupResult::iterator I = R.begin(), E = R.end(); I != E; ++I) {
+ NamedDecl *decl = (*I)->getUnderlyingDecl();
+ if (MemberKind *FK = dyn_cast<MemberKind>(decl))
+ Results.insert(FK);
+ }
+ return Results;
+}
+
+/// Check if we could call '.c_str()' on an object.
+///
+/// FIXME: This returns the wrong results in some cases (if cv-qualifiers don't
+/// allow the call, or if it would be ambiguous).
+bool Sema::hasCStrMethod(const Expr *E) {
+ using MethodSet = llvm::SmallPtrSet<CXXMethodDecl *, 1>;
+
+ MethodSet Results =
+ CXXRecordMembersNamed<CXXMethodDecl>("c_str", *this, E->getType());
+ for (MethodSet::iterator MI = Results.begin(), ME = Results.end();
+ MI != ME; ++MI)
+ if ((*MI)->getMinRequiredArguments() == 0)
+ return true;
+ return false;
+}
+
+// Check if a (w)string was passed when a (w)char* was needed, and offer a
+// better diagnostic if so. AT is assumed to be valid.
+// Returns true when a c_str() conversion method is found.
+bool CheckPrintfHandler::checkForCStrMembers(
+ const analyze_printf::ArgType &AT, const Expr *E) {
+ using MethodSet = llvm::SmallPtrSet<CXXMethodDecl *, 1>;
+
+ MethodSet Results =
+ CXXRecordMembersNamed<CXXMethodDecl>("c_str", S, E->getType());
+
+ for (MethodSet::iterator MI = Results.begin(), ME = Results.end();
+ MI != ME; ++MI) {
+ const CXXMethodDecl *Method = *MI;
+ if (Method->getMinRequiredArguments() == 0 &&
+ AT.matchesType(S.Context, Method->getReturnType())) {
+ // FIXME: Suggest parens if the expression needs them.
+ SourceLocation EndLoc = S.getLocForEndOfToken(E->getEndLoc());
+ S.Diag(E->getBeginLoc(), diag::note_printf_c_str)
+ << "c_str()" << FixItHint::CreateInsertion(EndLoc, ".c_str()");
+ return true;
+ }
+ }
+
+ return false;
+}
+
+bool CheckPrintfHandler::HandlePrintfSpecifier(
+ const analyze_printf::PrintfSpecifier &FS, const char *startSpecifier,
+ unsigned specifierLen, const TargetInfo &Target) {
+ using namespace analyze_format_string;
+ using namespace analyze_printf;
+
+ const PrintfConversionSpecifier &CS = FS.getConversionSpecifier();
+
+ if (FS.consumesDataArgument()) {
+ if (atFirstArg) {
+ atFirstArg = false;
+ usesPositionalArgs = FS.usesPositionalArg();
+ }
+ else if (usesPositionalArgs != FS.usesPositionalArg()) {
+ HandlePositionalNonpositionalArgs(getLocationOfByte(CS.getStart()),
+ startSpecifier, specifierLen);
+ return false;
+ }
+ }
+
+ // First check if the field width, precision, and conversion specifier
+ // have matching data arguments.
+ if (!HandleAmount(FS.getFieldWidth(), /* field width */ 0,
+ startSpecifier, specifierLen)) {
+ return false;
+ }
+
+ if (!HandleAmount(FS.getPrecision(), /* precision */ 1,
+ startSpecifier, specifierLen)) {
+ return false;
+ }
+
+ if (!CS.consumesDataArgument()) {
+ // FIXME: Technically specifying a precision or field width here
+ // makes no sense. Worth issuing a warning at some point.
+ return true;
+ }
+
+ // Consume the argument.
+ unsigned argIndex = FS.getArgIndex();
+ if (argIndex < NumDataArgs) {
+ // The check to see if the argIndex is valid will come later.
+ // We set the bit here because we may exit early from this
+ // function if we encounter some other error.
+ CoveredArgs.set(argIndex);
+ }
+
+ // FreeBSD kernel extensions.
+ if (CS.getKind() == ConversionSpecifier::FreeBSDbArg ||
+ CS.getKind() == ConversionSpecifier::FreeBSDDArg) {
+ // We need at least two arguments.
+ if (!CheckNumArgs(FS, CS, startSpecifier, specifierLen, argIndex + 1))
+ return false;
+
+ if (HasFormatArguments()) {
+ // Claim the second argument.
+ CoveredArgs.set(argIndex + 1);
+
+ // Type check the first argument (int for %b, pointer for %D)
+ const Expr *Ex = getDataArg(argIndex);
+ const analyze_printf::ArgType &AT =
+ (CS.getKind() == ConversionSpecifier::FreeBSDbArg)
+ ? ArgType(S.Context.IntTy)
+ : ArgType::CPointerTy;
+ if (AT.isValid() && !AT.matchesType(S.Context, Ex->getType()))
+ EmitFormatDiagnostic(
+ S.PDiag(diag::warn_format_conversion_argument_type_mismatch)
+ << AT.getRepresentativeTypeName(S.Context) << Ex->getType()
+ << false << Ex->getSourceRange(),
+ Ex->getBeginLoc(), /*IsStringLocation*/ false,
+ getSpecifierRange(startSpecifier, specifierLen));
+
+ // Type check the second argument (char * for both %b and %D)
+ Ex = getDataArg(argIndex + 1);
+ const analyze_printf::ArgType &AT2 = ArgType::CStrTy;
+ if (AT2.isValid() && !AT2.matchesType(S.Context, Ex->getType()))
+ EmitFormatDiagnostic(
+ S.PDiag(diag::warn_format_conversion_argument_type_mismatch)
+ << AT2.getRepresentativeTypeName(S.Context) << Ex->getType()
+ << false << Ex->getSourceRange(),
+ Ex->getBeginLoc(), /*IsStringLocation*/ false,
+ getSpecifierRange(startSpecifier, specifierLen));
+ }
+ return true;
+ }
+
+ // Check for using an Objective-C specific conversion specifier
+ // in a non-ObjC literal.
+ if (!allowsObjCArg() && CS.isObjCArg()) {
+ return HandleInvalidPrintfConversionSpecifier(FS, startSpecifier,
+ specifierLen);
+ }
+
+ // %P can only be used with os_log.
+ if (FSType != FormatStringType::OSLog &&
+ CS.getKind() == ConversionSpecifier::PArg) {
+ return HandleInvalidPrintfConversionSpecifier(FS, startSpecifier,
+ specifierLen);
+ }
+
+ // %n is not allowed with os_log.
+ if (FSType == FormatStringType::OSLog &&
+ CS.getKind() == ConversionSpecifier::nArg) {
+ EmitFormatDiagnostic(S.PDiag(diag::warn_os_log_format_narg),
+ getLocationOfByte(CS.getStart()),
+ /*IsStringLocation*/ false,
+ getSpecifierRange(startSpecifier, specifierLen));
+
+ return true;
+ }
+
+ // Only scalars are allowed for os_trace.
+ if (FSType == FormatStringType::OSTrace &&
+ (CS.getKind() == ConversionSpecifier::PArg ||
+ CS.getKind() == ConversionSpecifier::sArg ||
+ CS.getKind() == ConversionSpecifier::ObjCObjArg)) {
+ return HandleInvalidPrintfConversionSpecifier(FS, startSpecifier,
+ specifierLen);
+ }
+
+ // Check for use of public/private annotation outside of os_log().
+ if (FSType != FormatStringType::OSLog) {
+ if (FS.isPublic().isSet()) {
+ EmitFormatDiagnostic(S.PDiag(diag::warn_format_invalid_annotation)
+ << "public",
+ getLocationOfByte(FS.isPublic().getPosition()),
+ /*IsStringLocation*/ false,
+ getSpecifierRange(startSpecifier, specifierLen));
+ }
+ if (FS.isPrivate().isSet()) {
+ EmitFormatDiagnostic(S.PDiag(diag::warn_format_invalid_annotation)
+ << "private",
+ getLocationOfByte(FS.isPrivate().getPosition()),
+ /*IsStringLocation*/ false,
+ getSpecifierRange(startSpecifier, specifierLen));
+ }
+ }
+
+ const llvm::Triple &Triple = Target.getTriple();
+ if (CS.getKind() == ConversionSpecifier::nArg &&
+ (Triple.isAndroid() || Triple.isOSFuchsia())) {
+ EmitFormatDiagnostic(S.PDiag(diag::warn_printf_narg_not_supported),
+ getLocationOfByte(CS.getStart()),
+ /*IsStringLocation*/ false,
+ getSpecifierRange(startSpecifier, specifierLen));
+ }
+
+ // Check for invalid use of field width
+ if (!FS.hasValidFieldWidth()) {
+ HandleInvalidAmount(FS, FS.getFieldWidth(), /* field width */ 0,
+ startSpecifier, specifierLen);
+ }
+
+ // Check for invalid use of precision
+ if (!FS.hasValidPrecision()) {
+ HandleInvalidAmount(FS, FS.getPrecision(), /* precision */ 1,
+ startSpecifier, specifierLen);
+ }
+
+ // Precision is mandatory for %P specifier.
+ if (CS.getKind() == ConversionSpecifier::PArg &&
+ FS.getPrecision().getHowSpecified() == OptionalAmount::NotSpecified) {
+ EmitFormatDiagnostic(S.PDiag(diag::warn_format_P_no_precision),
+ getLocationOfByte(startSpecifier),
+ /*IsStringLocation*/ false,
+ getSpecifierRange(startSpecifier, specifierLen));
+ }
+
+ // Check each flag does not conflict with any other component.
+ if (!FS.hasValidThousandsGroupingPrefix())
+ HandleFlag(FS, FS.hasThousandsGrouping(), startSpecifier, specifierLen);
+ if (!FS.hasValidLeadingZeros())
+ HandleFlag(FS, FS.hasLeadingZeros(), startSpecifier, specifierLen);
+ if (!FS.hasValidPlusPrefix())
+ HandleFlag(FS, FS.hasPlusPrefix(), startSpecifier, specifierLen);
+ if (!FS.hasValidSpacePrefix())
+ HandleFlag(FS, FS.hasSpacePrefix(), startSpecifier, specifierLen);
+ if (!FS.hasValidAlternativeForm())
+ HandleFlag(FS, FS.hasAlternativeForm(), startSpecifier, specifierLen);
+ if (!FS.hasValidLeftJustified())
+ HandleFlag(FS, FS.isLeftJustified(), startSpecifier, specifierLen);
+
+ // Check that flags are not ignored by another flag
+ if (FS.hasSpacePrefix() && FS.hasPlusPrefix()) // ' ' ignored by '+'
+ HandleIgnoredFlag(FS, FS.hasSpacePrefix(), FS.hasPlusPrefix(),
+ startSpecifier, specifierLen);
+ if (FS.hasLeadingZeros() && FS.isLeftJustified()) // '0' ignored by '-'
+ HandleIgnoredFlag(FS, FS.hasLeadingZeros(), FS.isLeftJustified(),
+ startSpecifier, specifierLen);
+
+ // Check the length modifier is valid with the given conversion specifier.
+ if (!FS.hasValidLengthModifier(S.getASTContext().getTargetInfo(),
+ S.getLangOpts()))
+ HandleInvalidLengthModifier(FS, CS, startSpecifier, specifierLen,
+ diag::warn_format_nonsensical_length);
+ else if (!FS.hasStandardLengthModifier())
+ HandleNonStandardLengthModifier(FS, startSpecifier, specifierLen);
+ else if (!FS.hasStandardLengthConversionCombination())
+ HandleInvalidLengthModifier(FS, CS, startSpecifier, specifierLen,
+ diag::warn_format_non_standard_conversion_spec);
+
+ if (!FS.hasStandardConversionSpecifier(S.getLangOpts()))
+ HandleNonStandardConversionSpecifier(CS, startSpecifier, specifierLen);
+
+ // The remaining checks depend on the data arguments.
+ if (!HasFormatArguments())
+ return true;
+
+ if (!CheckNumArgs(FS, CS, startSpecifier, specifierLen, argIndex))
+ return false;
+
+ const Expr *Arg = getDataArg(argIndex);
+ if (!Arg)
+ return true;
+
+ return checkFormatExpr(FS, startSpecifier, specifierLen, Arg);
+}
+
+static bool requiresParensToAddCast(const Expr *E) {
+ // FIXME: We should have a general way to reason about operator
+ // precedence and whether parens are actually needed here.
+ // Take care of a few common cases where they aren't.
+ const Expr *Inside = E->IgnoreImpCasts();
+ if (const PseudoObjectExpr *POE = dyn_cast<PseudoObjectExpr>(Inside))
+ Inside = POE->getSyntacticForm()->IgnoreImpCasts();
+
+ switch (Inside->getStmtClass()) {
+ case Stmt::ArraySubscriptExprClass:
+ case Stmt::CallExprClass:
+ case Stmt::CharacterLiteralClass:
+ case Stmt::CXXBoolLiteralExprClass:
+ case Stmt::DeclRefExprClass:
+ case Stmt::FloatingLiteralClass:
+ case Stmt::IntegerLiteralClass:
+ case Stmt::MemberExprClass:
+ case Stmt::ObjCArrayLiteralClass:
+ case Stmt::ObjCBoolLiteralExprClass:
+ case Stmt::ObjCBoxedExprClass:
+ case Stmt::ObjCDictionaryLiteralClass:
+ case Stmt::ObjCEncodeExprClass:
+ case Stmt::ObjCIvarRefExprClass:
+ case Stmt::ObjCMessageExprClass:
+ case Stmt::ObjCPropertyRefExprClass:
+ case Stmt::ObjCStringLiteralClass:
+ case Stmt::ObjCSubscriptRefExprClass:
+ case Stmt::ParenExprClass:
+ case Stmt::StringLiteralClass:
+ case Stmt::UnaryOperatorClass:
+ return false;
+ default:
+ return true;
+ }
+}
+
+static std::pair<QualType, StringRef>
+shouldNotPrintDirectly(const ASTContext &Context,
+ QualType IntendedTy,
+ const Expr *E) {
+ // Use a 'while' to peel off layers of typedefs.
+ QualType TyTy = IntendedTy;
+ while (const TypedefType *UserTy = TyTy->getAs<TypedefType>()) {
+ StringRef Name = UserTy->getDecl()->getName();
+ QualType CastTy = llvm::StringSwitch<QualType>(Name)
+ .Case("CFIndex", Context.getNSIntegerType())
+ .Case("NSInteger", Context.getNSIntegerType())
+ .Case("NSUInteger", Context.getNSUIntegerType())
+ .Case("SInt32", Context.IntTy)
+ .Case("UInt32", Context.UnsignedIntTy)
+ .Default(QualType());
+
+ if (!CastTy.isNull())
+ return std::make_pair(CastTy, Name);
+
+ TyTy = UserTy->desugar();
+ }
+
+ // Strip parens if necessary.
+ if (const ParenExpr *PE = dyn_cast<ParenExpr>(E))
+ return shouldNotPrintDirectly(Context,
+ PE->getSubExpr()->getType(),
+ PE->getSubExpr());
+
+ // If this is a conditional expression, then its result type is constructed
+ // via usual arithmetic conversions and thus there might be no necessary
+ // typedef sugar there. Recurse to operands to check for NSInteger &
+ // Co. usage condition.
+ if (const ConditionalOperator *CO = dyn_cast<ConditionalOperator>(E)) {
+ QualType TrueTy, FalseTy;
+ StringRef TrueName, FalseName;
+
+ std::tie(TrueTy, TrueName) =
+ shouldNotPrintDirectly(Context,
+ CO->getTrueExpr()->getType(),
+ CO->getTrueExpr());
+ std::tie(FalseTy, FalseName) =
+ shouldNotPrintDirectly(Context,
+ CO->getFalseExpr()->getType(),
+ CO->getFalseExpr());
+
+ if (TrueTy == FalseTy)
+ return std::make_pair(TrueTy, TrueName);
+ else if (TrueTy.isNull())
+ return std::make_pair(FalseTy, FalseName);
+ else if (FalseTy.isNull())
+ return std::make_pair(TrueTy, TrueName);
+ }
+
+ return std::make_pair(QualType(), StringRef());
+}
+
+/// Return true if \p ICE is an implicit argument promotion of an arithmetic
+/// type. Bit-field 'promotions' from a higher ranked type to a lower ranked
+/// type do not count.
+static bool
+isArithmeticArgumentPromotion(Sema &S, const ImplicitCastExpr *ICE) {
+ QualType From = ICE->getSubExpr()->getType();
+ QualType To = ICE->getType();
+ // It's an integer promotion if the destination type is the promoted
+ // source type.
+ if (ICE->getCastKind() == CK_IntegralCast &&
+ S.Context.isPromotableIntegerType(From) &&
+ S.Context.getPromotedIntegerType(From) == To)
+ return true;
+ // Look through vector types, since we do default argument promotion for
+ // those in OpenCL.
+ if (const auto *VecTy = From->getAs<ExtVectorType>())
+ From = VecTy->getElementType();
+ if (const auto *VecTy = To->getAs<ExtVectorType>())
+ To = VecTy->getElementType();
+ // It's a floating promotion if the source type is a lower rank.
+ return ICE->getCastKind() == CK_FloatingCast &&
+ S.Context.getFloatingTypeOrder(From, To) < 0;
+}
+
+static analyze_format_string::ArgType::MatchKind
+handleFormatSignedness(analyze_format_string::ArgType::MatchKind Match,
+ DiagnosticsEngine &Diags, SourceLocation Loc) {
+ if (Match == analyze_format_string::ArgType::NoMatchSignedness) {
+ if (Diags.isIgnored(
+ diag::warn_format_conversion_argument_type_mismatch_signedness,
+ Loc) ||
+ Diags.isIgnored(
+ // Arbitrary -Wformat diagnostic to detect -Wno-format:
+ diag::warn_format_conversion_argument_type_mismatch, Loc)) {
+ return analyze_format_string::ArgType::Match;
+ }
+ }
+ return Match;
+}
+
+bool
+CheckPrintfHandler::checkFormatExpr(const analyze_printf::PrintfSpecifier &FS,
+ const char *StartSpecifier,
+ unsigned SpecifierLen,
+ const Expr *E) {
+ using namespace analyze_format_string;
+ using namespace analyze_printf;
+
+ // Now type check the data expression that matches the
+ // format specifier.
+ const analyze_printf::ArgType &AT = FS.getArgType(S.Context, isObjCContext());
+ if (!AT.isValid())
+ return true;
+
+ QualType ExprTy = E->getType();
+ while (const TypeOfExprType *TET = dyn_cast<TypeOfExprType>(ExprTy)) {
+ ExprTy = TET->getUnderlyingExpr()->getType();
+ }
+
+ // When using the format attribute in C++, you can receive a function or an
+ // array that will necessarily decay to a pointer when passed to the final
+ // format consumer. Apply decay before type comparison.
+ if (ExprTy->canDecayToPointerType())
+ ExprTy = S.Context.getDecayedType(ExprTy);
+
+ // Diagnose attempts to print a boolean value as a character. Unlike other
+ // -Wformat diagnostics, this is fine from a type perspective, but it still
+ // doesn't make sense.
+ if (FS.getConversionSpecifier().getKind() == ConversionSpecifier::cArg &&
+ E->isKnownToHaveBooleanValue()) {
+ const CharSourceRange &CSR =
+ getSpecifierRange(StartSpecifier, SpecifierLen);
+ SmallString<4> FSString;
+ llvm::raw_svector_ostream os(FSString);
+ FS.toString(os);
+ EmitFormatDiagnostic(S.PDiag(diag::warn_format_bool_as_character)
+ << FSString,
+ E->getExprLoc(), false, CSR);
+ return true;
+ }
+
+ // Diagnose attempts to use '%P' with ObjC object types, which will result in
+ // dumping raw class data (like is-a pointer), not actual data.
+ if (FS.getConversionSpecifier().getKind() == ConversionSpecifier::PArg &&
+ ExprTy->isObjCObjectPointerType()) {
+ const CharSourceRange &CSR =
+ getSpecifierRange(StartSpecifier, SpecifierLen);
+ EmitFormatDiagnostic(S.PDiag(diag::warn_format_P_with_objc_pointer),
+ E->getExprLoc(), false, CSR);
+ return true;
+ }
+
+ ArgType::MatchKind ImplicitMatch = ArgType::NoMatch;
+ ArgType::MatchKind Match = AT.matchesType(S.Context, ExprTy);
+ ArgType::MatchKind OrigMatch = Match;
+
+ Match = handleFormatSignedness(Match, S.getDiagnostics(), E->getExprLoc());
+ if (Match == ArgType::Match)
+ return true;
+
+ // NoMatchPromotionTypeConfusion should be only returned in ImplictCastExpr
+ assert(Match != ArgType::NoMatchPromotionTypeConfusion);
+
+ // Look through argument promotions for our error message's reported type.
+ // This includes the integral and floating promotions, but excludes array
+ // and function pointer decay (seeing that an argument intended to be a
+ // string has type 'char [6]' is probably more confusing than 'char *') and
+ // certain bitfield promotions (bitfields can be 'demoted' to a lesser type).
+ if (const ImplicitCastExpr *ICE = dyn_cast<ImplicitCastExpr>(E)) {
+ if (isArithmeticArgumentPromotion(S, ICE)) {
+ E = ICE->getSubExpr();
+ ExprTy = E->getType();
+
+ // Check if we didn't match because of an implicit cast from a 'char'
+ // or 'short' to an 'int'. This is done because printf is a varargs
+ // function.
+ if (ICE->getType() == S.Context.IntTy ||
+ ICE->getType() == S.Context.UnsignedIntTy) {
+ // All further checking is done on the subexpression
+ ImplicitMatch = AT.matchesType(S.Context, ExprTy);
+ if (OrigMatch == ArgType::NoMatchSignedness &&
+ ImplicitMatch != ArgType::NoMatchSignedness)
+ // If the original match was a signedness match this match on the
+ // implicit cast type also need to be signedness match otherwise we
+ // might introduce new unexpected warnings from -Wformat-signedness.
+ return true;
+ ImplicitMatch = handleFormatSignedness(
+ ImplicitMatch, S.getDiagnostics(), E->getExprLoc());
+ if (ImplicitMatch == ArgType::Match)
+ return true;
+ }
+ }
+ } else if (const CharacterLiteral *CL = dyn_cast<CharacterLiteral>(E)) {
+ // Special case for 'a', which has type 'int' in C.
+ // Note, however, that we do /not/ want to treat multibyte constants like
+ // 'MooV' as characters! This form is deprecated but still exists. In
+ // addition, don't treat expressions as of type 'char' if one byte length
+ // modifier is provided.
+ if (ExprTy == S.Context.IntTy &&
+ FS.getLengthModifier().getKind() != LengthModifier::AsChar)
+ if (llvm::isUIntN(S.Context.getCharWidth(), CL->getValue())) {
+ ExprTy = S.Context.CharTy;
+ // To improve check results, we consider a character literal in C
+ // to be a 'char' rather than an 'int'. 'printf("%hd", 'a');' is
+ // more likely a type confusion situation, so we will suggest to
+ // use '%hhd' instead by discarding the MatchPromotion.
+ if (Match == ArgType::MatchPromotion)
+ Match = ArgType::NoMatch;
+ }
+ }
+ if (Match == ArgType::MatchPromotion) {
+ // WG14 N2562 only clarified promotions in *printf
+ // For NSLog in ObjC, just preserve -Wformat behavior
+ if (!S.getLangOpts().ObjC &&
+ ImplicitMatch != ArgType::NoMatchPromotionTypeConfusion &&
+ ImplicitMatch != ArgType::NoMatchTypeConfusion)
+ return true;
+ Match = ArgType::NoMatch;
+ }
+ if (ImplicitMatch == ArgType::NoMatchPedantic ||
+ ImplicitMatch == ArgType::NoMatchTypeConfusion)
+ Match = ImplicitMatch;
+ assert(Match != ArgType::MatchPromotion);
+
+ // Look through unscoped enums to their underlying type.
+ bool IsEnum = false;
+ bool IsScopedEnum = false;
+ QualType IntendedTy = ExprTy;
+ if (const auto *ED = ExprTy->getAsEnumDecl()) {
+ IntendedTy = ED->getIntegerType();
+ if (!ED->isScoped()) {
+ ExprTy = IntendedTy;
+ // This controls whether we're talking about the underlying type or not,
+ // which we only want to do when it's an unscoped enum.
+ IsEnum = true;
+ } else {
+ IsScopedEnum = true;
+ }
+ }
+
+ // %C in an Objective-C context prints a unichar, not a wchar_t.
+ // If the argument is an integer of some kind, believe the %C and suggest
+ // a cast instead of changing the conversion specifier.
+ if (isObjCContext() &&
+ FS.getConversionSpecifier().getKind() == ConversionSpecifier::CArg) {
+ if (ExprTy->isIntegralOrUnscopedEnumerationType() &&
+ !ExprTy->isCharType()) {
+ // 'unichar' is defined as a typedef of unsigned short, but we should
+ // prefer using the typedef if it is visible.
+ IntendedTy = S.Context.UnsignedShortTy;
+
+ // While we are here, check if the value is an IntegerLiteral that happens
+ // to be within the valid range.
+ if (const IntegerLiteral *IL = dyn_cast<IntegerLiteral>(E)) {
+ const llvm::APInt &V = IL->getValue();
+ if (V.getActiveBits() <= S.Context.getTypeSize(IntendedTy))
+ return true;
+ }
+
+ LookupResult Result(S, &S.Context.Idents.get("unichar"), E->getBeginLoc(),
+ Sema::LookupOrdinaryName);
+ if (S.LookupName(Result, S.getCurScope())) {
+ NamedDecl *ND = Result.getFoundDecl();
+ if (TypedefNameDecl *TD = dyn_cast<TypedefNameDecl>(ND))
+ if (TD->getUnderlyingType() == IntendedTy)
+ IntendedTy =
+ S.Context.getTypedefType(ElaboratedTypeKeyword::None,
+ /*Qualifier=*/std::nullopt, TD);
+ }
+ }
+ }
+
+ // Special-case some of Darwin's platform-independence types by suggesting
+ // casts to primitive types that are known to be large enough.
+ bool ShouldNotPrintDirectly = false; StringRef CastTyName;
+ if (S.Context.getTargetInfo().getTriple().isOSDarwin()) {
+ QualType CastTy;
+ std::tie(CastTy, CastTyName) = shouldNotPrintDirectly(S.Context, IntendedTy, E);
+ if (!CastTy.isNull()) {
+ // %zi/%zu and %td/%tu are OK to use for NSInteger/NSUInteger of type int
+ // (long in ASTContext). Only complain to pedants or when they're the
+ // underlying type of a scoped enum (which always needs a cast).
+ if (!IsScopedEnum &&
+ (CastTyName == "NSInteger" || CastTyName == "NSUInteger") &&
+ (AT.isSizeT() || AT.isPtrdiffT()) &&
+ AT.matchesType(S.Context, CastTy))
+ Match = ArgType::NoMatchPedantic;
+ IntendedTy = CastTy;
+ ShouldNotPrintDirectly = true;
+ }
+ }
+
+ // We may be able to offer a FixItHint if it is a supported type.
+ PrintfSpecifier fixedFS = FS;
+ bool Success =
+ fixedFS.fixType(IntendedTy, S.getLangOpts(), S.Context, isObjCContext());
+
+ if (Success) {
+ // Get the fix string from the fixed format specifier
+ SmallString<16> buf;
+ llvm::raw_svector_ostream os(buf);
+ fixedFS.toString(os);
+
+ CharSourceRange SpecRange = getSpecifierRange(StartSpecifier, SpecifierLen);
+
+ if (IntendedTy == ExprTy && !ShouldNotPrintDirectly && !IsScopedEnum) {
+ unsigned Diag;
+ switch (Match) {
+ case ArgType::Match:
+ case ArgType::MatchPromotion:
+ case ArgType::NoMatchPromotionTypeConfusion:
+ llvm_unreachable("expected non-matching");
+ case ArgType::NoMatchSignedness:
+ Diag = diag::warn_format_conversion_argument_type_mismatch_signedness;
+ break;
+ case ArgType::NoMatchPedantic:
+ Diag = diag::warn_format_conversion_argument_type_mismatch_pedantic;
+ break;
+ case ArgType::NoMatchTypeConfusion:
+ Diag = diag::warn_format_conversion_argument_type_mismatch_confusion;
+ break;
+ case ArgType::NoMatch:
+ Diag = diag::warn_format_conversion_argument_type_mismatch;
+ break;
+ }
+
+ // In this case, the specifier is wrong and should be changed to match
+ // the argument.
+ EmitFormatDiagnostic(S.PDiag(Diag)
+ << AT.getRepresentativeTypeName(S.Context)
+ << IntendedTy << IsEnum << E->getSourceRange(),
+ E->getBeginLoc(),
+ /*IsStringLocation*/ false, SpecRange,
+ FixItHint::CreateReplacement(SpecRange, os.str()));
+ } else {
+ // The canonical type for formatting this value is different from the
+ // actual type of the expression. (This occurs, for example, with Darwin's
+ // NSInteger on 32-bit platforms, where it is typedef'd as 'int', but
+ // should be printed as 'long' for 64-bit compatibility.)
+ // Rather than emitting a normal format/argument mismatch, we want to
+ // add a cast to the recommended type (and correct the format string
+ // if necessary). We should also do so for scoped enumerations.
+ SmallString<16> CastBuf;
+ llvm::raw_svector_ostream CastFix(CastBuf);
+ CastFix << (S.LangOpts.CPlusPlus ? "static_cast<" : "(");
+ IntendedTy.print(CastFix, S.Context.getPrintingPolicy());
+ CastFix << (S.LangOpts.CPlusPlus ? ">" : ")");
+
+ SmallVector<FixItHint,4> Hints;
+ ArgType::MatchKind IntendedMatch = AT.matchesType(S.Context, IntendedTy);
+ IntendedMatch = handleFormatSignedness(IntendedMatch, S.getDiagnostics(),
+ E->getExprLoc());
+ if ((IntendedMatch != ArgType::Match) || ShouldNotPrintDirectly)
+ Hints.push_back(FixItHint::CreateReplacement(SpecRange, os.str()));
+
+ if (const CStyleCastExpr *CCast = dyn_cast<CStyleCastExpr>(E)) {
+ // If there's already a cast present, just replace it.
+ SourceRange CastRange(CCast->getLParenLoc(), CCast->getRParenLoc());
+ Hints.push_back(FixItHint::CreateReplacement(CastRange, CastFix.str()));
+
+ } else if (!requiresParensToAddCast(E) && !S.LangOpts.CPlusPlus) {
+ // If the expression has high enough precedence,
+ // just write the C-style cast.
+ Hints.push_back(
+ FixItHint::CreateInsertion(E->getBeginLoc(), CastFix.str()));
+ } else {
+ // Otherwise, add parens around the expression as well as the cast.
+ CastFix << "(";
+ Hints.push_back(
+ FixItHint::CreateInsertion(E->getBeginLoc(), CastFix.str()));
+
+ // We don't use getLocForEndOfToken because it returns invalid source
+ // locations for macro expansions (by design).
+ SourceLocation EndLoc = S.SourceMgr.getSpellingLoc(E->getEndLoc());
+ SourceLocation After = EndLoc.getLocWithOffset(
+ Lexer::MeasureTokenLength(EndLoc, S.SourceMgr, S.LangOpts));
+ Hints.push_back(FixItHint::CreateInsertion(After, ")"));
+ }
+
+ if (ShouldNotPrintDirectly && !IsScopedEnum) {
+ // The expression has a type that should not be printed directly.
+ // We extract the name from the typedef because we don't want to show
+ // the underlying type in the diagnostic.
+ StringRef Name;
+ if (const auto *TypedefTy = ExprTy->getAs<TypedefType>())
+ Name = TypedefTy->getDecl()->getName();
+ else
+ Name = CastTyName;
+ unsigned Diag = Match == ArgType::NoMatchPedantic
+ ? diag::warn_format_argument_needs_cast_pedantic
+ : diag::warn_format_argument_needs_cast;
+ EmitFormatDiagnostic(S.PDiag(Diag) << Name << IntendedTy << IsEnum
+ << E->getSourceRange(),
+ E->getBeginLoc(), /*IsStringLocation=*/false,
+ SpecRange, Hints);
+ } else {
+ // In this case, the expression could be printed using a different
+ // specifier, but we've decided that the specifier is probably correct
+ // and we should cast instead. Just use the normal warning message.
+
+ unsigned Diag =
+ IsScopedEnum
+ ? diag::warn_format_conversion_argument_type_mismatch_pedantic
+ : diag::warn_format_conversion_argument_type_mismatch;
+
+ EmitFormatDiagnostic(
+ S.PDiag(Diag) << AT.getRepresentativeTypeName(S.Context) << ExprTy
+ << IsEnum << E->getSourceRange(),
+ E->getBeginLoc(), /*IsStringLocation*/ false, SpecRange, Hints);
+ }
+ }
+ } else {
+ const CharSourceRange &CSR = getSpecifierRange(StartSpecifier,
+ SpecifierLen);
+ // Since the warning for passing non-POD types to variadic functions
+ // was deferred until now, we emit a warning for non-POD
+ // arguments here.
+ bool EmitTypeMismatch = false;
+ switch (S.isValidVarArgType(ExprTy)) {
+ case VarArgKind::Valid:
+ case VarArgKind::ValidInCXX11: {
+ unsigned Diag;
+ switch (Match) {
+ case ArgType::Match:
+ case ArgType::MatchPromotion:
+ case ArgType::NoMatchPromotionTypeConfusion:
+ llvm_unreachable("expected non-matching");
+ case ArgType::NoMatchSignedness:
+ Diag = diag::warn_format_conversion_argument_type_mismatch_signedness;
+ break;
+ case ArgType::NoMatchPedantic:
+ Diag = diag::warn_format_conversion_argument_type_mismatch_pedantic;
+ break;
+ case ArgType::NoMatchTypeConfusion:
+ Diag = diag::warn_format_conversion_argument_type_mismatch_confusion;
+ break;
+ case ArgType::NoMatch:
+ Diag = isInvalidOSLogArgTypeForCodeGen(FSType, ExprTy)
+ ? diag::err_format_conversion_argument_type_mismatch
+ : diag::warn_format_conversion_argument_type_mismatch;
+ break;
+ }
+
+ EmitFormatDiagnostic(
+ S.PDiag(Diag) << AT.getRepresentativeTypeName(S.Context) << ExprTy
+ << IsEnum << CSR << E->getSourceRange(),
+ E->getBeginLoc(), /*IsStringLocation*/ false, CSR);
+ break;
+ }
+ case VarArgKind::Undefined:
+ case VarArgKind::MSVCUndefined:
+ if (CallType == VariadicCallType::DoesNotApply) {
+ EmitTypeMismatch = true;
+ } else {
+ EmitFormatDiagnostic(
+ S.PDiag(diag::warn_non_pod_vararg_with_format_string)
+ << S.getLangOpts().CPlusPlus11 << ExprTy << CallType
+ << AT.getRepresentativeTypeName(S.Context) << CSR
+ << E->getSourceRange(),
+ E->getBeginLoc(), /*IsStringLocation*/ false, CSR);
+ checkForCStrMembers(AT, E);
+ }
+ break;
+
+ case VarArgKind::Invalid:
+ if (CallType == VariadicCallType::DoesNotApply)
+ EmitTypeMismatch = true;
+ else if (ExprTy->isObjCObjectType())
+ EmitFormatDiagnostic(
+ S.PDiag(diag::err_cannot_pass_objc_interface_to_vararg_format)
+ << S.getLangOpts().CPlusPlus11 << ExprTy << CallType
+ << AT.getRepresentativeTypeName(S.Context) << CSR
+ << E->getSourceRange(),
+ E->getBeginLoc(), /*IsStringLocation*/ false, CSR);
+ else
+ // FIXME: If this is an initializer list, suggest removing the braces
+ // or inserting a cast to the target type.
+ S.Diag(E->getBeginLoc(), diag::err_cannot_pass_to_vararg_format)
+ << isa<InitListExpr>(E) << ExprTy << CallType
+ << AT.getRepresentativeTypeName(S.Context) << E->getSourceRange();
+ break;
+ }
+
+ if (EmitTypeMismatch) {
+ // The function is not variadic, so we do not generate warnings about
+ // being allowed to pass that object as a variadic argument. Instead,
+ // since there are inherently no printf specifiers for types which cannot
+ // be passed as variadic arguments, emit a plain old specifier mismatch
+ // argument.
+ EmitFormatDiagnostic(
+ S.PDiag(diag::warn_format_conversion_argument_type_mismatch)
+ << AT.getRepresentativeTypeName(S.Context) << ExprTy << false
+ << E->getSourceRange(),
+ E->getBeginLoc(), false, CSR);
+ }
+
+ assert(FirstDataArg + FS.getArgIndex() < CheckedVarArgs.size() &&
+ "format string specifier index out of range");
+ CheckedVarArgs[FirstDataArg + FS.getArgIndex()] = true;
+ }
+
+ return true;
+}
+
+//===--- CHECK: Scanf format string checking ------------------------------===//
+
+namespace {
+
+class CheckScanfHandler : public CheckFormatHandler {
+public:
+ CheckScanfHandler(Sema &s, const FormatStringLiteral *fexpr,
+ const Expr *origFormatExpr, FormatStringType type,
+ unsigned firstDataArg, unsigned numDataArgs,
+ const char *beg, Sema::FormatArgumentPassingKind APK,
+ ArrayRef<const Expr *> Args, unsigned formatIdx,
+ bool inFunctionCall, VariadicCallType CallType,
+ llvm::SmallBitVector &CheckedVarArgs,
+ UncoveredArgHandler &UncoveredArg)
+ : CheckFormatHandler(s, fexpr, origFormatExpr, type, firstDataArg,
+ numDataArgs, beg, APK, Args, formatIdx,
+ inFunctionCall, CallType, CheckedVarArgs,
+ UncoveredArg) {}
+
+ bool HandleScanfSpecifier(const analyze_scanf::ScanfSpecifier &FS,
+ const char *startSpecifier,
+ unsigned specifierLen) override;
+
+ bool HandleInvalidScanfConversionSpecifier(
+ const analyze_scanf::ScanfSpecifier &FS,
+ const char *startSpecifier,
+ unsigned specifierLen) override;
+
+ void HandleIncompleteScanList(const char *start, const char *end) override;
+};
+
+} // namespace
+
+void CheckScanfHandler::HandleIncompleteScanList(const char *start,
+ const char *end) {
+ EmitFormatDiagnostic(S.PDiag(diag::warn_scanf_scanlist_incomplete),
+ getLocationOfByte(end), /*IsStringLocation*/true,
+ getSpecifierRange(start, end - start));
+}
+
+bool CheckScanfHandler::HandleInvalidScanfConversionSpecifier(
+ const analyze_scanf::ScanfSpecifier &FS,
+ const char *startSpecifier,
+ unsigned specifierLen) {
+ const analyze_scanf::ScanfConversionSpecifier &CS =
+ FS.getConversionSpecifier();
+
+ return HandleInvalidConversionSpecifier(FS.getArgIndex(),
+ getLocationOfByte(CS.getStart()),
+ startSpecifier, specifierLen,
+ CS.getStart(), CS.getLength());
+}
+
+bool CheckScanfHandler::HandleScanfSpecifier(
+ const analyze_scanf::ScanfSpecifier &FS,
+ const char *startSpecifier,
+ unsigned specifierLen) {
+ using namespace analyze_scanf;
+ using namespace analyze_format_string;
+
+ const ScanfConversionSpecifier &CS = FS.getConversionSpecifier();
+
+ // Handle case where '%' and '*' don't consume an argument. These shouldn't
+ // be used to decide if we are using positional arguments consistently.
+ if (FS.consumesDataArgument()) {
+ if (atFirstArg) {
+ atFirstArg = false;
+ usesPositionalArgs = FS.usesPositionalArg();
+ }
+ else if (usesPositionalArgs != FS.usesPositionalArg()) {
+ HandlePositionalNonpositionalArgs(getLocationOfByte(CS.getStart()),
+ startSpecifier, specifierLen);
+ return false;
+ }
+ }
+
+ // Check if the field with is non-zero.
+ const OptionalAmount &Amt = FS.getFieldWidth();
+ if (Amt.getHowSpecified() == OptionalAmount::Constant) {
+ if (Amt.getConstantAmount() == 0) {
+ const CharSourceRange &R = getSpecifierRange(Amt.getStart(),
+ Amt.getConstantLength());
+ EmitFormatDiagnostic(S.PDiag(diag::warn_scanf_nonzero_width),
+ getLocationOfByte(Amt.getStart()),
+ /*IsStringLocation*/true, R,
+ FixItHint::CreateRemoval(R));
+ }
+ }
+
+ if (!FS.consumesDataArgument()) {
+ // FIXME: Technically specifying a precision or field width here
+ // makes no sense. Worth issuing a warning at some point.
+ return true;
+ }
+
+ // Consume the argument.
+ unsigned argIndex = FS.getArgIndex();
+ if (argIndex < NumDataArgs) {
+ // The check to see if the argIndex is valid will come later.
+ // We set the bit here because we may exit early from this
+ // function if we encounter some other error.
+ CoveredArgs.set(argIndex);
+ }
+
+ // Check the length modifier is valid with the given conversion specifier.
+ if (!FS.hasValidLengthModifier(S.getASTContext().getTargetInfo(),
+ S.getLangOpts()))
+ HandleInvalidLengthModifier(FS, CS, startSpecifier, specifierLen,
+ diag::warn_format_nonsensical_length);
+ else if (!FS.hasStandardLengthModifier())
+ HandleNonStandardLengthModifier(FS, startSpecifier, specifierLen);
+ else if (!FS.hasStandardLengthConversionCombination())
+ HandleInvalidLengthModifier(FS, CS, startSpecifier, specifierLen,
+ diag::warn_format_non_standard_conversion_spec);
+
+ if (!FS.hasStandardConversionSpecifier(S.getLangOpts()))
+ HandleNonStandardConversionSpecifier(CS, startSpecifier, specifierLen);
+
+ // The remaining checks depend on the data arguments.
+ if (!HasFormatArguments())
+ return true;
+
+ if (!CheckNumArgs(FS, CS, startSpecifier, specifierLen, argIndex))
+ return false;
+
+ // Check that the argument type matches the format specifier.
+ const Expr *Ex = getDataArg(argIndex);
+ if (!Ex)
+ return true;
+
+ const analyze_format_string::ArgType &AT = FS.getArgType(S.Context);
+
+ if (!AT.isValid()) {
+ return true;
+ }
+
+ analyze_format_string::ArgType::MatchKind Match =
+ AT.matchesType(S.Context, Ex->getType());
+ Match = handleFormatSignedness(Match, S.getDiagnostics(), Ex->getExprLoc());
+ if (Match == analyze_format_string::ArgType::Match)
+ return true;
+ bool Pedantic = Match == analyze_format_string::ArgType::NoMatchPedantic;
+ bool Signedness = Match == analyze_format_string::ArgType::NoMatchSignedness;
+
+ ScanfSpecifier fixedFS = FS;
+ bool Success = fixedFS.fixType(Ex->getType(), Ex->IgnoreImpCasts()->getType(),
+ S.getLangOpts(), S.Context);
+
+ unsigned Diag =
+ Pedantic ? diag::warn_format_conversion_argument_type_mismatch_pedantic
+ : Signedness
+ ? diag::warn_format_conversion_argument_type_mismatch_signedness
+ : diag::warn_format_conversion_argument_type_mismatch;
+
+ if (Success) {
+ // Get the fix string from the fixed format specifier.
+ SmallString<128> buf;
+ llvm::raw_svector_ostream os(buf);
+ fixedFS.toString(os);
+
+ EmitFormatDiagnostic(
+ S.PDiag(Diag) << AT.getRepresentativeTypeName(S.Context)
+ << Ex->getType() << false << Ex->getSourceRange(),
+ Ex->getBeginLoc(),
+ /*IsStringLocation*/ false,
+ getSpecifierRange(startSpecifier, specifierLen),
+ FixItHint::CreateReplacement(
+ getSpecifierRange(startSpecifier, specifierLen), os.str()));
+ } else {
+ EmitFormatDiagnostic(S.PDiag(Diag)
+ << AT.getRepresentativeTypeName(S.Context)
+ << Ex->getType() << false << Ex->getSourceRange(),
+ Ex->getBeginLoc(),
+ /*IsStringLocation*/ false,
+ getSpecifierRange(startSpecifier, specifierLen));
+ }
+
+ return true;
+}
+
+static bool CompareFormatSpecifiers(Sema &S, const StringLiteral *Ref,
+ ArrayRef<EquatableFormatArgument> RefArgs,
+ const StringLiteral *Fmt,
+ ArrayRef<EquatableFormatArgument> FmtArgs,
+ const Expr *FmtExpr, bool InFunctionCall) {
+ bool HadError = false;
+ auto FmtIter = FmtArgs.begin(), FmtEnd = FmtArgs.end();
+ auto RefIter = RefArgs.begin(), RefEnd = RefArgs.end();
+ while (FmtIter < FmtEnd && RefIter < RefEnd) {
+ // In positional-style format strings, the same specifier can appear
+ // multiple times (like %2$i %2$d). Specifiers in both RefArgs and FmtArgs
+ // are sorted by getPosition(), and we process each range of equal
+ // getPosition() values as one group.
+ // RefArgs are taken from a string literal that was given to
+ // attribute(format_matches), and if we got this far, we have already
+ // verified that if it has positional specifiers that appear in multiple
+ // locations, then they are all mutually compatible. What's left for us to
+ // do is verify that all specifiers with the same position in FmtArgs are
+ // compatible with the RefArgs specifiers. We check each specifier from
+ // FmtArgs against the first member of the RefArgs group.
+ for (; FmtIter < FmtEnd; ++FmtIter) {
+ // Clang does not diagnose missing format specifiers in positional-style
+ // strings (TODO: which it probably should do, as it is UB to skip over a
+ // format argument). Skip specifiers if needed.
+ if (FmtIter->getPosition() < RefIter->getPosition())
+ continue;
+
+ // Delimits a new getPosition() value.
+ if (FmtIter->getPosition() > RefIter->getPosition())
+ break;
+
+ HadError |=
+ !FmtIter->VerifyCompatible(S, *RefIter, FmtExpr, InFunctionCall);
+ }
+
+ // Jump RefIter to the start of the next group.
+ RefIter = std::find_if(RefIter + 1, RefEnd, [=](const auto &Arg) {
+ return Arg.getPosition() != RefIter->getPosition();
+ });
+ }
+
+ if (FmtIter < FmtEnd) {
+ CheckFormatHandler::EmitFormatDiagnostic(
+ S, InFunctionCall, FmtExpr,
+ S.PDiag(diag::warn_format_cmp_specifier_arity) << 1,
+ FmtExpr->getBeginLoc(), false, FmtIter->getSourceRange());
+ HadError = S.Diag(Ref->getBeginLoc(), diag::note_format_cmp_with) << 1;
+ } else if (RefIter < RefEnd) {
+ CheckFormatHandler::EmitFormatDiagnostic(
+ S, InFunctionCall, FmtExpr,
+ S.PDiag(diag::warn_format_cmp_specifier_arity) << 0,
+ FmtExpr->getBeginLoc(), false, Fmt->getSourceRange());
+ HadError = S.Diag(Ref->getBeginLoc(), diag::note_format_cmp_with)
+ << 1 << RefIter->getSourceRange();
+ }
+ return !HadError;
+}
+
+static void CheckFormatString(
+ Sema &S, const FormatStringLiteral *FExpr,
+ const StringLiteral *ReferenceFormatString, const Expr *OrigFormatExpr,
+ ArrayRef<const Expr *> Args, Sema::FormatArgumentPassingKind APK,
+ unsigned format_idx, unsigned firstDataArg, FormatStringType Type,
+ bool inFunctionCall, VariadicCallType CallType,
+ llvm::SmallBitVector &CheckedVarArgs, UncoveredArgHandler &UncoveredArg,
+ bool IgnoreStringsWithoutSpecifiers) {
+ // CHECK: is the format string a wide literal?
+ if (!FExpr->isAscii() && !FExpr->isUTF8()) {
+ CheckFormatHandler::EmitFormatDiagnostic(
+ S, inFunctionCall, Args[format_idx],
+ S.PDiag(diag::warn_format_string_is_wide_literal), FExpr->getBeginLoc(),
+ /*IsStringLocation*/ true, OrigFormatExpr->getSourceRange());
+ return;
+ }
+
+ // Str - The format string. NOTE: this is NOT null-terminated!
+ StringRef StrRef = FExpr->getString();
+ const char *Str = StrRef.data();
+ // Account for cases where the string literal is truncated in a declaration.
+ const ConstantArrayType *T =
+ S.Context.getAsConstantArrayType(FExpr->getType());
+ assert(T && "String literal not of constant array type!");
+ size_t TypeSize = T->getZExtSize();
+ size_t StrLen = std::min(std::max(TypeSize, size_t(1)) - 1, StrRef.size());
+ const unsigned numDataArgs = Args.size() - firstDataArg;
+
+ if (IgnoreStringsWithoutSpecifiers &&
+ !analyze_format_string::parseFormatStringHasFormattingSpecifiers(
+ Str, Str + StrLen, S.getLangOpts(), S.Context.getTargetInfo()))
+ return;
+
+ // Emit a warning if the string literal is truncated and does not contain an
+ // embedded null character.
+ if (TypeSize <= StrRef.size() && !StrRef.substr(0, TypeSize).contains('\0')) {
+ CheckFormatHandler::EmitFormatDiagnostic(
+ S, inFunctionCall, Args[format_idx],
+ S.PDiag(diag::warn_printf_format_string_not_null_terminated),
+ FExpr->getBeginLoc(),
+ /*IsStringLocation=*/true, OrigFormatExpr->getSourceRange());
+ return;
+ }
+
+ // CHECK: empty format string?
+ if (StrLen == 0 && numDataArgs > 0) {
+ CheckFormatHandler::EmitFormatDiagnostic(
+ S, inFunctionCall, Args[format_idx],
+ S.PDiag(diag::warn_empty_format_string), FExpr->getBeginLoc(),
+ /*IsStringLocation*/ true, OrigFormatExpr->getSourceRange());
+ return;
+ }
+
+ if (Type == FormatStringType::Printf || Type == FormatStringType::NSString ||
+ Type == FormatStringType::Kprintf ||
+ Type == FormatStringType::FreeBSDKPrintf ||
+ Type == FormatStringType::OSLog || Type == FormatStringType::OSTrace) {
+ bool IsObjC =
+ Type == FormatStringType::NSString || Type == FormatStringType::OSTrace;
+ if (ReferenceFormatString == nullptr) {
+ CheckPrintfHandler H(S, FExpr, OrigFormatExpr, Type, firstDataArg,
+ numDataArgs, IsObjC, Str, APK, Args, format_idx,
+ inFunctionCall, CallType, CheckedVarArgs,
+ UncoveredArg);
+
+ if (!analyze_format_string::ParsePrintfString(
+ H, Str, Str + StrLen, S.getLangOpts(), S.Context.getTargetInfo(),
+ Type == FormatStringType::Kprintf ||
+ Type == FormatStringType::FreeBSDKPrintf))
+ H.DoneProcessing();
+ } else {
+ S.CheckFormatStringsCompatible(
+ Type, ReferenceFormatString, FExpr->getFormatString(),
+ inFunctionCall ? nullptr : Args[format_idx]);
+ }
+ } else if (Type == FormatStringType::Scanf) {
+ CheckScanfHandler H(S, FExpr, OrigFormatExpr, Type, firstDataArg,
+ numDataArgs, Str, APK, Args, format_idx, inFunctionCall,
+ CallType, CheckedVarArgs, UncoveredArg);
+
+ if (!analyze_format_string::ParseScanfString(
+ H, Str, Str + StrLen, S.getLangOpts(), S.Context.getTargetInfo()))
+ H.DoneProcessing();
+ } // TODO: handle other formats
+}
+
+bool Sema::CheckFormatStringsCompatible(
+ FormatStringType Type, const StringLiteral *AuthoritativeFormatString,
+ const StringLiteral *TestedFormatString, const Expr *FunctionCallArg) {
+ if (Type != FormatStringType::Printf && Type != FormatStringType::NSString &&
+ Type != FormatStringType::Kprintf &&
+ Type != FormatStringType::FreeBSDKPrintf &&
+ Type != FormatStringType::OSLog && Type != FormatStringType::OSTrace)
+ return true;
+
+ bool IsObjC =
+ Type == FormatStringType::NSString || Type == FormatStringType::OSTrace;
+ llvm::SmallVector<EquatableFormatArgument, 9> RefArgs, FmtArgs;
+ FormatStringLiteral RefLit = AuthoritativeFormatString;
+ FormatStringLiteral TestLit = TestedFormatString;
+ const Expr *Arg;
+ bool DiagAtStringLiteral;
+ if (FunctionCallArg) {
+ Arg = FunctionCallArg;
+ DiagAtStringLiteral = false;
+ } else {
+ Arg = TestedFormatString;
+ DiagAtStringLiteral = true;
+ }
+ if (DecomposePrintfHandler::GetSpecifiers(*this, &RefLit,
+ AuthoritativeFormatString, Type,
+ IsObjC, true, RefArgs) &&
+ DecomposePrintfHandler::GetSpecifiers(*this, &TestLit, Arg, Type, IsObjC,
+ DiagAtStringLiteral, FmtArgs)) {
+ return CompareFormatSpecifiers(*this, AuthoritativeFormatString, RefArgs,
+ TestedFormatString, FmtArgs, Arg,
+ DiagAtStringLiteral);
+ }
+ return false;
+}
+
+bool Sema::ValidateFormatString(FormatStringType Type,
+ const StringLiteral *Str) {
+ if (Type != FormatStringType::Printf && Type != FormatStringType::NSString &&
+ Type != FormatStringType::Kprintf &&
+ Type != FormatStringType::FreeBSDKPrintf &&
+ Type != FormatStringType::OSLog && Type != FormatStringType::OSTrace)
+ return true;
+
+ FormatStringLiteral RefLit = Str;
+ llvm::SmallVector<EquatableFormatArgument, 9> Args;
+ bool IsObjC =
+ Type == FormatStringType::NSString || Type == FormatStringType::OSTrace;
+ if (!DecomposePrintfHandler::GetSpecifiers(*this, &RefLit, Str, Type, IsObjC,
+ true, Args))
+ return false;
+
+ // Group arguments by getPosition() value, and check that each member of the
+ // group is compatible with the first member. This verifies that when
+ // positional arguments are used multiple times (such as %2$i %2$d), all uses
+ // are mutually compatible. As an optimization, don't test the first member
+ // against itself.
+ bool HadError = false;
+ auto Iter = Args.begin();
+ auto End = Args.end();
+ while (Iter != End) {
+ const auto &FirstInGroup = *Iter;
+ for (++Iter;
+ Iter != End && Iter->getPosition() == FirstInGroup.getPosition();
+ ++Iter) {
+ HadError |= !Iter->VerifyCompatible(*this, FirstInGroup, Str, true);
+ }
+ }
+ return !HadError;
+}
+
+bool Sema::FormatStringHasSArg(const StringLiteral *FExpr) {
+ // Str - The format string. NOTE: this is NOT null-terminated!
+ StringRef StrRef = FExpr->getString();
+ const char *Str = StrRef.data();
+ // Account for cases where the string literal is truncated in a declaration.
+ const ConstantArrayType *T = Context.getAsConstantArrayType(FExpr->getType());
+ assert(T && "String literal not of constant array type!");
+ size_t TypeSize = T->getZExtSize();
+ size_t StrLen = std::min(std::max(TypeSize, size_t(1)) - 1, StrRef.size());
+ return analyze_format_string::ParseFormatStringHasSArg(Str, Str + StrLen,
+ getLangOpts(),
+ Context.getTargetInfo());
+}
+
+//===--- CHECK: Warn on use of wrong absolute value function. -------------===//
+
+// Returns the related absolute value function that is larger, of 0 if one
+// does not exist.
+static unsigned getLargerAbsoluteValueFunction(unsigned AbsFunction) {
+ switch (AbsFunction) {
+ default:
+ return 0;
+
+ case Builtin::BI__builtin_abs:
+ return Builtin::BI__builtin_labs;
+ case Builtin::BI__builtin_labs:
+ return Builtin::BI__builtin_llabs;
+ case Builtin::BI__builtin_llabs:
+ return 0;
+
+ case Builtin::BI__builtin_fabsf:
+ return Builtin::BI__builtin_fabs;
+ case Builtin::BI__builtin_fabs:
+ return Builtin::BI__builtin_fabsl;
+ case Builtin::BI__builtin_fabsl:
+ return 0;
+
+ case Builtin::BI__builtin_cabsf:
+ return Builtin::BI__builtin_cabs;
+ case Builtin::BI__builtin_cabs:
+ return Builtin::BI__builtin_cabsl;
+ case Builtin::BI__builtin_cabsl:
+ return 0;
+
+ case Builtin::BIabs:
+ return Builtin::BIlabs;
+ case Builtin::BIlabs:
+ return Builtin::BIllabs;
+ case Builtin::BIllabs:
+ return 0;
+
+ case Builtin::BIfabsf:
+ return Builtin::BIfabs;
+ case Builtin::BIfabs:
+ return Builtin::BIfabsl;
+ case Builtin::BIfabsl:
+ return 0;
+
+ case Builtin::BIcabsf:
+ return Builtin::BIcabs;
+ case Builtin::BIcabs:
+ return Builtin::BIcabsl;
+ case Builtin::BIcabsl:
+ return 0;
+ }
+}
+
+// Returns the argument type of the absolute value function.
+static QualType getAbsoluteValueArgumentType(ASTContext &Context,
+ unsigned AbsType) {
+ if (AbsType == 0)
+ return QualType();
+
+ ASTContext::GetBuiltinTypeError Error = ASTContext::GE_None;
+ QualType BuiltinType = Context.GetBuiltinType(AbsType, Error);
+ if (Error != ASTContext::GE_None)
+ return QualType();
+
+ const FunctionProtoType *FT = BuiltinType->getAs<FunctionProtoType>();
+ if (!FT)
+ return QualType();
+
+ if (FT->getNumParams() != 1)
+ return QualType();
+
+ return FT->getParamType(0);
+}
+
+// Returns the best absolute value function, or zero, based on type and
+// current absolute value function.
+static unsigned getBestAbsFunction(ASTContext &Context, QualType ArgType,
+ unsigned AbsFunctionKind) {
+ unsigned BestKind = 0;
+ uint64_t ArgSize = Context.getTypeSize(ArgType);
+ for (unsigned Kind = AbsFunctionKind; Kind != 0;
+ Kind = getLargerAbsoluteValueFunction(Kind)) {
+ QualType ParamType = getAbsoluteValueArgumentType(Context, Kind);
+ if (Context.getTypeSize(ParamType) >= ArgSize) {
+ if (BestKind == 0)
+ BestKind = Kind;
+ else if (Context.hasSameType(ParamType, ArgType)) {
+ BestKind = Kind;
+ break;
+ }
+ }
+ }
+ return BestKind;
+}
+
+enum AbsoluteValueKind {
+ AVK_Integer,
+ AVK_Floating,
+ AVK_Complex
+};
+
+static AbsoluteValueKind getAbsoluteValueKind(QualType T) {
+ if (T->isIntegralOrEnumerationType())
+ return AVK_Integer;
+ if (T->isRealFloatingType())
+ return AVK_Floating;
+ if (T->isAnyComplexType())
+ return AVK_Complex;
+
+ llvm_unreachable("Type not integer, floating, or complex");
+}
+
+// Changes the absolute value function to a different type. Preserves whether
+// the function is a builtin.
+static unsigned changeAbsFunction(unsigned AbsKind,
+ AbsoluteValueKind ValueKind) {
+ switch (ValueKind) {
+ case AVK_Integer:
+ switch (AbsKind) {
+ default:
+ return 0;
+ case Builtin::BI__builtin_fabsf:
+ case Builtin::BI__builtin_fabs:
+ case Builtin::BI__builtin_fabsl:
+ case Builtin::BI__builtin_cabsf:
+ case Builtin::BI__builtin_cabs:
+ case Builtin::BI__builtin_cabsl:
+ return Builtin::BI__builtin_abs;
+ case Builtin::BIfabsf:
+ case Builtin::BIfabs:
+ case Builtin::BIfabsl:
+ case Builtin::BIcabsf:
+ case Builtin::BIcabs:
+ case Builtin::BIcabsl:
+ return Builtin::BIabs;
+ }
+ case AVK_Floating:
+ switch (AbsKind) {
+ default:
+ return 0;
+ case Builtin::BI__builtin_abs:
+ case Builtin::BI__builtin_labs:
+ case Builtin::BI__builtin_llabs:
+ case Builtin::BI__builtin_cabsf:
+ case Builtin::BI__builtin_cabs:
+ case Builtin::BI__builtin_cabsl:
+ return Builtin::BI__builtin_fabsf;
+ case Builtin::BIabs:
+ case Builtin::BIlabs:
+ case Builtin::BIllabs:
+ case Builtin::BIcabsf:
+ case Builtin::BIcabs:
+ case Builtin::BIcabsl:
+ return Builtin::BIfabsf;
+ }
+ case AVK_Complex:
+ switch (AbsKind) {
+ default:
+ return 0;
+ case Builtin::BI__builtin_abs:
+ case Builtin::BI__builtin_labs:
+ case Builtin::BI__builtin_llabs:
+ case Builtin::BI__builtin_fabsf:
+ case Builtin::BI__builtin_fabs:
+ case Builtin::BI__builtin_fabsl:
+ return Builtin::BI__builtin_cabsf;
+ case Builtin::BIabs:
+ case Builtin::BIlabs:
+ case Builtin::BIllabs:
+ case Builtin::BIfabsf:
+ case Builtin::BIfabs:
+ case Builtin::BIfabsl:
+ return Builtin::BIcabsf;
+ }
+ }
+ llvm_unreachable("Unable to convert function");
+}
+
+static unsigned getAbsoluteValueFunctionKind(const FunctionDecl *FDecl) {
+ const IdentifierInfo *FnInfo = FDecl->getIdentifier();
+ if (!FnInfo)
+ return 0;
+
+ switch (FDecl->getBuiltinID()) {
+ default:
+ return 0;
+ case Builtin::BI__builtin_abs:
+ case Builtin::BI__builtin_fabs:
+ case Builtin::BI__builtin_fabsf:
+ case Builtin::BI__builtin_fabsl:
+ case Builtin::BI__builtin_labs:
+ case Builtin::BI__builtin_llabs:
+ case Builtin::BI__builtin_cabs:
+ case Builtin::BI__builtin_cabsf:
+ case Builtin::BI__builtin_cabsl:
+ case Builtin::BIabs:
+ case Builtin::BIlabs:
+ case Builtin::BIllabs:
+ case Builtin::BIfabs:
+ case Builtin::BIfabsf:
+ case Builtin::BIfabsl:
+ case Builtin::BIcabs:
+ case Builtin::BIcabsf:
+ case Builtin::BIcabsl:
+ return FDecl->getBuiltinID();
+ }
+ llvm_unreachable("Unknown Builtin type");
+}
+
+// If the replacement is valid, emit a note with replacement function.
+// Additionally, suggest including the proper header if not already included.
+static void emitReplacement(Sema &S, SourceLocation Loc, SourceRange Range,
+ unsigned AbsKind, QualType ArgType) {
+ bool EmitHeaderHint = true;
+ const char *HeaderName = nullptr;
+ std::string FunctionName;
+ if (S.getLangOpts().CPlusPlus && !ArgType->isAnyComplexType()) {
+ FunctionName = "std::abs";
+ if (ArgType->isIntegralOrEnumerationType()) {
+ HeaderName = "cstdlib";
+ } else if (ArgType->isRealFloatingType()) {
+ HeaderName = "cmath";
+ } else {
+ llvm_unreachable("Invalid Type");
+ }
+
+ // Lookup all std::abs
+ if (NamespaceDecl *Std = S.getStdNamespace()) {
+ LookupResult R(S, &S.Context.Idents.get("abs"), Loc, Sema::LookupAnyName);
+ R.suppressDiagnostics();
+ S.LookupQualifiedName(R, Std);
+
+ for (const auto *I : R) {
+ const FunctionDecl *FDecl = nullptr;
+ if (const UsingShadowDecl *UsingD = dyn_cast<UsingShadowDecl>(I)) {
+ FDecl = dyn_cast<FunctionDecl>(UsingD->getTargetDecl());
+ } else {
+ FDecl = dyn_cast<FunctionDecl>(I);
+ }
+ if (!FDecl)
+ continue;
+
+ // Found std::abs(), check that they are the right ones.
+ if (FDecl->getNumParams() != 1)
+ continue;
+
+ // Check that the parameter type can handle the argument.
+ QualType ParamType = FDecl->getParamDecl(0)->getType();
+ if (getAbsoluteValueKind(ArgType) == getAbsoluteValueKind(ParamType) &&
+ S.Context.getTypeSize(ArgType) <=
+ S.Context.getTypeSize(ParamType)) {
+ // Found a function, don't need the header hint.
+ EmitHeaderHint = false;
+ break;
+ }
+ }
+ }
+ } else {
+ FunctionName = S.Context.BuiltinInfo.getName(AbsKind);
+ HeaderName = S.Context.BuiltinInfo.getHeaderName(AbsKind);
+
+ if (HeaderName) {
+ DeclarationName DN(&S.Context.Idents.get(FunctionName));
+ LookupResult R(S, DN, Loc, Sema::LookupAnyName);
+ R.suppressDiagnostics();
+ S.LookupName(R, S.getCurScope());
+
+ if (R.isSingleResult()) {
+ FunctionDecl *FD = dyn_cast<FunctionDecl>(R.getFoundDecl());
+ if (FD && FD->getBuiltinID() == AbsKind) {
+ EmitHeaderHint = false;
+ } else {
+ return;
+ }
+ } else if (!R.empty()) {
+ return;
+ }
+ }
+ }
+
+ S.Diag(Loc, diag::note_replace_abs_function)
+ << FunctionName << FixItHint::CreateReplacement(Range, FunctionName);
+
+ if (!HeaderName)
+ return;
+
+ if (!EmitHeaderHint)
+ return;
+
+ S.Diag(Loc, diag::note_include_header_or_declare) << HeaderName
+ << FunctionName;
+}
+
+template <std::size_t StrLen>
+static bool IsStdFunction(const FunctionDecl *FDecl,
+ const char (&Str)[StrLen]) {
+ if (!FDecl)
+ return false;
+ if (!FDecl->getIdentifier() || !FDecl->getIdentifier()->isStr(Str))
+ return false;
+ if (!FDecl->isInStdNamespace())
+ return false;
+
+ return true;
+}
+
+enum class MathCheck { NaN, Inf };
+static bool IsInfOrNanFunction(StringRef calleeName, MathCheck Check) {
+ auto MatchesAny = [&](std::initializer_list<llvm::StringRef> names) {
+ return llvm::is_contained(names, calleeName);
+ };
+
+ switch (Check) {
+ case MathCheck::NaN:
+ return MatchesAny({"__builtin_nan", "__builtin_nanf", "__builtin_nanl",
+ "__builtin_nanf16", "__builtin_nanf128"});
+ case MathCheck::Inf:
+ return MatchesAny({"__builtin_inf", "__builtin_inff", "__builtin_infl",
+ "__builtin_inff16", "__builtin_inff128"});
+ }
+ llvm_unreachable("unknown MathCheck");
+}
+
+static bool IsInfinityFunction(const FunctionDecl *FDecl) {
+ if (FDecl->getName() != "infinity")
+ return false;
+
+ if (const CXXMethodDecl *MDecl = dyn_cast<CXXMethodDecl>(FDecl)) {
+ const CXXRecordDecl *RDecl = MDecl->getParent();
+ if (RDecl->getName() != "numeric_limits")
+ return false;
+
+ if (const NamespaceDecl *NSDecl =
+ dyn_cast<NamespaceDecl>(RDecl->getDeclContext()))
+ return NSDecl->isStdNamespace();
+ }
+
+ return false;
+}
+
+void Sema::CheckInfNaNFunction(const CallExpr *Call,
+ const FunctionDecl *FDecl) {
+ if (!FDecl->getIdentifier())
+ return;
+
+ FPOptions FPO = Call->getFPFeaturesInEffect(getLangOpts());
+ if (FPO.getNoHonorNaNs() &&
+ (IsStdFunction(FDecl, "isnan") || IsStdFunction(FDecl, "isunordered") ||
+ IsInfOrNanFunction(FDecl->getName(), MathCheck::NaN))) {
+ Diag(Call->getBeginLoc(), diag::warn_fp_nan_inf_when_disabled)
+ << 1 << 0 << Call->getSourceRange();
+ return;
+ }
+
+ if (FPO.getNoHonorInfs() &&
+ (IsStdFunction(FDecl, "isinf") || IsStdFunction(FDecl, "isfinite") ||
+ IsInfinityFunction(FDecl) ||
+ IsInfOrNanFunction(FDecl->getName(), MathCheck::Inf))) {
+ Diag(Call->getBeginLoc(), diag::warn_fp_nan_inf_when_disabled)
+ << 0 << 0 << Call->getSourceRange();
+ }
+}
+
+void Sema::CheckAbsoluteValueFunction(const CallExpr *Call,
+ const FunctionDecl *FDecl) {
+ if (Call->getNumArgs() != 1)
+ return;
+
+ unsigned AbsKind = getAbsoluteValueFunctionKind(FDecl);
+ bool IsStdAbs = IsStdFunction(FDecl, "abs");
+ if (AbsKind == 0 && !IsStdAbs)
+ return;
+
+ QualType ArgType = Call->getArg(0)->IgnoreParenImpCasts()->getType();
+ QualType ParamType = Call->getArg(0)->getType();
+
+ // Unsigned types cannot be negative. Suggest removing the absolute value
+ // function call.
+ if (ArgType->isUnsignedIntegerType()) {
+ std::string FunctionName =
+ IsStdAbs ? "std::abs" : Context.BuiltinInfo.getName(AbsKind);
+ Diag(Call->getExprLoc(), diag::warn_unsigned_abs) << ArgType << ParamType;
+ Diag(Call->getExprLoc(), diag::note_remove_abs)
+ << FunctionName
+ << FixItHint::CreateRemoval(Call->getCallee()->getSourceRange());
+ return;
+ }
+
+ // Taking the absolute value of a pointer is very suspicious, they probably
+ // wanted to index into an array, dereference a pointer, call a function, etc.
+ if (ArgType->isPointerType() || ArgType->canDecayToPointerType()) {
+ unsigned DiagType = 0;
+ if (ArgType->isFunctionType())
+ DiagType = 1;
+ else if (ArgType->isArrayType())
+ DiagType = 2;
+
+ Diag(Call->getExprLoc(), diag::warn_pointer_abs) << DiagType << ArgType;
+ return;
+ }
+
+ // std::abs has overloads which prevent most of the absolute value problems
+ // from occurring.
+ if (IsStdAbs)
+ return;
+
+ AbsoluteValueKind ArgValueKind = getAbsoluteValueKind(ArgType);
+ AbsoluteValueKind ParamValueKind = getAbsoluteValueKind(ParamType);
+
+ // The argument and parameter are the same kind. Check if they are the right
+ // size.
+ if (ArgValueKind == ParamValueKind) {
+ if (Context.getTypeSize(ArgType) <= Context.getTypeSize(ParamType))
+ return;
+
+ unsigned NewAbsKind = getBestAbsFunction(Context, ArgType, AbsKind);
+ Diag(Call->getExprLoc(), diag::warn_abs_too_small)
+ << FDecl << ArgType << ParamType;
+
+ if (NewAbsKind == 0)
+ return;
+
+ emitReplacement(*this, Call->getExprLoc(),
+ Call->getCallee()->getSourceRange(), NewAbsKind, ArgType);
+ return;
+ }
+
+ // ArgValueKind != ParamValueKind
+ // The wrong type of absolute value function was used. Attempt to find the
+ // proper one.
+ unsigned NewAbsKind = changeAbsFunction(AbsKind, ArgValueKind);
+ NewAbsKind = getBestAbsFunction(Context, ArgType, NewAbsKind);
+ if (NewAbsKind == 0)
+ return;
+
+ Diag(Call->getExprLoc(), diag::warn_wrong_absolute_value_type)
+ << FDecl << ParamValueKind << ArgValueKind;
+
+ emitReplacement(*this, Call->getExprLoc(),
+ Call->getCallee()->getSourceRange(), NewAbsKind, ArgType);
+}
+
+//===--- CHECK: Warn on use of std::max and unsigned zero. r---------------===//
+void Sema::CheckMaxUnsignedZero(const CallExpr *Call,
+ const FunctionDecl *FDecl) {
+ if (!Call || !FDecl) return;
+
+ // Ignore template specializations and macros.
+ if (inTemplateInstantiation()) return;
+ if (Call->getExprLoc().isMacroID()) return;
+
+ // Only care about the one template argument, two function parameter std::max
+ if (Call->getNumArgs() != 2) return;
+ if (!IsStdFunction(FDecl, "max")) return;
+ const auto * ArgList = FDecl->getTemplateSpecializationArgs();
+ if (!ArgList) return;
+ if (ArgList->size() != 1) return;
+
+ // Check that template type argument is unsigned integer.
+ const auto& TA = ArgList->get(0);
+ if (TA.getKind() != TemplateArgument::Type) return;
+ QualType ArgType = TA.getAsType();
+ if (!ArgType->isUnsignedIntegerType()) return;
+
+ // See if either argument is a literal zero.
+ auto IsLiteralZeroArg = [](const Expr* E) -> bool {
+ const auto *MTE = dyn_cast<MaterializeTemporaryExpr>(E);
+ if (!MTE) return false;
+ const auto *Num = dyn_cast<IntegerLiteral>(MTE->getSubExpr());
+ if (!Num) return false;
+ if (Num->getValue() != 0) return false;
+ return true;
+ };
+
+ const Expr *FirstArg = Call->getArg(0);
+ const Expr *SecondArg = Call->getArg(1);
+ const bool IsFirstArgZero = IsLiteralZeroArg(FirstArg);
+ const bool IsSecondArgZero = IsLiteralZeroArg(SecondArg);
+
+ // Only warn when exactly one argument is zero.
+ if (IsFirstArgZero == IsSecondArgZero) return;
+
+ SourceRange FirstRange = FirstArg->getSourceRange();
+ SourceRange SecondRange = SecondArg->getSourceRange();
+
+ SourceRange ZeroRange = IsFirstArgZero ? FirstRange : SecondRange;
+
+ Diag(Call->getExprLoc(), diag::warn_max_unsigned_zero)
+ << IsFirstArgZero << Call->getCallee()->getSourceRange() << ZeroRange;
+
+ // Deduce what parts to remove so that "std::max(0u, foo)" becomes "(foo)".
+ SourceRange RemovalRange;
+ if (IsFirstArgZero) {
+ RemovalRange = SourceRange(FirstRange.getBegin(),
+ SecondRange.getBegin().getLocWithOffset(-1));
+ } else {
+ RemovalRange = SourceRange(getLocForEndOfToken(FirstRange.getEnd()),
+ SecondRange.getEnd());
+ }
+
+ Diag(Call->getExprLoc(), diag::note_remove_max_call)
+ << FixItHint::CreateRemoval(Call->getCallee()->getSourceRange())
+ << FixItHint::CreateRemoval(RemovalRange);
+}
+
+//===--- CHECK: Standard memory functions ---------------------------------===//
+
+/// Takes the expression passed to the size_t parameter of functions
+/// such as memcmp, strncat, etc and warns if it's a comparison.
+///
+/// This is to catch typos like `if (memcmp(&a, &b, sizeof(a) > 0))`.
+static bool CheckMemorySizeofForComparison(Sema &S, const Expr *E,
+ const IdentifierInfo *FnName,
+ SourceLocation FnLoc,
+ SourceLocation RParenLoc) {
+ const auto *Size = dyn_cast<BinaryOperator>(E);
+ if (!Size)
+ return false;
+
+ // if E is binop and op is <=>, >, <, >=, <=, ==, &&, ||:
+ if (!Size->isComparisonOp() && !Size->isLogicalOp())
+ return false;
+
+ SourceRange SizeRange = Size->getSourceRange();
+ S.Diag(Size->getOperatorLoc(), diag::warn_memsize_comparison)
+ << SizeRange << FnName;
+ S.Diag(FnLoc, diag::note_memsize_comparison_paren)
+ << FnName
+ << FixItHint::CreateInsertion(
+ S.getLocForEndOfToken(Size->getLHS()->getEndLoc()), ")")
+ << FixItHint::CreateRemoval(RParenLoc);
+ S.Diag(SizeRange.getBegin(), diag::note_memsize_comparison_cast_silence)
+ << FixItHint::CreateInsertion(SizeRange.getBegin(), "(size_t)(")
+ << FixItHint::CreateInsertion(S.getLocForEndOfToken(SizeRange.getEnd()),
+ ")");
+
+ return true;
+}
+
+/// Determine whether the given type is or contains a dynamic class type
+/// (e.g., whether it has a vtable).
+static const CXXRecordDecl *getContainedDynamicClass(QualType T,
+ bool &IsContained) {
+ // Look through array types while ignoring qualifiers.
+ const Type *Ty = T->getBaseElementTypeUnsafe();
+ IsContained = false;
+
+ const CXXRecordDecl *RD = Ty->getAsCXXRecordDecl();
+ RD = RD ? RD->getDefinition() : nullptr;
+ if (!RD || RD->isInvalidDecl())
+ return nullptr;
+
+ if (RD->isDynamicClass())
+ return RD;
+
+ // Check all the fields. If any bases were dynamic, the class is dynamic.
+ // It's impossible for a class to transitively contain itself by value, so
+ // infinite recursion is impossible.
+ for (auto *FD : RD->fields()) {
+ bool SubContained;
+ if (const CXXRecordDecl *ContainedRD =
+ getContainedDynamicClass(FD->getType(), SubContained)) {
+ IsContained = true;
+ return ContainedRD;
+ }
+ }
+
+ return nullptr;
+}
+
+static const UnaryExprOrTypeTraitExpr *getAsSizeOfExpr(const Expr *E) {
+ if (const auto *Unary = dyn_cast<UnaryExprOrTypeTraitExpr>(E))
+ if (Unary->getKind() == UETT_SizeOf)
+ return Unary;
+ return nullptr;
+}
+
+/// If E is a sizeof expression, returns its argument expression,
+/// otherwise returns NULL.
+static const Expr *getSizeOfExprArg(const Expr *E) {
+ if (const UnaryExprOrTypeTraitExpr *SizeOf = getAsSizeOfExpr(E))
+ if (!SizeOf->isArgumentType())
+ return SizeOf->getArgumentExpr()->IgnoreParenImpCasts();
+ return nullptr;
+}
+
+/// If E is a sizeof expression, returns its argument type.
+static QualType getSizeOfArgType(const Expr *E) {
+ if (const UnaryExprOrTypeTraitExpr *SizeOf = getAsSizeOfExpr(E))
+ return SizeOf->getTypeOfArgument();
+ return QualType();
+}
+
+namespace {
+
+struct SearchNonTrivialToInitializeField
+ : DefaultInitializedTypeVisitor<SearchNonTrivialToInitializeField> {
+ using Super =
+ DefaultInitializedTypeVisitor<SearchNonTrivialToInitializeField>;
+
+ SearchNonTrivialToInitializeField(const Expr *E, Sema &S) : E(E), S(S) {}
+
+ void visitWithKind(QualType::PrimitiveDefaultInitializeKind PDIK, QualType FT,
+ SourceLocation SL) {
+ if (const auto *AT = asDerived().getContext().getAsArrayType(FT)) {
+ asDerived().visitArray(PDIK, AT, SL);
+ return;
+ }
+
+ Super::visitWithKind(PDIK, FT, SL);
+ }
+
+ void visitARCStrong(QualType FT, SourceLocation SL) {
+ S.DiagRuntimeBehavior(SL, E, S.PDiag(diag::note_nontrivial_field) << 1);
+ }
+ void visitARCWeak(QualType FT, SourceLocation SL) {
+ S.DiagRuntimeBehavior(SL, E, S.PDiag(diag::note_nontrivial_field) << 1);
+ }
+ void visitStruct(QualType FT, SourceLocation SL) {
+ for (const FieldDecl *FD : FT->castAsRecordDecl()->fields())
+ visit(FD->getType(), FD->getLocation());
+ }
+ void visitArray(QualType::PrimitiveDefaultInitializeKind PDIK,
+ const ArrayType *AT, SourceLocation SL) {
+ visit(getContext().getBaseElementType(AT), SL);
+ }
+ void visitTrivial(QualType FT, SourceLocation SL) {}
+
+ static void diag(QualType RT, const Expr *E, Sema &S) {
+ SearchNonTrivialToInitializeField(E, S).visitStruct(RT, SourceLocation());
+ }
+
+ ASTContext &getContext() { return S.getASTContext(); }
+
+ const Expr *E;
+ Sema &S;
+};
+
+struct SearchNonTrivialToCopyField
+ : CopiedTypeVisitor<SearchNonTrivialToCopyField, false> {
+ using Super = CopiedTypeVisitor<SearchNonTrivialToCopyField, false>;
+
+ SearchNonTrivialToCopyField(const Expr *E, Sema &S) : E(E), S(S) {}
+
+ void visitWithKind(QualType::PrimitiveCopyKind PCK, QualType FT,
+ SourceLocation SL) {
+ if (const auto *AT = asDerived().getContext().getAsArrayType(FT)) {
+ asDerived().visitArray(PCK, AT, SL);
+ return;
+ }
+
+ Super::visitWithKind(PCK, FT, SL);
+ }
+
+ void visitARCStrong(QualType FT, SourceLocation SL) {
+ S.DiagRuntimeBehavior(SL, E, S.PDiag(diag::note_nontrivial_field) << 0);
+ }
+ void visitARCWeak(QualType FT, SourceLocation SL) {
+ S.DiagRuntimeBehavior(SL, E, S.PDiag(diag::note_nontrivial_field) << 0);
+ }
+ void visitPtrAuth(QualType FT, SourceLocation SL) {
+ S.DiagRuntimeBehavior(SL, E, S.PDiag(diag::note_nontrivial_field) << 0);
+ }
+ void visitStruct(QualType FT, SourceLocation SL) {
+ for (const FieldDecl *FD : FT->castAsRecordDecl()->fields())
+ visit(FD->getType(), FD->getLocation());
+ }
+ void visitArray(QualType::PrimitiveCopyKind PCK, const ArrayType *AT,
+ SourceLocation SL) {
+ visit(getContext().getBaseElementType(AT), SL);
+ }
+ void preVisit(QualType::PrimitiveCopyKind PCK, QualType FT,
+ SourceLocation SL) {}
+ void visitTrivial(QualType FT, SourceLocation SL) {}
+ void visitVolatileTrivial(QualType FT, SourceLocation SL) {}
+
+ static void diag(QualType RT, const Expr *E, Sema &S) {
+ SearchNonTrivialToCopyField(E, S).visitStruct(RT, SourceLocation());
+ }
+
+ ASTContext &getContext() { return S.getASTContext(); }
+
+ const Expr *E;
+ Sema &S;
+};
+
+}
+
+/// Detect if \c SizeofExpr is likely to calculate the sizeof an object.
+static bool doesExprLikelyComputeSize(const Expr *SizeofExpr) {
+ SizeofExpr = SizeofExpr->IgnoreParenImpCasts();
+
+ if (const auto *BO = dyn_cast<BinaryOperator>(SizeofExpr)) {
+ if (BO->getOpcode() != BO_Mul && BO->getOpcode() != BO_Add)
+ return false;
+
+ return doesExprLikelyComputeSize(BO->getLHS()) ||
+ doesExprLikelyComputeSize(BO->getRHS());
+ }
+
+ return getAsSizeOfExpr(SizeofExpr) != nullptr;
+}
+
+/// Check if the ArgLoc originated from a macro passed to the call at CallLoc.
+///
+/// \code
+/// #define MACRO 0
+/// foo(MACRO);
+/// foo(0);
+/// \endcode
+///
+/// This should return true for the first call to foo, but not for the second
+/// (regardless of whether foo is a macro or function).
+static bool isArgumentExpandedFromMacro(SourceManager &SM,
+ SourceLocation CallLoc,
+ SourceLocation ArgLoc) {
+ if (!CallLoc.isMacroID())
+ return SM.getFileID(CallLoc) != SM.getFileID(ArgLoc);
+
+ return SM.getFileID(SM.getImmediateMacroCallerLoc(CallLoc)) !=
+ SM.getFileID(SM.getImmediateMacroCallerLoc(ArgLoc));
+}
+
+/// Diagnose cases like 'memset(buf, sizeof(buf), 0)', which should have the
+/// last two arguments transposed.
+static void CheckMemaccessSize(Sema &S, unsigned BId, const CallExpr *Call) {
+ if (BId != Builtin::BImemset && BId != Builtin::BIbzero)
+ return;
+
+ const Expr *SizeArg =
+ Call->getArg(BId == Builtin::BImemset ? 2 : 1)->IgnoreImpCasts();
+
+ auto isLiteralZero = [](const Expr *E) {
+ return (isa<IntegerLiteral>(E) &&
+ cast<IntegerLiteral>(E)->getValue() == 0) ||
+ (isa<CharacterLiteral>(E) &&
+ cast<CharacterLiteral>(E)->getValue() == 0);
+ };
+
+ // If we're memsetting or bzeroing 0 bytes, then this is likely an error.
+ SourceLocation CallLoc = Call->getRParenLoc();
+ SourceManager &SM = S.getSourceManager();
+ if (isLiteralZero(SizeArg) &&
+ !isArgumentExpandedFromMacro(SM, CallLoc, SizeArg->getExprLoc())) {
+
+ SourceLocation DiagLoc = SizeArg->getExprLoc();
+
+ // Some platforms #define bzero to __builtin_memset. See if this is the
+ // case, and if so, emit a better diagnostic.
+ if (BId == Builtin::BIbzero ||
+ (CallLoc.isMacroID() && Lexer::getImmediateMacroName(
+ CallLoc, SM, S.getLangOpts()) == "bzero")) {
+ S.Diag(DiagLoc, diag::warn_suspicious_bzero_size);
+ S.Diag(DiagLoc, diag::note_suspicious_bzero_size_silence);
+ } else if (!isLiteralZero(Call->getArg(1)->IgnoreImpCasts())) {
+ S.Diag(DiagLoc, diag::warn_suspicious_sizeof_memset) << 0;
+ S.Diag(DiagLoc, diag::note_suspicious_sizeof_memset_silence) << 0;
+ }
+ return;
+ }
+
+ // If the second argument to a memset is a sizeof expression and the third
+ // isn't, this is also likely an error. This should catch
+ // 'memset(buf, sizeof(buf), 0xff)'.
+ if (BId == Builtin::BImemset &&
+ doesExprLikelyComputeSize(Call->getArg(1)) &&
+ !doesExprLikelyComputeSize(Call->getArg(2))) {
+ SourceLocation DiagLoc = Call->getArg(1)->getExprLoc();
+ S.Diag(DiagLoc, diag::warn_suspicious_sizeof_memset) << 1;
+ S.Diag(DiagLoc, diag::note_suspicious_sizeof_memset_silence) << 1;
+ return;
+ }
+}
+
+void Sema::CheckMemaccessArguments(const CallExpr *Call,
+ unsigned BId,
+ IdentifierInfo *FnName) {
+ assert(BId != 0);
+
+ // It is possible to have a non-standard definition of memset. Validate
+ // we have enough arguments, and if not, abort further checking.
+ unsigned ExpectedNumArgs =
+ (BId == Builtin::BIstrndup || BId == Builtin::BIbzero ? 2 : 3);
+ if (Call->getNumArgs() < ExpectedNumArgs)
+ return;
+
+ unsigned LastArg = (BId == Builtin::BImemset || BId == Builtin::BIbzero ||
+ BId == Builtin::BIstrndup ? 1 : 2);
+ unsigned LenArg =
+ (BId == Builtin::BIbzero || BId == Builtin::BIstrndup ? 1 : 2);
+ const Expr *LenExpr = Call->getArg(LenArg)->IgnoreParenImpCasts();
+
+ if (CheckMemorySizeofForComparison(*this, LenExpr, FnName,
+ Call->getBeginLoc(), Call->getRParenLoc()))
+ return;
+
+ // Catch cases like 'memset(buf, sizeof(buf), 0)'.
+ CheckMemaccessSize(*this, BId, Call);
+
+ // We have special checking when the length is a sizeof expression.
+ QualType SizeOfArgTy = getSizeOfArgType(LenExpr);
+ const Expr *SizeOfArg = getSizeOfExprArg(LenExpr);
+ llvm::FoldingSetNodeID SizeOfArgID;
+
+ // Although widely used, 'bzero' is not a standard function. Be more strict
+ // with the argument types before allowing diagnostics and only allow the
+ // form bzero(ptr, sizeof(...)).
+ QualType FirstArgTy = Call->getArg(0)->IgnoreParenImpCasts()->getType();
+ if (BId == Builtin::BIbzero && !FirstArgTy->getAs<PointerType>())
+ return;
+
+ for (unsigned ArgIdx = 0; ArgIdx != LastArg; ++ArgIdx) {
+ const Expr *Dest = Call->getArg(ArgIdx)->IgnoreParenImpCasts();
+ SourceRange ArgRange = Call->getArg(ArgIdx)->getSourceRange();
+
+ QualType DestTy = Dest->getType();
+ QualType PointeeTy;
+ if (const PointerType *DestPtrTy = DestTy->getAs<PointerType>()) {
+ PointeeTy = DestPtrTy->getPointeeType();
+
+ // Never warn about void type pointers. This can be used to suppress
+ // false positives.
+ if (PointeeTy->isVoidType())
+ continue;
+
+ // Catch "memset(p, 0, sizeof(p))" -- needs to be sizeof(*p). Do this by
+ // actually comparing the expressions for equality. Because computing the
+ // expression IDs can be expensive, we only do this if the diagnostic is
+ // enabled.
+ if (SizeOfArg &&
+ !Diags.isIgnored(diag::warn_sizeof_pointer_expr_memaccess,
+ SizeOfArg->getExprLoc())) {
+ // We only compute IDs for expressions if the warning is enabled, and
+ // cache the sizeof arg's ID.
+ if (SizeOfArgID == llvm::FoldingSetNodeID())
+ SizeOfArg->Profile(SizeOfArgID, Context, true);
+ llvm::FoldingSetNodeID DestID;
+ Dest->Profile(DestID, Context, true);
+ if (DestID == SizeOfArgID) {
+ // TODO: For strncpy() and friends, this could suggest sizeof(dst)
+ // over sizeof(src) as well.
+ unsigned ActionIdx = 0; // Default is to suggest dereferencing.
+ StringRef ReadableName = FnName->getName();
+
+ if (const UnaryOperator *UnaryOp = dyn_cast<UnaryOperator>(Dest))
+ if (UnaryOp->getOpcode() == UO_AddrOf)
+ ActionIdx = 1; // If its an address-of operator, just remove it.
+ if (!PointeeTy->isIncompleteType() &&
+ (Context.getTypeSize(PointeeTy) == Context.getCharWidth()))
+ ActionIdx = 2; // If the pointee's size is sizeof(char),
+ // suggest an explicit length.
+
+ // If the function is defined as a builtin macro, do not show macro
+ // expansion.
+ SourceLocation SL = SizeOfArg->getExprLoc();
+ SourceRange DSR = Dest->getSourceRange();
+ SourceRange SSR = SizeOfArg->getSourceRange();
+ SourceManager &SM = getSourceManager();
+
+ if (SM.isMacroArgExpansion(SL)) {
+ ReadableName = Lexer::getImmediateMacroName(SL, SM, LangOpts);
+ SL = SM.getSpellingLoc(SL);
+ DSR = SourceRange(SM.getSpellingLoc(DSR.getBegin()),
+ SM.getSpellingLoc(DSR.getEnd()));
+ SSR = SourceRange(SM.getSpellingLoc(SSR.getBegin()),
+ SM.getSpellingLoc(SSR.getEnd()));
+ }
+
+ DiagRuntimeBehavior(SL, SizeOfArg,
+ PDiag(diag::warn_sizeof_pointer_expr_memaccess)
+ << ReadableName
+ << PointeeTy
+ << DestTy
+ << DSR
+ << SSR);
+ DiagRuntimeBehavior(SL, SizeOfArg,
+ PDiag(diag::warn_sizeof_pointer_expr_memaccess_note)
+ << ActionIdx
+ << SSR);
+
+ break;
+ }
+ }
+
+ // Also check for cases where the sizeof argument is the exact same
+ // type as the memory argument, and where it points to a user-defined
+ // record type.
+ if (SizeOfArgTy != QualType()) {
+ if (PointeeTy->isRecordType() &&
+ Context.typesAreCompatible(SizeOfArgTy, DestTy)) {
+ DiagRuntimeBehavior(LenExpr->getExprLoc(), Dest,
+ PDiag(diag::warn_sizeof_pointer_type_memaccess)
+ << FnName << SizeOfArgTy << ArgIdx
+ << PointeeTy << Dest->getSourceRange()
+ << LenExpr->getSourceRange());
+ break;
+ }
+ }
+ } else if (DestTy->isArrayType()) {
+ PointeeTy = DestTy;
+ }
+
+ if (PointeeTy == QualType())
+ continue;
+
+ // Always complain about dynamic classes.
+ bool IsContained;
+ if (const CXXRecordDecl *ContainedRD =
+ getContainedDynamicClass(PointeeTy, IsContained)) {
+
+ unsigned OperationType = 0;
+ const bool IsCmp = BId == Builtin::BImemcmp || BId == Builtin::BIbcmp;
+ // "overwritten" if we're warning about the destination for any call
+ // but memcmp; otherwise a verb appropriate to the call.
+ if (ArgIdx != 0 || IsCmp) {
+ if (BId == Builtin::BImemcpy)
+ OperationType = 1;
+ else if(BId == Builtin::BImemmove)
+ OperationType = 2;
+ else if (IsCmp)
+ OperationType = 3;
+ }
+
+ DiagRuntimeBehavior(Dest->getExprLoc(), Dest,
+ PDiag(diag::warn_dyn_class_memaccess)
+ << (IsCmp ? ArgIdx + 2 : ArgIdx) << FnName
+ << IsContained << ContainedRD << OperationType
+ << Call->getCallee()->getSourceRange());
+ } else if (PointeeTy.hasNonTrivialObjCLifetime() &&
+ BId != Builtin::BImemset)
+ DiagRuntimeBehavior(
+ Dest->getExprLoc(), Dest,
+ PDiag(diag::warn_arc_object_memaccess)
+ << ArgIdx << FnName << PointeeTy
+ << Call->getCallee()->getSourceRange());
+ else if (const auto *RD = PointeeTy->getAsRecordDecl()) {
+
+ // FIXME: Do not consider incomplete types even though they may be
+ // completed later. GCC does not diagnose such code, but we may want to
+ // consider diagnosing it in the future, perhaps under a different, but
+ // related, diagnostic group.
+ bool NonTriviallyCopyableCXXRecord =
+ getLangOpts().CPlusPlus && RD->isCompleteDefinition() &&
+ !PointeeTy.isTriviallyCopyableType(Context);
+
+ if ((BId == Builtin::BImemset || BId == Builtin::BIbzero) &&
+ RD->isNonTrivialToPrimitiveDefaultInitialize()) {
+ DiagRuntimeBehavior(Dest->getExprLoc(), Dest,
+ PDiag(diag::warn_cstruct_memaccess)
+ << ArgIdx << FnName << PointeeTy << 0);
+ SearchNonTrivialToInitializeField::diag(PointeeTy, Dest, *this);
+ } else if ((BId == Builtin::BImemset || BId == Builtin::BIbzero) &&
+ NonTriviallyCopyableCXXRecord && ArgIdx == 0) {
+ // FIXME: Limiting this warning to dest argument until we decide
+ // whether it's valid for source argument too.
+ DiagRuntimeBehavior(Dest->getExprLoc(), Dest,
+ PDiag(diag::warn_cxxstruct_memaccess)
+ << FnName << PointeeTy);
+ } else if ((BId == Builtin::BImemcpy || BId == Builtin::BImemmove) &&
+ RD->isNonTrivialToPrimitiveCopy()) {
+ DiagRuntimeBehavior(Dest->getExprLoc(), Dest,
+ PDiag(diag::warn_cstruct_memaccess)
+ << ArgIdx << FnName << PointeeTy << 1);
+ SearchNonTrivialToCopyField::diag(PointeeTy, Dest, *this);
+ } else if ((BId == Builtin::BImemcpy || BId == Builtin::BImemmove) &&
+ NonTriviallyCopyableCXXRecord && ArgIdx == 0) {
+ // FIXME: Limiting this warning to dest argument until we decide
+ // whether it's valid for source argument too.
+ DiagRuntimeBehavior(Dest->getExprLoc(), Dest,
+ PDiag(diag::warn_cxxstruct_memaccess)
+ << FnName << PointeeTy);
+ } else {
+ continue;
+ }
+ } else
+ continue;
+
+ DiagRuntimeBehavior(
+ Dest->getExprLoc(), Dest,
+ PDiag(diag::note_bad_memaccess_silence)
+ << FixItHint::CreateInsertion(ArgRange.getBegin(), "(void*)"));
+ break;
+ }
+}
+
+// A little helper routine: ignore addition and subtraction of integer literals.
+// This intentionally does not ignore all integer constant expressions because
+// we don't want to remove sizeof().
+static const Expr *ignoreLiteralAdditions(const Expr *Ex, ASTContext &Ctx) {
+ Ex = Ex->IgnoreParenCasts();
+
+ while (true) {
+ const BinaryOperator * BO = dyn_cast<BinaryOperator>(Ex);
+ if (!BO || !BO->isAdditiveOp())
+ break;
+
+ const Expr *RHS = BO->getRHS()->IgnoreParenCasts();
+ const Expr *LHS = BO->getLHS()->IgnoreParenCasts();
+
+ if (isa<IntegerLiteral>(RHS))
+ Ex = LHS;
+ else if (isa<IntegerLiteral>(LHS))
+ Ex = RHS;
+ else
+ break;
+ }
+
+ return Ex;
+}
+
+static bool isConstantSizeArrayWithMoreThanOneElement(QualType Ty,
+ ASTContext &Context) {
+ // Only handle constant-sized or VLAs, but not flexible members.
+ if (const ConstantArrayType *CAT = Context.getAsConstantArrayType(Ty)) {
+ // Only issue the FIXIT for arrays of size > 1.
+ if (CAT->getZExtSize() <= 1)
+ return false;
+ } else if (!Ty->isVariableArrayType()) {
+ return false;
+ }
+ return true;
+}
+
+void Sema::CheckStrlcpycatArguments(const CallExpr *Call,
+ IdentifierInfo *FnName) {
+
+ // Don't crash if the user has the wrong number of arguments
+ unsigned NumArgs = Call->getNumArgs();
+ if ((NumArgs != 3) && (NumArgs != 4))
+ return;
+
+ const Expr *SrcArg = ignoreLiteralAdditions(Call->getArg(1), Context);
+ const Expr *SizeArg = ignoreLiteralAdditions(Call->getArg(2), Context);
+ const Expr *CompareWithSrc = nullptr;
+
+ if (CheckMemorySizeofForComparison(*this, SizeArg, FnName,
+ Call->getBeginLoc(), Call->getRParenLoc()))
+ return;
+
+ // Look for 'strlcpy(dst, x, sizeof(x))'
+ if (const Expr *Ex = getSizeOfExprArg(SizeArg))
+ CompareWithSrc = Ex;
+ else {
+ // Look for 'strlcpy(dst, x, strlen(x))'
+ if (const CallExpr *SizeCall = dyn_cast<CallExpr>(SizeArg)) {
+ if (SizeCall->getBuiltinCallee() == Builtin::BIstrlen &&
+ SizeCall->getNumArgs() == 1)
+ CompareWithSrc = ignoreLiteralAdditions(SizeCall->getArg(0), Context);
+ }
+ }
+
+ if (!CompareWithSrc)
+ return;
+
+ // Determine if the argument to sizeof/strlen is equal to the source
+ // argument. In principle there's all kinds of things you could do
+ // here, for instance creating an == expression and evaluating it with
+ // EvaluateAsBooleanCondition, but this uses a more direct technique:
+ const DeclRefExpr *SrcArgDRE = dyn_cast<DeclRefExpr>(SrcArg);
+ if (!SrcArgDRE)
+ return;
+
+ const DeclRefExpr *CompareWithSrcDRE = dyn_cast<DeclRefExpr>(CompareWithSrc);
+ if (!CompareWithSrcDRE ||
+ SrcArgDRE->getDecl() != CompareWithSrcDRE->getDecl())
+ return;
+
+ const Expr *OriginalSizeArg = Call->getArg(2);
+ Diag(CompareWithSrcDRE->getBeginLoc(), diag::warn_strlcpycat_wrong_size)
+ << OriginalSizeArg->getSourceRange() << FnName;
+
+ // Output a FIXIT hint if the destination is an array (rather than a
+ // pointer to an array). This could be enhanced to handle some
+ // pointers if we know the actual size, like if DstArg is 'array+2'
+ // we could say 'sizeof(array)-2'.
+ const Expr *DstArg = Call->getArg(0)->IgnoreParenImpCasts();
+ if (!isConstantSizeArrayWithMoreThanOneElement(DstArg->getType(), Context))
+ return;
+
+ SmallString<128> sizeString;
+ llvm::raw_svector_ostream OS(sizeString);
+ OS << "sizeof(";
+ DstArg->printPretty(OS, nullptr, getPrintingPolicy());
+ OS << ")";
+
+ Diag(OriginalSizeArg->getBeginLoc(), diag::note_strlcpycat_wrong_size)
+ << FixItHint::CreateReplacement(OriginalSizeArg->getSourceRange(),
+ OS.str());
+}
+
+/// Check if two expressions refer to the same declaration.
+static bool referToTheSameDecl(const Expr *E1, const Expr *E2) {
+ if (const DeclRefExpr *D1 = dyn_cast_or_null<DeclRefExpr>(E1))
+ if (const DeclRefExpr *D2 = dyn_cast_or_null<DeclRefExpr>(E2))
+ return D1->getDecl() == D2->getDecl();
+ return false;
+}
+
+static const Expr *getStrlenExprArg(const Expr *E) {
+ if (const CallExpr *CE = dyn_cast<CallExpr>(E)) {
+ const FunctionDecl *FD = CE->getDirectCallee();
+ if (!FD || FD->getMemoryFunctionKind() != Builtin::BIstrlen)
+ return nullptr;
+ return CE->getArg(0)->IgnoreParenCasts();
+ }
+ return nullptr;
+}
+
+void Sema::CheckStrncatArguments(const CallExpr *CE,
+ const IdentifierInfo *FnName) {
+ // Don't crash if the user has the wrong number of arguments.
+ if (CE->getNumArgs() < 3)
+ return;
+ const Expr *DstArg = CE->getArg(0)->IgnoreParenCasts();
+ const Expr *SrcArg = CE->getArg(1)->IgnoreParenCasts();
+ const Expr *LenArg = CE->getArg(2)->IgnoreParenCasts();
+
+ if (CheckMemorySizeofForComparison(*this, LenArg, FnName, CE->getBeginLoc(),
+ CE->getRParenLoc()))
+ return;
+
+ // Identify common expressions, which are wrongly used as the size argument
+ // to strncat and may lead to buffer overflows.
+ unsigned PatternType = 0;
+ if (const Expr *SizeOfArg = getSizeOfExprArg(LenArg)) {
+ // - sizeof(dst)
+ if (referToTheSameDecl(SizeOfArg, DstArg))
+ PatternType = 1;
+ // - sizeof(src)
+ else if (referToTheSameDecl(SizeOfArg, SrcArg))
+ PatternType = 2;
+ } else if (const BinaryOperator *BE = dyn_cast<BinaryOperator>(LenArg)) {
+ if (BE->getOpcode() == BO_Sub) {
+ const Expr *L = BE->getLHS()->IgnoreParenCasts();
+ const Expr *R = BE->getRHS()->IgnoreParenCasts();
+ // - sizeof(dst) - strlen(dst)
+ if (referToTheSameDecl(DstArg, getSizeOfExprArg(L)) &&
+ referToTheSameDecl(DstArg, getStrlenExprArg(R)))
+ PatternType = 1;
+ // - sizeof(src) - (anything)
+ else if (referToTheSameDecl(SrcArg, getSizeOfExprArg(L)))
+ PatternType = 2;
+ }
+ }
+
+ if (PatternType == 0)
+ return;
+
+ // Generate the diagnostic.
+ SourceLocation SL = LenArg->getBeginLoc();
+ SourceRange SR = LenArg->getSourceRange();
+ SourceManager &SM = getSourceManager();
+
+ // If the function is defined as a builtin macro, do not show macro expansion.
+ if (SM.isMacroArgExpansion(SL)) {
+ SL = SM.getSpellingLoc(SL);
+ SR = SourceRange(SM.getSpellingLoc(SR.getBegin()),
+ SM.getSpellingLoc(SR.getEnd()));
+ }
+
+ // Check if the destination is an array (rather than a pointer to an array).
+ QualType DstTy = DstArg->getType();
+ bool isKnownSizeArray = isConstantSizeArrayWithMoreThanOneElement(DstTy,
+ Context);
+ if (!isKnownSizeArray) {
+ if (PatternType == 1)
+ Diag(SL, diag::warn_strncat_wrong_size) << SR;
+ else
+ Diag(SL, diag::warn_strncat_src_size) << SR;
+ return;
+ }
+
+ if (PatternType == 1)
+ Diag(SL, diag::warn_strncat_large_size) << SR;
+ else
+ Diag(SL, diag::warn_strncat_src_size) << SR;
+
+ SmallString<128> sizeString;
+ llvm::raw_svector_ostream OS(sizeString);
+ OS << "sizeof(";
+ DstArg->printPretty(OS, nullptr, getPrintingPolicy());
+ OS << ") - ";
+ OS << "strlen(";
+ DstArg->printPretty(OS, nullptr, getPrintingPolicy());
+ OS << ") - 1";
+
+ Diag(SL, diag::note_strncat_wrong_size)
+ << FixItHint::CreateReplacement(SR, OS.str());
+}
+
+namespace {
+void CheckFreeArgumentsOnLvalue(Sema &S, const std::string &CalleeName,
+ const UnaryOperator *UnaryExpr, const Decl *D) {
+ if (isa<FieldDecl, FunctionDecl, VarDecl>(D)) {
+ S.Diag(UnaryExpr->getBeginLoc(), diag::warn_free_nonheap_object)
+ << CalleeName << 0 /*object: */ << cast<NamedDecl>(D);
+ return;
+ }
+}
+
+void CheckFreeArgumentsAddressof(Sema &S, const std::string &CalleeName,
+ const UnaryOperator *UnaryExpr) {
+ if (const auto *Lvalue = dyn_cast<DeclRefExpr>(UnaryExpr->getSubExpr())) {
+ const Decl *D = Lvalue->getDecl();
+ if (const auto *DD = dyn_cast<DeclaratorDecl>(D)) {
+ if (!DD->getType()->isReferenceType())
+ return CheckFreeArgumentsOnLvalue(S, CalleeName, UnaryExpr, D);
+ }
+ }
+
+ if (const auto *Lvalue = dyn_cast<MemberExpr>(UnaryExpr->getSubExpr()))
+ return CheckFreeArgumentsOnLvalue(S, CalleeName, UnaryExpr,
+ Lvalue->getMemberDecl());
+}
+
+void CheckFreeArgumentsPlus(Sema &S, const std::string &CalleeName,
+ const UnaryOperator *UnaryExpr) {
+ const auto *Lambda = dyn_cast<LambdaExpr>(
+ UnaryExpr->getSubExpr()->IgnoreImplicitAsWritten()->IgnoreParens());
+ if (!Lambda)
+ return;
+
+ S.Diag(Lambda->getBeginLoc(), diag::warn_free_nonheap_object)
+ << CalleeName << 2 /*object: lambda expression*/;
+}
+
+void CheckFreeArgumentsStackArray(Sema &S, const std::string &CalleeName,
+ const DeclRefExpr *Lvalue) {
+ const auto *Var = dyn_cast<VarDecl>(Lvalue->getDecl());
+ if (Var == nullptr)
+ return;
+
+ S.Diag(Lvalue->getBeginLoc(), diag::warn_free_nonheap_object)
+ << CalleeName << 0 /*object: */ << Var;
+}
+
+void CheckFreeArgumentsCast(Sema &S, const std::string &CalleeName,
+ const CastExpr *Cast) {
+ SmallString<128> SizeString;
+ llvm::raw_svector_ostream OS(SizeString);
+
+ clang::CastKind Kind = Cast->getCastKind();
+ if (Kind == clang::CK_BitCast &&
+ !Cast->getSubExpr()->getType()->isFunctionPointerType())
+ return;
+ if (Kind == clang::CK_IntegralToPointer &&
+ !isa<IntegerLiteral>(
+ Cast->getSubExpr()->IgnoreParenImpCasts()->IgnoreParens()))
+ return;
+
+ switch (Cast->getCastKind()) {
+ case clang::CK_BitCast:
+ case clang::CK_IntegralToPointer:
+ case clang::CK_FunctionToPointerDecay:
+ OS << '\'';
+ Cast->printPretty(OS, nullptr, S.getPrintingPolicy());
+ OS << '\'';
+ break;
+ default:
+ return;
+ }
+
+ S.Diag(Cast->getBeginLoc(), diag::warn_free_nonheap_object)
+ << CalleeName << 0 /*object: */ << OS.str();
+}
+} // namespace
+
+void Sema::CheckFreeArguments(const CallExpr *E) {
+ const std::string CalleeName =
+ cast<FunctionDecl>(E->getCalleeDecl())->getQualifiedNameAsString();
+
+ { // Prefer something that doesn't involve a cast to make things simpler.
+ const Expr *Arg = E->getArg(0)->IgnoreParenCasts();
+ if (const auto *UnaryExpr = dyn_cast<UnaryOperator>(Arg))
+ switch (UnaryExpr->getOpcode()) {
+ case UnaryOperator::Opcode::UO_AddrOf:
+ return CheckFreeArgumentsAddressof(*this, CalleeName, UnaryExpr);
+ case UnaryOperator::Opcode::UO_Plus:
+ return CheckFreeArgumentsPlus(*this, CalleeName, UnaryExpr);
+ default:
+ break;
+ }
+
+ if (const auto *Lvalue = dyn_cast<DeclRefExpr>(Arg))
+ if (Lvalue->getType()->isArrayType())
+ return CheckFreeArgumentsStackArray(*this, CalleeName, Lvalue);
+
+ if (const auto *Label = dyn_cast<AddrLabelExpr>(Arg)) {
+ Diag(Label->getBeginLoc(), diag::warn_free_nonheap_object)
+ << CalleeName << 0 /*object: */ << Label->getLabel()->getIdentifier();
+ return;
+ }
+
+ if (isa<BlockExpr>(Arg)) {
+ Diag(Arg->getBeginLoc(), diag::warn_free_nonheap_object)
+ << CalleeName << 1 /*object: block*/;
+ return;
+ }
+ }
+ // Maybe the cast was important, check after the other cases.
+ if (const auto *Cast = dyn_cast<CastExpr>(E->getArg(0)))
+ return CheckFreeArgumentsCast(*this, CalleeName, Cast);
+}
+
+void
+Sema::CheckReturnValExpr(Expr *RetValExp, QualType lhsType,
+ SourceLocation ReturnLoc,
+ bool isObjCMethod,
+ const AttrVec *Attrs,
+ const FunctionDecl *FD) {
+ // Check if the return value is null but should not be.
+ if (((Attrs && hasSpecificAttr<ReturnsNonNullAttr>(*Attrs)) ||
+ (!isObjCMethod && isNonNullType(lhsType))) &&
+ CheckNonNullExpr(*this, RetValExp))
+ Diag(ReturnLoc, diag::warn_null_ret)
+ << (isObjCMethod ? 1 : 0) << RetValExp->getSourceRange();
+
+ // C++11 [basic.stc.dynamic.allocation]p4:
+ // If an allocation function declared with a non-throwing
+ // exception-specification fails to allocate storage, it shall return
+ // a null pointer. Any other allocation function that fails to allocate
+ // storage shall indicate failure only by throwing an exception [...]
+ if (FD) {
+ OverloadedOperatorKind Op = FD->getOverloadedOperator();
+ if (Op == OO_New || Op == OO_Array_New) {
+ const FunctionProtoType *Proto
+ = FD->getType()->castAs<FunctionProtoType>();
+ if (!Proto->isNothrow(/*ResultIfDependent*/true) &&
+ CheckNonNullExpr(*this, RetValExp))
+ Diag(ReturnLoc, diag::warn_operator_new_returns_null)
+ << FD << getLangOpts().CPlusPlus11;
+ }
+ }
+
+ if (RetValExp && RetValExp->getType()->isWebAssemblyTableType()) {
+ Diag(ReturnLoc, diag::err_wasm_table_art) << 1;
+ }
+
+ // PPC MMA non-pointer types are not allowed as return type. Checking the type
+ // here prevent the user from using a PPC MMA type as trailing return type.
+ if (Context.getTargetInfo().getTriple().isPPC64())
+ PPC().CheckPPCMMAType(RetValExp->getType(), ReturnLoc);
+}
+
+void Sema::CheckFloatComparison(SourceLocation Loc, const Expr *LHS,
+ const Expr *RHS, BinaryOperatorKind Opcode) {
+ if (!BinaryOperator::isEqualityOp(Opcode))
+ return;
+
+ // Match and capture subexpressions such as "(float) X == 0.1".
+ const FloatingLiteral *FPLiteral;
+ const CastExpr *FPCast;
+ auto getCastAndLiteral = [&FPLiteral, &FPCast](const Expr *L, const Expr *R) {
+ FPLiteral = dyn_cast<FloatingLiteral>(L->IgnoreParens());
+ FPCast = dyn_cast<CastExpr>(R->IgnoreParens());
+ return FPLiteral && FPCast;
+ };
+
+ if (getCastAndLiteral(LHS, RHS) || getCastAndLiteral(RHS, LHS)) {
+ auto *SourceTy = FPCast->getSubExpr()->getType()->getAs<BuiltinType>();
+ auto *TargetTy = FPLiteral->getType()->getAs<BuiltinType>();
+ if (SourceTy && TargetTy && SourceTy->isFloatingPoint() &&
+ TargetTy->isFloatingPoint()) {
+ bool Lossy;
+ llvm::APFloat TargetC = FPLiteral->getValue();
+ TargetC.convert(Context.getFloatTypeSemantics(QualType(SourceTy, 0)),
+ llvm::APFloat::rmNearestTiesToEven, &Lossy);
+ if (Lossy) {
+ // If the literal cannot be represented in the source type, then a
+ // check for == is always false and check for != is always true.
+ Diag(Loc, diag::warn_float_compare_literal)
+ << (Opcode == BO_EQ) << QualType(SourceTy, 0)
+ << LHS->getSourceRange() << RHS->getSourceRange();
+ return;
+ }
+ }
+ }
+
+ // Match a more general floating-point equality comparison (-Wfloat-equal).
+ const Expr *LeftExprSansParen = LHS->IgnoreParenImpCasts();
+ const Expr *RightExprSansParen = RHS->IgnoreParenImpCasts();
+
+ // Special case: check for x == x (which is OK).
+ // Do not emit warnings for such cases.
+ if (const auto *DRL = dyn_cast<DeclRefExpr>(LeftExprSansParen))
+ if (const auto *DRR = dyn_cast<DeclRefExpr>(RightExprSansParen))
+ if (DRL->getDecl() == DRR->getDecl())
+ return;
+
+ // Special case: check for comparisons against literals that can be exactly
+ // represented by APFloat. In such cases, do not emit a warning. This
+ // is a heuristic: often comparison against such literals are used to
+ // detect if a value in a variable has not changed. This clearly can
+ // lead to false negatives.
+ if (const auto *FLL = dyn_cast<FloatingLiteral>(LeftExprSansParen)) {
+ if (FLL->isExact())
+ return;
+ } else if (const auto *FLR = dyn_cast<FloatingLiteral>(RightExprSansParen))
+ if (FLR->isExact())
+ return;
+
+ // Check for comparisons with builtin types.
+ if (const auto *CL = dyn_cast<CallExpr>(LeftExprSansParen);
+ CL && CL->getBuiltinCallee())
+ return;
+
+ if (const auto *CR = dyn_cast<CallExpr>(RightExprSansParen);
+ CR && CR->getBuiltinCallee())
+ return;
+
+ // Emit the diagnostic.
+ Diag(Loc, diag::warn_floatingpoint_eq)
+ << LHS->getSourceRange() << RHS->getSourceRange();
+}
+
+//===--- CHECK: Integer mixed-sign comparisons (-Wsign-compare) --------===//
+//===--- CHECK: Lossy implicit conversions (-Wconversion) --------------===//
+
+namespace {
+
+/// Structure recording the 'active' range of an integer-valued
+/// expression.
+struct IntRange {
+ /// The number of bits active in the int. Note that this includes exactly one
+ /// sign bit if !NonNegative.
+ unsigned Width;
+
+ /// True if the int is known not to have negative values. If so, all leading
+ /// bits before Width are known zero, otherwise they are known to be the
+ /// same as the MSB within Width.
+ bool NonNegative;
+
+ IntRange(unsigned Width, bool NonNegative)
+ : Width(Width), NonNegative(NonNegative) {}
+
+ /// Number of bits excluding the sign bit.
+ unsigned valueBits() const {
+ return NonNegative ? Width : Width - 1;
+ }
+
+ /// Returns the range of the bool type.
+ static IntRange forBoolType() {
+ return IntRange(1, true);
+ }
+
+ /// Returns the range of an opaque value of the given integral type.
+ static IntRange forValueOfType(ASTContext &C, QualType T) {
+ return forValueOfCanonicalType(C,
+ T->getCanonicalTypeInternal().getTypePtr());
+ }
+
+ /// Returns the range of an opaque value of a canonical integral type.
+ static IntRange forValueOfCanonicalType(ASTContext &C, const Type *T) {
+ assert(T->isCanonicalUnqualified());
+
+ if (const auto *VT = dyn_cast<VectorType>(T))
+ T = VT->getElementType().getTypePtr();
+ if (const auto *CT = dyn_cast<ComplexType>(T))
+ T = CT->getElementType().getTypePtr();
+ if (const auto *AT = dyn_cast<AtomicType>(T))
+ T = AT->getValueType().getTypePtr();
+
+ if (!C.getLangOpts().CPlusPlus) {
+ // For enum types in C code, use the underlying datatype.
+ if (const auto *ED = T->getAsEnumDecl())
+ T = ED->getIntegerType().getDesugaredType(C).getTypePtr();
+ } else if (auto *Enum = T->getAsEnumDecl()) {
+ // For enum types in C++, use the known bit width of the enumerators.
+ // In C++11, enums can have a fixed underlying type. Use this type to
+ // compute the range.
+ if (Enum->isFixed()) {
+ return IntRange(C.getIntWidth(QualType(T, 0)),
+ !Enum->getIntegerType()->isSignedIntegerType());
+ }
+
+ unsigned NumPositive = Enum->getNumPositiveBits();
+ unsigned NumNegative = Enum->getNumNegativeBits();
+
+ if (NumNegative == 0)
+ return IntRange(NumPositive, true/*NonNegative*/);
+ else
+ return IntRange(std::max(NumPositive + 1, NumNegative),
+ false/*NonNegative*/);
+ }
+
+ if (const auto *EIT = dyn_cast<BitIntType>(T))
+ return IntRange(EIT->getNumBits(), EIT->isUnsigned());
+
+ const BuiltinType *BT = cast<BuiltinType>(T);
+ assert(BT->isInteger());
+
+ return IntRange(C.getIntWidth(QualType(T, 0)), BT->isUnsignedInteger());
+ }
+
+ /// Returns the "target" range of a canonical integral type, i.e.
+ /// the range of values expressible in the type.
+ ///
+ /// This matches forValueOfCanonicalType except that enums have the
+ /// full range of their type, not the range of their enumerators.
+ static IntRange forTargetOfCanonicalType(ASTContext &C, const Type *T) {
+ assert(T->isCanonicalUnqualified());
+
+ if (const VectorType *VT = dyn_cast<VectorType>(T))
+ T = VT->getElementType().getTypePtr();
+ if (const ComplexType *CT = dyn_cast<ComplexType>(T))
+ T = CT->getElementType().getTypePtr();
+ if (const AtomicType *AT = dyn_cast<AtomicType>(T))
+ T = AT->getValueType().getTypePtr();
+ if (const auto *ED = T->getAsEnumDecl())
+ T = C.getCanonicalType(ED->getIntegerType()).getTypePtr();
+
+ if (const auto *EIT = dyn_cast<BitIntType>(T))
+ return IntRange(EIT->getNumBits(), EIT->isUnsigned());
+
+ const BuiltinType *BT = cast<BuiltinType>(T);
+ assert(BT->isInteger());
+
+ return IntRange(C.getIntWidth(QualType(T, 0)), BT->isUnsignedInteger());
+ }
+
+ /// Returns the supremum of two ranges: i.e. their conservative merge.
+ static IntRange join(IntRange L, IntRange R) {
+ bool Unsigned = L.NonNegative && R.NonNegative;
+ return IntRange(std::max(L.valueBits(), R.valueBits()) + !Unsigned,
+ L.NonNegative && R.NonNegative);
+ }
+
+ /// Return the range of a bitwise-AND of the two ranges.
+ static IntRange bit_and(IntRange L, IntRange R) {
+ unsigned Bits = std::max(L.Width, R.Width);
+ bool NonNegative = false;
+ if (L.NonNegative) {
+ Bits = std::min(Bits, L.Width);
+ NonNegative = true;
+ }
+ if (R.NonNegative) {
+ Bits = std::min(Bits, R.Width);
+ NonNegative = true;
+ }
+ return IntRange(Bits, NonNegative);
+ }
+
+ /// Return the range of a sum of the two ranges.
+ static IntRange sum(IntRange L, IntRange R) {
+ bool Unsigned = L.NonNegative && R.NonNegative;
+ return IntRange(std::max(L.valueBits(), R.valueBits()) + 1 + !Unsigned,
+ Unsigned);
+ }
+
+ /// Return the range of a difference of the two ranges.
+ static IntRange difference(IntRange L, IntRange R) {
+ // We need a 1-bit-wider range if:
+ // 1) LHS can be negative: least value can be reduced.
+ // 2) RHS can be negative: greatest value can be increased.
+ bool CanWiden = !L.NonNegative || !R.NonNegative;
+ bool Unsigned = L.NonNegative && R.Width == 0;
+ return IntRange(std::max(L.valueBits(), R.valueBits()) + CanWiden +
+ !Unsigned,
+ Unsigned);
+ }
+
+ /// Return the range of a product of the two ranges.
+ static IntRange product(IntRange L, IntRange R) {
+ // If both LHS and RHS can be negative, we can form
+ // -2^L * -2^R = 2^(L + R)
+ // which requires L + R + 1 value bits to represent.
+ bool CanWiden = !L.NonNegative && !R.NonNegative;
+ bool Unsigned = L.NonNegative && R.NonNegative;
+ return IntRange(L.valueBits() + R.valueBits() + CanWiden + !Unsigned,
+ Unsigned);
+ }
+
+ /// Return the range of a remainder operation between the two ranges.
+ static IntRange rem(IntRange L, IntRange R) {
+ // The result of a remainder can't be larger than the result of
+ // either side. The sign of the result is the sign of the LHS.
+ bool Unsigned = L.NonNegative;
+ return IntRange(std::min(L.valueBits(), R.valueBits()) + !Unsigned,
+ Unsigned);
+ }
+};
+
+} // namespace
+
+static IntRange GetValueRange(llvm::APSInt &value, unsigned MaxWidth) {
+ if (value.isSigned() && value.isNegative())
+ return IntRange(value.getSignificantBits(), false);
+
+ if (value.getBitWidth() > MaxWidth)
+ value = value.trunc(MaxWidth);
+
+ // isNonNegative() just checks the sign bit without considering
+ // signedness.
+ return IntRange(value.getActiveBits(), true);
+}
+
+static IntRange GetValueRange(APValue &result, QualType Ty, unsigned MaxWidth) {
+ if (result.isInt())
+ return GetValueRange(result.getInt(), MaxWidth);
+
+ if (result.isVector()) {
+ IntRange R = GetValueRange(result.getVectorElt(0), Ty, MaxWidth);
+ for (unsigned i = 1, e = result.getVectorLength(); i != e; ++i) {
+ IntRange El = GetValueRange(result.getVectorElt(i), Ty, MaxWidth);
+ R = IntRange::join(R, El);
+ }
+ return R;
+ }
+
+ if (result.isComplexInt()) {
+ IntRange R = GetValueRange(result.getComplexIntReal(), MaxWidth);
+ IntRange I = GetValueRange(result.getComplexIntImag(), MaxWidth);
+ return IntRange::join(R, I);
+ }
+
+ // This can happen with lossless casts to intptr_t of "based" lvalues.
+ // Assume it might use arbitrary bits.
+ // FIXME: The only reason we need to pass the type in here is to get
+ // the sign right on this one case. It would be nice if APValue
+ // preserved this.
+ assert(result.isLValue() || result.isAddrLabelDiff());
+ return IntRange(MaxWidth, Ty->isUnsignedIntegerOrEnumerationType());
+}
+
+static QualType GetExprType(const Expr *E) {
+ QualType Ty = E->getType();
+ if (const auto *AtomicRHS = Ty->getAs<AtomicType>())
+ Ty = AtomicRHS->getValueType();
+ return Ty;
+}
+
+/// Attempts to estimate an approximate range for the given integer expression.
+/// Returns a range if successful, otherwise it returns \c std::nullopt if a
+/// reliable estimation cannot be determined.
+///
+/// \param MaxWidth The width to which the value will be truncated.
+/// \param InConstantContext If \c true, interpret the expression within a
+/// constant context.
+/// \param Approximate If \c true, provide a likely range of values by assuming
+/// that arithmetic on narrower types remains within those types.
+/// If \c false, return a range that includes all possible values
+/// resulting from the expression.
+/// \returns A range of values that the expression might take, or
+/// std::nullopt if a reliable estimation cannot be determined.
+static std::optional<IntRange> TryGetExprRange(ASTContext &C, const Expr *E,
+ unsigned MaxWidth,
+ bool InConstantContext,
+ bool Approximate) {
+ E = E->IgnoreParens();
+
+ // Try a full evaluation first.
+ Expr::EvalResult result;
+ if (E->EvaluateAsRValue(result, C, InConstantContext))
+ return GetValueRange(result.Val, GetExprType(E), MaxWidth);
+
+ // I think we only want to look through implicit casts here; if the
+ // user has an explicit widening cast, we should treat the value as
+ // being of the new, wider type.
+ if (const auto *CE = dyn_cast<ImplicitCastExpr>(E)) {
+ if (CE->getCastKind() == CK_NoOp || CE->getCastKind() == CK_LValueToRValue)
+ return TryGetExprRange(C, CE->getSubExpr(), MaxWidth, InConstantContext,
+ Approximate);
+
+ IntRange OutputTypeRange = IntRange::forValueOfType(C, GetExprType(CE));
+
+ bool isIntegerCast = CE->getCastKind() == CK_IntegralCast ||
+ CE->getCastKind() == CK_BooleanToSignedIntegral;
+
+ // Assume that non-integer casts can span the full range of the type.
+ if (!isIntegerCast)
+ return OutputTypeRange;
+
+ std::optional<IntRange> SubRange = TryGetExprRange(
+ C, CE->getSubExpr(), std::min(MaxWidth, OutputTypeRange.Width),
+ InConstantContext, Approximate);
+ if (!SubRange)
+ return std::nullopt;
+
+ // Bail out if the subexpr's range is as wide as the cast type.
+ if (SubRange->Width >= OutputTypeRange.Width)
+ return OutputTypeRange;
+
+ // Otherwise, we take the smaller width, and we're non-negative if
+ // either the output type or the subexpr is.
+ return IntRange(SubRange->Width,
+ SubRange->NonNegative || OutputTypeRange.NonNegative);
+ }
+
+ if (const auto *CO = dyn_cast<ConditionalOperator>(E)) {
+ // If we can fold the condition, just take that operand.
+ bool CondResult;
+ if (CO->getCond()->EvaluateAsBooleanCondition(CondResult, C))
+ return TryGetExprRange(
+ C, CondResult ? CO->getTrueExpr() : CO->getFalseExpr(), MaxWidth,
+ InConstantContext, Approximate);
+
+ // Otherwise, conservatively merge.
+ // TryGetExprRange requires an integer expression, but a throw expression
+ // results in a void type.
+ Expr *TrueExpr = CO->getTrueExpr();
+ if (TrueExpr->getType()->isVoidType())
+ return std::nullopt;
+
+ std::optional<IntRange> L =
+ TryGetExprRange(C, TrueExpr, MaxWidth, InConstantContext, Approximate);
+ if (!L)
+ return std::nullopt;
+
+ Expr *FalseExpr = CO->getFalseExpr();
+ if (FalseExpr->getType()->isVoidType())
+ return std::nullopt;
+
+ std::optional<IntRange> R =
+ TryGetExprRange(C, FalseExpr, MaxWidth, InConstantContext, Approximate);
+ if (!R)
+ return std::nullopt;
+
+ return IntRange::join(*L, *R);
+ }
+
+ if (const auto *BO = dyn_cast<BinaryOperator>(E)) {
+ IntRange (*Combine)(IntRange, IntRange) = IntRange::join;
+
+ switch (BO->getOpcode()) {
+ case BO_Cmp:
+ llvm_unreachable("builtin <=> should have class type");
+
+ // Boolean-valued operations are single-bit and positive.
+ case BO_LAnd:
+ case BO_LOr:
+ case BO_LT:
+ case BO_GT:
+ case BO_LE:
+ case BO_GE:
+ case BO_EQ:
+ case BO_NE:
+ return IntRange::forBoolType();
+
+ // The type of the assignments is the type of the LHS, so the RHS
+ // is not necessarily the same type.
+ case BO_MulAssign:
+ case BO_DivAssign:
+ case BO_RemAssign:
+ case BO_AddAssign:
+ case BO_SubAssign:
+ case BO_XorAssign:
+ case BO_OrAssign:
+ // TODO: bitfields?
+ return IntRange::forValueOfType(C, GetExprType(E));
+
+ // Simple assignments just pass through the RHS, which will have
+ // been coerced to the LHS type.
+ case BO_Assign:
+ // TODO: bitfields?
+ return TryGetExprRange(C, BO->getRHS(), MaxWidth, InConstantContext,
+ Approximate);
+
+ // Operations with opaque sources are black-listed.
+ case BO_PtrMemD:
+ case BO_PtrMemI:
+ return IntRange::forValueOfType(C, GetExprType(E));
+
+ // Bitwise-and uses the *infinum* of the two source ranges.
+ case BO_And:
+ case BO_AndAssign:
+ Combine = IntRange::bit_and;
+ break;
+
+ // Left shift gets black-listed based on a judgement call.
+ case BO_Shl:
+ // ...except that we want to treat '1 << (blah)' as logically
+ // positive. It's an important idiom.
+ if (IntegerLiteral *I
+ = dyn_cast<IntegerLiteral>(BO->getLHS()->IgnoreParenCasts())) {
+ if (I->getValue() == 1) {
+ IntRange R = IntRange::forValueOfType(C, GetExprType(E));
+ return IntRange(R.Width, /*NonNegative*/ true);
+ }
+ }
+ [[fallthrough]];
+
+ case BO_ShlAssign:
+ return IntRange::forValueOfType(C, GetExprType(E));
+
+ // Right shift by a constant can narrow its left argument.
+ case BO_Shr:
+ case BO_ShrAssign: {
+ std::optional<IntRange> L = TryGetExprRange(
+ C, BO->getLHS(), MaxWidth, InConstantContext, Approximate);
+ if (!L)
+ return std::nullopt;
+
+ // If the shift amount is a positive constant, drop the width by
+ // that much.
+ if (std::optional<llvm::APSInt> shift =
+ BO->getRHS()->getIntegerConstantExpr(C)) {
+ if (shift->isNonNegative()) {
+ if (shift->uge(L->Width))
+ L->Width = (L->NonNegative ? 0 : 1);
+ else
+ L->Width -= shift->getZExtValue();
+ }
+ }
+
+ return L;
+ }
+
+ // Comma acts as its right operand.
+ case BO_Comma:
+ return TryGetExprRange(C, BO->getRHS(), MaxWidth, InConstantContext,
+ Approximate);
+
+ case BO_Add:
+ if (!Approximate)
+ Combine = IntRange::sum;
+ break;
+
+ case BO_Sub:
+ if (BO->getLHS()->getType()->isPointerType())
+ return IntRange::forValueOfType(C, GetExprType(E));
+ if (!Approximate)
+ Combine = IntRange::difference;
+ break;
+
+ case BO_Mul:
+ if (!Approximate)
+ Combine = IntRange::product;
+ break;
+
+ // The width of a division result is mostly determined by the size
+ // of the LHS.
+ case BO_Div: {
+ // Don't 'pre-truncate' the operands.
+ unsigned opWidth = C.getIntWidth(GetExprType(E));
+ std::optional<IntRange> L = TryGetExprRange(
+ C, BO->getLHS(), opWidth, InConstantContext, Approximate);
+ if (!L)
+ return std::nullopt;
+
+ // If the divisor is constant, use that.
+ if (std::optional<llvm::APSInt> divisor =
+ BO->getRHS()->getIntegerConstantExpr(C)) {
+ unsigned log2 = divisor->logBase2(); // floor(log_2(divisor))
+ if (log2 >= L->Width)
+ L->Width = (L->NonNegative ? 0 : 1);
+ else
+ L->Width = std::min(L->Width - log2, MaxWidth);
+ return L;
+ }
+
+ // Otherwise, just use the LHS's width.
+ // FIXME: This is wrong if the LHS could be its minimal value and the RHS
+ // could be -1.
+ std::optional<IntRange> R = TryGetExprRange(
+ C, BO->getRHS(), opWidth, InConstantContext, Approximate);
+ if (!R)
+ return std::nullopt;
+
+ return IntRange(L->Width, L->NonNegative && R->NonNegative);
+ }
+
+ case BO_Rem:
+ Combine = IntRange::rem;
+ break;
+
+ // The default behavior is okay for these.
+ case BO_Xor:
+ case BO_Or:
+ break;
+ }
+
+ // Combine the two ranges, but limit the result to the type in which we
+ // performed the computation.
+ QualType T = GetExprType(E);
+ unsigned opWidth = C.getIntWidth(T);
+ std::optional<IntRange> L = TryGetExprRange(C, BO->getLHS(), opWidth,
+ InConstantContext, Approximate);
+ if (!L)
+ return std::nullopt;
+
+ std::optional<IntRange> R = TryGetExprRange(C, BO->getRHS(), opWidth,
+ InConstantContext, Approximate);
+ if (!R)
+ return std::nullopt;
+
+ IntRange C = Combine(*L, *R);
+ C.NonNegative |= T->isUnsignedIntegerOrEnumerationType();
+ C.Width = std::min(C.Width, MaxWidth);
+ return C;
+ }
+
+ if (const auto *UO = dyn_cast<UnaryOperator>(E)) {
+ switch (UO->getOpcode()) {
+ // Boolean-valued operations are white-listed.
+ case UO_LNot:
+ return IntRange::forBoolType();
+
+ // Operations with opaque sources are black-listed.
+ case UO_Deref:
+ case UO_AddrOf: // should be impossible
+ return IntRange::forValueOfType(C, GetExprType(E));
+
+ case UO_Minus: {
+ if (E->getType()->isUnsignedIntegerType()) {
+ return TryGetExprRange(C, UO->getSubExpr(), MaxWidth, InConstantContext,
+ Approximate);
+ }
+
+ std::optional<IntRange> SubRange = TryGetExprRange(
+ C, UO->getSubExpr(), MaxWidth, InConstantContext, Approximate);
+
+ if (!SubRange)
+ return std::nullopt;
+
+ // If the range was previously non-negative, we need an extra bit for the
+ // sign bit. Otherwise, we need an extra bit because the negation of the
+ // most-negative value is one bit wider than that value.
+ return IntRange(std::min(SubRange->Width + 1, MaxWidth), false);
+ }
+
+ case UO_Not: {
+ if (E->getType()->isUnsignedIntegerType()) {
+ return TryGetExprRange(C, UO->getSubExpr(), MaxWidth, InConstantContext,
+ Approximate);
+ }
+
+ std::optional<IntRange> SubRange = TryGetExprRange(
+ C, UO->getSubExpr(), MaxWidth, InConstantContext, Approximate);
+
+ if (!SubRange)
+ return std::nullopt;
+
+ // The width increments by 1 if the sub-expression cannot be negative
+ // since it now can be.
+ return IntRange(
+ std::min(SubRange->Width + (int)SubRange->NonNegative, MaxWidth),
+ false);
+ }
+
+ default:
+ return TryGetExprRange(C, UO->getSubExpr(), MaxWidth, InConstantContext,
+ Approximate);
+ }
+ }
+
+ if (const auto *OVE = dyn_cast<OpaqueValueExpr>(E))
+ return TryGetExprRange(C, OVE->getSourceExpr(), MaxWidth, InConstantContext,
+ Approximate);
+
+ if (const auto *BitField = E->getSourceBitField())
+ return IntRange(BitField->getBitWidthValue(),
+ BitField->getType()->isUnsignedIntegerOrEnumerationType());
+
+ if (GetExprType(E)->isVoidType())
+ return std::nullopt;
+
+ return IntRange::forValueOfType(C, GetExprType(E));
+}
+
+static std::optional<IntRange> TryGetExprRange(ASTContext &C, const Expr *E,
+ bool InConstantContext,
+ bool Approximate) {
+ return TryGetExprRange(C, E, C.getIntWidth(GetExprType(E)), InConstantContext,
+ Approximate);
+}
+
+/// Checks whether the given value, which currently has the given
+/// source semantics, has the same value when coerced through the
+/// target semantics.
+static bool IsSameFloatAfterCast(const llvm::APFloat &value,
+ const llvm::fltSemantics &Src,
+ const llvm::fltSemantics &Tgt) {
+ llvm::APFloat truncated = value;
+
+ bool ignored;
+ truncated.convert(Src, llvm::APFloat::rmNearestTiesToEven, &ignored);
+ truncated.convert(Tgt, llvm::APFloat::rmNearestTiesToEven, &ignored);
+
+ return truncated.bitwiseIsEqual(value);
+}
+
+/// Checks whether the given value, which currently has the given
+/// source semantics, has the same value when coerced through the
+/// target semantics.
+///
+/// The value might be a vector of floats (or a complex number).
+static bool IsSameFloatAfterCast(const APValue &value,
+ const llvm::fltSemantics &Src,
+ const llvm::fltSemantics &Tgt) {
+ if (value.isFloat())
+ return IsSameFloatAfterCast(value.getFloat(), Src, Tgt);
+
+ if (value.isVector()) {
+ for (unsigned i = 0, e = value.getVectorLength(); i != e; ++i)
+ if (!IsSameFloatAfterCast(value.getVectorElt(i), Src, Tgt))
+ return false;
+ return true;
+ }
+
+ assert(value.isComplexFloat());
+ return (IsSameFloatAfterCast(value.getComplexFloatReal(), Src, Tgt) &&
+ IsSameFloatAfterCast(value.getComplexFloatImag(), Src, Tgt));
+}
+
+static void AnalyzeImplicitConversions(Sema &S, Expr *E, SourceLocation CC,
+ bool IsListInit = false);
+
+static bool IsEnumConstOrFromMacro(Sema &S, const Expr *E) {
+ // Suppress cases where we are comparing against an enum constant.
+ if (const auto *DR = dyn_cast<DeclRefExpr>(E->IgnoreParenImpCasts()))
+ if (isa<EnumConstantDecl>(DR->getDecl()))
+ return true;
+
+ // Suppress cases where the value is expanded from a macro, unless that macro
+ // is how a language represents a boolean literal. This is the case in both C
+ // and Objective-C.
+ SourceLocation BeginLoc = E->getBeginLoc();
+ if (BeginLoc.isMacroID()) {
+ StringRef MacroName = Lexer::getImmediateMacroName(
+ BeginLoc, S.getSourceManager(), S.getLangOpts());
+ return MacroName != "YES" && MacroName != "NO" &&
+ MacroName != "true" && MacroName != "false";
+ }
+
+ return false;
+}
+
+static bool isKnownToHaveUnsignedValue(const Expr *E) {
+ return E->getType()->isIntegerType() &&
+ (!E->getType()->isSignedIntegerType() ||
+ !E->IgnoreParenImpCasts()->getType()->isSignedIntegerType());
+}
+
+namespace {
+/// The promoted range of values of a type. In general this has the
+/// following structure:
+///
+/// |-----------| . . . |-----------|
+/// ^ ^ ^ ^
+/// Min HoleMin HoleMax Max
+///
+/// ... where there is only a hole if a signed type is promoted to unsigned
+/// (in which case Min and Max are the smallest and largest representable
+/// values).
+struct PromotedRange {
+ // Min, or HoleMax if there is a hole.
+ llvm::APSInt PromotedMin;
+ // Max, or HoleMin if there is a hole.
+ llvm::APSInt PromotedMax;
+
+ PromotedRange(IntRange R, unsigned BitWidth, bool Unsigned) {
+ if (R.Width == 0)
+ PromotedMin = PromotedMax = llvm::APSInt(BitWidth, Unsigned);
+ else if (R.Width >= BitWidth && !Unsigned) {
+ // Promotion made the type *narrower*. This happens when promoting
+ // a < 32-bit unsigned / <= 32-bit signed bit-field to 'signed int'.
+ // Treat all values of 'signed int' as being in range for now.
+ PromotedMin = llvm::APSInt::getMinValue(BitWidth, Unsigned);
+ PromotedMax = llvm::APSInt::getMaxValue(BitWidth, Unsigned);
+ } else {
+ PromotedMin = llvm::APSInt::getMinValue(R.Width, R.NonNegative)
+ .extOrTrunc(BitWidth);
+ PromotedMin.setIsUnsigned(Unsigned);
+
+ PromotedMax = llvm::APSInt::getMaxValue(R.Width, R.NonNegative)
+ .extOrTrunc(BitWidth);
+ PromotedMax.setIsUnsigned(Unsigned);
+ }
+ }
+
+ // Determine whether this range is contiguous (has no hole).
+ bool isContiguous() const { return PromotedMin <= PromotedMax; }
+
+ // Where a constant value is within the range.
+ enum ComparisonResult {
+ LT = 0x1,
+ LE = 0x2,
+ GT = 0x4,
+ GE = 0x8,
+ EQ = 0x10,
+ NE = 0x20,
+ InRangeFlag = 0x40,
+
+ Less = LE | LT | NE,
+ Min = LE | InRangeFlag,
+ InRange = InRangeFlag,
+ Max = GE | InRangeFlag,
+ Greater = GE | GT | NE,
+
+ OnlyValue = LE | GE | EQ | InRangeFlag,
+ InHole = NE
+ };
+
+ ComparisonResult compare(const llvm::APSInt &Value) const {
+ assert(Value.getBitWidth() == PromotedMin.getBitWidth() &&
+ Value.isUnsigned() == PromotedMin.isUnsigned());
+ if (!isContiguous()) {
+ assert(Value.isUnsigned() && "discontiguous range for signed compare");
+ if (Value.isMinValue()) return Min;
+ if (Value.isMaxValue()) return Max;
+ if (Value >= PromotedMin) return InRange;
+ if (Value <= PromotedMax) return InRange;
+ return InHole;
+ }
+
+ switch (llvm::APSInt::compareValues(Value, PromotedMin)) {
+ case -1: return Less;
+ case 0: return PromotedMin == PromotedMax ? OnlyValue : Min;
+ case 1:
+ switch (llvm::APSInt::compareValues(Value, PromotedMax)) {
+ case -1: return InRange;
+ case 0: return Max;
+ case 1: return Greater;
+ }
+ }
+
+ llvm_unreachable("impossible compare result");
+ }
+
+ static std::optional<StringRef>
+ constantValue(BinaryOperatorKind Op, ComparisonResult R, bool ConstantOnRHS) {
+ if (Op == BO_Cmp) {
+ ComparisonResult LTFlag = LT, GTFlag = GT;
+ if (ConstantOnRHS) std::swap(LTFlag, GTFlag);
+
+ if (R & EQ) return StringRef("'std::strong_ordering::equal'");
+ if (R & LTFlag) return StringRef("'std::strong_ordering::less'");
+ if (R & GTFlag) return StringRef("'std::strong_ordering::greater'");
+ return std::nullopt;
+ }
+
+ ComparisonResult TrueFlag, FalseFlag;
+ if (Op == BO_EQ) {
+ TrueFlag = EQ;
+ FalseFlag = NE;
+ } else if (Op == BO_NE) {
+ TrueFlag = NE;
+ FalseFlag = EQ;
+ } else {
+ if ((Op == BO_LT || Op == BO_GE) ^ ConstantOnRHS) {
+ TrueFlag = LT;
+ FalseFlag = GE;
+ } else {
+ TrueFlag = GT;
+ FalseFlag = LE;
+ }
+ if (Op == BO_GE || Op == BO_LE)
+ std::swap(TrueFlag, FalseFlag);
+ }
+ if (R & TrueFlag)
+ return StringRef("true");
+ if (R & FalseFlag)
+ return StringRef("false");
+ return std::nullopt;
+ }
+};
+}
+
+static bool HasEnumType(const Expr *E) {
+ // Strip off implicit integral promotions.
+ while (const auto *ICE = dyn_cast<ImplicitCastExpr>(E)) {
+ if (ICE->getCastKind() != CK_IntegralCast &&
+ ICE->getCastKind() != CK_NoOp)
+ break;
+ E = ICE->getSubExpr();
+ }
+
+ return E->getType()->isEnumeralType();
+}
+
+static int classifyConstantValue(Expr *Constant) {
+ // The values of this enumeration are used in the diagnostics
+ // diag::warn_out_of_range_compare and diag::warn_tautological_bool_compare.
+ enum ConstantValueKind {
+ Miscellaneous = 0,
+ LiteralTrue,
+ LiteralFalse
+ };
+ if (auto *BL = dyn_cast<CXXBoolLiteralExpr>(Constant))
+ return BL->getValue() ? ConstantValueKind::LiteralTrue
+ : ConstantValueKind::LiteralFalse;
+ return ConstantValueKind::Miscellaneous;
+}
+
+static bool CheckTautologicalComparison(Sema &S, BinaryOperator *E,
+ Expr *Constant, Expr *Other,
+ const llvm::APSInt &Value,
+ bool RhsConstant) {
+ if (S.inTemplateInstantiation())
+ return false;
+
+ Expr *OriginalOther = Other;
+
+ Constant = Constant->IgnoreParenImpCasts();
+ Other = Other->IgnoreParenImpCasts();
+
+ // Suppress warnings on tautological comparisons between values of the same
+ // enumeration type. There are only two ways we could warn on this:
+ // - If the constant is outside the range of representable values of
+ // the enumeration. In such a case, we should warn about the cast
+ // to enumeration type, not about the comparison.
+ // - If the constant is the maximum / minimum in-range value. For an
+ // enumeratin type, such comparisons can be meaningful and useful.
+ if (Constant->getType()->isEnumeralType() &&
+ S.Context.hasSameUnqualifiedType(Constant->getType(), Other->getType()))
+ return false;
+
+ std::optional<IntRange> OtherValueRange = TryGetExprRange(
+ S.Context, Other, S.isConstantEvaluatedContext(), /*Approximate=*/false);
+ if (!OtherValueRange)
+ return false;
+
+ QualType OtherT = Other->getType();
+ if (const auto *AT = OtherT->getAs<AtomicType>())
+ OtherT = AT->getValueType();
+ IntRange OtherTypeRange = IntRange::forValueOfType(S.Context, OtherT);
+
+ // Special case for ObjC BOOL on targets where its a typedef for a signed char
+ // (Namely, macOS). FIXME: IntRange::forValueOfType should do this.
+ bool IsObjCSignedCharBool = S.getLangOpts().ObjC &&
+ S.ObjC().NSAPIObj->isObjCBOOLType(OtherT) &&
+ OtherT->isSpecificBuiltinType(BuiltinType::SChar);
+
+ // Whether we're treating Other as being a bool because of the form of
+ // expression despite it having another type (typically 'int' in C).
+ bool OtherIsBooleanDespiteType =
+ !OtherT->isBooleanType() && Other->isKnownToHaveBooleanValue();
+ if (OtherIsBooleanDespiteType || IsObjCSignedCharBool)
+ OtherTypeRange = *OtherValueRange = IntRange::forBoolType();
+
+ // Check if all values in the range of possible values of this expression
+ // lead to the same comparison outcome.
+ PromotedRange OtherPromotedValueRange(*OtherValueRange, Value.getBitWidth(),
+ Value.isUnsigned());
+ auto Cmp = OtherPromotedValueRange.compare(Value);
+ auto Result = PromotedRange::constantValue(E->getOpcode(), Cmp, RhsConstant);
+ if (!Result)
+ return false;
+
+ // Also consider the range determined by the type alone. This allows us to
+ // classify the warning under the proper diagnostic group.
+ bool TautologicalTypeCompare = false;
+ {
+ PromotedRange OtherPromotedTypeRange(OtherTypeRange, Value.getBitWidth(),
+ Value.isUnsigned());
+ auto TypeCmp = OtherPromotedTypeRange.compare(Value);
+ if (auto TypeResult = PromotedRange::constantValue(E->getOpcode(), TypeCmp,
+ RhsConstant)) {
+ TautologicalTypeCompare = true;
+ Cmp = TypeCmp;
+ Result = TypeResult;
+ }
+ }
+
+ // Don't warn if the non-constant operand actually always evaluates to the
+ // same value.
+ if (!TautologicalTypeCompare && OtherValueRange->Width == 0)
+ return false;
+
+ // Suppress the diagnostic for an in-range comparison if the constant comes
+ // from a macro or enumerator. We don't want to diagnose
+ //
+ // some_long_value <= INT_MAX
+ //
+ // when sizeof(int) == sizeof(long).
+ bool InRange = Cmp & PromotedRange::InRangeFlag;
+ if (InRange && IsEnumConstOrFromMacro(S, Constant))
+ return false;
+
+ // A comparison of an unsigned bit-field against 0 is really a type problem,
+ // even though at the type level the bit-field might promote to 'signed int'.
+ if (Other->refersToBitField() && InRange && Value == 0 &&
+ Other->getType()->isUnsignedIntegerOrEnumerationType())
+ TautologicalTypeCompare = true;
+
+ // If this is a comparison to an enum constant, include that
+ // constant in the diagnostic.
+ const EnumConstantDecl *ED = nullptr;
+ if (const auto *DR = dyn_cast<DeclRefExpr>(Constant))
+ ED = dyn_cast<EnumConstantDecl>(DR->getDecl());
+
+ // Should be enough for uint128 (39 decimal digits)
+ SmallString<64> PrettySourceValue;
+ llvm::raw_svector_ostream OS(PrettySourceValue);
+ if (ED) {
+ OS << '\'' << *ED << "' (" << Value << ")";
+ } else if (auto *BL = dyn_cast<ObjCBoolLiteralExpr>(
+ Constant->IgnoreParenImpCasts())) {
+ OS << (BL->getValue() ? "YES" : "NO");
+ } else {
+ OS << Value;
+ }
+
+ if (!TautologicalTypeCompare) {
+ S.Diag(E->getOperatorLoc(), diag::warn_tautological_compare_value_range)
+ << RhsConstant << OtherValueRange->Width << OtherValueRange->NonNegative
+ << E->getOpcodeStr() << OS.str() << *Result
+ << E->getLHS()->getSourceRange() << E->getRHS()->getSourceRange();
+ return true;
+ }
+
+ if (IsObjCSignedCharBool) {
+ S.DiagRuntimeBehavior(E->getOperatorLoc(), E,
+ S.PDiag(diag::warn_tautological_compare_objc_bool)
+ << OS.str() << *Result);
+ return true;
+ }
+
+ // FIXME: We use a somewhat different formatting for the in-range cases and
+ // cases involving boolean values for historical reasons. We should pick a
+ // consistent way of presenting these diagnostics.
+ if (!InRange || Other->isKnownToHaveBooleanValue()) {
+
+ S.DiagRuntimeBehavior(
+ E->getOperatorLoc(), E,
+ S.PDiag(!InRange ? diag::warn_out_of_range_compare
+ : diag::warn_tautological_bool_compare)
+ << OS.str() << classifyConstantValue(Constant) << OtherT
+ << OtherIsBooleanDespiteType << *Result
+ << E->getLHS()->getSourceRange() << E->getRHS()->getSourceRange());
+ } else {
+ bool IsCharTy = OtherT.withoutLocalFastQualifiers() == S.Context.CharTy;
+ unsigned Diag =
+ (isKnownToHaveUnsignedValue(OriginalOther) && Value == 0)
+ ? (HasEnumType(OriginalOther)
+ ? diag::warn_unsigned_enum_always_true_comparison
+ : IsCharTy ? diag::warn_unsigned_char_always_true_comparison
+ : diag::warn_unsigned_always_true_comparison)
+ : diag::warn_tautological_constant_compare;
+
+ S.Diag(E->getOperatorLoc(), Diag)
+ << RhsConstant << OtherT << E->getOpcodeStr() << OS.str() << *Result
+ << E->getLHS()->getSourceRange() << E->getRHS()->getSourceRange();
+ }
+
+ return true;
+}
+
+/// Analyze the operands of the given comparison. Implements the
+/// fallback case from AnalyzeComparison.
+static void AnalyzeImpConvsInComparison(Sema &S, BinaryOperator *E) {
+ AnalyzeImplicitConversions(S, E->getLHS(), E->getOperatorLoc());
+ AnalyzeImplicitConversions(S, E->getRHS(), E->getOperatorLoc());
+}
+
+/// Implements -Wsign-compare.
+///
+/// \param E the binary operator to check for warnings
+static void AnalyzeComparison(Sema &S, BinaryOperator *E) {
+ // The type the comparison is being performed in.
+ QualType T = E->getLHS()->getType();
+
+ // Only analyze comparison operators where both sides have been converted to
+ // the same type.
+ if (!S.Context.hasSameUnqualifiedType(T, E->getRHS()->getType()))
+ return AnalyzeImpConvsInComparison(S, E);
+
+ // Don't analyze value-dependent comparisons directly.
+ if (E->isValueDependent())
+ return AnalyzeImpConvsInComparison(S, E);
+
+ Expr *LHS = E->getLHS();
+ Expr *RHS = E->getRHS();
+
+ if (T->isIntegralType(S.Context)) {
+ std::optional<llvm::APSInt> RHSValue =
+ RHS->getIntegerConstantExpr(S.Context);
+ std::optional<llvm::APSInt> LHSValue =
+ LHS->getIntegerConstantExpr(S.Context);
+
+ // We don't care about expressions whose result is a constant.
+ if (RHSValue && LHSValue)
+ return AnalyzeImpConvsInComparison(S, E);
+
+ // We only care about expressions where just one side is literal
+ if ((bool)RHSValue ^ (bool)LHSValue) {
+ // Is the constant on the RHS or LHS?
+ const bool RhsConstant = (bool)RHSValue;
+ Expr *Const = RhsConstant ? RHS : LHS;
+ Expr *Other = RhsConstant ? LHS : RHS;
+ const llvm::APSInt &Value = RhsConstant ? *RHSValue : *LHSValue;
+
+ // Check whether an integer constant comparison results in a value
+ // of 'true' or 'false'.
+ if (CheckTautologicalComparison(S, E, Const, Other, Value, RhsConstant))
+ return AnalyzeImpConvsInComparison(S, E);
+ }
+ }
+
+ if (!T->hasUnsignedIntegerRepresentation()) {
+ // We don't do anything special if this isn't an unsigned integral
+ // comparison: we're only interested in integral comparisons, and
+ // signed comparisons only happen in cases we don't care to warn about.
+ return AnalyzeImpConvsInComparison(S, E);
+ }
+
+ LHS = LHS->IgnoreParenImpCasts();
+ RHS = RHS->IgnoreParenImpCasts();
+
+ if (!S.getLangOpts().CPlusPlus) {
+ // Avoid warning about comparison of integers with different signs when
+ // RHS/LHS has a `typeof(E)` type whose sign is different from the sign of
+ // the type of `E`.
+ if (const auto *TET = dyn_cast<TypeOfExprType>(LHS->getType()))
+ LHS = TET->getUnderlyingExpr()->IgnoreParenImpCasts();
+ if (const auto *TET = dyn_cast<TypeOfExprType>(RHS->getType()))
+ RHS = TET->getUnderlyingExpr()->IgnoreParenImpCasts();
+ }
+
+ // Check to see if one of the (unmodified) operands is of different
+ // signedness.
+ Expr *signedOperand, *unsignedOperand;
+ if (LHS->getType()->hasSignedIntegerRepresentation()) {
+ assert(!RHS->getType()->hasSignedIntegerRepresentation() &&
+ "unsigned comparison between two signed integer expressions?");
+ signedOperand = LHS;
+ unsignedOperand = RHS;
+ } else if (RHS->getType()->hasSignedIntegerRepresentation()) {
+ signedOperand = RHS;
+ unsignedOperand = LHS;
+ } else {
+ return AnalyzeImpConvsInComparison(S, E);
+ }
+
+ // Otherwise, calculate the effective range of the signed operand.
+ std::optional<IntRange> signedRange =
+ TryGetExprRange(S.Context, signedOperand, S.isConstantEvaluatedContext(),
+ /*Approximate=*/true);
+ if (!signedRange)
+ return;
+
+ // Go ahead and analyze implicit conversions in the operands. Note
+ // that we skip the implicit conversions on both sides.
+ AnalyzeImplicitConversions(S, LHS, E->getOperatorLoc());
+ AnalyzeImplicitConversions(S, RHS, E->getOperatorLoc());
+
+ // If the signed range is non-negative, -Wsign-compare won't fire.
+ if (signedRange->NonNegative)
+ return;
+
+ // For (in)equality comparisons, if the unsigned operand is a
+ // constant which cannot collide with a overflowed signed operand,
+ // then reinterpreting the signed operand as unsigned will not
+ // change the result of the comparison.
+ if (E->isEqualityOp()) {
+ unsigned comparisonWidth = S.Context.getIntWidth(T);
+ std::optional<IntRange> unsignedRange = TryGetExprRange(
+ S.Context, unsignedOperand, S.isConstantEvaluatedContext(),
+ /*Approximate=*/true);
+ if (!unsignedRange)
+ return;
+
+ // We should never be unable to prove that the unsigned operand is
+ // non-negative.
+ assert(unsignedRange->NonNegative && "unsigned range includes negative?");
+
+ if (unsignedRange->Width < comparisonWidth)
+ return;
+ }
+
+ S.DiagRuntimeBehavior(E->getOperatorLoc(), E,
+ S.PDiag(diag::warn_mixed_sign_comparison)
+ << LHS->getType() << RHS->getType()
+ << LHS->getSourceRange() << RHS->getSourceRange());
+}
+
+/// Analyzes an attempt to assign the given value to a bitfield.
+///
+/// Returns true if there was something fishy about the attempt.
+static bool AnalyzeBitFieldAssignment(Sema &S, FieldDecl *Bitfield, Expr *Init,
+ SourceLocation InitLoc) {
+ assert(Bitfield->isBitField());
+ if (Bitfield->isInvalidDecl())
+ return false;
+
+ // White-list bool bitfields.
+ QualType BitfieldType = Bitfield->getType();
+ if (BitfieldType->isBooleanType())
+ return false;
+
+ if (auto *BitfieldEnumDecl = BitfieldType->getAsEnumDecl()) {
+ // If the underlying enum type was not explicitly specified as an unsigned
+ // type and the enum contain only positive values, MSVC++ will cause an
+ // inconsistency by storing this as a signed type.
+ if (S.getLangOpts().CPlusPlus11 &&
+ !BitfieldEnumDecl->getIntegerTypeSourceInfo() &&
+ BitfieldEnumDecl->getNumPositiveBits() > 0 &&
+ BitfieldEnumDecl->getNumNegativeBits() == 0) {
+ S.Diag(InitLoc, diag::warn_no_underlying_type_specified_for_enum_bitfield)
+ << BitfieldEnumDecl;
+ }
+ }
+
+ // Ignore value- or type-dependent expressions.
+ if (Bitfield->getBitWidth()->isValueDependent() ||
+ Bitfield->getBitWidth()->isTypeDependent() ||
+ Init->isValueDependent() ||
+ Init->isTypeDependent())
+ return false;
+
+ Expr *OriginalInit = Init->IgnoreParenImpCasts();
+ unsigned FieldWidth = Bitfield->getBitWidthValue();
+
+ Expr::EvalResult Result;
+ if (!OriginalInit->EvaluateAsInt(Result, S.Context,
+ Expr::SE_AllowSideEffects)) {
+ // The RHS is not constant. If the RHS has an enum type, make sure the
+ // bitfield is wide enough to hold all the values of the enum without
+ // truncation.
+ const auto *ED = OriginalInit->getType()->getAsEnumDecl();
+ const PreferredTypeAttr *PTAttr = nullptr;
+ if (!ED) {
+ PTAttr = Bitfield->getAttr<PreferredTypeAttr>();
+ if (PTAttr)
+ ED = PTAttr->getType()->getAsEnumDecl();
+ }
+ if (ED) {
+ bool SignedBitfield = BitfieldType->isSignedIntegerOrEnumerationType();
+
+ // Enum types are implicitly signed on Windows, so check if there are any
+ // negative enumerators to see if the enum was intended to be signed or
+ // not.
+ bool SignedEnum = ED->getNumNegativeBits() > 0;
+
+ // Check for surprising sign changes when assigning enum values to a
+ // bitfield of different signedness. If the bitfield is signed and we
+ // have exactly the right number of bits to store this unsigned enum,
+ // suggest changing the enum to an unsigned type. This typically happens
+ // on Windows where unfixed enums always use an underlying type of 'int'.
+ unsigned DiagID = 0;
+ if (SignedEnum && !SignedBitfield) {
+ DiagID =
+ PTAttr == nullptr
+ ? diag::warn_unsigned_bitfield_assigned_signed_enum
+ : diag::
+ warn_preferred_type_unsigned_bitfield_assigned_signed_enum;
+ } else if (SignedBitfield && !SignedEnum &&
+ ED->getNumPositiveBits() == FieldWidth) {
+ DiagID =
+ PTAttr == nullptr
+ ? diag::warn_signed_bitfield_enum_conversion
+ : diag::warn_preferred_type_signed_bitfield_enum_conversion;
+ }
+ if (DiagID) {
+ S.Diag(InitLoc, DiagID) << Bitfield << ED;
+ TypeSourceInfo *TSI = Bitfield->getTypeSourceInfo();
+ SourceRange TypeRange =
+ TSI ? TSI->getTypeLoc().getSourceRange() : SourceRange();
+ S.Diag(Bitfield->getTypeSpecStartLoc(), diag::note_change_bitfield_sign)
+ << SignedEnum << TypeRange;
+ if (PTAttr)
+ S.Diag(PTAttr->getLocation(), diag::note_bitfield_preferred_type)
+ << ED;
+ }
+
+ // Compute the required bitwidth. If the enum has negative values, we need
+ // one more bit than the normal number of positive bits to represent the
+ // sign bit.
+ unsigned BitsNeeded = SignedEnum ? std::max(ED->getNumPositiveBits() + 1,
+ ED->getNumNegativeBits())
+ : ED->getNumPositiveBits();
+
+ // Check the bitwidth.
+ if (BitsNeeded > FieldWidth) {
+ Expr *WidthExpr = Bitfield->getBitWidth();
+ auto DiagID =
+ PTAttr == nullptr
+ ? diag::warn_bitfield_too_small_for_enum
+ : diag::warn_preferred_type_bitfield_too_small_for_enum;
+ S.Diag(InitLoc, DiagID) << Bitfield << ED;
+ S.Diag(WidthExpr->getExprLoc(), diag::note_widen_bitfield)
+ << BitsNeeded << ED << WidthExpr->getSourceRange();
+ if (PTAttr)
+ S.Diag(PTAttr->getLocation(), diag::note_bitfield_preferred_type)
+ << ED;
+ }
+ }
+
+ return false;
+ }
+
+ llvm::APSInt Value = Result.Val.getInt();
+
+ unsigned OriginalWidth = Value.getBitWidth();
+
+ // In C, the macro 'true' from stdbool.h will evaluate to '1'; To reduce
+ // false positives where the user is demonstrating they intend to use the
+ // bit-field as a Boolean, check to see if the value is 1 and we're assigning
+ // to a one-bit bit-field to see if the value came from a macro named 'true'.
+ bool OneAssignedToOneBitBitfield = FieldWidth == 1 && Value == 1;
+ if (OneAssignedToOneBitBitfield && !S.LangOpts.CPlusPlus) {
+ SourceLocation MaybeMacroLoc = OriginalInit->getBeginLoc();
+ if (S.SourceMgr.isInSystemMacro(MaybeMacroLoc) &&
+ S.findMacroSpelling(MaybeMacroLoc, "true"))
+ return false;
+ }
+
+ if (!Value.isSigned() || Value.isNegative())
+ if (UnaryOperator *UO = dyn_cast<UnaryOperator>(OriginalInit))
+ if (UO->getOpcode() == UO_Minus || UO->getOpcode() == UO_Not)
+ OriginalWidth = Value.getSignificantBits();
+
+ if (OriginalWidth <= FieldWidth)
+ return false;
+
+ // Compute the value which the bitfield will contain.
+ llvm::APSInt TruncatedValue = Value.trunc(FieldWidth);
+ TruncatedValue.setIsSigned(BitfieldType->isSignedIntegerType());
+
+ // Check whether the stored value is equal to the original value.
+ TruncatedValue = TruncatedValue.extend(OriginalWidth);
+ if (llvm::APSInt::isSameValue(Value, TruncatedValue))
+ return false;
+
+ std::string PrettyValue = toString(Value, 10);
+ std::string PrettyTrunc = toString(TruncatedValue, 10);
+
+ S.Diag(InitLoc, OneAssignedToOneBitBitfield
+ ? diag::warn_impcast_single_bit_bitield_precision_constant
+ : diag::warn_impcast_bitfield_precision_constant)
+ << PrettyValue << PrettyTrunc << OriginalInit->getType()
+ << Init->getSourceRange();
+
+ return true;
+}
+
+/// Analyze the given simple or compound assignment for warning-worthy
+/// operations.
+static void AnalyzeAssignment(Sema &S, BinaryOperator *E) {
+ // Just recurse on the LHS.
+ AnalyzeImplicitConversions(S, E->getLHS(), E->getOperatorLoc());
+
+ // We want to recurse on the RHS as normal unless we're assigning to
+ // a bitfield.
+ if (FieldDecl *Bitfield = E->getLHS()->getSourceBitField()) {
+ if (AnalyzeBitFieldAssignment(S, Bitfield, E->getRHS(),
+ E->getOperatorLoc())) {
+ // Recurse, ignoring any implicit conversions on the RHS.
+ return AnalyzeImplicitConversions(S, E->getRHS()->IgnoreParenImpCasts(),
+ E->getOperatorLoc());
+ }
+ }
+
+ AnalyzeImplicitConversions(S, E->getRHS(), E->getOperatorLoc());
+
+ // Diagnose implicitly sequentially-consistent atomic assignment.
+ if (E->getLHS()->getType()->isAtomicType())
+ S.Diag(E->getRHS()->getBeginLoc(), diag::warn_atomic_implicit_seq_cst);
+}
+
+/// Diagnose an implicit cast; purely a helper for CheckImplicitConversion.
+static void DiagnoseImpCast(Sema &S, const Expr *E, QualType SourceType,
+ QualType T, SourceLocation CContext, unsigned diag,
+ bool PruneControlFlow = false) {
+ // For languages like HLSL and OpenCL, implicit conversion diagnostics listing
+ // address space annotations isn't really useful. The warnings aren't because
+ // you're converting a `private int` to `unsigned int`, it is because you're
+ // conerting `int` to `unsigned int`.
+ if (SourceType.hasAddressSpace())
+ SourceType = S.getASTContext().removeAddrSpaceQualType(SourceType);
+ if (T.hasAddressSpace())
+ T = S.getASTContext().removeAddrSpaceQualType(T);
+ if (PruneControlFlow) {
+ S.DiagRuntimeBehavior(E->getExprLoc(), E,
+ S.PDiag(diag)
+ << SourceType << T << E->getSourceRange()
+ << SourceRange(CContext));
+ return;
+ }
+ S.Diag(E->getExprLoc(), diag)
+ << SourceType << T << E->getSourceRange() << SourceRange(CContext);
+}
+
+/// Diagnose an implicit cast; purely a helper for CheckImplicitConversion.
+static void DiagnoseImpCast(Sema &S, const Expr *E, QualType T,
+ SourceLocation CContext, unsigned diag,
+ bool PruneControlFlow = false) {
+ DiagnoseImpCast(S, E, E->getType(), T, CContext, diag, PruneControlFlow);
+}
+
+/// Diagnose an implicit cast from a floating point value to an integer value.
+static void DiagnoseFloatingImpCast(Sema &S, const Expr *E, QualType T,
+ SourceLocation CContext) {
+ bool IsBool = T->isSpecificBuiltinType(BuiltinType::Bool);
+ bool PruneWarnings = S.inTemplateInstantiation();
+
+ const Expr *InnerE = E->IgnoreParenImpCasts();
+ // We also want to warn on, e.g., "int i = -1.234"
+ if (const auto *UOp = dyn_cast<UnaryOperator>(InnerE))
+ if (UOp->getOpcode() == UO_Minus || UOp->getOpcode() == UO_Plus)
+ InnerE = UOp->getSubExpr()->IgnoreParenImpCasts();
+
+ bool IsLiteral = isa<FloatingLiteral>(E) || isa<FloatingLiteral>(InnerE);
+
+ llvm::APFloat Value(0.0);
+ bool IsConstant =
+ E->EvaluateAsFloat(Value, S.Context, Expr::SE_AllowSideEffects);
+ if (!IsConstant) {
+ if (S.ObjC().isSignedCharBool(T)) {
+ return S.ObjC().adornBoolConversionDiagWithTernaryFixit(
+ E, S.Diag(CContext, diag::warn_impcast_float_to_objc_signed_char_bool)
+ << E->getType());
+ }
+
+ return DiagnoseImpCast(S, E, T, CContext,
+ diag::warn_impcast_float_integer, PruneWarnings);
+ }
+
+ bool isExact = false;
+
+ llvm::APSInt IntegerValue(S.Context.getIntWidth(T),
+ T->hasUnsignedIntegerRepresentation());
+ llvm::APFloat::opStatus Result = Value.convertToInteger(
+ IntegerValue, llvm::APFloat::rmTowardZero, &isExact);
+
+ // FIXME: Force the precision of the source value down so we don't print
+ // digits which are usually useless (we don't really care here if we
+ // truncate a digit by accident in edge cases). Ideally, APFloat::toString
+ // would automatically print the shortest representation, but it's a bit
+ // tricky to implement.
+ SmallString<16> PrettySourceValue;
+ unsigned precision = llvm::APFloat::semanticsPrecision(Value.getSemantics());
+ precision = (precision * 59 + 195) / 196;
+ Value.toString(PrettySourceValue, precision);
+
+ if (S.ObjC().isSignedCharBool(T) && IntegerValue != 0 && IntegerValue != 1) {
+ return S.ObjC().adornBoolConversionDiagWithTernaryFixit(
+ E, S.Diag(CContext, diag::warn_impcast_constant_value_to_objc_bool)
+ << PrettySourceValue);
+ }
+
+ if (Result == llvm::APFloat::opOK && isExact) {
+ if (IsLiteral) return;
+ return DiagnoseImpCast(S, E, T, CContext, diag::warn_impcast_float_integer,
+ PruneWarnings);
+ }
+
+ // Conversion of a floating-point value to a non-bool integer where the
+ // integral part cannot be represented by the integer type is undefined.
+ if (!IsBool && Result == llvm::APFloat::opInvalidOp)
+ return DiagnoseImpCast(
+ S, E, T, CContext,
+ IsLiteral ? diag::warn_impcast_literal_float_to_integer_out_of_range
+ : diag::warn_impcast_float_to_integer_out_of_range,
+ PruneWarnings);
+
+ unsigned DiagID = 0;
+ if (IsLiteral) {
+ // Warn on floating point literal to integer.
+ DiagID = diag::warn_impcast_literal_float_to_integer;
+ } else if (IntegerValue == 0) {
+ if (Value.isZero()) { // Skip -0.0 to 0 conversion.
+ return DiagnoseImpCast(S, E, T, CContext,
+ diag::warn_impcast_float_integer, PruneWarnings);
+ }
+ // Warn on non-zero to zero conversion.
+ DiagID = diag::warn_impcast_float_to_integer_zero;
+ } else {
+ if (IntegerValue.isUnsigned()) {
+ if (!IntegerValue.isMaxValue()) {
+ return DiagnoseImpCast(S, E, T, CContext,
+ diag::warn_impcast_float_integer, PruneWarnings);
+ }
+ } else { // IntegerValue.isSigned()
+ if (!IntegerValue.isMaxSignedValue() &&
+ !IntegerValue.isMinSignedValue()) {
+ return DiagnoseImpCast(S, E, T, CContext,
+ diag::warn_impcast_float_integer, PruneWarnings);
+ }
+ }
+ // Warn on evaluatable floating point expression to integer conversion.
+ DiagID = diag::warn_impcast_float_to_integer;
+ }
+
+ SmallString<16> PrettyTargetValue;
+ if (IsBool)
+ PrettyTargetValue = Value.isZero() ? "false" : "true";
+ else
+ IntegerValue.toString(PrettyTargetValue);
+
+ if (PruneWarnings) {
+ S.DiagRuntimeBehavior(E->getExprLoc(), E,
+ S.PDiag(DiagID)
+ << E->getType() << T.getUnqualifiedType()
+ << PrettySourceValue << PrettyTargetValue
+ << E->getSourceRange() << SourceRange(CContext));
+ } else {
+ S.Diag(E->getExprLoc(), DiagID)
+ << E->getType() << T.getUnqualifiedType() << PrettySourceValue
+ << PrettyTargetValue << E->getSourceRange() << SourceRange(CContext);
+ }
+}
+
+/// Analyze the given compound assignment for the possible losing of
+/// floating-point precision.
+static void AnalyzeCompoundAssignment(Sema &S, BinaryOperator *E) {
+ assert(isa<CompoundAssignOperator>(E) &&
+ "Must be compound assignment operation");
+ // Recurse on the LHS and RHS in here
+ AnalyzeImplicitConversions(S, E->getLHS(), E->getOperatorLoc());
+ AnalyzeImplicitConversions(S, E->getRHS(), E->getOperatorLoc());
+
+ if (E->getLHS()->getType()->isAtomicType())
+ S.Diag(E->getOperatorLoc(), diag::warn_atomic_implicit_seq_cst);
+
+ // Now check the outermost expression
+ const auto *ResultBT = E->getLHS()->getType()->getAs<BuiltinType>();
+ const auto *RBT = cast<CompoundAssignOperator>(E)
+ ->getComputationResultType()
+ ->getAs<BuiltinType>();
+
+ // The below checks assume source is floating point.
+ if (!ResultBT || !RBT || !RBT->isFloatingPoint()) return;
+
+ // If source is floating point but target is an integer.
+ if (ResultBT->isInteger())
+ return DiagnoseImpCast(S, E, E->getRHS()->getType(), E->getLHS()->getType(),
+ E->getExprLoc(), diag::warn_impcast_float_integer);
+
+ if (!ResultBT->isFloatingPoint())
+ return;
+
+ // If both source and target are floating points, warn about losing precision.
+ int Order = S.getASTContext().getFloatingTypeSemanticOrder(
+ QualType(ResultBT, 0), QualType(RBT, 0));
+ if (Order < 0 && !S.SourceMgr.isInSystemMacro(E->getOperatorLoc()))
+ // warn about dropping FP rank.
+ DiagnoseImpCast(S, E->getRHS(), E->getLHS()->getType(), E->getOperatorLoc(),
+ diag::warn_impcast_float_result_precision);
+}
+
+static std::string PrettyPrintInRange(const llvm::APSInt &Value,
+ IntRange Range) {
+ if (!Range.Width) return "0";
+
+ llvm::APSInt ValueInRange = Value;
+ ValueInRange.setIsSigned(!Range.NonNegative);
+ ValueInRange = ValueInRange.trunc(Range.Width);
+ return toString(ValueInRange, 10);
+}
+
+static bool IsImplicitBoolFloatConversion(Sema &S, const Expr *Ex,
+ bool ToBool) {
+ if (!isa<ImplicitCastExpr>(Ex))
+ return false;
+
+ const Expr *InnerE = Ex->IgnoreParenImpCasts();
+ const Type *Target = S.Context.getCanonicalType(Ex->getType()).getTypePtr();
+ const Type *Source =
+ S.Context.getCanonicalType(InnerE->getType()).getTypePtr();
+ if (Target->isDependentType())
+ return false;
+
+ const auto *FloatCandidateBT =
+ dyn_cast<BuiltinType>(ToBool ? Source : Target);
+ const Type *BoolCandidateType = ToBool ? Target : Source;
+
+ return (BoolCandidateType->isSpecificBuiltinType(BuiltinType::Bool) &&
+ FloatCandidateBT && (FloatCandidateBT->isFloatingPoint()));
+}
+
+static void CheckImplicitArgumentConversions(Sema &S, const CallExpr *TheCall,
+ SourceLocation CC) {
+ for (unsigned I = 0, N = TheCall->getNumArgs(); I < N; ++I) {
+ const Expr *CurrA = TheCall->getArg(I);
+ if (!IsImplicitBoolFloatConversion(S, CurrA, true))
+ continue;
+
+ bool IsSwapped = ((I > 0) && IsImplicitBoolFloatConversion(
+ S, TheCall->getArg(I - 1), false));
+ IsSwapped |= ((I < (N - 1)) && IsImplicitBoolFloatConversion(
+ S, TheCall->getArg(I + 1), false));
+ if (IsSwapped) {
+ // Warn on this floating-point to bool conversion.
+ DiagnoseImpCast(S, CurrA->IgnoreParenImpCasts(),
+ CurrA->getType(), CC,
+ diag::warn_impcast_floating_point_to_bool);
+ }
+ }
+}
+
+static void DiagnoseNullConversion(Sema &S, Expr *E, QualType T,
+ SourceLocation CC) {
+ // Don't warn on functions which have return type nullptr_t.
+ if (isa<CallExpr>(E))
+ return;
+
+ // Check for NULL (GNUNull) or nullptr (CXX11_nullptr).
+ const Expr *NewE = E->IgnoreParenImpCasts();
+ bool IsGNUNullExpr = isa<GNUNullExpr>(NewE);
+ bool HasNullPtrType = NewE->getType()->isNullPtrType();
+ if (!IsGNUNullExpr && !HasNullPtrType)
+ return;
+
+ // Return if target type is a safe conversion.
+ if (T->isAnyPointerType() || T->isBlockPointerType() ||
+ T->isMemberPointerType() || !T->isScalarType() || T->isNullPtrType())
+ return;
+
+ if (S.Diags.isIgnored(diag::warn_impcast_null_pointer_to_integer,
+ E->getExprLoc()))
+ return;
+
+ SourceLocation Loc = E->getSourceRange().getBegin();
+
+ // Venture through the macro stacks to get to the source of macro arguments.
+ // The new location is a better location than the complete location that was
+ // passed in.
+ Loc = S.SourceMgr.getTopMacroCallerLoc(Loc);
+ CC = S.SourceMgr.getTopMacroCallerLoc(CC);
+
+ // __null is usually wrapped in a macro. Go up a macro if that is the case.
+ if (IsGNUNullExpr && Loc.isMacroID()) {
+ StringRef MacroName = Lexer::getImmediateMacroNameForDiagnostics(
+ Loc, S.SourceMgr, S.getLangOpts());
+ if (MacroName == "NULL")
+ Loc = S.SourceMgr.getImmediateExpansionRange(Loc).getBegin();
+ }
+
+ // Only warn if the null and context location are in the same macro expansion.
+ if (S.SourceMgr.getFileID(Loc) != S.SourceMgr.getFileID(CC))
+ return;
+
+ S.Diag(Loc, diag::warn_impcast_null_pointer_to_integer)
+ << HasNullPtrType << T << SourceRange(CC)
+ << FixItHint::CreateReplacement(Loc,
+ S.getFixItZeroLiteralForType(T, Loc));
+}
+
+// Helper function to filter out cases for constant width constant conversion.
+// Don't warn on char array initialization or for non-decimal values.
+static bool isSameWidthConstantConversion(Sema &S, Expr *E, QualType T,
+ SourceLocation CC) {
+ // If initializing from a constant, and the constant starts with '0',
+ // then it is a binary, octal, or hexadecimal. Allow these constants
+ // to fill all the bits, even if there is a sign change.
+ if (auto *IntLit = dyn_cast<IntegerLiteral>(E->IgnoreParenImpCasts())) {
+ const char FirstLiteralCharacter =
+ S.getSourceManager().getCharacterData(IntLit->getBeginLoc())[0];
+ if (FirstLiteralCharacter == '0')
+ return false;
+ }
+
+ // If the CC location points to a '{', and the type is char, then assume
+ // assume it is an array initialization.
+ if (CC.isValid() && T->isCharType()) {
+ const char FirstContextCharacter =
+ S.getSourceManager().getCharacterData(CC)[0];
+ if (FirstContextCharacter == '{')
+ return false;
+ }
+
+ return true;
+}
+
+static const IntegerLiteral *getIntegerLiteral(Expr *E) {
+ const auto *IL = dyn_cast<IntegerLiteral>(E);
+ if (!IL) {
+ if (auto *UO = dyn_cast<UnaryOperator>(E)) {
+ if (UO->getOpcode() == UO_Minus)
+ return dyn_cast<IntegerLiteral>(UO->getSubExpr());
+ }
+ }
+
+ return IL;
+}
+
+static void DiagnoseIntInBoolContext(Sema &S, Expr *E) {
+ E = E->IgnoreParenImpCasts();
+ SourceLocation ExprLoc = E->getExprLoc();
+
+ if (const auto *BO = dyn_cast<BinaryOperator>(E)) {
+ BinaryOperator::Opcode Opc = BO->getOpcode();
+ Expr::EvalResult Result;
+ // Do not diagnose unsigned shifts.
+ if (Opc == BO_Shl) {
+ const auto *LHS = getIntegerLiteral(BO->getLHS());
+ const auto *RHS = getIntegerLiteral(BO->getRHS());
+ if (LHS && LHS->getValue() == 0)
+ S.Diag(ExprLoc, diag::warn_left_shift_always) << 0;
+ else if (!E->isValueDependent() && LHS && RHS &&
+ RHS->getValue().isNonNegative() &&
+ E->EvaluateAsInt(Result, S.Context, Expr::SE_AllowSideEffects))
+ S.Diag(ExprLoc, diag::warn_left_shift_always)
+ << (Result.Val.getInt() != 0);
+ else if (E->getType()->isSignedIntegerType())
+ S.Diag(ExprLoc, diag::warn_left_shift_in_bool_context)
+ << FixItHint::CreateInsertion(E->getBeginLoc(), "(")
+ << FixItHint::CreateInsertion(S.getLocForEndOfToken(E->getEndLoc()),
+ ") != 0");
+ }
+ }
+
+ if (const auto *CO = dyn_cast<ConditionalOperator>(E)) {
+ const auto *LHS = getIntegerLiteral(CO->getTrueExpr());
+ const auto *RHS = getIntegerLiteral(CO->getFalseExpr());
+ if (!LHS || !RHS)
+ return;
+ if ((LHS->getValue() == 0 || LHS->getValue() == 1) &&
+ (RHS->getValue() == 0 || RHS->getValue() == 1))
+ // Do not diagnose common idioms.
+ return;
+ if (LHS->getValue() != 0 && RHS->getValue() != 0)
+ S.Diag(ExprLoc, diag::warn_integer_constants_in_conditional_always_true);
+ }
+}
+
+static void DiagnoseMixedUnicodeImplicitConversion(Sema &S, const Type *Source,
+ const Type *Target, Expr *E,
+ QualType T,
+ SourceLocation CC) {
+ assert(Source->isUnicodeCharacterType() && Target->isUnicodeCharacterType() &&
+ Source != Target);
+
+ // Lone surrogates have a distinct representation in UTF-32.
+ // Converting between UTF-16 and UTF-32 codepoints seems very widespread,
+ // so don't warn on such conversion.
+ if (Source->isChar16Type() && Target->isChar32Type())
+ return;
+
+ Expr::EvalResult Result;
+ if (E->EvaluateAsInt(Result, S.getASTContext(), Expr::SE_AllowSideEffects,
+ S.isConstantEvaluatedContext())) {
+ llvm::APSInt Value(32);
+ Value = Result.Val.getInt();
+ bool IsASCII = Value <= 0x7F;
+ bool IsBMP = Value <= 0xDFFF || (Value >= 0xE000 && Value <= 0xFFFF);
+ bool ConversionPreservesSemantics =
+ IsASCII || (!Source->isChar8Type() && !Target->isChar8Type() && IsBMP);
+
+ if (!ConversionPreservesSemantics) {
+ auto IsSingleCodeUnitCP = [](const QualType &T,
+ const llvm::APSInt &Value) {
+ if (T->isChar8Type())
+ return llvm::IsSingleCodeUnitUTF8Codepoint(Value.getExtValue());
+ if (T->isChar16Type())
+ return llvm::IsSingleCodeUnitUTF16Codepoint(Value.getExtValue());
+ assert(T->isChar32Type());
+ return llvm::IsSingleCodeUnitUTF32Codepoint(Value.getExtValue());
+ };
+
+ S.Diag(CC, diag::warn_impcast_unicode_char_type_constant)
+ << E->getType() << T
+ << IsSingleCodeUnitCP(E->getType().getUnqualifiedType(), Value)
+ << FormatUTFCodeUnitAsCodepoint(Value.getExtValue(), E->getType());
+ }
+ } else {
+ bool LosesPrecision = S.getASTContext().getIntWidth(E->getType()) >
+ S.getASTContext().getIntWidth(T);
+ DiagnoseImpCast(S, E, T, CC,
+ LosesPrecision ? diag::warn_impcast_unicode_precision
+ : diag::warn_impcast_unicode_char_type);
+ }
+}
+
+bool Sema::DiscardingCFIUncheckedCallee(QualType From, QualType To) const {
+ From = Context.getCanonicalType(From);
+ To = Context.getCanonicalType(To);
+ QualType MaybePointee = From->getPointeeType();
+ if (!MaybePointee.isNull() && MaybePointee->getAs<FunctionType>())
+ From = MaybePointee;
+ MaybePointee = To->getPointeeType();
+ if (!MaybePointee.isNull() && MaybePointee->getAs<FunctionType>())
+ To = MaybePointee;
+
+ if (const auto *FromFn = From->getAs<FunctionType>()) {
+ if (const auto *ToFn = To->getAs<FunctionType>()) {
+ if (FromFn->getCFIUncheckedCalleeAttr() &&
+ !ToFn->getCFIUncheckedCalleeAttr())
+ return true;
+ }
+ }
+ return false;
+}
+
+void Sema::CheckImplicitConversion(Expr *E, QualType T, SourceLocation CC,
+ bool *ICContext, bool IsListInit) {
+ if (E->isTypeDependent() || E->isValueDependent()) return;
+
+ const Type *Source = Context.getCanonicalType(E->getType()).getTypePtr();
+ const Type *Target = Context.getCanonicalType(T).getTypePtr();
+ if (Source == Target) return;
+ if (Target->isDependentType()) return;
+
+ // If the conversion context location is invalid don't complain. We also
+ // don't want to emit a warning if the issue occurs from the expansion of
+ // a system macro. The problem is that 'getSpellingLoc()' is slow, so we
+ // delay this check as long as possible. Once we detect we are in that
+ // scenario, we just return.
+ if (CC.isInvalid())
+ return;
+
+ if (Source->isAtomicType())
+ Diag(E->getExprLoc(), diag::warn_atomic_implicit_seq_cst);
+
+ // Diagnose implicit casts to bool.
+ if (Target->isSpecificBuiltinType(BuiltinType::Bool)) {
+ if (isa<StringLiteral>(E))
+ // Warn on string literal to bool. Checks for string literals in logical
+ // and expressions, for instance, assert(0 && "error here"), are
+ // prevented by a check in AnalyzeImplicitConversions().
+ return DiagnoseImpCast(*this, E, T, CC,
+ diag::warn_impcast_string_literal_to_bool);
+ if (isa<ObjCStringLiteral>(E) || isa<ObjCArrayLiteral>(E) ||
+ isa<ObjCDictionaryLiteral>(E) || isa<ObjCBoxedExpr>(E)) {
+ // This covers the literal expressions that evaluate to Objective-C
+ // objects.
+ return DiagnoseImpCast(*this, E, T, CC,
+ diag::warn_impcast_objective_c_literal_to_bool);
+ }
+ if (Source->isPointerType() || Source->canDecayToPointerType()) {
+ // Warn on pointer to bool conversion that is always true.
+ DiagnoseAlwaysNonNullPointer(E, Expr::NPCK_NotNull, /*IsEqual*/ false,
+ SourceRange(CC));
+ }
+ }
+
+ // If the we're converting a constant to an ObjC BOOL on a platform where BOOL
+ // is a typedef for signed char (macOS), then that constant value has to be 1
+ // or 0.
+ if (ObjC().isSignedCharBool(T) && Source->isIntegralType(Context)) {
+ Expr::EvalResult Result;
+ if (E->EvaluateAsInt(Result, getASTContext(), Expr::SE_AllowSideEffects)) {
+ if (Result.Val.getInt() != 1 && Result.Val.getInt() != 0) {
+ ObjC().adornBoolConversionDiagWithTernaryFixit(
+ E, Diag(CC, diag::warn_impcast_constant_value_to_objc_bool)
+ << toString(Result.Val.getInt(), 10));
+ }
+ return;
+ }
+ }
+
+ // Check implicit casts from Objective-C collection literals to specialized
+ // collection types, e.g., NSArray<NSString *> *.
+ if (auto *ArrayLiteral = dyn_cast<ObjCArrayLiteral>(E))
+ ObjC().checkArrayLiteral(QualType(Target, 0), ArrayLiteral);
+ else if (auto *DictionaryLiteral = dyn_cast<ObjCDictionaryLiteral>(E))
+ ObjC().checkDictionaryLiteral(QualType(Target, 0), DictionaryLiteral);
+
+ // Strip vector types.
+ if (isa<VectorType>(Source)) {
+ if (Target->isSveVLSBuiltinType() &&
+ (ARM().areCompatibleSveTypes(QualType(Target, 0),
+ QualType(Source, 0)) ||
+ ARM().areLaxCompatibleSveTypes(QualType(Target, 0),
+ QualType(Source, 0))))
+ return;
+
+ if (Target->isRVVVLSBuiltinType() &&
+ (Context.areCompatibleRVVTypes(QualType(Target, 0),
+ QualType(Source, 0)) ||
+ Context.areLaxCompatibleRVVTypes(QualType(Target, 0),
+ QualType(Source, 0))))
+ return;
+
+ if (!isa<VectorType>(Target)) {
+ if (SourceMgr.isInSystemMacro(CC))
+ return;
+ return DiagnoseImpCast(*this, E, T, CC, diag::warn_impcast_vector_scalar);
+ }
+ if (getLangOpts().HLSL &&
+ Target->castAs<VectorType>()->getNumElements() <
+ Source->castAs<VectorType>()->getNumElements()) {
+ // Diagnose vector truncation but don't return. We may also want to
+ // diagnose an element conversion.
+ DiagnoseImpCast(*this, E, T, CC,
+ diag::warn_hlsl_impcast_vector_truncation);
+ }
+
+ // If the vector cast is cast between two vectors of the same size, it is
+ // a bitcast, not a conversion, except under HLSL where it is a conversion.
+ if (!getLangOpts().HLSL &&
+ Context.getTypeSize(Source) == Context.getTypeSize(Target))
+ return;
+
+ Source = cast<VectorType>(Source)->getElementType().getTypePtr();
+ Target = cast<VectorType>(Target)->getElementType().getTypePtr();
+ }
+ if (const auto *VecTy = dyn_cast<VectorType>(Target))
+ Target = VecTy->getElementType().getTypePtr();
+
+ if (isa<ConstantMatrixType>(Source)) {
+ if (Target->isScalarType())
+ return DiagnoseImpCast(*this, E, T, CC, diag::warn_impcast_matrix_scalar);
+
+ if (getLangOpts().HLSL &&
+ Target->castAs<ConstantMatrixType>()->getNumElementsFlattened() <
+ Source->castAs<ConstantMatrixType>()->getNumElementsFlattened()) {
+ // Diagnose Matrix truncation but don't return. We may also want to
+ // diagnose an element conversion.
+ DiagnoseImpCast(*this, E, T, CC,
+ diag::warn_hlsl_impcast_matrix_truncation);
+ }
+ }
+ // Strip complex types.
+ if (isa<ComplexType>(Source)) {
+ if (!isa<ComplexType>(Target)) {
+ if (SourceMgr.isInSystemMacro(CC) || Target->isBooleanType())
+ return;
+
+ return DiagnoseImpCast(*this, E, T, CC,
+ getLangOpts().CPlusPlus
+ ? diag::err_impcast_complex_scalar
+ : diag::warn_impcast_complex_scalar);
+ }
+
+ Source = cast<ComplexType>(Source)->getElementType().getTypePtr();
+ Target = cast<ComplexType>(Target)->getElementType().getTypePtr();
+ }
+
+ const BuiltinType *SourceBT = dyn_cast<BuiltinType>(Source);
+ const BuiltinType *TargetBT = dyn_cast<BuiltinType>(Target);
+
+ // Strip SVE vector types
+ if (SourceBT && SourceBT->isSveVLSBuiltinType()) {
+ // Need the original target type for vector type checks
+ const Type *OriginalTarget = Context.getCanonicalType(T).getTypePtr();
+ // Handle conversion from scalable to fixed when msve-vector-bits is
+ // specified
+ if (ARM().areCompatibleSveTypes(QualType(OriginalTarget, 0),
+ QualType(Source, 0)) ||
+ ARM().areLaxCompatibleSveTypes(QualType(OriginalTarget, 0),
+ QualType(Source, 0)))
+ return;
+
+ // If the vector cast is cast between two vectors of the same size, it is
+ // a bitcast, not a conversion.
+ if (Context.getTypeSize(Source) == Context.getTypeSize(Target))
+ return;
+
+ Source = SourceBT->getSveEltType(Context).getTypePtr();
+ }
+
+ if (TargetBT && TargetBT->isSveVLSBuiltinType())
+ Target = TargetBT->getSveEltType(Context).getTypePtr();
+
+ // If the source is floating point...
+ if (SourceBT && SourceBT->isFloatingPoint()) {
+ // ...and the target is floating point...
+ if (TargetBT && TargetBT->isFloatingPoint()) {
+ // ...then warn if we're dropping FP rank.
+
+ int Order = getASTContext().getFloatingTypeSemanticOrder(
+ QualType(SourceBT, 0), QualType(TargetBT, 0));
+ if (Order > 0) {
+ // Don't warn about float constants that are precisely
+ // representable in the target type.
+ Expr::EvalResult result;
+ if (E->EvaluateAsRValue(result, Context)) {
+ // Value might be a float, a float vector, or a float complex.
+ if (IsSameFloatAfterCast(
+ result.Val,
+ Context.getFloatTypeSemantics(QualType(TargetBT, 0)),
+ Context.getFloatTypeSemantics(QualType(SourceBT, 0))))
+ return;
+ }
+
+ if (SourceMgr.isInSystemMacro(CC))
+ return;
+
+ DiagnoseImpCast(*this, E, T, CC, diag::warn_impcast_float_precision);
+ }
+ // ... or possibly if we're increasing rank, too
+ else if (Order < 0) {
+ if (SourceMgr.isInSystemMacro(CC))
+ return;
+
+ DiagnoseImpCast(*this, E, T, CC, diag::warn_impcast_double_promotion);
+ }
+ return;
+ }
+
+ // If the target is integral, always warn.
+ if (TargetBT && TargetBT->isInteger()) {
+ if (SourceMgr.isInSystemMacro(CC))
+ return;
+
+ DiagnoseFloatingImpCast(*this, E, T, CC);
+ }
+
+ // Detect the case where a call result is converted from floating-point to
+ // to bool, and the final argument to the call is converted from bool, to
+ // discover this typo:
+ //
+ // bool b = fabs(x < 1.0); // should be "bool b = fabs(x) < 1.0;"
+ //
+ // FIXME: This is an incredibly special case; is there some more general
+ // way to detect this class of misplaced-parentheses bug?
+ if (Target->isBooleanType() && isa<CallExpr>(E)) {
+ // Check last argument of function call to see if it is an
+ // implicit cast from a type matching the type the result
+ // is being cast to.
+ CallExpr *CEx = cast<CallExpr>(E);
+ if (unsigned NumArgs = CEx->getNumArgs()) {
+ Expr *LastA = CEx->getArg(NumArgs - 1);
+ Expr *InnerE = LastA->IgnoreParenImpCasts();
+ if (isa<ImplicitCastExpr>(LastA) &&
+ InnerE->getType()->isBooleanType()) {
+ // Warn on this floating-point to bool conversion
+ DiagnoseImpCast(*this, E, T, CC,
+ diag::warn_impcast_floating_point_to_bool);
+ }
+ }
+ }
+ return;
+ }
+
+ // Valid casts involving fixed point types should be accounted for here.
+ if (Source->isFixedPointType()) {
+ if (Target->isUnsaturatedFixedPointType()) {
+ Expr::EvalResult Result;
+ if (E->EvaluateAsFixedPoint(Result, Context, Expr::SE_AllowSideEffects,
+ isConstantEvaluatedContext())) {
+ llvm::APFixedPoint Value = Result.Val.getFixedPoint();
+ llvm::APFixedPoint MaxVal = Context.getFixedPointMax(T);
+ llvm::APFixedPoint MinVal = Context.getFixedPointMin(T);
+ if (Value > MaxVal || Value < MinVal) {
+ DiagRuntimeBehavior(E->getExprLoc(), E,
+ PDiag(diag::warn_impcast_fixed_point_range)
+ << Value.toString() << T
+ << E->getSourceRange()
+ << clang::SourceRange(CC));
+ return;
+ }
+ }
+ } else if (Target->isIntegerType()) {
+ Expr::EvalResult Result;
+ if (!isConstantEvaluatedContext() &&
+ E->EvaluateAsFixedPoint(Result, Context, Expr::SE_AllowSideEffects)) {
+ llvm::APFixedPoint FXResult = Result.Val.getFixedPoint();
+
+ bool Overflowed;
+ llvm::APSInt IntResult = FXResult.convertToInt(
+ Context.getIntWidth(T), Target->isSignedIntegerOrEnumerationType(),
+ &Overflowed);
+
+ if (Overflowed) {
+ DiagRuntimeBehavior(E->getExprLoc(), E,
+ PDiag(diag::warn_impcast_fixed_point_range)
+ << FXResult.toString() << T
+ << E->getSourceRange()
+ << clang::SourceRange(CC));
+ return;
+ }
+ }
+ }
+ } else if (Target->isUnsaturatedFixedPointType()) {
+ if (Source->isIntegerType()) {
+ Expr::EvalResult Result;
+ if (!isConstantEvaluatedContext() &&
+ E->EvaluateAsInt(Result, Context, Expr::SE_AllowSideEffects)) {
+ llvm::APSInt Value = Result.Val.getInt();
+
+ bool Overflowed;
+ llvm::APFixedPoint IntResult = llvm::APFixedPoint::getFromIntValue(
+ Value, Context.getFixedPointSemantics(T), &Overflowed);
+
+ if (Overflowed) {
+ DiagRuntimeBehavior(E->getExprLoc(), E,
+ PDiag(diag::warn_impcast_fixed_point_range)
+ << toString(Value, /*Radix=*/10) << T
+ << E->getSourceRange()
+ << clang::SourceRange(CC));
+ return;
+ }
+ }
+ }
+ }
+
+ // If we are casting an integer type to a floating point type without
+ // initialization-list syntax, we might lose accuracy if the floating
+ // point type has a narrower significand than the integer type.
+ if (SourceBT && TargetBT && SourceBT->isIntegerType() &&
+ TargetBT->isFloatingType() && !IsListInit) {
+ // Determine the number of precision bits in the source integer type.
+ std::optional<IntRange> SourceRange =
+ TryGetExprRange(Context, E, isConstantEvaluatedContext(),
+ /*Approximate=*/true);
+ if (!SourceRange)
+ return;
+ unsigned int SourcePrecision = SourceRange->Width;
+
+ // Determine the number of precision bits in the
+ // target floating point type.
+ unsigned int TargetPrecision = llvm::APFloatBase::semanticsPrecision(
+ Context.getFloatTypeSemantics(QualType(TargetBT, 0)));
+
+ if (SourcePrecision > 0 && TargetPrecision > 0 &&
+ SourcePrecision > TargetPrecision) {
+
+ if (std::optional<llvm::APSInt> SourceInt =
+ E->getIntegerConstantExpr(Context)) {
+ // If the source integer is a constant, convert it to the target
+ // floating point type. Issue a warning if the value changes
+ // during the whole conversion.
+ llvm::APFloat TargetFloatValue(
+ Context.getFloatTypeSemantics(QualType(TargetBT, 0)));
+ llvm::APFloat::opStatus ConversionStatus =
+ TargetFloatValue.convertFromAPInt(
+ *SourceInt, SourceBT->isSignedInteger(),
+ llvm::APFloat::rmNearestTiesToEven);
+
+ if (ConversionStatus != llvm::APFloat::opOK) {
+ SmallString<32> PrettySourceValue;
+ SourceInt->toString(PrettySourceValue, 10);
+ SmallString<32> PrettyTargetValue;
+ TargetFloatValue.toString(PrettyTargetValue, TargetPrecision);
+
+ DiagRuntimeBehavior(
+ E->getExprLoc(), E,
+ PDiag(diag::warn_impcast_integer_float_precision_constant)
+ << PrettySourceValue << PrettyTargetValue << E->getType() << T
+ << E->getSourceRange() << clang::SourceRange(CC));
+ }
+ } else {
+ // Otherwise, the implicit conversion may lose precision.
+ DiagnoseImpCast(*this, E, T, CC,
+ diag::warn_impcast_integer_float_precision);
+ }
+ }
+ }
+
+ DiagnoseNullConversion(*this, E, T, CC);
+
+ DiscardMisalignedMemberAddress(Target, E);
+
+ if (Source->isUnicodeCharacterType() && Target->isUnicodeCharacterType()) {
+ DiagnoseMixedUnicodeImplicitConversion(*this, Source, Target, E, T, CC);
+ return;
+ }
+
+ if (Target->isBooleanType())
+ DiagnoseIntInBoolContext(*this, E);
+
+ if (DiscardingCFIUncheckedCallee(QualType(Source, 0), QualType(Target, 0))) {
+ Diag(CC, diag::warn_cast_discards_cfi_unchecked_callee)
+ << QualType(Source, 0) << QualType(Target, 0);
+ }
+
+ if (!Source->isIntegerType() || !Target->isIntegerType())
+ return;
+
+ // TODO: remove this early return once the false positives for constant->bool
+ // in templates, macros, etc, are reduced or removed.
+ if (Target->isSpecificBuiltinType(BuiltinType::Bool))
+ return;
+
+ if (ObjC().isSignedCharBool(T) && !Source->isCharType() &&
+ !E->isKnownToHaveBooleanValue(/*Semantic=*/false)) {
+ return ObjC().adornBoolConversionDiagWithTernaryFixit(
+ E, Diag(CC, diag::warn_impcast_int_to_objc_signed_char_bool)
+ << E->getType());
+ }
+ std::optional<IntRange> LikelySourceRange = TryGetExprRange(
+ Context, E, isConstantEvaluatedContext(), /*Approximate=*/true);
+ if (!LikelySourceRange)
+ return;
+
+ IntRange SourceTypeRange =
+ IntRange::forTargetOfCanonicalType(Context, Source);
+ IntRange TargetRange = IntRange::forTargetOfCanonicalType(Context, Target);
+
+ if (LikelySourceRange->Width > TargetRange.Width) {
+ // If the source is a constant, use a default-on diagnostic.
+ // TODO: this should happen for bitfield stores, too.
+ Expr::EvalResult Result;
+ if (E->EvaluateAsInt(Result, Context, Expr::SE_AllowSideEffects,
+ isConstantEvaluatedContext())) {
+ llvm::APSInt Value(32);
+ Value = Result.Val.getInt();
+
+ if (SourceMgr.isInSystemMacro(CC))
+ return;
+
+ std::string PrettySourceValue = toString(Value, 10);
+ std::string PrettyTargetValue = PrettyPrintInRange(Value, TargetRange);
+
+ DiagRuntimeBehavior(E->getExprLoc(), E,
+ PDiag(diag::warn_impcast_integer_precision_constant)
+ << PrettySourceValue << PrettyTargetValue
+ << E->getType() << T << E->getSourceRange()
+ << SourceRange(CC));
+ return;
+ }
+
+ // People want to build with -Wshorten-64-to-32 and not -Wconversion.
+ if (SourceMgr.isInSystemMacro(CC))
+ return;
+
+ if (const auto *UO = dyn_cast<UnaryOperator>(E)) {
+ if (UO->getOpcode() == UO_Minus)
+ return DiagnoseImpCast(
+ *this, E, T, CC, diag::warn_impcast_integer_precision_on_negation);
+ }
+
+ if (TargetRange.Width == 32 && Context.getIntWidth(E->getType()) == 64)
+ return DiagnoseImpCast(*this, E, T, CC, diag::warn_impcast_integer_64_32,
+ /* pruneControlFlow */ true);
+ return DiagnoseImpCast(*this, E, T, CC,
+ diag::warn_impcast_integer_precision);
+ }
+
+ if (TargetRange.Width > SourceTypeRange.Width) {
+ if (auto *UO = dyn_cast<UnaryOperator>(E))
+ if (UO->getOpcode() == UO_Minus)
+ if (Source->isUnsignedIntegerType()) {
+ if (Target->isUnsignedIntegerType())
+ return DiagnoseImpCast(*this, E, T, CC,
+ diag::warn_impcast_high_order_zero_bits);
+ if (Target->isSignedIntegerType())
+ return DiagnoseImpCast(*this, E, T, CC,
+ diag::warn_impcast_nonnegative_result);
+ }
+ }
+
+ if (TargetRange.Width == LikelySourceRange->Width &&
+ !TargetRange.NonNegative && LikelySourceRange->NonNegative &&
+ Source->isSignedIntegerType()) {
+ // Warn when doing a signed to signed conversion, warn if the positive
+ // source value is exactly the width of the target type, which will
+ // cause a negative value to be stored.
+
+ Expr::EvalResult Result;
+ if (E->EvaluateAsInt(Result, Context, Expr::SE_AllowSideEffects) &&
+ !SourceMgr.isInSystemMacro(CC)) {
+ llvm::APSInt Value = Result.Val.getInt();
+ if (isSameWidthConstantConversion(*this, E, T, CC)) {
+ std::string PrettySourceValue = toString(Value, 10);
+ std::string PrettyTargetValue = PrettyPrintInRange(Value, TargetRange);
+
+ Diag(E->getExprLoc(),
+ PDiag(diag::warn_impcast_integer_precision_constant)
+ << PrettySourceValue << PrettyTargetValue << E->getType() << T
+ << E->getSourceRange() << SourceRange(CC));
+ return;
+ }
+ }
+
+ // Fall through for non-constants to give a sign conversion warning.
+ }
+
+ if ((!isa<EnumType>(Target) || !isa<EnumType>(Source)) &&
+ ((TargetRange.NonNegative && !LikelySourceRange->NonNegative) ||
+ (!TargetRange.NonNegative && LikelySourceRange->NonNegative &&
+ LikelySourceRange->Width == TargetRange.Width))) {
+ if (SourceMgr.isInSystemMacro(CC))
+ return;
+
+ if (SourceBT && SourceBT->isInteger() && TargetBT &&
+ TargetBT->isInteger() &&
+ Source->isSignedIntegerType() == Target->isSignedIntegerType()) {
+ return;
+ }
+
+ unsigned DiagID = diag::warn_impcast_integer_sign;
+
+ // Traditionally, gcc has warned about this under -Wsign-compare.
+ // We also want to warn about it in -Wconversion.
+ // So if -Wconversion is off, use a completely identical diagnostic
+ // in the sign-compare group.
+ // The conditional-checking code will
+ if (ICContext) {
+ DiagID = diag::warn_impcast_integer_sign_conditional;
+ *ICContext = true;
+ }
+
+ DiagnoseImpCast(*this, E, T, CC, DiagID);
+ }
+
+ // If we're implicitly converting from an integer into an enumeration, that
+ // is valid in C but invalid in C++.
+ QualType SourceType = E->getEnumCoercedType(Context);
+ const BuiltinType *CoercedSourceBT = SourceType->getAs<BuiltinType>();
+ if (CoercedSourceBT && CoercedSourceBT->isInteger() && isa<EnumType>(Target))
+ return DiagnoseImpCast(*this, E, T, CC, diag::warn_impcast_int_to_enum);
+
+ // Diagnose conversions between different enumeration types.
+ // In C, we pretend that the type of an EnumConstantDecl is its enumeration
+ // type, to give us better diagnostics.
+ Source = Context.getCanonicalType(SourceType).getTypePtr();
+
+ if (const EnumType *SourceEnum = Source->getAsCanonical<EnumType>())
+ if (const EnumType *TargetEnum = Target->getAsCanonical<EnumType>())
+ if (SourceEnum->getDecl()->hasNameForLinkage() &&
+ TargetEnum->getDecl()->hasNameForLinkage() &&
+ SourceEnum != TargetEnum) {
+ if (SourceMgr.isInSystemMacro(CC))
+ return;
+
+ return DiagnoseImpCast(*this, E, SourceType, T, CC,
+ diag::warn_impcast_different_enum_types);
+ }
+}
+
+static void CheckConditionalOperator(Sema &S, AbstractConditionalOperator *E,
+ SourceLocation CC, QualType T);
+
+static void CheckConditionalOperand(Sema &S, Expr *E, QualType T,
+ SourceLocation CC, bool &ICContext) {
+ E = E->IgnoreParenImpCasts();
+ // Diagnose incomplete type for second or third operand in C.
+ if (!S.getLangOpts().CPlusPlus && E->getType()->isRecordType())
+ S.RequireCompleteExprType(E, diag::err_incomplete_type);
+
+ if (auto *CO = dyn_cast<AbstractConditionalOperator>(E))
+ return CheckConditionalOperator(S, CO, CC, T);
+
+ AnalyzeImplicitConversions(S, E, CC);
+ if (E->getType() != T)
+ return S.CheckImplicitConversion(E, T, CC, &ICContext);
+}
+
+static void CheckConditionalOperator(Sema &S, AbstractConditionalOperator *E,
+ SourceLocation CC, QualType T) {
+ AnalyzeImplicitConversions(S, E->getCond(), E->getQuestionLoc());
+
+ Expr *TrueExpr = E->getTrueExpr();
+ if (auto *BCO = dyn_cast<BinaryConditionalOperator>(E))
+ TrueExpr = BCO->getCommon();
+
+ bool Suspicious = false;
+ CheckConditionalOperand(S, TrueExpr, T, CC, Suspicious);
+ CheckConditionalOperand(S, E->getFalseExpr(), T, CC, Suspicious);
+
+ if (T->isBooleanType())
+ DiagnoseIntInBoolContext(S, E);
+
+ // If -Wconversion would have warned about either of the candidates
+ // for a signedness conversion to the context type...
+ if (!Suspicious) return;
+
+ // ...but it's currently ignored...
+ if (!S.Diags.isIgnored(diag::warn_impcast_integer_sign_conditional, CC))
+ return;
+
+ // ...then check whether it would have warned about either of the
+ // candidates for a signedness conversion to the condition type.
+ if (E->getType() == T) return;
+
+ Suspicious = false;
+ S.CheckImplicitConversion(TrueExpr->IgnoreParenImpCasts(), E->getType(), CC,
+ &Suspicious);
+ if (!Suspicious)
+ S.CheckImplicitConversion(E->getFalseExpr()->IgnoreParenImpCasts(),
+ E->getType(), CC, &Suspicious);
+}
+
+/// Check conversion of given expression to boolean.
+/// Input argument E is a logical expression.
+static void CheckBoolLikeConversion(Sema &S, Expr *E, SourceLocation CC) {
+ // Run the bool-like conversion checks only for C since there bools are
+ // still not used as the return type from "boolean" operators or as the input
+ // type for conditional operators.
+ if (S.getLangOpts().CPlusPlus)
+ return;
+ if (E->IgnoreParenImpCasts()->getType()->isAtomicType())
+ return;
+ S.CheckImplicitConversion(E->IgnoreParenImpCasts(), S.Context.BoolTy, CC);
+}
+
+namespace {
+struct AnalyzeImplicitConversionsWorkItem {
+ Expr *E;
+ SourceLocation CC;
+ bool IsListInit;
+};
+}
+
+static void CheckCommaOperand(
+ Sema &S, Expr *E, QualType T, SourceLocation CC,
+ bool ExtraCheckForImplicitConversion,
+ llvm::SmallVectorImpl<AnalyzeImplicitConversionsWorkItem> &WorkList) {
+ E = E->IgnoreParenImpCasts();
+ WorkList.push_back({E, CC, false});
+
+ if (ExtraCheckForImplicitConversion && E->getType() != T)
+ S.CheckImplicitConversion(E, T, CC);
+}
+
+/// Data recursive variant of AnalyzeImplicitConversions. Subexpressions
+/// that should be visited are added to WorkList.
+static void AnalyzeImplicitConversions(
+ Sema &S, AnalyzeImplicitConversionsWorkItem Item,
+ llvm::SmallVectorImpl<AnalyzeImplicitConversionsWorkItem> &WorkList) {
+ Expr *OrigE = Item.E;
+ SourceLocation CC = Item.CC;
+
+ QualType T = OrigE->getType();
+ Expr *E = OrigE->IgnoreParenImpCasts();
+
+ // Propagate whether we are in a C++ list initialization expression.
+ // If so, we do not issue warnings for implicit int-float conversion
+ // precision loss, because C++11 narrowing already handles it.
+ //
+ // HLSL's initialization lists are special, so they shouldn't observe the C++
+ // behavior here.
+ bool IsListInit =
+ Item.IsListInit || (isa<InitListExpr>(OrigE) &&
+ S.getLangOpts().CPlusPlus && !S.getLangOpts().HLSL);
+
+ if (E->isTypeDependent() || E->isValueDependent())
+ return;
+
+ Expr *SourceExpr = E;
+ // Examine, but don't traverse into the source expression of an
+ // OpaqueValueExpr, since it may have multiple parents and we don't want to
+ // emit duplicate diagnostics. Its fine to examine the form or attempt to
+ // evaluate it in the context of checking the specific conversion to T though.
+ if (auto *OVE = dyn_cast<OpaqueValueExpr>(E))
+ if (auto *Src = OVE->getSourceExpr())
+ SourceExpr = Src;
+
+ if (const auto *UO = dyn_cast<UnaryOperator>(SourceExpr))
+ if (UO->getOpcode() == UO_Not &&
+ UO->getSubExpr()->isKnownToHaveBooleanValue())
+ S.Diag(UO->getBeginLoc(), diag::warn_bitwise_negation_bool)
+ << OrigE->getSourceRange() << T->isBooleanType()
+ << FixItHint::CreateReplacement(UO->getBeginLoc(), "!");
+
+ if (auto *BO = dyn_cast<BinaryOperator>(SourceExpr)) {
+ if ((BO->getOpcode() == BO_And || BO->getOpcode() == BO_Or) &&
+ BO->getLHS()->isKnownToHaveBooleanValue() &&
+ BO->getRHS()->isKnownToHaveBooleanValue() &&
+ BO->getLHS()->HasSideEffects(S.Context) &&
+ BO->getRHS()->HasSideEffects(S.Context)) {
+ SourceManager &SM = S.getSourceManager();
+ const LangOptions &LO = S.getLangOpts();
+ SourceLocation BLoc = BO->getOperatorLoc();
+ SourceLocation ELoc = Lexer::getLocForEndOfToken(BLoc, 0, SM, LO);
+ StringRef SR = clang::Lexer::getSourceText(
+ clang::CharSourceRange::getTokenRange(BLoc, ELoc), SM, LO);
+ // To reduce false positives, only issue the diagnostic if the operator
+ // is explicitly spelled as a punctuator. This suppresses the diagnostic
+ // when using 'bitand' or 'bitor' either as keywords in C++ or as macros
+ // in C, along with other macro spellings the user might invent.
+ if (SR.str() == "&" || SR.str() == "|") {
+
+ S.Diag(BO->getBeginLoc(), diag::warn_bitwise_instead_of_logical)
+ << (BO->getOpcode() == BO_And ? "&" : "|")
+ << OrigE->getSourceRange()
+ << FixItHint::CreateReplacement(
+ BO->getOperatorLoc(),
+ (BO->getOpcode() == BO_And ? "&&" : "||"));
+ S.Diag(BO->getBeginLoc(), diag::note_cast_operand_to_int);
+ }
+ } else if (BO->isCommaOp() && !S.getLangOpts().CPlusPlus) {
+ /// Analyze the given comma operator. The basic idea behind the analysis
+ /// is to analyze the left and right operands slightly differently. The
+ /// left operand needs to check whether the operand itself has an implicit
+ /// conversion, but not whether the left operand induces an implicit
+ /// conversion for the entire comma expression itself. This is similar to
+ /// how CheckConditionalOperand behaves; it's as-if the correct operand
+ /// were directly used for the implicit conversion check.
+ CheckCommaOperand(S, BO->getLHS(), T, BO->getOperatorLoc(),
+ /*ExtraCheckForImplicitConversion=*/false, WorkList);
+ CheckCommaOperand(S, BO->getRHS(), T, BO->getOperatorLoc(),
+ /*ExtraCheckForImplicitConversion=*/true, WorkList);
+ return;
+ }
+ }
+
+ // For conditional operators, we analyze the arguments as if they
+ // were being fed directly into the output.
+ if (auto *CO = dyn_cast<AbstractConditionalOperator>(SourceExpr)) {
+ CheckConditionalOperator(S, CO, CC, T);
+ return;
+ }
+
+ // Check implicit argument conversions for function calls.
+ if (const auto *Call = dyn_cast<CallExpr>(SourceExpr))
+ CheckImplicitArgumentConversions(S, Call, CC);
+
+ // Go ahead and check any implicit conversions we might have skipped.
+ // The non-canonical typecheck is just an optimization;
+ // CheckImplicitConversion will filter out dead implicit conversions.
+ if (SourceExpr->getType() != T)
+ S.CheckImplicitConversion(SourceExpr, T, CC, nullptr, IsListInit);
+
+ // Now continue drilling into this expression.
+
+ if (PseudoObjectExpr *POE = dyn_cast<PseudoObjectExpr>(E)) {
+ // The bound subexpressions in a PseudoObjectExpr are not reachable
+ // as transitive children.
+ // FIXME: Use a more uniform representation for this.
+ for (auto *SE : POE->semantics())
+ if (auto *OVE = dyn_cast<OpaqueValueExpr>(SE))
+ WorkList.push_back({OVE->getSourceExpr(), CC, IsListInit});
+ }
+
+ // Skip past explicit casts.
+ if (auto *CE = dyn_cast<ExplicitCastExpr>(E)) {
+ E = CE->getSubExpr();
+ // In the special case of a C++ function-style cast with braces,
+ // CXXFunctionalCastExpr has an InitListExpr as direct child with a single
+ // initializer. This InitListExpr basically belongs to the cast itself, so
+ // we skip it too. Specifically this is needed to silence -Wdouble-promotion
+ if (isa<CXXFunctionalCastExpr>(CE)) {
+ if (auto *InitListE = dyn_cast<InitListExpr>(E)) {
+ if (InitListE->getNumInits() == 1) {
+ E = InitListE->getInit(0);
+ }
+ }
+ }
+ E = E->IgnoreParenImpCasts();
+ if (!CE->getType()->isVoidType() && E->getType()->isAtomicType())
+ S.Diag(E->getBeginLoc(), diag::warn_atomic_implicit_seq_cst);
+ WorkList.push_back({E, CC, IsListInit});
+ return;
+ }
+
+ if (auto *OutArgE = dyn_cast<HLSLOutArgExpr>(E)) {
+ WorkList.push_back({OutArgE->getArgLValue(), CC, IsListInit});
+ // The base expression is only used to initialize the parameter for
+ // arguments to `inout` parameters, so we only traverse down the base
+ // expression for `inout` cases.
+ if (OutArgE->isInOut())
+ WorkList.push_back(
+ {OutArgE->getCastedTemporary()->getSourceExpr(), CC, IsListInit});
+ WorkList.push_back({OutArgE->getWritebackCast(), CC, IsListInit});
+ return;
+ }
+
+ if (BinaryOperator *BO = dyn_cast<BinaryOperator>(E)) {
+ // Do a somewhat different check with comparison operators.
+ if (BO->isComparisonOp())
+ return AnalyzeComparison(S, BO);
+
+ // And with simple assignments.
+ if (BO->getOpcode() == BO_Assign)
+ return AnalyzeAssignment(S, BO);
+ // And with compound assignments.
+ if (BO->isAssignmentOp())
+ return AnalyzeCompoundAssignment(S, BO);
+ }
+
+ // These break the otherwise-useful invariant below. Fortunately,
+ // we don't really need to recurse into them, because any internal
+ // expressions should have been analyzed already when they were
+ // built into statements.
+ if (isa<StmtExpr>(E)) return;
+
+ // Don't descend into unevaluated contexts.
+ if (isa<UnaryExprOrTypeTraitExpr>(E)) return;
+
+ // Now just recurse over the expression's children.
+ CC = E->getExprLoc();
+ BinaryOperator *BO = dyn_cast<BinaryOperator>(E);
+ bool IsLogicalAndOperator = BO && BO->getOpcode() == BO_LAnd;
+ for (Stmt *SubStmt : E->children()) {
+ Expr *ChildExpr = dyn_cast_or_null<Expr>(SubStmt);
+ if (!ChildExpr)
+ continue;
+
+ if (auto *CSE = dyn_cast<CoroutineSuspendExpr>(E))
+ if (ChildExpr == CSE->getOperand())
+ // Do not recurse over a CoroutineSuspendExpr's operand.
+ // The operand is also a subexpression of getCommonExpr(), and
+ // recursing into it directly would produce duplicate diagnostics.
+ continue;
+
+ if (IsLogicalAndOperator &&
+ isa<StringLiteral>(ChildExpr->IgnoreParenImpCasts()))
+ // Ignore checking string literals that are in logical and operators.
+ // This is a common pattern for asserts.
+ continue;
+ WorkList.push_back({ChildExpr, CC, IsListInit});
+ }
+
+ if (BO && BO->isLogicalOp()) {
+ Expr *SubExpr = BO->getLHS()->IgnoreParenImpCasts();
+ if (!IsLogicalAndOperator || !isa<StringLiteral>(SubExpr))
+ ::CheckBoolLikeConversion(S, SubExpr, BO->getExprLoc());
+
+ SubExpr = BO->getRHS()->IgnoreParenImpCasts();
+ if (!IsLogicalAndOperator || !isa<StringLiteral>(SubExpr))
+ ::CheckBoolLikeConversion(S, SubExpr, BO->getExprLoc());
+ }
+
+ if (const UnaryOperator *U = dyn_cast<UnaryOperator>(E)) {
+ if (U->getOpcode() == UO_LNot) {
+ ::CheckBoolLikeConversion(S, U->getSubExpr(), CC);
+ } else if (U->getOpcode() != UO_AddrOf) {
+ if (U->getSubExpr()->getType()->isAtomicType())
+ S.Diag(U->getSubExpr()->getBeginLoc(),
+ diag::warn_atomic_implicit_seq_cst);
+ }
+ }
+}
+
+/// AnalyzeImplicitConversions - Find and report any interesting
+/// implicit conversions in the given expression. There are a couple
+/// of competing diagnostics here, -Wconversion and -Wsign-compare.
+static void AnalyzeImplicitConversions(Sema &S, Expr *OrigE, SourceLocation CC,
+ bool IsListInit/*= false*/) {
+ llvm::SmallVector<AnalyzeImplicitConversionsWorkItem, 16> WorkList;
+ WorkList.push_back({OrigE, CC, IsListInit});
+ while (!WorkList.empty())
+ AnalyzeImplicitConversions(S, WorkList.pop_back_val(), WorkList);
+}
+
+// Helper function for Sema::DiagnoseAlwaysNonNullPointer.
+// Returns true when emitting a warning about taking the address of a reference.
+static bool CheckForReference(Sema &SemaRef, const Expr *E,
+ const PartialDiagnostic &PD) {
+ E = E->IgnoreParenImpCasts();
+
+ const FunctionDecl *FD = nullptr;
+
+ if (const DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(E)) {
+ if (!DRE->getDecl()->getType()->isReferenceType())
+ return false;
+ } else if (const MemberExpr *M = dyn_cast<MemberExpr>(E)) {
+ if (!M->getMemberDecl()->getType()->isReferenceType())
+ return false;
+ } else if (const CallExpr *Call = dyn_cast<CallExpr>(E)) {
+ if (!Call->getCallReturnType(SemaRef.Context)->isReferenceType())
+ return false;
+ FD = Call->getDirectCallee();
+ } else {
+ return false;
+ }
+
+ SemaRef.Diag(E->getExprLoc(), PD);
+
+ // If possible, point to location of function.
+ if (FD) {
+ SemaRef.Diag(FD->getLocation(), diag::note_reference_is_return_value) << FD;
+ }
+
+ return true;
+}
+
+// Returns true if the SourceLocation is expanded from any macro body.
+// Returns false if the SourceLocation is invalid, is from not in a macro
+// expansion, or is from expanded from a top-level macro argument.
+static bool IsInAnyMacroBody(const SourceManager &SM, SourceLocation Loc) {
+ if (Loc.isInvalid())
+ return false;
+
+ while (Loc.isMacroID()) {
+ if (SM.isMacroBodyExpansion(Loc))
+ return true;
+ Loc = SM.getImmediateMacroCallerLoc(Loc);
+ }
+
+ return false;
+}
+
+void Sema::DiagnoseAlwaysNonNullPointer(Expr *E,
+ Expr::NullPointerConstantKind NullKind,
+ bool IsEqual, SourceRange Range) {
+ if (!E)
+ return;
+
+ // Don't warn inside macros.
+ if (E->getExprLoc().isMacroID()) {
+ const SourceManager &SM = getSourceManager();
+ if (IsInAnyMacroBody(SM, E->getExprLoc()) ||
+ IsInAnyMacroBody(SM, Range.getBegin()))
+ return;
+ }
+ E = E->IgnoreImpCasts();
+
+ const bool IsCompare = NullKind != Expr::NPCK_NotNull;
+
+ if (isa<CXXThisExpr>(E)) {
+ unsigned DiagID = IsCompare ? diag::warn_this_null_compare
+ : diag::warn_this_bool_conversion;
+ Diag(E->getExprLoc(), DiagID) << E->getSourceRange() << Range << IsEqual;
+ return;
+ }
+
+ bool IsAddressOf = false;
+
+ if (auto *UO = dyn_cast<UnaryOperator>(E->IgnoreParens())) {
+ if (UO->getOpcode() != UO_AddrOf)
+ return;
+ IsAddressOf = true;
+ E = UO->getSubExpr();
+ }
+
+ if (IsAddressOf) {
+ unsigned DiagID = IsCompare
+ ? diag::warn_address_of_reference_null_compare
+ : diag::warn_address_of_reference_bool_conversion;
+ PartialDiagnostic PD = PDiag(DiagID) << E->getSourceRange() << Range
+ << IsEqual;
+ if (CheckForReference(*this, E, PD)) {
+ return;
+ }
+ }
+
+ auto ComplainAboutNonnullParamOrCall = [&](const Attr *NonnullAttr) {
+ bool IsParam = isa<NonNullAttr>(NonnullAttr);
+ std::string Str;
+ llvm::raw_string_ostream S(Str);
+ E->printPretty(S, nullptr, getPrintingPolicy());
+ unsigned DiagID = IsCompare ? diag::warn_nonnull_expr_compare
+ : diag::warn_cast_nonnull_to_bool;
+ Diag(E->getExprLoc(), DiagID) << IsParam << S.str()
+ << E->getSourceRange() << Range << IsEqual;
+ Diag(NonnullAttr->getLocation(), diag::note_declared_nonnull) << IsParam;
+ };
+
+ // If we have a CallExpr that is tagged with returns_nonnull, we can complain.
+ if (auto *Call = dyn_cast<CallExpr>(E->IgnoreParenImpCasts())) {
+ if (auto *Callee = Call->getDirectCallee()) {
+ if (const Attr *A = Callee->getAttr<ReturnsNonNullAttr>()) {
+ ComplainAboutNonnullParamOrCall(A);
+ return;
+ }
+ }
+ }
+
+ // Complain if we are converting a lambda expression to a boolean value
+ // outside of instantiation.
+ if (!inTemplateInstantiation()) {
+ if (const auto *MCallExpr = dyn_cast<CXXMemberCallExpr>(E)) {
+ if (const auto *MRecordDecl = MCallExpr->getRecordDecl();
+ MRecordDecl && MRecordDecl->isLambda()) {
+ Diag(E->getExprLoc(), diag::warn_impcast_pointer_to_bool)
+ << /*LambdaPointerConversionOperatorType=*/3
+ << MRecordDecl->getSourceRange() << Range << IsEqual;
+ return;
+ }
+ }
+ }
+
+ // Expect to find a single Decl. Skip anything more complicated.
+ ValueDecl *D = nullptr;
+ if (DeclRefExpr *R = dyn_cast<DeclRefExpr>(E)) {
+ D = R->getDecl();
+ } else if (MemberExpr *M = dyn_cast<MemberExpr>(E)) {
+ D = M->getMemberDecl();
+ }
+
+ // Weak Decls can be null.
+ if (!D || D->isWeak())
+ return;
+
+ // Check for parameter decl with nonnull attribute
+ if (const auto* PV = dyn_cast<ParmVarDecl>(D)) {
+ if (getCurFunction() &&
+ !getCurFunction()->ModifiedNonNullParams.count(PV)) {
+ if (const Attr *A = PV->getAttr<NonNullAttr>()) {
+ ComplainAboutNonnullParamOrCall(A);
+ return;
+ }
+
+ if (const auto *FD = dyn_cast<FunctionDecl>(PV->getDeclContext())) {
+ // Skip function template not specialized yet.
+ if (FD->getTemplatedKind() == FunctionDecl::TK_FunctionTemplate)
+ return;
+ auto ParamIter = llvm::find(FD->parameters(), PV);
+ assert(ParamIter != FD->param_end());
+ unsigned ParamNo = std::distance(FD->param_begin(), ParamIter);
+
+ for (const auto *NonNull : FD->specific_attrs<NonNullAttr>()) {
+ if (!NonNull->args_size()) {
+ ComplainAboutNonnullParamOrCall(NonNull);
+ return;
+ }
+
+ for (const ParamIdx &ArgNo : NonNull->args()) {
+ if (ArgNo.getASTIndex() == ParamNo) {
+ ComplainAboutNonnullParamOrCall(NonNull);
+ return;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ QualType T = D->getType();
+ const bool IsArray = T->isArrayType();
+ const bool IsFunction = T->isFunctionType();
+
+ // Address of function is used to silence the function warning.
+ if (IsAddressOf && IsFunction) {
+ return;
+ }
+
+ // Found nothing.
+ if (!IsAddressOf && !IsFunction && !IsArray)
+ return;
+
+ // Pretty print the expression for the diagnostic.
+ std::string Str;
+ llvm::raw_string_ostream S(Str);
+ E->printPretty(S, nullptr, getPrintingPolicy());
+
+ unsigned DiagID = IsCompare ? diag::warn_null_pointer_compare
+ : diag::warn_impcast_pointer_to_bool;
+ enum {
+ AddressOf,
+ FunctionPointer,
+ ArrayPointer
+ } DiagType;
+ if (IsAddressOf)
+ DiagType = AddressOf;
+ else if (IsFunction)
+ DiagType = FunctionPointer;
+ else if (IsArray)
+ DiagType = ArrayPointer;
+ else
+ llvm_unreachable("Could not determine diagnostic.");
+ Diag(E->getExprLoc(), DiagID) << DiagType << S.str() << E->getSourceRange()
+ << Range << IsEqual;
+
+ if (!IsFunction)
+ return;
+
+ // Suggest '&' to silence the function warning.
+ Diag(E->getExprLoc(), diag::note_function_warning_silence)
+ << FixItHint::CreateInsertion(E->getBeginLoc(), "&");
+
+ // Check to see if '()' fixit should be emitted.
+ QualType ReturnType;
+ UnresolvedSet<4> NonTemplateOverloads;
+ tryExprAsCall(*E, ReturnType, NonTemplateOverloads);
+ if (ReturnType.isNull())
+ return;
+
+ if (IsCompare) {
+ // There are two cases here. If there is null constant, the only suggest
+ // for a pointer return type. If the null is 0, then suggest if the return
+ // type is a pointer or an integer type.
+ if (!ReturnType->isPointerType()) {
+ if (NullKind == Expr::NPCK_ZeroExpression ||
+ NullKind == Expr::NPCK_ZeroLiteral) {
+ if (!ReturnType->isIntegerType())
+ return;
+ } else {
+ return;
+ }
+ }
+ } else { // !IsCompare
+ // For function to bool, only suggest if the function pointer has bool
+ // return type.
+ if (!ReturnType->isSpecificBuiltinType(BuiltinType::Bool))
+ return;
+ }
+ Diag(E->getExprLoc(), diag::note_function_to_function_call)
+ << FixItHint::CreateInsertion(getLocForEndOfToken(E->getEndLoc()), "()");
+}
+
+void Sema::CheckImplicitConversions(Expr *E, SourceLocation CC) {
+ // Don't diagnose in unevaluated contexts.
+ if (isUnevaluatedContext())
+ return;
+
+ // Don't diagnose for value- or type-dependent expressions.
+ if (E->isTypeDependent() || E->isValueDependent())
+ return;
+
+ // Check for array bounds violations in cases where the check isn't triggered
+ // elsewhere for other Expr types (like BinaryOperators), e.g. when an
+ // ArraySubscriptExpr is on the RHS of a variable initialization.
+ CheckArrayAccess(E);
+
+ // This is not the right CC for (e.g.) a variable initialization.
+ AnalyzeImplicitConversions(*this, E, CC);
+}
+
+void Sema::CheckBoolLikeConversion(Expr *E, SourceLocation CC) {
+ ::CheckBoolLikeConversion(*this, E, CC);
+}
+
+void Sema::CheckForIntOverflow (const Expr *E) {
+ // Use a work list to deal with nested struct initializers.
+ SmallVector<const Expr *, 2> Exprs(1, E);
+
+ do {
+ const Expr *OriginalE = Exprs.pop_back_val();
+ const Expr *E = OriginalE->IgnoreParenCasts();
+
+ if (isa<BinaryOperator, UnaryOperator>(E)) {
+ E->EvaluateForOverflow(Context);
+ continue;
+ }
+
+ if (const auto *InitList = dyn_cast<InitListExpr>(OriginalE))
+ Exprs.append(InitList->inits().begin(), InitList->inits().end());
+ else if (isa<ObjCBoxedExpr>(OriginalE))
+ E->EvaluateForOverflow(Context);
+ else if (const auto *Call = dyn_cast<CallExpr>(E))
+ Exprs.append(Call->arg_begin(), Call->arg_end());
+ else if (const auto *Message = dyn_cast<ObjCMessageExpr>(E))
+ Exprs.append(Message->arg_begin(), Message->arg_end());
+ else if (const auto *Construct = dyn_cast<CXXConstructExpr>(E))
+ Exprs.append(Construct->arg_begin(), Construct->arg_end());
+ else if (const auto *Temporary = dyn_cast<CXXBindTemporaryExpr>(E))
+ Exprs.push_back(Temporary->getSubExpr());
+ else if (const auto *Array = dyn_cast<ArraySubscriptExpr>(E))
+ Exprs.push_back(Array->getIdx());
+ else if (const auto *Compound = dyn_cast<CompoundLiteralExpr>(E))
+ Exprs.push_back(Compound->getInitializer());
+ else if (const auto *New = dyn_cast<CXXNewExpr>(E);
+ New && New->isArray()) {
+ if (auto ArraySize = New->getArraySize())
+ Exprs.push_back(*ArraySize);
+ } else if (const auto *MTE = dyn_cast<MaterializeTemporaryExpr>(OriginalE))
+ Exprs.push_back(MTE->getSubExpr());
+ } while (!Exprs.empty());
+}
+
+namespace {
+
+/// Visitor for expressions which looks for unsequenced operations on the
+/// same object.
+class SequenceChecker : public ConstEvaluatedExprVisitor<SequenceChecker> {
+ using Base = ConstEvaluatedExprVisitor<SequenceChecker>;
+
+ /// A tree of sequenced regions within an expression. Two regions are
+ /// unsequenced if one is an ancestor or a descendent of the other. When we
+ /// finish processing an expression with sequencing, such as a comma
+ /// expression, we fold its tree nodes into its parent, since they are
+ /// unsequenced with respect to nodes we will visit later.
+ class SequenceTree {
+ struct Value {
+ explicit Value(unsigned Parent) : Parent(Parent), Merged(false) {}
+ unsigned Parent : 31;
+ LLVM_PREFERRED_TYPE(bool)
+ unsigned Merged : 1;
+ };
+ SmallVector<Value, 8> Values;
+
+ public:
+ /// A region within an expression which may be sequenced with respect
+ /// to some other region.
+ class Seq {
+ friend class SequenceTree;
+
+ unsigned Index;
+
+ explicit Seq(unsigned N) : Index(N) {}
+
+ public:
+ Seq() : Index(0) {}
+ };
+
+ SequenceTree() { Values.push_back(Value(0)); }
+ Seq root() const { return Seq(0); }
+
+ /// Create a new sequence of operations, which is an unsequenced
+ /// subset of \p Parent. This sequence of operations is sequenced with
+ /// respect to other children of \p Parent.
+ Seq allocate(Seq Parent) {
+ Values.push_back(Value(Parent.Index));
+ return Seq(Values.size() - 1);
+ }
+
+ /// Merge a sequence of operations into its parent.
+ void merge(Seq S) {
+ Values[S.Index].Merged = true;
+ }
+
+ /// Determine whether two operations are unsequenced. This operation
+ /// is asymmetric: \p Cur should be the more recent sequence, and \p Old
+ /// should have been merged into its parent as appropriate.
+ bool isUnsequenced(Seq Cur, Seq Old) {
+ unsigned C = representative(Cur.Index);
+ unsigned Target = representative(Old.Index);
+ while (C >= Target) {
+ if (C == Target)
+ return true;
+ C = Values[C].Parent;
+ }
+ return false;
+ }
+
+ private:
+ /// Pick a representative for a sequence.
+ unsigned representative(unsigned K) {
+ if (Values[K].Merged)
+ // Perform path compression as we go.
+ return Values[K].Parent = representative(Values[K].Parent);
+ return K;
+ }
+ };
+
+ /// An object for which we can track unsequenced uses.
+ using Object = const ValueDecl *;
+
+ /// Different flavors of object usage which we track. We only track the
+ /// least-sequenced usage of each kind.
+ enum UsageKind {
+ /// A read of an object. Multiple unsequenced reads are OK.
+ UK_Use,
+
+ /// A modification of an object which is sequenced before the value
+ /// computation of the expression, such as ++n in C++.
+ UK_ModAsValue,
+
+ /// A modification of an object which is not sequenced before the value
+ /// computation of the expression, such as n++.
+ UK_ModAsSideEffect,
+
+ UK_Count = UK_ModAsSideEffect + 1
+ };
+
+ enum WarningKind {
+ /// A warning message is one for unsequenced use and modification
+ WK_UseAndMod,
+
+ /// A warning message is one for multiple unsequenced modification
+ WK_ModAndMod,
+
+ /// A warning message is one for multiple unsequenced volatile use
+ WK_Volatile_UseAndUse
+ };
+
+ /// Bundle together a sequencing region and the expression corresponding
+ /// to a specific usage. One Usage is stored for each usage kind in UsageInfo.
+ struct Usage {
+ const Expr *UsageExpr = nullptr;
+ SequenceTree::Seq Seq;
+
+ Usage() = default;
+ };
+
+ struct UsageInfo {
+ Usage Uses[UK_Count];
+
+ /// Have we issued a diagnostic for this object already?
+ bool Diagnosed = false;
+
+ UsageInfo();
+ };
+ using UsageInfoMap = llvm::SmallDenseMap<Object, UsageInfo, 16>;
+
+ Sema &SemaRef;
+
+ /// Sequenced regions within the expression.
+ SequenceTree Tree;
+
+ /// Declaration modifications and references which we have seen.
+ UsageInfoMap UsageMap;
+
+ /// The region we are currently within.
+ SequenceTree::Seq Region;
+
+ /// Filled in with declarations which were modified as a side-effect
+ /// (that is, post-increment operations).
+ SmallVectorImpl<std::pair<Object, Usage>> *ModAsSideEffect = nullptr;
+
+ /// Expressions to check later. We defer checking these to reduce
+ /// stack usage.
+ SmallVectorImpl<const Expr *> &WorkList;
+
+ /// RAII object wrapping the visitation of a sequenced subexpression of an
+ /// expression. At the end of this process, the side-effects of the evaluation
+ /// become sequenced with respect to the value computation of the result, so
+ /// we downgrade any UK_ModAsSideEffect within the evaluation to
+ /// UK_ModAsValue.
+ struct SequencedSubexpression {
+ SequencedSubexpression(SequenceChecker &Self)
+ : Self(Self), OldModAsSideEffect(Self.ModAsSideEffect) {
+ Self.ModAsSideEffect = &ModAsSideEffect;
+ }
+
+ ~SequencedSubexpression() {
+ for (const std::pair<Object, Usage> &M : llvm::reverse(ModAsSideEffect)) {
+ // Add a new usage with usage kind UK_ModAsValue, and then restore
+ // the previous usage with UK_ModAsSideEffect (thus clearing it if
+ // the previous one was empty).
+ UsageInfo &UI = Self.UsageMap[M.first];
+ auto &SideEffectUsage = UI.Uses[UK_ModAsSideEffect];
+ Self.addUsage(M.first, UI, SideEffectUsage.UsageExpr, UK_ModAsValue);
+ SideEffectUsage = M.second;
+ }
+ Self.ModAsSideEffect = OldModAsSideEffect;
+ }
+
+ SequenceChecker &Self;
+ SmallVector<std::pair<Object, Usage>, 4> ModAsSideEffect;
+ SmallVectorImpl<std::pair<Object, Usage>> *OldModAsSideEffect;
+ };
+
+ /// RAII object wrapping the visitation of a subexpression which we might
+ /// choose to evaluate as a constant. If any subexpression is evaluated and
+ /// found to be non-constant, this allows us to suppress the evaluation of
+ /// the outer expression.
+ class EvaluationTracker {
+ public:
+ EvaluationTracker(SequenceChecker &Self)
+ : Self(Self), Prev(Self.EvalTracker) {
+ Self.EvalTracker = this;
+ }
+
+ ~EvaluationTracker() {
+ Self.EvalTracker = Prev;
+ if (Prev)
+ Prev->EvalOK &= EvalOK;
+ }
+
+ bool evaluate(const Expr *E, bool &Result) {
+ if (!EvalOK || E->isValueDependent())
+ return false;
+ EvalOK = E->EvaluateAsBooleanCondition(
+ Result, Self.SemaRef.Context,
+ Self.SemaRef.isConstantEvaluatedContext());
+ return EvalOK;
+ }
+
+ private:
+ SequenceChecker &Self;
+ EvaluationTracker *Prev;
+ bool EvalOK = true;
+ } *EvalTracker = nullptr;
+
+ /// Find the object which is produced by the specified expression,
+ /// if any.
+ Object getObject(const Expr *E, bool Mod) const {
+ E = E->IgnoreParenCasts();
+ if (const UnaryOperator *UO = dyn_cast<UnaryOperator>(E)) {
+ if (Mod && (UO->getOpcode() == UO_PreInc || UO->getOpcode() == UO_PreDec))
+ return getObject(UO->getSubExpr(), Mod);
+ } else if (const BinaryOperator *BO = dyn_cast<BinaryOperator>(E)) {
+ if (BO->getOpcode() == BO_Comma)
+ return getObject(BO->getRHS(), Mod);
+ if (Mod && BO->isAssignmentOp())
+ return getObject(BO->getLHS(), Mod);
+ } else if (const MemberExpr *ME = dyn_cast<MemberExpr>(E)) {
+ // FIXME: Check for more interesting cases, like "x.n = ++x.n".
+ if (isa<CXXThisExpr>(ME->getBase()->IgnoreParenCasts()))
+ return ME->getMemberDecl();
+ } else if (const DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(E))
+ // FIXME: If this is a reference, map through to its value.
+ return DRE->getDecl();
+ return nullptr;
+ }
+
+ /// Note that an object \p O was modified or used by an expression
+ /// \p UsageExpr with usage kind \p UK. \p UI is the \p UsageInfo for
+ /// the object \p O as obtained via the \p UsageMap.
+ void addUsage(Object O, UsageInfo &UI, const Expr *UsageExpr, UsageKind UK) {
+ // Get the old usage for the given object and usage kind.
+ Usage &U = UI.Uses[UK];
+ if (!U.UsageExpr || !Tree.isUnsequenced(Region, U.Seq)) {
+ // If we have a modification as side effect and are in a sequenced
+ // subexpression, save the old Usage so that we can restore it later
+ // in SequencedSubexpression::~SequencedSubexpression.
+ if (UK == UK_ModAsSideEffect && ModAsSideEffect)
+ ModAsSideEffect->push_back(std::make_pair(O, U));
+ // Then record the new usage with the current sequencing region.
+ U.UsageExpr = UsageExpr;
+ U.Seq = Region;
+ }
+ }
+
+ /// Check whether a modification or use of an object \p O in an expression
+ /// \p UsageExpr conflicts with a prior usage of kind \p OtherKind. \p UI is
+ /// the \p UsageInfo for the object \p O as obtained via the \p UsageMap.
+ /// \p IsModMod is true when we are checking for a mod-mod unsequenced
+ /// usage and false we are checking for a mod-use unsequenced usage.
+ void checkUsage(Object O, UsageInfo &UI, const Expr *UsageExpr,
+ UsageKind OtherKind, WarningKind WarnKind) {
+ if (UI.Diagnosed)
+ return;
+
+ const Usage &U = UI.Uses[OtherKind];
+ if (!U.UsageExpr || !Tree.isUnsequenced(Region, U.Seq))
+ return;
+
+ const Expr *Mod = U.UsageExpr;
+ const Expr *ModOrUse = UsageExpr;
+ if (OtherKind == UK_Use)
+ std::swap(Mod, ModOrUse);
+
+ unsigned DiagID = 0;
+ switch (WarnKind) {
+ case WK_UseAndMod:
+ DiagID = diag::warn_unsequenced_mod_use;
+ break;
+ case WK_ModAndMod:
+ DiagID = diag::warn_unsequenced_mod_mod;
+ break;
+ case WK_Volatile_UseAndUse:
+ DiagID = diag::warn_unsequenced_use_use_volatile;
+ break;
+ }
+
+ SemaRef.DiagRuntimeBehavior(
+ Mod->getExprLoc(), {Mod, ModOrUse},
+ SemaRef.PDiag(DiagID) << O << SourceRange(ModOrUse->getExprLoc()));
+ UI.Diagnosed = true;
+ }
+
+ // A note on note{Pre, Post}{Use, Mod}:
+ //
+ // (It helps to follow the algorithm with an expression such as
+ // "((++k)++, k) = k" or "k = (k++, k++)". Both contain unsequenced
+ // operations before C++17 and both are well-defined in C++17).
+ //
+ // When visiting a node which uses/modify an object we first call notePreUse
+ // or notePreMod before visiting its sub-expression(s). At this point the
+ // children of the current node have not yet been visited and so the eventual
+ // uses/modifications resulting from the children of the current node have not
+ // been recorded yet.
+ //
+ // We then visit the children of the current node. After that notePostUse or
+ // notePostMod is called. These will 1) detect an unsequenced modification
+ // as side effect (as in "k++ + k") and 2) add a new usage with the
+ // appropriate usage kind.
+ //
+ // We also have to be careful that some operation sequences modification as
+ // side effect as well (for example: || or ,). To account for this we wrap
+ // the visitation of such a sub-expression (for example: the LHS of || or ,)
+ // with SequencedSubexpression. SequencedSubexpression is an RAII object
+ // which record usages which are modifications as side effect, and then
+ // downgrade them (or more accurately restore the previous usage which was a
+ // modification as side effect) when exiting the scope of the sequenced
+ // subexpression.
+
+ void notePreUse(Object O, const Expr *UseExpr) {
+ UsageInfo &UI = UsageMap[O];
+ // Uses conflict with other modifications.
+ checkUsage(O, UI, UseExpr, /*OtherKind=*/UK_ModAsValue, WK_UseAndMod);
+ // Volatile uses conflict with other uses.
+ if (O->getType().isVolatileQualified())
+ checkUsage(O, UI, UseExpr, /*OtherKind=*/UK_Use, WK_Volatile_UseAndUse);
+ }
+
+ void notePostUse(Object O, const Expr *UseExpr) {
+ UsageInfo &UI = UsageMap[O];
+ checkUsage(O, UI, UseExpr, /*OtherKind=*/UK_ModAsSideEffect, WK_UseAndMod);
+ addUsage(O, UI, UseExpr, /*UsageKind=*/UK_Use);
+ }
+
+ void notePreMod(Object O, const Expr *ModExpr) {
+ UsageInfo &UI = UsageMap[O];
+ // Modifications conflict with other modifications and with uses.
+ checkUsage(O, UI, ModExpr, /*OtherKind=*/UK_ModAsValue, WK_ModAndMod);
+ checkUsage(O, UI, ModExpr, /*OtherKind=*/UK_Use, WK_UseAndMod);
+ }
+
+ void notePostMod(Object O, const Expr *ModExpr, UsageKind UK) {
+ UsageInfo &UI = UsageMap[O];
+ checkUsage(O, UI, ModExpr, /*OtherKind=*/UK_ModAsSideEffect, WK_ModAndMod);
+ addUsage(O, UI, ModExpr, /*UsageKind=*/UK);
+ }
+
+public:
+ SequenceChecker(Sema &S, const Expr *E,
+ SmallVectorImpl<const Expr *> &WorkList)
+ : Base(S.Context), SemaRef(S), Region(Tree.root()), WorkList(WorkList) {
+ Visit(E);
+ // Silence a -Wunused-private-field since WorkList is now unused.
+ // TODO: Evaluate if it can be used, and if not remove it.
+ (void)this->WorkList;
+ }
+
+ void VisitStmt(const Stmt *S) {
+ // Skip all statements which aren't expressions for now.
+ }
+
+ void VisitExpr(const Expr *E) {
+ // By default, just recurse to evaluated subexpressions.
+ Base::VisitStmt(E);
+ }
+
+ void VisitCoroutineSuspendExpr(const CoroutineSuspendExpr *CSE) {
+ for (auto *Sub : CSE->children()) {
+ const Expr *ChildExpr = dyn_cast_or_null<Expr>(Sub);
+ if (!ChildExpr)
+ continue;
+
+ if (ChildExpr == CSE->getOperand())
+ // Do not recurse over a CoroutineSuspendExpr's operand.
+ // The operand is also a subexpression of getCommonExpr(), and
+ // recursing into it directly could confuse object management
+ // for the sake of sequence tracking.
+ continue;
+
+ Visit(Sub);
+ }
+ }
+
+ void VisitCastExpr(const CastExpr *E) {
+ Object O = Object();
+ if (E->getCastKind() == CK_LValueToRValue)
+ O = getObject(E->getSubExpr(), false);
+
+ if (O)
+ notePreUse(O, E);
+ VisitExpr(E);
+ if (O)
+ notePostUse(O, E);
+ }
+
+ void VisitSequencedExpressions(const Expr *SequencedBefore,
+ const Expr *SequencedAfter) {
+ SequenceTree::Seq BeforeRegion = Tree.allocate(Region);
+ SequenceTree::Seq AfterRegion = Tree.allocate(Region);
+ SequenceTree::Seq OldRegion = Region;
+
+ {
+ SequencedSubexpression SeqBefore(*this);
+ Region = BeforeRegion;
+ Visit(SequencedBefore);
+ }
+
+ Region = AfterRegion;
+ Visit(SequencedAfter);
+
+ Region = OldRegion;
+
+ Tree.merge(BeforeRegion);
+ Tree.merge(AfterRegion);
+ }
+
+ void VisitArraySubscriptExpr(const ArraySubscriptExpr *ASE) {
+ // C++17 [expr.sub]p1:
+ // The expression E1[E2] is identical (by definition) to *((E1)+(E2)). The
+ // expression E1 is sequenced before the expression E2.
+ if (SemaRef.getLangOpts().CPlusPlus17)
+ VisitSequencedExpressions(ASE->getLHS(), ASE->getRHS());
+ else {
+ Visit(ASE->getLHS());
+ Visit(ASE->getRHS());
+ }
+ }
+
+ void VisitBinPtrMemD(const BinaryOperator *BO) { VisitBinPtrMem(BO); }
+ void VisitBinPtrMemI(const BinaryOperator *BO) { VisitBinPtrMem(BO); }
+ void VisitBinPtrMem(const BinaryOperator *BO) {
+ // C++17 [expr.mptr.oper]p4:
+ // Abbreviating pm-expression.*cast-expression as E1.*E2, [...]
+ // the expression E1 is sequenced before the expression E2.
+ if (SemaRef.getLangOpts().CPlusPlus17)
+ VisitSequencedExpressions(BO->getLHS(), BO->getRHS());
+ else {
+ Visit(BO->getLHS());
+ Visit(BO->getRHS());
+ }
+ }
+
+ void VisitBinShl(const BinaryOperator *BO) { VisitBinShlShr(BO); }
+ void VisitBinShr(const BinaryOperator *BO) { VisitBinShlShr(BO); }
+ void VisitBinShlShr(const BinaryOperator *BO) {
+ // C++17 [expr.shift]p4:
+ // The expression E1 is sequenced before the expression E2.
+ if (SemaRef.getLangOpts().CPlusPlus17)
+ VisitSequencedExpressions(BO->getLHS(), BO->getRHS());
+ else {
+ Visit(BO->getLHS());
+ Visit(BO->getRHS());
+ }
+ }
+
+ void VisitBinComma(const BinaryOperator *BO) {
+ // C++11 [expr.comma]p1:
+ // Every value computation and side effect associated with the left
+ // expression is sequenced before every value computation and side
+ // effect associated with the right expression.
+ VisitSequencedExpressions(BO->getLHS(), BO->getRHS());
+ }
+
+ void VisitBinAssign(const BinaryOperator *BO) {
+ SequenceTree::Seq RHSRegion;
+ SequenceTree::Seq LHSRegion;
+ if (SemaRef.getLangOpts().CPlusPlus17) {
+ RHSRegion = Tree.allocate(Region);
+ LHSRegion = Tree.allocate(Region);
+ } else {
+ RHSRegion = Region;
+ LHSRegion = Region;
+ }
+ SequenceTree::Seq OldRegion = Region;
+
+ // C++11 [expr.ass]p1:
+ // [...] the assignment is sequenced after the value computation
+ // of the right and left operands, [...]
+ //
+ // so check it before inspecting the operands and update the
+ // map afterwards.
+ Object O = getObject(BO->getLHS(), /*Mod=*/true);
+ if (O)
+ notePreMod(O, BO);
+
+ if (SemaRef.getLangOpts().CPlusPlus17) {
+ // C++17 [expr.ass]p1:
+ // [...] The right operand is sequenced before the left operand. [...]
+ {
+ SequencedSubexpression SeqBefore(*this);
+ Region = RHSRegion;
+ Visit(BO->getRHS());
+ }
+
+ Region = LHSRegion;
+ Visit(BO->getLHS());
+
+ if (O && isa<CompoundAssignOperator>(BO))
+ notePostUse(O, BO);
+
+ } else {
+ // C++11 does not specify any sequencing between the LHS and RHS.
+ Region = LHSRegion;
+ Visit(BO->getLHS());
+
+ if (O && isa<CompoundAssignOperator>(BO))
+ notePostUse(O, BO);
+
+ Region = RHSRegion;
+ Visit(BO->getRHS());
+ }
+
+ // C++11 [expr.ass]p1:
+ // the assignment is sequenced [...] before the value computation of the
+ // assignment expression.
+ // C11 6.5.16/3 has no such rule.
+ Region = OldRegion;
+ if (O)
+ notePostMod(O, BO,
+ SemaRef.getLangOpts().CPlusPlus ? UK_ModAsValue
+ : UK_ModAsSideEffect);
+ if (SemaRef.getLangOpts().CPlusPlus17) {
+ Tree.merge(RHSRegion);
+ Tree.merge(LHSRegion);
+ }
+ }
+
+ void VisitCompoundAssignOperator(const CompoundAssignOperator *CAO) {
+ VisitBinAssign(CAO);
+ }
+
+ void VisitUnaryPreInc(const UnaryOperator *UO) { VisitUnaryPreIncDec(UO); }
+ void VisitUnaryPreDec(const UnaryOperator *UO) { VisitUnaryPreIncDec(UO); }
+ void VisitUnaryPreIncDec(const UnaryOperator *UO) {
+ Object O = getObject(UO->getSubExpr(), true);
+ if (!O)
+ return VisitExpr(UO);
+
+ notePreMod(O, UO);
+ Visit(UO->getSubExpr());
+ // C++11 [expr.pre.incr]p1:
+ // the expression ++x is equivalent to x+=1
+ notePostMod(O, UO,
+ SemaRef.getLangOpts().CPlusPlus ? UK_ModAsValue
+ : UK_ModAsSideEffect);
+ }
+
+ void VisitUnaryPostInc(const UnaryOperator *UO) { VisitUnaryPostIncDec(UO); }
+ void VisitUnaryPostDec(const UnaryOperator *UO) { VisitUnaryPostIncDec(UO); }
+ void VisitUnaryPostIncDec(const UnaryOperator *UO) {
+ Object O = getObject(UO->getSubExpr(), true);
+ if (!O)
+ return VisitExpr(UO);
+
+ notePreMod(O, UO);
+ Visit(UO->getSubExpr());
+ notePostMod(O, UO, UK_ModAsSideEffect);
+ }
+
+ void VisitBinLOr(const BinaryOperator *BO) {
+ // C++11 [expr.log.or]p2:
+ // If the second expression is evaluated, every value computation and
+ // side effect associated with the first expression is sequenced before
+ // every value computation and side effect associated with the
+ // second expression.
+ SequenceTree::Seq LHSRegion = Tree.allocate(Region);
+ SequenceTree::Seq RHSRegion = Tree.allocate(Region);
+ SequenceTree::Seq OldRegion = Region;
+
+ EvaluationTracker Eval(*this);
+ {
+ SequencedSubexpression Sequenced(*this);
+ Region = LHSRegion;
+ Visit(BO->getLHS());
+ }
+
+ // C++11 [expr.log.or]p1:
+ // [...] the second operand is not evaluated if the first operand
+ // evaluates to true.
+ bool EvalResult = false;
+ bool EvalOK = Eval.evaluate(BO->getLHS(), EvalResult);
+ bool ShouldVisitRHS = !EvalOK || !EvalResult;
+ if (ShouldVisitRHS) {
+ Region = RHSRegion;
+ Visit(BO->getRHS());
+ }
+
+ Region = OldRegion;
+ Tree.merge(LHSRegion);
+ Tree.merge(RHSRegion);
+ }
+
+ void VisitBinLAnd(const BinaryOperator *BO) {
+ // C++11 [expr.log.and]p2:
+ // If the second expression is evaluated, every value computation and
+ // side effect associated with the first expression is sequenced before
+ // every value computation and side effect associated with the
+ // second expression.
+ SequenceTree::Seq LHSRegion = Tree.allocate(Region);
+ SequenceTree::Seq RHSRegion = Tree.allocate(Region);
+ SequenceTree::Seq OldRegion = Region;
+
+ EvaluationTracker Eval(*this);
+ {
+ SequencedSubexpression Sequenced(*this);
+ Region = LHSRegion;
+ Visit(BO->getLHS());
+ }
+
+ // C++11 [expr.log.and]p1:
+ // [...] the second operand is not evaluated if the first operand is false.
+ bool EvalResult = false;
+ bool EvalOK = Eval.evaluate(BO->getLHS(), EvalResult);
+ bool ShouldVisitRHS = !EvalOK || EvalResult;
+ if (ShouldVisitRHS) {
+ Region = RHSRegion;
+ Visit(BO->getRHS());
+ }
+
+ Region = OldRegion;
+ Tree.merge(LHSRegion);
+ Tree.merge(RHSRegion);
+ }
+
+ void VisitAbstractConditionalOperator(const AbstractConditionalOperator *CO) {
+ // C++11 [expr.cond]p1:
+ // [...] Every value computation and side effect associated with the first
+ // expression is sequenced before every value computation and side effect
+ // associated with the second or third expression.
+ SequenceTree::Seq ConditionRegion = Tree.allocate(Region);
+
+ // No sequencing is specified between the true and false expression.
+ // However since exactly one of both is going to be evaluated we can
+ // consider them to be sequenced. This is needed to avoid warning on
+ // something like "x ? y+= 1 : y += 2;" in the case where we will visit
+ // both the true and false expressions because we can't evaluate x.
+ // This will still allow us to detect an expression like (pre C++17)
+ // "(x ? y += 1 : y += 2) = y".
+ //
+ // We don't wrap the visitation of the true and false expression with
+ // SequencedSubexpression because we don't want to downgrade modifications
+ // as side effect in the true and false expressions after the visition
+ // is done. (for example in the expression "(x ? y++ : y++) + y" we should
+ // not warn between the two "y++", but we should warn between the "y++"
+ // and the "y".
+ SequenceTree::Seq TrueRegion = Tree.allocate(Region);
+ SequenceTree::Seq FalseRegion = Tree.allocate(Region);
+ SequenceTree::Seq OldRegion = Region;
+
+ EvaluationTracker Eval(*this);
+ {
+ SequencedSubexpression Sequenced(*this);
+ Region = ConditionRegion;
+ Visit(CO->getCond());
+ }
+
+ // C++11 [expr.cond]p1:
+ // [...] The first expression is contextually converted to bool (Clause 4).
+ // It is evaluated and if it is true, the result of the conditional
+ // expression is the value of the second expression, otherwise that of the
+ // third expression. Only one of the second and third expressions is
+ // evaluated. [...]
+ bool EvalResult = false;
+ bool EvalOK = Eval.evaluate(CO->getCond(), EvalResult);
+ bool ShouldVisitTrueExpr = !EvalOK || EvalResult;
+ bool ShouldVisitFalseExpr = !EvalOK || !EvalResult;
+ if (ShouldVisitTrueExpr) {
+ Region = TrueRegion;
+ Visit(CO->getTrueExpr());
+ }
+ if (ShouldVisitFalseExpr) {
+ Region = FalseRegion;
+ Visit(CO->getFalseExpr());
+ }
+
+ Region = OldRegion;
+ Tree.merge(ConditionRegion);
+ Tree.merge(TrueRegion);
+ Tree.merge(FalseRegion);
+ }
+
+ void VisitCallExpr(const CallExpr *CE) {
+ // FIXME: CXXNewExpr and CXXDeleteExpr implicitly call functions.
+
+ if (CE->isUnevaluatedBuiltinCall(Context))
+ return;
+
+ // C++11 [intro.execution]p15:
+ // When calling a function [...], every value computation and side effect
+ // associated with any argument expression, or with the postfix expression
+ // designating the called function, is sequenced before execution of every
+ // expression or statement in the body of the function [and thus before
+ // the value computation of its result].
+ SequencedSubexpression Sequenced(*this);
+ SemaRef.runWithSufficientStackSpace(CE->getExprLoc(), [&] {
+ // C++17 [expr.call]p5
+ // The postfix-expression is sequenced before each expression in the
+ // expression-list and any default argument. [...]
+ SequenceTree::Seq CalleeRegion;
+ SequenceTree::Seq OtherRegion;
+ if (SemaRef.getLangOpts().CPlusPlus17) {
+ CalleeRegion = Tree.allocate(Region);
+ OtherRegion = Tree.allocate(Region);
+ } else {
+ CalleeRegion = Region;
+ OtherRegion = Region;
+ }
+ SequenceTree::Seq OldRegion = Region;
+
+ // Visit the callee expression first.
+ Region = CalleeRegion;
+ if (SemaRef.getLangOpts().CPlusPlus17) {
+ SequencedSubexpression Sequenced(*this);
+ Visit(CE->getCallee());
+ } else {
+ Visit(CE->getCallee());
+ }
+
+ // Then visit the argument expressions.
+ Region = OtherRegion;
+ for (const Expr *Argument : CE->arguments())
+ Visit(Argument);
+
+ Region = OldRegion;
+ if (SemaRef.getLangOpts().CPlusPlus17) {
+ Tree.merge(CalleeRegion);
+ Tree.merge(OtherRegion);
+ }
+ });
+ }
+
+ void VisitCXXOperatorCallExpr(const CXXOperatorCallExpr *CXXOCE) {
+ // C++17 [over.match.oper]p2:
+ // [...] the operator notation is first transformed to the equivalent
+ // function-call notation as summarized in Table 12 (where @ denotes one
+ // of the operators covered in the specified subclause). However, the
+ // operands are sequenced in the order prescribed for the built-in
+ // operator (Clause 8).
+ //
+ // From the above only overloaded binary operators and overloaded call
+ // operators have sequencing rules in C++17 that we need to handle
+ // separately.
+ if (!SemaRef.getLangOpts().CPlusPlus17 ||
+ (CXXOCE->getNumArgs() != 2 && CXXOCE->getOperator() != OO_Call))
+ return VisitCallExpr(CXXOCE);
+
+ enum {
+ NoSequencing,
+ LHSBeforeRHS,
+ RHSBeforeLHS,
+ LHSBeforeRest
+ } SequencingKind;
+ switch (CXXOCE->getOperator()) {
+ case OO_Equal:
+ case OO_PlusEqual:
+ case OO_MinusEqual:
+ case OO_StarEqual:
+ case OO_SlashEqual:
+ case OO_PercentEqual:
+ case OO_CaretEqual:
+ case OO_AmpEqual:
+ case OO_PipeEqual:
+ case OO_LessLessEqual:
+ case OO_GreaterGreaterEqual:
+ SequencingKind = RHSBeforeLHS;
+ break;
+
+ case OO_LessLess:
+ case OO_GreaterGreater:
+ case OO_AmpAmp:
+ case OO_PipePipe:
+ case OO_Comma:
+ case OO_ArrowStar:
+ case OO_Subscript:
+ SequencingKind = LHSBeforeRHS;
+ break;
+
+ case OO_Call:
+ SequencingKind = LHSBeforeRest;
+ break;
+
+ default:
+ SequencingKind = NoSequencing;
+ break;
+ }
+
+ if (SequencingKind == NoSequencing)
+ return VisitCallExpr(CXXOCE);
+
+ // This is a call, so all subexpressions are sequenced before the result.
+ SequencedSubexpression Sequenced(*this);
+
+ SemaRef.runWithSufficientStackSpace(CXXOCE->getExprLoc(), [&] {
+ assert(SemaRef.getLangOpts().CPlusPlus17 &&
+ "Should only get there with C++17 and above!");
+ assert((CXXOCE->getNumArgs() == 2 || CXXOCE->getOperator() == OO_Call) &&
+ "Should only get there with an overloaded binary operator"
+ " or an overloaded call operator!");
+
+ if (SequencingKind == LHSBeforeRest) {
+ assert(CXXOCE->getOperator() == OO_Call &&
+ "We should only have an overloaded call operator here!");
+
+ // This is very similar to VisitCallExpr, except that we only have the
+ // C++17 case. The postfix-expression is the first argument of the
+ // CXXOperatorCallExpr. The expressions in the expression-list, if any,
+ // are in the following arguments.
+ //
+ // Note that we intentionally do not visit the callee expression since
+ // it is just a decayed reference to a function.
+ SequenceTree::Seq PostfixExprRegion = Tree.allocate(Region);
+ SequenceTree::Seq ArgsRegion = Tree.allocate(Region);
+ SequenceTree::Seq OldRegion = Region;
+
+ assert(CXXOCE->getNumArgs() >= 1 &&
+ "An overloaded call operator must have at least one argument"
+ " for the postfix-expression!");
+ const Expr *PostfixExpr = CXXOCE->getArgs()[0];
+ llvm::ArrayRef<const Expr *> Args(CXXOCE->getArgs() + 1,
+ CXXOCE->getNumArgs() - 1);
+
+ // Visit the postfix-expression first.
+ {
+ Region = PostfixExprRegion;
+ SequencedSubexpression Sequenced(*this);
+ Visit(PostfixExpr);
+ }
+
+ // Then visit the argument expressions.
+ Region = ArgsRegion;
+ for (const Expr *Arg : Args)
+ Visit(Arg);
+
+ Region = OldRegion;
+ Tree.merge(PostfixExprRegion);
+ Tree.merge(ArgsRegion);
+ } else {
+ assert(CXXOCE->getNumArgs() == 2 &&
+ "Should only have two arguments here!");
+ assert((SequencingKind == LHSBeforeRHS ||
+ SequencingKind == RHSBeforeLHS) &&
+ "Unexpected sequencing kind!");
+
+ // We do not visit the callee expression since it is just a decayed
+ // reference to a function.
+ const Expr *E1 = CXXOCE->getArg(0);
+ const Expr *E2 = CXXOCE->getArg(1);
+ if (SequencingKind == RHSBeforeLHS)
+ std::swap(E1, E2);
+
+ return VisitSequencedExpressions(E1, E2);
+ }
+ });
+ }
+
+ void VisitCXXConstructExpr(const CXXConstructExpr *CCE) {
+ // This is a call, so all subexpressions are sequenced before the result.
+ SequencedSubexpression Sequenced(*this);
+
+ if (!CCE->isListInitialization())
+ return VisitExpr(CCE);
+
+ // In C++11, list initializations are sequenced.
+ SequenceExpressionsInOrder(
+ llvm::ArrayRef(CCE->getArgs(), CCE->getNumArgs()));
+ }
+
+ void VisitInitListExpr(const InitListExpr *ILE) {
+ if (!SemaRef.getLangOpts().CPlusPlus11)
+ return VisitExpr(ILE);
+
+ // In C++11, list initializations are sequenced.
+ SequenceExpressionsInOrder(ILE->inits());
+ }
+
+ void VisitCXXParenListInitExpr(const CXXParenListInitExpr *PLIE) {
+ // C++20 parenthesized list initializations are sequenced. See C++20
+ // [decl.init.general]p16.5 and [decl.init.general]p16.6.2.2.
+ SequenceExpressionsInOrder(PLIE->getInitExprs());
+ }
+
+private:
+ void SequenceExpressionsInOrder(ArrayRef<const Expr *> ExpressionList) {
+ SmallVector<SequenceTree::Seq, 32> Elts;
+ SequenceTree::Seq Parent = Region;
+ for (const Expr *E : ExpressionList) {
+ if (!E)
+ continue;
+ Region = Tree.allocate(Parent);
+ Elts.push_back(Region);
+ Visit(E);
+ }
+
+ // Forget that the initializers are sequenced.
+ Region = Parent;
+ for (unsigned I = 0; I < Elts.size(); ++I)
+ Tree.merge(Elts[I]);
+ }
+};
+
+SequenceChecker::UsageInfo::UsageInfo() = default;
+
+} // namespace
+
+void Sema::CheckUnsequencedOperations(const Expr *E) {
+ SmallVector<const Expr *, 8> WorkList;
+ WorkList.push_back(E);
+ while (!WorkList.empty()) {
+ const Expr *Item = WorkList.pop_back_val();
+ SequenceChecker(*this, Item, WorkList);
+ }
+}
+
+void Sema::CheckCompletedExpr(Expr *E, SourceLocation CheckLoc,
+ bool IsConstexpr) {
+ llvm::SaveAndRestore ConstantContext(isConstantEvaluatedOverride,
+ IsConstexpr || isa<ConstantExpr>(E));
+ CheckImplicitConversions(E, CheckLoc);
+ if (!E->isInstantiationDependent())
+ CheckUnsequencedOperations(E);
+ if (!IsConstexpr && !E->isValueDependent())
+ CheckForIntOverflow(E);
+}
+
+void Sema::CheckBitFieldInitialization(SourceLocation InitLoc,
+ FieldDecl *BitField,
+ Expr *Init) {
+ (void) AnalyzeBitFieldAssignment(*this, BitField, Init, InitLoc);
+}
+
+static void diagnoseArrayStarInParamType(Sema &S, QualType PType,
+ SourceLocation Loc) {
+ if (!PType->isVariablyModifiedType())
+ return;
+ if (const auto *PointerTy = dyn_cast<PointerType>(PType)) {
+ diagnoseArrayStarInParamType(S, PointerTy->getPointeeType(), Loc);
+ return;
+ }
+ if (const auto *ReferenceTy = dyn_cast<ReferenceType>(PType)) {
+ diagnoseArrayStarInParamType(S, ReferenceTy->getPointeeType(), Loc);
+ return;
+ }
+ if (const auto *ParenTy = dyn_cast<ParenType>(PType)) {
+ diagnoseArrayStarInParamType(S, ParenTy->getInnerType(), Loc);
+ return;
+ }
+
+ const ArrayType *AT = S.Context.getAsArrayType(PType);
+ if (!AT)
+ return;
+
+ if (AT->getSizeModifier() != ArraySizeModifier::Star) {
+ diagnoseArrayStarInParamType(S, AT->getElementType(), Loc);
+ return;
+ }
+
+ S.Diag(Loc, diag::err_array_star_in_function_definition);
+}
+
+bool Sema::CheckParmsForFunctionDef(ArrayRef<ParmVarDecl *> Parameters,
+ bool CheckParameterNames) {
+ bool HasInvalidParm = false;
+ for (ParmVarDecl *Param : Parameters) {
+ assert(Param && "null in a parameter list");
+ // C99 6.7.5.3p4: the parameters in a parameter type list in a
+ // function declarator that is part of a function definition of
+ // that function shall not have incomplete type.
+ //
+ // C++23 [dcl.fct.def.general]/p2
+ // The type of a parameter [...] for a function definition
+ // shall not be a (possibly cv-qualified) class type that is incomplete
+ // or abstract within the function body unless the function is deleted.
+ if (!Param->isInvalidDecl() &&
+ (RequireCompleteType(Param->getLocation(), Param->getType(),
+ diag::err_typecheck_decl_incomplete_type) ||
+ RequireNonAbstractType(Param->getBeginLoc(), Param->getOriginalType(),
+ diag::err_abstract_type_in_decl,
+ AbstractParamType))) {
+ Param->setInvalidDecl();
+ HasInvalidParm = true;
+ }
+
+ // C99 6.9.1p5: If the declarator includes a parameter type list, the
+ // declaration of each parameter shall include an identifier.
+ if (CheckParameterNames && Param->getIdentifier() == nullptr &&
+ !Param->isImplicit() && !getLangOpts().CPlusPlus) {
+ // Diagnose this as an extension in C17 and earlier.
+ if (!getLangOpts().C23)
+ Diag(Param->getLocation(), diag::ext_parameter_name_omitted_c23);
+ }
+
+ // C99 6.7.5.3p12:
+ // If the function declarator is not part of a definition of that
+ // function, parameters may have incomplete type and may use the [*]
+ // notation in their sequences of declarator specifiers to specify
+ // variable length array types.
+ QualType PType = Param->getOriginalType();
+ // FIXME: This diagnostic should point the '[*]' if source-location
+ // information is added for it.
+ diagnoseArrayStarInParamType(*this, PType, Param->getLocation());
+
+ // If the parameter is a c++ class type and it has to be destructed in the
+ // callee function, declare the destructor so that it can be called by the
+ // callee function. Do not perform any direct access check on the dtor here.
+ if (!Param->isInvalidDecl()) {
+ if (CXXRecordDecl *ClassDecl = Param->getType()->getAsCXXRecordDecl()) {
+ if (!ClassDecl->isInvalidDecl() &&
+ !ClassDecl->hasIrrelevantDestructor() &&
+ !ClassDecl->isDependentContext() &&
+ ClassDecl->isParamDestroyedInCallee()) {
+ CXXDestructorDecl *Destructor = LookupDestructor(ClassDecl);
+ MarkFunctionReferenced(Param->getLocation(), Destructor);
+ DiagnoseUseOfDecl(Destructor, Param->getLocation());
+ }
+ }
+ }
+
+ // Parameters with the pass_object_size attribute only need to be marked
+ // constant at function definitions. Because we lack information about
+ // whether we're on a declaration or definition when we're instantiating the
+ // attribute, we need to check for constness here.
+ if (const auto *Attr = Param->getAttr<PassObjectSizeAttr>())
+ if (!Param->getType().isConstQualified())
+ Diag(Param->getLocation(), diag::err_attribute_pointers_only)
+ << Attr->getSpelling() << 1;
+
+ // Check for parameter names shadowing fields from the class.
+ if (LangOpts.CPlusPlus && !Param->isInvalidDecl()) {
+ // The owning context for the parameter should be the function, but we
+ // want to see if this function's declaration context is a record.
+ DeclContext *DC = Param->getDeclContext();
+ if (DC && DC->isFunctionOrMethod()) {
+ if (auto *RD = dyn_cast<CXXRecordDecl>(DC->getParent()))
+ CheckShadowInheritedFields(Param->getLocation(), Param->getDeclName(),
+ RD, /*DeclIsField*/ false);
+ }
+ }
+
+ if (!Param->isInvalidDecl() &&
+ Param->getOriginalType()->isWebAssemblyTableType()) {
+ Param->setInvalidDecl();
+ HasInvalidParm = true;
+ Diag(Param->getLocation(), diag::err_wasm_table_as_function_parameter);
+ }
+ }
+
+ return HasInvalidParm;
+}
+
+std::optional<std::pair<
+ CharUnits, CharUnits>> static getBaseAlignmentAndOffsetFromPtr(const Expr
+ *E,
+ ASTContext
+ &Ctx);
+
+/// Compute the alignment and offset of the base class object given the
+/// derived-to-base cast expression and the alignment and offset of the derived
+/// class object.
+static std::pair<CharUnits, CharUnits>
+getDerivedToBaseAlignmentAndOffset(const CastExpr *CE, QualType DerivedType,
+ CharUnits BaseAlignment, CharUnits Offset,
+ ASTContext &Ctx) {
+ for (auto PathI = CE->path_begin(), PathE = CE->path_end(); PathI != PathE;
+ ++PathI) {
+ const CXXBaseSpecifier *Base = *PathI;
+ const CXXRecordDecl *BaseDecl = Base->getType()->getAsCXXRecordDecl();
+ if (Base->isVirtual()) {
+ // The complete object may have a lower alignment than the non-virtual
+ // alignment of the base, in which case the base may be misaligned. Choose
+ // the smaller of the non-virtual alignment and BaseAlignment, which is a
+ // conservative lower bound of the complete object alignment.
+ CharUnits NonVirtualAlignment =
+ Ctx.getASTRecordLayout(BaseDecl).getNonVirtualAlignment();
+ BaseAlignment = std::min(BaseAlignment, NonVirtualAlignment);
+ Offset = CharUnits::Zero();
+ } else {
+ const ASTRecordLayout &RL =
+ Ctx.getASTRecordLayout(DerivedType->getAsCXXRecordDecl());
+ Offset += RL.getBaseClassOffset(BaseDecl);
+ }
+ DerivedType = Base->getType();
+ }
+
+ return std::make_pair(BaseAlignment, Offset);
+}
+
+/// Compute the alignment and offset of a binary additive operator.
+static std::optional<std::pair<CharUnits, CharUnits>>
+getAlignmentAndOffsetFromBinAddOrSub(const Expr *PtrE, const Expr *IntE,
+ bool IsSub, ASTContext &Ctx) {
+ QualType PointeeType = PtrE->getType()->getPointeeType();
+
+ if (!PointeeType->isConstantSizeType())
+ return std::nullopt;
+
+ auto P = getBaseAlignmentAndOffsetFromPtr(PtrE, Ctx);
+
+ if (!P)
+ return std::nullopt;
+
+ CharUnits EltSize = Ctx.getTypeSizeInChars(PointeeType);
+ if (std::optional<llvm::APSInt> IdxRes = IntE->getIntegerConstantExpr(Ctx)) {
+ CharUnits Offset = EltSize * IdxRes->getExtValue();
+ if (IsSub)
+ Offset = -Offset;
+ return std::make_pair(P->first, P->second + Offset);
+ }
+
+ // If the integer expression isn't a constant expression, compute the lower
+ // bound of the alignment using the alignment and offset of the pointer
+ // expression and the element size.
+ return std::make_pair(
+ P->first.alignmentAtOffset(P->second).alignmentAtOffset(EltSize),
+ CharUnits::Zero());
+}
+
+/// This helper function takes an lvalue expression and returns the alignment of
+/// a VarDecl and a constant offset from the VarDecl.
+std::optional<std::pair<
+ CharUnits,
+ CharUnits>> static getBaseAlignmentAndOffsetFromLValue(const Expr *E,
+ ASTContext &Ctx) {
+ E = E->IgnoreParens();
+ switch (E->getStmtClass()) {
+ default:
+ break;
+ case Stmt::CStyleCastExprClass:
+ case Stmt::CXXStaticCastExprClass:
+ case Stmt::ImplicitCastExprClass: {
+ auto *CE = cast<CastExpr>(E);
+ const Expr *From = CE->getSubExpr();
+ switch (CE->getCastKind()) {
+ default:
+ break;
+ case CK_NoOp:
+ return getBaseAlignmentAndOffsetFromLValue(From, Ctx);
+ case CK_UncheckedDerivedToBase:
+ case CK_DerivedToBase: {
+ auto P = getBaseAlignmentAndOffsetFromLValue(From, Ctx);
+ if (!P)
+ break;
+ return getDerivedToBaseAlignmentAndOffset(CE, From->getType(), P->first,
+ P->second, Ctx);
+ }
+ }
+ break;
+ }
+ case Stmt::ArraySubscriptExprClass: {
+ auto *ASE = cast<ArraySubscriptExpr>(E);
+ return getAlignmentAndOffsetFromBinAddOrSub(ASE->getBase(), ASE->getIdx(),
+ false, Ctx);
+ }
+ case Stmt::DeclRefExprClass: {
+ if (auto *VD = dyn_cast<VarDecl>(cast<DeclRefExpr>(E)->getDecl())) {
+ // FIXME: If VD is captured by copy or is an escaping __block variable,
+ // use the alignment of VD's type.
+ if (!VD->getType()->isReferenceType()) {
+ // Dependent alignment cannot be resolved -> bail out.
+ if (VD->hasDependentAlignment())
+ break;
+ return std::make_pair(Ctx.getDeclAlign(VD), CharUnits::Zero());
+ }
+ if (VD->hasInit())
+ return getBaseAlignmentAndOffsetFromLValue(VD->getInit(), Ctx);
+ }
+ break;
+ }
+ case Stmt::MemberExprClass: {
+ auto *ME = cast<MemberExpr>(E);
+ auto *FD = dyn_cast<FieldDecl>(ME->getMemberDecl());
+ if (!FD || FD->getType()->isReferenceType() ||
+ FD->getParent()->isInvalidDecl())
+ break;
+ std::optional<std::pair<CharUnits, CharUnits>> P;
+ if (ME->isArrow())
+ P = getBaseAlignmentAndOffsetFromPtr(ME->getBase(), Ctx);
+ else
+ P = getBaseAlignmentAndOffsetFromLValue(ME->getBase(), Ctx);
+ if (!P)
+ break;
+ const ASTRecordLayout &Layout = Ctx.getASTRecordLayout(FD->getParent());
+ uint64_t Offset = Layout.getFieldOffset(FD->getFieldIndex());
+ return std::make_pair(P->first,
+ P->second + CharUnits::fromQuantity(Offset));
+ }
+ case Stmt::UnaryOperatorClass: {
+ auto *UO = cast<UnaryOperator>(E);
+ switch (UO->getOpcode()) {
+ default:
+ break;
+ case UO_Deref:
+ return getBaseAlignmentAndOffsetFromPtr(UO->getSubExpr(), Ctx);
+ }
+ break;
+ }
+ case Stmt::BinaryOperatorClass: {
+ auto *BO = cast<BinaryOperator>(E);
+ auto Opcode = BO->getOpcode();
+ switch (Opcode) {
+ default:
+ break;
+ case BO_Comma:
+ return getBaseAlignmentAndOffsetFromLValue(BO->getRHS(), Ctx);
+ }
+ break;
+ }
+ }
+ return std::nullopt;
+}
+
+/// This helper function takes a pointer expression and returns the alignment of
+/// a VarDecl and a constant offset from the VarDecl.
+std::optional<std::pair<
+ CharUnits, CharUnits>> static getBaseAlignmentAndOffsetFromPtr(const Expr
+ *E,
+ ASTContext
+ &Ctx) {
+ E = E->IgnoreParens();
+ switch (E->getStmtClass()) {
+ default:
+ break;
+ case Stmt::CStyleCastExprClass:
+ case Stmt::CXXStaticCastExprClass:
+ case Stmt::ImplicitCastExprClass: {
+ auto *CE = cast<CastExpr>(E);
+ const Expr *From = CE->getSubExpr();
+ switch (CE->getCastKind()) {
+ default:
+ break;
+ case CK_NoOp:
+ return getBaseAlignmentAndOffsetFromPtr(From, Ctx);
+ case CK_ArrayToPointerDecay:
+ return getBaseAlignmentAndOffsetFromLValue(From, Ctx);
+ case CK_UncheckedDerivedToBase:
+ case CK_DerivedToBase: {
+ auto P = getBaseAlignmentAndOffsetFromPtr(From, Ctx);
+ if (!P)
+ break;
+ return getDerivedToBaseAlignmentAndOffset(
+ CE, From->getType()->getPointeeType(), P->first, P->second, Ctx);
+ }
+ }
+ break;
+ }
+ case Stmt::CXXThisExprClass: {
+ auto *RD = E->getType()->getPointeeType()->getAsCXXRecordDecl();
+ CharUnits Alignment = Ctx.getASTRecordLayout(RD).getNonVirtualAlignment();
+ return std::make_pair(Alignment, CharUnits::Zero());
+ }
+ case Stmt::UnaryOperatorClass: {
+ auto *UO = cast<UnaryOperator>(E);
+ if (UO->getOpcode() == UO_AddrOf)
+ return getBaseAlignmentAndOffsetFromLValue(UO->getSubExpr(), Ctx);
+ break;
+ }
+ case Stmt::BinaryOperatorClass: {
+ auto *BO = cast<BinaryOperator>(E);
+ auto Opcode = BO->getOpcode();
+ switch (Opcode) {
+ default:
+ break;
+ case BO_Add:
+ case BO_Sub: {
+ const Expr *LHS = BO->getLHS(), *RHS = BO->getRHS();
+ if (Opcode == BO_Add && !RHS->getType()->isIntegralOrEnumerationType())
+ std::swap(LHS, RHS);
+ return getAlignmentAndOffsetFromBinAddOrSub(LHS, RHS, Opcode == BO_Sub,
+ Ctx);
+ }
+ case BO_Comma:
+ return getBaseAlignmentAndOffsetFromPtr(BO->getRHS(), Ctx);
+ }
+ break;
+ }
+ }
+ return std::nullopt;
+}
+
+static CharUnits getPresumedAlignmentOfPointer(const Expr *E, Sema &S) {
+ // See if we can compute the alignment of a VarDecl and an offset from it.
+ std::optional<std::pair<CharUnits, CharUnits>> P =
+ getBaseAlignmentAndOffsetFromPtr(E, S.Context);
+
+ if (P)
+ return P->first.alignmentAtOffset(P->second);
+
+ // If that failed, return the type's alignment.
+ return S.Context.getTypeAlignInChars(E->getType()->getPointeeType());
+}
+
+void Sema::CheckCastAlign(Expr *Op, QualType T, SourceRange TRange) {
+ // This is actually a lot of work to potentially be doing on every
+ // cast; don't do it if we're ignoring -Wcast_align (as is the default).
+ if (getDiagnostics().isIgnored(diag::warn_cast_align, TRange.getBegin()))
+ return;
+
+ // Ignore dependent types.
+ if (T->isDependentType() || Op->getType()->isDependentType())
+ return;
+
+ // Require that the destination be a pointer type.
+ const PointerType *DestPtr = T->getAs<PointerType>();
+ if (!DestPtr) return;
+
+ // If the destination has alignment 1, we're done.
+ QualType DestPointee = DestPtr->getPointeeType();
+ if (DestPointee->isIncompleteType()) return;
+ CharUnits DestAlign = Context.getTypeAlignInChars(DestPointee);
+ if (DestAlign.isOne()) return;
+
+ // Require that the source be a pointer type.
+ const PointerType *SrcPtr = Op->getType()->getAs<PointerType>();
+ if (!SrcPtr) return;
+ QualType SrcPointee = SrcPtr->getPointeeType();
+
+ // Explicitly allow casts from cv void*. We already implicitly
+ // allowed casts to cv void*, since they have alignment 1.
+ // Also allow casts involving incomplete types, which implicitly
+ // includes 'void'.
+ if (SrcPointee->isIncompleteType()) return;
+
+ CharUnits SrcAlign = getPresumedAlignmentOfPointer(Op, *this);
+
+ if (SrcAlign >= DestAlign) return;
+
+ Diag(TRange.getBegin(), diag::warn_cast_align)
+ << Op->getType() << T
+ << static_cast<unsigned>(SrcAlign.getQuantity())
+ << static_cast<unsigned>(DestAlign.getQuantity())
+ << TRange << Op->getSourceRange();
+}
+
+void Sema::CheckArrayAccess(const Expr *BaseExpr, const Expr *IndexExpr,
+ const ArraySubscriptExpr *ASE,
+ bool AllowOnePastEnd, bool IndexNegated) {
+ // Already diagnosed by the constant evaluator.
+ if (isConstantEvaluatedContext())
+ return;
+
+ IndexExpr = IndexExpr->IgnoreParenImpCasts();
+ if (IndexExpr->isValueDependent())
+ return;
+
+ const Type *EffectiveType =
+ BaseExpr->getType()->getPointeeOrArrayElementType();
+ BaseExpr = BaseExpr->IgnoreParenCasts();
+ const ConstantArrayType *ArrayTy =
+ Context.getAsConstantArrayType(BaseExpr->getType());
+
+ LangOptions::StrictFlexArraysLevelKind
+ StrictFlexArraysLevel = getLangOpts().getStrictFlexArraysLevel();
+
+ const Type *BaseType =
+ ArrayTy == nullptr ? nullptr : ArrayTy->getElementType().getTypePtr();
+ bool IsUnboundedArray =
+ BaseType == nullptr || BaseExpr->isFlexibleArrayMemberLike(
+ Context, StrictFlexArraysLevel,
+ /*IgnoreTemplateOrMacroSubstitution=*/true);
+ if (EffectiveType->isDependentType() ||
+ (!IsUnboundedArray && BaseType->isDependentType()))
+ return;
+
+ Expr::EvalResult Result;
+ if (!IndexExpr->EvaluateAsInt(Result, Context, Expr::SE_AllowSideEffects))
+ return;
+
+ llvm::APSInt index = Result.Val.getInt();
+ if (IndexNegated) {
+ index.setIsUnsigned(false);
+ index = -index;
+ }
+
+ if (IsUnboundedArray) {
+ if (EffectiveType->isFunctionType())
+ return;
+ if (index.isUnsigned() || !index.isNegative()) {
+ const auto &ASTC = getASTContext();
+ unsigned AddrBits = ASTC.getTargetInfo().getPointerWidth(
+ EffectiveType->getCanonicalTypeInternal().getAddressSpace());
+ if (index.getBitWidth() < AddrBits)
+ index = index.zext(AddrBits);
+ std::optional<CharUnits> ElemCharUnits =
+ ASTC.getTypeSizeInCharsIfKnown(EffectiveType);
+ // PR50741 - If EffectiveType has unknown size (e.g., if it's a void
+ // pointer) bounds-checking isn't meaningful.
+ if (!ElemCharUnits || ElemCharUnits->isZero())
+ return;
+ llvm::APInt ElemBytes(index.getBitWidth(), ElemCharUnits->getQuantity());
+ // If index has more active bits than address space, we already know
+ // we have a bounds violation to warn about. Otherwise, compute
+ // address of (index + 1)th element, and warn about bounds violation
+ // only if that address exceeds address space.
+ if (index.getActiveBits() <= AddrBits) {
+ bool Overflow;
+ llvm::APInt Product(index);
+ Product += 1;
+ Product = Product.umul_ov(ElemBytes, Overflow);
+ if (!Overflow && Product.getActiveBits() <= AddrBits)
+ return;
+ }
+
+ // Need to compute max possible elements in address space, since that
+ // is included in diag message.
+ llvm::APInt MaxElems = llvm::APInt::getMaxValue(AddrBits);
+ MaxElems = MaxElems.zext(std::max(AddrBits + 1, ElemBytes.getBitWidth()));
+ MaxElems += 1;
+ ElemBytes = ElemBytes.zextOrTrunc(MaxElems.getBitWidth());
+ MaxElems = MaxElems.udiv(ElemBytes);
+
+ unsigned DiagID =
+ ASE ? diag::warn_array_index_exceeds_max_addressable_bounds
+ : diag::warn_ptr_arith_exceeds_max_addressable_bounds;
+
+ // Diag message shows element size in bits and in "bytes" (platform-
+ // dependent CharUnits)
+ DiagRuntimeBehavior(BaseExpr->getBeginLoc(), BaseExpr,
+ PDiag(DiagID) << index << AddrBits
+ << (unsigned)ASTC.toBits(*ElemCharUnits)
+ << ElemBytes << MaxElems
+ << MaxElems.getZExtValue()
+ << IndexExpr->getSourceRange());
+
+ const NamedDecl *ND = nullptr;
+ // Try harder to find a NamedDecl to point at in the note.
+ while (const auto *ASE = dyn_cast<ArraySubscriptExpr>(BaseExpr))
+ BaseExpr = ASE->getBase()->IgnoreParenCasts();
+ if (const auto *DRE = dyn_cast<DeclRefExpr>(BaseExpr))
+ ND = DRE->getDecl();
+ if (const auto *ME = dyn_cast<MemberExpr>(BaseExpr))
+ ND = ME->getMemberDecl();
+
+ if (ND)
+ DiagRuntimeBehavior(ND->getBeginLoc(), BaseExpr,
+ PDiag(diag::note_array_declared_here) << ND);
+ }
+ return;
+ }
+
+ if (index.isUnsigned() || !index.isNegative()) {
+ // It is possible that the type of the base expression after
+ // IgnoreParenCasts is incomplete, even though the type of the base
+ // expression before IgnoreParenCasts is complete (see PR39746 for an
+ // example). In this case we have no information about whether the array
+ // access exceeds the array bounds. However we can still diagnose an array
+ // access which precedes the array bounds.
+ if (BaseType->isIncompleteType())
+ return;
+
+ llvm::APInt size = ArrayTy->getSize();
+
+ if (BaseType != EffectiveType) {
+ // Make sure we're comparing apples to apples when comparing index to
+ // size.
+ uint64_t ptrarith_typesize = Context.getTypeSize(EffectiveType);
+ uint64_t array_typesize = Context.getTypeSize(BaseType);
+
+ // Handle ptrarith_typesize being zero, such as when casting to void*.
+ // Use the size in bits (what "getTypeSize()" returns) rather than bytes.
+ if (!ptrarith_typesize)
+ ptrarith_typesize = Context.getCharWidth();
+
+ if (ptrarith_typesize != array_typesize) {
+ // There's a cast to a different size type involved.
+ uint64_t ratio = array_typesize / ptrarith_typesize;
+
+ // TODO: Be smarter about handling cases where array_typesize is not a
+ // multiple of ptrarith_typesize.
+ if (ptrarith_typesize * ratio == array_typesize)
+ size *= llvm::APInt(size.getBitWidth(), ratio);
+ }
+ }
+
+ if (size.getBitWidth() > index.getBitWidth())
+ index = index.zext(size.getBitWidth());
+ else if (size.getBitWidth() < index.getBitWidth())
+ size = size.zext(index.getBitWidth());
+
+ // For array subscripting the index must be less than size, but for pointer
+ // arithmetic also allow the index (offset) to be equal to size since
+ // computing the next address after the end of the array is legal and
+ // commonly done e.g. in C++ iterators and range-based for loops.
+ if (AllowOnePastEnd ? index.ule(size) : index.ult(size))
+ return;
+
+ // Suppress the warning if the subscript expression (as identified by the
+ // ']' location) and the index expression are both from macro expansions
+ // within a system header.
+ if (ASE) {
+ SourceLocation RBracketLoc = SourceMgr.getSpellingLoc(
+ ASE->getRBracketLoc());
+ if (SourceMgr.isInSystemHeader(RBracketLoc)) {
+ SourceLocation IndexLoc =
+ SourceMgr.getSpellingLoc(IndexExpr->getBeginLoc());
+ if (SourceMgr.isWrittenInSameFile(RBracketLoc, IndexLoc))
+ return;
+ }
+ }
+
+ unsigned DiagID = ASE ? diag::warn_array_index_exceeds_bounds
+ : diag::warn_ptr_arith_exceeds_bounds;
+ unsigned CastMsg = (!ASE || BaseType == EffectiveType) ? 0 : 1;
+ QualType CastMsgTy = ASE ? ASE->getLHS()->getType() : QualType();
+
+ DiagRuntimeBehavior(BaseExpr->getBeginLoc(), BaseExpr,
+ PDiag(DiagID)
+ << index << ArrayTy->desugar() << CastMsg
+ << CastMsgTy << IndexExpr->getSourceRange());
+ } else {
+ unsigned DiagID = diag::warn_array_index_precedes_bounds;
+ if (!ASE) {
+ DiagID = diag::warn_ptr_arith_precedes_bounds;
+ if (index.isNegative()) index = -index;
+ }
+
+ DiagRuntimeBehavior(BaseExpr->getBeginLoc(), BaseExpr,
+ PDiag(DiagID) << index << IndexExpr->getSourceRange());
+ }
+
+ const NamedDecl *ND = nullptr;
+ // Try harder to find a NamedDecl to point at in the note.
+ while (const auto *ASE = dyn_cast<ArraySubscriptExpr>(BaseExpr))
+ BaseExpr = ASE->getBase()->IgnoreParenCasts();
+ if (const auto *DRE = dyn_cast<DeclRefExpr>(BaseExpr))
+ ND = DRE->getDecl();
+ if (const auto *ME = dyn_cast<MemberExpr>(BaseExpr))
+ ND = ME->getMemberDecl();
+
+ if (ND)
+ DiagRuntimeBehavior(ND->getBeginLoc(), BaseExpr,
+ PDiag(diag::note_array_declared_here) << ND);
+}
+
+void Sema::CheckArrayAccess(const Expr *expr) {
+ int AllowOnePastEnd = 0;
+ while (expr) {
+ expr = expr->IgnoreParenImpCasts();
+ switch (expr->getStmtClass()) {
+ case Stmt::ArraySubscriptExprClass: {
+ const ArraySubscriptExpr *ASE = cast<ArraySubscriptExpr>(expr);
+ CheckArrayAccess(ASE->getBase(), ASE->getIdx(), ASE,
+ AllowOnePastEnd > 0);
+ expr = ASE->getBase();
+ break;
+ }
+ case Stmt::MemberExprClass: {
+ expr = cast<MemberExpr>(expr)->getBase();
+ break;
+ }
+ case Stmt::CXXMemberCallExprClass: {
+ expr = cast<CXXMemberCallExpr>(expr)->getImplicitObjectArgument();
+ break;
+ }
+ case Stmt::ArraySectionExprClass: {
+ const ArraySectionExpr *ASE = cast<ArraySectionExpr>(expr);
+ // FIXME: We should probably be checking all of the elements to the
+ // 'length' here as well.
+ if (ASE->getLowerBound())
+ CheckArrayAccess(ASE->getBase(), ASE->getLowerBound(),
+ /*ASE=*/nullptr, AllowOnePastEnd > 0);
+ return;
+ }
+ case Stmt::UnaryOperatorClass: {
+ // Only unwrap the * and & unary operators
+ const UnaryOperator *UO = cast<UnaryOperator>(expr);
+ expr = UO->getSubExpr();
+ switch (UO->getOpcode()) {
+ case UO_AddrOf:
+ AllowOnePastEnd++;
+ break;
+ case UO_Deref:
+ AllowOnePastEnd--;
+ break;
+ default:
+ return;
+ }
+ break;
+ }
+ case Stmt::ConditionalOperatorClass: {
+ const ConditionalOperator *cond = cast<ConditionalOperator>(expr);
+ if (const Expr *lhs = cond->getLHS())
+ CheckArrayAccess(lhs);
+ if (const Expr *rhs = cond->getRHS())
+ CheckArrayAccess(rhs);
+ return;
+ }
+ case Stmt::CXXOperatorCallExprClass: {
+ const auto *OCE = cast<CXXOperatorCallExpr>(expr);
+ for (const auto *Arg : OCE->arguments())
+ CheckArrayAccess(Arg);
+ return;
+ }
+ default:
+ return;
+ }
+ }
+}
+
+static bool checkUnsafeAssignLiteral(Sema &S, SourceLocation Loc,
+ Expr *RHS, bool isProperty) {
+ // Check if RHS is an Objective-C object literal, which also can get
+ // immediately zapped in a weak reference. Note that we explicitly
+ // allow ObjCStringLiterals, since those are designed to never really die.
+ RHS = RHS->IgnoreParenImpCasts();
+
+ // This enum needs to match with the 'select' in
+ // warn_objc_arc_literal_assign (off-by-1).
+ SemaObjC::ObjCLiteralKind Kind = S.ObjC().CheckLiteralKind(RHS);
+ if (Kind == SemaObjC::LK_String || Kind == SemaObjC::LK_None)
+ return false;
+
+ S.Diag(Loc, diag::warn_arc_literal_assign)
+ << (unsigned) Kind
+ << (isProperty ? 0 : 1)
+ << RHS->getSourceRange();
+
+ return true;
+}
+
+static bool checkUnsafeAssignObject(Sema &S, SourceLocation Loc,
+ Qualifiers::ObjCLifetime LT,
+ Expr *RHS, bool isProperty) {
+ // Strip off any implicit cast added to get to the one ARC-specific.
+ while (ImplicitCastExpr *cast = dyn_cast<ImplicitCastExpr>(RHS)) {
+ if (cast->getCastKind() == CK_ARCConsumeObject) {
+ S.Diag(Loc, diag::warn_arc_retained_assign)
+ << (LT == Qualifiers::OCL_ExplicitNone)
+ << (isProperty ? 0 : 1)
+ << RHS->getSourceRange();
+ return true;
+ }
+ RHS = cast->getSubExpr();
+ }
+
+ if (LT == Qualifiers::OCL_Weak &&
+ checkUnsafeAssignLiteral(S, Loc, RHS, isProperty))
+ return true;
+
+ return false;
+}
+
+bool Sema::checkUnsafeAssigns(SourceLocation Loc,
+ QualType LHS, Expr *RHS) {
+ Qualifiers::ObjCLifetime LT = LHS.getObjCLifetime();
+
+ if (LT != Qualifiers::OCL_Weak && LT != Qualifiers::OCL_ExplicitNone)
+ return false;
+
+ if (checkUnsafeAssignObject(*this, Loc, LT, RHS, false))
+ return true;
+
+ return false;
+}
+
+void Sema::checkUnsafeExprAssigns(SourceLocation Loc,
+ Expr *LHS, Expr *RHS) {
+ QualType LHSType;
+ // PropertyRef on LHS type need be directly obtained from
+ // its declaration as it has a PseudoType.
+ ObjCPropertyRefExpr *PRE
+ = dyn_cast<ObjCPropertyRefExpr>(LHS->IgnoreParens());
+ if (PRE && !PRE->isImplicitProperty()) {
+ const ObjCPropertyDecl *PD = PRE->getExplicitProperty();
+ if (PD)
+ LHSType = PD->getType();
+ }
+
+ if (LHSType.isNull())
+ LHSType = LHS->getType();
+
+ Qualifiers::ObjCLifetime LT = LHSType.getObjCLifetime();
+
+ if (LT == Qualifiers::OCL_Weak) {
+ if (!Diags.isIgnored(diag::warn_arc_repeated_use_of_weak, Loc))
+ getCurFunction()->markSafeWeakUse(LHS);
+ }
+
+ if (checkUnsafeAssigns(Loc, LHSType, RHS))
+ return;
+
+ // FIXME. Check for other life times.
+ if (LT != Qualifiers::OCL_None)
+ return;
+
+ if (PRE) {
+ if (PRE->isImplicitProperty())
+ return;
+ const ObjCPropertyDecl *PD = PRE->getExplicitProperty();
+ if (!PD)
+ return;
+
+ unsigned Attributes = PD->getPropertyAttributes();
+ if (Attributes & ObjCPropertyAttribute::kind_assign) {
+ // when 'assign' attribute was not explicitly specified
+ // by user, ignore it and rely on property type itself
+ // for lifetime info.
+ unsigned AsWrittenAttr = PD->getPropertyAttributesAsWritten();
+ if (!(AsWrittenAttr & ObjCPropertyAttribute::kind_assign) &&
+ LHSType->isObjCRetainableType())
+ return;
+
+ while (ImplicitCastExpr *cast = dyn_cast<ImplicitCastExpr>(RHS)) {
+ if (cast->getCastKind() == CK_ARCConsumeObject) {
+ Diag(Loc, diag::warn_arc_retained_property_assign)
+ << RHS->getSourceRange();
+ return;
+ }
+ RHS = cast->getSubExpr();
+ }
+ } else if (Attributes & ObjCPropertyAttribute::kind_weak) {
+ if (checkUnsafeAssignObject(*this, Loc, Qualifiers::OCL_Weak, RHS, true))
+ return;
+ }
+ }
+}
+
+//===--- CHECK: Empty statement body (-Wempty-body) ---------------------===//
+
+static bool ShouldDiagnoseEmptyStmtBody(const SourceManager &SourceMgr,
+ SourceLocation StmtLoc,
+ const NullStmt *Body) {
+ // Do not warn if the body is a macro that expands to nothing, e.g:
+ //
+ // #define CALL(x)
+ // if (condition)
+ // CALL(0);
+ if (Body->hasLeadingEmptyMacro())
+ return false;
+
+ // Get line numbers of statement and body.
+ bool StmtLineInvalid;
+ unsigned StmtLine = SourceMgr.getPresumedLineNumber(StmtLoc,
+ &StmtLineInvalid);
+ if (StmtLineInvalid)
+ return false;
+
+ bool BodyLineInvalid;
+ unsigned BodyLine = SourceMgr.getSpellingLineNumber(Body->getSemiLoc(),
+ &BodyLineInvalid);
+ if (BodyLineInvalid)
+ return false;
+
+ // Warn if null statement and body are on the same line.
+ if (StmtLine != BodyLine)
+ return false;
+
+ return true;
+}
+
+void Sema::DiagnoseEmptyStmtBody(SourceLocation StmtLoc,
+ const Stmt *Body,
+ unsigned DiagID) {
+ // Since this is a syntactic check, don't emit diagnostic for template
+ // instantiations, this just adds noise.
+ if (CurrentInstantiationScope)
+ return;
+
+ // The body should be a null statement.
+ const NullStmt *NBody = dyn_cast<NullStmt>(Body);
+ if (!NBody)
+ return;
+
+ // Do the usual checks.
+ if (!ShouldDiagnoseEmptyStmtBody(SourceMgr, StmtLoc, NBody))
+ return;
+
+ Diag(NBody->getSemiLoc(), DiagID);
+ Diag(NBody->getSemiLoc(), diag::note_empty_body_on_separate_line);
+}
+
+void Sema::DiagnoseEmptyLoopBody(const Stmt *S,
+ const Stmt *PossibleBody) {
+ assert(!CurrentInstantiationScope); // Ensured by caller
+
+ SourceLocation StmtLoc;
+ const Stmt *Body;
+ unsigned DiagID;
+ if (const ForStmt *FS = dyn_cast<ForStmt>(S)) {
+ StmtLoc = FS->getRParenLoc();
+ Body = FS->getBody();
+ DiagID = diag::warn_empty_for_body;
+ } else if (const WhileStmt *WS = dyn_cast<WhileStmt>(S)) {
+ StmtLoc = WS->getRParenLoc();
+ Body = WS->getBody();
+ DiagID = diag::warn_empty_while_body;
+ } else
+ return; // Neither `for' nor `while'.
+
+ // The body should be a null statement.
+ const NullStmt *NBody = dyn_cast<NullStmt>(Body);
+ if (!NBody)
+ return;
+
+ // Skip expensive checks if diagnostic is disabled.
+ if (Diags.isIgnored(DiagID, NBody->getSemiLoc()))
+ return;
+
+ // Do the usual checks.
+ if (!ShouldDiagnoseEmptyStmtBody(SourceMgr, StmtLoc, NBody))
+ return;
+
+ // `for(...);' and `while(...);' are popular idioms, so in order to keep
+ // noise level low, emit diagnostics only if for/while is followed by a
+ // CompoundStmt, e.g.:
+ // for (int i = 0; i < n; i++);
+ // {
+ // a(i);
+ // }
+ // or if for/while is followed by a statement with more indentation
+ // than for/while itself:
+ // for (int i = 0; i < n; i++);
+ // a(i);
+ bool ProbableTypo = isa<CompoundStmt>(PossibleBody);
+ if (!ProbableTypo) {
+ bool BodyColInvalid;
+ unsigned BodyCol = SourceMgr.getPresumedColumnNumber(
+ PossibleBody->getBeginLoc(), &BodyColInvalid);
+ if (BodyColInvalid)
+ return;
+
+ bool StmtColInvalid;
+ unsigned StmtCol =
+ SourceMgr.getPresumedColumnNumber(S->getBeginLoc(), &StmtColInvalid);
+ if (StmtColInvalid)
+ return;
+
+ if (BodyCol > StmtCol)
+ ProbableTypo = true;
+ }
+
+ if (ProbableTypo) {
+ Diag(NBody->getSemiLoc(), DiagID);
+ Diag(NBody->getSemiLoc(), diag::note_empty_body_on_separate_line);
+ }
+}
+
+//===--- CHECK: Warn on self move with std::move. -------------------------===//
+
+void Sema::DiagnoseSelfMove(const Expr *LHSExpr, const Expr *RHSExpr,
+ SourceLocation OpLoc) {
+ if (Diags.isIgnored(diag::warn_sizeof_pointer_expr_memaccess, OpLoc))
+ return;
+
+ if (inTemplateInstantiation())
+ return;
+
+ // Strip parens and casts away.
+ LHSExpr = LHSExpr->IgnoreParenImpCasts();
+ RHSExpr = RHSExpr->IgnoreParenImpCasts();
+
+ // Check for a call to std::move or for a static_cast<T&&>(..) to an xvalue
+ // which we can treat as an inlined std::move
+ if (const auto *CE = dyn_cast<CallExpr>(RHSExpr);
+ CE && CE->getNumArgs() == 1 && CE->isCallToStdMove())
+ RHSExpr = CE->getArg(0);
+ else if (const auto *CXXSCE = dyn_cast<CXXStaticCastExpr>(RHSExpr);
+ CXXSCE && CXXSCE->isXValue())
+ RHSExpr = CXXSCE->getSubExpr();
+ else
+ return;
+
+ const DeclRefExpr *LHSDeclRef = dyn_cast<DeclRefExpr>(LHSExpr);
+ const DeclRefExpr *RHSDeclRef = dyn_cast<DeclRefExpr>(RHSExpr);
+
+ // Two DeclRefExpr's, check that the decls are the same.
+ if (LHSDeclRef && RHSDeclRef) {
+ if (!LHSDeclRef->getDecl() || !RHSDeclRef->getDecl())
+ return;
+ if (LHSDeclRef->getDecl()->getCanonicalDecl() !=
+ RHSDeclRef->getDecl()->getCanonicalDecl())
+ return;
+
+ auto D = Diag(OpLoc, diag::warn_self_move)
+ << LHSExpr->getType() << LHSExpr->getSourceRange()
+ << RHSExpr->getSourceRange();
+ if (const FieldDecl *F =
+ getSelfAssignmentClassMemberCandidate(RHSDeclRef->getDecl()))
+ D << 1 << F
+ << FixItHint::CreateInsertion(LHSDeclRef->getBeginLoc(), "this->");
+ else
+ D << 0;
+ return;
+ }
+
+ // Member variables require a different approach to check for self moves.
+ // MemberExpr's are the same if every nested MemberExpr refers to the same
+ // Decl and that the base Expr's are DeclRefExpr's with the same Decl or
+ // the base Expr's are CXXThisExpr's.
+ const Expr *LHSBase = LHSExpr;
+ const Expr *RHSBase = RHSExpr;
+ const MemberExpr *LHSME = dyn_cast<MemberExpr>(LHSExpr);
+ const MemberExpr *RHSME = dyn_cast<MemberExpr>(RHSExpr);
+ if (!LHSME || !RHSME)
+ return;
+
+ while (LHSME && RHSME) {
+ if (LHSME->getMemberDecl()->getCanonicalDecl() !=
+ RHSME->getMemberDecl()->getCanonicalDecl())
+ return;
+
+ LHSBase = LHSME->getBase();
+ RHSBase = RHSME->getBase();
+ LHSME = dyn_cast<MemberExpr>(LHSBase);
+ RHSME = dyn_cast<MemberExpr>(RHSBase);
+ }
+
+ LHSDeclRef = dyn_cast<DeclRefExpr>(LHSBase);
+ RHSDeclRef = dyn_cast<DeclRefExpr>(RHSBase);
+ if (LHSDeclRef && RHSDeclRef) {
+ if (!LHSDeclRef->getDecl() || !RHSDeclRef->getDecl())
+ return;
+ if (LHSDeclRef->getDecl()->getCanonicalDecl() !=
+ RHSDeclRef->getDecl()->getCanonicalDecl())
+ return;
+
+ Diag(OpLoc, diag::warn_self_move)
+ << LHSExpr->getType() << 0 << LHSExpr->getSourceRange()
+ << RHSExpr->getSourceRange();
+ return;
+ }
+
+ if (isa<CXXThisExpr>(LHSBase) && isa<CXXThisExpr>(RHSBase))
+ Diag(OpLoc, diag::warn_self_move)
+ << LHSExpr->getType() << 0 << LHSExpr->getSourceRange()
+ << RHSExpr->getSourceRange();
+}
+
+//===--- Layout compatibility ----------------------------------------------//
+
+static bool isLayoutCompatible(const ASTContext &C, QualType T1, QualType T2);
+
+/// Check if two enumeration types are layout-compatible.
+static bool isLayoutCompatible(const ASTContext &C, const EnumDecl *ED1,
+ const EnumDecl *ED2) {
+ // C++11 [dcl.enum] p8:
+ // Two enumeration types are layout-compatible if they have the same
+ // underlying type.
+ return ED1->isComplete() && ED2->isComplete() &&
+ C.hasSameType(ED1->getIntegerType(), ED2->getIntegerType());
+}
+
+/// Check if two fields are layout-compatible.
+/// Can be used on union members, which are exempt from alignment requirement
+/// of common initial sequence.
+static bool isLayoutCompatible(const ASTContext &C, const FieldDecl *Field1,
+ const FieldDecl *Field2,
+ bool AreUnionMembers = false) {
+#ifndef NDEBUG
+ CanQualType Field1Parent = C.getCanonicalTagType(Field1->getParent());
+ CanQualType Field2Parent = C.getCanonicalTagType(Field2->getParent());
+ assert(((Field1Parent->isStructureOrClassType() &&
+ Field2Parent->isStructureOrClassType()) ||
+ (Field1Parent->isUnionType() && Field2Parent->isUnionType())) &&
+ "Can't evaluate layout compatibility between a struct field and a "
+ "union field.");
+ assert(((!AreUnionMembers && Field1Parent->isStructureOrClassType()) ||
+ (AreUnionMembers && Field1Parent->isUnionType())) &&
+ "AreUnionMembers should be 'true' for union fields (only).");
+#endif
+
+ if (!isLayoutCompatible(C, Field1->getType(), Field2->getType()))
+ return false;
+
+ if (Field1->isBitField() != Field2->isBitField())
+ return false;
+
+ if (Field1->isBitField()) {
+ // Make sure that the bit-fields are the same length.
+ unsigned Bits1 = Field1->getBitWidthValue();
+ unsigned Bits2 = Field2->getBitWidthValue();
+
+ if (Bits1 != Bits2)
+ return false;
+ }
+
+ if (Field1->hasAttr<clang::NoUniqueAddressAttr>() ||
+ Field2->hasAttr<clang::NoUniqueAddressAttr>())
+ return false;
+
+ if (!AreUnionMembers &&
+ Field1->getMaxAlignment() != Field2->getMaxAlignment())
+ return false;
+
+ return true;
+}
+
+/// Check if two standard-layout structs are layout-compatible.
+/// (C++11 [class.mem] p17)
+static bool isLayoutCompatibleStruct(const ASTContext &C, const RecordDecl *RD1,
+ const RecordDecl *RD2) {
+ // Get to the class where the fields are declared
+ if (const CXXRecordDecl *D1CXX = dyn_cast<CXXRecordDecl>(RD1))
+ RD1 = D1CXX->getStandardLayoutBaseWithFields();
+
+ if (const CXXRecordDecl *D2CXX = dyn_cast<CXXRecordDecl>(RD2))
+ RD2 = D2CXX->getStandardLayoutBaseWithFields();
+
+ // Check the fields.
+ return llvm::equal(RD1->fields(), RD2->fields(),
+ [&C](const FieldDecl *F1, const FieldDecl *F2) -> bool {
+ return isLayoutCompatible(C, F1, F2);
+ });
+}
+
+/// Check if two standard-layout unions are layout-compatible.
+/// (C++11 [class.mem] p18)
+static bool isLayoutCompatibleUnion(const ASTContext &C, const RecordDecl *RD1,
+ const RecordDecl *RD2) {
+ llvm::SmallPtrSet<const FieldDecl *, 8> UnmatchedFields(llvm::from_range,
+ RD2->fields());
+
+ for (auto *Field1 : RD1->fields()) {
+ auto I = UnmatchedFields.begin();
+ auto E = UnmatchedFields.end();
+
+ for ( ; I != E; ++I) {
+ if (isLayoutCompatible(C, Field1, *I, /*IsUnionMember=*/true)) {
+ bool Result = UnmatchedFields.erase(*I);
+ (void) Result;
+ assert(Result);
+ break;
+ }
+ }
+ if (I == E)
+ return false;
+ }
+
+ return UnmatchedFields.empty();
+}
+
+static bool isLayoutCompatible(const ASTContext &C, const RecordDecl *RD1,
+ const RecordDecl *RD2) {
+ if (RD1->isUnion() != RD2->isUnion())
+ return false;
+
+ if (RD1->isUnion())
+ return isLayoutCompatibleUnion(C, RD1, RD2);
+ else
+ return isLayoutCompatibleStruct(C, RD1, RD2);
+}
+
+/// Check if two types are layout-compatible in C++11 sense.
+static bool isLayoutCompatible(const ASTContext &C, QualType T1, QualType T2) {
+ if (T1.isNull() || T2.isNull())
+ return false;
+
+ // C++20 [basic.types] p11:
+ // Two types cv1 T1 and cv2 T2 are layout-compatible types
+ // if T1 and T2 are the same type, layout-compatible enumerations (9.7.1),
+ // or layout-compatible standard-layout class types (11.4).
+ T1 = T1.getCanonicalType().getUnqualifiedType();
+ T2 = T2.getCanonicalType().getUnqualifiedType();
+
+ if (C.hasSameType(T1, T2))
+ return true;
+
+ const Type::TypeClass TC1 = T1->getTypeClass();
+ const Type::TypeClass TC2 = T2->getTypeClass();
+
+ if (TC1 != TC2)
+ return false;
+
+ if (TC1 == Type::Enum)
+ return isLayoutCompatible(C, T1->castAsEnumDecl(), T2->castAsEnumDecl());
+ if (TC1 == Type::Record) {
+ if (!T1->isStandardLayoutType() || !T2->isStandardLayoutType())
+ return false;
+
+ return isLayoutCompatible(C, T1->castAsRecordDecl(),
+ T2->castAsRecordDecl());
+ }
+
+ return false;
+}
+
+bool Sema::IsLayoutCompatible(QualType T1, QualType T2) const {
+ return isLayoutCompatible(getASTContext(), T1, T2);
+}
+
+//===-------------- Pointer interconvertibility ----------------------------//
+
+bool Sema::IsPointerInterconvertibleBaseOf(const TypeSourceInfo *Base,
+ const TypeSourceInfo *Derived) {
+ QualType BaseT = Base->getType()->getCanonicalTypeUnqualified();
+ QualType DerivedT = Derived->getType()->getCanonicalTypeUnqualified();
+
+ if (BaseT->isStructureOrClassType() && DerivedT->isStructureOrClassType() &&
+ getASTContext().hasSameType(BaseT, DerivedT))
+ return true;
+
+ if (!IsDerivedFrom(Derived->getTypeLoc().getBeginLoc(), DerivedT, BaseT))
+ return false;
+
+ // Per [basic.compound]/4.3, containing object has to be standard-layout.
+ if (DerivedT->getAsCXXRecordDecl()->isStandardLayout())
+ return true;
+
+ return false;
+}
+
+//===--- CHECK: pointer_with_type_tag attribute: datatypes should match ----//
+
+/// Given a type tag expression find the type tag itself.
+///
+/// \param TypeExpr Type tag expression, as it appears in user's code.
+///
+/// \param VD Declaration of an identifier that appears in a type tag.
+///
+/// \param MagicValue Type tag magic value.
+///
+/// \param isConstantEvaluated whether the evalaution should be performed in
+
+/// constant context.
+static bool FindTypeTagExpr(const Expr *TypeExpr, const ASTContext &Ctx,
+ const ValueDecl **VD, uint64_t *MagicValue,
+ bool isConstantEvaluated) {
+ while(true) {
+ if (!TypeExpr)
+ return false;
+
+ TypeExpr = TypeExpr->IgnoreParenImpCasts()->IgnoreParenCasts();
+
+ switch (TypeExpr->getStmtClass()) {
+ case Stmt::UnaryOperatorClass: {
+ const UnaryOperator *UO = cast<UnaryOperator>(TypeExpr);
+ if (UO->getOpcode() == UO_AddrOf || UO->getOpcode() == UO_Deref) {
+ TypeExpr = UO->getSubExpr();
+ continue;
+ }
+ return false;
+ }
+
+ case Stmt::DeclRefExprClass: {
+ const DeclRefExpr *DRE = cast<DeclRefExpr>(TypeExpr);
+ *VD = DRE->getDecl();
+ return true;
+ }
+
+ case Stmt::IntegerLiteralClass: {
+ const IntegerLiteral *IL = cast<IntegerLiteral>(TypeExpr);
+ llvm::APInt MagicValueAPInt = IL->getValue();
+ if (MagicValueAPInt.getActiveBits() <= 64) {
+ *MagicValue = MagicValueAPInt.getZExtValue();
+ return true;
+ } else
+ return false;
+ }
+
+ case Stmt::BinaryConditionalOperatorClass:
+ case Stmt::ConditionalOperatorClass: {
+ const AbstractConditionalOperator *ACO =
+ cast<AbstractConditionalOperator>(TypeExpr);
+ bool Result;
+ if (ACO->getCond()->EvaluateAsBooleanCondition(Result, Ctx,
+ isConstantEvaluated)) {
+ if (Result)
+ TypeExpr = ACO->getTrueExpr();
+ else
+ TypeExpr = ACO->getFalseExpr();
+ continue;
+ }
+ return false;
+ }
+
+ case Stmt::BinaryOperatorClass: {
+ const BinaryOperator *BO = cast<BinaryOperator>(TypeExpr);
+ if (BO->getOpcode() == BO_Comma) {
+ TypeExpr = BO->getRHS();
+ continue;
+ }
+ return false;
+ }
+
+ default:
+ return false;
+ }
+ }
+}
+
+/// Retrieve the C type corresponding to type tag TypeExpr.
+///
+/// \param TypeExpr Expression that specifies a type tag.
+///
+/// \param MagicValues Registered magic values.
+///
+/// \param FoundWrongKind Set to true if a type tag was found, but of a wrong
+/// kind.
+///
+/// \param TypeInfo Information about the corresponding C type.
+///
+/// \param isConstantEvaluated whether the evalaution should be performed in
+/// constant context.
+///
+/// \returns true if the corresponding C type was found.
+static bool GetMatchingCType(
+ const IdentifierInfo *ArgumentKind, const Expr *TypeExpr,
+ const ASTContext &Ctx,
+ const llvm::DenseMap<Sema::TypeTagMagicValue, Sema::TypeTagData>
+ *MagicValues,
+ bool &FoundWrongKind, Sema::TypeTagData &TypeInfo,
+ bool isConstantEvaluated) {
+ FoundWrongKind = false;
+
+ // Variable declaration that has type_tag_for_datatype attribute.
+ const ValueDecl *VD = nullptr;
+
+ uint64_t MagicValue;
+
+ if (!FindTypeTagExpr(TypeExpr, Ctx, &VD, &MagicValue, isConstantEvaluated))
+ return false;
+
+ if (VD) {
+ if (TypeTagForDatatypeAttr *I = VD->getAttr<TypeTagForDatatypeAttr>()) {
+ if (I->getArgumentKind() != ArgumentKind) {
+ FoundWrongKind = true;
+ return false;
+ }
+ TypeInfo.Type = I->getMatchingCType();
+ TypeInfo.LayoutCompatible = I->getLayoutCompatible();
+ TypeInfo.MustBeNull = I->getMustBeNull();
+ return true;
+ }
+ return false;
+ }
+
+ if (!MagicValues)
+ return false;
+
+ llvm::DenseMap<Sema::TypeTagMagicValue,
+ Sema::TypeTagData>::const_iterator I =
+ MagicValues->find(std::make_pair(ArgumentKind, MagicValue));
+ if (I == MagicValues->end())
+ return false;
+
+ TypeInfo = I->second;
+ return true;
+}
+
+void Sema::RegisterTypeTagForDatatype(const IdentifierInfo *ArgumentKind,
+ uint64_t MagicValue, QualType Type,
+ bool LayoutCompatible,
+ bool MustBeNull) {
+ if (!TypeTagForDatatypeMagicValues)
+ TypeTagForDatatypeMagicValues.reset(
+ new llvm::DenseMap<TypeTagMagicValue, TypeTagData>);
+
+ TypeTagMagicValue Magic(ArgumentKind, MagicValue);
+ (*TypeTagForDatatypeMagicValues)[Magic] =
+ TypeTagData(Type, LayoutCompatible, MustBeNull);
+}
+
+static bool IsSameCharType(QualType T1, QualType T2) {
+ const BuiltinType *BT1 = T1->getAs<BuiltinType>();
+ if (!BT1)
+ return false;
+
+ const BuiltinType *BT2 = T2->getAs<BuiltinType>();
+ if (!BT2)
+ return false;
+
+ BuiltinType::Kind T1Kind = BT1->getKind();
+ BuiltinType::Kind T2Kind = BT2->getKind();
+
+ return (T1Kind == BuiltinType::SChar && T2Kind == BuiltinType::Char_S) ||
+ (T1Kind == BuiltinType::UChar && T2Kind == BuiltinType::Char_U) ||
+ (T1Kind == BuiltinType::Char_U && T2Kind == BuiltinType::UChar) ||
+ (T1Kind == BuiltinType::Char_S && T2Kind == BuiltinType::SChar);
+}
+
+void Sema::CheckArgumentWithTypeTag(const ArgumentWithTypeTagAttr *Attr,
+ const ArrayRef<const Expr *> ExprArgs,
+ SourceLocation CallSiteLoc) {
+ const IdentifierInfo *ArgumentKind = Attr->getArgumentKind();
+ bool IsPointerAttr = Attr->getIsPointer();
+
+ // Retrieve the argument representing the 'type_tag'.
+ unsigned TypeTagIdxAST = Attr->getTypeTagIdx().getASTIndex();
+ if (TypeTagIdxAST >= ExprArgs.size()) {
+ Diag(CallSiteLoc, diag::err_tag_index_out_of_range)
+ << 0 << Attr->getTypeTagIdx().getSourceIndex();
+ return;
+ }
+ const Expr *TypeTagExpr = ExprArgs[TypeTagIdxAST];
+ bool FoundWrongKind;
+ TypeTagData TypeInfo;
+ if (!GetMatchingCType(ArgumentKind, TypeTagExpr, Context,
+ TypeTagForDatatypeMagicValues.get(), FoundWrongKind,
+ TypeInfo, isConstantEvaluatedContext())) {
+ if (FoundWrongKind)
+ Diag(TypeTagExpr->getExprLoc(),
+ diag::warn_type_tag_for_datatype_wrong_kind)
+ << TypeTagExpr->getSourceRange();
+ return;
+ }
+
+ // Retrieve the argument representing the 'arg_idx'.
+ unsigned ArgumentIdxAST = Attr->getArgumentIdx().getASTIndex();
+ if (ArgumentIdxAST >= ExprArgs.size()) {
+ Diag(CallSiteLoc, diag::err_tag_index_out_of_range)
+ << 1 << Attr->getArgumentIdx().getSourceIndex();
+ return;
+ }
+ const Expr *ArgumentExpr = ExprArgs[ArgumentIdxAST];
+ if (IsPointerAttr) {
+ // Skip implicit cast of pointer to `void *' (as a function argument).
+ if (const ImplicitCastExpr *ICE = dyn_cast<ImplicitCastExpr>(ArgumentExpr))
+ if (ICE->getType()->isVoidPointerType() &&
+ ICE->getCastKind() == CK_BitCast)
+ ArgumentExpr = ICE->getSubExpr();
+ }
+ QualType ArgumentType = ArgumentExpr->getType();
+
+ // Passing a `void*' pointer shouldn't trigger a warning.
+ if (IsPointerAttr && ArgumentType->isVoidPointerType())
+ return;
+
+ if (TypeInfo.MustBeNull) {
+ // Type tag with matching void type requires a null pointer.
+ if (!ArgumentExpr->isNullPointerConstant(Context,
+ Expr::NPC_ValueDependentIsNotNull)) {
+ Diag(ArgumentExpr->getExprLoc(),
+ diag::warn_type_safety_null_pointer_required)
+ << ArgumentKind->getName()
+ << ArgumentExpr->getSourceRange()
+ << TypeTagExpr->getSourceRange();
+ }
+ return;
+ }
+
+ QualType RequiredType = TypeInfo.Type;
+ if (IsPointerAttr)
+ RequiredType = Context.getPointerType(RequiredType);
+
+ bool mismatch = false;
+ if (!TypeInfo.LayoutCompatible) {
+ mismatch = !Context.hasSameType(ArgumentType, RequiredType);
+
+ // C++11 [basic.fundamental] p1:
+ // Plain char, signed char, and unsigned char are three distinct types.
+ //
+ // But we treat plain `char' as equivalent to `signed char' or `unsigned
+ // char' depending on the current char signedness mode.
+ if (mismatch)
+ if ((IsPointerAttr && IsSameCharType(ArgumentType->getPointeeType(),
+ RequiredType->getPointeeType())) ||
+ (!IsPointerAttr && IsSameCharType(ArgumentType, RequiredType)))
+ mismatch = false;
+ } else
+ if (IsPointerAttr)
+ mismatch = !isLayoutCompatible(Context,
+ ArgumentType->getPointeeType(),
+ RequiredType->getPointeeType());
+ else
+ mismatch = !isLayoutCompatible(Context, ArgumentType, RequiredType);
+
+ if (mismatch)
+ Diag(ArgumentExpr->getExprLoc(), diag::warn_type_safety_type_mismatch)
+ << ArgumentType << ArgumentKind
+ << TypeInfo.LayoutCompatible << RequiredType
+ << ArgumentExpr->getSourceRange()
+ << TypeTagExpr->getSourceRange();
+}
+
+void Sema::AddPotentialMisalignedMembers(Expr *E, RecordDecl *RD, ValueDecl *MD,
+ CharUnits Alignment) {
+ currentEvaluationContext().MisalignedMembers.emplace_back(E, RD, MD,
+ Alignment);
+}
+
+void Sema::DiagnoseMisalignedMembers() {
+ for (MisalignedMember &m : currentEvaluationContext().MisalignedMembers) {
+ const NamedDecl *ND = m.RD;
+ if (ND->getName().empty()) {
+ if (const TypedefNameDecl *TD = m.RD->getTypedefNameForAnonDecl())
+ ND = TD;
+ }
+ Diag(m.E->getBeginLoc(), diag::warn_taking_address_of_packed_member)
+ << m.MD << ND << m.E->getSourceRange();
+ }
+ currentEvaluationContext().MisalignedMembers.clear();
+}
+
+void Sema::DiscardMisalignedMemberAddress(const Type *T, Expr *E) {
+ E = E->IgnoreParens();
+ if (!T->isPointerType() && !T->isIntegerType() && !T->isDependentType())
+ return;
+ if (isa<UnaryOperator>(E) &&
+ cast<UnaryOperator>(E)->getOpcode() == UO_AddrOf) {
+ auto *Op = cast<UnaryOperator>(E)->getSubExpr()->IgnoreParens();
+ if (isa<MemberExpr>(Op)) {
+ auto &MisalignedMembersForExpr =
+ currentEvaluationContext().MisalignedMembers;
+ auto *MA = llvm::find(MisalignedMembersForExpr, MisalignedMember(Op));
+ if (MA != MisalignedMembersForExpr.end() &&
+ (T->isDependentType() || T->isIntegerType() ||
+ (T->isPointerType() && (T->getPointeeType()->isIncompleteType() ||
+ Context.getTypeAlignInChars(
+ T->getPointeeType()) <= MA->Alignment))))
+ MisalignedMembersForExpr.erase(MA);
+ }
+ }
+}
+
+void Sema::RefersToMemberWithReducedAlignment(
+ Expr *E,
+ llvm::function_ref<void(Expr *, RecordDecl *, FieldDecl *, CharUnits)>
+ Action) {
+ const auto *ME = dyn_cast<MemberExpr>(E);
+ if (!ME)
+ return;
+
+ // No need to check expressions with an __unaligned-qualified type.
+ if (E->getType().getQualifiers().hasUnaligned())
+ return;
+
+ // For a chain of MemberExpr like "a.b.c.d" this list
+ // will keep FieldDecl's like [d, c, b].
+ SmallVector<FieldDecl *, 4> ReverseMemberChain;
+ const MemberExpr *TopME = nullptr;
+ bool AnyIsPacked = false;
+ do {
+ QualType BaseType = ME->getBase()->getType();
+ if (BaseType->isDependentType())
+ return;
+ if (ME->isArrow())
+ BaseType = BaseType->getPointeeType();
+ auto *RD = BaseType->castAsRecordDecl();
+ if (RD->isInvalidDecl())
+ return;
+
+ ValueDecl *MD = ME->getMemberDecl();
+ auto *FD = dyn_cast<FieldDecl>(MD);
+ // We do not care about non-data members.
+ if (!FD || FD->isInvalidDecl())
+ return;
+
+ AnyIsPacked =
+ AnyIsPacked || (RD->hasAttr<PackedAttr>() || MD->hasAttr<PackedAttr>());
+ ReverseMemberChain.push_back(FD);
+
+ TopME = ME;
+ ME = dyn_cast<MemberExpr>(ME->getBase()->IgnoreParens());
+ } while (ME);
+ assert(TopME && "We did not compute a topmost MemberExpr!");
+
+ // Not the scope of this diagnostic.
+ if (!AnyIsPacked)
+ return;
+
+ const Expr *TopBase = TopME->getBase()->IgnoreParenImpCasts();
+ const auto *DRE = dyn_cast<DeclRefExpr>(TopBase);
+ // TODO: The innermost base of the member expression may be too complicated.
+ // For now, just disregard these cases. This is left for future
+ // improvement.
+ if (!DRE && !isa<CXXThisExpr>(TopBase))
+ return;
+
+ // Alignment expected by the whole expression.
+ CharUnits ExpectedAlignment = Context.getTypeAlignInChars(E->getType());
+
+ // No need to do anything else with this case.
+ if (ExpectedAlignment.isOne())
+ return;
+
+ // Synthesize offset of the whole access.
+ CharUnits Offset;
+ for (const FieldDecl *FD : llvm::reverse(ReverseMemberChain))
+ Offset += Context.toCharUnitsFromBits(Context.getFieldOffset(FD));
+
+ // Compute the CompleteObjectAlignment as the alignment of the whole chain.
+ CharUnits CompleteObjectAlignment = Context.getTypeAlignInChars(
+ Context.getCanonicalTagType(ReverseMemberChain.back()->getParent()));
+
+ // The base expression of the innermost MemberExpr may give
+ // stronger guarantees than the class containing the member.
+ if (DRE && !TopME->isArrow()) {
+ const ValueDecl *VD = DRE->getDecl();
+ if (!VD->getType()->isReferenceType())
+ CompleteObjectAlignment =
+ std::max(CompleteObjectAlignment, Context.getDeclAlign(VD));
+ }
+
+ // Check if the synthesized offset fulfills the alignment.
+ if (!Offset.isMultipleOf(ExpectedAlignment) ||
+ // It may fulfill the offset it but the effective alignment may still be
+ // lower than the expected expression alignment.
+ CompleteObjectAlignment < ExpectedAlignment) {
+ // If this happens, we want to determine a sensible culprit of this.
+ // Intuitively, watching the chain of member expressions from right to
+ // left, we start with the required alignment (as required by the field
+ // type) but some packed attribute in that chain has reduced the alignment.
+ // It may happen that another packed structure increases it again. But if
+ // we are here such increase has not been enough. So pointing the first
+ // FieldDecl that either is packed or else its RecordDecl is,
+ // seems reasonable.
+ FieldDecl *FD = nullptr;
+ CharUnits Alignment;
+ for (FieldDecl *FDI : ReverseMemberChain) {
+ if (FDI->hasAttr<PackedAttr>() ||
+ FDI->getParent()->hasAttr<PackedAttr>()) {
+ FD = FDI;
+ Alignment = std::min(Context.getTypeAlignInChars(FD->getType()),
+ Context.getTypeAlignInChars(
+ Context.getCanonicalTagType(FD->getParent())));
+ break;
+ }
+ }
+ assert(FD && "We did not find a packed FieldDecl!");
+ Action(E, FD->getParent(), FD, Alignment);
+ }
+}
+
+void Sema::CheckAddressOfPackedMember(Expr *rhs) {
+ using namespace std::placeholders;
+
+ RefersToMemberWithReducedAlignment(
+ rhs, std::bind(&Sema::AddPotentialMisalignedMembers, std::ref(*this), _1,
+ _2, _3, _4));
+}
+
+bool Sema::PrepareBuiltinElementwiseMathOneArgCall(
+ CallExpr *TheCall, EltwiseBuiltinArgTyRestriction ArgTyRestr) {
+ if (checkArgCount(TheCall, 1))
+ return true;
+
+ ExprResult A = BuiltinVectorMathConversions(*this, TheCall->getArg(0));
+ if (A.isInvalid())
+ return true;
+
+ TheCall->setArg(0, A.get());
+ QualType TyA = A.get()->getType();
+
+ if (checkMathBuiltinElementType(*this, A.get()->getBeginLoc(), TyA,
+ ArgTyRestr, 1))
+ return true;
+
+ TheCall->setType(TyA);
+ return false;
+}
+
+bool Sema::BuiltinElementwiseMath(CallExpr *TheCall,
+ EltwiseBuiltinArgTyRestriction ArgTyRestr) {
+ if (auto Res = BuiltinVectorMath(TheCall, ArgTyRestr); Res.has_value()) {
+ TheCall->setType(*Res);
+ return false;
+ }
+ return true;
+}
+
+bool Sema::BuiltinVectorToScalarMath(CallExpr *TheCall) {
+ std::optional<QualType> Res = BuiltinVectorMath(TheCall);
+ if (!Res)
+ return true;
+
+ if (auto *VecTy0 = (*Res)->getAs<VectorType>())
+ TheCall->setType(VecTy0->getElementType());
+ else
+ TheCall->setType(*Res);
+
+ return false;
+}
+
+static bool checkBuiltinVectorMathMixedEnums(Sema &S, Expr *LHS, Expr *RHS,
+ SourceLocation Loc) {
+ QualType L = LHS->getEnumCoercedType(S.Context),
+ R = RHS->getEnumCoercedType(S.Context);
+ if (L->isUnscopedEnumerationType() && R->isUnscopedEnumerationType() &&
+ !S.Context.hasSameUnqualifiedType(L, R)) {
+ return S.Diag(Loc, diag::err_conv_mixed_enum_types)
+ << LHS->getSourceRange() << RHS->getSourceRange()
+ << /*Arithmetic Between*/ 0 << L << R;
+ }
+ return false;
+}
+
+/// Check if all arguments have the same type. If the types don't match, emit an
+/// error message and return true. Otherwise return false.
+///
+/// For scalars we directly compare their unqualified types. But even if we
+/// compare unqualified vector types, a difference in qualifiers in the element
+/// types can make the vector types be considered not equal. For example,
+/// vector of 4 'const float' values vs vector of 4 'float' values.
+/// So we compare unqualified types of their elements and number of elements.
+static bool checkBuiltinVectorMathArgTypes(Sema &SemaRef,
+ ArrayRef<Expr *> Args) {
+ assert(!Args.empty() && "Should have at least one argument.");
+
+ Expr *Arg0 = Args.front();
+ QualType Ty0 = Arg0->getType();
+
+ auto EmitError = [&](Expr *ArgI) {
+ SemaRef.Diag(Arg0->getBeginLoc(),
+ diag::err_typecheck_call_different_arg_types)
+ << Arg0->getType() << ArgI->getType();
+ };
+
+ // Compare scalar types.
+ if (!Ty0->isVectorType()) {
+ for (Expr *ArgI : Args.drop_front())
+ if (!SemaRef.Context.hasSameUnqualifiedType(Ty0, ArgI->getType())) {
+ EmitError(ArgI);
+ return true;
+ }
+
+ return false;
+ }
+
+ // Compare vector types.
+ const auto *Vec0 = Ty0->castAs<VectorType>();
+ for (Expr *ArgI : Args.drop_front()) {
+ const auto *VecI = ArgI->getType()->getAs<VectorType>();
+ if (!VecI ||
+ !SemaRef.Context.hasSameUnqualifiedType(Vec0->getElementType(),
+ VecI->getElementType()) ||
+ Vec0->getNumElements() != VecI->getNumElements()) {
+ EmitError(ArgI);
+ return true;
+ }
+ }
+
+ return false;
+}
+
+std::optional<QualType>
+Sema::BuiltinVectorMath(CallExpr *TheCall,
+ EltwiseBuiltinArgTyRestriction ArgTyRestr) {
+ if (checkArgCount(TheCall, 2))
+ return std::nullopt;
+
+ if (checkBuiltinVectorMathMixedEnums(
+ *this, TheCall->getArg(0), TheCall->getArg(1), TheCall->getExprLoc()))
+ return std::nullopt;
+
+ Expr *Args[2];
+ for (int I = 0; I < 2; ++I) {
+ ExprResult Converted =
+ BuiltinVectorMathConversions(*this, TheCall->getArg(I));
+ if (Converted.isInvalid())
+ return std::nullopt;
+ Args[I] = Converted.get();
+ }
+
+ SourceLocation LocA = Args[0]->getBeginLoc();
+ QualType TyA = Args[0]->getType();
+
+ if (checkMathBuiltinElementType(*this, LocA, TyA, ArgTyRestr, 1))
+ return std::nullopt;
+
+ if (checkBuiltinVectorMathArgTypes(*this, Args))
+ return std::nullopt;
+
+ TheCall->setArg(0, Args[0]);
+ TheCall->setArg(1, Args[1]);
+ return TyA;
+}
+
+bool Sema::BuiltinElementwiseTernaryMath(
+ CallExpr *TheCall, EltwiseBuiltinArgTyRestriction ArgTyRestr) {
+ if (checkArgCount(TheCall, 3))
+ return true;
+
+ SourceLocation Loc = TheCall->getExprLoc();
+ if (checkBuiltinVectorMathMixedEnums(*this, TheCall->getArg(0),
+ TheCall->getArg(1), Loc) ||
+ checkBuiltinVectorMathMixedEnums(*this, TheCall->getArg(1),
+ TheCall->getArg(2), Loc))
+ return true;
+
+ Expr *Args[3];
+ for (int I = 0; I < 3; ++I) {
+ ExprResult Converted =
+ BuiltinVectorMathConversions(*this, TheCall->getArg(I));
+ if (Converted.isInvalid())
+ return true;
+ Args[I] = Converted.get();
+ }
+
+ int ArgOrdinal = 1;
+ for (Expr *Arg : Args) {
+ if (checkMathBuiltinElementType(*this, Arg->getBeginLoc(), Arg->getType(),
+ ArgTyRestr, ArgOrdinal++))
+ return true;
+ }
+
+ if (checkBuiltinVectorMathArgTypes(*this, Args))
+ return true;
+
+ for (int I = 0; I < 3; ++I)
+ TheCall->setArg(I, Args[I]);
+
+ TheCall->setType(Args[0]->getType());
+ return false;
+}
+
+bool Sema::PrepareBuiltinReduceMathOneArgCall(CallExpr *TheCall) {
+ if (checkArgCount(TheCall, 1))
+ return true;
+
+ ExprResult A = UsualUnaryConversions(TheCall->getArg(0));
+ if (A.isInvalid())
+ return true;
+
+ TheCall->setArg(0, A.get());
+ return false;
+}
+
+bool Sema::BuiltinNonDeterministicValue(CallExpr *TheCall) {
+ if (checkArgCount(TheCall, 1))
+ return true;
+
+ ExprResult Arg = TheCall->getArg(0);
+ QualType TyArg = Arg.get()->getType();
+
+ if (!TyArg->isBuiltinType() && !TyArg->isVectorType())
+ return Diag(TheCall->getArg(0)->getBeginLoc(),
+ diag::err_builtin_invalid_arg_type)
+ << 1 << /* vector */ 2 << /* integer */ 1 << /* fp */ 1 << TyArg;
+
+ TheCall->setType(TyArg);
+ return false;
+}
+
+ExprResult Sema::BuiltinMatrixTranspose(CallExpr *TheCall,
+ ExprResult CallResult) {
+ if (checkArgCount(TheCall, 1))
+ return ExprError();
+
+ ExprResult MatrixArg = DefaultLvalueConversion(TheCall->getArg(0));
+ if (MatrixArg.isInvalid())
+ return MatrixArg;
+ Expr *Matrix = MatrixArg.get();
+
+ auto *MType = Matrix->getType()->getAs<ConstantMatrixType>();
+ if (!MType) {
+ Diag(Matrix->getBeginLoc(), diag::err_builtin_invalid_arg_type)
+ << 1 << /* matrix */ 3 << /* no int */ 0 << /* no fp */ 0
+ << Matrix->getType();
+ return ExprError();
+ }
+
+ // Create returned matrix type by swapping rows and columns of the argument
+ // matrix type.
+ QualType ResultType = Context.getConstantMatrixType(
+ MType->getElementType(), MType->getNumColumns(), MType->getNumRows());
+
+ // Change the return type to the type of the returned matrix.
+ TheCall->setType(ResultType);
+
+ // Update call argument to use the possibly converted matrix argument.
+ TheCall->setArg(0, Matrix);
+ return CallResult;
+}
+
+// Get and verify the matrix dimensions.
+static std::optional<unsigned>
+getAndVerifyMatrixDimension(Expr *Expr, StringRef Name, Sema &S) {
+ std::optional<llvm::APSInt> Value = Expr->getIntegerConstantExpr(S.Context);
+ if (!Value) {
+ S.Diag(Expr->getBeginLoc(), diag::err_builtin_matrix_scalar_unsigned_arg)
+ << Name;
+ return {};
+ }
+ uint64_t Dim = Value->getZExtValue();
+ if (Dim == 0 || Dim > S.Context.getLangOpts().MaxMatrixDimension) {
+ S.Diag(Expr->getBeginLoc(), diag::err_builtin_matrix_invalid_dimension)
+ << Name << S.Context.getLangOpts().MaxMatrixDimension;
+ return {};
+ }
+ return Dim;
+}
+
+ExprResult Sema::BuiltinMatrixColumnMajorLoad(CallExpr *TheCall,
+ ExprResult CallResult) {
+ if (!getLangOpts().MatrixTypes) {
+ Diag(TheCall->getBeginLoc(), diag::err_builtin_matrix_disabled);
+ return ExprError();
+ }
+
+ if (getLangOpts().getDefaultMatrixMemoryLayout() !=
+ LangOptions::MatrixMemoryLayout::MatrixColMajor) {
+ Diag(TheCall->getBeginLoc(), diag::err_builtin_matrix_major_order_disabled)
+ << /*column*/ 1 << /*load*/ 0;
+ return ExprError();
+ }
+
+ if (checkArgCount(TheCall, 4))
+ return ExprError();
+
+ unsigned PtrArgIdx = 0;
+ Expr *PtrExpr = TheCall->getArg(PtrArgIdx);
+ Expr *RowsExpr = TheCall->getArg(1);
+ Expr *ColumnsExpr = TheCall->getArg(2);
+ Expr *StrideExpr = TheCall->getArg(3);
+
+ bool ArgError = false;
+
+ // Check pointer argument.
+ {
+ ExprResult PtrConv = DefaultFunctionArrayLvalueConversion(PtrExpr);
+ if (PtrConv.isInvalid())
+ return PtrConv;
+ PtrExpr = PtrConv.get();
+ TheCall->setArg(0, PtrExpr);
+ if (PtrExpr->isTypeDependent()) {
+ TheCall->setType(Context.DependentTy);
+ return TheCall;
+ }
+ }
+
+ auto *PtrTy = PtrExpr->getType()->getAs<PointerType>();
+ QualType ElementTy;
+ if (!PtrTy) {
+ Diag(PtrExpr->getBeginLoc(), diag::err_builtin_invalid_arg_type)
+ << PtrArgIdx + 1 << 0 << /* pointer to element ty */ 5 << /* no fp */ 0
+ << PtrExpr->getType();
+ ArgError = true;
+ } else {
+ ElementTy = PtrTy->getPointeeType().getUnqualifiedType();
+
+ if (!ConstantMatrixType::isValidElementType(ElementTy, getLangOpts())) {
+ Diag(PtrExpr->getBeginLoc(), diag::err_builtin_invalid_arg_type)
+ << PtrArgIdx + 1 << 0 << /* pointer to element ty */ 5
+ << /* no fp */ 0 << PtrExpr->getType();
+ ArgError = true;
+ }
+ }
+
+ // Apply default Lvalue conversions and convert the expression to size_t.
+ auto ApplyArgumentConversions = [this](Expr *E) {
+ ExprResult Conv = DefaultLvalueConversion(E);
+ if (Conv.isInvalid())
+ return Conv;
+
+ return tryConvertExprToType(Conv.get(), Context.getSizeType());
+ };
+
+ // Apply conversion to row and column expressions.
+ ExprResult RowsConv = ApplyArgumentConversions(RowsExpr);
+ if (!RowsConv.isInvalid()) {
+ RowsExpr = RowsConv.get();
+ TheCall->setArg(1, RowsExpr);
+ } else
+ RowsExpr = nullptr;
+
+ ExprResult ColumnsConv = ApplyArgumentConversions(ColumnsExpr);
+ if (!ColumnsConv.isInvalid()) {
+ ColumnsExpr = ColumnsConv.get();
+ TheCall->setArg(2, ColumnsExpr);
+ } else
+ ColumnsExpr = nullptr;
+
+ // If any part of the result matrix type is still pending, just use
+ // Context.DependentTy, until all parts are resolved.
+ if ((RowsExpr && RowsExpr->isTypeDependent()) ||
+ (ColumnsExpr && ColumnsExpr->isTypeDependent())) {
+ TheCall->setType(Context.DependentTy);
+ return CallResult;
+ }
+
+ // Check row and column dimensions.
+ std::optional<unsigned> MaybeRows;
+ if (RowsExpr)
+ MaybeRows = getAndVerifyMatrixDimension(RowsExpr, "row", *this);
+
+ std::optional<unsigned> MaybeColumns;
+ if (ColumnsExpr)
+ MaybeColumns = getAndVerifyMatrixDimension(ColumnsExpr, "column", *this);
+
+ // Check stride argument.
+ ExprResult StrideConv = ApplyArgumentConversions(StrideExpr);
+ if (StrideConv.isInvalid())
+ return ExprError();
+ StrideExpr = StrideConv.get();
+ TheCall->setArg(3, StrideExpr);
+
+ if (MaybeRows) {
+ if (std::optional<llvm::APSInt> Value =
+ StrideExpr->getIntegerConstantExpr(Context)) {
+ uint64_t Stride = Value->getZExtValue();
+ if (Stride < *MaybeRows) {
+ Diag(StrideExpr->getBeginLoc(),
+ diag::err_builtin_matrix_stride_too_small);
+ ArgError = true;
+ }
+ }
+ }
+
+ if (ArgError || !MaybeRows || !MaybeColumns)
+ return ExprError();
+
+ TheCall->setType(
+ Context.getConstantMatrixType(ElementTy, *MaybeRows, *MaybeColumns));
+ return CallResult;
+}
+
+ExprResult Sema::BuiltinMatrixColumnMajorStore(CallExpr *TheCall,
+ ExprResult CallResult) {
+ if (!getLangOpts().MatrixTypes) {
+ Diag(TheCall->getBeginLoc(), diag::err_builtin_matrix_disabled);
+ return ExprError();
+ }
+
+ if (getLangOpts().getDefaultMatrixMemoryLayout() !=
+ LangOptions::MatrixMemoryLayout::MatrixColMajor) {
+ Diag(TheCall->getBeginLoc(), diag::err_builtin_matrix_major_order_disabled)
+ << /*column*/ 1 << /*store*/ 1;
+ return ExprError();
+ }
+
+ if (checkArgCount(TheCall, 3))
+ return ExprError();
+
+ unsigned PtrArgIdx = 1;
+ Expr *MatrixExpr = TheCall->getArg(0);
+ Expr *PtrExpr = TheCall->getArg(PtrArgIdx);
+ Expr *StrideExpr = TheCall->getArg(2);
+
+ bool ArgError = false;
+
+ {
+ ExprResult MatrixConv = DefaultLvalueConversion(MatrixExpr);
+ if (MatrixConv.isInvalid())
+ return MatrixConv;
+ MatrixExpr = MatrixConv.get();
+ TheCall->setArg(0, MatrixExpr);
+ }
+ if (MatrixExpr->isTypeDependent()) {
+ TheCall->setType(Context.DependentTy);
+ return TheCall;
+ }
+
+ auto *MatrixTy = MatrixExpr->getType()->getAs<ConstantMatrixType>();
+ if (!MatrixTy) {
+ Diag(MatrixExpr->getBeginLoc(), diag::err_builtin_invalid_arg_type)
+ << 1 << /* matrix ty */ 3 << 0 << 0 << MatrixExpr->getType();
+ ArgError = true;
+ }
+
+ {
+ ExprResult PtrConv = DefaultFunctionArrayLvalueConversion(PtrExpr);
+ if (PtrConv.isInvalid())
+ return PtrConv;
+ PtrExpr = PtrConv.get();
+ TheCall->setArg(1, PtrExpr);
+ if (PtrExpr->isTypeDependent()) {
+ TheCall->setType(Context.DependentTy);
+ return TheCall;
+ }
+ }
+
+ // Check pointer argument.
+ auto *PtrTy = PtrExpr->getType()->getAs<PointerType>();
+ if (!PtrTy) {
+ Diag(PtrExpr->getBeginLoc(), diag::err_builtin_invalid_arg_type)
+ << PtrArgIdx + 1 << 0 << /* pointer to element ty */ 5 << 0
+ << PtrExpr->getType();
+ ArgError = true;
+ } else {
+ QualType ElementTy = PtrTy->getPointeeType();
+ if (ElementTy.isConstQualified()) {
+ Diag(PtrExpr->getBeginLoc(), diag::err_builtin_matrix_store_to_const);
+ ArgError = true;
+ }
+ ElementTy = ElementTy.getUnqualifiedType().getCanonicalType();
+ if (MatrixTy &&
+ !Context.hasSameType(ElementTy, MatrixTy->getElementType())) {
+ Diag(PtrExpr->getBeginLoc(),
+ diag::err_builtin_matrix_pointer_arg_mismatch)
+ << ElementTy << MatrixTy->getElementType();
+ ArgError = true;
+ }
+ }
+
+ // Apply default Lvalue conversions and convert the stride expression to
+ // size_t.
+ {
+ ExprResult StrideConv = DefaultLvalueConversion(StrideExpr);
+ if (StrideConv.isInvalid())
+ return StrideConv;
+
+ StrideConv = tryConvertExprToType(StrideConv.get(), Context.getSizeType());
+ if (StrideConv.isInvalid())
+ return StrideConv;
+ StrideExpr = StrideConv.get();
+ TheCall->setArg(2, StrideExpr);
+ }
+
+ // Check stride argument.
+ if (MatrixTy) {
+ if (std::optional<llvm::APSInt> Value =
+ StrideExpr->getIntegerConstantExpr(Context)) {
+ uint64_t Stride = Value->getZExtValue();
+ if (Stride < MatrixTy->getNumRows()) {
+ Diag(StrideExpr->getBeginLoc(),
+ diag::err_builtin_matrix_stride_too_small);
+ ArgError = true;
+ }
+ }
+ }
+
+ if (ArgError)
+ return ExprError();
+
+ return CallResult;
+}
+
+void Sema::CheckTCBEnforcement(const SourceLocation CallExprLoc,
+ const NamedDecl *Callee) {
+ // This warning does not make sense in code that has no runtime behavior.
+ if (isUnevaluatedContext())
+ return;
+
+ const NamedDecl *Caller = getCurFunctionOrMethodDecl();
+
+ if (!Caller || !Caller->hasAttr<EnforceTCBAttr>())
+ return;
+
+ // Search through the enforce_tcb and enforce_tcb_leaf attributes to find
+ // all TCBs the callee is a part of.
+ llvm::StringSet<> CalleeTCBs;
+ for (const auto *A : Callee->specific_attrs<EnforceTCBAttr>())
+ CalleeTCBs.insert(A->getTCBName());
+ for (const auto *A : Callee->specific_attrs<EnforceTCBLeafAttr>())
+ CalleeTCBs.insert(A->getTCBName());
+
+ // Go through the TCBs the caller is a part of and emit warnings if Caller
+ // is in a TCB that the Callee is not.
+ for (const auto *A : Caller->specific_attrs<EnforceTCBAttr>()) {
+ StringRef CallerTCB = A->getTCBName();
+ if (CalleeTCBs.count(CallerTCB) == 0) {
+ this->Diag(CallExprLoc, diag::warn_tcb_enforcement_violation)
+ << Callee << CallerTCB;
+ }
+ }
+}
diff --git a/clang/test/SemaCXX/warn-unsequenced.cpp b/clang/test/SemaCXX/warn-unsequenced.cpp
index 73b1e3718b211..5b040bb1bae9f 100644
--- a/clang/test/SemaCXX/warn-unsequenced.cpp
+++ b/clang/test/SemaCXX/warn-unsequenced.cpp
@@ -1,843 +1,846 @@
-// RUN: %clang_cc1 -fsyntax-only -verify=cxx11 -std=c++11 -Wno-unused -Wno-uninitialized \
-// RUN: -Wunsequenced -Wno-c++17-extensions -Wno-c++14-extensions %s
-// RUN: %clang_cc1 -fsyntax-only -verify=cxx17 -std=c++17 -Wno-unused -Wno-uninitialized \
-// RUN: -Wunsequenced -Wno-c++17-extensions -Wno-c++14-extensions %s
-
-int f(int, int = 0);
-int g1();
-int g2(int);
-
-struct A {
- int x, y;
-};
-struct S {
- S(int, int);
- int n;
-};
-
-void test() {
- int a;
- int xs[10];
- ++a = 0; // ok
- a + ++a; // cxx11-warning {{unsequenced modification and access to 'a'}}
- // cxx17-warning at -1 {{unsequenced modification and access to 'a'}}
- a = ++a; // ok
- a + a++; // cxx11-warning {{unsequenced modification and access to 'a'}}
- // cxx17-warning at -1 {{unsequenced modification and access to 'a'}}
- a = a++; // cxx11-warning {{multiple unsequenced modifications to 'a'}}
- ++ ++a; // ok
- (a++, a++); // ok
- ++a + ++a; // cxx11-warning {{multiple unsequenced modifications to 'a'}}
- // cxx17-warning at -1 {{multiple unsequenced modifications to 'a'}}
- a++ + a++; // cxx11-warning {{multiple unsequenced modifications to 'a'}}
- // cxx17-warning at -1 {{multiple unsequenced modifications to 'a'}}
- (a++, a) = 0; // ok, increment is sequenced before value computation of LHS
- a = xs[++a]; // ok
- a = xs[a++]; // cxx11-warning {{multiple unsequenced modifications to 'a'}}
- (a ? xs[0] : xs[1]) = ++a; // cxx11-warning {{unsequenced modification and access to 'a'}}
- a = (++a, ++a); // ok
- a = (a++, ++a); // ok
- a = (a++, a++); // cxx11-warning {{multiple unsequenced modifications to 'a'}}
- f(a, a); // ok
- f(a = 0, a); // cxx11-warning {{unsequenced modification and access to 'a'}}
- // cxx17-warning at -1 {{unsequenced modification and access to 'a'}}
- f(a, a += 0); // cxx11-warning {{unsequenced modification and access to 'a'}}
- // cxx17-warning at -1 {{unsequenced modification and access to 'a'}}
- f(a = 0, a = 0); // cxx11-warning {{multiple unsequenced modifications to 'a'}}
- // cxx17-warning at -1 {{multiple unsequenced modifications to 'a'}}
- a = f(++a); // ok
- a = f(a++); // ok
- a = f(++a, a++); // cxx11-warning {{multiple unsequenced modifications to 'a'}}
- // cxx17-warning at -1 {{multiple unsequenced modifications to 'a'}}
-
- // Compound assignment "A OP= B" is equivalent to "A = A OP B" except that A
- // is evaluated only once.
- (++a, a) = 1; // ok
- (++a, a) += 1; // ok
- a = ++a; // ok
- a += ++a; // cxx11-warning {{unsequenced modification and access to 'a'}}
-
- A agg1 = { a++, a++ }; // ok
- A agg2 = { a++ + a, a++ }; // cxx11-warning {{unsequenced modification and access to 'a'}}
- // cxx17-warning at -1 {{unsequenced modification and access to 'a'}}
-
- S str1(a++, a++); // cxx11-warning {{multiple unsequenced modifications to 'a'}}
- // cxx17-warning at -1 {{multiple unsequenced modifications to 'a'}}
- S str2 = { a++, a++ }; // ok
- S str3 = { a++ + a, a++ }; // cxx11-warning {{unsequenced modification and access to 'a'}}
- // cxx17-warning at -1 {{unsequenced modification and access to 'a'}}
-
- struct Z { A a; S s; } z = { { ++a, ++a }, { ++a, ++a } }; // ok
- a = S { ++a, a++ }.n; // ok
- A { ++a, a++ }.x; // ok
- a = A { ++a, a++ }.x; // cxx11-warning {{multiple unsequenced modifications to 'a'}}
- A { ++a, a++ }.x + A { ++a, a++ }.y; // cxx11-warning {{multiple unsequenced modifications to 'a'}}
- // cxx17-warning at -1 {{multiple unsequenced modifications to 'a'}}
-
- (xs[2] && (a = 0)) + a; // cxx11-warning {{unsequenced modification and access to 'a'}}
- // cxx17-warning at -1 {{unsequenced modification and access to 'a'}}
- (0 && (a = 0)) + a; // ok
- (1 && (a = 0)) + a; // cxx11-warning {{unsequenced modification and access to 'a'}}
- // cxx17-warning at -1 {{unsequenced modification and access to 'a'}}
-
- (xs[3] || (a = 0)) + a; // cxx11-warning {{unsequenced modification and access to 'a'}}
- // cxx17-warning at -1 {{unsequenced modification and access to 'a'}}
- (0 || (a = 0)) + a; // cxx11-warning {{unsequenced modification and access to 'a'}}
- // cxx17-warning at -1 {{unsequenced modification and access to 'a'}}
- (1 || (a = 0)) + a; // ok
-
- (xs[4] ? a : ++a) + a; // cxx11-warning {{unsequenced modification and access to 'a'}}
- // cxx17-warning at -1 {{unsequenced modification and access to 'a'}}
- (0 ? a : ++a) + a; // cxx11-warning {{unsequenced modification and access to 'a'}}
- // cxx17-warning at -1 {{unsequenced modification and access to 'a'}}
- (1 ? a : ++a) + a; // ok
- (0 ? a : a++) + a; // cxx11-warning {{unsequenced modification and access to 'a'}}
- // cxx17-warning at -1 {{unsequenced modification and access to 'a'}}
- (1 ? a : a++) + a; // ok
- (xs[5] ? ++a : ++a) + a; // cxx11-warning {{unsequenced modification and access to 'a'}}
- // cxx17-warning at -1 {{unsequenced modification and access to 'a'}}
-
- (++a, xs[6] ? ++a : 0) + a; // cxx11-warning {{unsequenced modification and access to 'a'}}
- // cxx17-warning at -1 {{unsequenced modification and access to 'a'}}
-
- // Here, the read of the fourth 'a' might happen before or after the write to
- // the second 'a'.
- a += (a++, a) + a; // cxx11-warning {{unsequenced modification and access to 'a'}}
- // cxx17-warning at -1 {{unsequenced modification and access to 'a'}}
-
- a = a++ && a; // ok
-
- A *q = &agg1;
- (q = &agg2)->y = q->x; // cxx11-warning {{unsequenced modification and access to 'q'}}
-
- // This has undefined behavior if a == 0; otherwise, the side-effect of the
- // increment is sequenced before the value computation of 'f(a, a)', which is
- // sequenced before the value computation of the '&&', which is sequenced
- // before the assignment. We treat the sequencing in '&&' as being
- // unconditional.
- a = a++ && f(a, a);
-
- // This has undefined behavior if a != 0.
- (a && a++) + a; // cxx11-warning {{unsequenced modification and access to 'a'}}
- // cxx17-warning at -1 {{unsequenced modification and access to 'a'}}
-
- // FIXME: Don't warn here.
- (xs[7] && ++a) * (!xs[7] && ++a); // cxx11-warning {{multiple unsequenced modifications to 'a'}}
- // cxx17-warning at -1 {{multiple unsequenced modifications to 'a'}}
-
- xs[0] = (a = 1, a); // ok
- (a -= 128) &= 128; // ok
- ++a += 1; // ok
-
- xs[8] ? ++a + a++ : 0; // cxx11-warning {{multiple unsequenced modifications to 'a'}}
- // cxx17-warning at -1 {{multiple unsequenced modifications to 'a'}}
- xs[8] ? 0 : ++a + a++; // cxx11-warning {{multiple unsequenced modifications to 'a'}}
- // cxx17-warning at -1 {{multiple unsequenced modifications to 'a'}}
- xs[8] ? ++a : a++; // no-warning
- xs[8] ? a+=1 : a+= 2; // no-warning
- (xs[8] ? a+=1 : a+= 2) = a; // cxx11-warning {{unsequenced modification and access to 'a'}}
- (xs[8] ? a+=1 : a) = a; // cxx11-warning {{unsequenced modification and access to 'a'}}
- (xs[8] ? a : a+= 2) = a; // cxx11-warning {{unsequenced modification and access to 'a'}}
- a = (xs[8] ? a+=1 : a+= 2); // no-warning
- a += (xs[8] ? a+=1 : a+= 2); // cxx11-warning {{unsequenced modification and access to 'a'}}
-
- (false ? a+=1 : a) = a; // no-warning
- (true ? a+=1 : a) = a; // cxx11-warning {{unsequenced modification and access to 'a'}}
- (false ? a : a+=2) = a; // cxx11-warning {{unsequenced modification and access to 'a'}}
- (true ? a : a+=2) = a; // no-warning
-
- xs[8] && (++a + a++); // cxx11-warning {{multiple unsequenced modifications to 'a'}}
- // cxx17-warning at -1 {{multiple unsequenced modifications to 'a'}}
- xs[8] || (++a + a++); // cxx11-warning {{multiple unsequenced modifications to 'a'}}
- // cxx17-warning at -1 {{multiple unsequenced modifications to 'a'}}
-
- ((a++, false) || (a++, false)); // no-warning PR39779
- ((a++, true) && (a++, true)); // no-warning PR39779
-
- int i,j;
- (i = g1(), false) || (j = g2(i)); // no-warning PR22197
- (i = g1(), true) && (j = g2(i)); // no-warning PR22197
-
- (a++, false) || (a++, false) || (a++, false) || (a++, false); // no-warning
- (a++, true) || (a++, true) || (a++, true) || (a++, true); // no-warning
- a = ((a++, false) || (a++, false) || (a++, false) || (a++, false)); // no-warning
- a = ((a++, true) && (a++, true) && (a++, true) && (a++, true)); // no-warning
- a = ((a++, false) || (a++, false) || (a++, false) || a++); // cxx11-warning {{multiple unsequenced modifications to 'a'}}
- a = ((a++, true) && (a++, true) && (a++, true) && a++); // cxx11-warning {{multiple unsequenced modifications to 'a'}}
- a = ((a++, false) || (a++, false) || (a++, false) || (a + a, false)); // no-warning
- a = ((a++, true) && (a++, true) && (a++, true) && (a + a, true)); // no-warning
-
- a = (false && a++); // no-warning
- a = (true && a++); // cxx11-warning {{multiple unsequenced modifications to 'a'}}
- a = (true && ++a); // no-warning
- a = (true || a++); // no-warning
- a = (false || a++); // cxx11-warning {{multiple unsequenced modifications to 'a'}}
- a = (false || ++a); // no-warning
-
- (a++) | (a++); // cxx11-warning {{multiple unsequenced modifications to 'a'}}
- // cxx17-warning at -1 {{multiple unsequenced modifications to 'a'}}
- (a++) & (a++); // cxx11-warning {{multiple unsequenced modifications to 'a'}}
- // cxx17-warning at -1 {{multiple unsequenced modifications to 'a'}}
- (a++) ^ (a++); // cxx11-warning {{multiple unsequenced modifications to 'a'}}
- // cxx17-warning at -1 {{multiple unsequenced modifications to 'a'}}
-
- (__builtin_classify_type(++a) ? 1 : 0) + ++a; // ok
- (__builtin_constant_p(++a) ? 1 : 0) + ++a; // ok
- (__builtin_object_size(&(++a, a), 0) ? 1 : 0) + ++a; // ok
- (__builtin_expect(++a, 0) ? 1 : 0) + ++a; // cxx11-warning {{multiple unsequenced modifications to 'a'}}
- // cxx17-warning at -1 {{multiple unsequenced modifications to 'a'}}
-
-
- int *p = xs;
- a = *(a++, p); // no-warning
- p[(long long unsigned)(p = 0)]; // cxx11-warning {{unsequenced modification and access to 'p'}}
- (i++, xs)[i++]; // cxx11-warning {{multiple unsequenced modifications to 'i'}}
- (++i, xs)[++i]; // cxx11-warning {{multiple unsequenced modifications to 'i'}}
- (i, xs)[++i + ++i]; // cxx11-warning {{multiple unsequenced modifications to 'i'}}
- // cxx17-warning at -1 {{multiple unsequenced modifications to 'i'}}
- p++[p == xs]; // cxx11-warning {{unsequenced modification and access to 'p'}}
- ++p[p++ == xs]; // cxx11-warning {{unsequenced modification and access to 'p'}}
-
- struct S { int x; } s, *ps = &s;
- int (S::*PtrMem);
- (PtrMem = &S::x ,s).*(PtrMem); // cxx11-warning {{unsequenced modification and access to 'PtrMem'}}
- (PtrMem = &S::x ,s).*(PtrMem = &S::x); // cxx11-warning {{multiple unsequenced modifications to 'PtrMem'}}
- (PtrMem = &S::x ,ps)->*(PtrMem); // cxx11-warning {{unsequenced modification and access to 'PtrMem'}}
- (PtrMem = &S::x ,ps)->*(PtrMem = &S::x); // cxx11-warning {{multiple unsequenced modifications to 'PtrMem'}}
- (PtrMem = nullptr) == (PtrMem = nullptr); // cxx11-warning {{multiple unsequenced modifications to 'PtrMem'}}
- // cxx17-warning at -1 {{multiple unsequenced modifications to 'PtrMem'}}
- (PtrMem = nullptr) == PtrMem; // cxx11-warning {{unsequenced modification and access to 'PtrMem'}}
- // cxx17-warning at -1 {{unsequenced modification and access to 'PtrMem'}}
-
- i++ << i++; // cxx11-warning {{multiple unsequenced modifications to 'i'}}
- ++i << ++i; // cxx11-warning {{multiple unsequenced modifications to 'i'}}
- i++ << i; // cxx11-warning {{unsequenced modification and access to 'i'}}
- i << i++; // cxx11-warning {{unsequenced modification and access to 'i'}}
- i++ >> i++; // cxx11-warning {{multiple unsequenced modifications to 'i'}}
- ++i >> ++i; // cxx11-warning {{multiple unsequenced modifications to 'i'}}
- i++ >> i; // cxx11-warning {{unsequenced modification and access to 'i'}}
- i >> i++; // cxx11-warning {{unsequenced modification and access to 'i'}}
- (i++ << i) + i; // cxx11-warning {{unsequenced modification and access to 'i'}}
- // cxx17-warning at -1 {{unsequenced modification and access to 'i'}}
- (i++ << i) << i++; // cxx11-warning {{unsequenced modification and access to 'i'}}
-
- ++i = i++; // cxx11-warning {{multiple unsequenced modifications to 'i'}}
- i = i+= 1; // no-warning
- i = i++ + ++i; // cxx11-warning {{multiple unsequenced modifications to 'i'}}
- // cxx17-warning at -1 {{multiple unsequenced modifications to 'i'}}
- ++i += ++i; // cxx11-warning {{multiple unsequenced modifications to 'i'}}
- ++i += i++; // cxx11-warning {{multiple unsequenced modifications to 'i'}}
- (i++, i) += ++i; // cxx11-warning {{multiple unsequenced modifications to 'i'}}
- (i++, i) += i++; // cxx11-warning {{multiple unsequenced modifications to 'i'}}
- i += i+= 1; // cxx11-warning {{unsequenced modification and access to 'i'}}
- i += i++; // cxx11-warning {{unsequenced modification and access to 'i'}}
- i += ++i; // cxx11-warning {{unsequenced modification and access to 'i'}}
- i -= i++; // cxx11-warning {{unsequenced modification and access to 'i'}}
- i -= ++i; // cxx11-warning {{unsequenced modification and access to 'i'}}
- i *= i++; // cxx11-warning {{unsequenced modification and access to 'i'}}
- i *= ++i; // cxx11-warning {{unsequenced modification and access to 'i'}}
- i /= i++; // cxx11-warning {{unsequenced modification and access to 'i'}}
- i /= ++i; // cxx11-warning {{unsequenced modification and access to 'i'}}
- i %= i++; // cxx11-warning {{unsequenced modification and access to 'i'}}
- i %= ++i; // cxx11-warning {{unsequenced modification and access to 'i'}}
- i ^= i++; // cxx11-warning {{unsequenced modification and access to 'i'}}
- i ^= ++i; // cxx11-warning {{unsequenced modification and access to 'i'}}
- i |= i++; // cxx11-warning {{unsequenced modification and access to 'i'}}
- i |= ++i; // cxx11-warning {{unsequenced modification and access to 'i'}}
- i &= i++; // cxx11-warning {{unsequenced modification and access to 'i'}}
- i &= ++i; // cxx11-warning {{unsequenced modification and access to 'i'}}
- i <<= i++; // cxx11-warning {{unsequenced modification and access to 'i'}}
- i <<= ++i; // cxx11-warning {{unsequenced modification and access to 'i'}}
- i >>= i++; // cxx11-warning {{unsequenced modification and access to 'i'}}
- i >>= ++i; // cxx11-warning {{unsequenced modification and access to 'i'}}
-
- p[i++] = i; // cxx11-warning {{unsequenced modification and access to 'i'}}
- p[i++] = (i = 42); // cxx11-warning {{multiple unsequenced modifications to 'i'}}
- p++[i++] = (i = p ? i++ : i++); // cxx11-warning {{unsequenced modification and access to 'p'}}
- // cxx11-warning at -1 {{multiple unsequenced modifications to 'i'}}
-
- (i++, f)(i++, 42); // cxx11-warning {{multiple unsequenced modifications to 'i'}}
- (i++ + i++, f)(42, 42); // cxx11-warning {{multiple unsequenced modifications to 'i'}}
- // cxx17-warning at -1 {{multiple unsequenced modifications to 'i'}}
- int (*pf)(int, int);
- (pf = f)(pf != nullptr, pf != nullptr); // cxx11-warning {{unsequenced modification and access to 'pf'}}
- pf((pf = f) != nullptr, 42); // cxx11-warning {{unsequenced modification and access to 'pf'}}
- f((pf = f, 42), (pf = f, 42)); // cxx11-warning {{multiple unsequenced modifications to 'pf'}}
- // cxx17-warning at -1 {{multiple unsequenced modifications to 'pf'}}
- pf((pf = f) != nullptr, pf == nullptr); // cxx11-warning {{unsequenced modification and access to 'pf'}}
- // cxx17-warning at -1 {{unsequenced modification and access to 'pf'}}
-}
-
-namespace PR20819 {
- struct foo { void bar(int); };
- foo get_foo(int);
-
- void g() {
- int a = 0;
- get_foo(a).bar(a++); // cxx11-warning {{unsequenced modification and access to 'a'}}
- }
-}
-
-namespace overloaded_operators {
- struct E {
- E &operator=(E &);
- E operator()(E);
- E operator()(E, E);
- E operator[](E);
- } e;
- // Binary operators with unsequenced operands.
- E operator+(E,E);
- E operator-(E,E);
- E operator*(E,E);
- E operator/(E,E);
- E operator%(E,E);
- E operator^(E,E);
- E operator&(E,E);
- E operator|(E,E);
-
- E operator<(E,E);
- E operator>(E,E);
- E operator==(E,E);
- E operator!=(E,E);
- E operator>=(E,E);
- E operator<=(E,E);
-
- // Binary operators where the RHS is sequenced before the LHS in C++17.
- E operator+=(E,E);
- E operator-=(E,E);
- E operator*=(E,E);
- E operator/=(E,E);
- E operator%=(E,E);
- E operator^=(E,E);
- E operator&=(E,E);
- E operator|=(E,E);
- E operator<<=(E,E);
- E operator>>=(E,E);
-
- // Binary operators where the LHS is sequenced before the RHS in C++17.
- E operator<<(E,E);
- E operator>>(E,E);
- E operator&&(E,E);
- E operator||(E,E);
- E operator,(E,E);
- E operator->*(E,E);
-
- void test() {
- int i = 0;
- // Binary operators with unsequenced operands.
- ((void)i++,e) + ((void)i++,e);
- // cxx11-warning at -1 {{multiple unsequenced modifications to 'i'}}
- // cxx17-warning at -2 {{multiple unsequenced modifications to 'i'}}
- ((void)i++,e) - ((void)i++,e);
- // cxx11-warning at -1 {{multiple unsequenced modifications to 'i'}}
- // cxx17-warning at -2 {{multiple unsequenced modifications to 'i'}}
- ((void)i++,e) * ((void)i++,e);
- // cxx11-warning at -1 {{multiple unsequenced modifications to 'i'}}
- // cxx17-warning at -2 {{multiple unsequenced modifications to 'i'}}
- ((void)i++,e) / ((void)i++,e);
- // cxx11-warning at -1 {{multiple unsequenced modifications to 'i'}}
- // cxx17-warning at -2 {{multiple unsequenced modifications to 'i'}}
- ((void)i++,e) % ((void)i++,e);
- // cxx11-warning at -1 {{multiple unsequenced modifications to 'i'}}
- // cxx17-warning at -2 {{multiple unsequenced modifications to 'i'}}
- ((void)i++,e) ^ ((void)i++,e);
- // cxx11-warning at -1 {{multiple unsequenced modifications to 'i'}}
- // cxx17-warning at -2 {{multiple unsequenced modifications to 'i'}}
- ((void)i++,e) & ((void)i++,e);
- // cxx11-warning at -1 {{multiple unsequenced modifications to 'i'}}
- // cxx17-warning at -2 {{multiple unsequenced modifications to 'i'}}
- ((void)i++,e) | ((void)i++,e);
- // cxx11-warning at -1 {{multiple unsequenced modifications to 'i'}}
- // cxx17-warning at -2 {{multiple unsequenced modifications to 'i'}}
-
- ((void)i++,e) < ((void)i++,e);
- // cxx11-warning at -1 {{multiple unsequenced modifications to 'i'}}
- // cxx17-warning at -2 {{multiple unsequenced modifications to 'i'}}
- ((void)i++,e) > ((void)i++,e);
- // cxx11-warning at -1 {{multiple unsequenced modifications to 'i'}}
- // cxx17-warning at -2 {{multiple unsequenced modifications to 'i'}}
- ((void)i++,e) == ((void)i++,e);
- // cxx11-warning at -1 {{multiple unsequenced modifications to 'i'}}
- // cxx17-warning at -2 {{multiple unsequenced modifications to 'i'}}
- ((void)i++,e) != ((void)i++,e);
- // cxx11-warning at -1 {{multiple unsequenced modifications to 'i'}}
- // cxx17-warning at -2 {{multiple unsequenced modifications to 'i'}}
- ((void)i++,e) <= ((void)i++,e);
- // cxx11-warning at -1 {{multiple unsequenced modifications to 'i'}}
- // cxx17-warning at -2 {{multiple unsequenced modifications to 'i'}}
- ((void)i++,e) >= ((void)i++,e);
- // cxx11-warning at -1 {{multiple unsequenced modifications to 'i'}}
- // cxx17-warning at -2 {{multiple unsequenced modifications to 'i'}}
-
- // Binary operators where the RHS is sequenced before the LHS in C++17.
- ((void)i++,e) = ((void)i++,e);
- // cxx11-warning at -1 {{multiple unsequenced modifications to 'i'}}
- ((void)i++,e) += ((void)i++,e);
- // cxx11-warning at -1 {{multiple unsequenced modifications to 'i'}}
- ((void)i++,e) -= ((void)i++,e);
- // cxx11-warning at -1 {{multiple unsequenced modifications to 'i'}}
- ((void)i++,e) *= ((void)i++,e);
- // cxx11-warning at -1 {{multiple unsequenced modifications to 'i'}}
- ((void)i++,e) /= ((void)i++,e);
- // cxx11-warning at -1 {{multiple unsequenced modifications to 'i'}}
- ((void)i++,e) %= ((void)i++,e);
- // cxx11-warning at -1 {{multiple unsequenced modifications to 'i'}}
- ((void)i++,e) ^= ((void)i++,e);
- // cxx11-warning at -1 {{multiple unsequenced modifications to 'i'}}
- ((void)i++,e) &= ((void)i++,e);
- // cxx11-warning at -1 {{multiple unsequenced modifications to 'i'}}
- ((void)i++,e) |= ((void)i++,e);
- // cxx11-warning at -1 {{multiple unsequenced modifications to 'i'}}
- ((void)i++,e) <<= ((void)i++,e);
- // cxx11-warning at -1 {{multiple unsequenced modifications to 'i'}}
- ((void)i++,e) >>= ((void)i++,e);
- // cxx11-warning at -1 {{multiple unsequenced modifications to 'i'}}
-
- operator+=(((void)i++,e), ((void)i++,e));
- // cxx11-warning at -1 {{multiple unsequenced modifications to 'i'}}
- // cxx17-warning at -2 {{multiple unsequenced modifications to 'i'}}
-
- // Binary operators where the LHS is sequenced before the RHS in C++17.
- ((void)i++,e) << ((void)i++,e);
- // cxx11-warning at -1 {{multiple unsequenced modifications to 'i'}}
- ((void)i++,e) >> ((void)i++,e);
- // cxx11-warning at -1 {{multiple unsequenced modifications to 'i'}}
- ((void)i++,e) || ((void)i++,e);
- // cxx11-warning at -1 {{multiple unsequenced modifications to 'i'}}
- ((void)i++,e) && ((void)i++,e);
- // cxx11-warning at -1 {{multiple unsequenced modifications to 'i'}}
- ((void)i++,e) , ((void)i++,e);
- // cxx11-warning at -1 {{multiple unsequenced modifications to 'i'}}
- ((void)i++,e)->*((void)i++,e);
- // cxx11-warning at -1 {{multiple unsequenced modifications to 'i'}}
-
- operator<<(((void)i++,e), ((void)i++,e));
- // cxx11-warning at -1 {{multiple unsequenced modifications to 'i'}}
- // cxx17-warning at -2 {{multiple unsequenced modifications to 'i'}}
-
- ((void)i++,e)[((void)i++,e)];
- // cxx11-warning at -1 {{multiple unsequenced modifications to 'i'}}
-
- ((void)i++,e)(((void)i++,e));
- // cxx11-warning at -1 {{multiple unsequenced modifications to 'i'}}
- e(((void)i++,e), ((void)i++,e));
- // cxx11-warning at -1 {{multiple unsequenced modifications to 'i'}}
- // cxx17-warning at -2 {{multiple unsequenced modifications to 'i'}}
-
- ((void)i++,e).operator()(((void)i++,e));
- // cxx11-warning at -1 {{multiple unsequenced modifications to 'i'}}
-
- }
-}
-
-namespace PR35340 {
- struct S {};
- S &operator<<(S &, int);
-
- void test() {
- S s;
- int i = 0;
- s << i++ << i++;
- // cxx11-warning at -1 {{multiple unsequenced modifications to 'i'}}
-
- operator<<(operator<<(s, i++), i++);
- // cxx11-warning at -1 {{multiple unsequenced modifications to 'i'}}
- // cxx17-warning at -2 {{multiple unsequenced modifications to 'i'}}
- }
-}
-
-namespace members {
-
-struct S1 {
- unsigned bf1 : 2;
- unsigned bf2 : 2;
- unsigned a;
- unsigned b;
- static unsigned x;
- void member_f(S1 &s);
-};
-
-void S1::member_f(S1 &s) {
- ++a + ++a; // cxx11-warning {{multiple unsequenced modifications to 'a'}}
- // cxx17-warning at -1 {{multiple unsequenced modifications to 'a'}}
- a + ++a; // cxx11-warning {{unsequenced modification and access to 'a'}}
- // cxx17-warning at -1 {{unsequenced modification and access to 'a'}}
- ++a + ++b; // no-warning
- a + ++b; // no-warning
-
- // TODO: Warn here.
- ++s.a + ++s.a; // no-warning TODO {{multiple unsequenced modifications to}}
- s.a + ++s.a; // no-warning TODO {{unsequenced modification and access to}}
- ++s.a + ++s.b; // no-warning
- s.a + ++s.b; // no-warning
-
- ++a + ++s.a; // no-warning
- a + ++s.a; // no-warning
- ++a + ++s.b; // no-warning
- a + ++s.b; // no-warning
-
- // TODO Warn here for bit-fields in the same memory location.
- ++bf1 + ++bf1; // cxx11-warning {{multiple unsequenced modifications to 'bf1'}}
- // cxx17-warning at -1 {{multiple unsequenced modifications to 'bf1'}}
- bf1 + ++bf1; // cxx11-warning {{unsequenced modification and access to 'bf1'}}
- // cxx17-warning at -1 {{unsequenced modification and access to 'bf1'}}
- ++bf1 + ++bf2; // no-warning TODO {{multiple unsequenced modifications to}}
- bf1 + ++bf2; // no-warning TODO {{unsequenced modification and access to}}
-
- // TODO Warn here for bit-fields in the same memory location.
- ++s.bf1 + ++s.bf1; // no-warning TODO {{multiple unsequenced modifications to}}
- s.bf1 + ++s.bf1; // no-warning TODO {{unsequenced modification and access to}}
- ++s.bf1 + ++s.bf2; // no-warning TODO {{multiple unsequenced modifications to}}
- s.bf1 + ++s.bf2; // no-warning TODO {{unsequenced modification and access to}}
-
- ++bf1 + ++s.bf1; // no-warning
- bf1 + ++s.bf1; // no-warning
- ++bf1 + ++s.bf2; // no-warning
- bf1 + ++s.bf2; // no-warning
-
- struct Der : S1 {};
- Der d;
- Der &d_ref = d;
- S1 &s1_ref = d_ref;
-
- ++s1_ref.a + ++d_ref.a; // no-warning TODO {{multiple unsequenced modifications to member 'a' of 'd'}}
- ++s1_ref.a + d_ref.a; // no-warning TODO {{unsequenced modification and access to member 'a' of 'd'}}
- ++s1_ref.a + ++d_ref.b; // no-warning
- ++s1_ref.a + d_ref.b; // no-warning
-
- ++x + ++x; // cxx11-warning {{multiple unsequenced modifications to 'x'}}
- // cxx17-warning at -1 {{multiple unsequenced modifications to 'x'}}
- ++x + x; // cxx11-warning {{unsequenced modification and access to 'x'}}
- // cxx17-warning at -1 {{unsequenced modification and access to 'x'}}
- ++s.x + x; // no-warning TODO {{unsequenced modification and access to static member 'x' of 'S1'}}
- ++this->x + x; // cxx11-warning {{unsequenced modification and access to 'x'}}
- // cxx17-warning at -1 {{unsequenced modification and access to 'x'}}
- ++d_ref.x + ++S1::x; // no-warning TODO {{unsequenced modification and access to static member 'x' of 'S1'}}
-}
-
-struct S2 {
- union { unsigned x, y; };
- void f2();
-};
-
-void S2::f2() {
- ++x + ++x; // no-warning TODO {{multiple unsequenced modifications to}}
- x + ++x; // no-warning TODO {{unsequenced modification and access to}}
- ++x + ++y; // no-warning
- x + ++y; // no-warning
-}
-
-void f2(S2 &s) {
- ++s.x + ++s.x; // no-warning TODO {{multiple unsequenced modifications to}}
- s.x + ++s.x; // no-warning TODO {{unsequenced modification and access to}}
- ++s.x + ++s.y; // no-warning
- s.x + ++s.y; // no-warning
-}
-
-struct S3 {
- union {
- union {
- unsigned x;
- };
- };
- unsigned y;
- void f3();
-};
-
-void S3::f3() {
- ++x + ++x; // no-warning TODO {{multiple unsequenced modifications to}}
- x + ++x; // no-warning TODO {{unsequenced modification and access to}}
- ++x + ++y; // no-warning
- x + ++y; // no-warning
-}
-
-void f3(S3 &s) {
- ++s.x + ++s.x; // no-warning TODO {{multiple unsequenced modifications to}}
- s.x + ++s.x; // no-warning TODO {{unsequenced modification and access to}}
- ++s.x + ++s.y; // no-warning
- s.x + ++s.y; // no-warning
-}
-
-struct S4 : S3 {
- unsigned y;
- void f4();
-};
-
-void S4::f4() {
- ++x + ++x; // no-warning TODO {{multiple unsequenced modifications to}}
- x + ++x; // no-warning TODO {{unsequenced modification and access to}}
- ++x + ++y; // no-warning
- x + ++y; // no-warning
- ++S3::y + ++y; // no-warning
- S3::y + ++y; // no-warning
-}
-
-void f4(S4 &s) {
- ++s.x + ++s.x; // no-warning TODO {{multiple unsequenced modifications to}}
- s.x + ++s.x; // no-warning TODO {{unsequenced modification and access to}}
- ++s.x + ++s.y; // no-warning
- s.x + ++s.y; // no-warning
- ++s.S3::y + ++s.y; // no-warning
- s.S3::y + ++s.y; // no-warning
-}
-
-static union {
- unsigned Ux;
- unsigned Uy;
-};
-
-void f5() {
- ++Ux + ++Ux; // no-warning TODO {{multiple unsequenced modifications to}}
- Ux + ++Ux; // no-warning TODO {{unsequenced modification and access to}}
- ++Ux + ++Uy; // no-warning
- Ux + ++Uy; // no-warning
-}
-
-void f6() {
- struct S { unsigned x, y; } s;
- ++s.x + ++s.x; // no-warning TODO {{multiple unsequenced modifications to}}
- s.x + ++s.x; // no-warning TODO {{unsequenced modification and access to}}
- ++s.x + ++s.y; // no-warning
- s.x + ++s.y; // no-warning
-
- struct { unsigned x, y; } t;
- ++t.x + ++t.x; // no-warning TODO {{multiple unsequenced modifications to}}
- t.x + ++t.x; // no-warning TODO {{unsequenced modification and access to}}
- ++t.x + ++t.y; // no-warning
- t.x + ++t.y; // no-warning
-}
-
-} // namespace members
-
-namespace references {
-void reference_f() {
- // TODO: Check that we can see through references.
- // For now this is completely unhandled.
- int a;
- int xs[10];
- int &b = a;
- int &c = b;
- int &ra1 = c;
- int &ra2 = b;
- int other;
-
- ++ra1 + ++ra2; // no-warning TODO {{multiple unsequenced modifications to}}
- ra1 + ++ra2; // no-warning TODO {{unsequenced modification and access to}}
- ++ra1 + ++other; // no-warning
- ra1 + ++other; // no-warning
-
- // Make sure we handle reference cycles.
- int &ref_cycle = ref_cycle;
- ++ref_cycle + ++ref_cycle; // cxx11-warning {{multiple unsequenced modifications to 'ref_cycle'}}
- // cxx17-warning at -1 {{multiple unsequenced modifications to 'ref_cycle'}}
- ref_cycle + ++ref_cycle; // cxx11-warning {{unsequenced modification and access to 'ref_cycle'}}
- // cxx17-warning at -1 {{unsequenced modification and access to 'ref_cycle'}}
-}
-} // namespace references
-
-namespace std {
- using size_t = decltype(sizeof(0));
- template<typename> struct tuple_size;
- template<size_t, typename> struct tuple_element { using type = int; };
-}
-namespace bindings {
-
- struct A { int x, y; };
- typedef int B[2];
- struct C { template<int> int get(); };
- struct D : A {};
-
-} // namespace bindings
-template<> struct std::tuple_size<bindings::C> { enum { value = 2 }; };
-namespace bindings {
-void testa() {
- A a;
- {
- auto [x, y] = a;
- ++x + ++x; // cxx11-warning {{multiple unsequenced modifications to 'x'}}
- // cxx17-warning at -1 {{multiple unsequenced modifications to 'x'}}
- ++x + x; // cxx11-warning {{unsequenced modification and access to 'x'}}
- // cxx17-warning at -1 {{unsequenced modification and access to 'x'}}
- ++x + ++y; // no-warning
- ++x + y; // no-warning
- ++x + ++a.x; // no-warning
- ++x + a.x; // no-warning
- }
- {
- auto &[x, y] = a;
- ++x + ++x; // cxx11-warning {{multiple unsequenced modifications to 'x'}}
- // cxx17-warning at -1 {{multiple unsequenced modifications to 'x'}}
- ++x + x; // cxx11-warning {{unsequenced modification and access to 'x'}}
- // cxx17-warning at -1 {{unsequenced modification and access to 'x'}}
- ++x + ++y; // no-warning
- ++x + y; // no-warning
- ++x + ++a.x; // no-warning TODO
- ++x + a.x; // no-warning TODO
- }
-}
-void testb() {
- B b;
- {
- auto [x, y] = b;
- ++x + ++x; // cxx11-warning {{multiple unsequenced modifications to 'x'}}
- // cxx17-warning at -1 {{multiple unsequenced modifications to 'x'}}
- ++x + x; // cxx11-warning {{unsequenced modification and access to 'x'}}
- // cxx17-warning at -1 {{unsequenced modification and access to 'x'}}
- ++x + ++y; // no-warning
- ++x + y; // no-warning
- ++x + ++b[0]; // no-warning
- ++x + b[0]; // no-warning
- }
- {
- auto &[x, y] = b;
- ++x + ++x; // cxx11-warning {{multiple unsequenced modifications to 'x'}}
- // cxx17-warning at -1 {{multiple unsequenced modifications to 'x'}}
- ++x + x; // cxx11-warning {{unsequenced modification and access to 'x'}}
- // cxx17-warning at -1 {{unsequenced modification and access to 'x'}}
- ++x + ++y; // no-warning
- ++x + y; // no-warning
- ++x + ++b[0]; // no-warning TODO
- ++x + b[0]; // no-warning TODO
- }
-}
-void testc() {
- C c;
- {
- auto [x, y] = c;
- ++x + ++x; // cxx11-warning {{multiple unsequenced modifications to 'x'}}
- // cxx17-warning at -1 {{multiple unsequenced modifications to 'x'}}
- ++x + x; // cxx11-warning {{unsequenced modification and access to 'x'}}
- // cxx17-warning at -1 {{unsequenced modification and access to 'x'}}
- ++x + ++y; // no-warning
- ++x + y; // no-warning
- }
- {
- auto &[x, y] = c;
- ++x + ++x; // cxx11-warning {{multiple unsequenced modifications to 'x'}}
- // cxx17-warning at -1 {{multiple unsequenced modifications to 'x'}}
- ++x + x; // cxx11-warning {{unsequenced modification and access to 'x'}}
- // cxx17-warning at -1 {{unsequenced modification and access to 'x'}}
- ++x + ++y; // no-warning
- ++x + y; // no-warning
- }
-}
-void testd() {
- D d;
- {
- auto [x, y] = d;
- ++x + ++x; // cxx11-warning {{multiple unsequenced modifications to 'x'}}
- // cxx17-warning at -1 {{multiple unsequenced modifications to 'x'}}
- ++x + x; // cxx11-warning {{unsequenced modification and access to 'x'}}
- // cxx17-warning at -1 {{unsequenced modification and access to 'x'}}
- ++x + ++y; // no-warning
- ++x + y; // no-warning
- ++x + ++d.x; // no-warning
- ++x + d.x; // no-warning
- }
- {
- auto &[x, y] = d;
- ++x + ++x; // cxx11-warning {{multiple unsequenced modifications to 'x'}}
- // cxx17-warning at -1 {{multiple unsequenced modifications to 'x'}}
- ++x + x; // cxx11-warning {{unsequenced modification and access to 'x'}}
- // cxx17-warning at -1 {{unsequenced modification and access to 'x'}}
- ++x + ++y; // no-warning
- ++x + y; // no-warning
- ++x + ++d.x; // no-warning TODO
- ++x + d.x; // no-warning TODO
- }
-}
-} // namespace bindings
-
-namespace templates {
-
-template <typename T>
-struct Bar {
- T get() { return 0; }
-};
-
-template <typename X>
-struct Foo {
- int Run();
- Bar<int> bar;
-};
-
-enum E {e1, e2};
-bool operator&&(E, E);
-
-void foo(int, int);
-
-template <typename X>
-int Foo<X>::Run() {
- char num = 0;
-
- // Before instantiation, Clang may consider the builtin operator here as
- // unresolved function calls, and treat the arguments as unordered when
- // the builtin operator evaluatation is well-ordered. Waiting until
- // instantiation to check these expressions will prevent false positives.
- if ((num = bar.get()) < 5 && num < 10) { }
- if ((num = bar.get()) < 5 || num < 10) { }
- if (static_cast<E>((num = bar.get()) < 5) || static_cast<E>(num < 10)) { }
-
- if (static_cast<E>((num = bar.get()) < 5) && static_cast<E>(num < 10)) { }
- // cxx11-warning at -1 {{unsequenced modification and access to 'num'}}
-
- foo(num++, num++);
- // cxx11-warning at -1 {{multiple unsequenced modifications to 'num'}}
- // cxx17-warning at -2 {{multiple unsequenced modifications to 'num'}}
- return 1;
-}
-
-int x = Foo<int>().Run();
-// cxx11-note at -1 {{in instantiation of member function 'templates::Foo<int>::Run'}}
-// cxx17-note at -2 {{in instantiation of member function 'templates::Foo<int>::Run'}}
-
-
-template <typename T>
-int Run2() {
- T t = static_cast<T>(0);
- return (t = static_cast<T>(1)) && t;
- // cxx11-warning at -1 {{unsequenced modification and access to 't'}}
-}
-
-int y = Run2<bool>();
-int z = Run2<E>();
-// cxx11-note at -1{{in instantiation of function template specialization 'templates::Run2<templates::E>' requested here}}
-
-template <typename T> int var = sizeof(T);
-void test_var() {
- var<int>++ + var<int>++; // cxx11-warning {{multiple unsequenced modifications to 'var<int>'}}
- // cxx17-warning at -1 {{multiple unsequenced modifications to 'var<int>'}}
- var<int>++ + var<int>; // cxx11-warning {{unsequenced modification and access to 'var<int>'}}
- // cxx17-warning at -1 {{unsequenced modification and access to 'var<int>'}}
- int &r = var<int>;
- r++ + var<int>++; // no-warning TODO {{multiple unsequenced modifications to 'var<int>'}}
- r++ + var<long>++; // no-warning
-}
-
-} // namespace templates
-
-namespace muliple_read_volatile {
- volatile int v1;
-
- void PositiveTest(){
- int x = 0;
- int y = 0;
- x = v1 + v1; // cxx11-warning {{unsequenced accesses to volatile qualified 'v1'}}
- // cxx17-warning at -1 {{unsequenced accesses to volatile qualified 'v1'}}
- v1 = v1 * v1; // cxx11-warning {{unsequenced accesses to volatile qualified 'v1'}}
- // cxx17-warning at -1 {{unsequenced accesses to volatile qualified 'v1'}}
- x = v1 + (y++, v1); // cxx11-warning {{unsequenced accesses to volatile qualified 'v1'}}
- // cxx17-warning at -1 {{unsequenced accesses to volatile qualified 'v1'}}
- x = v1 + v1 || y; // cxx11-warning {{unsequenced accesses to volatile qualified 'v1'}}
- // cxx17-warning at -1 {{unsequenced accesses to volatile qualified 'v1'}}
- }
-
- void NegativeTest(){
- int x = 0;
- int y = 0;
- x = v1 + y; // no-warning
- v1 = v1 * y; // no-warning
- x = (v1, v1); // no-warning
- x = v1 || v1; // no-warning
- }
-} // namespace muliple_read_volatile
+// RUN: %clang_cc1 -fsyntax-only -verify=cxx11 -std=c++11 -Wno-unused -Wno-uninitialized \
+// RUN: -Wunsequenced -Wno-c++17-extensions -Wno-c++14-extensions %s
+// RUN: %clang_cc1 -fsyntax-only -verify=cxx17 -std=c++17 -Wno-unused -Wno-uninitialized \
+// RUN: -Wunsequenced -Wno-c++17-extensions -Wno-c++14-extensions %s
+
+int f(int, int = 0);
+int g1();
+int g2(int);
+
+struct A {
+ int x, y;
+};
+struct S {
+ S(int, int);
+ int n;
+};
+
+void test() {
+ int a;
+ int xs[10];
+ ++a = 0; // ok
+ a + ++a; // cxx11-warning {{unsequenced modification and access to 'a'}}
+ // cxx17-warning at -1 {{unsequenced modification and access to 'a'}}
+ a = ++a; // ok
+ a + a++; // cxx11-warning {{unsequenced modification and access to 'a'}}
+ // cxx17-warning at -1 {{unsequenced modification and access to 'a'}}
+ a = a++; // cxx11-warning {{multiple unsequenced modifications to 'a'}}
+ ++ ++a; // ok
+ (a++, a++); // ok
+ ++a + ++a; // cxx11-warning {{multiple unsequenced modifications to 'a'}}
+ // cxx17-warning at -1 {{multiple unsequenced modifications to 'a'}}
+ a++ + a++; // cxx11-warning {{multiple unsequenced modifications to 'a'}}
+ // cxx17-warning at -1 {{multiple unsequenced modifications to 'a'}}
+ (a++, a) = 0; // ok, increment is sequenced before value computation of LHS
+ a = xs[++a]; // ok
+ a = xs[a++]; // cxx11-warning {{multiple unsequenced modifications to 'a'}}
+ (a ? xs[0] : xs[1]) = ++a; // cxx11-warning {{unsequenced modification and access to 'a'}}
+ a = (++a, ++a); // ok
+ a = (a++, ++a); // ok
+ a = (a++, a++); // cxx11-warning {{multiple unsequenced modifications to 'a'}}
+ f(a, a); // ok
+ f(a = 0, a); // cxx11-warning {{unsequenced modification and access to 'a'}}
+ // cxx17-warning at -1 {{unsequenced modification and access to 'a'}}
+ f(a, a += 0); // cxx11-warning {{unsequenced modification and access to 'a'}}
+ // cxx17-warning at -1 {{unsequenced modification and access to 'a'}}
+ f(a = 0, a = 0); // cxx11-warning {{multiple unsequenced modifications to 'a'}}
+ // cxx17-warning at -1 {{multiple unsequenced modifications to 'a'}}
+ a = f(++a); // ok
+ a = f(a++); // ok
+ a = f(++a, a++); // cxx11-warning {{multiple unsequenced modifications to 'a'}}
+ // cxx17-warning at -1 {{multiple unsequenced modifications to 'a'}}
+
+ // Compound assignment "A OP= B" is equivalent to "A = A OP B" except that A
+ // is evaluated only once.
+ (++a, a) = 1; // ok
+ (++a, a) += 1; // ok
+ a = ++a; // ok
+ a += ++a; // cxx11-warning {{unsequenced modification and access to 'a'}}
+
+ A agg1 = { a++, a++ }; // ok
+ A agg2 = { a++ + a, a++ }; // cxx11-warning {{unsequenced modification and access to 'a'}}
+ // cxx17-warning at -1 {{unsequenced modification and access to 'a'}}
+
+ S str1(a++, a++); // cxx11-warning {{multiple unsequenced modifications to 'a'}}
+ // cxx17-warning at -1 {{multiple unsequenced modifications to 'a'}}
+ S str2 = { a++, a++ }; // ok
+ S str3 = { a++ + a, a++ }; // cxx11-warning {{unsequenced modification and access to 'a'}}
+ // cxx17-warning at -1 {{unsequenced modification and access to 'a'}}
+
+ struct Z { A a; S s; } z = { { ++a, ++a }, { ++a, ++a } }; // ok
+ a = S { ++a, a++ }.n; // ok
+ A { ++a, a++ }.x; // ok
+ a = A { ++a, a++ }.x; // cxx11-warning {{multiple unsequenced modifications to 'a'}}
+ A { ++a, a++ }.x + A { ++a, a++ }.y; // cxx11-warning {{multiple unsequenced modifications to 'a'}}
+ // cxx17-warning at -1 {{multiple unsequenced modifications to 'a'}}
+
+ (xs[2] && (a = 0)) + a; // cxx11-warning {{unsequenced modification and access to 'a'}}
+ // cxx17-warning at -1 {{unsequenced modification and access to 'a'}}
+ (0 && (a = 0)) + a; // ok
+ (1 && (a = 0)) + a; // cxx11-warning {{unsequenced modification and access to 'a'}}
+ // cxx17-warning at -1 {{unsequenced modification and access to 'a'}}
+
+ (xs[3] || (a = 0)) + a; // cxx11-warning {{unsequenced modification and access to 'a'}}
+ // cxx17-warning at -1 {{unsequenced modification and access to 'a'}}
+ (0 || (a = 0)) + a; // cxx11-warning {{unsequenced modification and access to 'a'}}
+ // cxx17-warning at -1 {{unsequenced modification and access to 'a'}}
+ (1 || (a = 0)) + a; // ok
+
+ (xs[4] ? a : ++a) + a; // cxx11-warning {{unsequenced modification and access to 'a'}}
+ // cxx17-warning at -1 {{unsequenced modification and access to 'a'}}
+ (0 ? a : ++a) + a; // cxx11-warning {{unsequenced modification and access to 'a'}}
+ // cxx17-warning at -1 {{unsequenced modification and access to 'a'}}
+ (1 ? a : ++a) + a; // ok
+ (0 ? a : a++) + a; // cxx11-warning {{unsequenced modification and access to 'a'}}
+ // cxx17-warning at -1 {{unsequenced modification and access to 'a'}}
+ (1 ? a : a++) + a; // ok
+ (xs[5] ? ++a : ++a) + a; // cxx11-warning {{unsequenced modification and access to 'a'}}
+ // cxx17-warning at -1 {{unsequenced modification and access to 'a'}}
+
+ (++a, xs[6] ? ++a : 0) + a; // cxx11-warning {{unsequenced modification and access to 'a'}}
+ // cxx17-warning at -1 {{unsequenced modification and access to 'a'}}
+
+ // Here, the read of the fourth 'a' might happen before or after the write to
+ // the second 'a'.
+ a += (a++, a) + a; // cxx11-warning {{unsequenced modification and access to 'a'}}
+ // cxx17-warning at -1 {{unsequenced modification and access to 'a'}}
+
+ a = a++ && a; // ok
+
+ A *q = &agg1;
+ (q = &agg2)->y = q->x; // cxx11-warning {{unsequenced modification and access to 'q'}}
+
+ // This has undefined behavior if a == 0; otherwise, the side-effect of the
+ // increment is sequenced before the value computation of 'f(a, a)', which is
+ // sequenced before the value computation of the '&&', which is sequenced
+ // before the assignment. We treat the sequencing in '&&' as being
+ // unconditional.
+ a = a++ && f(a, a);
+
+ // This has undefined behavior if a != 0.
+ (a && a++) + a; // cxx11-warning {{unsequenced modification and access to 'a'}}
+ // cxx17-warning at -1 {{unsequenced modification and access to 'a'}}
+
+ // FIXME: Don't warn here.
+ (xs[7] && ++a) * (!xs[7] && ++a); // cxx11-warning {{multiple unsequenced modifications to 'a'}}
+ // cxx17-warning at -1 {{multiple unsequenced modifications to 'a'}}
+
+ xs[0] = (a = 1, a); // ok
+ (a -= 128) &= 128; // ok
+ ++a += 1; // ok
+
+ xs[8] ? ++a + a++ : 0; // cxx11-warning {{multiple unsequenced modifications to 'a'}}
+ // cxx17-warning at -1 {{multiple unsequenced modifications to 'a'}}
+ xs[8] ? 0 : ++a + a++; // cxx11-warning {{multiple unsequenced modifications to 'a'}}
+ // cxx17-warning at -1 {{multiple unsequenced modifications to 'a'}}
+ xs[8] ? ++a : a++; // no-warning
+ xs[8] ? a+=1 : a+= 2; // no-warning
+ (xs[8] ? a+=1 : a+= 2) = a; // cxx11-warning {{unsequenced modification and access to 'a'}}
+ (xs[8] ? a+=1 : a) = a; // cxx11-warning {{unsequenced modification and access to 'a'}}
+ (xs[8] ? a : a+= 2) = a; // cxx11-warning {{unsequenced modification and access to 'a'}}
+ a = (xs[8] ? a+=1 : a+= 2); // no-warning
+ a += (xs[8] ? a+=1 : a+= 2); // cxx11-warning {{unsequenced modification and access to 'a'}}
+
+ (false ? a+=1 : a) = a; // no-warning
+ (true ? a+=1 : a) = a; // cxx11-warning {{unsequenced modification and access to 'a'}}
+ (false ? a : a+=2) = a; // cxx11-warning {{unsequenced modification and access to 'a'}}
+ (true ? a : a+=2) = a; // no-warning
+
+ xs[8] && (++a + a++); // cxx11-warning {{multiple unsequenced modifications to 'a'}}
+ // cxx17-warning at -1 {{multiple unsequenced modifications to 'a'}}
+ xs[8] || (++a + a++); // cxx11-warning {{multiple unsequenced modifications to 'a'}}
+ // cxx17-warning at -1 {{multiple unsequenced modifications to 'a'}}
+
+ ((a++, false) || (a++, false)); // no-warning PR39779
+ ((a++, true) && (a++, true)); // no-warning PR39779
+
+ int i,j;
+ (i = g1(), false) || (j = g2(i)); // no-warning PR22197
+ (i = g1(), true) && (j = g2(i)); // no-warning PR22197
+
+ (a++, false) || (a++, false) || (a++, false) || (a++, false); // no-warning
+ (a++, true) || (a++, true) || (a++, true) || (a++, true); // no-warning
+ a = ((a++, false) || (a++, false) || (a++, false) || (a++, false)); // no-warning
+ a = ((a++, true) && (a++, true) && (a++, true) && (a++, true)); // no-warning
+ a = ((a++, false) || (a++, false) || (a++, false) || a++); // cxx11-warning {{multiple unsequenced modifications to 'a'}}
+ a = ((a++, true) && (a++, true) && (a++, true) && a++); // cxx11-warning {{multiple unsequenced modifications to 'a'}}
+ a = ((a++, false) || (a++, false) || (a++, false) || (a + a, false)); // no-warning
+ a = ((a++, true) && (a++, true) && (a++, true) && (a + a, true)); // no-warning
+
+ a = (false && a++); // no-warning
+ a = (true && a++); // cxx11-warning {{multiple unsequenced modifications to 'a'}}
+ a = (true && ++a); // no-warning
+ a = (true || a++); // no-warning
+ a = (false || a++); // cxx11-warning {{multiple unsequenced modifications to 'a'}}
+ a = (false || ++a); // no-warning
+
+ (a++) | (a++); // cxx11-warning {{multiple unsequenced modifications to 'a'}}
+ // cxx17-warning at -1 {{multiple unsequenced modifications to 'a'}}
+ (a++) & (a++); // cxx11-warning {{multiple unsequenced modifications to 'a'}}
+ // cxx17-warning at -1 {{multiple unsequenced modifications to 'a'}}
+ (a++) ^ (a++); // cxx11-warning {{multiple unsequenced modifications to 'a'}}
+ // cxx17-warning at -1 {{multiple unsequenced modifications to 'a'}}
+
+ (__builtin_classify_type(++a) ? 1 : 0) + ++a; // ok
+ (__builtin_constant_p(++a) ? 1 : 0) + ++a; // ok
+ (__builtin_object_size(&(++a, a), 0) ? 1 : 0) + ++a; // ok
+ (__builtin_expect(++a, 0) ? 1 : 0) + ++a; // cxx11-warning {{multiple unsequenced modifications to 'a'}}
+ // cxx17-warning at -1 {{multiple unsequenced modifications to 'a'}}
+
+
+ int *p = xs;
+ a = *(a++, p); // no-warning
+ p[(long long unsigned)(p = 0)]; // cxx11-warning {{unsequenced modification and access to 'p'}}
+ (i++, xs)[i++]; // cxx11-warning {{multiple unsequenced modifications to 'i'}}
+ (++i, xs)[++i]; // cxx11-warning {{multiple unsequenced modifications to 'i'}}
+ (i, xs)[++i + ++i]; // cxx11-warning {{multiple unsequenced modifications to 'i'}}
+ // cxx17-warning at -1 {{multiple unsequenced modifications to 'i'}}
+ p++[p == xs]; // cxx11-warning {{unsequenced modification and access to 'p'}}
+ ++p[p++ == xs]; // cxx11-warning {{unsequenced modification and access to 'p'}}
+
+ struct S { int x; } s, *ps = &s;
+ int (S::*PtrMem);
+ (PtrMem = &S::x ,s).*(PtrMem); // cxx11-warning {{unsequenced modification and access to 'PtrMem'}}
+ (PtrMem = &S::x ,s).*(PtrMem = &S::x); // cxx11-warning {{multiple unsequenced modifications to 'PtrMem'}}
+ (PtrMem = &S::x ,ps)->*(PtrMem); // cxx11-warning {{unsequenced modification and access to 'PtrMem'}}
+ (PtrMem = &S::x ,ps)->*(PtrMem = &S::x); // cxx11-warning {{multiple unsequenced modifications to 'PtrMem'}}
+ (PtrMem = nullptr) == (PtrMem = nullptr); // cxx11-warning {{multiple unsequenced modifications to 'PtrMem'}}
+ // cxx17-warning at -1 {{multiple unsequenced modifications to 'PtrMem'}}
+ (PtrMem = nullptr) == PtrMem; // cxx11-warning {{unsequenced modification and access to 'PtrMem'}}
+ // cxx17-warning at -1 {{unsequenced modification and access to 'PtrMem'}}
+
+ i++ << i++; // cxx11-warning {{multiple unsequenced modifications to 'i'}}
+ ++i << ++i; // cxx11-warning {{multiple unsequenced modifications to 'i'}}
+ i++ << i; // cxx11-warning {{unsequenced modification and access to 'i'}}
+ i << i++; // cxx11-warning {{unsequenced modification and access to 'i'}}
+ i++ >> i++; // cxx11-warning {{multiple unsequenced modifications to 'i'}}
+ ++i >> ++i; // cxx11-warning {{multiple unsequenced modifications to 'i'}}
+ i++ >> i; // cxx11-warning {{unsequenced modification and access to 'i'}}
+ i >> i++; // cxx11-warning {{unsequenced modification and access to 'i'}}
+ (i++ << i) + i; // cxx11-warning {{unsequenced modification and access to 'i'}}
+ // cxx17-warning at -1 {{unsequenced modification and access to 'i'}}
+ (i++ << i) << i++; // cxx11-warning {{unsequenced modification and access to 'i'}}
+
+ ++i = i++; // cxx11-warning {{multiple unsequenced modifications to 'i'}}
+ i = i+= 1; // no-warning
+ i = i++ + ++i; // cxx11-warning {{multiple unsequenced modifications to 'i'}}
+ // cxx17-warning at -1 {{multiple unsequenced modifications to 'i'}}
+ ++i += ++i; // cxx11-warning {{multiple unsequenced modifications to 'i'}}
+ ++i += i++; // cxx11-warning {{multiple unsequenced modifications to 'i'}}
+ (i++, i) += ++i; // cxx11-warning {{multiple unsequenced modifications to 'i'}}
+ (i++, i) += i++; // cxx11-warning {{multiple unsequenced modifications to 'i'}}
+ i += i+= 1; // cxx11-warning {{unsequenced modification and access to 'i'}}
+ i += i++; // cxx11-warning {{unsequenced modification and access to 'i'}}
+ i += ++i; // cxx11-warning {{unsequenced modification and access to 'i'}}
+ i -= i++; // cxx11-warning {{unsequenced modification and access to 'i'}}
+ i -= ++i; // cxx11-warning {{unsequenced modification and access to 'i'}}
+ i *= i++; // cxx11-warning {{unsequenced modification and access to 'i'}}
+ i *= ++i; // cxx11-warning {{unsequenced modification and access to 'i'}}
+ i /= i++; // cxx11-warning {{unsequenced modification and access to 'i'}}
+ i /= ++i; // cxx11-warning {{unsequenced modification and access to 'i'}}
+ i %= i++; // cxx11-warning {{unsequenced modification and access to 'i'}}
+ i %= ++i; // cxx11-warning {{unsequenced modification and access to 'i'}}
+ i ^= i++; // cxx11-warning {{unsequenced modification and access to 'i'}}
+ i ^= ++i; // cxx11-warning {{unsequenced modification and access to 'i'}}
+ i |= i++; // cxx11-warning {{unsequenced modification and access to 'i'}}
+ i |= ++i; // cxx11-warning {{unsequenced modification and access to 'i'}}
+ i &= i++; // cxx11-warning {{unsequenced modification and access to 'i'}}
+ i &= ++i; // cxx11-warning {{unsequenced modification and access to 'i'}}
+ i <<= i++; // cxx11-warning {{unsequenced modification and access to 'i'}}
+ i <<= ++i; // cxx11-warning {{unsequenced modification and access to 'i'}}
+ i >>= i++; // cxx11-warning {{unsequenced modification and access to 'i'}}
+ i >>= ++i; // cxx11-warning {{unsequenced modification and access to 'i'}}
+
+ p[i++] = i; // cxx11-warning {{unsequenced modification and access to 'i'}}
+ p[i++] = (i = 42); // cxx11-warning {{multiple unsequenced modifications to 'i'}}
+ p++[i++] = (i = p ? i++ : i++); // cxx11-warning {{unsequenced modification and access to 'p'}}
+ // cxx11-warning at -1 {{multiple unsequenced modifications to 'i'}}
+
+ (i++, f)(i++, 42); // cxx11-warning {{multiple unsequenced modifications to 'i'}}
+ (i++ + i++, f)(42, 42); // cxx11-warning {{multiple unsequenced modifications to 'i'}}
+ // cxx17-warning at -1 {{multiple unsequenced modifications to 'i'}}
+ int (*pf)(int, int);
+ (pf = f)(pf != nullptr, pf != nullptr); // cxx11-warning {{unsequenced modification and access to 'pf'}}
+ pf((pf = f) != nullptr, 42); // cxx11-warning {{unsequenced modification and access to 'pf'}}
+ f((pf = f, 42), (pf = f, 42)); // cxx11-warning {{multiple unsequenced modifications to 'pf'}}
+ // cxx17-warning at -1 {{multiple unsequenced modifications to 'pf'}}
+ pf((pf = f) != nullptr, pf == nullptr); // cxx11-warning {{unsequenced modification and access to 'pf'}}
+ // cxx17-warning at -1 {{unsequenced modification and access to 'pf'}}
+}
+
+namespace PR20819 {
+ struct foo { void bar(int); };
+ foo get_foo(int);
+
+ void g() {
+ int a = 0;
+ get_foo(a).bar(a++); // cxx11-warning {{unsequenced modification and access to 'a'}}
+ }
+}
+
+namespace overloaded_operators {
+ struct E {
+ E &operator=(E &);
+ E operator()(E);
+ E operator()(E, E);
+ E operator[](E);
+ } e;
+ // Binary operators with unsequenced operands.
+ E operator+(E,E);
+ E operator-(E,E);
+ E operator*(E,E);
+ E operator/(E,E);
+ E operator%(E,E);
+ E operator^(E,E);
+ E operator&(E,E);
+ E operator|(E,E);
+
+ E operator<(E,E);
+ E operator>(E,E);
+ E operator==(E,E);
+ E operator!=(E,E);
+ E operator>=(E,E);
+ E operator<=(E,E);
+
+ // Binary operators where the RHS is sequenced before the LHS in C++17.
+ E operator+=(E,E);
+ E operator-=(E,E);
+ E operator*=(E,E);
+ E operator/=(E,E);
+ E operator%=(E,E);
+ E operator^=(E,E);
+ E operator&=(E,E);
+ E operator|=(E,E);
+ E operator<<=(E,E);
+ E operator>>=(E,E);
+
+ // Binary operators where the LHS is sequenced before the RHS in C++17.
+ E operator<<(E,E);
+ E operator>>(E,E);
+ E operator&&(E,E);
+ E operator||(E,E);
+ E operator,(E,E);
+ E operator->*(E,E);
+
+ void test() {
+ int i = 0;
+ // Binary operators with unsequenced operands.
+ ((void)i++,e) + ((void)i++,e);
+ // cxx11-warning at -1 {{multiple unsequenced modifications to 'i'}}
+ // cxx17-warning at -2 {{multiple unsequenced modifications to 'i'}}
+ ((void)i++,e) - ((void)i++,e);
+ // cxx11-warning at -1 {{multiple unsequenced modifications to 'i'}}
+ // cxx17-warning at -2 {{multiple unsequenced modifications to 'i'}}
+ ((void)i++,e) * ((void)i++,e);
+ // cxx11-warning at -1 {{multiple unsequenced modifications to 'i'}}
+ // cxx17-warning at -2 {{multiple unsequenced modifications to 'i'}}
+ ((void)i++,e) / ((void)i++,e);
+ // cxx11-warning at -1 {{multiple unsequenced modifications to 'i'}}
+ // cxx17-warning at -2 {{multiple unsequenced modifications to 'i'}}
+ ((void)i++,e) % ((void)i++,e);
+ // cxx11-warning at -1 {{multiple unsequenced modifications to 'i'}}
+ // cxx17-warning at -2 {{multiple unsequenced modifications to 'i'}}
+ ((void)i++,e) ^ ((void)i++,e);
+ // cxx11-warning at -1 {{multiple unsequenced modifications to 'i'}}
+ // cxx17-warning at -2 {{multiple unsequenced modifications to 'i'}}
+ ((void)i++,e) & ((void)i++,e);
+ // cxx11-warning at -1 {{multiple unsequenced modifications to 'i'}}
+ // cxx17-warning at -2 {{multiple unsequenced modifications to 'i'}}
+ ((void)i++,e) | ((void)i++,e);
+ // cxx11-warning at -1 {{multiple unsequenced modifications to 'i'}}
+ // cxx17-warning at -2 {{multiple unsequenced modifications to 'i'}}
+
+ ((void)i++,e) < ((void)i++,e);
+ // cxx11-warning at -1 {{multiple unsequenced modifications to 'i'}}
+ // cxx17-warning at -2 {{multiple unsequenced modifications to 'i'}}
+ ((void)i++,e) > ((void)i++,e);
+ // cxx11-warning at -1 {{multiple unsequenced modifications to 'i'}}
+ // cxx17-warning at -2 {{multiple unsequenced modifications to 'i'}}
+ ((void)i++,e) == ((void)i++,e);
+ // cxx11-warning at -1 {{multiple unsequenced modifications to 'i'}}
+ // cxx17-warning at -2 {{multiple unsequenced modifications to 'i'}}
+ ((void)i++,e) != ((void)i++,e);
+ // cxx11-warning at -1 {{multiple unsequenced modifications to 'i'}}
+ // cxx17-warning at -2 {{multiple unsequenced modifications to 'i'}}
+ ((void)i++,e) <= ((void)i++,e);
+ // cxx11-warning at -1 {{multiple unsequenced modifications to 'i'}}
+ // cxx17-warning at -2 {{multiple unsequenced modifications to 'i'}}
+ ((void)i++,e) >= ((void)i++,e);
+ // cxx11-warning at -1 {{multiple unsequenced modifications to 'i'}}
+ // cxx17-warning at -2 {{multiple unsequenced modifications to 'i'}}
+
+ // Binary operators where the RHS is sequenced before the LHS in C++17.
+ ((void)i++,e) = ((void)i++,e);
+ // cxx11-warning at -1 {{multiple unsequenced modifications to 'i'}}
+ ((void)i++,e) += ((void)i++,e);
+ // cxx11-warning at -1 {{multiple unsequenced modifications to 'i'}}
+ ((void)i++,e) -= ((void)i++,e);
+ // cxx11-warning at -1 {{multiple unsequenced modifications to 'i'}}
+ ((void)i++,e) *= ((void)i++,e);
+ // cxx11-warning at -1 {{multiple unsequenced modifications to 'i'}}
+ ((void)i++,e) /= ((void)i++,e);
+ // cxx11-warning at -1 {{multiple unsequenced modifications to 'i'}}
+ ((void)i++,e) %= ((void)i++,e);
+ // cxx11-warning at -1 {{multiple unsequenced modifications to 'i'}}
+ ((void)i++,e) ^= ((void)i++,e);
+ // cxx11-warning at -1 {{multiple unsequenced modifications to 'i'}}
+ ((void)i++,e) &= ((void)i++,e);
+ // cxx11-warning at -1 {{multiple unsequenced modifications to 'i'}}
+ ((void)i++,e) |= ((void)i++,e);
+ // cxx11-warning at -1 {{multiple unsequenced modifications to 'i'}}
+ ((void)i++,e) <<= ((void)i++,e);
+ // cxx11-warning at -1 {{multiple unsequenced modifications to 'i'}}
+ ((void)i++,e) >>= ((void)i++,e);
+ // cxx11-warning at -1 {{multiple unsequenced modifications to 'i'}}
+
+ operator+=(((void)i++,e), ((void)i++,e));
+ // cxx11-warning at -1 {{multiple unsequenced modifications to 'i'}}
+ // cxx17-warning at -2 {{multiple unsequenced modifications to 'i'}}
+
+ // Binary operators where the LHS is sequenced before the RHS in C++17.
+ ((void)i++,e) << ((void)i++,e);
+ // cxx11-warning at -1 {{multiple unsequenced modifications to 'i'}}
+ ((void)i++,e) >> ((void)i++,e);
+ // cxx11-warning at -1 {{multiple unsequenced modifications to 'i'}}
+ ((void)i++,e) || ((void)i++,e);
+ // cxx11-warning at -1 {{multiple unsequenced modifications to 'i'}}
+ ((void)i++,e) && ((void)i++,e);
+ // cxx11-warning at -1 {{multiple unsequenced modifications to 'i'}}
+ ((void)i++,e) , ((void)i++,e);
+ // cxx11-warning at -1 {{multiple unsequenced modifications to 'i'}}
+ ((void)i++,e)->*((void)i++,e);
+ // cxx11-warning at -1 {{multiple unsequenced modifications to 'i'}}
+
+ operator<<(((void)i++,e), ((void)i++,e));
+ // cxx11-warning at -1 {{multiple unsequenced modifications to 'i'}}
+ // cxx17-warning at -2 {{multiple unsequenced modifications to 'i'}}
+
+ ((void)i++,e)[((void)i++,e)];
+ // cxx11-warning at -1 {{multiple unsequenced modifications to 'i'}}
+
+ ((void)i++,e)(((void)i++,e));
+ // cxx11-warning at -1 {{multiple unsequenced modifications to 'i'}}
+ e(((void)i++,e), ((void)i++,e));
+ // cxx11-warning at -1 {{multiple unsequenced modifications to 'i'}}
+ // cxx17-warning at -2 {{multiple unsequenced modifications to 'i'}}
+
+ ((void)i++,e).operator()(((void)i++,e));
+ // cxx11-warning at -1 {{multiple unsequenced modifications to 'i'}}
+
+ }
+}
+
+namespace PR35340 {
+ struct S {};
+ S &operator<<(S &, int);
+
+ void test() {
+ S s;
+ int i = 0;
+ s << i++ << i++;
+ // cxx11-warning at -1 {{multiple unsequenced modifications to 'i'}}
+
+ operator<<(operator<<(s, i++), i++);
+ // cxx11-warning at -1 {{multiple unsequenced modifications to 'i'}}
+ // cxx17-warning at -2 {{multiple unsequenced modifications to 'i'}}
+ }
+}
+
+namespace members {
+
+struct S1 {
+ unsigned bf1 : 2;
+ unsigned bf2 : 2;
+ unsigned a;
+ unsigned b;
+ static unsigned x;
+ void member_f(S1 &s);
+};
+
+void S1::member_f(S1 &s) {
+ ++a + ++a; // cxx11-warning {{multiple unsequenced modifications to 'a'}}
+ // cxx17-warning at -1 {{multiple unsequenced modifications to 'a'}}
+ a + ++a; // cxx11-warning {{unsequenced modification and access to 'a'}}
+ // cxx17-warning at -1 {{unsequenced modification and access to 'a'}}
+ ++a + ++b; // no-warning
+ a + ++b; // no-warning
+
+ // TODO: Warn here.
+ ++s.a + ++s.a; // no-warning TODO {{multiple unsequenced modifications to}}
+ s.a + ++s.a; // no-warning TODO {{unsequenced modification and access to}}
+ ++s.a + ++s.b; // no-warning
+ s.a + ++s.b; // no-warning
+
+ ++a + ++s.a; // no-warning
+ a + ++s.a; // no-warning
+ ++a + ++s.b; // no-warning
+ a + ++s.b; // no-warning
+
+ // TODO Warn here for bit-fields in the same memory location.
+ ++bf1 + ++bf1; // cxx11-warning {{multiple unsequenced modifications to 'bf1'}}
+ // cxx17-warning at -1 {{multiple unsequenced modifications to 'bf1'}}
+ bf1 + ++bf1; // cxx11-warning {{unsequenced modification and access to 'bf1'}}
+ // cxx17-warning at -1 {{unsequenced modification and access to 'bf1'}}
+ ++bf1 + ++bf2; // no-warning TODO {{multiple unsequenced modifications to}}
+ bf1 + ++bf2; // no-warning TODO {{unsequenced modification and access to}}
+
+ // TODO Warn here for bit-fields in the same memory location.
+ ++s.bf1 + ++s.bf1; // no-warning TODO {{multiple unsequenced modifications to}}
+ s.bf1 + ++s.bf1; // no-warning TODO {{unsequenced modification and access to}}
+ ++s.bf1 + ++s.bf2; // no-warning TODO {{multiple unsequenced modifications to}}
+ s.bf1 + ++s.bf2; // no-warning TODO {{unsequenced modification and access to}}
+
+ ++bf1 + ++s.bf1; // no-warning
+ bf1 + ++s.bf1; // no-warning
+ ++bf1 + ++s.bf2; // no-warning
+ bf1 + ++s.bf2; // no-warning
+
+ struct Der : S1 {};
+ Der d;
+ Der &d_ref = d;
+ S1 &s1_ref = d_ref;
+
+ ++s1_ref.a + ++d_ref.a; // no-warning TODO {{multiple unsequenced modifications to member 'a' of 'd'}}
+ ++s1_ref.a + d_ref.a; // no-warning TODO {{unsequenced modification and access to member 'a' of 'd'}}
+ ++s1_ref.a + ++d_ref.b; // no-warning
+ ++s1_ref.a + d_ref.b; // no-warning
+
+ ++x + ++x; // cxx11-warning {{multiple unsequenced modifications to 'x'}}
+ // cxx17-warning at -1 {{multiple unsequenced modifications to 'x'}}
+ ++x + x; // cxx11-warning {{unsequenced modification and access to 'x'}}
+ // cxx17-warning at -1 {{unsequenced modification and access to 'x'}}
+ ++s.x + x; // no-warning TODO {{unsequenced modification and access to static member 'x' of 'S1'}}
+ ++this->x + x; // cxx11-warning {{unsequenced modification and access to 'x'}}
+ // cxx17-warning at -1 {{unsequenced modification and access to 'x'}}
+ ++d_ref.x + ++S1::x; // no-warning TODO {{unsequenced modification and access to static member 'x' of 'S1'}}
+}
+
+struct S2 {
+ union { unsigned x, y; };
+ void f2();
+};
+
+void S2::f2() {
+ ++x + ++x; // no-warning TODO {{multiple unsequenced modifications to}}
+ x + ++x; // no-warning TODO {{unsequenced modification and access to}}
+ ++x + ++y; // no-warning
+ x + ++y; // no-warning
+}
+
+void f2(S2 &s) {
+ ++s.x + ++s.x; // no-warning TODO {{multiple unsequenced modifications to}}
+ s.x + ++s.x; // no-warning TODO {{unsequenced modification and access to}}
+ ++s.x + ++s.y; // no-warning
+ s.x + ++s.y; // no-warning
+}
+
+struct S3 {
+ union {
+ union {
+ unsigned x;
+ };
+ };
+ unsigned y;
+ void f3();
+};
+
+void S3::f3() {
+ ++x + ++x; // no-warning TODO {{multiple unsequenced modifications to}}
+ x + ++x; // no-warning TODO {{unsequenced modification and access to}}
+ ++x + ++y; // no-warning
+ x + ++y; // no-warning
+}
+
+void f3(S3 &s) {
+ ++s.x + ++s.x; // no-warning TODO {{multiple unsequenced modifications to}}
+ s.x + ++s.x; // no-warning TODO {{unsequenced modification and access to}}
+ ++s.x + ++s.y; // no-warning
+ s.x + ++s.y; // no-warning
+}
+
+struct S4 : S3 {
+ unsigned y;
+ void f4();
+};
+
+void S4::f4() {
+ ++x + ++x; // no-warning TODO {{multiple unsequenced modifications to}}
+ x + ++x; // no-warning TODO {{unsequenced modification and access to}}
+ ++x + ++y; // no-warning
+ x + ++y; // no-warning
+ ++S3::y + ++y; // no-warning
+ S3::y + ++y; // no-warning
+}
+
+void f4(S4 &s) {
+ ++s.x + ++s.x; // no-warning TODO {{multiple unsequenced modifications to}}
+ s.x + ++s.x; // no-warning TODO {{unsequenced modification and access to}}
+ ++s.x + ++s.y; // no-warning
+ s.x + ++s.y; // no-warning
+ ++s.S3::y + ++s.y; // no-warning
+ s.S3::y + ++s.y; // no-warning
+}
+
+static union {
+ unsigned Ux;
+ unsigned Uy;
+};
+
+void f5() {
+ ++Ux + ++Ux; // no-warning TODO {{multiple unsequenced modifications to}}
+ Ux + ++Ux; // no-warning TODO {{unsequenced modification and access to}}
+ ++Ux + ++Uy; // no-warning
+ Ux + ++Uy; // no-warning
+}
+
+void f6() {
+ struct S { unsigned x, y; } s;
+ ++s.x + ++s.x; // no-warning TODO {{multiple unsequenced modifications to}}
+ s.x + ++s.x; // no-warning TODO {{unsequenced modification and access to}}
+ ++s.x + ++s.y; // no-warning
+ s.x + ++s.y; // no-warning
+
+ struct { unsigned x, y; } t;
+ ++t.x + ++t.x; // no-warning TODO {{multiple unsequenced modifications to}}
+ t.x + ++t.x; // no-warning TODO {{unsequenced modification and access to}}
+ ++t.x + ++t.y; // no-warning
+ t.x + ++t.y; // no-warning
+}
+
+} // namespace members
+
+namespace references {
+void reference_f() {
+ // TODO: Check that we can see through references.
+ // For now this is completely unhandled.
+ int a;
+ int xs[10];
+ int &b = a;
+ int &c = b;
+ int &ra1 = c;
+ int &ra2 = b;
+ int other;
+
+ ++ra1 + ++ra2; // no-warning TODO {{multiple unsequenced modifications to}}
+ ra1 + ++ra2; // no-warning TODO {{unsequenced modification and access to}}
+ ++ra1 + ++other; // no-warning
+ ra1 + ++other; // no-warning
+
+ // Make sure we handle reference cycles.
+ int &ref_cycle = ref_cycle;
+ ++ref_cycle + ++ref_cycle; // cxx11-warning {{multiple unsequenced modifications to 'ref_cycle'}}
+ // cxx17-warning at -1 {{multiple unsequenced modifications to 'ref_cycle'}}
+ ref_cycle + ++ref_cycle; // cxx11-warning {{unsequenced modification and access to 'ref_cycle'}}
+ // cxx17-warning at -1 {{unsequenced modification and access to 'ref_cycle'}}
+}
+} // namespace references
+
+namespace std {
+ using size_t = decltype(sizeof(0));
+ template<typename> struct tuple_size;
+ template<size_t, typename> struct tuple_element { using type = int; };
+}
+namespace bindings {
+
+ struct A { int x, y; };
+ typedef int B[2];
+ struct C { template<int> int get(); };
+ struct D : A {};
+
+} // namespace bindings
+template<> struct std::tuple_size<bindings::C> { enum { value = 2 }; };
+namespace bindings {
+void testa() {
+ A a;
+ {
+ auto [x, y] = a;
+ ++x + ++x; // cxx11-warning {{multiple unsequenced modifications to 'x'}}
+ // cxx17-warning at -1 {{multiple unsequenced modifications to 'x'}}
+ ++x + x; // cxx11-warning {{unsequenced modification and access to 'x'}}
+ // cxx17-warning at -1 {{unsequenced modification and access to 'x'}}
+ ++x + ++y; // no-warning
+ ++x + y; // no-warning
+ ++x + ++a.x; // no-warning
+ ++x + a.x; // no-warning
+ }
+ {
+ auto &[x, y] = a;
+ ++x + ++x; // cxx11-warning {{multiple unsequenced modifications to 'x'}}
+ // cxx17-warning at -1 {{multiple unsequenced modifications to 'x'}}
+ ++x + x; // cxx11-warning {{unsequenced modification and access to 'x'}}
+ // cxx17-warning at -1 {{unsequenced modification and access to 'x'}}
+ ++x + ++y; // no-warning
+ ++x + y; // no-warning
+ ++x + ++a.x; // no-warning TODO
+ ++x + a.x; // no-warning TODO
+ }
+}
+void testb() {
+ B b;
+ {
+ auto [x, y] = b;
+ ++x + ++x; // cxx11-warning {{multiple unsequenced modifications to 'x'}}
+ // cxx17-warning at -1 {{multiple unsequenced modifications to 'x'}}
+ ++x + x; // cxx11-warning {{unsequenced modification and access to 'x'}}
+ // cxx17-warning at -1 {{unsequenced modification and access to 'x'}}
+ ++x + ++y; // no-warning
+ ++x + y; // no-warning
+ ++x + ++b[0]; // no-warning
+ ++x + b[0]; // no-warning
+ }
+ {
+ auto &[x, y] = b;
+ ++x + ++x; // cxx11-warning {{multiple unsequenced modifications to 'x'}}
+ // cxx17-warning at -1 {{multiple unsequenced modifications to 'x'}}
+ ++x + x; // cxx11-warning {{unsequenced modification and access to 'x'}}
+ // cxx17-warning at -1 {{unsequenced modification and access to 'x'}}
+ ++x + ++y; // no-warning
+ ++x + y; // no-warning
+ ++x + ++b[0]; // no-warning TODO
+ ++x + b[0]; // no-warning TODO
+ }
+}
+void testc() {
+ C c;
+ {
+ auto [x, y] = c;
+ ++x + ++x; // cxx11-warning {{multiple unsequenced modifications to 'x'}}
+ // cxx17-warning at -1 {{multiple unsequenced modifications to 'x'}}
+ ++x + x; // cxx11-warning {{unsequenced modification and access to 'x'}}
+ // cxx17-warning at -1 {{unsequenced modification and access to 'x'}}
+ ++x + ++y; // no-warning
+ ++x + y; // no-warning
+ }
+ {
+ auto &[x, y] = c;
+ ++x + ++x; // cxx11-warning {{multiple unsequenced modifications to 'x'}}
+ // cxx17-warning at -1 {{multiple unsequenced modifications to 'x'}}
+ ++x + x; // cxx11-warning {{unsequenced modification and access to 'x'}}
+ // cxx17-warning at -1 {{unsequenced modification and access to 'x'}}
+ ++x + ++y; // no-warning
+ ++x + y; // no-warning
+ }
+}
+void testd() {
+ D d;
+ {
+ auto [x, y] = d;
+ ++x + ++x; // cxx11-warning {{multiple unsequenced modifications to 'x'}}
+ // cxx17-warning at -1 {{multiple unsequenced modifications to 'x'}}
+ ++x + x; // cxx11-warning {{unsequenced modification and access to 'x'}}
+ // cxx17-warning at -1 {{unsequenced modification and access to 'x'}}
+ ++x + ++y; // no-warning
+ ++x + y; // no-warning
+ ++x + ++d.x; // no-warning
+ ++x + d.x; // no-warning
+ }
+ {
+ auto &[x, y] = d;
+ ++x + ++x; // cxx11-warning {{multiple unsequenced modifications to 'x'}}
+ // cxx17-warning at -1 {{multiple unsequenced modifications to 'x'}}
+ ++x + x; // cxx11-warning {{unsequenced modification and access to 'x'}}
+ // cxx17-warning at -1 {{unsequenced modification and access to 'x'}}
+ ++x + ++y; // no-warning
+ ++x + y; // no-warning
+ ++x + ++d.x; // no-warning TODO
+ ++x + d.x; // no-warning TODO
+ }
+}
+} // namespace bindings
+
+namespace templates {
+
+template <typename T>
+struct Bar {
+ T get() { return 0; }
+};
+
+template <typename X>
+struct Foo {
+ int Run();
+ Bar<int> bar;
+};
+
+enum E {e1, e2};
+bool operator&&(E, E);
+
+void foo(int, int);
+
+template <typename X>
+int Foo<X>::Run() {
+ char num = 0;
+
+ // Before instantiation, Clang may consider the builtin operator here as
+ // unresolved function calls, and treat the arguments as unordered when
+ // the builtin operator evaluatation is well-ordered. Waiting until
+ // instantiation to check these expressions will prevent false positives.
+ if ((num = bar.get()) < 5 && num < 10) { }
+ if ((num = bar.get()) < 5 || num < 10) { }
+ if (static_cast<E>((num = bar.get()) < 5) || static_cast<E>(num < 10)) { }
+
+ if (static_cast<E>((num = bar.get()) < 5) && static_cast<E>(num < 10)) { }
+ // cxx11-warning at -1 {{unsequenced modification and access to 'num'}}
+
+ foo(num++, num++);
+ // cxx11-warning at -1 {{multiple unsequenced modifications to 'num'}}
+ // cxx17-warning at -2 {{multiple unsequenced modifications to 'num'}}
+ return 1;
+}
+
+int x = Foo<int>().Run();
+// cxx11-note at -1 {{in instantiation of member function 'templates::Foo<int>::Run'}}
+// cxx17-note at -2 {{in instantiation of member function 'templates::Foo<int>::Run'}}
+
+
+template <typename T>
+int Run2() {
+ T t = static_cast<T>(0);
+ return (t = static_cast<T>(1)) && t;
+ // cxx11-warning at -1 {{unsequenced modification and access to 't'}}
+}
+
+int y = Run2<bool>();
+int z = Run2<E>();
+// cxx11-note at -1{{in instantiation of function template specialization 'templates::Run2<templates::E>' requested here}}
+
+template <typename T> int var = sizeof(T);
+void test_var() {
+ var<int>++ + var<int>++; // cxx11-warning {{multiple unsequenced modifications to 'var<int>'}}
+ // cxx17-warning at -1 {{multiple unsequenced modifications to 'var<int>'}}
+ var<int>++ + var<int>; // cxx11-warning {{unsequenced modification and access to 'var<int>'}}
+ // cxx17-warning at -1 {{unsequenced modification and access to 'var<int>'}}
+ int &r = var<int>;
+ r++ + var<int>++; // no-warning TODO {{multiple unsequenced modifications to 'var<int>'}}
+ r++ + var<long>++; // no-warning
+}
+
+} // namespace templates
+
+namespace muliple_read_volatile {
+ volatile int v1, v2;
+
+
+ void PositiveTest(){
+ int x = 0;
+ int y = 0;
+ x = v1 + v1; // cxx11-warning {{unsequenced accesses to volatile qualified 'v1'}}
+ // cxx17-warning at -1 {{unsequenced accesses to volatile qualified 'v1'}}
+ v1 = v1 * v1; // cxx11-warning {{unsequenced accesses to volatile qualified 'v1'}}
+ // cxx17-warning at -1 {{unsequenced accesses to volatile qualified 'v1'}}
+ x = v1 + (y++, v1); // cxx11-warning {{unsequenced accesses to volatile qualified 'v1'}}
+ // cxx17-warning at -1 {{unsequenced accesses to volatile qualified 'v1'}}
+ x = v1 + v1 || y; // cxx11-warning {{unsequenced accesses to volatile qualified 'v1'}}
+ // cxx17-warning at -1 {{unsequenced accesses to volatile qualified 'v1'}}
+ }
+
+ void NegativeTest(){
+ int x = 0;
+ int y = 0;
+ x = v1 + y; // no-warning
+ x = v1 + v2; // no-warning
+ v1 = v1 * y; // no-warning
+ x = (v1, v1); // no-warning
+ x = v1 || v1; // no-warning
+ x = v1 && v1; // no-warning
+ }
+} // namespace muliple_read_volatile
>From 050b6254c7b89bf805a485634e880b31444ec396 Mon Sep 17 00:00:00 2001
From: Seraphimt <svet58585 at mail.ru>
Date: Thu, 20 Aug 2026 19:38:28 +0300
Subject: [PATCH 09/12] Revert "Add Wno-sequence-point in tests x2."
This reverts commit da20fc4992ed4912c071d76041dc7ee7a612d0f2.
---
clang/test/CodeGen/PowerPC/builtins-ppc-fma.c | 2 +-
.../CodeGen/SystemZ/builtins-systemz-vector-constrained.c | 2 +-
clang/test/CodeGen/SystemZ/builtins-systemz-zvector5.c | 4 ++--
3 files changed, 4 insertions(+), 4 deletions(-)
diff --git a/clang/test/CodeGen/PowerPC/builtins-ppc-fma.c b/clang/test/CodeGen/PowerPC/builtins-ppc-fma.c
index e26d7793ffb2b..4dd7295d8efe1 100644
--- a/clang/test/CodeGen/PowerPC/builtins-ppc-fma.c
+++ b/clang/test/CodeGen/PowerPC/builtins-ppc-fma.c
@@ -1,5 +1,5 @@
// RUN: %clang_cc1 -triple powerpc64le-gnu-linux \
-// RUN: -target-feature +vsx -Wall -Wno-sequence-point -Wno-unused -Werror -emit-llvm %s -o - | FileCheck \
+// RUN: -target-feature +vsx -Wall -Wno-sequence-point -Werror -emit-llvm %s -o - | FileCheck \
// RUN: %s
typedef __attribute__((vector_size(4 * sizeof(float)))) float vec_float;
diff --git a/clang/test/CodeGen/SystemZ/builtins-systemz-vector-constrained.c b/clang/test/CodeGen/SystemZ/builtins-systemz-vector-constrained.c
index 0877067a1fdcd..ff24ef9a091b7 100644
--- a/clang/test/CodeGen/SystemZ/builtins-systemz-vector-constrained.c
+++ b/clang/test/CodeGen/SystemZ/builtins-systemz-vector-constrained.c
@@ -1,6 +1,6 @@
// REQUIRES: systemz-registered-target
// RUN: %clang_cc1 -target-cpu z13 -triple s390x-ibm-linux -flax-vector-conversions=none \
-// RUN: -ffp-exception-behavior=strict -Wall -Wno-sequence-point -Wno-unused -Werror -emit-llvm %s -o - | FileCheck %s
+// RUN: -ffp-exception-behavior=strict -Wall -Wno-unused -Werror -emit-llvm %s -o - | FileCheck %s
typedef __attribute__((vector_size(16))) signed long long vec_slong;
typedef __attribute__((vector_size(16))) double vec_double;
diff --git a/clang/test/CodeGen/SystemZ/builtins-systemz-zvector5.c b/clang/test/CodeGen/SystemZ/builtins-systemz-zvector5.c
index b354219e817f0..cd0fafdb7435f 100644
--- a/clang/test/CodeGen/SystemZ/builtins-systemz-zvector5.c
+++ b/clang/test/CodeGen/SystemZ/builtins-systemz-zvector5.c
@@ -1,10 +1,10 @@
// REQUIRES: systemz-registered-target
// RUN: %clang_cc1 -target-cpu z17 -triple s390x-linux-gnu \
// RUN: -O2 -fzvector -flax-vector-conversions=none \
-// RUN: -Wall -Wno-sequence-point -Wno-unused -Werror -emit-llvm %s -o - | FileCheck %s
+// RUN: -Wall -Wno-unused -Werror -emit-llvm %s -o - | FileCheck %s
// RUN: %clang_cc1 -target-cpu z17 -triple s390x-linux-gnu \
// RUN: -O2 -fzvector -flax-vector-conversions=none \
-// RUN: -Wall -Wno-sequence-point -Wno-unused -Werror -S %s -o - | FileCheck %s --check-prefix=CHECK-ASM
+// RUN: -Wall -Wno-unused -Werror -S %s -o - | FileCheck %s --check-prefix=CHECK-ASM
#include <vecintrin.h>
>From 54d9b11be85e30ebec799ea4cb631848d1d52ccd Mon Sep 17 00:00:00 2001
From: Seraphimt <svet58585 at mail.ru>
Date: Thu, 20 Aug 2026 19:39:29 +0300
Subject: [PATCH 10/12] Revert "Add Wno-sequence-point in tests."
This reverts commit d8edb3d1f48db3da764da0d46791dd79e110befd.
---
clang/test/CodeGen/PowerPC/builtins-ppc-fma.c | 2 +-
clang/test/CodeGen/SystemZ/builtins-systemz-i128.c | 2 +-
clang/test/CodeGen/SystemZ/builtins-systemz-vector.c | 2 +-
.../CodeGen/SystemZ/builtins-systemz-vector2-constrained.c | 2 +-
clang/test/CodeGen/SystemZ/builtins-systemz-vector2.c | 2 +-
clang/test/CodeGen/SystemZ/builtins-systemz-vector3.c | 2 +-
clang/test/CodeGen/SystemZ/builtins-systemz-vector4.c | 2 +-
clang/test/CodeGen/SystemZ/builtins-systemz-vector5.c | 2 +-
.../CodeGen/SystemZ/builtins-systemz-zvector-constrained.c | 4 ++--
clang/test/CodeGen/SystemZ/builtins-systemz-zvector-error.c | 2 +-
clang/test/CodeGen/SystemZ/builtins-systemz-zvector.c | 4 ++--
.../CodeGen/SystemZ/builtins-systemz-zvector2-constrained.c | 4 ++--
clang/test/CodeGen/SystemZ/builtins-systemz-zvector2.c | 4 ++--
clang/test/CodeGen/SystemZ/builtins-systemz-zvector3.c | 4 ++--
clang/test/CodeGen/SystemZ/builtins-systemz-zvector4.c | 4 ++--
clang/test/CodeGen/SystemZ/builtins-systemz.c | 4 ++--
16 files changed, 23 insertions(+), 23 deletions(-)
diff --git a/clang/test/CodeGen/PowerPC/builtins-ppc-fma.c b/clang/test/CodeGen/PowerPC/builtins-ppc-fma.c
index 4dd7295d8efe1..61421fbf2fecd 100644
--- a/clang/test/CodeGen/PowerPC/builtins-ppc-fma.c
+++ b/clang/test/CodeGen/PowerPC/builtins-ppc-fma.c
@@ -1,5 +1,5 @@
// RUN: %clang_cc1 -triple powerpc64le-gnu-linux \
-// RUN: -target-feature +vsx -Wall -Wno-sequence-point -Werror -emit-llvm %s -o - | FileCheck \
+// RUN: -target-feature +vsx -Wall -Wno-unused -Werror -emit-llvm %s -o - | FileCheck \
// RUN: %s
typedef __attribute__((vector_size(4 * sizeof(float)))) float vec_float;
diff --git a/clang/test/CodeGen/SystemZ/builtins-systemz-i128.c b/clang/test/CodeGen/SystemZ/builtins-systemz-i128.c
index 436d6a0039f9b..2a20607123af9 100644
--- a/clang/test/CodeGen/SystemZ/builtins-systemz-i128.c
+++ b/clang/test/CodeGen/SystemZ/builtins-systemz-i128.c
@@ -2,7 +2,7 @@
// REQUIRES: systemz-registered-target
// RUN: %clang_cc1 -target-cpu z14 -triple s390x-linux-gnu \
// RUN: -O2 -fzvector -flax-vector-conversions=none \
-// RUN: -Wall -Wno-unused -Wno-sequence-point -Werror -emit-llvm %s -o - | FileCheck %s
+// RUN: -Wall -Wno-unused -Werror -emit-llvm %s -o - | FileCheck %s
#include <vecintrin.h>
diff --git a/clang/test/CodeGen/SystemZ/builtins-systemz-vector.c b/clang/test/CodeGen/SystemZ/builtins-systemz-vector.c
index 0cf7e3732b8c4..ae3b08a1b67eb 100644
--- a/clang/test/CodeGen/SystemZ/builtins-systemz-vector.c
+++ b/clang/test/CodeGen/SystemZ/builtins-systemz-vector.c
@@ -1,6 +1,6 @@
// REQUIRES: systemz-registered-target
// RUN: %clang_cc1 -target-cpu z13 -triple s390x-ibm-linux -flax-vector-conversions=none \
-// RUN: -Wall -Wno-sequence-point -Wno-unused -Werror -emit-llvm %s -o - | FileCheck %s
+// RUN: -Wall -Wno-unused -Werror -emit-llvm %s -o - | FileCheck %s
typedef __attribute__((vector_size(16))) signed char vec_schar;
typedef __attribute__((vector_size(16))) signed short vec_sshort;
diff --git a/clang/test/CodeGen/SystemZ/builtins-systemz-vector2-constrained.c b/clang/test/CodeGen/SystemZ/builtins-systemz-vector2-constrained.c
index 682e6f613f44c..12c675041af76 100644
--- a/clang/test/CodeGen/SystemZ/builtins-systemz-vector2-constrained.c
+++ b/clang/test/CodeGen/SystemZ/builtins-systemz-vector2-constrained.c
@@ -1,6 +1,6 @@
// REQUIRES: systemz-registered-target
// RUN: %clang_cc1 -target-cpu z14 -triple s390x-ibm-linux -flax-vector-conversions=none \
-// RUN: -ffp-exception-behavior=strict -Wall -Wno-sequence-point -Wno-unused -Werror -emit-llvm %s -o - | FileCheck %s
+// RUN: -ffp-exception-behavior=strict -Wall -Wno-unused -Werror -emit-llvm %s -o - | FileCheck %s
typedef __attribute__((vector_size(16))) double vec_double;
typedef __attribute__((vector_size(16))) float vec_float;
diff --git a/clang/test/CodeGen/SystemZ/builtins-systemz-vector2.c b/clang/test/CodeGen/SystemZ/builtins-systemz-vector2.c
index 3af63f225afad..8f9e164ebef8c 100644
--- a/clang/test/CodeGen/SystemZ/builtins-systemz-vector2.c
+++ b/clang/test/CodeGen/SystemZ/builtins-systemz-vector2.c
@@ -1,6 +1,6 @@
// REQUIRES: systemz-registered-target
// RUN: %clang_cc1 -target-cpu z14 -triple s390x-ibm-linux -flax-vector-conversions=none \
-// RUN: -Wall -Wno-sequence-point -Wno-unused -Werror -emit-llvm %s -o - | FileCheck %s
+// RUN: -Wall -Wno-unused -Werror -emit-llvm %s -o - | FileCheck %s
typedef __attribute__((vector_size(16))) signed char vec_schar;
typedef __attribute__((vector_size(16))) signed short vec_sshort;
diff --git a/clang/test/CodeGen/SystemZ/builtins-systemz-vector3.c b/clang/test/CodeGen/SystemZ/builtins-systemz-vector3.c
index 0a04affb9c445..e4af1c1e54b86 100644
--- a/clang/test/CodeGen/SystemZ/builtins-systemz-vector3.c
+++ b/clang/test/CodeGen/SystemZ/builtins-systemz-vector3.c
@@ -1,6 +1,6 @@
// REQUIRES: systemz-registered-target
// RUN: %clang_cc1 -target-cpu z15 -triple s390x-ibm-linux -flax-vector-conversions=none \
-// RUN: -Wall -Wno-sequence-point -Wno-unused -Werror -emit-llvm %s -o - | FileCheck %s
+// RUN: -Wall -Wno-unused -Werror -emit-llvm %s -o - | FileCheck %s
typedef __attribute__((vector_size(16))) signed char vec_schar;
typedef __attribute__((vector_size(16))) signed short vec_sshort;
diff --git a/clang/test/CodeGen/SystemZ/builtins-systemz-vector4.c b/clang/test/CodeGen/SystemZ/builtins-systemz-vector4.c
index 09ab10fac5116..c5ce47cea2ea8 100644
--- a/clang/test/CodeGen/SystemZ/builtins-systemz-vector4.c
+++ b/clang/test/CodeGen/SystemZ/builtins-systemz-vector4.c
@@ -1,6 +1,6 @@
// REQUIRES: systemz-registered-target
// RUN: %clang_cc1 -target-cpu z16 -triple s390x-ibm-linux -flax-vector-conversions=none \
-// RUN: -Wall -Wno-sequence-point -Wno-unused -Werror -emit-llvm %s -o - | FileCheck %s
+// RUN: -Wall -Wno-unused -Werror -emit-llvm %s -o - | FileCheck %s
typedef __attribute__((vector_size(16))) signed char vec_schar;
typedef __attribute__((vector_size(16))) signed short vec_sshort;
diff --git a/clang/test/CodeGen/SystemZ/builtins-systemz-vector5.c b/clang/test/CodeGen/SystemZ/builtins-systemz-vector5.c
index 95fe321d606b1..b765fa64b33d4 100644
--- a/clang/test/CodeGen/SystemZ/builtins-systemz-vector5.c
+++ b/clang/test/CodeGen/SystemZ/builtins-systemz-vector5.c
@@ -1,6 +1,6 @@
// REQUIRES: systemz-registered-target
// RUN: %clang_cc1 -target-cpu z17 -triple s390x-ibm-linux -flax-vector-conversions=none \
-// RUN: -Wall -Wno-sequence-point -Wno-unused -Werror -emit-llvm %s -o - | FileCheck %s
+// RUN: -Wall -Wno-unused -Werror -emit-llvm %s -o - | FileCheck %s
typedef __attribute__((vector_size(16))) signed char vec_schar;
typedef __attribute__((vector_size(16))) signed short vec_sshort;
diff --git a/clang/test/CodeGen/SystemZ/builtins-systemz-zvector-constrained.c b/clang/test/CodeGen/SystemZ/builtins-systemz-zvector-constrained.c
index af750a24767c6..4993df20df143 100644
--- a/clang/test/CodeGen/SystemZ/builtins-systemz-zvector-constrained.c
+++ b/clang/test/CodeGen/SystemZ/builtins-systemz-zvector-constrained.c
@@ -2,11 +2,11 @@
// RUN: %clang_cc1 -target-cpu z13 -triple s390x-linux-gnu \
// RUN: -O2 -fzvector -flax-vector-conversions=none \
// RUN: -ffp-exception-behavior=strict \
-// RUN: -Wall -Wno-sequence-point -Wno-unused -Werror -emit-llvm %s -o - | FileCheck %s
+// RUN: -Wall -Wno-unused -Werror -emit-llvm %s -o - | FileCheck %s
// RUN: %clang_cc1 -target-cpu z13 -triple s390x-linux-gnu \
// RUN: -O2 -fzvector -flax-vector-conversions=none \
// RUN: -ffp-exception-behavior=strict \
-// RUN: -Wall -Wno-sequence-point -Wno-unused -Werror -S %s -o - | FileCheck %s --check-prefix=CHECK-ASM
+// RUN: -Wall -Wno-unused -Werror -S %s -o - | FileCheck %s --check-prefix=CHECK-ASM
#include <vecintrin.h>
diff --git a/clang/test/CodeGen/SystemZ/builtins-systemz-zvector-error.c b/clang/test/CodeGen/SystemZ/builtins-systemz-zvector-error.c
index d82cbf0c51b2b..2ec1d960aa5bb 100644
--- a/clang/test/CodeGen/SystemZ/builtins-systemz-zvector-error.c
+++ b/clang/test/CodeGen/SystemZ/builtins-systemz-zvector-error.c
@@ -1,7 +1,7 @@
// REQUIRES: systemz-registered-target
// RUN: %clang_cc1 -target-cpu z13 -triple s390x-linux-gnu \
// RUN: -fzvector -flax-vector-conversions=none \
-// RUN: -Wall -Wno-sequence-point -Wno-unused -Werror -fsyntax-only -verify %s
+// RUN: -Wall -Wno-unused -Werror -fsyntax-only -verify %s
#include <vecintrin.h>
diff --git a/clang/test/CodeGen/SystemZ/builtins-systemz-zvector.c b/clang/test/CodeGen/SystemZ/builtins-systemz-zvector.c
index 9f1283724201c..42298339733d4 100644
--- a/clang/test/CodeGen/SystemZ/builtins-systemz-zvector.c
+++ b/clang/test/CodeGen/SystemZ/builtins-systemz-zvector.c
@@ -1,10 +1,10 @@
// REQUIRES: systemz-registered-target
// RUN: %clang_cc1 -target-cpu z13 -triple s390x-linux-gnu \
// RUN: -O2 -fzvector -flax-vector-conversions=none \
-// RUN: -Wall -Wno-sequence-point -Wno-unused -Werror -emit-llvm %s -o - | FileCheck %s
+// RUN: -Wall -Wno-unused -Werror -emit-llvm %s -o - | FileCheck %s
// RUN: %clang_cc1 -target-cpu z13 -triple s390x-linux-gnu \
// RUN: -O2 -fzvector -flax-vector-conversions=none \
-// RUN: -Wall -Wno-sequence-point -Wno-unused -Werror -S %s -o - | FileCheck %s --check-prefix=CHECK-ASM
+// RUN: -Wall -Wno-unused -Werror -S %s -o - | FileCheck %s --check-prefix=CHECK-ASM
#include <vecintrin.h>
diff --git a/clang/test/CodeGen/SystemZ/builtins-systemz-zvector2-constrained.c b/clang/test/CodeGen/SystemZ/builtins-systemz-zvector2-constrained.c
index 6dea4f6bf6600..25b3e0b68cd02 100644
--- a/clang/test/CodeGen/SystemZ/builtins-systemz-zvector2-constrained.c
+++ b/clang/test/CodeGen/SystemZ/builtins-systemz-zvector2-constrained.c
@@ -2,11 +2,11 @@
// RUN: %clang_cc1 -target-cpu z14 -triple s390x-linux-gnu \
// RUN: -O2 -fzvector -flax-vector-conversions=none \
// RUN: -ffp-exception-behavior=strict \
-// RUN: -Wall -Wno-sequence-point -Wno-unused -Werror -emit-llvm %s -o - | FileCheck %s
+// RUN: -Wall -Wno-unused -Werror -emit-llvm %s -o - | FileCheck %s
// RUN: %clang_cc1 -target-cpu z14 -triple s390x-linux-gnu \
// RUN: -O2 -fzvector -flax-vector-conversions=none \
// RUN: -ffp-exception-behavior=strict \
-// RUN: -Wall -Wno-sequence-point -Wno-unused -Werror -S %s -o - | FileCheck %s --check-prefix=CHECK-ASM
+// RUN: -Wall -Wno-unused -Werror -S %s -o - | FileCheck %s --check-prefix=CHECK-ASM
#include <vecintrin.h>
diff --git a/clang/test/CodeGen/SystemZ/builtins-systemz-zvector2.c b/clang/test/CodeGen/SystemZ/builtins-systemz-zvector2.c
index aedd7f734edb8..c1ef178fcfaa9 100644
--- a/clang/test/CodeGen/SystemZ/builtins-systemz-zvector2.c
+++ b/clang/test/CodeGen/SystemZ/builtins-systemz-zvector2.c
@@ -1,10 +1,10 @@
// REQUIRES: systemz-registered-target
// RUN: %clang_cc1 -target-cpu z14 -triple s390x-linux-gnu \
// RUN: -O2 -fzvector -flax-vector-conversions=none \
-// RUN: -Wall -Wno-sequence-point -Wno-unused -Werror -emit-llvm %s -o - | FileCheck %s
+// RUN: -Wall -Wno-unused -Werror -emit-llvm %s -o - | FileCheck %s
// RUN: %clang_cc1 -target-cpu z14 -triple s390x-linux-gnu \
// RUN: -O2 -fzvector -flax-vector-conversions=none \
-// RUN: -Wall -Wno-sequence-point -Wno-unused -Werror -S %s -o - | FileCheck %s --check-prefix=CHECK-ASM
+// RUN: -Wall -Wno-unused -Werror -S %s -o - | FileCheck %s --check-prefix=CHECK-ASM
#include <vecintrin.h>
diff --git a/clang/test/CodeGen/SystemZ/builtins-systemz-zvector3.c b/clang/test/CodeGen/SystemZ/builtins-systemz-zvector3.c
index dd7ec19b2412f..ce8b315127237 100644
--- a/clang/test/CodeGen/SystemZ/builtins-systemz-zvector3.c
+++ b/clang/test/CodeGen/SystemZ/builtins-systemz-zvector3.c
@@ -1,10 +1,10 @@
// REQUIRES: systemz-registered-target
// RUN: %clang_cc1 -target-cpu z15 -triple s390x-linux-gnu \
// RUN: -O2 -fzvector -flax-vector-conversions=none \
-// RUN: -Wall -Wno-sequence-point -Wno-unused -Werror -emit-llvm %s -o - | FileCheck %s
+// RUN: -Wall -Wno-unused -Werror -emit-llvm %s -o - | FileCheck %s
// RUN: %clang_cc1 -target-cpu z15 -triple s390x-linux-gnu \
// RUN: -O2 -fzvector -flax-vector-conversions=none \
-// RUN: -Wall -Wno-sequence-point -Wno-unused -Werror -S %s -o - | FileCheck %s --check-prefix=CHECK-ASM
+// RUN: -Wall -Wno-unused -Werror -S %s -o - | FileCheck %s --check-prefix=CHECK-ASM
#include <vecintrin.h>
diff --git a/clang/test/CodeGen/SystemZ/builtins-systemz-zvector4.c b/clang/test/CodeGen/SystemZ/builtins-systemz-zvector4.c
index 23685cdff8994..adc55927ce366 100644
--- a/clang/test/CodeGen/SystemZ/builtins-systemz-zvector4.c
+++ b/clang/test/CodeGen/SystemZ/builtins-systemz-zvector4.c
@@ -1,10 +1,10 @@
// REQUIRES: systemz-registered-target
// RUN: %clang_cc1 -target-cpu z16 -triple s390x-linux-gnu \
// RUN: -O2 -fzvector -flax-vector-conversions=none \
-// RUN: -Wall -Wno-sequence-point -Wno-unused -Werror -emit-llvm %s -o - | FileCheck %s
+// RUN: -Wall -Wno-unused -Werror -emit-llvm %s -o - | FileCheck %s
// RUN: %clang_cc1 -target-cpu z16 -triple s390x-linux-gnu \
// RUN: -O2 -fzvector -flax-vector-conversions=none \
-// RUN: -Wall -Wno-sequence-point -Wno-unused -Werror -S %s -o - | FileCheck %s --check-prefix=CHECK-ASM
+// RUN: -Wall -Wno-unused -Werror -S %s -o - | FileCheck %s --check-prefix=CHECK-ASM
#include <vecintrin.h>
diff --git a/clang/test/CodeGen/SystemZ/builtins-systemz.c b/clang/test/CodeGen/SystemZ/builtins-systemz.c
index 61c7cd934f7c4..4848bef4b4aad 100644
--- a/clang/test/CodeGen/SystemZ/builtins-systemz.c
+++ b/clang/test/CodeGen/SystemZ/builtins-systemz.c
@@ -1,6 +1,6 @@
// REQUIRES: systemz-registered-target
-// RUN: %clang_cc1 -target-cpu zEC12 -triple s390x-ibm-linux -Wall -Wno-unused -Wno-sequence-point -Werror -emit-llvm %s -o - | FileCheck %s
-// RUN: %clang_cc1 -target-cpu zEC12 -triple s390x-ibm-linux -Wall -Wno-unused -Wno-sequence-point -Werror -emit-llvm -x c++ %s -o - | FileCheck %s
+// RUN: %clang_cc1 -target-cpu zEC12 -triple s390x-ibm-linux -Wall -Wno-unused -Werror -emit-llvm %s -o - | FileCheck %s
+// RUN: %clang_cc1 -target-cpu zEC12 -triple s390x-ibm-linux -Wall -Wno-unused -Werror -emit-llvm -x c++ %s -o - | FileCheck %s
#include <stdint.h>
#include <htmintrin.h>
>From b7a6f50048e53870b08f3b8dc5f4224125ded070 Mon Sep 17 00:00:00 2001
From: Seraphimt <svet58585 at mail.ru>
Date: Fri, 21 Aug 2026 09:53:35 +0300
Subject: [PATCH 11/12] Fix missed cases.
---
clang/test/CodeGen/SystemZ/builtins-systemz-zvector.c | 10 ++++++----
1 file changed, 6 insertions(+), 4 deletions(-)
diff --git a/clang/test/CodeGen/SystemZ/builtins-systemz-zvector.c b/clang/test/CodeGen/SystemZ/builtins-systemz-zvector.c
index a78eaf3606a53..b3c232474709c 100644
--- a/clang/test/CodeGen/SystemZ/builtins-systemz-zvector.c
+++ b/clang/test/CodeGen/SystemZ/builtins-systemz-zvector.c
@@ -58,12 +58,14 @@ volatile vector double vd2;
volatile signed char sc;
volatile signed short ss;
volatile signed int si;
-volatile signed long long sl;
+volatile signed long long sl1;
+volatile signed long long sl2;
volatile signed __int128 slll;
volatile unsigned char uc;
volatile unsigned short us;
volatile unsigned int ui;
-volatile unsigned long long ul;
+volatile unsigned long long ul1;
+volatile unsigned long long ul2;
volatile unsigned __int128 ulll;
volatile double d;
@@ -769,9 +771,9 @@ void test_core(void) {
// CHECK: call void @llvm.s390.vstl(<16 x i8> %{{.*}}, i32 %{{.*}}, ptr %{{.*}})
// CHECK-ASM: vstl
- vsl = vec_load_pair(sl + 1, sl - 1);
+ vsl = vec_load_pair(sl1 + 1, sl2 - 1);
// CHECK-ASM: vlvgp
- vul = vec_load_pair(ul + 1, ul - 1);
+ vul = vec_load_pair(ul1 + 1, ul1 - 1);
// CHECK-ASM: vlvgp
vuc = vec_genmask(0);
>From e187ee373f5756209112940c583725f3f9b79f8f Mon Sep 17 00:00:00 2001
From: Seraphimt <svet58585 at mail.ru>
Date: Fri, 21 Aug 2026 14:34:47 +0300
Subject: [PATCH 12/12] Missing part of the commit with the
builtins-systemz-zvector.c fix
---
clang/test/CodeGen/SystemZ/builtins-systemz-zvector.c | 2 ++
1 file changed, 2 insertions(+)
diff --git a/clang/test/CodeGen/SystemZ/builtins-systemz-zvector.c b/clang/test/CodeGen/SystemZ/builtins-systemz-zvector.c
index b3c232474709c..49e3e4cf9956c 100644
--- a/clang/test/CodeGen/SystemZ/builtins-systemz-zvector.c
+++ b/clang/test/CodeGen/SystemZ/builtins-systemz-zvector.c
@@ -58,12 +58,14 @@ volatile vector double vd2;
volatile signed char sc;
volatile signed short ss;
volatile signed int si;
+volatile signed long long sl;
volatile signed long long sl1;
volatile signed long long sl2;
volatile signed __int128 slll;
volatile unsigned char uc;
volatile unsigned short us;
volatile unsigned int ui;
+volatile unsigned long long ul;
volatile unsigned long long ul1;
volatile unsigned long long ul2;
volatile unsigned __int128 ulll;
More information about the cfe-commits
mailing list