Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Rollup of 4 pull requests #127489

Merged
merged 12 commits into from
Jul 8, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
102 changes: 94 additions & 8 deletions compiler/rustc_hir_typeck/src/closure.rs
Original file line number Diff line number Diff line change
Expand Up @@ -424,9 +424,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
if let Some(trait_def_id) = trait_def_id {
let found_kind = match closure_kind {
hir::ClosureKind::Closure => self.tcx.fn_trait_kind_from_def_id(trait_def_id),
hir::ClosureKind::CoroutineClosure(hir::CoroutineDesugaring::Async) => {
self.tcx.async_fn_trait_kind_from_def_id(trait_def_id)
}
hir::ClosureKind::CoroutineClosure(hir::CoroutineDesugaring::Async) => self
.tcx
.async_fn_trait_kind_from_def_id(trait_def_id)
.or_else(|| self.tcx.fn_trait_kind_from_def_id(trait_def_id)),
_ => None,
};

Expand Down Expand Up @@ -470,14 +471,37 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
// for closures and async closures, respectively.
match closure_kind {
hir::ClosureKind::Closure
if self.tcx.fn_trait_kind_from_def_id(trait_def_id).is_some() => {}
if self.tcx.fn_trait_kind_from_def_id(trait_def_id).is_some() =>
{
self.extract_sig_from_projection(cause_span, projection)
}
hir::ClosureKind::CoroutineClosure(hir::CoroutineDesugaring::Async)
if self.tcx.async_fn_trait_kind_from_def_id(trait_def_id).is_some() =>
{
self.extract_sig_from_projection(cause_span, projection)
}
// It's possible we've passed the closure to a (somewhat out-of-fashion)
// `F: FnOnce() -> Fut, Fut: Future<Output = T>` style bound. Let's still
// guide inference here, since it's beneficial for the user.
hir::ClosureKind::CoroutineClosure(hir::CoroutineDesugaring::Async)
if self.tcx.async_fn_trait_kind_from_def_id(trait_def_id).is_some() => {}
_ => return None,
if self.tcx.fn_trait_kind_from_def_id(trait_def_id).is_some() =>
{
self.extract_sig_from_projection_and_future_bound(cause_span, projection)
}
_ => None,
}
}

/// Given an `FnOnce::Output` or `AsyncFn::Output` projection, extract the args
/// and return type to infer a [`ty::PolyFnSig`] for the closure.
fn extract_sig_from_projection(
&self,
cause_span: Option<Span>,
projection: ty::PolyProjectionPredicate<'tcx>,
) -> Option<ExpectedSig<'tcx>> {
let projection = self.resolve_vars_if_possible(projection);

let arg_param_ty = projection.skip_binder().projection_term.args.type_at(1);
let arg_param_ty = self.resolve_vars_if_possible(arg_param_ty);
debug!(?arg_param_ty);

