Merge from rustc

This commit is contained in:
Oli Scherer 2023-01-23 08:59:23 +00:00
commit 461f272d77
56 changed files with 824 additions and 230 deletions

View file

@ -291,6 +291,14 @@ jobs:
- name: x86_64-gnu-distcheck
os: ubuntu-20.04-xl
env: {}
- name: x86_64-gnu-llvm-15
env:
RUST_BACKTRACE: 1
os: ubuntu-20.04-xl
- name: x86_64-gnu-llvm-14
env:
RUST_BACKTRACE: 1
os: ubuntu-20.04-xl
- name: x86_64-gnu-llvm-13
env:
RUST_BACKTRACE: 1

View file

@ -325,6 +325,7 @@ Lennart Kudling <github@kudling.de>
Léo Lanteri Thauvin <leseulartichaut@gmail.com>
Léo Lanteri Thauvin <leseulartichaut@gmail.com> <38361244+LeSeulArtichaut@users.noreply.github.com>
Léo Testard <leo.testard@gmail.com>
León Orell Valerian Liehr <me@fmease.dev> <liehr.exchange@gmx.net>
Leonardo Yvens <leoyvens@gmail.com>
Liigo Zhuang <liigo@qq.com>
Lily Ballard <lily@ballards.net> <kevin@sb.org>

View file

