Skip to content
Permalink
Browse files

Read-only arrays cannot be written to with any-typed indexes, and bet…

…ter errors

Summary:
Before we could write to a `$ReadOnlyArray` if the index was any-typed, like `declare var x: $ReadOnlyArray<number>; x[(42: any)] = 99;`. This seems wrong: read-onlyness is a property of the array, so why should we be able to write to it at all? It would be like saying that you can call a number as if it were a function if you passed it `(42: any)` as an argument. This diff removes this behavior.

Additionally, the error message generated from writing to a read only array refers to writing to a tuple with an unknown indexer. This doesn't really apply in the situation, so I've added a new error message for writing to a read-only array specifically.

Reviewed By: dsainati1

Differential Revision: D14783752

fbshipit-source-id: 4239f397fe857775bbd029c3141498257a66963e
  • Loading branch information...
mvitousek authored and facebook-github-bot committed Apr 5, 2019
1 parent 6c93e91 commit 875cb7a94d22237cb324aa0e896fcf78c99342bf
@@ -2619,6 +2619,11 @@ let dump_error_message =
(dump_reason cx reason1)
(dump_reason cx reason2)
(string_of_use_op use_op)
| EROArrayWrite ((reason1, reason2), use_op) ->
spf "EROArrayWrite (%s, %s, %s)"
(dump_reason cx reason1)
(dump_reason cx reason2)
(string_of_use_op use_op)
| EUnionSpeculationFailed { use_op; reason; reason_op; branches = _ } ->
spf "EUnionSpeculationFailed { use_op = %s; reason = %s; reason_op = %s; branches = _ }"
(string_of_use_op use_op)
@@ -77,6 +77,7 @@ and 'loc t' =
| ENonLitArrayToTuple of ('loc virtual_reason * 'loc virtual_reason) * 'loc virtual_use_op
| ETupleOutOfBounds of ('loc virtual_reason * 'loc virtual_reason) * int * int * 'loc virtual_use_op
| ETupleUnsafeWrite of ('loc virtual_reason * 'loc virtual_reason) * 'loc virtual_use_op
| EROArrayWrite of ('loc virtual_reason * 'loc virtual_reason) * 'loc virtual_use_op
| EUnionSpeculationFailed of {
use_op: 'loc virtual_use_op;
reason: 'loc virtual_reason;
@@ -362,6 +363,8 @@ let map_loc_of_error_message (f : 'a -> 'b) : 'a t' -> 'b t' =
ETupleOutOfBounds ((map_reason r1, map_reason r2), l, i, map_use_op op)
| ETupleUnsafeWrite ((r1, r2), op) ->
ETupleUnsafeWrite ((map_reason r1, map_reason r2), map_use_op op)
| EROArrayWrite ((r1, r2), op) ->
EROArrayWrite ((map_reason r1, map_reason r2), map_use_op op)
| EUnionSpeculationFailed {use_op; reason; reason_op; branches} ->
EUnionSpeculationFailed {use_op = map_use_op use_op; reason = map_reason reason;
reason_op = map_reason reason_op;
@@ -512,6 +515,7 @@ let util_use_op_of_msg nope util = function
| ENonLitArrayToTuple (rs, op) -> util op (fun op -> ENonLitArrayToTuple (rs, op))
| ETupleOutOfBounds (rs, l, i, op) -> util op (fun op -> ETupleOutOfBounds (rs, l, i, op))
| ETupleUnsafeWrite (rs, op) -> util op (fun op -> ETupleUnsafeWrite (rs, op))
| EROArrayWrite (rs, op) -> util op (fun op -> EROArrayWrite (rs, op))
| EUnionSpeculationFailed {use_op; reason; reason_op; branches} ->
util use_op (fun use_op -> EUnionSpeculationFailed {use_op; reason; reason_op; branches})
| EIncompatibleWithExact (rs, op) -> util op (fun op -> EIncompatibleWithExact (rs, op))
@@ -742,6 +746,7 @@ let aloc_of_msg : t -> ALoc.t option = function
| EIncompatibleWithExact _
| EUnionSpeculationFailed _
| ETupleUnsafeWrite _
| EROArrayWrite _
| ETupleOutOfBounds _
| ENonLitArrayToTuple _
| ETupleArityMismatch _
@@ -1099,6 +1104,12 @@ let friendly_message_of_msg : Loc.t t' -> Loc.t friendly_message_recipe =
[text "the index must be statically known to write a tuple element"],
use_op)

| EROArrayWrite (reasons, use_op) ->
let (lower, _) = reasons in
UseOp (loc_of_reason lower,
[text "read-only arrays cannot be written to"],
use_op)

| EUnionSpeculationFailed { use_op; reason; reason_op=_; branches } ->
Speculation (loc_of_reason reason, use_op, branches)

@@ -5327,7 +5327,16 @@ let rec __flow cx ((l: Type.t), (u: Type.use_t)) trace =
* a source of unsoundness, so that's ok. `tup[(0: any)] = 123` should not
* error when `tup[0] = 123` does not. *)
| AnyT _,
ElemT (use_op, reason_op, (DefT (_, _, ArrT arrtype) as arr), action) ->
ElemT (use_op, reason_op, (DefT (reason_tup, _, ArrT arrtype) as arr), action) ->
begin match action, arrtype with
| WriteElem _, ROArrayAT _ ->
let reasons = (reason_op, reason_tup) in
add_output
cx
~trace
(Error_message.EROArrayWrite (reasons, use_op))
| _ -> ()
end;
let value = elemt_of_arrtype arrtype in
perform_elem_action cx trace ~use_op reason_op arr value action

@@ -5337,7 +5346,7 @@ let rec __flow cx ((l: Type.t), (u: Type.use_t)) trace =
| TupleAT(value, ts) -> value, Some ts, true
| ROArrayAT (value) -> value, None, true
end in
let exact_index, value = match l with
let can_write_tuple, value = match l with
| DefT (_, _, NumT (Literal (_, (float_value, _)))) ->
begin match ts with
| None -> false, value
@@ -5358,17 +5367,23 @@ let rec __flow cx ((l: Type.t), (u: Type.use_t)) trace =
end
| _ -> false, value
in
if is_tuple && not exact_index then begin
if is_tuple && not can_write_tuple then begin
match action with
(* These are safe to do with tuples and unknown indexes *)
| ReadElem _ | CallElem _ -> ()
(* This isn't *)
| WriteElem _ ->
let reasons = (reason, reason_tup) in
add_output
cx
~trace
(Error_message.ETupleUnsafeWrite (reasons, use_op))
let error =
match ts with
| Some _ -> Error_message.ETupleUnsafeWrite (reasons, use_op)
| None -> Error_message.EROArrayWrite (reasons, use_op)
in
add_output
cx
~trace
error

end;

perform_elem_action cx trace ~use_op reason arr value action
@@ -26,5 +26,21 @@ References:
^ [1]


Error ----------------------------------------------------------------------------------------------------- test.js:38:1

Found 2 errors
Cannot assign `42` to `x[0]` because read-only arrays cannot be written to.

38| x[0] = 42;
^^^^


Error ----------------------------------------------------------------------------------------------------- test.js:39:1

Cannot assign `42` to `x[0]` because read-only arrays cannot be written to.

39| x[(0: any)] = 42;
^^^^^^^^^^^



Found 4 errors
@@ -33,3 +33,7 @@ class ContraBaz<-T> {
return 0;
}
}

declare var x: $ReadOnlyArray<number>;
x[0] = 42;
x[(0: any)] = 42;

0 comments on commit 875cb7a

Please sign in to comment.
You can’t perform that action at this time.