Skip to content

Composing labelled (implements) imports of one interface gives output wit-parser panics on #219

Open

Description

@sansmoraxz

When a composition has several (implements "I") imports of the same interface, CompositionGraph::encode gives them all one instance type:

(import "primary" (implements "test:test/logger") (instance (;0;) (type 0)))
(import "backup"  (implements "test:test/logger") (instance (;1;) (type 0)))

Several labels for one interface is the case labelled imports exist for (#200). This happens:

  • through implicit imports (an instantiated component's labelled imports left unsatisfied);
  • through explicit CompositionGraph::import nodes.

The output validates, and wasmtime runs it. But when the interface defines a type, wit-parser's decoder panics on it, and so does every tool built on that decoder:

  • wasm-tools component wit;
  • wit-bindgen;
  • jco. jco transpile aborts the same way (it uses wit-parser 0.259), so the composition can't be run in JavaScript.

The input component decodes fine: it's wit-component's shape, with one instance type per labelled import. Only wac's output has the shared type.

Reproduction

Cargo.toml:

[package]
name = "wac-implements-repro"
version = "0.1.0"
edition = "2024"

[dependencies]
wac-graph = "=0.12.0"
wit-parser = "=0.258.0"
wat = "1"

src/main.rs:

use wac_graph::{types::Package, CompositionGraph, EncodeOptions};

/// A component importing one interface, which defines a type, under two
/// labels: the shape wit-component produces (one instance type per import).
const CONSUMER: &str = r#"
(component
  (type (instance
    (type (enum "debug" "info"))
    (export "level" (type (eq 0)))
    (type (func (param "level" 1) (param "message" string)))
    (export "log" (func (type 2)))))
  (import "primary" (implements "test:test/logger") (instance (type 0)))
  (type (instance
    (type (enum "debug" "info"))
    (export "level" (type (eq 0)))
    (type (func (param "level" 1) (param "message" string)))
    (export "log" (func (type 2)))))
  (import "backup" (implements "test:test/logger") (instance (type 1)))
)
"#;

fn main() {
    let consumer = wat::parse_str(CONSUMER).unwrap();
    // The input decodes fine.
    wit_parser::decoding::decode(&consumer).expect("the consumer decodes");
    println!("consumer: decodes");

    let mut graph = CompositionGraph::new();
    let package = Package::from_bytes("test:consumer", None, consumer, graph.types_mut()).unwrap();
    let package = graph.register_package(package).unwrap();
    graph.instantiate(package);
    let composed = graph.encode(EncodeOptions::default()).unwrap();
    println!("composed: {} bytes, validated by wac", composed.len());

    // The composition does not: wit-parser panics.
    wit_parser::decoding::decode(&composed).expect("the composition decodes");
    println!("composition: decodes");
}

Output:

consumer: decodes
composed: 348 bytes, validated by wac

thread 'main' panicked at wit-parser-0.258.0/src/decoding.rs:797:29:
assertion failed: prev.is_none()

Expected

The composition decodes as the input does: each labelled import has its own instance type, as wit-component encodes it.

Related

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions