Skip to content

Commit 7133059

Browse files
authored
wasmparser(CM+GC): Allow declaring a core function type when lowering (#2166)
* wasmparser(CM+GC): Allow declaring a core function type when lowering When lowering a component function into a core function, allow the declaration of which core function type the lowered function (or lowered CM intrinsic) should have. Right now, this is effectively just double checking that the declared type matches the actual lowered type. After future PRs, this will allow declaring a Wasm GC-using function type, and lowering will adjust accordingly. This also lets the Wasm declare which of the "equivalent" function types it wants to lower to in the case that there are multiple rec groups containing the "same" function type, or in the face of function type subtyping. Again, subsequent PRs will start adding support for rec groups and subtyping and such to CM core types. * rename the canonical option `(core-type ...)` instead of `(core type ...)` to avoid parsing ambiguity
1 parent f32698b commit 7133059

File tree

12 files changed

+251
-28
lines changed

12 files changed

+251
-28
lines changed

crates/wasm-encoder/src/component/canonicals.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@ pub enum CanonicalOption {
2727
/// The function to use if the async lifting of a function should receive task/stream/future progress events
2828
/// using a callback.
2929
Callback(u32),
30+
/// The core function type to lower a component function into.
31+
CoreType(u32),
3032
}
3133

3234
impl Encode for CanonicalOption {
@@ -54,6 +56,10 @@ impl Encode for CanonicalOption {
5456
sink.push(0x07);
5557
idx.encode(sink);
5658
}
59+
Self::CoreType(idx) => {
60+
sink.push(0x08);
61+
idx.encode(sink);
62+
}
5763
}
5864
}
5965
}

crates/wasm-encoder/src/reencode/component.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1367,6 +1367,9 @@ pub mod component_utils {
13671367
wasmparser::CanonicalOption::Callback(u) => {
13681368
crate::component::CanonicalOption::Callback(reencoder.function_index(u))
13691369
}
1370+
wasmparser::CanonicalOption::CoreType(u) => {
1371+
crate::component::CanonicalOption::CoreType(reencoder.type_index(u))
1372+
}
13701373
}
13711374
}
13721375

crates/wasmparser/src/readers/component/canonicals.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@ pub enum CanonicalOption {
2828
/// The function to use if the async lifting of a function should receive task/stream/future progress events
2929
/// using a callback.
3030
Callback(u32),
31+
/// The core function type to lower this component function to.
32+
CoreType(u32),
3133
}
3234

3335
/// Represents a canonical function in a WebAssembly component.
@@ -401,6 +403,7 @@ impl<'a> FromReader<'a> for CanonicalOption {
401403
0x05 => CanonicalOption::PostReturn(reader.read_var_u32()?),
402404
0x06 => CanonicalOption::Async,
403405
0x07 => CanonicalOption::Callback(reader.read_var_u32()?),
406+
0x08 => CanonicalOption::CoreType(reader.read_var_u32()?),
404407
x => return reader.invalid_leading_byte(x, "canonical option"),
405408
})
406409
}

crates/wasmparser/src/validator/component.rs

Lines changed: 138 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -238,6 +238,7 @@ pub(crate) struct CanonicalOptions {
238238
pub(crate) realloc: Option<u32>,
239239
pub(crate) post_return: Option<u32>,
240240
pub(crate) concurrency: Concurrency,
241+
pub(crate) core_type: Option<CoreTypeId>,
241242
}
242243

243244
impl CanonicalOptions {
@@ -341,8 +342,50 @@ impl CanonicalOptions {
341342
}
342343
}
343344

