CINXE.COM
Fixing type promotion failures | Dart
<!doctype html><html lang="en"><head><meta charset="utf-8"><meta http-equiv="x-ua-compatible" content="ie=edge"><meta name="viewport" content="width=device-width,initial-scale=1"><script>!function(e,t,a,n){e[n]=e[n]||[],e[n].push({"gtm.start":(new Date).getTime(),event:"gtm.js"});var g=t.getElementsByTagName(a)[0],m=t.createElement(a);m.async=!0,m.src="https://www.googletagmanager.com/gtm.js?id=GTM-5VSZM5J",g.parentNode.insertBefore(m,g)}(window,document,"script","dataLayer")</script><meta name="description" content="Solutions for cases where you know more about a field's type than Dart can determine."><title>Fixing type promotion failures | Dart</title><link rel="icon" sizes="64x64" href="/assets/img/logo/dart-64.png" eleventy:ignore><link href="/assets/img/touch-icon-iphone.png" rel="apple-touch-icon" eleventy:ignore><link href="/assets/img/touch-icon-ipad.png" rel="apple-touch-icon" sizes="152x152" eleventy:ignore><link href="/assets/img/touch-icon-iphone-retina.png" rel="apple-touch-icon" sizes="180x180" eleventy:ignore><link href="/assets/img/touch-icon-ipad-retina.png" rel="apple-touch-icon" sizes="167x167" eleventy:ignore><meta name="twitter:card" content="summary"><meta name="twitter:site" content="@dart_lang"><meta name="twitter:title" content="Fixing type promotion failures"><meta name="twitter:description" content="Solutions for cases where you know more about a field's type than Dart can determine."><meta property="og:title" content="Fixing type promotion failures"><meta property="og:description" content="Solutions for cases where you know more about a field's type than Dart can determine."><meta property="og:url" content="/tools/non-promotion-reasons/"><meta property="og:image" content="/assets/img/logo/dart-logo-for-shares.png?2" eleventy:ignore><link rel="preconnect" href="https://fonts.googleapis.com"><link rel="preconnect" href="https://fonts.gstatic.com" crossorigin><link href="https://fonts.googleapis.com/css2?family=Google+Sans:wght@400;500;700&display=swap" rel="stylesheet"><link href="https://fonts.googleapis.com/css2?family=Google+Sans+Display:wght@400&display=swap" rel="stylesheet"><link href="https://fonts.googleapis.com/css2?family=Google+Sans+Mono:wght@400;500;700&display=swap" rel="stylesheet"><link href="https://fonts.googleapis.com/css2?family=Google+Sans+Text:wght@400;500;700&display=swap" rel="stylesheet"><link href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:opsz,wght,FILL,GRAD@24,400,0,0" rel="stylesheet"><link rel="stylesheet" href="/assets/css/main.css"><script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.7.0/jquery.min.js" integrity="sha512-3gJwYpMe3QewGELv8k/BX9vcqhryRdzRMxVfq6ngyWXwo03GFEzjsUm8Q7RZcHPHksttq7/GFoxjCVUjkjvPdw==" crossorigin="anonymous" referrerpolicy="no-referrer"></script><script src="https://cdnjs.cloudflare.com/ajax/libs/bootstrap/4.6.2/js/bootstrap.min.js" integrity="sha384-+sLIOodYLS7CIrQpBjl+C7nPvqq+FbNUBDunl/OZv93DB7Ln/533i8e/mZXLi/P+" crossorigin="anonymous" referrerpolicy="no-referrer"></script><script src="https://cdnjs.cloudflare.com/ajax/libs/js-cookie/3.0.5/js.cookie.min.js" integrity="sha512-nlp9/l96/EpjYBx7EP7pGASVXNe80hGhYAUrjeXnu/fyF5Py0/RXav4BBNs7n5Hx1WFhOEOWSAVjGeC3oKxDVQ==" crossorigin="anonymous" referrerpolicy="no-referrer"></script><script src="/assets/js/os-tabs.js"></script><script src="/assets/js/utilities.js"></script><script src="/assets/js/main.js"></script><script>!function(e,a,t,n,c,o,s){e.GoogleAnalyticsObject=c,e[c]=e[c]||function(){(e[c].q=e[c].q||[]).push(arguments)},e[c].l=1*new Date,o=a.createElement(t),s=a.getElementsByTagName(t)[0],o.async=1,o.src="//www.google-analytics.com/analytics.js",s.parentNode.insertBefore(o,s)}(window,document,"script",0,"ga"),ga("create","UA-26406144-4","auto"),ga("send","pageview")</script></head><body class="default.html show_banner"><a id="skip" href="#site-content-title">Skip to main content</a><section id="cookie-notice"><div class="container"><p>dart.dev uses cookies from Google to deliver and enhance the quality of its services and to analyze traffic. <a href="https://policies.google.com/technologies/cookies" target="_blank" rel="noopener">Learn more</a>.</p><button id="cookie-consent" class="btn btn-primary">OK, got it</button></div></section><noscript><iframe src="https://www.googletagmanager.com/ns.html?id=GTM-5VSZM5J" height="0" width="0" style="display:none;visibility:hidden"></iframe></noscript><header id="page-header" class="site-header"><nav id="mainnav" class="site-header"><div id="menu-toggle"><i class="material-symbols">menu</i></div><a href="/" class="brand" title="Dart"><img src="/assets/img/logo/logo-white-text.svg" alt="Dart"></a><ul class="navbar"><li><a href="/overview" class="nav-link">Overview</a></li><li class="mainnav__get-started"><a href="/guides" class="nav-link active"><span>Docs</span></a></li><li><a href="/community" class="nav-link">Community</a></li><li><a href="/#try-dart" class="nav-link">Try Dart</a></li><li><a href="/get-dart" class="nav-link">Get Dart</a></li><li class="searchfield"><form action="/search" class="site-header__search form-inline" id="cse-search-box"><input type="hidden" name="cx" value="011220921317074318178:_yy-tmb5t_i"> <input type="hidden" name="ie" value="UTF-8"> <input type="hidden" name="hl" value="en"> <input class="site-header__searchfield form-control search-field" type="search" name="q" id="search-main" autocomplete="off" placeholder="Search" aria-label="Search"></form></li></ul></nav></header><div class="banner"><p class="banner__text">Announcing Dart 3.5 and an updated Dart roadmap! <a href="https://medium.com/dartlang/dart-3-5-6ca36259fa2f" target="_blank">Learn more</a></p></div><div id="sidenav"><form action="/search/" class="site-header__search form-inline"><input class="site-header__searchfield form-control search-field" type="search" name="q" id="search-side" autocomplete="off" placeholder="Search" aria-label="Search"></form><div class="site-sidebar"><ul class="navbar-nav"><li class="nav-item"><a href="/overview" class="nav-link">Overview</a></li><li class="nav-item"><a href="/community" class="nav-link">Community</a></li><li class="nav-item"><a href="https://dartpad.dev" class="nav-link">Try Dart</a></li><li class="nav-item"><a href="/get-dart" class="nav-link">Get Dart</a></li><li class="nav-item"><a href="/guides" class="nav-link">Docs</a></li><li aria-hidden="true"><div class="sidebar-primary-divider"></div></li></ul><ul class="nav flex-column"><li class="nav-item"><a class="nav-link collapsed collapsible" data-toggle="collapse" href="#sidenav-1" role="button" aria-expanded="false" aria-controls="sidenav-1">Language</a><ul class="nav flex-column flex-nowrap collapse" id="sidenav-1"><li class="nav-item"><a class="nav-link" href="/language">Introduction</a></li><li class="nav-item"><a class="nav-link collapsible collapsed" data-toggle="collapse" data-target="#sidenav-1-2" href="#sidenav-1-2" role="button" aria-expanded="false" aria-controls="sidenav-1-2">Syntax basics</a><ul class="nav flex-column flex-nowrap collapse" id="sidenav-1-2"><li class="nav-item"><a class="nav-link" href="/language/variables">Variables</a></li><li class="nav-item"><a class="nav-link" href="/language/operators">Operators</a></li><li class="nav-item"><a class="nav-link" href="/language/comments">Comments</a></li><li class="nav-item"><a class="nav-link" href="/language/metadata">Metadata</a></li><li class="nav-item"><a class="nav-link" href="/language/libraries">Libraries & imports</a></li><li class="nav-item"><a class="nav-link" href="/language/keywords">Keywords</a></li></ul></li><li class="nav-item"><a class="nav-link collapsible collapsed" data-toggle="collapse" data-target="#sidenav-1-3" href="#sidenav-1-3" role="button" aria-expanded="false" aria-controls="sidenav-1-3">Types</a><ul class="nav flex-column flex-nowrap collapse" id="sidenav-1-3"><li class="nav-item"><a class="nav-link" href="/language/built-in-types">Built-in types</a></li><li class="nav-item"><a class="nav-link" href="/language/records">Records</a></li><li class="nav-item"><a class="nav-link" href="/language/collections">Collections</a></li><li class="nav-item"><a class="nav-link" href="/language/generics">Generics</a></li><li class="nav-item"><a class="nav-link" href="/language/typedefs">Typedefs</a></li><li class="nav-item"><a class="nav-link" href="/language/type-system">Type system</a></li></ul></li><li class="nav-item"><a class="nav-link collapsible collapsed" data-toggle="collapse" data-target="#sidenav-1-4" href="#sidenav-1-4" role="button" aria-expanded="false" aria-controls="sidenav-1-4">Patterns</a><ul class="nav flex-column flex-nowrap collapse" id="sidenav-1-4"><li class="nav-item"><a class="nav-link" href="/language/patterns">Overview & usage</a></li><li class="nav-item"><a class="nav-link" href="/language/pattern-types">Pattern types</a></li><li class="nav-item"><a class="nav-link" href="https://codelabs.developers.google.com/codelabs/dart-patterns-records" target="_blank" rel="noopener">Applied tutorial</a></li></ul></li><li class="nav-item"><a class="nav-link" href="/language/functions">Functions</a></li><li class="nav-item"><a class="nav-link collapsible collapsed" data-toggle="collapse" data-target="#sidenav-1-6" href="#sidenav-1-6" role="button" aria-expanded="false" aria-controls="sidenav-1-6">Control flow</a><ul class="nav flex-column flex-nowrap collapse" id="sidenav-1-6"><li class="nav-item"><a class="nav-link" href="/language/loops">Loops</a></li><li class="nav-item"><a class="nav-link" href="/language/branches">Branches</a></li><li class="nav-item"><a class="nav-link" href="/language/error-handling">Error handling</a></li></ul></li><li class="nav-item"><a class="nav-link collapsible collapsed" data-toggle="collapse" data-target="#sidenav-1-7" href="#sidenav-1-7" role="button" aria-expanded="false" aria-controls="sidenav-1-7">Classes & objects</a><ul class="nav flex-column flex-nowrap collapse" id="sidenav-1-7"><li class="nav-item"><a class="nav-link" href="/language/classes">Classes</a></li><li class="nav-item"><a class="nav-link" href="/language/constructors">Constructors</a></li><li class="nav-item"><a class="nav-link" href="/language/methods">Methods</a></li><li class="nav-item"><a class="nav-link" href="/language/extend">Extend a class</a></li><li class="nav-item"><a class="nav-link" href="/language/mixins">Mixins</a></li><li class="nav-item"><a class="nav-link" href="/language/enums">Enums</a></li><li class="nav-item"><a class="nav-link" href="/language/extension-methods">Extension methods</a></li><li class="nav-item"><a class="nav-link" href="/language/extension-types">Extension types</a></li><li class="nav-item"><a class="nav-link" href="/language/callable-objects">Callable objects</a></li></ul></li><li class="nav-item"><a class="nav-link collapsible collapsed" data-toggle="collapse" data-target="#sidenav-1-8" href="#sidenav-1-8" role="button" aria-expanded="false" aria-controls="sidenav-1-8">Class modifiers</a><ul class="nav flex-column flex-nowrap collapse" id="sidenav-1-8"><li class="nav-item"><a class="nav-link" href="/language/class-modifiers">Overview & usage</a></li><li class="nav-item"><a class="nav-link" href="/language/class-modifiers-for-apis">Class modifiers for API maintainers</a></li><li class="nav-item"><a class="nav-link" href="/language/modifier-reference">Reference</a></li></ul></li><li class="nav-item"><a class="nav-link collapsible collapsed" data-toggle="collapse" data-target="#sidenav-1-9" href="#sidenav-1-9" role="button" aria-expanded="false" aria-controls="sidenav-1-9">Concurrency</a><ul class="nav flex-column flex-nowrap collapse" id="sidenav-1-9"><li class="nav-item"><a class="nav-link" href="/language/concurrency">Overview</a></li><li class="nav-item"><a class="nav-link" href="/language/async">Asynchronous support</a></li><li class="nav-item"><a class="nav-link" href="/language/isolates">Isolates</a></li></ul></li><li class="nav-item"><a class="nav-link collapsible collapsed" data-toggle="collapse" data-target="#sidenav-1-10" href="#sidenav-1-10" role="button" aria-expanded="false" aria-controls="sidenav-1-10">Null safety</a><ul class="nav flex-column flex-nowrap collapse" id="sidenav-1-10"><li class="nav-item"><a class="nav-link" href="/null-safety">Sound null safety</a></li><li class="nav-item"><a class="nav-link" href="/null-safety/migration-guide">Migrating to null safety</a></li><li class="nav-item"><a class="nav-link" href="/null-safety/understanding-null-safety">Understanding null safety</a></li><li class="nav-item"><a class="nav-link" href="/null-safety/unsound-null-safety">Unsound null safety</a></li><li class="nav-item"><a class="nav-link" href="/null-safety/faq">FAQ</a></li></ul></li></ul></li><li class="nav-item"><a class="nav-link collapsed collapsible" data-toggle="collapse" href="#sidenav-2" role="button" aria-expanded="false" aria-controls="sidenav-2">Core libraries</a><ul class="nav flex-column flex-nowrap collapse" id="sidenav-2"><li class="nav-item"><a class="nav-link" href="/libraries">Overview</a></li><li class="nav-item"><a class="nav-link" href="/libraries/dart-core">dart:core</a></li><li class="nav-item"><a class="nav-link" href="/libraries/dart-async">dart:async</a></li><li class="nav-item"><a class="nav-link" href="/libraries/dart-math">dart:math</a></li><li class="nav-item"><a class="nav-link" href="/libraries/dart-convert">dart:convert</a></li><li class="nav-item"><a class="nav-link" href="/libraries/dart-io">dart:io</a></li><li class="nav-item"><a class="nav-link" href="/libraries/dart-html">dart:html</a></li><div class="dropdown-divider"></div><li class="nav-item"><a class="nav-link" href="/libraries/collections/iterables">Iterable collections</a></li><li class="nav-item"><a class="nav-link collapsible collapsed" data-toggle="collapse" data-target="#sidenav-2-10" href="#sidenav-2-10" role="button" aria-expanded="false" aria-controls="sidenav-2-10">Asynchronous programming</a><ul class="nav flex-column flex-nowrap collapse" id="sidenav-2-10"><li class="nav-item"><a class="nav-link" href="/libraries/async/async-await">Tutorial</a></li><li class="nav-item"><a class="nav-link" href="/libraries/async/futures-error-handling">Futures and error handling</a></li><li class="nav-item"><a class="nav-link" href="/libraries/async/using-streams">Using streams</a></li><li class="nav-item"><a class="nav-link" href="/libraries/async/creating-streams">Creating streams</a></li></ul></li></ul></li><li class="nav-item"><a class="nav-link collapsed collapsible" data-toggle="collapse" href="#sidenav-3" role="button" aria-expanded="false" aria-controls="sidenav-3">Effective Dart</a><ul class="nav flex-column flex-nowrap collapse" id="sidenav-3"><li class="nav-item"><a class="nav-link" href="/effective-dart">Overview</a></li><li class="nav-item"><a class="nav-link" href="/effective-dart/style">Style</a></li><li class="nav-item"><a class="nav-link" href="/effective-dart/documentation">Documentation</a></li><li class="nav-item"><a class="nav-link" href="/effective-dart/usage">Usage</a></li><li class="nav-item"><a class="nav-link" href="/effective-dart/design">Design</a></li></ul></li><li class="nav-item"><a class="nav-link collapsed collapsible" data-toggle="collapse" href="#sidenav-4" role="button" aria-expanded="false" aria-controls="sidenav-4">Packages</a><ul class="nav flex-column flex-nowrap collapse" id="sidenav-4"><li class="nav-item"><a class="nav-link" href="/tools/pub/packages">How to use packages</a></li><li class="nav-item"><a class="nav-link" href="/resources/useful-packages">Commonly used packages</a></li><li class="nav-item"><a class="nav-link" href="/guides/libraries/create-packages">Creating packages</a></li><li class="nav-item"><a class="nav-link" href="/tools/pub/publishing">Publishing packages</a></li><li class="nav-item"><a class="nav-link" href="/tools/pub/writing-package-pages">Writing package pages</a></li><li class="nav-item"><a class="nav-link collapsible collapsed" data-toggle="collapse" data-target="#sidenav-4-6" href="#sidenav-4-6" role="button" aria-expanded="false" aria-controls="sidenav-4-6">Package reference</a><ul class="nav flex-column flex-nowrap collapse" id="sidenav-4-6"><li class="nav-item"><a class="nav-link" href="/tools/pub/dependencies">Dependencies</a></li><li class="nav-item"><a class="nav-link" href="/tools/pub/glossary">Glossary</a></li><li class="nav-item"><a class="nav-link" href="/tools/pub/package-layout">Package layout conventions</a></li><li class="nav-item"><a class="nav-link" href="/tools/pub/environment-variables">Pub environment variables</a></li><li class="nav-item"><a class="nav-link" href="/tools/pub/pubspec">Pubspec file</a></li><li class="nav-item"><a class="nav-link" href="/tools/pub/troubleshoot">Troubleshooting pub</a></li><li class="nav-item"><a class="nav-link" href="/tools/pub/verified-publishers">Verified publishers</a></li><li class="nav-item"><a class="nav-link" href="/tools/pub/security-advisories">Security advisories</a></li><li class="nav-item"><a class="nav-link" href="/tools/pub/versioning">Versioning</a></li><li class="nav-item"><a class="nav-link" href="/tools/pub/custom-package-repositories">Custom package repositories</a></li></ul></li><li class="nav-item"><a class="nav-link" href="/guides/libraries/private-files">What not to commit</a></li></ul></li><li class="nav-item"><a class="nav-link collapsed collapsible" data-toggle="collapse" href="#sidenav-5" role="button" aria-expanded="false" aria-controls="sidenav-5">Development</a><ul class="nav flex-column flex-nowrap collapse" id="sidenav-5"><li class="nav-item"><a class="nav-link" href="/guides/json">JSON</a></li><li class="nav-item"><a class="nav-link" href="/guides/language/numbers">Number representation</a></li><li class="nav-item"><a class="nav-link" href="/resources/google-apis">Google APIs</a></li><li class="nav-item"><a class="nav-link" href="/multiplatform-apps">Multi-platform apps</a></li><li class="nav-item"><a class="nav-link collapsible collapsed" data-toggle="collapse" data-target="#sidenav-5-5" href="#sidenav-5-5" role="button" aria-expanded="false" aria-controls="sidenav-5-5">Command-line & server apps</a><ul class="nav flex-column flex-nowrap collapse" id="sidenav-5-5"><li class="nav-item"><a class="nav-link" href="/server">Overview</a></li><li class="nav-item"><a class="nav-link" href="/tutorials/server/get-started">Get started</a></li><li class="nav-item"><a class="nav-link" href="/tutorials/server/cmdline">Write command-line apps</a></li><li class="nav-item"><a class="nav-link" href="/tutorials/server/fetch-data">Fetch data from the internet</a></li><li class="nav-item"><a class="nav-link" href="/tutorials/server/httpserver">Write HTTP servers</a></li><li class="nav-item"><a class="nav-link" href="/server/libraries">Libraries & packages</a></li><li class="nav-item"><a class="nav-link" href="/server/google-cloud">Google Cloud</a></li></ul></li><li class="nav-item"><a class="nav-link collapsible collapsed" data-toggle="collapse" data-target="#sidenav-5-6" href="#sidenav-5-6" role="button" aria-expanded="false" aria-controls="sidenav-5-6">Web apps</a><ul class="nav flex-column flex-nowrap collapse" id="sidenav-5-6"><li class="nav-item"><a class="nav-link" href="/web">Overview</a></li><li class="nav-item"><a class="nav-link" href="/web/get-started">Get started</a></li><li class="nav-item"><a class="nav-link" href="/web/deployment">Deployment</a></li><li class="nav-item"><a class="nav-link" href="/web/libraries">Libraries & packages</a></li><li class="nav-item"><a class="nav-link" href="/web/wasm">Wasm compilation</a></li></ul></li><li class="nav-item"><a class="nav-link" href="/guides/environment-declarations">Environment declarations</a></li></ul></li><li class="nav-item"><a class="nav-link collapsed collapsible" data-toggle="collapse" href="#sidenav-6" role="button" aria-expanded="false" aria-controls="sidenav-6">Interoperability</a><ul class="nav flex-column flex-nowrap collapse" id="sidenav-6"><li class="nav-item"><a class="nav-link" href="/interop/c-interop">C interop</a></li><li class="nav-item"><a class="nav-link" href="/interop/objective-c-interop">Objective-C & Swift interop</a></li><li class="nav-item"><a class="nav-link" href="/interop/java-interop">Java & Kotlin interop</a></li><li class="nav-item"><a class="nav-link collapsible collapsed" data-toggle="collapse" data-target="#sidenav-6-4" href="#sidenav-6-4" role="button" aria-expanded="false" aria-controls="sidenav-6-4">JavaScript interop</a><ul class="nav flex-column flex-nowrap collapse" id="sidenav-6-4"><li class="nav-item"><a class="nav-link" href="/interop/js-interop">Overview</a></li><li class="nav-item"><a class="nav-link" href="/interop/js-interop/usage">Usage</a></li><li class="nav-item"><a class="nav-link" href="/interop/js-interop/js-types">JS types</a></li><li class="nav-item"><a class="nav-link" href="/interop/js-interop/tutorials">Tutorials</a></li><li class="nav-item"><a class="nav-link" href="/interop/js-interop/past-js-interop">Past JS interop</a></li><div class="dropdown-divider"></div><li class="nav-item"><a class="nav-link" href="/interop/js-interop/package-web">Web interop</a></li></ul></li></ul></li><li class="nav-item"><a class="nav-link active collapsible" data-toggle="collapse" href="#sidenav-7" role="button" aria-expanded="true" aria-controls="sidenav-7">Tools & techniques</a><ul class="nav flex-column flex-nowrap collapse show" id="sidenav-7"><li class="nav-item"><a class="nav-link" href="/tools">Overview</a></li><li class="nav-item"><a class="nav-link collapsible collapsed" data-toggle="collapse" data-target="#sidenav-7-2" href="#sidenav-7-2" role="button" aria-expanded="false" aria-controls="sidenav-7-2">Editors & debuggers</a><ul class="nav flex-column flex-nowrap collapse" id="sidenav-7-2"><li class="nav-item"><a class="nav-link" href="/tools/jetbrains-plugin">IntelliJ & Android Studio</a></li><li class="nav-item"><a class="nav-link" href="/tools/vs-code">VS Code</a></li><li class="nav-item"><a class="nav-link" href="/tools/dart-devtools">Dart DevTools</a></li><li class="nav-item"><a class="nav-link collapsible collapsed" data-toggle="collapse" data-target="#sidenav-7-2-4" href="#sidenav-7-2-4" role="button" aria-expanded="false" aria-controls="sidenav-7-2-4">DartPad</a><ul class="nav flex-column flex-nowrap collapse" id="sidenav-7-2-4"><li class="nav-item"><a class="nav-link" href="/tools/dartpad">Overview</a></li><li class="nav-item"><a class="nav-link" href="/tools/dartpad/troubleshoot">Troubleshooting DartPad</a></li></ul></li></ul></li><li class="nav-item"><a class="nav-link collapsible collapsed" data-toggle="collapse" data-target="#sidenav-7-3" href="#sidenav-7-3" role="button" aria-expanded="false" aria-controls="sidenav-7-3">Command-line tools</a><ul class="nav flex-column flex-nowrap collapse" id="sidenav-7-3"><li class="nav-item"><a class="nav-link collapsible" data-toggle="collapse" data-target="#sidenav-7-3-1" href="#sidenav-7-3-1" role="button" aria-expanded="true" aria-controls="sidenav-7-3-1">Dart SDK</a><ul class="nav flex-column flex-nowrap collapse show" id="sidenav-7-3-1"><li class="nav-item"><a class="nav-link" href="/tools/sdk">Overview</a></li><li class="nav-item"><a class="nav-link" href="/tools/dart-tool">dart</a></li><li class="nav-item"><a class="nav-link" href="/tools/dart-analyze">dart analyze</a></li><li class="nav-item"><a class="nav-link" href="/tools/dart-compile">dart compile</a></li><li class="nav-item"><a class="nav-link" href="/tools/dart-create">dart create</a></li><li class="nav-item"><a class="nav-link" href="/tools/dart-doc">dart doc</a></li><li class="nav-item"><a class="nav-link" href="/tools/dart-fix">dart fix</a></li><li class="nav-item"><a class="nav-link" href="/tools/dart-format">dart format</a></li><li class="nav-item"><a class="nav-link" href="/tools/dart-info">dart info</a></li><li class="nav-item"><a class="nav-link" href="/tools/pub/cmd">dart pub</a></li><li class="nav-item"><a class="nav-link" href="/tools/dart-run">dart run</a></li><li class="nav-item"><a class="nav-link" href="/tools/dart-test">dart test</a></li><li class="nav-item"><a class="nav-link" href="/tools/dartaotruntime">dartaotruntime</a></li><li class="nav-item"><a class="nav-link" href="/tools/experiment-flags">Experiment flags</a></li></ul></li><li class="nav-item"><a class="nav-link collapsible" data-toggle="collapse" data-target="#sidenav-7-3-2" href="#sidenav-7-3-2" role="button" aria-expanded="true" aria-controls="sidenav-7-3-2">Other command-line tools</a><ul class="nav flex-column flex-nowrap collapse show" id="sidenav-7-3-2"><li class="nav-item"><a class="nav-link" href="/tools/build_runner">build_runner</a></li><li class="nav-item"><a class="nav-link" href="/tools/webdev">webdev</a></li></ul></li></ul></li><li class="nav-item"><a class="nav-link active collapsible" data-toggle="collapse" data-target="#sidenav-7-4" href="#sidenav-7-4" role="button" aria-expanded="true" aria-controls="sidenav-7-4">Static analysis</a><ul class="nav flex-column flex-nowrap collapse show" id="sidenav-7-4"><li class="nav-item"><a class="nav-link" href="/tools/analysis">Customizing static analysis</a></li><li class="nav-item"><a class="nav-link" href="/guides/language/sound-problems">Fixing common type problems</a></li><li class="nav-item"><a class="nav-link active" href="/tools/non-promotion-reasons">Fixing type promotion failures</a></li><li class="nav-item"><a class="nav-link" href="/tools/linter-rules">Linter rules</a></li><li class="nav-item"><a class="nav-link" href="/tools/diagnostic-messages">Diagnostic messages</a></li></ul></li><li class="nav-item"><a class="nav-link collapsible collapsed" data-toggle="collapse" data-target="#sidenav-7-5" href="#sidenav-7-5" role="button" aria-expanded="false" aria-controls="sidenav-7-5">Testing & optimization</a><ul class="nav flex-column flex-nowrap collapse" id="sidenav-7-5"><li class="nav-item"><a class="nav-link" href="/guides/testing">Testing</a></li><li class="nav-item"><a class="nav-link" href="/web/debugging">Debugging web apps</a></li></ul></li></ul></li><li aria-hidden="true"><div class="sidebar-primary-divider"></div></li><li class="nav-item"><a class="nav-link collapsed collapsible" data-toggle="collapse" href="#sidenav-9" role="button" aria-expanded="false" aria-controls="sidenav-9">Resources</a><ul class="nav flex-column flex-nowrap collapse" id="sidenav-9"><li class="nav-item"><a class="nav-link" href="/resources/dart-cheatsheet">Language cheatsheet</a></li><li class="nav-item"><a class="nav-link" href="/resources/breaking-changes">Breaking changes</a></li><li class="nav-item"><a class="nav-link" href="/guides/language/evolution">Language evolution</a></li><li class="nav-item"><a class="nav-link" href="/guides/language/spec">Language specification</a></li><li class="nav-item"><a class="nav-link" href="/resources/dart-3-migration">Dart 3 migration guide</a></li><li class="nav-item"><a class="nav-link collapsible collapsed" data-toggle="collapse" data-target="#sidenav-9-6" href="#sidenav-9-6" role="button" aria-expanded="false" aria-controls="sidenav-9-6">Coming from ...</a><ul class="nav flex-column flex-nowrap collapse" id="sidenav-9-6"><li class="nav-item"><a class="nav-link" href="/resources/coming-from/js-to-dart">JavaScript to Dart</a></li><li class="nav-item"><a class="nav-link" href="/resources/coming-from/swift-to-dart">Swift to Dart</a></li></ul></li><div class="dropdown-divider"></div><li class="nav-item"><a class="nav-link" href="/resources/faq">FAQ</a></li><li class="nav-item"><a class="nav-link" href="/resources/glossary">Glossary</a></li><li class="nav-item"><a class="nav-link" href="/resources/books">Books</a></li><li class="nav-item"><a class="nav-link" href="/resources/videos">Videos</a></li><li class="nav-item"><a class="nav-link" href="/tutorials">Tutorials</a></li></ul></li><li class="nav-item"><a class="nav-link collapsible" data-toggle="collapse" href="#sidenav-10" role="button" aria-expanded="true" aria-controls="sidenav-10">Related sites</a><ul class="nav flex-column flex-nowrap collapse show" id="sidenav-10"><li class="nav-item"><a class="nav-link" href="https://api.dart.dev" target="_blank" rel="noopener">API reference</a></li><li class="nav-item"><a class="nav-link" href="https://medium.com/dartlang" target="_blank" rel="noopener">Blog</a></li><li class="nav-item"><a class="nav-link" href="https://dartpad.dev" target="_blank" rel="noopener">DartPad (online editor)</a></li><li class="nav-item"><a class="nav-link" href="https://flutter.dev" target="_blank" rel="noopener">Flutter</a></li><li class="nav-item"><a class="nav-link" href="https://pub.dev" target="_blank" rel="noopener">Package site</a></li></ul></li></ul></div></div><main id="page-content"><div id="site-toc--side" class="site-toc"><header class="site-toc__title">Contents</header><ul class="section-nav"><li class="toc-entry nav-item"><a class="nav-link" href="#language-version">Unsupported language version for field promotion</a></li><li class="toc-entry nav-item"><a class="nav-link" href="#property">Only local variables can be promoted (before Dart 3.2)</a></li><li class="toc-entry nav-item"><a class="nav-link" href="#other-causes-and-workarounds">Other causes and workarounds</a><ul class="nav"><li class="toc-entry nav-item"><a class="nav-link" href="#this">Can't promote this</a></li><li class="toc-entry nav-item"><a class="nav-link" href="#private">Only private fields can be promoted</a></li><li class="toc-entry nav-item"><a class="nav-link" href="#final">Only final fields can be promoted</a></li><li class="toc-entry nav-item"><a class="nav-link" href="#not-field">Getters can't be promoted</a></li><li class="toc-entry nav-item"><a class="nav-link" href="#external">External fields can't be promoted</a></li><li class="toc-entry nav-item"><a class="nav-link" href="#getter-name">Conflict with getter elsewhere in library</a></li><li class="toc-entry nav-item"><a class="nav-link" href="#field-name">Conflict with non-promotable field elsewhere in library</a></li><li class="toc-entry nav-item"><a class="nav-link" href="#nosuchmethod">Conflict with implicit noSuchMethod forwarder</a></li><li class="toc-entry nav-item"><a class="nav-link" href="#write">Possibly written after promotion</a></li><li class="toc-entry nav-item"><a class="nav-link" href="#loop-or-switch">Possibly written in a previous loop iteration</a></li><li class="toc-entry nav-item"><a class="nav-link" href="#catch">In catch after possible write in try</a></li><li class="toc-entry nav-item"><a class="nav-link" href="#subtype-mismatch">Subtype mismatch</a></li><li class="toc-entry nav-item"><a class="nav-link" href="#captured-local">Write captured by a local function</a></li><li class="toc-entry nav-item"><a class="nav-link" href="#write-outer">Written outside of the current closure or function expression</a></li><li class="toc-entry nav-item"><a class="nav-link" href="#captured-outer">Write captured outside of the current closure or function expression</a></li></ul></li></ul></div><article><div class="content"><div id="site-content-title"><h1>Fixing type promotion failures</h1></div><div id="site-toc--inline" class="site-toc toc-collapsible toc-collapsed"><header class="site-toc__title">Contents <span class="site-toc--inline__toggle toc-toggle-down"><i class="material-symbols">keyboard_arrow_down</i></span> <span class="site-toc--inline__toggle toc-toggle-up"><i class="material-symbols">keyboard_arrow_up</i></span></header><ul class="section-nav"><li class="toc-entry"><a href="#language-version">Unsupported language version for field promotion</a></li><li class="toc-entry"><a href="#property">Only local variables can be promoted (before Dart 3.2)</a></li><li class="toc-entry"><a href="#other-causes-and-workarounds">Other causes and workarounds</a><ul><li class="toc-entry"><a href="#this">Can't promote this</a></li><li class="toc-entry"><a href="#private">Only private fields can be promoted</a></li><li class="toc-entry"><a href="#final">Only final fields can be promoted</a></li><li class="toc-entry"><a href="#not-field">Getters can't be promoted</a></li><li class="toc-entry"><a href="#external">External fields can't be promoted</a></li><li class="toc-entry"><a href="#getter-name">Conflict with getter elsewhere in library</a></li><li class="toc-entry"><a href="#field-name">Conflict with non-promotable field elsewhere in library</a></li><li class="toc-entry"><a href="#nosuchmethod">Conflict with implicit noSuchMethod forwarder</a></li><li class="toc-entry"><a href="#write">Possibly written after promotion</a></li><li class="toc-entry"><a href="#loop-or-switch">Possibly written in a previous loop iteration</a></li><li class="toc-entry"><a href="#catch">In catch after possible write in try</a></li><li class="toc-entry"><a href="#subtype-mismatch">Subtype mismatch</a></li><li class="toc-entry"><a href="#captured-local">Write captured by a local function</a></li><li class="toc-entry"><a href="#write-outer">Written outside of the current closure or function expression</a></li><li class="toc-entry"><a href="#captured-outer">Write captured outside of the current closure or function expression</a></li></ul></li></ul><span class="site-toc--inline__toggle toc-toggle-more-items"><i class="material-symbols">more_horiz</i></span></div><p><a href="/null-safety/understanding-null-safety#type-promotion-on-null-checks">Type promotion</a> occurs when flow analysis can soundly confirm a variable with a <a href="/null-safety/understanding-null-safety#non-nullable-and-nullable-types">nullable type</a> is <em>not null</em>, and that it will not change from that point on. Many circumstances can weaken a type's soundness, causing type promotion to fail.</p><p>This page lists reasons why type promotion failures occur, with tips on how to fix them. To learn more about flow analysis and type promotion, check out the <a href="/null-safety/understanding-null-safety">Understanding null safety</a> page.</p><div class="header-wrapper"><h2 id="language-version">Unsupported language version for field promotion</h2><a class="heading-link" href="#language-version" aria-label="Link to 'Unsupported language version for field promotion' section">#</a></div><p><strong>The cause:</strong> You're trying to promote a field, but field promotion is language versioned, and your code is set to a language version before 3.2.</p><p>If you're already using an SDK version >= Dart 3.2, your code might still be explicitly targeted for an earlier <a href="/guides/language/evolution#language-versioning">language version</a>. This can happen either because:</p><ul><li>Your <a href="/tools/pub/pubspec"><code>pubspec.yaml</code></a> declares an SDK constraint with a lower bound below 3.2, or</li><li>You have a <code>// @dart=version</code> comment at the top of the file, where <code>version</code> is lower than 3.2.</li></ul><p><strong>Example:</strong></p><div class="code-block-wrapper language-dart"><div class="code-block-body has-tag tag-bad"><span class="code-block-tag">bad</span><span class="code-block-language" title="Language dart">dart</span><pre class="shiki dash-light" tabindex="0"><code><span class="line"><span style="color:#6E6E70">// @dart=3.1</span></span> <span class="line"></span> <span class="line"><span style="color:#D43324">class</span><span style="color:#0468D7"> C</span><span style="color:#222222"> {</span></span> <span class="line"><span style="color:#D43324"> final</span><span style="color:#0468D7"> int</span><span style="color:#222222">? _i;</span></span> <span class="line"><span style="color:#0468D7"> C</span><span style="color:#222222">(</span><span style="color:#D43324">this</span><span style="color:#222222">._i);</span></span> <span class="line"></span> <span class="line"><span style="color:#D43324"> void</span><span style="color:#6200EE"> f</span><span style="color:#222222">() {</span></span> <span class="line"><span style="color:#D43324"> if</span><span style="color:#222222"> (_i != </span><span style="color:#11796D">null</span><span style="color:#222222">) {</span></span> <span class="line"><span style="color:#0468D7"> int</span><span style="color:#222222"> i = _i; </span><span style="color:#6E6E70">// ERROR</span></span> <span class="line"><span style="color:#222222"> }</span></span> <span class="line"><span style="color:#222222"> }</span></span> <span class="line"><span style="color:#222222">}</span></span></code></pre></div></div><p><strong>Message:</strong></p><div class="code-block-wrapper language-plaintext"><div class="code-block-body"><pre class="shiki dash-light" tabindex="0"><code><span class="line"><span>'_i' refers to a field. It couldn't be promoted because field promotion is only available in Dart 3.2 and above.</span></span></code></pre></div></div><p><strong>Solution:</strong></p><p>Ensure your library isn't using a <a href="/guides/language/evolution#language-versioning">language version</a> earlier than 3.2. Check the top of your file for an outdated <code>// @dart=version</code> comment, or your <code>pubspec.yaml</code> for an outdated <a href="/tools/pub/pubspec#sdk-constraints">SDK constraint lower-bound</a>.</p><div class="header-wrapper"><h2 id="property">Only local variables can be promoted (before Dart 3.2)</h2><a class="heading-link" href="#property" aria-label="Link to 'Only local variables can be promoted (before Dart 3.2)' section">#</a></div><p><strong>The cause:</strong> You're trying to promote a property, but only local variables can be promoted in Dart versions earlier than 3.2, and you are using a version earlier than 3.2.</p><p><strong>Example:</strong></p><div class="code-block-wrapper language-dart"><div class="code-block-body has-tag tag-bad"><span class="code-block-tag">bad</span><span class="code-block-language" title="Language dart">dart</span><pre class="shiki dash-light" tabindex="0"><code><span class="line"><span style="color:#D43324">class</span><span style="color:#0468D7"> C</span><span style="color:#222222"> {</span></span> <span class="line"><span style="color:#0468D7"> int</span><span style="color:#222222">? i;</span></span> <span class="line"><span style="color:#D43324"> void</span><span style="color:#6200EE"> f</span><span style="color:#222222">() {</span></span> <span class="line"><span style="color:#D43324"> if</span><span style="color:#222222"> (i == </span><span style="color:#11796D">null</span><span style="color:#222222">) </span><span style="color:#D43324">return</span><span style="color:#222222">;</span></span> <span class="line"><span style="color:#6200EE"> print</span><span style="color:#222222">(i.isEven); </span><span style="color:#6E6E70">// ERROR</span></span> <span class="line"><span style="color:#222222"> }</span></span> <span class="line"><span style="color:#222222">}</span></span></code></pre></div></div><p><strong>Message:</strong></p><div class="code-block-wrapper language-plaintext"><div class="code-block-body"><pre class="shiki dash-light" tabindex="0"><code><span class="line"><span>'i' refers to a property so it couldn't be promoted.</span></span></code></pre></div></div><p><strong>Solution:</strong></p><p>If you are using Dart 3.1 or earlier, <a href="/get-dart">upgrade to 3.2 or later</a>.</p><p>If you need to keep using an older version, read <a href="#other-causes-and-workarounds">Other causes and workarounds</a></p><div class="header-wrapper"><h2 id="other-causes-and-workarounds">Other causes and workarounds</h2><a class="heading-link" href="#other-causes-and-workarounds" aria-label="Link to 'Other causes and workarounds' section">#</a></div><p>The remaining examples on this page document reasons for promotion failures unrelated to version inconsistencies, for both field and local variable failures, with examples and workarounds.</p><p>In general, the usual fixes for promotion failures are one or more of the following:</p><ul><li>Assign the property's value to a local variable with the non-nullable type you need.</li><li>Add an explicit null check (for example, <code>i == null</code>).</li><li>Use <code>!</code> or <code>as</code> as a <a href="#redundant-check">redundant check</a> if you're sure an expression can't be <code>null</code>.</li></ul><p>Here's an example of creating a local variable (which can be named <code>i</code>) that holds the value of <code>i</code>:</p> <?code-excerpt "non_promotion/lib/non_promotion.dart (property-copy)" replace="/final.*/[!$&!]/g"?> <div class="code-block-wrapper language-dart"><div class="code-block-body has-tag tag-good"><span class="code-block-tag">good</span><span class="code-block-language" title="Language dart">dart</span><pre class="shiki dash-light" tabindex="0"><code><span class="line"><span style="color:#D43324">class</span><span style="color:#0468D7"> C</span><span style="color:#222222"> {</span></span> <span class="line"><span style="color:#0468D7"> int</span><span style="color:#222222">? i;</span></span> <span class="line"><span style="color:#D43324"> void</span><span style="color:#6200EE"> f</span><span style="color:#222222">() {</span></span> <span class="line"><span style="color:#D43324"> </span><mark class="highlight"><span style="color:#D43324">final</span><span style="color:#222222"> i = </span><span style="color:#D43324">this</span><span style="color:#222222">.i;</span></mark></span> <span class="line"><span style="color:#D43324"> if</span><span style="color:#222222"> (i == </span><span style="color:#11796D">null</span><span style="color:#222222">) </span><span style="color:#D43324">return</span><span style="color:#222222">;</span></span> <span class="line"><span style="color:#6200EE"> print</span><span style="color:#222222">(i.isEven);</span></span> <span class="line"><span style="color:#222222"> }</span></span> <span class="line"><span style="color:#222222">}</span></span></code></pre></div></div><p>This example features an instance field, but it could instead use an instance getter, a static field or getter, a top-level variable or getter, or <a href="#this"><code>this</code></a>.</p><aside class="alert alert-success"><div class="alert-header"><i class="material-symbols" aria-hidden="true">lightbulb</i> <span>Tip</span></div><div class="alert-content"><p>When creating a local variable to hold a field's value, <strong>make the variable <code>final</code></strong>. That way, you can't accidentally update the local variable when you intend to update the field.</p></div></aside><p>And here's an example of using <code>i!</code>:</p> <?code-excerpt "non_promotion/lib/non_promotion.dart (property-bang)" replace="/!/[!!!]/g"?> <div class="code-block-wrapper language-dart"><div class="code-block-body has-tag tag-good"><span class="code-block-tag">good</span><span class="code-block-language" title="Language dart">dart</span><pre class="shiki dash-light" tabindex="0"><code><span class="line"><span style="color:#6200EE">print</span><span style="color:#222222">(i</span><mark class="highlight"><span style="color:#222222">!</span></mark><span style="color:#222222">.isEven);</span></span></code></pre></div></div><p><a id="redundant-check" aria-hidden="true"></a></p><aside class="alert alert-info"><div class="alert-header"><i class="material-symbols" aria-hidden="true">info</i> <span>Note</span></div><div class="alert-content"><p>You can work around all of these non-promotion examples by adding a <em>redundant check</em>鈥攃ode that confirms a condition that's already been checked. If the promotion that's failing is a null check, use <code>!</code>; if it's a type check, you can use <code>as</code>.</p><p>Redundant checks are an easy but error-prone solution to type promotion failures. Because they overrule the compiler, they can lead to mistakes in a way that other solutions don't.</p><p>It's up to you whether to do the extra work to get types to promote (giving you confidence that the code is correct) or to do a redundant check (which might introduce a bug if your reasoning is wrong).</p></div></aside><div class="header-wrapper"><h3 id="this">Can't promote <code>this</code></h3><a class="heading-link" href="#this" aria-label="Link to 'Can't promote this' section">#</a></div><p><strong>The cause:</strong> You're trying to promote <code>this</code>, but type promotion for <code>this</code> is not yet supported.</p><p>One common <code>this</code> promotion scenario is when writing <a href="/language/extension-methods">extension methods</a>. If the <a href="/language/extension-methods#implementing-extension-methods"><code>on</code> type</a> of the extension method is a nullable type, you'd want to do a null check to see whether <code>this</code> is <code>null</code>:</p><p><strong>Example:</strong></p><div class="code-block-wrapper language-dart"><div class="code-block-body has-tag tag-bad"><span class="code-block-tag">bad</span><span class="code-block-language" title="Language dart">dart</span><pre class="shiki dash-light" tabindex="0"><code><span class="line"><span style="color:#D43324">extension</span><span style="color:#D43324"> on</span><span style="color:#0468D7"> int</span><span style="color:#222222">? {</span></span> <span class="line"><span style="color:#0468D7"> int</span><span style="color:#D43324"> get</span><span style="color:#222222"> valueOrZero {</span></span> <span class="line"><span style="color:#D43324"> return</span><span style="color:#D43324"> this</span><span style="color:#222222"> == </span><span style="color:#11796D">null</span><span style="color:#222222"> ? </span><span style="color:#11796D">0</span><span style="color:#222222"> : </span><span style="color:#D43324">this</span><span style="color:#222222">; </span><span style="color:#6E6E70">// ERROR</span></span> <span class="line"><span style="color:#222222"> }</span></span> <span class="line"><span style="color:#222222">}</span></span></code></pre></div></div><p><strong>Message:</strong></p><div class="code-block-wrapper language-plaintext"><div class="code-block-body"><pre class="shiki dash-light" tabindex="0"><code><span class="line"><span>`this` can't be promoted.</span></span></code></pre></div></div><p><strong>Solution:</strong></p><p>Create a local variable to hold the value of <code>this</code>, then perform the null check.</p> <?code-excerpt "non_promotion/lib/non_promotion.dart (this)" replace="/final.*/[!$&!]/g"?> <div class="code-block-wrapper language-dart"><div class="code-block-body has-tag tag-good"><span class="code-block-tag">good</span><span class="code-block-language" title="Language dart">dart</span><pre class="shiki dash-light" tabindex="0"><code><span class="line"><span style="color:#D43324">extension</span><span style="color:#D43324"> on</span><span style="color:#0468D7"> int</span><span style="color:#222222">? {</span></span> <span class="line"><span style="color:#0468D7"> int</span><span style="color:#D43324"> get</span><span style="color:#222222"> valueOrZero {</span></span> <span class="line"><span style="color:#D43324"> </span><mark class="highlight"><span style="color:#D43324">final</span><span style="color:#222222"> self = </span><span style="color:#D43324">this</span><span style="color:#222222">;</span></mark></span> <span class="line"><span style="color:#D43324"> return</span><span style="color:#222222"> self == </span><span style="color:#11796D">null</span><span style="color:#222222"> ? </span><span style="color:#11796D">0</span><span style="color:#222222"> : self;</span></span> <span class="line"><span style="color:#222222"> }</span></span> <span class="line"><span style="color:#222222">}</span></span></code></pre></div></div><div class="header-wrapper"><h3 id="private">Only private fields can be promoted</h3><a class="heading-link" href="#private" aria-label="Link to 'Only private fields can be promoted' section">#</a></div><p><strong>The cause:</strong> You're trying to promote a field, but the field is not private.</p><p>It's possible for other libraries in your program to override public fields with a getter. Because <a href="#not-field">getters might not return a stable value</a>, and the compiler can't know what other libraries are doing, non-private fields cannot be promoted.</p><p><strong>Example:</strong></p><div class="code-block-wrapper language-dart"><div class="code-block-body has-tag tag-bad"><span class="code-block-tag">bad</span><span class="code-block-language" title="Language dart">dart</span><pre class="shiki dash-light" tabindex="0"><code><span class="line"><span style="color:#D43324">class</span><span style="color:#0468D7"> Example</span><span style="color:#222222"> {</span></span> <span class="line"><span style="color:#D43324"> final</span><span style="color:#0468D7"> int</span><span style="color:#222222">? value;</span></span> <span class="line"><span style="color:#0468D7"> Example</span><span style="color:#222222">(</span><span style="color:#D43324">this</span><span style="color:#222222">.value);</span></span> <span class="line"><span style="color:#222222">}</span></span> <span class="line"></span> <span class="line"><span style="color:#D43324">void</span><span style="color:#6200EE"> test</span><span style="color:#222222">(</span><span style="color:#0468D7">Example</span><span style="color:#222222"> x) {</span></span> <span class="line"><span style="color:#D43324"> if</span><span style="color:#222222"> (x.value != </span><span style="color:#11796D">null</span><span style="color:#222222">) {</span></span> <span class="line"><span style="color:#6200EE"> print</span><span style="color:#222222">(x.value + </span><span style="color:#11796D">1</span><span style="color:#222222">); </span><span style="color:#6E6E70">// ERROR</span></span> <span class="line"><span style="color:#222222"> }</span></span> <span class="line"><span style="color:#222222">}</span></span></code></pre></div></div><p><strong>Message:</strong></p><div class="code-block-wrapper language-plaintext"><div class="code-block-body"><pre class="shiki dash-light" tabindex="0"><code><span class="line"><span>'value' refers to a public property so it couldn't be promoted.</span></span></code></pre></div></div><p><strong>Solution:</strong></p><p>Making the field private lets the compiler be sure that no outside libraries could possibly override its value, so it's safe to promote.</p> <?code-excerpt "non_promotion/lib/non_promotion.dart (private)" replace="/_val/_value/g; /_value;/[!_value!];/g; /PrivateFieldExample/Example/g;"?> <div class="code-block-wrapper language-dart"><div class="code-block-body has-tag tag-good"><span class="code-block-tag">good</span><span class="code-block-language" title="Language dart">dart</span><pre class="shiki dash-light" tabindex="0"><code><span class="line"><span style="color:#D43324">class</span><span style="color:#0468D7"> Example</span><span style="color:#222222"> {</span></span> <span class="line"><span style="color:#D43324"> final</span><span style="color:#0468D7"> int</span><span style="color:#222222">? </span><mark class="highlight"><span style="color:#222222">_value</span></mark><span style="color:#222222">;</span></span> <span class="line"><span style="color:#0468D7"> Example</span><span style="color:#222222">(</span><span style="color:#D43324">this</span><span style="color:#222222">._value);</span></span> <span class="line"><span style="color:#222222">}</span></span> <span class="line"></span> <span class="line"><span style="color:#D43324">void</span><span style="color:#6200EE"> test</span><span style="color:#222222">(</span><span style="color:#0468D7">Example</span><span style="color:#222222"> x) {</span></span> <span class="line"><span style="color:#D43324"> if</span><span style="color:#222222"> (x._value != </span><span style="color:#11796D">null</span><span style="color:#222222">) {</span></span> <span class="line"><span style="color:#6200EE"> print</span><span style="color:#222222">(x._value + </span><span style="color:#11796D">1</span><span style="color:#222222">);</span></span> <span class="line"><span style="color:#222222"> }</span></span> <span class="line"><span style="color:#222222">}</span></span></code></pre></div></div><div class="header-wrapper"><h3 id="final">Only final fields can be promoted</h3><a class="heading-link" href="#final" aria-label="Link to 'Only final fields can be promoted' section">#</a></div><p><strong>The cause:</strong> You're trying to promote a field, but the field is not final.</p><p>To the compiler, non-final fields could, in principle, be modified any time between the time they're tested and the time they're used. So it's not safe for the compiler to promote a non-final nullable type to a non-nullable type.</p><p><strong>Example:</strong></p><div class="code-block-wrapper language-dart"><div class="code-block-body has-tag tag-bad"><span class="code-block-tag">bad</span><span class="code-block-language" title="Language dart">dart</span><pre class="shiki dash-light" tabindex="0"><code><span class="line"><span style="color:#D43324">class</span><span style="color:#0468D7"> Example</span><span style="color:#222222"> {</span></span> <span class="line"><span style="color:#0468D7"> int</span><span style="color:#222222">? _mutablePrivateField;</span></span> <span class="line"><span style="color:#0468D7"> Example</span><span style="color:#222222">(</span><span style="color:#D43324">this</span><span style="color:#222222">._mutablePrivateField);</span></span> <span class="line"></span> <span class="line"><span style="color:#D43324"> void</span><span style="color:#6200EE"> f</span><span style="color:#222222">() {</span></span> <span class="line"><span style="color:#D43324"> if</span><span style="color:#222222"> (_mutablePrivateField != </span><span style="color:#11796D">null</span><span style="color:#222222">) {</span></span> <span class="line"><span style="color:#0468D7"> int</span><span style="color:#222222"> i = _mutablePrivateField; </span><span style="color:#6E6E70">// ERROR</span></span> <span class="line"><span style="color:#222222"> }</span></span> <span class="line"><span style="color:#222222"> }</span></span> <span class="line"><span style="color:#222222">}</span></span></code></pre></div></div><p><strong>Message:</strong></p><div class="code-block-wrapper language-plaintext"><div class="code-block-body"><pre class="shiki dash-light" tabindex="0"><code><span class="line"><span>'_mutablePrivateField' refers to a non-final field so it couldn't be promoted.</span></span></code></pre></div></div><p><strong>Solution:</strong></p><p>Make the field <code>final</code>:</p> <?code-excerpt "non_promotion/lib/non_promotion.dart (final)" replace="/final/[!$&!]/g; /FinalExample/Example/g;"?> <div class="code-block-wrapper language-dart"><div class="code-block-body has-tag tag-good"><span class="code-block-tag">good</span><span class="code-block-language" title="Language dart">dart</span><pre class="shiki dash-light" tabindex="0"><code><span class="line"><span style="color:#D43324">class</span><span style="color:#0468D7"> Example</span><span style="color:#222222"> {</span></span> <span class="line"><span style="color:#D43324"> </span><mark class="highlight"><span style="color:#D43324">final</span></mark><span style="color:#0468D7"> int</span><span style="color:#222222">? _immutablePrivateField;</span></span> <span class="line"><span style="color:#0468D7"> Example</span><span style="color:#222222">(</span><span style="color:#D43324">this</span><span style="color:#222222">._immutablePrivateField);</span></span> <span class="line"></span> <span class="line"><span style="color:#D43324"> void</span><span style="color:#6200EE"> f</span><span style="color:#222222">() {</span></span> <span class="line"><span style="color:#D43324"> if</span><span style="color:#222222"> (_immutablePrivateField != </span><span style="color:#11796D">null</span><span style="color:#222222">) {</span></span> <span class="line"><span style="color:#0468D7"> int</span><span style="color:#222222"> i = _immutablePrivateField; </span><span style="color:#6E6E70">// OK</span></span> <span class="line"><span style="color:#222222"> }</span></span> <span class="line"><span style="color:#222222"> }</span></span> <span class="line"><span style="color:#222222">}</span></span></code></pre></div></div><div class="header-wrapper"><h3 id="not-field">Getters can't be promoted</h3><a class="heading-link" href="#not-field" aria-label="Link to 'Getters can't be promoted' section">#</a></div><p><strong>The cause:</strong> You're trying to promote a getter, but only instance <em>fields</em> can be promoted, not instance getters.</p><p>The compiler has no way to guarantee that a getter returns the same result every time. Because their stability can't be confirmed, getters are not safe to promote.</p><p><strong>Example:</strong></p><div class="code-block-wrapper language-dart"><div class="code-block-body has-tag tag-bad"><span class="code-block-tag">bad</span><span class="code-block-language" title="Language dart">dart</span><pre class="shiki dash-light" tabindex="0"><code><span class="line"><span style="color:#D43324">import</span><span style="color:#11796D"> 'dart:math'</span><span style="color:#222222">;</span></span> <span class="line"></span> <span class="line"><span style="color:#D43324">abstract</span><span style="color:#D43324"> class</span><span style="color:#0468D7"> Example</span><span style="color:#222222"> {</span></span> <span class="line"><span style="color:#0468D7"> int</span><span style="color:#222222">? </span><span style="color:#D43324">get</span><span style="color:#222222"> _value => </span><span style="color:#0468D7">Random</span><span style="color:#222222">().</span><span style="color:#6200EE">nextBool</span><span style="color:#222222">() ? </span><span style="color:#11796D">123</span><span style="color:#222222"> : </span><span style="color:#11796D">null</span><span style="color:#222222">;</span></span> <span class="line"><span style="color:#222222">}</span></span> <span class="line"></span> <span class="line"><span style="color:#D43324">void</span><span style="color:#6200EE"> f</span><span style="color:#222222">(</span><span style="color:#0468D7">Example</span><span style="color:#222222"> x) {</span></span> <span class="line"><span style="color:#D43324"> if</span><span style="color:#222222"> (x._value != </span><span style="color:#11796D">null</span><span style="color:#222222">) {</span></span> <span class="line"><span style="color:#6200EE"> print</span><span style="color:#222222">(x._value.isEven); </span><span style="color:#6E6E70">// ERROR</span></span> <span class="line"><span style="color:#222222"> }</span></span> <span class="line"><span style="color:#222222">}</span></span></code></pre></div></div><p><strong>Message:</strong></p><div class="code-block-wrapper language-plaintext"><div class="code-block-body"><pre class="shiki dash-light" tabindex="0"><code><span class="line"><span>'_value' refers to a getter so it couldn't be promoted.</span></span></code></pre></div></div><p><strong>Solution:</strong></p><p>Assign the getter to a local variable:</p> <?code-excerpt "non_promotion/lib/non_promotion.dart (not-field)" plaster="" replace="/final.*/[!$&!]/g; /NotFieldExample/Example/g;"?> <div class="code-block-wrapper language-dart"><div class="code-block-body has-tag tag-good"><span class="code-block-tag">good</span><span class="code-block-language" title="Language dart">dart</span><pre class="shiki dash-light" tabindex="0"><code><span class="line"><span style="color:#D43324">import</span><span style="color:#11796D"> 'dart:math'</span><span style="color:#222222">;</span></span> <span class="line"></span> <span class="line"><span style="color:#D43324">abstract</span><span style="color:#D43324"> class</span><span style="color:#0468D7"> Example</span><span style="color:#222222"> {</span></span> <span class="line"><span style="color:#0468D7"> int</span><span style="color:#222222">? </span><span style="color:#D43324">get</span><span style="color:#222222"> _value => </span><span style="color:#0468D7">Random</span><span style="color:#222222">().</span><span style="color:#6200EE">nextBool</span><span style="color:#222222">() ? </span><span style="color:#11796D">123</span><span style="color:#222222"> : </span><span style="color:#11796D">null</span><span style="color:#222222">;</span></span> <span class="line"><span style="color:#222222">}</span></span> <span class="line"></span> <span class="line"><span style="color:#D43324">void</span><span style="color:#6200EE"> f</span><span style="color:#222222">(</span><span style="color:#0468D7">Example</span><span style="color:#222222"> x) {</span></span> <span class="line"><span style="color:#D43324"> </span><mark class="highlight"><span style="color:#D43324">final</span><span style="color:#222222"> value = x._value;</span></mark></span> <span class="line"><span style="color:#D43324"> if</span><span style="color:#222222"> (value != </span><span style="color:#11796D">null</span><span style="color:#222222">) {</span></span> <span class="line"><span style="color:#6200EE"> print</span><span style="color:#222222">(value.isEven); </span><span style="color:#6E6E70">// OK</span></span> <span class="line"><span style="color:#222222"> }</span></span> <span class="line"><span style="color:#222222">}</span></span></code></pre></div></div><aside class="alert alert-info"><div class="alert-header"><i class="material-symbols" aria-hidden="true">info</i> <span>Note</span></div><div class="alert-content"><p>Flow analysis considers <code>abstract</code> getters stable enough to allow type promotion, as long as there are no conflicting declarations.</p></div></aside><div class="header-wrapper"><h3 id="external">External fields can't be promoted</h3><a class="heading-link" href="#external" aria-label="Link to 'External fields can't be promoted' section">#</a></div><p><strong>The cause:</strong> You're trying to promote a field, but the field is marked <code>external</code>.</p><p>External fields don't promote because they are essentially external getters; their implementation is code from outside of Dart, so there's no guarantee for the compiler that an external field will return the same value each time it's called.</p><p><strong>Example:</strong></p><div class="code-block-wrapper language-dart"><div class="code-block-body has-tag tag-bad"><span class="code-block-tag">bad</span><span class="code-block-language" title="Language dart">dart</span><pre class="shiki dash-light" tabindex="0"><code><span class="line"><span style="color:#D43324">class</span><span style="color:#0468D7"> Example</span><span style="color:#222222"> {</span></span> <span class="line"><span style="color:#D43324"> external</span><span style="color:#D43324"> final</span><span style="color:#0468D7"> int</span><span style="color:#222222">? _externalField;</span></span> <span class="line"></span> <span class="line"><span style="color:#D43324"> void</span><span style="color:#6200EE"> f</span><span style="color:#222222">() {</span></span> <span class="line"><span style="color:#D43324"> if</span><span style="color:#222222"> (_externalField != </span><span style="color:#11796D">null</span><span style="color:#222222">) {</span></span> <span class="line"><span style="color:#6200EE"> print</span><span style="color:#222222">(_externalField.isEven); </span><span style="color:#6E6E70">// ERROR</span></span> <span class="line"><span style="color:#222222"> }</span></span> <span class="line"><span style="color:#222222"> }</span></span> <span class="line"><span style="color:#222222">}</span></span></code></pre></div></div><p><strong>Message:</strong></p><div class="code-block-wrapper language-plaintext"><div class="code-block-body"><pre class="shiki dash-light" tabindex="0"><code><span class="line"><span>'_externalField' refers to an external field so it couldn't be promoted.</span></span></code></pre></div></div><p><strong>Solution:</strong></p><p>Assign the external field's value to a local variable:</p> <?code-excerpt "non_promotion/lib/non_promotion.dart (external)" replace="/final i =.*/[!$&!]/g; /ExternalExample/Example/g;"?> <div class="code-block-wrapper language-dart"><div class="code-block-body has-tag tag-good"><span class="code-block-tag">good</span><span class="code-block-language" title="Language dart">dart</span><pre class="shiki dash-light" tabindex="0"><code><span class="line"><span style="color:#D43324">class</span><span style="color:#0468D7"> Example</span><span style="color:#222222"> {</span></span> <span class="line"><span style="color:#D43324"> external</span><span style="color:#D43324"> final</span><span style="color:#0468D7"> int</span><span style="color:#222222">? _externalField;</span></span> <span class="line"></span> <span class="line"><span style="color:#D43324"> void</span><span style="color:#6200EE"> f</span><span style="color:#222222">() {</span></span> <span class="line"><span style="color:#D43324"> </span><mark class="highlight"><span style="color:#D43324">final</span><span style="color:#222222"> i = _externalField;</span></mark></span> <span class="line"><span style="color:#D43324"> if</span><span style="color:#222222"> (i != </span><span style="color:#11796D">null</span><span style="color:#222222">) {</span></span> <span class="line"><span style="color:#6200EE"> print</span><span style="color:#222222">(i.isEven); </span><span style="color:#6E6E70">// OK</span></span> <span class="line"><span style="color:#222222"> }</span></span> <span class="line"><span style="color:#222222"> }</span></span> <span class="line"><span style="color:#222222">}</span></span></code></pre></div></div><div class="header-wrapper"><h3 id="getter-name">Conflict with getter elsewhere in library</h3><a class="heading-link" href="#getter-name" aria-label="Link to 'Conflict with getter elsewhere in library' section">#</a></div><p><strong>The cause:</strong> You're trying to promote a field, but another class in the same library contains a concrete getter with the same name.</p><p><strong>Example:</strong></p><div class="code-block-wrapper language-dart"><div class="code-block-body has-tag tag-bad"><span class="code-block-tag">bad</span><span class="code-block-language" title="Language dart">dart</span><pre class="shiki dash-light" tabindex="0"><code><span class="line"><span style="color:#D43324">import</span><span style="color:#11796D"> 'dart:math'</span><span style="color:#222222">;</span></span> <span class="line"></span> <span class="line"><span style="color:#D43324">class</span><span style="color:#0468D7"> Example</span><span style="color:#222222"> {</span></span> <span class="line"><span style="color:#D43324"> final</span><span style="color:#0468D7"> int</span><span style="color:#222222">? _overridden;</span></span> <span class="line"><span style="color:#0468D7"> Example</span><span style="color:#222222">(</span><span style="color:#D43324">this</span><span style="color:#222222">._overridden);</span></span> <span class="line"><span style="color:#222222">}</span></span> <span class="line"></span> <span class="line"><span style="color:#D43324">class</span><span style="color:#0468D7"> Override</span><span style="color:#D43324"> implements</span><span style="color:#0468D7"> Example</span><span style="color:#222222"> {</span></span> <span class="line"><span style="color:#D43324"> @override</span></span> <span class="line"><span style="color:#0468D7"> int</span><span style="color:#222222">? </span><span style="color:#D43324">get</span><span style="color:#222222"> _overridden => </span><span style="color:#0468D7">Random</span><span style="color:#222222">().</span><span style="color:#6200EE">nextBool</span><span style="color:#222222">() ? </span><span style="color:#11796D">1</span><span style="color:#222222"> : </span><span style="color:#11796D">null</span><span style="color:#222222">;</span></span> <span class="line"><span style="color:#222222">}</span></span> <span class="line"></span> <span class="line"><span style="color:#D43324">void</span><span style="color:#6200EE"> testParity</span><span style="color:#222222">(</span><span style="color:#0468D7">Example</span><span style="color:#222222"> x) {</span></span> <span class="line"><span style="color:#D43324"> if</span><span style="color:#222222"> (x._overridden != </span><span style="color:#11796D">null</span><span style="color:#222222">) {</span></span> <span class="line"><span style="color:#6200EE"> print</span><span style="color:#222222">(x._overridden.isEven); </span><span style="color:#6E6E70">// ERROR</span></span> <span class="line"><span style="color:#222222"> }</span></span> <span class="line"><span style="color:#222222">}</span></span></code></pre></div></div><p><strong>Message:</strong></p><div class="code-block-wrapper language-plaintext"><div class="code-block-body"><pre class="shiki dash-light" tabindex="0"><code><span class="line"><span>'_overriden' couldn't be promoted because there is a conflicting getter in class 'Override'.</span></span></code></pre></div></div><p><strong>Solution</strong>:</p><p>If the getter and field are related and need to share their name (like when one of them overrides the other, as in the example above), then you can enable type promotion by assigning the value to a local variable:</p> <?code-excerpt "non_promotion/lib/non_promotion.dart (conflicting-getter)" plaster="" replace="/final i =.*/[!$&!]/g; /GetterExample/Example/g;"?> <div class="code-block-wrapper language-dart"><div class="code-block-body has-tag tag-good"><span class="code-block-tag">good</span><span class="code-block-language" title="Language dart">dart</span><pre class="shiki dash-light" tabindex="0"><code><span class="line"><span style="color:#D43324">import</span><span style="color:#11796D"> 'dart:math'</span><span style="color:#222222">;</span></span> <span class="line"></span> <span class="line"><span style="color:#D43324">class</span><span style="color:#0468D7"> Example</span><span style="color:#222222"> {</span></span> <span class="line"><span style="color:#D43324"> final</span><span style="color:#0468D7"> int</span><span style="color:#222222">? _overridden;</span></span> <span class="line"><span style="color:#0468D7"> Example</span><span style="color:#222222">(</span><span style="color:#D43324">this</span><span style="color:#222222">._overridden);</span></span> <span class="line"><span style="color:#222222">}</span></span> <span class="line"></span> <span class="line"><span style="color:#D43324">class</span><span style="color:#0468D7"> Override</span><span style="color:#D43324"> implements</span><span style="color:#0468D7"> Example</span><span style="color:#222222"> {</span></span> <span class="line"><span style="color:#D43324"> @override</span></span> <span class="line"><span style="color:#0468D7"> int</span><span style="color:#222222">? </span><span style="color:#D43324">get</span><span style="color:#222222"> _overridden => </span><span style="color:#0468D7">Random</span><span style="color:#222222">().</span><span style="color:#6200EE">nextBool</span><span style="color:#222222">() ? </span><span style="color:#11796D">1</span><span style="color:#222222"> : </span><span style="color:#11796D">null</span><span style="color:#222222">;</span></span> <span class="line"><span style="color:#222222">}</span></span> <span class="line"></span> <span class="line"><span style="color:#D43324">void</span><span style="color:#6200EE"> testParity</span><span style="color:#222222">(</span><span style="color:#0468D7">Example</span><span style="color:#222222"> x) {</span></span> <span class="line"><span style="color:#D43324"> </span><mark class="highlight"><span style="color:#D43324">final</span><span style="color:#222222"> i = x._overridden;</span></mark></span> <span class="line"><span style="color:#D43324"> if</span><span style="color:#222222"> (i != </span><span style="color:#11796D">null</span><span style="color:#222222">) {</span></span> <span class="line"><span style="color:#6200EE"> print</span><span style="color:#222222">(i.isEven); </span><span style="color:#6E6E70">// OK</span></span> <span class="line"><span style="color:#222222"> }</span></span> <span class="line"><span style="color:#222222">}</span></span></code></pre></div></div><div class="header-wrapper"><h4 id="note-about-unrelated-classes">Note about unrelated classes</h4><a class="heading-link" href="#note-about-unrelated-classes" aria-label="Link to 'Note about unrelated classes' section">#</a></div><p>Note that in the above example it's clear why it's unsafe to promote the field <code>_overridden</code>: because there's an override relationship between the field and the getter. However, a conflicting getter will prevent field promotion even if the classes are unrelated. For example:</p><div class="code-block-wrapper language-dart"><div class="code-block-body has-tag tag-bad"><span class="code-block-tag">bad</span><span class="code-block-language" title="Language dart">dart</span><pre class="shiki dash-light" tabindex="0"><code><span class="line"><span style="color:#D43324">import</span><span style="color:#11796D"> 'dart:math'</span><span style="color:#222222">;</span></span> <span class="line"></span> <span class="line"><span style="color:#D43324">class</span><span style="color:#0468D7"> Example</span><span style="color:#222222"> {</span></span> <span class="line"><span style="color:#D43324"> final</span><span style="color:#0468D7"> int</span><span style="color:#222222">? _i;</span></span> <span class="line"><span style="color:#0468D7"> Example</span><span style="color:#222222">(</span><span style="color:#D43324">this</span><span style="color:#222222">._i);</span></span> <span class="line"><span style="color:#222222">}</span></span> <span class="line"></span> <span class="line"><span style="color:#D43324">class</span><span style="color:#0468D7"> Unrelated</span><span style="color:#222222"> {</span></span> <span class="line"><span style="color:#0468D7"> int</span><span style="color:#222222">? </span><span style="color:#D43324">get</span><span style="color:#222222"> _i => </span><span style="color:#0468D7">Random</span><span style="color:#222222">().</span><span style="color:#6200EE">nextBool</span><span style="color:#222222">() ? </span><span style="color:#11796D">1</span><span style="color:#222222"> : </span><span style="color:#11796D">null</span><span style="color:#222222">;</span></span> <span class="line"><span style="color:#222222">}</span></span> <span class="line"></span> <span class="line"><span style="color:#D43324">void</span><span style="color:#6200EE"> f</span><span style="color:#222222">(</span><span style="color:#0468D7">Example</span><span style="color:#222222"> x) {</span></span> <span class="line"><span style="color:#D43324"> if</span><span style="color:#222222"> (x._i != </span><span style="color:#11796D">null</span><span style="color:#222222">) {</span></span> <span class="line"><span style="color:#0468D7"> int</span><span style="color:#222222"> i = x._i; </span><span style="color:#6E6E70">// ERROR</span></span> <span class="line"><span style="color:#222222"> }</span></span> <span class="line"><span style="color:#222222">}</span></span></code></pre></div></div><p>Another library might contain a class that combines the two unrelated classes together into the same class hierarchy, which would cause the reference in function <code>f</code> to <code>x._i</code> to get dispatched to <code>Unrelated._i</code>. For example:</p><div class="code-block-wrapper language-dart"><div class="code-block-body has-tag tag-bad"><span class="code-block-tag">bad</span><span class="code-block-language" title="Language dart">dart</span><pre class="shiki dash-light" tabindex="0"><code><span class="line"><span style="color:#D43324">class</span><span style="color:#0468D7"> Surprise</span><span style="color:#D43324"> extends</span><span style="color:#0468D7"> Unrelated</span><span style="color:#D43324"> implements</span><span style="color:#0468D7"> Example</span><span style="color:#222222"> {}</span></span> <span class="line"></span> <span class="line"><span style="color:#D43324">void</span><span style="color:#6200EE"> main</span><span style="color:#222222">() {</span></span> <span class="line"><span style="color:#6200EE"> f</span><span style="color:#222222">(</span><span style="color:#0468D7">Surprise</span><span style="color:#222222">());</span></span> <span class="line"><span style="color:#222222">}</span></span></code></pre></div></div><p><strong>Solution:</strong></p><p>If the field and the conflicting entity are truly unrelated, you can work around the problem by giving them different names:</p> <?code-excerpt "non_promotion/lib/non_promotion.dart (unrelated)" replace="/get _j/[!$&!]/g; /UnrelatedExample/Example/g; /f2/f/g;"?> <div class="code-block-wrapper language-dart"><div class="code-block-body has-tag tag-good"><span class="code-block-tag">good</span><span class="code-block-language" title="Language dart">dart</span><pre class="shiki dash-light" tabindex="0"><code><span class="line"><span style="color:#D43324">class</span><span style="color:#0468D7"> Example</span><span style="color:#222222"> {</span></span> <span class="line"><span style="color:#D43324"> final</span><span style="color:#0468D7"> int</span><span style="color:#222222">? _i;</span></span> <span class="line"><span style="color:#0468D7"> Example</span><span style="color:#222222">(</span><span style="color:#D43324">this</span><span style="color:#222222">._i);</span></span> <span class="line"><span style="color:#222222">}</span></span> <span class="line"></span> <span class="line"><span style="color:#D43324">class</span><span style="color:#0468D7"> Unrelated</span><span style="color:#222222"> {</span></span> <span class="line"><span style="color:#0468D7"> int</span><span style="color:#222222">? </span><mark class="highlight"><span style="color:#D43324">get</span><span style="color:#222222"> _j</span></mark><span style="color:#222222"> => </span><span style="color:#0468D7">Random</span><span style="color:#222222">().</span><span style="color:#6200EE">nextBool</span><span style="color:#222222">() ? </span><span style="color:#11796D">1</span><span style="color:#222222"> : </span><span style="color:#11796D">null</span><span style="color:#222222">;</span></span> <span class="line"><span style="color:#222222">}</span></span> <span class="line"></span> <span class="line"><span style="color:#D43324">void</span><span style="color:#6200EE"> f</span><span style="color:#222222">(</span><span style="color:#0468D7">Example</span><span style="color:#222222"> x) {</span></span> <span class="line"><span style="color:#D43324"> if</span><span style="color:#222222"> (x._i != </span><span style="color:#11796D">null</span><span style="color:#222222">) {</span></span> <span class="line"><span style="color:#0468D7"> int</span><span style="color:#222222"> i = x._i; </span><span style="color:#6E6E70">// OK</span></span> <span class="line"><span style="color:#222222"> }</span></span> <span class="line"><span style="color:#222222">}</span></span></code></pre></div></div><div class="header-wrapper"><h3 id="field-name">Conflict with non-promotable field elsewhere in library</h3><a class="heading-link" href="#field-name" aria-label="Link to 'Conflict with non-promotable field elsewhere in library' section">#</a></div><p><strong>The cause:</strong> You're trying to promote a field, but another class in the same library contains a field with the same name that isn't promotable (for any of the other reasons listed on this page).</p><p><strong>Example:</strong></p><div class="code-block-wrapper language-dart"><div class="code-block-body has-tag tag-bad"><span class="code-block-tag">bad</span><span class="code-block-language" title="Language dart">dart</span><pre class="shiki dash-light" tabindex="0"><code><span class="line"><span style="color:#D43324">class</span><span style="color:#0468D7"> Example</span><span style="color:#222222"> {</span></span> <span class="line"><span style="color:#D43324"> final</span><span style="color:#0468D7"> int</span><span style="color:#222222">? _overridden;</span></span> <span class="line"><span style="color:#0468D7"> Example</span><span style="color:#222222">(</span><span style="color:#D43324">this</span><span style="color:#222222">._overridden);</span></span> <span class="line"><span style="color:#222222">}</span></span> <span class="line"></span> <span class="line"><span style="color:#D43324">class</span><span style="color:#0468D7"> Override</span><span style="color:#D43324"> implements</span><span style="color:#0468D7"> Example</span><span style="color:#222222"> {</span></span> <span class="line"><span style="color:#D43324"> @override</span></span> <span class="line"><span style="color:#0468D7"> int</span><span style="color:#222222">? _overridden;</span></span> <span class="line"><span style="color:#222222">}</span></span> <span class="line"></span> <span class="line"><span style="color:#D43324">void</span><span style="color:#6200EE"> f</span><span style="color:#222222">(</span><span style="color:#0468D7">Example</span><span style="color:#222222"> x) {</span></span> <span class="line"><span style="color:#D43324"> if</span><span style="color:#222222"> (x._overridden != </span><span style="color:#11796D">null</span><span style="color:#222222">) {</span></span> <span class="line"><span style="color:#6200EE"> print</span><span style="color:#222222">(x._overridden.isEven); </span><span style="color:#6E6E70">// ERROR</span></span> <span class="line"><span style="color:#222222"> }</span></span> <span class="line"><span style="color:#222222">}</span></span></code></pre></div></div><p>This example fails because at runtime, <code>x</code> might actually be an instance of <code>Override</code>, so promotion would not be sound.</p><p><strong>Message:</strong></p><div class="code-block-wrapper language-plaintext"><div class="code-block-body"><pre class="shiki dash-light" tabindex="0"><code><span class="line"><span>'overridden' couldn't be promoted because there is a conflicting non-promotable field in class 'Override'.</span></span></code></pre></div></div><p><strong>Solution:</strong></p><p>If the fields are actually related and need to share a name, then you can enable type promotion by assigning the value to a final local variable to promote:</p> <?code-excerpt "non_promotion/lib/non_promotion.dart (conflicting-field)" replace="/final i =.*/[!$&!]/g; /FieldExample/Example/g; /f3/f/g; /Override2/Override/g;"?> <div class="code-block-wrapper language-dart"><div class="code-block-body has-tag tag-good"><span class="code-block-tag">good</span><span class="code-block-language" title="Language dart">dart</span><pre class="shiki dash-light" tabindex="0"><code><span class="line"><span style="color:#D43324">class</span><span style="color:#0468D7"> Example</span><span style="color:#222222"> {</span></span> <span class="line"><span style="color:#D43324"> final</span><span style="color:#0468D7"> int</span><span style="color:#222222">? _overridden;</span></span> <span class="line"><span style="color:#0468D7"> Example</span><span style="color:#222222">(</span><span style="color:#D43324">this</span><span style="color:#222222">._overridden);</span></span> <span class="line"><span style="color:#222222">}</span></span> <span class="line"></span> <span class="line"><span style="color:#D43324">class</span><span style="color:#0468D7"> Override</span><span style="color:#D43324"> implements</span><span style="color:#0468D7"> Example</span><span style="color:#222222"> {</span></span> <span class="line"><span style="color:#D43324"> @override</span></span> <span class="line"><span style="color:#0468D7"> int</span><span style="color:#222222">? _overridden;</span></span> <span class="line"><span style="color:#222222">}</span></span> <span class="line"></span> <span class="line"><span style="color:#D43324">void</span><span style="color:#6200EE"> f</span><span style="color:#222222">(</span><span style="color:#0468D7">Example</span><span style="color:#222222"> x) {</span></span> <span class="line"><span style="color:#D43324"> </span><mark class="highlight"><span style="color:#D43324">final</span><span style="color:#222222"> i = x._overridden;</span></mark></span> <span class="line"><span style="color:#D43324"> if</span><span style="color:#222222"> (i != </span><span style="color:#11796D">null</span><span style="color:#222222">) {</span></span> <span class="line"><span style="color:#6200EE"> print</span><span style="color:#222222">(i.isEven); </span><span style="color:#6E6E70">// OK</span></span> <span class="line"><span style="color:#222222"> }</span></span> <span class="line"><span style="color:#222222">}</span></span></code></pre></div></div><p>If the fields are unrelated, then rename one of the fields, so they don't conflict. Read the <a href="#note-about-unrelated-classes">Note about unrelated classes</a>.</p><div class="header-wrapper"><h3 id="nosuchmethod">Conflict with implicit <code>noSuchMethod</code> forwarder</h3><a class="heading-link" href="#nosuchmethod" aria-label="Link to 'Conflict with implicit noSuchMethod forwarder' section">#</a></div><p><strong>The cause:</strong> You're trying to promote a field that is private and final, but another class in the same library contains an <a href="/language/extend#nosuchmethod">implicit <code>noSuchMethod</code> forwarder</a> with the same name as the field.</p><p>This is unsound because there's no guarantee that <code>noSuchMethod</code> will return a stable value from one invocation to the next.</p><p><strong>Example:</strong></p><div class="code-block-wrapper language-dart"><div class="code-block-body has-tag tag-bad"><span class="code-block-tag">bad</span><span class="code-block-language" title="Language dart">dart</span><pre class="shiki dash-light" tabindex="0"><code><span class="line"><span style="color:#D43324">import</span><span style="color:#11796D"> 'package:mockito/mockito.dart'</span><span style="color:#222222">;</span></span> <span class="line"></span> <span class="line"><span style="color:#D43324">class</span><span style="color:#0468D7"> Example</span><span style="color:#222222"> {</span></span> <span class="line"><span style="color:#D43324"> final</span><span style="color:#0468D7"> int</span><span style="color:#222222">? _i;</span></span> <span class="line"><span style="color:#0468D7"> Example</span><span style="color:#222222">(</span><span style="color:#D43324">this</span><span style="color:#222222">._i);</span></span> <span class="line"><span style="color:#222222">}</span></span> <span class="line"></span> <span class="line"><span style="color:#D43324">class</span><span style="color:#0468D7"> MockExample</span><span style="color:#D43324"> extends</span><span style="color:#0468D7"> Mock</span><span style="color:#D43324"> implements</span><span style="color:#0468D7"> Example</span><span style="color:#222222"> {}</span></span> <span class="line"></span> <span class="line"><span style="color:#D43324">void</span><span style="color:#6200EE"> f</span><span style="color:#222222">(</span><span style="color:#0468D7">Example</span><span style="color:#222222"> x) {</span></span> <span class="line"><span style="color:#D43324"> if</span><span style="color:#222222"> (x._i != </span><span style="color:#11796D">null</span><span style="color:#222222">) {</span></span> <span class="line"><span style="color:#0468D7"> int</span><span style="color:#222222"> i = x._i; </span><span style="color:#6E6E70">// ERROR</span></span> <span class="line"><span style="color:#222222"> }</span></span> <span class="line"><span style="color:#222222">}</span></span></code></pre></div></div><p>In this example, <code>_i</code> can't be promoted because it could resolve to the unsound implicit <code>noSuchMethod</code> forwarder (also named <code>_i</code>) that the compiler generates inside <code>MockExample</code>.</p><p>The compiler creates this implicit implementation of <code>_i</code> because <code>MockExample</code> promises to support a getter for <code>_i</code> when it implements <code>Example</code> in its declaration, but doesn't fulfill that promise. So, the undefined getter implementation is handled by <a href="https://pub.dev/documentation/mockito/latest/mockito/Mock/noSuchMethod.html"><code>Mock</code>'s <code>noSuchMethod</code> definition</a>, which creates an implicit <code>noSuchMethod</code> forwarder of the same name.</p><p>The failure can also occur between fields in <a href="#note-about-unrelated-classes">unrelated classes</a>.</p><p><strong>Message:</strong></p><div class="code-block-wrapper language-plaintext"><div class="code-block-body"><pre class="shiki dash-light" tabindex="0"><code><span class="line"><span>'_i' couldn't be promoted because there is a conflicting noSuchMethod forwarder in class 'MockExample'.</span></span></code></pre></div></div><p><strong>Solution:</strong></p><p>Define the getter in question so that <code>noSuchMethod</code> doesn't have to implicitly handle its implementation:</p> <?code-excerpt "non_promotion/lib/non_promotion.dart (mock)" plaster="" replace="/late.*/[!$&!]/g; /MockingExample/Example/g; /f4/f/g;"?> <div class="code-block-wrapper language-dart"><div class="code-block-body has-tag tag-good"><span class="code-block-tag">good</span><span class="code-block-language" title="Language dart">dart</span><pre class="shiki dash-light" tabindex="0"><code><span class="line"><span style="color:#D43324">import</span><span style="color:#11796D"> 'package:mockito/mockito.dart'</span><span style="color:#222222">;</span></span> <span class="line"></span> <span class="line"><span style="color:#D43324">class</span><span style="color:#0468D7"> Example</span><span style="color:#222222"> {</span></span> <span class="line"><span style="color:#D43324"> final</span><span style="color:#0468D7"> int</span><span style="color:#222222">? _i;</span></span> <span class="line"><span style="color:#0468D7"> Example</span><span style="color:#222222">(</span><span style="color:#D43324">this</span><span style="color:#222222">._i);</span></span> <span class="line"><span style="color:#222222">}</span></span> <span class="line"></span> <span class="line"><span style="color:#D43324">class</span><span style="color:#0468D7"> MockExample</span><span style="color:#D43324"> extends</span><span style="color:#0468D7"> Mock</span><span style="color:#D43324"> implements</span><span style="color:#0468D7"> Example</span><span style="color:#222222"> {</span></span> <span class="line"><span style="color:#D43324"> @override</span></span> <span class="line"><span style="color:#D43324"> </span><mark class="highlight"><span style="color:#D43324">late</span><span style="color:#D43324"> final</span><span style="color:#0468D7"> int</span><span style="color:#222222">? _i;</span></mark></span> <span class="line"><span style="color:#222222">}</span></span> <span class="line"></span> <span class="line"><span style="color:#D43324">void</span><span style="color:#6200EE"> f</span><span style="color:#222222">(</span><span style="color:#0468D7">Example</span><span style="color:#222222"> x) {</span></span> <span class="line"><span style="color:#D43324"> if</span><span style="color:#222222"> (x._i != </span><span style="color:#11796D">null</span><span style="color:#222222">) {</span></span> <span class="line"><span style="color:#0468D7"> int</span><span style="color:#222222"> i = x._i; </span><span style="color:#6E6E70">// OK</span></span> <span class="line"><span style="color:#222222"> }</span></span> <span class="line"><span style="color:#222222">}</span></span></code></pre></div></div><p>The getter is declared <code>late</code> to be consistent with how mocks are generally used; it's not necessary to declare the getter <code>late</code> to solve this type promotion failure in scenarios not involving mocks.</p><aside class="alert alert-info"><div class="alert-header"><i class="material-symbols" aria-hidden="true">info</i> <span>Note</span></div><div class="alert-content"><p>The example above uses <a href="https://pub.dev/packages/mockito">mocks</a> simply because <code>Mock</code> already contains a <code>noSuchMethod</code> definition, so we don't have to define an arbitrary one and can keep the example code short.</p><p>We don't expect problems like this to arise very often in practice with mocks, because usually mocks are declared in a different library than the class they are mocking. When the classes in question are declared in different libraries, private names aren't forwarded to <code>noSuchMethod</code> (because that would violate privacy expectations), so it's still safe to promote the field.</p></div></aside><div class="header-wrapper"><h3 id="write">Possibly written after promotion</h3><a class="heading-link" href="#write" aria-label="Link to 'Possibly written after promotion' section">#</a></div><p><strong>The cause:</strong> You're trying to promote a variable that might have been written to since it was promoted.</p><p><strong>Example:</strong></p><div class="code-block-wrapper language-dart"><div class="code-block-body has-tag tag-bad"><span class="code-block-tag">bad</span><span class="code-block-language" title="Language dart">dart</span><pre class="shiki dash-light" tabindex="0"><code><span class="line"><span style="color:#D43324">void</span><span style="color:#6200EE"> f</span><span style="color:#222222">(</span><span style="color:#0468D7">bool</span><span style="color:#222222"> b, </span><span style="color:#0468D7">int</span><span style="color:#222222">? i, </span><span style="color:#0468D7">int</span><span style="color:#222222">? j) {</span></span> <span class="line"><span style="color:#D43324"> if</span><span style="color:#222222"> (i == </span><span style="color:#11796D">null</span><span style="color:#222222">) </span><span style="color:#D43324">return</span><span style="color:#222222">;</span></span> <span class="line"><span style="color:#D43324"> if</span><span style="color:#222222"> (b) {</span></span> <span class="line"><span style="color:#222222"> i = j; </span><span style="color:#6E6E70">// (1)</span></span> <span class="line"><span style="color:#222222"> }</span></span> <span class="line"><span style="color:#D43324"> if</span><span style="color:#222222"> (!b) {</span></span> <span class="line"><span style="color:#6200EE"> print</span><span style="color:#222222">(i.isEven); </span><span style="color:#6E6E70">// (2) ERROR</span></span> <span class="line"><span style="color:#222222"> }</span></span> <span class="line"><span style="color:#222222">}</span></span></code></pre></div></div><p><strong>Solution</strong>:</p><p>In this example, when flow analysis hits (1), it demotes <code>i</code> from non-nullable <code>int</code> back to nullable <code>int?</code>. A human can tell that the access at (2) is safe because there's no code path that includes both (1) and (2), but flow analysis isn't smart enough to see that, because it doesn't track correlations between conditions in separate <code>if</code> statements.</p><p>You might fix the problem by combining the two <code>if</code> statements:</p> <?code-excerpt "non_promotion/lib/non_promotion.dart (write-combine-ifs)" replace="/else/[!$&!]/g"?> <div class="code-block-wrapper language-dart"><div class="code-block-body has-tag tag-good"><span class="code-block-tag">good</span><span class="code-block-language" title="Language dart">dart</span><pre class="shiki dash-light" tabindex="0"><code><span class="line"><span style="color:#D43324">void</span><span style="color:#6200EE"> f</span><span style="color:#222222">(</span><span style="color:#0468D7">bool</span><span style="color:#222222"> b, </span><span style="color:#0468D7">int</span><span style="color:#222222">? i, </span><span style="color:#0468D7">int</span><span style="color:#222222">? j) {</span></span> <span class="line"><span style="color:#D43324"> if</span><span style="color:#222222"> (i == </span><span style="color:#11796D">null</span><span style="color:#222222">) </span><span style="color:#D43324">return</span><span style="color:#222222">;</span></span> <span class="line"><span style="color:#D43324"> if</span><span style="color:#222222"> (b) {</span></span> <span class="line"><span style="color:#222222"> i = j;</span></span> <span class="line"><span style="color:#222222"> } </span><mark class="highlight"><span style="color:#D43324">else</span></mark><span style="color:#222222"> {</span></span> <span class="line"><span style="color:#6200EE"> print</span><span style="color:#222222">(i.isEven);</span></span> <span class="line"><span style="color:#222222"> }</span></span> <span class="line"><span style="color:#222222">}</span></span></code></pre></div></div><p>In straight-line control flow cases like these (no loops), flow analysis takes into account the right hand side of the assignment when deciding whether to demote. As a result, another way to fix this code is to change the type of <code>j</code> to <code>int</code>.</p> <?code-excerpt "non_promotion/lib/non_promotion.dart (write-change-type)" replace="/int j/[!$&!]/g"?> <div class="code-block-wrapper language-dart"><div class="code-block-body has-tag tag-good"><span class="code-block-tag">good</span><span class="code-block-language" title="Language dart">dart</span><pre class="shiki dash-light" tabindex="0"><code><span class="line"><span style="color:#D43324">void</span><span style="color:#6200EE"> f</span><span style="color:#222222">(</span><span style="color:#0468D7">bool</span><span style="color:#222222"> b, </span><span style="color:#0468D7">int</span><span style="color:#222222">? i, </span><mark class="highlight"><span style="color:#0468D7">int</span><span style="color:#222222"> j</span></mark><span style="color:#222222">) {</span></span> <span class="line"><span style="color:#D43324"> if</span><span style="color:#222222"> (i == </span><span style="color:#11796D">null</span><span style="color:#222222">) </span><span style="color:#D43324">return</span><span style="color:#222222">;</span></span> <span class="line"><span style="color:#D43324"> if</span><span style="color:#222222"> (b) {</span></span> <span class="line"><span style="color:#222222"> i = j;</span></span> <span class="line"><span style="color:#222222"> }</span></span> <span class="line"><span style="color:#D43324"> if</span><span style="color:#222222"> (!b) {</span></span> <span class="line"><span style="color:#6200EE"> print</span><span style="color:#222222">(i.isEven);</span></span> <span class="line"><span style="color:#222222"> }</span></span> <span class="line"><span style="color:#222222">}</span></span></code></pre></div></div><div class="header-wrapper"><h3 id="loop-or-switch">Possibly written in a previous loop iteration</h3><a class="heading-link" href="#loop-or-switch" aria-label="Link to 'Possibly written in a previous loop iteration' section">#</a></div><p><strong>The cause:</strong> You're trying to promote something that might have been written to in a previous iteration of a loop, and so the promotion was invalidated.</p><p><strong>Example:</strong></p><div class="code-block-wrapper language-dart"><div class="code-block-body has-tag tag-bad"><span class="code-block-tag">bad</span><span class="code-block-language" title="Language dart">dart</span><pre class="shiki dash-light" tabindex="0"><code><span class="line"><span style="color:#D43324">void</span><span style="color:#6200EE"> f</span><span style="color:#222222">(</span><span style="color:#0468D7">Link</span><span style="color:#222222">? p) {</span></span> <span class="line"><span style="color:#D43324"> if</span><span style="color:#222222"> (p != </span><span style="color:#11796D">null</span><span style="color:#222222">) </span><span style="color:#D43324">return</span><span style="color:#222222">;</span></span> <span class="line"><span style="color:#D43324"> while</span><span style="color:#222222"> (</span><span style="color:#11796D">true</span><span style="color:#222222">) { </span><span style="color:#6E6E70">// (1)</span></span> <span class="line"><span style="color:#6200EE"> print</span><span style="color:#222222">(p.value); </span><span style="color:#6E6E70">// (2) ERROR</span></span> <span class="line"><span style="color:#D43324"> var</span><span style="color:#222222"> next = p.next;</span></span> <span class="line"><span style="color:#D43324"> if</span><span style="color:#222222"> (next == </span><span style="color:#11796D">null</span><span style="color:#222222">) </span><span style="color:#D43324">break</span><span style="color:#222222">;</span></span> <span class="line"><span style="color:#222222"> p = next; </span><span style="color:#6E6E70">// (3)</span></span> <span class="line"><span style="color:#222222"> }</span></span> <span class="line"><span style="color:#222222">}</span></span></code></pre></div></div><p>When flow analysis reaches (1), it looks ahead and sees the write to <code>p</code> at (3). But because it's looking ahead, it hasn't yet figured out the type of the right-hand side of the assignment, so it doesn't know whether it's safe to retain the promotion. To be safe, it invalidates the promotion.</p><p><strong>Solution</strong>:</p><p>You can fix this problem by moving the null check to the top of the loop:</p> <?code-excerpt "non_promotion/lib/non_promotion.dart (loop)" replace="/p != null/[!$&!]/g"?> <div class="code-block-wrapper language-dart"><div class="code-block-body has-tag tag-good"><span class="code-block-tag">good</span><span class="code-block-language" title="Language dart">dart</span><pre class="shiki dash-light" tabindex="0"><code><span class="line"><span style="color:#D43324">void</span><span style="color:#6200EE"> f</span><span style="color:#222222">(</span><span style="color:#0468D7">Link</span><span style="color:#222222">? p) {</span></span> <span class="line"><span style="color:#D43324"> while</span><span style="color:#222222"> (</span><mark class="highlight"><span style="color:#222222">p != </span><span style="color:#11796D">null</span></mark><span style="color:#222222">) {</span></span> <span class="line"><span style="color:#6200EE"> print</span><span style="color:#222222">(p.value);</span></span> <span class="line"><span style="color:#222222"> p = p.next;</span></span> <span class="line"><span style="color:#222222"> }</span></span> <span class="line"><span style="color:#222222">}</span></span></code></pre></div></div><p>This situation can also arise in <code>switch</code> statements if a <code>case</code> block has a label, because you can use labeled <code>switch</code> statements to construct loops:</p><div class="code-block-wrapper language-dart"><div class="code-block-body has-tag tag-bad"><span class="code-block-tag">bad</span><span class="code-block-language" title="Language dart">dart</span><pre class="shiki dash-light" tabindex="0"><code><span class="line"><span style="color:#D43324">void</span><span style="color:#6200EE"> f</span><span style="color:#222222">(</span><span style="color:#0468D7">int</span><span style="color:#222222"> i, </span><span style="color:#0468D7">int</span><span style="color:#222222">? j, </span><span style="color:#0468D7">int</span><span style="color:#222222">? k) {</span></span> <span class="line"><span style="color:#D43324"> if</span><span style="color:#222222"> (j == </span><span style="color:#11796D">null</span><span style="color:#222222">) </span><span style="color:#D43324">return</span><span style="color:#222222">;</span></span> <span class="line"><span style="color:#D43324"> switch</span><span style="color:#222222"> (i) {</span></span> <span class="line"><span style="color:#222222"> label:</span></span> <span class="line"><span style="color:#D43324"> case</span><span style="color:#11796D"> 0</span><span style="color:#222222">:</span></span> <span class="line"><span style="color:#6200EE"> print</span><span style="color:#222222">(j.isEven); </span><span style="color:#6E6E70">// ERROR</span></span> <span class="line"><span style="color:#222222"> j = k;</span></span> <span class="line"><span style="color:#D43324"> continue</span><span style="color:#222222"> label;</span></span> <span class="line"><span style="color:#222222"> }</span></span> <span class="line"><span style="color:#222222">}</span></span></code></pre></div></div><p>Again, you can fix the problem by moving the null check to the top of the loop:</p> <?code-excerpt "non_promotion/lib/non_promotion.dart (switch-loop)" replace="/if .*/[!$&!]/g"?> <div class="code-block-wrapper language-dart"><div class="code-block-body has-tag tag-good"><span class="code-block-tag">good</span><span class="code-block-language" title="Language dart">dart</span><pre class="shiki dash-light" tabindex="0"><code><span class="line"><span style="color:#D43324">void</span><span style="color:#6200EE"> f</span><span style="color:#222222">(</span><span style="color:#0468D7">int</span><span style="color:#222222"> i, </span><span style="color:#0468D7">int</span><span style="color:#222222">? j, </span><span style="color:#0468D7">int</span><span style="color:#222222">? k) {</span></span> <span class="line"><span style="color:#D43324"> switch</span><span style="color:#222222"> (i) {</span></span> <span class="line"><span style="color:#222222"> label:</span></span> <span class="line"><span style="color:#D43324"> case</span><span style="color:#11796D"> 0</span><span style="color:#222222">:</span></span> <span class="line"><span style="color:#D43324"> </span><mark class="highlight"><span style="color:#D43324">if</span><span style="color:#222222"> (j == </span><span style="color:#11796D">null</span><span style="color:#222222">) </span><span style="color:#D43324">return</span><span style="color:#222222">;</span></mark></span> <span class="line"><span style="color:#6200EE"> print</span><span style="color:#222222">(j.isEven);</span></span> <span class="line"><span style="color:#222222"> j = k;</span></span> <span class="line"><span style="color:#D43324"> continue</span><span style="color:#222222"> label;</span></span> <span class="line"><span style="color:#222222"> }</span></span> <span class="line"><span style="color:#222222">}</span></span></code></pre></div></div><div class="header-wrapper"><h3 id="catch">In catch after possible write in try</h3><a class="heading-link" href="#catch" aria-label="Link to 'In catch after possible write in try' section">#</a></div><p><strong>The cause:</strong> The variable might have been written to in a <code>try</code> block, and execution is now in a <code>catch</code> block.</p><p><strong>Example:</strong></p><div class="code-block-wrapper language-dart"><div class="code-block-body has-tag tag-bad"><span class="code-block-tag">bad</span><span class="code-block-language" title="Language dart">dart</span><pre class="shiki dash-light" tabindex="0"><code><span class="line"><span style="color:#D43324">void</span><span style="color:#6200EE"> f</span><span style="color:#222222">(</span><span style="color:#0468D7">int</span><span style="color:#222222">? i, </span><span style="color:#0468D7">int</span><span style="color:#222222">? j) {</span></span> <span class="line"><span style="color:#D43324"> if</span><span style="color:#222222"> (i == </span><span style="color:#11796D">null</span><span style="color:#222222">) </span><span style="color:#D43324">return</span><span style="color:#222222">;</span></span> <span class="line"><span style="color:#D43324"> try</span><span style="color:#222222"> {</span></span> <span class="line"><span style="color:#222222"> i = j; </span><span style="color:#6E6E70">// (1)</span></span> <span class="line"><span style="color:#6E6E70"> // ... Additional code ...</span></span> <span class="line"><span style="color:#D43324"> if</span><span style="color:#222222"> (i == </span><span style="color:#11796D">null</span><span style="color:#222222">) </span><span style="color:#D43324">return</span><span style="color:#222222">; </span><span style="color:#6E6E70">// (2)</span></span> <span class="line"><span style="color:#6E6E70"> // ... Additional code ...</span></span> <span class="line"><span style="color:#222222"> } </span><span style="color:#D43324">catch</span><span style="color:#222222"> (e) {</span></span> <span class="line"><span style="color:#6200EE"> print</span><span style="color:#222222">(i.isEven); </span><span style="color:#6E6E70">// (3) ERROR</span></span> <span class="line"><span style="color:#222222"> }</span></span> <span class="line"><span style="color:#222222">}</span></span></code></pre></div></div><p>In this case, flow analysis doesn't consider <code>i.isEven</code> (3) safe, because it has no way of knowing when in the <code>try</code> block the exception might have occurred, so it conservatively assumes that it might have happened between (1) and (2), when <code>i</code> was potentially <code>null</code>.</p><p>Similar situations can occur between <code>try</code> and <code>finally</code> blocks, and between <code>catch</code> and <code>finally</code> blocks. Because of a historical artifact of how the implementation was done, these <code>try</code>/<code>catch</code>/<code>finally</code> situations don't take into account the right-hand side of the assignment, similar to what happens in loops.</p><p><strong>Solution</strong>:</p><p>To fix the problem, make sure that the <code>catch</code> block doesn't rely on assumptions about the state of variables that get changed inside the <code>try</code> block. Remember, the exception might occur at any time during the <code>try</code> block, possibly when <code>i</code> is <code>null</code>.</p><p>The safest solution is to add a null check inside the <code>catch</code> block:</p> <?code-excerpt "non_promotion/lib/non_promotion.dart (catch-null-check)" replace="/if.*/[!$&!]/g;/(} else {| \/\/ H.*)/[!$&!]/g;/ }/ [!}!]/g"?> <div class="code-block-wrapper language-dart"><div class="code-block-body has-tag tag-good"><span class="code-block-tag">good</span><span class="code-block-language" title="Language dart">dart</span><pre class="shiki dash-light" tabindex="0"><code><span class="line"><span style="color:#D43324">try</span><span style="color:#222222"> {</span></span> <span class="line"><span style="color:#6E6E70"> // 路路路</span></span> <span class="line"><span style="color:#222222">} </span><span style="color:#D43324">catch</span><span style="color:#222222"> (e) {</span></span> <span class="line"><span style="color:#D43324"> </span><mark class="highlight"><span style="color:#D43324">if</span><span style="color:#222222"> (i != </span><span style="color:#11796D">null</span><span style="color:#222222">) {</span></mark></span> <span class="line"><span style="color:#6200EE"> print</span><span style="color:#222222">(i.isEven); </span><span style="color:#6E6E70">// (3) OK due to the null check in the line above.</span></span> <span class="line"><span style="color:#222222"> </span><mark class="highlight"><span style="color:#222222">} </span><span style="color:#D43324">else</span><span style="color:#222222"> {</span></mark></span> <span class="line"><span style="color:#6E6E70"> </span><mark class="highlight"><span style="color:#6E6E70"> // Handle the case where i is null.</span></mark></span> <span class="line"><span style="color:#222222"> </span><mark class="highlight"><span style="color:#222222">}</span></mark></span> <span class="line"><span style="color:#222222">}</span></span></code></pre></div></div><p>Or, if you're sure that an exception can't occur while <code>i</code> is <code>null</code>, just use the <code>!</code> operator:</p> <?code-excerpt "non_promotion/lib/non_promotion.dart (catch-bang)" replace="/i!/i[!!!]/g"?> <div class="code-block-wrapper language-dart"><div class="code-block-body"><span class="code-block-language" title="Language dart">dart</span><pre class="shiki dash-light" tabindex="0"><code><span class="line"><span style="color:#D43324">try</span><span style="color:#222222"> {</span></span> <span class="line"><span style="color:#6E6E70"> // 路路路</span></span> <span class="line"><span style="color:#222222">} </span><span style="color:#D43324">catch</span><span style="color:#222222"> (e) {</span></span> <span class="line"><span style="color:#6200EE"> print</span><span style="color:#222222">(i</span><mark class="highlight"><span style="color:#222222">!</span></mark><span style="color:#222222">.isEven); </span><span style="color:#6E6E70">// (3) OK because of the `!`.</span></span> <span class="line"><span style="color:#222222">}</span></span></code></pre></div></div><div class="header-wrapper"><h3 id="subtype-mismatch">Subtype mismatch</h3><a class="heading-link" href="#subtype-mismatch" aria-label="Link to 'Subtype mismatch' section">#</a></div><p><strong>The cause:</strong> You're trying to promote to a type isn't a subtype of the variable's current promoted type (or wasn't a subtype at the time of the promotion attempt).</p><p><strong>Example:</strong></p><div class="code-block-wrapper language-dart"><div class="code-block-body has-tag tag-bad"><span class="code-block-tag">bad</span><span class="code-block-language" title="Language dart">dart</span><pre class="shiki dash-light" tabindex="0"><code><span class="line"><span style="color:#D43324">void</span><span style="color:#6200EE"> f</span><span style="color:#222222">(</span><span style="color:#0468D7">Object</span><span style="color:#222222"> o) {</span></span> <span class="line"><span style="color:#D43324"> if</span><span style="color:#222222"> (o is </span><span style="color:#0468D7">Comparable</span><span style="color:#6E6E70"> /* (1) */</span><span style="color:#222222">) {</span></span> <span class="line"><span style="color:#D43324"> if</span><span style="color:#222222"> (o is </span><span style="color:#0468D7">Pattern</span><span style="color:#6E6E70"> /* (2) */</span><span style="color:#222222">) {</span></span> <span class="line"><span style="color:#6200EE"> print</span><span style="color:#222222">(o.</span><span style="color:#6200EE">matchAsPrefix</span><span style="color:#222222">(</span><span style="color:#11796D">'foo'</span><span style="color:#222222">)); </span><span style="color:#6E6E70">// (3) ERROR</span></span> <span class="line"><span style="color:#222222"> }</span></span> <span class="line"><span style="color:#222222"> }</span></span> <span class="line"><span style="color:#222222">}</span></span></code></pre></div></div><p>In this example, <code>o</code> is promoted to <code>Comparable</code> at (1), but it isn't promoted to <code>Pattern</code> at (2), because <code>Pattern</code> isn't a subtype of <code>Comparable</code>. (The rationale is that if it did promote, then you wouldn't be able to use methods on <code>Comparable</code>.) Note that just because <code>Pattern</code> isn't a subtype of <code>Comparable</code> doesn't mean the code at (3) is dead; <code>o</code> might have a type鈥攍ike <code>String</code>鈥攖hat implements both <code>Comparable</code> and <code>Pattern</code>.</p><p><strong>Solution</strong>:</p><p>One possible solution is to create a new local variable so that the original variable is promoted to <code>Comparable</code>, and the new variable is promoted to <code>Pattern</code>:</p> <?code-excerpt "non_promotion/lib/non_promotion.dart (subtype-variable)" replace="/Object o2.*/[!$&!]/g;/(o2)(\.| is)/[!$1!]$2/g"?> <div class="code-block-wrapper language-dart"><div class="code-block-body"><span class="code-block-language" title="Language dart">dart</span><pre class="shiki dash-light" tabindex="0"><code><span class="line"><span style="color:#D43324">void</span><span style="color:#6200EE"> f</span><span style="color:#222222">(</span><span style="color:#0468D7">Object</span><span style="color:#222222"> o) {</span></span> <span class="line"><span style="color:#D43324"> if</span><span style="color:#222222"> (o is </span><span style="color:#0468D7">Comparable</span><span style="color:#6E6E70"> /* (1) */</span><span style="color:#222222">) {</span></span> <span class="line"><span style="color:#0468D7"> </span><mark class="highlight"><span style="color:#0468D7">Object</span><span style="color:#222222"> o2 = o;</span></mark></span> <span class="line"><span style="color:#D43324"> if</span><span style="color:#222222"> (</span><mark class="highlight"><span style="color:#222222">o2</span></mark><span style="color:#222222"> is </span><span style="color:#0468D7">Pattern</span><span style="color:#6E6E70"> /* (2) */</span><span style="color:#222222">) {</span></span> <span class="line"><span style="color:#6200EE"> print</span><span style="color:#222222">(</span></span> <span class="line"><span style="color:#222222"> </span><mark class="highlight"><span style="color:#222222">o2</span></mark><span style="color:#222222">.</span><span style="color:#6200EE">matchAsPrefix</span><span style="color:#222222">(</span><span style="color:#11796D">'foo'</span><span style="color:#222222">)); </span><span style="color:#6E6E70">// (3) OK; o2 was promoted to `Pattern`.</span></span> <span class="line"><span style="color:#222222"> }</span></span> <span class="line"><span style="color:#222222"> }</span></span> <span class="line"><span style="color:#222222">}</span></span></code></pre></div></div><p>However, someone who edits the code later might be tempted to change <code>Object o2</code> to <code>var o2</code>. That change gives <code>o2</code> a type of <code>Comparable</code>, which brings back the problem of the object not being promotable to <code>Pattern</code>.</p><p>A redundant type check might be a better solution:</p> <?code-excerpt "non_promotion/lib/non_promotion.dart (subtype-redundant)" replace="/\(o as Pattern\)/[!$&!]/g"?> <div class="code-block-wrapper language-dart"><div class="code-block-body has-tag tag-good"><span class="code-block-tag">good</span><span class="code-block-language" title="Language dart">dart</span><pre class="shiki dash-light" tabindex="0"><code><span class="line"><span style="color:#D43324">void</span><span style="color:#6200EE"> f</span><span style="color:#222222">(</span><span style="color:#0468D7">Object</span><span style="color:#222222"> o) {</span></span> <span class="line"><span style="color:#D43324"> if</span><span style="color:#222222"> (o is </span><span style="color:#0468D7">Comparable</span><span style="color:#6E6E70"> /* (1) */</span><span style="color:#222222">) {</span></span> <span class="line"><span style="color:#D43324"> if</span><span style="color:#222222"> (o is </span><span style="color:#0468D7">Pattern</span><span style="color:#6E6E70"> /* (2) */</span><span style="color:#222222">) {</span></span> <span class="line"><span style="color:#6200EE"> print</span><span style="color:#222222">(</span><mark class="highlight"><span style="color:#222222">(o </span><span style="color:#D43324">as</span><span style="color:#0468D7"> Pattern</span><span style="color:#222222">)</span></mark><span style="color:#222222">.</span><span style="color:#6200EE">matchAsPrefix</span><span style="color:#222222">(</span><span style="color:#11796D">'foo'</span><span style="color:#222222">)); </span><span style="color:#6E6E70">// (3) OK</span></span> <span class="line"><span style="color:#222222"> }</span></span> <span class="line"><span style="color:#222222"> }</span></span> <span class="line"><span style="color:#222222">}</span></span></code></pre></div></div><p>Another solution that sometimes works is when you can use a more precise type. If line 3 cares only about strings, then you can use <code>String</code> in your type check. Because <code>String</code> is a subtype of <code>Comparable</code>, the promotion works:</p> <?code-excerpt "non_promotion/lib/non_promotion.dart (subtype-string)" replace="/is String/is [!String!]/g"?> <div class="code-block-wrapper language-dart"><div class="code-block-body has-tag tag-good"><span class="code-block-tag">good</span><span class="code-block-language" title="Language dart">dart</span><pre class="shiki dash-light" tabindex="0"><code><span class="line"><span style="color:#D43324">void</span><span style="color:#6200EE"> f</span><span style="color:#222222">(</span><span style="color:#0468D7">Object</span><span style="color:#222222"> o) {</span></span> <span class="line"><span style="color:#D43324"> if</span><span style="color:#222222"> (o is </span><span style="color:#0468D7">Comparable</span><span style="color:#6E6E70"> /* (1) */</span><span style="color:#222222">) {</span></span> <span class="line"><span style="color:#D43324"> if</span><span style="color:#222222"> (o is </span><mark class="highlight"><span style="color:#0468D7">String</span></mark><span style="color:#6E6E70"> /* (2) */</span><span style="color:#222222">) {</span></span> <span class="line"><span style="color:#6200EE"> print</span><span style="color:#222222">(o.</span><span style="color:#6200EE">matchAsPrefix</span><span style="color:#222222">(</span><span style="color:#11796D">'foo'</span><span style="color:#222222">)); </span><span style="color:#6E6E70">// (3) OK</span></span> <span class="line"><span style="color:#222222"> }</span></span> <span class="line"><span style="color:#222222"> }</span></span> <span class="line"><span style="color:#222222">}</span></span></code></pre></div></div><div class="header-wrapper"><h3 id="captured-local">Write captured by a local function</h3><a class="heading-link" href="#captured-local" aria-label="Link to 'Write captured by a local function' section">#</a></div><p><strong>The cause:</strong> The variable has been write-captured by a local function or function expression.</p><p><strong>Example:</strong></p><div class="code-block-wrapper language-dart"><div class="code-block-body has-tag tag-bad"><span class="code-block-tag">bad</span><span class="code-block-language" title="Language dart">dart</span><pre class="shiki dash-light" tabindex="0"><code><span class="line"><span style="color:#D43324">void</span><span style="color:#6200EE"> f</span><span style="color:#222222">(</span><span style="color:#0468D7">int</span><span style="color:#222222">? i, </span><span style="color:#0468D7">int</span><span style="color:#222222">? j) {</span></span> <span class="line"><span style="color:#D43324"> var</span><span style="color:#222222"> foo = () {</span></span> <span class="line"><span style="color:#222222"> i = j;</span></span> <span class="line"><span style="color:#222222"> };</span></span> <span class="line"><span style="color:#6E6E70"> // ... Use foo ... </span></span> <span class="line"><span style="color:#D43324"> if</span><span style="color:#222222"> (i == </span><span style="color:#11796D">null</span><span style="color:#222222">) </span><span style="color:#D43324">return</span><span style="color:#222222">; </span><span style="color:#6E6E70">// (1)</span></span> <span class="line"><span style="color:#6E6E70"> // ... Additional code ...</span></span> <span class="line"><span style="color:#6200EE"> print</span><span style="color:#222222">(i.isEven); </span><span style="color:#6E6E70">// (2) ERROR</span></span> <span class="line"><span style="color:#222222">}</span></span></code></pre></div></div><p>Flow analysis reasons that as soon as the definition of <code>foo</code> is reached, it might get called at any time, therefore it's no longer safe to promote <code>i</code> at all. As with loops, this demotion happens regardless of the type of the right hand side of the assignment.</p><p><strong>Solution</strong>:</p><p>Sometimes it's possible to restructure the logic so that the promotion is before the write capture:</p> <?code-excerpt "non_promotion/lib/non_promotion.dart (local-write-capture-reorder)" replace="/( )((var foo| i = j|\}\;|\/\/ ... Use foo).*)/$1[!$2!]/g"?> <div class="code-block-wrapper language-dart"><div class="code-block-body has-tag tag-good"><span class="code-block-tag">good</span><span class="code-block-language" title="Language dart">dart</span><pre class="shiki dash-light" tabindex="0"><code><span class="line"><span style="color:#D43324">void</span><span style="color:#6200EE"> f</span><span style="color:#222222">(</span><span style="color:#0468D7">int</span><span style="color:#222222">? i, </span><span style="color:#0468D7">int</span><span style="color:#222222">? j) {</span></span> <span class="line"><span style="color:#D43324"> if</span><span style="color:#222222"> (i == </span><span style="color:#11796D">null</span><span style="color:#222222">) </span><span style="color:#D43324">return</span><span style="color:#222222">; </span><span style="color:#6E6E70">// (1)</span></span> <span class="line"><span style="color:#6E6E70"> // ... Additional code ...</span></span> <span class="line"><span style="color:#6200EE"> print</span><span style="color:#222222">(i.isEven); </span><span style="color:#6E6E70">// (2) OK</span></span> <span class="line"><span style="color:#D43324"> </span><mark class="highlight"><span style="color:#D43324">var</span><span style="color:#222222"> foo = () {</span></mark></span> <span class="line"><span style="color:#222222"> </span><mark class="highlight"><span style="color:#222222"> i = j;</span></mark></span> <span class="line"><span style="color:#222222"> </span><mark class="highlight"><span style="color:#222222">};</span></mark></span> <span class="line"><span style="color:#6E6E70"> </span><mark class="highlight"><span style="color:#6E6E70">// ... Use foo ...</span></mark></span> <span class="line"><span style="color:#222222">}</span></span></code></pre></div></div><p>Another option is to create a local variable, so it isn't write captured:</p> <?code-excerpt "non_promotion/lib/non_promotion.dart (local-write-capture-copy)" replace="/var i2.*/[!$&!]/g;/(i2)( ==|\.)/[!$1!]$2/g"?> <div class="code-block-wrapper language-dart"><div class="code-block-body has-tag tag-good"><span class="code-block-tag">good</span><span class="code-block-language" title="Language dart">dart</span><pre class="shiki dash-light" tabindex="0"><code><span class="line"><span style="color:#D43324">void</span><span style="color:#6200EE"> f</span><span style="color:#222222">(</span><span style="color:#0468D7">int</span><span style="color:#222222">? i, </span><span style="color:#0468D7">int</span><span style="color:#222222">? j) {</span></span> <span class="line"><span style="color:#D43324"> var</span><span style="color:#222222"> foo = () {</span></span> <span class="line"><span style="color:#222222"> i = j;</span></span> <span class="line"><span style="color:#222222"> };</span></span> <span class="line"><span style="color:#6E6E70"> // ... Use foo ...</span></span> <span class="line"><span style="color:#D43324"> </span><mark class="highlight"><span style="color:#D43324">var</span><span style="color:#222222"> i2 = i;</span></mark></span> <span class="line"><span style="color:#D43324"> if</span><span style="color:#222222"> (</span><mark class="highlight"><span style="color:#222222">i2</span></mark><span style="color:#222222"> == </span><span style="color:#11796D">null</span><span style="color:#222222">) </span><span style="color:#D43324">return</span><span style="color:#222222">; </span><span style="color:#6E6E70">// (1)</span></span> <span class="line"><span style="color:#6E6E70"> // ... Additional code ...</span></span> <span class="line"><span style="color:#6200EE"> print</span><span style="color:#222222">(</span><mark class="highlight"><span style="color:#222222">i2</span></mark><span style="color:#222222">.isEven); </span><span style="color:#6E6E70">// (2) OK because `i2` isn't write captured.</span></span> <span class="line"><span style="color:#222222">}</span></span></code></pre></div></div><p>Or you can do a redundant check:</p> <?code-excerpt "non_promotion/lib/non_promotion.dart (local-write-capture-bang)" replace="/i!/i[!!!]/g"?> <div class="code-block-wrapper language-dart"><div class="code-block-body"><span class="code-block-language" title="Language dart">dart</span><pre class="shiki dash-light" tabindex="0"><code><span class="line"><span style="color:#D43324">void</span><span style="color:#6200EE"> f</span><span style="color:#222222">(</span><span style="color:#0468D7">int</span><span style="color:#222222">? i, </span><span style="color:#0468D7">int</span><span style="color:#222222">? j) {</span></span> <span class="line"><span style="color:#D43324"> var</span><span style="color:#222222"> foo = () {</span></span> <span class="line"><span style="color:#222222"> i = j;</span></span> <span class="line"><span style="color:#222222"> };</span></span> <span class="line"><span style="color:#6E6E70"> // ... Use foo ...</span></span> <span class="line"><span style="color:#D43324"> if</span><span style="color:#222222"> (i == </span><span style="color:#11796D">null</span><span style="color:#222222">) </span><span style="color:#D43324">return</span><span style="color:#222222">; </span><span style="color:#6E6E70">// (1)</span></span> <span class="line"><span style="color:#6E6E70"> // ... Additional code ...</span></span> <span class="line"><span style="color:#6200EE"> print</span><span style="color:#222222">(i</span><mark class="highlight"><span style="color:#222222">!</span></mark><span style="color:#222222">.isEven); </span><span style="color:#6E6E70">// (2) OK due to `!` check.</span></span> <span class="line"><span style="color:#222222">}</span></span></code></pre></div></div><div class="header-wrapper"><h3 id="write-outer">Written outside of the current closure or function expression</h3><a class="heading-link" href="#write-outer" aria-label="Link to 'Written outside of the current closure or function expression' section">#</a></div><p><strong>The cause:</strong> The variable is written to outside of a closure or function expression, and the type promotion location is inside the closure or function expression.</p><p><strong>Example:</strong></p><div class="code-block-wrapper language-dart"><div class="code-block-body has-tag tag-bad"><span class="code-block-tag">bad</span><span class="code-block-language" title="Language dart">dart</span><pre class="shiki dash-light" tabindex="0"><code><span class="line"><span style="color:#D43324">void</span><span style="color:#6200EE"> f</span><span style="color:#222222">(</span><span style="color:#0468D7">int</span><span style="color:#222222">? i, </span><span style="color:#0468D7">int</span><span style="color:#222222">? j) {</span></span> <span class="line"><span style="color:#D43324"> if</span><span style="color:#222222"> (i == </span><span style="color:#11796D">null</span><span style="color:#222222">) </span><span style="color:#D43324">return</span><span style="color:#222222">;</span></span> <span class="line"><span style="color:#D43324"> var</span><span style="color:#222222"> foo = () {</span></span> <span class="line"><span style="color:#6200EE"> print</span><span style="color:#222222">(i.isEven); </span><span style="color:#6E6E70">// (1) ERROR</span></span> <span class="line"><span style="color:#222222"> };</span></span> <span class="line"><span style="color:#222222"> i = j; </span><span style="color:#6E6E70">// (2)</span></span> <span class="line"><span style="color:#222222">}</span></span></code></pre></div></div><p>Flow analysis reasons that there's no way to determine when <code>foo</code> might get called, so it might get called after the assignment at (2), and thus the promotion might no longer be valid. As with loops, this demotion happens regardless of the type of the right hand side of the assignment.</p><p><strong>Solution</strong>:</p><p>A solution is to create a local variable:</p> <?code-excerpt "non_promotion/lib/non_promotion.dart (closure-new-var)" replace="/var i2.*/[!$&!]/g;/i2\./[!i2!]./g"?> <div class="code-block-wrapper language-dart"><div class="code-block-body has-tag tag-good"><span class="code-block-tag">good</span><span class="code-block-language" title="Language dart">dart</span><pre class="shiki dash-light" tabindex="0"><code><span class="line"><span style="color:#D43324">void</span><span style="color:#6200EE"> f</span><span style="color:#222222">(</span><span style="color:#0468D7">int</span><span style="color:#222222">? i, </span><span style="color:#0468D7">int</span><span style="color:#222222">? j) {</span></span> <span class="line"><span style="color:#D43324"> if</span><span style="color:#222222"> (i == </span><span style="color:#11796D">null</span><span style="color:#222222">) </span><span style="color:#D43324">return</span><span style="color:#222222">;</span></span> <span class="line"><span style="color:#D43324"> </span><mark class="highlight"><span style="color:#D43324">var</span><span style="color:#222222"> i2 = i;</span></mark></span> <span class="line"><span style="color:#D43324"> var</span><span style="color:#222222"> foo = () {</span></span> <span class="line"><span style="color:#6200EE"> print</span><span style="color:#222222">(</span><mark class="highlight"><span style="color:#222222">i2</span></mark><span style="color:#222222">.isEven); </span><span style="color:#6E6E70">// (1) OK because `i2` isn't changed later.</span></span> <span class="line"><span style="color:#222222"> };</span></span> <span class="line"><span style="color:#222222"> i = j; </span><span style="color:#6E6E70">// (2)</span></span> <span class="line"><span style="color:#222222">}</span></span></code></pre></div></div><p><strong>Example:</strong></p><p>A particularly nasty case looks like this:</p><div class="code-block-wrapper language-dart"><div class="code-block-body has-tag tag-bad"><span class="code-block-tag">bad</span><span class="code-block-language" title="Language dart">dart</span><pre class="shiki dash-light" tabindex="0"><code><span class="line"><span style="color:#D43324">void</span><span style="color:#6200EE"> f</span><span style="color:#222222">(</span><span style="color:#0468D7">int</span><span style="color:#222222">? i) {</span></span> <span class="line"><span style="color:#222222"> i ??= </span><span style="color:#11796D">0</span><span style="color:#222222">;</span></span> <span class="line"><span style="color:#D43324"> var</span><span style="color:#222222"> foo = () {</span></span> <span class="line"><span style="color:#6200EE"> print</span><span style="color:#222222">(i.isEven); </span><span style="color:#6E6E70">// ERROR</span></span> <span class="line"><span style="color:#222222"> };</span></span> <span class="line"><span style="color:#222222">}</span></span></code></pre></div></div><p>In this case, a human can see that the promotion is safe because the only write to <code>i</code> uses a non-null value and happens before <code>foo</code> is ever created. But <a href="https://github.com/dart-lang/language/issues/1536">flow analysis isn't that smart</a>.</p><p><strong>Solution</strong>:</p><p>Again, a solution is to create a local variable:</p> <?code-excerpt "non_promotion/lib/non_promotion.dart (closure-new-var2)" replace="/var j.*/[!$&!]/g;/j\./[!j!]./g"?> <div class="code-block-wrapper language-dart"><div class="code-block-body has-tag tag-good"><span class="code-block-tag">good</span><span class="code-block-language" title="Language dart">dart</span><pre class="shiki dash-light" tabindex="0"><code><span class="line"><span style="color:#D43324">void</span><span style="color:#6200EE"> f</span><span style="color:#222222">(</span><span style="color:#0468D7">int</span><span style="color:#222222">? i) {</span></span> <span class="line"><span style="color:#D43324"> </span><mark class="highlight"><span style="color:#D43324">var</span><span style="color:#222222"> j = i ?? </span><span style="color:#11796D">0</span><span style="color:#222222">;</span></mark></span> <span class="line"><span style="color:#D43324"> var</span><span style="color:#222222"> foo = () {</span></span> <span class="line"><span style="color:#6200EE"> print</span><span style="color:#222222">(</span><mark class="highlight"><span style="color:#222222">j</span></mark><span style="color:#222222">.isEven); </span><span style="color:#6E6E70">// OK</span></span> <span class="line"><span style="color:#222222"> };</span></span> <span class="line"><span style="color:#222222">}</span></span></code></pre></div></div><p>This solution works because <code>j</code> is inferred to have a non-nullable type (<code>int</code>) due to its initial value (<code>i ?? 0</code>). Because <code>j</code> has a non-nullable type, whether or not it's assigned later, <code>j</code> can never have a non-null value.</p><div class="header-wrapper"><h3 id="captured-outer">Write captured outside of the current closure or function expression</h3><a class="heading-link" href="#captured-outer" aria-label="Link to 'Write captured outside of the current closure or function expression' section">#</a></div><p><strong>The cause:</strong> The variable you're trying to promote is write captured outside of a closure or function expression, but this use of the variable is inside of the closure or function expression that's trying to promote it.</p><p><strong>Example:</strong></p><div class="code-block-wrapper language-dart"><div class="code-block-body has-tag tag-bad"><span class="code-block-tag">bad</span><span class="code-block-language" title="Language dart">dart</span><pre class="shiki dash-light" tabindex="0"><code><span class="line"><span style="color:#D43324">void</span><span style="color:#6200EE"> f</span><span style="color:#222222">(</span><span style="color:#0468D7">int</span><span style="color:#222222">? i, </span><span style="color:#0468D7">int</span><span style="color:#222222">? j) {</span></span> <span class="line"><span style="color:#D43324"> var</span><span style="color:#222222"> foo = () {</span></span> <span class="line"><span style="color:#D43324"> if</span><span style="color:#222222"> (i == </span><span style="color:#11796D">null</span><span style="color:#222222">) </span><span style="color:#D43324">return</span><span style="color:#222222">;</span></span> <span class="line"><span style="color:#6200EE"> print</span><span style="color:#222222">(i.isEven); </span><span style="color:#6E6E70">// ERROR</span></span> <span class="line"><span style="color:#222222"> };</span></span> <span class="line"><span style="color:#D43324"> var</span><span style="color:#222222"> bar = () {</span></span> <span class="line"><span style="color:#222222"> i = j;</span></span> <span class="line"><span style="color:#222222"> };</span></span> <span class="line"><span style="color:#222222">}</span></span></code></pre></div></div><p>Flow analysis reasons that there's no way of telling what order <code>foo</code> and <code>bar</code> might be executed in; in fact, <code>bar</code> might even get executed halfway through executing <code>foo</code> (due to <code>foo</code> calling something that calls <code>bar</code>). So it isn't safe to promote <code>i</code> at all inside <code>foo</code>.</p><p><strong>Solution</strong>:</p><p>The best solution is probably to create a local variable:</p> <?code-excerpt "non_promotion/lib/non_promotion.dart (closure-write-capture)" replace="/var i2.*/[!$&!]/g;/(i2)( ==|\.)/[!i2!]$2/g"?> <div class="code-block-wrapper language-dart"><div class="code-block-body has-tag tag-good"><span class="code-block-tag">good</span><span class="code-block-language" title="Language dart">dart</span><pre class="shiki dash-light" tabindex="0"><code><span class="line"><span style="color:#D43324">void</span><span style="color:#6200EE"> f</span><span style="color:#222222">(</span><span style="color:#0468D7">int</span><span style="color:#222222">? i, </span><span style="color:#0468D7">int</span><span style="color:#222222">? j) {</span></span> <span class="line"><span style="color:#D43324"> var</span><span style="color:#222222"> foo = () {</span></span> <span class="line"><span style="color:#D43324"> </span><mark class="highlight"><span style="color:#D43324">var</span><span style="color:#222222"> i2 = i;</span></mark></span> <span class="line"><span style="color:#D43324"> if</span><span style="color:#222222"> (</span><mark class="highlight"><span style="color:#222222">i2</span></mark><span style="color:#222222"> == </span><span style="color:#11796D">null</span><span style="color:#222222">) </span><span style="color:#D43324">return</span><span style="color:#222222">;</span></span> <span class="line"><span style="color:#6200EE"> print</span><span style="color:#222222">(</span><mark class="highlight"><span style="color:#222222">i2</span></mark><span style="color:#222222">.isEven); </span><span style="color:#6E6E70">// OK because i2 is local to this closure.</span></span> <span class="line"><span style="color:#222222"> };</span></span> <span class="line"><span style="color:#D43324"> var</span><span style="color:#222222"> bar = () {</span></span> <span class="line"><span style="color:#222222"> i = j;</span></span> <span class="line"><span style="color:#222222"> };</span></span> <span class="line"><span style="color:#222222">}</span></span></code></pre></div></div><p id="page-github-links"><span>Unless stated otherwise, the documentation on this site reflects Dart 3.5.4. Page last updated on 2024-06-21.</span> <a href="https://github.com/dart-lang/site-www/tree/main/src/content/tools/non-promotion-reasons.md" target="_blank" rel="noopener">View source</a> <span>or </span><a href="https://github.com/dart-lang/site-www/issues/new?template=1_page_issue.yml&page-url=https://dart.dev/tools/non-promotion-reasons/&page-source=https://github.com/dart-lang/site-www/tree/main/src/content/tools/non-promotion-reasons.md" title="Report an issue with this page" target="_blank" rel="noopener">report an issue</a>.</p></div></article></main><footer id="page-footer"><div class="footer-section footer-main"><a href="/" class="brand" title="Dart"><img src="/assets/img/logo/logo-white-text.svg" alt="Dart" width="164"></a><div class="footer-social-links"><a href="https://medium.com/dartlang" target="_blank" rel="noopener" title="Medium blog"><svg><use href="/assets/img/social/medium.svg#medium"></use></svg> </a><a href="https://github.com/dart-lang" target="_blank" rel="noopener" title="GitHub"><svg><use href="/assets/img/social/github.svg#github"></use></svg> </a><a href="https://twitter.com/dart_lang" target="_blank" rel="noopener" title="X (Twitter)"><svg><use href="/assets/img/social/x.svg#x"></use></svg></a></div></div><div class="footer-section footer-tray"><div class="footer-licenses">Except as otherwise noted, this site is licensed under a <a href="https://creativecommons.org/licenses/by/4.0/">Creative Commons Attribution 4.0 International License</a>, and code samples are licensed under the <a href="https://opensource.org/licenses/BSD-3-Clause">3-Clause BSD License</a>.</div><div class="footer-utility-links"><ul><li><a href="/terms" title="Terms of use">Terms</a></li><li><a href="https://policies.google.com/privacy" target="_blank" rel="noopener" title="Privacy policy">Privacy</a></li><li><a href="/security" title="Security philosophy and practices">Security</a></li></ul></div></div></footer></body></html>