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

Generic mixin is operator throws Unbound type parameter found in Function ':Eval' #55574

Open
niclasEX opened this issue Apr 26, 2024 · 3 comments
Labels
area-front-end Use area-front-end for front end / CFE / kernel format related issues. cfe-expression-compilation Issues related to expression compilation in the CFE

Comments

@niclasEX
Copy link

This tracker is for issues related to:

  • Dart core libraries (dart:async, dart:io, etc.) ?

Dart info output:

General info

  • Dart 3.3.4 (stable) (Tue Apr 16 19:56:12 2024 +0000) on "macos_arm64"
  • on macos / Version 14.4.1 (Build 23E224)
  • locale is de-DE

Project info

  • sdk constraint: '^3.3.4'
  • dependencies:
  • dev_dependencies:

Process info

Memory CPU Elapsed time Command line
11 MB 0.0% 05:12:22 dart --enable-vm-service=0 --pause_isolates_on_start --disable-dart-dev -DSILENT_VM_SERVICE=true --write-service-info=file:/vm.json --pause_isolates_on_exit --enable-asserts /cancelable.dart
187 MB 0.0% 00:08 dart --enable-vm-service=0 --pause_isolates_on_start --disable-dart-dev -DSILENT_VM_SERVICE=true --write-service-info=file:/vm.json --pause_isolates_on_exit --enable-asserts /eval_bug.dart
10 MB 0.0% 05:12:23 dart debug_adapter
96 MB 0.0% 00:08 dart debug_adapter
10 MB 0.0% 22:22:37 dart devtools --machine --allow-embedding
10 MB 0.0% 56:51 dart devtools --machine --allow-embedding
11 MB 0.0% 05:39:41 dart devtools --machine --allow-embedding --port 9101
17 MB 0.0% 22:22:37 dart language-server --protocol=lsp --client-id=VS-Code --client-version=3.86.0
15 MB 0.0% 22:22:37 dart language-server --protocol=lsp --client-id=VS-Code --client-version=3.86.0
31 MB 0.0% 05:39:41 dart language-server --protocol=lsp --client-id=VS-Code --client-version=3.86.0
24 MB 0.0% 05:25:31 dart language-server --protocol=lsp --client-id=VS-Code --client-version=3.86.0
74 MB 0.0% 56:51 dart language-server --protocol=lsp --client-id=VS-Code --client-version=3.86.0
387 MB 0.0% 01:03 dart language-server --protocol=lsp --client-id=VS-Code --client-version=3.86.0
31 MB 0.4% 22:22:37 flutter_tools.snapshot daemon
39 MB 0.2% 05:39:41 flutter_tools.snapshot daemon
44 MB 0.2% 56:51 flutter_tools.snapshot daemon
18 MB 0.0% 22:22:33 flutter_tools.snapshot debug_adapter
23 MB 0.0% 37:23 flutter_tools.snapshot debug_adapter
28 MB 0.0% 06:27 flutter_tools.snapshot debug_adapter --test
22 MB 0.0% 22:22:33 flutter_tools.snapshot run --machine --start-paused -d macos --devtools-server-address http:/ --target /main.dart
27 MB 0.0% 37:22 flutter_tools.snapshot run --machine --start-paused -d macos --devtools-server-address http:/ --target /main.dart
38 MB 0.0% 06:27 flutter_tools.snapshot test --machine --start-paused --timeout 1d /asd_test.dart
8 MB 0.0% 22:22:31 frontend_server.dart.snapshot --sdk-root / --incremental --target=flutter --experimental-emit-debug-metadata -DFLUTTER_WEB_AUTO_DETECT=true -DFLUTTER_WEB_CANVASKIT_URL=https:/ --output-dill /app.dill --packages /package_config.json -Ddart.vm.profile=false -Ddart.vm.product=false --enable-asserts --track-widget-creation --filesystem-scheme org-dartlang-root --initialize-from-dill build/9ffe3fb9e2063e195ddfbcea0cabcfef.cache.dill.track.dill --source file:/dart_plugin_registrant.dart --source package:flutter/dart_plugin_registrant.dart -Dflutter.dart_plugin_registrant=file:/dart_plugin_registrant.dart --verbosity=error --enable-experiment=alternative-invalidation-strategy
31 MB 0.0% 37:21 frontend_server.dart.snapshot --sdk-root / --incremental --target=flutter --experimental-emit-debug-metadata -DFLUTTER_WEB_AUTO_DETECT=true -DFLUTTER_WEB_CANVASKIT_URL=https:/ --output-dill /app.dill --packages /package_config.json -Ddart.vm.profile=false -Ddart.vm.product=false --enable-asserts --track-widget-creation --filesystem-scheme org-dartlang-root --initialize-from-dill build/9ffe3fb9e2063e195ddfbcea0cabcfef.cache.dill.track.dill --source file:/dart_plugin_registrant.dart --source package:flutter/dart_plugin_registrant.dart -Dflutter.dart_plugin_registrant=file:/dart_plugin_registrant.dart --verbosity=error --enable-experiment=alternative-invalidation-strategy
void main() {
 final a = A();
 final b = B();

 print(a == b); // returns true
}

abstract class Parent {}

class A extends Parent with ValueEquality<Parent> {
 @override
 Object get value => 0;
}

class B extends Parent with ValueEquality<Parent> {
 @override
 Object get value => 0;
}

mixin ValueEquality<T> {
 Object? get value;

 @override
 bool operator ==(Object other) {
   return other is T; // breakpoint here, other is T returns
   // error: Unbound type parameter found in Function ':Eval':..
   // Please report this at dartbug.com.
 }

 @override
 int get hashCode => runtimeType.hashCode ^ value.hashCode;
}

