Skip to content
Closed
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
b368dcb
unconditionally allow shadow call-stack for AArch64 whenever fixed-x1…
dingxiangfei2009 Jul 29, 2024
55e9810
compiletest: implement `needs-lvm-zstd` directive
lqd Aug 10, 2024
c5d9110
mark `rust-lld-compress-debug-sections` test as needing zstd
lqd Aug 10, 2024
2d4af9f
make `compressed-debuginfo` test about zlib only
lqd Aug 10, 2024
2bcbfdb
prepare test for expanding scope
lqd Aug 10, 2024
0728264
expand zstd debuginfo compression test
lqd Aug 10, 2024
e42e5c2
move and rename zstd script
lqd Aug 10, 2024
323bcdc
strip whitespace for ignored tests reason comments
lqd Aug 11, 2024
f27f3a3
enable `llvm.libzstd` on test x64 linux builder
lqd Aug 11, 2024
1e1d839
Fix projections when parent capture is by-ref
compiler-errors Aug 14, 2024
f264e5d
Remove redundant type ops
compiler-errors Aug 14, 2024
bb84372
rust-analyzer: use in-tree pattern_analysis crate
Nadrieril Aug 10, 2024
4290943
Infer async closure args from Fn bound even if there is no correspond…
compiler-errors Aug 14, 2024
9028b53
rustdoc-json: Use FxHashMap from rustdoc_json_types
aDotInTheVoid Aug 15, 2024
a19a8f8
Remove duplicated `Rustdoc::output` method from `run-make-support` lib
GuillaumeGomez Aug 15, 2024
d562a4a
About rmake tests
GuillaumeGomez Aug 15, 2024
64ef806
Rollup merge of #128348 - dingxiangfei2009:allow-shadow-call-stack-sa…
GuillaumeGomez Aug 15, 2024
41b7f98
Rollup merge of #128922 - Nadrieril:r-a-use-upstream-pat-ana, r=Veykril
GuillaumeGomez Aug 15, 2024
d8f7b52
Rollup merge of #128935 - lqd:needs-zstd, r=Kobzol
GuillaumeGomez Aug 15, 2024
6467cc9
Rollup merge of #129072 - compiler-errors:more-powerful-async-closure…
GuillaumeGomez Aug 15, 2024
8832e00
Rollup merge of #129101 - compiler-errors:deref-on-parent-by-ref, r=lcnr
GuillaumeGomez Aug 15, 2024
ce5775c
Rollup merge of #129106 - compiler-errors:unused-type-ops, r=jieyouxu
GuillaumeGomez Aug 15, 2024
efb59ed
Rollup merge of #129122 - GuillaumeGomez:remove-duplicated-rustdoc-ou…
GuillaumeGomez Aug 15, 2024
38c9f38
Rollup merge of #129124 - aDotInTheVoid:rdj-hashmap-3, r=GuillaumeGomez
GuillaumeGomez Aug 15, 2024
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
45 changes: 34 additions & 11 deletions compiler/rustc_hir_typeck/src/closure.rs
Original file line number Diff line number Diff line change
@@ -14,7 +14,7 @@ use rustc_middle::span_bug;
use rustc_middle::ty::visit::{TypeVisitable, TypeVisitableExt};
use rustc_middle::ty::{self, GenericArgs, Ty, TyCtxt, TypeSuperVisitable, TypeVisitor};
use rustc_span::def_id::LocalDefId;
use rustc_span::Span;
use rustc_span::{Span, DUMMY_SP};
use rustc_target::spec::abi::Abi;
use rustc_trait_selection::error_reporting::traits::ArgKind;
use rustc_trait_selection::traits;
@@ -539,6 +539,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
/// 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.
///
/// This function is actually best-effort with the return type; if we don't find a
/// `Future` projection, we still will return arguments that we extracted from the `FnOnce`
/// projection, and the output will be an unconstrained type variable instead.
fn extract_sig_from_projection_and_future_bound(
&self,
cause_span: Option<Span>,
@@ -564,24 +568,43 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
};

// FIXME: We may want to elaborate here, though I assume this will be exceedingly rare.
let mut return_ty = None;
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 });
return_ty = Some(ret_projection.term.expect_type());
break;
}
}

None
// SUBTLE: If we didn't find a `Future<Output = ...>` bound for the return
// vid, we still want to attempt to provide inference guidance for the async
// closure's arguments. Instantiate a new vid to plug into the output type.
//
// You may be wondering, what if it's higher-ranked? Well, given that we
// found a type variable for the `FnOnce::Output` projection above, we know
// that the output can't mention any of the vars.
//
// Also note that we use a fresh var here for the signature since the signature
// records the output of the *future*, and `return_vid` above is the type
// variable of the future, not its output.
//
// FIXME: We probably should store this signature inference output in a way
// that does not misuse a `FnSig` type, but that can be done separately.
let return_ty =
return_ty.unwrap_or_else(|| self.next_ty_var(cause_span.unwrap_or(DUMMY_SP)));

let sig = projection.rebind(self.tcx.mk_fn_sig(
input_tys,
return_ty,
false,
hir::Safety::Safe,
Abi::Rust,
));

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

