From d4fe9553f65df51a18999e956fd507e26271e74e Mon Sep 17 00:00:00 2001 From: mibac138 <5672750+mibac138@users.noreply.github.com> Date: Thu, 7 May 2020 03:57:31 +0200 Subject: [PATCH 001/123] Implement partial error recovery for `let` with `BinOpEq` When parsing `let x: i8 += 1` the compiler interprets `i8` as a trait which makes it more complicated to do error recovery. More advanced error recovery is not implemented in this commit. --- src/librustc_parse/parser/stmt.rs | 29 ++++++++++++++++++++++-- src/test/ui/parser/let-binop-plus.rs | 7 ++++++ src/test/ui/parser/let-binop-plus.stderr | 9 ++++++++ src/test/ui/parser/let-binop.rs | 8 +++++++ src/test/ui/parser/let-binop.stderr | 21 +++++++++++++++++ 5 files changed, 72 insertions(+), 2 deletions(-) create mode 100644 src/test/ui/parser/let-binop-plus.rs create mode 100644 src/test/ui/parser/let-binop-plus.stderr create mode 100644 src/test/ui/parser/let-binop.rs create mode 100644 src/test/ui/parser/let-binop.stderr diff --git a/src/librustc_parse/parser/stmt.rs b/src/librustc_parse/parser/stmt.rs index 849193151c33..049aa7447f4d 100644 --- a/src/librustc_parse/parser/stmt.rs +++ b/src/librustc_parse/parser/stmt.rs @@ -12,7 +12,7 @@ use rustc_ast::ast::{Block, BlockCheckMode, Expr, ExprKind, Local, Stmt, StmtKin use rustc_ast::ptr::P; use rustc_ast::token::{self, TokenKind}; use rustc_ast::util::classify; -use rustc_errors::{Applicability, PResult}; +use rustc_errors::{struct_span_err, Applicability, PResult}; use rustc_span::source_map::{BytePos, Span}; use rustc_span::symbol::{kw, sym}; @@ -217,7 +217,32 @@ impl<'a> Parser<'a> { /// Parses the RHS of a local variable declaration (e.g., '= 14;'). fn parse_initializer(&mut self, skip_eq: bool) -> PResult<'a, Option>> { - if self.eat(&token::Eq) || skip_eq { Ok(Some(self.parse_expr()?)) } else { Ok(None) } + let parse = if !self.eat(&token::Eq) && !skip_eq { + // Error recovery for `let x += 1` + if matches!(self.token.kind, TokenKind::BinOpEq(_)) { + struct_span_err!( + self.sess.span_diagnostic, + self.token.span, + E0067, + "can't reassign to a uninitialized variable" + ) + .span_suggestion_short( + self.token.span, + "replace with `=` to initialize the variable", + "=".to_string(), + Applicability::MaybeIncorrect, + ) + .emit(); + self.bump(); + true + } else { + false + } + } else { + true + }; + + if parse { Ok(Some(self.parse_expr()?)) } else { Ok(None) } } /// Parses a block. No inner attributes are allowed. diff --git a/src/test/ui/parser/let-binop-plus.rs b/src/test/ui/parser/let-binop-plus.rs new file mode 100644 index 000000000000..8d883d6e2489 --- /dev/null +++ b/src/test/ui/parser/let-binop-plus.rs @@ -0,0 +1,7 @@ +#![allow(bare_trait_objects)] + +fn main() { + let a: i8 += 1; + //~^ ERROR expected trait, found builtin type `i8` + let _ = a; +} diff --git a/src/test/ui/parser/let-binop-plus.stderr b/src/test/ui/parser/let-binop-plus.stderr new file mode 100644 index 000000000000..baa935aff713 --- /dev/null +++ b/src/test/ui/parser/let-binop-plus.stderr @@ -0,0 +1,9 @@ +error[E0404]: expected trait, found builtin type `i8` + --> $DIR/let-binop-plus.rs:4:12 + | +LL | let a: i8 += 1; + | ^^ not a trait + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0404`. diff --git a/src/test/ui/parser/let-binop.rs b/src/test/ui/parser/let-binop.rs new file mode 100644 index 000000000000..d445ab6bb8a1 --- /dev/null +++ b/src/test/ui/parser/let-binop.rs @@ -0,0 +1,8 @@ +fn main() { + let a: i8 *= 1; //~ ERROR can't reassign to a uninitialized variable + let _ = a; + let b += 1; //~ ERROR can't reassign to a uninitialized variable + let _ = b; + let c *= 1; //~ ERROR can't reassign to a uninitialized variable + let _ = c; +} diff --git a/src/test/ui/parser/let-binop.stderr b/src/test/ui/parser/let-binop.stderr new file mode 100644 index 000000000000..3e9d4a80a70e --- /dev/null +++ b/src/test/ui/parser/let-binop.stderr @@ -0,0 +1,21 @@ +error[E0067]: can't reassign to a uninitialized variable + --> $DIR/let-binop.rs:2:15 + | +LL | let a: i8 *= 1; + | ^^ help: replace with `=` to initialize the variable + +error[E0067]: can't reassign to a uninitialized variable + --> $DIR/let-binop.rs:4:11 + | +LL | let b += 1; + | ^^ help: replace with `=` to initialize the variable + +error[E0067]: can't reassign to a uninitialized variable + --> $DIR/let-binop.rs:6:11 + | +LL | let c *= 1; + | ^^ help: replace with `=` to initialize the variable + +error: aborting due to 3 previous errors + +For more information about this error, try `rustc --explain E0067`. From 48ff12acb184672393692e087927a66ff7907d71 Mon Sep 17 00:00:00 2001 From: mibac138 <5672750+mibac138@users.noreply.github.com> Date: Thu, 7 May 2020 04:09:57 +0200 Subject: [PATCH 002/123] Expand partial error recovery for `let` with `BinOpEq` --- src/librustc_parse/parser/stmt.rs | 40 +++++++++++++++++++++-------- src/test/ui/parser/let-binop.stderr | 22 ++++++++++++++-- 2 files changed, 50 insertions(+), 12 deletions(-) diff --git a/src/librustc_parse/parser/stmt.rs b/src/librustc_parse/parser/stmt.rs index 049aa7447f4d..aceee8143289 100644 --- a/src/librustc_parse/parser/stmt.rs +++ b/src/librustc_parse/parser/stmt.rs @@ -145,12 +145,12 @@ impl<'a> Parser<'a> { } fn parse_local_mk(&mut self, lo: Span, attrs: AttrVec) -> PResult<'a, Stmt> { - let local = self.parse_local(attrs)?; + let local = self.parse_local(lo, attrs)?; Ok(self.mk_stmt(lo.to(self.prev_token.span), StmtKind::Local(local))) } /// Parses a local variable declaration. - fn parse_local(&mut self, attrs: AttrVec) -> PResult<'a, P> { + fn parse_local(&mut self, let_span: Span, attrs: AttrVec) -> PResult<'a, P> { let lo = self.prev_token.span; let pat = self.parse_top_pat(GateOr::Yes)?; @@ -174,7 +174,7 @@ impl<'a> Parser<'a> { } else { (None, None) }; - let init = match (self.parse_initializer(err.is_some()), err) { + let init = match (self.parse_initializer(let_span, ty.is_some(), err.is_some()), err) { (Ok(init), None) => { // init parsed, ty parsed init @@ -216,23 +216,43 @@ impl<'a> Parser<'a> { } /// Parses the RHS of a local variable declaration (e.g., '= 14;'). - fn parse_initializer(&mut self, skip_eq: bool) -> PResult<'a, Option>> { + fn parse_initializer( + &mut self, + let_span: Span, + has_ty: bool, + skip_eq: bool, + ) -> PResult<'a, Option>> { let parse = if !self.eat(&token::Eq) && !skip_eq { // Error recovery for `let x += 1` if matches!(self.token.kind, TokenKind::BinOpEq(_)) { - struct_span_err!( + let mut err = struct_span_err!( self.sess.span_diagnostic, self.token.span, E0067, "can't reassign to a uninitialized variable" - ) - .span_suggestion_short( + ); + err.span_suggestion_short( self.token.span, "replace with `=` to initialize the variable", "=".to_string(), - Applicability::MaybeIncorrect, - ) - .emit(); + if has_ty { + // for `let x: i8 += 1` it's highly likely that the `+` is a typo + Applicability::MachineApplicable + } else { + // for `let x += 1` it's a bit less likely that the `+` is a typo + Applicability::MaybeIncorrect + }, + ); + // In case of code like `let x += 1` it's possible the user may have meant to write `x += 1` + if !has_ty { + err.span_suggestion_short( + let_span, + "remove to reassign to a previously initialized variable", + "".to_string(), + Applicability::MaybeIncorrect, + ); + } + err.emit(); self.bump(); true } else { diff --git a/src/test/ui/parser/let-binop.stderr b/src/test/ui/parser/let-binop.stderr index 3e9d4a80a70e..c37612430cef 100644 --- a/src/test/ui/parser/let-binop.stderr +++ b/src/test/ui/parser/let-binop.stderr @@ -8,13 +8,31 @@ error[E0067]: can't reassign to a uninitialized variable --> $DIR/let-binop.rs:4:11 | LL | let b += 1; - | ^^ help: replace with `=` to initialize the variable + | ^^ + | +help: replace with `=` to initialize the variable + | +LL | let b = 1; + | ^ +help: remove to reassign to a previously initialized variable + | +LL | b += 1; + | -- error[E0067]: can't reassign to a uninitialized variable --> $DIR/let-binop.rs:6:11 | LL | let c *= 1; - | ^^ help: replace with `=` to initialize the variable + | ^^ + | +help: replace with `=` to initialize the variable + | +LL | let c = 1; + | ^ +help: remove to reassign to a previously initialized variable + | +LL | c *= 1; + | -- error: aborting due to 3 previous errors From 05d653199871955eba90abdd3b176603f030ab60 Mon Sep 17 00:00:00 2001 From: mibac138 <5672750+mibac138@users.noreply.github.com> Date: Thu, 7 May 2020 05:00:59 +0200 Subject: [PATCH 003/123] Error recovery for `let` with `+=` --- src/librustc_parse/parser/stmt.rs | 65 ++++++++++++------------ src/test/ui/parser/let-binop-plus.rs | 1 + src/test/ui/parser/let-binop-plus.stderr | 11 +++- 3 files changed, 42 insertions(+), 35 deletions(-) diff --git a/src/librustc_parse/parser/stmt.rs b/src/librustc_parse/parser/stmt.rs index aceee8143289..224f4ebf5382 100644 --- a/src/librustc_parse/parser/stmt.rs +++ b/src/librustc_parse/parser/stmt.rs @@ -222,44 +222,43 @@ impl<'a> Parser<'a> { has_ty: bool, skip_eq: bool, ) -> PResult<'a, Option>> { - let parse = if !self.eat(&token::Eq) && !skip_eq { + // In case of code like `let x: i8 += 1`, `i8` is interpreted as a trait consuming the `+` + // from `+=`. + let ate_plus = self.prev_token.is_like_plus() && has_ty; + let parse = if !skip_eq && (ate_plus || matches!(self.token.kind, TokenKind::BinOpEq(_))) { // Error recovery for `let x += 1` - if matches!(self.token.kind, TokenKind::BinOpEq(_)) { - let mut err = struct_span_err!( - self.sess.span_diagnostic, - self.token.span, - E0067, - "can't reassign to a uninitialized variable" - ); + let mut err = struct_span_err!( + self.sess.span_diagnostic, + self.token.span, + E0067, + "can't reassign to a uninitialized variable" + ); + err.span_suggestion_short( + self.token.span, + "replace with `=` to initialize the variable", + "=".to_string(), + if has_ty { + // for `let x: i8 += 1` it's highly likely that the `+` is a typo + Applicability::MachineApplicable + } else { + // for `let x += 1` it's a bit less likely that the `+` is a typo + Applicability::MaybeIncorrect + }, + ); + // In case of code like `let x += 1` it's possible the user may have meant to write `x += 1` + if !has_ty { err.span_suggestion_short( - self.token.span, - "replace with `=` to initialize the variable", - "=".to_string(), - if has_ty { - // for `let x: i8 += 1` it's highly likely that the `+` is a typo - Applicability::MachineApplicable - } else { - // for `let x += 1` it's a bit less likely that the `+` is a typo - Applicability::MaybeIncorrect - }, + let_span, + "remove to reassign to a previously initialized variable", + "".to_string(), + Applicability::MaybeIncorrect, ); - // In case of code like `let x += 1` it's possible the user may have meant to write `x += 1` - if !has_ty { - err.span_suggestion_short( - let_span, - "remove to reassign to a previously initialized variable", - "".to_string(), - Applicability::MaybeIncorrect, - ); - } - err.emit(); - self.bump(); - true - } else { - false } - } else { + err.emit(); + self.bump(); true + } else { + self.eat(&token::Eq) || skip_eq }; if parse { Ok(Some(self.parse_expr()?)) } else { Ok(None) } diff --git a/src/test/ui/parser/let-binop-plus.rs b/src/test/ui/parser/let-binop-plus.rs index 8d883d6e2489..98473e9f096d 100644 --- a/src/test/ui/parser/let-binop-plus.rs +++ b/src/test/ui/parser/let-binop-plus.rs @@ -3,5 +3,6 @@ fn main() { let a: i8 += 1; //~^ ERROR expected trait, found builtin type `i8` + //~| ERROR can't reassign to a uninitialized variable let _ = a; } diff --git a/src/test/ui/parser/let-binop-plus.stderr b/src/test/ui/parser/let-binop-plus.stderr index baa935aff713..d7d84ff16a0a 100644 --- a/src/test/ui/parser/let-binop-plus.stderr +++ b/src/test/ui/parser/let-binop-plus.stderr @@ -1,9 +1,16 @@ +error[E0067]: can't reassign to a uninitialized variable + --> $DIR/let-binop-plus.rs:4:16 + | +LL | let a: i8 += 1; + | ^ help: replace with `=` to initialize the variable + error[E0404]: expected trait, found builtin type `i8` --> $DIR/let-binop-plus.rs:4:12 | LL | let a: i8 += 1; | ^^ not a trait -error: aborting due to previous error +error: aborting due to 2 previous errors -For more information about this error, try `rustc --explain E0404`. +Some errors have detailed explanations: E0067, E0404. +For more information about an error, try `rustc --explain E0067`. From 6ad24baf06c687517f188e8c6e6ce848924d001c Mon Sep 17 00:00:00 2001 From: mibac138 <5672750+mibac138@users.noreply.github.com> Date: Thu, 7 May 2020 23:45:51 +0200 Subject: [PATCH 004/123] Adjust according to estebank's review comments --- src/librustc_parse/parser/stmt.rs | 19 ++++++++----------- src/test/ui/parser/let-binop-plus.rs | 2 +- src/test/ui/parser/let-binop-plus.stderr | 4 ++-- src/test/ui/parser/let-binop.rs | 6 +++--- src/test/ui/parser/let-binop.stderr | 20 ++++++++++---------- 5 files changed, 24 insertions(+), 27 deletions(-) diff --git a/src/librustc_parse/parser/stmt.rs b/src/librustc_parse/parser/stmt.rs index 224f4ebf5382..bec810fde081 100644 --- a/src/librustc_parse/parser/stmt.rs +++ b/src/librustc_parse/parser/stmt.rs @@ -174,7 +174,10 @@ impl<'a> Parser<'a> { } else { (None, None) }; - let init = match (self.parse_initializer(let_span, ty.is_some(), err.is_some()), err) { + let init = match ( + self.parse_initializer(let_span.until(pat.span), ty.is_some(), err.is_some()), + err, + ) { (Ok(init), None) => { // init parsed, ty parsed init @@ -231,25 +234,19 @@ impl<'a> Parser<'a> { self.sess.span_diagnostic, self.token.span, E0067, - "can't reassign to a uninitialized variable" + "can't reassign to an uninitialized variable" ); err.span_suggestion_short( self.token.span, - "replace with `=` to initialize the variable", + "initialize the variable", "=".to_string(), - if has_ty { - // for `let x: i8 += 1` it's highly likely that the `+` is a typo - Applicability::MachineApplicable - } else { - // for `let x += 1` it's a bit less likely that the `+` is a typo - Applicability::MaybeIncorrect - }, + Applicability::MaybeIncorrect, ); // In case of code like `let x += 1` it's possible the user may have meant to write `x += 1` if !has_ty { err.span_suggestion_short( let_span, - "remove to reassign to a previously initialized variable", + "otherwise, reassign to a previously initialized variable", "".to_string(), Applicability::MaybeIncorrect, ); diff --git a/src/test/ui/parser/let-binop-plus.rs b/src/test/ui/parser/let-binop-plus.rs index 98473e9f096d..4d6d9b5c8d37 100644 --- a/src/test/ui/parser/let-binop-plus.rs +++ b/src/test/ui/parser/let-binop-plus.rs @@ -3,6 +3,6 @@ fn main() { let a: i8 += 1; //~^ ERROR expected trait, found builtin type `i8` - //~| ERROR can't reassign to a uninitialized variable + //~| ERROR can't reassign to an uninitialized variable let _ = a; } diff --git a/src/test/ui/parser/let-binop-plus.stderr b/src/test/ui/parser/let-binop-plus.stderr index d7d84ff16a0a..91a59fe24fed 100644 --- a/src/test/ui/parser/let-binop-plus.stderr +++ b/src/test/ui/parser/let-binop-plus.stderr @@ -1,8 +1,8 @@ -error[E0067]: can't reassign to a uninitialized variable +error[E0067]: can't reassign to an uninitialized variable --> $DIR/let-binop-plus.rs:4:16 | LL | let a: i8 += 1; - | ^ help: replace with `=` to initialize the variable + | ^ help: initialize the variable error[E0404]: expected trait, found builtin type `i8` --> $DIR/let-binop-plus.rs:4:12 diff --git a/src/test/ui/parser/let-binop.rs b/src/test/ui/parser/let-binop.rs index d445ab6bb8a1..7f58f5df2d41 100644 --- a/src/test/ui/parser/let-binop.rs +++ b/src/test/ui/parser/let-binop.rs @@ -1,8 +1,8 @@ fn main() { - let a: i8 *= 1; //~ ERROR can't reassign to a uninitialized variable + let a: i8 *= 1; //~ ERROR can't reassign to an uninitialized variable let _ = a; - let b += 1; //~ ERROR can't reassign to a uninitialized variable + let b += 1; //~ ERROR can't reassign to an uninitialized variable let _ = b; - let c *= 1; //~ ERROR can't reassign to a uninitialized variable + let c *= 1; //~ ERROR can't reassign to an uninitialized variable let _ = c; } diff --git a/src/test/ui/parser/let-binop.stderr b/src/test/ui/parser/let-binop.stderr index c37612430cef..8a90b7cf74a4 100644 --- a/src/test/ui/parser/let-binop.stderr +++ b/src/test/ui/parser/let-binop.stderr @@ -1,37 +1,37 @@ -error[E0067]: can't reassign to a uninitialized variable +error[E0067]: can't reassign to an uninitialized variable --> $DIR/let-binop.rs:2:15 | LL | let a: i8 *= 1; - | ^^ help: replace with `=` to initialize the variable + | ^^ help: initialize the variable -error[E0067]: can't reassign to a uninitialized variable +error[E0067]: can't reassign to an uninitialized variable --> $DIR/let-binop.rs:4:11 | LL | let b += 1; | ^^ | -help: replace with `=` to initialize the variable +help: initialize the variable | LL | let b = 1; | ^ -help: remove to reassign to a previously initialized variable +help: otherwise, reassign to a previously initialized variable | -LL | b += 1; +LL | b += 1; | -- -error[E0067]: can't reassign to a uninitialized variable +error[E0067]: can't reassign to an uninitialized variable --> $DIR/let-binop.rs:6:11 | LL | let c *= 1; | ^^ | -help: replace with `=` to initialize the variable +help: initialize the variable | LL | let c = 1; | ^ -help: remove to reassign to a previously initialized variable +help: otherwise, reassign to a previously initialized variable | -LL | c *= 1; +LL | c *= 1; | -- error: aborting due to 3 previous errors From 98532a30901d7544c49fe82f499db53699645de0 Mon Sep 17 00:00:00 2001 From: mibac138 <5672750+mibac138@users.noreply.github.com> Date: Wed, 20 May 2020 22:09:03 +0200 Subject: [PATCH 005/123] Adjust according to petrochenkov's review comments --- src/librustc_parse/parser/stmt.rs | 65 ++++++++---------------- src/test/ui/parser/let-binop-plus.rs | 8 --- src/test/ui/parser/let-binop-plus.stderr | 16 ------ src/test/ui/parser/let-binop.stderr | 29 ++--------- 4 files changed, 27 insertions(+), 91 deletions(-) delete mode 100644 src/test/ui/parser/let-binop-plus.rs delete mode 100644 src/test/ui/parser/let-binop-plus.stderr diff --git a/src/librustc_parse/parser/stmt.rs b/src/librustc_parse/parser/stmt.rs index bec810fde081..53f32b7c800b 100644 --- a/src/librustc_parse/parser/stmt.rs +++ b/src/librustc_parse/parser/stmt.rs @@ -12,7 +12,7 @@ use rustc_ast::ast::{Block, BlockCheckMode, Expr, ExprKind, Local, Stmt, StmtKin use rustc_ast::ptr::P; use rustc_ast::token::{self, TokenKind}; use rustc_ast::util::classify; -use rustc_errors::{struct_span_err, Applicability, PResult}; +use rustc_errors::{Applicability, PResult}; use rustc_span::source_map::{BytePos, Span}; use rustc_span::symbol::{kw, sym}; @@ -145,12 +145,12 @@ impl<'a> Parser<'a> { } fn parse_local_mk(&mut self, lo: Span, attrs: AttrVec) -> PResult<'a, Stmt> { - let local = self.parse_local(lo, attrs)?; + let local = self.parse_local(attrs)?; Ok(self.mk_stmt(lo.to(self.prev_token.span), StmtKind::Local(local))) } /// Parses a local variable declaration. - fn parse_local(&mut self, let_span: Span, attrs: AttrVec) -> PResult<'a, P> { + fn parse_local(&mut self, attrs: AttrVec) -> PResult<'a, P> { let lo = self.prev_token.span; let pat = self.parse_top_pat(GateOr::Yes)?; @@ -174,10 +174,7 @@ impl<'a> Parser<'a> { } else { (None, None) }; - let init = match ( - self.parse_initializer(let_span.until(pat.span), ty.is_some(), err.is_some()), - err, - ) { + let init = match (self.parse_initializer(err.is_some()), err) { (Ok(init), None) => { // init parsed, ty parsed init @@ -219,46 +216,28 @@ impl<'a> Parser<'a> { } /// Parses the RHS of a local variable declaration (e.g., '= 14;'). - fn parse_initializer( - &mut self, - let_span: Span, - has_ty: bool, - skip_eq: bool, - ) -> PResult<'a, Option>> { - // In case of code like `let x: i8 += 1`, `i8` is interpreted as a trait consuming the `+` - // from `+=`. - let ate_plus = self.prev_token.is_like_plus() && has_ty; - let parse = if !skip_eq && (ate_plus || matches!(self.token.kind, TokenKind::BinOpEq(_))) { - // Error recovery for `let x += 1` - let mut err = struct_span_err!( - self.sess.span_diagnostic, - self.token.span, - E0067, - "can't reassign to an uninitialized variable" - ); - err.span_suggestion_short( - self.token.span, - "initialize the variable", - "=".to_string(), - Applicability::MaybeIncorrect, - ); - // In case of code like `let x += 1` it's possible the user may have meant to write `x += 1` - if !has_ty { - err.span_suggestion_short( - let_span, - "otherwise, reassign to a previously initialized variable", - "".to_string(), + fn parse_initializer(&mut self, eq_optional: bool) -> PResult<'a, Option>> { + let eq_consumed = match self.token.kind { + token::BinOpEq(..) => { + // Recover `let x = 1` as `let x = 1` + self.struct_span_err( + self.token.span, + "can't reassign to an uninitialized variable", + ) + .span_suggestion_short( + self.token.span, + "initialize the variable", + "=".to_string(), Applicability::MaybeIncorrect, - ); + ) + .emit(); + self.bump(); + true } - err.emit(); - self.bump(); - true - } else { - self.eat(&token::Eq) || skip_eq + _ => self.eat(&token::Eq), }; - if parse { Ok(Some(self.parse_expr()?)) } else { Ok(None) } + Ok(if eq_consumed || eq_optional { Some(self.parse_expr()?) } else { None }) } /// Parses a block. No inner attributes are allowed. diff --git a/src/test/ui/parser/let-binop-plus.rs b/src/test/ui/parser/let-binop-plus.rs deleted file mode 100644 index 4d6d9b5c8d37..000000000000 --- a/src/test/ui/parser/let-binop-plus.rs +++ /dev/null @@ -1,8 +0,0 @@ -#![allow(bare_trait_objects)] - -fn main() { - let a: i8 += 1; - //~^ ERROR expected trait, found builtin type `i8` - //~| ERROR can't reassign to an uninitialized variable - let _ = a; -} diff --git a/src/test/ui/parser/let-binop-plus.stderr b/src/test/ui/parser/let-binop-plus.stderr deleted file mode 100644 index 91a59fe24fed..000000000000 --- a/src/test/ui/parser/let-binop-plus.stderr +++ /dev/null @@ -1,16 +0,0 @@ -error[E0067]: can't reassign to an uninitialized variable - --> $DIR/let-binop-plus.rs:4:16 - | -LL | let a: i8 += 1; - | ^ help: initialize the variable - -error[E0404]: expected trait, found builtin type `i8` - --> $DIR/let-binop-plus.rs:4:12 - | -LL | let a: i8 += 1; - | ^^ not a trait - -error: aborting due to 2 previous errors - -Some errors have detailed explanations: E0067, E0404. -For more information about an error, try `rustc --explain E0067`. diff --git a/src/test/ui/parser/let-binop.stderr b/src/test/ui/parser/let-binop.stderr index 8a90b7cf74a4..71431499ac70 100644 --- a/src/test/ui/parser/let-binop.stderr +++ b/src/test/ui/parser/let-binop.stderr @@ -1,39 +1,20 @@ -error[E0067]: can't reassign to an uninitialized variable +error: can't reassign to an uninitialized variable --> $DIR/let-binop.rs:2:15 | LL | let a: i8 *= 1; | ^^ help: initialize the variable -error[E0067]: can't reassign to an uninitialized variable +error: can't reassign to an uninitialized variable --> $DIR/let-binop.rs:4:11 | LL | let b += 1; - | ^^ - | -help: initialize the variable - | -LL | let b = 1; - | ^ -help: otherwise, reassign to a previously initialized variable - | -LL | b += 1; - | -- + | ^^ help: initialize the variable -error[E0067]: can't reassign to an uninitialized variable +error: can't reassign to an uninitialized variable --> $DIR/let-binop.rs:6:11 | LL | let c *= 1; - | ^^ - | -help: initialize the variable - | -LL | let c = 1; - | ^ -help: otherwise, reassign to a previously initialized variable - | -LL | c *= 1; - | -- + | ^^ help: initialize the variable error: aborting due to 3 previous errors -For more information about this error, try `rustc --explain E0067`. From 730f7366bba5abdf5ae0c2f1222795e40d48f90c Mon Sep 17 00:00:00 2001 From: Benjamin Saunders Date: Fri, 22 May 2020 20:49:20 -0700 Subject: [PATCH 006/123] Fix asinh of negative values When `x` has large magnitude, `x + ((x * x) + 1.0).sqrt()` approaches `x + x.abs()`. For negative values of `x`, this leads to catastrophic cancellation, resulting in large errors or even 0 being passed to `ln`, producing incorrect results including `-inf`. Becuase asinh is an odd function, i.e. -asinh(x) = asinh(-x) for all x, we can avoid the catastrophic cancellation and obtain correct results by taking the absolute value of `self` for the first term. `self * self` is always positive, so in effect this gives us `x.abs().asinh().copysign(x)` which as discussed above is algebraically equivalent, but is much more accurate. --- src/libstd/f32.rs | 4 +++- src/libstd/f64.rs | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/libstd/f32.rs b/src/libstd/f32.rs index 8e743ace99bf..ab468f42b3bd 100644 --- a/src/libstd/f32.rs +++ b/src/libstd/f32.rs @@ -835,7 +835,7 @@ impl f32 { if self == Self::NEG_INFINITY { Self::NEG_INFINITY } else { - (self + ((self * self) + 1.0).sqrt()).ln().copysign(self) + (self.abs() + ((self * self) + 1.0).sqrt()).ln().copysign(self) } } @@ -1414,6 +1414,8 @@ mod tests { assert!((-0.0f32).asinh().is_sign_negative()); // issue 63271 assert_approx_eq!(2.0f32.asinh(), 1.443635475178810342493276740273105f32); assert_approx_eq!((-2.0f32).asinh(), -1.443635475178810342493276740273105f32); + // regression test for the catastrophic cancellation fixed in 72486 + assert_approx_eq!((-3000.0f32).asinh(), -8.699514775987968673236893537700647f32); } #[test] diff --git a/src/libstd/f64.rs b/src/libstd/f64.rs index fe64d27b1efc..c033198f021c 100644 --- a/src/libstd/f64.rs +++ b/src/libstd/f64.rs @@ -837,7 +837,7 @@ impl f64 { if self == Self::NEG_INFINITY { Self::NEG_INFINITY } else { - (self + ((self * self) + 1.0).sqrt()).ln().copysign(self) + (self.abs() + ((self * self) + 1.0).sqrt()).ln().copysign(self) } } @@ -1443,6 +1443,8 @@ mod tests { // issue 63271 assert_approx_eq!(2.0f64.asinh(), 1.443635475178810342493276740273105f64); assert_approx_eq!((-2.0f64).asinh(), -1.443635475178810342493276740273105f64); + // regression test for the catastrophic cancellation fixed in 72486 + assert_approx_eq!((-67452098.07139316f64).asinh(), -18.72007542627454439398548429400083); } #[test] From 591584e71f7d8a613d586066c8b01c1eecf08f35 Mon Sep 17 00:00:00 2001 From: Mikail Bagishov Date: Tue, 26 May 2020 23:48:36 +0300 Subject: [PATCH 007/123] Add tests for 'impl Default for [T; N]' --- src/libcore/tests/array.rs | 41 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/src/libcore/tests/array.rs b/src/libcore/tests/array.rs index c2a816f0a7d9..41855a9a8cba 100644 --- a/src/libcore/tests/array.rs +++ b/src/libcore/tests/array.rs @@ -241,3 +241,44 @@ fn iterator_drops() { } assert_eq!(i.get(), 5); } + +#[test] +fn array_default_impl_avoids_leaks_on_panic() { + use core::sync::atomic::{AtomicUsize, Ordering::Relaxed}; + static COUNTER: AtomicUsize = AtomicUsize::new(0); + #[derive(Debug)] + struct Bomb(usize); + + impl Default for Bomb { + fn default() -> Bomb { + if COUNTER.load(Relaxed) == 3 { + panic!("bomb limit exceeded"); + } + + COUNTER.fetch_add(1, Relaxed); + Bomb(COUNTER.load(Relaxed)) + } + } + + impl Drop for Bomb { + fn drop(&mut self) { + COUNTER.fetch_sub(1, Relaxed); + } + } + + let res = std::panic::catch_unwind(|| <[Bomb; 5]>::default()); + let panic_msg = match res { + Ok(_) => unreachable!(), + Err(p) => p.downcast::<&'static str>().unwrap(), + }; + assert_eq!(*panic_msg, "bomb limit exceeded"); + // check that all bombs are successfully dropped + assert_eq!(COUNTER.load(Relaxed), 0); +} + +#[test] +fn empty_array_is_always_default() { + struct DoesNotImplDefault; + + let _arr = <[DoesNotImplDefault; 0]>::default(); +} From 3313bf62ac45fab2c39e49c788423153754087a9 Mon Sep 17 00:00:00 2001 From: Mikail Bagishov Date: Thu, 28 May 2020 20:45:21 +0300 Subject: [PATCH 008/123] Skip leak test on targets without panic=unwind --- src/libcore/tests/array.rs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/libcore/tests/array.rs b/src/libcore/tests/array.rs index 41855a9a8cba..4bc44e98fc80 100644 --- a/src/libcore/tests/array.rs +++ b/src/libcore/tests/array.rs @@ -242,7 +242,14 @@ fn iterator_drops() { assert_eq!(i.get(), 5); } +// This test does not work on targets without panic=unwind support. +// To work around this problem, test is marked is should_panic, so it will +// be automagically skipped on unsuitable targets, such as +// wasm32-unknown-unkown. +// +// It means that we use panic for indicating success. #[test] +#[should_panic(expected = "test succeeded")] fn array_default_impl_avoids_leaks_on_panic() { use core::sync::atomic::{AtomicUsize, Ordering::Relaxed}; static COUNTER: AtomicUsize = AtomicUsize::new(0); @@ -274,6 +281,7 @@ fn array_default_impl_avoids_leaks_on_panic() { assert_eq!(*panic_msg, "bomb limit exceeded"); // check that all bombs are successfully dropped assert_eq!(COUNTER.load(Relaxed), 0); + panic!("test succeeded") } #[test] From f7d745f33d950c050e0a5a2ee2ee9f0e1269e956 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sat, 23 May 2020 13:22:45 +0200 Subject: [PATCH 009/123] tag/niche terminology cleanup --- .../debuginfo/metadata.rs | 105 +++++++++--------- src/librustc_codegen_ssa/mir/place.rs | 44 ++++---- src/librustc_lint/types.rs | 14 +-- src/librustc_middle/ty/layout.rs | 58 +++++----- src/librustc_mir/interpret/operand.rs | 27 ++--- src/librustc_mir/interpret/place.rs | 41 +++---- src/librustc_mir/interpret/step.rs | 4 +- src/librustc_mir/interpret/validity.rs | 4 +- src/librustc_target/abi/mod.rs | 27 +++-- src/test/ui/layout/debug.stderr | 12 +- 10 files changed, 167 insertions(+), 169 deletions(-) diff --git a/src/librustc_codegen_llvm/debuginfo/metadata.rs b/src/librustc_codegen_llvm/debuginfo/metadata.rs index 0cce0b25e589..01f630a31a18 100644 --- a/src/librustc_codegen_llvm/debuginfo/metadata.rs +++ b/src/librustc_codegen_llvm/debuginfo/metadata.rs @@ -1,4 +1,4 @@ -use self::EnumDiscriminantInfo::*; +use self::EnumTagInfo::*; use self::MemberDescriptionFactory::*; use self::RecursiveTypeDescription::*; @@ -40,7 +40,7 @@ use rustc_middle::{bug, span_bug}; use rustc_session::config::{self, DebugInfo}; use rustc_span::symbol::{Interner, Symbol}; use rustc_span::{self, SourceFile, SourceFileHash, Span}; -use rustc_target::abi::{Abi, Align, DiscriminantKind, HasDataLayout, Integer, LayoutOf}; +use rustc_target::abi::{Abi, Align, HasDataLayout, Integer, LayoutOf, TagEncoding}; use rustc_target::abi::{Int, Pointer, F32, F64}; use rustc_target::abi::{Primitive, Size, VariantIdx, Variants}; @@ -1335,7 +1335,7 @@ fn generator_layout_and_saved_local_names( struct EnumMemberDescriptionFactory<'ll, 'tcx> { enum_type: Ty<'tcx>, layout: TyAndLayout<'tcx>, - discriminant_type_metadata: Option<&'ll DIType>, + tag_type_metadata: Option<&'ll DIType>, containing_scope: &'ll DIScope, span: Span, } @@ -1385,7 +1385,7 @@ impl EnumMemberDescriptionFactory<'ll, 'tcx> { cx, self.layout, variant_info, - NoDiscriminant, + NoTag, self_metadata, self.span, ); @@ -1409,19 +1409,19 @@ impl EnumMemberDescriptionFactory<'ll, 'tcx> { }] } Variants::Multiple { - discr_kind: DiscriminantKind::Tag, - discr_index, + tag_encoding: TagEncoding::Direct, + tag_field, ref variants, .. } => { - let discriminant_info = if fallback { - RegularDiscriminant { - discr_field: Field::from(discr_index), - discr_type_metadata: self.discriminant_type_metadata.unwrap(), + let tag_info = if fallback { + RegularTag { + tag_field: Field::from(tag_field), + tag_type_metadata: self.tag_type_metadata.unwrap(), } } else { // This doesn't matter in this case. - NoDiscriminant + NoTag }; variants .iter_enumerated() @@ -1432,7 +1432,7 @@ impl EnumMemberDescriptionFactory<'ll, 'tcx> { cx, variant, variant_info, - discriminant_info, + tag_info, self_metadata, self.span, ); @@ -1467,11 +1467,11 @@ impl EnumMemberDescriptionFactory<'ll, 'tcx> { .collect() } Variants::Multiple { - discr_kind: - DiscriminantKind::Niche { ref niche_variants, niche_start, dataful_variant }, - ref discr, + tag_encoding: + TagEncoding::Niche { ref niche_variants, niche_start, dataful_variant }, + ref tag, ref variants, - discr_index, + tag_field, } => { if fallback { let variant = self.layout.for_variant(cx, dataful_variant); @@ -1480,7 +1480,7 @@ impl EnumMemberDescriptionFactory<'ll, 'tcx> { cx, variant, variant_info_for(dataful_variant), - OptimizedDiscriminant, + OptimizedTag, self.containing_scope, self.span, ); @@ -1524,8 +1524,8 @@ impl EnumMemberDescriptionFactory<'ll, 'tcx> { cx, &mut name, self.layout, - self.layout.fields.offset(discr_index), - self.layout.field(cx, discr_index).size, + self.layout.fields.offset(tag_field), + self.layout.field(cx, tag_field).size, ); variant_info_for(*niche_variants.start()).map_struct_name(|variant_name| { name.push_str(variant_name); @@ -1552,7 +1552,7 @@ impl EnumMemberDescriptionFactory<'ll, 'tcx> { cx, variant, variant_info, - OptimizedDiscriminant, + OptimizedTag, self_metadata, self.span, ); @@ -1573,7 +1573,7 @@ impl EnumMemberDescriptionFactory<'ll, 'tcx> { let value = (i.as_u32() as u128) .wrapping_sub(niche_variants.start().as_u32() as u128) .wrapping_add(niche_start); - let value = truncate(value, discr.value.size(cx)); + let value = truncate(value, tag.value.size(cx)); // NOTE(eddyb) do *NOT* remove this assert, until // we pass the full 128-bit value to LLVM, otherwise // truncation will be silent and remain undetected. @@ -1603,7 +1603,7 @@ struct VariantMemberDescriptionFactory<'ll, 'tcx> { /// Cloned from the `layout::Struct` describing the variant. offsets: Vec, args: Vec<(String, Ty<'tcx>)>, - discriminant_type_metadata: Option<&'ll DIType>, + tag_type_metadata: Option<&'ll DIType>, span: Span, } @@ -1617,7 +1617,7 @@ impl VariantMemberDescriptionFactory<'ll, 'tcx> { MemberDescription { name: name.to_string(), type_metadata: if use_enum_fallback(cx) { - match self.discriminant_type_metadata { + match self.tag_type_metadata { // Discriminant is always the first field of our variant // when using the enum fallback. Some(metadata) if i == 0 => metadata, @@ -1638,10 +1638,10 @@ impl VariantMemberDescriptionFactory<'ll, 'tcx> { } #[derive(Copy, Clone)] -enum EnumDiscriminantInfo<'ll> { - RegularDiscriminant { discr_field: Field, discr_type_metadata: &'ll DIType }, - OptimizedDiscriminant, - NoDiscriminant, +enum EnumTagInfo<'ll> { + RegularTag { tag_field: Field, tag_type_metadata: &'ll DIType }, + OptimizedTag, + NoTag, } #[derive(Copy, Clone)] @@ -1706,7 +1706,7 @@ fn describe_enum_variant( cx: &CodegenCx<'ll, 'tcx>, layout: layout::TyAndLayout<'tcx>, variant: VariantInfo<'_, 'tcx>, - discriminant_info: EnumDiscriminantInfo<'ll>, + discriminant_info: EnumTagInfo<'ll>, containing_scope: &'ll DIScope, span: Span, ) -> (&'ll DICompositeType, MemberDescriptionFactory<'ll, 'tcx>) { @@ -1722,12 +1722,12 @@ fn describe_enum_variant( let (offsets, args) = if use_enum_fallback(cx) { // If this is not a univariant enum, there is also the discriminant field. let (discr_offset, discr_arg) = match discriminant_info { - RegularDiscriminant { discr_field, .. } => { + RegularTag { tag_field, .. } => { // We have the layout of an enum variant, we need the layout of the outer enum let enum_layout = cx.layout_of(layout.ty); - let offset = enum_layout.fields.offset(discr_field.as_usize()); + let offset = enum_layout.fields.offset(tag_field.as_usize()); let args = - ("RUST$ENUM$DISR".to_owned(), enum_layout.field(cx, discr_field.as_usize()).ty); + ("RUST$ENUM$DISR".to_owned(), enum_layout.field(cx, tag_field.as_usize()).ty); (Some(offset), Some(args)) } _ => (None, None), @@ -1757,8 +1757,8 @@ fn describe_enum_variant( let member_description_factory = VariantMDF(VariantMemberDescriptionFactory { offsets, args, - discriminant_type_metadata: match discriminant_info { - RegularDiscriminant { discr_type_metadata, .. } => Some(discr_type_metadata), + tag_type_metadata: match discriminant_info { + RegularTag { tag_type_metadata, .. } => Some(tag_type_metadata), _ => None, }, span, @@ -1880,18 +1880,18 @@ fn prepare_enum_metadata( if let ( &Abi::Scalar(_), - &Variants::Multiple { discr_kind: DiscriminantKind::Tag, ref discr, .. }, + &Variants::Multiple { tag_encoding: TagEncoding::Direct, ref tag, .. }, ) = (&layout.abi, &layout.variants) { - return FinalMetadata(discriminant_type_metadata(discr.value)); + return FinalMetadata(discriminant_type_metadata(tag.value)); } if use_enum_fallback(cx) { let discriminant_type_metadata = match layout.variants { Variants::Single { .. } - | Variants::Multiple { discr_kind: DiscriminantKind::Niche { .. }, .. } => None, - Variants::Multiple { discr_kind: DiscriminantKind::Tag, ref discr, .. } => { - Some(discriminant_type_metadata(discr.value)) + | Variants::Multiple { tag_encoding: TagEncoding::Niche { .. }, .. } => None, + Variants::Multiple { tag_encoding: TagEncoding::Direct, ref tag, .. } => { + Some(discriminant_type_metadata(tag.value)) } }; @@ -1927,7 +1927,7 @@ fn prepare_enum_metadata( EnumMDF(EnumMemberDescriptionFactory { enum_type, layout, - discriminant_type_metadata, + tag_type_metadata: discriminant_type_metadata, containing_scope, span, }), @@ -1943,16 +1943,13 @@ fn prepare_enum_metadata( Variants::Single { .. } => None, Variants::Multiple { - discr_kind: DiscriminantKind::Niche { .. }, - ref discr, - discr_index, - .. + tag_encoding: TagEncoding::Niche { .. }, ref tag, tag_field, .. } => { // Find the integer type of the correct size. - let size = discr.value.size(cx); - let align = discr.value.align(cx); + let size = tag.value.size(cx); + let align = tag.value.align(cx); - let discr_type = match discr.value { + let tag_type = match tag.value { Int(t, _) => t, F32 => Integer::I32, F64 => Integer::I64, @@ -1960,7 +1957,7 @@ fn prepare_enum_metadata( } .to_ty(cx.tcx, false); - let discr_metadata = basic_type_metadata(cx, discr_type); + let tag_metadata = basic_type_metadata(cx, tag_type); unsafe { Some(llvm::LLVMRustDIBuilderCreateMemberType( DIB(cx), @@ -1971,17 +1968,15 @@ fn prepare_enum_metadata( UNKNOWN_LINE_NUMBER, size.bits(), align.abi.bits() as u32, - layout.fields.offset(discr_index).bits(), + layout.fields.offset(tag_field).bits(), DIFlags::FlagArtificial, - discr_metadata, + tag_metadata, )) } } - Variants::Multiple { - discr_kind: DiscriminantKind::Tag, ref discr, discr_index, .. - } => { - let discr_type = discr.value.to_ty(cx.tcx); + Variants::Multiple { tag_encoding: TagEncoding::Direct, ref tag, tag_field, .. } => { + let discr_type = tag.value.to_ty(cx.tcx); let (size, align) = cx.size_and_align_of(discr_type); let discr_metadata = basic_type_metadata(cx, discr_type); @@ -1995,7 +1990,7 @@ fn prepare_enum_metadata( UNKNOWN_LINE_NUMBER, size.bits(), align.bits() as u32, - layout.fields.offset(discr_index).bits(), + layout.fields.offset(tag_field).bits(), DIFlags::FlagArtificial, discr_metadata, )) @@ -2081,7 +2076,7 @@ fn prepare_enum_metadata( EnumMDF(EnumMemberDescriptionFactory { enum_type, layout, - discriminant_type_metadata: None, + tag_type_metadata: None, containing_scope, span, }), diff --git a/src/librustc_codegen_ssa/mir/place.rs b/src/librustc_codegen_ssa/mir/place.rs index 2be067938290..0c8638b673d4 100644 --- a/src/librustc_codegen_ssa/mir/place.rs +++ b/src/librustc_codegen_ssa/mir/place.rs @@ -10,7 +10,7 @@ use rustc_middle::mir; use rustc_middle::mir::tcx::PlaceTy; use rustc_middle::ty::layout::{HasTyCtxt, TyAndLayout}; use rustc_middle::ty::{self, Ty}; -use rustc_target::abi::{Abi, Align, DiscriminantKind, FieldsShape, Int}; +use rustc_target::abi::{Abi, Align, FieldsShape, Int, TagEncoding}; use rustc_target::abi::{LayoutOf, VariantIdx, Variants}; #[derive(Copy, Clone, Debug)] @@ -199,7 +199,7 @@ impl<'a, 'tcx, V: CodegenObject> PlaceRef<'tcx, V> { if self.layout.abi.is_uninhabited() { return bx.cx().const_undef(cast_to); } - let (discr_scalar, discr_kind, discr_index) = match self.layout.variants { + let (tag_scalar, tag_encoding, tag_field) = match self.layout.variants { Variants::Single { index } => { let discr_val = self .layout @@ -208,33 +208,33 @@ impl<'a, 'tcx, V: CodegenObject> PlaceRef<'tcx, V> { .map_or(index.as_u32() as u128, |discr| discr.val); return bx.cx().const_uint_big(cast_to, discr_val); } - Variants::Multiple { ref discr, ref discr_kind, discr_index, .. } => { - (discr, discr_kind, discr_index) + Variants::Multiple { ref tag, ref tag_encoding, tag_field, .. } => { + (tag, tag_encoding, tag_field) } }; // Read the tag/niche-encoded discriminant from memory. - let encoded_discr = self.project_field(bx, discr_index); - let encoded_discr = bx.load_operand(encoded_discr); + let tag = self.project_field(bx, tag_field); + let tag = bx.load_operand(tag); // Decode the discriminant (specifically if it's niche-encoded). - match *discr_kind { - DiscriminantKind::Tag => { - let signed = match discr_scalar.value { + match *tag_encoding { + TagEncoding::Direct => { + let signed = match tag_scalar.value { // We use `i1` for bytes that are always `0` or `1`, // e.g., `#[repr(i8)] enum E { A, B }`, but we can't // let LLVM interpret the `i1` as signed, because // then `i1 1` (i.e., `E::B`) is effectively `i8 -1`. - Int(_, signed) => !discr_scalar.is_bool() && signed, + Int(_, signed) => !tag_scalar.is_bool() && signed, _ => false, }; - bx.intcast(encoded_discr.immediate(), cast_to, signed) + bx.intcast(tag.immediate(), cast_to, signed) } - DiscriminantKind::Niche { dataful_variant, ref niche_variants, niche_start } => { + TagEncoding::Niche { dataful_variant, ref niche_variants, niche_start } => { // Rebase from niche values to discriminants, and check // whether the result is in range for the niche variants. - let niche_llty = bx.cx().immediate_backend_type(encoded_discr.layout); - let encoded_discr = encoded_discr.immediate(); + let niche_llty = bx.cx().immediate_backend_type(tag.layout); + let tag = tag.immediate(); // We first compute the "relative discriminant" (wrt `niche_variants`), // that is, if `n = niche_variants.end() - niche_variants.start()`, @@ -248,9 +248,9 @@ impl<'a, 'tcx, V: CodegenObject> PlaceRef<'tcx, V> { let relative_discr = if niche_start == 0 { // Avoid subtracting `0`, which wouldn't work for pointers. // FIXME(eddyb) check the actual primitive type here. - encoded_discr + tag } else { - bx.sub(encoded_discr, bx.cx().const_uint_big(niche_llty, niche_start)) + bx.sub(tag, bx.cx().const_uint_big(niche_llty, niche_start)) }; let relative_max = niche_variants.end().as_u32() - niche_variants.start().as_u32(); let is_niche = { @@ -312,8 +312,8 @@ impl<'a, 'tcx, V: CodegenObject> PlaceRef<'tcx, V> { Variants::Single { index } => { assert_eq!(index, variant_index); } - Variants::Multiple { discr_kind: DiscriminantKind::Tag, discr_index, .. } => { - let ptr = self.project_field(bx, discr_index); + Variants::Multiple { tag_encoding: TagEncoding::Direct, tag_field, .. } => { + let ptr = self.project_field(bx, tag_field); let to = self.layout.ty.discriminant_for_variant(bx.tcx(), variant_index).unwrap().val; bx.store( @@ -323,9 +323,9 @@ impl<'a, 'tcx, V: CodegenObject> PlaceRef<'tcx, V> { ); } Variants::Multiple { - discr_kind: - DiscriminantKind::Niche { dataful_variant, ref niche_variants, niche_start }, - discr_index, + tag_encoding: + TagEncoding::Niche { dataful_variant, ref niche_variants, niche_start }, + tag_field, .. } => { if variant_index != dataful_variant { @@ -339,7 +339,7 @@ impl<'a, 'tcx, V: CodegenObject> PlaceRef<'tcx, V> { bx.memset(self.llval, fill_byte, size, self.align, MemFlags::empty()); } - let niche = self.project_field(bx, discr_index); + let niche = self.project_field(bx, tag_field); let niche_llty = bx.cx().immediate_backend_type(niche.layout); let niche_value = variant_index.as_u32() - niche_variants.start().as_u32(); let niche_value = (niche_value as u128).wrapping_add(niche_start); diff --git a/src/librustc_lint/types.rs b/src/librustc_lint/types.rs index 703c2a7a443a..6f4e2c69e339 100644 --- a/src/librustc_lint/types.rs +++ b/src/librustc_lint/types.rs @@ -16,7 +16,7 @@ use rustc_middle::ty::{self, AdtKind, ParamEnv, Ty, TyCtxt}; use rustc_span::source_map; use rustc_span::symbol::sym; use rustc_span::Span; -use rustc_target::abi::{DiscriminantKind, Integer, LayoutOf, VariantIdx, Variants}; +use rustc_target::abi::{Integer, LayoutOf, TagEncoding, VariantIdx, Variants}; use rustc_target::spec::abi::Abi; use log::debug; @@ -1036,15 +1036,15 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for VariantSizeDifferences { }; let (variants, tag) = match layout.variants { Variants::Multiple { - discr_kind: DiscriminantKind::Tag, - ref discr, + tag_encoding: TagEncoding::Direct, + ref tag, ref variants, .. - } => (variants, discr), + } => (variants, tag), _ => return, }; - let discr_size = tag.value.size(&cx.tcx).bytes(); + let tag_size = tag.value.size(&cx.tcx).bytes(); debug!( "enum `{}` is {} bytes large with layout:\n{:#?}", @@ -1058,8 +1058,8 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for VariantSizeDifferences { .iter() .zip(variants) .map(|(variant, variant_layout)| { - // Subtract the size of the enum discriminant. - let bytes = variant_layout.size.bytes().saturating_sub(discr_size); + // Subtract the size of the enum tag. + let bytes = variant_layout.size.bytes().saturating_sub(tag_size); debug!("- variant `{}` is {} bytes large", variant.ident, bytes); bytes diff --git a/src/librustc_middle/ty/layout.rs b/src/librustc_middle/ty/layout.rs index 5566e187c0c5..fde2627e29dc 100644 --- a/src/librustc_middle/ty/layout.rs +++ b/src/librustc_middle/ty/layout.rs @@ -975,13 +975,13 @@ impl<'tcx> LayoutCx<'tcx, TyCtxt<'tcx>> { return Ok(tcx.intern_layout(Layout { variants: Variants::Multiple { - discr: niche_scalar, - discr_kind: DiscriminantKind::Niche { + tag: niche_scalar, + tag_encoding: TagEncoding::Niche { dataful_variant: i, niche_variants, niche_start, }, - discr_index: 0, + tag_field: 0, variants: st, }, fields: FieldsShape::Arbitrary { @@ -1217,9 +1217,9 @@ impl<'tcx> LayoutCx<'tcx, TyCtxt<'tcx>> { tcx.intern_layout(Layout { variants: Variants::Multiple { - discr: tag, - discr_kind: DiscriminantKind::Tag, - discr_index: 0, + tag, + tag_encoding: TagEncoding::Direct, + tag_field: 0, variants: layout_variants, }, fields: FieldsShape::Arbitrary { @@ -1400,15 +1400,15 @@ impl<'tcx> LayoutCx<'tcx, TyCtxt<'tcx>> { // Build a prefix layout, including "promoting" all ineligible // locals as part of the prefix. We compute the layout of all of // these fields at once to get optimal packing. - let discr_index = substs.as_generator().prefix_tys().count(); + let tag_index = substs.as_generator().prefix_tys().count(); // `info.variant_fields` already accounts for the reserved variants, so no need to add them. let max_discr = (info.variant_fields.len() - 1) as u128; let discr_int = Integer::fit_unsigned(max_discr); let discr_int_ty = discr_int.to_ty(tcx, false); - let discr = Scalar { value: Primitive::Int(discr_int, false), valid_range: 0..=max_discr }; - let discr_layout = self.tcx.intern_layout(Layout::scalar(self, discr.clone())); - let discr_layout = TyAndLayout { ty: discr_int_ty, layout: discr_layout }; + let tag = Scalar { value: Primitive::Int(discr_int, false), valid_range: 0..=max_discr }; + let tag_layout = self.tcx.intern_layout(Layout::scalar(self, tag.clone())); + let tag_layout = TyAndLayout { ty: discr_int_ty, layout: tag_layout }; let promoted_layouts = ineligible_locals .iter() @@ -1419,7 +1419,7 @@ impl<'tcx> LayoutCx<'tcx, TyCtxt<'tcx>> { .as_generator() .prefix_tys() .map(|ty| self.layout_of(ty)) - .chain(iter::once(Ok(discr_layout))) + .chain(iter::once(Ok(tag_layout))) .chain(promoted_layouts) .collect::, _>>()?; let prefix = self.univariant_uninterned( @@ -1442,7 +1442,7 @@ impl<'tcx> LayoutCx<'tcx, TyCtxt<'tcx>> { // "a" (`0..b_start`) and "b" (`b_start..`) correspond to // "outer" and "promoted" fields respectively. - let b_start = (discr_index + 1) as u32; + let b_start = (tag_index + 1) as u32; let offsets_b = offsets.split_off(b_start as usize); let offsets_a = offsets; @@ -1559,9 +1559,9 @@ impl<'tcx> LayoutCx<'tcx, TyCtxt<'tcx>> { let layout = tcx.intern_layout(Layout { variants: Variants::Multiple { - discr, - discr_kind: DiscriminantKind::Tag, - discr_index, + tag: tag, + tag_encoding: TagEncoding::Direct, + tag_field: tag_index, variants, }, fields: outer_fields, @@ -1681,7 +1681,7 @@ impl<'tcx> LayoutCx<'tcx, TyCtxt<'tcx>> { } } - Variants::Multiple { ref discr, ref discr_kind, .. } => { + Variants::Multiple { ref tag, ref tag_encoding, .. } => { debug!( "print-type-size `{:#?}` adt general variants def {}", layout.ty, @@ -1703,8 +1703,8 @@ impl<'tcx> LayoutCx<'tcx, TyCtxt<'tcx>> { record( adt_kind.into(), adt_packed, - match discr_kind { - DiscriminantKind::Tag => Some(discr.value.size(self)), + match tag_encoding { + TagEncoding::Direct => Some(tag.value.size(self)), _ => None, }, variant_infos, @@ -2029,11 +2029,11 @@ where fn field(this: TyAndLayout<'tcx>, cx: &C, i: usize) -> C::TyAndLayout { let tcx = cx.tcx(); - let discr_layout = |discr: &Scalar| -> C::TyAndLayout { - let layout = Layout::scalar(cx, discr.clone()); + let tag_layout = |tag: &Scalar| -> C::TyAndLayout { + let layout = Layout::scalar(cx, tag.clone()); MaybeResult::from(Ok(TyAndLayout { layout: tcx.intern_layout(layout), - ty: discr.value.to_ty(tcx), + ty: tag.value.to_ty(tcx), })) }; @@ -2110,9 +2110,9 @@ where .unwrap() .nth(i) .unwrap(), - Variants::Multiple { ref discr, discr_index, .. } => { - if i == discr_index { - return discr_layout(discr); + Variants::Multiple { ref tag, tag_field, .. } => { + if i == tag_field { + return tag_layout(tag); } substs.as_generator().prefix_tys().nth(i).unwrap() } @@ -2129,9 +2129,9 @@ where Variants::Single { index } => def.variants[index].fields[i].ty(tcx, substs), // Discriminant field for enums (where applicable). - Variants::Multiple { ref discr, .. } => { + Variants::Multiple { ref tag, .. } => { assert_eq!(i, 0); - return discr_layout(discr); + return tag_layout(tag); } } } @@ -2208,10 +2208,10 @@ where // using more niches than just null (e.g., the first page of // the address space, or unaligned pointers). Variants::Multiple { - discr_kind: DiscriminantKind::Niche { dataful_variant, .. }, - discr_index, + tag_encoding: TagEncoding::Niche { dataful_variant, .. }, + tag_field, .. - } if this.fields.offset(discr_index) == offset => { + } if this.fields.offset(tag_field) == offset => { Some(this.for_variant(cx, dataful_variant)) } _ => Some(this), diff --git a/src/librustc_mir/interpret/operand.rs b/src/librustc_mir/interpret/operand.rs index db4473154c47..7da50eaa3e39 100644 --- a/src/librustc_mir/interpret/operand.rs +++ b/src/librustc_mir/interpret/operand.rs @@ -11,7 +11,7 @@ use rustc_middle::ty::layout::{PrimitiveExt, TyAndLayout}; use rustc_middle::ty::print::{FmtPrinter, PrettyPrinter, Printer}; use rustc_middle::ty::Ty; use rustc_middle::{mir, ty}; -use rustc_target::abi::{Abi, DiscriminantKind, HasDataLayout, LayoutOf, Size}; +use rustc_target::abi::{Abi, TagEncoding, HasDataLayout, LayoutOf, Size}; use rustc_target::abi::{VariantIdx, Variants}; use super::{ @@ -587,7 +587,6 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { op: OpTy<'tcx, M::PointerTag>, ) -> InterpResult<'tcx, (Scalar, VariantIdx)> { trace!("read_discriminant_value {:#?}", op.layout); - // Get type and layout of the discriminant. let discr_layout = self.layout_of(op.layout.ty.discriminant_ty(*self.tcx))?; trace!("discriminant type: {:?}", discr_layout.ty); @@ -596,10 +595,8 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { // This is not to be confused with its "variant index", which is just determining its position in the // declared list of variants -- they can differ with explicitly assigned discriminants. // We use "tag" to refer to how the discriminant is encoded in memory, which can be either - // straight-forward (`DiscriminantKind::Tag`) or with a niche (`DiscriminantKind::Niche`). - // Unfortunately, the rest of the compiler calls the latter "discriminant", too, which makes things - // rather confusing. - let (tag_scalar_layout, tag_kind, tag_index) = match op.layout.variants { + // straight-forward (`TagEncoding::Direct`) or with a niche (`TagEncoding::Niche`). + let (tag_scalar_layout, tag_encoding, tag_field) = match op.layout.variants { Variants::Single { index } => { let discr = match op.layout.ty.discriminant_for_variant(*self.tcx, index) { Some(discr) => { @@ -615,8 +612,8 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { }; return Ok((discr, index)); } - Variants::Multiple { ref discr, ref discr_kind, discr_index, .. } => { - (discr, discr_kind, discr_index) + Variants::Multiple { ref tag, ref tag_encoding, tag_field, .. } => { + (tag, tag_encoding, tag_field) } }; @@ -633,21 +630,21 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { let tag_layout = self.layout_of(tag_scalar_layout.value.to_int_ty(*self.tcx))?; // Read tag and sanity-check `tag_layout`. - let tag_val = self.read_immediate(self.operand_field(op, tag_index)?)?; + let tag_val = self.read_immediate(self.operand_field(op, tag_field)?)?; assert_eq!(tag_layout.size, tag_val.layout.size); assert_eq!(tag_layout.abi.is_signed(), tag_val.layout.abi.is_signed()); let tag_val = tag_val.to_scalar()?; trace!("tag value: {:?}", tag_val); // Figure out which discriminant and variant this corresponds to. - Ok(match *tag_kind { - DiscriminantKind::Tag => { + Ok(match *tag_encoding { + TagEncoding::Direct => { let tag_bits = self .force_bits(tag_val, tag_layout.size) .map_err(|_| err_ub!(InvalidDiscriminant(tag_val.erase_tag())))?; // Cast bits from tag layout to discriminant layout. - let discr_val_cast = self.cast_from_scalar(tag_bits, tag_layout, discr_layout.ty); - let discr_bits = discr_val_cast.assert_bits(discr_layout.size); + let discr_val = self.cast_from_scalar(tag_bits, tag_layout, discr_layout.ty); + let discr_bits = discr_val.assert_bits(discr_layout.size); // Convert discriminant to variant index, and catch invalid discriminants. let index = match op.layout.ty.kind { ty::Adt(adt, _) => { @@ -663,9 +660,9 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { } .ok_or_else(|| err_ub!(InvalidDiscriminant(tag_val.erase_tag())))?; // Return the cast value, and the index. - (discr_val_cast, index.0) + (discr_val, index.0) } - DiscriminantKind::Niche { dataful_variant, ref niche_variants, niche_start } => { + TagEncoding::Niche { dataful_variant, ref niche_variants, niche_start } => { // Compute the variant this niche value/"tag" corresponds to. With niche layout, // discriminant (encoded in niche/tag) and variant index are the same. let variants_start = niche_variants.start().as_u32(); diff --git a/src/librustc_mir/interpret/place.rs b/src/librustc_mir/interpret/place.rs index 3f0800b12b54..1e6335c871e7 100644 --- a/src/librustc_mir/interpret/place.rs +++ b/src/librustc_mir/interpret/place.rs @@ -9,7 +9,7 @@ use rustc_macros::HashStable; use rustc_middle::mir; use rustc_middle::ty::layout::{PrimitiveExt, TyAndLayout}; use rustc_middle::ty::{self, Ty}; -use rustc_target::abi::{Abi, Align, DiscriminantKind, FieldsShape}; +use rustc_target::abi::{Abi, Align, FieldsShape, TagEncoding}; use rustc_target::abi::{HasDataLayout, LayoutOf, Size, VariantIdx, Variants}; use super::{ @@ -1031,7 +1031,8 @@ where MPlaceTy { mplace, layout } } - pub fn write_discriminant_index( + /// Writes the discriminant of the given variant. + pub fn write_discriminant( &mut self, variant_index: VariantIdx, dest: PlaceTy<'tcx, M::PointerTag>, @@ -1047,9 +1048,9 @@ where assert_eq!(index, variant_index); } Variants::Multiple { - discr_kind: DiscriminantKind::Tag, - discr: ref discr_layout, - discr_index, + tag_encoding: TagEncoding::Direct, + tag: ref tag_layout, + tag_field, .. } => { // No need to validate that the discriminant here because the @@ -1061,17 +1062,17 @@ where // raw discriminants for enums are isize or bigger during // their computation, but the in-memory tag is the smallest possible // representation - let size = discr_layout.value.size(self); - let discr_val = truncate(discr_val, size); + let size = tag_layout.value.size(self); + let tag_val = truncate(discr_val, size); - let discr_dest = self.place_field(dest, discr_index)?; - self.write_scalar(Scalar::from_uint(discr_val, size), discr_dest)?; + let tag_dest = self.place_field(dest, tag_field)?; + self.write_scalar(Scalar::from_uint(tag_val, size), tag_dest)?; } Variants::Multiple { - discr_kind: - DiscriminantKind::Niche { dataful_variant, ref niche_variants, niche_start }, - discr: ref discr_layout, - discr_index, + tag_encoding: + TagEncoding::Niche { dataful_variant, ref niche_variants, niche_start }, + tag: ref tag_layout, + tag_field, .. } => { // No need to validate that the discriminant here because the @@ -1084,19 +1085,19 @@ where .checked_sub(variants_start) .expect("overflow computing relative variant idx"); // We need to use machine arithmetic when taking into account `niche_start`: - // discr_val = variant_index_relative + niche_start_val - let discr_layout = self.layout_of(discr_layout.value.to_int_ty(*self.tcx))?; - let niche_start_val = ImmTy::from_uint(niche_start, discr_layout); + // tag_val = variant_index_relative + niche_start_val + let tag_layout = self.layout_of(tag_layout.value.to_int_ty(*self.tcx))?; + let niche_start_val = ImmTy::from_uint(niche_start, tag_layout); let variant_index_relative_val = - ImmTy::from_uint(variant_index_relative, discr_layout); - let discr_val = self.binary_op( + ImmTy::from_uint(variant_index_relative, tag_layout); + let tag_val = self.binary_op( mir::BinOp::Add, variant_index_relative_val, niche_start_val, )?; // Write result. - let niche_dest = self.place_field(dest, discr_index)?; - self.write_immediate(*discr_val, niche_dest)?; + let niche_dest = self.place_field(dest, tag_field)?; + self.write_immediate(*tag_val, niche_dest)?; } } } diff --git a/src/librustc_mir/interpret/step.rs b/src/librustc_mir/interpret/step.rs index bd4df788057e..029b83492d59 100644 --- a/src/librustc_mir/interpret/step.rs +++ b/src/librustc_mir/interpret/step.rs @@ -89,7 +89,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { SetDiscriminant { place, variant_index } => { let dest = self.eval_place(**place)?; - self.write_discriminant_index(*variant_index, dest)?; + self.write_discriminant(*variant_index, dest)?; } // Mark locals as alive @@ -174,7 +174,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { Aggregate(ref kind, ref operands) => { let (dest, active_field_index) = match **kind { mir::AggregateKind::Adt(adt_def, variant_index, _, _, active_field_index) => { - self.write_discriminant_index(variant_index, dest)?; + self.write_discriminant(variant_index, dest)?; if adt_def.is_enum() { (self.place_downcast(dest, variant_index)?, active_field_index) } else { diff --git a/src/librustc_mir/interpret/validity.rs b/src/librustc_mir/interpret/validity.rs index e962dfb2b3e8..f21a96a97909 100644 --- a/src/librustc_mir/interpret/validity.rs +++ b/src/librustc_mir/interpret/validity.rs @@ -208,8 +208,8 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, ' fn aggregate_field_path_elem(&mut self, layout: TyAndLayout<'tcx>, field: usize) -> PathElem { // First, check if we are projecting to a variant. match layout.variants { - Variants::Multiple { discr_index, .. } => { - if discr_index == field { + Variants::Multiple { tag_field, .. } => { + if tag_field == field { return match layout.ty.kind { ty::Adt(def, ..) if def.is_enum() => PathElem::EnumTag, ty::Generator(..) => PathElem::GeneratorTag, diff --git a/src/librustc_target/abi/mod.rs b/src/librustc_target/abi/mod.rs index dcf181cb59f4..c79e9bb28900 100644 --- a/src/librustc_target/abi/mod.rs +++ b/src/librustc_target/abi/mod.rs @@ -809,25 +809,30 @@ pub enum Variants { /// Single enum variants, structs/tuples, unions, and all non-ADTs. Single { index: VariantIdx }, - /// Enum-likes with more than one inhabited variant: for each case there is - /// a struct, and they all have space reserved for the discriminant. - /// For enums this is the sole field of the layout. + /// Enum-likes with more than one inhabited variant: each variant comes with + /// a *discriminant* (usually the same as the variant index but the user can + /// assign explicit discriminant values). That discriminant is encoded + /// as a *tag* on the machine. The layout of each variant is + /// a struct, and they all have space reserved for the tag. + /// For enums, the tag is the sole field of the layout. Multiple { - discr: Scalar, - discr_kind: DiscriminantKind, - discr_index: usize, + tag: Scalar, + tag_encoding: TagEncoding, + tag_field: usize, variants: IndexVec, }, } #[derive(PartialEq, Eq, Hash, Debug, HashStable_Generic)] -pub enum DiscriminantKind { - /// Integer tag holding the discriminant value itself. - Tag, +pub enum TagEncoding { + /// The tag directly stores the discriminant, but possibly with a smaller layout + /// (so converting the tag to the discriminant can require sign extension). + Direct, /// Niche (values invalid for a type) encoding the discriminant: - /// the variant `dataful_variant` contains a niche at an arbitrary - /// offset (field `discr_index` of the enum), which for a variant with + /// Discriminant and variant index coincide. + /// The variant `dataful_variant` contains a niche at an arbitrary + /// offset (field `tag_field` of the enum), which for a variant with /// discriminant `d` is set to /// `(d - niche_variants.start).wrapping_add(niche_start)`. /// diff --git a/src/test/ui/layout/debug.stderr b/src/test/ui/layout/debug.stderr index cd8ebdffb730..1a371c6b1700 100644 --- a/src/test/ui/layout/debug.stderr +++ b/src/test/ui/layout/debug.stderr @@ -10,15 +10,15 @@ error: layout_of(E) = Layout { ], }, variants: Multiple { - discr: Scalar { + tag: Scalar { value: Int( I32, false, ), valid_range: 0..=0, }, - discr_kind: Tag, - discr_index: 0, + tag_encoding: Direct, + tag_field: 0, variants: [ Layout { fields: Arbitrary { @@ -202,15 +202,15 @@ error: layout_of(std::result::Result) = Layout { ], }, variants: Multiple { - discr: Scalar { + tag: Scalar { value: Int( I32, false, ), valid_range: 0..=1, }, - discr_kind: Tag, - discr_index: 0, + tag_encoding: Direct, + tag_field: 0, variants: [ Layout { fields: Arbitrary { From 7a6d03c2699787644b44f7bc3e8252d1508880b8 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sat, 30 May 2020 14:21:56 +0200 Subject: [PATCH 010/123] miri errors: rename InvalidDiscriminant -> InvalidTag --- src/librustc_middle/mir/interpret/error.rs | 6 +++--- src/librustc_mir/interpret/operand.rs | 8 ++++---- src/librustc_mir/interpret/validity.rs | 4 ++-- src/test/ui/consts/const-eval/double_check2.stderr | 2 +- src/test/ui/consts/const-eval/ub-enum.stderr | 4 ++-- 5 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/librustc_middle/mir/interpret/error.rs b/src/librustc_middle/mir/interpret/error.rs index fc588e049d7d..6646aad6fc74 100644 --- a/src/librustc_middle/mir/interpret/error.rs +++ b/src/librustc_middle/mir/interpret/error.rs @@ -390,8 +390,8 @@ pub enum UndefinedBehaviorInfo<'tcx> { InvalidBool(u8), /// Using a non-character `u32` as character. InvalidChar(u32), - /// An enum discriminant was set to a value which was outside the range of valid values. - InvalidDiscriminant(Scalar), + /// The tag of an enum does not encode an actual discriminant. + InvalidTag(Scalar), /// Using a pointer-not-to-a-function as function pointer. InvalidFunctionPointer(Pointer), /// Using a string that is not valid UTF-8, @@ -463,7 +463,7 @@ impl fmt::Display for UndefinedBehaviorInfo<'_> { InvalidChar(c) => { write!(f, "interpreting an invalid 32-bit value as a char: 0x{:08x}", c) } - InvalidDiscriminant(val) => write!(f, "enum value has invalid discriminant: {}", val), + InvalidTag(val) => write!(f, "enum value has invalid tag: {}", val), InvalidFunctionPointer(p) => { write!(f, "using {} as function pointer but it does not point to a function", p) } diff --git a/src/librustc_mir/interpret/operand.rs b/src/librustc_mir/interpret/operand.rs index 7da50eaa3e39..fb08e83b7694 100644 --- a/src/librustc_mir/interpret/operand.rs +++ b/src/librustc_mir/interpret/operand.rs @@ -11,7 +11,7 @@ use rustc_middle::ty::layout::{PrimitiveExt, TyAndLayout}; use rustc_middle::ty::print::{FmtPrinter, PrettyPrinter, Printer}; use rustc_middle::ty::Ty; use rustc_middle::{mir, ty}; -use rustc_target::abi::{Abi, TagEncoding, HasDataLayout, LayoutOf, Size}; +use rustc_target::abi::{Abi, HasDataLayout, LayoutOf, Size, TagEncoding}; use rustc_target::abi::{VariantIdx, Variants}; use super::{ @@ -641,7 +641,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { TagEncoding::Direct => { let tag_bits = self .force_bits(tag_val, tag_layout.size) - .map_err(|_| err_ub!(InvalidDiscriminant(tag_val.erase_tag())))?; + .map_err(|_| err_ub!(InvalidTag(tag_val.erase_tag())))?; // Cast bits from tag layout to discriminant layout. let discr_val = self.cast_from_scalar(tag_bits, tag_layout, discr_layout.ty); let discr_bits = discr_val.assert_bits(discr_layout.size); @@ -658,7 +658,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { } _ => bug!("tagged layout for non-adt non-generator"), } - .ok_or_else(|| err_ub!(InvalidDiscriminant(tag_val.erase_tag())))?; + .ok_or_else(|| err_ub!(InvalidTag(tag_val.erase_tag())))?; // Return the cast value, and the index. (discr_val, index.0) } @@ -674,7 +674,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { && variants_start == variants_end && !self.memory.ptr_may_be_null(ptr); if !ptr_valid { - throw_ub!(InvalidDiscriminant(tag_val.erase_tag())) + throw_ub!(InvalidTag(tag_val.erase_tag())) } dataful_variant } diff --git a/src/librustc_mir/interpret/validity.rs b/src/librustc_mir/interpret/validity.rs index f21a96a97909..8dea811d8bcd 100644 --- a/src/librustc_mir/interpret/validity.rs +++ b/src/librustc_mir/interpret/validity.rs @@ -696,8 +696,8 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValueVisitor<'mir, 'tcx, M> try_validation!( self.walk_value(op), self.path, - err_ub!(InvalidDiscriminant(val)) => - { "{}", val } expected { "a valid enum discriminant" }, + err_ub!(InvalidTag(val)) => + { "{}", val } expected { "a valid enum tag" }, err_unsup!(ReadPointerAsBytes) => { "a pointer" } expected { "plain (non-pointer) bytes" }, ); diff --git a/src/test/ui/consts/const-eval/double_check2.stderr b/src/test/ui/consts/const-eval/double_check2.stderr index 81fa5c0df13f..93dd9a53ec99 100644 --- a/src/test/ui/consts/const-eval/double_check2.stderr +++ b/src/test/ui/consts/const-eval/double_check2.stderr @@ -5,7 +5,7 @@ LL | / static FOO: (&Foo, &Bar) = unsafe {( LL | | Union { u8: &BAR }.foo, LL | | Union { u8: &BAR }.bar, LL | | )}; - | |___^ type validation failed: encountered 0x05 at .1., but expected a valid enum discriminant + | |___^ type validation failed: encountered 0x05 at .1., but expected a valid enum tag | = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. diff --git a/src/test/ui/consts/const-eval/ub-enum.stderr b/src/test/ui/consts/const-eval/ub-enum.stderr index d8dafac3e70a..d40073fb18fb 100644 --- a/src/test/ui/consts/const-eval/ub-enum.stderr +++ b/src/test/ui/consts/const-eval/ub-enum.stderr @@ -2,7 +2,7 @@ error[E0080]: it is undefined behavior to use this value --> $DIR/ub-enum.rs:24:1 | LL | const BAD_ENUM: Enum = unsafe { mem::transmute(1usize) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered 0x00000001, but expected a valid enum discriminant + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered 0x00000001, but expected a valid enum tag | = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. @@ -26,7 +26,7 @@ error[E0080]: it is undefined behavior to use this value --> $DIR/ub-enum.rs:42:1 | LL | const BAD_ENUM2: Enum2 = unsafe { mem::transmute(0usize) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered 0x00000000, but expected a valid enum discriminant + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered 0x00000000, but expected a valid enum tag | = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. From d1c275b350f9ae74be92114f4819d6afb55d6007 Mon Sep 17 00:00:00 2001 From: Vadim Petrochenkov Date: Sat, 30 May 2020 18:46:52 +0300 Subject: [PATCH 011/123] linker: MSVC supports linking static libraries as a whole archive --- src/librustc_codegen_ssa/back/linker.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/librustc_codegen_ssa/back/linker.rs b/src/librustc_codegen_ssa/back/linker.rs index 46c365efdb5f..511c851fd31a 100644 --- a/src/librustc_codegen_ssa/back/linker.rs +++ b/src/librustc_codegen_ssa/back/linker.rs @@ -704,12 +704,14 @@ impl<'a> Linker for MsvcLinker<'a> { } fn link_whole_staticlib(&mut self, lib: Symbol, _search_path: &[PathBuf]) { - // not supported? self.link_staticlib(lib); + self.cmd.arg(format!("/WHOLEARCHIVE:{}.lib", lib)); } fn link_whole_rlib(&mut self, path: &Path) { - // not supported? self.link_rlib(path); + let mut arg = OsString::from("/WHOLEARCHIVE:"); + arg.push(path); + self.cmd.arg(arg); } fn optimize(&mut self) { // Needs more investigation of `/OPT` arguments From 1bc4e45b3fe3a0817908bd7cc21ec23798d38d63 Mon Sep 17 00:00:00 2001 From: "Carol (Nichols || Goulding)" Date: Mon, 1 Jun 2020 22:18:38 -0400 Subject: [PATCH 012/123] Only highlight results via mouseover if mouse has moved --- src/librustdoc/html/static/main.js | 37 +++++++++++++++++++----------- 1 file changed, 24 insertions(+), 13 deletions(-) diff --git a/src/librustdoc/html/static/main.js b/src/librustdoc/html/static/main.js index ac5a2f96b26c..fc31f6c76067 100644 --- a/src/librustdoc/html/static/main.js +++ b/src/librustdoc/html/static/main.js @@ -100,6 +100,8 @@ function defocusSearchBar() { // 2 for "In Return Types" var currentTab = 0; + var mouseMovedAfterSearch = true; + var titleBeforeSearch = document.title; function clearInputTimeout() { @@ -162,6 +164,7 @@ function defocusSearchBar() { } addClass(main, "hidden"); removeClass(search, "hidden"); + mouseMovedAfterSearch = false; } function hideSearchResults(search) { @@ -424,6 +427,12 @@ function defocusSearchBar() { document.addEventListener("keypress", handleShortcut); document.addEventListener("keydown", handleShortcut); + function resetMouseMoved(ev) { + mouseMovedAfterSearch = true; + } + + document.addEventListener("mousemove", resetMouseMoved); + var handleSourceHighlight = (function() { var prev_line_id = 0; @@ -1353,20 +1362,22 @@ function defocusSearchBar() { } }; var mouseover_func = function(e) { - var el = e.target; - // to retrieve the real "owner" of the event. - while (el.tagName !== "TR") { - el = el.parentNode; - } - clearTimeout(hoverTimeout); - hoverTimeout = setTimeout(function() { - onEachLazy(document.getElementsByClassName("search-results"), function(e) { - onEachLazy(e.getElementsByClassName("result"), function(i_e) { - removeClass(i_e, "highlighted"); + if (mouseMovedAfterSearch) { + var el = e.target; + // to retrieve the real "owner" of the event. + while (el.tagName !== "TR") { + el = el.parentNode; + } + clearTimeout(hoverTimeout); + hoverTimeout = setTimeout(function() { + onEachLazy(document.getElementsByClassName("search-results"), function(e) { + onEachLazy(e.getElementsByClassName("result"), function(i_e) { + removeClass(i_e, "highlighted"); + }); }); - }); - addClass(el, "highlighted"); - }, 20); + addClass(el, "highlighted"); + }, 20); + } }; onEachLazy(document.getElementsByClassName("search-results"), function(e) { onEachLazy(e.getElementsByClassName("result"), function(i_e) { From 20abc70e04eefa252e5a8bfe934cb54c51c1da37 Mon Sep 17 00:00:00 2001 From: Oliver Scherer Date: Wed, 3 Jun 2020 13:45:56 +0200 Subject: [PATCH 013/123] Don't intern memory in const prop. This isn't sound without validation. We don't want to report errors in case of failure to intern and validate, we just don't want to const prop. Interning and const prop is not built for this, let's not do it until we have a clearer picture on aggregate propagation. --- src/librustc_mir/interpret/intern.rs | 12 ++++++------ src/librustc_mir/transform/const_prop.rs | 12 ++++-------- 2 files changed, 10 insertions(+), 14 deletions(-) diff --git a/src/librustc_mir/interpret/intern.rs b/src/librustc_mir/interpret/intern.rs index 02a7f24a1e35..4c1cf1b56ae6 100644 --- a/src/librustc_mir/interpret/intern.rs +++ b/src/librustc_mir/interpret/intern.rs @@ -294,7 +294,6 @@ pub enum InternKind { Static(hir::Mutability), Constant, Promoted, - ConstProp, } /// Intern `ret` and everything it references. @@ -315,9 +314,7 @@ pub fn intern_const_alloc_recursive>( let base_intern_mode = match intern_kind { InternKind::Static(mutbl) => InternMode::Static(mutbl), // FIXME: what about array lengths, array initializers? - InternKind::Constant | InternKind::ConstProp | InternKind::Promoted => { - InternMode::ConstBase - } + InternKind::Constant | InternKind::Promoted => InternMode::ConstBase, }; // Type based interning. @@ -359,7 +356,10 @@ pub fn intern_const_alloc_recursive>( Err(error) => { ecx.tcx.sess.delay_span_bug( ecx.tcx.span, - "error during interning should later cause validation failure", + &format!( + "error during interning should later cause validation failure: {}", + error + ), ); // Some errors shouldn't come up because creating them causes // an allocation, which we should avoid. When that happens, @@ -400,7 +400,7 @@ pub fn intern_const_alloc_recursive>( // immutability is so important. alloc.mutability = Mutability::Not; } - InternKind::Constant | InternKind::ConstProp => { + InternKind::Constant => { // If it's a constant, we should not have any "leftovers" as everything // is tracked by const-checking. // FIXME: downgrade this to a warning? It rejects some legitimate consts, diff --git a/src/librustc_mir/transform/const_prop.rs b/src/librustc_mir/transform/const_prop.rs index 92000e641135..97e5b8bc047f 100644 --- a/src/librustc_mir/transform/const_prop.rs +++ b/src/librustc_mir/transform/const_prop.rs @@ -27,9 +27,9 @@ use rustc_trait_selection::traits; use crate::const_eval::error_to_const_error; use crate::interpret::{ - self, compile_time_machine, intern_const_alloc_recursive, AllocId, Allocation, Frame, ImmTy, - Immediate, InternKind, InterpCx, LocalState, LocalValue, Memory, MemoryKind, OpTy, - Operand as InterpOperand, PlaceTy, Pointer, ScalarMaybeUninit, StackPopCleanup, + self, compile_time_machine, AllocId, Allocation, Frame, ImmTy, Immediate, InterpCx, LocalState, + LocalValue, Memory, MemoryKind, OpTy, Operand as InterpOperand, PlaceTy, Pointer, + ScalarMaybeUninit, StackPopCleanup, }; use crate::transform::{MirPass, MirSource}; @@ -707,11 +707,7 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> { ScalarMaybeUninit::Scalar(l), ScalarMaybeUninit::Scalar(r), )) => l.is_bits() && r.is_bits(), - interpret::Operand::Indirect(_) if mir_opt_level >= 2 => { - let mplace = op.assert_mem_place(&self.ecx); - intern_const_alloc_recursive(&mut self.ecx, InternKind::ConstProp, mplace, false); - true - } + interpret::Operand::Indirect(_) if mir_opt_level >= 2 => true, _ => false, } } From 1e88f130a347f8b223031eafe75c0772ccfec00c Mon Sep 17 00:00:00 2001 From: Wesley Wiser Date: Mon, 8 Jun 2020 08:14:45 -0400 Subject: [PATCH 014/123] Stop allowing `Indirect(..)` values to be propagated Closes #72679 Closes #72372 Closes #72285 --- src/librustc_mir/transform/const_prop.rs | 1 - src/test/mir-opt/const_prop/discriminant.rs | 5 ++ .../32bit/rustc.main.ConstProp.diff | 75 +++++++++---------- .../64bit/rustc.main.ConstProp.diff | 75 +++++++++---------- 4 files changed, 73 insertions(+), 83 deletions(-) diff --git a/src/librustc_mir/transform/const_prop.rs b/src/librustc_mir/transform/const_prop.rs index 97e5b8bc047f..39548d8a535f 100644 --- a/src/librustc_mir/transform/const_prop.rs +++ b/src/librustc_mir/transform/const_prop.rs @@ -707,7 +707,6 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> { ScalarMaybeUninit::Scalar(l), ScalarMaybeUninit::Scalar(r), )) => l.is_bits() && r.is_bits(), - interpret::Operand::Indirect(_) if mir_opt_level >= 2 => true, _ => false, } } diff --git a/src/test/mir-opt/const_prop/discriminant.rs b/src/test/mir-opt/const_prop/discriminant.rs index 04541b94ad78..13e8eb3e44e1 100644 --- a/src/test/mir-opt/const_prop/discriminant.rs +++ b/src/test/mir-opt/const_prop/discriminant.rs @@ -1,5 +1,10 @@ // compile-flags: -O +// FIXME(wesleywiser): Ideally, we could const-prop away all of this and just be left with +// `let x = 42` but that doesn't work because const-prop doesn't support `Operand::Indirect` +// and `InterpCx::eval_place()` always forces an allocation which creates the `Indirect`. +// Fixing either of those will allow us to const-prop this away. + // EMIT_MIR_FOR_EACH_BIT_WIDTH // EMIT_MIR rustc.main.ConstProp.diff fn main() { diff --git a/src/test/mir-opt/const_prop/discriminant/32bit/rustc.main.ConstProp.diff b/src/test/mir-opt/const_prop/discriminant/32bit/rustc.main.ConstProp.diff index 9979ea954989..1c873f53f378 100644 --- a/src/test/mir-opt/const_prop/discriminant/32bit/rustc.main.ConstProp.diff +++ b/src/test/mir-opt/const_prop/discriminant/32bit/rustc.main.ConstProp.diff @@ -2,100 +2,93 @@ + // MIR for `main` after ConstProp fn main() -> () { - let mut _0: (); // return place in scope 0 at $DIR/discriminant.rs:5:11: 5:11 - let _1: i32; // in scope 0 at $DIR/discriminant.rs:6:9: 6:10 - let mut _2: i32; // in scope 0 at $DIR/discriminant.rs:6:13: 6:64 - let mut _3: std::option::Option; // in scope 0 at $DIR/discriminant.rs:6:34: 6:44 - let mut _4: isize; // in scope 0 at $DIR/discriminant.rs:6:21: 6:31 + let mut _0: (); // return place in scope 0 at $DIR/discriminant.rs:10:11: 10:11 + let _1: i32; // in scope 0 at $DIR/discriminant.rs:11:9: 11:10 + let mut _2: i32; // in scope 0 at $DIR/discriminant.rs:11:13: 11:64 + let mut _3: std::option::Option; // in scope 0 at $DIR/discriminant.rs:11:34: 11:44 + let mut _4: isize; // in scope 0 at $DIR/discriminant.rs:11:21: 11:31 scope 1 { - debug x => _1; // in scope 1 at $DIR/discriminant.rs:6:9: 6:10 + debug x => _1; // in scope 1 at $DIR/discriminant.rs:11:9: 11:10 } bb0: { - StorageLive(_1); // scope 0 at $DIR/discriminant.rs:6:9: 6:10 - StorageLive(_2); // scope 0 at $DIR/discriminant.rs:6:13: 6:64 - StorageLive(_3); // scope 0 at $DIR/discriminant.rs:6:34: 6:44 -- _3 = std::option::Option::::Some(const true); // scope 0 at $DIR/discriminant.rs:6:34: 6:44 -+ _3 = const std::option::Option::::Some(true); // scope 0 at $DIR/discriminant.rs:6:34: 6:44 + StorageLive(_1); // scope 0 at $DIR/discriminant.rs:11:9: 11:10 + StorageLive(_2); // scope 0 at $DIR/discriminant.rs:11:13: 11:64 + StorageLive(_3); // scope 0 at $DIR/discriminant.rs:11:34: 11:44 +- _3 = std::option::Option::::Some(const true); // scope 0 at $DIR/discriminant.rs:11:34: 11:44 ++ _3 = const std::option::Option::::Some(true); // scope 0 at $DIR/discriminant.rs:11:34: 11:44 // ty::Const - // + ty: bool + // + ty: std::option::Option // + val: Value(Scalar(0x01)) // mir::Constant -- // + span: $DIR/discriminant.rs:6:39: 6:43 +- // + span: $DIR/discriminant.rs:11:39: 11:43 - // + literal: Const { ty: bool, val: Value(Scalar(0x01)) } -- _4 = discriminant(_3); // scope 0 at $DIR/discriminant.rs:6:21: 6:31 -- switchInt(move _4) -> [1isize: bb2, otherwise: bb1]; // scope 0 at $DIR/discriminant.rs:6:21: 6:31 -+ // + span: $DIR/discriminant.rs:6:34: 6:44 +- _4 = discriminant(_3); // scope 0 at $DIR/discriminant.rs:11:21: 11:31 +- switchInt(move _4) -> [1isize: bb2, otherwise: bb1]; // scope 0 at $DIR/discriminant.rs:11:21: 11:31 ++ // + span: $DIR/discriminant.rs:11:34: 11:44 + // + literal: Const { ty: std::option::Option, val: Value(Scalar(0x01)) } -+ _4 = const 1isize; // scope 0 at $DIR/discriminant.rs:6:21: 6:31 ++ _4 = const 1isize; // scope 0 at $DIR/discriminant.rs:11:21: 11:31 + // ty::Const + // + ty: isize + // + val: Value(Scalar(0x00000001)) + // mir::Constant -+ // + span: $DIR/discriminant.rs:6:21: 6:31 ++ // + span: $DIR/discriminant.rs:11:21: 11:31 + // + literal: Const { ty: isize, val: Value(Scalar(0x00000001)) } -+ switchInt(const 1isize) -> [1isize: bb2, otherwise: bb1]; // scope 0 at $DIR/discriminant.rs:6:21: 6:31 ++ switchInt(const 1isize) -> [1isize: bb2, otherwise: bb1]; // scope 0 at $DIR/discriminant.rs:11:21: 11:31 + // ty::Const + // + ty: isize + // + val: Value(Scalar(0x00000001)) + // mir::Constant -+ // + span: $DIR/discriminant.rs:6:21: 6:31 ++ // + span: $DIR/discriminant.rs:11:21: 11:31 + // + literal: Const { ty: isize, val: Value(Scalar(0x00000001)) } } bb1: { - _2 = const 10i32; // scope 0 at $DIR/discriminant.rs:6:59: 6:61 + _2 = const 10i32; // scope 0 at $DIR/discriminant.rs:11:59: 11:61 // ty::Const // + ty: i32 // + val: Value(Scalar(0x0000000a)) // mir::Constant - // + span: $DIR/discriminant.rs:6:59: 6:61 + // + span: $DIR/discriminant.rs:11:59: 11:61 // + literal: Const { ty: i32, val: Value(Scalar(0x0000000a)) } - goto -> bb4; // scope 0 at $DIR/discriminant.rs:6:13: 6:64 + goto -> bb4; // scope 0 at $DIR/discriminant.rs:11:13: 11:64 } bb2: { -- switchInt(((_3 as Some).0: bool)) -> [false: bb1, otherwise: bb3]; // scope 0 at $DIR/discriminant.rs:6:26: 6:30 -+ switchInt(const true) -> [false: bb1, otherwise: bb3]; // scope 0 at $DIR/discriminant.rs:6:26: 6:30 -+ // ty::Const -+ // + ty: bool -+ // + val: Value(Scalar(0x01)) -+ // mir::Constant -+ // + span: $DIR/discriminant.rs:6:26: 6:30 -+ // + literal: Const { ty: bool, val: Value(Scalar(0x01)) } + switchInt(((_3 as Some).0: bool)) -> [false: bb1, otherwise: bb3]; // scope 0 at $DIR/discriminant.rs:11:26: 11:30 } bb3: { - _2 = const 42i32; // scope 0 at $DIR/discriminant.rs:6:47: 6:49 + _2 = const 42i32; // scope 0 at $DIR/discriminant.rs:11:47: 11:49 // ty::Const // + ty: i32 // + val: Value(Scalar(0x0000002a)) // mir::Constant - // + span: $DIR/discriminant.rs:6:47: 6:49 + // + span: $DIR/discriminant.rs:11:47: 11:49 // + literal: Const { ty: i32, val: Value(Scalar(0x0000002a)) } - goto -> bb4; // scope 0 at $DIR/discriminant.rs:6:13: 6:64 + goto -> bb4; // scope 0 at $DIR/discriminant.rs:11:13: 11:64 } bb4: { - _1 = Add(move _2, const 0i32); // scope 0 at $DIR/discriminant.rs:6:13: 6:68 + _1 = Add(move _2, const 0i32); // scope 0 at $DIR/discriminant.rs:11:13: 11:68 // ty::Const // + ty: i32 // + val: Value(Scalar(0x00000000)) // mir::Constant - // + span: $DIR/discriminant.rs:6:67: 6:68 + // + span: $DIR/discriminant.rs:11:67: 11:68 // + literal: Const { ty: i32, val: Value(Scalar(0x00000000)) } - StorageDead(_2); // scope 0 at $DIR/discriminant.rs:6:67: 6:68 - StorageDead(_3); // scope 0 at $DIR/discriminant.rs:6:68: 6:69 - _0 = const (); // scope 0 at $DIR/discriminant.rs:5:11: 7:2 + StorageDead(_2); // scope 0 at $DIR/discriminant.rs:11:67: 11:68 + StorageDead(_3); // scope 0 at $DIR/discriminant.rs:11:68: 11:69 + _0 = const (); // scope 0 at $DIR/discriminant.rs:10:11: 12:2 // ty::Const // + ty: () // + val: Value(Scalar()) // mir::Constant - // + span: $DIR/discriminant.rs:5:11: 7:2 + // + span: $DIR/discriminant.rs:10:11: 12:2 // + literal: Const { ty: (), val: Value(Scalar()) } - StorageDead(_1); // scope 0 at $DIR/discriminant.rs:7:1: 7:2 - return; // scope 0 at $DIR/discriminant.rs:7:2: 7:2 + StorageDead(_1); // scope 0 at $DIR/discriminant.rs:12:1: 12:2 + return; // scope 0 at $DIR/discriminant.rs:12:2: 12:2 } } diff --git a/src/test/mir-opt/const_prop/discriminant/64bit/rustc.main.ConstProp.diff b/src/test/mir-opt/const_prop/discriminant/64bit/rustc.main.ConstProp.diff index ec0341e3de25..75b4b7e5a62b 100644 --- a/src/test/mir-opt/const_prop/discriminant/64bit/rustc.main.ConstProp.diff +++ b/src/test/mir-opt/const_prop/discriminant/64bit/rustc.main.ConstProp.diff @@ -2,100 +2,93 @@ + // MIR for `main` after ConstProp fn main() -> () { - let mut _0: (); // return place in scope 0 at $DIR/discriminant.rs:5:11: 5:11 - let _1: i32; // in scope 0 at $DIR/discriminant.rs:6:9: 6:10 - let mut _2: i32; // in scope 0 at $DIR/discriminant.rs:6:13: 6:64 - let mut _3: std::option::Option; // in scope 0 at $DIR/discriminant.rs:6:34: 6:44 - let mut _4: isize; // in scope 0 at $DIR/discriminant.rs:6:21: 6:31 + let mut _0: (); // return place in scope 0 at $DIR/discriminant.rs:10:11: 10:11 + let _1: i32; // in scope 0 at $DIR/discriminant.rs:11:9: 11:10 + let mut _2: i32; // in scope 0 at $DIR/discriminant.rs:11:13: 11:64 + let mut _3: std::option::Option; // in scope 0 at $DIR/discriminant.rs:11:34: 11:44 + let mut _4: isize; // in scope 0 at $DIR/discriminant.rs:11:21: 11:31 scope 1 { - debug x => _1; // in scope 1 at $DIR/discriminant.rs:6:9: 6:10 + debug x => _1; // in scope 1 at $DIR/discriminant.rs:11:9: 11:10 } bb0: { - StorageLive(_1); // scope 0 at $DIR/discriminant.rs:6:9: 6:10 - StorageLive(_2); // scope 0 at $DIR/discriminant.rs:6:13: 6:64 - StorageLive(_3); // scope 0 at $DIR/discriminant.rs:6:34: 6:44 -- _3 = std::option::Option::::Some(const true); // scope 0 at $DIR/discriminant.rs:6:34: 6:44 -+ _3 = const std::option::Option::::Some(true); // scope 0 at $DIR/discriminant.rs:6:34: 6:44 + StorageLive(_1); // scope 0 at $DIR/discriminant.rs:11:9: 11:10 + StorageLive(_2); // scope 0 at $DIR/discriminant.rs:11:13: 11:64 + StorageLive(_3); // scope 0 at $DIR/discriminant.rs:11:34: 11:44 +- _3 = std::option::Option::::Some(const true); // scope 0 at $DIR/discriminant.rs:11:34: 11:44 ++ _3 = const std::option::Option::::Some(true); // scope 0 at $DIR/discriminant.rs:11:34: 11:44 // ty::Const - // + ty: bool + // + ty: std::option::Option // + val: Value(Scalar(0x01)) // mir::Constant -- // + span: $DIR/discriminant.rs:6:39: 6:43 +- // + span: $DIR/discriminant.rs:11:39: 11:43 - // + literal: Const { ty: bool, val: Value(Scalar(0x01)) } -- _4 = discriminant(_3); // scope 0 at $DIR/discriminant.rs:6:21: 6:31 -- switchInt(move _4) -> [1isize: bb2, otherwise: bb1]; // scope 0 at $DIR/discriminant.rs:6:21: 6:31 -+ // + span: $DIR/discriminant.rs:6:34: 6:44 +- _4 = discriminant(_3); // scope 0 at $DIR/discriminant.rs:11:21: 11:31 +- switchInt(move _4) -> [1isize: bb2, otherwise: bb1]; // scope 0 at $DIR/discriminant.rs:11:21: 11:31 ++ // + span: $DIR/discriminant.rs:11:34: 11:44 + // + literal: Const { ty: std::option::Option, val: Value(Scalar(0x01)) } -+ _4 = const 1isize; // scope 0 at $DIR/discriminant.rs:6:21: 6:31 ++ _4 = const 1isize; // scope 0 at $DIR/discriminant.rs:11:21: 11:31 + // ty::Const + // + ty: isize + // + val: Value(Scalar(0x0000000000000001)) + // mir::Constant -+ // + span: $DIR/discriminant.rs:6:21: 6:31 ++ // + span: $DIR/discriminant.rs:11:21: 11:31 + // + literal: Const { ty: isize, val: Value(Scalar(0x0000000000000001)) } -+ switchInt(const 1isize) -> [1isize: bb2, otherwise: bb1]; // scope 0 at $DIR/discriminant.rs:6:21: 6:31 ++ switchInt(const 1isize) -> [1isize: bb2, otherwise: bb1]; // scope 0 at $DIR/discriminant.rs:11:21: 11:31 + // ty::Const + // + ty: isize + // + val: Value(Scalar(0x0000000000000001)) + // mir::Constant -+ // + span: $DIR/discriminant.rs:6:21: 6:31 ++ // + span: $DIR/discriminant.rs:11:21: 11:31 + // + literal: Const { ty: isize, val: Value(Scalar(0x0000000000000001)) } } bb1: { - _2 = const 10i32; // scope 0 at $DIR/discriminant.rs:6:59: 6:61 + _2 = const 10i32; // scope 0 at $DIR/discriminant.rs:11:59: 11:61 // ty::Const // + ty: i32 // + val: Value(Scalar(0x0000000a)) // mir::Constant - // + span: $DIR/discriminant.rs:6:59: 6:61 + // + span: $DIR/discriminant.rs:11:59: 11:61 // + literal: Const { ty: i32, val: Value(Scalar(0x0000000a)) } - goto -> bb4; // scope 0 at $DIR/discriminant.rs:6:13: 6:64 + goto -> bb4; // scope 0 at $DIR/discriminant.rs:11:13: 11:64 } bb2: { -- switchInt(((_3 as Some).0: bool)) -> [false: bb1, otherwise: bb3]; // scope 0 at $DIR/discriminant.rs:6:26: 6:30 -+ switchInt(const true) -> [false: bb1, otherwise: bb3]; // scope 0 at $DIR/discriminant.rs:6:26: 6:30 -+ // ty::Const -+ // + ty: bool -+ // + val: Value(Scalar(0x01)) -+ // mir::Constant -+ // + span: $DIR/discriminant.rs:6:26: 6:30 -+ // + literal: Const { ty: bool, val: Value(Scalar(0x01)) } + switchInt(((_3 as Some).0: bool)) -> [false: bb1, otherwise: bb3]; // scope 0 at $DIR/discriminant.rs:11:26: 11:30 } bb3: { - _2 = const 42i32; // scope 0 at $DIR/discriminant.rs:6:47: 6:49 + _2 = const 42i32; // scope 0 at $DIR/discriminant.rs:11:47: 11:49 // ty::Const // + ty: i32 // + val: Value(Scalar(0x0000002a)) // mir::Constant - // + span: $DIR/discriminant.rs:6:47: 6:49 + // + span: $DIR/discriminant.rs:11:47: 11:49 // + literal: Const { ty: i32, val: Value(Scalar(0x0000002a)) } - goto -> bb4; // scope 0 at $DIR/discriminant.rs:6:13: 6:64 + goto -> bb4; // scope 0 at $DIR/discriminant.rs:11:13: 11:64 } bb4: { - _1 = Add(move _2, const 0i32); // scope 0 at $DIR/discriminant.rs:6:13: 6:68 + _1 = Add(move _2, const 0i32); // scope 0 at $DIR/discriminant.rs:11:13: 11:68 // ty::Const // + ty: i32 // + val: Value(Scalar(0x00000000)) // mir::Constant - // + span: $DIR/discriminant.rs:6:67: 6:68 + // + span: $DIR/discriminant.rs:11:67: 11:68 // + literal: Const { ty: i32, val: Value(Scalar(0x00000000)) } - StorageDead(_2); // scope 0 at $DIR/discriminant.rs:6:67: 6:68 - StorageDead(_3); // scope 0 at $DIR/discriminant.rs:6:68: 6:69 - _0 = const (); // scope 0 at $DIR/discriminant.rs:5:11: 7:2 + StorageDead(_2); // scope 0 at $DIR/discriminant.rs:11:67: 11:68 + StorageDead(_3); // scope 0 at $DIR/discriminant.rs:11:68: 11:69 + _0 = const (); // scope 0 at $DIR/discriminant.rs:10:11: 12:2 // ty::Const // + ty: () // + val: Value(Scalar()) // mir::Constant - // + span: $DIR/discriminant.rs:5:11: 7:2 + // + span: $DIR/discriminant.rs:10:11: 12:2 // + literal: Const { ty: (), val: Value(Scalar()) } - StorageDead(_1); // scope 0 at $DIR/discriminant.rs:7:1: 7:2 - return; // scope 0 at $DIR/discriminant.rs:7:2: 7:2 + StorageDead(_1); // scope 0 at $DIR/discriminant.rs:12:1: 12:2 + return; // scope 0 at $DIR/discriminant.rs:12:2: 12:2 } } From 9ceb9bb20324630380153c1db5aeb56a433ab8d9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20Miku=C5=82a?= Date: Tue, 2 Jun 2020 22:35:31 +0200 Subject: [PATCH 015/123] Move copying of self-contained objects to new function --- src/bootstrap/compile.rs | 90 ++++++++++++++++++++++++++++------------ 1 file changed, 64 insertions(+), 26 deletions(-) diff --git a/src/bootstrap/compile.rs b/src/bootstrap/compile.rs index c09b73b04201..56d72d72b610 100644 --- a/src/bootstrap/compile.rs +++ b/src/bootstrap/compile.rs @@ -74,6 +74,7 @@ impl Step for Std { // Even if we're not building std this stage, the new sysroot must // still contain the third party objects needed by various targets. copy_third_party_objects(builder, &compiler, target); + copy_self_contained_objects(builder, &compiler, target); builder.ensure(StdLink { compiler: compiler_to_use, @@ -84,6 +85,7 @@ impl Step for Std { } target_deps.extend(copy_third_party_objects(builder, &compiler, target).into_iter()); + target_deps.extend(copy_self_contained_objects(builder, &compiler, target)); let mut cargo = builder.cargo(compiler, Mode::Std, target, "build"); std_cargo(builder, target, compiler.stage, &mut cargo); @@ -109,6 +111,19 @@ impl Step for Std { } } +fn copy_and_stamp( + builder: &Builder<'_>, + libdir: &Path, + sourcedir: &Path, + name: &str, + target_deps: &mut Vec, +) { + let target = libdir.join(name); + builder.copy(&sourcedir.join(name), &target); + + target_deps.push((target, dependency_type)); +} + /// Copies third party objects needed by various targets. fn copy_third_party_objects( builder: &Builder<'_>, @@ -116,14 +131,43 @@ fn copy_third_party_objects( target: Interned, ) -> Vec { let libdir = builder.sysroot_libdir(*compiler, target); - let mut target_deps = vec![]; - let mut copy_and_stamp = |sourcedir: &Path, name: &str| { - let target = libdir.join(name); - builder.copy(&sourcedir.join(name), &target); - target_deps.push(target); - }; + // Copies libunwind.a compiled to be linked with x86_64-fortanix-unknown-sgx. + // + // This target needs to be linked to Fortanix's port of llvm's libunwind. + // libunwind requires support for rwlock and printing to stderr, + // which is provided by std for this target. + if target == "x86_64-fortanix-unknown-sgx" { + let src_path_env = "X86_FORTANIX_SGX_LIBS"; + let src = + env::var(src_path_env).unwrap_or_else(|_| panic!("{} not found in env", src_path_env)); + copy_and_stamp( + builder, + &*libdir, + Path::new(&src), + "libunwind.a", + &mut target_deps, + ); + } + + if builder.config.sanitizers && compiler.stage != 0 { + // The sanitizers are only copied in stage1 or above, + // to avoid creating dependency on LLVM. + target_deps.extend(copy_sanitizers(builder, &compiler, target)); + } + + target_deps +} + +/// Copies third party objects needed by various targets for self-contained linkage. +fn copy_self_contained_objects( + builder: &Builder<'_>, + compiler: &Compiler, + target: Interned, +) -> Vec { + let libdir = builder.sysroot_libdir(*compiler, target); + let mut target_deps = vec![]; // Copies the CRT objects. // @@ -135,29 +179,23 @@ fn copy_third_party_objects( if target.contains("musl") { let srcdir = builder.musl_root(target).unwrap().join("lib"); for &obj in &["crt1.o", "Scrt1.o", "rcrt1.o", "crti.o", "crtn.o"] { - copy_and_stamp(&srcdir, obj); + copy_and_stamp( + builder, + &libdir_self_contained, + &srcdir, + obj, + &mut target_deps, + ); } } else if target.ends_with("-wasi") { let srcdir = builder.wasi_root(target).unwrap().join("lib/wasm32-wasi"); - copy_and_stamp(&srcdir, "crt1.o"); - } - - // Copies libunwind.a compiled to be linked with x86_64-fortanix-unknown-sgx. - // - // This target needs to be linked to Fortanix's port of llvm's libunwind. - // libunwind requires support for rwlock and printing to stderr, - // which is provided by std for this target. - if target == "x86_64-fortanix-unknown-sgx" { - let src_path_env = "X86_FORTANIX_SGX_LIBS"; - let src = - env::var(src_path_env).unwrap_or_else(|_| panic!("{} not found in env", src_path_env)); - copy_and_stamp(Path::new(&src), "libunwind.a"); - } - - if builder.config.sanitizers && compiler.stage != 0 { - // The sanitizers are only copied in stage1 or above, - // to avoid creating dependency on LLVM. - target_deps.extend(copy_sanitizers(builder, &compiler, target)); + copy_and_stamp( + builder, + &libdir_self_contained, + &srcdir, + "crt1.o", + &mut target_deps, + ); } target_deps From 638ebbc5859a38794408a988ffec6f54e0dc0f0b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20Miku=C5=82a?= Date: Tue, 2 Jun 2020 23:18:41 +0200 Subject: [PATCH 016/123] Move copying of MinGW CRT to the better location --- src/bootstrap/compile.rs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/bootstrap/compile.rs b/src/bootstrap/compile.rs index 56d72d72b610..4f58e55f2140 100644 --- a/src/bootstrap/compile.rs +++ b/src/bootstrap/compile.rs @@ -196,6 +196,13 @@ fn copy_self_contained_objects( "crt1.o", &mut target_deps, ); + } else if target.contains("windows-gnu") { + for obj in ["crt2.o", "dllcrt2.o"].iter() { + let src = compiler_file(builder, builder.cc(target), target, obj); + let target = libdir.join(obj); + builder.copy(&src, &target); + target_deps.push(target); + } } target_deps @@ -419,13 +426,6 @@ impl Step for StartupObjects { target_deps.push(target); } - for obj in ["crt2.o", "dllcrt2.o"].iter() { - let src = compiler_file(builder, builder.cc(target), target, obj); - let target = sysroot_dir.join(obj); - builder.copy(&src, &target); - target_deps.push(target); - } - target_deps } } From 961974fe0348f479255f9e95b5924419c2c15a77 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20Miku=C5=82a?= Date: Wed, 3 Jun 2020 00:56:27 +0200 Subject: [PATCH 017/123] Use enum to distinguish dependency type --- src/bootstrap/compile.rs | 52 ++++++++++++++++++++++++---------------- src/bootstrap/dist.rs | 6 ++--- src/bootstrap/lib.rs | 22 ++++++++++++++--- 3 files changed, 54 insertions(+), 26 deletions(-) diff --git a/src/bootstrap/compile.rs b/src/bootstrap/compile.rs index 4f58e55f2140..5fae7277149d 100644 --- a/src/bootstrap/compile.rs +++ b/src/bootstrap/compile.rs @@ -23,7 +23,7 @@ use crate::builder::Cargo; use crate::dist; use crate::native; use crate::util::{exe, is_dylib, symlink_dir}; -use crate::{Compiler, GitRepo, Mode}; +use crate::{Compiler, DependencyType, GitRepo, Mode}; use crate::builder::{Builder, Kind, RunConfig, ShouldRun, Step}; use crate::cache::{Interned, INTERNER}; @@ -84,7 +84,7 @@ impl Step for Std { return; } - target_deps.extend(copy_third_party_objects(builder, &compiler, target).into_iter()); + target_deps.extend(copy_third_party_objects(builder, &compiler, target)); target_deps.extend(copy_self_contained_objects(builder, &compiler, target)); let mut cargo = builder.cargo(compiler, Mode::Std, target, "build"); @@ -116,7 +116,8 @@ fn copy_and_stamp( libdir: &Path, sourcedir: &Path, name: &str, - target_deps: &mut Vec, + target_deps: &mut Vec<(PathBuf, DependencyType)>, + dependency_type: DependencyType, ) { let target = libdir.join(name); builder.copy(&sourcedir.join(name), &target); @@ -129,7 +130,7 @@ fn copy_third_party_objects( builder: &Builder<'_>, compiler: &Compiler, target: Interned, -) -> Vec { +) -> Vec<(PathBuf, DependencyType)> { let libdir = builder.sysroot_libdir(*compiler, target); let mut target_deps = vec![]; @@ -148,13 +149,18 @@ fn copy_third_party_objects( Path::new(&src), "libunwind.a", &mut target_deps, + DependencyType::Target, ); } if builder.config.sanitizers && compiler.stage != 0 { // The sanitizers are only copied in stage1 or above, // to avoid creating dependency on LLVM. - target_deps.extend(copy_sanitizers(builder, &compiler, target)); + target_deps.extend( + copy_sanitizers(builder, &compiler, target) + .into_iter() + .map(|d| (d, DependencyType::Target)), + ); } target_deps @@ -165,7 +171,7 @@ fn copy_self_contained_objects( builder: &Builder<'_>, compiler: &Compiler, target: Interned, -) -> Vec { +) -> Vec<(PathBuf, DependencyType)> { let libdir = builder.sysroot_libdir(*compiler, target); let mut target_deps = vec![]; @@ -185,6 +191,7 @@ fn copy_self_contained_objects( &srcdir, obj, &mut target_deps, + DependencyType::TargetSelfContained, ); } } else if target.ends_with("-wasi") { @@ -195,13 +202,14 @@ fn copy_self_contained_objects( &srcdir, "crt1.o", &mut target_deps, + DependencyType::TargetSelfContained, ); } else if target.contains("windows-gnu") { for obj in ["crt2.o", "dllcrt2.o"].iter() { let src = compiler_file(builder, builder.cc(target), target, obj); let target = libdir.join(obj); builder.copy(&src, &target); - target_deps.push(target); + target_deps.push((target, DependencyType::TargetSelfContained)); } } @@ -370,7 +378,7 @@ pub struct StartupObjects { } impl Step for StartupObjects { - type Output = Vec; + type Output = Vec<(PathBuf, DependencyType)>; fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { run.path("src/rtstartup") @@ -389,7 +397,7 @@ impl Step for StartupObjects { /// They don't require any library support as they're just plain old object /// files, so we just use the nightly snapshot compiler to always build them (as /// no other compilers are guaranteed to be available). - fn run(self, builder: &Builder<'_>) -> Vec { + fn run(self, builder: &Builder<'_>) -> Vec<(PathBuf, DependencyType)> { let for_compiler = self.compiler; let target = self.target; if !target.contains("windows-gnu") { @@ -423,7 +431,7 @@ impl Step for StartupObjects { let target = sysroot_dir.join((*file).to_string() + ".o"); builder.copy(dst_file, &target); - target_deps.push(target); + target_deps.push((target, DependencyType::Target)); } target_deps @@ -838,8 +846,8 @@ pub fn add_to_sysroot( ) { t!(fs::create_dir_all(&sysroot_dst)); t!(fs::create_dir_all(&sysroot_host_dst)); - for (path, host) in builder.read_stamp_file(stamp) { - if host { + for (path, dependency_type) in builder.read_stamp_file(stamp) { + if dependency_type == DependencyType::Host { builder.copy(&path, &sysroot_host_dst.join(path.file_name().unwrap())); } else { builder.copy(&path, &sysroot_dst.join(path.file_name().unwrap())); @@ -852,7 +860,7 @@ pub fn run_cargo( cargo: Cargo, tail_args: Vec, stamp: &Path, - additional_target_deps: Vec, + additional_target_deps: Vec<(PathBuf, DependencyType)>, is_check: bool, ) -> Vec { if builder.config.dry_run { @@ -903,7 +911,7 @@ pub fn run_cargo( if filename.starts_with(&host_root_dir) { // Unless it's a proc macro used in the compiler if crate_types.iter().any(|t| t == "proc-macro") { - deps.push((filename.to_path_buf(), true)); + deps.push((filename.to_path_buf(), DependencyType::Host)); } continue; } @@ -911,7 +919,7 @@ pub fn run_cargo( // If this was output in the `deps` dir then this is a precise file // name (hash included) so we start tracking it. if filename.starts_with(&target_deps_dir) { - deps.push((filename.to_path_buf(), false)); + deps.push((filename.to_path_buf(), DependencyType::Target)); continue; } @@ -963,17 +971,21 @@ pub fn run_cargo( let candidate = format!("{}.lib", path_to_add); let candidate = PathBuf::from(candidate); if candidate.exists() { - deps.push((candidate, false)); + deps.push((candidate, DependencyType::Target)); } } - deps.push((path_to_add.into(), false)); + deps.push((path_to_add.into(), DependencyType::Target)); } - deps.extend(additional_target_deps.into_iter().map(|d| (d, false))); + deps.extend(additional_target_deps); deps.sort(); let mut new_contents = Vec::new(); - for (dep, proc_macro) in deps.iter() { - new_contents.extend(if *proc_macro { b"h" } else { b"t" }); + for (dep, dependency_type) in deps.iter() { + new_contents.extend(match *dependency_type { + DependencyType::Host => b"h", + DependencyType::Target => b"t", + DependencyType::TargetSelfContained => b"s", + }); new_contents.extend(dep.to_str().unwrap().as_bytes()); new_contents.extend(b"\0"); } diff --git a/src/bootstrap/dist.rs b/src/bootstrap/dist.rs index 5e966d7055bf..77dd9c784bad 100644 --- a/src/bootstrap/dist.rs +++ b/src/bootstrap/dist.rs @@ -22,7 +22,7 @@ use crate::channel; use crate::compile; use crate::tool::{self, Tool}; use crate::util::{exe, is_dylib, timeit}; -use crate::{Compiler, Mode, LLVM_TOOLS}; +use crate::{Compiler, DependencyType, Mode, LLVM_TOOLS}; use time::{self, Timespec}; pub fn pkgname(builder: &Builder<'_>, component: &str) -> String { @@ -651,8 +651,8 @@ fn skip_host_target_lib(builder: &Builder<'_>, compiler: Compiler) -> bool { fn copy_target_libs(builder: &Builder<'_>, target: &str, image: &Path, stamp: &Path) { let dst = image.join("lib/rustlib").join(target).join("lib"); t!(fs::create_dir_all(&dst)); - for (path, host) in builder.read_stamp_file(stamp) { - if !host || builder.config.build == target { + for (path, dependency_type) in builder.read_stamp_file(stamp) { + if dependency_type != DependencyType::Host || builder.config.build == target { builder.copy(&path, &dst.join(path.file_name().unwrap())); } } diff --git a/src/bootstrap/lib.rs b/src/bootstrap/lib.rs index 8d8a036caef8..db861cb70137 100644 --- a/src/bootstrap/lib.rs +++ b/src/bootstrap/lib.rs @@ -280,6 +280,17 @@ impl Crate { } } +/// When building Rust various objects are handled differently. +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] +pub enum DependencyType { + /// Libraries originating from proc-macros. + Host, + /// Typical Rust libraries. + Target, + /// Non Rust libraries and objects shipped to ease usage of certain targets. + TargetSelfContained, +} + /// The various "modes" of invoking Cargo. /// /// These entries currently correspond to the various output directories of the @@ -1097,7 +1108,7 @@ impl Build { ret } - fn read_stamp_file(&self, stamp: &Path) -> Vec<(PathBuf, bool)> { + fn read_stamp_file(&self, stamp: &Path) -> Vec<(PathBuf, DependencyType)> { if self.config.dry_run { return Vec::new(); } @@ -1110,9 +1121,14 @@ impl Build { if part.is_empty() { continue; } - let host = part[0] as char == 'h'; + let dependency_type = match part[0] as char { + 'h' => DependencyType::Host, + 's' => DependencyType::TargetSelfContained, + 't' => DependencyType::Target, + _ => unreachable!(), + }; let path = PathBuf::from(t!(str::from_utf8(&part[1..]))); - paths.push((path, host)); + paths.push((path, dependency_type)); } paths } From 5d298836f2254144f80e56ee37af44ac79f3eb2c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20Miku=C5=82a?= Date: Thu, 11 Jun 2020 18:12:19 +0200 Subject: [PATCH 018/123] Move some libs to self-contained directory --- src/bootstrap/compile.rs | 24 +++++++++++++++++------- src/bootstrap/dist.rs | 13 +++++++++++-- src/librustc_codegen_ssa/back/link.rs | 7 +++++++ 3 files changed, 35 insertions(+), 9 deletions(-) diff --git a/src/bootstrap/compile.rs b/src/bootstrap/compile.rs index 5fae7277149d..4b739da91ce1 100644 --- a/src/bootstrap/compile.rs +++ b/src/bootstrap/compile.rs @@ -172,7 +172,14 @@ fn copy_self_contained_objects( compiler: &Compiler, target: Interned, ) -> Vec<(PathBuf, DependencyType)> { - let libdir = builder.sysroot_libdir(*compiler, target); + // cfg(bootstrap) + // Remove when upgrading bootstrap compiler. + let libdir_self_contained = if compiler.stage == 0 { + builder.sysroot_libdir(*compiler, target).to_path_buf() + } else { + builder.sysroot_libdir(*compiler, target).join("self-contained") + }; + t!(fs::create_dir_all(&libdir_self_contained)); let mut target_deps = vec![]; // Copies the CRT objects. @@ -207,7 +214,7 @@ fn copy_self_contained_objects( } else if target.contains("windows-gnu") { for obj in ["crt2.o", "dllcrt2.o"].iter() { let src = compiler_file(builder, builder.cc(target), target, obj); - let target = libdir.join(obj); + let target = libdir_self_contained.join(obj); builder.copy(&src, &target); target_deps.push((target, DependencyType::TargetSelfContained)); } @@ -844,14 +851,17 @@ pub fn add_to_sysroot( sysroot_host_dst: &Path, stamp: &Path, ) { + let self_contained_dst = &sysroot_dst.join("self-contained"); t!(fs::create_dir_all(&sysroot_dst)); t!(fs::create_dir_all(&sysroot_host_dst)); + t!(fs::create_dir_all(&self_contained_dst)); for (path, dependency_type) in builder.read_stamp_file(stamp) { - if dependency_type == DependencyType::Host { - builder.copy(&path, &sysroot_host_dst.join(path.file_name().unwrap())); - } else { - builder.copy(&path, &sysroot_dst.join(path.file_name().unwrap())); - } + let dst = match dependency_type { + DependencyType::Host => sysroot_host_dst, + DependencyType::Target => sysroot_dst, + DependencyType::TargetSelfContained => self_contained_dst, + }; + builder.copy(&path, &dst.join(path.file_name().unwrap())); } } diff --git a/src/bootstrap/dist.rs b/src/bootstrap/dist.rs index 77dd9c784bad..08737d9a0474 100644 --- a/src/bootstrap/dist.rs +++ b/src/bootstrap/dist.rs @@ -321,7 +321,12 @@ fn make_win_dist( ); //Copy platform libs to platform-specific lib directory - let target_lib_dir = plat_root.join("lib").join("rustlib").join(target_triple).join("lib"); + let target_lib_dir = plat_root + .join("lib") + .join("rustlib") + .join(target_triple) + .join("lib") + .join("self-contained"); fs::create_dir_all(&target_lib_dir).expect("creating target_lib_dir failed"); for src in target_libs { builder.copy_to_folder(&src, &target_lib_dir); @@ -650,9 +655,13 @@ fn skip_host_target_lib(builder: &Builder<'_>, compiler: Compiler) -> bool { /// Copy stamped files into an image's `target/lib` directory. fn copy_target_libs(builder: &Builder<'_>, target: &str, image: &Path, stamp: &Path) { let dst = image.join("lib/rustlib").join(target).join("lib"); + let self_contained_dst = dst.join("self-contained"); t!(fs::create_dir_all(&dst)); + t!(fs::create_dir_all(&self_contained_dst)); for (path, dependency_type) in builder.read_stamp_file(stamp) { - if dependency_type != DependencyType::Host || builder.config.build == target { + if dependency_type == DependencyType::TargetSelfContained { + builder.copy(&path, &self_contained_dst.join(path.file_name().unwrap())); + } else if dependency_type == DependencyType::Target || builder.config.build == target { builder.copy(&path, &dst.join(path.file_name().unwrap())); } } diff --git a/src/librustc_codegen_ssa/back/link.rs b/src/librustc_codegen_ssa/back/link.rs index 53e3da3c0baf..c57b01dff280 100644 --- a/src/librustc_codegen_ssa/back/link.rs +++ b/src/librustc_codegen_ssa/back/link.rs @@ -1075,6 +1075,10 @@ fn get_object_file_path(sess: &Session, name: &str) -> PathBuf { if file_path.exists() { return file_path; } + let file_path = fs.get_lib_path().join("self-contained").join(name); + if file_path.exists() { + return file_path; + } for search_path in fs.search_paths() { let file_path = search_path.dir.join(name); if file_path.exists() { @@ -1470,6 +1474,9 @@ fn add_library_search_dirs(cmd: &mut dyn Linker, sess: &Session) { // The location of crates will be determined as needed. let lib_path = sess.target_filesearch(PathKind::All).get_lib_path(); cmd.include_path(&fix_windows_verbatim_for_gcc(&lib_path)); + + let lib_path = sess.target_filesearch(PathKind::All).get_lib_path().join("self-contained"); + cmd.include_path(&fix_windows_verbatim_for_gcc(&lib_path)); } /// Add options making relocation sections in the produced ELF files read-only From e9ac01a9beeae77a15badcec094a7a4da0bebecb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20Miku=C5=82a?= Date: Mon, 8 Jun 2020 12:47:56 +0200 Subject: [PATCH 019/123] Get self-contained directory path via dedicated function --- src/librustc_codegen_ssa/back/link.rs | 4 ++-- src/librustc_session/filesearch.rs | 4 ++++ 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/librustc_codegen_ssa/back/link.rs b/src/librustc_codegen_ssa/back/link.rs index c57b01dff280..1eef86f6c931 100644 --- a/src/librustc_codegen_ssa/back/link.rs +++ b/src/librustc_codegen_ssa/back/link.rs @@ -1075,7 +1075,7 @@ fn get_object_file_path(sess: &Session, name: &str) -> PathBuf { if file_path.exists() { return file_path; } - let file_path = fs.get_lib_path().join("self-contained").join(name); + let file_path = fs.get_selfcontained_lib_path().join(name); if file_path.exists() { return file_path; } @@ -1475,7 +1475,7 @@ fn add_library_search_dirs(cmd: &mut dyn Linker, sess: &Session) { let lib_path = sess.target_filesearch(PathKind::All).get_lib_path(); cmd.include_path(&fix_windows_verbatim_for_gcc(&lib_path)); - let lib_path = sess.target_filesearch(PathKind::All).get_lib_path().join("self-contained"); + let lib_path = sess.target_filesearch(PathKind::All).get_selfcontained_lib_path(); cmd.include_path(&fix_windows_verbatim_for_gcc(&lib_path)); } diff --git a/src/librustc_session/filesearch.rs b/src/librustc_session/filesearch.rs index e98746231fb3..37d7b0c7e1f7 100644 --- a/src/librustc_session/filesearch.rs +++ b/src/librustc_session/filesearch.rs @@ -41,6 +41,10 @@ impl<'a> FileSearch<'a> { make_target_lib_path(self.sysroot, self.triple) } + pub fn get_selfcontained_lib_path(&self) -> PathBuf { + self.get_lib_path().join("self-contained") + } + pub fn search(&self, mut pick: F) where F: FnMut(&SearchPathFile, PathKind) -> FileMatch, From 43905cd7501fd37090cb9de6069faaba761e514a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20Miku=C5=82a?= Date: Mon, 8 Jun 2020 13:20:26 +0200 Subject: [PATCH 020/123] Move shipped MinGW linker to self-contained dir --- src/bootstrap/dist.rs | 7 ++++++- src/librustc_session/filesearch.rs | 2 +- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/src/bootstrap/dist.rs b/src/bootstrap/dist.rs index 08737d9a0474..df1253f3f9e7 100644 --- a/src/bootstrap/dist.rs +++ b/src/bootstrap/dist.rs @@ -306,7 +306,12 @@ fn make_win_dist( } //Copy platform tools to platform-specific bin directory - let target_bin_dir = plat_root.join("lib").join("rustlib").join(target_triple).join("bin"); + let target_bin_dir = plat_root + .join("lib") + .join("rustlib") + .join(target_triple) + .join("bin") + .join("self-contained"); fs::create_dir_all(&target_bin_dir).expect("creating target_bin_dir failed"); for src in target_tools { builder.copy_to_folder(&src, &target_bin_dir); diff --git a/src/librustc_session/filesearch.rs b/src/librustc_session/filesearch.rs index 37d7b0c7e1f7..5586b82b0edc 100644 --- a/src/librustc_session/filesearch.rs +++ b/src/librustc_session/filesearch.rs @@ -98,7 +98,7 @@ impl<'a> FileSearch<'a> { p.push(RUST_LIB_DIR); p.push(&self.triple); p.push("bin"); - vec![p] + vec![p.clone(), p.join("self-contained")] } } From f0d2e78d39d0efdb431af0b59c498d532d8146e2 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Fri, 12 Jun 2020 18:17:30 +0200 Subject: [PATCH 021/123] add raw_ref macros --- src/libcore/ptr/mod.rs | 67 ++++++++++++++++++++++++++++++++++++++++++ src/libstd/lib.rs | 1 + 2 files changed, 68 insertions(+) diff --git a/src/libcore/ptr/mod.rs b/src/libcore/ptr/mod.rs index 777284ca5c09..199f08c3d505 100644 --- a/src/libcore/ptr/mod.rs +++ b/src/libcore/ptr/mod.rs @@ -1399,3 +1399,70 @@ fnptr_impls_args! { A, B, C, D, E, F, G, H, I } fnptr_impls_args! { A, B, C, D, E, F, G, H, I, J } fnptr_impls_args! { A, B, C, D, E, F, G, H, I, J, K } fnptr_impls_args! { A, B, C, D, E, F, G, H, I, J, K, L } + +/// Create a `const` raw pointer to a place, without creating an intermediate reference. +/// +/// Creating a reference with `&`/`&mut` is only allowed if the pointer is properly aligned +/// and points to initialized data. For cases where those requirements do not hold, +/// raw pointers should be used instead. However, `&expr as *const _` creates a reference +/// before casting it to a raw pointer, and that reference is subject to the same rules +/// as all other references. This macro can create a raw pointer *without* creating +/// a reference first. +/// +/// # Example +/// +/// ``` +/// #![feature(raw_ref_macros)] +/// use std::ptr; +/// +/// #[repr(packed)] +/// struct Packed { +/// f1: u8, +/// f2: u16, +/// } +/// +/// let packed = Packed { f1: 1, f2: 2 }; +/// // `&packed.f2` would create an unaligned reference, and thus be Undefined Behavior! +/// let raw_f2 = ptr::raw_const!(packed.f2); +/// assert_eq!(unsafe { raw_f2.read_unaligned() }, 2); +/// ``` +#[unstable(feature = "raw_ref_macros", issue = "none")] +#[rustc_macro_transparency = "semitransparent"] +#[allow_internal_unstable(raw_ref_op)] +pub macro raw_const($e:expr) { + &raw const $e +} + +/// Create a `mut` raw pointer to a place, without creating an intermediate reference. +/// +/// Creating a reference with `&`/`&mut` is only allowed if the pointer is properly aligned +/// and points to initialized data. For cases where those requirements do not hold, +/// raw pointers should be used instead. However, `&mut expr as *mut _` creates a reference +/// before casting it to a raw pointer, and that reference is subject to the same rules +/// as all other references. This macro can create a raw pointer *without* creating +/// a reference first. +/// +/// # Example +/// +/// ``` +/// #![feature(raw_ref_macros)] +/// use std::ptr; +/// +/// #[repr(packed)] +/// struct Packed { +/// f1: u8, +/// f2: u16, +/// } +/// +/// let mut packed = Packed { f1: 1, f2: 2 }; +/// // `&mut packed.f2` would create an unaligned reference, and thus be Undefined Behavior! +/// let raw_f2 = ptr::raw_mut!(packed.f2); +/// unsafe { raw_f2.write_unaligned(42); } +/// assert_eq!({packed.f2}, 42); // `{...}` forces copying the field instead of creating a reference. +/// ``` +#[unstable(feature = "raw_ref_macros", issue = "none")] +#[rustc_macro_transparency = "semitransparent"] +#[allow_internal_unstable(raw_ref_op)] +pub macro raw_mut($e:expr) { + &raw mut $e +} diff --git a/src/libstd/lib.rs b/src/libstd/lib.rs index d6493454db59..ef699ede2a14 100644 --- a/src/libstd/lib.rs +++ b/src/libstd/lib.rs @@ -298,6 +298,7 @@ #![feature(prelude_import)] #![feature(ptr_internals)] #![feature(raw)] +#![feature(raw_ref_macros)] #![feature(renamed_spin_loop)] #![feature(rustc_attrs)] #![feature(rustc_private)] From d40e624a3625c7c2d68c949435fd883cd43dd065 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Mi=C4=85sko?= Date: Fri, 5 Jun 2020 00:00:00 +0000 Subject: [PATCH 022/123] compiletest: Add directives to detect sanitizer support Add needs-sanitizer-{address,leak,memory,thread} directive indicating that test requires target with support for specific sanitizer. This is an addition to the existing needs-sanitizer-support directive indicating that test requires a sanitizer runtime library. --- .../codegen/sanitizer-memory-track-orgins.rs | 4 +-- .../codegen/sanitizer-no-sanitize-inlining.rs | 6 ++-- src/test/codegen/sanitizer-no-sanitize.rs | 2 +- src/test/codegen/sanitizer-recover.rs | 5 ++-- .../sanitizer-cdylib-link/Makefile | 2 +- .../sanitizer-dylib-link/Makefile | 2 +- .../sanitizer-staticlib-link/Makefile | 2 +- src/test/rustdoc/sanitizer-option.rs | 1 + src/test/ui/sanitize/address.rs | 2 +- src/test/ui/sanitize/badfree.rs | 2 +- src/test/ui/sanitize/cfg.rs | 6 ++-- .../sanitize/issue-72154-lifetime-markers.rs | 2 +- src/test/ui/sanitize/leak.rs | 2 +- src/test/ui/sanitize/memory.rs | 3 +- .../new-llvm-pass-manager-thin-lto.rs | 2 +- src/test/ui/sanitize/thread.rs | 2 +- src/test/ui/sanitize/use-after-scope.rs | 2 +- src/tools/compiletest/src/header.rs | 28 +++++++++++++++---- src/tools/compiletest/src/header/tests.rs | 19 +++++++++++++ src/tools/compiletest/src/util.rs | 11 ++++++++ 20 files changed, 75 insertions(+), 30 deletions(-) diff --git a/src/test/codegen/sanitizer-memory-track-orgins.rs b/src/test/codegen/sanitizer-memory-track-orgins.rs index 8ea41c5d44bb..4bd50508d152 100644 --- a/src/test/codegen/sanitizer-memory-track-orgins.rs +++ b/src/test/codegen/sanitizer-memory-track-orgins.rs @@ -1,9 +1,7 @@ // Verifies that MemorySanitizer track-origins level can be controlled // with -Zsanitizer-memory-track-origins option. // -// needs-sanitizer-support -// only-linux -// only-x86_64 +// needs-sanitizer-memory // revisions:MSAN-0 MSAN-1 MSAN-2 MSAN-1-LTO MSAN-2-LTO // //[MSAN-0] compile-flags: -Zsanitizer=memory diff --git a/src/test/codegen/sanitizer-no-sanitize-inlining.rs b/src/test/codegen/sanitizer-no-sanitize-inlining.rs index d96e76618d32..b00441e4fc5a 100644 --- a/src/test/codegen/sanitizer-no-sanitize-inlining.rs +++ b/src/test/codegen/sanitizer-no-sanitize-inlining.rs @@ -1,11 +1,9 @@ // Verifies that no_sanitize attribute prevents inlining when // given sanitizer is enabled, but has no effect on inlining otherwise. // -// needs-sanitizer-support -// only-x86_64 -// +// needs-sanitizer-address +// needs-sanitizer-leak // revisions: ASAN LSAN -// //[ASAN] compile-flags: -Zsanitizer=address -C opt-level=3 -Z mir-opt-level=3 //[LSAN] compile-flags: -Zsanitizer=leak -C opt-level=3 -Z mir-opt-level=3 diff --git a/src/test/codegen/sanitizer-no-sanitize.rs b/src/test/codegen/sanitizer-no-sanitize.rs index dfceb28c8dd1..1b2b18822e63 100644 --- a/src/test/codegen/sanitizer-no-sanitize.rs +++ b/src/test/codegen/sanitizer-no-sanitize.rs @@ -1,7 +1,7 @@ // Verifies that no_sanitze attribute can be used to // selectively disable sanitizer instrumentation. // -// needs-sanitizer-support +// needs-sanitizer-address // compile-flags: -Zsanitizer=address #![crate_type="lib"] diff --git a/src/test/codegen/sanitizer-recover.rs b/src/test/codegen/sanitizer-recover.rs index 05b4ab5653cc..719f219ce4ef 100644 --- a/src/test/codegen/sanitizer-recover.rs +++ b/src/test/codegen/sanitizer-recover.rs @@ -1,9 +1,8 @@ // Verifies that AddressSanitizer and MemorySanitizer // recovery mode can be enabled with -Zsanitizer-recover. // -// needs-sanitizer-support -// only-linux -// only-x86_64 +// needs-sanitizer-address +// needs-sanitizer-memory // revisions:ASAN ASAN-RECOVER MSAN MSAN-RECOVER MSAN-RECOVER-LTO // no-prefer-dynamic // diff --git a/src/test/run-make-fulldeps/sanitizer-cdylib-link/Makefile b/src/test/run-make-fulldeps/sanitizer-cdylib-link/Makefile index 5d46be87eac6..b11d4c4cab7c 100644 --- a/src/test/run-make-fulldeps/sanitizer-cdylib-link/Makefile +++ b/src/test/run-make-fulldeps/sanitizer-cdylib-link/Makefile @@ -1,5 +1,5 @@ # needs-sanitizer-support -# only-x86_64 +# needs-sanitizer-address # only-linux -include ../tools.mk diff --git a/src/test/run-make-fulldeps/sanitizer-dylib-link/Makefile b/src/test/run-make-fulldeps/sanitizer-dylib-link/Makefile index f62c3a6654ed..c2ebd2a6d8ca 100644 --- a/src/test/run-make-fulldeps/sanitizer-dylib-link/Makefile +++ b/src/test/run-make-fulldeps/sanitizer-dylib-link/Makefile @@ -1,5 +1,5 @@ # needs-sanitizer-support -# only-x86_64 +# needs-sanitizer-address # only-linux -include ../tools.mk diff --git a/src/test/run-make-fulldeps/sanitizer-staticlib-link/Makefile b/src/test/run-make-fulldeps/sanitizer-staticlib-link/Makefile index f56475b441f1..5ceff16471ce 100644 --- a/src/test/run-make-fulldeps/sanitizer-staticlib-link/Makefile +++ b/src/test/run-make-fulldeps/sanitizer-staticlib-link/Makefile @@ -1,5 +1,5 @@ # needs-sanitizer-support -# only-x86_64 +# needs-sanitizer-address # only-linux -include ../tools.mk diff --git a/src/test/rustdoc/sanitizer-option.rs b/src/test/rustdoc/sanitizer-option.rs index 6af9ed3e33f6..a79b37ee0821 100644 --- a/src/test/rustdoc/sanitizer-option.rs +++ b/src/test/rustdoc/sanitizer-option.rs @@ -1,4 +1,5 @@ // needs-sanitizer-support +// needs-sanitizer-address // compile-flags: --test -Z sanitizer=address // // #43031: Verify that rustdoc passes `-Z` options to rustc. Use an extern diff --git a/src/test/ui/sanitize/address.rs b/src/test/ui/sanitize/address.rs index f8650cd86d51..cee73b0425ad 100644 --- a/src/test/ui/sanitize/address.rs +++ b/src/test/ui/sanitize/address.rs @@ -1,5 +1,5 @@ // needs-sanitizer-support -// only-x86_64 +// needs-sanitizer-address // // compile-flags: -Z sanitizer=address -O -g // diff --git a/src/test/ui/sanitize/badfree.rs b/src/test/ui/sanitize/badfree.rs index 1ca082c8b470..095a6f4697b1 100644 --- a/src/test/ui/sanitize/badfree.rs +++ b/src/test/ui/sanitize/badfree.rs @@ -1,5 +1,5 @@ // needs-sanitizer-support -// only-x86_64 +// needs-sanitizer-address // // compile-flags: -Z sanitizer=address -O // diff --git a/src/test/ui/sanitize/cfg.rs b/src/test/ui/sanitize/cfg.rs index 9c198543a866..79dfe58f04d0 100644 --- a/src/test/ui/sanitize/cfg.rs +++ b/src/test/ui/sanitize/cfg.rs @@ -2,8 +2,10 @@ // the `#[cfg(sanitize = "option")]` attribute is configured. // needs-sanitizer-support -// only-linux -// only-x86_64 +// needs-sanitizer-address +// needs-sanitizer-leak +// needs-sanitizer-memory +// needs-sanitizer-thread // check-pass // revisions: address leak memory thread //[address]compile-flags: -Zsanitizer=address --cfg address diff --git a/src/test/ui/sanitize/issue-72154-lifetime-markers.rs b/src/test/ui/sanitize/issue-72154-lifetime-markers.rs index 458f99143b64..b2e182238ce2 100644 --- a/src/test/ui/sanitize/issue-72154-lifetime-markers.rs +++ b/src/test/ui/sanitize/issue-72154-lifetime-markers.rs @@ -4,7 +4,7 @@ // miscompilation which was subsequently detected by AddressSanitizer as UB. // // needs-sanitizer-support -// only-x86_64 +// needs-sanitizer-address // // compile-flags: -Copt-level=0 -Zsanitizer=address // run-pass diff --git a/src/test/ui/sanitize/leak.rs b/src/test/ui/sanitize/leak.rs index 5c2f2cb4e868..c9f10fe4f467 100644 --- a/src/test/ui/sanitize/leak.rs +++ b/src/test/ui/sanitize/leak.rs @@ -1,5 +1,5 @@ // needs-sanitizer-support -// only-x86_64 +// needs-sanitizer-leak // // compile-flags: -Z sanitizer=leak -O // diff --git a/src/test/ui/sanitize/memory.rs b/src/test/ui/sanitize/memory.rs index 3e1cf4509a31..a26649a58001 100644 --- a/src/test/ui/sanitize/memory.rs +++ b/src/test/ui/sanitize/memory.rs @@ -1,6 +1,5 @@ // needs-sanitizer-support -// only-linux -// only-x86_64 +// needs-sanitizer-memory // // compile-flags: -Z sanitizer=memory -Zsanitizer-memory-track-origins -O // diff --git a/src/test/ui/sanitize/new-llvm-pass-manager-thin-lto.rs b/src/test/ui/sanitize/new-llvm-pass-manager-thin-lto.rs index d0984bbe65fd..64d6ccf34091 100644 --- a/src/test/ui/sanitize/new-llvm-pass-manager-thin-lto.rs +++ b/src/test/ui/sanitize/new-llvm-pass-manager-thin-lto.rs @@ -4,7 +4,7 @@ // // min-llvm-version 9.0 // needs-sanitizer-support -// only-x86_64 +// needs-sanitizer-address // // no-prefer-dynamic // revisions: opt0 opt1 diff --git a/src/test/ui/sanitize/thread.rs b/src/test/ui/sanitize/thread.rs index 26590be8b187..c70cf5accc07 100644 --- a/src/test/ui/sanitize/thread.rs +++ b/src/test/ui/sanitize/thread.rs @@ -11,7 +11,7 @@ // would occasionally fail, making test flaky. // // needs-sanitizer-support -// only-x86_64 +// needs-sanitizer-thread // // compile-flags: -Z sanitizer=thread -O // diff --git a/src/test/ui/sanitize/use-after-scope.rs b/src/test/ui/sanitize/use-after-scope.rs index 6a2067e157af..30be2ae6f090 100644 --- a/src/test/ui/sanitize/use-after-scope.rs +++ b/src/test/ui/sanitize/use-after-scope.rs @@ -1,5 +1,5 @@ // needs-sanitizer-support -// only-x86_64 +// needs-sanitizer-address // // compile-flags: -Zsanitizer=address // run-fail diff --git a/src/tools/compiletest/src/header.rs b/src/tools/compiletest/src/header.rs index 9d1940dd4d6c..9614707433e1 100644 --- a/src/tools/compiletest/src/header.rs +++ b/src/tools/compiletest/src/header.rs @@ -43,6 +43,10 @@ impl EarlyProps { let mut props = EarlyProps::default(); let rustc_has_profiler_support = env::var_os("RUSTC_PROFILER_SUPPORT").is_some(); let rustc_has_sanitizer_support = env::var_os("RUSTC_SANITIZER_SUPPORT").is_some(); + let has_asan = util::ASAN_SUPPORTED_TARGETS.contains(&&*config.target); + let has_lsan = util::LSAN_SUPPORTED_TARGETS.contains(&&*config.target); + let has_msan = util::MSAN_SUPPORTED_TARGETS.contains(&&*config.target); + let has_tsan = util::TSAN_SUPPORTED_TARGETS.contains(&&*config.target); iter_header(testfile, None, rdr, &mut |ln| { // we should check if any only- exists and if it exists @@ -74,7 +78,25 @@ impl EarlyProps { props.ignore = true; } - if !rustc_has_sanitizer_support && config.parse_needs_sanitizer_support(ln) { + if !rustc_has_sanitizer_support + && config.parse_name_directive(ln, "needs-sanitizer-support") + { + props.ignore = true; + } + + if !has_asan && config.parse_name_directive(ln, "needs-sanitizer-address") { + props.ignore = true; + } + + if !has_lsan && config.parse_name_directive(ln, "needs-sanitizer-leak") { + props.ignore = true; + } + + if !has_msan && config.parse_name_directive(ln, "needs-sanitizer-memory") { + props.ignore = true; + } + + if !has_tsan && config.parse_name_directive(ln, "needs-sanitizer-thread") { props.ignore = true; } @@ -829,10 +851,6 @@ impl Config { self.parse_name_directive(line, "needs-profiler-support") } - fn parse_needs_sanitizer_support(&self, line: &str) -> bool { - self.parse_name_directive(line, "needs-sanitizer-support") - } - /// Parses a name-value directive which contains config-specific information, e.g., `ignore-x86` /// or `normalize-stderr-32bit`. fn parse_cfg_name_directive(&self, line: &str, prefix: &str) -> ParsedNameDirective { diff --git a/src/tools/compiletest/src/header/tests.rs b/src/tools/compiletest/src/header/tests.rs index 31d991e0c2f8..036409fbf070 100644 --- a/src/tools/compiletest/src/header/tests.rs +++ b/src/tools/compiletest/src/header/tests.rs @@ -195,3 +195,22 @@ fn debugger() { config.debugger = Some(Debugger::Lldb); assert!(parse_rs(&config, "// ignore-lldb").ignore); } + +#[test] +fn sanitizers() { + let mut config = config(); + + // Target that supports all sanitizers: + config.target = "x86_64-unknown-linux-gnu".to_owned(); + assert!(!parse_rs(&config, "// needs-sanitizer-address").ignore); + assert!(!parse_rs(&config, "// needs-sanitizer-leak").ignore); + assert!(!parse_rs(&config, "// needs-sanitizer-memory").ignore); + assert!(!parse_rs(&config, "// needs-sanitizer-thread").ignore); + + // Target that doesn't support sanitizers: + config.target = "wasm32-unknown-emscripten".to_owned(); + assert!(parse_rs(&config, "// needs-sanitizer-address").ignore); + assert!(parse_rs(&config, "// needs-sanitizer-leak").ignore); + assert!(parse_rs(&config, "// needs-sanitizer-memory").ignore); + assert!(parse_rs(&config, "// needs-sanitizer-thread").ignore); +} diff --git a/src/tools/compiletest/src/util.rs b/src/tools/compiletest/src/util.rs index c61bee0f8d9e..b9087dee6174 100644 --- a/src/tools/compiletest/src/util.rs +++ b/src/tools/compiletest/src/util.rs @@ -81,6 +81,17 @@ const ARCH_TABLE: &'static [(&'static str, &'static str)] = &[ ("xcore", "xcore"), ]; +pub const ASAN_SUPPORTED_TARGETS: &'static [&'static str] = + &["aarch64-fuchsia", "x86_64-apple-darwin", "x86_64-fuchsia", "x86_64-unknown-linux-gnu"]; + +pub const LSAN_SUPPORTED_TARGETS: &'static [&'static str] = + &["x86_64-apple-darwin", "x86_64-unknown-linux-gnu"]; + +pub const MSAN_SUPPORTED_TARGETS: &'static [&'static str] = &["x86_64-unknown-linux-gnu"]; + +pub const TSAN_SUPPORTED_TARGETS: &'static [&'static str] = + &["x86_64-apple-darwin", "x86_64-unknown-linux-gnu"]; + pub fn matches_os(triple: &str, name: &str) -> bool { // For the wasm32 bare target we ignore anything also ignored on emscripten // and then we also recognize `wasm32-bare` as the os for the target From 724dfba460e2c98311cacb5d4f6bb6da36ceec67 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Sat, 13 Jun 2020 15:05:37 +0200 Subject: [PATCH 023/123] Clean up some weird command strings --- src/librustdoc/lib.rs | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/src/librustdoc/lib.rs b/src/librustdoc/lib.rs index 82d6cda986a9..95d113166e00 100644 --- a/src/librustdoc/lib.rs +++ b/src/librustdoc/lib.rs @@ -165,9 +165,8 @@ fn opts() -> Vec { o.optmulti( "", "passes", - "list of passes to also run, you might want \ - to pass it multiple times; a value of `list` \ - will print available passes", + "list of passes to also run, you might want to pass it multiple times; a value of \ + `list` will print available passes", "PASSES", ) }), @@ -248,8 +247,8 @@ fn opts() -> Vec { "e", "extend-css", "To add some CSS rules with a given file to generate doc with your \ - own theme. However, your theme might break if the rustdoc's generated HTML \ - changes, so be careful!", + own theme. However, your theme might break if the rustdoc's generated HTML \ + changes, so be careful!", "PATH", ) }), @@ -262,7 +261,7 @@ fn opts() -> Vec { "", "playground-url", "URL to send code snippets to, may be reset by --markdown-playground-url \ - or `#![doc(html_playground_url=...)]`", + or `#![doc(html_playground_url=...)]`", "URL", ) }), @@ -276,8 +275,7 @@ fn opts() -> Vec { o.optflag( "", "sort-modules-by-appearance", - "sort modules by where they appear in the \ - program, rather than alphabetically", + "sort modules by where they appear in the program, rather than alphabetically", ) }), stable("theme", |o| { @@ -358,7 +356,7 @@ fn opts() -> Vec { "", "static-root-path", "Path string to force loading static files from in output pages. \ - If not set, uses combinations of '../' to reach the documentation root.", + If not set, uses combinations of '../' to reach the documentation root.", "PATH", ) }), From 0687b78d56b93d28ceeaa05e794849757d7341a4 Mon Sep 17 00:00:00 2001 From: Eric Huss Date: Sat, 13 Jun 2020 10:29:56 -0700 Subject: [PATCH 024/123] Speed up bootstrap a little. --- src/bootstrap/flags.rs | 9 ++--- src/bootstrap/lib.rs | 2 +- src/bootstrap/metadata.rs | 69 ++++++++++++++++++--------------------- src/bootstrap/test.rs | 4 +-- 4 files changed, 36 insertions(+), 48 deletions(-) diff --git a/src/bootstrap/flags.rs b/src/bootstrap/flags.rs index cfaa43f39709..03b7028b2fa5 100644 --- a/src/bootstrap/flags.rs +++ b/src/bootstrap/flags.rs @@ -10,11 +10,9 @@ use std::process; use getopts::Options; use crate::builder::Builder; -use crate::config::Config; -use crate::metadata; -use crate::{Build, DocTests}; - use crate::cache::{Interned, INTERNER}; +use crate::config::Config; +use crate::{Build, DocTests}; /// Deserialized version of all flags for this compile. pub struct Flags { @@ -444,8 +442,7 @@ Arguments: // All subcommands except `clean` can have an optional "Available paths" section if matches.opt_present("verbose") { let config = Config::parse(&["build".to_string()]); - let mut build = Build::new(config); - metadata::build(&mut build); + let build = Build::new(config); let maybe_rules_help = Builder::get_help(&build, subcommand.as_str()); extra_help.push_str(maybe_rules_help.unwrap_or_default().as_str()); diff --git a/src/bootstrap/lib.rs b/src/bootstrap/lib.rs index 8d8a036caef8..a125b49fc01e 100644 --- a/src/bootstrap/lib.rs +++ b/src/bootstrap/lib.rs @@ -271,7 +271,7 @@ struct Crate { impl Crate { fn is_local(&self, build: &Build) -> bool { - self.path.starts_with(&build.config.src) && !self.path.to_string_lossy().ends_with("_shim") + self.path.starts_with(&build.config.src) } fn local_path(&self, build: &Build) -> PathBuf { diff --git a/src/bootstrap/metadata.rs b/src/bootstrap/metadata.rs index 292aa3b1e24a..185f0ddb831e 100644 --- a/src/bootstrap/metadata.rs +++ b/src/bootstrap/metadata.rs @@ -35,17 +35,39 @@ struct ResolveNode { } pub fn build(build: &mut Build) { - let mut resolves = Vec::new(); - build_krate(&build.std_features(), build, &mut resolves, "src/libstd"); - build_krate("", build, &mut resolves, "src/libtest"); - build_krate(&build.rustc_features(), build, &mut resolves, "src/rustc"); - - let mut id2name = HashMap::with_capacity(build.crates.len()); - for (name, krate) in build.crates.iter() { - id2name.insert(krate.id.clone(), name.clone()); + // Run `cargo metadata` to figure out what crates we're testing. + let features: Vec<_> = build + .std_features() + .split_whitespace() + .map(|f| format!("test/{}", f)) + .chain(build.rustc_features().split_whitespace().map(|f| format!("rustc-main/{}", f))) + .collect(); + let mut cargo = Command::new(&build.initial_cargo); + cargo + .arg("metadata") + .arg("--format-version") + .arg("1") + .arg("--features") + .arg(features.join(",")) + .arg("-Zpackage-features") + .arg("--manifest-path") + .arg(build.src.join("Cargo.toml")) + .env("RUSTC_BOOTSTRAP", "1"); + let output = output(&mut cargo); + let output: Output = serde_json::from_str(&output).unwrap(); + for package in output.packages { + if package.source.is_none() { + let name = INTERNER.intern_string(package.name); + let mut path = PathBuf::from(package.manifest_path); + path.pop(); + build.crates.insert(name, Crate { name, id: package.id, deps: HashSet::new(), path }); + } } - for node in resolves { + let id2name: HashMap<_, _> = + build.crates.iter().map(|(name, krate)| (krate.id.clone(), name.clone())).collect(); + + for node in output.resolve.nodes { let name = match id2name.get(&node.id) { Some(name) => name, None => continue, @@ -61,32 +83,3 @@ pub fn build(build: &mut Build) { } } } - -fn build_krate(features: &str, build: &mut Build, resolves: &mut Vec, krate: &str) { - // Run `cargo metadata` to figure out what crates we're testing. - // - // Down below we're going to call `cargo test`, but to test the right set - // of packages we're going to have to know what `-p` arguments to pass it - // to know what crates to test. Here we run `cargo metadata` to learn about - // the dependency graph and what `-p` arguments there are. - let mut cargo = Command::new(&build.initial_cargo); - cargo - .arg("metadata") - .arg("--format-version") - .arg("1") - .arg("--features") - .arg(features) - .arg("--manifest-path") - .arg(build.src.join(krate).join("Cargo.toml")); - let output = output(&mut cargo); - let output: Output = serde_json::from_str(&output).unwrap(); - for package in output.packages { - if package.source.is_none() { - let name = INTERNER.intern_string(package.name); - let mut path = PathBuf::from(package.manifest_path); - path.pop(); - build.crates.insert(name, Crate { name, id: package.id, deps: HashSet::new(), path }); - } - } - resolves.extend(output.resolve.nodes); -} diff --git a/src/bootstrap/test.rs b/src/bootstrap/test.rs index 163132f56342..8659acf1cc5a 100644 --- a/src/bootstrap/test.rs +++ b/src/bootstrap/test.rs @@ -1654,9 +1654,7 @@ impl Step for Crate { fn should_run(mut run: ShouldRun<'_>) -> ShouldRun<'_> { let builder = run.builder; for krate in run.builder.in_tree_crates("test") { - if !(krate.name.starts_with("rustc_") && krate.name.ends_with("san")) { - run = run.path(krate.local_path(&builder).to_str().unwrap()); - } + run = run.path(krate.local_path(&builder).to_str().unwrap()); } run } From 4606168dd508007fb1014b6ab12b27e320e07038 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Sat, 13 Jun 2020 11:12:29 -0700 Subject: [PATCH 025/123] Make new type param suggestion more targetted Do not suggest new type param when encountering a missing type in an ADT field with generic parameters. Fix #72640. --- src/librustc_resolve/build_reduced_graph.rs | 1 + src/librustc_resolve/late/diagnostics.rs | 2 +- src/librustc_resolve/lib.rs | 14 ++++++++------ .../ui/suggestions/type-not-found-in-adt-field.rs | 5 +++++ .../suggestions/type-not-found-in-adt-field.stderr | 9 +++++++++ 5 files changed, 24 insertions(+), 7 deletions(-) create mode 100644 src/test/ui/suggestions/type-not-found-in-adt-field.rs create mode 100644 src/test/ui/suggestions/type-not-found-in-adt-field.stderr diff --git a/src/librustc_resolve/build_reduced_graph.rs b/src/librustc_resolve/build_reduced_graph.rs index 2ae063660e38..8661af6d7a1d 100644 --- a/src/librustc_resolve/build_reduced_graph.rs +++ b/src/librustc_resolve/build_reduced_graph.rs @@ -485,6 +485,7 @@ impl<'a, 'b> BuildReducedGraphVisitor<'a, 'b> { module_path.push(Segment { ident: Ident { name: kw::PathRoot, span: source.ident.span }, id: Some(self.r.next_node_id()), + has_args: false, }); source.ident.name = crate_name; } diff --git a/src/librustc_resolve/late/diagnostics.rs b/src/librustc_resolve/late/diagnostics.rs index b1a1f8725a18..28ff89f66925 100644 --- a/src/librustc_resolve/late/diagnostics.rs +++ b/src/librustc_resolve/late/diagnostics.rs @@ -920,7 +920,7 @@ impl<'a> LateResolutionVisitor<'a, '_, '_> { path: &[Segment], ) -> Option<(Span, &'static str, String, Applicability)> { let ident = match path { - [segment] => segment.ident, + [segment] if !segment.has_args => segment.ident, _ => return None, }; match ( diff --git a/src/librustc_resolve/lib.rs b/src/librustc_resolve/lib.rs index 6bd73877fab7..f7ec919fa04e 100644 --- a/src/librustc_resolve/lib.rs +++ b/src/librustc_resolve/lib.rs @@ -225,13 +225,15 @@ enum VisResolutionError<'a> { ModuleOnly(Span), } -// A minimal representation of a path segment. We use this in resolve because -// we synthesize 'path segments' which don't have the rest of an AST or HIR -// `PathSegment`. +/// A minimal representation of a path segment. We use this in resolve because we synthesize 'path +/// segments' which don't have the rest of an AST or HIR `PathSegment`. #[derive(Clone, Copy, Debug)] pub struct Segment { ident: Ident, id: Option, + /// Signals whether this `PathSegment` has generic arguments. Used to avoid providing + /// nonsensical suggestions. + has_args: bool, } impl Segment { @@ -240,7 +242,7 @@ impl Segment { } fn from_ident(ident: Ident) -> Segment { - Segment { ident, id: None } + Segment { ident, id: None, has_args: false } } fn names_to_string(segments: &[Segment]) -> String { @@ -250,7 +252,7 @@ impl Segment { impl<'a> From<&'a ast::PathSegment> for Segment { fn from(seg: &'a ast::PathSegment) -> Segment { - Segment { ident: seg.ident, id: Some(seg.id) } + Segment { ident: seg.ident, id: Some(seg.id), has_args: seg.args.is_some() } } } @@ -2017,7 +2019,7 @@ impl<'a> Resolver<'a> { path, opt_ns, record_used, path_span, crate_lint, ); - for (i, &Segment { ident, id }) in path.iter().enumerate() { + for (i, &Segment { ident, id, has_args: _ }) in path.iter().enumerate() { debug!("resolve_path ident {} {:?} {:?}", i, ident, id); let record_segment_res = |this: &mut Self, res| { if record_used { diff --git a/src/test/ui/suggestions/type-not-found-in-adt-field.rs b/src/test/ui/suggestions/type-not-found-in-adt-field.rs new file mode 100644 index 000000000000..6bd42472f5a5 --- /dev/null +++ b/src/test/ui/suggestions/type-not-found-in-adt-field.rs @@ -0,0 +1,5 @@ +struct S { + m: Vec>, //~ ERROR cannot find type `Hashmap` in this scope + //~^ NOTE not found in this scope +} +fn main() {} diff --git a/src/test/ui/suggestions/type-not-found-in-adt-field.stderr b/src/test/ui/suggestions/type-not-found-in-adt-field.stderr new file mode 100644 index 000000000000..cfad8c689d03 --- /dev/null +++ b/src/test/ui/suggestions/type-not-found-in-adt-field.stderr @@ -0,0 +1,9 @@ +error[E0412]: cannot find type `Hashmap` in this scope + --> $DIR/type-not-found-in-adt-field.rs:2:12 + | +LL | m: Vec>, + | ^^^^^^^ not found in this scope + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0412`. From d5ea0e9f8def9a3ec0eb2dd88f0465d4d1a81c21 Mon Sep 17 00:00:00 2001 From: oddg Date: Thu, 14 May 2020 19:58:43 -0700 Subject: [PATCH 026/123] Report error when casting an C-like enum implementing Drop --- src/librustc_session/lint/builtin.rs | 11 ++++++++++ src/librustc_typeck/check/cast.rs | 30 +++++++++++++++++++++++++--- 2 files changed, 38 insertions(+), 3 deletions(-) diff --git a/src/librustc_session/lint/builtin.rs b/src/librustc_session/lint/builtin.rs index 58388bafbedd..5a8f5c1b9fbc 100644 --- a/src/librustc_session/lint/builtin.rs +++ b/src/librustc_session/lint/builtin.rs @@ -534,6 +534,16 @@ declare_lint! { @feature_gate = sym::unsafe_block_in_unsafe_fn; } +declare_lint! { + pub CENUM_IMPL_DROP_CAST, + Warn, + "a C-like enum implementing Drop is cast", + @future_incompatible = FutureIncompatibleInfo { + reference: "issue #73333 ", + edition: None, + }; +} + declare_lint_pass! { /// Does nothing as a lint pass, but registers some `Lint`s /// that are used by other parts of the compiler. @@ -607,6 +617,7 @@ declare_lint_pass! { ASM_SUB_REGISTER, UNSAFE_OP_IN_UNSAFE_FN, INCOMPLETE_INCLUDE, + CENUM_IMPL_DROP_CAST, ] } diff --git a/src/librustc_typeck/check/cast.rs b/src/librustc_typeck/check/cast.rs index 46d6706cbf42..bea5e0e99665 100644 --- a/src/librustc_typeck/check/cast.rs +++ b/src/librustc_typeck/check/cast.rs @@ -609,7 +609,10 @@ impl<'a, 'tcx> CastCheck<'tcx> { (FnPtr, Ptr(mt)) => self.check_fptr_ptr_cast(fcx, mt), // prim -> prim - (Int(CEnum), Int(_)) => Ok(CastKind::EnumCast), + (Int(CEnum), Int(_)) => { + self.cenum_impl_drop_lint(fcx); + Ok(CastKind::EnumCast) + } (Int(Char) | Int(Bool), Int(_)) => Ok(CastKind::PrimIntCast), (Int(_) | Float, Int(_) | Float) => Ok(CastKind::NumericCast), @@ -706,11 +709,13 @@ impl<'a, 'tcx> CastCheck<'tcx> { // Coerce to a raw pointer so that we generate AddressOf in MIR. let array_ptr_type = fcx.tcx.mk_ptr(m_expr); fcx.try_coerce(self.expr, self.expr_ty, array_ptr_type, AllowTwoPhase::No) - .unwrap_or_else(|_| bug!( + .unwrap_or_else(|_| { + bug!( "could not cast from reference to array to pointer to array ({:?} to {:?})", self.expr_ty, array_ptr_type, - )); + ) + }); // this will report a type mismatch if needed fcx.demand_eqtype(self.span, ety, m_cast.ty); @@ -740,6 +745,25 @@ impl<'a, 'tcx> CastCheck<'tcx> { Err(err) => Err(err), } } + + fn cenum_impl_drop_lint(&self, fcx: &FnCtxt<'a, 'tcx>) { + if let ty::Adt(d, _) = self.expr_ty.kind { + if d.has_dtor(fcx.tcx) { + fcx.tcx.struct_span_lint_hir( + lint::builtin::CENUM_IMPL_DROP_CAST, + self.expr.hir_id, + self.span, + |err| { + err.build(&format!( + "Cast `enum` implementing `Drop` `{}` to integer `{}`", + self.expr_ty, self.cast_ty + )) + .emit(); + }, + ); + } + } + } } impl<'a, 'tcx> FnCtxt<'a, 'tcx> { From e32db8458457849f5d894ffa4a1672b9071708f0 Mon Sep 17 00:00:00 2001 From: asrar Date: Sun, 14 Jun 2020 21:44:11 +0530 Subject: [PATCH 027/123] Add rust features to print target features `crt-static` is a rust specific target feature that's absent from llvm feature table, adding it there. --- src/rustllvm/PassWrapper.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/rustllvm/PassWrapper.cpp b/src/rustllvm/PassWrapper.cpp index 3d252fe70afe..7586dd91ab68 100644 --- a/src/rustllvm/PassWrapper.cpp +++ b/src/rustllvm/PassWrapper.cpp @@ -425,6 +425,9 @@ extern "C" void LLVMRustPrintTargetFeatures(LLVMTargetMachineRef TM) { for (auto &Feature : FeatTable) printf(" %-*s - %s.\n", MaxFeatLen, Feature.Key, Feature.Desc); printf("\n"); + // Rust specific target features + printf(" %-*s - %s.\n", MaxFeatLen, "crt-static", "Enables libraries with C Run-time Libraries(CRT) to be statically linked"); + printf("\n"); printf("Use +feature to enable a feature, or -feature to disable it.\n" "For example, rustc -C -target-cpu=mycpu -C " From a40156e5b7053dcc36ba1ad65fb567b183c4e1fb Mon Sep 17 00:00:00 2001 From: oddg Date: Sun, 14 Jun 2020 15:49:20 -0700 Subject: [PATCH 028/123] UI test for deprecation warning of casting enum implementing Drop --- src/test/ui/cenum_impl_drop_cast.rs | 18 ++++++++++++++++++ src/test/ui/cenum_impl_drop_cast.stderr | 16 ++++++++++++++++ 2 files changed, 34 insertions(+) create mode 100644 src/test/ui/cenum_impl_drop_cast.rs create mode 100644 src/test/ui/cenum_impl_drop_cast.stderr diff --git a/src/test/ui/cenum_impl_drop_cast.rs b/src/test/ui/cenum_impl_drop_cast.rs new file mode 100644 index 000000000000..623460673bfa --- /dev/null +++ b/src/test/ui/cenum_impl_drop_cast.rs @@ -0,0 +1,18 @@ +#![deny(cenum_impl_drop_cast)] + +enum E { + A = 0, +} + +impl Drop for E { + fn drop(&mut self) { + println!("Drop"); + } +} + +fn main() { + let e = E::A; + let i = e as u32; + //~^ ERROR Cast `enum` implementing `Drop` `E` to integer `u32` + //~| WARN this was previously accepted +} diff --git a/src/test/ui/cenum_impl_drop_cast.stderr b/src/test/ui/cenum_impl_drop_cast.stderr new file mode 100644 index 000000000000..5c8f86ffd72a --- /dev/null +++ b/src/test/ui/cenum_impl_drop_cast.stderr @@ -0,0 +1,16 @@ +error: Cast `enum` implementing `Drop` `E` to integer `u32` + --> $DIR/cenum_impl_drop_cast.rs:15:13 + | +LL | let i = e as u32; + | ^^^^^^^^ + | +note: the lint level is defined here + --> $DIR/cenum_impl_drop_cast.rs:1:9 + | +LL | #![deny(cenum_impl_drop_cast)] + | ^^^^^^^^^^^^^^^^^^^^ + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #73333 + +error: aborting due to previous error + From 607e85110ef9c79ce5a52286bb69d385471bc675 Mon Sep 17 00:00:00 2001 From: Eric Huss Date: Sun, 14 Jun 2020 15:57:21 -0700 Subject: [PATCH 029/123] Switch bootstrap metadata to --no-deps. This should run much faster. There are also some drive-by cleanups here to try to simplify things. Also, the paths for in-tree crates are now displayed as relative in `x.py test -h -v`. --- src/bootstrap/builder.rs | 6 +++-- src/bootstrap/doc.rs | 20 ++------------ src/bootstrap/lib.rs | 25 +++++++++++------- src/bootstrap/metadata.rs | 55 +++++++++------------------------------ src/bootstrap/test.rs | 8 ++---- 5 files changed, 37 insertions(+), 77 deletions(-) diff --git a/src/bootstrap/builder.rs b/src/bootstrap/builder.rs index ffdd8485181f..345af600c2ad 100644 --- a/src/bootstrap/builder.rs +++ b/src/bootstrap/builder.rs @@ -255,7 +255,8 @@ impl<'a> ShouldRun<'a> { pub fn all_krates(mut self, name: &str) -> Self { let mut set = BTreeSet::new(); for krate in self.builder.in_tree_crates(name) { - set.insert(PathBuf::from(&krate.path)); + let path = krate.local_path(self.builder); + set.insert(path); } self.paths.insert(PathSet::Set(set)); self @@ -263,7 +264,8 @@ impl<'a> ShouldRun<'a> { pub fn krate(mut self, name: &str) -> Self { for krate in self.builder.in_tree_crates(name) { - self.paths.insert(PathSet::one(&krate.path)); + let path = krate.local_path(self.builder); + self.paths.insert(PathSet::one(path)); } self } diff --git a/src/bootstrap/doc.rs b/src/bootstrap/doc.rs index 5c01c5e852c4..6d7fb7acfcb0 100644 --- a/src/bootstrap/doc.rs +++ b/src/bootstrap/doc.rs @@ -548,8 +548,8 @@ impl Step for Rustc { // Find dependencies for top level crates. let mut compiler_crates = HashSet::new(); for root_crate in &["rustc_driver", "rustc_codegen_llvm", "rustc_codegen_ssa"] { - let interned_root_crate = INTERNER.intern_str(root_crate); - find_compiler_crates(builder, &interned_root_crate, &mut compiler_crates); + compiler_crates + .extend(builder.in_tree_crates(root_crate).into_iter().map(|krate| krate.name)); } for krate in &compiler_crates { @@ -564,22 +564,6 @@ impl Step for Rustc { } } -fn find_compiler_crates( - builder: &Builder<'_>, - name: &Interned, - crates: &mut HashSet>, -) { - // Add current crate. - crates.insert(*name); - - // Look for dependencies. - for dep in builder.crates.get(name).unwrap().deps.iter() { - if builder.crates.get(dep).unwrap().is_local(builder) { - find_compiler_crates(builder, dep, crates); - } - } -} - #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)] pub struct Rustdoc { stage: u32, diff --git a/src/bootstrap/lib.rs b/src/bootstrap/lib.rs index a125b49fc01e..9d3830da3906 100644 --- a/src/bootstrap/lib.rs +++ b/src/bootstrap/lib.rs @@ -270,12 +270,7 @@ struct Crate { } impl Crate { - fn is_local(&self, build: &Build) -> bool { - self.path.starts_with(&build.config.src) - } - fn local_path(&self, build: &Build) -> PathBuf { - assert!(self.is_local(build)); self.path.strip_prefix(&build.config.src).unwrap().into() } } @@ -1079,17 +1074,29 @@ impl Build { } } + /// Returns a Vec of all the dependencies of the given root crate, + /// including transitive dependencies and the root itself. Only includes + /// "local" crates (those in the local source tree, not from a registry). fn in_tree_crates(&self, root: &str) -> Vec<&Crate> { let mut ret = Vec::new(); let mut list = vec![INTERNER.intern_str(root)]; let mut visited = HashSet::new(); while let Some(krate) = list.pop() { let krate = &self.crates[&krate]; - if krate.is_local(self) { - ret.push(krate); - } + ret.push(krate); for dep in &krate.deps { - if visited.insert(dep) && dep != "build_helper" { + // Don't include optional deps if their features are not + // enabled. Ideally this would be computed from `cargo + // metadata --features …`, but that is somewhat slow. Just + // skip `build_helper` since there aren't any operations we + // want to perform on it. In the future, we may want to + // consider just filtering all build and dev dependencies in + // metadata::build. + if visited.insert(dep) + && dep != "build_helper" + && (dep != "profiler_builtins" || self.config.profiler) + && (dep != "rustc_codegen_llvm" || self.config.llvm_enabled()) + { list.push(*dep); } } diff --git a/src/bootstrap/metadata.rs b/src/bootstrap/metadata.rs index 185f0ddb831e..a38391c7b88f 100644 --- a/src/bootstrap/metadata.rs +++ b/src/bootstrap/metadata.rs @@ -1,5 +1,3 @@ -use std::collections::HashMap; -use std::collections::HashSet; use std::path::PathBuf; use std::process::Command; @@ -12,7 +10,6 @@ use crate::{Build, Crate}; #[derive(Deserialize)] struct Output { packages: Vec, - resolve: Resolve, } #[derive(Deserialize)] @@ -21,38 +18,25 @@ struct Package { name: String, source: Option, manifest_path: String, + dependencies: Vec, } #[derive(Deserialize)] -struct Resolve { - nodes: Vec, -} - -#[derive(Deserialize)] -struct ResolveNode { - id: String, - dependencies: Vec, +struct Dependency { + name: String, + source: Option, } pub fn build(build: &mut Build) { // Run `cargo metadata` to figure out what crates we're testing. - let features: Vec<_> = build - .std_features() - .split_whitespace() - .map(|f| format!("test/{}", f)) - .chain(build.rustc_features().split_whitespace().map(|f| format!("rustc-main/{}", f))) - .collect(); let mut cargo = Command::new(&build.initial_cargo); cargo .arg("metadata") .arg("--format-version") .arg("1") - .arg("--features") - .arg(features.join(",")) - .arg("-Zpackage-features") + .arg("--no-deps") .arg("--manifest-path") - .arg(build.src.join("Cargo.toml")) - .env("RUSTC_BOOTSTRAP", "1"); + .arg(build.src.join("Cargo.toml")); let output = output(&mut cargo); let output: Output = serde_json::from_str(&output).unwrap(); for package in output.packages { @@ -60,26 +44,13 @@ pub fn build(build: &mut Build) { let name = INTERNER.intern_string(package.name); let mut path = PathBuf::from(package.manifest_path); path.pop(); - build.crates.insert(name, Crate { name, id: package.id, deps: HashSet::new(), path }); - } - } - - let id2name: HashMap<_, _> = - build.crates.iter().map(|(name, krate)| (krate.id.clone(), name.clone())).collect(); - - for node in output.resolve.nodes { - let name = match id2name.get(&node.id) { - Some(name) => name, - None => continue, - }; - - let krate = build.crates.get_mut(name).unwrap(); - for dep in node.dependencies.iter() { - let dep = match id2name.get(dep) { - Some(dep) => dep, - None => continue, - }; - krate.deps.insert(*dep); + let deps = package + .dependencies + .into_iter() + .filter(|dep| dep.source.is_none()) + .map(|dep| INTERNER.intern_string(dep.name)) + .collect(); + build.crates.insert(name, Crate { name, id: package.id, deps, path }); } } } diff --git a/src/bootstrap/test.rs b/src/bootstrap/test.rs index 8659acf1cc5a..c1d0316920be 100644 --- a/src/bootstrap/test.rs +++ b/src/bootstrap/test.rs @@ -1651,12 +1651,8 @@ impl Step for Crate { type Output = (); const DEFAULT: bool = true; - fn should_run(mut run: ShouldRun<'_>) -> ShouldRun<'_> { - let builder = run.builder; - for krate in run.builder.in_tree_crates("test") { - run = run.path(krate.local_path(&builder).to_str().unwrap()); - } - run + fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { + run.krate("test") } fn make_run(run: RunConfig<'_>) { From 5393a2995bcea9a927c23d88a921c55bea886771 Mon Sep 17 00:00:00 2001 From: Gary Guo Date: Sat, 16 May 2020 18:44:31 +0100 Subject: [PATCH 030/123] Move convert_place_derefs_to_mutable out from check/method/confirm.rs This can live inside FnCtxt rather than ConfirmContext, and would be useful for other operations as well. --- src/librustc_typeck/check/method/confirm.rs | 151 +------------------ src/librustc_typeck/check/mod.rs | 1 + src/librustc_typeck/check/reconciliation.rs | 153 ++++++++++++++++++++ 3 files changed, 157 insertions(+), 148 deletions(-) create mode 100644 src/librustc_typeck/check/reconciliation.rs diff --git a/src/librustc_typeck/check/method/confirm.rs b/src/librustc_typeck/check/method/confirm.rs index 007794ce1b7f..867dacede6e4 100644 --- a/src/librustc_typeck/check/method/confirm.rs +++ b/src/librustc_typeck/check/method/confirm.rs @@ -1,12 +1,12 @@ use super::{probe, MethodCallee}; use crate::astconv::AstConv; -use crate::check::{callee, FnCtxt, Needs, PlaceOp}; +use crate::check::{callee, FnCtxt, Needs}; use crate::hir::def_id::DefId; use crate::hir::GenericArg; use rustc_hir as hir; use rustc_infer::infer::{self, InferOk}; -use rustc_middle::ty::adjustment::{Adjust, Adjustment, OverloadedDeref, PointerCast}; +use rustc_middle::ty::adjustment::{Adjust, Adjustment, PointerCast}; use rustc_middle::ty::adjustment::{AllowTwoPhase, AutoBorrow, AutoBorrowMutability}; use rustc_middle::ty::fold::TypeFoldable; use rustc_middle::ty::subst::{Subst, SubstsRef}; @@ -121,7 +121,7 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> { let callee = MethodCallee { def_id: pick.item.def_id, substs: all_substs, sig: method_sig }; if let Some(hir::Mutability::Mut) = pick.autoref { - self.convert_place_derefs_to_mutable(); + self.convert_place_derefs_to_mutable(self.self_expr); } ConfirmResult { callee, illegal_sized_bound } @@ -416,151 +416,6 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> { self.register_wf_obligation(fty.into(), self.span, traits::MiscObligation); } - /////////////////////////////////////////////////////////////////////////// - // RECONCILIATION - - /// When we select a method with a mutable autoref, we have to go convert any - /// auto-derefs, indices, etc from `Deref` and `Index` into `DerefMut` and `IndexMut` - /// respectively. - fn convert_place_derefs_to_mutable(&self) { - // Gather up expressions we want to munge. - let mut exprs = vec![self.self_expr]; - - loop { - match exprs.last().unwrap().kind { - hir::ExprKind::Field(ref expr, _) - | hir::ExprKind::Index(ref expr, _) - | hir::ExprKind::Unary(hir::UnOp::UnDeref, ref expr) => exprs.push(&expr), - _ => break, - } - } - - debug!("convert_place_derefs_to_mutable: exprs={:?}", exprs); - - // Fix up autoderefs and derefs. - for (i, &expr) in exprs.iter().rev().enumerate() { - debug!("convert_place_derefs_to_mutable: i={} expr={:?}", i, expr); - - // Fix up the autoderefs. Autorefs can only occur immediately preceding - // overloaded place ops, and will be fixed by them in order to get - // the correct region. - let mut source = self.node_ty(expr.hir_id); - // Do not mutate adjustments in place, but rather take them, - // and replace them after mutating them, to avoid having the - // tables borrowed during (`deref_mut`) method resolution. - let previous_adjustments = - self.tables.borrow_mut().adjustments_mut().remove(expr.hir_id); - if let Some(mut adjustments) = previous_adjustments { - let needs = Needs::MutPlace; - for adjustment in &mut adjustments { - if let Adjust::Deref(Some(ref mut deref)) = adjustment.kind { - if let Some(ok) = self.try_overloaded_deref(expr.span, source, needs) { - let method = self.register_infer_ok_obligations(ok); - if let ty::Ref(region, _, mutbl) = method.sig.output().kind { - *deref = OverloadedDeref { region, mutbl }; - } - } - } - source = adjustment.target; - } - self.tables.borrow_mut().adjustments_mut().insert(expr.hir_id, adjustments); - } - - match expr.kind { - hir::ExprKind::Index(ref base_expr, ref index_expr) => { - // We need to get the final type in case dereferences were needed for the trait - // to apply (#72002). - let index_expr_ty = self.tables.borrow().expr_ty_adjusted(index_expr); - self.convert_place_op_to_mutable( - PlaceOp::Index, - expr, - base_expr, - &[index_expr_ty], - ); - } - hir::ExprKind::Unary(hir::UnOp::UnDeref, ref base_expr) => { - self.convert_place_op_to_mutable(PlaceOp::Deref, expr, base_expr, &[]); - } - _ => {} - } - } - } - - fn convert_place_op_to_mutable( - &self, - op: PlaceOp, - expr: &hir::Expr<'_>, - base_expr: &hir::Expr<'_>, - arg_tys: &[Ty<'tcx>], - ) { - debug!("convert_place_op_to_mutable({:?}, {:?}, {:?}, {:?})", op, expr, base_expr, arg_tys); - if !self.tables.borrow().is_method_call(expr) { - debug!("convert_place_op_to_mutable - builtin, nothing to do"); - return; - } - - let base_ty = self - .tables - .borrow() - .expr_adjustments(base_expr) - .last() - .map_or_else(|| self.node_ty(expr.hir_id), |adj| adj.target); - let base_ty = self.resolve_vars_if_possible(&base_ty); - - // Need to deref because overloaded place ops take self by-reference. - let base_ty = - base_ty.builtin_deref(false).expect("place op takes something that is not a ref").ty; - - let method = self.try_overloaded_place_op(expr.span, base_ty, arg_tys, Needs::MutPlace, op); - let method = match method { - Some(ok) => self.register_infer_ok_obligations(ok), - None => return self.tcx.sess.delay_span_bug(expr.span, "re-trying op failed"), - }; - debug!("convert_place_op_to_mutable: method={:?}", method); - self.write_method_call(expr.hir_id, method); - - let (region, mutbl) = if let ty::Ref(r, _, mutbl) = method.sig.inputs()[0].kind { - (r, mutbl) - } else { - span_bug!(expr.span, "input to place op is not a ref?"); - }; - - // Convert the autoref in the base expr to mutable with the correct - // region and mutability. - let base_expr_ty = self.node_ty(base_expr.hir_id); - if let Some(adjustments) = - self.tables.borrow_mut().adjustments_mut().get_mut(base_expr.hir_id) - { - let mut source = base_expr_ty; - for adjustment in &mut adjustments[..] { - if let Adjust::Borrow(AutoBorrow::Ref(..)) = adjustment.kind { - debug!("convert_place_op_to_mutable: converting autoref {:?}", adjustment); - let mutbl = match mutbl { - hir::Mutability::Not => AutoBorrowMutability::Not, - hir::Mutability::Mut => AutoBorrowMutability::Mut { - // For initial two-phase borrow - // deployment, conservatively omit - // overloaded operators. - allow_two_phase_borrow: AllowTwoPhase::No, - }, - }; - adjustment.kind = Adjust::Borrow(AutoBorrow::Ref(region, mutbl)); - adjustment.target = - self.tcx.mk_ref(region, ty::TypeAndMut { ty: source, mutbl: mutbl.into() }); - } - source = adjustment.target; - } - - // If we have an autoref followed by unsizing at the end, fix the unsize target. - - if let [.., Adjustment { kind: Adjust::Borrow(AutoBorrow::Ref(..)), .. }, Adjustment { kind: Adjust::Pointer(PointerCast::Unsize), ref mut target }] = - adjustments[..] - { - *target = method.sig.inputs()[0]; - } - } - } - /////////////////////////////////////////////////////////////////////////// // MISCELLANY diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index fabedc3800ae..1e4085f02676 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -79,6 +79,7 @@ pub mod intrinsic; pub mod method; mod op; mod pat; +mod reconciliation; mod regionck; mod upvar; mod wfcheck; diff --git a/src/librustc_typeck/check/reconciliation.rs b/src/librustc_typeck/check/reconciliation.rs new file mode 100644 index 000000000000..b05155ae2aed --- /dev/null +++ b/src/librustc_typeck/check/reconciliation.rs @@ -0,0 +1,153 @@ +use crate::check::{FnCtxt, Needs, PlaceOp}; +use rustc_hir as hir; +use rustc_middle::ty::adjustment::{Adjust, Adjustment, OverloadedDeref, PointerCast}; +use rustc_middle::ty::adjustment::{AllowTwoPhase, AutoBorrow, AutoBorrowMutability}; +use rustc_middle::ty::{self, Ty}; + +impl<'a, 'tcx> FnCtxt<'a, 'tcx> { + /// Convert auto-derefs, indices, etc of an expression from `Deref` and `Index` + /// into `DerefMut` and `IndexMut` respectively. + /// + /// This is a second pass of typechecking derefs/indices. We need this we do not + /// always know whether a place needs to be mutable or not in the first pass. + /// This happens whether there is an implicit mutable reborrow, e.g. when the type + /// is used as the receiver of a method call. + pub fn convert_place_derefs_to_mutable(&self, expr: &hir::Expr<'_>) { + // Gather up expressions we want to munge. + let mut exprs = vec![expr]; + + loop { + match exprs.last().unwrap().kind { + hir::ExprKind::Field(ref expr, _) + | hir::ExprKind::Index(ref expr, _) + | hir::ExprKind::Unary(hir::UnOp::UnDeref, ref expr) => exprs.push(&expr), + _ => break, + } + } + + debug!("convert_place_derefs_to_mutable: exprs={:?}", exprs); + + // Fix up autoderefs and derefs. + for (i, &expr) in exprs.iter().rev().enumerate() { + debug!("convert_place_derefs_to_mutable: i={} expr={:?}", i, expr); + + // Fix up the autoderefs. Autorefs can only occur immediately preceding + // overloaded place ops, and will be fixed by them in order to get + // the correct region. + let mut source = self.node_ty(expr.hir_id); + // Do not mutate adjustments in place, but rather take them, + // and replace them after mutating them, to avoid having the + // tables borrowed during (`deref_mut`) method resolution. + let previous_adjustments = + self.tables.borrow_mut().adjustments_mut().remove(expr.hir_id); + if let Some(mut adjustments) = previous_adjustments { + let needs = Needs::MutPlace; + for adjustment in &mut adjustments { + if let Adjust::Deref(Some(ref mut deref)) = adjustment.kind { + if let Some(ok) = self.try_overloaded_deref(expr.span, source, needs) { + let method = self.register_infer_ok_obligations(ok); + if let ty::Ref(region, _, mutbl) = method.sig.output().kind { + *deref = OverloadedDeref { region, mutbl }; + } + } + } + source = adjustment.target; + } + self.tables.borrow_mut().adjustments_mut().insert(expr.hir_id, adjustments); + } + + match expr.kind { + hir::ExprKind::Index(ref base_expr, ref index_expr) => { + // We need to get the final type in case dereferences were needed for the trait + // to apply (#72002). + let index_expr_ty = self.tables.borrow().expr_ty_adjusted(index_expr); + self.convert_place_op_to_mutable( + PlaceOp::Index, + expr, + base_expr, + &[index_expr_ty], + ); + } + hir::ExprKind::Unary(hir::UnOp::UnDeref, ref base_expr) => { + self.convert_place_op_to_mutable(PlaceOp::Deref, expr, base_expr, &[]); + } + _ => {} + } + } + } + + fn convert_place_op_to_mutable( + &self, + op: PlaceOp, + expr: &hir::Expr<'_>, + base_expr: &hir::Expr<'_>, + arg_tys: &[Ty<'tcx>], + ) { + debug!("convert_place_op_to_mutable({:?}, {:?}, {:?}, {:?})", op, expr, base_expr, arg_tys); + if !self.tables.borrow().is_method_call(expr) { + debug!("convert_place_op_to_mutable - builtin, nothing to do"); + return; + } + + let base_ty = self + .tables + .borrow() + .expr_adjustments(base_expr) + .last() + .map_or_else(|| self.node_ty(expr.hir_id), |adj| adj.target); + let base_ty = self.resolve_vars_if_possible(&base_ty); + + // Need to deref because overloaded place ops take self by-reference. + let base_ty = + base_ty.builtin_deref(false).expect("place op takes something that is not a ref").ty; + + let method = self.try_overloaded_place_op(expr.span, base_ty, arg_tys, Needs::MutPlace, op); + let method = match method { + Some(ok) => self.register_infer_ok_obligations(ok), + None => return self.tcx.sess.delay_span_bug(expr.span, "re-trying op failed"), + }; + debug!("convert_place_op_to_mutable: method={:?}", method); + self.write_method_call(expr.hir_id, method); + + let (region, mutbl) = if let ty::Ref(r, _, mutbl) = method.sig.inputs()[0].kind { + (r, mutbl) + } else { + span_bug!(expr.span, "input to place op is not a ref?"); + }; + + // Convert the autoref in the base expr to mutable with the correct + // region and mutability. + let base_expr_ty = self.node_ty(base_expr.hir_id); + if let Some(adjustments) = + self.tables.borrow_mut().adjustments_mut().get_mut(base_expr.hir_id) + { + let mut source = base_expr_ty; + for adjustment in &mut adjustments[..] { + if let Adjust::Borrow(AutoBorrow::Ref(..)) = adjustment.kind { + debug!("convert_place_op_to_mutable: converting autoref {:?}", adjustment); + let mutbl = match mutbl { + hir::Mutability::Not => AutoBorrowMutability::Not, + hir::Mutability::Mut => AutoBorrowMutability::Mut { + // For initial two-phase borrow + // deployment, conservatively omit + // overloaded operators. + allow_two_phase_borrow: AllowTwoPhase::No, + }, + }; + adjustment.kind = Adjust::Borrow(AutoBorrow::Ref(region, mutbl)); + adjustment.target = + self.tcx.mk_ref(region, ty::TypeAndMut { ty: source, mutbl: mutbl.into() }); + } + source = adjustment.target; + } + + // If we have an autoref followed by unsizing at the end, fix the unsize target. + + if let [.., Adjustment { kind: Adjust::Borrow(AutoBorrow::Ref(..)), .. }, Adjustment { kind: Adjust::Pointer(PointerCast::Unsize), ref mut target }] = + adjustments[..] + { + *target = method.sig.inputs()[0]; + } + } + } +} From fb0793c610d99857820721f58456920e9e0bc240 Mon Sep 17 00:00:00 2001 From: Gary Guo Date: Mon, 15 Jun 2020 00:57:21 +0100 Subject: [PATCH 031/123] Add some comments to librustc_typeck/check/callee.rs --- src/librustc_typeck/check/callee.rs | 37 +++++++++++++++++------------ 1 file changed, 22 insertions(+), 15 deletions(-) diff --git a/src/librustc_typeck/check/callee.rs b/src/librustc_typeck/check/callee.rs index aa316105f7f1..f86b7f07b7fc 100644 --- a/src/librustc_typeck/check/callee.rs +++ b/src/librustc_typeck/check/callee.rs @@ -220,21 +220,28 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let method = self.register_infer_ok_obligations(ok); let mut autoref = None; if borrow { - if let ty::Ref(region, _, mutbl) = method.sig.inputs()[0].kind { - let mutbl = match mutbl { - hir::Mutability::Not => AutoBorrowMutability::Not, - hir::Mutability::Mut => AutoBorrowMutability::Mut { - // For initial two-phase borrow - // deployment, conservatively omit - // overloaded function call ops. - allow_two_phase_borrow: AllowTwoPhase::No, - }, - }; - autoref = Some(Adjustment { - kind: Adjust::Borrow(AutoBorrow::Ref(region, mutbl)), - target: method.sig.inputs()[0], - }); - } + // Check for &self vs &mut self in the method signature. Since this is either + // the Fn or FnMut trait, it should be one of those. + let (region, mutbl) = if let ty::Ref(r, _, mutbl) = method.sig.inputs()[0].kind + { + (r, mutbl) + } else { + span_bug!(call_expr.span, "input to call/call_mut is not a ref?"); + }; + + let mutbl = match mutbl { + hir::Mutability::Not => AutoBorrowMutability::Not, + hir::Mutability::Mut => AutoBorrowMutability::Mut { + // For initial two-phase borrow + // deployment, conservatively omit + // overloaded function call ops. + allow_two_phase_borrow: AllowTwoPhase::No, + }, + }; + autoref = Some(Adjustment { + kind: Adjust::Borrow(AutoBorrow::Ref(region, mutbl)), + target: method.sig.inputs()[0], + }); } return Some((autoref, method)); } From c2b920fab328201a2b5507b9a484c8c09752af93 Mon Sep 17 00:00:00 2001 From: Eric Huss Date: Sun, 14 Jun 2020 16:58:45 -0700 Subject: [PATCH 032/123] Show suite paths (`src/test/ui/...`) in help output. --- src/bootstrap/builder.rs | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/src/bootstrap/builder.rs b/src/bootstrap/builder.rs index 345af600c2ad..545ad64ba2cf 100644 --- a/src/bootstrap/builder.rs +++ b/src/bootstrap/builder.rs @@ -489,13 +489,19 @@ impl<'a> Builder<'a> { should_run = (desc.should_run)(should_run); } let mut help = String::from("Available paths:\n"); + let mut add_path = |path: &Path| { + help.push_str(&format!(" ./x.py {} {}\n", subcommand, path.display())); + }; for pathset in should_run.paths { - if let PathSet::Set(set) = pathset { - set.iter().for_each(|path| { - help.push_str( - format!(" ./x.py {} {}\n", subcommand, path.display()).as_str(), - ) - }) + match pathset { + PathSet::Set(set) => { + for path in set { + add_path(&path); + } + } + PathSet::Suite(path) => { + add_path(&path.join("...")); + } } } Some(help) From f17fd7b0e692c59075db58ac2e7ca3ac2d5e19bd Mon Sep 17 00:00:00 2001 From: Eric Huss Date: Sun, 14 Jun 2020 17:00:34 -0700 Subject: [PATCH 033/123] Add some doc comments regarding PathSet. --- src/bootstrap/builder.rs | 28 +++++++++++++++++++++++++--- 1 file changed, 25 insertions(+), 3 deletions(-) diff --git a/src/bootstrap/builder.rs b/src/bootstrap/builder.rs index 545ad64ba2cf..c2f748f161f1 100644 --- a/src/bootstrap/builder.rs +++ b/src/bootstrap/builder.rs @@ -97,9 +97,21 @@ struct StepDescription { name: &'static str, } +/// Collection of paths used to match a task rule. #[derive(Debug, Clone, PartialOrd, Ord, PartialEq, Eq)] pub enum PathSet { + /// A collection of individual paths. + /// + /// These are generally matched as a path suffix. For example, a + /// command-line value of `libstd` will match if `src/libstd` is in the + /// set. Set(BTreeSet), + /// A "suite" of paths. + /// + /// These can match as a path suffix (like `Set`), or as a prefix. For + /// example, a command-line value of `src/test/ui/abi/variadic-ffi.rs` + /// will match `src/test/ui`. A command-line value of `ui` would also + /// match `src/test/ui`. Suite(PathBuf), } @@ -249,9 +261,15 @@ impl<'a> ShouldRun<'a> { self } - // Unlike `krate` this will create just one pathset. As such, it probably shouldn't actually - // ever be used, but as we transition to having all rules properly handle passing krate(...) by - // actually doing something different for every crate passed. + /// Indicates it should run if the command-line selects the given crate or + /// any of its (local) dependencies. + /// + /// Compared to `krate`, this treats the dependencies as aliases for the + /// same job. Generally it is preferred to use `krate`, and treat each + /// individual path separately. For example `./x.py test src/liballoc` + /// (which uses `krate`) will test just `liballoc`. However, `./x.py check + /// src/liballoc` (which uses `all_krates`) will check all of `libtest`. + /// `all_krates` should probably be removed at some point. pub fn all_krates(mut self, name: &str) -> Self { let mut set = BTreeSet::new(); for krate in self.builder.in_tree_crates(name) { @@ -262,6 +280,10 @@ impl<'a> ShouldRun<'a> { self } + /// Indicates it should run if the command-line selects the given crate or + /// any of its (local) dependencies. + /// + /// `make_run` will be called separately for each matching command-line path. pub fn krate(mut self, name: &str) -> Self { for krate in self.builder.in_tree_crates(name) { let path = krate.local_path(self.builder); From 8121d2e0576e74b23f0019e857b1088197ef8c04 Mon Sep 17 00:00:00 2001 From: Gary Guo Date: Mon, 15 Jun 2020 00:58:37 +0100 Subject: [PATCH 034/123] Fix up autoderef when performing mutable auto borrow --- src/librustc_typeck/check/method/confirm.rs | 5 ----- src/librustc_typeck/check/mod.rs | 15 +++++++++++++++ 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/src/librustc_typeck/check/method/confirm.rs b/src/librustc_typeck/check/method/confirm.rs index 867dacede6e4..c0f1f356ef37 100644 --- a/src/librustc_typeck/check/method/confirm.rs +++ b/src/librustc_typeck/check/method/confirm.rs @@ -119,11 +119,6 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> { // Create the final `MethodCallee`. let callee = MethodCallee { def_id: pick.item.def_id, substs: all_substs, sig: method_sig }; - - if let Some(hir::Mutability::Mut) = pick.autoref { - self.convert_place_derefs_to_mutable(self.self_expr); - } - ConfirmResult { callee, illegal_sized_bound } } diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index 1e4085f02676..82523f843aef 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -3183,6 +3183,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { return; } + let autoborrow_mut = adj.iter().any(|adj| { + matches!(adj, &Adjustment { + kind: Adjust::Borrow(AutoBorrow::Ref(_, AutoBorrowMutability::Mut { .. })), + .. + }) + }); + match self.tables.borrow_mut().adjustments_mut().entry(expr.hir_id) { Entry::Vacant(entry) => { entry.insert(adj); @@ -3212,6 +3219,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { *entry.get_mut() = adj; } } + + // When there is an auto mutable borrow, it is equivalent to `&mut expr`, + // thus `expr` is ought to be typechecked with needs = [`Needs::MutPlace`]. + // However in many cases it might not be checked this way originally, e.g. + // the receiver of a method call. We need to fix them up. + if autoborrow_mut { + self.convert_place_derefs_to_mutable(expr); + } } /// Basically whenever we are converting from a type scheme into From 4710f85882c08594a900b09c13fbe51ca207daec Mon Sep 17 00:00:00 2001 From: Gary Guo Date: Mon, 15 Jun 2020 00:59:03 +0100 Subject: [PATCH 035/123] Add ui tests for issue 68590 and 72225 --- .../issue-68590-reborrow-through-derefmut.rs | 25 +++++++++++++++++++ ...issue-72225-call-fnmut-through-derefmut.rs | 21 ++++++++++++++++ 2 files changed, 46 insertions(+) create mode 100644 src/test/ui/typeck/issue-68590-reborrow-through-derefmut.rs create mode 100644 src/test/ui/typeck/issue-72225-call-fnmut-through-derefmut.rs diff --git a/src/test/ui/typeck/issue-68590-reborrow-through-derefmut.rs b/src/test/ui/typeck/issue-68590-reborrow-through-derefmut.rs new file mode 100644 index 000000000000..e4436260e70a --- /dev/null +++ b/src/test/ui/typeck/issue-68590-reborrow-through-derefmut.rs @@ -0,0 +1,25 @@ +// check-pass + +// rust-lang/rust#68590: confusing diagnostics when reborrowing through DerefMut. + +use std::cell::RefCell; + +struct A; + +struct S<'a> { + a: &'a mut A, +} + +fn take_a(_: &mut A) {} + +fn test<'a>(s: &RefCell>) { + let mut guard = s.borrow_mut(); + take_a(guard.a); + let _s2 = S { a: guard.a }; +} + +fn main() { + let a = &mut A; + let s = RefCell::new(S { a }); + test(&s); +} diff --git a/src/test/ui/typeck/issue-72225-call-fnmut-through-derefmut.rs b/src/test/ui/typeck/issue-72225-call-fnmut-through-derefmut.rs new file mode 100644 index 000000000000..3ea05389f04a --- /dev/null +++ b/src/test/ui/typeck/issue-72225-call-fnmut-through-derefmut.rs @@ -0,0 +1,21 @@ +// check-pass + +// rust-lang/rust#72225: confusing diagnostics when calling FnMut through DerefMut. + +use std::cell::RefCell; + +struct S { + f: Box +} + +fn test(s: &RefCell) { + let mut guard = s.borrow_mut(); + (guard.f)(); +} + +fn main() { + let s = RefCell::new(S { + f: Box::new(|| ()) + }); + test(&s); +} From 0906066ae7d8a5e217e8cbf7f17de78a00d4ed83 Mon Sep 17 00:00:00 2001 From: Erik Desjardins Date: Mon, 15 Jun 2020 00:50:56 -0400 Subject: [PATCH 036/123] Test that bounds checks are elided when slice len is checked up-front --- src/test/codegen/issue-69101-bounds-check.rs | 26 ++++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 src/test/codegen/issue-69101-bounds-check.rs diff --git a/src/test/codegen/issue-69101-bounds-check.rs b/src/test/codegen/issue-69101-bounds-check.rs new file mode 100644 index 000000000000..cdbe51da03cc --- /dev/null +++ b/src/test/codegen/issue-69101-bounds-check.rs @@ -0,0 +1,26 @@ +// no-system-llvm +// compile-flags: -O +#![crate_type = "lib"] + +// CHECK-LABEL: @already_sliced_no_bounds_check +#[no_mangle] +pub fn already_sliced_no_bounds_check(a: &[u8], b: &[u8], c: &mut [u8]) { + // CHECK: slice_index_len_fail + // CHECK-NOT: panic_bounds_check + let _ = (&a[..2048], &b[..2048], &mut c[..2048]); + for i in 0..1024 { + c[i] = a[i] ^ b[i]; + } +} + +// make sure we're checking for the right thing: there can be a panic if the slice is too small +// CHECK-LABEL: @already_sliced_bounds_check +#[no_mangle] +pub fn already_sliced_bounds_check(a: &[u8], b: &[u8], c: &mut [u8]) { + // CHECK: slice_index_len_fail + // CHECK: panic_bounds_check + let _ = (&a[..1023], &b[..2048], &mut c[..2048]); + for i in 0..1024 { + c[i] = a[i] ^ b[i]; + } +} From f62903b74a8630fa62e721f69e6621d1d441e7f1 Mon Sep 17 00:00:00 2001 From: Nathan Corbyn Date: Fri, 5 Jun 2020 16:47:37 +0100 Subject: [PATCH 037/123] Export `#[inline] #[no_mangle]` fns in cdylibs and staticlibs --- src/librustc_codegen_ssa/back/symbol_export.rs | 9 +++++---- src/librustc_middle/mir/mono.rs | 1 + src/librustc_middle/query/mod.rs | 4 ++++ src/librustc_typeck/collect.rs | 12 ++++++++++++ src/test/codegen/cdylib-external-no-mangle-fns.rs | 13 +++++++++++++ .../codegen/staticlib-external-no-mangle-fns.rs | 13 +++++++++++++ 6 files changed, 48 insertions(+), 4 deletions(-) create mode 100644 src/test/codegen/cdylib-external-no-mangle-fns.rs create mode 100644 src/test/codegen/staticlib-external-no-mangle-fns.rs diff --git a/src/librustc_codegen_ssa/back/symbol_export.rs b/src/librustc_codegen_ssa/back/symbol_export.rs index 970d13b30c04..bf8693f3547a 100644 --- a/src/librustc_codegen_ssa/back/symbol_export.rs +++ b/src/librustc_codegen_ssa/back/symbol_export.rs @@ -89,10 +89,11 @@ fn reachable_non_generics_provider(tcx: TyCtxt<'_>, cnum: CrateNum) -> DefIdMap< | Node::ImplItem(&hir::ImplItem { kind: hir::ImplItemKind::Fn(..), .. }) => { let def_id = tcx.hir().local_def_id(hir_id); let generics = tcx.generics_of(def_id); - if !generics.requires_monomorphization(tcx) && - // Functions marked with #[inline] are only ever codegened - // with "internal" linkage and are never exported. - !Instance::mono(tcx, def_id.to_def_id()).def.generates_cgu_internal_copy(tcx) + if !generics.requires_monomorphization(tcx) + && (!Instance::mono(tcx, def_id.to_def_id()) + .def + .generates_cgu_internal_copy(tcx) + || tcx.inline_exportable(def_id.to_def_id())) { Some(def_id) } else { diff --git a/src/librustc_middle/mir/mono.rs b/src/librustc_middle/mir/mono.rs index c889dbc0a449..d8dcf0dea8a5 100644 --- a/src/librustc_middle/mir/mono.rs +++ b/src/librustc_middle/mir/mono.rs @@ -95,6 +95,7 @@ impl<'tcx> MonoItem<'tcx> { // linkage, then we'll be creating a globally shared version. if self.explicit_linkage(tcx).is_some() || !instance.def.generates_cgu_internal_copy(tcx) + || tcx.inline_exportable(instance.def_id()) || Some(instance.def_id()) == entry_def_id.map(LocalDefId::to_def_id) { return InstantiationMode::GloballyShared { may_conflict: false }; diff --git a/src/librustc_middle/query/mod.rs b/src/librustc_middle/query/mod.rs index be15e6c576f6..20487fdb6696 100644 --- a/src/librustc_middle/query/mod.rs +++ b/src/librustc_middle/query/mod.rs @@ -697,6 +697,10 @@ rustc_queries! { storage(ArenaCacheSelector<'tcx>) cache_on_disk_if { true } } + + query inline_exportable(def_id: DefId) -> bool { + desc { |tcx| "computing whether `{}` should be explicitly exported", tcx.def_path_str(def_id) } + } } Other { diff --git a/src/librustc_typeck/collect.rs b/src/librustc_typeck/collect.rs index 1d59d749634e..08a6330e718e 100644 --- a/src/librustc_typeck/collect.rs +++ b/src/librustc_typeck/collect.rs @@ -40,6 +40,7 @@ use rustc_middle::ty::util::Discr; use rustc_middle::ty::util::IntTypeExt; use rustc_middle::ty::{self, AdtKind, Const, ToPolyTraitRef, Ty, TyCtxt}; use rustc_middle::ty::{ReprOptions, ToPredicate, WithConstness}; +use rustc_session::config::CrateType; use rustc_session::lint; use rustc_session::parse::feature_err; use rustc_span::symbol::{kw, sym, Ident, Symbol}; @@ -79,6 +80,7 @@ pub fn provide(providers: &mut Providers<'_>) { static_mutability, generator_kind, codegen_fn_attrs, + inline_exportable, collect_mod_item_types, ..*providers }; @@ -2599,6 +2601,16 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, id: DefId) -> CodegenFnAttrs { codegen_fn_attrs } +fn inline_exportable(tcx: TyCtxt<'_>, def_id: DefId) -> bool { + // Functions marked with #[inline] are only ever codegened + // with "internal" linkage and are never exported unless we're + // building a `staticlib` or `cdylib` and they are marked + // `#[no_mangle]`. + tcx.codegen_fn_attrs(def_id).flags.contains(CodegenFnAttrFlags::NO_MANGLE) + && (tcx.sess.crate_types().contains(&CrateType::Cdylib) + || tcx.sess.crate_types().contains(&CrateType::Staticlib)) +} + /// Checks if the provided DefId is a method in a trait impl for a trait which has track_caller /// applied to the method prototype. fn should_inherit_track_caller(tcx: TyCtxt<'_>, def_id: DefId) -> bool { diff --git a/src/test/codegen/cdylib-external-no-mangle-fns.rs b/src/test/codegen/cdylib-external-no-mangle-fns.rs new file mode 100644 index 000000000000..827de7e5c11d --- /dev/null +++ b/src/test/codegen/cdylib-external-no-mangle-fns.rs @@ -0,0 +1,13 @@ +// compile-flags: -C no-prepopulate-passes + +#![crate_type = "cdylib"] + +// CHECK: define void @a() +#[no_mangle] +#[inline] +pub extern "C" fn a() { + // side effect to keep `a` around + unsafe { + core::ptr::read_volatile(&42); + } +} diff --git a/src/test/codegen/staticlib-external-no-mangle-fns.rs b/src/test/codegen/staticlib-external-no-mangle-fns.rs new file mode 100644 index 000000000000..0b4a37febb20 --- /dev/null +++ b/src/test/codegen/staticlib-external-no-mangle-fns.rs @@ -0,0 +1,13 @@ +// compile-flags: -C no-prepopulate-passes + +#![crate_type = "staticlib"] + +// CHECK: define void @a() +#[no_mangle] +#[inline] +pub extern "C" fn a() { + // side effect to keep `a` around + unsafe { + core::ptr::read_volatile(&42); + } +} From 6b7cacb2c99567a76cf0d5ce6833a129c8bb8814 Mon Sep 17 00:00:00 2001 From: Nathan Corbyn Date: Mon, 8 Jun 2020 09:37:11 +0100 Subject: [PATCH 038/123] Export all fns with extern indicator --- .../back/symbol_export.rs | 9 ++++---- src/librustc_middle/mir/mono.rs | 12 ++++++---- src/librustc_middle/query/mod.rs | 4 ---- src/librustc_typeck/collect.rs | 12 ---------- .../codegen/cdylib-external-inline-fns.rs | 23 +++++++++++++++++++ .../codegen/cdylib-external-no-mangle-fns.rs | 13 ----------- src/test/codegen/export-no-mangle.rs | 5 ++++ src/test/codegen/external-no-mangle-fns.rs | 10 ++++++++ .../codegen/staticlib-external-inline-fns.rs | 23 +++++++++++++++++++ .../staticlib-external-no-mangle-fns.rs | 13 ----------- 10 files changed, 74 insertions(+), 50 deletions(-) create mode 100644 src/test/codegen/cdylib-external-inline-fns.rs delete mode 100644 src/test/codegen/cdylib-external-no-mangle-fns.rs create mode 100644 src/test/codegen/staticlib-external-inline-fns.rs delete mode 100644 src/test/codegen/staticlib-external-no-mangle-fns.rs diff --git a/src/librustc_codegen_ssa/back/symbol_export.rs b/src/librustc_codegen_ssa/back/symbol_export.rs index bf8693f3547a..98f7da8361cc 100644 --- a/src/librustc_codegen_ssa/back/symbol_export.rs +++ b/src/librustc_codegen_ssa/back/symbol_export.rs @@ -90,10 +90,11 @@ fn reachable_non_generics_provider(tcx: TyCtxt<'_>, cnum: CrateNum) -> DefIdMap< let def_id = tcx.hir().local_def_id(hir_id); let generics = tcx.generics_of(def_id); if !generics.requires_monomorphization(tcx) - && (!Instance::mono(tcx, def_id.to_def_id()) - .def - .generates_cgu_internal_copy(tcx) - || tcx.inline_exportable(def_id.to_def_id())) + // Functions marked with #[inline] are codegened with "internal" + // linkage and are not exported unless marked with an extern + // inidicator + && (!Instance::mono(tcx, def_id.to_def_id()).def.generates_cgu_internal_copy(tcx) + || tcx.codegen_fn_attrs(def_id.to_def_id()).contains_extern_indicator()) { Some(def_id) } else { diff --git a/src/librustc_middle/mir/mono.rs b/src/librustc_middle/mir/mono.rs index d8dcf0dea8a5..886690da212d 100644 --- a/src/librustc_middle/mir/mono.rs +++ b/src/librustc_middle/mir/mono.rs @@ -92,10 +92,10 @@ impl<'tcx> MonoItem<'tcx> { MonoItem::Fn(ref instance) => { let entry_def_id = tcx.entry_fn(LOCAL_CRATE).map(|(id, _)| id); // If this function isn't inlined or otherwise has explicit - // linkage, then we'll be creating a globally shared version. + // linkage or an extern indicator, then we'll be creating a + // globally shared version. if self.explicit_linkage(tcx).is_some() || !instance.def.generates_cgu_internal_copy(tcx) - || tcx.inline_exportable(instance.def_id()) || Some(instance.def_id()) == entry_def_id.map(LocalDefId::to_def_id) { return InstantiationMode::GloballyShared { may_conflict: false }; @@ -103,8 +103,12 @@ impl<'tcx> MonoItem<'tcx> { // At this point we don't have explicit linkage and we're an // inlined function. If we're inlining into all CGUs then we'll - // be creating a local copy per CGU - if generate_cgu_internal_copies { + // be creating a local copy per CGU. We need to watch out here + // for an extern indicator as we don't want to optimise away + // inlined functions that should be exported. + if generate_cgu_internal_copies + && !tcx.codegen_fn_attrs(instance.def_id()).contains_extern_indicator() + { return InstantiationMode::LocalCopy; } diff --git a/src/librustc_middle/query/mod.rs b/src/librustc_middle/query/mod.rs index 20487fdb6696..be15e6c576f6 100644 --- a/src/librustc_middle/query/mod.rs +++ b/src/librustc_middle/query/mod.rs @@ -697,10 +697,6 @@ rustc_queries! { storage(ArenaCacheSelector<'tcx>) cache_on_disk_if { true } } - - query inline_exportable(def_id: DefId) -> bool { - desc { |tcx| "computing whether `{}` should be explicitly exported", tcx.def_path_str(def_id) } - } } Other { diff --git a/src/librustc_typeck/collect.rs b/src/librustc_typeck/collect.rs index 08a6330e718e..1d59d749634e 100644 --- a/src/librustc_typeck/collect.rs +++ b/src/librustc_typeck/collect.rs @@ -40,7 +40,6 @@ use rustc_middle::ty::util::Discr; use rustc_middle::ty::util::IntTypeExt; use rustc_middle::ty::{self, AdtKind, Const, ToPolyTraitRef, Ty, TyCtxt}; use rustc_middle::ty::{ReprOptions, ToPredicate, WithConstness}; -use rustc_session::config::CrateType; use rustc_session::lint; use rustc_session::parse::feature_err; use rustc_span::symbol::{kw, sym, Ident, Symbol}; @@ -80,7 +79,6 @@ pub fn provide(providers: &mut Providers<'_>) { static_mutability, generator_kind, codegen_fn_attrs, - inline_exportable, collect_mod_item_types, ..*providers }; @@ -2601,16 +2599,6 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, id: DefId) -> CodegenFnAttrs { codegen_fn_attrs } -fn inline_exportable(tcx: TyCtxt<'_>, def_id: DefId) -> bool { - // Functions marked with #[inline] are only ever codegened - // with "internal" linkage and are never exported unless we're - // building a `staticlib` or `cdylib` and they are marked - // `#[no_mangle]`. - tcx.codegen_fn_attrs(def_id).flags.contains(CodegenFnAttrFlags::NO_MANGLE) - && (tcx.sess.crate_types().contains(&CrateType::Cdylib) - || tcx.sess.crate_types().contains(&CrateType::Staticlib)) -} - /// Checks if the provided DefId is a method in a trait impl for a trait which has track_caller /// applied to the method prototype. fn should_inherit_track_caller(tcx: TyCtxt<'_>, def_id: DefId) -> bool { diff --git a/src/test/codegen/cdylib-external-inline-fns.rs b/src/test/codegen/cdylib-external-inline-fns.rs new file mode 100644 index 000000000000..58f806b5a1f3 --- /dev/null +++ b/src/test/codegen/cdylib-external-inline-fns.rs @@ -0,0 +1,23 @@ +// compile-flags: -C no-prepopulate-passes + +#![crate_type = "cdylib"] + +// CHECK: define void @a() +#[no_mangle] +#[inline] +pub extern "C" fn a() {} + +// CHECK: define void @b() +#[export_name = "b"] +#[inline] +pub extern "C" fn b() {} + +// CHECK: define void @c() +#[no_mangle] +#[inline] +extern "C" fn c() {} + +// CHECK: define void @d() +#[export_name = "d"] +#[inline] +extern "C" fn d() {} diff --git a/src/test/codegen/cdylib-external-no-mangle-fns.rs b/src/test/codegen/cdylib-external-no-mangle-fns.rs deleted file mode 100644 index 827de7e5c11d..000000000000 --- a/src/test/codegen/cdylib-external-no-mangle-fns.rs +++ /dev/null @@ -1,13 +0,0 @@ -// compile-flags: -C no-prepopulate-passes - -#![crate_type = "cdylib"] - -// CHECK: define void @a() -#[no_mangle] -#[inline] -pub extern "C" fn a() { - // side effect to keep `a` around - unsafe { - core::ptr::read_volatile(&42); - } -} diff --git a/src/test/codegen/export-no-mangle.rs b/src/test/codegen/export-no-mangle.rs index 78d41e4be0ae..793636bb1b03 100644 --- a/src/test/codegen/export-no-mangle.rs +++ b/src/test/codegen/export-no-mangle.rs @@ -18,4 +18,9 @@ mod private { // CHECK: void @bar() #[export_name = "bar"] extern fn bar() {} + + // CHECK: void @baz() + #[export_name = "baz"] + #[inline] + extern fn baz() {} } diff --git a/src/test/codegen/external-no-mangle-fns.rs b/src/test/codegen/external-no-mangle-fns.rs index 902882144996..aefa9ce21c3e 100644 --- a/src/test/codegen/external-no-mangle-fns.rs +++ b/src/test/codegen/external-no-mangle-fns.rs @@ -53,3 +53,13 @@ fn x() { core::ptr::read_volatile(&42); } } + +// CHECK: define void @i() +#[no_mangle] +#[inline] +fn i() {} + +// CHECK: define void @j() +#[no_mangle] +#[inline] +pub fn j() {} diff --git a/src/test/codegen/staticlib-external-inline-fns.rs b/src/test/codegen/staticlib-external-inline-fns.rs new file mode 100644 index 000000000000..8f55a5303311 --- /dev/null +++ b/src/test/codegen/staticlib-external-inline-fns.rs @@ -0,0 +1,23 @@ +// compile-flags: -C no-prepopulate-passes + +#![crate_type = "staticlib"] + +// CHECK: define void @a() +#[no_mangle] +#[inline] +pub extern "C" fn a() {} + +// CHECK: define void @b() +#[export_name = "b"] +#[inline] +pub extern "C" fn b() {} + +// CHECK: define void @c() +#[no_mangle] +#[inline] +extern "C" fn c() {} + +// CHECK: define void @d() +#[export_name = "d"] +#[inline] +extern "C" fn d() {} diff --git a/src/test/codegen/staticlib-external-no-mangle-fns.rs b/src/test/codegen/staticlib-external-no-mangle-fns.rs deleted file mode 100644 index 0b4a37febb20..000000000000 --- a/src/test/codegen/staticlib-external-no-mangle-fns.rs +++ /dev/null @@ -1,13 +0,0 @@ -// compile-flags: -C no-prepopulate-passes - -#![crate_type = "staticlib"] - -// CHECK: define void @a() -#[no_mangle] -#[inline] -pub extern "C" fn a() { - // side effect to keep `a` around - unsafe { - core::ptr::read_volatile(&42); - } -} From d23bedd13d70597a7db70ef5ea549fc7a4063d10 Mon Sep 17 00:00:00 2001 From: Nathan Corbyn Date: Mon, 8 Jun 2020 09:54:33 +0100 Subject: [PATCH 039/123] Fix whitespace --- src/test/codegen/export-no-mangle.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/codegen/export-no-mangle.rs b/src/test/codegen/export-no-mangle.rs index 793636bb1b03..a52fac37021d 100644 --- a/src/test/codegen/export-no-mangle.rs +++ b/src/test/codegen/export-no-mangle.rs @@ -18,7 +18,7 @@ mod private { // CHECK: void @bar() #[export_name = "bar"] extern fn bar() {} - + // CHECK: void @baz() #[export_name = "baz"] #[inline] From ee810a75e41368387918759ed191657f05650f05 Mon Sep 17 00:00:00 2001 From: Nathan Corbyn Date: Tue, 9 Jun 2020 15:49:59 +0100 Subject: [PATCH 040/123] Fix exports with `#[inline(always)]` --- src/librustc_middle/mir/mono.rs | 15 +++++-------- .../codegen/cdylib-external-inline-fns.rs | 20 ++++++++++++++++++ src/test/codegen/export-no-mangle.rs | 21 ++++++++++++------- src/test/codegen/external-no-mangle-fns.rs | 10 +++++++++ .../codegen/staticlib-external-inline-fns.rs | 20 ++++++++++++++++++ 5 files changed, 68 insertions(+), 18 deletions(-) diff --git a/src/librustc_middle/mir/mono.rs b/src/librustc_middle/mir/mono.rs index 886690da212d..24d324ff09c4 100644 --- a/src/librustc_middle/mir/mono.rs +++ b/src/librustc_middle/mir/mono.rs @@ -91,10 +91,9 @@ impl<'tcx> MonoItem<'tcx> { match *self { MonoItem::Fn(ref instance) => { let entry_def_id = tcx.entry_fn(LOCAL_CRATE).map(|(id, _)| id); - // If this function isn't inlined or otherwise has explicit - // linkage or an extern indicator, then we'll be creating a - // globally shared version. - if self.explicit_linkage(tcx).is_some() + // If this function isn't inlined or otherwise has an extern + // indicator, then we'll be creating a globally shared version. + if tcx.codegen_fn_attrs(instance.def_id()).contains_extern_indicator() || !instance.def.generates_cgu_internal_copy(tcx) || Some(instance.def_id()) == entry_def_id.map(LocalDefId::to_def_id) { @@ -103,12 +102,8 @@ impl<'tcx> MonoItem<'tcx> { // At this point we don't have explicit linkage and we're an // inlined function. If we're inlining into all CGUs then we'll - // be creating a local copy per CGU. We need to watch out here - // for an extern indicator as we don't want to optimise away - // inlined functions that should be exported. - if generate_cgu_internal_copies - && !tcx.codegen_fn_attrs(instance.def_id()).contains_extern_indicator() - { + // be creating a local copy per CGU. + if generate_cgu_internal_copies { return InstantiationMode::LocalCopy; } diff --git a/src/test/codegen/cdylib-external-inline-fns.rs b/src/test/codegen/cdylib-external-inline-fns.rs index 58f806b5a1f3..519be6b6a99a 100644 --- a/src/test/codegen/cdylib-external-inline-fns.rs +++ b/src/test/codegen/cdylib-external-inline-fns.rs @@ -21,3 +21,23 @@ extern "C" fn c() {} #[export_name = "d"] #[inline] extern "C" fn d() {} + +// CHECK: define void @e() +#[no_mangle] +#[inline(always)] +pub extern "C" fn e() {} + +// CHECK: define void @f() +#[export_name = "f"] +#[inline(always)] +pub extern "C" fn f() {} + +// CHECK: define void @g() +#[no_mangle] +#[inline(always)] +extern "C" fn g() {} + +// CHECK: define void @h() +#[export_name = "h"] +#[inline(always)] +extern "C" fn h() {} diff --git a/src/test/codegen/export-no-mangle.rs b/src/test/codegen/export-no-mangle.rs index a52fac37021d..11427ae38822 100644 --- a/src/test/codegen/export-no-mangle.rs +++ b/src/test/codegen/export-no-mangle.rs @@ -11,16 +11,21 @@ mod private { #[export_name = "BAR"] static BAR: u32 = 3; - // CHECK: void @foo() + // CHECK: void @a() #[no_mangle] - pub extern fn foo() {} + pub extern fn a() {} - // CHECK: void @bar() - #[export_name = "bar"] - extern fn bar() {} + // CHECK: void @b() + #[export_name = "b"] + extern fn b() {} - // CHECK: void @baz() - #[export_name = "baz"] + // CHECK: void @c() + #[export_name = "c"] #[inline] - extern fn baz() {} + extern fn c() {} + + // CHECK: void @d() + #[export_name = "d"] + #[inline(always)] + extern fn d() {} } diff --git a/src/test/codegen/external-no-mangle-fns.rs b/src/test/codegen/external-no-mangle-fns.rs index aefa9ce21c3e..41820b057f1e 100644 --- a/src/test/codegen/external-no-mangle-fns.rs +++ b/src/test/codegen/external-no-mangle-fns.rs @@ -63,3 +63,13 @@ fn i() {} #[no_mangle] #[inline] pub fn j() {} + +// CHECK: define void @k() +#[no_mangle] +#[inline(always)] +fn k() {} + +// CHECK: define void @l() +#[no_mangle] +#[inline(always)] +pub fn l() {} diff --git a/src/test/codegen/staticlib-external-inline-fns.rs b/src/test/codegen/staticlib-external-inline-fns.rs index 8f55a5303311..8876ab7376af 100644 --- a/src/test/codegen/staticlib-external-inline-fns.rs +++ b/src/test/codegen/staticlib-external-inline-fns.rs @@ -21,3 +21,23 @@ extern "C" fn c() {} #[export_name = "d"] #[inline] extern "C" fn d() {} + +// CHECK: define void @e() +#[no_mangle] +#[inline(always)] +pub extern "C" fn e() {} + +// CHECK: define void @f() +#[export_name = "f"] +#[inline(always)] +pub extern "C" fn f() {} + +// CHECK: define void @g() +#[no_mangle] +#[inline(always)] +extern "C" fn g() {} + +// CHECK: define void @h() +#[export_name = "h"] +#[inline(always)] +extern "C" fn h() {} From 11b56fbfb63ef8e2494b8631488e478794c80ed4 Mon Sep 17 00:00:00 2001 From: Nathan Corbyn Date: Tue, 9 Jun 2020 15:54:34 +0100 Subject: [PATCH 041/123] Fix whitespace --- src/librustc_middle/mir/mono.rs | 2 +- src/test/codegen/export-no-mangle.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/librustc_middle/mir/mono.rs b/src/librustc_middle/mir/mono.rs index 24d324ff09c4..f1c1b962ab99 100644 --- a/src/librustc_middle/mir/mono.rs +++ b/src/librustc_middle/mir/mono.rs @@ -102,7 +102,7 @@ impl<'tcx> MonoItem<'tcx> { // At this point we don't have explicit linkage and we're an // inlined function. If we're inlining into all CGUs then we'll - // be creating a local copy per CGU. + // be creating a local copy per CGU. if generate_cgu_internal_copies { return InstantiationMode::LocalCopy; } diff --git a/src/test/codegen/export-no-mangle.rs b/src/test/codegen/export-no-mangle.rs index 11427ae38822..59e97601c838 100644 --- a/src/test/codegen/export-no-mangle.rs +++ b/src/test/codegen/export-no-mangle.rs @@ -23,7 +23,7 @@ mod private { #[export_name = "c"] #[inline] extern fn c() {} - + // CHECK: void @d() #[export_name = "d"] #[inline(always)] From babda9470ea1e5932d238b7f805e76379f01d37c Mon Sep 17 00:00:00 2001 From: Nathan Corbyn Date: Mon, 15 Jun 2020 10:21:19 +0100 Subject: [PATCH 042/123] Fix sanitizer test --- src/test/codegen/sanitizer-no-sanitize-inlining.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/test/codegen/sanitizer-no-sanitize-inlining.rs b/src/test/codegen/sanitizer-no-sanitize-inlining.rs index d96e76618d32..86e58c3c9bb1 100644 --- a/src/test/codegen/sanitizer-no-sanitize-inlining.rs +++ b/src/test/codegen/sanitizer-no-sanitize-inlining.rs @@ -13,7 +13,7 @@ #![feature(no_sanitize)] // ASAN-LABEL: define void @test -// ASAN: tail call fastcc void @random_inline +// ASAN: tail call fastcc // ASAN: } // // LSAN-LABEL: define void @test @@ -26,7 +26,6 @@ pub fn test(n: &mut u32) { #[no_sanitize(address)] #[inline] -#[no_mangle] pub fn random_inline(n: &mut u32) { *n = 42; } From e8e0a0e4e220533db31bc6a572ed9f1b99b31289 Mon Sep 17 00:00:00 2001 From: Nathan Corbyn Date: Mon, 15 Jun 2020 11:12:19 +0100 Subject: [PATCH 043/123] Update sanitizer test --- src/test/codegen/sanitizer-no-sanitize-inlining.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/test/codegen/sanitizer-no-sanitize-inlining.rs b/src/test/codegen/sanitizer-no-sanitize-inlining.rs index 86e58c3c9bb1..48231d6f7208 100644 --- a/src/test/codegen/sanitizer-no-sanitize-inlining.rs +++ b/src/test/codegen/sanitizer-no-sanitize-inlining.rs @@ -13,7 +13,7 @@ #![feature(no_sanitize)] // ASAN-LABEL: define void @test -// ASAN: tail call fastcc +// ASAN: call {{.*}} @random_inline // ASAN: } // // LSAN-LABEL: define void @test @@ -26,6 +26,7 @@ pub fn test(n: &mut u32) { #[no_sanitize(address)] #[inline] +#[no_mangle] pub fn random_inline(n: &mut u32) { *n = 42; } From d3ca6fd71ed3003e58c6b58d7beb0505d0c8adc3 Mon Sep 17 00:00:00 2001 From: Harald Hoyer Date: Mon, 15 Jun 2020 13:23:38 +0200 Subject: [PATCH 044/123] Enable static-pie for the x86_64-unknown-linux-musl target Fixes: https://github.com/rust-lang/rust/issues/70693 --- .../spec/x86_64_unknown_linux_musl.rs | 1 + src/test/run-make/static-pie/Makefile | 15 +++++++ src/test/run-make/static-pie/test-aslr.rs | 43 +++++++++++++++++++ 3 files changed, 59 insertions(+) create mode 100644 src/test/run-make/static-pie/Makefile create mode 100644 src/test/run-make/static-pie/test-aslr.rs diff --git a/src/librustc_target/spec/x86_64_unknown_linux_musl.rs b/src/librustc_target/spec/x86_64_unknown_linux_musl.rs index 34c628e8f67b..3a22290da685 100644 --- a/src/librustc_target/spec/x86_64_unknown_linux_musl.rs +++ b/src/librustc_target/spec/x86_64_unknown_linux_musl.rs @@ -6,6 +6,7 @@ pub fn target() -> TargetResult { base.max_atomic_width = Some(64); base.pre_link_args.get_mut(&LinkerFlavor::Gcc).unwrap().push("-m64".to_string()); base.stack_probes = true; + base.static_position_independent_executables = true; Ok(Target { llvm_target: "x86_64-unknown-linux-musl".to_string(), diff --git a/src/test/run-make/static-pie/Makefile b/src/test/run-make/static-pie/Makefile new file mode 100644 index 000000000000..1d3cc8213892 --- /dev/null +++ b/src/test/run-make/static-pie/Makefile @@ -0,0 +1,15 @@ +-include ../../run-make-fulldeps/tools.mk + +# only-x86_64-unknown-linux-musl + +# How to manually run this +# $ ./x.py test --target x86_64-unknown-linux-musl src/test/run-make/static-pie + +all: + $(RUSTC) --target $(TARGET) -C target-feature=+crt-static test-aslr.rs + # Check that no dynamic interpreter is set + ! readelf -l $(call RUN_BINFILE,test-aslr) | $(CGREP) INTERP + # Check that we have a dynamic executable + readelf -l $(call RUN_BINFILE,test-aslr) | $(CGREP) DYNAMIC + # Check for address space layout randomization + $(call RUN,test-aslr) --test-aslr diff --git a/src/test/run-make/static-pie/test-aslr.rs b/src/test/run-make/static-pie/test-aslr.rs new file mode 100644 index 000000000000..f28e00f7f4cf --- /dev/null +++ b/src/test/run-make/static-pie/test-aslr.rs @@ -0,0 +1,43 @@ +const NUM_RUNS: usize = 10; + +fn run_self(exe: &str) -> usize { + use std::process::Command; + let mut set = std::collections::HashSet::new(); + + let mut cmd = Command::new(exe); + cmd.arg("--report"); + (0..NUM_RUNS).for_each(|_| { + set.insert(cmd.output().expect("failed to execute process").stdout); + }); + set.len() +} + +fn main() { + let mut args = std::env::args(); + let arg0 = args.next().unwrap(); + match args.next() { + Some(s) if s.eq("--report") => { + println!("main = {:#?}", &main as *const _); + } + Some(s) if s.eq("--test-no-aslr") => { + let cnt = run_self(&arg0); + if cnt != 1 { + eprintln!("FAIL: {} most likely ASLR", arg0); + std::process::exit(1); + } + println!("PASS: {} does no ASLR", arg0); + } + Some(s) if s.eq("--test-aslr") => { + let cnt = run_self(&arg0); + if cnt != NUM_RUNS { + eprintln!("FAIL: {} most likely no ASLR", arg0); + std::process::exit(1); + } + println!("PASS: {} does ASLR", arg0); + } + Some(_) | None => { + println!("Usage: {} --test-no-aslr | --test-aslr", arg0); + std::process::exit(1); + } + } +} From 9e510085ecaedaee86b44410a4b3e4c85d97d6e0 Mon Sep 17 00:00:00 2001 From: Alexis Bourget Date: Mon, 15 Jun 2020 15:19:02 +0200 Subject: [PATCH 045/123] Complete the std::time documentation to warn about the inconsistencies between OS --- src/libstd/time.rs | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/libstd/time.rs b/src/libstd/time.rs index c36e78b1d004..c58168bd446d 100644 --- a/src/libstd/time.rs +++ b/src/libstd/time.rs @@ -60,6 +60,21 @@ pub use core::time::Duration; /// } /// ``` /// +/// # OS-specific behaviors +/// +/// An `Instant` is a wrapper around system-specific types and it may behave +/// differently depending on the underlying operating system. For example, +/// the following snippet is fine on Linux but panics on macOS: +/// +/// ```no_run +/// use std::time::{Instant, Duration}; +/// +/// let now = Instant::now(); +/// let max_nanoseconds = u64::MAX / 1_000_000_000; +/// let duration = Duration::new(max_nanoseconds, 0); +/// println!("{:?}", now + duration); +/// ``` +/// /// # Underlying System calls /// Currently, the following system calls are being used to get the current time using `now()`: /// From d6156e8fe5619143c687983d3ffa5b7ccc37c77e Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Mon, 8 Jun 2020 09:02:57 -0700 Subject: [PATCH 046/123] Change how compiler-builtins gets many CGUs This commit intends to fix an accidental regression from #70846. The goal of #70846 was to build compiler-builtins with a maximal number of CGUs to ensure that each module in the source corresponds to an object file. This high degree of control for compiler-builtins is desirable to ensure that there's at most one exported symbol per CGU, ideally enabling compiler-builtins to not conflict with the system libgcc as often. In #70846, however, only part of the compiler understands that compiler-builtins is built with many CGUs. The rest of the compiler thinks it's building with `sess.codegen_units()`. Notably the calculation of `sess.lto()` consults `sess.codegen_units()`, which when there's only one CGU it disables ThinLTO. This means that compiler-builtins is built without ThinLTO, which is quite harmful to performance! This is the root of the cause from #73135 where intrinsics were found to not be inlining trivial functions. The fix applied in this commit is to remove the special-casing of compiler-builtins in the compiler. Instead the build system is now responsible for special-casing compiler-builtins. It doesn't know exactly how many CGUs will be needed but it passes a large number that is assumed to be much greater than the number of source-level modules needed. After reading the various locations in the compiler source, this seemed like the best solution rather than adding more and more special casing in the compiler for compiler-builtins. Closes #73135 --- Cargo.toml | 13 ++++++ src/librustc_mir/monomorphize/partitioning.rs | 9 +---- .../partitioning/compiler-builtins.rs | 40 ------------------- 3 files changed, 14 insertions(+), 48 deletions(-) delete mode 100644 src/test/codegen-units/partitioning/compiler-builtins.rs diff --git a/Cargo.toml b/Cargo.toml index f2177a99a9b8..f10d539d8296 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -42,6 +42,19 @@ debug-assertions = false debug = false debug-assertions = false +[profile.release.package.compiler_builtins] +# For compiler-builtins we always use a high number of codegen units. +# The goal here is to place every single intrinsic into its own object +# file to avoid symbol clashes with the system libgcc if possible. Note +# that this number doesn't actually produce this many object files, we +# just don't create more than this number of object files. +# +# It's a bit of a bummer that we have to pass this here, unfortunately. +# Ideally this would be specified through an env var to Cargo so Cargo +# knows how many CGUs are for this specific crate, but for now +# per-crate configuration isn't specifiable in the environment. +codegen-units = 10000 + # We want the RLS to use the version of Cargo that we've got vendored in this # repository to ensure that the same exact version of Cargo is used by both the # RLS and the Cargo binary itself. The RLS depends on Cargo as a git repository diff --git a/src/librustc_mir/monomorphize/partitioning.rs b/src/librustc_mir/monomorphize/partitioning.rs index db1ea72c0a53..a945c1d626a9 100644 --- a/src/librustc_mir/monomorphize/partitioning.rs +++ b/src/librustc_mir/monomorphize/partitioning.rs @@ -454,18 +454,11 @@ fn default_visibility(tcx: TyCtxt<'_>, id: DefId, is_generic: bool) -> Visibilit fn merge_codegen_units<'tcx>( tcx: TyCtxt<'tcx>, initial_partitioning: &mut PreInliningPartitioning<'tcx>, - mut target_cgu_count: usize, + target_cgu_count: usize, ) { assert!(target_cgu_count >= 1); let codegen_units = &mut initial_partitioning.codegen_units; - if tcx.is_compiler_builtins(LOCAL_CRATE) { - // Compiler builtins require some degree of control over how mono items - // are partitioned into compilation units. Provide it by keeping the - // original partitioning when compiling the compiler builtins crate. - target_cgu_count = codegen_units.len(); - } - // Note that at this point in time the `codegen_units` here may not be in a // deterministic order (but we know they're deterministically the same set). // We want this merging to produce a deterministic ordering of codegen units diff --git a/src/test/codegen-units/partitioning/compiler-builtins.rs b/src/test/codegen-units/partitioning/compiler-builtins.rs deleted file mode 100644 index 25195743b040..000000000000 --- a/src/test/codegen-units/partitioning/compiler-builtins.rs +++ /dev/null @@ -1,40 +0,0 @@ -// Verifies that during compiler_builtins compilation the codegen units are kept -// unmerged. Even when only a single codegen unit is requested with -Ccodegen-units=1. -// -// compile-flags: -Zprint-mono-items=eager -Ccodegen-units=1 - -#![compiler_builtins] -#![crate_type="lib"] -#![feature(compiler_builtins)] - -mod atomics { - //~ MONO_ITEM fn compiler_builtins::atomics[0]::sync_1[0] @@ compiler_builtins-cgu.0[External] - #[no_mangle] - pub extern "C" fn sync_1() {} - - //~ MONO_ITEM fn compiler_builtins::atomics[0]::sync_2[0] @@ compiler_builtins-cgu.0[External] - #[no_mangle] - pub extern "C" fn sync_2() {} - - //~ MONO_ITEM fn compiler_builtins::atomics[0]::sync_3[0] @@ compiler_builtins-cgu.0[External] - #[no_mangle] - pub extern "C" fn sync_3() {} -} - -mod x { - //~ MONO_ITEM fn compiler_builtins::x[0]::x[0] @@ compiler_builtins-cgu.1[External] - #[no_mangle] - pub extern "C" fn x() {} -} - -mod y { - //~ MONO_ITEM fn compiler_builtins::y[0]::y[0] @@ compiler_builtins-cgu.2[External] - #[no_mangle] - pub extern "C" fn y() {} -} - -mod z { - //~ MONO_ITEM fn compiler_builtins::z[0]::z[0] @@ compiler_builtins-cgu.3[External] - #[no_mangle] - pub extern "C" fn z() {} -} From e390acdfccdc5297e8fbb186bbb890cb6a3d0e57 Mon Sep 17 00:00:00 2001 From: Gary Guo Date: Mon, 15 Jun 2020 15:59:51 +0100 Subject: [PATCH 047/123] Use expr_ty_adjusted in convert_place_op_to_mutable --- src/librustc_typeck/check/reconciliation.rs | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/src/librustc_typeck/check/reconciliation.rs b/src/librustc_typeck/check/reconciliation.rs index b05155ae2aed..0a4293140a82 100644 --- a/src/librustc_typeck/check/reconciliation.rs +++ b/src/librustc_typeck/check/reconciliation.rs @@ -89,17 +89,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { return; } + // Need to deref because overloaded place ops take self by-reference. let base_ty = self .tables .borrow() - .expr_adjustments(base_expr) - .last() - .map_or_else(|| self.node_ty(expr.hir_id), |adj| adj.target); - let base_ty = self.resolve_vars_if_possible(&base_ty); - - // Need to deref because overloaded place ops take self by-reference. - let base_ty = - base_ty.builtin_deref(false).expect("place op takes something that is not a ref").ty; + .expr_ty_adjusted(base_expr) + .builtin_deref(false) + .expect("place op takes something that is not a ref") + .ty; let method = self.try_overloaded_place_op(expr.span, base_ty, arg_tys, Needs::MutPlace, op); let method = match method { From e857696cf8c3b4a4381d00424f165c10f93a9ebd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Sun, 14 Jun 2020 21:36:25 -0700 Subject: [PATCH 048/123] Tweak "non-primitive cast" error - Suggest borrowing expression if it would allow cast to work. - Suggest using `::from()` when appropriate. - Minor tweak to `;` typo suggestion. Partily address #47136. --- src/libcore/convert/mod.rs | 1 + src/librustc_parse/parser/diagnostics.rs | 2 +- src/librustc_span/symbol.rs | 1 + src/librustc_typeck/check/cast.rs | 107 ++++++++++++++---- src/test/ui/cast/cast-from-nil.stderr | 4 +- src/test/ui/cast/cast-to-bare-fn.stderr | 8 +- src/test/ui/cast/cast-to-nil.stderr | 4 +- ...-to-unsized-trait-object-suggestion.stderr | 2 +- src/test/ui/closures/closure-no-fn-3.stderr | 4 +- .../ui/coercion/coerce-to-bang-cast.stderr | 8 +- .../const-eval/const-eval-overflow-4b.stderr | 2 +- src/test/ui/error-codes/E0604.stderr | 2 +- src/test/ui/error-codes/E0605.stderr | 8 +- src/test/ui/error-festival.stderr | 6 +- src/test/ui/fat-ptr-cast.stderr | 4 +- src/test/ui/issues/issue-10991.stderr | 4 +- src/test/ui/issues/issue-16048.rs | 4 +- src/test/ui/issues/issue-16048.stderr | 8 +- src/test/ui/issues/issue-17441.stderr | 2 +- src/test/ui/issues/issue-22289.stderr | 7 +- src/test/ui/issues/issue-22312.rs | 2 +- src/test/ui/issues/issue-22312.stderr | 7 +- src/test/ui/issues/issue-2995.stderr | 4 +- src/test/ui/issues/issue-45730.stderr | 18 +-- .../ui/mismatched_types/cast-rfc0401.stderr | 22 +--- .../ui/mismatched_types/issue-26480.stderr | 3 +- src/test/ui/nonscalar-cast.fixed | 16 +++ src/test/ui/nonscalar-cast.rs | 8 ++ src/test/ui/nonscalar-cast.stderr | 6 +- .../ui/order-dependent-cast-inference.stderr | 6 +- .../ui/tag-variant-cast-non-nullary.fixed | 20 ++++ src/test/ui/tag-variant-cast-non-nullary.rs | 11 ++ .../ui/tag-variant-cast-non-nullary.stderr | 6 +- .../never_reveal_concrete_type.stderr | 4 +- .../uninhabited/uninhabited-enum-cast.stderr | 4 +- 35 files changed, 204 insertions(+), 121 deletions(-) create mode 100644 src/test/ui/nonscalar-cast.fixed create mode 100644 src/test/ui/tag-variant-cast-non-nullary.fixed diff --git a/src/libcore/convert/mod.rs b/src/libcore/convert/mod.rs index eef9ee7cb009..8ff1ced53b07 100644 --- a/src/libcore/convert/mod.rs +++ b/src/libcore/convert/mod.rs @@ -374,6 +374,7 @@ pub trait Into: Sized { /// [`Into`]: trait.Into.html /// [`from`]: trait.From.html#tymethod.from /// [book]: ../../book/ch09-00-error-handling.html +#[rustc_diagnostic_item = "from_trait"] #[stable(feature = "rust1", since = "1.0.0")] #[rustc_on_unimplemented(on( all(_Self = "&str", T = "std::string::String"), diff --git a/src/librustc_parse/parser/diagnostics.rs b/src/librustc_parse/parser/diagnostics.rs index 660a63841bce..dafc0a9e048a 100644 --- a/src/librustc_parse/parser/diagnostics.rs +++ b/src/librustc_parse/parser/diagnostics.rs @@ -961,7 +961,7 @@ impl<'a> Parser<'a> { self.bump(); let sp = self.prev_token.span; self.struct_span_err(sp, &msg) - .span_suggestion(sp, "change this to `;`", ";".to_string(), appl) + .span_suggestion_short(sp, "change this to `;`", ";".to_string(), appl) .emit(); return Ok(()); } else if self.look_ahead(0, |t| { diff --git a/src/librustc_span/symbol.rs b/src/librustc_span/symbol.rs index d165409696ec..9925e631c5c2 100644 --- a/src/librustc_span/symbol.rs +++ b/src/librustc_span/symbol.rs @@ -343,6 +343,7 @@ symbols! { from_method, from_ok, from_usize, + from_trait, fundamental, future, Future, diff --git a/src/librustc_typeck/check/cast.rs b/src/librustc_typeck/check/cast.rs index 46d6706cbf42..bad009f4039a 100644 --- a/src/librustc_typeck/check/cast.rs +++ b/src/librustc_typeck/check/cast.rs @@ -43,6 +43,7 @@ use rustc_middle::ty::subst::SubstsRef; use rustc_middle::ty::{self, Ty, TypeAndMut, TypeFoldable}; use rustc_session::lint; use rustc_session::Session; +use rustc_span::symbol::sym; use rustc_span::Span; use rustc_trait_selection::traits; use rustc_trait_selection::traits::error_reporting::report_object_safety_error; @@ -333,10 +334,11 @@ impl<'a, 'tcx> CastCheck<'tcx> { "only `u8` can be cast as `char`, not `{}`", self.expr_ty ) + .span_label(self.span, "invalid cast") .emit(); } CastError::NonScalar => { - type_error_struct!( + let mut err = type_error_struct!( fcx.tcx.sess, self.span, self.expr_ty, @@ -344,12 +346,75 @@ impl<'a, 'tcx> CastCheck<'tcx> { "non-primitive cast: `{}` as `{}`", self.expr_ty, fcx.ty_to_string(self.cast_ty) - ) - .note( - "an `as` expression can only be used to convert between \ - primitive types. Consider using the `From` trait", - ) - .emit(); + ); + let mut sugg = None; + if let ty::Ref(reg, _, mutbl) = self.cast_ty.kind { + if fcx + .try_coerce( + self.expr, + fcx.tcx.mk_ref(reg, TypeAndMut { ty: self.expr_ty, mutbl }), + self.cast_ty, + AllowTwoPhase::No, + ) + .is_ok() + { + sugg = Some(format!("&{}", mutbl.prefix_str())); + } + } + if let Some(sugg) = sugg { + err.span_label(self.span, "invalid cast"); + err.span_suggestion_verbose( + self.expr.span.shrink_to_lo(), + "borrow the value for the cast to be valid", + sugg, + Applicability::MachineApplicable, + ); + } else if !matches!( + self.cast_ty.kind, + ty::FnDef(..) | ty::FnPtr(..) | ty::Closure(..) + ) { + let mut label = true; + // Check `impl From for self.cast_ty {}` for accurate suggestion: + if let Ok(snippet) = fcx.tcx.sess.source_map().span_to_snippet(self.expr.span) { + if let Some(from_trait) = fcx.tcx.get_diagnostic_item(sym::from_trait) { + let ty = fcx.resolve_vars_if_possible(&self.cast_ty); + // Erase regions to avoid panic in `prove_value` when calling + // `type_implements_trait`. + let ty = fcx.tcx.erase_regions(&ty); + let expr_ty = fcx.resolve_vars_if_possible(&self.expr_ty); + let expr_ty = fcx.tcx.erase_regions(&expr_ty); + let ty_params = fcx.tcx.mk_substs_trait(expr_ty, &[]); + // Check for infer types because cases like `Option<{integer}>` would + // panic otherwise. + if !expr_ty.has_infer_types() + && fcx.tcx.type_implements_trait(( + from_trait, + ty, + ty_params, + fcx.param_env, + )) + { + label = false; + err.span_suggestion( + self.span, + "consider using the `From` trait instead", + format!("{}::from({})", self.cast_ty, snippet), + Applicability::MaybeIncorrect, + ); + } + } + } + let msg = "an `as` expression can only be used to convert between primitive \ + types or to coerce to a specific trait object"; + if label { + err.span_label(self.span, msg); + } else { + err.note(msg); + } + } else { + err.span_label(self.span, "invalid cast"); + } + err.emit(); } CastError::SizedUnsizedCast => { use crate::structured_errors::{SizedUnsizedCastError, StructuredDiagnostic}; @@ -370,21 +435,22 @@ impl<'a, 'tcx> CastCheck<'tcx> { }; let mut err = struct_span_err!( fcx.tcx.sess, - self.span, + if unknown_cast_to { self.cast_span } else { self.span }, E0641, "cannot cast {} a pointer of an unknown kind", if unknown_cast_to { "to" } else { "from" } ); - err.note( - "the type information given here is insufficient to check whether \ - the pointer cast is valid", - ); if unknown_cast_to { - err.span_suggestion_short( - self.cast_span, - "consider giving more type information", - String::new(), - Applicability::Unspecified, + err.span_label(self.cast_span, "needs more type information"); + err.note( + "the type information given here is insufficient to check whether \ + the pointer cast is valid", + ); + } else { + err.span_label( + self.span, + "the type information given here is insufficient to check whether \ + the pointer cast is valid", ); } err.emit(); @@ -438,13 +504,16 @@ impl<'a, 'tcx> CastCheck<'tcx> { Ok(s) => { err.span_suggestion( self.cast_span, - "try casting to a `Box` instead", + "you can cast to a `Box` instead", format!("Box<{}>", s), Applicability::MachineApplicable, ); } Err(_) => { - err.span_help(self.cast_span, &format!("did you mean `Box<{}>`?", tstr)); + err.span_help( + self.cast_span, + &format!("you might have meant `Box<{}>`", tstr), + ); } } } diff --git a/src/test/ui/cast/cast-from-nil.stderr b/src/test/ui/cast/cast-from-nil.stderr index c8e3628a7ded..dab133cfb4b6 100644 --- a/src/test/ui/cast/cast-from-nil.stderr +++ b/src/test/ui/cast/cast-from-nil.stderr @@ -2,9 +2,7 @@ error[E0605]: non-primitive cast: `()` as `u32` --> $DIR/cast-from-nil.rs:2:21 | LL | fn main() { let u = (assert!(true) as u32); } - | ^^^^^^^^^^^^^^^^^^^^^^ - | - = note: an `as` expression can only be used to convert between primitive types. Consider using the `From` trait + | ^^^^^^^^^^^^^^^^^^^^^^ an `as` expression can only be used to convert between primitive types or to coerce to a specific trait object error: aborting due to previous error diff --git a/src/test/ui/cast/cast-to-bare-fn.stderr b/src/test/ui/cast/cast-to-bare-fn.stderr index 84933dca929a..d97b0c5f8aad 100644 --- a/src/test/ui/cast/cast-to-bare-fn.stderr +++ b/src/test/ui/cast/cast-to-bare-fn.stderr @@ -2,17 +2,13 @@ error[E0605]: non-primitive cast: `fn(isize) {foo}` as `extern "C" fn() -> isize --> $DIR/cast-to-bare-fn.rs:5:13 | LL | let x = foo as extern "C" fn() -> isize; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: an `as` expression can only be used to convert between primitive types. Consider using the `From` trait + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ invalid cast error[E0605]: non-primitive cast: `u64` as `fn(isize) -> (isize, isize)` --> $DIR/cast-to-bare-fn.rs:7:13 | LL | let y = v as extern "Rust" fn(isize) -> (isize, isize); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: an `as` expression can only be used to convert between primitive types. Consider using the `From` trait + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ invalid cast error: aborting due to 2 previous errors diff --git a/src/test/ui/cast/cast-to-nil.stderr b/src/test/ui/cast/cast-to-nil.stderr index 478f6b69dafc..29a9baffd71d 100644 --- a/src/test/ui/cast/cast-to-nil.stderr +++ b/src/test/ui/cast/cast-to-nil.stderr @@ -2,9 +2,7 @@ error[E0605]: non-primitive cast: `u32` as `()` --> $DIR/cast-to-nil.rs:2:21 | LL | fn main() { let u = 0u32 as (); } - | ^^^^^^^^^^ - | - = note: an `as` expression can only be used to convert between primitive types. Consider using the `From` trait + | ^^^^^^^^^^ an `as` expression can only be used to convert between primitive types or to coerce to a specific trait object error: aborting due to previous error diff --git a/src/test/ui/cast/cast-to-unsized-trait-object-suggestion.stderr b/src/test/ui/cast/cast-to-unsized-trait-object-suggestion.stderr index ffa02533d8b6..9b86f8d4def8 100644 --- a/src/test/ui/cast/cast-to-unsized-trait-object-suggestion.stderr +++ b/src/test/ui/cast/cast-to-unsized-trait-object-suggestion.stderr @@ -12,7 +12,7 @@ error[E0620]: cast to unsized type: `std::boxed::Box<{integer}>` as `dyn std::ma LL | Box::new(1) as dyn Send; | ^^^^^^^^^^^^^^^-------- | | - | help: try casting to a `Box` instead: `Box` + | help: you can cast to a `Box` instead: `Box` error: aborting due to 2 previous errors diff --git a/src/test/ui/closures/closure-no-fn-3.stderr b/src/test/ui/closures/closure-no-fn-3.stderr index ab6056b65473..4b3b4be798fc 100644 --- a/src/test/ui/closures/closure-no-fn-3.stderr +++ b/src/test/ui/closures/closure-no-fn-3.stderr @@ -2,9 +2,7 @@ error[E0605]: non-primitive cast: `[closure@$DIR/closure-no-fn-3.rs:6:27: 6:37 b --> $DIR/closure-no-fn-3.rs:6:27 | LL | let baz: fn() -> u8 = (|| { b }) as fn() -> u8; - | ^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: an `as` expression can only be used to convert between primitive types. Consider using the `From` trait + | ^^^^^^^^^^^^^^^^^^^^^^^^ invalid cast error: aborting due to previous error diff --git a/src/test/ui/coercion/coerce-to-bang-cast.stderr b/src/test/ui/coercion/coerce-to-bang-cast.stderr index ff30ebc09c63..d3adbd5158db 100644 --- a/src/test/ui/coercion/coerce-to-bang-cast.stderr +++ b/src/test/ui/coercion/coerce-to-bang-cast.stderr @@ -2,17 +2,13 @@ error[E0605]: non-primitive cast: `i32` as `!` --> $DIR/coerce-to-bang-cast.rs:6:13 | LL | let y = {return; 22} as !; - | ^^^^^^^^^^^^^^^^^ - | - = note: an `as` expression can only be used to convert between primitive types. Consider using the `From` trait + | ^^^^^^^^^^^^^^^^^ an `as` expression can only be used to convert between primitive types or to coerce to a specific trait object error[E0605]: non-primitive cast: `i32` as `!` --> $DIR/coerce-to-bang-cast.rs:11:13 | LL | let y = 22 as !; - | ^^^^^^^ - | - = note: an `as` expression can only be used to convert between primitive types. Consider using the `From` trait + | ^^^^^^^ an `as` expression can only be used to convert between primitive types or to coerce to a specific trait object error: aborting due to 2 previous errors diff --git a/src/test/ui/consts/const-eval/const-eval-overflow-4b.stderr b/src/test/ui/consts/const-eval/const-eval-overflow-4b.stderr index 5b2c4116c4b1..e4d256c0ad19 100644 --- a/src/test/ui/consts/const-eval/const-eval-overflow-4b.stderr +++ b/src/test/ui/consts/const-eval/const-eval-overflow-4b.stderr @@ -16,7 +16,7 @@ error[E0604]: only `u8` can be cast as `char`, not `i8` --> $DIR/const-eval-overflow-4b.rs:25:13 | LL | : [u32; 5i8 as char as usize] - | ^^^^^^^^^^^ + | ^^^^^^^^^^^ invalid cast error: aborting due to 3 previous errors diff --git a/src/test/ui/error-codes/E0604.stderr b/src/test/ui/error-codes/E0604.stderr index 5861bdcb7a95..18835310bd5e 100644 --- a/src/test/ui/error-codes/E0604.stderr +++ b/src/test/ui/error-codes/E0604.stderr @@ -2,7 +2,7 @@ error[E0604]: only `u8` can be cast as `char`, not `u32` --> $DIR/E0604.rs:2:5 | LL | 1u32 as char; - | ^^^^^^^^^^^^ + | ^^^^^^^^^^^^ invalid cast error: aborting due to previous error diff --git a/src/test/ui/error-codes/E0605.stderr b/src/test/ui/error-codes/E0605.stderr index 95e899db8b7e..f23d2008e0b5 100644 --- a/src/test/ui/error-codes/E0605.stderr +++ b/src/test/ui/error-codes/E0605.stderr @@ -2,17 +2,13 @@ error[E0605]: non-primitive cast: `u8` as `std::vec::Vec` --> $DIR/E0605.rs:3:5 | LL | x as Vec; - | ^^^^^^^^^^^^ - | - = note: an `as` expression can only be used to convert between primitive types. Consider using the `From` trait + | ^^^^^^^^^^^^ an `as` expression can only be used to convert between primitive types or to coerce to a specific trait object error[E0605]: non-primitive cast: `*const u8` as `&u8` --> $DIR/E0605.rs:6:5 | LL | v as &u8; - | ^^^^^^^^ - | - = note: an `as` expression can only be used to convert between primitive types. Consider using the `From` trait + | ^^^^^^^^ an `as` expression can only be used to convert between primitive types or to coerce to a specific trait object error: aborting due to 2 previous errors diff --git a/src/test/ui/error-festival.stderr b/src/test/ui/error-festival.stderr index 7f524230ef00..905195d4ad96 100644 --- a/src/test/ui/error-festival.stderr +++ b/src/test/ui/error-festival.stderr @@ -42,15 +42,13 @@ error[E0604]: only `u8` can be cast as `char`, not `u32` --> $DIR/error-festival.rs:25:5 | LL | 0u32 as char; - | ^^^^^^^^^^^^ + | ^^^^^^^^^^^^ invalid cast error[E0605]: non-primitive cast: `u8` as `std::vec::Vec` --> $DIR/error-festival.rs:29:5 | LL | x as Vec; - | ^^^^^^^^^^^^ - | - = note: an `as` expression can only be used to convert between primitive types. Consider using the `From` trait + | ^^^^^^^^^^^^ an `as` expression can only be used to convert between primitive types or to coerce to a specific trait object error[E0054]: cannot cast as `bool` --> $DIR/error-festival.rs:33:24 diff --git a/src/test/ui/fat-ptr-cast.stderr b/src/test/ui/fat-ptr-cast.stderr index 93e1471838f7..56d5a26beb04 100644 --- a/src/test/ui/fat-ptr-cast.stderr +++ b/src/test/ui/fat-ptr-cast.stderr @@ -34,9 +34,7 @@ error[E0605]: non-primitive cast: `std::boxed::Box<[i32]>` as `usize` --> $DIR/fat-ptr-cast.rs:14:5 | LL | b as usize; - | ^^^^^^^^^^ - | - = note: an `as` expression can only be used to convert between primitive types. Consider using the `From` trait + | ^^^^^^^^^^ an `as` expression can only be used to convert between primitive types or to coerce to a specific trait object error[E0606]: casting `*const [i32]` as `usize` is invalid --> $DIR/fat-ptr-cast.rs:15:5 diff --git a/src/test/ui/issues/issue-10991.stderr b/src/test/ui/issues/issue-10991.stderr index f12539b47cf4..5b8a18233869 100644 --- a/src/test/ui/issues/issue-10991.stderr +++ b/src/test/ui/issues/issue-10991.stderr @@ -2,9 +2,7 @@ error[E0605]: non-primitive cast: `()` as `usize` --> $DIR/issue-10991.rs:3:14 | LL | let _t = nil as usize; - | ^^^^^^^^^^^^ - | - = note: an `as` expression can only be used to convert between primitive types. Consider using the `From` trait + | ^^^^^^^^^^^^ an `as` expression can only be used to convert between primitive types or to coerce to a specific trait object error: aborting due to previous error diff --git a/src/test/ui/issues/issue-16048.rs b/src/test/ui/issues/issue-16048.rs index 7d24f3a40a74..eaf6acff26bf 100644 --- a/src/test/ui/issues/issue-16048.rs +++ b/src/test/ui/issues/issue-16048.rs @@ -18,12 +18,12 @@ impl<'a> Test<'a> for Foo<'a> { } impl<'a> NoLifetime for Foo<'a> { - fn get<'p, T : Test<'a>>(&self) -> T { + fn get<'p, T: Test<'a> + From>>(&self) -> T { //~^ ERROR E0195 //~| NOTE lifetimes do not match method in trait return *self as T; //~^ ERROR non-primitive cast: `Foo<'a>` as `T` - //~| NOTE an `as` expression can only be used to convert between primitive types. + //~| NOTE an `as` expression can only be used to convert between primitive types } } diff --git a/src/test/ui/issues/issue-16048.stderr b/src/test/ui/issues/issue-16048.stderr index a137bcdf1915..73610942d7a7 100644 --- a/src/test/ui/issues/issue-16048.stderr +++ b/src/test/ui/issues/issue-16048.stderr @@ -4,16 +4,16 @@ error[E0195]: lifetime parameters or bounds on method `get` do not match the tra LL | fn get<'p, T : Test<'p>>(&self) -> T; | ------------------ lifetimes in impl do not match this method in trait ... -LL | fn get<'p, T : Test<'a>>(&self) -> T { - | ^^^^^^^^^^^^^^^^^^ lifetimes do not match method in trait +LL | fn get<'p, T: Test<'a> + From>>(&self) -> T { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ lifetimes do not match method in trait error[E0605]: non-primitive cast: `Foo<'a>` as `T` --> $DIR/issue-16048.rs:24:16 | LL | return *self as T; - | ^^^^^^^^^^ + | ^^^^^^^^^^ help: consider using the `From` trait instead: `T::from(*self)` | - = note: an `as` expression can only be used to convert between primitive types. Consider using the `From` trait + = note: an `as` expression can only be used to convert between primitive types or to coerce to a specific trait object error: aborting due to 2 previous errors diff --git a/src/test/ui/issues/issue-17441.stderr b/src/test/ui/issues/issue-17441.stderr index 0ab035515a05..b63a3995d255 100644 --- a/src/test/ui/issues/issue-17441.stderr +++ b/src/test/ui/issues/issue-17441.stderr @@ -16,7 +16,7 @@ error[E0620]: cast to unsized type: `std::boxed::Box` as `dyn std::fmt::D LL | let _bar = Box::new(1_usize) as dyn std::fmt::Debug; | ^^^^^^^^^^^^^^^^^^^^^------------------- | | - | help: try casting to a `Box` instead: `Box` + | help: you can cast to a `Box` instead: `Box` error[E0620]: cast to unsized type: `usize` as `dyn std::fmt::Debug` --> $DIR/issue-17441.rs:8:16 diff --git a/src/test/ui/issues/issue-22289.stderr b/src/test/ui/issues/issue-22289.stderr index cc7ace30cabe..4c35deb1fbe4 100644 --- a/src/test/ui/issues/issue-22289.stderr +++ b/src/test/ui/issues/issue-22289.stderr @@ -2,9 +2,12 @@ error[E0605]: non-primitive cast: `i32` as `&(dyn std::any::Any + 'static)` --> $DIR/issue-22289.rs:2:5 | LL | 0 as &dyn std::any::Any; - | ^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^ invalid cast | - = note: an `as` expression can only be used to convert between primitive types. Consider using the `From` trait +help: borrow the value for the cast to be valid + | +LL | &0 as &dyn std::any::Any; + | ^ error: aborting due to previous error diff --git a/src/test/ui/issues/issue-22312.rs b/src/test/ui/issues/issue-22312.rs index 250fec258870..4e359b3412a7 100644 --- a/src/test/ui/issues/issue-22312.rs +++ b/src/test/ui/issues/issue-22312.rs @@ -1,6 +1,6 @@ use std::ops::Index; -pub trait Array2D: Index { +pub trait Array2D: Index + Sized { fn rows(&self) -> usize; fn columns(&self) -> usize; fn get<'a>(&'a self, y: usize, x: usize) -> Option<&'a >::Output> { diff --git a/src/test/ui/issues/issue-22312.stderr b/src/test/ui/issues/issue-22312.stderr index fc32fd376b75..28564b074633 100644 --- a/src/test/ui/issues/issue-22312.stderr +++ b/src/test/ui/issues/issue-22312.stderr @@ -2,9 +2,12 @@ error[E0605]: non-primitive cast: `Self` as `&dyn std::ops::Index $DIR/issue-22312.rs:11:24 | LL | let indexer = &(*self as &dyn Index>::Output>); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ invalid cast | - = note: an `as` expression can only be used to convert between primitive types. Consider using the `From` trait +help: borrow the value for the cast to be valid + | +LL | let indexer = &(&*self as &dyn Index>::Output>); + | ^ error: aborting due to previous error diff --git a/src/test/ui/issues/issue-2995.stderr b/src/test/ui/issues/issue-2995.stderr index c316780d5f6a..9f5968399a37 100644 --- a/src/test/ui/issues/issue-2995.stderr +++ b/src/test/ui/issues/issue-2995.stderr @@ -2,9 +2,7 @@ error[E0605]: non-primitive cast: `*const isize` as `&isize` --> $DIR/issue-2995.rs:2:22 | LL | let _q: &isize = p as &isize; - | ^^^^^^^^^^^ - | - = note: an `as` expression can only be used to convert between primitive types. Consider using the `From` trait + | ^^^^^^^^^^^ an `as` expression can only be used to convert between primitive types or to coerce to a specific trait object error: aborting due to previous error diff --git a/src/test/ui/issues/issue-45730.stderr b/src/test/ui/issues/issue-45730.stderr index d4ddba52df14..d00f3d91b49d 100644 --- a/src/test/ui/issues/issue-45730.stderr +++ b/src/test/ui/issues/issue-45730.stderr @@ -1,30 +1,24 @@ error[E0641]: cannot cast to a pointer of an unknown kind - --> $DIR/issue-45730.rs:3:23 + --> $DIR/issue-45730.rs:3:28 | LL | let x: *const _ = 0 as _; - | ^^^^^- - | | - | help: consider giving more type information + | ^ needs more type information | = note: the type information given here is insufficient to check whether the pointer cast is valid error[E0641]: cannot cast to a pointer of an unknown kind - --> $DIR/issue-45730.rs:5:23 + --> $DIR/issue-45730.rs:5:28 | LL | let x: *const _ = 0 as *const _; - | ^^^^^-------- - | | - | help: consider giving more type information + | ^^^^^^^^ needs more type information | = note: the type information given here is insufficient to check whether the pointer cast is valid error[E0641]: cannot cast to a pointer of an unknown kind - --> $DIR/issue-45730.rs:8:13 + --> $DIR/issue-45730.rs:8:44 | LL | let x = 0 as *const i32 as *const _ as *mut _; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^------ - | | - | help: consider giving more type information + | ^^^^^^ needs more type information | = note: the type information given here is insufficient to check whether the pointer cast is valid diff --git a/src/test/ui/mismatched_types/cast-rfc0401.stderr b/src/test/ui/mismatched_types/cast-rfc0401.stderr index f94dfd100a6f..95936de218b8 100644 --- a/src/test/ui/mismatched_types/cast-rfc0401.stderr +++ b/src/test/ui/mismatched_types/cast-rfc0401.stderr @@ -24,41 +24,31 @@ error[E0605]: non-primitive cast: `*const u8` as `&u8` --> $DIR/cast-rfc0401.rs:29:13 | LL | let _ = v as &u8; - | ^^^^^^^^ - | - = note: an `as` expression can only be used to convert between primitive types. Consider using the `From` trait + | ^^^^^^^^ an `as` expression can only be used to convert between primitive types or to coerce to a specific trait object error[E0605]: non-primitive cast: `*const u8` as `E` --> $DIR/cast-rfc0401.rs:30:13 | LL | let _ = v as E; - | ^^^^^^ - | - = note: an `as` expression can only be used to convert between primitive types. Consider using the `From` trait + | ^^^^^^ an `as` expression can only be used to convert between primitive types or to coerce to a specific trait object error[E0605]: non-primitive cast: `*const u8` as `fn()` --> $DIR/cast-rfc0401.rs:31:13 | LL | let _ = v as fn(); - | ^^^^^^^^^ - | - = note: an `as` expression can only be used to convert between primitive types. Consider using the `From` trait + | ^^^^^^^^^ invalid cast error[E0605]: non-primitive cast: `*const u8` as `(u32,)` --> $DIR/cast-rfc0401.rs:32:13 | LL | let _ = v as (u32,); - | ^^^^^^^^^^^ - | - = note: an `as` expression can only be used to convert between primitive types. Consider using the `From` trait + | ^^^^^^^^^^^ an `as` expression can only be used to convert between primitive types or to coerce to a specific trait object error[E0605]: non-primitive cast: `std::option::Option<&*const u8>` as `*const u8` --> $DIR/cast-rfc0401.rs:33:13 | LL | let _ = Some(&v) as *const u8; - | ^^^^^^^^^^^^^^^^^^^^^ - | - = note: an `as` expression can only be used to convert between primitive types. Consider using the `From` trait + | ^^^^^^^^^^^^^^^^^^^^^ an `as` expression can only be used to convert between primitive types or to coerce to a specific trait object error[E0606]: casting `*const u8` as `f32` is invalid --> $DIR/cast-rfc0401.rs:35:13 @@ -102,7 +92,7 @@ error[E0604]: only `u8` can be cast as `char`, not `u32` --> $DIR/cast-rfc0401.rs:41:13 | LL | let _ = 0x61u32 as char; - | ^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^ invalid cast error[E0606]: casting `bool` as `f32` is invalid --> $DIR/cast-rfc0401.rs:43:13 diff --git a/src/test/ui/mismatched_types/issue-26480.stderr b/src/test/ui/mismatched_types/issue-26480.stderr index 69a9d03e474b..d39b0a320776 100644 --- a/src/test/ui/mismatched_types/issue-26480.stderr +++ b/src/test/ui/mismatched_types/issue-26480.stderr @@ -17,12 +17,11 @@ error[E0605]: non-primitive cast: `{integer}` as `()` --> $DIR/issue-26480.rs:22:19 | LL | ($x:expr) => ($x as ()) - | ^^^^^^^^ + | ^^^^^^^^ an `as` expression can only be used to convert between primitive types or to coerce to a specific trait object ... LL | cast!(2); | --------- in this macro invocation | - = note: an `as` expression can only be used to convert between primitive types. Consider using the `From` trait = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to 2 previous errors diff --git a/src/test/ui/nonscalar-cast.fixed b/src/test/ui/nonscalar-cast.fixed new file mode 100644 index 000000000000..0a4b98469b2b --- /dev/null +++ b/src/test/ui/nonscalar-cast.fixed @@ -0,0 +1,16 @@ +// run-rustfix + +#[derive(Debug)] +struct Foo { + x: isize +} + +impl From for isize { + fn from(val: Foo) -> isize { + val.x + } +} + +fn main() { + println!("{}", isize::from(Foo { x: 1 })); //~ non-primitive cast: `Foo` as `isize` [E0605] +} diff --git a/src/test/ui/nonscalar-cast.rs b/src/test/ui/nonscalar-cast.rs index 7e6f1fd038fb..59fcf09666b2 100644 --- a/src/test/ui/nonscalar-cast.rs +++ b/src/test/ui/nonscalar-cast.rs @@ -1,8 +1,16 @@ +// run-rustfix + #[derive(Debug)] struct Foo { x: isize } +impl From for isize { + fn from(val: Foo) -> isize { + val.x + } +} + fn main() { println!("{}", Foo { x: 1 } as isize); //~ non-primitive cast: `Foo` as `isize` [E0605] } diff --git a/src/test/ui/nonscalar-cast.stderr b/src/test/ui/nonscalar-cast.stderr index 9338688b037f..2a7037121876 100644 --- a/src/test/ui/nonscalar-cast.stderr +++ b/src/test/ui/nonscalar-cast.stderr @@ -1,10 +1,10 @@ error[E0605]: non-primitive cast: `Foo` as `isize` - --> $DIR/nonscalar-cast.rs:7:20 + --> $DIR/nonscalar-cast.rs:15:20 | LL | println!("{}", Foo { x: 1 } as isize); - | ^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^ help: consider using the `From` trait instead: `isize::from(Foo { x: 1 })` | - = note: an `as` expression can only be used to convert between primitive types. Consider using the `From` trait + = note: an `as` expression can only be used to convert between primitive types or to coerce to a specific trait object error: aborting due to previous error diff --git a/src/test/ui/order-dependent-cast-inference.stderr b/src/test/ui/order-dependent-cast-inference.stderr index ad50b415869d..9f4ac0fea36e 100644 --- a/src/test/ui/order-dependent-cast-inference.stderr +++ b/src/test/ui/order-dependent-cast-inference.stderr @@ -1,10 +1,8 @@ error[E0641]: cannot cast to a pointer of an unknown kind - --> $DIR/order-dependent-cast-inference.rs:5:17 + --> $DIR/order-dependent-cast-inference.rs:5:22 | LL | let mut y = 0 as *const _; - | ^^^^^-------- - | | - | help: consider giving more type information + | ^^^^^^^^ needs more type information | = note: the type information given here is insufficient to check whether the pointer cast is valid diff --git a/src/test/ui/tag-variant-cast-non-nullary.fixed b/src/test/ui/tag-variant-cast-non-nullary.fixed new file mode 100644 index 000000000000..53e68c2ac6af --- /dev/null +++ b/src/test/ui/tag-variant-cast-non-nullary.fixed @@ -0,0 +1,20 @@ +// run-rustfix +#![allow(dead_code, unused_variables)] +enum NonNullary { + Nullary, + Other(isize), +} + +impl From for isize { + fn from(val: NonNullary) -> isize { + match val { + NonNullary::Nullary => 0, + NonNullary::Other(i) => i, + } + } +} + +fn main() { + let v = NonNullary::Nullary; + let val = isize::from(v); //~ ERROR non-primitive cast: `NonNullary` as `isize` [E0605] +} diff --git a/src/test/ui/tag-variant-cast-non-nullary.rs b/src/test/ui/tag-variant-cast-non-nullary.rs index bb34e82cdca3..0d0c6188ad11 100644 --- a/src/test/ui/tag-variant-cast-non-nullary.rs +++ b/src/test/ui/tag-variant-cast-non-nullary.rs @@ -1,8 +1,19 @@ +// run-rustfix +#![allow(dead_code, unused_variables)] enum NonNullary { Nullary, Other(isize), } +impl From for isize { + fn from(val: NonNullary) -> isize { + match val { + NonNullary::Nullary => 0, + NonNullary::Other(i) => i, + } + } +} + fn main() { let v = NonNullary::Nullary; let val = v as isize; //~ ERROR non-primitive cast: `NonNullary` as `isize` [E0605] diff --git a/src/test/ui/tag-variant-cast-non-nullary.stderr b/src/test/ui/tag-variant-cast-non-nullary.stderr index 87ec20f20d78..ae2f5a7aead5 100644 --- a/src/test/ui/tag-variant-cast-non-nullary.stderr +++ b/src/test/ui/tag-variant-cast-non-nullary.stderr @@ -1,10 +1,10 @@ error[E0605]: non-primitive cast: `NonNullary` as `isize` - --> $DIR/tag-variant-cast-non-nullary.rs:8:15 + --> $DIR/tag-variant-cast-non-nullary.rs:19:15 | LL | let val = v as isize; - | ^^^^^^^^^^ + | ^^^^^^^^^^ help: consider using the `From` trait instead: `isize::from(v)` | - = note: an `as` expression can only be used to convert between primitive types. Consider using the `From` trait + = note: an `as` expression can only be used to convert between primitive types or to coerce to a specific trait object error: aborting due to previous error diff --git a/src/test/ui/type-alias-impl-trait/never_reveal_concrete_type.stderr b/src/test/ui/type-alias-impl-trait/never_reveal_concrete_type.stderr index 70c99c944d65..360633bba622 100644 --- a/src/test/ui/type-alias-impl-trait/never_reveal_concrete_type.stderr +++ b/src/test/ui/type-alias-impl-trait/never_reveal_concrete_type.stderr @@ -16,9 +16,7 @@ error[E0605]: non-primitive cast: `NoReveal` as `&'static str` --> $DIR/never_reveal_concrete_type.rs:14:13 | LL | let _ = x as &'static str; - | ^^^^^^^^^^^^^^^^^ - | - = note: an `as` expression can only be used to convert between primitive types. Consider using the `From` trait + | ^^^^^^^^^^^^^^^^^ an `as` expression can only be used to convert between primitive types or to coerce to a specific trait object error: aborting due to 2 previous errors diff --git a/src/test/ui/uninhabited/uninhabited-enum-cast.stderr b/src/test/ui/uninhabited/uninhabited-enum-cast.stderr index a39af7832f8c..a9f10dfec994 100644 --- a/src/test/ui/uninhabited/uninhabited-enum-cast.stderr +++ b/src/test/ui/uninhabited/uninhabited-enum-cast.stderr @@ -2,9 +2,7 @@ error[E0605]: non-primitive cast: `E` as `isize` --> $DIR/uninhabited-enum-cast.rs:4:20 | LL | println!("{}", (e as isize).to_string()); - | ^^^^^^^^^^^^ - | - = note: an `as` expression can only be used to convert between primitive types. Consider using the `From` trait + | ^^^^^^^^^^^^ an `as` expression can only be used to convert between primitive types or to coerce to a specific trait object error: aborting due to previous error From 81c909488eebcba16610402349563380772e0d1d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Fri, 29 May 2020 15:09:43 -0700 Subject: [PATCH 049/123] Suggest substituting `'static` lifetime in impl/dyn `Trait + 'static` return types --- .../nice_region_error/static_impl_trait.rs | 64 ++++++++-- src/librustc_middle/ty/context.rs | 13 +- src/librustc_middle/ty/diagnostics.rs | 8 +- ...t_outlive_least_region_or_bound.nll.stderr | 38 +++++- .../must_outlive_least_region_or_bound.rs | 21 ++++ .../must_outlive_least_region_or_bound.stderr | 117 ++++++++++++++++-- 6 files changed, 232 insertions(+), 29 deletions(-) diff --git a/src/librustc_infer/infer/error_reporting/nice_region_error/static_impl_trait.rs b/src/librustc_infer/infer/error_reporting/nice_region_error/static_impl_trait.rs index f4c86ddae604..88d6c23d5144 100644 --- a/src/librustc_infer/infer/error_reporting/nice_region_error/static_impl_trait.rs +++ b/src/librustc_infer/infer/error_reporting/nice_region_error/static_impl_trait.rs @@ -4,6 +4,7 @@ use crate::infer::error_reporting::msg_span_from_free_region; use crate::infer::error_reporting::nice_region_error::NiceRegionError; use crate::infer::lexical_region_resolve::RegionResolutionError; use rustc_errors::{Applicability, ErrorReported}; +use rustc_hir::{GenericBound, ItemKind, Lifetime, LifetimeName, TyKind}; use rustc_middle::ty::RegionKind; impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { @@ -20,8 +21,9 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { ) = error.clone() { let anon_reg_sup = self.tcx().is_suitable_region(sup_r)?; - let (fn_return_span, is_dyn) = - self.tcx().return_type_impl_or_dyn_trait(anon_reg_sup.def_id)?; + let fn_return = self.tcx().return_type_impl_or_dyn_trait(anon_reg_sup.def_id)?; + let is_dyn = matches!(fn_return.kind, TyKind::TraitObject(..)); + let fn_return_span = fn_return.span; if sub_r == &RegionKind::ReStatic { let sp = var_origin.span(); let return_sp = sub_origin.span(); @@ -67,12 +69,58 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { lifetime, ); // FIXME: account for the need of parens in `&(dyn Trait + '_)` - err.span_suggestion_verbose( - fn_return_span.shrink_to_hi(), - &msg, - format!(" + {}", lifetime_name), - Applicability::MaybeIncorrect, - ); + match fn_return.kind { + TyKind::Def(item_id, _) => { + let item = self.tcx().hir().item(item_id.id); + let opaque = if let ItemKind::OpaqueTy(opaque) = &item.kind { + opaque + } else { + err.emit(); + return Some(ErrorReported); + }; + let (span, sugg) = opaque + .bounds + .iter() + .filter_map(|arg| match arg { + GenericBound::Outlives(Lifetime { + name: LifetimeName::Static, + span, + .. + }) => Some((*span, lifetime_name.clone())), + _ => None, + }) + .next() + .unwrap_or_else(|| { + ( + fn_return_span.shrink_to_hi(), + format!(" + {}", lifetime_name), + ) + }); + + err.span_suggestion_verbose( + span, + &msg, + sugg, + Applicability::MaybeIncorrect, + ); + } + TyKind::TraitObject(_, lt) => { + let (span, sugg) = match lt.name { + LifetimeName::ImplicitObjectLifetimeDefault => ( + fn_return_span.shrink_to_hi(), + format!(" + {}", lifetime_name), + ), + _ => (lt.span, lifetime_name), + }; + err.span_suggestion_verbose( + span, + &msg, + sugg, + Applicability::MaybeIncorrect, + ); + } + _ => {} + } } err.emit(); return Some(ErrorReported); diff --git a/src/librustc_middle/ty/context.rs b/src/librustc_middle/ty/context.rs index d5be3508d2d8..4770993d9cb0 100644 --- a/src/librustc_middle/ty/context.rs +++ b/src/librustc_middle/ty/context.rs @@ -1383,7 +1383,10 @@ impl<'tcx> TyCtxt<'tcx> { }) } - pub fn return_type_impl_or_dyn_trait(&self, scope_def_id: DefId) -> Option<(Span, bool)> { + pub fn return_type_impl_or_dyn_trait( + &self, + scope_def_id: DefId, + ) -> Option<&'tcx hir::Ty<'tcx>> { let hir_id = self.hir().as_local_hir_id(scope_def_id.expect_local()); let hir_output = match self.hir().get(hir_id) { Node::Item(hir::Item { @@ -1429,15 +1432,17 @@ impl<'tcx> TyCtxt<'tcx> { let output = self.erase_late_bound_regions(&sig.output()); if output.is_impl_trait() { let fn_decl = self.hir().fn_decl_by_hir_id(hir_id).unwrap(); - Some((fn_decl.output.span(), false)) + if let hir::FnRetTy::Return(ty) = fn_decl.output { + return Some(ty); + } } else { let mut v = TraitObjectVisitor(vec![]); rustc_hir::intravisit::walk_ty(&mut v, hir_output); if v.0.len() == 1 { - return Some((v.0[0], true)); + return Some(v.0[0]); } - None } + None } _ => None, } diff --git a/src/librustc_middle/ty/diagnostics.rs b/src/librustc_middle/ty/diagnostics.rs index 2e9aa724ac5a..3ca506fe0d59 100644 --- a/src/librustc_middle/ty/diagnostics.rs +++ b/src/librustc_middle/ty/diagnostics.rs @@ -236,21 +236,21 @@ pub fn suggest_constraining_type_param( } } -pub struct TraitObjectVisitor(pub Vec); -impl<'v> hir::intravisit::Visitor<'v> for TraitObjectVisitor { +pub struct TraitObjectVisitor<'tcx>(pub Vec<&'tcx hir::Ty<'tcx>>); +impl<'v> hir::intravisit::Visitor<'v> for TraitObjectVisitor<'v> { type Map = rustc_hir::intravisit::ErasedMap<'v>; fn nested_visit_map(&mut self) -> hir::intravisit::NestedVisitorMap { hir::intravisit::NestedVisitorMap::None } - fn visit_ty(&mut self, ty: &hir::Ty<'_>) { + fn visit_ty(&mut self, ty: &'v hir::Ty<'v>) { if let hir::TyKind::TraitObject( _, hir::Lifetime { name: hir::LifetimeName::ImplicitObjectLifetimeDefault, .. }, ) = ty.kind { - self.0.push(ty.span); + self.0.push(ty); } } } diff --git a/src/test/ui/impl-trait/must_outlive_least_region_or_bound.nll.stderr b/src/test/ui/impl-trait/must_outlive_least_region_or_bound.nll.stderr index 1806d2607a3a..ca9ca8a9debe 100644 --- a/src/test/ui/impl-trait/must_outlive_least_region_or_bound.nll.stderr +++ b/src/test/ui/impl-trait/must_outlive_least_region_or_bound.nll.stderr @@ -26,7 +26,34 @@ LL | fn explicit<'a>(x: &'a i32) -> impl Copy + 'a { x } | ^^^^^^^^^^^^^^ error: lifetime may not live long enough - --> $DIR/must_outlive_least_region_or_bound.rs:12:69 + --> $DIR/must_outlive_least_region_or_bound.rs:9:46 + | +LL | fn elided2(x: &i32) -> impl Copy + 'static { x } + | - ^ returning this value requires that `'1` must outlive `'static` + | | + | let's call the lifetime of this reference `'1` + | + = help: consider replacing `'1` with `'static` + +error: lifetime may not live long enough + --> $DIR/must_outlive_least_region_or_bound.rs:12:55 + | +LL | fn explicit2<'a>(x: &'a i32) -> impl Copy + 'static { x } + | -- lifetime `'a` defined here ^ returning this value requires that `'a` must outlive `'static` + | + = help: consider replacing `'a` with `'static` + = help: consider replacing `'a` with `'static` + +error[E0621]: explicit lifetime required in the type of `x` + --> $DIR/must_outlive_least_region_or_bound.rs:15:41 + | +LL | fn foo<'a>(x: &i32) -> impl Copy + 'a { x } + | ---- ^ lifetime `'a` required + | | + | help: add explicit lifetime `'a` to the type of `x`: `&'a i32` + +error: lifetime may not live long enough + --> $DIR/must_outlive_least_region_or_bound.rs:33:69 | LL | fn with_bound<'a>(x: &'a i32) -> impl LifetimeTrait<'a> + 'static { x } | -- lifetime `'a` defined here ^ returning this value requires that `'a` must outlive `'static` @@ -35,7 +62,7 @@ LL | fn with_bound<'a>(x: &'a i32) -> impl LifetimeTrait<'a> + 'static { x } = help: consider replacing `'a` with `'static` error: lifetime may not live long enough - --> $DIR/must_outlive_least_region_or_bound.rs:17:61 + --> $DIR/must_outlive_least_region_or_bound.rs:38:61 | LL | fn move_lifetime_into_fn<'a, 'b>(x: &'a u32, y: &'b u32) -> impl Fn(&'a u32) { | -- -- lifetime `'b` defined here ^^^^^^^^^^^^^^^^ opaque type requires that `'b` must outlive `'a` @@ -45,13 +72,14 @@ LL | fn move_lifetime_into_fn<'a, 'b>(x: &'a u32, y: &'b u32) -> impl Fn(&'a u32 = help: consider adding the following bound: `'b: 'a` error[E0310]: the parameter type `T` may not live long enough - --> $DIR/must_outlive_least_region_or_bound.rs:22:51 + --> $DIR/must_outlive_least_region_or_bound.rs:43:51 | LL | fn ty_param_wont_outlive_static(x: T) -> impl Debug + 'static { | ^^^^^^^^^^^^^^^^^^^^ | = help: consider adding an explicit lifetime bound `T: 'static`... -error: aborting due to 5 previous errors +error: aborting due to 8 previous errors -For more information about this error, try `rustc --explain E0310`. +Some errors have detailed explanations: E0310, E0621. +For more information about an error, try `rustc --explain E0310`. diff --git a/src/test/ui/impl-trait/must_outlive_least_region_or_bound.rs b/src/test/ui/impl-trait/must_outlive_least_region_or_bound.rs index 00f3490991b5..beafe9258209 100644 --- a/src/test/ui/impl-trait/must_outlive_least_region_or_bound.rs +++ b/src/test/ui/impl-trait/must_outlive_least_region_or_bound.rs @@ -6,6 +6,27 @@ fn elided(x: &i32) -> impl Copy { x } fn explicit<'a>(x: &'a i32) -> impl Copy { x } //~^ ERROR cannot infer an appropriate lifetime +fn elided2(x: &i32) -> impl Copy + 'static { x } +//~^ ERROR cannot infer an appropriate lifetime + +fn explicit2<'a>(x: &'a i32) -> impl Copy + 'static { x } +//~^ ERROR cannot infer an appropriate lifetime + +fn foo<'a>(x: &i32) -> impl Copy + 'a { x } +//~^ ERROR explicit lifetime required in the type of `x` + +fn elided3(x: &i32) -> Box { Box::new(x) } +//~^ ERROR cannot infer an appropriate lifetime + +fn explicit3<'a>(x: &'a i32) -> Box { Box::new(x) } +//~^ ERROR cannot infer an appropriate lifetime + +fn elided4(x: &i32) -> Box { Box::new(x) } +//~^ ERROR explicit lifetime required in the type of `x` + +fn explicit4<'a>(x: &'a i32) -> Box { Box::new(x) } +//~^ ERROR cannot infer an appropriate lifetime + trait LifetimeTrait<'a> {} impl<'a> LifetimeTrait<'a> for &'a i32 {} diff --git a/src/test/ui/impl-trait/must_outlive_least_region_or_bound.stderr b/src/test/ui/impl-trait/must_outlive_least_region_or_bound.stderr index d7dae6a08a7b..525e271bea9c 100644 --- a/src/test/ui/impl-trait/must_outlive_least_region_or_bound.stderr +++ b/src/test/ui/impl-trait/must_outlive_least_region_or_bound.stderr @@ -27,7 +27,43 @@ LL | fn explicit<'a>(x: &'a i32) -> impl Copy + 'a { x } | ^^^^ error: cannot infer an appropriate lifetime - --> $DIR/must_outlive_least_region_or_bound.rs:12:69 + --> $DIR/must_outlive_least_region_or_bound.rs:9:46 + | +LL | fn elided2(x: &i32) -> impl Copy + 'static { x } + | ---- ------------------- ^ ...and is captured here + | | | + | | ...is required to be `'static` by this... + | data with this lifetime... + | +help: to permit non-static references in an `impl Trait` value, you can add an explicit bound for the anonymous lifetime #1 defined on the function body at 9:1 + | +LL | fn elided2(x: &i32) -> impl Copy + '_ { x } + | ^^ + +error: cannot infer an appropriate lifetime + --> $DIR/must_outlive_least_region_or_bound.rs:12:55 + | +LL | fn explicit2<'a>(x: &'a i32) -> impl Copy + 'static { x } + | ------- ------------------- ^ ...and is captured here + | | | + | | ...is required to be `'static` by this... + | data with this lifetime... + | +help: to permit non-static references in an `impl Trait` value, you can add an explicit bound for the lifetime `'a` as defined on the function body at 12:14 + | +LL | fn explicit2<'a>(x: &'a i32) -> impl Copy + 'a { x } + | ^^ + +error[E0621]: explicit lifetime required in the type of `x` + --> $DIR/must_outlive_least_region_or_bound.rs:15:24 + | +LL | fn foo<'a>(x: &i32) -> impl Copy + 'a { x } + | ---- ^^^^^^^^^^^^^^ lifetime `'a` required + | | + | help: add explicit lifetime `'a` to the type of `x`: `&'a i32` + +error: cannot infer an appropriate lifetime + --> $DIR/must_outlive_least_region_or_bound.rs:33:69 | LL | fn with_bound<'a>(x: &'a i32) -> impl LifetimeTrait<'a> + 'static { x } | ------- -------------------------------- ^ ...and is captured here @@ -35,13 +71,13 @@ LL | fn with_bound<'a>(x: &'a i32) -> impl LifetimeTrait<'a> + 'static { x } | | ...is required to be `'static` by this... | data with this lifetime... | -help: to permit non-static references in an `impl Trait` value, you can add an explicit bound for the lifetime `'a` as defined on the function body at 12:15 +help: to permit non-static references in an `impl Trait` value, you can add an explicit bound for the lifetime `'a` as defined on the function body at 33:15 | -LL | fn with_bound<'a>(x: &'a i32) -> impl LifetimeTrait<'a> + 'static + 'a { x } - | ^^^^ +LL | fn with_bound<'a>(x: &'a i32) -> impl LifetimeTrait<'a> + 'a { x } + | ^^ error[E0623]: lifetime mismatch - --> $DIR/must_outlive_least_region_or_bound.rs:17:61 + --> $DIR/must_outlive_least_region_or_bound.rs:38:61 | LL | fn move_lifetime_into_fn<'a, 'b>(x: &'a u32, y: &'b u32) -> impl Fn(&'a u32) { | ------- ^^^^^^^^^^^^^^^^ @@ -50,14 +86,79 @@ LL | fn move_lifetime_into_fn<'a, 'b>(x: &'a u32, y: &'b u32) -> impl Fn(&'a u32 | this parameter and the return type are declared with different lifetimes... error[E0310]: the parameter type `T` may not live long enough - --> $DIR/must_outlive_least_region_or_bound.rs:22:51 + --> $DIR/must_outlive_least_region_or_bound.rs:43:51 | LL | fn ty_param_wont_outlive_static(x: T) -> impl Debug + 'static { | -- ^^^^^^^^^^^^^^^^^^^^ ...so that the type `T` will meet its required lifetime bounds | | | help: consider adding an explicit lifetime bound...: `T: 'static +` -error: aborting due to 5 previous errors +error: cannot infer an appropriate lifetime + --> $DIR/must_outlive_least_region_or_bound.rs:18:50 + | +LL | fn elided3(x: &i32) -> Box { Box::new(x) } + | ---- ---------^- + | | | | + | | | ...and is captured here + | | ...is required to be `'static` by this... + | data with this lifetime... + | +help: to permit non-static references in a `dyn Trait` value, you can add an explicit bound for the anonymous lifetime #1 defined on the function body at 18:1 + | +LL | fn elided3(x: &i32) -> Box { Box::new(x) } + | ^^^^ -Some errors have detailed explanations: E0310, E0623. +error: cannot infer an appropriate lifetime + --> $DIR/must_outlive_least_region_or_bound.rs:21:59 + | +LL | fn explicit3<'a>(x: &'a i32) -> Box { Box::new(x) } + | ------- ---------^- + | | | | + | | | ...and is captured here + | | ...is required to be `'static` by this... + | data with this lifetime... + | +help: to permit non-static references in a `dyn Trait` value, you can add an explicit bound for the lifetime `'a` as defined on the function body at 21:14 + | +LL | fn explicit3<'a>(x: &'a i32) -> Box { Box::new(x) } + | ^^^^ + +error[E0621]: explicit lifetime required in the type of `x` + --> $DIR/must_outlive_least_region_or_bound.rs:24:51 + | +LL | fn elided4(x: &i32) -> Box { Box::new(x) } + | ---- ^^^^^^^^^^^ lifetime `'static` required + | | + | help: add explicit lifetime `'static` to the type of `x`: `&'static i32` + +error[E0495]: cannot infer an appropriate lifetime due to conflicting requirements + --> $DIR/must_outlive_least_region_or_bound.rs:27:69 + | +LL | fn explicit4<'a>(x: &'a i32) -> Box { Box::new(x) } + | ^ + | +note: first, the lifetime cannot outlive the lifetime `'a` as defined on the function body at 27:14... + --> $DIR/must_outlive_least_region_or_bound.rs:27:14 + | +LL | fn explicit4<'a>(x: &'a i32) -> Box { Box::new(x) } + | ^^ +note: ...so that the expression is assignable + --> $DIR/must_outlive_least_region_or_bound.rs:27:69 + | +LL | fn explicit4<'a>(x: &'a i32) -> Box { Box::new(x) } + | ^ + = note: expected `&i32` + found `&'a i32` + = note: but, the lifetime must be valid for the static lifetime... +note: ...so that the expression is assignable + --> $DIR/must_outlive_least_region_or_bound.rs:27:60 + | +LL | fn explicit4<'a>(x: &'a i32) -> Box { Box::new(x) } + | ^^^^^^^^^^^ + = note: expected `std::boxed::Box<(dyn std::fmt::Debug + 'static)>` + found `std::boxed::Box` + +error: aborting due to 12 previous errors + +Some errors have detailed explanations: E0310, E0495, E0621, E0623. For more information about an error, try `rustc --explain E0310`. From 4e90f177cc530371a314f51f522a4c2e70885e03 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Fri, 29 May 2020 18:05:20 -0700 Subject: [PATCH 050/123] When `'static` is explicit, suggest constraining argument with it --- .../infer/error_reporting/mod.rs | 3 +- .../nice_region_error/static_impl_trait.rs | 115 +++++++++++------- src/librustc_middle/ty/diagnostics.rs | 5 +- .../must_outlive_least_region_or_bound.rs | 2 +- .../must_outlive_least_region_or_bound.stderr | 75 +++++++----- src/test/ui/issues/issue-16922.stderr | 2 +- ...ect-lifetime-default-from-box-error.stderr | 2 +- ...ion-object-lifetime-in-coercion.nll.stderr | 27 ++-- .../region-object-lifetime-in-coercion.rs | 5 +- .../region-object-lifetime-in-coercion.stderr | 67 ++++++---- .../regions-close-object-into-object-2.stderr | 34 +++--- .../regions-close-object-into-object-4.stderr | 34 +++--- .../regions-proc-bound-capture.nll.stderr | 11 ++ .../ui/regions/regions-proc-bound-capture.rs | 4 +- .../regions/regions-proc-bound-capture.stderr | 25 ++-- .../dyn-trait-underscore.stderr | 2 +- 16 files changed, 246 insertions(+), 167 deletions(-) create mode 100644 src/test/ui/regions/regions-proc-bound-capture.nll.stderr diff --git a/src/librustc_infer/infer/error_reporting/mod.rs b/src/librustc_infer/infer/error_reporting/mod.rs index 12f7a9c0ca50..9cfa11dd7c81 100644 --- a/src/librustc_infer/infer/error_reporting/mod.rs +++ b/src/librustc_infer/infer/error_reporting/mod.rs @@ -2035,8 +2035,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { self.tcx.sess, var_origin.span(), E0495, - "cannot infer an appropriate lifetime{} \ - due to conflicting requirements", + "cannot infer an appropriate lifetime{} due to conflicting requirements", var_description ) } diff --git a/src/librustc_infer/infer/error_reporting/nice_region_error/static_impl_trait.rs b/src/librustc_infer/infer/error_reporting/nice_region_error/static_impl_trait.rs index 88d6c23d5144..e24535bba5fd 100644 --- a/src/librustc_infer/infer/error_reporting/nice_region_error/static_impl_trait.rs +++ b/src/librustc_infer/infer/error_reporting/nice_region_error/static_impl_trait.rs @@ -10,6 +10,7 @@ use rustc_middle::ty::RegionKind; impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { /// Print the error message for lifetime errors when the return type is a static impl Trait. pub(super) fn try_report_static_impl_trait(&self) -> Option { + debug!("try_report_static_impl_trait(error={:?})", self.error); if let Some(ref error) = self.error { if let RegionResolutionError::SubSupConflict( _, @@ -18,19 +19,24 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { sub_r, sup_origin, sup_r, - ) = error.clone() + ) = error { + debug!( + "try_report_static_impl_trait(var={:?}, sub={:?} {:?} sup={:?} {:?})", + var_origin, sub_origin, sub_r, sup_origin, sup_r + ); let anon_reg_sup = self.tcx().is_suitable_region(sup_r)?; + debug!("try_report_static_impl_trait: anon_reg_sup={:?}", anon_reg_sup); let fn_return = self.tcx().return_type_impl_or_dyn_trait(anon_reg_sup.def_id)?; - let is_dyn = matches!(fn_return.kind, TyKind::TraitObject(..)); - let fn_return_span = fn_return.span; - if sub_r == &RegionKind::ReStatic { + debug!("try_report_static_impl_trait: fn_return={:?}", fn_return); + if **sub_r == RegionKind::ReStatic { let sp = var_origin.span(); let return_sp = sub_origin.span(); + let param_info = self.find_param_with_region(sup_r, sub_r)?; let mut err = self.tcx().sess.struct_span_err(sp, "cannot infer an appropriate lifetime"); - let param_info = self.find_param_with_region(sup_r, sub_r)?; err.span_label(param_info.param_ty_span, "data with this lifetime..."); + debug!("try_report_static_impl_trait: param_info={:?}", param_info); // We try to make the output have fewer overlapping spans if possible. if (sp == sup_origin.span() || !return_sp.overlaps(sup_origin.span())) @@ -60,14 +66,7 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { if sup_r.has_name() { sup_r.to_string() } else { "'_".to_owned() }; // only apply this suggestion onto functions with // explicit non-desugar'able return. - if fn_return_span.desugaring_kind().is_none() { - let msg = format!( - "to permit non-static references in {} `{} Trait` value, you can add \ - an explicit bound for {}", - if is_dyn { "a" } else { "an" }, - if is_dyn { "dyn" } else { "impl" }, - lifetime, - ); + if fn_return.span.desugaring_kind().is_none() { // FIXME: account for the need of parens in `&(dyn Trait + '_)` match fn_return.kind { TyKind::Def(item_id, _) => { @@ -78,7 +77,8 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { err.emit(); return Some(ErrorReported); }; - let (span, sugg) = opaque + + if let Some(span) = opaque .bounds .iter() .filter_map(|arg| match arg { @@ -86,38 +86,71 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { name: LifetimeName::Static, span, .. - }) => Some((*span, lifetime_name.clone())), + }) => Some(*span), _ => None, }) .next() - .unwrap_or_else(|| { - ( - fn_return_span.shrink_to_hi(), - format!(" + {}", lifetime_name), - ) - }); - - err.span_suggestion_verbose( - span, - &msg, - sugg, - Applicability::MaybeIncorrect, - ); + { + err.span_suggestion_verbose( + span, + "consider changing the `impl Trait`'s explicit \ + `'static` bound", + lifetime_name, + Applicability::MaybeIncorrect, + ); + err.span_suggestion_verbose( + param_info.param_ty_span, + "alternatively, set an explicit `'static` lifetime to \ + this parameter", + param_info.param_ty.to_string(), + Applicability::MaybeIncorrect, + ); + } else { + err.span_suggestion_verbose( + fn_return.span.shrink_to_hi(), + &format!( + "to permit non-static references in an `impl Trait` \ + value, you can add an explicit bound for {}", + lifetime, + ), + format!(" + {}", lifetime_name), + Applicability::MaybeIncorrect, + ); + }; } TyKind::TraitObject(_, lt) => { - let (span, sugg) = match lt.name { - LifetimeName::ImplicitObjectLifetimeDefault => ( - fn_return_span.shrink_to_hi(), - format!(" + {}", lifetime_name), - ), - _ => (lt.span, lifetime_name), - }; - err.span_suggestion_verbose( - span, - &msg, - sugg, - Applicability::MaybeIncorrect, - ); + match lt.name { + LifetimeName::ImplicitObjectLifetimeDefault => { + err.span_suggestion_verbose( + fn_return.span.shrink_to_hi(), + &format!( + "to permit non-static references in a trait object \ + value, you can add an explicit bound for {}", + lifetime, + ), + format!(" + {}", lifetime_name), + Applicability::MaybeIncorrect, + ); + } + _ => { + err.span_suggestion_verbose( + lt.span, + "consider changing the trait object's explicit \ + `'static` bound", + lifetime_name, + Applicability::MaybeIncorrect, + ); + err.span_suggestion_verbose( + param_info.param_ty_span, + &format!( + "alternatively, set an explicit `'static` lifetime \ + in this parameter", + ), + param_info.param_ty.to_string(), + Applicability::MaybeIncorrect, + ); + } + } } _ => {} } diff --git a/src/librustc_middle/ty/diagnostics.rs b/src/librustc_middle/ty/diagnostics.rs index 3ca506fe0d59..a2812e117ed3 100644 --- a/src/librustc_middle/ty/diagnostics.rs +++ b/src/librustc_middle/ty/diagnostics.rs @@ -247,7 +247,10 @@ impl<'v> hir::intravisit::Visitor<'v> for TraitObjectVisitor<'v> { fn visit_ty(&mut self, ty: &'v hir::Ty<'v>) { if let hir::TyKind::TraitObject( _, - hir::Lifetime { name: hir::LifetimeName::ImplicitObjectLifetimeDefault, .. }, + hir::Lifetime { + name: hir::LifetimeName::ImplicitObjectLifetimeDefault | hir::LifetimeName::Static, + .. + }, ) = ty.kind { self.0.push(ty); diff --git a/src/test/ui/impl-trait/must_outlive_least_region_or_bound.rs b/src/test/ui/impl-trait/must_outlive_least_region_or_bound.rs index beafe9258209..837244b02272 100644 --- a/src/test/ui/impl-trait/must_outlive_least_region_or_bound.rs +++ b/src/test/ui/impl-trait/must_outlive_least_region_or_bound.rs @@ -22,7 +22,7 @@ fn explicit3<'a>(x: &'a i32) -> Box { Box::new(x) } //~^ ERROR cannot infer an appropriate lifetime fn elided4(x: &i32) -> Box { Box::new(x) } -//~^ ERROR explicit lifetime required in the type of `x` +//~^ ERROR cannot infer an appropriate lifetime fn explicit4<'a>(x: &'a i32) -> Box { Box::new(x) } //~^ ERROR cannot infer an appropriate lifetime diff --git a/src/test/ui/impl-trait/must_outlive_least_region_or_bound.stderr b/src/test/ui/impl-trait/must_outlive_least_region_or_bound.stderr index 525e271bea9c..96d4a121c16a 100644 --- a/src/test/ui/impl-trait/must_outlive_least_region_or_bound.stderr +++ b/src/test/ui/impl-trait/must_outlive_least_region_or_bound.stderr @@ -35,10 +35,14 @@ LL | fn elided2(x: &i32) -> impl Copy + 'static { x } | | ...is required to be `'static` by this... | data with this lifetime... | -help: to permit non-static references in an `impl Trait` value, you can add an explicit bound for the anonymous lifetime #1 defined on the function body at 9:1 +help: consider changing the `impl Trait`'s explicit `'static` bound | LL | fn elided2(x: &i32) -> impl Copy + '_ { x } | ^^ +help: alternatively, set an explicit `'static` lifetime to this parameter + | +LL | fn elided2(x: &'static i32) -> impl Copy + 'static { x } + | ^^^^^^^^^^^^ error: cannot infer an appropriate lifetime --> $DIR/must_outlive_least_region_or_bound.rs:12:55 @@ -49,10 +53,14 @@ LL | fn explicit2<'a>(x: &'a i32) -> impl Copy + 'static { x } | | ...is required to be `'static` by this... | data with this lifetime... | -help: to permit non-static references in an `impl Trait` value, you can add an explicit bound for the lifetime `'a` as defined on the function body at 12:14 +help: consider changing the `impl Trait`'s explicit `'static` bound | LL | fn explicit2<'a>(x: &'a i32) -> impl Copy + 'a { x } | ^^ +help: alternatively, set an explicit `'static` lifetime to this parameter + | +LL | fn explicit2<'a>(x: &'static i32) -> impl Copy + 'static { x } + | ^^^^^^^^^^^^ error[E0621]: explicit lifetime required in the type of `x` --> $DIR/must_outlive_least_region_or_bound.rs:15:24 @@ -71,10 +79,14 @@ LL | fn with_bound<'a>(x: &'a i32) -> impl LifetimeTrait<'a> + 'static { x } | | ...is required to be `'static` by this... | data with this lifetime... | -help: to permit non-static references in an `impl Trait` value, you can add an explicit bound for the lifetime `'a` as defined on the function body at 33:15 +help: consider changing the `impl Trait`'s explicit `'static` bound | LL | fn with_bound<'a>(x: &'a i32) -> impl LifetimeTrait<'a> + 'a { x } | ^^ +help: alternatively, set an explicit `'static` lifetime to this parameter + | +LL | fn with_bound<'a>(x: &'static i32) -> impl LifetimeTrait<'a> + 'static { x } + | ^^^^^^^^^^^^ error[E0623]: lifetime mismatch --> $DIR/must_outlive_least_region_or_bound.rs:38:61 @@ -103,7 +115,7 @@ LL | fn elided3(x: &i32) -> Box { Box::new(x) } | | ...is required to be `'static` by this... | data with this lifetime... | -help: to permit non-static references in a `dyn Trait` value, you can add an explicit bound for the anonymous lifetime #1 defined on the function body at 18:1 +help: to permit non-static references in a trait object value, you can add an explicit bound for the anonymous lifetime #1 defined on the function body at 18:1 | LL | fn elided3(x: &i32) -> Box { Box::new(x) } | ^^^^ @@ -118,47 +130,48 @@ LL | fn explicit3<'a>(x: &'a i32) -> Box { Box::new(x) } | | ...is required to be `'static` by this... | data with this lifetime... | -help: to permit non-static references in a `dyn Trait` value, you can add an explicit bound for the lifetime `'a` as defined on the function body at 21:14 +help: to permit non-static references in a trait object value, you can add an explicit bound for the lifetime `'a` as defined on the function body at 21:14 | LL | fn explicit3<'a>(x: &'a i32) -> Box { Box::new(x) } | ^^^^ -error[E0621]: explicit lifetime required in the type of `x` - --> $DIR/must_outlive_least_region_or_bound.rs:24:51 +error: cannot infer an appropriate lifetime + --> $DIR/must_outlive_least_region_or_bound.rs:24:60 | LL | fn elided4(x: &i32) -> Box { Box::new(x) } - | ---- ^^^^^^^^^^^ lifetime `'static` required - | | - | help: add explicit lifetime `'static` to the type of `x`: `&'static i32` + | ---- ---------^- + | | | | + | | | ...and is captured here + | data with this lifetime... ...is required to be `'static` by this... + | +help: consider changing the trait object's explicit `'static` bound + | +LL | fn elided4(x: &i32) -> Box { Box::new(x) } + | ^^ +help: alternatively, set an explicit `'static` lifetime in this parameter + | +LL | fn elided4(x: &'static i32) -> Box { Box::new(x) } + | ^^^^^^^^^^^^ -error[E0495]: cannot infer an appropriate lifetime due to conflicting requirements +error: cannot infer an appropriate lifetime --> $DIR/must_outlive_least_region_or_bound.rs:27:69 | LL | fn explicit4<'a>(x: &'a i32) -> Box { Box::new(x) } - | ^ + | ------- ---------^- + | | | | + | | | ...and is captured here + | data with this lifetime... ...is required to be `'static` by this... | -note: first, the lifetime cannot outlive the lifetime `'a` as defined on the function body at 27:14... - --> $DIR/must_outlive_least_region_or_bound.rs:27:14 +help: consider changing the trait object's explicit `'static` bound | -LL | fn explicit4<'a>(x: &'a i32) -> Box { Box::new(x) } - | ^^ -note: ...so that the expression is assignable - --> $DIR/must_outlive_least_region_or_bound.rs:27:69 +LL | fn explicit4<'a>(x: &'a i32) -> Box { Box::new(x) } + | ^^ +help: alternatively, set an explicit `'static` lifetime in this parameter | -LL | fn explicit4<'a>(x: &'a i32) -> Box { Box::new(x) } - | ^ - = note: expected `&i32` - found `&'a i32` - = note: but, the lifetime must be valid for the static lifetime... -note: ...so that the expression is assignable - --> $DIR/must_outlive_least_region_or_bound.rs:27:60 - | -LL | fn explicit4<'a>(x: &'a i32) -> Box { Box::new(x) } - | ^^^^^^^^^^^ - = note: expected `std::boxed::Box<(dyn std::fmt::Debug + 'static)>` - found `std::boxed::Box` +LL | fn explicit4<'a>(x: &'static i32) -> Box { Box::new(x) } + | ^^^^^^^^^^^^ error: aborting due to 12 previous errors -Some errors have detailed explanations: E0310, E0495, E0621, E0623. +Some errors have detailed explanations: E0310, E0621, E0623. For more information about an error, try `rustc --explain E0310`. diff --git a/src/test/ui/issues/issue-16922.stderr b/src/test/ui/issues/issue-16922.stderr index 02d33aae023f..038df47e1bd9 100644 --- a/src/test/ui/issues/issue-16922.stderr +++ b/src/test/ui/issues/issue-16922.stderr @@ -9,7 +9,7 @@ LL | Box::new(value) as Box | | ...and is captured here | ...is required to be `'static` by this... | -help: to permit non-static references in a `dyn Trait` value, you can add an explicit bound for the anonymous lifetime #1 defined on the function body at 3:1 +help: to permit non-static references in a trait object value, you can add an explicit bound for the anonymous lifetime #1 defined on the function body at 3:1 | LL | fn foo(value: &T) -> Box { | ^^^^ diff --git a/src/test/ui/object-lifetime/object-lifetime-default-from-box-error.stderr b/src/test/ui/object-lifetime/object-lifetime-default-from-box-error.stderr index 70a9bf22b8db..555622c9d13c 100644 --- a/src/test/ui/object-lifetime/object-lifetime-default-from-box-error.stderr +++ b/src/test/ui/object-lifetime/object-lifetime-default-from-box-error.stderr @@ -7,7 +7,7 @@ LL | fn load(ss: &mut SomeStruct) -> Box { LL | ss.r | ^^^^ ...is captured and required to be `'static` here | -help: to permit non-static references in a `dyn Trait` value, you can add an explicit bound for the anonymous lifetime #2 defined on the function body at 14:1 +help: to permit non-static references in a trait object value, you can add an explicit bound for the anonymous lifetime #2 defined on the function body at 14:1 | LL | fn load(ss: &mut SomeStruct) -> Box { | ^^^^ diff --git a/src/test/ui/regions/region-object-lifetime-in-coercion.nll.stderr b/src/test/ui/regions/region-object-lifetime-in-coercion.nll.stderr index bf02ba8eb919..7e8f78067e08 100644 --- a/src/test/ui/regions/region-object-lifetime-in-coercion.nll.stderr +++ b/src/test/ui/regions/region-object-lifetime-in-coercion.nll.stderr @@ -1,21 +1,21 @@ -error[E0621]: explicit lifetime required in the type of `v` +error: lifetime may not live long enough --> $DIR/region-object-lifetime-in-coercion.rs:8:12 | LL | fn a(v: &[u8]) -> Box { - | ----- help: add explicit lifetime `'static` to the type of `v`: `&'static [u8]` + | - let's call the lifetime of this reference `'1` LL | let x: Box = Box::new(v); - | ^^^^^^^^^^^^^^^^^^^^^^ lifetime `'static` required - -error[E0621]: explicit lifetime required in the type of `v` - --> $DIR/region-object-lifetime-in-coercion.rs:14:5 - | -LL | fn b(v: &[u8]) -> Box { - | ----- help: add explicit lifetime `'static` to the type of `v`: `&'static [u8]` -LL | Box::new(v) - | ^^^^^^^^^^^ lifetime `'static` required + | ^^^^^^^^^^^^^^^^^^^^^^ type annotation requires that `'1` must outlive `'static` error: lifetime may not live long enough - --> $DIR/region-object-lifetime-in-coercion.rs:20:5 + --> $DIR/region-object-lifetime-in-coercion.rs:13:5 + | +LL | fn b(v: &[u8]) -> Box { + | - let's call the lifetime of this reference `'1` +LL | Box::new(v) + | ^^^^^^^^^^^ returning this value requires that `'1` must outlive `'static` + +error: lifetime may not live long enough + --> $DIR/region-object-lifetime-in-coercion.rs:19:5 | LL | fn c(v: &[u8]) -> Box { | - let's call the lifetime of this reference `'1` @@ -24,7 +24,7 @@ LL | Box::new(v) | ^^^^^^^^^^^ returning this value requires that `'1` must outlive `'static` error: lifetime may not live long enough - --> $DIR/region-object-lifetime-in-coercion.rs:24:5 + --> $DIR/region-object-lifetime-in-coercion.rs:23:5 | LL | fn d<'a,'b>(v: &'a [u8]) -> Box { | -- -- lifetime `'b` defined here @@ -37,4 +37,3 @@ LL | Box::new(v) error: aborting due to 4 previous errors -For more information about this error, try `rustc --explain E0621`. diff --git a/src/test/ui/regions/region-object-lifetime-in-coercion.rs b/src/test/ui/regions/region-object-lifetime-in-coercion.rs index d56eaf77b664..5d199149c39b 100644 --- a/src/test/ui/regions/region-object-lifetime-in-coercion.rs +++ b/src/test/ui/regions/region-object-lifetime-in-coercion.rs @@ -5,13 +5,12 @@ trait Foo {} impl<'a> Foo for &'a [u8] {} fn a(v: &[u8]) -> Box { - let x: Box = Box::new(v); - //~^ ERROR explicit lifetime required in the type of `v` [E0621] + let x: Box = Box::new(v); //~ ERROR cannot infer an appropriate lifetime x } fn b(v: &[u8]) -> Box { - Box::new(v) //~ ERROR explicit lifetime required in the type of `v` [E0621] + Box::new(v) //~ ERROR cannot infer an appropriate lifetime } fn c(v: &[u8]) -> Box { diff --git a/src/test/ui/regions/region-object-lifetime-in-coercion.stderr b/src/test/ui/regions/region-object-lifetime-in-coercion.stderr index 1462af44cb15..673300cebc26 100644 --- a/src/test/ui/regions/region-object-lifetime-in-coercion.stderr +++ b/src/test/ui/regions/region-object-lifetime-in-coercion.stderr @@ -1,21 +1,45 @@ -error[E0621]: explicit lifetime required in the type of `v` - --> $DIR/region-object-lifetime-in-coercion.rs:8:37 +error: cannot infer an appropriate lifetime + --> $DIR/region-object-lifetime-in-coercion.rs:8:46 | LL | fn a(v: &[u8]) -> Box { - | ----- help: add explicit lifetime `'static` to the type of `v`: `&'static [u8]` + | ----- data with this lifetime... LL | let x: Box = Box::new(v); - | ^^^^^^^^^^^ lifetime `'static` required - -error[E0621]: explicit lifetime required in the type of `v` - --> $DIR/region-object-lifetime-in-coercion.rs:14:5 + | ---------^- + | | | + | | ...and is captured here + | ...is required to be `'static` by this... | -LL | fn b(v: &[u8]) -> Box { - | ----- help: add explicit lifetime `'static` to the type of `v`: `&'static [u8]` -LL | Box::new(v) - | ^^^^^^^^^^^ lifetime `'static` required +help: consider changing the trait object's explicit `'static` bound + | +LL | fn a(v: &[u8]) -> Box { + | ^^ +help: alternatively, set an explicit `'static` lifetime in this parameter + | +LL | fn a(v: &'static [u8]) -> Box { + | ^^^^^^^^^^^^^ error: cannot infer an appropriate lifetime - --> $DIR/region-object-lifetime-in-coercion.rs:20:14 + --> $DIR/region-object-lifetime-in-coercion.rs:13:14 + | +LL | fn b(v: &[u8]) -> Box { + | ----- data with this lifetime... +LL | Box::new(v) + | ---------^- + | | | + | | ...and is captured here + | ...is required to be `'static` by this... + | +help: consider changing the trait object's explicit `'static` bound + | +LL | fn b(v: &[u8]) -> Box { + | ^^ +help: alternatively, set an explicit `'static` lifetime in this parameter + | +LL | fn b(v: &'static [u8]) -> Box { + | ^^^^^^^^^^^^^ + +error: cannot infer an appropriate lifetime + --> $DIR/region-object-lifetime-in-coercion.rs:19:14 | LL | fn c(v: &[u8]) -> Box { | ----- data with this lifetime... @@ -26,36 +50,36 @@ LL | Box::new(v) | | ...and is captured here | ...is required to be `'static` by this... | -help: to permit non-static references in a `dyn Trait` value, you can add an explicit bound for the anonymous lifetime #1 defined on the function body at 17:1 +help: to permit non-static references in a trait object value, you can add an explicit bound for the anonymous lifetime #1 defined on the function body at 16:1 | LL | fn c(v: &[u8]) -> Box { | ^^^^ error[E0495]: cannot infer an appropriate lifetime due to conflicting requirements - --> $DIR/region-object-lifetime-in-coercion.rs:24:14 + --> $DIR/region-object-lifetime-in-coercion.rs:23:14 | LL | Box::new(v) | ^ | -note: first, the lifetime cannot outlive the lifetime `'a` as defined on the function body at 23:6... - --> $DIR/region-object-lifetime-in-coercion.rs:23:6 +note: first, the lifetime cannot outlive the lifetime `'a` as defined on the function body at 22:6... + --> $DIR/region-object-lifetime-in-coercion.rs:22:6 | LL | fn d<'a,'b>(v: &'a [u8]) -> Box { | ^^ note: ...so that the expression is assignable - --> $DIR/region-object-lifetime-in-coercion.rs:24:14 + --> $DIR/region-object-lifetime-in-coercion.rs:23:14 | LL | Box::new(v) | ^ = note: expected `&[u8]` found `&'a [u8]` -note: but, the lifetime must be valid for the lifetime `'b` as defined on the function body at 23:9... - --> $DIR/region-object-lifetime-in-coercion.rs:23:9 +note: but, the lifetime must be valid for the lifetime `'b` as defined on the function body at 22:9... + --> $DIR/region-object-lifetime-in-coercion.rs:22:9 | LL | fn d<'a,'b>(v: &'a [u8]) -> Box { | ^^ note: ...so that the expression is assignable - --> $DIR/region-object-lifetime-in-coercion.rs:24:5 + --> $DIR/region-object-lifetime-in-coercion.rs:23:5 | LL | Box::new(v) | ^^^^^^^^^^^ @@ -64,5 +88,4 @@ LL | Box::new(v) error: aborting due to 4 previous errors -Some errors have detailed explanations: E0495, E0621. -For more information about an error, try `rustc --explain E0495`. +For more information about this error, try `rustc --explain E0495`. diff --git a/src/test/ui/regions/regions-close-object-into-object-2.stderr b/src/test/ui/regions/regions-close-object-into-object-2.stderr index 147f7f354181..982ed07232a8 100644 --- a/src/test/ui/regions/regions-close-object-into-object-2.stderr +++ b/src/test/ui/regions/regions-close-object-into-object-2.stderr @@ -1,28 +1,22 @@ -error[E0495]: cannot infer an appropriate lifetime for borrow expression due to conflicting requirements +error: cannot infer an appropriate lifetime --> $DIR/regions-close-object-into-object-2.rs:10:11 | -LL | box B(&*v) as Box - | ^^^ - | -note: first, the lifetime cannot outlive the lifetime `'a` as defined on the function body at 9:6... - --> $DIR/regions-close-object-into-object-2.rs:9:6 - | LL | fn g<'a, T: 'static>(v: Box + 'a>) -> Box { - | ^^ -note: ...so that the type `(dyn A + 'a)` is not borrowed for too long - --> $DIR/regions-close-object-into-object-2.rs:10:11 - | + | ------------------ data with this lifetime... LL | box B(&*v) as Box - | ^^^ - = note: but, the lifetime must be valid for the static lifetime... -note: ...so that the expression is assignable - --> $DIR/regions-close-object-into-object-2.rs:10:5 + | ------^^^--------------- + | | | + | | ...and is captured here + | ...is required to be `'static` by this... | -LL | box B(&*v) as Box - | ^^^^^^^^^^^^^^^^^^^^^^^^ - = note: expected `std::boxed::Box<(dyn X + 'static)>` - found `std::boxed::Box` +help: consider changing the trait object's explicit `'static` bound + | +LL | fn g<'a, T: 'static>(v: Box + 'a>) -> Box { + | ^^ +help: alternatively, set an explicit `'static` lifetime in this parameter + | +LL | fn g<'a, T: 'static>(v: std::boxed::Box<(dyn A + 'static)>) -> Box { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: aborting due to previous error -For more information about this error, try `rustc --explain E0495`. diff --git a/src/test/ui/regions/regions-close-object-into-object-4.stderr b/src/test/ui/regions/regions-close-object-into-object-4.stderr index 6e7d6152cd09..1b82098ee13c 100644 --- a/src/test/ui/regions/regions-close-object-into-object-4.stderr +++ b/src/test/ui/regions/regions-close-object-into-object-4.stderr @@ -1,28 +1,22 @@ -error[E0495]: cannot infer an appropriate lifetime for borrow expression due to conflicting requirements +error: cannot infer an appropriate lifetime --> $DIR/regions-close-object-into-object-4.rs:10:11 | -LL | box B(&*v) as Box - | ^^^ - | -note: first, the lifetime cannot outlive the lifetime `'a` as defined on the function body at 9:6... - --> $DIR/regions-close-object-into-object-4.rs:9:6 - | LL | fn i<'a, T, U>(v: Box+'a>) -> Box { - | ^^ -note: ...so that the type `(dyn A + 'a)` is not borrowed for too long - --> $DIR/regions-close-object-into-object-4.rs:10:11 - | + | ---------------- data with this lifetime... LL | box B(&*v) as Box - | ^^^ - = note: but, the lifetime must be valid for the static lifetime... -note: ...so that the expression is assignable - --> $DIR/regions-close-object-into-object-4.rs:10:5 + | ------^^^--------------- + | | | + | | ...and is captured here + | ...is required to be `'static` by this... | -LL | box B(&*v) as Box - | ^^^^^^^^^^^^^^^^^^^^^^^^ - = note: expected `std::boxed::Box<(dyn X + 'static)>` - found `std::boxed::Box` +help: consider changing the trait object's explicit `'static` bound + | +LL | fn i<'a, T, U>(v: Box+'a>) -> Box { + | ^^ +help: alternatively, set an explicit `'static` lifetime in this parameter + | +LL | fn i<'a, T, U>(v: std::boxed::Box<(dyn A + 'static)>) -> Box { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: aborting due to previous error -For more information about this error, try `rustc --explain E0495`. diff --git a/src/test/ui/regions/regions-proc-bound-capture.nll.stderr b/src/test/ui/regions/regions-proc-bound-capture.nll.stderr new file mode 100644 index 000000000000..75890b858153 --- /dev/null +++ b/src/test/ui/regions/regions-proc-bound-capture.nll.stderr @@ -0,0 +1,11 @@ +error: lifetime may not live long enough + --> $DIR/regions-proc-bound-capture.rs:9:5 + | +LL | fn static_proc(x: &isize) -> Box (isize) + 'static> { + | - let's call the lifetime of this reference `'1` +LL | // This is illegal, because the region bound on `proc` is 'static. +LL | Box::new(move || { *x }) + | ^^^^^^^^^^^^^^^^^^^^^^^^ returning this value requires that `'1` must outlive `'static` + +error: aborting due to previous error + diff --git a/src/test/ui/regions/regions-proc-bound-capture.rs b/src/test/ui/regions/regions-proc-bound-capture.rs index 0c903b738499..8617c0e9da8f 100644 --- a/src/test/ui/regions/regions-proc-bound-capture.rs +++ b/src/test/ui/regions/regions-proc-bound-capture.rs @@ -4,9 +4,9 @@ fn borrowed_proc<'a>(x: &'a isize) -> Box(isize) + 'a> { Box::new(move|| { *x }) } -fn static_proc(x: &isize) -> Box(isize) + 'static> { +fn static_proc(x: &isize) -> Box (isize) + 'static> { // This is illegal, because the region bound on `proc` is 'static. - Box::new(move|| { *x }) //~ ERROR explicit lifetime required in the type of `x` [E0621] + Box::new(move || { *x }) //~ ERROR cannot infer an appropriate lifetime } fn main() { } diff --git a/src/test/ui/regions/regions-proc-bound-capture.stderr b/src/test/ui/regions/regions-proc-bound-capture.stderr index c53af34456ef..e7bbfaababe8 100644 --- a/src/test/ui/regions/regions-proc-bound-capture.stderr +++ b/src/test/ui/regions/regions-proc-bound-capture.stderr @@ -1,12 +1,23 @@ -error[E0621]: explicit lifetime required in the type of `x` - --> $DIR/regions-proc-bound-capture.rs:9:5 +error: cannot infer an appropriate lifetime + --> $DIR/regions-proc-bound-capture.rs:9:14 | -LL | fn static_proc(x: &isize) -> Box(isize) + 'static> { - | ------ help: add explicit lifetime `'static` to the type of `x`: `&'static isize` +LL | fn static_proc(x: &isize) -> Box (isize) + 'static> { + | ------ data with this lifetime... LL | // This is illegal, because the region bound on `proc` is 'static. -LL | Box::new(move|| { *x }) - | ^^^^^^^^^^^^^^^^^^^^^^^ lifetime `'static` required +LL | Box::new(move || { *x }) + | ---------^^^^^^^^^^^^^^- + | | | + | | ...and is captured here + | ...is required to be `'static` by this... + | +help: consider changing the trait object's explicit `'static` bound + | +LL | fn static_proc(x: &isize) -> Box (isize) + '_> { + | ^^ +help: alternatively, set an explicit `'static` lifetime in this parameter + | +LL | fn static_proc(x: &'static isize) -> Box (isize) + 'static> { + | ^^^^^^^^^^^^^^ error: aborting due to previous error -For more information about this error, try `rustc --explain E0621`. diff --git a/src/test/ui/underscore-lifetime/dyn-trait-underscore.stderr b/src/test/ui/underscore-lifetime/dyn-trait-underscore.stderr index 3577dd59289e..4dc4aac6ceac 100644 --- a/src/test/ui/underscore-lifetime/dyn-trait-underscore.stderr +++ b/src/test/ui/underscore-lifetime/dyn-trait-underscore.stderr @@ -7,7 +7,7 @@ LL | // ^^^^^^^^^^^^^^^^^^^^^ bound *here* defaults to LL | Box::new(items.iter()) | ---------------^^^^--- ...is captured and required to be `'static` here | -help: to permit non-static references in a `dyn Trait` value, you can add an explicit bound for the anonymous lifetime #1 defined on the function body at 6:1 +help: to permit non-static references in a trait object value, you can add an explicit bound for the anonymous lifetime #1 defined on the function body at 6:1 | LL | fn a(items: &[T]) -> Box + '_> { | ^^^^ From 921f35fe73e8749dee8531f7fbaf2cb4958fa799 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Fri, 29 May 2020 18:59:42 -0700 Subject: [PATCH 051/123] Reduce verbosity of suggestion message and mention lifetime in label --- .../nice_region_error/static_impl_trait.rs | 89 ++++++++++--------- .../ui/async-await/issues/issue-62097.stderr | 2 +- .../must_outlive_least_region_or_bound.stderr | 38 ++++---- .../static-return-lifetime-infered.stderr | 8 +- src/test/ui/issues/issue-16922.stderr | 4 +- ...ect-lifetime-default-from-box-error.stderr | 4 +- .../region-object-lifetime-in-coercion.stderr | 12 +-- .../regions-close-object-into-object-2.stderr | 4 +- .../regions-close-object-into-object-4.stderr | 4 +- .../regions/regions-proc-bound-capture.stderr | 4 +- ...types_pin_lifetime_impl_trait-async.stderr | 2 +- ..._self_types_pin_lifetime_impl_trait.stderr | 4 +- .../missing-lifetimes-in-signature.stderr | 4 +- .../dyn-trait-underscore.stderr | 4 +- 14 files changed, 96 insertions(+), 87 deletions(-) diff --git a/src/librustc_infer/infer/error_reporting/nice_region_error/static_impl_trait.rs b/src/librustc_infer/infer/error_reporting/nice_region_error/static_impl_trait.rs index e24535bba5fd..e9f165d309f8 100644 --- a/src/librustc_infer/infer/error_reporting/nice_region_error/static_impl_trait.rs +++ b/src/librustc_infer/infer/error_reporting/nice_region_error/static_impl_trait.rs @@ -1,6 +1,5 @@ //! Error Reporting for static impl Traits. -use crate::infer::error_reporting::msg_span_from_free_region; use crate::infer::error_reporting::nice_region_error::NiceRegionError; use crate::infer::lexical_region_resolve::RegionResolutionError; use rustc_errors::{Applicability, ErrorReported}; @@ -33,9 +32,17 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { let sp = var_origin.span(); let return_sp = sub_origin.span(); let param_info = self.find_param_with_region(sup_r, sub_r)?; + let (lifetime_name, lifetime) = if sup_r.has_name() { + (sup_r.to_string(), format!("lifetime `{}`", sup_r)) + } else { + ("'_".to_owned(), "the anonymous lifetime `'_`".to_string()) + }; let mut err = self.tcx().sess.struct_span_err(sp, "cannot infer an appropriate lifetime"); - err.span_label(param_info.param_ty_span, "data with this lifetime..."); + err.span_label( + param_info.param_ty_span, + &format!("this data with {}...", lifetime), + ); debug!("try_report_static_impl_trait: param_info={:?}", param_info); // We try to make the output have fewer overlapping spans if possible. @@ -60,10 +67,6 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { ); } - let (lifetime, _) = msg_span_from_free_region(self.tcx(), sup_r); - - let lifetime_name = - if sup_r.has_name() { sup_r.to_string() } else { "'_".to_owned() }; // only apply this suggestion onto functions with // explicit non-desugar'able return. if fn_return.span.desugaring_kind().is_none() { @@ -93,8 +96,11 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { { err.span_suggestion_verbose( span, - "consider changing the `impl Trait`'s explicit \ - `'static` bound", + &format!( + "consider changing the `impl Trait`'s explicit \ + `'static` bound to {}", + lifetime, + ), lifetime_name, Applicability::MaybeIncorrect, ); @@ -118,40 +124,41 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { ); }; } - TyKind::TraitObject(_, lt) => { - match lt.name { - LifetimeName::ImplicitObjectLifetimeDefault => { - err.span_suggestion_verbose( - fn_return.span.shrink_to_hi(), - &format!( - "to permit non-static references in a trait object \ - value, you can add an explicit bound for {}", - lifetime, - ), - format!(" + {}", lifetime_name), - Applicability::MaybeIncorrect, - ); - } - _ => { - err.span_suggestion_verbose( - lt.span, - "consider changing the trait object's explicit \ - `'static` bound", - lifetime_name, - Applicability::MaybeIncorrect, - ); - err.span_suggestion_verbose( - param_info.param_ty_span, - &format!( - "alternatively, set an explicit `'static` lifetime \ - in this parameter", - ), - param_info.param_ty.to_string(), - Applicability::MaybeIncorrect, - ); - } + TyKind::TraitObject(_, lt) => match lt.name { + LifetimeName::ImplicitObjectLifetimeDefault => { + err.span_suggestion_verbose( + fn_return.span.shrink_to_hi(), + &format!( + "to permit non-static references in a trait object \ + value, you can add an explicit bound for {}", + lifetime, + ), + format!(" + {}", lifetime_name), + Applicability::MaybeIncorrect, + ); } - } + _ => { + err.span_suggestion_verbose( + lt.span, + &format!( + "consider changing the trait object's explicit \ + `'static` bound to {}", + lifetime, + ), + lifetime_name, + Applicability::MaybeIncorrect, + ); + err.span_suggestion_verbose( + param_info.param_ty_span, + &format!( + "alternatively, set an explicit `'static` lifetime \ + in this parameter", + ), + param_info.param_ty.to_string(), + Applicability::MaybeIncorrect, + ); + } + }, _ => {} } } diff --git a/src/test/ui/async-await/issues/issue-62097.stderr b/src/test/ui/async-await/issues/issue-62097.stderr index af8fc2cd2ab4..fff43ae9f47b 100644 --- a/src/test/ui/async-await/issues/issue-62097.stderr +++ b/src/test/ui/async-await/issues/issue-62097.stderr @@ -4,7 +4,7 @@ error: cannot infer an appropriate lifetime LL | pub async fn run_dummy_fn(&self) { | ^^^^^ | | - | data with this lifetime... + | this data with the anonymous lifetime `'_`... | ...is captured here... LL | foo(|| self.bar()).await; | --- ...and required to be `'static` by this diff --git a/src/test/ui/impl-trait/must_outlive_least_region_or_bound.stderr b/src/test/ui/impl-trait/must_outlive_least_region_or_bound.stderr index 96d4a121c16a..00b6ec38323c 100644 --- a/src/test/ui/impl-trait/must_outlive_least_region_or_bound.stderr +++ b/src/test/ui/impl-trait/must_outlive_least_region_or_bound.stderr @@ -5,9 +5,9 @@ LL | fn elided(x: &i32) -> impl Copy { x } | ---- --------- ^ ...and is captured here | | | | | ...is required to be `'static` by this... - | data with this lifetime... + | this data with the anonymous lifetime `'_`... | -help: to permit non-static references in an `impl Trait` value, you can add an explicit bound for the anonymous lifetime #1 defined on the function body at 3:1 +help: to permit non-static references in an `impl Trait` value, you can add an explicit bound for the anonymous lifetime `'_` | LL | fn elided(x: &i32) -> impl Copy + '_ { x } | ^^^^ @@ -19,9 +19,9 @@ LL | fn explicit<'a>(x: &'a i32) -> impl Copy { x } | ------- --------- ^ ...and is captured here | | | | | ...is required to be `'static` by this... - | data with this lifetime... + | this data with lifetime `'a`... | -help: to permit non-static references in an `impl Trait` value, you can add an explicit bound for the lifetime `'a` as defined on the function body at 6:13 +help: to permit non-static references in an `impl Trait` value, you can add an explicit bound for lifetime `'a` | LL | fn explicit<'a>(x: &'a i32) -> impl Copy + 'a { x } | ^^^^ @@ -33,9 +33,9 @@ LL | fn elided2(x: &i32) -> impl Copy + 'static { x } | ---- ------------------- ^ ...and is captured here | | | | | ...is required to be `'static` by this... - | data with this lifetime... + | this data with the anonymous lifetime `'_`... | -help: consider changing the `impl Trait`'s explicit `'static` bound +help: consider changing the `impl Trait`'s explicit `'static` bound to the anonymous lifetime `'_` | LL | fn elided2(x: &i32) -> impl Copy + '_ { x } | ^^ @@ -51,9 +51,9 @@ LL | fn explicit2<'a>(x: &'a i32) -> impl Copy + 'static { x } | ------- ------------------- ^ ...and is captured here | | | | | ...is required to be `'static` by this... - | data with this lifetime... + | this data with lifetime `'a`... | -help: consider changing the `impl Trait`'s explicit `'static` bound +help: consider changing the `impl Trait`'s explicit `'static` bound to lifetime `'a` | LL | fn explicit2<'a>(x: &'a i32) -> impl Copy + 'a { x } | ^^ @@ -77,9 +77,9 @@ LL | fn with_bound<'a>(x: &'a i32) -> impl LifetimeTrait<'a> + 'static { x } | ------- -------------------------------- ^ ...and is captured here | | | | | ...is required to be `'static` by this... - | data with this lifetime... + | this data with lifetime `'a`... | -help: consider changing the `impl Trait`'s explicit `'static` bound +help: consider changing the `impl Trait`'s explicit `'static` bound to lifetime `'a` | LL | fn with_bound<'a>(x: &'a i32) -> impl LifetimeTrait<'a> + 'a { x } | ^^ @@ -113,9 +113,9 @@ LL | fn elided3(x: &i32) -> Box { Box::new(x) } | | | | | | | ...and is captured here | | ...is required to be `'static` by this... - | data with this lifetime... + | this data with the anonymous lifetime `'_`... | -help: to permit non-static references in a trait object value, you can add an explicit bound for the anonymous lifetime #1 defined on the function body at 18:1 +help: to permit non-static references in a trait object value, you can add an explicit bound for the anonymous lifetime `'_` | LL | fn elided3(x: &i32) -> Box { Box::new(x) } | ^^^^ @@ -128,9 +128,9 @@ LL | fn explicit3<'a>(x: &'a i32) -> Box { Box::new(x) } | | | | | | | ...and is captured here | | ...is required to be `'static` by this... - | data with this lifetime... + | this data with lifetime `'a`... | -help: to permit non-static references in a trait object value, you can add an explicit bound for the lifetime `'a` as defined on the function body at 21:14 +help: to permit non-static references in a trait object value, you can add an explicit bound for lifetime `'a` | LL | fn explicit3<'a>(x: &'a i32) -> Box { Box::new(x) } | ^^^^ @@ -142,9 +142,10 @@ LL | fn elided4(x: &i32) -> Box { Box::new(x) } | ---- ---------^- | | | | | | | ...and is captured here - | data with this lifetime... ...is required to be `'static` by this... + | | ...is required to be `'static` by this... + | this data with the anonymous lifetime `'_`... | -help: consider changing the trait object's explicit `'static` bound +help: consider changing the trait object's explicit `'static` bound to the anonymous lifetime `'_` | LL | fn elided4(x: &i32) -> Box { Box::new(x) } | ^^ @@ -160,9 +161,10 @@ LL | fn explicit4<'a>(x: &'a i32) -> Box { Box::new(x) } | ------- ---------^- | | | | | | | ...and is captured here - | data with this lifetime... ...is required to be `'static` by this... + | | ...is required to be `'static` by this... + | this data with lifetime `'a`... | -help: consider changing the trait object's explicit `'static` bound +help: consider changing the trait object's explicit `'static` bound to lifetime `'a` | LL | fn explicit4<'a>(x: &'a i32) -> Box { Box::new(x) } | ^^ diff --git a/src/test/ui/impl-trait/static-return-lifetime-infered.stderr b/src/test/ui/impl-trait/static-return-lifetime-infered.stderr index 1c3a5979ee55..67d4f60dff6f 100644 --- a/src/test/ui/impl-trait/static-return-lifetime-infered.stderr +++ b/src/test/ui/impl-trait/static-return-lifetime-infered.stderr @@ -4,13 +4,13 @@ error: cannot infer an appropriate lifetime LL | fn iter_values_anon(&self) -> impl Iterator { | ----- ----------------------- ...is required to be `'static` by this... | | - | data with this lifetime... + | this data with the anonymous lifetime `'_`... LL | self.x.iter().map(|a| a.0) | ------ ^^^^ | | | ...and is captured here | -help: to permit non-static references in an `impl Trait` value, you can add an explicit bound for the anonymous lifetime #1 defined on the method body at 6:5 +help: to permit non-static references in an `impl Trait` value, you can add an explicit bound for the anonymous lifetime `'_` | LL | fn iter_values_anon(&self) -> impl Iterator + '_ { | ^^^^ @@ -21,13 +21,13 @@ error: cannot infer an appropriate lifetime LL | fn iter_values<'a>(&'a self) -> impl Iterator { | -------- ----------------------- ...is required to be `'static` by this... | | - | data with this lifetime... + | this data with lifetime `'a`... LL | self.x.iter().map(|a| a.0) | ------ ^^^^ | | | ...and is captured here | -help: to permit non-static references in an `impl Trait` value, you can add an explicit bound for the lifetime `'a` as defined on the method body at 10:20 +help: to permit non-static references in an `impl Trait` value, you can add an explicit bound for lifetime `'a` | LL | fn iter_values<'a>(&'a self) -> impl Iterator + 'a { | ^^^^ diff --git a/src/test/ui/issues/issue-16922.stderr b/src/test/ui/issues/issue-16922.stderr index 038df47e1bd9..c533a72dfc01 100644 --- a/src/test/ui/issues/issue-16922.stderr +++ b/src/test/ui/issues/issue-16922.stderr @@ -2,14 +2,14 @@ error: cannot infer an appropriate lifetime --> $DIR/issue-16922.rs:4:14 | LL | fn foo(value: &T) -> Box { - | -- data with this lifetime... + | -- this data with the anonymous lifetime `'_`... LL | Box::new(value) as Box | ---------^^^^^- | | | | | ...and is captured here | ...is required to be `'static` by this... | -help: to permit non-static references in a trait object value, you can add an explicit bound for the anonymous lifetime #1 defined on the function body at 3:1 +help: to permit non-static references in a trait object value, you can add an explicit bound for the anonymous lifetime `'_` | LL | fn foo(value: &T) -> Box { | ^^^^ diff --git a/src/test/ui/object-lifetime/object-lifetime-default-from-box-error.stderr b/src/test/ui/object-lifetime/object-lifetime-default-from-box-error.stderr index 555622c9d13c..6edef8086b93 100644 --- a/src/test/ui/object-lifetime/object-lifetime-default-from-box-error.stderr +++ b/src/test/ui/object-lifetime/object-lifetime-default-from-box-error.stderr @@ -2,12 +2,12 @@ error: cannot infer an appropriate lifetime --> $DIR/object-lifetime-default-from-box-error.rs:18:5 | LL | fn load(ss: &mut SomeStruct) -> Box { - | --------------- data with this lifetime... + | --------------- this data with the anonymous lifetime `'_`... ... LL | ss.r | ^^^^ ...is captured and required to be `'static` here | -help: to permit non-static references in a trait object value, you can add an explicit bound for the anonymous lifetime #2 defined on the function body at 14:1 +help: to permit non-static references in a trait object value, you can add an explicit bound for the anonymous lifetime `'_` | LL | fn load(ss: &mut SomeStruct) -> Box { | ^^^^ diff --git a/src/test/ui/regions/region-object-lifetime-in-coercion.stderr b/src/test/ui/regions/region-object-lifetime-in-coercion.stderr index 673300cebc26..4b08c4bff2eb 100644 --- a/src/test/ui/regions/region-object-lifetime-in-coercion.stderr +++ b/src/test/ui/regions/region-object-lifetime-in-coercion.stderr @@ -2,14 +2,14 @@ error: cannot infer an appropriate lifetime --> $DIR/region-object-lifetime-in-coercion.rs:8:46 | LL | fn a(v: &[u8]) -> Box { - | ----- data with this lifetime... + | ----- this data with the anonymous lifetime `'_`... LL | let x: Box = Box::new(v); | ---------^- | | | | | ...and is captured here | ...is required to be `'static` by this... | -help: consider changing the trait object's explicit `'static` bound +help: consider changing the trait object's explicit `'static` bound to the anonymous lifetime `'_` | LL | fn a(v: &[u8]) -> Box { | ^^ @@ -22,14 +22,14 @@ error: cannot infer an appropriate lifetime --> $DIR/region-object-lifetime-in-coercion.rs:13:14 | LL | fn b(v: &[u8]) -> Box { - | ----- data with this lifetime... + | ----- this data with the anonymous lifetime `'_`... LL | Box::new(v) | ---------^- | | | | | ...and is captured here | ...is required to be `'static` by this... | -help: consider changing the trait object's explicit `'static` bound +help: consider changing the trait object's explicit `'static` bound to the anonymous lifetime `'_` | LL | fn b(v: &[u8]) -> Box { | ^^ @@ -42,7 +42,7 @@ error: cannot infer an appropriate lifetime --> $DIR/region-object-lifetime-in-coercion.rs:19:14 | LL | fn c(v: &[u8]) -> Box { - | ----- data with this lifetime... + | ----- this data with the anonymous lifetime `'_`... ... LL | Box::new(v) | ---------^- @@ -50,7 +50,7 @@ LL | Box::new(v) | | ...and is captured here | ...is required to be `'static` by this... | -help: to permit non-static references in a trait object value, you can add an explicit bound for the anonymous lifetime #1 defined on the function body at 16:1 +help: to permit non-static references in a trait object value, you can add an explicit bound for the anonymous lifetime `'_` | LL | fn c(v: &[u8]) -> Box { | ^^^^ diff --git a/src/test/ui/regions/regions-close-object-into-object-2.stderr b/src/test/ui/regions/regions-close-object-into-object-2.stderr index 982ed07232a8..894be310fd14 100644 --- a/src/test/ui/regions/regions-close-object-into-object-2.stderr +++ b/src/test/ui/regions/regions-close-object-into-object-2.stderr @@ -2,14 +2,14 @@ error: cannot infer an appropriate lifetime --> $DIR/regions-close-object-into-object-2.rs:10:11 | LL | fn g<'a, T: 'static>(v: Box + 'a>) -> Box { - | ------------------ data with this lifetime... + | ------------------ this data with lifetime `'a`... LL | box B(&*v) as Box | ------^^^--------------- | | | | | ...and is captured here | ...is required to be `'static` by this... | -help: consider changing the trait object's explicit `'static` bound +help: consider changing the trait object's explicit `'static` bound to lifetime `'a` | LL | fn g<'a, T: 'static>(v: Box + 'a>) -> Box { | ^^ diff --git a/src/test/ui/regions/regions-close-object-into-object-4.stderr b/src/test/ui/regions/regions-close-object-into-object-4.stderr index 1b82098ee13c..ce261d78c290 100644 --- a/src/test/ui/regions/regions-close-object-into-object-4.stderr +++ b/src/test/ui/regions/regions-close-object-into-object-4.stderr @@ -2,14 +2,14 @@ error: cannot infer an appropriate lifetime --> $DIR/regions-close-object-into-object-4.rs:10:11 | LL | fn i<'a, T, U>(v: Box+'a>) -> Box { - | ---------------- data with this lifetime... + | ---------------- this data with lifetime `'a`... LL | box B(&*v) as Box | ------^^^--------------- | | | | | ...and is captured here | ...is required to be `'static` by this... | -help: consider changing the trait object's explicit `'static` bound +help: consider changing the trait object's explicit `'static` bound to lifetime `'a` | LL | fn i<'a, T, U>(v: Box+'a>) -> Box { | ^^ diff --git a/src/test/ui/regions/regions-proc-bound-capture.stderr b/src/test/ui/regions/regions-proc-bound-capture.stderr index e7bbfaababe8..a0df1815247c 100644 --- a/src/test/ui/regions/regions-proc-bound-capture.stderr +++ b/src/test/ui/regions/regions-proc-bound-capture.stderr @@ -2,7 +2,7 @@ error: cannot infer an appropriate lifetime --> $DIR/regions-proc-bound-capture.rs:9:14 | LL | fn static_proc(x: &isize) -> Box (isize) + 'static> { - | ------ data with this lifetime... + | ------ this data with the anonymous lifetime `'_`... LL | // This is illegal, because the region bound on `proc` is 'static. LL | Box::new(move || { *x }) | ---------^^^^^^^^^^^^^^- @@ -10,7 +10,7 @@ LL | Box::new(move || { *x }) | | ...and is captured here | ...is required to be `'static` by this... | -help: consider changing the trait object's explicit `'static` bound +help: consider changing the trait object's explicit `'static` bound to the anonymous lifetime `'_` | LL | fn static_proc(x: &isize) -> Box (isize) + '_> { | ^^ diff --git a/src/test/ui/self/arbitrary_self_types_pin_lifetime_impl_trait-async.stderr b/src/test/ui/self/arbitrary_self_types_pin_lifetime_impl_trait-async.stderr index 1aeabce5e8aa..5520341b5b1c 100644 --- a/src/test/ui/self/arbitrary_self_types_pin_lifetime_impl_trait-async.stderr +++ b/src/test/ui/self/arbitrary_self_types_pin_lifetime_impl_trait-async.stderr @@ -4,7 +4,7 @@ error: cannot infer an appropriate lifetime LL | async fn f(self: Pin<&Self>) -> impl Clone { self } | ^^^^ ---------- ---------- ...and required to be `'static` by this | | | - | | data with this lifetime... + | | this data with the anonymous lifetime `'_`... | ...is captured here... error: aborting due to previous error diff --git a/src/test/ui/self/arbitrary_self_types_pin_lifetime_impl_trait.stderr b/src/test/ui/self/arbitrary_self_types_pin_lifetime_impl_trait.stderr index 04c475be787b..5374929f3a45 100644 --- a/src/test/ui/self/arbitrary_self_types_pin_lifetime_impl_trait.stderr +++ b/src/test/ui/self/arbitrary_self_types_pin_lifetime_impl_trait.stderr @@ -5,9 +5,9 @@ LL | fn f(self: Pin<&Self>) -> impl Clone { self } | ---------- ---------- ^^^^ ...and is captured here | | | | | ...is required to be `'static` by this... - | data with this lifetime... + | this data with the anonymous lifetime `'_`... | -help: to permit non-static references in an `impl Trait` value, you can add an explicit bound for the anonymous lifetime #1 defined on the method body at 6:5 +help: to permit non-static references in an `impl Trait` value, you can add an explicit bound for the anonymous lifetime `'_` | LL | fn f(self: Pin<&Self>) -> impl Clone + '_ { self } | ^^^^ diff --git a/src/test/ui/suggestions/lifetimes/missing-lifetimes-in-signature.stderr b/src/test/ui/suggestions/lifetimes/missing-lifetimes-in-signature.stderr index 5cf170d566ca..471f3cd14aa3 100644 --- a/src/test/ui/suggestions/lifetimes/missing-lifetimes-in-signature.stderr +++ b/src/test/ui/suggestions/lifetimes/missing-lifetimes-in-signature.stderr @@ -12,14 +12,14 @@ error: cannot infer an appropriate lifetime LL | fn foo(g: G, dest: &mut T) -> impl FnOnce() | ------ ------------- ...is required to be `'static` by this... | | - | data with this lifetime... + | this data with the anonymous lifetime `'_`... ... LL | / move || { LL | | *dest = g.get(); LL | | } | |_____^ ...and is captured here | -help: to permit non-static references in an `impl Trait` value, you can add an explicit bound for the anonymous lifetime #1 defined on the function body at 15:1 +help: to permit non-static references in an `impl Trait` value, you can add an explicit bound for the anonymous lifetime `'_` | LL | fn foo(g: G, dest: &mut T) -> impl FnOnce() + '_ | ^^^^ diff --git a/src/test/ui/underscore-lifetime/dyn-trait-underscore.stderr b/src/test/ui/underscore-lifetime/dyn-trait-underscore.stderr index 4dc4aac6ceac..5fd03f9770e5 100644 --- a/src/test/ui/underscore-lifetime/dyn-trait-underscore.stderr +++ b/src/test/ui/underscore-lifetime/dyn-trait-underscore.stderr @@ -2,12 +2,12 @@ error: cannot infer an appropriate lifetime --> $DIR/dyn-trait-underscore.rs:8:20 | LL | fn a(items: &[T]) -> Box> { - | ---- data with this lifetime... + | ---- this data with the anonymous lifetime `'_`... LL | // ^^^^^^^^^^^^^^^^^^^^^ bound *here* defaults to `'static` LL | Box::new(items.iter()) | ---------------^^^^--- ...is captured and required to be `'static` here | -help: to permit non-static references in a trait object value, you can add an explicit bound for the anonymous lifetime #1 defined on the function body at 6:1 +help: to permit non-static references in a trait object value, you can add an explicit bound for the anonymous lifetime `'_` | LL | fn a(items: &[T]) -> Box + '_> { | ^^^^ From e75588934c01d6bc9abb02979eb61168a7b5c598 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Fri, 29 May 2020 19:46:22 -0700 Subject: [PATCH 052/123] Move overlapping span to a note --- .../nice_region_error/static_impl_trait.rs | 31 ++++++++++++- .../must_outlive_least_region_or_bound.stderr | 44 ++++++++++++------- src/test/ui/issues/issue-16922.stderr | 10 +++-- .../region-object-lifetime-in-coercion.stderr | 30 ++++++++----- .../regions-close-object-into-object-2.stderr | 10 +++-- .../regions-close-object-into-object-4.stderr | 10 +++-- .../regions/regions-proc-bound-capture.stderr | 10 +++-- 7 files changed, 99 insertions(+), 46 deletions(-) diff --git a/src/librustc_infer/infer/error_reporting/nice_region_error/static_impl_trait.rs b/src/librustc_infer/infer/error_reporting/nice_region_error/static_impl_trait.rs index e9f165d309f8..cc95441b68a0 100644 --- a/src/librustc_infer/infer/error_reporting/nice_region_error/static_impl_trait.rs +++ b/src/librustc_infer/infer/error_reporting/nice_region_error/static_impl_trait.rs @@ -53,7 +53,36 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { // Customize the spans and labels depending on their relative order so // that split sentences flow correctly. - if sup_origin.span().shrink_to_hi() <= return_sp.shrink_to_lo() { + if sup_origin.span().overlaps(return_sp) && sp == sup_origin.span() { + // Avoid the following: + // + // error: cannot infer an appropriate lifetime + // --> $DIR/must_outlive_least_region_or_bound.rs:18:50 + // | + // LL | fn foo(x: &i32) -> Box { Box::new(x) } + // | ---- ---------^- + // | | | | + // | | | ...and is captured here + // | | ...is required to be `'static` by this... + // | this data with the anonymous lifetime `'_`... + // + // and instead show: + // + // error: cannot infer an appropriate lifetime + // --> $DIR/must_outlive_least_region_or_bound.rs:18:50 + // | + // LL | fn foo(x: &i32) -> Box { Box::new(x) } + // | ---- ^ ...is captured here with a `'static` requirement + // | | + // | this data with the anonymous lifetime `'_`... + // | + // note: ...is required to be `'static` by this + // | + // LL | fn elided3(x: &i32) -> Box { Box::new(x) } + // | ^^^^^^^^^^^ + err.span_label(sup_origin.span(), "...is captured here..."); + err.span_note(return_sp, "...and required to be `'static` by this"); + } else if sup_origin.span() <= return_sp { err.span_label(sup_origin.span(), "...is captured here..."); err.span_label(return_sp, "...and required to be `'static` by this"); } else { diff --git a/src/test/ui/impl-trait/must_outlive_least_region_or_bound.stderr b/src/test/ui/impl-trait/must_outlive_least_region_or_bound.stderr index 00b6ec38323c..2da49379ea8c 100644 --- a/src/test/ui/impl-trait/must_outlive_least_region_or_bound.stderr +++ b/src/test/ui/impl-trait/must_outlive_least_region_or_bound.stderr @@ -109,12 +109,15 @@ error: cannot infer an appropriate lifetime --> $DIR/must_outlive_least_region_or_bound.rs:18:50 | LL | fn elided3(x: &i32) -> Box { Box::new(x) } - | ---- ---------^- - | | | | - | | | ...and is captured here - | | ...is required to be `'static` by this... + | ---- ^ ...is captured here... + | | | this data with the anonymous lifetime `'_`... | +note: ...and required to be `'static` by this + --> $DIR/must_outlive_least_region_or_bound.rs:18:41 + | +LL | fn elided3(x: &i32) -> Box { Box::new(x) } + | ^^^^^^^^^^^ help: to permit non-static references in a trait object value, you can add an explicit bound for the anonymous lifetime `'_` | LL | fn elided3(x: &i32) -> Box { Box::new(x) } @@ -124,12 +127,15 @@ error: cannot infer an appropriate lifetime --> $DIR/must_outlive_least_region_or_bound.rs:21:59 | LL | fn explicit3<'a>(x: &'a i32) -> Box { Box::new(x) } - | ------- ---------^- - | | | | - | | | ...and is captured here - | | ...is required to be `'static` by this... + | ------- ^ ...is captured here... + | | | this data with lifetime `'a`... | +note: ...and required to be `'static` by this + --> $DIR/must_outlive_least_region_or_bound.rs:21:50 + | +LL | fn explicit3<'a>(x: &'a i32) -> Box { Box::new(x) } + | ^^^^^^^^^^^ help: to permit non-static references in a trait object value, you can add an explicit bound for lifetime `'a` | LL | fn explicit3<'a>(x: &'a i32) -> Box { Box::new(x) } @@ -139,12 +145,15 @@ error: cannot infer an appropriate lifetime --> $DIR/must_outlive_least_region_or_bound.rs:24:60 | LL | fn elided4(x: &i32) -> Box { Box::new(x) } - | ---- ---------^- - | | | | - | | | ...and is captured here - | | ...is required to be `'static` by this... + | ---- ^ ...is captured here... + | | | this data with the anonymous lifetime `'_`... | +note: ...and required to be `'static` by this + --> $DIR/must_outlive_least_region_or_bound.rs:24:51 + | +LL | fn elided4(x: &i32) -> Box { Box::new(x) } + | ^^^^^^^^^^^ help: consider changing the trait object's explicit `'static` bound to the anonymous lifetime `'_` | LL | fn elided4(x: &i32) -> Box { Box::new(x) } @@ -158,12 +167,13 @@ error: cannot infer an appropriate lifetime --> $DIR/must_outlive_least_region_or_bound.rs:27:69 | LL | fn explicit4<'a>(x: &'a i32) -> Box { Box::new(x) } - | ------- ---------^- - | | | | - | | | ...and is captured here - | | ...is required to be `'static` by this... - | this data with lifetime `'a`... + | ------- this data with lifetime `'a`... ^ ...is captured here... | +note: ...and required to be `'static` by this + --> $DIR/must_outlive_least_region_or_bound.rs:27:60 + | +LL | fn explicit4<'a>(x: &'a i32) -> Box { Box::new(x) } + | ^^^^^^^^^^^ help: consider changing the trait object's explicit `'static` bound to lifetime `'a` | LL | fn explicit4<'a>(x: &'a i32) -> Box { Box::new(x) } diff --git a/src/test/ui/issues/issue-16922.stderr b/src/test/ui/issues/issue-16922.stderr index c533a72dfc01..6c0b26a86b65 100644 --- a/src/test/ui/issues/issue-16922.stderr +++ b/src/test/ui/issues/issue-16922.stderr @@ -4,11 +4,13 @@ error: cannot infer an appropriate lifetime LL | fn foo(value: &T) -> Box { | -- this data with the anonymous lifetime `'_`... LL | Box::new(value) as Box - | ---------^^^^^- - | | | - | | ...and is captured here - | ...is required to be `'static` by this... + | ^^^^^ ...is captured here... | +note: ...and required to be `'static` by this + --> $DIR/issue-16922.rs:4:5 + | +LL | Box::new(value) as Box + | ^^^^^^^^^^^^^^^ help: to permit non-static references in a trait object value, you can add an explicit bound for the anonymous lifetime `'_` | LL | fn foo(value: &T) -> Box { diff --git a/src/test/ui/regions/region-object-lifetime-in-coercion.stderr b/src/test/ui/regions/region-object-lifetime-in-coercion.stderr index 4b08c4bff2eb..b333c314c57c 100644 --- a/src/test/ui/regions/region-object-lifetime-in-coercion.stderr +++ b/src/test/ui/regions/region-object-lifetime-in-coercion.stderr @@ -4,11 +4,13 @@ error: cannot infer an appropriate lifetime LL | fn a(v: &[u8]) -> Box { | ----- this data with the anonymous lifetime `'_`... LL | let x: Box = Box::new(v); - | ---------^- - | | | - | | ...and is captured here - | ...is required to be `'static` by this... + | ^ ...is captured here... | +note: ...and required to be `'static` by this + --> $DIR/region-object-lifetime-in-coercion.rs:8:37 + | +LL | let x: Box = Box::new(v); + | ^^^^^^^^^^^ help: consider changing the trait object's explicit `'static` bound to the anonymous lifetime `'_` | LL | fn a(v: &[u8]) -> Box { @@ -24,11 +26,13 @@ error: cannot infer an appropriate lifetime LL | fn b(v: &[u8]) -> Box { | ----- this data with the anonymous lifetime `'_`... LL | Box::new(v) - | ---------^- - | | | - | | ...and is captured here - | ...is required to be `'static` by this... + | ^ ...is captured here... | +note: ...and required to be `'static` by this + --> $DIR/region-object-lifetime-in-coercion.rs:13:5 + | +LL | Box::new(v) + | ^^^^^^^^^^^ help: consider changing the trait object's explicit `'static` bound to the anonymous lifetime `'_` | LL | fn b(v: &[u8]) -> Box { @@ -45,11 +49,13 @@ LL | fn c(v: &[u8]) -> Box { | ----- this data with the anonymous lifetime `'_`... ... LL | Box::new(v) - | ---------^- - | | | - | | ...and is captured here - | ...is required to be `'static` by this... + | ^ ...is captured here... | +note: ...and required to be `'static` by this + --> $DIR/region-object-lifetime-in-coercion.rs:19:5 + | +LL | Box::new(v) + | ^^^^^^^^^^^ help: to permit non-static references in a trait object value, you can add an explicit bound for the anonymous lifetime `'_` | LL | fn c(v: &[u8]) -> Box { diff --git a/src/test/ui/regions/regions-close-object-into-object-2.stderr b/src/test/ui/regions/regions-close-object-into-object-2.stderr index 894be310fd14..3127ae65ace7 100644 --- a/src/test/ui/regions/regions-close-object-into-object-2.stderr +++ b/src/test/ui/regions/regions-close-object-into-object-2.stderr @@ -4,11 +4,13 @@ error: cannot infer an appropriate lifetime LL | fn g<'a, T: 'static>(v: Box + 'a>) -> Box { | ------------------ this data with lifetime `'a`... LL | box B(&*v) as Box - | ------^^^--------------- - | | | - | | ...and is captured here - | ...is required to be `'static` by this... + | ^^^ ...is captured here... | +note: ...and required to be `'static` by this + --> $DIR/regions-close-object-into-object-2.rs:10:5 + | +LL | box B(&*v) as Box + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider changing the trait object's explicit `'static` bound to lifetime `'a` | LL | fn g<'a, T: 'static>(v: Box + 'a>) -> Box { diff --git a/src/test/ui/regions/regions-close-object-into-object-4.stderr b/src/test/ui/regions/regions-close-object-into-object-4.stderr index ce261d78c290..b18c61f13769 100644 --- a/src/test/ui/regions/regions-close-object-into-object-4.stderr +++ b/src/test/ui/regions/regions-close-object-into-object-4.stderr @@ -4,11 +4,13 @@ error: cannot infer an appropriate lifetime LL | fn i<'a, T, U>(v: Box+'a>) -> Box { | ---------------- this data with lifetime `'a`... LL | box B(&*v) as Box - | ------^^^--------------- - | | | - | | ...and is captured here - | ...is required to be `'static` by this... + | ^^^ ...is captured here... | +note: ...and required to be `'static` by this + --> $DIR/regions-close-object-into-object-4.rs:10:5 + | +LL | box B(&*v) as Box + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider changing the trait object's explicit `'static` bound to lifetime `'a` | LL | fn i<'a, T, U>(v: Box+'a>) -> Box { diff --git a/src/test/ui/regions/regions-proc-bound-capture.stderr b/src/test/ui/regions/regions-proc-bound-capture.stderr index a0df1815247c..5cb9506afd35 100644 --- a/src/test/ui/regions/regions-proc-bound-capture.stderr +++ b/src/test/ui/regions/regions-proc-bound-capture.stderr @@ -5,11 +5,13 @@ LL | fn static_proc(x: &isize) -> Box (isize) + 'static> { | ------ this data with the anonymous lifetime `'_`... LL | // This is illegal, because the region bound on `proc` is 'static. LL | Box::new(move || { *x }) - | ---------^^^^^^^^^^^^^^- - | | | - | | ...and is captured here - | ...is required to be `'static` by this... + | ^^^^^^^^^^^^^^ ...is captured here... | +note: ...and required to be `'static` by this + --> $DIR/regions-proc-bound-capture.rs:9:5 + | +LL | Box::new(move || { *x }) + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider changing the trait object's explicit `'static` bound to the anonymous lifetime `'_` | LL | fn static_proc(x: &isize) -> Box (isize) + '_> { From bc1579060981b5e95a18409e876c92bf0c9307e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Sat, 30 May 2020 09:54:05 -0700 Subject: [PATCH 053/123] Tweak output for overlapping required/captured spans --- .../nice_region_error/static_impl_trait.rs | 10 +++---- .../must_outlive_least_region_or_bound.stderr | 28 +++---------------- src/test/ui/issues/issue-16922.stderr | 7 +---- .../region-object-lifetime-in-coercion.stderr | 21 ++------------ .../regions-close-object-into-object-2.stderr | 7 +---- .../regions-close-object-into-object-4.stderr | 7 +---- .../regions/regions-proc-bound-capture.stderr | 7 +---- 7 files changed, 15 insertions(+), 72 deletions(-) diff --git a/src/librustc_infer/infer/error_reporting/nice_region_error/static_impl_trait.rs b/src/librustc_infer/infer/error_reporting/nice_region_error/static_impl_trait.rs index cc95441b68a0..253057536f13 100644 --- a/src/librustc_infer/infer/error_reporting/nice_region_error/static_impl_trait.rs +++ b/src/librustc_infer/infer/error_reporting/nice_region_error/static_impl_trait.rs @@ -76,12 +76,10 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { // | | // | this data with the anonymous lifetime `'_`... // | - // note: ...is required to be `'static` by this - // | - // LL | fn elided3(x: &i32) -> Box { Box::new(x) } - // | ^^^^^^^^^^^ - err.span_label(sup_origin.span(), "...is captured here..."); - err.span_note(return_sp, "...and required to be `'static` by this"); + err.span_label( + sup_origin.span(), + "...is captured here with a `'static` requirement", + ); } else if sup_origin.span() <= return_sp { err.span_label(sup_origin.span(), "...is captured here..."); err.span_label(return_sp, "...and required to be `'static` by this"); diff --git a/src/test/ui/impl-trait/must_outlive_least_region_or_bound.stderr b/src/test/ui/impl-trait/must_outlive_least_region_or_bound.stderr index 2da49379ea8c..82e44cff9cc4 100644 --- a/src/test/ui/impl-trait/must_outlive_least_region_or_bound.stderr +++ b/src/test/ui/impl-trait/must_outlive_least_region_or_bound.stderr @@ -109,15 +109,10 @@ error: cannot infer an appropriate lifetime --> $DIR/must_outlive_least_region_or_bound.rs:18:50 | LL | fn elided3(x: &i32) -> Box { Box::new(x) } - | ---- ^ ...is captured here... + | ---- ^ ...is captured here with a `'static` requirement | | | this data with the anonymous lifetime `'_`... | -note: ...and required to be `'static` by this - --> $DIR/must_outlive_least_region_or_bound.rs:18:41 - | -LL | fn elided3(x: &i32) -> Box { Box::new(x) } - | ^^^^^^^^^^^ help: to permit non-static references in a trait object value, you can add an explicit bound for the anonymous lifetime `'_` | LL | fn elided3(x: &i32) -> Box { Box::new(x) } @@ -127,15 +122,10 @@ error: cannot infer an appropriate lifetime --> $DIR/must_outlive_least_region_or_bound.rs:21:59 | LL | fn explicit3<'a>(x: &'a i32) -> Box { Box::new(x) } - | ------- ^ ...is captured here... + | ------- ^ ...is captured here with a `'static` requirement | | | this data with lifetime `'a`... | -note: ...and required to be `'static` by this - --> $DIR/must_outlive_least_region_or_bound.rs:21:50 - | -LL | fn explicit3<'a>(x: &'a i32) -> Box { Box::new(x) } - | ^^^^^^^^^^^ help: to permit non-static references in a trait object value, you can add an explicit bound for lifetime `'a` | LL | fn explicit3<'a>(x: &'a i32) -> Box { Box::new(x) } @@ -145,15 +135,10 @@ error: cannot infer an appropriate lifetime --> $DIR/must_outlive_least_region_or_bound.rs:24:60 | LL | fn elided4(x: &i32) -> Box { Box::new(x) } - | ---- ^ ...is captured here... + | ---- ^ ...is captured here with a `'static` requirement | | | this data with the anonymous lifetime `'_`... | -note: ...and required to be `'static` by this - --> $DIR/must_outlive_least_region_or_bound.rs:24:51 - | -LL | fn elided4(x: &i32) -> Box { Box::new(x) } - | ^^^^^^^^^^^ help: consider changing the trait object's explicit `'static` bound to the anonymous lifetime `'_` | LL | fn elided4(x: &i32) -> Box { Box::new(x) } @@ -167,13 +152,8 @@ error: cannot infer an appropriate lifetime --> $DIR/must_outlive_least_region_or_bound.rs:27:69 | LL | fn explicit4<'a>(x: &'a i32) -> Box { Box::new(x) } - | ------- this data with lifetime `'a`... ^ ...is captured here... + | ------- this data with lifetime `'a`... ^ ...is captured here with a `'static` requirement | -note: ...and required to be `'static` by this - --> $DIR/must_outlive_least_region_or_bound.rs:27:60 - | -LL | fn explicit4<'a>(x: &'a i32) -> Box { Box::new(x) } - | ^^^^^^^^^^^ help: consider changing the trait object's explicit `'static` bound to lifetime `'a` | LL | fn explicit4<'a>(x: &'a i32) -> Box { Box::new(x) } diff --git a/src/test/ui/issues/issue-16922.stderr b/src/test/ui/issues/issue-16922.stderr index 6c0b26a86b65..a254343cd1bb 100644 --- a/src/test/ui/issues/issue-16922.stderr +++ b/src/test/ui/issues/issue-16922.stderr @@ -4,13 +4,8 @@ error: cannot infer an appropriate lifetime LL | fn foo(value: &T) -> Box { | -- this data with the anonymous lifetime `'_`... LL | Box::new(value) as Box - | ^^^^^ ...is captured here... + | ^^^^^ ...is captured here with a `'static` requirement | -note: ...and required to be `'static` by this - --> $DIR/issue-16922.rs:4:5 - | -LL | Box::new(value) as Box - | ^^^^^^^^^^^^^^^ help: to permit non-static references in a trait object value, you can add an explicit bound for the anonymous lifetime `'_` | LL | fn foo(value: &T) -> Box { diff --git a/src/test/ui/regions/region-object-lifetime-in-coercion.stderr b/src/test/ui/regions/region-object-lifetime-in-coercion.stderr index b333c314c57c..97d1f3579fcd 100644 --- a/src/test/ui/regions/region-object-lifetime-in-coercion.stderr +++ b/src/test/ui/regions/region-object-lifetime-in-coercion.stderr @@ -4,13 +4,8 @@ error: cannot infer an appropriate lifetime LL | fn a(v: &[u8]) -> Box { | ----- this data with the anonymous lifetime `'_`... LL | let x: Box = Box::new(v); - | ^ ...is captured here... + | ^ ...is captured here with a `'static` requirement | -note: ...and required to be `'static` by this - --> $DIR/region-object-lifetime-in-coercion.rs:8:37 - | -LL | let x: Box = Box::new(v); - | ^^^^^^^^^^^ help: consider changing the trait object's explicit `'static` bound to the anonymous lifetime `'_` | LL | fn a(v: &[u8]) -> Box { @@ -26,13 +21,8 @@ error: cannot infer an appropriate lifetime LL | fn b(v: &[u8]) -> Box { | ----- this data with the anonymous lifetime `'_`... LL | Box::new(v) - | ^ ...is captured here... + | ^ ...is captured here with a `'static` requirement | -note: ...and required to be `'static` by this - --> $DIR/region-object-lifetime-in-coercion.rs:13:5 - | -LL | Box::new(v) - | ^^^^^^^^^^^ help: consider changing the trait object's explicit `'static` bound to the anonymous lifetime `'_` | LL | fn b(v: &[u8]) -> Box { @@ -49,13 +39,8 @@ LL | fn c(v: &[u8]) -> Box { | ----- this data with the anonymous lifetime `'_`... ... LL | Box::new(v) - | ^ ...is captured here... + | ^ ...is captured here with a `'static` requirement | -note: ...and required to be `'static` by this - --> $DIR/region-object-lifetime-in-coercion.rs:19:5 - | -LL | Box::new(v) - | ^^^^^^^^^^^ help: to permit non-static references in a trait object value, you can add an explicit bound for the anonymous lifetime `'_` | LL | fn c(v: &[u8]) -> Box { diff --git a/src/test/ui/regions/regions-close-object-into-object-2.stderr b/src/test/ui/regions/regions-close-object-into-object-2.stderr index 3127ae65ace7..3d707f2d9994 100644 --- a/src/test/ui/regions/regions-close-object-into-object-2.stderr +++ b/src/test/ui/regions/regions-close-object-into-object-2.stderr @@ -4,13 +4,8 @@ error: cannot infer an appropriate lifetime LL | fn g<'a, T: 'static>(v: Box + 'a>) -> Box { | ------------------ this data with lifetime `'a`... LL | box B(&*v) as Box - | ^^^ ...is captured here... + | ^^^ ...is captured here with a `'static` requirement | -note: ...and required to be `'static` by this - --> $DIR/regions-close-object-into-object-2.rs:10:5 - | -LL | box B(&*v) as Box - | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider changing the trait object's explicit `'static` bound to lifetime `'a` | LL | fn g<'a, T: 'static>(v: Box + 'a>) -> Box { diff --git a/src/test/ui/regions/regions-close-object-into-object-4.stderr b/src/test/ui/regions/regions-close-object-into-object-4.stderr index b18c61f13769..70282c8cbdb3 100644 --- a/src/test/ui/regions/regions-close-object-into-object-4.stderr +++ b/src/test/ui/regions/regions-close-object-into-object-4.stderr @@ -4,13 +4,8 @@ error: cannot infer an appropriate lifetime LL | fn i<'a, T, U>(v: Box+'a>) -> Box { | ---------------- this data with lifetime `'a`... LL | box B(&*v) as Box - | ^^^ ...is captured here... + | ^^^ ...is captured here with a `'static` requirement | -note: ...and required to be `'static` by this - --> $DIR/regions-close-object-into-object-4.rs:10:5 - | -LL | box B(&*v) as Box - | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider changing the trait object's explicit `'static` bound to lifetime `'a` | LL | fn i<'a, T, U>(v: Box+'a>) -> Box { diff --git a/src/test/ui/regions/regions-proc-bound-capture.stderr b/src/test/ui/regions/regions-proc-bound-capture.stderr index 5cb9506afd35..8f93fad7fe9d 100644 --- a/src/test/ui/regions/regions-proc-bound-capture.stderr +++ b/src/test/ui/regions/regions-proc-bound-capture.stderr @@ -5,13 +5,8 @@ LL | fn static_proc(x: &isize) -> Box (isize) + 'static> { | ------ this data with the anonymous lifetime `'_`... LL | // This is illegal, because the region bound on `proc` is 'static. LL | Box::new(move || { *x }) - | ^^^^^^^^^^^^^^ ...is captured here... + | ^^^^^^^^^^^^^^ ...is captured here with a `'static` requirement | -note: ...and required to be `'static` by this - --> $DIR/regions-proc-bound-capture.rs:9:5 - | -LL | Box::new(move || { *x }) - | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider changing the trait object's explicit `'static` bound to the anonymous lifetime `'_` | LL | fn static_proc(x: &isize) -> Box (isize) + '_> { From 539e9783dfb713b3af0a9967af8fd0639d700555 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Sat, 30 May 2020 10:15:58 -0700 Subject: [PATCH 054/123] Tweak wording and add error code --- .../nice_region_error/static_impl_trait.rs | 35 +++++++------ .../ui/async-await/issues/issue-62097.stderr | 6 +-- .../must_outlive_least_region_or_bound.stderr | 52 +++++++++---------- .../static-return-lifetime-infered.stderr | 12 ++--- src/test/ui/issues/issue-16922.stderr | 8 +-- ...ect-lifetime-default-from-box-error.stderr | 8 +-- .../region-object-lifetime-in-coercion.stderr | 24 ++++----- .../regions-close-object-into-object-2.stderr | 4 +- .../regions-close-object-into-object-4.stderr | 4 +- .../regions/regions-proc-bound-capture.stderr | 8 +-- ...types_pin_lifetime_impl_trait-async.stderr | 6 +-- ..._self_types_pin_lifetime_impl_trait.stderr | 8 +-- .../missing-lifetimes-in-signature.stderr | 8 +-- .../dyn-trait-underscore.stderr | 8 +-- 14 files changed, 97 insertions(+), 94 deletions(-) diff --git a/src/librustc_infer/infer/error_reporting/nice_region_error/static_impl_trait.rs b/src/librustc_infer/infer/error_reporting/nice_region_error/static_impl_trait.rs index 253057536f13..4e16e8c2c571 100644 --- a/src/librustc_infer/infer/error_reporting/nice_region_error/static_impl_trait.rs +++ b/src/librustc_infer/infer/error_reporting/nice_region_error/static_impl_trait.rs @@ -2,7 +2,7 @@ use crate::infer::error_reporting::nice_region_error::NiceRegionError; use crate::infer::lexical_region_resolve::RegionResolutionError; -use rustc_errors::{Applicability, ErrorReported}; +use rustc_errors::{struct_span_err, Applicability, ErrorReported}; use rustc_hir::{GenericBound, ItemKind, Lifetime, LifetimeName, TyKind}; use rustc_middle::ty::RegionKind; @@ -35,10 +35,14 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { let (lifetime_name, lifetime) = if sup_r.has_name() { (sup_r.to_string(), format!("lifetime `{}`", sup_r)) } else { - ("'_".to_owned(), "the anonymous lifetime `'_`".to_string()) + ("'_".to_owned(), "an anonymous lifetime `'_`".to_string()) }; - let mut err = - self.tcx().sess.struct_span_err(sp, "cannot infer an appropriate lifetime"); + let mut err = struct_span_err!( + self.tcx().sess, + sp, + E0758, + "cannot infer an appropriate lifetime" + ); err.span_label( param_info.param_ty_span, &format!("this data with {}...", lifetime), @@ -61,10 +65,6 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { // | // LL | fn foo(x: &i32) -> Box { Box::new(x) } // | ---- ---------^- - // | | | | - // | | | ...and is captured here - // | | ...is required to be `'static` by this... - // | this data with the anonymous lifetime `'_`... // // and instead show: // @@ -72,25 +72,28 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { // --> $DIR/must_outlive_least_region_or_bound.rs:18:50 // | // LL | fn foo(x: &i32) -> Box { Box::new(x) } - // | ---- ^ ...is captured here with a `'static` requirement - // | | - // | this data with the anonymous lifetime `'_`... - // | + // | ---- ^ err.span_label( sup_origin.span(), - "...is captured here with a `'static` requirement", + "...is captured here requiring it to live as long as `'static`", ); } else if sup_origin.span() <= return_sp { err.span_label(sup_origin.span(), "...is captured here..."); - err.span_label(return_sp, "...and required to be `'static` by this"); + err.span_label( + return_sp, + "...and required to live as long as `'static` by this", + ); } else { - err.span_label(return_sp, "...is required to be `'static` by this..."); + err.span_label( + return_sp, + "...is required to live as long as `'static` by this...", + ); err.span_label(sup_origin.span(), "...and is captured here"); } } else { err.span_label( return_sp, - "...is captured and required to be `'static` here", + "...is captured and required live as long as `'static` here", ); } diff --git a/src/test/ui/async-await/issues/issue-62097.stderr b/src/test/ui/async-await/issues/issue-62097.stderr index fff43ae9f47b..558d89f92894 100644 --- a/src/test/ui/async-await/issues/issue-62097.stderr +++ b/src/test/ui/async-await/issues/issue-62097.stderr @@ -1,13 +1,13 @@ -error: cannot infer an appropriate lifetime +error[E0758]: cannot infer an appropriate lifetime --> $DIR/issue-62097.rs:12:31 | LL | pub async fn run_dummy_fn(&self) { | ^^^^^ | | - | this data with the anonymous lifetime `'_`... + | this data with an anonymous lifetime `'_`... | ...is captured here... LL | foo(|| self.bar()).await; - | --- ...and required to be `'static` by this + | --- ...and required to live as long as `'static` by this error: aborting due to previous error diff --git a/src/test/ui/impl-trait/must_outlive_least_region_or_bound.stderr b/src/test/ui/impl-trait/must_outlive_least_region_or_bound.stderr index 82e44cff9cc4..eff56ddc440d 100644 --- a/src/test/ui/impl-trait/must_outlive_least_region_or_bound.stderr +++ b/src/test/ui/impl-trait/must_outlive_least_region_or_bound.stderr @@ -1,24 +1,24 @@ -error: cannot infer an appropriate lifetime +error[E0758]: cannot infer an appropriate lifetime --> $DIR/must_outlive_least_region_or_bound.rs:3:35 | LL | fn elided(x: &i32) -> impl Copy { x } | ---- --------- ^ ...and is captured here | | | - | | ...is required to be `'static` by this... - | this data with the anonymous lifetime `'_`... + | | ...is required to live as long as `'static` by this... + | this data with an anonymous lifetime `'_`... | -help: to permit non-static references in an `impl Trait` value, you can add an explicit bound for the anonymous lifetime `'_` +help: to permit non-static references in an `impl Trait` value, you can add an explicit bound for an anonymous lifetime `'_` | LL | fn elided(x: &i32) -> impl Copy + '_ { x } | ^^^^ -error: cannot infer an appropriate lifetime +error[E0758]: cannot infer an appropriate lifetime --> $DIR/must_outlive_least_region_or_bound.rs:6:44 | LL | fn explicit<'a>(x: &'a i32) -> impl Copy { x } | ------- --------- ^ ...and is captured here | | | - | | ...is required to be `'static` by this... + | | ...is required to live as long as `'static` by this... | this data with lifetime `'a`... | help: to permit non-static references in an `impl Trait` value, you can add an explicit bound for lifetime `'a` @@ -26,16 +26,16 @@ help: to permit non-static references in an `impl Trait` value, you can add an e LL | fn explicit<'a>(x: &'a i32) -> impl Copy + 'a { x } | ^^^^ -error: cannot infer an appropriate lifetime +error[E0758]: cannot infer an appropriate lifetime --> $DIR/must_outlive_least_region_or_bound.rs:9:46 | LL | fn elided2(x: &i32) -> impl Copy + 'static { x } | ---- ------------------- ^ ...and is captured here | | | - | | ...is required to be `'static` by this... - | this data with the anonymous lifetime `'_`... + | | ...is required to live as long as `'static` by this... + | this data with an anonymous lifetime `'_`... | -help: consider changing the `impl Trait`'s explicit `'static` bound to the anonymous lifetime `'_` +help: consider changing the `impl Trait`'s explicit `'static` bound to an anonymous lifetime `'_` | LL | fn elided2(x: &i32) -> impl Copy + '_ { x } | ^^ @@ -44,13 +44,13 @@ help: alternatively, set an explicit `'static` lifetime to this parameter LL | fn elided2(x: &'static i32) -> impl Copy + 'static { x } | ^^^^^^^^^^^^ -error: cannot infer an appropriate lifetime +error[E0758]: cannot infer an appropriate lifetime --> $DIR/must_outlive_least_region_or_bound.rs:12:55 | LL | fn explicit2<'a>(x: &'a i32) -> impl Copy + 'static { x } | ------- ------------------- ^ ...and is captured here | | | - | | ...is required to be `'static` by this... + | | ...is required to live as long as `'static` by this... | this data with lifetime `'a`... | help: consider changing the `impl Trait`'s explicit `'static` bound to lifetime `'a` @@ -70,13 +70,13 @@ LL | fn foo<'a>(x: &i32) -> impl Copy + 'a { x } | | | help: add explicit lifetime `'a` to the type of `x`: `&'a i32` -error: cannot infer an appropriate lifetime +error[E0758]: cannot infer an appropriate lifetime --> $DIR/must_outlive_least_region_or_bound.rs:33:69 | LL | fn with_bound<'a>(x: &'a i32) -> impl LifetimeTrait<'a> + 'static { x } | ------- -------------------------------- ^ ...and is captured here | | | - | | ...is required to be `'static` by this... + | | ...is required to live as long as `'static` by this... | this data with lifetime `'a`... | help: consider changing the `impl Trait`'s explicit `'static` bound to lifetime `'a` @@ -105,24 +105,24 @@ LL | fn ty_param_wont_outlive_static(x: T) -> impl Debug + 'static { | | | help: consider adding an explicit lifetime bound...: `T: 'static +` -error: cannot infer an appropriate lifetime +error[E0758]: cannot infer an appropriate lifetime --> $DIR/must_outlive_least_region_or_bound.rs:18:50 | LL | fn elided3(x: &i32) -> Box { Box::new(x) } - | ---- ^ ...is captured here with a `'static` requirement + | ---- ^ ...is captured here requiring it to live as long as `'static` | | - | this data with the anonymous lifetime `'_`... + | this data with an anonymous lifetime `'_`... | -help: to permit non-static references in a trait object value, you can add an explicit bound for the anonymous lifetime `'_` +help: to permit non-static references in a trait object value, you can add an explicit bound for an anonymous lifetime `'_` | LL | fn elided3(x: &i32) -> Box { Box::new(x) } | ^^^^ -error: cannot infer an appropriate lifetime +error[E0758]: cannot infer an appropriate lifetime --> $DIR/must_outlive_least_region_or_bound.rs:21:59 | LL | fn explicit3<'a>(x: &'a i32) -> Box { Box::new(x) } - | ------- ^ ...is captured here with a `'static` requirement + | ------- ^ ...is captured here requiring it to live as long as `'static` | | | this data with lifetime `'a`... | @@ -131,15 +131,15 @@ help: to permit non-static references in a trait object value, you can add an ex LL | fn explicit3<'a>(x: &'a i32) -> Box { Box::new(x) } | ^^^^ -error: cannot infer an appropriate lifetime +error[E0758]: cannot infer an appropriate lifetime --> $DIR/must_outlive_least_region_or_bound.rs:24:60 | LL | fn elided4(x: &i32) -> Box { Box::new(x) } - | ---- ^ ...is captured here with a `'static` requirement + | ---- ^ ...is captured here requiring it to live as long as `'static` | | - | this data with the anonymous lifetime `'_`... + | this data with an anonymous lifetime `'_`... | -help: consider changing the trait object's explicit `'static` bound to the anonymous lifetime `'_` +help: consider changing the trait object's explicit `'static` bound to an anonymous lifetime `'_` | LL | fn elided4(x: &i32) -> Box { Box::new(x) } | ^^ @@ -148,11 +148,11 @@ help: alternatively, set an explicit `'static` lifetime in this parameter LL | fn elided4(x: &'static i32) -> Box { Box::new(x) } | ^^^^^^^^^^^^ -error: cannot infer an appropriate lifetime +error[E0758]: cannot infer an appropriate lifetime --> $DIR/must_outlive_least_region_or_bound.rs:27:69 | LL | fn explicit4<'a>(x: &'a i32) -> Box { Box::new(x) } - | ------- this data with lifetime `'a`... ^ ...is captured here with a `'static` requirement + | ------- this data with lifetime `'a`... ^ ...is captured here requiring it to live as long as `'static` | help: consider changing the trait object's explicit `'static` bound to lifetime `'a` | diff --git a/src/test/ui/impl-trait/static-return-lifetime-infered.stderr b/src/test/ui/impl-trait/static-return-lifetime-infered.stderr index 67d4f60dff6f..a48580ee2d2f 100644 --- a/src/test/ui/impl-trait/static-return-lifetime-infered.stderr +++ b/src/test/ui/impl-trait/static-return-lifetime-infered.stderr @@ -1,25 +1,25 @@ -error: cannot infer an appropriate lifetime +error[E0758]: cannot infer an appropriate lifetime --> $DIR/static-return-lifetime-infered.rs:7:16 | LL | fn iter_values_anon(&self) -> impl Iterator { - | ----- ----------------------- ...is required to be `'static` by this... + | ----- ----------------------- ...is required to live as long as `'static` by this... | | - | this data with the anonymous lifetime `'_`... + | this data with an anonymous lifetime `'_`... LL | self.x.iter().map(|a| a.0) | ------ ^^^^ | | | ...and is captured here | -help: to permit non-static references in an `impl Trait` value, you can add an explicit bound for the anonymous lifetime `'_` +help: to permit non-static references in an `impl Trait` value, you can add an explicit bound for an anonymous lifetime `'_` | LL | fn iter_values_anon(&self) -> impl Iterator + '_ { | ^^^^ -error: cannot infer an appropriate lifetime +error[E0758]: cannot infer an appropriate lifetime --> $DIR/static-return-lifetime-infered.rs:11:16 | LL | fn iter_values<'a>(&'a self) -> impl Iterator { - | -------- ----------------------- ...is required to be `'static` by this... + | -------- ----------------------- ...is required to live as long as `'static` by this... | | | this data with lifetime `'a`... LL | self.x.iter().map(|a| a.0) diff --git a/src/test/ui/issues/issue-16922.stderr b/src/test/ui/issues/issue-16922.stderr index a254343cd1bb..53fd658800a7 100644 --- a/src/test/ui/issues/issue-16922.stderr +++ b/src/test/ui/issues/issue-16922.stderr @@ -1,12 +1,12 @@ -error: cannot infer an appropriate lifetime +error[E0758]: cannot infer an appropriate lifetime --> $DIR/issue-16922.rs:4:14 | LL | fn foo(value: &T) -> Box { - | -- this data with the anonymous lifetime `'_`... + | -- this data with an anonymous lifetime `'_`... LL | Box::new(value) as Box - | ^^^^^ ...is captured here with a `'static` requirement + | ^^^^^ ...is captured here requiring it to live as long as `'static` | -help: to permit non-static references in a trait object value, you can add an explicit bound for the anonymous lifetime `'_` +help: to permit non-static references in a trait object value, you can add an explicit bound for an anonymous lifetime `'_` | LL | fn foo(value: &T) -> Box { | ^^^^ diff --git a/src/test/ui/object-lifetime/object-lifetime-default-from-box-error.stderr b/src/test/ui/object-lifetime/object-lifetime-default-from-box-error.stderr index 6edef8086b93..04a06104faf9 100644 --- a/src/test/ui/object-lifetime/object-lifetime-default-from-box-error.stderr +++ b/src/test/ui/object-lifetime/object-lifetime-default-from-box-error.stderr @@ -1,13 +1,13 @@ -error: cannot infer an appropriate lifetime +error[E0758]: cannot infer an appropriate lifetime --> $DIR/object-lifetime-default-from-box-error.rs:18:5 | LL | fn load(ss: &mut SomeStruct) -> Box { - | --------------- this data with the anonymous lifetime `'_`... + | --------------- this data with an anonymous lifetime `'_`... ... LL | ss.r - | ^^^^ ...is captured and required to be `'static` here + | ^^^^ ...is captured and required live as long as `'static` here | -help: to permit non-static references in a trait object value, you can add an explicit bound for the anonymous lifetime `'_` +help: to permit non-static references in a trait object value, you can add an explicit bound for an anonymous lifetime `'_` | LL | fn load(ss: &mut SomeStruct) -> Box { | ^^^^ diff --git a/src/test/ui/regions/region-object-lifetime-in-coercion.stderr b/src/test/ui/regions/region-object-lifetime-in-coercion.stderr index 97d1f3579fcd..34cf131319a1 100644 --- a/src/test/ui/regions/region-object-lifetime-in-coercion.stderr +++ b/src/test/ui/regions/region-object-lifetime-in-coercion.stderr @@ -1,12 +1,12 @@ -error: cannot infer an appropriate lifetime +error[E0758]: cannot infer an appropriate lifetime --> $DIR/region-object-lifetime-in-coercion.rs:8:46 | LL | fn a(v: &[u8]) -> Box { - | ----- this data with the anonymous lifetime `'_`... + | ----- this data with an anonymous lifetime `'_`... LL | let x: Box = Box::new(v); - | ^ ...is captured here with a `'static` requirement + | ^ ...is captured here requiring it to live as long as `'static` | -help: consider changing the trait object's explicit `'static` bound to the anonymous lifetime `'_` +help: consider changing the trait object's explicit `'static` bound to an anonymous lifetime `'_` | LL | fn a(v: &[u8]) -> Box { | ^^ @@ -15,15 +15,15 @@ help: alternatively, set an explicit `'static` lifetime in this parameter LL | fn a(v: &'static [u8]) -> Box { | ^^^^^^^^^^^^^ -error: cannot infer an appropriate lifetime +error[E0758]: cannot infer an appropriate lifetime --> $DIR/region-object-lifetime-in-coercion.rs:13:14 | LL | fn b(v: &[u8]) -> Box { - | ----- this data with the anonymous lifetime `'_`... + | ----- this data with an anonymous lifetime `'_`... LL | Box::new(v) - | ^ ...is captured here with a `'static` requirement + | ^ ...is captured here requiring it to live as long as `'static` | -help: consider changing the trait object's explicit `'static` bound to the anonymous lifetime `'_` +help: consider changing the trait object's explicit `'static` bound to an anonymous lifetime `'_` | LL | fn b(v: &[u8]) -> Box { | ^^ @@ -32,16 +32,16 @@ help: alternatively, set an explicit `'static` lifetime in this parameter LL | fn b(v: &'static [u8]) -> Box { | ^^^^^^^^^^^^^ -error: cannot infer an appropriate lifetime +error[E0758]: cannot infer an appropriate lifetime --> $DIR/region-object-lifetime-in-coercion.rs:19:14 | LL | fn c(v: &[u8]) -> Box { - | ----- this data with the anonymous lifetime `'_`... + | ----- this data with an anonymous lifetime `'_`... ... LL | Box::new(v) - | ^ ...is captured here with a `'static` requirement + | ^ ...is captured here requiring it to live as long as `'static` | -help: to permit non-static references in a trait object value, you can add an explicit bound for the anonymous lifetime `'_` +help: to permit non-static references in a trait object value, you can add an explicit bound for an anonymous lifetime `'_` | LL | fn c(v: &[u8]) -> Box { | ^^^^ diff --git a/src/test/ui/regions/regions-close-object-into-object-2.stderr b/src/test/ui/regions/regions-close-object-into-object-2.stderr index 3d707f2d9994..be47ef589af8 100644 --- a/src/test/ui/regions/regions-close-object-into-object-2.stderr +++ b/src/test/ui/regions/regions-close-object-into-object-2.stderr @@ -1,10 +1,10 @@ -error: cannot infer an appropriate lifetime +error[E0758]: cannot infer an appropriate lifetime --> $DIR/regions-close-object-into-object-2.rs:10:11 | LL | fn g<'a, T: 'static>(v: Box + 'a>) -> Box { | ------------------ this data with lifetime `'a`... LL | box B(&*v) as Box - | ^^^ ...is captured here with a `'static` requirement + | ^^^ ...is captured here requiring it to live as long as `'static` | help: consider changing the trait object's explicit `'static` bound to lifetime `'a` | diff --git a/src/test/ui/regions/regions-close-object-into-object-4.stderr b/src/test/ui/regions/regions-close-object-into-object-4.stderr index 70282c8cbdb3..1b099c7d8bdf 100644 --- a/src/test/ui/regions/regions-close-object-into-object-4.stderr +++ b/src/test/ui/regions/regions-close-object-into-object-4.stderr @@ -1,10 +1,10 @@ -error: cannot infer an appropriate lifetime +error[E0758]: cannot infer an appropriate lifetime --> $DIR/regions-close-object-into-object-4.rs:10:11 | LL | fn i<'a, T, U>(v: Box+'a>) -> Box { | ---------------- this data with lifetime `'a`... LL | box B(&*v) as Box - | ^^^ ...is captured here with a `'static` requirement + | ^^^ ...is captured here requiring it to live as long as `'static` | help: consider changing the trait object's explicit `'static` bound to lifetime `'a` | diff --git a/src/test/ui/regions/regions-proc-bound-capture.stderr b/src/test/ui/regions/regions-proc-bound-capture.stderr index 8f93fad7fe9d..e8baf44bd10a 100644 --- a/src/test/ui/regions/regions-proc-bound-capture.stderr +++ b/src/test/ui/regions/regions-proc-bound-capture.stderr @@ -1,13 +1,13 @@ -error: cannot infer an appropriate lifetime +error[E0758]: cannot infer an appropriate lifetime --> $DIR/regions-proc-bound-capture.rs:9:14 | LL | fn static_proc(x: &isize) -> Box (isize) + 'static> { - | ------ this data with the anonymous lifetime `'_`... + | ------ this data with an anonymous lifetime `'_`... LL | // This is illegal, because the region bound on `proc` is 'static. LL | Box::new(move || { *x }) - | ^^^^^^^^^^^^^^ ...is captured here with a `'static` requirement + | ^^^^^^^^^^^^^^ ...is captured here requiring it to live as long as `'static` | -help: consider changing the trait object's explicit `'static` bound to the anonymous lifetime `'_` +help: consider changing the trait object's explicit `'static` bound to an anonymous lifetime `'_` | LL | fn static_proc(x: &isize) -> Box (isize) + '_> { | ^^ diff --git a/src/test/ui/self/arbitrary_self_types_pin_lifetime_impl_trait-async.stderr b/src/test/ui/self/arbitrary_self_types_pin_lifetime_impl_trait-async.stderr index 5520341b5b1c..92e1473a5da7 100644 --- a/src/test/ui/self/arbitrary_self_types_pin_lifetime_impl_trait-async.stderr +++ b/src/test/ui/self/arbitrary_self_types_pin_lifetime_impl_trait-async.stderr @@ -1,10 +1,10 @@ -error: cannot infer an appropriate lifetime +error[E0758]: cannot infer an appropriate lifetime --> $DIR/arbitrary_self_types_pin_lifetime_impl_trait-async.rs:8:16 | LL | async fn f(self: Pin<&Self>) -> impl Clone { self } - | ^^^^ ---------- ---------- ...and required to be `'static` by this + | ^^^^ ---------- ---------- ...and required to live as long as `'static` by this | | | - | | this data with the anonymous lifetime `'_`... + | | this data with an anonymous lifetime `'_`... | ...is captured here... error: aborting due to previous error diff --git a/src/test/ui/self/arbitrary_self_types_pin_lifetime_impl_trait.stderr b/src/test/ui/self/arbitrary_self_types_pin_lifetime_impl_trait.stderr index 5374929f3a45..6721d41bb73c 100644 --- a/src/test/ui/self/arbitrary_self_types_pin_lifetime_impl_trait.stderr +++ b/src/test/ui/self/arbitrary_self_types_pin_lifetime_impl_trait.stderr @@ -1,13 +1,13 @@ -error: cannot infer an appropriate lifetime +error[E0758]: cannot infer an appropriate lifetime --> $DIR/arbitrary_self_types_pin_lifetime_impl_trait.rs:6:44 | LL | fn f(self: Pin<&Self>) -> impl Clone { self } | ---------- ---------- ^^^^ ...and is captured here | | | - | | ...is required to be `'static` by this... - | this data with the anonymous lifetime `'_`... + | | ...is required to live as long as `'static` by this... + | this data with an anonymous lifetime `'_`... | -help: to permit non-static references in an `impl Trait` value, you can add an explicit bound for the anonymous lifetime `'_` +help: to permit non-static references in an `impl Trait` value, you can add an explicit bound for an anonymous lifetime `'_` | LL | fn f(self: Pin<&Self>) -> impl Clone + '_ { self } | ^^^^ diff --git a/src/test/ui/suggestions/lifetimes/missing-lifetimes-in-signature.stderr b/src/test/ui/suggestions/lifetimes/missing-lifetimes-in-signature.stderr index 471f3cd14aa3..ba56255af5b0 100644 --- a/src/test/ui/suggestions/lifetimes/missing-lifetimes-in-signature.stderr +++ b/src/test/ui/suggestions/lifetimes/missing-lifetimes-in-signature.stderr @@ -6,20 +6,20 @@ LL | fn baz(g: G, dest: &mut T) -> impl FnOnce() + '_ | | | help: consider introducing lifetime `'a` here: `'a,` -error: cannot infer an appropriate lifetime +error[E0758]: cannot infer an appropriate lifetime --> $DIR/missing-lifetimes-in-signature.rs:19:5 | LL | fn foo(g: G, dest: &mut T) -> impl FnOnce() - | ------ ------------- ...is required to be `'static` by this... + | ------ ------------- ...is required to live as long as `'static` by this... | | - | this data with the anonymous lifetime `'_`... + | this data with an anonymous lifetime `'_`... ... LL | / move || { LL | | *dest = g.get(); LL | | } | |_____^ ...and is captured here | -help: to permit non-static references in an `impl Trait` value, you can add an explicit bound for the anonymous lifetime `'_` +help: to permit non-static references in an `impl Trait` value, you can add an explicit bound for an anonymous lifetime `'_` | LL | fn foo(g: G, dest: &mut T) -> impl FnOnce() + '_ | ^^^^ diff --git a/src/test/ui/underscore-lifetime/dyn-trait-underscore.stderr b/src/test/ui/underscore-lifetime/dyn-trait-underscore.stderr index 5fd03f9770e5..20d3640d4118 100644 --- a/src/test/ui/underscore-lifetime/dyn-trait-underscore.stderr +++ b/src/test/ui/underscore-lifetime/dyn-trait-underscore.stderr @@ -1,13 +1,13 @@ -error: cannot infer an appropriate lifetime +error[E0758]: cannot infer an appropriate lifetime --> $DIR/dyn-trait-underscore.rs:8:20 | LL | fn a(items: &[T]) -> Box> { - | ---- this data with the anonymous lifetime `'_`... + | ---- this data with an anonymous lifetime `'_`... LL | // ^^^^^^^^^^^^^^^^^^^^^ bound *here* defaults to `'static` LL | Box::new(items.iter()) - | ---------------^^^^--- ...is captured and required to be `'static` here + | ---------------^^^^--- ...is captured and required live as long as `'static` here | -help: to permit non-static references in a trait object value, you can add an explicit bound for the anonymous lifetime `'_` +help: to permit non-static references in a trait object value, you can add an explicit bound for an anonymous lifetime `'_` | LL | fn a(items: &[T]) -> Box + '_> { | ^^^^ From 31ea589a06e336a3d596e20e3a3f4327c8356aa4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Mon, 1 Jun 2020 16:15:10 -0700 Subject: [PATCH 055/123] review comments: wording --- .../nice_region_error/static_impl_trait.rs | 58 ++++++++++--------- .../ui/async-await/issues/issue-62097.stderr | 2 +- .../must_outlive_least_region_or_bound.stderr | 46 +++++++-------- .../static-return-lifetime-infered.stderr | 8 +-- src/test/ui/issues/issue-16922.stderr | 4 +- ...ect-lifetime-default-from-box-error.stderr | 2 +- .../region-object-lifetime-in-coercion.stderr | 16 ++--- .../regions-close-object-into-object-2.stderr | 6 +- .../regions-close-object-into-object-4.stderr | 6 +- .../regions/regions-proc-bound-capture.stderr | 6 +- ...types_pin_lifetime_impl_trait-async.stderr | 2 +- ..._self_types_pin_lifetime_impl_trait.stderr | 4 +- .../missing-lifetimes-in-signature.stderr | 4 +- .../dyn-trait-underscore.stderr | 2 +- 14 files changed, 85 insertions(+), 81 deletions(-) diff --git a/src/librustc_infer/infer/error_reporting/nice_region_error/static_impl_trait.rs b/src/librustc_infer/infer/error_reporting/nice_region_error/static_impl_trait.rs index 4e16e8c2c571..86f310eb71d7 100644 --- a/src/librustc_infer/infer/error_reporting/nice_region_error/static_impl_trait.rs +++ b/src/librustc_infer/infer/error_reporting/nice_region_error/static_impl_trait.rs @@ -75,18 +75,18 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { // | ---- ^ err.span_label( sup_origin.span(), - "...is captured here requiring it to live as long as `'static`", + "...is captured here, requiring it to live as long as `'static`", ); } else if sup_origin.span() <= return_sp { err.span_label(sup_origin.span(), "...is captured here..."); err.span_label( return_sp, - "...and required to live as long as `'static` by this", + "...and is required to live as long as `'static` here", ); } else { err.span_label( return_sp, - "...is required to live as long as `'static` by this...", + "...is required to live as long as `'static` here...", ); err.span_label(sup_origin.span(), "...and is captured here"); } @@ -101,6 +101,20 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { // explicit non-desugar'able return. if fn_return.span.desugaring_kind().is_none() { // FIXME: account for the need of parens in `&(dyn Trait + '_)` + + let consider = "consider changing the"; + let declare = "to declare that the"; + let arg = match param_info.param.pat.simple_ident() { + Some(simple_ident) => format!("argument `{}`", simple_ident), + None => "the argument".to_string(), + }; + let explicit = + format!("you can add an explicit `{}` lifetime bound", lifetime_name); + let explicit_static = format!("explicit `'static` bound to {}", arg); + let captures = format!("captures data from {}", arg); + let add_static_bound = + "alternatively, add an explicit `'static` bound to this reference"; + let plus_lt = format!(" + {}", lifetime_name); match fn_return.kind { TyKind::Def(item_id, _) => { let item = self.tcx().hir().item(item_id.id); @@ -126,18 +140,13 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { { err.span_suggestion_verbose( span, - &format!( - "consider changing the `impl Trait`'s explicit \ - `'static` bound to {}", - lifetime, - ), + &format!("{} `impl Trait`'s {}", consider, explicit_static), lifetime_name, Applicability::MaybeIncorrect, ); err.span_suggestion_verbose( param_info.param_ty_span, - "alternatively, set an explicit `'static` lifetime to \ - this parameter", + add_static_bound, param_info.param_ty.to_string(), Applicability::MaybeIncorrect, ); @@ -145,11 +154,12 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { err.span_suggestion_verbose( fn_return.span.shrink_to_hi(), &format!( - "to permit non-static references in an `impl Trait` \ - value, you can add an explicit bound for {}", - lifetime, + "{declare} `impl Trait` {captures}, {explicit}", + declare = declare, + captures = captures, + explicit = explicit, ), - format!(" + {}", lifetime_name), + plus_lt, Applicability::MaybeIncorrect, ); }; @@ -159,31 +169,25 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { err.span_suggestion_verbose( fn_return.span.shrink_to_hi(), &format!( - "to permit non-static references in a trait object \ - value, you can add an explicit bound for {}", - lifetime, + "{declare} trait object {captures}, {explicit}", + declare = declare, + captures = captures, + explicit = explicit, ), - format!(" + {}", lifetime_name), + plus_lt, Applicability::MaybeIncorrect, ); } _ => { err.span_suggestion_verbose( lt.span, - &format!( - "consider changing the trait object's explicit \ - `'static` bound to {}", - lifetime, - ), + &format!("{} trait object's {}", consider, explicit_static), lifetime_name, Applicability::MaybeIncorrect, ); err.span_suggestion_verbose( param_info.param_ty_span, - &format!( - "alternatively, set an explicit `'static` lifetime \ - in this parameter", - ), + add_static_bound, param_info.param_ty.to_string(), Applicability::MaybeIncorrect, ); diff --git a/src/test/ui/async-await/issues/issue-62097.stderr b/src/test/ui/async-await/issues/issue-62097.stderr index 558d89f92894..e9f155c6ced3 100644 --- a/src/test/ui/async-await/issues/issue-62097.stderr +++ b/src/test/ui/async-await/issues/issue-62097.stderr @@ -7,7 +7,7 @@ LL | pub async fn run_dummy_fn(&self) { | this data with an anonymous lifetime `'_`... | ...is captured here... LL | foo(|| self.bar()).await; - | --- ...and required to live as long as `'static` by this + | --- ...and is required to live as long as `'static` here error: aborting due to previous error diff --git a/src/test/ui/impl-trait/must_outlive_least_region_or_bound.stderr b/src/test/ui/impl-trait/must_outlive_least_region_or_bound.stderr index eff56ddc440d..a9fa0e93fed6 100644 --- a/src/test/ui/impl-trait/must_outlive_least_region_or_bound.stderr +++ b/src/test/ui/impl-trait/must_outlive_least_region_or_bound.stderr @@ -4,10 +4,10 @@ error[E0758]: cannot infer an appropriate lifetime LL | fn elided(x: &i32) -> impl Copy { x } | ---- --------- ^ ...and is captured here | | | - | | ...is required to live as long as `'static` by this... + | | ...is required to live as long as `'static` here... | this data with an anonymous lifetime `'_`... | -help: to permit non-static references in an `impl Trait` value, you can add an explicit bound for an anonymous lifetime `'_` +help: to declare that the `impl Trait` captures data from argument `x`, you can add an explicit `'_` lifetime bound | LL | fn elided(x: &i32) -> impl Copy + '_ { x } | ^^^^ @@ -18,10 +18,10 @@ error[E0758]: cannot infer an appropriate lifetime LL | fn explicit<'a>(x: &'a i32) -> impl Copy { x } | ------- --------- ^ ...and is captured here | | | - | | ...is required to live as long as `'static` by this... + | | ...is required to live as long as `'static` here... | this data with lifetime `'a`... | -help: to permit non-static references in an `impl Trait` value, you can add an explicit bound for lifetime `'a` +help: to declare that the `impl Trait` captures data from argument `x`, you can add an explicit `'a` lifetime bound | LL | fn explicit<'a>(x: &'a i32) -> impl Copy + 'a { x } | ^^^^ @@ -32,14 +32,14 @@ error[E0758]: cannot infer an appropriate lifetime LL | fn elided2(x: &i32) -> impl Copy + 'static { x } | ---- ------------------- ^ ...and is captured here | | | - | | ...is required to live as long as `'static` by this... + | | ...is required to live as long as `'static` here... | this data with an anonymous lifetime `'_`... | -help: consider changing the `impl Trait`'s explicit `'static` bound to an anonymous lifetime `'_` +help: consider changing the `impl Trait`'s explicit `'static` bound to argument `x` | LL | fn elided2(x: &i32) -> impl Copy + '_ { x } | ^^ -help: alternatively, set an explicit `'static` lifetime to this parameter +help: alternatively, add an explicit `'static` bound to this reference | LL | fn elided2(x: &'static i32) -> impl Copy + 'static { x } | ^^^^^^^^^^^^ @@ -50,14 +50,14 @@ error[E0758]: cannot infer an appropriate lifetime LL | fn explicit2<'a>(x: &'a i32) -> impl Copy + 'static { x } | ------- ------------------- ^ ...and is captured here | | | - | | ...is required to live as long as `'static` by this... + | | ...is required to live as long as `'static` here... | this data with lifetime `'a`... | -help: consider changing the `impl Trait`'s explicit `'static` bound to lifetime `'a` +help: consider changing the `impl Trait`'s explicit `'static` bound to argument `x` | LL | fn explicit2<'a>(x: &'a i32) -> impl Copy + 'a { x } | ^^ -help: alternatively, set an explicit `'static` lifetime to this parameter +help: alternatively, add an explicit `'static` bound to this reference | LL | fn explicit2<'a>(x: &'static i32) -> impl Copy + 'static { x } | ^^^^^^^^^^^^ @@ -76,14 +76,14 @@ error[E0758]: cannot infer an appropriate lifetime LL | fn with_bound<'a>(x: &'a i32) -> impl LifetimeTrait<'a> + 'static { x } | ------- -------------------------------- ^ ...and is captured here | | | - | | ...is required to live as long as `'static` by this... + | | ...is required to live as long as `'static` here... | this data with lifetime `'a`... | -help: consider changing the `impl Trait`'s explicit `'static` bound to lifetime `'a` +help: consider changing the `impl Trait`'s explicit `'static` bound to argument `x` | LL | fn with_bound<'a>(x: &'a i32) -> impl LifetimeTrait<'a> + 'a { x } | ^^ -help: alternatively, set an explicit `'static` lifetime to this parameter +help: alternatively, add an explicit `'static` bound to this reference | LL | fn with_bound<'a>(x: &'static i32) -> impl LifetimeTrait<'a> + 'static { x } | ^^^^^^^^^^^^ @@ -109,11 +109,11 @@ error[E0758]: cannot infer an appropriate lifetime --> $DIR/must_outlive_least_region_or_bound.rs:18:50 | LL | fn elided3(x: &i32) -> Box { Box::new(x) } - | ---- ^ ...is captured here requiring it to live as long as `'static` + | ---- ^ ...is captured here, requiring it to live as long as `'static` | | | this data with an anonymous lifetime `'_`... | -help: to permit non-static references in a trait object value, you can add an explicit bound for an anonymous lifetime `'_` +help: to declare that the trait object captures data from argument `x`, you can add an explicit `'_` lifetime bound | LL | fn elided3(x: &i32) -> Box { Box::new(x) } | ^^^^ @@ -122,11 +122,11 @@ error[E0758]: cannot infer an appropriate lifetime --> $DIR/must_outlive_least_region_or_bound.rs:21:59 | LL | fn explicit3<'a>(x: &'a i32) -> Box { Box::new(x) } - | ------- ^ ...is captured here requiring it to live as long as `'static` + | ------- ^ ...is captured here, requiring it to live as long as `'static` | | | this data with lifetime `'a`... | -help: to permit non-static references in a trait object value, you can add an explicit bound for lifetime `'a` +help: to declare that the trait object captures data from argument `x`, you can add an explicit `'a` lifetime bound | LL | fn explicit3<'a>(x: &'a i32) -> Box { Box::new(x) } | ^^^^ @@ -135,15 +135,15 @@ error[E0758]: cannot infer an appropriate lifetime --> $DIR/must_outlive_least_region_or_bound.rs:24:60 | LL | fn elided4(x: &i32) -> Box { Box::new(x) } - | ---- ^ ...is captured here requiring it to live as long as `'static` + | ---- ^ ...is captured here, requiring it to live as long as `'static` | | | this data with an anonymous lifetime `'_`... | -help: consider changing the trait object's explicit `'static` bound to an anonymous lifetime `'_` +help: consider changing the trait object's explicit `'static` bound to argument `x` | LL | fn elided4(x: &i32) -> Box { Box::new(x) } | ^^ -help: alternatively, set an explicit `'static` lifetime in this parameter +help: alternatively, add an explicit `'static` bound to this reference | LL | fn elided4(x: &'static i32) -> Box { Box::new(x) } | ^^^^^^^^^^^^ @@ -152,13 +152,13 @@ error[E0758]: cannot infer an appropriate lifetime --> $DIR/must_outlive_least_region_or_bound.rs:27:69 | LL | fn explicit4<'a>(x: &'a i32) -> Box { Box::new(x) } - | ------- this data with lifetime `'a`... ^ ...is captured here requiring it to live as long as `'static` + | ------- this data with lifetime `'a`... ^ ...is captured here, requiring it to live as long as `'static` | -help: consider changing the trait object's explicit `'static` bound to lifetime `'a` +help: consider changing the trait object's explicit `'static` bound to argument `x` | LL | fn explicit4<'a>(x: &'a i32) -> Box { Box::new(x) } | ^^ -help: alternatively, set an explicit `'static` lifetime in this parameter +help: alternatively, add an explicit `'static` bound to this reference | LL | fn explicit4<'a>(x: &'static i32) -> Box { Box::new(x) } | ^^^^^^^^^^^^ diff --git a/src/test/ui/impl-trait/static-return-lifetime-infered.stderr b/src/test/ui/impl-trait/static-return-lifetime-infered.stderr index a48580ee2d2f..6681eaa909ee 100644 --- a/src/test/ui/impl-trait/static-return-lifetime-infered.stderr +++ b/src/test/ui/impl-trait/static-return-lifetime-infered.stderr @@ -2,7 +2,7 @@ error[E0758]: cannot infer an appropriate lifetime --> $DIR/static-return-lifetime-infered.rs:7:16 | LL | fn iter_values_anon(&self) -> impl Iterator { - | ----- ----------------------- ...is required to live as long as `'static` by this... + | ----- ----------------------- ...is required to live as long as `'static` here... | | | this data with an anonymous lifetime `'_`... LL | self.x.iter().map(|a| a.0) @@ -10,7 +10,7 @@ LL | self.x.iter().map(|a| a.0) | | | ...and is captured here | -help: to permit non-static references in an `impl Trait` value, you can add an explicit bound for an anonymous lifetime `'_` +help: to declare that the `impl Trait` captures data from argument `self`, you can add an explicit `'_` lifetime bound | LL | fn iter_values_anon(&self) -> impl Iterator + '_ { | ^^^^ @@ -19,7 +19,7 @@ error[E0758]: cannot infer an appropriate lifetime --> $DIR/static-return-lifetime-infered.rs:11:16 | LL | fn iter_values<'a>(&'a self) -> impl Iterator { - | -------- ----------------------- ...is required to live as long as `'static` by this... + | -------- ----------------------- ...is required to live as long as `'static` here... | | | this data with lifetime `'a`... LL | self.x.iter().map(|a| a.0) @@ -27,7 +27,7 @@ LL | self.x.iter().map(|a| a.0) | | | ...and is captured here | -help: to permit non-static references in an `impl Trait` value, you can add an explicit bound for lifetime `'a` +help: to declare that the `impl Trait` captures data from argument `self`, you can add an explicit `'a` lifetime bound | LL | fn iter_values<'a>(&'a self) -> impl Iterator + 'a { | ^^^^ diff --git a/src/test/ui/issues/issue-16922.stderr b/src/test/ui/issues/issue-16922.stderr index 53fd658800a7..95f46bd7f3eb 100644 --- a/src/test/ui/issues/issue-16922.stderr +++ b/src/test/ui/issues/issue-16922.stderr @@ -4,9 +4,9 @@ error[E0758]: cannot infer an appropriate lifetime LL | fn foo(value: &T) -> Box { | -- this data with an anonymous lifetime `'_`... LL | Box::new(value) as Box - | ^^^^^ ...is captured here requiring it to live as long as `'static` + | ^^^^^ ...is captured here, requiring it to live as long as `'static` | -help: to permit non-static references in a trait object value, you can add an explicit bound for an anonymous lifetime `'_` +help: to declare that the trait object captures data from argument `value`, you can add an explicit `'_` lifetime bound | LL | fn foo(value: &T) -> Box { | ^^^^ diff --git a/src/test/ui/object-lifetime/object-lifetime-default-from-box-error.stderr b/src/test/ui/object-lifetime/object-lifetime-default-from-box-error.stderr index 04a06104faf9..e585db262f2d 100644 --- a/src/test/ui/object-lifetime/object-lifetime-default-from-box-error.stderr +++ b/src/test/ui/object-lifetime/object-lifetime-default-from-box-error.stderr @@ -7,7 +7,7 @@ LL | fn load(ss: &mut SomeStruct) -> Box { LL | ss.r | ^^^^ ...is captured and required live as long as `'static` here | -help: to permit non-static references in a trait object value, you can add an explicit bound for an anonymous lifetime `'_` +help: to declare that the trait object captures data from argument `ss`, you can add an explicit `'_` lifetime bound | LL | fn load(ss: &mut SomeStruct) -> Box { | ^^^^ diff --git a/src/test/ui/regions/region-object-lifetime-in-coercion.stderr b/src/test/ui/regions/region-object-lifetime-in-coercion.stderr index 34cf131319a1..8d048d90cb34 100644 --- a/src/test/ui/regions/region-object-lifetime-in-coercion.stderr +++ b/src/test/ui/regions/region-object-lifetime-in-coercion.stderr @@ -4,13 +4,13 @@ error[E0758]: cannot infer an appropriate lifetime LL | fn a(v: &[u8]) -> Box { | ----- this data with an anonymous lifetime `'_`... LL | let x: Box = Box::new(v); - | ^ ...is captured here requiring it to live as long as `'static` + | ^ ...is captured here, requiring it to live as long as `'static` | -help: consider changing the trait object's explicit `'static` bound to an anonymous lifetime `'_` +help: consider changing the trait object's explicit `'static` bound to argument `v` | LL | fn a(v: &[u8]) -> Box { | ^^ -help: alternatively, set an explicit `'static` lifetime in this parameter +help: alternatively, add an explicit `'static` bound to this reference | LL | fn a(v: &'static [u8]) -> Box { | ^^^^^^^^^^^^^ @@ -21,13 +21,13 @@ error[E0758]: cannot infer an appropriate lifetime LL | fn b(v: &[u8]) -> Box { | ----- this data with an anonymous lifetime `'_`... LL | Box::new(v) - | ^ ...is captured here requiring it to live as long as `'static` + | ^ ...is captured here, requiring it to live as long as `'static` | -help: consider changing the trait object's explicit `'static` bound to an anonymous lifetime `'_` +help: consider changing the trait object's explicit `'static` bound to argument `v` | LL | fn b(v: &[u8]) -> Box { | ^^ -help: alternatively, set an explicit `'static` lifetime in this parameter +help: alternatively, add an explicit `'static` bound to this reference | LL | fn b(v: &'static [u8]) -> Box { | ^^^^^^^^^^^^^ @@ -39,9 +39,9 @@ LL | fn c(v: &[u8]) -> Box { | ----- this data with an anonymous lifetime `'_`... ... LL | Box::new(v) - | ^ ...is captured here requiring it to live as long as `'static` + | ^ ...is captured here, requiring it to live as long as `'static` | -help: to permit non-static references in a trait object value, you can add an explicit bound for an anonymous lifetime `'_` +help: to declare that the trait object captures data from argument `v`, you can add an explicit `'_` lifetime bound | LL | fn c(v: &[u8]) -> Box { | ^^^^ diff --git a/src/test/ui/regions/regions-close-object-into-object-2.stderr b/src/test/ui/regions/regions-close-object-into-object-2.stderr index be47ef589af8..5dfe384112b2 100644 --- a/src/test/ui/regions/regions-close-object-into-object-2.stderr +++ b/src/test/ui/regions/regions-close-object-into-object-2.stderr @@ -4,13 +4,13 @@ error[E0758]: cannot infer an appropriate lifetime LL | fn g<'a, T: 'static>(v: Box + 'a>) -> Box { | ------------------ this data with lifetime `'a`... LL | box B(&*v) as Box - | ^^^ ...is captured here requiring it to live as long as `'static` + | ^^^ ...is captured here, requiring it to live as long as `'static` | -help: consider changing the trait object's explicit `'static` bound to lifetime `'a` +help: consider changing the trait object's explicit `'static` bound to argument `v` | LL | fn g<'a, T: 'static>(v: Box + 'a>) -> Box { | ^^ -help: alternatively, set an explicit `'static` lifetime in this parameter +help: alternatively, add an explicit `'static` bound to this reference | LL | fn g<'a, T: 'static>(v: std::boxed::Box<(dyn A + 'static)>) -> Box { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/src/test/ui/regions/regions-close-object-into-object-4.stderr b/src/test/ui/regions/regions-close-object-into-object-4.stderr index 1b099c7d8bdf..4d23118ba06f 100644 --- a/src/test/ui/regions/regions-close-object-into-object-4.stderr +++ b/src/test/ui/regions/regions-close-object-into-object-4.stderr @@ -4,13 +4,13 @@ error[E0758]: cannot infer an appropriate lifetime LL | fn i<'a, T, U>(v: Box+'a>) -> Box { | ---------------- this data with lifetime `'a`... LL | box B(&*v) as Box - | ^^^ ...is captured here requiring it to live as long as `'static` + | ^^^ ...is captured here, requiring it to live as long as `'static` | -help: consider changing the trait object's explicit `'static` bound to lifetime `'a` +help: consider changing the trait object's explicit `'static` bound to argument `v` | LL | fn i<'a, T, U>(v: Box+'a>) -> Box { | ^^ -help: alternatively, set an explicit `'static` lifetime in this parameter +help: alternatively, add an explicit `'static` bound to this reference | LL | fn i<'a, T, U>(v: std::boxed::Box<(dyn A + 'static)>) -> Box { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/src/test/ui/regions/regions-proc-bound-capture.stderr b/src/test/ui/regions/regions-proc-bound-capture.stderr index e8baf44bd10a..e36f77ec1da9 100644 --- a/src/test/ui/regions/regions-proc-bound-capture.stderr +++ b/src/test/ui/regions/regions-proc-bound-capture.stderr @@ -5,13 +5,13 @@ LL | fn static_proc(x: &isize) -> Box (isize) + 'static> { | ------ this data with an anonymous lifetime `'_`... LL | // This is illegal, because the region bound on `proc` is 'static. LL | Box::new(move || { *x }) - | ^^^^^^^^^^^^^^ ...is captured here requiring it to live as long as `'static` + | ^^^^^^^^^^^^^^ ...is captured here, requiring it to live as long as `'static` | -help: consider changing the trait object's explicit `'static` bound to an anonymous lifetime `'_` +help: consider changing the trait object's explicit `'static` bound to argument `x` | LL | fn static_proc(x: &isize) -> Box (isize) + '_> { | ^^ -help: alternatively, set an explicit `'static` lifetime in this parameter +help: alternatively, add an explicit `'static` bound to this reference | LL | fn static_proc(x: &'static isize) -> Box (isize) + 'static> { | ^^^^^^^^^^^^^^ diff --git a/src/test/ui/self/arbitrary_self_types_pin_lifetime_impl_trait-async.stderr b/src/test/ui/self/arbitrary_self_types_pin_lifetime_impl_trait-async.stderr index 92e1473a5da7..365e38515b12 100644 --- a/src/test/ui/self/arbitrary_self_types_pin_lifetime_impl_trait-async.stderr +++ b/src/test/ui/self/arbitrary_self_types_pin_lifetime_impl_trait-async.stderr @@ -2,7 +2,7 @@ error[E0758]: cannot infer an appropriate lifetime --> $DIR/arbitrary_self_types_pin_lifetime_impl_trait-async.rs:8:16 | LL | async fn f(self: Pin<&Self>) -> impl Clone { self } - | ^^^^ ---------- ---------- ...and required to live as long as `'static` by this + | ^^^^ ---------- ---------- ...and is required to live as long as `'static` here | | | | | this data with an anonymous lifetime `'_`... | ...is captured here... diff --git a/src/test/ui/self/arbitrary_self_types_pin_lifetime_impl_trait.stderr b/src/test/ui/self/arbitrary_self_types_pin_lifetime_impl_trait.stderr index 6721d41bb73c..bd3f3efad82f 100644 --- a/src/test/ui/self/arbitrary_self_types_pin_lifetime_impl_trait.stderr +++ b/src/test/ui/self/arbitrary_self_types_pin_lifetime_impl_trait.stderr @@ -4,10 +4,10 @@ error[E0758]: cannot infer an appropriate lifetime LL | fn f(self: Pin<&Self>) -> impl Clone { self } | ---------- ---------- ^^^^ ...and is captured here | | | - | | ...is required to live as long as `'static` by this... + | | ...is required to live as long as `'static` here... | this data with an anonymous lifetime `'_`... | -help: to permit non-static references in an `impl Trait` value, you can add an explicit bound for an anonymous lifetime `'_` +help: to declare that the `impl Trait` captures data from argument `self`, you can add an explicit `'_` lifetime bound | LL | fn f(self: Pin<&Self>) -> impl Clone + '_ { self } | ^^^^ diff --git a/src/test/ui/suggestions/lifetimes/missing-lifetimes-in-signature.stderr b/src/test/ui/suggestions/lifetimes/missing-lifetimes-in-signature.stderr index ba56255af5b0..d96a5f961bdb 100644 --- a/src/test/ui/suggestions/lifetimes/missing-lifetimes-in-signature.stderr +++ b/src/test/ui/suggestions/lifetimes/missing-lifetimes-in-signature.stderr @@ -10,7 +10,7 @@ error[E0758]: cannot infer an appropriate lifetime --> $DIR/missing-lifetimes-in-signature.rs:19:5 | LL | fn foo(g: G, dest: &mut T) -> impl FnOnce() - | ------ ------------- ...is required to live as long as `'static` by this... + | ------ ------------- ...is required to live as long as `'static` here... | | | this data with an anonymous lifetime `'_`... ... @@ -19,7 +19,7 @@ LL | | *dest = g.get(); LL | | } | |_____^ ...and is captured here | -help: to permit non-static references in an `impl Trait` value, you can add an explicit bound for an anonymous lifetime `'_` +help: to declare that the `impl Trait` captures data from argument `dest`, you can add an explicit `'_` lifetime bound | LL | fn foo(g: G, dest: &mut T) -> impl FnOnce() + '_ | ^^^^ diff --git a/src/test/ui/underscore-lifetime/dyn-trait-underscore.stderr b/src/test/ui/underscore-lifetime/dyn-trait-underscore.stderr index 20d3640d4118..7c649f9c08d6 100644 --- a/src/test/ui/underscore-lifetime/dyn-trait-underscore.stderr +++ b/src/test/ui/underscore-lifetime/dyn-trait-underscore.stderr @@ -7,7 +7,7 @@ LL | // ^^^^^^^^^^^^^^^^^^^^^ bound *here* defaults to LL | Box::new(items.iter()) | ---------------^^^^--- ...is captured and required live as long as `'static` here | -help: to permit non-static references in a trait object value, you can add an explicit bound for an anonymous lifetime `'_` +help: to declare that the trait object captures data from argument `items`, you can add an explicit `'_` lifetime bound | LL | fn a(items: &[T]) -> Box + '_> { | ^^^^ From 10d9bf17671ed1d3099f2b2e25b418931075cda7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Mon, 1 Jun 2020 17:51:12 -0700 Subject: [PATCH 056/123] Use note for requirement source span --- .../nice_region_error/static_impl_trait.rs | 10 +--- .../ui/async-await/issues/issue-62097.stderr | 6 ++- .../must_outlive_least_region_or_bound.stderr | 50 +++++++++++++------ .../static-return-lifetime-infered.stderr | 22 +++++--- ...types_pin_lifetime_impl_trait-async.stderr | 11 ++-- ..._self_types_pin_lifetime_impl_trait.stderr | 10 ++-- .../missing-lifetimes-in-signature.stderr | 11 ++-- 7 files changed, 77 insertions(+), 43 deletions(-) diff --git a/src/librustc_infer/infer/error_reporting/nice_region_error/static_impl_trait.rs b/src/librustc_infer/infer/error_reporting/nice_region_error/static_impl_trait.rs index 86f310eb71d7..74267a8dec05 100644 --- a/src/librustc_infer/infer/error_reporting/nice_region_error/static_impl_trait.rs +++ b/src/librustc_infer/infer/error_reporting/nice_region_error/static_impl_trait.rs @@ -77,18 +77,12 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { sup_origin.span(), "...is captured here, requiring it to live as long as `'static`", ); - } else if sup_origin.span() <= return_sp { + } else { err.span_label(sup_origin.span(), "...is captured here..."); - err.span_label( + err.span_note( return_sp, "...and is required to live as long as `'static` here", ); - } else { - err.span_label( - return_sp, - "...is required to live as long as `'static` here...", - ); - err.span_label(sup_origin.span(), "...and is captured here"); } } else { err.span_label( diff --git a/src/test/ui/async-await/issues/issue-62097.stderr b/src/test/ui/async-await/issues/issue-62097.stderr index e9f155c6ced3..ff7007dd30b1 100644 --- a/src/test/ui/async-await/issues/issue-62097.stderr +++ b/src/test/ui/async-await/issues/issue-62097.stderr @@ -6,8 +6,12 @@ LL | pub async fn run_dummy_fn(&self) { | | | this data with an anonymous lifetime `'_`... | ...is captured here... + | +note: ...and is required to live as long as `'static` here + --> $DIR/issue-62097.rs:13:9 + | LL | foo(|| self.bar()).await; - | --- ...and is required to live as long as `'static` here + | ^^^ error: aborting due to previous error diff --git a/src/test/ui/impl-trait/must_outlive_least_region_or_bound.stderr b/src/test/ui/impl-trait/must_outlive_least_region_or_bound.stderr index a9fa0e93fed6..698464b4971a 100644 --- a/src/test/ui/impl-trait/must_outlive_least_region_or_bound.stderr +++ b/src/test/ui/impl-trait/must_outlive_least_region_or_bound.stderr @@ -2,11 +2,15 @@ error[E0758]: cannot infer an appropriate lifetime --> $DIR/must_outlive_least_region_or_bound.rs:3:35 | LL | fn elided(x: &i32) -> impl Copy { x } - | ---- --------- ^ ...and is captured here - | | | - | | ...is required to live as long as `'static` here... + | ---- ^ ...is captured here... + | | | this data with an anonymous lifetime `'_`... | +note: ...and is required to live as long as `'static` here + --> $DIR/must_outlive_least_region_or_bound.rs:3:23 + | +LL | fn elided(x: &i32) -> impl Copy { x } + | ^^^^^^^^^ help: to declare that the `impl Trait` captures data from argument `x`, you can add an explicit `'_` lifetime bound | LL | fn elided(x: &i32) -> impl Copy + '_ { x } @@ -16,11 +20,15 @@ error[E0758]: cannot infer an appropriate lifetime --> $DIR/must_outlive_least_region_or_bound.rs:6:44 | LL | fn explicit<'a>(x: &'a i32) -> impl Copy { x } - | ------- --------- ^ ...and is captured here - | | | - | | ...is required to live as long as `'static` here... + | ------- ^ ...is captured here... + | | | this data with lifetime `'a`... | +note: ...and is required to live as long as `'static` here + --> $DIR/must_outlive_least_region_or_bound.rs:6:32 + | +LL | fn explicit<'a>(x: &'a i32) -> impl Copy { x } + | ^^^^^^^^^ help: to declare that the `impl Trait` captures data from argument `x`, you can add an explicit `'a` lifetime bound | LL | fn explicit<'a>(x: &'a i32) -> impl Copy + 'a { x } @@ -30,11 +38,15 @@ error[E0758]: cannot infer an appropriate lifetime --> $DIR/must_outlive_least_region_or_bound.rs:9:46 | LL | fn elided2(x: &i32) -> impl Copy + 'static { x } - | ---- ------------------- ^ ...and is captured here - | | | - | | ...is required to live as long as `'static` here... + | ---- ^ ...is captured here... + | | | this data with an anonymous lifetime `'_`... | +note: ...and is required to live as long as `'static` here + --> $DIR/must_outlive_least_region_or_bound.rs:9:24 + | +LL | fn elided2(x: &i32) -> impl Copy + 'static { x } + | ^^^^^^^^^^^^^^^^^^^ help: consider changing the `impl Trait`'s explicit `'static` bound to argument `x` | LL | fn elided2(x: &i32) -> impl Copy + '_ { x } @@ -48,11 +60,15 @@ error[E0758]: cannot infer an appropriate lifetime --> $DIR/must_outlive_least_region_or_bound.rs:12:55 | LL | fn explicit2<'a>(x: &'a i32) -> impl Copy + 'static { x } - | ------- ------------------- ^ ...and is captured here - | | | - | | ...is required to live as long as `'static` here... + | ------- ^ ...is captured here... + | | | this data with lifetime `'a`... | +note: ...and is required to live as long as `'static` here + --> $DIR/must_outlive_least_region_or_bound.rs:12:33 + | +LL | fn explicit2<'a>(x: &'a i32) -> impl Copy + 'static { x } + | ^^^^^^^^^^^^^^^^^^^ help: consider changing the `impl Trait`'s explicit `'static` bound to argument `x` | LL | fn explicit2<'a>(x: &'a i32) -> impl Copy + 'a { x } @@ -74,11 +90,13 @@ error[E0758]: cannot infer an appropriate lifetime --> $DIR/must_outlive_least_region_or_bound.rs:33:69 | LL | fn with_bound<'a>(x: &'a i32) -> impl LifetimeTrait<'a> + 'static { x } - | ------- -------------------------------- ^ ...and is captured here - | | | - | | ...is required to live as long as `'static` here... - | this data with lifetime `'a`... + | ------- this data with lifetime `'a`... ^ ...is captured here... | +note: ...and is required to live as long as `'static` here + --> $DIR/must_outlive_least_region_or_bound.rs:33:34 + | +LL | fn with_bound<'a>(x: &'a i32) -> impl LifetimeTrait<'a> + 'static { x } + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider changing the `impl Trait`'s explicit `'static` bound to argument `x` | LL | fn with_bound<'a>(x: &'a i32) -> impl LifetimeTrait<'a> + 'a { x } diff --git a/src/test/ui/impl-trait/static-return-lifetime-infered.stderr b/src/test/ui/impl-trait/static-return-lifetime-infered.stderr index 6681eaa909ee..bcc46785c591 100644 --- a/src/test/ui/impl-trait/static-return-lifetime-infered.stderr +++ b/src/test/ui/impl-trait/static-return-lifetime-infered.stderr @@ -2,14 +2,17 @@ error[E0758]: cannot infer an appropriate lifetime --> $DIR/static-return-lifetime-infered.rs:7:16 | LL | fn iter_values_anon(&self) -> impl Iterator { - | ----- ----------------------- ...is required to live as long as `'static` here... - | | - | this data with an anonymous lifetime `'_`... + | ----- this data with an anonymous lifetime `'_`... LL | self.x.iter().map(|a| a.0) | ------ ^^^^ | | - | ...and is captured here + | ...is captured here... | +note: ...and is required to live as long as `'static` here + --> $DIR/static-return-lifetime-infered.rs:6:35 + | +LL | fn iter_values_anon(&self) -> impl Iterator { + | ^^^^^^^^^^^^^^^^^^^^^^^ help: to declare that the `impl Trait` captures data from argument `self`, you can add an explicit `'_` lifetime bound | LL | fn iter_values_anon(&self) -> impl Iterator + '_ { @@ -19,14 +22,17 @@ error[E0758]: cannot infer an appropriate lifetime --> $DIR/static-return-lifetime-infered.rs:11:16 | LL | fn iter_values<'a>(&'a self) -> impl Iterator { - | -------- ----------------------- ...is required to live as long as `'static` here... - | | - | this data with lifetime `'a`... + | -------- this data with lifetime `'a`... LL | self.x.iter().map(|a| a.0) | ------ ^^^^ | | - | ...and is captured here + | ...is captured here... | +note: ...and is required to live as long as `'static` here + --> $DIR/static-return-lifetime-infered.rs:10:37 + | +LL | fn iter_values<'a>(&'a self) -> impl Iterator { + | ^^^^^^^^^^^^^^^^^^^^^^^ help: to declare that the `impl Trait` captures data from argument `self`, you can add an explicit `'a` lifetime bound | LL | fn iter_values<'a>(&'a self) -> impl Iterator + 'a { diff --git a/src/test/ui/self/arbitrary_self_types_pin_lifetime_impl_trait-async.stderr b/src/test/ui/self/arbitrary_self_types_pin_lifetime_impl_trait-async.stderr index 365e38515b12..2ffbf6e08158 100644 --- a/src/test/ui/self/arbitrary_self_types_pin_lifetime_impl_trait-async.stderr +++ b/src/test/ui/self/arbitrary_self_types_pin_lifetime_impl_trait-async.stderr @@ -2,10 +2,15 @@ error[E0758]: cannot infer an appropriate lifetime --> $DIR/arbitrary_self_types_pin_lifetime_impl_trait-async.rs:8:16 | LL | async fn f(self: Pin<&Self>) -> impl Clone { self } - | ^^^^ ---------- ---------- ...and is required to live as long as `'static` here - | | | - | | this data with an anonymous lifetime `'_`... + | ^^^^ ---------- this data with an anonymous lifetime `'_`... + | | | ...is captured here... + | +note: ...and is required to live as long as `'static` here + --> $DIR/arbitrary_self_types_pin_lifetime_impl_trait-async.rs:8:37 + | +LL | async fn f(self: Pin<&Self>) -> impl Clone { self } + | ^^^^^^^^^^ error: aborting due to previous error diff --git a/src/test/ui/self/arbitrary_self_types_pin_lifetime_impl_trait.stderr b/src/test/ui/self/arbitrary_self_types_pin_lifetime_impl_trait.stderr index bd3f3efad82f..2da7bcf543d6 100644 --- a/src/test/ui/self/arbitrary_self_types_pin_lifetime_impl_trait.stderr +++ b/src/test/ui/self/arbitrary_self_types_pin_lifetime_impl_trait.stderr @@ -2,11 +2,15 @@ error[E0758]: cannot infer an appropriate lifetime --> $DIR/arbitrary_self_types_pin_lifetime_impl_trait.rs:6:44 | LL | fn f(self: Pin<&Self>) -> impl Clone { self } - | ---------- ---------- ^^^^ ...and is captured here - | | | - | | ...is required to live as long as `'static` here... + | ---------- ^^^^ ...is captured here... + | | | this data with an anonymous lifetime `'_`... | +note: ...and is required to live as long as `'static` here + --> $DIR/arbitrary_self_types_pin_lifetime_impl_trait.rs:6:31 + | +LL | fn f(self: Pin<&Self>) -> impl Clone { self } + | ^^^^^^^^^^ help: to declare that the `impl Trait` captures data from argument `self`, you can add an explicit `'_` lifetime bound | LL | fn f(self: Pin<&Self>) -> impl Clone + '_ { self } diff --git a/src/test/ui/suggestions/lifetimes/missing-lifetimes-in-signature.stderr b/src/test/ui/suggestions/lifetimes/missing-lifetimes-in-signature.stderr index d96a5f961bdb..95d905af0508 100644 --- a/src/test/ui/suggestions/lifetimes/missing-lifetimes-in-signature.stderr +++ b/src/test/ui/suggestions/lifetimes/missing-lifetimes-in-signature.stderr @@ -10,15 +10,18 @@ error[E0758]: cannot infer an appropriate lifetime --> $DIR/missing-lifetimes-in-signature.rs:19:5 | LL | fn foo(g: G, dest: &mut T) -> impl FnOnce() - | ------ ------------- ...is required to live as long as `'static` here... - | | - | this data with an anonymous lifetime `'_`... + | ------ this data with an anonymous lifetime `'_`... ... LL | / move || { LL | | *dest = g.get(); LL | | } - | |_____^ ...and is captured here + | |_____^ ...is captured here... | +note: ...and is required to live as long as `'static` here + --> $DIR/missing-lifetimes-in-signature.rs:15:37 + | +LL | fn foo(g: G, dest: &mut T) -> impl FnOnce() + | ^^^^^^^^^^^^^ help: to declare that the `impl Trait` captures data from argument `dest`, you can add an explicit `'_` lifetime bound | LL | fn foo(g: G, dest: &mut T) -> impl FnOnce() + '_ From 34d8692262a8299025379e5178041d0d52a0329e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Tue, 2 Jun 2020 10:47:58 -0700 Subject: [PATCH 057/123] Register new eror code --- src/test/ui/async-await/issues/issue-62097.stderr | 1 + .../ui/impl-trait/must_outlive_least_region_or_bound.stderr | 2 +- src/test/ui/impl-trait/static-return-lifetime-infered.stderr | 1 + src/test/ui/issues/issue-16922.stderr | 1 + .../object-lifetime-default-from-box-error.stderr | 3 ++- src/test/ui/regions/region-object-lifetime-in-coercion.stderr | 3 ++- src/test/ui/regions/regions-close-object-into-object-2.stderr | 1 + src/test/ui/regions/regions-close-object-into-object-4.stderr | 1 + src/test/ui/regions/regions-proc-bound-capture.stderr | 1 + .../arbitrary_self_types_pin_lifetime_impl_trait-async.stderr | 1 + .../self/arbitrary_self_types_pin_lifetime_impl_trait.stderr | 1 + .../lifetimes/missing-lifetimes-in-signature.stderr | 2 +- src/test/ui/underscore-lifetime/dyn-trait-underscore.stderr | 1 + 13 files changed, 15 insertions(+), 4 deletions(-) diff --git a/src/test/ui/async-await/issues/issue-62097.stderr b/src/test/ui/async-await/issues/issue-62097.stderr index ff7007dd30b1..b3754cce4083 100644 --- a/src/test/ui/async-await/issues/issue-62097.stderr +++ b/src/test/ui/async-await/issues/issue-62097.stderr @@ -15,3 +15,4 @@ LL | foo(|| self.bar()).await; error: aborting due to previous error +For more information about this error, try `rustc --explain E0758`. diff --git a/src/test/ui/impl-trait/must_outlive_least_region_or_bound.stderr b/src/test/ui/impl-trait/must_outlive_least_region_or_bound.stderr index 698464b4971a..5ab450a85d9e 100644 --- a/src/test/ui/impl-trait/must_outlive_least_region_or_bound.stderr +++ b/src/test/ui/impl-trait/must_outlive_least_region_or_bound.stderr @@ -183,5 +183,5 @@ LL | fn explicit4<'a>(x: &'static i32) -> Box { Box::new(x) error: aborting due to 12 previous errors -Some errors have detailed explanations: E0310, E0621, E0623. +Some errors have detailed explanations: E0310, E0621, E0623, E0758. For more information about an error, try `rustc --explain E0310`. diff --git a/src/test/ui/impl-trait/static-return-lifetime-infered.stderr b/src/test/ui/impl-trait/static-return-lifetime-infered.stderr index bcc46785c591..90aada01d991 100644 --- a/src/test/ui/impl-trait/static-return-lifetime-infered.stderr +++ b/src/test/ui/impl-trait/static-return-lifetime-infered.stderr @@ -40,3 +40,4 @@ LL | fn iter_values<'a>(&'a self) -> impl Iterator + 'a { error: aborting due to 2 previous errors +For more information about this error, try `rustc --explain E0758`. diff --git a/src/test/ui/issues/issue-16922.stderr b/src/test/ui/issues/issue-16922.stderr index 95f46bd7f3eb..ec80f6569f5a 100644 --- a/src/test/ui/issues/issue-16922.stderr +++ b/src/test/ui/issues/issue-16922.stderr @@ -13,3 +13,4 @@ LL | fn foo(value: &T) -> Box { error: aborting due to previous error +For more information about this error, try `rustc --explain E0758`. diff --git a/src/test/ui/object-lifetime/object-lifetime-default-from-box-error.stderr b/src/test/ui/object-lifetime/object-lifetime-default-from-box-error.stderr index e585db262f2d..ac7502e004c4 100644 --- a/src/test/ui/object-lifetime/object-lifetime-default-from-box-error.stderr +++ b/src/test/ui/object-lifetime/object-lifetime-default-from-box-error.stderr @@ -23,4 +23,5 @@ LL | ss.r = b; error: aborting due to 2 previous errors -For more information about this error, try `rustc --explain E0621`. +Some errors have detailed explanations: E0621, E0758. +For more information about an error, try `rustc --explain E0621`. diff --git a/src/test/ui/regions/region-object-lifetime-in-coercion.stderr b/src/test/ui/regions/region-object-lifetime-in-coercion.stderr index 8d048d90cb34..fe191e9b6956 100644 --- a/src/test/ui/regions/region-object-lifetime-in-coercion.stderr +++ b/src/test/ui/regions/region-object-lifetime-in-coercion.stderr @@ -79,4 +79,5 @@ LL | Box::new(v) error: aborting due to 4 previous errors -For more information about this error, try `rustc --explain E0495`. +Some errors have detailed explanations: E0495, E0758. +For more information about an error, try `rustc --explain E0495`. diff --git a/src/test/ui/regions/regions-close-object-into-object-2.stderr b/src/test/ui/regions/regions-close-object-into-object-2.stderr index 5dfe384112b2..2ea970afcede 100644 --- a/src/test/ui/regions/regions-close-object-into-object-2.stderr +++ b/src/test/ui/regions/regions-close-object-into-object-2.stderr @@ -17,3 +17,4 @@ LL | fn g<'a, T: 'static>(v: std::boxed::Box<(dyn A + 'static)>) -> Box(v: std::boxed::Box<(dyn A + 'static)>) -> Box Box (isize) + 'static> error: aborting due to previous error +For more information about this error, try `rustc --explain E0758`. diff --git a/src/test/ui/self/arbitrary_self_types_pin_lifetime_impl_trait-async.stderr b/src/test/ui/self/arbitrary_self_types_pin_lifetime_impl_trait-async.stderr index 2ffbf6e08158..69022607bd19 100644 --- a/src/test/ui/self/arbitrary_self_types_pin_lifetime_impl_trait-async.stderr +++ b/src/test/ui/self/arbitrary_self_types_pin_lifetime_impl_trait-async.stderr @@ -14,3 +14,4 @@ LL | async fn f(self: Pin<&Self>) -> impl Clone { self } error: aborting due to previous error +For more information about this error, try `rustc --explain E0758`. diff --git a/src/test/ui/self/arbitrary_self_types_pin_lifetime_impl_trait.stderr b/src/test/ui/self/arbitrary_self_types_pin_lifetime_impl_trait.stderr index 2da7bcf543d6..c37eb2d136ef 100644 --- a/src/test/ui/self/arbitrary_self_types_pin_lifetime_impl_trait.stderr +++ b/src/test/ui/self/arbitrary_self_types_pin_lifetime_impl_trait.stderr @@ -18,3 +18,4 @@ LL | fn f(self: Pin<&Self>) -> impl Clone + '_ { self } error: aborting due to previous error +For more information about this error, try `rustc --explain E0758`. diff --git a/src/test/ui/suggestions/lifetimes/missing-lifetimes-in-signature.stderr b/src/test/ui/suggestions/lifetimes/missing-lifetimes-in-signature.stderr index 95d905af0508..7e8ab1bfe163 100644 --- a/src/test/ui/suggestions/lifetimes/missing-lifetimes-in-signature.stderr +++ b/src/test/ui/suggestions/lifetimes/missing-lifetimes-in-signature.stderr @@ -125,5 +125,5 @@ LL | fn bak<'a, G, T>(g: G, dest: &'a mut T) -> impl FnOnce() + 'a error: aborting due to 7 previous errors -Some errors have detailed explanations: E0261, E0309, E0621. +Some errors have detailed explanations: E0261, E0309, E0621, E0758. For more information about an error, try `rustc --explain E0261`. diff --git a/src/test/ui/underscore-lifetime/dyn-trait-underscore.stderr b/src/test/ui/underscore-lifetime/dyn-trait-underscore.stderr index 7c649f9c08d6..e768d7c8ab99 100644 --- a/src/test/ui/underscore-lifetime/dyn-trait-underscore.stderr +++ b/src/test/ui/underscore-lifetime/dyn-trait-underscore.stderr @@ -14,3 +14,4 @@ LL | fn a(items: &[T]) -> Box + '_> { error: aborting due to previous error +For more information about this error, try `rustc --explain E0758`. From e31367de6b5ed3878711cdd1761828587b9639fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Tue, 2 Jun 2020 16:05:48 -0700 Subject: [PATCH 058/123] small tweaks --- .../nice_region_error/static_impl_trait.rs | 20 +++++++++++++------ .../ui/async-await/issues/issue-62097.stderr | 6 +----- .../must_outlive_least_region_or_bound.stderr | 10 +++++----- ...ect-lifetime-default-from-box-error.stderr | 2 +- .../region-object-lifetime-in-coercion.stderr | 4 ++-- .../regions-close-object-into-object-2.stderr | 2 +- .../regions-close-object-into-object-4.stderr | 2 +- .../regions/regions-proc-bound-capture.stderr | 2 +- ...types_pin_lifetime_impl_trait-async.stderr | 11 +++------- .../dyn-trait-underscore.stderr | 2 +- 10 files changed, 30 insertions(+), 31 deletions(-) diff --git a/src/librustc_infer/infer/error_reporting/nice_region_error/static_impl_trait.rs b/src/librustc_infer/infer/error_reporting/nice_region_error/static_impl_trait.rs index 74267a8dec05..5ad76e396403 100644 --- a/src/librustc_infer/infer/error_reporting/nice_region_error/static_impl_trait.rs +++ b/src/librustc_infer/infer/error_reporting/nice_region_error/static_impl_trait.rs @@ -79,15 +79,22 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { ); } else { err.span_label(sup_origin.span(), "...is captured here..."); - err.span_note( - return_sp, - "...and is required to live as long as `'static` here", - ); + if return_sp < sup_origin.span() { + err.span_note( + return_sp, + "...and is required to live as long as `'static` here", + ); + } else { + err.span_label( + return_sp, + "...and is required to live as long as `'static` here", + ); + } } } else { err.span_label( return_sp, - "...is captured and required live as long as `'static` here", + "...is captured and required to live as long as `'static` here", ); } @@ -104,7 +111,8 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { }; let explicit = format!("you can add an explicit `{}` lifetime bound", lifetime_name); - let explicit_static = format!("explicit `'static` bound to {}", arg); + let explicit_static = + format!("explicit `'static` bound to the lifetime of {}", arg); let captures = format!("captures data from {}", arg); let add_static_bound = "alternatively, add an explicit `'static` bound to this reference"; diff --git a/src/test/ui/async-await/issues/issue-62097.stderr b/src/test/ui/async-await/issues/issue-62097.stderr index b3754cce4083..aa443b9d2fc1 100644 --- a/src/test/ui/async-await/issues/issue-62097.stderr +++ b/src/test/ui/async-await/issues/issue-62097.stderr @@ -6,12 +6,8 @@ LL | pub async fn run_dummy_fn(&self) { | | | this data with an anonymous lifetime `'_`... | ...is captured here... - | -note: ...and is required to live as long as `'static` here - --> $DIR/issue-62097.rs:13:9 - | LL | foo(|| self.bar()).await; - | ^^^ + | --- ...and is required to live as long as `'static` here error: aborting due to previous error diff --git a/src/test/ui/impl-trait/must_outlive_least_region_or_bound.stderr b/src/test/ui/impl-trait/must_outlive_least_region_or_bound.stderr index 5ab450a85d9e..baed43783a13 100644 --- a/src/test/ui/impl-trait/must_outlive_least_region_or_bound.stderr +++ b/src/test/ui/impl-trait/must_outlive_least_region_or_bound.stderr @@ -47,7 +47,7 @@ note: ...and is required to live as long as `'static` here | LL | fn elided2(x: &i32) -> impl Copy + 'static { x } | ^^^^^^^^^^^^^^^^^^^ -help: consider changing the `impl Trait`'s explicit `'static` bound to argument `x` +help: consider changing the `impl Trait`'s explicit `'static` bound to the lifetime of argument `x` | LL | fn elided2(x: &i32) -> impl Copy + '_ { x } | ^^ @@ -69,7 +69,7 @@ note: ...and is required to live as long as `'static` here | LL | fn explicit2<'a>(x: &'a i32) -> impl Copy + 'static { x } | ^^^^^^^^^^^^^^^^^^^ -help: consider changing the `impl Trait`'s explicit `'static` bound to argument `x` +help: consider changing the `impl Trait`'s explicit `'static` bound to the lifetime of argument `x` | LL | fn explicit2<'a>(x: &'a i32) -> impl Copy + 'a { x } | ^^ @@ -97,7 +97,7 @@ note: ...and is required to live as long as `'static` here | LL | fn with_bound<'a>(x: &'a i32) -> impl LifetimeTrait<'a> + 'static { x } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -help: consider changing the `impl Trait`'s explicit `'static` bound to argument `x` +help: consider changing the `impl Trait`'s explicit `'static` bound to the lifetime of argument `x` | LL | fn with_bound<'a>(x: &'a i32) -> impl LifetimeTrait<'a> + 'a { x } | ^^ @@ -157,7 +157,7 @@ LL | fn elided4(x: &i32) -> Box { Box::new(x) } | | | this data with an anonymous lifetime `'_`... | -help: consider changing the trait object's explicit `'static` bound to argument `x` +help: consider changing the trait object's explicit `'static` bound to the lifetime of argument `x` | LL | fn elided4(x: &i32) -> Box { Box::new(x) } | ^^ @@ -172,7 +172,7 @@ error[E0758]: cannot infer an appropriate lifetime LL | fn explicit4<'a>(x: &'a i32) -> Box { Box::new(x) } | ------- this data with lifetime `'a`... ^ ...is captured here, requiring it to live as long as `'static` | -help: consider changing the trait object's explicit `'static` bound to argument `x` +help: consider changing the trait object's explicit `'static` bound to the lifetime of argument `x` | LL | fn explicit4<'a>(x: &'a i32) -> Box { Box::new(x) } | ^^ diff --git a/src/test/ui/object-lifetime/object-lifetime-default-from-box-error.stderr b/src/test/ui/object-lifetime/object-lifetime-default-from-box-error.stderr index ac7502e004c4..d461001adead 100644 --- a/src/test/ui/object-lifetime/object-lifetime-default-from-box-error.stderr +++ b/src/test/ui/object-lifetime/object-lifetime-default-from-box-error.stderr @@ -5,7 +5,7 @@ LL | fn load(ss: &mut SomeStruct) -> Box { | --------------- this data with an anonymous lifetime `'_`... ... LL | ss.r - | ^^^^ ...is captured and required live as long as `'static` here + | ^^^^ ...is captured and required to live as long as `'static` here | help: to declare that the trait object captures data from argument `ss`, you can add an explicit `'_` lifetime bound | diff --git a/src/test/ui/regions/region-object-lifetime-in-coercion.stderr b/src/test/ui/regions/region-object-lifetime-in-coercion.stderr index fe191e9b6956..c684373c67e9 100644 --- a/src/test/ui/regions/region-object-lifetime-in-coercion.stderr +++ b/src/test/ui/regions/region-object-lifetime-in-coercion.stderr @@ -6,7 +6,7 @@ LL | fn a(v: &[u8]) -> Box { LL | let x: Box = Box::new(v); | ^ ...is captured here, requiring it to live as long as `'static` | -help: consider changing the trait object's explicit `'static` bound to argument `v` +help: consider changing the trait object's explicit `'static` bound to the lifetime of argument `v` | LL | fn a(v: &[u8]) -> Box { | ^^ @@ -23,7 +23,7 @@ LL | fn b(v: &[u8]) -> Box { LL | Box::new(v) | ^ ...is captured here, requiring it to live as long as `'static` | -help: consider changing the trait object's explicit `'static` bound to argument `v` +help: consider changing the trait object's explicit `'static` bound to the lifetime of argument `v` | LL | fn b(v: &[u8]) -> Box { | ^^ diff --git a/src/test/ui/regions/regions-close-object-into-object-2.stderr b/src/test/ui/regions/regions-close-object-into-object-2.stderr index 2ea970afcede..c4ba39517983 100644 --- a/src/test/ui/regions/regions-close-object-into-object-2.stderr +++ b/src/test/ui/regions/regions-close-object-into-object-2.stderr @@ -6,7 +6,7 @@ LL | fn g<'a, T: 'static>(v: Box + 'a>) -> Box { LL | box B(&*v) as Box | ^^^ ...is captured here, requiring it to live as long as `'static` | -help: consider changing the trait object's explicit `'static` bound to argument `v` +help: consider changing the trait object's explicit `'static` bound to the lifetime of argument `v` | LL | fn g<'a, T: 'static>(v: Box + 'a>) -> Box { | ^^ diff --git a/src/test/ui/regions/regions-close-object-into-object-4.stderr b/src/test/ui/regions/regions-close-object-into-object-4.stderr index 493b7a1df992..e45930bd9571 100644 --- a/src/test/ui/regions/regions-close-object-into-object-4.stderr +++ b/src/test/ui/regions/regions-close-object-into-object-4.stderr @@ -6,7 +6,7 @@ LL | fn i<'a, T, U>(v: Box+'a>) -> Box { LL | box B(&*v) as Box | ^^^ ...is captured here, requiring it to live as long as `'static` | -help: consider changing the trait object's explicit `'static` bound to argument `v` +help: consider changing the trait object's explicit `'static` bound to the lifetime of argument `v` | LL | fn i<'a, T, U>(v: Box+'a>) -> Box { | ^^ diff --git a/src/test/ui/regions/regions-proc-bound-capture.stderr b/src/test/ui/regions/regions-proc-bound-capture.stderr index c10b9850a4e9..69c7256364d7 100644 --- a/src/test/ui/regions/regions-proc-bound-capture.stderr +++ b/src/test/ui/regions/regions-proc-bound-capture.stderr @@ -7,7 +7,7 @@ LL | // This is illegal, because the region bound on `proc` is 'static. LL | Box::new(move || { *x }) | ^^^^^^^^^^^^^^ ...is captured here, requiring it to live as long as `'static` | -help: consider changing the trait object's explicit `'static` bound to argument `x` +help: consider changing the trait object's explicit `'static` bound to the lifetime of argument `x` | LL | fn static_proc(x: &isize) -> Box (isize) + '_> { | ^^ diff --git a/src/test/ui/self/arbitrary_self_types_pin_lifetime_impl_trait-async.stderr b/src/test/ui/self/arbitrary_self_types_pin_lifetime_impl_trait-async.stderr index 69022607bd19..9b04069136be 100644 --- a/src/test/ui/self/arbitrary_self_types_pin_lifetime_impl_trait-async.stderr +++ b/src/test/ui/self/arbitrary_self_types_pin_lifetime_impl_trait-async.stderr @@ -2,15 +2,10 @@ error[E0758]: cannot infer an appropriate lifetime --> $DIR/arbitrary_self_types_pin_lifetime_impl_trait-async.rs:8:16 | LL | async fn f(self: Pin<&Self>) -> impl Clone { self } - | ^^^^ ---------- this data with an anonymous lifetime `'_`... - | | + | ^^^^ ---------- ---------- ...and is required to live as long as `'static` here + | | | + | | this data with an anonymous lifetime `'_`... | ...is captured here... - | -note: ...and is required to live as long as `'static` here - --> $DIR/arbitrary_self_types_pin_lifetime_impl_trait-async.rs:8:37 - | -LL | async fn f(self: Pin<&Self>) -> impl Clone { self } - | ^^^^^^^^^^ error: aborting due to previous error diff --git a/src/test/ui/underscore-lifetime/dyn-trait-underscore.stderr b/src/test/ui/underscore-lifetime/dyn-trait-underscore.stderr index e768d7c8ab99..fc4c80194951 100644 --- a/src/test/ui/underscore-lifetime/dyn-trait-underscore.stderr +++ b/src/test/ui/underscore-lifetime/dyn-trait-underscore.stderr @@ -5,7 +5,7 @@ LL | fn a(items: &[T]) -> Box> { | ---- this data with an anonymous lifetime `'_`... LL | // ^^^^^^^^^^^^^^^^^^^^^ bound *here* defaults to `'static` LL | Box::new(items.iter()) - | ---------------^^^^--- ...is captured and required live as long as `'static` here + | ---------------^^^^--- ...is captured and required to live as long as `'static` here | help: to declare that the trait object captures data from argument `items`, you can add an explicit `'_` lifetime bound | From f7a1f97307e2f878297c6096f3afcc6fc2a31f22 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Wed, 3 Jun 2020 11:34:04 -0700 Subject: [PATCH 059/123] Change E0758 to E0759 to avoid conflict with #72912 --- src/librustc_error_codes/error_codes.rs | 1 + src/librustc_error_codes/error_codes/E0759.md | 67 +++++++++++++++++++ .../nice_region_error/static_impl_trait.rs | 2 +- .../ui/async-await/issues/issue-62097.stderr | 4 +- .../must_outlive_least_region_or_bound.stderr | 20 +++--- .../static-return-lifetime-infered.stderr | 6 +- src/test/ui/issues/issue-16922.stderr | 4 +- ...ect-lifetime-default-from-box-error.stderr | 4 +- .../region-object-lifetime-in-coercion.stderr | 8 +-- .../regions-close-object-into-object-2.stderr | 4 +- .../regions-close-object-into-object-4.stderr | 4 +- .../regions/regions-proc-bound-capture.stderr | 4 +- ...types_pin_lifetime_impl_trait-async.stderr | 4 +- ..._self_types_pin_lifetime_impl_trait.stderr | 4 +- .../missing-lifetimes-in-signature.stderr | 4 +- .../dyn-trait-underscore.stderr | 4 +- 16 files changed, 106 insertions(+), 38 deletions(-) create mode 100644 src/librustc_error_codes/error_codes/E0759.md diff --git a/src/librustc_error_codes/error_codes.rs b/src/librustc_error_codes/error_codes.rs index 3fb5e04efc92..99ef226f94aa 100644 --- a/src/librustc_error_codes/error_codes.rs +++ b/src/librustc_error_codes/error_codes.rs @@ -439,6 +439,7 @@ E0752: include_str!("./error_codes/E0752.md"), E0753: include_str!("./error_codes/E0753.md"), E0754: include_str!("./error_codes/E0754.md"), E0758: include_str!("./error_codes/E0758.md"), +E0759: include_str!("./error_codes/E0759.md"), E0760: include_str!("./error_codes/E0760.md"), E0761: include_str!("./error_codes/E0761.md"), E0762: include_str!("./error_codes/E0762.md"), diff --git a/src/librustc_error_codes/error_codes/E0759.md b/src/librustc_error_codes/error_codes/E0759.md new file mode 100644 index 000000000000..a74759bdf634 --- /dev/null +++ b/src/librustc_error_codes/error_codes/E0759.md @@ -0,0 +1,67 @@ +A `'static` requirement in a return type involving a trait is not fulfilled. + +Erroneous code examples: + +```compile_fail,E0759 +use std::fmt::Debug; + +fn foo(x: &i32) -> impl Debug { + x +} +``` + +```compile_fail,E0759 +# use std::fmt::Debug; +fn bar(x: &i32) -> Box { + Box::new(x) +} +``` + +These examples have the same semantics as the following: + +```compile_fail,E0759 +# use std::fmt::Debug; +fn foo(x: &i32) -> impl Debug + 'static { + x +} +``` + +```compile_fail,E0759 +# use std::fmt::Debug; +fn bar(x: &i32) -> Box { + Box::new(x) +} +``` + +Both [`dyn Trait`] and [`impl Trait`] in return types have a an implicit +`'static` requirement, meaning that the value implementing them that is being +returned has to be either a `'static` borrow or an owned value. + +In order to change the requirement from `'static` to be a lifetime derived from +its arguments, you can add an explicit bound, either to an anonymous lifetime +`'_` or some appropriate named lifetime. + +``` +# use std::fmt::Debug; +fn foo(x: &i32) -> impl Debug + '_ { + x +} +fn bar(x: &i32) -> Box { + Box::new(x) +} +``` + +These are equivalent to the following explicit lifetime annotations: + +``` +# use std::fmt::Debug; +fn foo<'a>(x: &'a i32) -> impl Debug + 'a { + x +} +fn bar<'a>(x: &'a i32) -> Box { + Box::new(x) +} +``` + +[`dyn Trait`]: https://doc.rust-lang.org/book/ch17-02-trait-objects.html#using-trait-objects-that-allow-for-values-of-different-types +[`impl Trait`]: https://doc.rust-lang.org/book/ch10-02-traits.html#returning-types-that-implement-traits diff --git a/src/librustc_infer/infer/error_reporting/nice_region_error/static_impl_trait.rs b/src/librustc_infer/infer/error_reporting/nice_region_error/static_impl_trait.rs index 5ad76e396403..853a41429070 100644 --- a/src/librustc_infer/infer/error_reporting/nice_region_error/static_impl_trait.rs +++ b/src/librustc_infer/infer/error_reporting/nice_region_error/static_impl_trait.rs @@ -40,7 +40,7 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { let mut err = struct_span_err!( self.tcx().sess, sp, - E0758, + E0759, "cannot infer an appropriate lifetime" ); err.span_label( diff --git a/src/test/ui/async-await/issues/issue-62097.stderr b/src/test/ui/async-await/issues/issue-62097.stderr index aa443b9d2fc1..0f58b158904d 100644 --- a/src/test/ui/async-await/issues/issue-62097.stderr +++ b/src/test/ui/async-await/issues/issue-62097.stderr @@ -1,4 +1,4 @@ -error[E0758]: cannot infer an appropriate lifetime +error[E0759]: cannot infer an appropriate lifetime --> $DIR/issue-62097.rs:12:31 | LL | pub async fn run_dummy_fn(&self) { @@ -11,4 +11,4 @@ LL | foo(|| self.bar()).await; error: aborting due to previous error -For more information about this error, try `rustc --explain E0758`. +For more information about this error, try `rustc --explain E0759`. diff --git a/src/test/ui/impl-trait/must_outlive_least_region_or_bound.stderr b/src/test/ui/impl-trait/must_outlive_least_region_or_bound.stderr index baed43783a13..e1fa4f02b6fc 100644 --- a/src/test/ui/impl-trait/must_outlive_least_region_or_bound.stderr +++ b/src/test/ui/impl-trait/must_outlive_least_region_or_bound.stderr @@ -1,4 +1,4 @@ -error[E0758]: cannot infer an appropriate lifetime +error[E0759]: cannot infer an appropriate lifetime --> $DIR/must_outlive_least_region_or_bound.rs:3:35 | LL | fn elided(x: &i32) -> impl Copy { x } @@ -16,7 +16,7 @@ help: to declare that the `impl Trait` captures data from argument `x`, you can LL | fn elided(x: &i32) -> impl Copy + '_ { x } | ^^^^ -error[E0758]: cannot infer an appropriate lifetime +error[E0759]: cannot infer an appropriate lifetime --> $DIR/must_outlive_least_region_or_bound.rs:6:44 | LL | fn explicit<'a>(x: &'a i32) -> impl Copy { x } @@ -34,7 +34,7 @@ help: to declare that the `impl Trait` captures data from argument `x`, you can LL | fn explicit<'a>(x: &'a i32) -> impl Copy + 'a { x } | ^^^^ -error[E0758]: cannot infer an appropriate lifetime +error[E0759]: cannot infer an appropriate lifetime --> $DIR/must_outlive_least_region_or_bound.rs:9:46 | LL | fn elided2(x: &i32) -> impl Copy + 'static { x } @@ -56,7 +56,7 @@ help: alternatively, add an explicit `'static` bound to this reference LL | fn elided2(x: &'static i32) -> impl Copy + 'static { x } | ^^^^^^^^^^^^ -error[E0758]: cannot infer an appropriate lifetime +error[E0759]: cannot infer an appropriate lifetime --> $DIR/must_outlive_least_region_or_bound.rs:12:55 | LL | fn explicit2<'a>(x: &'a i32) -> impl Copy + 'static { x } @@ -86,7 +86,7 @@ LL | fn foo<'a>(x: &i32) -> impl Copy + 'a { x } | | | help: add explicit lifetime `'a` to the type of `x`: `&'a i32` -error[E0758]: cannot infer an appropriate lifetime +error[E0759]: cannot infer an appropriate lifetime --> $DIR/must_outlive_least_region_or_bound.rs:33:69 | LL | fn with_bound<'a>(x: &'a i32) -> impl LifetimeTrait<'a> + 'static { x } @@ -123,7 +123,7 @@ LL | fn ty_param_wont_outlive_static(x: T) -> impl Debug + 'static { | | | help: consider adding an explicit lifetime bound...: `T: 'static +` -error[E0758]: cannot infer an appropriate lifetime +error[E0759]: cannot infer an appropriate lifetime --> $DIR/must_outlive_least_region_or_bound.rs:18:50 | LL | fn elided3(x: &i32) -> Box { Box::new(x) } @@ -136,7 +136,7 @@ help: to declare that the trait object captures data from argument `x`, you can LL | fn elided3(x: &i32) -> Box { Box::new(x) } | ^^^^ -error[E0758]: cannot infer an appropriate lifetime +error[E0759]: cannot infer an appropriate lifetime --> $DIR/must_outlive_least_region_or_bound.rs:21:59 | LL | fn explicit3<'a>(x: &'a i32) -> Box { Box::new(x) } @@ -149,7 +149,7 @@ help: to declare that the trait object captures data from argument `x`, you can LL | fn explicit3<'a>(x: &'a i32) -> Box { Box::new(x) } | ^^^^ -error[E0758]: cannot infer an appropriate lifetime +error[E0759]: cannot infer an appropriate lifetime --> $DIR/must_outlive_least_region_or_bound.rs:24:60 | LL | fn elided4(x: &i32) -> Box { Box::new(x) } @@ -166,7 +166,7 @@ help: alternatively, add an explicit `'static` bound to this reference LL | fn elided4(x: &'static i32) -> Box { Box::new(x) } | ^^^^^^^^^^^^ -error[E0758]: cannot infer an appropriate lifetime +error[E0759]: cannot infer an appropriate lifetime --> $DIR/must_outlive_least_region_or_bound.rs:27:69 | LL | fn explicit4<'a>(x: &'a i32) -> Box { Box::new(x) } @@ -183,5 +183,5 @@ LL | fn explicit4<'a>(x: &'static i32) -> Box { Box::new(x) error: aborting due to 12 previous errors -Some errors have detailed explanations: E0310, E0621, E0623, E0758. +Some errors have detailed explanations: E0310, E0621, E0623, E0759. For more information about an error, try `rustc --explain E0310`. diff --git a/src/test/ui/impl-trait/static-return-lifetime-infered.stderr b/src/test/ui/impl-trait/static-return-lifetime-infered.stderr index 90aada01d991..df0db6e4fc6d 100644 --- a/src/test/ui/impl-trait/static-return-lifetime-infered.stderr +++ b/src/test/ui/impl-trait/static-return-lifetime-infered.stderr @@ -1,4 +1,4 @@ -error[E0758]: cannot infer an appropriate lifetime +error[E0759]: cannot infer an appropriate lifetime --> $DIR/static-return-lifetime-infered.rs:7:16 | LL | fn iter_values_anon(&self) -> impl Iterator { @@ -18,7 +18,7 @@ help: to declare that the `impl Trait` captures data from argument `self`, you c LL | fn iter_values_anon(&self) -> impl Iterator + '_ { | ^^^^ -error[E0758]: cannot infer an appropriate lifetime +error[E0759]: cannot infer an appropriate lifetime --> $DIR/static-return-lifetime-infered.rs:11:16 | LL | fn iter_values<'a>(&'a self) -> impl Iterator { @@ -40,4 +40,4 @@ LL | fn iter_values<'a>(&'a self) -> impl Iterator + 'a { error: aborting due to 2 previous errors -For more information about this error, try `rustc --explain E0758`. +For more information about this error, try `rustc --explain E0759`. diff --git a/src/test/ui/issues/issue-16922.stderr b/src/test/ui/issues/issue-16922.stderr index ec80f6569f5a..919594fc9af4 100644 --- a/src/test/ui/issues/issue-16922.stderr +++ b/src/test/ui/issues/issue-16922.stderr @@ -1,4 +1,4 @@ -error[E0758]: cannot infer an appropriate lifetime +error[E0759]: cannot infer an appropriate lifetime --> $DIR/issue-16922.rs:4:14 | LL | fn foo(value: &T) -> Box { @@ -13,4 +13,4 @@ LL | fn foo(value: &T) -> Box { error: aborting due to previous error -For more information about this error, try `rustc --explain E0758`. +For more information about this error, try `rustc --explain E0759`. diff --git a/src/test/ui/object-lifetime/object-lifetime-default-from-box-error.stderr b/src/test/ui/object-lifetime/object-lifetime-default-from-box-error.stderr index d461001adead..1b1e0d961072 100644 --- a/src/test/ui/object-lifetime/object-lifetime-default-from-box-error.stderr +++ b/src/test/ui/object-lifetime/object-lifetime-default-from-box-error.stderr @@ -1,4 +1,4 @@ -error[E0758]: cannot infer an appropriate lifetime +error[E0759]: cannot infer an appropriate lifetime --> $DIR/object-lifetime-default-from-box-error.rs:18:5 | LL | fn load(ss: &mut SomeStruct) -> Box { @@ -23,5 +23,5 @@ LL | ss.r = b; error: aborting due to 2 previous errors -Some errors have detailed explanations: E0621, E0758. +Some errors have detailed explanations: E0621, E0759. For more information about an error, try `rustc --explain E0621`. diff --git a/src/test/ui/regions/region-object-lifetime-in-coercion.stderr b/src/test/ui/regions/region-object-lifetime-in-coercion.stderr index c684373c67e9..7f5a3a47976c 100644 --- a/src/test/ui/regions/region-object-lifetime-in-coercion.stderr +++ b/src/test/ui/regions/region-object-lifetime-in-coercion.stderr @@ -1,4 +1,4 @@ -error[E0758]: cannot infer an appropriate lifetime +error[E0759]: cannot infer an appropriate lifetime --> $DIR/region-object-lifetime-in-coercion.rs:8:46 | LL | fn a(v: &[u8]) -> Box { @@ -15,7 +15,7 @@ help: alternatively, add an explicit `'static` bound to this reference LL | fn a(v: &'static [u8]) -> Box { | ^^^^^^^^^^^^^ -error[E0758]: cannot infer an appropriate lifetime +error[E0759]: cannot infer an appropriate lifetime --> $DIR/region-object-lifetime-in-coercion.rs:13:14 | LL | fn b(v: &[u8]) -> Box { @@ -32,7 +32,7 @@ help: alternatively, add an explicit `'static` bound to this reference LL | fn b(v: &'static [u8]) -> Box { | ^^^^^^^^^^^^^ -error[E0758]: cannot infer an appropriate lifetime +error[E0759]: cannot infer an appropriate lifetime --> $DIR/region-object-lifetime-in-coercion.rs:19:14 | LL | fn c(v: &[u8]) -> Box { @@ -79,5 +79,5 @@ LL | Box::new(v) error: aborting due to 4 previous errors -Some errors have detailed explanations: E0495, E0758. +Some errors have detailed explanations: E0495, E0759. For more information about an error, try `rustc --explain E0495`. diff --git a/src/test/ui/regions/regions-close-object-into-object-2.stderr b/src/test/ui/regions/regions-close-object-into-object-2.stderr index c4ba39517983..114e4052aae0 100644 --- a/src/test/ui/regions/regions-close-object-into-object-2.stderr +++ b/src/test/ui/regions/regions-close-object-into-object-2.stderr @@ -1,4 +1,4 @@ -error[E0758]: cannot infer an appropriate lifetime +error[E0759]: cannot infer an appropriate lifetime --> $DIR/regions-close-object-into-object-2.rs:10:11 | LL | fn g<'a, T: 'static>(v: Box + 'a>) -> Box { @@ -17,4 +17,4 @@ LL | fn g<'a, T: 'static>(v: std::boxed::Box<(dyn A + 'static)>) -> Box $DIR/regions-close-object-into-object-4.rs:10:11 | LL | fn i<'a, T, U>(v: Box+'a>) -> Box { @@ -17,4 +17,4 @@ LL | fn i<'a, T, U>(v: std::boxed::Box<(dyn A + 'static)>) -> Box $DIR/regions-proc-bound-capture.rs:9:14 | LL | fn static_proc(x: &isize) -> Box (isize) + 'static> { @@ -18,4 +18,4 @@ LL | fn static_proc(x: &'static isize) -> Box (isize) + 'static> error: aborting due to previous error -For more information about this error, try `rustc --explain E0758`. +For more information about this error, try `rustc --explain E0759`. diff --git a/src/test/ui/self/arbitrary_self_types_pin_lifetime_impl_trait-async.stderr b/src/test/ui/self/arbitrary_self_types_pin_lifetime_impl_trait-async.stderr index 9b04069136be..88bd990b1e81 100644 --- a/src/test/ui/self/arbitrary_self_types_pin_lifetime_impl_trait-async.stderr +++ b/src/test/ui/self/arbitrary_self_types_pin_lifetime_impl_trait-async.stderr @@ -1,4 +1,4 @@ -error[E0758]: cannot infer an appropriate lifetime +error[E0759]: cannot infer an appropriate lifetime --> $DIR/arbitrary_self_types_pin_lifetime_impl_trait-async.rs:8:16 | LL | async fn f(self: Pin<&Self>) -> impl Clone { self } @@ -9,4 +9,4 @@ LL | async fn f(self: Pin<&Self>) -> impl Clone { self } error: aborting due to previous error -For more information about this error, try `rustc --explain E0758`. +For more information about this error, try `rustc --explain E0759`. diff --git a/src/test/ui/self/arbitrary_self_types_pin_lifetime_impl_trait.stderr b/src/test/ui/self/arbitrary_self_types_pin_lifetime_impl_trait.stderr index c37eb2d136ef..2e10ab3d3f9b 100644 --- a/src/test/ui/self/arbitrary_self_types_pin_lifetime_impl_trait.stderr +++ b/src/test/ui/self/arbitrary_self_types_pin_lifetime_impl_trait.stderr @@ -1,4 +1,4 @@ -error[E0758]: cannot infer an appropriate lifetime +error[E0759]: cannot infer an appropriate lifetime --> $DIR/arbitrary_self_types_pin_lifetime_impl_trait.rs:6:44 | LL | fn f(self: Pin<&Self>) -> impl Clone { self } @@ -18,4 +18,4 @@ LL | fn f(self: Pin<&Self>) -> impl Clone + '_ { self } error: aborting due to previous error -For more information about this error, try `rustc --explain E0758`. +For more information about this error, try `rustc --explain E0759`. diff --git a/src/test/ui/suggestions/lifetimes/missing-lifetimes-in-signature.stderr b/src/test/ui/suggestions/lifetimes/missing-lifetimes-in-signature.stderr index 7e8ab1bfe163..9ab060328537 100644 --- a/src/test/ui/suggestions/lifetimes/missing-lifetimes-in-signature.stderr +++ b/src/test/ui/suggestions/lifetimes/missing-lifetimes-in-signature.stderr @@ -6,7 +6,7 @@ LL | fn baz(g: G, dest: &mut T) -> impl FnOnce() + '_ | | | help: consider introducing lifetime `'a` here: `'a,` -error[E0758]: cannot infer an appropriate lifetime +error[E0759]: cannot infer an appropriate lifetime --> $DIR/missing-lifetimes-in-signature.rs:19:5 | LL | fn foo(g: G, dest: &mut T) -> impl FnOnce() @@ -125,5 +125,5 @@ LL | fn bak<'a, G, T>(g: G, dest: &'a mut T) -> impl FnOnce() + 'a error: aborting due to 7 previous errors -Some errors have detailed explanations: E0261, E0309, E0621, E0758. +Some errors have detailed explanations: E0261, E0309, E0621, E0759. For more information about an error, try `rustc --explain E0261`. diff --git a/src/test/ui/underscore-lifetime/dyn-trait-underscore.stderr b/src/test/ui/underscore-lifetime/dyn-trait-underscore.stderr index fc4c80194951..dda5de431d30 100644 --- a/src/test/ui/underscore-lifetime/dyn-trait-underscore.stderr +++ b/src/test/ui/underscore-lifetime/dyn-trait-underscore.stderr @@ -1,4 +1,4 @@ -error[E0758]: cannot infer an appropriate lifetime +error[E0759]: cannot infer an appropriate lifetime --> $DIR/dyn-trait-underscore.rs:8:20 | LL | fn a(items: &[T]) -> Box> { @@ -14,4 +14,4 @@ LL | fn a(items: &[T]) -> Box + '_> { error: aborting due to previous error -For more information about this error, try `rustc --explain E0758`. +For more information about this error, try `rustc --explain E0759`. From bfe1434d3bb849d3eb993c42fb57aa0819f9be65 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Mon, 15 Jun 2020 09:09:20 -0700 Subject: [PATCH 060/123] fix rebase --- .../error_reporting/nice_region_error/static_impl_trait.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/librustc_infer/infer/error_reporting/nice_region_error/static_impl_trait.rs b/src/librustc_infer/infer/error_reporting/nice_region_error/static_impl_trait.rs index 853a41429070..82feebc80292 100644 --- a/src/librustc_infer/infer/error_reporting/nice_region_error/static_impl_trait.rs +++ b/src/librustc_infer/infer/error_reporting/nice_region_error/static_impl_trait.rs @@ -118,7 +118,7 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { "alternatively, add an explicit `'static` bound to this reference"; let plus_lt = format!(" + {}", lifetime_name); match fn_return.kind { - TyKind::Def(item_id, _) => { + TyKind::OpaqueDef(item_id, _) => { let item = self.tcx().hir().item(item_id.id); let opaque = if let ItemKind::OpaqueTy(opaque) = &item.kind { opaque From b5809b027262cb9404519862ccbe06a13cae408b Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Mon, 15 Jun 2020 12:12:22 -0400 Subject: [PATCH 061/123] Update src/librustc_typeck/check/cast.rs Co-authored-by: lzutao --- src/librustc_typeck/check/cast.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/librustc_typeck/check/cast.rs b/src/librustc_typeck/check/cast.rs index bea5e0e99665..fa10851abd4f 100644 --- a/src/librustc_typeck/check/cast.rs +++ b/src/librustc_typeck/check/cast.rs @@ -755,7 +755,7 @@ impl<'a, 'tcx> CastCheck<'tcx> { self.span, |err| { err.build(&format!( - "Cast `enum` implementing `Drop` `{}` to integer `{}`", + "cannot cast enum `{}` into integer `{}` because it implements `Drop`", self.expr_ty, self.cast_ty )) .emit(); From 8e7606f204d8775b051cdd9a427ec6dd89b837b8 Mon Sep 17 00:00:00 2001 From: "NODA, Kai" Date: Mon, 15 Jun 2020 02:11:35 +0800 Subject: [PATCH 062/123] bootstrap/install.rs: support a nonexistent `prefix` in `x.py install` PR #49778 introduced fs::canonicalize() which fails for a nonexistent path. This is a surprise for someone used to GNU Autotools' configure which can create any necessary intermediate directories in prefix. This change makes it run fs::create_dir_all() before canonicalize(). --- src/bootstrap/install.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/bootstrap/install.rs b/src/bootstrap/install.rs index fafd3cdf927c..fbdef9d8272f 100644 --- a/src/bootstrap/install.rs +++ b/src/bootstrap/install.rs @@ -70,7 +70,10 @@ fn install_sh( let libdir_default = PathBuf::from("lib"); let mandir_default = datadir_default.join("man"); let prefix = builder.config.prefix.as_ref().map_or(prefix_default, |p| { - fs::canonicalize(p).unwrap_or_else(|_| panic!("could not canonicalize {}", p.display())) + fs::create_dir_all(p) + .unwrap_or_else(|err| panic!("could not create {}: {}", p.display(), err)); + fs::canonicalize(p) + .unwrap_or_else(|err| panic!("could not canonicalize {}: {}", p.display(), err)) }); let sysconfdir = builder.config.sysconfdir.as_ref().unwrap_or(&sysconfdir_default); let datadir = builder.config.datadir.as_ref().unwrap_or(&datadir_default); From 10c8d2afb8b90e03e2d1563df021146150338c45 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Mon, 15 Jun 2020 19:12:14 +0200 Subject: [PATCH 063/123] add FIXME to EnumTagInfo --- src/librustc_codegen_llvm/debuginfo/metadata.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/librustc_codegen_llvm/debuginfo/metadata.rs b/src/librustc_codegen_llvm/debuginfo/metadata.rs index 01f630a31a18..b7e7489cba0a 100644 --- a/src/librustc_codegen_llvm/debuginfo/metadata.rs +++ b/src/librustc_codegen_llvm/debuginfo/metadata.rs @@ -1637,6 +1637,9 @@ impl VariantMemberDescriptionFactory<'ll, 'tcx> { } } +// FIXME: terminology here should be aligned with `abi::TagEncoding`. +// `OptimizedTag` is `TagEncoding::Niche`, `RegularTag` is `TagEncoding::Direct`. +// `NoTag` should be removed; users should use `Option` instead. #[derive(Copy, Clone)] enum EnumTagInfo<'ll> { RegularTag { tag_field: Field, tag_type_metadata: &'ll DIType }, From 96f5584b808cd08c3c8208db4fee04d2c2b71d79 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Sun, 19 Apr 2020 16:52:15 -0700 Subject: [PATCH 064/123] Expand "recursive opaque type" diagnostic Fix #70968, partially address #66523. --- src/librustc_hir/hir.rs | 12 ++ .../traits/error_reporting/suggestions.rs | 4 +- src/librustc_typeck/check/mod.rs | 195 ++++++++++++++++-- .../ui/impl-trait/binding-without-value.rs | 9 + .../impl-trait/binding-without-value.stderr | 16 ++ .../issues/infinite-impl-trait-issue-38064.rs | 4 +- .../infinite-impl-trait-issue-38064.stderr | 24 ++- .../recursive-impl-trait-type-direct.stderr | 9 +- .../recursive-impl-trait-type-indirect.stderr | 153 ++++++++------ ...e-impl-trait-type-through-non-recursive.rs | 8 +- ...pl-trait-type-through-non-recursive.stderr | 44 ++-- src/test/ui/impl-trait/where-allowed-2.rs | 3 +- src/test/ui/impl-trait/where-allowed-2.stderr | 8 +- 13 files changed, 374 insertions(+), 115 deletions(-) create mode 100644 src/test/ui/impl-trait/binding-without-value.rs create mode 100644 src/test/ui/impl-trait/binding-without-value.stderr diff --git a/src/librustc_hir/hir.rs b/src/librustc_hir/hir.rs index 634ab32a2854..bed2044c7085 100644 --- a/src/librustc_hir/hir.rs +++ b/src/librustc_hir/hir.rs @@ -2726,6 +2726,18 @@ impl Node<'_> { } } + pub fn body_id(&self) -> Option { + match self { + Node::TraitItem(TraitItem { + kind: TraitItemKind::Fn(_, TraitFn::Provided(body_id)), + .. + }) + | Node::ImplItem(ImplItem { kind: ImplItemKind::Fn(_, body_id), .. }) + | Node::Item(Item { kind: ItemKind::Fn(.., body_id), .. }) => Some(*body_id), + _ => None, + } + } + pub fn generics(&self) -> Option<&Generics<'_>> { match self { Node::TraitItem(TraitItem { generics, .. }) diff --git a/src/librustc_trait_selection/traits/error_reporting/suggestions.rs b/src/librustc_trait_selection/traits/error_reporting/suggestions.rs index 8796cfb52165..edff67929f6d 100644 --- a/src/librustc_trait_selection/traits/error_reporting/suggestions.rs +++ b/src/librustc_trait_selection/traits/error_reporting/suggestions.rs @@ -1992,8 +1992,8 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { /// Collect all the returned expressions within the input expression. /// Used to point at the return spans when we want to suggest some change to them. #[derive(Default)] -struct ReturnsVisitor<'v> { - returns: Vec<&'v hir::Expr<'v>>, +pub struct ReturnsVisitor<'v> { + pub returns: Vec<&'v hir::Expr<'v>>, in_block_tail: bool, } diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index a409e20953da..d4db32abe2a1 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -138,6 +138,7 @@ use rustc_target::spec::abi::Abi; use rustc_trait_selection::infer::InferCtxtExt as _; use rustc_trait_selection::opaque_types::{InferCtxtExt as _, OpaqueTypeDecl}; use rustc_trait_selection::traits::error_reporting::recursive_type_with_infinite_size_error; +use rustc_trait_selection::traits::error_reporting::suggestions::ReturnsVisitor; use rustc_trait_selection::traits::error_reporting::InferCtxtExt as _; use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt as _; use rustc_trait_selection::traits::{ @@ -1711,6 +1712,181 @@ fn check_opaque_for_inheriting_lifetimes(tcx: TyCtxt<'tcx>, def_id: LocalDefId, } } +/// Given a `DefId` for an opaque type in return position, find its parent item's return +/// expressions. +fn get_owner_return_paths( + tcx: TyCtxt<'tcx>, + def_id: LocalDefId, +) -> Option<(hir::HirId, ReturnsVisitor<'tcx>)> { + let hir_id = tcx.hir().as_local_hir_id(def_id); + let id = tcx.hir().get_parent_item(hir_id); + tcx.hir() + .find(id) + .map(|n| (id, n)) + .and_then(|(hir_id, node)| node.body_id().map(|b| (hir_id, b))) + .map(|(hir_id, body_id)| { + let body = tcx.hir().body(body_id); + let mut visitor = ReturnsVisitor::default(); + visitor.visit_body(body); + (hir_id, visitor) + }) +} + +/// Emit an error for recursive opaque types. +/// +/// If this is a return `impl Trait`, find the item's return expressions and point at them. For +/// direct recursion this is enough, but for indirect recursion also point at the last intermediary +/// `impl Trait`. +/// +/// If all the return expressions evaluate to `!`, then we explain that the error will go away +/// after changing it. This can happen when a user uses `panic!()` or similar as a placeholder. +fn opaque_type_cycle_error(tcx: TyCtxt<'tcx>, def_id: LocalDefId, span: Span) { + let mut err = + struct_span_err!(tcx.sess, span, E0720, "cannot resolve opaque type to a concrete type"); + + let mut label = false; + if let Some((hir_id, visitor)) = get_owner_return_paths(tcx, def_id) { + let tables = tcx.typeck_tables_of(tcx.hir().local_def_id(hir_id)); + if visitor + .returns + .iter() + .filter_map(|expr| tables.node_type_opt(expr.hir_id)) + .map(|ty| tcx.infer_ctxt().enter(|infcx| infcx.resolve_vars_if_possible(&ty))) + .all(|ty| matches!(ty.kind, ty::Never)) + { + let spans = visitor + .returns + .iter() + .filter(|expr| tables.node_type_opt(expr.hir_id).is_some()) + .map(|expr| expr.span) + .collect::>(); + let span_len = spans.len(); + if span_len == 1 { + err.span_label(spans[0], "this returned value is of `!` type"); + } else { + let mut multispan: MultiSpan = spans.clone().into(); + for span in spans { + multispan + .push_span_label(span, "this returned value is of `!` type".to_string()); + } + err.span_note(multispan, "these returned values have a concrete \"never\" type"); + } + err.help("this error will resolve once the item's body returns a concrete type"); + } else { + let mut seen = FxHashSet::default(); + seen.insert(span); + err.span_label(span, "recursive opaque type"); + label = true; + for (sp, ty) in visitor + .returns + .iter() + .filter_map(|e| tables.node_type_opt(e.hir_id).map(|t| (e.span, t))) + .filter(|(_, ty)| !matches!(ty.kind, ty::Never)) + .map(|(sp, ty)| { + (sp, tcx.infer_ctxt().enter(|infcx| infcx.resolve_vars_if_possible(&ty))) + }) + { + struct VisitTypes(Vec); + impl<'tcx> ty::fold::TypeVisitor<'tcx> for VisitTypes { + fn visit_ty(&mut self, t: Ty<'tcx>) -> bool { + match t.kind { + ty::Opaque(def, _) => { + self.0.push(def); + false + } + _ => t.super_visit_with(self), + } + } + } + let mut visitor = VisitTypes(vec![]); + ty.visit_with(&mut visitor); + for def_id in visitor.0 { + let ty_span = tcx.def_span(def_id); + if !seen.contains(&ty_span) { + err.span_label(ty_span, &format!("returning this opaque type `{}`", ty)); + seen.insert(ty_span); + } + err.span_label(sp, &format!("returning here with type `{}`", ty)); + } + } + } + } + if !label { + err.span_label(span, "cannot resolve to a concrete type"); + } + err.emit(); +} + +/// Emit an error for recursive opaque types in a `let` binding. +fn binding_opaque_type_cycle_error( + tcx: TyCtxt<'tcx>, + def_id: LocalDefId, + span: Span, + partially_expanded_type: Ty<'tcx>, +) { + let mut err = + struct_span_err!(tcx.sess, span, E0720, "cannot resolve opaque type to a concrete type"); + err.span_label(span, "cannot resolve to a concrete type"); + let hir_id = tcx.hir().as_local_hir_id(def_id); + let mut prev_hir_id = hir_id; + let mut hir_id = tcx.hir().get_parent_node(hir_id); + while let Some(node) = tcx.hir().find(hir_id) { + match node { + hir::Node::Local(hir::Local { + pat, + init: None, + ty: Some(ty), + source: hir::LocalSource::Normal, + .. + }) => { + err.span_label(pat.span, "this binding might not have a concrete type"); + err.span_suggestion_verbose( + ty.span.shrink_to_hi(), + "set the binding to a value for a concrete type to be resolved", + " = /* value */".to_string(), + Applicability::HasPlaceholders, + ); + } + hir::Node::Local(hir::Local { + init: Some(expr), + source: hir::LocalSource::Normal, + .. + }) => { + let hir_id = tcx.hir().as_local_hir_id(def_id); + let tables = + tcx.typeck_tables_of(tcx.hir().local_def_id(tcx.hir().get_parent_item(hir_id))); + let ty = tables.node_type_opt(expr.hir_id); + if let Some(ty) = + tcx.infer_ctxt().enter(|infcx| infcx.resolve_vars_if_possible(&ty)) + { + err.span_label( + expr.span, + &format!( + "this is of type `{}`, which doesn't constrain \ + `{}` enough to arrive to a concrete type", + ty, partially_expanded_type + ), + ); + } + } + _ => {} + } + if prev_hir_id == hir_id { + break; + } + prev_hir_id = hir_id; + hir_id = tcx.hir().get_parent_node(hir_id); + } + err.emit(); +} + +fn async_opaque_type_cycle_error(tcx: TyCtxt<'tcx>, span: Span) { + struct_span_err!(tcx.sess, span, E0733, "recursion in an `async fn` requires boxing") + .span_label(span, "recursive `async fn`") + .note("a recursive `async fn` must be rewritten to return a boxed `dyn Future`") + .emit(); +} + /// Checks that an opaque type does not contain cycles. fn check_opaque_for_cycles<'tcx>( tcx: TyCtxt<'tcx>, @@ -1721,21 +1897,12 @@ fn check_opaque_for_cycles<'tcx>( ) { if let Err(partially_expanded_type) = tcx.try_expand_impl_trait_type(def_id.to_def_id(), substs) { - if let hir::OpaqueTyOrigin::AsyncFn = origin { - struct_span_err!(tcx.sess, span, E0733, "recursion in an `async fn` requires boxing",) - .span_label(span, "recursive `async fn`") - .note("a recursive `async fn` must be rewritten to return a boxed `dyn Future`") - .emit(); - } else { - let mut err = - struct_span_err!(tcx.sess, span, E0720, "opaque type expands to a recursive type",); - err.span_label(span, "expands to a recursive type"); - if let ty::Opaque(..) = partially_expanded_type.kind { - err.note("type resolves to itself"); - } else { - err.note(&format!("expanded type is `{}`", partially_expanded_type)); + match origin { + hir::OpaqueTyOrigin::AsyncFn => async_opaque_type_cycle_error(tcx, span), + hir::OpaqueTyOrigin::Binding => { + binding_opaque_type_cycle_error(tcx, def_id, span, partially_expanded_type) } - err.emit(); + _ => opaque_type_cycle_error(tcx, def_id, span), } } } diff --git a/src/test/ui/impl-trait/binding-without-value.rs b/src/test/ui/impl-trait/binding-without-value.rs new file mode 100644 index 000000000000..6a97f28ff552 --- /dev/null +++ b/src/test/ui/impl-trait/binding-without-value.rs @@ -0,0 +1,9 @@ +#![allow(incomplete_features)] +#![feature(impl_trait_in_bindings)] + +fn foo() { + let _ : impl Copy; + //~^ ERROR cannot resolve opaque type +} + +fn main() {} diff --git a/src/test/ui/impl-trait/binding-without-value.stderr b/src/test/ui/impl-trait/binding-without-value.stderr new file mode 100644 index 000000000000..1898af5b63ee --- /dev/null +++ b/src/test/ui/impl-trait/binding-without-value.stderr @@ -0,0 +1,16 @@ +error[E0720]: cannot resolve opaque type to a concrete type + --> $DIR/binding-without-value.rs:5:13 + | +LL | let _ : impl Copy; + | - ^^^^^^^^^ cannot resolve to a concrete type + | | + | this binding might not have a concrete type + | +help: set the binding to a value for a concrete type to be resolved + | +LL | let _ : impl Copy = /* value */; + | ^^^^^^^^^^^^^ + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0720`. diff --git a/src/test/ui/impl-trait/issues/infinite-impl-trait-issue-38064.rs b/src/test/ui/impl-trait/issues/infinite-impl-trait-issue-38064.rs index 150a8015cbc7..451ddb3cce0e 100644 --- a/src/test/ui/impl-trait/issues/infinite-impl-trait-issue-38064.rs +++ b/src/test/ui/impl-trait/issues/infinite-impl-trait-issue-38064.rs @@ -5,13 +5,13 @@ trait Quux {} -fn foo() -> impl Quux { //~ opaque type expands to a recursive type +fn foo() -> impl Quux { //~ ERROR cannot resolve opaque type struct Foo(T); impl Quux for Foo {} Foo(bar()) } -fn bar() -> impl Quux { //~ opaque type expands to a recursive type +fn bar() -> impl Quux { //~ ERROR cannot resolve opaque type struct Bar(T); impl Quux for Bar {} Bar(foo()) diff --git a/src/test/ui/impl-trait/issues/infinite-impl-trait-issue-38064.stderr b/src/test/ui/impl-trait/issues/infinite-impl-trait-issue-38064.stderr index d10001e8a8e5..a38fb7cb56e9 100644 --- a/src/test/ui/impl-trait/issues/infinite-impl-trait-issue-38064.stderr +++ b/src/test/ui/impl-trait/issues/infinite-impl-trait-issue-38064.stderr @@ -1,18 +1,26 @@ -error[E0720]: opaque type expands to a recursive type +error[E0720]: cannot resolve opaque type to a concrete type --> $DIR/infinite-impl-trait-issue-38064.rs:8:13 | LL | fn foo() -> impl Quux { - | ^^^^^^^^^ expands to a recursive type - | - = note: expanded type is `foo::Foo>` + | ^^^^^^^^^ recursive opaque type +... +LL | Foo(bar()) + | ---------- returning here with type `foo::Foo` +... +LL | fn bar() -> impl Quux { + | --------- returning this opaque type `foo::Foo` -error[E0720]: opaque type expands to a recursive type +error[E0720]: cannot resolve opaque type to a concrete type --> $DIR/infinite-impl-trait-issue-38064.rs:14:13 | +LL | fn foo() -> impl Quux { + | --------- returning this opaque type `bar::Bar` +... LL | fn bar() -> impl Quux { - | ^^^^^^^^^ expands to a recursive type - | - = note: expanded type is `bar::Bar>` + | ^^^^^^^^^ recursive opaque type +... +LL | Bar(foo()) + | ---------- returning here with type `bar::Bar` error: aborting due to 2 previous errors diff --git a/src/test/ui/impl-trait/recursive-impl-trait-type-direct.stderr b/src/test/ui/impl-trait/recursive-impl-trait-type-direct.stderr index 5a95e2969d1b..5149d42370c7 100644 --- a/src/test/ui/impl-trait/recursive-impl-trait-type-direct.stderr +++ b/src/test/ui/impl-trait/recursive-impl-trait-type-direct.stderr @@ -1,10 +1,11 @@ -error[E0720]: opaque type expands to a recursive type +error[E0720]: cannot resolve opaque type to a concrete type --> $DIR/recursive-impl-trait-type-direct.rs:5:14 | LL | fn test() -> impl Sized { - | ^^^^^^^^^^ expands to a recursive type - | - = note: type resolves to itself + | ^^^^^^^^^^ recursive opaque type +LL | +LL | test() + | ------ returning here with type `impl Sized` error: aborting due to previous error diff --git a/src/test/ui/impl-trait/recursive-impl-trait-type-indirect.stderr b/src/test/ui/impl-trait/recursive-impl-trait-type-indirect.stderr index 6573b00870c5..0bf362e9a6d4 100644 --- a/src/test/ui/impl-trait/recursive-impl-trait-type-indirect.stderr +++ b/src/test/ui/impl-trait/recursive-impl-trait-type-indirect.stderr @@ -1,114 +1,147 @@ -error[E0720]: opaque type expands to a recursive type +error[E0720]: cannot resolve opaque type to a concrete type --> $DIR/recursive-impl-trait-type-indirect.rs:7:22 | LL | fn option(i: i32) -> impl Sized { - | ^^^^^^^^^^ expands to a recursive type - | - = note: expanded type is `std::option::Option<(impl Sized, i32)>` + | ^^^^^^^^^^ recursive opaque type +LL | +LL | if i < 0 { None } else { Some((option(i - 1), i)) } + | ---- ------------------------ returning here with type `std::option::Option<(impl Sized, i32)>` + | | + | returning here with type `std::option::Option<(impl Sized, i32)>` -error[E0720]: opaque type expands to a recursive type +error[E0720]: cannot resolve opaque type to a concrete type --> $DIR/recursive-impl-trait-type-indirect.rs:12:15 | LL | fn tuple() -> impl Sized { - | ^^^^^^^^^^ expands to a recursive type - | - = note: expanded type is `(impl Sized,)` + | ^^^^^^^^^^ recursive opaque type +LL | +LL | (tuple(),) + | ---------- returning here with type `(impl Sized,)` -error[E0720]: opaque type expands to a recursive type +error[E0720]: cannot resolve opaque type to a concrete type --> $DIR/recursive-impl-trait-type-indirect.rs:17:15 | LL | fn array() -> impl Sized { - | ^^^^^^^^^^ expands to a recursive type - | - = note: expanded type is `[impl Sized; 1]` + | ^^^^^^^^^^ recursive opaque type +LL | +LL | [array()] + | --------- returning here with type `[impl Sized; 1]` -error[E0720]: opaque type expands to a recursive type +error[E0720]: cannot resolve opaque type to a concrete type --> $DIR/recursive-impl-trait-type-indirect.rs:22:13 | LL | fn ptr() -> impl Sized { - | ^^^^^^^^^^ expands to a recursive type - | - = note: expanded type is `*const impl Sized` + | ^^^^^^^^^^ recursive opaque type +LL | +LL | &ptr() as *const _ + | ------------------ returning here with type `*const impl Sized` -error[E0720]: opaque type expands to a recursive type +error[E0720]: cannot resolve opaque type to a concrete type --> $DIR/recursive-impl-trait-type-indirect.rs:27:16 | LL | fn fn_ptr() -> impl Sized { - | ^^^^^^^^^^ expands to a recursive type - | - = note: expanded type is `fn() -> impl Sized` + | ^^^^^^^^^^ recursive opaque type +LL | +LL | fn_ptr as fn() -> _ + | ------------------- returning here with type `fn() -> impl Sized` -error[E0720]: opaque type expands to a recursive type +error[E0720]: cannot resolve opaque type to a concrete type --> $DIR/recursive-impl-trait-type-indirect.rs:32:25 | -LL | fn closure_capture() -> impl Sized { - | ^^^^^^^^^^ expands to a recursive type - | - = note: expanded type is `[closure@$DIR/recursive-impl-trait-type-indirect.rs:35:5: 37:6 x:impl Sized]` +LL | fn closure_capture() -> impl Sized { + | ^^^^^^^^^^ recursive opaque type +... +LL | / move || { +LL | | x; +LL | | } + | |_____- returning here with type `[closure@$DIR/recursive-impl-trait-type-indirect.rs:35:5: 37:6 x:impl Sized]` -error[E0720]: opaque type expands to a recursive type +error[E0720]: cannot resolve opaque type to a concrete type --> $DIR/recursive-impl-trait-type-indirect.rs:40:29 | -LL | fn closure_ref_capture() -> impl Sized { - | ^^^^^^^^^^ expands to a recursive type - | - = note: expanded type is `[closure@$DIR/recursive-impl-trait-type-indirect.rs:43:5: 45:6 x:impl Sized]` +LL | fn closure_ref_capture() -> impl Sized { + | ^^^^^^^^^^ recursive opaque type +... +LL | / move || { +LL | | &x; +LL | | } + | |_____- returning here with type `[closure@$DIR/recursive-impl-trait-type-indirect.rs:43:5: 45:6 x:impl Sized]` -error[E0720]: opaque type expands to a recursive type +error[E0720]: cannot resolve opaque type to a concrete type --> $DIR/recursive-impl-trait-type-indirect.rs:48:21 | LL | fn closure_sig() -> impl Sized { - | ^^^^^^^^^^ expands to a recursive type - | - = note: expanded type is `[closure@$DIR/recursive-impl-trait-type-indirect.rs:50:5: 50:21]` + | ^^^^^^^^^^ recursive opaque type +LL | +LL | || closure_sig() + | ---------------- returning here with type `[closure@$DIR/recursive-impl-trait-type-indirect.rs:50:5: 50:21]` -error[E0720]: opaque type expands to a recursive type +error[E0720]: cannot resolve opaque type to a concrete type --> $DIR/recursive-impl-trait-type-indirect.rs:53:23 | LL | fn generator_sig() -> impl Sized { - | ^^^^^^^^^^ expands to a recursive type - | - = note: expanded type is `[closure@$DIR/recursive-impl-trait-type-indirect.rs:55:5: 55:23]` + | ^^^^^^^^^^ recursive opaque type +LL | +LL | || generator_sig() + | ------------------ returning here with type `[closure@$DIR/recursive-impl-trait-type-indirect.rs:55:5: 55:23]` -error[E0720]: opaque type expands to a recursive type +error[E0720]: cannot resolve opaque type to a concrete type --> $DIR/recursive-impl-trait-type-indirect.rs:58:27 | -LL | fn generator_capture() -> impl Sized { - | ^^^^^^^^^^ expands to a recursive type - | - = note: expanded type is `[generator@$DIR/recursive-impl-trait-type-indirect.rs:61:5: 64:6 x:impl Sized {()}]` +LL | fn generator_capture() -> impl Sized { + | ^^^^^^^^^^ recursive opaque type +... +LL | / move || { +LL | | yield; +LL | | x; +LL | | } + | |_____- returning here with type `[generator@$DIR/recursive-impl-trait-type-indirect.rs:61:5: 64:6 x:impl Sized {()}]` -error[E0720]: opaque type expands to a recursive type +error[E0720]: cannot resolve opaque type to a concrete type --> $DIR/recursive-impl-trait-type-indirect.rs:67:35 | LL | fn substs_change() -> impl Sized { - | ^^^^^^^^^^ expands to a recursive type - | - = note: expanded type is `(impl Sized,)` + | ^^^^^^^^^^ recursive opaque type +LL | +LL | (substs_change::<&T>(),) + | ------------------------ returning here with type `(impl Sized,)` -error[E0720]: opaque type expands to a recursive type +error[E0720]: cannot resolve opaque type to a concrete type --> $DIR/recursive-impl-trait-type-indirect.rs:72:24 | -LL | fn generator_hold() -> impl Sized { - | ^^^^^^^^^^ expands to a recursive type - | - = note: expanded type is `[generator@$DIR/recursive-impl-trait-type-indirect.rs:74:5: 78:6 {impl Sized, ()}]` +LL | fn generator_hold() -> impl Sized { + | ^^^^^^^^^^ recursive opaque type +LL | +LL | / move || { +LL | | let x = generator_hold(); +LL | | yield; +LL | | x; +LL | | } + | |_____- returning here with type `[generator@$DIR/recursive-impl-trait-type-indirect.rs:74:5: 78:6 {impl Sized, ()}]` -error[E0720]: opaque type expands to a recursive type +error[E0720]: cannot resolve opaque type to a concrete type --> $DIR/recursive-impl-trait-type-indirect.rs:86:26 | LL | fn mutual_recursion() -> impl Sync { - | ^^^^^^^^^ expands to a recursive type - | - = note: type resolves to itself + | ^^^^^^^^^ recursive opaque type +LL | +LL | mutual_recursion_b() + | -------------------- returning here with type `impl Sized` +... +LL | fn mutual_recursion_b() -> impl Sized { + | ---------- returning this opaque type `impl Sized` -error[E0720]: opaque type expands to a recursive type +error[E0720]: cannot resolve opaque type to a concrete type --> $DIR/recursive-impl-trait-type-indirect.rs:91:28 | +LL | fn mutual_recursion() -> impl Sync { + | --------- returning this opaque type `impl std::marker::Sync` +... LL | fn mutual_recursion_b() -> impl Sized { - | ^^^^^^^^^^ expands to a recursive type - | - = note: type resolves to itself + | ^^^^^^^^^^ recursive opaque type +LL | +LL | mutual_recursion() + | ------------------ returning here with type `impl std::marker::Sync` error: aborting due to 14 previous errors diff --git a/src/test/ui/impl-trait/recursive-impl-trait-type-through-non-recursive.rs b/src/test/ui/impl-trait/recursive-impl-trait-type-through-non-recursive.rs index cfd9c0ec5b45..818e40365394 100644 --- a/src/test/ui/impl-trait/recursive-impl-trait-type-through-non-recursive.rs +++ b/src/test/ui/impl-trait/recursive-impl-trait-type-through-non-recursive.rs @@ -4,21 +4,21 @@ fn id(t: T) -> impl Sized { t } -fn recursive_id() -> impl Sized { //~ ERROR opaque type expands to a recursive type +fn recursive_id() -> impl Sized { //~ ERROR cannot resolve opaque type id(recursive_id2()) } -fn recursive_id2() -> impl Sized { //~ ERROR opaque type expands to a recursive type +fn recursive_id2() -> impl Sized { //~ ERROR cannot resolve opaque type id(recursive_id()) } fn wrap(t: T) -> impl Sized { (t,) } -fn recursive_wrap() -> impl Sized { //~ ERROR opaque type expands to a recursive type +fn recursive_wrap() -> impl Sized { //~ ERROR cannot resolve opaque type wrap(recursive_wrap2()) } -fn recursive_wrap2() -> impl Sized { //~ ERROR opaque type expands to a recursive type +fn recursive_wrap2() -> impl Sized { //~ ERROR cannot resolve opaque type wrap(recursive_wrap()) } diff --git a/src/test/ui/impl-trait/recursive-impl-trait-type-through-non-recursive.stderr b/src/test/ui/impl-trait/recursive-impl-trait-type-through-non-recursive.stderr index 73c12f6137d2..65e0b8882c42 100644 --- a/src/test/ui/impl-trait/recursive-impl-trait-type-through-non-recursive.stderr +++ b/src/test/ui/impl-trait/recursive-impl-trait-type-through-non-recursive.stderr @@ -1,34 +1,46 @@ -error[E0720]: opaque type expands to a recursive type +error[E0720]: cannot resolve opaque type to a concrete type --> $DIR/recursive-impl-trait-type-through-non-recursive.rs:7:22 | +LL | fn id(t: T) -> impl Sized { t } + | ---------- returning this opaque type `impl Sized` +LL | LL | fn recursive_id() -> impl Sized { - | ^^^^^^^^^^ expands to a recursive type - | - = note: type resolves to itself + | ^^^^^^^^^^ recursive opaque type +LL | id(recursive_id2()) + | ------------------- returning here with type `impl Sized` -error[E0720]: opaque type expands to a recursive type +error[E0720]: cannot resolve opaque type to a concrete type --> $DIR/recursive-impl-trait-type-through-non-recursive.rs:11:23 | +LL | fn id(t: T) -> impl Sized { t } + | ---------- returning this opaque type `impl Sized` +... LL | fn recursive_id2() -> impl Sized { - | ^^^^^^^^^^ expands to a recursive type - | - = note: type resolves to itself + | ^^^^^^^^^^ recursive opaque type +LL | id(recursive_id()) + | ------------------ returning here with type `impl Sized` -error[E0720]: opaque type expands to a recursive type +error[E0720]: cannot resolve opaque type to a concrete type --> $DIR/recursive-impl-trait-type-through-non-recursive.rs:17:24 | +LL | fn wrap(t: T) -> impl Sized { (t,) } + | ---------- returning this opaque type `impl Sized` +LL | LL | fn recursive_wrap() -> impl Sized { - | ^^^^^^^^^^ expands to a recursive type - | - = note: expanded type is `((impl Sized,),)` + | ^^^^^^^^^^ recursive opaque type +LL | wrap(recursive_wrap2()) + | ----------------------- returning here with type `impl Sized` -error[E0720]: opaque type expands to a recursive type +error[E0720]: cannot resolve opaque type to a concrete type --> $DIR/recursive-impl-trait-type-through-non-recursive.rs:21:25 | +LL | fn wrap(t: T) -> impl Sized { (t,) } + | ---------- returning this opaque type `impl Sized` +... LL | fn recursive_wrap2() -> impl Sized { - | ^^^^^^^^^^ expands to a recursive type - | - = note: expanded type is `((impl Sized,),)` + | ^^^^^^^^^^ recursive opaque type +LL | wrap(recursive_wrap()) + | ---------------------- returning here with type `impl Sized` error: aborting due to 4 previous errors diff --git a/src/test/ui/impl-trait/where-allowed-2.rs b/src/test/ui/impl-trait/where-allowed-2.rs index f7744ef1b3ea..462508f306ef 100644 --- a/src/test/ui/impl-trait/where-allowed-2.rs +++ b/src/test/ui/impl-trait/where-allowed-2.rs @@ -3,7 +3,6 @@ use std::fmt::Debug; // Disallowed -fn in_adt_in_return() -> Vec { panic!() } -//~^ ERROR opaque type expands to a recursive type +fn in_adt_in_return() -> Vec { panic!() } //~ ERROR cannot resolve opaque type fn main() {} diff --git a/src/test/ui/impl-trait/where-allowed-2.stderr b/src/test/ui/impl-trait/where-allowed-2.stderr index 1de15014c1f8..6c0e0a4c9a38 100644 --- a/src/test/ui/impl-trait/where-allowed-2.stderr +++ b/src/test/ui/impl-trait/where-allowed-2.stderr @@ -1,10 +1,12 @@ -error[E0720]: opaque type expands to a recursive type +error[E0720]: cannot resolve opaque type to a concrete type --> $DIR/where-allowed-2.rs:6:30 | LL | fn in_adt_in_return() -> Vec { panic!() } - | ^^^^^^^^^^ expands to a recursive type + | ^^^^^^^^^^ -------- this returned value is of `!` type + | | + | cannot resolve to a concrete type | - = note: type resolves to itself + = help: this error will resolve once the item's body returns a concrete type error: aborting due to previous error From 8f12485335f506f4c9633305f323e55cdc3c8c2b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Mon, 15 Jun 2020 12:11:28 -0700 Subject: [PATCH 065/123] review comments --- src/librustc_typeck/check/mod.rs | 22 +++++---------- .../impl-trait/binding-without-value.stderr | 4 +-- .../infinite-impl-trait-issue-38064.stderr | 4 +-- .../recursive-impl-trait-type-direct.stderr | 2 +- .../recursive-impl-trait-type-indirect.stderr | 28 +++++++++---------- ...pl-trait-type-through-non-recursive.stderr | 8 +++--- src/test/ui/impl-trait/where-allowed-2.stderr | 4 +-- 7 files changed, 32 insertions(+), 40 deletions(-) diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index d4db32abe2a1..1fff8fff9c03 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -1741,8 +1741,7 @@ fn get_owner_return_paths( /// If all the return expressions evaluate to `!`, then we explain that the error will go away /// after changing it. This can happen when a user uses `panic!()` or similar as a placeholder. fn opaque_type_cycle_error(tcx: TyCtxt<'tcx>, def_id: LocalDefId, span: Span) { - let mut err = - struct_span_err!(tcx.sess, span, E0720, "cannot resolve opaque type to a concrete type"); + let mut err = struct_span_err!(tcx.sess, span, E0720, "cannot resolve opaque type"); let mut label = false; if let Some((hir_id, visitor)) = get_owner_return_paths(tcx, def_id) { @@ -1751,7 +1750,6 @@ fn opaque_type_cycle_error(tcx: TyCtxt<'tcx>, def_id: LocalDefId, span: Span) { .returns .iter() .filter_map(|expr| tables.node_type_opt(expr.hir_id)) - .map(|ty| tcx.infer_ctxt().enter(|infcx| infcx.resolve_vars_if_possible(&ty))) .all(|ty| matches!(ty.kind, ty::Never)) { let spans = visitor @@ -1782,9 +1780,6 @@ fn opaque_type_cycle_error(tcx: TyCtxt<'tcx>, def_id: LocalDefId, span: Span) { .iter() .filter_map(|e| tables.node_type_opt(e.hir_id).map(|t| (e.span, t))) .filter(|(_, ty)| !matches!(ty.kind, ty::Never)) - .map(|(sp, ty)| { - (sp, tcx.infer_ctxt().enter(|infcx| infcx.resolve_vars_if_possible(&ty))) - }) { struct VisitTypes(Vec); impl<'tcx> ty::fold::TypeVisitor<'tcx> for VisitTypes { @@ -1812,7 +1807,7 @@ fn opaque_type_cycle_error(tcx: TyCtxt<'tcx>, def_id: LocalDefId, span: Span) { } } if !label { - err.span_label(span, "cannot resolve to a concrete type"); + err.span_label(span, "cannot resolve opaque type"); } err.emit(); } @@ -1824,9 +1819,9 @@ fn binding_opaque_type_cycle_error( span: Span, partially_expanded_type: Ty<'tcx>, ) { - let mut err = - struct_span_err!(tcx.sess, span, E0720, "cannot resolve opaque type to a concrete type"); - err.span_label(span, "cannot resolve to a concrete type"); + let mut err = struct_span_err!(tcx.sess, span, E0720, "cannot resolve opaque type"); + err.span_label(span, "cannot resolve opaque type"); + // Find the the owner that declared this `impl Trait` type. let hir_id = tcx.hir().as_local_hir_id(def_id); let mut prev_hir_id = hir_id; let mut hir_id = tcx.hir().get_parent_node(hir_id); @@ -1855,15 +1850,12 @@ fn binding_opaque_type_cycle_error( let hir_id = tcx.hir().as_local_hir_id(def_id); let tables = tcx.typeck_tables_of(tcx.hir().local_def_id(tcx.hir().get_parent_item(hir_id))); - let ty = tables.node_type_opt(expr.hir_id); - if let Some(ty) = - tcx.infer_ctxt().enter(|infcx| infcx.resolve_vars_if_possible(&ty)) - { + if let Some(ty) = tables.node_type_opt(expr.hir_id) { err.span_label( expr.span, &format!( "this is of type `{}`, which doesn't constrain \ - `{}` enough to arrive to a concrete type", + `{}` enough to arrive to a concrete type", ty, partially_expanded_type ), ); diff --git a/src/test/ui/impl-trait/binding-without-value.stderr b/src/test/ui/impl-trait/binding-without-value.stderr index 1898af5b63ee..0d2faeaf85d1 100644 --- a/src/test/ui/impl-trait/binding-without-value.stderr +++ b/src/test/ui/impl-trait/binding-without-value.stderr @@ -1,8 +1,8 @@ -error[E0720]: cannot resolve opaque type to a concrete type +error[E0720]: cannot resolve opaque type --> $DIR/binding-without-value.rs:5:13 | LL | let _ : impl Copy; - | - ^^^^^^^^^ cannot resolve to a concrete type + | - ^^^^^^^^^ cannot resolve opaque type | | | this binding might not have a concrete type | diff --git a/src/test/ui/impl-trait/issues/infinite-impl-trait-issue-38064.stderr b/src/test/ui/impl-trait/issues/infinite-impl-trait-issue-38064.stderr index a38fb7cb56e9..c538b77098a2 100644 --- a/src/test/ui/impl-trait/issues/infinite-impl-trait-issue-38064.stderr +++ b/src/test/ui/impl-trait/issues/infinite-impl-trait-issue-38064.stderr @@ -1,4 +1,4 @@ -error[E0720]: cannot resolve opaque type to a concrete type +error[E0720]: cannot resolve opaque type --> $DIR/infinite-impl-trait-issue-38064.rs:8:13 | LL | fn foo() -> impl Quux { @@ -10,7 +10,7 @@ LL | Foo(bar()) LL | fn bar() -> impl Quux { | --------- returning this opaque type `foo::Foo` -error[E0720]: cannot resolve opaque type to a concrete type +error[E0720]: cannot resolve opaque type --> $DIR/infinite-impl-trait-issue-38064.rs:14:13 | LL | fn foo() -> impl Quux { diff --git a/src/test/ui/impl-trait/recursive-impl-trait-type-direct.stderr b/src/test/ui/impl-trait/recursive-impl-trait-type-direct.stderr index 5149d42370c7..5a3027ec751a 100644 --- a/src/test/ui/impl-trait/recursive-impl-trait-type-direct.stderr +++ b/src/test/ui/impl-trait/recursive-impl-trait-type-direct.stderr @@ -1,4 +1,4 @@ -error[E0720]: cannot resolve opaque type to a concrete type +error[E0720]: cannot resolve opaque type --> $DIR/recursive-impl-trait-type-direct.rs:5:14 | LL | fn test() -> impl Sized { diff --git a/src/test/ui/impl-trait/recursive-impl-trait-type-indirect.stderr b/src/test/ui/impl-trait/recursive-impl-trait-type-indirect.stderr index 0bf362e9a6d4..75ff9e078cc2 100644 --- a/src/test/ui/impl-trait/recursive-impl-trait-type-indirect.stderr +++ b/src/test/ui/impl-trait/recursive-impl-trait-type-indirect.stderr @@ -1,4 +1,4 @@ -error[E0720]: cannot resolve opaque type to a concrete type +error[E0720]: cannot resolve opaque type --> $DIR/recursive-impl-trait-type-indirect.rs:7:22 | LL | fn option(i: i32) -> impl Sized { @@ -9,7 +9,7 @@ LL | if i < 0 { None } else { Some((option(i - 1), i)) } | | | returning here with type `std::option::Option<(impl Sized, i32)>` -error[E0720]: cannot resolve opaque type to a concrete type +error[E0720]: cannot resolve opaque type --> $DIR/recursive-impl-trait-type-indirect.rs:12:15 | LL | fn tuple() -> impl Sized { @@ -18,7 +18,7 @@ LL | LL | (tuple(),) | ---------- returning here with type `(impl Sized,)` -error[E0720]: cannot resolve opaque type to a concrete type +error[E0720]: cannot resolve opaque type --> $DIR/recursive-impl-trait-type-indirect.rs:17:15 | LL | fn array() -> impl Sized { @@ -27,7 +27,7 @@ LL | LL | [array()] | --------- returning here with type `[impl Sized; 1]` -error[E0720]: cannot resolve opaque type to a concrete type +error[E0720]: cannot resolve opaque type --> $DIR/recursive-impl-trait-type-indirect.rs:22:13 | LL | fn ptr() -> impl Sized { @@ -36,7 +36,7 @@ LL | LL | &ptr() as *const _ | ------------------ returning here with type `*const impl Sized` -error[E0720]: cannot resolve opaque type to a concrete type +error[E0720]: cannot resolve opaque type --> $DIR/recursive-impl-trait-type-indirect.rs:27:16 | LL | fn fn_ptr() -> impl Sized { @@ -45,7 +45,7 @@ LL | LL | fn_ptr as fn() -> _ | ------------------- returning here with type `fn() -> impl Sized` -error[E0720]: cannot resolve opaque type to a concrete type +error[E0720]: cannot resolve opaque type --> $DIR/recursive-impl-trait-type-indirect.rs:32:25 | LL | fn closure_capture() -> impl Sized { @@ -56,7 +56,7 @@ LL | | x; LL | | } | |_____- returning here with type `[closure@$DIR/recursive-impl-trait-type-indirect.rs:35:5: 37:6 x:impl Sized]` -error[E0720]: cannot resolve opaque type to a concrete type +error[E0720]: cannot resolve opaque type --> $DIR/recursive-impl-trait-type-indirect.rs:40:29 | LL | fn closure_ref_capture() -> impl Sized { @@ -67,7 +67,7 @@ LL | | &x; LL | | } | |_____- returning here with type `[closure@$DIR/recursive-impl-trait-type-indirect.rs:43:5: 45:6 x:impl Sized]` -error[E0720]: cannot resolve opaque type to a concrete type +error[E0720]: cannot resolve opaque type --> $DIR/recursive-impl-trait-type-indirect.rs:48:21 | LL | fn closure_sig() -> impl Sized { @@ -76,7 +76,7 @@ LL | LL | || closure_sig() | ---------------- returning here with type `[closure@$DIR/recursive-impl-trait-type-indirect.rs:50:5: 50:21]` -error[E0720]: cannot resolve opaque type to a concrete type +error[E0720]: cannot resolve opaque type --> $DIR/recursive-impl-trait-type-indirect.rs:53:23 | LL | fn generator_sig() -> impl Sized { @@ -85,7 +85,7 @@ LL | LL | || generator_sig() | ------------------ returning here with type `[closure@$DIR/recursive-impl-trait-type-indirect.rs:55:5: 55:23]` -error[E0720]: cannot resolve opaque type to a concrete type +error[E0720]: cannot resolve opaque type --> $DIR/recursive-impl-trait-type-indirect.rs:58:27 | LL | fn generator_capture() -> impl Sized { @@ -97,7 +97,7 @@ LL | | x; LL | | } | |_____- returning here with type `[generator@$DIR/recursive-impl-trait-type-indirect.rs:61:5: 64:6 x:impl Sized {()}]` -error[E0720]: cannot resolve opaque type to a concrete type +error[E0720]: cannot resolve opaque type --> $DIR/recursive-impl-trait-type-indirect.rs:67:35 | LL | fn substs_change() -> impl Sized { @@ -106,7 +106,7 @@ LL | LL | (substs_change::<&T>(),) | ------------------------ returning here with type `(impl Sized,)` -error[E0720]: cannot resolve opaque type to a concrete type +error[E0720]: cannot resolve opaque type --> $DIR/recursive-impl-trait-type-indirect.rs:72:24 | LL | fn generator_hold() -> impl Sized { @@ -119,7 +119,7 @@ LL | | x; LL | | } | |_____- returning here with type `[generator@$DIR/recursive-impl-trait-type-indirect.rs:74:5: 78:6 {impl Sized, ()}]` -error[E0720]: cannot resolve opaque type to a concrete type +error[E0720]: cannot resolve opaque type --> $DIR/recursive-impl-trait-type-indirect.rs:86:26 | LL | fn mutual_recursion() -> impl Sync { @@ -131,7 +131,7 @@ LL | mutual_recursion_b() LL | fn mutual_recursion_b() -> impl Sized { | ---------- returning this opaque type `impl Sized` -error[E0720]: cannot resolve opaque type to a concrete type +error[E0720]: cannot resolve opaque type --> $DIR/recursive-impl-trait-type-indirect.rs:91:28 | LL | fn mutual_recursion() -> impl Sync { diff --git a/src/test/ui/impl-trait/recursive-impl-trait-type-through-non-recursive.stderr b/src/test/ui/impl-trait/recursive-impl-trait-type-through-non-recursive.stderr index 65e0b8882c42..fbc58837a8e9 100644 --- a/src/test/ui/impl-trait/recursive-impl-trait-type-through-non-recursive.stderr +++ b/src/test/ui/impl-trait/recursive-impl-trait-type-through-non-recursive.stderr @@ -1,4 +1,4 @@ -error[E0720]: cannot resolve opaque type to a concrete type +error[E0720]: cannot resolve opaque type --> $DIR/recursive-impl-trait-type-through-non-recursive.rs:7:22 | LL | fn id(t: T) -> impl Sized { t } @@ -9,7 +9,7 @@ LL | fn recursive_id() -> impl Sized { LL | id(recursive_id2()) | ------------------- returning here with type `impl Sized` -error[E0720]: cannot resolve opaque type to a concrete type +error[E0720]: cannot resolve opaque type --> $DIR/recursive-impl-trait-type-through-non-recursive.rs:11:23 | LL | fn id(t: T) -> impl Sized { t } @@ -20,7 +20,7 @@ LL | fn recursive_id2() -> impl Sized { LL | id(recursive_id()) | ------------------ returning here with type `impl Sized` -error[E0720]: cannot resolve opaque type to a concrete type +error[E0720]: cannot resolve opaque type --> $DIR/recursive-impl-trait-type-through-non-recursive.rs:17:24 | LL | fn wrap(t: T) -> impl Sized { (t,) } @@ -31,7 +31,7 @@ LL | fn recursive_wrap() -> impl Sized { LL | wrap(recursive_wrap2()) | ----------------------- returning here with type `impl Sized` -error[E0720]: cannot resolve opaque type to a concrete type +error[E0720]: cannot resolve opaque type --> $DIR/recursive-impl-trait-type-through-non-recursive.rs:21:25 | LL | fn wrap(t: T) -> impl Sized { (t,) } diff --git a/src/test/ui/impl-trait/where-allowed-2.stderr b/src/test/ui/impl-trait/where-allowed-2.stderr index 6c0e0a4c9a38..b8e06725cbcd 100644 --- a/src/test/ui/impl-trait/where-allowed-2.stderr +++ b/src/test/ui/impl-trait/where-allowed-2.stderr @@ -1,10 +1,10 @@ -error[E0720]: cannot resolve opaque type to a concrete type +error[E0720]: cannot resolve opaque type --> $DIR/where-allowed-2.rs:6:30 | LL | fn in_adt_in_return() -> Vec { panic!() } | ^^^^^^^^^^ -------- this returned value is of `!` type | | - | cannot resolve to a concrete type + | cannot resolve opaque type | = help: this error will resolve once the item's body returns a concrete type From 5cedf5dfba1c83f2fe3e2fcb7acbc20c6e34604a Mon Sep 17 00:00:00 2001 From: Gary Guo Date: Mon, 15 Jun 2020 21:59:09 +0100 Subject: [PATCH 066/123] Refactor usage of Needs in typeck --- src/librustc_typeck/check/autoderef.rs | 12 +- src/librustc_typeck/check/callee.rs | 8 +- src/librustc_typeck/check/coercion.rs | 5 +- src/librustc_typeck/check/expr.rs | 87 ++--- src/librustc_typeck/check/method/confirm.rs | 4 +- src/librustc_typeck/check/mod.rs | 153 +-------- src/librustc_typeck/check/op.rs | 6 +- src/librustc_typeck/check/place_op.rs | 334 ++++++++++++++++++++ src/librustc_typeck/check/reconciliation.rs | 150 --------- 9 files changed, 382 insertions(+), 377 deletions(-) create mode 100644 src/librustc_typeck/check/place_op.rs delete mode 100644 src/librustc_typeck/check/reconciliation.rs diff --git a/src/librustc_typeck/check/autoderef.rs b/src/librustc_typeck/check/autoderef.rs index 73d4e2b78206..2570025959cb 100644 --- a/src/librustc_typeck/check/autoderef.rs +++ b/src/librustc_typeck/check/autoderef.rs @@ -1,5 +1,5 @@ use super::method::MethodCallee; -use super::{FnCtxt, Needs, PlaceOp}; +use super::{FnCtxt, PlaceOp}; use rustc_errors::struct_span_err; use rustc_hir as hir; @@ -170,14 +170,13 @@ impl<'a, 'tcx> Autoderef<'a, 'tcx> { } /// Returns the adjustment steps. - pub fn adjust_steps(&self, fcx: &FnCtxt<'a, 'tcx>, needs: Needs) -> Vec> { - fcx.register_infer_ok_obligations(self.adjust_steps_as_infer_ok(fcx, needs)) + pub fn adjust_steps(&self, fcx: &FnCtxt<'a, 'tcx>) -> Vec> { + fcx.register_infer_ok_obligations(self.adjust_steps_as_infer_ok(fcx)) } pub fn adjust_steps_as_infer_ok( &self, fcx: &FnCtxt<'a, 'tcx>, - needs: Needs, ) -> InferOk<'tcx, Vec>> { let mut obligations = vec![]; let targets = self.steps.iter().skip(1).map(|&(ty, _)| ty).chain(iter::once(self.cur_ty)); @@ -186,7 +185,7 @@ impl<'a, 'tcx> Autoderef<'a, 'tcx> { .iter() .map(|&(source, kind)| { if let AutoderefKind::Overloaded = kind { - fcx.try_overloaded_deref(self.span, source, needs).and_then( + fcx.try_overloaded_deref(self.span, source).and_then( |InferOk { value: method, obligations: o }| { obligations.extend(o); if let ty::Ref(region, _, mutbl) = method.sig.output().kind { @@ -266,8 +265,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { &self, span: Span, base_ty: Ty<'tcx>, - needs: Needs, ) -> Option>> { - self.try_overloaded_place_op(span, base_ty, &[], needs, PlaceOp::Deref) + self.try_overloaded_place_op(span, base_ty, &[], PlaceOp::Deref) } } diff --git a/src/librustc_typeck/check/callee.rs b/src/librustc_typeck/check/callee.rs index f86b7f07b7fc..916fe9afc876 100644 --- a/src/librustc_typeck/check/callee.rs +++ b/src/librustc_typeck/check/callee.rs @@ -1,6 +1,6 @@ use super::autoderef::Autoderef; use super::method::MethodCallee; -use super::{Expectation, FnCtxt, Needs, TupleArgumentsFlag}; +use super::{Expectation, FnCtxt, TupleArgumentsFlag}; use crate::type_error_struct; use rustc_errors::{struct_span_err, Applicability, DiagnosticBuilder}; @@ -115,7 +115,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // If the callee is a bare function or a closure, then we're all set. match adjusted_ty.kind { ty::FnDef(..) | ty::FnPtr(_) => { - let adjustments = autoderef.adjust_steps(self, Needs::None); + let adjustments = autoderef.adjust_steps(self); self.apply_adjustments(callee_expr, adjustments); return Some(CallStep::Builtin(adjusted_ty)); } @@ -135,7 +135,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { &closure_sig, ) .0; - let adjustments = autoderef.adjust_steps(self, Needs::None); + let adjustments = autoderef.adjust_steps(self); self.record_deferred_call_resolution( def_id, DeferredCallResolution { @@ -176,7 +176,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.try_overloaded_call_traits(call_expr, adjusted_ty, Some(arg_exprs)) .or_else(|| self.try_overloaded_call_traits(call_expr, adjusted_ty, None)) .map(|(autoref, method)| { - let mut adjustments = autoderef.adjust_steps(self, Needs::None); + let mut adjustments = autoderef.adjust_steps(self); adjustments.extend(autoref); self.apply_adjustments(callee_expr, adjustments); CallStep::Overloaded(method) diff --git a/src/librustc_typeck/check/coercion.rs b/src/librustc_typeck/check/coercion.rs index 96c0d98ab061..085bb384e124 100644 --- a/src/librustc_typeck/check/coercion.rs +++ b/src/librustc_typeck/check/coercion.rs @@ -51,7 +51,7 @@ //! we may want to adjust precisely when coercions occur. use crate::astconv::AstConv; -use crate::check::{FnCtxt, Needs}; +use crate::check::FnCtxt; use rustc_errors::{struct_span_err, DiagnosticBuilder}; use rustc_hir as hir; use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; @@ -421,9 +421,8 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { return success(vec![], ty, obligations); } - let needs = Needs::maybe_mut_place(mutbl_b); let InferOk { value: mut adjustments, obligations: o } = - autoderef.adjust_steps_as_infer_ok(self, needs); + autoderef.adjust_steps_as_infer_ok(self); obligations.extend(o); obligations.extend(autoderef.into_obligations()); diff --git a/src/librustc_typeck/check/expr.rs b/src/librustc_typeck/check/expr.rs index bc3ef73d851e..c15d747be529 100644 --- a/src/librustc_typeck/check/expr.rs +++ b/src/librustc_typeck/check/expr.rs @@ -29,9 +29,7 @@ use rustc_hir::{ExprKind, QPath}; use rustc_infer::infer; use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; use rustc_middle::ty; -use rustc_middle::ty::adjustment::{ - Adjust, Adjustment, AllowTwoPhase, AutoBorrow, AutoBorrowMutability, -}; +use rustc_middle::ty::adjustment::{Adjust, Adjustment, AllowTwoPhase}; use rustc_middle::ty::Ty; use rustc_middle::ty::TypeFoldable; use rustc_middle::ty::{AdtKind, Visibility}; @@ -113,12 +111,21 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.check_expr_with_expectation(expr, ExpectHasType(expected)) } - pub(super) fn check_expr_with_expectation( + fn check_expr_with_expectation_and_needs( &self, expr: &'tcx hir::Expr<'tcx>, expected: Expectation<'tcx>, + needs: Needs, ) -> Ty<'tcx> { - self.check_expr_with_expectation_and_needs(expr, expected, Needs::None) + let ty = self.check_expr_with_expectation(expr, expected); + + // If the expression is used in a place whether mutable place is required + // e.g. LHS of assignment, perform the conversion. + if let Needs::MutPlace = needs { + self.convert_place_derefs_to_mutable(expr); + } + + ty } pub(super) fn check_expr(&self, expr: &'tcx hir::Expr<'tcx>) -> Ty<'tcx> { @@ -143,11 +150,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// Note that inspecting a type's structure *directly* may expose the fact /// that there are actually multiple representations for `Error`, so avoid /// that when err needs to be handled differently. - fn check_expr_with_expectation_and_needs( + pub(super) fn check_expr_with_expectation( &self, expr: &'tcx hir::Expr<'tcx>, expected: Expectation<'tcx>, - needs: Needs, ) -> Ty<'tcx> { debug!(">> type-checking: expr={:?} expected={:?}", expr, expected); @@ -171,7 +177,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let old_diverges = self.diverges.replace(Diverges::Maybe); let old_has_errors = self.has_errors.replace(false); - let ty = self.check_expr_kind(expr, expected, needs); + let ty = self.check_expr_kind(expr, expected); // Warn for non-block expressions with diverging children. match expr.kind { @@ -213,9 +219,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { &self, expr: &'tcx hir::Expr<'tcx>, expected: Expectation<'tcx>, - needs: Needs, ) -> Ty<'tcx> { - debug!("check_expr_kind(expr={:?}, expected={:?}, needs={:?})", expr, expected, needs,); + debug!("check_expr_kind(expr={:?}, expected={:?})", expr, expected); let tcx = self.tcx; match expr.kind { @@ -226,9 +231,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.check_expr_assign(expr, expected, lhs, rhs, span) } ExprKind::AssignOp(op, ref lhs, ref rhs) => self.check_binop_assign(expr, op, lhs, rhs), - ExprKind::Unary(unop, ref oprnd) => { - self.check_expr_unary(unop, oprnd, expected, needs, expr) - } + ExprKind::Unary(unop, ref oprnd) => self.check_expr_unary(unop, oprnd, expected, expr), ExprKind::AddrOf(kind, mutbl, ref oprnd) => { self.check_expr_addr_of(kind, mutbl, oprnd, expected, expr) } @@ -264,7 +267,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ExprKind::Block(ref body, _) => self.check_block_with_expected(&body, expected), ExprKind::Call(ref callee, ref args) => self.check_call(expr, &callee, args, expected), ExprKind::MethodCall(ref segment, span, ref args, _) => { - self.check_method_call(expr, segment, span, args, expected, needs) + self.check_method_call(expr, segment, span, args, expected) } ExprKind::Cast(ref e, ref t) => self.check_expr_cast(e, t, expr), ExprKind::Type(ref e, ref t) => { @@ -281,8 +284,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ExprKind::Struct(ref qpath, fields, ref base_expr) => { self.check_expr_struct(expr, expected, qpath, fields, base_expr) } - ExprKind::Field(ref base, field) => self.check_field(expr, needs, &base, field), - ExprKind::Index(ref base, ref idx) => self.check_expr_index(base, idx, needs, expr), + ExprKind::Field(ref base, field) => self.check_field(expr, &base, field), + ExprKind::Index(ref base, ref idx) => self.check_expr_index(base, idx, expr), ExprKind::Yield(ref value, ref src) => self.check_expr_yield(value, expr, src), hir::ExprKind::Err => tcx.types.err, } @@ -302,7 +305,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { unop: hir::UnOp, oprnd: &'tcx hir::Expr<'tcx>, expected: Expectation<'tcx>, - needs: Needs, expr: &'tcx hir::Expr<'tcx>, ) -> Ty<'tcx> { let tcx = self.tcx; @@ -310,40 +312,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { hir::UnOp::UnNot | hir::UnOp::UnNeg => expected, hir::UnOp::UnDeref => NoExpectation, }; - let needs = match unop { - hir::UnOp::UnDeref => needs, - _ => Needs::None, - }; - let mut oprnd_t = self.check_expr_with_expectation_and_needs(&oprnd, expected_inner, needs); + let mut oprnd_t = self.check_expr_with_expectation(&oprnd, expected_inner); if !oprnd_t.references_error() { oprnd_t = self.structurally_resolved_type(expr.span, oprnd_t); match unop { hir::UnOp::UnDeref => { - if let Some(mt) = oprnd_t.builtin_deref(true) { - oprnd_t = mt.ty; - } else if let Some(ok) = self.try_overloaded_deref(expr.span, oprnd_t, needs) { - let method = self.register_infer_ok_obligations(ok); - if let ty::Ref(region, _, mutbl) = method.sig.inputs()[0].kind { - let mutbl = match mutbl { - hir::Mutability::Not => AutoBorrowMutability::Not, - hir::Mutability::Mut => AutoBorrowMutability::Mut { - // (It shouldn't actually matter for unary ops whether - // we enable two-phase borrows or not, since a unary - // op has no additional operands.) - allow_two_phase_borrow: AllowTwoPhase::No, - }, - }; - self.apply_adjustments( - oprnd, - vec![Adjustment { - kind: Adjust::Borrow(AutoBorrow::Ref(region, mutbl)), - target: method.sig.inputs()[0], - }], - ); - } - oprnd_t = self.make_overloaded_place_return_type(method).ty; - self.write_method_call(expr.hir_id, method); + if let Some(ty) = self.lookup_derefing(expr, oprnd, oprnd_t) { + oprnd_t = ty; } else { let mut err = type_error_struct!( tcx.sess, @@ -405,8 +381,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { _ => NoExpectation, } }); - let needs = Needs::maybe_mut_place(mutbl); - let ty = self.check_expr_with_expectation_and_needs(&oprnd, hint, needs); + let ty = + self.check_expr_with_expectation_and_needs(&oprnd, hint, Needs::maybe_mut_place(mutbl)); let tm = ty::TypeAndMut { ty, mutbl }; match kind { @@ -861,10 +837,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { span: Span, args: &'tcx [hir::Expr<'tcx>], expected: Expectation<'tcx>, - needs: Needs, ) -> Ty<'tcx> { let rcvr = &args[0]; - let rcvr_t = self.check_expr_with_needs(&rcvr, needs); + let rcvr_t = self.check_expr(&rcvr); // no need to check for bot/err -- callee does that let rcvr_t = self.structurally_resolved_type(args[0].span, rcvr_t); @@ -1443,11 +1418,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { fn check_field( &self, expr: &'tcx hir::Expr<'tcx>, - needs: Needs, base: &'tcx hir::Expr<'tcx>, field: Ident, ) -> Ty<'tcx> { - let expr_t = self.check_expr_with_needs(base, needs); + let expr_t = self.check_expr(base); let expr_t = self.structurally_resolved_type(base.span, expr_t); let mut private_candidate = None; let mut autoderef = self.autoderef(expr.span, expr_t); @@ -1467,7 +1441,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // of error recovery. self.write_field_index(expr.hir_id, index); if field.vis.is_accessible_from(def_scope, self.tcx) { - let adjustments = autoderef.adjust_steps(self, needs); + let adjustments = autoderef.adjust_steps(self); self.apply_adjustments(base, adjustments); autoderef.finalize(self); @@ -1482,7 +1456,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if let Ok(index) = fstr.parse::() { if fstr == index.to_string() { if let Some(field_ty) = tys.get(index) { - let adjustments = autoderef.adjust_steps(self, needs); + let adjustments = autoderef.adjust_steps(self); self.apply_adjustments(base, adjustments); autoderef.finalize(self); @@ -1721,10 +1695,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { &self, base: &'tcx hir::Expr<'tcx>, idx: &'tcx hir::Expr<'tcx>, - needs: Needs, expr: &'tcx hir::Expr<'tcx>, ) -> Ty<'tcx> { - let base_t = self.check_expr_with_needs(&base, needs); + let base_t = self.check_expr(&base); let idx_t = self.check_expr(&idx); if base_t.references_error() { @@ -1733,7 +1706,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { idx_t } else { let base_t = self.structurally_resolved_type(base.span, base_t); - match self.lookup_indexing(expr, base, base_t, idx_t, needs) { + match self.lookup_indexing(expr, base, base_t, idx_t) { Some((index_ty, element_ty)) => { // two-phase not needed because index_ty is never mutable self.demand_coerce(idx, idx_t, index_ty, None, AllowTwoPhase::No); diff --git a/src/librustc_typeck/check/method/confirm.rs b/src/librustc_typeck/check/method/confirm.rs index c0f1f356ef37..21c359abf0b0 100644 --- a/src/librustc_typeck/check/method/confirm.rs +++ b/src/librustc_typeck/check/method/confirm.rs @@ -1,7 +1,7 @@ use super::{probe, MethodCallee}; use crate::astconv::AstConv; -use crate::check::{callee, FnCtxt, Needs}; +use crate::check::{callee, FnCtxt}; use crate::hir::def_id::DefId; use crate::hir::GenericArg; use rustc_hir as hir; @@ -145,7 +145,7 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> { }; assert_eq!(n, pick.autoderefs); - let mut adjustments = autoderef.adjust_steps(self, Needs::None); + let mut adjustments = autoderef.adjust_steps(self); let mut target = autoderef.unambiguous_final_ty(self); diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index 82523f843aef..b1d32213b729 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -79,7 +79,7 @@ pub mod intrinsic; pub mod method; mod op; mod pat; -mod reconciliation; +mod place_op; mod regionck; mod upvar; mod wfcheck; @@ -115,7 +115,7 @@ use rustc_infer::infer::{InferCtxt, InferOk, InferResult, RegionVariableOrigin, use rustc_middle::hir::map::blocks::FnLikeNode; use rustc_middle::mir::interpret::ConstValue; use rustc_middle::ty::adjustment::{ - Adjust, Adjustment, AllowTwoPhase, AutoBorrow, AutoBorrowMutability, PointerCast, + Adjust, Adjustment, AllowTwoPhase, AutoBorrow, AutoBorrowMutability, }; use rustc_middle::ty::fold::{TypeFoldable, TypeFolder}; use rustc_middle::ty::query::Providers; @@ -156,7 +156,6 @@ use std::slice; use crate::require_c_abi_if_c_variadic; use crate::util::common::indenter; -use self::autoderef::Autoderef; use self::callee::DeferredCallResolution; use self::coercion::{CoerceMany, DynamicCoerceMany}; use self::compare_method::{compare_const_impl, compare_impl_method, compare_ty_impl}; @@ -3618,154 +3617,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ret_ty.builtin_deref(true).unwrap() } - fn lookup_indexing( - &self, - expr: &hir::Expr<'_>, - base_expr: &'tcx hir::Expr<'tcx>, - base_ty: Ty<'tcx>, - idx_ty: Ty<'tcx>, - needs: Needs, - ) -> Option<(/*index type*/ Ty<'tcx>, /*element type*/ Ty<'tcx>)> { - // FIXME(#18741) -- this is almost but not quite the same as the - // autoderef that normal method probing does. They could likely be - // consolidated. - - let mut autoderef = self.autoderef(base_expr.span, base_ty); - let mut result = None; - while result.is_none() && autoderef.next().is_some() { - result = self.try_index_step(expr, base_expr, &autoderef, needs, idx_ty); - } - autoderef.finalize(self); - result - } - - /// To type-check `base_expr[index_expr]`, we progressively autoderef - /// (and otherwise adjust) `base_expr`, looking for a type which either - /// supports builtin indexing or overloaded indexing. - /// This loop implements one step in that search; the autoderef loop - /// is implemented by `lookup_indexing`. - fn try_index_step( - &self, - expr: &hir::Expr<'_>, - base_expr: &hir::Expr<'_>, - autoderef: &Autoderef<'a, 'tcx>, - needs: Needs, - index_ty: Ty<'tcx>, - ) -> Option<(/*index type*/ Ty<'tcx>, /*element type*/ Ty<'tcx>)> { - let adjusted_ty = autoderef.unambiguous_final_ty(self); - debug!( - "try_index_step(expr={:?}, base_expr={:?}, adjusted_ty={:?}, \ - index_ty={:?})", - expr, base_expr, adjusted_ty, index_ty - ); - - for &unsize in &[false, true] { - let mut self_ty = adjusted_ty; - if unsize { - // We only unsize arrays here. - if let ty::Array(element_ty, _) = adjusted_ty.kind { - self_ty = self.tcx.mk_slice(element_ty); - } else { - continue; - } - } - - // If some lookup succeeds, write callee into table and extract index/element - // type from the method signature. - // If some lookup succeeded, install method in table - let input_ty = self.next_ty_var(TypeVariableOrigin { - kind: TypeVariableOriginKind::AutoDeref, - span: base_expr.span, - }); - let method = self.try_overloaded_place_op( - expr.span, - self_ty, - &[input_ty], - needs, - PlaceOp::Index, - ); - - let result = method.map(|ok| { - debug!("try_index_step: success, using overloaded indexing"); - let method = self.register_infer_ok_obligations(ok); - - let mut adjustments = autoderef.adjust_steps(self, needs); - if let ty::Ref(region, _, r_mutbl) = method.sig.inputs()[0].kind { - let mutbl = match r_mutbl { - hir::Mutability::Not => AutoBorrowMutability::Not, - hir::Mutability::Mut => AutoBorrowMutability::Mut { - // Indexing can be desugared to a method call, - // so maybe we could use two-phase here. - // See the documentation of AllowTwoPhase for why that's - // not the case today. - allow_two_phase_borrow: AllowTwoPhase::No, - }, - }; - adjustments.push(Adjustment { - kind: Adjust::Borrow(AutoBorrow::Ref(region, mutbl)), - target: self - .tcx - .mk_ref(region, ty::TypeAndMut { mutbl: r_mutbl, ty: adjusted_ty }), - }); - } - if unsize { - adjustments.push(Adjustment { - kind: Adjust::Pointer(PointerCast::Unsize), - target: method.sig.inputs()[0], - }); - } - self.apply_adjustments(base_expr, adjustments); - - self.write_method_call(expr.hir_id, method); - (input_ty, self.make_overloaded_place_return_type(method).ty) - }); - if result.is_some() { - return result; - } - } - - None - } - - fn resolve_place_op(&self, op: PlaceOp, is_mut: bool) -> (Option, Ident) { - let (tr, name) = match (op, is_mut) { - (PlaceOp::Deref, false) => (self.tcx.lang_items().deref_trait(), sym::deref), - (PlaceOp::Deref, true) => (self.tcx.lang_items().deref_mut_trait(), sym::deref_mut), - (PlaceOp::Index, false) => (self.tcx.lang_items().index_trait(), sym::index), - (PlaceOp::Index, true) => (self.tcx.lang_items().index_mut_trait(), sym::index_mut), - }; - (tr, Ident::with_dummy_span(name)) - } - - fn try_overloaded_place_op( - &self, - span: Span, - base_ty: Ty<'tcx>, - arg_tys: &[Ty<'tcx>], - needs: Needs, - op: PlaceOp, - ) -> Option>> { - debug!("try_overloaded_place_op({:?},{:?},{:?},{:?})", span, base_ty, needs, op); - - // Try Mut first, if needed. - let (mut_tr, mut_op) = self.resolve_place_op(op, true); - let method = match (needs, mut_tr) { - (Needs::MutPlace, Some(trait_did)) => { - self.lookup_method_in_trait(span, mut_op, trait_did, base_ty, Some(arg_tys)) - } - _ => None, - }; - - // Otherwise, fall back to the immutable version. - let (imm_tr, imm_op) = self.resolve_place_op(op, false); - match (method, imm_tr) { - (None, Some(trait_did)) => { - self.lookup_method_in_trait(span, imm_op, trait_did, base_ty, Some(arg_tys)) - } - (method, _) => method, - } - } - fn check_method_argument_types( &self, sp: Span, diff --git a/src/librustc_typeck/check/op.rs b/src/librustc_typeck/check/op.rs index a3a27dc138be..41088b079081 100644 --- a/src/librustc_typeck/check/op.rs +++ b/src/librustc_typeck/check/op.rs @@ -1,7 +1,7 @@ //! Code related to processing overloaded binary and unary operators. use super::method::MethodCallee; -use super::{FnCtxt, Needs}; +use super::FnCtxt; use rustc_errors::{self, struct_span_err, Applicability, DiagnosticBuilder}; use rustc_hir as hir; use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; @@ -165,7 +165,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // trait matching creating lifetime constraints that are too strict. // e.g., adding `&'a T` and `&'b T`, given `&'x T: Add<&'x T>`, will result // in `&'a T <: &'x T` and `&'b T <: &'x T`, instead of `'a = 'b = 'x`. - let lhs_ty = self.check_expr_with_needs(lhs_expr, Needs::None); + let lhs_ty = self.check_expr(lhs_expr); let fresh_var = self.next_ty_var(TypeVariableOrigin { kind: TypeVariableOriginKind::MiscVariable, span: lhs_expr.span, @@ -177,7 +177,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // equivalence on the LHS of an assign-op like `+=`; // overwritten or mutably-borrowed places cannot be // coerced to a supertype. - self.check_expr_with_needs(lhs_expr, Needs::MutPlace) + self.check_expr(lhs_expr) } }; let lhs_ty = self.resolve_vars_with_obligations(lhs_ty); diff --git a/src/librustc_typeck/check/place_op.rs b/src/librustc_typeck/check/place_op.rs new file mode 100644 index 000000000000..ce4b6f8baf91 --- /dev/null +++ b/src/librustc_typeck/check/place_op.rs @@ -0,0 +1,334 @@ +use crate::check::autoderef::Autoderef; +use crate::check::method::MethodCallee; +use crate::check::{FnCtxt, PlaceOp}; +use rustc_hir as hir; +use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; +use rustc_infer::infer::InferOk; +use rustc_middle::ty::adjustment::{Adjust, Adjustment, OverloadedDeref, PointerCast}; +use rustc_middle::ty::adjustment::{AllowTwoPhase, AutoBorrow, AutoBorrowMutability}; +use rustc_middle::ty::{self, Ty}; +use rustc_span::symbol::{sym, Ident}; +use rustc_span::Span; + +impl<'a, 'tcx> FnCtxt<'a, 'tcx> { + pub(super) fn lookup_derefing( + &self, + expr: &hir::Expr<'_>, + oprnd: &'tcx hir::Expr<'tcx>, + oprnd_ty: Ty<'tcx>, + ) -> Option> { + if let Some(mt) = oprnd_ty.builtin_deref(true) { + return Some(mt.ty); + } + + let ok = self.try_overloaded_deref(expr.span, oprnd_ty)?; + let method = self.register_infer_ok_obligations(ok); + if let ty::Ref(region, _, hir::Mutability::Not) = method.sig.inputs()[0].kind { + self.apply_adjustments( + oprnd, + vec![Adjustment { + kind: Adjust::Borrow(AutoBorrow::Ref(region, AutoBorrowMutability::Not)), + target: method.sig.inputs()[0], + }], + ); + } else { + span_bug!(expr.span, "input to deref is not a ref?"); + } + let ty = self.make_overloaded_place_return_type(method).ty; + self.write_method_call(expr.hir_id, method); + Some(ty) + } + + pub(super) fn lookup_indexing( + &self, + expr: &hir::Expr<'_>, + base_expr: &'tcx hir::Expr<'tcx>, + base_ty: Ty<'tcx>, + idx_ty: Ty<'tcx>, + ) -> Option<(/*index type*/ Ty<'tcx>, /*element type*/ Ty<'tcx>)> { + // FIXME(#18741) -- this is almost but not quite the same as the + // autoderef that normal method probing does. They could likely be + // consolidated. + + let mut autoderef = self.autoderef(base_expr.span, base_ty); + let mut result = None; + while result.is_none() && autoderef.next().is_some() { + result = self.try_index_step(expr, base_expr, &autoderef, idx_ty); + } + autoderef.finalize(self); + result + } + + /// To type-check `base_expr[index_expr]`, we progressively autoderef + /// (and otherwise adjust) `base_expr`, looking for a type which either + /// supports builtin indexing or overloaded indexing. + /// This loop implements one step in that search; the autoderef loop + /// is implemented by `lookup_indexing`. + fn try_index_step( + &self, + expr: &hir::Expr<'_>, + base_expr: &hir::Expr<'_>, + autoderef: &Autoderef<'a, 'tcx>, + index_ty: Ty<'tcx>, + ) -> Option<(/*index type*/ Ty<'tcx>, /*element type*/ Ty<'tcx>)> { + let adjusted_ty = autoderef.unambiguous_final_ty(self); + debug!( + "try_index_step(expr={:?}, base_expr={:?}, adjusted_ty={:?}, \ + index_ty={:?})", + expr, base_expr, adjusted_ty, index_ty + ); + + for &unsize in &[false, true] { + let mut self_ty = adjusted_ty; + if unsize { + // We only unsize arrays here. + if let ty::Array(element_ty, _) = adjusted_ty.kind { + self_ty = self.tcx.mk_slice(element_ty); + } else { + continue; + } + } + + // If some lookup succeeds, write callee into table and extract index/element + // type from the method signature. + // If some lookup succeeded, install method in table + let input_ty = self.next_ty_var(TypeVariableOrigin { + kind: TypeVariableOriginKind::AutoDeref, + span: base_expr.span, + }); + let method = + self.try_overloaded_place_op(expr.span, self_ty, &[input_ty], PlaceOp::Index); + + let result = method.map(|ok| { + debug!("try_index_step: success, using overloaded indexing"); + let method = self.register_infer_ok_obligations(ok); + + let mut adjustments = autoderef.adjust_steps(self); + if let ty::Ref(region, _, hir::Mutability::Not) = method.sig.inputs()[0].kind { + adjustments.push(Adjustment { + kind: Adjust::Borrow(AutoBorrow::Ref(region, AutoBorrowMutability::Not)), + target: self.tcx.mk_ref( + region, + ty::TypeAndMut { mutbl: hir::Mutability::Not, ty: adjusted_ty }, + ), + }); + } else { + span_bug!(expr.span, "input to index is not a ref?"); + } + if unsize { + adjustments.push(Adjustment { + kind: Adjust::Pointer(PointerCast::Unsize), + target: method.sig.inputs()[0], + }); + } + self.apply_adjustments(base_expr, adjustments); + + self.write_method_call(expr.hir_id, method); + (input_ty, self.make_overloaded_place_return_type(method).ty) + }); + if result.is_some() { + return result; + } + } + + None + } + + /// Try to resolve an overloaded place op. We only deal with the immutable + /// variant here (Deref/Index). In some contexts we would need the mutable + /// variant (DerefMut/IndexMut); those would be later converted by + /// `convert_place_derefs_to_mutable`. + pub(super) fn try_overloaded_place_op( + &self, + span: Span, + base_ty: Ty<'tcx>, + arg_tys: &[Ty<'tcx>], + op: PlaceOp, + ) -> Option>> { + debug!("try_overloaded_place_op({:?},{:?},{:?})", span, base_ty, op); + + let (imm_tr, imm_op) = match op { + PlaceOp::Deref => (self.tcx.lang_items().deref_trait(), sym::deref), + PlaceOp::Index => (self.tcx.lang_items().index_trait(), sym::index), + }; + imm_tr.and_then(|trait_did| { + self.lookup_method_in_trait( + span, + Ident::with_dummy_span(imm_op), + trait_did, + base_ty, + Some(arg_tys), + ) + }) + } + + fn try_mutable_overloaded_place_op( + &self, + span: Span, + base_ty: Ty<'tcx>, + arg_tys: &[Ty<'tcx>], + op: PlaceOp, + ) -> Option>> { + debug!("try_mutable_overloaded_place_op({:?},{:?},{:?})", span, base_ty, op); + + let (mut_tr, mut_op) = match op { + PlaceOp::Deref => (self.tcx.lang_items().deref_mut_trait(), sym::deref_mut), + PlaceOp::Index => (self.tcx.lang_items().index_mut_trait(), sym::index_mut), + }; + mut_tr.and_then(|trait_did| { + self.lookup_method_in_trait( + span, + Ident::with_dummy_span(mut_op), + trait_did, + base_ty, + Some(arg_tys), + ) + }) + } + + /// Convert auto-derefs, indices, etc of an expression from `Deref` and `Index` + /// into `DerefMut` and `IndexMut` respectively. + /// + /// This is a second pass of typechecking derefs/indices. We need this we do not + /// always know whether a place needs to be mutable or not in the first pass. + /// This happens whether there is an implicit mutable reborrow, e.g. when the type + /// is used as the receiver of a method call. + pub fn convert_place_derefs_to_mutable(&self, expr: &hir::Expr<'_>) { + // Gather up expressions we want to munge. + let mut exprs = vec![expr]; + + loop { + match exprs.last().unwrap().kind { + hir::ExprKind::Field(ref expr, _) + | hir::ExprKind::Index(ref expr, _) + | hir::ExprKind::Unary(hir::UnOp::UnDeref, ref expr) => exprs.push(&expr), + _ => break, + } + } + + debug!("convert_place_derefs_to_mutable: exprs={:?}", exprs); + + // Fix up autoderefs and derefs. + for (i, &expr) in exprs.iter().rev().enumerate() { + debug!("convert_place_derefs_to_mutable: i={} expr={:?}", i, expr); + + // Fix up the autoderefs. Autorefs can only occur immediately preceding + // overloaded place ops, and will be fixed by them in order to get + // the correct region. + let mut source = self.node_ty(expr.hir_id); + // Do not mutate adjustments in place, but rather take them, + // and replace them after mutating them, to avoid having the + // tables borrowed during (`deref_mut`) method resolution. + let previous_adjustments = + self.tables.borrow_mut().adjustments_mut().remove(expr.hir_id); + if let Some(mut adjustments) = previous_adjustments { + for adjustment in &mut adjustments { + if let Adjust::Deref(Some(ref mut deref)) = adjustment.kind { + if let Some(ok) = self.try_mutable_overloaded_place_op( + expr.span, + source, + &[], + PlaceOp::Deref, + ) { + let method = self.register_infer_ok_obligations(ok); + if let ty::Ref(region, _, mutbl) = method.sig.output().kind { + *deref = OverloadedDeref { region, mutbl }; + } + } + } + source = adjustment.target; + } + self.tables.borrow_mut().adjustments_mut().insert(expr.hir_id, adjustments); + } + + match expr.kind { + hir::ExprKind::Index(ref base_expr, ref index_expr) => { + // We need to get the final type in case dereferences were needed for the trait + // to apply (#72002). + let index_expr_ty = self.tables.borrow().expr_ty_adjusted(index_expr); + self.convert_place_op_to_mutable( + PlaceOp::Index, + expr, + base_expr, + &[index_expr_ty], + ); + } + hir::ExprKind::Unary(hir::UnOp::UnDeref, ref base_expr) => { + self.convert_place_op_to_mutable(PlaceOp::Deref, expr, base_expr, &[]); + } + _ => {} + } + } + } + + fn convert_place_op_to_mutable( + &self, + op: PlaceOp, + expr: &hir::Expr<'_>, + base_expr: &hir::Expr<'_>, + arg_tys: &[Ty<'tcx>], + ) { + debug!("convert_place_op_to_mutable({:?}, {:?}, {:?}, {:?})", op, expr, base_expr, arg_tys); + if !self.tables.borrow().is_method_call(expr) { + debug!("convert_place_op_to_mutable - builtin, nothing to do"); + return; + } + + // Need to deref because overloaded place ops take self by-reference. + let base_ty = self + .tables + .borrow() + .expr_ty_adjusted(base_expr) + .builtin_deref(false) + .expect("place op takes something that is not a ref") + .ty; + + let method = self.try_mutable_overloaded_place_op(expr.span, base_ty, arg_tys, op); + let method = match method { + Some(ok) => self.register_infer_ok_obligations(ok), + // Couldn't find the mutable variant of the place op, keep the + // current, immutable version. + None => return, + }; + debug!("convert_place_op_to_mutable: method={:?}", method); + self.write_method_call(expr.hir_id, method); + + let region = if let ty::Ref(r, _, hir::Mutability::Mut) = method.sig.inputs()[0].kind { + r + } else { + span_bug!(expr.span, "input to mutable place op is not a mut ref?"); + }; + + // Convert the autoref in the base expr to mutable with the correct + // region and mutability. + let base_expr_ty = self.node_ty(base_expr.hir_id); + if let Some(adjustments) = + self.tables.borrow_mut().adjustments_mut().get_mut(base_expr.hir_id) + { + let mut source = base_expr_ty; + for adjustment in &mut adjustments[..] { + if let Adjust::Borrow(AutoBorrow::Ref(..)) = adjustment.kind { + debug!("convert_place_op_to_mutable: converting autoref {:?}", adjustment); + let mutbl = AutoBorrowMutability::Mut { + // Deref/indexing can be desugared to a method call, + // so maybe we could use two-phase here. + // See the documentation of AllowTwoPhase for why that's + // not the case today. + allow_two_phase_borrow: AllowTwoPhase::No, + }; + adjustment.kind = Adjust::Borrow(AutoBorrow::Ref(region, mutbl)); + adjustment.target = + self.tcx.mk_ref(region, ty::TypeAndMut { ty: source, mutbl: mutbl.into() }); + } + source = adjustment.target; + } + + // If we have an autoref followed by unsizing at the end, fix the unsize target. + if let [.., Adjustment { kind: Adjust::Borrow(AutoBorrow::Ref(..)), .. }, Adjustment { kind: Adjust::Pointer(PointerCast::Unsize), ref mut target }] = + adjustments[..] + { + *target = method.sig.inputs()[0]; + } + } + } +} diff --git a/src/librustc_typeck/check/reconciliation.rs b/src/librustc_typeck/check/reconciliation.rs deleted file mode 100644 index 0a4293140a82..000000000000 --- a/src/librustc_typeck/check/reconciliation.rs +++ /dev/null @@ -1,150 +0,0 @@ -use crate::check::{FnCtxt, Needs, PlaceOp}; -use rustc_hir as hir; -use rustc_middle::ty::adjustment::{Adjust, Adjustment, OverloadedDeref, PointerCast}; -use rustc_middle::ty::adjustment::{AllowTwoPhase, AutoBorrow, AutoBorrowMutability}; -use rustc_middle::ty::{self, Ty}; - -impl<'a, 'tcx> FnCtxt<'a, 'tcx> { - /// Convert auto-derefs, indices, etc of an expression from `Deref` and `Index` - /// into `DerefMut` and `IndexMut` respectively. - /// - /// This is a second pass of typechecking derefs/indices. We need this we do not - /// always know whether a place needs to be mutable or not in the first pass. - /// This happens whether there is an implicit mutable reborrow, e.g. when the type - /// is used as the receiver of a method call. - pub fn convert_place_derefs_to_mutable(&self, expr: &hir::Expr<'_>) { - // Gather up expressions we want to munge. - let mut exprs = vec![expr]; - - loop { - match exprs.last().unwrap().kind { - hir::ExprKind::Field(ref expr, _) - | hir::ExprKind::Index(ref expr, _) - | hir::ExprKind::Unary(hir::UnOp::UnDeref, ref expr) => exprs.push(&expr), - _ => break, - } - } - - debug!("convert_place_derefs_to_mutable: exprs={:?}", exprs); - - // Fix up autoderefs and derefs. - for (i, &expr) in exprs.iter().rev().enumerate() { - debug!("convert_place_derefs_to_mutable: i={} expr={:?}", i, expr); - - // Fix up the autoderefs. Autorefs can only occur immediately preceding - // overloaded place ops, and will be fixed by them in order to get - // the correct region. - let mut source = self.node_ty(expr.hir_id); - // Do not mutate adjustments in place, but rather take them, - // and replace them after mutating them, to avoid having the - // tables borrowed during (`deref_mut`) method resolution. - let previous_adjustments = - self.tables.borrow_mut().adjustments_mut().remove(expr.hir_id); - if let Some(mut adjustments) = previous_adjustments { - let needs = Needs::MutPlace; - for adjustment in &mut adjustments { - if let Adjust::Deref(Some(ref mut deref)) = adjustment.kind { - if let Some(ok) = self.try_overloaded_deref(expr.span, source, needs) { - let method = self.register_infer_ok_obligations(ok); - if let ty::Ref(region, _, mutbl) = method.sig.output().kind { - *deref = OverloadedDeref { region, mutbl }; - } - } - } - source = adjustment.target; - } - self.tables.borrow_mut().adjustments_mut().insert(expr.hir_id, adjustments); - } - - match expr.kind { - hir::ExprKind::Index(ref base_expr, ref index_expr) => { - // We need to get the final type in case dereferences were needed for the trait - // to apply (#72002). - let index_expr_ty = self.tables.borrow().expr_ty_adjusted(index_expr); - self.convert_place_op_to_mutable( - PlaceOp::Index, - expr, - base_expr, - &[index_expr_ty], - ); - } - hir::ExprKind::Unary(hir::UnOp::UnDeref, ref base_expr) => { - self.convert_place_op_to_mutable(PlaceOp::Deref, expr, base_expr, &[]); - } - _ => {} - } - } - } - - fn convert_place_op_to_mutable( - &self, - op: PlaceOp, - expr: &hir::Expr<'_>, - base_expr: &hir::Expr<'_>, - arg_tys: &[Ty<'tcx>], - ) { - debug!("convert_place_op_to_mutable({:?}, {:?}, {:?}, {:?})", op, expr, base_expr, arg_tys); - if !self.tables.borrow().is_method_call(expr) { - debug!("convert_place_op_to_mutable - builtin, nothing to do"); - return; - } - - // Need to deref because overloaded place ops take self by-reference. - let base_ty = self - .tables - .borrow() - .expr_ty_adjusted(base_expr) - .builtin_deref(false) - .expect("place op takes something that is not a ref") - .ty; - - let method = self.try_overloaded_place_op(expr.span, base_ty, arg_tys, Needs::MutPlace, op); - let method = match method { - Some(ok) => self.register_infer_ok_obligations(ok), - None => return self.tcx.sess.delay_span_bug(expr.span, "re-trying op failed"), - }; - debug!("convert_place_op_to_mutable: method={:?}", method); - self.write_method_call(expr.hir_id, method); - - let (region, mutbl) = if let ty::Ref(r, _, mutbl) = method.sig.inputs()[0].kind { - (r, mutbl) - } else { - span_bug!(expr.span, "input to place op is not a ref?"); - }; - - // Convert the autoref in the base expr to mutable with the correct - // region and mutability. - let base_expr_ty = self.node_ty(base_expr.hir_id); - if let Some(adjustments) = - self.tables.borrow_mut().adjustments_mut().get_mut(base_expr.hir_id) - { - let mut source = base_expr_ty; - for adjustment in &mut adjustments[..] { - if let Adjust::Borrow(AutoBorrow::Ref(..)) = adjustment.kind { - debug!("convert_place_op_to_mutable: converting autoref {:?}", adjustment); - let mutbl = match mutbl { - hir::Mutability::Not => AutoBorrowMutability::Not, - hir::Mutability::Mut => AutoBorrowMutability::Mut { - // For initial two-phase borrow - // deployment, conservatively omit - // overloaded operators. - allow_two_phase_borrow: AllowTwoPhase::No, - }, - }; - adjustment.kind = Adjust::Borrow(AutoBorrow::Ref(region, mutbl)); - adjustment.target = - self.tcx.mk_ref(region, ty::TypeAndMut { ty: source, mutbl: mutbl.into() }); - } - source = adjustment.target; - } - - // If we have an autoref followed by unsizing at the end, fix the unsize target. - - if let [.., Adjustment { kind: Adjust::Borrow(AutoBorrow::Ref(..)), .. }, Adjustment { kind: Adjust::Pointer(PointerCast::Unsize), ref mut target }] = - adjustments[..] - { - *target = method.sig.inputs()[0]; - } - } - } -} From e0975b9b0150118d376ea19908cc9bfdf400bfd5 Mon Sep 17 00:00:00 2001 From: Erik Desjardins Date: Mon, 15 Jun 2020 18:19:54 -0400 Subject: [PATCH 067/123] elaborate, add check for exact bounds --- src/test/codegen/issue-69101-bounds-check.rs | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/src/test/codegen/issue-69101-bounds-check.rs b/src/test/codegen/issue-69101-bounds-check.rs index cdbe51da03cc..cf062b6ad758 100644 --- a/src/test/codegen/issue-69101-bounds-check.rs +++ b/src/test/codegen/issue-69101-bounds-check.rs @@ -2,6 +2,12 @@ // compile-flags: -O #![crate_type = "lib"] +// Make sure no bounds checks are emitted in the loop when upfront slicing +// ensures that the slices are big enough. +// In particular, bounds checks were not always optimized out if the upfront +// check was for a greater len than the loop requires. +// (i.e. `already_sliced_no_bounds_check` was not always optimized even when +// `already_sliced_no_bounds_check_exact` was) // CHECK-LABEL: @already_sliced_no_bounds_check #[no_mangle] pub fn already_sliced_no_bounds_check(a: &[u8], b: &[u8], c: &mut [u8]) { @@ -13,7 +19,18 @@ pub fn already_sliced_no_bounds_check(a: &[u8], b: &[u8], c: &mut [u8]) { } } -// make sure we're checking for the right thing: there can be a panic if the slice is too small +// CHECK-LABEL: @already_sliced_no_bounds_check_exact +#[no_mangle] +pub fn already_sliced_no_bounds_check_exact(a: &[u8], b: &[u8], c: &mut [u8]) { + // CHECK: slice_index_len_fail + // CHECK-NOT: panic_bounds_check + let _ = (&a[..1024], &b[..1024], &mut c[..1024]); + for i in 0..1024 { + c[i] = a[i] ^ b[i]; + } +} + +// Make sure we're checking for the right thing: there can be a panic if the slice is too small. // CHECK-LABEL: @already_sliced_bounds_check #[no_mangle] pub fn already_sliced_bounds_check(a: &[u8], b: &[u8], c: &mut [u8]) { From 268decbac8bad298299702a0d17c9213f0a14f2e Mon Sep 17 00:00:00 2001 From: mark Date: Tue, 5 May 2020 23:02:09 -0500 Subject: [PATCH 068/123] make all uses of ty::Error or ConstKind::Error delay a span bug --- .../debuginfo/type_names.rs | 2 +- .../infer/canonical/canonicalizer.rs | 2 +- src/librustc_infer/infer/canonical/mod.rs | 2 +- src/librustc_infer/infer/freshen.rs | 4 +- src/librustc_infer/infer/mod.rs | 3 +- src/librustc_infer/infer/resolve.rs | 8 +-- src/librustc_infer/infer/sub.rs | 4 +- src/librustc_lint/types.rs | 2 +- src/librustc_middle/traits/query.rs | 2 +- src/librustc_middle/ty/_match.rs | 2 +- src/librustc_middle/ty/context.rs | 49 +++++++++++++++-- src/librustc_middle/ty/error.rs | 4 +- src/librustc_middle/ty/fast_reject.rs | 2 +- src/librustc_middle/ty/flags.rs | 4 +- src/librustc_middle/ty/layout.rs | 4 +- src/librustc_middle/ty/outlives.rs | 2 +- src/librustc_middle/ty/print/mod.rs | 2 +- src/librustc_middle/ty/print/obsolete.rs | 2 +- src/librustc_middle/ty/print/pretty.rs | 4 +- src/librustc_middle/ty/query/values.rs | 4 +- src/librustc_middle/ty/relate.rs | 4 +- src/librustc_middle/ty/structural_impls.rs | 8 +-- src/librustc_middle/ty/sty.rs | 33 ++++++++---- src/librustc_middle/ty/util.rs | 8 +-- src/librustc_middle/ty/walk.rs | 4 +- .../type_check/free_region_relations.rs | 2 +- .../borrow_check/type_check/mod.rs | 4 +- .../interpret/intrinsics/type_name.rs | 2 +- src/librustc_mir/interpret/operand.rs | 2 +- src/librustc_mir/interpret/validity.rs | 2 +- src/librustc_mir_build/build/mod.rs | 6 ++- src/librustc_mir_build/hair/cx/expr.rs | 2 +- src/librustc_mir_build/hair/pattern/mod.rs | 6 +-- src/librustc_passes/intrinsicck.rs | 4 +- src/librustc_privacy/lib.rs | 2 +- src/librustc_save_analysis/lib.rs | 2 +- src/librustc_symbol_mangling/v0.rs | 4 +- src/librustc_trait_selection/opaque_types.rs | 6 +-- .../traits/coherence.rs | 2 +- .../traits/error_reporting/mod.rs | 2 +- .../traits/error_reporting/suggestions.rs | 2 +- .../traits/project.rs | 20 +++---- .../traits/query/dropck_outlives.rs | 2 +- .../traits/select/mod.rs | 6 +-- .../traits/structural_match.rs | 2 +- src/librustc_trait_selection/traits/wf.rs | 4 +- src/librustc_traits/chalk/lowering.rs | 4 +- src/librustc_traits/dropck_outlives.rs | 2 +- src/librustc_ty/ty.rs | 2 +- src/librustc_typeck/astconv.rs | 22 ++++---- src/librustc_typeck/check/_match.rs | 2 +- src/librustc_typeck/check/callee.rs | 2 +- src/librustc_typeck/check/cast.rs | 2 +- src/librustc_typeck/check/closure.rs | 4 +- src/librustc_typeck/check/coercion.rs | 8 +-- src/librustc_typeck/check/expr.rs | 52 +++++++++---------- src/librustc_typeck/check/method/confirm.rs | 3 +- src/librustc_typeck/check/method/probe.rs | 4 +- src/librustc_typeck/check/mod.rs | 28 +++++----- src/librustc_typeck/check/op.rs | 4 +- src/librustc_typeck/check/pat.rs | 43 ++++++++------- src/librustc_typeck/check/upvar.rs | 2 +- src/librustc_typeck/check/writeback.rs | 9 ++-- src/librustc_typeck/coherence/builtin.rs | 2 +- .../coherence/inherent_impls.rs | 2 +- src/librustc_typeck/collect.rs | 15 +++--- src/librustc_typeck/collect/type_of.rs | 35 ++++++------- src/librustc_typeck/variance/constraints.rs | 2 +- src/librustdoc/clean/mod.rs | 2 +- .../internal-lints/ty_tykind_usage.rs | 2 +- .../internal-lints/ty_tykind_usage.stderr | 2 +- 71 files changed, 279 insertions(+), 225 deletions(-) diff --git a/src/librustc_codegen_ssa/debuginfo/type_names.rs b/src/librustc_codegen_ssa/debuginfo/type_names.rs index 57a3d8b5edca..a64489c04c81 100644 --- a/src/librustc_codegen_ssa/debuginfo/type_names.rs +++ b/src/librustc_codegen_ssa/debuginfo/type_names.rs @@ -195,7 +195,7 @@ pub fn push_debuginfo_type_name<'tcx>( tcx.def_key(def_id).disambiguated_data.disambiguator )); } - ty::Error + ty::Error(_) | ty::Infer(_) | ty::Placeholder(..) | ty::Projection(..) diff --git a/src/librustc_infer/infer/canonical/canonicalizer.rs b/src/librustc_infer/infer/canonical/canonicalizer.rs index c2dae6ba4f83..427cc55a428a 100644 --- a/src/librustc_infer/infer/canonical/canonicalizer.rs +++ b/src/librustc_infer/infer/canonical/canonicalizer.rs @@ -403,7 +403,7 @@ impl<'cx, 'tcx> TypeFolder<'tcx> for Canonicalizer<'cx, 'tcx> { | ty::Float(..) | ty::Adt(..) | ty::Str - | ty::Error + | ty::Error(_) | ty::Array(..) | ty::Slice(..) | ty::RawPtr(..) diff --git a/src/librustc_infer/infer/canonical/mod.rs b/src/librustc_infer/infer/canonical/mod.rs index 7310d2c3bdcf..2b8c46f1de42 100644 --- a/src/librustc_infer/infer/canonical/mod.rs +++ b/src/librustc_infer/infer/canonical/mod.rs @@ -154,7 +154,7 @@ impl<'cx, 'tcx> InferCtxt<'cx, 'tcx> { self.tcx .mk_const(ty::Const { val: ty::ConstKind::Placeholder(placeholder_mapped), - ty: self.tcx.types.err, // FIXME(const_generics) + ty: self.tcx.ty_error(), // FIXME(const_generics) }) .into() } diff --git a/src/librustc_infer/infer/freshen.rs b/src/librustc_infer/infer/freshen.rs index b4cfcb3a1c32..02bebe10ed04 100644 --- a/src/librustc_infer/infer/freshen.rs +++ b/src/librustc_infer/infer/freshen.rs @@ -192,7 +192,7 @@ impl<'a, 'tcx> TypeFolder<'tcx> for TypeFreshener<'a, 'tcx> { | ty::Float(..) | ty::Adt(..) | ty::Str - | ty::Error + | ty::Error(_) | ty::Array(..) | ty::Slice(..) | ty::RawPtr(..) @@ -250,7 +250,7 @@ impl<'a, 'tcx> TypeFolder<'tcx> for TypeFreshener<'a, 'tcx> { ty::ConstKind::Param(_) | ty::ConstKind::Value(_) | ty::ConstKind::Unevaluated(..) - | ty::ConstKind::Error => {} + | ty::ConstKind::Error(_) => {} } ct.super_fold_with(self) diff --git a/src/librustc_infer/infer/mod.rs b/src/librustc_infer/infer/mod.rs index 92387f753f55..91f4b3323f30 100644 --- a/src/librustc_infer/infer/mod.rs +++ b/src/librustc_infer/infer/mod.rs @@ -1751,9 +1751,10 @@ impl<'tcx> TypeTrace<'tcx> { } pub fn dummy(tcx: TyCtxt<'tcx>) -> TypeTrace<'tcx> { + let err = tcx.ty_error(); TypeTrace { cause: ObligationCause::dummy(), - values: Types(ExpectedFound { expected: tcx.types.err, found: tcx.types.err }), + values: Types(ExpectedFound { expected: err, found: err }), } } } diff --git a/src/librustc_infer/infer/resolve.rs b/src/librustc_infer/infer/resolve.rs index e28cf49c7f25..df166d21a36c 100644 --- a/src/librustc_infer/infer/resolve.rs +++ b/src/librustc_infer/infer/resolve.rs @@ -189,15 +189,15 @@ impl<'a, 'tcx> TypeFolder<'tcx> for FullTypeResolver<'a, 'tcx> { match t.kind { ty::Infer(ty::TyVar(vid)) => { self.err = Some(FixupError::UnresolvedTy(vid)); - self.tcx().types.err + self.tcx().ty_error() } ty::Infer(ty::IntVar(vid)) => { self.err = Some(FixupError::UnresolvedIntTy(vid)); - self.tcx().types.err + self.tcx().ty_error() } ty::Infer(ty::FloatVar(vid)) => { self.err = Some(FixupError::UnresolvedFloatTy(vid)); - self.tcx().types.err + self.tcx().ty_error() } ty::Infer(_) => { bug!("Unexpected type in full type resolver: {:?}", t); @@ -228,7 +228,7 @@ impl<'a, 'tcx> TypeFolder<'tcx> for FullTypeResolver<'a, 'tcx> { match c.val { ty::ConstKind::Infer(InferConst::Var(vid)) => { self.err = Some(FixupError::UnresolvedConst(vid)); - return self.tcx().mk_const(ty::Const { val: ty::ConstKind::Error, ty: c.ty }); + return self.tcx().const_error(c.ty); } ty::ConstKind::Infer(InferConst::Fresh(_)) => { bug!("Unexpected const in full const resolver: {:?}", c); diff --git a/src/librustc_infer/infer/sub.rs b/src/librustc_infer/infer/sub.rs index b51af19883fd..90962d210b5b 100644 --- a/src/librustc_infer/infer/sub.rs +++ b/src/librustc_infer/infer/sub.rs @@ -119,9 +119,9 @@ impl TypeRelation<'tcx> for Sub<'combine, 'infcx, 'tcx> { Ok(a) } - (&ty::Error, _) | (_, &ty::Error) => { + (&ty::Error(_), _) | (_, &ty::Error(_)) => { infcx.set_tainted_by_errors(); - Ok(self.tcx().types.err) + Ok(self.tcx().ty_error()) } _ => { diff --git a/src/librustc_lint/types.rs b/src/librustc_lint/types.rs index a8ecfdd0f3d4..1dd6d837d4ea 100644 --- a/src/librustc_lint/types.rs +++ b/src/librustc_lint/types.rs @@ -889,7 +889,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { ty::Param(..) | ty::Infer(..) | ty::Bound(..) - | ty::Error + | ty::Error(_) | ty::Closure(..) | ty::Generator(..) | ty::GeneratorWitness(..) diff --git a/src/librustc_middle/traits/query.rs b/src/librustc_middle/traits/query.rs index e030125b5b15..69696ac9e93c 100644 --- a/src/librustc_middle/traits/query.rs +++ b/src/librustc_middle/traits/query.rs @@ -221,7 +221,7 @@ pub fn trivial_dropck_outlives<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> bool { | ty::Ref(..) | ty::Str | ty::Foreign(..) - | ty::Error => true, + | ty::Error(_) => true, // [T; N] and [T] have same properties as T. ty::Array(ty, _) | ty::Slice(ty) => trivial_dropck_outlives(tcx, ty), diff --git a/src/librustc_middle/ty/_match.rs b/src/librustc_middle/ty/_match.rs index 02abe868f394..db9229ae3d21 100644 --- a/src/librustc_middle/ty/_match.rs +++ b/src/librustc_middle/ty/_match.rs @@ -79,7 +79,7 @@ impl TypeRelation<'tcx> for Match<'tcx> { Err(TypeError::Sorts(relate::expected_found(self, &a, &b))) } - (&ty::Error, _) | (_, &ty::Error) => Ok(self.tcx().types.err), + (&ty::Error(_), _) | (_, &ty::Error(_)) => Ok(self.tcx().ty_error()), _ => relate::super_relate_tys(self, a, b), } diff --git a/src/librustc_middle/ty/context.rs b/src/librustc_middle/ty/context.rs index d5be3508d2d8..4fe8173becff 100644 --- a/src/librustc_middle/ty/context.rs +++ b/src/librustc_middle/ty/context.rs @@ -46,7 +46,7 @@ use rustc_session::lint::{Level, Lint}; use rustc_session::Session; use rustc_span::source_map::MultiSpan; use rustc_span::symbol::{kw, sym, Symbol}; -use rustc_span::Span; +use rustc_span::{Span, DUMMY_SP}; use rustc_target::abi::{Layout, TargetDataLayout, VariantIdx}; use rustc_target::spec::abi; @@ -145,7 +145,6 @@ pub struct CommonTypes<'tcx> { pub f64: Ty<'tcx>, pub never: Ty<'tcx>, pub self_param: Ty<'tcx>, - pub err: Ty<'tcx>, /// Dummy type used for the `Self` of a `TraitRef` created for converting /// a trait object, and which gets removed in `ExistentialTraitRef`. @@ -804,7 +803,6 @@ impl<'tcx> CommonTypes<'tcx> { bool: mk(Bool), char: mk(Char), never: mk(Never), - err: mk(Error), isize: mk(Int(ast::IntTy::Isize)), i8: mk(Int(ast::IntTy::I8)), i16: mk(Int(ast::IntTy::I16)), @@ -1143,6 +1141,49 @@ impl<'tcx> TyCtxt<'tcx> { } } + /// Constructs a `TyKind::Error` type and registers a `delay_span_bug` to ensure it gets used. + #[track_caller] + pub fn ty_error(self) -> Ty<'tcx> { + self.err_with_message_and_location( + DUMMY_SP, + "TyKind::Error constructed but no error reported", + std::panic::Location::caller(), + ) + } + + /// Constructs a `TyKind::Error` type and registers a `delay_span_bug` with the given `msg to + /// ensure it gets used. + #[track_caller] + pub fn ty_error_with_message>(self, span: S, msg: &str) -> Ty<'tcx> { + self.err_with_message_and_location(span, msg, std::panic::Location::caller()) + } + + pub fn err_with_message_and_location>( + self, + span: S, + msg: &str, + loc: &'static std::panic::Location<'static>, + ) -> Ty<'tcx> { + self.sess.delay_span_bug(span, &format!("{}: {}", loc, msg)); + self.mk_ty(Error(super::sty::DelaySpanBugEmitted(()))) + } + + /// Like `err` but for constants. + #[track_caller] + pub fn const_error(self, ty: Ty<'tcx>) -> &'tcx Const<'tcx> { + self.sess.delay_span_bug( + DUMMY_SP, + &format!( + "ty::ConstKind::Error constructed but no error reported. {}", + std::panic::Location::caller() + ), + ); + self.mk_const(ty::Const { + val: ty::ConstKind::Error(super::sty::DelaySpanBugEmitted(())), + ty, + }) + } + pub fn consider_optimizing String>(&self, msg: T) -> bool { let cname = self.crate_name(LOCAL_CRATE).as_str(); self.sess.consider_optimizing(&cname, msg) @@ -1846,7 +1887,7 @@ macro_rules! sty_debug_print { let variant = match t.kind { ty::Bool | ty::Char | ty::Int(..) | ty::Uint(..) | ty::Float(..) | ty::Str | ty::Never => continue, - ty::Error => /* unimportant */ continue, + ty::Error(_) => /* unimportant */ continue, $(ty::$variant(..) => &mut $variant,)* }; let lt = t.flags.intersects(ty::TypeFlags::HAS_RE_INFER); diff --git a/src/librustc_middle/ty/error.rs b/src/librustc_middle/ty/error.rs index be3bf748225b..6113359ca93a 100644 --- a/src/librustc_middle/ty/error.rs +++ b/src/librustc_middle/ty/error.rs @@ -286,14 +286,14 @@ impl<'tcx> ty::TyS<'tcx> { ty::Projection(_) => "associated type".into(), ty::Param(p) => format!("type parameter `{}`", p).into(), ty::Opaque(..) => "opaque type".into(), - ty::Error => "type error".into(), + ty::Error(_) => "type error".into(), } } pub fn prefix_string(&self) -> Cow<'static, str> { match self.kind { ty::Infer(_) - | ty::Error + | ty::Error(_) | ty::Bool | ty::Char | ty::Int(_) diff --git a/src/librustc_middle/ty/fast_reject.rs b/src/librustc_middle/ty/fast_reject.rs index 16d8e3794076..b0fb179b18bd 100644 --- a/src/librustc_middle/ty/fast_reject.rs +++ b/src/librustc_middle/ty/fast_reject.rs @@ -104,7 +104,7 @@ pub fn simplify_type( } ty::Opaque(def_id, _) => Some(OpaqueSimplifiedType(def_id)), ty::Foreign(def_id) => Some(ForeignSimplifiedType(def_id)), - ty::Placeholder(..) | ty::Bound(..) | ty::Infer(_) | ty::Error => None, + ty::Placeholder(..) | ty::Bound(..) | ty::Infer(_) | ty::Error(_) => None, } } diff --git a/src/librustc_middle/ty/flags.rs b/src/librustc_middle/ty/flags.rs index edcb69c5e8cb..bee42be8a538 100644 --- a/src/librustc_middle/ty/flags.rs +++ b/src/librustc_middle/ty/flags.rs @@ -70,7 +70,7 @@ impl FlagComputation { | &ty::Str | &ty::Foreign(..) => {} - &ty::Error => self.add_flags(TypeFlags::HAS_ERROR), + &ty::Error(_) => self.add_flags(TypeFlags::HAS_ERROR), &ty::Param(_) => { self.add_flags(TypeFlags::HAS_TY_PARAM); @@ -227,7 +227,7 @@ impl FlagComputation { self.add_flags(TypeFlags::STILL_FURTHER_SPECIALIZABLE); } ty::ConstKind::Value(_) => {} - ty::ConstKind::Error => self.add_flags(TypeFlags::HAS_ERROR), + ty::ConstKind::Error(_) => self.add_flags(TypeFlags::HAS_ERROR), } } diff --git a/src/librustc_middle/ty/layout.rs b/src/librustc_middle/ty/layout.rs index f5bca90c2bd5..d58ebdc8dfc4 100644 --- a/src/librustc_middle/ty/layout.rs +++ b/src/librustc_middle/ty/layout.rs @@ -1245,7 +1245,7 @@ impl<'tcx> LayoutCx<'tcx, TyCtxt<'tcx>> { bug!("Layout::compute: unexpected type `{}`", ty) } - ty::Param(_) | ty::Error => { + ty::Param(_) | ty::Error(_) => { return Err(LayoutError::Unknown(ty)); } }) @@ -2141,7 +2141,7 @@ where | ty::Opaque(..) | ty::Param(_) | ty::Infer(_) - | ty::Error => bug!("TyAndLayout::field_type: unexpected type `{}`", this.ty), + | ty::Error(_) => bug!("TyAndLayout::field_type: unexpected type `{}`", this.ty), }) } diff --git a/src/librustc_middle/ty/outlives.rs b/src/librustc_middle/ty/outlives.rs index 1da042e16173..1a8693b8df71 100644 --- a/src/librustc_middle/ty/outlives.rs +++ b/src/librustc_middle/ty/outlives.rs @@ -171,7 +171,7 @@ fn compute_components(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, out: &mut SmallVec<[Compo ty::Dynamic(..) | // OutlivesObject, OutlivesFragment (*) ty::Placeholder(..) | ty::Bound(..) | - ty::Error => { + ty::Error(_) => { // (*) Function pointers and trait objects are both binders. // In the RFC, this means we would add the bound regions to // the "bound regions list". In our representation, no such diff --git a/src/librustc_middle/ty/print/mod.rs b/src/librustc_middle/ty/print/mod.rs index 69b36980bd73..6c8f23c139f6 100644 --- a/src/librustc_middle/ty/print/mod.rs +++ b/src/librustc_middle/ty/print/mod.rs @@ -298,7 +298,7 @@ pub fn characteristic_def_id_of_type(ty: Ty<'_>) -> Option { | ty::Opaque(..) | ty::Infer(_) | ty::Bound(..) - | ty::Error + | ty::Error(_) | ty::GeneratorWitness(..) | ty::Never | ty::Float(_) => None, diff --git a/src/librustc_middle/ty/print/obsolete.rs b/src/librustc_middle/ty/print/obsolete.rs index 7d9943ab0790..67b6433b6114 100644 --- a/src/librustc_middle/ty/print/obsolete.rs +++ b/src/librustc_middle/ty/print/obsolete.rs @@ -144,7 +144,7 @@ impl DefPathBasedNames<'tcx> { let substs = substs.truncate_to(self.tcx, generics); self.push_generic_params(substs, iter::empty(), output, debug); } - ty::Error + ty::Error(_) | ty::Bound(..) | ty::Infer(_) | ty::Placeholder(..) diff --git a/src/librustc_middle/ty/print/pretty.rs b/src/librustc_middle/ty/print/pretty.rs index d782dd07a658..17203fcce5e5 100644 --- a/src/librustc_middle/ty/print/pretty.rs +++ b/src/librustc_middle/ty/print/pretty.rs @@ -518,7 +518,7 @@ pub trait PrettyPrinter<'tcx>: p!(write("{}", infer_ty)) } } - ty::Error => p!(write("[type error]")), + ty::Error(_) => p!(write("[type error]")), ty::Param(ref param_ty) => p!(write("{}", param_ty)), ty::Bound(debruijn, bound_ty) => match bound_ty.kind { ty::BoundTyKind::Anon => self.pretty_print_bound_var(debruijn, bound_ty.var)?, @@ -919,7 +919,7 @@ pub trait PrettyPrinter<'tcx>: self.pretty_print_bound_var(debruijn, bound_var)? } ty::ConstKind::Placeholder(placeholder) => p!(write("Placeholder({:?})", placeholder)), - ty::ConstKind::Error => p!(write("[const error]")), + ty::ConstKind::Error(_) => p!(write("[const error]")), }; Ok(self) } diff --git a/src/librustc_middle/ty/query/values.rs b/src/librustc_middle/ty/query/values.rs index b1f76ff6a03b..0a0ff101b520 100644 --- a/src/librustc_middle/ty/query/values.rs +++ b/src/librustc_middle/ty/query/values.rs @@ -17,7 +17,7 @@ impl<'tcx> Value<'tcx> for &'_ TyS<'_> { fn from_cycle_error(tcx: TyCtxt<'tcx>) -> Self { // SAFETY: This is never called when `Self` is not `Ty<'tcx>`. // FIXME: Represent the above fact in the trait system somehow. - unsafe { std::mem::transmute::, Ty<'_>>(tcx.types.err) } + unsafe { std::mem::transmute::, Ty<'_>>(tcx.ty_error()) } } } @@ -33,7 +33,7 @@ impl<'tcx> Value<'tcx> for AdtSizedConstraint<'_> { // FIXME: Represent the above fact in the trait system somehow. unsafe { std::mem::transmute::, AdtSizedConstraint<'_>>( - AdtSizedConstraint(tcx.intern_type_list(&[tcx.types.err])), + AdtSizedConstraint(tcx.intern_type_list(&[tcx.ty_error()])), ) } } diff --git a/src/librustc_middle/ty/relate.rs b/src/librustc_middle/ty/relate.rs index cddd7081ca37..14cddd11c438 100644 --- a/src/librustc_middle/ty/relate.rs +++ b/src/librustc_middle/ty/relate.rs @@ -354,7 +354,7 @@ pub fn super_relate_tys>( bug!("bound types encountered in super_relate_tys") } - (&ty::Error, _) | (_, &ty::Error) => Ok(tcx.types.err), + (&ty::Error(_), _) | (_, &ty::Error(_)) => Ok(tcx.ty_error()), (&ty::Never, _) | (&ty::Char, _) @@ -524,7 +524,7 @@ pub fn super_relate_consts>( bug!("var types encountered in super_relate_consts: {:?} {:?}", a, b) } - (ty::ConstKind::Error, _) | (_, ty::ConstKind::Error) => Ok(ty::ConstKind::Error), + (ty::ConstKind::Error(d), _) | (_, ty::ConstKind::Error(d)) => Ok(ty::ConstKind::Error(d)), (ty::ConstKind::Param(a_p), ty::ConstKind::Param(b_p)) if a_p.index == b_p.index => { return Ok(a); diff --git a/src/librustc_middle/ty/structural_impls.rs b/src/librustc_middle/ty/structural_impls.rs index f6f5dfd65161..f04d31601ea5 100644 --- a/src/librustc_middle/ty/structural_impls.rs +++ b/src/librustc_middle/ty/structural_impls.rs @@ -911,7 +911,7 @@ impl<'tcx> TypeFoldable<'tcx> for Ty<'tcx> { | ty::Int(_) | ty::Uint(_) | ty::Float(_) - | ty::Error + | ty::Error(_) | ty::Infer(_) | ty::Param(..) | ty::Bound(..) @@ -952,7 +952,7 @@ impl<'tcx> TypeFoldable<'tcx> for Ty<'tcx> { | ty::Int(_) | ty::Uint(_) | ty::Float(_) - | ty::Error + | ty::Error(_) | ty::Infer(_) | ty::Bound(..) | ty::Placeholder(..) @@ -1051,7 +1051,7 @@ impl<'tcx> TypeFoldable<'tcx> for ty::ConstKind<'tcx> { ty::ConstKind::Value(_) | ty::ConstKind::Bound(..) | ty::ConstKind::Placeholder(..) - | ty::ConstKind::Error => *self, + | ty::ConstKind::Error(_) => *self, } } @@ -1063,7 +1063,7 @@ impl<'tcx> TypeFoldable<'tcx> for ty::ConstKind<'tcx> { ty::ConstKind::Value(_) | ty::ConstKind::Bound(..) | ty::ConstKind::Placeholder(_) - | ty::ConstKind::Error => false, + | ty::ConstKind::Error(_) => false, } } } diff --git a/src/librustc_middle/ty/sty.rs b/src/librustc_middle/ty/sty.rs index fad96aa86cc0..656c05b5520e 100644 --- a/src/librustc_middle/ty/sty.rs +++ b/src/librustc_middle/ty/sty.rs @@ -203,9 +203,26 @@ pub enum TyKind<'tcx> { /// A placeholder for a type which could not be computed; this is /// propagated to avoid useless error messages. - Error, + Error(DelaySpanBugEmitted), } +/// A type that is not publicly constructable. This prevents people from making `TyKind::Error` +/// except through `tcx.err*()`. +#[derive( + Copy, + Clone, + Debug, + Eq, + Hash, + PartialEq, + PartialOrd, + Ord, + RustcEncodable, + RustcDecodable, + HashStable +)] +pub struct DelaySpanBugEmitted(pub(super) ()); + // `TyKind` is used a lot. Make sure it doesn't unintentionally get bigger. #[cfg(target_arch = "x86_64")] static_assert_size!(TyKind<'_>, 24); @@ -1984,7 +2001,7 @@ impl<'tcx> TyS<'tcx> { #[inline] pub fn has_concrete_skeleton(&self) -> bool { match self.kind { - Param(_) | Infer(_) | Error => false, + Param(_) | Infer(_) | Error(_) => false, _ => true, } } @@ -2016,7 +2033,7 @@ impl<'tcx> TyS<'tcx> { match self.kind { FnDef(def_id, substs) => tcx.fn_sig(def_id).subst(tcx, substs), FnPtr(f) => f, - Error => { + Error(_) => { // ignore errors (#54954) ty::Binder::dummy(FnSig::fake()) } @@ -2140,7 +2157,7 @@ impl<'tcx> TyS<'tcx> { // closure type is not yet known Bound(..) | Infer(_) => None, - Error => Some(ty::ClosureKind::Fn), + Error(_) => Some(ty::ClosureKind::Fn), _ => bug!("cannot convert type `{:?}` to a closure kind", self), } @@ -2167,7 +2184,7 @@ impl<'tcx> TyS<'tcx> { | ty::Array(..) | ty::Closure(..) | ty::Never - | ty::Error => true, + | ty::Error(_) => true, ty::Str | ty::Slice(_) | ty::Dynamic(..) | ty::Foreign(..) => false, @@ -2372,9 +2389,7 @@ impl<'tcx> Const<'tcx> { // can leak through `val` into the const we return. Ok(val) => Const::from_value(tcx, val, self.ty), Err(ErrorHandled::TooGeneric | ErrorHandled::Linted) => self, - Err(ErrorHandled::Reported(ErrorReported)) => { - tcx.mk_const(ty::Const { val: ty::ConstKind::Error, ty: self.ty }) - } + Err(ErrorHandled::Reported(ErrorReported)) => tcx.const_error(self.ty), } } else { self @@ -2434,7 +2449,7 @@ pub enum ConstKind<'tcx> { /// A placeholder for a const which could not be computed; this is /// propagated to avoid useless error messages. - Error, + Error(DelaySpanBugEmitted), } #[cfg(target_arch = "x86_64")] diff --git a/src/librustc_middle/ty/util.rs b/src/librustc_middle/ty/util.rs index ff284b709c2c..47110be53b25 100644 --- a/src/librustc_middle/ty/util.rs +++ b/src/librustc_middle/ty/util.rs @@ -176,7 +176,7 @@ impl<'tcx> TyCtxt<'tcx> { if let ty::Adt(def, substs) = ty.kind { for field in def.all_fields() { let field_ty = field.ty(self, substs); - if let Error = field_ty.kind { + if let Error(_) = field_ty.kind { return true; } } @@ -731,7 +731,7 @@ impl<'tcx> ty::TyS<'tcx> { | ty::Ref(..) | ty::RawPtr(_) | ty::FnDef(..) - | ty::Error + | ty::Error(_) | ty::FnPtr(_) => true, ty::Tuple(_) => self.tuple_fields().all(Self::is_trivially_freeze), ty::Slice(elem_ty) | ty::Array(elem_ty, _) => elem_ty.is_trivially_freeze(), @@ -826,7 +826,7 @@ impl<'tcx> ty::TyS<'tcx> { // called for known, fully-monomorphized types. Projection(_) | Opaque(..) | Param(_) | Bound(..) | Placeholder(_) | Infer(_) => false, - Foreign(_) | GeneratorWitness(..) | Error => false, + Foreign(_) | GeneratorWitness(..) | Error(_) => false, } } @@ -1109,7 +1109,7 @@ pub fn needs_drop_components( // Foreign types can never have destructors. ty::Foreign(..) => Ok(SmallVec::new()), - ty::Dynamic(..) | ty::Error => Err(AlwaysRequiresDrop), + ty::Dynamic(..) | ty::Error(_) => Err(AlwaysRequiresDrop), ty::Slice(ty) => needs_drop_components(ty, target_layout), ty::Array(elem_ty, size) => { diff --git a/src/librustc_middle/ty/walk.rs b/src/librustc_middle/ty/walk.rs index bf988a430263..d6f504fdb338 100644 --- a/src/librustc_middle/ty/walk.rs +++ b/src/librustc_middle/ty/walk.rs @@ -108,7 +108,7 @@ fn push_inner<'tcx>(stack: &mut TypeWalkerStack<'tcx>, parent: GenericArg<'tcx>) | ty::Infer(_) | ty::Param(_) | ty::Never - | ty::Error + | ty::Error(_) | ty::Placeholder(..) | ty::Bound(..) | ty::Foreign(..) => {} @@ -171,7 +171,7 @@ fn push_inner<'tcx>(stack: &mut TypeWalkerStack<'tcx>, parent: GenericArg<'tcx>) | ty::ConstKind::Placeholder(_) | ty::ConstKind::Bound(..) | ty::ConstKind::Value(_) - | ty::ConstKind::Error => {} + | ty::ConstKind::Error(_) => {} ty::ConstKind::Unevaluated(_, substs, _) => { stack.extend(substs.iter().rev()); diff --git a/src/librustc_mir/borrow_check/type_check/free_region_relations.rs b/src/librustc_mir/borrow_check/type_check/free_region_relations.rs index 5707127340d8..beee31812563 100644 --- a/src/librustc_mir/borrow_check/type_check/free_region_relations.rs +++ b/src/librustc_mir/borrow_check/type_check/free_region_relations.rs @@ -264,7 +264,7 @@ impl UniversalRegionRelationsBuilder<'cx, 'tcx> { .tcx .sess .delay_span_bug(DUMMY_SP, &format!("failed to normalize {:?}", ty)); - (self.infcx.tcx.types.err, None) + (self.infcx.tcx.ty_error(), None) }); let constraints2 = self.add_implied_bounds(ty); normalized_inputs_and_output.push(ty); diff --git a/src/librustc_mir/borrow_check/type_check/mod.rs b/src/librustc_mir/borrow_check/type_check/mod.rs index 168612f9beec..011aeb4b8162 100644 --- a/src/librustc_mir/borrow_check/type_check/mod.rs +++ b/src/librustc_mir/borrow_check/type_check/mod.rs @@ -498,7 +498,7 @@ impl<'a, 'b, 'tcx> TypeVerifier<'a, 'b, 'tcx> { if place_ty.variant_index.is_none() { if place_ty.ty.references_error() { assert!(self.errors_reported); - return PlaceTy::from_ty(self.tcx().types.err); + return PlaceTy::from_ty(self.tcx().ty_error()); } } place_ty = self.sanitize_projection(place_ty, elem, place, location) @@ -725,7 +725,7 @@ impl<'a, 'b, 'tcx> TypeVerifier<'a, 'b, 'tcx> { fn error(&mut self) -> Ty<'tcx> { self.errors_reported = true; - self.tcx().types.err + self.tcx().ty_error() } fn field_ty( diff --git a/src/librustc_mir/interpret/intrinsics/type_name.rs b/src/librustc_mir/interpret/intrinsics/type_name.rs index 71cca725982f..379117f3b846 100644 --- a/src/librustc_mir/interpret/intrinsics/type_name.rs +++ b/src/librustc_mir/interpret/intrinsics/type_name.rs @@ -50,7 +50,7 @@ impl<'tcx> Printer<'tcx> for AbsolutePathPrinter<'tcx> { | ty::Dynamic(_, _) => self.pretty_print_type(ty), // Placeholders (all printed as `_` to uniformize them). - ty::Param(_) | ty::Bound(..) | ty::Placeholder(_) | ty::Infer(_) | ty::Error => { + ty::Param(_) | ty::Bound(..) | ty::Placeholder(_) | ty::Infer(_) | ty::Error(_) => { write!(self, "_")?; Ok(self) } diff --git a/src/librustc_mir/interpret/operand.rs b/src/librustc_mir/interpret/operand.rs index 38f5988d0eb3..0cb878a49dcf 100644 --- a/src/librustc_mir/interpret/operand.rs +++ b/src/librustc_mir/interpret/operand.rs @@ -527,7 +527,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { // Early-return cases. let val_val = match val.val { ty::ConstKind::Param(_) => throw_inval!(TooGeneric), - ty::ConstKind::Error => throw_inval!(TypeckError(ErrorReported)), + ty::ConstKind::Error(_) => throw_inval!(TypeckError(ErrorReported)), ty::ConstKind::Unevaluated(def_id, substs, promoted) => { let instance = self.resolve(def_id, substs)?; // We use `const_eval` here and `const_eval_raw` elsewhere in mir interpretation. diff --git a/src/librustc_mir/interpret/validity.rs b/src/librustc_mir/interpret/validity.rs index 7e3b6c08e08f..999f2fe76b4d 100644 --- a/src/librustc_mir/interpret/validity.rs +++ b/src/librustc_mir/interpret/validity.rs @@ -561,7 +561,7 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, ' | ty::Generator(..) => Ok(false), // Some types only occur during typechecking, they have no layout. // We should not see them here and we could not check them anyway. - ty::Error + ty::Error(_) | ty::Infer(..) | ty::Placeholder(..) | ty::Bound(..) diff --git a/src/librustc_mir_build/build/mod.rs b/src/librustc_mir_build/build/mod.rs index 2efe93d057b9..e2cf1bce733d 100644 --- a/src/librustc_mir_build/build/mod.rs +++ b/src/librustc_mir_build/build/mod.rs @@ -687,7 +687,7 @@ fn construct_error<'a, 'tcx>(hir: Cx<'a, 'tcx>, body_id: hir::BodyId) -> Body<'t let tcx = hir.tcx(); let owner_id = tcx.hir().body_owner(body_id); let span = tcx.hir().span(owner_id); - let ty = tcx.types.err; + let ty = tcx.ty_error(); let num_params = match hir.body_owner_kind { hir::BodyOwnerKind::Fn => tcx.hir().fn_decl_by_hir_id(owner_id).unwrap().inputs.len(), hir::BodyOwnerKind::Closure => { @@ -909,7 +909,9 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { self.local_decls[local].mutability = mutability; self.local_decls[local].source_info.scope = self.source_scope; self.local_decls[local].local_info = if let Some(kind) = self_binding { - Some(box LocalInfo::User(ClearCrossCrate::Set(BindingForm::ImplicitSelf(*kind)))) + Some(box LocalInfo::User(ClearCrossCrate::Set( + BindingForm::ImplicitSelf(*kind), + ))) } else { let binding_mode = ty::BindingMode::BindByValue(mutability); Some(box LocalInfo::User(ClearCrossCrate::Set(BindingForm::Var( diff --git a/src/librustc_mir_build/hair/cx/expr.rs b/src/librustc_mir_build/hair/cx/expr.rs index 703f6ef8dc41..a1796c9433ea 100644 --- a/src/librustc_mir_build/hair/cx/expr.rs +++ b/src/librustc_mir_build/hair/cx/expr.rs @@ -478,7 +478,7 @@ fn make_mirror_unadjusted<'a, 'tcx>( ); // Not a real fn, but we're not reaching codegen anyways... - ty = cx.tcx.types.err; + ty = cx.tcx.ty_error(); InlineAsmOperand::SymFn { expr: Expr { ty, diff --git a/src/librustc_mir_build/hair/pattern/mod.rs b/src/librustc_mir_build/hair/pattern/mod.rs index e9aa7f597beb..5c30b2a448c6 100644 --- a/src/librustc_mir_build/hair/pattern/mod.rs +++ b/src/librustc_mir_build/hair/pattern/mod.rs @@ -509,7 +509,7 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { fn lower_pattern_unadjusted(&mut self, pat: &'tcx hir::Pat<'tcx>) -> Pat<'tcx> { let mut ty = self.tables.node_type(pat.hir_id); - if let ty::Error = ty.kind { + if let ty::Error(_) = ty.kind { // Avoid ICEs (e.g., #50577 and #50585). return Pat { span: pat.span, ty, kind: Box::new(PatKind::Wild) }; } @@ -708,7 +708,7 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { if adt_def.is_enum() { let substs = match ty.kind { ty::Adt(_, substs) | ty::FnDef(_, substs) => substs, - ty::Error => { + ty::Error(_) => { // Avoid ICE (#50585) return PatKind::Wild; } @@ -1051,7 +1051,7 @@ crate fn compare_const_vals<'tcx>( let b_bits = b.try_eval_bits(tcx, param_env, ty); if let (Some(a), Some(b)) = (a_bits, b_bits) { - use ::rustc_apfloat::Float; + use rustc_apfloat::Float; return match ty.kind { ty::Float(ast::FloatTy::F32) => { let l = ::rustc_apfloat::ieee::Single::from_bits(a); diff --git a/src/librustc_passes/intrinsicck.rs b/src/librustc_passes/intrinsicck.rs index e2bfcf18edb1..88fb78f85e42 100644 --- a/src/librustc_passes/intrinsicck.rs +++ b/src/librustc_passes/intrinsicck.rs @@ -150,7 +150,7 @@ impl ExprVisitor<'tcx> { _ => unreachable!(), }; let asm_ty = match ty.kind { - ty::Never | ty::Error => return None, + ty::Never | ty::Error(_) => return None, ty::Int(IntTy::I8) | ty::Uint(UintTy::U8) => Some(InlineAsmType::I8), ty::Int(IntTy::I16) | ty::Uint(UintTy::U16) => Some(InlineAsmType::I16), ty::Int(IntTy::I32) | ty::Uint(UintTy::U32) => Some(InlineAsmType::I32), @@ -167,7 +167,7 @@ impl ExprVisitor<'tcx> { let fields = &adt.non_enum_variant().fields; let elem_ty = fields[0].ty(self.tcx, substs); match elem_ty.kind { - ty::Never | ty::Error => return None, + ty::Never | ty::Error(_) => return None, ty::Int(IntTy::I8) | ty::Uint(UintTy::U8) => { Some(InlineAsmType::VecI8(fields.len() as u64)) } diff --git a/src/librustc_privacy/lib.rs b/src/librustc_privacy/lib.rs index 3c1b56a9ef40..9e6e7ea962bc 100644 --- a/src/librustc_privacy/lib.rs +++ b/src/librustc_privacy/lib.rs @@ -220,7 +220,7 @@ where | ty::Ref(..) | ty::FnPtr(..) | ty::Param(..) - | ty::Error + | ty::Error(_) | ty::GeneratorWitness(..) => {} ty::Bound(..) | ty::Placeholder(..) | ty::Infer(..) => { bug!("unexpected type: {:?}", ty) diff --git a/src/librustc_save_analysis/lib.rs b/src/librustc_save_analysis/lib.rs index 12d2c8c7eb9a..cae501e942b6 100644 --- a/src/librustc_save_analysis/lib.rs +++ b/src/librustc_save_analysis/lib.rs @@ -520,7 +520,7 @@ impl<'l, 'tcx> SaveContext<'l, 'tcx> { pub fn get_expr_data(&self, expr: &hir::Expr<'_>) -> Option { let hir_node = self.tcx.hir().expect_expr(expr.hir_id); let ty = self.tables.expr_ty_adjusted_opt(&hir_node); - if ty.is_none() || ty.unwrap().kind == ty::Error { + if ty.is_none() || matches!(ty.unwrap().kind, ty::Error(_)) { return None; } match expr.kind { diff --git a/src/librustc_symbol_mangling/v0.rs b/src/librustc_symbol_mangling/v0.rs index 1a536b6a4294..7d117b77cf5e 100644 --- a/src/librustc_symbol_mangling/v0.rs +++ b/src/librustc_symbol_mangling/v0.rs @@ -345,7 +345,7 @@ impl Printer<'tcx> for SymbolMangler<'tcx> { ty::Never => "z", // Placeholders (should be demangled as `_`). - ty::Param(_) | ty::Bound(..) | ty::Placeholder(_) | ty::Infer(_) | ty::Error => "p", + ty::Param(_) | ty::Bound(..) | ty::Placeholder(_) | ty::Infer(_) | ty::Error(_) => "p", _ => "", }; @@ -367,7 +367,7 @@ impl Printer<'tcx> for SymbolMangler<'tcx> { ty::Tuple(_) if ty.is_unit() => unreachable!(), // Placeholders, also handled as part of basic types. - ty::Param(_) | ty::Bound(..) | ty::Placeholder(_) | ty::Infer(_) | ty::Error => { + ty::Param(_) | ty::Bound(..) | ty::Placeholder(_) | ty::Infer(_) | ty::Error(_) => { unreachable!() } diff --git a/src/librustc_trait_selection/opaque_types.rs b/src/librustc_trait_selection/opaque_types.rs index d53a0ec9ef88..adccdd0b2617 100644 --- a/src/librustc_trait_selection/opaque_types.rs +++ b/src/librustc_trait_selection/opaque_types.rs @@ -941,7 +941,7 @@ impl TypeFolder<'tcx> for ReverseMapper<'tcx> { ) .emit(); - self.tcx().types.err + self.tcx().ty_error() } } } @@ -974,7 +974,7 @@ impl TypeFolder<'tcx> for ReverseMapper<'tcx> { ) .emit(); - self.tcx().mk_const(ty::Const { val: ty::ConstKind::Error, ty: ct.ty }) + self.tcx().const_error(ct.ty) } } } @@ -1002,7 +1002,7 @@ impl<'a, 'tcx> Instantiator<'a, 'tcx> { tcx, ty_op: |ty| { if ty.references_error() { - return tcx.types.err; + return tcx.ty_error(); } else if let ty::Opaque(def_id, substs) = ty.kind { // Check that this is `impl Trait` type is // declared by `parent_def_id` -- i.e., one whose diff --git a/src/librustc_trait_selection/traits/coherence.rs b/src/librustc_trait_selection/traits/coherence.rs index 85c2f9246afc..706cbf058b71 100644 --- a/src/librustc_trait_selection/traits/coherence.rs +++ b/src/librustc_trait_selection/traits/coherence.rs @@ -565,7 +565,7 @@ fn ty_is_non_local_constructor(ty: Ty<'_>, in_crate: InCrate) -> Option> } } - ty::Error => None, + ty::Error(_) => None, ty::Closure(..) | ty::Generator(..) | ty::GeneratorWitness(..) => { bug!("ty_is_local invoked on unexpected type: {:?}", ty) diff --git a/src/librustc_trait_selection/traits/error_reporting/mod.rs b/src/librustc_trait_selection/traits/error_reporting/mod.rs index d31e04cffd55..e5a6c9a2e391 100644 --- a/src/librustc_trait_selection/traits/error_reporting/mod.rs +++ b/src/librustc_trait_selection/traits/error_reporting/mod.rs @@ -1246,7 +1246,7 @@ impl<'a, 'tcx> InferCtxtPrivExt<'tcx> for InferCtxt<'a, 'tcx> { ty::Generator(..) => Some(18), ty::Foreign(..) => Some(19), ty::GeneratorWitness(..) => Some(20), - ty::Placeholder(..) | ty::Bound(..) | ty::Infer(..) | ty::Error => None, + ty::Placeholder(..) | ty::Bound(..) | ty::Infer(..) | ty::Error(_) => None, } } diff --git a/src/librustc_trait_selection/traits/error_reporting/suggestions.rs b/src/librustc_trait_selection/traits/error_reporting/suggestions.rs index 8796cfb52165..f4da25debd7c 100644 --- a/src/librustc_trait_selection/traits/error_reporting/suggestions.rs +++ b/src/librustc_trait_selection/traits/error_reporting/suggestions.rs @@ -948,7 +948,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { ty| { let ty = self.resolve_vars_if_possible(&ty); same &= - ty.kind != ty::Error + !matches!(ty.kind, ty::Error(_)) && last_ty.map_or(true, |last_ty| { // FIXME: ideally we would use `can_coerce` here instead, but `typeck` comes // *after* in the dependency graph. diff --git a/src/librustc_trait_selection/traits/project.rs b/src/librustc_trait_selection/traits/project.rs index 9492c3c34099..ed108613bfa2 100644 --- a/src/librustc_trait_selection/traits/project.rs +++ b/src/librustc_trait_selection/traits/project.rs @@ -784,7 +784,7 @@ struct Progress<'tcx> { impl<'tcx> Progress<'tcx> { fn error(tcx: TyCtxt<'tcx>) -> Self { - Progress { ty: tcx.types.err, obligations: vec![] } + Progress { ty: tcx.ty_error(), obligations: vec![] } } fn with_addl_obligations(mut self, mut obligations: Vec>) -> Self { @@ -1085,7 +1085,7 @@ fn assemble_candidates_from_impls<'cx, 'tcx>( | ty::Bound(..) | ty::Placeholder(..) | ty::Infer(..) - | ty::Error => false, + | ty::Error(_) => false, } } super::ImplSourceParam(..) => { @@ -1440,8 +1440,8 @@ fn confirm_param_env_candidate<'cx, 'tcx>( obligation, poly_cache_entry, e, ); debug!("confirm_param_env_candidate: {}", msg); - infcx.tcx.sess.delay_span_bug(obligation.cause.span, &msg); - Progress { ty: infcx.tcx.types.err, obligations: vec![] } + let err = infcx.tcx.ty_error_with_message(obligation.cause.span, &msg); + Progress { ty: err, obligations: vec![] } } } } @@ -1460,7 +1460,7 @@ fn confirm_impl_candidate<'cx, 'tcx>( let param_env = obligation.param_env; let assoc_ty = match assoc_ty_def(selcx, impl_def_id, assoc_item_id) { Ok(assoc_ty) => assoc_ty, - Err(ErrorReported) => return Progress { ty: tcx.types.err, obligations: nested }, + Err(ErrorReported) => return Progress { ty: tcx.ty_error(), obligations: nested }, }; if !assoc_ty.item.defaultness.has_value() { @@ -1472,16 +1472,18 @@ fn confirm_impl_candidate<'cx, 'tcx>( "confirm_impl_candidate: no associated type {:?} for {:?}", assoc_ty.item.ident, obligation.predicate ); - return Progress { ty: tcx.types.err, obligations: nested }; + return Progress { ty: tcx.ty_error(), obligations: nested }; } let substs = obligation.predicate.substs.rebase_onto(tcx, trait_def_id, substs); let substs = translate_substs(selcx.infcx(), param_env, impl_def_id, substs, assoc_ty.defining_node); let ty = tcx.type_of(assoc_ty.item.def_id); if substs.len() != tcx.generics_of(assoc_ty.item.def_id).count() { - tcx.sess - .delay_span_bug(DUMMY_SP, "impl item and trait item have different parameter counts"); - Progress { ty: tcx.types.err, obligations: nested } + let err = tcx.ty_error_with_message( + DUMMY_SP, + "impl item and trait item have different parameter counts", + ); + Progress { ty: err, obligations: nested } } else { Progress { ty: ty.subst(tcx, substs), obligations: nested } } diff --git a/src/librustc_trait_selection/traits/query/dropck_outlives.rs b/src/librustc_trait_selection/traits/query/dropck_outlives.rs index 856a2111fc82..d07c95270e00 100644 --- a/src/librustc_trait_selection/traits/query/dropck_outlives.rs +++ b/src/librustc_trait_selection/traits/query/dropck_outlives.rs @@ -101,7 +101,7 @@ pub fn trivial_dropck_outlives<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> bool { | ty::Ref(..) | ty::Str | ty::Foreign(..) - | ty::Error => true, + | ty::Error(_) => true, // [T; N] and [T] have same properties as T. ty::Array(ty, _) | ty::Slice(ty) => trivial_dropck_outlives(tcx, ty), diff --git a/src/librustc_trait_selection/traits/select/mod.rs b/src/librustc_trait_selection/traits/select/mod.rs index 7ebf30f61c09..3fd566eab437 100644 --- a/src/librustc_trait_selection/traits/select/mod.rs +++ b/src/librustc_trait_selection/traits/select/mod.rs @@ -1569,7 +1569,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { | ty::Array(..) | ty::Closure(..) | ty::Never - | ty::Error => { + | ty::Error(_) => { // safe for everything Where(ty::Binder::dummy(Vec::new())) } @@ -1613,7 +1613,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { | ty::Infer(ty::FloatVar(_)) | ty::FnDef(..) | ty::FnPtr(_) - | ty::Error => Where(ty::Binder::dummy(Vec::new())), + | ty::Error(_) => Where(ty::Binder::dummy(Vec::new())), ty::Uint(_) | ty::Int(_) @@ -1690,7 +1690,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { | ty::FnDef(..) | ty::FnPtr(_) | ty::Str - | ty::Error + | ty::Error(_) | ty::Infer(ty::IntVar(_) | ty::FloatVar(_)) | ty::Never | ty::Char => Vec::new(), diff --git a/src/librustc_trait_selection/traits/structural_match.rs b/src/librustc_trait_selection/traits/structural_match.rs index c4deb639140c..201edf27a655 100644 --- a/src/librustc_trait_selection/traits/structural_match.rs +++ b/src/librustc_trait_selection/traits/structural_match.rs @@ -219,7 +219,7 @@ impl<'a, 'tcx> TypeVisitor<'tcx> for Search<'a, 'tcx> { ty::Infer(_) | ty::Placeholder(_) | ty::Bound(..) => { bug!("unexpected type during structural-match checking: {:?}", ty); } - ty::Error => { + ty::Error(_) => { self.tcx().sess.delay_span_bug(self.span, "ty::Error in structural-match check"); // We still want to check other types after encountering an error, // as this may still emit relevant errors. diff --git a/src/librustc_trait_selection/traits/wf.rs b/src/librustc_trait_selection/traits/wf.rs index 5024392a0f98..e2e5f4e7c58b 100644 --- a/src/librustc_trait_selection/traits/wf.rs +++ b/src/librustc_trait_selection/traits/wf.rs @@ -391,7 +391,7 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> { )); } } - ty::ConstKind::Error + ty::ConstKind::Error(_) | ty::ConstKind::Param(_) | ty::ConstKind::Bound(..) | ty::ConstKind::Placeholder(..) => { @@ -411,7 +411,7 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> { | ty::Int(..) | ty::Uint(..) | ty::Float(..) - | ty::Error + | ty::Error(_) | ty::Str | ty::GeneratorWitness(..) | ty::Never diff --git a/src/librustc_traits/chalk/lowering.rs b/src/librustc_traits/chalk/lowering.rs index 9530b07e47cd..c9dd06e9f1ba 100644 --- a/src/librustc_traits/chalk/lowering.rs +++ b/src/librustc_traits/chalk/lowering.rs @@ -168,7 +168,7 @@ impl<'tcx> LowerInto<'tcx, chalk_ir::GoalData>> for ty::Predi ty::PredicateKind::WellFormed(arg) => match arg.unpack() { GenericArgKind::Type(ty) => match ty.kind { // These types are always WF. - ty::Str | ty::Placeholder(..) | ty::Error | ty::Never => { + ty::Str | ty::Placeholder(..) | ty::Error(_) | ty::Never => { chalk_ir::GoalData::All(chalk_ir::Goals::new(interner)) } @@ -376,7 +376,7 @@ impl<'tcx> LowerInto<'tcx, chalk_ir::Ty>> for Ty<'tcx> { }) .intern(interner), Infer(_infer) => unimplemented!(), - Error => unimplemented!(), + Error(_) => unimplemented!(), } } } diff --git a/src/librustc_traits/dropck_outlives.rs b/src/librustc_traits/dropck_outlives.rs index 11c48559bd68..6339f8288d54 100644 --- a/src/librustc_traits/dropck_outlives.rs +++ b/src/librustc_traits/dropck_outlives.rs @@ -271,7 +271,7 @@ fn dtorck_constraint_for_ty<'tcx>( constraints.dtorck_types.push(ty); } - ty::Placeholder(..) | ty::Bound(..) | ty::Infer(..) | ty::Error => { + ty::Placeholder(..) | ty::Bound(..) | ty::Infer(..) | ty::Error(_) => { // By the time this code runs, all type variables ought to // be fully resolved. return Err(NoSolution); diff --git a/src/librustc_ty/ty.rs b/src/librustc_ty/ty.rs index 99094246a637..cf70a845af0a 100644 --- a/src/librustc_ty/ty.rs +++ b/src/librustc_ty/ty.rs @@ -20,7 +20,7 @@ fn sized_constraint_for_ty<'tcx>( Bool | Char | Int(..) | Uint(..) | Float(..) | RawPtr(..) | Ref(..) | FnDef(..) | FnPtr(_) | Array(..) | Closure(..) | Generator(..) | Never => vec![], - Str | Dynamic(..) | Slice(_) | Foreign(..) | Error | GeneratorWitness(..) => { + Str | Dynamic(..) | Slice(_) | Foreign(..) | Error(_) | GeneratorWitness(..) => { // these are never sized - return the target type vec![ty] } diff --git a/src/librustc_typeck/astconv.rs b/src/librustc_typeck/astconv.rs index 267f3d9f3ef6..7cdcb2face82 100644 --- a/src/librustc_typeck/astconv.rs +++ b/src/librustc_typeck/astconv.rs @@ -819,7 +819,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { (GenericParamDefKind::Type { .. }, GenericArg::Type(ty)) => { if let (hir::TyKind::Infer, false) = (&ty.kind, self.allow_ty_infer()) { inferred_params.push(ty.span); - tcx.types.err.into() + tcx.ty_error().into() } else { self.ast_ty_to_ty(&ty).into() } @@ -845,7 +845,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { // careful! if default_needs_object_self(param) { missing_type_params.push(param.name.to_string()); - tcx.types.err.into() + tcx.ty_error().into() } else { // This is a default type parameter. self.normalize_ty( @@ -865,7 +865,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { self.ty_infer(param, span).into() } else { // We've already errored above about the mismatch. - tcx.types.err.into() + tcx.ty_error().into() } } GenericParamDefKind::Const => { @@ -876,7 +876,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { self.ct_infer(ty, Some(param), span).into() } else { // We've already errored above about the mismatch. - tcx.mk_const(ty::Const { val: ty::ConstKind::Error, ty }).into() + tcx.const_error(ty).into() } } } @@ -1607,7 +1607,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { "at least one trait is required for an object type" ) .emit(); - return tcx.types.err; + return tcx.ty_error(); } // Check that there are no gross object safety violations; @@ -1624,7 +1624,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { &object_safety_violations[..], ) .emit(); - return tcx.types.err; + return tcx.ty_error(); } } @@ -2434,7 +2434,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { &path_str, item_segment.ident.name, ); - return tcx.types.err; + return tcx.ty_error(); }; debug!("qpath_to_ty: self_type={:?}", self_ty); @@ -2792,7 +2792,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { } Res::Err => { self.set_tainted_by_errors(); - self.tcx().types.err + self.tcx().ty_error() } _ => span_bug!(span, "unexpected resolution: {:?}", path.res), } @@ -2860,7 +2860,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { }; self.associated_path_to_ty(ast_ty.hir_id, ast_ty.span, ty, res, segment, false) .map(|(ty, _, _)| ty) - .unwrap_or(tcx.types.err) + .unwrap_or_else(|_| tcx.ty_error()) } hir::TyKind::Array(ref ty, ref length) => { let length_def_id = tcx.hir().local_def_id(length.hir_id); @@ -2878,7 +2878,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { .span_label(ast_ty.span, "reserved keyword") .emit(); - tcx.types.err + tcx.ty_error() } hir::TyKind::Infer => { // Infer also appears as the type of arguments or return @@ -2887,7 +2887,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { // handled specially and will not descend into this routine. self.ty_infer(None, ast_ty.span) } - hir::TyKind::Err => tcx.types.err, + hir::TyKind::Err => tcx.ty_error(), }; debug!("ast_ty_to_ty: result_ty={:?}", result_ty); diff --git a/src/librustc_typeck/check/_match.rs b/src/librustc_typeck/check/_match.rs index fb139b5033b3..9e23f5df3c6a 100644 --- a/src/librustc_typeck/check/_match.rs +++ b/src/librustc_typeck/check/_match.rs @@ -105,7 +105,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { && i != 0 && self.if_fallback_coercion(expr.span, &arms[0].body, &mut coercion) { - tcx.types.err + tcx.ty_error() } else { // Only call this if this is not an `if` expr with an expected type and no `else` // clause to avoid duplicated type errors. (#60254) diff --git a/src/librustc_typeck/check/callee.rs b/src/librustc_typeck/check/callee.rs index aa316105f7f1..13695be83a9d 100644 --- a/src/librustc_typeck/check/callee.rs +++ b/src/librustc_typeck/check/callee.rs @@ -383,7 +383,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ( ty::Binder::bind(self.tcx.mk_fn_sig( self.err_args(arg_exprs.len()).into_iter(), - self.tcx.types.err, + self.tcx.ty_error(), false, hir::Unsafety::Normal, abi::Abi::Rust, diff --git a/src/librustc_typeck/check/cast.rs b/src/librustc_typeck/check/cast.rs index 46d6706cbf42..c55f0cf1fcb6 100644 --- a/src/librustc_typeck/check/cast.rs +++ b/src/librustc_typeck/check/cast.rs @@ -135,7 +135,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { | ty::Generator(..) | ty::Adt(..) | ty::Never - | ty::Error => { + | ty::Error(_) => { self.tcx .sess .delay_span_bug(span, &format!("`{:?}` should be sized but is not?", t)); diff --git a/src/librustc_typeck/check/closure.rs b/src/librustc_typeck/check/closure.rs index 206619588c71..6d09ddc925ff 100644 --- a/src/librustc_typeck/check/closure.rs +++ b/src/librustc_typeck/check/closure.rs @@ -700,7 +700,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let supplied_arguments = decl.inputs.iter().map(|a| { // Convert the types that the user supplied (if any), but ignore them. astconv.ast_ty_to_ty(a); - self.tcx.types.err + self.tcx.ty_error() }); if let hir::FnRetTy::Return(ref output) = decl.output { @@ -709,7 +709,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let result = ty::Binder::bind(self.tcx.mk_fn_sig( supplied_arguments, - self.tcx.types.err, + self.tcx.ty_error(), decl.c_variadic, hir::Unsafety::Normal, Abi::RustCall, diff --git a/src/librustc_typeck/check/coercion.rs b/src/librustc_typeck/check/coercion.rs index 15ec92568fb4..dd3f292fb568 100644 --- a/src/librustc_typeck/check/coercion.rs +++ b/src/librustc_typeck/check/coercion.rs @@ -162,7 +162,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { // Just ignore error types. if a.references_error() || b.references_error() { - return success(vec![], self.fcx.tcx.types.err, vec![]); + return success(vec![], self.fcx.tcx.ty_error(), vec![]); } if a.is_never() { @@ -864,7 +864,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let (adjustments, _) = self.register_infer_ok_obligations(ok); self.apply_adjustments(expr, adjustments); - Ok(if expr_ty.references_error() { self.tcx.types.err } else { target }) + Ok(if expr_ty.references_error() { self.tcx.ty_error() } else { target }) } /// Same as `try_coerce()`, but without side-effects. @@ -1239,7 +1239,7 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> { // If we see any error types, just propagate that error // upwards. if expression_ty.references_error() || self.merged_ty().references_error() { - self.final_ty = Some(fcx.tcx.types.err); + self.final_ty = Some(fcx.tcx.ty_error()); return; } @@ -1396,7 +1396,7 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> { err.emit_unless(assign_to_bool || unsized_return); - self.final_ty = Some(fcx.tcx.types.err); + self.final_ty = Some(fcx.tcx.ty_error()); } } } diff --git a/src/librustc_typeck/check/expr.rs b/src/librustc_typeck/check/expr.rs index bc3ef73d851e..69cfafc21648 100644 --- a/src/librustc_typeck/check/expr.rs +++ b/src/librustc_typeck/check/expr.rs @@ -248,7 +248,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { tcx.types.never } else { // There was an error; make type-check fail. - tcx.types.err + tcx.ty_error() } } ExprKind::Ret(ref expr_opt) => self.check_expr_return(expr_opt.as_deref(), expr), @@ -284,7 +284,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ExprKind::Field(ref base, field) => self.check_field(expr, needs, &base, field), ExprKind::Index(ref base, ref idx) => self.check_expr_index(base, idx, needs, expr), ExprKind::Yield(ref value, ref src) => self.check_expr_yield(value, expr, src), - hir::ExprKind::Err => tcx.types.err, + hir::ExprKind::Err => tcx.ty_error(), } } @@ -360,7 +360,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { tcx.sess.parse_sess.expr_parentheses_needed(&mut err, *sp, None); } err.emit(); - oprnd_t = tcx.types.err; + oprnd_t = tcx.ty_error(); } } hir::UnOp::UnNot => { @@ -410,7 +410,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let tm = ty::TypeAndMut { ty, mutbl }; match kind { - _ if tm.ty.references_error() => self.tcx.types.err, + _ if tm.ty.references_error() => self.tcx.ty_error(), hir::BorrowKind::Raw => { self.check_named_place_expr(oprnd); self.tcx.mk_ptr(tm) @@ -476,11 +476,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let ty = match res { Res::Err => { self.set_tainted_by_errors(); - tcx.types.err + tcx.ty_error() } Res::Def(DefKind::Ctor(_, CtorKind::Fictive), _) => { report_unexpected_variant_res(tcx, res, expr.span); - tcx.types.err + tcx.ty_error() } _ => self.instantiate_value_path(segs, opt_ty, res, expr.span, expr.hir_id).0, }; @@ -560,11 +560,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { Some(ctxt) => ctxt.coerce.as_ref().map(|coerce| coerce.expected_ty()), None => { // Avoid ICE when `break` is inside a closure (#65383). - self.tcx.sess.delay_span_bug( + return tcx.ty_error_with_message( expr.span, "break was outside loop, but no error was emitted", ); - return tcx.types.err; } } }; @@ -572,7 +571,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // If the loop context is not a `loop { }`, then break with // a value is illegal, and `opt_coerce_to` will be `None`. // Just set expectation to error in that case. - let coerce_to = opt_coerce_to.unwrap_or(tcx.types.err); + let coerce_to = opt_coerce_to.unwrap_or_else(|| tcx.ty_error()); // Recurse without `enclosing_breakables` borrowed. e_ty = self.check_expr_with_hint(e, coerce_to); @@ -592,11 +591,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { Some(ctxt) => ctxt, None => { // Avoid ICE when `break` is inside a closure (#65383). - self.tcx.sess.delay_span_bug( + return tcx.ty_error_with_message( expr.span, "break was outside loop, but no error was emitted", ); - return tcx.types.err; } }; @@ -649,14 +647,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // this can only happen if the `break` was not // inside a loop at all, which is caught by the // loop-checking pass. - self.tcx - .sess - .delay_span_bug(expr.span, "break was outside loop, but no error was emitted"); + let err = self.tcx.ty_error_with_message( + expr.span, + "break was outside loop, but no error was emitted", + ); // We still need to assign a type to the inner expression to // prevent the ICE in #43162. if let Some(ref e) = expr_opt { - self.check_expr_with_hint(e, tcx.types.err); + self.check_expr_with_hint(e, err); // ... except when we try to 'break rust;'. // ICE this expression in particular (see #43162). @@ -666,8 +665,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } } + // There was an error; make type-check fail. - tcx.types.err + err } } @@ -803,7 +803,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.require_type_is_sized(lhs_ty, lhs.span, traits::AssignmentLhsSized); if lhs_ty.references_error() || rhs_ty.references_error() { - self.tcx.types.err + self.tcx.ty_error() } else { self.tcx.mk_unit() } @@ -957,7 +957,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // Eagerly check for some obvious errors. if t_expr.references_error() || t_cast.references_error() { - self.tcx.types.err + self.tcx.ty_error() } else { // Defer other checks until we're done type checking. let mut deferred_cast_checks = self.deferred_cast_checks.borrow_mut(); @@ -966,7 +966,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { deferred_cast_checks.push(cast_check); t_cast } - Err(ErrorReported) => self.tcx.types.err, + Err(ErrorReported) => self.tcx.ty_error(), } } } @@ -1041,7 +1041,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { }; if element_ty.references_error() { - return tcx.types.err; + return tcx.ty_error(); } tcx.mk_ty(ty::Array(t, count)) @@ -1071,7 +1071,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { }); let tuple = self.tcx.mk_tup(elt_ts_iter); if tuple.references_error() { - self.tcx.types.err + self.tcx.ty_error() } else { self.require_type_is_sized(tuple, expr.span, traits::TupleInitializerSized); tuple @@ -1092,7 +1092,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { variant_ty } else { self.check_struct_fields_on_error(fields, base_expr); - return self.tcx.types.err; + return self.tcx.ty_error(); }; let path_span = match *qpath { @@ -1233,7 +1233,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.report_unknown_field(adt_ty, variant, field, ast_fields, kind_name, span); } - tcx.types.err + tcx.ty_error() }; // Make sure to give a type to the field even if there's @@ -1519,7 +1519,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .emit(); } - self.tcx().types.err + self.tcx().ty_error() } fn ban_nonexisting_field( @@ -1775,7 +1775,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } err.emit(); - self.tcx.types.err + self.tcx.ty_error() } } } @@ -1887,7 +1887,7 @@ pub(super) fn ty_kind_suggestion(ty: Ty<'_>) -> Option<&'static str> { ty::Char => "'a'", ty::Int(_) | ty::Uint(_) => "42", ty::Float(_) => "3.14159", - ty::Error | ty::Never => return None, + ty::Error(_) | ty::Never => return None, _ => "value", }) } diff --git a/src/librustc_typeck/check/method/confirm.rs b/src/librustc_typeck/check/method/confirm.rs index 007794ce1b7f..8651c643cee8 100644 --- a/src/librustc_typeck/check/method/confirm.rs +++ b/src/librustc_typeck/check/method/confirm.rs @@ -141,11 +141,10 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> { let (_, n) = match autoderef.nth(pick.autoderefs) { Some(n) => n, None => { - self.tcx.sess.delay_span_bug( + return self.tcx.ty_error_with_message( rustc_span::DUMMY_SP, &format!("failed autoderef {}", pick.autoderefs), ); - return self.tcx.types.err; } }; assert_eq!(n, pick.autoderefs); diff --git a/src/librustc_typeck/check/method/probe.rs b/src/librustc_typeck/check/method/probe.rs index 37652330108c..93bcd5cf2914 100644 --- a/src/librustc_typeck/check/method/probe.rs +++ b/src/librustc_typeck/check/method/probe.rs @@ -400,7 +400,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .probe_instantiate_query_response(span, &orig_values, ty) .unwrap_or_else(|_| span_bug!(span, "instantiating {:?} failed?", ty)); let ty = self.structurally_resolved_type(span, ty.value); - assert_eq!(ty, self.tcx.types.err); + assert!(matches!(ty.kind, ty::Error(_))); return Err(MethodError::NoMatch(NoMatchData::new( Vec::new(), Vec::new(), @@ -478,7 +478,7 @@ fn method_autoderef_steps<'tcx>( let final_ty = autoderef.maybe_ambiguous_final_ty(); let opt_bad_ty = match final_ty.kind { - ty::Infer(ty::TyVar(_)) | ty::Error => Some(MethodAutoderefBadTy { + ty::Infer(ty::TyVar(_)) | ty::Error(_) => Some(MethodAutoderefBadTy { reached_raw_pointer, ty: infcx .make_query_response_ignoring_pending_obligations(inference_vars, final_ty), diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index a409e20953da..96f1c6ff1610 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -967,8 +967,7 @@ fn diagnostic_only_typeck_tables_of<'tcx>( ) -> &ty::TypeckTables<'tcx> { let fallback = move || { let span = tcx.hir().span(tcx.hir().as_local_hir_id(def_id)); - tcx.sess.delay_span_bug(span, "diagnostic only typeck table used"); - tcx.types.err + tcx.ty_error_with_message(span, "diagnostic only typeck table used") }; typeck_tables_of_with_fallback(tcx, def_id, fallback) } @@ -3387,7 +3386,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { pub fn node_ty(&self, id: hir::HirId) -> Ty<'tcx> { match self.tables.borrow().node_types().get(id) { Some(&t) => t, - None if self.is_tainted_by_errors() => self.tcx.types.err, + None if self.is_tainted_by_errors() => self.tcx.ty_error(), None => { bug!( "no type for node {}: {} in fcx {}", @@ -3501,7 +3500,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { assert!(ty.is_ty_infer()); let fallback = match self.type_is_unconstrained_numeric(ty) { - _ if self.is_tainted_by_errors() => self.tcx().types.err, + _ if self.is_tainted_by_errors() => self.tcx().ty_error(), UnconstrainedInt => self.tcx.types.i32, UnconstrainedFloat => self.tcx.types.f64, Neither if self.type_var_diverges(ty) => self.tcx.mk_diverging_default(), @@ -3774,7 +3773,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { tuple_arguments, None, ); - return self.tcx.types.err; + return self.tcx.ty_error(); } let method = method.unwrap(); @@ -4161,7 +4160,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } fn err_args(&self, len: usize) -> Vec> { - vec![self.tcx.types.err; len] + vec![self.tcx.ty_error(); len] } /// Given a vec of evaluated `FulfillmentError`s and an `fn` call argument expressions, we walk @@ -4305,7 +4304,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { opt_ty.unwrap_or_else(|| self.next_float_var()) } ast::LitKind::Bool(_) => tcx.types.bool, - ast::LitKind::Err(_) => tcx.types.err, + ast::LitKind::Err(_) => tcx.ty_error(), } } @@ -4442,7 +4441,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { }; let result = AstConv::associated_path_to_ty(self, hir_id, path_span, ty, res, segment, true); - let ty = result.map(|(ty, _, _)| ty).unwrap_or(self.tcx().types.err); + let ty = result.map(|(ty, _, _)| ty).unwrap_or_else(|_| self.tcx().ty_error()); let result = result.map(|(_, kind, def_id)| (kind, def_id)); // Write back the new resolution. @@ -4570,7 +4569,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ty: Ty<'tcx>, ) { if ty.references_error() { - // Override the types everywhere with `types.err` to avoid knock on errors. + // Override the types everywhere with `err()` to avoid knock on errors. self.write_ty(local.hir_id, ty); self.write_ty(local.pat.hir_id, ty); let local_ty = LocalTy { decl_ty, revealed_ty: ty }; @@ -4790,7 +4789,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let mut ty = ctxt.coerce.unwrap().complete(self); if self.has_errors.get() || ty.references_error() { - ty = self.tcx.types.err + ty = self.tcx.ty_error() } self.write_ty(blk.hir_id, ty); @@ -5378,7 +5377,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { _ => return None, }; let last_expr_ty = self.node_ty(last_expr.hir_id); - if matches!(last_expr_ty.kind, ty::Error) + if matches!(last_expr_ty.kind, ty::Error(_)) || self.can_sub(self.param_env, last_expr_ty, expected_ty).is_err() { return None; @@ -5538,7 +5537,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } err.emit(); - return (tcx.types.err, res); + return (tcx.ty_error(), res); } } } else { @@ -5731,8 +5730,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .note("type must be known at this point") .emit(); } - self.demand_suptype(sp, self.tcx.types.err, ty); - self.tcx.types.err + let err = self.tcx.ty_error(); + self.demand_suptype(sp, err, ty); + err } } diff --git a/src/librustc_typeck/check/op.rs b/src/librustc_typeck/check/op.rs index a3a27dc138be..fe5087091164 100644 --- a/src/librustc_typeck/check/op.rs +++ b/src/librustc_typeck/check/op.rs @@ -497,7 +497,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } } - self.tcx.types.err + self.tcx.ty_error() } }; @@ -709,7 +709,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } err.emit(); } - self.tcx.types.err + self.tcx.ty_error() } } } diff --git a/src/librustc_typeck/check/pat.rs b/src/librustc_typeck/check/pat.rs index 8a10427260ee..7965c9c9ce12 100644 --- a/src/librustc_typeck/check/pat.rs +++ b/src/librustc_typeck/check/pat.rs @@ -442,7 +442,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // There exists a side that didn't meet our criteria that the end-point // be of a numeric or char type, as checked in `calc_side` above. self.emit_err_pat_range(span, lhs, rhs); - return self.tcx.types.err; + return self.tcx.ty_error(); } // Now that we know the types can be unified we find the unified type @@ -673,11 +673,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { { variant_ty } else { + let err = self.tcx.ty_error(); for field in fields { let ti = TopInfo { parent_pat: Some(&pat), ..ti }; - self.check_pat(&field.pat, self.tcx.types.err, def_bm, ti); + self.check_pat(&field.pat, err, def_bm, ti); } - return self.tcx.types.err; + return err; }; // Type-check the path. @@ -687,7 +688,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if self.check_struct_pat_fields(pat_ty, &pat, variant, fields, etc, def_bm, ti) { pat_ty } else { - self.tcx.types.err + self.tcx.ty_error() } } @@ -705,11 +706,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { match res { Res::Err => { self.set_tainted_by_errors(); - return tcx.types.err; + return tcx.ty_error(); } Res::Def(DefKind::AssocFn | DefKind::Ctor(_, CtorKind::Fictive | CtorKind::Fn), _) => { report_unexpected_variant_res(tcx, res, pat.span); - return tcx.types.err; + return tcx.ty_error(); } Res::SelfCtor(..) | Res::Def( @@ -788,7 +789,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let on_error = || { let parent_pat = Some(pat); for pat in subpats { - self.check_pat(&pat, tcx.types.err, def_bm, TopInfo { parent_pat, ..ti }); + self.check_pat(&pat, tcx.ty_error(), def_bm, TopInfo { parent_pat, ..ti }); } }; let report_unexpected_res = |res: Res| { @@ -824,7 +825,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if res == Res::Err { self.set_tainted_by_errors(); on_error(); - return self.tcx.types.err; + return self.tcx.ty_error(); } // Type-check the path. @@ -832,18 +833,18 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.instantiate_value_path(segments, opt_ty, res, pat.span, pat.hir_id); if !pat_ty.is_fn() { report_unexpected_res(res); - return tcx.types.err; + return tcx.ty_error(); } let variant = match res { Res::Err => { self.set_tainted_by_errors(); on_error(); - return tcx.types.err; + return tcx.ty_error(); } Res::Def(DefKind::AssocConst | DefKind::AssocFn, _) => { report_unexpected_res(res); - return tcx.types.err; + return tcx.ty_error(); } Res::Def(DefKind::Ctor(_, CtorKind::Fn), _) => tcx.expect_variant_res(res), _ => bug!("unexpected pattern resolution: {:?}", res), @@ -880,7 +881,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // Pattern has wrong number of fields. self.e0023(pat.span, res, qpath, subpats, &variant.fields, expected, had_err); on_error(); - return tcx.types.err; + return tcx.ty_error(); } pat_ty } @@ -1001,9 +1002,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { err.emit(); // Walk subpatterns with an expected type of `err` in this case to silence // further errors being emitted when using the bindings. #50333 - let element_tys_iter = (0..max_len).map(|_| tcx.types.err); + let element_tys_iter = (0..max_len).map(|_| tcx.ty_error()); for (_, elem) in elements.iter().enumerate_and_adjust(max_len, ddpos) { - self.check_pat(elem, &tcx.types.err, def_bm, ti); + self.check_pat(elem, &tcx.ty_error(), def_bm, ti); } tcx.mk_tup(element_tys_iter) } else { @@ -1052,7 +1053,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { Occupied(occupied) => { self.error_field_already_bound(span, field.ident, *occupied.get()); no_field_errors = false; - tcx.types.err + tcx.ty_error() } Vacant(vacant) => { vacant.insert(span); @@ -1066,7 +1067,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .unwrap_or_else(|| { inexistent_fields.push(field.ident); no_field_errors = false; - tcx.types.err + tcx.ty_error() }) } }; @@ -1281,7 +1282,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.demand_eqtype_pat(span, expected, box_ty, ti); (box_ty, inner_ty) } else { - (tcx.types.err, tcx.types.err) + let err = tcx.ty_error(); + (err, err) }; self.check_pat(&inner, inner_ty, def_bm, ti); box_ty @@ -1327,7 +1329,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } } else { - (tcx.types.err, tcx.types.err) + let err = tcx.ty_error(); + (err, err) }; self.check_pat(&inner, inner_ty, def_bm, TopInfo { parent_pat: Some(&pat), ..ti }); rptr_ty @@ -1378,7 +1381,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if !expected.references_error() { self.error_expected_array_or_slice(span, expected); } - let err = self.tcx.types.err; + let err = self.tcx.ty_error(); (err, Some(err), err) } }; @@ -1445,7 +1448,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } // If we get here, we must have emitted an error. - (Some(self.tcx.types.err), arr_ty) + (Some(self.tcx.ty_error()), arr_ty) } fn error_scrutinee_inconsistent_length(&self, span: Span, min_len: u64, size: u64) { diff --git a/src/librustc_typeck/check/upvar.rs b/src/librustc_typeck/check/upvar.rs index 19a23e5a5947..8403c99f01bb 100644 --- a/src/librustc_typeck/check/upvar.rs +++ b/src/librustc_typeck/check/upvar.rs @@ -91,7 +91,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let (closure_def_id, substs) = match ty.kind { ty::Closure(def_id, substs) => (def_id, UpvarSubsts::Closure(substs)), ty::Generator(def_id, substs, _) => (def_id, UpvarSubsts::Generator(substs)), - ty::Error => { + ty::Error(_) => { // #51714: skip analysis when we have already encountered type errors return; } diff --git a/src/librustc_typeck/check/writeback.rs b/src/librustc_typeck/check/writeback.rs index 159d3d7a538a..ba806430f17f 100644 --- a/src/librustc_typeck/check/writeback.rs +++ b/src/librustc_typeck/check/writeback.rs @@ -208,11 +208,10 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> { // to access an unexistend index. We assume that more relevant errors will // already have been emitted, so we only gate on this with an ICE if no // error has been emitted. (#64638) - self.tcx().sess.delay_span_bug( + self.fcx.tcx.ty_error_with_message( e.span, &format!("bad index {:?} for base: `{:?}`", index, base), - ); - self.fcx.tcx.types.err + ) }); let index_ty = self.fcx.resolve_vars_if_possible(&index_ty); @@ -681,7 +680,7 @@ impl<'cx, 'tcx> TypeFolder<'tcx> for Resolver<'cx, 'tcx> { debug!("Resolver::fold_ty: input type `{:?}` not fully resolvable", t); self.report_type_error(t); self.replaced_with_error = true; - self.tcx().types.err + self.tcx().ty_error() } } } @@ -698,7 +697,7 @@ impl<'cx, 'tcx> TypeFolder<'tcx> for Resolver<'cx, 'tcx> { debug!("Resolver::fold_const: input const `{:?}` not fully resolvable", ct); self.report_const_error(ct); self.replaced_with_error = true; - self.tcx().mk_const(ty::Const { val: ty::ConstKind::Error, ty: ct.ty }) + self.tcx().const_error(ct.ty) } } } diff --git a/src/librustc_typeck/coherence/builtin.rs b/src/librustc_typeck/coherence/builtin.rs index c5dd314dc655..8c6161a62647 100644 --- a/src/librustc_typeck/coherence/builtin.rs +++ b/src/librustc_typeck/coherence/builtin.rs @@ -50,7 +50,7 @@ impl<'tcx> Checker<'tcx> { fn visit_implementation_of_drop(tcx: TyCtxt<'_>, impl_did: LocalDefId) { // Destructors only work on nominal types. - if let ty::Adt(..) | ty::Error = tcx.type_of(impl_did).kind { + if let ty::Adt(..) | ty::Error(_) = tcx.type_of(impl_did).kind { return; } diff --git a/src/librustc_typeck/coherence/inherent_impls.rs b/src/librustc_typeck/coherence/inherent_impls.rs index 653b7b8f2a5b..93ee87f6c572 100644 --- a/src/librustc_typeck/coherence/inherent_impls.rs +++ b/src/librustc_typeck/coherence/inherent_impls.rs @@ -296,7 +296,7 @@ impl ItemLikeVisitor<'v> for InherentCollect<'tcx> { item.span, ); } - ty::Error => {} + ty::Error(_) => {} _ => { struct_span_err!( self.tcx.sess, diff --git a/src/librustc_typeck/collect.rs b/src/librustc_typeck/collect.rs index 1d59d749634e..3bd75095bb60 100644 --- a/src/librustc_typeck/collect.rs +++ b/src/librustc_typeck/collect.rs @@ -307,8 +307,7 @@ impl AstConv<'tcx> for ItemCtxt<'tcx> { } fn ty_infer(&self, _: Option<&ty::GenericParamDef>, span: Span) -> Ty<'tcx> { - self.tcx().sess.delay_span_bug(span, "bad placeholder type"); - self.tcx().types.err + self.tcx().ty_error_with_message(span, "bad_placeholder_type") } fn ct_infer( @@ -318,8 +317,7 @@ impl AstConv<'tcx> for ItemCtxt<'tcx> { span: Span, ) -> &'tcx Const<'tcx> { bad_placeholder_type(self.tcx(), vec![span]).emit(); - - self.tcx().mk_const(ty::Const { val: ty::ConstKind::Error, ty }) + self.tcx().const_error(ty) } fn projected_ty_from_poly_trait_ref( @@ -419,7 +417,7 @@ impl AstConv<'tcx> for ItemCtxt<'tcx> { _ => {} } err.emit(); - self.tcx().types.err + self.tcx().ty_error() } } @@ -1465,7 +1463,7 @@ fn fn_sig(tcx: TyCtxt<'_>, def_id: DefId) -> ty::PolyFnSig<'_> { visitor.visit_ty(ty); let mut diag = bad_placeholder_type(tcx, visitor.0); let ret_ty = fn_sig.output(); - if ret_ty != tcx.types.err { + if ret_ty != tcx.ty_error() { diag.span_suggestion( ty.span, "replace with the correct return type", @@ -2004,12 +2002,11 @@ fn associated_item_predicates( // once they are handled by the trait system. ty::GenericParamDefKind::Type { .. } => { unimplemented_error("type"); - tcx.types.err.into() + tcx.ty_error().into() } ty::GenericParamDefKind::Const => { unimplemented_error("const"); - tcx.mk_const(ty::Const { val: ty::ConstKind::Error, ty: tcx.type_of(param.def_id) }) - .into() + tcx.const_error(tcx.type_of(param.def_id)).into() } } }; diff --git a/src/librustc_typeck/collect/type_of.rs b/src/librustc_typeck/collect/type_of.rs index 549a20531e29..cf5f2ec69d8d 100644 --- a/src/librustc_typeck/collect/type_of.rs +++ b/src/librustc_typeck/collect/type_of.rs @@ -127,7 +127,7 @@ pub(super) fn type_of(tcx: TyCtxt<'_>, def_id: DefId) -> Ty<'_> { // Some error in the // owner fn prevented us from populating // the `concrete_opaque_types` table. - tcx.types.err + tcx.ty_error() } else { // We failed to resolve the opaque type or it // resolves to itself. Return the non-revealed @@ -217,11 +217,10 @@ pub(super) fn type_of(tcx: TyCtxt<'_>, def_id: DefId) -> Ty<'_> { }) | Node::TraitRef(&TraitRef { path, .. }) => &*path, _ => { - tcx.sess.delay_span_bug( + return tcx.ty_error_with_message( DUMMY_SP, &format!("unexpected const parent path {:?}", parent_node), ); - return tcx.types.err; } }; @@ -254,14 +253,13 @@ pub(super) fn type_of(tcx: TyCtxt<'_>, def_id: DefId) -> Ty<'_> { } Res::Def(_, def_id) => tcx.generics_of(def_id), res => { - tcx.sess.delay_span_bug( + return tcx.ty_error_with_message( DUMMY_SP, &format!( "unexpected anon const res {:?} in path: {:?}", res, path, ), - ); - return tcx.types.err; + ); } }; @@ -283,24 +281,21 @@ pub(super) fn type_of(tcx: TyCtxt<'_>, def_id: DefId) -> Ty<'_> { } else { // This is no generic parameter associated with the arg. This is // probably from an extra arg where one is not needed. - tcx.sess.delay_span_bug( + tcx.ty_error_with_message( DUMMY_SP, &format!( - "missing generic parameter for `AnonConst`, parent: {:?}, res: {:?}", + "missing generic parameter for `AnonConst`, \ + parent: {:?}, res: {:?}", parent_node, res ), - ); - tcx.types.err + ) } } - x => { - tcx.sess.delay_span_bug( - DUMMY_SP, - &format!("unexpected const parent in type_of_def_id(): {:?}", x), - ); - tcx.types.err - } + x => tcx.ty_error_with_message( + DUMMY_SP, + &format!("unexpected const parent in type_of_def_id(): {:?}", x), + ), } } @@ -568,7 +563,7 @@ fn find_opaque_ty_constraints(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Ty<'_> { None => { let span = tcx.def_span(def_id); tcx.sess.span_err(span, "could not find defining uses"); - tcx.types.err + tcx.ty_error() } } } @@ -605,7 +600,7 @@ fn let_position_impl_trait_type(tcx: TyCtxt<'_>, opaque_ty_id: LocalDefId) -> Ty if let Some(ErrorReported) = owner_tables.tainted_by_errors { // Some error in the owner fn prevented us from populating the // `concrete_opaque_types` table. - tcx.types.err + tcx.ty_error() } else { // We failed to resolve the opaque type or it resolves to // itself. Return the non-revealed type, which should result in @@ -655,7 +650,7 @@ fn infer_placeholder_type( } None => { let mut diag = bad_placeholder_type(tcx, vec![span]); - if ty != tcx.types.err { + if !matches!(ty.kind, ty::Error(_)) { diag.span_suggestion( span, "replace `_` with the correct type", diff --git a/src/librustc_typeck/variance/constraints.rs b/src/librustc_typeck/variance/constraints.rs index eee0f764373a..cae09267994e 100644 --- a/src/librustc_typeck/variance/constraints.rs +++ b/src/librustc_typeck/variance/constraints.rs @@ -339,7 +339,7 @@ impl<'a, 'tcx> ConstraintContext<'a, 'tcx> { self.add_constraints_from_sig(current, sig, variance); } - ty::Error => { + ty::Error(_) => { // we encounter this when walking the trait references for object // types, where we use Error as the Self type } diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index adb2ae9a5d66..73fe87b05d47 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -1717,7 +1717,7 @@ impl<'tcx> Clean for Ty<'tcx> { ty::Placeholder(..) => panic!("Placeholder"), ty::GeneratorWitness(..) => panic!("GeneratorWitness"), ty::Infer(..) => panic!("Infer"), - ty::Error => panic!("Error"), + ty::Error(_) => panic!("Error"), } } } diff --git a/src/test/ui-fulldeps/internal-lints/ty_tykind_usage.rs b/src/test/ui-fulldeps/internal-lints/ty_tykind_usage.rs index 27fe432e96de..973294e985f7 100644 --- a/src/test/ui-fulldeps/internal-lints/ty_tykind_usage.rs +++ b/src/test/ui-fulldeps/internal-lints/ty_tykind_usage.rs @@ -37,7 +37,7 @@ fn main() { TyKind::Bound(..) => (), //~ ERROR usage of `ty::TyKind::` TyKind::Placeholder(..) => (), //~ ERROR usage of `ty::TyKind::` TyKind::Infer(..) => (), //~ ERROR usage of `ty::TyKind::` - TyKind::Error => (), //~ ERROR usage of `ty::TyKind::` + TyKind::Error(_) => (), //~ ERROR usage of `ty::TyKind::` } if let ty::Int(int_ty) = kind {} diff --git a/src/test/ui-fulldeps/internal-lints/ty_tykind_usage.stderr b/src/test/ui-fulldeps/internal-lints/ty_tykind_usage.stderr index 0486c90a5a07..d6e4c85c190d 100644 --- a/src/test/ui-fulldeps/internal-lints/ty_tykind_usage.stderr +++ b/src/test/ui-fulldeps/internal-lints/ty_tykind_usage.stderr @@ -169,7 +169,7 @@ LL | TyKind::Infer(..) => (), error: usage of `ty::TyKind::` --> $DIR/ty_tykind_usage.rs:40:9 | -LL | TyKind::Error => (), +LL | TyKind::Error(_) => (), | ^^^^^^ help: try using ty:: directly: `ty` error: usage of `ty::TyKind::` From e855b90a8e159d27e014847ef50d9536aa1249d0 Mon Sep 17 00:00:00 2001 From: mark Date: Tue, 26 May 2020 12:49:11 -0500 Subject: [PATCH 069/123] track caller for delay_span_bug --- src/librustc_errors/lib.rs | 4 ++++ src/librustc_middle/ty/context.rs | 26 ++++---------------------- 2 files changed, 8 insertions(+), 22 deletions(-) diff --git a/src/librustc_errors/lib.rs b/src/librustc_errors/lib.rs index 7261c638ce01..0c1418d3cad2 100644 --- a/src/librustc_errors/lib.rs +++ b/src/librustc_errors/lib.rs @@ -5,6 +5,7 @@ #![doc(html_root_url = "https://doc.rust-lang.org/nightly/")] #![feature(crate_visibility_modifier)] #![feature(nll)] +#![feature(track_caller)] pub use emitter::ColorConfig; @@ -621,6 +622,7 @@ impl Handler { self.inner.borrow_mut().span_bug(span, msg) } + #[track_caller] pub fn delay_span_bug(&self, span: impl Into, msg: &str) { self.inner.borrow_mut().delay_span_bug(span, msg) } @@ -873,6 +875,7 @@ impl HandlerInner { self.emit_diagnostic(diag.set_span(sp)); } + #[track_caller] fn delay_span_bug(&mut self, sp: impl Into, msg: &str) { // This is technically `self.treat_err_as_bug()` but `delay_span_bug` is called before // incrementing `err_count` by one, so we need to +1 the comparing. @@ -883,6 +886,7 @@ impl HandlerInner { } let mut diagnostic = Diagnostic::new(Level::Bug, msg); diagnostic.set_span(sp.into()); + diagnostic.note(&format!("delayed at {}", std::panic::Location::caller())); self.delay_as_bug(diagnostic) } diff --git a/src/librustc_middle/ty/context.rs b/src/librustc_middle/ty/context.rs index 4fe8173becff..1715b545662b 100644 --- a/src/librustc_middle/ty/context.rs +++ b/src/librustc_middle/ty/context.rs @@ -1144,40 +1144,22 @@ impl<'tcx> TyCtxt<'tcx> { /// Constructs a `TyKind::Error` type and registers a `delay_span_bug` to ensure it gets used. #[track_caller] pub fn ty_error(self) -> Ty<'tcx> { - self.err_with_message_and_location( - DUMMY_SP, - "TyKind::Error constructed but no error reported", - std::panic::Location::caller(), - ) + self.ty_error_with_message(DUMMY_SP, "TyKind::Error constructed but no error reported") } /// Constructs a `TyKind::Error` type and registers a `delay_span_bug` with the given `msg to /// ensure it gets used. #[track_caller] pub fn ty_error_with_message>(self, span: S, msg: &str) -> Ty<'tcx> { - self.err_with_message_and_location(span, msg, std::panic::Location::caller()) - } - - pub fn err_with_message_and_location>( - self, - span: S, - msg: &str, - loc: &'static std::panic::Location<'static>, - ) -> Ty<'tcx> { - self.sess.delay_span_bug(span, &format!("{}: {}", loc, msg)); + self.sess.delay_span_bug(span, msg); self.mk_ty(Error(super::sty::DelaySpanBugEmitted(()))) } /// Like `err` but for constants. #[track_caller] pub fn const_error(self, ty: Ty<'tcx>) -> &'tcx Const<'tcx> { - self.sess.delay_span_bug( - DUMMY_SP, - &format!( - "ty::ConstKind::Error constructed but no error reported. {}", - std::panic::Location::caller() - ), - ); + self.sess + .delay_span_bug(DUMMY_SP, "ty::ConstKind::Error constructed but no error reported."); self.mk_const(ty::Const { val: ty::ConstKind::Error(super::sty::DelaySpanBugEmitted(())), ty, From 5068ae1ca05b2be0c2a98206a58d894aa620b312 Mon Sep 17 00:00:00 2001 From: Rich Kadel Date: Wed, 3 Jun 2020 21:19:34 -0700 Subject: [PATCH 070/123] [WIP] injects llvm intrinsic instrprof.increment for coverage reports This initial version only injects counters at the top of each function. Rust Coverage will require injecting additional counters at each conditional code branch. --- src/libcore/intrinsics.rs | 9 + src/librustc_codegen_llvm/builder.rs | 27 ++ src/librustc_codegen_llvm/context.rs | 2 + src/librustc_codegen_llvm/intrinsic.rs | 21 + src/librustc_codegen_llvm/llvm/ffi.rs | 1 + src/librustc_codegen_ssa/back/write.rs | 6 + src/librustc_codegen_ssa/mir/block.rs | 6 +- src/librustc_codegen_ssa/traits/builder.rs | 8 + src/librustc_codegen_ssa/traits/intrinsic.rs | 1 + src/librustc_hir/lang_items.rs | 2 + src/librustc_interface/tests.rs | 1 + src/librustc_middle/mir/mono.rs | 1 + src/librustc_middle/ty/instance.rs | 7 + src/librustc_middle/ty/mod.rs | 1 + src/librustc_middle/ty/structural_impls.rs | 11 +- src/librustc_mir/interpret/terminator.rs | 3 + src/librustc_mir/monomorphize/collector.rs | 5 +- src/librustc_mir/monomorphize/partitioning.rs | 2 + src/librustc_mir/shim.rs | 3 + .../transform/instrument_coverage.rs | 100 +++++ src/librustc_mir/transform/mod.rs | 3 + src/librustc_session/options.rs | 3 + src/librustc_span/symbol.rs | 1 + src/librustc_ty/instance.rs | 4 + src/rustllvm/RustWrapper.cpp | 6 + .../codegen/coverage-experiments/Cargo.lock | 5 + .../codegen/coverage-experiments/Cargo.toml | 103 +++++ .../README-THIS-IS-TEMPORARY.md | 157 ++++++++ .../src/coverage_injection_test.rs | 335 ++++++++++++++++ .../src/coverage_injection_test2.rs | 320 ++++++++++++++++ .../src/coverage_injection_test_alt.rs | 362 ++++++++++++++++++ .../coverage-experiments/src/drop_trait.rs | 25 ++ .../src/drop_trait_with_comments_prints.rs | 53 +++ .../codegen/coverage-experiments/src/for.rs | 41 ++ .../src/for_with_comments.rs | 24 ++ .../codegen/coverage-experiments/src/if.rs | 80 ++++ .../src/if_with_comments.rs | 39 ++ .../src/increment_intrinsic.rs | 11 + .../coverage-experiments/src/just_main.rs | 3 + .../coverage-experiments/src/lazy_boolean.rs | 17 + .../src/loop_break_value.rs | 15 + .../codegen/coverage-experiments/src/match.rs | 22 ++ .../src/match_with_increment.rs | 305 +++++++++++++++ .../src/match_with_increment_alt.rs | 296 ++++++++++++++ .../src/match_without_increment.mir | 0 .../src/match_without_increment.rs | 5 + .../src/match_without_increment_alt.mir | 0 ..._mark_err_status_handling_with_comments.rs | 24 ++ .../codegen/coverage-experiments/src/while.rs | 23 ++ .../coverage-experiments/src/while_clean.rs | 6 + .../src/while_early_return.rs | 10 + .../src/while_with_comments.rs | 51 +++ 52 files changed, 2561 insertions(+), 5 deletions(-) create mode 100644 src/librustc_mir/transform/instrument_coverage.rs create mode 100644 src/test/codegen/coverage-experiments/Cargo.lock create mode 100644 src/test/codegen/coverage-experiments/Cargo.toml create mode 100644 src/test/codegen/coverage-experiments/README-THIS-IS-TEMPORARY.md create mode 100644 src/test/codegen/coverage-experiments/src/coverage_injection_test.rs create mode 100644 src/test/codegen/coverage-experiments/src/coverage_injection_test2.rs create mode 100644 src/test/codegen/coverage-experiments/src/coverage_injection_test_alt.rs create mode 100644 src/test/codegen/coverage-experiments/src/drop_trait.rs create mode 100644 src/test/codegen/coverage-experiments/src/drop_trait_with_comments_prints.rs create mode 100644 src/test/codegen/coverage-experiments/src/for.rs create mode 100644 src/test/codegen/coverage-experiments/src/for_with_comments.rs create mode 100644 src/test/codegen/coverage-experiments/src/if.rs create mode 100644 src/test/codegen/coverage-experiments/src/if_with_comments.rs create mode 100644 src/test/codegen/coverage-experiments/src/increment_intrinsic.rs create mode 100644 src/test/codegen/coverage-experiments/src/just_main.rs create mode 100644 src/test/codegen/coverage-experiments/src/lazy_boolean.rs create mode 100644 src/test/codegen/coverage-experiments/src/loop_break_value.rs create mode 100644 src/test/codegen/coverage-experiments/src/match.rs create mode 100644 src/test/codegen/coverage-experiments/src/match_with_increment.rs create mode 100644 src/test/codegen/coverage-experiments/src/match_with_increment_alt.rs create mode 100644 src/test/codegen/coverage-experiments/src/match_without_increment.mir create mode 100644 src/test/codegen/coverage-experiments/src/match_without_increment.rs create mode 100644 src/test/codegen/coverage-experiments/src/match_without_increment_alt.mir create mode 100644 src/test/codegen/coverage-experiments/src/question_mark_err_status_handling_with_comments.rs create mode 100644 src/test/codegen/coverage-experiments/src/while.rs create mode 100644 src/test/codegen/coverage-experiments/src/while_clean.rs create mode 100644 src/test/codegen/coverage-experiments/src/while_early_return.rs create mode 100644 src/test/codegen/coverage-experiments/src/while_with_comments.rs diff --git a/src/libcore/intrinsics.rs b/src/libcore/intrinsics.rs index 85076a573b52..abb35e838ea2 100644 --- a/src/libcore/intrinsics.rs +++ b/src/libcore/intrinsics.rs @@ -1943,6 +1943,15 @@ extern "rust-intrinsic" { pub fn miri_start_panic(payload: *mut u8) -> !; } +#[cfg(not(bootstrap))] +#[cfg_attr(not(bootstrap), lang = "count_code_region")] +pub fn count_code_region(_index: u32) { + #[cfg_attr(not(bootstrap), allow(unused_unsafe))] // remove `unsafe` on bootstrap bump + unsafe { + abort() + } +} + // Some functions are defined here because they accidentally got made // available in this module on stable. See . // (`transmute` also falls into this category, but it cannot be wrapped due to the diff --git a/src/librustc_codegen_llvm/builder.rs b/src/librustc_codegen_llvm/builder.rs index f5ae9824df89..ba285b5ef38d 100644 --- a/src/librustc_codegen_llvm/builder.rs +++ b/src/librustc_codegen_llvm/builder.rs @@ -997,6 +997,33 @@ impl BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> { self.call_lifetime_intrinsic("llvm.lifetime.end.p0i8", ptr, size); } + fn instrprof_increment( + &mut self, + fn_name: &'ll Value, + hash: &'ll Value, + num_counters: &'ll Value, + index: &'ll Value, + ) -> &'ll Value { + debug!( + "instrprof_increment() with args ({:?}, {:?}, {:?}, {:?})", + fn_name, hash, num_counters, index + ); + + let llfn = unsafe { llvm::LLVMRustGetInstrprofIncrementIntrinsic(self.cx().llmod) }; + let args = &[fn_name, hash, num_counters, index]; + let args = self.check_call("call", llfn, args); + + unsafe { + llvm::LLVMRustBuildCall( + self.llbuilder, + llfn, + args.as_ptr() as *const &llvm::Value, + args.len() as c_uint, + None, + ) + } + } + fn call( &mut self, llfn: &'ll Value, diff --git a/src/librustc_codegen_llvm/context.rs b/src/librustc_codegen_llvm/context.rs index 4c810a37d418..7ff5ac5cbdc1 100644 --- a/src/librustc_codegen_llvm/context.rs +++ b/src/librustc_codegen_llvm/context.rs @@ -749,6 +749,8 @@ impl CodegenCx<'b, 'tcx> { ifn!("llvm.lifetime.start.p0i8", fn(t_i64, i8p) -> void); ifn!("llvm.lifetime.end.p0i8", fn(t_i64, i8p) -> void); + ifn!("llvm.instrprof.increment", fn(i8p, t_i64, t_i32, t_i32) -> void); + ifn!("llvm.expect.i1", fn(i1, i1) -> i1); ifn!("llvm.eh.typeid.for", fn(i8p) -> t_i32); ifn!("llvm.localescape", fn(...) -> void); diff --git a/src/librustc_codegen_llvm/intrinsic.rs b/src/librustc_codegen_llvm/intrinsic.rs index 1e6d2e3dbb74..7fddda99185b 100644 --- a/src/librustc_codegen_llvm/intrinsic.rs +++ b/src/librustc_codegen_llvm/intrinsic.rs @@ -7,6 +7,8 @@ use crate::type_of::LayoutLlvmExt; use crate::va_arg::emit_va_arg; use crate::value::Value; +use log::debug; + use rustc_ast::ast; use rustc_codegen_ssa::base::{compare_simd_types, to_immediate, wants_msvc_seh}; use rustc_codegen_ssa::common::span_invalid_monomorphization_error; @@ -21,6 +23,7 @@ use rustc_middle::ty::layout::{FnAbiExt, HasTyCtxt}; use rustc_middle::ty::{self, Ty}; use rustc_middle::{bug, span_bug}; use rustc_span::Span; +use rustc_span::Symbol; use rustc_target::abi::{self, HasDataLayout, LayoutOf, Primitive}; use rustc_target::spec::PanicStrategy; @@ -86,6 +89,7 @@ impl IntrinsicCallMethods<'tcx> for Builder<'a, 'll, 'tcx> { args: &[OperandRef<'tcx, &'ll Value>], llresult: &'ll Value, span: Span, + caller_instance: ty::Instance<'tcx>, ) { let tcx = self.tcx; let callee_ty = instance.monomorphic_ty(tcx); @@ -136,6 +140,23 @@ impl IntrinsicCallMethods<'tcx> for Builder<'a, 'll, 'tcx> { let llfn = self.get_intrinsic(&("llvm.debugtrap")); self.call(llfn, &[], None) } + "count_code_region" => { + if let ty::InstanceDef::Item(fn_def_id) = caller_instance.def { + let caller_fn_path = tcx.def_path_str(fn_def_id); + debug!( + "count_code_region to llvm.instrprof.increment(fn_name={})", + caller_fn_path + ); + + let (fn_name, _len_val) = self.const_str(Symbol::intern(&caller_fn_path)); + let index = args[0].immediate(); + let hash = self.const_u64(1234); + let num_counters = self.const_u32(1); + self.instrprof_increment(fn_name, hash, num_counters, index) + } else { + bug!("intrinsic count_code_region: no src.instance"); + } + } "va_start" => self.va_start(args[0].immediate()), "va_end" => self.va_end(args[0].immediate()), "va_copy" => { diff --git a/src/librustc_codegen_llvm/llvm/ffi.rs b/src/librustc_codegen_llvm/llvm/ffi.rs index 54cf99e1c6d6..372fb17573a4 100644 --- a/src/librustc_codegen_llvm/llvm/ffi.rs +++ b/src/librustc_codegen_llvm/llvm/ffi.rs @@ -1360,6 +1360,7 @@ extern "C" { // Miscellaneous instructions pub fn LLVMBuildPhi(B: &Builder<'a>, Ty: &'a Type, Name: *const c_char) -> &'a Value; + pub fn LLVMRustGetInstrprofIncrementIntrinsic(M: &Module) -> &'a Value; pub fn LLVMRustBuildCall( B: &Builder<'a>, Fn: &'a Value, diff --git a/src/librustc_codegen_ssa/back/write.rs b/src/librustc_codegen_ssa/back/write.rs index c118e5ebdb72..49054765b9da 100644 --- a/src/librustc_codegen_ssa/back/write.rs +++ b/src/librustc_codegen_ssa/back/write.rs @@ -175,6 +175,12 @@ impl ModuleConfig { if sess.opts.debugging_opts.profile && !is_compiler_builtins { passes.push("insert-gcov-profiling".to_owned()); } + + // The rustc option `-Zinstrument_coverage` injects intrinsic calls to + // `llvm.instrprof.increment()`, which requires the LLVM `instrprof` pass. + if sess.opts.debugging_opts.instrument_coverage { + passes.push("instrprof".to_owned()); + } passes }, vec![] diff --git a/src/librustc_codegen_ssa/mir/block.rs b/src/librustc_codegen_ssa/mir/block.rs index ef59ad486eef..d7db65715499 100644 --- a/src/librustc_codegen_ssa/mir/block.rs +++ b/src/librustc_codegen_ssa/mir/block.rs @@ -566,7 +566,10 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { // Handle intrinsics old codegen wants Expr's for, ourselves. let intrinsic = match def { - Some(ty::InstanceDef::Intrinsic(def_id)) => Some(bx.tcx().item_name(def_id).as_str()), + Some(ty::InstanceDef::Intrinsic(def_id)) + | Some(ty::InstanceDef::InjectedCode(def_id)) => { + Some(bx.tcx().item_name(def_id).as_str()) + } _ => None, }; let intrinsic = intrinsic.as_ref().map(|s| &s[..]); @@ -693,6 +696,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { &args, dest, terminator.source_info.span, + self.instance, ); if let ReturnDest::IndirectOperand(dst, _) = ret_dest { diff --git a/src/librustc_codegen_ssa/traits/builder.rs b/src/librustc_codegen_ssa/traits/builder.rs index caba7ebef593..7ffc9f15bffd 100644 --- a/src/librustc_codegen_ssa/traits/builder.rs +++ b/src/librustc_codegen_ssa/traits/builder.rs @@ -260,6 +260,14 @@ pub trait BuilderMethods<'a, 'tcx>: /// Called for `StorageDead` fn lifetime_end(&mut self, ptr: Self::Value, size: Size); + fn instrprof_increment( + &mut self, + fn_name: Self::Value, + hash: Self::Value, + num_counters: Self::Value, + index: Self::Value, + ) -> Self::Value; + fn call( &mut self, llfn: Self::Value, diff --git a/src/librustc_codegen_ssa/traits/intrinsic.rs b/src/librustc_codegen_ssa/traits/intrinsic.rs index 9d48e233de65..f62019498511 100644 --- a/src/librustc_codegen_ssa/traits/intrinsic.rs +++ b/src/librustc_codegen_ssa/traits/intrinsic.rs @@ -15,6 +15,7 @@ pub trait IntrinsicCallMethods<'tcx>: BackendTypes { args: &[OperandRef<'tcx, Self::Value>], llresult: Self::Value, span: Span, + caller_instance: ty::Instance<'tcx>, ); fn abort(&mut self); diff --git a/src/librustc_hir/lang_items.rs b/src/librustc_hir/lang_items.rs index 83bada404196..091ded6d74d0 100644 --- a/src/librustc_hir/lang_items.rs +++ b/src/librustc_hir/lang_items.rs @@ -242,6 +242,8 @@ language_item_table! { StartFnLangItem, "start", start_fn, Target::Fn; + CountCodeRegionFnLangItem, "count_code_region", count_code_region_fn, Target::Fn; + EhPersonalityLangItem, "eh_personality", eh_personality, Target::Fn; EhCatchTypeinfoLangItem, "eh_catch_typeinfo", eh_catch_typeinfo, Target::Static; diff --git a/src/librustc_interface/tests.rs b/src/librustc_interface/tests.rs index 87647f3b0b01..c2a7d1a4a610 100644 --- a/src/librustc_interface/tests.rs +++ b/src/librustc_interface/tests.rs @@ -548,6 +548,7 @@ fn test_debugging_options_tracking_hash() { tracked!(human_readable_cgu_names, true); tracked!(inline_in_all_cgus, Some(true)); tracked!(insert_sideeffect, true); + tracked!(instrument_coverage, true); tracked!(instrument_mcount, true); tracked!(link_only, true); tracked!(merge_functions, Some(MergeFunctions::Disabled)); diff --git a/src/librustc_middle/mir/mono.rs b/src/librustc_middle/mir/mono.rs index c889dbc0a449..b2c00849d9f8 100644 --- a/src/librustc_middle/mir/mono.rs +++ b/src/librustc_middle/mir/mono.rs @@ -352,6 +352,7 @@ impl<'tcx> CodegenUnit<'tcx> { InstanceDef::VtableShim(..) | InstanceDef::ReifyShim(..) | InstanceDef::Intrinsic(..) + | InstanceDef::InjectedCode(..) | InstanceDef::FnPtrShim(..) | InstanceDef::Virtual(..) | InstanceDef::ClosureOnceShim { .. } diff --git a/src/librustc_middle/ty/instance.rs b/src/librustc_middle/ty/instance.rs index 1ce079821a22..4f88e64c5039 100644 --- a/src/librustc_middle/ty/instance.rs +++ b/src/librustc_middle/ty/instance.rs @@ -21,6 +21,10 @@ pub enum InstanceDef<'tcx> { Item(DefId), Intrinsic(DefId), + /// Injected call to a placeholder function that is replaced with + /// For example: `core::intrinsic::count_code_region()` for code coverage. + InjectedCode(DefId), + /// `::method` where `method` receives unsizeable `self: Self`. VtableShim(DefId), @@ -149,6 +153,7 @@ impl<'tcx> InstanceDef<'tcx> { | InstanceDef::FnPtrShim(def_id, _) | InstanceDef::Virtual(def_id, _) | InstanceDef::Intrinsic(def_id) + | InstanceDef::InjectedCode(def_id) | InstanceDef::ClosureOnceShim { call_once: def_id } | InstanceDef::DropGlue(def_id, _) | InstanceDef::CloneShim(def_id, _) => def_id, @@ -236,6 +241,7 @@ impl<'tcx> fmt::Display for Instance<'tcx> { InstanceDef::VtableShim(_) => write!(f, " - shim(vtable)"), InstanceDef::ReifyShim(_) => write!(f, " - shim(reify)"), InstanceDef::Intrinsic(_) => write!(f, " - intrinsic"), + InstanceDef::InjectedCode(_) => write!(f, " - injected-code"), InstanceDef::Virtual(_, num) => write!(f, " - virtual#{}", num), InstanceDef::FnPtrShim(_, ty) => write!(f, " - shim({:?})", ty), InstanceDef::ClosureOnceShim { .. } => write!(f, " - shim"), @@ -415,6 +421,7 @@ impl<'tcx> Instance<'tcx> { | InstanceDef::FnPtrShim(..) | InstanceDef::Item(_) | InstanceDef::Intrinsic(..) + | InstanceDef::InjectedCode(..) | InstanceDef::ReifyShim(..) | InstanceDef::Virtual(..) | InstanceDef::VtableShim(..) => Some(self.substs), diff --git a/src/librustc_middle/ty/mod.rs b/src/librustc_middle/ty/mod.rs index 93ef73171993..9b1e717731e8 100644 --- a/src/librustc_middle/ty/mod.rs +++ b/src/librustc_middle/ty/mod.rs @@ -2717,6 +2717,7 @@ impl<'tcx> TyCtxt<'tcx> { ty::InstanceDef::VtableShim(..) | ty::InstanceDef::ReifyShim(..) | ty::InstanceDef::Intrinsic(..) + | ty::InstanceDef::InjectedCode(..) | ty::InstanceDef::FnPtrShim(..) | ty::InstanceDef::Virtual(..) | ty::InstanceDef::ClosureOnceShim { .. } diff --git a/src/librustc_middle/ty/structural_impls.rs b/src/librustc_middle/ty/structural_impls.rs index f6f5dfd65161..b6cbd2082a51 100644 --- a/src/librustc_middle/ty/structural_impls.rs +++ b/src/librustc_middle/ty/structural_impls.rs @@ -674,6 +674,7 @@ impl<'a, 'tcx> Lift<'tcx> for ty::InstanceDef<'a> { ty::InstanceDef::VtableShim(def_id) => Some(ty::InstanceDef::VtableShim(def_id)), ty::InstanceDef::ReifyShim(def_id) => Some(ty::InstanceDef::ReifyShim(def_id)), ty::InstanceDef::Intrinsic(def_id) => Some(ty::InstanceDef::Intrinsic(def_id)), + ty::InstanceDef::InjectedCode(def_id) => Some(ty::InstanceDef::Intrinsic(def_id)), ty::InstanceDef::FnPtrShim(def_id, ref ty) => { Some(ty::InstanceDef::FnPtrShim(def_id, tcx.lift(ty)?)) } @@ -846,6 +847,7 @@ impl<'tcx> TypeFoldable<'tcx> for ty::instance::Instance<'tcx> { VtableShim(did) => VtableShim(did.fold_with(folder)), ReifyShim(did) => ReifyShim(did.fold_with(folder)), Intrinsic(did) => Intrinsic(did.fold_with(folder)), + InjectedCode(did) => InjectedCode(did.fold_with(folder)), FnPtrShim(did, ty) => FnPtrShim(did.fold_with(folder), ty.fold_with(folder)), Virtual(did, i) => Virtual(did.fold_with(folder), i), ClosureOnceShim { call_once } => { @@ -861,9 +863,12 @@ impl<'tcx> TypeFoldable<'tcx> for ty::instance::Instance<'tcx> { use crate::ty::InstanceDef::*; self.substs.visit_with(visitor) || match self.def { - Item(did) | VtableShim(did) | ReifyShim(did) | Intrinsic(did) | Virtual(did, _) => { - did.visit_with(visitor) - } + Item(did) + | VtableShim(did) + | ReifyShim(did) + | Intrinsic(did) + | InjectedCode(did) + | Virtual(did, _) => did.visit_with(visitor), FnPtrShim(did, ty) | CloneShim(did, ty) => { did.visit_with(visitor) || ty.visit_with(visitor) } diff --git a/src/librustc_mir/interpret/terminator.rs b/src/librustc_mir/interpret/terminator.rs index cd7621ea9752..82fa471b54d7 100644 --- a/src/librustc_mir/interpret/terminator.rs +++ b/src/librustc_mir/interpret/terminator.rs @@ -257,6 +257,9 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { assert!(caller_abi == Abi::RustIntrinsic || caller_abi == Abi::PlatformIntrinsic); M::call_intrinsic(self, instance, args, ret, unwind) } + ty::InstanceDef::InjectedCode(..) => { + M::call_intrinsic(self, instance, args, ret, unwind) + } ty::InstanceDef::VtableShim(..) | ty::InstanceDef::ReifyShim(..) | ty::InstanceDef::ClosureOnceShim { .. } diff --git a/src/librustc_mir/monomorphize/collector.rs b/src/librustc_mir/monomorphize/collector.rs index 994d1e69f2e3..24c4226bb4e9 100644 --- a/src/librustc_mir/monomorphize/collector.rs +++ b/src/librustc_mir/monomorphize/collector.rs @@ -714,7 +714,9 @@ fn visit_instance_use<'tcx>( } match instance.def { - ty::InstanceDef::Virtual(..) | ty::InstanceDef::Intrinsic(_) => { + ty::InstanceDef::Virtual(..) + | ty::InstanceDef::Intrinsic(_) + | ty::InstanceDef::InjectedCode(_) => { if !is_direct_call { bug!("{:?} being reified", instance); } @@ -751,6 +753,7 @@ fn should_monomorphize_locally<'tcx>(tcx: TyCtxt<'tcx>, instance: &Instance<'tcx | ty::InstanceDef::FnPtrShim(..) | ty::InstanceDef::DropGlue(..) | ty::InstanceDef::Intrinsic(_) + | ty::InstanceDef::InjectedCode(_) | ty::InstanceDef::CloneShim(..) => return true, }; diff --git a/src/librustc_mir/monomorphize/partitioning.rs b/src/librustc_mir/monomorphize/partitioning.rs index db1ea72c0a53..7c97b9d611e1 100644 --- a/src/librustc_mir/monomorphize/partitioning.rs +++ b/src/librustc_mir/monomorphize/partitioning.rs @@ -322,6 +322,7 @@ fn mono_item_visibility( | InstanceDef::FnPtrShim(..) | InstanceDef::Virtual(..) | InstanceDef::Intrinsic(..) + | InstanceDef::InjectedCode(..) | InstanceDef::ClosureOnceShim { .. } | InstanceDef::DropGlue(..) | InstanceDef::CloneShim(..) => return Visibility::Hidden, @@ -717,6 +718,7 @@ fn characteristic_def_id_of_mono_item<'tcx>( | ty::InstanceDef::FnPtrShim(..) | ty::InstanceDef::ClosureOnceShim { .. } | ty::InstanceDef::Intrinsic(..) + | ty::InstanceDef::InjectedCode(..) | ty::InstanceDef::DropGlue(..) | ty::InstanceDef::Virtual(..) | ty::InstanceDef::CloneShim(..) => return None, diff --git a/src/librustc_mir/shim.rs b/src/librustc_mir/shim.rs index f95fd9b9e90c..b4477d9c86d4 100644 --- a/src/librustc_mir/shim.rs +++ b/src/librustc_mir/shim.rs @@ -109,6 +109,9 @@ fn make_shim<'tcx>(tcx: TyCtxt<'tcx>, instance: ty::InstanceDef<'tcx>) -> Body<' ty::InstanceDef::Intrinsic(_) => { bug!("creating shims from intrinsics ({:?}) is unsupported", instance) } + ty::InstanceDef::InjectedCode(_) => { + bug!("creating shims from injected code ({:?}) is unsupported", instance) + } }; debug!("make_shim({:?}) = untransformed {:?}", instance, result); diff --git a/src/librustc_mir/transform/instrument_coverage.rs b/src/librustc_mir/transform/instrument_coverage.rs new file mode 100644 index 000000000000..045cd03d1f7d --- /dev/null +++ b/src/librustc_mir/transform/instrument_coverage.rs @@ -0,0 +1,100 @@ +use crate::transform::{MirPass, MirSource}; +use rustc_index::vec::Idx; +use rustc_middle::mir::interpret::Scalar; +use rustc_middle::mir::*; +use rustc_middle::mir::{Local, LocalDecl}; +use rustc_middle::ty; +use rustc_middle::ty::Ty; +use rustc_middle::ty::TyCtxt; +use rustc_span::def_id::DefId; +use rustc_span::Span; + +pub struct InstrumentCoverage; + +/** + * Inserts call to count_code_region() as a placeholder to be replaced during code generation with + * the intrinsic llvm.instrprof.increment. + */ + +// FIXME(richkadel): As a first step, counters are only injected at the top of each function. +// The complete solution will inject counters at each conditional code branch. + +impl<'tcx> MirPass<'tcx> for InstrumentCoverage { + fn run_pass(&self, tcx: TyCtxt<'tcx>, src: MirSource<'tcx>, body: &mut Body<'tcx>) { + if tcx.sess.opts.debugging_opts.instrument_coverage { + if let Some(callee_fn_def_id) = tcx.lang_items().count_code_region_fn() { + debug!("instrumenting {:?}", src.def_id()); + instrument_coverage(tcx, callee_fn_def_id, body); + } + } + } +} + +pub fn instrument_coverage<'tcx>( + tcx: TyCtxt<'tcx>, + callee_fn_def_id: DefId, + body: &mut Body<'tcx>, +) { + let span = body.span.shrink_to_lo(); + + let ret_ty = tcx.fn_sig(callee_fn_def_id).output(); + let ret_ty = ret_ty.no_bound_vars().unwrap(); + let substs = tcx.mk_substs(::std::iter::once(ty::subst::GenericArg::from(ret_ty))); + + let count_code_region_fn: Operand<'_> = + Operand::function_handle(tcx, callee_fn_def_id, substs, span); + + let index = const_int_operand(tcx, span.clone(), tcx.types.u32, 0); + + let args = vec![index]; + + let source_info = SourceInfo { span: span, scope: OUTERMOST_SOURCE_SCOPE }; + + let new_block = START_BLOCK + body.basic_blocks().len(); + + let next_local = body.local_decls.len(); + let new_temp = Local::new(next_local); + let unit_temp = Place::from(new_temp); + + let storage_live = Statement { source_info, kind: StatementKind::StorageLive(new_temp) }; + let storage_dead = Statement { source_info, kind: StatementKind::StorageDead(new_temp) }; + + let count_code_region_call = TerminatorKind::Call { + func: count_code_region_fn, + args, + destination: Some((unit_temp, new_block)), + cleanup: None, + from_hir_call: false, + }; + + body.local_decls.push(LocalDecl::new(tcx.mk_unit(), body.span)); + body.basic_blocks_mut().push(BasicBlockData { + statements: vec![storage_live], + is_cleanup: false, + terminator: Some(Terminator { source_info, kind: count_code_region_call }), + }); + + body.basic_blocks_mut().swap(START_BLOCK, new_block); + body[new_block].statements.push(storage_dead); + + // FIXME(richkadel): ALSO add each computed Span for each conditional branch to the coverage map + // and provide that map to LLVM to encode in the final binary. +} + +fn const_int_operand<'tcx>( + tcx: TyCtxt<'tcx>, + span: Span, + ty: Ty<'tcx>, + val: u128, +) -> Operand<'tcx> { + let param_env_and_ty = ty::ParamEnv::empty().and(ty); + let size = tcx + .layout_of(param_env_and_ty) + .unwrap_or_else(|e| panic!("could not compute layout for {:?}: {:?}", ty, e)) + .size; + Operand::Constant(box Constant { + span, + user_ty: None, + literal: ty::Const::from_scalar(tcx, Scalar::from_uint(val, size), ty), + }) +} diff --git a/src/librustc_mir/transform/mod.rs b/src/librustc_mir/transform/mod.rs index 4240b528a612..e03ef48f7483 100644 --- a/src/librustc_mir/transform/mod.rs +++ b/src/librustc_mir/transform/mod.rs @@ -28,6 +28,7 @@ pub mod elaborate_drops; pub mod generator; pub mod inline; pub mod instcombine; +pub mod instrument_coverage; pub mod no_landing_pads; pub mod nrvo; pub mod promote_consts; @@ -287,6 +288,8 @@ fn mir_validated( &[&[ // What we need to run borrowck etc. &promote_pass, + // FIXME(richkadel): is this the best place for the InstrumentCoverage pass? + &instrument_coverage::InstrumentCoverage, &simplify::SimplifyCfg::new("qualify-consts"), ]], ); diff --git a/src/librustc_session/options.rs b/src/librustc_session/options.rs index d22c6ec9d7d0..599ce595e131 100644 --- a/src/librustc_session/options.rs +++ b/src/librustc_session/options.rs @@ -876,6 +876,9 @@ options! {DebuggingOptions, DebuggingSetter, basic_debugging_options, "fix undefined behavior when a thread doesn't eventually make progress \ (such as entering an empty infinite loop) by inserting llvm.sideeffect \ (default: no)"), + instrument_coverage: bool = (false, parse_bool, [TRACKED], + "instrument the generated code with LLVM code region counters for \ + generating coverage reports (default: no)"), instrument_mcount: bool = (false, parse_bool, [TRACKED], "insert function instrument code for mcount-based tracing (default: no)"), keep_hygiene_data: bool = (false, parse_bool, [UNTRACKED], diff --git a/src/librustc_span/symbol.rs b/src/librustc_span/symbol.rs index fdeb58b7b7a3..623c27973473 100644 --- a/src/librustc_span/symbol.rs +++ b/src/librustc_span/symbol.rs @@ -240,6 +240,7 @@ symbols! { copy_closures, core, core_intrinsics, + count_code_region, crate_id, crate_in_paths, crate_local, diff --git a/src/librustc_ty/instance.rs b/src/librustc_ty/instance.rs index 0acf76916813..d4ceeff32445 100644 --- a/src/librustc_ty/instance.rs +++ b/src/librustc_ty/instance.rs @@ -35,6 +35,10 @@ fn resolve_instance<'tcx>( debug!(" => intrinsic"); ty::InstanceDef::Intrinsic(def_id) } + ty::FnDef(def_id, _) if Some(def_id) == tcx.lang_items().count_code_region_fn() => { + debug!(" => injected placeholder function to be replaced"); + ty::InstanceDef::InjectedCode(def_id) + } ty::FnDef(def_id, substs) if Some(def_id) == tcx.lang_items().drop_in_place_fn() => { let ty = substs.type_at(0); diff --git a/src/rustllvm/RustWrapper.cpp b/src/rustllvm/RustWrapper.cpp index 4704622922af..cdb3a157eab9 100644 --- a/src/rustllvm/RustWrapper.cpp +++ b/src/rustllvm/RustWrapper.cpp @@ -5,6 +5,7 @@ #include "llvm/IR/DiagnosticPrinter.h" #include "llvm/IR/GlobalVariable.h" #include "llvm/IR/Instructions.h" +#include "llvm/IR/Intrinsics.h" #include "llvm/Object/Archive.h" #include "llvm/Object/ObjectFile.h" #include "llvm/Bitcode/BitcodeWriterPass.h" @@ -1364,6 +1365,11 @@ extern "C" LLVMValueRef LLVMRustBuildCall(LLVMBuilderRef B, LLVMValueRef Fn, unwrap(Fn), makeArrayRef(unwrap(Args), NumArgs), Bundles)); } +extern "C" LLVMValueRef LLVMRustGetInstrprofIncrementIntrinsic(LLVMModuleRef M) { + return wrap(llvm::Intrinsic::getDeclaration(unwrap(M), + (llvm::Intrinsic::ID)llvm::Intrinsic::instrprof_increment)); +} + extern "C" LLVMValueRef LLVMRustBuildMemCpy(LLVMBuilderRef B, LLVMValueRef Dst, unsigned DstAlign, LLVMValueRef Src, unsigned SrcAlign, diff --git a/src/test/codegen/coverage-experiments/Cargo.lock b/src/test/codegen/coverage-experiments/Cargo.lock new file mode 100644 index 000000000000..132469cbb182 --- /dev/null +++ b/src/test/codegen/coverage-experiments/Cargo.lock @@ -0,0 +1,5 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +[[package]] +name = "coverage_experiments" +version = "0.1.0" diff --git a/src/test/codegen/coverage-experiments/Cargo.toml b/src/test/codegen/coverage-experiments/Cargo.toml new file mode 100644 index 000000000000..296a8d5c9af2 --- /dev/null +++ b/src/test/codegen/coverage-experiments/Cargo.toml @@ -0,0 +1,103 @@ +[workspace] + +[package] +name = "coverage_experiments" +version = "0.1.0" +license = "BSD-3-Clause" +authors = ["rust-fuchsia@fuchsia.com"] +edition = "2018" + +[[bin]] + +name = "coverage_injection_test" +path = "src/coverage_injection_test.rs" + +[[bin]] + +name = "coverage_injection_test2" +path = "src/coverage_injection_test2.rs" + +[[bin]] + +name = "while" +path = "src/while.rs" + +[[bin]] + +name = "while_clean" +path = "src/while_clean.rs" + +[[bin]] + +name = "while_early_return" +path = "src/while_early_return.rs" + +[[bin]] + +name = "if_with_comments" +path = "src/if_with_comments.rs" + +[[bin]] + +name = "if" +path = "src/if.rs" + +[[bin]] + +name = "increment_intrinsic" +path = "src/increment_intrinsic.rs" + +[[bin]] + +name = "just_main" +path = "src/just_main.rs" + +[[bin]] + +name = "lazy_boolean" +path = "src/lazy_boolean.rs" + +[[bin]] + +name = "match" +path = "src/match.rs" + +[[bin]] + +name = "match_without_increment" +path = "src/match_without_increment.rs" # identical to -Zunpretty=hir output + +[[bin]] + +name = "match_with_increment" +path = "src/match_with_increment.rs" + +[[bin]] + +name = "match_with_increment_alt" +path = "src/match_with_increment_alt.rs" + +[[bin]] + +name = "loop_break_value" +path = "src/loop_break_value.rs" + +[[bin]] + +name = "for_with_comments" +path = "src/for_with_comments.rs" + +[[bin]] + +name = "for" +path = "src/for.rs" + +[[bin]] + +name = "drop_trait" +path = "src/drop_trait.rs" + +#[dependencies] # Should not need to manually add coverage dependencies +#version = "0.1.0" +#path = "../__builtin" # for mod __builtin::coverage + diff --git a/src/test/codegen/coverage-experiments/README-THIS-IS-TEMPORARY.md b/src/test/codegen/coverage-experiments/README-THIS-IS-TEMPORARY.md new file mode 100644 index 000000000000..3b69c0a40659 --- /dev/null +++ b/src/test/codegen/coverage-experiments/README-THIS-IS-TEMPORARY.md @@ -0,0 +1,157 @@ +# codegen/coverage-experiments +*

THIS DIRECTORY IS TEMPORARY

* + +This directory contains some work-in-progress (WIP) code used for experimental development and +testing of Rust Coverage feature development. + +The code in this directory will be removed, or migrated into product tests, when the Rust +Coverage feature is complete. + +[TOC] + +## Development Notes + +### config.toml + +config.toml probably requires (I should verify that intrinsic `llvm.instrprof.increment` +code generation ONLY works with this config option): + + profiler = true + +## First build + +```shell +./x.py clean +./x.py build -i --stage 1 src/libstd +``` + +## Incremental builds *IF POSSIBLE!* + +```shell +./x.py build -i --stage 1 src/libstd --keep-stage 1 +``` + +*Note: Some changes made for Rust Coverage required the full build (without `--keep-stage 1`), and in some cases, required `./x.py clean` first!. Occassionally I would get errors when building or when compiling a test program with `--Zinstrument-coverage` that work correctly only after a full clean and build.* + +## Compile a test program with LLVM coverage instrumentation + +*Note: This PR is still a work in progress. At the time of this writing, the `llvm.instrprof.increment` intrinsic is injected, and recognized by the LLVM code generation stage, but it does not appear to be included in the final binary. This is not surprising since other steps are still to be implemented, such as generating the coverage map. See the suggested additional `llvm` flags for ways to verify the `llvm` passes at least get the right intrinsic.* + +Suggested debug configuration to confirm Rust coverage features: +```shell +$ export RUSTC_LOG=rustc_codegen_llvm::intrinsic,rustc_mir::transform::instrument_coverage=debug +``` + +Ensure the new compiled `rustc` is used (the path below, relative to the `rust` code repository root, is an example only): + +```shell +$ build/x86_64-unknown-linux-gnu/stage1/bin/rustc \ + src/test/codegen/coverage-experiments/just_main.rs \ + -Zinstrument-coverage +``` + +### About the test programs in coverage-experiments/src/ + +The `coverage-experiments/src/` directory contains some sample (and very simple) Rust programs used to analyze Rust compiler output at various stages, with or without the Rust code coverage compiler option. For now, these are only used for the in-progress development and will be removed at a future date. (These are *not* formal test programs.) + +The src director may also contain some snapshots of mir output from experimentation, particularly if the saved snapshots highlight results that are important to the future development, individually or when compared with other output files. + +Be aware that some of the files and/or comments may be outdated. + +### Additional `llvm` flags (append to the `rustc` command) + +These optional flags generate additional files and/or terminal output. LLVM's `-print-before=all` should show the `instrprof.increment` intrinsic with arguments computed by the experimental Rust coverage feature code: + +```shell + --emit llvm-ir \ + -Zverify-llvm-ir \ + -Zprint-llvm-passes \ + -Csave-temps \ + -Cllvm-args=-print-before-all +``` + +### Additional flags for MIR analysis and transforms + +These optional flags generate a directory with many files representing the MIR as text (`.mir` files) and as a visual graph (`.dot` files) rendered by `graphviz`. (**Some IDEs, such as `VSCode` have `graphviz` extensions.**) + +```shell + -Zdump-mir=main \ + -Zdump-mir-graphviz +``` + +### Flags I've used but appear to be irrelvant to `-Zinstrument-coverage` after all: +```shell + # -Zprofile + # -Ccodegen-units=1 + # -Cinline-threshold=0 + # -Clink-dead-code + # -Coverflow-checks=off +``` + +## Run the test program compiled with code coverage instrumentation (maybe): + +As stated above, at the time of this writing, this work-in-progress seems to generate `llvm.instrprof.increment` intrinsic calls correctly, and are visibile in early `llvm` code generation passes, but are eventually stripped. + +The test program should run as expected, currently does not generate any coverage output. + +*Example:* + +```shell + $ src/test/codegen/coverage-experiments/just_main + hello world! (should be covered) +``` + +### Running the coverage-enabled `rustc` compiler in the `lldb` debugger: + +For example, to verify the intrinsic is codegen'ed, set breakpoint in `lldb` where it validates a certain instruction is the `llvm.instrprof.increment` instruction. + +First, update config.toml for debugging: + +```toml + [llvm] + optimize = false + release-debuginfo = true + + [rust] + debug = true + debuginfo-level = 2 +``` + +*(Note, in case this is relevant after all, I also have the following changes; but I don't think I need them:)* + +```toml + # Add and uncomment these if relevant/useful: + # codegen-units = 0 + # python = '/usr/bin/python3.6' +``` + +Run the compiler with additional flags as needed: + +```shell +lldb \ + build/x86_64-unknown-linux-gnu/stage1/bin/rustc \ + -- \ + src/test/codegen/coverage-experiments/just_main.rs \ + -Zinstrument-coverage \ + -Zdump-mir=main \ + -Zdump-mir-graphviz +``` + +Note the specific line numbers may be different: + +```c++ +(lldb) b lib/Transforms/Instrumentation/InstrProfiling.cpp:418 +(lldb) r + +Process 93855 stopped +* thread #6, name = 'rustc', stop reason = breakpoint 2.1 + frame #0: 0x00007fffedff7738 librustc_driver-5a0990d8d18fb2b4.so`llvm::InstrProfiling::lowerIntrinsics(this=0x00007fffcc001d40, F=0x00007fffe4552198) at InstrProfiling.cpp:418:23 + 415 auto Instr = I++; + 416 InstrProfIncrementInst *Inc = castToIncrementInst(&*Instr); + 417 if (Inc) { +-> 418 lowerIncrement(Inc); + 419 MadeChange = true; + 420 } else if (auto *Ind = dyn_cast(Instr)) { + 421 lowerValueProfileInst(Ind); +(lldb) +``` \ No newline at end of file diff --git a/src/test/codegen/coverage-experiments/src/coverage_injection_test.rs b/src/test/codegen/coverage-experiments/src/coverage_injection_test.rs new file mode 100644 index 000000000000..231da1dc1a67 --- /dev/null +++ b/src/test/codegen/coverage-experiments/src/coverage_injection_test.rs @@ -0,0 +1,335 @@ +/* */ use std::io::Error; +/* */ use std::io::ErrorKind; +/* */ +/* */ /// Align Rust counter increment with with: +/* */ /// [‘llvm.instrprof.increment’ Intrinsic](https://llvm.org/docs/LangRef.html#llvm-instrprof-increment-intrinsic) +/* */ /// +/* */ /// declare void @llvm.instrprof.increment(i8* , i64 , i32 , i32 ) +/* */ /// +/* */ /// The first argument is a pointer to a global variable containing the name of the entity +/* */ /// being instrumented. This should generally be the (mangled) function name for a set of +/* */ /// counters. +/* */ /// +/* */ /// The second argument is a hash value that can be used by the consumer of the profile data +/* */ /// to detect changes to the instrumented source, and the third is the number of counters +/* */ /// associated with name. It is an error if hash or num-counters differ between two +/* */ /// instances of instrprof.increment that refer to the same name. +/* */ /// +/* */ /// The last argument refers to which of the counters for name should be incremented. It +/* */ /// should be a value between 0 and num-counters. +/* */ /// +/* */ /// # Arguments +/* */ /// +/* */ /// `mangled_fn_name` - &'static ref to computed and injected static str, using: +/* */ /// +/* */ /// ``` +/* */ /// fn rustc_symbol_mangling::compute_symbol_name( +/* */ /// tcx: TyCtxt<'tcx>, +/* */ /// instance: Instance<'tcx>, +/* */ /// compute_instantiating_crate: impl FnOnce() -> CrateNum, +/* */ /// ) -> String +/* */ /// ``` +/* */ /// +/* */ /// `source_version_hash` - Compute hash based that only changes if there are "significant" +/* */ /// to control-flow inside the function. +/* */ /// +/* */ /// `num_counters` - The total number of counter calls [MAX(counter_index) + 1] within the +/* */ /// function. +/* */ /// +/* */ /// `counter_index` - zero-based counter index scoped by the function. (Ordering of +/* */ /// counters, relative to the source code location, is apparently not expected.) +/* */ /// +/* */ /// # Notes +/* */ /// +/* */ /// * The mangled_fn_name may not be computable until generics are monomorphized (see +/* */ /// parameters required by rustc_symbol_mangling::compute_symbol_name). +/* */ /// * The version hash may be computable from AST analysis, and may not benefit from further +/* */ /// lowering. +/* */ /// * num_counters depends on having already identified all counter insertion locations. +/* */ /// * counter_index can be computed at time of counter insertion (incrementally). +/* */ /// * Numeric parameters are signed to match the llvm increment intrinsic parameter types. +/* */ fn __lower_incr_cov(_mangled_fn_name: &'static str, _fn_version_hash: i64, _num_counters: i32, _counter_index: i32) { +/* */ } +/* */ +/* */ /// A coverage counter implementation that will work as both an intermediate coverage +/* */ /// counting and reporting implementation at the AST-level only--for debugging and +/* */ /// development--but also serves as a "marker" to be replaced by calls to LLVM +/* */ /// intrinsic coverage counter APIs during the lowering process. +/* */ /// +/* */ /// Calls to this function will be injected automatically into the AST. When LLVM intrinsics +/* */ /// are enabled, the counter function calls that were injected into the AST serve as +/* */ /// placeholders, to be replaced by an alternative, such as: +/* */ /// +/* */ /// * direct invocation of the `llvm.instrprof.increment()` intrinsic; or +/* */ /// * the `__lower_incr_cov()` function, defined above, that would invoke the +/* */ /// `llvm.instrprof.increment()` intrinsic; or +/* */ /// * a similar expression wrapper, with the additional parameters (as defined above +/* */ /// for `__lower_incr_cov()`, that invokes `llvm.instrprof.increment()` and returns the +/* */ /// result of the wrapped expression) +/* */ /// +/* */ /// The first two options would require replacing the inlined wrapper call with something +/* */ /// like: +/* */ /// +/* */ /// ``` +/* */ /// { let result = {expr}; __inlined_incr_cov(context, counter); result } +/* */ /// ``` +/* */ /// +/* */ /// But if the lowering process is already unwrapping the inlined call to `__incr_cov()`, then +/* */ /// it may be a perfect opportunity to replace the function with one of these more +/* */ /// direct methods. +/* */ /// +/* */ #[inline(always)] +/* */ pub fn __incr_cov(region_loc: &str, /*index: u32,*/ result: T) -> T { +/* */ // Either call the intermediate non-llvm coverage counter API or +/* */ // replace the call to this function with the expanded `__lower_incr_cov()` call. +/* */ +/* */ // let _lock = increment_counter(counter); +/* */ println!("{}", region_loc); +/* */ +/* */ result +/* */ } +/* */ +/* */ /// Write a report identifying each incremented counter and the number of times each counter +/* */ /// was incremented. +/* */ fn __report() { +/* */ println!("WRITE REPORT!"); +/* */ } +/* */ +/* */ /// Increment the counter after evaluating the wrapped expression (see `__incr_cov()`), then +/* */ /// write a report identifying each incremented counter and the number of times each counter +/* */ /// was incremented. +/* */ #[inline(always)] +/* */ pub fn __incr_cov_and_report(region_loc: &str, /*counter: u32,*/ result: T) -> T { +/* */ __incr_cov(region_loc, /*counter,*/ ()); +/* */ __report(); +/* */ result +/* */ } +/* */ +/* */ macro_rules! from { +/* */ ($from:expr) => { &format!("from: {}\n to: {}:{}:{}", $from, file!(), line!(), column!()) }; +/* */ } +/* */ +/* */ #[derive(Debug)] +/* */ enum TestEnum { +/* */ Red, +/* */ Green, +/* */ Blue, +/* */ } +/* */ +/* */ struct TestStruct { +/* */ field: i32, +/* */ } +/* */ +/* */ // IMPORTANT! IS WRAPPING main() ENOUGH? OR DO I ALSO NEED TO WRAP THREAD FUNCTIONS, ASSUMING +/* */ // THEY ARE STILL RUNNING WITH MAIN EXITS? (IF THEY CAN). NOT SURE HOW RUST HANDLES THAT. +/* */ +/* */ // I SUSPECT USING THREAD_LOCAL COUNTERS MAY NOT ACTUALLY BE AN OPTIMIZATION OVER MUTEX LOCKS, +/* */ // BUT MAYBE I SHOULD ASK. +/* */ +/* */ impl TestStruct { +/* - */ fn new() -> Self { +/* ┃ */ __incr_cov(from!("fn new()"),Self::new_with_value(31415)) // function-scoped counter index = 0 +/* - */ } +/* */ +/* - */ fn new_with_value(field: i32) -> Self { +/* ┃ */ __incr_cov(from!("fn new_with_value()"),Self { +/* ┃ */ field, +/* ┃ */ }) // function-scoped counter index = 0 +/* - */ } +/* */ +/* */ fn call_closure(&self, closure: F) -> bool +/* */ where +/* */ F: FnOnce( +/* */ i32, +/* */ ) -> bool, +/* - */ { +/* ┃ */ __incr_cov(from!("fn call_closure()"),closure(123)) // function-scoped counter index = 0 +/* - */ } +/* */ +/* - */ fn various(&self) -> Result<(),Error> { +/* ┃ */ use TestEnum::*; +/* ┃ */ let mut color = Red; +/* ┃ */ let _ = color; +/* ┃ */ color = Blue; +/* ┃ */ let _ = color; +/* ┃ */ color = Green; +/* ┃ */ match __incr_cov(from!("fn various"),color) { // function-scoped counter index = 0 +/* : */ +/* : */ // !!! RECORD SPAN FROM START OF INNERMOST CONTAINING BLOCK (THE FUNCTION IN THIS CASE) TO END OF MATCH EXPRESSION +/* : */ // If `match`, `while`, `loop`, `for`, `if`, etc. expression has a `return`, `break`, or `continue` +/* : */ // (if legal), then RECORD SPAN FROM START OF INNERMOST CONTAINING BLOCK TO END OF `return` EXPRESSION +/* : */ // If the expression includes lazy booleans, nest calls to `__incr_cov()`. +/* : I */ Red => __incr_cov(from!("Red => or end of MatchArmGuard expression inside pattern, if any"),println!("roses")), +/* : - */ Green => { +/* : ┃ */ let spidey = 100; +/* : ┃ */ let goblin = 50; +/* : ┃ */ // if spidey > goblin {__incr_cov(from!(""),{ +/* : ┃ */ // println!("what ev"); +/* : ┃ */ // })} +/* : ┃ */ // ACTUALLY, WRAPPING THE ENTIRE IF BLOCK IN `__incr_cov` IS NOT A GREAT GENERAL RULE. +/* : ┃ */ // JUST INSERTING A `return`, `break`, or `continue` IN THAT BLOCK (without an intermediate condition) +/* : ┃ */ // MAKES THE `__incr_cov()` CALL UNREACHABLE! +/* : ┃ */ // MY ORIGINAL SOLUTION WORKS BETTER (WRAP LAST EXPRESSION OR AFTER LAST SEMICOLON STATEMENT IN BLOCK) +/* : ┃ */ // UNLESS THE EXPRESSION IS NOT A BLOCK. +/* : ┃ - */ if __incr_cov(from!("Green => or end of MatchArmGuard expression inside pattern, if any"),spidey > goblin) { +/* : : ┃ */ println!("spidey beats goblin"); +/* : : ┃ */ __incr_cov(from!("block start"),()); +/* : ┃ - */ } else if __incr_cov(from!("`else if` on this line"),spidey == goblin) { +/* : : ┃ */ // COVERAGE NOTE: Do we mark only the expression span (that may be trivial, as in this case), +/* : : ┃ */ // or associate it with the outer block, similar to how the `if` expression is associated with +/* : : ┃ */ // the outer block? (Although it is a continuation, in a sense, it is discontiguous in this case, +/* : : ┃ */ // so I think simpler to just make it its own coverage region.) +/* : : ┃ */ println!("it's a draw"); +/* : : ┃ */ __incr_cov(from!("block start"),()); +/* : ┃ - - - */ } else if if __incr_cov(from!("`else if` on this line"),true) { +/* : : : ┃ */ // return __incr_cov(from!("after `if true`"),Ok(())); +/* : : : ┃ */ // ACTUALLY, BECAUSE OF `return`, WE DO NOT RECORD THE `if true` EVEN THOUGH WE COVERED IT. +/* : : : ┃ */ // IN FACT, IF THIS NESTED CONDITIONAL IN A CONDITIONAL EXPRESSION WAS AN `if` (WITHOUT PRECEDING ELSE) +/* : : : ┃ */ // WE WOULD NOT HAVE RECORDED THE COVERAGE OF STATEMENTS LEADING UP TO THE `if`, SO +/* : : : ┃ */ // IT SHOULD BE: +/* ┏-:---:-------:---< */ return __incr_cov(from!(""),Ok(())); +/* V : : : : */ // NOTE THE `from` STRING IS SAME FOR THE `else if`s `__incr_cov` AND THIS `return`. +/* : : : : */ // ONLY ONE OF THESE WILL EXECUTE, TO RECORD COVERAGE FROM THAT SPOT. +/* : : ┃ - */ } else { +/* : : : I */ __incr_cov(from!("`else`"),false) +/* : : - - */ } { +/* : : ┃ */ println!("wierd science"); +/* : : ┃ */ __incr_cov(from!("block start"),()); +/* : ┃ - */ } else { +/* : : ┃ */ println!("goblin wins"); +/* ┏-:---:---< */ return __incr_cov(from!("`else`"),Ok(())); // THIS COUNTS LAST STATEMENT IN `else` BLOCK +/* V : : : */ // COVERAGE NOTE: When counting the span for `return`, +/* : : : */ // `break`, or `continue`, also report the outer spans +/* : : : */ // got this far--including this `else` block. Record +/* : : : */ // The start positions for those outer blocks, but: +/* : : : */ // * For the block containing the `return`, `break`, or +/* : : : */ // `continue`, end report the end position is the +/* : : : */ // start of the `return` span (or 1 char before it). +/* : : : */ // * Anything else? +/* : ┃ - */ } +/* : ┃ - */ // __incr_cov(from!(""),()); // DO NOT COUNT HERE IF NO STATEMENTS AFTER LAST `if` or `match` +/* : - */ }, +/* : I */ Blue => __incr_cov(from!("Blue => or end of MatchArmGuard expression inside pattern, if any"),println!("violets")), +/* ┃ */ } +/* ┃ */ +/* ┃ */ let condition1 = true; +/* ┃ */ let condition2 = false; +/* ┃ */ let condition3 = true; +/* ┃ */ +/* ┃ */ println!("Called `various()` for TestStruct with field={}", self.field); +/* ┃ */ +/* ┃ - */ if __incr_cov(from!("after block end of prior `match` (or `if-else if-else`)"),condition1) { +/* : ┃ */ println!("before while loop"); +/* : ┃ */ let mut countdown = 10; +/* : ┃ */ __incr_cov(from!("block start"),()); // Must increment before repeated while text expression +/* : : I */ while __incr_cov(from!("while test"), countdown > 0) { // span is just the while test expression +/* : : ┃ */ println!("top of `while` loop"); +/* : : ┃ */ countdown -= 1; +/* : : ┃ */ // __incr_cov(from!("while loop"),()); // Counter not needed, but span is computed as "while test" minus "block start" +/* : : ┃ */ // If test expression is 11, and the outer block runs only once, 11-1 = 10 +/* : ┃ - */ } +/* : ┃ */ println!("before for loop"); +/* : ┃ - */ for index in __incr_cov(from!("end of while"),0..10) { +/* : : ┃ */ println!("top of `for` loop"); +/* : : ┃ - */ if __incr_cov(from!("block start"),index == 8) { +/* : : : ┃ */ println!("before break"); +/* : : : ┃ */ // note the following is not legal here: +/* : : : ┃ */ // "can only break with a value inside `loop` or breakable block" +/* : : : ┃ */ // break __incr_cov(from!(""),()); +/* : : : ┃ */ __incr_cov(from!("block start"),()); +/* : : ┏-----< */ break; +/* : : V : : */ +/* : : : : */ // FIXME(richkadel): add examples with loop labels, breaking out of inner and outer loop to outer loop label, with expression. +/* : : : : */ // May want to record both the span and the start position after the broken out block depdnding on label +/* : : ┃ - */ } +/* : : ┃ */ println!("after `break` test"); +/* : : ┃ - */ if __incr_cov(from!("block end of `if index == 8`"),condition2) { +/* ┏-:---:---:---< */ return __incr_cov(from!("block start"),Ok(())); +/* V : : ┃ - */ } +/* : : ┃ */ +/* : : ┃ */ // BECAUSE THE PREVIOUS COVERAGE REGION HAS A `return`, THEN +/* : : ┃ */ // IF PREVIOUS COVERAGE REGION IS NOT COUNTED THEN OUTER REGION REACHED HERE. +/* : : ┃ */ // ADD A COVERAGE REGION FOR THE SPAN FROM JUST AFTER PREVIOUS REGION TO END +/* : : ┃ */ // OF OUTER SPAN, THEN TRUNCATE TO NEXT REGION NOT REACHED. +/* : : ┃ - */ if index % 3 == 2 { // NO __incr_cov() HERE BECAUSE NO STATEMENTS BETWEEN LAST CONDITIONAL BLOCK AND START OF THIS ONE +/* : : Λ : ┃ */ __incr_cov(from!("block end of `if condition2`"),()); +/* : : ┗-----< */ continue; +/* : : ┃ - */ } +/* : : ┃ */ println!("after `continue` test"); +/* : : ┃ */ // maybe add a runtime flag for a possible `return` here? +/* : : ┃ */ __incr_cov(from!("for loop"),()); +/* : ┃ - */ } +/* : ┃ */ println!("after for loop"); +/* : ┃ */ let result = if { // START OF NEW CONDITIONAL EXPRESSION. NEXT "GUARANTEED" COUNTER SHOULD COUNT FROM END OF LAST CONDITIONAL EXPRESSION +/* : ┃ */ // A "GUARANTEED" COUNTER CALL IS ONE THAT WILL BE CALLED REGARDLESS OF OTHER CONDITIONS. THIS INCLUDES: +/* : ┃ */ // * A CONDITIONAL EXPRESSION THAT IS NOT A BLOCK (OR ANOTHER CONDITIONAL STATEMENT, WHICH WOULD CONTAIN A BLOCK) +/* : ┃ */ // * OR IF THE NEXT CONDITIONAL EXPRESSION IS A BLOCK OR CONDITIONAL STATEMENT, THEN THE FIRST "GUARANTEED" COUNTER IN THAT BLOCK +/* : ┃ */ // * END OF BLOCK IF THE BLOCK DOES NOT HAVE INNER CONDITIONAL EXPRESSIONS +/* : ┃ */ // * BRANCHING STATEMENTS (`return`, `break`, `continue`) BY EITHER WRAPPING THE BRANCH STATEMENT NON-BLOCK EXPRESSION, +/* : ┃ */ // OR PREPENDING A COUNTER WITH EMPTY TUPLE IF NO EXPRESSION, OR IF EXPRESSION IS A BLOCK, THEN THE NEXT "GUARANTEED" +/* : ┃ */ // COUNTER CALL WITHIN THAT BLOCK. +/* : ┃ */ // BASICALLY, CARRY THE START OF COVERAGE SPAN FORWARD UNTIL THE GUARANTEED COUNTER IS FOUND +/* : ┃ */ println!("after result = if ..."); +/* : ┃ - */ if __incr_cov(from!("block end of `for` loop"),condition2) { +/* : : ┃ */ println!("before first return"); +/* ┏-:---:-------< */ return __incr_cov(from!("block start"),Ok(())); +/* V : : - */ } else if __incr_cov(from!("`else`"),condition3) { +/* : : ┃ */ // THE ABOVE COUNTER IS _NOT_ REALLY NECESSARY IF EXPRESSION IS GUARANTEED TO EXECUTE. +/* : : ┃ */ // IF WE GET COUNTER IN `else if` BLOCK WE COVERED EXPRESSION. +/* : : ┃ */ // IF WE GET TO ANY REMAINING `else` or `else if` BLOCK WE KNOW WE EVALUATED THIS CONDITION +/* : : ┃ */ // AND ALL OTHERS UP TO THE EXECUTED BLOCK. BUT THE SPAN WOULD HAVE "HOLES" FOR UNEXECUTED BLOCKS. +/* : : ┃ */ println!("not second return"); +/* ┏-:---:-------< */ return __incr_cov(from!("block start"),Ok(())); +/* V : : - */ } else { +/* : : ┃ */ println!("not returning"); +/* : : ┃ */ __incr_cov(from!("block start"),false) +/* : : - */ } +/* : ┃ */ // NO COUNTER HERE BECAUSE NO STATEMENTS AFTER CONDITIONAL BLOCK +/* : ┃ - */ } { +/* : : ┃ */ println!("branched condition returned true"); +/* : : ┃ */ __incr_cov(from!(""),Ok(())) +/* : ┃ - */ } else if self.call_closure( +/* : : - */ |closure_param| __incr_cov(from!(""), +/* : : ┃ - */ if condition3 { +/* : : : ┃ */ println!("in closure, captured condition said to print the param {}", closure_param); +/* : : : ┃ */ __incr_cov(from!(""),false) +/* : : ┃ - */ } else { +/* : : : ┃ */ println!("in closure, captured condition was false"); +/* : : : ┃ */ __incr_cov(from!(""),true) +/* : : ┃ - */ } +/* : : - */ ) +/* : : - */ ) { +/* : : ┃ */ println!("closure returned true"); +/* : : ┃ */ __incr_cov(from!(""),Err(Error::new(ErrorKind::Other, "Result is error if closure returned true"))) +/* : ┃ - */ } else { +/* : : ┃ */ println!("closure returned false"); +/* : : ┃ */ __incr_cov(from!(""),Err(Error::new(ErrorKind::Other, "Result is error if closure returned false"))) +/* : ┃ - */ }; +/* : ┃ */ println!("bottom of function might be skipped if early `return`"); +/* : ┃ */ __incr_cov(from!("if condition1"),result) +/* ┃ - */ } else { +/* : ┃ */ println!("skipping everything in `various()`"); +/* : ┃ */ __incr_cov(from!(""),Ok(())) +/* ┃ - */ } +/* ┃ - */ // __incr_cov(from!(""),0) // DO NOT COUNT IF NO STATEMENTS AFTER CONDITIONAL BLOCK. ALL COVERAGE IS ALREADY COUNTED +/* - */ } +/* */ } +/* */ +/* - */ fn main() -> Result<(), std::io::Error> { +/* ┃ */ //let mut status: u8 = 2; +/* ┃ */ let mut status: u8 = 1; +/* : - */ let result = if status < 2 && +/* : ┃ */ __incr_cov(from!(""),{ +/* : ┃ */ status -= 1; +/* : ┃ */ status == 0 +/* : - - */ }) { +/* : ┃ */ let test_struct = TestStruct::new_with_value(100); +/* : ┃ */ let _ = test_struct.various(); +/* ┏-:---< */ return __incr_cov_and_report(from!(""),Err(Error::new(ErrorKind::Other, format!("Error status {}", status)))) +/* V : - */ } else { +/* : ┃ */ let test_struct = TestStruct::new(); +/* : ┃ */ __incr_cov(from!(""),test_struct.various()) +/* : - */ }; +/* ┃ */ println!("done"); +/* ┃ */ __incr_cov_and_report(from!(""),result) // function-scoped counter index = 0 +/* - */ } \ No newline at end of file diff --git a/src/test/codegen/coverage-experiments/src/coverage_injection_test2.rs b/src/test/codegen/coverage-experiments/src/coverage_injection_test2.rs new file mode 100644 index 000000000000..8f4399ab51d0 --- /dev/null +++ b/src/test/codegen/coverage-experiments/src/coverage_injection_test2.rs @@ -0,0 +1,320 @@ +/* */ use std::io::Error; +/* */ use std::io::ErrorKind; +/* */ +/* */ /// Align Rust counter increment with with: +/* */ /// [‘llvm.instrprof.increment’ Intrinsic](https://llvm.org/docs/LangRef.html#llvm-instrprof-increment-intrinsic) +/* */ /// +/* */ /// declare void @llvm.instrprof.increment(i8* , i64 , i32 , i32 ) +/* */ /// +/* */ /// The first argument is a pointer to a global variable containing the name of the entity +/* */ /// being instrumented. This should generally be the (mangled) function name for a set of +/* */ /// counters. +/* */ /// +/* */ /// The second argument is a hash value that can be used by the consumer of the profile data +/* */ /// to detect changes to the instrumented source, and the third is the number of counters +/* */ /// associated with name. It is an error if hash or num-counters differ between two +/* */ /// instances of instrprof.increment that refer to the same name. +/* */ /// +/* */ /// The last argument refers to which of the counters for name should be incremented. It +/* */ /// should be a value between 0 and num-counters. +/* */ /// +/* */ /// # Arguments +/* */ /// +/* */ /// `mangled_fn_name` - &'static ref to computed and injected static str, using: +/* */ /// +/* */ /// ``` +/* */ /// fn rustc_symbol_mangling::compute_symbol_name( +/* */ /// tcx: TyCtxt<'tcx>, +/* */ /// instance: Instance<'tcx>, +/* */ /// compute_instantiating_crate: impl FnOnce() -> CrateNum, +/* */ /// ) -> String +/* */ /// ``` +/* */ /// +/* */ /// `source_version_hash` - Compute hash based that only changes if there are "significant" +/* */ /// to control-flow inside the function. +/* */ /// +/* */ /// `num_counters` - The total number of counter calls [MAX(counter_index) + 1] within the +/* */ /// function. +/* */ /// +/* */ /// `counter_index` - zero-based counter index scoped by the function. (Ordering of +/* */ /// counters, relative to the source code location, is apparently not expected.) +/* */ /// +/* */ /// # Notes +/* */ /// +/* */ /// * The mangled_fn_name may not be computable until generics are monomorphized (see +/* */ /// parameters required by rustc_symbol_mangling::compute_symbol_name). +/* */ /// * The version hash may be computable from AST analysis, and may not benefit from further +/* */ /// lowering. +/* */ /// * num_counters depends on having already identified all counter insertion locations. +/* */ /// * counter_index can be computed at time of counter insertion (incrementally). +/* */ /// * Numeric parameters are signed to match the llvm increment intrinsic parameter types. +/* */ fn __lower_incr_cov(_mangled_fn_name: &'static str, _fn_version_hash: i64, _num_counters: i32, _counter_index: i32) { +/* */ } +/* */ +/* */ /// A coverage counter implementation that will work as both an intermediate coverage +/* */ /// counting and reporting implementation at the AST-level only--for debugging and +/* */ /// development--but also serves as a "marker" to be replaced by calls to LLVM +/* */ /// intrinsic coverage counter APIs during the lowering process. +/* */ /// +/* */ /// Calls to this function will be injected automatically into the AST. When LLVM intrinsics +/* */ /// are enabled, the counter function calls that were injected into the AST serve as +/* */ /// placeholders, to be replaced by an alternative, such as: +/* */ /// +/* */ /// * direct invocation of the `llvm.instrprof.increment()` intrinsic; or +/* */ /// * the `__lower_incr_cov()` function, defined above, that would invoke the +/* */ /// `llvm.instrprof.increment()` intrinsic; or +/* */ /// * a similar expression wrapper, with the additional parameters (as defined above +/* */ /// for `__lower_incr_cov()`, that invokes `llvm.instrprof.increment()` and returns the +/* */ /// result of the wrapped expression) +/* */ /// +/* */ /// The first two options would require replacing the inlined wrapper call with something +/* */ /// like: +/* */ /// +/* */ /// ``` +/* */ /// { let result = {expr}; __inlined_incr_cov(context, counter); result } +/* */ /// ``` +/* */ /// +/* */ /// But if the lowering process is already unwrapping the inlined call to `__incr_cov()`, then +/* */ /// it may be a perfect opportunity to replace the function with one of these more +/* */ /// direct methods. +/* */ /// +/* */ #[inline(always)] +/* */ pub fn __incr_cov(region_loc: &str) { +/* */ // Either call the intermediate non-llvm coverage counter API or +/* */ // replace the call to this function with the expanded `__lower_incr_cov()` call. +/* */ +/* */ // let _lock = increment_counter(counter); +/* */ println!("{}", region_loc); +/* */ } +/* */ +/* */ /// Write a report identifying each incremented counter and the number of times each counter +/* */ /// was incremented. +/* */ fn __report() { +/* */ println!("WRITE REPORT!"); +/* */ } +/* */ +/* */ macro_rules! from { +/* */ ($from:expr) => { &format!("from: {}\n to: {}:{}:{}", $from, file!(), line!(), column!()) }; +/* */ } +/* */ +/* */ #[derive(Debug)] +/* */ enum TestEnum { +/* */ Red, +/* */ Green, +/* */ Blue, +/* */ } +/* */ +/* */ struct TestStruct { +/* */ field: i32, +/* */ } +/* */ +/* */ // IMPORTANT! IS WRAPPING main() ENOUGH? OR DO I ALSO NEED TO WRAP THREAD FUNCTIONS, ASSUMING +/* */ // THEY ARE STILL RUNNING WITH MAIN EXITS? (IF THEY CAN). NOT SURE HOW RUST HANDLES THAT. +/* */ +/* */ // I SUSPECT USING THREAD_LOCAL COUNTERS MAY NOT ACTUALLY BE AN OPTIMIZATION OVER MUTEX LOCKS, +/* */ // BUT MAYBE I SHOULD ASK. +/* */ +/* */ impl TestStruct { +/* - */ fn new() -> Self { +/* ┃ */ let __result = Self::new_with_value(31415); // function-scoped counter index = 0 +/* ┃ */ __incr_cov(from!("fn new()")); +/* ┃ */ __result +/* - */ } +/* */ +/* - */ fn new_with_value(field: i32) -> Self { +/* ┃ */ let __result = Self { +/* ┃ */ field, +/* ┃ */ }; +/* ┃ */ __incr_cov(from!("fn new_with_value()")); // function-scoped counter index = 0 +/* ┃ */ __result +/* - */ } +/* */ +/* */ fn call_closure(&self, closure: F) -> bool +/* */ where +/* */ F: FnOnce( +/* */ i32, +/* */ ) -> bool, +/* - */ { +/* ┃ */ let __result = closure(123); +/* ┃ */ __incr_cov(from!("fn call_closure()")); // function-scoped counter index = 0 +/* ┃ */ __result +/* - */ } +/* */ +/* - */ fn various(&self) -> Result<(),Error> { +/* ┃ */ use TestEnum::*; +/* ┃ */ let mut color = Red; +/* ┃ */ let _ = color; +/* ┃ */ color = Blue; +/* ┃ */ let _ = color; +/* ┃ */ color = Green; +/* ┃ */ match { let __result = color; __incr_cov(from!("fn various")); __result } { // function-scoped counter index = 0 +/* : */ +/* : */ // !!! RECORD SPAN FROM START OF INNERMOST CONTAINING BLOCK (THE FUNCTION IN THIS CASE) TO END OF MATCH EXPRESSION +/* : */ // If `match`, `while`, `loop`, `for`, `if`, etc. expression has a `return`, `break`, or `continue` +/* : */ // (if legal), then RECORD SPAN FROM START OF INNERMOST CONTAINING BLOCK TO END OF `return` EXPRESSION +/* : */ // If the expression includes lazy booleans, nest calls to `__incr_cov()`. +/* : I */ Red => {println!("roses"); __incr_cov(from!("Red => or end of MatchArmGuard expression inside pattern, if any"));} +/* : - */ Green => { +/* : ┃ */ let spidey = 100; +/* : ┃ */ let goblin = 50; +/* : ┃ */ // if spidey > goblin {__incr_cov(from!(""),{ +/* : ┃ */ // println!("what ev"); +/* : ┃ */ // })} +/* : ┃ */ // ACTUALLY, WRAPPING THE ENTIRE IF BLOCK IN `__incr_cov` IS NOT A GREAT GENERAL RULE. +/* : ┃ */ // JUST INSERTING A `return`, `break`, or `continue` IN THAT BLOCK (without an intermediate condition) +/* : ┃ */ // MAKES THE `__incr_cov()` CALL UNREACHABLE! +/* : ┃ */ // MY ORIGINAL SOLUTION WORKS BETTER (WRAP LAST EXPRESSION OR AFTER LAST SEMICOLON STATEMENT IN BLOCK) +/* : ┃ */ // UNLESS THE EXPRESSION IS NOT A BLOCK. +/* : ┃ - */ if { let __result = spidey > goblin; __incr_cov(from!("Green => or end of MatchArmGuard expression inside pattern, if any")); __result } { +/* : : ┃ */ println!("spidey beats goblin"); +/* : : ┃ */ __incr_cov(from!("block start")); +/* : ┃ - */ } else if { let __result = spidey == goblin; __incr_cov(from!("`else if` on this line")); __result } { +/* : : ┃ */ // COVERAGE NOTE: Do we mark only the expression span (that may be trivial, as in this case), +/* : : ┃ */ // or associate it with the outer block, similar to how the `if` expression is associated with +/* : : ┃ */ // the outer block? (Although it is a continuation, in a sense, it is discontiguous in this case, +/* : : ┃ */ // so I think simpler to just make it its own coverage region.) +/* : : ┃ */ println!("it's a draw"); +/* : : ┃ */ __incr_cov(from!("block start")); +/* : ┃ - - - */ } else if if { let __result = true; __incr_cov(from!("`else if` on this line")); __result } { +/* : : : ┃ */ // return __incr_cov(from!("after `if true`"),Ok(())); +/* : : : ┃ */ // ACTUALLY, BECAUSE OF `return`, WE DO NOT RECORD THE `if true` EVEN THOUGH WE COVERED IT. +/* : : : ┃ */ // IN FACT, IF THIS NESTED CONDITIONAL IN A CONDITIONAL EXPRESSION WAS AN `if` (WITHOUT PRECEDING ELSE) +/* : : : ┃ */ // WE WOULD NOT HAVE RECORDED THE COVERAGE OF STATEMENTS LEADING UP TO THE `if`, SO +/* : : : ┃ */ // IT SHOULD BE: +/* ┏-:---:-------:---< */ return { let __result = Ok(()); __incr_cov(from!("")); __result }; +/* V : : : : */ // NOTE THE `from` STRING IS SAME FOR THE `else if`s `__incr_cov` AND THIS `return`. +/* : : : : */ // ONLY ONE OF THESE WILL EXECUTE, TO RECORD COVERAGE FROM THAT SPOT. +/* : : ┃ - */ } else { +/* : : : I */ { let __result = false; __incr_cov(from!("`else`")); __result } +/* : : - - */ } { +/* : : ┃ */ println!("wierd science"); +/* : : ┃ */ __incr_cov(from!("block start")); +/* : ┃ - */ } else { +/* : : ┃ */ println!("goblin wins"); +/* ┏-:---:---< */ return { let __result = Ok(()); __incr_cov(from!("`else`")); __result }; // THIS COUNTS LAST STATEMENT IN `else` BLOCK +/* V : : : */ // COVERAGE NOTE: When counting the span for `return`, +/* : : : */ // `break`, or `continue`, also report the outer spans +/* : : : */ // got this far--including this `else` block. Record +/* : : : */ // The start positions for those outer blocks, but: +/* : : : */ // * For the block containing the `return`, `break`, or +/* : : : */ // `continue`, end report the end position is the +/* : : : */ // start of the `return` span (or 1 char before it). +/* : : : */ // * Anything else? +/* : ┃ - */ } +/* : ┃ - */ // __incr_cov(from!("")); // DO NOT COUNT HERE IF NO STATEMENTS AFTER LAST `if` or `match` +/* : - */ }, +/* : I */ Blue => { println!("violets"); __incr_cov(from!("Blue => or end of MatchArmGuard expression inside pattern, if any")); } +/* ┃ */ } +/* ┃ */ +/* ┃ */ let condition1 = true; +/* ┃ */ let condition2 = false; +/* ┃ */ let condition3 = true; +/* ┃ */ +/* ┃ */ println!("Called `various()` for TestStruct with field={}", self.field); +/* ┃ */ +/* ┃ - */ if { let __result = condition1; __incr_cov(from!("after block end of prior `match` (or `if-else if-else`)")); __result } { +/* : ┃ */ println!("before for loop"); +/* : ┃ - */ for index in { let __result = 0..10; __incr_cov(from!("block start")); __result } { +/* : : ┃ */ println!("top of `for` loop"); +/* : : ┃ - */ if { let __result = index == 8; __incr_cov(from!("block start")); __result } { +/* : : : ┃ */ println!("before break"); +/* : : : ┃ */ // note the following is not legal here: +/* : : : ┃ */ // "can only break with a value inside `loop` or breakable block" +/* : : : ┃ */ // break __incr_cov(from!("")); +/* : : : ┃ */ __incr_cov(from!("block start")); +/* : : ┏-----< */ break; +/* : : V : : */ +/* : : : : */ // FIXME(richkadel): add examples with loop labels, breaking out of inner and outer loop to outer loop label, with expression. +/* : : : : */ // May want to record both the span and the start position after the broken out block depdnding on label +/* : : ┃ - */ } +/* : : ┃ */ println!("after `break` test"); +/* : : ┃ - */ if { let __result = condition2; __incr_cov(from!("block end of `if index == 8`")); __result } { +/* ┏-:---:---:---< */ return { let __result = Ok(()); __incr_cov(from!("block start")); __result }; +/* V : : ┃ - */ } +/* : : ┃ */ +/* : : ┃ */ // BECAUSE THE PREVIOUS COVERAGE REGION HAS A `return`, THEN +/* : : ┃ */ // IF PREVIOUS COVERAGE REGION IS NOT COUNTED THEN OUTER REGION REACHED HERE. +/* : : ┃ */ // ADD A COVERAGE REGION FOR THE SPAN FROM JUST AFTER PREVIOUS REGION TO END +/* : : ┃ */ // OF OUTER SPAN, THEN TRUNCATE TO NEXT REGION NOT REACHED. +/* : : ┃ - */ if index % 3 == 2 { // NO __incr_cov() HERE BECAUSE NO STATEMENTS BETWEEN LAST CONDITIONAL BLOCK AND START OF THIS ONE +/* : : Λ : ┃ */ __incr_cov(from!("block end of `if condition2`")); +/* : : ┗-----< */ continue; +/* : : ┃ - */ } +/* : : ┃ */ println!("after `continue` test"); +/* : : ┃ */ // maybe add a runtime flag for a possible `return` here? +/* : : ┃ */ __incr_cov(from!("")); +/* : ┃ - */ } +/* : ┃ */ println!("after for loop"); +/* : ┃ */ let result = if { // START OF NEW CONDITIONAL EXPRESSION. NEXT "GUARANTEED" COUNTER SHOULD COUNT FROM END OF LAST CONDITIONAL EXPRESSION +/* : ┃ */ // A "GUARANTEED" COUNTER CALL IS ONE THAT WILL BE CALLED REGARDLESS OF OTHER CONDITIONS. THIS INCLUDES: +/* : ┃ */ // * A CONDITIONAL EXPRESSION THAT IS NOT A BLOCK (OR ANOTHER CONDITIONAL STATEMENT, WHICH WOULD CONTAIN A BLOCK) +/* : ┃ */ // * OR IF THE NEXT CONDITIONAL EXPRESSION IS A BLOCK OR CONDITIONAL STATEMENT, THEN THE FIRST "GUARANTEED" COUNTER IN THAT BLOCK +/* : ┃ */ // * END OF BLOCK IF THE BLOCK DOES NOT HAVE INNER CONDITIONAL EXPRESSIONS +/* : ┃ */ // * BRANCHING STATEMENTS (`return`, `break`, `continue`) BY EITHER WRAPPING THE BRANCH STATEMENT NON-BLOCK EXPRESSION, +/* : ┃ */ // OR PREPENDING A COUNTER WITH EMPTY TUPLE IF NO EXPRESSION, OR IF EXPRESSION IS A BLOCK, THEN THE NEXT "GUARANTEED" +/* : ┃ */ // COUNTER CALL WITHIN THAT BLOCK. +/* : ┃ */ // BASICALLY, CARRY THE START OF COVERAGE SPAN FORWARD UNTIL THE GUARANTEED COUNTER IS FOUND +/* : ┃ */ println!("after result = if ..."); +/* : ┃ - */ if { let __result = condition2; __incr_cov(from!("block end of `for` loop")); __result } { +/* : : ┃ */ println!("before first return"); +/* ┏-:---:-------< */ return { let __result = Ok(()); __incr_cov(from!("block start")); __result }; +/* V : : - */ } else if { let __result = condition3; __incr_cov(from!("`else`")); __result } { +/* : : ┃ */ // THE ABOVE COUNTER IS _NOT_ REALLY NECESSARY IF EXPRESSION IS GUARANTEED TO EXECUTE. +/* : : ┃ */ // IF WE GET COUNTER IN `else if` BLOCK WE COVERED EXPRESSION. +/* : : ┃ */ // IF WE GET TO ANY REMAINING `else` or `else if` BLOCK WE KNOW WE EVALUATED THIS CONDITION +/* : : ┃ */ // AND ALL OTHERS UP TO THE EXECUTED BLOCK. BUT THE SPAN WOULD HAVE "HOLES" FOR UNEXECUTED BLOCKS. +/* : : ┃ */ println!("not second return"); +/* ┏-:---:-------< */ return { let __result = Ok(()); __incr_cov(from!("block start")); __result }; +/* V : : - */ } else { +/* : : ┃ */ println!("not returning"); +/* : : ┃ */ { let __result = false; __incr_cov(from!("block start")); __result } +/* : : - */ } +/* : ┃ */ // NO COUNTER HERE BECAUSE NO STATEMENTS AFTER CONDITIONAL BLOCK +/* : ┃ - */ } { +/* : : ┃ */ println!("branched condition returned true"); +/* : : ┃ */ { let __result = Ok(()); __incr_cov(from!("")); __result } +/* : ┃ - */ } else if self.call_closure( +/* : : - */ |closure_param| { +/* : : ┃ - */ let __result = if condition3 { +/* : : : ┃ */ println!("in closure, captured condition said to print the param {}", closure_param); +/* : : : ┃ */ { let __result = false; __incr_cov(from!("")); __result } +/* : : ┃ - */ } else { +/* : : : ┃ */ println!("in closure, captured condition was false"); +/* : : : ┃ */ { let __result = true; __incr_cov(from!("")); __result } +/* : : ┃ - */ }; +/* : : - */ __incr_cov(from!("")); __result } +/* : : - */ ) { +/* : : ┃ */ println!("closure returned true"); +/* : : ┃ */ { let __result = Err(Error::new(ErrorKind::Other, "Result is error if closure returned true")); __incr_cov(from!("")); __result } +/* : ┃ - */ } else { +/* : : ┃ */ println!("closure returned false"); +/* : : ┃ */ { let __result = Err(Error::new(ErrorKind::Other, "Result is error if closure returned false")); __incr_cov(from!("")); __result } +/* : ┃ - */ }; +/* : ┃ */ println!("bottom of function might be skipped if early `return`"); +/* : ┃ */ { let __result = result; __incr_cov(from!("if condition1")); __result } +/* ┃ - */ } else { +/* : ┃ */ println!("skipping everything in `various()`"); +/* : ┃ */ { let __result = Ok(()); __incr_cov(from!("")); __result } +/* ┃ - */ } +/* ┃ - */ // __incr_cov(from!(""),0) // DO NOT COUNT IF NO STATEMENTS AFTER CONDITIONAL BLOCK. ALL COVERAGE IS ALREADY COUNTED +/* - */ } +/* */ } +/* */ +/* - */ fn main() -> Result<(), std::io::Error> { +/* ┃ */ //let mut status: u8 = 2; +/* ┃ */ let mut status: u8 = 1; +/* : - */ let result = if status < 2 && +/* : ┃ */ { let __result = { +/* : ┃ */ status -= 1; +/* : ┃ */ status == 0 +/* : - - */ }; __incr_cov(from!("")); __result } { +/* : ┃ */ let test_struct = TestStruct::new_with_value(100); +/* : ┃ */ let _ = test_struct.various(); +/* ┏-:---< */ return { let __result = Err(Error::new(ErrorKind::Other, format!("Error status {}", status))); __incr_cov(from!("")); __report(); __result } +/* V : - */ } else { +/* : ┃ */ let test_struct = TestStruct::new(); +/* : ┃ */ { let __result = test_struct.various(); __incr_cov(from!("")); __result } +/* : - */ }; +/* ┃ */ println!("done"); +/* ┃ */ { let __result = result; __incr_cov(from!("")); __report(); __result } +/* - */ } \ No newline at end of file diff --git a/src/test/codegen/coverage-experiments/src/coverage_injection_test_alt.rs b/src/test/codegen/coverage-experiments/src/coverage_injection_test_alt.rs new file mode 100644 index 000000000000..20c4835dd882 --- /dev/null +++ b/src/test/codegen/coverage-experiments/src/coverage_injection_test_alt.rs @@ -0,0 +1,362 @@ +/* */ use std::io::Error; +/* */ use std::io::ErrorKind; +/* */ +/* */ /// Align Rust counter increment with with: +/* */ /// [‘llvm.instrprof.increment’ Intrinsic](https://llvm.org/docs/LangRef.html#llvm-instrprof-increment-intrinsic) +/* */ /// +/* */ /// declare void @llvm.instrprof.increment(i8* , i64 , i32 , i32 ) +/* */ /// +/* */ /// The first argument is a pointer to a global variable containing the name of the entity +/* */ /// being instrumented. This should generally be the (mangled) function name for a set of +/* */ /// counters. +/* */ /// +/* */ /// The second argument is a hash value that can be used by the consumer of the profile data +/* */ /// to detect changes to the instrumented source, and the third is the number of counters +/* */ /// associated with name. It is an error if hash or num-counters differ between two +/* */ /// instances of instrprof.increment that refer to the same name. +/* */ /// +/* */ /// The last argument refers to which of the counters for name should be incremented. It +/* */ /// should be a value between 0 and num-counters. +/* */ /// +/* */ /// # Arguments +/* */ /// +/* */ /// `mangled_fn_name` - &'static ref to computed and injected static str, using: +/* */ /// +/* */ /// ``` +/* */ /// fn rustc_symbol_mangling::compute_symbol_name( +/* */ /// tcx: TyCtxt<'tcx>, +/* */ /// instance: Instance<'tcx>, +/* */ /// compute_instantiating_crate: impl FnOnce() -> CrateNum, +/* */ /// ) -> String +/* */ /// ``` +/* */ /// +/* */ /// `source_version_hash` - Compute hash based that only changes if there are "significant" +/* */ /// to control-flow inside the function. +/* */ /// +/* */ /// `num_counters` - The total number of counter calls [MAX(counter_index) + 1] within the +/* */ /// function. +/* */ /// +/* */ /// `counter_index` - zero-based counter index scoped by the function. (Ordering of +/* */ /// counters, relative to the source code location, is apparently not expected.) +/* */ /// +/* */ /// # Notes +/* */ /// +/* */ /// * The mangled_fn_name may not be computable until generics are monomorphized (see +/* */ /// parameters required by rustc_symbol_mangling::compute_symbol_name). +/* */ /// * The version hash may be computable from AST analysis, and may not benefit from further +/* */ /// lowering. +/* */ /// * num_counters depends on having already identified all counter insertion locations. +/* */ /// * counter_index can be computed at time of counter insertion (incrementally). +/* */ /// * Numeric parameters are signed to match the llvm increment intrinsic parameter types. +/* */ fn __lower_incr_cov(_mangled_fn_name: &'static str, _fn_version_hash: i64, _num_counters: i32, _counter_index: i32) { +/* */ } +/* */ +/* */ /// A coverage counter implementation that will work as both an intermediate coverage +/* */ /// counting and reporting implementation at the AST-level only--for debugging and +/* */ /// development--but also serves as a "marker" to be replaced by calls to LLVM +/* */ /// intrinsic coverage counter APIs during the lowering process. +/* */ /// +/* */ /// Calls to this function will be injected automatically into the AST. When LLVM intrinsics +/* */ /// are enabled, the counter function calls that were injected into the AST serve as +/* */ /// placeholders, to be replaced by an alternative, such as: +/* */ /// +/* */ /// * direct invocation of the `llvm.instrprof.increment()` intrinsic; or +/* */ /// * the `__lower_incr_cov()` function, defined above, that would invoke the +/* */ /// `llvm.instrprof.increment()` intrinsic; or +/* */ /// * a similar expression wrapper, with the additional parameters (as defined above +/* */ /// for `__lower_incr_cov()`, that invokes `llvm.instrprof.increment()` and returns the +/* */ /// result of the wrapped expression) +/* */ /// +/* */ /// The first two options would require replacing the inlined wrapper call with something +/* */ /// like: +/* */ /// +/* */ /// ``` +/* */ /// { let result = {expr}; __inlined_incr_cov(context, counter); result } +/* */ /// ``` +/* */ /// +/* */ /// But if the lowering process is already unwrapping the inlined call to `__incr_cov()`, then +/* */ /// it may be a perfect opportunity to replace the function with one of these more +/* */ /// direct methods. +/* */ /// +/* */ #[inline(always)] +/* */ pub fn __incr_cov(region_loc: &str, /*index: u32,*/) { +/* */ // Either call the intermediate non-llvm coverage counter API or +/* */ // replace the call to this function with the expanded `__lower_incr_cov()` call. +/* */ +/* */ // let _lock = increment_counter(counter); +/* */ println!("{}", region_loc); +/* */ } +/* */ +/* */ /// Write a report identifying each incremented counter and the number of times each counter +/* */ /// was incremented. +/* */ fn __report() { +/* */ println!("WRITE REPORT!"); +/* */ } +/* */ +/* */ /// Increment the counter after evaluating the wrapped expression (see `__incr_cov()`), then +/* */ /// write a report identifying each incremented counter and the number of times each counter +/* */ /// was incremented. +/* */ #[inline(always)] +/* */ pub fn __incr_cov_and_report(region_loc: &str, /*counter: u32,*/ result: T) -> T { +/* */ __incr_cov(region_loc, /*counter,*/); +/* */ __report(); +/* */ result +/* */ } +/* */ +/* */ macro_rules! from { +/* */ ($from:expr) => { &format!("from: {}\n to: {}:{}:{}", $from, file!(), line!(), column!()) }; +/* */ } +/* */ +/* */ macro_rules! to { +/* */ ($to:expr) => { &format!("to: {}\n to: {}:{}:{}", $to, file!(), line!(), column!()) }; +/* */ } +/* */ +/* */ #[derive(Debug)] +/* */ enum TestEnum { +/* */ Red, +/* */ Green, +/* */ Blue, +/* */ } +/* */ +/* */ struct TestStruct { +/* */ field: i32, +/* */ } +/* */ +/* */ // IMPORTANT! IS WRAPPING main() ENOUGH? OR DO I ALSO NEED TO WRAP THREAD FUNCTIONS, ASSUMING +/* */ // THEY ARE STILL RUNNING WITH MAIN EXITS? (IF THEY CAN). NOT SURE HOW RUST HANDLES THAT. +/* */ +/* */ // I SUSPECT USING THREAD_LOCAL COUNTERS MAY NOT ACTUALLY BE AN OPTIMIZATION OVER MUTEX LOCKS, +/* */ // BUT MAYBE I SHOULD ASK. +/* */ +/* */ impl TestStruct { +/* - */ fn new() -> Self { +/* ┃ */ __incr_cov(to!("end of fn new()")); // function-scoped counter index = 0 +/* ┃ */ Self::new_with_value(31415) +/* - */ } +/* */ +/* - */ fn new_with_value(field: i32) -> Self { +/* ┃ */ __incr_cov(to!("end of fn new_with_value()")); // function-scoped counter index = 0 +/* ┃ */ Self { +/* ┃ */ field, +/* ┃ */ } +/* - */ } +/* */ +/* */ fn call_closure(&self, closure: F) -> bool +/* */ where +/* */ F: FnOnce( +/* */ i32, +/* */ ) -> bool, +/* - */ { +/* ┃ */ __incr_cov(to!("end of fn call_closure()")); // function-scoped counter index = 0 +/* ┃ */ closure(123) +/* - */ } +/* */ +/* - */ fn various(&self) -> Result<(),Error> { +/* ┃ */ __incr_cov(to!("just before next branch: after `match color`: pattern selection")); +/* ┃ */ use TestEnum::*; +/* ┃ */ let mut color = Red; +/* ┃ */ let _ = color; +/* ┃ */ color = Blue; +/* ┃ */ let _ = color; +/* ┃ */ color = Green; +/* ┃ */ match color { // function-scoped counter index = 0 +/* : */ +/* : */ // !!! RECORD SPAN FROM START OF INNERMOST CONTAINING BLOCK (THE FUNCTION IN THIS CASE) TO END OF MATCH EXPRESSION +/* : */ // If `match`, `while`, `loop`, `for`, `if`, etc. expression has a `return`, `break`, or `continue` +/* : */ // (if legal), then RECORD SPAN FROM START OF INNERMOST CONTAINING BLOCK TO END OF `return` EXPRESSION +/* : */ // If the expression includes lazy booleans, nest calls to `__incr_cov()`. +/* : - */ Red => { +/* : ┃ */ __incr_cov(to!("end of matched Red")); +/* : ┃ */ println!("roses"); +/* : - */ } +/* : - */ Green => { +/* : ┃ */ __incr_cov(to!("just before next branch: after `if spidey > goblin`")); +/* : ┃ */ let spidey = 100; +/* : ┃ */ let goblin = 50; +/* : ┃ */ // if spidey > goblin {__incr_cov(from!(""),{ +/* : ┃ */ // println!("what ev"); +/* : ┃ */ // })} +/* : ┃ */ // ACTUALLY, WRAPPING THE ENTIRE IF BLOCK IN `__incr_cov` IS NOT A GREAT GENERAL RULE. +/* : ┃ */ // JUST INSERTING A `return`, `break`, or `continue` IN THAT BLOCK (without an intermediate condition) +/* : ┃ */ // MAKES THE `__incr_cov()` CALL UNREACHABLE! +/* : ┃ */ // MY ORIGINAL SOLUTION WORKS BETTER (WRAP LAST EXPRESSION OR AFTER LAST SEMICOLON STATEMENT IN BLOCK) +/* : ┃ */ // UNLESS THE EXPRESSION IS NOT A BLOCK. +/* : ┃ - */ if spidey > goblin { +/* : : ┃ */ __incr_cov(to!("end of if block, if no earlier branch in this scope")); +/* : : ┃ */ println!("spidey beats goblin"); +/* : : ┃ */ +/* : ┃ - */ } else if { +/* : : : */ // Make sure we can't compute the coverage count here. +/* : : : */ // We know the expression executed if the previous if block DID NOT +/* : : : */ // execute, and either this `else if` block does execute OR any subsequent +/* : : : */ // `else if` or `else` blocks execute, OR none of the blocks in the +/* : : : */ // `if`, `else if` or `else` blocks execute. +/* : : : */ // `if`, `else if` or `else` blocks execute. +/* : : ┃ */ __incr_cov(to!("end of `else if spidey == goblin` expression")); +/* : : ┃ */ spidey == goblin +/* : ┃ - */ } { +/* : : ┃ */ __incr_cov(to!("end of if block, if no earlier branch in this scope")); +/* : : ┃ */ // COVERAGE NOTE: Do we mark only the expression span (that may be trivial, as in this case), +/* : : ┃ */ // or associate it with the outer block, similar to how the `if` expression is associated with +/* : : ┃ */ // the outer block? (Although it is a continuation, in a sense, it is discontiguous in this case, +/* : : ┃ */ // so I think simpler to just make it its own coverage region.) +/* : : ┃ */ println!("it's a draw"); +/* : : ┃ */ +/* : ┃ - - - */ } else if { +/* : : ┃ */ __incr_cov(to!("end of `if true`")); +/* : ┃ - - - */ if true { +/* : : : ┃ */ __incr_cov(to!("end of `return Ok(())`")); +/* ┏-:---:-------:---< */ return Ok(()); +/* V : : ┃ - */ } else { +/* : : : ┃ */ // __incr_cov(to!("end of else block")); +/* : : : ┃ */ // computed counter expression +/* : : : ┃ */ false +/* : : : - */ } +/* : : - - - */ } { +/* : : ┃ */ __incr_cov(to!("end of if block")); +/* : : ┃ */ println!("wierd science"); +/* : ┃ - */ } else { +/* : : ┃ */ // __incr_cov(to!("end of `return Ok(())")); +/* : : ┃ */ // counter expression: (start of Green match arm) - (if spidey > goblin) - (previous `} else if {`) +/* : : ┃ */ println!("goblin wins"); +/* ┏-:---:---< */ return Ok(()); // THIS COUNTS LAST STATEMENT IN `else` BLOCK +/* V : : : */ // COVERAGE NOTE: When counting the span for `return`, +/* : : : */ // `break`, or `continue`, also report the outer spans +/* : : : */ // got this far--including this `else` block. Record +/* : : : */ // The start positions for those outer blocks, but: +/* : : : */ // * For the block containing the `return`, `break`, or +/* : : : */ // `continue`, end report the end position is the +/* : : : */ // start of the `return` span (or 1 char before it). +/* : : : */ // * Anything else? +/* : ┃ - */ } +/* : : */ // __incr_cov(to!("end of matched Green")); +/* : : */ // // DO NOT COUNT HERE IF NO STATEMENTS AFTER LAST `if` or `match` +/* : - */ }, +/* : - */ Blue => { +/* : ┃ */ __incr_cov(to!("end of matched Blue")); +/* : ┃ */ println!("violets"); +/* : - */ } +/* ┃ */ } +/* ┃ */ __incr_cov(to!("just before next branch: after `if condition1` (HIR: 'match condition1')")); +/* ┃ */ +/* ┃ */ let condition1 = true; +/* ┃ */ let condition2 = false; +/* ┃ */ let condition3 = true; +/* ┃ */ +/* ┃ */ println!("Called `various()` for TestStruct with field={}", self.field); +/* ┃ */ +/* ┃ - */ if condition1 { +/* : ┃ */ println!("before while loop"); +/* : ┃ */ let mut countdown = 10; +/* : ┃ */ // Must increment before repeated while text expression +/* : : I */ while countdown > 0 { // span is just the while test expression +/* : : ┃ */ println!("top of `while` loop"); +/* : : ┃ */ countdown -= 1; +/* : : ┃ */ // // Counter not needed, but span is computed as "while test" minus "block start" +/* : : ┃ */ // If test expression is 11, and the outer block runs only once, 11-1 = 10 +/* : ┃ - */ } +/* : ┃ */ println!("before for loop"); +/* : ┃ - */ for index in 0..10 { +/* : : ┃ */ println!("top of `for` loop"); +/* : : ┃ - */ if index == 8 { +/* : : : ┃ */ println!("before break"); +/* : : : ┃ */ // note the following is not legal here: +/* : : : ┃ */ // "can only break with a value inside `loop` or breakable block" +/* : : : ┃ */ // break +/* : : : ┃ */ +/* : : ┏-----< */ break; +/* : : V : : */ +/* : : : : */ // FIXME(richkadel): add examples with loop labels, breaking out of inner and outer loop to outer loop label, with expression. +/* : : : : */ // May want to record both the span and the start position after the broken out block depdnding on label +/* : : ┃ - */ } +/* : : ┃ */ println!("after `break` test"); +/* : : ┃ - */ if condition2 { +/* ┏-:---:---:---< */ return Ok(()); +/* V : : ┃ - */ } +/* : : ┃ */ +/* : : ┃ */ // BECAUSE THE PREVIOUS COVERAGE REGION HAS A `return`, THEN +/* : : ┃ */ // IF PREVIOUS COVERAGE REGION IS NOT COUNTED THEN OUTER REGION REACHED HERE. +/* : : ┃ */ // ADD A COVERAGE REGION FOR THE SPAN FROM JUST AFTER PREVIOUS REGION TO END +/* : : ┃ */ // OF OUTER SPAN, THEN TRUNCATE TO NEXT REGION NOT REACHED. +/* : : ┃ - */ if index % 3 == 2 { // NO __incr_cov() HERE BECAUSE NO STATEMENTS BETWEEN LAST CONDITIONAL BLOCK AND START OF THIS ONE +/* : : Λ : ┃ */ +/* : : ┗-----< */ continue; +/* : : ┃ - */ } +/* : : ┃ */ println!("after `continue` test"); +/* : : ┃ */ // maybe add a runtime flag for a possible `return` here? +/* : : ┃ */ +/* : ┃ - */ } +/* : ┃ */ println!("after for loop"); +/* : ┃ */ let result = if { // START OF NEW CONDITIONAL EXPRESSION. NEXT "GUARANTEED" COUNTER SHOULD COUNT FROM END OF LAST CONDITIONAL EXPRESSION +/* : ┃ */ // A "GUARANTEED" COUNTER CALL IS ONE THAT WILL BE CALLED REGARDLESS OF OTHER CONDITIONS. THIS INCLUDES: +/* : ┃ */ // * A CONDITIONAL EXPRESSION THAT IS NOT A BLOCK (OR ANOTHER CONDITIONAL STATEMENT, WHICH WOULD CONTAIN A BLOCK) +/* : ┃ */ // * OR IF THE NEXT CONDITIONAL EXPRESSION IS A BLOCK OR CONDITIONAL STATEMENT, THEN THE FIRST "GUARANTEED" COUNTER IN THAT BLOCK +/* : ┃ */ // * END OF BLOCK IF THE BLOCK DOES NOT HAVE INNER CONDITIONAL EXPRESSIONS +/* : ┃ */ // * BRANCHING STATEMENTS (`return`, `break`, `continue`) BY EITHER WRAPPING THE BRANCH STATEMENT NON-BLOCK EXPRESSION, +/* : ┃ */ // OR PREPENDING A COUNTER WITH EMPTY TUPLE IF NO EXPRESSION, OR IF EXPRESSION IS A BLOCK, THEN THE NEXT "GUARANTEED" +/* : ┃ */ // COUNTER CALL WITHIN THAT BLOCK. +/* : ┃ */ // BASICALLY, CARRY THE START OF COVERAGE SPAN FORWARD UNTIL THE GUARANTEED COUNTER IS FOUND +/* : ┃ */ println!("after result = if ..."); +/* : ┃ - */ if condition2 { +/* : : ┃ */ println!("before first return"); +/* ┏-:---:-------< */ return Ok(()); +/* V : : - */ } else if condition3 { +/* : : ┃ */ // THE ABOVE COUNTER IS _NOT_ REALLY NECESSARY IF EXPRESSION IS GUARANTEED TO EXECUTE. +/* : : ┃ */ // IF WE GET COUNTER IN `else if` BLOCK WE COVERED EXPRESSION. +/* : : ┃ */ // IF WE GET TO ANY REMAINING `else` or `else if` BLOCK WE KNOW WE EVALUATED THIS CONDITION +/* : : ┃ */ // AND ALL OTHERS UP TO THE EXECUTED BLOCK. BUT THE SPAN WOULD HAVE "HOLES" FOR UNEXECUTED BLOCKS. +/* : : ┃ */ println!("not second return"); +/* ┏-:---:-------< */ return Ok(()); +/* V : : - */ } else { +/* : : ┃ */ println!("not returning"); +/* : : ┃ */ false +/* : : - */ } +/* : ┃ */ // NO COUNTER HERE BECAUSE NO STATEMENTS AFTER CONDITIONAL BLOCK +/* : ┃ - */ } { +/* : : ┃ */ println!("branched condition returned true"); +/* : : ┃ */ Ok(()) +/* : ┃ - */ } else if self.call_closure( +/* : : - */ |closure_param| +/* : : ┃ - */ if condition3 { +/* : : : ┃ */ println!("in closure, captured condition said to print the param {}", closure_param); +/* : : : ┃ */ false +/* : : ┃ - */ } else { +/* : : : ┃ */ println!("in closure, captured condition was false"); +/* : : : ┃ */ true +/* : : ┃ - */ } +/* : : - */ +/* : : - */ ) { +/* : : ┃ */ println!("closure returned true"); +/* : : ┃ */ Err(Error::new(ErrorKind::Other, "Result is error if closure returned true")) +/* : ┃ - */ } else { +/* : : ┃ */ println!("closure returned false"); +/* : : ┃ */ Err(Error::new(ErrorKind::Other, "Result is error if closure returned false")) +/* : ┃ - */ }; +/* : ┃ */ println!("bottom of function might be skipped if early `return`"); +/* : ┃ */ result +/* ┃ - */ } else { +/* : ┃ */ println!("skipping everything in `various()`"); +/* : ┃ */ Ok(()) +/* ┃ - */ } +/* ┃ - */ // 0 // DO NOT COUNT IF NO STATEMENTS AFTER CONDITIONAL BLOCK. ALL COVERAGE IS ALREADY COUNTED +/* - */ } +/* */ } +/* */ +/* - */ fn main() -> Result<(), std::io::Error> { +/* ┃ */ //let mut status: u8 = 2; +/* ┃ */ let mut status: u8 = 1; +/* : - */ let result = if status < 2 && +/* : ┃ */ { +/* : ┃ */ status -= 1; +/* : ┃ */ status == 0 +/* : - - */ } { +/* : ┃ */ let test_struct = TestStruct::new_with_value(100); +/* : ┃ */ let _ = test_struct.various(); +/* ┏-:---< */ return __incr_cov_and_report(from!(""),Err(Error::new(ErrorKind::Other, format!("Error status {}", status)))) +/* V : - */ } else { +/* : ┃ */ let test_struct = TestStruct::new(); +/* : ┃ */ test_struct.various() +/* : - */ }; +/* ┃ */ println!("done"); +/* ┃ */ __incr_cov_and_report(from!(""),result) // function-scoped counter index = 0 +/* - */ } \ No newline at end of file diff --git a/src/test/codegen/coverage-experiments/src/drop_trait.rs b/src/test/codegen/coverage-experiments/src/drop_trait.rs new file mode 100644 index 000000000000..75400e037e9f --- /dev/null +++ b/src/test/codegen/coverage-experiments/src/drop_trait.rs @@ -0,0 +1,25 @@ +#[inline(always)] +pub fn __incr_cov(_region_loc: &str, result: T) -> T { + result +} + +struct Firework { + _strength: i32, +} + +impl Drop for Firework { + fn drop(&mut self) { + __incr_cov("start of drop()", ()); + } +} + +fn main() -> Result<(),u8> { + let _firecracker = Firework { _strength: 1 }; + + if __incr_cov("start of main()", true) { + return __incr_cov("if true", { let _t = Err(1); _t }); + } + + let _tnt = Firework { _strength: 100 }; + Ok(()) +} \ No newline at end of file diff --git a/src/test/codegen/coverage-experiments/src/drop_trait_with_comments_prints.rs b/src/test/codegen/coverage-experiments/src/drop_trait_with_comments_prints.rs new file mode 100644 index 000000000000..de9f5d5cb464 --- /dev/null +++ b/src/test/codegen/coverage-experiments/src/drop_trait_with_comments_prints.rs @@ -0,0 +1,53 @@ +// +// +// +// It's interesting to speculate if there is a way to leverage the Drop trait functionality +// to increment counters when a scope is closed, but I don't think it would help "out of the box". +// +// A `return` or `break` with expression might not need a temp value expression wrapper +// such as `return { let _t = result_expression; __incr_counter(...); _t };` +// +// ... **if** the __incr_counter() was somehow called from a "drop()" trait function. +// +// The problem is, since the drop call is automatic, there is no way to have argument variants +// depending on where the drop() occurs (e.g., from a `return` statement vs. from the end of +// the function). We need 2 different code regions though. +// +// +// +// + +#[inline(always)] +pub fn __incr_cov(_region_loc: &str, /*index: u32,*/ result: T) -> T { + // println!("from: {}", _region_loc); + result +} + +struct Firework { + strength: i32, +} + +impl Drop for Firework { + fn drop(&mut self) { + println!("BOOM times {}!!!", self.strength); + __incr_cov("start of drop()", ()); + } +} + +fn main() -> Result<(),u8> { + let _firecracker = Firework { strength: 1 }; + + if __incr_cov("start of main()", true) { + return __incr_cov("if true", { let _t = Err(1); println!("computing return value"); _t }); + } + + let _tnt = Firework { strength: 100 }; + // __incr_cov("after if block", Ok(())) // CAN USE COUNTER EXPRESSION: "start of drop()" - "if true" + Ok(()) +} + +// OUTPUT WHEN RUNNING THIS PROGRAM IS AS EXPECTED: + +// computing return value +// BOOM times 1!!! +// Error: 1 diff --git a/src/test/codegen/coverage-experiments/src/for.rs b/src/test/codegen/coverage-experiments/src/for.rs new file mode 100644 index 000000000000..3f44c382a1e3 --- /dev/null +++ b/src/test/codegen/coverage-experiments/src/for.rs @@ -0,0 +1,41 @@ +#[inline(always)] +pub fn __incr_cov(_region_loc: &str, /*index: u32,*/ result: T) -> T { + result +} + +fn main() { + for countdown in __incr_cov("start", 10..0) { + let _ = countdown; + __incr_cov("top of for", ()); + } +} + +// LOWERED TO HIR: +// +// fn main() { +// { +// let _t = +// match ::std::iter::IntoIterator::into_iter(__incr_cov("start", +// ::std::ops::Range{start: +// 10, +// end: +// 0,})) +// { +// mut iter => +// loop { +// let mut __next; +// match ::std::iter::Iterator::next(&mut iter) { +// ::std::option::Option::Some(val) => +// __next = val, +// ::std::option::Option::None => break , +// } +// let countdown = __next; +// { +// let _ = countdown; +// __incr_cov("top of for", ()); +// } +// }, +// }; +// _t +// } +// } \ No newline at end of file diff --git a/src/test/codegen/coverage-experiments/src/for_with_comments.rs b/src/test/codegen/coverage-experiments/src/for_with_comments.rs new file mode 100644 index 000000000000..03d11b2c230c --- /dev/null +++ b/src/test/codegen/coverage-experiments/src/for_with_comments.rs @@ -0,0 +1,24 @@ +/* */ #[inline(always)] +/* */ pub fn __incr_cov(_region_loc: &str, /*index: u32,*/ result: T) -> T { +/* */ result +/* */ } +/* */ +/* - */ fn main() { +/* : I */ for countdown in __incr_cov("start", 10..0) { // span is just the while test expression +/* : ┃ */ let _ = countdown; +/* : ┃ */ __incr_cov("top of for", ()); +/* ┃ - */ } +/* - */ } + + +// -Z unpretty=val -- present the input source, unstable (and less-pretty) variants; +// valid types are any of the types for `--pretty`, as well as: +// `expanded`, `expanded,identified`, +// `expanded,hygiene` (with internal representations), +// `everybody_loops` (all function bodies replaced with `loop {}`), +// `hir` (the HIR), `hir,identified`, +// `hir,typed` (HIR with types for each node), +// `hir-tree` (dump the raw HIR), +// `mir` (the MIR), or `mir-cfg` (graphviz formatted MIR) + +// argument to `pretty` must be one of `normal`, `expanded`, `identified`, or `expanded,identified` diff --git a/src/test/codegen/coverage-experiments/src/if.rs b/src/test/codegen/coverage-experiments/src/if.rs new file mode 100644 index 000000000000..ad50f6be1900 --- /dev/null +++ b/src/test/codegen/coverage-experiments/src/if.rs @@ -0,0 +1,80 @@ +#![feature(core_intrinsics)] + +pub fn __llvm_incr_counter(_region_loc: &str) { +} + +#[inline(always)] +pub fn __incr_cov(region_loc: &str, result: T) -> T { + __llvm_incr_counter(region_loc); + result +} + +static TEST_FUNC_NAME: &'static [u8; 6] = b"main()"; + +fn main() { + let mut countdown = 10; + if __incr_cov("start", countdown > 0) { + + + // // TEST CALLING INTRINSIC: + unsafe { core::intrinsics::instrprof_increment(TEST_FUNC_NAME as *const u8, 1234 as u64, 314 as u32, 31 as u32) }; + // // Results in: + // // LLVM ERROR: Cannot select: intrinsic %llvm.instrprof.increment + // // I may need to pass one or more of the following flags (or equivalent opts) to LLVM to enable this: + // // -fprofile-instr-generate -fcoverage-mapping + + + countdown -= 1; + __incr_cov("if block",()); + } else if countdown > 5 { + countdown -= 2; + __incr_cov("else if block",()); + } else { + countdown -= 3; + } + + let mut countdown = 10; + if { let _tcov = countdown > 0; __llvm_incr_counter("start", ); _tcov } { + countdown -= 1; + __incr_cov("if block",()); + } else if countdown > 5 { + countdown -= 2; + __incr_cov("else if block",()); + } else { + countdown -= 3; + } +} + +// NOTE: hir REDUNDANTLY lowers the manually inlined counter in the second if block to: +// +// match { +// let _t = +// { +// let _tcov = countdown > 0; +// __llvm_incr_counter("start"); +// _tcov +// }; +// _t +// } { + +// I don't know if optimization phases will fix this or not. +// Otherwise, a more optimal (but definitely special case) way to handle this would be +// to inject the counter between the hir-introduced temp `_t` assignment and the block result +// line returning `_t`: +// +// match { +// let _t = countdown > 0; +// __llvm_incr_counter("start"); // <-- the only thing inserted for coverage here +// _t +// } +// +// UNFORTUNATELY THIS IS NOT A PATTERN WE CAN ALWAYS LEVERAGE, FOR EXPRESSIONS THAT HAVE VALUES +// WHERE WE NEED TO INJECT THE COUNTER AFTER THE EXPRESSION BUT BEFORE IT IS USED. +// +// IT DOES APPEAR TO BE THE CASE FOR WHILE EXPRESSIONS, (BECOMES loop { match { let _t = condition; _t} { true => {...} _ => break, }}) +// AND IS TRUE FOR IF EXPRESSIONS AS NOTED +// BUT NOT FOR RETURN STATEMENT (and I'm guessing not for loop { break value; } ? ) +// +// AND NOT FOR LAZY BOOLEAN EXPRESSIONS! +// +// AND NOT FOR MATCH EXPRESSIONS IN THE ORIGINAL SOURCE! \ No newline at end of file diff --git a/src/test/codegen/coverage-experiments/src/if_with_comments.rs b/src/test/codegen/coverage-experiments/src/if_with_comments.rs new file mode 100644 index 000000000000..267e7bca2c5a --- /dev/null +++ b/src/test/codegen/coverage-experiments/src/if_with_comments.rs @@ -0,0 +1,39 @@ +/* */ #[inline(always)] +/* */ pub fn __incr_cov(_region_loc: &str, /*index: u32,*/ result: T) -> T { +/* */ result +/* */ } +/* */ +/* - */ fn main() { +/* ┃ */ let mut countdown = 10; +/* : I */ if __incr_cov("start", countdown > 0) { // span is from start of main() +/* : ┃ */ countdown -= 1; +/* : ┃ */ __incr_cov("if block",()); +/* ┃ - */ } + + let mut countdown = 10; + if __incr_cov("start", countdown > 0) { + countdown -= 1; + __incr_cov("if block",()); + } else if countdown > 5 { // counter expression "start" - "if block" + countdown -= 2; + __incr_cov("else if block",()); + } else { + countdown -= 3; + // __incr_cov("else block",()); // counter expression (countdown > 5 counter expression) - "else if block" + // PLACED AT END OF ELSE BLOCK OR START OF FIRST CONDITIONAL BLOCK, IF ANY (PRESUMING POSSIBLE EARLY EXIT). + // IF WE CAN GUARANTEE NO EARLY EXIT IN THIS BLOCK, THEN AT THE END IS FINE EVEN IF ELSE BLOCK CONTAINS OTHER CONDITIONS. + } + +/* - */ } + +// -Z unpretty=val -- present the input source, unstable (and less-pretty) variants; +// valid types are any of the types for `--pretty`, as well as: +// `expanded`, `expanded,identified`, +// `expanded,hygiene` (with internal representations), +// `everybody_loops` (all function bodies replaced with `loop {}`), +// `hir` (the HIR), `hir,identified`, +// `hir,typed` (HIR with types for each node), +// `hir-tree` (dump the raw HIR), +// `mir` (the MIR), or `mir-cfg` (graphviz formatted MIR) + +// argument to `pretty` must be one of `normal`, `expanded`, `identified`, or `expanded,identified` diff --git a/src/test/codegen/coverage-experiments/src/increment_intrinsic.rs b/src/test/codegen/coverage-experiments/src/increment_intrinsic.rs new file mode 100644 index 000000000000..d4708cd367ff --- /dev/null +++ b/src/test/codegen/coverage-experiments/src/increment_intrinsic.rs @@ -0,0 +1,11 @@ +#![feature(core_intrinsics)] + +pub fn not_instrprof_increment(_hash: u64, _num_counters: u32, _index: u32) { +} + +fn main() { + // COMPARE THIS WITH INTRINSIC INSERTION + //not_instrprof_increment(1234 as u64, 314 as u32, 31 as u32); + + unsafe { core::intrinsics::instrprof_increment(1234 as u64, 314 as u32, 31 as u32) }; +} \ No newline at end of file diff --git a/src/test/codegen/coverage-experiments/src/just_main.rs b/src/test/codegen/coverage-experiments/src/just_main.rs new file mode 100644 index 000000000000..081e5d72a6e0 --- /dev/null +++ b/src/test/codegen/coverage-experiments/src/just_main.rs @@ -0,0 +1,3 @@ +fn main() { + println!("hello world! (should be covered)"); +} diff --git a/src/test/codegen/coverage-experiments/src/lazy_boolean.rs b/src/test/codegen/coverage-experiments/src/lazy_boolean.rs new file mode 100644 index 000000000000..263277c7cdc4 --- /dev/null +++ b/src/test/codegen/coverage-experiments/src/lazy_boolean.rs @@ -0,0 +1,17 @@ +pub fn __llvm_incr_counter(_region_loc: &str) { +} + +#[inline(always)] +pub fn __incr_cov(region_loc: &str, result: T) -> T { + __llvm_incr_counter(region_loc); + result +} + +fn main() { + let a = 1; + let b = 10; + let c = 100; + let _result = __incr_cov("start", a < b) || __incr_cov("or", b < c); + + let _result = { let _t = a < b; __llvm_incr_counter("start"); _t } || { let _t = b < c; __llvm_incr_counter("start"); _t }; +} \ No newline at end of file diff --git a/src/test/codegen/coverage-experiments/src/loop_break_value.rs b/src/test/codegen/coverage-experiments/src/loop_break_value.rs new file mode 100644 index 000000000000..76caa833ec4f --- /dev/null +++ b/src/test/codegen/coverage-experiments/src/loop_break_value.rs @@ -0,0 +1,15 @@ +pub fn __llvm_incr_counter(_region_loc: &str) { +} + +#[inline(always)] +pub fn __incr_cov(region_loc: &str, result: T) -> T { + __llvm_incr_counter(region_loc); + result +} + +fn main() { + __incr_cov("start", ()); + let _result = loop { + break __incr_cov("top of loop", true); + }; +} \ No newline at end of file diff --git a/src/test/codegen/coverage-experiments/src/match.rs b/src/test/codegen/coverage-experiments/src/match.rs new file mode 100644 index 000000000000..afbb20888eab --- /dev/null +++ b/src/test/codegen/coverage-experiments/src/match.rs @@ -0,0 +1,22 @@ +pub fn __llvm_incr_counter(_region_loc: &str) { +} + +#[inline(always)] +pub fn __incr_cov(region_loc: &str, result: T) -> T { + __llvm_incr_counter(region_loc); + result +} + +fn main() { + let a = 1; + let b = 10; + let _result = match a < b { + true => true, + _ => false, + }; + + let _result = match __incr_cov("end of first match", a < b) { + true => __incr_cov("matched true", true), + _ => false, // counter expression "end of first match" - "matched true" + }; +} \ No newline at end of file diff --git a/src/test/codegen/coverage-experiments/src/match_with_increment.rs b/src/test/codegen/coverage-experiments/src/match_with_increment.rs new file mode 100644 index 000000000000..f618b37ed524 --- /dev/null +++ b/src/test/codegen/coverage-experiments/src/match_with_increment.rs @@ -0,0 +1,305 @@ +#![feature(core_intrinsics)] +//static TEST_FUNC_NAME: &'static [u8; 7] = b"main()\0"; + static TEST_FUNC_NAME: &'static [u8; 6] = b"main()"; +fn main() { + let a = 1; + let b = 10; + let _result = match { + let _t = a < b; + unsafe { core::intrinsics::instrprof_increment(TEST_FUNC_NAME as *const u8, 1234 as u64, 3 as u32, 0 as u32) }; + _t + } { + true => { + let _t = true; + unsafe { core::intrinsics::instrprof_increment(TEST_FUNC_NAME as *const u8, 1234 as u64, 3 as u32, 1 as u32) }; + _t + } + _ => false, + }; +} + +/* + +I NEED TO INSERT THE instrprof_increment() CALL: + + 1. JUST BEFORE THE switchInt(_4) (because we haven't counted entering the function main() yet, deferring that to "JUST BEFORE FIRST BRANCH") + 2. SOME TIME AFTER THE switchInt(_4), AND JUST BEFORE ANOTHER BRANCH (in this case, before "goto") + 2.a. NOT BEFORE BOTH GOTO'S AFTER switchInt(_4) (because one can be calculated by counter expression), BUT PERHAPS INSERT A noop PLACEHOLDER + AS A MARKER TO INCLUDE THE COVERAGE REGION AND REFERENCE THE COUNTERS TO BE SUBTRACTED (AND/OR SUMMED)? + + WHY DEFER INSERTING COUNTERS TO "JUST BEFORE FIRST BRANCH"? We can ignore panic/unwind() and only count if the coverage region ACTUALLY + executed in entirety. BUT IS THAT NECESSARY? IS IT MUCH EASIER TO INSERT COUNTERS AT THE TOP OF A REGION THAT MUST EXECUTE IN ENTIRETY IF + PANIC DOES NOT OCCUR? AND WHAT IF WE ADD SUPPORT FOR PANIC UNWIND (later)? + + IS THERE A BENEFIT OF THE DEFERRED APPROACH WHEN CONSIDERING EXPRESSIONS MAY HAVE EARLY RETURNS? (BECAUSE, WE STILL NEED TO COUNT THE REGION + LEADING UP TO THE EXPRESSION ANYWAY) + +================================================= +================================================= + +To inject an intrinsic after computing a final expression value of a coverage region: + +Replace the following basic block end (last statement plus terminator): + +... ... +StorageLive(_4) +StorageLive(_5) +_5 = _1 +StorageLive(_6) +_6 = _2 +_4 = Lt(move _5, move _6) +StorageDead(_6) +StorageDead(_5) + <------ to insert instrprof_increment() here +FakeRead(ForMatchedPlace, _4) +-------------------------------------------------------------------------------------- +switchInt(_4) + + +================================================= +Insert call to intrinsic with: + +StorageLive(_4) # _4 is now meant for deferred FakeRead(ForMatchdPlace, _4) in BasicBlock after increment() call +StorageLive(_5) # Unchanged except _4 is now _5 +StorageLive(_6) # Unchanged except _5 is now _6 +_6 = _1 # Unchanged except _5 is now _6 +StorageLive(_7) # Unchanged except _6 is now _7 +_7 = _2 # Unchanged except _6 is now _7 +_5 = Lt(move _6, move _7) # Unchanged except _4, _5, _6 is now _5, _6, _7 +StorageDead(_7) # Unchanged except _6 is now _7 +StorageDead(_6) # Unchanged except _5 is now _6 + +FakeRead(ForLet, _5) # CHANGED ForMatchedPlace to ForLet + +> # ALL NEW AND NECESSARY TO CALL instrprof_increment() +> StorageLive(_8) # ?? stores function pointer to instrprof_increment function? +> StorageLive(_9) +> StorageLive(_10) +> StorageLive(_11) +> _11 = const {alloc1+0: &&[u8; 6]} +> _10 = &raw const (*(*_11)) +> _9 = move _10 as *const u8 (Pointer(ArrayToPointer)) +> StorageDead(_10) +> StorageLive(_12) +> _12 = const 1234u64 +> StorageLive(_13) +> _13 = const 3u32 +> StorageLive(_14) +> _14 = const 0u32 +> -------------------------------------------------------------------------------------- +> _8 = const std::intrinsics::instrprof_increment(move _9, move _12, move _13, move _14) +> +> -> return +> +> StorageDead(_14) +> StorageDead(_13) +> StorageDead(_12) +> StorageDead(_9) +> StorageDead(_11) +> StorageDead(_8) + +_4 = _5 # ARE THESE LINES REDUNDANT? CAN I JUST PASS _5 DIRECTLY TO FakeRead()? +StorageDead(_5) # DROP "_t" temp result of `let _t = a < b` + # (NOTE THAT IF SO, I CAN REMOVE _5 altogether, and use _4, which coincidentally makes less changes) + # SEE BELOW + +FakeRead(ForMatchedPlace, _4) # Unchanged +-------------------------------------------------------------------------------------- +switchInt(_4) # Unchanged + + +================================================= +Can I skip the extra variable and insert call to intrinsic with: + +StorageLive(_4) # Unchanged +StorageLive(_5) # Unchanged +_5 = _1 # Unchanged +StorageLive(_6) # Unchanged +_6 = _2 # Unchanged +_4 = Lt(move _5, move _6) # Unchanged +StorageDead(_6) # Unchanged +StorageDead(_5) # Unchanged + +> # ALL NEW AND NECESSARY TO CALL instrprof_increment() +> FakeRead(ForLet, _4) # Save the post-increment result in temp "_t" +> StorageLive(_8) # ?? stores function pointer to instrprof_increment function? +> StorageLive(_9) +> StorageLive(_10) +> StorageLive(_11) +> _11 = const {alloc1+0: &&[u8; 6]} +> _10 = &raw const (*(*_11)) +> _9 = move _10 as *const u8 (Pointer(ArrayToPointer)) +> StorageDead(_10) +> StorageLive(_12) +> _12 = const 1234u64 +> StorageLive(_13) +> _13 = const 3u32 +> StorageLive(_14) +> _14 = const 0u32 +> -------------------------------------------------------------------------------------- +> _8 = const std::intrinsics::instrprof_increment(move _9, move _12, move _13, move _14) +> +> -> return +> +> StorageDead(_14) +> StorageDead(_13) +> StorageDead(_12) +> StorageDead(_9) +> StorageDead(_11) +> StorageDead(_8) + +FakeRead(ForMatchedPlace, _4) # Unchanged (PREVIOUSLY USED IN FakeRead(ForLet), is that OK?) +-------------------------------------------------------------------------------------- +switchInt(_4) # Unchanged + + + + + +================================================= +================================================= + +For the second inserted call to instrprof_increment, without that call we have: + +-------------------------------------------------------------------------------------- +switchInt(_4) # From above + +-> otherwise # that is, "NOT false" + +_3 = const true + <------ to insert instrprof_increment() here +-------------------------------------------------------------------------------------- +goto + +-> # No label. No condition, and not a "return" + +FakeRead(ForLet, _3) # NOTE: Unused result +StorageDead(_4) +_0 = () +StorageDead(_3) +StorageDead(_2) +StorageDead(_1) +-------------------------------------------------------------------------------------- +goto + +-> # No label. No condition, and not a "return" + +return # from main() + + +================================================= +With the call to increment(): + +-------------------------------------------------------------------------------------- +switchInt(_4) # From above + +-> otherwise # "NOT false" # UNCHANGED + +StorageLive(_15) # CHANGED! Allocated new storage (_15) for the result of match, if true. +_15 = const true # UNCHANGED except _3 is now _15 +FakeRead(ForLet, _15) # CHANGED! Assign value to temporary (to be assigned to _3 later) ... Do I need to do this? + +> # ALL NEW AND NECESSARY TO CALL instrprof_increment() +> StorageLive(_16) # pointer to instrprof_increment() function ? +> StorageLive(_17) +> StorageLive(_18) +> StorageLive(_19) +> _19 = const {alloc1+0: &&[u8; 6]} +> _18 = &raw const (*(*_19)) +> _17 = move _18 as *const u8 (Pointer(ArrayToPointer)) +> StorageDead(_18) +> StorageLive(_20) +> _20 = const 1234u64 +> StorageLive(_21) +> _21 = const 3u32 +> StorageLive(_22) +> _22 = const 1u32 +> -------------------------------------------------------------------------------------- +> _16 = const std::intrinsics::instrprof_increment(move _17, move _20, move _21, move _22) +> +> -> return +> +> StorageDead(_22) +> StorageDead(_21) +> StorageDead(_20) +> StorageDead(_17) +> StorageDead(_19) +> StorageDead(_16) +> _3 = _15 +> StorageDead(_15) + +--------------------------------# UNCHANGED------------------------------------------- +goto # UNCHANGED + +-> # UNCHANGED + +FakeRead(ForLet, _3) # UNCHANGED +StorageDead(_4) # UNCHANGED +_0 = () # UNCHANGED +StorageDead(_3) # UNCHANGED +StorageDead(_2) # UNCHANGED +StorageDead(_1) # UNCHANGED +-------------------------------------------------------------------------------------- +goto # UNCHANGED + +-> # UNCHANGED + +return # from main() # UNCHANGED + +================================================= +As before, can I skip the extra variable (_15) and insert the call to intrinsic with _3 directly?: + + +-------------------------------------------------------------------------------------- +switchInt(_4) # From above + +-> otherwise # "NOT false" # UNCHANGED + +_3 = const true # UNCHANGED? + +> # ALL NEW AND NECESSARY TO CALL instrprof_increment() +> StorageLive(_16) # pointer to instrprof_increment() function ? +> StorageLive(_17) +> StorageLive(_18) +> StorageLive(_19) +> _19 = const {alloc1+0: &&[u8; 6]} +> _18 = &raw const (*(*_19)) +> _17 = move _18 as *const u8 (Pointer(ArrayToPointer)) +> StorageDead(_18) +> StorageLive(_20) +> _20 = const 1234u64 +> StorageLive(_21) +> _21 = const 3u32 +> StorageLive(_22) +> _22 = const 1u32 +> -------------------------------------------------------------------------------------- +> _16 = const std::intrinsics::instrprof_increment(move _17, move _20, move _21, move _22) +> +> -> return +> +> StorageDead(_22) +> StorageDead(_21) +> StorageDead(_20) +> StorageDead(_17) +> StorageDead(_19) +> StorageDead(_16) + +--------------------------------# UNCHANGED------------------------------------------- +goto # UNCHANGED + +-> # UNCHANGED + +FakeRead(ForLet, _3) # UNCHANGED +StorageDead(_4) # UNCHANGED +_0 = () # UNCHANGED +StorageDead(_3) # UNCHANGED +StorageDead(_2) # UNCHANGED +StorageDead(_1) # UNCHANGED +-------------------------------------------------------------------------------------- +goto # UNCHANGED + +-> # UNCHANGED + +return # from main() # UNCHANGED + +*/ \ No newline at end of file diff --git a/src/test/codegen/coverage-experiments/src/match_with_increment_alt.rs b/src/test/codegen/coverage-experiments/src/match_with_increment_alt.rs new file mode 100644 index 000000000000..60586967920c --- /dev/null +++ b/src/test/codegen/coverage-experiments/src/match_with_increment_alt.rs @@ -0,0 +1,296 @@ +#![feature(core_intrinsics)] +//static TEST_FUNC_NAME: &'static [u8; 7] = b"main()\0"; + static TEST_FUNC_NAME: &'static [u8; 6] = b"main()"; +fn main() { + unsafe { core::intrinsics::instrprof_increment(TEST_FUNC_NAME as *const u8, 1234 as u64, 3 as u32, 0 as u32) }; + let a = 1; + let b = 10; + let _result = match a < b { + true => { + unsafe { core::intrinsics::instrprof_increment(TEST_FUNC_NAME as *const u8, 1234 as u64, 3 as u32, 1 as u32) }; + true + } + _ => false, + }; +} + +/* + +ALTERNATE APPROACH: + + IS IT MUCH EASIER TO INSERT COUNTERS AT THE TOP OF A REGION THAT MUST EXECUTE IN ENTIRETY IF + PANIC DOES NOT OCCUR? AND WHAT IF WE ADD SUPPORT FOR PANIC UNWIND (later)? + + IS THERE A DETRACTOR COMPARED TO THE DEFERRED APPROACH WHEN CONSIDERING EXPRESSIONS MAY HAVE EARLY RETURNS? + + (BECAUSE, WE STILL NEED TO COUNT THE REGION LEADING UP TO THE EXPRESSION ANYWAY) + +================================================= +================================================= + +To inject an intrinsic after computing a final expression value of a coverage region: + +Replace the following basic block end (last statement plus terminator): + +... ... +StorageLive(_4) +StorageLive(_5) +_5 = _1 +StorageLive(_6) +_6 = _2 +_4 = Lt(move _5, move _6) +StorageDead(_6) +StorageDead(_5) + <------ to insert instrprof_increment() here +FakeRead(ForMatchedPlace, _4) +-------------------------------------------------------------------------------------- +switchInt(_4) + + +================================================= +Insert call to intrinsic with: + +StorageLive(_4) # _4 is now meant for deferred FakeRead(ForMatchdPlace, _4) in BasicBlock after increment() call +StorageLive(_5) # Unchanged except _4 is now _5 +StorageLive(_6) # Unchanged except _5 is now _6 +_6 = _1 # Unchanged except _5 is now _6 +StorageLive(_7) # Unchanged except _6 is now _7 +_7 = _2 # Unchanged except _6 is now _7 +_5 = Lt(move _6, move _7) # Unchanged except _4, _5, _6 is now _5, _6, _7 +StorageDead(_7) # Unchanged except _6 is now _7 +StorageDead(_6) # Unchanged except _5 is now _6 + +FakeRead(ForLet, _5) # CHANGED ForMatchedPlace to ForLet + +> # ALL NEW AND NECESSARY TO CALL instrprof_increment() +> StorageLive(_8) # ?? stores function pointer to instrprof_increment function? +> StorageLive(_9) +> StorageLive(_10) +> StorageLive(_11) +> _11 = const {alloc1+0: &&[u8; 6]} +> _10 = &raw const (*(*_11)) +> _9 = move _10 as *const u8 (Pointer(ArrayToPointer)) +> StorageDead(_10) +> StorageLive(_12) +> _12 = const 1234u64 +> StorageLive(_13) +> _13 = const 3u32 +> StorageLive(_14) +> _14 = const 0u32 +> -------------------------------------------------------------------------------------- +> _8 = const std::intrinsics::instrprof_increment(move _9, move _12, move _13, move _14) +> +> -> return +> +> StorageDead(_14) +> StorageDead(_13) +> StorageDead(_12) +> StorageDead(_9) +> StorageDead(_11) +> StorageDead(_8) + +_4 = _5 # ARE THESE LINES REDUNDANT? CAN I JUST PASS _5 DIRECTLY TO FakeRead()? +StorageDead(_5) # DROP "_t" temp result of `let _t = a < b` + # (NOTE THAT IF SO, I CAN REMOVE _5 altogether, and use _4, which coincidentally makes less changes) + # SEE BELOW + +FakeRead(ForMatchedPlace, _4) # Unchanged +-------------------------------------------------------------------------------------- +switchInt(_4) # Unchanged + + +================================================= +Can I skip the extra variable and insert call to intrinsic with: + +StorageLive(_4) # Unchanged +StorageLive(_5) # Unchanged +_5 = _1 # Unchanged +StorageLive(_6) # Unchanged +_6 = _2 # Unchanged +_4 = Lt(move _5, move _6) # Unchanged +StorageDead(_6) # Unchanged +StorageDead(_5) # Unchanged + +> # ALL NEW AND NECESSARY TO CALL instrprof_increment() +> FakeRead(ForLet, _4) # Save the post-increment result in temp "_t" +> StorageLive(_8) # ?? stores function pointer to instrprof_increment function? +> StorageLive(_9) +> StorageLive(_10) +> StorageLive(_11) +> _11 = const {alloc1+0: &&[u8; 6]} +> _10 = &raw const (*(*_11)) +> _9 = move _10 as *const u8 (Pointer(ArrayToPointer)) +> StorageDead(_10) +> StorageLive(_12) +> _12 = const 1234u64 +> StorageLive(_13) +> _13 = const 3u32 +> StorageLive(_14) +> _14 = const 0u32 +> -------------------------------------------------------------------------------------- +> _8 = const std::intrinsics::instrprof_increment(move _9, move _12, move _13, move _14) +> +> -> return +> +> StorageDead(_14) +> StorageDead(_13) +> StorageDead(_12) +> StorageDead(_9) +> StorageDead(_11) +> StorageDead(_8) + +FakeRead(ForMatchedPlace, _4) # Unchanged (PREVIOUSLY USED IN FakeRead(ForLet), is that OK?) +-------------------------------------------------------------------------------------- +switchInt(_4) # Unchanged + + + + + +================================================= +================================================= + +For the second inserted call to instrprof_increment, without that call we have: + +-------------------------------------------------------------------------------------- +switchInt(_4) # From above + +-> otherwise # that is, "NOT false" + +_3 = const true + <------ to insert instrprof_increment() here +-------------------------------------------------------------------------------------- +goto + +-> # No label. No condition, and not a "return" + +FakeRead(ForLet, _3) # NOTE: Unused result +StorageDead(_4) +_0 = () +StorageDead(_3) +StorageDead(_2) +StorageDead(_1) +-------------------------------------------------------------------------------------- +goto + +-> # No label. No condition, and not a "return" + +return # from main() + + +================================================= +With the call to increment(): + +-------------------------------------------------------------------------------------- +switchInt(_4) # From above + +-> otherwise # "NOT false" # UNCHANGED + +StorageLive(_15) # CHANGED! Allocated new storage (_15) for the result of match, if true. +_15 = const true # UNCHANGED except _3 is now _15 +FakeRead(ForLet, _15) # CHANGED! Assign value to temporary (to be assigned to _3 later) ... Do I need to do this? + +> # ALL NEW AND NECESSARY TO CALL instrprof_increment() +> StorageLive(_16) # pointer to instrprof_increment() function ? +> StorageLive(_17) +> StorageLive(_18) +> StorageLive(_19) +> _19 = const {alloc1+0: &&[u8; 6]} +> _18 = &raw const (*(*_19)) +> _17 = move _18 as *const u8 (Pointer(ArrayToPointer)) +> StorageDead(_18) +> StorageLive(_20) +> _20 = const 1234u64 +> StorageLive(_21) +> _21 = const 3u32 +> StorageLive(_22) +> _22 = const 1u32 +> -------------------------------------------------------------------------------------- +> _16 = const std::intrinsics::instrprof_increment(move _17, move _20, move _21, move _22) +> +> -> return +> +> StorageDead(_22) +> StorageDead(_21) +> StorageDead(_20) +> StorageDead(_17) +> StorageDead(_19) +> StorageDead(_16) +> _3 = _15 +> StorageDead(_15) + +--------------------------------# UNCHANGED------------------------------------------- +goto # UNCHANGED + +-> # UNCHANGED + +FakeRead(ForLet, _3) # UNCHANGED +StorageDead(_4) # UNCHANGED +_0 = () # UNCHANGED +StorageDead(_3) # UNCHANGED +StorageDead(_2) # UNCHANGED +StorageDead(_1) # UNCHANGED +-------------------------------------------------------------------------------------- +goto # UNCHANGED + +-> # UNCHANGED + +return # from main() # UNCHANGED + +================================================= +As before, can I skip the extra variable (_15) and insert the call to intrinsic with _3 directly?: + + +-------------------------------------------------------------------------------------- +switchInt(_4) # From above + +-> otherwise # "NOT false" # UNCHANGED + +_3 = const true # UNCHANGED? + +> # ALL NEW AND NECESSARY TO CALL instrprof_increment() +> StorageLive(_16) # pointer to instrprof_increment() function ? +> StorageLive(_17) +> StorageLive(_18) +> StorageLive(_19) +> _19 = const {alloc1+0: &&[u8; 6]} +> _18 = &raw const (*(*_19)) +> _17 = move _18 as *const u8 (Pointer(ArrayToPointer)) +> StorageDead(_18) +> StorageLive(_20) +> _20 = const 1234u64 +> StorageLive(_21) +> _21 = const 3u32 +> StorageLive(_22) +> _22 = const 1u32 +> -------------------------------------------------------------------------------------- +> _16 = const std::intrinsics::instrprof_increment(move _17, move _20, move _21, move _22) +> +> -> return +> +> StorageDead(_22) +> StorageDead(_21) +> StorageDead(_20) +> StorageDead(_17) +> StorageDead(_19) +> StorageDead(_16) + +--------------------------------# UNCHANGED------------------------------------------- +goto # UNCHANGED + +-> # UNCHANGED + +FakeRead(ForLet, _3) # UNCHANGED +StorageDead(_4) # UNCHANGED +_0 = () # UNCHANGED +StorageDead(_3) # UNCHANGED +StorageDead(_2) # UNCHANGED +StorageDead(_1) # UNCHANGED +-------------------------------------------------------------------------------------- +goto # UNCHANGED + +-> # UNCHANGED + +return # from main() # UNCHANGED + +*/ \ No newline at end of file diff --git a/src/test/codegen/coverage-experiments/src/match_without_increment.mir b/src/test/codegen/coverage-experiments/src/match_without_increment.mir new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/src/test/codegen/coverage-experiments/src/match_without_increment.rs b/src/test/codegen/coverage-experiments/src/match_without_increment.rs new file mode 100644 index 000000000000..fa85833e0543 --- /dev/null +++ b/src/test/codegen/coverage-experiments/src/match_without_increment.rs @@ -0,0 +1,5 @@ +fn main() { + let a = 1; + let b = 10; + let _result = match a < b { true => true, _ => false, }; +} \ No newline at end of file diff --git a/src/test/codegen/coverage-experiments/src/match_without_increment_alt.mir b/src/test/codegen/coverage-experiments/src/match_without_increment_alt.mir new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/src/test/codegen/coverage-experiments/src/question_mark_err_status_handling_with_comments.rs b/src/test/codegen/coverage-experiments/src/question_mark_err_status_handling_with_comments.rs new file mode 100644 index 000000000000..03d11b2c230c --- /dev/null +++ b/src/test/codegen/coverage-experiments/src/question_mark_err_status_handling_with_comments.rs @@ -0,0 +1,24 @@ +/* */ #[inline(always)] +/* */ pub fn __incr_cov(_region_loc: &str, /*index: u32,*/ result: T) -> T { +/* */ result +/* */ } +/* */ +/* - */ fn main() { +/* : I */ for countdown in __incr_cov("start", 10..0) { // span is just the while test expression +/* : ┃ */ let _ = countdown; +/* : ┃ */ __incr_cov("top of for", ()); +/* ┃ - */ } +/* - */ } + + +// -Z unpretty=val -- present the input source, unstable (and less-pretty) variants; +// valid types are any of the types for `--pretty`, as well as: +// `expanded`, `expanded,identified`, +// `expanded,hygiene` (with internal representations), +// `everybody_loops` (all function bodies replaced with `loop {}`), +// `hir` (the HIR), `hir,identified`, +// `hir,typed` (HIR with types for each node), +// `hir-tree` (dump the raw HIR), +// `mir` (the MIR), or `mir-cfg` (graphviz formatted MIR) + +// argument to `pretty` must be one of `normal`, `expanded`, `identified`, or `expanded,identified` diff --git a/src/test/codegen/coverage-experiments/src/while.rs b/src/test/codegen/coverage-experiments/src/while.rs new file mode 100644 index 000000000000..3cb185eda544 --- /dev/null +++ b/src/test/codegen/coverage-experiments/src/while.rs @@ -0,0 +1,23 @@ +#[inline(always)] +pub fn __incr_cov(_region_loc: &str, result: T) -> T { + result +} + +fn main() { + let mut countdown = 10; + __incr_cov("block start",()); + while __incr_cov("while test", countdown > 0) { + countdown -= 1; + } + + let mut countdown = 10; + __incr_cov("after first while loop",()); + while __incr_cov("while test", countdown > 0) { + countdown -= 1; + if countdown < 5 { + __incr_cov("top of if countdown < 5",()); + break; + } + countdown -= 2; + } +} \ No newline at end of file diff --git a/src/test/codegen/coverage-experiments/src/while_clean.rs b/src/test/codegen/coverage-experiments/src/while_clean.rs new file mode 100644 index 000000000000..e9ed1efc220d --- /dev/null +++ b/src/test/codegen/coverage-experiments/src/while_clean.rs @@ -0,0 +1,6 @@ +fn main() { + let mut countdown = 10; + while countdown > 0 { + countdown -= 1; + } +} \ No newline at end of file diff --git a/src/test/codegen/coverage-experiments/src/while_early_return.rs b/src/test/codegen/coverage-experiments/src/while_early_return.rs new file mode 100644 index 000000000000..35709ffba3a0 --- /dev/null +++ b/src/test/codegen/coverage-experiments/src/while_early_return.rs @@ -0,0 +1,10 @@ +fn main() -> u8 { // this will lower to HIR but will not compile: `main` can only return types that implement `std::process::Termination` + let mut countdown = 10; + while countdown > 0 { + if false { + return if countdown > 8 { 1 } else { return 2; }; + } + countdown -= 1; + } + 0 +} \ No newline at end of file diff --git a/src/test/codegen/coverage-experiments/src/while_with_comments.rs b/src/test/codegen/coverage-experiments/src/while_with_comments.rs new file mode 100644 index 000000000000..56417fedf00d --- /dev/null +++ b/src/test/codegen/coverage-experiments/src/while_with_comments.rs @@ -0,0 +1,51 @@ +/* */ #[inline(always)] +/* */ pub fn __incr_cov(_region_loc: &str, /*index: u32,*/ result: T) -> T { +/* */ result +/* */ } +/* */ +/* - */ fn main() { +/* ┃ */ let mut countdown = 10; +/* ┃ */ __incr_cov("block start",()); // Must increment before repeated while text expression +/* : I */ while __incr_cov("while test", countdown > 0) { // span is just the while test expression +/* : ┃ */ countdown -= 1; +/* : ┃ */ // __incr_cov("while loop",()); // Counter not needed, but span is computed as "while test" minus "block start" +/* : ┃ */ // If while criteria is tested 11 times, and the outer block runs only once, 11-1 = 10 +/* : ┃ */ // REMOVING COUNTER ASSUMES NO EARLY RETURN THOUGH. +/* : ┃ */ // I THINK WE CAN ONLY USE THE COUNTER EXPRESSION UP TO FIRST CONDITIONAL BLOCK, IF ANY (if, match, maybe any loop) +/* ┃ - */ } + + let mut countdown = 10; + __incr_cov("after first while loop",()); + while __incr_cov("while test", countdown > 0) { + countdown -= 1; + // if __incr_cov("top of while loop", countdown < 5) { + if countdown < 5 { // "top of while loop" = counter expression "while test" - "after first while loop" + __incr_cov("top of if countdown < 5",()); + break; + } + countdown -= 2; + // __incr_cov("after if countdown < 5 block", ()); + // "after if countdown < 5 block" = counter expression "top of while loop" - "top of if countdown < 5" + // HOWEVER, WE CAN ONLY REMOVE THE COUNTER AND USE COUNTER EXPRESSION IF WE **KNOW** THAT THE BODY OF THE IF + // WILL **ALWAYS** BREAK (OR RETURN, OR CONTINUE?) + // AND THUS WE TREAT THE STATEMENTS FOLLOWING THE IF BLOCK AS IF THEY WERE AN ELSE BLOCK. + // THAT'S A LOT TO ASK. + + // PERHAPS TREAT EARLY RETURNS AS A SPECIAL KIND OF COUNTER AND IF ANY ARE INVOKED BEFORE STATEMENTS AFTER THE BLOCK THAT CONTAINS THEM, + // THEN SUBTRACT THOSE COUNTS FROM THE COUNT BEFORE THE BLOCK (AS WE DO HERE)? (SO ONE SET OF EXPRESSIONS MUST SUM ALL OF THE EARLY + // RETURNS) + } +/* - */ } + + +// -Z unpretty=val -- present the input source, unstable (and less-pretty) variants; +// valid types are any of the types for `--pretty`, as well as: +// `expanded`, `expanded,identified`, +// `expanded,hygiene` (with internal representations), +// `everybody_loops` (all function bodies replaced with `loop {}`), +// `hir` (the HIR), `hir,identified`, +// `hir,typed` (HIR with types for each node), +// `hir-tree` (dump the raw HIR), +// `mir` (the MIR), or `mir-cfg` (graphviz formatted MIR) + +// argument to `pretty` must be one of `normal`, `expanded`, `identified`, or `expanded,identified` From 088037a04414dee0b3a792a6fad193e081ee8e37 Mon Sep 17 00:00:00 2001 From: Rich Kadel Date: Thu, 4 Jun 2020 17:47:21 -0700 Subject: [PATCH 071/123] explained lang_item function body (count_code_region) --- src/libcore/intrinsics.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/libcore/intrinsics.rs b/src/libcore/intrinsics.rs index abb35e838ea2..0e5af35229ca 100644 --- a/src/libcore/intrinsics.rs +++ b/src/libcore/intrinsics.rs @@ -1943,6 +1943,12 @@ extern "rust-intrinsic" { pub fn miri_start_panic(payload: *mut u8) -> !; } +// Since `count_code_region` is lang_item, it must have a function body that the compiler can use +// to register its DefId with the lang_item entry. This function body is never actually called +// (and is therefore implemented as an aborting stub) because it is replaced with the +// LLVM intrinsic `llvm.instrprof.increment` by +// `rustc_codegen_llvm::intrinsic::IntrinsicCallMethods::codegen_intrinsic_call()`. +#[doc(hidden)] #[cfg(not(bootstrap))] #[cfg_attr(not(bootstrap), lang = "count_code_region")] pub fn count_code_region(_index: u32) { From 2c5c2a6bc2f7023ee8ad252d2ee5a45fbfb2de22 Mon Sep 17 00:00:00 2001 From: Rich Kadel Date: Thu, 4 Jun 2020 17:52:27 -0700 Subject: [PATCH 072/123] removed experiments for cleaner github PR --- src/libcore/intrinsics.rs | 17 +- .../codegen/coverage-experiments/Cargo.lock | 5 - .../codegen/coverage-experiments/Cargo.toml | 103 ----- .../README-THIS-IS-TEMPORARY.md | 157 -------- .../src/coverage_injection_test.rs | 335 ---------------- .../src/coverage_injection_test2.rs | 320 ---------------- .../src/coverage_injection_test_alt.rs | 362 ------------------ .../coverage-experiments/src/drop_trait.rs | 25 -- .../src/drop_trait_with_comments_prints.rs | 53 --- .../codegen/coverage-experiments/src/for.rs | 41 -- .../src/for_with_comments.rs | 24 -- .../codegen/coverage-experiments/src/if.rs | 80 ---- .../src/if_with_comments.rs | 39 -- .../src/increment_intrinsic.rs | 11 - .../coverage-experiments/src/just_main.rs | 3 - .../coverage-experiments/src/lazy_boolean.rs | 17 - .../src/loop_break_value.rs | 15 - .../codegen/coverage-experiments/src/match.rs | 22 -- .../src/match_with_increment.rs | 305 --------------- .../src/match_with_increment_alt.rs | 296 -------------- .../src/match_without_increment.mir | 0 .../src/match_without_increment.rs | 5 - .../src/match_without_increment_alt.mir | 0 ..._mark_err_status_handling_with_comments.rs | 24 -- .../codegen/coverage-experiments/src/while.rs | 23 -- .../coverage-experiments/src/while_clean.rs | 6 - .../src/while_early_return.rs | 10 - .../src/while_with_comments.rs | 51 --- 28 files changed, 9 insertions(+), 2340 deletions(-) delete mode 100644 src/test/codegen/coverage-experiments/Cargo.lock delete mode 100644 src/test/codegen/coverage-experiments/Cargo.toml delete mode 100644 src/test/codegen/coverage-experiments/README-THIS-IS-TEMPORARY.md delete mode 100644 src/test/codegen/coverage-experiments/src/coverage_injection_test.rs delete mode 100644 src/test/codegen/coverage-experiments/src/coverage_injection_test2.rs delete mode 100644 src/test/codegen/coverage-experiments/src/coverage_injection_test_alt.rs delete mode 100644 src/test/codegen/coverage-experiments/src/drop_trait.rs delete mode 100644 src/test/codegen/coverage-experiments/src/drop_trait_with_comments_prints.rs delete mode 100644 src/test/codegen/coverage-experiments/src/for.rs delete mode 100644 src/test/codegen/coverage-experiments/src/for_with_comments.rs delete mode 100644 src/test/codegen/coverage-experiments/src/if.rs delete mode 100644 src/test/codegen/coverage-experiments/src/if_with_comments.rs delete mode 100644 src/test/codegen/coverage-experiments/src/increment_intrinsic.rs delete mode 100644 src/test/codegen/coverage-experiments/src/just_main.rs delete mode 100644 src/test/codegen/coverage-experiments/src/lazy_boolean.rs delete mode 100644 src/test/codegen/coverage-experiments/src/loop_break_value.rs delete mode 100644 src/test/codegen/coverage-experiments/src/match.rs delete mode 100644 src/test/codegen/coverage-experiments/src/match_with_increment.rs delete mode 100644 src/test/codegen/coverage-experiments/src/match_with_increment_alt.rs delete mode 100644 src/test/codegen/coverage-experiments/src/match_without_increment.mir delete mode 100644 src/test/codegen/coverage-experiments/src/match_without_increment.rs delete mode 100644 src/test/codegen/coverage-experiments/src/match_without_increment_alt.mir delete mode 100644 src/test/codegen/coverage-experiments/src/question_mark_err_status_handling_with_comments.rs delete mode 100644 src/test/codegen/coverage-experiments/src/while.rs delete mode 100644 src/test/codegen/coverage-experiments/src/while_clean.rs delete mode 100644 src/test/codegen/coverage-experiments/src/while_early_return.rs delete mode 100644 src/test/codegen/coverage-experiments/src/while_with_comments.rs diff --git a/src/libcore/intrinsics.rs b/src/libcore/intrinsics.rs index 0e5af35229ca..06a432a26961 100644 --- a/src/libcore/intrinsics.rs +++ b/src/libcore/intrinsics.rs @@ -1943,16 +1943,17 @@ extern "rust-intrinsic" { pub fn miri_start_panic(payload: *mut u8) -> !; } -// Since `count_code_region` is lang_item, it must have a function body that the compiler can use -// to register its DefId with the lang_item entry. This function body is never actually called -// (and is therefore implemented as an aborting stub) because it is replaced with the -// LLVM intrinsic `llvm.instrprof.increment` by -// `rustc_codegen_llvm::intrinsic::IntrinsicCallMethods::codegen_intrinsic_call()`. -#[doc(hidden)] +/// Defines the `count_code_region` intrinsic as a `LangItem`. `LangItem`s require a function body +/// to register its DefId with the LangItem entry. The function body is never actually called (and +/// is therefore implemented as an aborting stub) because it is replaced with the LLVM intrinsic +/// `llvm.instrprof.increment` by +/// `rustc_codegen_llvm::intrinsic::IntrinsicCallMethods::codegen_intrinsic_call()`. #[cfg(not(bootstrap))] #[cfg_attr(not(bootstrap), lang = "count_code_region")] -pub fn count_code_region(_index: u32) { - #[cfg_attr(not(bootstrap), allow(unused_unsafe))] // remove `unsafe` on bootstrap bump +fn count_code_region(_index: u32) { + // remove `unsafe` (and safety comment) on bootstrap bump + #[cfg_attr(not(bootstrap), allow(unused_unsafe))] + // SAFETY: the `abort` intrinsic has no requirements to be called. unsafe { abort() } diff --git a/src/test/codegen/coverage-experiments/Cargo.lock b/src/test/codegen/coverage-experiments/Cargo.lock deleted file mode 100644 index 132469cbb182..000000000000 --- a/src/test/codegen/coverage-experiments/Cargo.lock +++ /dev/null @@ -1,5 +0,0 @@ -# This file is automatically @generated by Cargo. -# It is not intended for manual editing. -[[package]] -name = "coverage_experiments" -version = "0.1.0" diff --git a/src/test/codegen/coverage-experiments/Cargo.toml b/src/test/codegen/coverage-experiments/Cargo.toml deleted file mode 100644 index 296a8d5c9af2..000000000000 --- a/src/test/codegen/coverage-experiments/Cargo.toml +++ /dev/null @@ -1,103 +0,0 @@ -[workspace] - -[package] -name = "coverage_experiments" -version = "0.1.0" -license = "BSD-3-Clause" -authors = ["rust-fuchsia@fuchsia.com"] -edition = "2018" - -[[bin]] - -name = "coverage_injection_test" -path = "src/coverage_injection_test.rs" - -[[bin]] - -name = "coverage_injection_test2" -path = "src/coverage_injection_test2.rs" - -[[bin]] - -name = "while" -path = "src/while.rs" - -[[bin]] - -name = "while_clean" -path = "src/while_clean.rs" - -[[bin]] - -name = "while_early_return" -path = "src/while_early_return.rs" - -[[bin]] - -name = "if_with_comments" -path = "src/if_with_comments.rs" - -[[bin]] - -name = "if" -path = "src/if.rs" - -[[bin]] - -name = "increment_intrinsic" -path = "src/increment_intrinsic.rs" - -[[bin]] - -name = "just_main" -path = "src/just_main.rs" - -[[bin]] - -name = "lazy_boolean" -path = "src/lazy_boolean.rs" - -[[bin]] - -name = "match" -path = "src/match.rs" - -[[bin]] - -name = "match_without_increment" -path = "src/match_without_increment.rs" # identical to -Zunpretty=hir output - -[[bin]] - -name = "match_with_increment" -path = "src/match_with_increment.rs" - -[[bin]] - -name = "match_with_increment_alt" -path = "src/match_with_increment_alt.rs" - -[[bin]] - -name = "loop_break_value" -path = "src/loop_break_value.rs" - -[[bin]] - -name = "for_with_comments" -path = "src/for_with_comments.rs" - -[[bin]] - -name = "for" -path = "src/for.rs" - -[[bin]] - -name = "drop_trait" -path = "src/drop_trait.rs" - -#[dependencies] # Should not need to manually add coverage dependencies -#version = "0.1.0" -#path = "../__builtin" # for mod __builtin::coverage - diff --git a/src/test/codegen/coverage-experiments/README-THIS-IS-TEMPORARY.md b/src/test/codegen/coverage-experiments/README-THIS-IS-TEMPORARY.md deleted file mode 100644 index 3b69c0a40659..000000000000 --- a/src/test/codegen/coverage-experiments/README-THIS-IS-TEMPORARY.md +++ /dev/null @@ -1,157 +0,0 @@ -# codegen/coverage-experiments -*

THIS DIRECTORY IS TEMPORARY

* - -This directory contains some work-in-progress (WIP) code used for experimental development and -testing of Rust Coverage feature development. - -The code in this directory will be removed, or migrated into product tests, when the Rust -Coverage feature is complete. - -[TOC] - -## Development Notes - -### config.toml - -config.toml probably requires (I should verify that intrinsic `llvm.instrprof.increment` -code generation ONLY works with this config option): - - profiler = true - -## First build - -```shell -./x.py clean -./x.py build -i --stage 1 src/libstd -``` - -## Incremental builds *IF POSSIBLE!* - -```shell -./x.py build -i --stage 1 src/libstd --keep-stage 1 -``` - -*Note: Some changes made for Rust Coverage required the full build (without `--keep-stage 1`), and in some cases, required `./x.py clean` first!. Occassionally I would get errors when building or when compiling a test program with `--Zinstrument-coverage` that work correctly only after a full clean and build.* - -## Compile a test program with LLVM coverage instrumentation - -*Note: This PR is still a work in progress. At the time of this writing, the `llvm.instrprof.increment` intrinsic is injected, and recognized by the LLVM code generation stage, but it does not appear to be included in the final binary. This is not surprising since other steps are still to be implemented, such as generating the coverage map. See the suggested additional `llvm` flags for ways to verify the `llvm` passes at least get the right intrinsic.* - -Suggested debug configuration to confirm Rust coverage features: -```shell -$ export RUSTC_LOG=rustc_codegen_llvm::intrinsic,rustc_mir::transform::instrument_coverage=debug -``` - -Ensure the new compiled `rustc` is used (the path below, relative to the `rust` code repository root, is an example only): - -```shell -$ build/x86_64-unknown-linux-gnu/stage1/bin/rustc \ - src/test/codegen/coverage-experiments/just_main.rs \ - -Zinstrument-coverage -``` - -### About the test programs in coverage-experiments/src/ - -The `coverage-experiments/src/` directory contains some sample (and very simple) Rust programs used to analyze Rust compiler output at various stages, with or without the Rust code coverage compiler option. For now, these are only used for the in-progress development and will be removed at a future date. (These are *not* formal test programs.) - -The src director may also contain some snapshots of mir output from experimentation, particularly if the saved snapshots highlight results that are important to the future development, individually or when compared with other output files. - -Be aware that some of the files and/or comments may be outdated. - -### Additional `llvm` flags (append to the `rustc` command) - -These optional flags generate additional files and/or terminal output. LLVM's `-print-before=all` should show the `instrprof.increment` intrinsic with arguments computed by the experimental Rust coverage feature code: - -```shell - --emit llvm-ir \ - -Zverify-llvm-ir \ - -Zprint-llvm-passes \ - -Csave-temps \ - -Cllvm-args=-print-before-all -``` - -### Additional flags for MIR analysis and transforms - -These optional flags generate a directory with many files representing the MIR as text (`.mir` files) and as a visual graph (`.dot` files) rendered by `graphviz`. (**Some IDEs, such as `VSCode` have `graphviz` extensions.**) - -```shell - -Zdump-mir=main \ - -Zdump-mir-graphviz -``` - -### Flags I've used but appear to be irrelvant to `-Zinstrument-coverage` after all: -```shell - # -Zprofile - # -Ccodegen-units=1 - # -Cinline-threshold=0 - # -Clink-dead-code - # -Coverflow-checks=off -``` - -## Run the test program compiled with code coverage instrumentation (maybe): - -As stated above, at the time of this writing, this work-in-progress seems to generate `llvm.instrprof.increment` intrinsic calls correctly, and are visibile in early `llvm` code generation passes, but are eventually stripped. - -The test program should run as expected, currently does not generate any coverage output. - -*Example:* - -```shell - $ src/test/codegen/coverage-experiments/just_main - hello world! (should be covered) -``` - -### Running the coverage-enabled `rustc` compiler in the `lldb` debugger: - -For example, to verify the intrinsic is codegen'ed, set breakpoint in `lldb` where it validates a certain instruction is the `llvm.instrprof.increment` instruction. - -First, update config.toml for debugging: - -```toml - [llvm] - optimize = false - release-debuginfo = true - - [rust] - debug = true - debuginfo-level = 2 -``` - -*(Note, in case this is relevant after all, I also have the following changes; but I don't think I need them:)* - -```toml - # Add and uncomment these if relevant/useful: - # codegen-units = 0 - # python = '/usr/bin/python3.6' -``` - -Run the compiler with additional flags as needed: - -```shell -lldb \ - build/x86_64-unknown-linux-gnu/stage1/bin/rustc \ - -- \ - src/test/codegen/coverage-experiments/just_main.rs \ - -Zinstrument-coverage \ - -Zdump-mir=main \ - -Zdump-mir-graphviz -``` - -Note the specific line numbers may be different: - -```c++ -(lldb) b lib/Transforms/Instrumentation/InstrProfiling.cpp:418 -(lldb) r - -Process 93855 stopped -* thread #6, name = 'rustc', stop reason = breakpoint 2.1 - frame #0: 0x00007fffedff7738 librustc_driver-5a0990d8d18fb2b4.so`llvm::InstrProfiling::lowerIntrinsics(this=0x00007fffcc001d40, F=0x00007fffe4552198) at InstrProfiling.cpp:418:23 - 415 auto Instr = I++; - 416 InstrProfIncrementInst *Inc = castToIncrementInst(&*Instr); - 417 if (Inc) { --> 418 lowerIncrement(Inc); - 419 MadeChange = true; - 420 } else if (auto *Ind = dyn_cast(Instr)) { - 421 lowerValueProfileInst(Ind); -(lldb) -``` \ No newline at end of file diff --git a/src/test/codegen/coverage-experiments/src/coverage_injection_test.rs b/src/test/codegen/coverage-experiments/src/coverage_injection_test.rs deleted file mode 100644 index 231da1dc1a67..000000000000 --- a/src/test/codegen/coverage-experiments/src/coverage_injection_test.rs +++ /dev/null @@ -1,335 +0,0 @@ -/* */ use std::io::Error; -/* */ use std::io::ErrorKind; -/* */ -/* */ /// Align Rust counter increment with with: -/* */ /// [‘llvm.instrprof.increment’ Intrinsic](https://llvm.org/docs/LangRef.html#llvm-instrprof-increment-intrinsic) -/* */ /// -/* */ /// declare void @llvm.instrprof.increment(i8* , i64 , i32 , i32 ) -/* */ /// -/* */ /// The first argument is a pointer to a global variable containing the name of the entity -/* */ /// being instrumented. This should generally be the (mangled) function name for a set of -/* */ /// counters. -/* */ /// -/* */ /// The second argument is a hash value that can be used by the consumer of the profile data -/* */ /// to detect changes to the instrumented source, and the third is the number of counters -/* */ /// associated with name. It is an error if hash or num-counters differ between two -/* */ /// instances of instrprof.increment that refer to the same name. -/* */ /// -/* */ /// The last argument refers to which of the counters for name should be incremented. It -/* */ /// should be a value between 0 and num-counters. -/* */ /// -/* */ /// # Arguments -/* */ /// -/* */ /// `mangled_fn_name` - &'static ref to computed and injected static str, using: -/* */ /// -/* */ /// ``` -/* */ /// fn rustc_symbol_mangling::compute_symbol_name( -/* */ /// tcx: TyCtxt<'tcx>, -/* */ /// instance: Instance<'tcx>, -/* */ /// compute_instantiating_crate: impl FnOnce() -> CrateNum, -/* */ /// ) -> String -/* */ /// ``` -/* */ /// -/* */ /// `source_version_hash` - Compute hash based that only changes if there are "significant" -/* */ /// to control-flow inside the function. -/* */ /// -/* */ /// `num_counters` - The total number of counter calls [MAX(counter_index) + 1] within the -/* */ /// function. -/* */ /// -/* */ /// `counter_index` - zero-based counter index scoped by the function. (Ordering of -/* */ /// counters, relative to the source code location, is apparently not expected.) -/* */ /// -/* */ /// # Notes -/* */ /// -/* */ /// * The mangled_fn_name may not be computable until generics are monomorphized (see -/* */ /// parameters required by rustc_symbol_mangling::compute_symbol_name). -/* */ /// * The version hash may be computable from AST analysis, and may not benefit from further -/* */ /// lowering. -/* */ /// * num_counters depends on having already identified all counter insertion locations. -/* */ /// * counter_index can be computed at time of counter insertion (incrementally). -/* */ /// * Numeric parameters are signed to match the llvm increment intrinsic parameter types. -/* */ fn __lower_incr_cov(_mangled_fn_name: &'static str, _fn_version_hash: i64, _num_counters: i32, _counter_index: i32) { -/* */ } -/* */ -/* */ /// A coverage counter implementation that will work as both an intermediate coverage -/* */ /// counting and reporting implementation at the AST-level only--for debugging and -/* */ /// development--but also serves as a "marker" to be replaced by calls to LLVM -/* */ /// intrinsic coverage counter APIs during the lowering process. -/* */ /// -/* */ /// Calls to this function will be injected automatically into the AST. When LLVM intrinsics -/* */ /// are enabled, the counter function calls that were injected into the AST serve as -/* */ /// placeholders, to be replaced by an alternative, such as: -/* */ /// -/* */ /// * direct invocation of the `llvm.instrprof.increment()` intrinsic; or -/* */ /// * the `__lower_incr_cov()` function, defined above, that would invoke the -/* */ /// `llvm.instrprof.increment()` intrinsic; or -/* */ /// * a similar expression wrapper, with the additional parameters (as defined above -/* */ /// for `__lower_incr_cov()`, that invokes `llvm.instrprof.increment()` and returns the -/* */ /// result of the wrapped expression) -/* */ /// -/* */ /// The first two options would require replacing the inlined wrapper call with something -/* */ /// like: -/* */ /// -/* */ /// ``` -/* */ /// { let result = {expr}; __inlined_incr_cov(context, counter); result } -/* */ /// ``` -/* */ /// -/* */ /// But if the lowering process is already unwrapping the inlined call to `__incr_cov()`, then -/* */ /// it may be a perfect opportunity to replace the function with one of these more -/* */ /// direct methods. -/* */ /// -/* */ #[inline(always)] -/* */ pub fn __incr_cov(region_loc: &str, /*index: u32,*/ result: T) -> T { -/* */ // Either call the intermediate non-llvm coverage counter API or -/* */ // replace the call to this function with the expanded `__lower_incr_cov()` call. -/* */ -/* */ // let _lock = increment_counter(counter); -/* */ println!("{}", region_loc); -/* */ -/* */ result -/* */ } -/* */ -/* */ /// Write a report identifying each incremented counter and the number of times each counter -/* */ /// was incremented. -/* */ fn __report() { -/* */ println!("WRITE REPORT!"); -/* */ } -/* */ -/* */ /// Increment the counter after evaluating the wrapped expression (see `__incr_cov()`), then -/* */ /// write a report identifying each incremented counter and the number of times each counter -/* */ /// was incremented. -/* */ #[inline(always)] -/* */ pub fn __incr_cov_and_report(region_loc: &str, /*counter: u32,*/ result: T) -> T { -/* */ __incr_cov(region_loc, /*counter,*/ ()); -/* */ __report(); -/* */ result -/* */ } -/* */ -/* */ macro_rules! from { -/* */ ($from:expr) => { &format!("from: {}\n to: {}:{}:{}", $from, file!(), line!(), column!()) }; -/* */ } -/* */ -/* */ #[derive(Debug)] -/* */ enum TestEnum { -/* */ Red, -/* */ Green, -/* */ Blue, -/* */ } -/* */ -/* */ struct TestStruct { -/* */ field: i32, -/* */ } -/* */ -/* */ // IMPORTANT! IS WRAPPING main() ENOUGH? OR DO I ALSO NEED TO WRAP THREAD FUNCTIONS, ASSUMING -/* */ // THEY ARE STILL RUNNING WITH MAIN EXITS? (IF THEY CAN). NOT SURE HOW RUST HANDLES THAT. -/* */ -/* */ // I SUSPECT USING THREAD_LOCAL COUNTERS MAY NOT ACTUALLY BE AN OPTIMIZATION OVER MUTEX LOCKS, -/* */ // BUT MAYBE I SHOULD ASK. -/* */ -/* */ impl TestStruct { -/* - */ fn new() -> Self { -/* ┃ */ __incr_cov(from!("fn new()"),Self::new_with_value(31415)) // function-scoped counter index = 0 -/* - */ } -/* */ -/* - */ fn new_with_value(field: i32) -> Self { -/* ┃ */ __incr_cov(from!("fn new_with_value()"),Self { -/* ┃ */ field, -/* ┃ */ }) // function-scoped counter index = 0 -/* - */ } -/* */ -/* */ fn call_closure(&self, closure: F) -> bool -/* */ where -/* */ F: FnOnce( -/* */ i32, -/* */ ) -> bool, -/* - */ { -/* ┃ */ __incr_cov(from!("fn call_closure()"),closure(123)) // function-scoped counter index = 0 -/* - */ } -/* */ -/* - */ fn various(&self) -> Result<(),Error> { -/* ┃ */ use TestEnum::*; -/* ┃ */ let mut color = Red; -/* ┃ */ let _ = color; -/* ┃ */ color = Blue; -/* ┃ */ let _ = color; -/* ┃ */ color = Green; -/* ┃ */ match __incr_cov(from!("fn various"),color) { // function-scoped counter index = 0 -/* : */ -/* : */ // !!! RECORD SPAN FROM START OF INNERMOST CONTAINING BLOCK (THE FUNCTION IN THIS CASE) TO END OF MATCH EXPRESSION -/* : */ // If `match`, `while`, `loop`, `for`, `if`, etc. expression has a `return`, `break`, or `continue` -/* : */ // (if legal), then RECORD SPAN FROM START OF INNERMOST CONTAINING BLOCK TO END OF `return` EXPRESSION -/* : */ // If the expression includes lazy booleans, nest calls to `__incr_cov()`. -/* : I */ Red => __incr_cov(from!("Red => or end of MatchArmGuard expression inside pattern, if any"),println!("roses")), -/* : - */ Green => { -/* : ┃ */ let spidey = 100; -/* : ┃ */ let goblin = 50; -/* : ┃ */ // if spidey > goblin {__incr_cov(from!(""),{ -/* : ┃ */ // println!("what ev"); -/* : ┃ */ // })} -/* : ┃ */ // ACTUALLY, WRAPPING THE ENTIRE IF BLOCK IN `__incr_cov` IS NOT A GREAT GENERAL RULE. -/* : ┃ */ // JUST INSERTING A `return`, `break`, or `continue` IN THAT BLOCK (without an intermediate condition) -/* : ┃ */ // MAKES THE `__incr_cov()` CALL UNREACHABLE! -/* : ┃ */ // MY ORIGINAL SOLUTION WORKS BETTER (WRAP LAST EXPRESSION OR AFTER LAST SEMICOLON STATEMENT IN BLOCK) -/* : ┃ */ // UNLESS THE EXPRESSION IS NOT A BLOCK. -/* : ┃ - */ if __incr_cov(from!("Green => or end of MatchArmGuard expression inside pattern, if any"),spidey > goblin) { -/* : : ┃ */ println!("spidey beats goblin"); -/* : : ┃ */ __incr_cov(from!("block start"),()); -/* : ┃ - */ } else if __incr_cov(from!("`else if` on this line"),spidey == goblin) { -/* : : ┃ */ // COVERAGE NOTE: Do we mark only the expression span (that may be trivial, as in this case), -/* : : ┃ */ // or associate it with the outer block, similar to how the `if` expression is associated with -/* : : ┃ */ // the outer block? (Although it is a continuation, in a sense, it is discontiguous in this case, -/* : : ┃ */ // so I think simpler to just make it its own coverage region.) -/* : : ┃ */ println!("it's a draw"); -/* : : ┃ */ __incr_cov(from!("block start"),()); -/* : ┃ - - - */ } else if if __incr_cov(from!("`else if` on this line"),true) { -/* : : : ┃ */ // return __incr_cov(from!("after `if true`"),Ok(())); -/* : : : ┃ */ // ACTUALLY, BECAUSE OF `return`, WE DO NOT RECORD THE `if true` EVEN THOUGH WE COVERED IT. -/* : : : ┃ */ // IN FACT, IF THIS NESTED CONDITIONAL IN A CONDITIONAL EXPRESSION WAS AN `if` (WITHOUT PRECEDING ELSE) -/* : : : ┃ */ // WE WOULD NOT HAVE RECORDED THE COVERAGE OF STATEMENTS LEADING UP TO THE `if`, SO -/* : : : ┃ */ // IT SHOULD BE: -/* ┏-:---:-------:---< */ return __incr_cov(from!(""),Ok(())); -/* V : : : : */ // NOTE THE `from` STRING IS SAME FOR THE `else if`s `__incr_cov` AND THIS `return`. -/* : : : : */ // ONLY ONE OF THESE WILL EXECUTE, TO RECORD COVERAGE FROM THAT SPOT. -/* : : ┃ - */ } else { -/* : : : I */ __incr_cov(from!("`else`"),false) -/* : : - - */ } { -/* : : ┃ */ println!("wierd science"); -/* : : ┃ */ __incr_cov(from!("block start"),()); -/* : ┃ - */ } else { -/* : : ┃ */ println!("goblin wins"); -/* ┏-:---:---< */ return __incr_cov(from!("`else`"),Ok(())); // THIS COUNTS LAST STATEMENT IN `else` BLOCK -/* V : : : */ // COVERAGE NOTE: When counting the span for `return`, -/* : : : */ // `break`, or `continue`, also report the outer spans -/* : : : */ // got this far--including this `else` block. Record -/* : : : */ // The start positions for those outer blocks, but: -/* : : : */ // * For the block containing the `return`, `break`, or -/* : : : */ // `continue`, end report the end position is the -/* : : : */ // start of the `return` span (or 1 char before it). -/* : : : */ // * Anything else? -/* : ┃ - */ } -/* : ┃ - */ // __incr_cov(from!(""),()); // DO NOT COUNT HERE IF NO STATEMENTS AFTER LAST `if` or `match` -/* : - */ }, -/* : I */ Blue => __incr_cov(from!("Blue => or end of MatchArmGuard expression inside pattern, if any"),println!("violets")), -/* ┃ */ } -/* ┃ */ -/* ┃ */ let condition1 = true; -/* ┃ */ let condition2 = false; -/* ┃ */ let condition3 = true; -/* ┃ */ -/* ┃ */ println!("Called `various()` for TestStruct with field={}", self.field); -/* ┃ */ -/* ┃ - */ if __incr_cov(from!("after block end of prior `match` (or `if-else if-else`)"),condition1) { -/* : ┃ */ println!("before while loop"); -/* : ┃ */ let mut countdown = 10; -/* : ┃ */ __incr_cov(from!("block start"),()); // Must increment before repeated while text expression -/* : : I */ while __incr_cov(from!("while test"), countdown > 0) { // span is just the while test expression -/* : : ┃ */ println!("top of `while` loop"); -/* : : ┃ */ countdown -= 1; -/* : : ┃ */ // __incr_cov(from!("while loop"),()); // Counter not needed, but span is computed as "while test" minus "block start" -/* : : ┃ */ // If test expression is 11, and the outer block runs only once, 11-1 = 10 -/* : ┃ - */ } -/* : ┃ */ println!("before for loop"); -/* : ┃ - */ for index in __incr_cov(from!("end of while"),0..10) { -/* : : ┃ */ println!("top of `for` loop"); -/* : : ┃ - */ if __incr_cov(from!("block start"),index == 8) { -/* : : : ┃ */ println!("before break"); -/* : : : ┃ */ // note the following is not legal here: -/* : : : ┃ */ // "can only break with a value inside `loop` or breakable block" -/* : : : ┃ */ // break __incr_cov(from!(""),()); -/* : : : ┃ */ __incr_cov(from!("block start"),()); -/* : : ┏-----< */ break; -/* : : V : : */ -/* : : : : */ // FIXME(richkadel): add examples with loop labels, breaking out of inner and outer loop to outer loop label, with expression. -/* : : : : */ // May want to record both the span and the start position after the broken out block depdnding on label -/* : : ┃ - */ } -/* : : ┃ */ println!("after `break` test"); -/* : : ┃ - */ if __incr_cov(from!("block end of `if index == 8`"),condition2) { -/* ┏-:---:---:---< */ return __incr_cov(from!("block start"),Ok(())); -/* V : : ┃ - */ } -/* : : ┃ */ -/* : : ┃ */ // BECAUSE THE PREVIOUS COVERAGE REGION HAS A `return`, THEN -/* : : ┃ */ // IF PREVIOUS COVERAGE REGION IS NOT COUNTED THEN OUTER REGION REACHED HERE. -/* : : ┃ */ // ADD A COVERAGE REGION FOR THE SPAN FROM JUST AFTER PREVIOUS REGION TO END -/* : : ┃ */ // OF OUTER SPAN, THEN TRUNCATE TO NEXT REGION NOT REACHED. -/* : : ┃ - */ if index % 3 == 2 { // NO __incr_cov() HERE BECAUSE NO STATEMENTS BETWEEN LAST CONDITIONAL BLOCK AND START OF THIS ONE -/* : : Λ : ┃ */ __incr_cov(from!("block end of `if condition2`"),()); -/* : : ┗-----< */ continue; -/* : : ┃ - */ } -/* : : ┃ */ println!("after `continue` test"); -/* : : ┃ */ // maybe add a runtime flag for a possible `return` here? -/* : : ┃ */ __incr_cov(from!("for loop"),()); -/* : ┃ - */ } -/* : ┃ */ println!("after for loop"); -/* : ┃ */ let result = if { // START OF NEW CONDITIONAL EXPRESSION. NEXT "GUARANTEED" COUNTER SHOULD COUNT FROM END OF LAST CONDITIONAL EXPRESSION -/* : ┃ */ // A "GUARANTEED" COUNTER CALL IS ONE THAT WILL BE CALLED REGARDLESS OF OTHER CONDITIONS. THIS INCLUDES: -/* : ┃ */ // * A CONDITIONAL EXPRESSION THAT IS NOT A BLOCK (OR ANOTHER CONDITIONAL STATEMENT, WHICH WOULD CONTAIN A BLOCK) -/* : ┃ */ // * OR IF THE NEXT CONDITIONAL EXPRESSION IS A BLOCK OR CONDITIONAL STATEMENT, THEN THE FIRST "GUARANTEED" COUNTER IN THAT BLOCK -/* : ┃ */ // * END OF BLOCK IF THE BLOCK DOES NOT HAVE INNER CONDITIONAL EXPRESSIONS -/* : ┃ */ // * BRANCHING STATEMENTS (`return`, `break`, `continue`) BY EITHER WRAPPING THE BRANCH STATEMENT NON-BLOCK EXPRESSION, -/* : ┃ */ // OR PREPENDING A COUNTER WITH EMPTY TUPLE IF NO EXPRESSION, OR IF EXPRESSION IS A BLOCK, THEN THE NEXT "GUARANTEED" -/* : ┃ */ // COUNTER CALL WITHIN THAT BLOCK. -/* : ┃ */ // BASICALLY, CARRY THE START OF COVERAGE SPAN FORWARD UNTIL THE GUARANTEED COUNTER IS FOUND -/* : ┃ */ println!("after result = if ..."); -/* : ┃ - */ if __incr_cov(from!("block end of `for` loop"),condition2) { -/* : : ┃ */ println!("before first return"); -/* ┏-:---:-------< */ return __incr_cov(from!("block start"),Ok(())); -/* V : : - */ } else if __incr_cov(from!("`else`"),condition3) { -/* : : ┃ */ // THE ABOVE COUNTER IS _NOT_ REALLY NECESSARY IF EXPRESSION IS GUARANTEED TO EXECUTE. -/* : : ┃ */ // IF WE GET COUNTER IN `else if` BLOCK WE COVERED EXPRESSION. -/* : : ┃ */ // IF WE GET TO ANY REMAINING `else` or `else if` BLOCK WE KNOW WE EVALUATED THIS CONDITION -/* : : ┃ */ // AND ALL OTHERS UP TO THE EXECUTED BLOCK. BUT THE SPAN WOULD HAVE "HOLES" FOR UNEXECUTED BLOCKS. -/* : : ┃ */ println!("not second return"); -/* ┏-:---:-------< */ return __incr_cov(from!("block start"),Ok(())); -/* V : : - */ } else { -/* : : ┃ */ println!("not returning"); -/* : : ┃ */ __incr_cov(from!("block start"),false) -/* : : - */ } -/* : ┃ */ // NO COUNTER HERE BECAUSE NO STATEMENTS AFTER CONDITIONAL BLOCK -/* : ┃ - */ } { -/* : : ┃ */ println!("branched condition returned true"); -/* : : ┃ */ __incr_cov(from!(""),Ok(())) -/* : ┃ - */ } else if self.call_closure( -/* : : - */ |closure_param| __incr_cov(from!(""), -/* : : ┃ - */ if condition3 { -/* : : : ┃ */ println!("in closure, captured condition said to print the param {}", closure_param); -/* : : : ┃ */ __incr_cov(from!(""),false) -/* : : ┃ - */ } else { -/* : : : ┃ */ println!("in closure, captured condition was false"); -/* : : : ┃ */ __incr_cov(from!(""),true) -/* : : ┃ - */ } -/* : : - */ ) -/* : : - */ ) { -/* : : ┃ */ println!("closure returned true"); -/* : : ┃ */ __incr_cov(from!(""),Err(Error::new(ErrorKind::Other, "Result is error if closure returned true"))) -/* : ┃ - */ } else { -/* : : ┃ */ println!("closure returned false"); -/* : : ┃ */ __incr_cov(from!(""),Err(Error::new(ErrorKind::Other, "Result is error if closure returned false"))) -/* : ┃ - */ }; -/* : ┃ */ println!("bottom of function might be skipped if early `return`"); -/* : ┃ */ __incr_cov(from!("if condition1"),result) -/* ┃ - */ } else { -/* : ┃ */ println!("skipping everything in `various()`"); -/* : ┃ */ __incr_cov(from!(""),Ok(())) -/* ┃ - */ } -/* ┃ - */ // __incr_cov(from!(""),0) // DO NOT COUNT IF NO STATEMENTS AFTER CONDITIONAL BLOCK. ALL COVERAGE IS ALREADY COUNTED -/* - */ } -/* */ } -/* */ -/* - */ fn main() -> Result<(), std::io::Error> { -/* ┃ */ //let mut status: u8 = 2; -/* ┃ */ let mut status: u8 = 1; -/* : - */ let result = if status < 2 && -/* : ┃ */ __incr_cov(from!(""),{ -/* : ┃ */ status -= 1; -/* : ┃ */ status == 0 -/* : - - */ }) { -/* : ┃ */ let test_struct = TestStruct::new_with_value(100); -/* : ┃ */ let _ = test_struct.various(); -/* ┏-:---< */ return __incr_cov_and_report(from!(""),Err(Error::new(ErrorKind::Other, format!("Error status {}", status)))) -/* V : - */ } else { -/* : ┃ */ let test_struct = TestStruct::new(); -/* : ┃ */ __incr_cov(from!(""),test_struct.various()) -/* : - */ }; -/* ┃ */ println!("done"); -/* ┃ */ __incr_cov_and_report(from!(""),result) // function-scoped counter index = 0 -/* - */ } \ No newline at end of file diff --git a/src/test/codegen/coverage-experiments/src/coverage_injection_test2.rs b/src/test/codegen/coverage-experiments/src/coverage_injection_test2.rs deleted file mode 100644 index 8f4399ab51d0..000000000000 --- a/src/test/codegen/coverage-experiments/src/coverage_injection_test2.rs +++ /dev/null @@ -1,320 +0,0 @@ -/* */ use std::io::Error; -/* */ use std::io::ErrorKind; -/* */ -/* */ /// Align Rust counter increment with with: -/* */ /// [‘llvm.instrprof.increment’ Intrinsic](https://llvm.org/docs/LangRef.html#llvm-instrprof-increment-intrinsic) -/* */ /// -/* */ /// declare void @llvm.instrprof.increment(i8* , i64 , i32 , i32 ) -/* */ /// -/* */ /// The first argument is a pointer to a global variable containing the name of the entity -/* */ /// being instrumented. This should generally be the (mangled) function name for a set of -/* */ /// counters. -/* */ /// -/* */ /// The second argument is a hash value that can be used by the consumer of the profile data -/* */ /// to detect changes to the instrumented source, and the third is the number of counters -/* */ /// associated with name. It is an error if hash or num-counters differ between two -/* */ /// instances of instrprof.increment that refer to the same name. -/* */ /// -/* */ /// The last argument refers to which of the counters for name should be incremented. It -/* */ /// should be a value between 0 and num-counters. -/* */ /// -/* */ /// # Arguments -/* */ /// -/* */ /// `mangled_fn_name` - &'static ref to computed and injected static str, using: -/* */ /// -/* */ /// ``` -/* */ /// fn rustc_symbol_mangling::compute_symbol_name( -/* */ /// tcx: TyCtxt<'tcx>, -/* */ /// instance: Instance<'tcx>, -/* */ /// compute_instantiating_crate: impl FnOnce() -> CrateNum, -/* */ /// ) -> String -/* */ /// ``` -/* */ /// -/* */ /// `source_version_hash` - Compute hash based that only changes if there are "significant" -/* */ /// to control-flow inside the function. -/* */ /// -/* */ /// `num_counters` - The total number of counter calls [MAX(counter_index) + 1] within the -/* */ /// function. -/* */ /// -/* */ /// `counter_index` - zero-based counter index scoped by the function. (Ordering of -/* */ /// counters, relative to the source code location, is apparently not expected.) -/* */ /// -/* */ /// # Notes -/* */ /// -/* */ /// * The mangled_fn_name may not be computable until generics are monomorphized (see -/* */ /// parameters required by rustc_symbol_mangling::compute_symbol_name). -/* */ /// * The version hash may be computable from AST analysis, and may not benefit from further -/* */ /// lowering. -/* */ /// * num_counters depends on having already identified all counter insertion locations. -/* */ /// * counter_index can be computed at time of counter insertion (incrementally). -/* */ /// * Numeric parameters are signed to match the llvm increment intrinsic parameter types. -/* */ fn __lower_incr_cov(_mangled_fn_name: &'static str, _fn_version_hash: i64, _num_counters: i32, _counter_index: i32) { -/* */ } -/* */ -/* */ /// A coverage counter implementation that will work as both an intermediate coverage -/* */ /// counting and reporting implementation at the AST-level only--for debugging and -/* */ /// development--but also serves as a "marker" to be replaced by calls to LLVM -/* */ /// intrinsic coverage counter APIs during the lowering process. -/* */ /// -/* */ /// Calls to this function will be injected automatically into the AST. When LLVM intrinsics -/* */ /// are enabled, the counter function calls that were injected into the AST serve as -/* */ /// placeholders, to be replaced by an alternative, such as: -/* */ /// -/* */ /// * direct invocation of the `llvm.instrprof.increment()` intrinsic; or -/* */ /// * the `__lower_incr_cov()` function, defined above, that would invoke the -/* */ /// `llvm.instrprof.increment()` intrinsic; or -/* */ /// * a similar expression wrapper, with the additional parameters (as defined above -/* */ /// for `__lower_incr_cov()`, that invokes `llvm.instrprof.increment()` and returns the -/* */ /// result of the wrapped expression) -/* */ /// -/* */ /// The first two options would require replacing the inlined wrapper call with something -/* */ /// like: -/* */ /// -/* */ /// ``` -/* */ /// { let result = {expr}; __inlined_incr_cov(context, counter); result } -/* */ /// ``` -/* */ /// -/* */ /// But if the lowering process is already unwrapping the inlined call to `__incr_cov()`, then -/* */ /// it may be a perfect opportunity to replace the function with one of these more -/* */ /// direct methods. -/* */ /// -/* */ #[inline(always)] -/* */ pub fn __incr_cov(region_loc: &str) { -/* */ // Either call the intermediate non-llvm coverage counter API or -/* */ // replace the call to this function with the expanded `__lower_incr_cov()` call. -/* */ -/* */ // let _lock = increment_counter(counter); -/* */ println!("{}", region_loc); -/* */ } -/* */ -/* */ /// Write a report identifying each incremented counter and the number of times each counter -/* */ /// was incremented. -/* */ fn __report() { -/* */ println!("WRITE REPORT!"); -/* */ } -/* */ -/* */ macro_rules! from { -/* */ ($from:expr) => { &format!("from: {}\n to: {}:{}:{}", $from, file!(), line!(), column!()) }; -/* */ } -/* */ -/* */ #[derive(Debug)] -/* */ enum TestEnum { -/* */ Red, -/* */ Green, -/* */ Blue, -/* */ } -/* */ -/* */ struct TestStruct { -/* */ field: i32, -/* */ } -/* */ -/* */ // IMPORTANT! IS WRAPPING main() ENOUGH? OR DO I ALSO NEED TO WRAP THREAD FUNCTIONS, ASSUMING -/* */ // THEY ARE STILL RUNNING WITH MAIN EXITS? (IF THEY CAN). NOT SURE HOW RUST HANDLES THAT. -/* */ -/* */ // I SUSPECT USING THREAD_LOCAL COUNTERS MAY NOT ACTUALLY BE AN OPTIMIZATION OVER MUTEX LOCKS, -/* */ // BUT MAYBE I SHOULD ASK. -/* */ -/* */ impl TestStruct { -/* - */ fn new() -> Self { -/* ┃ */ let __result = Self::new_with_value(31415); // function-scoped counter index = 0 -/* ┃ */ __incr_cov(from!("fn new()")); -/* ┃ */ __result -/* - */ } -/* */ -/* - */ fn new_with_value(field: i32) -> Self { -/* ┃ */ let __result = Self { -/* ┃ */ field, -/* ┃ */ }; -/* ┃ */ __incr_cov(from!("fn new_with_value()")); // function-scoped counter index = 0 -/* ┃ */ __result -/* - */ } -/* */ -/* */ fn call_closure(&self, closure: F) -> bool -/* */ where -/* */ F: FnOnce( -/* */ i32, -/* */ ) -> bool, -/* - */ { -/* ┃ */ let __result = closure(123); -/* ┃ */ __incr_cov(from!("fn call_closure()")); // function-scoped counter index = 0 -/* ┃ */ __result -/* - */ } -/* */ -/* - */ fn various(&self) -> Result<(),Error> { -/* ┃ */ use TestEnum::*; -/* ┃ */ let mut color = Red; -/* ┃ */ let _ = color; -/* ┃ */ color = Blue; -/* ┃ */ let _ = color; -/* ┃ */ color = Green; -/* ┃ */ match { let __result = color; __incr_cov(from!("fn various")); __result } { // function-scoped counter index = 0 -/* : */ -/* : */ // !!! RECORD SPAN FROM START OF INNERMOST CONTAINING BLOCK (THE FUNCTION IN THIS CASE) TO END OF MATCH EXPRESSION -/* : */ // If `match`, `while`, `loop`, `for`, `if`, etc. expression has a `return`, `break`, or `continue` -/* : */ // (if legal), then RECORD SPAN FROM START OF INNERMOST CONTAINING BLOCK TO END OF `return` EXPRESSION -/* : */ // If the expression includes lazy booleans, nest calls to `__incr_cov()`. -/* : I */ Red => {println!("roses"); __incr_cov(from!("Red => or end of MatchArmGuard expression inside pattern, if any"));} -/* : - */ Green => { -/* : ┃ */ let spidey = 100; -/* : ┃ */ let goblin = 50; -/* : ┃ */ // if spidey > goblin {__incr_cov(from!(""),{ -/* : ┃ */ // println!("what ev"); -/* : ┃ */ // })} -/* : ┃ */ // ACTUALLY, WRAPPING THE ENTIRE IF BLOCK IN `__incr_cov` IS NOT A GREAT GENERAL RULE. -/* : ┃ */ // JUST INSERTING A `return`, `break`, or `continue` IN THAT BLOCK (without an intermediate condition) -/* : ┃ */ // MAKES THE `__incr_cov()` CALL UNREACHABLE! -/* : ┃ */ // MY ORIGINAL SOLUTION WORKS BETTER (WRAP LAST EXPRESSION OR AFTER LAST SEMICOLON STATEMENT IN BLOCK) -/* : ┃ */ // UNLESS THE EXPRESSION IS NOT A BLOCK. -/* : ┃ - */ if { let __result = spidey > goblin; __incr_cov(from!("Green => or end of MatchArmGuard expression inside pattern, if any")); __result } { -/* : : ┃ */ println!("spidey beats goblin"); -/* : : ┃ */ __incr_cov(from!("block start")); -/* : ┃ - */ } else if { let __result = spidey == goblin; __incr_cov(from!("`else if` on this line")); __result } { -/* : : ┃ */ // COVERAGE NOTE: Do we mark only the expression span (that may be trivial, as in this case), -/* : : ┃ */ // or associate it with the outer block, similar to how the `if` expression is associated with -/* : : ┃ */ // the outer block? (Although it is a continuation, in a sense, it is discontiguous in this case, -/* : : ┃ */ // so I think simpler to just make it its own coverage region.) -/* : : ┃ */ println!("it's a draw"); -/* : : ┃ */ __incr_cov(from!("block start")); -/* : ┃ - - - */ } else if if { let __result = true; __incr_cov(from!("`else if` on this line")); __result } { -/* : : : ┃ */ // return __incr_cov(from!("after `if true`"),Ok(())); -/* : : : ┃ */ // ACTUALLY, BECAUSE OF `return`, WE DO NOT RECORD THE `if true` EVEN THOUGH WE COVERED IT. -/* : : : ┃ */ // IN FACT, IF THIS NESTED CONDITIONAL IN A CONDITIONAL EXPRESSION WAS AN `if` (WITHOUT PRECEDING ELSE) -/* : : : ┃ */ // WE WOULD NOT HAVE RECORDED THE COVERAGE OF STATEMENTS LEADING UP TO THE `if`, SO -/* : : : ┃ */ // IT SHOULD BE: -/* ┏-:---:-------:---< */ return { let __result = Ok(()); __incr_cov(from!("")); __result }; -/* V : : : : */ // NOTE THE `from` STRING IS SAME FOR THE `else if`s `__incr_cov` AND THIS `return`. -/* : : : : */ // ONLY ONE OF THESE WILL EXECUTE, TO RECORD COVERAGE FROM THAT SPOT. -/* : : ┃ - */ } else { -/* : : : I */ { let __result = false; __incr_cov(from!("`else`")); __result } -/* : : - - */ } { -/* : : ┃ */ println!("wierd science"); -/* : : ┃ */ __incr_cov(from!("block start")); -/* : ┃ - */ } else { -/* : : ┃ */ println!("goblin wins"); -/* ┏-:---:---< */ return { let __result = Ok(()); __incr_cov(from!("`else`")); __result }; // THIS COUNTS LAST STATEMENT IN `else` BLOCK -/* V : : : */ // COVERAGE NOTE: When counting the span for `return`, -/* : : : */ // `break`, or `continue`, also report the outer spans -/* : : : */ // got this far--including this `else` block. Record -/* : : : */ // The start positions for those outer blocks, but: -/* : : : */ // * For the block containing the `return`, `break`, or -/* : : : */ // `continue`, end report the end position is the -/* : : : */ // start of the `return` span (or 1 char before it). -/* : : : */ // * Anything else? -/* : ┃ - */ } -/* : ┃ - */ // __incr_cov(from!("")); // DO NOT COUNT HERE IF NO STATEMENTS AFTER LAST `if` or `match` -/* : - */ }, -/* : I */ Blue => { println!("violets"); __incr_cov(from!("Blue => or end of MatchArmGuard expression inside pattern, if any")); } -/* ┃ */ } -/* ┃ */ -/* ┃ */ let condition1 = true; -/* ┃ */ let condition2 = false; -/* ┃ */ let condition3 = true; -/* ┃ */ -/* ┃ */ println!("Called `various()` for TestStruct with field={}", self.field); -/* ┃ */ -/* ┃ - */ if { let __result = condition1; __incr_cov(from!("after block end of prior `match` (or `if-else if-else`)")); __result } { -/* : ┃ */ println!("before for loop"); -/* : ┃ - */ for index in { let __result = 0..10; __incr_cov(from!("block start")); __result } { -/* : : ┃ */ println!("top of `for` loop"); -/* : : ┃ - */ if { let __result = index == 8; __incr_cov(from!("block start")); __result } { -/* : : : ┃ */ println!("before break"); -/* : : : ┃ */ // note the following is not legal here: -/* : : : ┃ */ // "can only break with a value inside `loop` or breakable block" -/* : : : ┃ */ // break __incr_cov(from!("")); -/* : : : ┃ */ __incr_cov(from!("block start")); -/* : : ┏-----< */ break; -/* : : V : : */ -/* : : : : */ // FIXME(richkadel): add examples with loop labels, breaking out of inner and outer loop to outer loop label, with expression. -/* : : : : */ // May want to record both the span and the start position after the broken out block depdnding on label -/* : : ┃ - */ } -/* : : ┃ */ println!("after `break` test"); -/* : : ┃ - */ if { let __result = condition2; __incr_cov(from!("block end of `if index == 8`")); __result } { -/* ┏-:---:---:---< */ return { let __result = Ok(()); __incr_cov(from!("block start")); __result }; -/* V : : ┃ - */ } -/* : : ┃ */ -/* : : ┃ */ // BECAUSE THE PREVIOUS COVERAGE REGION HAS A `return`, THEN -/* : : ┃ */ // IF PREVIOUS COVERAGE REGION IS NOT COUNTED THEN OUTER REGION REACHED HERE. -/* : : ┃ */ // ADD A COVERAGE REGION FOR THE SPAN FROM JUST AFTER PREVIOUS REGION TO END -/* : : ┃ */ // OF OUTER SPAN, THEN TRUNCATE TO NEXT REGION NOT REACHED. -/* : : ┃ - */ if index % 3 == 2 { // NO __incr_cov() HERE BECAUSE NO STATEMENTS BETWEEN LAST CONDITIONAL BLOCK AND START OF THIS ONE -/* : : Λ : ┃ */ __incr_cov(from!("block end of `if condition2`")); -/* : : ┗-----< */ continue; -/* : : ┃ - */ } -/* : : ┃ */ println!("after `continue` test"); -/* : : ┃ */ // maybe add a runtime flag for a possible `return` here? -/* : : ┃ */ __incr_cov(from!("")); -/* : ┃ - */ } -/* : ┃ */ println!("after for loop"); -/* : ┃ */ let result = if { // START OF NEW CONDITIONAL EXPRESSION. NEXT "GUARANTEED" COUNTER SHOULD COUNT FROM END OF LAST CONDITIONAL EXPRESSION -/* : ┃ */ // A "GUARANTEED" COUNTER CALL IS ONE THAT WILL BE CALLED REGARDLESS OF OTHER CONDITIONS. THIS INCLUDES: -/* : ┃ */ // * A CONDITIONAL EXPRESSION THAT IS NOT A BLOCK (OR ANOTHER CONDITIONAL STATEMENT, WHICH WOULD CONTAIN A BLOCK) -/* : ┃ */ // * OR IF THE NEXT CONDITIONAL EXPRESSION IS A BLOCK OR CONDITIONAL STATEMENT, THEN THE FIRST "GUARANTEED" COUNTER IN THAT BLOCK -/* : ┃ */ // * END OF BLOCK IF THE BLOCK DOES NOT HAVE INNER CONDITIONAL EXPRESSIONS -/* : ┃ */ // * BRANCHING STATEMENTS (`return`, `break`, `continue`) BY EITHER WRAPPING THE BRANCH STATEMENT NON-BLOCK EXPRESSION, -/* : ┃ */ // OR PREPENDING A COUNTER WITH EMPTY TUPLE IF NO EXPRESSION, OR IF EXPRESSION IS A BLOCK, THEN THE NEXT "GUARANTEED" -/* : ┃ */ // COUNTER CALL WITHIN THAT BLOCK. -/* : ┃ */ // BASICALLY, CARRY THE START OF COVERAGE SPAN FORWARD UNTIL THE GUARANTEED COUNTER IS FOUND -/* : ┃ */ println!("after result = if ..."); -/* : ┃ - */ if { let __result = condition2; __incr_cov(from!("block end of `for` loop")); __result } { -/* : : ┃ */ println!("before first return"); -/* ┏-:---:-------< */ return { let __result = Ok(()); __incr_cov(from!("block start")); __result }; -/* V : : - */ } else if { let __result = condition3; __incr_cov(from!("`else`")); __result } { -/* : : ┃ */ // THE ABOVE COUNTER IS _NOT_ REALLY NECESSARY IF EXPRESSION IS GUARANTEED TO EXECUTE. -/* : : ┃ */ // IF WE GET COUNTER IN `else if` BLOCK WE COVERED EXPRESSION. -/* : : ┃ */ // IF WE GET TO ANY REMAINING `else` or `else if` BLOCK WE KNOW WE EVALUATED THIS CONDITION -/* : : ┃ */ // AND ALL OTHERS UP TO THE EXECUTED BLOCK. BUT THE SPAN WOULD HAVE "HOLES" FOR UNEXECUTED BLOCKS. -/* : : ┃ */ println!("not second return"); -/* ┏-:---:-------< */ return { let __result = Ok(()); __incr_cov(from!("block start")); __result }; -/* V : : - */ } else { -/* : : ┃ */ println!("not returning"); -/* : : ┃ */ { let __result = false; __incr_cov(from!("block start")); __result } -/* : : - */ } -/* : ┃ */ // NO COUNTER HERE BECAUSE NO STATEMENTS AFTER CONDITIONAL BLOCK -/* : ┃ - */ } { -/* : : ┃ */ println!("branched condition returned true"); -/* : : ┃ */ { let __result = Ok(()); __incr_cov(from!("")); __result } -/* : ┃ - */ } else if self.call_closure( -/* : : - */ |closure_param| { -/* : : ┃ - */ let __result = if condition3 { -/* : : : ┃ */ println!("in closure, captured condition said to print the param {}", closure_param); -/* : : : ┃ */ { let __result = false; __incr_cov(from!("")); __result } -/* : : ┃ - */ } else { -/* : : : ┃ */ println!("in closure, captured condition was false"); -/* : : : ┃ */ { let __result = true; __incr_cov(from!("")); __result } -/* : : ┃ - */ }; -/* : : - */ __incr_cov(from!("")); __result } -/* : : - */ ) { -/* : : ┃ */ println!("closure returned true"); -/* : : ┃ */ { let __result = Err(Error::new(ErrorKind::Other, "Result is error if closure returned true")); __incr_cov(from!("")); __result } -/* : ┃ - */ } else { -/* : : ┃ */ println!("closure returned false"); -/* : : ┃ */ { let __result = Err(Error::new(ErrorKind::Other, "Result is error if closure returned false")); __incr_cov(from!("")); __result } -/* : ┃ - */ }; -/* : ┃ */ println!("bottom of function might be skipped if early `return`"); -/* : ┃ */ { let __result = result; __incr_cov(from!("if condition1")); __result } -/* ┃ - */ } else { -/* : ┃ */ println!("skipping everything in `various()`"); -/* : ┃ */ { let __result = Ok(()); __incr_cov(from!("")); __result } -/* ┃ - */ } -/* ┃ - */ // __incr_cov(from!(""),0) // DO NOT COUNT IF NO STATEMENTS AFTER CONDITIONAL BLOCK. ALL COVERAGE IS ALREADY COUNTED -/* - */ } -/* */ } -/* */ -/* - */ fn main() -> Result<(), std::io::Error> { -/* ┃ */ //let mut status: u8 = 2; -/* ┃ */ let mut status: u8 = 1; -/* : - */ let result = if status < 2 && -/* : ┃ */ { let __result = { -/* : ┃ */ status -= 1; -/* : ┃ */ status == 0 -/* : - - */ }; __incr_cov(from!("")); __result } { -/* : ┃ */ let test_struct = TestStruct::new_with_value(100); -/* : ┃ */ let _ = test_struct.various(); -/* ┏-:---< */ return { let __result = Err(Error::new(ErrorKind::Other, format!("Error status {}", status))); __incr_cov(from!("")); __report(); __result } -/* V : - */ } else { -/* : ┃ */ let test_struct = TestStruct::new(); -/* : ┃ */ { let __result = test_struct.various(); __incr_cov(from!("")); __result } -/* : - */ }; -/* ┃ */ println!("done"); -/* ┃ */ { let __result = result; __incr_cov(from!("")); __report(); __result } -/* - */ } \ No newline at end of file diff --git a/src/test/codegen/coverage-experiments/src/coverage_injection_test_alt.rs b/src/test/codegen/coverage-experiments/src/coverage_injection_test_alt.rs deleted file mode 100644 index 20c4835dd882..000000000000 --- a/src/test/codegen/coverage-experiments/src/coverage_injection_test_alt.rs +++ /dev/null @@ -1,362 +0,0 @@ -/* */ use std::io::Error; -/* */ use std::io::ErrorKind; -/* */ -/* */ /// Align Rust counter increment with with: -/* */ /// [‘llvm.instrprof.increment’ Intrinsic](https://llvm.org/docs/LangRef.html#llvm-instrprof-increment-intrinsic) -/* */ /// -/* */ /// declare void @llvm.instrprof.increment(i8* , i64 , i32 , i32 ) -/* */ /// -/* */ /// The first argument is a pointer to a global variable containing the name of the entity -/* */ /// being instrumented. This should generally be the (mangled) function name for a set of -/* */ /// counters. -/* */ /// -/* */ /// The second argument is a hash value that can be used by the consumer of the profile data -/* */ /// to detect changes to the instrumented source, and the third is the number of counters -/* */ /// associated with name. It is an error if hash or num-counters differ between two -/* */ /// instances of instrprof.increment that refer to the same name. -/* */ /// -/* */ /// The last argument refers to which of the counters for name should be incremented. It -/* */ /// should be a value between 0 and num-counters. -/* */ /// -/* */ /// # Arguments -/* */ /// -/* */ /// `mangled_fn_name` - &'static ref to computed and injected static str, using: -/* */ /// -/* */ /// ``` -/* */ /// fn rustc_symbol_mangling::compute_symbol_name( -/* */ /// tcx: TyCtxt<'tcx>, -/* */ /// instance: Instance<'tcx>, -/* */ /// compute_instantiating_crate: impl FnOnce() -> CrateNum, -/* */ /// ) -> String -/* */ /// ``` -/* */ /// -/* */ /// `source_version_hash` - Compute hash based that only changes if there are "significant" -/* */ /// to control-flow inside the function. -/* */ /// -/* */ /// `num_counters` - The total number of counter calls [MAX(counter_index) + 1] within the -/* */ /// function. -/* */ /// -/* */ /// `counter_index` - zero-based counter index scoped by the function. (Ordering of -/* */ /// counters, relative to the source code location, is apparently not expected.) -/* */ /// -/* */ /// # Notes -/* */ /// -/* */ /// * The mangled_fn_name may not be computable until generics are monomorphized (see -/* */ /// parameters required by rustc_symbol_mangling::compute_symbol_name). -/* */ /// * The version hash may be computable from AST analysis, and may not benefit from further -/* */ /// lowering. -/* */ /// * num_counters depends on having already identified all counter insertion locations. -/* */ /// * counter_index can be computed at time of counter insertion (incrementally). -/* */ /// * Numeric parameters are signed to match the llvm increment intrinsic parameter types. -/* */ fn __lower_incr_cov(_mangled_fn_name: &'static str, _fn_version_hash: i64, _num_counters: i32, _counter_index: i32) { -/* */ } -/* */ -/* */ /// A coverage counter implementation that will work as both an intermediate coverage -/* */ /// counting and reporting implementation at the AST-level only--for debugging and -/* */ /// development--but also serves as a "marker" to be replaced by calls to LLVM -/* */ /// intrinsic coverage counter APIs during the lowering process. -/* */ /// -/* */ /// Calls to this function will be injected automatically into the AST. When LLVM intrinsics -/* */ /// are enabled, the counter function calls that were injected into the AST serve as -/* */ /// placeholders, to be replaced by an alternative, such as: -/* */ /// -/* */ /// * direct invocation of the `llvm.instrprof.increment()` intrinsic; or -/* */ /// * the `__lower_incr_cov()` function, defined above, that would invoke the -/* */ /// `llvm.instrprof.increment()` intrinsic; or -/* */ /// * a similar expression wrapper, with the additional parameters (as defined above -/* */ /// for `__lower_incr_cov()`, that invokes `llvm.instrprof.increment()` and returns the -/* */ /// result of the wrapped expression) -/* */ /// -/* */ /// The first two options would require replacing the inlined wrapper call with something -/* */ /// like: -/* */ /// -/* */ /// ``` -/* */ /// { let result = {expr}; __inlined_incr_cov(context, counter); result } -/* */ /// ``` -/* */ /// -/* */ /// But if the lowering process is already unwrapping the inlined call to `__incr_cov()`, then -/* */ /// it may be a perfect opportunity to replace the function with one of these more -/* */ /// direct methods. -/* */ /// -/* */ #[inline(always)] -/* */ pub fn __incr_cov(region_loc: &str, /*index: u32,*/) { -/* */ // Either call the intermediate non-llvm coverage counter API or -/* */ // replace the call to this function with the expanded `__lower_incr_cov()` call. -/* */ -/* */ // let _lock = increment_counter(counter); -/* */ println!("{}", region_loc); -/* */ } -/* */ -/* */ /// Write a report identifying each incremented counter and the number of times each counter -/* */ /// was incremented. -/* */ fn __report() { -/* */ println!("WRITE REPORT!"); -/* */ } -/* */ -/* */ /// Increment the counter after evaluating the wrapped expression (see `__incr_cov()`), then -/* */ /// write a report identifying each incremented counter and the number of times each counter -/* */ /// was incremented. -/* */ #[inline(always)] -/* */ pub fn __incr_cov_and_report(region_loc: &str, /*counter: u32,*/ result: T) -> T { -/* */ __incr_cov(region_loc, /*counter,*/); -/* */ __report(); -/* */ result -/* */ } -/* */ -/* */ macro_rules! from { -/* */ ($from:expr) => { &format!("from: {}\n to: {}:{}:{}", $from, file!(), line!(), column!()) }; -/* */ } -/* */ -/* */ macro_rules! to { -/* */ ($to:expr) => { &format!("to: {}\n to: {}:{}:{}", $to, file!(), line!(), column!()) }; -/* */ } -/* */ -/* */ #[derive(Debug)] -/* */ enum TestEnum { -/* */ Red, -/* */ Green, -/* */ Blue, -/* */ } -/* */ -/* */ struct TestStruct { -/* */ field: i32, -/* */ } -/* */ -/* */ // IMPORTANT! IS WRAPPING main() ENOUGH? OR DO I ALSO NEED TO WRAP THREAD FUNCTIONS, ASSUMING -/* */ // THEY ARE STILL RUNNING WITH MAIN EXITS? (IF THEY CAN). NOT SURE HOW RUST HANDLES THAT. -/* */ -/* */ // I SUSPECT USING THREAD_LOCAL COUNTERS MAY NOT ACTUALLY BE AN OPTIMIZATION OVER MUTEX LOCKS, -/* */ // BUT MAYBE I SHOULD ASK. -/* */ -/* */ impl TestStruct { -/* - */ fn new() -> Self { -/* ┃ */ __incr_cov(to!("end of fn new()")); // function-scoped counter index = 0 -/* ┃ */ Self::new_with_value(31415) -/* - */ } -/* */ -/* - */ fn new_with_value(field: i32) -> Self { -/* ┃ */ __incr_cov(to!("end of fn new_with_value()")); // function-scoped counter index = 0 -/* ┃ */ Self { -/* ┃ */ field, -/* ┃ */ } -/* - */ } -/* */ -/* */ fn call_closure(&self, closure: F) -> bool -/* */ where -/* */ F: FnOnce( -/* */ i32, -/* */ ) -> bool, -/* - */ { -/* ┃ */ __incr_cov(to!("end of fn call_closure()")); // function-scoped counter index = 0 -/* ┃ */ closure(123) -/* - */ } -/* */ -/* - */ fn various(&self) -> Result<(),Error> { -/* ┃ */ __incr_cov(to!("just before next branch: after `match color`: pattern selection")); -/* ┃ */ use TestEnum::*; -/* ┃ */ let mut color = Red; -/* ┃ */ let _ = color; -/* ┃ */ color = Blue; -/* ┃ */ let _ = color; -/* ┃ */ color = Green; -/* ┃ */ match color { // function-scoped counter index = 0 -/* : */ -/* : */ // !!! RECORD SPAN FROM START OF INNERMOST CONTAINING BLOCK (THE FUNCTION IN THIS CASE) TO END OF MATCH EXPRESSION -/* : */ // If `match`, `while`, `loop`, `for`, `if`, etc. expression has a `return`, `break`, or `continue` -/* : */ // (if legal), then RECORD SPAN FROM START OF INNERMOST CONTAINING BLOCK TO END OF `return` EXPRESSION -/* : */ // If the expression includes lazy booleans, nest calls to `__incr_cov()`. -/* : - */ Red => { -/* : ┃ */ __incr_cov(to!("end of matched Red")); -/* : ┃ */ println!("roses"); -/* : - */ } -/* : - */ Green => { -/* : ┃ */ __incr_cov(to!("just before next branch: after `if spidey > goblin`")); -/* : ┃ */ let spidey = 100; -/* : ┃ */ let goblin = 50; -/* : ┃ */ // if spidey > goblin {__incr_cov(from!(""),{ -/* : ┃ */ // println!("what ev"); -/* : ┃ */ // })} -/* : ┃ */ // ACTUALLY, WRAPPING THE ENTIRE IF BLOCK IN `__incr_cov` IS NOT A GREAT GENERAL RULE. -/* : ┃ */ // JUST INSERTING A `return`, `break`, or `continue` IN THAT BLOCK (without an intermediate condition) -/* : ┃ */ // MAKES THE `__incr_cov()` CALL UNREACHABLE! -/* : ┃ */ // MY ORIGINAL SOLUTION WORKS BETTER (WRAP LAST EXPRESSION OR AFTER LAST SEMICOLON STATEMENT IN BLOCK) -/* : ┃ */ // UNLESS THE EXPRESSION IS NOT A BLOCK. -/* : ┃ - */ if spidey > goblin { -/* : : ┃ */ __incr_cov(to!("end of if block, if no earlier branch in this scope")); -/* : : ┃ */ println!("spidey beats goblin"); -/* : : ┃ */ -/* : ┃ - */ } else if { -/* : : : */ // Make sure we can't compute the coverage count here. -/* : : : */ // We know the expression executed if the previous if block DID NOT -/* : : : */ // execute, and either this `else if` block does execute OR any subsequent -/* : : : */ // `else if` or `else` blocks execute, OR none of the blocks in the -/* : : : */ // `if`, `else if` or `else` blocks execute. -/* : : : */ // `if`, `else if` or `else` blocks execute. -/* : : ┃ */ __incr_cov(to!("end of `else if spidey == goblin` expression")); -/* : : ┃ */ spidey == goblin -/* : ┃ - */ } { -/* : : ┃ */ __incr_cov(to!("end of if block, if no earlier branch in this scope")); -/* : : ┃ */ // COVERAGE NOTE: Do we mark only the expression span (that may be trivial, as in this case), -/* : : ┃ */ // or associate it with the outer block, similar to how the `if` expression is associated with -/* : : ┃ */ // the outer block? (Although it is a continuation, in a sense, it is discontiguous in this case, -/* : : ┃ */ // so I think simpler to just make it its own coverage region.) -/* : : ┃ */ println!("it's a draw"); -/* : : ┃ */ -/* : ┃ - - - */ } else if { -/* : : ┃ */ __incr_cov(to!("end of `if true`")); -/* : ┃ - - - */ if true { -/* : : : ┃ */ __incr_cov(to!("end of `return Ok(())`")); -/* ┏-:---:-------:---< */ return Ok(()); -/* V : : ┃ - */ } else { -/* : : : ┃ */ // __incr_cov(to!("end of else block")); -/* : : : ┃ */ // computed counter expression -/* : : : ┃ */ false -/* : : : - */ } -/* : : - - - */ } { -/* : : ┃ */ __incr_cov(to!("end of if block")); -/* : : ┃ */ println!("wierd science"); -/* : ┃ - */ } else { -/* : : ┃ */ // __incr_cov(to!("end of `return Ok(())")); -/* : : ┃ */ // counter expression: (start of Green match arm) - (if spidey > goblin) - (previous `} else if {`) -/* : : ┃ */ println!("goblin wins"); -/* ┏-:---:---< */ return Ok(()); // THIS COUNTS LAST STATEMENT IN `else` BLOCK -/* V : : : */ // COVERAGE NOTE: When counting the span for `return`, -/* : : : */ // `break`, or `continue`, also report the outer spans -/* : : : */ // got this far--including this `else` block. Record -/* : : : */ // The start positions for those outer blocks, but: -/* : : : */ // * For the block containing the `return`, `break`, or -/* : : : */ // `continue`, end report the end position is the -/* : : : */ // start of the `return` span (or 1 char before it). -/* : : : */ // * Anything else? -/* : ┃ - */ } -/* : : */ // __incr_cov(to!("end of matched Green")); -/* : : */ // // DO NOT COUNT HERE IF NO STATEMENTS AFTER LAST `if` or `match` -/* : - */ }, -/* : - */ Blue => { -/* : ┃ */ __incr_cov(to!("end of matched Blue")); -/* : ┃ */ println!("violets"); -/* : - */ } -/* ┃ */ } -/* ┃ */ __incr_cov(to!("just before next branch: after `if condition1` (HIR: 'match condition1')")); -/* ┃ */ -/* ┃ */ let condition1 = true; -/* ┃ */ let condition2 = false; -/* ┃ */ let condition3 = true; -/* ┃ */ -/* ┃ */ println!("Called `various()` for TestStruct with field={}", self.field); -/* ┃ */ -/* ┃ - */ if condition1 { -/* : ┃ */ println!("before while loop"); -/* : ┃ */ let mut countdown = 10; -/* : ┃ */ // Must increment before repeated while text expression -/* : : I */ while countdown > 0 { // span is just the while test expression -/* : : ┃ */ println!("top of `while` loop"); -/* : : ┃ */ countdown -= 1; -/* : : ┃ */ // // Counter not needed, but span is computed as "while test" minus "block start" -/* : : ┃ */ // If test expression is 11, and the outer block runs only once, 11-1 = 10 -/* : ┃ - */ } -/* : ┃ */ println!("before for loop"); -/* : ┃ - */ for index in 0..10 { -/* : : ┃ */ println!("top of `for` loop"); -/* : : ┃ - */ if index == 8 { -/* : : : ┃ */ println!("before break"); -/* : : : ┃ */ // note the following is not legal here: -/* : : : ┃ */ // "can only break with a value inside `loop` or breakable block" -/* : : : ┃ */ // break -/* : : : ┃ */ -/* : : ┏-----< */ break; -/* : : V : : */ -/* : : : : */ // FIXME(richkadel): add examples with loop labels, breaking out of inner and outer loop to outer loop label, with expression. -/* : : : : */ // May want to record both the span and the start position after the broken out block depdnding on label -/* : : ┃ - */ } -/* : : ┃ */ println!("after `break` test"); -/* : : ┃ - */ if condition2 { -/* ┏-:---:---:---< */ return Ok(()); -/* V : : ┃ - */ } -/* : : ┃ */ -/* : : ┃ */ // BECAUSE THE PREVIOUS COVERAGE REGION HAS A `return`, THEN -/* : : ┃ */ // IF PREVIOUS COVERAGE REGION IS NOT COUNTED THEN OUTER REGION REACHED HERE. -/* : : ┃ */ // ADD A COVERAGE REGION FOR THE SPAN FROM JUST AFTER PREVIOUS REGION TO END -/* : : ┃ */ // OF OUTER SPAN, THEN TRUNCATE TO NEXT REGION NOT REACHED. -/* : : ┃ - */ if index % 3 == 2 { // NO __incr_cov() HERE BECAUSE NO STATEMENTS BETWEEN LAST CONDITIONAL BLOCK AND START OF THIS ONE -/* : : Λ : ┃ */ -/* : : ┗-----< */ continue; -/* : : ┃ - */ } -/* : : ┃ */ println!("after `continue` test"); -/* : : ┃ */ // maybe add a runtime flag for a possible `return` here? -/* : : ┃ */ -/* : ┃ - */ } -/* : ┃ */ println!("after for loop"); -/* : ┃ */ let result = if { // START OF NEW CONDITIONAL EXPRESSION. NEXT "GUARANTEED" COUNTER SHOULD COUNT FROM END OF LAST CONDITIONAL EXPRESSION -/* : ┃ */ // A "GUARANTEED" COUNTER CALL IS ONE THAT WILL BE CALLED REGARDLESS OF OTHER CONDITIONS. THIS INCLUDES: -/* : ┃ */ // * A CONDITIONAL EXPRESSION THAT IS NOT A BLOCK (OR ANOTHER CONDITIONAL STATEMENT, WHICH WOULD CONTAIN A BLOCK) -/* : ┃ */ // * OR IF THE NEXT CONDITIONAL EXPRESSION IS A BLOCK OR CONDITIONAL STATEMENT, THEN THE FIRST "GUARANTEED" COUNTER IN THAT BLOCK -/* : ┃ */ // * END OF BLOCK IF THE BLOCK DOES NOT HAVE INNER CONDITIONAL EXPRESSIONS -/* : ┃ */ // * BRANCHING STATEMENTS (`return`, `break`, `continue`) BY EITHER WRAPPING THE BRANCH STATEMENT NON-BLOCK EXPRESSION, -/* : ┃ */ // OR PREPENDING A COUNTER WITH EMPTY TUPLE IF NO EXPRESSION, OR IF EXPRESSION IS A BLOCK, THEN THE NEXT "GUARANTEED" -/* : ┃ */ // COUNTER CALL WITHIN THAT BLOCK. -/* : ┃ */ // BASICALLY, CARRY THE START OF COVERAGE SPAN FORWARD UNTIL THE GUARANTEED COUNTER IS FOUND -/* : ┃ */ println!("after result = if ..."); -/* : ┃ - */ if condition2 { -/* : : ┃ */ println!("before first return"); -/* ┏-:---:-------< */ return Ok(()); -/* V : : - */ } else if condition3 { -/* : : ┃ */ // THE ABOVE COUNTER IS _NOT_ REALLY NECESSARY IF EXPRESSION IS GUARANTEED TO EXECUTE. -/* : : ┃ */ // IF WE GET COUNTER IN `else if` BLOCK WE COVERED EXPRESSION. -/* : : ┃ */ // IF WE GET TO ANY REMAINING `else` or `else if` BLOCK WE KNOW WE EVALUATED THIS CONDITION -/* : : ┃ */ // AND ALL OTHERS UP TO THE EXECUTED BLOCK. BUT THE SPAN WOULD HAVE "HOLES" FOR UNEXECUTED BLOCKS. -/* : : ┃ */ println!("not second return"); -/* ┏-:---:-------< */ return Ok(()); -/* V : : - */ } else { -/* : : ┃ */ println!("not returning"); -/* : : ┃ */ false -/* : : - */ } -/* : ┃ */ // NO COUNTER HERE BECAUSE NO STATEMENTS AFTER CONDITIONAL BLOCK -/* : ┃ - */ } { -/* : : ┃ */ println!("branched condition returned true"); -/* : : ┃ */ Ok(()) -/* : ┃ - */ } else if self.call_closure( -/* : : - */ |closure_param| -/* : : ┃ - */ if condition3 { -/* : : : ┃ */ println!("in closure, captured condition said to print the param {}", closure_param); -/* : : : ┃ */ false -/* : : ┃ - */ } else { -/* : : : ┃ */ println!("in closure, captured condition was false"); -/* : : : ┃ */ true -/* : : ┃ - */ } -/* : : - */ -/* : : - */ ) { -/* : : ┃ */ println!("closure returned true"); -/* : : ┃ */ Err(Error::new(ErrorKind::Other, "Result is error if closure returned true")) -/* : ┃ - */ } else { -/* : : ┃ */ println!("closure returned false"); -/* : : ┃ */ Err(Error::new(ErrorKind::Other, "Result is error if closure returned false")) -/* : ┃ - */ }; -/* : ┃ */ println!("bottom of function might be skipped if early `return`"); -/* : ┃ */ result -/* ┃ - */ } else { -/* : ┃ */ println!("skipping everything in `various()`"); -/* : ┃ */ Ok(()) -/* ┃ - */ } -/* ┃ - */ // 0 // DO NOT COUNT IF NO STATEMENTS AFTER CONDITIONAL BLOCK. ALL COVERAGE IS ALREADY COUNTED -/* - */ } -/* */ } -/* */ -/* - */ fn main() -> Result<(), std::io::Error> { -/* ┃ */ //let mut status: u8 = 2; -/* ┃ */ let mut status: u8 = 1; -/* : - */ let result = if status < 2 && -/* : ┃ */ { -/* : ┃ */ status -= 1; -/* : ┃ */ status == 0 -/* : - - */ } { -/* : ┃ */ let test_struct = TestStruct::new_with_value(100); -/* : ┃ */ let _ = test_struct.various(); -/* ┏-:---< */ return __incr_cov_and_report(from!(""),Err(Error::new(ErrorKind::Other, format!("Error status {}", status)))) -/* V : - */ } else { -/* : ┃ */ let test_struct = TestStruct::new(); -/* : ┃ */ test_struct.various() -/* : - */ }; -/* ┃ */ println!("done"); -/* ┃ */ __incr_cov_and_report(from!(""),result) // function-scoped counter index = 0 -/* - */ } \ No newline at end of file diff --git a/src/test/codegen/coverage-experiments/src/drop_trait.rs b/src/test/codegen/coverage-experiments/src/drop_trait.rs deleted file mode 100644 index 75400e037e9f..000000000000 --- a/src/test/codegen/coverage-experiments/src/drop_trait.rs +++ /dev/null @@ -1,25 +0,0 @@ -#[inline(always)] -pub fn __incr_cov(_region_loc: &str, result: T) -> T { - result -} - -struct Firework { - _strength: i32, -} - -impl Drop for Firework { - fn drop(&mut self) { - __incr_cov("start of drop()", ()); - } -} - -fn main() -> Result<(),u8> { - let _firecracker = Firework { _strength: 1 }; - - if __incr_cov("start of main()", true) { - return __incr_cov("if true", { let _t = Err(1); _t }); - } - - let _tnt = Firework { _strength: 100 }; - Ok(()) -} \ No newline at end of file diff --git a/src/test/codegen/coverage-experiments/src/drop_trait_with_comments_prints.rs b/src/test/codegen/coverage-experiments/src/drop_trait_with_comments_prints.rs deleted file mode 100644 index de9f5d5cb464..000000000000 --- a/src/test/codegen/coverage-experiments/src/drop_trait_with_comments_prints.rs +++ /dev/null @@ -1,53 +0,0 @@ -// -// -// -// It's interesting to speculate if there is a way to leverage the Drop trait functionality -// to increment counters when a scope is closed, but I don't think it would help "out of the box". -// -// A `return` or `break` with expression might not need a temp value expression wrapper -// such as `return { let _t = result_expression; __incr_counter(...); _t };` -// -// ... **if** the __incr_counter() was somehow called from a "drop()" trait function. -// -// The problem is, since the drop call is automatic, there is no way to have argument variants -// depending on where the drop() occurs (e.g., from a `return` statement vs. from the end of -// the function). We need 2 different code regions though. -// -// -// -// - -#[inline(always)] -pub fn __incr_cov(_region_loc: &str, /*index: u32,*/ result: T) -> T { - // println!("from: {}", _region_loc); - result -} - -struct Firework { - strength: i32, -} - -impl Drop for Firework { - fn drop(&mut self) { - println!("BOOM times {}!!!", self.strength); - __incr_cov("start of drop()", ()); - } -} - -fn main() -> Result<(),u8> { - let _firecracker = Firework { strength: 1 }; - - if __incr_cov("start of main()", true) { - return __incr_cov("if true", { let _t = Err(1); println!("computing return value"); _t }); - } - - let _tnt = Firework { strength: 100 }; - // __incr_cov("after if block", Ok(())) // CAN USE COUNTER EXPRESSION: "start of drop()" - "if true" - Ok(()) -} - -// OUTPUT WHEN RUNNING THIS PROGRAM IS AS EXPECTED: - -// computing return value -// BOOM times 1!!! -// Error: 1 diff --git a/src/test/codegen/coverage-experiments/src/for.rs b/src/test/codegen/coverage-experiments/src/for.rs deleted file mode 100644 index 3f44c382a1e3..000000000000 --- a/src/test/codegen/coverage-experiments/src/for.rs +++ /dev/null @@ -1,41 +0,0 @@ -#[inline(always)] -pub fn __incr_cov(_region_loc: &str, /*index: u32,*/ result: T) -> T { - result -} - -fn main() { - for countdown in __incr_cov("start", 10..0) { - let _ = countdown; - __incr_cov("top of for", ()); - } -} - -// LOWERED TO HIR: -// -// fn main() { -// { -// let _t = -// match ::std::iter::IntoIterator::into_iter(__incr_cov("start", -// ::std::ops::Range{start: -// 10, -// end: -// 0,})) -// { -// mut iter => -// loop { -// let mut __next; -// match ::std::iter::Iterator::next(&mut iter) { -// ::std::option::Option::Some(val) => -// __next = val, -// ::std::option::Option::None => break , -// } -// let countdown = __next; -// { -// let _ = countdown; -// __incr_cov("top of for", ()); -// } -// }, -// }; -// _t -// } -// } \ No newline at end of file diff --git a/src/test/codegen/coverage-experiments/src/for_with_comments.rs b/src/test/codegen/coverage-experiments/src/for_with_comments.rs deleted file mode 100644 index 03d11b2c230c..000000000000 --- a/src/test/codegen/coverage-experiments/src/for_with_comments.rs +++ /dev/null @@ -1,24 +0,0 @@ -/* */ #[inline(always)] -/* */ pub fn __incr_cov(_region_loc: &str, /*index: u32,*/ result: T) -> T { -/* */ result -/* */ } -/* */ -/* - */ fn main() { -/* : I */ for countdown in __incr_cov("start", 10..0) { // span is just the while test expression -/* : ┃ */ let _ = countdown; -/* : ┃ */ __incr_cov("top of for", ()); -/* ┃ - */ } -/* - */ } - - -// -Z unpretty=val -- present the input source, unstable (and less-pretty) variants; -// valid types are any of the types for `--pretty`, as well as: -// `expanded`, `expanded,identified`, -// `expanded,hygiene` (with internal representations), -// `everybody_loops` (all function bodies replaced with `loop {}`), -// `hir` (the HIR), `hir,identified`, -// `hir,typed` (HIR with types for each node), -// `hir-tree` (dump the raw HIR), -// `mir` (the MIR), or `mir-cfg` (graphviz formatted MIR) - -// argument to `pretty` must be one of `normal`, `expanded`, `identified`, or `expanded,identified` diff --git a/src/test/codegen/coverage-experiments/src/if.rs b/src/test/codegen/coverage-experiments/src/if.rs deleted file mode 100644 index ad50f6be1900..000000000000 --- a/src/test/codegen/coverage-experiments/src/if.rs +++ /dev/null @@ -1,80 +0,0 @@ -#![feature(core_intrinsics)] - -pub fn __llvm_incr_counter(_region_loc: &str) { -} - -#[inline(always)] -pub fn __incr_cov(region_loc: &str, result: T) -> T { - __llvm_incr_counter(region_loc); - result -} - -static TEST_FUNC_NAME: &'static [u8; 6] = b"main()"; - -fn main() { - let mut countdown = 10; - if __incr_cov("start", countdown > 0) { - - - // // TEST CALLING INTRINSIC: - unsafe { core::intrinsics::instrprof_increment(TEST_FUNC_NAME as *const u8, 1234 as u64, 314 as u32, 31 as u32) }; - // // Results in: - // // LLVM ERROR: Cannot select: intrinsic %llvm.instrprof.increment - // // I may need to pass one or more of the following flags (or equivalent opts) to LLVM to enable this: - // // -fprofile-instr-generate -fcoverage-mapping - - - countdown -= 1; - __incr_cov("if block",()); - } else if countdown > 5 { - countdown -= 2; - __incr_cov("else if block",()); - } else { - countdown -= 3; - } - - let mut countdown = 10; - if { let _tcov = countdown > 0; __llvm_incr_counter("start", ); _tcov } { - countdown -= 1; - __incr_cov("if block",()); - } else if countdown > 5 { - countdown -= 2; - __incr_cov("else if block",()); - } else { - countdown -= 3; - } -} - -// NOTE: hir REDUNDANTLY lowers the manually inlined counter in the second if block to: -// -// match { -// let _t = -// { -// let _tcov = countdown > 0; -// __llvm_incr_counter("start"); -// _tcov -// }; -// _t -// } { - -// I don't know if optimization phases will fix this or not. -// Otherwise, a more optimal (but definitely special case) way to handle this would be -// to inject the counter between the hir-introduced temp `_t` assignment and the block result -// line returning `_t`: -// -// match { -// let _t = countdown > 0; -// __llvm_incr_counter("start"); // <-- the only thing inserted for coverage here -// _t -// } -// -// UNFORTUNATELY THIS IS NOT A PATTERN WE CAN ALWAYS LEVERAGE, FOR EXPRESSIONS THAT HAVE VALUES -// WHERE WE NEED TO INJECT THE COUNTER AFTER THE EXPRESSION BUT BEFORE IT IS USED. -// -// IT DOES APPEAR TO BE THE CASE FOR WHILE EXPRESSIONS, (BECOMES loop { match { let _t = condition; _t} { true => {...} _ => break, }}) -// AND IS TRUE FOR IF EXPRESSIONS AS NOTED -// BUT NOT FOR RETURN STATEMENT (and I'm guessing not for loop { break value; } ? ) -// -// AND NOT FOR LAZY BOOLEAN EXPRESSIONS! -// -// AND NOT FOR MATCH EXPRESSIONS IN THE ORIGINAL SOURCE! \ No newline at end of file diff --git a/src/test/codegen/coverage-experiments/src/if_with_comments.rs b/src/test/codegen/coverage-experiments/src/if_with_comments.rs deleted file mode 100644 index 267e7bca2c5a..000000000000 --- a/src/test/codegen/coverage-experiments/src/if_with_comments.rs +++ /dev/null @@ -1,39 +0,0 @@ -/* */ #[inline(always)] -/* */ pub fn __incr_cov(_region_loc: &str, /*index: u32,*/ result: T) -> T { -/* */ result -/* */ } -/* */ -/* - */ fn main() { -/* ┃ */ let mut countdown = 10; -/* : I */ if __incr_cov("start", countdown > 0) { // span is from start of main() -/* : ┃ */ countdown -= 1; -/* : ┃ */ __incr_cov("if block",()); -/* ┃ - */ } - - let mut countdown = 10; - if __incr_cov("start", countdown > 0) { - countdown -= 1; - __incr_cov("if block",()); - } else if countdown > 5 { // counter expression "start" - "if block" - countdown -= 2; - __incr_cov("else if block",()); - } else { - countdown -= 3; - // __incr_cov("else block",()); // counter expression (countdown > 5 counter expression) - "else if block" - // PLACED AT END OF ELSE BLOCK OR START OF FIRST CONDITIONAL BLOCK, IF ANY (PRESUMING POSSIBLE EARLY EXIT). - // IF WE CAN GUARANTEE NO EARLY EXIT IN THIS BLOCK, THEN AT THE END IS FINE EVEN IF ELSE BLOCK CONTAINS OTHER CONDITIONS. - } - -/* - */ } - -// -Z unpretty=val -- present the input source, unstable (and less-pretty) variants; -// valid types are any of the types for `--pretty`, as well as: -// `expanded`, `expanded,identified`, -// `expanded,hygiene` (with internal representations), -// `everybody_loops` (all function bodies replaced with `loop {}`), -// `hir` (the HIR), `hir,identified`, -// `hir,typed` (HIR with types for each node), -// `hir-tree` (dump the raw HIR), -// `mir` (the MIR), or `mir-cfg` (graphviz formatted MIR) - -// argument to `pretty` must be one of `normal`, `expanded`, `identified`, or `expanded,identified` diff --git a/src/test/codegen/coverage-experiments/src/increment_intrinsic.rs b/src/test/codegen/coverage-experiments/src/increment_intrinsic.rs deleted file mode 100644 index d4708cd367ff..000000000000 --- a/src/test/codegen/coverage-experiments/src/increment_intrinsic.rs +++ /dev/null @@ -1,11 +0,0 @@ -#![feature(core_intrinsics)] - -pub fn not_instrprof_increment(_hash: u64, _num_counters: u32, _index: u32) { -} - -fn main() { - // COMPARE THIS WITH INTRINSIC INSERTION - //not_instrprof_increment(1234 as u64, 314 as u32, 31 as u32); - - unsafe { core::intrinsics::instrprof_increment(1234 as u64, 314 as u32, 31 as u32) }; -} \ No newline at end of file diff --git a/src/test/codegen/coverage-experiments/src/just_main.rs b/src/test/codegen/coverage-experiments/src/just_main.rs deleted file mode 100644 index 081e5d72a6e0..000000000000 --- a/src/test/codegen/coverage-experiments/src/just_main.rs +++ /dev/null @@ -1,3 +0,0 @@ -fn main() { - println!("hello world! (should be covered)"); -} diff --git a/src/test/codegen/coverage-experiments/src/lazy_boolean.rs b/src/test/codegen/coverage-experiments/src/lazy_boolean.rs deleted file mode 100644 index 263277c7cdc4..000000000000 --- a/src/test/codegen/coverage-experiments/src/lazy_boolean.rs +++ /dev/null @@ -1,17 +0,0 @@ -pub fn __llvm_incr_counter(_region_loc: &str) { -} - -#[inline(always)] -pub fn __incr_cov(region_loc: &str, result: T) -> T { - __llvm_incr_counter(region_loc); - result -} - -fn main() { - let a = 1; - let b = 10; - let c = 100; - let _result = __incr_cov("start", a < b) || __incr_cov("or", b < c); - - let _result = { let _t = a < b; __llvm_incr_counter("start"); _t } || { let _t = b < c; __llvm_incr_counter("start"); _t }; -} \ No newline at end of file diff --git a/src/test/codegen/coverage-experiments/src/loop_break_value.rs b/src/test/codegen/coverage-experiments/src/loop_break_value.rs deleted file mode 100644 index 76caa833ec4f..000000000000 --- a/src/test/codegen/coverage-experiments/src/loop_break_value.rs +++ /dev/null @@ -1,15 +0,0 @@ -pub fn __llvm_incr_counter(_region_loc: &str) { -} - -#[inline(always)] -pub fn __incr_cov(region_loc: &str, result: T) -> T { - __llvm_incr_counter(region_loc); - result -} - -fn main() { - __incr_cov("start", ()); - let _result = loop { - break __incr_cov("top of loop", true); - }; -} \ No newline at end of file diff --git a/src/test/codegen/coverage-experiments/src/match.rs b/src/test/codegen/coverage-experiments/src/match.rs deleted file mode 100644 index afbb20888eab..000000000000 --- a/src/test/codegen/coverage-experiments/src/match.rs +++ /dev/null @@ -1,22 +0,0 @@ -pub fn __llvm_incr_counter(_region_loc: &str) { -} - -#[inline(always)] -pub fn __incr_cov(region_loc: &str, result: T) -> T { - __llvm_incr_counter(region_loc); - result -} - -fn main() { - let a = 1; - let b = 10; - let _result = match a < b { - true => true, - _ => false, - }; - - let _result = match __incr_cov("end of first match", a < b) { - true => __incr_cov("matched true", true), - _ => false, // counter expression "end of first match" - "matched true" - }; -} \ No newline at end of file diff --git a/src/test/codegen/coverage-experiments/src/match_with_increment.rs b/src/test/codegen/coverage-experiments/src/match_with_increment.rs deleted file mode 100644 index f618b37ed524..000000000000 --- a/src/test/codegen/coverage-experiments/src/match_with_increment.rs +++ /dev/null @@ -1,305 +0,0 @@ -#![feature(core_intrinsics)] -//static TEST_FUNC_NAME: &'static [u8; 7] = b"main()\0"; - static TEST_FUNC_NAME: &'static [u8; 6] = b"main()"; -fn main() { - let a = 1; - let b = 10; - let _result = match { - let _t = a < b; - unsafe { core::intrinsics::instrprof_increment(TEST_FUNC_NAME as *const u8, 1234 as u64, 3 as u32, 0 as u32) }; - _t - } { - true => { - let _t = true; - unsafe { core::intrinsics::instrprof_increment(TEST_FUNC_NAME as *const u8, 1234 as u64, 3 as u32, 1 as u32) }; - _t - } - _ => false, - }; -} - -/* - -I NEED TO INSERT THE instrprof_increment() CALL: - - 1. JUST BEFORE THE switchInt(_4) (because we haven't counted entering the function main() yet, deferring that to "JUST BEFORE FIRST BRANCH") - 2. SOME TIME AFTER THE switchInt(_4), AND JUST BEFORE ANOTHER BRANCH (in this case, before "goto") - 2.a. NOT BEFORE BOTH GOTO'S AFTER switchInt(_4) (because one can be calculated by counter expression), BUT PERHAPS INSERT A noop PLACEHOLDER - AS A MARKER TO INCLUDE THE COVERAGE REGION AND REFERENCE THE COUNTERS TO BE SUBTRACTED (AND/OR SUMMED)? - - WHY DEFER INSERTING COUNTERS TO "JUST BEFORE FIRST BRANCH"? We can ignore panic/unwind() and only count if the coverage region ACTUALLY - executed in entirety. BUT IS THAT NECESSARY? IS IT MUCH EASIER TO INSERT COUNTERS AT THE TOP OF A REGION THAT MUST EXECUTE IN ENTIRETY IF - PANIC DOES NOT OCCUR? AND WHAT IF WE ADD SUPPORT FOR PANIC UNWIND (later)? - - IS THERE A BENEFIT OF THE DEFERRED APPROACH WHEN CONSIDERING EXPRESSIONS MAY HAVE EARLY RETURNS? (BECAUSE, WE STILL NEED TO COUNT THE REGION - LEADING UP TO THE EXPRESSION ANYWAY) - -================================================= -================================================= - -To inject an intrinsic after computing a final expression value of a coverage region: - -Replace the following basic block end (last statement plus terminator): - -... ... -StorageLive(_4) -StorageLive(_5) -_5 = _1 -StorageLive(_6) -_6 = _2 -_4 = Lt(move _5, move _6) -StorageDead(_6) -StorageDead(_5) - <------ to insert instrprof_increment() here -FakeRead(ForMatchedPlace, _4) --------------------------------------------------------------------------------------- -switchInt(_4) - - -================================================= -Insert call to intrinsic with: - -StorageLive(_4) # _4 is now meant for deferred FakeRead(ForMatchdPlace, _4) in BasicBlock after increment() call -StorageLive(_5) # Unchanged except _4 is now _5 -StorageLive(_6) # Unchanged except _5 is now _6 -_6 = _1 # Unchanged except _5 is now _6 -StorageLive(_7) # Unchanged except _6 is now _7 -_7 = _2 # Unchanged except _6 is now _7 -_5 = Lt(move _6, move _7) # Unchanged except _4, _5, _6 is now _5, _6, _7 -StorageDead(_7) # Unchanged except _6 is now _7 -StorageDead(_6) # Unchanged except _5 is now _6 - -FakeRead(ForLet, _5) # CHANGED ForMatchedPlace to ForLet - -> # ALL NEW AND NECESSARY TO CALL instrprof_increment() -> StorageLive(_8) # ?? stores function pointer to instrprof_increment function? -> StorageLive(_9) -> StorageLive(_10) -> StorageLive(_11) -> _11 = const {alloc1+0: &&[u8; 6]} -> _10 = &raw const (*(*_11)) -> _9 = move _10 as *const u8 (Pointer(ArrayToPointer)) -> StorageDead(_10) -> StorageLive(_12) -> _12 = const 1234u64 -> StorageLive(_13) -> _13 = const 3u32 -> StorageLive(_14) -> _14 = const 0u32 -> -------------------------------------------------------------------------------------- -> _8 = const std::intrinsics::instrprof_increment(move _9, move _12, move _13, move _14) -> -> -> return -> -> StorageDead(_14) -> StorageDead(_13) -> StorageDead(_12) -> StorageDead(_9) -> StorageDead(_11) -> StorageDead(_8) - -_4 = _5 # ARE THESE LINES REDUNDANT? CAN I JUST PASS _5 DIRECTLY TO FakeRead()? -StorageDead(_5) # DROP "_t" temp result of `let _t = a < b` - # (NOTE THAT IF SO, I CAN REMOVE _5 altogether, and use _4, which coincidentally makes less changes) - # SEE BELOW - -FakeRead(ForMatchedPlace, _4) # Unchanged --------------------------------------------------------------------------------------- -switchInt(_4) # Unchanged - - -================================================= -Can I skip the extra variable and insert call to intrinsic with: - -StorageLive(_4) # Unchanged -StorageLive(_5) # Unchanged -_5 = _1 # Unchanged -StorageLive(_6) # Unchanged -_6 = _2 # Unchanged -_4 = Lt(move _5, move _6) # Unchanged -StorageDead(_6) # Unchanged -StorageDead(_5) # Unchanged - -> # ALL NEW AND NECESSARY TO CALL instrprof_increment() -> FakeRead(ForLet, _4) # Save the post-increment result in temp "_t" -> StorageLive(_8) # ?? stores function pointer to instrprof_increment function? -> StorageLive(_9) -> StorageLive(_10) -> StorageLive(_11) -> _11 = const {alloc1+0: &&[u8; 6]} -> _10 = &raw const (*(*_11)) -> _9 = move _10 as *const u8 (Pointer(ArrayToPointer)) -> StorageDead(_10) -> StorageLive(_12) -> _12 = const 1234u64 -> StorageLive(_13) -> _13 = const 3u32 -> StorageLive(_14) -> _14 = const 0u32 -> -------------------------------------------------------------------------------------- -> _8 = const std::intrinsics::instrprof_increment(move _9, move _12, move _13, move _14) -> -> -> return -> -> StorageDead(_14) -> StorageDead(_13) -> StorageDead(_12) -> StorageDead(_9) -> StorageDead(_11) -> StorageDead(_8) - -FakeRead(ForMatchedPlace, _4) # Unchanged (PREVIOUSLY USED IN FakeRead(ForLet), is that OK?) --------------------------------------------------------------------------------------- -switchInt(_4) # Unchanged - - - - - -================================================= -================================================= - -For the second inserted call to instrprof_increment, without that call we have: - --------------------------------------------------------------------------------------- -switchInt(_4) # From above - --> otherwise # that is, "NOT false" - -_3 = const true - <------ to insert instrprof_increment() here --------------------------------------------------------------------------------------- -goto - --> # No label. No condition, and not a "return" - -FakeRead(ForLet, _3) # NOTE: Unused result -StorageDead(_4) -_0 = () -StorageDead(_3) -StorageDead(_2) -StorageDead(_1) --------------------------------------------------------------------------------------- -goto - --> # No label. No condition, and not a "return" - -return # from main() - - -================================================= -With the call to increment(): - --------------------------------------------------------------------------------------- -switchInt(_4) # From above - --> otherwise # "NOT false" # UNCHANGED - -StorageLive(_15) # CHANGED! Allocated new storage (_15) for the result of match, if true. -_15 = const true # UNCHANGED except _3 is now _15 -FakeRead(ForLet, _15) # CHANGED! Assign value to temporary (to be assigned to _3 later) ... Do I need to do this? - -> # ALL NEW AND NECESSARY TO CALL instrprof_increment() -> StorageLive(_16) # pointer to instrprof_increment() function ? -> StorageLive(_17) -> StorageLive(_18) -> StorageLive(_19) -> _19 = const {alloc1+0: &&[u8; 6]} -> _18 = &raw const (*(*_19)) -> _17 = move _18 as *const u8 (Pointer(ArrayToPointer)) -> StorageDead(_18) -> StorageLive(_20) -> _20 = const 1234u64 -> StorageLive(_21) -> _21 = const 3u32 -> StorageLive(_22) -> _22 = const 1u32 -> -------------------------------------------------------------------------------------- -> _16 = const std::intrinsics::instrprof_increment(move _17, move _20, move _21, move _22) -> -> -> return -> -> StorageDead(_22) -> StorageDead(_21) -> StorageDead(_20) -> StorageDead(_17) -> StorageDead(_19) -> StorageDead(_16) -> _3 = _15 -> StorageDead(_15) - ---------------------------------# UNCHANGED------------------------------------------- -goto # UNCHANGED - --> # UNCHANGED - -FakeRead(ForLet, _3) # UNCHANGED -StorageDead(_4) # UNCHANGED -_0 = () # UNCHANGED -StorageDead(_3) # UNCHANGED -StorageDead(_2) # UNCHANGED -StorageDead(_1) # UNCHANGED --------------------------------------------------------------------------------------- -goto # UNCHANGED - --> # UNCHANGED - -return # from main() # UNCHANGED - -================================================= -As before, can I skip the extra variable (_15) and insert the call to intrinsic with _3 directly?: - - --------------------------------------------------------------------------------------- -switchInt(_4) # From above - --> otherwise # "NOT false" # UNCHANGED - -_3 = const true # UNCHANGED? - -> # ALL NEW AND NECESSARY TO CALL instrprof_increment() -> StorageLive(_16) # pointer to instrprof_increment() function ? -> StorageLive(_17) -> StorageLive(_18) -> StorageLive(_19) -> _19 = const {alloc1+0: &&[u8; 6]} -> _18 = &raw const (*(*_19)) -> _17 = move _18 as *const u8 (Pointer(ArrayToPointer)) -> StorageDead(_18) -> StorageLive(_20) -> _20 = const 1234u64 -> StorageLive(_21) -> _21 = const 3u32 -> StorageLive(_22) -> _22 = const 1u32 -> -------------------------------------------------------------------------------------- -> _16 = const std::intrinsics::instrprof_increment(move _17, move _20, move _21, move _22) -> -> -> return -> -> StorageDead(_22) -> StorageDead(_21) -> StorageDead(_20) -> StorageDead(_17) -> StorageDead(_19) -> StorageDead(_16) - ---------------------------------# UNCHANGED------------------------------------------- -goto # UNCHANGED - --> # UNCHANGED - -FakeRead(ForLet, _3) # UNCHANGED -StorageDead(_4) # UNCHANGED -_0 = () # UNCHANGED -StorageDead(_3) # UNCHANGED -StorageDead(_2) # UNCHANGED -StorageDead(_1) # UNCHANGED --------------------------------------------------------------------------------------- -goto # UNCHANGED - --> # UNCHANGED - -return # from main() # UNCHANGED - -*/ \ No newline at end of file diff --git a/src/test/codegen/coverage-experiments/src/match_with_increment_alt.rs b/src/test/codegen/coverage-experiments/src/match_with_increment_alt.rs deleted file mode 100644 index 60586967920c..000000000000 --- a/src/test/codegen/coverage-experiments/src/match_with_increment_alt.rs +++ /dev/null @@ -1,296 +0,0 @@ -#![feature(core_intrinsics)] -//static TEST_FUNC_NAME: &'static [u8; 7] = b"main()\0"; - static TEST_FUNC_NAME: &'static [u8; 6] = b"main()"; -fn main() { - unsafe { core::intrinsics::instrprof_increment(TEST_FUNC_NAME as *const u8, 1234 as u64, 3 as u32, 0 as u32) }; - let a = 1; - let b = 10; - let _result = match a < b { - true => { - unsafe { core::intrinsics::instrprof_increment(TEST_FUNC_NAME as *const u8, 1234 as u64, 3 as u32, 1 as u32) }; - true - } - _ => false, - }; -} - -/* - -ALTERNATE APPROACH: - - IS IT MUCH EASIER TO INSERT COUNTERS AT THE TOP OF A REGION THAT MUST EXECUTE IN ENTIRETY IF - PANIC DOES NOT OCCUR? AND WHAT IF WE ADD SUPPORT FOR PANIC UNWIND (later)? - - IS THERE A DETRACTOR COMPARED TO THE DEFERRED APPROACH WHEN CONSIDERING EXPRESSIONS MAY HAVE EARLY RETURNS? - - (BECAUSE, WE STILL NEED TO COUNT THE REGION LEADING UP TO THE EXPRESSION ANYWAY) - -================================================= -================================================= - -To inject an intrinsic after computing a final expression value of a coverage region: - -Replace the following basic block end (last statement plus terminator): - -... ... -StorageLive(_4) -StorageLive(_5) -_5 = _1 -StorageLive(_6) -_6 = _2 -_4 = Lt(move _5, move _6) -StorageDead(_6) -StorageDead(_5) - <------ to insert instrprof_increment() here -FakeRead(ForMatchedPlace, _4) --------------------------------------------------------------------------------------- -switchInt(_4) - - -================================================= -Insert call to intrinsic with: - -StorageLive(_4) # _4 is now meant for deferred FakeRead(ForMatchdPlace, _4) in BasicBlock after increment() call -StorageLive(_5) # Unchanged except _4 is now _5 -StorageLive(_6) # Unchanged except _5 is now _6 -_6 = _1 # Unchanged except _5 is now _6 -StorageLive(_7) # Unchanged except _6 is now _7 -_7 = _2 # Unchanged except _6 is now _7 -_5 = Lt(move _6, move _7) # Unchanged except _4, _5, _6 is now _5, _6, _7 -StorageDead(_7) # Unchanged except _6 is now _7 -StorageDead(_6) # Unchanged except _5 is now _6 - -FakeRead(ForLet, _5) # CHANGED ForMatchedPlace to ForLet - -> # ALL NEW AND NECESSARY TO CALL instrprof_increment() -> StorageLive(_8) # ?? stores function pointer to instrprof_increment function? -> StorageLive(_9) -> StorageLive(_10) -> StorageLive(_11) -> _11 = const {alloc1+0: &&[u8; 6]} -> _10 = &raw const (*(*_11)) -> _9 = move _10 as *const u8 (Pointer(ArrayToPointer)) -> StorageDead(_10) -> StorageLive(_12) -> _12 = const 1234u64 -> StorageLive(_13) -> _13 = const 3u32 -> StorageLive(_14) -> _14 = const 0u32 -> -------------------------------------------------------------------------------------- -> _8 = const std::intrinsics::instrprof_increment(move _9, move _12, move _13, move _14) -> -> -> return -> -> StorageDead(_14) -> StorageDead(_13) -> StorageDead(_12) -> StorageDead(_9) -> StorageDead(_11) -> StorageDead(_8) - -_4 = _5 # ARE THESE LINES REDUNDANT? CAN I JUST PASS _5 DIRECTLY TO FakeRead()? -StorageDead(_5) # DROP "_t" temp result of `let _t = a < b` - # (NOTE THAT IF SO, I CAN REMOVE _5 altogether, and use _4, which coincidentally makes less changes) - # SEE BELOW - -FakeRead(ForMatchedPlace, _4) # Unchanged --------------------------------------------------------------------------------------- -switchInt(_4) # Unchanged - - -================================================= -Can I skip the extra variable and insert call to intrinsic with: - -StorageLive(_4) # Unchanged -StorageLive(_5) # Unchanged -_5 = _1 # Unchanged -StorageLive(_6) # Unchanged -_6 = _2 # Unchanged -_4 = Lt(move _5, move _6) # Unchanged -StorageDead(_6) # Unchanged -StorageDead(_5) # Unchanged - -> # ALL NEW AND NECESSARY TO CALL instrprof_increment() -> FakeRead(ForLet, _4) # Save the post-increment result in temp "_t" -> StorageLive(_8) # ?? stores function pointer to instrprof_increment function? -> StorageLive(_9) -> StorageLive(_10) -> StorageLive(_11) -> _11 = const {alloc1+0: &&[u8; 6]} -> _10 = &raw const (*(*_11)) -> _9 = move _10 as *const u8 (Pointer(ArrayToPointer)) -> StorageDead(_10) -> StorageLive(_12) -> _12 = const 1234u64 -> StorageLive(_13) -> _13 = const 3u32 -> StorageLive(_14) -> _14 = const 0u32 -> -------------------------------------------------------------------------------------- -> _8 = const std::intrinsics::instrprof_increment(move _9, move _12, move _13, move _14) -> -> -> return -> -> StorageDead(_14) -> StorageDead(_13) -> StorageDead(_12) -> StorageDead(_9) -> StorageDead(_11) -> StorageDead(_8) - -FakeRead(ForMatchedPlace, _4) # Unchanged (PREVIOUSLY USED IN FakeRead(ForLet), is that OK?) --------------------------------------------------------------------------------------- -switchInt(_4) # Unchanged - - - - - -================================================= -================================================= - -For the second inserted call to instrprof_increment, without that call we have: - --------------------------------------------------------------------------------------- -switchInt(_4) # From above - --> otherwise # that is, "NOT false" - -_3 = const true - <------ to insert instrprof_increment() here --------------------------------------------------------------------------------------- -goto - --> # No label. No condition, and not a "return" - -FakeRead(ForLet, _3) # NOTE: Unused result -StorageDead(_4) -_0 = () -StorageDead(_3) -StorageDead(_2) -StorageDead(_1) --------------------------------------------------------------------------------------- -goto - --> # No label. No condition, and not a "return" - -return # from main() - - -================================================= -With the call to increment(): - --------------------------------------------------------------------------------------- -switchInt(_4) # From above - --> otherwise # "NOT false" # UNCHANGED - -StorageLive(_15) # CHANGED! Allocated new storage (_15) for the result of match, if true. -_15 = const true # UNCHANGED except _3 is now _15 -FakeRead(ForLet, _15) # CHANGED! Assign value to temporary (to be assigned to _3 later) ... Do I need to do this? - -> # ALL NEW AND NECESSARY TO CALL instrprof_increment() -> StorageLive(_16) # pointer to instrprof_increment() function ? -> StorageLive(_17) -> StorageLive(_18) -> StorageLive(_19) -> _19 = const {alloc1+0: &&[u8; 6]} -> _18 = &raw const (*(*_19)) -> _17 = move _18 as *const u8 (Pointer(ArrayToPointer)) -> StorageDead(_18) -> StorageLive(_20) -> _20 = const 1234u64 -> StorageLive(_21) -> _21 = const 3u32 -> StorageLive(_22) -> _22 = const 1u32 -> -------------------------------------------------------------------------------------- -> _16 = const std::intrinsics::instrprof_increment(move _17, move _20, move _21, move _22) -> -> -> return -> -> StorageDead(_22) -> StorageDead(_21) -> StorageDead(_20) -> StorageDead(_17) -> StorageDead(_19) -> StorageDead(_16) -> _3 = _15 -> StorageDead(_15) - ---------------------------------# UNCHANGED------------------------------------------- -goto # UNCHANGED - --> # UNCHANGED - -FakeRead(ForLet, _3) # UNCHANGED -StorageDead(_4) # UNCHANGED -_0 = () # UNCHANGED -StorageDead(_3) # UNCHANGED -StorageDead(_2) # UNCHANGED -StorageDead(_1) # UNCHANGED --------------------------------------------------------------------------------------- -goto # UNCHANGED - --> # UNCHANGED - -return # from main() # UNCHANGED - -================================================= -As before, can I skip the extra variable (_15) and insert the call to intrinsic with _3 directly?: - - --------------------------------------------------------------------------------------- -switchInt(_4) # From above - --> otherwise # "NOT false" # UNCHANGED - -_3 = const true # UNCHANGED? - -> # ALL NEW AND NECESSARY TO CALL instrprof_increment() -> StorageLive(_16) # pointer to instrprof_increment() function ? -> StorageLive(_17) -> StorageLive(_18) -> StorageLive(_19) -> _19 = const {alloc1+0: &&[u8; 6]} -> _18 = &raw const (*(*_19)) -> _17 = move _18 as *const u8 (Pointer(ArrayToPointer)) -> StorageDead(_18) -> StorageLive(_20) -> _20 = const 1234u64 -> StorageLive(_21) -> _21 = const 3u32 -> StorageLive(_22) -> _22 = const 1u32 -> -------------------------------------------------------------------------------------- -> _16 = const std::intrinsics::instrprof_increment(move _17, move _20, move _21, move _22) -> -> -> return -> -> StorageDead(_22) -> StorageDead(_21) -> StorageDead(_20) -> StorageDead(_17) -> StorageDead(_19) -> StorageDead(_16) - ---------------------------------# UNCHANGED------------------------------------------- -goto # UNCHANGED - --> # UNCHANGED - -FakeRead(ForLet, _3) # UNCHANGED -StorageDead(_4) # UNCHANGED -_0 = () # UNCHANGED -StorageDead(_3) # UNCHANGED -StorageDead(_2) # UNCHANGED -StorageDead(_1) # UNCHANGED --------------------------------------------------------------------------------------- -goto # UNCHANGED - --> # UNCHANGED - -return # from main() # UNCHANGED - -*/ \ No newline at end of file diff --git a/src/test/codegen/coverage-experiments/src/match_without_increment.mir b/src/test/codegen/coverage-experiments/src/match_without_increment.mir deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/src/test/codegen/coverage-experiments/src/match_without_increment.rs b/src/test/codegen/coverage-experiments/src/match_without_increment.rs deleted file mode 100644 index fa85833e0543..000000000000 --- a/src/test/codegen/coverage-experiments/src/match_without_increment.rs +++ /dev/null @@ -1,5 +0,0 @@ -fn main() { - let a = 1; - let b = 10; - let _result = match a < b { true => true, _ => false, }; -} \ No newline at end of file diff --git a/src/test/codegen/coverage-experiments/src/match_without_increment_alt.mir b/src/test/codegen/coverage-experiments/src/match_without_increment_alt.mir deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/src/test/codegen/coverage-experiments/src/question_mark_err_status_handling_with_comments.rs b/src/test/codegen/coverage-experiments/src/question_mark_err_status_handling_with_comments.rs deleted file mode 100644 index 03d11b2c230c..000000000000 --- a/src/test/codegen/coverage-experiments/src/question_mark_err_status_handling_with_comments.rs +++ /dev/null @@ -1,24 +0,0 @@ -/* */ #[inline(always)] -/* */ pub fn __incr_cov(_region_loc: &str, /*index: u32,*/ result: T) -> T { -/* */ result -/* */ } -/* */ -/* - */ fn main() { -/* : I */ for countdown in __incr_cov("start", 10..0) { // span is just the while test expression -/* : ┃ */ let _ = countdown; -/* : ┃ */ __incr_cov("top of for", ()); -/* ┃ - */ } -/* - */ } - - -// -Z unpretty=val -- present the input source, unstable (and less-pretty) variants; -// valid types are any of the types for `--pretty`, as well as: -// `expanded`, `expanded,identified`, -// `expanded,hygiene` (with internal representations), -// `everybody_loops` (all function bodies replaced with `loop {}`), -// `hir` (the HIR), `hir,identified`, -// `hir,typed` (HIR with types for each node), -// `hir-tree` (dump the raw HIR), -// `mir` (the MIR), or `mir-cfg` (graphviz formatted MIR) - -// argument to `pretty` must be one of `normal`, `expanded`, `identified`, or `expanded,identified` diff --git a/src/test/codegen/coverage-experiments/src/while.rs b/src/test/codegen/coverage-experiments/src/while.rs deleted file mode 100644 index 3cb185eda544..000000000000 --- a/src/test/codegen/coverage-experiments/src/while.rs +++ /dev/null @@ -1,23 +0,0 @@ -#[inline(always)] -pub fn __incr_cov(_region_loc: &str, result: T) -> T { - result -} - -fn main() { - let mut countdown = 10; - __incr_cov("block start",()); - while __incr_cov("while test", countdown > 0) { - countdown -= 1; - } - - let mut countdown = 10; - __incr_cov("after first while loop",()); - while __incr_cov("while test", countdown > 0) { - countdown -= 1; - if countdown < 5 { - __incr_cov("top of if countdown < 5",()); - break; - } - countdown -= 2; - } -} \ No newline at end of file diff --git a/src/test/codegen/coverage-experiments/src/while_clean.rs b/src/test/codegen/coverage-experiments/src/while_clean.rs deleted file mode 100644 index e9ed1efc220d..000000000000 --- a/src/test/codegen/coverage-experiments/src/while_clean.rs +++ /dev/null @@ -1,6 +0,0 @@ -fn main() { - let mut countdown = 10; - while countdown > 0 { - countdown -= 1; - } -} \ No newline at end of file diff --git a/src/test/codegen/coverage-experiments/src/while_early_return.rs b/src/test/codegen/coverage-experiments/src/while_early_return.rs deleted file mode 100644 index 35709ffba3a0..000000000000 --- a/src/test/codegen/coverage-experiments/src/while_early_return.rs +++ /dev/null @@ -1,10 +0,0 @@ -fn main() -> u8 { // this will lower to HIR but will not compile: `main` can only return types that implement `std::process::Termination` - let mut countdown = 10; - while countdown > 0 { - if false { - return if countdown > 8 { 1 } else { return 2; }; - } - countdown -= 1; - } - 0 -} \ No newline at end of file diff --git a/src/test/codegen/coverage-experiments/src/while_with_comments.rs b/src/test/codegen/coverage-experiments/src/while_with_comments.rs deleted file mode 100644 index 56417fedf00d..000000000000 --- a/src/test/codegen/coverage-experiments/src/while_with_comments.rs +++ /dev/null @@ -1,51 +0,0 @@ -/* */ #[inline(always)] -/* */ pub fn __incr_cov(_region_loc: &str, /*index: u32,*/ result: T) -> T { -/* */ result -/* */ } -/* */ -/* - */ fn main() { -/* ┃ */ let mut countdown = 10; -/* ┃ */ __incr_cov("block start",()); // Must increment before repeated while text expression -/* : I */ while __incr_cov("while test", countdown > 0) { // span is just the while test expression -/* : ┃ */ countdown -= 1; -/* : ┃ */ // __incr_cov("while loop",()); // Counter not needed, but span is computed as "while test" minus "block start" -/* : ┃ */ // If while criteria is tested 11 times, and the outer block runs only once, 11-1 = 10 -/* : ┃ */ // REMOVING COUNTER ASSUMES NO EARLY RETURN THOUGH. -/* : ┃ */ // I THINK WE CAN ONLY USE THE COUNTER EXPRESSION UP TO FIRST CONDITIONAL BLOCK, IF ANY (if, match, maybe any loop) -/* ┃ - */ } - - let mut countdown = 10; - __incr_cov("after first while loop",()); - while __incr_cov("while test", countdown > 0) { - countdown -= 1; - // if __incr_cov("top of while loop", countdown < 5) { - if countdown < 5 { // "top of while loop" = counter expression "while test" - "after first while loop" - __incr_cov("top of if countdown < 5",()); - break; - } - countdown -= 2; - // __incr_cov("after if countdown < 5 block", ()); - // "after if countdown < 5 block" = counter expression "top of while loop" - "top of if countdown < 5" - // HOWEVER, WE CAN ONLY REMOVE THE COUNTER AND USE COUNTER EXPRESSION IF WE **KNOW** THAT THE BODY OF THE IF - // WILL **ALWAYS** BREAK (OR RETURN, OR CONTINUE?) - // AND THUS WE TREAT THE STATEMENTS FOLLOWING THE IF BLOCK AS IF THEY WERE AN ELSE BLOCK. - // THAT'S A LOT TO ASK. - - // PERHAPS TREAT EARLY RETURNS AS A SPECIAL KIND OF COUNTER AND IF ANY ARE INVOKED BEFORE STATEMENTS AFTER THE BLOCK THAT CONTAINS THEM, - // THEN SUBTRACT THOSE COUNTS FROM THE COUNT BEFORE THE BLOCK (AS WE DO HERE)? (SO ONE SET OF EXPRESSIONS MUST SUM ALL OF THE EARLY - // RETURNS) - } -/* - */ } - - -// -Z unpretty=val -- present the input source, unstable (and less-pretty) variants; -// valid types are any of the types for `--pretty`, as well as: -// `expanded`, `expanded,identified`, -// `expanded,hygiene` (with internal representations), -// `everybody_loops` (all function bodies replaced with `loop {}`), -// `hir` (the HIR), `hir,identified`, -// `hir,typed` (HIR with types for each node), -// `hir-tree` (dump the raw HIR), -// `mir` (the MIR), or `mir-cfg` (graphviz formatted MIR) - -// argument to `pretty` must be one of `normal`, `expanded`, `identified`, or `expanded,identified` From d2cd59a0315809afa58df0196c34b33ee0a8c161 Mon Sep 17 00:00:00 2001 From: Rich Kadel Date: Fri, 5 Jun 2020 09:14:45 -0700 Subject: [PATCH 073/123] Add case for count_code_region() extern lang_item As suggested in PR feedback: https://github.com/rust-lang/rust/pull/73011#discussion_r435728923 This allows count_code_region() to be handled like a normal intrinsic so the InstanceDef::InjectedCode variant is no longer needed. --- src/libcore/intrinsics.rs | 21 ++++++------------- src/librustc_codegen_ssa/mir/block.rs | 5 +---- src/librustc_middle/mir/mono.rs | 1 - src/librustc_middle/ty/instance.rs | 7 ------- src/librustc_middle/ty/mod.rs | 1 - src/librustc_middle/ty/structural_impls.rs | 11 +++------- src/librustc_mir/interpret/terminator.rs | 3 --- src/librustc_mir/monomorphize/collector.rs | 5 +---- src/librustc_mir/monomorphize/partitioning.rs | 2 -- src/librustc_mir/shim.rs | 3 --- src/librustc_passes/weak_lang_items.rs | 16 ++++++++++++-- src/librustc_ty/instance.rs | 4 ---- src/librustc_typeck/check/intrinsic.rs | 2 ++ 13 files changed, 27 insertions(+), 54 deletions(-) diff --git a/src/libcore/intrinsics.rs b/src/libcore/intrinsics.rs index 06a432a26961..7ce5814d39a0 100644 --- a/src/libcore/intrinsics.rs +++ b/src/libcore/intrinsics.rs @@ -1941,22 +1941,13 @@ extern "rust-intrinsic" { /// /// Perma-unstable: do not use. pub fn miri_start_panic(payload: *mut u8) -> !; -} -/// Defines the `count_code_region` intrinsic as a `LangItem`. `LangItem`s require a function body -/// to register its DefId with the LangItem entry. The function body is never actually called (and -/// is therefore implemented as an aborting stub) because it is replaced with the LLVM intrinsic -/// `llvm.instrprof.increment` by -/// `rustc_codegen_llvm::intrinsic::IntrinsicCallMethods::codegen_intrinsic_call()`. -#[cfg(not(bootstrap))] -#[cfg_attr(not(bootstrap), lang = "count_code_region")] -fn count_code_region(_index: u32) { - // remove `unsafe` (and safety comment) on bootstrap bump - #[cfg_attr(not(bootstrap), allow(unused_unsafe))] - // SAFETY: the `abort` intrinsic has no requirements to be called. - unsafe { - abort() - } + /// Internal placeholder for injecting code coverage counters when the "instrument-coverage" + /// option is enabled. The placeholder is replaced with `llvm.instrprof.increment` during code + /// generation. + #[cfg(not(bootstrap))] + #[cfg_attr(not(bootstrap), lang = "count_code_region")] + pub fn count_code_region(_index: u32); } // Some functions are defined here because they accidentally got made diff --git a/src/librustc_codegen_ssa/mir/block.rs b/src/librustc_codegen_ssa/mir/block.rs index d7db65715499..665ef7709098 100644 --- a/src/librustc_codegen_ssa/mir/block.rs +++ b/src/librustc_codegen_ssa/mir/block.rs @@ -566,10 +566,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { // Handle intrinsics old codegen wants Expr's for, ourselves. let intrinsic = match def { - Some(ty::InstanceDef::Intrinsic(def_id)) - | Some(ty::InstanceDef::InjectedCode(def_id)) => { - Some(bx.tcx().item_name(def_id).as_str()) - } + Some(ty::InstanceDef::Intrinsic(def_id)) => Some(bx.tcx().item_name(def_id).as_str()), _ => None, }; let intrinsic = intrinsic.as_ref().map(|s| &s[..]); diff --git a/src/librustc_middle/mir/mono.rs b/src/librustc_middle/mir/mono.rs index b2c00849d9f8..c889dbc0a449 100644 --- a/src/librustc_middle/mir/mono.rs +++ b/src/librustc_middle/mir/mono.rs @@ -352,7 +352,6 @@ impl<'tcx> CodegenUnit<'tcx> { InstanceDef::VtableShim(..) | InstanceDef::ReifyShim(..) | InstanceDef::Intrinsic(..) - | InstanceDef::InjectedCode(..) | InstanceDef::FnPtrShim(..) | InstanceDef::Virtual(..) | InstanceDef::ClosureOnceShim { .. } diff --git a/src/librustc_middle/ty/instance.rs b/src/librustc_middle/ty/instance.rs index 4f88e64c5039..1ce079821a22 100644 --- a/src/librustc_middle/ty/instance.rs +++ b/src/librustc_middle/ty/instance.rs @@ -21,10 +21,6 @@ pub enum InstanceDef<'tcx> { Item(DefId), Intrinsic(DefId), - /// Injected call to a placeholder function that is replaced with - /// For example: `core::intrinsic::count_code_region()` for code coverage. - InjectedCode(DefId), - /// `::method` where `method` receives unsizeable `self: Self`. VtableShim(DefId), @@ -153,7 +149,6 @@ impl<'tcx> InstanceDef<'tcx> { | InstanceDef::FnPtrShim(def_id, _) | InstanceDef::Virtual(def_id, _) | InstanceDef::Intrinsic(def_id) - | InstanceDef::InjectedCode(def_id) | InstanceDef::ClosureOnceShim { call_once: def_id } | InstanceDef::DropGlue(def_id, _) | InstanceDef::CloneShim(def_id, _) => def_id, @@ -241,7 +236,6 @@ impl<'tcx> fmt::Display for Instance<'tcx> { InstanceDef::VtableShim(_) => write!(f, " - shim(vtable)"), InstanceDef::ReifyShim(_) => write!(f, " - shim(reify)"), InstanceDef::Intrinsic(_) => write!(f, " - intrinsic"), - InstanceDef::InjectedCode(_) => write!(f, " - injected-code"), InstanceDef::Virtual(_, num) => write!(f, " - virtual#{}", num), InstanceDef::FnPtrShim(_, ty) => write!(f, " - shim({:?})", ty), InstanceDef::ClosureOnceShim { .. } => write!(f, " - shim"), @@ -421,7 +415,6 @@ impl<'tcx> Instance<'tcx> { | InstanceDef::FnPtrShim(..) | InstanceDef::Item(_) | InstanceDef::Intrinsic(..) - | InstanceDef::InjectedCode(..) | InstanceDef::ReifyShim(..) | InstanceDef::Virtual(..) | InstanceDef::VtableShim(..) => Some(self.substs), diff --git a/src/librustc_middle/ty/mod.rs b/src/librustc_middle/ty/mod.rs index 9b1e717731e8..93ef73171993 100644 --- a/src/librustc_middle/ty/mod.rs +++ b/src/librustc_middle/ty/mod.rs @@ -2717,7 +2717,6 @@ impl<'tcx> TyCtxt<'tcx> { ty::InstanceDef::VtableShim(..) | ty::InstanceDef::ReifyShim(..) | ty::InstanceDef::Intrinsic(..) - | ty::InstanceDef::InjectedCode(..) | ty::InstanceDef::FnPtrShim(..) | ty::InstanceDef::Virtual(..) | ty::InstanceDef::ClosureOnceShim { .. } diff --git a/src/librustc_middle/ty/structural_impls.rs b/src/librustc_middle/ty/structural_impls.rs index b6cbd2082a51..f6f5dfd65161 100644 --- a/src/librustc_middle/ty/structural_impls.rs +++ b/src/librustc_middle/ty/structural_impls.rs @@ -674,7 +674,6 @@ impl<'a, 'tcx> Lift<'tcx> for ty::InstanceDef<'a> { ty::InstanceDef::VtableShim(def_id) => Some(ty::InstanceDef::VtableShim(def_id)), ty::InstanceDef::ReifyShim(def_id) => Some(ty::InstanceDef::ReifyShim(def_id)), ty::InstanceDef::Intrinsic(def_id) => Some(ty::InstanceDef::Intrinsic(def_id)), - ty::InstanceDef::InjectedCode(def_id) => Some(ty::InstanceDef::Intrinsic(def_id)), ty::InstanceDef::FnPtrShim(def_id, ref ty) => { Some(ty::InstanceDef::FnPtrShim(def_id, tcx.lift(ty)?)) } @@ -847,7 +846,6 @@ impl<'tcx> TypeFoldable<'tcx> for ty::instance::Instance<'tcx> { VtableShim(did) => VtableShim(did.fold_with(folder)), ReifyShim(did) => ReifyShim(did.fold_with(folder)), Intrinsic(did) => Intrinsic(did.fold_with(folder)), - InjectedCode(did) => InjectedCode(did.fold_with(folder)), FnPtrShim(did, ty) => FnPtrShim(did.fold_with(folder), ty.fold_with(folder)), Virtual(did, i) => Virtual(did.fold_with(folder), i), ClosureOnceShim { call_once } => { @@ -863,12 +861,9 @@ impl<'tcx> TypeFoldable<'tcx> for ty::instance::Instance<'tcx> { use crate::ty::InstanceDef::*; self.substs.visit_with(visitor) || match self.def { - Item(did) - | VtableShim(did) - | ReifyShim(did) - | Intrinsic(did) - | InjectedCode(did) - | Virtual(did, _) => did.visit_with(visitor), + Item(did) | VtableShim(did) | ReifyShim(did) | Intrinsic(did) | Virtual(did, _) => { + did.visit_with(visitor) + } FnPtrShim(did, ty) | CloneShim(did, ty) => { did.visit_with(visitor) || ty.visit_with(visitor) } diff --git a/src/librustc_mir/interpret/terminator.rs b/src/librustc_mir/interpret/terminator.rs index 82fa471b54d7..cd7621ea9752 100644 --- a/src/librustc_mir/interpret/terminator.rs +++ b/src/librustc_mir/interpret/terminator.rs @@ -257,9 +257,6 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { assert!(caller_abi == Abi::RustIntrinsic || caller_abi == Abi::PlatformIntrinsic); M::call_intrinsic(self, instance, args, ret, unwind) } - ty::InstanceDef::InjectedCode(..) => { - M::call_intrinsic(self, instance, args, ret, unwind) - } ty::InstanceDef::VtableShim(..) | ty::InstanceDef::ReifyShim(..) | ty::InstanceDef::ClosureOnceShim { .. } diff --git a/src/librustc_mir/monomorphize/collector.rs b/src/librustc_mir/monomorphize/collector.rs index 24c4226bb4e9..994d1e69f2e3 100644 --- a/src/librustc_mir/monomorphize/collector.rs +++ b/src/librustc_mir/monomorphize/collector.rs @@ -714,9 +714,7 @@ fn visit_instance_use<'tcx>( } match instance.def { - ty::InstanceDef::Virtual(..) - | ty::InstanceDef::Intrinsic(_) - | ty::InstanceDef::InjectedCode(_) => { + ty::InstanceDef::Virtual(..) | ty::InstanceDef::Intrinsic(_) => { if !is_direct_call { bug!("{:?} being reified", instance); } @@ -753,7 +751,6 @@ fn should_monomorphize_locally<'tcx>(tcx: TyCtxt<'tcx>, instance: &Instance<'tcx | ty::InstanceDef::FnPtrShim(..) | ty::InstanceDef::DropGlue(..) | ty::InstanceDef::Intrinsic(_) - | ty::InstanceDef::InjectedCode(_) | ty::InstanceDef::CloneShim(..) => return true, }; diff --git a/src/librustc_mir/monomorphize/partitioning.rs b/src/librustc_mir/monomorphize/partitioning.rs index 7c97b9d611e1..db1ea72c0a53 100644 --- a/src/librustc_mir/monomorphize/partitioning.rs +++ b/src/librustc_mir/monomorphize/partitioning.rs @@ -322,7 +322,6 @@ fn mono_item_visibility( | InstanceDef::FnPtrShim(..) | InstanceDef::Virtual(..) | InstanceDef::Intrinsic(..) - | InstanceDef::InjectedCode(..) | InstanceDef::ClosureOnceShim { .. } | InstanceDef::DropGlue(..) | InstanceDef::CloneShim(..) => return Visibility::Hidden, @@ -718,7 +717,6 @@ fn characteristic_def_id_of_mono_item<'tcx>( | ty::InstanceDef::FnPtrShim(..) | ty::InstanceDef::ClosureOnceShim { .. } | ty::InstanceDef::Intrinsic(..) - | ty::InstanceDef::InjectedCode(..) | ty::InstanceDef::DropGlue(..) | ty::InstanceDef::Virtual(..) | ty::InstanceDef::CloneShim(..) => return None, diff --git a/src/librustc_mir/shim.rs b/src/librustc_mir/shim.rs index b4477d9c86d4..f95fd9b9e90c 100644 --- a/src/librustc_mir/shim.rs +++ b/src/librustc_mir/shim.rs @@ -109,9 +109,6 @@ fn make_shim<'tcx>(tcx: TyCtxt<'tcx>, instance: ty::InstanceDef<'tcx>) -> Body<' ty::InstanceDef::Intrinsic(_) => { bug!("creating shims from intrinsics ({:?}) is unsupported", instance) } - ty::InstanceDef::InjectedCode(_) => { - bug!("creating shims from injected code ({:?}) is unsupported", instance) - } }; debug!("make_shim({:?}) = untransformed {:?}", instance, result); diff --git a/src/librustc_passes/weak_lang_items.rs b/src/librustc_passes/weak_lang_items.rs index 96ec23692df5..f2f07b5d4fb2 100644 --- a/src/librustc_passes/weak_lang_items.rs +++ b/src/librustc_passes/weak_lang_items.rs @@ -5,10 +5,12 @@ use rustc_errors::struct_span_err; use rustc_hir as hir; use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor}; use rustc_hir::lang_items; +use rustc_hir::lang_items::ITEM_REFS; use rustc_hir::weak_lang_items::WEAK_ITEMS_REFS; use rustc_middle::middle::lang_items::whitelisted; use rustc_middle::ty::TyCtxt; use rustc_session::config::CrateType; +use rustc_span::symbol::sym; use rustc_span::symbol::Symbol; use rustc_span::Span; @@ -70,11 +72,21 @@ fn verify<'tcx>(tcx: TyCtxt<'tcx>, items: &lang_items::LanguageItems) { } impl<'a, 'tcx> Context<'a, 'tcx> { - fn register(&mut self, name: Symbol, span: Span) { + fn register(&mut self, name: Symbol, span: Span, hir_id: hir::HirId) { if let Some(&item) = WEAK_ITEMS_REFS.get(&name) { if self.items.require(item).is_err() { self.items.missing.push(item); } + } else if name == sym::count_code_region { + // `core::intrinsics::code_count_region()` is (currently) the only `extern` lang item + // that is never actually linked. It is not a `weak_lang_item` that can be registered + // when used, and should be registered here instead. + if let Some((item_index, _)) = ITEM_REFS.get(&*name.as_str()).cloned() { + if self.items.items[item_index].is_none() { + let item_def_id = self.tcx.hir().local_def_id(hir_id).to_def_id(); + self.items.items[item_index] = Some(item_def_id); + } + } } else { struct_span_err!(self.tcx.sess, span, E0264, "unknown external lang item: `{}`", name) .emit(); @@ -91,7 +103,7 @@ impl<'a, 'tcx, 'v> Visitor<'v> for Context<'a, 'tcx> { fn visit_foreign_item(&mut self, i: &hir::ForeignItem<'_>) { if let Some((lang_item, _)) = hir::lang_items::extract(&i.attrs) { - self.register(lang_item, i.span); + self.register(lang_item, i.span, i.hir_id); } intravisit::walk_foreign_item(self, i) } diff --git a/src/librustc_ty/instance.rs b/src/librustc_ty/instance.rs index d4ceeff32445..0acf76916813 100644 --- a/src/librustc_ty/instance.rs +++ b/src/librustc_ty/instance.rs @@ -35,10 +35,6 @@ fn resolve_instance<'tcx>( debug!(" => intrinsic"); ty::InstanceDef::Intrinsic(def_id) } - ty::FnDef(def_id, _) if Some(def_id) == tcx.lang_items().count_code_region_fn() => { - debug!(" => injected placeholder function to be replaced"); - ty::InstanceDef::InjectedCode(def_id) - } ty::FnDef(def_id, substs) if Some(def_id) == tcx.lang_items().drop_in_place_fn() => { let ty = substs.type_at(0); diff --git a/src/librustc_typeck/check/intrinsic.rs b/src/librustc_typeck/check/intrinsic.rs index bded2c695c9d..3ec6973a17d5 100644 --- a/src/librustc_typeck/check/intrinsic.rs +++ b/src/librustc_typeck/check/intrinsic.rs @@ -347,6 +347,8 @@ pub fn check_intrinsic_type(tcx: TyCtxt<'_>, it: &hir::ForeignItem<'_>) { return; } + "count_code_region" => (0, vec![tcx.types.u32], tcx.mk_unit()), + ref other => { struct_span_err!( tcx.sess, From e4df7e70466611a49d3ff6c49d162b2045173449 Mon Sep 17 00:00:00 2001 From: Rich Kadel Date: Fri, 5 Jun 2020 09:49:31 -0700 Subject: [PATCH 074/123] Update src/libcore/intrinsics.rs Co-authored-by: bjorn3 --- src/libcore/intrinsics.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libcore/intrinsics.rs b/src/libcore/intrinsics.rs index 7ce5814d39a0..3806d3ae2548 100644 --- a/src/libcore/intrinsics.rs +++ b/src/libcore/intrinsics.rs @@ -1946,7 +1946,7 @@ extern "rust-intrinsic" { /// option is enabled. The placeholder is replaced with `llvm.instrprof.increment` during code /// generation. #[cfg(not(bootstrap))] - #[cfg_attr(not(bootstrap), lang = "count_code_region")] + #[lang = "count_code_region"] pub fn count_code_region(_index: u32); } From 7e49a9ec59f7950efa9950b65c10f9b3f3a4b6b2 Mon Sep 17 00:00:00 2001 From: Rich Kadel Date: Sun, 7 Jun 2020 19:35:15 -0700 Subject: [PATCH 075/123] moved to post_borrowck_cleanup & used MirPatch --- src/librustc_mir/interpret/intrinsics.rs | 1 + .../transform/instrument_coverage.rs | 112 +++++++++--------- src/librustc_mir/transform/mod.rs | 6 +- src/librustc_session/options.rs | 4 +- 4 files changed, 65 insertions(+), 58 deletions(-) diff --git a/src/librustc_mir/interpret/intrinsics.rs b/src/librustc_mir/interpret/intrinsics.rs index 47e5b8b4fcec..4d8120794f88 100644 --- a/src/librustc_mir/interpret/intrinsics.rs +++ b/src/librustc_mir/interpret/intrinsics.rs @@ -389,6 +389,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { ); self.copy_op(self.operand_index(args[0], index)?, dest)?; } + sym::count_code_region => (), _ => return Ok(false), } diff --git a/src/librustc_mir/transform/instrument_coverage.rs b/src/librustc_mir/transform/instrument_coverage.rs index 045cd03d1f7d..0604caadaea3 100644 --- a/src/librustc_mir/transform/instrument_coverage.rs +++ b/src/librustc_mir/transform/instrument_coverage.rs @@ -1,8 +1,7 @@ use crate::transform::{MirPass, MirSource}; -use rustc_index::vec::Idx; +use crate::util::patch::MirPatch; use rustc_middle::mir::interpret::Scalar; use rustc_middle::mir::*; -use rustc_middle::mir::{Local, LocalDecl}; use rustc_middle::ty; use rustc_middle::ty::Ty; use rustc_middle::ty::TyCtxt; @@ -16,69 +15,62 @@ pub struct InstrumentCoverage; * the intrinsic llvm.instrprof.increment. */ -// FIXME(richkadel): As a first step, counters are only injected at the top of each function. -// The complete solution will inject counters at each conditional code branch. - impl<'tcx> MirPass<'tcx> for InstrumentCoverage { fn run_pass(&self, tcx: TyCtxt<'tcx>, src: MirSource<'tcx>, body: &mut Body<'tcx>) { if tcx.sess.opts.debugging_opts.instrument_coverage { - if let Some(callee_fn_def_id) = tcx.lang_items().count_code_region_fn() { - debug!("instrumenting {:?}", src.def_id()); - instrument_coverage(tcx, callee_fn_def_id, body); - } + debug!("instrumenting {:?}", src.def_id()); + instrument_coverage(tcx, body); } } } -pub fn instrument_coverage<'tcx>( - tcx: TyCtxt<'tcx>, - callee_fn_def_id: DefId, - body: &mut Body<'tcx>, -) { +// The first counter (start of the function) is index zero. +const INIT_FUNCTION_COUNTER: u128 = 0; + +/// Injects calls to placeholder function `count_code_region()`. +// FIXME(richkadel): As a first step, counters are only injected at the top of each function. +// The complete solution will inject counters at each conditional code branch. +pub fn instrument_coverage<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { let span = body.span.shrink_to_lo(); - let ret_ty = tcx.fn_sig(callee_fn_def_id).output(); + let count_code_region_fn = + function_handle(tcx, span, tcx.lang_items().count_code_region_fn().unwrap()); + let counter_index = const_int_operand(tcx, span, tcx.types.u32, INIT_FUNCTION_COUNTER); + + let mut patch = MirPatch::new(body); + + let new_block = patch.new_block(placeholder_block(SourceInfo::outermost(body.span))); + let next_block = START_BLOCK; + + let temp = patch.new_temp(tcx.mk_unit(), body.span); + patch.patch_terminator( + new_block, + TerminatorKind::Call { + func: count_code_region_fn, + args: vec![counter_index], + // new_block will swapped with the next_block, after applying patch + destination: Some((Place::from(temp), new_block)), + cleanup: None, + from_hir_call: false, + }, + ); + + patch.add_statement(new_block.start_location(), StatementKind::StorageLive(temp)); + patch.add_statement(next_block.start_location(), StatementKind::StorageDead(temp)); + + patch.apply(body); + + // To insert the `new_block` in front of the first block in the counted branch (for example, + // the START_BLOCK, at the top of the function), just swap the indexes, leaving the rest of the + // graph unchanged. + body.basic_blocks_mut().swap(next_block, new_block); +} + +fn function_handle<'tcx>(tcx: TyCtxt<'tcx>, span: Span, fn_def_id: DefId) -> Operand<'tcx> { + let ret_ty = tcx.fn_sig(fn_def_id).output(); let ret_ty = ret_ty.no_bound_vars().unwrap(); let substs = tcx.mk_substs(::std::iter::once(ty::subst::GenericArg::from(ret_ty))); - - let count_code_region_fn: Operand<'_> = - Operand::function_handle(tcx, callee_fn_def_id, substs, span); - - let index = const_int_operand(tcx, span.clone(), tcx.types.u32, 0); - - let args = vec![index]; - - let source_info = SourceInfo { span: span, scope: OUTERMOST_SOURCE_SCOPE }; - - let new_block = START_BLOCK + body.basic_blocks().len(); - - let next_local = body.local_decls.len(); - let new_temp = Local::new(next_local); - let unit_temp = Place::from(new_temp); - - let storage_live = Statement { source_info, kind: StatementKind::StorageLive(new_temp) }; - let storage_dead = Statement { source_info, kind: StatementKind::StorageDead(new_temp) }; - - let count_code_region_call = TerminatorKind::Call { - func: count_code_region_fn, - args, - destination: Some((unit_temp, new_block)), - cleanup: None, - from_hir_call: false, - }; - - body.local_decls.push(LocalDecl::new(tcx.mk_unit(), body.span)); - body.basic_blocks_mut().push(BasicBlockData { - statements: vec![storage_live], - is_cleanup: false, - terminator: Some(Terminator { source_info, kind: count_code_region_call }), - }); - - body.basic_blocks_mut().swap(START_BLOCK, new_block); - body[new_block].statements.push(storage_dead); - - // FIXME(richkadel): ALSO add each computed Span for each conditional branch to the coverage map - // and provide that map to LLVM to encode in the final binary. + Operand::function_handle(tcx, fn_def_id, substs, span) } fn const_int_operand<'tcx>( @@ -98,3 +90,15 @@ fn const_int_operand<'tcx>( literal: ty::Const::from_scalar(tcx, Scalar::from_uint(val, size), ty), }) } + +fn placeholder_block<'tcx>(source_info: SourceInfo) -> BasicBlockData<'tcx> { + BasicBlockData { + statements: vec![], + terminator: Some(Terminator { + source_info, + // this gets overwritten by the counter Call + kind: TerminatorKind::Unreachable, + }), + is_cleanup: false, + } +} diff --git a/src/librustc_mir/transform/mod.rs b/src/librustc_mir/transform/mod.rs index e03ef48f7483..956ddd2051ba 100644 --- a/src/librustc_mir/transform/mod.rs +++ b/src/librustc_mir/transform/mod.rs @@ -288,8 +288,6 @@ fn mir_validated( &[&[ // What we need to run borrowck etc. &promote_pass, - // FIXME(richkadel): is this the best place for the InstrumentCoverage pass? - &instrument_coverage::InstrumentCoverage, &simplify::SimplifyCfg::new("qualify-consts"), ]], ); @@ -340,6 +338,10 @@ fn run_post_borrowck_cleanup_passes<'tcx>( // `AddRetag` needs to run after `ElaborateDrops`. Otherwise it should run fairly late, // but before optimizations begin. &add_retag::AddRetag, + // If the `instrument-coverage` option is enabled, analyze the CFG, identify each + // conditional branch, construct a coverage map to be passed to LLVM, and inject counters + // where needed. + &instrument_coverage::InstrumentCoverage, &simplify::SimplifyCfg::new("elaborate-drops"), ]; diff --git a/src/librustc_session/options.rs b/src/librustc_session/options.rs index 599ce595e131..2d231359057f 100644 --- a/src/librustc_session/options.rs +++ b/src/librustc_session/options.rs @@ -877,8 +877,8 @@ options! {DebuggingOptions, DebuggingSetter, basic_debugging_options, (such as entering an empty infinite loop) by inserting llvm.sideeffect \ (default: no)"), instrument_coverage: bool = (false, parse_bool, [TRACKED], - "instrument the generated code with LLVM code region counters for \ - generating coverage reports (default: no)"), + "instrument the generated code with LLVM code region counters to \ + (in the future) generate coverage reports (experimental; default: no)"), instrument_mcount: bool = (false, parse_bool, [TRACKED], "insert function instrument code for mcount-based tracing (default: no)"), keep_hygiene_data: bool = (false, parse_bool, [UNTRACKED], From 46ebd57c42439b3aedcb160f70b022a4f59f4afa Mon Sep 17 00:00:00 2001 From: Rich Kadel Date: Mon, 8 Jun 2020 16:20:26 -0700 Subject: [PATCH 076/123] moved instrument_coverage pass, optimized scalar, added FIXME --- src/librustc_codegen_llvm/intrinsic.rs | 5 ++++ .../transform/instrument_coverage.rs | 27 ++++++++++++------- src/librustc_mir/transform/mod.rs | 8 +++--- 3 files changed, 27 insertions(+), 13 deletions(-) diff --git a/src/librustc_codegen_llvm/intrinsic.rs b/src/librustc_codegen_llvm/intrinsic.rs index 7fddda99185b..95465939070a 100644 --- a/src/librustc_codegen_llvm/intrinsic.rs +++ b/src/librustc_codegen_llvm/intrinsic.rs @@ -148,6 +148,11 @@ impl IntrinsicCallMethods<'tcx> for Builder<'a, 'll, 'tcx> { caller_fn_path ); + // FIXME(richkadel): (1) Replace raw function name with mangled function name; + // (2) Replace hardcoded `1234` in `hash` with a computed hash (as discussed in) + // the MCP (compiler-team/issues/278); and replace the hardcoded `1` for + // `num_counters` with the actual number of counters per function (when the + // changes are made to inject more than one counter per function). let (fn_name, _len_val) = self.const_str(Symbol::intern(&caller_fn_path)); let index = args[0].immediate(); let hash = self.const_u64(1234); diff --git a/src/librustc_mir/transform/instrument_coverage.rs b/src/librustc_mir/transform/instrument_coverage.rs index 0604caadaea3..27abe813b067 100644 --- a/src/librustc_mir/transform/instrument_coverage.rs +++ b/src/librustc_mir/transform/instrument_coverage.rs @@ -7,6 +7,7 @@ use rustc_middle::ty::Ty; use rustc_middle::ty::TyCtxt; use rustc_span::def_id::DefId; use rustc_span::Span; +use rustc_target::abi; pub struct InstrumentCoverage; @@ -25,7 +26,7 @@ impl<'tcx> MirPass<'tcx> for InstrumentCoverage { } // The first counter (start of the function) is index zero. -const INIT_FUNCTION_COUNTER: u128 = 0; +const INIT_FUNCTION_COUNTER: u32 = 0; /// Injects calls to placeholder function `count_code_region()`. // FIXME(richkadel): As a first step, counters are only injected at the top of each function. @@ -35,7 +36,8 @@ pub fn instrument_coverage<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { let count_code_region_fn = function_handle(tcx, span, tcx.lang_items().count_code_region_fn().unwrap()); - let counter_index = const_int_operand(tcx, span, tcx.types.u32, INIT_FUNCTION_COUNTER); + let counter_index = + const_int_operand(tcx, span, tcx.types.u32, Scalar::from_u32(INIT_FUNCTION_COUNTER)); let mut patch = MirPatch::new(body); @@ -77,17 +79,24 @@ fn const_int_operand<'tcx>( tcx: TyCtxt<'tcx>, span: Span, ty: Ty<'tcx>, - val: u128, + val: Scalar, ) -> Operand<'tcx> { - let param_env_and_ty = ty::ParamEnv::empty().and(ty); - let size = tcx - .layout_of(param_env_and_ty) - .unwrap_or_else(|e| panic!("could not compute layout for {:?}: {:?}", ty, e)) - .size; + debug_assert!({ + let param_env_and_ty = ty::ParamEnv::empty().and(ty); + let type_size = tcx + .layout_of(param_env_and_ty) + .unwrap_or_else(|e| panic!("could not compute layout for {:?}: {:?}", ty, e)) + .size; + let scalar_size = abi::Size::from_bytes(match val { + Scalar::Raw { size, .. } => size, + _ => panic!("Invalid scalar type {:?}", val), + }); + scalar_size == type_size + }); Operand::Constant(box Constant { span, user_ty: None, - literal: ty::Const::from_scalar(tcx, Scalar::from_uint(val, size), ty), + literal: ty::Const::from_scalar(tcx, val, ty), }) } diff --git a/src/librustc_mir/transform/mod.rs b/src/librustc_mir/transform/mod.rs index 956ddd2051ba..846ed1f86d8d 100644 --- a/src/librustc_mir/transform/mod.rs +++ b/src/librustc_mir/transform/mod.rs @@ -289,6 +289,10 @@ fn mir_validated( // What we need to run borrowck etc. &promote_pass, &simplify::SimplifyCfg::new("qualify-consts"), + // If the `instrument-coverage` option is enabled, analyze the CFG, identify each + // conditional branch, construct a coverage map to be passed to LLVM, and inject counters + // where needed. + &instrument_coverage::InstrumentCoverage, ]], ); @@ -338,10 +342,6 @@ fn run_post_borrowck_cleanup_passes<'tcx>( // `AddRetag` needs to run after `ElaborateDrops`. Otherwise it should run fairly late, // but before optimizations begin. &add_retag::AddRetag, - // If the `instrument-coverage` option is enabled, analyze the CFG, identify each - // conditional branch, construct a coverage map to be passed to LLVM, and inject counters - // where needed. - &instrument_coverage::InstrumentCoverage, &simplify::SimplifyCfg::new("elaborate-drops"), ]; From 20aba8f634c13fa2bb1b043b51a074769dc06f66 Mon Sep 17 00:00:00 2001 From: Rich Kadel Date: Wed, 10 Jun 2020 09:54:02 -0700 Subject: [PATCH 077/123] added test, Operand::const_from_scalar, require_lang_item, & comments Addresses feedback from @oli-obk (Thanks!) --- src/librustc_middle/mir/mod.rs | 28 +++++++ src/librustc_mir/interpret/intrinsics.rs | 1 + .../transform/instrument_coverage.rs | 52 ++++-------- src/test/mir-opt/instrument_coverage.rs | 19 +++++ .../rustc.bar.InstrumentCoverage.diff | 41 ++++++++++ .../rustc.main.InstrumentCoverage.diff | 82 +++++++++++++++++++ 6 files changed, 186 insertions(+), 37 deletions(-) create mode 100644 src/test/mir-opt/instrument_coverage.rs create mode 100644 src/test/mir-opt/instrument_coverage/rustc.bar.InstrumentCoverage.diff create mode 100644 src/test/mir-opt/instrument_coverage/rustc.main.InstrumentCoverage.diff diff --git a/src/librustc_middle/mir/mod.rs b/src/librustc_middle/mir/mod.rs index 27848684706d..11ae2cf72c46 100644 --- a/src/librustc_middle/mir/mod.rs +++ b/src/librustc_middle/mir/mod.rs @@ -29,6 +29,7 @@ use rustc_macros::HashStable; use rustc_serialize::{Decodable, Encodable}; use rustc_span::symbol::Symbol; use rustc_span::{Span, DUMMY_SP}; +use rustc_target::abi; use rustc_target::asm::InlineAsmRegOrRegClass; use std::borrow::Cow; use std::fmt::{self, Debug, Display, Formatter, Write}; @@ -2218,6 +2219,33 @@ impl<'tcx> Operand<'tcx> { }) } + /// Convenience helper to make a literal-like constant from a given scalar value. + /// Since this is used to synthesize MIR, assumes `user_ty` is None. + pub fn const_from_scalar( + tcx: TyCtxt<'tcx>, + ty: Ty<'tcx>, + val: Scalar, + span: Span, + ) -> Operand<'tcx> { + debug_assert!({ + let param_env_and_ty = ty::ParamEnv::empty().and(ty); + let type_size = tcx + .layout_of(param_env_and_ty) + .unwrap_or_else(|e| panic!("could not compute layout for {:?}: {:?}", ty, e)) + .size; + let scalar_size = abi::Size::from_bytes(match val { + Scalar::Raw { size, .. } => size, + _ => panic!("Invalid scalar type {:?}", val), + }); + scalar_size == type_size + }); + Operand::Constant(box Constant { + span, + user_ty: None, + literal: ty::Const::from_scalar(tcx, val, ty), + }) + } + pub fn to_copy(&self) -> Self { match *self { Operand::Copy(_) | Operand::Constant(_) => self.clone(), diff --git a/src/librustc_mir/interpret/intrinsics.rs b/src/librustc_mir/interpret/intrinsics.rs index 4d8120794f88..ac28ccd18152 100644 --- a/src/librustc_mir/interpret/intrinsics.rs +++ b/src/librustc_mir/interpret/intrinsics.rs @@ -389,6 +389,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { ); self.copy_op(self.operand_index(args[0], index)?, dest)?; } + // FIXME(#73156): Handle source code coverage in const eval sym::count_code_region => (), _ => return Ok(false), } diff --git a/src/librustc_mir/transform/instrument_coverage.rs b/src/librustc_mir/transform/instrument_coverage.rs index 27abe813b067..fda7ad731fa2 100644 --- a/src/librustc_mir/transform/instrument_coverage.rs +++ b/src/librustc_mir/transform/instrument_coverage.rs @@ -1,21 +1,17 @@ use crate::transform::{MirPass, MirSource}; use crate::util::patch::MirPatch; +use rustc_hir::lang_items; use rustc_middle::mir::interpret::Scalar; use rustc_middle::mir::*; use rustc_middle::ty; -use rustc_middle::ty::Ty; use rustc_middle::ty::TyCtxt; use rustc_span::def_id::DefId; use rustc_span::Span; -use rustc_target::abi; +/// Inserts call to count_code_region() as a placeholder to be replaced during code generation with +/// the intrinsic llvm.instrprof.increment. pub struct InstrumentCoverage; -/** - * Inserts call to count_code_region() as a placeholder to be replaced during code generation with - * the intrinsic llvm.instrprof.increment. - */ - impl<'tcx> MirPass<'tcx> for InstrumentCoverage { fn run_pass(&self, tcx: TyCtxt<'tcx>, src: MirSource<'tcx>, body: &mut Body<'tcx>) { if tcx.sess.opts.debugging_opts.instrument_coverage { @@ -34,10 +30,17 @@ const INIT_FUNCTION_COUNTER: u32 = 0; pub fn instrument_coverage<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { let span = body.span.shrink_to_lo(); - let count_code_region_fn = - function_handle(tcx, span, tcx.lang_items().count_code_region_fn().unwrap()); - let counter_index = - const_int_operand(tcx, span, tcx.types.u32, Scalar::from_u32(INIT_FUNCTION_COUNTER)); + let count_code_region_fn = function_handle( + tcx, + tcx.require_lang_item(lang_items::CountCodeRegionFnLangItem, None), + span, + ); + let counter_index = Operand::const_from_scalar( + tcx, + tcx.types.u32, + Scalar::from_u32(INIT_FUNCTION_COUNTER), + span, + ); let mut patch = MirPatch::new(body); @@ -68,38 +71,13 @@ pub fn instrument_coverage<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { body.basic_blocks_mut().swap(next_block, new_block); } -fn function_handle<'tcx>(tcx: TyCtxt<'tcx>, span: Span, fn_def_id: DefId) -> Operand<'tcx> { +fn function_handle<'tcx>(tcx: TyCtxt<'tcx>, fn_def_id: DefId, span: Span) -> Operand<'tcx> { let ret_ty = tcx.fn_sig(fn_def_id).output(); let ret_ty = ret_ty.no_bound_vars().unwrap(); let substs = tcx.mk_substs(::std::iter::once(ty::subst::GenericArg::from(ret_ty))); Operand::function_handle(tcx, fn_def_id, substs, span) } -fn const_int_operand<'tcx>( - tcx: TyCtxt<'tcx>, - span: Span, - ty: Ty<'tcx>, - val: Scalar, -) -> Operand<'tcx> { - debug_assert!({ - let param_env_and_ty = ty::ParamEnv::empty().and(ty); - let type_size = tcx - .layout_of(param_env_and_ty) - .unwrap_or_else(|e| panic!("could not compute layout for {:?}: {:?}", ty, e)) - .size; - let scalar_size = abi::Size::from_bytes(match val { - Scalar::Raw { size, .. } => size, - _ => panic!("Invalid scalar type {:?}", val), - }); - scalar_size == type_size - }); - Operand::Constant(box Constant { - span, - user_ty: None, - literal: ty::Const::from_scalar(tcx, val, ty), - }) -} - fn placeholder_block<'tcx>(source_info: SourceInfo) -> BasicBlockData<'tcx> { BasicBlockData { statements: vec![], diff --git a/src/test/mir-opt/instrument_coverage.rs b/src/test/mir-opt/instrument_coverage.rs new file mode 100644 index 000000000000..e8c723b528a1 --- /dev/null +++ b/src/test/mir-opt/instrument_coverage.rs @@ -0,0 +1,19 @@ +// Test that the initial version of Rust coverage injects count_code_region() placeholder calls, +// at the top of each function. The placeholders are later converted into LLVM instrprof.increment +// intrinsics, during codegen. + +// compile-flags: -Zinstrument-coverage +// EMIT_MIR rustc.main.InstrumentCoverage.diff +// EMIT_MIR rustc.bar.InstrumentCoverage.diff +fn main() { + loop { + if bar() { + break; + } + } +} + +#[inline(never)] +fn bar() -> bool { + true +} diff --git a/src/test/mir-opt/instrument_coverage/rustc.bar.InstrumentCoverage.diff b/src/test/mir-opt/instrument_coverage/rustc.bar.InstrumentCoverage.diff new file mode 100644 index 000000000000..d23bb93d951d --- /dev/null +++ b/src/test/mir-opt/instrument_coverage/rustc.bar.InstrumentCoverage.diff @@ -0,0 +1,41 @@ +- // MIR for `bar` before InstrumentCoverage ++ // MIR for `bar` after InstrumentCoverage + + fn bar() -> bool { + let mut _0: bool; // return place in scope 0 at $DIR/instrument_coverage.rs:17:13: 17:17 ++ let mut _1: (); // in scope 0 at $DIR/instrument_coverage.rs:17:1: 19:2 + + bb0: { ++ StorageLive(_1); // scope 0 at $DIR/instrument_coverage.rs:17:1: 19:2 ++ _1 = const std::intrinsics::count_code_region(const 0u32) -> bb2; // scope 0 at $DIR/instrument_coverage.rs:17:1: 19:2 ++ // ty::Const ++ // + ty: unsafe extern "rust-intrinsic" fn(u32) {std::intrinsics::count_code_region} ++ // + val: Value(Scalar()) ++ // mir::Constant ++ // + span: $DIR/instrument_coverage.rs:17:1: 17:1 ++ // + literal: Const { ty: unsafe extern "rust-intrinsic" fn(u32) {std::intrinsics::count_code_region}, val: Value(Scalar()) } ++ // ty::Const ++ // + ty: u32 ++ // + val: Value(Scalar(0x00000000)) ++ // mir::Constant ++ // + span: $DIR/instrument_coverage.rs:17:1: 17:1 ++ // + literal: Const { ty: u32, val: Value(Scalar(0x00000000)) } ++ } ++ ++ bb1 (cleanup): { ++ resume; // scope 0 at $DIR/instrument_coverage.rs:17:1: 19:2 ++ } ++ ++ bb2: { ++ StorageDead(_1); // scope 0 at $DIR/instrument_coverage.rs:18:5: 18:9 + _0 = const true; // scope 0 at $DIR/instrument_coverage.rs:18:5: 18:9 + // ty::Const + // + ty: bool + // + val: Value(Scalar(0x01)) + // mir::Constant + // + span: $DIR/instrument_coverage.rs:18:5: 18:9 + // + literal: Const { ty: bool, val: Value(Scalar(0x01)) } + return; // scope 0 at $DIR/instrument_coverage.rs:19:2: 19:2 + } + } + diff --git a/src/test/mir-opt/instrument_coverage/rustc.main.InstrumentCoverage.diff b/src/test/mir-opt/instrument_coverage/rustc.main.InstrumentCoverage.diff new file mode 100644 index 000000000000..d5d0f82495d1 --- /dev/null +++ b/src/test/mir-opt/instrument_coverage/rustc.main.InstrumentCoverage.diff @@ -0,0 +1,82 @@ +- // MIR for `main` before InstrumentCoverage ++ // MIR for `main` after InstrumentCoverage + + fn main() -> () { + let mut _0: (); // return place in scope 0 at $DIR/instrument_coverage.rs:8:11: 8:11 + let mut _1: (); // in scope 0 at $DIR/instrument_coverage.rs:8:1: 14:2 + let mut _2: bool; // in scope 0 at $DIR/instrument_coverage.rs:10:12: 10:17 + let mut _3: !; // in scope 0 at $DIR/instrument_coverage.rs:10:18: 12:10 ++ let mut _4: (); // in scope 0 at $DIR/instrument_coverage.rs:8:1: 14:2 + + bb0: { +- falseUnwind -> [real: bb1, cleanup: bb6]; // scope 0 at $DIR/instrument_coverage.rs:9:5: 13:6 ++ StorageLive(_4); // scope 0 at $DIR/instrument_coverage.rs:8:1: 14:2 ++ _4 = const std::intrinsics::count_code_region(const 0u32) -> bb7; // scope 0 at $DIR/instrument_coverage.rs:8:1: 14:2 ++ // ty::Const ++ // + ty: unsafe extern "rust-intrinsic" fn(u32) {std::intrinsics::count_code_region} ++ // + val: Value(Scalar()) ++ // mir::Constant ++ // + span: $DIR/instrument_coverage.rs:8:1: 8:1 ++ // + literal: Const { ty: unsafe extern "rust-intrinsic" fn(u32) {std::intrinsics::count_code_region}, val: Value(Scalar()) } ++ // ty::Const ++ // + ty: u32 ++ // + val: Value(Scalar(0x00000000)) ++ // mir::Constant ++ // + span: $DIR/instrument_coverage.rs:8:1: 8:1 ++ // + literal: Const { ty: u32, val: Value(Scalar(0x00000000)) } + } + + bb1: { + StorageLive(_2); // scope 0 at $DIR/instrument_coverage.rs:10:12: 10:17 + _2 = const bar() -> [return: bb2, unwind: bb6]; // scope 0 at $DIR/instrument_coverage.rs:10:12: 10:17 + // ty::Const + // + ty: fn() -> bool {bar} + // + val: Value(Scalar()) + // mir::Constant + // + span: $DIR/instrument_coverage.rs:10:12: 10:15 + // + literal: Const { ty: fn() -> bool {bar}, val: Value(Scalar()) } + } + + bb2: { + FakeRead(ForMatchedPlace, _2); // scope 0 at $DIR/instrument_coverage.rs:10:12: 10:17 + switchInt(_2) -> [false: bb4, otherwise: bb3]; // scope 0 at $DIR/instrument_coverage.rs:10:9: 12:10 + } + + bb3: { + falseEdges -> [real: bb5, imaginary: bb4]; // scope 0 at $DIR/instrument_coverage.rs:10:9: 12:10 + } + + bb4: { + _1 = const (); // scope 0 at $DIR/instrument_coverage.rs:10:9: 12:10 + // ty::Const + // + ty: () + // + val: Value(Scalar()) + // mir::Constant + // + span: $DIR/instrument_coverage.rs:10:9: 12:10 + // + literal: Const { ty: (), val: Value(Scalar()) } + StorageDead(_2); // scope 0 at $DIR/instrument_coverage.rs:13:5: 13:6 + goto -> bb0; // scope 0 at $DIR/instrument_coverage.rs:9:5: 13:6 + } + + bb5: { + _0 = const (); // scope 0 at $DIR/instrument_coverage.rs:11:13: 11:18 + // ty::Const + // + ty: () + // + val: Value(Scalar()) + // mir::Constant + // + span: $DIR/instrument_coverage.rs:11:13: 11:18 + // + literal: Const { ty: (), val: Value(Scalar()) } + StorageDead(_2); // scope 0 at $DIR/instrument_coverage.rs:13:5: 13:6 + return; // scope 0 at $DIR/instrument_coverage.rs:14:2: 14:2 + } + + bb6 (cleanup): { + resume; // scope 0 at $DIR/instrument_coverage.rs:8:1: 14:2 ++ } ++ ++ bb7: { ++ StorageDead(_4); // scope 0 at $DIR/instrument_coverage.rs:9:5: 13:6 ++ falseUnwind -> [real: bb1, cleanup: bb6]; // scope 0 at $DIR/instrument_coverage.rs:9:5: 13:6 + } + } + From 163e5854562f5274f092d66318a5c805e18d83c5 Mon Sep 17 00:00:00 2001 From: Rich Kadel Date: Wed, 10 Jun 2020 12:48:30 -0700 Subject: [PATCH 078/123] updated mir-opt test due to other recent changes to MIR --- .../rustc.main.InstrumentCoverage.diff | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/test/mir-opt/instrument_coverage/rustc.main.InstrumentCoverage.diff b/src/test/mir-opt/instrument_coverage/rustc.main.InstrumentCoverage.diff index d5d0f82495d1..095246580409 100644 --- a/src/test/mir-opt/instrument_coverage/rustc.main.InstrumentCoverage.diff +++ b/src/test/mir-opt/instrument_coverage/rustc.main.InstrumentCoverage.diff @@ -9,7 +9,7 @@ + let mut _4: (); // in scope 0 at $DIR/instrument_coverage.rs:8:1: 14:2 bb0: { -- falseUnwind -> [real: bb1, cleanup: bb6]; // scope 0 at $DIR/instrument_coverage.rs:9:5: 13:6 +- falseUnwind -> [real: bb1, cleanup: bb2]; // scope 0 at $DIR/instrument_coverage.rs:9:5: 13:6 + StorageLive(_4); // scope 0 at $DIR/instrument_coverage.rs:8:1: 14:2 + _4 = const std::intrinsics::count_code_region(const 0u32) -> bb7; // scope 0 at $DIR/instrument_coverage.rs:8:1: 14:2 + // ty::Const @@ -28,7 +28,7 @@ bb1: { StorageLive(_2); // scope 0 at $DIR/instrument_coverage.rs:10:12: 10:17 - _2 = const bar() -> [return: bb2, unwind: bb6]; // scope 0 at $DIR/instrument_coverage.rs:10:12: 10:17 + _2 = const bar() -> [return: bb3, unwind: bb2]; // scope 0 at $DIR/instrument_coverage.rs:10:12: 10:17 // ty::Const // + ty: fn() -> bool {bar} // + val: Value(Scalar()) @@ -37,16 +37,20 @@ // + literal: Const { ty: fn() -> bool {bar}, val: Value(Scalar()) } } - bb2: { - FakeRead(ForMatchedPlace, _2); // scope 0 at $DIR/instrument_coverage.rs:10:12: 10:17 - switchInt(_2) -> [false: bb4, otherwise: bb3]; // scope 0 at $DIR/instrument_coverage.rs:10:9: 12:10 + bb2 (cleanup): { + resume; // scope 0 at $DIR/instrument_coverage.rs:8:1: 14:2 } bb3: { - falseEdges -> [real: bb5, imaginary: bb4]; // scope 0 at $DIR/instrument_coverage.rs:10:9: 12:10 + FakeRead(ForMatchedPlace, _2); // scope 0 at $DIR/instrument_coverage.rs:10:12: 10:17 + switchInt(_2) -> [false: bb5, otherwise: bb4]; // scope 0 at $DIR/instrument_coverage.rs:10:9: 12:10 } bb4: { + falseEdge -> [real: bb6, imaginary: bb5]; // scope 0 at $DIR/instrument_coverage.rs:10:9: 12:10 + } + + bb5: { _1 = const (); // scope 0 at $DIR/instrument_coverage.rs:10:9: 12:10 // ty::Const // + ty: () @@ -58,7 +62,7 @@ goto -> bb0; // scope 0 at $DIR/instrument_coverage.rs:9:5: 13:6 } - bb5: { + bb6: { _0 = const (); // scope 0 at $DIR/instrument_coverage.rs:11:13: 11:18 // ty::Const // + ty: () @@ -68,15 +72,11 @@ // + literal: Const { ty: (), val: Value(Scalar()) } StorageDead(_2); // scope 0 at $DIR/instrument_coverage.rs:13:5: 13:6 return; // scope 0 at $DIR/instrument_coverage.rs:14:2: 14:2 - } - - bb6 (cleanup): { - resume; // scope 0 at $DIR/instrument_coverage.rs:8:1: 14:2 + } + + bb7: { + StorageDead(_4); // scope 0 at $DIR/instrument_coverage.rs:9:5: 13:6 -+ falseUnwind -> [real: bb1, cleanup: bb6]; // scope 0 at $DIR/instrument_coverage.rs:9:5: 13:6 ++ falseUnwind -> [real: bb1, cleanup: bb2]; // scope 0 at $DIR/instrument_coverage.rs:9:5: 13:6 } } From 98685a4bf2ef50c6d6a64ef3867a29994d5a4a25 Mon Sep 17 00:00:00 2001 From: Rich Kadel Date: Mon, 15 Jun 2020 17:08:13 -0700 Subject: [PATCH 079/123] Add new `fn_span` to TerminatorKind::Call instance --- src/librustc_mir/transform/instrument_coverage.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/librustc_mir/transform/instrument_coverage.rs b/src/librustc_mir/transform/instrument_coverage.rs index fda7ad731fa2..c36614938e10 100644 --- a/src/librustc_mir/transform/instrument_coverage.rs +++ b/src/librustc_mir/transform/instrument_coverage.rs @@ -57,6 +57,7 @@ pub fn instrument_coverage<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { destination: Some((Place::from(temp), new_block)), cleanup: None, from_hir_call: false, + fn_span: span, }, ); From f3dfe80ee1d13ec923082487e73c6d5d5833eef0 Mon Sep 17 00:00:00 2001 From: oddg Date: Mon, 15 Jun 2020 21:28:50 -0700 Subject: [PATCH 080/123] Adjust error message --- src/test/ui/cenum_impl_drop_cast.rs | 2 +- src/test/ui/cenum_impl_drop_cast.stderr | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/test/ui/cenum_impl_drop_cast.rs b/src/test/ui/cenum_impl_drop_cast.rs index 623460673bfa..96e3d967e2c6 100644 --- a/src/test/ui/cenum_impl_drop_cast.rs +++ b/src/test/ui/cenum_impl_drop_cast.rs @@ -13,6 +13,6 @@ impl Drop for E { fn main() { let e = E::A; let i = e as u32; - //~^ ERROR Cast `enum` implementing `Drop` `E` to integer `u32` + //~^ ERROR cannot cast enum `E` into integer `u32` because it implements `Drop` //~| WARN this was previously accepted } diff --git a/src/test/ui/cenum_impl_drop_cast.stderr b/src/test/ui/cenum_impl_drop_cast.stderr index 5c8f86ffd72a..8d847a0c80b1 100644 --- a/src/test/ui/cenum_impl_drop_cast.stderr +++ b/src/test/ui/cenum_impl_drop_cast.stderr @@ -1,4 +1,4 @@ -error: Cast `enum` implementing `Drop` `E` to integer `u32` +error: cannot cast enum `E` into integer `u32` because it implements `Drop` --> $DIR/cenum_impl_drop_cast.rs:15:13 | LL | let i = e as u32; From 0265e4e61bcd51b11f0b13b712245feb9c59ab50 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Tue, 16 Jun 2020 09:25:29 +0200 Subject: [PATCH 081/123] add tracking issue --- src/libcore/ptr/mod.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libcore/ptr/mod.rs b/src/libcore/ptr/mod.rs index 199f08c3d505..30c0f9a37571 100644 --- a/src/libcore/ptr/mod.rs +++ b/src/libcore/ptr/mod.rs @@ -1426,7 +1426,7 @@ fnptr_impls_args! { A, B, C, D, E, F, G, H, I, J, K, L } /// let raw_f2 = ptr::raw_const!(packed.f2); /// assert_eq!(unsafe { raw_f2.read_unaligned() }, 2); /// ``` -#[unstable(feature = "raw_ref_macros", issue = "none")] +#[unstable(feature = "raw_ref_macros", issue = "73394")] #[rustc_macro_transparency = "semitransparent"] #[allow_internal_unstable(raw_ref_op)] pub macro raw_const($e:expr) { @@ -1460,7 +1460,7 @@ pub macro raw_const($e:expr) { /// unsafe { raw_f2.write_unaligned(42); } /// assert_eq!({packed.f2}, 42); // `{...}` forces copying the field instead of creating a reference. /// ``` -#[unstable(feature = "raw_ref_macros", issue = "none")] +#[unstable(feature = "raw_ref_macros", issue = "73394")] #[rustc_macro_transparency = "semitransparent"] #[allow_internal_unstable(raw_ref_op)] pub macro raw_mut($e:expr) { From 0bcefd9b5e68eb3a026843ed7b624761bea2de1e Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sun, 31 May 2020 12:13:29 +0200 Subject: [PATCH 082/123] remove visit_terminator_kind from MIR visitor --- src/librustc_codegen_ssa/mir/analyze.rs | 6 +++--- src/librustc_middle/mir/visit.rs | 14 +------------- src/librustc_mir/borrow_check/invalidation.rs | 8 ++++---- src/librustc_mir/borrow_check/used_muts.rs | 16 ++++++++++++---- src/librustc_mir/monomorphize/collector.rs | 8 ++++---- .../transform/check_consts/resolver.rs | 7 ++++--- src/librustc_mir/transform/generator.rs | 6 +++--- src/librustc_mir/transform/inline.rs | 12 ++++++------ src/librustc_mir/transform/no_landing_pads.rs | 6 +++--- src/librustc_mir/transform/promote_consts.rs | 6 +++--- 10 files changed, 43 insertions(+), 46 deletions(-) diff --git a/src/librustc_codegen_ssa/mir/analyze.rs b/src/librustc_codegen_ssa/mir/analyze.rs index 61692280d2a7..db935c2b3e26 100644 --- a/src/librustc_codegen_ssa/mir/analyze.rs +++ b/src/librustc_codegen_ssa/mir/analyze.rs @@ -234,8 +234,8 @@ impl<'mir, 'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> Visitor<'tcx> self.visit_rvalue(rvalue, location); } - fn visit_terminator_kind(&mut self, kind: &mir::TerminatorKind<'tcx>, location: Location) { - let check = match *kind { + fn visit_terminator(&mut self, terminator: &mir::Terminator<'tcx>, location: Location) { + let check = match terminator.kind { mir::TerminatorKind::Call { func: mir::Operand::Constant(ref c), ref args, .. } => { match c.literal.ty.kind { ty::FnDef(did, _) => Some((did, args)), @@ -259,7 +259,7 @@ impl<'mir, 'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> Visitor<'tcx> } } - self.super_terminator_kind(kind, location); + self.super_terminator(terminator, location); } fn visit_place(&mut self, place: &mir::Place<'tcx>, context: PlaceContext, location: Location) { diff --git a/src/librustc_middle/mir/visit.rs b/src/librustc_middle/mir/visit.rs index 5f9fcdca516b..1c5ed837b513 100644 --- a/src/librustc_middle/mir/visit.rs +++ b/src/librustc_middle/mir/visit.rs @@ -108,12 +108,6 @@ macro_rules! make_mir_visitor { self.super_terminator(terminator, location); } - fn visit_terminator_kind(&mut self, - kind: & $($mutability)? TerminatorKind<'tcx>, - location: Location) { - self.super_terminator_kind(kind, location); - } - fn visit_assert_message(&mut self, msg: & $($mutability)? AssertMessage<'tcx>, location: Location) { @@ -413,16 +407,10 @@ macro_rules! make_mir_visitor { fn super_terminator(&mut self, terminator: &$($mutability)? Terminator<'tcx>, - location: Location) { + source_location: Location) { let Terminator { source_info, kind } = terminator; self.visit_source_info(source_info); - self.visit_terminator_kind(kind, location); - } - - fn super_terminator_kind(&mut self, - kind: & $($mutability)? TerminatorKind<'tcx>, - source_location: Location) { match kind { TerminatorKind::Goto { .. } | TerminatorKind::Resume | diff --git a/src/librustc_mir/borrow_check/invalidation.rs b/src/librustc_mir/borrow_check/invalidation.rs index 17fa641ae6c1..e9475937de22 100644 --- a/src/librustc_mir/borrow_check/invalidation.rs +++ b/src/librustc_mir/borrow_check/invalidation.rs @@ -2,7 +2,7 @@ use rustc_data_structures::graph::dominators::Dominators; use rustc_middle::mir::visit::Visitor; use rustc_middle::mir::{BasicBlock, Body, Location, Place, Rvalue}; use rustc_middle::mir::{BorrowKind, Mutability, Operand}; -use rustc_middle::mir::{InlineAsmOperand, TerminatorKind}; +use rustc_middle::mir::{InlineAsmOperand, Terminator, TerminatorKind}; use rustc_middle::mir::{Statement, StatementKind}; use rustc_middle::ty::TyCtxt; @@ -112,10 +112,10 @@ impl<'cx, 'tcx> Visitor<'tcx> for InvalidationGenerator<'cx, 'tcx> { self.super_statement(statement, location); } - fn visit_terminator_kind(&mut self, kind: &TerminatorKind<'tcx>, location: Location) { + fn visit_terminator(&mut self, terminator: &Terminator<'tcx>, location: Location) { self.check_activations(location); - match kind { + match &terminator.kind { TerminatorKind::SwitchInt { ref discr, switch_ty: _, values: _, targets: _ } => { self.consume_operand(location, discr); } @@ -222,7 +222,7 @@ impl<'cx, 'tcx> Visitor<'tcx> for InvalidationGenerator<'cx, 'tcx> { } } - self.super_terminator_kind(kind, location); + self.super_terminator(terminator, location); } } diff --git a/src/librustc_mir/borrow_check/used_muts.rs b/src/librustc_mir/borrow_check/used_muts.rs index 2da72f3bcc51..021629846347 100644 --- a/src/librustc_mir/borrow_check/used_muts.rs +++ b/src/librustc_mir/borrow_check/used_muts.rs @@ -1,5 +1,7 @@ use rustc_middle::mir::visit::{PlaceContext, Visitor}; -use rustc_middle::mir::{Local, Location, Place, Statement, StatementKind, TerminatorKind}; +use rustc_middle::mir::{ + Local, Location, Place, Statement, StatementKind, Terminator, TerminatorKind, +}; use rustc_data_structures::fx::FxHashSet; @@ -62,9 +64,9 @@ impl GatherUsedMutsVisitor<'_, '_, '_> { } impl<'visit, 'cx, 'tcx> Visitor<'tcx> for GatherUsedMutsVisitor<'visit, 'cx, 'tcx> { - fn visit_terminator_kind(&mut self, kind: &TerminatorKind<'tcx>, _location: Location) { - debug!("visit_terminator_kind: kind={:?}", kind); - match &kind { + fn visit_terminator(&mut self, terminator: &Terminator<'tcx>, _location: Location) { + debug!("visit_terminator: terminator={:?}", terminator); + match &terminator.kind { TerminatorKind::Call { destination: Some((into, _)), .. } => { self.remove_never_initialized_mut_locals(*into); } @@ -73,6 +75,8 @@ impl<'visit, 'cx, 'tcx> Visitor<'tcx> for GatherUsedMutsVisitor<'visit, 'cx, 'tc } _ => {} } + + // FIXME: no super_terminator? } fn visit_statement(&mut self, statement: &Statement<'tcx>, _location: Location) { @@ -84,6 +88,8 @@ impl<'visit, 'cx, 'tcx> Visitor<'tcx> for GatherUsedMutsVisitor<'visit, 'cx, 'tc ); self.remove_never_initialized_mut_locals(*into); } + + // FIXME: no super_statement? } fn visit_local(&mut self, local: &Local, place_context: PlaceContext, location: Location) { @@ -101,5 +107,7 @@ impl<'visit, 'cx, 'tcx> Visitor<'tcx> for GatherUsedMutsVisitor<'visit, 'cx, 'tc } } } + + // FIXME: no super_local? } } diff --git a/src/librustc_mir/monomorphize/collector.rs b/src/librustc_mir/monomorphize/collector.rs index 994d1e69f2e3..ac19d59f04fb 100644 --- a/src/librustc_mir/monomorphize/collector.rs +++ b/src/librustc_mir/monomorphize/collector.rs @@ -616,11 +616,11 @@ impl<'a, 'tcx> MirVisitor<'tcx> for MirNeighborCollector<'a, 'tcx> { self.super_const(constant); } - fn visit_terminator_kind(&mut self, kind: &mir::TerminatorKind<'tcx>, location: Location) { - debug!("visiting terminator {:?} @ {:?}", kind, location); + fn visit_terminator(&mut self, terminator: &mir::Terminator<'tcx>, location: Location) { + debug!("visiting terminator {:?} @ {:?}", terminator, location); let tcx = self.tcx; - match *kind { + match terminator.kind { mir::TerminatorKind::Call { ref func, .. } => { let callee_ty = func.ty(self.body, tcx); let callee_ty = self.monomorphize(callee_ty); @@ -663,7 +663,7 @@ impl<'a, 'tcx> MirVisitor<'tcx> for MirNeighborCollector<'a, 'tcx> { | mir::TerminatorKind::FalseUnwind { .. } => bug!(), } - self.super_terminator_kind(kind, location); + self.super_terminator(terminator, location); } fn visit_local( diff --git a/src/librustc_mir/transform/check_consts/resolver.rs b/src/librustc_mir/transform/check_consts/resolver.rs index a81d7a23be2f..c67545d5b39e 100644 --- a/src/librustc_mir/transform/check_consts/resolver.rs +++ b/src/librustc_mir/transform/check_consts/resolver.rs @@ -121,11 +121,12 @@ where self.super_assign(place, rvalue, location); } - fn visit_terminator_kind(&mut self, kind: &mir::TerminatorKind<'tcx>, location: Location) { + fn visit_terminator(&mut self, terminator: &mir::Terminator<'tcx>, location: Location) { // The effect of assignment to the return place in `TerminatorKind::Call` is not applied // here; that occurs in `apply_call_return_effect`. - if let mir::TerminatorKind::DropAndReplace { value, location: dest, .. } = kind { + if let mir::TerminatorKind::DropAndReplace { value, location: dest, .. } = &terminator.kind + { let qualif = qualifs::in_operand::( self.ccx, &mut |l| self.qualifs_per_local.contains(l), @@ -139,7 +140,7 @@ where // We need to assign qualifs to the dropped location before visiting the operand that // replaces it since qualifs can be cleared on move. - self.super_terminator_kind(kind, location); + self.super_terminator(terminator, location); } } diff --git a/src/librustc_mir/transform/generator.rs b/src/librustc_mir/transform/generator.rs index 25b6a51d91b9..7215f390d40d 100644 --- a/src/librustc_mir/transform/generator.rs +++ b/src/librustc_mir/transform/generator.rs @@ -93,13 +93,13 @@ impl<'tcx> MutVisitor<'tcx> for RenameLocalVisitor<'tcx> { } } - fn visit_terminator_kind(&mut self, kind: &mut TerminatorKind<'tcx>, location: Location) { - match kind { + fn visit_terminator(&mut self, terminator: &mut Terminator<'tcx>, location: Location) { + match terminator.kind { TerminatorKind::Return => { // Do not replace the implicit `_0` access here, as that's not possible. The // transform already handles `return` correctly. } - _ => self.super_terminator_kind(kind, location), + _ => self.super_terminator(terminator, location), } } } diff --git a/src/librustc_mir/transform/inline.rs b/src/librustc_mir/transform/inline.rs index 47aa4fbf60c0..65400b58eebc 100644 --- a/src/librustc_mir/transform/inline.rs +++ b/src/librustc_mir/transform/inline.rs @@ -731,14 +731,14 @@ impl<'a, 'tcx> MutVisitor<'tcx> for Integrator<'a, 'tcx> { } } - fn visit_terminator_kind(&mut self, kind: &mut TerminatorKind<'tcx>, loc: Location) { + fn visit_terminator(&mut self, terminator: &mut Terminator<'tcx>, loc: Location) { // Don't try to modify the implicit `_0` access on return (`return` terminators are // replaced down below anyways). - if !matches!(kind, TerminatorKind::Return) { - self.super_terminator_kind(kind, loc); + if !matches!(terminator.kind, TerminatorKind::Return) { + self.super_terminator(terminator, loc); } - match *kind { + match terminator.kind { TerminatorKind::GeneratorDrop | TerminatorKind::Yield { .. } => bug!(), TerminatorKind::Goto { ref mut target } => { *target = self.update_target(*target); @@ -782,11 +782,11 @@ impl<'a, 'tcx> MutVisitor<'tcx> for Integrator<'a, 'tcx> { } } TerminatorKind::Return => { - *kind = TerminatorKind::Goto { target: self.return_block }; + terminator.kind = TerminatorKind::Goto { target: self.return_block }; } TerminatorKind::Resume => { if let Some(tgt) = self.cleanup_block { - *kind = TerminatorKind::Goto { target: tgt } + terminator.kind = TerminatorKind::Goto { target: tgt } } } TerminatorKind::Abort => {} diff --git a/src/librustc_mir/transform/no_landing_pads.rs b/src/librustc_mir/transform/no_landing_pads.rs index 3bffafa1b2f9..1d83733e4cd3 100644 --- a/src/librustc_mir/transform/no_landing_pads.rs +++ b/src/librustc_mir/transform/no_landing_pads.rs @@ -34,10 +34,10 @@ impl<'tcx> MutVisitor<'tcx> for NoLandingPads<'tcx> { self.tcx } - fn visit_terminator_kind(&mut self, kind: &mut TerminatorKind<'tcx>, location: Location) { - if let Some(unwind) = kind.unwind_mut() { + fn visit_terminator(&mut self, terminator: &mut Terminator<'tcx>, location: Location) { + if let Some(unwind) = terminator.kind.unwind_mut() { unwind.take(); } - self.super_terminator_kind(kind, location); + self.super_terminator(terminator, location); } } diff --git a/src/librustc_mir/transform/promote_consts.rs b/src/librustc_mir/transform/promote_consts.rs index e1311ccd3746..f2179a21a3f4 100644 --- a/src/librustc_mir/transform/promote_consts.rs +++ b/src/librustc_mir/transform/promote_consts.rs @@ -216,10 +216,10 @@ impl<'tcx> Visitor<'tcx> for Collector<'_, 'tcx> { } } - fn visit_terminator_kind(&mut self, kind: &TerminatorKind<'tcx>, location: Location) { - self.super_terminator_kind(kind, location); + fn visit_terminator(&mut self, terminator: &Terminator<'tcx>, location: Location) { + self.super_terminator(terminator, location); - match *kind { + match terminator.kind { TerminatorKind::Call { ref func, .. } => { if let ty::FnDef(def_id, _) = func.ty(self.ccx.body, self.ccx.tcx).kind { let fn_sig = self.ccx.tcx.fn_sig(def_id); From 302fb5039b6434ac1be617d3f4ac2863cf9ecfb1 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sun, 31 May 2020 14:47:54 +0200 Subject: [PATCH 083/123] get rid of an unused 'span' field --- src/librustc_mir/transform/promote_consts.rs | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/librustc_mir/transform/promote_consts.rs b/src/librustc_mir/transform/promote_consts.rs index f2179a21a3f4..662431032636 100644 --- a/src/librustc_mir/transform/promote_consts.rs +++ b/src/librustc_mir/transform/promote_consts.rs @@ -147,7 +147,6 @@ struct Collector<'a, 'tcx> { ccx: &'a ConstCx<'a, 'tcx>, temps: IndexVec, candidates: Vec, - span: Span, } impl<'tcx> Visitor<'tcx> for Collector<'_, 'tcx> { @@ -254,10 +253,6 @@ impl<'tcx> Visitor<'tcx> for Collector<'_, 'tcx> { _ => {} } } - - fn visit_source_info(&mut self, source_info: &SourceInfo) { - self.span = source_info.span; - } } pub fn collect_temps_and_candidates( @@ -267,7 +262,6 @@ pub fn collect_temps_and_candidates( let mut collector = Collector { temps: IndexVec::from_elem(TempState::Undefined, &ccx.body.local_decls), candidates: vec![], - span: ccx.body.span, ccx, }; for (bb, data) in rpo { From 93022be9917a472f7a16410ce7cb2b76a5d3afdd Mon Sep 17 00:00:00 2001 From: David Wood Date: Sat, 13 Jun 2020 17:04:06 +0100 Subject: [PATCH 084/123] bootstrap: read config from $RUST_BOOTSTRAP_CONFIG This commit modifies bootstrap so that `config.toml` is read first from `RUST_BOOTSTRAP_CONFIG`, then `--config` and finally `config.toml` in the current directory. This is a subjective change, intended to improve the ergnomics when using "development shells" for rustc development (for example, using tools such as Nix) which set environment variables to ensure a reproducible environment (these development shells can then be version controlled). By optionally reading `config.toml` from an environment variable, a `config.toml` can be defined in the development shell and a path to it exposed in the `RUST_BOOTSTRAP_CONFIG` environment variable - avoiding the need to manually symlink the contents of this file to `config.toml` in the working directory. Signed-off-by: David Wood --- src/bootstrap/bootstrap.py | 3 ++- src/bootstrap/flags.rs | 10 ++-------- 2 files changed, 4 insertions(+), 9 deletions(-) diff --git a/src/bootstrap/bootstrap.py b/src/bootstrap/bootstrap.py index b7d0fac5be31..969d16d11e81 100644 --- a/src/bootstrap/bootstrap.py +++ b/src/bootstrap/bootstrap.py @@ -894,7 +894,7 @@ def bootstrap(help_triggered): build.clean = args.clean try: - toml_path = args.config or 'config.toml' + toml_path = os.getenv('RUST_BOOTSTRAP_CONFIG') or args.config or 'config.toml' if not os.path.exists(toml_path): toml_path = os.path.join(build.rust_root, toml_path) @@ -947,6 +947,7 @@ def bootstrap(help_triggered): env["SRC"] = build.rust_root env["BOOTSTRAP_PARENT_ID"] = str(os.getpid()) env["BOOTSTRAP_PYTHON"] = sys.executable + env["BOOTSTRAP_CONFIG"] = toml_path env["BUILD_DIR"] = build.build_dir env["RUSTC_BOOTSTRAP"] = '1' env["CARGO"] = build.cargo() diff --git a/src/bootstrap/flags.rs b/src/bootstrap/flags.rs index cfaa43f39709..47b983868285 100644 --- a/src/bootstrap/flags.rs +++ b/src/bootstrap/flags.rs @@ -3,7 +3,7 @@ //! This module implements the command-line parsing of the build system which //! has various flags to configure how it's run. -use std::fs; +use std::env; use std::path::PathBuf; use std::process; @@ -433,13 +433,7 @@ Arguments: // Get any optional paths which occur after the subcommand let paths = matches.free[1..].iter().map(|p| p.into()).collect::>(); - let cfg_file = matches.opt_str("config").map(PathBuf::from).or_else(|| { - if fs::metadata("config.toml").is_ok() { - Some(PathBuf::from("config.toml")) - } else { - None - } - }); + let cfg_file = env::var_os("BOOTSTRAP_CONFIG").map(PathBuf::from); // All subcommands except `clean` can have an optional "Available paths" section if matches.opt_present("verbose") { From 046165a80729a22b9692614a658f105d833bfc8d Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Wed, 10 Jun 2020 09:56:54 +0200 Subject: [PATCH 085/123] rename location field of Drop terminators to place --- src/librustc_codegen_ssa/mir/block.rs | 4 +- src/librustc_middle/mir/mod.rs | 10 ++--- src/librustc_middle/mir/type_foldable.rs | 14 +++--- src/librustc_middle/mir/visit.rs | 8 ++-- src/librustc_mir/borrow_check/invalidation.rs | 4 +- src/librustc_mir/borrow_check/mod.rs | 4 +- .../borrow_check/type_check/mod.rs | 4 +- src/librustc_mir/borrow_check/used_muts.rs | 4 +- .../dataflow/framework/direction.rs | 4 +- .../dataflow/impls/borrowed_locals.rs | 4 +- .../dataflow/move_paths/builder.rs | 10 ++--- src/librustc_mir/interpret/terminator.rs | 6 +-- src/librustc_mir/monomorphize/collector.rs | 6 +-- src/librustc_mir/shim.rs | 16 ++----- .../transform/add_moves_for_packed_drops.rs | 14 +++--- .../check_consts/post_drop_elaboration.rs | 2 +- .../transform/check_consts/resolver.rs | 7 ++- .../transform/check_consts/validation.rs | 4 +- src/librustc_mir/transform/elaborate_drops.rs | 44 +++++++++---------- src/librustc_mir/transform/generator.rs | 11 ++--- src/librustc_mir/transform/inline.rs | 8 ++-- src/librustc_mir/transform/promote_consts.rs | 2 +- .../transform/qualify_min_const_fn.rs | 6 +-- src/librustc_mir/util/elaborate_drops.rs | 6 +-- src/librustc_mir_build/build/scope.rs | 8 ++-- 25 files changed, 99 insertions(+), 111 deletions(-) diff --git a/src/librustc_codegen_ssa/mir/block.rs b/src/librustc_codegen_ssa/mir/block.rs index ef59ad486eef..c486d5c64baa 100644 --- a/src/librustc_codegen_ssa/mir/block.rs +++ b/src/librustc_codegen_ssa/mir/block.rs @@ -998,8 +998,8 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { bx.unreachable(); } - mir::TerminatorKind::Drop { location, target, unwind } => { - self.codegen_drop_terminator(helper, bx, location, target, unwind); + mir::TerminatorKind::Drop { place, target, unwind } => { + self.codegen_drop_terminator(helper, bx, place, target, unwind); } mir::TerminatorKind::Assert { ref cond, expected, ref msg, target, cleanup } => { diff --git a/src/librustc_middle/mir/mod.rs b/src/librustc_middle/mir/mod.rs index 27848684706d..f281c1351766 100644 --- a/src/librustc_middle/mir/mod.rs +++ b/src/librustc_middle/mir/mod.rs @@ -1112,7 +1112,7 @@ pub enum TerminatorKind<'tcx> { Unreachable, /// Drop the `Place`. - Drop { location: Place<'tcx>, target: BasicBlock, unwind: Option }, + Drop { place: Place<'tcx>, target: BasicBlock, unwind: Option }, /// Drop the `Place` and assign the new value over it. This ensures /// that the assignment to `P` occurs *even if* the destructor for @@ -1141,7 +1141,7 @@ pub enum TerminatorKind<'tcx> { /// } /// ``` DropAndReplace { - location: Place<'tcx>, + place: Place<'tcx>, value: Operand<'tcx>, target: BasicBlock, unwind: Option, @@ -1607,9 +1607,9 @@ impl<'tcx> TerminatorKind<'tcx> { Abort => write!(fmt, "abort"), Yield { value, resume_arg, .. } => write!(fmt, "{:?} = yield({:?})", resume_arg, value), Unreachable => write!(fmt, "unreachable"), - Drop { location, .. } => write!(fmt, "drop({:?})", location), - DropAndReplace { location, value, .. } => { - write!(fmt, "replace({:?} <- {:?})", location, value) + Drop { place, .. } => write!(fmt, "drop({:?})", place), + DropAndReplace { place, value, .. } => { + write!(fmt, "replace({:?} <- {:?})", place, value) } Call { func, args, destination, .. } => { if let Some((destination, _)) = destination { diff --git a/src/librustc_middle/mir/type_foldable.rs b/src/librustc_middle/mir/type_foldable.rs index 3f5d528d9e7c..89f8f10449e2 100644 --- a/src/librustc_middle/mir/type_foldable.rs +++ b/src/librustc_middle/mir/type_foldable.rs @@ -27,11 +27,11 @@ impl<'tcx> TypeFoldable<'tcx> for Terminator<'tcx> { values: values.clone(), targets: targets.clone(), }, - Drop { ref location, target, unwind } => { - Drop { location: location.fold_with(folder), target, unwind } + Drop { ref place, target, unwind } => { + Drop { place: place.fold_with(folder), target, unwind } } - DropAndReplace { ref location, ref value, target, unwind } => DropAndReplace { - location: location.fold_with(folder), + DropAndReplace { ref place, ref value, target, unwind } => DropAndReplace { + place: place.fold_with(folder), value: value.fold_with(folder), target, unwind, @@ -97,9 +97,9 @@ impl<'tcx> TypeFoldable<'tcx> for Terminator<'tcx> { SwitchInt { ref discr, switch_ty, .. } => { discr.visit_with(visitor) || switch_ty.visit_with(visitor) } - Drop { ref location, .. } => location.visit_with(visitor), - DropAndReplace { ref location, ref value, .. } => { - location.visit_with(visitor) || value.visit_with(visitor) + Drop { ref place, .. } => place.visit_with(visitor), + DropAndReplace { ref place, ref value, .. } => { + place.visit_with(visitor) || value.visit_with(visitor) } Yield { ref value, .. } => value.visit_with(visitor), Call { ref func, ref args, ref destination, .. } => { diff --git a/src/librustc_middle/mir/visit.rs b/src/librustc_middle/mir/visit.rs index 1c5ed837b513..f2eda96f34ad 100644 --- a/src/librustc_middle/mir/visit.rs +++ b/src/librustc_middle/mir/visit.rs @@ -449,25 +449,25 @@ macro_rules! make_mir_visitor { } TerminatorKind::Drop { - location, + place, target: _, unwind: _, } => { self.visit_place( - location, + place, PlaceContext::MutatingUse(MutatingUseContext::Drop), source_location ); } TerminatorKind::DropAndReplace { - location, + place, value, target: _, unwind: _, } => { self.visit_place( - location, + place, PlaceContext::MutatingUse(MutatingUseContext::Drop), source_location ); diff --git a/src/librustc_mir/borrow_check/invalidation.rs b/src/librustc_mir/borrow_check/invalidation.rs index e9475937de22..fd8f17718e79 100644 --- a/src/librustc_mir/borrow_check/invalidation.rs +++ b/src/librustc_mir/borrow_check/invalidation.rs @@ -119,7 +119,7 @@ impl<'cx, 'tcx> Visitor<'tcx> for InvalidationGenerator<'cx, 'tcx> { TerminatorKind::SwitchInt { ref discr, switch_ty: _, values: _, targets: _ } => { self.consume_operand(location, discr); } - TerminatorKind::Drop { location: drop_place, target: _, unwind: _ } => { + TerminatorKind::Drop { place: drop_place, target: _, unwind: _ } => { self.access_place( location, *drop_place, @@ -128,7 +128,7 @@ impl<'cx, 'tcx> Visitor<'tcx> for InvalidationGenerator<'cx, 'tcx> { ); } TerminatorKind::DropAndReplace { - location: drop_place, + place: drop_place, value: ref new_value, target: _, unwind: _, diff --git a/src/librustc_mir/borrow_check/mod.rs b/src/librustc_mir/borrow_check/mod.rs index d099f48adc5c..83691d439eb8 100644 --- a/src/librustc_mir/borrow_check/mod.rs +++ b/src/librustc_mir/borrow_check/mod.rs @@ -663,7 +663,7 @@ impl<'cx, 'tcx> dataflow::ResultsVisitor<'cx, 'tcx> for MirBorrowckCtxt<'cx, 'tc TerminatorKind::SwitchInt { ref discr, switch_ty: _, values: _, targets: _ } => { self.consume_operand(loc, (discr, span), flow_state); } - TerminatorKind::Drop { location: ref drop_place, target: _, unwind: _ } => { + TerminatorKind::Drop { place: ref drop_place, target: _, unwind: _ } => { let tcx = self.infcx.tcx; // Compute the type with accurate region information. @@ -692,7 +692,7 @@ impl<'cx, 'tcx> dataflow::ResultsVisitor<'cx, 'tcx> for MirBorrowckCtxt<'cx, 'tc ); } TerminatorKind::DropAndReplace { - location: drop_place, + place: drop_place, value: ref new_value, target: _, unwind: _, diff --git a/src/librustc_mir/borrow_check/type_check/mod.rs b/src/librustc_mir/borrow_check/type_check/mod.rs index 168612f9beec..0eb0651d5fdc 100644 --- a/src/librustc_mir/borrow_check/type_check/mod.rs +++ b/src/librustc_mir/borrow_check/type_check/mod.rs @@ -1558,8 +1558,8 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { // no checks needed for these } - TerminatorKind::DropAndReplace { ref location, ref value, target: _, unwind: _ } => { - let place_ty = location.ty(body, tcx).ty; + TerminatorKind::DropAndReplace { ref place, ref value, target: _, unwind: _ } => { + let place_ty = place.ty(body, tcx).ty; let rv_ty = value.ty(body, tcx); let locations = term_location.to_locations(); diff --git a/src/librustc_mir/borrow_check/used_muts.rs b/src/librustc_mir/borrow_check/used_muts.rs index 021629846347..56764a4be5d9 100644 --- a/src/librustc_mir/borrow_check/used_muts.rs +++ b/src/librustc_mir/borrow_check/used_muts.rs @@ -70,8 +70,8 @@ impl<'visit, 'cx, 'tcx> Visitor<'tcx> for GatherUsedMutsVisitor<'visit, 'cx, 'tc TerminatorKind::Call { destination: Some((into, _)), .. } => { self.remove_never_initialized_mut_locals(*into); } - TerminatorKind::DropAndReplace { location, .. } => { - self.remove_never_initialized_mut_locals(*location); + TerminatorKind::DropAndReplace { place, .. } => { + self.remove_never_initialized_mut_locals(*place); } _ => {} } diff --git a/src/librustc_mir/dataflow/framework/direction.rs b/src/librustc_mir/dataflow/framework/direction.rs index 6c9cb529dc2f..4512ae96c083 100644 --- a/src/librustc_mir/dataflow/framework/direction.rs +++ b/src/librustc_mir/dataflow/framework/direction.rs @@ -441,8 +441,8 @@ impl Direction for Forward { Goto { target } => propagate(target, exit_state), Assert { target, cleanup: unwind, expected: _, msg: _, cond: _ } - | Drop { target, unwind, location: _ } - | DropAndReplace { target, unwind, value: _, location: _ } + | Drop { target, unwind, place: _ } + | DropAndReplace { target, unwind, value: _, place: _ } | FalseUnwind { real_target: target, unwind } => { if let Some(unwind) = unwind { if dead_unwinds.map_or(true, |dead| !dead.contains(bb)) { diff --git a/src/librustc_mir/dataflow/impls/borrowed_locals.rs b/src/librustc_mir/dataflow/impls/borrowed_locals.rs index 1d49a32e1964..70c916a08927 100644 --- a/src/librustc_mir/dataflow/impls/borrowed_locals.rs +++ b/src/librustc_mir/dataflow/impls/borrowed_locals.rs @@ -189,8 +189,8 @@ where self.super_terminator(terminator, location); match terminator.kind { - mir::TerminatorKind::Drop { location: dropped_place, .. } - | mir::TerminatorKind::DropAndReplace { location: dropped_place, .. } => { + mir::TerminatorKind::Drop { place: dropped_place, .. } + | mir::TerminatorKind::DropAndReplace { place: dropped_place, .. } => { // See documentation for `unsound_ignore_borrow_on_drop` for an explanation. if !self.ignore_borrow_on_drop { self.trans.gen(dropped_place.local); diff --git a/src/librustc_mir/dataflow/move_paths/builder.rs b/src/librustc_mir/dataflow/move_paths/builder.rs index 41c7bd95a96c..7c8aa1db71ff 100644 --- a/src/librustc_mir/dataflow/move_paths/builder.rs +++ b/src/librustc_mir/dataflow/move_paths/builder.rs @@ -387,13 +387,13 @@ impl<'b, 'a, 'tcx> Gatherer<'b, 'a, 'tcx> { self.gather_init(place.as_ref(), InitKind::Deep); } - TerminatorKind::Drop { location, target: _, unwind: _ } => { - self.gather_move(location); + TerminatorKind::Drop { place, target: _, unwind: _ } => { + self.gather_move(place); } - TerminatorKind::DropAndReplace { location, ref value, .. } => { - self.create_move_path(location); + TerminatorKind::DropAndReplace { place, ref value, .. } => { + self.create_move_path(place); self.gather_operand(value); - self.gather_init(location.as_ref(), InitKind::Deep); + self.gather_init(place.as_ref(), InitKind::Deep); } TerminatorKind::Call { ref func, diff --git a/src/librustc_mir/interpret/terminator.rs b/src/librustc_mir/interpret/terminator.rs index cd7621ea9752..1d57fce39734 100644 --- a/src/librustc_mir/interpret/terminator.rs +++ b/src/librustc_mir/interpret/terminator.rs @@ -91,10 +91,10 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { } } - Drop { location, target, unwind } => { - let place = self.eval_place(location)?; + Drop { place, target, unwind } => { + let place = self.eval_place(place)?; let ty = place.layout.ty; - trace!("TerminatorKind::drop: {:?}, type {}", location, ty); + trace!("TerminatorKind::drop: {:?}, type {}", place, ty); let instance = Instance::resolve_drop_in_place(*self.tcx, ty); self.drop_in_place(place, instance, target, unwind)?; diff --git a/src/librustc_mir/monomorphize/collector.rs b/src/librustc_mir/monomorphize/collector.rs index ac19d59f04fb..586944542410 100644 --- a/src/librustc_mir/monomorphize/collector.rs +++ b/src/librustc_mir/monomorphize/collector.rs @@ -626,9 +626,9 @@ impl<'a, 'tcx> MirVisitor<'tcx> for MirNeighborCollector<'a, 'tcx> { let callee_ty = self.monomorphize(callee_ty); visit_fn_use(self.tcx, callee_ty, true, &mut self.output); } - mir::TerminatorKind::Drop { ref location, .. } - | mir::TerminatorKind::DropAndReplace { ref location, .. } => { - let ty = location.ty(self.body, self.tcx).ty; + mir::TerminatorKind::Drop { ref place, .. } + | mir::TerminatorKind::DropAndReplace { ref place, .. } => { + let ty = place.ty(self.body, self.tcx).ty; let ty = self.monomorphize(ty); visit_drop_use(self.tcx, ty, true, self.output); } diff --git a/src/librustc_mir/shim.rs b/src/librustc_mir/shim.rs index f95fd9b9e90c..71fff8515314 100644 --- a/src/librustc_mir/shim.rs +++ b/src/librustc_mir/shim.rs @@ -582,7 +582,7 @@ impl CloneShimBuilder<'tcx> { self.block( vec![], TerminatorKind::Drop { - location: self.tcx.mk_place_index(dest, beg), + place: self.tcx.mk_place_index(dest, beg), target: BasicBlock::new(8), unwind: None, }, @@ -634,7 +634,7 @@ impl CloneShimBuilder<'tcx> { self.block( vec![], TerminatorKind::Drop { - location: previous_field, + place: previous_field, target: previous_cleanup, unwind: None, }, @@ -799,11 +799,7 @@ fn build_call_shim<'tcx>( block( &mut blocks, vec![], - TerminatorKind::Drop { - location: rcvr_place(), - target: BasicBlock::new(2), - unwind: None, - }, + TerminatorKind::Drop { place: rcvr_place(), target: BasicBlock::new(2), unwind: None }, false, ); } @@ -814,11 +810,7 @@ fn build_call_shim<'tcx>( block( &mut blocks, vec![], - TerminatorKind::Drop { - location: rcvr_place(), - target: BasicBlock::new(4), - unwind: None, - }, + TerminatorKind::Drop { place: rcvr_place(), target: BasicBlock::new(4), unwind: None }, true, ); diff --git a/src/librustc_mir/transform/add_moves_for_packed_drops.rs b/src/librustc_mir/transform/add_moves_for_packed_drops.rs index 39ce2340aed2..a02d0f655600 100644 --- a/src/librustc_mir/transform/add_moves_for_packed_drops.rs +++ b/src/librustc_mir/transform/add_moves_for_packed_drops.rs @@ -64,8 +64,8 @@ fn add_moves_for_packed_drops_patch<'tcx>( let terminator = data.terminator(); match terminator.kind { - TerminatorKind::Drop { location, .. } - if util::is_disaligned(tcx, body, param_env, location) => + TerminatorKind::Drop { place, .. } + if util::is_disaligned(tcx, body, param_env, place) => { add_move_for_packed_drop(tcx, body, &mut patch, terminator, loc, data.is_cleanup); } @@ -88,13 +88,13 @@ fn add_move_for_packed_drop<'tcx>( is_cleanup: bool, ) { debug!("add_move_for_packed_drop({:?} @ {:?})", terminator, loc); - let (location, target, unwind) = match terminator.kind { - TerminatorKind::Drop { ref location, target, unwind } => (location, target, unwind), + let (place, target, unwind) = match terminator.kind { + TerminatorKind::Drop { ref place, target, unwind } => (place, target, unwind), _ => unreachable!(), }; let source_info = terminator.source_info; - let ty = location.ty(body, tcx).ty; + let ty = place.ty(body, tcx).ty; let temp = patch.new_temp(ty, terminator.source_info.span); let storage_dead_block = patch.new_block(BasicBlockData { @@ -104,9 +104,9 @@ fn add_move_for_packed_drop<'tcx>( }); patch.add_statement(loc, StatementKind::StorageLive(temp)); - patch.add_assign(loc, Place::from(temp), Rvalue::Use(Operand::Move(*location))); + patch.add_assign(loc, Place::from(temp), Rvalue::Use(Operand::Move(*place))); patch.patch_terminator( loc.block, - TerminatorKind::Drop { location: Place::from(temp), target: storage_dead_block, unwind }, + TerminatorKind::Drop { place: Place::from(temp), target: storage_dead_block, unwind }, ); } diff --git a/src/librustc_mir/transform/check_consts/post_drop_elaboration.rs b/src/librustc_mir/transform/check_consts/post_drop_elaboration.rs index 226e0e2049eb..124606fb423e 100644 --- a/src/librustc_mir/transform/check_consts/post_drop_elaboration.rs +++ b/src/librustc_mir/transform/check_consts/post_drop_elaboration.rs @@ -78,7 +78,7 @@ impl Visitor<'tcx> for CheckLiveDrops<'mir, 'tcx> { trace!("visit_terminator: terminator={:?} location={:?}", terminator, location); match &terminator.kind { - mir::TerminatorKind::Drop { location: dropped_place, .. } => { + mir::TerminatorKind::Drop { place: dropped_place, .. } => { let dropped_ty = dropped_place.ty(self.body, self.tcx).ty; if !NeedsDrop::in_any_value_of_ty(self.ccx, dropped_ty) { return; diff --git a/src/librustc_mir/transform/check_consts/resolver.rs b/src/librustc_mir/transform/check_consts/resolver.rs index c67545d5b39e..b8104292aab2 100644 --- a/src/librustc_mir/transform/check_consts/resolver.rs +++ b/src/librustc_mir/transform/check_consts/resolver.rs @@ -125,16 +125,15 @@ where // The effect of assignment to the return place in `TerminatorKind::Call` is not applied // here; that occurs in `apply_call_return_effect`. - if let mir::TerminatorKind::DropAndReplace { value, location: dest, .. } = &terminator.kind - { + if let mir::TerminatorKind::DropAndReplace { value, place, .. } = &terminator.kind { let qualif = qualifs::in_operand::( self.ccx, &mut |l| self.qualifs_per_local.contains(l), value, ); - if !dest.is_indirect() { - self.assign_qualif_direct(dest, qualif); + if !place.is_indirect() { + self.assign_qualif_direct(place, qualif); } } diff --git a/src/librustc_mir/transform/check_consts/validation.rs b/src/librustc_mir/transform/check_consts/validation.rs index 428a74bcdcbf..35a8df62cb83 100644 --- a/src/librustc_mir/transform/check_consts/validation.rs +++ b/src/librustc_mir/transform/check_consts/validation.rs @@ -560,8 +560,8 @@ impl Visitor<'tcx> for Validator<'mir, 'tcx> { // Forbid all `Drop` terminators unless the place being dropped is a local with no // projections that cannot be `NeedsDrop`. - TerminatorKind::Drop { location: dropped_place, .. } - | TerminatorKind::DropAndReplace { location: dropped_place, .. } => { + TerminatorKind::Drop { place: dropped_place, .. } + | TerminatorKind::DropAndReplace { place: dropped_place, .. } => { // If we are checking live drops after drop-elaboration, don't emit duplicate // errors here. if super::post_drop_elaboration::checking_enabled(self.tcx) { diff --git a/src/librustc_mir/transform/elaborate_drops.rs b/src/librustc_mir/transform/elaborate_drops.rs index e4129f447d53..1704d8baabdc 100644 --- a/src/librustc_mir/transform/elaborate_drops.rs +++ b/src/librustc_mir/transform/elaborate_drops.rs @@ -85,15 +85,15 @@ fn find_dead_unwinds<'tcx>( .iterate_to_fixpoint() .into_results_cursor(body); for (bb, bb_data) in body.basic_blocks().iter_enumerated() { - let location = match bb_data.terminator().kind { - TerminatorKind::Drop { ref location, unwind: Some(_), .. } - | TerminatorKind::DropAndReplace { ref location, unwind: Some(_), .. } => location, + let place = match bb_data.terminator().kind { + TerminatorKind::Drop { ref place, unwind: Some(_), .. } + | TerminatorKind::DropAndReplace { ref place, unwind: Some(_), .. } => place, _ => continue, }; debug!("find_dead_unwinds @ {:?}: {:?}", bb, bb_data); - let path = match env.move_data.rev_lookup.find(location.as_ref()) { + let path = match env.move_data.rev_lookup.find(place.as_ref()) { LookupResult::Exact(e) => e, LookupResult::Parent(..) => { debug!("find_dead_unwinds: has parent; skipping"); @@ -105,7 +105,7 @@ fn find_dead_unwinds<'tcx>( debug!( "find_dead_unwinds @ {:?}: path({:?})={:?}; init_data={:?}", bb, - location, + place, path, flow_inits.get() ); @@ -294,16 +294,16 @@ impl<'b, 'tcx> ElaborateDropsCtxt<'b, 'tcx> { fn collect_drop_flags(&mut self) { for (bb, data) in self.body.basic_blocks().iter_enumerated() { let terminator = data.terminator(); - let location = match terminator.kind { - TerminatorKind::Drop { ref location, .. } - | TerminatorKind::DropAndReplace { ref location, .. } => location, + let place = match terminator.kind { + TerminatorKind::Drop { ref place, .. } + | TerminatorKind::DropAndReplace { ref place, .. } => place, _ => continue, }; self.init_data.seek_before(self.body.terminator_loc(bb)); - let path = self.move_data().rev_lookup.find(location.as_ref()); - debug!("collect_drop_flags: {:?}, place {:?} ({:?})", bb, location, path); + let path = self.move_data().rev_lookup.find(place.as_ref()); + debug!("collect_drop_flags: {:?}, place {:?} ({:?})", bb, place, path); let path = match path { LookupResult::Exact(e) => e, @@ -315,7 +315,7 @@ impl<'b, 'tcx> ElaborateDropsCtxt<'b, 'tcx> { terminator.source_info.span, "drop of untracked, uninitialized value {:?}, place {:?} ({:?})", bb, - location, + place, path ); } @@ -328,7 +328,7 @@ impl<'b, 'tcx> ElaborateDropsCtxt<'b, 'tcx> { debug!( "collect_drop_flags: collecting {:?} from {:?}@{:?} - {:?}", child, - location, + place, path, (maybe_live, maybe_dead) ); @@ -346,13 +346,13 @@ impl<'b, 'tcx> ElaborateDropsCtxt<'b, 'tcx> { let resume_block = self.patch.resume_block(); match terminator.kind { - TerminatorKind::Drop { location, target, unwind } => { + TerminatorKind::Drop { place, target, unwind } => { self.init_data.seek_before(loc); - match self.move_data().rev_lookup.find(location.as_ref()) { + match self.move_data().rev_lookup.find(place.as_ref()) { LookupResult::Exact(path) => elaborate_drop( &mut Elaborator { ctxt: self }, terminator.source_info, - location, + place, path, target, if data.is_cleanup { @@ -371,10 +371,10 @@ impl<'b, 'tcx> ElaborateDropsCtxt<'b, 'tcx> { } } } - TerminatorKind::DropAndReplace { location, ref value, target, unwind } => { + TerminatorKind::DropAndReplace { place, ref value, target, unwind } => { assert!(!data.is_cleanup); - self.elaborate_replace(loc, location, value, target, unwind); + self.elaborate_replace(loc, place, value, target, unwind); } _ => continue, } @@ -396,7 +396,7 @@ impl<'b, 'tcx> ElaborateDropsCtxt<'b, 'tcx> { fn elaborate_replace( &mut self, loc: Location, - location: Place<'tcx>, + place: Place<'tcx>, value: &Operand<'tcx>, target: BasicBlock, unwind: Option, @@ -407,7 +407,7 @@ impl<'b, 'tcx> ElaborateDropsCtxt<'b, 'tcx> { assert!(!data.is_cleanup, "DropAndReplace in unwind path not supported"); let assign = Statement { - kind: StatementKind::Assign(box (location, Rvalue::Use(value.clone()))), + kind: StatementKind::Assign(box (place, Rvalue::Use(value.clone()))), source_info: terminator.source_info, }; @@ -427,14 +427,14 @@ impl<'b, 'tcx> ElaborateDropsCtxt<'b, 'tcx> { is_cleanup: false, }); - match self.move_data().rev_lookup.find(location.as_ref()) { + match self.move_data().rev_lookup.find(place.as_ref()) { LookupResult::Exact(path) => { debug!("elaborate_drop_and_replace({:?}) - tracked {:?}", terminator, path); self.init_data.seek_before(loc); elaborate_drop( &mut Elaborator { ctxt: self }, terminator.source_info, - location, + place, path, target, Unwind::To(unwind), @@ -459,7 +459,7 @@ impl<'b, 'tcx> ElaborateDropsCtxt<'b, 'tcx> { debug!("elaborate_drop_and_replace({:?}) - untracked {:?}", terminator, parent); self.patch.patch_terminator( bb, - TerminatorKind::Drop { location, target, unwind: Some(unwind) }, + TerminatorKind::Drop { place, target, unwind: Some(unwind) }, ); } } diff --git a/src/librustc_mir/transform/generator.rs b/src/librustc_mir/transform/generator.rs index 7215f390d40d..b2431e98e2d0 100644 --- a/src/librustc_mir/transform/generator.rs +++ b/src/librustc_mir/transform/generator.rs @@ -835,8 +835,8 @@ fn elaborate_generator_drops<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId, body: &mut for (block, block_data) in body.basic_blocks().iter_enumerated() { let (target, unwind, source_info) = match block_data.terminator() { - Terminator { source_info, kind: TerminatorKind::Drop { location, target, unwind } } => { - if let Some(local) = location.as_local() { + Terminator { source_info, kind: TerminatorKind::Drop { place, target, unwind } } => { + if let Some(local) = place.as_local() { if local == SELF_ARG { (target, unwind, source_info) } else { @@ -1102,11 +1102,8 @@ fn create_generator_resume_function<'tcx>( fn insert_clean_drop(body: &mut Body<'_>) -> BasicBlock { let return_block = insert_term_block(body, TerminatorKind::Return); - let term = TerminatorKind::Drop { - location: Place::from(SELF_ARG), - target: return_block, - unwind: None, - }; + let term = + TerminatorKind::Drop { place: Place::from(SELF_ARG), target: return_block, unwind: None }; let source_info = SourceInfo::outermost(body.span); // Create a block to destroy an unresumed generators. This can only destroy upvars. diff --git a/src/librustc_mir/transform/inline.rs b/src/librustc_mir/transform/inline.rs index 65400b58eebc..db909494aed6 100644 --- a/src/librustc_mir/transform/inline.rs +++ b/src/librustc_mir/transform/inline.rs @@ -319,13 +319,13 @@ impl Inliner<'tcx> { let term = blk.terminator(); let mut is_drop = false; match term.kind { - TerminatorKind::Drop { ref location, target, unwind } - | TerminatorKind::DropAndReplace { ref location, target, unwind, .. } => { + TerminatorKind::Drop { ref place, target, unwind } + | TerminatorKind::DropAndReplace { ref place, target, unwind, .. } => { is_drop = true; work_list.push(target); - // If the location doesn't actually need dropping, treat it like + // If the place doesn't actually need dropping, treat it like // a regular goto. - let ty = location.ty(callee_body, tcx).subst(tcx, callsite.substs).ty; + let ty = place.ty(callee_body, tcx).subst(tcx, callsite.substs).ty; if ty.needs_drop(tcx, param_env) { cost += CALL_PENALTY; if let Some(unwind) = unwind { diff --git a/src/librustc_mir/transform/promote_consts.rs b/src/librustc_mir/transform/promote_consts.rs index 662431032636..330f6c1640ff 100644 --- a/src/librustc_mir/transform/promote_consts.rs +++ b/src/librustc_mir/transform/promote_consts.rs @@ -1186,7 +1186,7 @@ pub fn promote_candidates<'tcx>( _ => true, }); let terminator = block.terminator_mut(); - if let TerminatorKind::Drop { location: place, target, .. } = &terminator.kind { + if let TerminatorKind::Drop { place, target, .. } = &terminator.kind { if let Some(index) = place.as_local() { if promoted(index) { terminator.kind = TerminatorKind::Goto { target: *target }; diff --git a/src/librustc_mir/transform/qualify_min_const_fn.rs b/src/librustc_mir/transform/qualify_min_const_fn.rs index 4c8fc49099b2..caf6c7715a9e 100644 --- a/src/librustc_mir/transform/qualify_min_const_fn.rs +++ b/src/librustc_mir/transform/qualify_min_const_fn.rs @@ -349,9 +349,9 @@ fn check_terminator( | TerminatorKind::Resume | TerminatorKind::Unreachable => Ok(()), - TerminatorKind::Drop { location, .. } => check_place(tcx, *location, span, def_id, body), - TerminatorKind::DropAndReplace { location, value, .. } => { - check_place(tcx, *location, span, def_id, body)?; + TerminatorKind::Drop { place, .. } => check_place(tcx, *place, span, def_id, body), + TerminatorKind::DropAndReplace { place, value, .. } => { + check_place(tcx, *place, span, def_id, body)?; check_operand(tcx, value, span, def_id, body) } diff --git a/src/librustc_mir/util/elaborate_drops.rs b/src/librustc_mir/util/elaborate_drops.rs index a1345452ca97..5f55a812a4e0 100644 --- a/src/librustc_mir/util/elaborate_drops.rs +++ b/src/librustc_mir/util/elaborate_drops.rs @@ -238,7 +238,7 @@ where self.elaborator.patch().patch_terminator( bb, TerminatorKind::Drop { - location: self.place, + place: self.place, target: self.succ, unwind: self.unwind.into_option(), }, @@ -723,7 +723,7 @@ where self.elaborator.patch().patch_terminator( drop_block, TerminatorKind::Drop { - location: tcx.mk_place_deref(ptr), + place: tcx.mk_place_deref(ptr), target: loop_block, unwind: unwind.into_option(), }, @@ -1000,7 +1000,7 @@ where fn drop_block(&mut self, target: BasicBlock, unwind: Unwind) -> BasicBlock { let block = - TerminatorKind::Drop { location: self.place, target, unwind: unwind.into_option() }; + TerminatorKind::Drop { place: self.place, target, unwind: unwind.into_option() }; self.new_block(unwind, block) } diff --git a/src/librustc_mir_build/build/scope.rs b/src/librustc_mir_build/build/scope.rs index 4daf567d7d45..b8df27094471 100644 --- a/src/librustc_mir_build/build/scope.rs +++ b/src/librustc_mir_build/build/scope.rs @@ -1037,7 +1037,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { &mut self, block: BasicBlock, span: Span, - location: Place<'tcx>, + place: Place<'tcx>, value: Operand<'tcx>, ) -> BlockAnd<()> { let source_info = self.source_info(span); @@ -1047,7 +1047,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { block, source_info, TerminatorKind::DropAndReplace { - location, + place, value, target: next_target, unwind: Some(diverge_target), @@ -1158,7 +1158,7 @@ fn build_scope_drops<'tcx>( block, source_info, TerminatorKind::Drop { - location: local.into(), + place: local.into(), target: next, unwind: Some(unwind_to), }, @@ -1272,7 +1272,7 @@ fn build_diverge_scope<'tcx>( block, source_info(drop_data.span), TerminatorKind::Drop { - location: drop_data.local.into(), + place: drop_data.local.into(), target, unwind: None, }, From 6c5345f2defa99fd8fef8940f682fe2b6c6249f9 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Wed, 10 Jun 2020 10:03:26 +0200 Subject: [PATCH 086/123] fmt; make visit_terminator arg names consistent with the rest --- src/librustc_middle/mir/visit.rs | 38 ++++++++++++++++---------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/src/librustc_middle/mir/visit.rs b/src/librustc_middle/mir/visit.rs index f2eda96f34ad..2efc5f1dabed 100644 --- a/src/librustc_middle/mir/visit.rs +++ b/src/librustc_middle/mir/visit.rs @@ -407,7 +407,7 @@ macro_rules! make_mir_visitor { fn super_terminator(&mut self, terminator: &$($mutability)? Terminator<'tcx>, - source_location: Location) { + location: Location) { let Terminator { source_info, kind } = terminator; self.visit_source_info(source_info); @@ -428,7 +428,7 @@ macro_rules! make_mir_visitor { self.visit_local( & $($mutability)? local, PlaceContext::NonMutatingUse(NonMutatingUseContext::Move), - source_location, + location, ); assert_eq!( @@ -444,8 +444,8 @@ macro_rules! make_mir_visitor { values: _, targets: _ } => { - self.visit_operand(discr, source_location); - self.visit_ty(switch_ty, TyContext::Location(source_location)); + self.visit_operand(discr, location); + self.visit_ty(switch_ty, TyContext::Location(location)); } TerminatorKind::Drop { @@ -456,7 +456,7 @@ macro_rules! make_mir_visitor { self.visit_place( place, PlaceContext::MutatingUse(MutatingUseContext::Drop), - source_location + location ); } @@ -469,9 +469,9 @@ macro_rules! make_mir_visitor { self.visit_place( place, PlaceContext::MutatingUse(MutatingUseContext::Drop), - source_location + location ); - self.visit_operand(value, source_location); + self.visit_operand(value, location); } TerminatorKind::Call { @@ -482,15 +482,15 @@ macro_rules! make_mir_visitor { from_hir_call: _, fn_span: _ } => { - self.visit_operand(func, source_location); + self.visit_operand(func, location); for arg in args { - self.visit_operand(arg, source_location); + self.visit_operand(arg, location); } if let Some((destination, _)) = destination { self.visit_place( destination, PlaceContext::MutatingUse(MutatingUseContext::Call), - source_location + location ); } } @@ -502,8 +502,8 @@ macro_rules! make_mir_visitor { target: _, cleanup: _, } => { - self.visit_operand(cond, source_location); - self.visit_assert_message(msg, source_location); + self.visit_operand(cond, location); + self.visit_assert_message(msg, location); } TerminatorKind::Yield { @@ -512,11 +512,11 @@ macro_rules! make_mir_visitor { resume_arg, drop: _, } => { - self.visit_operand(value, source_location); + self.visit_operand(value, location); self.visit_place( resume_arg, PlaceContext::MutatingUse(MutatingUseContext::Yield), - source_location, + location, ); } @@ -531,29 +531,29 @@ macro_rules! make_mir_visitor { match op { InlineAsmOperand::In { value, .. } | InlineAsmOperand::Const { value } => { - self.visit_operand(value, source_location); + self.visit_operand(value, location); } InlineAsmOperand::Out { place, .. } => { if let Some(place) = place { self.visit_place( place, PlaceContext::MutatingUse(MutatingUseContext::Store), - source_location, + location, ); } } InlineAsmOperand::InOut { in_value, out_place, .. } => { - self.visit_operand(in_value, source_location); + self.visit_operand(in_value, location); if let Some(out_place) = out_place { self.visit_place( out_place, PlaceContext::MutatingUse(MutatingUseContext::Store), - source_location, + location, ); } } InlineAsmOperand::SymFn { value } => { - self.visit_constant(value, source_location); + self.visit_constant(value, location); } InlineAsmOperand::SymStatic { def_id: _ } => {} } From 827ccf77183c02dc7d362b64debe2bf1c6bfd5fa Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Wed, 10 Jun 2020 10:10:09 +0200 Subject: [PATCH 087/123] add probably accidentally missing super_* calls --- src/librustc_mir/borrow_check/used_muts.rs | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/librustc_mir/borrow_check/used_muts.rs b/src/librustc_mir/borrow_check/used_muts.rs index 56764a4be5d9..e027056842db 100644 --- a/src/librustc_mir/borrow_check/used_muts.rs +++ b/src/librustc_mir/borrow_check/used_muts.rs @@ -64,7 +64,7 @@ impl GatherUsedMutsVisitor<'_, '_, '_> { } impl<'visit, 'cx, 'tcx> Visitor<'tcx> for GatherUsedMutsVisitor<'visit, 'cx, 'tcx> { - fn visit_terminator(&mut self, terminator: &Terminator<'tcx>, _location: Location) { + fn visit_terminator(&mut self, terminator: &Terminator<'tcx>, location: Location) { debug!("visit_terminator: terminator={:?}", terminator); match &terminator.kind { TerminatorKind::Call { destination: Some((into, _)), .. } => { @@ -76,10 +76,10 @@ impl<'visit, 'cx, 'tcx> Visitor<'tcx> for GatherUsedMutsVisitor<'visit, 'cx, 'tc _ => {} } - // FIXME: no super_terminator? + self.super_terminator(terminator, location); } - fn visit_statement(&mut self, statement: &Statement<'tcx>, _location: Location) { + fn visit_statement(&mut self, statement: &Statement<'tcx>, location: Location) { if let StatementKind::Assign(box (into, _)) = &statement.kind { debug!( "visit_statement: statement={:?} local={:?} \ @@ -89,7 +89,7 @@ impl<'visit, 'cx, 'tcx> Visitor<'tcx> for GatherUsedMutsVisitor<'visit, 'cx, 'tc self.remove_never_initialized_mut_locals(*into); } - // FIXME: no super_statement? + self.super_statement(statement, location); } fn visit_local(&mut self, local: &Local, place_context: PlaceContext, location: Location) { @@ -107,7 +107,5 @@ impl<'visit, 'cx, 'tcx> Visitor<'tcx> for GatherUsedMutsVisitor<'visit, 'cx, 'tc } } } - - // FIXME: no super_local? } } From a19dfb573d18c8b937c159cda24a3bb40ca5082d Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Fri, 12 Jun 2020 14:01:47 +0200 Subject: [PATCH 088/123] Create new E0763 error code for unterminated byte constant --- src/librustc_error_codes/error_codes.rs | 1 + src/librustc_error_codes/error_codes/E0763.md | 13 +++++++++++++ src/librustc_parse/lexer/mod.rs | 11 +++++++++-- 3 files changed, 23 insertions(+), 2 deletions(-) create mode 100644 src/librustc_error_codes/error_codes/E0763.md diff --git a/src/librustc_error_codes/error_codes.rs b/src/librustc_error_codes/error_codes.rs index 3fb5e04efc92..1d6a579bdff3 100644 --- a/src/librustc_error_codes/error_codes.rs +++ b/src/librustc_error_codes/error_codes.rs @@ -442,6 +442,7 @@ E0758: include_str!("./error_codes/E0758.md"), E0760: include_str!("./error_codes/E0760.md"), E0761: include_str!("./error_codes/E0761.md"), E0762: include_str!("./error_codes/E0762.md"), +E0763: include_str!("./error_codes/E0763.md"), ; // E0006, // merged with E0005 // E0008, // cannot bind by-move into a pattern guard diff --git a/src/librustc_error_codes/error_codes/E0763.md b/src/librustc_error_codes/error_codes/E0763.md new file mode 100644 index 000000000000..095b779f3e78 --- /dev/null +++ b/src/librustc_error_codes/error_codes/E0763.md @@ -0,0 +1,13 @@ +A byte constant wasn't correctly ended. + +Erroneous code example: + +```compile_fail,E0763 +let c = b'a; // error! +``` + +To fix this error, add the missing quote: + +``` +let c = b'a'; // ok! +``` diff --git a/src/librustc_parse/lexer/mod.rs b/src/librustc_parse/lexer/mod.rs index 84b3335a0f62..2e3cf4e746ae 100644 --- a/src/librustc_parse/lexer/mod.rs +++ b/src/librustc_parse/lexer/mod.rs @@ -339,8 +339,15 @@ impl<'a> StringReader<'a> { } rustc_lexer::LiteralKind::Byte { terminated } => { if !terminated { - self.fatal_span_(start + BytePos(1), suffix_start, "unterminated byte constant") - .raise() + self.sess + .span_diagnostic + .struct_span_fatal_with_code( + self.mk_sp(start + BytePos(1), suffix_start), + "unterminated byte constant", + error_code!(E0763), + ) + .emit(); + FatalError.raise(); } (token::Byte, Mode::Byte, 2, 1) // b' ' } From bad252c9faebf55565091f50bad784a0a3f1e756 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Fri, 12 Jun 2020 14:01:53 +0200 Subject: [PATCH 089/123] Update ui tests --- src/test/ui/parser/byte-literals.rs | 2 +- src/test/ui/parser/byte-literals.stderr | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/test/ui/parser/byte-literals.rs b/src/test/ui/parser/byte-literals.rs index dadf3971220f..9683a83e7209 100644 --- a/src/test/ui/parser/byte-literals.rs +++ b/src/test/ui/parser/byte-literals.rs @@ -8,5 +8,5 @@ pub fn main() { b' '; //~ ERROR byte constant must be escaped b'''; //~ ERROR byte constant must be escaped b'é'; //~ ERROR byte constant must be ASCII - b'a //~ ERROR unterminated byte constant + b'a //~ ERROR unterminated byte constant [E0763] } diff --git a/src/test/ui/parser/byte-literals.stderr b/src/test/ui/parser/byte-literals.stderr index 53d50af88d33..7bbdc07cd835 100644 --- a/src/test/ui/parser/byte-literals.stderr +++ b/src/test/ui/parser/byte-literals.stderr @@ -34,7 +34,7 @@ error: byte constant must be ASCII. Use a \xHH escape for a non-ASCII byte LL | b'é'; | ^ -error: unterminated byte constant +error[E0763]: unterminated byte constant --> $DIR/byte-literals.rs:11:6 | LL | b'a @@ -42,3 +42,4 @@ LL | b'a error: aborting due to 7 previous errors +For more information about this error, try `rustc --explain E0763`. From 1990f9777f137ad7d7a69dfd23425d4337c06358 Mon Sep 17 00:00:00 2001 From: Charles Lew Date: Sat, 13 Jun 2020 13:27:22 +0800 Subject: [PATCH 090/123] Disallow loading crates with non-ascii identifier name. --- src/librustc_metadata/creader.rs | 8 ++++++++ .../rfc-2457/crate_name_nonascii_forbidden-1.rs | 6 ++++++ .../crate_name_nonascii_forbidden-1.stderr | 15 +++++++++++++++ .../rfc-2457/crate_name_nonascii_forbidden-2.rs | 9 +++++++++ .../crate_name_nonascii_forbidden-2.stderr | 15 +++++++++++++++ 5 files changed, 53 insertions(+) create mode 100644 src/test/ui/rfc-2457/crate_name_nonascii_forbidden-1.rs create mode 100644 src/test/ui/rfc-2457/crate_name_nonascii_forbidden-1.stderr create mode 100644 src/test/ui/rfc-2457/crate_name_nonascii_forbidden-2.rs create mode 100644 src/test/ui/rfc-2457/crate_name_nonascii_forbidden-2.stderr diff --git a/src/librustc_metadata/creader.rs b/src/librustc_metadata/creader.rs index 7e902f0ade2e..c40b76d62a05 100644 --- a/src/librustc_metadata/creader.rs +++ b/src/librustc_metadata/creader.rs @@ -452,6 +452,14 @@ impl<'a> CrateLoader<'a> { if dep.is_none() { self.used_extern_options.insert(name); } + if !name.as_str().is_ascii() { + self.sess + .struct_span_err( + span, + &format!("cannot load a crate with a non-ascii name `{}`", name,), + ) + .emit(); + } self.maybe_resolve_crate(name, span, dep_kind, dep).unwrap_or_else(|err| err.report()) } diff --git a/src/test/ui/rfc-2457/crate_name_nonascii_forbidden-1.rs b/src/test/ui/rfc-2457/crate_name_nonascii_forbidden-1.rs new file mode 100644 index 000000000000..3fb1cf9f557b --- /dev/null +++ b/src/test/ui/rfc-2457/crate_name_nonascii_forbidden-1.rs @@ -0,0 +1,6 @@ +#![feature(non_ascii_idents)] + +extern crate ьаг; //~ ERROR cannot load a crate with a non-ascii name `ьаг` +//~| ERROR can't find crate for `ьаг` + +fn main() {} diff --git a/src/test/ui/rfc-2457/crate_name_nonascii_forbidden-1.stderr b/src/test/ui/rfc-2457/crate_name_nonascii_forbidden-1.stderr new file mode 100644 index 000000000000..1e424237fd23 --- /dev/null +++ b/src/test/ui/rfc-2457/crate_name_nonascii_forbidden-1.stderr @@ -0,0 +1,15 @@ +error: cannot load a crate with a non-ascii name `ьаг` + --> $DIR/crate_name_nonascii_forbidden-1.rs:3:1 + | +LL | extern crate ьаг; + | ^^^^^^^^^^^^^^^^^ + +error[E0463]: can't find crate for `ьаг` + --> $DIR/crate_name_nonascii_forbidden-1.rs:3:1 + | +LL | extern crate ьаг; + | ^^^^^^^^^^^^^^^^^ can't find crate + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0463`. diff --git a/src/test/ui/rfc-2457/crate_name_nonascii_forbidden-2.rs b/src/test/ui/rfc-2457/crate_name_nonascii_forbidden-2.rs new file mode 100644 index 000000000000..e1acdbff0618 --- /dev/null +++ b/src/test/ui/rfc-2457/crate_name_nonascii_forbidden-2.rs @@ -0,0 +1,9 @@ +// compile-flags:--extern му_сгате +// edition:2018 +#![feature(non_ascii_idents)] + +use му_сгате::baz; //~ ERROR cannot load a crate with a non-ascii name `му_сгате` + //~| can't find crate for `му_сгате` + + +fn main() {} diff --git a/src/test/ui/rfc-2457/crate_name_nonascii_forbidden-2.stderr b/src/test/ui/rfc-2457/crate_name_nonascii_forbidden-2.stderr new file mode 100644 index 000000000000..c06405ebb37e --- /dev/null +++ b/src/test/ui/rfc-2457/crate_name_nonascii_forbidden-2.stderr @@ -0,0 +1,15 @@ +error: cannot load a crate with a non-ascii name `му_сгате` + --> $DIR/crate_name_nonascii_forbidden-2.rs:5:5 + | +LL | use му_сгате::baz; + | ^^^^^^^^ + +error[E0463]: can't find crate for `му_сгате` + --> $DIR/crate_name_nonascii_forbidden-2.rs:5:5 + | +LL | use му_сгате::baz; + | ^^^^^^^^ can't find crate + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0463`. From cfdbbb5600c0a9f95dd662ce98f463d921e30cf0 Mon Sep 17 00:00:00 2001 From: Who? Me?! Date: Tue, 16 Jun 2020 09:41:05 -0500 Subject: [PATCH 091/123] format derives Co-authored-by: lzutao --- src/librustc_middle/ty/sty.rs | 15 ++------------- 1 file changed, 2 insertions(+), 13 deletions(-) diff --git a/src/librustc_middle/ty/sty.rs b/src/librustc_middle/ty/sty.rs index 656c05b5520e..cf11adb0285a 100644 --- a/src/librustc_middle/ty/sty.rs +++ b/src/librustc_middle/ty/sty.rs @@ -208,19 +208,8 @@ pub enum TyKind<'tcx> { /// A type that is not publicly constructable. This prevents people from making `TyKind::Error` /// except through `tcx.err*()`. -#[derive( - Copy, - Clone, - Debug, - Eq, - Hash, - PartialEq, - PartialOrd, - Ord, - RustcEncodable, - RustcDecodable, - HashStable -)] +#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq, PartialOrd, Ord)] +#[derive(RustcEncodable, RustcDecodable, HashStable)] pub struct DelaySpanBugEmitted(pub(super) ()); // `TyKind` is used a lot. Make sure it doesn't unintentionally get bigger. From 4506a358ca4be0b3f7a1f320a677168d964ca9f7 Mon Sep 17 00:00:00 2001 From: root Date: Tue, 16 Jun 2020 17:36:04 +0000 Subject: [PATCH 092/123] add header for rust specific feature --- src/rustllvm/PassWrapper.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/rustllvm/PassWrapper.cpp b/src/rustllvm/PassWrapper.cpp index 7586dd91ab68..47c03069182a 100644 --- a/src/rustllvm/PassWrapper.cpp +++ b/src/rustllvm/PassWrapper.cpp @@ -424,8 +424,7 @@ extern "C" void LLVMRustPrintTargetFeatures(LLVMTargetMachineRef TM) { printf("Available features for this target:\n"); for (auto &Feature : FeatTable) printf(" %-*s - %s.\n", MaxFeatLen, Feature.Key, Feature.Desc); - printf("\n"); - // Rust specific target features + printf("Rust-specific features:\n"); printf(" %-*s - %s.\n", MaxFeatLen, "crt-static", "Enables libraries with C Run-time Libraries(CRT) to be statically linked"); printf("\n"); From 9f50f84ef106c7f521d1322ec39562610339f74d Mon Sep 17 00:00:00 2001 From: root Date: Tue, 16 Jun 2020 18:14:32 +0000 Subject: [PATCH 093/123] break long line for formatting --- src/rustllvm/PassWrapper.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/rustllvm/PassWrapper.cpp b/src/rustllvm/PassWrapper.cpp index 47c03069182a..16c2ac332278 100644 --- a/src/rustllvm/PassWrapper.cpp +++ b/src/rustllvm/PassWrapper.cpp @@ -425,7 +425,11 @@ extern "C" void LLVMRustPrintTargetFeatures(LLVMTargetMachineRef TM) { for (auto &Feature : FeatTable) printf(" %-*s - %s.\n", MaxFeatLen, Feature.Key, Feature.Desc); printf("Rust-specific features:\n"); - printf(" %-*s - %s.\n", MaxFeatLen, "crt-static", "Enables libraries with C Run-time Libraries(CRT) to be statically linked"); + printf(" %-*s - %s.\n", + MaxFeatLen, + "crt-static", + "Enables libraries with C Run-time Libraries(CRT) to be statically linked" + ); printf("\n"); printf("Use +feature to enable a feature, or -feature to disable it.\n" From 457acbd5a80153fc06bca12663405c175fd9453f Mon Sep 17 00:00:00 2001 From: root Date: Tue, 16 Jun 2020 18:53:30 +0000 Subject: [PATCH 094/123] trim whitespace --- src/rustllvm/PassWrapper.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/rustllvm/PassWrapper.cpp b/src/rustllvm/PassWrapper.cpp index 16c2ac332278..28caf61a0197 100644 --- a/src/rustllvm/PassWrapper.cpp +++ b/src/rustllvm/PassWrapper.cpp @@ -425,9 +425,9 @@ extern "C" void LLVMRustPrintTargetFeatures(LLVMTargetMachineRef TM) { for (auto &Feature : FeatTable) printf(" %-*s - %s.\n", MaxFeatLen, Feature.Key, Feature.Desc); printf("Rust-specific features:\n"); - printf(" %-*s - %s.\n", - MaxFeatLen, - "crt-static", + printf(" %-*s - %s.\n", + MaxFeatLen, + "crt-static", "Enables libraries with C Run-time Libraries(CRT) to be statically linked" ); printf("\n"); From f0a42332b8ed123a92e9f8e2c329cbd2ad1753e9 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sat, 6 Jun 2020 11:56:58 +0200 Subject: [PATCH 095/123] memory access sanity checks: abort instead of panic --- src/libcore/intrinsics.rs | 17 ++++++++++++----- src/libcore/ptr/mod.rs | 28 +++++++++++++++++++++------- 2 files changed, 33 insertions(+), 12 deletions(-) diff --git a/src/libcore/intrinsics.rs b/src/libcore/intrinsics.rs index 85076a573b52..59809239678a 100644 --- a/src/libcore/intrinsics.rs +++ b/src/libcore/intrinsics.rs @@ -2057,9 +2057,14 @@ pub unsafe fn copy_nonoverlapping(src: *const T, dst: *mut T, count: usize) { fn copy_nonoverlapping(src: *const T, dst: *mut T, count: usize); } - debug_assert!(is_aligned_and_not_null(src), "attempt to copy from unaligned or null pointer"); - debug_assert!(is_aligned_and_not_null(dst), "attempt to copy to unaligned or null pointer"); - debug_assert!(is_nonoverlapping(src, dst, count), "attempt to copy to overlapping memory"); + if cfg!(debug_assertions) + && !(is_aligned_and_not_null(src) + && is_aligned_and_not_null(dst) + && is_nonoverlapping(src, dst, count)) + { + // Not panicking to keep codegen impact smaller. + abort(); + } copy_nonoverlapping(src, dst, count) } @@ -2122,8 +2127,10 @@ pub unsafe fn copy(src: *const T, dst: *mut T, count: usize) { fn copy(src: *const T, dst: *mut T, count: usize); } - debug_assert!(is_aligned_and_not_null(src), "attempt to copy from unaligned or null pointer"); - debug_assert!(is_aligned_and_not_null(dst), "attempt to copy to unaligned or null pointer"); + if cfg!(debug_assertions) && !(is_aligned_and_not_null(src) && is_aligned_and_not_null(dst)) { + // Not panicking to keep codegen impact smaller. + abort(); + } copy(src, dst, count) } diff --git a/src/libcore/ptr/mod.rs b/src/libcore/ptr/mod.rs index 1be05d5effff..fec638c947ce 100644 --- a/src/libcore/ptr/mod.rs +++ b/src/libcore/ptr/mod.rs @@ -70,7 +70,7 @@ use crate::cmp::Ordering; use crate::fmt; use crate::hash; -use crate::intrinsics::{self, is_aligned_and_not_null, is_nonoverlapping}; +use crate::intrinsics::{self, abort, is_aligned_and_not_null, is_nonoverlapping}; use crate::mem::{self, MaybeUninit}; #[stable(feature = "rust1", since = "1.0.0")] @@ -420,9 +420,14 @@ pub unsafe fn swap(x: *mut T, y: *mut T) { #[inline] #[stable(feature = "swap_nonoverlapping", since = "1.27.0")] pub unsafe fn swap_nonoverlapping(x: *mut T, y: *mut T, count: usize) { - debug_assert!(is_aligned_and_not_null(x), "attempt to swap unaligned or null pointer"); - debug_assert!(is_aligned_and_not_null(y), "attempt to swap unaligned or null pointer"); - debug_assert!(is_nonoverlapping(x, y, count), "attempt to swap overlapping memory"); + if cfg!(debug_assertions) + && !(is_aligned_and_not_null(x) + && is_aligned_and_not_null(y) + && is_nonoverlapping(x, y, count)) + { + // Not panicking to keep codegen impact smaller. + abort(); + } let x = x as *mut u8; let y = y as *mut u8; @@ -838,7 +843,10 @@ pub unsafe fn read_unaligned(src: *const T) -> T { #[inline] #[stable(feature = "rust1", since = "1.0.0")] pub unsafe fn write(dst: *mut T, src: T) { - debug_assert!(is_aligned_and_not_null(dst), "attempt to write to unaligned or null pointer"); + if cfg!(debug_assertions) && !is_aligned_and_not_null(dst) { + // Not panicking to keep codegen impact smaller. + abort(); + } intrinsics::move_val_init(&mut *dst, src) } @@ -1003,7 +1011,10 @@ pub unsafe fn write_unaligned(dst: *mut T, src: T) { #[inline] #[stable(feature = "volatile", since = "1.9.0")] pub unsafe fn read_volatile(src: *const T) -> T { - debug_assert!(is_aligned_and_not_null(src), "attempt to read from unaligned or null pointer"); + if cfg!(debug_assertions) && !is_aligned_and_not_null(src) { + // Not panicking to keep codegen impact smaller. + abort(); + } intrinsics::volatile_load(src) } @@ -1072,7 +1083,10 @@ pub unsafe fn read_volatile(src: *const T) -> T { #[inline] #[stable(feature = "volatile", since = "1.9.0")] pub unsafe fn write_volatile(dst: *mut T, src: T) { - debug_assert!(is_aligned_and_not_null(dst), "attempt to write to unaligned or null pointer"); + if cfg!(debug_assertions) && !is_aligned_and_not_null(dst) { + // Not panicking to keep codegen impact smaller. + abort(); + } intrinsics::volatile_store(dst, src); } From 81c7ebd54418fe2f91be10b7371c7a3f5cca3771 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sat, 6 Jun 2020 12:19:29 +0200 Subject: [PATCH 096/123] we can enable one more codegen test in debug mode now --- src/test/codegen/vec-clear.rs | 1 - src/test/codegen/vec-optimizes-away.rs | 1 - 2 files changed, 2 deletions(-) diff --git a/src/test/codegen/vec-clear.rs b/src/test/codegen/vec-clear.rs index b9ffce8b0cb3..15bfe421e9d3 100644 --- a/src/test/codegen/vec-clear.rs +++ b/src/test/codegen/vec-clear.rs @@ -1,4 +1,3 @@ -// ignore-debug: the debug assertions get in the way // compile-flags: -O #![crate_type = "lib"] diff --git a/src/test/codegen/vec-optimizes-away.rs b/src/test/codegen/vec-optimizes-away.rs index ebede0908c6c..9143fad23408 100644 --- a/src/test/codegen/vec-optimizes-away.rs +++ b/src/test/codegen/vec-optimizes-away.rs @@ -1,4 +1,3 @@ -// // ignore-debug: the debug assertions get in the way // no-system-llvm // compile-flags: -O From 15cd51af5e193789e82024ffbe194f4c0dbdfbc3 Mon Sep 17 00:00:00 2001 From: Alexis Bourget Date: Tue, 16 Jun 2020 23:33:23 +0200 Subject: [PATCH 097/123] Mention functions pointers in the documentation --- src/libcore/mem/mod.rs | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/libcore/mem/mod.rs b/src/libcore/mem/mod.rs index 8bce980cadd1..226454561f6f 100644 --- a/src/libcore/mem/mod.rs +++ b/src/libcore/mem/mod.rs @@ -581,11 +581,12 @@ pub const fn needs_drop() -> bool { /// This means that, for example, the padding byte in `(u8, u16)` is not /// necessarily zeroed. /// -/// There is no guarantee that an all-zero byte-pattern represents a valid value of -/// some type `T`. For example, the all-zero byte-pattern is not a valid value -/// for reference types (`&T` and `&mut T`). Using `zeroed` on such types -/// causes immediate [undefined behavior][ub] because [the Rust compiler assumes][inv] -/// that there always is a valid value in a variable it considers initialized. +/// There is no guarantee that an all-zero byte-pattern represents a valid value +/// of some type `T`. For example, the all-zero byte-pattern is not a valid value +/// for reference types (`&T`, `&mut T` and functions pointers). Using `zeroed` on +/// such types on such types causes immediate [undefined behavior][ub] because +/// [the Rust compiler assumes][inv] that there always is a valid value in a +/// variable it considers initialized. /// /// This has the same effect as [`MaybeUninit::zeroed().assume_init()`][zeroed]. /// It is useful for FFI sometimes, but should generally be avoided. @@ -612,6 +613,7 @@ pub const fn needs_drop() -> bool { /// use std::mem; /// /// let _x: &i32 = unsafe { mem::zeroed() }; // Undefined behavior! +/// let _y: fn() = unsafe { mem::zeroed() }; // And again ! /// ``` #[inline(always)] #[stable(feature = "rust1", since = "1.0.0")] From 2b7d8588668bc79a1855a2c335572a1ac8ceaf34 Mon Sep 17 00:00:00 2001 From: Gary Guo Date: Tue, 16 Jun 2020 22:48:35 +0100 Subject: [PATCH 098/123] Add some comments related to place op typeck --- src/librustc_typeck/check/mod.rs | 7 +++---- src/librustc_typeck/check/place_op.rs | 6 ++++-- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index b1d32213b729..fa7dd2156edf 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -3219,10 +3219,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } - // When there is an auto mutable borrow, it is equivalent to `&mut expr`, - // thus `expr` is ought to be typechecked with needs = [`Needs::MutPlace`]. - // However in many cases it might not be checked this way originally, e.g. - // the receiver of a method call. We need to fix them up. + // If there is an mutable auto-borrow, it is equivalent to `&mut `. + // In this case implicit use of `Deref` and `Index` within `` should + // instead be `DerefMut` and `IndexMut`, so fix those up. if autoborrow_mut { self.convert_place_derefs_to_mutable(expr); } diff --git a/src/librustc_typeck/check/place_op.rs b/src/librustc_typeck/check/place_op.rs index ce4b6f8baf91..d1c22cd1ac03 100644 --- a/src/librustc_typeck/check/place_op.rs +++ b/src/librustc_typeck/check/place_op.rs @@ -11,10 +11,11 @@ use rustc_span::symbol::{sym, Ident}; use rustc_span::Span; impl<'a, 'tcx> FnCtxt<'a, 'tcx> { + /// Type-check `*oprnd_expr` with `oprnd_expr` type-checked already. pub(super) fn lookup_derefing( &self, expr: &hir::Expr<'_>, - oprnd: &'tcx hir::Expr<'tcx>, + oprnd_expr: &'tcx hir::Expr<'tcx>, oprnd_ty: Ty<'tcx>, ) -> Option> { if let Some(mt) = oprnd_ty.builtin_deref(true) { @@ -25,7 +26,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let method = self.register_infer_ok_obligations(ok); if let ty::Ref(region, _, hir::Mutability::Not) = method.sig.inputs()[0].kind { self.apply_adjustments( - oprnd, + oprnd_expr, vec![Adjustment { kind: Adjust::Borrow(AutoBorrow::Ref(region, AutoBorrowMutability::Not)), target: method.sig.inputs()[0], @@ -39,6 +40,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { Some(ty) } + /// Type-check `*base_expr[index_expr]` with `base_expr` and `index_expr` type-checked already. pub(super) fn lookup_indexing( &self, expr: &hir::Expr<'_>, From af45d8a5bb4909e6908c4d7bba80eedcdcd7f961 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Sat, 13 Jun 2020 12:08:32 -0700 Subject: [PATCH 099/123] Suggest new type param on single char ident Suggest new type parameter on single char uppercase ident even if it doesn't appear in a field's type parameter. Address comment in #72641. --- src/librustc_resolve/late/diagnostics.rs | 41 +++++++++++++++---- .../associated-types-eq-1.stderr | 11 ++++- .../type-not-found-in-adt-field.rs | 8 +++- .../type-not-found-in-adt-field.stderr | 16 ++++++-- 4 files changed, 61 insertions(+), 15 deletions(-) diff --git a/src/librustc_resolve/late/diagnostics.rs b/src/librustc_resolve/late/diagnostics.rs index 28ff89f66925..694e2ac2a36a 100644 --- a/src/librustc_resolve/late/diagnostics.rs +++ b/src/librustc_resolve/late/diagnostics.rs @@ -919,20 +919,45 @@ impl<'a> LateResolutionVisitor<'a, '_, '_> { &self, path: &[Segment], ) -> Option<(Span, &'static str, String, Applicability)> { - let ident = match path { - [segment] if !segment.has_args => segment.ident, + let (ident, span) = match path { + [segment] if !segment.has_args => (segment.ident.to_string(), segment.ident.span), _ => return None, }; - match ( - self.diagnostic_metadata.current_item, - self.diagnostic_metadata.currently_processing_generics, - ) { - (Some(Item { kind: ItemKind::Fn(..), ident, .. }), true) if ident.name == sym::main => { + let mut iter = ident.chars().map(|c| c.is_uppercase()); + let single_uppercase_char = + matches!(iter.next(), Some(true)) && matches!(iter.next(), None); + if !self.diagnostic_metadata.currently_processing_generics && !single_uppercase_char { + return None; + } + match (self.diagnostic_metadata.current_item, single_uppercase_char) { + (Some(Item { kind: ItemKind::Fn(..), ident, .. }), _) if ident.name == sym::main => { // Ignore `fn main()` as we don't want to suggest `fn main()` } - (Some(Item { kind, .. }), true) => { + ( + Some(Item { + kind: + kind @ ItemKind::Fn(..) + | kind @ ItemKind::Enum(..) + | kind @ ItemKind::Struct(..) + | kind @ ItemKind::Union(..), + .. + }), + true, + ) + | (Some(Item { kind, .. }), false) => { // Likely missing type parameter. if let Some(generics) = kind.generics() { + if span.overlaps(generics.span) { + // Avoid the following: + // error[E0405]: cannot find trait `A` in this scope + // --> $DIR/typo-suggestion-named-underscore.rs:CC:LL + // | + // L | fn foo(x: T) {} // Shouldn't suggest underscore + // | ^- help: you might be missing a type parameter: `, A` + // | | + // | not found in this scope + return None; + } let msg = "you might be missing a type parameter"; let (span, sugg) = if let [.., param] = &generics.params[..] { let span = if let [.., bound] = ¶m.bounds[..] { diff --git a/src/test/ui/associated-types/associated-types-eq-1.stderr b/src/test/ui/associated-types/associated-types-eq-1.stderr index 66c5f34644c0..53a45cf4e4f4 100644 --- a/src/test/ui/associated-types/associated-types-eq-1.stderr +++ b/src/test/ui/associated-types/associated-types-eq-1.stderr @@ -4,7 +4,16 @@ error[E0412]: cannot find type `A` in this scope LL | fn foo2(x: I) { | - similarly named type parameter `I` defined here LL | let _: A = x.boo(); - | ^ help: a type parameter with a similar name exists: `I` + | ^ + | +help: a type parameter with a similar name exists + | +LL | let _: I = x.boo(); + | ^ +help: you might be missing a type parameter + | +LL | fn foo2(x: I) { + | ^^^ error: aborting due to previous error diff --git a/src/test/ui/suggestions/type-not-found-in-adt-field.rs b/src/test/ui/suggestions/type-not-found-in-adt-field.rs index 6bd42472f5a5..4cbfe58d3570 100644 --- a/src/test/ui/suggestions/type-not-found-in-adt-field.rs +++ b/src/test/ui/suggestions/type-not-found-in-adt-field.rs @@ -1,5 +1,9 @@ -struct S { - m: Vec>, //~ ERROR cannot find type `Hashmap` in this scope +struct Struct { + m: Vec>, //~ ERROR cannot find type `Someunknownname` in this scope + //~^ NOTE not found in this scope +} +struct OtherStruct { //~ HELP you might be missing a type parameter + m: K, //~ ERROR cannot find type `K` in this scope //~^ NOTE not found in this scope } fn main() {} diff --git a/src/test/ui/suggestions/type-not-found-in-adt-field.stderr b/src/test/ui/suggestions/type-not-found-in-adt-field.stderr index cfad8c689d03..e990fb5ba121 100644 --- a/src/test/ui/suggestions/type-not-found-in-adt-field.stderr +++ b/src/test/ui/suggestions/type-not-found-in-adt-field.stderr @@ -1,9 +1,17 @@ -error[E0412]: cannot find type `Hashmap` in this scope +error[E0412]: cannot find type `Someunknownname` in this scope --> $DIR/type-not-found-in-adt-field.rs:2:12 | -LL | m: Vec>, - | ^^^^^^^ not found in this scope +LL | m: Vec>, + | ^^^^^^^^^^^^^^^ not found in this scope -error: aborting due to previous error +error[E0412]: cannot find type `K` in this scope + --> $DIR/type-not-found-in-adt-field.rs:6:8 + | +LL | struct OtherStruct { + | - help: you might be missing a type parameter: `` +LL | m: K, + | ^ not found in this scope + +error: aborting due to 2 previous errors For more information about this error, try `rustc --explain E0412`. From 9f2e8adc3597b21389dda31b687540e1a688fe87 Mon Sep 17 00:00:00 2001 From: pierwill <19642016+pierwill@users.noreply.github.com> Date: Tue, 16 Jun 2020 18:11:47 -0700 Subject: [PATCH 100/123] Fix typo in librustc_ast docs Fixed sentence by removing a word. --- src/librustc_ast/ast.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/librustc_ast/ast.rs b/src/librustc_ast/ast.rs index 62406552e318..ce186c4834d7 100644 --- a/src/librustc_ast/ast.rs +++ b/src/librustc_ast/ast.rs @@ -5,7 +5,7 @@ //! additional metadata), and [`ItemKind`] (which represents a concrete type and contains //! information specific to the type of the item). //! -//! Other module items that worth mentioning: +//! Other module items worth mentioning: //! - [`Ty`] and [`TyKind`]: A parsed Rust type. //! - [`Expr`] and [`ExprKind`]: A parsed Rust expression. //! - [`Pat`] and [`PatKind`]: A parsed Rust pattern. Patterns are often dual to expressions. From e75fa896ba53eb5fc5c3dd2741101f377488c2db Mon Sep 17 00:00:00 2001 From: Poliorcetics Date: Wed, 17 Jun 2020 03:30:41 +0200 Subject: [PATCH 101/123] Don't imply function pointers are references Co-authored-by: David Tolnay --- src/libcore/mem/mod.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libcore/mem/mod.rs b/src/libcore/mem/mod.rs index 226454561f6f..70083ffa4be3 100644 --- a/src/libcore/mem/mod.rs +++ b/src/libcore/mem/mod.rs @@ -583,7 +583,7 @@ pub const fn needs_drop() -> bool { /// /// There is no guarantee that an all-zero byte-pattern represents a valid value /// of some type `T`. For example, the all-zero byte-pattern is not a valid value -/// for reference types (`&T`, `&mut T` and functions pointers). Using `zeroed` on +/// for reference types (`&T`, `&mut T`) and functions pointers. Using `zeroed` on /// such types on such types causes immediate [undefined behavior][ub] because /// [the Rust compiler assumes][inv] that there always is a valid value in a /// variable it considers initialized. @@ -613,7 +613,7 @@ pub const fn needs_drop() -> bool { /// use std::mem; /// /// let _x: &i32 = unsafe { mem::zeroed() }; // Undefined behavior! -/// let _y: fn() = unsafe { mem::zeroed() }; // And again ! +/// let _y: fn() = unsafe { mem::zeroed() }; // And again! /// ``` #[inline(always)] #[stable(feature = "rust1", since = "1.0.0")] From 1db44afecd892351ae91499b1baefee433bbc04b Mon Sep 17 00:00:00 2001 From: Rich Kadel Date: Tue, 16 Jun 2020 18:48:46 -0700 Subject: [PATCH 102/123] Ensure profiling runtime for -Zinstrument-coverage If config.toml `profiler = false`, the test/mir-opt/instrument_coverage test is ignored. Otherwise, this patch ensures the profiler_runtime is loaded when -Zinstrument-coverage is enabled. Confirmed that this works for MacOS. --- config.toml.example | 3 +- src/librustc_metadata/creader.rs | 4 +- src/test/mir-opt/instrument_coverage.rs | 1 + .../rustc.bar.InstrumentCoverage.diff | 22 ++++---- .../rustc.main.InstrumentCoverage.diff | 54 +++++++++---------- 5 files changed, 44 insertions(+), 40 deletions(-) diff --git a/config.toml.example b/config.toml.example index d995554913f8..bc6760334170 100644 --- a/config.toml.example +++ b/config.toml.example @@ -209,7 +209,8 @@ # Build the sanitizer runtimes #sanitizers = false -# Build the profiler runtime +# Build the profiler runtime (required when compiling with options that depend +# on this runtime, such as `-C profile-generate` or `-Z instrument-coverage`). #profiler = false # Indicates whether the native libraries linked into Cargo will be statically diff --git a/src/librustc_metadata/creader.rs b/src/librustc_metadata/creader.rs index b8ebcd6c8a8f..f3e4f40bd5a1 100644 --- a/src/librustc_metadata/creader.rs +++ b/src/librustc_metadata/creader.rs @@ -698,7 +698,9 @@ impl<'a> CrateLoader<'a> { } fn inject_profiler_runtime(&mut self) { - if (self.sess.opts.debugging_opts.profile || self.sess.opts.cg.profile_generate.enabled()) + if (self.sess.opts.debugging_opts.instrument_coverage + || self.sess.opts.debugging_opts.profile + || self.sess.opts.cg.profile_generate.enabled()) && !self.sess.opts.debugging_opts.no_profiler_runtime { info!("loading profiler"); diff --git a/src/test/mir-opt/instrument_coverage.rs b/src/test/mir-opt/instrument_coverage.rs index e8c723b528a1..3fe010ef68fc 100644 --- a/src/test/mir-opt/instrument_coverage.rs +++ b/src/test/mir-opt/instrument_coverage.rs @@ -2,6 +2,7 @@ // at the top of each function. The placeholders are later converted into LLVM instrprof.increment // intrinsics, during codegen. +// needs-profiler-support // compile-flags: -Zinstrument-coverage // EMIT_MIR rustc.main.InstrumentCoverage.diff // EMIT_MIR rustc.bar.InstrumentCoverage.diff diff --git a/src/test/mir-opt/instrument_coverage/rustc.bar.InstrumentCoverage.diff b/src/test/mir-opt/instrument_coverage/rustc.bar.InstrumentCoverage.diff index d23bb93d951d..1e64379aa0e4 100644 --- a/src/test/mir-opt/instrument_coverage/rustc.bar.InstrumentCoverage.diff +++ b/src/test/mir-opt/instrument_coverage/rustc.bar.InstrumentCoverage.diff @@ -2,40 +2,40 @@ + // MIR for `bar` after InstrumentCoverage fn bar() -> bool { - let mut _0: bool; // return place in scope 0 at $DIR/instrument_coverage.rs:17:13: 17:17 -+ let mut _1: (); // in scope 0 at $DIR/instrument_coverage.rs:17:1: 19:2 + let mut _0: bool; // return place in scope 0 at $DIR/instrument_coverage.rs:18:13: 18:17 ++ let mut _1: (); // in scope 0 at $DIR/instrument_coverage.rs:18:1: 20:2 bb0: { -+ StorageLive(_1); // scope 0 at $DIR/instrument_coverage.rs:17:1: 19:2 -+ _1 = const std::intrinsics::count_code_region(const 0u32) -> bb2; // scope 0 at $DIR/instrument_coverage.rs:17:1: 19:2 ++ StorageLive(_1); // scope 0 at $DIR/instrument_coverage.rs:18:1: 20:2 ++ _1 = const std::intrinsics::count_code_region(const 0u32) -> bb2; // scope 0 at $DIR/instrument_coverage.rs:18:1: 20:2 + // ty::Const + // + ty: unsafe extern "rust-intrinsic" fn(u32) {std::intrinsics::count_code_region} + // + val: Value(Scalar()) + // mir::Constant -+ // + span: $DIR/instrument_coverage.rs:17:1: 17:1 ++ // + span: $DIR/instrument_coverage.rs:18:1: 18:1 + // + literal: Const { ty: unsafe extern "rust-intrinsic" fn(u32) {std::intrinsics::count_code_region}, val: Value(Scalar()) } + // ty::Const + // + ty: u32 + // + val: Value(Scalar(0x00000000)) + // mir::Constant -+ // + span: $DIR/instrument_coverage.rs:17:1: 17:1 ++ // + span: $DIR/instrument_coverage.rs:18:1: 18:1 + // + literal: Const { ty: u32, val: Value(Scalar(0x00000000)) } + } + + bb1 (cleanup): { -+ resume; // scope 0 at $DIR/instrument_coverage.rs:17:1: 19:2 ++ resume; // scope 0 at $DIR/instrument_coverage.rs:18:1: 20:2 + } + + bb2: { -+ StorageDead(_1); // scope 0 at $DIR/instrument_coverage.rs:18:5: 18:9 - _0 = const true; // scope 0 at $DIR/instrument_coverage.rs:18:5: 18:9 ++ StorageDead(_1); // scope 0 at $DIR/instrument_coverage.rs:19:5: 19:9 + _0 = const true; // scope 0 at $DIR/instrument_coverage.rs:19:5: 19:9 // ty::Const // + ty: bool // + val: Value(Scalar(0x01)) // mir::Constant - // + span: $DIR/instrument_coverage.rs:18:5: 18:9 + // + span: $DIR/instrument_coverage.rs:19:5: 19:9 // + literal: Const { ty: bool, val: Value(Scalar(0x01)) } - return; // scope 0 at $DIR/instrument_coverage.rs:19:2: 19:2 + return; // scope 0 at $DIR/instrument_coverage.rs:20:2: 20:2 } } diff --git a/src/test/mir-opt/instrument_coverage/rustc.main.InstrumentCoverage.diff b/src/test/mir-opt/instrument_coverage/rustc.main.InstrumentCoverage.diff index 095246580409..82d21467827e 100644 --- a/src/test/mir-opt/instrument_coverage/rustc.main.InstrumentCoverage.diff +++ b/src/test/mir-opt/instrument_coverage/rustc.main.InstrumentCoverage.diff @@ -2,81 +2,81 @@ + // MIR for `main` after InstrumentCoverage fn main() -> () { - let mut _0: (); // return place in scope 0 at $DIR/instrument_coverage.rs:8:11: 8:11 - let mut _1: (); // in scope 0 at $DIR/instrument_coverage.rs:8:1: 14:2 - let mut _2: bool; // in scope 0 at $DIR/instrument_coverage.rs:10:12: 10:17 - let mut _3: !; // in scope 0 at $DIR/instrument_coverage.rs:10:18: 12:10 -+ let mut _4: (); // in scope 0 at $DIR/instrument_coverage.rs:8:1: 14:2 + let mut _0: (); // return place in scope 0 at $DIR/instrument_coverage.rs:9:11: 9:11 + let mut _1: (); // in scope 0 at $DIR/instrument_coverage.rs:9:1: 15:2 + let mut _2: bool; // in scope 0 at $DIR/instrument_coverage.rs:11:12: 11:17 + let mut _3: !; // in scope 0 at $DIR/instrument_coverage.rs:11:18: 13:10 ++ let mut _4: (); // in scope 0 at $DIR/instrument_coverage.rs:9:1: 15:2 bb0: { -- falseUnwind -> [real: bb1, cleanup: bb2]; // scope 0 at $DIR/instrument_coverage.rs:9:5: 13:6 -+ StorageLive(_4); // scope 0 at $DIR/instrument_coverage.rs:8:1: 14:2 -+ _4 = const std::intrinsics::count_code_region(const 0u32) -> bb7; // scope 0 at $DIR/instrument_coverage.rs:8:1: 14:2 +- falseUnwind -> [real: bb1, cleanup: bb2]; // scope 0 at $DIR/instrument_coverage.rs:10:5: 14:6 ++ StorageLive(_4); // scope 0 at $DIR/instrument_coverage.rs:9:1: 15:2 ++ _4 = const std::intrinsics::count_code_region(const 0u32) -> bb7; // scope 0 at $DIR/instrument_coverage.rs:9:1: 15:2 + // ty::Const + // + ty: unsafe extern "rust-intrinsic" fn(u32) {std::intrinsics::count_code_region} + // + val: Value(Scalar()) + // mir::Constant -+ // + span: $DIR/instrument_coverage.rs:8:1: 8:1 ++ // + span: $DIR/instrument_coverage.rs:9:1: 9:1 + // + literal: Const { ty: unsafe extern "rust-intrinsic" fn(u32) {std::intrinsics::count_code_region}, val: Value(Scalar()) } + // ty::Const + // + ty: u32 + // + val: Value(Scalar(0x00000000)) + // mir::Constant -+ // + span: $DIR/instrument_coverage.rs:8:1: 8:1 ++ // + span: $DIR/instrument_coverage.rs:9:1: 9:1 + // + literal: Const { ty: u32, val: Value(Scalar(0x00000000)) } } bb1: { - StorageLive(_2); // scope 0 at $DIR/instrument_coverage.rs:10:12: 10:17 - _2 = const bar() -> [return: bb3, unwind: bb2]; // scope 0 at $DIR/instrument_coverage.rs:10:12: 10:17 + StorageLive(_2); // scope 0 at $DIR/instrument_coverage.rs:11:12: 11:17 + _2 = const bar() -> [return: bb3, unwind: bb2]; // scope 0 at $DIR/instrument_coverage.rs:11:12: 11:17 // ty::Const // + ty: fn() -> bool {bar} // + val: Value(Scalar()) // mir::Constant - // + span: $DIR/instrument_coverage.rs:10:12: 10:15 + // + span: $DIR/instrument_coverage.rs:11:12: 11:15 // + literal: Const { ty: fn() -> bool {bar}, val: Value(Scalar()) } } bb2 (cleanup): { - resume; // scope 0 at $DIR/instrument_coverage.rs:8:1: 14:2 + resume; // scope 0 at $DIR/instrument_coverage.rs:9:1: 15:2 } bb3: { - FakeRead(ForMatchedPlace, _2); // scope 0 at $DIR/instrument_coverage.rs:10:12: 10:17 - switchInt(_2) -> [false: bb5, otherwise: bb4]; // scope 0 at $DIR/instrument_coverage.rs:10:9: 12:10 + FakeRead(ForMatchedPlace, _2); // scope 0 at $DIR/instrument_coverage.rs:11:12: 11:17 + switchInt(_2) -> [false: bb5, otherwise: bb4]; // scope 0 at $DIR/instrument_coverage.rs:11:9: 13:10 } bb4: { - falseEdge -> [real: bb6, imaginary: bb5]; // scope 0 at $DIR/instrument_coverage.rs:10:9: 12:10 + falseEdge -> [real: bb6, imaginary: bb5]; // scope 0 at $DIR/instrument_coverage.rs:11:9: 13:10 } bb5: { - _1 = const (); // scope 0 at $DIR/instrument_coverage.rs:10:9: 12:10 + _1 = const (); // scope 0 at $DIR/instrument_coverage.rs:11:9: 13:10 // ty::Const // + ty: () // + val: Value(Scalar()) // mir::Constant - // + span: $DIR/instrument_coverage.rs:10:9: 12:10 + // + span: $DIR/instrument_coverage.rs:11:9: 13:10 // + literal: Const { ty: (), val: Value(Scalar()) } - StorageDead(_2); // scope 0 at $DIR/instrument_coverage.rs:13:5: 13:6 - goto -> bb0; // scope 0 at $DIR/instrument_coverage.rs:9:5: 13:6 + StorageDead(_2); // scope 0 at $DIR/instrument_coverage.rs:14:5: 14:6 + goto -> bb0; // scope 0 at $DIR/instrument_coverage.rs:10:5: 14:6 } bb6: { - _0 = const (); // scope 0 at $DIR/instrument_coverage.rs:11:13: 11:18 + _0 = const (); // scope 0 at $DIR/instrument_coverage.rs:12:13: 12:18 // ty::Const // + ty: () // + val: Value(Scalar()) // mir::Constant - // + span: $DIR/instrument_coverage.rs:11:13: 11:18 + // + span: $DIR/instrument_coverage.rs:12:13: 12:18 // + literal: Const { ty: (), val: Value(Scalar()) } - StorageDead(_2); // scope 0 at $DIR/instrument_coverage.rs:13:5: 13:6 - return; // scope 0 at $DIR/instrument_coverage.rs:14:2: 14:2 + StorageDead(_2); // scope 0 at $DIR/instrument_coverage.rs:14:5: 14:6 + return; // scope 0 at $DIR/instrument_coverage.rs:15:2: 15:2 + } + + bb7: { -+ StorageDead(_4); // scope 0 at $DIR/instrument_coverage.rs:9:5: 13:6 -+ falseUnwind -> [real: bb1, cleanup: bb2]; // scope 0 at $DIR/instrument_coverage.rs:9:5: 13:6 ++ StorageDead(_4); // scope 0 at $DIR/instrument_coverage.rs:10:5: 14:6 ++ falseUnwind -> [real: bb1, cleanup: bb2]; // scope 0 at $DIR/instrument_coverage.rs:10:5: 14:6 } } From caffb28ece818ce87f1c7573992cae4210544015 Mon Sep 17 00:00:00 2001 From: asrar Date: Wed, 17 Jun 2020 10:31:46 +0530 Subject: [PATCH 103/123] add blank line bw sections Separate target features from rust ones with a blank line Co-authored-by: Josh Stone --- src/rustllvm/PassWrapper.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rustllvm/PassWrapper.cpp b/src/rustllvm/PassWrapper.cpp index 28caf61a0197..323bd26c698a 100644 --- a/src/rustllvm/PassWrapper.cpp +++ b/src/rustllvm/PassWrapper.cpp @@ -424,7 +424,7 @@ extern "C" void LLVMRustPrintTargetFeatures(LLVMTargetMachineRef TM) { printf("Available features for this target:\n"); for (auto &Feature : FeatTable) printf(" %-*s - %s.\n", MaxFeatLen, Feature.Key, Feature.Desc); - printf("Rust-specific features:\n"); + printf("\nRust-specific features:\n"); printf(" %-*s - %s.\n", MaxFeatLen, "crt-static", From c9dc73d757003c58430d759b6e567afa356470a7 Mon Sep 17 00:00:00 2001 From: Dylan MacKenzie Date: Wed, 17 Jun 2020 09:29:33 -0700 Subject: [PATCH 104/123] Make novel structural match violations a warning --- src/librustc_mir_build/hair/pattern/const_to_pat.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/librustc_mir_build/hair/pattern/const_to_pat.rs b/src/librustc_mir_build/hair/pattern/const_to_pat.rs index 087c2c064cfa..8c4230dfa5d6 100644 --- a/src/librustc_mir_build/hair/pattern/const_to_pat.rs +++ b/src/librustc_mir_build/hair/pattern/const_to_pat.rs @@ -107,8 +107,12 @@ impl<'a, 'tcx> ConstToPat<'a, 'tcx> { cv.ty, structural ); + // This can occur because const qualification treats all associated constants as + // opaque, whereas `search_for_structural_match_violation` tries to monomorphize them + // before it runs. See #73431 for an example. if structural.is_none() && mir_structural_match_violation { - bug!("MIR const-checker found novel structural match violation"); + warn!("MIR const-checker found novel structural match violation"); + return inlined_const_as_pat; } if let Some(non_sm_ty) = structural { From 38e921b2c134015b8eb5542f271a4f9e104ddaaf Mon Sep 17 00:00:00 2001 From: Dylan MacKenzie Date: Wed, 17 Jun 2020 09:29:50 -0700 Subject: [PATCH 105/123] Add regression test for #73431 --- .../ui/consts/const_in_pattern/issue-73431.rs | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 src/test/ui/consts/const_in_pattern/issue-73431.rs diff --git a/src/test/ui/consts/const_in_pattern/issue-73431.rs b/src/test/ui/consts/const_in_pattern/issue-73431.rs new file mode 100644 index 000000000000..fa18a3af1b09 --- /dev/null +++ b/src/test/ui/consts/const_in_pattern/issue-73431.rs @@ -0,0 +1,29 @@ +// run-pass + +// Regression test for https://github.com/rust-lang/rust/issues/73431. + +pub trait Zero { + const ZERO: Self; +} + +impl Zero for usize { + const ZERO: Self = 0; +} + +impl Zero for Wrapper { + const ZERO: Self = Wrapper(T::ZERO); +} + +#[derive(Debug, PartialEq, Eq)] +pub struct Wrapper(T); + +fn is_zero(x: Wrapper) -> bool { + match x { + Zero::ZERO => true, + _ => false, + } +} + +fn main() { + let _ = is_zero(Wrapper(42)); +} From 3a1207f6883f799af13027ceb1715ff492382e87 Mon Sep 17 00:00:00 2001 From: Dylan MacKenzie Date: Wed, 17 Jun 2020 10:02:09 -0700 Subject: [PATCH 106/123] Add issue number to novel violation warning --- src/librustc_mir_build/hair/pattern/const_to_pat.rs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/librustc_mir_build/hair/pattern/const_to_pat.rs b/src/librustc_mir_build/hair/pattern/const_to_pat.rs index 8c4230dfa5d6..1aed8e844b60 100644 --- a/src/librustc_mir_build/hair/pattern/const_to_pat.rs +++ b/src/librustc_mir_build/hair/pattern/const_to_pat.rs @@ -109,9 +109,12 @@ impl<'a, 'tcx> ConstToPat<'a, 'tcx> { // This can occur because const qualification treats all associated constants as // opaque, whereas `search_for_structural_match_violation` tries to monomorphize them - // before it runs. See #73431 for an example. + // before it runs. + // + // FIXME(#73448): Find a way to bring const qualification into parity with + // `search_for_structural_match_violation`. if structural.is_none() && mir_structural_match_violation { - warn!("MIR const-checker found novel structural match violation"); + warn!("MIR const-checker found novel structural match violation. See #73448."); return inlined_const_as_pat; } From 6351850d8fc89c8150d6503ab6dc8524650e359d Mon Sep 17 00:00:00 2001 From: erikdesjardins Date: Wed, 17 Jun 2020 13:10:49 -0400 Subject: [PATCH 107/123] ignore-debug: debug assertions in slice indexing prevent the optimization --- src/test/codegen/issue-69101-bounds-check.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/test/codegen/issue-69101-bounds-check.rs b/src/test/codegen/issue-69101-bounds-check.rs index cf062b6ad758..8ade583b5712 100644 --- a/src/test/codegen/issue-69101-bounds-check.rs +++ b/src/test/codegen/issue-69101-bounds-check.rs @@ -1,5 +1,6 @@ // no-system-llvm // compile-flags: -O +// ignore-debug: the debug assertions get in the way #![crate_type = "lib"] // Make sure no bounds checks are emitted in the loop when upfront slicing From c3387293d4ff050cce2c7f3a79c8e7b040515f8c Mon Sep 17 00:00:00 2001 From: Rich Kadel Date: Wed, 17 Jun 2020 10:29:00 -0700 Subject: [PATCH 108/123] Update src/libcore/intrinsics.rs Co-authored-by: bjorn3 --- src/libcore/intrinsics.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libcore/intrinsics.rs b/src/libcore/intrinsics.rs index 3806d3ae2548..2d3e18146610 100644 --- a/src/libcore/intrinsics.rs +++ b/src/libcore/intrinsics.rs @@ -1947,7 +1947,7 @@ extern "rust-intrinsic" { /// generation. #[cfg(not(bootstrap))] #[lang = "count_code_region"] - pub fn count_code_region(_index: u32); + pub fn count_code_region(index: u32); } // Some functions are defined here because they accidentally got made From b9f0304af8b74bdf01cd39f1603d73bdc599de79 Mon Sep 17 00:00:00 2001 From: Rich Kadel Date: Wed, 17 Jun 2020 11:24:43 -0700 Subject: [PATCH 109/123] temporarily enable mac and windows tests on bors try testing platform-specific changes --- src/ci/azure-pipelines/try.yml | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/src/ci/azure-pipelines/try.yml b/src/ci/azure-pipelines/try.yml index 38a0685e0f75..b57b691641ca 100644 --- a/src/ci/azure-pipelines/try.yml +++ b/src/ci/azure-pipelines/try.yml @@ -82,3 +82,33 @@ jobs: # INITIAL_RUST_CONFIGURE_ARGS: --build=x86_64-pc-windows-msvc --enable-extended --enable-profiler # SCRIPT: python x.py dist # DEPLOY_ALT: 1 + +- job: macOS + timeoutInMinutes: 600 + pool: + vmImage: macos-10.15 + steps: + - template: steps/run.yml + strategy: + matrix: + x86_64-apple: + SCRIPT: ./x.py test + INITIAL_RUST_CONFIGURE_ARGS: --build=x86_64-apple-darwin --enable-sanitizers --enable-profiler --set rust.jemalloc + RUSTC_RETRY_LINKER_ON_SEGFAULT: 1 + MACOSX_DEPLOYMENT_TARGET: 10.8 + MACOSX_STD_DEPLOYMENT_TARGET: 10.7 + NO_LLVM_ASSERTIONS: 1 + NO_DEBUG_ASSERTIONS: 1 +- job: Windows + timeoutInMinutes: 600 + pool: + vmImage: 'vs2017-win2016' + steps: + - template: steps/run.yml + strategy: + matrix: + x86_64-msvc: + INITIAL_RUST_CONFIGURE_ARGS: > + --build=x86_64-pc-windows-msvc + --enable-profiler + SCRIPT: python x.py test From 36c9014ddd3e2ac6b6a0e9f623e791281c40473d Mon Sep 17 00:00:00 2001 From: Rich Kadel Date: Wed, 17 Jun 2020 14:50:28 -0700 Subject: [PATCH 110/123] removed try config to test mac & windows (passed) https://dev.azure.com/rust-lang/rust/_build/results?buildId=32224&view=results --- src/ci/azure-pipelines/try.yml | 30 ------------------------------ 1 file changed, 30 deletions(-) diff --git a/src/ci/azure-pipelines/try.yml b/src/ci/azure-pipelines/try.yml index b57b691641ca..38a0685e0f75 100644 --- a/src/ci/azure-pipelines/try.yml +++ b/src/ci/azure-pipelines/try.yml @@ -82,33 +82,3 @@ jobs: # INITIAL_RUST_CONFIGURE_ARGS: --build=x86_64-pc-windows-msvc --enable-extended --enable-profiler # SCRIPT: python x.py dist # DEPLOY_ALT: 1 - -- job: macOS - timeoutInMinutes: 600 - pool: - vmImage: macos-10.15 - steps: - - template: steps/run.yml - strategy: - matrix: - x86_64-apple: - SCRIPT: ./x.py test - INITIAL_RUST_CONFIGURE_ARGS: --build=x86_64-apple-darwin --enable-sanitizers --enable-profiler --set rust.jemalloc - RUSTC_RETRY_LINKER_ON_SEGFAULT: 1 - MACOSX_DEPLOYMENT_TARGET: 10.8 - MACOSX_STD_DEPLOYMENT_TARGET: 10.7 - NO_LLVM_ASSERTIONS: 1 - NO_DEBUG_ASSERTIONS: 1 -- job: Windows - timeoutInMinutes: 600 - pool: - vmImage: 'vs2017-win2016' - steps: - - template: steps/run.yml - strategy: - matrix: - x86_64-msvc: - INITIAL_RUST_CONFIGURE_ARGS: > - --build=x86_64-pc-windows-msvc - --enable-profiler - SCRIPT: python x.py test From 8d1a3801faa199acb2a86580247afd672bd838f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Wed, 17 Jun 2020 16:29:03 -0700 Subject: [PATCH 111/123] review comments --- src/librustc_resolve/build_reduced_graph.rs | 2 +- src/librustc_resolve/late/diagnostics.rs | 4 +++- src/librustc_resolve/lib.rs | 8 ++++---- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/src/librustc_resolve/build_reduced_graph.rs b/src/librustc_resolve/build_reduced_graph.rs index 8661af6d7a1d..ef2389fcefaf 100644 --- a/src/librustc_resolve/build_reduced_graph.rs +++ b/src/librustc_resolve/build_reduced_graph.rs @@ -485,7 +485,7 @@ impl<'a, 'b> BuildReducedGraphVisitor<'a, 'b> { module_path.push(Segment { ident: Ident { name: kw::PathRoot, span: source.ident.span }, id: Some(self.r.next_node_id()), - has_args: false, + has_generic_args: false, }); source.ident.name = crate_name; } diff --git a/src/librustc_resolve/late/diagnostics.rs b/src/librustc_resolve/late/diagnostics.rs index 694e2ac2a36a..a6e016e9e6a2 100644 --- a/src/librustc_resolve/late/diagnostics.rs +++ b/src/librustc_resolve/late/diagnostics.rs @@ -920,7 +920,9 @@ impl<'a> LateResolutionVisitor<'a, '_, '_> { path: &[Segment], ) -> Option<(Span, &'static str, String, Applicability)> { let (ident, span) = match path { - [segment] if !segment.has_args => (segment.ident.to_string(), segment.ident.span), + [segment] if !segment.has_generic_args => { + (segment.ident.to_string(), segment.ident.span) + } _ => return None, }; let mut iter = ident.chars().map(|c| c.is_uppercase()); diff --git a/src/librustc_resolve/lib.rs b/src/librustc_resolve/lib.rs index f7ec919fa04e..a30e5cc1ab60 100644 --- a/src/librustc_resolve/lib.rs +++ b/src/librustc_resolve/lib.rs @@ -233,7 +233,7 @@ pub struct Segment { id: Option, /// Signals whether this `PathSegment` has generic arguments. Used to avoid providing /// nonsensical suggestions. - has_args: bool, + has_generic_args: bool, } impl Segment { @@ -242,7 +242,7 @@ impl Segment { } fn from_ident(ident: Ident) -> Segment { - Segment { ident, id: None, has_args: false } + Segment { ident, id: None, has_generic_args: false } } fn names_to_string(segments: &[Segment]) -> String { @@ -252,7 +252,7 @@ impl Segment { impl<'a> From<&'a ast::PathSegment> for Segment { fn from(seg: &'a ast::PathSegment) -> Segment { - Segment { ident: seg.ident, id: Some(seg.id), has_args: seg.args.is_some() } + Segment { ident: seg.ident, id: Some(seg.id), has_generic_args: seg.args.is_some() } } } @@ -2019,7 +2019,7 @@ impl<'a> Resolver<'a> { path, opt_ns, record_used, path_span, crate_lint, ); - for (i, &Segment { ident, id, has_args: _ }) in path.iter().enumerate() { + for (i, &Segment { ident, id, has_generic_args: _ }) in path.iter().enumerate() { debug!("resolve_path ident {} {:?} {:?}", i, ident, id); let record_segment_res = |this: &mut Self, res| { if record_used { From a7c2cf8f510788040c4a8a721582a3533d0bf35a Mon Sep 17 00:00:00 2001 From: Josh Stone Date: Wed, 17 Jun 2020 16:30:27 -0700 Subject: [PATCH 112/123] Reduce pointer casts in Box::into_boxed_slice We only need to cast the pointer once to change `Box` to an array `Box<[T; 1]>`, then we can let unsized coercion return `Box<[T]>`. --- src/liballoc/boxed.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/liballoc/boxed.rs b/src/liballoc/boxed.rs index 22c344323a2e..ab0dde0ada66 100644 --- a/src/liballoc/boxed.rs +++ b/src/liballoc/boxed.rs @@ -248,7 +248,7 @@ impl Box { #[unstable(feature = "box_into_boxed_slice", issue = "71582")] pub fn into_boxed_slice(boxed: Box) -> Box<[T]> { // *mut T and *mut [T; 1] have the same size and alignment - unsafe { Box::from_raw(Box::into_raw(boxed) as *mut [T; 1] as *mut [T]) } + unsafe { Box::from_raw(Box::into_raw(boxed) as *mut [T; 1]) } } } From 2da9ca72bcdc6b67fbacf26d9245da367089a113 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Wed, 17 Jun 2020 17:17:07 -0700 Subject: [PATCH 113/123] Remove duplicate sentence fragment from mem::zeroed doc --- src/libcore/mem/mod.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/libcore/mem/mod.rs b/src/libcore/mem/mod.rs index 70083ffa4be3..066bb8b3dc78 100644 --- a/src/libcore/mem/mod.rs +++ b/src/libcore/mem/mod.rs @@ -583,10 +583,10 @@ pub const fn needs_drop() -> bool { /// /// There is no guarantee that an all-zero byte-pattern represents a valid value /// of some type `T`. For example, the all-zero byte-pattern is not a valid value -/// for reference types (`&T`, `&mut T`) and functions pointers. Using `zeroed` on -/// such types on such types causes immediate [undefined behavior][ub] because -/// [the Rust compiler assumes][inv] that there always is a valid value in a -/// variable it considers initialized. +/// for reference types (`&T`, `&mut T`) and functions pointers. Using `zeroed` +/// on such types causes immediate [undefined behavior][ub] because [the Rust +/// compiler assumes][inv] that there always is a valid value in a variable it +/// considers initialized. /// /// This has the same effect as [`MaybeUninit::zeroed().assume_init()`][zeroed]. /// It is useful for FFI sometimes, but should generally be avoided. From 4e77214583fcf3067ebe3c2f8aea884bb5ff18eb Mon Sep 17 00:00:00 2001 From: Lzu Tao Date: Thu, 18 Jun 2020 02:09:53 +0000 Subject: [PATCH 114/123] Improve document for `Result::as_deref(_mut)` --- src/libcore/result.rs | 56 ++++++++++++++++++++++++++++++------------- 1 file changed, 40 insertions(+), 16 deletions(-) diff --git a/src/libcore/result.rs b/src/libcore/result.rs index c7b5777a16e7..2080ae193667 100644 --- a/src/libcore/result.rs +++ b/src/libcore/result.rs @@ -1145,45 +1145,69 @@ impl> Result { } } -#[unstable(feature = "inner_deref", reason = "newly added", issue = "50264")] +#[unstable(feature = "inner_deref", issue = "50264")] impl Result { - /// Converts from `Result` (or `&Result`) to `Result<&T::Target, &E>`. + /// Converts from `Result` (or `&Result`) to `Result<&::Target, &E>`. /// - /// Leaves the original `Result` in-place, creating a new one containing a reference to the - /// `Ok` type's `Deref::Target` type. + /// Coerces the [`Ok`] variant of the original [`Result`] via [`Deref`](crate::ops::Deref) + /// and returns the new [`Result`]. + /// + /// # Examples + /// + /// ``` + /// let x: Result = Ok("hello".to_string()); + /// let y: Result<&str, &u32> = Ok("hello"); + /// assert_eq!(x.as_deref(), y); + /// + /// let x: Result = Err(42); + /// let y: Result<&str, &u32> = Err(&42); + /// assert_eq!(x.as_deref(), y); + /// ``` pub fn as_deref(&self) -> Result<&T::Target, &E> { self.as_ref().map(|t| t.deref()) } } -#[unstable(feature = "inner_deref", reason = "newly added", issue = "50264")] +#[unstable(feature = "inner_deref", issue = "50264")] impl Result { - /// Converts from `Result` (or `&Result`) to `Result<&T, &E::Target>`. + /// Converts from `Result` (or `&Result`) to `Result<&T, &::Target>`. /// - /// Leaves the original `Result` in-place, creating a new one containing a reference to the - /// `Err` type's `Deref::Target` type. + /// Coerces the [`Err`] variant of the original [`Result`] via [`Deref`](crate::ops::Deref) + /// and returns the new [`Result`]. pub fn as_deref_err(&self) -> Result<&T, &E::Target> { self.as_ref().map_err(|e| e.deref()) } } -#[unstable(feature = "inner_deref", reason = "newly added", issue = "50264")] +#[unstable(feature = "inner_deref", issue = "50264")] impl Result { - /// Converts from `Result` (or `&mut Result`) to `Result<&mut T::Target, &mut E>`. + /// Converts from `Result` (or `&mut Result`) to `Result<&mut ::Target, &mut E>`. /// - /// Leaves the original `Result` in-place, creating a new one containing a mutable reference to - /// the `Ok` type's `Deref::Target` type. + /// Coerces the [`Ok`] variant of the original [`Result`] via [`DerefMut`](crate::ops::DerefMut) + /// and returns the new [`Result`]. + /// + /// # Examples + /// + /// ``` + /// let mut x: Result = Ok("hello".to_string()); + /// let y: Result<&mut str, &mut u32> = Ok("HELLO"); + /// assert_eq!(x.as_deref_mut().map(|x| { x.make_ascii_uppercase(); x }), y); + /// + /// let mut x: Result = Err(42); + /// let y: Result<&mut str, &mut u32> = Err(&42); + /// assert_eq!(x.as_deref_mut().map(|x| { x.make_ascii_uppercase(); x }), y); + /// ``` pub fn as_deref_mut(&mut self) -> Result<&mut T::Target, &mut E> { self.as_mut().map(|t| t.deref_mut()) } } -#[unstable(feature = "inner_deref", reason = "newly added", issue = "50264")] +#[unstable(feature = "inner_deref", issue = "50264")] impl Result { - /// Converts from `Result` (or `&mut Result`) to `Result<&mut T, &mut E::Target>`. + /// Converts from `Result` (or `&mut Result`) to `Result<&mut T, &mut ::Target>`. /// - /// Leaves the original `Result` in-place, creating a new one containing a mutable reference to - /// the `Err` type's `Deref::Target` type. + /// Coerces the [`Err`] variant of the original [`Result`] via [`DerefMut`](crate::ops::DerefMut) + /// and returns the new [`Result`]. pub fn as_deref_mut_err(&mut self) -> Result<&mut T, &mut E::Target> { self.as_mut().map_err(|e| e.deref_mut()) } From d134870bd4bedefaeb07c88a8d24a520bce639cf Mon Sep 17 00:00:00 2001 From: qy3u <65523321+qy3u@users.noreply.github.com> Date: Thu, 18 Jun 2020 10:13:35 +0800 Subject: [PATCH 115/123] Document format correction --- src/libstd/sys/unix/ext/fs.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/libstd/sys/unix/ext/fs.rs b/src/libstd/sys/unix/ext/fs.rs index 732cd677a185..e4d714936047 100644 --- a/src/libstd/sys/unix/ext/fs.rs +++ b/src/libstd/sys/unix/ext/fs.rs @@ -242,7 +242,8 @@ pub trait PermissionsExt { /// let permissions = metadata.permissions(); /// /// println!("permissions: {:o}", permissions.mode()); - /// Ok(()) } + /// Ok(()) + /// } /// ``` #[stable(feature = "fs_ext", since = "1.1.0")] fn mode(&self) -> u32; @@ -262,7 +263,8 @@ pub trait PermissionsExt { /// /// permissions.set_mode(0o644); // Read/write for owner and read for others. /// assert_eq!(permissions.mode(), 0o644); - /// Ok(()) } + /// Ok(()) + /// } /// ``` #[stable(feature = "fs_ext", since = "1.1.0")] fn set_mode(&mut self, mode: u32); From b805f2c4dce9d9fb25ea48d6290d60f8546df64f Mon Sep 17 00:00:00 2001 From: Jake Degen Date: Thu, 18 Jun 2020 08:48:37 -0400 Subject: [PATCH 116/123] Added tooltip for should_panic code examples. Previously, compile_fail and ignore code examples displayed a tooltip indicating this in the documentation. This tooltip has now also been added to should_panic examples. --- src/librustdoc/html/markdown.rs | 8 ++++++++ src/librustdoc/html/static/rustdoc.css | 2 +- src/librustdoc/html/static/themes/dark.css | 16 ++++++++++++++++ src/librustdoc/html/static/themes/light.css | 16 ++++++++++++++++ src/test/rustdoc/codeblock-title.rs | 5 +++++ 5 files changed, 46 insertions(+), 1 deletion(-) diff --git a/src/librustdoc/html/markdown.rs b/src/librustdoc/html/markdown.rs index c129e54c0f28..7a6626766d38 100644 --- a/src/librustdoc/html/markdown.rs +++ b/src/librustdoc/html/markdown.rs @@ -192,6 +192,7 @@ impl<'a, I: Iterator>> Iterator for CodeBlocks<'_, 'a, I> { fn next(&mut self) -> Option { let event = self.inner.next(); let compile_fail; + let should_panic; let ignore; let edition; if let Some(Event::Start(Tag::CodeBlock(kind))) = event { @@ -205,6 +206,7 @@ impl<'a, I: Iterator>> Iterator for CodeBlocks<'_, 'a, I> { return Some(Event::Start(Tag::CodeBlock(kind))); } compile_fail = parse_result.compile_fail; + should_panic = parse_result.should_panic; ignore = parse_result.ignore; edition = parse_result.edition; } else { @@ -280,6 +282,8 @@ impl<'a, I: Iterator>> Iterator for CodeBlocks<'_, 'a, I> { Some(("This example is not tested".to_owned(), "ignore")) } else if compile_fail { Some(("This example deliberately fails to compile".to_owned(), "compile_fail")) + } else if should_panic { + Some(("This example panics".to_owned(), "should_panic")) } else if explicit_edition { Some((format!("This code runs with edition {}", edition), "edition")) } else { @@ -295,6 +299,8 @@ impl<'a, I: Iterator>> Iterator for CodeBlocks<'_, 'a, I> { " ignore" } else if compile_fail { " compile_fail" + } else if should_panic { + " should_panic" } else if explicit_edition { " edition " } else { @@ -314,6 +320,8 @@ impl<'a, I: Iterator>> Iterator for CodeBlocks<'_, 'a, I> { " ignore" } else if compile_fail { " compile_fail" + } else if should_panic { + " should_panic" } else if explicit_edition { " edition " } else { diff --git a/src/librustdoc/html/static/rustdoc.css b/src/librustdoc/html/static/rustdoc.css index 2cb3347135c1..9c6dd25394db 100644 --- a/src/librustdoc/html/static/rustdoc.css +++ b/src/librustdoc/html/static/rustdoc.css @@ -1089,7 +1089,7 @@ h3 > .collapse-toggle, h4 > .collapse-toggle { border-style: solid; } -.tooltip.compile_fail, .tooltip.ignore { +.tooltip.compile_fail, .tooltip.should_panic, .tooltip.ignore { font-weight: bold; font-size: 20px; } diff --git a/src/librustdoc/html/static/themes/dark.css b/src/librustdoc/html/static/themes/dark.css index a2986c7b927e..41dcb5c24507 100644 --- a/src/librustdoc/html/static/themes/dark.css +++ b/src/librustdoc/html/static/themes/dark.css @@ -283,6 +283,14 @@ pre.compile_fail:hover, .information:hover + pre.compile_fail { border-left: 2px solid #f00; } +pre.should_panic { + border-left: 2px solid rgba(255,0,0,.8); +} + +pre.should_panic:hover, .information:hover + pre.should_panic { + border-left: 2px solid #f00; +} + pre.ignore { border-left: 2px solid rgba(255,142,0,.6); } @@ -299,6 +307,14 @@ pre.ignore:hover, .information:hover + pre.ignore { color: #f00; } +.tooltip.should_panic { + color: rgba(255,0,0,.8); +} + +.information > .should_panic:hover { + color: #f00; +} + .tooltip.ignore { color: rgba(255,142,0,.6); } diff --git a/src/librustdoc/html/static/themes/light.css b/src/librustdoc/html/static/themes/light.css index be173d8eb46d..386fe2398e63 100644 --- a/src/librustdoc/html/static/themes/light.css +++ b/src/librustdoc/html/static/themes/light.css @@ -278,6 +278,14 @@ pre.compile_fail:hover, .information:hover + pre.compile_fail { border-left: 2px solid #f00; } +pre.should_panic { + border-left: 2px solid rgba(255,0,0,.5); +} + +pre.should_panic:hover, .information:hover + pre.should_panic { + border-left: 2px solid #f00; +} + pre.ignore { border-left: 2px solid rgba(255,142,0,.6); } @@ -294,6 +302,14 @@ pre.ignore:hover, .information:hover + pre.ignore { color: #f00; } +.tooltip.should_panic { + color: rgba(255,0,0,.5); +} + +.information > .should_panic:hover { + color: #f00; +} + .tooltip.ignore { color: rgba(255,142,0,.6); } diff --git a/src/test/rustdoc/codeblock-title.rs b/src/test/rustdoc/codeblock-title.rs index 2f77929c74e3..2157a4d53894 100644 --- a/src/test/rustdoc/codeblock-title.rs +++ b/src/test/rustdoc/codeblock-title.rs @@ -4,6 +4,7 @@ // @has foo/fn.bar.html '//*[@class="tooltip compile_fail"]/span' "This example deliberately fails to compile" // @has foo/fn.bar.html '//*[@class="tooltip ignore"]/span' "This example is not tested" +// @has foo/fn.bar.html '//*[@class="tooltip should_panic"]/span' "This example panics" /// foo /// @@ -15,6 +16,10 @@ /// goo(); /// ``` /// +/// ```should_panic +/// hoo(); +/// ``` +/// /// ``` /// let x = 0; /// ``` From 721facf29c4ba7bc1d75acafce1b4916d5feafa5 Mon Sep 17 00:00:00 2001 From: Jake Degen Date: Thu, 18 Jun 2020 11:45:52 -0400 Subject: [PATCH 117/123] Removed trailing whitespace --- src/test/rustdoc/codeblock-title.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/rustdoc/codeblock-title.rs b/src/test/rustdoc/codeblock-title.rs index 2157a4d53894..b59b21111b00 100644 --- a/src/test/rustdoc/codeblock-title.rs +++ b/src/test/rustdoc/codeblock-title.rs @@ -19,7 +19,7 @@ /// ```should_panic /// hoo(); /// ``` -/// +/// /// ``` /// let x = 0; /// ``` From abb5800dd5b05a1b0b3e1c840866389f05548ed5 Mon Sep 17 00:00:00 2001 From: Eric Huss Date: Mon, 8 Jun 2020 09:09:21 -0700 Subject: [PATCH 118/123] Ensure std benchmarks get tested. --- src/liballoc/Cargo.toml | 1 + src/libcore/Cargo.toml | 1 + src/libstd/Cargo.toml | 5 +++++ 3 files changed, 7 insertions(+) diff --git a/src/liballoc/Cargo.toml b/src/liballoc/Cargo.toml index d1119f7b7c0a..914195f015b5 100644 --- a/src/liballoc/Cargo.toml +++ b/src/liballoc/Cargo.toml @@ -25,6 +25,7 @@ path = "../liballoc/tests/lib.rs" [[bench]] name = "collectionsbenches" path = "../liballoc/benches/lib.rs" +test = true [[bench]] name = "vec_deque_append_bench" diff --git a/src/libcore/Cargo.toml b/src/libcore/Cargo.toml index ac07ffb14feb..42c555cafac8 100644 --- a/src/libcore/Cargo.toml +++ b/src/libcore/Cargo.toml @@ -19,6 +19,7 @@ path = "../libcore/tests/lib.rs" [[bench]] name = "corebenches" path = "../libcore/benches/lib.rs" +test = true [dev-dependencies] rand = "0.7" diff --git a/src/libstd/Cargo.toml b/src/libstd/Cargo.toml index 83029a864209..490afb5a0438 100644 --- a/src/libstd/Cargo.toml +++ b/src/libstd/Cargo.toml @@ -74,3 +74,8 @@ std_detect_dlsym_getauxval = [] threads = 125 # Maximum heap size heap_size = 0x8000000 + +[[bench]] +name = "stdbenches" +path = "benches/lib.rs" +test = true From 8aecafe8e8c375168baac42f7fdc038048089e8f Mon Sep 17 00:00:00 2001 From: Ivan Tham Date: Fri, 19 Jun 2020 00:33:24 +0800 Subject: [PATCH 119/123] Fix liballoc doc spelling --- src/liballoc/vec.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/liballoc/vec.rs b/src/liballoc/vec.rs index 06462fd96d9a..13dfb8b332d4 100644 --- a/src/liballoc/vec.rs +++ b/src/liballoc/vec.rs @@ -1639,7 +1639,7 @@ impl Vec { } } -// This code generalises `extend_with_{element,default}`. +// This code generalizes `extend_with_{element,default}`. trait ExtendWith { fn next(&mut self) -> T; fn last(self) -> T; From ec8ff1cc242a447e9bef6ba908ce1fdd233a0367 Mon Sep 17 00:00:00 2001 From: Ivan Tham Date: Fri, 19 Jun 2020 00:34:20 +0800 Subject: [PATCH 120/123] Liballoc clean up macro_rules style --- src/liballoc/vec.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/liballoc/vec.rs b/src/liballoc/vec.rs index 13dfb8b332d4..31cadbd84bfc 100644 --- a/src/liballoc/vec.rs +++ b/src/liballoc/vec.rs @@ -1837,7 +1837,7 @@ unsafe trait IsZero { } macro_rules! impl_is_zero { - ($t: ty, $is_zero: expr) => { + ($t:ty, $is_zero:expr) => { unsafe impl IsZero for $t { #[inline] fn is_zero(&self) -> bool { From 111c2d27f501ad935daa2ba22001dceb92113a0e Mon Sep 17 00:00:00 2001 From: Ivan Tham Date: Fri, 19 Jun 2020 00:34:49 +0800 Subject: [PATCH 121/123] Rearrange liballoc __impl_slice_eq1 --- src/liballoc/vec.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/liballoc/vec.rs b/src/liballoc/vec.rs index 31cadbd84bfc..f16cac05ea03 100644 --- a/src/liballoc/vec.rs +++ b/src/liballoc/vec.rs @@ -2362,9 +2362,9 @@ macro_rules! __impl_slice_eq1 { __impl_slice_eq1! { [] Vec, Vec, } __impl_slice_eq1! { [] Vec, &[B], } __impl_slice_eq1! { [] Vec, &mut [B], } +__impl_slice_eq1! { [] Cow<'_, [A]>, Vec, A: Clone } __impl_slice_eq1! { [] Cow<'_, [A]>, &[B], A: Clone } __impl_slice_eq1! { [] Cow<'_, [A]>, &mut [B], A: Clone } -__impl_slice_eq1! { [] Cow<'_, [A]>, Vec, A: Clone } __impl_slice_eq1! { [const N: usize] Vec, [B; N], [B; N]: LengthAtMost32 } __impl_slice_eq1! { [const N: usize] Vec, &[B; N], [B; N]: LengthAtMost32 } From 5a9ff052b4879e4ef6748c2f85563a7db0548c0b Mon Sep 17 00:00:00 2001 From: Eric Huss Date: Thu, 18 Jun 2020 14:45:19 -0700 Subject: [PATCH 122/123] Disable core benches on wasm (benches not supported). --- src/libcore/benches/lib.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/libcore/benches/lib.rs b/src/libcore/benches/lib.rs index 570fc4ab9339..de4ef7949f34 100644 --- a/src/libcore/benches/lib.rs +++ b/src/libcore/benches/lib.rs @@ -1,3 +1,5 @@ +// wasm32 does not support benches (no time). +#![cfg(not(target_arch = "wasm32"))] #![feature(flt2dec)] #![feature(test)] From 35a2915bf3f24edbbee2d6a9c8f2318304b99df3 Mon Sep 17 00:00:00 2001 From: Benjamin Saunders Date: Thu, 18 Jun 2020 18:52:26 -0700 Subject: [PATCH 123/123] Remove now-redundant branch --- src/libstd/f32.rs | 6 +----- src/libstd/f64.rs | 6 +----- 2 files changed, 2 insertions(+), 10 deletions(-) diff --git a/src/libstd/f32.rs b/src/libstd/f32.rs index ab468f42b3bd..7899c422da62 100644 --- a/src/libstd/f32.rs +++ b/src/libstd/f32.rs @@ -832,11 +832,7 @@ impl f32 { #[stable(feature = "rust1", since = "1.0.0")] #[inline] pub fn asinh(self) -> f32 { - if self == Self::NEG_INFINITY { - Self::NEG_INFINITY - } else { - (self.abs() + ((self * self) + 1.0).sqrt()).ln().copysign(self) - } + (self.abs() + ((self * self) + 1.0).sqrt()).ln().copysign(self) } /// Inverse hyperbolic cosine function. diff --git a/src/libstd/f64.rs b/src/libstd/f64.rs index c033198f021c..c663c5ffc9be 100644 --- a/src/libstd/f64.rs +++ b/src/libstd/f64.rs @@ -834,11 +834,7 @@ impl f64 { #[stable(feature = "rust1", since = "1.0.0")] #[inline] pub fn asinh(self) -> f64 { - if self == Self::NEG_INFINITY { - Self::NEG_INFINITY - } else { - (self.abs() + ((self * self) + 1.0).sqrt()).ln().copysign(self) - } + (self.abs() + ((self * self) + 1.0).sqrt()).ln().copysign(self) } /// Inverse hyperbolic cosine function.