Skip to content

Commit ac90a85

Browse files
committed
Preserve narrowing for const-qualified pointer types
1 parent 674e9e6 commit ac90a85

File tree

4 files changed

+82
-25
lines changed

4 files changed

+82
-25
lines changed

CONTRIBUTING.md

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,18 @@ Development branch: `null-safe-c-dev`
55
## Building
66

77
### Standard (x86)
8+
9+
**Clang only:**
810
```bash
911
mkdir build && cd build
1012
cmake -G Ninja -DCMAKE_BUILD_TYPE=Release -DLLVM_ENABLE_PROJECTS="clang" ../llvm
13+
ninja clang
14+
```
15+
16+
**Clang + clangd (language server):**
17+
```bash
18+
mkdir build && cd build
19+
cmake -G Ninja -DCMAKE_BUILD_TYPE=Release -DLLVM_ENABLE_PROJECTS="clang;clang-tools-extra" ../llvm
1120
ninja clang clangd
1221
```
1322

@@ -36,7 +45,9 @@ ninja clang
3645

3746
## Testing
3847
```bash
39-
build/bin/llvm-lit -v clang/test/Sema/strict-nullability.c
48+
cd build
49+
ninja FileCheck count not split-file llvm-config
50+
./bin/llvm-lit -v ../clang/test/Sema/strict-nullability.c
4051
```
4152

4253
## Releases

README.md

Lines changed: 33 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -2,45 +2,53 @@
22

