[core] Let shared_progmem_array emit an unshared array

Identical lambdas can carry separate static state, so callers need a way
to keep such tables apart while still getting a collision free name.
This commit is contained in:
J. Nick Koston
2026-10-02 21:24:29 -05:00
parent 83b38d9cdd
commit 96df32024d
2 changed files with 21 additions and 10 deletions
+14 -10
View File
@@ -469,12 +469,13 @@ def progmem_array(id_, rhs) -> "MockObj":
def shared_progmem_array(
name: str, type_: "MockObjClass", rhs: SafeExpType
name: str, type_: "MockObjClass", rhs: SafeExpType, *, share: bool = True
) -> "MockObj":
"""Emit a global PROGMEM array once per distinct type and contents; later calls reuse it.
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.
``share=False`` always emits a new array, e.g. for lambdas that may keep static state.
"""
from esphome.config import iter_ids
from esphome.config_validation import RESERVED_IDS
@@ -482,15 +483,18 @@ def shared_progmem_array(
arrays: dict[str, MockObj] = CORE.data.setdefault("shared_progmem_array", {})
rhs = safe_exp(rhs)
key = f"{type_} {rhs}"
if (array := arrays.get(key)) is None:
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
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)
if share and (array := arrays.get(key)) is not None:
return 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
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 = MockObj(id_, ".")
CORE.register_variable(id_, array)
if share:
arrays[key] = array
return array
+7
View File
@@ -822,6 +822,13 @@ class TestSharedProgmemArray:
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_share_false_always_emits_a_new_array(self) -> None:
CORE.config = {}
a = cg.shared_progmem_array("table", ct.uint8, [1], share=False)
b = cg.shared_progmem_array("table", ct.uint8, [1], share=False)
assert str(a) != str(b)
assert sum("PROGMEM" in str(st) for st in CORE.global_statements) == 2
def test_same_contents_different_type_are_separate(self) -> None:
CORE.config = {}
a = cg.shared_progmem_array("table", ct.uint8, [1])