From 1dc4e417fa2a73d5ffa24428df6b7d4dbbf41f4a Mon Sep 17 00:00:00 2001 From: Oliver Scherer Date: Fri, 18 Jan 2019 11:40:53 +0100 Subject: [PATCH 1/9] Manually inline a function that was only used once --- src/librustc/ty/mod.rs | 10 ---------- src/librustc_mir/interpret/eval_context.rs | 10 +++++----- 2 files changed, 5 insertions(+), 15 deletions(-) diff --git a/src/librustc/ty/mod.rs b/src/librustc/ty/mod.rs index 26b4735d926a5..2fdcb2681db0b 100644 --- a/src/librustc/ty/mod.rs +++ b/src/librustc/ty/mod.rs @@ -2946,16 +2946,6 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> { } } - /// Given the DefId of an item, returns its MIR, borrowed immutably. - /// Returns None if there is no MIR for the DefId - pub fn maybe_optimized_mir(self, did: DefId) -> Option<&'gcx Mir<'gcx>> { - if self.is_mir_available(did) { - Some(self.optimized_mir(did)) - } else { - None - } - } - /// Get the attributes of a definition. pub fn get_attrs(self, did: DefId) -> Attributes<'gcx> { if let Some(id) = self.hir().as_local_node_id(did) { diff --git a/src/librustc_mir/interpret/eval_context.rs b/src/librustc_mir/interpret/eval_context.rs index 19362b6cfdb1c..90f7ba761ea1d 100644 --- a/src/librustc_mir/interpret/eval_context.rs +++ b/src/librustc_mir/interpret/eval_context.rs @@ -265,11 +265,11 @@ impl<'a, 'mir, 'tcx: 'mir, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tc } trace!("load mir {:?}", instance); match instance { - ty::InstanceDef::Item(def_id) => { - self.tcx.maybe_optimized_mir(def_id).ok_or_else(|| - EvalErrorKind::NoMirFor(self.tcx.item_path_str(def_id)).into() - ) - } + ty::InstanceDef::Item(def_id) => if self.tcx.is_mir_available(did) { + Ok(self.tcx.optimized_mir(did)) + } else { + err!(NoMirFor(self.tcx.item_path_str(def_id))) + }, _ => Ok(self.tcx.instance_mir(instance)), } } From efda6816bd29beea2702c7c3f76a252a405e60ae Mon Sep 17 00:00:00 2001 From: Oliver Scherer Date: Fri, 18 Jan 2019 13:31:05 +0100 Subject: [PATCH 2/9] Allow evaluating trivial drop glue in constants --- src/librustc_mir/const_eval.rs | 29 ++++++++++++++++------------- src/test/ui/consts/drop_none.rs | 13 +++++++++++++ 2 files changed, 29 insertions(+), 13 deletions(-) create mode 100644 src/test/ui/consts/drop_none.rs diff --git a/src/librustc_mir/const_eval.rs b/src/librustc_mir/const_eval.rs index f5f4048167938..f6ecf7c9436a2 100644 --- a/src/librustc_mir/const_eval.rs +++ b/src/librustc_mir/const_eval.rs @@ -391,19 +391,22 @@ impl<'a, 'mir, 'tcx> interpret::Machine<'a, 'mir, 'tcx> ret: Option, ) -> EvalResult<'tcx, Option<&'mir mir::Mir<'tcx>>> { debug!("eval_fn_call: {:?}", instance); - // Execution might have wandered off into other crates, so we cannot to a stability- - // sensitive check here. But we can at least rule out functions that are not const - // at all. - if !ecx.tcx.is_const_fn_raw(instance.def_id()) { - // Some functions we support even if they are non-const -- but avoid testing - // that for const fn! We certainly do *not* want to actually call the fn - // though, so be sure we return here. - return if ecx.hook_fn(instance, args, dest)? { - ecx.goto_block(ret)?; // fully evaluated and done - Ok(None) - } else { - err!(MachineError(format!("calling non-const function `{}`", instance))) - }; + // Only check non-glue functions + if let ty::InstanceDef::Item(def_id) = instance.def { + // Execution might have wandered off into other crates, so we cannot to a stability- + // sensitive check here. But we can at least rule out functions that are not const + // at all. + if !ecx.tcx.is_const_fn_raw(def_id) { + // Some functions we support even if they are non-const -- but avoid testing + // that for const fn! We certainly do *not* want to actually call the fn + // though, so be sure we return here. + return if ecx.hook_fn(instance, args, dest)? { + ecx.goto_block(ret)?; // fully evaluated and done + Ok(None) + } else { + err!(MachineError(format!("calling non-const function `{}`", instance))) + }; + } } // This is a const fn. Call it. Ok(Some(match ecx.load_mir(instance.def) { diff --git a/src/test/ui/consts/drop_none.rs b/src/test/ui/consts/drop_none.rs new file mode 100644 index 0000000000000..86a197ffb993e --- /dev/null +++ b/src/test/ui/consts/drop_none.rs @@ -0,0 +1,13 @@ +// compile-pass +#![allow(dead_code)] +struct A; +impl Drop for A { + fn drop(&mut self) {} +} + +const FOO: Option = None; + +const BAR: () = (FOO, ()).1; + + +fn main() {} From a3383514d9db48295124b349323379952c33a2c4 Mon Sep 17 00:00:00 2001 From: kenta7777 Date: Mon, 10 Dec 2018 23:45:33 +0900 Subject: [PATCH 3/9] reduce some code repetitions --- src/librustc_lint/types.rs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/librustc_lint/types.rs b/src/librustc_lint/types.rs index 642681a73a8a0..6e394f78226cd 100644 --- a/src/librustc_lint/types.rs +++ b/src/librustc_lint/types.rs @@ -21,6 +21,8 @@ use syntax::source_map; use rustc::hir; +use rustc::mir::interpret::{sign_extend, truncate}; + declare_lint! { UNUSED_COMPARISONS, Warn, @@ -368,14 +370,14 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for TypeLimits { let (t, actually) = match ty { ty::Int(t) => { let ity = attr::IntType::SignedInt(t); - let bits = layout::Integer::from_attr(&cx.tcx, ity).size().bits(); - let actually = (val << (128 - bits)) as i128 >> (128 - bits); + let size = layout::Integer::from_attr(&cx.tcx, ity).size(); + let actually = sign_extend(val, size); (format!("{:?}", t), actually.to_string()) } ty::Uint(t) => { let ity = attr::IntType::UnsignedInt(t); - let bits = layout::Integer::from_attr(&cx.tcx, ity).size().bits(); - let actually = (val << (128 - bits)) >> (128 - bits); + let size = layout::Integer::from_attr(&cx.tcx, ity).size(); + let actually = truncate(val, size); (format!("{:?}", t), actually.to_string()) } _ => bug!(), From b80332ed4c142496096ff2bf9aa378cc6b3bf806 Mon Sep 17 00:00:00 2001 From: kenta7777 Date: Sun, 20 Jan 2019 10:45:25 +0900 Subject: [PATCH 4/9] cast the sign_extend result to i128. --- src/librustc_lint/types.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/librustc_lint/types.rs b/src/librustc_lint/types.rs index 6e394f78226cd..9d3275ffde2c0 100644 --- a/src/librustc_lint/types.rs +++ b/src/librustc_lint/types.rs @@ -371,7 +371,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for TypeLimits { ty::Int(t) => { let ity = attr::IntType::SignedInt(t); let size = layout::Integer::from_attr(&cx.tcx, ity).size(); - let actually = sign_extend(val, size); + let actually = sign_extend(val, size) as i128; (format!("{:?}", t), actually.to_string()) } ty::Uint(t) => { From af153cef0ce27e3b525c2c161afe469d2ffdbb57 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C3=A4r=20Karlsson?= Date: Fri, 25 Jan 2019 08:43:56 +0100 Subject: [PATCH 5/9] Fix wording in diagnostics page --- src/librustc_typeck/diagnostics.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/librustc_typeck/diagnostics.rs b/src/librustc_typeck/diagnostics.rs index c0a8dd87ee2d5..1f260d317e4c6 100644 --- a/src/librustc_typeck/diagnostics.rs +++ b/src/librustc_typeck/diagnostics.rs @@ -763,7 +763,7 @@ function's return type and the value being returned. "##, E0070: r##" -The left-hand side of an assignment operator must be a place expression. An +The left-hand side of an assignment operator must be a place expression. A place expression represents a memory location and can be a variable (with optional namespacing), a dereference, an indexing expression or a field reference. From 506393eaaf912ece2d216de4b6e2fd7ca04a945d Mon Sep 17 00:00:00 2001 From: Oliver Scherer Date: Wed, 23 Jan 2019 16:21:33 +0100 Subject: [PATCH 6/9] Add a compile-fail test for `Drop` in constants in the presence of `Option`s --- src/test/ui/static/static-drop-scope.nll.stderr | 14 +++++++++++++- src/test/ui/static/static-drop-scope.rs | 8 ++++++++ src/test/ui/static/static-drop-scope.stderr | 14 +++++++++++++- 3 files changed, 34 insertions(+), 2 deletions(-) diff --git a/src/test/ui/static/static-drop-scope.nll.stderr b/src/test/ui/static/static-drop-scope.nll.stderr index 3c01f694bff47..df6383b4fc222 100644 --- a/src/test/ui/static/static-drop-scope.nll.stderr +++ b/src/test/ui/static/static-drop-scope.nll.stderr @@ -54,7 +54,19 @@ error[E0493]: destructors cannot be evaluated at compile-time LL | (x, ()).1 | ^^^^^^^ constant functions cannot evaluate destructors -error: aborting due to 8 previous errors +error[E0493]: destructors cannot be evaluated at compile-time + --> $DIR/static-drop-scope.rs:31:34 + | +LL | const EARLY_DROP_C_OPTION: i32 = (Some(WithDtor), 0).1; + | ^^^^^^^^^^^^^^^^^^^ constants cannot evaluate destructors + +error[E0493]: destructors cannot be evaluated at compile-time + --> $DIR/static-drop-scope.rs:36:43 + | +LL | const EARLY_DROP_C_OPTION_CONSTANT: i32 = (HELPER, 0).1; + | ^^^^^^^^^^^ constants cannot evaluate destructors + +error: aborting due to 10 previous errors Some errors occurred: E0493, E0716. For more information about an error, try `rustc --explain E0493`. diff --git a/src/test/ui/static/static-drop-scope.rs b/src/test/ui/static/static-drop-scope.rs index a11a9f020e0dc..e5a9f2a405644 100644 --- a/src/test/ui/static/static-drop-scope.rs +++ b/src/test/ui/static/static-drop-scope.rs @@ -28,4 +28,12 @@ const fn const_drop2(x: T) { //~^ ERROR destructors cannot be evaluated at compile-time } +const EARLY_DROP_C_OPTION: i32 = (Some(WithDtor), 0).1; +//~^ ERROR destructors cannot be evaluated at compile-time + +const HELPER: Option = Some(WithDtor); + +const EARLY_DROP_C_OPTION_CONSTANT: i32 = (HELPER, 0).1; +//~^ ERROR destructors cannot be evaluated at compile-time + fn main () {} diff --git a/src/test/ui/static/static-drop-scope.stderr b/src/test/ui/static/static-drop-scope.stderr index 89b31d95a2a4e..3e3032eb4fb60 100644 --- a/src/test/ui/static/static-drop-scope.stderr +++ b/src/test/ui/static/static-drop-scope.stderr @@ -54,7 +54,19 @@ error[E0493]: destructors cannot be evaluated at compile-time LL | (x, ()).1 | ^^^^^^^ constant functions cannot evaluate destructors -error: aborting due to 8 previous errors +error[E0493]: destructors cannot be evaluated at compile-time + --> $DIR/static-drop-scope.rs:31:34 + | +LL | const EARLY_DROP_C_OPTION: i32 = (Some(WithDtor), 0).1; + | ^^^^^^^^^^^^^^^^^^^ constants cannot evaluate destructors + +error[E0493]: destructors cannot be evaluated at compile-time + --> $DIR/static-drop-scope.rs:36:43 + | +LL | const EARLY_DROP_C_OPTION_CONSTANT: i32 = (HELPER, 0).1; + | ^^^^^^^^^^^ constants cannot evaluate destructors + +error: aborting due to 10 previous errors Some errors occurred: E0493, E0597. For more information about an error, try `rustc --explain E0493`. From 463e623ca967c2bd301cc0291fae219130b53daf Mon Sep 17 00:00:00 2001 From: David Wood Date: Thu, 24 Jan 2019 23:24:58 +0100 Subject: [PATCH 7/9] Suggestion moving types before associated types. This commit extends existing suggestions to move lifetimes before types in generic arguments to also suggest moving types behind associated type bindings. --- src/libsyntax/parse/parser.rs | 67 ++++++++++++++----- src/test/ui/parser/issue-32214.stderr | 4 ++ src/test/ui/suggestions/suggest-move-types.rs | 42 ++++++++++++ .../ui/suggestions/suggest-move-types.stderr | 49 ++++++++++++++ 4 files changed, 147 insertions(+), 15 deletions(-) create mode 100644 src/test/ui/suggestions/suggest-move-types.rs create mode 100644 src/test/ui/suggestions/suggest-move-types.stderr diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index c7e33a1656428..232b8bb5966c7 100644 --- a/src/libsyntax/parse/parser.rs +++ b/src/libsyntax/parse/parser.rs @@ -5530,22 +5530,31 @@ impl<'a> Parser<'a> { fn parse_generic_args(&mut self) -> PResult<'a, (Vec, Vec)> { let mut args = Vec::new(); let mut bindings = Vec::new(); + let mut seen_type = false; let mut seen_binding = false; + + let mut last_comma_span = None; let mut first_type_or_binding_span: Option = None; + let mut first_binding_span: Option = None; + let mut bad_lifetime_pos = vec![]; - let mut last_comma_span = None; - let mut suggestions = vec![]; + let mut bad_type_pos = vec![]; + + let mut lifetime_suggestions = vec![]; + let mut type_suggestions = vec![]; loop { if self.check_lifetime() && self.look_ahead(1, |t| !t.is_like_plus()) { // Parse lifetime argument. args.push(GenericArg::Lifetime(self.expect_lifetime())); + if seen_type || seen_binding { let remove_sp = last_comma_span.unwrap_or(self.prev_span).to(self.prev_span); bad_lifetime_pos.push(self.prev_span); + if let Ok(snippet) = self.sess.source_map().span_to_snippet(self.prev_span) { - suggestions.push((remove_sp, String::new())); - suggestions.push(( + lifetime_suggestions.push((remove_sp, String::new())); + lifetime_suggestions.push(( first_type_or_binding_span.unwrap().shrink_to_lo(), format!("{}, ", snippet))); } @@ -5563,24 +5572,29 @@ impl<'a> Parser<'a> { ty, span, }); + seen_binding = true; if first_type_or_binding_span.is_none() { first_type_or_binding_span = Some(span); } + if first_binding_span.is_none() { + first_binding_span = Some(span); + } } else if self.check_type() { // Parse type argument. let ty_param = self.parse_ty()?; if seen_binding { - self.struct_span_err( - ty_param.span, - "type parameters must be declared prior to associated type bindings" - ) - .span_label( - ty_param.span, - "must be declared prior to associated type bindings", - ) - .emit(); + let remove_sp = last_comma_span.unwrap_or(self.prev_span).to(self.prev_span); + bad_type_pos.push(self.prev_span); + + if let Ok(snippet) = self.sess.source_map().span_to_snippet(self.prev_span) { + type_suggestions.push((remove_sp, String::new())); + type_suggestions.push(( + first_binding_span.unwrap().shrink_to_lo(), + format!("{}, ", snippet))); + } } + if first_type_or_binding_span.is_none() { first_type_or_binding_span = Some(ty_param.span); } @@ -5596,6 +5610,7 @@ impl<'a> Parser<'a> { last_comma_span = Some(self.prev_span); } } + if !bad_lifetime_pos.is_empty() { let mut err = self.struct_span_err( bad_lifetime_pos.clone(), @@ -5604,18 +5619,40 @@ impl<'a> Parser<'a> { for sp in &bad_lifetime_pos { err.span_label(*sp, "must be declared prior to type parameters"); } - if !suggestions.is_empty() { + if !lifetime_suggestions.is_empty() { err.multipart_suggestion_with_applicability( &format!( "move the lifetime parameter{} prior to the first type parameter", if bad_lifetime_pos.len() > 1 { "s" } else { "" }, ), - suggestions, + lifetime_suggestions, Applicability::MachineApplicable, ); } err.emit(); } + + if !bad_type_pos.is_empty() { + let mut err = self.struct_span_err( + bad_type_pos.clone(), + "type parameters must be declared prior to associated type bindings" + ); + for sp in &bad_type_pos { + err.span_label(*sp, "must be declared prior to associated type bindings"); + } + if !type_suggestions.is_empty() { + err.multipart_suggestion_with_applicability( + &format!( + "move the type parameter{} prior to the first associated type binding", + if bad_type_pos.len() > 1 { "s" } else { "" }, + ), + type_suggestions, + Applicability::MachineApplicable, + ); + } + err.emit(); + } + Ok((args, bindings)) } diff --git a/src/test/ui/parser/issue-32214.stderr b/src/test/ui/parser/issue-32214.stderr index a889513eaee52..660e517c85a13 100644 --- a/src/test/ui/parser/issue-32214.stderr +++ b/src/test/ui/parser/issue-32214.stderr @@ -3,6 +3,10 @@ error: type parameters must be declared prior to associated type bindings | LL | pub fn test >() {} | ^ must be declared prior to associated type bindings +help: move the type parameter prior to the first associated type binding + | +LL | pub fn test >() {} + | ^^ -- error: aborting due to previous error diff --git a/src/test/ui/suggestions/suggest-move-types.rs b/src/test/ui/suggestions/suggest-move-types.rs new file mode 100644 index 0000000000000..8f35e4ecbcace --- /dev/null +++ b/src/test/ui/suggestions/suggest-move-types.rs @@ -0,0 +1,42 @@ +#![allow(warnings)] + +// This test verifies that the suggestion to move types before associated type bindings +// is correct. + +trait One { + type A; +} + +trait Three { + type A; + type B; + type C; +} + +struct A> { //~ ERROR type parameters must be declared + m: M, + t: T, +} + +struct B> { //~ ERROR type parameters must be declared + m: M, + t: T, + u: U, + v: V, +} + +struct C> { //~ ERROR type parameters must be declared + m: M, + t: T, + u: U, + v: V, +} + +struct D> { //~ ERROR type parameters must be declared + m: M, + t: T, + u: U, + v: V, +} + +fn main() {} diff --git a/src/test/ui/suggestions/suggest-move-types.stderr b/src/test/ui/suggestions/suggest-move-types.stderr new file mode 100644 index 0000000000000..c74f79a00c78c --- /dev/null +++ b/src/test/ui/suggestions/suggest-move-types.stderr @@ -0,0 +1,49 @@ +error: type parameters must be declared prior to associated type bindings + --> $DIR/suggest-move-types.rs:16:26 + | +LL | struct A> { //~ ERROR type parameters must be declared + | ^ must be declared prior to associated type bindings +help: move the type parameter prior to the first associated type binding + | +LL | struct A> { //~ ERROR type parameters must be declared + | ^^ -- + +error: type parameters must be declared prior to associated type bindings + --> $DIR/suggest-move-types.rs:21:46 + | +LL | struct B> { //~ ERROR type parameters must be declared + | ^ ^ ^ must be declared prior to associated type bindings + | | | + | | must be declared prior to associated type bindings + | must be declared prior to associated type bindings +help: move the type parameters prior to the first associated type binding + | +LL | struct B> { //~ ERROR type parameters must be declared + | ^^ ^^ ^^ -- + +error: type parameters must be declared prior to associated type bindings + --> $DIR/suggest-move-types.rs:28:49 + | +LL | struct C> { //~ ERROR type parameters must be declared + | ^ ^ must be declared prior to associated type bindings + | | + | must be declared prior to associated type bindings +help: move the type parameters prior to the first associated type binding + | +LL | struct C> { //~ ERROR type parameters must be declared + | ^^ ^^ -- + +error: type parameters must be declared prior to associated type bindings + --> $DIR/suggest-move-types.rs:35:43 + | +LL | struct D> { //~ ERROR type parameters must be declared + | ^ ^ must be declared prior to associated type bindings + | | + | must be declared prior to associated type bindings +help: move the type parameters prior to the first associated type binding + | +LL | struct D> { //~ ERROR type parameters must be declared + | ^^ ^^ -- -- + +error: aborting due to 4 previous errors + From 7a0abbff8be746e46841ac7eef5a17364d6b8b51 Mon Sep 17 00:00:00 2001 From: David Wood Date: Fri, 25 Jan 2019 00:36:28 +0100 Subject: [PATCH 8/9] Combining move lifetime and type suggestions. This commit combines the move lifetime and move type suggestions so that when rustfix applies them they don't conflict with each other. --- src/libsyntax/parse/parser.rs | 103 +++++++++++++----- src/test/ui/suggestions/suggest-move-types.rs | 43 ++++++++ .../ui/suggestions/suggest-move-types.stderr | 68 +++++++++++- 3 files changed, 179 insertions(+), 35 deletions(-) diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index 232b8bb5966c7..955dce47721e2 100644 --- a/src/libsyntax/parse/parser.rs +++ b/src/libsyntax/parse/parser.rs @@ -5611,49 +5611,92 @@ impl<'a> Parser<'a> { } } - if !bad_lifetime_pos.is_empty() { - let mut err = self.struct_span_err( + self.maybe_report_incorrect_generic_argument_order( + bad_lifetime_pos, bad_type_pos, lifetime_suggestions, type_suggestions + ); + + Ok((args, bindings)) + } + + /// Maybe report an error about incorrect generic argument order - "lifetime parameters + /// must be declared before type parameters", "type parameters must be declared before + /// associated type bindings" or both. + fn maybe_report_incorrect_generic_argument_order( + &self, + bad_lifetime_pos: Vec, + bad_type_pos: Vec, + lifetime_suggestions: Vec<(Span, String)>, + type_suggestions: Vec<(Span, String)>, + ) { + let mut err = if !bad_lifetime_pos.is_empty() && !bad_type_pos.is_empty() { + let mut positions = bad_lifetime_pos.clone(); + positions.extend_from_slice(&bad_type_pos); + + self.struct_span_err( + positions, + "generic arguments must declare lifetimes, types and associated type bindings in \ + that order", + ) + } else if !bad_lifetime_pos.is_empty() { + self.struct_span_err( bad_lifetime_pos.clone(), "lifetime parameters must be declared prior to type parameters" - ); + ) + } else if !bad_type_pos.is_empty() { + self.struct_span_err( + bad_type_pos.clone(), + "type parameters must be declared prior to associated type bindings" + ) + } else { + return; + }; + + if !bad_lifetime_pos.is_empty() { for sp in &bad_lifetime_pos { err.span_label(*sp, "must be declared prior to type parameters"); } - if !lifetime_suggestions.is_empty() { - err.multipart_suggestion_with_applicability( - &format!( - "move the lifetime parameter{} prior to the first type parameter", - if bad_lifetime_pos.len() > 1 { "s" } else { "" }, - ), - lifetime_suggestions, - Applicability::MachineApplicable, - ); - } - err.emit(); } if !bad_type_pos.is_empty() { - let mut err = self.struct_span_err( - bad_type_pos.clone(), - "type parameters must be declared prior to associated type bindings" - ); for sp in &bad_type_pos { err.span_label(*sp, "must be declared prior to associated type bindings"); } - if !type_suggestions.is_empty() { - err.multipart_suggestion_with_applicability( - &format!( - "move the type parameter{} prior to the first associated type binding", - if bad_type_pos.len() > 1 { "s" } else { "" }, - ), - type_suggestions, - Applicability::MachineApplicable, - ); - } - err.emit(); } - Ok((args, bindings)) + if !lifetime_suggestions.is_empty() && !type_suggestions.is_empty() { + let mut suggestions = lifetime_suggestions; + suggestions.extend_from_slice(&type_suggestions); + + let plural = bad_lifetime_pos.len() + bad_type_pos.len() > 1; + err.multipart_suggestion_with_applicability( + &format!( + "move the parameter{}", + if plural { "s" } else { "" }, + ), + suggestions, + Applicability::MachineApplicable, + ); + } else if !lifetime_suggestions.is_empty() { + err.multipart_suggestion_with_applicability( + &format!( + "move the lifetime parameter{} prior to the first type parameter", + if bad_lifetime_pos.len() > 1 { "s" } else { "" }, + ), + lifetime_suggestions, + Applicability::MachineApplicable, + ); + } else if !type_suggestions.is_empty() { + err.multipart_suggestion_with_applicability( + &format!( + "move the type parameter{} prior to the first associated type binding", + if bad_type_pos.len() > 1 { "s" } else { "" }, + ), + type_suggestions, + Applicability::MachineApplicable, + ); + } + + err.emit(); } /// Parses an optional `where` clause and places it in `generics`. diff --git a/src/test/ui/suggestions/suggest-move-types.rs b/src/test/ui/suggestions/suggest-move-types.rs index 8f35e4ecbcace..fd10ba4350c4a 100644 --- a/src/test/ui/suggestions/suggest-move-types.rs +++ b/src/test/ui/suggestions/suggest-move-types.rs @@ -1,3 +1,5 @@ +// ignore-tidy-linelength + #![allow(warnings)] // This test verifies that the suggestion to move types before associated type bindings @@ -7,17 +9,34 @@ trait One { type A; } +trait OneWithLifetime<'a, T> { + type A; +} + trait Three { type A; type B; type C; } +trait ThreeWithLifetime<'a, 'b, 'c, T, U, V> { + type A; + type B; + type C; +} + struct A> { //~ ERROR type parameters must be declared m: M, t: T, } + +struct Al<'a, T, M: OneWithLifetime> { +//~^ ERROR generic arguments must declare lifetimes, types and associated type bindings in that order + m: M, + t: &'a T, +} + struct B> { //~ ERROR type parameters must be declared m: M, t: T, @@ -25,6 +44,14 @@ struct B> { //~ ERROR type paramete v: V, } +struct Bl<'a, 'b, 'c, T, U, V, M: ThreeWithLifetime> { +//~^ ERROR generic arguments must declare lifetimes, types and associated type bindings in that order + m: M, + t: &'a T, + u: &'b U, + v: &'c V, +} + struct C> { //~ ERROR type parameters must be declared m: M, t: T, @@ -32,6 +59,14 @@ struct C> { //~ ERROR type paramete v: V, } +struct Cl<'a, 'b, 'c, T, U, V, M: ThreeWithLifetime> { +//~^ ERROR generic arguments must declare lifetimes, types and associated type bindings in that order + m: M, + t: &'a T, + u: &'b U, + v: &'c V, +} + struct D> { //~ ERROR type parameters must be declared m: M, t: T, @@ -39,4 +74,12 @@ struct D> { //~ ERROR type paramete v: V, } +struct Dl<'a, 'b, 'c, T, U, V, M: ThreeWithLifetime> { +//~^ ERROR generic arguments must declare lifetimes, types and associated type bindings in that order + m: M, + t: &'a T, + u: &'b U, + v: &'c V, +} + fn main() {} diff --git a/src/test/ui/suggestions/suggest-move-types.stderr b/src/test/ui/suggestions/suggest-move-types.stderr index c74f79a00c78c..3643d9a912455 100644 --- a/src/test/ui/suggestions/suggest-move-types.stderr +++ b/src/test/ui/suggestions/suggest-move-types.stderr @@ -1,5 +1,5 @@ error: type parameters must be declared prior to associated type bindings - --> $DIR/suggest-move-types.rs:16:26 + --> $DIR/suggest-move-types.rs:28:26 | LL | struct A> { //~ ERROR type parameters must be declared | ^ must be declared prior to associated type bindings @@ -8,8 +8,20 @@ help: move the type parameter prior to the first associated type binding LL | struct A> { //~ ERROR type parameters must be declared | ^^ -- +error: generic arguments must declare lifetimes, types and associated type bindings in that order + --> $DIR/suggest-move-types.rs:34:46 + | +LL | struct Al<'a, T, M: OneWithLifetime> { + | ^ ^^ must be declared prior to type parameters + | | + | must be declared prior to associated type bindings +help: move the parameters + | +LL | struct Al<'a, T, M: OneWithLifetime<'a, T, A=()>> { + | ^^^ ^^ -- + error: type parameters must be declared prior to associated type bindings - --> $DIR/suggest-move-types.rs:21:46 + --> $DIR/suggest-move-types.rs:40:46 | LL | struct B> { //~ ERROR type parameters must be declared | ^ ^ ^ must be declared prior to associated type bindings @@ -21,8 +33,24 @@ help: move the type parameters prior to the first associated type binding LL | struct B> { //~ ERROR type parameters must be declared | ^^ ^^ ^^ -- +error: generic arguments must declare lifetimes, types and associated type bindings in that order + --> $DIR/suggest-move-types.rs:47:80 + | +LL | struct Bl<'a, 'b, 'c, T, U, V, M: ThreeWithLifetime> { + | ^ ^ ^ ^^ ^^ ^^ must be declared prior to type parameters + | | | | | | + | | | | | must be declared prior to type parameters + | | | | must be declared prior to type parameters + | | | must be declared prior to associated type bindings + | | must be declared prior to associated type bindings + | must be declared prior to associated type bindings +help: move the parameters + | +LL | struct Bl<'a, 'b, 'c, T, U, V, M: ThreeWithLifetime<'a, 'b, 'c, T, U, V, A=(), B=(), C=()>> { + | ^^^ ^^^ ^^^ ^^ ^^ ^^ -- + error: type parameters must be declared prior to associated type bindings - --> $DIR/suggest-move-types.rs:28:49 + --> $DIR/suggest-move-types.rs:55:49 | LL | struct C> { //~ ERROR type parameters must be declared | ^ ^ must be declared prior to associated type bindings @@ -33,8 +61,23 @@ help: move the type parameters prior to the first associated type binding LL | struct C> { //~ ERROR type parameters must be declared | ^^ ^^ -- +error: generic arguments must declare lifetimes, types and associated type bindings in that order + --> $DIR/suggest-move-types.rs:62:56 + | +LL | struct Cl<'a, 'b, 'c, T, U, V, M: ThreeWithLifetime> { + | ^^ ^ ^^ ^ ^^ must be declared prior to type parameters + | | | | | + | | | | must be declared prior to associated type bindings + | | | must be declared prior to type parameters + | | must be declared prior to associated type bindings + | must be declared prior to type parameters +help: move the parameters + | +LL | struct Cl<'a, 'b, 'c, T, U, V, M: ThreeWithLifetime<'a, 'b, 'c, T, U, V, A=(), B=(), C=()>> { + | ^^^ ^^^ ^^^ -- ^^ ^^ -- + error: type parameters must be declared prior to associated type bindings - --> $DIR/suggest-move-types.rs:35:43 + --> $DIR/suggest-move-types.rs:70:43 | LL | struct D> { //~ ERROR type parameters must be declared | ^ ^ must be declared prior to associated type bindings @@ -45,5 +88,20 @@ help: move the type parameters prior to the first associated type binding LL | struct D> { //~ ERROR type parameters must be declared | ^^ ^^ -- -- -error: aborting due to 4 previous errors +error: generic arguments must declare lifetimes, types and associated type bindings in that order + --> $DIR/suggest-move-types.rs:77:56 + | +LL | struct Dl<'a, 'b, 'c, T, U, V, M: ThreeWithLifetime> { + | ^^ ^ ^^ ^ ^^ must be declared prior to type parameters + | | | | | + | | | | must be declared prior to associated type bindings + | | | must be declared prior to type parameters + | | must be declared prior to associated type bindings + | must be declared prior to type parameters +help: move the parameters + | +LL | struct Dl<'a, 'b, 'c, T, U, V, M: ThreeWithLifetime<'a, 'b, 'c, T, U, V, A=(), B=(), C=()>> { + | ^^^ ^^^ ^^^ -- ^^ ^^ -- -- + +error: aborting due to 8 previous errors From 8e4c57fca2f4bae61f7b567e7c72f96245f385bc Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Wed, 9 Jan 2019 15:16:32 -0500 Subject: [PATCH 9/9] distinguish "no data" from "heterogeneous" for ABI purposes Also, add a testing infrastructure and tests that lets us dump layout. --- src/librustc_driver/driver.rs | 5 +- src/librustc_passes/layout_test.rs | 132 ++++++++++++++++++ src/librustc_passes/lib.rs | 1 + src/librustc_target/abi/call/aarch64.rs | 2 +- src/librustc_target/abi/call/arm.rs | 2 +- src/librustc_target/abi/call/asmjs.rs | 2 +- src/librustc_target/abi/call/mod.rs | 79 +++++++++-- src/librustc_target/abi/call/powerpc64.rs | 2 +- src/librustc_target/abi/call/sparc64.rs | 2 +- src/librustc_target/abi/call/x86.rs | 2 +- src/libsyntax/feature_gate.rs | 7 + .../homogeneous-aggr-zero-sized-c-struct.rs | 36 +++++ ...omogeneous-aggr-zero-sized-c-struct.stderr | 14 ++ .../homogeneous-aggr-zero-sized-repr-rust.rs | 73 ++++++++++ ...mogeneous-aggr-zero-sized-repr-rust.stderr | 32 +++++ src/test/ui/layout/zero-sized-array-union.rs | 95 +++++++++++++ .../ui/layout/zero-sized-array-union.stderr | 26 ++++ 17 files changed, 492 insertions(+), 20 deletions(-) create mode 100644 src/librustc_passes/layout_test.rs create mode 100644 src/test/ui/layout/homogeneous-aggr-zero-sized-c-struct.rs create mode 100644 src/test/ui/layout/homogeneous-aggr-zero-sized-c-struct.stderr create mode 100644 src/test/ui/layout/homogeneous-aggr-zero-sized-repr-rust.rs create mode 100644 src/test/ui/layout/homogeneous-aggr-zero-sized-repr-rust.stderr create mode 100644 src/test/ui/layout/zero-sized-array-union.rs create mode 100644 src/test/ui/layout/zero-sized-array-union.stderr diff --git a/src/librustc_driver/driver.rs b/src/librustc_driver/driver.rs index 142d60c8d00b2..55f9d8a1109ce 100644 --- a/src/librustc_driver/driver.rs +++ b/src/librustc_driver/driver.rs @@ -22,7 +22,7 @@ use rustc_incremental; use rustc_metadata::creader::CrateLoader; use rustc_metadata::cstore::{self, CStore}; use rustc_mir as mir; -use rustc_passes::{self, ast_validation, hir_stats, loops, rvalue_promotion}; +use rustc_passes::{self, ast_validation, hir_stats, loops, rvalue_promotion, layout_test}; use rustc_plugin as plugin; use rustc_plugin::registry::Registry; use rustc_privacy; @@ -1287,6 +1287,9 @@ where mir::transform::check_unsafety::check_unsafety(tcx, def_id) } }); + + time(sess, "layout testing", || layout_test::test_layout(tcx)); + // Avoid overwhelming user with errors if type checking failed. // I'm not sure how helpful this is, to be honest, but it avoids // a diff --git a/src/librustc_passes/layout_test.rs b/src/librustc_passes/layout_test.rs new file mode 100644 index 0000000000000..d21707c578b2a --- /dev/null +++ b/src/librustc_passes/layout_test.rs @@ -0,0 +1,132 @@ +use rustc::hir; +use rustc::hir::def_id::DefId; +use rustc::hir::itemlikevisit::ItemLikeVisitor; +use rustc::hir::ItemKind; +use rustc::ty::layout::HasDataLayout; +use rustc::ty::layout::HasTyCtxt; +use rustc::ty::layout::LayoutOf; +use rustc::ty::layout::TargetDataLayout; +use rustc::ty::layout::TyLayout; +use rustc::ty::ParamEnv; +use rustc::ty::Ty; +use rustc::ty::TyCtxt; +use syntax::ast::Attribute; + +pub fn test_layout<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>) { + if tcx.features().rustc_attrs { + // if the `rustc_attrs` feature is not enabled, don't bother testing layout + tcx.hir() + .krate() + .visit_all_item_likes(&mut VarianceTest { tcx }); + } +} + +struct VarianceTest<'a, 'tcx: 'a> { + tcx: TyCtxt<'a, 'tcx, 'tcx>, +} + +impl<'a, 'tcx> ItemLikeVisitor<'tcx> for VarianceTest<'a, 'tcx> { + fn visit_item(&mut self, item: &'tcx hir::Item) { + let item_def_id = self.tcx.hir().local_def_id(item.id); + + if let ItemKind::Ty(..) = item.node { + for attr in self.tcx.get_attrs(item_def_id).iter() { + if attr.check_name("rustc_layout") { + self.dump_layout_of(item_def_id, item, attr); + } + } + } + } + + fn visit_trait_item(&mut self, _: &'tcx hir::TraitItem) {} + fn visit_impl_item(&mut self, _: &'tcx hir::ImplItem) {} +} + +impl<'a, 'tcx> VarianceTest<'a, 'tcx> { + fn dump_layout_of(&self, item_def_id: DefId, item: &hir::Item, attr: &Attribute) { + let tcx = self.tcx; + let param_env = self.tcx.param_env(item_def_id); + let ty = self.tcx.type_of(item_def_id); + match self.tcx.layout_of(param_env.and(ty)) { + Ok(ty_layout) => { + // Check out the `#[rustc_layout(..)]` attribute to tell what to dump. + // The `..` are the names of fields to dump. + let meta_items = attr.meta_item_list().unwrap_or_default(); + for meta_item in meta_items { + let name = meta_item.word().map(|mi| mi.name().as_str()); + let name = name.as_ref().map(|s| &s[..]).unwrap_or(""); + + match name { + "abi" => { + self.tcx + .sess + .span_err(item.span, &format!("abi: {:?}", ty_layout.abi)); + } + + "align" => { + self.tcx + .sess + .span_err(item.span, &format!("align: {:?}", ty_layout.align)); + } + + "size" => { + self.tcx + .sess + .span_err(item.span, &format!("size: {:?}", ty_layout.size)); + } + + "homogeneous_aggregate" => { + self.tcx.sess.span_err( + item.span, + &format!( + "homogeneous_aggregate: {:?}", + ty_layout + .homogeneous_aggregate(&UnwrapLayoutCx { tcx, param_env }), + ), + ); + } + + _ => { + self.tcx.sess.span_err( + meta_item.span, + &format!("unrecognized field name `{}`", name), + ); + } + } + } + } + + Err(layout_error) => { + self.tcx + .sess + .span_err(item.span, &format!("layout error: {:?}", layout_error)); + } + } + } +} + +struct UnwrapLayoutCx<'me, 'tcx> { + tcx: TyCtxt<'me, 'tcx, 'tcx>, + param_env: ParamEnv<'tcx>, +} + +impl<'me, 'tcx> LayoutOf for UnwrapLayoutCx<'me, 'tcx> { + type Ty = Ty<'tcx>; + type TyLayout = TyLayout<'tcx>; + + fn layout_of(&self, ty: Ty<'tcx>) -> Self::TyLayout { + self.tcx.layout_of(self.param_env.and(ty)).unwrap() + } +} + +impl<'me, 'tcx> HasTyCtxt<'tcx> for UnwrapLayoutCx<'me, 'tcx> { + fn tcx<'a>(&'a self) -> TyCtxt<'a, 'tcx, 'tcx> { + self.tcx + } +} + +impl<'me, 'tcx> HasDataLayout for UnwrapLayoutCx<'me, 'tcx> { + fn data_layout(&self) -> &TargetDataLayout { + self.tcx.data_layout() + } +} diff --git a/src/librustc_passes/lib.rs b/src/librustc_passes/lib.rs index 829d4b34cf779..a181bc7e9b480 100644 --- a/src/librustc_passes/lib.rs +++ b/src/librustc_passes/lib.rs @@ -32,6 +32,7 @@ mod diagnostics; pub mod ast_validation; pub mod rvalue_promotion; pub mod hir_stats; +pub mod layout_test; pub mod loops; __build_diagnostic_array! { librustc_passes, DIAGNOSTICS } diff --git a/src/librustc_target/abi/call/aarch64.rs b/src/librustc_target/abi/call/aarch64.rs index 121f80c75b9ac..9f9bba14b963e 100644 --- a/src/librustc_target/abi/call/aarch64.rs +++ b/src/librustc_target/abi/call/aarch64.rs @@ -6,7 +6,7 @@ fn is_homogeneous_aggregate<'a, Ty, C>(cx: &C, arg: &mut ArgType<'a, Ty>) where Ty: TyLayoutMethods<'a, C> + Copy, C: LayoutOf> + HasDataLayout { - arg.layout.homogeneous_aggregate(cx).and_then(|unit| { + arg.layout.homogeneous_aggregate(cx).unit().and_then(|unit| { let size = arg.layout.size; // Ensure we have at most four uniquely addressable members. diff --git a/src/librustc_target/abi/call/arm.rs b/src/librustc_target/abi/call/arm.rs index 2b3588d31bf1f..228dd36216158 100644 --- a/src/librustc_target/abi/call/arm.rs +++ b/src/librustc_target/abi/call/arm.rs @@ -7,7 +7,7 @@ fn is_homogeneous_aggregate<'a, Ty, C>(cx: &C, arg: &mut ArgType<'a, Ty>) where Ty: TyLayoutMethods<'a, C> + Copy, C: LayoutOf> + HasDataLayout { - arg.layout.homogeneous_aggregate(cx).and_then(|unit| { + arg.layout.homogeneous_aggregate(cx).unit().and_then(|unit| { let size = arg.layout.size; // Ensure we have at most four uniquely addressable members. diff --git a/src/librustc_target/abi/call/asmjs.rs b/src/librustc_target/abi/call/asmjs.rs index 32c6634b3d250..85444500c5e11 100644 --- a/src/librustc_target/abi/call/asmjs.rs +++ b/src/librustc_target/abi/call/asmjs.rs @@ -11,7 +11,7 @@ fn classify_ret_ty<'a, Ty, C>(cx: &C, ret: &mut ArgType<'a, Ty>) C: LayoutOf> + HasDataLayout { if ret.layout.is_aggregate() { - if let Some(unit) = ret.layout.homogeneous_aggregate(cx) { + if let Some(unit) = ret.layout.homogeneous_aggregate(cx).unit() { let size = ret.layout.size; if unit.size == size { ret.cast_to(Uniform { diff --git a/src/librustc_target/abi/call/mod.rs b/src/librustc_target/abi/call/mod.rs index 37bf6e68247d2..0d50439c67ec0 100644 --- a/src/librustc_target/abi/call/mod.rs +++ b/src/librustc_target/abi/call/mod.rs @@ -228,6 +228,33 @@ impl CastTarget { } } +/// Return value from the `homogeneous_aggregate` test function. +#[derive(Copy, Clone, Debug)] +pub enum HomogeneousAggregate { + /// Yes, all the "leaf fields" of this struct are passed in the + /// same way (specified in the `Reg` value). + Homogeneous(Reg), + + /// There are distinct leaf fields passed in different ways, + /// or this is uninhabited. + Heterogeneous, + + /// There are no leaf fields at all. + NoData, +} + +impl HomogeneousAggregate { + /// If this is a homogeneous aggregate, returns the homogeneous + /// unit, else `None`. + pub fn unit(self) -> Option { + if let HomogeneousAggregate::Homogeneous(r) = self { + Some(r) + } else { + None + } + } +} + impl<'a, Ty> TyLayout<'a, Ty> { fn is_aggregate(&self) -> bool { match self.abi { @@ -239,11 +266,21 @@ impl<'a, Ty> TyLayout<'a, Ty> { } } - fn homogeneous_aggregate(&self, cx: &C) -> Option + /// True if this layout is an aggregate containing fields of only + /// a single type (e.g., `(u32, u32)`). Such aggregates are often + /// special-cased in ABIs. + /// + /// Note: We generally ignore fields of zero-sized type when computing + /// this value (cc #56877). + /// + /// This is public so that it can be used in unit tests, but + /// should generally only be relevant to the ABI details of + /// specific targets. + pub fn homogeneous_aggregate(&self, cx: &C) -> HomogeneousAggregate where Ty: TyLayoutMethods<'a, C> + Copy, C: LayoutOf { match self.abi { - Abi::Uninhabited => None, + Abi::Uninhabited => HomogeneousAggregate::Heterogeneous, // The primitive for this algorithm. Abi::Scalar(ref scalar) => { @@ -252,14 +289,15 @@ impl<'a, Ty> TyLayout<'a, Ty> { abi::Pointer => RegKind::Integer, abi::Float(_) => RegKind::Float, }; - Some(Reg { + HomogeneousAggregate::Homogeneous(Reg { kind, size: self.size }) } Abi::Vector { .. } => { - Some(Reg { + assert!(!self.is_zst()); + HomogeneousAggregate::Homogeneous(Reg { kind: RegKind::Vector, size: self.size }) @@ -275,7 +313,7 @@ impl<'a, Ty> TyLayout<'a, Ty> { if count > 0 { return self.field(cx, 0).homogeneous_aggregate(cx); } else { - return None; + return HomogeneousAggregate::NoData; } } FieldPlacement::Union(_) => true, @@ -284,21 +322,27 @@ impl<'a, Ty> TyLayout<'a, Ty> { for i in 0..self.fields.count() { if !is_union && total != self.fields.offset(i) { - return None; + return HomogeneousAggregate::Heterogeneous; } let field = self.field(cx, i); + match (result, field.homogeneous_aggregate(cx)) { - // The field itself must be a homogeneous aggregate. - (_, None) => return None, + (_, HomogeneousAggregate::NoData) => { + // Ignore fields that have no data + } + (_, HomogeneousAggregate::Heterogeneous) => { + // The field itself must be a homogeneous aggregate. + return HomogeneousAggregate::Heterogeneous; + } // If this is the first field, record the unit. - (None, Some(unit)) => { + (None, HomogeneousAggregate::Homogeneous(unit)) => { result = Some(unit); } // For all following fields, the unit must be the same. - (Some(prev_unit), Some(unit)) => { + (Some(prev_unit), HomogeneousAggregate::Homogeneous(unit)) => { if prev_unit != unit { - return None; + return HomogeneousAggregate::Heterogeneous; } } } @@ -314,9 +358,18 @@ impl<'a, Ty> TyLayout<'a, Ty> { // There needs to be no padding. if total != self.size { - None + HomogeneousAggregate::Heterogeneous } else { - result + match result { + Some(reg) => { + assert_ne!(total, Size::ZERO); + HomogeneousAggregate::Homogeneous(reg) + } + None => { + assert_eq!(total, Size::ZERO); + HomogeneousAggregate::NoData + } + } } } } diff --git a/src/librustc_target/abi/call/powerpc64.rs b/src/librustc_target/abi/call/powerpc64.rs index 3ae9ab9ac5ec9..305a2d4225056 100644 --- a/src/librustc_target/abi/call/powerpc64.rs +++ b/src/librustc_target/abi/call/powerpc64.rs @@ -18,7 +18,7 @@ fn is_homogeneous_aggregate<'a, Ty, C>(cx: &C, arg: &mut ArgType<'a, Ty>, abi: A where Ty: TyLayoutMethods<'a, C> + Copy, C: LayoutOf> + HasDataLayout { - arg.layout.homogeneous_aggregate(cx).and_then(|unit| { + arg.layout.homogeneous_aggregate(cx).unit().and_then(|unit| { // ELFv1 only passes one-member aggregates transparently. // ELFv2 passes up to eight uniquely addressable members. if (abi == ELFv1 && arg.layout.size > unit.size) diff --git a/src/librustc_target/abi/call/sparc64.rs b/src/librustc_target/abi/call/sparc64.rs index bb0c9b7967afc..150b48a8d0255 100644 --- a/src/librustc_target/abi/call/sparc64.rs +++ b/src/librustc_target/abi/call/sparc64.rs @@ -8,7 +8,7 @@ fn is_homogeneous_aggregate<'a, Ty, C>(cx: &C, arg: &mut ArgType<'a, Ty>) where Ty: TyLayoutMethods<'a, C> + Copy, C: LayoutOf> + HasDataLayout { - arg.layout.homogeneous_aggregate(cx).and_then(|unit| { + arg.layout.homogeneous_aggregate(cx).unit().and_then(|unit| { // Ensure we have at most eight uniquely addressable members. if arg.layout.size > unit.size.checked_mul(8, cx).unwrap() { return None; diff --git a/src/librustc_target/abi/call/x86.rs b/src/librustc_target/abi/call/x86.rs index 748bef4de66b3..648a4b5bb9d79 100644 --- a/src/librustc_target/abi/call/x86.rs +++ b/src/librustc_target/abi/call/x86.rs @@ -99,7 +99,7 @@ pub fn compute_abi_info<'a, Ty, C>(cx: &C, fty: &mut FnType<'a, Ty>, flavor: Fla }; // At this point we know this must be a primitive of sorts. - let unit = arg.layout.homogeneous_aggregate(cx).unwrap(); + let unit = arg.layout.homogeneous_aggregate(cx).unit().unwrap(); assert_eq!(unit.size, arg.layout.size); if unit.kind == RegKind::Float { continue; diff --git a/src/libsyntax/feature_gate.rs b/src/libsyntax/feature_gate.rs index 9e107fee5bad3..85e80f7bdaf19 100644 --- a/src/libsyntax/feature_gate.rs +++ b/src/libsyntax/feature_gate.rs @@ -938,6 +938,13 @@ pub const BUILTIN_ATTRIBUTES: &[(&str, AttributeType, AttributeTemplate, Attribu is just used for rustc unit tests \ and will never be stable", cfg_fn!(rustc_attrs))), + ("rustc_layout", Normal, template!(List: "field1, field2, ..."), + Gated(Stability::Unstable, + "rustc_attrs", + "the `#[rustc_layout]` attribute \ + is just used for rustc unit tests \ + and will never be stable", + cfg_fn!(rustc_attrs))), ("rustc_regions", Normal, template!(Word), Gated(Stability::Unstable, "rustc_attrs", "the `#[rustc_regions]` attribute \ diff --git a/src/test/ui/layout/homogeneous-aggr-zero-sized-c-struct.rs b/src/test/ui/layout/homogeneous-aggr-zero-sized-c-struct.rs new file mode 100644 index 0000000000000..622709e7de584 --- /dev/null +++ b/src/test/ui/layout/homogeneous-aggr-zero-sized-c-struct.rs @@ -0,0 +1,36 @@ +#![feature(rustc_attrs)] + +// Show that `homogeneous_aggregate` code ignores zero-length C +// arrays. This matches the recent C standard, though not the +// behavior of all older compilers, which somtimes consider `T[0]` to +// be a "flexible array member" (see discussion on #56877 for +// details). + +#[repr(C)] +pub struct Foo { + x: u32 +} + +#[repr(C)] +pub struct Middle { + pub a: f32, + pub foo: [Foo; 0], + pub b: f32, +} + +#[rustc_layout(homogeneous_aggregate)] +pub type TestMiddle = Middle; +//~^ ERROR homogeneous_aggregate: Homogeneous + +#[repr(C)] +pub struct Final { + pub a: f32, + pub b: f32, + pub foo: [Foo; 0], +} + +#[rustc_layout(homogeneous_aggregate)] +pub type TestFinal = Final; +//~^ ERROR homogeneous_aggregate: Homogeneous + +fn main() { } diff --git a/src/test/ui/layout/homogeneous-aggr-zero-sized-c-struct.stderr b/src/test/ui/layout/homogeneous-aggr-zero-sized-c-struct.stderr new file mode 100644 index 0000000000000..0d44260635187 --- /dev/null +++ b/src/test/ui/layout/homogeneous-aggr-zero-sized-c-struct.stderr @@ -0,0 +1,14 @@ +error: homogeneous_aggregate: Homogeneous(Reg { kind: Float, size: Size { raw: 4 } }) + --> $DIR/homogeneous-aggr-zero-sized-c-struct.rs:22:1 + | +LL | pub type TestMiddle = Middle; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: homogeneous_aggregate: Homogeneous(Reg { kind: Float, size: Size { raw: 4 } }) + --> $DIR/homogeneous-aggr-zero-sized-c-struct.rs:33:1 + | +LL | pub type TestFinal = Final; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 2 previous errors + diff --git a/src/test/ui/layout/homogeneous-aggr-zero-sized-repr-rust.rs b/src/test/ui/layout/homogeneous-aggr-zero-sized-repr-rust.rs new file mode 100644 index 0000000000000..4b429412aebfc --- /dev/null +++ b/src/test/ui/layout/homogeneous-aggr-zero-sized-repr-rust.rs @@ -0,0 +1,73 @@ +#![feature(rustc_attrs)] + +// Regression test for #56877. We want to ensure that the presence of +// `PhantomData` does not prevent `Bar` from being considered a +// homogeneous aggregate. + +#[repr(C)] +pub struct BaseCase { + pub a: f32, + pub b: f32, +} + +#[repr(C)] +pub struct WithPhantomData { + pub a: f32, + pub b: f32, + pub _unit: std::marker::PhantomData<()>, +} + +pub struct EmptyRustStruct { +} + +#[repr(C)] +pub struct WithEmptyRustStruct { + pub a: f32, + pub b: f32, + pub _unit: EmptyRustStruct, +} + +pub struct TransitivelyEmptyRustStruct { + field: EmptyRustStruct, + array: [u32; 0], +} + +#[repr(C)] +pub struct WithTransitivelyEmptyRustStruct { + pub a: f32, + pub b: f32, + pub _unit: TransitivelyEmptyRustStruct, +} + +pub enum EmptyRustEnum { + Dummy, +} + +#[repr(C)] +pub struct WithEmptyRustEnum { + pub a: f32, + pub b: f32, + pub _unit: EmptyRustEnum, +} + +#[rustc_layout(homogeneous_aggregate)] +pub type Test1 = BaseCase; +//~^ ERROR homogeneous_aggregate: Homogeneous(Reg { kind: Float, size: Size { raw: 4 } }) + +#[rustc_layout(homogeneous_aggregate)] +pub type Test2 = WithPhantomData; +//~^ ERROR homogeneous_aggregate: Homogeneous(Reg { kind: Float, size: Size { raw: 4 } }) + +#[rustc_layout(homogeneous_aggregate)] +pub type Test3 = WithEmptyRustStruct; +//~^ ERROR homogeneous_aggregate: Homogeneous(Reg { kind: Float, size: Size { raw: 4 } }) + +#[rustc_layout(homogeneous_aggregate)] +pub type Test4 = WithTransitivelyEmptyRustStruct; +//~^ ERROR homogeneous_aggregate: Homogeneous(Reg { kind: Float, size: Size { raw: 4 } }) + +#[rustc_layout(homogeneous_aggregate)] +pub type Test5 = WithEmptyRustEnum; +//~^ ERROR homogeneous_aggregate: Homogeneous(Reg { kind: Float, size: Size { raw: 4 } }) + +fn main() { } diff --git a/src/test/ui/layout/homogeneous-aggr-zero-sized-repr-rust.stderr b/src/test/ui/layout/homogeneous-aggr-zero-sized-repr-rust.stderr new file mode 100644 index 0000000000000..be04ba3e7f6cb --- /dev/null +++ b/src/test/ui/layout/homogeneous-aggr-zero-sized-repr-rust.stderr @@ -0,0 +1,32 @@ +error: homogeneous_aggregate: Homogeneous(Reg { kind: Float, size: Size { raw: 4 } }) + --> $DIR/homogeneous-aggr-zero-sized-repr-rust.rs:54:1 + | +LL | pub type Test1 = BaseCase; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: homogeneous_aggregate: Homogeneous(Reg { kind: Float, size: Size { raw: 4 } }) + --> $DIR/homogeneous-aggr-zero-sized-repr-rust.rs:58:1 + | +LL | pub type Test2 = WithPhantomData; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: homogeneous_aggregate: Homogeneous(Reg { kind: Float, size: Size { raw: 4 } }) + --> $DIR/homogeneous-aggr-zero-sized-repr-rust.rs:62:1 + | +LL | pub type Test3 = WithEmptyRustStruct; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: homogeneous_aggregate: Homogeneous(Reg { kind: Float, size: Size { raw: 4 } }) + --> $DIR/homogeneous-aggr-zero-sized-repr-rust.rs:66:1 + | +LL | pub type Test4 = WithTransitivelyEmptyRustStruct; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: homogeneous_aggregate: Homogeneous(Reg { kind: Float, size: Size { raw: 4 } }) + --> $DIR/homogeneous-aggr-zero-sized-repr-rust.rs:70:1 + | +LL | pub type Test5 = WithEmptyRustEnum; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 5 previous errors + diff --git a/src/test/ui/layout/zero-sized-array-union.rs b/src/test/ui/layout/zero-sized-array-union.rs new file mode 100644 index 0000000000000..68b218249eb9a --- /dev/null +++ b/src/test/ui/layout/zero-sized-array-union.rs @@ -0,0 +1,95 @@ +#![feature(rustc_attrs)] + +// Various tests around the behavior of zero-sized arrays and +// unions. This matches the behavior of modern C compilers, though +// older compilers (and sometimes clang) treat `T[0]` as a "flexible +// array member". See more +// details in #56877. + +#[derive(Copy, Clone)] +#[repr(C)] +struct Empty { } + +#[derive(Copy, Clone)] +#[repr(C)] +struct Empty2 { + e: Empty +} + +#[derive(Copy, Clone)] +#[repr(C)] +struct Empty3 { + z: [f32; 0], +} + +#[derive(Copy, Clone)] +#[repr(C)] +struct Empty4 { + e: Empty3 +} + +#[repr(C)] +union U1 { + s: Empty +} + +#[repr(C)] +union U2 { + s: Empty2 +} + +#[repr(C)] +union U3 { + s: Empty3 +} + +#[repr(C)] +union U4 { + s: Empty4 +} + +#[repr(C)] +struct Baz1 { + x: f32, + y: f32, + u: U1, +} + +#[rustc_layout(homogeneous_aggregate)] +type TestBaz1 = Baz1; +//~^ ERROR homogeneous_aggregate: Homogeneous + +#[repr(C)] +struct Baz2 { + x: f32, + y: f32, + u: U2, +} + +#[rustc_layout(homogeneous_aggregate)] +type TestBaz2 = Baz2; +//~^ ERROR homogeneous_aggregate: Homogeneous + +#[repr(C)] +struct Baz3 { + x: f32, + y: f32, + u: U3, +} + +#[rustc_layout(homogeneous_aggregate)] +type TestBaz3 = Baz3; +//~^ ERROR homogeneous_aggregate: Homogeneous + +#[repr(C)] +struct Baz4 { + x: f32, + y: f32, + u: U4, +} + +#[rustc_layout(homogeneous_aggregate)] +type TestBaz4 = Baz4; +//~^ ERROR homogeneous_aggregate: Homogeneous + +fn main() { } diff --git a/src/test/ui/layout/zero-sized-array-union.stderr b/src/test/ui/layout/zero-sized-array-union.stderr new file mode 100644 index 0000000000000..1bb31aaf7b7b9 --- /dev/null +++ b/src/test/ui/layout/zero-sized-array-union.stderr @@ -0,0 +1,26 @@ +error: homogeneous_aggregate: Homogeneous(Reg { kind: Float, size: Size { raw: 4 } }) + --> $DIR/zero-sized-array-union.rs:59:1 + | +LL | type TestBaz1 = Baz1; + | ^^^^^^^^^^^^^^^^^^^^^ + +error: homogeneous_aggregate: Homogeneous(Reg { kind: Float, size: Size { raw: 4 } }) + --> $DIR/zero-sized-array-union.rs:70:1 + | +LL | type TestBaz2 = Baz2; + | ^^^^^^^^^^^^^^^^^^^^^ + +error: homogeneous_aggregate: Homogeneous(Reg { kind: Float, size: Size { raw: 4 } }) + --> $DIR/zero-sized-array-union.rs:81:1 + | +LL | type TestBaz3 = Baz3; + | ^^^^^^^^^^^^^^^^^^^^^ + +error: homogeneous_aggregate: Homogeneous(Reg { kind: Float, size: Size { raw: 4 } }) + --> $DIR/zero-sized-array-union.rs:92:1 + | +LL | type TestBaz4 = Baz4; + | ^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 4 previous errors +