Merge from rustc
This commit is contained in:
commit
461f272d77
56 changed files with 824 additions and 230 deletions
8
.github/workflows/ci.yml
vendored
8
.github/workflows/ci.yml
vendored
|
|
@ -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
|
||||
|
|
|
|||
1
.mailmap
1
.mailmap
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -4326,6 +4326,7 @@ dependencies = [
|
|||
name = "rustc_metadata"
|
||||
version = "0.0.0"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"libloading",
|
||||
"odht",
|
||||
"rustc_ast",
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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 don’t take self as a parameter and
|
||||
// they are not methods because they don’t 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,
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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> {
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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];
|
||||
|
||||
|
|
|
|||
|
|
@ -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)`.
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
///
|
||||
|
|
|
|||
|
|
@ -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)))
|
||||
|
|
|
|||
|
|
@ -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`")
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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> {
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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 isn’t 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]> {
|
||||
|
|
|
|||
|
|
@ -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"));
|
||||
|
|
|
|||
|
|
@ -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)]
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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")]
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 });
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)) {
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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).
|
||||
//
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
67
src/ci/docker/host-x86_64/x86_64-gnu-llvm-14/Dockerfile
Normal file
67
src/ci/docker/host-x86_64/x86_64-gnu-llvm-14/Dockerfile
Normal 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
|
||||
67
src/ci/docker/host-x86_64/x86_64-gnu-llvm-15/Dockerfile
Normal file
67
src/ci/docker/host-x86_64/x86_64-gnu-llvm-15/Dockerfile
Normal 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
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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),
|
||||
|
|
|
|||
20
tests/ui/generic-associated-types/issue-88360.fixed
Normal file
20
tests/ui/generic-associated-types/issue-88360.fixed
Normal 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() {}
|
||||
|
|
@ -1,3 +1,5 @@
|
|||
// run-rustfix
|
||||
|
||||
trait GatTrait {
|
||||
type Gat<'a> where Self: 'a;
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
11
tests/ui/methods/method-not-found-but-doc-alias.rs
Normal file
11
tests/ui/methods/method-not-found-but-doc-alias.rs
Normal 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
|
||||
}
|
||||
12
tests/ui/methods/method-not-found-but-doc-alias.stderr
Normal file
12
tests/ui/methods/method-not-found-but-doc-alias.stderr
Normal 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`.
|
||||
28
tests/ui/suggestions/suggest-remove-deref.fixed
Normal file
28
tests/ui/suggestions/suggest-remove-deref.fixed
Normal 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();
|
||||
}
|
||||
28
tests/ui/suggestions/suggest-remove-deref.rs
Normal file
28
tests/ui/suggestions/suggest-remove-deref.rs
Normal 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();
|
||||
}
|
||||
43
tests/ui/suggestions/suggest-remove-deref.stderr
Normal file
43
tests/ui/suggestions/suggest-remove-deref.stderr
Normal 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`.
|
||||
|
|
@ -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) {}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue