Skip to content

Rollup of 16 pull requests #77366

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

Closed
wants to merge 33 commits into from
Closed
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
2ad46ac
EarlyOtherwiseBranch::run_pass(): don't convert Place to Place (clipp…
matthiaskrgr Sep 21, 2020
d7a5c57
use std::mem::take(x) instead of std::mem::replace(x, Default::defaul…
matthiaskrgr Sep 21, 2020
6d0390c
Add regression test
JulianKnodt Sep 2, 2020
3e485d7
BTreeMap: keep an eye out on the size of the main components
ssomers Sep 26, 2020
37f7956
libary: Forward compiler-builtins "mem" feature
josephlr Sep 28, 2020
db5b70f
move candidate_from_obligation_no_cache
lcnr Sep 28, 2020
63bb51d
Add unstable book docs for `-Zunsound-mir-opts`
wesleywiser Sep 29, 2020
c6107c5
Don't fire `const_item_mutation` lint on writes through a pointer
Aaron1011 Sep 29, 2020
a2526b4
Use `rtassert!` instead of `assert!` from the child process after for…
Sep 29, 2020
9340ee4
Ensure that all LLVM components requested by tests are available on CI
petrochenkov Sep 27, 2020
2c38504
Add test for async/await combined with const-generics.
hameerabbasi Sep 29, 2020
b141e49
Fix typo in alloc vec comment
pickfire Sep 29, 2020
f9b625f
Alloc vec use imported path
pickfire Sep 29, 2020
f4d5275
Update books
ehuss Sep 29, 2020
93e3db3
liveness: Use Option::None to represent absent live nodes
tmiasko Sep 30, 2020
607d30d
Add test for issue #74761
samlich Sep 29, 2020
609786d
Validate `rustc_args_required_const`
varkor Sep 29, 2020
5c20326
Rollup merge of #76257 - JulianKnodt:i75777, r=Dylan-DPC
jonas-schievink Sep 30, 2020
277da4b
Rollup merge of #77037 - matthiaskrgr:cl42ppy, r=Dylan-DPC
jonas-schievink Sep 30, 2020
d2f3329
Rollup merge of #77233 - ssomers:btree_size_matters, r=Mark-Simulacrum
jonas-schievink Sep 30, 2020
ca6ba02
Rollup merge of #77280 - petrochenkov:llvmcomp, r=Mark-Simulacrum
jonas-schievink Sep 30, 2020
2edc7a3
Rollup merge of #77284 - josephlr:mem, r=Mark-Simulacrum
jonas-schievink Sep 30, 2020
dba0930
Rollup merge of #77296 - tmiasko:liveness-option, r=ecstatic-morse
jonas-schievink Sep 30, 2020
a489686
Rollup merge of #77305 - lcnr:candidate_from_obligation, r=davidtwco
jonas-schievink Sep 30, 2020
87c438e
Rollup merge of #77322 - rust-lang:wesleywiser-patch-1, r=steveklabnik
jonas-schievink Sep 30, 2020
840be97
Rollup merge of #77324 - Aaron1011:fix/const-item-mutation-ptr, r=pet…
jonas-schievink Sep 30, 2020
f007b5a
Rollup merge of #77328 - hyd-dev:assert-to-rtassert, r=Amanieu
jonas-schievink Sep 30, 2020
fa8b401
Rollup merge of #77331 - hameerabbasi:issue-74906, r=lcnr
jonas-schievink Sep 30, 2020
2bd5dd8
Rollup merge of #77338 - pickfire:patch-7, r=jyn514
jonas-schievink Sep 30, 2020
d9284c2
Rollup merge of #77340 - pickfire:patch-9, r=kennytm
jonas-schievink Sep 30, 2020
f19a5e7
Rollup merge of #77343 - varkor:rustc_args_required_const-validation,…
jonas-schievink Sep 30, 2020
1e7e3eb
Rollup merge of #77345 - samlich:test-issue-74761, r=lcnr
jonas-schievink Sep 30, 2020
71e4b21
Rollup merge of #77348 - ehuss:update-books, r=ehuss
jonas-schievink Sep 30, 2020
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
Original file line number Diff line number Diff line change
@@ -62,8 +62,7 @@ impl<'a, 'tcx> ConstraintConversion<'a, 'tcx> {
// `self.constraints`, but we also want to be mutating
// `self.member_constraints`. For now, just swap out the value
// we want and replace at the end.
let mut tmp =
std::mem::replace(&mut self.constraints.member_constraints, Default::default());
let mut tmp = std::mem::take(&mut self.constraints.member_constraints);
for member_constraint in member_constraints {
tmp.push_constraint(member_constraint, |r| self.to_region_vid(r));
}
14 changes: 9 additions & 5 deletions compiler/rustc_mir/src/transform/check_const_item_mutation.rs
Original file line number Diff line number Diff line change
@@ -60,11 +60,15 @@ impl<'a, 'tcx> Visitor<'tcx> for ConstMutationChecker<'a, 'tcx> {
// so emitting a lint would be redundant.
if !lhs.projection.is_empty() {
if let Some(def_id) = self.is_const_item(lhs.local) {
self.lint_const_item_usage(def_id, loc, |lint| {
let mut lint = lint.build("attempting to modify a `const` item");
lint.note("each usage of a `const` item creates a new temporary - the original `const` item will not be modified");
lint
})
// Don't lint on writes through a pointer
// (e.g. `unsafe { *FOO = 0; *BAR.field = 1; }`)
if !matches!(lhs.projection.last(), Some(PlaceElem::Deref)) {
self.lint_const_item_usage(def_id, loc, |lint| {
let mut lint = lint.build("attempting to modify a `const` item");
lint.note("each usage of a `const` item creates a new temporary - the original `const` item will not be modified");
lint
})
}
}
}
// We are looking for MIR of the form:
2 changes: 1 addition & 1 deletion compiler/rustc_mir/src/transform/early_otherwise_branch.rs
Original file line number Diff line number Diff line change
@@ -91,7 +91,7 @@ impl<'tcx> MirPass<'tcx> for EarlyOtherwiseBranch {
let not_equal_rvalue = Rvalue::BinaryOp(
not_equal,
Operand::Copy(Place::from(second_discriminant_temp)),
Operand::Copy(Place::from(first_descriminant_place)),
Operand::Copy(first_descriminant_place),
);
patch.add_statement(
end_of_block_location,
2 changes: 1 addition & 1 deletion compiler/rustc_mir/src/transform/promote_consts.rs
Original file line number Diff line number Diff line change
@@ -137,7 +137,7 @@ fn args_required_const(tcx: TyCtxt<'_>, def_id: DefId) -> Option<Vec<usize>> {
LitKind::Int(a, _) => {
ret.push(a as usize);
}
_ => return None,
_ => bug!("invalid arg index"),
}
}
Some(ret)
110 changes: 101 additions & 9 deletions compiler/rustc_passes/src/check_attr.rs
Original file line number Diff line number Diff line change
@@ -8,12 +8,12 @@ use rustc_middle::hir::map::Map;
use rustc_middle::ty::query::Providers;
use rustc_middle::ty::TyCtxt;

use rustc_ast::{Attribute, NestedMetaItem};
use rustc_errors::struct_span_err;
use rustc_ast::{Attribute, LitKind, NestedMetaItem};
use rustc_errors::{pluralize, struct_span_err};
use rustc_hir as hir;
use rustc_hir::def_id::LocalDefId;
use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor};
use rustc_hir::{self, HirId, Item, ItemKind, TraitItem};
use rustc_hir::{self, FnSig, ForeignItem, ForeignItemKind, HirId, Item, ItemKind, TraitItem};
use rustc_hir::{MethodKind, Target};
use rustc_session::lint::builtin::{CONFLICTING_REPR_HINTS, UNUSED_ATTRIBUTES};
use rustc_session::parse::feature_err;
@@ -43,6 +43,12 @@ pub(crate) fn target_from_impl_item<'tcx>(
}
}

