diff --git a/hilti/toolchain/src/compiler/optimizer.cc b/hilti/toolchain/src/compiler/optimizer.cc index d2cb43ec2..ea922a700 100644 --- a/hilti/toolchain/src/compiler/optimizer.cc +++ b/hilti/toolchain/src/compiler/optimizer.cc @@ -1477,6 +1477,18 @@ struct MemberVisitor : OptimizerVisitor, visitor::PreOrder } }; +// Helper function to detect whether a struct has lifecycle hooks attached. +bool hasLifecycleHooks(const type::Struct& s) { + // NOLINTNEXTLINE(readability-use-anyofallof) + for ( const auto& f : s.fields() ) { + // Currently the only lifecycle hook we have is `~finally`. + if ( f.id().str() == "~finally" ) + return true; + } + + return false; +}; + // Optimizer pass which removes dead store and variable declarations. struct DeadStoreVisitor : OptimizerVisitor, visitor::PreOrder { // We use decl identities here instead of their canonical IDs since for @@ -1546,10 +1558,9 @@ struct DeadStoreVisitor : OptimizerVisitor, visitor::PreOrder() && ! decl.isA() ) return false; - // Do not work on assignments to values of struct types since structs - // can have hooks attached which could make the assignment result - // visible non-locally. - if ( target->type().isA() ) + // Do not work on assignments to values of struct types which have + // lifecycle hooks attached. + if ( auto s = target->type().tryAs(); s && hasLifecycleHooks(*s) ) return false; switch ( _stage ) { @@ -1614,9 +1625,9 @@ struct DeadStoreVisitor : OptimizerVisitor, visitor::PreOrder() ) { - // Do not attempt to remove declarations of structs since - // they might have additional state attached via hooks. - if ( local->type().isA() ) + // Do not attempt to remove declarations of structs with + // lifecycle hooks. + if ( auto s = local->type().tryAs(); s && hasLifecycleHooks(*s) ) break; // If the local is initialized do not remove it to still @@ -1805,10 +1816,8 @@ struct DeadCodeVisitor : OptimizerVisitor, visitor::PreOrderflavor() == type::function::Flavor::Hook ) return true; - // If the function returns a struct type there might be hooks - // like `finally` attached to the type so calling the function - // has side effects. - if ( fn->result().type().isA() ) + // If the function returns a struct type with lifecycle hooks it has side effects. + if ( auto s = fn->result().type().tryAs(); s && hasLifecycleHooks(*s) ) return true; // If the function is not marked pure it has side effects. @@ -1834,17 +1843,6 @@ struct DeadCodeVisitor : OptimizerVisitor, visitor::PreOrder {