diff --git a/docs/language-guide.md b/docs/language-guide.md
index c7c1345..ae1fea0 100644
--- a/docs/language-guide.md
+++ b/docs/language-guide.md
@@ -64,7 +64,7 @@ We document a lot so nothing feels hidden—but Rib is not heavyweight. For REPL
---
-Companion WIT — [`example.wit`](example.wit) exports `inventory` (records, enums, plain funcs) and `shopping` (a `cart` resource) from world `guide-demo`. Start with [§1](#1-instance-and-calling-exports); [§0](#0-example-wit-examplewit) spells out the WIT if you want it on the page, and [§9 *`match`*](#9-match-and-patterns) onward when you need more than export calls.
+Companion WIT — [`example.wit`](example.wit) exports `inventory` (records, enums, plain funcs) and `shopping` (a `cart` resource) from world `guide-demo`. Start with [§1](#1-instance-and-calling-exports); [§0](#0-example-wit-examplewit) spells out the WIT if you want it on the page, and [§9 *`match`*](#9-match-and-patterns) onward when you need more than export calls. [§16](#16-common-compilation-errors-examples) lists typical **compile-time** mistakes (arity, types, `match` coverage) with small broken snippets.
---
@@ -86,6 +86,7 @@ Companion WIT — [`example.wit`](example.wit) exports `inventory` (records, enu
13. [`option` and `result`](#13-option-and-result)
14. [String interpolation](#14-string-interpolation)
15. [Invoking resource methods](#15-invoking-resource-methods)
+16. [Common compilation errors (examples)](#16-common-compilation-errors-examples)
---
@@ -425,6 +426,8 @@ match home {
}
```
+
+
### 9.8 Compile-time errors (`match`, enums, calls)
Rib reports many mistakes **while compiling** Rib source (REPL line or script)—**before** your embedder invokes Wasm. A few common cases:
@@ -435,6 +438,8 @@ Rib reports many mistakes **while compiling** Rib source (REPL line or script)
**Calls** — Arguments are checked against the **`func`** signature. Example: **`validate-qty`** expects **`u32`**; passing a **string** or the wrong **record** shape to **`length`** is rejected **at compile time**, not as a failed Wasm call later.
+Short **copy-paste examples** of invalid programs (and what goes wrong) are in [§16](#16-common-compilation-errors-examples).
+
---
## 10. List comprehensions (`for` … `yield`)
@@ -537,6 +542,56 @@ shopping-cart.line-count()
---
+## 16. Common compilation errors (examples)
+
+Assume [`example.wit`](example.wit) is loaded and **`let my-instance = instance();`** already exists. The snippets below are **invalid** Rib: the compiler rejects them **before** any Wasm runs. Exact wording of diagnostics can change between versions; the point is **why** each program fails.
+
+### Wrong number of parameters
+
+From WIT, **`validate-qty`** takes **one** `u32`, and **`length`** takes **one** `point` record:
+
+```rust
+// Invalid — missing argument
+my-instance.validate-qty()
+
+// Invalid — too many arguments
+my-instance.length({ x: 1, y: 2 }, { x: 0, y: 0 })
+```
+
+Rib reports an **arity** / argument-count mismatch against the export’s signature.
+
+### Type mismatch
+
+**`validate-qty`** expects a **`u32`**, not a string; **`length`** expects a **`point`**, not a bare number:
+
+```rust
+// Invalid — string where u32 is required
+my-instance.validate-qty("100")
+
+// Invalid — s32 where a point record is required
+my-instance.length(42)
+```
+
+The compiler compares your expression types to the WIT parameter types and stops with a **type** error.
+
+### Non-exhaustive pattern match (`match`)
+
+`variant payment-info` has **three** cases (`card`, `wallet`, `failed`). This `match` is **incomplete**—there is no arm for **`failed`**, and no **`_`** fallback:
+
+```rust
+// Invalid — not all variant cases covered (`p` has type `payment-info`)
+match p {
+ card(_) => "c",
+ wallet => "w"
+}
+```
+
+Either add **`failed(_) => …`** or a catch-all **`_ => …`**. The same idea applies to **`enum order-stage`**: every **`draft` / `placed` / `shipped`** arm must be present, or you use **`_`**.
+
+Enum and variant **exhaustiveness** is checked at compile time (see also [§9.8](#sec-9-8)).
+
+---
+
## Quick reference card
| Topic | Syntax / reminder |
@@ -547,7 +602,7 @@ shopping-cart.line-count()
| Inference | fixed-point over WIT; REPL tab completion (§5) |
| If | `if c then a else b` |
| Match | `match e { pat => x, _ => y }` |
-| Compile-time | Exhaustive `match`, enum case names, call arity/types vs WIT (§9.8) |
+| Compile-time | Exhaustive `match`, enum case names, call arity/types vs WIT (§9.8, §16) |
| Call | `f(a, b)` or `recv.method(a)` |
| Instance | `let my-instance = instance();` then `my-instance.lookup-sku(7)` (name is yours) |
| For | `for x in xs { yield y; }` |
diff --git a/rib-lang/src/compiler/mod.rs b/rib-lang/src/compiler/mod.rs
index 9c9ca31..0127cef 100644
--- a/rib-lang/src/compiler/mod.rs
+++ b/rib-lang/src/compiler/mod.rs
@@ -173,14 +173,14 @@ impl RibCompilerConfig {
}
}
-pub trait GenerateWorkerName {
- fn generate_worker_name(&self) -> String;
+pub trait GenerateInstanceName {
+ fn generate_instance_name(&self) -> String;
}
pub struct DefaultWorkerNameGenerator;
-impl GenerateWorkerName for DefaultWorkerNameGenerator {
- fn generate_worker_name(&self) -> String {
+impl GenerateInstanceName for DefaultWorkerNameGenerator {
+ fn generate_instance_name(&self) -> String {
let uuid = uuid::Uuid::new_v4();
format!("instance-{uuid}")
}
diff --git a/rib-lang/src/function_name.rs b/rib-lang/src/function_name.rs
index 9dd7f13..ba9e600 100644
--- a/rib-lang/src/function_name.rs
+++ b/rib-lang/src/function_name.rs
@@ -384,6 +384,55 @@ impl ParsedFunctionName {
function: self.function.clone(),
}
}
+
+ /// Segments for resolving a WebAssembly component export via nested instance names, then the
+ /// function export name.
+ ///
+ /// Packaged WIT paths like `component:pkg/iface.{fn}` identify the interface in metadata, but
+ /// component worlds usually export the interface **instance at the root** as `iface`, not as a
+ /// nested export named `component:pkg`. Runtimes should walk `Component::get_export_index` with
+ /// these segments (for example `["inventory", "lookup-sku"]`), not a path derived from splitting
+ /// the full [`ParsedFunctionSite::interface_name`] string.
+ pub fn wasm_component_export_path(&self) -> Vec {
+ let mut segments: Vec = match &self.site {
+ ParsedFunctionSite::Global => Vec::new(),
+ ParsedFunctionSite::Interface { name } => name
+ .split('/')
+ .filter(|s| !s.is_empty())
+ .map(str::to_string)
+ .collect(),
+ ParsedFunctionSite::PackagedInterface { interface, .. } => vec![interface.clone()],
+ };
+ let leaf = match &self.function {
+ ParsedFunctionReference::Function { function } => function.clone(),
+ _ => self.function.function_name(),
+ };
+ segments.push(leaf);
+ segments
+ }
+
+ /// Like [`wasm_component_export_path`](Self::wasm_component_export_path), but includes common
+ /// alternate spellings for the **last** segment (WIT kebab-case vs snake_case in lowered names).
+ pub fn wasm_component_export_path_candidates(&self) -> Vec> {
+ let primary = self.wasm_component_export_path();
+ let mut out: Vec> = Vec::new();
+ let mut push = |p: Vec| {
+ if !out.iter().any(|e| e == &p) {
+ out.push(p);
+ }
+ };
+ push(primary.clone());
+ if let Some(last) = primary.last() {
+ let snake = last.replace('-', "_");
+ if snake != *last {
+ let mut alt = primary.clone();
+ alt.pop();
+ alt.push(snake);
+ push(alt);
+ }
+ }
+ out
+ }
}
#[cfg(test)]
@@ -434,6 +483,19 @@ mod function_name_tests {
);
}
+ #[test]
+ fn wasm_component_export_path_packaged_matches_world_root() {
+ let parsed =
+ ParsedFunctionName::parse("component:rib-smoke/inventory.{lookup-sku}").expect("parse");
+ assert_eq!(
+ parsed.wasm_component_export_path(),
+ vec!["inventory", "lookup-sku"]
+ );
+ let cands = parsed.wasm_component_export_path_candidates();
+ assert!(cands.iter().any(|p| p == &vec!["inventory", "lookup-sku"]));
+ assert!(cands.iter().any(|p| p == &vec!["inventory", "lookup_sku"]));
+ }
+
#[test]
fn parse_function_name_in_exported_interface() {
let parsed = ParsedFunctionName::parse("ns:name/interface.{fn1}").expect("Parsing failed");
diff --git a/rib-lang/src/interpreter/eval.rs b/rib-lang/src/interpreter/eval.rs
index 67665c7..edc690e 100644
--- a/rib-lang/src/interpreter/eval.rs
+++ b/rib-lang/src/interpreter/eval.rs
@@ -1,5 +1,5 @@
use crate::{
- DefaultWorkerNameGenerator, Expr, GenerateWorkerName, RibCompilationError, RibCompiler,
+ DefaultWorkerNameGenerator, Expr, GenerateInstanceName, RibCompilationError, RibCompiler,
RibCompilerConfig, RibComponentFunctionInvoke, RibInput, RibResult, RibRuntimeError,
};
use std::sync::Arc;
@@ -8,7 +8,7 @@ pub struct RibEvalConfig {
compiler_config: RibCompilerConfig,
rib_input: RibInput,
function_invoke: Arc,
- generate_worker_name: Arc,
+ generate_instance_name: Arc,
}
impl RibEvalConfig {
@@ -16,13 +16,13 @@ impl RibEvalConfig {
compiler_config: RibCompilerConfig,
rib_input: RibInput,
function_invoke: Arc,
- generate_worker_name: Option>,
+ generate_worker_name: Option>,
) -> Self {
RibEvalConfig {
compiler_config,
rib_input,
function_invoke,
- generate_worker_name: generate_worker_name
+ generate_instance_name: generate_worker_name
.unwrap_or_else(|| Arc::new(DefaultWorkerNameGenerator)),
}
}
@@ -47,7 +47,7 @@ impl RibEvaluator {
compiled.byte_code,
self.config.rib_input,
self.config.function_invoke,
- Some(self.config.generate_worker_name.clone()),
+ Some(self.config.generate_instance_name.clone()),
)
.await?;
diff --git a/rib-lang/src/interpreter/interpreter_stack_value.rs b/rib-lang/src/interpreter/interpreter_stack_value.rs
index 8326f1e..bc9ee1d 100644
--- a/rib-lang/src/interpreter/interpreter_stack_value.rs
+++ b/rib-lang/src/interpreter/interpreter_stack_value.rs
@@ -210,9 +210,15 @@ impl fmt::Debug for RibInterpreterStackValue {
RibInterpreterStackValue::Unit => "unit".to_string(),
RibInterpreterStackValue::Val(value) => {
match &value.value {
- Value::Handle { uri, resource_id } => {
+ Value::Handle {
+ uri,
+ resource_id,
+ instance_name,
+ } => {
// wasm-wave don't support resource handles yet
- format!("handle:{{uri:{uri}, resource-id:{resource_id}}}")
+ format!(
+ "handle:{{uri:{uri}, resource-id:{resource_id}, instance:{instance_name}}}"
+ )
}
_ => value.to_string(),
diff --git a/rib-lang/src/interpreter/mod.rs b/rib-lang/src/interpreter/mod.rs
index 1988cc0..acb08c3 100644
--- a/rib-lang/src/interpreter/mod.rs
+++ b/rib-lang/src/interpreter/mod.rs
@@ -20,14 +20,14 @@ mod rib_interpreter;
mod rib_runtime_error;
mod stack;
-use crate::{DefaultWorkerNameGenerator, GenerateWorkerName, RibByteCode};
+use crate::{DefaultWorkerNameGenerator, GenerateInstanceName, RibByteCode};
use std::sync::Arc;
pub async fn interpret(
rib: RibByteCode,
rib_input: RibInput,
function_invoke: Arc,
- generate_worker_name: Option>,
+ generate_worker_name: Option>,
) -> Result {
let mut interpreter = Interpreter::new(
rib_input,
@@ -43,7 +43,7 @@ pub async fn interpret(
pub async fn interpret_pure(
rib: RibByteCode,
rib_input: RibInput,
- generate_worker_name: Option>,
+ generate_worker_name: Option>,
) -> Result {
let mut interpreter = Interpreter::pure(
rib_input,
diff --git a/rib-lang/src/interpreter/rib_interpreter.rs b/rib-lang/src/interpreter/rib_interpreter.rs
index 6b16ba7..fe63e14 100644
--- a/rib-lang/src/interpreter/rib_interpreter.rs
+++ b/rib-lang/src/interpreter/rib_interpreter.rs
@@ -6,7 +6,7 @@ use crate::interpreter::rib_runtime_error::{
};
use crate::interpreter::stack::InterpreterStack;
use crate::{
- internal_corrupted_state, DefaultWorkerNameGenerator, GenerateWorkerName, RibByteCode,
+ internal_corrupted_state, DefaultWorkerNameGenerator, GenerateInstanceName, RibByteCode,
RibComponentFunctionInvoke, RibIR, RibInput, RibResult,
};
use std::sync::Arc;
@@ -14,7 +14,7 @@ use std::sync::Arc;
pub struct Interpreter {
pub input: RibInput,
pub invoke: Arc,
- pub generate_worker_name: Arc,
+ pub generate_worker_name: Arc,
}
impl Default for Interpreter {
@@ -33,7 +33,7 @@ impl Interpreter {
pub fn new(
input: RibInput,
invoke: Arc,
- generate_worker_name: Arc,
+ generate_worker_name: Arc,
) -> Self {
Interpreter {
input: input.clone(),
@@ -46,7 +46,7 @@ impl Interpreter {
// All it needs is environment with the required variables to evaluate the Rib script
pub fn pure(
input: RibInput,
- generate_worker_name: Arc,
+ generate_worker_name: Arc,
) -> Self {
Interpreter {
input,
@@ -716,7 +716,7 @@ mod internal {
) -> RibInterpreterResult<()> {
match variable_id {
None => {
- let worker_name = interpreter.generate_worker_name.generate_worker_name();
+ let worker_name = interpreter.generate_worker_name.generate_instance_name();
interpreter_stack
.push_val(ValueAndType::new(Value::String(worker_name.clone()), str()));
@@ -741,7 +741,7 @@ mod internal {
}
None => {
- let worker_name = interpreter.generate_worker_name.generate_worker_name();
+ let worker_name = interpreter.generate_worker_name.generate_instance_name();
interpreter_stack
.push_val(ValueAndType::new(Value::String(worker_name.clone()), str()));
@@ -1302,8 +1302,10 @@ mod internal {
})?;
match &handle.value {
- Value::Handle { uri, .. } => {
- let worker_name = uri.rsplit_once('/').map(|(_, last)| last).unwrap_or(uri);
+ Value::Handle { .. } => {
+ let instance_name = handle.value.handle_instance_name().ok_or(
+ internal_corrupted_state!("resource handle missing instance name"),
+ )?;
final_args.push(handle.clone());
final_args.extend(parameter_values);
@@ -1312,7 +1314,7 @@ mod internal {
.invoke_worker_function_async(
component_info,
instruction_id,
- worker_name.to_string(),
+ instance_name,
function_name_cloned.clone(),
final_args,
expected_result_type.clone(),
@@ -4257,7 +4259,7 @@ mod tests {
use crate::{print_value_and_type, IntoValueAndType, Value, ValueAndType};
use crate::{
ComponentDependency, ComponentDependencyKey, DefaultWorkerNameGenerator,
- EvaluatedFnArgs, EvaluatedFqFn, EvaluatedWorkerName, GenerateWorkerName,
+ EvaluatedFnArgs, EvaluatedFqFn, EvaluatedWorkerName, GenerateInstanceName,
GetLiteralValue, InstructionId, RibComponentFunctionInvoke, RibFunctionInvokeResult,
RibInput,
};
@@ -4859,6 +4861,7 @@ mod tests {
Value::Handle {
uri,
resource_id: 0,
+ instance_name: worker_name.clone(),
},
handle(AnalysedResourceId(0), AnalysedResourceMode::Owned),
)
@@ -4908,14 +4911,13 @@ mod tests {
}
"golem:it/api.{[static]cart.create}" => {
- let uri = format!(
- "urn:worker:99738bab-a3bf-4a12-8830-b6fd783d1ef2/{}",
- worker_name.0
- );
+ let wn = worker_name.0.clone();
+ let uri = format!("urn:worker:99738bab-a3bf-4a12-8830-b6fd783d1ef2/{wn}");
let value = Value::Handle {
uri,
resource_id: 0,
+ instance_name: wn,
};
Ok(Some(ValueAndType::new(
@@ -4925,14 +4927,13 @@ mod tests {
}
"golem:it/api.{[static]cart.create-safe}" => {
- let uri = format!(
- "urn:worker:99738bab-a3bf-4a12-8830-b6fd783d1ef2/{}",
- worker_name.0
- );
+ let wn = worker_name.0.clone();
+ let uri = format!("urn:worker:99738bab-a3bf-4a12-8830-b6fd783d1ef2/{wn}");
let resource = Value::Handle {
uri,
resource_id: 0,
+ instance_name: wn,
};
let value = Value::Result(Ok(Some(Box::new(resource))));
@@ -5118,8 +5119,8 @@ mod tests {
pub(crate) struct StaticWorkerNameGenerator;
- impl GenerateWorkerName for StaticWorkerNameGenerator {
- fn generate_worker_name(&self) -> String {
+ impl GenerateInstanceName for StaticWorkerNameGenerator {
+ fn generate_instance_name(&self) -> String {
"test-worker".to_string()
}
}
diff --git a/rib-lang/src/value.rs b/rib-lang/src/value.rs
index d7c95c1..32b3483 100644
--- a/rib-lang/src/value.rs
+++ b/rib-lang/src/value.rs
@@ -24,12 +24,45 @@ pub enum Value {
Flags(Vec),
Option(Option>),
Result(Result