Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Unhelpful Error when using Extensions in Switch Statements #58963

Open
PvtPuddles opened this issue Nov 19, 2024 · 1 comment
Open

Unhelpful Error when using Extensions in Switch Statements #58963

PvtPuddles opened this issue Nov 19, 2024 · 1 comment
Assignees
Labels
area-front-end Use area-front-end for front end / CFE / kernel format related issues. cfe-dysfunctionalities Issues for the CFE not behaving as intended type-bug Incorrect behavior (everything from a crash to more subtle misbehavior)

Comments

@PvtPuddles
Copy link

Hello, I've run into an issue on MacOS using the command line. I recently made some changes to my code around the same time as I upgraded flutter, so I spent several hours trying to debug this issue as a flutter issue before realizing that my change was actually the culprit and that this issue has nothing to do with Flutter.

I've managed to trace my problem; I refactored a switch expression into a switch statement to streamline some logic. Unfortunately, one of the conditions wasn't constant, but neither I nor the compiler managed to catch this error. See code below.

I have an extension that provides a >= operator, which I was using in the switch expression. This was working before. However, if I try to use that same extension in the switch statement, neither the analyzer nor compiler issue a warning or error. Instead, when I try to run the test, I get a pretty big, unhelpful error.

Instead, I would expect:

  1. An error is surfaced by the analyzer on my non-constant switch case
  2. In the error case, a more helpful error message to help me find the problem
  3. That both the switch statement and the switch expression would function (or not function in) the same way. If one throws, I would expect the other to as well.

Test Code

import 'package:test/test.dart';

void main() {
  test("Switch Expression", () {
    final result = switch (MyEnum.first) {
      >= MyEnum.second => "More than second",
      MyEnum.first => "One value",
      _ => "Some values",
    };
    expect(result, "One value");
  });

  // Causes an obtuse `InvalidArgument(s): Type parameter StructuralParameter(T) is not indexed`
  // error.  Comment out this test and the file will compile again.
  test("Switch Statement", () {
    String result;
    switch (MyEnum.first) {
      case >= MyEnum.second:
        result = "More than second";
      case MyEnum.first:
        result = "One value";
      default:
        result = "Some values";
    }
    expect(result, "One value");
  });
}

enum MyEnum implements Comparable<MyEnum> {
  first(1),
  second(2),
  third(3);

  const MyEnum(this.value);

  final int value;

  @override
  int compareTo(MyEnum other) => value.compareTo(other.value);

  // FIXME: Operator >= not implemented
  // operator >=(MyEnum other) => value.compareTo(other.value) >= 0;
}

extension GreaterThanOrEqual<T> on Comparable<T> {
  operator >=(T other) => compareTo(other) >= 0;
}

Compile-time Error

