Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions src/resolver.rs
Original file line number Diff line number Diff line change
Expand Up @@ -214,6 +214,8 @@ impl TypeAnnotator<'_> {
}
StatementAnnotation::Value { resulting_type } => {
self.dependencies.insert(Dependency::Datatype(resulting_type.to_string()));
self.dependencies
.extend(self.get_datatype_dependencies(resulting_type, FxIndexSet::default()));
}
_ => (),
};
Expand Down
52 changes: 52 additions & 0 deletions src/resolver/tests/resolver_dependency_resolution.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1471,3 +1471,55 @@ fn cross_unit_function_with_typedef_array_parameter() {
assert!(dependencies.contains(&Dependency::Datatype("REAL".into())));
assert_eq!(dependencies.len(), 9);
}

#[test]
fn value_annotation_resolves_struct_member_dependencies() {
// When a cast expression like `MyStruct#(...)` produces a Value annotation with
// resulting_type = "MyStruct", the resolver must recursively resolve MyStruct's
// member types as dependencies. This is the mechanism that ensures itable struct
// types (and their function pointer members) are available during codegen.
//
// Simulates the post-lowering state where an FB in a separate unit casts a void
// pointer to an itable struct type: `__itable_IA#(reference.table^).foo^(...)`
let units = [
"
TYPE inner : STRUCT
x : DINT;
END_STRUCT
END_TYPE
",
"
TYPE wrapper : STRUCT
field : REF_TO inner;
END_STRUCT
END_TYPE
",
// A program that references `wrapper` only through a cast expression
// (not through a variable declaration), analogous to how the lowered
// itable dispatch references the itable struct type.
"
FUNCTION foo : DINT
VAR
ptr : REF_TO BYTE;
END_VAR
foo := wrapper#(ptr^).field^.x;
END_FUNCTION
",
];

let id_provider = IdProvider::default();
let (_unit1, index1) = index_with_ids(units[0], id_provider.clone());
let (_unit2, index2) = index_with_ids(units[1], id_provider.clone());
let (unit3, index3) = index_with_ids(units[2], id_provider.clone());
let mut index = index1;
index.import(index2);
index.import(index3);

let (_, dependencies, _) = TypeAnnotator::visit_unit(&index, &unit3, id_provider);
// The cast to `wrapper` must pull in wrapper and its member types
assert!(dependencies.contains(&Dependency::Datatype("foo".into())));
assert!(dependencies.contains(&Dependency::Datatype("wrapper".into())));
assert!(dependencies.contains(&Dependency::Datatype("__wrapper_field".into())));
assert!(dependencies.contains(&Dependency::Datatype("inner".into())));
assert!(dependencies.contains(&Dependency::Datatype("DINT".into())));
}
16 changes: 16 additions & 0 deletions tests/lit/multi/interface_consumer_separate_file/consumer.st
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
FUNCTION_BLOCK FbConsumer
VAR
stored : IFoo;
END_VAR

METHOD setRef
VAR_INPUT
iface : IFoo;
END_VAR
stored := iface;
END_METHOD

METHOD invoke : DINT
invoke := stored.bar();
END_METHOD
END_FUNCTION_BLOCK
4 changes: 4 additions & 0 deletions tests/lit/multi/interface_consumer_separate_file/iface.st
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
INTERFACE IFoo
METHOD bar : DINT
END_METHOD
END_INTERFACE
5 changes: 5 additions & 0 deletions tests/lit/multi/interface_consumer_separate_file/impl.st
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
FUNCTION_BLOCK FbImpl IMPLEMENTS IFoo
METHOD bar : DINT
bar := 42;
END_METHOD
END_FUNCTION_BLOCK
9 changes: 9 additions & 0 deletions tests/lit/multi/interface_consumer_separate_file/main.st
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
FUNCTION main
VAR
impl : FbImpl;
consumer : FbConsumer;
END_VAR

consumer.setRef(impl);
printf('%d$N', consumer.invoke());
END_FUNCTION
2 changes: 2 additions & 0 deletions tests/lit/multi/interface_consumer_separate_file/run.test
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
RUN: %COMPILE %S/main.st %S/iface.st %S/impl.st %S/consumer.st && %RUN | %CHECK %s
CHECK: 42
Loading