#[derive(Clone, Copy)]
enum ItemLike<'tcx> {
Item(&'tcx Item<'tcx>),
ForeignItem(&'tcx ForeignItem<'tcx>),
}

struct CheckAttrVisitor<'tcx> {
tcx: TyCtxt<'tcx>,
}
@@ -55,7 +61,7 @@ impl CheckAttrVisitor<'tcx> {
attrs: &'hir [Attribute],
span: &Span,
target: Target,
item: Option<&Item<'_>>,
item: Option<ItemLike<'_>>,
) {
let mut is_valid = true;
for attr in attrs {
@@ -75,6 +81,8 @@ impl CheckAttrVisitor<'tcx> {
self.check_no_link(&attr, span, target)
} else if self.tcx.sess.check_name(attr, sym::export_name) {
self.check_export_name(&attr, span, target)
} else if self.tcx.sess.check_name(attr, sym::rustc_args_required_const) {
self.check_rustc_args_required_const(&attr, span, target, item)
} else {
// lint-only checks
if self.tcx.sess.check_name(attr, sym::cold) {
@@ -400,6 +408,71 @@ impl CheckAttrVisitor<'tcx> {
}
}

/// Checks if `#[rustc_args_required_const]` is applied to a function and has a valid argument.
fn check_rustc_args_required_const(
&self,
attr: &Attribute,
span: &Span,
target: Target,
item: Option<ItemLike<'_>>,
) -> bool {
if let Target::Fn | Target::Method(..) | Target::ForeignFn = target {
let mut invalid_args = vec![];
for meta in attr.meta_item_list().expect("no meta item list") {
if let Some(LitKind::Int(val, _)) = meta.literal().map(|lit| &lit.kind) {
if let Some(ItemLike::Item(Item {
kind: ItemKind::Fn(FnSig { decl, .. }, ..),
..
}))
| Some(ItemLike::ForeignItem(ForeignItem {
kind: ForeignItemKind::Fn(decl, ..),
..
})) = item
{
let arg_count = decl.inputs.len() as u128;
if *val >= arg_count {
let span = meta.span();
self.tcx
.sess
.struct_span_err(span, "index exceeds number of arguments")
.span_label(
span,
format!(
"there {} only {} argument{}",
if arg_count != 1 { "are" } else { "is" },
arg_count,
pluralize!(arg_count)
),
)
.emit();
return false;
}
} else {
bug!("should be a function item");
}
} else {
invalid_args.push(meta.span());
}
}
if !invalid_args.is_empty() {
self.tcx
.sess
.struct_span_err(invalid_args, "arguments should be non-negative integers")
.emit();
false
} else {
true
}
} else {
self.tcx
.sess
.struct_span_err(attr.span, "attribute should be applied to a function")
.span_label(*span, "not a function")
.emit();
false
}
}

/// Checks if `#[link_section]` is applied to a function or static.
fn check_link_section(&self, hir_id: HirId, attr: &Attribute, span: &Span, target: Target) {
match target {
@@ -448,7 +521,7 @@ impl CheckAttrVisitor<'tcx> {
attrs: &'hir [Attribute],
span: &Span,
target: Target,
item: Option<&Item<'_>>,
item: Option<ItemLike<'_>>,
hir_id: HirId,
) {
// Extract the names of all repr hints, e.g., [foo, bar, align] for:
@@ -564,7 +637,14 @@ impl CheckAttrVisitor<'tcx> {
// Warn on repr(u8, u16), repr(C, simd), and c-like-enum-repr(C, u8)
if (int_reprs > 1)
|| (is_simd && is_c)
|| (int_reprs == 1 && is_c && item.map_or(false, |item| is_c_like_enum(item)))
|| (int_reprs == 1
&& is_c
&& item.map_or(false, |item| {
if let ItemLike::Item(item) = item {
return is_c_like_enum(item);
}
return false;
}))
{
self.tcx.struct_span_lint_hir(
CONFLICTING_REPR_HINTS,
@@ -649,7 +729,13 @@ impl Visitor<'tcx> for CheckAttrVisitor<'tcx> {

fn visit_item(&mut self, item: &'tcx Item<'tcx>) {
let target = Target::from_item(item);
self.check_attributes(item.hir_id, item.attrs, &item.span, target, Some(item));
self.check_attributes(
item.hir_id,
item.attrs,
&item.span,
target,
Some(ItemLike::Item(item)),
);
intravisit::walk_item(self, item)
}

@@ -659,9 +745,15 @@ impl Visitor<'tcx> for CheckAttrVisitor<'tcx> {
intravisit::walk_trait_item(self, trait_item)
}

fn visit_foreign_item(&mut self, f_item: &'tcx hir::ForeignItem<'tcx>) {
fn visit_foreign_item(&mut self, f_item: &'tcx ForeignItem<'tcx>) {
let target = Target::from_foreign_item(f_item);
self.check_attributes(f_item.hir_id, &f_item.attrs, &f_item.span, target, None);
self.check_attributes(
f_item.hir_id,
&f_item.attrs,
&f_item.span,
target,
Some(ItemLike::ForeignItem(f_item)),
);
intravisit::walk_foreign_item(self, f_item)
}

76 changes: 36 additions & 40 deletions compiler/rustc_passes/src/liveness.rs
Original file line number Diff line number Diff line change
@@ -62,13 +62,13 @@
//! - `reader`: the `LiveNode` ID of some node which will read the value
//! that `V` holds on entry to `N`. Formally: a node `M` such
//! that there exists a path `P` from `N` to `M` where `P` does not
//! write `V`. If the `reader` is `INVALID_NODE`, then the current
//! write `V`. If the `reader` is `None`, then the current
//! value will never be read (the variable is dead, essentially).
//!
//! - `writer`: the `LiveNode` ID of some node which will write the
//! variable `V` and which is reachable from `N`. Formally: a node `M`
//! such that there exists a path `P` from `N` to `M` and `M` writes
//! `V`. If the `writer` is `INVALID_NODE`, then there is no writer
//! `V`. If the `writer` is `None`, then there is no writer
//! of `V` that follows `N`.
//!
//! - `used`: a boolean value indicating whether `V` is *used*. We
@@ -114,7 +114,6 @@ rustc_index::newtype_index! {
rustc_index::newtype_index! {
pub struct LiveNode {
DEBUG_FORMAT = "ln({})",
const INVALID_NODE = LiveNode::MAX_AS_U32,
}
}

@@ -168,12 +167,6 @@ pub fn provide(providers: &mut Providers) {
// variable must not be assigned if there is some successor
// assignment. And so forth.

impl LiveNode {
fn is_valid(self) -> bool {
self != INVALID_NODE
}
}

struct CaptureInfo {
ln: LiveNode,
var_hid: HirId,
@@ -478,8 +471,8 @@ impl<'tcx> Visitor<'tcx> for IrMaps<'tcx> {

#[derive(Clone, Copy)]
struct RWU {
reader: LiveNode,
writer: LiveNode,
reader: Option<LiveNode>,
writer: Option<LiveNode>,
used: bool,
}

@@ -501,10 +494,10 @@ struct RWUTable {
unpacked_rwus: Vec<RWU>,
}

// A constant representing `RWU { reader: INVALID_NODE; writer: INVALID_NODE; used: false }`.
// A constant representing `RWU { reader: None; writer: None; used: false }`.
const INV_INV_FALSE: u32 = u32::MAX;

// A constant representing `RWU { reader: INVALID_NODE; writer: INVALID_NODE; used: true }`.
// A constant representing `RWU { reader: None; writer: None; used: true }`.
const INV_INV_TRUE: u32 = u32::MAX - 1;

impl RWUTable {
@@ -515,24 +508,24 @@ impl RWUTable {
fn get(&self, idx: usize) -> RWU {
let packed_rwu = self.packed_rwus[idx];
match packed_rwu {
INV_INV_FALSE => RWU { reader: INVALID_NODE, writer: INVALID_NODE, used: false },
INV_INV_TRUE => RWU { reader: INVALID_NODE, writer: INVALID_NODE, used: true },
INV_INV_FALSE => RWU { reader: None, writer: None, used: false },
INV_INV_TRUE => RWU { reader: None, writer: None, used: true },
_ => self.unpacked_rwus[packed_rwu as usize],
}
}

fn get_reader(&self, idx: usize) -> LiveNode {
fn get_reader(&self, idx: usize) -> Option<LiveNode> {
let packed_rwu = self.packed_rwus[idx];
match packed_rwu {
INV_INV_FALSE | INV_INV_TRUE => INVALID_NODE,
INV_INV_FALSE | INV_INV_TRUE => None,
_ => self.unpacked_rwus[packed_rwu as usize].reader,
}
}

fn get_writer(&self, idx: usize) -> LiveNode {
fn get_writer(&self, idx: usize) -> Option<LiveNode> {
let packed_rwu = self.packed_rwus[idx];
match packed_rwu {
INV_INV_FALSE | INV_INV_TRUE => INVALID_NODE,
INV_INV_FALSE | INV_INV_TRUE => None,
_ => self.unpacked_rwus[packed_rwu as usize].writer,
}
}
@@ -552,7 +545,7 @@ impl RWUTable {
}

fn assign_unpacked(&mut self, idx: usize, rwu: RWU) {
if rwu.reader == INVALID_NODE && rwu.writer == INVALID_NODE {
if rwu.reader == None && rwu.writer == None {
// When we overwrite an indexing entry in `self.packed_rwus` with
// `INV_INV_{TRUE,FALSE}` we don't remove the corresponding entry
// from `self.unpacked_rwus`; it's not worth the effort, and we
@@ -581,7 +574,7 @@ struct Liveness<'a, 'tcx> {
typeck_results: &'a ty::TypeckResults<'tcx>,
param_env: ty::ParamEnv<'tcx>,
upvars: Option<&'tcx FxIndexMap<hir::HirId, hir::Upvar>>,
successors: IndexVec<LiveNode, LiveNode>,
successors: IndexVec<LiveNode, Option<LiveNode>>,
rwu_table: RWUTable,

/// A live node representing a point of execution before closure entry &
@@ -617,7 +610,7 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> {
typeck_results,
param_env,
upvars,
successors: IndexVec::from_elem_n(INVALID_NODE, num_live_nodes),
successors: IndexVec::from_elem_n(None, num_live_nodes),
rwu_table: RWUTable::new(num_live_nodes * num_vars),
closure_ln,
exit_ln,
@@ -662,30 +655,33 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> {
}

fn live_on_entry(&self, ln: LiveNode, var: Variable) -> Option<LiveNodeKind> {
assert!(ln.is_valid());
let reader = self.rwu_table.get_reader(self.idx(ln, var));
if reader.is_valid() { Some(self.ir.lnks[reader]) } else { None }
if let Some(reader) = self.rwu_table.get_reader(self.idx(ln, var)) {
Some(self.ir.lnks[reader])
} else {
None
}
}

// Is this variable live on entry to any of its successor nodes?
fn live_on_exit(&self, ln: LiveNode, var: Variable) -> Option<LiveNodeKind> {
let successor = self.successors[ln];
let successor = self.successors[ln].unwrap();
self.live_on_entry(successor, var)
}

fn used_on_entry(&self, ln: LiveNode, var: Variable) -> bool {
assert!(ln.is_valid());
self.rwu_table.get_used(self.idx(ln, var))
}

fn assigned_on_entry(&self, ln: LiveNode, var: Variable) -> Option<LiveNodeKind> {
assert!(ln.is_valid());
let writer = self.rwu_table.get_writer(self.idx(ln, var));
if writer.is_valid() { Some(self.ir.lnks[writer]) } else { None }
if let Some(writer) = self.rwu_table.get_writer(self.idx(ln, var)) {
Some(self.ir.lnks[writer])
} else {
None
}
}

fn assigned_on_exit(&self, ln: LiveNode, var: Variable) -> Option<LiveNodeKind> {
let successor = self.successors[ln];
let successor = self.successors[ln].unwrap();
self.assigned_on_entry(successor, var)
}

@@ -720,9 +716,9 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> {
{
let wr = &mut wr as &mut dyn Write;
write!(wr, "[{:?} of kind {:?} reads", ln, self.ir.lnks[ln]);
self.write_vars(wr, ln, |idx| self.rwu_table.get_reader(idx).is_valid());
self.write_vars(wr, ln, |idx| self.rwu_table.get_reader(idx).is_some());
write!(wr, " writes");
self.write_vars(wr, ln, |idx| self.rwu_table.get_writer(idx).is_valid());
self.write_vars(wr, ln, |idx| self.rwu_table.get_writer(idx).is_some());
write!(wr, " uses");
self.write_vars(wr, ln, |idx| self.rwu_table.get_used(idx));

@@ -746,7 +742,7 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> {
}

fn init_empty(&mut self, ln: LiveNode, succ_ln: LiveNode) {
self.successors[ln] = succ_ln;
self.successors[ln] = Some(succ_ln);

// It is not necessary to initialize the RWUs here because they are all
// set to INV_INV_FALSE when they are created, and the sets only grow
@@ -755,7 +751,7 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> {

fn init_from_succ(&mut self, ln: LiveNode, succ_ln: LiveNode) {
// more efficient version of init_empty() / merge_from_succ()
self.successors[ln] = succ_ln;
self.successors[ln] = Some(succ_ln);

self.indices2(ln, succ_ln, |this, idx, succ_idx| {
this.rwu_table.copy_packed(idx, succ_idx);
@@ -779,12 +775,12 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> {
let mut changed = false;
let mut rwu = this.rwu_table.get(idx);
let succ_rwu = this.rwu_table.get(succ_idx);
if succ_rwu.reader.is_valid() && !rwu.reader.is_valid() {
if succ_rwu.reader.is_some() && rwu.reader.is_none() {
rwu.reader = succ_rwu.reader;
changed = true
}

if succ_rwu.writer.is_valid() && !rwu.writer.is_valid() {
if succ_rwu.writer.is_some() && rwu.writer.is_none() {
rwu.writer = succ_rwu.writer;
changed = true
}
@@ -828,14 +824,14 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> {
let mut rwu = self.rwu_table.get(idx);

if (acc & ACC_WRITE) != 0 {
rwu.reader = INVALID_NODE;
rwu.writer = ln;
rwu.reader = None;
rwu.writer = Some(ln);
}

// Important: if we both read/write, must do read second
// or else the write will override.
if (acc & ACC_READ) != 0 {
rwu.reader = ln;
rwu.reader = Some(ln);
}

if (acc & ACC_USE) != 0 {
Original file line number Diff line number Diff line change
@@ -7,14 +7,19 @@
//! [rustc dev guide]:https://rustc-dev-guide.rust-lang.org/traits/resolution.html#candidate-assembly
use rustc_hir as hir;
use rustc_infer::traits::{Obligation, SelectionError, TraitObligation};
use rustc_middle::ty::print::with_no_trimmed_paths;
use rustc_middle::ty::{self, TypeFoldable};
use rustc_target::spec::abi::Abi;

use crate::traits::coherence::Conflict;
use crate::traits::{util, SelectionResult};
use crate::traits::{Overflow, Unimplemented};

use super::BuiltinImplConditions;
use super::IntercrateAmbiguityCause;
use super::OverflowError;
use super::SelectionCandidate::{self, *};
use super::{SelectionCandidateSet, SelectionContext, TraitObligationStack};
use super::{EvaluatedCandidate, SelectionCandidateSet, SelectionContext, TraitObligationStack};

impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
pub(super) fn candidate_from_obligation<'o>(
@@ -62,6 +67,161 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
candidate
}

fn candidate_from_obligation_no_cache<'o>(
&mut self,
stack: &TraitObligationStack<'o, 'tcx>,
) -> SelectionResult<'tcx, SelectionCandidate<'tcx>> {
if let Some(conflict) = self.is_knowable(stack) {
debug!("coherence stage: not knowable");
if self.intercrate_ambiguity_causes.is_some() {
debug!("evaluate_stack: intercrate_ambiguity_causes is some");
// Heuristics: show the diagnostics when there are no candidates in crate.
if let Ok(candidate_set) = self.assemble_candidates(stack) {
let mut no_candidates_apply = true;

for c in candidate_set.vec.iter() {
if self.evaluate_candidate(stack, &c)?.may_apply() {
no_candidates_apply = false;
break;
}
}

if !candidate_set.ambiguous && no_candidates_apply {
let trait_ref = stack.obligation.predicate.skip_binder().trait_ref;
let self_ty = trait_ref.self_ty();
let (trait_desc, self_desc) = with_no_trimmed_paths(|| {
let trait_desc = trait_ref.print_only_trait_path().to_string();
let self_desc = if self_ty.has_concrete_skeleton() {
Some(self_ty.to_string())
} else {
None
};
(trait_desc, self_desc)
});
let cause = if let Conflict::Upstream = conflict {
IntercrateAmbiguityCause::UpstreamCrateUpdate { trait_desc, self_desc }
} else {
IntercrateAmbiguityCause::DownstreamCrate { trait_desc, self_desc }
};
debug!("evaluate_stack: pushing cause = {:?}", cause);
self.intercrate_ambiguity_causes.as_mut().unwrap().push(cause);
}
}
}
return Ok(None);
}

let candidate_set = self.assemble_candidates(stack)?;

if candidate_set.ambiguous {
debug!("candidate set contains ambig");
return Ok(None);
}

let mut candidates = candidate_set.vec;

debug!("assembled {} candidates for {:?}: {:?}", candidates.len(), stack, candidates);

// At this point, we know that each of the entries in the
// candidate set is *individually* applicable. Now we have to
// figure out if they contain mutual incompatibilities. This
// frequently arises if we have an unconstrained input type --
// for example, we are looking for `$0: Eq` where `$0` is some
// unconstrained type variable. In that case, we'll get a
// candidate which assumes $0 == int, one that assumes `$0 ==
// usize`, etc. This spells an ambiguity.

// If there is more than one candidate, first winnow them down
// by considering extra conditions (nested obligations and so
// forth). We don't winnow if there is exactly one
// candidate. This is a relatively minor distinction but it
// can lead to better inference and error-reporting. An
// example would be if there was an impl:
//
// impl<T:Clone> Vec<T> { fn push_clone(...) { ... } }
//
// and we were to see some code `foo.push_clone()` where `boo`
// is a `Vec<Bar>` and `Bar` does not implement `Clone`. If
// we were to winnow, we'd wind up with zero candidates.
// Instead, we select the right impl now but report "`Bar` does
// not implement `Clone`".
if candidates.len() == 1 {
return self.filter_negative_and_reservation_impls(candidates.pop().unwrap());
}

// Winnow, but record the exact outcome of evaluation, which
// is needed for specialization. Propagate overflow if it occurs.
let mut candidates = candidates
.into_iter()
.map(|c| match self.evaluate_candidate(stack, &c) {
Ok(eval) if eval.may_apply() => {
Ok(Some(EvaluatedCandidate { candidate: c, evaluation: eval }))
}
Ok(_) => Ok(None),
Err(OverflowError) => Err(Overflow),
})
.flat_map(Result::transpose)
.collect::<Result<Vec<_>, _>>()?;

debug!("winnowed to {} candidates for {:?}: {:?}", candidates.len(), stack, candidates);

let needs_infer = stack.obligation.predicate.needs_infer();

// If there are STILL multiple candidates, we can further
// reduce the list by dropping duplicates -- including
// resolving specializations.
if candidates.len() > 1 {
let mut i = 0;
while i < candidates.len() {
let is_dup = (0..candidates.len()).filter(|&j| i != j).any(|j| {
self.candidate_should_be_dropped_in_favor_of(
&candidates[i],
&candidates[j],
needs_infer,
)
});
if is_dup {
debug!("Dropping candidate #{}/{}: {:?}", i, candidates.len(), candidates[i]);
candidates.swap_remove(i);
} else {
debug!("Retaining candidate #{}/{}: {:?}", i, candidates.len(), candidates[i]);
i += 1;

// If there are *STILL* multiple candidates, give up
// and report ambiguity.
if i > 1 {
debug!("multiple matches, ambig");
return Ok(None);
}
}
}
}

// If there are *NO* candidates, then there are no impls --
// that we know of, anyway. Note that in the case where there
// are unbound type variables within the obligation, it might
// be the case that you could still satisfy the obligation
// from another crate by instantiating the type variables with
// a type from another crate that does have an impl. This case
// is checked for in `evaluate_stack` (and hence users
// who might care about this case, like coherence, should use
// that function).
if candidates.is_empty() {
// If there's an error type, 'downgrade' our result from
// `Err(Unimplemented)` to `Ok(None)`. This helps us avoid
// emitting additional spurious errors, since we're guaranteed
// to have emitted at least one.
if stack.obligation.references_error() {
debug!("no results for error type, treating as ambiguous");
return Ok(None);
}
return Err(Unimplemented);
}

// Just one candidate left.
self.filter_negative_and_reservation_impls(candidates.pop().unwrap().candidate)
}

pub(super) fn assemble_candidates<'o>(
&mut self,
stack: &TraitObligationStack<'o, 'tcx>,
155 changes: 0 additions & 155 deletions compiler/rustc_trait_selection/src/traits/select/mod.rs
Original file line number Diff line number Diff line change
@@ -1029,161 +1029,6 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
Ok(Some(candidate))
}

fn candidate_from_obligation_no_cache<'o>(
&mut self,
stack: &TraitObligationStack<'o, 'tcx>,
) -> SelectionResult<'tcx, SelectionCandidate<'tcx>> {
if let Some(conflict) = self.is_knowable(stack) {
debug!("coherence stage: not knowable");
if self.intercrate_ambiguity_causes.is_some() {
debug!("evaluate_stack: intercrate_ambiguity_causes is some");
// Heuristics: show the diagnostics when there are no candidates in crate.
if let Ok(candidate_set) = self.assemble_candidates(stack) {
let mut no_candidates_apply = true;

for c in candidate_set.vec.iter() {
if self.evaluate_candidate(stack, &c)?.may_apply() {
no_candidates_apply = false;
break;
}
}

if !candidate_set.ambiguous && no_candidates_apply {
let trait_ref = stack.obligation.predicate.skip_binder().trait_ref;
let self_ty = trait_ref.self_ty();
let (trait_desc, self_desc) = with_no_trimmed_paths(|| {
let trait_desc = trait_ref.print_only_trait_path().to_string();
let self_desc = if self_ty.has_concrete_skeleton() {
Some(self_ty.to_string())
} else {
None
};
(trait_desc, self_desc)
});
let cause = if let Conflict::Upstream = conflict {
IntercrateAmbiguityCause::UpstreamCrateUpdate { trait_desc, self_desc }
} else {
IntercrateAmbiguityCause::DownstreamCrate { trait_desc, self_desc }
};
debug!("evaluate_stack: pushing cause = {:?}", cause);
self.intercrate_ambiguity_causes.as_mut().unwrap().push(cause);
}
}
}
return Ok(None);
}

let candidate_set = self.assemble_candidates(stack)?;

if candidate_set.ambiguous {
debug!("candidate set contains ambig");
return Ok(None);
}

let mut candidates = candidate_set.vec;

debug!("assembled {} candidates for {:?}: {:?}", candidates.len(), stack, candidates);

// At this point, we know that each of the entries in the
// candidate set is *individually* applicable. Now we have to
// figure out if they contain mutual incompatibilities. This
// frequently arises if we have an unconstrained input type --
// for example, we are looking for `$0: Eq` where `$0` is some
// unconstrained type variable. In that case, we'll get a
// candidate which assumes $0 == int, one that assumes `$0 ==
// usize`, etc. This spells an ambiguity.

// If there is more than one candidate, first winnow them down
// by considering extra conditions (nested obligations and so
// forth). We don't winnow if there is exactly one
// candidate. This is a relatively minor distinction but it
// can lead to better inference and error-reporting. An
// example would be if there was an impl:
//
// impl<T:Clone> Vec<T> { fn push_clone(...) { ... } }
//
// and we were to see some code `foo.push_clone()` where `boo`
// is a `Vec<Bar>` and `Bar` does not implement `Clone`. If
// we were to winnow, we'd wind up with zero candidates.
// Instead, we select the right impl now but report "`Bar` does
// not implement `Clone`".
if candidates.len() == 1 {
return self.filter_negative_and_reservation_impls(candidates.pop().unwrap());
}

// Winnow, but record the exact outcome of evaluation, which
// is needed for specialization. Propagate overflow if it occurs.
let mut candidates = candidates
.into_iter()
.map(|c| match self.evaluate_candidate(stack, &c) {
Ok(eval) if eval.may_apply() => {
Ok(Some(EvaluatedCandidate { candidate: c, evaluation: eval }))
}
Ok(_) => Ok(None),
Err(OverflowError) => Err(Overflow),
})
.flat_map(Result::transpose)
.collect::<Result<Vec<_>, _>>()?;

debug!("winnowed to {} candidates for {:?}: {:?}", candidates.len(), stack, candidates);

let needs_infer = stack.obligation.predicate.needs_infer();

// If there are STILL multiple candidates, we can further
// reduce the list by dropping duplicates -- including
// resolving specializations.
if candidates.len() > 1 {
let mut i = 0;
while i < candidates.len() {
let is_dup = (0..candidates.len()).filter(|&j| i != j).any(|j| {
self.candidate_should_be_dropped_in_favor_of(
&candidates[i],
&candidates[j],
needs_infer,
)
});
if is_dup {
debug!("Dropping candidate #{}/{}: {:?}", i, candidates.len(), candidates[i]);
candidates.swap_remove(i);
} else {
debug!("Retaining candidate #{}/{}: {:?}", i, candidates.len(), candidates[i]);
i += 1;

// If there are *STILL* multiple candidates, give up
// and report ambiguity.
if i > 1 {
debug!("multiple matches, ambig");
return Ok(None);
}
}
}
}

// If there are *NO* candidates, then there are no impls --
// that we know of, anyway. Note that in the case where there
// are unbound type variables within the obligation, it might
// be the case that you could still satisfy the obligation
// from another crate by instantiating the type variables with
// a type from another crate that does have an impl. This case
// is checked for in `evaluate_stack` (and hence users
// who might care about this case, like coherence, should use
// that function).
if candidates.is_empty() {
// If there's an error type, 'downgrade' our result from
// `Err(Unimplemented)` to `Ok(None)`. This helps us avoid
// emitting additional spurious errors, since we're guaranteed
// to have emitted at least one.
if stack.obligation.references_error() {
debug!("no results for error type, treating as ambiguous");
return Ok(None);
}
return Err(Unimplemented);
}

// Just one candidate left.
self.filter_negative_and_reservation_impls(candidates.pop().unwrap().candidate)
}

fn is_knowable<'o>(&mut self, stack: &TraitObligationStack<'o, 'tcx>) -> Option<Conflict> {
debug!("is_knowable(intercrate={:?})", self.intercrate);

6 changes: 2 additions & 4 deletions compiler/rustc_typeck/src/check/writeback.rs
Original file line number Diff line number Diff line change
@@ -70,10 +70,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
debug!("used_trait_imports({:?}) = {:?}", item_def_id, used_trait_imports);
wbcx.typeck_results.used_trait_imports = used_trait_imports;

wbcx.typeck_results.closure_captures = mem::replace(
&mut self.typeck_results.borrow_mut().closure_captures,
Default::default(),
);
wbcx.typeck_results.closure_captures =
mem::take(&mut self.typeck_results.borrow_mut().closure_captures);

if self.is_tainted_by_errors() {
// FIXME(eddyb) keep track of `ErrorReported` from where the error was emitted.
9 changes: 9 additions & 0 deletions library/alloc/src/collections/btree/node/tests.rs
Original file line number Diff line number Diff line change
@@ -23,3 +23,12 @@ fn test_splitpoint() {
assert!(left_len + right_len == CAPACITY);
}
}

#[test]
#[cfg(target_arch = "x86_64")]
fn test_sizes() {
assert_eq!(core::mem::size_of::<LeafNode<(), ()>>(), 16);
assert_eq!(core::mem::size_of::<LeafNode<i64, i64>>(), 16 + CAPACITY * 8 * 2);
assert_eq!(core::mem::size_of::<InternalNode<(), ()>>(), 112);
assert_eq!(core::mem::size_of::<InternalNode<i64, i64>>(), 112 + CAPACITY * 8 * 2);
}
4 changes: 2 additions & 2 deletions library/alloc/src/vec.rs
Original file line number Diff line number Diff line change
@@ -2281,14 +2281,14 @@ where

// use try-fold since
// - it vectorizes better for some iterator adapters
// - unlike most internal iteration methods methods it only takes a &mut self
// - unlike most internal iteration methods, it only takes a &mut self
// - it lets us thread the write pointer through its innards and get it back in the end
let sink = InPlaceDrop { inner: dst_buf, dst: dst_buf };
let sink = iterator
.try_fold::<_, _, Result<_, !>>(sink, write_in_place_with_drop(dst_end))
.unwrap();
// iteration succeeded, don't drop head
let dst = mem::ManuallyDrop::new(sink).dst;
let dst = ManuallyDrop::new(sink).dst;

let src = unsafe { iterator.as_inner().as_into_iter() };
// check if SourceIter contract was upheld
1 change: 1 addition & 0 deletions library/std/Cargo.toml
Original file line number Diff line number Diff line change
@@ -59,6 +59,7 @@ gimli-symbolize = []
panic-unwind = ["panic_unwind"]
profiler = ["profiler_builtins"]
compiler-builtins-c = ["alloc/compiler-builtins-c"]
compiler-builtins-mem = ["alloc/compiler-builtins-mem"]
llvm-libunwind = ["unwind/llvm-libunwind"]

# Make panics and failed asserts immediately abort without formatting any message
2 changes: 1 addition & 1 deletion library/std/src/sys/unix/process/process_unix.rs
Original file line number Diff line number Diff line change
@@ -67,7 +67,7 @@ impl Command {
// pipe I/O up to PIPE_BUF bytes should be atomic, and then
// we want to be sure we *don't* run at_exit destructors as
// we're being torn down regardless
assert!(output.write(&bytes).is_ok());
rtassert!(output.write(&bytes).is_ok());
libc::_exit(1)
}
n => n,
1 change: 1 addition & 0 deletions library/test/Cargo.toml
Original file line number Diff line number Diff line change
@@ -25,6 +25,7 @@ proc_macro = { path = "../proc_macro" }
default = ["std_detect_file_io", "std_detect_dlsym_getauxval", "panic-unwind"]
backtrace = ["std/backtrace"]
compiler-builtins-c = ["std/compiler-builtins-c"]
compiler-builtins-mem = ["std/compiler-builtins-mem"]
llvm-libunwind = ["std/llvm-libunwind"]
panic-unwind = ["std/panic_unwind"]
panic_immediate_abort = ["std/panic_immediate_abort"]
2 changes: 2 additions & 0 deletions src/ci/run.sh
Original file line number Diff line number Diff line change
@@ -104,6 +104,8 @@ if [ "$RUST_RELEASE_CHANNEL" = "nightly" ] || [ "$DIST_REQUIRE_ALL_TOOLS" = "" ]
RUST_CONFIGURE_ARGS="$RUST_CONFIGURE_ARGS --enable-missing-tools"
fi

export COMPILETEST_NEEDS_ALL_LLVM_COMPONENTS=1

# Print the date from the local machine and the date from an external source to
# check for clock drifts. An HTTP URL is used instead of HTTPS since on Azure
# Pipelines it happened that the certificates were marked as expired.
2 changes: 1 addition & 1 deletion src/doc/embedded-book
8 changes: 8 additions & 0 deletions src/doc/unstable-book/src/compiler-flags/unsound-mir-opts.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# `unsound-mir-opts`

--------------------

The `-Zunsound-mir-opts` compiler flag enables [MIR optimization passes] which can cause unsound behavior.
This flag should only be used by MIR optimization tests in the rustc test suite.

[MIR optimization passes]: https://rustc-dev-guide.rust-lang.org/mir/optimizations.html
25 changes: 25 additions & 0 deletions src/test/ui/const-generics/issue-74906.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
// edition:2018
// check-pass
// revisions: full min
#![cfg_attr(full, feature(const_generics))]
#![cfg_attr(full, allow(incomplete_features))]
#![cfg_attr(min, feature(min_const_generics))]

const SIZE: usize = 16;

struct Bar<const H: usize> {}

struct Foo<const H: usize> {}

impl<const H: usize> Foo<H> {
async fn biz(_: &[[u8; SIZE]]) -> Vec<()> {
vec![]
}

pub async fn baz(&self) -> Bar<H> {
Self::biz(&vec![]).await;
Bar {}
}
}

fn main() { }
26 changes: 26 additions & 0 deletions src/test/ui/invalid-rustc_args_required_const-arguments.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
#![feature(rustc_attrs)]

#[rustc_args_required_const(0)] //~ ERROR index exceeds number of arguments
fn foo1() {}

#[rustc_args_required_const(1)] //~ ERROR index exceeds number of arguments
fn foo2(_: u8) {}

#[rustc_args_required_const(a)] //~ ERROR arguments should be non-negative integers
fn foo4() {}

#[rustc_args_required_const(1, a, 2, b)] //~ ERROR arguments should be non-negative integers
fn foo5(_: u8, _: u8, _: u8) {}

#[rustc_args_required_const(0)] //~ ERROR attribute should be applied to a function
struct S;

#[rustc_args_required_const(0usize)] //~ ERROR suffixed literals are not allowed in attributes
fn foo6(_: u8) {}

extern {
#[rustc_args_required_const(1)] //~ ERROR index exceeds number of arguments
fn foo7(_: u8);
}

fn main() {}
48 changes: 48 additions & 0 deletions src/test/ui/invalid-rustc_args_required_const-arguments.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
error: suffixed literals are not allowed in attributes
--> $DIR/invalid-rustc_args_required_const-arguments.rs:18:29
|
LL | #[rustc_args_required_const(0usize)]
| ^^^^^^
|
= help: instead of using a suffixed literal (`1u8`, `1.0f32`, etc.), use an unsuffixed version (`1`, `1.0`, etc.)

error: index exceeds number of arguments
--> $DIR/invalid-rustc_args_required_const-arguments.rs:3:29
|
LL | #[rustc_args_required_const(0)]
| ^ there are only 0 arguments

error: index exceeds number of arguments
--> $DIR/invalid-rustc_args_required_const-arguments.rs:6:29
|
LL | #[rustc_args_required_const(1)]
| ^ there is only 1 argument

error: arguments should be non-negative integers
--> $DIR/invalid-rustc_args_required_const-arguments.rs:9:29
|
LL | #[rustc_args_required_const(a)]
| ^

error: arguments should be non-negative integers
--> $DIR/invalid-rustc_args_required_const-arguments.rs:12:32
|
LL | #[rustc_args_required_const(1, a, 2, b)]
| ^ ^

error: attribute should be applied to a function
--> $DIR/invalid-rustc_args_required_const-arguments.rs:15:1
|
LL | #[rustc_args_required_const(0)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
LL | struct S;
| --------- not a function

error: index exceeds number of arguments
--> $DIR/invalid-rustc_args_required_const-arguments.rs:22:33
|
LL | #[rustc_args_required_const(1)]
| ^ there is only 1 argument

error: aborting due to 7 previous errors

17 changes: 17 additions & 0 deletions src/test/ui/issues/issue-75777.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
// Regression test for #75777.
// Checks that a boxed future can be properly constructed.

#![feature(future_readiness_fns)]

use std::future::{self, Future};
use std::pin::Pin;

type BoxFuture<'a, T> = Pin<Box<dyn Future<Output = T> + 'a + Send>>;

fn inject<'a, Env: 'a, A: 'a + Send>(v: A) -> Box<dyn FnOnce(&'a Env) -> BoxFuture<'a, A>> {
let fut: BoxFuture<'a, A> = Box::pin(future::ready(v));
Box::new(move |_| fut)
//~^ ERROR: cannot infer an appropriate lifetime
}

fn main() {}
30 changes: 30 additions & 0 deletions src/test/ui/issues/issue-75777.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
error[E0495]: cannot infer an appropriate lifetime due to conflicting requirements
--> $DIR/issue-75777.rs:13:14
|
LL | Box::new(move |_| fut)
| ^^^^^^^^^^^^
|
note: first, the lifetime cannot outlive the lifetime `'a` as defined on the function body at 11:11...
--> $DIR/issue-75777.rs:11:11
|
LL | fn inject<'a, Env: 'a, A: 'a + Send>(v: A) -> Box<dyn FnOnce(&'a Env) -> BoxFuture<'a, A>> {
| ^^
note: ...so that the types are compatible
--> $DIR/issue-75777.rs:13:14
|
LL | Box::new(move |_| fut)
| ^^^^^^^^^^^^
= note: expected `Pin<Box<dyn Future<Output = A> + Send>>`
found `Pin<Box<(dyn Future<Output = A> + Send + 'a)>>`
= note: but, the lifetime must be valid for the static lifetime...
note: ...so that the expression is assignable
--> $DIR/issue-75777.rs:13:5
|
LL | Box::new(move |_| fut)
| ^^^^^^^^^^^^^^^^^^^^^^
= note: expected `Box<(dyn FnOnce(&'a Env) -> Pin<Box<(dyn Future<Output = A> + Send + 'a)>> + 'static)>`
found `Box<dyn FnOnce(&'a Env) -> Pin<Box<(dyn Future<Output = A> + Send + 'a)>>>`

error: aborting due to previous error

For more information about this error, try `rustc --explain E0495`.
13 changes: 12 additions & 1 deletion src/test/ui/lint/lint-const-item-mutation.rs
Original file line number Diff line number Diff line change
@@ -3,13 +3,15 @@
struct MyStruct {
field: bool,
inner_array: [char; 1],
raw_ptr: *mut u8
}
impl MyStruct {
fn use_mut(&mut self) {}
}

const ARRAY: [u8; 1] = [25];
const MY_STRUCT: MyStruct = MyStruct { field: true, inner_array: ['a'] };
const MY_STRUCT: MyStruct = MyStruct { field: true, inner_array: ['a'], raw_ptr: 2 as *mut u8 };
const RAW_PTR: *mut u8 = 1 as *mut u8;

fn main() {
ARRAY[0] = 5; //~ WARN attempting to modify
@@ -18,4 +20,13 @@ fn main() {
MY_STRUCT.use_mut(); //~ WARN taking
&mut MY_STRUCT; //~ WARN taking
(&mut MY_STRUCT).use_mut(); //~ WARN taking

// Test that we don't warn when writing through
// a raw pointer
// This is U.B., but this test is check-pass,
// so this never actually executes
unsafe {
*RAW_PTR = 0;
*MY_STRUCT.raw_ptr = 0;
}
}
46 changes: 23 additions & 23 deletions src/test/ui/lint/lint-const-item-mutation.stderr
Original file line number Diff line number Diff line change
@@ -1,89 +1,89 @@
warning: attempting to modify a `const` item
--> $DIR/lint-const-item-mutation.rs:15:5
--> $DIR/lint-const-item-mutation.rs:17:5
|
LL | ARRAY[0] = 5;
| ^^^^^^^^^^^^
|
= note: `#[warn(const_item_mutation)]` on by default
= note: each usage of a `const` item creates a new temporary - the original `const` item will not be modified
note: `const` item defined here
--> $DIR/lint-const-item-mutation.rs:11:1
--> $DIR/lint-const-item-mutation.rs:12:1
|
LL | const ARRAY: [u8; 1] = [25];
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^

warning: attempting to modify a `const` item
--> $DIR/lint-const-item-mutation.rs:16:5
--> $DIR/lint-const-item-mutation.rs:18:5
|
LL | MY_STRUCT.field = false;
| ^^^^^^^^^^^^^^^^^^^^^^^
|
= note: each usage of a `const` item creates a new temporary - the original `const` item will not be modified
note: `const` item defined here
--> $DIR/lint-const-item-mutation.rs:12:1
--> $DIR/lint-const-item-mutation.rs:13:1
|
LL | const MY_STRUCT: MyStruct = MyStruct { field: true, inner_array: ['a'] };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
LL | const MY_STRUCT: MyStruct = MyStruct { field: true, inner_array: ['a'], raw_ptr: 2 as *mut u8 };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

warning: attempting to modify a `const` item
--> $DIR/lint-const-item-mutation.rs:17:5
--> $DIR/lint-const-item-mutation.rs:19:5
|
LL | MY_STRUCT.inner_array[0] = 'b';
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: each usage of a `const` item creates a new temporary - the original `const` item will not be modified
note: `const` item defined here
--> $DIR/lint-const-item-mutation.rs:12:1
--> $DIR/lint-const-item-mutation.rs:13:1
|
LL | const MY_STRUCT: MyStruct = MyStruct { field: true, inner_array: ['a'] };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
LL | const MY_STRUCT: MyStruct = MyStruct { field: true, inner_array: ['a'], raw_ptr: 2 as *mut u8 };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

warning: taking a mutable reference to a `const` item
--> $DIR/lint-const-item-mutation.rs:18:5
--> $DIR/lint-const-item-mutation.rs:20:5
|
LL | MY_STRUCT.use_mut();
| ^^^^^^^^^^^^^^^^^^^
|
= note: each usage of a `const` item creates a new temporary
= note: the mutable reference will refer to this temporary, not the original `const` item
note: mutable reference created due to call to this method
--> $DIR/lint-const-item-mutation.rs:8:5
--> $DIR/lint-const-item-mutation.rs:9:5
|
LL | fn use_mut(&mut self) {}
| ^^^^^^^^^^^^^^^^^^^^^
note: `const` item defined here
--> $DIR/lint-const-item-mutation.rs:12:1
--> $DIR/lint-const-item-mutation.rs:13:1
|
LL | const MY_STRUCT: MyStruct = MyStruct { field: true, inner_array: ['a'] };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
LL | const MY_STRUCT: MyStruct = MyStruct { field: true, inner_array: ['a'], raw_ptr: 2 as *mut u8 };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

warning: taking a mutable reference to a `const` item
--> $DIR/lint-const-item-mutation.rs:19:5
--> $DIR/lint-const-item-mutation.rs:21:5
|
LL | &mut MY_STRUCT;
| ^^^^^^^^^^^^^^
|
= note: each usage of a `const` item creates a new temporary
= note: the mutable reference will refer to this temporary, not the original `const` item
note: `const` item defined here
--> $DIR/lint-const-item-mutation.rs:12:1
--> $DIR/lint-const-item-mutation.rs:13:1
|
LL | const MY_STRUCT: MyStruct = MyStruct { field: true, inner_array: ['a'] };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
LL | const MY_STRUCT: MyStruct = MyStruct { field: true, inner_array: ['a'], raw_ptr: 2 as *mut u8 };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

warning: taking a mutable reference to a `const` item
--> $DIR/lint-const-item-mutation.rs:20:5
--> $DIR/lint-const-item-mutation.rs:22:5
|
LL | (&mut MY_STRUCT).use_mut();
| ^^^^^^^^^^^^^^^^
|
= note: each usage of a `const` item creates a new temporary
= note: the mutable reference will refer to this temporary, not the original `const` item
note: `const` item defined here
--> $DIR/lint-const-item-mutation.rs:12:1
--> $DIR/lint-const-item-mutation.rs:13:1
|
LL | const MY_STRUCT: MyStruct = MyStruct { field: true, inner_array: ['a'] };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
LL | const MY_STRUCT: MyStruct = MyStruct { field: true, inner_array: ['a'], raw_ptr: 2 as *mut u8 };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

warning: 6 warnings emitted

16 changes: 16 additions & 0 deletions src/test/ui/type-alias-impl-trait/issue-74761.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
#![feature(member_constraints)]
#![feature(type_alias_impl_trait)]

pub trait A {
type B;
fn f(&self) -> Self::B;
}
impl<'a, 'b> A for () {
//~^ ERROR the lifetime parameter `'a` is not constrained
//~| ERROR the lifetime parameter `'b` is not constrained
type B = impl core::fmt::Debug;

fn f(&self) -> Self::B {}
}

fn main() {}
15 changes: 15 additions & 0 deletions src/test/ui/type-alias-impl-trait/issue-74761.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
error[E0207]: the lifetime parameter `'a` is not constrained by the impl trait, self type, or predicates
--> $DIR/issue-74761.rs:8:6
|
LL | impl<'a, 'b> A for () {
| ^^ unconstrained lifetime parameter

error[E0207]: the lifetime parameter `'b` is not constrained by the impl trait, self type, or predicates
--> $DIR/issue-74761.rs:8:10
|
LL | impl<'a, 'b> A for () {
| ^^ unconstrained lifetime parameter

error: aborting due to 2 previous errors

For more information about this error, try `rustc --explain E0207`.
7 changes: 5 additions & 2 deletions src/tools/compiletest/src/header.rs
Original file line number Diff line number Diff line change
@@ -208,10 +208,13 @@ impl EarlyProps {
config.parse_name_value_directive(line, "needs-llvm-components")
{
let components: HashSet<_> = config.llvm_components.split_whitespace().collect();
if !needed_components
if let Some(missing_component) = needed_components
.split_whitespace()
.all(|needed_component| components.contains(needed_component))
.find(|needed_component| !components.contains(needed_component))
{
if env::var_os("COMPILETEST_NEEDS_ALL_LLVM_COMPONENTS").is_some() {
panic!("missing LLVM component: {}", missing_component);
}
return true;
}
}