As stated in the dart info output, this is on macOS running in vscode.
The dart code runs without any exceptions and returns true as expected.

I stumbled upon this in my flutter project where I tried to create a Map<Parent, dynamic> and inserting objects threw the aforementioned exception

@mraleph mraleph added the area-front-end Use area-front-end for front end / CFE / kernel format related issues. label Apr 29, 2024
@mraleph
Copy link
Member

mraleph commented Apr 29, 2024

/cc @jensjoha seems like a problem with expression evaluation

@johnniwinther johnniwinther added the cfe-expression-compilation Issues related to expression compilation in the CFE label Apr 29, 2024
@jensjoha
Copy link
Contributor

Thanks for the reproducible bug report!

Technical details:

The request sent via kernel service from the VM is this:

[...]
request[10] = file:///path/to/file.dart
request[11] = ValueEquality
request[12] = ==
request[13] = false
request[14] = 395
request[15] = file:///path/to/file.dart
[...]

i.e. it' saying that we're in the library (the file) [10], in class ValueEquality [11], in method == [12] which is not static [13] at position 395 [14] in script (still the file) [15] --- so when doing the expression calculation we're told we're in ValueEquality.== --- which compiled looks like this:

abstract class ValueEquality<T extends core::Object? = dynamic> extends core::Object /*isMixinDeclaration*/  {
  [...]
  operator ==(core::Object other) → core::bool {
    return other is self::ValueEquality::T%;
  }
  [...]
}

It has a T so we're happily compiling the expresion T to return [0] [0] #lib1::ValueEquality::T%;

Though, as we can see from the "frame name" (in Observatory) we're actually in _A&Parent&ValueEquality.== with the class looking like this:

abstract class _A&Parent&ValueEquality extends self::Parent implements self::ValueEquality<self::Parent> /*isAnonymousMixin,isEliminatedMixin*/  {
  [...]
  operator ==(core::Object other) → core::bool {
    return other is self::Parent;
  }
  [...]
}

so no T actually exists.
And so the VM returns the Unbound type parameter found in %s. Please report this at dartbug.com. error.

It seems the VM made the change to send the name of the mixin class in https://dart-review.googlesource.com/c/sdk/+/279238 (response to #51027) --- though giving the error The getter 'T' isn't defined for the class '_A&Parent&ValueEquality'. (the case without that change) probably wouldn't be more helpful.

@mraleph could a solution on the VM side be something like this:

diff --git a/runtime/vm/compiler/frontend/kernel_translation_helper.cc b/runtime/vm/compiler/frontend/kernel_translation_helper.cc
index c841c209eb5..5a0967322d7 100644
--- a/runtime/vm/compiler/frontend/kernel_translation_helper.cc
+++ b/runtime/vm/compiler/frontend/kernel_translation_helper.cc
@@ -3583,12 +3583,40 @@ void TypeTranslator::BuildTypeParameterType() {
 
   // If the type is from a constant, the parameter index isn't offset by the
   // enclosing context.
+  bool special_mixin_handling_for_expression_evaluation = false;
   if (!in_constant_context_) {
-    const intptr_t class_type_parameter_count =
-        active_class_->klass->NumTypeParameters();
+    auto klass = active_class_->klass;
+    if (klass->is_transformed_mixin_application() &&
+        active_class_->member != nullptr) {
+      const String& name = String::Handle(Z, active_class_->member->name());
+      if (name.Equals(Symbols::DebugProcedureName())) {
+        // In vm/service.cc we send over the "klass->Mixin()" class to the CFE.
+        // It's therefore compiled as is we're in the mixin, but we're not. If
+        // there's type arguments in play we have to handle that special.
+        // This is the first part of what "klass->Mixin()" does,
+        // only we extract the actual type arguments.
+        const Array& interfaces = Array::Handle(klass->interfaces());
+        const Type& mixin_type =
+            Type::Handle(Type::RawCast(interfaces.At(interfaces.Length() - 1)));
+        const TypeArguments& type_args =
+            TypeArguments::Handle(Z, mixin_type.arguments());
+        const intptr_t type_args_count = type_args.Length();
+        if (type_args_count > parameter_index) {
+          result_ = type_args.TypeAt(parameter_index);
+          return;
+        }
+        // Not pointing to the type arguments of the class,
+        // but pick the class with type parameters to shift the parameter_index
+        // correctly below.
+        Class& method_cls = Class::Handle(Z, klass->Mixin());
+        klass = &method_cls;
+        special_mixin_handling_for_expression_evaluation = true;
+      }
+    }
+    const intptr_t class_type_parameter_count = klass->NumTypeParameters();
     if (class_type_parameter_count > parameter_index) {
-      result_ =
-          active_class_->klass->TypeParameterAt(parameter_index, nullability);
+      ASSERT(!special_mixin_handling_for_expression_evaluation);
+      result_ = klass->TypeParameterAt(parameter_index, nullability);
       return;
     }
     parameter_index -= class_type_parameter_count;
@@ -3613,8 +3641,7 @@ void TypeTranslator::BuildTypeParameterType() {
         //   }
         //
         if (class_type_parameter_count > parameter_index) {
-          result_ = active_class_->klass->TypeParameterAt(parameter_index,
-                                                          nullability);
+          result_ = klass->TypeParameterAt(parameter_index, nullability);
           return;
         }
         parameter_index -= class_type_parameter_count;

?

@mraleph
Copy link
Member

mraleph commented Apr 30, 2024

/cc @rmacnak-google @bkonyi

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-expression-compilation Issues related to expression compilation in the CFE
Projects
None yet
Development

No branches or pull requests

4 participants