let ty::Tuple(input_tys) = *arg_param_ty.kind() else {
Expand All @@ -486,7 +510,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {

// Since this is a return parameter type it is safe to unwrap.
let ret_param_ty = projection.skip_binder().term.expect_type();
let ret_param_ty = self.resolve_vars_if_possible(ret_param_ty);
debug!(?ret_param_ty);

let sig = projection.rebind(self.tcx.mk_fn_sig(
Expand All @@ -500,6 +523,69 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
Some(ExpectedSig { cause_span, sig })
}

/// When an async closure is passed to a function that has a "two-part" `Fn`
/// and `Future` trait bound, like:
///
/// ```rust
/// use std::future::Future;
///
/// fn not_exactly_an_async_closure<F, Fut>(_f: F)
/// where
/// F: FnOnce(String, u32) -> Fut,
/// Fut: Future<Output = i32>,
/// {}
/// ```
///
/// The we want to be able to extract the signature to guide inference in the async
/// closure. We will have two projection predicates registered in this case. First,
/// we identify the `FnOnce<Args, Output = ?Fut>` bound, and if the output type is
/// an inference variable `?Fut`, we check if that is bounded by a `Future<Output = Ty>`
/// projection.
fn extract_sig_from_projection_and_future_bound(
&self,
cause_span: Option<Span>,
projection: ty::PolyProjectionPredicate<'tcx>,
) -> Option<ExpectedSig<'tcx>> {
let projection = self.resolve_vars_if_possible(projection);

let arg_param_ty = projection.skip_binder().projection_term.args.type_at(1);
debug!(?arg_param_ty);

let ty::Tuple(input_tys) = *arg_param_ty.kind() else {
return None;
};

// If the return type is a type variable, look for bounds on it.
// We could theoretically support other kinds of return types here,
// but none of them would be useful, since async closures return
// concrete anonymous future types, and their futures are not coerced
// into any other type within the body of the async closure.
let ty::Infer(ty::TyVar(return_vid)) = *projection.skip_binder().term.expect_type().kind()
else {
return None;
};

// FIXME: We may want to elaborate here, though I assume this will be exceedingly rare.
for bound in self.obligations_for_self_ty(return_vid) {
if let Some(ret_projection) = bound.predicate.as_projection_clause()
&& let Some(ret_projection) = ret_projection.no_bound_vars()
&& self.tcx.is_lang_item(ret_projection.def_id(), LangItem::FutureOutput)
{
let sig = projection.rebind(self.tcx.mk_fn_sig(
input_tys,
ret_projection.term.expect_type(),
false,
hir::Safety::Safe,
Abi::Rust,
));

return Some(ExpectedSig { cause_span, sig });
}
}

None
}

fn sig_of_closure(
&self,
expr_def_id: LocalDefId,
Expand Down
20 changes: 10 additions & 10 deletions src/tools/run-make-support/src/fs_wrapper.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use std::fs;
use std::path::Path;

/// A wrapper around [`std::fs::remove_file`] which includes the file path in the panic message..
/// A wrapper around [`std::fs::remove_file`] which includes the file path in the panic message.
#[track_caller]
pub fn remove_file<P: AsRef<Path>>(path: P) {
fs::remove_file(path.as_ref())
Expand All @@ -18,21 +18,21 @@ pub fn copy<P: AsRef<Path>, Q: AsRef<Path>>(from: P, to: Q) {
));
}

/// A wrapper around [`std::fs::File::create`] which includes the file path in the panic message..
/// A wrapper around [`std::fs::File::create`] which includes the file path in the panic message.
#[track_caller]
pub fn create_file<P: AsRef<Path>>(path: P) {
fs::File::create(path.as_ref())
.expect(&format!("the file in path \"{}\" could not be created", path.as_ref().display()));
}

/// A wrapper around [`std::fs::read`] which includes the file path in the panic message..
/// A wrapper around [`std::fs::read`] which includes the file path in the panic message.
#[track_caller]
pub fn read<P: AsRef<Path>>(path: P) -> Vec<u8> {
fs::read(path.as_ref())
.expect(&format!("the file in path \"{}\" could not be read", path.as_ref().display()))
}

/// A wrapper around [`std::fs::read_to_string`] which includes the file path in the panic message..
/// A wrapper around [`std::fs::read_to_string`] which includes the file path in the panic message.
#[track_caller]
pub fn read_to_string<P: AsRef<Path>>(path: P) -> String {
fs::read_to_string(path.as_ref()).expect(&format!(
Expand All @@ -41,14 +41,14 @@ pub fn read_to_string<P: AsRef<Path>>(path: P) -> String {
))
}

/// A wrapper around [`std::fs::read_dir`] which includes the file path in the panic message..
/// A wrapper around [`std::fs::read_dir`] which includes the file path in the panic message.
#[track_caller]
pub fn read_dir<P: AsRef<Path>>(path: P) -> fs::ReadDir {
fs::read_dir(path.as_ref())
.expect(&format!("the directory in path \"{}\" could not be read", path.as_ref().display()))
}

/// A wrapper around [`std::fs::write`] which includes the file path in the panic message..
/// A wrapper around [`std::fs::write`] which includes the file path in the panic message.
#[track_caller]
pub fn write<P: AsRef<Path>, C: AsRef<[u8]>>(path: P, contents: C) {
fs::write(path.as_ref(), contents.as_ref()).expect(&format!(
Expand All @@ -57,7 +57,7 @@ pub fn write<P: AsRef<Path>, C: AsRef<[u8]>>(path: P, contents: C) {
));
}

/// A wrapper around [`std::fs::remove_dir_all`] which includes the file path in the panic message..
/// A wrapper around [`std::fs::remove_dir_all`] which includes the file path in the panic message.
#[track_caller]
pub fn remove_dir_all<P: AsRef<Path>>(path: P) {
fs::remove_dir_all(path.as_ref()).expect(&format!(
Expand All @@ -66,7 +66,7 @@ pub fn remove_dir_all<P: AsRef<Path>>(path: P) {
));
}

/// A wrapper around [`std::fs::create_dir`] which includes the file path in the panic message..
/// A wrapper around [`std::fs::create_dir`] which includes the file path in the panic message.
#[track_caller]
pub fn create_dir<P: AsRef<Path>>(path: P) {
fs::create_dir(path.as_ref()).expect(&format!(
Expand All @@ -75,7 +75,7 @@ pub fn create_dir<P: AsRef<Path>>(path: P) {
));
}

/// A wrapper around [`std::fs::create_dir_all`] which includes the file path in the panic message..
/// A wrapper around [`std::fs::create_dir_all`] which includes the file path in the panic message.
#[track_caller]
pub fn create_dir_all<P: AsRef<Path>>(path: P) {
fs::create_dir_all(path.as_ref()).expect(&format!(
Expand All @@ -84,7 +84,7 @@ pub fn create_dir_all<P: AsRef<Path>>(path: P) {
));
}

/// A wrapper around [`std::fs::metadata`] which includes the file path in the panic message..
/// A wrapper around [`std::fs::metadata`] which includes the file path in the panic message.
#[track_caller]
pub fn metadata<P: AsRef<Path>>(path: P) -> fs::Metadata {
fs::metadata(path.as_ref()).expect(&format!(
Expand Down
14 changes: 14 additions & 0 deletions src/tools/run-make-support/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -303,6 +303,20 @@ pub fn filename_not_in_denylist<P: AsRef<Path>, V: AsRef<[String]>>(path: P, exp
.is_some_and(|name| !expected.contains(&name.to_str().unwrap().to_owned()))
}

/// Gathers all files in the current working directory that have the extension `ext`, and counts
/// the number of lines within that contain a match with the regex pattern `re`.
pub fn count_regex_matches_in_files_with_extension(re: &regex::Regex, ext: &str) -> usize {
let fetched_files = shallow_find_files(cwd(), |path| has_extension(path, ext));

let mut count = 0;
for file in fetched_files {
let content = fs_wrapper::read_to_string(file);
count += content.lines().filter(|line| re.is_match(&line)).count();
}

count
}

/// Use `cygpath -w` on a path to get a Windows path string back. This assumes that `cygpath` is
/// available on the platform!
#[track_caller]
Expand Down
7 changes: 0 additions & 7 deletions src/tools/tidy/src/allowed_run_make_makefiles.txt
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,6 @@ run-make/foreign-rust-exceptions/Makefile
run-make/incr-add-rust-src-component/Makefile
run-make/incr-foreign-head-span/Makefile
run-make/interdependent-c-libraries/Makefile
run-make/intrinsic-unreachable/Makefile
run-make/issue-107094/Makefile
run-make/issue-109934-lto-debuginfo/Makefile
run-make/issue-14698/Makefile
Expand Down Expand Up @@ -130,9 +129,6 @@ run-make/rustc-macro-dep-files/Makefile
run-make/sanitizer-cdylib-link/Makefile
run-make/sanitizer-dylib-link/Makefile
run-make/sanitizer-staticlib-link/Makefile
run-make/sepcomp-cci-copies/Makefile
run-make/sepcomp-inlining/Makefile
run-make/sepcomp-separate/Makefile
run-make/share-generics-dylib/Makefile
run-make/silly-file-names/Makefile
run-make/simd-ffi/Makefile
Expand All @@ -147,9 +143,6 @@ run-make/symbol-mangling-hashed/Makefile
run-make/symbol-visibility/Makefile
run-make/symbols-include-type-name/Makefile
run-make/sysroot-crates-are-unstable/Makefile
run-make/target-cpu-native/Makefile
run-make/target-specs/Makefile
run-make/target-without-atomic-cas/Makefile
run-make/test-benches/Makefile
run-make/thumb-none-cortex-m/Makefile
run-make/thumb-none-qemu/Makefile
Expand Down
12 changes: 0 additions & 12 deletions tests/run-make/intrinsic-unreachable/Makefile

This file was deleted.

20 changes: 20 additions & 0 deletions tests/run-make/intrinsic-unreachable/rmake.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// intrinsics::unreachable tells the compiler that a certain point in the code
// is not reachable by any means, which enables some useful optimizations.
// In this test, exit-unreachable contains this instruction and exit-ret does not,
// which means the emitted artifacts should be shorter in length.
// See https://github.com/rust-lang/rust/pull/16970

//@ needs-asm-support
//@ ignore-windows
// Reason: Because of Windows exception handling, the code is not necessarily any shorter.

use run_make_support::{fs_wrapper, rustc};

fn main() {
rustc().opt().emit("asm").input("exit-ret.rs").run();
rustc().opt().emit("asm").input("exit-unreachable.rs").run();
assert!(
fs_wrapper::read_to_string("exit-unreachable.s").lines().count()
< fs_wrapper::read_to_string("exit-ret.s").lines().count()
);
}
22 changes: 8 additions & 14 deletions tests/run-make/llvm-ident/rmake.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@
//@ ignore-cross-compile

use run_make_support::llvm::llvm_bin_dir;
use run_make_support::{cmd, env_var, llvm_filecheck, read_dir, rustc, source_root};

use std::ffi::OsStr;
use run_make_support::{
cmd, env_var, has_extension, llvm_filecheck, rustc, shallow_find_files, source_root,
};

fn main() {
// `-Ccodegen-units=16 -Copt-level=2` is used here to trigger thin LTO
Expand All @@ -22,20 +22,14 @@ fn main() {

// `llvm-dis` is used here since `--emit=llvm-ir` does not emit LLVM IR
// for temporary outputs.
let mut files = Vec::new();
read_dir(".", |path| {
if path.is_file() && path.extension().is_some_and(|ext| ext == OsStr::new("bc")) {
files.push(path.to_path_buf());
}
});
let files = shallow_find_files(".", |path| has_extension(path, "bc"));
cmd(llvm_bin_dir().join("llvm-dis")).args(files).run();

// Check LLVM IR files (including temporary outputs) have `!llvm.ident`
// named metadata, reusing the related codegen test.
let llvm_ident_path = source_root().join("tests/codegen/llvm-ident.rs");
read_dir(".", |path| {
if path.is_file() && path.extension().is_some_and(|ext| ext == OsStr::new("ll")) {
llvm_filecheck().input_file(path).arg(&llvm_ident_path).run();
}
});
let files = shallow_find_files(".", |path| has_extension(path, "ll"));
for file in files {
llvm_filecheck().input_file(file).arg(&llvm_ident_path).run();
}
}
12 changes: 0 additions & 12 deletions tests/run-make/sepcomp-cci-copies/Makefile

This file was deleted.

17 changes: 17 additions & 0 deletions tests/run-make/sepcomp-cci-copies/rmake.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
// Check that cross-crate inlined items are inlined in all compilation units
// that refer to them, and not in any other compilation units.
// Note that we have to pass `-C codegen-units=6` because up to two CGUs may be
// created for each source module (see `rustc_const_eval::monomorphize::partitioning`).
// See https://github.com/rust-lang/rust/pull/16367

use run_make_support::{
count_regex_matches_in_files_with_extension, cwd, fs_wrapper, has_extension, regex, rustc,
shallow_find_files,
};

fn main() {
rustc().input("cci_lib.rs").run();
rustc().input("foo.rs").emit("llvm-ir").codegen_units(6).arg("-Zinline-in-all-cgus").run();
let re = regex::Regex::new(r#"define\ .*cci_fn"#).unwrap();
assert_eq!(count_regex_matches_in_files_with_extension(&re, "ll"), 2);
}
15 changes: 0 additions & 15 deletions tests/run-make/sepcomp-inlining/Makefile

This file was deleted.

Loading
Loading