Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,6 @@ package-lock.json
node_modules
ace
*.wasm
.vscode
wasi-sdk-*
**/*/.mypy_cache
7 changes: 7 additions & 0 deletions WIT.md
Original file line number Diff line number Diff line change
Expand Up @@ -546,6 +546,13 @@ type foo = u32
type foo = u64 // ERROR: name `foo` already defined
```

A type and a function may not share the same name either:

```wit
type foo = u32
foo: func() // ERROR: name `foo` already defined
```

Names do not need to be defined before they're used (unlike in C or C++),
it's ok to define a type after it's used:

Expand Down
43 changes: 40 additions & 3 deletions crates/parser/src/ast/resolve.rs
Original file line number Diff line number Diff line change
Expand Up @@ -269,7 +269,17 @@ impl Resolver {
}

fn register_names(&mut self, fields: &[Item<'_>]) -> Result<()> {
let mut values = HashSet::new();
// TODO: add span info and generate error message pointing
// original definition in case of duplicates.
#[derive(Debug, Clone)]
enum Definition {
Resource,
Function,
Global,
Type,
}

let mut values = HashMap::new();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Implementation-wise I think that this isn't the best way to structure this one-namespace limitation. To support multiple namespaces the resolution here uses separate hash maps to prevent collisions between maps, for example resource_lookup and type_lookup. I think that instead of having a duplicate map here track that it would be better to have something like name_lookup which matches to an enum like this. That way define_{resource,type} and function insertion below would all modify the map.

for field in fields {
match field {
Item::Resource(r) => {
Expand All @@ -287,6 +297,16 @@ impl Resolver {
foreign_module: None,
});
self.define_type(&r.name.name, r.name.span, type_id)?;
if let Some(existing) = values.insert(&r.name.name, Definition::Resource) {
return Err(Error {
span: r.name.span,
msg: format!(
"Resource {:?} already defined as a {:?}",
r.name.name, existing
),
}
.into());
}
}
Item::TypeDef(t) => {
let docs = self.docs(&t.docs);
Expand All @@ -299,12 +319,29 @@ impl Resolver {
foreign_module: None,
});
self.define_type(&t.name.name, t.name.span, id)?;
if let Some(existing) = values.insert(&t.name.name, Definition::Type) {
return Err(Error {
span: t.name.span,
msg: format!(
"Type {:?} already defined as a {:?}",
t.name.name, existing
),
}
.into());
}
}
Item::Value(f) => {
if !values.insert(&f.name.name) {
let kind = match &f.kind {
ValueKind::Function { .. } => Definition::Function,
ValueKind::Global { .. } => Definition::Global,
};
if let Some(existing) = values.insert(&f.name.name, kind.clone()) {
return Err(Error {
span: f.name.span,
msg: format!("{:?} defined twice", f.name.name),
msg: format!(
"{:?} {:?} already defined as a {:?}",
kind, f.name.name, existing
),
}
.into());
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
"foo" defined twice
Function "foo" already defined as a Function
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it might be better to use "function" instead of "Function" here personally (avoiding the capital "F")

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it might be better to use "function" instead of "Function" here personally (avoiding the capital "F")

--> tests/ui/parse-fail/duplicate-functions.wit:4:1
|
4 | foo: func()
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
"a" defined twice
Global "a" already defined as a Global
--> tests/ui/parse-fail/duplicate-value.wit:3:1
|
3 | a: u32
Expand Down
4 changes: 4 additions & 0 deletions crates/parser/tests/ui/parse-fail/func-type-collide.wit
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
// parse-fail

type foo = s32
foo: func()
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
Function "foo" already defined as a Type
--> tests/ui/parse-fail/func-type-collide.wit:4:1
|
4 | foo: func()
| ^--
4 changes: 2 additions & 2 deletions crates/parser/tests/ui/values.wit
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
a: s32
b: tuple<>
record a {}
c: tuple<u32, a>
record c {}
d: tuple<u32, c>
4 changes: 2 additions & 2 deletions crates/parser/tests/ui/values.wit.result
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"types": [
{
"idx": 0,
"name": "a",
"name": "c",
"record": {
"fields": []
}
Expand Down Expand Up @@ -33,7 +33,7 @@
"ty": "type-1"
},
{
"name": "c",
"name": "d",
"ty": "type-2"
}
]
Expand Down
12 changes: 6 additions & 6 deletions tests/runtime/flavorful/exports.wit
Original file line number Diff line number Diff line change
Expand Up @@ -6,21 +6,21 @@ record list-in-record3 { a: string }
record list-in-record4 { a: string }
type list-in-alias = list-in-record4

list-in-record1: func(a: list-in-record1)
list-in-record2: func() -> list-in-record2
list-in-record3: func(a: list-in-record3) -> list-in-record3
list-in-record4: func(a: list-in-alias) -> list-in-alias
list-in-record1-func: func(a: list-in-record1)
list-in-record2-func: func() -> list-in-record2
list-in-record3-func: func(a: list-in-record3) -> list-in-record3
list-in-record4-func: func(a: list-in-alias) -> list-in-alias

type list-in-variant1-v1 = option<string>
type list-in-variant1-v2 = expected<unit, string>
union list-in-variant1-v3 { string, float32 }
list-in-variant1: func(a: list-in-variant1-v1, b: list-in-variant1-v2, c: list-in-variant1-v3)

type list-in-variant2 = option<string>
list-in-variant2: func() -> list-in-variant2
list-in-variant2-func: func() -> list-in-variant2

type list-in-variant3 = option<string>
list-in-variant3: func(a: list-in-variant3) -> list-in-variant3
list-in-variant3-func: func(a: list-in-variant3) -> list-in-variant3

enum my-errno { success, a, b }
errno-result: func() -> expected<unit, my-errno>
Expand Down
24 changes: 12 additions & 12 deletions tests/runtime/flavorful/host.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,17 @@
import wasmtime

class MyImports:
def list_in_record1(self, a: i.ListInRecord1) -> None:
def list_in_record1_func(self, a: i.ListInRecord1) -> None:
pass

def list_in_record2(self) -> i.ListInRecord2:
def list_in_record2_func(self) -> i.ListInRecord2:
return i.ListInRecord2('list_in_record2')

def list_in_record3(self, a: i.ListInRecord3) -> i.ListInRecord3:
def list_in_record3_func(self, a: i.ListInRecord3) -> i.ListInRecord3:
assert(a.a == 'list_in_record3 input')
return i.ListInRecord3('list_in_record3 output')

def list_in_record4(self, a: i.ListInAlias) -> i.ListInAlias:
def list_in_record4_func(self, a: i.ListInAlias) -> i.ListInAlias:
assert(a.a == 'input4')
return i.ListInRecord4('result4')

Expand All @@ -26,10 +26,10 @@ def list_in_variant1(self, a: i.ListInVariant1V1, b: i.ListInVariant1V2, c: i.Li
assert(b == i.Err('bar'))
assert(c == 'baz')

def list_in_variant2(self) -> i.ListInVariant2:
def list_in_variant2_func(self) -> i.ListInVariant2:
return 'list_in_variant2'

def list_in_variant3(self, a: i.ListInVariant3) -> i.ListInVariant3:
def list_in_variant3_func(self, a: i.ListInVariant3) -> i.ListInVariant3:
assert(a == 'input3')
return 'output3'

Expand Down Expand Up @@ -66,15 +66,15 @@ def run(wasm_file: str) -> None:
wasm = Exports(store, linker, module)

wasm.test_imports(store)
wasm.list_in_record1(store, e.ListInRecord1("list_in_record1"))
assert(wasm.list_in_record2(store) == e.ListInRecord2(a="list_in_record2"))
wasm.list_in_record1_func(store, e.ListInRecord1("list_in_record1"))
assert(wasm.list_in_record2_func(store) == e.ListInRecord2(a="list_in_record2"))

assert(wasm.list_in_record3(store, e.ListInRecord3("list_in_record3 input")).a == "list_in_record3 output")
assert(wasm.list_in_record4(store, e.ListInRecord4("input4")).a == "result4")
assert(wasm.list_in_record3_func(store, e.ListInRecord3("list_in_record3 input")).a == "list_in_record3 output")
assert(wasm.list_in_record4_func(store, e.ListInRecord4("input4")).a == "result4")

wasm.list_in_variant1(store, "foo", e.Err("bar"), 'baz')
assert(wasm.list_in_variant2(store) == "list_in_variant2")
assert(wasm.list_in_variant3(store, "input3") == "output3")
assert(wasm.list_in_variant2_func(store) == "list_in_variant2")
assert(wasm.list_in_variant3_func(store, "input3") == "output3")

assert(isinstance(wasm.errno_result(store), e.Err))

Expand Down
24 changes: 12 additions & 12 deletions tests/runtime/flavorful/host.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,24 +8,24 @@ use imports::*;
pub struct MyImports;

impl Imports for MyImports {
fn list_in_record1(&mut self, ty: ListInRecord1<'_>) {
fn list_in_record1_func(&mut self, ty: ListInRecord1<'_>) {
assert_eq!(ty.a, "list_in_record1");
}

fn list_in_record2(&mut self) -> ListInRecord2 {
fn list_in_record2_func(&mut self) -> ListInRecord2 {
ListInRecord2 {
a: "list_in_record2".to_string(),
}
}

fn list_in_record3(&mut self, a: ListInRecord3Param<'_>) -> ListInRecord3Result {
fn list_in_record3_func(&mut self, a: ListInRecord3Param<'_>) -> ListInRecord3Result {
assert_eq!(a.a, "list_in_record3 input");
ListInRecord3Result {
a: "list_in_record3 output".to_string(),
}
}

fn list_in_record4(&mut self, a: ListInAliasParam<'_>) -> ListInAliasResult {
fn list_in_record4_func(&mut self, a: ListInAliasParam<'_>) -> ListInAliasResult {
assert_eq!(a.a, "input4");
ListInRecord4Result {
a: "result4".to_string(),
Expand All @@ -46,11 +46,11 @@ impl Imports for MyImports {
}
}

fn list_in_variant2(&mut self) -> Option<String> {
fn list_in_variant2_func(&mut self) -> Option<String> {
Some("list_in_variant2".to_string())
}

fn list_in_variant3(&mut self, a: ListInVariant3Param<'_>) -> Option<String> {
fn list_in_variant3_func(&mut self, a: ListInVariant3Param<'_>) -> Option<String> {
assert_eq!(a.unwrap(), "input3");
Some("output3".to_string())
}
Expand Down Expand Up @@ -104,17 +104,17 @@ fn run(wasm: &str) -> Result<()> {

exports.test_imports(&mut store)?;

exports.list_in_record1(
exports.list_in_record1_func(
&mut store,
ListInRecord1 {
a: "list_in_record1",
},
)?;
assert_eq!(exports.list_in_record2(&mut store)?.a, "list_in_record2");
assert_eq!(exports.list_in_record2_func(&mut store)?.a, "list_in_record2");

assert_eq!(
exports
.list_in_record3(
.list_in_record3_func(
&mut store,
ListInRecord3Param {
a: "list_in_record3 input"
Expand All @@ -126,7 +126,7 @@ fn run(wasm: &str) -> Result<()> {

assert_eq!(
exports
.list_in_record4(&mut store, ListInAliasParam { a: "input4" })?
.list_in_record4_func(&mut store, ListInAliasParam { a: "input4" })?
.a,
"result4"
);
Expand All @@ -138,11 +138,11 @@ fn run(wasm: &str) -> Result<()> {
ListInVariant1V3::String("baz"),
)?;
assert_eq!(
exports.list_in_variant2(&mut store)?,
exports.list_in_variant2_func(&mut store)?,
Some("list_in_variant2".to_string())
);
assert_eq!(
exports.list_in_variant3(&mut store, Some("input3"))?,
exports.list_in_variant3_func(&mut store, Some("input3"))?,
Some("output3".to_string())
);

Expand Down
24 changes: 12 additions & 12 deletions tests/runtime/flavorful/host.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,13 @@ import * as assert from 'assert';
async function run() {
const importObj = {};
const imports: Imports = {
listInRecord1(x) {},
listInRecord2() { return { a: 'list_in_record2' }; },
listInRecord3(x) {
listInRecord1Func(x) {},
listInRecord2Func() { return { a: 'list_in_record2' }; },
listInRecord3Func(x) {
assert.strictEqual(x.a, 'list_in_record3 input');
return { a: 'list_in_record3 output' };
},
listInRecord4(x) {
listInRecord4Func(x) {
assert.strictEqual(x.a, 'input4');
return { a: 'result4' };
},
Expand All @@ -23,8 +23,8 @@ async function run() {
assert.deepStrictEqual(b, { tag: 'err', val: 'bar' });
assert.deepStrictEqual(c, { tag: 0, val: 'baz' });
},
listInVariant2() { return 'list_in_variant2'; },
listInVariant3(x) {
listInVariant2Func() { return 'list_in_variant2'; },
listInVariant3Func(x) {
assert.strictEqual(x, 'input3');
return 'output3';
},
Expand Down Expand Up @@ -57,23 +57,23 @@ async function run() {
instance = wasm.instance;

wasm.testImports();
wasm.listInRecord1({ a: "list_in_record1" });
assert.deepStrictEqual(wasm.listInRecord2(), { a: "list_in_record2" });
wasm.listInRecord1Func({ a: "list_in_record1" });
assert.deepStrictEqual(wasm.listInRecord2Func(), { a: "list_in_record2" });

assert.deepStrictEqual(
wasm.listInRecord3({ a: "list_in_record3 input" }),
wasm.listInRecord3Func({ a: "list_in_record3 input" }),
{ a: "list_in_record3 output" },
);

assert.deepStrictEqual(
wasm.listInRecord4({ a: "input4" }),
wasm.listInRecord4Func({ a: "input4" }),
{ a: "result4" },
);

wasm.listInVariant1("foo", { tag: 'err', val: 'bar' }, { tag: 0, val: 'baz' });

assert.deepStrictEqual(wasm.listInVariant2(), "list_in_variant2");
assert.deepStrictEqual(wasm.listInVariant3("input3"), "output3");
assert.deepStrictEqual(wasm.listInVariant2Func(), "list_in_variant2");
assert.deepStrictEqual(wasm.listInVariant3Func("input3"), "output3");

assert.deepStrictEqual(wasm.errnoResult().tag, 'err');

Expand Down
12 changes: 6 additions & 6 deletions tests/runtime/flavorful/imports.wit
Original file line number Diff line number Diff line change
Expand Up @@ -4,21 +4,21 @@ record list-in-record3 { a: string }
record list-in-record4 { a: string }
type list-in-alias = list-in-record4

list-in-record1: func(a: list-in-record1)
list-in-record2: func() -> list-in-record2
list-in-record3: func(a: list-in-record3) -> list-in-record3
list-in-record4: func(a: list-in-alias) -> list-in-alias
list-in-record1-func: func(a: list-in-record1)
list-in-record2-func: func() -> list-in-record2
list-in-record3-func: func(a: list-in-record3) -> list-in-record3
list-in-record4-func: func(a: list-in-alias) -> list-in-alias

type list-in-variant1-v1 = option<string>
type list-in-variant1-v2 = expected<unit, string>
union list-in-variant1-v3 { string, float32 }
list-in-variant1: func(a: list-in-variant1-v1, b: list-in-variant1-v2, c: list-in-variant1-v3)

type list-in-variant2 = option<string>
list-in-variant2: func() -> list-in-variant2
list-in-variant2-func: func() -> list-in-variant2

type list-in-variant3 = option<string>
list-in-variant3: func(a: list-in-variant3) -> list-in-variant3
list-in-variant3-func: func(a: list-in-variant3) -> list-in-variant3

enum my-errno { success, a, b }
errno-result: func() -> expected<unit, my-errno>
Expand Down
Loading