packages/deriver/tests/Unit/Deriver/Evaluator/Execution/Call/ParameterBindingTest.php

1<?php
2
3declare (strict_types=1);
4
5namespace Tests\Unit\Deriver\Evaluator\Execution\Call;
6
7use Deriver\Deriver;
8use Deriver\Deriver\Candidate\ResultRef;
9use Deriver\Deriver\Candidate\Snapshot;
10use Deriver\Deriver\Candidate\Statistics;
11use Deriver\Deriver\Contributions;
12use Deriver\Deriver\DeriverConfig;
13use Deriver\Deriver\Evaluator\Call\BoundInstruction;
14use Deriver\Deriver\Evaluator\Call\Evaluators;
15use Deriver\Deriver\Evaluator\Call\Extensions;
16use Deriver\Deriver\Evaluator\Call\Registrations;
17use Deriver\Deriver\Evaluator\Call\StateRegistry;
18use Deriver\Deriver\Evaluator\EvaluatorConfig;
19use Deriver\Deriver\Evaluator\Evidence\Frontier;
20use Deriver\Deriver\Evaluator\Execution\Call\Allocation;
21use Deriver\Deriver\Evaluator\Execution\Call\ArgumentBinding;
22use Deriver\Deriver\Evaluator\Execution\Call\ArgumentOrder;
23use Deriver\Deriver\Evaluator\Execution\Call\CallableCheck;
24use Deriver\Deriver\Evaluator\Execution\Call\CallExecutor;
25use Deriver\Deriver\Evaluator\Execution\Call\CallResolution;
26use Deriver\Deriver\Evaluator\Execution\Call\Creation\Access;
27use Deriver\Deriver\Evaluator\Execution\Call\Member\Invocation;
28use Deriver\Deriver\Evaluator\Execution\Call\Native\Invocation as NativeInvocation;
29use Deriver\Deriver\Evaluator\Execution\Call\Native\Properties;
30use Deriver\Deriver\Evaluator\Execution\Call\Native\Signatures;
31use Deriver\Deriver\Evaluator\Execution\Call\ParameterBinding;
32use Deriver\Deriver\Evaluator\Execution\Call\PassedArgument;
33use Deriver\Deriver\Evaluator\Execution\Call\Preparation\Arguments;
34use Deriver\Deriver\Evaluator\Execution\Call\Preparation\Creation;
35use Deriver\Deriver\Evaluator\Execution\Call\Preparation\Modes;
36use Deriver\Deriver\Evaluator\Execution\Call\Preparation\Resolution;
37use Deriver\Deriver\Evaluator\Execution\Call\Preparation\Target;
38use Deriver\Deriver\Evaluator\Execution\Call\Preparation\Transfer;
39use Deriver\Deriver\Evaluator\Execution\Call\TypeBinding;
40use Deriver\Deriver\Evaluator\Execution\Call\TypeCheck;
41use Deriver\Deriver\Evaluator\Execution\Completion\Alternative;
42use Deriver\Deriver\Evaluator\Execution\Completion\Assessment;
43use Deriver\Deriver\Evaluator\Execution\Completion\Completion;
44use Deriver\Deriver\Evaluator\Execution\Completion\CompletionKind;
45use Deriver\Deriver\Evaluator\Execution\Completion\StorageSnapshot;
46use Deriver\Deriver\Evaluator\Execution\Constraints;
47use Deriver\Deriver\Evaluator\Execution\Context;
48use Deriver\Deriver\Evaluator\Execution\Control\ExceptionChain;
49use Deriver\Deriver\Evaluator\Execution\Control\ExceptionMatch;
50use Deriver\Deriver\Evaluator\Execution\Control\Handler;
51use Deriver\Deriver\Evaluator\Execution\Control\ObservationLimit;
52use Deriver\Deriver\Evaluator\Execution\Control\StateJoin;
53use Deriver\Deriver\Evaluator\Execution\Control\Unwinding;
54use Deriver\Deriver\Evaluator\Execution\Demand\Cell;
55use Deriver\Deriver\Evaluator\Execution\Demand\Components;
56use Deriver\Deriver\Evaluator\Execution\Demand\Discovery;
57use Deriver\Deriver\Evaluator\Execution\Demand\Key;
58use Deriver\Deriver\Evaluator\Execution\Demand\Table;
59use Deriver\Deriver\Evaluator\Execution\Dependencies;
60use Deriver\Deriver\Evaluator\Execution\Evaluator;
61use Deriver\Deriver\Evaluator\Execution\EvaluatorConfig as ExecutionEvaluatorConfig;
62use Deriver\Deriver\Evaluator\Execution\Havoc;
63use Deriver\Deriver\Evaluator\Execution\InstructionTransfer;
64use Deriver\Deriver\Evaluator\Execution\Memory\Location;
65use Deriver\Deriver\Evaluator\Execution\Memory\Materialization;
66use Deriver\Deriver\Evaluator\Execution\Memory\Memory;
67use Deriver\Deriver\Evaluator\Execution\Memory\ReferenceConstraint;
68use Deriver\Deriver\Evaluator\Execution\Memory\Slots\SlotReference;
69use Deriver\Deriver\Evaluator\Execution\Memory\Slots\StateStorage;
70use Deriver\Deriver\Evaluator\Execution\Memory\StorageCapture;
71use Deriver\Deriver\Evaluator\Execution\ObservationCollector;
72use Deriver\Deriver\Evaluator\Execution\Operation\Conversions;
73use Deriver\Deriver\Evaluator\Execution\Operation\ScalarErrors;
74use Deriver\Deriver\Evaluator\Execution\State;
75use Deriver\Deriver\Evaluator\Execution\Summary\CompletionRecord;
76use Deriver\Deriver\Evaluator\Execution\Summary\Evaluation;
77use Deriver\Deriver\Evaluator\Execution\Summary\Invocation as SummaryInvocation;
78use Deriver\Deriver\Evaluator\Execution\Summary\Isolation;
79use Deriver\Deriver\Evaluator\Execution\Trace\Derivation;
80use Deriver\Deriver\Evaluator\Execution\Transfer\MemoryStep;
81use Deriver\Deriver\Evaluator\Execution\Transfer\ObjectAccess;
82use Deriver\Deriver\Evaluator\Execution\Transfer\PropertyAccessCheck;
83use Deriver\Deriver\Evaluator\Execution\Transfer\PropertyLookup;
84use Deriver\Deriver\Evaluator\Execution\Transfer\PropertyReference;
85use Deriver\Deriver\Evaluator\Execution\Transfer\PropertySlot;
86use Deriver\Deriver\Evaluator\Execution\Transfer\PropertyTransfer;
87use Deriver\Deriver\Evaluator\Execution\Transfer\PureStep;
88use Deriver\Deriver\Evaluator\Execution\Transfer\ReferenceAssignment;
89use Deriver\Deriver\Evaluator\Request\Budget;
90use Deriver\Deriver\Evaluator\Request\EntryPoint;
91use Deriver\Deriver\Evaluator\Request\QueryScope;
92use Deriver\Deriver\Evaluator\Request\ReturnQuery;
93use Deriver\Deriver\Evaluator\Scalar\Arrays;
94use Deriver\Deriver\Evaluator\Scalar\Identity;
95use Deriver\Deriver\Evaluator\Scalar\IntegerConversion;
96use Deriver\Deriver\Evaluator\Scalar\NumericString;
97use Deriver\Deriver\Evaluator\Scalar\Operations;
98use Deriver\Deriver\Evaluator\TargetProfile;
99use Deriver\Deriver\Evaluator\Term;
100use Deriver\Deriver\Evaluator\TermKind;
101use Deriver\Deriver\Execution\DerivationResult;
102use Deriver\Deriver\Execution\QueryExecution;
103use Deriver\Deriver\Execution\ResultAssessment;
104use Deriver\Deriver\Identity\QueryEncoding;
105use Deriver\Deriver\Identity\ValueGraph;
106use Deriver\Deriver\Lowerer\AggregateLowering;
107use Deriver\Deriver\Lowerer\AssignmentLowering;
108use Deriver\Deriver\Lowerer\CallLowering;
109use Deriver\Deriver\Lowerer\Control\DestructuringLowering;
110use Deriver\Deriver\Lowerer\Control\ExceptionLowering;
111use Deriver\Deriver\Lowerer\Declaration\CallableIdentity;
112use Deriver\Deriver\Lowerer\Declaration\CallableSource;
113use Deriver\Deriver\Lowerer\Declaration\ConstantSignatures;
114use Deriver\Deriver\Lowerer\Declaration\Index;
115use Deriver\Deriver\Lowerer\Declaration\Indexer;
116use Deriver\Deriver\Lowerer\Declaration\Metadata\ClassDeclaration;
117use Deriver\Deriver\Lowerer\Declaration\Metadata\PropertyDeclaration;
118use Deriver\Deriver\Lowerer\Declaration\Traits\Composition;
119use Deriver\Deriver\Lowerer\ExpressionLowering;
120use Deriver\Deriver\Lowerer\Graph\Argument;
121use Deriver\Deriver\Lowerer\Graph\BasicBlock;
122use Deriver\Deriver\Lowerer\Graph\CallableGraph;
123use Deriver\Deriver\Lowerer\Graph\CatchTarget;
124use Deriver\Deriver\Lowerer\Graph\ExceptionRegion;
125use Deriver\Deriver\Lowerer\Graph\Instruction;
126use Deriver\Deriver\Lowerer\Graph\Parameter;
127use Deriver\Deriver\Lowerer\Graph\Terminator;
128use Deriver\Deriver\Lowerer\Graph\TerminatorKind;
129use Deriver\Deriver\Lowerer\Hierarchy\Access as HierarchyAccess;
130use Deriver\Deriver\Lowerer\Hierarchy\Builtins;
131use Deriver\Deriver\Lowerer\Hierarchy\Dispatch;
132use Deriver\Deriver\Lowerer\InstructionEmitter;
133use Deriver\Deriver\Lowerer\Lowerer;
134use Deriver\Deriver\Lowerer\LoweringScope;
135use Deriver\Deriver\Lowerer\StatementLowering;
136use Deriver\Deriver\MicroEvaluatorConversion;
137use Deriver\Deriver\Parser\Node\File;
138use Deriver\Deriver\Parser\Parser;
139use Deriver\Deriver\Parser\ParserConfig;
140use Deriver\Deriver\Parser\TargetSyntax;
141use Deriver\Deriver\Providers\ProviderExpansion;
142use Deriver\Deriver\Resolver\AssignmentPatterns;
143use Deriver\Deriver\Resolver\ClassScope;
144use Deriver\Deriver\Resolver\ExpressionValidation;
145use Deriver\Deriver\Resolver\MagicContext;
146use Deriver\Deriver\Resolver\Resolver;
147use Deriver\Deriver\Selection\QueryValidation;
148use Deriver\Deriver\SnapshotIdentity;
149use Deriver\Deriver\Sources\LineMap;
150use Deriver\Deriver\Sources\Path;
151use Deriver\Deriver\Sources\Source;
152use Deriver\Deriver\Sources\SourceRef;
153use Deriver\Deriver\Sources\Sources;
154use Deriver\Exception\ParserException;
155use Deriver\Exception\ResolutionException;
156use PHPUnit\Framework\Attributes\CoversClass;
157use PHPUnit\Framework\Attributes\DataProvider;
158use PHPUnit\Framework\Attributes\Small;
159use PHPUnit\Framework\Attributes\UsesClass;
160use PHPUnit\Framework\TestCase;
161
162#[UsesClass(Resolver::class)]
163#[UsesClass(ExpressionValidation::class)]
164#[UsesClass(ResolutionException::class)]
165#[UsesClass(ParserConfig::class)]
166#[UsesClass(ParserException::class)]
167#[CoversClass(ParameterBinding::class)]
168#[UsesClass(QueryExecution::class)]
169#[UsesClass(QueryValidation::class)]
170#[UsesClass(ResultAssessment::class)]
171#[UsesClass(Deriver::class)]
172#[UsesClass(Constraints::class)]
173#[UsesClass(Argument::class)]
174#[UsesClass(BasicBlock::class)]
175#[UsesClass(CallableGraph::class)]
176#[UsesClass(CallableIdentity::class)]
177#[UsesClass(CatchTarget::class)]
178#[UsesClass(ClassDeclaration::class)]
179#[UsesClass(ExceptionRegion::class)]
180#[UsesClass(Instruction::class)]
181#[UsesClass(Parameter::class)]
182#[UsesClass(PropertyDeclaration::class)]
183#[UsesClass(Terminator::class)]
184#[UsesClass(Allocation::class)]
185#[UsesClass(ArgumentBinding::class)]
186#[UsesClass(ArgumentOrder::class)]
187#[UsesClass(CallExecutor::class)]
188#[UsesClass(CallResolution::class)]
189#[UsesClass(CallableCheck::class)]
190#[UsesClass(Access::class)]
191#[UsesClass(Builtins::class)]
192#[UsesClass(Dispatch::class)]
193#[UsesClass(HierarchyAccess::class)]
194#[UsesClass(Invocation::class)]
195#[UsesClass(NativeInvocation::class)]
196#[UsesClass(Properties::class)]
197#[UsesClass(Signatures::class)]
198#[UsesClass(PassedArgument::class)]
199#[UsesClass(Arguments::class)]
200#[UsesClass(Creation::class)]
201#[UsesClass(Modes::class)]
202#[UsesClass(Resolution::class)]
203#[UsesClass(Target::class)]
204#[UsesClass(Transfer::class)]
205#[UsesClass(TypeBinding::class)]
206#[UsesClass(TypeCheck::class)]
207#[UsesClass(Completion::class)]
208#[UsesClass(Context::class)]
209#[UsesClass(ExceptionChain::class)]
210#[UsesClass(ExceptionMatch::class)]
211#[UsesClass(Handler::class)]
212#[UsesClass(ObservationLimit::class)]
213#[UsesClass(StateJoin::class)]
214#[UsesClass(Unwinding::class)]
215#[UsesClass(Cell::class)]
216#[UsesClass(Components::class)]
217#[UsesClass(Discovery::class)]
218#[UsesClass(Key::class)]
219#[UsesClass(Table::class)]
220#[UsesClass(Dependencies::class)]
221#[UsesClass(Havoc::class)]
222#[UsesClass(InstructionTransfer::class)]
223#[UsesClass(Evaluator::class)]
224#[UsesClass(SlotReference::class)]
225#[UsesClass(StateStorage::class)]
226#[UsesClass(ObservationCollector::class)]
227#[UsesClass(Conversions::class)]
228#[UsesClass(ScalarErrors::class)]
229#[UsesClass(State::class)]
230#[UsesClass(CompletionRecord::class)]
231#[UsesClass(Evaluation::class)]
232#[UsesClass(SummaryInvocation::class)]
233#[UsesClass(Isolation::class)]
234#[UsesClass(MemoryStep::class)]
235#[UsesClass(ObjectAccess::class)]
236#[UsesClass(PropertyAccessCheck::class)]
237#[UsesClass(PropertyLookup::class)]
238#[UsesClass(PropertyReference::class)]
239#[UsesClass(PropertySlot::class)]
240#[UsesClass(PropertyTransfer::class)]
241#[UsesClass(PureStep::class)]
242#[UsesClass(ReferenceAssignment::class)]
243#[UsesClass(Location::class)]
244#[UsesClass(Materialization::class)]
245#[UsesClass(Memory::class)]
246#[UsesClass(ReferenceConstraint::class)]
247#[UsesClass(StorageCapture::class)]
248#[UsesClass(Extensions::class)]
249#[UsesClass(ProviderExpansion::class)]
250#[UsesClass(Evaluators::class)]
251#[UsesClass(StateRegistry::class)]
252#[UsesClass(DeriverConfig::class)]
253#[UsesClass(EntryPoint::class)]
254#[UsesClass(Sources::class)]
255#[UsesClass(Path::class)]
256#[UsesClass(Snapshot::class)]
257#[UsesClass(Source::class)]
258#[UsesClass(TargetProfile::class)]
259#[UsesClass(Budget::class)]
260#[UsesClass(QueryScope::class)]
261#[UsesClass(ReturnQuery::class)]
262#[UsesClass(ResultRef::class)]
263#[UsesClass(SourceRef::class)]
264#[UsesClass(Alternative::class)]
265#[UsesClass(Assessment::class)]
266#[UsesClass(Derivation::class)]
267#[UsesClass(DerivationResult::class)]
268#[UsesClass(Frontier::class)]
269#[UsesClass(QueryEncoding::class)]
270#[UsesClass(ValueGraph::class)]
271#[UsesClass(Statistics::class)]
272#[UsesClass(StorageSnapshot::class)]
273#[UsesClass(Parser::class)]
274#[UsesClass(File::class)]
275#[UsesClass(AggregateLowering::class)]
276#[UsesClass(AssignmentLowering::class)]
277#[UsesClass(CallLowering::class)]
278#[UsesClass(Lowerer::class)]
279#[UsesClass(DestructuringLowering::class)]
280#[UsesClass(ExceptionLowering::class)]
281#[UsesClass(ExpressionLowering::class)]
282#[UsesClass(InstructionEmitter::class)]
283#[UsesClass(LoweringScope::class)]
284#[UsesClass(StatementLowering::class)]
285#[UsesClass(ConstantSignatures::class)]
286#[UsesClass(CallableSource::class)]
287#[UsesClass(Indexer::class)]
288#[UsesClass(Index::class)]
289#[UsesClass(Composition::class)]
290#[UsesClass(LineMap::class)]
291#[UsesClass(MagicContext::class)]
292#[UsesClass(AssignmentPatterns::class)]
293#[UsesClass(ClassScope::class)]
294#[UsesClass(TargetSyntax::class)]
295#[UsesClass(Arrays::class)]
296#[UsesClass(Identity::class)]
297#[UsesClass(IntegerConversion::class)]
298#[UsesClass(NumericString::class)]
299#[UsesClass(Operations::class)]
300#[UsesClass(Term::class)]
301#[Small]
302final class ParameterBindingTest extends TestCase
303{
304    public function testBindPreservesTheSemanticContract(): void
305    {
306        $source = '<?php class Box{function __construct(public string $value){}}function target(){return (new Box("yes"))->value;}';
307        $symbol = 'target';
308        $input = new Sources([new Source('fixture.php', $source)]);
309        $configuration = new DeriverConfig();
310        $contributions = new Contributions();
311        $target = new TargetProfile($configuration->phpVersion, $configuration->integerBits, $configuration->floatPrecision);
312        $providers = new ProviderExpansion($input, array_map(Term::fromNative(...), $configuration->environment), $contributions);
313        $contributions1 = $providers->contributions;
314        $evaluators = new Evaluators(new TargetProfile($configuration->phpVersion, $configuration->integerBits, $configuration->floatPrecision), new Registrations($contributions1->evaluators, $contributions1->providers, $contributions1->domains, $contributions1->intrinsics, $contributions1->stateSlots), $configuration->standardEvaluators);
315        $settings = new EvaluatorConfig($target, $providers->environment, $configuration->standardEvaluators, array_map((new MicroEvaluatorConversion())->convert(...), $configuration->evaluators));
316        $identity = new SnapshotIdentity($providers->input, $settings, $evaluators, $providers->manifest);
317        $snapshotId = $identity->id;
318        $input1 = $providers->input;
319        $parser = new Parser(new ParserConfig($target->version));
320        $trees = array_map(static fn (Source $source1): File => (new Resolver())->resolve($parser->parse(new Source(Path::normalize($source1->path), $source1->contents, $source1->declarationsOnly))), iterator_to_array($input1, false));
321        $program = new Index($snapshotId, $trees);
322        $result = (new QueryExecution($program, new ExecutionEvaluatorConfig($target, $providers->environment, $configuration->standardEvaluators), $evaluators, $identity->snapshot(array_map(Frontier::fromDiagnostic(...), $program->diagnostics()))))->derive(new ReturnQuery($symbol));
323        self::assertSame('yes', $result->normalOutcomes[0]->values['return']->native());
324        self::assertSame([], $result->frontiers);
325        self::assertSame([], $result->exceptionalOutcomes);
326    }
327    public function testVariadicPreservesNamedKeysAndChecksEveryElement(): void
328    {
329        $source = '<?php function f(int ...$items){return $items;}function target(){return f(1,second:2);}';
330        $symbol = 'target';
331        $input = new Sources([new Source('fixture.php', $source)]);
332        $configuration = new DeriverConfig();
333        $contributions = new Contributions();
334        $target = new TargetProfile($configuration->phpVersion, $configuration->integerBits, $configuration->floatPrecision);
335        $providers = new ProviderExpansion($input, array_map(Term::fromNative(...), $configuration->environment), $contributions);
336        $contributions1 = $providers->contributions;
337        $evaluators = new Evaluators(new TargetProfile($configuration->phpVersion, $configuration->integerBits, $configuration->floatPrecision), new Registrations($contributions1->evaluators, $contributions1->providers, $contributions1->domains, $contributions1->intrinsics, $contributions1->stateSlots), $configuration->standardEvaluators);
338        $settings = new EvaluatorConfig($target, $providers->environment, $configuration->standardEvaluators, array_map((new MicroEvaluatorConversion())->convert(...), $configuration->evaluators));
339        $identity = new SnapshotIdentity($providers->input, $settings, $evaluators, $providers->manifest);
340        $snapshotId = $identity->id;
341        $input1 = $providers->input;
342        $parser = new Parser(new ParserConfig($target->version));
343        $trees = array_map(static fn (Source $source1): File => (new Resolver())->resolve($parser->parse(new Source(Path::normalize($source1->path), $source1->contents, $source1->declarationsOnly))), iterator_to_array($input1, false));
344        $program = new Index($snapshotId, $trees);
345        $result = (new QueryExecution($program, new ExecutionEvaluatorConfig($target, $providers->environment, $configuration->standardEvaluators), $evaluators, $identity->snapshot(array_map(Frontier::fromDiagnostic(...), $program->diagnostics()))))->derive(new ReturnQuery($symbol));
346        self::assertSame([], $result->frontiers);
347        self::assertSame([], $result->exceptionalOutcomes);
348        self::assertCount(1, $result->normalOutcomes);
349        self::assertSame([0 => 1, 'second' => 2], $result->normalOutcomes[0]->values['return']->native());
350    }
351    public function testDefaultArgumentRunsOnlyWhenTheCallerOmitsIt(): void
352    {
353        $source = '<?php function f($x=3){return $x;}function target(){return [f(),f(9)];}';
354        $symbol = 'target';
355        $input = new Sources([new Source('fixture.php', $source)]);
356        $configuration = new DeriverConfig();
357        $contributions = new Contributions();
358        $target = new TargetProfile($configuration->phpVersion, $configuration->integerBits, $configuration->floatPrecision);
359        $providers = new ProviderExpansion($input, array_map(Term::fromNative(...), $configuration->environment), $contributions);
360        $contributions1 = $providers->contributions;
361        $evaluators = new Evaluators(new TargetProfile($configuration->phpVersion, $configuration->integerBits, $configuration->floatPrecision), new Registrations($contributions1->evaluators, $contributions1->providers, $contributions1->domains, $contributions1->intrinsics, $contributions1->stateSlots), $configuration->standardEvaluators);
362        $settings = new EvaluatorConfig($target, $providers->environment, $configuration->standardEvaluators, array_map((new MicroEvaluatorConversion())->convert(...), $configuration->evaluators));
363        $identity = new SnapshotIdentity($providers->input, $settings, $evaluators, $providers->manifest);
364        $snapshotId = $identity->id;
365        $input1 = $providers->input;
366        $parser = new Parser(new ParserConfig($target->version));
367        $trees = array_map(static fn (Source $source1): File => (new Resolver())->resolve($parser->parse(new Source(Path::normalize($source1->path), $source1->contents, $source1->declarationsOnly))), iterator_to_array($input1, false));
368        $program = new Index($snapshotId, $trees);
369        $result = (new QueryExecution($program, new ExecutionEvaluatorConfig($target, $providers->environment, $configuration->standardEvaluators), $evaluators, $identity->snapshot(array_map(Frontier::fromDiagnostic(...), $program->diagnostics()))))->derive(new ReturnQuery($symbol));
370        self::assertSame([], $result->frontiers);
371        self::assertSame([], $result->exceptionalOutcomes);
372        self::assertCount(1, $result->normalOutcomes);
373        self::assertSame([3, 9], $result->normalOutcomes[0]->values['return']->native());
374    }
375    public function testVariadicPreservesTypedPropertyConstraintsBeforeCreatingAReference(): void
376    {
377        $source = '<?php class B{public int $x=1;}function change(float &...$values){$values[0]=2.5;}function target(){$b=new B;try{change($b->x);}catch(TypeError $e){return $b->x;}return 999;}';
378        $symbol = 'target';
379        $input = new Sources([new Source('fixture.php', $source)]);
380        $configuration = new DeriverConfig();
381        $contributions = new Contributions();
382        $target = new TargetProfile($configuration->phpVersion, $configuration->integerBits, $configuration->floatPrecision);
383        $providers = new ProviderExpansion($input, array_map(Term::fromNative(...), $configuration->environment), $contributions);
384        $contributions1 = $providers->contributions;
385        $evaluators = new Evaluators(new TargetProfile($configuration->phpVersion, $configuration->integerBits, $configuration->floatPrecision), new Registrations($contributions1->evaluators, $contributions1->providers, $contributions1->domains, $contributions1->intrinsics, $contributions1->stateSlots), $configuration->standardEvaluators);
386        $settings = new EvaluatorConfig($target, $providers->environment, $configuration->standardEvaluators, array_map((new MicroEvaluatorConversion())->convert(...), $configuration->evaluators));
387        $identity = new SnapshotIdentity($providers->input, $settings, $evaluators, $providers->manifest);
388        $snapshotId = $identity->id;
389        $input1 = $providers->input;
390        $parser = new Parser(new ParserConfig($target->version));
391        $trees = array_map(static fn (Source $source1): File => (new Resolver())->resolve($parser->parse(new Source(Path::normalize($source1->path), $source1->contents, $source1->declarationsOnly))), iterator_to_array($input1, false));
392        $program = new Index($snapshotId, $trees);
393        $result = (new QueryExecution($program, new ExecutionEvaluatorConfig($target, $providers->environment, $configuration->standardEvaluators), $evaluators, $identity->snapshot(array_map(Frontier::fromDiagnostic(...), $program->diagnostics()))))->derive(new ReturnQuery($symbol));
394        self::assertSame(1, $result->normalOutcomes[0]->values['return']->native());
395        self::assertSame([], $result->frontiers);
396    }
397    public function testValueReadsTheCurrentReferenceCellAfterEarlierParameterCoercions(): void
398    {
399        $state = new State();
400        $location = $state->memory->allocate(Term::constant(1));
401        $actual = new PassedArgument(Term::constant('1.5'), location: $location);
402        $source = '<?php function target(){return 1;}';
403        $budget = new Budget();
404        $configuration = new DeriverConfig();
405        $contributions = new Contributions();
406        $input = new Sources([new Source('fixture.php', $source)]);
407        $target = new TargetProfile($configuration->phpVersion, $configuration->integerBits, $configuration->floatPrecision);
408        $providers = new ProviderExpansion($input, array_map(Term::fromNative(...), $configuration->environment), $contributions);
409        $contributions1 = $providers->contributions;
410        $evaluators = new Evaluators(new TargetProfile($configuration->phpVersion, $configuration->integerBits, $configuration->floatPrecision), new Registrations($contributions1->evaluators, $contributions1->providers, $contributions1->domains, $contributions1->intrinsics, $contributions1->stateSlots), $configuration->standardEvaluators);
411        $settings = new EvaluatorConfig($target, $providers->environment, $configuration->standardEvaluators, array_map((new MicroEvaluatorConversion())->convert(...), $configuration->evaluators));
412        $identity = new SnapshotIdentity($providers->input, $settings, $evaluators, $providers->manifest);
413        $snapshotId = $identity->id;
414        $input1 = $providers->input;
415        $parser = new Parser(new ParserConfig($target->version));
416        $trees = array_map(static fn (Source $source1): File => (new Resolver())->resolve($parser->parse(new Source(Path::normalize($source1->path), $source1->contents, $source1->declarationsOnly))), iterator_to_array($input1, false));
417        $program1 = new Index($snapshotId, $trees);
418        $execution = new QueryExecution($program1, new ExecutionEvaluatorConfig($target, $providers->environment, $configuration->standardEvaluators), $evaluators, $identity->snapshot(array_map(Frontier::fromDiagnostic(...), $program1->diagnostics())));
419        $snapshotId1 = 'test';
420        $input2 = new Sources([new Source('fixture.php', $source)]);
421        $profile = $execution->configuration->target;
422        $parser1 = new Parser(new ParserConfig($profile->version));
423        $trees1 = array_map(static fn (Source $source2): File => (new Resolver())->resolve($parser1->parse(new Source(Path::normalize($source2->path), $source2->contents, $source2->declarationsOnly))), iterator_to_array($input2, false));
424        $program = new Index($snapshotId1, $trees1);
425        $binding = new ParameterBinding(new Evaluator(new Context($program, new ReturnQuery('target', budget: $budget), $execution->configuration, $execution->evaluators)));
426        self::assertSame(1, $binding->value(new Parameter('x', byReference: true), $actual, $state)->native());
427        self::assertSame('1.5', $binding->value(new Parameter('x'), $actual, $state)->native());
428    }
429    /**
430     * @param Parameter $parameter Symbolic signature
431     * @param string $expectedType Resolved valid-input type
432     */
433    #[DataProvider('providerSymbolicParameters')]
434    public function testBindKeepsSymbolicInputsIndependentOfOmission(Parameter $parameter, string $expectedType): void
435    {
436        $source = '<?php class Box{function target(self $value){}}';
437        $budget = new Budget();
438        $configuration = new DeriverConfig();
439        $contributions = new Contributions();
440        $input = new Sources([new Source('fixture.php', $source)]);
441        $target = new TargetProfile($configuration->phpVersion, $configuration->integerBits, $configuration->floatPrecision);
442        $providers = new ProviderExpansion($input, array_map(Term::fromNative(...), $configuration->environment), $contributions);
443        $contributions1 = $providers->contributions;
444        $evaluators = new Evaluators(new TargetProfile($configuration->phpVersion, $configuration->integerBits, $configuration->floatPrecision), new Registrations($contributions1->evaluators, $contributions1->providers, $contributions1->domains, $contributions1->intrinsics, $contributions1->stateSlots), $configuration->standardEvaluators);
445        $settings = new EvaluatorConfig($target, $providers->environment, $configuration->standardEvaluators, array_map((new MicroEvaluatorConversion())->convert(...), $configuration->evaluators));
446        $identity = new SnapshotIdentity($providers->input, $settings, $evaluators, $providers->manifest);
447        $snapshotId = $identity->id;
448        $input1 = $providers->input;
449        $parser = new Parser(new ParserConfig($target->version));
450        $trees = array_map(static fn (Source $source1): File => (new Resolver())->resolve($parser->parse(new Source(Path::normalize($source1->path), $source1->contents, $source1->declarationsOnly))), iterator_to_array($input1, false));
451        $program1 = new Index($snapshotId, $trees);
452        $execution = new QueryExecution($program1, new ExecutionEvaluatorConfig($target, $providers->environment, $configuration->standardEvaluators), $evaluators, $identity->snapshot(array_map(Frontier::fromDiagnostic(...), $program1->diagnostics())));
453        $snapshotId1 = 'test';
454        $input2 = new Sources([new Source('fixture.php', $source)]);
455        $profile = $execution->configuration->target;
456        $parser1 = new Parser(new ParserConfig($profile->version));
457        $trees1 = array_map(static fn (Source $source2): File => (new Resolver())->resolve($parser1->parse(new Source(Path::normalize($source2->path), $source2->contents, $source2->declarationsOnly))), iterator_to_array($input2, false));
458        $program = new Index($snapshotId1, $trees1);
459        $context = new Context($program, new ReturnQuery('target', budget: $budget), $execution->configuration, $execution->evaluators);
460        $callable = $context->program->callable('Box::target');
461        self::assertNotNull($callable);
462        $state = new State();
463        $paths = (new ParameterBinding(new Evaluator($context)))->bind($callable, $parameter, null, $state, true);
464        self::assertSame([$state], $paths);
465        self::assertSame(CompletionKind::Normal, $state->completion->kind);
466        $value = $state->memory->read($state->local($parameter->name));
467        self::assertSame(TermKind::Parameter, $value->kind);
468        self::assertSame($parameter->name, $value->literal);
469        self::assertSame($expectedType, $value->attributes['type']);
470    }
471    /**
472     * @return array<string, array{Parameter,string}>
473     */
474    public static function providerSymbolicParameters(): array
475    {
476        return ['scalar' => [new Parameter('value', 'int'), 'int'], 'reference' => [new Parameter('value', 'int', byReference: true), 'int'], 'variadic collection' => [new Parameter('items', 'int', variadic: true), 'array'], 'lexical class' => [new Parameter('value', 'self'), 'Box']];
477    }
478    /**
479     * @param Parameter $parameter Selected signature
480     * @param PassedArgument|null $actual Omitted or invalid actual
481     * @param string $exception Expected target error
482     */
483    #[DataProvider('providerInvalidArguments')]
484    public function testBindRejectsInvalidArgumentsWithoutInitializingTheLocal(Parameter $parameter, ?PassedArgument $actual, string $exception): void
485    {
486        $source = '<?php function target(){return 1;}';
487        $budget = new Budget();
488        $configuration = new DeriverConfig();
489        $contributions = new Contributions();
490        $input = new Sources([new Source('fixture.php', $source)]);
491        $target = new TargetProfile($configuration->phpVersion, $configuration->integerBits, $configuration->floatPrecision);
492        $providers = new ProviderExpansion($input, array_map(Term::fromNative(...), $configuration->environment), $contributions);
493        $contributions1 = $providers->contributions;
494        $evaluators = new Evaluators(new TargetProfile($configuration->phpVersion, $configuration->integerBits, $configuration->floatPrecision), new Registrations($contributions1->evaluators, $contributions1->providers, $contributions1->domains, $contributions1->intrinsics, $contributions1->stateSlots), $configuration->standardEvaluators);
495        $settings = new EvaluatorConfig($target, $providers->environment, $configuration->standardEvaluators, array_map((new MicroEvaluatorConversion())->convert(...), $configuration->evaluators));
496        $identity = new SnapshotIdentity($providers->input, $settings, $evaluators, $providers->manifest);
497        $snapshotId = $identity->id;
498        $input1 = $providers->input;
499        $parser = new Parser(new ParserConfig($target->version));
500        $trees = array_map(static fn (Source $source1): File => (new Resolver())->resolve($parser->parse(new Source(Path::normalize($source1->path), $source1->contents, $source1->declarationsOnly))), iterator_to_array($input1, false));
501        $program1 = new Index($snapshotId, $trees);
502        $execution = new QueryExecution($program1, new ExecutionEvaluatorConfig($target, $providers->environment, $configuration->standardEvaluators), $evaluators, $identity->snapshot(array_map(Frontier::fromDiagnostic(...), $program1->diagnostics())));
503        $snapshotId1 = 'test';
504        $input2 = new Sources([new Source('fixture.php', $source)]);
505        $profile = $execution->configuration->target;
506        $parser1 = new Parser(new ParserConfig($profile->version));
507        $trees1 = array_map(static fn (Source $source2): File => (new Resolver())->resolve($parser1->parse(new Source(Path::normalize($source2->path), $source2->contents, $source2->declarationsOnly))), iterator_to_array($input2, false));
508        $program = new Index($snapshotId1, $trees1);
509        $context = new Context($program, new ReturnQuery('target', budget: $budget), $execution->configuration, $execution->evaluators);
510        $callable = $context->program->callable('target');
511        self::assertNotNull($callable);
512        $state = new State();
513        $paths = (new ParameterBinding(new Evaluator($context)))->bind($callable, $parameter, $actual, $state, false, true);
514        self::assertSame([$state], $paths);
515        self::assertSame(CompletionKind::Throw, $state->completion->kind);
516        self::assertSame($exception, $state->completion->value?->literal);
517        self::assertSame([], $state->locals);
518    }
519    /**
520     * @return array<string, array{Parameter,PassedArgument|null,string}>
521     */
522    public static function providerInvalidArguments(): array
523    {
524        return ['missing required' => [new Parameter('x'), null, 'ArgumentCountError'], 'invalid scalar type' => [new Parameter('x', 'int'), new PassedArgument(Term::constant('1')), 'TypeError'], 'reference requires address' => [new Parameter('x', byReference: true), new PassedArgument(Term::constant(1)), 'Error'], 'reference requires writable address' => [new Parameter('x', byReference: true), new PassedArgument(Term::constant(1), location: new Location('readonly'), writable: false), 'Error']];
525    }
526    public function testBindPreservesBothOutcomesWhenAnActualMayViolateItsType(): void
527    {
528        $source = '<?php function target(){return 1;}';
529        $budget = new Budget();
530        $configuration = new DeriverConfig();
531        $contributions = new Contributions();
532        $input = new Sources([new Source('fixture.php', $source)]);
533        $target = new TargetProfile($configuration->phpVersion, $configuration->integerBits, $configuration->floatPrecision);
534        $providers = new ProviderExpansion($input, array_map(Term::fromNative(...), $configuration->environment), $contributions);
535        $contributions1 = $providers->contributions;
536        $evaluators = new Evaluators(new TargetProfile($configuration->phpVersion, $configuration->integerBits, $configuration->floatPrecision), new Registrations($contributions1->evaluators, $contributions1->providers, $contributions1->domains, $contributions1->intrinsics, $contributions1->stateSlots), $configuration->standardEvaluators);
537        $settings = new EvaluatorConfig($target, $providers->environment, $configuration->standardEvaluators, array_map((new MicroEvaluatorConversion())->convert(...), $configuration->evaluators));
538        $identity = new SnapshotIdentity($providers->input, $settings, $evaluators, $providers->manifest);
539        $snapshotId = $identity->id;
540        $input1 = $providers->input;
541        $parser = new Parser(new ParserConfig($target->version));
542        $trees = array_map(static fn (Source $source1): File => (new Resolver())->resolve($parser->parse(new Source(Path::normalize($source1->path), $source1->contents, $source1->declarationsOnly))), iterator_to_array($input1, false));
543        $program1 = new Index($snapshotId, $trees);
544        $execution = new QueryExecution($program1, new ExecutionEvaluatorConfig($target, $providers->environment, $configuration->standardEvaluators), $evaluators, $identity->snapshot(array_map(Frontier::fromDiagnostic(...), $program1->diagnostics())));
545        $snapshotId1 = 'test';
546        $input2 = new Sources([new Source('fixture.php', $source)]);
547        $profile = $execution->configuration->target;
548        $parser1 = new Parser(new ParserConfig($profile->version));
549        $trees1 = array_map(static fn (Source $source2): File => (new Resolver())->resolve($parser1->parse(new Source(Path::normalize($source2->path), $source2->contents, $source2->declarationsOnly))), iterator_to_array($input2, false));
550        $program = new Index($snapshotId1, $trees1);
551        $context = new Context($program, new ReturnQuery('target', budget: $budget), $execution->configuration, $execution->evaluators);
552        $callable = $context->program->callable('target');
553        self::assertNotNull($callable);
554        $state = new State();
555        $actual = new PassedArgument(Term::parameter('input'));
556        $paths = (new ParameterBinding(new Evaluator($context)))->bind($callable, new Parameter('x', 'int'), $actual, $state, false, true);
557        self::assertCount(2, $paths);
558        self::assertSame(CompletionKind::Normal, $paths[0]->completion->kind);
559        self::assertSame('int', $paths[0]->memory->read($paths[0]->local('x'))->attributes['type']);
560        self::assertSame(CompletionKind::Throw, $paths[1]->completion->kind);
561        self::assertSame('TypeError', $paths[1]->completion->value?->literal);
562        self::assertSame([], $paths[1]->locals);
563        self::assertNotSame($paths[0]->memory, $paths[1]->memory);
564    }
565    public function testBindUpdatesACallerReferenceAfterWeakCoercion(): void
566    {
567        $source = '<?php function target(){return 1;}';
568        $budget = new Budget();
569        $configuration = new DeriverConfig();
570        $contributions = new Contributions();
571        $input = new Sources([new Source('fixture.php', $source)]);
572        $target = new TargetProfile($configuration->phpVersion, $configuration->integerBits, $configuration->floatPrecision);
573        $providers = new ProviderExpansion($input, array_map(Term::fromNative(...), $configuration->environment), $contributions);
574        $contributions1 = $providers->contributions;
575        $evaluators = new Evaluators(new TargetProfile($configuration->phpVersion, $configuration->integerBits, $configuration->floatPrecision), new Registrations($contributions1->evaluators, $contributions1->providers, $contributions1->domains, $contributions1->intrinsics, $contributions1->stateSlots), $configuration->standardEvaluators);
576        $settings = new EvaluatorConfig($target, $providers->environment, $configuration->standardEvaluators, array_map((new MicroEvaluatorConversion())->convert(...), $configuration->evaluators));
577        $identity = new SnapshotIdentity($providers->input, $settings, $evaluators, $providers->manifest);
578        $snapshotId = $identity->id;
579        $input1 = $providers->input;
580        $parser = new Parser(new ParserConfig($target->version));
581        $trees = array_map(static fn (Source $source1): File => (new Resolver())->resolve($parser->parse(new Source(Path::normalize($source1->path), $source1->contents, $source1->declarationsOnly))), iterator_to_array($input1, false));
582        $program1 = new Index($snapshotId, $trees);
583        $execution = new QueryExecution($program1, new ExecutionEvaluatorConfig($target, $providers->environment, $configuration->standardEvaluators), $evaluators, $identity->snapshot(array_map(Frontier::fromDiagnostic(...), $program1->diagnostics())));
584        $snapshotId1 = 'test';
585        $input2 = new Sources([new Source('fixture.php', $source)]);
586        $profile = $execution->configuration->target;
587        $parser1 = new Parser(new ParserConfig($profile->version));
588        $trees1 = array_map(static fn (Source $source2): File => (new Resolver())->resolve($parser1->parse(new Source(Path::normalize($source2->path), $source2->contents, $source2->declarationsOnly))), iterator_to_array($input2, false));
589        $program = new Index($snapshotId1, $trees1);
590        $context = new Context($program, new ReturnQuery('target', budget: $budget), $execution->configuration, $execution->evaluators);
591        $callable = $context->program->callable('target');
592        self::assertNotNull($callable);
593        $state = new State();
594        $location = $state->memory->allocate(Term::constant('2'));
595        $actual = new PassedArgument(Term::constant('2'), location: $location);
596        $paths = (new ParameterBinding(new Evaluator($context)))->bind($callable, new Parameter('x', 'int', byReference: true), $actual, $state, false);
597        self::assertSame([$state], $paths);
598        self::assertSame(CompletionKind::Normal, $state->completion->kind);
599        self::assertSame(2, $state->memory->read($location)->native());
600        $state->memory->write($state->local('x'), Term::constant(3));
601        self::assertSame(3, $state->memory->read($location)->native());
602    }
603    public function testVariadicRetainsNamedAliasesAndAppliesCoercionsToEveryCallerCell(): void
604    {
605        $state = new State();
606        $first = $state->memory->allocate(Term::constant('2'));
607        $second = $state->memory->allocate(Term::constant('3'));
608        $actual = new PassedArgument(Term::array([]), elements: [new PassedArgument(Term::constant('2'), location: $first), 'named' => new PassedArgument(Term::constant('3'), location: $second)]);
609        $source = '<?php function target(){return 1;}';
610        $budget = new Budget();
611        $configuration = new DeriverConfig();
612        $contributions = new Contributions();
613        $input = new Sources([new Source('fixture.php', $source)]);
614        $target = new TargetProfile($configuration->phpVersion, $configuration->integerBits, $configuration->floatPrecision);
615        $providers = new ProviderExpansion($input, array_map(Term::fromNative(...), $configuration->environment), $contributions);
616        $contributions1 = $providers->contributions;
617        $evaluators = new Evaluators(new TargetProfile($configuration->phpVersion, $configuration->integerBits, $configuration->floatPrecision), new Registrations($contributions1->evaluators, $contributions1->providers, $contributions1->domains, $contributions1->intrinsics, $contributions1->stateSlots), $configuration->standardEvaluators);
618        $settings = new EvaluatorConfig($target, $providers->environment, $configuration->standardEvaluators, array_map((new MicroEvaluatorConversion())->convert(...), $configuration->evaluators));
619        $identity = new SnapshotIdentity($providers->input, $settings, $evaluators, $providers->manifest);
620        $snapshotId = $identity->id;
621        $input1 = $providers->input;
622        $parser = new Parser(new ParserConfig($target->version));
623        $trees = array_map(static fn (Source $source1): File => (new Resolver())->resolve($parser->parse(new Source(Path::normalize($source1->path), $source1->contents, $source1->declarationsOnly))), iterator_to_array($input1, false));
624        $program1 = new Index($snapshotId, $trees);
625        $execution = new QueryExecution($program1, new ExecutionEvaluatorConfig($target, $providers->environment, $configuration->standardEvaluators), $evaluators, $identity->snapshot(array_map(Frontier::fromDiagnostic(...), $program1->diagnostics())));
626        $snapshotId1 = 'test';
627        $input2 = new Sources([new Source('fixture.php', $source)]);
628        $profile = $execution->configuration->target;
629        $parser1 = new Parser(new ParserConfig($profile->version));
630        $trees1 = array_map(static fn (Source $source2): File => (new Resolver())->resolve($parser1->parse(new Source(Path::normalize($source2->path), $source2->contents, $source2->declarationsOnly))), iterator_to_array($input2, false));
631        $program = new Index($snapshotId1, $trees1);
632        $binding = new ParameterBinding(new Evaluator(new Context($program, new ReturnQuery('target', budget: $budget), $execution->configuration, $execution->evaluators)));
633        self::assertSame([$state], $binding->variadic(new Parameter('items', 'int', byReference: true, variadic: true), $actual, $state, false));
634        self::assertSame(2, $state->memory->read($first)->native());
635        self::assertSame(3, $state->memory->read($second)->native());
636        $items = $state->memory->read($state->local('items'));
637        self::assertSame([0, 'named'], array_keys($items->operands));
638        self::assertSame(TermKind::Cell, $items->operands['named']->kind);
639        $state->memory->write(new Location($state->local('items')->root, ['named']), Term::constant(4));
640        self::assertSame(4, $state->memory->read($second)->native());
641    }
642    /**
643     * @param bool $byReference Whether an address is required
644     * @param PassedArgument $element Invalid variadic element
645     * @param string $exception \Deriver\Deriver\Evaluator\Execution\Call\Preparation\Target error class
646     */
647    #[DataProvider('providerInvalidVariadics')]
648    public function testVariadicRejectsAnInvalidElementBeforePublishingTheCollection(bool $byReference, PassedArgument $element, string $exception): void
649    {
650        $state = new State();
651        $source = '<?php function target(){return 1;}';
652        $budget = new Budget();
653        $configuration = new DeriverConfig();
654        $contributions = new Contributions();
655        $input = new Sources([new Source('fixture.php', $source)]);
656        $target = new TargetProfile($configuration->phpVersion, $configuration->integerBits, $configuration->floatPrecision);
657        $providers = new ProviderExpansion($input, array_map(Term::fromNative(...), $configuration->environment), $contributions);
658        $contributions1 = $providers->contributions;
659        $evaluators = new Evaluators(new TargetProfile($configuration->phpVersion, $configuration->integerBits, $configuration->floatPrecision), new Registrations($contributions1->evaluators, $contributions1->providers, $contributions1->domains, $contributions1->intrinsics, $contributions1->stateSlots), $configuration->standardEvaluators);
660        $settings = new EvaluatorConfig($target, $providers->environment, $configuration->standardEvaluators, array_map((new MicroEvaluatorConversion())->convert(...), $configuration->evaluators));
661        $identity = new SnapshotIdentity($providers->input, $settings, $evaluators, $providers->manifest);
662        $snapshotId = $identity->id;
663        $input1 = $providers->input;
664        $parser = new Parser(new ParserConfig($target->version));
665        $trees = array_map(static fn (Source $source1): File => (new Resolver())->resolve($parser->parse(new Source(Path::normalize($source1->path), $source1->contents, $source1->declarationsOnly))), iterator_to_array($input1, false));
666        $program1 = new Index($snapshotId, $trees);
667        $execution = new QueryExecution($program1, new ExecutionEvaluatorConfig($target, $providers->environment, $configuration->standardEvaluators), $evaluators, $identity->snapshot(array_map(Frontier::fromDiagnostic(...), $program1->diagnostics())));
668        $snapshotId1 = 'test';
669        $input2 = new Sources([new Source('fixture.php', $source)]);
670        $profile = $execution->configuration->target;
671        $parser1 = new Parser(new ParserConfig($profile->version));
672        $trees1 = array_map(static fn (Source $source2): File => (new Resolver())->resolve($parser1->parse(new Source(Path::normalize($source2->path), $source2->contents, $source2->declarationsOnly))), iterator_to_array($input2, false));
673        $program = new Index($snapshotId1, $trees1);
674        $binding = new ParameterBinding(new Evaluator(new Context($program, new ReturnQuery('target', budget: $budget), $execution->configuration, $execution->evaluators)));
675        $paths = $binding->variadic(new Parameter('items', 'int', byReference: $byReference, variadic: true), new PassedArgument(Term::array([]), elements: [$element]), $state, true);
676        self::assertSame([$state], $paths);
677        self::assertSame(CompletionKind::Throw, $state->completion->kind);
678        self::assertSame($exception, $state->completion->value?->literal);
679        self::assertSame([], $state->locals);
680    }
681    /**
682     * @return array<string,array{bool,PassedArgument,string}>
683     */
684    public static function providerInvalidVariadics(): array
685    {
686        return ['type error' => [false, new PassedArgument(Term::constant('2')), 'TypeError'], 'missing address' => [true, new PassedArgument(Term::constant(2)), 'Error'], 'readonly address' => [true, new PassedArgument(Term::constant(2), location: new Location('readonly'), writable: false), 'Error']];
687    }
688    public function testVariadicPreservesPossibleTypeFailureAlongsideTheNormalCollection(): void
689    {
690        $state = new State();
691        $source = '<?php function target(){return 1;}';
692        $budget = new Budget();
693        $configuration = new DeriverConfig();
694        $contributions = new Contributions();
695        $input = new Sources([new Source('fixture.php', $source)]);
696        $target = new TargetProfile($configuration->phpVersion, $configuration->integerBits, $configuration->floatPrecision);
697        $providers = new ProviderExpansion($input, array_map(Term::fromNative(...), $configuration->environment), $contributions);
698        $contributions1 = $providers->contributions;
699        $evaluators = new Evaluators(new TargetProfile($configuration->phpVersion, $configuration->integerBits, $configuration->floatPrecision), new Registrations($contributions1->evaluators, $contributions1->providers, $contributions1->domains, $contributions1->intrinsics, $contributions1->stateSlots), $configuration->standardEvaluators);
700        $settings = new EvaluatorConfig($target, $providers->environment, $configuration->standardEvaluators, array_map((new MicroEvaluatorConversion())->convert(...), $configuration->evaluators));
701        $identity = new SnapshotIdentity($providers->input, $settings, $evaluators, $providers->manifest);
702        $snapshotId = $identity->id;
703        $input1 = $providers->input;
704        $parser = new Parser(new ParserConfig($target->version));
705        $trees = array_map(static fn (Source $source1): File => (new Resolver())->resolve($parser->parse(new Source(Path::normalize($source1->path), $source1->contents, $source1->declarationsOnly))), iterator_to_array($input1, false));
706        $program1 = new Index($snapshotId, $trees);
707        $execution = new QueryExecution($program1, new ExecutionEvaluatorConfig($target, $providers->environment, $configuration->standardEvaluators), $evaluators, $identity->snapshot(array_map(Frontier::fromDiagnostic(...), $program1->diagnostics())));
708        $snapshotId1 = 'test';
709        $input2 = new Sources([new Source('fixture.php', $source)]);
710        $profile = $execution->configuration->target;
711        $parser1 = new Parser(new ParserConfig($profile->version));
712        $trees1 = array_map(static fn (Source $source2): File => (new Resolver())->resolve($parser1->parse(new Source(Path::normalize($source2->path), $source2->contents, $source2->declarationsOnly))), iterator_to_array($input2, false));
713        $program = new Index($snapshotId1, $trees1);
714        $binding = new ParameterBinding(new Evaluator(new Context($program, new ReturnQuery('target', budget: $budget), $execution->configuration, $execution->evaluators)));
715        $paths = $binding->variadic(new Parameter('items', 'self', variadic: true), new PassedArgument(Term::array([]), elements: [new PassedArgument(Term::parameter('unknown')), new PassedArgument(Term::constant(1))]), $state, true, 'int');
716        self::assertCount(2, $paths);
717        self::assertSame(CompletionKind::Normal, $paths[0]->completion->kind);
718        $items = $paths[0]->memory->read($paths[0]->local('items'));
719        self::assertSame('int', $items->operands[0]->attributes['type']);
720        self::assertSame(1, $items->operands[1]->native());
721        self::assertSame(CompletionKind::Throw, $paths[1]->completion->kind);
722        self::assertSame('TypeError', $paths[1]->completion->value?->literal);
723        self::assertNotSame($paths[0]->memory, $paths[1]->memory);
724    }
725    public function testDefaultArgumentRetainsAnOmittedMarkerWithoutEnforcingAnAbsentType(): void
726    {
727        $source = '<?php function target(){return 1;}';
728        $budget = new Budget();
729        $configuration = new DeriverConfig();
730        $contributions = new Contributions();
731        $input = new Sources([new Source('fixture.php', $source)]);
732        $target = new TargetProfile($configuration->phpVersion, $configuration->integerBits, $configuration->floatPrecision);
733        $providers = new ProviderExpansion($input, array_map(Term::fromNative(...), $configuration->environment), $contributions);
734        $contributions1 = $providers->contributions;
735        $evaluators = new Evaluators(new TargetProfile($configuration->phpVersion, $configuration->integerBits, $configuration->floatPrecision), new Registrations($contributions1->evaluators, $contributions1->providers, $contributions1->domains, $contributions1->intrinsics, $contributions1->stateSlots), $configuration->standardEvaluators);
736        $settings = new EvaluatorConfig($target, $providers->environment, $configuration->standardEvaluators, array_map((new MicroEvaluatorConversion())->convert(...), $configuration->evaluators));
737        $identity = new SnapshotIdentity($providers->input, $settings, $evaluators, $providers->manifest);
738        $snapshotId = $identity->id;
739        $input1 = $providers->input;
740        $parser = new Parser(new ParserConfig($target->version));
741        $trees = array_map(static fn (Source $source1): File => (new Resolver())->resolve($parser->parse(new Source(Path::normalize($source1->path), $source1->contents, $source1->declarationsOnly))), iterator_to_array($input1, false));
742        $program1 = new Index($snapshotId, $trees);
743        $execution = new QueryExecution($program1, new ExecutionEvaluatorConfig($target, $providers->environment, $configuration->standardEvaluators), $evaluators, $identity->snapshot(array_map(Frontier::fromDiagnostic(...), $program1->diagnostics())));
744        $snapshotId1 = 'test';
745        $input2 = new Sources([new Source('fixture.php', $source)]);
746        $profile = $execution->configuration->target;
747        $parser1 = new Parser(new ParserConfig($profile->version));
748        $trees1 = array_map(static fn (Source $source2): File => (new Resolver())->resolve($parser1->parse(new Source(Path::normalize($source2->path), $source2->contents, $source2->declarationsOnly))), iterator_to_array($input2, false));
749        $program = new Index($snapshotId1, $trees1);
750        $context = new Context($program, new ReturnQuery('target', budget: $budget), $execution->configuration, $execution->evaluators);
751        $callable = $context->program->callable('target');
752        self::assertNotNull($callable);
753        $default = new CallableGraph('default', [], [new BasicBlock(0, [new BoundInstruction('default', 'constant', $callable->source, 'r', constant: new Term(TermKind::Omitted))], new Terminator(TerminatorKind::Return, 'r'))], $callable->source);
754        $state = new State();
755        $paths = (new ParameterBinding(new Evaluator($context)))->defaultArgument($callable, new Parameter('optional', 'int'), $default, $state, true);
756        self::assertCount(1, $paths);
757        self::assertSame(CompletionKind::Normal, $paths[0]->completion->kind);
758        self::assertSame(TermKind::Omitted, $paths[0]->memory->read($paths[0]->local('optional'))->kind);
759        self::assertSame([], $state->locals);
760        self::assertNotSame($state->memory, $paths[0]->memory);
761    }
762    public function testDefaultArgumentPreservesInitializerEffectsAndEveryCompletion(): void
763    {
764        $source = '<?php class Initializer{function __construct(){global $flag,$count;$count=7;if($flag){throw new Exception;}}}function target($x=new Initializer){}';
765        $budget = new Budget();
766        $configuration = new DeriverConfig();
767        $contributions = new Contributions();
768        $input = new Sources([new Source('fixture.php', $source)]);
769        $target = new TargetProfile($configuration->phpVersion, $configuration->integerBits, $configuration->floatPrecision);
770        $providers = new ProviderExpansion($input, array_map(Term::fromNative(...), $configuration->environment), $contributions);
771        $contributions1 = $providers->contributions;
772        $evaluators = new Evaluators(new TargetProfile($configuration->phpVersion, $configuration->integerBits, $configuration->floatPrecision), new Registrations($contributions1->evaluators, $contributions1->providers, $contributions1->domains, $contributions1->intrinsics, $contributions1->stateSlots), $configuration->standardEvaluators);
773        $settings = new EvaluatorConfig($target, $providers->environment, $configuration->standardEvaluators, array_map((new MicroEvaluatorConversion())->convert(...), $configuration->evaluators));
774        $identity = new SnapshotIdentity($providers->input, $settings, $evaluators, $providers->manifest);
775        $snapshotId = $identity->id;
776        $input1 = $providers->input;
777        $parser = new Parser(new ParserConfig($target->version));
778        $trees = array_map(static fn (Source $source1): File => (new Resolver())->resolve($parser->parse(new Source(Path::normalize($source1->path), $source1->contents, $source1->declarationsOnly))), iterator_to_array($input1, false));
779        $program1 = new Index($snapshotId, $trees);
780        $execution = new QueryExecution($program1, new ExecutionEvaluatorConfig($target, $providers->environment, $configuration->standardEvaluators), $evaluators, $identity->snapshot(array_map(Frontier::fromDiagnostic(...), $program1->diagnostics())));
781        $snapshotId1 = 'test';
782        $input2 = new Sources([new Source('fixture.php', $source)]);
783        $profile = $execution->configuration->target;
784        $parser1 = new Parser(new ParserConfig($profile->version));
785        $trees1 = array_map(static fn (Source $source2): File => (new Resolver())->resolve($parser1->parse(new Source(Path::normalize($source2->path), $source2->contents, $source2->declarationsOnly))), iterator_to_array($input2, false));
786        $program = new Index($snapshotId1, $trees1);
787        $context = new Context($program, new ReturnQuery('target', budget: $budget), $execution->configuration, $execution->evaluators);
788        $callable = $context->program->callable('target');
789        self::assertNotNull($callable);
790        $default = $callable->parameters[0]->default;
791        self::assertNotNull($default);
792        $state = new State();
793        $paths = (new ParameterBinding(new Evaluator($context)))->defaultArgument($callable, $callable->parameters[0], $default, $state, true);
794        self::assertCount(2, $paths);
795        self::assertEqualsCanonicalizing([CompletionKind::Normal, CompletionKind::Throw], array_column(array_column($paths, 'completion'), 'kind'));
796        self::assertSame(7, $paths[0]->memory->read(new Location('global:count'))->native());
797        self::assertSame(7, $paths[1]->memory->read(new Location('global:count'))->native());
798        self::assertSame(TermKind::Uninitialized, $state->memory->read(new Location('global:count'))->kind);
799        self::assertNotSame($paths[0]->memory, $paths[1]->memory);
800    }
801    public function testVariadicReportsWeakScalarWarningsAtTheBindingSource(): void
802    {
803        $source = '<?php function target(){return 1;}';
804        $budget = new Budget();
805        $configuration = new DeriverConfig();
806        $contributions = new Contributions();
807        $input = new Sources([new Source('fixture.php', $source)]);
808        $target = new TargetProfile($configuration->phpVersion, $configuration->integerBits, $configuration->floatPrecision);
809        $providers = new ProviderExpansion($input, array_map(Term::fromNative(...), $configuration->environment), $contributions);
810        $contributions1 = $providers->contributions;
811        $evaluators = new Evaluators(new TargetProfile($configuration->phpVersion, $configuration->integerBits, $configuration->floatPrecision), new Registrations($contributions1->evaluators, $contributions1->providers, $contributions1->domains, $contributions1->intrinsics, $contributions1->stateSlots), $configuration->standardEvaluators);
812        $settings = new EvaluatorConfig($target, $providers->environment, $configuration->standardEvaluators, array_map((new MicroEvaluatorConversion())->convert(...), $configuration->evaluators));
813        $identity = new SnapshotIdentity($providers->input, $settings, $evaluators, $providers->manifest);
814        $snapshotId = $identity->id;
815        $input1 = $providers->input;
816        $parser = new Parser(new ParserConfig($target->version));
817        $trees = array_map(static fn (Source $source1): File => (new Resolver())->resolve($parser->parse(new Source(Path::normalize($source1->path), $source1->contents, $source1->declarationsOnly))), iterator_to_array($input1, false));
818        $program1 = new Index($snapshotId, $trees);
819        $execution = new QueryExecution($program1, new ExecutionEvaluatorConfig($target, $providers->environment, $configuration->standardEvaluators), $evaluators, $identity->snapshot(array_map(Frontier::fromDiagnostic(...), $program1->diagnostics())));
820        $snapshotId1 = 'test';
821        $input2 = new Sources([new Source('fixture.php', $source)]);
822        $profile = $execution->configuration->target;
823        $parser1 = new Parser(new ParserConfig($profile->version));
824        $trees1 = array_map(static fn (Source $source2): File => (new Resolver())->resolve($parser1->parse(new Source(Path::normalize($source2->path), $source2->contents, $source2->declarationsOnly))), iterator_to_array($input2, false));
825        $program = new Index($snapshotId1, $trees1);
826        $context = new Context($program, new ReturnQuery('target', budget: $budget), $execution->configuration, $execution->evaluators);
827        $callable = $context->program->callable('target');
828        self::assertNotNull($callable);
829        $state = new State();
830        $binding = new ParameterBinding(new Evaluator($context));
831        $paths = $binding->variadic(new Parameter('items', 'int', variadic: true), new PassedArgument(Term::array([]), elements: [new PassedArgument(Term::constant(1.5))]), $state, false, source: $callable->source);
832        self::assertSame([$state], $paths);
833        self::assertSame([1], $state->memory->read($state->local('items'))->native());
834        self::assertSame(['PHP_WARNING'], array_column($context->frontiers, 'code'));
835    }
836    public function testVariadicRetainsArbitraryThrowableFromAnUnresolvedObjectCoercion(): void
837    {
838        $source = '<?php class Text{function __toString(){return "text";}}function target(){}';
839        $budget = new Budget();
840        $configuration = new DeriverConfig();
841        $contributions = new Contributions();
842        $input = new Sources([new Source('fixture.php', $source)]);
843        $target = new TargetProfile($configuration->phpVersion, $configuration->integerBits, $configuration->floatPrecision);
844        $providers = new ProviderExpansion($input, array_map(Term::fromNative(...), $configuration->environment), $contributions);
845        $contributions1 = $providers->contributions;
846        $evaluators = new Evaluators(new TargetProfile($configuration->phpVersion, $configuration->integerBits, $configuration->floatPrecision), new Registrations($contributions1->evaluators, $contributions1->providers, $contributions1->domains, $contributions1->intrinsics, $contributions1->stateSlots), $configuration->standardEvaluators);
847        $settings = new EvaluatorConfig($target, $providers->environment, $configuration->standardEvaluators, array_map((new MicroEvaluatorConversion())->convert(...), $configuration->evaluators));
848        $identity = new SnapshotIdentity($providers->input, $settings, $evaluators, $providers->manifest);
849        $snapshotId = $identity->id;
850        $input1 = $providers->input;
851        $parser = new Parser(new ParserConfig($target->version));
852        $trees = array_map(static fn (Source $source1): File => (new Resolver())->resolve($parser->parse(new Source(Path::normalize($source1->path), $source1->contents, $source1->declarationsOnly))), iterator_to_array($input1, false));
853        $program1 = new Index($snapshotId, $trees);
854        $execution = new QueryExecution($program1, new ExecutionEvaluatorConfig($target, $providers->environment, $configuration->standardEvaluators), $evaluators, $identity->snapshot(array_map(Frontier::fromDiagnostic(...), $program1->diagnostics())));
855        $snapshotId1 = 'test';
856        $input2 = new Sources([new Source('fixture.php', $source)]);
857        $profile = $execution->configuration->target;
858        $parser1 = new Parser(new ParserConfig($profile->version));
859        $trees1 = array_map(static fn (Source $source2): File => (new Resolver())->resolve($parser1->parse(new Source(Path::normalize($source2->path), $source2->contents, $source2->declarationsOnly))), iterator_to_array($input2, false));
860        $program = new Index($snapshotId1, $trees1);
861        $context = new Context($program, new ReturnQuery('target', budget: $budget), $execution->configuration, $execution->evaluators);
862        $callable = $context->program->callable('target');
863        self::assertNotNull($callable);
864        $state = new State();
865        $binding = new ParameterBinding(new Evaluator($context));
866        $actual = new PassedArgument(Term::array([]), elements: [new PassedArgument(new Term(TermKind::Object, 'text', attributes: ['class' => 'Text'])), new PassedArgument(Term::constant('last'))]);
867        $paths = $binding->variadic(new Parameter('items', 'string', variadic: true), $actual, $state, false, source: $callable->source);
868        self::assertCount(2, $paths);
869        self::assertSame(CompletionKind::Normal, $paths[0]->completion->kind);
870        self::assertSame(CompletionKind::Throw, $paths[1]->completion->kind);
871        self::assertSame('Throwable', $paths[1]->completion->value?->literal);
872        self::assertTrue($paths[1]->completion->value->attributes['uncertain']);
873        self::assertSame(['UNSUPPORTED_LANGUAGE_FEATURE'], array_column($context->frontiers, 'code'));
874    }
875}
876