From f49a11e29a167b50120e9b1ed136af2aa9fc006a Mon Sep 17 00:00:00 2001 From: Sam Goldman Date: Tue, 8 Sep 2015 10:01:02 -0700 Subject: [PATCH] ES6 Generators Summary: Generators are iterable. Each value yielded from the generator function is iterated. Generators are iterators. Values can be passed in through the `next` method. The iterator result values will be either a) the yielded values, b) the value returned from the generator (the completion value) or c) `undefined`, if `next` is called after completion. Generators can delegate to other iterables, including other generators. Values passed to `next` on the delegator generator will be passed through to the delegatee. Similarly, values yielded by the delegatee will be returned along the iterable/iterator interfaces. The "delegate yield" expression, `yield *`, returns the completion value of the delegatee. While normal functions' return type is the type of the `return` statement, generators are different. The return type is always a Generator, where the second type argument is the type of the `return` statement. We use the same process to infer this type--an Closes https://github.com/facebook/flow/pull/534 Reviewed By: @jeffmo Differential Revision: D2416847 Pulled By: @gabelevi --- lib/core.js | 18 +++-- src/typing/constraint_js.ml | 15 ++-- src/typing/constraint_js.mli | 6 +- src/typing/env_js.ml | 8 +- src/typing/env_js.mli | 1 + src/typing/type_inference_js.ml | 139 +++++++++++++++++++++++--------- tests/async/async.exp | 14 ++-- tests/forof/forof.exp | 4 +- tests/generators/.flowconfig | 7 ++ tests/generators/async.js | 4 + tests/generators/class.js | 118 +++++++++++++++++++++++++++ tests/generators/generators.exp | 112 +++++++++++++++++++++++++ tests/generators/generators.js | 106 ++++++++++++++++++++++++ tests/iterable/iterable.exp | 2 +- tests/promises/promises.exp | 26 +++--- tests/union/union.exp | 2 +- 16 files changed, 503 insertions(+), 79 deletions(-) create mode 100644 tests/generators/.flowconfig create mode 100644 tests/generators/async.js create mode 100644 tests/generators/class.js create mode 100644 tests/generators/generators.exp create mode 100644 tests/generators/generators.js diff --git a/lib/core.js b/lib/core.js index 1380e2fa6e8..c4e7afe76c3 100644 --- a/lib/core.js +++ b/lib/core.js @@ -300,13 +300,21 @@ interface IteratorResult { value?: T; } -interface Iterator { - next(): IteratorResult; - @@iterator(): Iterator; +interface $Iterator { + @@iterator(): $Iterator; + next(value?: Next): IteratorResult; } +type Iterator = $Iterator; -interface Iterable { - @@iterator(): Iterator; +interface $Iterable { + @@iterator(): $Iterator; +} +type Iterable = $Iterable; + +/* Generators */ +interface Generator { + @@iterator(): $Iterator; + next(value?: Next): IteratorResult; } /* Maps and Sets */ diff --git a/src/typing/constraint_js.ml b/src/typing/constraint_js.ml index e7b141ff1ae..c33295057ba 100644 --- a/src/typing/constraint_js.ml +++ b/src/typing/constraint_js.ml @@ -806,7 +806,8 @@ module Scope = struct (* a var scope corresponds to a runtime activation, e.g. a function. *) type var_scope_attrs = { - async: bool + async: bool; + generator: bool } (* var and lexical scopes differ in hoisting behavior @@ -836,11 +837,10 @@ module Scope = struct refis = KeyMap.empty; } - (* return a fresh scope of the most common kind (var, non-async) *) - let fresh () = fresh_impl (VarScope { async = false }) - - (* return a fresh async var scope *) - let fresh_async () = fresh_impl (VarScope { async = true }) + (* return a fresh scope of the most common kind (var) *) + let fresh ?(async=false) ?(generator=false) () = + assert (not (async && generator)); + fresh_impl (VarScope { async; generator }) (* return a fresh lexical scope *) let fresh_lex () = fresh_impl LexScope @@ -2520,7 +2520,8 @@ let string_of_scope = Scope.( in let string_of_scope_kind = function - | VarScope attrs -> spf "VarScope { async: %b }" attrs.async + | VarScope { async; generator } -> + spf "VarScope { async: %b; generator: %b }" async generator | LexScope -> "Lex" in diff --git a/src/typing/constraint_js.mli b/src/typing/constraint_js.mli index 615e6f6f581..39a4b3c1541 100644 --- a/src/typing/constraint_js.mli +++ b/src/typing/constraint_js.mli @@ -342,7 +342,8 @@ module Scope: sig = Key.t type var_scope_attrs = { - async: bool + async: bool; + generator: bool } type kind = @@ -361,8 +362,7 @@ module Scope: sig mutable refis: refi_binding KeyMap.t } - val fresh: unit -> t - val fresh_async: unit -> t + val fresh: ?async:bool -> ?generator:bool -> unit -> t val fresh_lex: unit -> t val clone: t -> t diff --git a/src/typing/env_js.ml b/src/typing/env_js.ml index 53469853d8b..66322686282 100644 --- a/src/typing/env_js.ml +++ b/src/typing/env_js.ml @@ -97,7 +97,13 @@ let clone_scopes scopes = (* TODO will need to walk once LexScopes appear *) let in_async_scope () = Scope.( match (peek_scope ()).kind with - | VarScope { async } -> async + | VarScope { async; _ } -> async + | _ -> false +) + +let in_generator_scope () = Scope.( + match (peek_scope ()).kind with + | VarScope { generator; _ } -> generator | _ -> false ) diff --git a/src/typing/env_js.mli b/src/typing/env_js.mli index 078751feb9d..576f702e031 100644 --- a/src/typing/env_js.mli +++ b/src/typing/env_js.mli @@ -20,6 +20,7 @@ val get_scopes: unit -> Scope.t list val clone_scopes: Scope.t list -> Scope.t list val in_async_scope: unit -> bool +val in_generator_scope: unit -> bool val all_entries: unit -> Entry.t SMap.t diff --git a/src/typing/type_inference_js.ml b/src/typing/type_inference_js.ml index 01adfe7cc89..8d8ce3e1d18 100644 --- a/src/typing/type_inference_js.ml +++ b/src/typing/type_inference_js.ml @@ -822,7 +822,6 @@ let rec convert cx type_params_map = Ast.Type.(function mk_nominal_type cx reason type_params_map (c, params) ) - (* TODO: unsupported generators *) | loc, Function { Function.params; returnType; rest; typeParameters } -> let typeparams, type_params_map = mk_type_param_declarations cx type_params_map typeParameters in @@ -1624,18 +1623,26 @@ and statement cx type_params_map = Ast.Statement.( | None -> void_ loc | Some expr -> expression cx type_params_map expr in - (* if we're in an async function, convert the return - expression's type T to Promise *) - let t = if Env_js.in_async_scope () - then - (* Unwrap and wrap t with a Promise by returning whatever - Promise.resolve(t) returns. *) + let t = + if Env_js.in_async_scope () then + (* Convert the return expression's type T to Promise. If the + * expression type is itself a Promise, ensure we still return + * a Promise via Promise.resolve. *) let reason = mk_reason "async return" loc in let promise = Env_js.get_var cx "Promise" reason in Flow_js.mk_tvar_where cx reason (fun tvar -> let call = Flow_js.mk_methodtype promise [t] tvar in Flow_js.flow cx (promise, MethodT (reason, "resolve", call)) ) + else if Env_js.in_generator_scope () then + (* Convert the return expression's type R to Generator, where + * Y and R are internals, installed earlier. *) + let reason = mk_reason "generator return" loc in + Flow_js.get_builtin_typeapp cx reason "Generator" [ + Env_js.get_var cx (internal_name "yield") reason; + t; + Env_js.get_var cx (internal_name "next") reason + ] else t in Flow_js.flow cx (t, ret); @@ -2021,8 +2028,8 @@ and statement cx type_params_map = Ast.Statement.( let o = Flow_js.get_builtin_typeapp cx (mk_reason "iteration expected on Iterable" loc) - "Iterable" - [element_tvar] in + "$Iterable" + [element_tvar; AnyT.at loc; AnyT.at loc] in Flow_js.flow cx (t, o); (* null/undefined are NOT allowed *) @@ -2076,11 +2083,11 @@ and statement cx type_params_map = Ast.Statement.( | (loc, Debugger) -> () - (* TODO: unsupported generators *) | (loc, FunctionDeclaration { FunctionDeclaration.id; params; defaults; rest; body; + generator; returnType; typeParameters; async; @@ -2088,7 +2095,7 @@ and statement cx type_params_map = Ast.Statement.( }) -> let reason = mk_reason "function" loc in let this = Flow_js.mk_tvar cx (replace_reason "this" reason) in - let fn_type = mk_function None cx type_params_map reason ~async + let fn_type = mk_function None cx type_params_map reason ~async ~generator typeParameters (params, defaults, rest) returnType body this in @@ -2707,11 +2714,11 @@ and object_prop cx type_params_map map = Ast.Expression.Object.(function _ }) -> Ast.Expression.Function.( let { params; defaults; rest; body; - returnType; typeParameters; id; async; _ } = func + returnType; typeParameters; id; async; generator; _ } = func in let reason = mk_reason "function" vloc in let this = Flow_js.mk_tvar cx (replace_reason "this" reason) in - let ft = mk_function id cx type_params_map ~async reason typeParameters + let ft = mk_function id cx type_params_map ~async ~generator reason typeParameters (params, defaults, rest) returnType body this in Hashtbl.replace cx.type_table vloc ft; @@ -2761,7 +2768,7 @@ and object_prop cx type_params_map map = Ast.Expression.Object.(function let { body; returnType; _ } = func in let reason = mk_reason "getter function" vloc in let this = Flow_js.mk_tvar cx (replace_reason "this" reason) in - let function_type = mk_function None cx type_params_map ~async:false reason None + let function_type = mk_function None cx type_params_map ~async:false ~generator:false reason None ([], [], None) returnType body this in let return_t = extract_getter_type function_type in @@ -2786,7 +2793,7 @@ and object_prop cx type_params_map map = Ast.Expression.Object.(function let { params; defaults; body; returnType; _ } = func in let reason = mk_reason "setter function" vloc in let this = Flow_js.mk_tvar cx (replace_reason "this" reason) in - let function_type = mk_function None cx type_params_map ~async:false reason None + let function_type = mk_function None cx type_params_map ~async:false ~generator:false reason None (params, defaults, None) returnType body this in let param_t = extract_setter_type function_type in @@ -3431,6 +3438,7 @@ and expression_ ~is_cond cx type_params_map loc e = Ast.Expression.(match e with params; defaults; rest; body; async; + generator; returnType; typeParameters; _ @@ -3438,10 +3446,9 @@ and expression_ ~is_cond cx type_params_map loc e = Ast.Expression.(match e with let desc = (if async then "async " else "") ^ "function" in let reason = mk_reason desc loc in let this = Flow_js.mk_tvar cx (replace_reason "this" reason) in - mk_function id cx type_params_map reason ~async + mk_function id cx type_params_map reason ~async ~generator typeParameters (params, defaults, rest) returnType body this - (* TODO: unsupported generators *) | ArrowFunction { ArrowFunction.id; params; defaults; rest; @@ -3455,7 +3462,7 @@ and expression_ ~is_cond cx type_params_map loc e = Ast.Expression.(match e with let reason = mk_reason desc loc in let this = this_ cx reason in let super = super_ cx reason in - mk_arrow id cx type_params_map reason ~async + mk_arrow id cx type_params_map reason ~async ~generator:false typeParameters (params, defaults, rest) returnType body this super | TaggedTemplate { @@ -3515,8 +3522,37 @@ and expression_ ~is_cond cx type_params_map loc e = Ast.Expression.(match e with class_t; | None -> mk_class cx type_params_map loc reason c) + | Yield { Yield.argument; delegate = false } -> + let reason = mk_reason "yield" loc in + let yield = Env_js.get_var cx (internal_name "yield") reason in + let t = expression cx type_params_map argument in + Flow_js.flow cx (t, yield); + let next = Env_js.get_var cx (internal_name "next") reason in + OptionalT next + + | Yield { Yield.argument; delegate = true } -> + let reason = mk_reason "yield* delegate" loc in + let next = Env_js.get_var cx + (internal_name "next") + (prefix_reason "next of parent generator in " reason) in + let yield = Env_js.get_var cx + (internal_name "yield") + (prefix_reason "yield of parent generator in " reason) in + let t = expression cx type_params_map argument in + + let ret = Flow_js.mk_tvar cx + (prefix_reason "return of child generator in " reason) in + + (* widen yield with the element type of the delegated-to iterable *) + let iterable = Flow_js.get_builtin_typeapp cx + (mk_reason "iteration expected on Iterable" loc) + "$Iterable" + [yield; ret; next] in + Flow_js.flow cx (t, iterable); + + ret + (* TODO *) - | Yield _ | Comprehension _ | Generator _ | Let _ -> @@ -4289,7 +4325,7 @@ and react_create_class cx type_params_map loc class_props = Ast.Expression.( returnType; typeParameters; _ } = func in let reason = mk_reason "defaultProps" vloc in - let t = mk_method cx type_params_map reason ~async:false (params, defaults, rest) + let t = mk_method cx type_params_map reason ~async:false ~generator:false (params, defaults, rest) returnType body this (MixedT reason) in (match t with @@ -4310,7 +4346,7 @@ and react_create_class cx type_params_map loc class_props = Ast.Expression.( returnType; typeParameters; _ } = func in let reason = mk_reason "initialState" vloc in - let t = mk_method cx type_params_map reason ~async:false (params, defaults, rest) + let t = mk_method cx type_params_map reason ~async:false ~generator:false (params, defaults, rest) returnType body this (MixedT reason) in let override_state = @@ -4330,10 +4366,10 @@ and react_create_class cx type_params_map loc class_props = Ast.Expression.( _ }) -> Ast.Expression.Function.( let { params; defaults; rest; body; - returnType; typeParameters; async; _ } = func + returnType; typeParameters; async; generator; _ } = func in let reason = mk_reason "function" vloc in - let t = mk_method cx type_params_map reason ~async (params, defaults, rest) + let t = mk_method cx type_params_map reason ~async ~generator (params, defaults, rest) returnType body this (MixedT reason) in fmap, SMap.add name t mmap @@ -5123,7 +5159,7 @@ and mk_class_elements cx instance_info static_info body = Ast.Class.( Method.key = Ast.Expression.Object.Property.Identifier (_, { Ast.Identifier.name; _ }); value = _, { Ast.Expression.Function.params; defaults; rest; - returnType; typeParameters; body; async; _ }; + returnType; typeParameters; body; async; generator; _ }; static; kind; decorators; @@ -5145,6 +5181,14 @@ and mk_class_elements cx instance_info static_info body = Ast.Class.( (_, _, ret, param_types_map, param_loc_map) = SMap.find_unsafe name sigs_to_use in + let yield, next = if generator then ( + Flow_js.mk_tvar cx (prefix_reason "yield of " reason), + Flow_js.mk_tvar cx (prefix_reason "next of " reason) + ) else ( + MixedT (replace_reason "no yield" reason), + MixedT (replace_reason "no next" reason) + ) in + let save_return_exn = Abnormal.swap Abnormal.Return false in let save_throw_exn = Abnormal.swap Abnormal.Throw false in Flow_js.generate_tests cx reason typeparams (fun map_ -> @@ -5158,8 +5202,8 @@ and mk_class_elements cx instance_info static_info body = Ast.Class.( | MixedT _ -> false | _ -> name = "constructor" in - mk_body None cx type_params_map ~async ~derived_ctor - param_types_map param_loc_map ret body this super; + mk_body None cx type_params_map ~async ~generator ~derived_ctor + param_types_map param_loc_map ret body this super yield next; ); ignore (Abnormal.swap Abnormal.Return save_return_exn); ignore (Abnormal.swap Abnormal.Throw save_throw_exn) @@ -5512,7 +5556,7 @@ and mk_interface cx reason_i typeparams type_params_map signature consisting of type parameters, parameter types, parameter names, and return type, check the body against that signature by adding `this` and `super` to the environment, and return the signature. *) -and function_decl id cx type_params_map (reason:reason) ~async +and function_decl id cx type_params_map (reason:reason) ~async ~generator type_params params ret body this super = let typeparams, type_params_map = @@ -5521,6 +5565,14 @@ and function_decl id cx type_params_map (reason:reason) ~async let (params, pnames, ret, param_types_map, param_types_loc) = mk_params_ret cx type_params_map params (body, ret) in + let yield, next = if generator then ( + Flow_js.mk_tvar cx (prefix_reason "yield of " reason), + Flow_js.mk_tvar cx (prefix_reason "next of " reason) + ) else ( + MixedT (replace_reason "no yield" reason), + MixedT (replace_reason "no next" reason) + ) in + let save_return_exn = Abnormal.swap Abnormal.Return false in let save_throw_exn = Abnormal.swap Abnormal.Throw false in Flow_js.generate_tests cx reason typeparams (fun map_ -> @@ -5530,7 +5582,8 @@ and function_decl id cx type_params_map (reason:reason) ~async param_types_map |> SMap.map (Flow_js.subst cx map_) in let ret = Flow_js.subst cx map_ ret in - mk_body id cx type_params_map ~async param_types_map param_types_loc ret body this super; + mk_body id cx type_params_map ~async ~generator + param_types_map param_types_loc ret body this super yield next; ); ignore (Abnormal.swap Abnormal.Return save_return_exn); @@ -5582,8 +5635,8 @@ and define_internal cx reason x = let opt = Env_js.get_var_declared_type cx ix reason in Env_js.set_var cx ix (Flow_js.filter_optional cx reason opt) reason -and mk_body id cx type_params_map ~async ?(derived_ctor=false) - param_types_map param_locs_map ret body this super = +and mk_body id cx type_params_map ~async ~generator ?(derived_ctor=false) + param_types_map param_locs_map ret body this super yield next = let ctx = Env_js.get_scopes () in let new_ctx = Env_js.clone_scopes ctx in Env_js.update_env cx new_ctx; @@ -5591,7 +5644,7 @@ and mk_body id cx type_params_map ~async ?(derived_ctor=false) (* create and prepopulate function scope *) let function_scope = - let scope = Scope.(if async then fresh_async else fresh) () in + let scope = Scope.fresh ~async ~generator () in (* add param bindings *) param_types_map |> SMap.iter (fun name t -> let entry = Scope.Entry.(match SMap.get name param_locs_map with @@ -5608,8 +5661,12 @@ and mk_body id cx type_params_map ~async ?(derived_ctor=false) (* special bindings for this, super, and return value slot *) initialize_this_super derived_ctor this super scope; Scope.( - let entry = Entry.(new_const ~state:Initialized ret) in - add_entry (internal_name "return") entry scope + let yield_entry = Entry.(new_const ~state:Initialized yield) in + let next_entry = Entry.(new_const ~state:Initialized next) in + let ret_entry = Entry.(new_const ~state:Initialized ret) in + add_entry (internal_name "yield") yield_entry scope; + add_entry (internal_name "next") next_entry scope; + add_entry (internal_name "return") ret_entry scope ); scope in @@ -5632,6 +5689,10 @@ and mk_body id cx type_params_map ~async ?(derived_ctor=false) let reason = mk_reason "return Promise" loc in let promise = Env_js.var_ref ~lookup_mode:ForType cx "Promise" reason in TypeAppT (promise, [VoidT.at loc]) + else if generator then + let reason = mk_reason "return Generator" loc in + let ret = VoidT.at loc in + Flow_js.get_builtin_typeapp cx reason "Generator" [yield; ret; next] else VoidT (mk_reason "return undefined" loc) in @@ -5781,18 +5842,18 @@ and extract_type_param_instantiations = function | Some (_, typeParameters) -> typeParameters.Ast.Type.ParameterInstantiation.params (* Process a function definition, returning a (polymorphic) function type. *) -and mk_function id cx type_params_map reason ~async type_params params ret body this = +and mk_function id cx type_params_map reason ~async ~generator type_params params ret body this = (* Normally, functions do not have access to super. *) let super = MixedT (replace_reason "empty super object" reason) in let signature = - function_decl id cx type_params_map reason ~async type_params params ret body this super + function_decl id cx type_params_map reason ~async ~generator type_params params ret body this super in mk_function_type cx reason this signature (* Process an arrow function, returning a (polymorphic) function type. *) -and mk_arrow id cx type_params_map reason ~async type_params params ret body this super = +and mk_arrow id cx type_params_map reason ~async ~generator type_params params ret body this super = let signature = - function_decl id cx type_params_map reason ~async type_params params ret body this super + function_decl id cx type_params_map reason ~async ~generator type_params params ret body this super in (* Do not expose the type of `this` in the function's type. The call to function_decl above has already done the necessary checking of `this` in @@ -5825,9 +5886,9 @@ and mk_function_type cx reason this signature = (* This function is around for the sole purpose of modeling some method-like behaviors of non-ES6 React classes. It is otherwise deprecated. *) -and mk_method cx type_params_map reason ~async params ret body this super = +and mk_method cx type_params_map reason ~async ~generator params ret body this super = let (_,params,pnames,ret) = - function_decl None cx type_params_map ~async reason None params ret body this super + function_decl None cx type_params_map ~async ~generator reason None params ret body this super in FunT (reason, Flow_js.dummy_static, Flow_js.dummy_prototype, Flow_js.mk_functiontype2 diff --git a/tests/async/async.exp b/tests/async/async.exp index 05f567e8e6f..f847b1ffc38 100644 --- a/tests/async/async.exp +++ b/tests/async/async.exp @@ -3,29 +3,29 @@ async.js:12:3,11: async return Error: async.js:12:10,10: number This type is incompatible with -[LIB] core.js:367:32,45: union type +[LIB] core.js:375:32,45: union type async.js:30:30,35: number This type is incompatible with -[LIB] core.js:367:32,45: union type +[LIB] core.js:375:32,45: union type async.js:45:22,25: undefined This type is incompatible with -[LIB] core.js:352:1,380:1: Promise +[LIB] core.js:360:1,388:1: Promise async2.js:6:3,12: async return Error: async2.js:6:10,11: number This type is incompatible with -[LIB] core.js:367:32,45: union type +[LIB] core.js:375:32,45: union type async2.js:29:21,24: undefined This type is incompatible with -[LIB] core.js:352:1,380:1: Promise +[LIB] core.js:360:1,388:1: Promise async2.js:43:28,31: undefined This type is incompatible with -[LIB] core.js:352:1,380:1: Promise +[LIB] core.js:360:1,388:1: Promise async2.js:48:3,17: undefined This type is incompatible with @@ -35,7 +35,7 @@ async3.js:23:11,21: await Error: async3.js:12:10,11: number This type is incompatible with -[LIB] core.js:367:32,45: union type +[LIB] core.js:375:32,45: union type await_not_in_async.js:5:9,9: Unexpected number diff --git a/tests/forof/forof.exp b/tests/forof/forof.exp index ffb52d66d1f..f58436e0688 100644 --- a/tests/forof/forof.exp +++ b/tests/forof/forof.exp @@ -13,11 +13,11 @@ This type is incompatible with forof.js:32:12,17: number This type is incompatible with -[LIB] core.js:315:28,33: tuple type +[LIB] core.js:323:28,33: tuple type forof.js:39:12,17: number This type is incompatible with -[LIB] core.js:315:28,33: tuple type +[LIB] core.js:323:28,33: tuple type forof.js:43:28,33: string This type is incompatible with diff --git a/tests/generators/.flowconfig b/tests/generators/.flowconfig new file mode 100644 index 00000000000..4a58bdcdef3 --- /dev/null +++ b/tests/generators/.flowconfig @@ -0,0 +1,7 @@ +[ignore] + +[include] + +[libs] + +[options] diff --git a/tests/generators/async.js b/tests/generators/async.js new file mode 100644 index 00000000000..21bfdb04572 --- /dev/null +++ b/tests/generators/async.js @@ -0,0 +1,4 @@ +/* TODO: Support async iteration + * See: https://github.com/zenparsing/async-iteration/#async-generator-functions + */ +async function *unsupported() {} diff --git a/tests/generators/class.js b/tests/generators/class.js new file mode 100644 index 00000000000..8360b9cc8df --- /dev/null +++ b/tests/generators/class.js @@ -0,0 +1,118 @@ +class GeneratorExamples { + *stmt_yield(): Generator { + yield 0; // ok + yield ""; // error: string ~> number + } + + *stmt_next(): Generator { + var a = yield undefined; + if (a) { + (a : number); // ok + } + + var b = yield undefined; + if (b) { + (b : string); // error: number ~> string + } + } + + *stmt_return_ok(): Generator { + return 0; // ok + } + + *stmt_return_err(): Generator { + return ""; // error: string ~> number + } + + *infer_stmt() { + var x: ?boolean = yield 0; + return ""; + } + + *widen_next() { + var x = yield 0; + if (x == null) { + } else if (typeof x === "number") { + } else if (typeof x === "boolean") { + } else { + (x : string) // ok, sherlock + } + } + + *widen_yield() { + yield 0; + yield ""; + yield true; + } + + *delegate_next_generator() { + function *inner() { + var x: ?number = yield undefined; // error: string ~> number + } + yield *inner(); + } + + *delegate_yield_generator() { + function *inner() { + yield ""; + } + + yield *inner(); + } + + *delegate_return_generator() { + function *inner() { + return ""; + } + + var x: number = yield *inner(); // error: string ~> number + } + + // only generators can make use of a value passed to next + *delegate_next_iterable(xs: Array) { + yield *xs; + } + + *delegate_yield_iterable(xs: Array) { + yield *xs; + } + + *delegate_return_iterable(xs: Array) { + var x: void = yield *xs // ok: Iterator has no yield value + } +} + +var examples = new GeneratorExamples(); + +for (var x of examples.infer_stmt()) { (x : string) } // error: number ~> string + +var infer_stmt_next = examples.infer_stmt().next(0).value; // error: number ~> boolean +if (typeof infer_stmt_next === "undefined") { +} else if (typeof infer_stmt_next === "number") { +} else { + (infer_stmt_next : boolean) // error: string ~> boolean +} + +examples.widen_next().next(0) +examples.widen_next().next("") +examples.widen_next().next(true) + +for (var x of examples.widen_yield()) { + if (typeof x === "number") { + } else if (typeof x === "boolean") { + } else { + (x : string) // ok, sherlock + } +} + +examples.delegate_next_generator().next(""); + +for (var x of examples.delegate_yield_generator()) { + (x : number) // error: string ~> number +} + +examples.delegate_next_iterable([]).next(""); // error: Iterator has no next value + +for (var x of examples.delegate_yield_iterable([])) { + (x : string) // error: number ~> string +} diff --git a/tests/generators/generators.exp b/tests/generators/generators.exp new file mode 100644 index 00000000000..289b0e277f9 --- /dev/null +++ b/tests/generators/generators.exp @@ -0,0 +1,112 @@ + +async.js:4:17,27: A function may not be both async and a generator + +class.js:4:11,12: string +This type is incompatible with +class.js:2:28,33: number + +class.js:7:39,44: number +This type is incompatible with +class.js:15:12,17: string + +class.js:23:39,44: number +This type is incompatible with +class.js:24:12,13: string + +class.js:24:12,13: string +This type is incompatible with +class.js:23:39,44: number + +class.js:28:29,29: number +This type is incompatible with +class.js:87:45,50: string + +class.js:57:13,14: string +This type is incompatible with +class.js:111:8,13: number + +class.js:65:14,15: string +This type is incompatible with +class.js:68:12,17: number + +class.js:76:38,43: number +This type is incompatible with +class.js:117:8,13: string + +class.js:89:23,51: call of method next +Error: +class.js:89:50,50: number +This type is incompatible with +class.js:28:13,19: boolean + +class.js:89:23,57: property `value` +Error: +class.js:29:12,13: string +This type is incompatible with +class.js:93:22,28: boolean + +class.js:108:41,42: string +This type is incompatible with +class.js:50:15,20: number + +class.js:114:1,44: call of method next +Error: +class.js:114:42,43: string +This type is incompatible with +[LIB] core.js:307:37,40: undefined + +generators.js:3:9,10: string +This type is incompatible with +generators.js:1:35,40: number + +generators.js:6:46,51: number +This type is incompatible with +generators.js:14:10,15: string + +generators.js:22:46,51: number +This type is incompatible with +generators.js:23:10,11: string + +generators.js:23:10,11: string +This type is incompatible with +generators.js:22:46,51: number + +generators.js:27:27,27: number +This type is incompatible with +generators.js:30:36,41: string + +generators.js:31:23,42: call of method next +Error: +generators.js:31:41,41: number +This type is incompatible with +generators.js:27:11,17: boolean + +generators.js:31:23,48: property `value` +Error: +generators.js:28:10,11: string +This type is incompatible with +generators.js:35:22,28: boolean + +generators.js:70:32,33: string +This type is incompatible with +generators.js:66:13,18: number + +generators.js:74:11,12: string +This type is incompatible with +generators.js:80:8,13: number + +generators.js:85:12,13: string +This type is incompatible with +generators.js:88:10,15: number + +generators.js:95:1,35: call of method next +Error: +generators.js:95:33,34: string +This type is incompatible with +[LIB] core.js:307:37,40: undefined + +generators.js:97:45,50: number +This type is incompatible with +generators.js:101:8,13: string + +Found 25 errors diff --git a/tests/generators/generators.js b/tests/generators/generators.js new file mode 100644 index 00000000000..086f22d252d --- /dev/null +++ b/tests/generators/generators.js @@ -0,0 +1,106 @@ +function *stmt_yield(): Generator { + yield 0; // ok + yield ""; // error: string ~> number +} + +function *stmt_next(): Generator { + var a = yield undefined; + if (a) { + (a : number); // ok + } + + var b = yield undefined; + if (b) { + (b : string); // error: number ~> string + } +} + +function *stmt_return_ok(): Generator { + return 0; // ok +} + +function *stmt_return_err(): Generator { + return ""; // error: string ~> number +} + +function *infer_stmt() { + var x: ?boolean = yield 0; + return ""; +} +for (var x of infer_stmt()) { (x : string) } // error: number ~> string +var infer_stmt_next = infer_stmt().next(0).value; // error: number ~> boolean +if (typeof infer_stmt_next === "undefined") { +} else if (typeof infer_stmt_next === "number") { +} else { + (infer_stmt_next : boolean) // error: string ~> boolean +} + +function *widen_next() { + var x = yield 0; + if (x == null) { + } else if (typeof x === "number") { + } else if (typeof x === "boolean") { + } else { + (x : string) // ok, sherlock + } +} +widen_next().next(0) +widen_next().next("") +widen_next().next(true) + +function *widen_yield() { + yield 0; + yield ""; + yield true; +} +for (var x of widen_yield()) { + if (typeof x === "number") { + } else if (typeof x === "boolean") { + } else { + (x : string) // ok, sherlock + } +} + +function *delegate_next_generator() { + function *inner() { + var x: ?number = yield undefined; // error: string ~> number + } + yield *inner(); +} +delegate_next_generator().next(""); + +function *delegate_yield_generator() { + function *inner() { + yield ""; + } + + yield *inner(); +} +for (var x of delegate_yield_generator()) { + (x : number) // error: string ~> number +} + +function *delegate_return_generator() { + function *inner() { + return ""; + } + + var x: number = yield *inner(); // error: string ~> number +} + +// only generators can make use of a value passed to next +function *delegate_next_iterable(xs: Array) { + yield *xs; +} +delegate_next_iterable([]).next(""); // error: Iterator has no next value + +function *delegate_yield_iterable(xs: Array) { + yield *xs; +} +for (var x of delegate_yield_iterable([])) { + (x : string) // error: number ~> string +} + +function *delegate_return_iterable(xs: Array) { + var x: void = yield *xs // ok: Iterator has no yield value +} diff --git a/tests/iterable/iterable.exp b/tests/iterable/iterable.exp index 5fd5a819f24..40f84da748f 100644 --- a/tests/iterable/iterable.exp +++ b/tests/iterable/iterable.exp @@ -17,7 +17,7 @@ caching_bug.js:21:62,67: string map.js:13:55,60: string This type is incompatible with -[LIB] core.js:315:28,33: tuple type +[LIB] core.js:323:28,33: tuple type set.js:13:28,33: string This type is incompatible with diff --git a/tests/promises/promises.exp b/tests/promises/promises.exp index 2ef69558c82..e491f239cef 100644 --- a/tests/promises/promises.exp +++ b/tests/promises/promises.exp @@ -3,78 +3,78 @@ promise.js:10:1,17:2: call of method then Error: promise.js:11:11,11: number This type is incompatible with -[LIB] core.js:354:25,38: union type +[LIB] core.js:362:25,38: union type promise.js:21:11,23:4: constructor call Error: promise.js:22:13,13: number This type is incompatible with -[LIB] core.js:354:25,38: union type +[LIB] core.js:362:25,38: union type promise.js:32:13,34:6: constructor call Error: promise.js:33:15,15: number This type is incompatible with -[LIB] core.js:354:25,38: union type +[LIB] core.js:362:25,38: union type promise.js:42:1,55:2: call of method then Error: promise.js:44:13,14: number This type is incompatible with -[LIB] core.js:354:25,38: union type +[LIB] core.js:362:25,38: union type promise.js:106:1,109:2: call of method then Error: promise.js:106:17,17: number This type is incompatible with -[LIB] core.js:367:32,45: union type +[LIB] core.js:375:32,45: union type promise.js:112:17,34: call of method resolve Error: promise.js:112:33,33: number This type is incompatible with -[LIB] core.js:367:32,45: union type +[LIB] core.js:375:32,45: union type promise.js:118:33,50: call of method resolve Error: promise.js:118:49,49: number This type is incompatible with -[LIB] core.js:367:32,45: union type +[LIB] core.js:375:32,45: union type promise.js:148:1,153:4: call of method then Error: promise.js:149:32,37: string This type is incompatible with -[LIB] core.js:359:33,46: union type +[LIB] core.js:367:33,46: union type promise.js:157:32,54: call of method resolve Error: promise.js:157:48,53: string This type is incompatible with -[LIB] core.js:367:32,45: union type +[LIB] core.js:375:32,45: union type promise.js:165:48,70: call of method resolve Error: promise.js:165:64,69: string This type is incompatible with -[LIB] core.js:367:32,45: union type +[LIB] core.js:375:32,45: union type promise.js:188:1,193:4: call of method then Error: promise.js:189:33,38: string This type is incompatible with -[LIB] core.js:364:34,48: union type +[LIB] core.js:372:34,48: union type promise.js:197:33,55: call of method resolve Error: promise.js:197:49,54: string This type is incompatible with -[LIB] core.js:367:32,45: union type +[LIB] core.js:375:32,45: union type promise.js:205:49,71: call of method resolve Error: promise.js:205:65,70: string This type is incompatible with -[LIB] core.js:367:32,45: union type +[LIB] core.js:375:32,45: union type Found 13 errors diff --git a/tests/union/union.exp b/tests/union/union.exp index 00d6d6e4fbe..469722b9b06 100644 --- a/tests/union/union.exp +++ b/tests/union/union.exp @@ -3,7 +3,7 @@ issue-198.js:1:9,10:6: call of method then Error: issue-198.js:5:16,28: string This type is incompatible with -[LIB] core.js:359:33,46: union type +[LIB] core.js:367:33,46: union type issue-324.js:3:7,9: Bar This type is incompatible with