Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Remove warnings from -Wchar-subscripts for known positive constants #69061

Merged
merged 1 commit into from
Dec 4, 2023

Conversation

wheatman
Copy link
Contributor

@wheatman wheatman commented Oct 14, 2023

This is to address #18763

it removes warnings from using a signed char as an array bound if the char is a known positives constant.

This goes one step farther than gcc does.

For example given the following code

char upper[300];

int main() {
  upper['a'] = 'A';
  char b = 'a';
  upper[b] = 'A';
  const char c = 'a';
  upper[c] = 'A';
  constexpr char d = 'a';
  upper[d] = 'A';
  char e = -1;
  upper[e] = 'A';
  const char f = -1;
  upper[f] = 'A';
  constexpr char g = -1;
  upper[g] = 'A';
  return 1;
}

clang currently gives warnings for all cases, while gcc gives warnings for all cases except for 'a' (https://godbolt.org/z/5ahjETTv3)

with the change there is no longer any warning for 'a', 'c', or 'd'.

../test.cpp:7:8: warning: array subscript is of type 'char' [-Wchar-subscripts]
    7 |   upper[b] = 'A';
      |        ^~
../test.cpp:13:8: warning: array subscript is of type 'char' [-Wchar-subscripts]
   13 |   upper[e] = 'A';
      |        ^~
../test.cpp:15:8: warning: array subscript is of type 'char' [-Wchar-subscripts]
   15 |   upper[f] = 'A';
      |        ^~
../test.cpp:17:8: warning: array subscript is of type 'char' [-Wchar-subscripts]
   17 |   upper[g] = 'A';
      |        ^~
../test.cpp:15:3: warning: array index -1 is before the beginning of the array [-Warray-bounds]
   15 |   upper[f] = 'A';
      |   ^     ~
../test.cpp:1:1: note: array 'upper' declared here
    1 | char upper[300];
      | ^
../test.cpp:17:3: warning: array index -1 is before the beginning of the array [-Warray-bounds]
   17 |   upper[g] = 'A';
      |   ^     ~
../test.cpp:1:1: note: array 'upper' declared here
    1 | char upper[300];
      | ^
6 warnings generated.

This pull request is my first change and I would appreciate any comments or suggestions.

@llvmbot llvmbot added clang Clang issues not falling into any other category clang:frontend Language frontend issues, e.g. anything involving "Sema" labels Oct 14, 2023
@llvmbot
Copy link
Collaborator

llvmbot commented Oct 14, 2023

@llvm/pr-subscribers-clang

Author: None (wheatman)

Changes

This is to address #18763

it removes warnings from using a signed char as an array bound if the char is a known positives constant.

This goes one step farther than gcc does.

For example given the following code

char upper[300];

int main() {
  upper['a'] = 'A';
  char b = 'a';
  upper[b] = 'A';
  const char c = 'a';
  upper[c] = 'A';
  constexpr char d = 'a';
  upper[d] = 'A';
  char e = -1;
  upper[e] = 'A';
  const char f = -1;
  upper[f] = 'A';
  constexpr char g = -1;
  upper[g] = 'A';
  return 1;
}

clang currently gives warnings for all cases, while gcc gives warnings for all cases except for 'a' (https://godbolt.org/z/5ahjETTv3)

with the change there is no longer any warning for 'a', 'c', or 'd'.

../test.cpp:7:8: warning: array subscript is of type 'char' [-Wchar-subscripts]
    7 |   upper[b] = 'A';
      |        ^~
../test.cpp:13:8: warning: array subscript is of type 'char' [-Wchar-subscripts]
   13 |   upper[e] = 'A';
      |        ^~
../test.cpp:15:8: warning: array subscript is of type 'char' [-Wchar-subscripts]
   15 |   upper[f] = 'A';
      |        ^~
../test.cpp:17:8: warning: array subscript is of type 'char' [-Wchar-subscripts]
   17 |   upper[g] = 'A';
      |        ^~
../test.cpp:15:3: warning: array index -1 is before the beginning of the array [-Warray-bounds]
   15 |   upper[f] = 'A';
      |   ^     ~
../test.cpp:1:1: note: array 'upper' declared here
    1 | char upper[300];
      | ^
../test.cpp:17:3: warning: array index -1 is before the beginning of the array [-Warray-bounds]
   17 |   upper[g] = 'A';
      |   ^     ~
../test.cpp:1:1: note: array 'upper' declared here
    1 | char upper[300];
      | ^
6 warnings generated.

This pull request in incomplete in that this is my first change submitted and I don't know how to add tests, any guidance on what sort of tests to add, and where to find documentation on the testing infrastructure


Full diff: https://github.com/llvm/llvm-project/pull/69061.diff

1 Files Affected:

  • (modified) clang/lib/Sema/SemaExpr.cpp (+9-3)
diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp
index aa30a3a03887558..dd9ba5cecaf2404 100644
--- a/clang/lib/Sema/SemaExpr.cpp
+++ b/clang/lib/Sema/SemaExpr.cpp
@@ -6018,9 +6018,15 @@ Sema::CreateBuiltinArraySubscriptExpr(Expr *Base, SourceLocation LLoc,
                      << IndexExpr->getSourceRange());
 
   if ((IndexExpr->getType()->isSpecificBuiltinType(BuiltinType::Char_S) ||
-       IndexExpr->getType()->isSpecificBuiltinType(BuiltinType::Char_U))
-         && !IndexExpr->isTypeDependent())
-    Diag(LLoc, diag::warn_subscript_is_char) << IndexExpr->getSourceRange();
+       IndexExpr->getType()->isSpecificBuiltinType(BuiltinType::Char_U)) &&
+      !IndexExpr->isTypeDependent()) {
+    std::optional<llvm::APSInt> IntegerContantExpr =
+        IndexExpr->getIntegerConstantExpr(getASTContext());
+    if (!(IntegerContantExpr.has_value() &&
+          IntegerContantExpr.value().isNonNegative())) {
+      Diag(LLoc, diag::warn_subscript_is_char) << IndexExpr->getSourceRange();
+    }
+  }
 
   // C99 6.5.2.1p1: "shall have type "pointer to *object* type". Similarly,
   // C++ [expr.sub]p1: The type "T" shall be a completely-defined object