dart test test/test.dart
Building package executable... 
Built test:test.
00:00 +0: loading test/test.dart                                                                                                                                                                                                        Unhandled exception:
Invalid argument(s): Type parameter StructuralParameter(T) is not indexed
#0      TypeParameterIndexer.[] (package:kernel/binary/ast_to_binary.dart:3420)
#1      BinaryPrinter.visitStructuralParameterType (package:kernel/binary/ast_to_binary.dart:2576)
#2      StructuralParameterType.accept (package:kernel/ast.dart:12765)
#3      BinaryPrinter.writeNode (package:kernel/binary/ast_to_binary.dart:441)
#4      BinaryPrinter.visitAsExpression (package:kernel/binary/ast_to_binary.dart:1987)
#5      AsExpression.accept (package:kernel/ast.dart:7607)
#6      BinaryPrinter.writeNode (package:kernel/binary/ast_to_binary.dart:441)
#7      BinaryPrinter.writeNodeList (package:kernel/binary/ast_to_binary.dart:351)
#8      BinaryPrinter.visitArguments (package:kernel/binary/ast_to_binary.dart:1866)
#9      Arguments.accept (package:kernel/ast.dart:5394)
#10     BinaryPrinter.writeArgumentsNode (package:kernel/binary/ast_to_binary.dart:455)
#11     BinaryPrinter.visitStaticInvocation (package:kernel/binary/ast_to_binary.dart:1849)
#12     StaticInvocation.accept (package:kernel/ast.dart:6582)
#13     BinaryPrinter.writeNode (package:kernel/binary/ast_to_binary.dart:441)
#14     BinaryPrinter.visitAsExpression (package:kernel/binary/ast_to_binary.dart:1986)
#15     AsExpression.accept (package:kernel/ast.dart:7607)
#16     BinaryPrinter.writeNode (package:kernel/binary/ast_to_binary.dart:441)
#17     BinaryPrinter.visitIfStatement (package:kernel/binary/ast_to_binary.dart:2351)
#18     IfStatement.accept (package:kernel/ast.dart:10077)
#19     BinaryPrinter.writeNode (package:kernel/binary/ast_to_binary.dart:441)
#20     BinaryPrinter.writeNodeList (package:kernel/binary/ast_to_binary.dart:351)
#21     BinaryPrinter.visitBlock (package:kernel/binary/ast_to_binary.dart:2207)
#22     Block.accept (package:kernel/ast.dart:9181)
#23     BinaryPrinter.writeNode (package:kernel/binary/ast_to_binary.dart:441)
#24     BinaryPrinter.writeNodeList (package:kernel/binary/ast_to_binary.dart:351)
#25     BinaryPrinter.visitBlock (package:kernel/binary/ast_to_binary.dart:2207)
#26     Block.accept (package:kernel/ast.dart:9181)
#27     BinaryPrinter.writeNode (package:kernel/binary/ast_to_binary.dart:441)
#28     BinaryPrinter.visitLabeledStatement (package:kernel/binary/ast_to_binary.dart:2241)
#29     LabeledStatement.accept (package:kernel/ast.dart:9384)
#30     BinaryPrinter.writeNode (package:kernel/binary/ast_to_binary.dart:441)
#31     BinaryPrinter.writeNodeList (package:kernel/binary/ast_to_binary.dart:351)
#32     BinaryPrinter.visitBlockExpression (package:kernel/binary/ast_to_binary.dart:2158)
#33     BlockExpression.accept (package:kernel/ast.dart:8765)
#34     BinaryPrinter.writeNode (package:kernel/binary/ast_to_binary.dart:441)
#35     BinaryPrinter.writeOptionalNode (package:kernel/binary/ast_to_binary.dart:533)
#36     BinaryPrinter.writeVariableDeclaration (package:kernel/binary/ast_to_binary.dart:2424)
#37     BinaryPrinter.visitVariableDeclaration (package:kernel/binary/ast_to_binary.dart:2410)
#38     VariableDeclaration.accept (package:kernel/ast.dart:10739)
#39     BinaryPrinter.writeNode (package:kernel/binary/ast_to_binary.dart:441)
#40     BinaryPrinter.writeNodeList (package:kernel/binary/ast_to_binary.dart:351)
#41     BinaryPrinter.visitBlock (package:kernel/binary/ast_to_binary.dart:2207)
#42     Block.accept (package:kernel/ast.dart:9181)
#43     BinaryPrinter.writeNode (package:kernel/binary/ast_to_binary.dart:441)
#44     BinaryPrinter.writeOptionalNode (package:kernel/binary/ast_to_binary.dart:533)
#45     BinaryPrinter.visitFunctionNode (package:kernel/binary/ast_to_binary.dart:1552)
#46     FunctionNode.accept (package:kernel/ast.dart:3872)
#47     BinaryPrinter.writeFunctionNode (package:kernel/binary/ast_to_binary.dart:448)
#48     BinaryPrinter.visitFunctionExpression (package:kernel/binary/ast_to_binary.dart:2136)
#49     FunctionExpression.accept (package:kernel/ast.dart:8588)
#50     BinaryPrinter.writeNode (package:kernel/binary/ast_to_binary.dart:441)
#51     BinaryPrinter.writeNodeList (package:kernel/binary/ast_to_binary.dart:351)
#52     BinaryPrinter.visitArguments (package:kernel/binary/ast_to_binary.dart:1866)
#53     Arguments.accept (package:kernel/ast.dart:5394)
#54     BinaryPrinter.writeArgumentsNode (package:kernel/binary/ast_to_binary.dart:455)
#55     BinaryPrinter.visitStaticInvocation (package:kernel/binary/ast_to_binary.dart:1849)
#56     StaticInvocation.accept (package:kernel/ast.dart:6582)
#57     BinaryPrinter.writeNode (package:kernel/binary/ast_to_binary.dart:441)
#58     BinaryPrinter.visitExpressionStatement (package:kernel/binary/ast_to_binary.dart:2196)
#59     ExpressionStatement.accept (package:kernel/ast.dart:9128)
#60     BinaryPrinter.writeNode (package:kernel/binary/ast_to_binary.dart:441)
#61     BinaryPrinter.writeNodeList (package:kernel/binary/ast_to_binary.dart:351)
#62     BinaryPrinter.visitBlock (package:kernel/binary/ast_to_binary.dart:2207)
#63     Block.accept (package:kernel/ast.dart:9181)
#64     BinaryPrinter.writeNode (package:kernel/binary/ast_to_binary.dart:441)
#65     BinaryPrinter.writeOptionalNode (package:kernel/binary/ast_to_binary.dart:533)
#66     BinaryPrinter.visitFunctionNode (package:kernel/binary/ast_to_binary.dart:1552)
#67     FunctionNode.accept (package:kernel/ast.dart:3872)
#68     BinaryPrinter.writeFunctionNode (package:kernel/binary/ast_to_binary.dart:448)
#69     BinaryPrinter.visitProcedure (package:kernel/binary/ast_to_binary.dart:1379)
#70     Procedure.accept (package:kernel/ast.dart:3228)
#71     BinaryPrinter.writeProcedureNode (package:kernel/binary/ast_to_binary.dart:469)
#72     BinaryPrinter.writeProcedureNodeList (package:kernel/binary/ast_to_binary.dart:360)
#73     BinaryPrinter.visitLibrary (package:kernel/binary/ast_to_binary.dart:1124)
#74     Library.accept (package:kernel/ast.dart:585)
#75     BinaryPrinter.writeLibraryNode (package:kernel/binary/ast_to_binary.dart:462)
#76     BinaryPrinter.writeLibraries (package:kernel/binary/ast_to_binary.dart:797)
#77     BinaryPrinter.writeComponentFile.<anonymous closure> (package:kernel/binary/ast_to_binary.dart:607)
#78     Timeline.timeSync (dart:developer/timeline.dart:173)
#79     BinaryPrinter.writeComponentFile (package:kernel/binary/ast_to_binary.dart:588)
#80     FrontendCompiler.writeDillFile (package:frontend_server/frontend_server.dart:916)
#81     FrontendCompiler.compile (package:frontend_server/frontend_server.dart:689)
<asynchronous suspension>
#82     listenAndCompile.<anonymous closure> (package:frontend_server/frontend_server.dart:1385)
<asynchronous suspension>
00:00 +0 -1: loading test/test.dart [E]                                                                                                                                                                                                 
  Failed to load "test/test.dart": 
  SocketException: Write failed (OS Error: Broken pipe, errno = 32), port = 0
  dart:io-patch/socket_patch.dart 1252:34                                _NativeSocket.write
  dart:io-patch/socket_patch.dart 2010:15                                _RawSocket.write
  dart:io-patch/socket_patch.dart 2487:18                                _Socket._write
  dart:io-patch/socket_patch.dart 2222:28                                _SocketStreamConsumer.write
  dart:io-patch/socket_patch.dart 2174:11                                _SocketStreamConsumer.addStream.<fn>
  dart:async/zone.dart 1407:47                                           _rootRunUnary
  dart:async/zone.dart 1308:19                                           _CustomZone.runUnary
  dart:async/zone.dart 1217:7                                            _CustomZone.runUnaryGuarded
  dart:async/stream_impl.dart 365:11                                     _BufferingStreamSubscription._sendData
  dart:async/stream_impl.dart 297:7                                      _BufferingStreamSubscription._add
  dart:async/stream_controller.dart 784:19                               _SyncStreamControllerDispatch._sendData
  dart:async/stream_controller.dart 658:7                                _StreamController._add
  dart:async/stream_controller.dart 606:5                                _StreamController.add
  dart:io/io_sink.dart 154:17                                            _StreamSinkImpl.add
  dart:io/io_sink.dart 287:5                                             _IOSinkImpl.write
  dart:io-patch/socket_patch.dart 2320:36                                _Socket.write
  dart:io/stdio.dart 401:13                                              _StdSink._write
  dart:io/stdio.dart 419:5                                               _StdSink.writeln
  package:frontend_server_client/src/frontend_server_client.dart 366:21  FrontendServerClient._sendCommand
  package:frontend_server_client/src/frontend_server_client.dart 283:5   FrontendServerClient.accept
  package:test_core/src/runner/vm/test_compiler.dart 128:30              _TestCompilerForLanguageVersion._compile
  ===== asynchronous gap ===========================
  package:pool/pool.dart 127:14                                          Pool.withResource
  ===== asynchronous gap ===========================
  package:test_core/src/runner/vm/platform.dart 239:9                    VMPlatform._compileToKernel
  ===== asynchronous gap ===========================
  package:test_core/src/runner/vm/platform.dart 220:13                   VMPlatform._spawnIsolate
  ===== asynchronous gap ===========================
  package:test_core/src/runner/vm/platform.dart 75:19                    VMPlatform.load
  ===== asynchronous gap ===========================
  package:test_core/src/runner/loader.dart 219:27                        Loader.loadFile.<fn>
  ===== asynchronous gap ===========================
  package:test_core/src/runner/load_suite.dart 97:19                     new LoadSuite.<fn>.<fn>

