Auto merge of #107919 - Dylan-DPC:rollup-fkl9swa, r=Dylan-DPC

Rollup of 9 pull requests

Successful merges:

 - #105019 (Add parentheses properly for borrowing suggestion)
 - #106001 (Stop at the first `NULL` argument when iterating `argv`)
 - #107098 (Suggest function call on pattern type mismatch)
 - #107490 (rustdoc: remove inconsistently-present sidebar tooltips)
 - #107855 (Add a couple random projection tests for new solver)
 - #107857 (Add ui test for implementation on projection)
 - #107878 (Clarify `new_size` for realloc means bytes)
 - #107888 (revert #107074, add regression test)
 - #107900 (Zero the `REPARSE_MOUNTPOINT_DATA_BUFFER` header)

Failed merges:

r? `@ghost`
`@rustbot` modify labels: rollup
This commit is contained in:
bors 2023-02-11 09:19:07 +00:00
commit 71f6675de1
25 changed files with 320 additions and 70 deletions

View File

@ -13,7 +13,7 @@ use rustc_middle::mir::{
RetagKind, RuntimePhase, Rvalue, SourceScope, Statement, StatementKind, Terminator,
TerminatorKind, UnOp, START_BLOCK,
};
use rustc_middle::ty::{self, InstanceDef, ParamEnv, Ty, TyCtxt};
use rustc_middle::ty::{self, InstanceDef, ParamEnv, Ty, TyCtxt, TypeVisitable};
use rustc_mir_dataflow::impls::MaybeStorageLive;
use rustc_mir_dataflow::storage::always_storage_live_locals;
use rustc_mir_dataflow::{Analysis, ResultsCursor};
@ -231,6 +231,15 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
return true;
}
// We sometimes have to use `defining_opaque_types` for subtyping
// to succeed here and figuring out how exactly that should work
// is annoying. It is harmless enough to just not validate anything
// in that case. We still check this after analysis as all opque
// types have been revealed at this point.
if (src, dest).has_opaque_types() {
return true;
}
crate::util::is_subtype(self.tcx, self.param_env, src, dest)
}
}

View File

@ -41,7 +41,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
// #55810: Type check patterns first so we get types for all bindings.
let scrut_span = scrut.span.find_ancestor_inside(expr.span).unwrap_or(scrut.span);
for arm in arms {
self.check_pat_top(&arm.pat, scrutinee_ty, Some(scrut_span), true);
self.check_pat_top(&arm.pat, scrutinee_ty, Some(scrut_span), Some(scrut));
}
// Now typecheck the blocks.

View File