345+
if self.core_type.is_some() {
346+
bail!(
347+
offset,
348+
"canonical option `core-type` is not allowed in `canon lift`"
349+
)
350+
}
351+
344352
Ok(self)
345353
}
354+
355+
pub(crate) fn check_core_type(
356+
&self,
357+
types: &mut TypeAlloc,
358+
actual: FuncType,
359+
offset: usize,
360+
) -> Result<CoreTypeId> {
361+
if let Some(declared_id) = self.core_type {
362+
let declared = types[declared_id].unwrap_func();
363+
364+
if actual.params() != declared.params() {
365+
bail!(
366+
offset,
367+
"declared core type has `{:?}` parameter types, but actual lowering has \
368+
`{:?}` parameter types",
369+
declared.params(),
370+
actual.params(),
371+
);
372+
}
373+
374+
if actual.results() != declared.results() {
375+
bail!(
376+
offset,
377+
"declared core type has `{:?}` result types, but actual lowering has \
378+
`{:?}` result types",
379+
declared.results(),
380+
actual.results(),
381+
);
382+
}
383+
384+
Ok(declared_id)
385+
} else {
386+
Ok(types.intern_func_type(actual, offset))
387+
}
388+
}
346389
}
347390

348391
impl ComponentState {
@@ -1185,6 +1228,7 @@ impl ComponentState {
11851228
let options = self.check_options(types, options, offset)?;
11861229
options.check_lift(types, self, core_ty, offset)?;
11871230
let func_ty = ty.lower(types, &options, Abi::Lift, offset)?;
1231+
debug_assert!(options.core_type.is_none());
11881232
let lowered_core_ty_id = types.intern_func_type(func_ty, offset);
11891233

11901234
if core_ty_id == lowered_core_ty_id {
@@ -1241,9 +1285,9 @@ impl ComponentState {
12411285
let options = self.check_options(types, options, offset)?;
12421286
options.check_lower(offset)?;
12431287
let func_ty = ty.lower(types, &options, Abi::Lower, offset)?;
1244-
let core_ty_id = types.intern_func_type(func_ty, offset);
1288+
let ty_id = options.check_core_type(types, func_ty, offset)?;
12451289

1246-
self.core_funcs.push(core_ty_id);
1290+
self.core_funcs.push(ty_id);
12471291
Ok(())
12481292
}
12491293

@@ -1349,9 +1393,9 @@ impl ComponentState {
13491393

13501394
let func_ty = func_ty.lower(types, &options, Abi::Lower, offset)?;
13511395
assert!(func_ty.results().is_empty());
1396+
let ty_id = options.check_core_type(types, func_ty, offset)?;
13521397

1353-
self.core_funcs
1354-
.push(types.intern_func_type(func_ty, offset));
1398+
self.core_funcs.push(ty_id);
13551399
Ok(())
13561400
}
13571401

@@ -1485,13 +1529,18 @@ impl ComponentState {
14851529
bail!(offset, "`stream.read` requires a stream type")
14861530
};
14871531

1488-
self.check_options(types, options, offset)?
1532+
let ty_id = self
1533+
.check_options(types, options, offset)?
14891534
.require_memory(offset)?
14901535
.require_realloc_if(offset, || elem_ty.is_some_and(|ty| ty.contains_ptr(types)))?
1491-
.check_lower(offset)?;
1536+
.check_lower(offset)?
1537+
.check_core_type(
1538+
types,
1539+
FuncType::new([ValType::I32; 3], [ValType::I32]),
1540+
offset,
1541+
)?;
14921542

1493-
self.core_funcs
1494-
.push(types.intern_func_type(FuncType::new([ValType::I32; 3], [ValType::I32]), offset));
1543+
self.core_funcs.push(ty_id);
14951544
Ok(())
14961545
}
14971546

@@ -1514,12 +1563,17 @@ impl ComponentState {
15141563
bail!(offset, "`stream.write` requires a stream type")
15151564
};
15161565

1517-
self.check_options(types, options, offset)?
1566+
let ty_id = self
1567+
.check_options(types, options, offset)?
15181568
.require_memory(offset)?
1519-
.check_lower(offset)?;
1569+
.check_lower(offset)?
1570+
.check_core_type(
1571+
types,
1572+
FuncType::new([ValType::I32; 3], [ValType::I32]),
1573+
offset,
1574+
)?;
15201575

1521-
self.core_funcs
1522-
.push(types.intern_func_type(FuncType::new([ValType::I32; 3], [ValType::I32]), offset));
1576+
self.core_funcs.push(ty_id);
15231577
Ok(())
15241578
}
15251579

@@ -1666,13 +1720,18 @@ impl ComponentState {
16661720
bail!(offset, "`future.read` requires a future type")
16671721
};
16681722

1669-
self.check_options(types, options, offset)?
1723+
let ty_id = self
1724+
.check_options(types, options, offset)?
16701725
.require_memory(offset)?
16711726
.require_realloc_if(offset, || elem_ty.is_some_and(|ty| ty.contains_ptr(types)))?
1672-
.check_lower(offset)?;
1727+
.check_lower(offset)?
1728+
.check_core_type(
1729+
types,
1730+
FuncType::new([ValType::I32; 2], [ValType::I32]),
1731+
offset,
1732+
)?;
16731733

1674-
self.core_funcs
1675-
.push(types.intern_func_type(FuncType::new([ValType::I32; 2], [ValType::I32]), offset));
1734+
self.core_funcs.push(ty_id);
16761735
Ok(())
16771736
}
16781737

