Skip to content

Commit

Permalink
Auto merge of #135592 - matthiaskrgr:rollup-4t69l7i, r=matthiaskrgr
Browse files Browse the repository at this point in the history
Rollup of 7 pull requests

Successful merges:

 - #134754 (Implement `use` associated items of traits)
 - #135481 (coverage: Completely overhaul counter assignment, using node-flow graphs)
 - #135504 (Allow coercing safe-to-call target_feature functions to safe fn pointers)
 - #135561 (Update docs for `-Clink-dead-code` to discourage its use)
 - #135574 (ci: mirror ubuntu:22.04 to ghcr.io)
 - #135585 (resolve symlinks of LLVM tool binaries before copying them)
 - #135588 (Add license-metadata.json to rustc-src tarball.)

r? `@ghost`
`@rustbot` modify labels: rollup
  • Loading branch information
bors committed Jan 16, 2025
2 parents 99db273 + f4bbe30 commit 76a030a
Show file tree
Hide file tree
Showing 91 changed files with 2,607 additions and 2,046 deletions.
57 changes: 57 additions & 0 deletions .github/workflows/ghcr.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
# Mirror DockerHub images used by the Rust project to ghcr.io.
# Images are available at /~https://github.com/orgs/rust-lang/packages.
#
# In some CI jobs, we pull images from ghcr.io instead of Docker Hub because
# Docker Hub has a rate limit, while ghcr.io doesn't.
# Those images are pushed to ghcr.io by this job.
#
# Note that authenticating to DockerHub or other registries isn't possible
# for PR jobs, because forks can't access secrets.
# That's why we use ghcr.io: it has no rate limit and it doesn't require authentication.

name: GHCR

on:
workflow_dispatch:
schedule:
# Run daily at midnight UTC
- cron: '0 0 * * *'

jobs:
mirror:
name: DockerHub mirror
runs-on: ubuntu-24.04
if: github.repository == 'rust-lang/rust'
permissions:
# Needed to write to the ghcr.io registry
packages: write
steps:
- uses: actions/checkout@v4
with:
persist-credentials: false

- name: Log in to registry
run: echo "${{ secrets.GITHUB_TOKEN }}" | docker login ghcr.io -u ${{ github.repository_owner }} --password-stdin

# Download crane in the current directory.
# We use crane because it copies the docker image for all the architectures available in
# DockerHub for the image.
# Learn more about crane at
# /~https://github.com/google/go-containerregistry/blob/main/cmd/crane/README.md
- name: Download crane
run: |
curl -sL "/~https://github.com/google/go-containerregistry/releases/download/${VERSION}/go-containerregistry_${OS}_${ARCH}.tar.gz" | tar -xzf -
env:
VERSION: v0.20.2
OS: Linux
ARCH: x86_64

