Skip to content

Dart 3 null safety - #1213

Open
DewJunkie wants to merge 13 commits into
munificent:masterfrom
DewJunkie:dart-3-null-safety
Open

Dart 3 null safety#1213
DewJunkie wants to merge 13 commits into
munificent:masterfrom
DewJunkie:dart-3-null-safety

Conversation

@DewJunkie

Copy link
Copy Markdown

No description provided.

DewJunkie and others added 4 commits September 7, 2025 15:23
Add shell.nix to install dart2 and zulu, nix will already have a c compiler
> allow debug C builds when tooling injects _FORTIFY_SOURCE
@DewJunkie

Copy link
Copy Markdown
Author

fixes #1212

@DewJunkie

DewJunkie commented May 16, 2026

Copy link
Copy Markdown
Author

I'm not sur if the updates in pubspec.yaml make this harder than it would/could be, but it feels like a good thing to do
It introduces more reviewing however for things like

-somewhere.</p>
-<aside name="dead">
+somewhere.</p><aside name="dead">

and

-may be several characters long, like numbers (<code>123</code>), string literals (<code>"hi!"</code>),
-and identifiers (<code>min</code>).</p>
-<aside name="word">
-<p>&ldquo;Lexical&rdquo; comes from the Greek root &ldquo;lex&rdquo;, meaning &ldquo;word&rdquo;.</p>
-</aside>
+may be several characters long, like numbers (<code>123</code>), string literals (<code>&quot;hi!&quot;</code>),
+and identifiers (<code>min</code>).</p><aside name="word">
+<p>&ldquo;Lexical&rdquo; comes from the Greek root &ldquo;lex&rdquo;, meaning &ldquo;word&rdquo;.</p></aside>

@DewJunkie

Copy link
Copy Markdown
Author

I didn't include any of the generated changes to site/, I wasn't sure if that was appropriate, but it would also mean you don't have to clone this branch in order to see those changes. Possibly this was outlined in a contributing.md, and I just didn't see it yet

@rossdancraig

Copy link
Copy Markdown

Thank you, this worked for me and got me unblocked!

Before

make get
Resolving dependencies... 
The lower bound of "sdk: '>2.11.0 <3.0.0'" must be 2.12.0'
or higher to enable null safety.

The current Dart SDK (3.12.0) only supports null safety.

For details, see https://dart.dev/null-safety
make: *** [get] Error 65

After

rossdan$ make get
Resolving dependencies... 
Downloading packages... (2.9s)
  args 2.5.0 (2.7.0 available)
  async 2.11.0 (2.13.1 available)
  collection 1.18.0 (1.19.1 available)
  ffi 2.1.0 (2.2.0 available)
  glob 2.1.2 (2.1.3 available)
  http 1.1.0 (1.6.0 available)
  http_parser 4.0.2 (4.1.2 available)
  image 4.8.0 (4.9.0 available)
  js 0.6.7 (0.7.2 available)
  markdown 7.2.1 (7.3.1 available)
  meta 1.16.0 (1.18.2 available)
  mustache_template 2.0.0 (2.0.4 available)
  package_config 2.1.0 (2.2.0 available)
  path 1.9.0 (1.9.1 available)
  petitparser 6.0.1 (7.0.2 available)
  pool 1.5.1 (1.5.2 available)
  protobuf 3.1.0 (6.0.0 available)
  pub_semver 2.1.4 (2.2.0 available)
  sass 1.66.0 (1.100.0 available)
  shelf 1.4.1 (1.4.2 available)
  source_maps 0.10.12 (0.10.13 available)
  stack_trace 1.11.1 (1.12.1 available)
  stream_channel 2.1.2 (2.1.4 available)
  typed_data 1.3.2 (1.4.0 available)
  watcher 1.1.4 (1.2.1 available)
  xml 6.4.2 (7.0.1 available)
Got dependencies!
26 packages have newer versions incompatible with dependency constraints.
Try `dart pub outdated` for more information.
rossdan$ make
Compiling Dart snapshot...
- site/index.css
- site/style.css
✓ Crafting Interpreters (14 words)
✓ Dedication (23 words)
✓ Acknowledgements (309 words)
✓ Table of Contents (14 words)
✓ I. Welcome (140 words)
  ✓ 1. Introduction (3913 words)
  ✓ 2. A Map of the Territory (5181 words)
  ✓ 3. The Lox Language (6595 words)
✓ II. A Tree-Walk Interpreter (185 words)
  ✓ 4. Scanning (7193 words, 294 loc)
  ✓ 5. Representing Code (7758 words, 169 loc)
  ✓ 6. Parsing Expressions (7546 words, 169 loc)
  ✓ 7. Evaluating Expressions (5546 words, 138 loc)
  ✓ 8. Statements and State (10223 words, 202 loc)
  ✓ 9. Control Flow (5481 words, 122 loc)
  ✓ 10. Functions (7976 words, 180 loc)
  ✓ 11. Resolving and Binding (7787 words, 231 loc)
  ✓ 12. Classes (9559 words, 235 loc)
  ✓ 13. Inheritance (5130 words, 98 loc)
