From: Utkarsh Bahuguna <[email protected]>

Expressions that failed to type check never put their error type in the
context, so a later lookup got null and crashed. Keep it. Operators and
array literals now also bail on an operand that already errored instead
of stacking mismatched types on top, and consts keep their declared type
when the init fails.

Fixes Rust-GCC/gccrs#5039

Assisted-by: Claude Opus (Anthropic)

gcc/rust/ChangeLog:

        * typecheck/rust-hir-type-check-expr.cc (TypeCheckExpr::Resolve):
        Keep the error type in the context.
        (TypeCheckExpr::visit (AssignmentExpr)): Bail out on an error
        operand.
        (TypeCheckExpr::visit (CompoundAssignmentExpr)): Likewise.
        (TypeCheckExpr::visit (ArithmeticOrLogicalExpr)): Likewise.
        (TypeCheckExpr::visit (ComparisonExpr)): Likewise.
        (TypeCheckExpr::visit (LazyBooleanExpr)): Likewise.
        (TypeCheckExpr::visit (NegationExpr)): Likewise.
        (TypeCheckExpr::visit (ArrayExpr)): Bail out on an error element.
        * typecheck/rust-hir-type-check-item.cc (TypeCheckItem::visit
        (StaticItem)): Keep the declared type when the initializer failed.
        (TypeCheckItem::visit (ConstantItem)): Likewise.

gcc/testsuite/ChangeLog:

        * rust/compile/issues/4165.rs: Drop the follow-up error.
        * rust/compile/issues/issue-5039.rs: New test.
        * rust/compile/issues/issue-5039-2.rs: New test.

Signed-off-by: Utkarsh Bahuguna <[email protected]>

Tested on x86_64-pc-linux-gnu.

---
 .../typecheck/rust-hir-type-check-expr.cc     | 30 ++++++++++++++++++-
 .../typecheck/rust-hir-type-check-item.cc     | 26 +++++++++-------
 gcc/testsuite/rust/compile/issues/4165.rs     |  1 -
 .../rust/compile/issues/issue-5039-2.rs       | 14 +++++++++
 .../rust/compile/issues/issue-5039.rs         | 25 ++++++++++++++++
 5 files changed, 83 insertions(+), 13 deletions(-)
 create mode 100644 gcc/testsuite/rust/compile/issues/issue-5039-2.rs
 create mode 100644 gcc/testsuite/rust/compile/issues/issue-5039.rs

diff --git a/gcc/rust/typecheck/rust-hir-type-check-expr.cc 
b/gcc/rust/typecheck/rust-hir-type-check-expr.cc
index b7acb787fc6..db88c8c1b65 100644
--- a/gcc/rust/typecheck/rust-hir-type-check-expr.cc
+++ b/gcc/rust/typecheck/rust-hir-type-check-expr.cc
@@ -50,8 +50,10 @@ TypeCheckExpr::Resolve (HIR::Expr &expr)
   TypeCheckExpr resolver;
   expr.accept_vis (resolver);
 
+  // the error type has to go in the context like any other, autoderef
+  // looks this expression's type up by ref later and would get nullptr
   if (resolver.infered == nullptr)