@@ -1695,12 +1754,17 @@ impl ComponentState {
16951754
bail!(offset, "`future.write` requires a future type")
16961755
};
16971756

1698-
self.check_options(types, &options, offset)?
1757+
let ty_id = self
1758+
.check_options(types, &options, offset)?
16991759
.require_memory(offset)?
1700-
.check_lower(offset)?;
1760+
.check_lower(offset)?
1761+
.check_core_type(
1762+
types,
1763+
FuncType::new([ValType::I32; 2], [ValType::I32]),
1764+
offset,
1765+
)?;
17011766

1702-
self.core_funcs
1703-
.push(types.intern_func_type(FuncType::new([ValType::I32; 2], [ValType::I32]), offset));
1767+
self.core_funcs.push(ty_id);
17041768
Ok(())
17051769
}
17061770

@@ -1823,13 +1887,18 @@ impl ComponentState {
18231887
)
18241888
}
18251889

1826-
self.check_options(types, &options, offset)?
1890+
let ty_id = self
1891+
.check_options(types, &options, offset)?
18271892
.require_memory(offset)?
18281893
.require_sync(offset, "error-context.new")?
1829-
.check_lower(offset)?;
1894+
.check_lower(offset)?
1895+
.check_core_type(
1896+
types,
1897+
FuncType::new([ValType::I32; 2], [ValType::I32]),
1898+
offset,
1899+
)?;
18301900

1831-
self.core_funcs
1832-
.push(types.intern_func_type(FuncType::new([ValType::I32; 2], [ValType::I32]), offset));
1901+
self.core_funcs.push(ty_id);
18331902
Ok(())
18341903
}
18351904

@@ -1846,14 +1915,15 @@ impl ComponentState {
18461915
)
18471916
}
18481917

1849-
self.check_options(types, &options, offset)?
1918+
let ty_id = self
1919+
.check_options(types, &options, offset)?
18501920
.require_memory(offset)?
18511921
.require_realloc(offset)?
18521922
.require_sync(offset, "error-context.debug-message")?
1853-
.check_lower(offset)?;
1923+
.check_lower(offset)?
1924+
.check_core_type(types, FuncType::new([ValType::I32; 2], []), offset)?;
18541925

1855-
self.core_funcs
1856-
.push(types.intern_func_type(FuncType::new([ValType::I32; 2], []), offset));
1926+
self.core_funcs.push(ty_id);
18571927
Ok(())
18581928
}
18591929

@@ -2264,6 +2334,7 @@ impl ComponentState {
22642334
CanonicalOption::PostReturn(_) => "post-return",
22652335
CanonicalOption::Async => "async",
22662336
CanonicalOption::Callback(_) => "callback",
2337+
CanonicalOption::CoreType(_) => "core type",
22672338
}
22682339
}
22692340