✓ III. A Bytecode Virtual Machine (161 words)
  ✓ 14. Chunks of Bytecode (9236 words, 225 loc)
  ✓ 15. A Virtual Machine (7176 words, 142 loc)
  ✓ 16. Scanning on Demand (7238 words, 332 loc)
  ✓ 17. Compiling Expressions (8025 words, 248 loc)
  ✓ 18. Types of Values (5847 words, 143 loc)
  ✓ 19. Strings (6520 words, 164 loc)
  ✓ 20. Hash Tables (10058 words, 192 loc)
  ✓ 21. Global Variables (6006 words, 177 loc)
  ✓ 22. Local Variables (5667 words, 138 loc)
  ✓ 23. Jumping Back and Forth (7504 words, 165 loc)
  ✓ 24. Calls and Functions (11683 words, 295 loc)
  ✓ 25. Closures (12775 words, 226 loc)
  ✓ 26. Garbage Collection (10960 words, 213 loc)
  ✓ 27. Classes and Instances (4785 words, 134 loc)
  ✓ 28. Methods and Initializers (10076 words, 196 loc)
  ✓ 29. Superclasses (5556 words, 95 loc)
  ✓ 30. Optimization (10253 words, 72 loc)
✓ Backmatter (61 words)
  ✓ A1. Appendix I (614 words)
  ✓ A2. Appendix II (1611 words, 341 loc)
Built 207,252 words and 5,336 lines of code (232,385 total words) in 2.93 seconds
      cc c/chunk.c                                -std=c99 -Wall -Wextra -Werror -Wno-unused-parameter -O3 -flto
      cc c/compiler.c                             -std=c99 -Wall -Wextra -Werror -Wno-unused-parameter -O3 -flto
      cc c/debug.c                                -std=c99 -Wall -Wextra -Werror -Wno-unused-parameter -O3 -flto
      cc c/main.c                                 -std=c99 -Wall -Wextra -Werror -Wno-unused-parameter -O3 -flto
      cc c/memory.c                               -std=c99 -Wall -Wextra -Werror -Wno-unused-parameter -O3 -flto
      cc c/object.c                               -std=c99 -Wall -Wextra -Werror -Wno-unused-parameter -O3 -flto
      cc c/scanner.c                              -std=c99 -Wall -Wextra -Werror -Wno-unused-parameter -O3 -flto
      cc c/table.c                                -std=c99 -Wall -Wextra -Werror -Wno-unused-parameter -O3 -flto
      cc c/value.c                                -std=c99 -Wall -Wextra -Werror -Wno-unused-parameter -O3 -flto
      cc c/vm.c                                   -std=c99 -Wall -Wextra -Werror -Wno-unused-parameter -O3 -flto
      cc build/clox                               -std=c99 -Wall -Wextra -Werror -Wno-unused-parameter -O3 -flto
   javac java/com/craftinginterpreters/tool/GenerateAst.java          -Werror
   javac java/com/craftinginterpreters/lox/AstPrinter.java            -Werror
   javac java/com/craftinginterpreters/lox/Environment.java           -Werror
   javac java/com/craftinginterpreters/lox/Expr.java                  -Werror
   javac java/com/craftinginterpreters/lox/Interpreter.java           -Werror
   javac java/com/craftinginterpreters/lox/Lox.java                   -Werror
   javac java/com/craftinginterpreters/lox/LoxCallable.java           -Werror
   javac java/com/craftinginterpreters/lox/LoxClass.java              -Werror
   javac java/com/craftinginterpreters/lox/LoxFunction.java           -Werror
   javac java/com/craftinginterpreters/lox/LoxInstance.java           -Werror
   javac java/com/craftinginterpreters/lox/Parser.java                -Werror
   javac java/com/craftinginterpreters/lox/Resolver.java              -Werror
   javac java/com/craftinginterpreters/lox/Return.java                -Werror
   javac java/com/craftinginterpreters/lox/RuntimeError.java          -Werror
   javac java/com/craftinginterpreters/lox/Scanner.java               -Werror
   javac java/com/craftinginterpreters/lox/Stmt.java                  -Werror
   javac java/com/craftinginterpreters/lox/Token.java                 -Werror
   javac java/com/craftinginterpreters/lox/TokenType.java             -Werror

Comment thread asset/sass/chapter.scss
Comment on lines +197 to +198
left: -(math.div($col, 2));
width: math.div($col, 2);

@rossdancraig rossdancraig May 25, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

