| // Copyright (c) 2026, 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. | |
| // Since dynamic downcasts can fail, their evaluation order is observable. This | |
| // test verifies that in an invocation with multiple arguments, each argument is | |
| // evaluated and then (if necessary) downcast prior to evaluating the next | |
| // argument. | |
| // The test pays special attention to the situation where a named argument | |
| // precedes a positional argument, since the front end is known to re-order such | |
| // arguments when converting to the kernel representation. | |
| // This test only makes sense to run on platforms that check implicit downcasts. | |
| // Requirements=checked-implicit-downcasts | |
| import 'package:expect/expect.dart'; | |
| final List<String> log = []; | |
| void f(int x, int y) { | |
| log.add('f($x, $y) called'); | |
| } | |
| void g(int x, {required int y}) { | |
| log.add('g($x, y: $y) called'); | |
| } | |
| void h({required int x, required int y}) { | |
| log.add('h(x: $x, y: $y) called'); | |
| } | |
| T compute<T>(T t) { | |
| log.add('computed $t'); | |
| return t; | |
| } | |
| void testTwoPositionalArgs() { | |
| log.clear(); | |
| Expect.throws<TypeError>(() => f(compute<dynamic>('bad'), compute<int>(1))); | |
| Expect.listEquals(['computed bad'], log); | |
| log.clear(); | |
| Expect.throws<TypeError>(() => f(compute<int>(0), compute<dynamic>('bad'))); | |
| Expect.listEquals(['computed 0', 'computed bad'], log); | |
| log.clear(); | |
| f(compute<int>(0), compute<int>(1)); | |
| Expect.listEquals(['computed 0', 'computed 1', 'f(0, 1) called'], log); | |
| } | |
| void testPositionalThenNamedArg() { | |
| log.clear(); | |
| Expect.throws<TypeError>( | |
| () => g(compute<dynamic>('bad'), y: compute<int>(1)), | |
| ); | |
| Expect.listEquals(['computed bad'], log); | |
| log.clear(); | |
| Expect.throws<TypeError>( | |
| () => g(compute<int>(0), y: compute<dynamic>('bad')), | |
| ); | |
| Expect.listEquals(['computed 0', 'computed bad'], log); | |
| log.clear(); | |
| g(compute<int>(0), y: compute<int>(1)); | |
| Expect.listEquals(['computed 0', 'computed 1', 'g(0, y: 1) called'], log); | |
| } | |
| void testNamedThenPositionalArg() { | |
| log.clear(); | |
| Expect.throws<TypeError>( | |
| () => g(y: compute<dynamic>('bad'), compute<int>(1)), | |
| ); | |
| Expect.listEquals(['computed bad'], log); | |
| log.clear(); | |
| Expect.throws<TypeError>( | |
| () => g(y: compute<int>(0), compute<dynamic>('bad')), | |
| ); | |
| Expect.listEquals(['computed 0', 'computed bad'], log); | |
| log.clear(); | |
| g(y: compute<int>(0), compute<int>(1)); | |
| Expect.listEquals(['computed 0', 'computed 1', 'g(1, y: 0) called'], log); | |
| } | |
| void testTwoNamedArgsInOrder() { | |
| log.clear(); | |
| Expect.throws<TypeError>( | |
| () => h(x: compute<dynamic>('bad'), y: compute<int>(1)), | |
| ); | |
| Expect.listEquals(['computed bad'], log); | |
| log.clear(); | |
| Expect.throws<TypeError>( | |
| () => h(x: compute<int>(0), y: compute<dynamic>('bad')), | |
| ); | |
| Expect.listEquals(['computed 0', 'computed bad'], log); | |
| log.clear(); | |
| h(x: compute<int>(0), y: compute<int>(1)); | |
| Expect.listEquals(['computed 0', 'computed 1', 'h(x: 0, y: 1) called'], log); | |
| } | |
| void testTwoNamedArgsOutOfOrder() { | |
| log.clear(); | |
| Expect.throws<TypeError>( | |
| () => h(y: compute<dynamic>('bad'), x: compute<int>(1)), | |
| ); | |
| Expect.listEquals(['computed bad'], log); | |
| log.clear(); | |
| Expect.throws<TypeError>( | |
| () => h(y: compute<int>(0), x: compute<dynamic>('bad')), | |
| ); | |
| Expect.listEquals(['computed 0', 'computed bad'], log); | |
| log.clear(); | |
| h(y: compute<int>(0), x: compute<int>(1)); | |
| Expect.listEquals(['computed 0', 'computed 1', 'h(x: 1, y: 0) called'], log); | |
| } | |
| void main() { | |
| testTwoPositionalArgs(); | |
| testPositionalThenNamedArg(); | |
| testNamedThenPositionalArg(); | |
| testTwoNamedArgsInOrder(); | |
| testTwoNamedArgsOutOfOrder(); | |
| } |