2222#define strdup _strdup
2323#endif
2424
25+ /* Forward declarations for local helpers used before their definitions. */
26+ static EnvEntry * env_find_local (Env * env , const char * name );
27+
2528/* ================================================================== */
2629/* Thread-local snapshots for env_get_entry */
2730/* ================================================================== */
@@ -53,6 +56,7 @@ static void env_entry_snap_clear(EnvEntry* e) {
5356 free (e -> alias_target );
5457 e -> alias_target = NULL ;
5558 }
59+ e -> alias_target_env = NULL ;
5660 value_free (e -> value );
5761 e -> value = value_null ();
5862 e -> decl_type = TYPE_UNKNOWN ;
@@ -80,9 +84,23 @@ static void env_entry_snap_from_raw(EnvEntry* dst, const EnvEntry* src) {
8084 dst -> frozen = src -> frozen ;
8185 dst -> permafrozen = src -> permafrozen ;
8286 dst -> alias_target = src -> alias_target ? strdup (src -> alias_target ) : NULL ;
87+ dst -> alias_target_env = src -> alias_target_env ;
8388 dst -> value = value_copy (src -> value );
8489}
8590
91+ // Helper: like env_get_entry_raw but also returns the Env* where the
92+ // entry was found via out_env (if non-NULL).
93+ static EnvEntry * env_get_entry_raw_with_env (Env * env , const char * name , Env * * out_env ) {
94+ for (Env * e = env ; e != NULL ; e = e -> parent ) {
95+ EnvEntry * entry = env_find_local (e , name );
96+ if (entry ) {
97+ if (out_env ) * out_env = e ;
98+ return entry ;
99+ }
100+ }
101+ return NULL ;
102+ }
103+
86104/* ================================================================== */
87105/* Helpers */
88106/* ================================================================== */
@@ -143,11 +161,7 @@ static EnvEntry* env_find_local(Env* env, const char* name) {
143161}
144162
145163static EnvEntry * env_get_entry_raw (Env * env , const char * name ) {
146- for (Env * e = env ; e != NULL ; e = e -> parent ) {
147- EnvEntry * entry = env_find_local (e , name );
148- if (entry ) return entry ;
149- }
150- return NULL ;
164+ return env_get_entry_raw_with_env (env , name , NULL );
151165}
152166
153167static bool env_get_raw (Env * env , const char * name ,
@@ -158,10 +172,12 @@ static bool env_get_raw(Env* env, const char* name,
158172 if (entry ) {
159173 /* Follow alias chain to the final target */
160174 EnvEntry * cur = entry ;
175+ Env * cur_env = e ;
161176 int depth = 0 ;
162177 while (cur && cur -> alias_target ) {
163178 if (depth ++ > 256 ) return false; /* cycle or too deep */
164- cur = env_get_entry_raw (env , cur -> alias_target );
179+ Env * lookup_env = cur -> alias_target_env ? cur -> alias_target_env : cur_env ;
180+ cur = env_get_entry_raw_with_env (lookup_env , cur -> alias_target , & cur_env );
165181 }
166182 if (!cur ) return false;
167183 if (out_value ) * out_value = value_copy (cur -> value );
@@ -231,6 +247,7 @@ bool env_define_direct(Env* env, const char* name, DeclType type) {
231247 entry -> frozen = false;
232248 entry -> permafrozen = false;
233249 entry -> alias_target = NULL ;
250+ entry -> alias_target_env = NULL ;
234251 entry -> value = value_null ();
235252 return true;
236253}
@@ -243,7 +260,9 @@ bool env_assign_direct(Env* env, const char* name, Value value,
243260 /* Route through alias chain */
244261 if (entry -> alias_target ) {
245262 const char * target_name = entry -> alias_target ;
246- EnvEntry * target = env_get_entry_raw (env , target_name );
263+ Env * lookup_env = entry -> alias_target_env ? entry -> alias_target_env : e ;
264+ Env * target_env_found = NULL ;
265+ EnvEntry * target = env_get_entry_raw_with_env (lookup_env , target_name , & target_env_found );
247266 if (!target ) return false;
248267 if (type != TYPE_UNKNOWN && target -> decl_type != type ) return false;
249268 DeclType actual_type = env_decl_type_from_value (value );
@@ -298,6 +317,7 @@ bool env_delete_direct(Env* env, const char* name) {
298317 if (entry -> alias_target ) {
299318 free (entry -> alias_target );
300319 entry -> alias_target = NULL ;
320+ entry -> alias_target_env = NULL ;
301321 }
302322 entry -> initialized = false;
303323 entry -> value = value_null ();
@@ -312,17 +332,82 @@ bool env_set_alias_direct(Env* env, const char* name,
312332 bool declare_if_missing ) {
313333 if (!env || !name || !target_name ) return false;
314334
315- /* Ensure the target exists */
316- EnvEntry * target = env_get_entry_raw (env , target_name );
335+ /* Ensure the target exists; capture the Env where it was found */
336+ Env * target_env_found = NULL ;
337+ EnvEntry * target = env_get_entry_raw_with_env (env , target_name , & target_env_found );
338+ if (!target ) return false;
339+
340+ /* Resolve final target through alias chain; detect cycles */
341+ EnvEntry * cur = target ;
342+ Env * cur_env = target_env_found ;
343+ int depth = 0 ;
344+ while (cur && cur -> alias_target ) {
345+ if (depth ++ > 256 ) return false;
346+ if (strcmp (cur -> alias_target , name ) == 0 ) return false; /* cycle */
347+ Env * lookup_env = cur -> alias_target_env ? cur -> alias_target_env : cur_env ;
348+ cur = env_get_entry_raw_with_env (lookup_env , cur -> alias_target , & cur_env );
349+ }
350+ if (!cur ) return false;
351+
352+ /* Disallow aliasing to frozen / permafrozen target */
353+ if (cur -> frozen || cur -> permafrozen ) return false;
354+
355+ /* Type compatibility */
356+ if (type != TYPE_UNKNOWN && type != cur -> decl_type ) return false;
357+
358+ /* Find existing local entry (but don't create it yet). Only create
359+ the local entry after all validation succeeds to avoid leaving a
360+ declared-but-uninitialized binding when alias setup fails. */
361+ EnvEntry * entry = env_find_local (env , name );
362+ if (!entry ) {
363+ if (!declare_if_missing ) return false;
364+ /* create now */
365+ if (!env_define_direct (env , name , type )) return false;
366+ entry = env_find_local (env , name );
367+ if (!entry ) return false;
368+ }
369+
370+ /* Respect frozen state on the entry itself */
371+ if (entry -> frozen || entry -> permafrozen ) return false;
372+
373+ /* Overwrite declared type with target's type */
374+ entry -> decl_type = cur -> decl_type ;
375+
376+ /* Clear any stored value and set alias */
377+ if (entry -> initialized ) {
378+ value_free (entry -> value );
379+ entry -> initialized = false;
380+ entry -> value = value_null ();
381+ }
382+ if (entry -> alias_target ) {
383+ free (entry -> alias_target );
384+ entry -> alias_target = NULL ;
385+ }
386+ entry -> alias_target = strdup (cur -> name );
387+ /* Record the Env where the final target was located so alias
388+ resolution works even when target lives in a different Env tree. */
389+ entry -> alias_target_env = cur_env ;
390+ entry -> initialized = true; /* alias is considered initialised */
391+ return true;
392+ }
393+
394+ bool env_set_alias_cross (Env * env , const char * name , Env * target_env , const char * target_name , DeclType type , bool declare_if_missing ) {
395+ if (!env || !name || !target_env || !target_name ) return false;
396+
397+ /* Ensure the target exists; capture the Env where it was found */
398+ Env * target_env_found = NULL ;
399+ EnvEntry * target = env_get_entry_raw_with_env (target_env , target_name , & target_env_found );
317400 if (!target ) return false;
318401
319402 /* Resolve final target through alias chain; detect cycles */
320403 EnvEntry * cur = target ;
404+ Env * cur_env = target_env_found ;
321405 int depth = 0 ;
322406 while (cur && cur -> alias_target ) {
323407 if (depth ++ > 256 ) return false;
324408 if (strcmp (cur -> alias_target , name ) == 0 ) return false; /* cycle */
325- cur = env_get_entry_raw (env , cur -> alias_target );
409+ Env * lookup_env = cur -> alias_target_env ? cur -> alias_target_env : cur_env ;
410+ cur = env_get_entry_raw_with_env (lookup_env , cur -> alias_target , & cur_env );
326411 }
327412 if (!cur ) return false;
328413
@@ -361,6 +446,7 @@ bool env_set_alias_direct(Env* env, const char* name,
361446 entry -> alias_target = NULL ;
362447 }
363448 entry -> alias_target = strdup (cur -> name );
449+ entry -> alias_target_env = cur_env ;
364450 entry -> initialized = true; /* alias is considered initialised */
365451 return true;
366452}
0 commit comments