Skip to content

Rollup of 8 pull requests #93288

New issue

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

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

Already on GitHub? Sign in to your account

Merged
merged 35 commits into from
Jan 25, 2022
Merged
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
18c14ad
Add a `try_clone()` function to `OwnedFd`.
sunfishcode Sep 9, 2021
622dfcc
Fix Windows compilation errors.
sunfishcode Sep 9, 2021
c986c6b
Fix more Windows compilation errors.
sunfishcode Sep 9, 2021
2d6a4c8
Fix another Windows compilation error.
sunfishcode Sep 9, 2021
53e072f
Fix compilation on WASI, which doesn't yet support `dup`.
sunfishcode Oct 5, 2021
83aebf8
Use the correct `cvt` for converting socket errors on Windows.
sunfishcode Jan 12, 2022
02f1a56
Properly track `DepNode`s in trait evaluation provisional cache
Aaron1011 Jan 19, 2022
f518827
Use impl1 and impl2 instead of a and b prefixes
spastorino Jan 21, 2022
052b31b
Move auxiliary fns out of overlap_with_probe
spastorino Jan 21, 2022
f4b4294
Remove FIXME and fix inconsistency of local blanket impls by using HI…
CraftSpider Jan 21, 2022
66d056a
Update test to include `self` case
CraftSpider Jan 21, 2022
1a0278e
Work around missing code coverage data causing llvm-cov failures
wesleywiser Jan 21, 2022
b2a45f0
Extract stable_disjoint fn
spastorino Jan 21, 2022
c2890ed
Add overlap mode
spastorino Jan 21, 2022
d2d25a5
Implement stable with negative coherence mode
spastorino Jan 21, 2022
1ec962f
Do not pass OverlapMode down, just create a closure to properly set t…
spastorino Jan 21, 2022
19e3c86
Make strict_disjoint use explicit_disjoint
spastorino Jan 21, 2022
e2567b0
Remove intermediate function doesn't make more sense
spastorino Jan 21, 2022
762bdbf
Change signature of point_at_arg_instead_of_call_if_possible
jackh726 Dec 25, 2021
ce31f68
Move param count error emission to near end of check_argument_types
jackh726 Jan 20, 2022
e5f2fdb
Restructure the code leveraging in abilities more than modes
spastorino Jan 22, 2022
9220631
Add has tests for blanket_with_local trait methods
CraftSpider Jan 23, 2022
7847ca8
Document OverlapMode
spastorino Jan 23, 2022
2693832
Rename strict_check to negative_impl_exists
spastorino Jan 23, 2022
8189bac
FIXME include regions too
spastorino Jan 23, 2022
11b17c6
rustdoc settings: use radio buttons for theme
jsha Jan 23, 2022
e02e958
Use error-on-mismatch policy for PAuth module flags.
jacobbramley Jan 19, 2022
687bb58
Rollup merge of #88794 - sunfishcode:sunfishcode/try-clone, r=joshtri…
matthiaskrgr Jan 25, 2022
cf70411
Rollup merge of #93064 - Aaron1011:provisional-dep-node, r=michaelwoe…
matthiaskrgr Jan 25, 2022
c8ede15
Rollup merge of #93118 - jackh726:param-heuristics-3, r=estebank
matthiaskrgr Jan 25, 2022
8dddc86
Rollup merge of #93144 - wesleywiser:uninhabited_type_code_cov2, r=tm…
matthiaskrgr Jan 25, 2022
677126c
Rollup merge of #93169 - CraftSpider:rustdoc-clean-inconsistency, r=G…
matthiaskrgr Jan 25, 2022
3d6f276
Rollup merge of #93175 - spastorino:negative-traits-coherence-new, r=…
matthiaskrgr Jan 25, 2022
c3ddca6
Rollup merge of #93251 - jsha:theme-radio, r=GuillaumeGomez
matthiaskrgr Jan 25, 2022
13b87d8
Rollup merge of #93269 - jacobbramley:dev/pauth-option-1, r=petrochenkov
matthiaskrgr Jan 25, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 24 additions & 7 deletions compiler/rustc_codegen_llvm/src/context.rs
Original file line number Diff line number Diff line change
@@ -215,16 +215,19 @@ pub unsafe fn create_module<'ll>(
// to ensure intrinsic calls don't use it.
if !sess.needs_plt() {
let avoid_plt = "RtLibUseGOT\0".as_ptr().cast();
llvm::LLVMRustAddModuleFlag(llmod, avoid_plt, 1);
llvm::LLVMRustAddModuleFlag(llmod, llvm::LLVMModFlagBehavior::Warning, avoid_plt, 1);
}

if sess.is_sanitizer_cfi_enabled() {
// FIXME(rcvalle): Add support for non canonical jump tables.
let canonical_jump_tables = "CFI Canonical Jump Tables\0".as_ptr().cast();
// FIXME(rcvalle): Add it with Override behavior flag--LLVMRustAddModuleFlag adds it with
// Warning behavior flag. Add support for specifying the behavior flag to
// LLVMRustAddModuleFlag.
llvm::LLVMRustAddModuleFlag(llmod, canonical_jump_tables, 1);
// FIXME(rcvalle): Add it with Override behavior flag.
llvm::LLVMRustAddModuleFlag(
llmod,
llvm::LLVMModFlagBehavior::Warning,
canonical_jump_tables,
1,
);
}

// Control Flow Guard is currently only supported by the MSVC linker on Windows.
@@ -233,11 +236,21 @@ pub unsafe fn create_module<'ll>(
CFGuard::Disabled => {}
CFGuard::NoChecks => {
// Set `cfguard=1` module flag to emit metadata only.
llvm::LLVMRustAddModuleFlag(llmod, "cfguard\0".as_ptr() as *const _, 1)
llvm::LLVMRustAddModuleFlag(
llmod,
llvm::LLVMModFlagBehavior::Warning,
"cfguard\0".as_ptr() as *const _,
1,
)
}
CFGuard::Checks => {
// Set `cfguard=2` module flag to emit metadata and checks.
llvm::LLVMRustAddModuleFlag(llmod, "cfguard\0".as_ptr() as *const _, 2)
llvm::LLVMRustAddModuleFlag(
llmod,
llvm::LLVMModFlagBehavior::Warning,
"cfguard\0".as_ptr() as *const _,
2,
)
}
}
}
@@ -247,24 +260,28 @@ pub unsafe fn create_module<'ll>(

llvm::LLVMRustAddModuleFlag(
llmod,
llvm::LLVMModFlagBehavior::Error,
"branch-target-enforcement\0".as_ptr().cast(),
bti.into(),
);

