From d7df0aaf5f12652196ec1c52449d4d528f66d977 Mon Sep 17 00:00:00 2001 From: Markus Westerlind Date: Fri, 10 Jul 2026 00:45:51 +0200 Subject: [PATCH 1/2] fix: Don't cascade errors if a module (or variable) is undefined --- check/src/typecheck.rs | 48 +++++++++++++++++++++----------------- check/tests/fail.rs | 10 ++++++++ check/tests/support/mod.rs | 7 +++--- tests/error.rs | 22 +++++++++++++++++ 4 files changed, 62 insertions(+), 25 deletions(-) diff --git a/check/src/typecheck.rs b/check/src/typecheck.rs index 1682dddbb..b45d37a65 100644 --- a/check/src/typecheck.rs +++ b/check/src/typecheck.rs @@ -654,6 +654,9 @@ impl<'a, 'ast> Typecheck<'a, 'ast> { } Err(err) => { returned_type = ModType::wobbly(self.subs.error()); + if let Some(exp) = expected_type { + self.unify_span(expr.span, &exp.concrete, returned_type.concrete.clone()); + } self.errors.push(Spanned { span: expr_check_span(expr), value: err.into(), @@ -1268,7 +1271,7 @@ impl<'a, 'ast> Typecheck<'a, 'ast> { ModType::wobbly( typ.as_ref() .map(|typ| self.translate_arc_type(typ)) - .unwrap_or_else(|| self.subs.new_var()), + .unwrap_or_else(|| self.subs.error()), ), Vec::new(), )), @@ -1672,37 +1675,41 @@ impl<'a, 'ast> Typecheck<'a, 'ast> { let name = name.value.clone(); // The `types` in the record type should have a type matching the // `name` - let field_type = record_match_type - .type_field_iter() - .find(|field| field.name.name_eq(&name)); + let field_type = match *record_match_type { + // Don't cascade into more errors if the type we are matching is already one + Type::Error => None, + _ => record_match_type + .type_field_iter() + .find(|field| field.name.name_eq(&name)) + .or_else(|| { + self.error( + span, + TypeError::UndefinedField( + match_type.concrete.clone(), + name.clone(), + ), + ); + None + }), + }; - let alias; - let alias = match field_type { + match field_type { Some(field_type) => { if let Some(meta) = self.implicit_resolver.metadata.remove(&name) { self.implicit_resolver .metadata .insert(field_type.typ.name.clone(), meta); } - &field_type.typ + self.stack_type(name, &field_type.typ); } None => { - self.error( - span, - TypeError::UndefinedField( - match_type.concrete.clone(), - name.clone(), - ), - ); // We still define the type so that any uses later on in the program // won't error on UndefinedType let hole = self.subs.error(); - alias = self.new_alias(name.clone(), Vec::new(), hole); - &alias + let alias = self.new_alias(name.clone(), Vec::new(), hole); + self.stack_type(name, &alias); } - }; - - self.stack_type(name, &alias); + } } if !missing_fields_from_match_type.is_empty() { @@ -1930,7 +1937,6 @@ impl<'a, 'ast> Typecheck<'a, 'ast> { } } - let mut types = Vec::new(); for (i, bind) in bindings.iter_mut().enumerate() { // Functions which are declared as `let f x = ...` are allowed to be self // recursive @@ -2002,8 +2008,6 @@ impl<'a, 'ast> Typecheck<'a, 'ast> { self.typecheck_let_pattern(&mut bind.name, resolved_type.clone()); debug!("Generalized to {}", bind.resolved_type); self.finish_pattern(level, &mut bind.name, &resolved_type); - } else { - types.push(typ); } } diff --git a/check/tests/fail.rs b/check/tests/fail.rs index 2205a1fd2..19fda6438 100644 --- a/check/tests/fail.rs +++ b/check/tests/fail.rs @@ -782,3 +782,13 @@ Cons "" Nil "#, Unification { .. } } + +test_check_err! { + missing_type_do_not_cascade, + r#" +let { Test } = x +"" + + "#, +UndefinedVariable { .. } +} diff --git a/check/tests/support/mod.rs b/check/tests/support/mod.rs index 3366e953e..67d304553 100644 --- a/check/tests/support/mod.rs +++ b/check/tests/support/mod.rs @@ -475,10 +475,10 @@ macro_rules! assert_err { $crate::support::Error::Parser(ref err) => panic!("{}", err), $crate::support::Error::Check(err) => err.into_errors(), }; - let mut iter = (&errors).into_iter(); + let mut iter = errors.iter(); $( match iter.next() { - Some(&crate::base::pos::Spanned { value: crate::base::error::Help { error: $id, .. }, .. }) => (), + Some(crate::base::pos::Spanned { value: crate::base::error::Help { error: $id, .. }, .. }) => (), _ => { if $text.is_empty() { assert!( @@ -498,7 +498,8 @@ macro_rules! assert_err { } } )+ - assert!(iter.count() == 0, "Found more errors than expected\n{}", errors); + let remaining: crate::base::error::Errors<_> = iter.cloned().collect(); + assert!(remaining.is_empty(), "Found more errors than expected\n{}", crate::support::in_file_error($text, remaining)); } } }} diff --git a/tests/error.rs b/tests/error.rs index 63f658f81..4e8af4807 100644 --- a/tests/error.rs +++ b/tests/error.rs @@ -138,3 +138,25 @@ fn module_imports_provide_a_type_despite_internal_errors() { "#]] .assert_eq(&result.unwrap_err().to_string()); } + +#[test] +fn missing_module_import_does_not_cascade() { + let _ = ::env_logger::try_init(); + let vm = support::make_vm(); + + let result = vm.run_expr::( + "test2", + r#" + let { Test } = import! test + 1.0 + "#, + ); + expect![[r#" + error: Could not find module 'test'. Searched `.`, `..`. + ┌─ test2:2:24 + │ + 2 │ let { Test } = import! test + │ ^^^^^^^^^^^^ + + "#]].assert_eq(&result.unwrap_err().to_string()); +} From 11f0bc37942e957c8afa7372e1b6a7f292aa361e Mon Sep 17 00:00:00 2001 From: Markus Westerlind Date: Fri, 10 Jul 2026 00:48:33 +0200 Subject: [PATCH 2/2] chore: Update the book to work with new mdbook versions --- book/book.toml | 1 - scripts/install_mdbook.sh | 4 ++-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/book/book.toml b/book/book.toml index 9e179e8b9..06bc66d9e 100644 --- a/book/book.toml +++ b/book/book.toml @@ -2,5 +2,4 @@ title = "Gluon Documentation" description = "Gluon is a static, type inferred and embeddable language written in Rust" authors = ["Markus Westerlind"] -multilingual = false src = "src" diff --git a/scripts/install_mdbook.sh b/scripts/install_mdbook.sh index f1288b461..c4ca4e617 100755 --- a/scripts/install_mdbook.sh +++ b/scripts/install_mdbook.sh @@ -9,7 +9,7 @@ else TARGET='x86_64-unknown-linux-musl' fi -MDBOOK_VERSION="mdbook-v0.2.1-${TARGET}" -curl -L "https://github.com/rust-lang-nursery/mdBook/releases/download/v0.2.1/$MDBOOK_VERSION.tar.gz" | tar -xvz +MDBOOK_VERSION="v0.5.4" +curl -L "https://github.com/rust-lang/mdBook/releases/download/${MDBOOK_VERSION}/mdbook-${MDBOOK_VERSION}-${TARGET}.tar.gz" | tar -xvz chmod +x ./mdbook mv ./mdbook $HOME/bin/