diff --git a/compiler/src/mir/passes.rs b/compiler/src/mir/passes.rs index 6162afad2..a69f3f627 100644 --- a/compiler/src/mir/passes.rs +++ b/compiler/src/mir/passes.rs @@ -2187,7 +2187,6 @@ impl<'a> LowerMethod<'a> { self.current_block_mut().get_field(tag_reg, reg, class, tag_field, loc); let out_reg = match node.kind { - types::ThrowKind::Unknown => unreachable!(), types::ThrowKind::Option(typ) => { let some_id = class .variant(self.db(), OPTION_SOME) @@ -2265,6 +2264,7 @@ impl<'a> LowerMethod<'a> { self.current_block_mut().return_value(ret_reg, loc); ok_reg } + _ => unreachable!(), }; self.current_block = after_block; diff --git a/compiler/src/type_check/expressions.rs b/compiler/src/type_check/expressions.rs index 052d0662f..592f459de 100644 --- a/compiler/src/type_check/expressions.rs +++ b/compiler/src/type_check/expressions.rs @@ -3014,23 +3014,28 @@ impl<'a> CheckMethodBody<'a> { } let ret_type = scope.return_type; + let throw_type = if scope.in_recover() && expr.is_owned(self.db()) { + expr.as_uni(self.db()) + } else { + expr + }; node.return_type = ret_type; + node.resolved_type = throw_type; match ret_type.throw_kind(self.db()) { ThrowKind::Unknown | ThrowKind::Option(_) => self .state .diagnostics .throw_not_available(self.file(), node.location.clone()), - ThrowKind::Result(ret_ok, ret_err) => { - node.resolved_type = - if scope.in_recover() && expr.is_owned(self.db()) { - expr.as_uni(self.db()) - } else { - expr - }; + ThrowKind::Infer(pid) => { + let var = TypeRef::placeholder(self.db_mut(), None); + let typ = TypeRef::result_type(self.db_mut(), var, expr); - if !TypeChecker::check(self.db(), expr, ret_err) { + pid.assign(self.db(), typ); + } + ThrowKind::Result(ret_ok, ret_err) => { + if !TypeChecker::check(self.db(), throw_type, ret_err) { self.state.diagnostics.invalid_throw( ThrowKind::Result(ret_ok, expr) .throw_type_name(self.db(), ret_ok), @@ -3857,6 +3862,18 @@ impl<'a> CheckMethodBody<'a> { // no type-checking is necessary in this case. return some; } + (ThrowKind::Option(some), ThrowKind::Infer(pid)) => { + let inferred = TypeRef::option_type(self.db_mut(), some); + + pid.assign(self.db(), inferred); + return some; + } + (ThrowKind::Result(ok, err), ThrowKind::Infer(pid)) => { + let inferred = TypeRef::result_type(self.db_mut(), ok, err); + + pid.assign(self.db(), inferred); + return ok; + } ( ThrowKind::Result(ok, expr_err), ThrowKind::Result(ret_ok, ret_err), @@ -3872,7 +3889,7 @@ impl<'a> CheckMethodBody<'a> { node.location.clone(), ); } - (ThrowKind::Unknown, _) => { + (ThrowKind::Unknown | ThrowKind::Infer(_), _) => { self.state.diagnostics.invalid_try( format_type(self.db(), expr), self.file(), diff --git a/types/src/lib.rs b/types/src/lib.rs index 96f17176e..f913a66cc 100644 --- a/types/src/lib.rs +++ b/types/src/lib.rs @@ -166,7 +166,7 @@ impl TypePlaceholderId { self.get(db).required } - fn assign(self, db: &Database, value: TypeRef) { + pub fn assign(self, db: &Database, value: TypeRef) { // Assigning placeholders to themselves isn't useful and results in // resolve() getting stuck. if let TypeRef::Placeholder(id) = value { @@ -2864,6 +2864,7 @@ pub enum ConstantPatternKind { #[derive(Debug, PartialEq, Eq, Clone, Copy)] pub enum ThrowKind { Unknown, + Infer(TypePlaceholderId), Option(TypeRef), Result(TypeRef, TypeRef), } @@ -2871,7 +2872,6 @@ pub enum ThrowKind { impl ThrowKind { pub fn throw_type_name(self, db: &Database, ok: TypeRef) -> String { match self { - ThrowKind::Unknown => "?".to_string(), ThrowKind::Option(_) => { format!("Option[{}]", format::format_type(db, ok)) } @@ -2882,6 +2882,7 @@ impl ThrowKind { format::format_type(db, err) ) } + _ => "?".to_string(), } } @@ -4075,12 +4076,41 @@ impl TypeRef { } } TypeRef::Placeholder(p) => { - p.value(db).map_or(ThrowKind::Unknown, |v| v.throw_kind(db)) + p.value(db).map_or(ThrowKind::Infer(p), |v| v.throw_kind(db)) } _ => ThrowKind::Unknown, } } + pub fn result_type( + db: &mut Database, + ok: TypeRef, + error: TypeRef, + ) -> TypeRef { + let class = db.class_in_module(RESULT_MODULE, RESULT_CLASS); + let params = class.type_parameters(db); + let mut args = TypeArguments::new(); + + args.assign(params[0], ok); + args.assign(params[1], error); + + TypeRef::Owned(TypeId::ClassInstance(ClassInstance::generic( + db, class, args, + ))) + } + + pub fn option_type(db: &mut Database, some: TypeRef) -> TypeRef { + let class = db.class_in_module(OPTION_MODULE, OPTION_CLASS); + let params = class.type_parameters(db); + let mut args = TypeArguments::new(); + + args.assign(params[0], some); + + TypeRef::Owned(TypeId::ClassInstance(ClassInstance::generic( + db, class, args, + ))) + } + fn is_instance_of(self, db: &Database, id: ClassId) -> bool { self.class_id(db) == Some(id) }