Sdk: Is this a bug on the language?

Created on 18 Feb 2020  Â·  6Comments  Â·  Source: dart-lang/sdk

I have a app in production using the latest stable version of Flutter:
`` Flutter (Channel stable, v1.12.13+hotfix.8, on Microsoft Windows [versão 10.0.18362.657], locale pt-BR)


This is definetly killing me for hours and I have no idea what is going on.  But iin short terms:

- I am passing a variable as parameter to a function and inside that function, it has a total different value (and type!)

This is the code :

I pass a variable `localId` to the function `finishOrCancelRecover`. In the moment I call that function, my variable is an integer of value `13983`. But inside the function it becames an `double` of value `-19.9848765`. WTF?

This is the declaration of the `localId` variable (as you can see, it is immutable!):
![image](https://user-images.githubusercontent.com/1146982/74691543-f047d980-51c1-11ea-8260-f2b4da1743e7.png)

```dart
return lastKnownLocation
                            .timeout(5.seconds)
                            .catchError((it) => null)
                            .then((location) async =>
                                DenoxRequests.finishOrCancelRecover(
                                    localId, //THIS VARIABLE HERE HAS A VALUE OF 13983
                                    false,
                                    location != null
                                        ? UserLocation(
                                            latitude: location.latitude,
                                            longitude: location.longitude,
                                            deviceId: await deviceId,
                                          )
                                        : null))
  • lastKnownLocation is a call for:
Future<Position> get lastKnownLocation async =>
    Geolocator().getLastKnownPosition(desiredAccuracy: LocationAccuracy.high);

I've set the debugger and I got this to proove that there is something wrong:

image

image

The result of printing the localId.runtimeType confirms that this is a double now:
image

As you can see on the photo above, the int variable localId has a double value and the value of 19.9848765 is exactly the latitude returned by the Geolocator package. How can it be possible?

Is this a bug on the language or I am crazy here?

area-vm type-bug

Most helpful comment

Thanks for the reproduction @shinayser! Things go wrong at the async transformation level - we don't correctly allocate async temporaries.

Pure Dart reduction:

foo(int x) async {
  return bar(x - 1, x != null ? [x + 1, x + 2, await null] : null);
}

bar(int a, List b) {
  if (a != (b[0] - 2)) {
    throw 'failure: a=$a, b=$b';
  }
}

void main() async {
  await foo(0);
}
$  out/ReleaseX64/dart test.dart
Unhandled exception:
failure: a=1, b=[1, 2, null]
#0      bar (file:///usr/local/google/home/vegorov/src/dart/sdk/test.dart:7:5)
#1      foo (file:///usr/local/google/home/vegorov/src/dart/sdk/test.dart:2:10)
<asynchronous suspension>
#2      main (file:///usr/local/google/home/vegorov/src/dart/sdk/test.dart:12:9)
#3      _startIsolate.<anonymous closure> (dart:isolate-patch/isolate_patch.dart:301:19)
#4      _RawReceivePortImpl._handleMessage (dart:isolate-patch/isolate_patch.dart:168:12)

Looking at Kernel reveals that we did not properly assign async temporary.

 function :async_op([dynamic :result, dynamic :exception, dynamic :stack_trace]) → dynamic yielding
       try {
         #L1:
         {
           :async_temporary_1 = x.{core::num::-}(1);  // Should be :async_temporary_2 I think.
           if(!x.{core::num::==}(null)) {
             :async_temporary_1 = x.{core::num::+}(1);
             :async_temporary_0 = x.{core::num::+}(2);
             [yield] let dynamic #t1 = asy::_awaitHelper(null, :async_op_then, :async_op_error, :async_op) in null;
             :async_temporary_0 = <dynamic>[:async_temporary_1, :async_temporary_0, _in::unsafeCast<core::Null?>(:result)];
           }
           else {
             :async_temporary_0 = null;
           }
           :return_value = test::bar(:async_temporary_1, :async_temporary_0);
           break #L1;
         }
         asy::_completeOnAsyncReturn(:async_completer, :return_value);
         return;
       }
       on dynamic catch(dynamic :exception, dynamic :stack_trace) {
         :async_completer.{asy::Completer::completeError}(:exception, :stack_trace);
       }

@cskau-g Clement could you take a look at this issue? It's in the async transformation.

All 6 comments

I PARTIALLY fixed the code by moving the inner await deviceId to above the request call. It fixes the value but the type changes to double:

Before:
image

Actual:
image

Perharps the inner pointers are getting crazy about those nested await calls?

Does this happen in Flutter release/profile mode only or does it consistently happen in Flutter debug mode?

Based on the answer we would need to ask you to provide a bit more information.

@shinayser It would seem to be a bug in our compiler. Could you provide us with a small reproduction example that triggers this? That would be very helpful.

WOW! After some hours trying to reproduce the project on pure Dart CLI app, I gave up and created a flutter project to reproduce it.

I've added as much as code I could to reproduce my real world production app, so you folks will find a lot of boilerplate code.

https://github.com/shinayser/dart_compiler_bug

Thanks for the reproduction @shinayser! Things go wrong at the async transformation level - we don't correctly allocate async temporaries.

Pure Dart reduction:

foo(int x) async {
  return bar(x - 1, x != null ? [x + 1, x + 2, await null] : null);
}

bar(int a, List b) {
  if (a != (b[0] - 2)) {
    throw 'failure: a=$a, b=$b';
  }
}

void main() async {
  await foo(0);
}
$  out/ReleaseX64/dart test.dart
Unhandled exception:
failure: a=1, b=[1, 2, null]
#0      bar (file:///usr/local/google/home/vegorov/src/dart/sdk/test.dart:7:5)
#1      foo (file:///usr/local/google/home/vegorov/src/dart/sdk/test.dart:2:10)
<asynchronous suspension>
#2      main (file:///usr/local/google/home/vegorov/src/dart/sdk/test.dart:12:9)
#3      _startIsolate.<anonymous closure> (dart:isolate-patch/isolate_patch.dart:301:19)
#4      _RawReceivePortImpl._handleMessage (dart:isolate-patch/isolate_patch.dart:168:12)

Looking at Kernel reveals that we did not properly assign async temporary.

 function :async_op([dynamic :result, dynamic :exception, dynamic :stack_trace]) → dynamic yielding
       try {
         #L1:
         {
           :async_temporary_1 = x.{core::num::-}(1);  // Should be :async_temporary_2 I think.
           if(!x.{core::num::==}(null)) {
             :async_temporary_1 = x.{core::num::+}(1);
             :async_temporary_0 = x.{core::num::+}(2);
             [yield] let dynamic #t1 = asy::_awaitHelper(null, :async_op_then, :async_op_error, :async_op) in null;
             :async_temporary_0 = <dynamic>[:async_temporary_1, :async_temporary_0, _in::unsafeCast<core::Null?>(:result)];
           }
           else {
             :async_temporary_0 = null;
           }
           :return_value = test::bar(:async_temporary_1, :async_temporary_0);
           break #L1;
         }
         asy::_completeOnAsyncReturn(:async_completer, :return_value);
         return;
       }
       on dynamic catch(dynamic :exception, dynamic :stack_trace) {
         :async_completer.{asy::Completer::completeError}(:exception, :stack_trace);
       }

@cskau-g Clement could you take a look at this issue? It's in the async transformation.

Alright, this issue should have been fixed at HEAD now.
Note however that it will still take a little bit for the fix to make it into a release.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

DartBot picture DartBot  Â·  3Comments

gspencergoog picture gspencergoog  Â·  3Comments

nex3 picture nex3  Â·  3Comments

DartBot picture DartBot  Â·  3Comments

matanlurey picture matanlurey  Â·  3Comments