Dart Info

(Sorry, built the project with Flutter)

#### General info

- Dart 3.5.4 (stable) (Wed Oct 16 16:18:51 2024 +0000) on "macos_arm64"
- on macos / Version 15.1 (Build 24B83)
- locale is en-US

#### Project info

- sdk constraint: '^3.5.4'
- dependencies: flutter, test
- dev_dependencies: flutter_lints, flutter_test

#### Process info

|  Memory |  CPU | Elapsed time | Command line                                                                               |
| ------: | ---: | -----------: | ------------------------------------------------------------------------------------------ |
|   12 MB | 0.0% |     01:59:25 | dart devtools --machine --dtd-uri=ws:<path>/I6TfSYmG1wvHrPy5                               |
|    8 MB | 0.0% |  11-04:34:54 | dart devtools --machine --dtd-uri=ws:<path>/SCrKN6CEHrmYXX48                               |
|   15 MB | 0.0% |     23:30:54 | dart devtools --no-launch-browser                                                          |
| 3068 MB | 0.0% |     01:59:25 | dart language-server --client-id=IntelliJ-IDEA --client-version=IU-243.21565.193 --protocol=analyzer |
|   12 MB | 0.0% |     01:59:25 | dart tooling-daemon --machine                                                              |
|    9 MB | 0.0% |  11-04:34:54 | dart tooling-daemon --machine                                                              |
|   56 MB | 0.6% |     01:40:34 | flutter_tools.snapshot daemon                                                              |
@dart-github-bot
Copy link
Collaborator

Summary: Using extensions in Dart switch statements with non-constant conditions causes unhelpful compiler errors. The analyzer should flag the issue and provide a more informative error message.

@dart-github-bot dart-github-bot added area-front-end Use area-front-end for front end / CFE / kernel format related issues. triage-automation See https://github.com/dart-lang/ecosystem/tree/main/pkgs/sdk_triage_bot. type-bug Incorrect behavior (everything from a crash to more subtle misbehavior) labels Nov 20, 2024
@johnniwinther johnniwinther added the cfe-dysfunctionalities Issues for the CFE not behaving as intended label Nov 20, 2024
@lrhn lrhn removed the triage-automation See https://github.com/dart-lang/ecosystem/tree/main/pkgs/sdk_triage_bot. label Nov 20, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area-front-end Use area-front-end for front end / CFE / kernel format related issues. cfe-dysfunctionalities Issues for the CFE not behaving as intended type-bug Incorrect behavior (everything from a crash to more subtle misbehavior)
Projects
None yet
Development

No branches or pull requests

5 participants