From e983b4f64ee6d919a60938b6e7371a66877f4a23 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Mon, 11 Feb 2019 07:46:04 -0800 Subject: rustc: Implement incremental "fat" LTO Currently the compiler will produce an error if both incremental compilation and full fat LTO is requested. With recent changes and the advent of incremental ThinLTO, however, all the hard work is already done for us and it's actually not too bad to remove this error! This commit updates the codegen backend to allow incremental full fat LTO. The semantics are that the input modules to LTO are all produce incrementally, but the final LTO step is always done unconditionally regardless of whether the inputs changed or not. The only real incremental win we could have here is if zero of the input modules changed, but that's so rare it's unlikely to be worthwhile to implement such a code path. cc #57968 cc rust-lang/cargo#6643 --- src/test/incremental/lto.rs | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 src/test/incremental/lto.rs (limited to 'src/test') diff --git a/src/test/incremental/lto.rs b/src/test/incremental/lto.rs new file mode 100644 index 00000000000..2a3e3c2467c --- /dev/null +++ b/src/test/incremental/lto.rs @@ -0,0 +1,40 @@ +// no-prefer-dynamic +// revisions:rpass1 rpass2 +// compile-flags: -C lto + +mod x { + pub struct X { + x: u32, y: u32, + } + + #[cfg(rpass1)] + fn make() -> X { + X { x: 22, y: 0 } + } + + #[cfg(rpass2)] + fn make() -> X { + X { x: 11, y: 11 } + } + + pub fn new() -> X { + make() + } + + pub fn sum(x: &X) -> u32 { + x.x + x.y + } +} + +mod y { + use x; + + pub fn assert_sum() -> bool { + let x = x::new(); + x::sum(&x) == 22 + } +} + +pub fn main() { + y::assert_sum(); +} -- cgit 1.4.1-3-g733a5 From ee82d09b6c8516b460432ebe5dd718c3353c3084 Mon Sep 17 00:00:00 2001 From: David Wood Date: Mon, 11 Feb 2019 12:18:40 +0100 Subject: Check user type annotations for range patterns. This commit builds on the fix from #58161 (which fixed miscompilation caused by the introduction of `AscribeUserType` patterns for associated constants) to start checking these patterns are well-formed for ranges (previous fix just ignored them so that miscompilation wouldn't occur). --- src/librustc_mir/build/matches/mod.rs | 18 ++-- src/librustc_mir/build/matches/simplify.rs | 10 +- src/librustc_mir/hair/cx/block.rs | 10 +- src/librustc_mir/hair/pattern/_match.rs | 28 +++--- src/librustc_mir/hair/pattern/mod.rs | 145 +++++++++++++++++------------ src/test/ui/nll/issue-58299.rs | 30 ++++++ src/test/ui/nll/issue-58299.stderr | 20 ++++ 7 files changed, 176 insertions(+), 85 deletions(-) create mode 100644 src/test/ui/nll/issue-58299.rs create mode 100644 src/test/ui/nll/issue-58299.stderr (limited to 'src/test') diff --git a/src/librustc_mir/build/matches/mod.rs b/src/librustc_mir/build/matches/mod.rs index cf051ba2e0f..b8c1a1a8c45 100644 --- a/src/librustc_mir/build/matches/mod.rs +++ b/src/librustc_mir/build/matches/mod.rs @@ -7,7 +7,7 @@ use crate::build::scope::{CachedBlock, DropKind}; use crate::build::ForGuard::{self, OutsideGuard, RefWithinGuard, ValWithinGuard}; use crate::build::{BlockAnd, BlockAndExtension, Builder}; use crate::build::{GuardFrame, GuardFrameLocal, LocalsForNode}; -use crate::hair::*; +use crate::hair::{self, *}; use rustc::mir::*; use rustc::ty::{self, CanonicalUserTypeAnnotation, Ty}; use rustc::ty::layout::VariantIdx; @@ -283,9 +283,11 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { }, .. }, - user_ty: pat_ascription_ty, - variance: _, - user_ty_span, + ascription: hair::pattern::Ascription { + user_ty: pat_ascription_ty, + variance: _, + user_ty_span, + }, } => { let place = self.storage_live_binding(block, var, irrefutable_pat.span, OutsideGuard); @@ -560,9 +562,11 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { } PatternKind::AscribeUserType { ref subpattern, - ref user_ty, - user_ty_span, - variance: _, + ascription: hair::pattern::Ascription { + ref user_ty, + user_ty_span, + variance: _, + }, } => { // This corresponds to something like // diff --git a/src/librustc_mir/build/matches/simplify.rs b/src/librustc_mir/build/matches/simplify.rs index 6be9ccb2703..b8e38e40b63 100644 --- a/src/librustc_mir/build/matches/simplify.rs +++ b/src/librustc_mir/build/matches/simplify.rs @@ -14,7 +14,7 @@ use crate::build::Builder; use crate::build::matches::{Ascription, Binding, MatchPair, Candidate}; -use crate::hair::*; +use crate::hair::{self, *}; use rustc::ty; use rustc::ty::layout::{Integer, IntegerExt, Size}; use syntax::attr::{SignedInt, UnsignedInt}; @@ -58,9 +58,11 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { match *match_pair.pattern.kind { PatternKind::AscribeUserType { ref subpattern, - variance, - ref user_ty, - user_ty_span + ascription: hair::pattern::Ascription { + variance, + ref user_ty, + user_ty_span, + }, } => { // Apply the type ascription to the value at `match_pair.place`, which is the // value being matched, taking the variance field into account. diff --git a/src/librustc_mir/hair/cx/block.rs b/src/librustc_mir/hair/cx/block.rs index c24cf956504..7c1dbb449c5 100644 --- a/src/librustc_mir/hair/cx/block.rs +++ b/src/librustc_mir/hair/cx/block.rs @@ -1,4 +1,4 @@ -use crate::hair::*; +use crate::hair::{self, *}; use crate::hair::cx::Cx; use crate::hair::cx::to_ref::ToRef; use rustc::middle::region; @@ -83,10 +83,12 @@ fn mirror_stmts<'a, 'gcx, 'tcx>(cx: &mut Cx<'a, 'gcx, 'tcx>, ty: pattern.ty, span: pattern.span, kind: Box::new(PatternKind::AscribeUserType { - user_ty: PatternTypeProjection::from_user_type(user_ty), - user_ty_span: ty.span, + ascription: hair::pattern::Ascription { + user_ty: PatternTypeProjection::from_user_type(user_ty), + user_ty_span: ty.span, + variance: ty::Variance::Covariant, + }, subpattern: pattern, - variance: ty::Variance::Covariant, }) }; } diff --git a/src/librustc_mir/hair/pattern/_match.rs b/src/librustc_mir/hair/pattern/_match.rs index 5779a032acc..3bdc10bcb06 100644 --- a/src/librustc_mir/hair/pattern/_match.rs +++ b/src/librustc_mir/hair/pattern/_match.rs @@ -871,18 +871,24 @@ impl<'tcx> IntRange<'tcx> { } fn from_pat(tcx: TyCtxt<'_, 'tcx, 'tcx>, - pat: &Pattern<'tcx>) + mut pat: &Pattern<'tcx>) -> Option> { - Self::from_ctor(tcx, &match pat.kind { - box PatternKind::Constant { value } => ConstantValue(value), - box PatternKind::Range(PatternRange { lo, hi, ty, end }) => ConstantRange( - lo.to_bits(tcx, ty::ParamEnv::empty().and(ty)).unwrap(), - hi.to_bits(tcx, ty::ParamEnv::empty().and(ty)).unwrap(), - ty, - end, - ), - _ => return None, - }) + let range = loop { + match pat.kind { + box PatternKind::Constant { value } => break ConstantValue(value), + box PatternKind::Range(PatternRange { lo, hi, ty, end }) => break ConstantRange( + lo.to_bits(tcx, ty::ParamEnv::empty().and(ty)).unwrap(), + hi.to_bits(tcx, ty::ParamEnv::empty().and(ty)).unwrap(), + ty, + end, + ), + box PatternKind::AscribeUserType { ref subpattern, .. } => { + pat = subpattern; + }, + _ => return None, + } + }; + Self::from_ctor(tcx, &range) } // The return value of `signed_bias` should be XORed with an endpoint to encode/decode it. diff --git a/src/librustc_mir/hair/pattern/mod.rs b/src/librustc_mir/hair/pattern/mod.rs index 84d8f32954c..84f1d979b48 100644 --- a/src/librustc_mir/hair/pattern/mod.rs +++ b/src/librustc_mir/hair/pattern/mod.rs @@ -58,7 +58,7 @@ pub struct Pattern<'tcx> { } -#[derive(Clone, Debug)] +#[derive(Copy, Clone, Debug, PartialEq)] pub struct PatternTypeProjection<'tcx> { pub user_ty: CanonicalUserType<'tcx>, } @@ -87,33 +87,38 @@ impl<'tcx> PatternTypeProjection<'tcx> { } } +#[derive(Copy, Clone, Debug, PartialEq)] +pub struct Ascription<'tcx> { + pub user_ty: PatternTypeProjection<'tcx>, + /// Variance to use when relating the type `user_ty` to the **type of the value being + /// matched**. Typically, this is `Variance::Covariant`, since the value being matched must + /// have a type that is some subtype of the ascribed type. + /// + /// Note that this variance does not apply for any bindings within subpatterns. The type + /// assigned to those bindings must be exactly equal to the `user_ty` given here. + /// + /// The only place where this field is not `Covariant` is when matching constants, where + /// we currently use `Contravariant` -- this is because the constant type just needs to + /// be "comparable" to the type of the input value. So, for example: + /// + /// ```text + /// match x { "foo" => .. } + /// ``` + /// + /// requires that `&'static str <: T_x`, where `T_x` is the type of `x`. Really, we should + /// probably be checking for a `PartialEq` impl instead, but this preserves the behavior + /// of the old type-check for now. See #57280 for details. + pub variance: ty::Variance, + pub user_ty_span: Span, +} + #[derive(Clone, Debug)] pub enum PatternKind<'tcx> { Wild, AscribeUserType { - user_ty: PatternTypeProjection<'tcx>, + ascription: Ascription<'tcx>, subpattern: Pattern<'tcx>, - /// Variance to use when relating the type `user_ty` to the **type of the value being - /// matched**. Typically, this is `Variance::Covariant`, since the value being matched must - /// have a type that is some subtype of the ascribed type. - /// - /// Note that this variance does not apply for any bindings within subpatterns. The type - /// assigned to those bindings must be exactly equal to the `user_ty` given here. - /// - /// The only place where this field is not `Covariant` is when matching constants, where - /// we currently use `Contravariant` -- this is because the constant type just needs to - /// be "comparable" to the type of the input value. So, for example: - /// - /// ```text - /// match x { "foo" => .. } - /// ``` - /// - /// requires that `&'static str <: T_x`, where `T_x` is the type of `x`. Really, we should - /// probably be checking for a `PartialEq` impl instead, but this preserves the behavior - /// of the old type-check for now. See #57280 for details. - variance: ty::Variance, - user_ty_span: Span, }, /// x, ref x, x @ P, etc @@ -167,18 +172,7 @@ pub enum PatternKind<'tcx> { }, } -impl<'tcx> PatternKind<'tcx> { - /// If this is a `PatternKind::AscribeUserType` then return the subpattern kind, otherwise - /// return this pattern kind. - fn with_user_type_ascription_subpattern(self) -> Self { - match self { - PatternKind::AscribeUserType { subpattern: Pattern { box kind, .. }, .. } => kind, - kind => kind, - } - } -} - -#[derive(Clone, Copy, Debug, PartialEq)] +#[derive(Copy, Clone, Debug, PartialEq)] pub struct PatternRange<'tcx> { pub lo: ty::Const<'tcx>, pub hi: ty::Const<'tcx>, @@ -405,6 +399,19 @@ impl<'a, 'tcx> PatternContext<'a, 'tcx> { ) } + fn lower_range_expr( + &mut self, + expr: &'tcx hir::Expr, + ) -> (PatternKind<'tcx>, Option>) { + match self.lower_lit(expr) { + PatternKind::AscribeUserType { + ascription: lo_ascription, + subpattern: Pattern { kind: box kind, .. }, + } => (kind, Some(lo_ascription)), + kind => (kind, None), + } + } + fn lower_pattern_unadjusted(&mut self, pat: &'tcx hir::Pat) -> Pattern<'tcx> { let mut ty = self.tables.node_id_to_type(pat.hir_id); @@ -414,14 +421,10 @@ impl<'a, 'tcx> PatternContext<'a, 'tcx> { PatKind::Lit(ref value) => self.lower_lit(value), PatKind::Range(ref lo_expr, ref hi_expr, end) => { - match ( - // Look for `PatternKind::Constant` patterns inside of any - // `PatternKind::AscribeUserType` patterns. Type ascriptions can be safely - // ignored for the purposes of lowering a range correctly - these are checked - // elsewhere for well-formedness. - self.lower_lit(lo_expr).with_user_type_ascription_subpattern(), - self.lower_lit(hi_expr).with_user_type_ascription_subpattern(), - ) { + let (lo, lo_ascription) = self.lower_range_expr(lo_expr); + let (hi, hi_ascription) = self.lower_range_expr(hi_expr); + + let mut kind = match (lo, hi) { (PatternKind::Constant { value: lo }, PatternKind::Constant { value: hi }) => { use std::cmp::Ordering; let cmp = compare_const_vals( @@ -470,17 +473,33 @@ impl<'a, 'tcx> PatternContext<'a, 'tcx> { PatternKind::Wild } } - } + }, ref pats => { self.tcx.sess.delay_span_bug( pat.span, - &format!("found bad range pattern `{:?}` outside of error recovery", - pats), + &format!( + "found bad range pattern `{:?}` outside of error recovery", + pats, + ), ); PatternKind::Wild + }, + }; + + // If we are handling a range with associated constants (e.g. + // `Foo::<'a>::A..=Foo::B`), we need to put the ascriptions for the associated + // constants somewhere. Have them on the range pattern. + for ascription in &[lo_ascription, hi_ascription] { + if let Some(ascription) = ascription { + kind = PatternKind::AscribeUserType { + ascription: *ascription, + subpattern: Pattern { span: pat.span, ty, kind: Box::new(kind), }, + }; } } + + kind } PatKind::Path(ref qpath) => { @@ -756,9 +775,11 @@ impl<'a, 'tcx> PatternContext<'a, 'tcx> { ty, kind: Box::new(kind), }, - user_ty: PatternTypeProjection::from_user_type(user_ty), - user_ty_span: span, - variance: ty::Variance::Covariant, + ascription: Ascription { + user_ty: PatternTypeProjection::from_user_type(user_ty), + user_ty_span: span, + variance: ty::Variance::Covariant, + }, }; } @@ -808,11 +829,13 @@ impl<'a, 'tcx> PatternContext<'a, 'tcx> { kind: Box::new( PatternKind::AscribeUserType { subpattern: pattern, - /// Note that use `Contravariant` here. See the - /// `variance` field documentation for details. - variance: ty::Variance::Contravariant, - user_ty, - user_ty_span: span, + ascription: Ascription { + /// Note that use `Contravariant` here. See the + /// `variance` field documentation for details. + variance: ty::Variance::Contravariant, + user_ty, + user_ty_span: span, + }, } ), ty: value.ty, @@ -1105,14 +1128,18 @@ impl<'tcx> PatternFoldable<'tcx> for PatternKind<'tcx> { PatternKind::Wild => PatternKind::Wild, PatternKind::AscribeUserType { ref subpattern, - variance, - ref user_ty, - user_ty_span, + ascription: Ascription { + variance, + ref user_ty, + user_ty_span, + }, } => PatternKind::AscribeUserType { subpattern: subpattern.fold_with(folder), - user_ty: user_ty.fold_with(folder), - variance, - user_ty_span, + ascription: Ascription { + user_ty: user_ty.fold_with(folder), + variance, + user_ty_span, + }, }, PatternKind::Binding { mutability, diff --git a/src/test/ui/nll/issue-58299.rs b/src/test/ui/nll/issue-58299.rs new file mode 100644 index 00000000000..9267cac5dd3 --- /dev/null +++ b/src/test/ui/nll/issue-58299.rs @@ -0,0 +1,30 @@ +#![allow(dead_code)] +#![feature(nll)] + +struct A<'a>(&'a ()); + +trait Y { + const X: i32; +} + +impl Y for A<'static> { + const X: i32 = 10; +} + +fn foo<'a>(x: i32) { + match x { + // This uses as Y>::X, but `A<'a>` does not implement `Y`. + A::<'a>::X..=A::<'static>::X => (), //~ ERROR lifetime may not live long enough + _ => (), + } +} + +fn bar<'a>(x: i32) { + match x { + // This uses as Y>::X, but `A<'a>` does not implement `Y`. + A::<'static>::X..=A::<'a>::X => (), //~ ERROR lifetime may not live long enough + _ => (), + } +} + +fn main() {} diff --git a/src/test/ui/nll/issue-58299.stderr b/src/test/ui/nll/issue-58299.stderr new file mode 100644 index 00000000000..b87d0de51a3 --- /dev/null +++ b/src/test/ui/nll/issue-58299.stderr @@ -0,0 +1,20 @@ +error: lifetime may not live long enough + --> $DIR/issue-58299.rs:17:9 + | +LL | fn foo<'a>(x: i32) { + | -- lifetime `'a` defined here +... +LL | A::<'a>::X..=A::<'static>::X => (), //~ ERROR lifetime may not live long enough + | ^^^^^^^^^^ requires that `'a` must outlive `'static` + +error: lifetime may not live long enough + --> $DIR/issue-58299.rs:25:27 + | +LL | fn bar<'a>(x: i32) { + | -- lifetime `'a` defined here +... +LL | A::<'static>::X..=A::<'a>::X => (), //~ ERROR lifetime may not live long enough + | ^^^^^^^^^^ requires that `'a` must outlive `'static` + +error: aborting due to 2 previous errors + -- cgit 1.4.1-3-g733a5 From 2f95299f6bf87a7b23381cda954570e72c80c159 Mon Sep 17 00:00:00 2001 From: Andy Russell Date: Tue, 12 Feb 2019 12:35:38 -0500 Subject: specify "upper camel case" in style lint Also, fix an issue where internal upper case letters were converted to lower case. --- src/librustc_lint/Cargo.toml | 1 - src/librustc_lint/nonstandard_style.rs | 148 +++++++++++++-------- src/test/ui/lint/lint-group-nonstandard-style.rs | 2 +- .../ui/lint/lint-group-nonstandard-style.stderr | 6 +- src/test/ui/lint/lint-non-camel-case-types.rs | 24 ++-- src/test/ui/lint/lint-non-camel-case-types.stderr | 66 ++++----- src/test/ui/utf8_idents.rs | 4 +- src/test/ui/utf8_idents.stderr | 14 +- 8 files changed, 139 insertions(+), 126 deletions(-) (limited to 'src/test') diff --git a/src/librustc_lint/Cargo.toml b/src/librustc_lint/Cargo.toml index 82f7118df2d..fd2b635faef 100644 --- a/src/librustc_lint/Cargo.toml +++ b/src/librustc_lint/Cargo.toml @@ -8,7 +8,6 @@ edition = "2018" name = "rustc_lint" path = "lib.rs" crate-type = ["dylib"] -test = false [dependencies] log = "0.4" diff --git a/src/librustc_lint/nonstandard_style.rs b/src/librustc_lint/nonstandard_style.rs index 2dbafc7ede2..c2dd9a3d1b8 100644 --- a/src/librustc_lint/nonstandard_style.rs +++ b/src/librustc_lint/nonstandard_style.rs @@ -38,66 +38,87 @@ declare_lint! { "types, variants, traits and type parameters should have camel case names" } -#[derive(Copy, Clone)] -pub struct NonCamelCaseTypes; +fn char_has_case(c: char) -> bool { + c.is_lowercase() || c.is_uppercase() +} -impl NonCamelCaseTypes { - fn check_case(&self, cx: &EarlyContext<'_>, sort: &str, ident: &Ident) { - fn char_has_case(c: char) -> bool { - c.is_lowercase() || c.is_uppercase() - } +fn is_camel_case(name: &str) -> bool { + let name = name.trim_matches('_'); + if name.is_empty() { + return true; + } - fn is_camel_case(name: &str) -> bool { - let name = name.trim_matches('_'); - if name.is_empty() { - return true; + // start with a non-lowercase letter rather than non-uppercase + // ones (some scripts don't have a concept of upper/lowercase) + !name.chars().next().unwrap().is_lowercase() + && !name.contains("__") + && !name.chars().collect::>().windows(2).any(|pair| { + // contains a capitalisable character followed by, or preceded by, an underscore + char_has_case(pair[0]) && pair[1] == '_' || char_has_case(pair[1]) && pair[0] == '_' + }) +} + +fn to_camel_case(s: &str) -> String { + s.trim_matches('_') + .split('_') + .filter(|component| !component.is_empty()) + .map(|component| { + let mut camel_cased_component = String::new(); + + let mut new_word = true; + let mut prev_is_lower_case = true; + + for c in component.chars() { + // Preserve the case if an uppercase letter follows a lowercase letter, so that + // `camelCase` is converted to `CamelCase`. + if prev_is_lower_case && c.is_uppercase() { + new_word = true; + } + + if new_word { + camel_cased_component.push_str(&c.to_uppercase().to_string()); + } else { + camel_cased_component.push_str(&c.to_lowercase().to_string()); + } + + prev_is_lower_case = c.is_lowercase(); + new_word = false; } - // start with a non-lowercase letter rather than non-uppercase - // ones (some scripts don't have a concept of upper/lowercase) - !name.is_empty() && !name.chars().next().unwrap().is_lowercase() && - !name.contains("__") && !name.chars().collect::>().windows(2).any(|pair| { - // contains a capitalisable character followed by, or preceded by, an underscore - char_has_case(pair[0]) && pair[1] == '_' || - char_has_case(pair[1]) && pair[0] == '_' - }) - } + camel_cased_component + }) + .fold( + (String::new(), None), + |(acc, prev): (String, Option), next| { + // separate two components with an underscore if their boundary cannot + // be distinguished using a uppercase/lowercase case distinction + let join = if let Some(prev) = prev { + let l = prev.chars().last().unwrap(); + let f = next.chars().next().unwrap(); + !char_has_case(l) && !char_has_case(f) + } else { + false + }; + (acc + if join { "_" } else { "" } + &next, Some(next)) + }, + ) + .0 +} - fn to_camel_case(s: &str) -> String { - s.trim_matches('_') - .split('_') - .map(|word| { - word.chars().enumerate().map(|(i, c)| if i == 0 { - c.to_uppercase().collect::() - } else { - c.to_lowercase().collect() - }) - .collect::() - }) - .filter(|x| !x.is_empty()) - .fold((String::new(), None), |(acc, prev): (String, Option), next| { - // separate two components with an underscore if their boundary cannot - // be distinguished using a uppercase/lowercase case distinction - let join = if let Some(prev) = prev { - let l = prev.chars().last().unwrap(); - let f = next.chars().next().unwrap(); - !char_has_case(l) && !char_has_case(f) - } else { false }; - (acc + if join { "_" } else { "" } + &next, Some(next)) - }).0 - } +#[derive(Copy, Clone)] +pub struct NonCamelCaseTypes; +impl NonCamelCaseTypes { + fn check_case(&self, cx: &EarlyContext<'_>, sort: &str, ident: &Ident) { let name = &ident.name.as_str(); if !is_camel_case(name) { - let c = to_camel_case(name); - - let msg = format!("{} `{}` should have a camel case name", sort, name); + let msg = format!("{} `{}` should have an upper camel case name", sort, name); cx.struct_span_lint(NON_CAMEL_CASE_TYPES, ident.span, &msg) .span_suggestion( ident.span, - "convert the identifier to camel case", - c, + "convert the identifier to upper camel case", + to_camel_case(name), Applicability::MaybeIncorrect, ) .emit(); @@ -119,11 +140,7 @@ impl EarlyLintPass for NonCamelCaseTypes { fn check_item(&mut self, cx: &EarlyContext<'_>, it: &ast::Item) { let has_repr_c = it.attrs .iter() - .any(|attr| { - attr::find_repr_attrs(&cx.sess.parse_sess, attr) - .iter() - .any(|r| r == &attr::ReprC) - }); + .any(|attr| attr::find_repr_attrs(&cx.sess.parse_sess, attr).contains(&attr::ReprC)); if has_repr_c { return; @@ -439,3 +456,28 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for NonUpperCaseGlobals { } } } + +#[cfg(test)] +mod tests { + use super::{is_camel_case, to_camel_case}; + + #[test] + fn camel_case() { + assert!(!is_camel_case("userData")); + assert_eq!(to_camel_case("userData"), "UserData"); + + assert!(is_camel_case("X86_64")); + + assert!(!is_camel_case("X86__64")); + assert_eq!(to_camel_case("X86__64"), "X86_64"); + + assert!(!is_camel_case("Abc_123")); + assert_eq!(to_camel_case("Abc_123"), "Abc123"); + + assert!(!is_camel_case("A1_b2_c3")); + assert_eq!(to_camel_case("A1_b2_c3"), "A1B2C3"); + + assert!(!is_camel_case("ONE_TWO_THREE")); + assert_eq!(to_camel_case("ONE_TWO_THREE"), "OneTwoThree"); + } +} diff --git a/src/test/ui/lint/lint-group-nonstandard-style.rs b/src/test/ui/lint/lint-group-nonstandard-style.rs index 0386daaa59b..bd7f327bc0f 100644 --- a/src/test/ui/lint/lint-group-nonstandard-style.rs +++ b/src/test/ui/lint/lint-group-nonstandard-style.rs @@ -19,7 +19,7 @@ mod test { fn CamelCase() {} //~ WARN should have a snake - struct snake_case; //~ WARN should have a camel + struct snake_case; //~ WARN should have an upper camel } } diff --git a/src/test/ui/lint/lint-group-nonstandard-style.stderr b/src/test/ui/lint/lint-group-nonstandard-style.stderr index f3c7d70054b..ab36cda57ec 100644 --- a/src/test/ui/lint/lint-group-nonstandard-style.stderr +++ b/src/test/ui/lint/lint-group-nonstandard-style.stderr @@ -1,8 +1,8 @@ -warning: type `snake_case` should have a camel case name +warning: type `snake_case` should have an upper camel case name --> $DIR/lint-group-nonstandard-style.rs:22:16 | -LL | struct snake_case; //~ WARN should have a camel - | ^^^^^^^^^^ help: convert the identifier to camel case: `SnakeCase` +LL | struct snake_case; //~ WARN should have an upper camel + | ^^^^^^^^^^ help: convert the identifier to upper camel case: `SnakeCase` | note: lint level defined here --> $DIR/lint-group-nonstandard-style.rs:18:17 diff --git a/src/test/ui/lint/lint-non-camel-case-types.rs b/src/test/ui/lint/lint-non-camel-case-types.rs index bca1992605b..d3b119a9441 100644 --- a/src/test/ui/lint/lint-non-camel-case-types.rs +++ b/src/test/ui/lint/lint-non-camel-case-types.rs @@ -2,43 +2,35 @@ #![allow(dead_code)] struct ONE_TWO_THREE; -//~^ ERROR type `ONE_TWO_THREE` should have a camel case name +//~^ ERROR type `ONE_TWO_THREE` should have an upper camel case name -struct foo { //~ ERROR type `foo` should have a camel case name +struct foo { //~ ERROR type `foo` should have an upper camel case name bar: isize, } -enum foo2 { //~ ERROR type `foo2` should have a camel case name +enum foo2 { //~ ERROR type `foo2` should have an upper camel case name Bar } -struct foo3 { //~ ERROR type `foo3` should have a camel case name +struct foo3 { //~ ERROR type `foo3` should have an upper camel case name bar: isize } -type foo4 = isize; //~ ERROR type `foo4` should have a camel case name +type foo4 = isize; //~ ERROR type `foo4` should have an upper camel case name enum Foo5 { - bar //~ ERROR variant `bar` should have a camel case name + bar //~ ERROR variant `bar` should have an upper camel case name } -trait foo6 { //~ ERROR trait `foo6` should have a camel case name +trait foo6 { //~ ERROR trait `foo6` should have an upper camel case name fn dummy(&self) { } } -fn f(_: ty) {} //~ ERROR type parameter `ty` should have a camel case name +fn f(_: ty) {} //~ ERROR type parameter `ty` should have an upper camel case name #[repr(C)] struct foo7 { bar: isize, } -struct X86_64; - -struct X86__64; //~ ERROR type `X86__64` should have a camel case name - -struct Abc_123; //~ ERROR type `Abc_123` should have a camel case name - -struct A1_b2_c3; //~ ERROR type `A1_b2_c3` should have a camel case name - fn main() { } diff --git a/src/test/ui/lint/lint-non-camel-case-types.stderr b/src/test/ui/lint/lint-non-camel-case-types.stderr index 74f9a5993b8..7afacf64d87 100644 --- a/src/test/ui/lint/lint-non-camel-case-types.stderr +++ b/src/test/ui/lint/lint-non-camel-case-types.stderr @@ -1,8 +1,8 @@ -error: type `ONE_TWO_THREE` should have a camel case name +error: type `ONE_TWO_THREE` should have an upper camel case name --> $DIR/lint-non-camel-case-types.rs:4:8 | LL | struct ONE_TWO_THREE; - | ^^^^^^^^^^^^^ help: convert the identifier to camel case: `OneTwoThree` + | ^^^^^^^^^^^^^ help: convert the identifier to upper camel case: `OneTwoThree` | note: lint level defined here --> $DIR/lint-non-camel-case-types.rs:1:11 @@ -10,65 +10,47 @@ note: lint level defined here LL | #![forbid(non_camel_case_types)] | ^^^^^^^^^^^^^^^^^^^^ -error: type `foo` should have a camel case name +error: type `foo` should have an upper camel case name --> $DIR/lint-non-camel-case-types.rs:7:8 | -LL | struct foo { //~ ERROR type `foo` should have a camel case name - | ^^^ help: convert the identifier to camel case: `Foo` +LL | struct foo { //~ ERROR type `foo` should have an upper camel case name + | ^^^ help: convert the identifier to upper camel case: `Foo` -error: type `foo2` should have a camel case name +error: type `foo2` should have an upper camel case name --> $DIR/lint-non-camel-case-types.rs:11:6 | -LL | enum foo2 { //~ ERROR type `foo2` should have a camel case name - | ^^^^ help: convert the identifier to camel case: `Foo2` +LL | enum foo2 { //~ ERROR type `foo2` should have an upper camel case name + | ^^^^ help: convert the identifier to upper camel case: `Foo2` -error: type `foo3` should have a camel case name +error: type `foo3` should have an upper camel case name --> $DIR/lint-non-camel-case-types.rs:15:8 | -LL | struct foo3 { //~ ERROR type `foo3` should have a camel case name - | ^^^^ help: convert the identifier to camel case: `Foo3` +LL | struct foo3 { //~ ERROR type `foo3` should have an upper camel case name + | ^^^^ help: convert the identifier to upper camel case: `Foo3` -error: type `foo4` should have a camel case name +error: type `foo4` should have an upper camel case name --> $DIR/lint-non-camel-case-types.rs:19:6 | -LL | type foo4 = isize; //~ ERROR type `foo4` should have a camel case name - | ^^^^ help: convert the identifier to camel case: `Foo4` +LL | type foo4 = isize; //~ ERROR type `foo4` should have an upper camel case name + | ^^^^ help: convert the identifier to upper camel case: `Foo4` -error: variant `bar` should have a camel case name +error: variant `bar` should have an upper camel case name --> $DIR/lint-non-camel-case-types.rs:22:5 | -LL | bar //~ ERROR variant `bar` should have a camel case name - | ^^^ help: convert the identifier to camel case: `Bar` +LL | bar //~ ERROR variant `bar` should have an upper camel case name + | ^^^ help: convert the identifier to upper camel case: `Bar` -error: trait `foo6` should have a camel case name +error: trait `foo6` should have an upper camel case name --> $DIR/lint-non-camel-case-types.rs:25:7 | -LL | trait foo6 { //~ ERROR trait `foo6` should have a camel case name - | ^^^^ help: convert the identifier to camel case: `Foo6` +LL | trait foo6 { //~ ERROR trait `foo6` should have an upper camel case name + | ^^^^ help: convert the identifier to upper camel case: `Foo6` -error: type parameter `ty` should have a camel case name +error: type parameter `ty` should have an upper camel case name --> $DIR/lint-non-camel-case-types.rs:29:6 | -LL | fn f(_: ty) {} //~ ERROR type parameter `ty` should have a camel case name - | ^^ help: convert the identifier to camel case: `Ty` +LL | fn f(_: ty) {} //~ ERROR type parameter `ty` should have an upper camel case name + | ^^ help: convert the identifier to upper camel case: `Ty` -error: type `X86__64` should have a camel case name - --> $DIR/lint-non-camel-case-types.rs:38:8 - | -LL | struct X86__64; //~ ERROR type `X86__64` should have a camel case name - | ^^^^^^^ help: convert the identifier to camel case: `X86_64` - -error: type `Abc_123` should have a camel case name - --> $DIR/lint-non-camel-case-types.rs:40:8 - | -LL | struct Abc_123; //~ ERROR type `Abc_123` should have a camel case name - | ^^^^^^^ help: convert the identifier to camel case: `Abc123` - -error: type `A1_b2_c3` should have a camel case name - --> $DIR/lint-non-camel-case-types.rs:42:8 - | -LL | struct A1_b2_c3; //~ ERROR type `A1_b2_c3` should have a camel case name - | ^^^^^^^^ help: convert the identifier to camel case: `A1B2C3` - -error: aborting due to 11 previous errors +error: aborting due to 8 previous errors diff --git a/src/test/ui/utf8_idents.rs b/src/test/ui/utf8_idents.rs index e601c6e4555..f59d5502aae 100644 --- a/src/test/ui/utf8_idents.rs +++ b/src/test/ui/utf8_idents.rs @@ -1,9 +1,7 @@ -// - fn foo< 'β, //~ ERROR non-ascii idents are not fully supported γ //~ ERROR non-ascii idents are not fully supported - //~^ WARN type parameter `γ` should have a camel case name + //~^ WARN type parameter `γ` should have an upper camel case name >() {} struct X { diff --git a/src/test/ui/utf8_idents.stderr b/src/test/ui/utf8_idents.stderr index 268dd99d060..52fb607af5b 100644 --- a/src/test/ui/utf8_idents.stderr +++ b/src/test/ui/utf8_idents.stderr @@ -1,5 +1,5 @@ error[E0658]: non-ascii idents are not fully supported. (see issue #55467) - --> $DIR/utf8_idents.rs:4:5 + --> $DIR/utf8_idents.rs:2:5 | LL | 'β, //~ ERROR non-ascii idents are not fully supported | ^^ @@ -7,7 +7,7 @@ LL | 'β, //~ ERROR non-ascii idents are not fully supported = help: add #![feature(non_ascii_idents)] to the crate attributes to enable error[E0658]: non-ascii idents are not fully supported. (see issue #55467) - --> $DIR/utf8_idents.rs:5:5 + --> $DIR/utf8_idents.rs:3:5 | LL | γ //~ ERROR non-ascii idents are not fully supported | ^ @@ -15,7 +15,7 @@ LL | γ //~ ERROR non-ascii idents are not fully supported = help: add #![feature(non_ascii_idents)] to the crate attributes to enable error[E0658]: non-ascii idents are not fully supported. (see issue #55467) - --> $DIR/utf8_idents.rs:10:5 + --> $DIR/utf8_idents.rs:8:5 | LL | δ: usize //~ ERROR non-ascii idents are not fully supported | ^ @@ -23,18 +23,18 @@ LL | δ: usize //~ ERROR non-ascii idents are not fully supported = help: add #![feature(non_ascii_idents)] to the crate attributes to enable error[E0658]: non-ascii idents are not fully supported. (see issue #55467) - --> $DIR/utf8_idents.rs:14:9 + --> $DIR/utf8_idents.rs:12:9 | LL | let α = 0.00001f64; //~ ERROR non-ascii idents are not fully supported | ^ | = help: add #![feature(non_ascii_idents)] to the crate attributes to enable -warning: type parameter `γ` should have a camel case name - --> $DIR/utf8_idents.rs:5:5 +warning: type parameter `γ` should have an upper camel case name + --> $DIR/utf8_idents.rs:3:5 | LL | γ //~ ERROR non-ascii idents are not fully supported - | ^ help: convert the identifier to camel case: `Γ` + | ^ help: convert the identifier to upper camel case: `Γ` | = note: #[warn(non_camel_case_types)] on by default -- cgit 1.4.1-3-g733a5 From 79e8c311765df4c35558aa9683f1e2004719175a Mon Sep 17 00:00:00 2001 From: Matthew Jasper Date: Wed, 6 Feb 2019 14:13:01 +0100 Subject: 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 (limited to 'src/test') 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 7a0cd2ac26c..cbeb5dc206e 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 7ddfd55dbbb..3b663ef6dad 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 00000000000..71d5d4053ee --- /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() {} -- cgit 1.4.1-3-g733a5