@@ -685,11 +685,16 @@ public function processArgs(
685685 // still its bound there - observe the declared parameter type,
686686 // where such a template is uninformative and the receiver's
687687 // class-level arguments are already in place
688- $ scope = $ scope ->addTemplateArgumentConstraints ($ this ->templateArgumentObserver ->collectArgument (
689- $ this ->findOriginalParameterType ($ argMetadataAcceptor , $ parameter ) ?? $ parameter ->getType (),
690- $ gatheredArgTypeByIndex [$ i ],
691- ($ calleeReflection instanceof FunctionReflection || $ calleeReflection instanceof ExtendedMethodReflection) && $ calleeReflection ->isPure ()->yes (),
692- ));
688+ $ isPure = ($ calleeReflection instanceof FunctionReflection || $ calleeReflection instanceof ExtendedMethodReflection) && $ calleeReflection ->isPure ()->yes ();
689+ if ($ originalArg ->unpack ) {
690+ $ scope = $ this ->observeUnpackedArgument ($ scope , $ argMetadataAcceptor , $ i , $ gatheredArgTypeByIndex [$ i ], $ isPure );
691+ } else {
692+ $ scope = $ scope ->addTemplateArgumentConstraints ($ this ->templateArgumentObserver ->collectArgument (
693+ $ this ->findOriginalParameterType ($ argMetadataAcceptor , $ parameter ) ?? $ parameter ->getType (),
694+ $ gatheredArgTypeByIndex [$ i ],
695+ $ isPure ,
696+ ));
697+ }
693698 }
694699 }
695700
@@ -1274,6 +1279,64 @@ private function resolveClosureThisType(
12741279 return null ;
12751280 }
12761281
1282+ /**
1283+ * An unpacked argument passes its values, not itself: each value is observed
1284+ * against the parameter it lands in - by position, or by name for a string
1285+ * key. Values of an array whose keys are not known may land in any parameter
1286+ * from the argument's position on, and with string keys in any at all.
1287+ */
1288+ private function observeUnpackedArgument (MutatingScope $ scope , ParametersAcceptor $ acceptor , int $ position , Type $ unpackedType , bool $ isPure ): MutatingScope
1289+ {
1290+ $ parameters = $ acceptor ->getParameters ();
1291+ $ constantArrays = $ unpackedType ->getConstantArrays ();
1292+ if (count ($ constantArrays ) === 0 ) {
1293+ $ valueType = $ unpackedType ->getIterableValueType ();
1294+ $ from = $ unpackedType ->getIterableKeyType ()->isString ()->no () ? $ position : 0 ;
1295+ for ($ k = $ from ; $ k < count ($ parameters ); $ k ++) {
1296+ $ scope = $ this ->observeArgumentValue ($ scope , $ acceptor , $ parameters [$ k ], $ valueType , $ isPure );
1297+ }
1298+
1299+ return $ scope ;
1300+ }
1301+
1302+ foreach ($ constantArrays as $ constantArray ) {
1303+ $ valueTypes = $ constantArray ->getValueTypes ();
1304+ foreach ($ constantArray ->getKeyTypes () as $ j => $ keyType ) {
1305+ $ key = $ keyType ->getValue ();
1306+ $ parameter = null ;
1307+ if (is_string ($ key )) {
1308+ foreach ($ parameters as $ candidate ) {
1309+ if ($ candidate ->getName () !== $ key ) {
1310+ continue ;
1311+ }
1312+ $ parameter = $ candidate ;
1313+ break ;
1314+ }
1315+ } else {
1316+ $ parameter = $ parameters [$ position + $ j ] ?? null ;
1317+ }
1318+ if ($ parameter === null && $ acceptor ->isVariadic () && count ($ parameters ) > 0 ) {
1319+ $ parameter = $ parameters [count ($ parameters ) - 1 ];
1320+ }
1321+ if ($ parameter === null ) {
1322+ continue ;
1323+ }
1324+ $ scope = $ this ->observeArgumentValue ($ scope , $ acceptor , $ parameter , $ valueTypes [$ j ], $ isPure );
1325+ }
1326+ }
1327+
1328+ return $ scope ;
1329+ }
1330+
1331+ private function observeArgumentValue (MutatingScope $ scope , ParametersAcceptor $ acceptor , ParameterReflection $ parameter , Type $ valueType , bool $ isPure ): MutatingScope
1332+ {
1333+ return $ scope ->addTemplateArgumentConstraints ($ this ->templateArgumentObserver ->collectArgument (
1334+ $ this ->findOriginalParameterType ($ acceptor , $ parameter ) ?? $ parameter ->getType (),
1335+ $ valueType ,
1336+ $ isPure ,
1337+ ));
1338+ }
1339+
12771340 /**
12781341 * The parameter type an argument is observed against: the declared one with
12791342 * the template types the call already decided substituted - the receiver's
0 commit comments