@ -90,7 +90,7 @@ pub(super) fn check_fn<'a, 'tcx>(
for (idx, (param_ty, param)) in inputs_fn.chain(maybe_va_list).zip(body.params).enumerate() {
// Check the pattern.
let ty_span = try { inputs_hir?.get(idx)?.span };
fcx.check_pat_top(&param.pat, param_ty, ty_span, false);
fcx.check_pat_top(&param.pat, param_ty, ty_span, None);
// Check that argument is Sized.
// The check for a non-trivial pattern is a hack to avoid duplicate warnings

View File

@ -1330,11 +1330,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
// Does the expected pattern type originate from an expression and what is the span?
let (origin_expr, ty_span) = match (decl.ty, decl.init) {
(Some(ty), _) => (false, Some(ty.span)), // Bias towards the explicit user type.
(Some(ty), _) => (None, Some(ty.span)), // Bias towards the explicit user type.
(_, Some(init)) => {
(true, Some(init.span.find_ancestor_inside(decl.span).unwrap_or(init.span)))
(Some(init), Some(init.span.find_ancestor_inside(decl.span).unwrap_or(init.span)))
} // No explicit type; so use the scrutinee.
_ => (false, None), // We have `let $pat;`, so the expected type is unconstrained.
_ => (None, None), // We have `let $pat;`, so the expected type is unconstrained.
};
// Type check the pattern. Override if necessary to avoid knock-on errors.

View File

@ -46,7 +46,7 @@ struct TopInfo<'tcx> {
/// Was the origin of the `span` from a scrutinee expression?
///
/// Otherwise there is no scrutinee and it could be e.g. from the type of a formal parameter.
origin_expr: bool,
origin_expr: Option<&'tcx hir::Expr<'tcx>>,
/// The span giving rise to the `expected` type, if one could be provided.
///
/// If `origin_expr` is `true`, then this is the span of the scrutinee as in:
@ -74,7 +74,8 @@ struct TopInfo<'tcx> {
impl<'tcx> FnCtxt<'_, 'tcx> {
fn pattern_cause(&self, ti: TopInfo<'tcx>, cause_span: Span) -> ObligationCause<'tcx> {
let code = Pattern { span: ti.span, root_ty: ti.expected, origin_expr: ti.origin_expr };
let code =
Pattern { span: ti.span, root_ty: ti.expected, origin_expr: ti.origin_expr.is_some() };
self.cause(cause_span, code)
}
@ -85,7 +86,14 @@ impl<'tcx> FnCtxt<'_, 'tcx> {
actual: Ty<'tcx>,
ti: TopInfo<'tcx>,
) -> Option<DiagnosticBuilder<'tcx, ErrorGuaranteed>> {
self.demand_eqtype_with_origin(&self.pattern_cause(ti, cause_span), expected, actual)
let mut diag =
self.demand_eqtype_with_origin(&self.pattern_cause(ti, cause_span), expected, actual)?;
if let Some(expr) = ti.origin_expr {
self.suggest_fn_call(&mut diag, expr, expected, |output| {
self.can_eq(self.param_env, output, actual).is_ok()
});
}
Some(diag)
}
fn demand_eqtype_pat(
@ -127,7 +135,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
pat: &'tcx Pat<'tcx>,
expected: Ty<'tcx>,
span: Option<Span>,
origin_expr: bool,
origin_expr: Option<&'tcx hir::Expr<'tcx>>,
) {
let info = TopInfo { expected, origin_expr, span };
self.check_pat(pat, expected, INITIAL_BM, info);
@ -2146,7 +2154,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
err.help("the semantics of slice patterns changed recently; see issue #62254");
} else if self.autoderef(span, expected_ty)
.any(|(ty, _)| matches!(ty.kind(), ty::Slice(..) | ty::Array(..)))
&& let (Some(span), true) = (ti.span, ti.origin_expr)
&& let Some(span) = ti.span
&& let Some(_) = ti.origin_expr
&& let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(span)
{
let ty = self.resolve_vars_if_possible(ti.expected);

View File

@ -19,6 +19,7 @@ use rustc_hir as hir;
use rustc_hir::def::DefKind;
use rustc_hir::def_id::DefId;
use rustc_hir::intravisit::Visitor;
use rustc_hir::is_range_literal;
use rustc_hir::lang_items::LangItem;
use rustc_hir::{AsyncGeneratorKind, GeneratorKind, Node};
use rustc_hir::{Expr, HirId};
@ -1349,14 +1350,41 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
Applicability::MaybeIncorrect,
);
} else {
// Issue #104961, we need to add parentheses properly for compond expressions
// for example, `x.starts_with("hi".to_string() + "you")`
// should be `x.starts_with(&("hi".to_string() + "you"))`
let Some(body_id) = self.tcx.hir().maybe_body_owned_by(obligation.cause.body_id) else { return false; };
let body = self.tcx.hir().body(body_id);
let mut expr_finder = FindExprBySpan::new(span);
expr_finder.visit_expr(body.value);
let Some(expr) = expr_finder.result else { return false; };
let needs_parens = match expr.kind {
// parenthesize if needed (Issue #46756)
hir::ExprKind::Cast(_, _) | hir::ExprKind::Binary(_, _, _) => true,
// parenthesize borrows of range literals (Issue #54505)
_ if is_range_literal(expr) => true,
_ => false,
};
let is_mut = mut_ref_self_ty_satisfies_pred || ref_inner_ty_mut;
err.span_suggestion_verbose(
span.shrink_to_lo(),
&format!(
"consider{} borrowing here",
if is_mut { " mutably" } else { "" }
),
format!("&{}", if is_mut { "mut " } else { "" }),
let span = if needs_parens { span } else { span.shrink_to_lo() };
let sugg_prefix = format!("&{}", if is_mut { "mut " } else { "" });
let sugg_msg = &format!(
"consider{} borrowing here",
if is_mut { " mutably" } else { "" }
);
let suggestions = if !needs_parens {
vec![(span.shrink_to_lo(), format!("{}", sugg_prefix))]
} else {
vec![
(span.shrink_to_lo(), format!("{}(", sugg_prefix)),
(span.shrink_to_hi(), ")".to_string()),
]
};
err.multipart_suggestion_verbose(
sugg_msg,
suggestions,
Applicability::MaybeIncorrect,
);
}

View File

@ -203,7 +203,7 @@ pub unsafe trait GlobalAlloc {
ptr
}
/// Shrink or grow a block of memory to the given `new_size`.
/// Shrink or grow a block of memory to the given `new_size` in bytes.
/// The block is described by the given `ptr` pointer and `layout`.
///
/// If this returns a non-null pointer, then ownership of the memory block
@ -211,10 +211,11 @@ pub unsafe trait GlobalAlloc {
/// Any access to the old `ptr` is Undefined Behavior, even if the
/// allocation remained in-place. The newly returned pointer is the only valid pointer
/// for accessing this memory now.
///
/// The new memory block is allocated with `layout`,
/// but with the `size` updated to `new_size`. This new layout must be
/// used when deallocating the new memory block with `dealloc`. The range
/// `0..min(layout.size(), new_size)` of the new memory block is
/// but with the `size` updated to `new_size` in bytes.
/// This new layout must be used when deallocating the new memory block with `dealloc`.
/// The range `0..min(layout.size(), new_size)` of the new memory block is
/// guaranteed to have the same values as the original block.
///
/// If this method returns null, then ownership of the memory

View File

@ -141,12 +141,28 @@ mod imp {
// list.
let argv = ARGV.load(Ordering::Relaxed);
let argc = if argv.is_null() { 0 } else { ARGC.load(Ordering::Relaxed) };
(0..argc)
.map(|i| {
let cstr = CStr::from_ptr(*argv.offset(i) as *const libc::c_char);
OsStringExt::from_vec(cstr.to_bytes().to_vec())
})
.collect()
let mut args = Vec::with_capacity(argc as usize);
for i in 0..argc {
let ptr = *argv.offset(i) as *const libc::c_char;
// Some C commandline parsers (e.g. GLib and Qt) are replacing already
// handled arguments in `argv` with `NULL` and move them to the end. That
// means that `argc` might be bigger than the actual number of non-`NULL`
// pointers in `argv` at this point.
//
// To handle this we simply stop iterating at the first `NULL` argument.
//
// `argv` is also guaranteed to be `NULL`-terminated so any non-`NULL` arguments
// after the first `NULL` can safely be ignored.
if ptr.is_null() {
break;
}
let cstr = CStr::from_ptr(ptr);
args.push(OsStringExt::from_vec(cstr.to_bytes().to_vec()));
}
args
}
}
}