llvm::LLVMRustAddModuleFlag(
llmod,
llvm::LLVMModFlagBehavior::Error,
"sign-return-address\0".as_ptr().cast(),
pac.is_some().into(),
);
let pac_opts = pac.unwrap_or(PacRet { leaf: false, key: PAuthKey::A });
llvm::LLVMRustAddModuleFlag(
llmod,
llvm::LLVMModFlagBehavior::Error,
"sign-return-address-all\0".as_ptr().cast(),
pac_opts.leaf.into(),
);
let is_bkey = if pac_opts.key == PAuthKey::A { false } else { true };
llvm::LLVMRustAddModuleFlag(
llmod,
llvm::LLVMModFlagBehavior::Error,
"sign-return-address-with-bkey\0".as_ptr().cast(),
is_bkey.into(),
);
17 changes: 13 additions & 4 deletions compiler/rustc_codegen_llvm/src/coverageinfo/mapgen.rs
Original file line number Diff line number Diff line change
@@ -9,6 +9,7 @@ use rustc_data_structures::fx::FxIndexSet;
use rustc_hir::def::DefKind;
use rustc_hir::def_id::DefIdSet;
use rustc_llvm::RustString;
use rustc_middle::bug;
use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags;
use rustc_middle::mir::coverage::CodeRegion;
use rustc_middle::ty::TyCtxt;
@@ -76,10 +77,18 @@ pub fn finalize<'ll, 'tcx>(cx: &CodegenCx<'ll, 'tcx>) {
let coverage_mapping_buffer = llvm::build_byte_buffer(|coverage_mapping_buffer| {
mapgen.write_coverage_mapping(expressions, counter_regions, coverage_mapping_buffer);
});
debug_assert!(
!coverage_mapping_buffer.is_empty(),
"Every `FunctionCoverage` should have at least one counter"
);

if coverage_mapping_buffer.is_empty() {
if function_coverage.is_used() {
bug!(
"A used function should have had coverage mapping data but did not: {}",
mangled_function_name
);
} else {
debug!("unused function had no coverage mapping data: {}", mangled_function_name);
continue;
}
}

function_data.push((mangled_function_name, source_hash, is_used, coverage_mapping_buffer));
}
15 changes: 13 additions & 2 deletions compiler/rustc_codegen_llvm/src/debuginfo/mod.rs
Original file line number Diff line number Diff line change
@@ -108,18 +108,29 @@ impl<'a, 'tcx> CrateDebugContext<'a, 'tcx> {
// This can be overridden using --llvm-opts -dwarf-version,N.
// Android has the same issue (#22398)
if let Some(version) = sess.target.dwarf_version {
llvm::LLVMRustAddModuleFlag(self.llmod, "Dwarf Version\0".as_ptr().cast(), version)
llvm::LLVMRustAddModuleFlag(
self.llmod,
llvm::LLVMModFlagBehavior::Warning,
"Dwarf Version\0".as_ptr().cast(),
version,
)
}

// Indicate that we want CodeView debug information on MSVC
if sess.target.is_like_msvc {
llvm::LLVMRustAddModuleFlag(self.llmod, "CodeView\0".as_ptr().cast(), 1)
llvm::LLVMRustAddModuleFlag(
self.llmod,
llvm::LLVMModFlagBehavior::Warning,
"CodeView\0".as_ptr().cast(),
1,
)
}

// Prevent bitcode readers from deleting the debug info.
let ptr = "Debug Info Version\0".as_ptr();
llvm::LLVMRustAddModuleFlag(
self.llmod,
llvm::LLVMModFlagBehavior::Warning,
ptr.cast(),
llvm::LLVMRustDebugMetadataVersion(),
);
31 changes: 30 additions & 1 deletion compiler/rustc_codegen_llvm/src/llvm/ffi.rs
Original file line number Diff line number Diff line change
@@ -61,6 +61,26 @@ pub enum LLVMMachineType {
ARM = 0x01c0,
}

/// LLVM's Module::ModFlagBehavior, defined in llvm/include/llvm/IR/Module.h.
///
/// When merging modules (e.g. during LTO), their metadata flags are combined. Conflicts are
/// resolved according to the merge behaviors specified here. Flags differing only in merge
/// behavior are still considered to be in conflict.
///
/// In order for Rust-C LTO to work, we must specify behaviors compatible with Clang. Notably,
/// 'Error' and 'Warning' cannot be mixed for a given flag.
#[derive(Copy, Clone, PartialEq)]
#[repr(C)]
pub enum LLVMModFlagBehavior {
Error = 1,
Warning = 2,
Require = 3,
Override = 4,
Append = 5,
AppendUnique = 6,
Max = 7,
}

// Consts for the LLVM CallConv type, pre-cast to usize.

/// LLVM CallingConv::ID. Should we wrap this?
@@ -1895,7 +1915,16 @@ extern "C" {

pub fn LLVMRustIsRustLLVM() -> bool;

pub fn LLVMRustAddModuleFlag(M: &Module, name: *const c_char, value: u32);
/// Add LLVM module flags.
///
/// In order for Rust-C LTO to work, module flags must be compatible with Clang. What
/// "compatible" means depends on the merge behaviors involved.
pub fn LLVMRustAddModuleFlag(
M: &Module,
merge_behavior: LLVMModFlagBehavior,
name: *const c_char,
value: u32,
);

pub fn LLVMRustMetadataAsValue<'a>(C: &'a Context, MD: &'a Metadata) -> &'a Value;

1 change: 1 addition & 0 deletions compiler/rustc_feature/src/builtin_attrs.rs
Original file line number Diff line number Diff line change
@@ -697,6 +697,7 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[
rustc_attr!(TEST, rustc_capture_analysis, Normal, template!(Word), WarnFollowing),
rustc_attr!(TEST, rustc_insignificant_dtor, Normal, template!(Word), WarnFollowing),
rustc_attr!(TEST, rustc_strict_coherence, Normal, template!(Word), WarnFollowing),
rustc_attr!(TEST, rustc_with_negative_coherence, Normal, template!(Word), WarnFollowing),
rustc_attr!(TEST, rustc_variance, Normal, template!(Word), WarnFollowing),
rustc_attr!(TEST, rustc_layout, Normal, template!(List: "field1, field2, ..."), WarnFollowing),
rustc_attr!(TEST, rustc_regions, Normal, template!(Word), WarnFollowing),
9 changes: 6 additions & 3 deletions compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp
Original file line number Diff line number Diff line change
@@ -722,9 +722,12 @@ extern "C" bool LLVMRustIsRustLLVM() {
#endif
}

extern "C" void LLVMRustAddModuleFlag(LLVMModuleRef M, const char *Name,
uint32_t Value) {
unwrap(M)->addModuleFlag(Module::Warning, Name, Value);
extern "C" void LLVMRustAddModuleFlag(
LLVMModuleRef M,
Module::ModFlagBehavior MergeBehavior,
const char *Name,
uint32_t Value) {
unwrap(M)->addModuleFlag(MergeBehavior, Name, Value);
}

extern "C" LLVMValueRef LLVMRustMetadataAsValue(LLVMContextRef C, LLVMMetadataRef MD) {
1 change: 1 addition & 0 deletions compiler/rustc_span/src/symbol.rs
Original file line number Diff line number Diff line change
@@ -1204,6 +1204,7 @@ symbols! {
rustc_trivial_field_reads,
rustc_unsafe_specialization_marker,
rustc_variance,
rustc_with_negative_coherence,
rustdoc,
rustdoc_internals,
rustfmt,
262 changes: 201 additions & 61 deletions compiler/rustc_trait_selection/src/traits/coherence.rs
Original file line number Diff line number Diff line change
@@ -7,9 +7,11 @@
use crate::infer::{CombinedSnapshot, InferOk, TyCtxtInferExt};
use crate::traits::query::evaluate_obligation::InferCtxtExt;
use crate::traits::select::IntercrateAmbiguityCause;
use crate::traits::util::impl_trait_ref_and_oblig;
use crate::traits::SkipLeakCheck;
use crate::traits::{
self, Normalized, Obligation, ObligationCause, PredicateObligation, SelectionContext,
self, FulfillmentContext, Normalized, Obligation, ObligationCause, PredicateObligation,
PredicateObligations, SelectionContext,
};
use rustc_hir::def_id::{DefId, LOCAL_CRATE};
use rustc_middle::ty::fast_reject::{self, SimplifyParams, StripReferences};
@@ -135,45 +137,83 @@ fn with_fresh_ty_vars<'cx, 'tcx>(
header
}

/// What kind of overlap check are we doing -- this exists just for testing and feature-gating
/// purposes.
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
enum OverlapMode {
/// The 1.0 rules (either types fail to unify, or where clauses are not implemented for crate-local types)
Stable,
/// Feature-gated test: Stable, *or* there is an explicit negative impl that rules out one of the where-clauses.
WithNegative,
/// Just check for negative impls, not for "where clause not implemented": used for testing.
Strict,
}

impl OverlapMode {
fn use_negative_impl(&self) -> bool {
*self == OverlapMode::Strict || *self == OverlapMode::WithNegative
}

fn use_implicit_negative(&self) -> bool {
*self == OverlapMode::Stable || *self == OverlapMode::WithNegative
}
}

fn overlap_mode<'tcx>(tcx: TyCtxt<'tcx>, impl1_def_id: DefId, impl2_def_id: DefId) -> OverlapMode {
if tcx.has_attr(impl1_def_id, sym::rustc_strict_coherence)
!= tcx.has_attr(impl2_def_id, sym::rustc_strict_coherence)
{
bug!("Use strict coherence on both impls",);
}

if tcx.has_attr(impl1_def_id, sym::rustc_with_negative_coherence)
!= tcx.has_attr(impl2_def_id, sym::rustc_with_negative_coherence)
{
bug!("Use with negative coherence on both impls",);
}

if tcx.has_attr(impl1_def_id, sym::rustc_strict_coherence) {
OverlapMode::Strict
} else if tcx.has_attr(impl1_def_id, sym::rustc_with_negative_coherence) {
OverlapMode::WithNegative
} else {
OverlapMode::Stable
}
}

/// Can both impl `a` and impl `b` be satisfied by a common type (including
/// where-clauses)? If so, returns an `ImplHeader` that unifies the two impls.
fn overlap<'cx, 'tcx>(
selcx: &mut SelectionContext<'cx, 'tcx>,
skip_leak_check: SkipLeakCheck,
a_def_id: DefId,
b_def_id: DefId,
impl1_def_id: DefId,
impl2_def_id: DefId,
) -> Option<OverlapResult<'tcx>> {
debug!("overlap(a_def_id={:?}, b_def_id={:?})", a_def_id, b_def_id);
debug!("overlap(impl1_def_id={:?}, impl2_def_id={:?})", impl1_def_id, impl2_def_id);

selcx.infcx().probe_maybe_skip_leak_check(skip_leak_check.is_yes(), |snapshot| {
overlap_within_probe(selcx, skip_leak_check, a_def_id, b_def_id, snapshot)
overlap_within_probe(selcx, skip_leak_check, impl1_def_id, impl2_def_id, snapshot)
})
}

fn overlap_within_probe<'cx, 'tcx>(
selcx: &mut SelectionContext<'cx, 'tcx>,
skip_leak_check: SkipLeakCheck,
a_def_id: DefId,
b_def_id: DefId,
impl1_def_id: DefId,
impl2_def_id: DefId,
snapshot: &CombinedSnapshot<'_, 'tcx>,
) -> Option<OverlapResult<'tcx>> {
fn loose_check<'cx, 'tcx>(
selcx: &mut SelectionContext<'cx, 'tcx>,
o: &PredicateObligation<'tcx>,
) -> bool {
!selcx.predicate_may_hold_fatal(o)
}
let infcx = selcx.infcx();
let tcx = infcx.tcx;

fn strict_check<'cx, 'tcx>(
selcx: &SelectionContext<'cx, 'tcx>,
o: &PredicateObligation<'tcx>,
) -> bool {
let infcx = selcx.infcx();
let tcx = infcx.tcx;
o.flip_polarity(tcx)
.as_ref()
.map(|o| selcx.infcx().predicate_must_hold_modulo_regions(o))
.unwrap_or(false)
let overlap_mode = overlap_mode(tcx, impl1_def_id, impl2_def_id);

if overlap_mode.use_negative_impl() {
if negative_impl(selcx, impl1_def_id, impl2_def_id)
|| negative_impl(selcx, impl2_def_id, impl1_def_id)
{
return None;
}
}

// For the purposes of this check, we don't bring any placeholder
@@ -182,26 +222,61 @@ fn overlap_within_probe<'cx, 'tcx>(
// empty environment.
let param_env = ty::ParamEnv::empty();

let a_impl_header = with_fresh_ty_vars(selcx, param_env, a_def_id);
let b_impl_header = with_fresh_ty_vars(selcx, param_env, b_def_id);
let impl1_header = with_fresh_ty_vars(selcx, param_env, impl1_def_id);
let impl2_header = with_fresh_ty_vars(selcx, param_env, impl2_def_id);

debug!("overlap: a_impl_header={:?}", a_impl_header);
debug!("overlap: b_impl_header={:?}", b_impl_header);
debug!("overlap: impl1_header={:?}", impl1_header);
debug!("overlap: impl2_header={:?}", impl2_header);

// Do `a` and `b` unify? If not, no overlap.
let obligations = match selcx
.infcx()
.at(&ObligationCause::dummy(), param_env)
.eq_impl_headers(&a_impl_header, &b_impl_header)
{
Ok(InferOk { obligations, value: () }) => obligations,
Err(_) => {
let obligations = equate_impl_headers(selcx, &impl1_header, &impl2_header)?;
debug!("overlap: unification check succeeded");

if overlap_mode.use_implicit_negative() {
if implicit_negative(selcx, param_env, &impl1_header, impl2_header, obligations) {
return None;
}
};
}

debug!("overlap: unification check succeeded");
if !skip_leak_check.is_yes() {
if infcx.leak_check(true, snapshot).is_err() {
debug!("overlap: leak check failed");
return None;
}
}

let intercrate_ambiguity_causes = selcx.take_intercrate_ambiguity_causes();
debug!("overlap: intercrate_ambiguity_causes={:#?}", intercrate_ambiguity_causes);

let involves_placeholder =
matches!(selcx.infcx().region_constraints_added_in_snapshot(snapshot), Some(true));

let impl_header = selcx.infcx().resolve_vars_if_possible(impl1_header);
Some(OverlapResult { impl_header, intercrate_ambiguity_causes, involves_placeholder })
}

fn equate_impl_headers<'cx, 'tcx>(
selcx: &mut SelectionContext<'cx, 'tcx>,
impl1_header: &ty::ImplHeader<'tcx>,
impl2_header: &ty::ImplHeader<'tcx>,
) -> Option<PredicateObligations<'tcx>> {
// Do `a` and `b` unify? If not, no overlap.
selcx
.infcx()
.at(&ObligationCause::dummy(), ty::ParamEnv::empty())
.eq_impl_headers(impl1_header, impl2_header)
.map(|infer_ok| infer_ok.obligations)
.ok()
}

/// Given impl1 and impl2 check if both impls can be satisfied by a common type (including
/// where-clauses) If so, return false, otherwise return true, they are disjoint.
fn implicit_negative<'cx, 'tcx>(
selcx: &mut SelectionContext<'cx, 'tcx>,
param_env: ty::ParamEnv<'tcx>,
impl1_header: &ty::ImplHeader<'tcx>,
impl2_header: ty::ImplHeader<'tcx>,
obligations: PredicateObligations<'tcx>,
) -> bool {
// There's no overlap if obligations are unsatisfiable or if the obligation negated is
// satisfied.
//
@@ -225,11 +300,11 @@ fn overlap_within_probe<'cx, 'tcx>(
// at some point an impl for `&'?a str: Error` could be added.
let infcx = selcx.infcx();
let tcx = infcx.tcx;
let opt_failing_obligation = a_impl_header
let opt_failing_obligation = impl1_header
.predicates
.iter()
.copied()
.chain(b_impl_header.predicates)
.chain(impl2_header.predicates)
.map(|p| infcx.resolve_vars_if_possible(p))
.map(|p| Obligation {
cause: ObligationCause::dummy(),
@@ -239,40 +314,105 @@ fn overlap_within_probe<'cx, 'tcx>(
})
.chain(obligations)
.find(|o| {
// if both impl headers are set to strict coherence it means that this will be accepted
// only if it's stated that T: !Trait. So only prove that the negated obligation holds.
if tcx.has_attr(a_def_id, sym::rustc_strict_coherence)
&& tcx.has_attr(b_def_id, sym::rustc_strict_coherence)
{
strict_check(selcx, o)
} else {
loose_check(selcx, o) || tcx.features().negative_impls && strict_check(selcx, o)
}
loose_check(selcx, o) || tcx.features().negative_impls && negative_impl_exists(selcx, o)
});
// FIXME: the call to `selcx.predicate_may_hold_fatal` above should be ported
// to the canonical trait query form, `infcx.predicate_may_hold`, once
// the new system supports intercrate mode (which coherence needs).

if let Some(failing_obligation) = opt_failing_obligation {
debug!("overlap: obligation unsatisfiable {:?}", failing_obligation);
return None;
true
} else {
false
}
}

if !skip_leak_check.is_yes() {
if infcx.leak_check(true, snapshot).is_err() {
debug!("overlap: leak check failed");
return None;
}
}
/// Given impl1 and impl2 check if both impls are never satisfied by a common type (including
/// where-clauses) If so, return true, they are disjoint and false otherwise.
fn negative_impl<'cx, 'tcx>(
selcx: &mut SelectionContext<'cx, 'tcx>,
impl1_def_id: DefId,
impl2_def_id: DefId,
) -> bool {
let tcx = selcx.infcx().tcx;

let impl_header = selcx.infcx().resolve_vars_if_possible(a_impl_header);
let intercrate_ambiguity_causes = selcx.take_intercrate_ambiguity_causes();
debug!("overlap: intercrate_ambiguity_causes={:#?}", intercrate_ambiguity_causes);
// create a parameter environment corresponding to a (placeholder) instantiation of impl1
let impl1_env = tcx.param_env(impl1_def_id);
let impl1_trait_ref = tcx.impl_trait_ref(impl1_def_id).unwrap();

let involves_placeholder =
matches!(selcx.infcx().region_constraints_added_in_snapshot(snapshot), Some(true));
// Create an infcx, taking the predicates of impl1 as assumptions:
tcx.infer_ctxt().enter(|infcx| {
// Normalize the trait reference. The WF rules ought to ensure
// that this always succeeds.
let impl1_trait_ref = match traits::fully_normalize(
&infcx,
FulfillmentContext::new(),
ObligationCause::dummy(),
impl1_env,
impl1_trait_ref,
) {
Ok(impl1_trait_ref) => impl1_trait_ref,
Err(err) => {
bug!("failed to fully normalize {:?}: {:?}", impl1_trait_ref, err);
}
};

// Attempt to prove that impl2 applies, given all of the above.
let selcx = &mut SelectionContext::new(&infcx);
let impl2_substs = infcx.fresh_substs_for_item(DUMMY_SP, impl2_def_id);
let (impl2_trait_ref, obligations) =
impl_trait_ref_and_oblig(selcx, impl1_env, impl2_def_id, impl2_substs);

// do the impls unify? If not, not disjoint.
let more_obligations = match infcx
.at(&ObligationCause::dummy(), impl1_env)
.eq(impl1_trait_ref, impl2_trait_ref)
{
Ok(InferOk { obligations, .. }) => obligations,
Err(_) => {
debug!(
"explicit_disjoint: {:?} does not unify with {:?}",
impl1_trait_ref, impl2_trait_ref
);
return false;
}
};

Some(OverlapResult { impl_header, intercrate_ambiguity_causes, involves_placeholder })
let opt_failing_obligation = obligations
.into_iter()
.chain(more_obligations)
.find(|o| negative_impl_exists(selcx, o));

if let Some(failing_obligation) = opt_failing_obligation {
debug!("overlap: obligation unsatisfiable {:?}", failing_obligation);
true
} else {
false
}
})
}

fn loose_check<'cx, 'tcx>(
selcx: &mut SelectionContext<'cx, 'tcx>,
o: &PredicateObligation<'tcx>,
) -> bool {
!selcx.predicate_may_hold_fatal(o)
}

fn negative_impl_exists<'cx, 'tcx>(
selcx: &SelectionContext<'cx, 'tcx>,
o: &PredicateObligation<'tcx>,
) -> bool {
let infcx = selcx.infcx();
let tcx = infcx.tcx;
o.flip_polarity(tcx)
.as_ref()
.map(|o| {
// FIXME This isn't quite correct, regions should be included
selcx.infcx().predicate_must_hold_modulo_regions(o)
})
.unwrap_or(false)
}

pub fn trait_ref_is_knowable<'tcx>(
63 changes: 51 additions & 12 deletions compiler/rustc_trait_selection/src/traits/select/mod.rs
Original file line number Diff line number Diff line change
@@ -765,14 +765,38 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
debug!(?result, "CACHE MISS");
self.insert_evaluation_cache(param_env, fresh_trait_pred, dep_node, result);

stack.cache().on_completion(stack.dfn, |fresh_trait_pred, provisional_result| {
self.insert_evaluation_cache(
param_env,
fresh_trait_pred,
dep_node,
provisional_result.max(result),
);
});
stack.cache().on_completion(
stack.dfn,
|fresh_trait_pred, provisional_result, provisional_dep_node| {
// Create a new `DepNode` that has dependencies on:
// * The `DepNode` for the original evaluation that resulted in a provisional cache
// entry being crated
// * The `DepNode` for the *current* evaluation, which resulted in us completing
// provisional caches entries and inserting them into the evaluation cache
//
// This ensures that when a query reads this entry from the evaluation cache,
// it will end up (transitively) dependening on all of the incr-comp dependencies
// created during the evaluation of this trait. For example, evaluating a trait
// will usually require us to invoke `type_of(field_def_id)` to determine the
// constituent types, and we want any queries reading from this evaluation
// cache entry to end up with a transitive `type_of(field_def_id`)` dependency.
//
// By using `in_task`, we're also creating an edge from the *current* query
// to the newly-created `combined_dep_node`. This is probably redundant,
// but it's better to add too many dep graph edges than to add too few
// dep graph edges.
let ((), combined_dep_node) = self.in_task(|this| {
this.tcx().dep_graph.read_index(provisional_dep_node);
this.tcx().dep_graph.read_index(dep_node);
});
self.insert_evaluation_cache(
param_env,
fresh_trait_pred,
combined_dep_node,
provisional_result.max(result),
);
},
);
} else {
debug!(?result, "PROVISIONAL");
debug!(
@@ -781,7 +805,13 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
fresh_trait_pred, stack.depth, reached_depth,
);

stack.cache().insert_provisional(stack.dfn, reached_depth, fresh_trait_pred, result);
stack.cache().insert_provisional(
stack.dfn,
reached_depth,
fresh_trait_pred,
result,
dep_node,
);
}

Ok(result)
@@ -2506,6 +2536,11 @@ struct ProvisionalEvaluation {
from_dfn: usize,
reached_depth: usize,
result: EvaluationResult,
/// The `DepNodeIndex` created for the `evaluate_stack` call for this provisional
/// evaluation. When we create an entry in the evaluation cache using this provisional
/// cache entry (see `on_completion`), we use this `dep_node` to ensure that future reads from
/// the cache will have all of the necessary incr comp dependencies tracked.
dep_node: DepNodeIndex,
}

impl<'tcx> Default for ProvisionalEvaluationCache<'tcx> {
@@ -2548,6 +2583,7 @@ impl<'tcx> ProvisionalEvaluationCache<'tcx> {
reached_depth: usize,
fresh_trait_pred: ty::PolyTraitPredicate<'tcx>,
result: EvaluationResult,
dep_node: DepNodeIndex,
) {
debug!(?from_dfn, ?fresh_trait_pred, ?result, "insert_provisional");

@@ -2573,7 +2609,10 @@ impl<'tcx> ProvisionalEvaluationCache<'tcx> {
}
}

map.insert(fresh_trait_pred, ProvisionalEvaluation { from_dfn, reached_depth, result });
map.insert(
fresh_trait_pred,
ProvisionalEvaluation { from_dfn, reached_depth, result, dep_node },
);
}

/// Invoked when the node with dfn `dfn` does not get a successful
@@ -2624,7 +2663,7 @@ impl<'tcx> ProvisionalEvaluationCache<'tcx> {
fn on_completion(
&self,
dfn: usize,
mut op: impl FnMut(ty::PolyTraitPredicate<'tcx>, EvaluationResult),
mut op: impl FnMut(ty::PolyTraitPredicate<'tcx>, EvaluationResult, DepNodeIndex),
) {
debug!(?dfn, "on_completion");

@@ -2633,7 +2672,7 @@ impl<'tcx> ProvisionalEvaluationCache<'tcx> {
{
debug!(?fresh_trait_pred, ?eval, "on_completion");

op(fresh_trait_pred, eval.result);
op(fresh_trait_pred, eval.result, eval.dep_node);
}
}
}
279 changes: 144 additions & 135 deletions compiler/rustc_typeck/src/check/fn_ctxt/checks.rs

Large diffs are not rendered by default.

33 changes: 33 additions & 0 deletions library/std/src/os/fd/owned.rs
Original file line number Diff line number Diff line change
@@ -8,6 +8,8 @@ use crate::fmt;
use crate::fs;
use crate::marker::PhantomData;
use crate::mem::forget;
#[cfg(not(target_os = "wasi"))]
use crate::sys::cvt;
use crate::sys_common::{AsInner, FromInner, IntoInner};

/// A borrowed file descriptor.
@@ -67,6 +69,37 @@ impl BorrowedFd<'_> {
}
}

impl OwnedFd {
/// Creates a new `OwnedFd` instance that shares the same underlying file handle
/// as the existing `OwnedFd` instance.
#[cfg(not(target_os = "wasi"))]
pub fn try_clone(&self) -> crate::io::Result<Self> {
// We want to atomically duplicate this file descriptor and set the
// CLOEXEC flag, and currently that's done via F_DUPFD_CLOEXEC. This
// is a POSIX flag that was added to Linux in 2.6.24.
#[cfg(not(target_os = "espidf"))]
let cmd = libc::F_DUPFD_CLOEXEC;

// For ESP-IDF, F_DUPFD is used instead, because the CLOEXEC semantics
// will never be supported, as this is a bare metal framework with
// no capabilities for multi-process execution. While F_DUPFD is also
// not supported yet, it might be (currently it returns ENOSYS).
#[cfg(target_os = "espidf")]
let cmd = libc::F_DUPFD;

let fd = cvt(unsafe { libc::fcntl(self.as_raw_fd(), cmd, 0) })?;
Ok(unsafe { Self::from_raw_fd(fd) })
}

#[cfg(target_os = "wasi")]
pub fn try_clone(&self) -> crate::io::Result<Self> {
Err(crate::io::Error::new_const(
crate::io::ErrorKind::Unsupported,
&"operation not supported on WASI yet",
))
}
}

#[unstable(feature = "io_safety", issue = "87074")]
impl AsRawFd for BorrowedFd<'_> {
#[inline]
32 changes: 32 additions & 0 deletions library/std/src/os/windows/io/handle.rs
Original file line number Diff line number Diff line change
@@ -6,9 +6,11 @@ use super::raw::{AsRawHandle, FromRawHandle, IntoRawHandle, RawHandle};
use crate::convert::TryFrom;
use crate::fmt;
use crate::fs;
use crate::io;
use crate::marker::PhantomData;
use crate::mem::forget;
use crate::sys::c;
use crate::sys::cvt;
use crate::sys_common::{AsInner, FromInner, IntoInner};

/// A borrowed handle.
@@ -144,6 +146,36 @@ impl TryFrom<HandleOrNull> for OwnedHandle {
}
}

impl OwnedHandle {
/// Creates a new `OwnedHandle` instance that shares the same underlying file handle
/// as the existing `OwnedHandle` instance.
pub fn try_clone(&self) -> crate::io::Result<Self> {
self.duplicate(0, false, c::DUPLICATE_SAME_ACCESS)
}

pub(crate) fn duplicate(
&self,
access: c::DWORD,
inherit: bool,
options: c::DWORD,
) -> io::Result<Self> {
let mut ret = 0 as c::HANDLE;
cvt(unsafe {
let cur_proc = c::GetCurrentProcess();
c::DuplicateHandle(
cur_proc,
self.as_raw_handle(),
cur_proc,
&mut ret,
access,
inherit as c::BOOL,
options,
)
})?;
unsafe { Ok(Self::from_raw_handle(ret)) }
}
}

impl TryFrom<HandleOrInvalid> for OwnedHandle {
type Error = ();

75 changes: 75 additions & 0 deletions library/std/src/os/windows/io/socket.rs
Original file line number Diff line number Diff line change
@@ -4,9 +4,13 @@

use super::raw::{AsRawSocket, FromRawSocket, IntoRawSocket, RawSocket};
use crate::fmt;
use crate::io;
use crate::marker::PhantomData;
use crate::mem;
use crate::mem::forget;
use crate::sys;
use crate::sys::c;
use crate::sys::cvt;

/// A borrowed socket.
///
@@ -69,6 +73,77 @@ impl BorrowedSocket<'_> {
}
}

impl OwnedSocket {
/// Creates a new `OwnedSocket` instance that shares the same underlying socket
/// as the existing `OwnedSocket` instance.
pub fn try_clone(&self) -> io::Result<Self> {
let mut info = unsafe { mem::zeroed::<c::WSAPROTOCOL_INFO>() };
let result = unsafe {
c::WSADuplicateSocketW(self.as_raw_socket(), c::GetCurrentProcessId(), &mut info)
};
sys::net::cvt(result)?;
let socket = unsafe {
c::WSASocketW(
info.iAddressFamily,
info.iSocketType,
info.iProtocol,
&mut info,
0,
c::WSA_FLAG_OVERLAPPED | c::WSA_FLAG_NO_HANDLE_INHERIT,
)
};

if socket != c::INVALID_SOCKET {
unsafe { Ok(OwnedSocket::from_raw_socket(socket)) }
} else {
let error = unsafe { c::WSAGetLastError() };

if error != c::WSAEPROTOTYPE && error != c::WSAEINVAL {
return Err(io::Error::from_raw_os_error(error));
}

let socket = unsafe {
c::WSASocketW(
info.iAddressFamily,
info.iSocketType,
info.iProtocol,
&mut info,
0,
c::WSA_FLAG_OVERLAPPED,
)
};

if socket == c::INVALID_SOCKET {
return Err(last_error());
}

unsafe {
let socket = OwnedSocket::from_raw_socket(socket);
socket.set_no_inherit()?;
Ok(socket)
}
}
}

#[cfg(not(target_vendor = "uwp"))]
pub(crate) fn set_no_inherit(&self) -> io::Result<()> {
cvt(unsafe {
c::SetHandleInformation(self.as_raw_socket() as c::HANDLE, c::HANDLE_FLAG_INHERIT, 0)
})
.map(drop)
}

#[cfg(target_vendor = "uwp")]
pub(crate) fn set_no_inherit(&self) -> io::Result<()> {
Err(io::Error::new_const(io::ErrorKind::Unsupported, &"Unavailable on UWP"))
}
}

/// Returns the last error from the Windows socket interface.
fn last_error() -> io::Error {
io::Error::from_raw_os_error(unsafe { c::WSAGetLastError() })
}

impl AsRawSocket for BorrowedSocket<'_> {
#[inline]
fn as_raw_socket(&self) -> RawSocket {
17 changes: 2 additions & 15 deletions library/std/src/sys/unix/fd.rs
Original file line number Diff line number Diff line change
@@ -259,22 +259,9 @@ impl FileDesc {
}
}

#[inline]
pub fn duplicate(&self) -> io::Result<FileDesc> {
// We want to atomically duplicate this file descriptor and set the
// CLOEXEC flag, and currently that's done via F_DUPFD_CLOEXEC. This
// is a POSIX flag that was added to Linux in 2.6.24.
#[cfg(not(target_os = "espidf"))]
let cmd = libc::F_DUPFD_CLOEXEC;

// For ESP-IDF, F_DUPFD is used instead, because the CLOEXEC semantics
// will never be supported, as this is a bare metal framework with
// no capabilities for multi-process execution. While F_DUPFD is also
// not supported yet, it might be (currently it returns ENOSYS).
#[cfg(target_os = "espidf")]
let cmd = libc::F_DUPFD;

let fd = cvt(unsafe { libc::fcntl(self.as_raw_fd(), cmd, 0) })?;
Ok(unsafe { FileDesc::from_raw_fd(fd) })
Ok(Self(self.0.try_clone()?))
}
}

2 changes: 1 addition & 1 deletion library/std/src/sys/windows/fs.rs
Original file line number Diff line number Diff line change
@@ -460,7 +460,7 @@ impl File {
}

pub fn duplicate(&self) -> io::Result<File> {
Ok(File { handle: self.handle.duplicate(0, false, c::DUPLICATE_SAME_ACCESS)? })
Ok(Self { handle: self.handle.try_clone()? })
}

fn reparse_point<'a>(
21 changes: 6 additions & 15 deletions library/std/src/sys/windows/handle.rs
Original file line number Diff line number Diff line change
@@ -262,26 +262,17 @@ impl Handle {
Ok(written as usize)
}

pub fn try_clone(&self) -> io::Result<Self> {
Ok(Self(self.0.try_clone()?))
}

pub fn duplicate(
&self,
access: c::DWORD,
inherit: bool,
options: c::DWORD,
) -> io::Result<Handle> {
let mut ret = 0 as c::HANDLE;
cvt(unsafe {
let cur_proc = c::GetCurrentProcess();
c::DuplicateHandle(
cur_proc,
self.as_raw_handle(),
cur_proc,
&mut ret,
access,
inherit as c::BOOL,
options,
)
})?;
unsafe { Ok(Handle::from_raw_handle(ret)) }
) -> io::Result<Self> {
Ok(Self(self.0.duplicate(access, inherit, options)?))
}
}

62 changes: 2 additions & 60 deletions library/std/src/sys/windows/net.rs
Original file line number Diff line number Diff line change
@@ -134,7 +134,7 @@ impl Socket {

unsafe {
let socket = Self::from_raw_socket(socket);
socket.set_no_inherit()?;
socket.0.set_no_inherit()?;
Ok(socket)
}
}
@@ -213,52 +213,7 @@ impl Socket {
}

pub fn duplicate(&self) -> io::Result<Socket> {
let mut info = unsafe { mem::zeroed::<c::WSAPROTOCOL_INFO>() };
let result = unsafe {
c::WSADuplicateSocketW(self.as_raw_socket(), c::GetCurrentProcessId(), &mut info)
};
cvt(result)?;
let socket = unsafe {
c::WSASocketW(
info.iAddressFamily,
info.iSocketType,
info.iProtocol,
&mut info,
0,
c::WSA_FLAG_OVERLAPPED | c::WSA_FLAG_NO_HANDLE_INHERIT,
)
};

if socket != c::INVALID_SOCKET {
unsafe { Ok(Self::from_inner(OwnedSocket::from_raw_socket(socket))) }
} else {
let error = unsafe { c::WSAGetLastError() };

if error != c::WSAEPROTOTYPE && error != c::WSAEINVAL {
return Err(io::Error::from_raw_os_error(error));
}

let socket = unsafe {
c::WSASocketW(
info.iAddressFamily,
info.iSocketType,
info.iProtocol,
&mut info,
0,
c::WSA_FLAG_OVERLAPPED,
)
};

if socket == c::INVALID_SOCKET {
return Err(last_error());
}

unsafe {
let socket = Self::from_inner(OwnedSocket::from_raw_socket(socket));
socket.set_no_inherit()?;
Ok(socket)
}
}
Ok(Self(self.0.try_clone()?))
}

fn recv_with_flags(&self, buf: &mut [u8], flags: c_int) -> io::Result<usize> {
@@ -421,19 +376,6 @@ impl Socket {
}
}

#[cfg(not(target_vendor = "uwp"))]
fn set_no_inherit(&self) -> io::Result<()> {
sys::cvt(unsafe {
c::SetHandleInformation(self.as_raw_socket() as c::HANDLE, c::HANDLE_FLAG_INHERIT, 0)
})
.map(drop)
}

#[cfg(target_vendor = "uwp")]
fn set_no_inherit(&self) -> io::Result<()> {
Err(io::Error::new_const(io::ErrorKind::Unsupported, &"Unavailable on UWP"))
}

pub fn shutdown(&self, how: Shutdown) -> io::Result<()> {
let how = match how {
Shutdown::Write => c::SD_SEND,
28 changes: 22 additions & 6 deletions src/librustdoc/clean/blanket_impl.rs
Original file line number Diff line number Diff line change
@@ -101,6 +101,27 @@ impl<'a, 'tcx> BlanketImplFinder<'a, 'tcx> {

cx.generated_synthetics.insert((ty, trait_def_id));

let hir_imp = impl_def_id.as_local()
.map(|local| cx.tcx.hir().expect_item(local))
.and_then(|item| if let hir::ItemKind::Impl(i) = &item.kind {
Some(i)
} else {
None
});

let items = match hir_imp {
Some(imp) => imp
.items
.iter()
.map(|ii| cx.tcx.hir().impl_item(ii.id).clean(cx))
.collect::<Vec<_>>(),
None => cx.tcx
.associated_items(impl_def_id)
.in_definition_order()
.map(|x| x.clean(cx))
.collect::<Vec<_>>(),
};

impls.push(Item {
name: None,
attrs: Default::default(),
@@ -117,12 +138,7 @@ impl<'a, 'tcx> BlanketImplFinder<'a, 'tcx> {
// the post-inference `trait_ref`, as it's more accurate.
trait_: Some(trait_ref.clean(cx)),
for_: ty.clean(cx),
items: cx
.tcx
.associated_items(impl_def_id)
.in_definition_order()
.map(|x| x.clean(cx))
.collect::<Vec<_>>(),
items,
polarity: ty::ImplPolarity::Positive,
kind: ImplKind::Blanket(box trait_ref.self_ty().clean(cx)),
}),
75 changes: 35 additions & 40 deletions src/librustdoc/html/render/mod.rs
Original file line number Diff line number Diff line change
@@ -376,25 +376,21 @@ impl Setting {
description,
),
Setting::Select { js_data_name, description, default_value, ref options } => format!(
"<div class=\"setting-line\">\
<div>{}</div>\
<label class=\"select-wrapper\">\
<select id=\"{}\" autocomplete=\"off\">{}</select>\
<img src=\"{}down-arrow{}.svg\" alt=\"Select item\">\
</label>\
</div>",
description,
"<div class=\"setting-line\"><div class=\"radio-line\" id=\"{}\"><span class=\"setting-name\">{}</span>{}</div></div>",
js_data_name,
description,
options
.iter()
.map(|opt| format!(
"<option value=\"{name}\" {}>{name}</option>",
if opt == default_value { "selected" } else { "" },
"<label for=\"{js_data_name}-{name}\" class=\"choice\">
<input type=\"radio\" name=\"{js_data_name}\" id=\"{js_data_name}-{name}\" value=\"{name}\" {checked}>\
{name}\
</label>",
js_data_name = js_data_name,
name = opt,
checked = if opt == default_value { "checked" } else { "" },
))
.collect::<String>(),
root_path,
suffix,
),
}
}
@@ -418,31 +414,25 @@ impl<T: Into<Setting>> From<(&'static str, Vec<T>)> for Setting {
fn settings(root_path: &str, suffix: &str, theme_names: Vec<String>) -> Result<String, Error> {
// (id, explanation, default value)
let settings: &[Setting] = &[
(
"Theme preferences",
vec![
Setting::from(("use-system-theme", "Use system theme", true)),
Setting::Select {
js_data_name: "theme",
description: "Theme",
default_value: "light",
options: theme_names.clone(),
},
Setting::Select {
js_data_name: "preferred-dark-theme",
description: "Preferred dark theme",
default_value: "dark",
options: theme_names.clone(),
},
Setting::Select {
js_data_name: "preferred-light-theme",
description: "Preferred light theme",
default_value: "light",
options: theme_names,
},
],
)
.into(),
Setting::from(("use-system-theme", "Use system theme", true)),
Setting::Select {
js_data_name: "theme",
description: "Theme",
default_value: "light",
options: theme_names.clone(),
},
Setting::Select {
js_data_name: "preferred-light-theme",
description: "Preferred light theme",
default_value: "light",
options: theme_names.clone(),
},
Setting::Select {
js_data_name: "preferred-dark-theme",
description: "Preferred dark theme",
default_value: "dark",
options: theme_names,
},
("auto-hide-large-items", "Auto-hide item contents for large items.", true).into(),
("auto-hide-method-docs", "Auto-hide item methods' documentation", false).into(),
("auto-hide-trait-implementations", "Auto-hide trait implementation documentation", false)
@@ -454,9 +444,14 @@ fn settings(root_path: &str, suffix: &str, theme_names: Vec<String>) -> Result<S
];

Ok(format!(
"<h1 class=\"fqn\">\
<span class=\"in-band\">Rustdoc settings</span>\
</h1>\
"<div class=\"main-heading\">
<h1 class=\"fqn\">\
<span class=\"in-band\">Rustdoc settings</span>\
</h1>\
<span class=\"out-of-band\">\
<a id=\"back\" href=\"javascript:void(0)\">Back</a>\
</span>\
</div>\
<div class=\"settings\">{}</div>\
<link rel=\"stylesheet\" href=\"{root_path}settings{suffix}.css\">\
<script src=\"{root_path}settings{suffix}.js\"></script>",
24 changes: 24 additions & 0 deletions src/librustdoc/html/static/css/settings.css
Original file line number Diff line number Diff line change
@@ -17,6 +17,30 @@
border-bottom: 1px solid;
}

.setting-line .radio-line {
display: flex;
flex-wrap: wrap;
}

.setting-line .radio-line > * {
padding: 0.3em;
}

.setting-line .radio-line .setting-name {
flex-grow: 1;
}

.setting-line .radio-line input {
margin-right: 0.3em;
}

.radio-line .choice {
border-radius: 0.1em;
border: 1px solid;
margin-left: 0.5em;
min-width: 3.5em;
}

.toggle {
position: relative;
display: inline-block;
29 changes: 19 additions & 10 deletions src/librustdoc/html/static/js/settings.js
Original file line number Diff line number Diff line change
@@ -33,19 +33,15 @@
}

function showLightAndDark() {
addClass(document.getElementById("theme").parentElement.parentElement, "hidden");
removeClass(document.getElementById("preferred-light-theme").parentElement.parentElement,
"hidden");
removeClass(document.getElementById("preferred-dark-theme").parentElement.parentElement,
"hidden");
addClass(document.getElementById("theme").parentElement, "hidden");
removeClass(document.getElementById("preferred-light-theme").parentElement, "hidden");
removeClass(document.getElementById("preferred-dark-theme").parentElement, "hidden");
}

function hideLightAndDark() {
addClass(document.getElementById("preferred-light-theme").parentElement.parentElement,
"hidden");
addClass(document.getElementById("preferred-dark-theme").parentElement.parentElement,
"hidden");
removeClass(document.getElementById("theme").parentElement.parentElement, "hidden");
addClass(document.getElementById("preferred-light-theme").parentElement, "hidden");
addClass(document.getElementById("preferred-dark-theme").parentElement, "hidden");
removeClass(document.getElementById("theme").parentElement, "hidden");
}

function updateLightAndDark() {
@@ -82,6 +78,19 @@
changeSetting(this.id, this.value);
};
});
onEachLazy(document.querySelectorAll("input[type=\"radio\"]"), function(elem) {
const settingId = elem.name;
const settingValue = getSettingValue(settingId);
if (settingValue !== null && settingValue !== "null") {
elem.checked = settingValue === elem.value;
}
elem.addEventListener("change", function(ev) {
changeSetting(ev.target.name, ev.target.value);
});
});
document.getElementById("back").addEventListener("click", function() {
history.back();
});
}

window.addEventListener("DOMContentLoaded", setEvents);
15 changes: 1 addition & 14 deletions src/librustdoc/json/mod.rs
Original file line number Diff line number Diff line change
@@ -172,21 +172,8 @@ impl<'tcx> FormatRenderer<'tcx> for JsonRenderer<'tcx> {
/// the hashmap because certain items (traits and types) need to have their mappings for trait
/// implementations filled out before they're inserted.
fn item(&mut self, item: clean::Item) -> Result<(), Error> {
let local_blanket_impl = match item.def_id {
clean::ItemId::Blanket { impl_id, .. } => impl_id.is_local(),
clean::ItemId::Auto { .. }
| clean::ItemId::DefId(_)
| clean::ItemId::Primitive(_, _) => false,
};

// Flatten items that recursively store other items
// FIXME(CraftSpider): We skip children of local blanket implementations, as we'll have
// already seen the actual generic impl, and the generated ones don't need documenting.
// This is necessary due to the visibility, return type, and self arg of the generated
// impls not quite matching, and will no longer be necessary when the mismatch is fixed.
if !local_blanket_impl {
item.kind.inner_items().for_each(|i| self.item(i.clone()).unwrap());
}
item.kind.inner_items().for_each(|i| self.item(i.clone()).unwrap());

let id = item.def_id;
if let Some(mut new_item) = self.convert_item(item) {
24 changes: 24 additions & 0 deletions src/test/incremental/issue-92987-provisional-dep-node.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
// revisions: rpass1 rpass2

// Regression test for issue #92987
// Tests that we properly manage `DepNode`s during trait evaluation
// involing an auto-trait cycle.

#[cfg(rpass1)]
struct CycleOne(Box<CycleTwo>);

#[cfg(rpass2)]
enum CycleOne {
Variant(Box<CycleTwo>)
}

struct CycleTwo(CycleOne);

fn assert_send<T: Send>() {}

fn bar() {
assert_send::<CycleOne>();
assert_send::<CycleTwo>();
}

fn main() {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
1| |// Regression test for #93054: Functions using uninhabited types often only have a single,
2| |// unreachable basic block which doesn't get instrumented. This should not cause llvm-cov to fail.
3| |// Since these kinds functions can't be invoked anyway, it's ok to not have coverage data for them.
4| |
5| |// compile-flags: --edition=2021
6| |
7| |enum Never { }
8| |
9| |impl Never {
10| | fn foo(self) {
11| | match self { }
12| | make().map(|never| match never { });
13| | }
14| |
15| | fn bar(&self) {
16| | match *self { }
17| | }
18| |}
19| |
20| 0|async fn foo2(never: Never) {
21| | match never { }
22| |}
23| |
24| 0|fn make() -> Option<Never> {
25| 0| None
26| 0|}
27| |
28| 1|fn main() { }

28 changes: 28 additions & 0 deletions src/test/run-make-fulldeps/coverage/issue-93054.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
// Regression test for #93054: Functions using uninhabited types often only have a single,
// unreachable basic block which doesn't get instrumented. This should not cause llvm-cov to fail.
// Since these kinds functions can't be invoked anyway, it's ok to not have coverage data for them.

// compile-flags: --edition=2021

enum Never { }

impl Never {
fn foo(self) {
match self { }
make().map(|never| match never { });
}

fn bar(&self) {
match *self { }
}
}

async fn foo2(never: Never) {
match never { }
}

fn make() -> Option<Never> {
None
}

fn main() { }
4 changes: 4 additions & 0 deletions src/test/rustdoc-json/impls/blanket_with_local.rs
Original file line number Diff line number Diff line change
@@ -3,11 +3,15 @@

// @has blanket_with_local.json "$.index[*][?(@.name=='Load')]"
pub trait Load {
// @has - "$.index[*][?(@.name=='load')]"
fn load() {}
// @has - "$.index[*][?(@.name=='write')]"
fn write(self) {}
}

impl<P> Load for P {
fn load() {}
fn write(self) {}
}

// @has - "$.index[*][?(@.name=='Wrapper')]"
8 changes: 8 additions & 0 deletions src/test/ui/coherence/auxiliary/option_future.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
#![crate_type = "lib"]
#![feature(negative_impls)]
#![feature(rustc_attrs)]

pub trait Future {}

#[rustc_with_negative_coherence]
impl<E> !Future for Option<E> where E: Sized {}
18 changes: 18 additions & 0 deletions src/test/ui/coherence/coherence-overlap-negative-trait2.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
// check-pass
// aux-build:option_future.rs
//
// Check that if we promise to not impl what would overlap it doesn't actually overlap

#![feature(rustc_attrs)]

extern crate option_future as lib;
use lib::Future;

trait Termination {}

#[rustc_with_negative_coherence]
impl<E> Termination for Option<E> where E: Sized {}
#[rustc_with_negative_coherence]
impl<F> Termination for F where F: Future + Sized {}

fn main() {}
1 change: 1 addition & 0 deletions src/test/ui/mismatched_types/overloaded-calls-bad.rs
Original file line number Diff line number Diff line change
@@ -30,4 +30,5 @@ fn main() {
//~^ ERROR this function takes 1 argument but 0 arguments were supplied
let ans = s("burma", "shave");
//~^ ERROR this function takes 1 argument but 2 arguments were supplied
//~| ERROR mismatched types
}
8 changes: 7 additions & 1 deletion src/test/ui/mismatched_types/overloaded-calls-bad.stderr
Original file line number Diff line number Diff line change
@@ -18,6 +18,12 @@ note: associated function defined here
LL | extern "rust-call" fn call_mut(&mut self, args: Args) -> Self::Output;
| ^^^^^^^^

error[E0308]: mismatched types
--> $DIR/overloaded-calls-bad.rs:31:17
|
LL | let ans = s("burma", "shave");
| ^^^^^^^ expected `isize`, found `&str`

error[E0057]: this function takes 1 argument but 2 arguments were supplied
--> $DIR/overloaded-calls-bad.rs:31:15
|
@@ -32,7 +38,7 @@ note: associated function defined here
LL | extern "rust-call" fn call_mut(&mut self, args: Args) -> Self::Output;
| ^^^^^^^^

error: aborting due to 3 previous errors
error: aborting due to 4 previous errors

Some errors have detailed explanations: E0057, E0308.
For more information about an error, try `rustc --explain E0057`.