diff --git a/compiler/backend_inkwell/candy_runtime/candy_builtin.c b/compiler/backend_inkwell/candy_runtime/candy_builtin.c index 7202c4193..2124394b6 100644 --- a/compiler/backend_inkwell/candy_runtime/candy_builtin.c +++ b/compiler/backend_inkwell/candy_runtime/candy_builtin.c @@ -3,7 +3,7 @@ #include #include "candy_runtime.h" -const candy_value_t *candy_builtin_equals(candy_value_t *left, candy_value_t *right) +const candy_value_t *candy_builtin_equals(candy_value_t *left, candy_value_t *right, candy_value_t *responsible) { if (left ->type != right->type) @@ -22,7 +22,7 @@ const candy_value_t *candy_builtin_equals(candy_value_t *left, candy_value_t *ri } } -const candy_value_t *candy_builtin_if_else(candy_value_t *condition, candy_value_t *then, candy_value_t *otherwise) +const candy_value_t *candy_builtin_if_else(candy_value_t *condition, candy_value_t *then, candy_value_t *otherwise, candy_value_t *responsible) { candy_value_t *body = candy_tag_to_bool(condition) ? then : otherwise; candy_function function = (body->value).function.function; @@ -30,17 +30,17 @@ const candy_value_t *candy_builtin_if_else(candy_value_t *condition, candy_value return function(environment); } -candy_value_t *candy_builtin_int_add(candy_value_t *left, candy_value_t *right) +candy_value_t *candy_builtin_int_add(candy_value_t *left, candy_value_t *right, candy_value_t *responsible) { return make_candy_int(left->value.integer + right->value.integer); } -candy_value_t *candy_builtin_int_subtract(candy_value_t *left, candy_value_t *right) +candy_value_t *candy_builtin_int_subtract(candy_value_t *left, candy_value_t *right, candy_value_t *responsible) { return make_candy_int(left->value.integer - right->value.integer); } -candy_value_t *candy_builtin_int_bit_length(candy_value_t *value) +candy_value_t *candy_builtin_int_bit_length(candy_value_t *value, candy_value_t *responsible) { int64_t int_value = value->value.integer; int is_negative = int_value < 0; @@ -57,22 +57,22 @@ candy_value_t *candy_builtin_int_bit_length(candy_value_t *value) return make_candy_int(shifts + is_negative); } -candy_value_t *candy_builtin_int_bitwise_and(candy_value_t *left, candy_value_t *right) +candy_value_t *candy_builtin_int_bitwise_and(candy_value_t *left, candy_value_t *right, candy_value_t *responsible) { return make_candy_int(left->value.integer & right->value.integer); } -candy_value_t *candy_builtin_int_bitwise_or(candy_value_t *left, candy_value_t *right) +candy_value_t *candy_builtin_int_bitwise_or(candy_value_t *left, candy_value_t *right, candy_value_t *responsible) { return make_candy_int(left->value.integer | right->value.integer); } -candy_value_t *candy_builtin_int_bitwise_xor(candy_value_t *left, candy_value_t *right) +candy_value_t *candy_builtin_int_bitwise_xor(candy_value_t *left, candy_value_t *right, candy_value_t *responsible) { return make_candy_int(left->value.integer ^ right->value.integer); } -const candy_value_t *candy_builtin_int_compare_to(candy_value_t *left, candy_value_t *right) +const candy_value_t *candy_builtin_int_compare_to(candy_value_t *left, candy_value_t *right, candy_value_t *responsible) { int64_t left_value = left->value.integer; int64_t right_value = right->value.integer; @@ -90,7 +90,7 @@ const candy_value_t *candy_builtin_int_compare_to(candy_value_t *left, candy_val } } -candy_value_t *candy_builtin_list_length(const candy_value_t *list) +candy_value_t *candy_builtin_list_length(const candy_value_t *list, candy_value_t *responsible) { size_t index = 0; while (list->value.list[index] != NULL) @@ -100,34 +100,34 @@ candy_value_t *candy_builtin_list_length(const candy_value_t *list) return make_candy_int(index); } -const candy_value_t *candy_builtin_print(candy_value_t *value) +const candy_value_t *candy_builtin_print(candy_value_t *value, candy_value_t *responsible) { print_candy_value(value); printf("\n"); return &__internal_nothing; } -candy_value_t *candy_builtin_struct_get(candy_value_t *structure, candy_value_t *key) +candy_value_t *candy_builtin_struct_get(candy_value_t *structure, candy_value_t *key, candy_value_t *responsible) { size_t index = 0; - while (!candy_tag_to_bool(candy_builtin_equals(structure->value.structure.keys[index], key))) + while (!candy_tag_to_bool(candy_builtin_equals(structure->value.structure.keys[index], key, responsible))) { index++; } return structure->value.structure.values[index]; } -candy_value_t *candy_builtin_struct_get_keys(candy_value_t *structure) +candy_value_t *candy_builtin_struct_get_keys(candy_value_t *structure, candy_value_t *responsible) { return make_candy_list(structure->value.structure.keys); } -const candy_value_t *candy_builtin_struct_has_key(candy_value_t *structure, candy_value_t *key) +const candy_value_t *candy_builtin_struct_has_key(candy_value_t *structure, candy_value_t *key, candy_value_t *responsible) { size_t index = 0; while (structure->value.structure.keys[index] != NULL) { - if (candy_tag_to_bool(candy_builtin_equals(structure->value.structure.keys[index], key))) + if (candy_tag_to_bool(candy_builtin_equals(structure->value.structure.keys[index], key, responsible))) { return &__internal_true; } @@ -135,20 +135,20 @@ const candy_value_t *candy_builtin_struct_has_key(candy_value_t *structure, cand return &__internal_false; } -const candy_value_t *candy_builtin_tag_has_value(candy_value_t *tag) +const candy_value_t *candy_builtin_tag_has_value(candy_value_t *tag, candy_value_t *responsible) { return to_candy_bool(tag->value.tag.value != NULL); } -candy_value_t *candy_builtin_tag_get_value(candy_value_t *tag) +candy_value_t *candy_builtin_tag_get_value(candy_value_t *tag, candy_value_t *responsible) { return tag->value.tag.value; } -candy_value_t *candy_builtin_tag_without_value(candy_value_t *tag) +candy_value_t *candy_builtin_tag_without_value(candy_value_t *tag, candy_value_t *responsible) { return make_candy_tag(tag->value.tag.text, NULL); } -const candy_value_t *candy_builtin_type_of(candy_value_t *value) +const candy_value_t *candy_builtin_type_of(candy_value_t *value, candy_value_t *responsible) { switch (value->type) { diff --git a/compiler/backend_inkwell/candy_runtime/candy_builtin.h b/compiler/backend_inkwell/candy_runtime/candy_builtin.h index edf876af3..ceab45772 100644 --- a/compiler/backend_inkwell/candy_runtime/candy_builtin.h +++ b/compiler/backend_inkwell/candy_runtime/candy_builtin.h @@ -1,20 +1,20 @@ #include "candy_runtime.h" -const candy_value_t *candy_builtin_equals(candy_value_t *left, candy_value_t *right); -const candy_value_t *candy_builtin_if_else(candy_value_t *condition, candy_value_t *then, candy_value_t *otherwise); -candy_value_t *candy_builtin_int_add(candy_value_t *left, candy_value_t *right); -candy_value_t *candy_builtin_int_subtract(candy_value_t *left, candy_value_t *right); -candy_value_t *candy_builtin_int_bit_length(candy_value_t *value); -candy_value_t *candy_builtin_int_bitwise_and(candy_value_t *left, candy_value_t *right); -candy_value_t *candy_builtin_int_bitwise_or(candy_value_t *left, candy_value_t *right); -candy_value_t *candy_builtin_int_bitwise_xor(candy_value_t *left, candy_value_t *right); -const candy_value_t *candy_builtin_int_compare_to(candy_value_t *left, candy_value_t *right); -candy_value_t *candy_builtin_list_length(const candy_value_t *list); -const candy_value_t *candy_builtin_print(candy_value_t *value); -candy_value_t *candy_builtin_struct_get(candy_value_t *structure, candy_value_t *key); -candy_value_t *candy_builtin_struct_get_keys(candy_value_t *structure); -const candy_value_t *candy_builtin_tag_has_value(candy_value_t *tag); -candy_value_t *candy_builtin_tag_get_value(candy_value_t *tag); -candy_value_t *candy_builtin_tag_without_value(candy_value_t *tag); -const candy_value_t *candy_builtin_struct_has_key(candy_value_t *structure, candy_value_t *key); -const candy_value_t *candy_builtin_type_of(candy_value_t *value); +const candy_value_t *candy_builtin_equals(candy_value_t *left, candy_value_t *right, candy_value_t *responsible); +const candy_value_t *candy_builtin_if_else(candy_value_t *condition, candy_value_t *then, candy_value_t *otherwise, candy_value_t *responsible); +candy_value_t *candy_builtin_int_add(candy_value_t *left, candy_value_t *right, candy_value_t *responsible); +candy_value_t *candy_builtin_int_subtract(candy_value_t *left, candy_value_t *right, candy_value_t *responsible); +candy_value_t *candy_builtin_int_bit_length(candy_value_t *value, candy_value_t *responsible); +candy_value_t *candy_builtin_int_bitwise_and(candy_value_t *left, candy_value_t *right, candy_value_t *responsible); +candy_value_t *candy_builtin_int_bitwise_or(candy_value_t *left, candy_value_t *right, candy_value_t *responsible); +candy_value_t *candy_builtin_int_bitwise_xor(candy_value_t *left, candy_value_t *right, candy_value_t *responsible); +const candy_value_t *candy_builtin_int_compare_to(candy_value_t *left, candy_value_t *right, candy_value_t *responsible); +candy_value_t *candy_builtin_list_length(const candy_value_t *list, candy_value_t *responsible); +const candy_value_t *candy_builtin_print(candy_value_t *value, candy_value_t *responsible); +candy_value_t *candy_builtin_struct_get(candy_value_t *structure, candy_value_t *key, candy_value_t *responsible); +candy_value_t *candy_builtin_struct_get_keys(candy_value_t *structure, candy_value_t *responsible); +const candy_value_t *candy_builtin_tag_has_value(candy_value_t *tag, candy_value_t *responsible); +candy_value_t *candy_builtin_tag_get_value(candy_value_t *tag, candy_value_t *responsible); +candy_value_t *candy_builtin_tag_without_value(candy_value_t *tag, candy_value_t *responsible); +const candy_value_t *candy_builtin_struct_has_key(candy_value_t *structure, candy_value_t *key, candy_value_t *responsible); +const candy_value_t *candy_builtin_type_of(candy_value_t *value, candy_value_t *responsible); diff --git a/compiler/backend_inkwell/candy_runtime/candy_runtime.c b/compiler/backend_inkwell/candy_runtime/candy_runtime.c index 84e4c1202..5bfd0f9ae 100644 --- a/compiler/backend_inkwell/candy_runtime/candy_runtime.c +++ b/compiler/backend_inkwell/candy_runtime/candy_runtime.c @@ -64,7 +64,9 @@ void print_candy_value(const candy_value_t *value) break; case CANDY_TYPE_LIST: printf("("); - candy_value_t *length = candy_builtin_list_length(value); + // TODO: The responsible parameter is not by builtin_list_length + // anyways, so we just pass anything. + candy_value_t *length = candy_builtin_list_length(value, value); size_t list_length = length->value.integer; free_candy_value(length); size_t index = 0; @@ -189,11 +191,14 @@ void *get_candy_function_environment(candy_value_t *function) return function->value.function.environment; } -void candy_panic(const candy_value_t *reason) +void candy_panic(const candy_value_t *reason, const candy_value_t* responsible) { printf("The program panicked for the following reason: \n"); print_candy_value(reason); printf("\n"); + printf("The code at "); + print_candy_value(responsible); + printf(" is responsible."); exit(-1); } diff --git a/compiler/backend_inkwell/src/lib.rs b/compiler/backend_inkwell/src/lib.rs index 934acd7bd..3d7bf533a 100644 --- a/compiler/backend_inkwell/src/lib.rs +++ b/compiler/backend_inkwell/src/lib.rs @@ -559,9 +559,7 @@ impl<'ctx> CodeGen<'ctx> { original_hirs, parameters, body, - responsible_parameter, } => { - self.unrepresented_ids.insert(*responsible_parameter); let name = original_hirs .iter() .sorted() @@ -652,9 +650,7 @@ impl<'ctx> CodeGen<'ctx> { candy_frontend::mir::Expression::Call { function, arguments, - responsible, } => { - self.unrepresented_ids.insert(*responsible); let mut args: Vec<_> = arguments .iter() .map(|arg| self.get_value_with_id(function_ctx, arg).unwrap().into()) @@ -730,12 +726,17 @@ impl<'ctx> CodeGen<'ctx> { } } candy_frontend::mir::Expression::UseModule { .. } => unreachable!(), - candy_frontend::mir::Expression::Panic { reason, .. } => { + candy_frontend::mir::Expression::Panic { + reason, + responsible, + } => { let panic_fn = self.module.get_function("candy_panic").unwrap(); let reason = self.get_value_with_id(function_ctx, reason).unwrap(); + let responsible = self.get_value_with_id(function_ctx, responsible).unwrap(); - self.builder.build_call(panic_fn, &[reason.into()], ""); + self.builder + .build_call(panic_fn, &[reason.into(), responsible.into()], ""); self.builder.build_unreachable(); diff --git a/compiler/cli/src/run.rs b/compiler/cli/src/run.rs index 3887a29b5..4f1bf5593 100644 --- a/compiler/cli/src/run.rs +++ b/compiler/cli/src/run.rs @@ -93,9 +93,9 @@ pub(crate) fn run(options: Options) -> ProgramResult { let vm = Vm::for_function( byte_code.clone(), &mut heap, - main, - &[environment_object], platform, + main, + &[environment_object, platform.into()], StackTracer::default(), ); let VmFinished { result, .. } = vm.run_forever_with_environment(&mut heap, &mut environment); diff --git a/compiler/frontend/src/builtin_functions.rs b/compiler/frontend/src/builtin_functions.rs index 410c49b5a..653006943 100644 --- a/compiler/frontend/src/builtin_functions.rs +++ b/compiler/frontend/src/builtin_functions.rs @@ -129,7 +129,8 @@ impl BuiltinFunction { #[must_use] pub const fn num_parameters(&self) -> usize { - match self { + // Responsibility parameter. + 1 + match self { Self::Equals => 2, Self::FunctionRun => 1, Self::GetArgumentCount => 1, diff --git a/compiler/frontend/src/hir_to_mir.rs b/compiler/frontend/src/hir_to_mir.rs index 33307413a..695e0b11a 100644 --- a/compiler/frontend/src/hir_to_mir.rs +++ b/compiler/frontend/src/hir_to_mir.rs @@ -94,7 +94,7 @@ fn generate_needs_function(body: &mut BodyBuilder) -> Id { let true_tag = body.push_bool(true); let false_tag = body.push_bool(false); let is_condition_true = - body.push_call(builtin_equals, vec![condition, true_tag], needs_code); + body.push_call(builtin_equals, vec![condition, true_tag, needs_code]); let is_condition_bool = body.push_if_else( &needs_id.child("isConditionTrue"), is_condition_true, @@ -102,7 +102,7 @@ fn generate_needs_function(body: &mut BodyBuilder) -> Id { body.push_reference(true_tag); }, |body| { - body.push_call(builtin_equals, vec![condition, false_tag], needs_code); + body.push_call(builtin_equals, vec![condition, false_tag, needs_code]); }, needs_code, ); @@ -122,12 +122,11 @@ fn generate_needs_function(body: &mut BodyBuilder) -> Id { // Make sure the reason is a text. let builtin_type_of = body.push_builtin(BuiltinFunction::TypeOf); - let type_of_reason = body.push_call(builtin_type_of, vec![reason], responsible_for_call); + let type_of_reason = body.push_call(builtin_type_of, vec![reason, responsible_for_call]); let text_tag = body.push_tag("Text".to_string(), None); let is_reason_text = body.push_call( builtin_equals, - vec![type_of_reason, text_tag], - responsible_for_call, + vec![type_of_reason, text_tag, responsible_for_call], ); body.push_if_else( &needs_id.child("isReasonText"), @@ -270,8 +269,7 @@ impl<'a> LoweringContext<'a> { let one = body.push_int(1.into()); let reason = body.push_call( list_get_function, - vec![pattern_result, one], - responsible, + vec![pattern_result, one, responsible], ); body.push_panic(reason, responsible); @@ -290,7 +288,7 @@ impl<'a> LoweringContext<'a> { let list_get = body.push_builtin(BuiltinFunction::ListGet); let index = body.push_int((identifier_id.0 + 1).into()); let responsible = body.push_hir_id(hir_id.clone()); - body.push_call(list_get, vec![result, index], responsible) + body.push_call(list_get, vec![result, index, responsible]) } } hir::Expression::Match { expression, cases } => { @@ -346,22 +344,21 @@ impl<'a> LoweringContext<'a> { function, arguments, } => { - let responsible = body.push_hir_id(hir_id.clone()); + let call_site = body.push_hir_id(hir_id.clone()); let arguments = arguments .iter() .map(|argument| self.mapping[argument]) + .chain([call_site]) .collect_vec(); if self.tracing.calls.is_enabled() { - let hir_call = body.push_hir_id(hir_id.clone()); body.push(Expression::TraceCallStarts { - hir_call, + hir_call: call_site, function: self.mapping[function], arguments: arguments.clone(), - responsible, }); } - let call = body.push_call(self.mapping[function], arguments, responsible); + let call = body.push_call(self.mapping[function], arguments); if self.tracing.calls.is_enabled() { body.push(Expression::TraceCallEnds { return_value: call }); body.push_reference(call) @@ -389,8 +386,8 @@ impl<'a> LoweringContext<'a> { self.mapping[condition], self.mapping[reason], responsible_for_needs, + responsible, ], - responsible, ) } hir::Expression::Error { errors, .. } => { @@ -474,8 +471,7 @@ impl<'a> LoweringContext<'a> { let one = body.push_int(1.into()); let reason = body.push_call( list_get_function, - vec![pattern_result, one], - responsible_for_match, + vec![pattern_result, one, responsible_for_match], ); no_match_reasons.push(reason); @@ -492,8 +488,12 @@ impl<'a> LoweringContext<'a> { }); body.push_call( builtin_if_else, - vec![is_match, then_function, else_function], - responsible_for_match, + vec![ + is_match, + then_function, + else_function, + responsible_for_match, + ], ) } } @@ -544,18 +544,17 @@ impl PatternLoweringContext { body.push_builtin(BuiltinFunction::TagWithoutValue); let actual_symbol = body.push_call( builtin_tag_without_value, - vec![expression], - self.responsible, + vec![expression, self.responsible], ); let expected_symbol = body.push_tag(symbol.clone(), None); self.compile_equals(body, expected_symbol, actual_symbol, |body| { let builtin_tag_has_value = body.push_builtin(BuiltinFunction::TagHasValue); - let actual_has_value = body.push_call(builtin_tag_has_value, vec![expression], self.responsible); + let actual_has_value = body.push_call(builtin_tag_has_value, vec![expression, self.responsible]); let expected_has_value = body.push_bool(value.is_some()); self.compile_equals(body, expected_has_value, actual_has_value, |body| { if let Some(value) = value { let builtin_tag_get_value = body.push_builtin(BuiltinFunction::TagGetValue); - let actual_value = body.push_call(builtin_tag_get_value, vec![expression], self.responsible); + let actual_value = body.push_call(builtin_tag_get_value, vec![expression, self.responsible]); self.compile(body, actual_value, value); } else { self.push_match(body, vec![]); @@ -567,9 +566,9 @@ impl PatternLoweringContext { ] } else { let builtin_tag_get_value = body.push_builtin(BuiltinFunction::TagGetValue); - let actual_value = body.push_call(builtin_tag_get_value, vec![expression], self.responsible); + let actual_value = body.push_call(builtin_tag_get_value, vec![expression, self.responsible]); let builtin_to_debug_text = body.push_builtin(BuiltinFunction::ToDebugText); - let actual_value_text = body.push_call(builtin_to_debug_text, vec![actual_value], self.responsible); + let actual_value_text = body.push_call(builtin_to_debug_text, vec![actual_value, self.responsible]); vec![ body.push_text("Expected tag to not have a value, but it has one: `".to_string()), actual_value_text, @@ -598,7 +597,7 @@ impl PatternLoweringContext { let expected = body.push_int(list.len().into()); let builtin_list_length = body.push_builtin(BuiltinFunction::ListLength); let actual_length = - body.push_call(builtin_list_length, vec![expression], self.responsible); + body.push_call(builtin_list_length, vec![expression, self.responsible]); self.compile_equals( body, expected, @@ -614,8 +613,7 @@ impl PatternLoweringContext { let index = body.push_int(index.into()); let item = body.push_call( builtin_list_get, - vec![expression, index], - self.responsible, + vec![expression, index, self.responsible], ); let result = self.compile(body, item, item_pattern); (result, item_pattern.captured_identifier_count()) @@ -651,8 +649,7 @@ impl PatternLoweringContext { let key = self.compile_pattern_to_key_expression(body, key_pattern); let has_key = body.push_call( builtin_struct_has_key, - vec![expression, key], - self.responsible, + vec![expression, key, self.responsible,], ); let result = body.push_if_else( @@ -661,8 +658,7 @@ impl PatternLoweringContext { |body| { let value = body.push_call( builtin_struct_get, - vec![expression, key], - self.responsible, + vec![expression, key, self.responsible], ); self.compile(body, value, value_pattern); }, @@ -672,14 +668,12 @@ impl PatternLoweringContext { let key_as_text = body.push_call( to_debug_text, - vec![key], - self.responsible, + vec![key, self.responsible], ); let struct_as_text = body.push_call( to_debug_text, - vec![expression], - self.responsible, + vec![expression, self.responsible], ); let reason_parts = vec![ @@ -736,7 +730,7 @@ impl PatternLoweringContext { let Some(index) = index else { return body.push_reference(nothing); }; let index = body.push_int((1 + index).into()); - body.push_call(list_get_function, vec![result, index], self.responsible) + body.push_call(list_get_function, vec![result, index, self.responsible]) }) .collect(); self.push_match(body, captured_identifiers); @@ -791,7 +785,7 @@ impl PatternLoweringContext { { let expected_type = body.push_tag(expected_type, None); let builtin_type_of = body.push_builtin(BuiltinFunction::TypeOf); - let type_ = body.push_call(builtin_type_of, vec![expression], self.responsible); + let type_ = body.push_call(builtin_type_of, vec![expression, self.responsible]); self.compile_equals( body, expected_type, @@ -822,7 +816,7 @@ impl PatternLoweringContext { E: FnOnce(&mut BodyBuilder, Id, Id) -> Vec, { let builtin_equals = body.push_builtin(BuiltinFunction::Equals); - let equals = body.push_call(builtin_equals, vec![expected, actual], self.responsible); + let equals = body.push_call(builtin_equals, vec![expected, actual, self.responsible]); body.push_if_else( &self.hir_id.child("equals"), @@ -831,8 +825,8 @@ impl PatternLoweringContext { |body| { let to_debug_text = body.push_builtin(BuiltinFunction::ToDebugText); let expected_as_text = - body.push_call(to_debug_text, vec![expected], self.responsible); - let actual_as_text = body.push_call(to_debug_text, vec![actual], self.responsible); + body.push_call(to_debug_text, vec![expected, self.responsible]); + let actual_as_text = body.push_call(to_debug_text, vec![actual, self.responsible]); let reason_parts = reason_factory(body, expected_as_text, actual_as_text); let reason = self.push_text_concatenate(body, reason_parts); self.push_no_match(body, reason); @@ -896,8 +890,7 @@ impl PatternLoweringContext { let index = body.push_int((index + 1).into()); let captured_identifier = body.push_call( list_get_function, - vec![return_value, index], - self.responsible, + vec![return_value, index, self.responsible], ); captured_identifiers.push(captured_identifier); } @@ -919,8 +912,7 @@ impl PatternLoweringContext { .reduce(|left, right| { body.push_call( builtin_text_concatenate, - vec![left, right], - self.responsible, + vec![left, right, self.responsible], ) }) .unwrap() @@ -951,16 +943,14 @@ impl BodyBuilder { let zero = self.push_int(0.into()); let match_or_no_match_tag = self.push_call( list_get_function, - vec![match_or_no_match, zero], - responsible, + vec![match_or_no_match, zero, responsible], ); let equals_function = self.push_builtin(BuiltinFunction::Equals); let match_tag = self.push_match_tag(); self.push_call( equals_function, - vec![match_or_no_match_tag, match_tag], - responsible, + vec![match_or_no_match_tag, match_tag, responsible], ) } diff --git a/compiler/frontend/src/lir/body.rs b/compiler/frontend/src/lir/body.rs index 446a8ea25..4cb8f47ed 100644 --- a/compiler/frontend/src/lir/body.rs +++ b/compiler/frontend/src/lir/body.rs @@ -73,24 +73,7 @@ impl ToRichIr for Bodies { builder.push_definition(parameter_id, range); } - let responsible_parameter_id = body.responsible_parameter_id(); - builder.push( - if body.parameter_count == 0 { - " (responsible " - } else { - " (+ responsible " - }, - None, - EnumSet::empty(), - ); - let range = builder.push( - responsible_parameter_id.to_string(), - TokenType::Parameter, - EnumSet::empty(), - ); - builder.push_definition(responsible_parameter_id, range); - - builder.push(") =", None, EnumSet::empty()); + builder.push(" =", None, EnumSet::empty()); builder.indent(); builder.push_newline(); @@ -106,7 +89,6 @@ impl ToRichIr for Bodies { /// /// - captured variables /// - parameters -/// - responsible parameter /// - locals #[derive(Clone, Debug, Eq, PartialEq)] pub struct Body { @@ -151,17 +133,12 @@ impl Body { (self.captured_count..self.captured_count + self.parameter_count).map(Id::from_usize) } - #[must_use] - pub fn responsible_parameter_id(&self) -> Id { - Id::from_usize(self.captured_count + self.parameter_count) - } - #[must_use] pub fn expressions(&self) -> &[Expression] { &self.expressions } pub fn ids_and_expressions(&self) -> impl Iterator { - let offset = self.captured_count + self.parameter_count + 1; + let offset = self.captured_count + self.parameter_count; self.expressions .iter() .enumerate() diff --git a/compiler/frontend/src/lir/expression.rs b/compiler/frontend/src/lir/expression.rs index 64d161ced..01ec9c186 100644 --- a/compiler/frontend/src/lir/expression.rs +++ b/compiler/frontend/src/lir/expression.rs @@ -45,7 +45,6 @@ pub enum Expression { Call { function: Id, arguments: Vec, - responsible: Id, }, Panic { @@ -57,7 +56,6 @@ pub enum Expression { hir_call: Id, function: Id, arguments: Vec, - responsible: Id, }, TraceCallEnds { @@ -113,13 +111,11 @@ impl Expression { Self::Call { function, arguments, - responsible, } => { *function = replacer(*function); for argument in arguments { *argument = replacer(*argument); } - *responsible = replacer(*responsible); } Self::Panic { reason, @@ -132,14 +128,12 @@ impl Expression { hir_call, function, arguments, - responsible, } => { *hir_call = replacer(*hir_call); *function = replacer(*function); for argument in arguments { *argument = replacer(*argument); } - *responsible = replacer(*responsible); } Self::TraceCallEnds { return_value } => { *return_value = replacer(*return_value); @@ -220,7 +214,6 @@ impl ToRichIr for Expression { Self::Call { function, arguments, - responsible, } => { builder.push("call ", None, EnumSet::empty()); function.build_rich_ir(builder); @@ -230,9 +223,6 @@ impl ToRichIr for Expression { } else { builder.push_children(arguments, " "); } - builder.push(" (", None, EnumSet::empty()); - responsible.build_rich_ir(builder); - builder.push(" is responsible)", None, EnumSet::empty()); } Self::Panic { reason, @@ -240,23 +230,20 @@ impl ToRichIr for Expression { } => { builder.push("panicking because ", None, EnumSet::empty()); reason.build_rich_ir(builder); - builder.push(" (", None, EnumSet::empty()); + builder.push(", ", None, EnumSet::empty()); responsible.build_rich_ir(builder); - builder.push(" is at fault)", None, EnumSet::empty()); + builder.push(" is at fault", None, EnumSet::empty()); } Self::TraceCallStarts { hir_call, function, arguments, - responsible, } => { builder.push("trace: start of call of ", None, EnumSet::empty()); function.build_rich_ir(builder); builder.push(" with ", None, EnumSet::empty()); builder.push_children(arguments, " "); - builder.push(" (", None, EnumSet::empty()); - responsible.build_rich_ir(builder); - builder.push(" is responsible, code is at ", None, EnumSet::empty()); + builder.push(" (code is at ", None, EnumSet::empty()); hir_call.build_rich_ir(builder); builder.push(")", None, EnumSet::empty()); } diff --git a/compiler/frontend/src/lir_optimize.rs b/compiler/frontend/src/lir_optimize.rs index b213e8e1a..132522b95 100644 --- a/compiler/frontend/src/lir_optimize.rs +++ b/compiler/frontend/src/lir_optimize.rs @@ -38,11 +38,7 @@ impl Body { let mut id_mapping = FxHashMap::default(); let mut reference_count_adjustments = self.get_combined_reference_count_adjustments(); - for id in self - .captured_ids() - .chain(self.parameter_ids()) - .chain([self.responsible_parameter_id()]) - { + for id in self.captured_ids().chain(self.parameter_ids()) { new_body.maybe_dup(&mut reference_count_adjustments, id, &id_mapping); } diff --git a/compiler/frontend/src/mir/body.rs b/compiler/frontend/src/mir/body.rs index 3854a7d96..9dc0fcd7e 100644 --- a/compiler/frontend/src/mir/body.rs +++ b/compiler/frontend/src/mir/body.rs @@ -242,21 +242,16 @@ impl Body { visitor: &mut dyn FnMut(Id, &mut Expression, &VisibleExpressions, bool), ) { if let Expression::Function { - parameters, - responsible_parameter, - body, - .. + parameters, body, .. } = expression { for parameter in &*parameters { visible.insert(*parameter, Expression::Parameter); } - visible.insert(*responsible_parameter, Expression::Parameter); body.visit_with_visible_rec(visible, visitor); for parameter in &*parameters { visible.remove(*parameter); } - visible.remove(*responsible_parameter); } visitor(id, expression, visible, is_returned); @@ -308,8 +303,11 @@ impl FunctionBodyBuilder { let (id_generator, body) = self.body_builder.finish(); let function = Expression::Function { original_hirs: vec![self.hir_id].into_iter().collect(), - parameters: self.parameters, - responsible_parameter: self.responsible_parameter, + parameters: self + .parameters + .into_iter() + .chain([self.responsible_parameter]) + .collect(), body, }; (id_generator, function) @@ -383,11 +381,10 @@ impl BodyBuilder { self.push(function) } - pub fn push_call(&mut self, function: Id, arguments: Vec, responsible: Id) -> Id { + pub fn push_call(&mut self, function: Id, arguments: Vec) -> Id { self.push(Expression::Call { function, arguments, - responsible, }) } pub fn push_if_else( @@ -407,8 +404,7 @@ impl BodyBuilder { let else_function = self.push_function(hir_id.child("else"), |body, _| else_builder(body)); self.push_call( builtin_if_else, - vec![condition, then_function, else_function], - responsible, + vec![condition, then_function, else_function, responsible], ) } diff --git a/compiler/frontend/src/mir/expression.rs b/compiler/frontend/src/mir/expression.rs index b4a4888b3..68f403b5f 100644 --- a/compiler/frontend/src/mir/expression.rs +++ b/compiler/frontend/src/mir/expression.rs @@ -53,7 +53,6 @@ pub enum Expression { Function { original_hirs: FxHashSet, parameters: Vec, - responsible_parameter: Id, body: Body, }, @@ -65,7 +64,6 @@ pub enum Expression { Call { function: Id, arguments: Vec, - responsible: Id, }, UseModule { @@ -86,7 +84,6 @@ pub enum Expression { hir_call: Id, function: Id, arguments: Vec, - responsible: Id, }, TraceCallEnds { @@ -228,7 +225,6 @@ impl Hash for Expression { Self::Function { original_hirs, parameters, - responsible_parameter, body, } => { { @@ -241,18 +237,15 @@ impl Hash for Expression { hash.hash(state); } parameters.hash(state); - responsible_parameter.hash(state); body.hash(state); } Self::Parameter => {} Self::Call { function, arguments, - responsible, } => { function.hash(state); arguments.hash(state); - responsible.hash(state); } Self::UseModule { current_module, @@ -274,12 +267,10 @@ impl Hash for Expression { hir_call, function, arguments, - responsible, } => { hir_call.hash(state); function.hash(state); arguments.hash(state); - responsible.hash(state); } Self::TraceCallEnds { return_value } => return_value.hash(state), Self::TraceExpressionEvaluated { @@ -353,7 +344,6 @@ impl ToRichIr for Expression { // assignment. original_hirs: _, parameters, - responsible_parameter, body, } => { builder.push("{ ", None, EnumSet::empty()); @@ -369,22 +359,9 @@ impl ToRichIr for Expression { }, " ", ); - builder.push( - if parameters.is_empty() { - "(responsible " - } else { - " (+ responsible " - }, - None, - EnumSet::empty(), - ); - let range = builder.push( - responsible_parameter.to_string(), - TokenType::Parameter, - EnumSet::empty(), - ); - builder.push_definition(*responsible_parameter, range); - builder.push(") ->", None, EnumSet::empty()); + if !parameters.is_empty() { + builder.push(" ->", None, EnumSet::empty()); + } builder.push_foldable(|builder| { builder.indent(); builder.push_newline(); @@ -400,7 +377,6 @@ impl ToRichIr for Expression { Self::Call { function, arguments, - responsible, } => { builder.push("call ", None, EnumSet::empty()); function.build_rich_ir(builder); @@ -410,9 +386,6 @@ impl ToRichIr for Expression { } else { builder.push_children(arguments, " "); } - builder.push(" (", None, EnumSet::empty()); - responsible.build_rich_ir(builder); - builder.push(" is responsible)", None, EnumSet::empty()); } Self::UseModule { current_module, @@ -441,15 +414,12 @@ impl ToRichIr for Expression { hir_call, function, arguments, - responsible, } => { builder.push("trace: start of call of ", None, EnumSet::empty()); function.build_rich_ir(builder); builder.push(" with ", None, EnumSet::empty()); builder.push_children(arguments, " "); - builder.push(" (", None, EnumSet::empty()); - responsible.build_rich_ir(builder); - builder.push(" is responsible, code is at ", None, EnumSet::empty()); + builder.push(" (code is at ", None, EnumSet::empty()); hir_call.build_rich_ir(builder); builder.push(")", None, EnumSet::empty()); } diff --git a/compiler/frontend/src/mir_optimize/common_subtree_elimination.rs b/compiler/frontend/src/mir_optimize/common_subtree_elimination.rs index bfb92c183..0431f8eab 100644 --- a/compiler/frontend/src/mir_optimize/common_subtree_elimination.rs +++ b/compiler/frontend/src/mir_optimize/common_subtree_elimination.rs @@ -172,11 +172,10 @@ impl NormalizationState { self.register_defined_id(*id); } } - fn register_function_ids(&mut self, parameters: &[Id], responsible_parameter: Id) { + fn register_function_ids(&mut self, parameters: &[Id]) { for parameter in parameters { self.register_defined_id(*parameter); } - self.register_defined_id(responsible_parameter); } fn register_defined_id(&mut self, id: Id) { let replacement = self.id_generator.generate(); @@ -390,29 +389,21 @@ impl NormalizedComparison for Expression { Self::Function { original_hirs: _, parameters: self_parameters, - responsible_parameter: self_responsible_parameter, body: self_body, }, Self::Function { original_hirs: _, parameters: other_parameters, - responsible_parameter: other_responsible_parameter, body: other_body, }, ) => { - self_normalization - .register_function_ids(self_parameters, *self_responsible_parameter); - other_normalization - .register_function_ids(other_parameters, *other_responsible_parameter); + self_normalization.register_function_ids(self_parameters); + other_normalization.register_function_ids(other_parameters); self_parameters.equals_normalized( self_normalization, other_parameters, other_normalization, - ) && self_responsible_parameter.equals_normalized( - self_normalization, - other_responsible_parameter, - other_normalization, ) && self_body.equals_normalized( self_normalization, other_body, @@ -424,12 +415,10 @@ impl NormalizedComparison for Expression { Self::Call { function: self_function, arguments: self_arguments, - responsible: self_responsible, }, Self::Call { function: other_function, arguments: other_arguments, - responsible: other_responsible, }, ) => { self_function.equals_normalized( @@ -440,10 +429,6 @@ impl NormalizedComparison for Expression { self_normalization, other_arguments, other_normalization, - ) && self_responsible.equals_normalized( - self_normalization, - other_responsible, - other_normalization, ) } ( @@ -494,13 +479,11 @@ impl NormalizedComparison for Expression { hir_call: self_hir_call, function: self_function, arguments: self_arguments, - responsible: self_responsible, }, Self::TraceCallStarts { hir_call: other_hir_call, function: other_function, arguments: other_arguments, - responsible: other_responsible, }, ) => { self_hir_call.equals_normalized( @@ -515,10 +498,6 @@ impl NormalizedComparison for Expression { self_normalization, other_arguments, other_normalization, - ) && self_responsible.equals_normalized( - self_normalization, - other_responsible, - other_normalization, ) } ( @@ -594,24 +573,20 @@ impl NormalizedComparison for Expression { Self::Function { original_hirs: _, parameters, - responsible_parameter, body, } => { - normalization.register_function_ids(parameters, *responsible_parameter); + normalization.register_function_ids(parameters); parameters.hash_normalized(normalization, state); - responsible_parameter.hash_normalized(normalization, state); body.hash_normalized(normalization, state); } Self::Parameter => {} Self::Call { function, arguments, - responsible, } => { function.hash_normalized(normalization, state); arguments.hash_normalized(normalization, state); - responsible.hash_normalized(normalization, state); } Self::UseModule { current_module, @@ -633,12 +608,10 @@ impl NormalizedComparison for Expression { hir_call, function, arguments, - responsible, } => { hir_call.hash_normalized(normalization, state); function.hash_normalized(normalization, state); arguments.hash_normalized(normalization, state); - responsible.hash_normalized(normalization, state); } Self::TraceCallEnds { return_value } => { return_value.hash_normalized(normalization, state); diff --git a/compiler/frontend/src/mir_optimize/constant_folding.rs b/compiler/frontend/src/mir_optimize/constant_folding.rs index 4d2dfc3e0..f38584f14 100644 --- a/compiler/frontend/src/mir_optimize/constant_folding.rs +++ b/compiler/frontend/src/mir_optimize/constant_folding.rs @@ -54,7 +54,6 @@ pub fn fold_constants(context: &mut Context, expression: &mut CurrentExpression) let Expression::Call { function, arguments, - responsible, } = &**expression else { return; @@ -64,7 +63,7 @@ pub fn fold_constants(context: &mut Context, expression: &mut CurrentExpression) Expression::Tag { symbol, value: None, - } if arguments.len() == 1 => { + } if arguments.len() == 2 => { **expression = Expression::Tag { symbol: symbol.clone(), value: Some(arguments[0]), @@ -72,12 +71,10 @@ pub fn fold_constants(context: &mut Context, expression: &mut CurrentExpression) } Expression::Builtin(builtin) => { let arguments = arguments.clone(); - let responsible = *responsible; let Some(result) = run_builtin( &mut *expression, *builtin, &arguments, - responsible, context.visible, context.id_generator, context.pureness, @@ -98,7 +95,6 @@ fn run_builtin( expression: &mut CurrentExpression, builtin: BuiltinFunction, arguments: &[Id], - responsible: Id, visible: &VisibleExpressions, id_generator: &mut IdGenerator, pureness: &PurenessInsights, @@ -108,6 +104,7 @@ fn run_builtin( builtin.num_parameters(), "Wrong number of arguments for calling {builtin}", ); + let (&responsible, arguments) = arguments.split_last().unwrap(); let result = match builtin { BuiltinFunction::Equals => { @@ -120,8 +117,7 @@ fn run_builtin( }; Expression::Call { function: *function, - arguments: vec![], - responsible, + arguments: vec![responsible], } } BuiltinFunction::GetArgumentCount => { @@ -131,7 +127,8 @@ fn run_builtin( let Expression::Function { parameters, .. } = visible.get(*function) else { return None; }; - parameters.len().into() + // The responsibility parameter doesn't count. + (parameters.len() - 1).into() } BuiltinFunction::IfElse => { let [condition, then, else_] = arguments else { @@ -140,8 +137,7 @@ fn run_builtin( let condition = visible.get(*condition).try_into().ok()?; Expression::Call { function: if condition { *then } else { *else_ }, - arguments: vec![], - responsible, + arguments: vec![responsible], } } BuiltinFunction::IntAdd => { diff --git a/compiler/frontend/src/mir_optimize/inlining.rs b/compiler/frontend/src/mir_optimize/inlining.rs index bd7268926..7f435778e 100644 --- a/compiler/frontend/src/mir_optimize/inlining.rs +++ b/compiler/frontend/src/mir_optimize/inlining.rs @@ -88,7 +88,6 @@ impl Context<'_> { let Expression::Call { function, arguments, - responsible: responsible_argument, } = &**expression else { // Expression is not a call. @@ -102,7 +101,6 @@ impl Context<'_> { let Expression::Function { original_hirs: _, parameters, - responsible_parameter, body, } = self.visible.get(*function) else { @@ -118,7 +116,6 @@ impl Context<'_> { .iter() .zip(arguments.iter()) .map(|(parameter, argument)| (*parameter, *argument)) - .chain([(*responsible_parameter, *responsible_argument)]) .chain( body.defined_ids() .into_iter() diff --git a/compiler/frontend/src/mir_optimize/mod.rs b/compiler/frontend/src/mir_optimize/mod.rs index 38db7b930..1e71d589a 100644 --- a/compiler/frontend/src/mir_optimize/mod.rs +++ b/compiler/frontend/src/mir_optimize/mod.rs @@ -166,26 +166,19 @@ impl Context<'_> { fn optimize_expression(&mut self, expression: &mut CurrentExpression) { 'outer: loop { if let Expression::Function { - parameters, - responsible_parameter, - body, - .. + parameters, body, .. } = &mut **expression { for parameter in &*parameters { self.visible.insert(*parameter, Expression::Parameter); } - self.visible - .insert(*responsible_parameter, Expression::Parameter); - self.pureness - .enter_function(parameters, *responsible_parameter); + self.pureness.enter_function(parameters); self.optimize_body(body); for parameter in &*parameters { self.visible.remove(*parameter); } - self.visible.remove(*responsible_parameter); } loop { @@ -212,6 +205,19 @@ impl Context<'_> { } } } + + // TODO: If this is a call to the `needs` function with `True` as the + // first argument, optimize it away. This is not correct – calling + // `needs True 3 4` should panic instead. But we figured this is + // temporarily fine until we have data flow. + if let Expression::Call { function, arguments, .. } = &**expression + && let Expression::Function { original_hirs, .. } = self.visible.get(*function) + && original_hirs.contains(&hir::Id::needs()) + && arguments.len() == 4 + && let Expression::Tag { symbol, value: None } = self.visible.get(arguments[0]) + && symbol == "True" { + **expression = Expression::nothing(); + } } } diff --git a/compiler/frontend/src/mir_optimize/pure.rs b/compiler/frontend/src/mir_optimize/pure.rs index f2d5b8116..75db26b59 100644 --- a/compiler/frontend/src/mir_optimize/pure.rs +++ b/compiler/frontend/src/mir_optimize/pure.rs @@ -60,20 +60,12 @@ impl PurenessInsights { // TODO: Don't optimize lifted constants again. // Then, we can also add asserts here about not visiting them twice. } - pub(super) fn enter_function(&mut self, parameters: &[Id], responsible_parameter: Id) { + pub(super) fn enter_function(&mut self, parameters: &[Id]) { self.definition_pureness .extend(parameters.iter().map(|id| (*id, true))); - let _existing = self.definition_pureness.insert(responsible_parameter, true); - // TODO: Handle lifted constants properly. - // assert!(existing.is_none()); self.definition_constness .extend(parameters.iter().map(|id| (*id, false))); - let _existing = self - .definition_constness - .insert(responsible_parameter, false); - // TODO: Handle lifted constants properly. - // assert!(existing.is_none()); } pub(super) fn on_normalize_ids(&mut self, mapping: &FxHashMap) { fn update(values: &mut FxHashMap, mapping: &FxHashMap) { diff --git a/compiler/frontend/src/mir_optimize/utils.rs b/compiler/frontend/src/mir_optimize/utils.rs index d408af400..189608bbe 100644 --- a/compiler/frontend/src/mir_optimize/utils.rs +++ b/compiler/frontend/src/mir_optimize/utils.rs @@ -15,14 +15,10 @@ impl Expression { } fn collect_defined_ids(&self, defined: &mut Vec) { if let Self::Function { - parameters, - responsible_parameter, - body, - .. + parameters, body, .. } = self { defined.extend(parameters); - defined.push(*responsible_parameter); body.collect_defined_ids(defined); } } @@ -79,11 +75,9 @@ impl Expression { Self::Call { function, arguments, - responsible, } => { referenced.insert(*function); referenced.extend(arguments); - referenced.insert(*responsible); } Self::UseModule { current_module: _, @@ -104,12 +98,10 @@ impl Expression { hir_call, function, arguments, - responsible, } => { referenced.insert(*hir_call); referenced.insert(*function); referenced.extend(arguments); - referenced.insert(*responsible); } Self::TraceCallEnds { return_value } => { referenced.insert(*return_value); @@ -223,26 +215,22 @@ impl Expression { Self::Function { original_hirs: _, parameters, - responsible_parameter, body, } => { for parameter in parameters { replacer(parameter); } - replacer(responsible_parameter); body.replace_id_references(replacer); } Self::Parameter => {} Self::Call { function, arguments, - responsible, } => { replacer(function); for argument in arguments { replacer(argument); } - replacer(responsible); } Self::UseModule { current_module: _, @@ -263,14 +251,12 @@ impl Expression { hir_call, function, arguments, - responsible, } => { replacer(hir_call); replacer(function); for argument in arguments { replacer(argument); } - replacer(responsible); } Self::TraceCallEnds { return_value } => { replacer(return_value); @@ -308,13 +294,11 @@ impl Expression { Self::Function { original_hirs: _, parameters, - responsible_parameter, body, } => { for parameter in parameters { replacer(parameter); } - replacer(responsible_parameter); body.replace_ids(replacer); } // All other expressions don't define IDs and instead only contain diff --git a/compiler/frontend/src/mir_optimize/validate.rs b/compiler/frontend/src/mir_optimize/validate.rs index e768c38ef..d541dfc44 100644 --- a/compiler/frontend/src/mir_optimize/validate.rs +++ b/compiler/frontend/src/mir_optimize/validate.rs @@ -27,13 +27,11 @@ impl Body { if let Expression::Function { original_hirs: _, parameters, - responsible_parameter, body, } = expression { let mut inner_visible = visible.clone(); inner_visible.extend(parameters.iter().copied()); - inner_visible.insert(*responsible_parameter); body.validate(defined_ids, inner_visible); } diff --git a/compiler/frontend/src/mir_to_lir.rs b/compiler/frontend/src/mir_to_lir.rs index 53d35d8d0..90ff8cd94 100644 --- a/compiler/frontend/src/mir_to_lir.rs +++ b/compiler/frontend/src/mir_to_lir.rs @@ -28,7 +28,6 @@ fn lir(db: &dyn MirToLir, module: Module, tracing: TracingConfig) -> LirResult { FxHashSet::from_iter([hir::Id::new(module, vec![])]), &[], &[], - mir::Id::from_usize(0), &mir.body, ); let lir = Lir::new(context.constants, context.bodies); @@ -52,17 +51,9 @@ impl LoweringContext { original_hirs: FxHashSet, captured: &[mir::Id], parameters: &[mir::Id], - responsible_parameter: mir::Id, body: &mir::Body, ) -> lir::BodyId { - let body = CurrentBody::compile_function( - self, - original_hirs, - captured, - parameters, - responsible_parameter, - body, - ); + let body = CurrentBody::compile_function(self, original_hirs, captured, parameters, body); self.bodies.push(body) } } @@ -80,10 +71,9 @@ impl CurrentBody { original_hirs: FxHashSet, captured: &[mir::Id], parameters: &[mir::Id], - responsible_parameter: mir::Id, body: &mir::Body, ) -> lir::Body { - let mut lir_body = Self::new(original_hirs, captured, parameters, responsible_parameter); + let mut lir_body = Self::new(original_hirs, captured, parameters); for (id, expression) in body.iter() { lir_body.compile_expression(context, id, expression); } @@ -94,25 +84,23 @@ impl CurrentBody { original_hirs: FxHashSet, captured: &[mir::Id], parameters: &[mir::Id], - responsible_parameter: mir::Id, ) -> Self { let body = lir::Body::new(original_hirs, captured.len(), parameters.len()); let id_mapping: FxHashMap<_, _> = captured .iter() .chain(parameters.iter()) .copied() - .chain([responsible_parameter]) .enumerate() .map(|(index, id)| (id, lir::Id::from_usize(index))) .collect(); - // The responsible parameter is a HIR ID, which is always constant. - // Hence, it never has to be dropped. - let ids_to_drop = id_mapping - .iter() - .filter(|(&k, _)| k != responsible_parameter) - .map(|(_, v)| v) - .copied() - .collect(); + + // Don't drop responsible parameters because they are guaranteed to be + // constant and thereby not reference-counted. + let mut ids_to_drop: FxHashSet = id_mapping.values().copied().collect(); + if let Some(responsible_id) = parameters.last() { + ids_to_drop.remove(&id_mapping[responsible_id]); + } + Self { id_mapping, body, @@ -215,7 +203,6 @@ impl CurrentBody { mir::Expression::Function { original_hirs, parameters, - responsible_parameter, body, } => { let captured = expression @@ -225,13 +212,8 @@ impl CurrentBody { .sorted() .collect_vec(); - let body_id = context.compile_function( - original_hirs.clone(), - &captured, - parameters, - *responsible_parameter, - body, - ); + let body_id = + context.compile_function(original_hirs.clone(), &captured, parameters, body); if captured.is_empty() { self.push_constant(context, id, body_id); } else { @@ -243,17 +225,14 @@ impl CurrentBody { mir::Expression::Call { function, arguments, - responsible, } => { let function = self.id_for(context, *function); let arguments = self.ids_for(context, arguments); - let responsible = self.id_for(context, *responsible); self.push( id, lir::Expression::Call { function, arguments, - responsible, }, ); } @@ -285,19 +264,16 @@ impl CurrentBody { hir_call, function, arguments, - responsible, } => { let hir_call = self.id_for(context, *hir_call); let function = self.id_for(context, *function); let arguments = self.ids_for(context, arguments); - let responsible = self.id_for(context, *responsible); self.push( id, lir::Expression::TraceCallStarts { hir_call, function, arguments, - responsible, }, ); } diff --git a/compiler/fuzzer/src/runner.rs b/compiler/fuzzer/src/runner.rs index 64b393c68..68716d09b 100644 --- a/compiler/fuzzer/src/runner.rs +++ b/compiler/fuzzer/src/runner.rs @@ -75,17 +75,18 @@ impl + Clone> Runner { .clone_to_heap_with_mapping(&mut heap, &mut mapping) .try_into() .unwrap(); - let arguments = input + let mut arguments = input .arguments .clone_to_heap_with_mapping(&mut heap, &mut mapping); - let responsible = HirId::create(&mut heap, true, Id::fuzzer()); + let fuzzer_responsibility = HirId::create(&mut heap, true, Id::fuzzer()); + arguments.push(fuzzer_responsibility.into()); let vm = Vm::for_function( byte_code.clone(), &mut heap, + fuzzer_responsibility, function, &arguments, - responsible, StackTracer::default(), ); diff --git a/compiler/language_server/src/debug_adapter/session.rs b/compiler/language_server/src/debug_adapter/session.rs index 826ddabb8..f34c9027a 100644 --- a/compiler/language_server/src/debug_adapter/session.rs +++ b/compiler/language_server/src/debug_adapter/session.rs @@ -219,9 +219,9 @@ impl DebugSession { let vm = Vm::for_function( Rc::new(byte_code), &mut heap, - main, - &[environment], platform, + main, + &[environment, platform.into()], tracer, ); diff --git a/compiler/language_server/src/debug_adapter/tracer.rs b/compiler/language_server/src/debug_adapter/tracer.rs index f7694ad06..aa0e39d4d 100644 --- a/compiler/language_server/src/debug_adapter/tracer.rs +++ b/compiler/language_server/src/debug_adapter/tracer.rs @@ -45,13 +45,11 @@ impl Tracer for DebugTracer { call_site: HirId, callee: InlineObject, arguments: Vec, - responsible: HirId, ) { let call = Call { call_site, callee, arguments, - responsible, }; call.dup(heap); self.call_stack.push(StackFrame::new(call)); diff --git a/compiler/language_server/src/features_candy/analyzer/static_panics.rs b/compiler/language_server/src/features_candy/analyzer/static_panics.rs index 29a204fcf..19328d48e 100644 --- a/compiler/language_server/src/features_candy/analyzer/static_panics.rs +++ b/compiler/language_server/src/features_candy/analyzer/static_panics.rs @@ -50,24 +50,22 @@ impl StaticPanicsOfExpression for Expression { let referenced = self.referenced_ids(); match self { Self::Function { - parameters, - responsible_parameter, - body, - .. + parameters, body, .. } => { - let is_fuzzable = referenced.contains(responsible_parameter); + parameters + .last() + .map(|responsible| referenced.contains(responsible)) + .unwrap_or(true); for parameter in &*parameters { visible.insert(*parameter, Self::Parameter); } - visible.insert(*responsible_parameter, Self::Parameter); body.collect_static_panics(visible, panics, is_fuzzable); for parameter in parameters { visible.remove(*parameter); } - visible.remove(*responsible_parameter); } Self::Panic { reason, diff --git a/compiler/vm/benches/utils.rs b/compiler/vm/benches/utils.rs index 60604ee31..b313a093f 100644 --- a/compiler/vm/benches/utils.rs +++ b/compiler/vm/benches/utils.rs @@ -114,8 +114,7 @@ pub fn run(byte_code: impl Borrow) -> (Heap, InlineObject) { byte_code, &mut heap, main, - &[environment.into()], - responsible, + &[environment.into(), responsible.into()], tracer, ) .run_forever_without_handles(&mut heap); diff --git a/compiler/vm/fuzz/fuzz_targets/vm.rs b/compiler/vm/fuzz/fuzz_targets/vm.rs index 634819e02..383e2830b 100644 --- a/compiler/vm/fuzz/fuzz_targets/vm.rs +++ b/compiler/vm/fuzz/fuzz_targets/vm.rs @@ -87,8 +87,7 @@ fuzz_target!(|data: &[u8]| { &byte_code, &mut heap, main, - &[environment.into()], - responsible, + &[environment.into(), responsible.into()], DummyTracer, ) .run_forever_without_handles(&mut heap); diff --git a/compiler/vm/src/builtin_functions.rs b/compiler/vm/src/builtin_functions.rs index 0007e195b..625801e4b 100644 --- a/compiler/vm/src/builtin_functions.rs +++ b/compiler/vm/src/builtin_functions.rs @@ -1,7 +1,7 @@ use crate::{ heap::{Data, Function, Heap, HirId, InlineObject, Int, List, Struct, Tag, Text, ToDebugText}, instructions::InstructionResult, - vm::{CallHandle, MachineState, Panic}, + vm::{self, MachineState, Panic}, }; use candy_frontend::{ builtin_functions::BuiltinFunction, @@ -28,8 +28,10 @@ impl MachineState { heap: &mut Heap, builtin_function: BuiltinFunction, args: &[InlineObject], - responsible: HirId, ) -> InstructionResult { + let responsible: HirId = (*args.last().unwrap()).try_into().unwrap(); + let args = &args[..args.len() - 1]; + let result = span!(Level::TRACE, "Running builtin").in_scope(|| match &builtin_function { BuiltinFunction::Equals => heap.equals(args), BuiltinFunction::FunctionRun => Heap::function_run(args, responsible), @@ -85,7 +87,7 @@ impl MachineState { Ok(DivergeControlFlow { function, responsible, - }) => self.call_function(heap, function, &[], responsible), + }) => self.call_function(heap, function, &[responsible.into()]), Ok(CallHandle(call)) => InstructionResult::CallHandle(call), Err(reason) => InstructionResult::Panic(Panic { reason, @@ -98,11 +100,11 @@ impl MachineState { type BuiltinResult = Result; enum SuccessfulBehavior { Return(InlineObject), + CallHandle(vm::CallHandle), DivergeControlFlow { function: Function, responsible: HirId, }, - CallHandle(CallHandle), } impl From for BuiltinResult { @@ -188,16 +190,16 @@ impl Heap { .to_string(), ); } - CallHandle(CallHandle { + CallHandle(vm::CallHandle { handle, arguments: vec![], - responsible, }) } _ => return Err("`✨.functionRun` expects a function or handle".to_string()), } }) } + fn get_argument_count(&mut self, args: &[InlineObject]) -> BuiltinResult { unpack_and_later_drop!(self, args, |function: Any| { let count = match **function { @@ -206,7 +208,8 @@ impl Heap { Data::Handle(handle) => handle.argument_count(), _ => return Err("`✨.getArgumentCount` expects a function or handle".to_string()), }; - Return(Int::create(self, true, count).into()) + let count_without_responsibility = count - 1; + Return(Int::create(self, true, count_without_responsibility).into()) }) } diff --git a/compiler/vm/src/byte_code.rs b/compiler/vm/src/byte_code.rs index 80b6836e7..4341c91ae 100644 --- a/compiler/vm/src/byte_code.rs +++ b/compiler/vm/src/byte_code.rs @@ -104,7 +104,7 @@ pub enum Instruction { /// a, reason, responsible -> 💥 Panic, - /// a, HIR ID, function, arg1, arg2, ..., argN, responsible -> a + /// a, HIR ID, function, arg1, arg2, ..., argN -> a TraceCallStarts { num_args: usize, }, @@ -161,7 +161,6 @@ impl Instruction { num_locals_to_pop, num_args, } => { - stack.pop(); // responsible stack.pop_multiple(*num_args); stack.pop(); // function/builtin stack.pop_multiple(*num_locals_to_pop); @@ -177,21 +176,20 @@ impl Instruction { stack.push(result); } Self::TraceCallStarts { num_args } => { - stack.pop(); // HIR ID - stack.pop(); // responsible stack.pop_multiple(*num_args); stack.pop(); // callee + stack.pop(); // HIR ID } Self::TraceCallEnds => { stack.pop(); // return value } Self::TraceExpressionEvaluated => { - stack.pop(); // HIR ID stack.pop(); // value + stack.pop(); // HIR ID } Self::TraceFoundFuzzableFunction => { + stack.pop(); // function stack.pop(); // HIR ID - stack.pop(); // value } } } diff --git a/compiler/vm/src/environment.rs b/compiler/vm/src/environment.rs index d3556459e..330a787bf 100644 --- a/compiler/vm/src/environment.rs +++ b/compiler/vm/src/environment.rs @@ -59,9 +59,9 @@ impl DefaultEnvironment { .map(|it| Text::create(heap, true, it).into()) .collect_vec(); let arguments = List::create(heap, true, arguments.as_slice()); - let get_random_bytes_handle = Handle::new(heap, 1); - let stdin_handle = Handle::new(heap, 0); - let stdout_handle = Handle::new(heap, 1); + let get_random_bytes_handle = Handle::new(heap, 2); + let stdin_handle = Handle::new(heap, 1); + let stdout_handle = Handle::new(heap, 2); let environment_object = Struct::create_with_symbol_keys( heap, true, diff --git a/compiler/vm/src/heap/object.rs b/compiler/vm/src/heap/object.rs index eae24df77..40b33ae87 100644 --- a/compiler/vm/src/heap/object.rs +++ b/compiler/vm/src/heap/object.rs @@ -571,6 +571,10 @@ pub struct Handle(InlineHandle); impl Handle { #[must_use] pub fn new(heap: &mut Heap, argument_count: usize) -> Self { + assert!( + argument_count >= 1, + "Each handle needs to accept at least a responsibility parameter." + ); let id = heap.handle_id_generator.generate(); Self::create(heap, id, argument_count) } diff --git a/compiler/vm/src/instructions.rs b/compiler/vm/src/instructions.rs index f869b2639..0730cb17f 100644 --- a/compiler/vm/src/instructions.rs +++ b/compiler/vm/src/instructions.rs @@ -115,7 +115,6 @@ impl MachineState { InstructionResult::Done } Instruction::Call { num_args } => { - let responsible = self.pop_from_data_stack().try_into().unwrap(); let mut arguments = (0..*num_args) .map(|_| self.pop_from_data_stack()) .collect_vec(); @@ -123,13 +122,12 @@ impl MachineState { arguments.reverse(); let callee = self.pop_from_data_stack(); - self.call(heap, callee, &arguments, responsible) + self.call(heap, callee, &arguments) } Instruction::TailCall { num_locals_to_pop, num_args, } => { - let responsible = self.pop_from_data_stack().try_into().unwrap(); let mut arguments = (0..*num_args) .map(|_| self.pop_from_data_stack()) .collect_vec(); @@ -143,7 +141,7 @@ impl MachineState { // Tail calling a function is basically just a normal call, but // pretending we are our caller. self.next_instruction = self.call_stack.pop(); - self.call(heap, callee, &arguments, responsible) + self.call(heap, callee, &arguments) } Instruction::Return => { self.next_instruction = self.call_stack.pop(); @@ -168,7 +166,6 @@ impl MachineState { }) } Instruction::TraceCallStarts { num_args } => { - let responsible = self.pop_from_data_stack().try_into().unwrap(); let mut args = vec![]; for _ in 0..*num_args { args.push(self.pop_from_data_stack()); @@ -177,7 +174,7 @@ impl MachineState { let call_site = self.pop_from_data_stack().try_into().unwrap(); args.reverse(); - tracer.call_started(heap, call_site, callee, args, responsible); + tracer.call_started(heap, call_site, callee, args); InstructionResult::Done } Instruction::TraceCallEnds => { @@ -210,25 +207,33 @@ impl MachineState { heap: &mut Heap, callee: InlineObject, arguments: &[InlineObject], - responsible: HirId, ) -> InstructionResult { match callee.into() { - Data::Function(function) => self.call_function(heap, function, arguments, responsible), + Data::Function(function) => self.call_function(heap, function, arguments), Data::Builtin(builtin) => { callee.drop(heap); - self.run_builtin_function(heap, builtin.get(), arguments, responsible) + self.run_builtin_function(heap, builtin.get(), arguments) } Data::Handle(handle) => { let parameter_count = handle.argument_count(); let argument_count = arguments.len(); if argument_count != parameter_count { + let responsible: HirId = arguments.last().unwrap().clone().try_into().unwrap(); return InstructionResult::Panic(Panic { reason: format!( "A function expected {} {}, but you called it with {} {}.", parameter_count, - if parameter_count == 1 { "parameter" } else { "parameters" }, + if parameter_count == 1 { + "parameter" + } else { + "parameters" + }, argument_count, - if argument_count == 1 { "argument" } else { "arguments" }, + if argument_count == 1 { + "argument" + } else { + "arguments" + }, ), responsible: responsible.get().clone(), }); @@ -236,10 +241,10 @@ impl MachineState { InstructionResult::CallHandle(CallHandle { handle, arguments: arguments.to_vec(), - responsible, }) - }, + } Data::Tag(tag) => { + let responsible: HirId = (*arguments.last().unwrap()).try_into().unwrap(); if tag.has_value() { return InstructionResult::Panic(Panic { reason: "A tag's value cannot be overwritten by calling it. Use `tag.withValue` instead.".to_string(), @@ -262,12 +267,15 @@ impl MachineState { }) } } - _ => InstructionResult::Panic(Panic { - reason: format!( - "You can only call functions, builtins, tags, and handles, but you tried to call {callee}.", - ), - responsible: responsible.get().clone(), - }), + _ => { + let responsible: HirId = (*arguments.last().unwrap()).try_into().unwrap(); + InstructionResult::Panic(Panic { + reason: format!( + "You can only call functions, builtins, tags, and handles, but you tried to call {callee}.", + ), + responsible: responsible.get().clone(), + }) + } } } pub fn call_function( @@ -275,10 +283,10 @@ impl MachineState { heap: &mut Heap, function: Function, arguments: &[InlineObject], - responsible: HirId, ) -> InstructionResult { let expected_num_args = function.argument_count(); if arguments.len() != expected_num_args { + let responsible: HirId = (*arguments.last().unwrap()).try_into().unwrap(); return InstructionResult::Panic(Panic { reason: format!( "A function expected {expected_num_args} parameters, but you called it with {} arguments.", @@ -297,7 +305,6 @@ impl MachineState { } self.data_stack.extend_from_slice(captured); self.data_stack.extend_from_slice(arguments); - self.push_to_data_stack(responsible); self.next_instruction = Some(function.body()); InstructionResult::Done } diff --git a/compiler/vm/src/mir_to_byte_code.rs b/compiler/vm/src/mir_to_byte_code.rs index 23350e15c..ab17a4a11 100644 --- a/compiler/vm/src/mir_to_byte_code.rs +++ b/compiler/vm/src/mir_to_byte_code.rs @@ -69,7 +69,6 @@ where &FxHashSet::from_iter([hir::Id::new(module, vec![])]), &FxHashSet::default(), &[], - Id::from_usize(0), &mir.body, ); module_function.set_body(start); @@ -83,7 +82,6 @@ fn compile_function( original_hirs: &FxHashSet, captured: &FxHashSet, parameters: &[Id], - responsible_parameter: Id, body: &Body, ) -> InstructionPointer { let mut context = LoweringContext { @@ -98,14 +96,13 @@ fn compile_function( for parameter in parameters { context.stack.push(*parameter); } - context.stack.push(responsible_parameter); for (id, expression) in body.iter() { context.compile_expression(id, expression); } // Expressions may not push things onto the stack, but to the constant heap // instead. - if *context.stack.last().unwrap() != body.return_value() { + if context.stack.last() != Some(&body.return_value()) { context.emit_reference_to(body.return_value()); } @@ -248,7 +245,6 @@ impl<'c> LoweringContext<'c> { Expression::Function { original_hirs, parameters, - responsible_parameter, body, } => { let captured = expression @@ -263,19 +259,18 @@ impl<'c> LoweringContext<'c> { original_hirs, &captured, parameters, - *responsible_parameter, body, ); if captured.is_empty() { - let list = Function::create( + let function = Function::create( &mut self.byte_code.constant_heap, false, &[], parameters.len(), instructions, ); - self.constants.insert(id, list.into()); + self.constants.insert(id, function.into()); } else { for captured in &captured { self.emit_reference_to(*captured); @@ -299,13 +294,11 @@ impl<'c> LoweringContext<'c> { Expression::Call { function, arguments, - responsible, } => { self.emit_reference_to(*function); for argument in arguments { self.emit_reference_to(*argument); } - self.emit_reference_to(*responsible); self.emit( id, Instruction::Call { @@ -335,14 +328,12 @@ impl<'c> LoweringContext<'c> { hir_call, function, arguments, - responsible, } => { self.emit_reference_to(*hir_call); self.emit_reference_to(*function); for argument in arguments { self.emit_reference_to(*argument); } - self.emit_reference_to(*responsible); self.emit( id, Instruction::TraceCallStarts { @@ -382,6 +373,9 @@ impl<'c> LoweringContext<'c> { } } fn emit(&mut self, id: Id, instruction: Instruction) { + if matches!(instruction, Instruction::PopMultipleBelowTop(0)) { + return; + } instruction.apply_to_stack(&mut self.stack, id); self.instructions.push(instruction); } diff --git a/compiler/vm/src/tracer/mod.rs b/compiler/vm/src/tracer/mod.rs index 2ad22ba94..64188ee5e 100644 --- a/compiler/vm/src/tracer/mod.rs +++ b/compiler/vm/src/tracer/mod.rs @@ -23,7 +23,6 @@ pub trait Tracer { _call_site: HirId, _callee: InlineObject, _arguments: Vec, - _responsible: HirId, ) { } fn call_ended(&mut self, _heap: &mut Heap, _return_value: InlineObject) {} diff --git a/compiler/vm/src/tracer/stack_trace.rs b/compiler/vm/src/tracer/stack_trace.rs index c2bf31cef..d28371f75 100644 --- a/compiler/vm/src/tracer/stack_trace.rs +++ b/compiler/vm/src/tracer/stack_trace.rs @@ -24,24 +24,19 @@ pub struct Call { pub call_site: HirId, pub callee: InlineObject, pub arguments: Vec, - pub responsible: HirId, } impl Call { pub fn dup(&self, heap: &mut Heap) { - self.call_site.dup(); self.callee.dup(heap); for argument in &self.arguments { argument.dup(heap); } - self.responsible.dup(); } pub fn drop(&self, heap: &mut Heap) { - self.call_site.drop(heap); self.callee.drop(heap); for argument in &self.arguments { argument.drop(heap); } - self.responsible.drop(heap); } } @@ -52,13 +47,11 @@ impl Tracer for StackTracer { call_site: HirId, callee: InlineObject, arguments: Vec, - responsible: HirId, ) { let call = Call { call_site, callee, arguments, - responsible, }; call.dup(heap); self.call_stack.push(call); diff --git a/compiler/vm/src/tracer/tuple.rs b/compiler/vm/src/tracer/tuple.rs index 3bc447cfa..c6e5abe8c 100644 --- a/compiler/vm/src/tracer/tuple.rs +++ b/compiler/vm/src/tracer/tuple.rs @@ -19,9 +19,8 @@ impl Tracer for Tuple { call_site: HirId, callee: InlineObject, arguments: Vec, - responsible: HirId, ) { - for_tuples!( #(Tuple.call_started(heap, call_site, callee, arguments.clone(), responsible);)* ); + for_tuples!( #(Tuple.call_started(heap, call_site, callee, arguments.clone());)* ); } fn call_ended(&mut self, heap: &mut Heap, return_value: InlineObject) { for_tuples!( #(Tuple.call_ended(heap, return_value);)* ); diff --git a/compiler/vm/src/vm.rs b/compiler/vm/src/vm.rs index e471a7de3..56868fcf8 100644 --- a/compiler/vm/src/vm.rs +++ b/compiler/vm/src/vm.rs @@ -35,7 +35,6 @@ pub struct MachineState { pub struct CallHandle { pub handle: Handle, pub arguments: Vec, - pub responsible: HirId, } #[derive(Clone, Debug)] @@ -52,25 +51,19 @@ where pub fn for_function( byte_code: B, heap: &mut Heap, + call_site: HirId, function: Function, arguments: &[InlineObject], - responsible: HirId, mut tracer: T, ) -> Self { - tracer.call_started( - heap, - responsible, - function.into(), - arguments.to_vec(), - responsible, - ); + tracer.call_started(heap, call_site, function.into(), arguments.to_vec()); let mut state = MachineState { next_instruction: None, data_stack: vec![], call_stack: vec![], }; - state.call_function(heap, function, arguments, responsible); + state.call_function(heap, function, arguments); let inner = Box::new(VmInner { byte_code, @@ -93,7 +86,7 @@ where 0, "Function is not a module function because it has arguments.", ); - Self::for_function(byte_code, heap, function, &[], responsible, tracer) + Self::for_function(byte_code, heap, responsible, function, &[], tracer) } #[must_use]