View File

@ -1393,6 +1393,8 @@ fn symlink_junction_inner(original: &Path, junction: &Path) -> io::Result<()> {
let mut data = Align8([MaybeUninit::<u8>::uninit(); c::MAXIMUM_REPARSE_DATA_BUFFER_SIZE]);
let data_ptr = data.0.as_mut_ptr();
let db = data_ptr.cast::<c::REPARSE_MOUNTPOINT_DATA_BUFFER>();
// Zero the header to ensure it's fully initialized, including reserved parameters.
*db = mem::zeroed();
let buf = ptr::addr_of_mut!((*db).ReparseTarget).cast::<c::WCHAR>();
let mut i = 0;
// FIXME: this conversion is very hacky

View File

@ -18,7 +18,7 @@ use super::search_index::build_index;
use super::write_shared::write_shared;
use super::{
collect_spans_and_sources, print_sidebar, scrape_examples_help, sidebar_module_like, AllTypes,
LinkFromSrc, NameDoc, StylePath,
LinkFromSrc, StylePath,
};
use crate::clean::{self, types::ExternalLocation, ExternalCrate};
@ -256,7 +256,7 @@ impl<'tcx> Context<'tcx> {
}
/// Construct a map of items shown in the sidebar to a plain-text summary of their docs.
fn build_sidebar_items(&self, m: &clean::Module) -> BTreeMap<String, Vec<NameDoc>> {
fn build_sidebar_items(&self, m: &clean::Module) -> BTreeMap<String, Vec<String>> {
// BTreeMap instead of HashMap to get a sorted output
let mut map: BTreeMap<_, Vec<_>> = BTreeMap::new();
let mut inserted: FxHashMap<ItemType, FxHashSet<Symbol>> = FxHashMap::default();
@ -274,10 +274,7 @@ impl<'tcx> Context<'tcx> {
if inserted.entry(short).or_default().insert(myname) {
let short = short.to_string();
let myname = myname.to_string();
map.entry(short).or_default().push((
myname,
Some(item.doc_value().map_or_else(String::new, |s| plain_text_summary(&s))),
));
map.entry(short).or_default().push(myname);
}
}

View File

@ -83,9 +83,6 @@ use crate::scrape_examples::{CallData, CallLocation};
use crate::try_none;
use crate::DOC_RUST_LANG_ORG_CHANNEL;
/// A pair of name and its optional document.
pub(crate) type NameDoc = (String, Option<String>);
pub(crate) fn ensure_trailing_slash(v: &str) -> impl fmt::Display + '_ {
crate::html::format::display_fn(move |f| {
if !v.ends_with('/') && !v.is_empty() { write!(f, "{}/", v) } else { f.write_str(v) }

View File

@ -455,10 +455,7 @@ function loadCss(cssUrl) {
const ul = document.createElement("ul");
ul.className = "block " + shortty;
for (const item of filtered) {
const name = item[0];
const desc = item[1]; // can be null
for (const name of filtered) {
let path;
if (shortty === "mod") {
path = name + "/index.html";
@ -468,7 +465,6 @@ function loadCss(cssUrl) {
const current_page = document.location.href.split("/").pop();
const link = document.createElement("a");
link.href = path;
link.title = desc;
if (path === current_page) {
link.className = "current";
}

View File

@ -1,27 +0,0 @@
#![crate_type = "lib"]
#![crate_name = "summaries"]
//! This *summary* has a [link] and `code`.
//!
//! This is the second paragraph.
//!
//! [link]: https://example.com
// @hasraw search-index.js 'This <em>summary</em> has a link and <code>code</code>.'
// @!hasraw - 'second paragraph'
/// This `code` will be rendered in a code tag.
///
/// This text should not be rendered.
pub struct Sidebar;
// @hasraw search-index.js 'This <code>code</code> will be rendered in a code tag.'
// @hasraw summaries/sidebar-items.js 'This `code` will be rendered in a code tag.'
// @!hasraw - 'text should not be rendered'
/// ```text
/// this block should not be rendered
/// ```
pub struct Sidebar2;
// @!hasraw summaries/sidebar-items.js 'block should not be rendered'

View File

@ -0,0 +1,19 @@
// This test ensures that if implementation on projections is supported,
// it doesn't end in very weird cycle error.
#![crate_type = "lib"]
pub trait Identity {
type Identity: ?Sized;
}
impl<T: ?Sized> Identity for T {
type Identity = Self;
}
pub struct I8<const F: i8>;
impl <I8<{i8::MIN}> as Identity>::Identity {
//~^ ERROR no nominal type found for inherent implementation
pub fn foo(&self) {}
}

View File

@ -0,0 +1,11 @@
error[E0118]: no nominal type found for inherent implementation
--> $DIR/wrong-normalization.rs:16:6
|
LL | impl <I8<{i8::MIN}> as Identity>::Identity {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ impl requires a nominal type
|
= note: either implement a trait on it or create a newtype to wrap it instead
error: aborting due to previous error
For more information about this error, try `rustc --explain E0118`.

View File

@ -1,4 +1,7 @@
// check-pass
// compile-flags: -Zvalidate-mir
// Using -Zvalidate-mir as a regression test for #107346.
trait Duh {}

View File

@ -1,5 +1,5 @@
warning: opaque type `impl Trait<Assoc = impl Send>` does not satisfy its associated type bounds
--> $DIR/nested-return-type2.rs:25:24
--> $DIR/nested-return-type2.rs:28:24
|
LL | type Assoc: Duh;
| --- this associated type bound is unsatisfied for `impl Send`

View File

@ -0,0 +1,16 @@
// run-rustfix
fn foo(x: &str) -> bool {
x.starts_with(&("hi".to_string() + " you"))
//~^ ERROR expected a `FnMut<(char,)>` closure, found `String`
}
fn foo2(x: &str) -> bool {
x.starts_with(&"hi".to_string())
//~^ ERROR expected a `FnMut<(char,)>` closure, found `String`
}
fn main() {
foo("hi you");
foo2("hi");
}

View File

@ -0,0 +1,16 @@
// run-rustfix
fn foo(x: &str) -> bool {
x.starts_with("hi".to_string() + " you")
//~^ ERROR expected a `FnMut<(char,)>` closure, found `String`
}
fn foo2(x: &str) -> bool {
x.starts_with("hi".to_string())
//~^ ERROR expected a `FnMut<(char,)>` closure, found `String`
}
fn main() {
foo("hi you");
foo2("hi");
}

View File

@ -0,0 +1,37 @@
error[E0277]: expected a `FnMut<(char,)>` closure, found `String`
--> $DIR/issue-104961.rs:4:19
|
LL | x.starts_with("hi".to_string() + " you")
| ----------- ^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Pattern<'_>` is not implemented for `String`
| |
| required by a bound introduced by this call
|
= note: the trait bound `String: Pattern<'_>` is not satisfied
= note: required for `String` to implement `Pattern<'_>`
note: required by a bound in `core::str::<impl str>::starts_with`
--> $SRC_DIR/core/src/str/mod.rs:LL:COL
help: consider borrowing here
|
LL | x.starts_with(&("hi".to_string() + " you"))
| ++ +
error[E0277]: expected a `FnMut<(char,)>` closure, found `String`
--> $DIR/issue-104961.rs:9:19
|
LL | x.starts_with("hi".to_string())
| ----------- ^^^^^^^^^^^^^^^^ the trait `Pattern<'_>` is not implemented for `String`
| |
| required by a bound introduced by this call
|
= note: the trait bound `String: Pattern<'_>` is not satisfied
= note: required for `String` to implement `Pattern<'_>`
note: required by a bound in `core::str::<impl str>::starts_with`
--> $SRC_DIR/core/src/str/mod.rs:LL:COL
help: consider borrowing here
|
LL | x.starts_with(&"hi".to_string())
| +
error: aborting due to 2 previous errors
For more information about this error, try `rustc --explain E0277`.

View File

@ -0,0 +1,16 @@
enum E {
One(i32, i32),
}
fn main() {
let var = E::One;
if let E::One(var1, var2) = var {
//~^ ERROR mismatched types
//~| HELP use parentheses to construct this tuple variant
println!("{var1} {var2}");
}
let Some(x) = Some;
//~^ ERROR mismatched types
//~| HELP use parentheses to construct this tuple variant
}

View File

@ -0,0 +1,33 @@
error[E0308]: mismatched types
--> $DIR/suggest-call-on-pat-mismatch.rs:7:12
|
LL | if let E::One(var1, var2) = var {
| ^^^^^^^^^^^^^^^^^^ --- this expression has type `fn(i32, i32) -> E {E::One}`
| |
| expected enum constructor, found `E`
|
= note: expected enum constructor `fn(i32, i32) -> E {E::One}`
found enum `E`
help: use parentheses to construct this tuple variant
|
LL | if let E::One(var1, var2) = var(/* i32 */, /* i32 */) {
| ++++++++++++++++++++++
error[E0308]: mismatched types
--> $DIR/suggest-call-on-pat-mismatch.rs:13:9
|
LL | let Some(x) = Some;
| ^^^^^^^ ---- this expression has type `fn(_) -> Option<_> {Option::<_>::Some}`
| |
| expected enum constructor, found `Option<_>`
|
= note: expected enum constructor `fn(_) -> Option<_> {Option::<_>::Some}`
found enum `Option<_>`
help: use parentheses to construct this tuple variant
|
LL | let Some(x) = Some(/* value */);
| +++++++++++++
error: aborting due to 2 previous errors
For more information about this error, try `rustc --explain E0308`.

View File

@ -0,0 +1,25 @@
// compile-flags: -Ztrait-solver=next
// check-pass
trait Foo {
type Assoc;
}
trait Bar {}
impl<T> Foo for T {
type Assoc = i32;
}
impl<T> Bar for T where T: Foo<Assoc = i32> {}
fn require_bar<T: Bar>() {}
fn foo<T: Foo>() {
// Unlike the classic solver, `<T as Foo>::Assoc = _` will still project
// down to `i32` even though there's a param-env candidate here, since we
// don't assemble any param-env projection candidates for `T: Foo` alone.
require_bar::<T>();
}
fn main() {}

View File

@ -0,0 +1,30 @@
// compile-flags: -Ztrait-solver=next
// When we're solving `<T as Foo>::Assoc = i32`, we actually first solve
// `<T as Foo>::Assoc = _#1t`, then unify `_#1t` with `i32`. That goal
// with the inference variable is ambiguous when there are >1 param-env
// candidates.
// We don't unify the RHS of a projection goal eagerly when solving, both
// for caching reasons and partly to make sure that we don't make the new
// trait solver smarter than it should be.
// This is (as far as I can tell) a forwards-compatible decision, but if you
// make this test go from fail to pass, be sure you understand the implications!
trait Foo {
type Assoc;
}
trait Bar {}
impl<T> Bar for T where T: Foo<Assoc = i32> {}
fn needs_bar<T: Bar>() {}
fn foo<T: Foo<Assoc = i32> + Foo<Assoc = u32>>() {
needs_bar::<T>();
//~^ ERROR type annotations needed: cannot satisfy `T: Bar`
}
fn main() {}

View File

@ -0,0 +1,16 @@
error[E0283]: type annotations needed: cannot satisfy `T: Bar`
--> $DIR/two-projection-param-candidates-are-ambiguous.rs:26:5
|
LL | needs_bar::<T>();
| ^^^^^^^^^^^^^^
|
= note: cannot satisfy `T: Bar`
note: required by a bound in `needs_bar`
--> $DIR/two-projection-param-candidates-are-ambiguous.rs:23:17
|
LL | fn needs_bar<T: Bar>() {}
| ^^^ required by this bound in `needs_bar`
error: aborting due to previous error
For more information about this error, try `rustc --explain E0283`.