@ -4326,6 +4326,7 @@ dependencies = [
name = "rustc_metadata"
version = "0.0.0"
dependencies = [
"bitflags",
"libloading",
"odht",
"rustc_ast",

View file

@ -162,7 +162,7 @@ struct UniversalRegionIndices<'tcx> {
/// `ty::Region` to the internal `RegionVid` we are using. This is
/// used because trait matching and type-checking will feed us
/// region constraints that reference those regions and we need to
/// be able to map them our internal `RegionVid`. This is
/// be able to map them to our internal `RegionVid`. This is
/// basically equivalent to an `InternalSubsts`, except that it also
/// contains an entry for `ReStatic` -- it might be nice to just
/// use a substs, and then handle `ReStatic` another way.

View file

@ -1233,6 +1233,22 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
sugg_sp = receiver.span;
}
}
if let hir::ExprKind::Unary(hir::UnOp::Deref, ref inner) = expr.kind
&& let Some(1) = self.deref_steps(expected, checked_ty) {
// We have `*&T`, check if what was expected was `&T`.
// If so, we may want to suggest removing a `*`.
sugg_sp = sugg_sp.with_hi(inner.span.lo());
return Some((
sugg_sp,
"consider removing deref here".to_string(),
"".to_string(),
Applicability::MachineApplicable,
true,
false,
));
}
if let Ok(src) = sm.span_to_snippet(sugg_sp) {
let needs_parens = match expr.kind {
// parenthesize if needed (Issue #46756)

View file

@ -196,8 +196,6 @@ impl<'tcx> FnCtxt<'_, 'tcx> {
) -> FxHashMap<Ty<'tcx>, Ty<'tcx>> {
debug!("calculate_diverging_fallback({:?})", unsolved_variables);
let relationships = self.fulfillment_cx.borrow_mut().relationships().clone();
// Construct a coercion graph where an edge `A -> B` indicates
// a type variable is that is coerced
let coercion_graph = self.create_coercion_graph();
@ -281,9 +279,7 @@ impl<'tcx> FnCtxt<'_, 'tcx> {
roots_reachable_from_non_diverging,
);
debug!("inherited: {:#?}", self.inh.fulfillment_cx.borrow_mut().pending_obligations());
debug!("obligations: {:#?}", self.fulfillment_cx.borrow_mut().pending_obligations());
debug!("relationships: {:#?}", relationships);
// For each diverging variable, figure out whether it can
// reach a member of N. If so, it falls back to `()`. Else
@ -297,16 +293,16 @@ impl<'tcx> FnCtxt<'_, 'tcx> {
.depth_first_search(root_vid)
.any(|n| roots_reachable_from_non_diverging.visited(n));
let mut relationship = ty::FoundRelationships { self_in_trait: false, output: false };
let mut found_infer_var_info = ty::InferVarInfo { self_in_trait: false, output: false };
for (vid, rel) in relationships.iter() {
if self.root_var(*vid) == root_vid {
relationship.self_in_trait |= rel.self_in_trait;
relationship.output |= rel.output;
for (vid, info) in self.inh.infer_var_info.borrow().iter() {
if self.infcx.root_var(*vid) == root_vid {
found_infer_var_info.self_in_trait |= info.self_in_trait;
found_infer_var_info.output |= info.output;
}
}
if relationship.self_in_trait && relationship.output {
if found_infer_var_info.self_in_trait && found_infer_var_info.output {
// This case falls back to () to ensure that the code pattern in
// tests/ui/never_type/fallback-closure-ret.rs continues to
// compile when never_type_fallback is enabled.

View file

@ -1,6 +1,6 @@
use super::callee::DeferredCallResolution;
use rustc_data_structures::fx::FxHashSet;
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
use rustc_hir as hir;
use rustc_hir::def_id::LocalDefId;
use rustc_hir::HirIdMap;
@ -10,7 +10,8 @@ use rustc_middle::ty::visit::TypeVisitable;
use rustc_middle::ty::{self, Ty, TyCtxt};
use rustc_span::def_id::LocalDefIdMap;
use rustc_span::{self, Span};
use rustc_trait_selection::traits::{self, TraitEngine, TraitEngineExt as _};
use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt;
use rustc_trait_selection::traits::{self, PredicateObligation, TraitEngine, TraitEngineExt as _};
use std::cell::RefCell;
use std::ops::Deref;
@ -63,6 +64,8 @@ pub struct Inherited<'tcx> {
/// we record that type variable here. This is later used to inform
/// fallback. See the `fallback` module for details.
pub(super) diverging_type_vars: RefCell<FxHashSet<Ty<'tcx>>>,
pub(super) infer_var_info: RefCell<FxHashMap<ty::TyVid, ty::InferVarInfo>>,
}
impl<'tcx> Deref for Inherited<'tcx> {
@ -128,6 +131,7 @@ impl<'tcx> Inherited<'tcx> {
deferred_generator_interiors: RefCell::new(Vec::new()),
diverging_type_vars: RefCell::new(Default::default()),
body_id,
infer_var_info: RefCell::new(Default::default()),
}
}
@ -136,6 +140,9 @@ impl<'tcx> Inherited<'tcx> {
if obligation.has_escaping_bound_vars() {
span_bug!(obligation.cause.span, "escaping bound vars in predicate {:?}", obligation);
}
self.update_infer_var_info(&obligation);
self.fulfillment_cx.borrow_mut().register_predicate_obligation(self, obligation);
}
@ -152,4 +159,43 @@ impl<'tcx> Inherited<'tcx> {
self.register_predicates(infer_ok.obligations);
infer_ok.value
}
pub fn update_infer_var_info(&self, obligation: &PredicateObligation<'tcx>) {
let infer_var_info = &mut self.infer_var_info.borrow_mut();
// (*) binder skipped
if let ty::PredicateKind::Clause(ty::Clause::Trait(tpred)) = obligation.predicate.kind().skip_binder()
&& let Some(ty) = self.shallow_resolve(tpred.self_ty()).ty_vid().map(|t| self.root_var(t))
&& self.tcx.lang_items().sized_trait().map_or(false, |st| st != tpred.trait_ref.def_id)
{
let new_self_ty = self.tcx.types.unit;
// Then construct a new obligation with Self = () added
// to the ParamEnv, and see if it holds.
let o = obligation.with(self.tcx,
obligation
.predicate
.kind()
.rebind(
// (*) binder moved here
ty::PredicateKind::Clause(ty::Clause::Trait(tpred.with_self_ty(self.tcx, new_self_ty)))
),
);
// Don't report overflow errors. Otherwise equivalent to may_hold.
if let Ok(result) = self.probe(|_| self.evaluate_obligation(&o)) && result.may_apply() {
infer_var_info.entry(ty).or_default().self_in_trait = true;
}
}
if let ty::PredicateKind::Clause(ty::Clause::Projection(predicate)) =
obligation.predicate.kind().skip_binder()
{
// If the projection predicate (Foo::Bar == X) has X as a non-TyVid,
// we need to make it into one.
if let Some(vid) = predicate.term.ty().and_then(|ty| ty.ty_vid()) {
debug!("infer_var_info: {:?}.output = true", vid);
infer_var_info.entry(vid).or_default().output = true;
}
}
}
}

View file

@ -76,7 +76,7 @@ pub struct NoMatchData<'tcx> {
pub unsatisfied_predicates:
Vec<(ty::Predicate<'tcx>, Option<ty::Predicate<'tcx>>, Option<ObligationCause<'tcx>>)>,
pub out_of_scope_traits: Vec<DefId>,
pub lev_candidate: Option<ty::AssocItem>,
pub similar_candidate: Option<ty::AssocItem>,
pub mode: probe::Mode,
}

View file

@ -461,7 +461,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
static_candidates: Vec::new(),
unsatisfied_predicates: Vec::new(),
out_of_scope_traits: Vec::new(),
lev_candidate: None,
similar_candidate: None,
mode,
}));
}
@ -1076,13 +1076,13 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
if let Some((kind, def_id)) = private_candidate {
return Err(MethodError::PrivateMatch(kind, def_id, out_of_scope_traits));
}
let lev_candidate = self.probe_for_lev_candidate()?;
let similar_candidate = self.probe_for_similar_candidate()?;
Err(MethodError::NoMatch(NoMatchData {
static_candidates,
unsatisfied_predicates,
out_of_scope_traits,
lev_candidate,
similar_candidate,
mode: self.mode,
}))
}
@ -1787,7 +1787,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
/// Similarly to `probe_for_return_type`, this method attempts to find the best matching
/// candidate method where the method name may have been misspelled. Similarly to other
/// Levenshtein based suggestions, we provide at most one such suggestion.
fn probe_for_lev_candidate(&mut self) -> Result<Option<ty::AssocItem>, MethodError<'tcx>> {
fn probe_for_similar_candidate(&mut self) -> Result<Option<ty::AssocItem>, MethodError<'tcx>> {
debug!("probing for method names similar to {:?}", self.method_name);
let steps = self.steps.clone();
@ -1831,6 +1831,12 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
None,
)
}
.or_else(|| {
applicable_close_candidates
.iter()
.find(|cand| self.matches_by_doc_alias(cand.def_id))
.map(|cand| cand.name)
})
.unwrap();
Ok(applicable_close_candidates.into_iter().find(|method| method.name == best_name))
}
@ -1981,6 +1987,38 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
}
}
/// Determine if the associated item withe the given DefId matches
/// the desired name via a doc alias.
fn matches_by_doc_alias(&self, def_id: DefId) -> bool {
let Some(name) = self.method_name else { return false; };
let Some(local_def_id) = def_id.as_local() else { return false; };
let hir_id = self.fcx.tcx.hir().local_def_id_to_hir_id(local_def_id);
let attrs = self.fcx.tcx.hir().attrs(hir_id);
for attr in attrs {
let sym::doc = attr.name_or_empty() else { continue; };
let Some(values) = attr.meta_item_list() else { continue; };
for v in values {
if v.name_or_empty() != sym::alias {
continue;
}
if let Some(nested) = v.meta_item_list() {
// #[doc(alias("foo", "bar"))]
for n in nested {
if let Some(lit) = n.lit() && name.as_str() == lit.symbol.as_str() {
return true;
}
}
} else if let Some(meta) = v.meta_item()
&& let Some(lit) = meta.name_value_literal()
&& name.as_str() == lit.symbol.as_str() {
// #[doc(alias = "foo")]
return true;
}
}
}
false
}
/// Finds the method with the appropriate name (or return type, as the case may be). If
/// `allow_similar_names` is set, find methods with close-matching names.
// The length of the returned iterator is nearly always 0 or 1 and this
@ -1996,6 +2034,9 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
if !self.is_relevant_kind_for_mode(x.kind) {
return false;
}
if self.matches_by_doc_alias(x.def_id) {
return true;
}
match lev_distance_with_substrings(name.as_str(), x.name.as_str(), max_dist)
{
Some(d) => d > 0,

View file

@ -262,7 +262,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
let ty_str = with_forced_trimmed_paths!(self.ty_to_string(rcvr_ty));
let is_method = mode == Mode::MethodCall;
let unsatisfied_predicates = &no_match_data.unsatisfied_predicates;
let lev_candidate = no_match_data.lev_candidate;
let similar_candidate = no_match_data.similar_candidate;
let item_kind = if is_method {
"method"
} else if rcvr_ty.is_enum() {
@ -937,7 +937,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
// give a helping note that it has to be called as `(x.f)(...)`.
if let SelfSource::MethodCall(expr) = source {
if !self.suggest_calling_field_as_fn(span, rcvr_ty, expr, item_name, &mut err)
&& lev_candidate.is_none()
&& similar_candidate.is_none()
&& !custom_span_label
{
label_span_not_found(&mut err);
@ -1015,20 +1015,20 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
if fallback_span {
err.span_label(span, msg);
}
} else if let Some(lev_candidate) = lev_candidate {
} else if let Some(similar_candidate) = similar_candidate {
// Don't emit a suggestion if we found an actual method
// that had unsatisfied trait bounds
if unsatisfied_predicates.is_empty() {
let def_kind = lev_candidate.kind.as_def_kind();
let def_kind = similar_candidate.kind.as_def_kind();
// Methods are defined within the context of a struct and their first parameter is always self,
// which represents the instance of the struct the method is being called on
// Associated functions dont take self as a parameter and
// they are not methods because they dont have an instance of the struct to work with.
if def_kind == DefKind::AssocFn && lev_candidate.fn_has_self_parameter {
if def_kind == DefKind::AssocFn && similar_candidate.fn_has_self_parameter {
err.span_suggestion(
span,
"there is a method with a similar name",
lev_candidate.name,
similar_candidate.name,
Applicability::MaybeIncorrect,
);
} else {
@ -1037,9 +1037,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
&format!(
"there is {} {} with a similar name",
def_kind.article(),
def_kind.descr(lev_candidate.def_id),
def_kind.descr(similar_candidate.def_id),
),
lev_candidate.name,
similar_candidate.name,
Applicability::MaybeIncorrect,
);
}

View file

@ -1,6 +1,5 @@
use crate::infer::InferCtxt;
use crate::traits::Obligation;
use rustc_data_structures::fx::FxHashMap;
use rustc_hir::def_id::DefId;
use rustc_middle::ty::{self, ToPredicate, Ty};
@ -42,8 +41,6 @@ pub trait TraitEngine<'tcx>: 'tcx {
fn select_where_possible(&mut self, infcx: &InferCtxt<'tcx>) -> Vec<FulfillmentError<'tcx>>;
fn pending_obligations(&self) -> Vec<PredicateObligation<'tcx>>;
fn relationships(&mut self) -> &mut FxHashMap<ty::TyVid, ty::FoundRelationships>;
}
pub trait TraitEngineExt<'tcx> {

View file

@ -6,6 +6,7 @@ edition = "2021"
[lib]
[dependencies]
bitflags = "1.2.1"
libloading = "0.7.1"
odht = { version = "0.3.1", features = ["nightly"] }
snap = "1"

View file

@ -1594,8 +1594,8 @@ impl<'a, 'tcx> CrateMetadataRef<'a> {
})
}
fn get_may_have_doc_links(self, index: DefIndex) -> bool {
self.root.tables.may_have_doc_links.get(self, index).is_some()
fn get_attr_flags(self, index: DefIndex) -> AttrFlags {
self.root.tables.attr_flags.get(self, index).unwrap_or(AttrFlags::empty())
}
fn get_is_intrinsic(self, index: DefIndex) -> bool {

View file

@ -1,6 +1,7 @@
use crate::creader::{CStore, LoadedMacro};
use crate::foreign_modules;
use crate::native_libs;
use crate::rmeta::AttrFlags;
use rustc_ast as ast;
use rustc_attr::Deprecation;
@ -338,6 +339,7 @@ provide! { tcx, def_id, other, cdata,
crate_extern_paths => { cdata.source().paths().cloned().collect() }
expn_that_defined => { cdata.get_expn_that_defined(def_id.index, tcx.sess) }
generator_diagnostic_data => { cdata.get_generator_diagnostic_data(tcx, def_id.index) }
is_doc_hidden => { cdata.get_attr_flags(def_id.index).contains(AttrFlags::IS_DOC_HIDDEN) }
}
pub(in crate::rmeta) fn provide(providers: &mut Providers) {
@ -425,7 +427,7 @@ pub(in crate::rmeta) fn provide(providers: &mut Providers) {
return;
}
if ty::util::is_doc_hidden(tcx, parent) {
if tcx.is_doc_hidden(parent) {
fallback_map.push((def_id, parent));
return;
}
@ -631,7 +633,9 @@ impl CStore {
}
pub fn may_have_doc_links_untracked(&self, def_id: DefId) -> bool {
self.get_crate_data(def_id.krate).get_may_have_doc_links(def_id.index)
self.get_crate_data(def_id.krate)
.get_attr_flags(def_id.index)
.contains(AttrFlags::MAY_HAVE_DOC_LINKS)
}
}

View file

@ -1111,15 +1111,26 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
let tcx = self.tcx;
let mut is_public: Option<bool> = None;
let mut attrs = tcx
.hir()
.attrs(tcx.hir().local_def_id_to_hir_id(def_id))
let hir_attrs = tcx.hir().attrs(tcx.hir().local_def_id_to_hir_id(def_id));
let mut attrs = hir_attrs
.iter()
.filter(move |attr| should_encode_attr(tcx, attr, def_id, &mut is_public));
record_array!(self.tables.attributes[def_id.to_def_id()] <- attrs.clone());
let mut attr_flags = AttrFlags::empty();
if attrs.any(|attr| attr.may_have_doc_links()) {
self.tables.may_have_doc_links.set(def_id.local_def_index, ());
attr_flags |= AttrFlags::MAY_HAVE_DOC_LINKS;
}
if hir_attrs
.iter()
.filter(|attr| attr.has_name(sym::doc))
.filter_map(|attr| attr.meta_item_list())
.any(|items| items.iter().any(|item| item.has_name(sym::hidden)))
{
attr_flags |= AttrFlags::IS_DOC_HIDDEN;
}
if !attr_flags.is_empty() {
self.tables.attr_flags.set(def_id.local_def_index, attr_flags);
}
}

View file

@ -395,7 +395,7 @@ define_tables! {
def_path_hashes: Table<DefIndex, DefPathHash>,
proc_macro_quoted_spans: Table<usize, LazyValue<Span>>,
generator_diagnostic_data: Table<DefIndex, LazyValue<GeneratorDiagnosticData<'static>>>,
may_have_doc_links: Table<DefIndex, ()>,
attr_flags: Table<DefIndex, AttrFlags>,
variant_data: Table<DefIndex, LazyValue<VariantData>>,
assoc_container: Table<DefIndex, ty::AssocItemContainer>,
// Slot is full when macro is macro_rules.
@ -418,6 +418,13 @@ struct VariantData {
is_non_exhaustive: bool,
}
bitflags::bitflags! {
pub struct AttrFlags: u8 {
const MAY_HAVE_DOC_LINKS = 1 << 0;
const IS_DOC_HIDDEN = 1 << 1;
}
}
// Tags used for encoding Spans:
const TAG_VALID_SPAN_LOCAL: u8 = 0;
const TAG_VALID_SPAN_FOREIGN: u8 = 1;
@ -440,4 +447,5 @@ trivially_parameterized_over_tcx! {
IncoherentImpls,
CrateRoot,
CrateDep,
AttrFlags,
}

View file

@ -199,6 +199,20 @@ impl FixedSizeEncoding for Option<RawDefId> {
}
}
impl FixedSizeEncoding for Option<AttrFlags> {
type ByteArray = [u8; 1];
#[inline]
fn from_bytes(b: &[u8; 1]) -> Self {
(b[0] != 0).then(|| AttrFlags::from_bits_truncate(b[0]))
}
#[inline]
fn write_to_bytes(self, b: &mut [u8; 1]) {
b[0] = self.map_or(0, |flags| flags.bits())
}
}
impl FixedSizeEncoding for Option<()> {
type ByteArray = [u8; 1];

View file

@ -1157,6 +1157,7 @@ rustc_queries! {
/// Determines whether an item is annotated with `doc(hidden)`.
query is_doc_hidden(def_id: DefId) -> bool {
desc { |tcx| "checking whether `{}` is `doc(hidden)`", tcx.def_path_str(def_id) }
separate_provide_extern
}
/// Determines whether an item is annotated with `doc(notable_trait)`.

View file

@ -2619,7 +2619,7 @@ impl<'tcx> fmt::Debug for SymbolName<'tcx> {
}
#[derive(Debug, Default, Copy, Clone)]
pub struct FoundRelationships {
pub struct InferVarInfo {
/// This is true if we identified that this Ty (`?T`) is found in a `?T: Foo`
/// obligation, where:
///

View file

@ -1310,7 +1310,8 @@ pub fn reveal_opaque_types_in_bounds<'tcx>(
}
/// Determines whether an item is annotated with `doc(hidden)`.
pub fn is_doc_hidden(tcx: TyCtxt<'_>, def_id: DefId) -> bool {
fn is_doc_hidden(tcx: TyCtxt<'_>, def_id: DefId) -> bool {
assert!(def_id.is_local());
tcx.get_attrs(def_id, sym::doc)
.filter_map(|attr| attr.meta_item_list())
.any(|items| items.iter().any(|item| item.has_name(sym::hidden)))

View file

@ -1,6 +1,5 @@
use std::mem;
use rustc_data_structures::fx::FxHashMap;
use rustc_infer::{
infer::InferCtxt,
traits::{
@ -8,7 +7,6 @@ use rustc_infer::{
SelectionError, TraitEngine,
},
};
use rustc_middle::ty;
use super::{search_graph, Certainty, EvalCtxt};
@ -102,8 +100,4 @@ impl<'tcx> TraitEngine<'tcx> for FulfillmentCtxt<'tcx> {
fn pending_obligations(&self) -> Vec<PredicateObligation<'tcx>> {
self.obligations.clone()
}
fn relationships(&mut self) -> &mut FxHashMap<ty::TyVid, ty::FoundRelationships> {
unimplemented!("Should be moved out of `TraitEngine`")
}
}

View file

@ -7,24 +7,18 @@ use crate::traits::{
ChalkEnvironmentAndGoal, FulfillmentError, FulfillmentErrorCode, PredicateObligation,
SelectionError, TraitEngine,
};
use rustc_data_structures::fx::{FxHashMap, FxIndexSet};
use rustc_middle::ty::{self, TypeVisitable};
use rustc_data_structures::fx::FxIndexSet;
use rustc_middle::ty::TypeVisitable;
pub struct FulfillmentContext<'tcx> {
obligations: FxIndexSet<PredicateObligation<'tcx>>,
relationships: FxHashMap<ty::TyVid, ty::FoundRelationships>,
usable_in_snapshot: bool,
}
impl FulfillmentContext<'_> {
pub(super) fn new() -> Self {
FulfillmentContext {
obligations: FxIndexSet::default(),
relationships: FxHashMap::default(),
usable_in_snapshot: false,
}
FulfillmentContext { obligations: FxIndexSet::default(), usable_in_snapshot: false }
}
pub(crate) fn new_in_snapshot() -> Self {
@ -43,8 +37,6 @@ impl<'tcx> TraitEngine<'tcx> for FulfillmentContext<'tcx> {
}
let obligation = infcx.resolve_vars_if_possible(obligation);
super::relationships::update(self, infcx, &obligation);
self.obligations.insert(obligation);
}
@ -154,8 +146,4 @@ impl<'tcx> TraitEngine<'tcx> for FulfillmentContext<'tcx> {
fn pending_obligations(&self) -> Vec<PredicateObligation<'tcx>> {
self.obligations.iter().cloned().collect()
}
fn relationships(&mut self) -> &mut FxHashMap<ty::TyVid, ty::FoundRelationships> {
&mut self.relationships
}
}

View file

@ -1,5 +1,4 @@
use crate::infer::{InferCtxt, TyOrConstInferVar};
use rustc_data_structures::fx::FxHashMap;
use rustc_data_structures::obligation_forest::ProcessResult;
use rustc_data_structures::obligation_forest::{Error, ForestObligation, Outcome};
use rustc_data_structures::obligation_forest::{ObligationForest, ObligationProcessor};
@ -54,8 +53,6 @@ pub struct FulfillmentContext<'tcx> {
// fulfillment context.
predicates: ObligationForest<PendingPredicateObligation<'tcx>>,
relationships: FxHashMap<ty::TyVid, ty::FoundRelationships>,
// Is it OK to register obligations into this infcx inside
// an infcx snapshot?
//
@ -85,19 +82,11 @@ static_assert_size!(PendingPredicateObligation<'_>, 72);
impl<'a, 'tcx> FulfillmentContext<'tcx> {
/// Creates a new fulfillment context.
pub(super) fn new() -> FulfillmentContext<'tcx> {
FulfillmentContext {
predicates: ObligationForest::new(),
relationships: FxHashMap::default(),
usable_in_snapshot: false,
}
FulfillmentContext { predicates: ObligationForest::new(), usable_in_snapshot: false }
}
pub(super) fn new_in_snapshot() -> FulfillmentContext<'tcx> {
FulfillmentContext {
predicates: ObligationForest::new(),
relationships: FxHashMap::default(),
usable_in_snapshot: true,
}
FulfillmentContext { predicates: ObligationForest::new(), usable_in_snapshot: true }
}
/// Attempts to select obligations using `selcx`.
@ -139,8 +128,6 @@ impl<'tcx> TraitEngine<'tcx> for FulfillmentContext<'tcx> {
assert!(!infcx.is_in_snapshot() || self.usable_in_snapshot);
super::relationships::update(self, infcx, &obligation);
self.predicates
.register_obligation(PendingPredicateObligation { obligation, stalled_on: vec![] });
}
@ -164,10 +151,6 @@ impl<'tcx> TraitEngine<'tcx> for FulfillmentContext<'tcx> {
fn pending_obligations(&self) -> Vec<PredicateObligation<'tcx>> {
self.predicates.map_pending_obligations(|o| o.obligation.clone())
}
fn relationships(&mut self) -> &mut FxHashMap<ty::TyVid, ty::FoundRelationships> {
&mut self.relationships
}
}
struct FulfillProcessor<'a, 'tcx> {

View file

@ -14,7 +14,6 @@ mod object_safety;
pub mod outlives_bounds;
mod project;
pub mod query;
pub(crate) mod relationships;
mod select;
mod specialize;
mod structural_match;

View file

@ -1,48 +0,0 @@
use crate::infer::InferCtxt;
use crate::traits::query::evaluate_obligation::InferCtxtExt;
use crate::traits::PredicateObligation;
use rustc_infer::traits::TraitEngine;
use rustc_middle::ty;
pub(crate) fn update<'tcx, T>(
engine: &mut T,
infcx: &InferCtxt<'tcx>,
obligation: &PredicateObligation<'tcx>,
) where
T: TraitEngine<'tcx>,
{
// (*) binder skipped
if let ty::PredicateKind::Clause(ty::Clause::Trait(tpred)) = obligation.predicate.kind().skip_binder()
&& let Some(ty) = infcx.shallow_resolve(tpred.self_ty()).ty_vid().map(|t| infcx.root_var(t))
&& infcx.tcx.lang_items().sized_trait().map_or(false, |st| st != tpred.trait_ref.def_id)
{
let new_self_ty = infcx.tcx.types.unit;
// Then construct a new obligation with Self = () added
// to the ParamEnv, and see if it holds.
let o = obligation.with(infcx.tcx,
obligation
.predicate
.kind()
.rebind(
// (*) binder moved here
ty::PredicateKind::Clause(ty::Clause::Trait(tpred.with_self_ty(infcx.tcx, new_self_ty)))
),
);
// Don't report overflow errors. Otherwise equivalent to may_hold.
if let Ok(result) = infcx.probe(|_| infcx.evaluate_obligation(&o)) && result.may_apply() {
engine.relationships().entry(ty).or_default().self_in_trait = true;
}
}
if let ty::PredicateKind::Clause(ty::Clause::Projection(predicate)) =
obligation.predicate.kind().skip_binder()
{
// If the projection predicate (Foo::Bar == X) has X as a non-TyVid,
// we need to make it into one.
if let Some(vid) = predicate.term.ty().and_then(|ty| ty.ty_vid()) {
debug!("relationship: {:?}.output = true", vid);
engine.relationships().entry(vid).or_default().output = true;
}
}
}

View file

@ -654,6 +654,20 @@ impl<T> Arc<T> {
///
/// This will succeed even if there are outstanding weak references.
///
// FIXME: when `Arc::into_inner` is stabilized, add this paragraph:
/*
/// It is strongly recommended to use [`Arc::into_inner`] instead if you don't
/// want to keep the `Arc` in the [`Err`] case.
/// Immediately dropping the [`Err`] payload, like in the expression
/// `Arc::try_unwrap(this).ok()`, can still cause the strong count to
/// drop to zero and the inner value of the `Arc` to be dropped:
/// For instance if two threads execute this expression in parallel, then
/// there is a race condition. The threads could first both check whether they
/// have the last clone of their `Arc` via `Arc::try_unwrap`, and then
/// both drop their `Arc` in the call to [`ok`][`Result::ok`],
/// taking the strong count from two down to zero.
///
*/
/// # Examples
///
/// ```
@ -685,6 +699,137 @@ impl<T> Arc<T> {
Ok(elem)
}
}
/// Returns the inner value, if the `Arc` has exactly one strong reference.
///
/// Otherwise, [`None`] is returned and the `Arc` is dropped.
///
/// This will succeed even if there are outstanding weak references.
///
/// If `Arc::into_inner` is called on every clone of this `Arc`,
/// it is guaranteed that exactly one of the calls returns the inner value.
/// This means in particular that the inner value is not dropped.
///
/// The similar expression `Arc::try_unwrap(this).ok()` does not
/// offer such a guarantee. See the last example below.
//
// FIXME: when `Arc::into_inner` is stabilized, add this to end
// of the previous sentence:
/*
/// and the documentation of [`Arc::try_unwrap`].
*/
///
/// # Examples
///
/// Minimal example demonstrating the guarantee that `Arc::into_inner` gives.
/// ```
/// #![feature(arc_into_inner)]
///
/// use std::sync::Arc;
///
/// let x = Arc::new(3);
/// let y = Arc::clone(&x);
///
/// // Two threads calling `Arc::into_inner` on both clones of an `Arc`:
/// let x_thread = std::thread::spawn(|| Arc::into_inner(x));
/// let y_thread = std::thread::spawn(|| Arc::into_inner(y));
///
/// let x_inner_value = x_thread.join().unwrap();
/// let y_inner_value = y_thread.join().unwrap();
///
/// // One of the threads is guaranteed to receive the inner value:
/// assert!(matches!(
/// (x_inner_value, y_inner_value),
/// (None, Some(3)) | (Some(3), None)
/// ));
/// // The result could also be `(None, None)` if the threads called
/// // `Arc::try_unwrap(x).ok()` and `Arc::try_unwrap(y).ok()` instead.
/// ```
///
/// A more practical example demonstrating the need for `Arc::into_inner`:
/// ```
/// #![feature(arc_into_inner)]
///
/// use std::sync::Arc;
///
/// // Definition of a simple singly linked list using `Arc`:
/// #[derive(Clone)]
/// struct LinkedList<T>(Option<Arc<Node<T>>>);
/// struct Node<T>(T, Option<Arc<Node<T>>>);
///
/// // Dropping a long `LinkedList<T>` relying on the destructor of `Arc`
/// // can cause a stack overflow. To prevent this, we can provide a
/// // manual `Drop` implementation that does the destruction in a loop:
/// impl<T> Drop for LinkedList<T> {
/// fn drop(&mut self) {
/// let mut link = self.0.take();
/// while let Some(arc_node) = link.take() {
/// if let Some(Node(_value, next)) = Arc::into_inner(arc_node) {
/// link = next;
/// }
/// }
/// }
/// }
///
/// // Implementation of `new` and `push` omitted
/// impl<T> LinkedList<T> {
/// /* ... */
/// # fn new() -> Self {
/// # LinkedList(None)
/// # }
/// # fn push(&mut self, x: T) {
/// # self.0 = Some(Arc::new(Node(x, self.0.take())));
/// # }
/// }
///
/// // The following code could have still caused a stack overflow
/// // despite the manual `Drop` impl if that `Drop` impl had used
/// // `Arc::try_unwrap(arc).ok()` instead of `Arc::into_inner(arc)`.
///
/// // Create a long list and clone it
/// let mut x = LinkedList::new();
/// for i in 0..100000 {
/// x.push(i); // Adds i to the front of x
/// }
/// let y = x.clone();
///
/// // Drop the clones in parallel
/// let x_thread = std::thread::spawn(|| drop(x));
/// let y_thread = std::thread::spawn(|| drop(y));
/// x_thread.join().unwrap();
/// y_thread.join().unwrap();
/// ```
// FIXME: when `Arc::into_inner` is stabilized, adjust above documentation
// and the documentation of `Arc::try_unwrap` according to the `FIXME`s. Also
// open an issue on rust-lang/rust-clippy, asking for a lint against
// `Arc::try_unwrap(...).ok()`.
#[inline]
#[unstable(feature = "arc_into_inner", issue = "106894")]
pub fn into_inner(this: Self) -> Option<T> {
// Make sure that the ordinary `Drop` implementation isnt called as well
let mut this = mem::ManuallyDrop::new(this);
// Following the implementation of `drop` and `drop_slow`
if this.inner().strong.fetch_sub(1, Release) != 1 {
return None;
}
acquire!(this.inner().strong);
// SAFETY: This mirrors the line
//
// unsafe { ptr::drop_in_place(Self::get_mut_unchecked(self)) };
//
// in `drop_slow`. Instead of dropping the value behind the pointer,
// it is read and eventually returned; `ptr::read` has the same
// safety conditions as `ptr::drop_in_place`.
let inner = unsafe { ptr::read(Self::get_mut_unchecked(&mut this)) };
drop(Weak { ptr: this.ptr });
Some(inner)
}
}
impl<T> Arc<[T]> {

View file

@ -101,6 +101,38 @@ fn try_unwrap() {
assert_eq!(Arc::try_unwrap(x), Ok(5));
}
#[test]
fn into_inner() {
for _ in 0..100
// ^ Increase chances of hitting potential race conditions
{
let x = Arc::new(3);
let y = Arc::clone(&x);
let r_thread = std::thread::spawn(|| Arc::into_inner(x));
let s_thread = std::thread::spawn(|| Arc::into_inner(y));
let r = r_thread.join().expect("r_thread panicked");
let s = s_thread.join().expect("s_thread panicked");
assert!(
matches!((r, s), (None, Some(3)) | (Some(3), None)),
"assertion failed: unexpected result `{:?}`\
\n expected `(None, Some(3))` or `(Some(3), None)`",
(r, s),
);
}
let x = Arc::new(3);
assert_eq!(Arc::into_inner(x), Some(3));
let x = Arc::new(4);
let y = Arc::clone(&x);
assert_eq!(Arc::into_inner(x), None);
assert_eq!(Arc::into_inner(y), Some(4));
let x = Arc::new(5);
let _w = Arc::downgrade(&x);
assert_eq!(Arc::into_inner(x), Some(5));
}
#[test]
fn into_from_raw() {
let x = Arc::new(Box::new("hello"));

View file

@ -238,7 +238,6 @@
#![feature(allocator_internals)]
#![feature(allow_internal_unsafe)]
#![feature(allow_internal_unstable)]
#![feature(box_syntax)]
#![feature(c_unwind)]
#![feature(cfg_target_thread_local)]
#![feature(concat_idents)]

View file

@ -1,8 +1,21 @@
use crate::cell::Cell;
use crate::cell::UnsafeCell;
use crate::fmt;
use crate::mem::ManuallyDrop;
use crate::ops::Deref;
use crate::panic::{RefUnwindSafe, UnwindSafe};
use crate::sync::OnceLock;
use crate::sync::Once;
use super::once::ExclusiveState;
// We use the state of a Once as discriminant value. Upon creation, the state is
// "incomplete" and `f` contains the initialization closure. In the first call to
// `call_once`, `f` is taken and run. If it succeeds, `value` is set and the state
// is changed to "complete". If it panics, the Once is poisoned, so none of the
// two fields is initialized.
union Data<T, F> {
value: ManuallyDrop<T>,
f: ManuallyDrop<F>,
}
/// A value which is initialized on the first access.
///
@ -43,16 +56,17 @@ use crate::sync::OnceLock;
/// ```
#[unstable(feature = "once_cell", issue = "74465")]
pub struct LazyLock<T, F = fn() -> T> {
cell: OnceLock<T>,
init: Cell<Option<F>>,
once: Once,
data: UnsafeCell<Data<T, F>>,
}
impl<T, F: FnOnce() -> T> LazyLock<T, F> {
/// Creates a new lazy value with the given initializing
/// function.
#[inline]
#[unstable(feature = "once_cell", issue = "74465")]
pub const fn new(f: F) -> LazyLock<T, F> {
LazyLock { cell: OnceLock::new(), init: Cell::new(Some(f)) }
LazyLock { once: Once::new(), data: UnsafeCell::new(Data { f: ManuallyDrop::new(f) }) }
}
/// Forces the evaluation of this lazy value and
@ -74,10 +88,50 @@ impl<T, F: FnOnce() -> T> LazyLock<T, F> {
#[inline]
#[unstable(feature = "once_cell", issue = "74465")]
pub fn force(this: &LazyLock<T, F>) -> &T {
this.cell.get_or_init(|| match this.init.take() {
Some(f) => f(),
None => panic!("Lazy instance has previously been poisoned"),
})
this.once.call_once(|| {
// SAFETY: `call_once` only runs this closure once, ever.
let data = unsafe { &mut *this.data.get() };
let f = unsafe { ManuallyDrop::take(&mut data.f) };
let value = f();
data.value = ManuallyDrop::new(value);
});
// SAFETY:
// There are four possible scenarios:
// * the closure was called and initialized `value`.
// * the closure was called and panicked, so this point is never reached.
// * the closure was not called, but a previous call initialized `value`.
// * the closure was not called because the Once is poisoned, so this point
// is never reached.
// So `value` has definitely been initialized and will not be modified again.
unsafe { &*(*this.data.get()).value }
}
}
impl<T, F> LazyLock<T, F> {
/// Get the inner value if it has already been initialized.
fn get(&self) -> Option<&T> {
if self.once.is_completed() {
// SAFETY:
// The closure has been run successfully, so `value` has been initialized
// and will not be modified again.
Some(unsafe { &*(*self.data.get()).value })
} else {
None
}
}
}
#[unstable(feature = "once_cell", issue = "74465")]
impl<T, F> Drop for LazyLock<T, F> {
fn drop(&mut self) {
match self.once.state() {
ExclusiveState::Incomplete => unsafe { ManuallyDrop::drop(&mut self.data.get_mut().f) },
ExclusiveState::Complete => unsafe {
ManuallyDrop::drop(&mut self.data.get_mut().value)
},
ExclusiveState::Poisoned => {}
}
}
}
@ -103,23 +157,23 @@ impl<T: Default> Default for LazyLock<T> {
#[unstable(feature = "once_cell", issue = "74465")]
impl<T: fmt::Debug, F> fmt::Debug for LazyLock<T, F> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Lazy").field("cell", &self.cell).finish_non_exhaustive()
match self.get() {
Some(v) => f.debug_tuple("LazyLock").field(v).finish(),
None => f.write_str("LazyLock(Uninit)"),
}
}
}
// We never create a `&F` from a `&LazyLock<T, F>` so it is fine
// to not impl `Sync` for `F`
// we do create a `&mut Option<F>` in `force`, but this is
// properly synchronized, so it only happens once
// so it also does not contribute to this impl.
#[unstable(feature = "once_cell", issue = "74465")]
unsafe impl<T, F: Send> Sync for LazyLock<T, F> where OnceLock<T>: Sync {}
unsafe impl<T: Sync + Send, F: Send> Sync for LazyLock<T, F> {}
// auto-derived `Send` impl is OK.
#[unstable(feature = "once_cell", issue = "74465")]
impl<T, F: UnwindSafe> RefUnwindSafe for LazyLock<T, F> where OnceLock<T>: RefUnwindSafe {}
impl<T: RefUnwindSafe + UnwindSafe, F: UnwindSafe> RefUnwindSafe for LazyLock<T, F> {}
#[unstable(feature = "once_cell", issue = "74465")]
impl<T, F: UnwindSafe> UnwindSafe for LazyLock<T, F> where OnceLock<T>: UnwindSafe {}
impl<T: UnwindSafe, F: UnwindSafe> UnwindSafe for LazyLock<T, F> {}
#[cfg(test)]
mod tests;

View file

@ -186,7 +186,7 @@ mod condvar;
mod lazy_lock;
mod mpmc;
mod mutex;
mod once;
pub(crate) mod once;
mod once_lock;
mod poison;
mod remutex;

View file

@ -43,6 +43,12 @@ pub struct OnceState {
pub(crate) inner: sys::OnceState,
}
pub(crate) enum ExclusiveState {
Incomplete,
Poisoned,
Complete,
}
/// Initialization value for static [`Once`] values.
///
/// # Examples
@ -248,6 +254,16 @@ impl Once {
pub fn is_completed(&self) -> bool {
self.inner.is_completed()
}
/// Returns the current state of the `Once` instance.
///
/// Since this takes a mutable reference, no initialization can currently
/// be running, so the state must be either "incomplete", "poisoned" or
/// "complete".
#[inline]
pub(crate) fn state(&mut self) -> ExclusiveState {
self.inner.state()
}
}
#[stable(feature = "std_debug", since = "1.16.0")]

View file

@ -27,10 +27,10 @@ impl Thread {
p: Box<dyn FnOnce()>,
core_id: isize,
) -> io::Result<Thread> {
let p = Box::into_raw(box p);
let p = Box::into_raw(Box::new(p));
let tid = abi::spawn2(
thread_start,
p as usize,
p.expose_addr(),
abi::Priority::into(abi::NORMAL_PRIO),
stack,
core_id,

View file

@ -5,32 +5,23 @@
// The this solution works like the implementation of macOS and
// doesn't additional OS support
use crate::cell::Cell;
use crate::ptr;
use crate::mem;
#[thread_local]
static DTORS: Cell<*mut List> = Cell::new(ptr::null_mut());
type List = Vec<(*mut u8, unsafe extern "C" fn(*mut u8))>;
static mut DTORS: Vec<(*mut u8, unsafe extern "C" fn(*mut u8))> = Vec::new();
pub unsafe fn register_dtor(t: *mut u8, dtor: unsafe extern "C" fn(*mut u8)) {
if DTORS.get().is_null() {
let v: Box<List> = box Vec::new();
DTORS.set(Box::into_raw(v));
}
let list: &mut List = &mut *DTORS.get();
let list = &mut DTORS;
list.push((t, dtor));
}
// every thread call this function to run through all possible destructors
pub unsafe fn run_dtors() {
let mut ptr = DTORS.replace(ptr::null_mut());
while !ptr.is_null() {
let list = Box::from_raw(ptr);
for (ptr, dtor) in list.into_iter() {
let mut list = mem::take(&mut DTORS);
while !list.is_empty() {
for (ptr, dtor) in list {
dtor(ptr);
}
ptr = DTORS.replace(ptr::null_mut());
list = mem::take(&mut DTORS);
}
}

View file

@ -5,43 +5,35 @@
use super::{abi, itron::task};
use crate::cell::Cell;
use crate::ptr;
use crate::mem;
#[thread_local]
static DTORS: Cell<*mut List> = Cell::new(ptr::null_mut());
static REGISTERED: Cell<bool> = Cell::new(false);
type List = Vec<(*mut u8, unsafe extern "C" fn(*mut u8))>;
#[thread_local]
static mut DTORS: Vec<(*mut u8, unsafe extern "C" fn(*mut u8))> = Vec::new();
pub unsafe fn register_dtor(t: *mut u8, dtor: unsafe extern "C" fn(*mut u8)) {
if DTORS.get().is_null() {
if !REGISTERED.get() {
let tid = task::current_task_id_aborting();
let v: Box<List> = box Vec::new();
DTORS.set(Box::into_raw(v));
// Register `tls_dtor` to make sure the TLS destructors are called
// for tasks created by other means than `std::thread`
unsafe { abi::SOLID_TLS_AddDestructor(tid as i32, tls_dtor) };
REGISTERED.set(true);
}
let list: &mut List = unsafe { &mut *DTORS.get() };
let list = unsafe { &mut DTORS };
list.push((t, dtor));
}
pub unsafe fn run_dtors() {
let ptr = DTORS.get();
if !ptr.is_null() {
// Swap the destructor list, call all registered destructors,
// and repeat this until the list becomes permanently empty.
while let Some(list) = Some(crate::mem::replace(unsafe { &mut *ptr }, Vec::new()))
.filter(|list| !list.is_empty())
{
for (ptr, dtor) in list.into_iter() {
unsafe { dtor(ptr) };
}
let mut list = mem::take(unsafe { &mut DTORS });
while !list.is_empty() {
for (ptr, dtor) in list {
unsafe { dtor(ptr) };
}
// Drop the destructor list
unsafe { Box::from_raw(DTORS.replace(ptr::null_mut())) };
list = mem::take(unsafe { &mut DTORS });
}
}

View file

@ -49,7 +49,7 @@ unsafe impl Sync for Thread {}
impl Thread {
// unsafe: see thread::Builder::spawn_unchecked for safety requirements
pub unsafe fn new(stack: usize, p: Box<dyn FnOnce()>) -> io::Result<Thread> {
let p = Box::into_raw(box p);
let p = Box::into_raw(Box::new(p));
let mut native: libc::pthread_t = mem::zeroed();
let mut attr: libc::pthread_attr_t = mem::zeroed();
assert_eq!(libc::pthread_attr_init(&mut attr), 0);

View file

@ -57,39 +57,34 @@ pub unsafe fn register_dtor(t: *mut u8, dtor: unsafe extern "C" fn(*mut u8)) {
#[cfg(target_os = "macos")]
pub unsafe fn register_dtor(t: *mut u8, dtor: unsafe extern "C" fn(*mut u8)) {
use crate::cell::Cell;
use crate::mem;
use crate::ptr;
#[thread_local]
static REGISTERED: Cell<bool> = Cell::new(false);
#[thread_local]
static mut DTORS: Vec<(*mut u8, unsafe extern "C" fn(*mut u8))> = Vec::new();
if !REGISTERED.get() {
_tlv_atexit(run_dtors, ptr::null_mut());
REGISTERED.set(true);
}
type List = Vec<(*mut u8, unsafe extern "C" fn(*mut u8))>;
#[thread_local]
static DTORS: Cell<*mut List> = Cell::new(ptr::null_mut());
if DTORS.get().is_null() {
let v: Box<List> = box Vec::new();
DTORS.set(Box::into_raw(v));
}
extern "C" {
fn _tlv_atexit(dtor: unsafe extern "C" fn(*mut u8), arg: *mut u8);
}
let list: &mut List = &mut *DTORS.get();
let list = &mut DTORS;
list.push((t, dtor));
unsafe extern "C" fn run_dtors(_: *mut u8) {
let mut ptr = DTORS.replace(ptr::null_mut());
while !ptr.is_null() {
let list = Box::from_raw(ptr);
for (ptr, dtor) in list.into_iter() {
let mut list = mem::take(&mut DTORS);
while !list.is_empty() {
for (ptr, dtor) in list {
dtor(ptr);
}
ptr = DTORS.replace(ptr::null_mut());
list = mem::take(&mut DTORS);
}
}
}

View file

@ -1,5 +1,6 @@
use crate::cell::Cell;
use crate::sync as public;
use crate::sync::once::ExclusiveState;
pub struct Once {
state: Cell<State>,
@ -44,6 +45,16 @@ impl Once {
self.state.get() == State::Complete
}
#[inline]
pub(crate) fn state(&mut self) -> ExclusiveState {
match self.state.get() {
State::Incomplete => ExclusiveState::Incomplete,
State::Poisoned => ExclusiveState::Poisoned,
State::Complete => ExclusiveState::Complete,
_ => unreachable!("invalid Once state"),
}
}
#[cold]
#[track_caller]
pub fn call(&self, ignore_poisoning: bool, f: &mut impl FnMut(&public::OnceState)) {

View file

@ -22,7 +22,7 @@ pub struct Thread {
impl Thread {
// unsafe: see thread::Builder::spawn_unchecked for safety requirements
pub unsafe fn new(stack: usize, p: Box<dyn FnOnce()>) -> io::Result<Thread> {
let p = Box::into_raw(box p);
let p = Box::into_raw(Box::new(p));
// FIXME On UNIX, we guard against stack sizes that are too small but
// that's because pthreads enforces that stacks are at least

View file

@ -4,6 +4,7 @@ use crate::sync::atomic::{
AtomicU32,
Ordering::{Acquire, Relaxed, Release},
};
use crate::sync::once::ExclusiveState;
use crate::sys::futex::{futex_wait, futex_wake_all};
// On some platforms, the OS is very nice and handles the waiter queue for us.
@ -78,6 +79,16 @@ impl Once {
self.state.load(Acquire) == COMPLETE
}
#[inline]
pub(crate) fn state(&mut self) -> ExclusiveState {
match *self.state.get_mut() {
INCOMPLETE => ExclusiveState::Incomplete,
POISONED => ExclusiveState::Poisoned,
COMPLETE => ExclusiveState::Complete,
_ => unreachable!("invalid Once state"),
}
}
// This uses FnMut to match the API of the generic implementation. As this
// implementation is quite light-weight, it is generic over the closure and
// so avoids the cost of dynamic dispatch.

View file

@ -60,6 +60,7 @@ use crate::fmt;
use crate::ptr;
use crate::sync as public;
use crate::sync::atomic::{AtomicBool, AtomicPtr, Ordering};
use crate::sync::once::ExclusiveState;
use crate::thread::{self, Thread};
type Masked = ();
@ -121,6 +122,16 @@ impl Once {
self.state_and_queue.load(Ordering::Acquire).addr() == COMPLETE
}
#[inline]
pub(crate) fn state(&mut self) -> ExclusiveState {
match self.state_and_queue.get_mut().addr() {
INCOMPLETE => ExclusiveState::Incomplete,
POISONED => ExclusiveState::Poisoned,
COMPLETE => ExclusiveState::Complete,
_ => unreachable!("invalid Once state"),
}
}
// This is a non-generic function to reduce the monomorphization cost of
// using `call_once` (this isn't exactly a trivial or small implementation).
//

View file

@ -30,7 +30,7 @@ pub unsafe fn register_dtor_fallback(t: *mut u8, dtor: unsafe extern "C" fn(*mut
static DTORS: StaticKey = StaticKey::new(Some(run_dtors));
type List = Vec<(*mut u8, unsafe extern "C" fn(*mut u8))>;
if DTORS.get().is_null() {
let v: Box<List> = box Vec::new();
let v: Box<List> = Box::new(Vec::new());
DTORS.set(Box::into_raw(v) as *mut u8);
}
let list: &mut List = &mut *(DTORS.get() as *mut List);

View file

@ -1110,8 +1110,7 @@ pub mod os {
let ptr = if ptr.is_null() {
// If the lookup returned null, we haven't initialized our own
// local copy, so do that now.
let ptr: Box<Value<T>> = box Value { inner: LazyKeyInner::new(), key: self };
let ptr = Box::into_raw(ptr);
let ptr = Box::into_raw(Box::new(Value { inner: LazyKeyInner::new(), key: self }));
// SAFETY: At this point we are sure there is no value inside
// ptr so setting it will not affect anyone else.
unsafe {

View file

@ -0,0 +1,67 @@
FROM ubuntu:22.04
ARG DEBIAN_FRONTEND=noninteractive
# NOTE: intentionally installs both python2 and python3 so we can test support for both.
RUN apt-get update && apt-get install -y --no-install-recommends \
g++ \
gcc-multilib \
make \
ninja-build \
file \
curl \
ca-certificates \
python2.7 \
python3 \
git \
cmake \
sudo \
gdb \
llvm-14-tools \
llvm-14-dev \
libedit-dev \
libssl-dev \
pkg-config \
zlib1g-dev \
xz-utils \
nodejs \
&& rm -rf /var/lib/apt/lists/*
# Install powershell (universal package) so we can test x.ps1 on Linux
RUN curl -sL "https://github.com/PowerShell/PowerShell/releases/download/v7.3.1/powershell_7.3.1-1.deb_amd64.deb" > powershell.deb && \
dpkg -i powershell.deb && \
rm -f powershell.deb
COPY scripts/sccache.sh /scripts/
RUN sh /scripts/sccache.sh
# We are disabling CI LLVM since this builder is intentionally using a host
# LLVM, rather than the typical src/llvm-project LLVM.
ENV NO_DOWNLOAD_CI_LLVM 1
# Using llvm-link-shared due to libffi issues -- see #34486
ENV RUST_CONFIGURE_ARGS \
--build=x86_64-unknown-linux-gnu \
--llvm-root=/usr/lib/llvm-14 \
--enable-llvm-link-shared \
--set rust.thin-lto-import-instr-limit=10
# NOTE: intentionally uses all of `x.py`, `x`, and `x.ps1` to make sure they all work on Linux.
ENV SCRIPT ../x.py --stage 2 test --exclude src/tools/tidy && \
# Run the `mir-opt` tests again but this time for a 32-bit target.
# This enforces that tests using `// EMIT_MIR_FOR_EACH_BIT_WIDTH` have
# both 32-bit and 64-bit outputs updated by the PR author, before
# the PR is approved and tested for merging.
# It will also detect tests lacking `// EMIT_MIR_FOR_EACH_BIT_WIDTH`,
# despite having different output on 32-bit vs 64-bit targets.
../x --stage 2 test tests/mir-opt \
--host='' --target=i686-unknown-linux-gnu && \
# Run the UI test suite again, but in `--pass=check` mode
#
# This is intended to make sure that both `--pass=check` continues to
# work.
#
../x.ps1 --stage 2 test tests/ui --pass=check \
--host='' --target=i686-unknown-linux-gnu && \
# Run tidy at the very end, after all the other tests.
python2.7 ../x.py --stage 2 test src/tools/tidy

View file

@ -0,0 +1,67 @@
FROM ubuntu:22.10
ARG DEBIAN_FRONTEND=noninteractive
# NOTE: intentionally installs both python2 and python3 so we can test support for both.
RUN apt-get update && apt-get install -y --no-install-recommends \
g++ \
gcc-multilib \
make \
ninja-build \
file \
curl \
ca-certificates \
python2.7 \
python3 \
git \
cmake \
sudo \
gdb \
llvm-15-tools \
llvm-15-dev \
libedit-dev \
libssl-dev \
pkg-config \
zlib1g-dev \
xz-utils \
nodejs \
&& rm -rf /var/lib/apt/lists/*
# Install powershell (universal package) so we can test x.ps1 on Linux
RUN curl -sL "https://github.com/PowerShell/PowerShell/releases/download/v7.3.1/powershell_7.3.1-1.deb_amd64.deb" > powershell.deb && \
dpkg -i powershell.deb && \
rm -f powershell.deb
COPY scripts/sccache.sh /scripts/
RUN sh /scripts/sccache.sh
# We are disabling CI LLVM since this builder is intentionally using a host
# LLVM, rather than the typical src/llvm-project LLVM.
ENV NO_DOWNLOAD_CI_LLVM 1
# Using llvm-link-shared due to libffi issues -- see #34486
ENV RUST_CONFIGURE_ARGS \
--build=x86_64-unknown-linux-gnu \
--llvm-root=/usr/lib/llvm-15 \
--enable-llvm-link-shared \
--set rust.thin-lto-import-instr-limit=10
# NOTE: intentionally uses all of `x.py`, `x`, and `x.ps1` to make sure they all work on Linux.
ENV SCRIPT ../x.py --stage 2 test --exclude src/tools/tidy && \
# Run the `mir-opt` tests again but this time for a 32-bit target.
# This enforces that tests using `// EMIT_MIR_FOR_EACH_BIT_WIDTH` have
# both 32-bit and 64-bit outputs updated by the PR author, before
# the PR is approved and tested for merging.
# It will also detect tests lacking `// EMIT_MIR_FOR_EACH_BIT_WIDTH`,
# despite having different output on 32-bit vs 64-bit targets.
../x --stage 2 test tests/mir-opt \
--host='' --target=i686-unknown-linux-gnu && \
# Run the UI test suite again, but in `--pass=check` mode
#
# This is intended to make sure that both `--pass=check` continues to
# work.
#
../x.ps1 --stage 2 test tests/ui --pass=check \
--host='' --target=i686-unknown-linux-gnu && \
# Run tidy at the very end, after all the other tests.
python2.7 ../x.py --stage 2 test src/tools/tidy

View file

@ -450,6 +450,16 @@ jobs:
- name: x86_64-gnu-distcheck
<<: *job-linux-xl
- name: x86_64-gnu-llvm-15
env:
RUST_BACKTRACE: 1
<<: *job-linux-xl
- name: x86_64-gnu-llvm-14
env:
RUST_BACKTRACE: 1
<<: *job-linux-xl
- name: x86_64-gnu-llvm-13
env:
RUST_BACKTRACE: 1

View file

@ -2498,14 +2498,7 @@ impl Import {
}
pub(crate) fn imported_item_is_doc_hidden(&self, tcx: TyCtxt<'_>) -> bool {
match self.source.did {
Some(did) => tcx
.get_attrs(did, sym::doc)
.filter_map(ast::Attribute::meta_item_list)
.flatten()
.has_word(sym::hidden),
None => false,
}
self.source.did.map_or(false, |did| tcx.is_doc_hidden(did))
}
}

View file

@ -1897,21 +1897,21 @@ in storage.js
right: 0.25em;
}
.scraped-example:not(.expanded) .code-wrapper:before,
.scraped-example:not(.expanded) .code-wrapper:after {
.scraped-example:not(.expanded) .code-wrapper::before,
.scraped-example:not(.expanded) .code-wrapper::after {
content: " ";
width: 100%;
height: 5px;
position: absolute;
z-index: 1;
}
.scraped-example:not(.expanded) .code-wrapper:before {
.scraped-example:not(.expanded) .code-wrapper::before {
top: 0;
background: linear-gradient(to bottom,
var(--scrape-example-code-wrapper-background-start),
var(--scrape-example-code-wrapper-background-end));
}
.scraped-example:not(.expanded) .code-wrapper:after {
.scraped-example:not(.expanded) .code-wrapper::after {
bottom: 0;
background: linear-gradient(to top,
var(--scrape-example-code-wrapper-background-start),

View file

@ -0,0 +1,20 @@
// run-rustfix
trait GatTrait {
type Gat<'a> where Self: 'a;
fn test(&self) -> Self::Gat<'_>;
}
trait SuperTrait<T>
where
Self: 'static,
for<'a> Self: GatTrait<Gat<'a> = &'a T>,
{
fn copy(&self) -> Self::Gat<'_> where T: Copy {
self.test()
//~^ mismatched types
}
}
fn main() {}

View file

@ -1,3 +1,5 @@
// run-rustfix
trait GatTrait {
type Gat<'a> where Self: 'a;

View file

@ -1,5 +1,5 @@
error[E0308]: mismatched types
--> $DIR/issue-88360.rs:13:9
--> $DIR/issue-88360.rs:15:9
|
LL | trait SuperTrait<T>
| - this type parameter
@ -7,13 +7,15 @@ LL | trait SuperTrait<T>
LL | fn copy(&self) -> Self::Gat<'_> where T: Copy {
| ------------- expected `&T` because of return type
LL | *self.test()
| ^^^^^^^^^^^^
| |
| expected `&T`, found type parameter `T`
| help: consider borrowing here: `&*self.test()`
| ^^^^^^^^^^^^ expected `&T`, found type parameter `T`
|
= note: expected reference `&T`
found type parameter `T`
help: consider removing deref here
|
LL - *self.test()
LL + self.test()
|
error: aborting due to previous error

View file

@ -0,0 +1,11 @@
struct Foo;
impl Foo {
#[doc(alias = "quux")]
fn bar(&self) {}
}
fn main() {
Foo.quux();
//~^ ERROR no method named `quux` found for struct `Foo` in the current scope
}

View file

@ -0,0 +1,12 @@
error[E0599]: no method named `quux` found for struct `Foo` in the current scope
--> $DIR/method-not-found-but-doc-alias.rs:9:9
|
LL | struct Foo;
| ---------- method `quux` not found for this struct
...
LL | Foo.quux();
| ^^^^ help: there is a method with a similar name: `bar`
error: aborting due to previous error
For more information about this error, try `rustc --explain E0599`.

View file

@ -0,0 +1,28 @@
// run-rustfix
//issue #106496
struct S;
trait X {}
impl X for S {}
fn foo<T: X>(_: &T) {}
fn test_foo() {
let hello = &S;
foo(hello);
//~^ ERROR mismatched types
}
fn bar(_: &String) {}
fn test_bar() {
let v = String::from("hello");
let s = &v;
bar(s);
//~^ ERROR mismatched types
}
fn main() {
test_foo();
test_bar();
}

View file

@ -0,0 +1,28 @@
// run-rustfix
//issue #106496
struct S;
trait X {}
impl X for S {}
fn foo<T: X>(_: &T) {}
fn test_foo() {
let hello = &S;
foo(*hello);
//~^ ERROR mismatched types
}
fn bar(_: &String) {}
fn test_bar() {
let v = String::from("hello");
let s = &v;
bar(*s);
//~^ ERROR mismatched types
}
fn main() {
test_foo();
test_bar();
}

View file

@ -0,0 +1,43 @@
error[E0308]: mismatched types
--> $DIR/suggest-remove-deref.rs:13:9
|
LL | foo(*hello);
| --- ^^^^^^ expected reference, found struct `S`
| |
| arguments to this function are incorrect
|
= note: expected reference `&_`
found struct `S`
note: function defined here
--> $DIR/suggest-remove-deref.rs:10:4
|
LL | fn foo<T: X>(_: &T) {}
| ^^^ -----
help: consider removing deref here
|
LL - foo(*hello);
LL + foo(hello);
|
error[E0308]: mismatched types
--> $DIR/suggest-remove-deref.rs:21:9
|
LL | bar(*s);
| --- ^^ expected `&String`, found struct `String`
| |
| arguments to this function are incorrect
|
note: function defined here
--> $DIR/suggest-remove-deref.rs:17:4
|
LL | fn bar(_: &String) {}
| ^^^ ----------
help: consider removing deref here
|
LL - bar(*s);
LL + bar(s);
|
error: aborting due to 2 previous errors
For more information about this error, try `rustc --explain E0308`.

View file

@ -1,12 +1,5 @@
// compile-flags: -Ztrait-solver=next
// known-bug: unknown
// failure-status: 101
// dont-check-compiler-stderr
// This test will fail until we fix `FulfillmentCtxt::relationships`. That's
// because we create a type variable for closure upvar types, which is not
// constrained until after we try to do fallback on diverging type variables.
// Thus, we will call that function, which is unimplemented.
// check-pass
fn require_fn(_: impl Fn() -> i32) {}