Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fixes #966, #797 and other clean ups #987

Merged
merged 15 commits into from
Oct 3, 2024
413 changes: 347 additions & 66 deletions golem-rib/src/compiler/byte_code.rs

Large diffs are not rendered by default.

10 changes: 5 additions & 5 deletions golem-rib/src/compiler/desugar.rs
Original file line number Diff line number Diff line change
Expand Up @@ -261,19 +261,19 @@ mod internal {
) -> Option<IfThenBranch> {
match pred_expr_inferred_type {
InferredType::Record(field_and_types) => {
// Resolution body is a list of expressions which grows (may be with some let bindings)
// Resolution body is a list of expressions which grows (maybe with some let bindings)
// as we recursively iterate over the bind patterns
// where bind patterns are {name: x, age: _, address : _ } in the case of `match record { {name: x, age: _, address : _ } ) =>`
// These will exist prior to the original resolution of a successful tuple match.
// These will exist prior to the original resolution of a successful record match.
let mut resolution_body = vec![];

// The conditions keep growing as we recursively iterate over the bind patterns
// and there are multiple conditions (if condition) for each element in the tuple
// and there are multiple conditions (if condition) for each element in the record.
let mut conditions = vec![];

// We assume pred-expr can be queried by field using Expr::select_field and we pick each element in the bind pattern
// We assume pred-expr can be queried by field using Expr::select_field, and we pick each element in the bind pattern
// to get the corresponding expr in pred-expr and keep recursively iterating until the record is completed.
// However there is no resolution body for each of this iteration, so we use an empty expression
// However, there is no resolution body for each of this iteration, so we use an empty expression
// and finally push the original resolution body once we fully build the conditions.
for (field, arm_pattern) in bind_patterns.iter() {
let new_pred = Expr::select_field(pred_expr.clone(), field);
Expand Down
12 changes: 8 additions & 4 deletions golem-rib/src/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,10 @@ impl Expr {
matches!(self, Expr::Cond(_, _, _, _))
}

pub fn is_function_call(&self) -> bool {
matches!(self, Expr::Call(_, _, _))
}

pub fn is_match_expr(&self) -> bool {
matches!(self, Expr::PatternMatch(_, _, _))
}
Expand Down Expand Up @@ -419,10 +423,10 @@ impl Expr {
self.bind_types();
self.name_binding_pattern_match_variables();
self.name_binding_local_variables();
self.infer_function_types(function_type_registry)
.map_err(|x| vec![x])?;
self.infer_variants(function_type_registry);
self.infer_enums(function_type_registry);
self.infer_call_arguments_type(function_type_registry)
.map_err(|x| vec![x])?;
type_inference::type_inference_fix_point(Self::inference_scan, self)
.map_err(|x| vec![x])?;
self.unify_types()?;
Expand Down Expand Up @@ -452,11 +456,11 @@ impl Expr {
}

// At this point we simply update the types to the parameter type expressions and the call expression itself.
pub fn infer_function_types(
pub fn infer_call_arguments_type(
&mut self,
function_type_registry: &FunctionTypeRegistry,
) -> Result<(), String> {
type_inference::infer_function_types(self, function_type_registry)
type_inference::infer_call_arguments_type(self, function_type_registry)
}

pub fn push_types_down(&mut self) -> Result<(), String> {
Expand Down
18 changes: 14 additions & 4 deletions golem-rib/src/function_name.rs
Original file line number Diff line number Diff line change
Expand Up @@ -476,25 +476,35 @@ impl ParsedFunctionReference {
Self::RawResourceStaticMethod {
resource, method, ..
} => format!("[static]{resource}.{method}"),
ParsedFunctionReference::IndexedResourceConstructor { resource, .. } => {
Self::IndexedResourceConstructor { resource, .. } => {
format!("[constructor]{resource}")
}
ParsedFunctionReference::IndexedResourceMethod {
Self::IndexedResourceMethod {
resource, method, ..
} => {
format!("[method]{resource}.{method}")
}
ParsedFunctionReference::IndexedResourceStaticMethod {
Self::IndexedResourceStaticMethod {
resource, method, ..
} => {
format!("[static]{resource}.{method}")
}
ParsedFunctionReference::IndexedResourceDrop { resource, .. } => {
Self::IndexedResourceDrop { resource, .. } => {
format!("[drop]{resource}")
}
}
}

pub fn resource_method_name(&self) -> Option<String> {
match self {
Self::IndexedResourceStaticMethod { method, .. }
| Self::RawResourceMethod { method, .. }
| Self::RawResourceStaticMethod { method, .. }
| Self::IndexedResourceMethod { method, .. } => Some(method.clone()),
_ => None,
}
}

pub fn method_as_static(&self) -> Option<ParsedFunctionReference> {
match self {
Self::RawResourceMethod { resource, method } => Some(Self::RawResourceStaticMethod {
Expand Down
30 changes: 21 additions & 9 deletions golem-rib/src/inferred_type.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1117,6 +1117,25 @@ impl InferredType {
}
}
}

pub fn from_variant_cases(type_variant: &TypeVariant) -> InferredType {
let cases = type_variant
.cases
.iter()
.map(|name_type_pair| {
(
name_type_pair.name.clone(),
name_type_pair.typ.clone().map(|t| t.into()),
)
})
.collect();

InferredType::Variant(cases)
}

pub fn from_enum_cases(type_enum: &TypeEnum) -> InferredType {
InferredType::Enum(type_enum.cases.clone())
}
}

impl From<AnalysedType> for InferredType {
Expand Down Expand Up @@ -1146,22 +1165,15 @@ impl From<AnalysedType> for InferredType {
.collect(),
),
AnalysedType::Flags(vs) => InferredType::Flags(vs.names),
AnalysedType::Enum(vs) => InferredType::Enum(vs.cases),
AnalysedType::Enum(vs) => InferredType::from_enum_cases(&vs),
AnalysedType::Option(t) => InferredType::Option(Box::new((*t.inner).into())),
AnalysedType::Result(golem_wasm_ast::analysis::TypeResult { ok, err, .. }) => {
InferredType::Result {
ok: ok.map(|t| Box::new((*t).into())),
error: err.map(|t| Box::new((*t).into())),
}
}
AnalysedType::Variant(vs) => InferredType::Variant(
vs.cases
.into_iter()
.map(|name_type_pair| {
(name_type_pair.name, name_type_pair.typ.map(|t| t.into()))
})
.collect(),
),
AnalysedType::Variant(vs) => InferredType::from_variant_cases(&vs),
AnalysedType::Handle(golem_wasm_ast::analysis::TypeHandle { resource_id, mode }) => {
InferredType::Resource {
resource_id: resource_id.0,
Expand Down
11 changes: 11 additions & 0 deletions golem-rib/src/interpreter/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,3 +35,14 @@ pub async fn interpret(
let mut interpreter = Interpreter::new(rib_input, function_invoke);
interpreter.run(rib.clone()).await
}

// This function can be used for those the Rib Scripts
// where there are no side effecting function calls.
// It is recommended to use `interpret` over `interpret_pure` if you are unsure.
pub async fn interpret_pure(
rib: &RibByteCode,
rib_input: &HashMap<String, TypeAnnotatedValue>,
) -> Result<RibInterpreterResult, String> {
let mut interpreter = Interpreter::pure(rib_input.clone());
interpreter.run(rib.clone()).await
}
7 changes: 4 additions & 3 deletions golem-rib/src/interpreter/result.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,20 +47,21 @@ impl RibInterpreterResult {
pub fn get_bool(&self) -> Option<bool> {
match self {
RibInterpreterResult::Val(TypeAnnotatedValue::Bool(bool)) => Some(*bool),
_ => None,
RibInterpreterResult::Val(_) => None,
RibInterpreterResult::Unit => None,
}
}
pub fn get_val(&self) -> Option<TypeAnnotatedValue> {
match self {
RibInterpreterResult::Val(val) => Some(val.clone()),
_ => None,
RibInterpreterResult::Unit => None,
}
}

pub fn get_literal(&self) -> Option<LiteralValue> {
match self {
RibInterpreterResult::Val(val) => val.get_literal(),
_ => None,
RibInterpreterResult::Unit => None,
}
}

Expand Down
77 changes: 40 additions & 37 deletions golem-rib/src/interpreter/rib_interpreter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,9 @@ impl Interpreter {
}
}

pub fn from_input(env: HashMap<String, TypeAnnotatedValue>) -> Self {
// Interpreter that's not expected to call a side-effecting function call.
// All it needs is environment with the required variables to evaluate the Rib script
pub fn pure(env: HashMap<String, TypeAnnotatedValue>) -> Self {
Interpreter {
stack: InterpreterStack::new(),
env: InterpreterEnv::from_input(env),
Expand Down Expand Up @@ -140,8 +142,8 @@ impl Interpreter {
internal::run_create_function_name_instruction(site, function_type, self)?;
}

RibIR::InvokeFunction(arity, _) => {
internal::run_call_instruction(arity, self).await?;
RibIR::InvokeFunction(arg_size, _) => {
internal::run_call_instruction(arg_size, self).await?;
}

RibIR::PushVariant(variant_name, analysed_type) => {
Expand Down Expand Up @@ -335,7 +337,7 @@ mod internal {
.map(|interpreter_result| {
interpreter_result
.get_val()
.ok_or("Failed to get value from the stack".to_string())
.ok_or("Internal Error: Failed to construct list".to_string())
})
.collect::<Result<Vec<TypeAnnotatedValue>, String>>()?;

Expand Down Expand Up @@ -363,12 +365,13 @@ mod internal {
.pop_n(list_size)
.ok_or(format!("Expected {} value on the stack", list_size))?;

dbg!(last_list.clone());
let type_annotated_values = last_list
.iter()
.map(|interpreter_result| {
interpreter_result
.get_val()
.ok_or("Failed to get value from the stack".to_string())
.ok_or("Internal Error: Failed to construct tuple".to_string())
})
.collect::<Result<Vec<TypeAnnotatedValue>, String>>()?;

Expand Down Expand Up @@ -634,7 +637,7 @@ mod internal {
.map(|interpreter_result| {
interpreter_result
.get_val()
.ok_or("Failed to get value from the stack".to_string())
.ok_or("Internal Error: Failed to construct resource".to_string())
})
.collect::<Result<Vec<TypeAnnotatedValue>, String>>()?;

Expand Down Expand Up @@ -666,9 +669,9 @@ mod internal {
let type_anntoated_values = last_n_elements
.iter()
.map(|interpreter_result| {
interpreter_result
.get_val()
.ok_or("Failed to get value from the stack".to_string())
interpreter_result.get_val().ok_or(
"Internal Error: Failed to call indexed resource method".to_string(),
)
})
.collect::<Result<Vec<TypeAnnotatedValue>, String>>()?;

Expand All @@ -693,17 +696,17 @@ mod internal {
arg_size,
method,
} => {
let last_n_elements = interpreter
.stack
.pop_n(arg_size)
.ok_or("Failed to get values from the stack".to_string())?;
let last_n_elements = interpreter.stack.pop_n(arg_size).ok_or(
"Internal error: Failed to get arguments for static resource method"
.to_string(),
)?;

let type_anntoated_values = last_n_elements
.iter()
.map(|interpreter_result| {
interpreter_result
.get_val()
.ok_or("Failed to get value from the stack".to_string())
interpreter_result.get_val().ok_or(
"Internal error: Failed to call static resource method".to_string(),
)
})
.collect::<Result<Vec<TypeAnnotatedValue>, String>>()?;

Expand All @@ -724,25 +727,25 @@ mod internal {
.push_val(TypeAnnotatedValue::Str(parsed_function_name.to_string()));
}
FunctionReferenceType::IndexedResourceDrop { resource, arg_size } => {
let last_n_elements = interpreter
.stack
.pop_n(arg_size)
.ok_or("Failed to get values from the stack".to_string())?;
let last_n_elements = interpreter.stack.pop_n(arg_size).ok_or(
"Internal Error: Failed to get resource parameters for indexed resource drop"
.to_string(),
)?;

let type_anntoated_values = last_n_elements
let type_annotated_values = last_n_elements
.iter()
.map(|interpreter_result| {
interpreter_result
.get_val()
.ok_or("Failed to get value from the stack".to_string())
interpreter_result.get_val().ok_or(
"Internal Error: Failed to call indexed resource drop".to_string(),
)
})
.collect::<Result<Vec<TypeAnnotatedValue>, String>>()?;

let parsed_function_name = ParsedFunctionName {
site,
function: ParsedFunctionReference::IndexedResourceDrop {
resource,
resource_params: type_anntoated_values
resource_params: type_annotated_values
.iter()
.map(type_annotated_value_to_string)
.collect::<Result<Vec<String>, String>>()?,
Expand All @@ -758,27 +761,27 @@ mod internal {
Ok(())
}

// Separate variant
pub(crate) async fn run_call_instruction(
argument_size: usize,
arg_size: usize,
interpreter: &mut Interpreter,
) -> Result<(), String> {
let function_name = interpreter
.stack
.pop_str()
.ok_or("Failed to get a function name from the stack".to_string())?;
.ok_or("Internal Error: Failed to get a function name".to_string())?;

let last_n_elements = interpreter
.stack
.pop_n(argument_size)
.ok_or("Failed to get values from the stack".to_string())?;
.pop_n(arg_size)
.ok_or("Internal Error: Failed to get arguments for the function call".to_string())?;

let type_anntoated_values = last_n_elements
.iter()
.map(|interpreter_result| {
interpreter_result
.get_val()
.ok_or("Failed to get value from the stack".to_string())
interpreter_result.get_val().ok_or(format!(
"Internal Error: Failed to call function {}",
function_name
))
})
.collect::<Result<Vec<TypeAnnotatedValue>, String>>()?;

Expand Down Expand Up @@ -921,19 +924,19 @@ mod internal {
) -> Result<(), String> {
let last_n_elements = interpreter_stack
.pop_n(arg_size)
.ok_or("Failed to get values from the stack".to_string())?;
.ok_or("Internal Error: Failed to get arguments for concatenation".to_string())?;

let type_anntoated_values = last_n_elements
let type_annotated_values = last_n_elements
.iter()
.map(|interpreter_result| {
interpreter_result
.get_val()
.ok_or("Failed to get value from the stack".to_string())
.ok_or("Internal Error: Failed to execute concatenation".to_string())
})
.collect::<Result<Vec<TypeAnnotatedValue>, String>>()?;

let mut str = String::new();
for value in type_anntoated_values {
for value in type_annotated_values {
let result = value
.get_literal()
.ok_or("Expected a literal value".to_string())?
Expand Down
Loading