From 9e934e22151d5589cbe3cb1193250d8ac0b366fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Thu, 7 Feb 2019 05:39:54 -0800 Subject: [PATCH 01/20] Reweork incompatible match arms error - Point at the body expression of the match arm with the type error. - Point at the prior match arms explicitely stating the evaluated type. - Point at the entire match expr in a secondary span, instead of primary. - For type errors in the first match arm, the cause is outside of the match, treat as implicit block error to give a more appropriate error. --- src/librustc/infer/error_reporting/mod.rs | 29 +++++++++++++---------- src/librustc/traits/mod.rs | 2 ++ src/librustc/traits/structural_impls.rs | 12 +++++++--- src/librustc_typeck/check/_match.rs | 23 ++++++++++++++---- src/test/ui/if/if-let-arm-types.rs | 9 +++---- src/test/ui/if/if-let-arm-types.stderr | 22 ++++++----------- src/test/ui/issues/issue-11319.rs | 10 ++++---- src/test/ui/issues/issue-11319.stderr | 18 ++++++++------ src/test/ui/issues/issue-17728.rs | 3 ++- src/test/ui/issues/issue-17728.stderr | 10 ++++---- src/test/ui/issues/issue-24036.rs | 2 +- src/test/ui/issues/issue-24036.stderr | 14 +++++------ 12 files changed, 92 insertions(+), 62 deletions(-) diff --git a/src/librustc/infer/error_reporting/mod.rs b/src/librustc/infer/error_reporting/mod.rs index 66e4cd49c807f..361aee4405b40 100644 --- a/src/librustc/infer/error_reporting/mod.rs +++ b/src/librustc/infer/error_reporting/mod.rs @@ -509,22 +509,27 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { } } } - ObligationCauseCode::MatchExpressionArm { arm_span, source } => match source { + ObligationCauseCode::MatchExpressionArm { + source, + ref prior_arms, + .. + } => match source { hir::MatchSource::IfLetDesugar { .. } => { - let msg = "`if let` arm with an incompatible type"; - if self.tcx.sess.source_map().is_multiline(arm_span) { - err.span_note(arm_span, msg); - } else { - err.span_label(arm_span, msg); - } + let msg = "`if let` arms have incompatible types"; + err.span_label(cause.span, msg); } hir::MatchSource::TryDesugar => {} _ => { - let msg = "match arm with an incompatible type"; - if self.tcx.sess.source_map().is_multiline(arm_span) { - err.span_note(arm_span, msg); - } else { - err.span_label(arm_span, msg); + let msg = "`match` arms have incompatible types"; + err.span_label(cause.span, msg); + if prior_arms.len() < 4 { + for (sp, ty) in prior_arms { + err.span_label(*sp, format!("this is found to be of type `{}`", ty)); + } + } else if let Some((sp, ty)) = prior_arms.last() { + err.span_label(*sp, format!( + "this and all prior arms are found to be of type `{}`", ty, + )); } } }, diff --git a/src/librustc/traits/mod.rs b/src/librustc/traits/mod.rs index 68383bef37a6a..9824ff8f397e1 100644 --- a/src/librustc/traits/mod.rs +++ b/src/librustc/traits/mod.rs @@ -145,6 +145,7 @@ impl<'tcx> ObligationCause<'tcx> { ObligationCauseCode::StartFunctionType => { tcx.sess.source_map().def_span(self.span) } + ObligationCauseCode::MatchExpressionArm { arm_span, .. } => arm_span, _ => self.span, } } @@ -223,6 +224,7 @@ pub enum ObligationCauseCode<'tcx> { MatchExpressionArm { arm_span: Span, source: hir::MatchSource, + prior_arms: Vec<(Span, Ty<'tcx>)>, }, /// Computing common supertype in the pattern guard for the arms of a match expression diff --git a/src/librustc/traits/structural_impls.rs b/src/librustc/traits/structural_impls.rs index 2f5df022218fe..c0e00cf291bac 100644 --- a/src/librustc/traits/structural_impls.rs +++ b/src/librustc/traits/structural_impls.rs @@ -513,10 +513,16 @@ impl<'a, 'tcx> Lift<'tcx> for traits::ObligationCauseCode<'a> { trait_item_def_id, }), super::ExprAssignable => Some(super::ExprAssignable), - super::MatchExpressionArm { arm_span, source } => Some(super::MatchExpressionArm { + super::MatchExpressionArm { arm_span, - source: source, - }), + source, + ref prior_arms, + } => { + let prior_arms = prior_arms.iter().filter_map(|(sp, ty)| { + tcx.lift(ty).map(|ty| (*sp, ty)) + }).collect(); + Some(super::MatchExpressionArm { arm_span, source, prior_arms }) + } super::MatchExpressionArmPattern { span, ty } => { tcx.lift(&ty).map(|ty| super::MatchExpressionArmPattern { span, ty }) } diff --git a/src/librustc_typeck/check/_match.rs b/src/librustc_typeck/check/_match.rs index a90d83f3f8be0..c662a49abf9ea 100644 --- a/src/librustc_typeck/check/_match.rs +++ b/src/librustc_typeck/check/_match.rs @@ -689,6 +689,7 @@ https://doc.rust-lang.org/reference/types.html#trait-objects"); CoerceMany::with_coercion_sites(coerce_first, arms) }; + let mut other_arms = vec![]; // used only for diagnostics for (i, (arm, pats_diverge)) in arms.iter().zip(all_arm_pats_diverge).enumerate() { if let Some(ref g) = arm.guard { self.diverges.set(pats_diverge); @@ -709,17 +710,31 @@ https://doc.rust-lang.org/reference/types.html#trait-objects"); _ => false }; + let arm_span = if let hir::ExprKind::Block(ref blk, _) = arm.body.node { + // Point at the block expr instead of the entire block + blk.expr.as_ref().map(|e| e.span).unwrap_or(arm.body.span) + } else { + arm.body.span + }; if is_if_let_fallback { let cause = self.cause(expr.span, ObligationCauseCode::IfExpressionWithNoElse); assert!(arm_ty.is_unit()); coercion.coerce_forced_unit(self, &cause, &mut |_| (), true); } else { - let cause = self.cause(expr.span, ObligationCauseCode::MatchExpressionArm { - arm_span: arm.body.span, - source: match_src - }); + let cause = if i == 0 { + // The reason for the first arm to fail is not that the match arms diverge, + // but rather that there's a prior obligation that doesn't hold. + self.cause(arm_span, ObligationCauseCode::BlockTailExpression(arm.body.id)) + } else { + self.cause(expr.span, ObligationCauseCode::MatchExpressionArm { + arm_span, + source: match_src, + prior_arms: other_arms.clone(), + }) + }; coercion.coerce(self, &cause, &arm.body, arm_ty); } + other_arms.push((arm_span, arm_ty)); } // We won't diverge unless the discriminant or all arms diverge. diff --git a/src/test/ui/if/if-let-arm-types.rs b/src/test/ui/if/if-let-arm-types.rs index 749c089ae9752..819f5dd1cfc35 100644 --- a/src/test/ui/if/if-let-arm-types.rs +++ b/src/test/ui/if/if-let-arm-types.rs @@ -1,10 +1,11 @@ fn main() { - if let Some(b) = None { //~ ERROR: `if let` arms have incompatible types - //~^ expected (), found integer - //~| expected type `()` - //~| found type `{integer}` + if let Some(b) = None { + //~^ NOTE if let` arms have incompatible types () } else { 1 }; + //~^^ ERROR: `if let` arms have incompatible types + //~| NOTE expected (), found integer + //~| NOTE expected type `()` } diff --git a/src/test/ui/if/if-let-arm-types.stderr b/src/test/ui/if/if-let-arm-types.stderr index fcf9e4695f675..6401a62c06ba2 100644 --- a/src/test/ui/if/if-let-arm-types.stderr +++ b/src/test/ui/if/if-let-arm-types.stderr @@ -1,25 +1,17 @@ error[E0308]: `if let` arms have incompatible types - --> $DIR/if-let-arm-types.rs:2:5 + --> $DIR/if-let-arm-types.rs:6:9 | -LL | / if let Some(b) = None { //~ ERROR: `if let` arms have incompatible types -LL | | //~^ expected (), found integer -LL | | //~| expected type `()` -LL | | //~| found type `{integer}` -... | +LL | / if let Some(b) = None { +LL | | //~^ NOTE if let` arms have incompatible types +LL | | () +LL | | } else { LL | | 1 + | | ^ expected (), found integer LL | | }; - | |_____^ expected (), found integer + | |_____- `if let` arms have incompatible types | = note: expected type `()` found type `{integer}` -note: `if let` arm with an incompatible type - --> $DIR/if-let-arm-types.rs:7:12 - | -LL | } else { - | ____________^ -LL | | 1 -LL | | }; - | |_____^ error: aborting due to previous error diff --git a/src/test/ui/issues/issue-11319.rs b/src/test/ui/issues/issue-11319.rs index ea901205544b6..726c437355e53 100644 --- a/src/test/ui/issues/issue-11319.rs +++ b/src/test/ui/issues/issue-11319.rs @@ -1,12 +1,14 @@ fn main() { match Some(10) { - //~^ ERROR match arms have incompatible types - //~| expected type `bool` - //~| found type `()` - //~| expected bool, found () + //~^ NOTE `match` arms have incompatible types Some(5) => false, + //~^ NOTE this is found to be of type `bool` Some(2) => true, + //~^ NOTE this is found to be of type `bool` None => (), + //~^ ERROR match arms have incompatible types + //~| NOTE expected bool, found () + //~| NOTE expected type `bool` _ => true } } diff --git a/src/test/ui/issues/issue-11319.stderr b/src/test/ui/issues/issue-11319.stderr index 44d63ba3e6879..10db477b8ca7b 100644 --- a/src/test/ui/issues/issue-11319.stderr +++ b/src/test/ui/issues/issue-11319.stderr @@ -1,16 +1,20 @@ error[E0308]: match arms have incompatible types - --> $DIR/issue-11319.rs:2:5 + --> $DIR/issue-11319.rs:8:20 | LL | / match Some(10) { -LL | | //~^ ERROR match arms have incompatible types -LL | | //~| expected type `bool` -LL | | //~| found type `()` -... | +LL | | //~^ NOTE `match` arms have incompatible types +LL | | Some(5) => false, + | | ----- this is found to be of type `bool` +LL | | //~^ NOTE this is found to be of type `bool` +LL | | Some(2) => true, + | | ---- this is found to be of type `bool` +LL | | //~^ NOTE this is found to be of type `bool` LL | | None => (), - | | -- match arm with an incompatible type + | | ^^ expected bool, found () +... | LL | | _ => true LL | | } - | |_____^ expected bool, found () + | |_____- `match` arms have incompatible types | = note: expected type `bool` found type `()` diff --git a/src/test/ui/issues/issue-17728.rs b/src/test/ui/issues/issue-17728.rs index 0f13ae3304d6c..15cea1d609d53 100644 --- a/src/test/ui/issues/issue-17728.rs +++ b/src/test/ui/issues/issue-17728.rs @@ -97,7 +97,7 @@ impl Debug for Player { } fn str_to_direction(to_parse: &str) -> RoomDirection { - match to_parse { //~ ERROR match arms have incompatible types + match to_parse { "w" | "west" => RoomDirection::West, "e" | "east" => RoomDirection::East, "n" | "north" => RoomDirection::North, @@ -108,6 +108,7 @@ fn str_to_direction(to_parse: &str) -> RoomDirection { "down" => RoomDirection::Down, _ => None } + //~^^ ERROR match arms have incompatible types } fn main() { diff --git a/src/test/ui/issues/issue-17728.stderr b/src/test/ui/issues/issue-17728.stderr index 355868f05690f..2c2efad19f569 100644 --- a/src/test/ui/issues/issue-17728.stderr +++ b/src/test/ui/issues/issue-17728.stderr @@ -10,17 +10,19 @@ LL | Some(entry) => Ok(entry), | ^^^^^^^^^ ...but data from `room` is returned here error[E0308]: match arms have incompatible types - --> $DIR/issue-17728.rs:100:5 + --> $DIR/issue-17728.rs:109:14 | -LL | / match to_parse { //~ ERROR match arms have incompatible types +LL | / match to_parse { LL | | "w" | "west" => RoomDirection::West, LL | | "e" | "east" => RoomDirection::East, LL | | "n" | "north" => RoomDirection::North, ... | +LL | | "down" => RoomDirection::Down, + | | ------------------- this and all prior arms are found to be of type `RoomDirection` LL | | _ => None - | | ---- match arm with an incompatible type + | | ^^^^ expected enum `RoomDirection`, found enum `std::option::Option` LL | | } - | |_____^ expected enum `RoomDirection`, found enum `std::option::Option` + | |_____- `match` arms have incompatible types | = note: expected type `RoomDirection` found type `std::option::Option<_>` diff --git a/src/test/ui/issues/issue-24036.rs b/src/test/ui/issues/issue-24036.rs index 3642085934abe..2f501b941b5a6 100644 --- a/src/test/ui/issues/issue-24036.rs +++ b/src/test/ui/issues/issue-24036.rs @@ -6,11 +6,11 @@ fn closure_to_loc() { fn closure_from_match() { let x = match 1usize { - //~^ ERROR match arms have incompatible types 1 => |c| c + 1, 2 => |c| c - 1, _ => |c| c - 1 }; + //~^^^ ERROR match arms have incompatible types } fn main() { } diff --git a/src/test/ui/issues/issue-24036.stderr b/src/test/ui/issues/issue-24036.stderr index 9f799c9b45069..fa9935fcf619d 100644 --- a/src/test/ui/issues/issue-24036.stderr +++ b/src/test/ui/issues/issue-24036.stderr @@ -10,20 +10,20 @@ LL | x = |c| c + 1; = help: consider boxing your closure and/or using it as a trait object error[E0308]: match arms have incompatible types - --> $DIR/issue-24036.rs:8:13 + --> $DIR/issue-24036.rs:10:14 | LL | let x = match 1usize { - | _____________^ -LL | | //~^ ERROR match arms have incompatible types + | _____________- LL | | 1 => |c| c + 1, + | | --------- this is found to be of type `[closure@$DIR/issue-24036.rs:9:14: 9:23]` LL | | 2 => |c| c - 1, - | | --------- match arm with an incompatible type + | | ^^^^^^^^^ expected closure, found a different closure LL | | _ => |c| c - 1 LL | | }; - | |_____^ expected closure, found a different closure + | |_____- `match` arms have incompatible types | - = note: expected type `[closure@$DIR/issue-24036.rs:10:14: 10:23]` - found type `[closure@$DIR/issue-24036.rs:11:14: 11:23]` + = note: expected type `[closure@$DIR/issue-24036.rs:9:14: 9:23]` + found type `[closure@$DIR/issue-24036.rs:10:14: 10:23]` = note: no two closures, even if identical, have the same type = help: consider boxing your closure and/or using it as a trait object From 7eb6a2a9729dd0f948fc30e391e32c0a51d3d0a7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Thu, 7 Feb 2019 16:12:12 -0800 Subject: [PATCH 02/20] Add test for type mismatch on first match arm --- src/test/ui/match/match-type-err-first-arm.rs | 27 ++++++++++++++++ .../ui/match/match-type-err-first-arm.stderr | 31 +++++++++++++++++++ 2 files changed, 58 insertions(+) create mode 100644 src/test/ui/match/match-type-err-first-arm.rs create mode 100644 src/test/ui/match/match-type-err-first-arm.stderr diff --git a/src/test/ui/match/match-type-err-first-arm.rs b/src/test/ui/match/match-type-err-first-arm.rs new file mode 100644 index 0000000000000..31fa50d2b452b --- /dev/null +++ b/src/test/ui/match/match-type-err-first-arm.rs @@ -0,0 +1,27 @@ +fn main() { + let _ = test_func1(1); + let _ = test_func2(1); +} + +fn test_func1(n: i32) -> i32 { + //~^ NOTE expected `i32` because of return type + match n { + 12 => 'b', + //~^ ERROR mismatched types + //~| NOTE expected i32, found char + _ => 42, + } +} + +fn test_func2(n: i32) -> i32 { + let x = match n { + //~^ NOTE `match` arms have incompatible types + 12 => 'b', + //~^ NOTE this is found to be of type `char` + _ => 42, + //~^ ERROR match arms have incompatible types + //~| NOTE expected char, found integer + //~| NOTE expected type `char` + }; + x +} diff --git a/src/test/ui/match/match-type-err-first-arm.stderr b/src/test/ui/match/match-type-err-first-arm.stderr new file mode 100644 index 0000000000000..8f722343a5875 --- /dev/null +++ b/src/test/ui/match/match-type-err-first-arm.stderr @@ -0,0 +1,31 @@ +error[E0308]: mismatched types + --> $DIR/match-type-err-first-arm.rs:9:15 + | +LL | fn test_func1(n: i32) -> i32 { + | --- expected `i32` because of return type +... +LL | 12 => 'b', + | ^^^ expected i32, found char + +error[E0308]: match arms have incompatible types + --> $DIR/match-type-err-first-arm.rs:21:14 + | +LL | let x = match n { + | _____________- +LL | | //~^ NOTE `match` arms have incompatible types +LL | | 12 => 'b', + | | --- this is found to be of type `char` +LL | | //~^ NOTE this is found to be of type `char` +LL | | _ => 42, + | | ^^ expected char, found integer +... | +LL | | //~| NOTE expected type `char` +LL | | }; + | |_____- `match` arms have incompatible types + | + = note: expected type `char` + found type `{integer}` + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0308`. From 802c897eb3c95e6643bd4e2cd85052786458e3d8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Fri, 8 Feb 2019 05:44:15 -0800 Subject: [PATCH 03/20] review comments: (marginally) reduce memory consumtion --- src/librustc/infer/error_reporting/mod.rs | 14 +++++++---- src/librustc/traits/mod.rs | 3 ++- src/librustc/traits/structural_impls.rs | 13 ++++++---- src/librustc_typeck/check/_match.rs | 8 ++++++- src/test/ui/match/match-type-err-first-arm.rs | 18 ++++++++++++++ .../ui/match/match-type-err-first-arm.stderr | 24 ++++++++++++++++++- 6 files changed, 68 insertions(+), 12 deletions(-) diff --git a/src/librustc/infer/error_reporting/mod.rs b/src/librustc/infer/error_reporting/mod.rs index 361aee4405b40..82e9a1e3c895b 100644 --- a/src/librustc/infer/error_reporting/mod.rs +++ b/src/librustc/infer/error_reporting/mod.rs @@ -512,6 +512,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { ObligationCauseCode::MatchExpressionArm { source, ref prior_arms, + last_ty, .. } => match source { hir::MatchSource::IfLetDesugar { .. } => { @@ -522,13 +523,16 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { _ => { let msg = "`match` arms have incompatible types"; err.span_label(cause.span, msg); - if prior_arms.len() < 4 { - for (sp, ty) in prior_arms { - err.span_label(*sp, format!("this is found to be of type `{}`", ty)); + if prior_arms.len() <= 4 { + for sp in prior_arms { + err.span_label(*sp, format!( + "this is found to be of type `{}`", + last_ty, + )); } - } else if let Some((sp, ty)) = prior_arms.last() { + } else if let Some(sp) = prior_arms.last() { err.span_label(*sp, format!( - "this and all prior arms are found to be of type `{}`", ty, + "this and all prior arms are found to be of type `{}`", last_ty, )); } } diff --git a/src/librustc/traits/mod.rs b/src/librustc/traits/mod.rs index 9824ff8f397e1..7fc27ba735e47 100644 --- a/src/librustc/traits/mod.rs +++ b/src/librustc/traits/mod.rs @@ -224,7 +224,8 @@ pub enum ObligationCauseCode<'tcx> { MatchExpressionArm { arm_span: Span, source: hir::MatchSource, - prior_arms: Vec<(Span, Ty<'tcx>)>, + prior_arms: Vec, + last_ty: Ty<'tcx>, }, /// Computing common supertype in the pattern guard for the arms of a match expression diff --git a/src/librustc/traits/structural_impls.rs b/src/librustc/traits/structural_impls.rs index c0e00cf291bac..8bbeec0b8ac2b 100644 --- a/src/librustc/traits/structural_impls.rs +++ b/src/librustc/traits/structural_impls.rs @@ -517,11 +517,16 @@ impl<'a, 'tcx> Lift<'tcx> for traits::ObligationCauseCode<'a> { arm_span, source, ref prior_arms, + last_ty, } => { - let prior_arms = prior_arms.iter().filter_map(|(sp, ty)| { - tcx.lift(ty).map(|ty| (*sp, ty)) - }).collect(); - Some(super::MatchExpressionArm { arm_span, source, prior_arms }) + tcx.lift(&last_ty).map(|last_ty| { + super::MatchExpressionArm { + arm_span, + source, + prior_arms: prior_arms.clone(), + last_ty, + } + }) } super::MatchExpressionArmPattern { span, ty } => { tcx.lift(&ty).map(|ty| super::MatchExpressionArmPattern { span, ty }) diff --git a/src/librustc_typeck/check/_match.rs b/src/librustc_typeck/check/_match.rs index c662a49abf9ea..cfc7cedc5e3e5 100644 --- a/src/librustc_typeck/check/_match.rs +++ b/src/librustc_typeck/check/_match.rs @@ -690,6 +690,7 @@ https://doc.rust-lang.org/reference/types.html#trait-objects"); }; let mut other_arms = vec![]; // used only for diagnostics + let mut prior_arm_ty = None; for (i, (arm, pats_diverge)) in arms.iter().zip(all_arm_pats_diverge).enumerate() { if let Some(ref g) = arm.guard { self.diverges.set(pats_diverge); @@ -730,11 +731,16 @@ https://doc.rust-lang.org/reference/types.html#trait-objects"); arm_span, source: match_src, prior_arms: other_arms.clone(), + last_ty: prior_arm_ty.unwrap(), }) }; coercion.coerce(self, &cause, &arm.body, arm_ty); } - other_arms.push((arm_span, arm_ty)); + other_arms.push(arm_span); + if other_arms.len() > 5 { + other_arms.remove(0); + } + prior_arm_ty = Some(arm_ty); } // We won't diverge unless the discriminant or all arms diverge. diff --git a/src/test/ui/match/match-type-err-first-arm.rs b/src/test/ui/match/match-type-err-first-arm.rs index 31fa50d2b452b..b4b84ef8f1cec 100644 --- a/src/test/ui/match/match-type-err-first-arm.rs +++ b/src/test/ui/match/match-type-err-first-arm.rs @@ -25,3 +25,21 @@ fn test_func2(n: i32) -> i32 { }; x } + +fn test_func3(n: i32) -> i32 { + let x = match n { + //~^ NOTE `match` arms have incompatible types + 1 => 'b', + 2 => 'b', + 3 => 'b', + 4 => 'b', + 5 => 'b', + 6 => 'b', + //~^ NOTE this and all prior arms are found to be of type `char` + _ => 42, + //~^ ERROR match arms have incompatible types + //~| NOTE expected char, found integer + //~| NOTE expected type `char` + }; + x +} diff --git a/src/test/ui/match/match-type-err-first-arm.stderr b/src/test/ui/match/match-type-err-first-arm.stderr index 8f722343a5875..db8bef8dc7755 100644 --- a/src/test/ui/match/match-type-err-first-arm.stderr +++ b/src/test/ui/match/match-type-err-first-arm.stderr @@ -26,6 +26,28 @@ LL | | }; = note: expected type `char` found type `{integer}` -error: aborting due to 2 previous errors +error[E0308]: match arms have incompatible types + --> $DIR/match-type-err-first-arm.rs:39:14 + | +LL | let x = match n { + | _____________- +LL | | //~^ NOTE `match` arms have incompatible types +LL | | 1 => 'b', +LL | | 2 => 'b', +... | +LL | | 6 => 'b', + | | --- this and all prior arms are found to be of type `char` +LL | | //~^ NOTE this and all prior arms are found to be of type `char` +LL | | _ => 42, + | | ^^ expected char, found integer +... | +LL | | //~| NOTE expected type `char` +LL | | }; + | |_____- `match` arms have incompatible types + | + = note: expected type `char` + found type `{integer}` + +error: aborting due to 3 previous errors For more information about this error, try `rustc --explain E0308`. From 05b4e7c8a9efa9593907008e16b8f65242c72594 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Fri, 8 Feb 2019 02:45:53 -0800 Subject: [PATCH 04/20] Add way to hide suggestion snippet window from cli output --- src/librustc_errors/diagnostic.rs | 9 ++-- src/librustc_errors/emitter.rs | 87 ++++++++++++++++++++----------- src/librustc_errors/lib.rs | 24 ++++++++- 3 files changed, 84 insertions(+), 36 deletions(-) diff --git a/src/librustc_errors/diagnostic.rs b/src/librustc_errors/diagnostic.rs index aefe296ad0fa7..484cfd045a69f 100644 --- a/src/librustc_errors/diagnostic.rs +++ b/src/librustc_errors/diagnostic.rs @@ -1,4 +1,5 @@ use crate::CodeSuggestion; +use crate::SuggestionStyle; use crate::SubstitutionPart; use crate::Substitution; use crate::Applicability; @@ -243,7 +244,7 @@ impl Diagnostic { .collect(), }], msg: msg.to_owned(), - show_code_when_inline: true, + style: SuggestionStyle::ShowCode, applicability, }); self @@ -277,7 +278,7 @@ impl Diagnostic { }], }], msg: msg.to_owned(), - show_code_when_inline: true, + style: SuggestionStyle::ShowCode, applicability, }); self @@ -295,7 +296,7 @@ impl Diagnostic { }], }).collect(), msg: msg.to_owned(), - show_code_when_inline: true, + style: SuggestionStyle::ShowCode, applicability, }); self @@ -316,7 +317,7 @@ impl Diagnostic { }], }], msg: msg.to_owned(), - show_code_when_inline: false, + style: SuggestionStyle::HideCodeInline, applicability: applicability, }); self diff --git a/src/librustc_errors/emitter.rs b/src/librustc_errors/emitter.rs index 2821201173ea0..5696b2eee8956 100644 --- a/src/librustc_errors/emitter.rs +++ b/src/librustc_errors/emitter.rs @@ -2,7 +2,10 @@ use Destination::*; use syntax_pos::{SourceFile, Span, MultiSpan}; -use crate::{Level, CodeSuggestion, DiagnosticBuilder, SubDiagnostic, SourceMapperDyn, DiagnosticId}; +use crate::{ + Level, CodeSuggestion, DiagnosticBuilder, SubDiagnostic, + SuggestionStyle, SourceMapperDyn, DiagnosticId, +}; use crate::snippet::{Annotation, AnnotationType, Line, MultilineAnnotation, StyledString, Style}; use crate::styled_buffer::StyledBuffer; @@ -45,7 +48,7 @@ impl Emitter for EmitterWriter { // don't display multiline suggestions as labels !sugg.substitutions[0].parts[0].snippet.contains('\n') { let substitution = &sugg.substitutions[0].parts[0].snippet.trim(); - let msg = if substitution.len() == 0 || !sugg.show_code_when_inline { + let msg = if substitution.len() == 0 || sugg.style.hide_inline() { // This substitution is only removal or we explicitly don't want to show the // code inline, don't show it format!("help: {}", sugg.msg) @@ -942,14 +945,15 @@ impl EmitterWriter { } } - fn emit_message_default(&mut self, - msp: &MultiSpan, - msg: &[(String, Style)], - code: &Option, - level: &Level, - max_line_num_len: usize, - is_secondary: bool) - -> io::Result<()> { + fn emit_message_default( + &mut self, + msp: &MultiSpan, + msg: &[(String, Style)], + code: &Option, + level: &Level, + max_line_num_len: usize, + is_secondary: bool, + ) -> io::Result<()> { let mut buffer = StyledBuffer::new(); let header_style = if is_secondary { Style::HeaderMsg @@ -1184,11 +1188,12 @@ impl EmitterWriter { } - fn emit_suggestion_default(&mut self, - suggestion: &CodeSuggestion, - level: &Level, - max_line_num_len: usize) - -> io::Result<()> { + fn emit_suggestion_default( + &mut self, + suggestion: &CodeSuggestion, + level: &Level, + max_line_num_len: usize, + ) -> io::Result<()> { if let Some(ref sm) = self.sm { let mut buffer = StyledBuffer::new(); @@ -1198,11 +1203,13 @@ impl EmitterWriter { buffer.append(0, &level_str, Style::Level(level.clone())); buffer.append(0, ": ", Style::HeaderMsg); } - self.msg_to_buffer(&mut buffer, - &[(suggestion.msg.to_owned(), Style::NoStyle)], - max_line_num_len, - "suggestion", - Some(Style::HeaderMsg)); + self.msg_to_buffer( + &mut buffer, + &[(suggestion.msg.to_owned(), Style::NoStyle)], + max_line_num_len, + "suggestion", + Some(Style::HeaderMsg), + ); // Render the replacements for each suggestion let suggestions = suggestion.splice_lines(&**sm); @@ -1340,22 +1347,40 @@ impl EmitterWriter { if !self.short_message { for child in children { let span = child.render_span.as_ref().unwrap_or(&child.span); - match self.emit_message_default(&span, - &child.styled_message(), - &None, - &child.level, - max_line_num_len, - true) { + match self.emit_message_default( + &span, + &child.styled_message(), + &None, + &child.level, + max_line_num_len, + true, + ) { Err(e) => panic!("failed to emit error: {}", e), _ => () } } for sugg in suggestions { - match self.emit_suggestion_default(sugg, - &Level::Help, - max_line_num_len) { - Err(e) => panic!("failed to emit error: {}", e), - _ => () + if sugg.style == SuggestionStyle::HideCodeAlways { + match self.emit_message_default( + &MultiSpan::new(), + &[(sugg.msg.to_owned(), Style::HeaderMsg)], + &None, + &Level::Help, + max_line_num_len, + true, + ) { + Err(e) => panic!("failed to emit error: {}", e), + _ => () + } + } else { + match self.emit_suggestion_default( + sugg, + &Level::Help, + max_line_num_len, + ) { + Err(e) => panic!("failed to emit error: {}", e), + _ => () + } } } } diff --git a/src/librustc_errors/lib.rs b/src/librustc_errors/lib.rs index ea530fa1bfb73..be959a29a5577 100644 --- a/src/librustc_errors/lib.rs +++ b/src/librustc_errors/lib.rs @@ -68,6 +68,27 @@ pub enum Applicability { Unspecified, } +#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash, RustcEncodable, RustcDecodable)] +pub enum SuggestionStyle { + /// Hide the suggested code when displaying this suggestion inline. + HideCodeInline, + /// Always hide the suggested code. + HideCodeAlways, + /// Always show the suggested code. + /// This will *not* show the code if the suggestion is inline *and* the suggested code is + /// empty. + ShowCode, +} + +impl SuggestionStyle { + fn hide_inline(&self) -> bool { + match *self { + SuggestionStyle::HideCodeAlways | SuggestionStyle::HideCodeInline => true, + SuggestionStyle::ShowCode => false, + } + } +} + #[derive(Clone, Debug, PartialEq, Hash, RustcEncodable, RustcDecodable)] pub struct CodeSuggestion { /// Each substitute can have multiple variants due to multiple @@ -93,7 +114,8 @@ pub struct CodeSuggestion { /// ``` pub substitutions: Vec, pub msg: String, - pub show_code_when_inline: bool, + /// Visual representation of this suggestion. + pub style: SuggestionStyle, /// Whether or not the suggestion is approximate /// /// Sometimes we may show suggestions with placeholders, From 6ea159ea7e4d7fb4c4527a60cb80ba5347bcff66 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Fri, 8 Feb 2019 02:50:53 -0800 Subject: [PATCH 05/20] Expose hidden snippet suggestions --- src/librustc_errors/diagnostic.rs | 23 +++++++++++++++++++++++ src/librustc_errors/diagnostic_builder.rs | 20 ++++++++++++++++++++ 2 files changed, 43 insertions(+) diff --git a/src/librustc_errors/diagnostic.rs b/src/librustc_errors/diagnostic.rs index 484cfd045a69f..8394e66850e8c 100644 --- a/src/librustc_errors/diagnostic.rs +++ b/src/librustc_errors/diagnostic.rs @@ -323,6 +323,29 @@ impl Diagnostic { self } + /// Prints out a message with for a suggestion without showing the suggested code. + /// + /// This is intended to be used for suggestions that are obvious in what the changes need to + /// be from the message, showing the span label inline would be visually unpleasant + /// (marginally overlapping spans or multiline spans) and showing the snippet window wouldn't + /// improve understandability. + pub fn span_suggestion_hidden( + &mut self, sp: Span, msg: &str, suggestion: String, applicability: Applicability + ) -> &mut Self { + self.suggestions.push(CodeSuggestion { + substitutions: vec![Substitution { + parts: vec![SubstitutionPart { + snippet: suggestion, + span: sp, + }], + }], + msg: msg.to_owned(), + style: SuggestionStyle::HideCodeInline, + applicability: applicability, + }); + self + } + pub fn set_span>(&mut self, sp: S) -> &mut Self { self.span = sp.into(); self diff --git a/src/librustc_errors/diagnostic_builder.rs b/src/librustc_errors/diagnostic_builder.rs index fd4ea7f2d823f..5b37a7bb3467d 100644 --- a/src/librustc_errors/diagnostic_builder.rs +++ b/src/librustc_errors/diagnostic_builder.rs @@ -261,6 +261,26 @@ impl<'a> DiagnosticBuilder<'a> { ); self } + + pub fn span_suggestion_hidden( + &mut self, + sp: Span, + msg: &str, + suggestion: String, + applicability: Applicability, + ) -> &mut Self { + if !self.allow_suggestions { + return self + } + self.diagnostic.span_suggestion_hidden( + sp, + msg, + suggestion, + applicability, + ); + self + } + forward!(pub fn set_span>(&mut self, sp: S) -> &mut Self); forward!(pub fn code(&mut self, s: DiagnosticId) -> &mut Self); From 7cfba1c5e807821095c7b874c92bf4a11674be36 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Fri, 8 Feb 2019 03:02:42 -0800 Subject: [PATCH 06/20] Never inline HideCodeAlways suggestions --- src/librustc_errors/emitter.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/librustc_errors/emitter.rs b/src/librustc_errors/emitter.rs index 5696b2eee8956..6e41196099887 100644 --- a/src/librustc_errors/emitter.rs +++ b/src/librustc_errors/emitter.rs @@ -46,7 +46,10 @@ impl Emitter for EmitterWriter { // don't display long messages as labels sugg.msg.split_whitespace().count() < 10 && // don't display multiline suggestions as labels - !sugg.substitutions[0].parts[0].snippet.contains('\n') { + !sugg.substitutions[0].parts[0].snippet.contains('\n') && + // when this style is set we want the suggestion to be a message, not inline + sugg.style != SuggestionStyle::HideCodeAlways + { let substitution = &sugg.substitutions[0].parts[0].snippet.trim(); let msg = if substitution.len() == 0 || sugg.style.hide_inline() { // This substitution is only removal or we explicitly don't want to show the From 235523c7d4acdbd38a6b31c53b7969475d460e97 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Sat, 9 Feb 2019 03:39:08 -0800 Subject: [PATCH 07/20] Add way to completely hide suggestion from cli output --- src/librustc_errors/diagnostic.rs | 21 +++++++++++++++++++++ src/librustc_errors/diagnostic_builder.rs | 19 +++++++++++++++++++ src/librustc_errors/emitter.rs | 4 +++- src/librustc_errors/lib.rs | 6 ++++-- 4 files changed, 47 insertions(+), 3 deletions(-) diff --git a/src/librustc_errors/diagnostic.rs b/src/librustc_errors/diagnostic.rs index 8394e66850e8c..588cdfcb1afff 100644 --- a/src/librustc_errors/diagnostic.rs +++ b/src/librustc_errors/diagnostic.rs @@ -346,6 +346,27 @@ impl Diagnostic { self } + /// Adds a suggestion to the json output, but otherwise remains silent/undisplayed in the cli. + /// + /// This is intended to be used for suggestions that are *very* obvious in what the changes + /// need to be from the message, but we still want other tools to be able to apply them. + pub fn tool_only_span_suggestion( + &mut self, sp: Span, msg: &str, suggestion: String, applicability: Applicability + ) -> &mut Self { + self.suggestions.push(CodeSuggestion { + substitutions: vec![Substitution { + parts: vec![SubstitutionPart { + snippet: suggestion, + span: sp, + }], + }], + msg: msg.to_owned(), + style: SuggestionStyle::CompletelyHidden, + applicability: applicability, + }); + self + } + pub fn set_span>(&mut self, sp: S) -> &mut Self { self.span = sp.into(); self diff --git a/src/librustc_errors/diagnostic_builder.rs b/src/librustc_errors/diagnostic_builder.rs index 5b37a7bb3467d..9f838987f0c19 100644 --- a/src/librustc_errors/diagnostic_builder.rs +++ b/src/librustc_errors/diagnostic_builder.rs @@ -281,6 +281,25 @@ impl<'a> DiagnosticBuilder<'a> { self } + pub fn tool_only_span_suggestion( + &mut self, + sp: Span, + msg: &str, + suggestion: String, + applicability: Applicability, + ) -> &mut Self { + if !self.allow_suggestions { + return self + } + self.diagnostic.tool_only_span_suggestion( + sp, + msg, + suggestion, + applicability, + ); + self + } + forward!(pub fn set_span>(&mut self, sp: S) -> &mut Self); forward!(pub fn code(&mut self, s: DiagnosticId) -> &mut Self); diff --git a/src/librustc_errors/emitter.rs b/src/librustc_errors/emitter.rs index 6e41196099887..5e7c5315d6876 100644 --- a/src/librustc_errors/emitter.rs +++ b/src/librustc_errors/emitter.rs @@ -1363,7 +1363,9 @@ impl EmitterWriter { } } for sugg in suggestions { - if sugg.style == SuggestionStyle::HideCodeAlways { + if sugg.style == SuggestionStyle::CompletelyHidden { + // do not display this suggestion, it is meant only for tools + } else if sugg.style == SuggestionStyle::HideCodeAlways { match self.emit_message_default( &MultiSpan::new(), &[(sugg.msg.to_owned(), Style::HeaderMsg)], diff --git a/src/librustc_errors/lib.rs b/src/librustc_errors/lib.rs index be959a29a5577..7805bf697c64a 100644 --- a/src/librustc_errors/lib.rs +++ b/src/librustc_errors/lib.rs @@ -72,8 +72,10 @@ pub enum Applicability { pub enum SuggestionStyle { /// Hide the suggested code when displaying this suggestion inline. HideCodeInline, - /// Always hide the suggested code. + /// Always hide the suggested code but display the message. HideCodeAlways, + /// Do not display this suggestion in the cli output, it is only meant for tools. + CompletelyHidden, /// Always show the suggested code. /// This will *not* show the code if the suggestion is inline *and* the suggested code is /// empty. @@ -83,8 +85,8 @@ pub enum SuggestionStyle { impl SuggestionStyle { fn hide_inline(&self) -> bool { match *self { - SuggestionStyle::HideCodeAlways | SuggestionStyle::HideCodeInline => true, SuggestionStyle::ShowCode => false, + _ => true, } } } From 87dd2e1df95f96dbf08a0f3ae77a2dcbd6d384e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Mon, 11 Feb 2019 11:16:22 -0800 Subject: [PATCH 08/20] Use hidden suggestions for unused imports lint --- src/librustc/lint/builtin.rs | 2 +- src/librustc_errors/diagnostic.rs | 30 +++++++++++++++++-- src/librustc_errors/diagnostic_builder.rs | 18 +++++++++++ src/librustc_errors/emitter.rs | 4 ++- src/test/ui/bad/bad-lint-cap2.stderr | 2 +- src/test/ui/bad/bad-lint-cap3.stderr | 2 +- src/test/ui/imports/unused.stderr | 2 +- src/test/ui/issues/issue-30730.stderr | 2 +- ...directives-on-use-items-issue-10534.stderr | 4 +-- src/test/ui/lint/lint-unused-imports.stderr | 16 +++++----- .../ui/lint/lints-in-foreign-macros.stderr | 6 ++-- .../rfc-2166-underscore-imports/basic.stderr | 4 +-- .../unused-2018.stderr | 4 +-- src/test/ui/span/multispan-import-lint.stderr | 4 --- .../use-nested-groups-unused-imports.stderr | 8 ++--- 15 files changed, 73 insertions(+), 35 deletions(-) diff --git a/src/librustc/lint/builtin.rs b/src/librustc/lint/builtin.rs index cb31441ca47e1..98b9d637b7203 100644 --- a/src/librustc/lint/builtin.rs +++ b/src/librustc/lint/builtin.rs @@ -557,7 +557,7 @@ impl BuiltinLintDiagnostics { } BuiltinLintDiagnostics::UnusedImports(message, replaces) => { if !replaces.is_empty() { - db.multipart_suggestion( + db.tool_only_multipart_suggestion( &message, replaces, Applicability::MachineApplicable, diff --git a/src/librustc_errors/diagnostic.rs b/src/librustc_errors/diagnostic.rs index 588cdfcb1afff..f0083949396b4 100644 --- a/src/librustc_errors/diagnostic.rs +++ b/src/librustc_errors/diagnostic.rs @@ -250,6 +250,32 @@ impl Diagnostic { self } + /// Prints out a message with for a multipart suggestion without showing the suggested code. + /// + /// This is intended to be used for suggestions that are obvious in what the changes need to + /// be from the message, showing the span label inline would be visually unpleasant + /// (marginally overlapping spans or multiline spans) and showing the snippet window wouldn't + /// improve understandability. + pub fn tool_only_multipart_suggestion( + &mut self, + msg: &str, + suggestion: Vec<(Span, String)>, + applicability: Applicability, + ) -> &mut Self { + self.suggestions.push(CodeSuggestion { + substitutions: vec![Substitution { + parts: suggestion + .into_iter() + .map(|(span, snippet)| SubstitutionPart { snippet, span }) + .collect(), + }], + msg: msg.to_owned(), + style: SuggestionStyle::CompletelyHidden, + applicability, + }); + self + } + /// Prints out a message with a suggested edit of the code. /// /// In case of short messages and a simple suggestion, rustc displays it as a label: @@ -318,7 +344,7 @@ impl Diagnostic { }], msg: msg.to_owned(), style: SuggestionStyle::HideCodeInline, - applicability: applicability, + applicability, }); self } @@ -341,7 +367,7 @@ impl Diagnostic { }], msg: msg.to_owned(), style: SuggestionStyle::HideCodeInline, - applicability: applicability, + applicability, }); self } diff --git a/src/librustc_errors/diagnostic_builder.rs b/src/librustc_errors/diagnostic_builder.rs index 9f838987f0c19..4ed7b0a2456e7 100644 --- a/src/librustc_errors/diagnostic_builder.rs +++ b/src/librustc_errors/diagnostic_builder.rs @@ -205,6 +205,24 @@ impl<'a> DiagnosticBuilder<'a> { self } + pub fn tool_only_multipart_suggestion( + &mut self, + msg: &str, + suggestion: Vec<(Span, String)>, + applicability: Applicability, + ) -> &mut Self { + if !self.allow_suggestions { + return self + } + self.diagnostic.tool_only_multipart_suggestion( + msg, + suggestion, + applicability, + ); + self + } + + pub fn span_suggestion( &mut self, sp: Span, diff --git a/src/librustc_errors/emitter.rs b/src/librustc_errors/emitter.rs index 5e7c5315d6876..eaae1e9e84840 100644 --- a/src/librustc_errors/emitter.rs +++ b/src/librustc_errors/emitter.rs @@ -48,7 +48,9 @@ impl Emitter for EmitterWriter { // don't display multiline suggestions as labels !sugg.substitutions[0].parts[0].snippet.contains('\n') && // when this style is set we want the suggestion to be a message, not inline - sugg.style != SuggestionStyle::HideCodeAlways + sugg.style != SuggestionStyle::HideCodeAlways && + // trivial suggestion for tooling's sake, never shown + sugg.style != SuggestionStyle::CompletelyHidden { let substitution = &sugg.substitutions[0].parts[0].snippet.trim(); let msg = if substitution.len() == 0 || sugg.style.hide_inline() { diff --git a/src/test/ui/bad/bad-lint-cap2.stderr b/src/test/ui/bad/bad-lint-cap2.stderr index b9638722778ee..d7ec41489d156 100644 --- a/src/test/ui/bad/bad-lint-cap2.stderr +++ b/src/test/ui/bad/bad-lint-cap2.stderr @@ -2,7 +2,7 @@ error: unused import: `std::option` --> $DIR/bad-lint-cap2.rs:6:5 | LL | use std::option; //~ ERROR - | ----^^^^^^^^^^^- help: remove the whole `use` item + | ^^^^^^^^^^^ | note: lint level defined here --> $DIR/bad-lint-cap2.rs:4:9 diff --git a/src/test/ui/bad/bad-lint-cap3.stderr b/src/test/ui/bad/bad-lint-cap3.stderr index 21ed50b550afc..5bf0b089afa20 100644 --- a/src/test/ui/bad/bad-lint-cap3.stderr +++ b/src/test/ui/bad/bad-lint-cap3.stderr @@ -2,7 +2,7 @@ warning: unused import: `std::option` --> $DIR/bad-lint-cap3.rs:7:5 | LL | use std::option; //~ WARN - | ----^^^^^^^^^^^- help: remove the whole `use` item + | ^^^^^^^^^^^ | note: lint level defined here --> $DIR/bad-lint-cap3.rs:4:9 diff --git a/src/test/ui/imports/unused.stderr b/src/test/ui/imports/unused.stderr index fa82e974e1e29..b56e930158cc1 100644 --- a/src/test/ui/imports/unused.stderr +++ b/src/test/ui/imports/unused.stderr @@ -2,7 +2,7 @@ error: unused import: `super::f` --> $DIR/unused.rs:7:24 | LL | pub(super) use super::f; //~ ERROR unused - | ---------------^^^^^^^^- help: remove the whole `use` item + | ^^^^^^^^ | note: lint level defined here --> $DIR/unused.rs:1:9 diff --git a/src/test/ui/issues/issue-30730.stderr b/src/test/ui/issues/issue-30730.stderr index 3cfadd33b8fec..0a901076f467a 100644 --- a/src/test/ui/issues/issue-30730.stderr +++ b/src/test/ui/issues/issue-30730.stderr @@ -2,7 +2,7 @@ error: unused import: `std::thread` --> $DIR/issue-30730.rs:3:5 | LL | use std::thread; - | ----^^^^^^^^^^^- help: remove the whole `use` item + | ^^^^^^^^^^^ | note: lint level defined here --> $DIR/issue-30730.rs:2:9 diff --git a/src/test/ui/lint/lint-directives-on-use-items-issue-10534.stderr b/src/test/ui/lint/lint-directives-on-use-items-issue-10534.stderr index e588d24517c8c..170b98a12a848 100644 --- a/src/test/ui/lint/lint-directives-on-use-items-issue-10534.stderr +++ b/src/test/ui/lint/lint-directives-on-use-items-issue-10534.stderr @@ -2,7 +2,7 @@ error: unused import: `a::x` --> $DIR/lint-directives-on-use-items-issue-10534.rs:12:9 | LL | use a::x; //~ ERROR: unused import - | ----^^^^- help: remove the whole `use` item + | ^^^^ | note: lint level defined here --> $DIR/lint-directives-on-use-items-issue-10534.rs:1:9 @@ -14,7 +14,7 @@ error: unused import: `a::y` --> $DIR/lint-directives-on-use-items-issue-10534.rs:21:9 | LL | use a::y; //~ ERROR: unused import - | ----^^^^- help: remove the whole `use` item + | ^^^^ | note: lint level defined here --> $DIR/lint-directives-on-use-items-issue-10534.rs:20:12 diff --git a/src/test/ui/lint/lint-unused-imports.stderr b/src/test/ui/lint/lint-unused-imports.stderr index 7970b0201db70..18f2fae0067eb 100644 --- a/src/test/ui/lint/lint-unused-imports.stderr +++ b/src/test/ui/lint/lint-unused-imports.stderr @@ -2,7 +2,7 @@ error: unused import: `std::fmt::{}` --> $DIR/lint-unused-imports.rs:8:5 | LL | use std::fmt::{}; - | ----^^^^^^^^^^^^- help: remove the whole `use` item + | ^^^^^^^^^^^^ | note: lint level defined here --> $DIR/lint-unused-imports.rs:1:9 @@ -14,39 +14,37 @@ error: unused imports: `None`, `Some` --> $DIR/lint-unused-imports.rs:12:27 | LL | use std::option::Option::{Some, None}; - | --------------------------^^^^--^^^^-- help: remove the whole `use` item + | ^^^^ ^^^^ error: unused import: `test::A` --> $DIR/lint-unused-imports.rs:15:5 | LL | use test::A; //~ ERROR unused import: `test::A` - | ----^^^^^^^- help: remove the whole `use` item + | ^^^^^^^ error: unused import: `bar` --> $DIR/lint-unused-imports.rs:24:18 | LL | use test2::{foo, bar}; //~ ERROR unused import: `bar` - | --^^^ - | | - | help: remove the unused import + | ^^^ error: unused import: `foo::Square` --> $DIR/lint-unused-imports.rs:52:13 | LL | use foo::Square; //~ ERROR unused import: `foo::Square` - | ----^^^^^^^^^^^- help: remove the whole `use` item + | ^^^^^^^^^^^ error: unused import: `self::g` --> $DIR/lint-unused-imports.rs:68:9 | LL | use self::g; //~ ERROR unused import: `self::g` - | ----^^^^^^^- help: remove the whole `use` item + | ^^^^^^^ error: unused import: `test2::foo` --> $DIR/lint-unused-imports.rs:77:9 | LL | use test2::foo; //~ ERROR unused import: `test2::foo` - | ----^^^^^^^^^^- help: remove the whole `use` item + | ^^^^^^^^^^ error: unused import: `test::B2` --> $DIR/lint-unused-imports.rs:20:5 diff --git a/src/test/ui/lint/lints-in-foreign-macros.stderr b/src/test/ui/lint/lints-in-foreign-macros.stderr index b808ca708a311..8287ca5692bd9 100644 --- a/src/test/ui/lint/lints-in-foreign-macros.stderr +++ b/src/test/ui/lint/lints-in-foreign-macros.stderr @@ -2,7 +2,7 @@ warning: unused import: `std::string::ToString` --> $DIR/lints-in-foreign-macros.rs:11:16 | LL | () => {use std::string::ToString;} //~ WARN: unused import - | ----^^^^^^^^^^^^^^^^^^^^^- help: remove the whole `use` item + | ^^^^^^^^^^^^^^^^^^^^^ ... LL | mod a { foo!(); } | ------- in this macro invocation @@ -17,13 +17,13 @@ warning: unused import: `std::string::ToString` --> $DIR/lints-in-foreign-macros.rs:16:18 | LL | mod c { baz!(use std::string::ToString;); } //~ WARN: unused import - | ----^^^^^^^^^^^^^^^^^^^^^- help: remove the whole `use` item + | ^^^^^^^^^^^^^^^^^^^^^ warning: unused import: `std::string::ToString` --> $DIR/lints-in-foreign-macros.rs:17:19 | LL | mod d { baz2!(use std::string::ToString;); } //~ WARN: unused import - | ----^^^^^^^^^^^^^^^^^^^^^- help: remove the whole `use` item + | ^^^^^^^^^^^^^^^^^^^^^ warning: missing documentation for crate --> $DIR/lints-in-foreign-macros.rs:4:1 diff --git a/src/test/ui/rfc-2166-underscore-imports/basic.stderr b/src/test/ui/rfc-2166-underscore-imports/basic.stderr index c7b36eaf2e76b..3080359192603 100644 --- a/src/test/ui/rfc-2166-underscore-imports/basic.stderr +++ b/src/test/ui/rfc-2166-underscore-imports/basic.stderr @@ -2,7 +2,7 @@ warning: unused import: `m::Tr1 as _` --> $DIR/basic.rs:26:9 | LL | use m::Tr1 as _; //~ WARN unused import - | ----^^^^^^^^^^^- help: remove the whole `use` item + | ^^^^^^^^^^^ | note: lint level defined here --> $DIR/basic.rs:4:9 @@ -14,5 +14,5 @@ warning: unused import: `S as _` --> $DIR/basic.rs:27:9 | LL | use S as _; //~ WARN unused import - | ----^^^^^^- help: remove the whole `use` item + | ^^^^^^ diff --git a/src/test/ui/rfc-2166-underscore-imports/unused-2018.stderr b/src/test/ui/rfc-2166-underscore-imports/unused-2018.stderr index 0bbc17276d98b..4163c2876074b 100644 --- a/src/test/ui/rfc-2166-underscore-imports/unused-2018.stderr +++ b/src/test/ui/rfc-2166-underscore-imports/unused-2018.stderr @@ -2,7 +2,7 @@ error: unused import: `core::any` --> $DIR/unused-2018.rs:6:9 | LL | use core::any; //~ ERROR unused import: `core::any` - | ----^^^^^^^^^- help: remove the whole `use` item + | ^^^^^^^^^ | note: lint level defined here --> $DIR/unused-2018.rs:3:9 @@ -14,7 +14,7 @@ error: unused import: `core` --> $DIR/unused-2018.rs:10:9 | LL | use core; //~ ERROR unused import: `core` - | ----^^^^- help: remove the whole `use` item + | ^^^^ error: aborting due to 2 previous errors diff --git a/src/test/ui/span/multispan-import-lint.stderr b/src/test/ui/span/multispan-import-lint.stderr index 6bd0e9be81f5e..a730d081b7c03 100644 --- a/src/test/ui/span/multispan-import-lint.stderr +++ b/src/test/ui/span/multispan-import-lint.stderr @@ -10,8 +10,4 @@ note: lint level defined here LL | #![warn(unused)] | ^^^^^^ = note: #[warn(unused_imports)] implied by #[warn(unused)] -help: remove the unused imports - | -LL | use std::cmp::{min}; - | -- -- diff --git a/src/test/ui/use/use-nested-groups-unused-imports.stderr b/src/test/ui/use/use-nested-groups-unused-imports.stderr index 6af6f449de5e6..c8df6cbc57dca 100644 --- a/src/test/ui/use/use-nested-groups-unused-imports.stderr +++ b/src/test/ui/use/use-nested-groups-unused-imports.stderr @@ -2,7 +2,7 @@ error: unused imports: `*`, `Foo`, `baz::{}`, `foobar::*` --> $DIR/use-nested-groups-unused-imports.rs:16:11 | LL | use foo::{Foo, bar::{baz::{}, foobar::*}, *}; - | ----------^^^--------^^^^^^^--^^^^^^^^^---^-- help: remove the whole `use` item + | ^^^ ^^^^^^^ ^^^^^^^^^ ^ | note: lint level defined here --> $DIR/use-nested-groups-unused-imports.rs:3:9 @@ -14,15 +14,13 @@ error: unused import: `*` --> $DIR/use-nested-groups-unused-imports.rs:18:24 | LL | use foo::bar::baz::{*, *}; - | --^ - | | - | help: remove the unused import + | ^ error: unused import: `foo::{}` --> $DIR/use-nested-groups-unused-imports.rs:20:5 | LL | use foo::{}; - | ----^^^^^^^- help: remove the whole `use` item + | ^^^^^^^ error: aborting due to 3 previous errors From 86f2d2278a987ea26bf27299f95c61ddc41d6165 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?John=20K=C3=A5re=20Alsaker?= Date: Sat, 8 Dec 2018 20:30:23 +0100 Subject: [PATCH 09/20] Introduce rustc_interface and move some methods there --- Cargo.lock | 31 + src/bootstrap/bin/rustc.rs | 2 + src/librustc/dep_graph/dep_node.rs | 1 + src/librustc/session/mod.rs | 109 +-- src/librustc/ty/context.rs | 4 +- src/librustc/ty/query/config.rs | 6 + src/librustc/ty/query/mod.rs | 8 +- src/librustc/ty/query/plumbing.rs | 1 + src/librustc_driver/Cargo.toml | 1 + src/librustc_driver/driver.rs | 475 +----------- src/librustc_driver/lib.rs | 306 +------- src/librustc_driver/pretty.rs | 222 +----- src/librustc_driver/test.rs | 11 +- src/librustc_interface/Cargo.toml | 35 + src/librustc_interface/lib.rs | 43 ++ src/librustc_interface/passes.rs | 296 ++++++++ .../proc_macro_decls.rs | 0 .../profile/mod.rs | 0 .../profile/trace.rs | 0 src/librustc_interface/util.rs | 702 ++++++++++++++++++ src/librustc_typeck/check/mod.rs | 6 +- src/librustc_typeck/lib.rs | 11 +- src/librustdoc/core.rs | 11 +- src/librustdoc/lib.rs | 1 + src/librustdoc/test.rs | 15 +- src/test/run-make-fulldeps/issue-19371/foo.rs | 4 +- .../rustdoc-ui/failed-doctest-output.stdout | 4 +- src/test/ui/issues/issue-23302-3.stderr | 1 + 28 files changed, 1297 insertions(+), 1009 deletions(-) create mode 100644 src/librustc_interface/Cargo.toml create mode 100644 src/librustc_interface/lib.rs create mode 100644 src/librustc_interface/passes.rs rename src/{librustc_driver => librustc_interface}/proc_macro_decls.rs (100%) rename src/{librustc_driver => librustc_interface}/profile/mod.rs (100%) rename src/{librustc_driver => librustc_interface}/profile/trace.rs (100%) create mode 100644 src/librustc_interface/util.rs diff --git a/Cargo.lock b/Cargo.lock index a36df24359df7..e34d737eb91bd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2716,6 +2716,7 @@ dependencies = [ "rustc_data_structures 0.0.0", "rustc_errors 0.0.0", "rustc_incremental 0.0.0", + "rustc_interface 0.0.0", "rustc_lint 0.0.0", "rustc_metadata 0.0.0", "rustc_mir 0.0.0", @@ -2768,6 +2769,36 @@ dependencies = [ "syntax_pos 0.0.0", ] +[[package]] +name = "rustc_interface" +version = "0.0.0" +dependencies = [ + "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc 0.0.0", + "rustc-rayon 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc_allocator 0.0.0", + "rustc_borrowck 0.0.0", + "rustc_codegen_utils 0.0.0", + "rustc_data_structures 0.0.0", + "rustc_errors 0.0.0", + "rustc_incremental 0.0.0", + "rustc_lint 0.0.0", + "rustc_metadata 0.0.0", + "rustc_mir 0.0.0", + "rustc_passes 0.0.0", + "rustc_plugin 0.0.0", + "rustc_privacy 0.0.0", + "rustc_resolve 0.0.0", + "rustc_traits 0.0.0", + "rustc_typeck 0.0.0", + "scoped-tls 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "serialize 0.0.0", + "smallvec 0.6.7 (registry+https://github.com/rust-lang/crates.io-index)", + "syntax 0.0.0", + "syntax_ext 0.0.0", + "syntax_pos 0.0.0", +] + [[package]] name = "rustc_lint" version = "0.0.0" diff --git a/src/bootstrap/bin/rustc.rs b/src/bootstrap/bin/rustc.rs index 7a765973e20f8..03a7535eef125 100644 --- a/src/bootstrap/bin/rustc.rs +++ b/src/bootstrap/bin/rustc.rs @@ -109,6 +109,8 @@ fn main() { // actually downloaded, so we just always pass the `--sysroot` option. cmd.arg("--sysroot").arg(&sysroot); + cmd.arg("-Zexternal-macro-backtrace"); + // When we build Rust dylibs they're all intended for intermediate // usage, so make sure we pass the -Cprefer-dynamic flag instead of // linking all deps statically into the dylib. diff --git a/src/librustc/dep_graph/dep_node.rs b/src/librustc/dep_graph/dep_node.rs index 796739c872174..d8546a678f549 100644 --- a/src/librustc/dep_graph/dep_node.rs +++ b/src/librustc/dep_graph/dep_node.rs @@ -456,6 +456,7 @@ define_dep_nodes!( <'tcx> [eval_always] CoherenceInherentImplOverlapCheck, [] CoherenceCheckTrait(DefId), [eval_always] PrivacyAccessLevels(CrateNum), + [eval_always] Analysis(CrateNum), // Represents the MIR for a fn; also used as the task node for // things read/modify that MIR. diff --git a/src/librustc/session/mod.rs b/src/librustc/session/mod.rs index 51b6205facb9e..3d6d8a6914d95 100644 --- a/src/librustc/session/mod.rs +++ b/src/librustc/session/mod.rs @@ -899,14 +899,14 @@ impl Session { /// Returns the number of query threads that should be used for this /// compilation - pub fn threads_from_opts(opts: &config::Options) -> usize { - opts.debugging_opts.threads.unwrap_or(::num_cpus::get()) + pub fn threads_from_count(query_threads: Option) -> usize { + query_threads.unwrap_or(::num_cpus::get()) } /// Returns the number of query threads that should be used for this /// compilation pub fn threads(&self) -> usize { - Self::threads_from_opts(&self.opts) + Self::threads_from_count(self.opts.debugging_opts.threads) } /// Returns the number of codegen units that should be used for this @@ -1023,16 +1023,67 @@ pub fn build_session( local_crate_source_file, registry, Lrc::new(source_map::SourceMap::new(file_path_mapping)), - None, + DiagnosticOutput::Default, + Default::default(), ) } +fn default_emitter( + sopts: &config::Options, + registry: errors::registry::Registry, + source_map: &Lrc, + emitter_dest: Option>, +) -> Box { + match (sopts.error_format, emitter_dest) { + (config::ErrorOutputType::HumanReadable(color_config), None) => Box::new( + EmitterWriter::stderr( + color_config, + Some(source_map.clone()), + false, + sopts.debugging_opts.teach, + ).ui_testing(sopts.debugging_opts.ui_testing), + ), + (config::ErrorOutputType::HumanReadable(_), Some(dst)) => Box::new( + EmitterWriter::new(dst, Some(source_map.clone()), false, false) + .ui_testing(sopts.debugging_opts.ui_testing), + ), + (config::ErrorOutputType::Json(pretty), None) => Box::new( + JsonEmitter::stderr( + Some(registry), + source_map.clone(), + pretty, + ).ui_testing(sopts.debugging_opts.ui_testing), + ), + (config::ErrorOutputType::Json(pretty), Some(dst)) => Box::new( + JsonEmitter::new( + dst, + Some(registry), + source_map.clone(), + pretty, + ).ui_testing(sopts.debugging_opts.ui_testing), + ), + (config::ErrorOutputType::Short(color_config), None) => Box::new( + EmitterWriter::stderr(color_config, Some(source_map.clone()), true, false), + ), + (config::ErrorOutputType::Short(_), Some(dst)) => { + Box::new(EmitterWriter::new(dst, Some(source_map.clone()), true, false)) + } + } +} + +pub enum DiagnosticOutput { + Default, + Raw(Box), + Emitter(Box) +} + pub fn build_session_with_source_map( sopts: config::Options, local_crate_source_file: Option, registry: errors::registry::Registry, source_map: Lrc, - emitter_dest: Option>, + diagnostics_output: DiagnosticOutput, + lint_caps: FxHashMap, ) -> Session { // FIXME: This is not general enough to make the warning lint completely override // normal diagnostic warnings, since the warning lint can also be denied and changed @@ -1054,42 +1105,13 @@ pub fn build_session_with_source_map( let external_macro_backtrace = sopts.debugging_opts.external_macro_backtrace; - let emitter: Box = - match (sopts.error_format, emitter_dest) { - (config::ErrorOutputType::HumanReadable(color_config), None) => Box::new( - EmitterWriter::stderr( - color_config, - Some(source_map.clone()), - false, - sopts.debugging_opts.teach, - ).ui_testing(sopts.debugging_opts.ui_testing), - ), - (config::ErrorOutputType::HumanReadable(_), Some(dst)) => Box::new( - EmitterWriter::new(dst, Some(source_map.clone()), false, false) - .ui_testing(sopts.debugging_opts.ui_testing), - ), - (config::ErrorOutputType::Json(pretty), None) => Box::new( - JsonEmitter::stderr( - Some(registry), - source_map.clone(), - pretty, - ).ui_testing(sopts.debugging_opts.ui_testing), - ), - (config::ErrorOutputType::Json(pretty), Some(dst)) => Box::new( - JsonEmitter::new( - dst, - Some(registry), - source_map.clone(), - pretty, - ).ui_testing(sopts.debugging_opts.ui_testing), - ), - (config::ErrorOutputType::Short(color_config), None) => Box::new( - EmitterWriter::stderr(color_config, Some(source_map.clone()), true, false), - ), - (config::ErrorOutputType::Short(_), Some(dst)) => { - Box::new(EmitterWriter::new(dst, Some(source_map.clone()), true, false)) - } - }; + let emitter = match diagnostics_output { + DiagnosticOutput::Default => default_emitter(&sopts, registry, &source_map, None), + DiagnosticOutput::Raw(write) => { + default_emitter(&sopts, registry, &source_map, Some(write)) + } + DiagnosticOutput::Emitter(emitter) => emitter, + }; let diagnostic_handler = errors::Handler::with_emitter_and_flags( emitter, @@ -1103,7 +1125,7 @@ pub fn build_session_with_source_map( }, ); - build_session_(sopts, local_crate_source_file, diagnostic_handler, source_map) + build_session_(sopts, local_crate_source_file, diagnostic_handler, source_map, lint_caps) } pub fn build_session_( @@ -1111,6 +1133,7 @@ pub fn build_session_( local_crate_source_file: Option, span_diagnostic: errors::Handler, source_map: Lrc, + driver_lint_caps: FxHashMap, ) -> Session { let host_triple = TargetTriple::from_triple(config::host_triple()); let host = Target::search(&host_triple).unwrap_or_else(|e| @@ -1235,7 +1258,7 @@ pub fn build_session_( }, has_global_allocator: Once::new(), has_panic_handler: Once::new(), - driver_lint_caps: Default::default(), + driver_lint_caps, }; validate_commandline_args_with_session_available(&sess); diff --git a/src/librustc/ty/context.rs b/src/librustc/ty/context.rs index 95287efd0acb0..814be9962ba2f 100644 --- a/src/librustc/ty/context.rs +++ b/src/librustc/ty/context.rs @@ -50,7 +50,7 @@ use rustc_data_structures::stable_hasher::{HashStable, hash_stable_hashmap, StableVec}; use arena::{TypedArena, SyncDroplessArena}; use rustc_data_structures::indexed_vec::{Idx, IndexVec}; -use rustc_data_structures::sync::{self, Lrc, Lock, WorkerLocal}; +use rustc_data_structures::sync::{Lrc, Lock, WorkerLocal}; use std::any::Any; use std::borrow::Borrow; use std::cmp::Ordering; @@ -1277,8 +1277,6 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> { let gcx = arenas.global_ctxt.as_ref().unwrap(); - sync::assert_send_val(&gcx); - let r = tls::enter_global(gcx, f); gcx.queries.record_computed_queries(s); diff --git a/src/librustc/ty/query/config.rs b/src/librustc/ty/query/config.rs index a3ee92f8e1263..353a93e78ef2a 100644 --- a/src/librustc/ty/query/config.rs +++ b/src/librustc/ty/query/config.rs @@ -610,6 +610,12 @@ impl<'tcx> QueryDescription<'tcx> for queries::extern_crate<'tcx> { } } +impl<'tcx> QueryDescription<'tcx> for queries::analysis<'tcx> { + fn describe(_tcx: TyCtxt<'_, '_, '_>, _: CrateNum) -> Cow<'static, str> { + "running analysis passes on this crate".into() + } +} + impl<'tcx> QueryDescription<'tcx> for queries::lint_levels<'tcx> { fn describe(_tcx: TyCtxt<'_, '_, '_>, _: CrateNum) -> Cow<'static, str> { "computing the lint levels for items in this crate".into() diff --git a/src/librustc/ty/query/mod.rs b/src/librustc/ty/query/mod.rs index 67a5c7d6c9a64..d38b7a2650908 100644 --- a/src/librustc/ty/query/mod.rs +++ b/src/librustc/ty/query/mod.rs @@ -21,7 +21,7 @@ use crate::mir::interpret::{ConstEvalRawResult, ConstEvalResult}; use crate::mir::mono::CodegenUnit; use crate::mir; use crate::mir::interpret::GlobalId; -use crate::session::{CompileResult, CrateDisambiguator}; +use crate::session::CrateDisambiguator; use crate::session::config::{EntryFnType, OutputFilenames, OptLevel}; use crate::traits::{self, Vtable}; use crate::traits::query::{ @@ -99,6 +99,9 @@ pub use self::on_disk_cache::OnDiskCache; // as they will raise an fatal error on query cycles instead. define_queries! { <'tcx> Other { + /// Run analysis passes on the crate + [] fn analysis: Analysis(CrateNum) -> Result<(), ErrorReported>, + /// Records the type of every item. [] fn type_of: TypeOfItem(DefId) -> Ty<'tcx>, @@ -281,7 +284,8 @@ define_queries! { <'tcx> }, TypeChecking { - [] fn typeck_item_bodies: typeck_item_bodies_dep_node(CrateNum) -> CompileResult, + [] fn typeck_item_bodies: + typeck_item_bodies_dep_node(CrateNum) -> Result<(), ErrorReported>, [] fn typeck_tables_of: TypeckTables(DefId) -> &'tcx ty::TypeckTables<'tcx>, }, diff --git a/src/librustc/ty/query/plumbing.rs b/src/librustc/ty/query/plumbing.rs index 9b2a70a6a6d20..a14e04fffa0bb 100644 --- a/src/librustc/ty/query/plumbing.rs +++ b/src/librustc/ty/query/plumbing.rs @@ -1367,6 +1367,7 @@ pub fn force_from_dep_node<'a, 'gcx, 'lcx>(tcx: TyCtxt<'a, 'gcx, 'lcx>, DepKind::CrateHash => { force!(crate_hash, krate!()); } DepKind::OriginalCrateName => { force!(original_crate_name, krate!()); } DepKind::ExtraFileName => { force!(extra_filename, krate!()); } + DepKind::Analysis => { force!(analysis, krate!()); } DepKind::AllTraitImplementations => { force!(all_trait_implementations, krate!()); diff --git a/src/librustc_driver/Cargo.toml b/src/librustc_driver/Cargo.toml index 8bcda409e6663..281030eec494d 100644 --- a/src/librustc_driver/Cargo.toml +++ b/src/librustc_driver/Cargo.toml @@ -33,6 +33,7 @@ rustc_save_analysis = { path = "../librustc_save_analysis" } rustc_traits = { path = "../librustc_traits" } rustc_codegen_utils = { path = "../librustc_codegen_utils" } rustc_typeck = { path = "../librustc_typeck" } +rustc_interface = { path = "../librustc_interface" } serialize = { path = "../libserialize" } syntax = { path = "../libsyntax" } smallvec = { version = "0.6.7", features = ["union", "may_dangle"] } diff --git a/src/librustc_driver/driver.rs b/src/librustc_driver/driver.rs index 09804a706ec98..086286accd4cd 100644 --- a/src/librustc_driver/driver.rs +++ b/src/librustc_driver/driver.rs @@ -2,41 +2,38 @@ use rustc::dep_graph::DepGraph; use rustc::hir; use rustc::hir::lowering::lower_crate; use rustc::hir::map as hir_map; +use rustc::hir::def_id::LOCAL_CRATE; use rustc::lint; use rustc::middle::{self, reachable, resolve_lifetime, stability}; use rustc::ty::{self, AllArenas, Resolutions, TyCtxt}; use rustc::traits; use rustc::util::common::{install_panic_hook, time, ErrorReported}; use rustc::util::profiling::ProfileCategory; -use rustc::session::{CompileResult, CrateDisambiguator, Session}; +use rustc::session::{CompileResult, Session}; use rustc::session::CompileIncomplete; use rustc::session::config::{self, Input, OutputFilenames, OutputType}; use rustc::session::search_paths::PathKind; use rustc_allocator as allocator; use rustc_borrowck as borrowck; use rustc_codegen_utils::codegen_backend::CodegenBackend; -use rustc_data_structures::fingerprint::Fingerprint; -use rustc_data_structures::stable_hasher::StableHasher; use rustc_data_structures::sync::{self, Lock}; 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, layout_test}; +use rustc_passes::{self, ast_validation, hir_stats}; use rustc_plugin as plugin; use rustc_plugin::registry::Registry; use rustc_privacy; use rustc_resolve::{Resolver, ResolverArenas}; use rustc_traits; use rustc_typeck as typeck; -use syntax::{self, ast, attr, diagnostics, visit}; +use syntax::{self, ast, diagnostics, visit}; use syntax::early_buffered_lints::BufferedEarlyLint; use syntax::ext::base::ExtCtxt; use syntax::mut_visit::MutVisitor; use syntax::parse::{self, PResult}; use syntax::util::node_count::NodeCounter; -use syntax::util::lev_distance::find_best_match_for_name; -use syntax::symbol::Symbol; use syntax_pos::{FileName, hygiene}; use syntax_ext; @@ -46,14 +43,11 @@ use std::any::Any; use std::env; use std::ffi::OsString; use std::fs; -use std::io::{self, Write}; use std::iter; use std::path::{Path, PathBuf}; use std::sync::mpsc; -use pretty::ReplaceBodyWithLoop; -use proc_macro_decls; -use profile; +use rustc_interface::{util, profile, passes}; use super::Compilation; #[cfg(not(parallel_compiler))] @@ -78,7 +72,7 @@ pub fn spawn_thread_pool R + sync::Send, R: sync:: let gcx_ptr = &Lock::new(0); let config = ThreadPoolBuilder::new() - .num_threads(Session::threads_from_opts(&opts)) + .num_threads(Session::threads_from_count(opts.debugging_opts.threads)) .deadlock_handler(|| unsafe { ty::query::handle_deadlock() }) .stack_size(::STACK_SIZE); @@ -160,7 +154,7 @@ pub fn compile_input( (compile_state.krate.unwrap(), compile_state.registry) }; - let outputs = build_output_filenames(input, outdir, output, &krate.attrs, sess); + let outputs = util::build_output_filenames(input, outdir, output, &krate.attrs, sess); let crate_name = ::rustc_codegen_utils::link::find_crate_name(Some(sess), &krate.attrs, input); install_panic_hook(); @@ -194,12 +188,17 @@ pub fn compile_input( )? }; - let output_paths = generated_output_paths(sess, &outputs, output.is_some(), &crate_name); + let output_paths = passes::generated_output_paths( + sess, + &outputs, + output.is_some(), + &crate_name + ); // Ensure the source file isn't accidentally overwritten during compilation. if let Some(ref input_path) = *input_path { if sess.opts.will_create_output_file() { - if output_contains_path(&output_paths, input_path) { + if passes::output_contains_path(&output_paths, input_path) { sess.err(&format!( "the input file \"{}\" would be overwritten by the generated \ executable", @@ -207,7 +206,7 @@ pub fn compile_input( )); return Err(CompileIncomplete::Stopped); } - if let Some(dir_path) = output_conflicts_with_dir(&output_paths) { + if let Some(dir_path) = passes::output_conflicts_with_dir(&output_paths) { sess.err(&format!( "the generated executable for the input file \"{}\" conflicts with the \ existing directory \"{}\"", @@ -219,7 +218,7 @@ pub fn compile_input( } } - write_out_deps(sess, &outputs, &output_paths); + passes::write_out_deps(sess, &outputs, &output_paths); if sess.opts.output_types.contains_key(&OutputType::DepInfo) && sess.opts.output_types.len() == 1 { @@ -801,10 +800,10 @@ where // these need to be set "early" so that expansion sees `quote` if enabled. sess.init_features(features); - let crate_types = collect_crate_types(sess, &krate.attrs); + let crate_types = util::collect_crate_types(sess, &krate.attrs); sess.crate_types.set(crate_types); - let disambiguator = compute_crate_disambiguator(sess); + let disambiguator = util::compute_crate_disambiguator(sess); sess.crate_disambiguator.set(disambiguator); rustc_incremental::prepare_session_directory(sess, &crate_name, disambiguator); @@ -1014,7 +1013,7 @@ where // If we're actually rustdoc then there's no need to actually compile // anything, so switch everything to just looping if sess.opts.actually_rustdoc { - ReplaceBodyWithLoop::new(sess).visit_crate(&mut krate); + util::ReplaceBodyWithLoop::new(sess).visit_crate(&mut krate); } let (has_proc_macro_decls, has_global_allocator) = time(sess, "AST validation", || { @@ -1140,7 +1139,7 @@ where } pub fn default_provide(providers: &mut ty::query::Providers) { - proc_macro_decls::provide(providers); + rustc_interface::passes::provide(providers); plugin::build::provide(providers); hir::provide(providers); borrowck::provide(providers); @@ -1222,114 +1221,9 @@ where // tcx available. time(sess, "dep graph tcx init", || rustc_incremental::dep_graph_tcx_init(tcx)); - parallel!({ - time(sess, "looking for entry point", || { - middle::entry::find_entry_point(tcx) - }); - - time(sess, "looking for plugin registrar", || { - plugin::build::find_plugin_registrar(tcx) - }); - - time(sess, "looking for derive registrar", || { - proc_macro_decls::find(tcx) - }); - }, { - time(sess, "loop checking", || loops::check_crate(tcx)); - }, { - time(sess, "attribute checking", || { - hir::check_attr::check_crate(tcx) - }); - }, { - time(sess, "stability checking", || { - stability::check_unstable_api_usage(tcx) - }); - }); - - // passes are timed inside typeck - match typeck::check_crate(tcx) { - Ok(x) => x, - Err(x) => { - f(tcx, rx, Err(x)); - return Err(x); - } - } - - time(sess, "misc checking", || { - parallel!({ - time(sess, "rvalue promotion", || { - rvalue_promotion::check_crate(tcx) - }); - }, { - time(sess, "intrinsic checking", || { - middle::intrinsicck::check_crate(tcx) - }); - }, { - time(sess, "match checking", || mir::matchck_crate(tcx)); - }, { - // this must run before MIR dump, because - // "not all control paths return a value" is reported here. - // - // maybe move the check to a MIR pass? - time(sess, "liveness checking", || { - middle::liveness::check_crate(tcx) - }); - }); - }); - - // Abort so we don't try to construct MIR with liveness errors. - // We also won't want to continue with errors from rvalue promotion - tcx.sess.abort_if_errors(); - - time(sess, "borrow checking", || { - if tcx.use_ast_borrowck() { - borrowck::check_crate(tcx); - } - }); - - time(sess, - "MIR borrow checking", - || tcx.par_body_owners(|def_id| { tcx.ensure().mir_borrowck(def_id); })); + tcx.analysis(LOCAL_CRATE).ok(); - time(sess, "dumping chalk-like clauses", || { - rustc_traits::lowering::dump_program_clauses(tcx); - }); - - time(sess, "MIR effect checking", || { - for def_id in tcx.body_owners() { - mir::transform::check_unsafety::check_unsafety(tcx, def_id) - } - }); - - time(sess, "layout testing", || layout_test::test_layout(tcx)); - - // Avoid overwhelming user with errors if borrow checking failed. - // I'm not sure how helpful this is, to be honest, but it avoids - // a - // lot of annoying errors in the compile-fail tests (basically, - // lint warnings and so on -- kindck used to do this abort, but - // kindck is gone now). -nmatsakis - if sess.err_count() > 0 { - return Ok(f(tcx, rx, sess.compile_status())); - } - - time(sess, "misc checking", || { - parallel!({ - time(sess, "privacy checking", || { - rustc_privacy::check_crate(tcx) - }); - }, { - time(sess, "death checking", || middle::dead::check_crate(tcx)); - }, { - time(sess, "unused lib feature checking", || { - stability::check_unused_or_stable_features(tcx) - }); - }, { - time(sess, "lint checking", || lint::check_crate(tcx)); - }); - }); - - return Ok(f(tcx, rx, tcx.sess.compile_status())); + Ok(f(tcx, rx, tcx.sess.compile_status())) }, ) } @@ -1354,328 +1248,3 @@ pub fn phase_4_codegen<'a, 'tcx>( codegen } - -fn escape_dep_filename(filename: &FileName) -> String { - // Apparently clang and gcc *only* escape spaces: - // http://llvm.org/klaus/clang/commit/9d50634cfc268ecc9a7250226dd5ca0e945240d4 - filename.to_string().replace(" ", "\\ ") -} - -// Returns all the paths that correspond to generated files. -fn generated_output_paths( - sess: &Session, - outputs: &OutputFilenames, - exact_name: bool, - crate_name: &str, -) -> Vec { - let mut out_filenames = Vec::new(); - for output_type in sess.opts.output_types.keys() { - let file = outputs.path(*output_type); - match *output_type { - // If the filename has been overridden using `-o`, it will not be modified - // by appending `.rlib`, `.exe`, etc., so we can skip this transformation. - OutputType::Exe if !exact_name => for crate_type in sess.crate_types.borrow().iter() { - let p = ::rustc_codegen_utils::link::filename_for_input( - sess, - *crate_type, - crate_name, - outputs, - ); - out_filenames.push(p); - }, - OutputType::DepInfo if sess.opts.debugging_opts.dep_info_omit_d_target => { - // Don't add the dep-info output when omitting it from dep-info targets - } - _ => { - out_filenames.push(file); - } - } - } - out_filenames -} - -// Runs `f` on every output file path and returns the first non-None result, or None if `f` -// returns None for every file path. -fn check_output(output_paths: &[PathBuf], f: F) -> Option -where - F: Fn(&PathBuf) -> Option, -{ - for output_path in output_paths { - if let Some(result) = f(output_path) { - return Some(result); - } - } - None -} - -pub fn output_contains_path(output_paths: &[PathBuf], input_path: &PathBuf) -> bool { - let input_path = input_path.canonicalize().ok(); - if input_path.is_none() { - return false; - } - let check = |output_path: &PathBuf| { - if output_path.canonicalize().ok() == input_path { - Some(()) - } else { - None - } - }; - check_output(output_paths, check).is_some() -} - -pub fn output_conflicts_with_dir(output_paths: &[PathBuf]) -> Option { - let check = |output_path: &PathBuf| { - if output_path.is_dir() { - Some(output_path.clone()) - } else { - None - } - }; - check_output(output_paths, check) -} - -fn write_out_deps(sess: &Session, outputs: &OutputFilenames, out_filenames: &[PathBuf]) { - // Write out dependency rules to the dep-info file if requested - if !sess.opts.output_types.contains_key(&OutputType::DepInfo) { - return; - } - let deps_filename = outputs.path(OutputType::DepInfo); - - let result = (|| -> io::Result<()> { - // Build a list of files used to compile the output and - // write Makefile-compatible dependency rules - let files: Vec = sess.source_map() - .files() - .iter() - .filter(|fmap| fmap.is_real_file()) - .filter(|fmap| !fmap.is_imported()) - .map(|fmap| escape_dep_filename(&fmap.name)) - .collect(); - let mut file = fs::File::create(&deps_filename)?; - for path in out_filenames { - writeln!(file, "{}: {}\n", path.display(), files.join(" "))?; - } - - // Emit a fake target for each input file to the compilation. This - // prevents `make` from spitting out an error if a file is later - // deleted. For more info see #28735 - for path in files { - writeln!(file, "{}:", path)?; - } - Ok(()) - })(); - - if let Err(e) = result { - sess.fatal(&format!( - "error writing dependencies to `{}`: {}", - deps_filename.display(), - e - )); - } -} - -pub fn collect_crate_types(session: &Session, attrs: &[ast::Attribute]) -> Vec { - // Unconditionally collect crate types from attributes to make them used - let attr_types: Vec = attrs - .iter() - .filter_map(|a| { - if a.check_name("crate_type") { - match a.value_str() { - Some(ref n) if *n == "rlib" => Some(config::CrateType::Rlib), - Some(ref n) if *n == "dylib" => Some(config::CrateType::Dylib), - Some(ref n) if *n == "cdylib" => Some(config::CrateType::Cdylib), - Some(ref n) if *n == "lib" => Some(config::default_lib_output()), - Some(ref n) if *n == "staticlib" => Some(config::CrateType::Staticlib), - Some(ref n) if *n == "proc-macro" => Some(config::CrateType::ProcMacro), - Some(ref n) if *n == "bin" => Some(config::CrateType::Executable), - Some(ref n) => { - let crate_types = vec![ - Symbol::intern("rlib"), - Symbol::intern("dylib"), - Symbol::intern("cdylib"), - Symbol::intern("lib"), - Symbol::intern("staticlib"), - Symbol::intern("proc-macro"), - Symbol::intern("bin") - ]; - - if let ast::MetaItemKind::NameValue(spanned) = a.meta().unwrap().node { - let span = spanned.span; - let lev_candidate = find_best_match_for_name( - crate_types.iter(), - &n.as_str(), - None - ); - if let Some(candidate) = lev_candidate { - session.buffer_lint_with_diagnostic( - lint::builtin::UNKNOWN_CRATE_TYPES, - ast::CRATE_NODE_ID, - span, - "invalid `crate_type` value", - lint::builtin::BuiltinLintDiagnostics:: - UnknownCrateTypes( - span, - "did you mean".to_string(), - format!("\"{}\"", candidate) - ) - ); - } else { - session.buffer_lint( - lint::builtin::UNKNOWN_CRATE_TYPES, - ast::CRATE_NODE_ID, - span, - "invalid `crate_type` value" - ); - } - } - None - } - None => None - } - } else { - None - } - }) - .collect(); - - // If we're generating a test executable, then ignore all other output - // styles at all other locations - if session.opts.test { - return vec![config::CrateType::Executable]; - } - - // Only check command line flags if present. If no types are specified by - // command line, then reuse the empty `base` Vec to hold the types that - // will be found in crate attributes. - let mut base = session.opts.crate_types.clone(); - if base.is_empty() { - base.extend(attr_types); - if base.is_empty() { - base.push(::rustc_codegen_utils::link::default_output_for_target( - session, - )); - } else { - base.sort(); - base.dedup(); - } - } - - base.retain(|crate_type| { - let res = !::rustc_codegen_utils::link::invalid_output_for_target(session, *crate_type); - - if !res { - session.warn(&format!( - "dropping unsupported crate type `{}` for target `{}`", - *crate_type, session.opts.target_triple - )); - } - - res - }); - - base -} - -pub fn compute_crate_disambiguator(session: &Session) -> CrateDisambiguator { - use std::hash::Hasher; - - // The crate_disambiguator is a 128 bit hash. The disambiguator is fed - // into various other hashes quite a bit (symbol hashes, incr. comp. hashes, - // debuginfo type IDs, etc), so we don't want it to be too wide. 128 bits - // should still be safe enough to avoid collisions in practice. - let mut hasher = StableHasher::::new(); - - let mut metadata = session.opts.cg.metadata.clone(); - // We don't want the crate_disambiguator to dependent on the order - // -C metadata arguments, so sort them: - metadata.sort(); - // Every distinct -C metadata value is only incorporated once: - metadata.dedup(); - - hasher.write(b"metadata"); - for s in &metadata { - // Also incorporate the length of a metadata string, so that we generate - // different values for `-Cmetadata=ab -Cmetadata=c` and - // `-Cmetadata=a -Cmetadata=bc` - hasher.write_usize(s.len()); - hasher.write(s.as_bytes()); - } - - // Also incorporate crate type, so that we don't get symbol conflicts when - // linking against a library of the same name, if this is an executable. - let is_exe = session - .crate_types - .borrow() - .contains(&config::CrateType::Executable); - hasher.write(if is_exe { b"exe" } else { b"lib" }); - - CrateDisambiguator::from(hasher.finish()) -} - -pub fn build_output_filenames( - input: &Input, - odir: &Option, - ofile: &Option, - attrs: &[ast::Attribute], - sess: &Session, -) -> OutputFilenames { - match *ofile { - None => { - // "-" as input file will cause the parser to read from stdin so we - // have to make up a name - // We want to toss everything after the final '.' - let dirpath = (*odir).as_ref().cloned().unwrap_or_default(); - - // If a crate name is present, we use it as the link name - let stem = sess.opts - .crate_name - .clone() - .or_else(|| attr::find_crate_name(attrs).map(|n| n.to_string())) - .unwrap_or_else(|| input.filestem().to_owned()); - - OutputFilenames { - out_directory: dirpath, - out_filestem: stem, - single_output_file: None, - extra: sess.opts.cg.extra_filename.clone(), - outputs: sess.opts.output_types.clone(), - } - } - - Some(ref out_file) => { - let unnamed_output_types = sess.opts - .output_types - .values() - .filter(|a| a.is_none()) - .count(); - let ofile = if unnamed_output_types > 1 { - sess.warn( - "due to multiple output types requested, the explicitly specified \ - output file name will be adapted for each output type", - ); - None - } else { - Some(out_file.clone()) - }; - if *odir != None { - sess.warn("ignoring --out-dir flag due to -o flag"); - } - if !sess.opts.cg.extra_filename.is_empty() { - sess.warn("ignoring -C extra-filename flag due to -o flag"); - } - - OutputFilenames { - out_directory: out_file.parent().unwrap_or_else(|| Path::new("")).to_path_buf(), - out_filestem: out_file - .file_stem() - .unwrap_or_default() - .to_str() - .unwrap() - .to_string(), - single_output_file: ofile, - extra: sess.opts.cg.extra_filename.clone(), - outputs: sess.opts.output_types.clone(), - } - } - } -} diff --git a/src/librustc_driver/lib.rs b/src/librustc_driver/lib.rs index 990ad4ada01a2..a8cc304b6ccf7 100644 --- a/src/librustc_driver/lib.rs +++ b/src/librustc_driver/lib.rs @@ -28,7 +28,6 @@ extern crate rustc; extern crate rustc_allocator; extern crate rustc_target; extern crate rustc_borrowck; -#[macro_use] extern crate rustc_data_structures; extern crate rustc_errors as errors; extern crate rustc_passes; @@ -43,6 +42,7 @@ extern crate rustc_save_analysis; extern crate rustc_traits; extern crate rustc_codegen_utils; extern crate rustc_typeck; +extern crate rustc_interface; extern crate scoped_tls; extern crate serialize; extern crate smallvec; @@ -59,19 +59,18 @@ use rustc_save_analysis as save; use rustc_save_analysis::DumpHandler; use rustc_data_structures::sync::{self, Lrc, Ordering::SeqCst}; use rustc_data_structures::OnDrop; -use rustc::session::{self, config, Session, build_session, CompileResult}; +use rustc::session::{self, config, Session, build_session, CompileResult, DiagnosticOutput}; use rustc::session::CompileIncomplete; use rustc::session::config::{Input, PrintRequest, ErrorOutputType}; use rustc::session::config::nightly_options; -use rustc::session::filesearch; use rustc::session::{early_error, early_warn}; use rustc::lint::Lint; use rustc::lint; use rustc_metadata::locator; use rustc_metadata::cstore::CStore; -use rustc_metadata::dynamic_lib::DynamicLibrary; use rustc::util::common::{time, ErrorReported}; use rustc_codegen_utils::codegen_backend::CodegenBackend; +use rustc_interface::util::{self, get_codegen_sysroot}; use serialize::json::ToJson; @@ -79,19 +78,15 @@ use std::any::Any; use std::borrow::Cow; use std::cmp::max; use std::default::Default; -use std::env::consts::{DLL_PREFIX, DLL_SUFFIX}; use std::env; use std::error::Error; use std::ffi::OsString; use std::fmt::{self, Display}; use std::io::{self, Read, Write}; -use std::mem; use std::panic; -use std::path::{PathBuf, Path}; +use std::path::PathBuf; use std::process::{self, Command, Stdio}; use std::str; -use std::sync::atomic::{AtomicBool, Ordering}; -use std::sync::{Once, ONCE_INIT}; use std::thread; use syntax::ast; @@ -103,34 +98,8 @@ use syntax_pos::{DUMMY_SP, MultiSpan, FileName}; #[cfg(test)] mod test; -pub mod profile; pub mod driver; pub mod pretty; -mod proc_macro_decls; - -pub mod target_features { - use syntax::ast; - use syntax::symbol::Symbol; - use rustc::session::Session; - use rustc_codegen_utils::codegen_backend::CodegenBackend; - - /// Adds `target_feature = "..."` cfgs for a variety of platform - /// specific features (SSE, NEON etc.). - /// - /// This is performed by checking whether a whitelisted set of - /// features is available on the target machine, by querying LLVM. - pub fn add_configuration(cfg: &mut ast::CrateConfig, - sess: &Session, - codegen_backend: &dyn CodegenBackend) { - let tf = Symbol::intern("target_feature"); - - cfg.extend(codegen_backend.target_features(sess).into_iter().map(|feat| (tf, Some(feat)))); - - if sess.crt_static_feature() { - cfg.insert((tf, Some(Symbol::intern("crt-static")))); - } - } -} /// Exit status code used for successful compilation and help output. pub const EXIT_SUCCESS: isize = 0; @@ -147,6 +116,13 @@ const ICE_REPORT_COMPILER_FLAGS_EXCLUDE: &[&str] = &["metadata", "extra-filename const ICE_REPORT_COMPILER_FLAGS_STRIP_VALUE: &[&str] = &["incremental"]; +pub fn source_name(input: &Input) -> FileName { + match *input { + Input::File(ref ifile) => ifile.clone().into(), + Input::Str { ref name, .. } => name.clone(), + } +} + pub fn abort_on_err(result: Result, sess: &Session) -> T { match result { Err(CompileIncomplete::Errored(ErrorReported)) => { @@ -197,235 +173,6 @@ pub fn run(run_compiler: F) -> isize } } -fn load_backend_from_dylib(path: &Path) -> fn() -> Box { - let lib = DynamicLibrary::open(Some(path)).unwrap_or_else(|err| { - let err = format!("couldn't load codegen backend {:?}: {:?}", path, err); - early_error(ErrorOutputType::default(), &err); - }); - unsafe { - match lib.symbol("__rustc_codegen_backend") { - Ok(f) => { - mem::forget(lib); - mem::transmute::<*mut u8, _>(f) - } - Err(e) => { - let err = format!("couldn't load codegen backend as it \ - doesn't export the `__rustc_codegen_backend` \ - symbol: {:?}", e); - early_error(ErrorOutputType::default(), &err); - } - } - } -} - -pub fn get_codegen_backend(sess: &Session) -> Box { - static INIT: Once = ONCE_INIT; - - #[allow(deprecated)] - #[no_debug] - static mut LOAD: fn() -> Box = || unreachable!(); - - INIT.call_once(|| { - let codegen_name = sess.opts.debugging_opts.codegen_backend.as_ref() - .unwrap_or(&sess.target.target.options.codegen_backend); - let backend = match &codegen_name[..] { - "metadata_only" => { - rustc_codegen_utils::codegen_backend::MetadataOnlyCodegenBackend::boxed - } - filename if filename.contains(".") => { - load_backend_from_dylib(filename.as_ref()) - } - codegen_name => get_codegen_sysroot(codegen_name), - }; - - unsafe { - LOAD = backend; - } - }); - let backend = unsafe { LOAD() }; - backend.init(sess); - backend -} - -fn get_codegen_sysroot(backend_name: &str) -> fn() -> Box { - // For now we only allow this function to be called once as it'll dlopen a - // few things, which seems to work best if we only do that once. In - // general this assertion never trips due to the once guard in `get_codegen_backend`, - // but there's a few manual calls to this function in this file we protect - // against. - static LOADED: AtomicBool = AtomicBool::new(false); - assert!(!LOADED.fetch_or(true, Ordering::SeqCst), - "cannot load the default codegen backend twice"); - - // When we're compiling this library with `--test` it'll run as a binary but - // not actually exercise much functionality. As a result most of the logic - // here is defunkt (it assumes we're a dynamic library in a sysroot) so - // let's just return a dummy creation function which won't be used in - // general anyway. - if cfg!(test) { - return rustc_codegen_utils::codegen_backend::MetadataOnlyCodegenBackend::boxed - } - - let target = session::config::host_triple(); - let mut sysroot_candidates = vec![filesearch::get_or_default_sysroot()]; - let path = current_dll_path() - .and_then(|s| s.canonicalize().ok()); - if let Some(dll) = path { - // use `parent` twice to chop off the file name and then also the - // directory containing the dll which should be either `lib` or `bin`. - if let Some(path) = dll.parent().and_then(|p| p.parent()) { - // The original `path` pointed at the `rustc_driver` crate's dll. - // Now that dll should only be in one of two locations. The first is - // in the compiler's libdir, for example `$sysroot/lib/*.dll`. The - // other is the target's libdir, for example - // `$sysroot/lib/rustlib/$target/lib/*.dll`. - // - // We don't know which, so let's assume that if our `path` above - // ends in `$target` we *could* be in the target libdir, and always - // assume that we may be in the main libdir. - sysroot_candidates.push(path.to_owned()); - - if path.ends_with(target) { - sysroot_candidates.extend(path.parent() // chop off `$target` - .and_then(|p| p.parent()) // chop off `rustlib` - .and_then(|p| p.parent()) // chop off `lib` - .map(|s| s.to_owned())); - } - } - } - - let sysroot = sysroot_candidates.iter() - .map(|sysroot| { - let libdir = filesearch::relative_target_lib_path(&sysroot, &target); - sysroot.join(libdir).with_file_name( - option_env!("CFG_CODEGEN_BACKENDS_DIR").unwrap_or("codegen-backends")) - }) - .filter(|f| { - info!("codegen backend candidate: {}", f.display()); - f.exists() - }) - .next(); - let sysroot = sysroot.unwrap_or_else(|| { - let candidates = sysroot_candidates.iter() - .map(|p| p.display().to_string()) - .collect::>() - .join("\n* "); - let err = format!("failed to find a `codegen-backends` folder \ - in the sysroot candidates:\n* {}", candidates); - early_error(ErrorOutputType::default(), &err); - }); - info!("probing {} for a codegen backend", sysroot.display()); - - let d = sysroot.read_dir().unwrap_or_else(|e| { - let err = format!("failed to load default codegen backend, couldn't \ - read `{}`: {}", sysroot.display(), e); - early_error(ErrorOutputType::default(), &err); - }); - - let mut file: Option = None; - - let expected_name = format!("rustc_codegen_llvm-{}", backend_name); - for entry in d.filter_map(|e| e.ok()) { - let path = entry.path(); - let filename = match path.file_name().and_then(|s| s.to_str()) { - Some(s) => s, - None => continue, - }; - if !(filename.starts_with(DLL_PREFIX) && filename.ends_with(DLL_SUFFIX)) { - continue - } - let name = &filename[DLL_PREFIX.len() .. filename.len() - DLL_SUFFIX.len()]; - if name != expected_name { - continue - } - if let Some(ref prev) = file { - let err = format!("duplicate codegen backends found\n\ - first: {}\n\ - second: {}\n\ - ", prev.display(), path.display()); - early_error(ErrorOutputType::default(), &err); - } - file = Some(path.clone()); - } - - match file { - Some(ref s) => return load_backend_from_dylib(s), - None => { - let err = format!("failed to load default codegen backend for `{}`, \ - no appropriate codegen dylib found in `{}`", - backend_name, sysroot.display()); - early_error(ErrorOutputType::default(), &err); - } - } - - #[cfg(unix)] - fn current_dll_path() -> Option { - use std::ffi::{OsStr, CStr}; - use std::os::unix::prelude::*; - - unsafe { - let addr = current_dll_path as usize as *mut _; - let mut info = mem::zeroed(); - if libc::dladdr(addr, &mut info) == 0 { - info!("dladdr failed"); - return None - } - if info.dli_fname.is_null() { - info!("dladdr returned null pointer"); - return None - } - let bytes = CStr::from_ptr(info.dli_fname).to_bytes(); - let os = OsStr::from_bytes(bytes); - Some(PathBuf::from(os)) - } - } - - #[cfg(windows)] - fn current_dll_path() -> Option { - use std::ffi::OsString; - use std::os::windows::prelude::*; - - extern "system" { - fn GetModuleHandleExW(dwFlags: u32, - lpModuleName: usize, - phModule: *mut usize) -> i32; - fn GetModuleFileNameW(hModule: usize, - lpFilename: *mut u16, - nSize: u32) -> u32; - } - - const GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS: u32 = 0x00000004; - - unsafe { - let mut module = 0; - let r = GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, - current_dll_path as usize, - &mut module); - if r == 0 { - info!("GetModuleHandleExW failed: {}", io::Error::last_os_error()); - return None - } - let mut space = Vec::with_capacity(1024); - let r = GetModuleFileNameW(module, - space.as_mut_ptr(), - space.capacity() as u32); - if r == 0 { - info!("GetModuleFileNameW failed: {}", io::Error::last_os_error()); - return None - } - let r = r as usize; - if r >= space.capacity() { - info!("our buffer was too small? {}", - io::Error::last_os_error()); - return None - } - space.set_len(r); - let os = OsString::from_wide(&space); - Some(PathBuf::from(os)) - } - } -} - // Parse args and run the compiler. This is the primary entry point for rustc. // See comments on CompilerCalls below for details about the callbacks argument. // The FileLoader provides a way to load files from sources other than the file system. @@ -486,7 +233,12 @@ fn run_compiler_with_pool<'a>( let loader = file_loader.unwrap_or(box RealFileLoader); let source_map = Lrc::new(SourceMap::with_file_loader(loader, sopts.file_path_mapping())); let mut sess = session::build_session_with_source_map( - sopts, input_file_path.clone(), descriptions, source_map, emitter_dest, + sopts, + input_file_path.clone(), + descriptions, + source_map, + emitter_dest.map(|e| DiagnosticOutput::Raw(e)).unwrap_or(DiagnosticOutput::Default), + Default::default(), ); if let Some(err) = input_err { @@ -496,12 +248,12 @@ fn run_compiler_with_pool<'a>( return (Err(CompileIncomplete::Stopped), Some(sess)); } - let codegen_backend = get_codegen_backend(&sess); + let codegen_backend = util::get_codegen_backend(&sess); rustc_lint::register_builtins(&mut sess.lint_store.borrow_mut(), Some(&sess)); let mut cfg = config::build_configuration(&sess, cfg); - target_features::add_configuration(&mut cfg, &sess, &*codegen_backend); + util::add_configuration(&mut cfg, &sess, &*codegen_backend); sess.parse_sess.config = cfg; let result = { @@ -711,8 +463,8 @@ fn stdout_isatty() -> bool { } fn handle_explain(code: &str, - descriptions: &errors::registry::Registry, output: ErrorOutputType) { + let descriptions = rustc_interface::util::diagnostics_registry(); let normalised = if code.starts_with("E") { code.to_string() } else { @@ -789,11 +541,11 @@ impl<'a> CompilerCalls<'a> for RustcDefaultCalls { matches: &getopts::Matches, _: &config::Options, _: &ast::CrateConfig, - descriptions: &errors::registry::Registry, + _: &errors::registry::Registry, output: ErrorOutputType) -> Compilation { if let Some(ref code) = matches.opt_str("explain") { - handle_explain(code, descriptions, output); + handle_explain(code, output); return Compilation::Stop; } @@ -821,8 +573,8 @@ impl<'a> CompilerCalls<'a> for RustcDefaultCalls { } rustc_lint::register_builtins(&mut sess.lint_store.borrow_mut(), Some(&sess)); let mut cfg = config::build_configuration(&sess, cfg.clone()); - let codegen_backend = get_codegen_backend(&sess); - target_features::add_configuration(&mut cfg, &sess, &*codegen_backend); + let codegen_backend = util::get_codegen_backend(&sess); + util::add_configuration(&mut cfg, &sess, &*codegen_backend); sess.parse_sess.config = cfg; let should_stop = RustcDefaultCalls::print_crate_info( &*codegen_backend, @@ -1025,13 +777,19 @@ impl RustcDefaultCalls { let input = input.unwrap_or_else(|| early_error(ErrorOutputType::default(), "no input file provided")); let attrs = attrs.as_ref().unwrap(); - let t_outputs = driver::build_output_filenames(input, odir, ofile, attrs, sess); + let t_outputs = rustc_interface::util::build_output_filenames( + input, + odir, + ofile, + attrs, + sess + ); let id = rustc_codegen_utils::link::find_crate_name(Some(sess), attrs, input); if *req == PrintRequest::CrateName { println!("{}", id); continue; } - let crate_types = driver::collect_crate_types(sess, attrs); + let crate_types = rustc_interface::util::collect_crate_types(sess, attrs); for &style in &crate_types { let fname = rustc_codegen_utils::link::filename_for_input( sess, diff --git a/src/librustc_driver/pretty.rs b/src/librustc_driver/pretty.rs index 4caf2ec676f07..6ea61f7f4b145 100644 --- a/src/librustc_driver/pretty.rs +++ b/src/librustc_driver/pretty.rs @@ -9,36 +9,34 @@ use rustc::hir::print as pprust_hir; use rustc::session::Session; use rustc::session::config::{Input, OutputFilenames}; use rustc::ty::{self, TyCtxt, Resolutions, AllArenas}; +use rustc_interface::util; use rustc_borrowck as borrowck; use rustc_borrowck::graphviz as borrowck_dot; -use rustc_data_structures::thin_vec::ThinVec; use rustc_metadata::cstore::CStore; use rustc_mir::util::{write_mir_pretty, write_mir_graphviz}; -use syntax::ast::{self, BlockCheckMode}; -use syntax::mut_visit::{*, MutVisitor, visit_clobber}; +use syntax::ast; +use syntax::mut_visit::MutVisitor; use syntax::print::{pprust}; use syntax::print::pprust::PrintState; -use syntax::ptr::P; -use syntax_pos::{self, FileName}; +use syntax_pos::FileName; use graphviz as dot; -use smallvec::SmallVec; use std::cell::Cell; use std::fs::File; use std::io::{self, Write}; -use std::ops::DerefMut; use std::option; use std::path::Path; use std::str::FromStr; -use std::mem; pub use self::UserIdentifiedItem::*; pub use self::PpSourceMode::*; pub use self::PpMode::*; use self::NodesMatchingUII::*; -use {abort_on_err, driver}; +use abort_on_err; +use driver; +use source_name; #[derive(Copy, Clone, PartialEq, Debug)] pub enum PpSourceMode { @@ -217,7 +215,7 @@ impl PpSourceMode { } PpmTyped => { let control = &driver::CompileController::basic(); - let codegen_backend = ::get_codegen_backend(sess); + let codegen_backend = util::get_codegen_backend(sess); let mut arenas = AllArenas::new(); abort_on_err(driver::phase_3_run_analysis_passes(&*codegen_backend, control, @@ -627,204 +625,6 @@ impl UserIdentifiedItem { } } -// Note: Also used by librustdoc, see PR #43348. Consider moving this struct elsewhere. -// -// FIXME: Currently the `everybody_loops` transformation is not applied to: -// * `const fn`, due to issue #43636 that `loop` is not supported for const evaluation. We are -// waiting for miri to fix that. -// * `impl Trait`, due to issue #43869 that functions returning impl Trait cannot be diverging. -// Solving this may require `!` to implement every trait, which relies on the an even more -// ambitious form of the closed RFC #1637. See also [#34511]. -// -// [#34511]: https://github.com/rust-lang/rust/issues/34511#issuecomment-322340401 -pub struct ReplaceBodyWithLoop<'a> { - within_static_or_const: bool, - nested_blocks: Option>, - sess: &'a Session, -} - -impl<'a> ReplaceBodyWithLoop<'a> { - pub fn new(sess: &'a Session) -> ReplaceBodyWithLoop<'a> { - ReplaceBodyWithLoop { - within_static_or_const: false, - nested_blocks: None, - sess - } - } - - fn run R>(&mut self, is_const: bool, action: F) -> R { - let old_const = mem::replace(&mut self.within_static_or_const, is_const); - let old_blocks = self.nested_blocks.take(); - let ret = action(self); - self.within_static_or_const = old_const; - self.nested_blocks = old_blocks; - ret - } - - fn should_ignore_fn(ret_ty: &ast::FnDecl) -> bool { - if let ast::FunctionRetTy::Ty(ref ty) = ret_ty.output { - fn involves_impl_trait(ty: &ast::Ty) -> bool { - match ty.node { - ast::TyKind::ImplTrait(..) => true, - ast::TyKind::Slice(ref subty) | - ast::TyKind::Array(ref subty, _) | - ast::TyKind::Ptr(ast::MutTy { ty: ref subty, .. }) | - ast::TyKind::Rptr(_, ast::MutTy { ty: ref subty, .. }) | - ast::TyKind::Paren(ref subty) => involves_impl_trait(subty), - ast::TyKind::Tup(ref tys) => any_involves_impl_trait(tys.iter()), - ast::TyKind::Path(_, ref path) => path.segments.iter().any(|seg| { - match seg.args.as_ref().map(|generic_arg| &**generic_arg) { - None => false, - Some(&ast::GenericArgs::AngleBracketed(ref data)) => { - let types = data.args.iter().filter_map(|arg| match arg { - ast::GenericArg::Type(ty) => Some(ty), - _ => None, - }); - any_involves_impl_trait(types.into_iter()) || - any_involves_impl_trait(data.bindings.iter().map(|b| &b.ty)) - }, - Some(&ast::GenericArgs::Parenthesized(ref data)) => { - any_involves_impl_trait(data.inputs.iter()) || - any_involves_impl_trait(data.output.iter()) - } - } - }), - _ => false, - } - } - - fn any_involves_impl_trait<'a, I: Iterator>>(mut it: I) -> bool { - it.any(|subty| involves_impl_trait(subty)) - } - - involves_impl_trait(ty) - } else { - false - } - } -} - -impl<'a> MutVisitor for ReplaceBodyWithLoop<'a> { - fn visit_item_kind(&mut self, i: &mut ast::ItemKind) { - let is_const = match i { - ast::ItemKind::Static(..) | ast::ItemKind::Const(..) => true, - ast::ItemKind::Fn(ref decl, ref header, _, _) => - header.constness.node == ast::Constness::Const || Self::should_ignore_fn(decl), - _ => false, - }; - self.run(is_const, |s| noop_visit_item_kind(i, s)) - } - - fn flat_map_trait_item(&mut self, i: ast::TraitItem) -> SmallVec<[ast::TraitItem; 1]> { - let is_const = match i.node { - ast::TraitItemKind::Const(..) => true, - ast::TraitItemKind::Method(ast::MethodSig { ref decl, ref header, .. }, _) => - header.constness.node == ast::Constness::Const || Self::should_ignore_fn(decl), - _ => false, - }; - self.run(is_const, |s| noop_flat_map_trait_item(i, s)) - } - - fn flat_map_impl_item(&mut self, i: ast::ImplItem) -> SmallVec<[ast::ImplItem; 1]> { - let is_const = match i.node { - ast::ImplItemKind::Const(..) => true, - ast::ImplItemKind::Method(ast::MethodSig { ref decl, ref header, .. }, _) => - header.constness.node == ast::Constness::Const || Self::should_ignore_fn(decl), - _ => false, - }; - self.run(is_const, |s| noop_flat_map_impl_item(i, s)) - } - - fn visit_anon_const(&mut self, c: &mut ast::AnonConst) { - self.run(true, |s| noop_visit_anon_const(c, s)) - } - - fn visit_block(&mut self, b: &mut P) { - fn stmt_to_block(rules: ast::BlockCheckMode, - s: Option, - sess: &Session) -> ast::Block { - ast::Block { - stmts: s.into_iter().collect(), - rules, - id: sess.next_node_id(), - span: syntax_pos::DUMMY_SP, - } - } - - fn block_to_stmt(b: ast::Block, sess: &Session) -> ast::Stmt { - let expr = P(ast::Expr { - id: sess.next_node_id(), - node: ast::ExprKind::Block(P(b), None), - span: syntax_pos::DUMMY_SP, - attrs: ThinVec::new(), - }); - - ast::Stmt { - id: sess.next_node_id(), - node: ast::StmtKind::Expr(expr), - span: syntax_pos::DUMMY_SP, - } - } - - let empty_block = stmt_to_block(BlockCheckMode::Default, None, self.sess); - let loop_expr = P(ast::Expr { - node: ast::ExprKind::Loop(P(empty_block), None), - id: self.sess.next_node_id(), - span: syntax_pos::DUMMY_SP, - attrs: ThinVec::new(), - }); - - let loop_stmt = ast::Stmt { - id: self.sess.next_node_id(), - span: syntax_pos::DUMMY_SP, - node: ast::StmtKind::Expr(loop_expr), - }; - - if self.within_static_or_const { - noop_visit_block(b, self) - } else { - visit_clobber(b.deref_mut(), |b| { - let mut stmts = vec![]; - for s in b.stmts { - let old_blocks = self.nested_blocks.replace(vec![]); - - stmts.extend(self.flat_map_stmt(s).into_iter().filter(|s| s.is_item())); - - // we put a Some in there earlier with that replace(), so this is valid - let new_blocks = self.nested_blocks.take().unwrap(); - self.nested_blocks = old_blocks; - stmts.extend(new_blocks.into_iter().map(|b| block_to_stmt(b, &self.sess))); - } - - let mut new_block = ast::Block { - stmts, - ..b - }; - - if let Some(old_blocks) = self.nested_blocks.as_mut() { - //push our fresh block onto the cache and yield an empty block with `loop {}` - if !new_block.stmts.is_empty() { - old_blocks.push(new_block); - } - - stmt_to_block(b.rules, Some(loop_stmt), self.sess) - } else { - //push `loop {}` onto the end of our fresh block and yield that - new_block.stmts.push(loop_stmt); - - new_block - } - }) - } - } - - // in general the pretty printer processes unexpanded code, so - // we override the default `visit_mac` method which panics. - fn visit_mac(&mut self, mac: &mut ast::Mac) { - noop_visit_mac(mac, self) - } -} - fn print_flowgraph<'a, 'tcx, W: Write>(variants: Vec, tcx: TyCtxt<'a, 'tcx, 'tcx>, code: blocks::Code<'tcx>, @@ -892,12 +692,12 @@ fn print_flowgraph<'a, 'tcx, W: Write>(variants: Vec, pub fn visit_crate(sess: &Session, krate: &mut ast::Crate, ppm: PpMode) { if let PpmSource(PpmEveryBodyLoops) = ppm { - ReplaceBodyWithLoop::new(sess).visit_crate(krate); + util::ReplaceBodyWithLoop::new(sess).visit_crate(krate); } } fn get_source(input: &Input, sess: &Session) -> (Vec, FileName) { - let src_name = driver::source_name(input); + let src_name = source_name(input); let src = sess.source_map() .get_source_file(&src_name) .unwrap() @@ -1117,7 +917,7 @@ fn print_with_analysis<'tcx, 'a: 'tcx>(sess: &'a Session, let mut out = Vec::new(); let control = &driver::CompileController::basic(); - let codegen_backend = ::get_codegen_backend(sess); + let codegen_backend = util::get_codegen_backend(sess); let mut arenas = AllArenas::new(); abort_on_err(driver::phase_3_run_analysis_passes(&*codegen_backend, control, diff --git a/src/librustc_driver/test.rs b/src/librustc_driver/test.rs index 2ec755bd62691..309a9f7b52522 100644 --- a/src/librustc_driver/test.rs +++ b/src/librustc_driver/test.rs @@ -16,6 +16,7 @@ use rustc::ty::query::OnDiskCache; use rustc::ty::subst::Subst; use rustc::ty::{self, Ty, TyCtxt, TypeFoldable}; use rustc_data_structures::sync::{self, Lrc}; +use rustc_interface::util; use rustc_lint; use rustc_metadata::cstore::CStore; use rustc_target::spec::abi::Abi; @@ -91,6 +92,13 @@ where options.debugging_opts.verbose = true; options.unstable_features = UnstableFeatures::Allow; + // When we're compiling this library with `--test` it'll run as a binary but + // not actually exercise much functionality. + // As a result most of the logic loading the codegen backend is defunkt + // (it assumes we're a dynamic library in a sysroot) + // so let's just use the metadata only backend which doesn't need to load any libraries. + options.debugging_opts.codegen_backend = Some("metadata_only".to_owned()); + driver::spawn_thread_pool(options, |options| { test_env_with_pool(options, source_string, args, body) }) @@ -111,8 +119,9 @@ fn test_env_with_pool( None, diagnostic_handler, Lrc::new(SourceMap::new(FilePathMapping::empty())), + Default::default(), ); - let cstore = CStore::new(::get_codegen_backend(&sess).metadata_loader()); + let cstore = CStore::new(util::get_codegen_backend(&sess).metadata_loader()); rustc_lint::register_builtins(&mut sess.lint_store.borrow_mut(), Some(&sess)); let input = config::Input::Str { name: FileName::anon_source_code(&source_string), diff --git a/src/librustc_interface/Cargo.toml b/src/librustc_interface/Cargo.toml new file mode 100644 index 0000000000000..1acd3dfc7656a --- /dev/null +++ b/src/librustc_interface/Cargo.toml @@ -0,0 +1,35 @@ +[package] +authors = ["The Rust Project Developers"] +name = "rustc_interface" +version = "0.0.0" + +[lib] +name = "rustc_interface" +path = "lib.rs" +crate-type = ["dylib"] + +[dependencies] +log = "0.4" +rustc-rayon = "0.1.1" +smallvec = { version = "0.6.7", features = ["union", "may_dangle"] } +scoped-tls = { version = "0.1.1", features = ["nightly"] } +syntax = { path = "../libsyntax" } +syntax_ext = { path = "../libsyntax_ext" } +syntax_pos = { path = "../libsyntax_pos" } +serialize = { path = "../libserialize" } +rustc = { path = "../librustc" } +rustc_allocator = { path = "../librustc_allocator" } +rustc_borrowck = { path = "../librustc_borrowck" } +rustc_incremental = { path = "../librustc_incremental" } +rustc_traits = { path = "../librustc_traits" } +rustc_data_structures = { path = "../librustc_data_structures" } +rustc_codegen_utils = { path = "../librustc_codegen_utils" } +rustc_metadata = { path = "../librustc_metadata" } +rustc_mir = { path = "../librustc_mir" } +rustc_passes = { path = "../librustc_passes" } +rustc_typeck = { path = "../librustc_typeck" } +rustc_lint = { path = "../librustc_lint" } +rustc_errors = { path = "../librustc_errors" } +rustc_plugin = { path = "../librustc_plugin" } +rustc_privacy = { path = "../librustc_privacy" } +rustc_resolve = { path = "../librustc_resolve" } diff --git a/src/librustc_interface/lib.rs b/src/librustc_interface/lib.rs new file mode 100644 index 0000000000000..e5c7c35a36d75 --- /dev/null +++ b/src/librustc_interface/lib.rs @@ -0,0 +1,43 @@ +#![feature(box_syntax)] +#![feature(set_stdio)] +#![feature(nll)] +#![feature(arbitrary_self_types)] +#![feature(generator_trait)] +#![cfg_attr(unix, feature(libc))] + +#![allow(unused_imports)] + +#![recursion_limit="256"] + +#[cfg(unix)] +extern crate libc; +#[macro_use] +extern crate log; +extern crate rustc; +extern crate rustc_codegen_utils; +extern crate rustc_allocator; +extern crate rustc_borrowck; +extern crate rustc_incremental; +extern crate rustc_traits; +#[macro_use] +extern crate rustc_data_structures; +extern crate rustc_errors; +extern crate rustc_lint; +extern crate rustc_metadata; +extern crate rustc_mir; +extern crate rustc_passes; +extern crate rustc_plugin; +extern crate rustc_privacy; +extern crate rustc_rayon as rayon; +extern crate rustc_resolve; +extern crate rustc_typeck; +extern crate smallvec; +extern crate serialize; +extern crate syntax; +extern crate syntax_pos; +extern crate syntax_ext; + +pub mod passes; +pub mod profile; +pub mod util; +pub mod proc_macro_decls; diff --git a/src/librustc_interface/passes.rs b/src/librustc_interface/passes.rs new file mode 100644 index 0000000000000..11b2ef3579d02 --- /dev/null +++ b/src/librustc_interface/passes.rs @@ -0,0 +1,296 @@ +use util; +use proc_macro_decls; + +use rustc::dep_graph::DepGraph; +use rustc::hir; +use rustc::hir::lowering::lower_crate; +use rustc::hir::def_id::{CrateNum, LOCAL_CRATE}; +use rustc::lint; +use rustc::middle::{self, reachable, resolve_lifetime, stability}; +use rustc::middle::privacy::AccessLevels; +use rustc::ty::{self, AllArenas, Resolutions, TyCtxt}; +use rustc::ty::steal::Steal; +use rustc::traits; +use rustc::util::common::{time, ErrorReported}; +use rustc::util::profiling::ProfileCategory; +use rustc::session::{CompileResult, CrateDisambiguator, Session}; +use rustc::session::config::{self, Input, OutputFilenames, OutputType}; +use rustc::session::search_paths::PathKind; +use rustc_allocator as allocator; +use rustc_borrowck as borrowck; +use rustc_codegen_utils::codegen_backend::CodegenBackend; +use rustc_data_structures::fingerprint::Fingerprint; +use rustc_data_structures::stable_hasher::StableHasher; +use rustc_data_structures::sync::Lrc; +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, layout_test}; +use rustc_plugin as plugin; +use rustc_plugin::registry::Registry; +use rustc_privacy; +use rustc_resolve::{Resolver, ResolverArenas}; +use rustc_traits; +use rustc_typeck as typeck; +use syntax::{self, ast, attr, diagnostics, visit}; +use syntax::early_buffered_lints::BufferedEarlyLint; +use syntax::ext::base::ExtCtxt; +use syntax::mut_visit::MutVisitor; +use syntax::parse::{self, PResult}; +use syntax::util::node_count::NodeCounter; +use syntax::util::lev_distance::find_best_match_for_name; +use syntax::symbol::Symbol; +use syntax_pos::{FileName, hygiene}; +use syntax_ext; + +use serialize::json; + +use std::any::Any; +use std::env; +use std::ffi::OsString; +use std::fs; +use std::io::{self, Write}; +use std::iter; +use std::path::{Path, PathBuf}; +use std::sync::mpsc; +use std::cell::RefCell; +use std::rc::Rc; +use std::mem; +use std::ops::Generator; + +// Returns all the paths that correspond to generated files. +pub fn generated_output_paths( + sess: &Session, + outputs: &OutputFilenames, + exact_name: bool, + crate_name: &str, +) -> Vec { + let mut out_filenames = Vec::new(); + for output_type in sess.opts.output_types.keys() { + let file = outputs.path(*output_type); + match *output_type { + // If the filename has been overridden using `-o`, it will not be modified + // by appending `.rlib`, `.exe`, etc., so we can skip this transformation. + OutputType::Exe if !exact_name => for crate_type in sess.crate_types.borrow().iter() { + let p = ::rustc_codegen_utils::link::filename_for_input( + sess, + *crate_type, + crate_name, + outputs, + ); + out_filenames.push(p); + }, + OutputType::DepInfo if sess.opts.debugging_opts.dep_info_omit_d_target => { + // Don't add the dep-info output when omitting it from dep-info targets + } + _ => { + out_filenames.push(file); + } + } + } + out_filenames +} + +// Runs `f` on every output file path and returns the first non-None result, or None if `f` +// returns None for every file path. +fn check_output(output_paths: &[PathBuf], f: F) -> Option +where + F: Fn(&PathBuf) -> Option, +{ + for output_path in output_paths { + if let Some(result) = f(output_path) { + return Some(result); + } + } + None +} + +pub fn output_contains_path(output_paths: &[PathBuf], input_path: &PathBuf) -> bool { + let input_path = input_path.canonicalize().ok(); + if input_path.is_none() { + return false; + } + let check = |output_path: &PathBuf| { + if output_path.canonicalize().ok() == input_path { + Some(()) + } else { + None + } + }; + check_output(output_paths, check).is_some() +} + +pub fn output_conflicts_with_dir(output_paths: &[PathBuf]) -> Option { + let check = |output_path: &PathBuf| { + if output_path.is_dir() { + Some(output_path.clone()) + } else { + None + } + }; + check_output(output_paths, check) +} + +fn escape_dep_filename(filename: &FileName) -> String { + // Apparently clang and gcc *only* escape spaces: + // http://llvm.org/klaus/clang/commit/9d50634cfc268ecc9a7250226dd5ca0e945240d4 + filename.to_string().replace(" ", "\\ ") +} + +pub fn write_out_deps(sess: &Session, outputs: &OutputFilenames, out_filenames: &[PathBuf]) { + // Write out dependency rules to the dep-info file if requested + if !sess.opts.output_types.contains_key(&OutputType::DepInfo) { + return; + } + let deps_filename = outputs.path(OutputType::DepInfo); + + let result = (|| -> io::Result<()> { + // Build a list of files used to compile the output and + // write Makefile-compatible dependency rules + let files: Vec = sess.source_map() + .files() + .iter() + .filter(|fmap| fmap.is_real_file()) + .filter(|fmap| !fmap.is_imported()) + .map(|fmap| escape_dep_filename(&fmap.name)) + .collect(); + let mut file = fs::File::create(&deps_filename)?; + for path in out_filenames { + writeln!(file, "{}: {}\n", path.display(), files.join(" "))?; + } + + // Emit a fake target for each input file to the compilation. This + // prevents `make` from spitting out an error if a file is later + // deleted. For more info see #28735 + for path in files { + writeln!(file, "{}:", path)?; + } + Ok(()) + })(); + + if let Err(e) = result { + sess.fatal(&format!( + "error writing dependencies to `{}`: {}", + deps_filename.display(), + e + )); + } +} + +pub fn provide(providers: &mut ty::query::Providers) { + providers.analysis = analysis; + proc_macro_decls::provide(providers); +} + +fn analysis<'tcx>( + tcx: TyCtxt<'_, 'tcx, 'tcx>, + cnum: CrateNum, +) -> Result<(), ErrorReported> { + assert_eq!(cnum, LOCAL_CRATE); + + let sess = tcx.sess; + + parallel!({ + time(sess, "looking for entry point", || { + middle::entry::find_entry_point(tcx) + }); + + time(sess, "looking for plugin registrar", || { + plugin::build::find_plugin_registrar(tcx) + }); + + time(sess, "looking for derive registrar", || { + proc_macro_decls::find(tcx) + }); + }, { + time(sess, "loop checking", || loops::check_crate(tcx)); + }, { + time(sess, "attribute checking", || { + hir::check_attr::check_crate(tcx) + }); + }, { + time(sess, "stability checking", || { + stability::check_unstable_api_usage(tcx) + }); + }); + + // passes are timed inside typeck + typeck::check_crate(tcx)?; + + time(sess, "misc checking", || { + parallel!({ + time(sess, "rvalue promotion", || { + rvalue_promotion::check_crate(tcx) + }); + }, { + time(sess, "intrinsic checking", || { + middle::intrinsicck::check_crate(tcx) + }); + }, { + time(sess, "match checking", || mir::matchck_crate(tcx)); + }, { + // this must run before MIR dump, because + // "not all control paths return a value" is reported here. + // + // maybe move the check to a MIR pass? + time(sess, "liveness checking", || { + middle::liveness::check_crate(tcx) + }); + }); + }); + + // Abort so we don't try to construct MIR with liveness errors. + // We also won't want to continue with errors from rvalue promotion + tcx.sess.abort_if_errors(); + + time(sess, "borrow checking", || { + if tcx.use_ast_borrowck() { + borrowck::check_crate(tcx); + } + }); + + time(sess, + "MIR borrow checking", + || tcx.par_body_owners(|def_id| { tcx.ensure().mir_borrowck(def_id); })); + + time(sess, "dumping chalk-like clauses", || { + rustc_traits::lowering::dump_program_clauses(tcx); + }); + + time(sess, "MIR effect checking", || { + for def_id in tcx.body_owners() { + mir::transform::check_unsafety::check_unsafety(tcx, def_id) + } + }); + + time(sess, "layout testing", || layout_test::test_layout(tcx)); + + // Avoid overwhelming user with errors if borrow checking failed. + // I'm not sure how helpful this is, to be honest, but it avoids + // a + // lot of annoying errors in the compile-fail tests (basically, + // lint warnings and so on -- kindck used to do this abort, but + // kindck is gone now). -nmatsakis + if sess.err_count() > 0 { + return Err(ErrorReported); + } + + time(sess, "misc checking", || { + parallel!({ + time(sess, "privacy checking", || { + rustc_privacy::check_crate(tcx) + }); + }, { + time(sess, "death checking", || middle::dead::check_crate(tcx)); + }, { + time(sess, "unused lib feature checking", || { + stability::check_unused_or_stable_features(tcx) + }); + }, { + time(sess, "lint checking", || lint::check_crate(tcx)); + }); + }); + + Ok(()) +} diff --git a/src/librustc_driver/proc_macro_decls.rs b/src/librustc_interface/proc_macro_decls.rs similarity index 100% rename from src/librustc_driver/proc_macro_decls.rs rename to src/librustc_interface/proc_macro_decls.rs diff --git a/src/librustc_driver/profile/mod.rs b/src/librustc_interface/profile/mod.rs similarity index 100% rename from src/librustc_driver/profile/mod.rs rename to src/librustc_interface/profile/mod.rs diff --git a/src/librustc_driver/profile/trace.rs b/src/librustc_interface/profile/trace.rs similarity index 100% rename from src/librustc_driver/profile/trace.rs rename to src/librustc_interface/profile/trace.rs diff --git a/src/librustc_interface/util.rs b/src/librustc_interface/util.rs new file mode 100644 index 0000000000000..6f92c30446215 --- /dev/null +++ b/src/librustc_interface/util.rs @@ -0,0 +1,702 @@ +use rustc::session::config::{Input, OutputFilenames, ErrorOutputType}; +use rustc::session::{self, config, early_error, filesearch, Session, DiagnosticOutput}; +use rustc::session::CrateDisambiguator; +use rustc::ty; +use rustc::lint; +use rustc_codegen_utils::codegen_backend::CodegenBackend; +use rustc_data_structures::sync::{Lock, Lrc}; +use rustc_data_structures::stable_hasher::StableHasher; +use rustc_data_structures::fingerprint::Fingerprint; +use rustc_data_structures::thin_vec::ThinVec; +use rustc_data_structures::fx::{FxHashSet, FxHashMap}; +use rustc_errors::registry::Registry; +use rustc_lint; +use rustc_metadata::dynamic_lib::DynamicLibrary; +use rustc_mir; +use rustc_passes; +use rustc_plugin; +use rustc_privacy; +use rustc_resolve; +use rustc_typeck; +use std::collections::HashSet; +use std::env; +use std::env::consts::{DLL_PREFIX, DLL_SUFFIX}; +use std::io::{self, Write}; +use std::mem; +use std::path::{Path, PathBuf}; +use std::sync::atomic::{AtomicBool, Ordering}; +use std::sync::{Arc, Mutex, Once}; +use std::ops::DerefMut; +use smallvec::SmallVec; +use syntax::ptr::P; +use syntax::mut_visit::{*, MutVisitor, visit_clobber}; +use syntax::ast::BlockCheckMode; +use syntax::util::lev_distance::find_best_match_for_name; +use syntax::source_map::{FileLoader, RealFileLoader, SourceMap}; +use syntax::symbol::Symbol; +use syntax::{self, ast, attr}; +#[cfg(not(parallel_compiler))] +use std::{thread, panic}; + +pub fn diagnostics_registry() -> Registry { + let mut all_errors = Vec::new(); + all_errors.extend_from_slice(&rustc::DIAGNOSTICS); + all_errors.extend_from_slice(&rustc_typeck::DIAGNOSTICS); + all_errors.extend_from_slice(&rustc_resolve::DIAGNOSTICS); + all_errors.extend_from_slice(&rustc_privacy::DIAGNOSTICS); + // FIXME: need to figure out a way to get these back in here + // all_errors.extend_from_slice(get_codegen_backend(sess).diagnostics()); + all_errors.extend_from_slice(&rustc_metadata::DIAGNOSTICS); + all_errors.extend_from_slice(&rustc_passes::DIAGNOSTICS); + all_errors.extend_from_slice(&rustc_plugin::DIAGNOSTICS); + all_errors.extend_from_slice(&rustc_mir::DIAGNOSTICS); + all_errors.extend_from_slice(&syntax::DIAGNOSTICS); + + Registry::new(&all_errors) +} + +/// Adds `target_feature = "..."` cfgs for a variety of platform +/// specific features (SSE, NEON etc.). +/// +/// This is performed by checking whether a whitelisted set of +/// features is available on the target machine, by querying LLVM. +pub fn add_configuration( + cfg: &mut ast::CrateConfig, + sess: &Session, + codegen_backend: &dyn CodegenBackend, +) { + let tf = Symbol::intern("target_feature"); + + cfg.extend( + codegen_backend + .target_features(sess) + .into_iter() + .map(|feat| (tf, Some(feat))), + ); + + if sess.crt_static_feature() { + cfg.insert((tf, Some(Symbol::intern("crt-static")))); + } +} + +fn load_backend_from_dylib(path: &Path) -> fn() -> Box { + let lib = DynamicLibrary::open(Some(path)).unwrap_or_else(|err| { + let err = format!("couldn't load codegen backend {:?}: {:?}", path, err); + early_error(ErrorOutputType::default(), &err); + }); + unsafe { + match lib.symbol("__rustc_codegen_backend") { + Ok(f) => { + mem::forget(lib); + mem::transmute::<*mut u8, _>(f) + } + Err(e) => { + let err = format!("couldn't load codegen backend as it \ + doesn't export the `__rustc_codegen_backend` \ + symbol: {:?}", e); + early_error(ErrorOutputType::default(), &err); + } + } + } +} + +pub fn get_codegen_backend(sess: &Session) -> Box { + static INIT: Once = Once::new(); + + static mut LOAD: fn() -> Box = || unreachable!(); + + INIT.call_once(|| { + let codegen_name = sess.opts.debugging_opts.codegen_backend.as_ref() + .unwrap_or(&sess.target.target.options.codegen_backend); + let backend = match &codegen_name[..] { + "metadata_only" => { + rustc_codegen_utils::codegen_backend::MetadataOnlyCodegenBackend::boxed + } + filename if filename.contains(".") => { + load_backend_from_dylib(filename.as_ref()) + } + codegen_name => get_codegen_sysroot(codegen_name), + }; + + unsafe { + LOAD = backend; + } + }); + let backend = unsafe { LOAD() }; + backend.init(sess); + backend +} + +pub fn get_codegen_sysroot(backend_name: &str) -> fn() -> Box { + // For now we only allow this function to be called once as it'll dlopen a + // few things, which seems to work best if we only do that once. In + // general this assertion never trips due to the once guard in `get_codegen_backend`, + // but there's a few manual calls to this function in this file we protect + // against. + static LOADED: AtomicBool = AtomicBool::new(false); + assert!(!LOADED.fetch_or(true, Ordering::SeqCst), + "cannot load the default codegen backend twice"); + + let target = session::config::host_triple(); + let mut sysroot_candidates = vec![filesearch::get_or_default_sysroot()]; + let path = current_dll_path() + .and_then(|s| s.canonicalize().ok()); + if let Some(dll) = path { + // use `parent` twice to chop off the file name and then also the + // directory containing the dll which should be either `lib` or `bin`. + if let Some(path) = dll.parent().and_then(|p| p.parent()) { + // The original `path` pointed at the `rustc_driver` crate's dll. + // Now that dll should only be in one of two locations. The first is + // in the compiler's libdir, for example `$sysroot/lib/*.dll`. The + // other is the target's libdir, for example + // `$sysroot/lib/rustlib/$target/lib/*.dll`. + // + // We don't know which, so let's assume that if our `path` above + // ends in `$target` we *could* be in the target libdir, and always + // assume that we may be in the main libdir. + sysroot_candidates.push(path.to_owned()); + + if path.ends_with(target) { + sysroot_candidates.extend(path.parent() // chop off `$target` + .and_then(|p| p.parent()) // chop off `rustlib` + .and_then(|p| p.parent()) // chop off `lib` + .map(|s| s.to_owned())); + } + } + } + + let sysroot = sysroot_candidates.iter() + .map(|sysroot| { + let libdir = filesearch::relative_target_lib_path(&sysroot, &target); + sysroot.join(libdir).with_file_name( + option_env!("CFG_CODEGEN_BACKENDS_DIR").unwrap_or("codegen-backends")) + }) + .filter(|f| { + info!("codegen backend candidate: {}", f.display()); + f.exists() + }) + .next(); + let sysroot = sysroot.unwrap_or_else(|| { + let candidates = sysroot_candidates.iter() + .map(|p| p.display().to_string()) + .collect::>() + .join("\n* "); + let err = format!("failed to find a `codegen-backends` folder \ + in the sysroot candidates:\n* {}", candidates); + early_error(ErrorOutputType::default(), &err); + }); + info!("probing {} for a codegen backend", sysroot.display()); + + let d = sysroot.read_dir().unwrap_or_else(|e| { + let err = format!("failed to load default codegen backend, couldn't \ + read `{}`: {}", sysroot.display(), e); + early_error(ErrorOutputType::default(), &err); + }); + + let mut file: Option = None; + + let expected_name = format!("rustc_codegen_llvm-{}", backend_name); + for entry in d.filter_map(|e| e.ok()) { + let path = entry.path(); + let filename = match path.file_name().and_then(|s| s.to_str()) { + Some(s) => s, + None => continue, + }; + if !(filename.starts_with(DLL_PREFIX) && filename.ends_with(DLL_SUFFIX)) { + continue + } + let name = &filename[DLL_PREFIX.len() .. filename.len() - DLL_SUFFIX.len()]; + if name != expected_name { + continue + } + if let Some(ref prev) = file { + let err = format!("duplicate codegen backends found\n\ + first: {}\n\ + second: {}\n\ + ", prev.display(), path.display()); + early_error(ErrorOutputType::default(), &err); + } + file = Some(path.clone()); + } + + match file { + Some(ref s) => return load_backend_from_dylib(s), + None => { + let err = format!("failed to load default codegen backend for `{}`, \ + no appropriate codegen dylib found in `{}`", + backend_name, sysroot.display()); + early_error(ErrorOutputType::default(), &err); + } + } + + #[cfg(unix)] + fn current_dll_path() -> Option { + use std::ffi::{OsStr, CStr}; + use std::os::unix::prelude::*; + + unsafe { + let addr = current_dll_path as usize as *mut _; + let mut info = mem::zeroed(); + if libc::dladdr(addr, &mut info) == 0 { + info!("dladdr failed"); + return None + } + if info.dli_fname.is_null() { + info!("dladdr returned null pointer"); + return None + } + let bytes = CStr::from_ptr(info.dli_fname).to_bytes(); + let os = OsStr::from_bytes(bytes); + Some(PathBuf::from(os)) + } + } + + #[cfg(windows)] + fn current_dll_path() -> Option { + use std::ffi::OsString; + use std::os::windows::prelude::*; + + extern "system" { + fn GetModuleHandleExW(dwFlags: u32, + lpModuleName: usize, + phModule: *mut usize) -> i32; + fn GetModuleFileNameW(hModule: usize, + lpFilename: *mut u16, + nSize: u32) -> u32; + } + + const GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS: u32 = 0x00000004; + + unsafe { + let mut module = 0; + let r = GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, + current_dll_path as usize, + &mut module); + if r == 0 { + info!("GetModuleHandleExW failed: {}", io::Error::last_os_error()); + return None + } + let mut space = Vec::with_capacity(1024); + let r = GetModuleFileNameW(module, + space.as_mut_ptr(), + space.capacity() as u32); + if r == 0 { + info!("GetModuleFileNameW failed: {}", io::Error::last_os_error()); + return None + } + let r = r as usize; + if r >= space.capacity() { + info!("our buffer was too small? {}", + io::Error::last_os_error()); + return None + } + space.set_len(r); + let os = OsString::from_wide(&space); + Some(PathBuf::from(os)) + } + } +} + +pub fn compute_crate_disambiguator(session: &Session) -> CrateDisambiguator { + use std::hash::Hasher; + + // The crate_disambiguator is a 128 bit hash. The disambiguator is fed + // into various other hashes quite a bit (symbol hashes, incr. comp. hashes, + // debuginfo type IDs, etc), so we don't want it to be too wide. 128 bits + // should still be safe enough to avoid collisions in practice. + let mut hasher = StableHasher::::new(); + + let mut metadata = session.opts.cg.metadata.clone(); + // We don't want the crate_disambiguator to dependent on the order + // -C metadata arguments, so sort them: + metadata.sort(); + // Every distinct -C metadata value is only incorporated once: + metadata.dedup(); + + hasher.write(b"metadata"); + for s in &metadata { + // Also incorporate the length of a metadata string, so that we generate + // different values for `-Cmetadata=ab -Cmetadata=c` and + // `-Cmetadata=a -Cmetadata=bc` + hasher.write_usize(s.len()); + hasher.write(s.as_bytes()); + } + + // Also incorporate crate type, so that we don't get symbol conflicts when + // linking against a library of the same name, if this is an executable. + let is_exe = session + .crate_types + .borrow() + .contains(&config::CrateType::Executable); + hasher.write(if is_exe { b"exe" } else { b"lib" }); + + CrateDisambiguator::from(hasher.finish()) +} + +pub fn collect_crate_types(session: &Session, attrs: &[ast::Attribute]) -> Vec { + // Unconditionally collect crate types from attributes to make them used + let attr_types: Vec = attrs + .iter() + .filter_map(|a| { + if a.check_name("crate_type") { + match a.value_str() { + Some(ref n) if *n == "rlib" => Some(config::CrateType::Rlib), + Some(ref n) if *n == "dylib" => Some(config::CrateType::Dylib), + Some(ref n) if *n == "cdylib" => Some(config::CrateType::Cdylib), + Some(ref n) if *n == "lib" => Some(config::default_lib_output()), + Some(ref n) if *n == "staticlib" => Some(config::CrateType::Staticlib), + Some(ref n) if *n == "proc-macro" => Some(config::CrateType::ProcMacro), + Some(ref n) if *n == "bin" => Some(config::CrateType::Executable), + Some(ref n) => { + let crate_types = vec![ + Symbol::intern("rlib"), + Symbol::intern("dylib"), + Symbol::intern("cdylib"), + Symbol::intern("lib"), + Symbol::intern("staticlib"), + Symbol::intern("proc-macro"), + Symbol::intern("bin") + ]; + + if let ast::MetaItemKind::NameValue(spanned) = a.meta().unwrap().node { + let span = spanned.span; + let lev_candidate = find_best_match_for_name( + crate_types.iter(), + &n.as_str(), + None + ); + if let Some(candidate) = lev_candidate { + session.buffer_lint_with_diagnostic( + lint::builtin::UNKNOWN_CRATE_TYPES, + ast::CRATE_NODE_ID, + span, + "invalid `crate_type` value", + lint::builtin::BuiltinLintDiagnostics:: + UnknownCrateTypes( + span, + "did you mean".to_string(), + format!("\"{}\"", candidate) + ) + ); + } else { + session.buffer_lint( + lint::builtin::UNKNOWN_CRATE_TYPES, + ast::CRATE_NODE_ID, + span, + "invalid `crate_type` value" + ); + } + } + None + } + None => None + } + } else { + None + } + }) + .collect(); + + // If we're generating a test executable, then ignore all other output + // styles at all other locations + if session.opts.test { + return vec![config::CrateType::Executable]; + } + + // Only check command line flags if present. If no types are specified by + // command line, then reuse the empty `base` Vec to hold the types that + // will be found in crate attributes. + let mut base = session.opts.crate_types.clone(); + if base.is_empty() { + base.extend(attr_types); + if base.is_empty() { + base.push(::rustc_codegen_utils::link::default_output_for_target( + session, + )); + } else { + base.sort(); + base.dedup(); + } + } + + base.retain(|crate_type| { + let res = !::rustc_codegen_utils::link::invalid_output_for_target(session, *crate_type); + + if !res { + session.warn(&format!( + "dropping unsupported crate type `{}` for target `{}`", + *crate_type, session.opts.target_triple + )); + } + + res + }); + + base +} + +pub fn build_output_filenames( + input: &Input, + odir: &Option, + ofile: &Option, + attrs: &[ast::Attribute], + sess: &Session, +) -> OutputFilenames { + match *ofile { + None => { + // "-" as input file will cause the parser to read from stdin so we + // have to make up a name + // We want to toss everything after the final '.' + let dirpath = (*odir).as_ref().cloned().unwrap_or_default(); + + // If a crate name is present, we use it as the link name + let stem = sess.opts + .crate_name + .clone() + .or_else(|| attr::find_crate_name(attrs).map(|n| n.to_string())) + .unwrap_or_else(|| input.filestem().to_owned()); + + OutputFilenames { + out_directory: dirpath, + out_filestem: stem, + single_output_file: None, + extra: sess.opts.cg.extra_filename.clone(), + outputs: sess.opts.output_types.clone(), + } + } + + Some(ref out_file) => { + let unnamed_output_types = sess.opts + .output_types + .values() + .filter(|a| a.is_none()) + .count(); + let ofile = if unnamed_output_types > 1 { + sess.warn( + "due to multiple output types requested, the explicitly specified \ + output file name will be adapted for each output type", + ); + None + } else { + Some(out_file.clone()) + }; + if *odir != None { + sess.warn("ignoring --out-dir flag due to -o flag"); + } + if !sess.opts.cg.extra_filename.is_empty() { + sess.warn("ignoring -C extra-filename flag due to -o flag"); + } + + OutputFilenames { + out_directory: out_file.parent().unwrap_or_else(|| Path::new("")).to_path_buf(), + out_filestem: out_file + .file_stem() + .unwrap_or_default() + .to_str() + .unwrap() + .to_string(), + single_output_file: ofile, + extra: sess.opts.cg.extra_filename.clone(), + outputs: sess.opts.output_types.clone(), + } + } + } +} + +// Note: Also used by librustdoc, see PR #43348. Consider moving this struct elsewhere. +// +// FIXME: Currently the `everybody_loops` transformation is not applied to: +// * `const fn`, due to issue #43636 that `loop` is not supported for const evaluation. We are +// waiting for miri to fix that. +// * `impl Trait`, due to issue #43869 that functions returning impl Trait cannot be diverging. +// Solving this may require `!` to implement every trait, which relies on the an even more +// ambitious form of the closed RFC #1637. See also [#34511]. +// +// [#34511]: https://github.com/rust-lang/rust/issues/34511#issuecomment-322340401 +pub struct ReplaceBodyWithLoop<'a> { + within_static_or_const: bool, + nested_blocks: Option>, + sess: &'a Session, +} + +impl<'a> ReplaceBodyWithLoop<'a> { + pub fn new(sess: &'a Session) -> ReplaceBodyWithLoop<'a> { + ReplaceBodyWithLoop { + within_static_or_const: false, + nested_blocks: None, + sess + } + } + + fn run R>(&mut self, is_const: bool, action: F) -> R { + let old_const = mem::replace(&mut self.within_static_or_const, is_const); + let old_blocks = self.nested_blocks.take(); + let ret = action(self); + self.within_static_or_const = old_const; + self.nested_blocks = old_blocks; + ret + } + + fn should_ignore_fn(ret_ty: &ast::FnDecl) -> bool { + if let ast::FunctionRetTy::Ty(ref ty) = ret_ty.output { + fn involves_impl_trait(ty: &ast::Ty) -> bool { + match ty.node { + ast::TyKind::ImplTrait(..) => true, + ast::TyKind::Slice(ref subty) | + ast::TyKind::Array(ref subty, _) | + ast::TyKind::Ptr(ast::MutTy { ty: ref subty, .. }) | + ast::TyKind::Rptr(_, ast::MutTy { ty: ref subty, .. }) | + ast::TyKind::Paren(ref subty) => involves_impl_trait(subty), + ast::TyKind::Tup(ref tys) => any_involves_impl_trait(tys.iter()), + ast::TyKind::Path(_, ref path) => path.segments.iter().any(|seg| { + match seg.args.as_ref().map(|generic_arg| &**generic_arg) { + None => false, + Some(&ast::GenericArgs::AngleBracketed(ref data)) => { + let types = data.args.iter().filter_map(|arg| match arg { + ast::GenericArg::Type(ty) => Some(ty), + _ => None, + }); + any_involves_impl_trait(types.into_iter()) || + any_involves_impl_trait(data.bindings.iter().map(|b| &b.ty)) + }, + Some(&ast::GenericArgs::Parenthesized(ref data)) => { + any_involves_impl_trait(data.inputs.iter()) || + any_involves_impl_trait(data.output.iter()) + } + } + }), + _ => false, + } + } + + fn any_involves_impl_trait<'a, I: Iterator>>(mut it: I) -> bool { + it.any(|subty| involves_impl_trait(subty)) + } + + involves_impl_trait(ty) + } else { + false + } + } +} + +impl<'a> MutVisitor for ReplaceBodyWithLoop<'a> { + fn visit_item_kind(&mut self, i: &mut ast::ItemKind) { + let is_const = match i { + ast::ItemKind::Static(..) | ast::ItemKind::Const(..) => true, + ast::ItemKind::Fn(ref decl, ref header, _, _) => + header.constness.node == ast::Constness::Const || Self::should_ignore_fn(decl), + _ => false, + }; + self.run(is_const, |s| noop_visit_item_kind(i, s)) + } + + fn flat_map_trait_item(&mut self, i: ast::TraitItem) -> SmallVec<[ast::TraitItem; 1]> { + let is_const = match i.node { + ast::TraitItemKind::Const(..) => true, + ast::TraitItemKind::Method(ast::MethodSig { ref decl, ref header, .. }, _) => + header.constness.node == ast::Constness::Const || Self::should_ignore_fn(decl), + _ => false, + }; + self.run(is_const, |s| noop_flat_map_trait_item(i, s)) + } + + fn flat_map_impl_item(&mut self, i: ast::ImplItem) -> SmallVec<[ast::ImplItem; 1]> { + let is_const = match i.node { + ast::ImplItemKind::Const(..) => true, + ast::ImplItemKind::Method(ast::MethodSig { ref decl, ref header, .. }, _) => + header.constness.node == ast::Constness::Const || Self::should_ignore_fn(decl), + _ => false, + }; + self.run(is_const, |s| noop_flat_map_impl_item(i, s)) + } + + fn visit_anon_const(&mut self, c: &mut ast::AnonConst) { + self.run(true, |s| noop_visit_anon_const(c, s)) + } + + fn visit_block(&mut self, b: &mut P) { + fn stmt_to_block(rules: ast::BlockCheckMode, + s: Option, + sess: &Session) -> ast::Block { + ast::Block { + stmts: s.into_iter().collect(), + rules, + id: sess.next_node_id(), + span: syntax_pos::DUMMY_SP, + } + } + + fn block_to_stmt(b: ast::Block, sess: &Session) -> ast::Stmt { + let expr = P(ast::Expr { + id: sess.next_node_id(), + node: ast::ExprKind::Block(P(b), None), + span: syntax_pos::DUMMY_SP, + attrs: ThinVec::new(), + }); + + ast::Stmt { + id: sess.next_node_id(), + node: ast::StmtKind::Expr(expr), + span: syntax_pos::DUMMY_SP, + } + } + + let empty_block = stmt_to_block(BlockCheckMode::Default, None, self.sess); + let loop_expr = P(ast::Expr { + node: ast::ExprKind::Loop(P(empty_block), None), + id: self.sess.next_node_id(), + span: syntax_pos::DUMMY_SP, + attrs: ThinVec::new(), + }); + + let loop_stmt = ast::Stmt { + id: self.sess.next_node_id(), + span: syntax_pos::DUMMY_SP, + node: ast::StmtKind::Expr(loop_expr), + }; + + if self.within_static_or_const { + noop_visit_block(b, self) + } else { + visit_clobber(b.deref_mut(), |b| { + let mut stmts = vec![]; + for s in b.stmts { + let old_blocks = self.nested_blocks.replace(vec![]); + + stmts.extend(self.flat_map_stmt(s).into_iter().filter(|s| s.is_item())); + + // we put a Some in there earlier with that replace(), so this is valid + let new_blocks = self.nested_blocks.take().unwrap(); + self.nested_blocks = old_blocks; + stmts.extend(new_blocks.into_iter().map(|b| block_to_stmt(b, &self.sess))); + } + + let mut new_block = ast::Block { + stmts, + ..b + }; + + if let Some(old_blocks) = self.nested_blocks.as_mut() { + //push our fresh block onto the cache and yield an empty block with `loop {}` + if !new_block.stmts.is_empty() { + old_blocks.push(new_block); + } + + stmt_to_block(b.rules, Some(loop_stmt), self.sess) + } else { + //push `loop {}` onto the end of our fresh block and yield that + new_block.stmts.push(loop_stmt); + + new_block + } + }) + } + } + + // in general the pretty printer processes unexpanded code, so + // we override the default `visit_mac` method which panics. + fn visit_mac(&mut self, mac: &mut ast::Mac) { + noop_visit_mac(mac, self) + } +} diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index acaa3d3966288..aa676e382a3fa 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -131,7 +131,7 @@ use std::ops::{self, Deref}; use std::slice; use crate::require_c_abi_if_variadic; -use crate::session::{CompileIncomplete, Session}; +use crate::session::Session; use crate::session::config::EntryFnType; use crate::TypeAndSubsts; use crate::lint; @@ -711,12 +711,12 @@ fn check_mod_item_types<'tcx>(tcx: TyCtxt<'_, 'tcx, 'tcx>, module_def_id: DefId) tcx.hir().visit_item_likes_in_module(module_def_id, &mut CheckItemTypesVisitor { tcx }); } -pub fn check_item_bodies<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>) -> Result<(), CompileIncomplete> { +pub fn check_item_bodies<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>) -> Result<(), ErrorReported> { tcx.typeck_item_bodies(LOCAL_CRATE) } fn typeck_item_bodies<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, crate_num: CrateNum) - -> Result<(), CompileIncomplete> + -> Result<(), ErrorReported> { debug_assert!(crate_num == LOCAL_CRATE); Ok(tcx.sess.track_errors(|| { diff --git a/src/librustc_typeck/lib.rs b/src/librustc_typeck/lib.rs index 931621fb53701..7ac6f2cda6218 100644 --- a/src/librustc_typeck/lib.rs +++ b/src/librustc_typeck/lib.rs @@ -105,7 +105,7 @@ use rustc::infer::InferOk; use rustc::lint; use rustc::middle; use rustc::session; -use rustc::session::CompileIncomplete; +use rustc::util::common::ErrorReported; use rustc::session::config::{EntryFnType, nightly_options}; use rustc::traits::{ObligationCause, ObligationCauseCode, TraitEngine, TraitEngineExt}; use rustc::ty::subst::Substs; @@ -318,7 +318,7 @@ pub fn provide(providers: &mut Providers<'_>) { } pub fn check_crate<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>) - -> Result<(), CompileIncomplete> + -> Result<(), ErrorReported> { tcx.sess.profiler(|p| p.start_activity(ProfileCategory::TypeChecking)); @@ -327,7 +327,6 @@ pub fn check_crate<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>) tcx.sess.track_errors(|| { time(tcx.sess, "type collecting", || collect::collect_item_types(tcx)); - })?; if tcx.features().rustc_attrs { @@ -365,7 +364,11 @@ pub fn check_crate<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>) tcx.sess.profiler(|p| p.end_activity(ProfileCategory::TypeChecking)); - tcx.sess.compile_status() + if tcx.sess.err_count() == 0 { + Ok(()) + } else { + Err(ErrorReported) + } } /// A quasi-deprecated helper used in rustdoc and save-analysis to get diff --git a/src/librustdoc/core.rs b/src/librustdoc/core.rs index e90127ca162d2..bb3ea87c54b08 100644 --- a/src/librustdoc/core.rs +++ b/src/librustdoc/core.rs @@ -1,5 +1,5 @@ use rustc_lint; -use rustc_driver::{self, driver, target_features, abort_on_err}; +use rustc_driver::{driver, abort_on_err}; use rustc::session::{self, config}; use rustc::hir::def_id::{DefId, DefIndex, DefIndexAddressSpace, CrateNum, LOCAL_CRATE}; use rustc::hir::def::Def; @@ -11,6 +11,7 @@ use rustc::hir::map as hir_map; use rustc::lint::{self, LintPass}; use rustc::session::config::ErrorOutputType; use rustc::util::nodemap::{FxHashMap, FxHashSet}; +use rustc_interface::util; use rustc_resolve as resolve; use rustc_metadata::creader::CrateLoader; use rustc_metadata::cstore::CStore; @@ -398,7 +399,7 @@ pub fn run_core(options: RustdocOptions) -> (clean::Crate, RenderInfo, RenderOpt debugging_options.ui_testing); let mut sess = session::build_session_( - sessopts, cpath, diagnostic_handler, source_map, + sessopts, cpath, diagnostic_handler, source_map, Default::default(), ); lint::builtin::HardwiredLints.get_lints() @@ -418,12 +419,12 @@ pub fn run_core(options: RustdocOptions) -> (clean::Crate, RenderInfo, RenderOpt lint::Allow); }); - let codegen_backend = rustc_driver::get_codegen_backend(&sess); + let codegen_backend = util::get_codegen_backend(&sess); let cstore = Rc::new(CStore::new(codegen_backend.metadata_loader())); rustc_lint::register_builtins(&mut sess.lint_store.borrow_mut(), Some(&sess)); let mut cfg = config::build_configuration(&sess, config::parse_cfgspecs(cfgs)); - target_features::add_configuration(&mut cfg, &sess, &*codegen_backend); + util::add_configuration(&mut cfg, &sess, &*codegen_backend); sess.parse_sess.config = cfg; let control = &driver::CompileController::basic(); @@ -477,7 +478,7 @@ pub fn run_core(options: RustdocOptions) -> (clean::Crate, RenderInfo, RenderOpt let mut arenas = AllArenas::new(); let hir_map = hir_map::map_crate(&sess, &*cstore, &mut hir_forest, &defs); - let output_filenames = driver::build_output_filenames(&input, + let output_filenames = util::build_output_filenames(&input, &None, &None, &[], diff --git a/src/librustdoc/lib.rs b/src/librustdoc/lib.rs index ddb730672d294..a7f8bacf09556 100644 --- a/src/librustdoc/lib.rs +++ b/src/librustdoc/lib.rs @@ -30,6 +30,7 @@ extern crate rustc_lint; extern crate rustc_metadata; extern crate rustc_target; extern crate rustc_typeck; +extern crate rustc_interface; extern crate serialize; extern crate syntax; extern crate syntax_pos; diff --git a/src/librustdoc/test.rs b/src/librustdoc/test.rs index 0b9fbc81da626..2b7f5432359dd 100644 --- a/src/librustdoc/test.rs +++ b/src/librustdoc/test.rs @@ -2,10 +2,11 @@ use errors::{self, FatalError}; use errors::emitter::ColorConfig; use rustc_data_structures::sync::Lrc; use rustc_lint; -use rustc_driver::{self, driver, target_features, Compilation}; +use rustc_driver::{self, driver, Compilation}; use rustc_driver::driver::phase_2_configure_and_expand; use rustc_metadata::cstore::CStore; use rustc_metadata::dynamic_lib::DynamicLibrary; +use rustc_interface::util; use rustc::hir; use rustc::hir::intravisit; use rustc::session::{self, CompileIncomplete, config}; @@ -72,15 +73,15 @@ pub fn run(mut options: Options) -> isize { Some(source_map.clone())); let mut sess = session::build_session_( - sessopts, Some(options.input), handler, source_map.clone(), + sessopts, Some(options.input), handler, source_map.clone(), Default::default(), ); - let codegen_backend = rustc_driver::get_codegen_backend(&sess); + let codegen_backend = util::get_codegen_backend(&sess); let cstore = CStore::new(codegen_backend.metadata_loader()); rustc_lint::register_builtins(&mut sess.lint_store.borrow_mut(), Some(&sess)); let mut cfg = config::build_configuration(&sess, config::parse_cfgspecs(options.cfgs.clone())); - target_features::add_configuration(&mut cfg, &sess, &*codegen_backend); + util::add_configuration(&mut cfg, &sess, &*codegen_backend); sess.parse_sess.config = cfg; let krate = @@ -276,9 +277,9 @@ fn run_test(test: &str, cratename: &str, filename: &FileName, line: usize, let diagnostic_handler = errors::Handler::with_emitter(true, false, box emitter); let mut sess = session::build_session_( - sessopts, None, diagnostic_handler, source_map, + sessopts, None, diagnostic_handler, source_map, Default::default(), ); - let codegen_backend = rustc_driver::get_codegen_backend(&sess); + let codegen_backend = util::get_codegen_backend(&sess); let cstore = CStore::new(codegen_backend.metadata_loader()); rustc_lint::register_builtins(&mut sess.lint_store.borrow_mut(), Some(&sess)); @@ -308,7 +309,7 @@ fn run_test(test: &str, cratename: &str, filename: &FileName, line: usize, let mut control = driver::CompileController::basic(); let mut cfg = config::build_configuration(&sess, config::parse_cfgspecs(cfgs.clone())); - target_features::add_configuration(&mut cfg, &sess, &*codegen_backend); + util::add_configuration(&mut cfg, &sess, &*codegen_backend); sess.parse_sess.config = cfg; let out = Some(outdir.lock().unwrap().path().join("rust_out")); diff --git a/src/test/run-make-fulldeps/issue-19371/foo.rs b/src/test/run-make-fulldeps/issue-19371/foo.rs index b8461c568c86d..e9d825df2a46e 100644 --- a/src/test/run-make-fulldeps/issue-19371/foo.rs +++ b/src/test/run-make-fulldeps/issue-19371/foo.rs @@ -6,6 +6,7 @@ extern crate rustc_lint; extern crate rustc_metadata; extern crate rustc_errors; extern crate rustc_codegen_utils; +extern crate rustc_interface; extern crate syntax; use rustc::session::{build_session, Session}; @@ -14,6 +15,7 @@ use rustc::session::config::{Input, Options, use rustc_driver::driver::{self, compile_input, CompileController}; use rustc_metadata::cstore::CStore; use rustc_errors::registry::Registry; +use rustc_interface::util; use syntax::source_map::FileName; use rustc_codegen_utils::codegen_backend::CodegenBackend; @@ -45,7 +47,7 @@ fn main() { fn basic_sess(opts: Options) -> (Session, Rc, Box) { let descriptions = Registry::new(&rustc::DIAGNOSTICS); let sess = build_session(opts, None, descriptions); - let codegen_backend = rustc_driver::get_codegen_backend(&sess); + let codegen_backend = util::get_codegen_backend(&sess); let cstore = Rc::new(CStore::new(codegen_backend.metadata_loader())); rustc_lint::register_builtins(&mut sess.lint_store.borrow_mut(), Some(&sess)); (sess, cstore, codegen_backend) diff --git a/src/test/rustdoc-ui/failed-doctest-output.stdout b/src/test/rustdoc-ui/failed-doctest-output.stdout index 8af05e9ca97b1..a67b2e6235e56 100644 --- a/src/test/rustdoc-ui/failed-doctest-output.stdout +++ b/src/test/rustdoc-ui/failed-doctest-output.stdout @@ -12,7 +12,7 @@ error[E0425]: cannot find value `no` in this scope 3 | no | ^^ not found in this scope -thread '$DIR/failed-doctest-output.rs - OtherStruct (line 17)' panicked at 'couldn't compile the test', src/librustdoc/test.rs:354:13 +thread '$DIR/failed-doctest-output.rs - OtherStruct (line 17)' panicked at 'couldn't compile the test', src/librustdoc/test.rs:355:13 note: Run with `RUST_BACKTRACE=1` environment variable to display a backtrace. ---- $DIR/failed-doctest-output.rs - SomeStruct (line 11) stdout ---- @@ -21,7 +21,7 @@ thread '$DIR/failed-doctest-output.rs - SomeStruct (line 11)' panicked at 'test thread 'main' panicked at 'oh no', $DIR/failed-doctest-output.rs:3:1 note: Run with `RUST_BACKTRACE=1` environment variable to display a backtrace. -', src/librustdoc/test.rs:389:17 +', src/librustdoc/test.rs:390:17 failures: diff --git a/src/test/ui/issues/issue-23302-3.stderr b/src/test/ui/issues/issue-23302-3.stderr index 392769b4c565f..b3ca736622a2a 100644 --- a/src/test/ui/issues/issue-23302-3.stderr +++ b/src/test/ui/issues/issue-23302-3.stderr @@ -20,6 +20,7 @@ note: ...which requires checking which parts of `B` are promotable to static... LL | const B: i32 = A; | ^ = note: ...which again requires const checking if rvalue is promotable to static `A`, completing the cycle + = note: cycle used when running analysis passes on this crate error: aborting due to previous error From db17b31306cd29fa59f31b37098f74635f1e081b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?John=20K=C3=A5re=20Alsaker?= Date: Thu, 7 Feb 2019 21:02:16 +0100 Subject: [PATCH 10/20] Address comments --- src/librustc/session/config.rs | 7 +++++ src/librustc_driver/driver.rs | 15 +++------ src/librustc_driver/lib.rs | 7 ----- src/librustc_driver/pretty.rs | 52 +++++++++++++++----------------- src/librustc_interface/passes.rs | 2 +- src/librustdoc/core.rs | 22 +++++++------- 6 files changed, 48 insertions(+), 57 deletions(-) diff --git a/src/librustc/session/config.rs b/src/librustc/session/config.rs index b6c7ca11f1f2b..f6f41c4333439 100644 --- a/src/librustc/session/config.rs +++ b/src/librustc/session/config.rs @@ -499,6 +499,13 @@ impl Input { Input::Str { ref mut input, .. } => Some(input), } } + + pub fn source_name(&self) -> FileName { + match *self { + Input::File(ref ifile) => ifile.clone().into(), + Input::Str { ref name, .. } => name.clone(), + } + } } #[derive(Clone, Hash)] diff --git a/src/librustc_driver/driver.rs b/src/librustc_driver/driver.rs index 086286accd4cd..05bdd086e2ec0 100644 --- a/src/librustc_driver/driver.rs +++ b/src/librustc_driver/driver.rs @@ -34,7 +34,7 @@ use syntax::ext::base::ExtCtxt; use syntax::mut_visit::MutVisitor; use syntax::parse::{self, PResult}; use syntax::util::node_count::NodeCounter; -use syntax_pos::{FileName, hygiene}; +use syntax_pos::hygiene; use syntax_ext; use serialize::json; @@ -327,7 +327,7 @@ pub fn compile_input( Ok((outputs.clone(), ongoing_codegen, tcx.dep_graph.clone())) }, - )?? + )? }; if sess.opts.debugging_opts.print_type_sizes { @@ -358,13 +358,6 @@ pub fn compile_input( Ok(()) } -pub fn source_name(input: &Input) -> FileName { - match *input { - Input::File(ref ifile) => ifile.clone().into(), - Input::Str { ref name, .. } => name.clone(), - } -} - /// CompileController is used to customize compilation, it allows compilation to /// be stopped and/or to call arbitrary code at various points in compilation. /// It also allows for various flags to be set to influence what information gets @@ -1180,7 +1173,7 @@ pub fn phase_3_run_analysis_passes<'tcx, F, R>( name: &str, output_filenames: &OutputFilenames, f: F, -) -> Result +) -> R where F: for<'a> FnOnce( TyCtxt<'a, 'tcx, 'tcx>, @@ -1223,7 +1216,7 @@ where tcx.analysis(LOCAL_CRATE).ok(); - Ok(f(tcx, rx, tcx.sess.compile_status())) + f(tcx, rx, tcx.sess.compile_status()) }, ) } diff --git a/src/librustc_driver/lib.rs b/src/librustc_driver/lib.rs index a8cc304b6ccf7..ed0d60242a887 100644 --- a/src/librustc_driver/lib.rs +++ b/src/librustc_driver/lib.rs @@ -116,13 +116,6 @@ const ICE_REPORT_COMPILER_FLAGS_EXCLUDE: &[&str] = &["metadata", "extra-filename const ICE_REPORT_COMPILER_FLAGS_STRIP_VALUE: &[&str] = &["incremental"]; -pub fn source_name(input: &Input) -> FileName { - match *input { - Input::File(ref ifile) => ifile.clone().into(), - Input::Str { ref name, .. } => name.clone(), - } -} - pub fn abort_on_err(result: Result, sess: &Session) -> T { match result { Err(CompileIncomplete::Errored(ErrorReported)) => { diff --git a/src/librustc_driver/pretty.rs b/src/librustc_driver/pretty.rs index 6ea61f7f4b145..2d41c6361a5ce 100644 --- a/src/librustc_driver/pretty.rs +++ b/src/librustc_driver/pretty.rs @@ -36,7 +36,6 @@ pub use self::PpMode::*; use self::NodesMatchingUII::*; use abort_on_err; use driver; -use source_name; #[derive(Copy, Clone, PartialEq, Debug)] pub enum PpSourceMode { @@ -217,16 +216,17 @@ impl PpSourceMode { let control = &driver::CompileController::basic(); let codegen_backend = util::get_codegen_backend(sess); let mut arenas = AllArenas::new(); - abort_on_err(driver::phase_3_run_analysis_passes(&*codegen_backend, - control, - sess, - cstore, - hir_map.clone(), - resolutions.clone(), - &mut arenas, - id, - output_filenames, - |tcx, _, _| { + driver::phase_3_run_analysis_passes(&*codegen_backend, + control, + sess, + cstore, + hir_map.clone(), + resolutions.clone(), + &mut arenas, + id, + output_filenames, + |tcx, _, result| { + abort_on_err(result, tcx.sess); let empty_tables = ty::TypeckTables::empty(None); let annotation = TypedAnnotation { tcx, @@ -235,8 +235,7 @@ impl PpSourceMode { tcx.dep_graph.with_ignore(|| { f(&annotation, hir_map.forest.krate()) }) - }), - sess) + }) } _ => panic!("Should use call_with_pp_support"), } @@ -697,7 +696,7 @@ pub fn visit_crate(sess: &Session, krate: &mut ast::Crate, ppm: PpMode) { } fn get_source(input: &Input, sess: &Session) -> (Vec, FileName) { - let src_name = source_name(input); + let src_name = input.source_name(); let src = sess.source_map() .get_source_file(&src_name) .unwrap() @@ -919,16 +918,17 @@ fn print_with_analysis<'tcx, 'a: 'tcx>(sess: &'a Session, let control = &driver::CompileController::basic(); let codegen_backend = util::get_codegen_backend(sess); let mut arenas = AllArenas::new(); - abort_on_err(driver::phase_3_run_analysis_passes(&*codegen_backend, - control, - sess, - cstore, - hir_map.clone(), - resolutions.clone(), - &mut arenas, - crate_name, - output_filenames, - |tcx, _, _| { + driver::phase_3_run_analysis_passes(&*codegen_backend, + control, + sess, + cstore, + hir_map.clone(), + resolutions.clone(), + &mut arenas, + crate_name, + output_filenames, + |tcx, _, result| { + abort_on_err(result, tcx.sess); match ppm { PpmMir | PpmMirCFG => { if let Some(nodeid) = nodeid { @@ -974,9 +974,7 @@ fn print_with_analysis<'tcx, 'a: 'tcx>(sess: &'a Session, } _ => unreachable!(), } - }), - sess) - .unwrap(); + }).unwrap(); write_output(out, ofile); } diff --git a/src/librustc_interface/passes.rs b/src/librustc_interface/passes.rs index 11b2ef3579d02..16ced6956380b 100644 --- a/src/librustc_interface/passes.rs +++ b/src/librustc_interface/passes.rs @@ -59,7 +59,7 @@ use std::rc::Rc; use std::mem; use std::ops::Generator; -// Returns all the paths that correspond to generated files. +/// Returns all the paths that correspond to generated files. pub fn generated_output_paths( sess: &Session, outputs: &OutputFilenames, diff --git a/src/librustdoc/core.rs b/src/librustdoc/core.rs index bb3ea87c54b08..e3457a6b6c52f 100644 --- a/src/librustdoc/core.rs +++ b/src/librustdoc/core.rs @@ -485,16 +485,16 @@ pub fn run_core(options: RustdocOptions) -> (clean::Crate, RenderInfo, RenderOpt &sess); let resolver = RefCell::new(resolver); - abort_on_err(driver::phase_3_run_analysis_passes(&*codegen_backend, - control, - &sess, - &*cstore, - hir_map, - resolutions, - &mut arenas, - &name, - &output_filenames, - |tcx, _, result| { + driver::phase_3_run_analysis_passes(&*codegen_backend, + control, + &sess, + &*cstore, + hir_map, + resolutions, + &mut arenas, + &name, + &output_filenames, + |tcx, _, result| { if result.is_err() { sess.fatal("Compilation failed, aborting rustdoc"); } @@ -609,6 +609,6 @@ pub fn run_core(options: RustdocOptions) -> (clean::Crate, RenderInfo, RenderOpt ctxt.sess().abort_if_errors(); (krate, ctxt.renderinfo.into_inner(), render_options, passes) - }), &sess) + }) }) } From e73f96abe7cc3a76223e5d0b50592c287c517750 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Fri, 8 Feb 2019 12:20:55 +0100 Subject: [PATCH 11/20] make OpTy.op private, and ImmTy.imm public instead --- src/librustc_mir/const_eval.rs | 15 ++----- src/librustc_mir/interpret/cast.rs | 4 +- src/librustc_mir/interpret/operand.rs | 57 +++++++++++++++++++----- src/librustc_mir/interpret/place.rs | 26 +++-------- src/librustc_mir/interpret/terminator.rs | 14 +++--- src/librustc_mir/interpret/traits.rs | 4 ++ src/librustc_mir/transform/const_prop.rs | 25 +++++------ 7 files changed, 80 insertions(+), 65 deletions(-) diff --git a/src/librustc_mir/const_eval.rs b/src/librustc_mir/const_eval.rs index fb0c19f764c13..5d6e9d64aeb58 100644 --- a/src/librustc_mir/const_eval.rs +++ b/src/librustc_mir/const_eval.rs @@ -77,7 +77,7 @@ pub fn op_to_const<'tcx>( let normalized_op = if normalize { ecx.try_read_immediate(op)? } else { - match op.op { + match *op { Operand::Indirect(mplace) => Err(mplace), Operand::Immediate(val) => Ok(val) } @@ -105,15 +105,6 @@ pub fn op_to_const<'tcx>( Ok(ty::Const { val, ty: op.layout.ty }) } -pub fn lazy_const_to_op<'tcx>( - ecx: &CompileTimeEvalContext<'_, '_, 'tcx>, - cnst: ty::LazyConst<'tcx>, - ty: ty::Ty<'tcx>, -) -> EvalResult<'tcx, OpTy<'tcx>> { - let op = ecx.const_value_to_op(cnst)?; - Ok(OpTy { op, layout: ecx.layout_of(ty)? }) -} - fn eval_body_and_ecx<'a, 'mir, 'tcx>( tcx: TyCtxt<'a, 'tcx, 'tcx>, cid: GlobalId<'tcx>, @@ -486,7 +477,7 @@ pub fn const_field<'a, 'tcx>( let ecx = mk_eval_cx(tcx, DUMMY_SP, param_env); let result = (|| { // get the operand again - let op = lazy_const_to_op(&ecx, ty::LazyConst::Evaluated(value), value.ty)?; + let op = ecx.lazy_const_to_op(ty::LazyConst::Evaluated(value), value.ty)?; // downcast let down = match variant { None => op, @@ -512,7 +503,7 @@ pub fn const_variant_index<'a, 'tcx>( ) -> EvalResult<'tcx, VariantIdx> { trace!("const_variant_index: {:?}", val); let ecx = mk_eval_cx(tcx, DUMMY_SP, param_env); - let op = lazy_const_to_op(&ecx, ty::LazyConst::Evaluated(val), val.ty)?; + let op = ecx.lazy_const_to_op(ty::LazyConst::Evaluated(val), val.ty)?; Ok(ecx.read_discriminant(op)?.1) } diff --git a/src/librustc_mir/interpret/cast.rs b/src/librustc_mir/interpret/cast.rs index c3b71be8354da..ce62d79e585a8 100644 --- a/src/librustc_mir/interpret/cast.rs +++ b/src/librustc_mir/interpret/cast.rs @@ -9,7 +9,7 @@ use rustc::mir::interpret::{ use rustc::mir::CastKind; use rustc_apfloat::Float; -use super::{EvalContext, Machine, PlaceTy, OpTy, Immediate}; +use super::{EvalContext, Machine, PlaceTy, OpTy, ImmTy, Immediate}; impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> { fn type_is_fat_ptr(&self, ty: Ty<'tcx>) -> bool { @@ -372,7 +372,7 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> assert_eq!(src.layout.fields.offset(i).bytes(), 0); assert_eq!(src_field_layout.size, src.layout.size); // just sawp out the layout - OpTy { op: src.op, layout: src_field_layout } + OpTy::from(ImmTy { imm: src.to_immediate(), layout: src_field_layout }) } }; if src_field.layout.ty == dst_field.layout.ty { diff --git a/src/librustc_mir/interpret/operand.rs b/src/librustc_mir/interpret/operand.rs index c0b26442dd918..4d0da5e6ecefb 100644 --- a/src/librustc_mir/interpret/operand.rs +++ b/src/librustc_mir/interpret/operand.rs @@ -11,7 +11,10 @@ use rustc::mir::interpret::{ ConstValue, Pointer, Scalar, EvalResult, EvalErrorKind, }; -use super::{EvalContext, Machine, MemPlace, MPlaceTy, MemoryKind}; +use super::{ + EvalContext, Machine, AllocMap, Allocation, AllocationExtra, + MemPlace, MPlaceTy, PlaceTy, Place, MemoryKind, +}; pub use rustc::mir::interpret::ScalarMaybeUndef; /// A `Value` represents a single immediate self-contained Rust value. @@ -112,7 +115,7 @@ impl<'tcx, Tag> Immediate { // as input for binary and cast operations. #[derive(Copy, Clone, Debug)] pub struct ImmTy<'tcx, Tag=()> { - immediate: Immediate, + crate imm: Immediate, // ideally we'd make this private, but const_prop needs this pub layout: TyLayout<'tcx>, } @@ -120,7 +123,7 @@ impl<'tcx, Tag> ::std::ops::Deref for ImmTy<'tcx, Tag> { type Target = Immediate; #[inline(always)] fn deref(&self) -> &Immediate { - &self.immediate + &self.imm } } @@ -180,7 +183,7 @@ impl Operand { #[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)] pub struct OpTy<'tcx, Tag=()> { - crate op: Operand, // ideally we'd make this private, but const_prop needs this + op: Operand, pub layout: TyLayout<'tcx>, } @@ -206,7 +209,7 @@ impl<'tcx, Tag> From> for OpTy<'tcx, Tag> { #[inline(always)] fn from(val: ImmTy<'tcx, Tag>) -> Self { OpTy { - op: Operand::Immediate(val.immediate), + op: Operand::Immediate(val.imm), layout: val.layout } } @@ -324,8 +327,8 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> &self, op: OpTy<'tcx, M::PointerTag> ) -> EvalResult<'tcx, ImmTy<'tcx, M::PointerTag>> { - if let Ok(immediate) = self.try_read_immediate(op)? { - Ok(ImmTy { immediate, layout: op.layout }) + if let Ok(imm) = self.try_read_immediate(op)? { + Ok(ImmTy { imm, layout: op.layout }) } else { bug!("primitive read failed for type: {:?}", op.layout.ty); } @@ -469,6 +472,22 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> Ok(OpTy { op, layout }) } + /// Every place can be read from, so we can turm them into an operand + #[inline(always)] + pub fn place_to_op( + &self, + place: PlaceTy<'tcx, M::PointerTag> + ) -> EvalResult<'tcx, OpTy<'tcx, M::PointerTag>> { + let op = match *place { + Place::Ptr(mplace) => { + Operand::Indirect(mplace) + } + Place::Local { frame, local } => + *self.stack[frame].locals[local].access()? + }; + Ok(OpTy { op, layout: place.layout }) + } + // Evaluate a place with the goal of reading from it. This lets us sometimes // avoid allocations. fn eval_place_to_op( @@ -531,10 +550,8 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> .collect() } - // Used when miri runs into a constant, and by CTFE. - // FIXME: CTFE should use allocations, then we can make this private (embed it into - // `eval_operand`, ideally). - pub(crate) fn const_value_to_op( + // Used when Miri runs into a constant, and (indirectly through lazy_const_to_op) by CTFE. + fn const_value_to_op( &self, val: ty::LazyConst<'tcx>, ) -> EvalResult<'tcx, Operand> { @@ -666,3 +683,21 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> } } + +impl<'a, 'mir, 'tcx, M> EvalContext<'a, 'mir, 'tcx, M> +where + M: Machine<'a, 'mir, 'tcx, PointerTag=()>, + // FIXME: Working around https://github.com/rust-lang/rust/issues/24159 + M::MemoryMap: AllocMap, Allocation<(), M::AllocExtra>)>, + M::AllocExtra: AllocationExtra<(), M::MemoryExtra>, +{ + // FIXME: CTFE should use allocations, then we can remove this. + pub(crate) fn lazy_const_to_op( + &self, + cnst: ty::LazyConst<'tcx>, + ty: ty::Ty<'tcx>, + ) -> EvalResult<'tcx, OpTy<'tcx>> { + let op = self.const_value_to_op(cnst)?; + Ok(OpTy { op, layout: self.layout_of(ty)? }) + } +} diff --git a/src/librustc_mir/interpret/place.rs b/src/librustc_mir/interpret/place.rs index 004a11e34d6e1..c1d0f0a8bd15b 100644 --- a/src/librustc_mir/interpret/place.rs +++ b/src/librustc_mir/interpret/place.rs @@ -244,10 +244,10 @@ impl<'tcx, Tag> MPlaceTy<'tcx, Tag> { } } -impl<'tcx, Tag: ::std::fmt::Debug> OpTy<'tcx, Tag> { +impl<'tcx, Tag: ::std::fmt::Debug + Copy> OpTy<'tcx, Tag> { #[inline(always)] pub fn try_as_mplace(self) -> Result, Immediate> { - match self.op { + match *self { Operand::Indirect(mplace) => Ok(MPlaceTy { mplace, layout: self.layout }), Operand::Immediate(imm) => Err(imm), } @@ -487,9 +487,9 @@ where Deref => self.deref_operand(base.into())?, Index(local) => { - let n = *self.frame().locals[local].access()?; - let n_layout = self.layout_of(self.tcx.types.usize)?; - let n = self.read_scalar(OpTy { op: n, layout: n_layout })?; + let layout = self.layout_of(self.tcx.types.usize)?; + let n = self.access_local(self.frame(), local, Some(layout))?; + let n = self.read_scalar(n)?; let n = n.to_bits(self.tcx.data_layout.pointer_size)?; self.mplace_field(base, u64::try_from(n).unwrap())? } @@ -991,22 +991,6 @@ where Ok(()) } - /// Every place can be read from, so we can turm them into an operand - #[inline(always)] - pub fn place_to_op( - &self, - place: PlaceTy<'tcx, M::PointerTag> - ) -> EvalResult<'tcx, OpTy<'tcx, M::PointerTag>> { - let op = match place.place { - Place::Ptr(mplace) => { - Operand::Indirect(mplace) - } - Place::Local { frame, local } => - *self.stack[frame].locals[local].access()? - }; - Ok(OpTy { op, layout: place.layout }) - } - pub fn raw_const_to_mplace( &self, raw: RawConst<'tcx>, diff --git a/src/librustc_mir/interpret/terminator.rs b/src/librustc_mir/interpret/terminator.rs index be50daa17092f..72d60f259e727 100644 --- a/src/librustc_mir/interpret/terminator.rs +++ b/src/librustc_mir/interpret/terminator.rs @@ -7,7 +7,7 @@ use rustc_target::spec::abi::Abi; use rustc::mir::interpret::{EvalResult, PointerArithmetic, EvalErrorKind, Scalar}; use super::{ - EvalContext, Machine, Immediate, OpTy, PlaceTy, MPlaceTy, Operand, StackPopCleanup + EvalContext, Machine, Immediate, OpTy, ImmTy, PlaceTy, MPlaceTy, StackPopCleanup }; impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> { @@ -418,8 +418,10 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> let mut args = args.to_vec(); let pointee = args[0].layout.ty.builtin_deref(true).unwrap().ty; let fake_fat_ptr_ty = self.tcx.mk_mut_ptr(pointee); - args[0].layout = self.layout_of(fake_fat_ptr_ty)?.field(self, 0)?; - args[0].op = Operand::Immediate(Immediate::Scalar(ptr.ptr.into())); // strip vtable + args[0] = OpTy::from(ImmTy { // strip vtable + layout: self.layout_of(fake_fat_ptr_ty)?.field(self, 0)?, + imm: Immediate::Scalar(ptr.ptr.into()) + }); trace!("Patched self operand to {:#?}", args[0]); // recurse with concrete function self.eval_fn_call(instance, span, caller_abi, &args, dest, ret) @@ -448,8 +450,8 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> _ => (instance, place), }; - let arg = OpTy { - op: Operand::Immediate(place.to_ref()), + let arg = ImmTy { + imm: place.to_ref(), layout: self.layout_of(self.tcx.mk_mut_ptr(place.layout.ty))?, }; @@ -460,7 +462,7 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> instance, span, Abi::Rust, - &[arg], + &[arg.into()], Some(dest.into()), Some(target), ) diff --git a/src/librustc_mir/interpret/traits.rs b/src/librustc_mir/interpret/traits.rs index 65d7060b544d6..1b0a9b17d3686 100644 --- a/src/librustc_mir/interpret/traits.rs +++ b/src/librustc_mir/interpret/traits.rs @@ -22,6 +22,10 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> let (ty, poly_trait_ref) = self.tcx.erase_regions(&(ty, poly_trait_ref)); if let Some(&vtable) = self.vtables.get(&(ty, poly_trait_ref)) { + // This means we guarantee that there are no duplicate vtables, we will + // always use the same vtable for the same (Type, Trait) combination. + // That's not what happens in rustc, but emulating per-crate deduplication + // does not sound like it actually makes anything any better. return Ok(Pointer::from(vtable).with_default_tag()); } diff --git a/src/librustc_mir/transform/const_prop.rs b/src/librustc_mir/transform/const_prop.rs index 018f71c39e513..d8d24c06f5a8b 100644 --- a/src/librustc_mir/transform/const_prop.rs +++ b/src/librustc_mir/transform/const_prop.rs @@ -18,10 +18,9 @@ use rustc::ty::layout::{ HasTyCtxt, TargetDataLayout, HasDataLayout, }; -use crate::interpret::{self, EvalContext, ScalarMaybeUndef, Immediate, OpTy, MemoryKind}; +use crate::interpret::{EvalContext, ScalarMaybeUndef, Immediate, OpTy, ImmTy, MemoryKind}; use crate::const_eval::{ CompileTimeInterpreter, error_to_const_error, eval_promoted, mk_eval_cx, - lazy_const_to_op, }; use crate::transform::{MirPass, MirSource}; @@ -254,7 +253,7 @@ impl<'a, 'mir, 'tcx> ConstPropagator<'a, 'mir, 'tcx> { source_info: SourceInfo, ) -> Option> { self.ecx.tcx.span = source_info.span; - match lazy_const_to_op(&self.ecx, *c.literal, c.ty) { + match self.ecx.lazy_const_to_op(*c.literal, c.ty) { Ok(op) => { Some((op, c.span)) }, @@ -345,15 +344,15 @@ impl<'a, 'mir, 'tcx> ConstPropagator<'a, 'mir, 'tcx> { Rvalue::Len(_) => None, Rvalue::NullaryOp(NullOp::SizeOf, ty) => { type_size_of(self.tcx, self.param_env, ty).and_then(|n| Some(( - OpTy { - op: interpret::Operand::Immediate(Immediate::Scalar( + ImmTy { + imm: Immediate::Scalar( Scalar::Bits { bits: n as u128, size: self.tcx.data_layout.pointer_size.bytes() as u8, }.into() - )), + ), layout: self.tcx.layout_of(self.param_env.and(self.tcx.types.usize)).ok()?, - }, + }.into(), span, ))) } @@ -388,11 +387,11 @@ impl<'a, 'mir, 'tcx> ConstPropagator<'a, 'mir, 'tcx> { // Now run the actual operation. this.ecx.unary_op(op, prim, arg.layout) })?; - let res = OpTy { - op: interpret::Operand::Immediate(Immediate::Scalar(val.into())), + let res = ImmTy { + imm: Immediate::Scalar(val.into()), layout: place_layout, }; - Some((res, span)) + Some((res.into(), span)) } Rvalue::CheckedBinaryOp(op, ref left, ref right) | Rvalue::BinaryOp(op, ref left, ref right) => { @@ -462,11 +461,11 @@ impl<'a, 'mir, 'tcx> ConstPropagator<'a, 'mir, 'tcx> { } Immediate::Scalar(val.into()) }; - let res = OpTy { - op: interpret::Operand::Immediate(val), + let res = ImmTy { + imm: val, layout: place_layout, }; - Some((res, span)) + Some((res.into(), span)) }, } } From b376ae6671a20914d63691fdd4b543b2e6341d5b Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Fri, 8 Feb 2019 14:00:52 +0100 Subject: [PATCH 12/20] make bin_op and unary_op APIs consistently work on ImmTy --- src/librustc_mir/const_eval.rs | 11 ++-- src/librustc_mir/interpret/intrinsics.rs | 2 +- src/librustc_mir/interpret/machine.rs | 10 ++-- src/librustc_mir/interpret/operand.rs | 20 ++++++- src/librustc_mir/interpret/operator.rs | 73 ++++++++++-------------- src/librustc_mir/interpret/step.rs | 2 +- src/librustc_mir/interpret/terminator.rs | 4 +- src/librustc_mir/transform/const_prop.rs | 9 ++- 8 files changed, 66 insertions(+), 65 deletions(-) diff --git a/src/librustc_mir/const_eval.rs b/src/librustc_mir/const_eval.rs index 5d6e9d64aeb58..7be7f4b439289 100644 --- a/src/librustc_mir/const_eval.rs +++ b/src/librustc_mir/const_eval.rs @@ -11,7 +11,7 @@ use rustc::hir::def::Def; use rustc::mir::interpret::{ConstEvalErr, ErrorHandled}; use rustc::mir; use rustc::ty::{self, TyCtxt, query::TyCtxtAt}; -use rustc::ty::layout::{self, LayoutOf, TyLayout, VariantIdx}; +use rustc::ty::layout::{self, LayoutOf, VariantIdx}; use rustc::ty::subst::Subst; use rustc::traits::Reveal; use rustc_data_structures::fx::FxHashMap; @@ -21,7 +21,8 @@ use syntax::ast::Mutability; use syntax::source_map::{Span, DUMMY_SP}; use crate::interpret::{self, - PlaceTy, MPlaceTy, MemPlace, OpTy, Operand, Immediate, Scalar, RawConst, ConstValue, Pointer, + PlaceTy, MPlaceTy, MemPlace, OpTy, ImmTy, Operand, Immediate, Scalar, Pointer, + RawConst, ConstValue, EvalResult, EvalError, EvalErrorKind, GlobalId, EvalContext, StackPopCleanup, Allocation, AllocId, MemoryKind, snapshot, RefTracking, @@ -379,10 +380,8 @@ impl<'a, 'mir, 'tcx> interpret::Machine<'a, 'mir, 'tcx> fn ptr_op( _ecx: &EvalContext<'a, 'mir, 'tcx, Self>, _bin_op: mir::BinOp, - _left: Scalar, - _left_layout: TyLayout<'tcx>, - _right: Scalar, - _right_layout: TyLayout<'tcx>, + _left: ImmTy<'tcx>, + _right: ImmTy<'tcx>, ) -> EvalResult<'tcx, (Scalar, bool)> { Err( ConstEvalError::NeedsRfc("pointer arithmetic or comparison".to_string()).into(), diff --git a/src/librustc_mir/interpret/intrinsics.rs b/src/librustc_mir/interpret/intrinsics.rs index 78c5c0a6d751c..6466dd4098acd 100644 --- a/src/librustc_mir/interpret/intrinsics.rs +++ b/src/librustc_mir/interpret/intrinsics.rs @@ -173,7 +173,7 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> "unchecked_shr" => BinOp::Shr, _ => bug!("Already checked for int ops") }; - let (val, overflowed) = self.binary_op_imm(bin_op, l, r)?; + let (val, overflowed) = self.binary_op(bin_op, l, r)?; if overflowed { let layout = self.layout_of(substs.type_at(0))?; let r_val = r.to_scalar()?.to_bits(layout.size)?; diff --git a/src/librustc_mir/interpret/machine.rs b/src/librustc_mir/interpret/machine.rs index 8f34b832f0b41..7fb4c47d92acb 100644 --- a/src/librustc_mir/interpret/machine.rs +++ b/src/librustc_mir/interpret/machine.rs @@ -7,11 +7,11 @@ use std::hash::Hash; use rustc::hir::{self, def_id::DefId}; use rustc::mir; -use rustc::ty::{self, layout::TyLayout, query::TyCtxtAt}; +use rustc::ty::{self, query::TyCtxtAt}; use super::{ Allocation, AllocId, EvalResult, Scalar, AllocationExtra, - EvalContext, PlaceTy, MPlaceTy, OpTy, Pointer, MemoryKind, + EvalContext, PlaceTy, MPlaceTy, OpTy, ImmTy, Pointer, MemoryKind, }; /// Whether this kind of memory is allowed to leak @@ -158,10 +158,8 @@ pub trait Machine<'a, 'mir, 'tcx>: Sized { fn ptr_op( ecx: &EvalContext<'a, 'mir, 'tcx, Self>, bin_op: mir::BinOp, - left: Scalar, - left_layout: TyLayout<'tcx>, - right: Scalar, - right_layout: TyLayout<'tcx>, + left: ImmTy<'tcx, Self::PointerTag>, + right: ImmTy<'tcx, Self::PointerTag>, ) -> EvalResult<'tcx, (Scalar, bool)>; /// Heap allocations via the `box` keyword. diff --git a/src/librustc_mir/interpret/operand.rs b/src/librustc_mir/interpret/operand.rs index 4d0da5e6ecefb..7da907028eebf 100644 --- a/src/librustc_mir/interpret/operand.rs +++ b/src/librustc_mir/interpret/operand.rs @@ -44,6 +44,11 @@ impl Immediate { } impl<'tcx, Tag> Immediate { + #[inline] + pub fn from_scalar(val: Scalar) -> Self { + Immediate::Scalar(ScalarMaybeUndef::Scalar(val)) + } + #[inline] pub fn erase_tag(self) -> Immediate { @@ -115,7 +120,7 @@ impl<'tcx, Tag> Immediate { // as input for binary and cast operations. #[derive(Copy, Clone, Debug)] pub struct ImmTy<'tcx, Tag=()> { - crate imm: Immediate, // ideally we'd make this private, but const_prop needs this + pub imm: Immediate, pub layout: TyLayout<'tcx>, } @@ -215,6 +220,19 @@ impl<'tcx, Tag> From> for OpTy<'tcx, Tag> { } } +impl<'tcx, Tag: Copy> ImmTy<'tcx, Tag> +{ + #[inline] + pub fn from_scalar(val: Scalar, layout: TyLayout<'tcx>) -> Self { + ImmTy { imm: Immediate::from_scalar(val), layout } + } + + #[inline] + pub fn to_bits(self) -> EvalResult<'tcx, u128> { + self.to_scalar()?.to_bits(self.layout.size) + } +} + impl<'tcx, Tag> OpTy<'tcx, Tag> { #[inline] diff --git a/src/librustc_mir/interpret/operator.rs b/src/librustc_mir/interpret/operator.rs index 5e3335f4c7219..b3b9c742d6c28 100644 --- a/src/librustc_mir/interpret/operator.rs +++ b/src/librustc_mir/interpret/operator.rs @@ -18,7 +18,7 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> right: ImmTy<'tcx, M::PointerTag>, dest: PlaceTy<'tcx, M::PointerTag>, ) -> EvalResult<'tcx> { - let (val, overflowed) = self.binary_op_imm(op, left, right)?; + let (val, overflowed) = self.binary_op(op, left, right)?; let val = Immediate::ScalarPair(val.into(), Scalar::from_bool(overflowed).into()); self.write_immediate(val, dest) } @@ -32,7 +32,7 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> right: ImmTy<'tcx, M::PointerTag>, dest: PlaceTy<'tcx, M::PointerTag>, ) -> EvalResult<'tcx> { - let (val, _overflowed) = self.binary_op_imm(op, left, right)?; + let (val, _overflowed) = self.binary_op(op, left, right)?; self.write_scalar(val, dest) } } @@ -272,69 +272,55 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> Ok((val, false)) } - /// Convenience wrapper that's useful when keeping the layout together with the - /// immediate value. + /// Returns the result of the specified operation and whether it overflowed. #[inline] - pub fn binary_op_imm( + pub fn binary_op( &self, bin_op: mir::BinOp, left: ImmTy<'tcx, M::PointerTag>, right: ImmTy<'tcx, M::PointerTag>, - ) -> EvalResult<'tcx, (Scalar, bool)> { - self.binary_op( - bin_op, - left.to_scalar()?, left.layout, - right.to_scalar()?, right.layout, - ) - } - - /// Returns the result of the specified operation and whether it overflowed. - pub fn binary_op( - &self, - bin_op: mir::BinOp, - left: Scalar, - left_layout: TyLayout<'tcx>, - right: Scalar, - right_layout: TyLayout<'tcx>, ) -> EvalResult<'tcx, (Scalar, bool)> { trace!("Running binary op {:?}: {:?} ({:?}), {:?} ({:?})", - bin_op, left, left_layout.ty, right, right_layout.ty); + bin_op, *left, left.layout.ty, *right, right.layout.ty); - match left_layout.ty.sty { + match left.layout.ty.sty { ty::Char => { - assert_eq!(left_layout.ty, right_layout.ty); - let left = left.to_char()?; - let right = right.to_char()?; + assert_eq!(left.layout.ty, right.layout.ty); + let left = left.to_scalar()?.to_char()?; + let right = right.to_scalar()?.to_char()?; self.binary_char_op(bin_op, left, right) } ty::Bool => { - assert_eq!(left_layout.ty, right_layout.ty); - let left = left.to_bool()?; - let right = right.to_bool()?; + assert_eq!(left.layout.ty, right.layout.ty); + let left = left.to_scalar()?.to_bool()?; + let right = right.to_scalar()?.to_bool()?; self.binary_bool_op(bin_op, left, right) } ty::Float(fty) => { - assert_eq!(left_layout.ty, right_layout.ty); - let left = left.to_bits(left_layout.size)?; - let right = right.to_bits(right_layout.size)?; + assert_eq!(left.layout.ty, right.layout.ty); + let left = left.to_bits()?; + let right = right.to_bits()?; self.binary_float_op(bin_op, fty, left, right) } _ => { // Must be integer(-like) types. Don't forget about == on fn pointers. - assert!(left_layout.ty.is_integral() || left_layout.ty.is_unsafe_ptr() || - left_layout.ty.is_fn()); - assert!(right_layout.ty.is_integral() || right_layout.ty.is_unsafe_ptr() || - right_layout.ty.is_fn()); + assert!(left.layout.ty.is_integral() || left.layout.ty.is_unsafe_ptr() || + left.layout.ty.is_fn()); + assert!(right.layout.ty.is_integral() || right.layout.ty.is_unsafe_ptr() || + right.layout.ty.is_fn()); // Handle operations that support pointer values - if left.is_ptr() || right.is_ptr() || bin_op == mir::BinOp::Offset { - return M::ptr_op(self, bin_op, left, left_layout, right, right_layout); + if left.to_scalar_ptr()?.is_ptr() || + right.to_scalar_ptr()?.is_ptr() || + bin_op == mir::BinOp::Offset + { + return M::ptr_op(self, bin_op, left, right); } // Everything else only works with "proper" bits - let left = left.to_bits(left_layout.size).expect("we checked is_ptr"); - let right = right.to_bits(right_layout.size).expect("we checked is_ptr"); - self.binary_int_op(bin_op, left, left_layout, right, right_layout) + let l = left.to_bits().expect("we checked is_ptr"); + let r = right.to_bits().expect("we checked is_ptr"); + self.binary_int_op(bin_op, l, left.layout, r, right.layout) } } } @@ -342,13 +328,14 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> pub fn unary_op( &self, un_op: mir::UnOp, - val: Scalar, - layout: TyLayout<'tcx>, + val: ImmTy<'tcx, M::PointerTag>, ) -> EvalResult<'tcx, Scalar> { use rustc::mir::UnOp::*; use rustc_apfloat::ieee::{Single, Double}; use rustc_apfloat::Float; + let layout = val.layout; + let val = val.to_scalar()?; trace!("Running unary op {:?}: {:?} ({:?})", un_op, val, layout.ty.sty); match layout.ty.sty { diff --git a/src/librustc_mir/interpret/step.rs b/src/librustc_mir/interpret/step.rs index 0c988eb681083..97ef2b5fa3485 100644 --- a/src/librustc_mir/interpret/step.rs +++ b/src/librustc_mir/interpret/step.rs @@ -176,7 +176,7 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> UnaryOp(un_op, ref operand) => { // The operand always has the same type as the result. let val = self.read_immediate(self.eval_operand(operand, Some(dest.layout))?)?; - let val = self.unary_op(un_op, val.to_scalar()?, dest.layout)?; + let val = self.unary_op(un_op, val)?; self.write_scalar(val, dest)?; } diff --git a/src/librustc_mir/interpret/terminator.rs b/src/librustc_mir/interpret/terminator.rs index 72d60f259e727..c2ee3f5715bd3 100644 --- a/src/librustc_mir/interpret/terminator.rs +++ b/src/librustc_mir/interpret/terminator.rs @@ -51,8 +51,8 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> // Compare using binary_op, to also support pointer values let const_int = Scalar::from_uint(const_int, discr.layout.size); let (res, _) = self.binary_op(mir::BinOp::Eq, - discr.to_scalar()?, discr.layout, - const_int, discr.layout, + discr, + ImmTy::from_scalar(const_int, discr.layout), )?; if res.to_bool()? { target_block = targets[index]; diff --git a/src/librustc_mir/transform/const_prop.rs b/src/librustc_mir/transform/const_prop.rs index d8d24c06f5a8b..7da00c4ea0c36 100644 --- a/src/librustc_mir/transform/const_prop.rs +++ b/src/librustc_mir/transform/const_prop.rs @@ -370,13 +370,12 @@ impl<'a, 'mir, 'tcx> ConstPropagator<'a, 'mir, 'tcx> { let (arg, _) = self.eval_operand(arg, source_info)?; let val = self.use_ecx(source_info, |this| { - let prim = this.ecx.read_scalar(arg)?.not_undef()?; + let prim = this.ecx.read_immediate(arg)?; match op { UnOp::Neg => { // Need to do overflow check here: For actual CTFE, MIR // generation emits code that does this before calling the op. - let size = arg.layout.size; - if prim.to_bits(size)? == (1 << (size.bits() - 1)) { + if prim.to_bits()? == (1 << (prim.layout.size.bits() - 1)) { return err!(OverflowNeg); } } @@ -385,7 +384,7 @@ impl<'a, 'mir, 'tcx> ConstPropagator<'a, 'mir, 'tcx> { } } // Now run the actual operation. - this.ecx.unary_op(op, prim, arg.layout) + this.ecx.unary_op(op, prim) })?; let res = ImmTy { imm: Immediate::Scalar(val.into()), @@ -446,7 +445,7 @@ impl<'a, 'mir, 'tcx> ConstPropagator<'a, 'mir, 'tcx> { })?; trace!("const evaluating {:?} for {:?} and {:?}", op, left, right); let (val, overflow) = self.use_ecx(source_info, |this| { - this.ecx.binary_op_imm(op, l, r) + this.ecx.binary_op(op, l, r) })?; let val = if let Rvalue::CheckedBinaryOp(..) = *rvalue { Immediate::ScalarPair( From 1a5304ae93d79c0e1fe59cfca6266af970cda061 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Fri, 8 Feb 2019 15:26:45 +0100 Subject: [PATCH 13/20] fix whitespace --- src/librustc_mir/interpret/place.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/librustc_mir/interpret/place.rs b/src/librustc_mir/interpret/place.rs index c1d0f0a8bd15b..b29e09900f6b1 100644 --- a/src/librustc_mir/interpret/place.rs +++ b/src/librustc_mir/interpret/place.rs @@ -487,7 +487,7 @@ where Deref => self.deref_operand(base.into())?, Index(local) => { - let layout = self.layout_of(self.tcx.types.usize)?; + let layout = self.layout_of(self.tcx.types.usize)?; let n = self.access_local(self.frame(), local, Some(layout))?; let n = self.read_scalar(n)?; let n = n.to_bits(self.tcx.data_layout.pointer_size)?; From 22d5e6a37a75db6b59931ee30c1de630f2b5016b Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Wed, 13 Feb 2019 11:14:40 +0100 Subject: [PATCH 14/20] fix rebase fallout --- src/librustc_mir/interpret/intrinsics.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/librustc_mir/interpret/intrinsics.rs b/src/librustc_mir/interpret/intrinsics.rs index 6466dd4098acd..e002c3fd511d6 100644 --- a/src/librustc_mir/interpret/intrinsics.rs +++ b/src/librustc_mir/interpret/intrinsics.rs @@ -126,7 +126,7 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> let l = self.read_immediate(args[0])?; let r = self.read_immediate(args[1])?; let is_add = intrinsic_name == "saturating_add"; - let (val, overflowed) = self.binary_op_imm(if is_add { + let (val, overflowed) = self.binary_op(if is_add { BinOp::Add } else { BinOp::Sub From e7f8e63ed425cef77a9ad43e463b39009c1a495a Mon Sep 17 00:00:00 2001 From: Lzu Tao Date: Wed, 23 Jan 2019 03:55:37 +0000 Subject: [PATCH 15/20] Convert old doc links to current edition Use footnote style to bypass the tidy check --- src/doc/guide-error-handling.md | 2 +- src/doc/guide-ownership.md | 2 +- src/doc/guide-pointers.md | 2 +- src/doc/guide-testing.md | 2 +- src/libcore/convert.rs | 5 +-- src/libcore/marker.rs | 4 +- src/libcore/mem.rs | 2 +- src/libproc_macro/lib.rs | 4 +- src/librustc/diagnostics.rs | 9 ++-- src/librustc_metadata/diagnostics.rs | 2 +- src/librustc_mir/diagnostics.rs | 14 +++--- src/librustc_typeck/diagnostics.rs | 55 +++++++++++++++--------- src/librustc_typeck/structured_errors.rs | 2 +- src/libstd/prelude/mod.rs | 8 ++-- src/libtest/lib.rs | 2 +- 15 files changed, 65 insertions(+), 50 deletions(-) diff --git a/src/doc/guide-error-handling.md b/src/doc/guide-error-handling.md index 54fa529f3aa8e..fd71d3e3c8e79 100644 --- a/src/doc/guide-error-handling.md +++ b/src/doc/guide-error-handling.md @@ -1,4 +1,4 @@ % Error Handling in Rust This content has moved into -[the Rust Programming Language book](book/error-handling.html). +[the Rust Programming Language book](book/ch09-00-error-handling.html). diff --git a/src/doc/guide-ownership.md b/src/doc/guide-ownership.md index 884f14726ca87..767dafc5baf92 100644 --- a/src/doc/guide-ownership.md +++ b/src/doc/guide-ownership.md @@ -1,4 +1,4 @@ % The (old) Rust Ownership Guide This content has moved into -[the Rust Programming Language book](book/ownership.html). +[the Rust Programming Language book](book/ch04-00-understanding-ownership.html). diff --git a/src/doc/guide-pointers.md b/src/doc/guide-pointers.md index dc80ec4399131..bafdb2fe0bbc3 100644 --- a/src/doc/guide-pointers.md +++ b/src/doc/guide-pointers.md @@ -2,6 +2,6 @@ This content has been removed, with no direct replacement. Rust only has two built-in pointer types now, -[references](book/references-and-borrowing.html) and [raw +[references](book/ch04-02-references-and-borrowing.html) and [raw pointers](book/raw-pointers.html). Older Rusts had many more pointer types, they’re gone now. diff --git a/src/doc/guide-testing.md b/src/doc/guide-testing.md index 67bcb0a5e546a..28d9fb48b73e7 100644 --- a/src/doc/guide-testing.md +++ b/src/doc/guide-testing.md @@ -1,4 +1,4 @@ % The (old) Rust Testing Guide This content has moved into -[the Rust Programming Language book](book/testing.html). +[the Rust Programming Language book](book/ch11-00-testing.html). diff --git a/src/libcore/convert.rs b/src/libcore/convert.rs index 203be541e492f..b24a442131e2b 100644 --- a/src/libcore/convert.rs +++ b/src/libcore/convert.rs @@ -113,9 +113,6 @@ pub const fn identity(x: T) -> T { x } /// - Use `Borrow` when the goal is related to writing code that is agnostic to /// the type of borrow and whether it is a reference or value /// -/// See [the book][book] for a more detailed comparison. -/// -/// [book]: ../../book/first-edition/borrow-and-asref.html /// [`Borrow`]: ../../std/borrow/trait.Borrow.html /// /// **Note: this trait must not fail**. If the conversion can fail, use a @@ -348,7 +345,7 @@ pub trait Into: Sized { /// [`String`]: ../../std/string/struct.String.html /// [`Into`]: trait.Into.html /// [`from`]: trait.From.html#tymethod.from -/// [book]: ../../book/first-edition/error-handling.html +/// [book]: ../../book/ch09-00-error-handling.html #[stable(feature = "rust1", since = "1.0.0")] pub trait From: Sized { /// Performs the conversion. diff --git a/src/libcore/marker.rs b/src/libcore/marker.rs index 65752ba032104..a92131ce84fff 100644 --- a/src/libcore/marker.rs +++ b/src/libcore/marker.rs @@ -78,7 +78,7 @@ impl !Send for *mut T { } /// // be made into an object /// ``` /// -/// [trait object]: ../../book/first-edition/trait-objects.html +/// [trait object]: ../../book/ch17-02-trait-objects.html #[stable(feature = "rust1", since = "1.0.0")] #[lang = "sized"] #[rustc_on_unimplemented( @@ -518,7 +518,7 @@ macro_rules! impls{ /// types. We track the Rust type using a phantom type parameter on /// the struct `ExternalResource` which wraps a handle. /// -/// [FFI]: ../../book/first-edition/ffi.html +/// [FFI]: ../../book/ch19-01-unsafe-rust.html#using-extern-functions-to-call-external-code /// /// ``` /// # #![allow(dead_code)] diff --git a/src/libcore/mem.rs b/src/libcore/mem.rs index 8fcbb73d9ce46..810a1f2fb4a14 100644 --- a/src/libcore/mem.rs +++ b/src/libcore/mem.rs @@ -298,7 +298,7 @@ pub const fn size_of() -> usize { /// then `size_of_val` can be used to get the dynamically-known size. /// /// [slice]: ../../std/primitive.slice.html -/// [trait object]: ../../book/first-edition/trait-objects.html +/// [trait object]: ../../book/ch17-02-trait-objects.html /// /// # Examples /// diff --git a/src/libproc_macro/lib.rs b/src/libproc_macro/lib.rs index 868190d01057d..5533dc2c4b100 100644 --- a/src/libproc_macro/lib.rs +++ b/src/libproc_macro/lib.rs @@ -5,7 +5,9 @@ //! function-like macros `#[proc_macro]`, macro attributes `#[proc_macro_attribute]` and //! custom derive attributes`#[proc_macro_derive]`. //! -//! See [the book](../book/first-edition/procedural-macros.html) for more. +//! See [the book] for more. +//! +//! [the book]: ../book/ch19-06-macros.html#procedural-macros-for-generating-code-from-attributes #![stable(feature = "proc_macro_lib", since = "1.15.0")] #![deny(missing_docs)] diff --git a/src/librustc/diagnostics.rs b/src/librustc/diagnostics.rs index 287a45a21eadf..de647771f9e75 100644 --- a/src/librustc/diagnostics.rs +++ b/src/librustc/diagnostics.rs @@ -1,3 +1,4 @@ +// ignore-tidy-linelength #![allow(non_snake_case)] // Error messages for EXXXX errors. @@ -406,7 +407,7 @@ fn baz<'a>(x: &'a str, y: &str) -> &str { } Lifetime elision in implementation headers was part of the lifetime elision RFC. It is, however, [currently unimplemented][iss15872]. -[book-le]: https://doc.rust-lang.org/nightly/book/first-edition/lifetimes.html#lifetime-elision +[book-le]: https://doc.rust-lang.org/book/ch10-03-lifetime-syntax.html#lifetime-elision [iss15872]: https://github.com/rust-lang/rust/issues/15872 "##, @@ -642,7 +643,9 @@ attributes: #![no_std] ``` -See also https://doc.rust-lang.org/book/first-edition/no-stdlib.html +See also the [unstable book][1]. + +[1]: https://doc.rust-lang.org/unstable-book/language-features/lang-items.html#writing-an-executable-without-stdlib "##, E0214: r##" @@ -1680,7 +1683,7 @@ fn main() { ``` To understand better how closures work in Rust, read: -https://doc.rust-lang.org/book/first-edition/closures.html +https://doc.rust-lang.org/book/ch13-01-closures.html "##, E0580: r##" diff --git a/src/librustc_metadata/diagnostics.rs b/src/librustc_metadata/diagnostics.rs index 1b1852434740c..a38601d11f1c1 100644 --- a/src/librustc_metadata/diagnostics.rs +++ b/src/librustc_metadata/diagnostics.rs @@ -35,7 +35,7 @@ extern {} ``` See more: -https://doc.rust-lang.org/book/first-edition/conditional-compilation.html +https://doc.rust-lang.org/reference/attributes.html#conditional-compilation "##, E0458: r##" diff --git a/src/librustc_mir/diagnostics.rs b/src/librustc_mir/diagnostics.rs index ea9e19c75c215..7f793134a9258 100644 --- a/src/librustc_mir/diagnostics.rs +++ b/src/librustc_mir/diagnostics.rs @@ -690,7 +690,7 @@ fn main() { } ``` -See also https://doc.rust-lang.org/book/first-edition/unsafe.html +See also https://doc.rust-lang.org/book/ch19-01-unsafe-rust.html "##, E0373: r##" @@ -873,7 +873,7 @@ that at most one writer or multiple readers can access the data at any one time. If you wish to learn more about ownership in Rust, start with the chapter in the Book: -https://doc.rust-lang.org/book/first-edition/ownership.html +https://doc.rust-lang.org/book/ch04-00-understanding-ownership.html "##, E0383: r##" @@ -1207,7 +1207,7 @@ let mut a = &mut i; Please note that in rust, you can either have many immutable references, or one mutable reference. Take a look at -https://doc.rust-lang.org/stable/book/references-and-borrowing.html for more +https://doc.rust-lang.org/book/ch04-02-references-and-borrowing.html for more information. Example: @@ -1374,7 +1374,7 @@ fn foo(a: &mut i32) { ``` For more information on the rust ownership system, take a look at -https://doc.rust-lang.org/stable/book/references-and-borrowing.html. +https://doc.rust-lang.org/book/ch04-02-references-and-borrowing.html. "##, E0503: r##" @@ -1430,7 +1430,7 @@ fn main() { ``` You can find more information about borrowing in the rust-book: -http://doc.rust-lang.org/stable/book/references-and-borrowing.html +http://doc.rust-lang.org/book/ch04-02-references-and-borrowing.html "##, E0504: r##" @@ -1614,7 +1614,7 @@ fn main() { ``` You can find more information about borrowing in the rust-book: -http://doc.rust-lang.org/stable/book/references-and-borrowing.html +http://doc.rust-lang.org/book/ch04-02-references-and-borrowing.html "##, E0506: r##" @@ -1825,7 +1825,7 @@ mem::replace(&mut borrowed.knight, TheDarkKnight).nothing_is_true(); // ok! ``` You can find more information about borrowing in the rust-book: -http://doc.rust-lang.org/book/first-edition/references-and-borrowing.html +http://doc.rust-lang.org/book/ch04-02-references-and-borrowing.html "##, E0508: r##" diff --git a/src/librustc_typeck/diagnostics.rs b/src/librustc_typeck/diagnostics.rs index c0a8dd87ee2d5..0a7a523ca9e15 100644 --- a/src/librustc_typeck/diagnostics.rs +++ b/src/librustc_typeck/diagnostics.rs @@ -1,3 +1,4 @@ +// ignore-tidy-linelength #![allow(non_snake_case)] register_long_diagnostics! { @@ -1543,7 +1544,9 @@ fn f() {} It is not possible to declare type parameters on a function that has the `start` attribute. Such a function must have the following type signature (for more -information: http://doc.rust-lang.org/stable/book/first-edition/no-stdlib.html): +information, view [the unstable book][1]): + +[1]: https://doc.rust-lang.org/unstable-book/language-features/lang-items.html#writing-an-executable-without-stdlib ``` # let _: @@ -2917,10 +2920,11 @@ impl Baz for Bar { } // Note: This is OK E0374: r##" A struct without a field containing an unsized type cannot implement -`CoerceUnsized`. An -[unsized type](https://doc.rust-lang.org/book/first-edition/unsized-types.html) -is any type that the compiler doesn't know the length or alignment of at -compile time. Any struct containing an unsized type is also unsized. +`CoerceUnsized`. An [unsized type][1] is any type that the compiler +doesn't know the length or alignment of at compile time. Any struct +containing an unsized type is also unsized. + +[1]: https://doc.rust-lang.org/book/ch19-04-advanced-types.html#dynamically-sized-types-and-the-sized-trait Example of erroneous code: @@ -2977,9 +2981,9 @@ A struct with more than one field containing an unsized type cannot implement `CoerceUnsized`. This only occurs when you are trying to coerce one of the types in your struct to another type in the struct. In this case we try to impl `CoerceUnsized` from `T` to `U` which are both types that the struct -takes. An [unsized type] is any type that the compiler doesn't know the length -or alignment of at compile time. Any struct containing an unsized type is also -unsized. +takes. An [unsized type][1] is any type that the compiler doesn't know the +length or alignment of at compile time. Any struct containing an unsized type +is also unsized. Example of erroneous code: @@ -3024,7 +3028,7 @@ fn coerce_foo, U>(t: T) -> Foo { } ``` -[unsized type]: https://doc.rust-lang.org/book/first-edition/unsized-types.html +[1]: https://doc.rust-lang.org/book/ch19-04-advanced-types.html#dynamically-sized-types-and-the-sized-trait "##, E0376: r##" @@ -3032,11 +3036,12 @@ The type you are trying to impl `CoerceUnsized` for is not a struct. `CoerceUnsized` can only be implemented for a struct. Unsized types are already able to be coerced without an implementation of `CoerceUnsized` whereas a struct containing an unsized type needs to know the unsized type -field it's containing is able to be coerced. An -[unsized type](https://doc.rust-lang.org/book/first-edition/unsized-types.html) +field it's containing is able to be coerced. An [unsized type][1] is any type that the compiler doesn't know the length or alignment of at compile time. Any struct containing an unsized type is also unsized. +[1]: https://doc.rust-lang.org/book/ch19-04-advanced-types.html#dynamically-sized-types-and-the-sized-trait + Example of erroneous code: ```compile_fail,E0376 @@ -3882,8 +3887,10 @@ let c = 86u8 as char; // ok! assert_eq!(c, 'V'); ``` -For more information about casts, take a look at The Book: -https://doc.rust-lang.org/book/first-edition/casting-between-types.html +For more information about casts, take a look at the Type cast section in +[The Reference Book][1]. + +[1]: https://doc.rust-lang.org/reference/expressions/operator-expr.html#type-cast-expressions "##, E0605: r##" @@ -3911,8 +3918,10 @@ let v = 0 as *const u8; v as *const i8; // ok! ``` -For more information about casts, take a look at The Book: -https://doc.rust-lang.org/book/first-edition/casting-between-types.html +For more information about casts, take a look at the Type cast section in +[The Reference Book][1]. + +[1]: https://doc.rust-lang.org/reference/expressions/operator-expr.html#type-cast-expressions "##, E0606: r##" @@ -3933,8 +3942,10 @@ let x = &0u8; let y: u32 = *x as u32; // We dereference it first and then cast it. ``` -For more information about casts, take a look at The Book: -https://doc.rust-lang.org/book/first-edition/casting-between-types.html +For more information about casts, take a look at the Type cast section in +[The Reference Book][1]. + +[1]: https://doc.rust-lang.org/reference/expressions/operator-expr.html#type-cast-expressions "##, E0607: r##" @@ -3960,8 +3971,10 @@ pointer holds is their size. To fix this error, don't try to cast directly between thin and fat pointers. -For more information about casts, take a look at The Book: -https://doc.rust-lang.org/book/first-edition/casting-between-types.html +For more information about casts, take a look at the Type cast section in +[The Reference Book][1]. + +[1]: https://doc.rust-lang.org/reference/expressions/operator-expr.html#type-cast-expressions "##, E0609: r##" @@ -4019,8 +4032,8 @@ println!("x: {}, y: {}", variable.x, variable.y); ``` For more information about primitives and structs, take a look at The Book: -https://doc.rust-lang.org/book/first-edition/primitive-types.html -https://doc.rust-lang.org/book/first-edition/structs.html +https://doc.rust-lang.org/book/ch03-02-data-types.html +https://doc.rust-lang.org/book/ch05-00-structs.html "##, E0614: r##" diff --git a/src/librustc_typeck/structured_errors.rs b/src/librustc_typeck/structured_errors.rs index 7e1ef8e021350..dea13c0e83611 100644 --- a/src/librustc_typeck/structured_errors.rs +++ b/src/librustc_typeck/structured_errors.rs @@ -137,7 +137,7 @@ To fix this error, don't try to cast directly between thin and fat pointers. For more information about casts, take a look at The Book: -https://doc.rust-lang.org/book/first-edition/casting-between-types.html"); +https://doc.rust-lang.org/reference/expressions/operator-expr.html#type-cast-expressions"); err } } diff --git a/src/libstd/prelude/mod.rs b/src/libstd/prelude/mod.rs index bf689bad559d3..551e982a3c685 100644 --- a/src/libstd/prelude/mod.rs +++ b/src/libstd/prelude/mod.rs @@ -129,10 +129,10 @@ //! [`std::string`]: ../string/index.html //! [`std::vec`]: ../vec/index.html //! [`to_owned`]: ../borrow/trait.ToOwned.html#tymethod.to_owned -//! [book-closures]: ../../book/first-edition/closures.html -//! [book-dtor]: ../../book/first-edition/drop.html -//! [book-enums]: ../../book/first-edition/enums.html -//! [book-iter]: ../../book/first-edition/iterators.html +//! [book-closures]: ../../book/ch13-01-closures.html +//! [book-dtor]: ../../book/ch15-03-drop.html +//! [book-enums]: ../../book/ch06-01-defining-an-enum.html +//! [book-iter]: ../../book/ch13-02-iterators.html #![stable(feature = "rust1", since = "1.0.0")] diff --git a/src/libtest/lib.rs b/src/libtest/lib.rs index 2cc80ddea2df4..d93f8b495d565 100644 --- a/src/libtest/lib.rs +++ b/src/libtest/lib.rs @@ -6,7 +6,7 @@ //! benchmarks themselves) should be done via the `#[test]` and //! `#[bench]` attributes. //! -//! See the [Testing Chapter](../book/first-edition/testing.html) of the book for more details. +//! See the [Testing Chapter](../book/ch11-00-testing.html) of the book for more details. // Currently, not much of this is meant for users. It is intended to // support the simplest interface possible for representing and From 285d4a7eb67cdf3ebba4b8fe60a5c5a6abf3edfc Mon Sep 17 00:00:00 2001 From: Dan Robertson Date: Wed, 13 Feb 2019 17:52:22 +0000 Subject: [PATCH 16/20] suggestion-diagnostics: as_ref improve snippet Improve the code snippet suggested in suggestion-diagnostics when suggesting the use of as_ref. --- src/librustc_typeck/check/demand.rs | 15 ++++++++++----- src/test/ui/suggestions/as-ref.stderr | 16 ++++++++-------- 2 files changed, 18 insertions(+), 13 deletions(-) diff --git a/src/librustc_typeck/check/demand.rs b/src/librustc_typeck/check/demand.rs index 33e93b582e540..bb9fc872b85d2 100644 --- a/src/librustc_typeck/check/demand.rs +++ b/src/librustc_typeck/check/demand.rs @@ -210,7 +210,10 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { /// ``` /// opt.map(|arg| { takes_ref(arg) }); /// ``` - fn can_use_as_ref(&self, expr: &hir::Expr) -> Option<(Span, &'static str, String)> { + fn can_use_as_ref( + &self, + expr: &hir::Expr, + ) -> Option<(Span, &'static str, String)> { if let hir::ExprKind::Path(hir::QPath::Resolved(_, ref path)) = expr.node { if let hir::def::Def::Local(id) = path.def { let parent = self.tcx.hir().get_parent_node(id); @@ -233,10 +236,12 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { self_ty.starts_with("std::option::Option") || self_ty.starts_with("std::result::Result") ) && (name == "map" || name == "and_then"); - if is_as_ref_able { - return Some((span.shrink_to_lo(), - "consider using `as_ref` instead", - "as_ref().".into())); + match (is_as_ref_able, self.sess().source_map().span_to_snippet(*span)) { + (true, Ok(src)) => { + return Some((*span, "consider using `as_ref` instead", + format!("as_ref().{}", src))); + }, + _ => () } } } diff --git a/src/test/ui/suggestions/as-ref.stderr b/src/test/ui/suggestions/as-ref.stderr index 7273496a7ce06..8143acc957b4c 100644 --- a/src/test/ui/suggestions/as-ref.stderr +++ b/src/test/ui/suggestions/as-ref.stderr @@ -2,9 +2,9 @@ error[E0308]: mismatched types --> $DIR/as-ref.rs:6:27 | LL | opt.map(|arg| takes_ref(arg)); - | - ^^^ expected &Foo, found struct `Foo` + | --- ^^^ expected &Foo, found struct `Foo` | | - | help: consider using `as_ref` instead: `as_ref().` + | help: consider using `as_ref` instead: `as_ref().map` | = note: expected type `&Foo` found type `Foo` @@ -13,9 +13,9 @@ error[E0308]: mismatched types --> $DIR/as-ref.rs:8:37 | LL | opt.and_then(|arg| Some(takes_ref(arg))); - | - ^^^ expected &Foo, found struct `Foo` + | -------- ^^^ expected &Foo, found struct `Foo` | | - | help: consider using `as_ref` instead: `as_ref().` + | help: consider using `as_ref` instead: `as_ref().and_then` | = note: expected type `&Foo` found type `Foo` @@ -24,9 +24,9 @@ error[E0308]: mismatched types --> $DIR/as-ref.rs:11:27 | LL | opt.map(|arg| takes_ref(arg)); - | - ^^^ expected &Foo, found struct `Foo` + | --- ^^^ expected &Foo, found struct `Foo` | | - | help: consider using `as_ref` instead: `as_ref().` + | help: consider using `as_ref` instead: `as_ref().map` | = note: expected type `&Foo` found type `Foo` @@ -35,9 +35,9 @@ error[E0308]: mismatched types --> $DIR/as-ref.rs:13:35 | LL | opt.and_then(|arg| Ok(takes_ref(arg))); - | - ^^^ expected &Foo, found struct `Foo` + | -------- ^^^ expected &Foo, found struct `Foo` | | - | help: consider using `as_ref` instead: `as_ref().` + | help: consider using `as_ref` instead: `as_ref().and_then` | = note: expected type `&Foo` found type `Foo` From 168c14a1a59de279534c09a1b4e23b3b7f8f774d Mon Sep 17 00:00:00 2001 From: Matthew Jasper Date: Sun, 10 Feb 2019 09:53:52 +0000 Subject: [PATCH 17/20] Avoid propagating redundant outlives constraints from closures --- .../borrow_check/nll/region_infer/mod.rs | 142 ++++++++++++------ 1 file changed, 93 insertions(+), 49 deletions(-) diff --git a/src/librustc_mir/borrow_check/nll/region_infer/mod.rs b/src/librustc_mir/borrow_check/nll/region_infer/mod.rs index 6de05777fe888..7a0cd2ac26c3b 100644 --- a/src/librustc_mir/borrow_check/nll/region_infer/mod.rs +++ b/src/librustc_mir/borrow_check/nll/region_infer/mod.rs @@ -15,7 +15,7 @@ use rustc::mir::{ ConstraintCategory, Local, Location, Mir, }; use rustc::ty::{self, RegionVid, Ty, TyCtxt, TypeFoldable}; -use rustc::util::common; +use rustc::util::common::{self, ErrorReported}; use rustc_data_structures::bit_set::BitSet; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_data_structures::graph::scc::Sccs; @@ -1157,63 +1157,107 @@ impl<'tcx> RegionInferenceContext<'tcx> { .is_none() ); + // Only check all of the relations for the main representative of each + // SCC, otherwise just check that we outlive said representative. This + // reduces the number of redundant relations propagated out of + // closures. + // Note that the representative will be a universal region if there is + // one in this SCC, so we will always check the representative here. + let representative = self.scc_representatives[longer_fr_scc]; + if representative != longer_fr { + self.check_universal_region_relation( + longer_fr, + representative, + infcx, + mir, + mir_def_id, + propagated_outlives_requirements, + errors_buffer, + ); + return; + } + // Find every region `o` such that `fr: o` // (because `fr` includes `end(o)`). for shorter_fr in self.scc_values.universal_regions_outlived_by(longer_fr_scc) { - // If it is known that `fr: o`, carry on. - if self.universal_region_relations - .outlives(longer_fr, shorter_fr) - { - continue; + if let Some(ErrorReported) = self.check_universal_region_relation( + longer_fr, + shorter_fr, + infcx, + mir, + mir_def_id, + propagated_outlives_requirements, + errors_buffer, + ) { + // continuing to iterate just reports more errors than necessary + return; } + } + } - debug!( - "check_universal_region: fr={:?} does not outlive shorter_fr={:?}", - longer_fr, shorter_fr, - ); + fn check_universal_region_relation( + &self, + longer_fr: RegionVid, + shorter_fr: RegionVid, + infcx: &InferCtxt<'_, 'gcx, 'tcx>, + mir: &Mir<'tcx>, + mir_def_id: DefId, + propagated_outlives_requirements: &mut Option<&mut Vec>>, + errors_buffer: &mut Vec, + ) -> Option { + // If it is known that `fr: o`, carry on. + if self.universal_region_relations + .outlives(longer_fr, shorter_fr) + { + return None; + } - let blame_span_category = self.find_outlives_blame_span(mir, longer_fr, shorter_fr); - - if let Some(propagated_outlives_requirements) = propagated_outlives_requirements { - // Shrink `fr` until we find a non-local region (if we do). - // We'll call that `fr-` -- it's ever so slightly smaller than `fr`. - if let Some(fr_minus) = self.universal_region_relations - .non_local_lower_bound(longer_fr) - { - debug!("check_universal_region: fr_minus={:?}", fr_minus); - - // Grow `shorter_fr` until we find a non-local - // region. (We always will.) We'll call that - // `shorter_fr+` -- it's ever so slightly larger than - // `fr`. - let shorter_fr_plus = self.universal_region_relations - .non_local_upper_bound(shorter_fr); - debug!( - "check_universal_region: shorter_fr_plus={:?}", - shorter_fr_plus - ); + debug!( + "check_universal_region_relation: fr={:?} does not outlive shorter_fr={:?}", + longer_fr, shorter_fr, + ); - // Push the constraint `fr-: shorter_fr+` - propagated_outlives_requirements.push(ClosureOutlivesRequirement { - subject: ClosureOutlivesSubject::Region(fr_minus), - outlived_free_region: shorter_fr_plus, - blame_span: blame_span_category.1, - category: blame_span_category.0, - }); - continue; - } - } - // If we are not in a context where we can propagate - // errors, or we could not shrink `fr` to something - // smaller, then just report an error. - // - // Note: in this case, we use the unapproximated regions - // to report the error. This gives better error messages - // in some cases. - self.report_error(mir, infcx, mir_def_id, longer_fr, shorter_fr, errors_buffer); - return; // continuing to iterate just reports more errors than necessary + if let Some(propagated_outlives_requirements) = propagated_outlives_requirements { + // Shrink `fr` until we find a non-local region (if we do). + // We'll call that `fr-` -- it's ever so slightly smaller than `fr`. + if let Some(fr_minus) = self.universal_region_relations + .non_local_lower_bound(longer_fr) + { + debug!("check_universal_region: fr_minus={:?}", fr_minus); + + let blame_span_category = self.find_outlives_blame_span(mir, longer_fr, shorter_fr); + + // Grow `shorter_fr` until we find a non-local + // region. (We always will.) We'll call that + // `shorter_fr+` -- it's ever so slightly larger than + // `fr`. + let shorter_fr_plus = self.universal_region_relations + .non_local_upper_bound(shorter_fr); + debug!( + "check_universal_region: shorter_fr_plus={:?}", + shorter_fr_plus + ); + + // Push the constraint `fr-: shorter_fr+` + propagated_outlives_requirements.push(ClosureOutlivesRequirement { + subject: ClosureOutlivesSubject::Region(fr_minus), + outlived_free_region: shorter_fr_plus, + blame_span: blame_span_category.1, + category: blame_span_category.0, + }); + return None; + } } + + // If we are not in a context where we can't propagate errors, or we + // could not shrink `fr` to something smaller, then just report an + // error. + // + // Note: in this case, we use the unapproximated regions to report the + // error. This gives better error messages in some cases. + self.report_error(mir, infcx, mir_def_id, longer_fr, shorter_fr, errors_buffer); + Some(ErrorReported) } fn check_bound_universal_region<'gcx>( From ea613f30d9728e9bc283ec213225ea8bdaa18f7c Mon Sep 17 00:00:00 2001 From: Matthew Jasper Date: Wed, 6 Feb 2019 14:11:09 +0100 Subject: [PATCH 18/20] Buffer and migrate nice region errors --- .../error_reporting/nice_region_error/mod.rs | 6 +++-- .../nice_region_error/named_anon_conflict.rs | 27 ++++++++++--------- .../nice_region_error/placeholder_error.rs | 8 +++--- .../nll/region_infer/error_reporting/mod.rs | 3 ++- 4 files changed, 23 insertions(+), 21 deletions(-) diff --git a/src/librustc/infer/error_reporting/nice_region_error/mod.rs b/src/librustc/infer/error_reporting/nice_region_error/mod.rs index dad1e3ba80da3..d995fe92337c4 100644 --- a/src/librustc/infer/error_reporting/nice_region_error/mod.rs +++ b/src/librustc/infer/error_reporting/nice_region_error/mod.rs @@ -1,9 +1,10 @@ use crate::infer::InferCtxt; use crate::infer::lexical_region_resolve::RegionResolutionError; use crate::infer::lexical_region_resolve::RegionResolutionError::*; -use syntax::source_map::Span; use crate::ty::{self, TyCtxt}; use crate::util::common::ErrorReported; +use errors::DiagnosticBuilder; +use syntax::source_map::Span; mod different_lifetimes; mod find_anon_type; @@ -59,7 +60,7 @@ impl<'cx, 'gcx, 'tcx> NiceRegionError<'cx, 'gcx, 'tcx> { self.infcx.tcx } - pub fn try_report_from_nll(&self) -> Option { + pub fn try_report_from_nll(&self) -> Option> { // Due to the improved diagnostics returned by the MIR borrow checker, only a subset of // the nice region errors are required when running under the MIR borrow checker. self.try_report_named_anon_conflict() @@ -68,6 +69,7 @@ impl<'cx, 'gcx, 'tcx> NiceRegionError<'cx, 'gcx, 'tcx> { pub fn try_report(&self) -> Option { self.try_report_from_nll() + .map(|mut diag| { diag.emit(); ErrorReported }) .or_else(|| self.try_report_anon_anon_conflict()) .or_else(|| self.try_report_outlives_closure()) .or_else(|| self.try_report_static_impl_trait()) diff --git a/src/librustc/infer/error_reporting/nice_region_error/named_anon_conflict.rs b/src/librustc/infer/error_reporting/nice_region_error/named_anon_conflict.rs index b10af400f2b6c..3821484d38e5f 100644 --- a/src/librustc/infer/error_reporting/nice_region_error/named_anon_conflict.rs +++ b/src/librustc/infer/error_reporting/nice_region_error/named_anon_conflict.rs @@ -2,13 +2,12 @@ //! where one region is named and the other is anonymous. use crate::infer::error_reporting::nice_region_error::NiceRegionError; use crate::ty; -use crate::util::common::ErrorReported; -use errors::Applicability; +use errors::{Applicability, DiagnosticBuilder}; impl<'a, 'gcx, 'tcx> NiceRegionError<'a, 'gcx, 'tcx> { /// When given a `ConcreteFailure` for a function with arguments containing a named region and /// an anonymous region, emit an descriptive diagnostic error. - pub(super) fn try_report_named_anon_conflict(&self) -> Option { + pub(super) fn try_report_named_anon_conflict(&self) -> Option> { let (span, sub, sup) = self.get_regions(); debug!( @@ -96,21 +95,23 @@ impl<'a, 'gcx, 'tcx> NiceRegionError<'a, 'gcx, 'tcx> { ("parameter type".to_owned(), "type".to_owned()) }; - struct_span_err!( + let mut diag = struct_span_err!( self.tcx().sess, span, E0621, "explicit lifetime required in {}", error_var - ).span_suggestion( - new_ty_span, - &format!("add explicit lifetime `{}` to {}", named, span_label_var), - new_ty.to_string(), - Applicability::Unspecified, - ) - .span_label(span, format!("lifetime `{}` required", named)) - .emit(); - return Some(ErrorReported); + ); + + diag.span_suggestion( + new_ty_span, + &format!("add explicit lifetime `{}` to {}", named, span_label_var), + new_ty.to_string(), + Applicability::Unspecified, + ) + .span_label(span, format!("lifetime `{}` required", named)); + + Some(diag) } // This method returns whether the given Region is Named diff --git a/src/librustc/infer/error_reporting/nice_region_error/placeholder_error.rs b/src/librustc/infer/error_reporting/nice_region_error/placeholder_error.rs index 843fa8b0182e2..3b2fb7d41008e 100644 --- a/src/librustc/infer/error_reporting/nice_region_error/placeholder_error.rs +++ b/src/librustc/infer/error_reporting/nice_region_error/placeholder_error.rs @@ -8,13 +8,12 @@ use crate::traits::{ObligationCause, ObligationCauseCode}; use crate::ty; use crate::ty::error::ExpectedFound; use crate::ty::subst::Substs; -use crate::util::common::ErrorReported; use crate::util::ppaux::RegionHighlightMode; impl NiceRegionError<'me, 'gcx, 'tcx> { /// When given a `ConcreteFailure` for a function with arguments containing a named region and /// an anonymous region, emit a descriptive diagnostic error. - pub(super) fn try_report_placeholder_conflict(&self) -> Option { + pub(super) fn try_report_placeholder_conflict(&self) -> Option> { match &self.error { /////////////////////////////////////////////////////////////////////////// // NB. The ordering of cases in this match is very @@ -178,7 +177,7 @@ impl NiceRegionError<'me, 'gcx, 'tcx> { trait_def_id: DefId, expected_substs: &'tcx Substs<'tcx>, actual_substs: &'tcx Substs<'tcx>, - ) -> ErrorReported { + ) -> DiagnosticBuilder<'me> { debug!( "try_report_placeholders_trait(\ vid={:?}, \ @@ -295,8 +294,7 @@ impl NiceRegionError<'me, 'gcx, 'tcx> { any_self_ty_has_vid, ); - err.emit(); - ErrorReported + err } /// Add notes with details about the expected and actual trait refs, with attention to cases diff --git a/src/librustc_mir/borrow_check/nll/region_infer/error_reporting/mod.rs b/src/librustc_mir/borrow_check/nll/region_infer/error_reporting/mod.rs index f741af0b228b5..081c458bfc17a 100644 --- a/src/librustc_mir/borrow_check/nll/region_infer/error_reporting/mod.rs +++ b/src/librustc_mir/borrow_check/nll/region_infer/error_reporting/mod.rs @@ -244,7 +244,8 @@ impl<'tcx> RegionInferenceContext<'tcx> { if let (Some(f), Some(o)) = (self.to_error_region(fr), self.to_error_region(outlived_fr)) { let tables = infcx.tcx.typeck_tables_of(mir_def_id); let nice = NiceRegionError::new_from_span(infcx, span, o, f, Some(tables)); - if let Some(_error_reported) = nice.try_report_from_nll() { + if let Some(diag) = nice.try_report_from_nll() { + diag.buffer(errors_buffer); return; } } From 79e8c311765df4c35558aa9683f1e2004719175a Mon Sep 17 00:00:00 2001 From: Matthew Jasper Date: Wed, 6 Feb 2019 14:13:01 +0100 Subject: [PATCH 19/20] Propagate region constraints more precisely from closures --- .../borrow_check/nll/region_infer/mod.rs | 66 +++++++----- .../nll/type_check/free_region_relations.rs | 102 +++++++++++------- .../issue-58127-mutliple-requirements.rs | 40 +++++++ 3 files changed, 142 insertions(+), 66 deletions(-) create mode 100644 src/test/ui/nll/closure-requirements/issue-58127-mutliple-requirements.rs diff --git a/src/librustc_mir/borrow_check/nll/region_infer/mod.rs b/src/librustc_mir/borrow_check/nll/region_infer/mod.rs index 7a0cd2ac26c3b..cbeb5dc206ee6 100644 --- a/src/librustc_mir/borrow_check/nll/region_infer/mod.rs +++ b/src/librustc_mir/borrow_check/nll/region_infer/mod.rs @@ -763,20 +763,26 @@ impl<'tcx> RegionInferenceContext<'tcx> { debug!("try_promote_type_test: ur={:?}", ur); - let non_local_ub = self.universal_region_relations.non_local_upper_bound(ur); + let non_local_ub = self.universal_region_relations.non_local_upper_bounds(&ur); debug!("try_promote_type_test: non_local_ub={:?}", non_local_ub); - assert!(self.universal_regions.is_universal_region(non_local_ub)); - assert!(!self.universal_regions.is_local_free_region(non_local_ub)); - - let requirement = ClosureOutlivesRequirement { - subject, - outlived_free_region: non_local_ub, - blame_span: locations.span(mir), - category: ConstraintCategory::Boring, - }; - debug!("try_promote_type_test: pushing {:#?}", requirement); - propagated_outlives_requirements.push(requirement); + // This is slightly too conservative. To show T: '1, given `'2: '1` + // and `'3: '1` we only need to prove that T: '2 *or* T: '3, but to + // avoid potential non-determinism we approximate this by requiring + // T: '1 and T: '2. + for &upper_bound in non_local_ub { + debug_assert!(self.universal_regions.is_universal_region(upper_bound)); + debug_assert!(!self.universal_regions.is_local_free_region(upper_bound)); + + let requirement = ClosureOutlivesRequirement { + subject, + outlived_free_region: upper_bound, + blame_span: locations.span(mir), + category: ConstraintCategory::Boring, + }; + debug!("try_promote_type_test: pushing {:#?}", requirement); + propagated_outlives_requirements.push(requirement); + } } true } @@ -1217,35 +1223,37 @@ impl<'tcx> RegionInferenceContext<'tcx> { longer_fr, shorter_fr, ); - if let Some(propagated_outlives_requirements) = propagated_outlives_requirements { - // Shrink `fr` until we find a non-local region (if we do). - // We'll call that `fr-` -- it's ever so slightly smaller than `fr`. - if let Some(fr_minus) = self.universal_region_relations + // Shrink `longer_fr` until we find a non-local region (if we do). + // We'll call it `fr-` -- it's ever so slightly smaller than + // `longer_fr`. + + if let Some(fr_minus) = self + .universal_region_relations .non_local_lower_bound(longer_fr) { debug!("check_universal_region: fr_minus={:?}", fr_minus); let blame_span_category = self.find_outlives_blame_span(mir, longer_fr, shorter_fr); - // Grow `shorter_fr` until we find a non-local - // region. (We always will.) We'll call that - // `shorter_fr+` -- it's ever so slightly larger than - // `fr`. + // Grow `shorter_fr` until we find some non-local regions. (We + // always will.) We'll call them `shorter_fr+` -- they're ever + // so slightly larger than `shorter_fr`. let shorter_fr_plus = self.universal_region_relations - .non_local_upper_bound(shorter_fr); + .non_local_upper_bounds(&shorter_fr); debug!( "check_universal_region: shorter_fr_plus={:?}", shorter_fr_plus ); - - // Push the constraint `fr-: shorter_fr+` - propagated_outlives_requirements.push(ClosureOutlivesRequirement { - subject: ClosureOutlivesSubject::Region(fr_minus), - outlived_free_region: shorter_fr_plus, - blame_span: blame_span_category.1, - category: blame_span_category.0, - }); + for &&fr in &shorter_fr_plus { + // Push the constraint `fr-: shorter_fr+` + propagated_outlives_requirements.push(ClosureOutlivesRequirement { + subject: ClosureOutlivesSubject::Region(fr_minus), + outlived_free_region: fr, + blame_span: blame_span_category.1, + category: blame_span_category.0, + }); + } return None; } } diff --git a/src/librustc_mir/borrow_check/nll/type_check/free_region_relations.rs b/src/librustc_mir/borrow_check/nll/type_check/free_region_relations.rs index 7ddfd55dbbb94..3b663ef6dad61 100644 --- a/src/librustc_mir/borrow_check/nll/type_check/free_region_relations.rs +++ b/src/librustc_mir/borrow_check/nll/type_check/free_region_relations.rs @@ -105,44 +105,89 @@ impl UniversalRegionRelations<'tcx> { /// Finds an "upper bound" for `fr` that is not local. In other /// words, returns the smallest (*) known region `fr1` that (a) - /// outlives `fr` and (b) is not local. This cannot fail, because - /// we will always find `'static` at worst. + /// outlives `fr` and (b) is not local. /// - /// (*) If there are multiple competing choices, we pick the "postdominating" - /// one. See `TransitiveRelation::postdom_upper_bound` for details. - crate fn non_local_upper_bound(&self, fr: RegionVid) -> RegionVid { + /// (*) If there are multiple competing choices, we return all of them. + crate fn non_local_upper_bounds(&'a self, fr: &'a RegionVid) -> Vec<&'a RegionVid> { debug!("non_local_upper_bound(fr={:?})", fr); - self.non_local_bound(&self.inverse_outlives, fr) + let res = self.non_local_bounds(&self.inverse_outlives, fr); + assert!(!res.is_empty(), "can't find an upper bound!?"); + res + } + + /// Returns the "postdominating" bound of the set of + /// `non_local_upper_bounds` for the given region. + crate fn non_local_upper_bound(&self, fr: RegionVid) -> RegionVid { + let upper_bounds = self.non_local_upper_bounds(&fr); + + // In case we find more than one, reduce to one for + // convenience. This is to prevent us from generating more + // complex constraints, but it will cause spurious errors. + let post_dom = self + .inverse_outlives + .mutual_immediate_postdominator(upper_bounds); + + debug!("non_local_bound: post_dom={:?}", post_dom); + + post_dom + .and_then(|&post_dom| { + // If the mutual immediate postdom is not local, then + // there is no non-local result we can return. + if !self.universal_regions.is_local_free_region(post_dom) { + Some(post_dom) + } else { + None + } + }) .unwrap_or(self.universal_regions.fr_static) } + /// Finds a "lower bound" for `fr` that is not local. In other /// words, returns the largest (*) known region `fr1` that (a) is - /// outlived by `fr` and (b) is not local. This cannot fail, - /// because we will always find `'static` at worst. + /// outlived by `fr` and (b) is not local. /// /// (*) If there are multiple competing choices, we pick the "postdominating" /// one. See `TransitiveRelation::postdom_upper_bound` for details. crate fn non_local_lower_bound(&self, fr: RegionVid) -> Option { debug!("non_local_lower_bound(fr={:?})", fr); - self.non_local_bound(&self.outlives, fr) + let lower_bounds = self.non_local_bounds(&self.outlives, &fr); + + // In case we find more than one, reduce to one for + // convenience. This is to prevent us from generating more + // complex constraints, but it will cause spurious errors. + let post_dom = self + .outlives + .mutual_immediate_postdominator(lower_bounds); + + debug!("non_local_bound: post_dom={:?}", post_dom); + + post_dom + .and_then(|&post_dom| { + // If the mutual immediate postdom is not local, then + // there is no non-local result we can return. + if !self.universal_regions.is_local_free_region(post_dom) { + Some(post_dom) + } else { + None + } + }) } - /// Helper for `non_local_upper_bound` and - /// `non_local_lower_bound`. Repeatedly invokes `postdom_parent` - /// until we find something that is not local. Returns `None` if we - /// never do so. - fn non_local_bound( + /// Helper for `non_local_upper_bounds` and `non_local_lower_bounds`. + /// Repeatedly invokes `postdom_parent` until we find something that is not + /// local. Returns `None` if we never do so. + fn non_local_bounds<'a>( &self, - relation: &TransitiveRelation, - fr0: RegionVid, - ) -> Option { + relation: &'a TransitiveRelation, + fr0: &'a RegionVid, + ) -> Vec<&'a RegionVid> { // This method assumes that `fr0` is one of the universally // quantified region variables. - assert!(self.universal_regions.is_universal_region(fr0)); + assert!(self.universal_regions.is_universal_region(*fr0)); let mut external_parents = vec![]; - let mut queue = vec![&fr0]; + let mut queue = vec![fr0]; // Keep expanding `fr` into its parents until we reach // non-local regions. @@ -157,24 +202,7 @@ impl UniversalRegionRelations<'tcx> { debug!("non_local_bound: external_parents={:?}", external_parents); - // In case we find more than one, reduce to one for - // convenience. This is to prevent us from generating more - // complex constraints, but it will cause spurious errors. - let post_dom = relation - .mutual_immediate_postdominator(external_parents) - .cloned(); - - debug!("non_local_bound: post_dom={:?}", post_dom); - - post_dom.and_then(|post_dom| { - // If the mutual immediate postdom is not local, then - // there is no non-local result we can return. - if !self.universal_regions.is_local_free_region(post_dom) { - Some(post_dom) - } else { - None - } - }) + external_parents } /// Returns `true` if fr1 is known to outlive fr2. diff --git a/src/test/ui/nll/closure-requirements/issue-58127-mutliple-requirements.rs b/src/test/ui/nll/closure-requirements/issue-58127-mutliple-requirements.rs new file mode 100644 index 0000000000000..71d5d4053ee25 --- /dev/null +++ b/src/test/ui/nll/closure-requirements/issue-58127-mutliple-requirements.rs @@ -0,0 +1,40 @@ +// revisions: migrate nll +//[migrate]compile-flags: -Z borrowck=migrate +#![cfg_attr(nll, feature(nll))] + +// compile-pass + +// Test that we propagate region relations from closures precisely when there is +// more than one non-local lower bound. + +// In this case the closure has signature +// |x: &'4 mut (&'5 (&'1 str, &'2 str), &'3 str)| -> .. +// We end up with a `'3: '5` constraint that we can propagate as +// `'3: '1`, `'3: '2`, but previously we approximated it as `'3: 'static`. + +// As an optimization, we primarily propagate bounds for the "representative" +// of each SCC. As such we have these two similar cases where hopefully one +// of them will test the case we want (case2, when this test was added). +mod case1 { + fn f(s: &str) { + g(s, |x| h(x)); + } + + fn g(_: T, _: F) + where F: Fn(&mut (&(T, T), T)) {} + + fn h(_: &mut (&(T, T), T)) {} +} + +mod case2 { + fn f(s: &str) { + g(s, |x| h(x)); + } + + fn g(_: T, _: F) + where F: Fn(&mut (T, &(T, T))) {} + + fn h(_: &mut (T, &(T, T))) {} +} + +fn main() {} From 5d65e8cc7aec3cdcd99367c58367a16c99a8ae22 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Thu, 7 Feb 2019 16:03:57 +1100 Subject: [PATCH 20/20] Reduce the size of `hir::Expr`. From 104 bytes to 72 bytes on x86-64. This slightly reduces instruction counts. Also add an assertion about the size. --- src/librustc/hir/lowering.rs | 10 +++++----- src/librustc/hir/mod.rs | 8 ++++++-- src/librustc_save_analysis/lib.rs | 9 +++++++-- src/librustc_typeck/check/demand.rs | 6 +++++- 4 files changed, 23 insertions(+), 10 deletions(-) diff --git a/src/librustc/hir/lowering.rs b/src/librustc/hir/lowering.rs index cc5b105bad0d4..84487c40f8745 100644 --- a/src/librustc/hir/lowering.rs +++ b/src/librustc/hir/lowering.rs @@ -3859,7 +3859,7 @@ impl<'a> LoweringContext<'a> { hir::ExprKind::Call(f, args.iter().map(|x| self.lower_expr(x)).collect()) } ExprKind::MethodCall(ref seg, ref args) => { - let hir_seg = self.lower_path_segment( + let hir_seg = P(self.lower_path_segment( e.span, seg, ParamMode::Optional, @@ -3867,7 +3867,7 @@ impl<'a> LoweringContext<'a> { ParenthesizedGenericArgs::Err, ImplTraitContext::disallowed(), None, - ); + )); let args = args.iter().map(|x| self.lower_expr(x)).collect(); hir::ExprKind::MethodCall(hir_seg, seg.ident.span, args) } @@ -4148,7 +4148,7 @@ impl<'a> LoweringContext<'a> { node: if is_unit { hir::ExprKind::Path(struct_path) } else { - hir::ExprKind::Struct(struct_path, fields, None) + hir::ExprKind::Struct(P(struct_path), fields, None) }, span: e.span, attrs: e.attrs.clone(), @@ -4220,13 +4220,13 @@ impl<'a> LoweringContext<'a> { hir::ExprKind::InlineAsm(P(hir_asm), outputs, inputs) } ExprKind::Struct(ref path, ref fields, ref maybe_expr) => hir::ExprKind::Struct( - self.lower_qpath( + P(self.lower_qpath( e.id, &None, path, ParamMode::Optional, ImplTraitContext::disallowed(), - ), + )), fields.iter().map(|x| self.lower_field(x)).collect(), maybe_expr.as_ref().map(|x| P(self.lower_expr(x))), ), diff --git a/src/librustc/hir/mod.rs b/src/librustc/hir/mod.rs index bf16ec0be83e7..e389b918d3c35 100644 --- a/src/librustc/hir/mod.rs +++ b/src/librustc/hir/mod.rs @@ -1319,6 +1319,10 @@ pub struct Expr { pub hir_id: HirId, } +// `Expr` is used a lot. Make sure it doesn't unintentionally get bigger. +#[cfg(target_arch = "x86_64")] +static_assert!(MEM_SIZE_OF_EXPR: std::mem::size_of::() == 72); + impl Expr { pub fn precedence(&self) -> ExprPrecedence { match self.node { @@ -1438,7 +1442,7 @@ pub enum ExprKind { /// and the remaining elements are the rest of the arguments. /// Thus, `x.foo::(a, b, c, d)` is represented as /// `ExprKind::MethodCall(PathSegment { foo, [Bar, Baz] }, [x, a, b, c, d])`. - MethodCall(PathSegment, Span, HirVec), + MethodCall(P, Span, HirVec), /// A tuple (e.g., `(a, b, c ,d)`). Tup(HirVec), /// A binary operation (e.g., `a + b`, `a * b`). @@ -1506,7 +1510,7 @@ pub enum ExprKind { /// /// For example, `Foo {x: 1, y: 2}`, or /// `Foo {x: 1, .. base}`, where `base` is the `Option`. - Struct(QPath, HirVec, Option>), + Struct(P, HirVec, Option>), /// An array literal constructed from one repeated element. /// diff --git a/src/librustc_save_analysis/lib.rs b/src/librustc_save_analysis/lib.rs index 8d20c44a5a4ef..8ab9a8e8dda86 100644 --- a/src/librustc_save_analysis/lib.rs +++ b/src/librustc_save_analysis/lib.rs @@ -614,11 +614,16 @@ impl<'l, 'tcx: 'l> SaveContext<'l, 'tcx> { Some(def) if def != HirDef::Err => def, _ => self.get_path_def(self.tcx.hir().get_parent_node(id)), } - }, + } + Node::Expr(&hir::Expr { node: hir::ExprKind::Struct(ref qpath, ..), .. - }) | + }) => { + let hir_id = self.tcx.hir().node_to_hir_id(id); + self.tables.qpath_def(qpath, hir_id) + } + Node::Expr(&hir::Expr { node: hir::ExprKind::Path(ref qpath), .. diff --git a/src/librustc_typeck/check/demand.rs b/src/librustc_typeck/check/demand.rs index 33e93b582e540..f6a0fd5caccdb 100644 --- a/src/librustc_typeck/check/demand.rs +++ b/src/librustc_typeck/check/demand.rs @@ -430,7 +430,11 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { match expr.node { // All built-in range literals but `..=` and `..` desugar to Structs - ExprKind::Struct(QPath::Resolved(None, ref path), _, _) | + ExprKind::Struct(ref qpath, _, _) => { + if let QPath::Resolved(None, ref path) = **qpath { + return is_range_path(&path) && span_is_range_literal(&expr.span); + } + } // `..` desugars to its struct path ExprKind::Path(QPath::Resolved(None, ref path)) => { return is_range_path(&path) && span_is_range_literal(&expr.span);