Skip to content

Commit

Permalink
fix(compiler): could not hydrate optionals (#3741)
Browse files Browse the repository at this point in the history
There are several open issues in progress that need to be able to define generic optionals. 
Such as: 
- #1868
- #3575
- #3653

I only added `tryAt` method for Array since the actual work is defined in another issue, but I needed an example to write some test cases with. Since there are several efforts that need this change I opted to not wait and include it in one of those PRs but just add the support here so other efforts can rebase.

## Checklist

- [x] Title matches [Winglang's style guide](https://www.winglang.io/contributing/start-here/pull_requests#how-are-pull-request-titles-formatted)
- [x] Description explains motivation and solution
- [x] Tests added (always)
- [x] Docs updated (only required for features)
- [x] Added `pr/e2e-full` label if this feature requires end-to-end testing

*By submitting this pull request, I confirm that my contribution is made under the terms of the [Wing Cloud Contribution License](https://github.com/winglang/wing/blob/main/CONTRIBUTION_LICENSE.md)*.
  • Loading branch information
hasanaburayyan authored Aug 8, 2023
1 parent af35df3 commit 6338ad7
Show file tree
Hide file tree
Showing 7 changed files with 106 additions and 33 deletions.
17 changes: 17 additions & 0 deletions docs/docs/04-standard-library/02-std/api-reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ Immutable Array.
| <code><a href="#@winglang/sdk.std.Array.indexOf">indexOf</a></code> | Returns the index of the first occurrence of searchElement found. |
| <code><a href="#@winglang/sdk.std.Array.join">join</a></code> | Returns a new string containing the concatenated values in this array, separated by commas or a specified separator string. |
| <code><a href="#@winglang/sdk.std.Array.lastIndexOf">lastIndexOf</a></code> | Returns the index of the last occurrence of searchElement found. |
| <code><a href="#@winglang/sdk.std.Array.tryAt">tryAt</a></code> | Get the value at the given index, returning nil if the index is out of bounds. |

---

Expand Down Expand Up @@ -137,6 +138,22 @@ to search for.

---

##### `tryAt` <a name="tryAt" id="@winglang/sdk.std.Array.tryAt"></a>

```wing
tryAt(index: num): <T>
```

Get the value at the given index, returning nil if the index is out of bounds.

###### `index`<sup>Required</sup> <a name="index" id="@winglang/sdk.std.Array.tryAt.parameter.index"></a>

- *Type:* num

index of the value to get.

---


#### Properties <a name="Properties" id="Properties"></a>

Expand Down
4 changes: 4 additions & 0 deletions examples/tests/invalid/container_types.w
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@ let arr3 = Array<str>["hello"];
let arr4: Array<num> = arr3;
arr1.someRandomMethod();

let arr5: Array<num> = [1, 2, 3];
let val: num = arr5.tryAt(0);
// ^^^^^^^^^^^^^ Expected type to be "num", but got "num?" instead

//Map tests
let m1: Map<num> = {"a" => 1, "b" => "2", "c" => 3};
let m2: Map<num> = ["a" => 1, "b" => "2", "c" => 3];
Expand Down
10 changes: 10 additions & 0 deletions examples/tests/valid/container_types.w
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,16 @@ let arr7: Array<num> = arr4;
assert(arr7.length == 3);
assert(arr7.at(1) == 2);

if let val = emptyArray.tryAt(0) {
assert(false); // Should not happen
}

if let val = arr1.tryAt(0) {
assert(val == 1);
} else {
assert(false); // Should not happen
}

//Map tests
let emptyMap = Map<num>{};
assert(emptyMap.size() == 0);
Expand Down
7 changes: 6 additions & 1 deletion libs/wingc/src/type_check.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3839,7 +3839,6 @@ impl<'a> TypeChecker<'a> {
// Replace type params in function signatures
if let Some(sig) = v.as_function_sig() {
let new_return_type = self.get_concrete_type_for_generic(sig.return_type, &types_map);

let new_this_type = if let Some(this_type) = sig.this_type {
Some(self.get_concrete_type_for_generic(this_type, &types_map))
} else {
Expand Down Expand Up @@ -3932,6 +3931,11 @@ impl<'a> TypeChecker<'a> {
} else {
// Handle generic return types
// TODO: If a generic class has a method that returns another generic, it must be a builtin
if let Type::Optional(t) = *type_to_maybe_replace {
let concrete_t = self.get_concrete_type_for_generic(t, types_map);
return self.types.add_type(Type::Optional(concrete_t));
}

if let Some(c) = type_to_maybe_replace.as_class() {
if let Some(type_parameters) = &c.type_parameters {
// For now all our generics only have a single type parameter so use the first type parameter as our "T1"
Expand All @@ -3942,6 +3946,7 @@ impl<'a> TypeChecker<'a> {
.map(|(_, n)| n)
.expect("generic must have a type parameter");
let fqn = format!("{}.{}", WINGSDK_STD_MODULE, c.name.name);

return match fqn.as_str() {
WINGSDK_MUT_ARRAY => self.types.add_type(Type::MutArray(t1_replacement)),
WINGSDK_ARRAY => self.types.add_type(Type::Array(t1_replacement)),
Expand Down
13 changes: 13 additions & 0 deletions libs/wingsdk/src/std/array.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,19 @@ export class Array {
throw new Error("Abstract");
}

/**
* Get the value at the given index, returning nil if the index is out of bounds.
*
* @macro ($self$.at($args$))
*
* @param index index of the value to get
* @returns the value at the given index, or undefined if the index is out of bounds
*/
public tryAt(index: number): T1 | undefined {
index;
throw new Error("Macro");
}

/**
* Merge arr to the end of this array
* @param arr array to merge
Expand Down
71 changes: 39 additions & 32 deletions tools/hangar/__snapshots__/invalid.ts.snap
Original file line number Diff line number Diff line change
Expand Up @@ -461,23 +461,23 @@ Duration <DURATION>"
exports[`container_types.w 1`] = `
"error: Unknown parser error
--> ../../../examples/tests/invalid/container_types.w:10:25
--> ../../../examples/tests/invalid/container_types.w:14:25
|
10 | let m2: Map<num> = [\\"a\\" => 1, \\"b\\" => \\"2\\", \\"c\\" => 3];
14 | let m2: Map<num> = [\\"a\\" => 1, \\"b\\" => \\"2\\", \\"c\\" => 3];
| ^^^^ Unknown parser error
error: Unexpected 'string'
--> ../../../examples/tests/invalid/container_types.w:10:31
--> ../../../examples/tests/invalid/container_types.w:14:31
|
10 | let m2: Map<num> = [\\"a\\" => 1, \\"b\\" => \\"2\\", \\"c\\" => 3];
14 | let m2: Map<num> = [\\"a\\" => 1, \\"b\\" => \\"2\\", \\"c\\" => 3];
| ^^^ Unexpected 'string'
error: Unknown parser error
--> ../../../examples/tests/invalid/container_types.w:10:47
--> ../../../examples/tests/invalid/container_types.w:14:47
|
10 | let m2: Map<num> = [\\"a\\" => 1, \\"b\\" => \\"2\\", \\"c\\" => 3];
14 | let m2: Map<num> = [\\"a\\" => 1, \\"b\\" => \\"2\\", \\"c\\" => 3];
| ^^^^ Unknown parser error
Expand Down Expand Up @@ -509,87 +509,94 @@ error: Unknown symbol \\"someRandomMethod\\"
| ^^^^^^^^^^^^^^^^ Unknown symbol \\"someRandomMethod\\"
error: Expected type to be \\"num\\", but got \\"str\\" instead
--> ../../../examples/tests/invalid/container_types.w:9:38
error: Expected type to be \\"num\\", but got \\"num?\\" instead
--> ../../../examples/tests/invalid/container_types.w:9:16
|
9 | let m1: Map<num> = {\\"a\\" => 1, \\"b\\" => \\"2\\", \\"c\\" => 3};
| ^^^ Expected type to be \\"num\\", but got \\"str\\" instead
9 | let val: num = arr5.tryAt(0);
| ^^^^^^^^^^^^^ Expected type to be \\"num\\", but got \\"num?\\" instead
error: Expected type to be \\"num\\", but got \\"str\\" instead
--> ../../../examples/tests/invalid/container_types.w:13:38
|
13 | let m1: Map<num> = {\\"a\\" => 1, \\"b\\" => \\"2\\", \\"c\\" => 3};
| ^^^ Expected type to be \\"num\\", but got \\"str\\" instead
error: Expected type to be \\"Map<num>\\", but got \\"Array<str>\\" instead
--> ../../../examples/tests/invalid/container_types.w:10:20
--> ../../../examples/tests/invalid/container_types.w:14:20
|
10 | let m2: Map<num> = [\\"a\\" => 1, \\"b\\" => \\"2\\", \\"c\\" => 3];
14 | let m2: Map<num> = [\\"a\\" => 1, \\"b\\" => \\"2\\", \\"c\\" => 3];
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Expected type to be \\"Map<num>\\", but got \\"Array<str>\\" instead
error: Expected type to be \\"Map<num>\\", but got \\"Map<str>\\" instead
--> ../../../examples/tests/invalid/container_types.w:12:20
--> ../../../examples/tests/invalid/container_types.w:16:20
|
12 | let m4: Map<num> = m3;
16 | let m4: Map<num> = m3;
| ^^ Expected type to be \\"Map<num>\\", but got \\"Map<str>\\" instead
error: Unknown symbol \\"someRandomMethod\\"
--> ../../../examples/tests/invalid/container_types.w:13:4
--> ../../../examples/tests/invalid/container_types.w:17:4
|
13 | m1.someRandomMethod();
17 | m1.someRandomMethod();
| ^^^^^^^^^^^^^^^^ Unknown symbol \\"someRandomMethod\\"
error: Expected type to be \\"num\\", but got \\"str\\" instead
--> ../../../examples/tests/invalid/container_types.w:16:24
--> ../../../examples/tests/invalid/container_types.w:20:24
|
16 | let s1: Set<num> = {1, \\"2\\", 3};
20 | let s1: Set<num> = {1, \\"2\\", 3};
| ^^^ Expected type to be \\"num\\", but got \\"str\\" instead
error: Expected \\"Array\\" type, found \\"Set<num>\\"
--> ../../../examples/tests/invalid/container_types.w:17:10
--> ../../../examples/tests/invalid/container_types.w:21:10
|
17 | let s2 = Set<num> [1, \\"2\\", 3];
21 | let s2 = Set<num> [1, \\"2\\", 3];
| ^^^^^^^^^^^^^^^^^^^^ Expected \\"Array\\" type, found \\"Set<num>\\"
error: Expected type to be \\"num\\", but got \\"str\\" instead
--> ../../../examples/tests/invalid/container_types.w:17:23
--> ../../../examples/tests/invalid/container_types.w:21:23
|
17 | let s2 = Set<num> [1, \\"2\\", 3];
21 | let s2 = Set<num> [1, \\"2\\", 3];
| ^^^ Expected type to be \\"num\\", but got \\"str\\" instead
error: Expected type to be \\"num\\", but got \\"str\\" instead
--> ../../../examples/tests/invalid/container_types.w:18:24
--> ../../../examples/tests/invalid/container_types.w:22:24
|
18 | let s3: Set<num> = [1, \\"2\\", 3];
22 | let s3: Set<num> = [1, \\"2\\", 3];
| ^^^ Expected type to be \\"num\\", but got \\"str\\" instead
error: Expected type to be \\"Set<num>\\", but got \\"Array<num>\\" instead
--> ../../../examples/tests/invalid/container_types.w:18:20
--> ../../../examples/tests/invalid/container_types.w:22:20
|
18 | let s3: Set<num> = [1, \\"2\\", 3];
22 | let s3: Set<num> = [1, \\"2\\", 3];
| ^^^^^^^^^^^ Expected type to be \\"Set<num>\\", but got \\"Array<num>\\" instead
error: Expected type to be \\"Set<str>\\", but got \\"Set<num>\\" instead
--> ../../../examples/tests/invalid/container_types.w:20:20
--> ../../../examples/tests/invalid/container_types.w:24:20
|
20 | let s5: Set<str> = s4;
24 | let s5: Set<str> = s4;
| ^^ Expected type to be \\"Set<str>\\", but got \\"Set<num>\\" instead
error: Unknown symbol \\"someRandomMethod\\"
--> ../../../examples/tests/invalid/container_types.w:21:4
--> ../../../examples/tests/invalid/container_types.w:25:4
|
21 | s1.someRandomMethod();
25 | s1.someRandomMethod();
| ^^^^^^^^^^^^^^^^ Unknown symbol \\"someRandomMethod\\"
error: Expected type to be \\"Array<str>\\", but got \\"MutArray<str>\\" instead
--> ../../../examples/tests/invalid/container_types.w:23:21
--> ../../../examples/tests/invalid/container_types.w:27:21
|
23 | let a: Array<str> = MutArray<str>[];
27 | let a: Array<str> = MutArray<str>[];
| ^^^^^^^^^^^^^^^ Expected type to be \\"Array<str>\\", but got \\"MutArray<str>\\" instead
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,23 @@ class $Root extends $stdlib.std.Resource {
const arr7 = arr4;
{((cond) => {if (!cond) throw new Error("assertion failed: arr7.length == 3")})((((a,b) => { try { return require('assert').deepStrictEqual(a,b) === undefined; } catch { return false; } })(arr7.length,3)))};
{((cond) => {if (!cond) throw new Error("assertion failed: arr7.at(1) == 2")})((((a,b) => { try { return require('assert').deepStrictEqual(a,b) === undefined; } catch { return false; } })((arr7.at(1)),2)))};
{
const $IF_LET_VALUE = (emptyArray.at(0));
if ($IF_LET_VALUE != undefined) {
const val = $IF_LET_VALUE;
{((cond) => {if (!cond) throw new Error("assertion failed: false")})(false)};
}
}
{
const $IF_LET_VALUE = (arr1.at(0));
if ($IF_LET_VALUE != undefined) {
const val = $IF_LET_VALUE;
{((cond) => {if (!cond) throw new Error("assertion failed: val == 1")})((((a,b) => { try { return require('assert').deepStrictEqual(a,b) === undefined; } catch { return false; } })(val,1)))};
}
else {
{((cond) => {if (!cond) throw new Error("assertion failed: false")})(false)};
}
}
const emptyMap = ({});
{((cond) => {if (!cond) throw new Error("assertion failed: emptyMap.size() == 0")})((((a,b) => { try { return require('assert').deepStrictEqual(a,b) === undefined; } catch { return false; } })(Object.keys(emptyMap).length,0)))};
const emptyMap2 = ({});
Expand Down

0 comments on commit 6338ad7

Please sign in to comment.