From a293619caad49bb0e8d9a4d417bedf50f31d1b06 Mon Sep 17 00:00:00 2001 From: Sebastian Toh Date: Sun, 27 Aug 2023 20:14:56 +0800 Subject: [PATCH 1/3] Add note that str cannot be matched exhaustively --- .../src/thir/pattern/check_match.rs | 16 ++++++++++------ .../issue-105479-str-non-exhaustiveness.rs | 12 ++++++++++++ .../issue-105479-str-non-exhaustiveness.stderr | 17 +++++++++++++++++ tests/ui/pattern/usefulness/issue-30240.stderr | 2 ++ 4 files changed, 41 insertions(+), 6 deletions(-) create mode 100644 tests/ui/pattern/usefulness/issue-105479-str-non-exhaustiveness.rs create mode 100644 tests/ui/pattern/usefulness/issue-105479-str-non-exhaustiveness.stderr diff --git a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs index f46cf0dc0ff1b..cce2a8c5259a4 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs @@ -735,17 +735,21 @@ fn non_exhaustive_match<'p, 'tcx>( collect_non_exhaustive_tys(&witnesses[0], &mut non_exhaustive_tys); for ty in non_exhaustive_tys { - if ty == cx.tcx.types.usize || ty == cx.tcx.types.isize { + if ty.is_ptr_sized_integral() { err.note(format!( "`{ty}` does not have a fixed maximum value, so a wildcard `_` is necessary to match \ - exhaustively", - )); + exhaustively", + )); if cx.tcx.sess.is_nightly_build() { err.help(format!( - "add `#![feature(precise_pointer_size_matching)]` to the crate attributes to \ - enable precise `{ty}` matching", - )); + "add `#![feature(precise_pointer_size_matching)]` to the crate attributes to \ + enable precise `{ty}` matching", + )); } + } else if ty == cx.tcx.types.str_ { + err.note(format!( + "`{ty}` cannot be matched exhaustively, so a wildcard `_` is necessary", + )); } } } diff --git a/tests/ui/pattern/usefulness/issue-105479-str-non-exhaustiveness.rs b/tests/ui/pattern/usefulness/issue-105479-str-non-exhaustiveness.rs new file mode 100644 index 0000000000000..faf37b07513f6 --- /dev/null +++ b/tests/ui/pattern/usefulness/issue-105479-str-non-exhaustiveness.rs @@ -0,0 +1,12 @@ +fn main() { + let a = ""; + let b = ""; + match (a, b) { + //~^ ERROR non-exhaustive patterns: `(&_, _)` not covered [E0004] + //~| NOTE pattern `(&_, _)` not covered + //~| NOTE the matched value is of type `(&str, &str)` + //~| NOTE `str` cannot be matched exhaustively, so a wildcard `_` is necessary + ("a", "b") => {} + ("c", "d") => {} + } +} diff --git a/tests/ui/pattern/usefulness/issue-105479-str-non-exhaustiveness.stderr b/tests/ui/pattern/usefulness/issue-105479-str-non-exhaustiveness.stderr new file mode 100644 index 0000000000000..5c09b3bada6b9 --- /dev/null +++ b/tests/ui/pattern/usefulness/issue-105479-str-non-exhaustiveness.stderr @@ -0,0 +1,17 @@ +error[E0004]: non-exhaustive patterns: `(&_, _)` not covered + --> $DIR/issue-105479-str-non-exhaustiveness.rs:4:11 + | +LL | match (a, b) { + | ^^^^^^ pattern `(&_, _)` not covered + | + = note: the matched value is of type `(&str, &str)` + = note: `str` cannot be matched exhaustively, so a wildcard `_` is necessary +help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown + | +LL ~ ("c", "d") => {}, +LL + (&_, _) => todo!() + | + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0004`. diff --git a/tests/ui/pattern/usefulness/issue-30240.stderr b/tests/ui/pattern/usefulness/issue-30240.stderr index ff755d681ac73..736ab34e16468 100644 --- a/tests/ui/pattern/usefulness/issue-30240.stderr +++ b/tests/ui/pattern/usefulness/issue-30240.stderr @@ -5,6 +5,7 @@ LL | match "world" { | ^^^^^^^ pattern `&_` not covered | = note: the matched value is of type `&str` + = note: `str` cannot be matched exhaustively, so a wildcard `_` is necessary help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown | LL ~ "hello" => {}, @@ -18,6 +19,7 @@ LL | match "world" { | ^^^^^^^ pattern `&_` not covered | = note: the matched value is of type `&str` + = note: `str` cannot be matched exhaustively, so a wildcard `_` is necessary help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown | LL ~ "hello" => {}, From 43dd8613a308745a9f75690bb5365f8b8ca05926 Mon Sep 17 00:00:00 2001 From: Sebastian Toh Date: Mon, 28 Aug 2023 14:25:52 +0800 Subject: [PATCH 2/3] Add note when matching on nested non-exhaustive enums --- .../src/thir/pattern/check_match.rs | 11 ++--- .../src/thir/pattern/usefulness.rs | 49 ++++++++++--------- .../match/non-exhaustive-match.stderr | 3 +- tests/ui/match/match_non_exhaustive.stderr | 3 +- .../usefulness/auxiliary/non-exhaustive.rs | 2 + .../usefulness/nested-non-exhaustive-enums.rs | 18 +++++++ .../nested-non-exhaustive-enums.stderr | 22 +++++++++ .../rfcs/rfc-2008-non-exhaustive/enum.stderr | 5 +- 8 files changed, 78 insertions(+), 35 deletions(-) create mode 100644 tests/ui/pattern/usefulness/auxiliary/non-exhaustive.rs create mode 100644 tests/ui/pattern/usefulness/nested-non-exhaustive-enums.rs create mode 100644 tests/ui/pattern/usefulness/nested-non-exhaustive-enums.stderr diff --git a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs index cce2a8c5259a4..38c24e0ab5ee3 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs @@ -720,15 +720,8 @@ fn non_exhaustive_match<'p, 'tcx>( }; }; - let is_variant_list_non_exhaustive = matches!(scrut_ty.kind(), - ty::Adt(def, _) if def.is_variant_list_non_exhaustive() && !def.did().is_local()); - adt_defined_here(cx, &mut err, scrut_ty, &witnesses); - err.note(format!( - "the matched value is of type `{}`{}", - scrut_ty, - if is_variant_list_non_exhaustive { ", which is marked as non-exhaustive" } else { "" } - )); + err.note(format!("the matched value is of type `{}`", scrut_ty)); if !is_empty_match && witnesses.len() == 1 { let mut non_exhaustive_tys = FxHashSet::default(); @@ -750,6 +743,8 @@ fn non_exhaustive_match<'p, 'tcx>( err.note(format!( "`{ty}` cannot be matched exhaustively, so a wildcard `_` is necessary", )); + } else if cx.is_foreign_non_exhaustive_enum(ty) { + err.note(format!("`{ty}` is marked as non-exhaustive")); } } } diff --git a/compiler/rustc_mir_build/src/thir/pattern/usefulness.rs b/compiler/rustc_mir_build/src/thir/pattern/usefulness.rs index 08cfe98bb68b9..21031e8ba9d7e 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/usefulness.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/usefulness.rs @@ -618,10 +618,15 @@ impl<'p, 'tcx> Usefulness<'p, 'tcx> { let new_witnesses = if let Constructor::Missing { .. } = ctor { // We got the special `Missing` constructor, so each of the missing constructors // gives a new pattern that is not caught by the match. We list those patterns. - let new_patterns = if pcx.is_non_exhaustive { - // Here we don't want the user to try to list all variants, we want them to add - // a wildcard, so we only suggest that. - vec![DeconstructedPat::wildcard(pcx.ty, pcx.span)] + if pcx.is_non_exhaustive { + witnesses + .into_iter() + // Here we don't want the user to try to list all variants, we want them to add + // a wildcard, so we only suggest that. + .map(|witness| { + witness.apply_constructor(pcx, &Constructor::NonExhaustive) + }) + .collect() } else { let mut split_wildcard = SplitWildcard::new(pcx); split_wildcard.split(pcx, matrix.heads().map(DeconstructedPat::ctor)); @@ -633,7 +638,7 @@ impl<'p, 'tcx> Usefulness<'p, 'tcx> { // constructor, that matches everything that can be built with // it. For example, if `ctor` is a `Constructor::Variant` for // `Option::Some`, we get the pattern `Some(_)`. - let mut new: Vec> = split_wildcard + let mut new_patterns: Vec> = split_wildcard .iter_missing(pcx) .filter_map(|missing_ctor| { // Check if this variant is marked `doc(hidden)` @@ -648,27 +653,25 @@ impl<'p, 'tcx> Usefulness<'p, 'tcx> { .collect(); if hide_variant_show_wild { - new.push(DeconstructedPat::wildcard(pcx.ty, pcx.span)); + new_patterns.push(DeconstructedPat::wildcard(pcx.ty, pcx.span)); } - new - }; - - witnesses - .into_iter() - .flat_map(|witness| { - new_patterns.iter().map(move |pat| { - Witness( - witness - .0 - .iter() - .chain(once(pat)) - .map(DeconstructedPat::clone_and_forget_reachability) - .collect(), - ) + witnesses + .into_iter() + .flat_map(|witness| { + new_patterns.iter().map(move |pat| { + Witness( + witness + .0 + .iter() + .chain(once(pat)) + .map(DeconstructedPat::clone_and_forget_reachability) + .collect(), + ) + }) }) - }) - .collect() + .collect() + } } else { witnesses .into_iter() diff --git a/tests/ui/closures/2229_closure_analysis/match/non-exhaustive-match.stderr b/tests/ui/closures/2229_closure_analysis/match/non-exhaustive-match.stderr index 3a5fad15421c6..fddd769c3dfc3 100644 --- a/tests/ui/closures/2229_closure_analysis/match/non-exhaustive-match.stderr +++ b/tests/ui/closures/2229_closure_analysis/match/non-exhaustive-match.stderr @@ -45,7 +45,8 @@ note: `E2` defined here | LL | pub enum E2 { A, B } | ^^^^^^^^^^^ - = note: the matched value is of type `E2`, which is marked as non-exhaustive + = note: the matched value is of type `E2` + = note: `E2` is marked as non-exhaustive help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown | LL | let _e = || { match e2 { E2::A => (), E2::B => (), _ => todo!() } }; diff --git a/tests/ui/match/match_non_exhaustive.stderr b/tests/ui/match/match_non_exhaustive.stderr index 46ee8d5179e6b..d07e284e299bb 100644 --- a/tests/ui/match/match_non_exhaustive.stderr +++ b/tests/ui/match/match_non_exhaustive.stderr @@ -45,7 +45,8 @@ note: `E2` defined here | LL | pub enum E2 { A, B } | ^^^^^^^^^^^ - = note: the matched value is of type `E2`, which is marked as non-exhaustive + = note: the matched value is of type `E2` + = note: `E2` is marked as non-exhaustive help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown | LL | match e2 { E2::A => (), E2::B => (), _ => todo!() }; diff --git a/tests/ui/pattern/usefulness/auxiliary/non-exhaustive.rs b/tests/ui/pattern/usefulness/auxiliary/non-exhaustive.rs new file mode 100644 index 0000000000000..6f459b8268f6f --- /dev/null +++ b/tests/ui/pattern/usefulness/auxiliary/non-exhaustive.rs @@ -0,0 +1,2 @@ +#[non_exhaustive] +pub enum NonExhaustiveEnum { A, B } diff --git a/tests/ui/pattern/usefulness/nested-non-exhaustive-enums.rs b/tests/ui/pattern/usefulness/nested-non-exhaustive-enums.rs new file mode 100644 index 0000000000000..3a8a74d1fd65f --- /dev/null +++ b/tests/ui/pattern/usefulness/nested-non-exhaustive-enums.rs @@ -0,0 +1,18 @@ +// aux-build:non-exhaustive.rs + +extern crate non_exhaustive; + +use non_exhaustive::NonExhaustiveEnum; + +fn main() { + match Some(NonExhaustiveEnum::A) { + //~^ ERROR non-exhaustive patterns: `Some(_)` not covered [E0004] + //~| NOTE pattern `Some(_)` not covered + //~| NOTE `Option` defined here + //~| NOTE the matched value is of type `Option` + //~| NOTE `NonExhaustiveEnum` is marked as non-exhaustive + Some(NonExhaustiveEnum::A) => {} + Some(NonExhaustiveEnum::B) => {} + None => {} + } +} diff --git a/tests/ui/pattern/usefulness/nested-non-exhaustive-enums.stderr b/tests/ui/pattern/usefulness/nested-non-exhaustive-enums.stderr new file mode 100644 index 0000000000000..ae81f307fde34 --- /dev/null +++ b/tests/ui/pattern/usefulness/nested-non-exhaustive-enums.stderr @@ -0,0 +1,22 @@ +error[E0004]: non-exhaustive patterns: `Some(_)` not covered + --> $DIR/nested-non-exhaustive-enums.rs:8:11 + | +LL | match Some(NonExhaustiveEnum::A) { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ pattern `Some(_)` not covered + | +note: `Option` defined here + --> $SRC_DIR/core/src/option.rs:LL:COL + ::: $SRC_DIR/core/src/option.rs:LL:COL + | + = note: not covered + = note: the matched value is of type `Option` + = note: `NonExhaustiveEnum` is marked as non-exhaustive +help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown + | +LL ~ None => {}, +LL + Some(_) => todo!() + | + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0004`. diff --git a/tests/ui/rfcs/rfc-2008-non-exhaustive/enum.stderr b/tests/ui/rfcs/rfc-2008-non-exhaustive/enum.stderr index 872cb9b8bc68e..50209e18bd173 100644 --- a/tests/ui/rfcs/rfc-2008-non-exhaustive/enum.stderr +++ b/tests/ui/rfcs/rfc-2008-non-exhaustive/enum.stderr @@ -28,7 +28,8 @@ note: `NonExhaustiveEnum` defined here | LL | pub enum NonExhaustiveEnum { | ^^^^^^^^^^^^^^^^^^^^^^^^^^ - = note: the matched value is of type `NonExhaustiveEnum`, which is marked as non-exhaustive + = note: the matched value is of type `NonExhaustiveEnum` + = note: `NonExhaustiveEnum` is marked as non-exhaustive help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown | LL ~ NonExhaustiveEnum::Struct { .. } => "third", @@ -46,7 +47,7 @@ note: `NonExhaustiveEnum` defined here | LL | pub enum NonExhaustiveEnum { | ^^^^^^^^^^^^^^^^^^^^^^^^^^ - = note: the matched value is of type `NonExhaustiveEnum`, which is marked as non-exhaustive + = note: the matched value is of type `NonExhaustiveEnum` help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown | LL ~ match enum_unit { From d87b87d10e630eb0fb9cd0bf11c70ee869f077b7 Mon Sep 17 00:00:00 2001 From: Sebastian Toh Date: Sun, 3 Sep 2023 19:55:11 +0800 Subject: [PATCH 3/3] Improve clarity of diagnostic message on non-exhaustive matches --- compiler/rustc_mir_build/src/thir/pattern/check_match.rs | 4 ++-- .../2229_closure_analysis/match/non-exhaustive-match.stderr | 2 +- tests/ui/match/match_non_exhaustive.stderr | 2 +- .../pattern/usefulness/issue-105479-str-non-exhaustiveness.rs | 2 +- .../usefulness/issue-105479-str-non-exhaustiveness.stderr | 2 +- tests/ui/pattern/usefulness/issue-30240.stderr | 4 ++-- .../ui/pattern/usefulness/nested-non-exhaustive-enums.stderr | 2 +- tests/ui/rfcs/rfc-2008-non-exhaustive/enum.stderr | 2 +- 8 files changed, 10 insertions(+), 10 deletions(-) diff --git a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs index 38c24e0ab5ee3..95dced644e161 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs @@ -741,10 +741,10 @@ fn non_exhaustive_match<'p, 'tcx>( } } else if ty == cx.tcx.types.str_ { err.note(format!( - "`{ty}` cannot be matched exhaustively, so a wildcard `_` is necessary", + "`&str` cannot be matched exhaustively, so a wildcard `_` is necessary", )); } else if cx.is_foreign_non_exhaustive_enum(ty) { - err.note(format!("`{ty}` is marked as non-exhaustive")); + err.note(format!("`{ty}` is marked as non-exhaustive, so a wildcard `_` is necessary to match exhaustively")); } } } diff --git a/tests/ui/closures/2229_closure_analysis/match/non-exhaustive-match.stderr b/tests/ui/closures/2229_closure_analysis/match/non-exhaustive-match.stderr index fddd769c3dfc3..0807f4590298f 100644 --- a/tests/ui/closures/2229_closure_analysis/match/non-exhaustive-match.stderr +++ b/tests/ui/closures/2229_closure_analysis/match/non-exhaustive-match.stderr @@ -46,7 +46,7 @@ note: `E2` defined here LL | pub enum E2 { A, B } | ^^^^^^^^^^^ = note: the matched value is of type `E2` - = note: `E2` is marked as non-exhaustive + = note: `E2` is marked as non-exhaustive, so a wildcard `_` is necessary to match exhaustively help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown | LL | let _e = || { match e2 { E2::A => (), E2::B => (), _ => todo!() } }; diff --git a/tests/ui/match/match_non_exhaustive.stderr b/tests/ui/match/match_non_exhaustive.stderr index d07e284e299bb..7b8bdfe0053ae 100644 --- a/tests/ui/match/match_non_exhaustive.stderr +++ b/tests/ui/match/match_non_exhaustive.stderr @@ -46,7 +46,7 @@ note: `E2` defined here LL | pub enum E2 { A, B } | ^^^^^^^^^^^ = note: the matched value is of type `E2` - = note: `E2` is marked as non-exhaustive + = note: `E2` is marked as non-exhaustive, so a wildcard `_` is necessary to match exhaustively help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown | LL | match e2 { E2::A => (), E2::B => (), _ => todo!() }; diff --git a/tests/ui/pattern/usefulness/issue-105479-str-non-exhaustiveness.rs b/tests/ui/pattern/usefulness/issue-105479-str-non-exhaustiveness.rs index faf37b07513f6..0ee7856c6803a 100644 --- a/tests/ui/pattern/usefulness/issue-105479-str-non-exhaustiveness.rs +++ b/tests/ui/pattern/usefulness/issue-105479-str-non-exhaustiveness.rs @@ -5,7 +5,7 @@ fn main() { //~^ ERROR non-exhaustive patterns: `(&_, _)` not covered [E0004] //~| NOTE pattern `(&_, _)` not covered //~| NOTE the matched value is of type `(&str, &str)` - //~| NOTE `str` cannot be matched exhaustively, so a wildcard `_` is necessary + //~| NOTE `&str` cannot be matched exhaustively, so a wildcard `_` is necessary ("a", "b") => {} ("c", "d") => {} } diff --git a/tests/ui/pattern/usefulness/issue-105479-str-non-exhaustiveness.stderr b/tests/ui/pattern/usefulness/issue-105479-str-non-exhaustiveness.stderr index 5c09b3bada6b9..771fc320a13f6 100644 --- a/tests/ui/pattern/usefulness/issue-105479-str-non-exhaustiveness.stderr +++ b/tests/ui/pattern/usefulness/issue-105479-str-non-exhaustiveness.stderr @@ -5,7 +5,7 @@ LL | match (a, b) { | ^^^^^^ pattern `(&_, _)` not covered | = note: the matched value is of type `(&str, &str)` - = note: `str` cannot be matched exhaustively, so a wildcard `_` is necessary + = note: `&str` cannot be matched exhaustively, so a wildcard `_` is necessary help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown | LL ~ ("c", "d") => {}, diff --git a/tests/ui/pattern/usefulness/issue-30240.stderr b/tests/ui/pattern/usefulness/issue-30240.stderr index 736ab34e16468..da8bbdffbf6d5 100644 --- a/tests/ui/pattern/usefulness/issue-30240.stderr +++ b/tests/ui/pattern/usefulness/issue-30240.stderr @@ -5,7 +5,7 @@ LL | match "world" { | ^^^^^^^ pattern `&_` not covered | = note: the matched value is of type `&str` - = note: `str` cannot be matched exhaustively, so a wildcard `_` is necessary + = note: `&str` cannot be matched exhaustively, so a wildcard `_` is necessary help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown | LL ~ "hello" => {}, @@ -19,7 +19,7 @@ LL | match "world" { | ^^^^^^^ pattern `&_` not covered | = note: the matched value is of type `&str` - = note: `str` cannot be matched exhaustively, so a wildcard `_` is necessary + = note: `&str` cannot be matched exhaustively, so a wildcard `_` is necessary help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown | LL ~ "hello" => {}, diff --git a/tests/ui/pattern/usefulness/nested-non-exhaustive-enums.stderr b/tests/ui/pattern/usefulness/nested-non-exhaustive-enums.stderr index ae81f307fde34..9fbd871db7c9d 100644 --- a/tests/ui/pattern/usefulness/nested-non-exhaustive-enums.stderr +++ b/tests/ui/pattern/usefulness/nested-non-exhaustive-enums.stderr @@ -10,7 +10,7 @@ note: `Option` defined here | = note: not covered = note: the matched value is of type `Option` - = note: `NonExhaustiveEnum` is marked as non-exhaustive + = note: `NonExhaustiveEnum` is marked as non-exhaustive, so a wildcard `_` is necessary to match exhaustively help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown | LL ~ None => {}, diff --git a/tests/ui/rfcs/rfc-2008-non-exhaustive/enum.stderr b/tests/ui/rfcs/rfc-2008-non-exhaustive/enum.stderr index 50209e18bd173..4e7f3098ab482 100644 --- a/tests/ui/rfcs/rfc-2008-non-exhaustive/enum.stderr +++ b/tests/ui/rfcs/rfc-2008-non-exhaustive/enum.stderr @@ -29,7 +29,7 @@ note: `NonExhaustiveEnum` defined here LL | pub enum NonExhaustiveEnum { | ^^^^^^^^^^^^^^^^^^^^^^^^^^ = note: the matched value is of type `NonExhaustiveEnum` - = note: `NonExhaustiveEnum` is marked as non-exhaustive + = note: `NonExhaustiveEnum` is marked as non-exhaustive, so a wildcard `_` is necessary to match exhaustively help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown | LL ~ NonExhaustiveEnum::Struct { .. } => "third",