@@ -2273,6 +2344,7 @@ impl ComponentState {
22732344
let mut post_return = None;
22742345
let mut is_async = false;
22752346
let mut callback = None;
2347+
let mut core_type = None;
22762348

22772349
for option in options {
22782350
match option {
@@ -2366,6 +2438,43 @@ impl ComponentState {
23662438
}
23672439
}
23682440
}
2441+
CanonicalOption::CoreType(idx) => {
2442+
core_type = match core_type {
2443+
None => {
2444+
if !self.features.cm_gc() {
2445+
bail!(
2446+
offset,
2447+
"canonical option `core type` requires the component model gc feature"
2448+
)
2449+
}
2450+
let ty = match self.core_type_at(*idx, offset)? {
2451+
ComponentCoreTypeId::Sub(ty) => ty,
2452+
ComponentCoreTypeId::Module(_) => return Err(BinaryReaderError::new(
2453+
"canonical option `core type` must reference a core function \
2454+
type",
2455+
offset,
2456+
)),
2457+
};
2458+
match &types[ty].composite_type.inner {
2459+
CompositeInnerType::Func(_) => {}
2460+
CompositeInnerType::Array(_) |
2461+
CompositeInnerType::Struct(_) |
2462+
CompositeInnerType::Cont(_) => return Err(BinaryReaderError::new(
2463+
"canonical option `core type` must reference a core function \
2464+
type",
2465+
offset,
2466+
)),
2467+
}
2468+
Some(ty)
2469+
}
2470+
Some(_) => {
2471+
return Err(BinaryReaderError::new(
2472+
"canonical option `core type` is specified more than once",
2473+
offset,
2474+
))
2475+
}
2476+
};
2477+
}
23692478
}
23702479
}
23712480

@@ -2385,6 +2494,7 @@ impl ComponentState {
23852494
realloc,
23862495
post_return,
23872496
concurrency,
2497+
core_type,
23882498
})
23892499
}
23902500

crates/wasmprinter/src/component.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -830,6 +830,11 @@ impl Printer<'_, '_> {
830830
self.print_idx(&state.core.func_names, *idx)?;
831831
self.end_group()?;
832832
}
833+
CanonicalOption::CoreType(idx) => {
834+
self.start_group("core-type ")?;
835+
self.print_idx(&state.core.type_names, *idx)?;
836+
self.end_group()?;
837+
}
833838
}
834839
}
835840
Ok(())

crates/wast/src/component/binary.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -975,6 +975,7 @@ impl From<&CanonOpt<'_>> for wasm_encoder::CanonicalOption {
975975
CanonOpt::PostReturn(f) => Self::PostReturn(f.idx.into()),
976976
CanonOpt::Async => Self::Async,
977977
CanonOpt::Callback(f) => Self::Callback(f.idx.into()),
978+
CanonOpt::CoreType(t) => Self::CoreType(t.idx.into()),
978979
}
979980
}
980981
}

crates/wast/src/component/func.rs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -949,6 +949,14 @@ pub enum CanonOpt<'a> {
949949
Async,
950950
/// Use the specified function to deliver async events to stackless coroutines.
951951
Callback(CoreItemRef<'a, kw::func>),
952+
/// Lower this component function into the specified core function type.
953+
CoreType(CoreItemRef<'a, kw::r#type>),
954+
}
955+
956+
impl Default for kw::r#type {
957+
fn default() -> Self {
958+
Self(Span::from_offset(0))
959+
}
952960
}
953961

954962
impl<'a> Parse<'a> for CanonOpt<'a> {
@@ -986,6 +994,11 @@ impl<'a> Parse<'a> for CanonOpt<'a> {
986994
Ok(CanonOpt::Callback(
987995
parser.parse::<IndexOrCoreRef<'_, _>>()?.0,
988996
))
997+
} else if l.peek::<kw::core_type>()? {
998+
parser.parse::<kw::core_type>()?;
999+
Ok(CanonOpt::CoreType(
1000+
parser.parse::<IndexOrCoreRef<'_, _>>()?.0,
1001+
))
9891002
} else {
9901003
Err(l.error())
9911004
}
@@ -1008,6 +1021,7 @@ impl Peek for CanonOpt<'_> {
10081021
|| kw::realloc::peek(next)?
10091022
|| kw::post_return::peek(next)?
10101023
|| kw::callback::peek(next)?
1024+
|| kw::core_type::peek(next)?
10111025
}
10121026
None => false,
10131027
})

0 commit comments

Comments
 (0)