- name: Mirror DockerHub
run: |
# DockerHub image we want to mirror
image="ubuntu:22.04"
# Mirror image from DockerHub to ghcr.io
./crane copy \
docker.io/${image} \
ghcr.io/${{ github.repository_owner }}/${image}
15 changes: 14 additions & 1 deletion compiler/rustc_borrowck/src/type_check/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1654,7 +1654,20 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
match *cast_kind {
CastKind::PointerCoercion(PointerCoercion::ReifyFnPointer, coercion_source) => {
let is_implicit_coercion = coercion_source == CoercionSource::Implicit;
let src_sig = op.ty(body, tcx).fn_sig(tcx);
let src_ty = op.ty(body, tcx);
let mut src_sig = src_ty.fn_sig(tcx);
if let ty::FnDef(def_id, _) = src_ty.kind()
&& let ty::FnPtr(_, target_hdr) = *ty.kind()
&& tcx.codegen_fn_attrs(def_id).safe_target_features
&& target_hdr.safety.is_safe()
&& let Some(safe_sig) = tcx.adjust_target_feature_sig(
*def_id,
src_sig,
body.source.def_id(),
)
{
src_sig = safe_sig;
}

// HACK: This shouldn't be necessary... We can remove this when we actually
// get binders with where clauses, then elaborate implied bounds into that
Expand Down
10 changes: 10 additions & 0 deletions compiler/rustc_data_structures/src/graph/iterate/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,16 @@ where
pub fn visited(&self, node: G::Node) -> bool {
self.visited.contains(node)
}

/// Returns a reference to the set of nodes that have been visited, with
/// the same caveats as [`Self::visited`].
///
/// When incorporating the visited nodes into another bitset, using bulk
/// operations like `union` or `intersect` can be more efficient than
/// processing each node individually.
pub fn visited_set(&self) -> &DenseBitSet<G::Node> {
&self.visited
}
}

impl<G> std::fmt::Debug for DepthFirstSearch<G>
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_data_structures/src/graph/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ pub mod dominators;
pub mod implementation;
pub mod iterate;
mod reference;
pub mod reversed;
pub mod scc;
pub mod vec_graph;

Expand Down
42 changes: 42 additions & 0 deletions compiler/rustc_data_structures/src/graph/reversed.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
use crate::graph::{DirectedGraph, Predecessors, Successors};

/// View that reverses the direction of edges in its underlying graph, so that
/// successors become predecessors and vice-versa.
///
/// Because of `impl<G: Graph> Graph for &G`, the underlying graph can be
/// wrapped by-reference instead of by-value if desired.
#[derive(Clone, Copy, Debug)]
pub struct ReversedGraph<G> {
pub inner: G,
}

impl<G> ReversedGraph<G> {
pub fn new(inner: G) -> Self {
Self { inner }
}
}

impl<G: DirectedGraph> DirectedGraph for ReversedGraph<G> {
type Node = G::Node;

fn num_nodes(&self) -> usize {
self.inner.num_nodes()
}
}

// Implementing `StartNode` is not possible in general, because the start node
// of an underlying graph is instead an _end_ node in the reversed graph.
// But would be possible to define another wrapper type that adds an explicit
// start node to its underlying graph, if desired.

impl<G: Predecessors> Successors for ReversedGraph<G> {
fn successors(&self, node: Self::Node) -> impl Iterator<Item = Self::Node> {
self.inner.predecessors(node)
}
}

impl<G: Successors> Predecessors for ReversedGraph<G> {
fn predecessors(&self, node: Self::Node) -> impl Iterator<Item = Self::Node> {
self.inner.successors(node)
}
}
12 changes: 6 additions & 6 deletions compiler/rustc_error_codes/src/error_codes/E0253.md
Original file line number Diff line number Diff line change
@@ -1,19 +1,19 @@
Attempt was made to import an unimportable value. This can happen when trying
to import a method from a trait.
Attempt was made to import an unimportable type. This can happen when trying
to import a type from a trait.

Erroneous code example:

```compile_fail,E0253
mod foo {
pub trait MyTrait {
fn do_something();
type SomeType;
}
}
use foo::MyTrait::do_something;
// error: `do_something` is not directly importable
use foo::MyTrait::SomeType;
// error: `SomeType` is not directly importable
fn main() {}
```

It's invalid to directly import methods belonging to a trait or concrete type.
It's invalid to directly import types belonging to a trait.
2 changes: 2 additions & 0 deletions compiler/rustc_feature/src/unstable.rs
Original file line number Diff line number Diff line change
Expand Up @@ -521,6 +521,8 @@ declare_features! (
(unstable, impl_trait_in_bindings, "1.64.0", Some(63065)),
/// Allows `impl Trait` as output type in `Fn` traits in return position of functions.
(unstable, impl_trait_in_fn_trait_return, "1.64.0", Some(99697)),
/// Allows `use` associated functions from traits.
(unstable, import_trait_associated_functions, "CURRENT_RUSTC_VERSION", Some(134691)),
/// Allows associated types in inherent impls.
(incomplete, inherent_associated_types, "1.52.0", Some(8995)),
/// Allow anonymous constants from an inline `const` block in pattern position
Expand Down
23 changes: 12 additions & 11 deletions compiler/rustc_hir_typeck/src/coercion.rs
Original file line number Diff line number Diff line change
Expand Up @@ -920,7 +920,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {

match b.kind() {
ty::FnPtr(_, b_hdr) => {
let a_sig = a.fn_sig(self.tcx);
let mut a_sig = a.fn_sig(self.tcx);
if let ty::FnDef(def_id, _) = *a.kind() {
// Intrinsics are not coercible to function pointers
if self.tcx.intrinsic(def_id).is_some() {
Expand All @@ -932,19 +932,20 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
return Err(TypeError::ForceInlineCast);
}

let fn_attrs = self.tcx.codegen_fn_attrs(def_id);
if matches!(fn_attrs.inline, InlineAttr::Force { .. }) {
return Err(TypeError::ForceInlineCast);
}

// FIXME(target_feature): Safe `#[target_feature]` functions could be cast to safe fn pointers (RFC 2396),
// as you can already write that "cast" in user code by wrapping a target_feature fn call in a closure,
// which is safe. This is sound because you already need to be executing code that is satisfying the target
// feature constraints..
if b_hdr.safety.is_safe()
&& self.tcx.codegen_fn_attrs(def_id).safe_target_features
{
return Err(TypeError::TargetFeatureCast(def_id));
// Allow the coercion if the current function has all the features that would be
// needed to call the coercee safely.
if let Some(safe_sig) = self.tcx.adjust_target_feature_sig(
def_id,
a_sig,
self.fcx.body_id.into(),
) {
a_sig = safe_sig;
} else {
return Err(TypeError::TargetFeatureCast(def_id));
}
}
}

Expand Down
30 changes: 30 additions & 0 deletions compiler/rustc_index/src/bit_set.rs
Original file line number Diff line number Diff line change
Expand Up @@ -281,6 +281,24 @@ impl<T: Idx> DenseBitSet<T> {
}

bit_relations_inherent_impls! {}

/// Sets `self = self | !other`.
///
/// FIXME: Incorporate this into [`BitRelations`] and fill out
/// implementations for other bitset types, if needed.
pub fn union_not(&mut self, other: &DenseBitSet<T>) {
assert_eq!(self.domain_size, other.domain_size);

// FIXME(Zalathar): If we were to forcibly _set_ all excess bits before
// the bitwise update, and then clear them again afterwards, we could
// quickly and accurately detect whether the update changed anything.
// But that's only worth doing if there's an actual use-case.

bitwise(&mut self.words, &other.words, |a, b| a | !b);
// The bitwise update `a | !b` can result in the last word containing
// out-of-domain bits, so we need to clear them.
self.clear_excess_bits();
}
}

// dense REL dense
Expand Down Expand Up @@ -1087,6 +1105,18 @@ impl<T: Idx> fmt::Debug for ChunkedBitSet<T> {
}
}

/// Sets `out_vec[i] = op(out_vec[i], in_vec[i])` for each index `i` in both
/// slices. The slices must have the same length.
///
/// Returns true if at least one bit in `out_vec` was changed.
///
/// ## Warning
/// Some bitwise operations (e.g. union-not, xor) can set output bits that were
/// unset in in both inputs. If this happens in the last word/chunk of a bitset,
/// it can cause the bitset to contain out-of-domain values, which need to
/// be cleared with `clear_excess_bits_in_final_word`. This also makes the
/// "changed" return value unreliable, because the change might have only
/// affected excess bits.
#[inline]
fn bitwise<Op>(out_vec: &mut [Word], in_vec: &[Word], op: Op) -> bool
where
Expand Down
26 changes: 26 additions & 0 deletions compiler/rustc_index/src/bit_set/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,32 @@ fn union_two_sets() {
assert!(set1.contains(64));
}

#[test]
fn union_not() {
let mut a = DenseBitSet::<usize>::new_empty(100);
let mut b = DenseBitSet::<usize>::new_empty(100);

a.insert(3);
a.insert(5);
a.insert(80);
a.insert(81);

b.insert(5); // Already in `a`.
b.insert(7);
b.insert(63);
b.insert(81); // Already in `a`.
b.insert(90);

a.union_not(&b);

// After union-not, `a` should contain all values in the domain, except for
// the ones that are in `b` and were _not_ already in `a`.
assert_eq!(
a.iter().collect::<Vec<_>>(),
(0usize..100).filter(|&x| !matches!(x, 7 | 63 | 90)).collect::<Vec<_>>(),
);
}

#[test]
fn chunked_bitset() {
let mut b0 = ChunkedBitSet::<usize>::new_empty(0);
Expand Down
33 changes: 32 additions & 1 deletion compiler/rustc_middle/src/ty/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ use crate::dep_graph::{DepGraph, DepKindStruct};
use crate::infer::canonical::{CanonicalParamEnvCache, CanonicalVarInfo, CanonicalVarInfos};
use crate::lint::lint_level;
use crate::metadata::ModChild;
use crate::middle::codegen_fn_attrs::CodegenFnAttrs;
use crate::middle::codegen_fn_attrs::{CodegenFnAttrs, TargetFeature};
use crate::middle::{resolve_bound_vars, stability};
use crate::mir::interpret::{self, Allocation, ConstAllocation};
use crate::mir::{Body, Local, Place, PlaceElem, ProjectionKind, Promoted};
Expand Down Expand Up @@ -1776,6 +1776,37 @@ impl<'tcx> TyCtxt<'tcx> {
pub fn dcx(self) -> DiagCtxtHandle<'tcx> {
self.sess.dcx()
}

pub fn is_target_feature_call_safe(
self,
callee_features: &[TargetFeature],
body_features: &[TargetFeature],
) -> bool {
// If the called function has target features the calling function hasn't,
// the call requires `unsafe`. Don't check this on wasm
// targets, though. For more information on wasm see the
// is_like_wasm check in hir_analysis/src/collect.rs
self.sess.target.options.is_like_wasm
|| callee_features
.iter()
.all(|feature| body_features.iter().any(|f| f.name == feature.name))
}

/// Returns the safe version of the signature of the given function, if calling it
/// would be safe in the context of the given caller.
pub fn adjust_target_feature_sig(
self,
fun_def: DefId,
fun_sig: ty::Binder<'tcx, ty::FnSig<'tcx>>,
caller: DefId,
) -> Option<ty::Binder<'tcx, ty::FnSig<'tcx>>> {
let fun_features = &self.codegen_fn_attrs(fun_def).target_features;
let callee_features = &self.codegen_fn_attrs(caller).target_features;
if self.is_target_feature_call_safe(&fun_features, &callee_features) {
return Some(fun_sig.map_bound(|sig| ty::FnSig { safety: hir::Safety::Safe, ..sig }));
}
None
}
}

impl<'tcx> TyCtxtAt<'tcx> {
Expand Down
11 changes: 3 additions & 8 deletions compiler/rustc_mir_build/src/check_unsafety.rs
Original file line number Diff line number Diff line change
Expand Up @@ -495,14 +495,9 @@ impl<'a, 'tcx> Visitor<'a, 'tcx> for UnsafetyVisitor<'a, 'tcx> {
};
self.requires_unsafe(expr.span, CallToUnsafeFunction(func_id));
} else if let &ty::FnDef(func_did, _) = fn_ty.kind() {
// If the called function has target features the calling function hasn't,
// the call requires `unsafe`. Don't check this on wasm
// targets, though. For more information on wasm see the
// is_like_wasm check in hir_analysis/src/collect.rs
if !self.tcx.sess.target.options.is_like_wasm
&& !callee_features.iter().all(|feature| {
self.body_target_features.iter().any(|f| f.name == feature.name)
})
if !self
.tcx
.is_target_feature_call_safe(callee_features, self.body_target_features)
{
let missing: Vec<_> = callee_features
.iter()
Expand Down
Loading

0 comments on commit 76a030a

Please sign in to comment.