nit: if possible, would prefer this PR to have been split into multiple parts instead of all boilerplate in 1 (thought not sure if that's possible). Ex:

  1. migration to @use "sass:math";
  2. CSS hexcodes --> hsl
  3. Non functional formatting changes

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Yeah, I kind of felt the same way about this one. It was mixed. I can work on splitting this up into the dart pacakges as one update, and the dart 3 in another. I can't remember if I started getting errors on one of the packages and that is why I went ahead and updated them, or if this just started to feel like a modernization story to me and I updated the packages. These were just warnings that they would break in the future at any rate. I try not to have warnings emitted with a build. But this isn't really related to the nullability safety at all.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Got it, thanks for context. I feel you, thanks for fixing them. Since I've already begun reviewing I'm cool with continuing, just a heads up for future reference!

Comment thread Makefile
BUILD_SNAPSHOT := $(BUILD_DIR)/build.dart.snapshot
TEST_SNAPSHOT := $(BUILD_DIR)/test.dart.snapshot

DART_FLAGS := --old_gen_heap_size=1024

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Why is this needed?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

On an early version of dart3, it will build fine without this. Prior to pushing this, I updated to a more recent dart 3, and the build started hanging with an OOM error. Adding this flag allowed it to once again build successfully. You could try with DART_FLAGS := "" and see if you can replicate it, or if it is needed. I don't use dart a whole lot, so I'm not sure if this was maybe something transient introduce in one version and then fixed, or if it will carry forward to future versions.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Got it thanks, can we add 1-line code comment so this is easier for people in future to know why it was set?

Comment thread shell.nix

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Since this is not required to unblock the main issue (Dart 3 dep), this should be in it's own separate PR. Being able to run this requires installing nix package manager. While this can be helpful, it also adds more steps for getting started, so tradeoffs for that can be discussed separately

@DewJunkie DewJunkie Jun 1, 2026

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

I based this PR onto my other PR which pinned the dart version at dart 2. Being able to have a locked down version of DART was kind of key to being able to validate this migration, because I could then compare the output of the dart2 to the output of dart3, and make sure I didn't accidentally introduce a regression. I know a lot of folks don't use nix, but this is why it was there. I had made the prior PR, because it would only build under dart2, and I had to spend a bit of time figuring out how to get an old version up and going. nix package manager is supported on many distros. Using a dockerfile could get a similar experience, and maybe be useful to more people. But I use nix.
#1199 // Dart 2 shell.nix PR

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Thanks for context, sure I'm ok with adding it since it's additive

Comment on lines +193 to +203
_location = _location.parent ?? (throw StateError('Location parent is null'));
}

// Module variables are only a single line.
if (_location.kind == "variable") {
_location = _location.parent;
_location = _location.parent ?? (throw StateError('Location parent is null'));
}

// Hack. There is a one-line class in Parser.java.
if (line.contains("class ParseError")) {
_location = _location.parent;
_location = _location.parent ?? (throw StateError('Location parent is null'));

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

would prefer to have centralized helper methods like getParent() where we only need to do null-check once. In general would prefer to do this instead of having to inline every callsite

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

I do more c# than dart by far. This is maybe a c# pattern than isn't as used?? But yes, adding a getParent would mean less boilerplate. Initially I did this with ! which also works, but I felt like the exception would be slightly better information. But also maybe the runtime ! failure would have given similar info and a stack trace. I was trying not to do any other refactoring as part of this PR, because I'm not supre familiar with this code base. But I do think this is good feedback, and I'll see if I can get to making this change when I strip out the dependency updates.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Oh yea, I think ! would be good for this case. Or also doing a check for _location.parent at the top of the flow

Comment on lines +213 to +214
var startChapterName = match[1] ?? (throw ArgumentError('Start chapter missing in match'));
var endChapterName = match[3] ?? (throw ArgumentError('End chapter missing in match'));

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Yea same thing, just some private helper like _assertNotNull(value, "var_name")

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Not sure if I agree on this one, but I'll give it some thought. To me this feels the same as _assertNoNull, but maybe I'm not getting what you are meaning by your comment. I think that for what you are sugesting, I may as well juse to match[1]! which is what I did on the first pass, but I thought having a more specific error message mya be helpful.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Yea I think just doing ! is helpful too. Anyways, not a huge blocker, just easier to read

@rossdancraig rossdancraig left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Thanks for doing this and getting people like me unblocked!

Overall lgtm just would prefer to split this up into parts that fix the dep issue vs. parts that don't (ex: nix script) and also centralize a few helpers, feel free to ping me again once ready for review again and I'll approve!

@DewJunkie

Copy link
Copy Markdown
Author

In general, I preferred
some_var ?? throw
over
somevar!
I mostly do c#, and I try to avoid the !' assert not null operator. But I'm not sure if I'm adding value by doing this, or making an odd pattern that is not used in dart. I'm open to feedback on this choice. To me they are functionally the same, but the ??coalesce vs the!` assertion gives the opportunity to provide some guidance around what went wrong, and so that is why I prefer it. Stack trace arguably does very similar.

@rossdancraig rossdancraig left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Cool I'm down to stamp, looks good enough to fix core issues where Lox can't be used with Dart 3 so hopefully this gets people unblocked. Feel free to message me if you decide to make any changes and need another review!

@DewJunkie
DewJunkie marked this pull request as ready for review June 18, 2026 20:54
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants