@@ -4588,6 +4588,14 @@ impl JavaDialect for DexJavaDialect {
45884588 } ;
45894589 let ty = self
45904590 . source_register_type ( register)
4591+ // A source variable can span disjoint DEX register lifetimes. Do
4592+ // not let an earlier primitive lifetime type a reference-valued
4593+ // enhanced-for binding (or vice versa).
4594+ . filter ( |source| {
4595+ let source_is_primitive = matches ! ( source, JavaType :: Primitive ( _) ) ;
4596+ !( source_is_primitive && binding_type. is_reference ( )
4597+ || !source_is_primitive && binding_type. is_primitive ( ) )
4598+ } )
45914599 . cloned ( )
45924600 . map ( Ok )
45934601 . unwrap_or_else ( || self . source_type ( binding_type) ) ?;
@@ -4890,6 +4898,73 @@ impl JavaDialect for DexJavaDialect {
48904898 }
48914899}
48924900
4901+ #[ cfg( test) ]
4902+ mod tests {
4903+ use std:: collections:: BTreeMap ;
4904+ use std:: sync:: Arc ;
4905+
4906+ use crate :: ir:: analysis:: SourceTypeEnvironment ;
4907+ use crate :: ir:: { ArgType , RegisterArg } ;
4908+
4909+ use super :: { DexJavaDialect , JavaDialect , JavaMemberNames , JavaType } ;
4910+
4911+ #[ test]
4912+ fn foreach_binding_ignores_a_reused_primitive_source_variable_type ( ) {
4913+ let erased = ArgType :: object ( "java/util/ArrayList" ) ;
4914+ let reference = JavaType :: source_class ( "java.util.ArrayList" ) ;
4915+ let mut dialect = DexJavaDialect :: new (
4916+ true ,
4917+ None ,
4918+ & [ ] ,
4919+ & [ ] ,
4920+ & SourceTypeEnvironment :: default ( ) ,
4921+ BTreeMap :: from ( [ ( erased. clone ( ) , reference. clone ( ) ) ] ) ,
4922+ Arc :: new ( JavaMemberNames :: default ( ) ) ,
4923+ )
4924+ . expect ( "static dialect" ) ;
4925+ dialect. source_variable_types . insert ( 6 , JavaType :: int ( ) ) ;
4926+ let register = RegisterArg {
4927+ reg_num : 1 ,
4928+ ty : erased,
4929+ ssa_version : Some ( 3 ) ,
4930+ code_var : Some ( 6 ) ,
4931+ } ;
4932+
4933+ let ( ty, _) = JavaDialect :: loop_variable ( & mut dialect, & register)
4934+ . expect ( "reference-valued foreach binding" ) ;
4935+
4936+ assert_eq ! ( ty, reference) ;
4937+ }
4938+
4939+ #[ test]
4940+ fn foreach_binding_keeps_a_reference_source_type_over_object_erasure ( ) {
4941+ let erased = ArgType :: object ( "java/lang/Object" ) ;
4942+ let reference = JavaType :: source_class ( "example.Element" ) ;
4943+ let mut dialect = DexJavaDialect :: new (
4944+ true ,
4945+ None ,
4946+ & [ ] ,
4947+ & [ ] ,
4948+ & SourceTypeEnvironment :: default ( ) ,
4949+ BTreeMap :: from ( [ ( erased. clone ( ) , JavaType :: source_class ( "java.lang.Object" ) ) ] ) ,
4950+ Arc :: new ( JavaMemberNames :: default ( ) ) ,
4951+ )
4952+ . expect ( "static dialect" ) ;
4953+ dialect. source_variable_types . insert ( 6 , reference. clone ( ) ) ;
4954+ let register = RegisterArg {
4955+ reg_num : 1 ,
4956+ ty : erased,
4957+ ssa_version : Some ( 3 ) ,
4958+ code_var : Some ( 6 ) ,
4959+ } ;
4960+
4961+ let ( ty, _) =
4962+ JavaDialect :: loop_variable ( & mut dialect, & register) . expect ( "reference source type" ) ;
4963+
4964+ assert_eq ! ( ty, reference) ;
4965+ }
4966+ }
4967+
48934968#[ derive( Default ) ]
48944969struct SourceBindings {
48954970 registers : Vec < ( SemanticBindingKind , RegisterArg ) > ,
0 commit comments