33
[![Test Null-Safety](https://github.com/cs01/llvm-project/actions/workflows/test-null-safety.yml/badge.svg)](https://github.com/cs01/llvm-project/actions/workflows/test-null-safety.yml)
44

5-
Nullsafe C adds NULL checks to catch errors at compile-time, not runtime. It is 100% compatible with existing C codebases and can be used incrementally to identify safety issues at compile-time.
5+
Nullsafe C adds NULL checks to catch errors at compile-time. It is 100% compatible with existing C codebases and can be used incrementally to identify safety issues at compile-time.
66

7-
You can annotate your code with `_Nonnull` to presere narrowing.
7+
This provides the following benefits:
8+
* This makes the code safer by reducing the number of potential runtime null dereferences
9+
* Improves developer experience by shifting errors left
10+
* Makes the code more readable
11+
* Adds type errors that other more modern languages have (Rust, TypeScript, Kotlin)
812

913
**Try it online:** [Interactive Playground](https://cs01.github.io/llvm-project/) - See null-safety warnings in real-time in your browser!
1014

11-
It does this by making two changes:
12-
1. All pointers are nullable by default, unless explicitly marked `_Nonnull`. Clang already allows the code to be annotated with `_Nullable` and `_Nonnull`, but this compiler treats all unmarked pointers as nullable by default.
13-
2. The compiler tracks when you've null-checked a pointer and knows it's safe to use. When you write `if (p)`, the type system understands `p` is non-null in that branch.
15+
Nullsafe C treats all pointers as potentially null ('nullable') unless it is certain they are not. It does this in two ways.
1416

15-
## Example
17+
The first is by semantic analysis: if you test a pointer with `if(p)`, then it knows that branch contains a non-null pointer.
18+
19+
The second is by using Clang's [`Nullability`](https://clang.llvm.org/docs/AttributeReference.html#nullability-attributes) attributes, in particular `_Nonnull`. If a pointer is marked as `_Nonnull` the compiler will require a pointer it knows it not null is passed to it. This can be done either by passing a `_Nonnull`-annotated pointer, or by doing type narrowing.
20+
21+
If using a compiler other than clang, you can add `#define _Nonnull` as a no-op. You will not get the same compile checks as with Nullsafe C (clang fork), but the compillation will still succeed without error.
22+
23+
## Examples
1624

1725
```c
1826
void unsafe(int *data) {
19-
*data = 42; // warning - data might be null!
27+
*data = 42; // warning: dereferencing nullable pointer of type 'int * _Nullable'
2028
}
29+
```
30+
[Try it in the interactive playground](https://cs01.github.io/llvm-project/?code=dm9pZCB1bnNhZmUoaW50ICpkYXRhKSB7CiAgKmRhdGEgPSA0MjsgLy8gd2FybmluZzogZGVyZWZlcmVuY2luZyBudWxsYWJsZSBwb2ludGVyIG9mIHR5cGUgJ2ludCAqIF9OdWxsYWJsZScKfQ%3D%3D)
2131
32+
Type narrowing:
33+
```c
2234
void safe(int *data) {
2335
if (data) {
2436
*data = 42; // OK - data is non-null here
2537
}
2638
}
39+
```
40+
[Try it in the interactive playground](https://cs01.github.io/llvm-project/?code=dm9pZCBzYWZlKGludCAqZGF0YSkgewogIGlmIChkYXRhKSB7CiAgICAqZGF0YSA9IDQyOyAvLyBPSyAtIGRhdGEgaXMgbm9uLW51bGwgaGVyZQogIH0KfQ%3D%3D)
2741

42+
Anontated with `_Nonnull`:
43+
```c
2844
void safe_typed(int *_Nonnull data) {
29-
*data = 42; // OK - data is known to be non-null by the compiler
45+
*data = 42; // OK - we know data is not null so we can derefernce it
3046
}
31-
3247
```
33-
Try it out in the [Interactive Playground](https://cs01.github.io/llvm-project/).
34-
35-
## Installation
48+
[Try it in the interactive playground](https://cs01.github.io/llvm-project/?code=dm9pZCBzYWZlX3R5cGVkKGludCAqX05vbm51bGwgZGF0YSkgewogICpkYXRhID0gNDI7IC8vIE9LIC0gd2Uga25vdyBkYXRhIGlzIG5vdCBudWxsIHNvIHdlIGNhbiBkZXJlZmVybmNlIGl0Cn0%3D)
3649
37-
### Prerequisites
3850
39-
**macOS:**
40-
```bash
41-
brew install zstd
42-
xcode-select --install # If not already installed
43-
```
51+
## Installation
4452
4553
### Quick Install
4654
@@ -50,6 +58,12 @@ curl -fsSL https://raw.githubusercontent.com/cs01/llvm-project/null-safe-c-dev/i
5058

5159
Or download manually from [releases](https://github.com/cs01/llvm-project/releases).
5260

61+
On mac you may need to do the following:
62+
```bash
63+
brew install zstd
64+
xcode-select --install # If not already installed
65+
```
66+
5367
### Windows
5468

5569
Builds not available at this time, you must clone and build locally.
@@ -62,7 +76,7 @@ Each release includes:
6276

6377
### IDE Integration
6478

65-
Once installed, configure your editor to use the null-safe `clangd`:
79+
Once installed, configure your editor to use the null-safe `clangd`. Install the `clangd` extension from llvm and set the path to the clangd binary you just downloaded.
6680

6781
**VSCode:**
6882
```json

clang/lib/Sema/Sema.cpp

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1051,16 +1051,33 @@ void Sema::HandleAssertNarrowing(FunctionDecl *FDecl, CallExpr *TheCall) {
10511051
}
10521052
}
10531053

1054-
// strict-nullability: Invalidate all narrowing in the current scope
10551054
void Sema::InvalidateNarrowingInCurrentScope() {
10561055
if (NullabilityNarrowingScopes.empty())
10571056
return;
10581057

1059-
// Clear ALL narrowing in all scopes, not just the current one.
1060-
// This is conservative: function calls can have arbitrary side effects through
1061-
// global state, so we can't trust any narrowing after a function call.
10621058
for (auto &Scope : NullabilityNarrowingScopes) {
1063-
Scope.clear();
1059+
llvm::SmallVector<const VarDecl*, 8> ToRemove;
1060+
1061+
for (const auto &Entry : Scope) {
1062+
const VarDecl *VD = Entry.first;
1063+
QualType VarType = VD->getType();
1064+
1065+
bool PreserveNarrowing = false;
1066+
if (const PointerType *PT = VarType->getAs<PointerType>()) {
1067+
QualType PointeeType = PT->getPointeeType();
1068+
if (PointeeType.isConstQualified()) {
1069+
PreserveNarrowing = true;
1070+
}
1071+
}
1072+
1073+
if (!PreserveNarrowing) {
1074+
ToRemove.push_back(VD);
1075+
}
1076+
}
1077+
1078+
for (const VarDecl *VD : ToRemove) {
1079+
Scope.erase(VD);
1080+
}
10641081
}
10651082
}
10661083

clang/test/Sema/strict-nullability-invalidation.c

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,3 +47,18 @@ void test_no_invalidation_without_calls(int* p) {
4747
}
4848
}
4949

50+
int is_valid(const char c);
51+
52+
void test_const_param_preserves_narrowing(const char* data) {
53+
if (!data) return;
54+
const int isvalid = is_valid(*data);
55+
char val = *data; // OK - narrowing preserved by const parameter
56+
}
57+
58+
void test_const_param_multiple_calls(const int* value) {
59+
if (!value) return;
60+
int copy1 = *value; // OK
61+
is_valid((char)*value);
62+
int copy2 = *value; // OK - narrowing preserved, parameter is const
63+
}
64+

0 commit comments

Comments
 (0)