[core] Emit shared PROGMEM arrays at namespace scope

A setup() local table could only be reused from the scope of its first
use. The arrays are now globals. A lambda in a table only references
variables that codegen has already declared, so writing the global
straight away keeps the order valid.
This commit is contained in:
J. Nick Koston
2026-10-02 20:45:20 -05:00
parent ad1e5ef9d0
commit ebef8390aa
2 changed files with 10 additions and 8 deletions
+8 -7
View File
@@ -471,10 +471,10 @@ def progmem_array(id_, rhs) -> "MockObj":
def shared_progmem_array(
name: str, type_: "MockObjClass", rhs: SafeExpType
) -> "MockObj":
"""Emit a PROGMEM array once per distinct type and contents; later calls reuse it.
"""Emit a global PROGMEM array once per distinct type and contents; later calls reuse it.
The name is made unique against every config id and registered variable. Call it at
setup() top level: the array is a static local that later reuses must be able to see.
The array is ``static constexpr``, so elements must be constant expressions and lambdas
must be captureless. Its name is made unique against every config id and variable.
"""
from esphome.config import iter_ids
from esphome.config_validation import RESERVED_IDS
@@ -486,10 +486,11 @@ def shared_progmem_array(
used = {str(i) for i, _ in iter_ids(CORE.config)}
used |= {str(i) for i in CORE.variables}
used |= set(RESERVED_IDS) | CORE.loaded_integrations
unique = ensure_unique_string(name, used)
array = arrays[key] = progmem_array(
ID(unique, is_declaration=True, type=type_), rhs
)
id_ = ID(ensure_unique_string(name, used), is_declaration=True, type=type_)
# Global, so any scope can use it; anything a lambda references is already declared.
CORE.add_global(ProgmemAssignmentExpression(type_, id_, rhs))
array = arrays[key] = MockObj(id_, ".")
CORE.register_variable(id_, array)
return array
+2 -1
View File
@@ -819,7 +819,8 @@ class TestSharedProgmemArray:
c = cg.shared_progmem_array("table", ct.uint8, [4])
assert a is b
assert str(a) != str(c)
assert sum("PROGMEM" in str(st) for st in CORE.main_statements) == 2
assert sum("PROGMEM" in str(st) for st in CORE.global_statements) == 2
assert not any("PROGMEM" in str(st) for st in CORE.main_statements)
def test_same_contents_different_type_are_separate(self) -> None:
CORE.config = {}