@wheatman wheatman added the clang:diagnostics New/improved warning or error message in Clang, but not in clang-tidy or static analyzer label Oct 14, 2023
Copy link
Collaborator

@shafik shafik left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You need to make sure you tests have newlines at the end

void t6(void) {
int array[1] = { 0 };
signed char subscript = 0;
int val = array[subscript]; // no warning for explicit signed char
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we exclude the signed char case?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That is an interesting questions, the behavior for that was not changed.
The current behavior for explicitly signed or unsigned chars is to have no warning, only unspecified chars have warnings.

https://godbolt.org/z/7oc3ET4h4

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We rule out explicitly signed or explicitly unsigned char type because of int8_t and uint8_t because those are pretty reasonable to use as array indexes.

Copy link
Collaborator

@AaronBallman AaronBallman left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for this! The changes should come with a release note in clang/docs/ReleaseNotes.rst so users know about the change in behavior.

Comment on lines 6023 to 6028
std::optional<llvm::APSInt> IntegerContantExpr =
IndexExpr->getIntegerConstantExpr(getASTContext());
if (!(IntegerContantExpr.has_value() &&
IntegerContantExpr.value().isNonNegative())) {
Diag(LLoc, diag::warn_subscript_is_char) << IndexExpr->getSourceRange();
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
std::optional<llvm::APSInt> IntegerContantExpr =
IndexExpr->getIntegerConstantExpr(getASTContext());
if (!(IntegerContantExpr.has_value() &&
IntegerContantExpr.value().isNonNegative())) {
Diag(LLoc, diag::warn_subscript_is_char) << IndexExpr->getSourceRange();
}
std::optional<llvm::APSInt> IntegerContantExpr =
IndexExpr->getIntegerConstantExpr(getASTContext());
if (!IntegerContantExpr.has_value() ||
IntegerContantExpr.value().isNegative())
Diag(LLoc, diag::warn_subscript_is_char) << IndexExpr->getSourceRange();

This performs extra work for each array subscript in the TU, but because it's limited to only array subscripts with a character type, I don't think the compile-time performance hit should be too bad, so this seems reasonable to me.

clang/test/Sema/warn-char-subscripts.c Show resolved Hide resolved
void t6(void) {
int array[1] = { 0 };
signed char subscript = 0;
int val = array[subscript]; // no warning for explicit signed char
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We rule out explicitly signed or explicitly unsigned char type because of int8_t and uint8_t because those are pretty reasonable to use as array indexes.

@wheatman
Copy link
Contributor Author

wheatman commented Nov 2, 2023

Thanks you for the comments. I made the updates and added the note in clang/docs/ReleaseNotes.rst

Copy link

github-actions bot commented Nov 2, 2023

✅ With the latest revision this PR passed the C/C++ code formatter.

Copy link
Collaborator

@AaronBallman AaronBallman left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, thank you for the fix!

@wheatman
Copy link
Contributor Author

wheatman commented Nov 3, 2023

Thank you for the review.
I don't have write access, so unless @shafik has more comments, could one of you merge it in

@wheatman
Copy link
Contributor Author

@AaronBallman if there are no more comments, could you please merge it in

@cor3ntin
Copy link
Contributor

cor3ntin commented Dec 3, 2023

@wheatman can you rebase again? I want to make sure CI passes before merging (and the bots were broken earlier today). Thanks.
LGTM otherwise

@wheatman
Copy link
Contributor Author

wheatman commented Dec 3, 2023

@cor3ntin done, thanks for taking a look

@cor3ntin cor3ntin merged commit 0031efe into llvm:main Dec 4, 2023
4 checks passed
@cor3ntin
Copy link
Contributor

cor3ntin commented Dec 4, 2023

Thanks for your first contribution. Maybe the first of many :D

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
clang:diagnostics New/improved warning or error message in Clang, but not in clang-tidy or static analyzer clang:frontend Language frontend issues, e.g. anything involving "Sema" clang Clang issues not falling into any other category
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

5 participants