-    return new TyTy::ErrorType (expr.get_mappings ().get_hirid ());
+    resolver.infered = new TyTy::ErrorType (expr.get_mappings ().get_hirid ());
 
   if (resolver.infered->get_kind () != TyTy::TypeKind::CONST)
     {
@@ -405,6 +407,9 @@ TypeCheckExpr::visit (HIR::AssignmentExpr &expr)
   auto lhs = TypeCheckExpr::Resolve (expr.get_lhs ());
   auto rhs = TypeCheckExpr::Resolve (expr.get_rhs ());
 
+  if (lhs->is<TyTy::ErrorType> () || rhs->is<TyTy::ErrorType> ())
+    return;
+
   coercion_site (expr.get_mappings ().get_hirid (),
                 TyTy::TyWithLocation (lhs, expr.get_lhs ().get_locus ()),
                 TyTy::TyWithLocation (rhs, expr.get_rhs ().get_locus ()),
@@ -419,6 +424,9 @@ TypeCheckExpr::visit (HIR::CompoundAssignmentExpr &expr)
   auto lhs = TypeCheckExpr::Resolve (expr.get_lhs ());
   auto rhs = TypeCheckExpr::Resolve (expr.get_rhs ());
 
+  if (lhs->is<TyTy::ErrorType> () || rhs->is<TyTy::ErrorType> ())
+    return;
+
   auto lang_item_type
     = LangItem::CompoundAssignmentOperatorToLangItem (expr.get_expr_type ());
   bool operator_overloaded
@@ -472,6 +480,9 @@ TypeCheckExpr::visit (HIR::ArithmeticOrLogicalExpr &expr)
   auto lhs = TypeCheckExpr::Resolve (expr.get_lhs ());
   auto rhs = TypeCheckExpr::Resolve (expr.get_rhs ());
 
+  if (lhs->is<TyTy::ErrorType> () || rhs->is<TyTy::ErrorType> ())
+    return;
+
   auto lang_item_type = LangItem::OperatorToLangItem (expr.get_expr_type ());
   bool operator_overloaded
     = resolve_operator_overload (lang_item_type, expr, lhs, rhs);
@@ -520,6 +531,9 @@ TypeCheckExpr::visit (HIR::ComparisonExpr &expr)
   auto lhs = TypeCheckExpr::Resolve (expr.get_lhs ());
   auto rhs = TypeCheckExpr::Resolve (expr.get_rhs ());
 
+  if (lhs->is<TyTy::ErrorType> () || rhs->is<TyTy::ErrorType> ())
+    return;
+
   auto borrowed_lhs
     = new TyTy::ReferenceType (mappings.get_next_hir_id (),
                               TyTy::TyVar (lhs->get_ref ()), Mutability::Imm);
@@ -555,6 +569,9 @@ TypeCheckExpr::visit (HIR::LazyBooleanExpr &expr)
   auto lhs = TypeCheckExpr::Resolve (expr.get_lhs ());
   auto rhs = TypeCheckExpr::Resolve (expr.get_rhs ());
 
+  if (lhs->is<TyTy::ErrorType> () || rhs->is<TyTy::ErrorType> ())
+    return;
+
   // we expect the lhs and rhs must be bools at this point
   TyTy::BaseType *boolean_node = nullptr;
   bool ok = context->lookup_builtin ("bool", &boolean_node);
@@ -585,6 +602,9 @@ TypeCheckExpr::visit (HIR::NegationExpr &expr)
 {
   auto negated_expr_ty = TypeCheckExpr::Resolve (expr.get_expr ());
 
+  if (negated_expr_ty->is<TyTy::ErrorType> ())
+    return;
+
   // check for operator overload
   auto lang_item_type
     = LangItem::NegationOperatorToLangItem (expr.get_expr_type ());
@@ -1232,6 +1252,9 @@ TypeCheckExpr::visit (HIR::ArrayExpr &expr)
          = TyTy::TyVar::get_implicit_infer_var (expr.get_locus ()).get_tyty ();
        for (auto &type : types)
          {
+           if (type->is<TyTy::ErrorType> ())
+             return;
+
            element_type
              = unify_site (expr.get_mappings ().get_hirid (),
                            TyTy::TyWithLocation (element_type),
@@ -1239,6 +1262,11 @@ TypeCheckExpr::visit (HIR::ArrayExpr &expr)
                            expr.get_locus ());
          }
 
+       // a failed unify leaves the shared error node here, whose ref
+       // isn't in the context
+       if (element_type->is<TyTy::ErrorType> ())
+         return;
+
        auto crate_num = mappings.crate.get_current_crate ();
        Analysis::NodeMapping mapping (crate_num, UNKNOWN_NODEID,
                                       mappings.get_next_hir_id (crate_num),
diff --git a/gcc/rust/typecheck/rust-hir-type-check-item.cc 
b/gcc/rust/typecheck/rust-hir-type-check-item.cc
index 1f66d68acaf..1b974b5cfb3 100644
--- a/gcc/rust/typecheck/rust-hir-type-check-item.cc
+++ b/gcc/rust/typecheck/rust-hir-type-check-item.cc
@@ -738,12 +738,13 @@ TypeCheckItem::visit (HIR::StaticItem &var)
   TyTy::BaseType *type = TypeCheckType::Resolve (var.get_type ());
   TyTy::BaseType *expr_type = TypeCheckExpr::Resolve (var.get_expr ());
 
-  TyTy::BaseType *unified
-    = coercion_site (var.get_mappings ().get_hirid (),
-                    TyTy::TyWithLocation (type, var.get_type ().get_locus ()),
-                    TyTy::TyWithLocation (expr_type,
-                                          var.get_expr ().get_locus ()),
-                    var.get_locus ());
+  TyTy::BaseType *unified = type;
+  if (!expr_type->is<TyTy::ErrorType> ())
+    unified = coercion_site (
+      var.get_mappings ().get_hirid (),
+      TyTy::TyWithLocation (type, var.get_type ().get_locus ()),
+      TyTy::TyWithLocation (expr_type, var.get_expr ().get_locus ()),
+      var.get_locus ());
   context->insert_type (var.get_mappings (), unified);
   infered = unified;
 }
@@ -756,11 +757,14 @@ TypeCheckItem::visit (HIR::ConstantItem &constant)
   TyTy::BaseType *expr_type = TypeCheckExpr::Resolve (constant.get_expr ());
   context->pop_const_context ();
 
-  TyTy::BaseType *unified = coercion_site (
-    constant.get_mappings ().get_hirid (),
-    TyTy::TyWithLocation (type, constant.get_type ().get_locus ()),
-    TyTy::TyWithLocation (expr_type, constant.get_expr ().get_locus ()),
-    constant.get_locus ());
+  // the declared type stands if the initializer failed
+  TyTy::BaseType *unified = type;
+  if (!expr_type->is<TyTy::ErrorType> ())
+    unified = coercion_site (
+      constant.get_mappings ().get_hirid (),
+      TyTy::TyWithLocation (type, constant.get_type ().get_locus ()),
+      TyTy::TyWithLocation (expr_type, constant.get_expr ().get_locus ()),
+      constant.get_locus ());
   context->insert_type (constant.get_mappings (), unified);
   infered = unified;
 }
diff --git a/gcc/testsuite/rust/compile/issues/4165.rs 
b/gcc/testsuite/rust/compile/issues/4165.rs
index 673c44702e2..e5fd9fec999 100644
--- a/gcc/testsuite/rust/compile/issues/4165.rs
+++ b/gcc/testsuite/rust/compile/issues/4165.rs
@@ -7,7 +7,6 @@ const ARR: [i32; N] = [42; X];
 // { dg-error {mismatched types, expected .usize. but got .bool. \[E0308\]} "" 
{ target *-*-* } .-2 }
 const X: bool = (N[0] == 99) && (ARR[0] == 0);
 // { dg-error {the type .usize. cannot be indexed by .<integer>. \[E0277\]} "" 
{ target *-*-* } .-1 }
-// { dg-error {mismatched types, expected .<tyty::error>. but got .<integer>. 
\[E0308\]} "" { target *-*-* } .-2 }
 
 fn main() {
     let _ = X;
diff --git a/gcc/testsuite/rust/compile/issues/issue-5039-2.rs 
b/gcc/testsuite/rust/compile/issues/issue-5039-2.rs
new file mode 100644
index 00000000000..71a7fdb8479
--- /dev/null
+++ b/gcc/testsuite/rust/compile/issues/issue-5039-2.rs
@@ -0,0 +1,14 @@
+#![feature(no_core, lang_items)]
+#![no_core]
+
+// Rust-GCC/gccrs#5039: an error-typed array element crashed when the array
+// was coerced to a slice.
+
+#[lang = "sized"]
+pub trait Sized {}
+
+pub fn g(_s: &[u32]) {}
+
+pub fn f(v: &[u32]) {
+    g(&[v.nope(), 1]); // { dg-error "no method named .nope." }
+}
diff --git a/gcc/testsuite/rust/compile/issues/issue-5039.rs 
b/gcc/testsuite/rust/compile/issues/issue-5039.rs
new file mode 100644
index 00000000000..6a508c00fb1
--- /dev/null
+++ b/gcc/testsuite/rust/compile/issues/issue-5039.rs
@@ -0,0 +1,25 @@
+#![feature(no_core, lang_items)]
+#![no_core]
+
+// Rust-GCC/gccrs#5039: applying an operator to an error-typed value crashed
+// while resolving the operator overload.
+
+#[lang = "sized"]
+pub trait Sized {}
+
+#[lang = "mul"]
+pub trait Mul<Rhs> {
+    type Output;
+    fn mul(self, rhs: Rhs) -> Self::Output;
+}
+
+impl Mul<i32> for i32 {
+    type Output = i32;
+    fn mul(self, rhs: i32) -> i32 {
+        self
+    }
+}
+
+pub fn f(v: &[u32]) {
+    let _ = v.nope() * 2; // { dg-error "no method named .nope." }
+}
-- 
2.55.0

Reply via email to