@@ -1352,7 +1352,7 @@ public function processStmtNode(
13521352 && ($ stmt ->keyVar !== null || $ byRefWithoutKey )
13531353 && (!$ hasExpr ->no () || !$ stmt ->expr instanceof Variable)
13541354 && $ exprType ->isArray ()->yes ()
1355- && $ exprType ->isConstantArray ()->no ()
1355+ && ( $ exprType ->isConstantArray ()->no () || $ stmt -> byRef )
13561356 ) {
13571357 $ nativeExprType = $ scope ->getNativeType ($ stmt ->expr );
13581358 $ arrayDimFetchLoopType = $ exprType ->getIterableValueType ();
@@ -1372,17 +1372,13 @@ public function processStmtNode(
13721372 $ keyLoopTypes = [];
13731373 foreach ($ scopesWithIterableValueType as $ scopeWithIterableValueType ) {
13741374 $ dimFetchType = $ scopeWithIterableValueType ->getType ($ arrayExprDimFetch );
1375- // Condition-based narrowings like `is_string($type)` apply to the value
1376- // variable but not automatically to the array dim fetch, even though the
1377- // two describe the same element for a given iteration. If the value var
1378- // hasn't been reassigned (OriginalForeachValueExpr still tracked) we use
1379- // the narrowed value-var type in place of the broader dim fetch type so
1380- // the loop's final array rewrite below picks up the sharper element type.
13811375 if ($ originalValueExpr !== null && $ scopeWithIterableValueType ->hasExpressionType ($ originalValueExpr )->yes ()) {
13821376 $ valueVarType = $ scopeWithIterableValueType ->getType ($ stmt ->valueVar );
13831377 if ($ dimFetchType ->isSuperTypeOf ($ valueVarType )->yes ()) {
13841378 $ dimFetchType = $ valueVarType ;
13851379 }
1380+ } elseif ($ stmt ->byRef && $ originalValueExpr !== null ) {
1381+ $ dimFetchType = $ scopeWithIterableValueType ->getType ($ stmt ->valueVar );
13861382 }
13871383 $ arrayDimFetchLoopTypes [] = $ dimFetchType ;
13881384 $ keyLoopTypes [] = $ scopeWithIterableValueType ->getType ($ stmt ->keyVar );
@@ -1400,6 +1396,8 @@ public function processStmtNode(
14001396 if ($ dimFetchNativeType ->isSuperTypeOf ($ valueVarNativeType )->yes ()) {
14011397 $ dimFetchNativeType = $ valueVarNativeType ;
14021398 }
1399+ } elseif ($ stmt ->byRef && $ originalValueExpr !== null ) {
1400+ $ dimFetchNativeType = $ scopeWithIterableValueType ->getNativeType ($ stmt ->valueVar );
14031401 }
14041402 $ arrayDimFetchLoopNativeTypes [] = $ dimFetchNativeType ;
14051403 $ keyLoopNativeTypes [] = $ scopeWithIterableValueType ->getType ($ stmt ->keyVar );
@@ -1432,6 +1430,10 @@ public function processStmtNode(
14321430 return $ traverse ($ type );
14331431 }
14341432
1433+ if ($ type ->isConstantArray ()->yes () && $ valueTypeChanged && !$ keyTypeChanged ) {
1434+ return $ type ->traverse (static fn () => $ arrayDimFetchLoopType );
1435+ }
1436+
14351437 if (!$ type instanceof ArrayType) {
14361438 return $ type ;
14371439 }
@@ -1446,6 +1448,10 @@ public function processStmtNode(
14461448 return $ traverse ($ type );
14471449 }
14481450
1451+ if ($ type ->isConstantArray ()->yes () && $ valueTypeChanged && !$ keyTypeChanged ) {
1452+ return $ type ->traverse (static fn () => $ arrayDimFetchLoopNativeType );
1453+ }
1454+
14491455 if (!$ type instanceof ArrayType) {
14501456 return $ type ;
14511457 }
0 commit comments