From 33ae8d0c33a44a0b348071c931b61c119cceca3d Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 11 Oct 2026 02:03:30 +0000 Subject: [PATCH] test: cover graph-ref alias boundaries with and without overlay bindings Pin that `@imported::name` may read only instance-independent consts and `@included::name` may read graph values, both when the resolver reaches the path and when an overlay binding the resolver cannot see supplies it. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01B2t1wANbEx5VhUyvdThmbW --- .../src/hir/expr_lower/tests.rs | 66 +++++++++++++++++++ 1 file changed, 66 insertions(+) diff --git a/crates/graphcal-compiler/src/hir/expr_lower/tests.rs b/crates/graphcal-compiler/src/hir/expr_lower/tests.rs index 1059393a6..efb0aafc4 100644 --- a/crates/graphcal-compiler/src/hir/expr_lower/tests.rs +++ b/crates/graphcal-compiler/src/hir/expr_lower/tests.rs @@ -863,6 +863,72 @@ fn include_output_ref_resolves_only_through_instance_bindings() { )); } +#[test] +fn source_graph_refs_keep_alias_rules_with_and_without_overlay_bindings() { + use crate::resolve::error::ExpectedDeclKind; + + let lib_id = DagId::root_in_package("test", "lib"); + let main_id = DagId::root_in_package("test", "main"); + let lib = desugared_source("pub node p: Dimensionless = 1.0;"); + let main = desugared_source( + "import lib as imported; + include lib() as included; + node local: Dimensionless = 0.0; + node imported_value: Dimensionless = @imported::p; + node included_value: Dimensionless = @included::p; + node imported_overlay: Dimensionless = @imported::generated; + node included_overlay: Dimensionless = @included::generated;", + ); + let resolver = resolver_with_import(&lib_id, &main_id, &lib, &main); + let scope = GenericScope::new(); + let time_zones = TimeZoneRegistry::bundled(); + let module = ModuleScope::new(&main_id, &resolver, &scope); + let no_units = HashMap::new(); + let no_templates = HashMap::new(); + let target = ResolvedDeclName::for_test(main_id.clone(), DeclName::expect_valid("local")); + let bindings = HashMap::from_iter(["imported", "included"].map(|alias| { + ( + ScopedName::in_scope( + crate::syntax::module_name::ModuleAliasName::expect_valid(alias), + DeclName::expect_valid("generated"), + ), + target.clone(), + ) + })); + + for (name, allowed) in [ + ("imported_value", false), + ("included_value", true), + ("imported_overlay", false), + ("included_overlay", true), + ] { + let result = lower_expr( + node_value(&main, name), + ExprLoweringContext::with_overlay( + module, + &time_zones, + BindingOverlay::Frozen(FrozenBindings { + unit_bindings: &no_units, + decl_bindings: &bindings, + instance_templates: &no_templates, + }), + ), + ); + match (allowed, result) { + (true, Ok(body)) => assert!(matches!(body.kind(), ExprKind::GraphRef(_))), + (false, Err(ExprLowerError::ModuleResolve { source, .. })) => assert!(matches!( + source, + ModuleResolveError::UnexpectedDeclKind { + expected: ExpectedDeclKind::InstanceIndependentConst, + actual: DeclSymbolKind::Node, + .. + } + )), + (_, result) => panic!("unexpected result for {name}: {result:?}"), + } + } +} + #[test] fn qualified_source_graph_ref_requires_a_module_alias() { let owner = DagId::root_in_package("test", "main");