Coder Social home page Coder Social logo

Comments (5)

lrhn avatar lrhn commented on July 21, 2024

This is definitely a bug. That code should not compile.

The return type of _PerformerImpl.perform is (String id, Future<void> completion), inherited from the super interface.

The function itself is async, so it should be a compile time error to have a return type that Future is not assignable to.

from sdk.

jellynoone avatar jellynoone commented on July 21, 2024

What is even more interesting is this.

Consider changing main to:

void main(List<String> args) async {
  final Performer performer = _PerformerImpl();

  final result = performer.perform();
  final (id, future) = result;

  print('id: ${id}');
  print('id: ${id.runtimeType}');
  print('future: ${future.runtimeType}');
  print('result: ${result.runtimeType}');
  if (id.length < 10) {
    print(id);
  }
  await future;
}

it prints:

id: 8
id: int
future: _RootZone
result: Future<Object?>

Unhandled exception:
NoSuchMethodError: Class 'int' has no instance getter 'length'.
Receiver: 8
Tried calling: length
#0      Object.noSuchMethod (dart:core-patch/object_patch.dart:38:5)
#1      main
invalid_destruction.dart:11
#2      _delayEntrypointInvocation.<anonymous closure> (dart:isolate-patch/isolate_patch.dart:295:33)
#3      _RawReceivePort._handleMessage (dart:isolate-patch/isolate_patch.dart:184:12)

However, if I change:

final (id, future) = result;

to

final (id, future) = result as dynamic;

The error I get is:

Unhandled exception:
type 'Future<Object?>' is not a subtype of type '(dynamic, dynamic)'
#0      main
invalid_destruction.dart:5
#1      _delayEntrypointInvocation.<anonymous closure> (dart:isolate-patch/isolate_patch.dart:295:33)
#2      _RawReceivePort._handleMessage (dart:isolate-patch/isolate_patch.dart:184:12)

from sdk.

jellynoone avatar jellynoone commented on July 21, 2024

The following example also brought interesting results:

void main(List<String> args) async {
  final Performer performer = _PerformerImpl();

  final result = performer.perform();
  final ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11) = result;

  print($1);
  print($1.runtimeType);
  print($2.runtimeType);
  print($3.runtimeType);
  print($4.runtimeType);
  print($5.runtimeType);
  print($6.runtimeType);
  print($7.runtimeType);
  print($8.runtimeType);
  print($9.runtimeType);
  print($10.runtimeType);
  print($11.runtimeType);
  print(result.runtimeType);
  if ($1.length < 10) {
    print($1);
  }
  await $2;
}

abstract interface class Performer {
  (
    String,
    String,
    String,
    String,
    String,
    String,
    String,
    String,
    String,
    String,
    String,
  ) perform();
}

class _PerformerImpl implements Performer {
  perform() async {
    return ('id1', 'id2');
  }
}

Output:

8
int
_RootZone
(String, String)
Null
int
int
String
String
int
Null
int
Future<Object?>

Unhandled exception:
NoSuchMethodError: Class 'int' has no instance getter 'length'.
Receiver: 8
Tried calling: length

from sdk.

jellynoone avatar jellynoone commented on July 21, 2024

Interestingly, as well:

void main(List<String> args) async {
  final Performer performer = _PerformerImpl();

  final result = performer.perform();
  final (:v1, :v2) = result;

  print(v1.runtimeType);
  print(v2.runtimeType);
  print(result.runtimeType);
}

abstract interface class Performer {
  ({
    String v1,
    String v2,
  }) perform();
}

class _PerformerImpl implements Performer {
  perform() async {
    return (value: 'v1', value2: 'v2');
  }
}

Executes with the destructed record in main having values even though no such property is ever returned.

On the other hand, doing:

void main(List<String> args) async {
  final Performer performer = _PerformerImpl();

  final result = performer.perform();
  final Value(:value) = result;

  print(value.runtimeType);
  print(result.runtimeType);
}

abstract interface class Performer {
  Value<String> perform();
}

class _PerformerImpl implements Performer {
  perform() async {
    return const Value(value: 'id');
  }
}

class Value<T> {
  final T value;

  const Value({
    required this.value,
  });
}

Errors at destruction because the returned Future is not a Value.

from sdk.

lrhn avatar lrhn commented on July 21, 2024

It seems the compiler trusts the type, and uses unchecked "get first instance variable value" and "get second instance variable value" operations to get the (presumed) first and second positional field of the record.
It just happens to be a _Future object, not a record, so the first field is an integer state and the second is the zone the future belongs to.

A state of "8" means completed with a value. If there had been a third field, you would likely see the value there, which is indeed what the 11-field version shows. (Then it reads past the end of the object, which is very concerning.)
When using named fields, the result is the same, because optimized record code can statically translate names to instance variable positions.

On the web, the behavior is different (it tries reading $1 which doesn't exist), but the code is still allowed.

from sdk.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.