packages/deriver/tests/Unit/Deriver/Evaluator/Execution/Offset/TransferTest.php

1<?php
2
3declare (strict_types=1);
4
5namespace Tests\Unit\Deriver\Evaluator\Execution\Offset;
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\Builtin\FunctionEvaluator;
14use Deriver\Deriver\Evaluator\Builtin\Library;
15use Deriver\Deriver\Evaluator\Builtin\ScalarFunctions;
16use Deriver\Deriver\Evaluator\Call\ArgumentBindings;
17use Deriver\Deriver\Evaluator\Call\BoundArgument;
18use Deriver\Deriver\Evaluator\Call\BoundInstruction;
19use Deriver\Deriver\Evaluator\Call\CallDecision;
20use Deriver\Deriver\Evaluator\Call\CallDescription;
21use Deriver\Deriver\Evaluator\Call\CallDescriptor;
22use Deriver\Deriver\Evaluator\Call\CallEvaluator;
23use Deriver\Deriver\Evaluator\Call\Evaluators;
24use Deriver\Deriver\Evaluator\Call\Extensions;
25use Deriver\Deriver\Evaluator\Call\Plan\Action;
26use Deriver\Deriver\Evaluator\Call\Plan\Expression;
27use Deriver\Deriver\Evaluator\Call\Plan\PlanActions;
28use Deriver\Deriver\Evaluator\Call\Plan\PlanCompiler;
29use Deriver\Deriver\Evaluator\Call\Plan\PlanFootprints;
30use Deriver\Deriver\Evaluator\Call\Plan\PlanValidation;
31use Deriver\Deriver\Evaluator\Call\Plan\SemanticPlan;
32use Deriver\Deriver\Evaluator\Call\Provider\DispatchDecision;
33use Deriver\Deriver\Evaluator\Call\Provider\DispatchRequest;
34use Deriver\Deriver\Evaluator\Call\Registrations;
35use Deriver\Deriver\Evaluator\Call\StateRegistry;
36use Deriver\Deriver\Evaluator\Declaration\Parameter as DeclarationParameter;
37use Deriver\Deriver\Evaluator\Declaration\Signature;
38use Deriver\Deriver\Evaluator\EvaluatorConfig;
39use Deriver\Deriver\Evaluator\Evidence\Frontier;
40use Deriver\Deriver\Evaluator\Execution\Call\Allocation;
41use Deriver\Deriver\Evaluator\Execution\Call\ArgumentBinding;
42use Deriver\Deriver\Evaluator\Execution\Call\ArgumentOrder;
43use Deriver\Deriver\Evaluator\Execution\Call\CallableCheck;
44use Deriver\Deriver\Evaluator\Execution\Call\CallExecutor;
45use Deriver\Deriver\Evaluator\Execution\Call\CallResolution;
46use Deriver\Deriver\Evaluator\Execution\Call\Contract\Inputs;
47use Deriver\Deriver\Evaluator\Execution\Call\Contract\NativeArguments;
48use Deriver\Deriver\Evaluator\Execution\Call\Creation\Access;
49use Deriver\Deriver\Evaluator\Execution\Call\Member\Invocation;
50use Deriver\Deriver\Evaluator\Execution\Call\MethodInvocation;
51use Deriver\Deriver\Evaluator\Execution\Call\Native\Invocation as NativeInvocation;
52use Deriver\Deriver\Evaluator\Execution\Call\Native\Properties;
53use Deriver\Deriver\Evaluator\Execution\Call\Native\Signatures;
54use Deriver\Deriver\Evaluator\Execution\Call\ParameterBinding;
55use Deriver\Deriver\Evaluator\Execution\Call\PassedArgument;
56use Deriver\Deriver\Evaluator\Execution\Call\Preparation\Arguments;
57use Deriver\Deriver\Evaluator\Execution\Call\Preparation\Creation;
58use Deriver\Deriver\Evaluator\Execution\Call\Preparation\Methods;
59use Deriver\Deriver\Evaluator\Execution\Call\Preparation\Modes;
60use Deriver\Deriver\Evaluator\Execution\Call\Preparation\Resolution;
61use Deriver\Deriver\Evaluator\Execution\Call\Preparation\Target;
62use Deriver\Deriver\Evaluator\Execution\Call\Preparation\Transfer as PreparationTransfer;
63use Deriver\Deriver\Evaluator\Execution\Call\ProviderDispatch;
64use Deriver\Deriver\Evaluator\Execution\Call\TypeBinding;
65use Deriver\Deriver\Evaluator\Execution\Call\TypeCheck;
66use Deriver\Deriver\Evaluator\Execution\Completion\Alternative;
67use Deriver\Deriver\Evaluator\Execution\Completion\Assessment;
68use Deriver\Deriver\Evaluator\Execution\Completion\Completion;
69use Deriver\Deriver\Evaluator\Execution\Completion\CompletionKind;
70use Deriver\Deriver\Evaluator\Execution\Completion\Exceptional;
71use Deriver\Deriver\Evaluator\Execution\Completion\StorageSnapshot;
72use Deriver\Deriver\Evaluator\Execution\Constant\ClassNames;
73use Deriver\Deriver\Evaluator\Execution\Constraints;
74use Deriver\Deriver\Evaluator\Execution\Context;
75use Deriver\Deriver\Evaluator\Execution\Control\ExceptionChain;
76use Deriver\Deriver\Evaluator\Execution\Control\ExceptionMatch;
77use Deriver\Deriver\Evaluator\Execution\Control\Handler;
78use Deriver\Deriver\Evaluator\Execution\Control\LoopConvergence;
79use Deriver\Deriver\Evaluator\Execution\Control\ObservationLimit;
80use Deriver\Deriver\Evaluator\Execution\Control\StateJoin;
81use Deriver\Deriver\Evaluator\Execution\Control\Unwinding;
82use Deriver\Deriver\Evaluator\Execution\Demand\Cell;
83use Deriver\Deriver\Evaluator\Execution\Demand\Components;
84use Deriver\Deriver\Evaluator\Execution\Demand\Discovery;
85use Deriver\Deriver\Evaluator\Execution\Demand\Key;
86use Deriver\Deriver\Evaluator\Execution\Demand\Table;
87use Deriver\Deriver\Evaluator\Execution\Dependencies;
88use Deriver\Deriver\Evaluator\Execution\Evaluator;
89use Deriver\Deriver\Evaluator\Execution\EvaluatorConfig as ExecutionEvaluatorConfig;
90use Deriver\Deriver\Evaluator\Execution\Havoc;
91use Deriver\Deriver\Evaluator\Execution\InstructionTransfer;
92use Deriver\Deriver\Evaluator\Execution\Memory\Location;
93use Deriver\Deriver\Evaluator\Execution\Memory\Materialization;
94use Deriver\Deriver\Evaluator\Execution\Memory\Memory;
95use Deriver\Deriver\Evaluator\Execution\Memory\ReferenceConstraint;
96use Deriver\Deriver\Evaluator\Execution\Memory\Slots\SlotReference;
97use Deriver\Deriver\Evaluator\Execution\Memory\Slots\StateStorage;
98use Deriver\Deriver\Evaluator\Execution\Memory\StorageCapture;
99use Deriver\Deriver\Evaluator\Execution\ObservationCollector;
100use Deriver\Deriver\Evaluator\Execution\Offset\Address;
101use Deriver\Deriver\Evaluator\Execution\Offset\Path;
102use Deriver\Deriver\Evaluator\Execution\Offset\Reader;
103use Deriver\Deriver\Evaluator\Execution\Offset\StringAccess;
104use Deriver\Deriver\Evaluator\Execution\Offset\Strings;
105use Deriver\Deriver\Evaluator\Execution\Offset\Transfer;
106use Deriver\Deriver\Evaluator\Execution\Operation\Conversions;
107use Deriver\Deriver\Evaluator\Execution\Operation\ScalarErrors;
108use Deriver\Deriver\Evaluator\Execution\State;
109use Deriver\Deriver\Evaluator\Execution\Summary\CompletionRecord;
110use Deriver\Deriver\Evaluator\Execution\Summary\Evaluation;
111use Deriver\Deriver\Evaluator\Execution\Summary\Invocation as SummaryInvocation;
112use Deriver\Deriver\Evaluator\Execution\Summary\Isolation;
113use Deriver\Deriver\Evaluator\Execution\Trace\Derivation;
114use Deriver\Deriver\Evaluator\Execution\Transfer\CallableTransfer;
115use Deriver\Deriver\Evaluator\Execution\Transfer\CompoundAssignment;
116use Deriver\Deriver\Evaluator\Execution\Transfer\ConstantTransfer;
117use Deriver\Deriver\Evaluator\Execution\Transfer\IntrinsicTransfer;
118use Deriver\Deriver\Evaluator\Execution\Transfer\MemoryStep;
119use Deriver\Deriver\Evaluator\Execution\Transfer\ObjectAccess;
120use Deriver\Deriver\Evaluator\Execution\Transfer\PropertyAccessCheck;
121use Deriver\Deriver\Evaluator\Execution\Transfer\PropertyLookup;
122use Deriver\Deriver\Evaluator\Execution\Transfer\PropertyMagic;
123use Deriver\Deriver\Evaluator\Execution\Transfer\PropertyReference;
124use Deriver\Deriver\Evaluator\Execution\Transfer\PropertySlot;
125use Deriver\Deriver\Evaluator\Execution\Transfer\PropertyTransfer;
126use Deriver\Deriver\Evaluator\Execution\Transfer\PureStep;
127use Deriver\Deriver\Evaluator\Execution\Transfer\ReferenceAssignment;
128use Deriver\Deriver\Evaluator\Request\Budget;
129use Deriver\Deriver\Evaluator\Request\EntryPoint;
130use Deriver\Deriver\Evaluator\Request\QueryScope;
131use Deriver\Deriver\Evaluator\Request\ReturnQuery;
132use Deriver\Deriver\Evaluator\Scalar\Arithmetic;
133use Deriver\Deriver\Evaluator\Scalar\Arrays;
134use Deriver\Deriver\Evaluator\Scalar\Comparison;
135use Deriver\Deriver\Evaluator\Scalar\Identity;
136use Deriver\Deriver\Evaluator\Scalar\Increment;
137use Deriver\Deriver\Evaluator\Scalar\IntegerConversion;
138use Deriver\Deriver\Evaluator\Scalar\Operations;
139use Deriver\Deriver\Evaluator\TargetProfile;
140use Deriver\Deriver\Evaluator\Term;
141use Deriver\Deriver\Evaluator\TermKind;
142use Deriver\Deriver\Execution\DerivationResult;
143use Deriver\Deriver\Execution\QueryExecution;
144use Deriver\Deriver\Execution\ResultAssessment;
145use Deriver\Deriver\Identity\QueryEncoding;
146use Deriver\Deriver\Identity\ValueGraph;
147use Deriver\Deriver\Lowerer\AggregateLowering;
148use Deriver\Deriver\Lowerer\AssignmentLowering;
149use Deriver\Deriver\Lowerer\CallLowering;
150use Deriver\Deriver\Lowerer\Control\ConditionalLowering;
151use Deriver\Deriver\Lowerer\Control\DestructuringLowering;
152use Deriver\Deriver\Lowerer\Control\ExceptionLowering;
153use Deriver\Deriver\Lowerer\Control\LoopLowering;
154use Deriver\Deriver\Lowerer\Declaration\CallableIdentity;
155use Deriver\Deriver\Lowerer\Declaration\CallableSource;
156use Deriver\Deriver\Lowerer\Declaration\ConstantSignatures;
157use Deriver\Deriver\Lowerer\Declaration\Index;
158use Deriver\Deriver\Lowerer\Declaration\Indexer;
159use Deriver\Deriver\Lowerer\Declaration\Metadata\ClassDeclaration;
160use Deriver\Deriver\Lowerer\Declaration\Metadata\PropertyDeclaration;
161use Deriver\Deriver\Lowerer\Declaration\Traits\Composition;
162use Deriver\Deriver\Lowerer\EffectInspection;
163use Deriver\Deriver\Lowerer\ExpressionLowering;
164use Deriver\Deriver\Lowerer\Graph\Argument;
165use Deriver\Deriver\Lowerer\Graph\BasicBlock;
166use Deriver\Deriver\Lowerer\Graph\CallableGraph;
167use Deriver\Deriver\Lowerer\Graph\CatchTarget;
168use Deriver\Deriver\Lowerer\Graph\ExceptionRegion;
169use Deriver\Deriver\Lowerer\Graph\Instruction;
170use Deriver\Deriver\Lowerer\Graph\Parameter;
171use Deriver\Deriver\Lowerer\Graph\Terminator;
172use Deriver\Deriver\Lowerer\Hierarchy\Access as HierarchyAccess;
173use Deriver\Deriver\Lowerer\Hierarchy\Builtins;
174use Deriver\Deriver\Lowerer\Hierarchy\Dispatch;
175use Deriver\Deriver\Lowerer\InstructionEmitter;
176use Deriver\Deriver\Lowerer\Lowerer;
177use Deriver\Deriver\Lowerer\LoweringScope;
178use Deriver\Deriver\Lowerer\StatementLowering;
179use Deriver\Deriver\MicroEvaluatorConversion;
180use Deriver\Deriver\Parser\Node\File;
181use Deriver\Deriver\Parser\Parser;
182use Deriver\Deriver\Parser\ParserConfig;
183use Deriver\Deriver\Parser\TargetSyntax;
184use Deriver\Deriver\Providers\ProviderExpansion;
185use Deriver\Deriver\Resolver\AssignmentPatterns;
186use Deriver\Deriver\Resolver\ClassScope;
187use Deriver\Deriver\Resolver\ExpressionValidation;
188use Deriver\Deriver\Resolver\MagicContext;
189use Deriver\Deriver\Resolver\Resolver;
190use Deriver\Deriver\Selection\QueryValidation;
191use Deriver\Deriver\SnapshotIdentity;
192use Deriver\Deriver\Sources\LineMap;
193use Deriver\Deriver\Sources\Path as SourcesPath;
194use Deriver\Deriver\Sources\Source;
195use Deriver\Deriver\Sources\SourceRef;
196use Deriver\Deriver\Sources\Sources;
197use Deriver\Exception\ParserException;
198use Deriver\Exception\ResolutionException;
199use JsonException;
200use PHPUnit\Framework\Attributes\CoversClass;
201use PHPUnit\Framework\Attributes\DataProvider;
202use PHPUnit\Framework\Attributes\Small;
203use PHPUnit\Framework\Attributes\UsesClass;
204use PHPUnit\Framework\TestCase;
205
206/**
207 * @covers \Deriver\Deriver\Evaluator\Execution\Offset\Transfer
208 */
209#[UsesClass(Resolver::class)]
210#[UsesClass(ExpressionValidation::class)]
211#[UsesClass(ResolutionException::class)]
212#[UsesClass(ParserConfig::class)]
213#[UsesClass(ParserException::class)]
214#[CoversClass(Transfer::class)]
215#[UsesClass(QueryExecution::class)]
216#[UsesClass(QueryValidation::class)]
217#[UsesClass(ResultAssessment::class)]
218#[UsesClass(Deriver::class)]
219#[UsesClass(Constraints::class)]
220#[UsesClass(Argument::class)]
221#[UsesClass(BasicBlock::class)]
222#[UsesClass(CallableGraph::class)]
223#[UsesClass(CallableIdentity::class)]
224#[UsesClass(CatchTarget::class)]
225#[UsesClass(ClassDeclaration::class)]
226#[UsesClass(ExceptionRegion::class)]
227#[UsesClass(Instruction::class)]
228#[UsesClass(Parameter::class)]
229#[UsesClass(PropertyDeclaration::class)]
230#[UsesClass(Terminator::class)]
231#[UsesClass(Allocation::class)]
232#[UsesClass(ArgumentBinding::class)]
233#[UsesClass(ArgumentOrder::class)]
234#[UsesClass(CallExecutor::class)]
235#[UsesClass(CallResolution::class)]
236#[UsesClass(CallableCheck::class)]
237#[UsesClass(Access::class)]
238#[UsesClass(Builtins::class)]
239#[UsesClass(Dispatch::class)]
240#[UsesClass(HierarchyAccess::class)]
241#[UsesClass(Invocation::class)]
242#[UsesClass(MethodInvocation::class)]
243#[UsesClass(Inputs::class)]
244#[UsesClass(NativeArguments::class)]
245#[UsesClass(NativeInvocation::class)]
246#[UsesClass(Properties::class)]
247#[UsesClass(Signatures::class)]
248#[UsesClass(ParameterBinding::class)]
249#[UsesClass(PassedArgument::class)]
250#[UsesClass(Arguments::class)]
251#[UsesClass(Creation::class)]
252#[UsesClass(Methods::class)]
253#[UsesClass(Modes::class)]
254#[UsesClass(Resolution::class)]
255#[UsesClass(Target::class)]
256#[UsesClass(PreparationTransfer::class)]
257#[UsesClass(ProviderDispatch::class)]
258#[UsesClass(TypeBinding::class)]
259#[UsesClass(TypeCheck::class)]
260#[UsesClass(Completion::class)]
261#[UsesClass(ClassNames::class)]
262#[UsesClass(Context::class)]
263#[UsesClass(ExceptionChain::class)]
264#[UsesClass(ExceptionMatch::class)]
265#[UsesClass(Handler::class)]
266#[UsesClass(LoopConvergence::class)]
267#[UsesClass(ObservationLimit::class)]
268#[UsesClass(StateJoin::class)]
269#[UsesClass(Unwinding::class)]
270#[UsesClass(Cell::class)]
271#[UsesClass(Components::class)]
272#[UsesClass(Discovery::class)]
273#[UsesClass(Key::class)]
274#[UsesClass(Table::class)]
275#[UsesClass(Dependencies::class)]
276#[UsesClass(Havoc::class)]
277#[UsesClass(InstructionTransfer::class)]
278#[UsesClass(Evaluator::class)]
279#[UsesClass(SlotReference::class)]
280#[UsesClass(StateStorage::class)]
281#[UsesClass(ObservationCollector::class)]
282#[UsesClass(Address::class)]
283#[UsesClass(Path::class)]
284#[UsesClass(Reader::class)]
285#[UsesClass(StringAccess::class)]
286#[UsesClass(Strings::class)]
287#[UsesClass(Conversions::class)]
288#[UsesClass(ScalarErrors::class)]
289#[UsesClass(State::class)]
290#[UsesClass(CompletionRecord::class)]
291#[UsesClass(Evaluation::class)]
292#[UsesClass(SummaryInvocation::class)]
293#[UsesClass(Isolation::class)]
294#[UsesClass(CallableTransfer::class)]
295#[UsesClass(CompoundAssignment::class)]
296#[UsesClass(ConstantTransfer::class)]
297#[UsesClass(IntrinsicTransfer::class)]
298#[UsesClass(MemoryStep::class)]
299#[UsesClass(ObjectAccess::class)]
300#[UsesClass(PropertyAccessCheck::class)]
301#[UsesClass(PropertyLookup::class)]
302#[UsesClass(PropertyMagic::class)]
303#[UsesClass(PropertyReference::class)]
304#[UsesClass(PropertySlot::class)]
305#[UsesClass(PropertyTransfer::class)]
306#[UsesClass(PureStep::class)]
307#[UsesClass(ReferenceAssignment::class)]
308#[UsesClass(Location::class)]
309#[UsesClass(Materialization::class)]
310#[UsesClass(Memory::class)]
311#[UsesClass(ReferenceConstraint::class)]
312#[UsesClass(StorageCapture::class)]
313#[UsesClass(ArgumentBindings::class)]
314#[UsesClass(BoundArgument::class)]
315#[UsesClass(FunctionEvaluator::class)]
316#[UsesClass(Library::class)]
317#[UsesClass(ScalarFunctions::class)]
318#[UsesClass(CallDescription::class)]
319#[UsesClass(CallEvaluator::class)]
320#[UsesClass(PlanActions::class)]
321#[UsesClass(PlanCompiler::class)]
322#[UsesClass(PlanFootprints::class)]
323#[UsesClass(PlanValidation::class)]
324#[UsesClass(CallDecision::class)]
325#[UsesClass(CallDescriptor::class)]
326#[UsesClass(Action::class)]
327#[UsesClass(Expression::class)]
328#[UsesClass(SemanticPlan::class)]
329#[UsesClass(DispatchDecision::class)]
330#[UsesClass(DispatchRequest::class)]
331#[UsesClass(Extensions::class)]
332#[UsesClass(ProviderExpansion::class)]
333#[UsesClass(Evaluators::class)]
334#[UsesClass(StateRegistry::class)]
335#[UsesClass(DeclarationParameter::class)]
336#[UsesClass(Signature::class)]
337#[UsesClass(DeriverConfig::class)]
338#[UsesClass(EntryPoint::class)]
339#[UsesClass(Sources::class)]
340#[UsesClass(SourcesPath::class)]
341#[UsesClass(Snapshot::class)]
342#[UsesClass(Source::class)]
343#[UsesClass(TargetProfile::class)]
344#[UsesClass(Budget::class)]
345#[UsesClass(QueryScope::class)]
346#[UsesClass(ReturnQuery::class)]
347#[UsesClass(ResultRef::class)]
348#[UsesClass(SourceRef::class)]
349#[UsesClass(Alternative::class)]
350#[UsesClass(Assessment::class)]
351#[UsesClass(Derivation::class)]
352#[UsesClass(DerivationResult::class)]
353#[UsesClass(Exceptional::class)]
354#[UsesClass(Frontier::class)]
355#[UsesClass(QueryEncoding::class)]
356#[UsesClass(ValueGraph::class)]
357#[UsesClass(Statistics::class)]
358#[UsesClass(StorageSnapshot::class)]
359#[UsesClass(Parser::class)]
360#[UsesClass(File::class)]
361#[UsesClass(AggregateLowering::class)]
362#[UsesClass(AssignmentLowering::class)]
363#[UsesClass(CallLowering::class)]
364#[UsesClass(Lowerer::class)]
365#[UsesClass(ConditionalLowering::class)]
366#[UsesClass(DestructuringLowering::class)]
367#[UsesClass(ExceptionLowering::class)]
368#[UsesClass(LoopLowering::class)]
369#[UsesClass(EffectInspection::class)]
370#[UsesClass(ExpressionLowering::class)]
371#[UsesClass(InstructionEmitter::class)]
372#[UsesClass(LoweringScope::class)]
373#[UsesClass(StatementLowering::class)]
374#[UsesClass(ConstantSignatures::class)]
375#[UsesClass(CallableSource::class)]
376#[UsesClass(Indexer::class)]
377#[UsesClass(Index::class)]
378#[UsesClass(Composition::class)]
379#[UsesClass(LineMap::class)]
380#[UsesClass(MagicContext::class)]
381#[UsesClass(AssignmentPatterns::class)]
382#[UsesClass(ClassScope::class)]
383#[UsesClass(TargetSyntax::class)]
384#[UsesClass(Arithmetic::class)]
385#[UsesClass(Arrays::class)]
386#[UsesClass(Comparison::class)]
387#[UsesClass(Identity::class)]
388#[UsesClass(Increment::class)]
389#[UsesClass(IntegerConversion::class)]
390#[UsesClass(Operations::class)]
391#[UsesClass(Term::class)]
392#[Small]
393final class TransferTest extends TestCase
394{
395    public function testApplyCreatesAReferenceCellForAnAbsentArrayElement(): void
396    {
397        $source = '<?php function target(){return 1;}';
398        $budget = new Budget();
399        $configuration = new DeriverConfig();
400        $contributions = new Contributions();
401        $input = new Sources([new Source('fixture.php', $source)]);
402        $target = new TargetProfile($configuration->phpVersion, $configuration->integerBits, $configuration->floatPrecision);
403        $providers = new ProviderExpansion($input, array_map(Term::fromNative(...), $configuration->environment), $contributions);
404        $contributions1 = $providers->contributions;
405        $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);
406        $settings = new EvaluatorConfig($target, $providers->environment, $configuration->standardEvaluators, array_map((new MicroEvaluatorConversion())->convert(...), $configuration->evaluators));
407        $identity = new SnapshotIdentity($providers->input, $settings, $evaluators, $providers->manifest);
408        $snapshotId = $identity->id;
409        $input1 = $providers->input;
410        $parser = new Parser(new ParserConfig($target->version));
411        $trees = array_map(static fn (Source $source1): File => (new Resolver())->resolve($parser->parse(new Source(SourcesPath::normalize($source1->path), $source1->contents, $source1->declarationsOnly))), iterator_to_array($input1, false));
412        $program1 = new Index($snapshotId, $trees);
413        $execution = new QueryExecution($program1, new ExecutionEvaluatorConfig($target, $providers->environment, $configuration->standardEvaluators), $evaluators, $identity->snapshot(array_map(Frontier::fromDiagnostic(...), $program1->diagnostics())));
414        $snapshotId1 = 'test';
415        $input2 = new Sources([new Source('fixture.php', $source)]);
416        $profile = $execution->configuration->target;
417        $parser1 = new Parser(new ParserConfig($profile->version));
418        $trees1 = array_map(static fn (Source $source2): File => (new Resolver())->resolve($parser1->parse(new Source(SourcesPath::normalize($source2->path), $source2->contents, $source2->declarationsOnly))), iterator_to_array($input2, false));
419        $program = new Index($snapshotId1, $trees1);
420        $context = new Context($program, new ReturnQuery('target', budget: $budget), $execution->configuration, $execution->evaluators);
421        $body = $context->program->callable('target');
422        self::assertNotNull($body);
423        $instruction = new BoundInstruction('offset', 'write', $body->source, 'result', ['slot', 'rhs']);
424        $state = new State();
425        $base = $state->memory->allocate(Term::constant(null));
426        $state->addresses['base'] = $base;
427        $state->offsets['slot'] = new Address('base', Term::constant('x'));
428        $path = new Path($context);
429        $read = new BoundInstruction('reference', 'reference', $body->source, 'result', ['slot']);
430        $paths = (new Transfer(new Evaluator($context)))->apply($body, $read, $state);
431        self::assertCount(1, $paths);
432        self::assertSame(TermKind::Cell, $paths[0]->registers['result']->kind);
433        self::assertSame(['x' => null], $paths[0]->memory->materialize($paths[0]->memory->read($base))->native());
434    }
435    public function testStringRejectsReferencesWithoutChangingTheContainingString(): void
436    {
437        $source = '<?php function target(){return 1;}';
438        $budget = new Budget();
439        $configuration = new DeriverConfig();
440        $contributions = new Contributions();
441        $input = new Sources([new Source('fixture.php', $source)]);
442        $target = new TargetProfile($configuration->phpVersion, $configuration->integerBits, $configuration->floatPrecision);
443        $providers = new ProviderExpansion($input, array_map(Term::fromNative(...), $configuration->environment), $contributions);
444        $contributions1 = $providers->contributions;
445        $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);
446        $settings = new EvaluatorConfig($target, $providers->environment, $configuration->standardEvaluators, array_map((new MicroEvaluatorConversion())->convert(...), $configuration->evaluators));
447        $identity = new SnapshotIdentity($providers->input, $settings, $evaluators, $providers->manifest);
448        $snapshotId = $identity->id;
449        $input1 = $providers->input;
450        $parser = new Parser(new ParserConfig($target->version));
451        $trees = array_map(static fn (Source $source1): File => (new Resolver())->resolve($parser->parse(new Source(SourcesPath::normalize($source1->path), $source1->contents, $source1->declarationsOnly))), iterator_to_array($input1, false));
452        $program1 = new Index($snapshotId, $trees);
453        $execution = new QueryExecution($program1, new ExecutionEvaluatorConfig($target, $providers->environment, $configuration->standardEvaluators), $evaluators, $identity->snapshot(array_map(Frontier::fromDiagnostic(...), $program1->diagnostics())));
454        $snapshotId1 = 'test';
455        $input2 = new Sources([new Source('fixture.php', $source)]);
456        $profile = $execution->configuration->target;
457        $parser1 = new Parser(new ParserConfig($profile->version));
458        $trees1 = array_map(static fn (Source $source2): File => (new Resolver())->resolve($parser1->parse(new Source(SourcesPath::normalize($source2->path), $source2->contents, $source2->declarationsOnly))), iterator_to_array($input2, false));
459        $program = new Index($snapshotId1, $trees1);
460        $context = new Context($program, new ReturnQuery('target', budget: $budget), $execution->configuration, $execution->evaluators);
461        $body = $context->program->callable('target');
462        self::assertNotNull($body);
463        $instruction = new BoundInstruction('offset', 'write', $body->source, 'result', ['slot', 'rhs']);
464        $state = new State();
465        $location = $state->memory->allocate(Term::constant('abc'));
466        $access = new StringAccess($location, Term::constant(1));
467        $reference = new BoundInstruction('reference', 'reference', $body->source, 'result', ['slot']);
468        $paths = (new Transfer(new Evaluator($context)))->string($reference, $state, $access);
469        self::assertSame('Error', $paths[0]->completion->value?->literal);
470        self::assertSame('abc', $paths[0]->memory->read($location)->native());
471    }
472    public function testResultMarksConcreteErrorsAsExceptional(): void
473    {
474        $source = '<?php function target(){return 1;}';
475        $budget = new Budget();
476        $configuration = new DeriverConfig();
477        $contributions = new Contributions();
478        $input = new Sources([new Source('fixture.php', $source)]);
479        $target = new TargetProfile($configuration->phpVersion, $configuration->integerBits, $configuration->floatPrecision);
480        $providers = new ProviderExpansion($input, array_map(Term::fromNative(...), $configuration->environment), $contributions);
481        $contributions1 = $providers->contributions;
482        $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);
483        $settings = new EvaluatorConfig($target, $providers->environment, $configuration->standardEvaluators, array_map((new MicroEvaluatorConversion())->convert(...), $configuration->evaluators));
484        $identity = new SnapshotIdentity($providers->input, $settings, $evaluators, $providers->manifest);
485        $snapshotId = $identity->id;
486        $input1 = $providers->input;
487        $parser = new Parser(new ParserConfig($target->version));
488        $trees = array_map(static fn (Source $source1): File => (new Resolver())->resolve($parser->parse(new Source(SourcesPath::normalize($source1->path), $source1->contents, $source1->declarationsOnly))), iterator_to_array($input1, false));
489        $program1 = new Index($snapshotId, $trees);
490        $execution = new QueryExecution($program1, new ExecutionEvaluatorConfig($target, $providers->environment, $configuration->standardEvaluators), $evaluators, $identity->snapshot(array_map(Frontier::fromDiagnostic(...), $program1->diagnostics())));
491        $snapshotId1 = 'test';
492        $input2 = new Sources([new Source('fixture.php', $source)]);
493        $profile = $execution->configuration->target;
494        $parser1 = new Parser(new ParserConfig($profile->version));
495        $trees1 = array_map(static fn (Source $source2): File => (new Resolver())->resolve($parser1->parse(new Source(SourcesPath::normalize($source2->path), $source2->contents, $source2->declarationsOnly))), iterator_to_array($input2, false));
496        $program = new Index($snapshotId1, $trees1);
497        $context = new Context($program, new ReturnQuery('target', budget: $budget), $execution->configuration, $execution->evaluators);
498        $body = $context->program->callable('target');
499        self::assertNotNull($body);
500        $instruction = new BoundInstruction('offset', 'write', $body->source, 'result', ['slot', 'rhs']);
501        $paths = (new Transfer(new Evaluator($context)))->result(new State(), $instruction, new Term(TermKind::Throwable, 'TypeError'));
502        self::assertSame(CompletionKind::Throw, $paths[0]->completion->kind);
503        self::assertSame('TypeError', $paths[0]->completion->value?->literal);
504    }
505    public function testBoundaryPreservesBothExitKindsAndInvalidatesAffectedState(): void
506    {
507        $source = '<?php function target(){return 1;}';
508        $budget = new Budget();
509        $configuration = new DeriverConfig();
510        $contributions = new Contributions();
511        $input = new Sources([new Source('fixture.php', $source)]);
512        $target = new TargetProfile($configuration->phpVersion, $configuration->integerBits, $configuration->floatPrecision);
513        $providers = new ProviderExpansion($input, array_map(Term::fromNative(...), $configuration->environment), $contributions);
514        $contributions1 = $providers->contributions;
515        $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);
516        $settings = new EvaluatorConfig($target, $providers->environment, $configuration->standardEvaluators, array_map((new MicroEvaluatorConversion())->convert(...), $configuration->evaluators));
517        $identity = new SnapshotIdentity($providers->input, $settings, $evaluators, $providers->manifest);
518        $snapshotId = $identity->id;
519        $input1 = $providers->input;
520        $parser = new Parser(new ParserConfig($target->version));
521        $trees = array_map(static fn (Source $source1): File => (new Resolver())->resolve($parser->parse(new Source(SourcesPath::normalize($source1->path), $source1->contents, $source1->declarationsOnly))), iterator_to_array($input1, false));
522        $program1 = new Index($snapshotId, $trees);
523        $execution = new QueryExecution($program1, new ExecutionEvaluatorConfig($target, $providers->environment, $configuration->standardEvaluators), $evaluators, $identity->snapshot(array_map(Frontier::fromDiagnostic(...), $program1->diagnostics())));
524        $snapshotId1 = 'test';
525        $input2 = new Sources([new Source('fixture.php', $source)]);
526        $profile = $execution->configuration->target;
527        $parser1 = new Parser(new ParserConfig($profile->version));
528        $trees1 = array_map(static fn (Source $source2): File => (new Resolver())->resolve($parser1->parse(new Source(SourcesPath::normalize($source2->path), $source2->contents, $source2->declarationsOnly))), iterator_to_array($input2, false));
529        $program = new Index($snapshotId1, $trees1);
530        $context = new Context($program, new ReturnQuery('target', budget: $budget), $execution->configuration, $execution->evaluators);
531        $body = $context->program->callable('target');
532        self::assertNotNull($body);
533        $instruction = new BoundInstruction('offset', 'write', $body->source, 'result', ['slot', 'rhs']);
534        $state = new State();
535        $location = $state->local('value');
536        $state->memory->write($location, Term::constant(1));
537        $paths = (new Transfer(new Evaluator($context)))->boundary($state, $instruction, Term::opaque('OFFSET_OPERATION'));
538        self::assertCount(2, $paths);
539        self::assertSame(CompletionKind::Normal, $paths[0]->completion->kind);
540        self::assertSame(CompletionKind::Throw, $paths[1]->completion->kind);
541        self::assertSame(1, $paths[0]->memory->read($location)->native());
542        self::assertSame(['UNSUPPORTED_LANGUAGE_FEATURE'], array_column(array_values($context->frontiers), 'code'));
543    }
544    public function testCompoundRejectsStringOffsetsAfterCheckingTheOriginalKey(): void
545    {
546        $source = '<?php function target(){$a="123";$b=0;try{$a[0]+=++$b;}catch(Error $e){return [$a,$b];}return 999;}';
547        $symbol = 'target';
548        $input = new Sources([new Source('fixture.php', $source)]);
549        $configuration = new DeriverConfig();
550        $contributions = new Contributions();
551        $target = new TargetProfile($configuration->phpVersion, $configuration->integerBits, $configuration->floatPrecision);
552        $providers = new ProviderExpansion($input, array_map(Term::fromNative(...), $configuration->environment), $contributions);
553        $contributions1 = $providers->contributions;
554        $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);
555        $settings = new EvaluatorConfig($target, $providers->environment, $configuration->standardEvaluators, array_map((new MicroEvaluatorConversion())->convert(...), $configuration->evaluators));
556        $identity = new SnapshotIdentity($providers->input, $settings, $evaluators, $providers->manifest);
557        $snapshotId = $identity->id;
558        $input1 = $providers->input;
559        $parser = new Parser(new ParserConfig($target->version));
560        $trees = array_map(static fn (Source $source1): File => (new Resolver())->resolve($parser->parse(new Source(SourcesPath::normalize($source1->path), $source1->contents, $source1->declarationsOnly))), iterator_to_array($input1, false));
561        $program = new Index($snapshotId, $trees);
562        $result = (new QueryExecution($program, new ExecutionEvaluatorConfig($target, $providers->environment, $configuration->standardEvaluators), $evaluators, $identity->snapshot(array_map(Frontier::fromDiagnostic(...), $program->diagnostics()))))->derive(new ReturnQuery($symbol));
563        self::assertSame(['123', 1], $result->normalOutcomes[0]->values['return']->native());
564        self::assertSame([], $result->frontiers);
565    }
566    /**
567     * @throws JsonException If recorded engine observations cannot be decoded
568     */
569    #[DataProvider('providerOffsetSemantics')]
570    public function testApplyPreservesRecordedOffsetValuesEffectsAndExceptions(string $source, string $normalJson, string $exception, bool $diagnostic): void
571    {
572        $expected = json_decode($normalJson, true, 512, JSON_THROW_ON_ERROR);
573        $symbol = 'target';
574        $input = new Sources([new Source('fixture.php', $source)]);
575        $configuration = new DeriverConfig();
576        $contributions = new Contributions();
577        $target = new TargetProfile($configuration->phpVersion, $configuration->integerBits, $configuration->floatPrecision);
578        $providers = new ProviderExpansion($input, array_map(Term::fromNative(...), $configuration->environment), $contributions);
579        $contributions1 = $providers->contributions;
580        $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);
581        $settings = new EvaluatorConfig($target, $providers->environment, $configuration->standardEvaluators, array_map((new MicroEvaluatorConversion())->convert(...), $configuration->evaluators));
582        $identity = new SnapshotIdentity($providers->input, $settings, $evaluators, $providers->manifest);
583        $snapshotId = $identity->id;
584        $input1 = $providers->input;
585        $parser = new Parser(new ParserConfig($target->version));
586        $trees = array_map(static fn (Source $source1): File => (new Resolver())->resolve($parser->parse(new Source(SourcesPath::normalize($source1->path), $source1->contents, $source1->declarationsOnly))), iterator_to_array($input1, false));
587        $program = new Index($snapshotId, $trees);
588        $result = (new QueryExecution($program, new ExecutionEvaluatorConfig($target, $providers->environment, $configuration->standardEvaluators), $evaluators, $identity->snapshot(array_map(Frontier::fromDiagnostic(...), $program->diagnostics()))))->derive(new ReturnQuery($symbol));
589        self::assertSame($expected, array_map(static fn (Alternative $outcome) => $outcome->values['return']->native(), $result->normalOutcomes));
590        self::assertSame($exception === '' ? [] : [$exception], array_map(static fn (Exceptional $outcome): int|float|string|bool|null => $outcome->exception->literal, $result->exceptionalOutcomes));
591        self::assertSame($diagnostic, in_array('PHP_WARNING', array_column($result->frontiers, 'code'), true));
592        self::assertSame([], array_diff(array_column($result->frontiers, 'code'), ['PHP_WARNING']));
593    }
594    /**
595     * @return iterable<string,array{string,string,string,bool}>
596     */
597    public static function providerOffsetSemantics(): iterable
598    {
599        foreach (['intWrite' => ['<?php function target(){$a=4;try{$a[0]=2;}catch(Error $e){return $a;}return 999;}', '[4]', '', false], 'nullWrite' => ['<?php function target(){$a=null;$a[0]=2;return $a;}', '[[2]]', '', false], 'falseWrite' => ['<?php function target(){$a=false;$a[0]=2;return $a;}', '[[2]]', '', true], 'arrayKey' => ['<?php function target(){$a=[];try{$a[[]]=2;}catch(TypeError $e){return $a;}return 999;}', '[[]]', '', false], 'nullInvalidKey' => ['<?php function target(){$a=null;try{$a[[]]=2;}catch(TypeError $e){return $a;}return 999;}', '[[]]', '', false], 'nestedInvalidKey' => ['<?php function target(){$a=[];try{$a["x"][[]]=2;}catch(TypeError $e){return $a;}return 999;}', '[{"x":[]}]', '', false], 'nullInvalidRead' => ['<?php function target(){$a=null;try{return $a[[]];}catch(TypeError $e){return "error";}}', '[null]', '', true], 'arrayInvalidRead' => ['<?php function target(){$a=[];try{return $a[[]];}catch(TypeError $e){return "error";}}', '["error"]', '', false], 'nullInvalidIsset' => ['<?php function target(){$a=null;try{return isset($a[[]]);}catch(TypeError $e){return "error";}}', '[false]', '', false], 'arrayInvalidIsset' => ['<?php function target(){$a=[];try{return isset($a[[]]);}catch(TypeError $e){return "error";}}', '["error"]', '', false], 'intRead' => ['<?php function target(){$a=4;return $a[0];}', '[null]', '', true], 'stringRead' => ['<?php function target(){$a="abc";return [$a[0],$a[-1],$a["1"]];}', '[["a","c","b"]]', '', false], 'stringWrite' => ['<?php function target(){$a="abc";$r=($a[1]="xy");return [$r,$a];}', '[["x","axc"]]', '', true], 'stringNegativeWrite' => ['<?php function target(){$a="abc";$a[-1]="X";return $a;}', '["abX"]', '', false], 'stringPad' => ['<?php function target(){$a="a";$a[3]="Z";return $a;}', '["a  Z"]', '', false], 'stringInvalidKey' => ['<?php function target(){$a="abc";try{$a["x"]="Z";}catch(TypeError $e){return $a;}return 999;}', '["abc"]', '', false], 'stringAppend' => ['<?php function target(){$a="abc";try{$a[]="Z";}catch(Error $e){return $a;}return 999;}', '["abc"]', '', false], 'stringEmptyWrite' => ['<?php function target(){$a="abc";try{$a[1]="";}catch(Error $e){return $a;}return 999;}', '["abc"]', '', false], 'stringArrayWrite' => ['<?php function target(){$a="abc";try{$a[1]=[];}catch(TypeError $e){return $a;}return $a;}', '["aAc"]', '', true], 'appendMax' => ['<?php function target(){$a=[9223372036854775807=>1];try{$a[]=2;}catch(Error $e){return $a;}return 999;}', '[{"9223372036854775807":1}]', '', false], 'appendMaxAfterUnset' => ['<?php function target(){$a=[9223372036854775807=>1];unset($a[9223372036854775807]);$a[]=2;return $a;}', '[{"9223372036854775807":2}]', '', false], 'string-"1.0"-read' => ['<?php function target(){$s="abc";return $s["1.0"];}', '[]', 'TypeError', false], 'string-"1.0"-isset' => ['<?php function target(){$s="abc";return isset($s["1.0"]);}', '[false]', '', false], 'string-"1.0"-write' => ['<?php function target(){$s="abc";$r=($s["1.0"]="X");return [$r,$s];}', '[]', 'TypeError', false], 'string-"1x"-read' => ['<?php function target(){$s="abc";return $s["1x"];}', '["b"]', '', true], 'string-"1x"-isset' => ['<?php function target(){$s="abc";return isset($s["1x"]);}', '[false]', '', false], 'string-"1x"-write' => ['<?php function target(){$s="abc";$r=($s["1x"]="X");return [$r,$s];}', '[["X","aXc"]]', '', true], 'string-" 1 "-read' => ['<?php function target(){$s="abc";return $s[" 1 "];}', '["b"]', '', false], 'string-" 1 "-isset' => ['<?php function target(){$s="abc";return isset($s[" 1 "]);}', '[true]', '', false], 'string-" 1 "-write' => ['<?php function target(){$s="abc";$r=($s[" 1 "]="X");return [$r,$s];}', '[["X","aXc"]]', '', false], 'string-"+1"-read' => ['<?php function target(){$s="abc";return $s["+1"];}', '["b"]', '', false], 'string-"+1"-isset' => ['<?php function target(){$s="abc";return isset($s["+1"]);}', '[true]', '', false], 'string-"+1"-write' => ['<?php function target(){$s="abc";$r=($s["+1"]="X");return [$r,$s];}', '[["X","aXc"]]', '', false], 'string-"01"-read' => ['<?php function target(){$s="abc";return $s["01"];}', '["b"]', '', false], 'string-"01"-isset' => ['<?php function target(){$s="abc";return isset($s["01"]);}', '[true]', '', false], 'string-"01"-write' => ['<?php function target(){$s="abc";$r=($s["01"]="X");return [$r,$s];}', '[["X","aXc"]]', '', false], 'string-1.5-read' => ['<?php function target(){$s="abc";return $s[1.5];}', '["b"]', '', true], 'string-1.5-isset' => ['<?php function target(){$s="abc";return isset($s[1.5]);}', '[true]', '', true], 'string-1.5-write' => ['<?php function target(){$s="abc";$r=($s[1.5]="X");return [$r,$s];}', '[["X","aXc"]]', '', true], 'string-true-read' => ['<?php function target(){$s="abc";return $s[true];}', '["b"]', '', true], 'string-true-isset' => ['<?php function target(){$s="abc";return isset($s[true]);}', '[true]', '', false], 'string-true-write' => ['<?php function target(){$s="abc";$r=($s[true]="X");return [$r,$s];}', '[["X","aXc"]]', '', true], 'string-null-read' => ['<?php function target(){$s="abc";return $s[null];}', '["a"]', '', true], 'string-null-isset' => ['<?php function target(){$s="abc";return isset($s[null]);}', '[true]', '', false], 'string-null-write' => ['<?php function target(){$s="abc";$r=($s[null]="X");return [$r,$s];}', '[["X","Xbc"]]', '', true], 'string-[]-read' => ['<?php function target(){$s="abc";return $s[[]];}', '[]', 'TypeError', false], 'string-[]-isset' => ['<?php function target(){$s="abc";return isset($s[[]]);}', '[false]', '', false], 'string-[]-write' => ['<?php function target(){$s="abc";$r=($s[[]]="X");return [$r,$s];}', '[]', 'TypeError', false], 'string-"9223372036854775807"-read' => ['<?php function target(){$s="abc";return $s["9223372036854775807"];}', '[""]', '', true], 'string-"9223372036854775807"-isset' => ['<?php function target(){$s="abc";return isset($s["9223372036854775807"]);}', '[false]', '', false], 'string-"9223372036854775808"-read' => ['<?php function target(){$s="abc";return $s["9223372036854775808"];}', '[]', 'TypeError', false], 'string-"9223372036854775808"-isset' => ['<?php function target(){$s="abc";return isset($s["9223372036854775808"]);}', '[false]', '', false], 'string-bounds--5' => ['<?php function target(){$s="abc";$r=($s[-5]="X");return [$r,$s];}', '[[null,"abc"]]', '', true], 'string-bounds--4' => ['<?php function target(){$s="abc";$r=($s[-4]="X");return [$r,$s];}', '[[null,"abc"]]', '', true], 'string-bounds--3' => ['<?php function target(){$s="abc";$r=($s[-3]="X");return [$r,$s];}', '[["X","Xbc"]]', '', false], 'string-bounds-3' => ['<?php function target(){$s="abc";$r=($s[3]="X");return [$r,$s];}', '[["X","abcX"]]', '', false], 'string-bounds-4' => ['<?php function target(){$s="abc";$r=($s[4]="X");return [$r,$s];}', '[["X","abc X"]]', '', false], 'literalString' => ['<?php function target(){return "abc"[1];}', '["b"]', '', false], 'literalArrayInvalid' => ['<?php function target(){return [1][[]];}', '[]', 'TypeError', false], 'literalScalar' => ['<?php function target(){return (4)[0];}', '[null]', '', true], 'nestedStringRead' => ['<?php function target(){$a=["abc"];return $a[0][-1][0];}', '["c"]', '', false], 'nestedStringWrite' => ['<?php function target(){$a=["abc"];$a[0][-1]="X";return $a;}', '[["abX"]]', '', false], 'privateArray' => ['<?php class B{private array $x=[];}function target(){$b=new B;try{$b->x[0]=1;}catch(Error $e){return "private";}return 999;}', '["private"]', '', false], 'privateArrayIsset' => ['<?php class B{private array $x=[1];}function target(){$b=new B;return isset($b->x[0]);}', '[false]', '', false], 'typedArrayCreate' => ['<?php class B{public array $x;}function target(){$b=new B;$b->x[0]=1;return $b->x;}', '[[1]]', '', false], 'typedNullableInt' => ['<?php class B{public ?int $x;}function target(){$b=new B;try{$b->x[0]=1;}catch(TypeError $e){return isset($b->x);}return 999;}', '[false]', '', false], 'typedNullableIntNull' => ['<?php class B{public ?int $x=null;}function target(){$b=new B;try{$b->x[0]=1;}catch(TypeError $e){return $b->x;}return 999;}', '[null]', '', false], 'staticArray' => ['<?php class B{public static array $x=[1];}function target(){B::$x[]=2;return B::$x;}', '[[1,2]]', '', false], 'readonlyArray' => ['<?php class B{public readonly array $x;function __construct(){$this->x=[1];}}function target(){$b=new B;try{$b->x[0]=2;}catch(Error $e){return $b->x;}return 999;}', '[[1]]', '', false], 'arrayReference' => ['<?php function target(){$a=[];$r=&$a["x"];$r=2;return $a;}', '[{"x":2}]', '', false], 'arrayReferenceInitial' => ['<?php function target(){$a=[];$r=&$a["x"];return [$a,$r];}', '[[{"x":null},null]]', '', false], 'arrayAliasDestination' => ['<?php function target(){$a=null;$b=2;$a["x"]=&$b;$b=3;return $a;}', '[{"x":3}]', '', false], 'stringReference' => ['<?php function target(){$a="abc";try{$r=&$a[0];}catch(Error $e){return $a;}return 999;}', '["abc"]', '', false], 'stringUnset' => ['<?php function target(){$a="abc";try{unset($a[0]);}catch(Error $e){return $a;}return 999;}', '["abc"]', '', false], 'scalarUnset' => ['<?php function target(){$a=4;unset($a[0]);return $a;}', '[]', 'Error', false], 'stringIncrement' => ['<?php function target(){$a="abc";try{$a[0]++;}catch(Error $e){return $a;}return 999;}', '["abc"]', '', false], 'arrayMissingIncrement' => ['<?php function target(){$a=[];$a[0]++;return $a;}', '[[1]]', '', true], 'plainObject' => ['<?php function target(){$a=new stdClass;try{$a[0]=1;}catch(Error $e){return "object";}return 999;}', '["object"]', '', false], 'arrayFractionalKey' => ['<?php function target(){$a=[];$a[1.5]=2;return $a;}', '[{"1":2}]', '', true], 'arrayaccess:read-value' => ['<?php class Box implements ArrayAccess {public array $items=["x"=>[1],"n"=>2];public array $log=[];public function offsetExists(mixed $key):bool{$this->log[]="exists:".$key;return isset($this->items[$key]);}public function offsetGet(mixed $key):mixed{$this->log[]="get:".$key;return $this->items[$key];}public function offsetSet(mixed $key,mixed $value):void{$this->log[]="set:".$key;$this->items[$key]=$value;}public function offsetUnset(mixed $key):void{$this->log[]="unset:".$key;unset($this->items[$key]);}}function target(){$b=new Box;$r=$b["n"];return [$r,$b->items,$b->log];}', '[[2,{"x":[1],"n":2},["get:n"]]]', '', false], 'arrayaccess:write-value' => ['<?php class Box implements ArrayAccess {public array $items=["x"=>[1],"n"=>2];public array $log=[];public function offsetExists(mixed $key):bool{$this->log[]="exists:".$key;return isset($this->items[$key]);}public function offsetGet(mixed $key):mixed{$this->log[]="get:".$key;return $this->items[$key];}public function offsetSet(mixed $key,mixed $value):void{$this->log[]="set:".$key;$this->items[$key]=$value;}public function offsetUnset(mixed $key):void{$this->log[]="unset:".$key;unset($this->items[$key]);}}function target(){$b=new Box;$r=($b["n"]=3);return [$r,$b->items,$b->log];}', '[[3,{"x":[1],"n":3},["set:n"]]]', '', false], 'arrayaccess:isset-value' => ['<?php class Box implements ArrayAccess {public array $items=["x"=>[1],"n"=>2];public array $log=[];public function offsetExists(mixed $key):bool{$this->log[]="exists:".$key;return isset($this->items[$key]);}public function offsetGet(mixed $key):mixed{$this->log[]="get:".$key;return $this->items[$key];}public function offsetSet(mixed $key,mixed $value):void{$this->log[]="set:".$key;$this->items[$key]=$value;}public function offsetUnset(mixed $key):void{$this->log[]="unset:".$key;unset($this->items[$key]);}}function target(){$b=new Box;$r=isset($b["n"]);return [$r,$b->items,$b->log];}', '[[true,{"x":[1],"n":2},["exists:n"]]]', '', false], 'arrayaccess:coalesce-value' => ['<?php class Box implements ArrayAccess {public array $items=["x"=>[1],"n"=>2];public array $log=[];public function offsetExists(mixed $key):bool{$this->log[]="exists:".$key;return isset($this->items[$key]);}public function offsetGet(mixed $key):mixed{$this->log[]="get:".$key;return $this->items[$key];}public function offsetSet(mixed $key,mixed $value):void{$this->log[]="set:".$key;$this->items[$key]=$value;}public function offsetUnset(mixed $key):void{$this->log[]="unset:".$key;unset($this->items[$key]);}}function target(){$b=new Box;$r=$b["n"]??9;return [$r,$b->items,$b->log];}', '[[2,{"x":[1],"n":2},["exists:n","get:n"]]]', '', false], 'arrayaccess:empty-value' => ['<?php class Box implements ArrayAccess {public array $items=["x"=>[1],"n"=>2];public array $log=[];public function offsetExists(mixed $key):bool{$this->log[]="exists:".$key;return isset($this->items[$key]);}public function offsetGet(mixed $key):mixed{$this->log[]="get:".$key;return $this->items[$key];}public function offsetSet(mixed $key,mixed $value):void{$this->log[]="set:".$key;$this->items[$key]=$value;}public function offsetUnset(mixed $key):void{$this->log[]="unset:".$key;unset($this->items[$key]);}}function target(){$b=new Box;$r=empty($b["n"]);return [$r,$b->items,$b->log];}', '[[false,{"x":[1],"n":2},["exists:n","get:n"]]]', '', false], 'arrayaccess:unset-value' => ['<?php class Box implements ArrayAccess {public array $items=["x"=>[1],"n"=>2];public array $log=[];public function offsetExists(mixed $key):bool{$this->log[]="exists:".$key;return isset($this->items[$key]);}public function offsetGet(mixed $key):mixed{$this->log[]="get:".$key;return $this->items[$key];}public function offsetSet(mixed $key,mixed $value):void{$this->log[]="set:".$key;$this->items[$key]=$value;}public function offsetUnset(mixed $key):void{$this->log[]="unset:".$key;unset($this->items[$key]);}}function target(){$b=new Box;unset($b["n"]);$r=1;return [$r,$b->items,$b->log];}', '[[1,{"x":[1]},["unset:n"]]]', '', false], 'arrayaccess:increment-value' => ['<?php class Box implements ArrayAccess {public array $items=["x"=>[1],"n"=>2];public array $log=[];public function offsetExists(mixed $key):bool{$this->log[]="exists:".$key;return isset($this->items[$key]);}public function offsetGet(mixed $key):mixed{$this->log[]="get:".$key;return $this->items[$key];}public function offsetSet(mixed $key,mixed $value):void{$this->log[]="set:".$key;$this->items[$key]=$value;}public function offsetUnset(mixed $key):void{$this->log[]="unset:".$key;unset($this->items[$key]);}}function target(){$b=new Box;$r=$b["n"]++;return [$r,$b->items,$b->log];}', '[[2,{"x":[1],"n":2},["get:n"]]]', '', true], 'arrayaccess:compound-value' => ['<?php class Box implements ArrayAccess {public array $items=["x"=>[1],"n"=>2];public array $log=[];public function offsetExists(mixed $key):bool{$this->log[]="exists:".$key;return isset($this->items[$key]);}public function offsetGet(mixed $key):mixed{$this->log[]="get:".$key;return $this->items[$key];}public function offsetSet(mixed $key,mixed $value):void{$this->log[]="set:".$key;$this->items[$key]=$value;}public function offsetUnset(mixed $key):void{$this->log[]="unset:".$key;unset($this->items[$key]);}}function target(){$b=new Box;$r=($b["n"]+=3);return [$r,$b->items,$b->log];}', '[[5,{"x":[1],"n":5},["get:n","set:n"]]]', '', false], 'arrayaccess:nestedwrite-value' => ['<?php class Box implements ArrayAccess {public array $items=["x"=>[1],"n"=>2];public array $log=[];public function offsetExists(mixed $key):bool{$this->log[]="exists:".$key;return isset($this->items[$key]);}public function offsetGet(mixed $key):mixed{$this->log[]="get:".$key;return $this->items[$key];}public function offsetSet(mixed $key,mixed $value):void{$this->log[]="set:".$key;$this->items[$key]=$value;}public function offsetUnset(mixed $key):void{$this->log[]="unset:".$key;unset($this->items[$key]);}}function target(){$b=new Box;$b["x"][0]=3;$r=1;return [$r,$b->items,$b->log];}', '[[1,{"x":[1],"n":2},["get:x"]]]', '', true], 'arrayaccess:nestedread-value' => ['<?php class Box implements ArrayAccess {public array $items=["x"=>[1],"n"=>2];public array $log=[];public function offsetExists(mixed $key):bool{$this->log[]="exists:".$key;return isset($this->items[$key]);}public function offsetGet(mixed $key):mixed{$this->log[]="get:".$key;return $this->items[$key];}public function offsetSet(mixed $key,mixed $value):void{$this->log[]="set:".$key;$this->items[$key]=$value;}public function offsetUnset(mixed $key):void{$this->log[]="unset:".$key;unset($this->items[$key]);}}function target(){$b=new Box;$r=$b["x"][0];return [$r,$b->items,$b->log];}', '[[1,{"x":[1],"n":2},["get:x"]]]', '', false], 'arrayaccess:nestedisset-value' => ['<?php class Box implements ArrayAccess {public array $items=["x"=>[1],"n"=>2];public array $log=[];public function offsetExists(mixed $key):bool{$this->log[]="exists:".$key;return isset($this->items[$key]);}public function offsetGet(mixed $key):mixed{$this->log[]="get:".$key;return $this->items[$key];}public function offsetSet(mixed $key,mixed $value):void{$this->log[]="set:".$key;$this->items[$key]=$value;}public function offsetUnset(mixed $key):void{$this->log[]="unset:".$key;unset($this->items[$key]);}}function target(){$b=new Box;$r=isset($b["x"][0]);return [$r,$b->items,$b->log];}', '[[true,{"x":[1],"n":2},["exists:x","get:x"]]]', '', false], 'arrayaccess:reference-value' => ['<?php class Box implements ArrayAccess {public array $items=["x"=>[1],"n"=>2];public array $log=[];public function offsetExists(mixed $key):bool{$this->log[]="exists:".$key;return isset($this->items[$key]);}public function offsetGet(mixed $key):mixed{$this->log[]="get:".$key;return $this->items[$key];}public function offsetSet(mixed $key,mixed $value):void{$this->log[]="set:".$key;$this->items[$key]=$value;}public function offsetUnset(mixed $key):void{$this->log[]="unset:".$key;unset($this->items[$key]);}}function target(){$b=new Box;$r=&$b["n"];$r=9;return [$r,$b->items,$b->log];}', '[[9,{"x":[1],"n":2},["get:n"]]]', '', true], 'arrayaccess:alias-value' => ['<?php class Box implements ArrayAccess {public array $items=["x"=>[1],"n"=>2];public array $log=[];public function offsetExists(mixed $key):bool{$this->log[]="exists:".$key;return isset($this->items[$key]);}public function offsetGet(mixed $key):mixed{$this->log[]="get:".$key;return $this->items[$key];}public function offsetSet(mixed $key,mixed $value):void{$this->log[]="set:".$key;$this->items[$key]=$value;}public function offsetUnset(mixed $key):void{$this->log[]="unset:".$key;unset($this->items[$key]);}}function target(){$b=new Box;$r=9;$b["n"]=&$r;return [$r,$b->items,$b->log];}', '[]', 'Error', true], 'arrayaccess:read-ref' => ['<?php class Box implements ArrayAccess {public array $items=["x"=>[1],"n"=>2];public array $log=[];public function offsetExists(mixed $key):bool{$this->log[]="exists:".$key;return isset($this->items[$key]);}public function &offsetGet(mixed $key):mixed{$this->log[]="get:".$key;return $this->items[$key];}public function offsetSet(mixed $key,mixed $value):void{$this->log[]="set:".$key;$this->items[$key]=$value;}public function offsetUnset(mixed $key):void{$this->log[]="unset:".$key;unset($this->items[$key]);}}function target(){$b=new Box;$r=$b["n"];return [$r,$b->items,$b->log];}', '[[2,{"x":[1],"n":2},["get:n"]]]', '', false], 'arrayaccess:write-ref' => ['<?php class Box implements ArrayAccess {public array $items=["x"=>[1],"n"=>2];public array $log=[];public function offsetExists(mixed $key):bool{$this->log[]="exists:".$key;return isset($this->items[$key]);}public function &offsetGet(mixed $key):mixed{$this->log[]="get:".$key;return $this->items[$key];}public function offsetSet(mixed $key,mixed $value):void{$this->log[]="set:".$key;$this->items[$key]=$value;}public function offsetUnset(mixed $key):void{$this->log[]="unset:".$key;unset($this->items[$key]);}}function target(){$b=new Box;$r=($b["n"]=3);return [$r,$b->items,$b->log];}', '[[3,{"x":[1],"n":3},["set:n"]]]', '', false], 'arrayaccess:isset-ref' => ['<?php class Box implements ArrayAccess {public array $items=["x"=>[1],"n"=>2];public array $log=[];public function offsetExists(mixed $key):bool{$this->log[]="exists:".$key;return isset($this->items[$key]);}public function &offsetGet(mixed $key):mixed{$this->log[]="get:".$key;return $this->items[$key];}public function offsetSet(mixed $key,mixed $value):void{$this->log[]="set:".$key;$this->items[$key]=$value;}public function offsetUnset(mixed $key):void{$this->log[]="unset:".$key;unset($this->items[$key]);}}function target(){$b=new Box;$r=isset($b["n"]);return [$r,$b->items,$b->log];}', '[[true,{"x":[1],"n":2},["exists:n"]]]', '', false], 'arrayaccess:coalesce-ref' => ['<?php class Box implements ArrayAccess {public array $items=["x"=>[1],"n"=>2];public array $log=[];public function offsetExists(mixed $key):bool{$this->log[]="exists:".$key;return isset($this->items[$key]);}public function &offsetGet(mixed $key):mixed{$this->log[]="get:".$key;return $this->items[$key];}public function offsetSet(mixed $key,mixed $value):void{$this->log[]="set:".$key;$this->items[$key]=$value;}public function offsetUnset(mixed $key):void{$this->log[]="unset:".$key;unset($this->items[$key]);}}function target(){$b=new Box;$r=$b["n"]??9;return [$r,$b->items,$b->log];}', '[[2,{"x":[1],"n":2},["exists:n","get:n"]]]', '', false], 'arrayaccess:empty-ref' => ['<?php class Box implements ArrayAccess {public array $items=["x"=>[1],"n"=>2];public array $log=[];public function offsetExists(mixed $key):bool{$this->log[]="exists:".$key;return isset($this->items[$key]);}public function &offsetGet(mixed $key):mixed{$this->log[]="get:".$key;return $this->items[$key];}public function offsetSet(mixed $key,mixed $value):void{$this->log[]="set:".$key;$this->items[$key]=$value;}public function offsetUnset(mixed $key):void{$this->log[]="unset:".$key;unset($this->items[$key]);}}function target(){$b=new Box;$r=empty($b["n"]);return [$r,$b->items,$b->log];}', '[[false,{"x":[1],"n":2},["exists:n","get:n"]]]', '', false], 'arrayaccess:unset-ref' => ['<?php class Box implements ArrayAccess {public array $items=["x"=>[1],"n"=>2];public array $log=[];public function offsetExists(mixed $key):bool{$this->log[]="exists:".$key;return isset($this->items[$key]);}public function &offsetGet(mixed $key):mixed{$this->log[]="get:".$key;return $this->items[$key];}public function offsetSet(mixed $key,mixed $value):void{$this->log[]="set:".$key;$this->items[$key]=$value;}public function offsetUnset(mixed $key):void{$this->log[]="unset:".$key;unset($this->items[$key]);}}function target(){$b=new Box;unset($b["n"]);$r=1;return [$r,$b->items,$b->log];}', '[[1,{"x":[1]},["unset:n"]]]', '', false], 'arrayaccess:increment-ref' => ['<?php class Box implements ArrayAccess {public array $items=["x"=>[1],"n"=>2];public array $log=[];public function offsetExists(mixed $key):bool{$this->log[]="exists:".$key;return isset($this->items[$key]);}public function &offsetGet(mixed $key):mixed{$this->log[]="get:".$key;return $this->items[$key];}public function offsetSet(mixed $key,mixed $value):void{$this->log[]="set:".$key;$this->items[$key]=$value;}public function offsetUnset(mixed $key):void{$this->log[]="unset:".$key;unset($this->items[$key]);}}function target(){$b=new Box;$r=$b["n"]++;return [$r,$b->items,$b->log];}', '[[2,{"x":[1],"n":3},["get:n"]]]', '', false], 'arrayaccess:compound-ref' => ['<?php class Box implements ArrayAccess {public array $items=["x"=>[1],"n"=>2];public array $log=[];public function offsetExists(mixed $key):bool{$this->log[]="exists:".$key;return isset($this->items[$key]);}public function &offsetGet(mixed $key):mixed{$this->log[]="get:".$key;return $this->items[$key];}public function offsetSet(mixed $key,mixed $value):void{$this->log[]="set:".$key;$this->items[$key]=$value;}public function offsetUnset(mixed $key):void{$this->log[]="unset:".$key;unset($this->items[$key]);}}function target(){$b=new Box;$r=($b["n"]+=3);return [$r,$b->items,$b->log];}', '[[5,{"x":[1],"n":5},["get:n","set:n"]]]', '', false], 'arrayaccess:nestedwrite-ref' => ['<?php class Box implements ArrayAccess {public array $items=["x"=>[1],"n"=>2];public array $log=[];public function offsetExists(mixed $key):bool{$this->log[]="exists:".$key;return isset($this->items[$key]);}public function &offsetGet(mixed $key):mixed{$this->log[]="get:".$key;return $this->items[$key];}public function offsetSet(mixed $key,mixed $value):void{$this->log[]="set:".$key;$this->items[$key]=$value;}public function offsetUnset(mixed $key):void{$this->log[]="unset:".$key;unset($this->items[$key]);}}function target(){$b=new Box;$b["x"][0]=3;$r=1;return [$r,$b->items,$b->log];}', '[[1,{"x":[3],"n":2},["get:x"]]]', '', false], 'arrayaccess:nestedread-ref' => ['<?php class Box implements ArrayAccess {public array $items=["x"=>[1],"n"=>2];public array $log=[];public function offsetExists(mixed $key):bool{$this->log[]="exists:".$key;return isset($this->items[$key]);}public function &offsetGet(mixed $key):mixed{$this->log[]="get:".$key;return $this->items[$key];}public function offsetSet(mixed $key,mixed $value):void{$this->log[]="set:".$key;$this->items[$key]=$value;}public function offsetUnset(mixed $key):void{$this->log[]="unset:".$key;unset($this->items[$key]);}}function target(){$b=new Box;$r=$b["x"][0];return [$r,$b->items,$b->log];}', '[[1,{"x":[1],"n":2},["get:x"]]]', '', false], 'arrayaccess:nestedisset-ref' => ['<?php class Box implements ArrayAccess {public array $items=["x"=>[1],"n"=>2];public array $log=[];public function offsetExists(mixed $key):bool{$this->log[]="exists:".$key;return isset($this->items[$key]);}public function &offsetGet(mixed $key):mixed{$this->log[]="get:".$key;return $this->items[$key];}public function offsetSet(mixed $key,mixed $value):void{$this->log[]="set:".$key;$this->items[$key]=$value;}public function offsetUnset(mixed $key):void{$this->log[]="unset:".$key;unset($this->items[$key]);}}function target(){$b=new Box;$r=isset($b["x"][0]);return [$r,$b->items,$b->log];}', '[[true,{"x":[1],"n":2},["exists:x","get:x"]]]', '', false], 'arrayaccess:reference-ref' => ['<?php class Box implements ArrayAccess {public array $items=["x"=>[1],"n"=>2];public array $log=[];public function offsetExists(mixed $key):bool{$this->log[]="exists:".$key;return isset($this->items[$key]);}public function &offsetGet(mixed $key):mixed{$this->log[]="get:".$key;return $this->items[$key];}public function offsetSet(mixed $key,mixed $value):void{$this->log[]="set:".$key;$this->items[$key]=$value;}public function offsetUnset(mixed $key):void{$this->log[]="unset:".$key;unset($this->items[$key]);}}function target(){$b=new Box;$r=&$b["n"];$r=9;return [$r,$b->items,$b->log];}', '[[9,{"x":[1],"n":9},["get:n"]]]', '', false], 'arrayaccess:alias-ref' => ['<?php class Box implements ArrayAccess {public array $items=["x"=>[1],"n"=>2];public array $log=[];public function offsetExists(mixed $key):bool{$this->log[]="exists:".$key;return isset($this->items[$key]);}public function &offsetGet(mixed $key):mixed{$this->log[]="get:".$key;return $this->items[$key];}public function offsetSet(mixed $key,mixed $value):void{$this->log[]="set:".$key;$this->items[$key]=$value;}public function offsetUnset(mixed $key):void{$this->log[]="unset:".$key;unset($this->items[$key]);}}function target(){$b=new Box;$r=9;$b["n"]=&$r;return [$r,$b->items,$b->log];}', '[]', 'Error', false], 'arrayaccess:unset-null' => ['<?php function target(){$a=null;unset($a[0]);return $a;}', '[null]', '', false], 'arrayaccess:unset-false' => ['<?php function target(){$a=false;unset($a[0]);return $a;}', '[false]', '', true], 'arrayaccess:unset-true' => ['<?php function target(){$a=true;unset($a[0]);return $a;}', '[]', 'Error', false], 'arrayaccess:unset-4' => ['<?php function target(){$a=4;unset($a[0]);return $a;}', '[]', 'Error', false], 'arrayaccess:unset-4.5' => ['<?php function target(){$a=4.5;unset($a[0]);return $a;}', '[]', 'Error', false], 'arrayaccess:readonly-object' => ['<?php class Box implements ArrayAccess {public array $items=["x"=>[1],"n"=>2];public array $log=[];public function offsetExists(mixed $key):bool{$this->log[]="exists:".$key;return isset($this->items[$key]);}public function offsetGet(mixed $key):mixed{$this->log[]="get:".$key;return $this->items[$key];}public function offsetSet(mixed $key,mixed $value):void{$this->log[]="set:".$key;$this->items[$key]=$value;}public function offsetUnset(mixed $key):void{$this->log[]="unset:".$key;unset($this->items[$key]);}}class Holder{public readonly Box $box;function __construct(){$this->box=new Box;}}function target(){$h=new Holder;$h->box["n"]=8;return [$h->box->items,$h->box->log];}', '[[{"x":[1],"n":8},["set:n"]]]', '', false], 'arrayaccess:computed-object' => ['<?php class Box implements ArrayAccess {public array $items=["x"=>[1],"n"=>2];public array $log=[];public function offsetExists(mixed $key):bool{$this->log[]="exists:".$key;return isset($this->items[$key]);}public function offsetGet(mixed $key):mixed{$this->log[]="get:".$key;return $this->items[$key];}public function offsetSet(mixed $key,mixed $value):void{$this->log[]="set:".$key;$this->items[$key]=$value;}public function offsetUnset(mixed $key):void{$this->log[]="unset:".$key;unset($this->items[$key]);}}function make(){return new Box;}function target(){return make()["n"];}', '[2]', '', false], 'arrayaccess:computed-coalesce' => ['<?php class Box implements ArrayAccess {public array $items=["x"=>[1],"n"=>2];public array $log=[];public function offsetExists(mixed $key):bool{$this->log[]="exists:".$key;return isset($this->items[$key]);}public function offsetGet(mixed $key):mixed{$this->log[]="get:".$key;return $this->items[$key];}public function offsetSet(mixed $key,mixed $value):void{$this->log[]="set:".$key;$this->items[$key]=$value;}public function offsetUnset(mixed $key):void{$this->log[]="unset:".$key;unset($this->items[$key]);}}function make(){return new Box;}function target(){return make()["absent"]??9;}', '[9]', '', false], 'arrayaccess:literal-coalesce' => ['<?php function target(){return []["absent"]??9;}', '[9]', '', false], 'arrayaccess:literal-empty' => ['<?php function target(){return empty([]["absent"]);}', '[true]', '', false], 'arrayaccess:literal-nested-coalesce' => ['<?php function target(){return []["absent"]["nested"]??9;}', '[9]', '', false], 'compound:local-order' => ['<?php function target(){$a=1;$a+=($a=2);return $a;}', '[4]', '', false], 'compound:array-order' => ['<?php function target(){$a=[1];$a[0]+=($a[0]=2);return $a;}', '[[4]]', '', false], 'compound:string-compound' => ['<?php function target(){$a="abc";try{$a[0].="x";}catch(Throwable $e){return get_class($e);}return $a;}', '["Error"]', '', false], 'compound:string-numeric' => ['<?php function target(){$a="123";try{$a[0]+=2;}catch(Throwable $e){return get_class($e);}return $a;}', '["Error"]', '', false], 'compound:string-rhs' => ['<?php function target(){$a="abc";$b=0;try{$a[0]+=++$b;}catch(Throwable $e){return [$b,get_class($e)];}return $a;}', '[[1,"Error"]]', '', false], 'compound:null-failed' => ['<?php function target(){$a=null;try{$a[0]+=new stdClass;}catch(Throwable $e){return [$a,get_class($e)];}return $a;}', '[[[null],"TypeError"]]', '', true], 'compound:array-failed' => ['<?php function target(){$a=[];try{$a[0]+=new stdClass;}catch(Throwable $e){return [$a,get_class($e)];}return $a;}', '[[[null],"TypeError"]]', '', true], 'compound:string-invalid-key' => ['<?php function target(){$a="123";try{$a[[]]+=2;}catch(Throwable $e){return get_class($e);}return $a;}', '["TypeError"]', '', false], 'compound:key-1.2x' => ['<?php function target(){$s="abc";return $s[\'1.2x\'];}', '[]', 'TypeError', false], 'compound:key-1e2x' => ['<?php function target(){$s="abc";return $s[\'1e2x\'];}', '[]', 'TypeError', false], 'compound:key-1e' => ['<?php function target(){$s="abc";return $s[\'1e\'];}', '["b"]', '', true], 'compound:key-1.x' => ['<?php function target(){$s="abc";return $s[\'1.x\'];}', '[]', 'TypeError', false], 'compound:key-  +001x' => ['<?php function target(){$s="abc";return $s[\'  +001x\'];}', '["b"]', '', true], 'compound:key-9223372036854775807x' => ['<?php function target(){$s="abc";return $s[\'9223372036854775807x\'];}', '[""]', '', true], 'compound:caught-inspection' => ['<?php function target(){try{1/0;}catch(DivisionByZeroError $e){$same=$e;return [get_class($e),$same===$e,$e->getCode(),$e->getPrevious(),$e instanceof Error];}return 999;}', '[["DivisionByZeroError",true,0,null,true]]', '', false], 'compound:caught-rethrow' => ['<?php function target(){try{try{1/0;}catch(DivisionByZeroError $e){throw $e;}}catch(Error $other){return get_class($other);}return 999;}', '["DivisionByZeroError"]', '', false], 'rejected:reference:[]' => ['<?php function target(){$s="abc";$r=&$s[[]];return $s;}', '[]', 'TypeError', false], 'rejected:increment:[]' => ['<?php function target(){$s="abc";$s[[]]++;return $s;}', '[]', 'TypeError', false], 'rejected:unset:[]' => ['<?php function target(){$s="abc";unset($s[[]]);return $s;}', '[]', 'Error', false], 'rejected:nested:[]' => ['<?php function target(){$s="abc";$s[[]][0]="X";return $s;}', '[]', 'TypeError', false], 'rejected:compound:[]' => ['<?php function target(){$s="abc";$s[[]].="X";return $s;}', '[]', 'TypeError', false], 'rejected:reference:"1x"' => ['<?php function target(){$s="abc";$r=&$s["1x"];return $s;}', '[]', 'Error', true], 'rejected:increment:"1x"' => ['<?php function target(){$s="abc";$s["1x"]++;return $s;}', '[]', 'Error', true], 'rejected:unset:"1x"' => ['<?php function target(){$s="abc";unset($s["1x"]);return $s;}', '[]', 'Error', false], 'rejected:nested:"1x"' => ['<?php function target(){$s="abc";$s["1x"][0]="X";return $s;}', '[]', 'Error', true], 'rejected:compound:"1x"' => ['<?php function target(){$s="abc";$s["1x"].="X";return $s;}', '[]', 'Error', true], 'rejected:reference:1.5' => ['<?php function target(){$s="abc";$r=&$s[1.5];return $s;}', '[]', 'Error', true], 'rejected:increment:1.5' => ['<?php function target(){$s="abc";$s[1.5]++;return $s;}', '[]', 'Error', true], 'rejected:unset:1.5' => ['<?php function target(){$s="abc";unset($s[1.5]);return $s;}', '[]', 'Error', false], 'rejected:nested:1.5' => ['<?php function target(){$s="abc";$s[1.5][0]="X";return $s;}', '[]', 'Error', true], 'rejected:compound:1.5' => ['<?php function target(){$s="abc";$s[1.5].="X";return $s;}', '[]', 'Error', true], 'rejected:readonly-array-object' => ['<?php class Box implements ArrayAccess {public array $items=["x"=>[1],"n"=>2];public array $log=[];public function offsetExists(mixed $key):bool{$this->log[]="exists:".$key;return isset($this->items[$key]);}public function offsetGet(mixed $key):mixed{$this->log[]="get:".$key;return $this->items[$key];}public function offsetSet(mixed $key,mixed $value):void{$this->log[]="set:".$key;$this->items[$key]=$value;}public function offsetUnset(mixed $key):void{$this->log[]="unset:".$key;unset($this->items[$key]);}}class Holder{public readonly array $boxes;function __construct(){$this->boxes=[new Box];}}function target(){$h=new Holder;$h->boxes[0]["n"]=8;return [$h->boxes[0]->items,$h->boxes[0]->log];}', '[]', 'Error', false], 'caught:fresh-identities' => ['<?php function caught(){try{1/0;}catch(Error $e){return $e;}}function target(){$objects=[];for($i=0;$i<2;$i++){$objects[]=caught();}return $objects[0]===$objects[1];}', '[false]', '', false]] as $name => $case) {
600            if (!str_contains($case[0], 'ArrayAccess')) {
601                yield $name => $case;
602            }
603        }
604    }
605}
606