// Copyright (c) 2024, the Dart project authors.  Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.

// Verifies that 'is' test is not removed due to an incorrect
// inferred range of LoadClassId of a nullable value.

import 'package:vm/testing/il_matchers.dart';

import 'dart:math';

@pragma('vm:never-inline')
void myprint(Object o) {
  print(o);
}

class RandomValue {
  final bool shouldReturnValue;
  const RandomValue(this.shouldReturnValue);

  @pragma('vm:never-inline')
  String randomString() => Random().nextInt(42).toString();

  @pragma('vm:prefer-inline')
  String? get valueOrNull {
    return shouldReturnValue ? randomString() : null;
  }
}

@pragma('vm:never-inline')
@pragma('vm:testing:print-flow-graph')
void doTest(RandomValue value) {
  if (value.valueOrNull case final String aString) {
    myprint(aString);
  }
}

void main() {
  doTest(RandomValue(true));
  doTest(RandomValue(false));
}

void matchIL$doTest(FlowGraph graph) {
  graph.dump();
  graph.match([
    match.block('Graph', [
      'c_null' << match.Constant(value: null),
      'c_true' << match.Constant(value: true),
    ]),
    match.block('Function', [
      'value' << match.Parameter(index: 0),
      match.CheckStackOverflow(),
      'condition' << match.LoadField('value', slot: 'shouldReturnValue'),
      match.Branch(
        match.StrictCompare('condition', 'c_true', kind: '==='),
        ifTrue: 'B8',
        ifFalse: 'B9',
      ),
    ]),
    'B8' <<
        match.block('Target', ['v19' << match.StaticCall(), match.Goto('B10')]),
    'B9' << match.block('Target', [match.Goto('B10')]),
    'B10' <<
        match.block('Join', [
          'v15' << match.Phi('v19', 'c_null'),
          'v8' << match.LoadClassId('v15'),
          match.Branch(
            match.TestRange('v8', kind: 'is'),
            ifTrue: 'B3',
            ifFalse: 'B4',
          ),
        ]),
    'B3' << match.block('Target', [match.StaticCall('v15'), match.Goto('B5')]),
    'B4' << match.block('Target', [match.Goto('B5')]),
    'B5' << match.block('Join', [match.DartReturn('c_null')]),
  ]);
}
