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

Type error when returning Future in DDC and staticInterop #48835

Closed
jodinathan opened this issue Apr 19, 2022 · 3 comments
Closed

Type error when returning Future in DDC and staticInterop #48835

jodinathan opened this issue Apr 19, 2022 · 3 comments
Assignees
Labels
area-web Use area-web for Dart web related issues, including the DDC and dart2js compilers and JS interop. web-js-interop Issues that impact all js interop

Comments

@jodinathan
Copy link

Dart SDK version: 2.16.2 (stable) (Tue Mar 22 13:15:13 2022 +0100) on "macos_x64"

In the example below, DDC throws an error in return ret; complaining that ret is not a Response, however, I just confirmed that it is with an assert, also all prints confirm that ret is Response.

Works as expected in Dart2JS

import 'package:js_bindings/js_bindings.dart';
import 'package:js_bindings/js_bindings.dart' as b;

Future<b.Response> fetchTest() async {
  final ret = await b.window.fetch('https://api.github.com/users/gawmanarnar');

  print(ret is Response);
  print(ret is b.Response);

  assert(ret is Response);

  return ret;
}

void main() async {
  final f = window.fetch('https://api.github.com/users/gawmanarnar');

  print('F:: $f, ${await f}');

  window.console.log(await f);

  print((await f) is b.Response);

  final foo = await fetchTest();

  print('foo: $foo');
}

error:

Expected a value of type 'window.Response', but got one of type '_Response'

fetch definition:

Future<Response> fetch(dynamic input, [RequestInit? init]) =>
      js_util.promiseToFuture(js_util.callMethod(this, 'fetch', [input, init]));

// Response definition

@JS()
@staticInterop
class Response implements Body {
  external Response([dynamic body, ResponseInit? init]);
}
@keertip keertip added the area-web Use area-web for Dart web related issues, including the DDC and dart2js compilers and JS interop. label Apr 19, 2022
@Markzipan Markzipan added the web-js-interop Issues that impact all js interop label Apr 19, 2022
@Markzipan
Copy link
Contributor

Pinging @rileyporter since she's more experienced with the js_bindings layer.

@srujzs
Copy link
Contributor

srujzs commented Apr 19, 2022

This looks like a bug in the static interop eraser.

Minimal repro:

@JS()
library response_test;

import 'dart:js_util' as js_util;

import 'package:js/js.dart';

@JS()
external Window get window;

@JS()
@staticInterop
class Window {}

extension E on Window {
  Future<Response> fetch(dynamic input) =>
      js_util.promiseToFuture(js_util.callMethod(this, 'fetch', [input]));
}

@JS()
@staticInterop
class Response {}

Future<Response> fetchTest() async {
  final ret = await window.fetch('https://api.github.com/users/gawmanarnar');
  print(ret is Response);
  assert(ret is Response);
  return ret;
}

void main() async {
  final foo = await fetchTest();
}

Looking at the relevant DDC output:

  response_test['E|fetch'] = function E$124fetch($this, input) {
    return js_util.promiseToFuture(_interceptors.JavaScriptObject, js_util.callMethod(core.Object, $this, "fetch", T.JSArrayOfObjectN().of([input])));
  };
  response_test['E|get#fetch'] = function E$124get$35fetch($this) {
    return dart.fn(input => response_test['E|fetch']($this, input), T.dynamicToFutureOfJavaScriptObject());
  };
  response_test.fetchTest = function fetchTest() {
    return async.async(dart.packageJSType("Response"), function* fetchTest() {
      let ret = (yield response_test['E|fetch'](dart.global.window, "https://api.github.com/users/gawmanarnar"));
      core.print(_interceptors.JavaScriptObject.is(ret));
      if (!_interceptors.JavaScriptObject.is(ret)) dart.assertFailed(null, I[0], 27, 10, "ret is Response");
      return ret;
    });
  };
  response_test.main = function main() {
    return async.async(dart.void, function* main() {
      let foo = (yield response_test.fetchTest());
    });
  };

We can see that the eraser is not erasing the type parameter of the Future return value of fetchTest. Weirdly enough, it doesn't have the same issue for the fetch call. I'll need to dig through the AST to see why we don't visit the type parameter.

@srujzs srujzs self-assigned this Apr 19, 2022
@srujzs
Copy link
Contributor

srujzs commented Apr 20, 2022

FYI - this is unique to instances where we use the futureValueType of a FunctionNode (namely when we return a Future of interop types). I have a CL to allow all AST visitors/transformers to visit this field as well, which will resolve this issue. Note that this field is currently only used in DDC in sound mode, so that's the only place this should arise.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area-web Use area-web for Dart web related issues, including the DDC and dart2js compilers and JS interop. web-js-interop Issues that impact all js interop
Projects
None yet
Development

No branches or pull requests

4 participants