fn sig_of_closure(
27 changes: 3 additions & 24 deletions compiler/rustc_middle/src/query/mod.rs
Original file line number Diff line number Diff line change
@@ -65,10 +65,9 @@ use crate::query::plumbing::{
};
use crate::traits::query::{
CanonicalAliasGoal, CanonicalPredicateGoal, CanonicalTyGoal,
CanonicalTypeOpAscribeUserTypeGoal, CanonicalTypeOpEqGoal, CanonicalTypeOpNormalizeGoal,
CanonicalTypeOpProvePredicateGoal, CanonicalTypeOpSubtypeGoal, DropckConstraint,
DropckOutlivesResult, MethodAutoderefStepsResult, NoSolution, NormalizationResult,
OutlivesBound,
CanonicalTypeOpAscribeUserTypeGoal, CanonicalTypeOpNormalizeGoal,
CanonicalTypeOpProvePredicateGoal, DropckConstraint, DropckOutlivesResult,
MethodAutoderefStepsResult, NoSolution, NormalizationResult, OutlivesBound,
};
use crate::traits::{
specialization_graph, CodegenObligationError, EvaluationResult, ImplSource,
@@ -2090,26 +2089,6 @@ rustc_queries! {
desc { "evaluating `type_op_ascribe_user_type` `{:?}`", goal.value.value }
}

/// Do not call this query directly: part of the `Eq` type-op
query type_op_eq(
goal: CanonicalTypeOpEqGoal<'tcx>
) -> Result<
&'tcx Canonical<'tcx, canonical::QueryResponse<'tcx, ()>>,
NoSolution,
> {
desc { "evaluating `type_op_eq` `{:?}`", goal.value.value }
}

/// Do not call this query directly: part of the `Subtype` type-op
query type_op_subtype(
goal: CanonicalTypeOpSubtypeGoal<'tcx>
) -> Result<
&'tcx Canonical<'tcx, canonical::QueryResponse<'tcx, ()>>,
NoSolution,
> {
desc { "evaluating `type_op_subtype` `{:?}`", goal.value.value }
}

/// Do not call this query directly: part of the `ProvePredicate` type-op
query type_op_prove_predicate(
goal: CanonicalTypeOpProvePredicateGoal<'tcx>
54 changes: 40 additions & 14 deletions compiler/rustc_mir_transform/src/coroutine/by_move_body.rs
Original file line number Diff line number Diff line change
@@ -78,6 +78,8 @@ use rustc_middle::mir::{self, dump_mir, MirPass};
use rustc_middle::ty::{self, InstanceKind, Ty, TyCtxt, TypeVisitableExt};
use rustc_target::abi::{FieldIdx, VariantIdx};

use crate::pass_manager::validate_body;

pub struct ByMoveBody;

impl<'tcx> MirPass<'tcx> for ByMoveBody {
@@ -131,20 +133,40 @@ impl<'tcx> MirPass<'tcx> for ByMoveBody {
|(parent_field_idx, parent_capture), (child_field_idx, child_capture)| {
// Store this set of additional projections (fields and derefs).
// We need to re-apply them later.
let child_precise_captures =
&child_capture.place.projections[parent_capture.place.projections.len()..];
let mut child_precise_captures = child_capture.place.projections
[parent_capture.place.projections.len()..]
.to_vec();

// If the parent captures by-move, and the child captures by-ref, then we
// need to peel an additional `deref` off of the body of the child.
let needs_deref = child_capture.is_by_ref() && !parent_capture.is_by_ref();
if needs_deref {
assert_ne!(
coroutine_kind,
ty::ClosureKind::FnOnce,
// If the parent capture is by-ref, then we need to apply an additional
// deref before applying any further projections to this place.
if parent_capture.is_by_ref() {
child_precise_captures.insert(
0,
Projection { ty: parent_capture.place.ty(), kind: ProjectionKind::Deref },
);
}
// If the child capture is by-ref, then we need to apply a "ref"
// projection (i.e. `&`) at the end. But wait! We don't have that
// as a projection kind. So instead, we can apply its dual and
// *peel* a deref off of the place when it shows up in the MIR body.
// Luckily, by construction this is always possible.
let peel_deref = if child_capture.is_by_ref() {
assert!(
parent_capture.is_by_ref() || coroutine_kind != ty::ClosureKind::FnOnce,
"`FnOnce` coroutine-closures return coroutines that capture from \
their body; it will always result in a borrowck error!"
);
}
true
} else {
false
};

// Regarding the behavior above, you may think that it's redundant to both
// insert a deref and then peel a deref if the parent and child are both
// captured by-ref. This would be correct, except for the case where we have
// precise capturing projections, since the inserted deref is to the *beginning*
// and the peeled deref is at the *end*. I cannot seem to actually find a
// case where this happens, though, but let's keep this code flexible.

// Finally, store the type of the parent's captured place. We need
// this when building the field projection in the MIR body later on.
@@ -164,7 +186,7 @@ impl<'tcx> MirPass<'tcx> for ByMoveBody {
(
FieldIdx::from_usize(parent_field_idx + num_args),
parent_capture_ty,
needs_deref,
peel_deref,
child_precise_captures,
),
)
@@ -192,6 +214,10 @@ impl<'tcx> MirPass<'tcx> for ByMoveBody {
let mut by_move_body = body.clone();
MakeByMoveBody { tcx, field_remapping, by_move_coroutine_ty }.visit_body(&mut by_move_body);
dump_mir(tcx, false, "coroutine_by_move", &0, &by_move_body, |_, _| Ok(()));

// Let's just always validate this body.
validate_body(tcx, &mut by_move_body, "Initial coroutine_by_move body".to_string());

// FIXME: use query feeding to generate the body right here and then only store the `DefId` of the new body.
by_move_body.source = mir::MirSource::from_instance(InstanceKind::CoroutineKindShim {
coroutine_def_id: coroutine_def_id.to_def_id(),
@@ -202,7 +228,7 @@ impl<'tcx> MirPass<'tcx> for ByMoveBody {

struct MakeByMoveBody<'tcx> {
tcx: TyCtxt<'tcx>,
field_remapping: UnordMap<FieldIdx, (FieldIdx, Ty<'tcx>, bool, &'tcx [Projection<'tcx>])>,
field_remapping: UnordMap<FieldIdx, (FieldIdx, Ty<'tcx>, bool, Vec<Projection<'tcx>>)>,
by_move_coroutine_ty: Ty<'tcx>,
}

@@ -223,14 +249,14 @@ impl<'tcx> MutVisitor<'tcx> for MakeByMoveBody<'tcx> {
if place.local == ty::CAPTURE_STRUCT_LOCAL
&& let Some((&mir::ProjectionElem::Field(idx, _), projection)) =
place.projection.split_first()
&& let Some(&(remapped_idx, remapped_ty, needs_deref, bridging_projections)) =
&& let Some(&(remapped_idx, remapped_ty, peel_deref, ref bridging_projections)) =
self.field_remapping.get(&idx)
{
// As noted before, if the parent closure captures a field by value, and
// the child captures a field by ref, then for the by-move body we're
// generating, we also are taking that field by value. Peel off a deref,
// since a layer of ref'ing has now become redundant.
let final_projections = if needs_deref {
let final_projections = if peel_deref {
let Some((mir::ProjectionElem::Deref, projection)) = projection.split_first()
else {
bug!(
7 changes: 6 additions & 1 deletion compiler/rustc_session/src/session.rs
Original file line number Diff line number Diff line change
@@ -1188,7 +1188,12 @@ fn validate_commandline_args_with_session_available(sess: &Session) {

// Sanitizers can only be used on platforms that we know have working sanitizer codegen.
let supported_sanitizers = sess.target.options.supported_sanitizers;
let unsupported_sanitizers = sess.opts.unstable_opts.sanitizer - supported_sanitizers;
let mut unsupported_sanitizers = sess.opts.unstable_opts.sanitizer - supported_sanitizers;
// Niche: if `fixed-x18`, or effectively switching on `reserved-x18` flag, is enabled
// we should allow Shadow Call Stack sanitizer.
if sess.opts.unstable_opts.fixed_x18 && sess.target.arch == "aarch64" {
unsupported_sanitizers -= SanitizerSet::SHADOWCALLSTACK;
}
match unsupported_sanitizers.into_iter().count() {
0 => {}
1 => {
33 changes: 0 additions & 33 deletions compiler/rustc_trait_selection/src/traits/query/type_op/eq.rs

This file was deleted.

Original file line number Diff line number Diff line change
@@ -16,12 +16,10 @@ use crate::traits::{ObligationCause, ObligationCtxt};

pub mod ascribe_user_type;
pub mod custom;
pub mod eq;
pub mod implied_outlives_bounds;
pub mod normalize;
pub mod outlives;
pub mod prove_predicate;
pub mod subtype;

pub use rustc_middle::traits::query::type_op::*;

30 changes: 0 additions & 30 deletions compiler/rustc_trait_selection/src/traits/query/type_op/subtype.rs

This file was deleted.

24 changes: 0 additions & 24 deletions compiler/rustc_traits/src/type_op.rs
Original file line number Diff line number Diff line change
@@ -10,18 +10,14 @@ use rustc_trait_selection::traits::query::normalize::QueryNormalizeExt;
use rustc_trait_selection::traits::query::type_op::ascribe_user_type::{
type_op_ascribe_user_type_with_span, AscribeUserType,
};
use rustc_trait_selection::traits::query::type_op::eq::Eq;
use rustc_trait_selection::traits::query::type_op::normalize::Normalize;
use rustc_trait_selection::traits::query::type_op::prove_predicate::ProvePredicate;
use rustc_trait_selection::traits::query::type_op::subtype::Subtype;
use rustc_trait_selection::traits::{Normalized, Obligation, ObligationCause, ObligationCtxt};

pub(crate) fn provide(p: &mut Providers) {
*p = Providers {
type_op_ascribe_user_type,
type_op_eq,
type_op_prove_predicate,
type_op_subtype,
type_op_normalize_ty,
type_op_normalize_clause,
type_op_normalize_fn_sig,
@@ -39,16 +35,6 @@ fn type_op_ascribe_user_type<'tcx>(
})
}

fn type_op_eq<'tcx>(
tcx: TyCtxt<'tcx>,
canonicalized: Canonical<'tcx, ParamEnvAnd<'tcx, Eq<'tcx>>>,
) -> Result<&'tcx Canonical<'tcx, QueryResponse<'tcx, ()>>, NoSolution> {
tcx.infer_ctxt().enter_canonical_trait_query(&canonicalized, |ocx, key| {
let (param_env, Eq { a, b }) = key.into_parts();
Ok(ocx.eq(&ObligationCause::dummy(), param_env, a, b)?)
})
}

fn type_op_normalize<'tcx, T>(
ocx: &ObligationCtxt<'_, 'tcx>,
key: ParamEnvAnd<'tcx, Normalize<T>>,
@@ -91,16 +77,6 @@ fn type_op_normalize_poly_fn_sig<'tcx>(
tcx.infer_ctxt().enter_canonical_trait_query(&canonicalized, type_op_normalize)
}

fn type_op_subtype<'tcx>(
tcx: TyCtxt<'tcx>,
canonicalized: Canonical<'tcx, ParamEnvAnd<'tcx, Subtype<'tcx>>>,
) -> Result<&'tcx Canonical<'tcx, QueryResponse<'tcx, ()>>, NoSolution> {
tcx.infer_ctxt().enter_canonical_trait_query(&canonicalized, |ocx, key| {
let (param_env, Subtype { sub, sup }) = key.into_parts();
Ok(ocx.sup(&ObligationCause::dummy(), param_env, sup, sub)?)
})
}

fn type_op_prove_predicate<'tcx>(
tcx: TyCtxt<'tcx>,
canonicalized: Canonical<'tcx, ParamEnvAnd<'tcx, ProvePredicate<'tcx>>>,
6 changes: 3 additions & 3 deletions src/ci/docker/host-x86_64/dist-x86_64-linux/Dockerfile
Original file line number Diff line number Diff line change
@@ -62,9 +62,9 @@ COPY host-x86_64/dist-x86_64-linux/build-clang.sh /tmp/
RUN ./build-clang.sh
ENV CC=clang CXX=clang++

# rustc's LLVM needs zstd.
COPY scripts/zstd.sh /tmp/
RUN ./zstd.sh
# Build zstd to enable `llvm.libzstd`.
COPY host-x86_64/dist-x86_64-linux/build-zstd.sh /tmp/
RUN ./build-zstd.sh

COPY scripts/sccache.sh /scripts/
RUN sh /scripts/sccache.sh
File renamed without changes.
3 changes: 2 additions & 1 deletion src/ci/docker/host-x86_64/x86_64-gnu/Dockerfile
Original file line number Diff line number Diff line change
@@ -28,5 +28,6 @@ ENV RUST_CONFIGURE_ARGS \
--build=x86_64-unknown-linux-gnu \
--enable-sanitizers \
--enable-profiler \
--enable-compiler-docs
--enable-compiler-docs \
--set llvm.libzstd=true
ENV SCRIPT python3 ../x.py --stage 2 test
5 changes: 5 additions & 0 deletions src/doc/rustc/src/platform-support/android.md
Original file line number Diff line number Diff line change
@@ -61,3 +61,8 @@ Currently the `riscv64-linux-android` target requires the following architecture
* `Zba` (address calculation instructions)
* `Zbb` (base instructions)
* `Zbs` (single-bit instructions)

### aarch64-linux-android on Nightly compilers

As soon as `-Zfixed-x18` compiler flag is supplied, the [`ShadowCallStack` sanitizer](https://releases.llvm.org/7.0.1/tools/clang/docs/ShadowCallStack.html)
instrumentation is also made avaiable by supplying the second compiler flag `-Zsanitizer=shadow-call-stack`.
7 changes: 6 additions & 1 deletion src/doc/unstable-book/src/compiler-flags/fixed-x18.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# `fixed-x18`

This option prevents the compiler from using the x18 register. It is only
supported on aarch64.
supported on `aarch64`.

From the [ABI spec][arm-abi]:

@@ -23,6 +23,11 @@ Currently, the `-Zsanitizer=shadow-call-stack` flag is only supported on
platforms that always treat x18 as a reserved register, and the `-Zfixed-x18`
flag is not required to use the sanitizer on such platforms. However, the
sanitizer may be supported on targets where this is not the case in the future.
One way to do so now on Nightly compilers is to explicitly supply this `-Zfixed-x18`
flag with `aarch64` targets, so that the sanitizer is available for instrumentation
on targets like `aarch64-unknown-none`, for instance. However, discretion is still
required to make sure that the runtime support is in place for this sanitizer
to be effective.

It is undefined behavior for `-Zsanitizer=shadow-call-stack` code to call into
code where x18 is a temporary register. On the other hand, when you are *not*
4 changes: 4 additions & 0 deletions src/doc/unstable-book/src/compiler-flags/sanitizer.md
Original file line number Diff line number Diff line change
@@ -787,6 +787,10 @@ A runtime must be provided by the application or operating system.
See the [Clang ShadowCallStack documentation][clang-scs] for more details.
* `aarch64-unknown-none`
In addition to support from a runtime by the application or operating system, the `-Zfixed-x18` flag is also mandatory.
# ThreadSanitizer
ThreadSanitizer is a data race detection tool. It is supported on the following
10 changes: 5 additions & 5 deletions src/librustdoc/json/mod.rs
Original file line number Diff line number Diff line change
@@ -13,12 +13,15 @@ use std::io::{stdout, BufWriter, Write};
use std::path::PathBuf;
use std::rc::Rc;

use rustc_data_structures::fx::FxHashMap;
use rustc_hir::def_id::{DefId, DefIdSet};
use rustc_middle::ty::TyCtxt;
use rustc_session::Session;
use rustc_span::def_id::LOCAL_CRATE;
use rustdoc_json_types as types;
// It's important to use the FxHashMap from rustdoc_json_types here, instead of
// the one from rustc_data_structures, as they're different types due to sysroots.
// See #110051 and #127456 for details
use rustdoc_json_types::FxHashMap;

use crate::clean::types::{ExternalCrate, ExternalLocation};
use crate::clean::ItemKind;
@@ -234,14 +237,11 @@ impl<'tcx> FormatRenderer<'tcx> for JsonRenderer<'tcx> {
let index = (*self.index).clone().into_inner();

debug!("Constructing Output");
// This needs to be the default HashMap for compatibility with the public interface for
// rustdoc-json-types
#[allow(rustc::default_hash_types)]
let output = types::Crate {
root: types::Id(format!("0:0:{}", e.name(self.tcx).as_u32())),
crate_version: self.cache.crate_version.clone(),
includes_private: self.cache.document_private,
index: index.into_iter().collect(),
index,
paths: self
.cache
.paths
2 changes: 1 addition & 1 deletion src/rustdoc-json-types/lib.rs
Original file line number Diff line number Diff line change
@@ -5,7 +5,7 @@
use std::path::PathBuf;

use rustc_hash::FxHashMap;
pub use rustc_hash::FxHashMap;
use serde::{Deserialize, Serialize};

/// The version of JSON output that this crate represents.
1 change: 1 addition & 0 deletions src/tools/compiletest/src/command-list.rs
Original file line number Diff line number Diff line change
@@ -138,6 +138,7 @@ const KNOWN_DIRECTIVE_NAMES: &[&str] = &[
"needs-force-clang-based-tests",
"needs-git-hash",
"needs-llvm-components",
"needs-llvm-zstd",
"needs-profiler-support",
"needs-relocation-model-pic",
"needs-run-enabled",
101 changes: 101 additions & 0 deletions src/tools/compiletest/src/header.rs
Original file line number Diff line number Diff line change
@@ -1203,6 +1203,107 @@ pub fn extract_llvm_version_from_binary(binary_path: &str) -> Option<u32> {
None
}

/// For tests using the `needs-llvm-zstd` directive:
/// - for local LLVM builds, try to find the static zstd library in the llvm-config system libs.
/// - for `download-ci-llvm`, see if `lld` was built with zstd support.
pub fn llvm_has_libzstd(config: &Config) -> bool {
// Strategy 1: works for local builds but not with `download-ci-llvm`.
//
// We check whether `llvm-config` returns the zstd library. Bootstrap's `llvm.libzstd` will only
// ask to statically link it when building LLVM, so we only check if the list of system libs
// contains a path to that static lib, and that it exists.
//
// See compiler/rustc_llvm/build.rs for more details and similar expectations.
fn is_zstd_in_config(llvm_bin_dir: &Path) -> Option<()> {
let llvm_config_path = llvm_bin_dir.join("llvm-config");
let output = Command::new(llvm_config_path).arg("--system-libs").output().ok()?;
assert!(output.status.success(), "running llvm-config --system-libs failed");

let libs = String::from_utf8(output.stdout).ok()?;
for lib in libs.split_whitespace() {
if lib.ends_with("libzstd.a") && Path::new(lib).exists() {
return Some(());
}
}

None
}

// Strategy 2: `download-ci-llvm`'s `llvm-config --system-libs` will not return any libs to
// use.
//
// The CI artifacts also don't contain the bootstrap config used to build them: otherwise we
// could have looked at the `llvm.libzstd` config.
//
// We infer whether `LLVM_ENABLE_ZSTD` was used to build LLVM as a byproduct of testing whether
// `lld` supports it. If not, an error will be emitted: "LLVM was not built with
// LLVM_ENABLE_ZSTD or did not find zstd at build time".
#[cfg(unix)]
fn is_lld_built_with_zstd(llvm_bin_dir: &Path) -> Option<()> {
let lld_path = llvm_bin_dir.join("lld");
if lld_path.exists() {
// We can't call `lld` as-is, it expects to be invoked by a compiler driver using a
// different name. Prepare a temporary symlink to do that.
let lld_symlink_path = llvm_bin_dir.join("ld.lld");
if !lld_symlink_path.exists() {
std::os::unix::fs::symlink(lld_path, &lld_symlink_path).ok()?;
}

// Run `lld` with a zstd flag. We expect this command to always error here, we don't
// want to link actual files and don't pass any.
let output = Command::new(&lld_symlink_path)
.arg("--compress-debug-sections=zstd")
.output()
.ok()?;
assert!(!output.status.success());

// Look for a specific error caused by LLVM not being built with zstd support. We could
// also look for the "no input files" message, indicating the zstd flag was accepted.
let stderr = String::from_utf8(output.stderr).ok()?;
let zstd_available = !stderr.contains("LLVM was not built with LLVM_ENABLE_ZSTD");

// We don't particularly need to clean the link up (so the previous commands could fail
// in theory but won't in practice), but we can try.
std::fs::remove_file(lld_symlink_path).ok()?;

if zstd_available {
return Some(());
}
}

None
}

#[cfg(not(unix))]
fn is_lld_built_with_zstd(llvm_bin_dir: &Path) -> Option<()> {
None
}

if let Some(llvm_bin_dir) = &config.llvm_bin_dir {
// Strategy 1: for local LLVM builds.
if is_zstd_in_config(llvm_bin_dir).is_some() {
return true;
}

// Strategy 2: for LLVM artifacts built on CI via `download-ci-llvm`.
//
// It doesn't work for cases where the artifacts don't contain the linker, but it's
// best-effort: CI has `llvm.libzstd` and `lld` enabled on the x64 linux artifacts, so it
// will at least work there.
//
// If this can be improved and expanded to less common cases in the future, it should.
if config.target == "x86_64-unknown-linux-gnu"
&& config.host == config.target
&& is_lld_built_with_zstd(llvm_bin_dir).is_some()
{
return true;
}
}

// Otherwise, all hope is lost.
false
}

/// Takes a directive of the form "<version1> [- <version2>]",
/// returns the numeric representation of <version1> and <version2> as
/// tuple: (<version1> as u32, <version2> as u32)
12 changes: 10 additions & 2 deletions src/tools/compiletest/src/header/needs.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use crate::common::{Config, Debugger, Sanitizer};
use crate::header::IgnoreDecision;
use crate::header::{llvm_has_libzstd, IgnoreDecision};

pub(super) fn handle_needs(
cache: &CachedNeedsConditions,
@@ -149,6 +149,11 @@ pub(super) fn handle_needs(
condition: cache.symlinks,
ignore_reason: "ignored if symlinks are unavailable",
},
Need {
name: "needs-llvm-zstd",
condition: cache.llvm_zstd,
ignore_reason: "ignored if LLVM wasn't build with zstd for ELF section compression",
},
];

let (name, comment) = match ln.split_once([':', ' ']) {
@@ -174,7 +179,7 @@ pub(super) fn handle_needs(
} else {
return IgnoreDecision::Ignore {
reason: if let Some(comment) = comment {
format!("{} ({comment})", need.ignore_reason)
format!("{} ({})", need.ignore_reason, comment.trim())
} else {
need.ignore_reason.into()
},
@@ -215,6 +220,8 @@ pub(super) struct CachedNeedsConditions {
rust_lld: bool,
dlltool: bool,
symlinks: bool,
/// Whether LLVM built with zstd, for the `needs-llvm-zstd` directive.
llvm_zstd: bool,
}

impl CachedNeedsConditions {
@@ -258,6 +265,7 @@ impl CachedNeedsConditions {
.join(if config.host.contains("windows") { "rust-lld.exe" } else { "rust-lld" })
.exists(),

llvm_zstd: llvm_has_libzstd(&config),
dlltool: find_dlltool(&config),
symlinks: has_symlinks(),
}
8 changes: 1 addition & 7 deletions src/tools/run-make-support/src/external_deps/rustdoc.rs
Original file line number Diff line number Diff line change
@@ -71,14 +71,8 @@ impl Rustdoc {
self
}

/// Specify path to the output folder.
pub fn output<P: AsRef<Path>>(&mut self, path: P) -> &mut Self {
self.cmd.arg("-o");
self.cmd.arg(path.as_ref());
self
}

/// Specify output directory.
#[doc(alias = "output")]
pub fn out_dir<P: AsRef<Path>>(&mut self, path: P) -> &mut Self {
self.cmd.arg("--out-dir").arg(path.as_ref());
self
6 changes: 4 additions & 2 deletions src/tools/rust-analyzer/crates/hir-ty/src/lib.rs
Original file line number Diff line number Diff line change
@@ -15,8 +15,10 @@ extern crate rustc_abi;
#[cfg(not(feature = "in-rust-tree"))]
extern crate ra_ap_rustc_abi as rustc_abi;

// Use the crates.io version unconditionally until the API settles enough that we can switch to
// using the in-tree one.
#[cfg(feature = "in-rust-tree")]
extern crate rustc_pattern_analysis;

#[cfg(not(feature = "in-rust-tree"))]
extern crate ra_ap_rustc_pattern_analysis as rustc_pattern_analysis;

mod builder;
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
//@ revisions: aarch64 android
//@[aarch64] compile-flags: --target aarch64-unknown-none -Zfixed-x18 -Zsanitizer=shadow-call-stack
//@[aarch64] needs-llvm-components: aarch64
//@[android] compile-flags: --target aarch64-linux-android -Zsanitizer=shadow-call-stack
//@[android] needs-llvm-components: aarch64

#![allow(internal_features)]
#![crate_type = "rlib"]
#![feature(no_core, lang_items)]
#![no_core]

#[lang = "sized"]
trait Sized {}

// CHECK: ; Function Attrs:{{.*}}shadowcallstack
#[no_mangle]
pub fn foo() {}

// CHECK: attributes #0 = {{.*}}shadowcallstack{{.*}}
42 changes: 42 additions & 0 deletions tests/run-make/compressed-debuginfo-zstd/rmake.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
// Checks debuginfo compression both for the always-enabled zlib, and when the optional zstd is
// enabled:
// - via rustc's `debuginfo-compression`,
// - and via rust-lld's `compress-debug-sections`

//@ needs-llvm-zstd: we want LLVM/LLD to be built with zstd support
//@ needs-rust-lld: the system linker will most likely not support zstd
//@ only-linux
//@ ignore-cross-compile

use run_make_support::{llvm_readobj, run_in_tmpdir, Rustc};

fn check_compression(compression: &str, to_find: &str) {
// check compressed debug sections via rustc flag
prepare_and_check(to_find, |rustc| {
rustc.arg(&format!("-Zdebuginfo-compression={compression}"))
});

// check compressed debug sections via rust-lld flag
prepare_and_check(to_find, |rustc| {
rustc.link_arg(&format!("-Wl,--compress-debug-sections={compression}"))
});
}

fn prepare_and_check<F: FnOnce(&mut Rustc) -> &mut Rustc>(to_find: &str, prepare_rustc: F) {
run_in_tmpdir(|| {
let mut rustc = Rustc::new();
rustc
.arg("-Zlinker-features=+lld")
.arg("-Clink-self-contained=+linker")
.arg("-Zunstable-options")
.arg("-Cdebuginfo=full")
.input("main.rs");
prepare_rustc(&mut rustc).run();
llvm_readobj().arg("-t").arg("main").run().assert_stdout_contains(to_find);
});
}

fn main() {
check_compression("zlib", "ZLIB");
check_compression("zstd", "ZSTD");
}
17 changes: 3 additions & 14 deletions tests/run-make/compressed-debuginfo/rmake.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
// Checks the `debuginfo-compression` option.
// Checks the always enabled `debuginfo-compression` option: zlib.

//@ only-linux
//@ ignore-cross-compile

// FIXME: This test isn't comprehensive and isn't covering all possible combinations.

use run_make_support::{assert_contains, llvm_readobj, run_in_tmpdir, rustc};
use run_make_support::{llvm_readobj, run_in_tmpdir, rustc};

fn check_compression(compression: &str, to_find: &str) {
run_in_tmpdir(|| {
@@ -17,19 +15,10 @@ fn check_compression(compression: &str, to_find: &str) {
.arg(&format!("-Zdebuginfo-compression={compression}"))
.input("foo.rs")
.run();
let stderr = out.stderr_utf8();
if stderr.is_empty() {
llvm_readobj().arg("-t").arg("foo.o").run().assert_stdout_contains(to_find);
} else {
assert_contains(
stderr,
format!("unknown debuginfo compression algorithm {compression}"),
);
}
llvm_readobj().arg("-t").arg("foo.o").run().assert_stdout_contains(to_find);
});
}

fn main() {
check_compression("zlib", "ZLIB");
check_compression("zstd", "ZSTD");
}
2 changes: 1 addition & 1 deletion tests/run-make/deref-impl-rustdoc-ice/rmake.rs
Original file line number Diff line number Diff line change
@@ -12,5 +12,5 @@ use run_make_support::{cwd, rustc, rustdoc};
fn main() {
rustc().input("foo.rs").run();
rustc().input("bar.rs").run();
rustdoc().input("baz.rs").library_search_path(cwd()).output(cwd()).run();
rustdoc().input("baz.rs").library_search_path(cwd()).out_dir(cwd()).run();
}
6 changes: 3 additions & 3 deletions tests/run-make/emit-shared-files/rmake.rs
Original file line number Diff line number Diff line change
@@ -13,7 +13,7 @@ fn main() {
rustdoc()
.arg("-Zunstable-options")
.arg("--emit=invocation-specific")
.output("invocation-only")
.out_dir("invocation-only")
.arg("--resource-suffix=-xxx")
.args(&["--theme", "y.css"])
.args(&["--extend-css", "z.css"])
@@ -34,7 +34,7 @@ fn main() {
rustdoc()
.arg("-Zunstable-options")
.arg("--emit=toolchain-shared-resources")
.output("toolchain-only")
.out_dir("toolchain-only")
.arg("--resource-suffix=-xxx")
.args(&["--extend-css", "z.css"])
.input("x.rs")
@@ -68,7 +68,7 @@ fn main() {
rustdoc()
.arg("-Zunstable-options")
.arg("--emit=toolchain-shared-resources,unversioned-shared-resources")
.output("all-shared")
.out_dir("all-shared")
.arg("--resource-suffix=-xxx")
.args(&["--extend-css", "z.css"])
.input("x.rs")
2 changes: 1 addition & 1 deletion tests/run-make/exit-code/rmake.rs
Original file line number Diff line number Diff line change
@@ -16,7 +16,7 @@ fn main() {
.run_fail()
.assert_exit_code(101);

rustdoc().arg("success.rs").output("exit-code").run();
rustdoc().arg("success.rs").out_dir("exit-code").run();

rustdoc().arg("--invalid-arg-foo").run_fail().assert_exit_code(1);

39 changes: 0 additions & 39 deletions tests/run-make/rust-lld-compress-debug-sections/rmake.rs

This file was deleted.

8 changes: 4 additions & 4 deletions tests/run-make/rustdoc-determinism/rmake.rs
Original file line number Diff line number Diff line change
@@ -7,12 +7,12 @@ use run_make_support::{diff, rustdoc};

fn main() {
let foo_first = Path::new("foo_first");
rustdoc().input("foo.rs").output(&foo_first).run();
rustdoc().input("bar.rs").output(&foo_first).run();
rustdoc().input("foo.rs").out_dir(&foo_first).run();
rustdoc().input("bar.rs").out_dir(&foo_first).run();

let bar_first = Path::new("bar_first");
rustdoc().input("bar.rs").output(&bar_first).run();
rustdoc().input("foo.rs").output(&bar_first).run();
rustdoc().input("bar.rs").out_dir(&bar_first).run();
rustdoc().input("foo.rs").out_dir(&bar_first).run();

diff()
.expected_file(foo_first.join("search-index.js"))
2 changes: 1 addition & 1 deletion tests/run-make/rustdoc-io-error/rmake.rs
Original file line number Diff line number Diff line change
@@ -25,7 +25,7 @@ fn main() {
permissions.set_readonly(true);
rfs::set_permissions(&out_dir, permissions);

let output = rustdoc().input("foo.rs").output(&out_dir).env("RUST_BACKTRACE", "1").run_fail();
let output = rustdoc().input("foo.rs").out_dir(&out_dir).env("RUST_BACKTRACE", "1").run_fail();

rfs::set_permissions(&out_dir, original_permissions);

2 changes: 1 addition & 1 deletion tests/run-make/rustdoc-map-file/rmake.rs
Original file line number Diff line number Diff line change
@@ -6,7 +6,7 @@ fn main() {
.input("foo.rs")
.arg("-Zunstable-options")
.arg("--generate-redirect-map")
.output(&out_dir)
.out_dir(&out_dir)
.run();
// FIXME (GuillaumeGomez): Port the python script to Rust as well.
python_command().arg("validate_json.py").arg(&out_dir).run();
2 changes: 1 addition & 1 deletion tests/run-make/rustdoc-output-path/rmake.rs
Original file line number Diff line number Diff line change
@@ -6,6 +6,6 @@ use run_make_support::rustdoc;

fn main() {
let out_dir = Path::new("foo/bar/doc");
rustdoc().input("foo.rs").output(&out_dir).run();
rustdoc().input("foo.rs").out_dir(&out_dir).run();
assert!(out_dir.exists());
}
2 changes: 1 addition & 1 deletion tests/run-make/rustdoc-output-stdout/rmake.rs
Original file line number Diff line number Diff line change
@@ -10,7 +10,7 @@ fn main() {
// First we check that we generate the JSON in the stdout.
rustdoc()
.input("foo.rs")
.output("-")
.out_dir("-")
.arg("-Zunstable-options")
.output_format("json")
.run()
4 changes: 2 additions & 2 deletions tests/run-make/rustdoc-scrape-examples-macros/rmake.rs
Original file line number Diff line number Diff line change
@@ -35,7 +35,7 @@ fn main() {
.input("examples/ex.rs")
.crate_name("ex")
.crate_type("bin")
.output(&out_dir)
.out_dir(&out_dir)
.extern_(crate_name, rust_lib_name(crate_name))
.extern_(proc_crate_name, dylib_name.trim())
.arg("-Zunstable-options")
@@ -49,7 +49,7 @@ fn main() {
.input("src/lib.rs")
.crate_name(crate_name)
.crate_type("lib")
.output(&out_dir)
.out_dir(&out_dir)
.arg("-Zunstable-options")
.arg("--with-examples")
.arg(&ex_dir)
4 changes: 2 additions & 2 deletions tests/run-make/rustdoc-scrape-examples-remap/scrape.rs
Original file line number Diff line number Diff line change
@@ -20,7 +20,7 @@ pub fn scrape(extra_args: &[&str]) {
.input(&dep)
.crate_name(&dep_stem)
.crate_type("bin")
.output(&out_dir)
.out_dir(&out_dir)
.extern_(crate_name, format!("lib{crate_name}.rmeta"))
.arg("-Zunstable-options")
.arg("--scrape-examples-output-path")
@@ -35,7 +35,7 @@ pub fn scrape(extra_args: &[&str]) {
let mut rustdoc = rustdoc();
rustdoc
.input("src/lib.rs")
.output(&out_dir)
.out_dir(&out_dir)
.crate_name(crate_name)
.crate_type("lib")
.arg("-Zunstable-options");
2 changes: 1 addition & 1 deletion tests/run-make/rustdoc-target-spec-json-path/rmake.rs
Original file line number Diff line number Diff line change
@@ -7,7 +7,7 @@ fn main() {
rustc().crate_type("lib").input("dummy_core.rs").target("target.json").run();
rustdoc()
.input("my_crate.rs")
.output(out_dir)
.out_dir(out_dir)
.library_search_path(cwd())
.target("target.json")
.run();
2 changes: 1 addition & 1 deletion tests/run-make/rustdoc-themes/rmake.rs
Original file line number Diff line number Diff line change
@@ -27,6 +27,6 @@ fn main() {
rfs::create_dir_all(&out_dir);
rfs::write(&test_css, test_content);

rustdoc().output(&out_dir).input("foo.rs").arg("--theme").arg(&test_css).run();
rustdoc().out_dir(&out_dir).input("foo.rs").arg("--theme").arg(&test_css).run();
htmldocck().arg(out_dir).arg("foo.rs").run();
}
2 changes: 1 addition & 1 deletion tests/run-make/rustdoc-with-out-dir-option/rmake.rs
Original file line number Diff line number Diff line change
@@ -2,6 +2,6 @@ use run_make_support::{htmldocck, rustdoc};

fn main() {
let out_dir = "rustdoc";
rustdoc().input("src/lib.rs").crate_name("foobar").crate_type("lib").output(&out_dir).run();
rustdoc().input("src/lib.rs").crate_name("foobar").crate_type("lib").out_dir(&out_dir).run();
htmldocck().arg(out_dir).arg("src/lib.rs").run();
}
15 changes: 15 additions & 0 deletions tests/ui/abi/shadow-call-stack-without-fixed-x18.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
//@ compile-flags: --target aarch64-unknown-none -Zsanitizer=shadow-call-stack
//@ error-pattern: shadow-call-stack sanitizer is not supported for this target
//@ dont-check-compiler-stderr
//@ needs-llvm-components: aarch64

#![allow(internal_features)]
#![crate_type = "rlib"]
#![feature(no_core, lang_items)]
#![no_core]

#[lang = "sized"]
trait Sized {}

#[no_mangle]
pub fn foo() {}
16 changes: 16 additions & 0 deletions tests/ui/async-await/async-closures/move-out-of-ref.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
//@ compile-flags: -Zvalidate-mir
//@ edition: 2021

#![feature(async_closure)]

// NOT copy.
struct Ty;

fn hello(x: &Ty) {
let c = async || {
*x;
//~^ ERROR cannot move out of `*x` which is behind a shared reference
};
}

fn main() {}
18 changes: 18 additions & 0 deletions tests/ui/async-await/async-closures/move-out-of-ref.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
error[E0507]: cannot move out of `*x` which is behind a shared reference
--> $DIR/move-out-of-ref.rs:11:9
|
LL | *x;
| ^^ move occurs because `*x` has type `Ty`, which does not implement the `Copy` trait
|
note: if `Ty` implemented `Clone`, you could clone the value
--> $DIR/move-out-of-ref.rs:7:1
|
LL | struct Ty;
| ^^^^^^^^^ consider implementing `Clone` for this type
...
LL | *x;
| -- you could clone this value

error: aborting due to 1 previous error

For more information about this error, try `rustc --explain E0507`.
49 changes: 49 additions & 0 deletions tests/ui/async-await/async-closures/sig-from-bare-fn.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
//@ check-pass
//@ edition: 2021

// Make sure that we infer the args of an async closure even if it's passed to
// a function that requires the async closure implement `Fn*` but does *not* have
// a `Future` bound on the return type.

#![feature(async_closure)]

use std::future::Future;

trait TryStream {
type Ok;
type Err;
}

trait TryFuture {
type Ok;
type Err;
}

impl<F, T, E> TryFuture for F where F: Future<Output = Result<T, E>> {
type Ok = T;
type Err = E;
}

trait TryStreamExt: TryStream {
fn try_for_each<F, Fut>(&self, f: F)
where
F: FnMut(Self::Ok) -> Fut,
Fut: TryFuture<Ok = (), Err = Self::Err>;
}

impl<S> TryStreamExt for S where S: TryStream {
fn try_for_each<F, Fut>(&self, f: F)
where
F: FnMut(Self::Ok) -> Fut,
Fut: TryFuture<Ok = (), Err = Self::Err>,
{ }
}

fn test(stream: impl TryStream<Ok = &'static str, Err = ()>) {
stream.try_for_each(async |s| {
s.trim(); // Make sure we know the type of `s` at this point.
Ok(())
});
}

fn main() {}