"rustc_incremental",
"rustc_index",
"rustc_macros",
+ "rustc_metadata",
"rustc_middle",
"rustc_serialize",
"rustc_session",
exclude = [
"build",
"compiler/rustc_codegen_cranelift",
+ "compiler/rustc_codegen_gcc",
"src/test/rustdoc-gui",
# HACK(eddyb) This hardcodes the fact that our CI uses `/checkout/obj`.
"obj",
}
}
+#[instrument(skip(fulfill_cx, infcx), level = "debug")]
fn try_extract_error_from_fulfill_cx<'tcx>(
mut fulfill_cx: Box<dyn TraitEngine<'tcx> + 'tcx>,
infcx: &InferCtxt<'_, 'tcx>,
let _errors = fulfill_cx.select_all_or_error(infcx).err().unwrap_or_else(Vec::new);
let (sub_region, cause) = infcx.with_region_constraints(|region_constraints| {
- debug!(?region_constraints);
+ debug!("{:#?}", region_constraints);
region_constraints.constraints.iter().find_map(|(constraint, cause)| {
match *constraint {
Constraint::RegSubReg(sub, sup) if sup == placeholder_region && sup != sub => {
})
})?;
- debug!(?sub_region, ?cause);
+ debug!(?sub_region, "cause = {:#?}", cause);
let nice_error = match (error_region, sub_region) {
(Some(error_region), &ty::ReVar(vid)) => NiceRegionError::new(
infcx,
ProjectionElem, Rvalue, Statement, StatementKind, Terminator, TerminatorKind, VarBindingForm,
};
use rustc_middle::ty::{self, suggest_constraining_type_param, Ty};
-use rustc_mir_dataflow::drop_flag_effects;
-use rustc_mir_dataflow::move_paths::{MoveOutIndex, MovePathIndex};
+use rustc_mir_dataflow::move_paths::{InitKind, MoveOutIndex, MovePathIndex};
use rustc_span::source_map::DesugaringKind;
use rustc_span::symbol::sym;
use rustc_span::{BytePos, MultiSpan, Span, DUMMY_SP};
}
}
+ let mut mpis = vec![mpi];
+ let move_paths = &self.move_data.move_paths;
+ mpis.extend(move_paths[mpi].parents(move_paths).map(|(mpi, _)| mpi));
+
let mut stack = Vec::new();
- stack.extend(predecessor_locations(self.body, location).map(|predecessor| {
- let is_back_edge = location.dominates(predecessor, &self.dominators);
- (predecessor, is_back_edge)
- }));
+ let mut back_edge_stack = Vec::new();
+
+ predecessor_locations(self.body, location).for_each(|predecessor| {
+ if location.dominates(predecessor, &self.dominators) {
+ back_edge_stack.push(predecessor)
+ } else {
+ stack.push(predecessor);
+ }
+ });
+
+ let mut reached_start = false;
+
+ /* Check if the mpi is initialized as an argument */
+ let mut is_argument = false;
+ for arg in self.body.args_iter() {
+ let path = self.move_data.rev_lookup.find_local(arg);
+ if mpis.contains(&path) {
+ is_argument = true;
+ }
+ }
let mut visited = FxHashSet::default();
let mut move_locations = FxHashSet::default();
let mut reinits = vec![];
let mut result = vec![];
- 'dfs: while let Some((location, is_back_edge)) = stack.pop() {
+ let mut dfs_iter = |result: &mut Vec<MoveSite>, location: Location, is_back_edge: bool| {
debug!(
"report_use_of_moved_or_uninitialized: (current_location={:?}, back_edge={})",
location, is_back_edge
);
if !visited.insert(location) {
- continue;
+ return true;
}
// check for moves
// worry about the other case: that is, if there is a move of a.b.c, it is already
// marked as a move of a.b and a as well, so we will generate the correct errors
// there.
- let mut mpis = vec![mpi];
- let move_paths = &self.move_data.move_paths;
- mpis.extend(move_paths[mpi].parents(move_paths).map(|(mpi, _)| mpi));
-
for moi in &self.move_data.loc_map[location] {
debug!("report_use_of_moved_or_uninitialized: moi={:?}", moi);
let path = self.move_data.moves[*moi].path;
// Because we stop the DFS here, we only highlight `let c = a`,
// and not `let b = a`. We will of course also report an error at
// `let c = a` which highlights `let b = a` as the move.
- continue 'dfs;
+ return true;
}
}
}
// check for inits
let mut any_match = false;
- drop_flag_effects::for_location_inits(
- self.infcx.tcx,
- &self.body,
- self.move_data,
- location,
- |m| {
- if m == mpi {
- any_match = true;
+ for ii in &self.move_data.init_loc_map[location] {
+ let init = self.move_data.inits[*ii];
+ match init.kind {
+ InitKind::Deep | InitKind::NonPanicPathOnly => {
+ if mpis.contains(&init.path) {
+ any_match = true;
+ }
}
- },
- );
+ InitKind::Shallow => {
+ if mpi == init.path {
+ any_match = true;
+ }
+ }
+ }
+ }
if any_match {
reinits.push(location);
- continue 'dfs;
+ return true;
}
+ return false;
+ };
- stack.extend(predecessor_locations(self.body, location).map(|predecessor| {
- let back_edge = location.dominates(predecessor, &self.dominators);
- (predecessor, is_back_edge || back_edge)
- }));
+ while let Some(location) = stack.pop() {
+ if dfs_iter(&mut result, location, false) {
+ continue;
+ }
+
+ let mut has_predecessor = false;
+ predecessor_locations(self.body, location).for_each(|predecessor| {
+ if location.dominates(predecessor, &self.dominators) {
+ back_edge_stack.push(predecessor)
+ } else {
+ stack.push(predecessor);
+ }
+ has_predecessor = true;
+ });
+
+ if !has_predecessor {
+ reached_start = true;
+ }
+ }
+ if (is_argument || !reached_start) && result.is_empty() {
+ /* Process back edges (moves in future loop iterations) only if
+ the move path is definitely initialized upon loop entry,
+ to avoid spurious "in previous iteration" errors.
+ During DFS, if there's a path from the error back to the start
+ of the function with no intervening init or move, then the
+ move path may be uninitialized at loop entry.
+ */
+ while let Some(location) = back_edge_stack.pop() {
+ if dfs_iter(&mut result, location, true) {
+ continue;
+ }
+
+ predecessor_locations(self.body, location)
+ .for_each(|predecessor| back_edge_stack.push(predecessor));
+ }
}
// Check if we can reach these reinits from a move location.
/// If `return_body_with_facts` is true, then return the body with non-erased
/// region ids on which the borrow checking was performed together with Polonius
/// facts.
+#[instrument(skip(infcx, input_body, input_promoted), level = "debug")]
fn do_mir_borrowck<'a, 'tcx>(
infcx: &InferCtxt<'a, 'tcx>,
input_body: &Body<'tcx>,
) -> (BorrowCheckResult<'tcx>, Option<Box<BodyWithBorrowckFacts<'tcx>>>) {
let def = input_body.source.with_opt_param().as_local().unwrap();
- debug!("do_mir_borrowck(def = {:?})", def);
+ debug!(?def);
let tcx = infcx.tcx;
let param_env = tcx.param_env(def.did);
/// Rewrites the regions in the MIR to use NLL variables, also scraping out the set of universal
/// regions (e.g., region parameters) declared on the function. That set will need to be given to
/// `compute_regions`.
+#[instrument(skip(infcx, param_env, body, promoted), level = "debug")]
pub(crate) fn replace_regions_in_mir<'cx, 'tcx>(
infcx: &InferCtxt<'cx, 'tcx>,
param_env: ty::ParamEnv<'tcx>,
) -> UniversalRegions<'tcx> {
let def = body.source.with_opt_param().as_local().unwrap();
- debug!("replace_regions_in_mir(def={:?})", def);
+ debug!(?def);
// Compute named region information. This also renumbers the inputs/outputs.
let universal_regions = UniversalRegions::new(infcx, def, param_env);
/// Performs region inference and report errors if we see any
/// unsatisfiable constraints. If this is a closure, returns the
/// region requirements to propagate to our creator, if any.
+ #[instrument(skip(self, infcx, body, polonius_output), level = "debug")]
pub(super) fn solve(
&mut self,
infcx: &InferCtxt<'_, 'tcx>,
/// for each region variable until all the constraints are
/// satisfied. Note that some values may grow **too** large to be
/// feasible, but we check this later.
+ #[instrument(skip(self, _body), level = "debug")]
fn propagate_constraints(&mut self, _body: &Body<'tcx>) {
- debug!("propagate_constraints()");
-
- debug!("propagate_constraints: constraints={:#?}", {
+ debug!("constraints={:#?}", {
let mut constraints: Vec<_> = self.constraints.outlives().iter().collect();
constraints.sort();
constraints
/// computed, by unioning the values of its successors.
/// Assumes that all successors have been computed already
/// (which is assured by iterating over SCCs in dependency order).
+ #[instrument(skip(self), level = "debug")]
fn compute_value_for_scc(&mut self, scc_a: ConstraintSccIndex) {
let constraint_sccs = self.constraint_sccs.clone();
// Walk each SCC `B` such that `A: B`...
for &scc_b in constraint_sccs.successors(scc_a) {
- debug!("propagate_constraint_sccs: scc_a = {:?} scc_b = {:?}", scc_a, scc_b);
+ debug!(?scc_b);
// ...and add elements from `B` into `A`. One complication
// arises because of universes: If `B` contains something
self.apply_member_constraint(scc_a, m_c_i, member_constraints.choice_regions(m_c_i));
}
- debug!(
- "propagate_constraint_sccs: scc_a = {:?} has value {:?}",
- scc_a,
- self.scc_values.region_value_str(scc_a),
- );
+ debug!(value = ?self.scc_values.region_value_str(scc_a));
}
/// Invoked for each `R0 member of [R1..Rn]` constraint.
/// is considered a *lower bound*. If possible, we will modify
/// the constraint to set it equal to one of the option regions.
/// If we make any changes, returns true, else false.
+ #[instrument(skip(self, member_constraint_index), level = "debug")]
fn apply_member_constraint(
&mut self,
scc: ConstraintSccIndex,
member_constraint_index: NllMemberConstraintIndex,
choice_regions: &[ty::RegionVid],
) -> bool {
- debug!("apply_member_constraint(scc={:?}, choice_regions={:#?})", scc, choice_regions,);
-
// Create a mutable vector of the options. We'll try to winnow
// them down.
let mut choice_regions: Vec<ty::RegionVid> = choice_regions.to_vec();
.universal_regions_outlived_by(scc)
.all(|lb| self.universal_region_relations.outlives(o_r, lb))
});
- debug!("apply_member_constraint: after lb, choice_regions={:?}", choice_regions);
+ debug!(?choice_regions, "after lb");
// Now find all the *upper bounds* -- that is, each UB is a
// free region that must outlive the member region `R0` (`UB:
let rev_scc_graph = self.reverse_scc_graph();
let universal_region_relations = &self.universal_region_relations;
for ub in rev_scc_graph.upper_bounds(scc) {
- debug!("apply_member_constraint: ub={:?}", ub);
+ debug!(?ub);
choice_regions.retain(|&o_r| universal_region_relations.outlives(ub, o_r));
}
- debug!("apply_member_constraint: after ub, choice_regions={:?}", choice_regions);
+ debug!(?choice_regions, "after ub");
// If we ruled everything out, we're done.
if choice_regions.is_empty() {
// Otherwise, we need to find the minimum remaining choice, if
// any, and take that.
- debug!("apply_member_constraint: choice_regions remaining are {:#?}", choice_regions);
+ debug!("choice_regions remaining are {:#?}", choice_regions);
let min = |r1: ty::RegionVid, r2: ty::RegionVid| -> Option<ty::RegionVid> {
let r1_outlives_r2 = self.universal_region_relations.outlives(r1, r2);
let r2_outlives_r1 = self.universal_region_relations.outlives(r2, r1);
};
let mut min_choice = choice_regions[0];
for &other_option in &choice_regions[1..] {
- debug!(
- "apply_member_constraint: min_choice={:?} other_option={:?}",
- min_choice, other_option,
- );
+ debug!(?min_choice, ?other_option,);
match min(min_choice, other_option) {
Some(m) => min_choice = m,
None => {
- debug!(
- "apply_member_constraint: {:?} and {:?} are incomparable; no min choice",
- min_choice, other_option,
- );
+ debug!(?min_choice, ?other_option, "incomparable; no min choice",);
return false;
}
}
}
let min_choice_scc = self.constraint_sccs.scc(min_choice);
- debug!(
- "apply_member_constraint: min_choice={:?} best_choice_scc={:?}",
- min_choice, min_choice_scc,
- );
+ debug!(?min_choice, ?min_choice_scc);
if self.scc_values.add_region(scc, min_choice_scc) {
self.member_constraints_applied.push(AppliedMemberConstraint {
member_region_scc: scc,
/// include the CFG anyhow.
/// - For each `end('x)` element in `'r`, compute the mutual LUB, yielding
/// a result `'y`.
+ #[instrument(skip(self), level = "debug")]
pub(crate) fn universal_upper_bound(&self, r: RegionVid) -> RegionVid {
- debug!("universal_upper_bound(r={:?}={})", r, self.region_value_str(r));
+ debug!(r = %self.region_value_str(r));
// Find the smallest universal region that contains all other
// universal regions within `region`.
lub = self.universal_region_relations.postdom_upper_bound(lub, ur);
}
- debug!("universal_upper_bound: r={:?} lub={:?}", r, lub);
+ debug!(?lub);
lub
}
}
// Evaluate whether `sup_region: sub_region`.
+ #[instrument(skip(self), level = "debug")]
fn eval_outlives(&self, sup_region: RegionVid, sub_region: RegionVid) -> bool {
- debug!("eval_outlives({:?}: {:?})", sup_region, sub_region);
-
debug!(
"eval_outlives: sup_region's value = {:?} universal={:?}",
self.region_value_str(sup_region),
///
/// Things that are to be propagated are accumulated into the
/// `outlives_requirements` vector.
+ #[instrument(
+ skip(self, body, propagated_outlives_requirements, errors_buffer),
+ level = "debug"
+ )]
fn check_universal_region(
&self,
body: &Body<'tcx>,
propagated_outlives_requirements: &mut Option<&mut Vec<ClosureOutlivesRequirement<'tcx>>>,
errors_buffer: &mut RegionErrors<'tcx>,
) {
- debug!("check_universal_region(fr={:?})", longer_fr);
-
let longer_fr_scc = self.constraint_sccs.scc(longer_fr);
// Because this free region must be in the ROOT universe, we
}
/// Finds some region R such that `fr1: R` and `R` is live at `elem`.
+ #[instrument(skip(self), level = "trace")]
crate fn find_sub_region_live_at(&self, fr1: RegionVid, elem: Location) -> RegionVid {
- debug!("find_sub_region_live_at(fr1={:?}, elem={:?})", fr1, elem);
- debug!("find_sub_region_live_at: {:?} is in scc {:?}", fr1, self.constraint_sccs.scc(fr1));
- debug!(
- "find_sub_region_live_at: {:?} is in universe {:?}",
- fr1,
- self.scc_universes[self.constraint_sccs.scc(fr1)]
- );
+ trace!(scc = ?self.constraint_sccs.scc(fr1));
+ trace!(universe = ?self.scc_universes[self.constraint_sccs.scc(fr1)]);
self.find_constraint_paths_between_regions(fr1, |r| {
// First look for some `r` such that `fr1: r` and `r` is live at `elem`
- debug!(
- "find_sub_region_live_at: liveness_constraints for {:?} are {:?}",
- r,
- self.liveness_constraints.region_value_str(r),
- );
+ trace!(?r, liveness_constraints=?self.liveness_constraints.region_value_str(r));
self.liveness_constraints.contains(r, elem)
})
.or_else(|| {
/// Replaces all free regions appearing in the MIR with fresh
/// inference variables, returning the number of variables created.
+#[instrument(skip(infcx, body, promoted), level = "debug")]
pub fn renumber_mir<'tcx>(
infcx: &InferCtxt<'_, 'tcx>,
body: &mut Body<'tcx>,
promoted: &mut IndexVec<Promoted, Body<'tcx>>,
) {
- debug!("renumber_mir()");
- debug!("renumber_mir: body.arg_count={:?}", body.arg_count);
+ debug!(?body.arg_count);
let mut visitor = NllVisitor { infcx };
/// Replaces all regions appearing in `value` with fresh inference
/// variables.
+#[instrument(skip(infcx), level = "debug")]
pub fn renumber_regions<'tcx, T>(infcx: &InferCtxt<'_, 'tcx>, value: T) -> T
where
T: TypeFoldable<'tcx>,
{
- debug!("renumber_regions(value={:?})", value);
-
infcx.tcx.fold_regions(value, &mut false, |_region, _depth| {
let origin = NllRegionVariableOrigin::Existential { from_forall: false };
infcx.next_nll_region_var(origin)
self.infcx.tcx
}
+ #[instrument(skip(self), level = "debug")]
fn visit_ty(&mut self, ty: &mut Ty<'tcx>, ty_context: TyContext) {
- debug!("visit_ty(ty={:?}, ty_context={:?})", ty, ty_context);
-
*ty = self.renumber_regions(ty);
- debug!("visit_ty: ty={:?}", ty);
+ debug!(?ty);
}
fn process_projection_elem(
None
}
+ #[instrument(skip(self), level = "debug")]
fn visit_substs(&mut self, substs: &mut SubstsRef<'tcx>, location: Location) {
- debug!("visit_substs(substs={:?}, location={:?})", substs, location);
-
*substs = self.renumber_regions(*substs);
- debug!("visit_substs: substs={:?}", substs);
+ debug!(?substs);
}
+ #[instrument(skip(self), level = "debug")]
fn visit_region(&mut self, region: &mut ty::Region<'tcx>, location: Location) {
- debug!("visit_region(region={:?}, location={:?})", region, location);
-
let old_region = *region;
*region = self.renumber_regions(&old_region);
- debug!("visit_region: region={:?}", region);
+ debug!(?region);
}
fn visit_const(&mut self, constant: &mut &'tcx ty::Const<'tcx>, _location: Location) {
/// **Any `rustc_infer::infer` operations that might generate region
/// constraints should occur within this method so that those
/// constraints can be properly localized!**
+ #[instrument(skip(self, category, op), level = "trace")]
pub(super) fn fully_perform_op<R, Op>(
&mut self,
locations: Locations,
}
}
+ #[instrument(skip(self), level = "debug")]
pub(super) fn prove_predicate(
&mut self,
predicate: ty::Predicate<'tcx>,
locations: Locations,
category: ConstraintCategory,
) {
- debug!("prove_predicate(predicate={:?}, location={:?})", predicate, locations,);
-
let param_env = self.param_env;
self.fully_perform_op(
locations,
})
}
+ #[instrument(skip(self), level = "debug")]
pub(super) fn normalize<T>(&mut self, value: T, location: impl NormalizeLocation) -> T
where
T: type_op::normalize::Normalizable<'tcx> + fmt::Display + Copy + 'tcx,
{
- debug!("normalize(value={:?}, location={:?})", value, location);
let param_env = self.param_env;
self.fully_perform_op(
location.to_locations(),
}
}
+ #[instrument(skip(self), level = "debug")]
pub(super) fn convert_all(&mut self, query_constraints: &QueryRegionConstraints<'tcx>) {
- debug!("convert_all(query_constraints={:#?})", query_constraints);
-
let QueryRegionConstraints { outlives, member_constraints } = query_constraints;
// Annoying: to invoke `self.to_region_vid`, we need access to
use super::{Locations, TypeChecker};
impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
+ #[instrument(skip(self, body, universal_regions), level = "debug")]
pub(super) fn equate_inputs_and_outputs(
&mut self,
body: &Body<'tcx>,
);
}
- debug!(
- "equate_inputs_and_outputs: normalized_input_tys = {:?}, local_decls = {:?}",
- normalized_input_tys, body.local_decls
- );
+ debug!(?normalized_input_tys, ?body.local_decls);
// Equate expected input tys with those in the MIR.
for (argument_index, &normalized_input_ty) in normalized_input_tys.iter().enumerate() {
}
}
+ #[instrument(skip(self, span), level = "debug")]
fn equate_normalized_input_or_output(&mut self, a: Ty<'tcx>, b: Ty<'tcx>, span: Span) {
- debug!("equate_normalized_input_or_output(a={:?}, b={:?})", a, b);
-
if let Err(_) =
self.eq_types(a, b, Locations::All(span), ConstraintCategory::BoringNoLocation)
{
.into_iter()
.filter_map(|(opaque_type_key, mut decl)| {
decl.concrete_ty = infcx.resolve_vars_if_possible(decl.concrete_ty);
+ trace!(
+ "finalized opaque type {:?} to {:#?}",
+ opaque_type_key,
+ decl.concrete_ty.kind()
+ );
if decl.concrete_ty.has_infer_types_or_consts() {
infcx.tcx.sess.delay_span_bug(
body.span,
MirTypeckResults { constraints, universal_region_relations, opaque_type_values }
}
+#[instrument(
+ skip(
+ infcx,
+ body,
+ promoted,
+ region_bound_pairs,
+ borrowck_context,
+ universal_region_relations,
+ extra
+ ),
+ level = "debug"
+)]
fn type_check_internal<'a, 'tcx, R>(
infcx: &'a InferCtxt<'a, 'tcx>,
param_env: ty::ParamEnv<'tcx>,
}
}
+ #[instrument(skip(self, data), level = "debug")]
fn push_region_constraints(
&mut self,
locations: Locations,
category: ConstraintCategory,
data: &QueryRegionConstraints<'tcx>,
) {
- debug!("push_region_constraints: constraints generated at {:?} are {:#?}", locations, data);
+ debug!("constraints generated: {:#?}", data);
constraint_conversion::ConstraintConversion::new(
self.infcx,
self.relate_types(expected, ty::Variance::Invariant, found, locations, category)
}
+ #[instrument(skip(self), level = "debug")]
fn relate_type_and_user_type(
&mut self,
a: Ty<'tcx>,
locations: Locations,
category: ConstraintCategory,
) -> Fallible<()> {
- debug!(
- "relate_type_and_user_type(a={:?}, v={:?}, user_ty={:?}, locations={:?})",
- a, v, user_ty, locations,
- );
-
let annotated_type = self.user_type_annotations[user_ty.base].inferred_ty;
let mut curr_projected_ty = PlaceTy::from_ty(annotated_type);
/// generics of `foo`). Note that `anon_ty` is not just the opaque type,
/// but the entire return type (which may contain opaque types within it).
/// * `revealed_ty` would be `Box<(T, u32)>`
+ #[instrument(skip(self), level = "debug")]
fn eq_opaque_type_and_type(
&mut self,
revealed_ty: Ty<'tcx>,
locations: Locations,
category: ConstraintCategory,
) -> Fallible<()> {
- debug!(
- "eq_opaque_type_and_type( \
- revealed_ty={:?}, \
- anon_ty={:?})",
- revealed_ty, anon_ty
- );
-
// Fast path for the common case.
if !anon_ty.has_opaque_types() {
if let Err(terr) = self.eq_types(anon_ty, revealed_ty, locations, category) {
let body = self.body;
let mir_def_id = body.source.def_id().expect_local();
- debug!("eq_opaque_type_and_type: mir_def_id={:?}", mir_def_id);
+ debug!(?mir_def_id);
self.fully_perform_op(
locations,
category,
anon_ty,
locations.span(body),
));
- debug!(
- "eq_opaque_type_and_type: \
- instantiated output_ty={:?} \
- revealed_ty={:?}",
- output_ty, revealed_ty
- );
+ debug!(?output_ty, ?revealed_ty);
// Make sure that the inferred types are well-formed. I'm
// not entirely sure this is needed (the HIR type check
.eq(output_ty, revealed_ty)?,
);
- debug!("eq_opaque_type_and_type: equated");
+ debug!("equated");
Ok(InferOk { value: (), obligations: obligations.into_vec() })
},
self.infcx.tcx
}
+ #[instrument(skip(self, body, location), level = "debug")]
fn check_stmt(&mut self, body: &Body<'tcx>, stmt: &Statement<'tcx>, location: Location) {
- debug!("check_stmt: {:?}", stmt);
let tcx = self.tcx();
match stmt.kind {
StatementKind::Assign(box (ref place, ref rv)) => {
}
}
+ #[instrument(skip(self, body, term_location), level = "debug")]
fn check_terminator(
&mut self,
body: &Body<'tcx>,
term: &Terminator<'tcx>,
term_location: Location,
) {
- debug!("check_terminator: {:?}", term);
let tcx = self.tcx();
match term.kind {
TerminatorKind::Goto { .. }
tcx.predicates_of(def_id).instantiate(tcx, substs)
}
+ #[instrument(skip(self, body), level = "debug")]
fn typeck_mir(&mut self, body: &Body<'tcx>) {
self.last_span = body.span;
- debug!("run_on_mir: {:?}", body.span);
+ debug!(?body.span);
for (local, local_decl) in body.local_decls.iter_enumerated() {
self.check_local(&body, local, local_decl);
///
/// N.B., the type `a` is permitted to have unresolved inference
/// variables, but not the type `b`.
+#[instrument(skip(infcx, param_env, borrowck_context), level = "debug")]
pub(super) fn relate_types<'tcx>(
infcx: &InferCtxt<'_, 'tcx>,
param_env: ty::ParamEnv<'tcx>,
category: ConstraintCategory,
borrowck_context: &mut BorrowCheckContext<'_, 'tcx>,
) -> Fallible<()> {
- debug!("relate_types(a={:?}, v={:?}, b={:?}, locations={:?})", a, v, b, locations);
TypeRelating::new(
infcx,
NllTypeRelatingDelegate::new(
stack = &stack[..index + REPORT_SYMBOL_NAMES.len()];
}
- const ENCODE_METADATA: &str = "rustc_middle::ty::context::TyCtxt::encode_metadata";
+ const ENCODE_METADATA: &str = "rustc_metadata::rmeta::encoder::encode_metadata";
if let Some(index) = stack.find(ENCODE_METADATA) {
stack = &stack[..index + ENCODE_METADATA.len()];
}
use rustc_ast::{InlineAsmOptions, InlineAsmTemplatePiece};
use rustc_codegen_ssa::{CodegenResults, CompiledModule, CrateInfo, ModuleKind};
use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
+use rustc_metadata::EncodedMetadata;
use rustc_middle::dep_graph::{WorkProduct, WorkProductId};
-use rustc_middle::middle::cstore::EncodedMetadata;
use rustc_middle::mir::mono::{CodegenUnit, MonoItem};
use rustc_session::cgu_reuse_tracker::CguReuse;
use rustc_session::config::{DebugInfo, OutputType};
use rustc_codegen_ssa::traits::CodegenBackend;
use rustc_codegen_ssa::CodegenResults;
use rustc_errors::ErrorReported;
+use rustc_metadata::EncodedMetadata;
use rustc_middle::dep_graph::{WorkProduct, WorkProductId};
-use rustc_middle::middle::cstore::EncodedMetadata;
use rustc_session::config::OutputFilenames;
use rustc_session::Session;
use object::write::{Object, StandardSegment, Symbol, SymbolSection};
use object::{SectionKind, SymbolFlags, SymbolKind, SymbolScope};
-use rustc_middle::middle::cstore::EncodedMetadata;
+use rustc_metadata::EncodedMetadata;
use rustc_middle::ty::TyCtxt;
// Adapted from https://github.com/rust-lang/rust/blob/da573206f87b5510de4b0ee1a9c044127e409bd3/src/librustc_codegen_llvm/base.rs#L47-L112
-pub(crate) fn new_metadata_object(tcx: TyCtxt<'_>, cgu_name: &str, metadata: &EncodedMetadata) -> Vec<u8> {
+pub(crate) fn new_metadata_object(
+ tcx: TyCtxt<'_>,
+ cgu_name: &str,
+ metadata: &EncodedMetadata,
+) -> Vec<u8> {
use snap::write::FrameEncoder;
use std::io::Write;
let mut compressed = rustc_metadata::METADATA_HEADER.to_vec();
- FrameEncoder::new(&mut compressed).write_all(&metadata.raw_data).unwrap();
+ FrameEncoder::new(&mut compressed).write_all(metadata.raw_data()).unwrap();
let triple = crate::target_triple(tcx.sess);
--- /dev/null
+name: CI
+
+on:
+ - push
+ - pull_request
+
+jobs:
+ build:
+ runs-on: ubuntu-latest
+
+ strategy:
+ fail-fast: false
+
+ steps:
+ - uses: actions/checkout@v2
+
+ - name: Install packages
+ run: sudo apt-get install ninja-build ripgrep
+
+ - name: Download artifact
+ uses: dawidd6/action-download-artifact@v2
+ with:
+ workflow: main.yml
+ name: libgccjit.so
+ path: gcc-build
+ repo: antoyo/gcc
+
+ - name: Setup path to libgccjit
+ run: |
+ echo $(readlink -f gcc-build) > gcc_path
+ ln gcc-build/libgccjit.so gcc-build/libgccjit.so.0
+
+ - name: Set LIBRARY_PATH
+ run: |
+ echo "LIBRARY_PATH=$(cat gcc_path)" >> $GITHUB_ENV
+ echo "LD_LIBRARY_PATH=$(cat gcc_path)" >> $GITHUB_ENV
+
+ # https://github.com/actions/cache/issues/133
+ - name: Fixup owner of ~/.cargo/
+ # Don't remove the trailing /. It is necessary to follow the symlink.
+ run: sudo chown -R $(whoami):$(id -ng) ~/.cargo/
+
+ - name: Cache cargo installed crates
+ uses: actions/cache@v1.1.2
+ with:
+ path: ~/.cargo/bin
+ key: cargo-installed-crates2-ubuntu-latest
+
+ - name: Cache cargo registry
+ uses: actions/cache@v1
+ with:
+ path: ~/.cargo/registry
+ key: ${{ runner.os }}-cargo-registry2-${{ hashFiles('**/Cargo.lock') }}
+
+ - name: Cache cargo index
+ uses: actions/cache@v1
+ with:
+ path: ~/.cargo/git
+ key: ${{ runner.os }}-cargo-index-${{ hashFiles('**/Cargo.lock') }}
+
+ - name: Cache cargo target dir
+ uses: actions/cache@v1.1.2
+ with:
+ path: target
+ key: ${{ runner.os }}-cargo-build-target-${{ hashFiles('rust-toolchain') }}
+
+ - name: Build
+ run: |
+ ./prepare_build.sh
+ ./build.sh
+ cargo test
+ ./clean_all.sh
+
+ - name: Prepare dependencies
+ run: |
+ git config --global user.email "user@example.com"
+ git config --global user.name "User"
+ ./prepare.sh
+
+ # Compile is a separate step, as the actions-rs/cargo action supports error annotations
+ - name: Compile
+ uses: actions-rs/cargo@v1.0.3
+ with:
+ command: build
+ args: --release
+
+ - name: Test
+ run: |
+ # Enable backtraces for easier debugging
+ export RUST_BACKTRACE=1
+
+ # Reduce amount of benchmark runs as they are slow
+ export COMPILE_RUNS=2
+ export RUN_RUNS=2
+
+ ./test.sh --release
--- /dev/null
+target
+**/*.rs.bk
+*.rlib
+*.o
+perf.data
+perf.data.old
+*.events
+*.string*
+/build_sysroot/sysroot
+/build_sysroot/sysroot_src
+/build_sysroot/Cargo.lock
+/build_sysroot/test_target/Cargo.lock
+/rust
+/simple-raytracer
+/regex
+gimple*
+*asm
+res
+test-backend
+gcc_path
--- /dev/null
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+version = 3
+
+[[package]]
+name = "aho-corasick"
+version = "0.7.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1e37cfd5e7657ada45f742d6e99ca5788580b5c529dc78faf11ece6dc702656f"
+dependencies = [
+ "memchr",
+]
+
+[[package]]
+name = "ar"
+version = "0.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "450575f58f7bee32816abbff470cbc47797397c2a81e0eaced4b98436daf52e1"
+
+[[package]]
+name = "autocfg"
+version = "1.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a"
+
+[[package]]
+name = "bitflags"
+version = "1.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
+
+[[package]]
+name = "cfg-if"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
+
+[[package]]
+name = "crc32fast"
+version = "1.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "81156fece84ab6a9f2afdb109ce3ae577e42b1228441eded99bd77f627953b1a"
+dependencies = [
+ "cfg-if",
+]
+
+[[package]]
+name = "fm"
+version = "0.1.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "68fda3cff2cce84c19e5dfa5179a4b35d2c0f18b893f108002b8a6a54984acca"
+dependencies = [
+ "regex",
+]
+
+[[package]]
+name = "gccjit"
+version = "1.0.0"
+source = "git+https://github.com/antoyo/gccjit.rs#2d4fea7319f80531b2e5d264fca9f1c498a3a62e"
+dependencies = [
+ "gccjit_sys",
+]
+
+[[package]]
+name = "gccjit_sys"
+version = "0.0.1"
+source = "git+https://github.com/antoyo/gccjit.rs#2d4fea7319f80531b2e5d264fca9f1c498a3a62e"
+dependencies = [
+ "libc 0.1.12",
+]
+
+[[package]]
+name = "getopts"
+version = "0.2.21"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "14dbbfd5c71d70241ecf9e6f13737f7b5ce823821063188d7e46c41d371eebd5"
+dependencies = [
+ "unicode-width",
+]
+
+[[package]]
+name = "getrandom"
+version = "0.2.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7fcd999463524c52659517fe2cea98493cfe485d10565e7b0fb07dbba7ad2753"
+dependencies = [
+ "cfg-if",
+ "libc 0.2.102",
+ "wasi",
+]
+
+[[package]]
+name = "hashbrown"
+version = "0.11.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e"
+
+[[package]]
+name = "hermit-abi"
+version = "0.1.19"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33"
+dependencies = [
+ "libc 0.2.102",
+]
+
+[[package]]
+name = "indexmap"
+version = "1.7.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bc633605454125dec4b66843673f01c7df2b89479b32e0ed634e43a91cff62a5"
+dependencies = [
+ "autocfg",
+ "hashbrown",
+]
+
+[[package]]
+name = "lang_tester"
+version = "0.3.13"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "96bd995a092cac79868250589869b5a5d656b02a02bd74c8ebdc566dc7203090"
+dependencies = [
+ "fm",
+ "getopts",
+ "libc 0.2.102",
+ "num_cpus",
+ "termcolor",
+ "threadpool",
+ "wait-timeout",
+ "walkdir",
+]
+
+[[package]]
+name = "libc"
+version = "0.1.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e32a70cf75e5846d53a673923498228bbec6a8624708a9ea5645f075d6276122"
+
+[[package]]
+name = "libc"
+version = "0.2.102"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a2a5ac8f984bfcf3a823267e5fde638acc3325f6496633a5da6bb6eb2171e103"
+
+[[package]]
+name = "memchr"
+version = "2.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a"
+
+[[package]]
+name = "num_cpus"
+version = "1.13.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "05499f3756671c15885fee9034446956fff3f243d6077b91e5767df161f766b3"
+dependencies = [
+ "hermit-abi",
+ "libc 0.2.102",
+]
+
+[[package]]
+name = "object"
+version = "0.25.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a38f2be3697a57b4060074ff41b44c16870d916ad7877c17696e063257482bc7"
+dependencies = [
+ "crc32fast",
+ "indexmap",
+ "memchr",
+]
+
+[[package]]
+name = "ppv-lite86"
+version = "0.2.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ac74c624d6b2d21f425f752262f42188365d7b8ff1aff74c82e45136510a4857"
+
+[[package]]
+name = "rand"
+version = "0.8.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2e7573632e6454cf6b99d7aac4ccca54be06da05aca2ef7423d22d27d4d4bcd8"
+dependencies = [
+ "libc 0.2.102",
+ "rand_chacha",
+ "rand_core",
+ "rand_hc",
+]
+
+[[package]]
+name = "rand_chacha"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
+dependencies = [
+ "ppv-lite86",
+ "rand_core",
+]
+
+[[package]]
+name = "rand_core"
+version = "0.6.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d34f1408f55294453790c48b2f1ebbb1c5b4b7563eb1f418bcfcfdbb06ebb4e7"
+dependencies = [
+ "getrandom",
+]
+
+[[package]]
+name = "rand_hc"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d51e9f596de227fda2ea6c84607f5558e196eeaf43c986b724ba4fb8fdf497e7"
+dependencies = [
+ "rand_core",
+]
+
+[[package]]
+name = "redox_syscall"
+version = "0.2.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8383f39639269cde97d255a32bdb68c047337295414940c68bdd30c2e13203ff"
+dependencies = [
+ "bitflags",
+]
+
+[[package]]
+name = "regex"
+version = "1.5.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d07a8629359eb56f1e2fb1652bb04212c072a87ba68546a04065d525673ac461"
+dependencies = [
+ "aho-corasick",
+ "memchr",
+ "regex-syntax",
+]
+
+[[package]]
+name = "regex-syntax"
+version = "0.6.25"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b"
+
+[[package]]
+name = "remove_dir_all"
+version = "0.5.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7"
+dependencies = [
+ "winapi",
+]
+
+[[package]]
+name = "rustc_codegen_gcc"
+version = "0.1.0"
+dependencies = [
+ "ar",
+ "gccjit",
+ "lang_tester",
+ "object",
+ "target-lexicon",
+ "tempfile",
+]
+
+[[package]]
+name = "same-file"
+version = "1.0.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502"
+dependencies = [
+ "winapi-util",
+]
+
+[[package]]
+name = "target-lexicon"
+version = "0.10.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ab0e7238dcc7b40a7be719a25365910f6807bd864f4cce6b2e6b873658e2b19d"
+
+[[package]]
+name = "tempfile"
+version = "3.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dac1c663cfc93810f88aed9b8941d48cabf856a1b111c29a40439018d870eb22"
+dependencies = [
+ "cfg-if",
+ "libc 0.2.102",
+ "rand",
+ "redox_syscall",
+ "remove_dir_all",
+ "winapi",
+]
+
+[[package]]
+name = "termcolor"
+version = "1.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2dfed899f0eb03f32ee8c6a0aabdb8a7949659e3466561fc0adf54e26d88c5f4"
+dependencies = [
+ "winapi-util",
+]
+
+[[package]]
+name = "threadpool"
+version = "1.8.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d050e60b33d41c19108b32cea32164033a9013fe3b46cbd4457559bfbf77afaa"
+dependencies = [
+ "num_cpus",
+]
+
+[[package]]
+name = "unicode-width"
+version = "0.1.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3ed742d4ea2bd1176e236172c8429aaf54486e7ac098db29ffe6529e0ce50973"
+
+[[package]]
+name = "wait-timeout"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9f200f5b12eb75f8c1ed65abd4b2db8a6e1b138a20de009dacee265a2498f3f6"
+dependencies = [
+ "libc 0.2.102",
+]
+
+[[package]]
+name = "walkdir"
+version = "2.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "808cf2735cd4b6866113f648b791c6adc5714537bc222d9347bb203386ffda56"
+dependencies = [
+ "same-file",
+ "winapi",
+ "winapi-util",
+]
+
+[[package]]
+name = "wasi"
+version = "0.10.2+wasi-snapshot-preview1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6"
+
+[[package]]
+name = "winapi"
+version = "0.3.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
+dependencies = [
+ "winapi-i686-pc-windows-gnu",
+ "winapi-x86_64-pc-windows-gnu",
+]
+
+[[package]]
+name = "winapi-i686-pc-windows-gnu"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
+
+[[package]]
+name = "winapi-util"
+version = "0.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178"
+dependencies = [
+ "winapi",
+]
+
+[[package]]
+name = "winapi-x86_64-pc-windows-gnu"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
--- /dev/null
+[package]
+name = "rustc_codegen_gcc"
+version = "0.1.0"
+authors = ["Antoni Boucher <bouanto@zoho.com>"]
+edition = "2018"
+license = "MIT OR Apache-2.0"
+
+[lib]
+crate-type = ["dylib"]
+
+[[test]]
+name = "lang_tests"
+path = "tests/lib.rs"
+harness = false
+
+[dependencies]
+gccjit = { git = "https://github.com/antoyo/gccjit.rs" }
+
+# Local copy.
+#gccjit = { path = "../gccjit.rs" }
+
+target-lexicon = "0.10.0"
+
+ar = "0.8.0"
+
+[dependencies.object]
+version = "0.25.0"
+default-features = false
+features = ["read", "std", "write"] # We don't need WASM support.
+
+[dev-dependencies]
+lang_tester = "0.3.9"
+tempfile = "3.1.0"
+
+[profile.dev]
+# By compiling dependencies with optimizations, performing tests gets much faster.
+opt-level = 3
+
+[profile.dev.package.rustc_codegen_gcc]
+# Disabling optimizations for cg_gccjit itself makes compilation after a change faster.
+opt-level = 0
+
+# Disable optimizations and debuginfo of build scripts and some of the heavy build deps, as the
+# execution time of build scripts is so fast that optimizing them slows down the total build time.
+[profile.dev.build-override]
+opt-level = 0
+debug = false
+
+[profile.release.build-override]
+opt-level = 0
+debug = false
--- /dev/null
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+END OF TERMS AND CONDITIONS
--- /dev/null
+Permission is hereby granted, free of charge, to any
+person obtaining a copy of this software and associated
+documentation files (the "Software"), to deal in the
+Software without restriction, including without
+limitation the rights to use, copy, modify, merge,
+publish, distribute, sublicense, and/or sell copies of
+the Software, and to permit persons to whom the Software
+is furnished to do so, subject to the following
+conditions:
+
+The above copyright notice and this permission notice
+shall be included in all copies or substantial portions
+of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
+ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
+TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
+PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
+SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
+IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
--- /dev/null
+# WIP libgccjit codegen backend for rust
+
+This is a GCC codegen for rustc, which means it can be loaded by the existing rustc frontend, but benefits from GCC: more architectures are supported and GCC's optimizations are used.
+
+**Despite its name, libgccjit can be used for ahead-of-time compilation, as is used here.**
+
+## Motivation
+
+The primary goal of this project is to be able to compile Rust code on platforms unsupported by LLVM.
+A secondary goal is to check if using the gcc backend will provide any run-time speed improvement for the programs compiled using rustc.
+
+## Building
+
+**This requires a patched libgccjit in order to work.
+The patches in [this repostory](https://github.com/antoyo/libgccjit-patches) need to be applied.
+(Those patches should work when applied on master, but in case it doesn't work, they are known to work when applied on 079c23cfe079f203d5df83fea8e92a60c7d7e878.)
+You can also use my [fork of gcc](https://github.com/antoyo/gcc) which already includes these patches.**
+
+**Put the path to your custom build of libgccjit in the file `gcc_path`.**
+
+```bash
+$ git clone https://github.com/rust-lang/rustc_codegen_gcc.git
+$ cd rustc_codegen_gcc
+$ ./prepare_build.sh # download and patch sysroot src
+$ ./build.sh --release
+```
+
+To run the tests:
+
+```bash
+$ ./prepare.sh # download and patch sysroot src and install hyperfine for benchmarking
+$ ./test.sh --release
+```
+
+## Usage
+
+`$cg_gccjit_dir` is the directory you cloned this repo into in the following instructions.
+
+### Cargo
+
+```bash
+$ CHANNEL="release" $cg_gccjit_dir/cargo.sh run
+```
+
+If you compiled cg_gccjit in debug mode (aka you didn't pass `--release` to `./test.sh`) you should use `CHANNEL="debug"` instead or omit `CHANNEL="release"` completely.
+
+### Rustc
+
+> You should prefer using the Cargo method.
+
+```bash
+$ rustc +$(cat $cg_gccjit_dir/rust-toolchain) -Cpanic=abort -Zcodegen-backend=$cg_gccjit_dir/target/release/librustc_codegen_gcc.so --sysroot $cg_gccjit_dir/build_sysroot/sysroot my_crate.rs
+```
+
+## Env vars
+
+<dl>
+ <dt>CG_GCCJIT_INCR_CACHE_DISABLED</dt>
+ <dd>Don't cache object files in the incremental cache. Useful during development of cg_gccjit
+ to make it possible to use incremental mode for all analyses performed by rustc without caching
+ object files when their content should have been changed by a change to cg_gccjit.</dd>
+ <dt>CG_GCCJIT_DISPLAY_CG_TIME</dt>
+ <dd>Display the time it took to perform codegen for a crate</dd>
+</dl>
+
+## Debugging
+
+Sometimes, libgccjit will crash and output an error like this:
+
+```
+during RTL pass: expand
+libgccjit.so: error: in expmed_mode_index, at expmed.h:249
+0x7f0da2e61a35 expmed_mode_index
+ ../../../gcc/gcc/expmed.h:249
+0x7f0da2e61aa4 expmed_op_cost_ptr
+ ../../../gcc/gcc/expmed.h:271
+0x7f0da2e620dc sdiv_cost_ptr
+ ../../../gcc/gcc/expmed.h:540
+0x7f0da2e62129 sdiv_cost
+ ../../../gcc/gcc/expmed.h:558
+0x7f0da2e73c12 expand_divmod(int, tree_code, machine_mode, rtx_def*, rtx_def*, rtx_def*, int)
+ ../../../gcc/gcc/expmed.c:4335
+0x7f0da2ea1423 expand_expr_real_2(separate_ops*, rtx_def*, machine_mode, expand_modifier)
+ ../../../gcc/gcc/expr.c:9240
+0x7f0da2cd1a1e expand_gimple_stmt_1
+ ../../../gcc/gcc/cfgexpand.c:3796
+0x7f0da2cd1c30 expand_gimple_stmt
+ ../../../gcc/gcc/cfgexpand.c:3857
+0x7f0da2cd90a9 expand_gimple_basic_block
+ ../../../gcc/gcc/cfgexpand.c:5898
+0x7f0da2cdade8 execute
+ ../../../gcc/gcc/cfgexpand.c:6582
+```
+
+To see the code which causes this error, call the following function:
+
+```c
+gcc_jit_context_dump_to_file(ctxt, "/tmp/output.c", 1 /* update_locations */)
+```
+
+This will create a C-like file and add the locations into the IR pointing to this C file.
+Then, rerun the program and it will output the location in the second line:
+
+```
+libgccjit.so: /tmp/something.c:61322:0: error: in expmed_mode_index, at expmed.h:249
+```
+
+Or add a breakpoint to `add_error` in gdb and print the line number using:
+
+```
+p loc->m_line
+```
+
+### How to use a custom-build rustc
+
+ * Build the stage2 compiler (`rustup toolchain link debug-current build/x86_64-unknown-linux-gnu/stage2`).
+ * Clean and rebuild the codegen with `debug-current` in the file `rust-toolchain`.
+
+### How to build a cross-compiling libgccjit
+
+#### Building libgccjit
+
+ * Follow these instructions: https://preshing.com/20141119/how-to-build-a-gcc-cross-compiler/ with the following changes:
+ * Configure gcc with `../gcc/configure --enable-host-shared --disable-multilib --enable-languages=c,jit,c++ --disable-bootstrap --enable-checking=release --prefix=/opt/m68k-gcc/ --target=m68k-linux --without-headers`.
+ * Some shells, like fish, don't define the environment variable `$MACHTYPE`.
+ * Add `CFLAGS="-Wno-error=attributes -g -O2"` at the end of the configure command for building glibc (`CFLAGS="-Wno-error=attributes -Wno-error=array-parameter -Wno-error=stringop-overflow -Wno-error=array-bounds -g -O2"` for glibc 2.31, which is useful for Debian).
+
+#### Configuring rustc_codegen_gcc
+
+ * Set `TARGET_TRIPLE="m68k-unknown-linux-gnu"` in config.sh.
+ * Since rustc doesn't support this architecture yet, set it back to `TARGET_TRIPLE="mips-unknown-linux-gnu"` (or another target having the same attributes). Alternatively, create a [target specification file](https://book.avr-rust.com/005.1-the-target-specification-json-file.html) (note that the `arch` specified in this file must be supported by the rust compiler).
+ * Set `linker='-Clinker=m68k-linux-gcc'`.
+ * Set the path to the cross-compiling libgccjit in `gcc_path`.
+ * Disable the 128-bit integer types if the target doesn't support them by using `let i128_type = context.new_type::<i64>();` in `context.rs` (same for u128_type).
+ * (might not be necessary) Disable the compilation of libstd.so (and possibly libcore.so?).
--- /dev/null
+#!/bin/bash
+
+#set -x
+set -e
+
+if [ -f ./gcc_path ]; then
+ export GCC_PATH=$(cat gcc_path)
+else
+ echo 'Please put the path to your custom build of libgccjit in the file `gcc_path`, see Readme.md for details'
+ exit 1
+fi
+
+export LD_LIBRARY_PATH="$GCC_PATH"
+export LIBRARY_PATH="$GCC_PATH"
+
+if [[ "$1" == "--release" ]]; then
+ export CHANNEL='release'
+ CARGO_INCREMENTAL=1 cargo rustc --release
+else
+ echo $LD_LIBRARY_PATH
+ export CHANNEL='debug'
+ cargo rustc
+fi
+
+source config.sh
+
+rm -r target/out || true
+mkdir -p target/out/gccjit
+
+echo "[BUILD] sysroot"
+time ./build_sysroot/build_sysroot.sh $CHANNEL
--- /dev/null
+[package]
+authors = ["bjorn3 <bjorn3@users.noreply.github.com>"]
+name = "sysroot"
+version = "0.0.0"
+
+[dependencies]
+core = { path = "./sysroot_src/library/core" }
+compiler_builtins = "0.1"
+alloc = { path = "./sysroot_src/library/alloc" }
+std = { path = "./sysroot_src/library/std", features = ["panic_unwind", "backtrace"] }
+test = { path = "./sysroot_src/library/test" }
+
+[patch.crates-io]
+rustc-std-workspace-core = { path = "./sysroot_src/library/rustc-std-workspace-core" }
+rustc-std-workspace-alloc = { path = "./sysroot_src/library/rustc-std-workspace-alloc" }
+rustc-std-workspace-std = { path = "./sysroot_src/library/rustc-std-workspace-std" }
+
+[profile.release]
+debug = true
--- /dev/null
+#!/bin/bash
+
+# Requires the CHANNEL env var to be set to `debug` or `release.`
+
+set -e
+cd $(dirname "$0")
+
+pushd ../ >/dev/null
+source ./config.sh
+popd >/dev/null
+
+# Cleanup for previous run
+# v Clean target dir except for build scripts and incremental cache
+rm -r target/*/{debug,release}/{build,deps,examples,libsysroot*,native} 2>/dev/null || true
+rm Cargo.lock test_target/Cargo.lock 2>/dev/null || true
+rm -r sysroot/ 2>/dev/null || true
+
+# Build libs
+export RUSTFLAGS="$RUSTFLAGS -Z force-unstable-if-unmarked -Cpanic=abort"
+if [[ "$1" == "--release" ]]; then
+ sysroot_channel='release'
+ RUSTFLAGS="$RUSTFLAGS -Zmir-opt-level=3" cargo build --target $TARGET_TRIPLE --release
+else
+ sysroot_channel='debug'
+ cargo build --target $TARGET_TRIPLE
+fi
+
+# Copy files to sysroot
+mkdir -p sysroot/lib/rustlib/$TARGET_TRIPLE/lib/
+cp -r target/$TARGET_TRIPLE/$sysroot_channel/deps/* sysroot/lib/rustlib/$TARGET_TRIPLE/lib/
--- /dev/null
+#!/bin/bash
+set -e
+cd $(dirname "$0")
+
+SRC_DIR=$(dirname $(rustup which rustc))"/../lib/rustlib/src/rust/"
+DST_DIR="sysroot_src"
+
+if [ ! -e $SRC_DIR ]; then
+ echo "Please install rust-src component"
+ exit 1
+fi
+
+rm -rf $DST_DIR
+mkdir -p $DST_DIR/library
+cp -r $SRC_DIR/library $DST_DIR/
+
+pushd $DST_DIR
+echo "[GIT] init"
+git init
+echo "[GIT] add"
+git add .
+echo "[GIT] commit"
+
+# This is needed on systems where nothing is configured.
+# git really needs something here, or it will fail.
+# Even using --author is not enough.
+git config user.email || git config user.email "none@example.com"
+git config user.name || git config user.name "None"
+
+git commit -m "Initial commit" -q
+for file in $(ls ../../patches/ | grep -v patcha); do
+echo "[GIT] apply" $file
+git apply ../../patches/$file
+git add -A
+git commit --no-gpg-sign -m "Patch $file"
+done
+popd
+
+echo "Successfully prepared libcore for building"
--- /dev/null
+#![no_std]
--- /dev/null
+#!/bin/bash
+
+if [ -z $CHANNEL ]; then
+export CHANNEL='debug'
+fi
+
+pushd $(dirname "$0") >/dev/null
+source config.sh
+
+# read nightly compiler from rust-toolchain file
+TOOLCHAIN=$(cat rust-toolchain)
+
+popd >/dev/null
+
+if [[ $(rustc -V) != $(rustc +${TOOLCHAIN} -V) ]]; then
+ echo "rustc_codegen_gcc is build for $(rustc +${TOOLCHAIN} -V) but the default rustc version is $(rustc -V)."
+ echo "Using $(rustc +${TOOLCHAIN} -V)."
+fi
+
+cmd=$1
+shift
+
+RUSTDOCFLAGS="$RUSTFLAGS" cargo +${TOOLCHAIN} $cmd --target $TARGET_TRIPLE $@
--- /dev/null
+#!/bin/bash --verbose
+set -e
+
+rm -rf target/ build_sysroot/{sysroot/,sysroot_src/,target/,Cargo.lock} perf.data{,.old}
+rm -rf regex/ simple-raytracer/
--- /dev/null
+set -e
+
+export CARGO_INCREMENTAL=0
+
+if [ -f ./gcc_path ]; then
+ export GCC_PATH=$(cat gcc_path)
+else
+ echo 'Please put the path to your custom build of libgccjit in the file `gcc_path`, see Readme.md for details'
+ exit 1
+fi
+
+unamestr=`uname`
+if [[ "$unamestr" == 'Linux' ]]; then
+ dylib_ext='so'
+elif [[ "$unamestr" == 'Darwin' ]]; then
+ dylib_ext='dylib'
+else
+ echo "Unsupported os"
+ exit 1
+fi
+
+HOST_TRIPLE=$(rustc -vV | grep host | cut -d: -f2 | tr -d " ")
+TARGET_TRIPLE=$HOST_TRIPLE
+#TARGET_TRIPLE="m68k-unknown-linux-gnu"
+
+linker=''
+RUN_WRAPPER=''
+if [[ "$HOST_TRIPLE" != "$TARGET_TRIPLE" ]]; then
+ if [[ "$TARGET_TRIPLE" == "m68k-unknown-linux-gnu" ]]; then
+ TARGET_TRIPLE="mips-unknown-linux-gnu"
+ linker='-Clinker=m68k-linux-gcc'
+ elif [[ "$TARGET_TRIPLE" == "aarch64-unknown-linux-gnu" ]]; then
+ # We are cross-compiling for aarch64. Use the correct linker and run tests in qemu.
+ linker='-Clinker=aarch64-linux-gnu-gcc'
+ RUN_WRAPPER='qemu-aarch64 -L /usr/aarch64-linux-gnu'
+ else
+ echo "Unknown non-native platform"
+ fi
+fi
+
+export RUSTFLAGS="$linker -Cpanic=abort -Zsymbol-mangling-version=v0 -Cdebuginfo=2 -Clto=off -Zpanic-abort-tests -Zcodegen-backend=$(pwd)/target/${CHANNEL:-debug}/librustc_codegen_gcc.$dylib_ext --sysroot $(pwd)/build_sysroot/sysroot"
+
+# FIXME(antoyo): remove once the atomic shim is gone
+if [[ `uname` == 'Darwin' ]]; then
+ export RUSTFLAGS="$RUSTFLAGS -Clink-arg=-undefined -Clink-arg=dynamic_lookup"
+fi
+
+RUSTC="rustc $RUSTFLAGS -L crate=target/out --out-dir target/out"
+export RUSTC_LOG=warn # display metadata load errors
+
+export LD_LIBRARY_PATH="$(pwd)/target/out:$(pwd)/build_sysroot/sysroot/lib/rustlib/$TARGET_TRIPLE/lib:$GCC_PATH"
+export DYLD_LIBRARY_PATH=$LD_LIBRARY_PATH
--- /dev/null
+#![feature(start, box_syntax, core_intrinsics, alloc_prelude, alloc_error_handler)]
+#![no_std]
+
+extern crate alloc;
+extern crate alloc_system;
+
+use alloc::prelude::v1::*;
+
+use alloc_system::System;
+
+#[global_allocator]
+static ALLOC: System = System;
+
+#[link(name = "c")]
+extern "C" {
+ fn puts(s: *const u8) -> i32;
+}
+
+#[panic_handler]
+fn panic_handler(_: &core::panic::PanicInfo) -> ! {
+ unsafe {
+ core::intrinsics::abort();
+ }
+}
+
+#[alloc_error_handler]
+fn alloc_error_handler(_: alloc::alloc::Layout) -> ! {
+ unsafe {
+ core::intrinsics::abort();
+ }
+}
+
+#[start]
+fn main(_argc: isize, _argv: *const *const u8) -> isize {
+ let world: Box<&str> = box "Hello World!\0";
+ unsafe {
+ puts(*world as *const str as *const u8);
+ }
+
+ 0
+}
--- /dev/null
+// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+#![no_std]
+#![feature(allocator_api, rustc_private)]
+#![cfg_attr(any(unix, target_os = "redox"), feature(libc))]
+
+// The minimum alignment guaranteed by the architecture. This value is used to
+// add fast paths for low alignment values.
+#[cfg(all(any(target_arch = "x86",
+ target_arch = "arm",
+ target_arch = "mips",
+ target_arch = "powerpc",
+ target_arch = "powerpc64")))]
+const MIN_ALIGN: usize = 8;
+#[cfg(all(any(target_arch = "x86_64",
+ target_arch = "aarch64",
+ target_arch = "mips64",
+ target_arch = "s390x",
+ target_arch = "sparc64")))]
+const MIN_ALIGN: usize = 16;
+
+pub struct System;
+#[cfg(any(windows, unix, target_os = "redox"))]
+mod realloc_fallback {
+ use core::alloc::{GlobalAlloc, Layout};
+ use core::cmp;
+ use core::ptr;
+ impl super::System {
+ pub(crate) unsafe fn realloc_fallback(&self, ptr: *mut u8, old_layout: Layout,
+ new_size: usize) -> *mut u8 {
+ // Docs for GlobalAlloc::realloc require this to be valid:
+ let new_layout = Layout::from_size_align_unchecked(new_size, old_layout.align());
+ let new_ptr = GlobalAlloc::alloc(self, new_layout);
+ if !new_ptr.is_null() {
+ let size = cmp::min(old_layout.size(), new_size);
+ ptr::copy_nonoverlapping(ptr, new_ptr, size);
+ GlobalAlloc::dealloc(self, ptr, old_layout);
+ }
+ new_ptr
+ }
+ }
+}
+#[cfg(any(unix, target_os = "redox"))]
+mod platform {
+ extern crate libc;
+ use core::ptr;
+ use MIN_ALIGN;
+ use System;
+ use core::alloc::{GlobalAlloc, Layout};
+ unsafe impl GlobalAlloc for System {
+ #[inline]
+ unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
+ if layout.align() <= MIN_ALIGN && layout.align() <= layout.size() {
+ libc::malloc(layout.size()) as *mut u8
+ } else {
+ #[cfg(target_os = "macos")]
+ {
+ if layout.align() > (1 << 31) {
+ return ptr::null_mut()
+ }
+ }
+ aligned_malloc(&layout)
+ }
+ }
+ #[inline]
+ unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut u8 {
+ if layout.align() <= MIN_ALIGN && layout.align() <= layout.size() {
+ libc::calloc(layout.size(), 1) as *mut u8
+ } else {
+ let ptr = self.alloc(layout.clone());
+ if !ptr.is_null() {
+ ptr::write_bytes(ptr, 0, layout.size());
+ }
+ ptr
+ }
+ }
+ #[inline]
+ unsafe fn dealloc(&self, ptr: *mut u8, _layout: Layout) {
+ libc::free(ptr as *mut libc::c_void)
+ }
+ #[inline]
+ unsafe fn realloc(&self, ptr: *mut u8, layout: Layout, new_size: usize) -> *mut u8 {
+ if layout.align() <= MIN_ALIGN && layout.align() <= new_size {
+ libc::realloc(ptr as *mut libc::c_void, new_size) as *mut u8
+ } else {
+ self.realloc_fallback(ptr, layout, new_size)
+ }
+ }
+ }
+ #[cfg(any(target_os = "android",
+ target_os = "hermit",
+ target_os = "redox",
+ target_os = "solaris"))]
+ #[inline]
+ unsafe fn aligned_malloc(layout: &Layout) -> *mut u8 {
+ // On android we currently target API level 9 which unfortunately
+ // doesn't have the `posix_memalign` API used below. Instead we use
+ // `memalign`, but this unfortunately has the property on some systems
+ // where the memory returned cannot be deallocated by `free`!
+ //
+ // Upon closer inspection, however, this appears to work just fine with
+ // Android, so for this platform we should be fine to call `memalign`
+ // (which is present in API level 9). Some helpful references could
+ // possibly be chromium using memalign [1], attempts at documenting that
+ // memalign + free is ok [2] [3], or the current source of chromium
+ // which still uses memalign on android [4].
+ //
+ // [1]: https://codereview.chromium.org/10796020/
+ // [2]: https://code.google.com/p/android/issues/detail?id=35391
+ // [3]: https://bugs.chromium.org/p/chromium/issues/detail?id=138579
+ // [4]: https://chromium.googlesource.com/chromium/src/base/+/master/
+ // /memory/aligned_memory.cc
+ libc::memalign(layout.align(), layout.size()) as *mut u8
+ }
+ #[cfg(not(any(target_os = "android",
+ target_os = "hermit",
+ target_os = "redox",
+ target_os = "solaris")))]
+ #[inline]
+ unsafe fn aligned_malloc(layout: &Layout) -> *mut u8 {
+ let mut out = ptr::null_mut();
+ let ret = libc::posix_memalign(&mut out, layout.align(), layout.size());
+ if ret != 0 {
+ ptr::null_mut()
+ } else {
+ out as *mut u8
+ }
+ }
+}
+#[cfg(windows)]
+#[allow(nonstandard_style)]
+mod platform {
+ use MIN_ALIGN;
+ use System;
+ use core::alloc::{GlobalAlloc, Layout};
+ type LPVOID = *mut u8;
+ type HANDLE = LPVOID;
+ type SIZE_T = usize;
+ type DWORD = u32;
+ type BOOL = i32;
+ extern "system" {
+ fn GetProcessHeap() -> HANDLE;
+ fn HeapAlloc(hHeap: HANDLE, dwFlags: DWORD, dwBytes: SIZE_T) -> LPVOID;
+ fn HeapReAlloc(hHeap: HANDLE, dwFlags: DWORD, lpMem: LPVOID, dwBytes: SIZE_T) -> LPVOID;
+ fn HeapFree(hHeap: HANDLE, dwFlags: DWORD, lpMem: LPVOID) -> BOOL;
+ fn GetLastError() -> DWORD;
+ }
+ #[repr(C)]
+ struct Header(*mut u8);
+ const HEAP_ZERO_MEMORY: DWORD = 0x00000008;
+ unsafe fn get_header<'a>(ptr: *mut u8) -> &'a mut Header {
+ &mut *(ptr as *mut Header).offset(-1)
+ }
+ unsafe fn align_ptr(ptr: *mut u8, align: usize) -> *mut u8 {
+ let aligned = ptr.add(align - (ptr as usize & (align - 1)));
+ *get_header(aligned) = Header(ptr);
+ aligned
+ }
+ #[inline]
+ unsafe fn allocate_with_flags(layout: Layout, flags: DWORD) -> *mut u8 {
+ let ptr = if layout.align() <= MIN_ALIGN {
+ HeapAlloc(GetProcessHeap(), flags, layout.size())
+ } else {
+ let size = layout.size() + layout.align();
+ let ptr = HeapAlloc(GetProcessHeap(), flags, size);
+ if ptr.is_null() {
+ ptr
+ } else {
+ align_ptr(ptr, layout.align())
+ }
+ };
+ ptr as *mut u8
+ }
+ unsafe impl GlobalAlloc for System {
+ #[inline]
+ unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
+ allocate_with_flags(layout, 0)
+ }
+ #[inline]
+ unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut u8 {
+ allocate_with_flags(layout, HEAP_ZERO_MEMORY)
+ }
+ #[inline]
+ unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) {
+ if layout.align() <= MIN_ALIGN {
+ let err = HeapFree(GetProcessHeap(), 0, ptr as LPVOID);
+ debug_assert!(err != 0, "Failed to free heap memory: {}",
+ GetLastError());
+ } else {
+ let header = get_header(ptr);
+ let err = HeapFree(GetProcessHeap(), 0, header.0 as LPVOID);
+ debug_assert!(err != 0, "Failed to free heap memory: {}",
+ GetLastError());
+ }
+ }
+ #[inline]
+ unsafe fn realloc(&self, ptr: *mut u8, layout: Layout, new_size: usize) -> *mut u8 {
+ if layout.align() <= MIN_ALIGN {
+ HeapReAlloc(GetProcessHeap(), 0, ptr as LPVOID, new_size) as *mut u8
+ } else {
+ self.realloc_fallback(ptr, layout, new_size)
+ }
+ }
+ }
+}
--- /dev/null
+// Adapted from rustc run-pass test suite
+
+#![feature(arbitrary_self_types, unsize, coerce_unsized, dispatch_from_dyn)]
+#![feature(rustc_attrs)]
+
+use std::{
+ ops::{Deref, CoerceUnsized, DispatchFromDyn},
+ marker::Unsize,
+};
+
+struct Ptr<T: ?Sized>(Box<T>);
+
+impl<T: ?Sized> Deref for Ptr<T> {
+ type Target = T;
+
+ fn deref(&self) -> &T {
+ &*self.0
+ }
+}
+
+impl<T: Unsize<U> + ?Sized, U: ?Sized> CoerceUnsized<Ptr<U>> for Ptr<T> {}
+impl<T: Unsize<U> + ?Sized, U: ?Sized> DispatchFromDyn<Ptr<U>> for Ptr<T> {}
+
+struct Wrapper<T: ?Sized>(T);
+
+impl<T: ?Sized> Deref for Wrapper<T> {
+ type Target = T;
+
+ fn deref(&self) -> &T {
+ &self.0
+ }
+}
+
+impl<T: CoerceUnsized<U>, U> CoerceUnsized<Wrapper<U>> for Wrapper<T> {}
+impl<T: DispatchFromDyn<U>, U> DispatchFromDyn<Wrapper<U>> for Wrapper<T> {}
+
+
+trait Trait {
+ // This method isn't object-safe yet. Unsized by-value `self` is object-safe (but not callable
+ // without unsized_locals), but wrappers arond `Self` currently are not.
+ // FIXME (mikeyhew) uncomment this when unsized rvalues object-safety is implemented
+ // fn wrapper(self: Wrapper<Self>) -> i32;
+ fn ptr_wrapper(self: Ptr<Wrapper<Self>>) -> i32;
+ fn wrapper_ptr(self: Wrapper<Ptr<Self>>) -> i32;
+ fn wrapper_ptr_wrapper(self: Wrapper<Ptr<Wrapper<Self>>>) -> i32;
+}
+
+impl Trait for i32 {
+ fn ptr_wrapper(self: Ptr<Wrapper<Self>>) -> i32 {
+ **self
+ }
+ fn wrapper_ptr(self: Wrapper<Ptr<Self>>) -> i32 {
+ **self
+ }
+ fn wrapper_ptr_wrapper(self: Wrapper<Ptr<Wrapper<Self>>>) -> i32 {
+ ***self
+ }
+}
+
+fn main() {
+ let pw = Ptr(Box::new(Wrapper(5))) as Ptr<Wrapper<dyn Trait>>;
+ assert_eq!(pw.ptr_wrapper(), 5);
+
+ let wp = Wrapper(Ptr(Box::new(6))) as Wrapper<Ptr<dyn Trait>>;
+ assert_eq!(wp.wrapper_ptr(), 6);
+
+ let wpw = Wrapper(Ptr(Box::new(Wrapper(7)))) as Wrapper<Ptr<Wrapper<dyn Trait>>>;
+ assert_eq!(wpw.wrapper_ptr_wrapper(), 7);
+}
--- /dev/null
+// run-pass
+#![allow(dead_code)]
+struct Foo<T: ?Sized> {
+ a: u16,
+ b: T
+}
+
+trait Bar {
+ fn get(&self) -> usize;
+}
+
+impl Bar for usize {
+ fn get(&self) -> usize { *self }
+}
+
+struct Baz<T: ?Sized> {
+ a: T
+}
+
+struct HasDrop<T: ?Sized> {
+ ptr: Box<usize>,
+ data: T
+}
+
+fn main() {
+ // Test that zero-offset works properly
+ let b : Baz<usize> = Baz { a: 7 };
+ assert_eq!(b.a.get(), 7);
+ let b : &Baz<dyn Bar> = &b;
+ assert_eq!(b.a.get(), 7);
+
+ // Test that the field is aligned properly
+ let f : Foo<usize> = Foo { a: 0, b: 11 };
+ assert_eq!(f.b.get(), 11);
+ let ptr1 : *const u8 = &f.b as *const _ as *const u8;
+
+ let f : &Foo<dyn Bar> = &f;
+ let ptr2 : *const u8 = &f.b as *const _ as *const u8;
+ assert_eq!(f.b.get(), 11);
+
+ // The pointers should be the same
+ assert_eq!(ptr1, ptr2);
+
+ // Test that nested DSTs work properly
+ let f : Foo<Foo<usize>> = Foo { a: 0, b: Foo { a: 1, b: 17 }};
+ assert_eq!(f.b.b.get(), 17);
+ let f : &Foo<Foo<dyn Bar>> = &f;
+ assert_eq!(f.b.b.get(), 17);
+
+ // Test that get the pointer via destructuring works
+
+ let f : Foo<usize> = Foo { a: 0, b: 11 };
+ let f : &Foo<dyn Bar> = &f;
+ let &Foo { a: _, b: ref bar } = f;
+ assert_eq!(bar.get(), 11);
+
+ // Make sure that drop flags don't screw things up
+
+ let d : HasDrop<Baz<[i32; 4]>> = HasDrop {
+ ptr: Box::new(0),
+ data: Baz { a: [1,2,3,4] }
+ };
+ assert_eq!([1,2,3,4], d.data.a);
+
+ let d : &HasDrop<Baz<[i32]>> = &d;
+ assert_eq!(&[1,2,3,4], &d.data.a);
+}
--- /dev/null
+#![feature(no_core, unboxed_closures)]
+#![no_core]
+#![allow(dead_code)]
+
+extern crate mini_core;
+
+use mini_core::*;
+
+fn abc(a: u8) -> u8 {
+ a * 2
+}
+
+fn bcd(b: bool, a: u8) -> u8 {
+ if b {
+ a * 2
+ } else {
+ a * 3
+ }
+}
+
+fn call() {
+ abc(42);
+}
+
+fn indirect_call() {
+ let f: fn() = call;
+ f();
+}
+
+enum BoolOption {
+ Some(bool),
+ None,
+}
+
+fn option_unwrap_or(o: BoolOption, d: bool) -> bool {
+ match o {
+ BoolOption::Some(b) => b,
+ BoolOption::None => d,
+ }
+}
+
+fn ret_42() -> u8 {
+ 42
+}
+
+fn return_str() -> &'static str {
+ "hello world"
+}
+
+fn promoted_val() -> &'static u8 {
+ &(1 * 2)
+}
+
+fn cast_ref_to_raw_ptr(abc: &u8) -> *const u8 {
+ abc as *const u8
+}
+
+fn cmp_raw_ptr(a: *const u8, b: *const u8) -> bool {
+ a == b
+}
+
+fn int_cast(a: u16, b: i16) -> (u8, u16, u32, usize, i8, i16, i32, isize, u8, u32) {
+ (
+ a as u8, a as u16, a as u32, a as usize, a as i8, a as i16, a as i32, a as isize, b as u8,
+ b as u32,
+ )
+}
+
+fn char_cast(c: char) -> u8 {
+ c as u8
+}
+
+pub struct DebugTuple(());
+
+fn debug_tuple() -> DebugTuple {
+ DebugTuple(())
+}
+
+fn size_of<T>() -> usize {
+ intrinsics::size_of::<T>()
+}
+
+fn use_size_of() -> usize {
+ size_of::<u64>()
+}
+
+unsafe fn use_copy_intrinsic(src: *const u8, dst: *mut u8) {
+ intrinsics::copy::<u8>(src, dst, 1);
+}
+
+unsafe fn use_copy_intrinsic_ref(src: *const u8, dst: *mut u8) {
+ let copy2 = &intrinsics::copy::<u8>;
+ copy2(src, dst, 1);
+}
+
+const ABC: u8 = 6 * 7;
+
+fn use_const() -> u8 {
+ ABC
+}
+
+pub fn call_closure_3arg() {
+ (|_, _, _| {})(0u8, 42u16, 0u8)
+}
+
+pub fn call_closure_2arg() {
+ (|_, _| {})(0u8, 42u16)
+}
+
+struct IsNotEmpty;
+
+impl<'a, 'b> FnOnce<(&'a &'b [u16],)> for IsNotEmpty {
+ type Output = (u8, u8);
+
+ #[inline]
+ extern "rust-call" fn call_once(mut self, arg: (&'a &'b [u16],)) -> (u8, u8) {
+ self.call_mut(arg)
+ }
+}
+
+impl<'a, 'b> FnMut<(&'a &'b [u16],)> for IsNotEmpty {
+ #[inline]
+ extern "rust-call" fn call_mut(&mut self, _arg: (&'a &'b [u16],)) -> (u8, u8) {
+ (0, 42)
+ }
+}
+
+pub fn call_is_not_empty() {
+ IsNotEmpty.call_once((&(&[0u16] as &[_]),));
+}
+
+fn eq_char(a: char, b: char) -> bool {
+ a == b
+}
+
+unsafe fn transmute(c: char) -> u32 {
+ intrinsics::transmute(c)
+}
+
+unsafe fn deref_str_ptr(s: *const str) -> &'static str {
+ &*s
+}
+
+fn use_array(arr: [u8; 3]) -> u8 {
+ arr[1]
+}
+
+fn repeat_array() -> [u8; 3] {
+ [0; 3]
+}
+
+fn array_as_slice(arr: &[u8; 3]) -> &[u8] {
+ arr
+}
+
+unsafe fn use_ctlz_nonzero(a: u16) -> u16 {
+ intrinsics::ctlz_nonzero(a)
+}
+
+fn ptr_as_usize(ptr: *const u8) -> usize {
+ ptr as usize
+}
+
+fn float_cast(a: f32, b: f64) -> (f64, f32) {
+ (a as f64, b as f32)
+}
+
+fn int_to_float(a: u8, b: i32) -> (f64, f32) {
+ (a as f64, b as f32)
+}
+
+fn make_array() -> [u8; 3] {
+ [42, 0, 5]
+}
+
+fn some_promoted_tuple() -> &'static (&'static str, &'static str) {
+ &("abc", "some")
+}
+
+fn index_slice(s: &[u8]) -> u8 {
+ s[2]
+}
+
+pub struct StrWrapper {
+ s: str,
+}
+
+fn str_wrapper_get(w: &StrWrapper) -> &str {
+ &w.s
+}
+
+fn i16_as_i8(a: i16) -> i8 {
+ a as i8
+}
+
+struct Unsized(u8, str);
+
+fn get_sized_field_ref_from_unsized_type(u: &Unsized) -> &u8 {
+ &u.0
+}
+
+fn get_unsized_field_ref_from_unsized_type(u: &Unsized) -> &str {
+ &u.1
+}
+
+pub fn reuse_byref_argument_storage(a: (u8, u16, u32)) -> u8 {
+ a.0
+}
--- /dev/null
+#![feature(
+ no_core, lang_items, intrinsics, unboxed_closures, type_ascription, extern_types,
+ untagged_unions, decl_macro, rustc_attrs, transparent_unions, auto_traits,
+ thread_local
+)]
+#![no_core]
+#![allow(dead_code)]
+
+#[no_mangle]
+unsafe extern "C" fn _Unwind_Resume() {
+ intrinsics::unreachable();
+}
+
+#[lang = "sized"]
+pub trait Sized {}
+
+#[lang = "unsize"]
+pub trait Unsize<T: ?Sized> {}
+
+#[lang = "coerce_unsized"]
+pub trait CoerceUnsized<T> {}
+
+impl<'a, 'b: 'a, T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<&'a U> for &'b T {}
+impl<'a, T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<&'a mut U> for &'a mut T {}
+impl<T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<*const U> for *const T {}
+impl<T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<*mut U> for *mut T {}
+
+#[lang = "dispatch_from_dyn"]
+pub trait DispatchFromDyn<T> {}
+
+// &T -> &U
+impl<'a, T: ?Sized+Unsize<U>, U: ?Sized> DispatchFromDyn<&'a U> for &'a T {}
+// &mut T -> &mut U
+impl<'a, T: ?Sized+Unsize<U>, U: ?Sized> DispatchFromDyn<&'a mut U> for &'a mut T {}
+// *const T -> *const U
+impl<T: ?Sized+Unsize<U>, U: ?Sized> DispatchFromDyn<*const U> for *const T {}
+// *mut T -> *mut U
+impl<T: ?Sized+Unsize<U>, U: ?Sized> DispatchFromDyn<*mut U> for *mut T {}
+impl<T: ?Sized + Unsize<U>, U: ?Sized> DispatchFromDyn<Box<U>> for Box<T> {}
+
+#[lang = "receiver"]
+pub trait Receiver {}
+
+impl<T: ?Sized> Receiver for &T {}
+impl<T: ?Sized> Receiver for &mut T {}
+impl<T: ?Sized> Receiver for Box<T> {}
+
+#[lang = "copy"]
+pub unsafe trait Copy {}
+
+unsafe impl Copy for bool {}
+unsafe impl Copy for u8 {}
+unsafe impl Copy for u16 {}
+unsafe impl Copy for u32 {}
+unsafe impl Copy for u64 {}
+unsafe impl Copy for usize {}
+unsafe impl Copy for i8 {}
+unsafe impl Copy for i16 {}
+unsafe impl Copy for i32 {}
+unsafe impl Copy for isize {}
+unsafe impl Copy for f32 {}
+unsafe impl Copy for char {}
+unsafe impl<'a, T: ?Sized> Copy for &'a T {}
+unsafe impl<T: ?Sized> Copy for *const T {}
+unsafe impl<T: ?Sized> Copy for *mut T {}
+
+#[lang = "sync"]
+pub unsafe trait Sync {}
+
+unsafe impl Sync for bool {}
+unsafe impl Sync for u8 {}
+unsafe impl Sync for u16 {}
+unsafe impl Sync for u32 {}
+unsafe impl Sync for u64 {}
+unsafe impl Sync for usize {}
+unsafe impl Sync for i8 {}
+unsafe impl Sync for i16 {}
+unsafe impl Sync for i32 {}
+unsafe impl Sync for isize {}
+unsafe impl Sync for char {}
+unsafe impl<'a, T: ?Sized> Sync for &'a T {}
+unsafe impl Sync for [u8; 16] {}
+
+#[lang = "freeze"]
+unsafe auto trait Freeze {}
+
+unsafe impl<T: ?Sized> Freeze for PhantomData<T> {}
+unsafe impl<T: ?Sized> Freeze for *const T {}
+unsafe impl<T: ?Sized> Freeze for *mut T {}
+unsafe impl<T: ?Sized> Freeze for &T {}
+unsafe impl<T: ?Sized> Freeze for &mut T {}
+
+#[lang = "structural_peq"]
+pub trait StructuralPartialEq {}
+
+#[lang = "structural_teq"]
+pub trait StructuralEq {}
+
+#[lang = "not"]
+pub trait Not {
+ type Output;
+
+ fn not(self) -> Self::Output;
+}
+
+impl Not for bool {
+ type Output = bool;
+
+ fn not(self) -> bool {
+ !self
+ }
+}
+
+#[lang = "mul"]
+pub trait Mul<RHS = Self> {
+ type Output;
+
+ #[must_use]
+ fn mul(self, rhs: RHS) -> Self::Output;
+}
+
+impl Mul for u8 {
+ type Output = Self;
+
+ fn mul(self, rhs: Self) -> Self::Output {
+ self * rhs
+ }
+}
+
+impl Mul for usize {
+ type Output = Self;
+
+ fn mul(self, rhs: Self) -> Self::Output {
+ self * rhs
+ }
+}
+
+#[lang = "add"]
+pub trait Add<RHS = Self> {
+ type Output;
+
+ fn add(self, rhs: RHS) -> Self::Output;
+}
+
+impl Add for u8 {
+ type Output = Self;
+
+ fn add(self, rhs: Self) -> Self {
+ self + rhs
+ }
+}
+
+impl Add for i8 {
+ type Output = Self;
+
+ fn add(self, rhs: Self) -> Self {
+ self + rhs
+ }
+}
+
+impl Add for usize {
+ type Output = Self;
+
+ fn add(self, rhs: Self) -> Self {
+ self + rhs
+ }
+}
+
+#[lang = "sub"]
+pub trait Sub<RHS = Self> {
+ type Output;
+
+ fn sub(self, rhs: RHS) -> Self::Output;
+}
+
+impl Sub for usize {
+ type Output = Self;
+
+ fn sub(self, rhs: Self) -> Self {
+ self - rhs
+ }
+}
+
+impl Sub for u8 {
+ type Output = Self;
+
+ fn sub(self, rhs: Self) -> Self {
+ self - rhs
+ }
+}
+
+impl Sub for i8 {
+ type Output = Self;
+
+ fn sub(self, rhs: Self) -> Self {
+ self - rhs
+ }
+}
+
+impl Sub for i16 {
+ type Output = Self;
+
+ fn sub(self, rhs: Self) -> Self {
+ self - rhs
+ }
+}
+
+#[lang = "rem"]
+pub trait Rem<RHS = Self> {
+ type Output;
+
+ fn rem(self, rhs: RHS) -> Self::Output;
+}
+
+impl Rem for usize {
+ type Output = Self;
+
+ fn rem(self, rhs: Self) -> Self {
+ self % rhs
+ }
+}
+
+#[lang = "bitor"]
+pub trait BitOr<RHS = Self> {
+ type Output;
+
+ #[must_use]
+ fn bitor(self, rhs: RHS) -> Self::Output;
+}
+
+impl BitOr for bool {
+ type Output = bool;
+
+ fn bitor(self, rhs: bool) -> bool {
+ self | rhs
+ }
+}
+
+impl<'a> BitOr<bool> for &'a bool {
+ type Output = bool;
+
+ fn bitor(self, rhs: bool) -> bool {
+ *self | rhs
+ }
+}
+
+#[lang = "eq"]
+pub trait PartialEq<Rhs: ?Sized = Self> {
+ fn eq(&self, other: &Rhs) -> bool;
+ fn ne(&self, other: &Rhs) -> bool;
+}
+
+impl PartialEq for u8 {
+ fn eq(&self, other: &u8) -> bool {
+ (*self) == (*other)
+ }
+ fn ne(&self, other: &u8) -> bool {
+ (*self) != (*other)
+ }
+}
+
+impl PartialEq for u16 {
+ fn eq(&self, other: &u16) -> bool {
+ (*self) == (*other)
+ }
+ fn ne(&self, other: &u16) -> bool {
+ (*self) != (*other)
+ }
+}
+
+impl PartialEq for u32 {
+ fn eq(&self, other: &u32) -> bool {
+ (*self) == (*other)
+ }
+ fn ne(&self, other: &u32) -> bool {
+ (*self) != (*other)
+ }
+}
+
+
+impl PartialEq for u64 {
+ fn eq(&self, other: &u64) -> bool {
+ (*self) == (*other)
+ }
+ fn ne(&self, other: &u64) -> bool {
+ (*self) != (*other)
+ }
+}
+
+impl PartialEq for usize {
+ fn eq(&self, other: &usize) -> bool {
+ (*self) == (*other)
+ }
+ fn ne(&self, other: &usize) -> bool {
+ (*self) != (*other)
+ }
+}
+
+impl PartialEq for i8 {
+ fn eq(&self, other: &i8) -> bool {
+ (*self) == (*other)
+ }
+ fn ne(&self, other: &i8) -> bool {
+ (*self) != (*other)
+ }
+}
+
+impl PartialEq for i32 {
+ fn eq(&self, other: &i32) -> bool {
+ (*self) == (*other)
+ }
+ fn ne(&self, other: &i32) -> bool {
+ (*self) != (*other)
+ }
+}
+
+impl PartialEq for isize {
+ fn eq(&self, other: &isize) -> bool {
+ (*self) == (*other)
+ }
+ fn ne(&self, other: &isize) -> bool {
+ (*self) != (*other)
+ }
+}
+
+impl PartialEq for char {
+ fn eq(&self, other: &char) -> bool {
+ (*self) == (*other)
+ }
+ fn ne(&self, other: &char) -> bool {
+ (*self) != (*other)
+ }
+}
+
+impl<T: ?Sized> PartialEq for *const T {
+ fn eq(&self, other: &*const T) -> bool {
+ *self == *other
+ }
+ fn ne(&self, other: &*const T) -> bool {
+ *self != *other
+ }
+}
+
+#[lang = "neg"]
+pub trait Neg {
+ type Output;
+
+ fn neg(self) -> Self::Output;
+}
+
+impl Neg for i8 {
+ type Output = i8;
+
+ fn neg(self) -> i8 {
+ -self
+ }
+}
+
+impl Neg for i16 {
+ type Output = i16;
+
+ fn neg(self) -> i16 {
+ self
+ }
+}
+
+impl Neg for isize {
+ type Output = isize;
+
+ fn neg(self) -> isize {
+ -self
+ }
+}
+
+impl Neg for f32 {
+ type Output = f32;
+
+ fn neg(self) -> f32 {
+ -self
+ }
+}
+
+pub enum Option<T> {
+ Some(T),
+ None,
+}
+
+pub use Option::*;
+
+#[lang = "phantom_data"]
+pub struct PhantomData<T: ?Sized>;
+
+#[lang = "fn_once"]
+#[rustc_paren_sugar]
+pub trait FnOnce<Args> {
+ #[lang = "fn_once_output"]
+ type Output;
+
+ extern "rust-call" fn call_once(self, args: Args) -> Self::Output;
+}
+
+#[lang = "fn_mut"]
+#[rustc_paren_sugar]
+pub trait FnMut<Args>: FnOnce<Args> {
+ extern "rust-call" fn call_mut(&mut self, args: Args) -> Self::Output;
+}
+
+#[lang = "panic"]
+#[track_caller]
+pub fn panic(_msg: &str) -> ! {
+ unsafe {
+ libc::puts("Panicking\n\0" as *const str as *const u8);
+ intrinsics::abort();
+ }
+}
+
+#[lang = "panic_bounds_check"]
+#[track_caller]
+fn panic_bounds_check(index: usize, len: usize) -> ! {
+ unsafe {
+ libc::printf("index out of bounds: the len is %d but the index is %d\n\0" as *const str as *const i8, len, index);
+ intrinsics::abort();
+ }
+}
+
+#[lang = "eh_personality"]
+fn eh_personality() -> ! {
+ loop {}
+}
+
+#[lang = "drop_in_place"]
+#[allow(unconditional_recursion)]
+pub unsafe fn drop_in_place<T: ?Sized>(to_drop: *mut T) {
+ // Code here does not matter - this is replaced by the
+ // real drop glue by the compiler.
+ drop_in_place(to_drop);
+}
+
+#[lang = "deref"]
+pub trait Deref {
+ type Target: ?Sized;
+
+ fn deref(&self) -> &Self::Target;
+}
+
+#[lang = "owned_box"]
+pub struct Box<T: ?Sized>(*mut T);
+
+impl<T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<Box<U>> for Box<T> {}
+
+impl<T: ?Sized> Drop for Box<T> {
+ fn drop(&mut self) {
+ // drop is currently performed by compiler.
+ }
+}
+
+impl<T> Deref for Box<T> {
+ type Target = T;
+
+ fn deref(&self) -> &Self::Target {
+ &**self
+ }
+}
+
+#[lang = "exchange_malloc"]
+unsafe fn allocate(size: usize, _align: usize) -> *mut u8 {
+ libc::malloc(size)
+}
+
+#[lang = "box_free"]
+unsafe fn box_free<T: ?Sized>(ptr: *mut T) {
+ libc::free(ptr as *mut u8);
+}
+
+#[lang = "drop"]
+pub trait Drop {
+ fn drop(&mut self);
+}
+
+#[lang = "manually_drop"]
+#[repr(transparent)]
+pub struct ManuallyDrop<T: ?Sized> {
+ pub value: T,
+}
+
+#[lang = "maybe_uninit"]
+#[repr(transparent)]
+pub union MaybeUninit<T> {
+ pub uninit: (),
+ pub value: ManuallyDrop<T>,
+}
+
+pub mod intrinsics {
+ extern "rust-intrinsic" {
+ pub fn abort() -> !;
+ pub fn size_of<T>() -> usize;
+ pub fn size_of_val<T: ?::Sized>(val: *const T) -> usize;
+ pub fn min_align_of<T>() -> usize;
+ pub fn min_align_of_val<T: ?::Sized>(val: *const T) -> usize;
+ pub fn copy<T>(src: *const T, dst: *mut T, count: usize);
+ pub fn transmute<T, U>(e: T) -> U;
+ pub fn ctlz_nonzero<T>(x: T) -> T;
+ pub fn needs_drop<T>() -> bool;
+ pub fn bitreverse<T>(x: T) -> T;
+ pub fn bswap<T>(x: T) -> T;
+ pub fn write_bytes<T>(dst: *mut T, val: u8, count: usize);
+ pub fn unreachable() -> !;
+ }
+}
+
+pub mod libc {
+ #[link(name = "c")]
+ extern "C" {
+ pub fn puts(s: *const u8) -> i32;
+ pub fn printf(format: *const i8, ...) -> i32;
+ pub fn malloc(size: usize) -> *mut u8;
+ pub fn free(ptr: *mut u8);
+ pub fn memcpy(dst: *mut u8, src: *const u8, size: usize);
+ pub fn memmove(dst: *mut u8, src: *const u8, size: usize);
+ pub fn strncpy(dst: *mut u8, src: *const u8, size: usize);
+ }
+}
+
+#[lang = "index"]
+pub trait Index<Idx: ?Sized> {
+ type Output: ?Sized;
+ fn index(&self, index: Idx) -> &Self::Output;
+}
+
+impl<T> Index<usize> for [T; 3] {
+ type Output = T;
+
+ fn index(&self, index: usize) -> &Self::Output {
+ &self[index]
+ }
+}
+
+impl<T> Index<usize> for [T] {
+ type Output = T;
+
+ fn index(&self, index: usize) -> &Self::Output {
+ &self[index]
+ }
+}
+
+extern {
+ type VaListImpl;
+}
+
+#[lang = "va_list"]
+#[repr(transparent)]
+pub struct VaList<'a>(&'a mut VaListImpl);
+
+#[rustc_builtin_macro]
+#[rustc_macro_transparency = "semitransparent"]
+pub macro stringify($($t:tt)*) { /* compiler built-in */ }
+
+#[rustc_builtin_macro]
+#[rustc_macro_transparency = "semitransparent"]
+pub macro file() { /* compiler built-in */ }
+
+#[rustc_builtin_macro]
+#[rustc_macro_transparency = "semitransparent"]
+pub macro line() { /* compiler built-in */ }
+
+#[rustc_builtin_macro]
+#[rustc_macro_transparency = "semitransparent"]
+pub macro cfg() { /* compiler built-in */ }
+
+pub static A_STATIC: u8 = 42;
+
+#[lang = "panic_location"]
+struct PanicLocation {
+ file: &'static str,
+ line: u32,
+ column: u32,
+}
+
+#[no_mangle]
+pub fn get_tls() -> u8 {
+ #[thread_local]
+ static A: u8 = 42;
+
+ A
+}
--- /dev/null
+// Adapted from https://github.com/sunfishcode/mir2cranelift/blob/master/rust-examples/nocore-hello-world.rs
+
+#![feature(
+ no_core, unboxed_closures, start, lang_items, box_syntax, never_type, linkage,
+ extern_types, thread_local
+)]
+#![no_core]
+#![allow(dead_code, non_camel_case_types)]
+
+extern crate mini_core;
+
+use mini_core::*;
+use mini_core::libc::*;
+
+unsafe extern "C" fn my_puts(s: *const u8) {
+ puts(s);
+}
+
+#[lang = "termination"]
+trait Termination {
+ fn report(self) -> i32;
+}
+
+impl Termination for () {
+ fn report(self) -> i32 {
+ unsafe {
+ NUM = 6 * 7 + 1 + (1u8 == 1u8) as u8; // 44
+ *NUM_REF as i32
+ }
+ }
+}
+
+trait SomeTrait {
+ fn object_safe(&self);
+}
+
+impl SomeTrait for &'static str {
+ fn object_safe(&self) {
+ unsafe {
+ puts(*self as *const str as *const u8);
+ }
+ }
+}
+
+struct NoisyDrop {
+ text: &'static str,
+ inner: NoisyDropInner,
+}
+
+struct NoisyDropInner;
+
+impl Drop for NoisyDrop {
+ fn drop(&mut self) {
+ unsafe {
+ puts(self.text as *const str as *const u8);
+ }
+ }
+}
+
+impl Drop for NoisyDropInner {
+ fn drop(&mut self) {
+ unsafe {
+ puts("Inner got dropped!\0" as *const str as *const u8);
+ }
+ }
+}
+
+impl SomeTrait for NoisyDrop {
+ fn object_safe(&self) {}
+}
+
+enum Ordering {
+ Less = -1,
+ Equal = 0,
+ Greater = 1,
+}
+
+#[lang = "start"]
+fn start<T: Termination + 'static>(
+ main: fn() -> T,
+ argc: isize,
+ argv: *const *const u8,
+) -> isize {
+ if argc == 3 {
+ unsafe { puts(*argv); }
+ unsafe { puts(*((argv as usize + intrinsics::size_of::<*const u8>()) as *const *const u8)); }
+ unsafe { puts(*((argv as usize + 2 * intrinsics::size_of::<*const u8>()) as *const *const u8)); }
+ }
+
+ main().report();
+ 0
+}
+
+static mut NUM: u8 = 6 * 7;
+static NUM_REF: &'static u8 = unsafe { &NUM };
+
+macro_rules! assert {
+ ($e:expr) => {
+ if !$e {
+ panic(stringify!(! $e));
+ }
+ };
+}
+
+macro_rules! assert_eq {
+ ($l:expr, $r: expr) => {
+ if $l != $r {
+ panic(stringify!($l != $r));
+ }
+ }
+}
+
+struct Unique<T: ?Sized> {
+ pointer: *const T,
+ _marker: PhantomData<T>,
+}
+
+impl<T: ?Sized, U: ?Sized> CoerceUnsized<Unique<U>> for Unique<T> where T: Unsize<U> {}
+
+unsafe fn zeroed<T>() -> T {
+ let mut uninit = MaybeUninit { uninit: () };
+ intrinsics::write_bytes(&mut uninit.value.value as *mut T, 0, 1);
+ uninit.value.value
+}
+
+fn take_f32(_f: f32) {}
+fn take_unique(_u: Unique<()>) {}
+
+fn return_u128_pair() -> (u128, u128) {
+ (0, 0)
+}
+
+fn call_return_u128_pair() {
+ return_u128_pair();
+}
+
+fn main() {
+ take_unique(Unique {
+ pointer: 0 as *const (),
+ _marker: PhantomData,
+ });
+ take_f32(0.1);
+
+ //call_return_u128_pair();
+
+ let slice = &[0, 1] as &[i32];
+ let slice_ptr = slice as *const [i32] as *const i32;
+
+ assert_eq!(slice_ptr as usize % 4, 0);
+
+ //return;
+
+ unsafe {
+ printf("Hello %s\n\0" as *const str as *const i8, "printf\0" as *const str as *const i8);
+
+ let hello: &[u8] = b"Hello\0" as &[u8; 6];
+ let ptr: *const u8 = hello as *const [u8] as *const u8;
+ puts(ptr);
+
+ let world: Box<&str> = box "World!\0";
+ puts(*world as *const str as *const u8);
+ world as Box<dyn SomeTrait>;
+
+ assert_eq!(intrinsics::bitreverse(0b10101000u8), 0b00010101u8);
+
+ assert_eq!(intrinsics::bswap(0xabu8), 0xabu8);
+ assert_eq!(intrinsics::bswap(0xddccu16), 0xccddu16);
+ assert_eq!(intrinsics::bswap(0xffee_ddccu32), 0xccdd_eeffu32);
+ assert_eq!(intrinsics::bswap(0x1234_5678_ffee_ddccu64), 0xccdd_eeff_7856_3412u64);
+
+ assert_eq!(intrinsics::size_of_val(hello) as u8, 6);
+
+ let chars = &['C', 'h', 'a', 'r', 's'];
+ let chars = chars as &[char];
+ assert_eq!(intrinsics::size_of_val(chars) as u8, 4 * 5);
+
+ let a: &dyn SomeTrait = &"abc\0";
+ a.object_safe();
+
+ assert_eq!(intrinsics::size_of_val(a) as u8, 16);
+ assert_eq!(intrinsics::size_of_val(&0u32) as u8, 4);
+
+ assert_eq!(intrinsics::min_align_of::<u16>() as u8, 2);
+ assert_eq!(intrinsics::min_align_of_val(&a) as u8, intrinsics::min_align_of::<&str>() as u8);
+
+ assert!(!intrinsics::needs_drop::<u8>());
+ assert!(intrinsics::needs_drop::<NoisyDrop>());
+
+ Unique {
+ pointer: 0 as *const &str,
+ _marker: PhantomData,
+ } as Unique<dyn SomeTrait>;
+
+ struct MyDst<T: ?Sized>(T);
+
+ intrinsics::size_of_val(&MyDst([0u8; 4]) as &MyDst<[u8]>);
+
+ struct Foo {
+ x: u8,
+ y: !,
+ }
+
+ unsafe fn uninitialized<T>() -> T {
+ MaybeUninit { uninit: () }.value.value
+ }
+
+ zeroed::<(u8, u8)>();
+ #[allow(unreachable_code)]
+ {
+ if false {
+ zeroed::<!>();
+ zeroed::<Foo>();
+ uninitialized::<Foo>();
+ }
+ }
+ }
+
+ let _ = box NoisyDrop {
+ text: "Boxed outer got dropped!\0",
+ inner: NoisyDropInner,
+ } as Box<dyn SomeTrait>;
+
+ const FUNC_REF: Option<fn()> = Some(main);
+ match FUNC_REF {
+ Some(_) => {},
+ None => assert!(false),
+ }
+
+ match Ordering::Less {
+ Ordering::Less => {},
+ _ => assert!(false),
+ }
+
+ [NoisyDropInner, NoisyDropInner];
+
+ let x = &[0u32, 42u32] as &[u32];
+ match x {
+ [] => assert_eq!(0u32, 1),
+ [_, ref y @ ..] => assert_eq!(&x[1] as *const u32 as usize, &y[0] as *const u32 as usize),
+ }
+
+ assert_eq!(((|()| 42u8) as fn(()) -> u8)(()), 42);
+
+ extern {
+ #[linkage = "weak"]
+ static ABC: *const u8;
+ }
+
+ {
+ extern {
+ #[linkage = "weak"]
+ static ABC: *const u8;
+ }
+ }
+
+ // TODO(antoyo): to make this work, support weak linkage.
+ //unsafe { assert_eq!(ABC as usize, 0); }
+
+ &mut (|| Some(0 as *const ())) as &mut dyn FnMut() -> Option<*const ()>;
+
+ let f = 1000.0;
+ assert_eq!(f as u8, 255);
+ let f2 = -1000.0;
+ assert_eq!(f2 as i8, -128);
+ assert_eq!(f2 as u8, 0);
+
+ static ANOTHER_STATIC: &u8 = &A_STATIC;
+ assert_eq!(*ANOTHER_STATIC, 42);
+
+ check_niche_behavior();
+
+ extern "C" {
+ type ExternType;
+ }
+
+ struct ExternTypeWrapper {
+ _a: ExternType,
+ }
+
+ let nullptr = 0 as *const ();
+ let extern_nullptr = nullptr as *const ExternTypeWrapper;
+ extern_nullptr as *const ();
+ let slice_ptr = &[] as *const [u8];
+ slice_ptr as *const u8;
+
+ #[cfg(not(jit))]
+ test_tls();
+}
+
+#[repr(C)]
+enum c_void {
+ _1,
+ _2,
+}
+
+type c_int = i32;
+type c_ulong = u64;
+
+type pthread_t = c_ulong;
+
+#[repr(C)]
+struct pthread_attr_t {
+ __size: [u64; 7],
+}
+
+#[link(name = "pthread")]
+extern "C" {
+ fn pthread_attr_init(attr: *mut pthread_attr_t) -> c_int;
+
+ fn pthread_create(
+ native: *mut pthread_t,
+ attr: *const pthread_attr_t,
+ f: extern "C" fn(_: *mut c_void) -> *mut c_void,
+ value: *mut c_void
+ ) -> c_int;
+
+ fn pthread_join(
+ native: pthread_t,
+ value: *mut *mut c_void
+ ) -> c_int;
+}
+
+#[thread_local]
+#[cfg(not(jit))]
+static mut TLS: u8 = 42;
+
+#[cfg(not(jit))]
+extern "C" fn mutate_tls(_: *mut c_void) -> *mut c_void {
+ unsafe { TLS = 0; }
+ 0 as *mut c_void
+}
+
+#[cfg(not(jit))]
+fn test_tls() {
+ unsafe {
+ let mut attr: pthread_attr_t = zeroed();
+ let mut thread: pthread_t = 0;
+
+ assert_eq!(TLS, 42);
+
+ if pthread_attr_init(&mut attr) != 0 {
+ assert!(false);
+ }
+
+ if pthread_create(&mut thread, &attr, mutate_tls, 0 as *mut c_void) != 0 {
+ assert!(false);
+ }
+
+ let mut res = 0 as *mut c_void;
+ pthread_join(thread, &mut res);
+
+ // TLS of main thread must not have been changed by the other thread.
+ assert_eq!(TLS, 42);
+
+ puts("TLS works!\n\0" as *const str as *const u8);
+ }
+}
+
+// Copied ui/issues/issue-61696.rs
+
+pub enum Infallible {}
+
+// The check that the `bool` field of `V1` is encoding a "niche variant"
+// (i.e. not `V1`, so `V3` or `V4`) used to be mathematically incorrect,
+// causing valid `V1` values to be interpreted as other variants.
+pub enum E1 {
+ V1 { f: bool },
+ V2 { f: Infallible },
+ V3,
+ V4,
+}
+
+// Computing the discriminant used to be done using the niche type (here `u8`,
+// from the `bool` field of `V1`), overflowing for variants with large enough
+// indices (`V3` and `V4`), causing them to be interpreted as other variants.
+pub enum E2<X> {
+ V1 { f: bool },
+
+ /*_00*/ _01(X), _02(X), _03(X), _04(X), _05(X), _06(X), _07(X),
+ _08(X), _09(X), _0A(X), _0B(X), _0C(X), _0D(X), _0E(X), _0F(X),
+ _10(X), _11(X), _12(X), _13(X), _14(X), _15(X), _16(X), _17(X),
+ _18(X), _19(X), _1A(X), _1B(X), _1C(X), _1D(X), _1E(X), _1F(X),
+ _20(X), _21(X), _22(X), _23(X), _24(X), _25(X), _26(X), _27(X),
+ _28(X), _29(X), _2A(X), _2B(X), _2C(X), _2D(X), _2E(X), _2F(X),
+ _30(X), _31(X), _32(X), _33(X), _34(X), _35(X), _36(X), _37(X),
+ _38(X), _39(X), _3A(X), _3B(X), _3C(X), _3D(X), _3E(X), _3F(X),
+ _40(X), _41(X), _42(X), _43(X), _44(X), _45(X), _46(X), _47(X),
+ _48(X), _49(X), _4A(X), _4B(X), _4C(X), _4D(X), _4E(X), _4F(X),
+ _50(X), _51(X), _52(X), _53(X), _54(X), _55(X), _56(X), _57(X),
+ _58(X), _59(X), _5A(X), _5B(X), _5C(X), _5D(X), _5E(X), _5F(X),
+ _60(X), _61(X), _62(X), _63(X), _64(X), _65(X), _66(X), _67(X),
+ _68(X), _69(X), _6A(X), _6B(X), _6C(X), _6D(X), _6E(X), _6F(X),
+ _70(X), _71(X), _72(X), _73(X), _74(X), _75(X), _76(X), _77(X),
+ _78(X), _79(X), _7A(X), _7B(X), _7C(X), _7D(X), _7E(X), _7F(X),
+ _80(X), _81(X), _82(X), _83(X), _84(X), _85(X), _86(X), _87(X),
+ _88(X), _89(X), _8A(X), _8B(X), _8C(X), _8D(X), _8E(X), _8F(X),
+ _90(X), _91(X), _92(X), _93(X), _94(X), _95(X), _96(X), _97(X),
+ _98(X), _99(X), _9A(X), _9B(X), _9C(X), _9D(X), _9E(X), _9F(X),
+ _A0(X), _A1(X), _A2(X), _A3(X), _A4(X), _A5(X), _A6(X), _A7(X),
+ _A8(X), _A9(X), _AA(X), _AB(X), _AC(X), _AD(X), _AE(X), _AF(X),
+ _B0(X), _B1(X), _B2(X), _B3(X), _B4(X), _B5(X), _B6(X), _B7(X),
+ _B8(X), _B9(X), _BA(X), _BB(X), _BC(X), _BD(X), _BE(X), _BF(X),
+ _C0(X), _C1(X), _C2(X), _C3(X), _C4(X), _C5(X), _C6(X), _C7(X),
+ _C8(X), _C9(X), _CA(X), _CB(X), _CC(X), _CD(X), _CE(X), _CF(X),
+ _D0(X), _D1(X), _D2(X), _D3(X), _D4(X), _D5(X), _D6(X), _D7(X),
+ _D8(X), _D9(X), _DA(X), _DB(X), _DC(X), _DD(X), _DE(X), _DF(X),
+ _E0(X), _E1(X), _E2(X), _E3(X), _E4(X), _E5(X), _E6(X), _E7(X),
+ _E8(X), _E9(X), _EA(X), _EB(X), _EC(X), _ED(X), _EE(X), _EF(X),
+ _F0(X), _F1(X), _F2(X), _F3(X), _F4(X), _F5(X), _F6(X), _F7(X),
+ _F8(X), _F9(X), _FA(X), _FB(X), _FC(X), _FD(X), _FE(X), _FF(X),
+
+ V3,
+ V4,
+}
+
+fn check_niche_behavior () {
+ if let E1::V2 { .. } = (E1::V1 { f: true }) {
+ intrinsics::abort();
+ }
+
+ if let E2::V1 { .. } = E2::V3::<Infallible> {
+ intrinsics::abort();
+ }
+}
--- /dev/null
+#![feature(start, box_syntax, core_intrinsics, lang_items)]
+#![no_std]
+
+#[link(name = "c")]
+extern {}
+
+#[panic_handler]
+fn panic_handler(_: &core::panic::PanicInfo) -> ! {
+ unsafe {
+ core::intrinsics::abort();
+ }
+}
+
+#[lang="eh_personality"]
+fn eh_personality(){}
+
+// Required for rustc_codegen_llvm
+#[no_mangle]
+unsafe extern "C" fn _Unwind_Resume() {
+ core::intrinsics::unreachable();
+}
+
+#[start]
+fn main(_argc: isize, _argv: *const *const u8) -> isize {
+ for i in 2..100_000_000 {
+ black_box((i + 1) % i);
+ }
+
+ 0
+}
+
+#[inline(never)]
+fn black_box(i: u32) {
+ if i != 1 {
+ unsafe { core::intrinsics::abort(); }
+ }
+}
--- /dev/null
+#![feature(core_intrinsics, generators, generator_trait, is_sorted)]
+
+use std::arch::x86_64::*;
+use std::io::Write;
+use std::ops::Generator;
+
+extern {
+ pub fn printf(format: *const i8, ...) -> i32;
+}
+
+fn main() {
+ let mutex = std::sync::Mutex::new(());
+ let _guard = mutex.lock().unwrap();
+
+ let _ = ::std::iter::repeat('a' as u8).take(10).collect::<Vec<_>>();
+ let stderr = ::std::io::stderr();
+ let mut stderr = stderr.lock();
+
+ std::thread::spawn(move || {
+ println!("Hello from another thread!");
+ });
+
+ writeln!(stderr, "some {} text", "<unknown>").unwrap();
+
+ let _ = std::process::Command::new("true").env("c", "d").spawn();
+
+ println!("cargo:rustc-link-lib=z");
+
+ static ONCE: std::sync::Once = std::sync::Once::new();
+ ONCE.call_once(|| {});
+
+ let _eq = LoopState::Continue(()) == LoopState::Break(());
+
+ // Make sure ByValPair values with differently sized components are correctly passed
+ map(None::<(u8, Box<Instruction>)>);
+
+ println!("{}", 2.3f32.exp());
+ println!("{}", 2.3f32.exp2());
+ println!("{}", 2.3f32.abs());
+ println!("{}", 2.3f32.sqrt());
+ println!("{}", 2.3f32.floor());
+ println!("{}", 2.3f32.ceil());
+ println!("{}", 2.3f32.min(1.0));
+ println!("{}", 2.3f32.max(1.0));
+ println!("{}", 2.3f32.powi(2));
+ println!("{}", 2.3f32.log2());
+ assert_eq!(2.3f32.copysign(-1.0), -2.3f32);
+ println!("{}", 2.3f32.powf(2.0));
+
+ assert_eq!(-128i8, (-128i8).saturating_sub(1));
+ assert_eq!(127i8, 127i8.saturating_sub(-128));
+ assert_eq!(-128i8, (-128i8).saturating_add(-128));
+ assert_eq!(127i8, 127i8.saturating_add(1));
+
+ assert_eq!(-32768i16, (-32768i16).saturating_add(-32768));
+ assert_eq!(32767i16, 32767i16.saturating_add(1));
+
+ assert_eq!(0b0000000000000000000000000010000010000000000000000000000000000000_0000000000100000000000000000000000001000000000000100000000000000u128.leading_zeros(), 26);
+ assert_eq!(0b0000000000000000000000000010000000000000000000000000000000000000_0000000000000000000000000000000000001000000000000000000010000000u128.trailing_zeros(), 7);
+
+ let _d = 0i128.checked_div(2i128);
+ let _d = 0u128.checked_div(2u128);
+ assert_eq!(1u128 + 2, 3);
+
+ assert_eq!(0b100010000000000000000000000000000u128 >> 10, 0b10001000000000000000000u128);
+ assert_eq!(0xFEDCBA987654321123456789ABCDEFu128 >> 64, 0xFEDCBA98765432u128);
+ assert_eq!(0xFEDCBA987654321123456789ABCDEFu128 as i128 >> 64, 0xFEDCBA98765432i128);
+
+ let tmp = 353985398u128;
+ assert_eq!(tmp * 932490u128, 330087843781020u128);
+
+ let tmp = -0x1234_5678_9ABC_DEF0i64;
+ assert_eq!(tmp as i128, -0x1234_5678_9ABC_DEF0i128);
+
+ // Check that all u/i128 <-> float casts work correctly.
+ let houndred_u128 = 100u128;
+ let houndred_i128 = 100i128;
+ let houndred_f32 = 100.0f32;
+ let houndred_f64 = 100.0f64;
+ assert_eq!(houndred_u128 as f32, 100.0);
+ assert_eq!(houndred_u128 as f64, 100.0);
+ assert_eq!(houndred_f32 as u128, 100);
+ assert_eq!(houndred_f64 as u128, 100);
+ assert_eq!(houndred_i128 as f32, 100.0);
+ assert_eq!(houndred_i128 as f64, 100.0);
+ assert_eq!(houndred_f32 as i128, 100);
+ assert_eq!(houndred_f64 as i128, 100);
+
+ let _a = 1u32 << 2u8;
+
+ let empty: [i32; 0] = [];
+ assert!(empty.is_sorted());
+
+ println!("{:?}", std::intrinsics::caller_location());
+
+ /*unsafe {
+ test_simd();
+ }*/
+
+ Box::pin(move |mut _task_context| {
+ yield ();
+ }).as_mut().resume(0);
+
+ println!("End");
+}
+
+/*#[target_feature(enable = "sse2")]
+unsafe fn test_simd() {
+ let x = _mm_setzero_si128();
+ let y = _mm_set1_epi16(7);
+ let or = _mm_or_si128(x, y);
+ let cmp_eq = _mm_cmpeq_epi8(y, y);
+ let cmp_lt = _mm_cmplt_epi8(y, y);
+
+ /*assert_eq!(std::mem::transmute::<_, [u16; 8]>(or), [7, 7, 7, 7, 7, 7, 7, 7]);
+ assert_eq!(std::mem::transmute::<_, [u16; 8]>(cmp_eq), [0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff]);
+ assert_eq!(std::mem::transmute::<_, [u16; 8]>(cmp_lt), [0, 0, 0, 0, 0, 0, 0, 0]);
+
+ test_mm_slli_si128();
+ test_mm_movemask_epi8();
+ test_mm256_movemask_epi8();
+ test_mm_add_epi8();
+ test_mm_add_pd();
+ test_mm_cvtepi8_epi16();
+ test_mm_cvtsi128_si64();
+
+ // FIXME(#666) implement `#[rustc_arg_required_const(..)]` support
+ //test_mm_extract_epi8();
+
+ let mask1 = _mm_movemask_epi8(dbg!(_mm_setr_epi8(255u8 as i8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)));
+ assert_eq!(mask1, 1);*/
+}*/
+
+/*#[target_feature(enable = "sse2")]
+unsafe fn test_mm_slli_si128() {
+ #[rustfmt::skip]
+ let a = _mm_setr_epi8(
+ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
+ );
+ let r = _mm_slli_si128(a, 1);
+ let e = _mm_setr_epi8(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15);
+ assert_eq_m128i(r, e);
+
+ #[rustfmt::skip]
+ let a = _mm_setr_epi8(
+ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
+ );
+ let r = _mm_slli_si128(a, 15);
+ let e = _mm_setr_epi8(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1);
+ assert_eq_m128i(r, e);
+
+ #[rustfmt::skip]
+ let a = _mm_setr_epi8(
+ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
+ );
+ let r = _mm_slli_si128(a, 16);
+ assert_eq_m128i(r, _mm_set1_epi8(0));
+
+ #[rustfmt::skip]
+ let a = _mm_setr_epi8(
+ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
+ );
+ let r = _mm_slli_si128(a, -1);
+ assert_eq_m128i(_mm_set1_epi8(0), r);
+
+ #[rustfmt::skip]
+ let a = _mm_setr_epi8(
+ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
+ );
+ let r = _mm_slli_si128(a, -0x80000000);
+ assert_eq_m128i(r, _mm_set1_epi8(0));
+}
+
+#[target_feature(enable = "sse2")]
+unsafe fn test_mm_movemask_epi8() {
+ #[rustfmt::skip]
+ let a = _mm_setr_epi8(
+ 0b1000_0000u8 as i8, 0b0, 0b1000_0000u8 as i8, 0b01,
+ 0b0101, 0b1111_0000u8 as i8, 0, 0,
+ 0, 0, 0b1111_0000u8 as i8, 0b0101,
+ 0b01, 0b1000_0000u8 as i8, 0b0, 0b1000_0000u8 as i8,
+ );
+ let r = _mm_movemask_epi8(a);
+ assert_eq!(r, 0b10100100_00100101);
+}
+
+#[target_feature(enable = "avx2")]
+unsafe fn test_mm256_movemask_epi8() {
+ let a = _mm256_set1_epi8(-1);
+ let r = _mm256_movemask_epi8(a);
+ let e = -1;
+ assert_eq!(r, e);
+}
+
+#[target_feature(enable = "sse2")]
+unsafe fn test_mm_add_epi8() {
+ let a = _mm_setr_epi8(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15);
+ #[rustfmt::skip]
+ let b = _mm_setr_epi8(
+ 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
+ );
+ let r = _mm_add_epi8(a, b);
+ #[rustfmt::skip]
+ let e = _mm_setr_epi8(
+ 16, 18, 20, 22, 24, 26, 28, 30, 32, 34, 36, 38, 40, 42, 44, 46,
+ );
+ assert_eq_m128i(r, e);
+}
+
+#[target_feature(enable = "sse2")]
+unsafe fn test_mm_add_pd() {
+ let a = _mm_setr_pd(1.0, 2.0);
+ let b = _mm_setr_pd(5.0, 10.0);
+ let r = _mm_add_pd(a, b);
+ assert_eq_m128d(r, _mm_setr_pd(6.0, 12.0));
+}
+
+fn assert_eq_m128i(x: std::arch::x86_64::__m128i, y: std::arch::x86_64::__m128i) {
+ unsafe {
+ assert_eq!(std::mem::transmute::<_, [u8; 16]>(x), std::mem::transmute::<_, [u8; 16]>(y));
+ }
+}
+
+#[target_feature(enable = "sse2")]
+pub unsafe fn assert_eq_m128d(a: __m128d, b: __m128d) {
+ if _mm_movemask_pd(_mm_cmpeq_pd(a, b)) != 0b11 {
+ panic!("{:?} != {:?}", a, b);
+ }
+}
+
+#[target_feature(enable = "sse2")]
+unsafe fn test_mm_cvtsi128_si64() {
+ let r = _mm_cvtsi128_si64(std::mem::transmute::<[i64; 2], _>([5, 0]));
+ assert_eq!(r, 5);
+}
+
+#[target_feature(enable = "sse4.1")]
+unsafe fn test_mm_cvtepi8_epi16() {
+ let a = _mm_set1_epi8(10);
+ let r = _mm_cvtepi8_epi16(a);
+ let e = _mm_set1_epi16(10);
+ assert_eq_m128i(r, e);
+ let a = _mm_set1_epi8(-10);
+ let r = _mm_cvtepi8_epi16(a);
+ let e = _mm_set1_epi16(-10);
+ assert_eq_m128i(r, e);
+}
+
+#[target_feature(enable = "sse4.1")]
+unsafe fn test_mm_extract_epi8() {
+ #[rustfmt::skip]
+ let a = _mm_setr_epi8(
+ -1, 1, 2, 3, 4, 5, 6, 7,
+ 8, 9, 10, 11, 12, 13, 14, 15
+ );
+ let r1 = _mm_extract_epi8(a, 0);
+ let r2 = _mm_extract_epi8(a, 19);
+ assert_eq!(r1, 0xFF);
+ assert_eq!(r2, 3);
+}*/
+
+#[derive(PartialEq)]
+enum LoopState {
+ Continue(()),
+ Break(())
+}
+
+pub enum Instruction {
+ Increment,
+ Loop,
+}
+
+fn map(a: Option<(u8, Box<Instruction>)>) -> Option<Box<Instruction>> {
+ match a {
+ None => None,
+ Some((_, instr)) => Some(instr),
+ }
+}
--- /dev/null
+// Based on https://github.com/rust-lang/rust/blob/c5840f9d252c2f5cc16698dbf385a29c5de3ca07/src/test/ui/array-slice-vec/subslice-patterns-const-eval-match.rs
+
+// Test that array subslice patterns are correctly handled in const evaluation.
+
+// run-pass
+
+#[derive(PartialEq, Debug, Clone)]
+struct N(u8);
+
+#[derive(PartialEq, Debug, Clone)]
+struct Z;
+
+macro_rules! n {
+ ($($e:expr),* $(,)?) => {
+ [$(N($e)),*]
+ }
+}
+
+// This macro has an unused variable so that it can be repeated base on the
+// number of times a repeated variable (`$e` in `z`) occurs.
+macro_rules! zed {
+ ($e:expr) => { Z }
+}
+
+macro_rules! z {
+ ($($e:expr),* $(,)?) => {
+ [$(zed!($e)),*]
+ }
+}
+
+// Compare constant evaluation and runtime evaluation of a given expression.
+macro_rules! compare_evaluation {
+ ($e:expr, $t:ty $(,)?) => {{
+ const CONST_EVAL: $t = $e;
+ const fn const_eval() -> $t { $e }
+ static CONST_EVAL2: $t = const_eval();
+ let runtime_eval = $e;
+ assert_eq!(CONST_EVAL, runtime_eval);
+ assert_eq!(CONST_EVAL2, runtime_eval);
+ }}
+}
+
+// Repeat `$test`, substituting the given macro variables with the given
+// identifiers.
+//
+// For example:
+//
+// repeat! {
+// ($name); X; Y:
+// struct $name;
+// }
+//
+// Expands to:
+//
+// struct X; struct Y;
+//
+// This is used to repeat the tests using both the `N` and `Z`
+// types.
+macro_rules! repeat {
+ (($($dollar:tt $placeholder:ident)*); $($($values:ident),+);*: $($test:tt)*) => {
+ macro_rules! single {
+ ($($dollar $placeholder:ident),*) => { $($test)* }
+ }
+ $(single!($($values),+);)*
+ }
+}
+
+fn main() {
+ repeat! {
+ ($arr $Ty); n, N; z, Z:
+ compare_evaluation!({ let [_, x @ .., _] = $arr!(1, 2, 3, 4); x }, [$Ty; 2]);
+ compare_evaluation!({ let [_, ref x @ .., _] = $arr!(1, 2, 3, 4); x }, &'static [$Ty; 2]);
+ compare_evaluation!({ let [_, x @ .., _] = &$arr!(1, 2, 3, 4); x }, &'static [$Ty; 2]);
+
+ compare_evaluation!({ let [_, _, x @ .., _, _] = $arr!(1, 2, 3, 4); x }, [$Ty; 0]);
+ compare_evaluation!(
+ { let [_, _, ref x @ .., _, _] = $arr!(1, 2, 3, 4); x },
+ &'static [$Ty; 0],
+ );
+ compare_evaluation!(
+ { let [_, _, x @ .., _, _] = &$arr!(1, 2, 3, 4); x },
+ &'static [$Ty; 0],
+ );
+
+ compare_evaluation!({ let [_, .., x] = $arr!(1, 2, 3, 4); x }, $Ty);
+ compare_evaluation!({ let [_, .., ref x] = $arr!(1, 2, 3, 4); x }, &'static $Ty);
+ compare_evaluation!({ let [_, _y @ .., x] = &$arr!(1, 2, 3, 4); x }, &'static $Ty);
+ }
+
+ compare_evaluation!({ let [_, .., N(x)] = n!(1, 2, 3, 4); x }, u8);
+ compare_evaluation!({ let [_, .., N(ref x)] = n!(1, 2, 3, 4); x }, &'static u8);
+ compare_evaluation!({ let [_, .., N(x)] = &n!(1, 2, 3, 4); x }, &'static u8);
+
+ compare_evaluation!({ let [N(x), .., _] = n!(1, 2, 3, 4); x }, u8);
+ compare_evaluation!({ let [N(ref x), .., _] = n!(1, 2, 3, 4); x }, &'static u8);
+ compare_evaluation!({ let [N(x), .., _] = &n!(1, 2, 3, 4); x }, &'static u8);
+}
--- /dev/null
+// Based on https://github.com/anp/rust/blob/175631311716d7dfeceec40d2587cde7142ffa8c/src/test/ui/rfc-2091-track-caller/track-caller-attribute.rs
+
+// run-pass
+
+use std::panic::Location;
+
+#[track_caller]
+fn tracked() -> &'static Location<'static> {
+ Location::caller()
+}
+
+fn nested_intrinsic() -> &'static Location<'static> {
+ Location::caller()
+}
+
+fn nested_tracked() -> &'static Location<'static> {
+ tracked()
+}
+
+fn main() {
+ let location = Location::caller();
+ assert_eq!(location.file(), file!());
+ assert_eq!(location.line(), 21);
+ assert_eq!(location.column(), 20);
+
+ let tracked = tracked();
+ assert_eq!(tracked.file(), file!());
+ assert_eq!(tracked.line(), 26);
+ assert_eq!(tracked.column(), 19);
+
+ let nested = nested_intrinsic();
+ assert_eq!(nested.file(), file!());
+ assert_eq!(nested.line(), 13);
+ assert_eq!(nested.column(), 5);
+
+ let contained = nested_tracked();
+ assert_eq!(contained.file(), file!());
+ assert_eq!(contained.line(), 17);
+ assert_eq!(contained.column(), 5);
+}
--- /dev/null
+From f6befc4bb51d84f5f1cf35938a168c953d421350 Mon Sep 17 00:00:00 2001
+From: bjorn3 <bjorn3@users.noreply.github.com>
+Date: Sun, 24 Nov 2019 15:10:23 +0100
+Subject: [PATCH] [core] Disable not compiling tests
+
+---
+ library/core/tests/Cargo.toml | 8 ++++++++
+ library/core/tests/num/flt2dec/mod.rs | 1 -
+ library/core/tests/num/int_macros.rs | 2 ++
+ library/core/tests/num/uint_macros.rs | 2 ++
+ library/core/tests/ptr.rs | 2 ++
+ library/core/tests/slice.rs | 2 ++
+ 6 files changed, 16 insertions(+), 1 deletion(-)
+ create mode 100644 library/core/tests/Cargo.toml
+
+diff --git a/library/core/tests/Cargo.toml b/library/core/tests/Cargo.toml
+new file mode 100644
+index 0000000..46fd999
+--- /dev/null
++++ b/library/core/tests/Cargo.toml
+@@ -0,0 +1,8 @@
++[package]
++name = "core"
++version = "0.0.0"
++edition = "2018"
++
++[lib]
++name = "coretests"
++path = "lib.rs"
+diff --git a/library/core/tests/num/flt2dec/mod.rs b/library/core/tests/num/flt2dec/mod.rs
+index a35897e..f0bf645 100644
+--- a/library/core/tests/num/flt2dec/mod.rs
++++ b/library/core/tests/num/flt2dec/mod.rs
+@@ -13,7 +13,6 @@ mod strategy {
+ mod dragon;
+ mod grisu;
+ }
+-mod random;
+
+ pub fn decode_finite<T: DecodableFloat>(v: T) -> Decoded {
+ match decode(v).1 {
+diff --git a/library/core/tests/slice.rs b/library/core/tests/slice.rs
+index 6609bc3..241b497 100644
+--- a/library/core/tests/slice.rs
++++ b/library/core/tests/slice.rs
+@@ -1209,6 +1209,7 @@ fn brute_force_rotate_test_1() {
+ }
+ }
+
++/*
+ #[test]
+ #[cfg(not(target_arch = "wasm32"))]
+ fn sort_unstable() {
+@@ -1394,6 +1395,7 @@ fn partition_at_index() {
+ v.select_nth_unstable(0);
+ assert!(v == [0xDEADBEEF]);
+ }
++*/
+
+ #[test]
+ #[should_panic(expected = "index 0 greater than length of slice")]
+--
+2.21.0 (Apple Git-122)
--- /dev/null
+From dd82e95c9de212524e14fc60155de1ae40156dfc Mon Sep 17 00:00:00 2001
+From: bjorn3 <bjorn3@users.noreply.github.com>
+Date: Sun, 24 Nov 2019 15:34:06 +0100
+Subject: [PATCH] [core] Ignore failing tests
+
+---
+ library/core/tests/iter.rs | 4 ++++
+ library/core/tests/num/bignum.rs | 10 ++++++++++
+ library/core/tests/num/mod.rs | 5 +++--
+ library/core/tests/time.rs | 1 +
+ 4 files changed, 18 insertions(+), 2 deletions(-)
+
+diff --git a/library/core/tests/array.rs b/library/core/tests/array.rs
+index 4bc44e9..8e3c7a4 100644
+--- a/library/core/tests/array.rs
++++ b/library/core/tests/array.rs
+@@ -242,6 +242,7 @@ fn iterator_drops() {
+ assert_eq!(i.get(), 5);
+ }
+
++/*
+ // This test does not work on targets without panic=unwind support.
+ // To work around this problem, test is marked is should_panic, so it will
+ // be automagically skipped on unsuitable targets, such as
+@@ -283,6 +284,7 @@ fn array_default_impl_avoids_leaks_on_panic() {
+ assert_eq!(COUNTER.load(Relaxed), 0);
+ panic!("test succeeded")
+ }
++*/
+
+ #[test]
+ fn empty_array_is_always_default() {
+@@ -304,6 +304,7 @@ fn array_map() {
+ assert_eq!(b, [1, 2, 3]);
+ }
+
++/*
+ // See note on above test for why `should_panic` is used.
+ #[test]
+ #[should_panic(expected = "test succeeded")]
+@@ -332,6 +333,7 @@ fn array_map_drop_safety() {
+ assert_eq!(DROPPED.load(Ordering::SeqCst), num_to_create);
+ panic!("test succeeded")
+ }
++*/
+
+ #[test]
+ fn cell_allows_array_cycle() {
+-- 2.21.0 (Apple Git-122)
--- /dev/null
+#!/bin/bash --verbose
+set -e
+
+source prepare_build.sh
+
+cargo install hyperfine || echo "Skipping hyperfine install"
+
+git clone https://github.com/rust-lang/regex.git || echo "rust-lang/regex has already been cloned"
+pushd regex
+git checkout -- .
+git checkout 341f207c1071f7290e3f228c710817c280c8dca1
+popd
+
+git clone https://github.com/ebobby/simple-raytracer || echo "ebobby/simple-raytracer has already been cloned"
+pushd simple-raytracer
+git checkout -- .
+git checkout 804a7a21b9e673a482797aa289a18ed480e4d813
+
+# build with cg_llvm for perf comparison
+cargo build
+mv target/debug/main raytracer_cg_llvm
+popd
--- /dev/null
+#!/bin/bash --verbose
+set -e
+
+rustup component add rust-src rustc-dev llvm-tools-preview
+./build_sysroot/prepare_sysroot_src.sh
--- /dev/null
+nightly-2021-09-28
--- /dev/null
+#!/bin/bash
+
+set -e
+
+case $1 in
+ "prepare")
+ TOOLCHAIN=$(date +%Y-%m-%d)
+
+ echo "=> Installing new nightly"
+ rustup toolchain install --profile minimal nightly-${TOOLCHAIN} # Sanity check to see if the nightly exists
+ echo nightly-${TOOLCHAIN} > rust-toolchain
+
+ echo "=> Uninstalling all old nighlies"
+ for nightly in $(rustup toolchain list | grep nightly | grep -v $TOOLCHAIN | grep -v nightly-x86_64); do
+ rustup toolchain uninstall $nightly
+ done
+
+ ./clean_all.sh
+ ./prepare.sh
+ ;;
+ "commit")
+ git add rust-toolchain
+ git commit -m "Rustup to $(rustc -V)"
+ ;;
+ *)
+ echo "Unknown command '$1'"
+ echo "Usage: ./rustup.sh prepare|commit"
+ ;;
+esac
--- /dev/null
+use gccjit::{ToRValue, Type};
+use rustc_codegen_ssa::traits::{AbiBuilderMethods, BaseTypeMethods};
+use rustc_middle::bug;
+use rustc_middle::ty::Ty;
+use rustc_target::abi::call::{CastTarget, FnAbi, PassMode, Reg, RegKind};
+
+use crate::builder::Builder;
+use crate::context::CodegenCx;
+use crate::intrinsic::ArgAbiExt;
+use crate::type_of::LayoutGccExt;
+
+impl<'a, 'gcc, 'tcx> AbiBuilderMethods<'tcx> for Builder<'a, 'gcc, 'tcx> {
+ fn apply_attrs_callsite(&mut self, _fn_abi: &FnAbi<'tcx, Ty<'tcx>>, _callsite: Self::Value) {
+ // TODO(antoyo)
+ }
+
+ fn get_param(&self, index: usize) -> Self::Value {
+ self.cx.current_func.borrow().expect("current func")
+ .get_param(index as i32)
+ .to_rvalue()
+ }
+}
+
+impl GccType for CastTarget {
+ fn gcc_type<'gcc>(&self, cx: &CodegenCx<'gcc, '_>) -> Type<'gcc> {
+ let rest_gcc_unit = self.rest.unit.gcc_type(cx);
+ let (rest_count, rem_bytes) =
+ if self.rest.unit.size.bytes() == 0 {
+ (0, 0)
+ }
+ else {
+ (self.rest.total.bytes() / self.rest.unit.size.bytes(), self.rest.total.bytes() % self.rest.unit.size.bytes())
+ };
+
+ if self.prefix.iter().all(|x| x.is_none()) {
+ // Simplify to a single unit when there is no prefix and size <= unit size
+ if self.rest.total <= self.rest.unit.size {
+ return rest_gcc_unit;
+ }
+
+ // Simplify to array when all chunks are the same size and type
+ if rem_bytes == 0 {
+ return cx.type_array(rest_gcc_unit, rest_count);
+ }
+ }
+
+ // Create list of fields in the main structure
+ let mut args: Vec<_> = self
+ .prefix
+ .iter()
+ .flat_map(|option_kind| {
+ option_kind.map(|kind| Reg { kind, size: self.prefix_chunk_size }.gcc_type(cx))
+ })
+ .chain((0..rest_count).map(|_| rest_gcc_unit))
+ .collect();
+
+ // Append final integer
+ if rem_bytes != 0 {
+ // Only integers can be really split further.
+ assert_eq!(self.rest.unit.kind, RegKind::Integer);
+ args.push(cx.type_ix(rem_bytes * 8));
+ }
+
+ cx.type_struct(&args, false)
+ }
+}
+
+pub trait GccType {
+ fn gcc_type<'gcc>(&self, cx: &CodegenCx<'gcc, '_>) -> Type<'gcc>;
+}
+
+impl GccType for Reg {
+ fn gcc_type<'gcc>(&self, cx: &CodegenCx<'gcc, '_>) -> Type<'gcc> {
+ match self.kind {
+ RegKind::Integer => cx.type_ix(self.size.bits()),
+ RegKind::Float => {
+ match self.size.bits() {
+ 32 => cx.type_f32(),
+ 64 => cx.type_f64(),
+ _ => bug!("unsupported float: {:?}", self),
+ }
+ },
+ RegKind::Vector => unimplemented!(), //cx.type_vector(cx.type_i8(), self.size.bytes()),
+ }
+ }
+}
+
+pub trait FnAbiGccExt<'gcc, 'tcx> {
+ // TODO(antoyo): return a function pointer type instead?
+ fn gcc_type(&self, cx: &CodegenCx<'gcc, 'tcx>) -> (Type<'gcc>, Vec<Type<'gcc>>, bool);
+ fn ptr_to_gcc_type(&self, cx: &CodegenCx<'gcc, 'tcx>) -> Type<'gcc>;
+}
+
+impl<'gcc, 'tcx> FnAbiGccExt<'gcc, 'tcx> for FnAbi<'tcx, Ty<'tcx>> {
+ fn gcc_type(&self, cx: &CodegenCx<'gcc, 'tcx>) -> (Type<'gcc>, Vec<Type<'gcc>>, bool) {
+ let args_capacity: usize = self.args.iter().map(|arg|
+ if arg.pad.is_some() {
+ 1
+ }
+ else {
+ 0
+ } +
+ if let PassMode::Pair(_, _) = arg.mode {
+ 2
+ } else {
+ 1
+ }
+ ).sum();
+ let mut argument_tys = Vec::with_capacity(
+ if let PassMode::Indirect { .. } = self.ret.mode {
+ 1
+ }
+ else {
+ 0
+ } + args_capacity,
+ );
+
+ let return_ty =
+ match self.ret.mode {
+ PassMode::Ignore => cx.type_void(),
+ PassMode::Direct(_) | PassMode::Pair(..) => self.ret.layout.immediate_gcc_type(cx),
+ PassMode::Cast(cast) => cast.gcc_type(cx),
+ PassMode::Indirect { .. } => {
+ argument_tys.push(cx.type_ptr_to(self.ret.memory_ty(cx)));
+ cx.type_void()
+ }
+ };
+
+ for arg in &self.args {
+ // add padding
+ if let Some(ty) = arg.pad {
+ argument_tys.push(ty.gcc_type(cx));
+ }
+
+ let arg_ty = match arg.mode {
+ PassMode::Ignore => continue,
+ PassMode::Direct(_) => arg.layout.immediate_gcc_type(cx),
+ PassMode::Pair(..) => {
+ argument_tys.push(arg.layout.scalar_pair_element_gcc_type(cx, 0, true));
+ argument_tys.push(arg.layout.scalar_pair_element_gcc_type(cx, 1, true));
+ continue;
+ }
+ PassMode::Indirect { extra_attrs: Some(_), .. } => {
+ unimplemented!();
+ }
+ PassMode::Cast(cast) => cast.gcc_type(cx),
+ PassMode::Indirect { extra_attrs: None, .. } => cx.type_ptr_to(arg.memory_ty(cx)),
+ };
+ argument_tys.push(arg_ty);
+ }
+
+ (return_ty, argument_tys, self.c_variadic)
+ }
+
+ fn ptr_to_gcc_type(&self, cx: &CodegenCx<'gcc, 'tcx>) -> Type<'gcc> {
+ let (return_type, params, variadic) = self.gcc_type(cx);
+ let pointer_type = cx.context.new_function_pointer_type(None, return_type, ¶ms, variadic);
+ pointer_type
+ }
+}
--- /dev/null
+use gccjit::{FunctionType, ToRValue};
+use rustc_ast::expand::allocator::{AllocatorKind, AllocatorTy, ALLOCATOR_METHODS};
+use rustc_middle::bug;
+use rustc_middle::ty::TyCtxt;
+use rustc_span::symbol::sym;
+
+use crate::GccContext;
+
+pub(crate) unsafe fn codegen(tcx: TyCtxt<'_>, mods: &mut GccContext, _module_name: &str, kind: AllocatorKind, has_alloc_error_handler: bool) {
+ let context = &mods.context;
+ let usize =
+ match tcx.sess.target.pointer_width {
+ 16 => context.new_type::<u16>(),
+ 32 => context.new_type::<u32>(),
+ 64 => context.new_type::<u64>(),
+ tws => bug!("Unsupported target word size for int: {}", tws),
+ };
+ let i8 = context.new_type::<i8>();
+ let i8p = i8.make_pointer();
+ let void = context.new_type::<()>();
+
+ for method in ALLOCATOR_METHODS {
+ let mut types = Vec::with_capacity(method.inputs.len());
+ for ty in method.inputs.iter() {
+ match *ty {
+ AllocatorTy::Layout => {
+ types.push(usize);
+ types.push(usize);
+ }
+ AllocatorTy::Ptr => types.push(i8p),
+ AllocatorTy::Usize => types.push(usize),
+
+ AllocatorTy::ResultPtr | AllocatorTy::Unit => panic!("invalid allocator arg"),
+ }
+ }
+ let output = match method.output {
+ AllocatorTy::ResultPtr => Some(i8p),
+ AllocatorTy::Unit => None,
+
+ AllocatorTy::Layout | AllocatorTy::Usize | AllocatorTy::Ptr => {
+ panic!("invalid allocator output")
+ }
+ };
+ let name = format!("__rust_{}", method.name);
+
+ let args: Vec<_> = types.iter().enumerate()
+ .map(|(index, typ)| context.new_parameter(None, *typ, &format!("param{}", index)))
+ .collect();
+ let func = context.new_function(None, FunctionType::Exported, output.unwrap_or(void), &args, name, false);
+
+ if tcx.sess.target.options.default_hidden_visibility {
+ // TODO(antoyo): set visibility.
+ }
+ if tcx.sess.must_emit_unwind_tables() {
+ // TODO(antoyo): emit unwind tables.
+ }
+
+ let callee = kind.fn_name(method.name);
+ let args: Vec<_> = types.iter().enumerate()
+ .map(|(index, typ)| context.new_parameter(None, *typ, &format!("param{}", index)))
+ .collect();
+ let callee = context.new_function(None, FunctionType::Extern, output.unwrap_or(void), &args, callee, false);
+ // TODO(antoyo): set visibility.
+
+ let block = func.new_block("entry");
+
+ let args = args
+ .iter()
+ .enumerate()
+ .map(|(i, _)| func.get_param(i as i32).to_rvalue())
+ .collect::<Vec<_>>();
+ let ret = context.new_call(None, callee, &args);
+ //llvm::LLVMSetTailCall(ret, True);
+ if output.is_some() {
+ block.end_with_return(None, ret);
+ }
+ else {
+ block.end_with_void_return(None);
+ }
+
+ // TODO(@Commeownist): Check if we need to emit some extra debugging info in certain circumstances
+ // as described in https://github.com/rust-lang/rust/commit/77a96ed5646f7c3ee8897693decc4626fe380643
+ }
+
+ let types = [usize, usize];
+ let name = "__rust_alloc_error_handler".to_string();
+ let args: Vec<_> = types.iter().enumerate()
+ .map(|(index, typ)| context.new_parameter(None, *typ, &format!("param{}", index)))
+ .collect();
+ let func = context.new_function(None, FunctionType::Exported, void, &args, name, false);
+
+ let kind =
+ if has_alloc_error_handler {
+ AllocatorKind::Global
+ }
+ else {
+ AllocatorKind::Default
+ };
+ let callee = kind.fn_name(sym::oom);
+ let args: Vec<_> = types.iter().enumerate()
+ .map(|(index, typ)| context.new_parameter(None, *typ, &format!("param{}", index)))
+ .collect();
+ let callee = context.new_function(None, FunctionType::Extern, void, &args, callee, false);
+ //llvm::LLVMRustSetVisibility(callee, llvm::Visibility::Hidden);
+
+ let block = func.new_block("entry");
+
+ let args = args
+ .iter()
+ .enumerate()
+ .map(|(i, _)| func.get_param(i as i32).to_rvalue())
+ .collect::<Vec<_>>();
+ let _ret = context.new_call(None, callee, &args);
+ //llvm::LLVMSetTailCall(ret, True);
+ block.end_with_void_return(None);
+}
--- /dev/null
+use std::fs::File;
+use std::path::{Path, PathBuf};
+
+use rustc_session::Session;
+use rustc_codegen_ssa::back::archive::ArchiveBuilder;
+
+use rustc_data_structures::temp_dir::MaybeTempDir;
+use rustc_middle::middle::cstore::DllImport;
+
+
+struct ArchiveConfig<'a> {
+ sess: &'a Session,
+ dst: PathBuf,
+ use_native_ar: bool,
+ use_gnu_style_archive: bool,
+}
+
+#[derive(Debug)]
+enum ArchiveEntry {
+ FromArchive {
+ archive_index: usize,
+ entry_index: usize,
+ },
+ File(PathBuf),
+}
+
+pub struct ArArchiveBuilder<'a> {
+ config: ArchiveConfig<'a>,
+ src_archives: Vec<(PathBuf, ar::Archive<File>)>,
+ // Don't use `HashMap` here, as the order is important. `rust.metadata.bin` must always be at
+ // the end of an archive for linkers to not get confused.
+ entries: Vec<(String, ArchiveEntry)>,
+}
+
+impl<'a> ArchiveBuilder<'a> for ArArchiveBuilder<'a> {
+ fn new(sess: &'a Session, output: &Path, input: Option<&Path>) -> Self {
+ let config = ArchiveConfig {
+ sess,
+ dst: output.to_path_buf(),
+ use_native_ar: false,
+ // FIXME test for linux and System V derivatives instead
+ use_gnu_style_archive: sess.target.options.archive_format == "gnu",
+ };
+
+ let (src_archives, entries) = if let Some(input) = input {
+ let mut archive = ar::Archive::new(File::open(input).unwrap());
+ let mut entries = Vec::new();
+
+ let mut i = 0;
+ while let Some(entry) = archive.next_entry() {
+ let entry = entry.unwrap();
+ entries.push((
+ String::from_utf8(entry.header().identifier().to_vec()).unwrap(),
+ ArchiveEntry::FromArchive {
+ archive_index: 0,
+ entry_index: i,
+ },
+ ));
+ i += 1;
+ }
+
+ (vec![(input.to_owned(), archive)], entries)
+ } else {
+ (vec![], Vec::new())
+ };
+
+ ArArchiveBuilder {
+ config,
+ src_archives,
+ entries,
+ }
+ }
+
+ fn src_files(&mut self) -> Vec<String> {
+ self.entries.iter().map(|(name, _)| name.clone()).collect()
+ }
+
+ fn remove_file(&mut self, name: &str) {
+ let index = self
+ .entries
+ .iter()
+ .position(|(entry_name, _)| entry_name == name)
+ .expect("Tried to remove file not existing in src archive");
+ self.entries.remove(index);
+ }
+
+ fn add_file(&mut self, file: &Path) {
+ self.entries.push((
+ file.file_name().unwrap().to_str().unwrap().to_string(),
+ ArchiveEntry::File(file.to_owned()),
+ ));
+ }
+
+ fn add_archive<F>(&mut self, archive_path: &Path, mut skip: F) -> std::io::Result<()>
+ where
+ F: FnMut(&str) -> bool + 'static,
+ {
+ let mut archive = ar::Archive::new(std::fs::File::open(&archive_path)?);
+ let archive_index = self.src_archives.len();
+
+ let mut i = 0;
+ while let Some(entry) = archive.next_entry() {
+ let entry = entry?;
+ let file_name = String::from_utf8(entry.header().identifier().to_vec())
+ .map_err(|err| std::io::Error::new(std::io::ErrorKind::InvalidData, err))?;
+ if !skip(&file_name) {
+ self.entries
+ .push((file_name, ArchiveEntry::FromArchive { archive_index, entry_index: i }));
+ }
+ i += 1;
+ }
+
+ self.src_archives.push((archive_path.to_owned(), archive));
+ Ok(())
+ }
+
+ fn update_symbols(&mut self) {
+ }
+
+ fn build(mut self) {
+ use std::process::Command;
+
+ fn add_file_using_ar(archive: &Path, file: &Path) {
+ Command::new("ar")
+ .arg("r") // add or replace file
+ .arg("-c") // silence created file message
+ .arg(archive)
+ .arg(&file)
+ .status()
+ .unwrap();
+ }
+
+ enum BuilderKind<'a> {
+ Bsd(ar::Builder<File>),
+ Gnu(ar::GnuBuilder<File>),
+ NativeAr(&'a Path),
+ }
+
+ let mut builder = if self.config.use_native_ar {
+ BuilderKind::NativeAr(&self.config.dst)
+ } else if self.config.use_gnu_style_archive {
+ BuilderKind::Gnu(ar::GnuBuilder::new(
+ File::create(&self.config.dst).unwrap(),
+ self.entries
+ .iter()
+ .map(|(name, _)| name.as_bytes().to_vec())
+ .collect(),
+ ))
+ } else {
+ BuilderKind::Bsd(ar::Builder::new(File::create(&self.config.dst).unwrap()))
+ };
+
+ // Add all files
+ for (entry_name, entry) in self.entries.into_iter() {
+ match entry {
+ ArchiveEntry::FromArchive {
+ archive_index,
+ entry_index,
+ } => {
+ let (ref src_archive_path, ref mut src_archive) =
+ self.src_archives[archive_index];
+ let entry = src_archive.jump_to_entry(entry_index).unwrap();
+ let header = entry.header().clone();
+
+ match builder {
+ BuilderKind::Bsd(ref mut builder) => {
+ builder.append(&header, entry).unwrap()
+ }
+ BuilderKind::Gnu(ref mut builder) => {
+ builder.append(&header, entry).unwrap()
+ }
+ BuilderKind::NativeAr(archive_file) => {
+ Command::new("ar")
+ .arg("x")
+ .arg(src_archive_path)
+ .arg(&entry_name)
+ .status()
+ .unwrap();
+ add_file_using_ar(archive_file, Path::new(&entry_name));
+ std::fs::remove_file(entry_name).unwrap();
+ }
+ }
+ }
+ ArchiveEntry::File(file) =>
+ match builder {
+ BuilderKind::Bsd(ref mut builder) => {
+ builder
+ .append_file(entry_name.as_bytes(), &mut File::open(file).expect("file for bsd builder"))
+ .unwrap()
+ },
+ BuilderKind::Gnu(ref mut builder) => {
+ builder
+ .append_file(entry_name.as_bytes(), &mut File::open(&file).expect(&format!("file {:?} for gnu builder", file)))
+ .unwrap()
+ },
+ BuilderKind::NativeAr(archive_file) => add_file_using_ar(archive_file, &file),
+ },
+ }
+ }
+
+ // Finalize archive
+ std::mem::drop(builder);
+
+ // Run ranlib to be able to link the archive
+ let status = std::process::Command::new("ranlib")
+ .arg(self.config.dst)
+ .status()
+ .expect("Couldn't run ranlib");
+
+ if !status.success() {
+ self.config.sess.fatal(&format!("Ranlib exited with code {:?}", status.code()));
+ }
+ }
+
+ fn inject_dll_import_lib(&mut self, _lib_name: &str, _dll_imports: &[DllImport], _tmpdir: &MaybeTempDir) {
+ unimplemented!();
+ }
+}
--- /dev/null
+use gccjit::{LValue, RValue, ToRValue, Type};
+use rustc_ast::ast::{InlineAsmOptions, InlineAsmTemplatePiece};
+use rustc_codegen_ssa::mir::operand::OperandValue;
+use rustc_codegen_ssa::mir::place::PlaceRef;
+use rustc_codegen_ssa::traits::{AsmBuilderMethods, AsmMethods, BaseTypeMethods, BuilderMethods, GlobalAsmOperandRef, InlineAsmOperandRef};
+
+use rustc_hir::LlvmInlineAsmInner;
+use rustc_middle::{bug, ty::Instance};
+use rustc_span::{Span, Symbol};
+use rustc_target::asm::*;
+
+use std::borrow::Cow;
+
+use crate::builder::Builder;
+use crate::context::CodegenCx;
+use crate::type_of::LayoutGccExt;
+
+
+// Rust asm! and GCC Extended Asm semantics differ substantially.
+//
+// 1. Rust asm operands go along as one list of operands. Operands themselves indicate
+// if they're "in" or "out". "In" and "out" operands can interleave. One operand can be
+// both "in" and "out" (`inout(reg)`).
+//
+// GCC asm has two different lists for "in" and "out" operands. In terms of gccjit,
+// this means that all "out" operands must go before "in" operands. "In" and "out" operands
+// cannot interleave.
+//
+// 2. Operand lists in both Rust and GCC are indexed. Index starts from 0. Indexes are important
+// because the asm template refers to operands by index.
+//
+// Mapping from Rust to GCC index would be 1-1 if it wasn't for...
+//
+// 3. Clobbers. GCC has a separate list of clobbers, and clobbers don't have indexes.
+// Contrary, Rust expresses clobbers through "out" operands that aren't tied to
+// a variable (`_`), and such "clobbers" do have index.
+//
+// 4. Furthermore, GCC Extended Asm does not support explicit register constraints
+// (like `out("eax")`) directly, offering so-called "local register variables"
+// as a workaround. These variables need to be declared and initialized *before*
+// the Extended Asm block but *after* normal local variables
+// (see comment in `codegen_inline_asm` for explanation).
+//
+// With that in mind, let's see how we translate Rust syntax to GCC
+// (from now on, `CC` stands for "constraint code"):
+//
+// * `out(reg_class) var` -> translated to output operand: `"=CC"(var)`
+// * `inout(reg_class) var` -> translated to output operand: `"+CC"(var)`
+// * `in(reg_class) var` -> translated to input operand: `"CC"(var)`
+//
+// * `out(reg_class) _` -> translated to one `=r(tmp)`, where "tmp" is a temporary unused variable
+//
+// * `out("explicit register") _` -> not translated to any operands, register is simply added to clobbers list
+//
+// * `inout(reg_class) in_var => out_var` -> translated to two operands:
+// output: `"=CC"(in_var)`
+// input: `"num"(out_var)` where num is the GCC index
+// of the corresponding output operand
+//
+// * `inout(reg_class) in_var => _` -> same as `inout(reg_class) in_var => tmp`,
+// where "tmp" is a temporary unused variable
+//
+// * `out/in/inout("explicit register") var` -> translated to one or two operands as described above
+// with `"r"(var)` constraint,
+// and one register variable assigned to the desired register.
+//
+
+const ATT_SYNTAX_INS: &str = ".att_syntax noprefix\n\t";
+const INTEL_SYNTAX_INS: &str = "\n\t.intel_syntax noprefix";
+
+
+struct AsmOutOperand<'a, 'tcx, 'gcc> {
+ rust_idx: usize,
+ constraint: &'a str,
+ late: bool,
+ readwrite: bool,
+
+ tmp_var: LValue<'gcc>,
+ out_place: Option<PlaceRef<'tcx, RValue<'gcc>>>
+}
+
+struct AsmInOperand<'a, 'tcx> {
+ rust_idx: usize,
+ constraint: Cow<'a, str>,
+ val: RValue<'tcx>
+}
+
+impl AsmOutOperand<'_, '_, '_> {
+ fn to_constraint(&self) -> String {
+ let mut res = String::with_capacity(self.constraint.len() + self.late as usize + 1);
+
+ let sign = if self.readwrite { '+' } else { '=' };
+ res.push(sign);
+ if !self.late {
+ res.push('&');
+ }
+
+ res.push_str(&self.constraint);
+ res
+ }
+}
+
+enum ConstraintOrRegister {
+ Constraint(&'static str),
+ Register(&'static str)
+}
+
+
+impl<'a, 'gcc, 'tcx> AsmBuilderMethods<'tcx> for Builder<'a, 'gcc, 'tcx> {
+ fn codegen_llvm_inline_asm(&mut self, _ia: &LlvmInlineAsmInner, _outputs: Vec<PlaceRef<'tcx, RValue<'gcc>>>, _inputs: Vec<RValue<'gcc>>, span: Span) -> bool {
+ self.sess().struct_span_err(span, "GCC backend does not support `llvm_asm!`")
+ .help("consider using the `asm!` macro instead")
+ .emit();
+
+ // We return `true` even if we've failed to generate the asm
+ // because we want to suppress the "malformed inline assembly" error
+ // generated by the frontend.
+ true
+ }
+
+ fn codegen_inline_asm(&mut self, template: &[InlineAsmTemplatePiece], rust_operands: &[InlineAsmOperandRef<'tcx, Self>], options: InlineAsmOptions, _span: &[Span]) {
+ let asm_arch = self.tcx.sess.asm_arch.unwrap();
+ let is_x86 = matches!(asm_arch, InlineAsmArch::X86 | InlineAsmArch::X86_64);
+ let att_dialect = is_x86 && options.contains(InlineAsmOptions::ATT_SYNTAX);
+ let intel_dialect = is_x86 && !options.contains(InlineAsmOptions::ATT_SYNTAX);
+
+ // GCC index of an output operand equals its position in the array
+ let mut outputs = vec![];
+
+ // GCC index of an input operand equals its position in the array
+ // added to `outputs.len()`
+ let mut inputs = vec![];
+
+ // Clobbers collected from `out("explicit register") _` and `inout("expl_reg") var => _`
+ let mut clobbers = vec![];
+
+ // We're trying to preallocate space for the template
+ let mut constants_len = 0;
+
+ // There are rules we must adhere to if we want GCC to do the right thing:
+ //
+ // * Every local variable that the asm block uses as an output must be declared *before*
+ // the asm block.
+ // * There must be no instructions whatsoever between the register variables and the asm.
+ //
+ // Therefore, the backend must generate the instructions strictly in this order:
+ //
+ // 1. Output variables.
+ // 2. Register variables.
+ // 3. The asm block.
+ //
+ // We also must make sure that no input operands are emitted before output operands.
+ //
+ // This is why we work in passes, first emitting local vars, then local register vars.
+ // Also, we don't emit any asm operands immediately; we save them to
+ // the one of the buffers to be emitted later.
+
+ // 1. Normal variables (and saving operands to buffers).
+ for (rust_idx, op) in rust_operands.iter().enumerate() {
+ match *op {
+ InlineAsmOperandRef::Out { reg, late, place } => {
+ use ConstraintOrRegister::*;
+
+ let (constraint, ty) = match (reg_to_gcc(reg), place) {
+ (Constraint(constraint), Some(place)) => (constraint, place.layout.gcc_type(self.cx, false)),
+ // When `reg` is a class and not an explicit register but the out place is not specified,
+ // we need to create an unused output variable to assign the output to. This var
+ // needs to be of a type that's "compatible" with the register class, but specific type
+ // doesn't matter.
+ (Constraint(constraint), None) => (constraint, dummy_output_type(self.cx, reg.reg_class())),
+ (Register(_), Some(_)) => {
+ // left for the next pass
+ continue
+ },
+ (Register(reg_name), None) => {
+ // `clobber_abi` can add lots of clobbers that are not supported by the target,
+ // such as AVX-512 registers, so we just ignore unsupported registers
+ let is_target_supported = reg.reg_class().supported_types(asm_arch).iter()
+ .any(|&(_, feature)| {
+ if let Some(feature) = feature {
+ self.tcx.sess.target_features.contains(&Symbol::intern(feature))
+ } else {
+ true // Register class is unconditionally supported
+ }
+ });
+
+ if is_target_supported && !clobbers.contains(®_name) {
+ clobbers.push(reg_name);
+ }
+ continue
+ }
+ };
+
+ let tmp_var = self.current_func().new_local(None, ty, "output_register");
+ outputs.push(AsmOutOperand {
+ constraint,
+ rust_idx,
+ late,
+ readwrite: false,
+ tmp_var,
+ out_place: place
+ });
+ }
+
+ InlineAsmOperandRef::In { reg, value } => {
+ if let ConstraintOrRegister::Constraint(constraint) = reg_to_gcc(reg) {
+ inputs.push(AsmInOperand {
+ constraint: Cow::Borrowed(constraint),
+ rust_idx,
+ val: value.immediate()
+ });
+ }
+ else {
+ // left for the next pass
+ continue
+ }
+ }
+
+ InlineAsmOperandRef::InOut { reg, late, in_value, out_place } => {
+ let constraint = if let ConstraintOrRegister::Constraint(constraint) = reg_to_gcc(reg) {
+ constraint
+ }
+ else {
+ // left for the next pass
+ continue
+ };
+
+ // Rustc frontend guarantees that input and output types are "compatible",
+ // so we can just use input var's type for the output variable.
+ //
+ // This decision is also backed by the fact that LLVM needs in and out
+ // values to be of *exactly the same type*, not just "compatible".
+ // I'm not sure if GCC is so picky too, but better safe than sorry.
+ let ty = in_value.layout.gcc_type(self.cx, false);
+ let tmp_var = self.current_func().new_local(None, ty, "output_register");
+
+ // If the out_place is None (i.e `inout(reg) _` syntax was used), we translate
+ // it to one "readwrite (+) output variable", otherwise we translate it to two
+ // "out and tied in" vars as described above.
+ let readwrite = out_place.is_none();
+ outputs.push(AsmOutOperand {
+ constraint,
+ rust_idx,
+ late,
+ readwrite,
+ tmp_var,
+ out_place,
+ });
+
+ if !readwrite {
+ let out_gcc_idx = outputs.len() - 1;
+ let constraint = Cow::Owned(out_gcc_idx.to_string());
+
+ inputs.push(AsmInOperand {
+ constraint,
+ rust_idx,
+ val: in_value.immediate()
+ });
+ }
+ }
+
+ InlineAsmOperandRef::Const { ref string } => {
+ constants_len += string.len() + att_dialect as usize;
+ }
+
+ InlineAsmOperandRef::SymFn { instance } => {
+ constants_len += self.tcx.symbol_name(instance).name.len();
+ }
+ InlineAsmOperandRef::SymStatic { def_id } => {
+ constants_len += self.tcx.symbol_name(Instance::mono(self.tcx, def_id)).name.len();
+ }
+ }
+ }
+
+ // 2. Register variables.
+ for (rust_idx, op) in rust_operands.iter().enumerate() {
+ match *op {
+ // `out("explicit register") var`
+ InlineAsmOperandRef::Out { reg, late, place } => {
+ if let ConstraintOrRegister::Register(reg_name) = reg_to_gcc(reg) {
+ let out_place = if let Some(place) = place {
+ place
+ }
+ else {
+ // processed in the previous pass
+ continue
+ };
+
+ let ty = out_place.layout.gcc_type(self.cx, false);
+ let tmp_var = self.current_func().new_local(None, ty, "output_register");
+ tmp_var.set_register_name(reg_name);
+
+ outputs.push(AsmOutOperand {
+ constraint: "r".into(),
+ rust_idx,
+ late,
+ readwrite: false,
+ tmp_var,
+ out_place: Some(out_place)
+ });
+ }
+
+ // processed in the previous pass
+ }
+
+ // `in("explicit register") var`
+ InlineAsmOperandRef::In { reg, value } => {
+ if let ConstraintOrRegister::Register(reg_name) = reg_to_gcc(reg) {
+ let ty = value.layout.gcc_type(self.cx, false);
+ let reg_var = self.current_func().new_local(None, ty, "input_register");
+ reg_var.set_register_name(reg_name);
+ self.llbb().add_assignment(None, reg_var, value.immediate());
+
+ inputs.push(AsmInOperand {
+ constraint: "r".into(),
+ rust_idx,
+ val: reg_var.to_rvalue()
+ });
+ }
+
+ // processed in the previous pass
+ }
+
+ // `inout("explicit register") in_var => out_var`
+ InlineAsmOperandRef::InOut { reg, late, in_value, out_place } => {
+ if let ConstraintOrRegister::Register(reg_name) = reg_to_gcc(reg) {
+ let out_place = if let Some(place) = out_place {
+ place
+ }
+ else {
+ // processed in the previous pass
+ continue
+ };
+
+ // See explanation in the first pass.
+ let ty = in_value.layout.gcc_type(self.cx, false);
+ let tmp_var = self.current_func().new_local(None, ty, "output_register");
+ tmp_var.set_register_name(reg_name);
+
+ outputs.push(AsmOutOperand {
+ constraint: "r".into(),
+ rust_idx,
+ late,
+ readwrite: false,
+ tmp_var,
+ out_place: Some(out_place)
+ });
+
+ let constraint = Cow::Owned((outputs.len() - 1).to_string());
+ inputs.push(AsmInOperand {
+ constraint,
+ rust_idx,
+ val: in_value.immediate()
+ });
+ }
+
+ // processed in the previous pass
+ }
+
+ InlineAsmOperandRef::Const { .. }
+ | InlineAsmOperandRef::SymFn { .. }
+ | InlineAsmOperandRef::SymStatic { .. } => {
+ // processed in the previous pass
+ }
+ }
+ }
+
+ // 3. Build the template string
+
+ let mut template_str = String::with_capacity(estimate_template_length(template, constants_len, att_dialect));
+ if !intel_dialect {
+ template_str.push_str(ATT_SYNTAX_INS);
+ }
+
+ for piece in template {
+ match *piece {
+ InlineAsmTemplatePiece::String(ref string) => {
+ // TODO(@Commeownist): switch to `Iterator::intersperse` once it's stable
+ let mut iter = string.split('%');
+ if let Some(s) = iter.next() {
+ template_str.push_str(s);
+ }
+
+ for s in iter {
+ template_str.push_str("%%");
+ template_str.push_str(s);
+ }
+ }
+ InlineAsmTemplatePiece::Placeholder { operand_idx, modifier, span: _ } => {
+ let mut push_to_template = |modifier, gcc_idx| {
+ use std::fmt::Write;
+
+ template_str.push('%');
+ if let Some(modifier) = modifier {
+ template_str.push(modifier);
+ }
+ write!(template_str, "{}", gcc_idx).expect("pushing to string failed");
+ };
+
+ match rust_operands[operand_idx] {
+ InlineAsmOperandRef::Out { reg, .. } => {
+ let modifier = modifier_to_gcc(asm_arch, reg.reg_class(), modifier);
+ let gcc_index = outputs.iter()
+ .position(|op| operand_idx == op.rust_idx)
+ .expect("wrong rust index");
+ push_to_template(modifier, gcc_index);
+ }
+
+ InlineAsmOperandRef::In { reg, .. } => {
+ let modifier = modifier_to_gcc(asm_arch, reg.reg_class(), modifier);
+ let in_gcc_index = inputs.iter()
+ .position(|op| operand_idx == op.rust_idx)
+ .expect("wrong rust index");
+ let gcc_index = in_gcc_index + outputs.len();
+ push_to_template(modifier, gcc_index);
+ }
+
+ InlineAsmOperandRef::InOut { reg, .. } => {
+ let modifier = modifier_to_gcc(asm_arch, reg.reg_class(), modifier);
+
+ // The input register is tied to the output, so we can just use the index of the output register
+ let gcc_index = outputs.iter()
+ .position(|op| operand_idx == op.rust_idx)
+ .expect("wrong rust index");
+ push_to_template(modifier, gcc_index);
+ }
+
+ InlineAsmOperandRef::SymFn { instance } => {
+ let name = self.tcx.symbol_name(instance).name;
+ template_str.push_str(name);
+ }
+
+ InlineAsmOperandRef::SymStatic { def_id } => {
+ // TODO(@Commeownist): This may not be sufficient for all kinds of statics.
+ // Some statics may need the `@plt` suffix, like thread-local vars.
+ let instance = Instance::mono(self.tcx, def_id);
+ let name = self.tcx.symbol_name(instance).name;
+ template_str.push_str(name);
+ }
+
+ InlineAsmOperandRef::Const { ref string } => {
+ // Const operands get injected directly into the template
+ if att_dialect {
+ template_str.push('$');
+ }
+ template_str.push_str(string);
+ }
+ }
+ }
+ }
+ }
+
+ if !intel_dialect {
+ template_str.push_str(INTEL_SYNTAX_INS);
+ }
+
+ // 4. Generate Extended Asm block
+
+ let block = self.llbb();
+ let extended_asm = block.add_extended_asm(None, &template_str);
+
+ for op in &outputs {
+ extended_asm.add_output_operand(None, &op.to_constraint(), op.tmp_var);
+ }
+
+ for op in &inputs {
+ extended_asm.add_input_operand(None, &op.constraint, op.val);
+ }
+
+ for clobber in clobbers.iter() {
+ extended_asm.add_clobber(clobber);
+ }
+
+ if !options.contains(InlineAsmOptions::PRESERVES_FLAGS) {
+ // TODO(@Commeownist): I'm not 100% sure this one clobber is sufficient
+ // on all architectures. For instance, what about FP stack?
+ extended_asm.add_clobber("cc");
+ }
+ if !options.contains(InlineAsmOptions::NOMEM) {
+ extended_asm.add_clobber("memory");
+ }
+ if !options.contains(InlineAsmOptions::PURE) {
+ extended_asm.set_volatile_flag(true);
+ }
+ if !options.contains(InlineAsmOptions::NOSTACK) {
+ // TODO(@Commeownist): figure out how to align stack
+ }
+ if options.contains(InlineAsmOptions::NORETURN) {
+ let builtin_unreachable = self.context.get_builtin_function("__builtin_unreachable");
+ let builtin_unreachable: RValue<'gcc> = unsafe { std::mem::transmute(builtin_unreachable) };
+ self.call(self.type_void(), builtin_unreachable, &[], None);
+ }
+
+ // Write results to outputs.
+ //
+ // We need to do this because:
+ // 1. Turning `PlaceRef` into `RValue` is error-prone and has nasty edge cases
+ // (especially with current `rustc_backend_ssa` API).
+ // 2. Not every output operand has an `out_place`, and it's required by `add_output_operand`.
+ //
+ // Instead, we generate a temporary output variable for each output operand, and then this loop,
+ // generates `out_place = tmp_var;` assignments if out_place exists.
+ for op in &outputs {
+ if let Some(place) = op.out_place {
+ OperandValue::Immediate(op.tmp_var.to_rvalue()).store(self, place);
+ }
+ }
+
+ }
+}
+
+fn estimate_template_length(template: &[InlineAsmTemplatePiece], constants_len: usize, att_dialect: bool) -> usize {
+ let len: usize = template.iter().map(|piece| {
+ match *piece {
+ InlineAsmTemplatePiece::String(ref string) => {
+ string.len()
+ }
+ InlineAsmTemplatePiece::Placeholder { .. } => {
+ // '%' + 1 char modifier + 1 char index
+ 3
+ }
+ }
+ })
+ .sum();
+
+ // increase it by 5% to account for possible '%' signs that'll be duplicated
+ // I pulled the number out of blue, but should be fair enough
+ // as the upper bound
+ let mut res = (len as f32 * 1.05) as usize + constants_len;
+
+ if att_dialect {
+ res += INTEL_SYNTAX_INS.len() + ATT_SYNTAX_INS.len();
+ }
+ res
+}
+
+/// Converts a register class to a GCC constraint code.
+fn reg_to_gcc(reg: InlineAsmRegOrRegClass) -> ConstraintOrRegister {
+ let constraint = match reg {
+ // For vector registers LLVM wants the register name to match the type size.
+ InlineAsmRegOrRegClass::Reg(reg) => {
+ match reg {
+ InlineAsmReg::X86(_) => {
+ // TODO(antoyo): add support for vector register.
+ //
+ // // For explicit registers, we have to create a register variable: https://stackoverflow.com/a/31774784/389119
+ return ConstraintOrRegister::Register(match reg.name() {
+ // Some of registers' names does not map 1-1 from rust to gcc
+ "st(0)" => "st",
+
+ name => name,
+ });
+ }
+
+ _ => unimplemented!(),
+ }
+ },
+ InlineAsmRegOrRegClass::RegClass(reg) => match reg {
+ InlineAsmRegClass::AArch64(AArch64InlineAsmRegClass::preg) => unimplemented!(),
+ InlineAsmRegClass::AArch64(AArch64InlineAsmRegClass::reg) => unimplemented!(),
+ InlineAsmRegClass::AArch64(AArch64InlineAsmRegClass::vreg) => unimplemented!(),
+ InlineAsmRegClass::AArch64(AArch64InlineAsmRegClass::vreg_low16) => unimplemented!(),
+ InlineAsmRegClass::Arm(ArmInlineAsmRegClass::reg) => unimplemented!(),
+ InlineAsmRegClass::Arm(ArmInlineAsmRegClass::reg_thumb) => unimplemented!(),
+ InlineAsmRegClass::Arm(ArmInlineAsmRegClass::sreg)
+ | InlineAsmRegClass::Arm(ArmInlineAsmRegClass::dreg_low16)
+ | InlineAsmRegClass::Arm(ArmInlineAsmRegClass::qreg_low8) => unimplemented!(),
+ InlineAsmRegClass::Arm(ArmInlineAsmRegClass::sreg_low16)
+ | InlineAsmRegClass::Arm(ArmInlineAsmRegClass::dreg_low8)
+ | InlineAsmRegClass::Arm(ArmInlineAsmRegClass::qreg_low4) => unimplemented!(),
+ InlineAsmRegClass::Arm(ArmInlineAsmRegClass::dreg)
+ | InlineAsmRegClass::Arm(ArmInlineAsmRegClass::qreg) => unimplemented!(),
+ InlineAsmRegClass::Bpf(_) => unimplemented!(),
+ InlineAsmRegClass::Hexagon(HexagonInlineAsmRegClass::reg) => unimplemented!(),
+ InlineAsmRegClass::Mips(MipsInlineAsmRegClass::reg) => unimplemented!(),
+ InlineAsmRegClass::Mips(MipsInlineAsmRegClass::freg) => unimplemented!(),
+ InlineAsmRegClass::Nvptx(NvptxInlineAsmRegClass::reg16) => unimplemented!(),
+ InlineAsmRegClass::Nvptx(NvptxInlineAsmRegClass::reg32) => unimplemented!(),
+ InlineAsmRegClass::Nvptx(NvptxInlineAsmRegClass::reg64) => unimplemented!(),
+ InlineAsmRegClass::PowerPC(PowerPCInlineAsmRegClass::reg) => unimplemented!(),
+ InlineAsmRegClass::PowerPC(PowerPCInlineAsmRegClass::reg_nonzero) => unimplemented!(),
+ InlineAsmRegClass::PowerPC(PowerPCInlineAsmRegClass::freg) => unimplemented!(),
+ InlineAsmRegClass::PowerPC(PowerPCInlineAsmRegClass::cr)
+ | InlineAsmRegClass::PowerPC(PowerPCInlineAsmRegClass::xer) => {
+ unreachable!("clobber-only")
+ },
+ InlineAsmRegClass::RiscV(RiscVInlineAsmRegClass::reg) => unimplemented!(),
+ InlineAsmRegClass::RiscV(RiscVInlineAsmRegClass::freg) => unimplemented!(),
+ InlineAsmRegClass::RiscV(RiscVInlineAsmRegClass::vreg) => unimplemented!(),
+ InlineAsmRegClass::X86(X86InlineAsmRegClass::reg) => "r",
+ InlineAsmRegClass::X86(X86InlineAsmRegClass::reg_abcd) => "Q",
+ InlineAsmRegClass::X86(X86InlineAsmRegClass::reg_byte) => "q",
+ InlineAsmRegClass::X86(X86InlineAsmRegClass::xmm_reg)
+ | InlineAsmRegClass::X86(X86InlineAsmRegClass::ymm_reg) => "x",
+ InlineAsmRegClass::X86(X86InlineAsmRegClass::zmm_reg) => "v",
+ InlineAsmRegClass::X86(X86InlineAsmRegClass::kreg) => unimplemented!(),
+ InlineAsmRegClass::Wasm(WasmInlineAsmRegClass::local) => unimplemented!(),
+ InlineAsmRegClass::X86(
+ X86InlineAsmRegClass::x87_reg | X86InlineAsmRegClass::mmx_reg,
+ ) => unreachable!("clobber-only"),
+ InlineAsmRegClass::SpirV(SpirVInlineAsmRegClass::reg) => {
+ bug!("GCC backend does not support SPIR-V")
+ }
+ InlineAsmRegClass::S390x(S390xInlineAsmRegClass::reg) => unimplemented!(),
+ InlineAsmRegClass::S390x(S390xInlineAsmRegClass::freg) => unimplemented!(),
+ InlineAsmRegClass::Err => unreachable!(),
+ }
+ };
+
+ ConstraintOrRegister::Constraint(constraint)
+}
+
+/// Type to use for outputs that are discarded. It doesn't really matter what
+/// the type is, as long as it is valid for the constraint code.
+fn dummy_output_type<'gcc, 'tcx>(cx: &CodegenCx<'gcc, 'tcx>, reg: InlineAsmRegClass) -> Type<'gcc> {
+ match reg {
+ InlineAsmRegClass::AArch64(AArch64InlineAsmRegClass::reg) => cx.type_i32(),
+ InlineAsmRegClass::AArch64(AArch64InlineAsmRegClass::preg) => unimplemented!(),
+ InlineAsmRegClass::AArch64(AArch64InlineAsmRegClass::vreg)
+ | InlineAsmRegClass::AArch64(AArch64InlineAsmRegClass::vreg_low16) => {
+ unimplemented!()
+ }
+ InlineAsmRegClass::Arm(ArmInlineAsmRegClass::reg)
+ | InlineAsmRegClass::Arm(ArmInlineAsmRegClass::reg_thumb) => cx.type_i32(),
+ InlineAsmRegClass::Arm(ArmInlineAsmRegClass::sreg)
+ | InlineAsmRegClass::Arm(ArmInlineAsmRegClass::sreg_low16) => cx.type_f32(),
+ InlineAsmRegClass::Arm(ArmInlineAsmRegClass::dreg)
+ | InlineAsmRegClass::Arm(ArmInlineAsmRegClass::dreg_low16)
+ | InlineAsmRegClass::Arm(ArmInlineAsmRegClass::dreg_low8) => cx.type_f64(),
+ InlineAsmRegClass::Arm(ArmInlineAsmRegClass::qreg)
+ | InlineAsmRegClass::Arm(ArmInlineAsmRegClass::qreg_low8)
+ | InlineAsmRegClass::Arm(ArmInlineAsmRegClass::qreg_low4) => {
+ unimplemented!()
+ }
+ InlineAsmRegClass::Bpf(_) => unimplemented!(),
+ InlineAsmRegClass::Hexagon(HexagonInlineAsmRegClass::reg) => cx.type_i32(),
+ InlineAsmRegClass::Mips(MipsInlineAsmRegClass::reg) => cx.type_i32(),
+ InlineAsmRegClass::Mips(MipsInlineAsmRegClass::freg) => cx.type_f32(),
+ InlineAsmRegClass::Nvptx(NvptxInlineAsmRegClass::reg16) => cx.type_i16(),
+ InlineAsmRegClass::Nvptx(NvptxInlineAsmRegClass::reg32) => cx.type_i32(),
+ InlineAsmRegClass::Nvptx(NvptxInlineAsmRegClass::reg64) => cx.type_i64(),
+ InlineAsmRegClass::PowerPC(PowerPCInlineAsmRegClass::reg) => cx.type_i32(),
+ InlineAsmRegClass::PowerPC(PowerPCInlineAsmRegClass::reg_nonzero) => cx.type_i32(),
+ InlineAsmRegClass::PowerPC(PowerPCInlineAsmRegClass::freg) => cx.type_f64(),
+ InlineAsmRegClass::PowerPC(PowerPCInlineAsmRegClass::cr)
+ | InlineAsmRegClass::PowerPC(PowerPCInlineAsmRegClass::xer) => {
+ unreachable!("clobber-only")
+ },
+ InlineAsmRegClass::RiscV(RiscVInlineAsmRegClass::reg) => cx.type_i32(),
+ InlineAsmRegClass::RiscV(RiscVInlineAsmRegClass::freg) => cx.type_f32(),
+ InlineAsmRegClass::RiscV(RiscVInlineAsmRegClass::vreg) => cx.type_f32(),
+ InlineAsmRegClass::X86(X86InlineAsmRegClass::reg)
+ | InlineAsmRegClass::X86(X86InlineAsmRegClass::reg_abcd) => cx.type_i32(),
+ InlineAsmRegClass::X86(X86InlineAsmRegClass::reg_byte) => cx.type_i8(),
+ InlineAsmRegClass::X86(X86InlineAsmRegClass::mmx_reg) => unimplemented!(),
+ InlineAsmRegClass::X86(X86InlineAsmRegClass::xmm_reg)
+ | InlineAsmRegClass::X86(X86InlineAsmRegClass::ymm_reg)
+ | InlineAsmRegClass::X86(X86InlineAsmRegClass::zmm_reg) => cx.type_f32(),
+ InlineAsmRegClass::X86(X86InlineAsmRegClass::x87_reg) => unimplemented!(),
+ InlineAsmRegClass::X86(X86InlineAsmRegClass::kreg) => cx.type_i16(),
+ InlineAsmRegClass::Wasm(WasmInlineAsmRegClass::local) => cx.type_i32(),
+ InlineAsmRegClass::SpirV(SpirVInlineAsmRegClass::reg) => {
+ bug!("LLVM backend does not support SPIR-V")
+ },
+ InlineAsmRegClass::S390x(S390xInlineAsmRegClass::reg) => cx.type_i32(),
+ InlineAsmRegClass::S390x(S390xInlineAsmRegClass::freg) => cx.type_f64(),
+ InlineAsmRegClass::Err => unreachable!(),
+ }
+}
+
+impl<'gcc, 'tcx> AsmMethods for CodegenCx<'gcc, 'tcx> {
+ fn codegen_global_asm(&self, template: &[InlineAsmTemplatePiece], operands: &[GlobalAsmOperandRef], options: InlineAsmOptions, _line_spans: &[Span]) {
+ let asm_arch = self.tcx.sess.asm_arch.unwrap();
+
+ // Default to Intel syntax on x86
+ let intel_syntax = matches!(asm_arch, InlineAsmArch::X86 | InlineAsmArch::X86_64)
+ && !options.contains(InlineAsmOptions::ATT_SYNTAX);
+
+ // Build the template string
+ let mut template_str = String::new();
+ for piece in template {
+ match *piece {
+ InlineAsmTemplatePiece::String(ref string) => {
+ for line in string.lines() {
+ // NOTE: gcc does not allow inline comment, so remove them.
+ let line =
+ if let Some(index) = line.rfind("//") {
+ &line[..index]
+ }
+ else {
+ line
+ };
+ template_str.push_str(line);
+ template_str.push('\n');
+ }
+ },
+ InlineAsmTemplatePiece::Placeholder { operand_idx, modifier: _, span: _ } => {
+ match operands[operand_idx] {
+ GlobalAsmOperandRef::Const { ref string } => {
+ // Const operands get injected directly into the
+ // template. Note that we don't need to escape %
+ // here unlike normal inline assembly.
+ template_str.push_str(string);
+ }
+ }
+ }
+ }
+ }
+
+ let template_str =
+ if intel_syntax {
+ format!("{}\n\t.intel_syntax noprefix", template_str)
+ }
+ else {
+ format!(".att_syntax\n\t{}\n\t.intel_syntax noprefix", template_str)
+ };
+ // NOTE: seems like gcc will put the asm in the wrong section, so set it to .text manually.
+ let template_str = format!(".pushsection .text\n{}\n.popsection", template_str);
+ self.context.add_top_level_asm(None, &template_str);
+ }
+}
+
+fn modifier_to_gcc(arch: InlineAsmArch, reg: InlineAsmRegClass, modifier: Option<char>) -> Option<char> {
+ match reg {
+ InlineAsmRegClass::AArch64(AArch64InlineAsmRegClass::reg) => modifier,
+ InlineAsmRegClass::AArch64(AArch64InlineAsmRegClass::preg) => modifier,
+ InlineAsmRegClass::AArch64(AArch64InlineAsmRegClass::vreg)
+ | InlineAsmRegClass::AArch64(AArch64InlineAsmRegClass::vreg_low16) => {
+ unimplemented!()
+ }
+ InlineAsmRegClass::Arm(ArmInlineAsmRegClass::reg)
+ | InlineAsmRegClass::Arm(ArmInlineAsmRegClass::reg_thumb) => unimplemented!(),
+ InlineAsmRegClass::Arm(ArmInlineAsmRegClass::sreg)
+ | InlineAsmRegClass::Arm(ArmInlineAsmRegClass::sreg_low16) => unimplemented!(),
+ InlineAsmRegClass::Arm(ArmInlineAsmRegClass::dreg)
+ | InlineAsmRegClass::Arm(ArmInlineAsmRegClass::dreg_low16)
+ | InlineAsmRegClass::Arm(ArmInlineAsmRegClass::dreg_low8) => unimplemented!(),
+ InlineAsmRegClass::Arm(ArmInlineAsmRegClass::qreg)
+ | InlineAsmRegClass::Arm(ArmInlineAsmRegClass::qreg_low8)
+ | InlineAsmRegClass::Arm(ArmInlineAsmRegClass::qreg_low4) => {
+ unimplemented!()
+ }
+ InlineAsmRegClass::Bpf(_) => unimplemented!(),
+ InlineAsmRegClass::Hexagon(_) => unimplemented!(),
+ InlineAsmRegClass::Mips(_) => unimplemented!(),
+ InlineAsmRegClass::Nvptx(_) => unimplemented!(),
+ InlineAsmRegClass::PowerPC(_) => unimplemented!(),
+ InlineAsmRegClass::RiscV(RiscVInlineAsmRegClass::reg)
+ | InlineAsmRegClass::RiscV(RiscVInlineAsmRegClass::freg) => unimplemented!(),
+ InlineAsmRegClass::RiscV(RiscVInlineAsmRegClass::vreg) => unimplemented!(),
+ InlineAsmRegClass::X86(X86InlineAsmRegClass::reg)
+ | InlineAsmRegClass::X86(X86InlineAsmRegClass::reg_abcd) => match modifier {
+ None => if arch == InlineAsmArch::X86_64 { Some('q') } else { Some('k') },
+ Some('l') => Some('b'),
+ Some('h') => Some('h'),
+ Some('x') => Some('w'),
+ Some('e') => Some('k'),
+ Some('r') => Some('q'),
+ _ => unreachable!(),
+ },
+ InlineAsmRegClass::X86(X86InlineAsmRegClass::reg_byte) => None,
+ InlineAsmRegClass::X86(reg @ X86InlineAsmRegClass::xmm_reg)
+ | InlineAsmRegClass::X86(reg @ X86InlineAsmRegClass::ymm_reg)
+ | InlineAsmRegClass::X86(reg @ X86InlineAsmRegClass::zmm_reg) => match (reg, modifier) {
+ (X86InlineAsmRegClass::xmm_reg, None) => Some('x'),
+ (X86InlineAsmRegClass::ymm_reg, None) => Some('t'),
+ (X86InlineAsmRegClass::zmm_reg, None) => Some('g'),
+ (_, Some('x')) => Some('x'),
+ (_, Some('y')) => Some('t'),
+ (_, Some('z')) => Some('g'),
+ _ => unreachable!(),
+ },
+ InlineAsmRegClass::X86(X86InlineAsmRegClass::kreg) => None,
+ InlineAsmRegClass::X86(X86InlineAsmRegClass::x87_reg | X86InlineAsmRegClass::mmx_reg) => {
+ unreachable!("clobber-only")
+ }
+ InlineAsmRegClass::Wasm(WasmInlineAsmRegClass::local) => unimplemented!(),
+ InlineAsmRegClass::SpirV(SpirVInlineAsmRegClass::reg) => {
+ bug!("LLVM backend does not support SPIR-V")
+ },
+ InlineAsmRegClass::S390x(S390xInlineAsmRegClass::reg) => unimplemented!(),
+ InlineAsmRegClass::S390x(S390xInlineAsmRegClass::freg) => unimplemented!(),
+ InlineAsmRegClass::Err => unreachable!(),
+ }
+}
--- /dev/null
+pub mod write;
--- /dev/null
+use std::fs;
+
+use gccjit::OutputKind;
+use rustc_codegen_ssa::{CompiledModule, ModuleCodegen};
+use rustc_codegen_ssa::back::write::{CodegenContext, EmitObj, ModuleConfig};
+use rustc_errors::Handler;
+use rustc_session::config::OutputType;
+use rustc_span::fatal_error::FatalError;
+use rustc_target::spec::SplitDebuginfo;
+
+use crate::{GccCodegenBackend, GccContext};
+
+pub(crate) unsafe fn codegen(cgcx: &CodegenContext<GccCodegenBackend>, _diag_handler: &Handler, module: ModuleCodegen<GccContext>, config: &ModuleConfig) -> Result<CompiledModule, FatalError> {
+ let _timer = cgcx.prof.generic_activity_with_arg("LLVM_module_codegen", &module.name[..]);
+ {
+ let context = &module.module_llvm.context;
+
+ let module_name = module.name.clone();
+ let module_name = Some(&module_name[..]);
+
+ let _bc_out = cgcx.output_filenames.temp_path(OutputType::Bitcode, module_name);
+ let obj_out = cgcx.output_filenames.temp_path(OutputType::Object, module_name);
+
+ if config.bitcode_needed() {
+ // TODO(antoyo)
+ }
+
+ if config.emit_ir {
+ unimplemented!();
+ }
+
+ if config.emit_asm {
+ let _timer = cgcx
+ .prof
+ .generic_activity_with_arg("LLVM_module_codegen_emit_asm", &module.name[..]);
+ let path = cgcx.output_filenames.temp_path(OutputType::Assembly, module_name);
+ context.compile_to_file(OutputKind::Assembler, path.to_str().expect("path to str"));
+ }
+
+ match config.emit_obj {
+ EmitObj::ObjectCode(_) => {
+ let _timer = cgcx
+ .prof
+ .generic_activity_with_arg("LLVM_module_codegen_emit_obj", &module.name[..]);
+ match &*module.name {
+ "std_example.7rcbfp3g-cgu.15" => {
+ println!("Dumping reproducer {}", module.name);
+ let _ = fs::create_dir("/tmp/reproducers");
+ // FIXME(antoyo): segfault in dump_reproducer_to_file() might be caused by
+ // transmuting an rvalue to an lvalue.
+ // Segfault is actually in gcc::jit::reproducer::get_identifier_as_lvalue
+ context.dump_reproducer_to_file(&format!("/tmp/reproducers/{}.c", module.name));
+ println!("Dumped reproducer {}", module.name);
+ },
+ _ => (),
+ }
+ context.compile_to_file(OutputKind::ObjectFile, obj_out.to_str().expect("path to str"));
+ }
+
+ EmitObj::Bitcode => {
+ // TODO(antoyo)
+ }
+
+ EmitObj::None => {}
+ }
+ }
+
+ Ok(module.into_compiled_module(
+ config.emit_obj != EmitObj::None,
+ cgcx.target_can_use_split_dwarf && cgcx.split_debuginfo == SplitDebuginfo::Unpacked,
+ config.emit_bc,
+ &cgcx.output_filenames,
+ ))
+}
+
+pub(crate) fn link(_cgcx: &CodegenContext<GccCodegenBackend>, _diag_handler: &Handler, mut _modules: Vec<ModuleCodegen<GccContext>>) -> Result<ModuleCodegen<GccContext>, FatalError> {
+ unimplemented!();
+}
--- /dev/null
+use std::env;
+use std::time::Instant;
+
+use gccjit::{
+ Context,
+ FunctionType,
+ GlobalKind,
+};
+use rustc_middle::dep_graph;
+use rustc_middle::middle::exported_symbols;
+use rustc_middle::ty::TyCtxt;
+use rustc_middle::mir::mono::Linkage;
+use rustc_codegen_ssa::{ModuleCodegen, ModuleKind};
+use rustc_codegen_ssa::base::maybe_create_entry_wrapper;
+use rustc_codegen_ssa::mono_item::MonoItemExt;
+use rustc_codegen_ssa::traits::DebugInfoMethods;
+use rustc_metadata::EncodedMetadata;
+use rustc_session::config::DebugInfo;
+use rustc_span::Symbol;
+
+use crate::GccContext;
+use crate::builder::Builder;
+use crate::context::CodegenCx;
+
+pub fn global_linkage_to_gcc(linkage: Linkage) -> GlobalKind {
+ match linkage {
+ Linkage::External => GlobalKind::Imported,
+ Linkage::AvailableExternally => GlobalKind::Imported,
+ Linkage::LinkOnceAny => unimplemented!(),
+ Linkage::LinkOnceODR => unimplemented!(),
+ Linkage::WeakAny => unimplemented!(),
+ Linkage::WeakODR => unimplemented!(),
+ Linkage::Appending => unimplemented!(),
+ Linkage::Internal => GlobalKind::Internal,
+ Linkage::Private => GlobalKind::Internal,
+ Linkage::ExternalWeak => GlobalKind::Imported, // TODO(antoyo): should be weak linkage.
+ Linkage::Common => unimplemented!(),
+ }
+}
+
+pub fn linkage_to_gcc(linkage: Linkage) -> FunctionType {
+ match linkage {
+ Linkage::External => FunctionType::Exported,
+ Linkage::AvailableExternally => FunctionType::Extern,
+ Linkage::LinkOnceAny => unimplemented!(),
+ Linkage::LinkOnceODR => unimplemented!(),
+ Linkage::WeakAny => FunctionType::Exported, // FIXME(antoyo): should be similar to linkonce.
+ Linkage::WeakODR => unimplemented!(),
+ Linkage::Appending => unimplemented!(),
+ Linkage::Internal => FunctionType::Internal,
+ Linkage::Private => FunctionType::Internal,
+ Linkage::ExternalWeak => unimplemented!(),
+ Linkage::Common => unimplemented!(),
+ }
+}
+
+pub fn compile_codegen_unit<'tcx>(tcx: TyCtxt<'tcx>, cgu_name: Symbol) -> (ModuleCodegen<GccContext>, u64) {
+ let prof_timer = tcx.prof.generic_activity("codegen_module");
+ let start_time = Instant::now();
+
+ let dep_node = tcx.codegen_unit(cgu_name).codegen_dep_node(tcx);
+ let (module, _) = tcx.dep_graph.with_task(dep_node, tcx, cgu_name, module_codegen, dep_graph::hash_result);
+ let time_to_codegen = start_time.elapsed();
+ drop(prof_timer);
+
+ // We assume that the cost to run GCC on a CGU is proportional to
+ // the time we needed for codegenning it.
+ let cost = time_to_codegen.as_secs() * 1_000_000_000 + time_to_codegen.subsec_nanos() as u64;
+
+ fn module_codegen(tcx: TyCtxt<'_>, cgu_name: Symbol) -> ModuleCodegen<GccContext> {
+ let cgu = tcx.codegen_unit(cgu_name);
+ // Instantiate monomorphizations without filling out definitions yet...
+ //let llvm_module = ModuleLlvm::new(tcx, &cgu_name.as_str());
+ let context = Context::default();
+ // TODO(antoyo): only set on x86 platforms.
+ context.add_command_line_option("-masm=intel");
+ for arg in &tcx.sess.opts.cg.llvm_args {
+ context.add_command_line_option(arg);
+ }
+ context.add_command_line_option("-fno-semantic-interposition");
+ if env::var("CG_GCCJIT_DUMP_CODE").as_deref() == Ok("1") {
+ context.set_dump_code_on_compile(true);
+ }
+ if env::var("CG_GCCJIT_DUMP_GIMPLE").as_deref() == Ok("1") {
+ context.set_dump_initial_gimple(true);
+ }
+ context.set_debug_info(true);
+ if env::var("CG_GCCJIT_DUMP_EVERYTHING").as_deref() == Ok("1") {
+ context.set_dump_everything(true);
+ }
+ if env::var("CG_GCCJIT_KEEP_INTERMEDIATES").as_deref() == Ok("1") {
+ context.set_keep_intermediates(true);
+ }
+
+ {
+ let cx = CodegenCx::new(&context, cgu, tcx);
+
+ let mono_items = cgu.items_in_deterministic_order(tcx);
+ for &(mono_item, (linkage, visibility)) in &mono_items {
+ mono_item.predefine::<Builder<'_, '_, '_>>(&cx, linkage, visibility);
+ }
+
+ // ... and now that we have everything pre-defined, fill out those definitions.
+ for &(mono_item, _) in &mono_items {
+ mono_item.define::<Builder<'_, '_, '_>>(&cx);
+ }
+
+ // If this codegen unit contains the main function, also create the
+ // wrapper here
+ maybe_create_entry_wrapper::<Builder<'_, '_, '_>>(&cx);
+
+ // Finalize debuginfo
+ if cx.sess().opts.debuginfo != DebugInfo::None {
+ cx.debuginfo_finalize();
+ }
+ }
+
+ ModuleCodegen {
+ name: cgu_name.to_string(),
+ module_llvm: GccContext {
+ context
+ },
+ kind: ModuleKind::Regular,
+ }
+ }
+
+ (module, cost)
+}
+
+pub fn write_compressed_metadata<'tcx>(tcx: TyCtxt<'tcx>, metadata: &EncodedMetadata, gcc_module: &mut GccContext) {
+ use snap::write::FrameEncoder;
+ use std::io::Write;
+
+ // Historical note:
+ //
+ // When using link.exe it was seen that the section name `.note.rustc`
+ // was getting shortened to `.note.ru`, and according to the PE and COFF
+ // specification:
+ //
+ // > Executable images do not use a string table and do not support
+ // > section names longer than 8 characters
+ //
+ // https://docs.microsoft.com/en-us/windows/win32/debug/pe-format
+ //
+ // As a result, we choose a slightly shorter name! As to why
+ // `.note.rustc` works on MinGW, see
+ // https://github.com/llvm/llvm-project/blob/llvmorg-12.0.0/lld/COFF/Writer.cpp#L1190-L1197
+ let section_name = if tcx.sess.target.is_like_osx { "__DATA,.rustc" } else { ".rustc" };
+
+ let context = &gcc_module.context;
+ let mut compressed = rustc_metadata::METADATA_HEADER.to_vec();
+ FrameEncoder::new(&mut compressed).write_all(&metadata.raw_data()).unwrap();
+
+ let name = exported_symbols::metadata_symbol_name(tcx);
+ let typ = context.new_array_type(None, context.new_type::<u8>(), compressed.len() as i32);
+ let global = context.new_global(None, GlobalKind::Exported, typ, name);
+ global.global_set_initializer(&compressed);
+ global.set_link_section(section_name);
+
+ // Also generate a .section directive to force no
+ // flags, at least for ELF outputs, so that the
+ // metadata doesn't get loaded into memory.
+ let directive = format!(".section {}", section_name);
+ context.add_top_level_asm(None, &directive);
+}
--- /dev/null
+use std::borrow::Cow;
+use std::cell::Cell;
+use std::convert::TryFrom;
+use std::ops::Deref;
+
+use gccjit::FunctionType;
+use gccjit::{
+ BinaryOp,
+ Block,
+ ComparisonOp,
+ Function,
+ LValue,
+ RValue,
+ ToRValue,
+ Type,
+ UnaryOp,
+};
+use rustc_codegen_ssa::MemFlags;
+use rustc_codegen_ssa::common::{AtomicOrdering, AtomicRmwBinOp, IntPredicate, RealPredicate, SynchronizationScope};
+use rustc_codegen_ssa::mir::operand::{OperandRef, OperandValue};
+use rustc_codegen_ssa::mir::place::PlaceRef;
+use rustc_codegen_ssa::traits::{
+ BackendTypes,
+ BaseTypeMethods,
+ BuilderMethods,
+ ConstMethods,
+ DerivedTypeMethods,
+ LayoutTypeMethods,
+ HasCodegen,
+ OverflowOp,
+ StaticBuilderMethods,
+};
+use rustc_middle::ty::{ParamEnv, Ty, TyCtxt};
+use rustc_middle::ty::layout::{FnAbiError, FnAbiOfHelpers, FnAbiRequest, HasParamEnv, HasTyCtxt, LayoutError, LayoutOfHelpers, TyAndLayout};
+use rustc_span::Span;
+use rustc_span::def_id::DefId;
+use rustc_target::abi::{
+ self,
+ call::FnAbi,
+ Align,
+ HasDataLayout,
+ Size,
+ TargetDataLayout,
+ WrappingRange,
+};
+use rustc_target::spec::{HasTargetSpec, Target};
+
+use crate::common::{SignType, TypeReflection, type_is_pointer};
+use crate::context::CodegenCx;
+use crate::type_of::LayoutGccExt;
+
+// TODO(antoyo)
+type Funclet = ();
+
+// TODO(antoyo): remove this variable.
+static mut RETURN_VALUE_COUNT: usize = 0;
+
+enum ExtremumOperation {
+ Max,
+ Min,
+}
+
+trait EnumClone {
+ fn clone(&self) -> Self;
+}
+
+impl EnumClone for AtomicOrdering {
+ fn clone(&self) -> Self {
+ match *self {
+ AtomicOrdering::NotAtomic => AtomicOrdering::NotAtomic,
+ AtomicOrdering::Unordered => AtomicOrdering::Unordered,
+ AtomicOrdering::Monotonic => AtomicOrdering::Monotonic,
+ AtomicOrdering::Acquire => AtomicOrdering::Acquire,
+ AtomicOrdering::Release => AtomicOrdering::Release,
+ AtomicOrdering::AcquireRelease => AtomicOrdering::AcquireRelease,
+ AtomicOrdering::SequentiallyConsistent => AtomicOrdering::SequentiallyConsistent,
+ }
+ }
+}
+
+pub struct Builder<'a: 'gcc, 'gcc, 'tcx> {
+ pub cx: &'a CodegenCx<'gcc, 'tcx>,
+ pub block: Option<Block<'gcc>>,
+ stack_var_count: Cell<usize>,
+}
+
+impl<'a, 'gcc, 'tcx> Builder<'a, 'gcc, 'tcx> {
+ fn with_cx(cx: &'a CodegenCx<'gcc, 'tcx>) -> Self {
+ Builder {
+ cx,
+ block: None,
+ stack_var_count: Cell::new(0),
+ }
+ }
+
+ fn atomic_extremum(&mut self, operation: ExtremumOperation, dst: RValue<'gcc>, src: RValue<'gcc>, order: AtomicOrdering) -> RValue<'gcc> {
+ let size = self.cx.int_width(src.get_type()) / 8;
+
+ let func = self.current_func();
+
+ let load_ordering =
+ match order {
+ // TODO(antoyo): does this make sense?
+ AtomicOrdering::AcquireRelease | AtomicOrdering::Release => AtomicOrdering::Acquire,
+ _ => order.clone(),
+ };
+ let previous_value = self.atomic_load(dst.get_type(), dst, load_ordering.clone(), Size::from_bytes(size));
+ let previous_var = func.new_local(None, previous_value.get_type(), "previous_value");
+ let return_value = func.new_local(None, previous_value.get_type(), "return_value");
+ self.llbb().add_assignment(None, previous_var, previous_value);
+ self.llbb().add_assignment(None, return_value, previous_var.to_rvalue());
+
+ let while_block = func.new_block("while");
+ let after_block = func.new_block("after_while");
+ self.llbb().end_with_jump(None, while_block);
+
+ // NOTE: since jumps were added and compare_exchange doesn't expect this, the current blocks in the
+ // state need to be updated.
+ self.block = Some(while_block);
+ *self.cx.current_block.borrow_mut() = Some(while_block);
+
+ let comparison_operator =
+ match operation {
+ ExtremumOperation::Max => ComparisonOp::LessThan,
+ ExtremumOperation::Min => ComparisonOp::GreaterThan,
+ };
+
+ let cond1 = self.context.new_comparison(None, comparison_operator, previous_var.to_rvalue(), self.context.new_cast(None, src, previous_value.get_type()));
+ let compare_exchange = self.compare_exchange(dst, previous_var, src, order, load_ordering, false);
+ let cond2 = self.cx.context.new_unary_op(None, UnaryOp::LogicalNegate, compare_exchange.get_type(), compare_exchange);
+ let cond = self.cx.context.new_binary_op(None, BinaryOp::LogicalAnd, self.cx.bool_type, cond1, cond2);
+
+ while_block.end_with_conditional(None, cond, while_block, after_block);
+
+ // NOTE: since jumps were added in a place rustc does not expect, the current blocks in the
+ // state need to be updated.
+ self.block = Some(after_block);
+ *self.cx.current_block.borrow_mut() = Some(after_block);
+
+ return_value.to_rvalue()
+ }
+
+ fn compare_exchange(&self, dst: RValue<'gcc>, cmp: LValue<'gcc>, src: RValue<'gcc>, order: AtomicOrdering, failure_order: AtomicOrdering, weak: bool) -> RValue<'gcc> {
+ let size = self.cx.int_width(src.get_type());
+ let compare_exchange = self.context.get_builtin_function(&format!("__atomic_compare_exchange_{}", size / 8));
+ let order = self.context.new_rvalue_from_int(self.i32_type, order.to_gcc());
+ let failure_order = self.context.new_rvalue_from_int(self.i32_type, failure_order.to_gcc());
+ let weak = self.context.new_rvalue_from_int(self.bool_type, weak as i32);
+
+ let void_ptr_type = self.context.new_type::<*mut ()>();
+ let volatile_void_ptr_type = void_ptr_type.make_volatile();
+ let dst = self.context.new_cast(None, dst, volatile_void_ptr_type);
+ let expected = self.context.new_cast(None, cmp.get_address(None), void_ptr_type);
+
+ // NOTE: not sure why, but we have the wrong type here.
+ let int_type = compare_exchange.get_param(2).to_rvalue().get_type();
+ let src = self.context.new_cast(None, src, int_type);
+ self.context.new_call(None, compare_exchange, &[dst, expected, src, weak, order, failure_order])
+ }
+
+ pub fn assign(&self, lvalue: LValue<'gcc>, value: RValue<'gcc>) {
+ self.llbb().add_assignment(None, lvalue, value);
+ }
+
+ fn check_call<'b>(&mut self, _typ: &str, func: Function<'gcc>, args: &'b [RValue<'gcc>]) -> Cow<'b, [RValue<'gcc>]> {
+ let mut all_args_match = true;
+ let mut param_types = vec![];
+ let param_count = func.get_param_count();
+ for (index, arg) in args.iter().enumerate().take(param_count) {
+ let param = func.get_param(index as i32);
+ let param = param.to_rvalue().get_type();
+ if param != arg.get_type() {
+ all_args_match = false;
+ }
+ param_types.push(param);
+ }
+
+ if all_args_match {
+ return Cow::Borrowed(args);
+ }
+
+ let casted_args: Vec<_> = param_types
+ .into_iter()
+ .zip(args.iter())
+ .enumerate()
+ .map(|(_i, (expected_ty, &actual_val))| {
+ let actual_ty = actual_val.get_type();
+ if expected_ty != actual_ty {
+ self.bitcast(actual_val, expected_ty)
+ }
+ else {
+ actual_val
+ }
+ })
+ .collect();
+
+ Cow::Owned(casted_args)
+ }
+
+ fn check_ptr_call<'b>(&mut self, _typ: &str, func_ptr: RValue<'gcc>, args: &'b [RValue<'gcc>]) -> Cow<'b, [RValue<'gcc>]> {
+ let mut all_args_match = true;
+ let mut param_types = vec![];
+ let gcc_func = func_ptr.get_type().is_function_ptr_type().expect("function ptr");
+ for (index, arg) in args.iter().enumerate().take(gcc_func.get_param_count()) {
+ let param = gcc_func.get_param_type(index);
+ if param != arg.get_type() {
+ all_args_match = false;
+ }
+ param_types.push(param);
+ }
+
+ if all_args_match {
+ return Cow::Borrowed(args);
+ }
+
+ let casted_args: Vec<_> = param_types
+ .into_iter()
+ .zip(args.iter())
+ .enumerate()
+ .map(|(_i, (expected_ty, &actual_val))| {
+ let actual_ty = actual_val.get_type();
+ if expected_ty != actual_ty {
+ self.bitcast(actual_val, expected_ty)
+ }
+ else {
+ actual_val
+ }
+ })
+ .collect();
+
+ Cow::Owned(casted_args)
+ }
+
+ fn check_store(&mut self, val: RValue<'gcc>, ptr: RValue<'gcc>) -> RValue<'gcc> {
+ let dest_ptr_ty = self.cx.val_ty(ptr).make_pointer(); // TODO(antoyo): make sure make_pointer() is okay here.
+ let stored_ty = self.cx.val_ty(val);
+ let stored_ptr_ty = self.cx.type_ptr_to(stored_ty);
+
+ if dest_ptr_ty == stored_ptr_ty {
+ ptr
+ }
+ else {
+ self.bitcast(ptr, stored_ptr_ty)
+ }
+ }
+
+ pub fn current_func(&self) -> Function<'gcc> {
+ self.block.expect("block").get_function()
+ }
+
+ fn function_call(&mut self, func: RValue<'gcc>, args: &[RValue<'gcc>], _funclet: Option<&Funclet>) -> RValue<'gcc> {
+ // TODO(antoyo): remove when the API supports a different type for functions.
+ let func: Function<'gcc> = self.cx.rvalue_as_function(func);
+ let args = self.check_call("call", func, args);
+
+ // gccjit requires to use the result of functions, even when it's not used.
+ // That's why we assign the result to a local or call add_eval().
+ let return_type = func.get_return_type();
+ let current_block = self.current_block.borrow().expect("block");
+ let void_type = self.context.new_type::<()>();
+ let current_func = current_block.get_function();
+ if return_type != void_type {
+ unsafe { RETURN_VALUE_COUNT += 1 };
+ let result = current_func.new_local(None, return_type, &format!("returnValue{}", unsafe { RETURN_VALUE_COUNT }));
+ current_block.add_assignment(None, result, self.cx.context.new_call(None, func, &args));
+ result.to_rvalue()
+ }
+ else {
+ current_block.add_eval(None, self.cx.context.new_call(None, func, &args));
+ // Return dummy value when not having return value.
+ self.context.new_rvalue_from_long(self.isize_type, 0)
+ }
+ }
+
+ fn function_ptr_call(&mut self, func_ptr: RValue<'gcc>, args: &[RValue<'gcc>], _funclet: Option<&Funclet>) -> RValue<'gcc> {
+ let args = self.check_ptr_call("call", func_ptr, args);
+
+ // gccjit requires to use the result of functions, even when it's not used.
+ // That's why we assign the result to a local or call add_eval().
+ let gcc_func = func_ptr.get_type().is_function_ptr_type().expect("function ptr");
+ let mut return_type = gcc_func.get_return_type();
+ let current_block = self.current_block.borrow().expect("block");
+ let void_type = self.context.new_type::<()>();
+ let current_func = current_block.get_function();
+
+ // FIXME(antoyo): As a temporary workaround for unsupported LLVM intrinsics.
+ if gcc_func.get_param_count() == 0 && format!("{:?}", func_ptr) == "__builtin_ia32_pmovmskb128" {
+ return_type = self.int_type;
+ }
+
+ if return_type != void_type {
+ unsafe { RETURN_VALUE_COUNT += 1 };
+ let result = current_func.new_local(None, return_type, &format!("returnValue{}", unsafe { RETURN_VALUE_COUNT }));
+ current_block.add_assignment(None, result, self.cx.context.new_call_through_ptr(None, func_ptr, &args));
+ result.to_rvalue()
+ }
+ else {
+ if gcc_func.get_param_count() == 0 {
+ // FIXME(antoyo): As a temporary workaround for unsupported LLVM intrinsics.
+ current_block.add_eval(None, self.cx.context.new_call_through_ptr(None, func_ptr, &[]));
+ }
+ else {
+ current_block.add_eval(None, self.cx.context.new_call_through_ptr(None, func_ptr, &args));
+ }
+ // Return dummy value when not having return value.
+ let result = current_func.new_local(None, self.isize_type, "dummyValueThatShouldNeverBeUsed");
+ current_block.add_assignment(None, result, self.context.new_rvalue_from_long(self.isize_type, 0));
+ result.to_rvalue()
+ }
+ }
+
+ pub fn overflow_call(&mut self, func: Function<'gcc>, args: &[RValue<'gcc>], _funclet: Option<&Funclet>) -> RValue<'gcc> {
+ // gccjit requires to use the result of functions, even when it's not used.
+ // That's why we assign the result to a local.
+ let return_type = self.context.new_type::<bool>();
+ let current_block = self.current_block.borrow().expect("block");
+ let current_func = current_block.get_function();
+ // TODO(antoyo): return the new_call() directly? Since the overflow function has no side-effects.
+ unsafe { RETURN_VALUE_COUNT += 1 };
+ let result = current_func.new_local(None, return_type, &format!("returnValue{}", unsafe { RETURN_VALUE_COUNT }));
+ current_block.add_assignment(None, result, self.cx.context.new_call(None, func, &args));
+ result.to_rvalue()
+ }
+}
+
+impl<'gcc, 'tcx> HasCodegen<'tcx> for Builder<'_, 'gcc, 'tcx> {
+ type CodegenCx = CodegenCx<'gcc, 'tcx>;
+}
+
+impl<'tcx> HasTyCtxt<'tcx> for Builder<'_, '_, 'tcx> {
+ fn tcx(&self) -> TyCtxt<'tcx> {
+ self.cx.tcx()
+ }
+}
+
+impl HasDataLayout for Builder<'_, '_, '_> {
+ fn data_layout(&self) -> &TargetDataLayout {
+ self.cx.data_layout()
+ }
+}
+
+impl<'tcx> LayoutOfHelpers<'tcx> for Builder<'_, '_, 'tcx> {
+ type LayoutOfResult = TyAndLayout<'tcx>;
+
+ #[inline]
+ fn handle_layout_err(&self, err: LayoutError<'tcx>, span: Span, ty: Ty<'tcx>) -> ! {
+ self.cx.handle_layout_err(err, span, ty)
+ }
+}
+
+impl<'tcx> FnAbiOfHelpers<'tcx> for Builder<'_, '_, 'tcx> {
+ type FnAbiOfResult = &'tcx FnAbi<'tcx, Ty<'tcx>>;
+
+ #[inline]
+ fn handle_fn_abi_err(
+ &self,
+ err: FnAbiError<'tcx>,
+ span: Span,
+ fn_abi_request: FnAbiRequest<'tcx>,
+ ) -> ! {
+ self.cx.handle_fn_abi_err(err, span, fn_abi_request)
+ }
+}
+
+impl<'gcc, 'tcx> Deref for Builder<'_, 'gcc, 'tcx> {
+ type Target = CodegenCx<'gcc, 'tcx>;
+
+ fn deref(&self) -> &Self::Target {
+ self.cx
+ }
+}
+
+impl<'gcc, 'tcx> BackendTypes for Builder<'_, 'gcc, 'tcx> {
+ type Value = <CodegenCx<'gcc, 'tcx> as BackendTypes>::Value;
+ type Function = <CodegenCx<'gcc, 'tcx> as BackendTypes>::Function;
+ type BasicBlock = <CodegenCx<'gcc, 'tcx> as BackendTypes>::BasicBlock;
+ type Type = <CodegenCx<'gcc, 'tcx> as BackendTypes>::Type;
+ type Funclet = <CodegenCx<'gcc, 'tcx> as BackendTypes>::Funclet;
+
+ type DIScope = <CodegenCx<'gcc, 'tcx> as BackendTypes>::DIScope;
+ type DILocation = <CodegenCx<'gcc, 'tcx> as BackendTypes>::DILocation;
+ type DIVariable = <CodegenCx<'gcc, 'tcx> as BackendTypes>::DIVariable;
+}
+
+impl<'a, 'gcc, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'gcc, 'tcx> {
+ fn build(cx: &'a CodegenCx<'gcc, 'tcx>, block: Block<'gcc>) -> Self {
+ let mut bx = Builder::with_cx(cx);
+ *cx.current_block.borrow_mut() = Some(block);
+ bx.block = Some(block);
+ bx
+ }
+
+ fn build_sibling_block(&mut self, name: &str) -> Self {
+ let block = self.append_sibling_block(name);
+ Self::build(self.cx, block)
+ }
+
+ fn llbb(&self) -> Block<'gcc> {
+ self.block.expect("block")
+ }
+
+ fn append_block(cx: &'a CodegenCx<'gcc, 'tcx>, func: RValue<'gcc>, name: &str) -> Block<'gcc> {
+ let func = cx.rvalue_as_function(func);
+ func.new_block(name)
+ }
+
+ fn append_sibling_block(&mut self, name: &str) -> Block<'gcc> {
+ let func = self.current_func();
+ func.new_block(name)
+ }
+
+ fn ret_void(&mut self) {
+ self.llbb().end_with_void_return(None)
+ }
+
+ fn ret(&mut self, value: RValue<'gcc>) {
+ let value =
+ if self.structs_as_pointer.borrow().contains(&value) {
+ // NOTE: hack to workaround a limitation of the rustc API: see comment on
+ // CodegenCx.structs_as_pointer
+ value.dereference(None).to_rvalue()
+ }
+ else {
+ value
+ };
+ self.llbb().end_with_return(None, value);
+ }
+
+ fn br(&mut self, dest: Block<'gcc>) {
+ self.llbb().end_with_jump(None, dest)
+ }
+
+ fn cond_br(&mut self, cond: RValue<'gcc>, then_block: Block<'gcc>, else_block: Block<'gcc>) {
+ self.llbb().end_with_conditional(None, cond, then_block, else_block)
+ }
+
+ fn switch(&mut self, value: RValue<'gcc>, default_block: Block<'gcc>, cases: impl ExactSizeIterator<Item = (u128, Block<'gcc>)>) {
+ let mut gcc_cases = vec![];
+ let typ = self.val_ty(value);
+ for (on_val, dest) in cases {
+ let on_val = self.const_uint_big(typ, on_val);
+ gcc_cases.push(self.context.new_case(on_val, on_val, dest));
+ }
+ self.block.expect("block").end_with_switch(None, value, default_block, &gcc_cases);
+ }
+
+ fn invoke(&mut self, _typ: Type<'gcc>, _func: RValue<'gcc>, _args: &[RValue<'gcc>], then: Block<'gcc>, catch: Block<'gcc>, _funclet: Option<&Funclet>) -> RValue<'gcc> {
+ let condition = self.context.new_rvalue_from_int(self.bool_type, 0);
+ self.llbb().end_with_conditional(None, condition, then, catch);
+ self.context.new_rvalue_from_int(self.int_type, 0)
+
+ // TODO(antoyo)
+ }
+
+ fn unreachable(&mut self) {
+ let func = self.context.get_builtin_function("__builtin_unreachable");
+ let block = self.block.expect("block");
+ block.add_eval(None, self.context.new_call(None, func, &[]));
+ let return_type = block.get_function().get_return_type();
+ let void_type = self.context.new_type::<()>();
+ if return_type == void_type {
+ block.end_with_void_return(None)
+ }
+ else {
+ let return_value = self.current_func()
+ .new_local(None, return_type, "unreachableReturn");
+ block.end_with_return(None, return_value)
+ }
+ }
+
+ fn add(&mut self, a: RValue<'gcc>, mut b: RValue<'gcc>) -> RValue<'gcc> {
+ // FIXME(antoyo): this should not be required.
+ if format!("{:?}", a.get_type()) != format!("{:?}", b.get_type()) {
+ b = self.context.new_cast(None, b, a.get_type());
+ }
+ a + b
+ }
+
+ fn fadd(&mut self, a: RValue<'gcc>, b: RValue<'gcc>) -> RValue<'gcc> {
+ a + b
+ }
+
+ fn sub(&mut self, a: RValue<'gcc>, mut b: RValue<'gcc>) -> RValue<'gcc> {
+ if a.get_type() != b.get_type() {
+ b = self.context.new_cast(None, b, a.get_type());
+ }
+ a - b
+ }
+
+ fn fsub(&mut self, a: RValue<'gcc>, b: RValue<'gcc>) -> RValue<'gcc> {
+ a - b
+ }
+
+ fn mul(&mut self, a: RValue<'gcc>, b: RValue<'gcc>) -> RValue<'gcc> {
+ a * b
+ }
+
+ fn fmul(&mut self, a: RValue<'gcc>, b: RValue<'gcc>) -> RValue<'gcc> {
+ a * b
+ }
+
+ fn udiv(&mut self, a: RValue<'gcc>, b: RValue<'gcc>) -> RValue<'gcc> {
+ // TODO(antoyo): convert the arguments to unsigned?
+ a / b
+ }
+
+ fn exactudiv(&mut self, a: RValue<'gcc>, b: RValue<'gcc>) -> RValue<'gcc> {
+ // TODO(antoyo): convert the arguments to unsigned?
+ // TODO(antoyo): poison if not exact.
+ a / b
+ }
+
+ fn sdiv(&mut self, a: RValue<'gcc>, b: RValue<'gcc>) -> RValue<'gcc> {
+ // TODO(antoyo): convert the arguments to signed?
+ a / b
+ }
+
+ fn exactsdiv(&mut self, a: RValue<'gcc>, b: RValue<'gcc>) -> RValue<'gcc> {
+ // TODO(antoyo): posion if not exact.
+ // FIXME(antoyo): rustc_codegen_ssa::mir::intrinsic uses different types for a and b but they
+ // should be the same.
+ let typ = a.get_type().to_signed(self);
+ let b = self.context.new_cast(None, b, typ);
+ a / b
+ }
+
+ fn fdiv(&mut self, a: RValue<'gcc>, b: RValue<'gcc>) -> RValue<'gcc> {
+ a / b
+ }
+
+ fn urem(&mut self, a: RValue<'gcc>, b: RValue<'gcc>) -> RValue<'gcc> {
+ a % b
+ }
+
+ fn srem(&mut self, a: RValue<'gcc>, b: RValue<'gcc>) -> RValue<'gcc> {
+ a % b
+ }
+
+ fn frem(&mut self, a: RValue<'gcc>, b: RValue<'gcc>) -> RValue<'gcc> {
+ if a.get_type() == self.cx.float_type {
+ let fmodf = self.context.get_builtin_function("fmodf");
+ // FIXME(antoyo): this seems to produce the wrong result.
+ return self.context.new_call(None, fmodf, &[a, b]);
+ }
+ assert_eq!(a.get_type(), self.cx.double_type);
+
+ let fmod = self.context.get_builtin_function("fmod");
+ return self.context.new_call(None, fmod, &[a, b]);
+ }
+
+ fn shl(&mut self, a: RValue<'gcc>, b: RValue<'gcc>) -> RValue<'gcc> {
+ // FIXME(antoyo): remove the casts when libgccjit can shift an unsigned number by an unsigned number.
+ let a_type = a.get_type();
+ let b_type = b.get_type();
+ if a_type.is_unsigned(self) && b_type.is_signed(self) {
+ let a = self.context.new_cast(None, a, b_type);
+ let result = a << b;
+ self.context.new_cast(None, result, a_type)
+ }
+ else if a_type.is_signed(self) && b_type.is_unsigned(self) {
+ let b = self.context.new_cast(None, b, a_type);
+ a << b
+ }
+ else {
+ a << b
+ }
+ }
+
+ fn lshr(&mut self, a: RValue<'gcc>, b: RValue<'gcc>) -> RValue<'gcc> {
+ // FIXME(antoyo): remove the casts when libgccjit can shift an unsigned number by an unsigned number.
+ // TODO(antoyo): cast to unsigned to do a logical shift if that does not work.
+ let a_type = a.get_type();
+ let b_type = b.get_type();
+ if a_type.is_unsigned(self) && b_type.is_signed(self) {
+ let a = self.context.new_cast(None, a, b_type);
+ let result = a >> b;
+ self.context.new_cast(None, result, a_type)
+ }
+ else if a_type.is_signed(self) && b_type.is_unsigned(self) {
+ let b = self.context.new_cast(None, b, a_type);
+ a >> b
+ }
+ else {
+ a >> b
+ }
+ }
+
+ fn ashr(&mut self, a: RValue<'gcc>, b: RValue<'gcc>) -> RValue<'gcc> {
+ // TODO(antoyo): check whether behavior is an arithmetic shift for >> .
+ // FIXME(antoyo): remove the casts when libgccjit can shift an unsigned number by an unsigned number.
+ let a_type = a.get_type();
+ let b_type = b.get_type();
+ if a_type.is_unsigned(self) && b_type.is_signed(self) {
+ let a = self.context.new_cast(None, a, b_type);
+ let result = a >> b;
+ self.context.new_cast(None, result, a_type)
+ }
+ else if a_type.is_signed(self) && b_type.is_unsigned(self) {
+ let b = self.context.new_cast(None, b, a_type);
+ a >> b
+ }
+ else {
+ a >> b
+ }
+ }
+
+ fn and(&mut self, a: RValue<'gcc>, mut b: RValue<'gcc>) -> RValue<'gcc> {
+ // FIXME(antoyo): hack by putting the result in a variable to workaround this bug:
+ // https://gcc.gnu.org/bugzilla//show_bug.cgi?id=95498
+ if a.get_type() != b.get_type() {
+ b = self.context.new_cast(None, b, a.get_type());
+ }
+ let res = self.current_func().new_local(None, b.get_type(), "andResult");
+ self.llbb().add_assignment(None, res, a & b);
+ res.to_rvalue()
+ }
+
+ fn or(&mut self, a: RValue<'gcc>, b: RValue<'gcc>) -> RValue<'gcc> {
+ // FIXME(antoyo): hack by putting the result in a variable to workaround this bug:
+ // https://gcc.gnu.org/bugzilla//show_bug.cgi?id=95498
+ let res = self.current_func().new_local(None, b.get_type(), "orResult");
+ self.llbb().add_assignment(None, res, a | b);
+ res.to_rvalue()
+ }
+
+ fn xor(&mut self, a: RValue<'gcc>, b: RValue<'gcc>) -> RValue<'gcc> {
+ a ^ b
+ }
+
+ fn neg(&mut self, a: RValue<'gcc>) -> RValue<'gcc> {
+ // TODO(antoyo): use new_unary_op()?
+ self.cx.context.new_rvalue_from_long(a.get_type(), 0) - a
+ }
+
+ fn fneg(&mut self, a: RValue<'gcc>) -> RValue<'gcc> {
+ self.cx.context.new_unary_op(None, UnaryOp::Minus, a.get_type(), a)
+ }
+
+ fn not(&mut self, a: RValue<'gcc>) -> RValue<'gcc> {
+ let operation =
+ if a.get_type().is_bool() {
+ UnaryOp::LogicalNegate
+ }
+ else {
+ UnaryOp::BitwiseNegate
+ };
+ self.cx.context.new_unary_op(None, operation, a.get_type(), a)
+ }
+
+ fn unchecked_sadd(&mut self, a: RValue<'gcc>, b: RValue<'gcc>) -> RValue<'gcc> {
+ a + b
+ }
+
+ fn unchecked_uadd(&mut self, a: RValue<'gcc>, b: RValue<'gcc>) -> RValue<'gcc> {
+ a + b
+ }
+
+ fn unchecked_ssub(&mut self, a: RValue<'gcc>, b: RValue<'gcc>) -> RValue<'gcc> {
+ a - b
+ }
+
+ fn unchecked_usub(&mut self, a: RValue<'gcc>, b: RValue<'gcc>) -> RValue<'gcc> {
+ // TODO(antoyo): should generate poison value?
+ a - b
+ }
+
+ fn unchecked_smul(&mut self, a: RValue<'gcc>, b: RValue<'gcc>) -> RValue<'gcc> {
+ a * b
+ }
+
+ fn unchecked_umul(&mut self, a: RValue<'gcc>, b: RValue<'gcc>) -> RValue<'gcc> {
+ a * b
+ }
+
+ fn fadd_fast(&mut self, _lhs: RValue<'gcc>, _rhs: RValue<'gcc>) -> RValue<'gcc> {
+ unimplemented!();
+ }
+
+ fn fsub_fast(&mut self, _lhs: RValue<'gcc>, _rhs: RValue<'gcc>) -> RValue<'gcc> {
+ unimplemented!();
+ }
+
+ fn fmul_fast(&mut self, _lhs: RValue<'gcc>, _rhs: RValue<'gcc>) -> RValue<'gcc> {
+ unimplemented!();
+ }
+
+ fn fdiv_fast(&mut self, _lhs: RValue<'gcc>, _rhs: RValue<'gcc>) -> RValue<'gcc> {
+ unimplemented!();
+ }
+
+ fn frem_fast(&mut self, _lhs: RValue<'gcc>, _rhs: RValue<'gcc>) -> RValue<'gcc> {
+ unimplemented!();
+ }
+
+ fn checked_binop(&mut self, oop: OverflowOp, typ: Ty<'_>, lhs: Self::Value, rhs: Self::Value) -> (Self::Value, Self::Value) {
+ use rustc_middle::ty::{Int, IntTy::*, Uint, UintTy::*};
+
+ let new_kind =
+ match typ.kind() {
+ Int(t @ Isize) => Int(t.normalize(self.tcx.sess.target.pointer_width)),
+ Uint(t @ Usize) => Uint(t.normalize(self.tcx.sess.target.pointer_width)),
+ t @ (Uint(_) | Int(_)) => t.clone(),
+ _ => panic!("tried to get overflow intrinsic for op applied to non-int type"),
+ };
+
+ // TODO(antoyo): remove duplication with intrinsic?
+ let name =
+ match oop {
+ OverflowOp::Add =>
+ match new_kind {
+ Int(I8) => "__builtin_add_overflow",
+ Int(I16) => "__builtin_add_overflow",
+ Int(I32) => "__builtin_sadd_overflow",
+ Int(I64) => "__builtin_saddll_overflow",
+ Int(I128) => "__builtin_add_overflow",
+
+ Uint(U8) => "__builtin_add_overflow",
+ Uint(U16) => "__builtin_add_overflow",
+ Uint(U32) => "__builtin_uadd_overflow",
+ Uint(U64) => "__builtin_uaddll_overflow",
+ Uint(U128) => "__builtin_add_overflow",
+
+ _ => unreachable!(),
+ },
+ OverflowOp::Sub =>
+ match new_kind {
+ Int(I8) => "__builtin_sub_overflow",
+ Int(I16) => "__builtin_sub_overflow",
+ Int(I32) => "__builtin_ssub_overflow",
+ Int(I64) => "__builtin_ssubll_overflow",
+ Int(I128) => "__builtin_sub_overflow",
+
+ Uint(U8) => "__builtin_sub_overflow",
+ Uint(U16) => "__builtin_sub_overflow",
+ Uint(U32) => "__builtin_usub_overflow",
+ Uint(U64) => "__builtin_usubll_overflow",
+ Uint(U128) => "__builtin_sub_overflow",
+
+ _ => unreachable!(),
+ },
+ OverflowOp::Mul =>
+ match new_kind {
+ Int(I8) => "__builtin_mul_overflow",
+ Int(I16) => "__builtin_mul_overflow",
+ Int(I32) => "__builtin_smul_overflow",
+ Int(I64) => "__builtin_smulll_overflow",
+ Int(I128) => "__builtin_mul_overflow",
+
+ Uint(U8) => "__builtin_mul_overflow",
+ Uint(U16) => "__builtin_mul_overflow",
+ Uint(U32) => "__builtin_umul_overflow",
+ Uint(U64) => "__builtin_umulll_overflow",
+ Uint(U128) => "__builtin_mul_overflow",
+
+ _ => unreachable!(),
+ },
+ };
+
+ let intrinsic = self.context.get_builtin_function(&name);
+ let res = self.current_func()
+ // TODO(antoyo): is it correct to use rhs type instead of the parameter typ?
+ .new_local(None, rhs.get_type(), "binopResult")
+ .get_address(None);
+ let overflow = self.overflow_call(intrinsic, &[lhs, rhs, res], None);
+ (res.dereference(None).to_rvalue(), overflow)
+ }
+
+ fn alloca(&mut self, ty: Type<'gcc>, align: Align) -> RValue<'gcc> {
+ // FIXME(antoyo): this check that we don't call get_aligned() a second time on a type.
+ // Ideally, we shouldn't need to do this check.
+ let aligned_type =
+ if ty == self.cx.u128_type || ty == self.cx.i128_type {
+ ty
+ }
+ else {
+ ty.get_aligned(align.bytes())
+ };
+ // TODO(antoyo): It might be better to return a LValue, but fixing the rustc API is non-trivial.
+ self.stack_var_count.set(self.stack_var_count.get() + 1);
+ self.current_func().new_local(None, aligned_type, &format!("stack_var_{}", self.stack_var_count.get())).get_address(None)
+ }
+
+ fn dynamic_alloca(&mut self, _ty: Type<'gcc>, _align: Align) -> RValue<'gcc> {
+ unimplemented!();
+ }
+
+ fn array_alloca(&mut self, _ty: Type<'gcc>, _len: RValue<'gcc>, _align: Align) -> RValue<'gcc> {
+ unimplemented!();
+ }
+
+ fn load(&mut self, _ty: Type<'gcc>, ptr: RValue<'gcc>, _align: Align) -> RValue<'gcc> {
+ // TODO(antoyo): use ty.
+ let block = self.llbb();
+ let function = block.get_function();
+ // NOTE: instead of returning the dereference here, we have to assign it to a variable in
+ // the current basic block. Otherwise, it could be used in another basic block, causing a
+ // dereference after a drop, for instance.
+ // TODO(antoyo): handle align.
+ let deref = ptr.dereference(None).to_rvalue();
+ let value_type = deref.get_type();
+ unsafe { RETURN_VALUE_COUNT += 1 };
+ let loaded_value = function.new_local(None, value_type, &format!("loadedValue{}", unsafe { RETURN_VALUE_COUNT }));
+ block.add_assignment(None, loaded_value, deref);
+ loaded_value.to_rvalue()
+ }
+
+ fn volatile_load(&mut self, _ty: Type<'gcc>, ptr: RValue<'gcc>) -> RValue<'gcc> {
+ // TODO(antoyo): use ty.
+ let ptr = self.context.new_cast(None, ptr, ptr.get_type().make_volatile());
+ ptr.dereference(None).to_rvalue()
+ }
+
+ fn atomic_load(&mut self, _ty: Type<'gcc>, ptr: RValue<'gcc>, order: AtomicOrdering, size: Size) -> RValue<'gcc> {
+ // TODO(antoyo): use ty.
+ // TODO(antoyo): handle alignment.
+ let atomic_load = self.context.get_builtin_function(&format!("__atomic_load_{}", size.bytes()));
+ let ordering = self.context.new_rvalue_from_int(self.i32_type, order.to_gcc());
+
+ let volatile_const_void_ptr_type = self.context.new_type::<*mut ()>().make_const().make_volatile();
+ let ptr = self.context.new_cast(None, ptr, volatile_const_void_ptr_type);
+ self.context.new_call(None, atomic_load, &[ptr, ordering])
+ }
+
+ fn load_operand(&mut self, place: PlaceRef<'tcx, RValue<'gcc>>) -> OperandRef<'tcx, RValue<'gcc>> {
+ assert_eq!(place.llextra.is_some(), place.layout.is_unsized());
+
+ if place.layout.is_zst() {
+ return OperandRef::new_zst(self, place.layout);
+ }
+
+ fn scalar_load_metadata<'a, 'gcc, 'tcx>(bx: &mut Builder<'a, 'gcc, 'tcx>, load: RValue<'gcc>, scalar: &abi::Scalar) {
+ let vr = scalar.valid_range.clone();
+ match scalar.value {
+ abi::Int(..) => {
+ if !scalar.is_always_valid(bx) {
+ bx.range_metadata(load, scalar.valid_range);
+ }
+ }
+ abi::Pointer if vr.start < vr.end && !vr.contains(0) => {
+ bx.nonnull_metadata(load);
+ }
+ _ => {}
+ }
+ }
+
+ let val =
+ if let Some(llextra) = place.llextra {
+ OperandValue::Ref(place.llval, Some(llextra), place.align)
+ }
+ else if place.layout.is_gcc_immediate() {
+ let load = self.load(place.llval.get_type(), place.llval, place.align);
+ if let abi::Abi::Scalar(ref scalar) = place.layout.abi {
+ scalar_load_metadata(self, load, scalar);
+ }
+ OperandValue::Immediate(self.to_immediate(load, place.layout))
+ }
+ else if let abi::Abi::ScalarPair(ref a, ref b) = place.layout.abi {
+ let b_offset = a.value.size(self).align_to(b.value.align(self).abi);
+ let pair_type = place.layout.gcc_type(self, false);
+
+ let mut load = |i, scalar: &abi::Scalar, align| {
+ let llptr = self.struct_gep(pair_type, place.llval, i as u64);
+ let load = self.load(llptr.get_type(), llptr, align);
+ scalar_load_metadata(self, load, scalar);
+ if scalar.is_bool() { self.trunc(load, self.type_i1()) } else { load }
+ };
+
+ OperandValue::Pair(
+ load(0, a, place.align),
+ load(1, b, place.align.restrict_for_offset(b_offset)),
+ )
+ }
+ else {
+ OperandValue::Ref(place.llval, None, place.align)
+ };
+
+ OperandRef { val, layout: place.layout }
+ }
+
+ fn write_operand_repeatedly(mut self, cg_elem: OperandRef<'tcx, RValue<'gcc>>, count: u64, dest: PlaceRef<'tcx, RValue<'gcc>>) -> Self {
+ let zero = self.const_usize(0);
+ let count = self.const_usize(count);
+ let start = dest.project_index(&mut self, zero).llval;
+ let end = dest.project_index(&mut self, count).llval;
+
+ let mut header_bx = self.build_sibling_block("repeat_loop_header");
+ let mut body_bx = self.build_sibling_block("repeat_loop_body");
+ let next_bx = self.build_sibling_block("repeat_loop_next");
+
+ let ptr_type = start.get_type();
+ let current = self.llbb().get_function().new_local(None, ptr_type, "loop_var");
+ let current_val = current.to_rvalue();
+ self.assign(current, start);
+
+ self.br(header_bx.llbb());
+
+ let keep_going = header_bx.icmp(IntPredicate::IntNE, current_val, end);
+ header_bx.cond_br(keep_going, body_bx.llbb(), next_bx.llbb());
+
+ let align = dest.align.restrict_for_offset(dest.layout.field(self.cx(), 0).size);
+ cg_elem.val.store(&mut body_bx, PlaceRef::new_sized_aligned(current_val, cg_elem.layout, align));
+
+ let next = body_bx.inbounds_gep(self.backend_type(cg_elem.layout), current.to_rvalue(), &[self.const_usize(1)]);
+ body_bx.llbb().add_assignment(None, current, next);
+ body_bx.br(header_bx.llbb());
+
+ next_bx
+ }
+
+ fn range_metadata(&mut self, _load: RValue<'gcc>, _range: WrappingRange) {
+ // TODO(antoyo)
+ }
+
+ fn nonnull_metadata(&mut self, _load: RValue<'gcc>) {
+ // TODO(antoyo)
+ }
+
+ fn store(&mut self, val: RValue<'gcc>, ptr: RValue<'gcc>, align: Align) -> RValue<'gcc> {
+ self.store_with_flags(val, ptr, align, MemFlags::empty())
+ }
+
+ fn store_with_flags(&mut self, val: RValue<'gcc>, ptr: RValue<'gcc>, _align: Align, _flags: MemFlags) -> RValue<'gcc> {
+ let ptr = self.check_store(val, ptr);
+ self.llbb().add_assignment(None, ptr.dereference(None), val);
+ // TODO(antoyo): handle align and flags.
+ // NOTE: dummy value here since it's never used. FIXME(antoyo): API should not return a value here?
+ self.cx.context.new_rvalue_zero(self.type_i32())
+ }
+
+ fn atomic_store(&mut self, value: RValue<'gcc>, ptr: RValue<'gcc>, order: AtomicOrdering, size: Size) {
+ // TODO(antoyo): handle alignment.
+ let atomic_store = self.context.get_builtin_function(&format!("__atomic_store_{}", size.bytes()));
+ let ordering = self.context.new_rvalue_from_int(self.i32_type, order.to_gcc());
+ let volatile_const_void_ptr_type = self.context.new_type::<*mut ()>().make_const().make_volatile();
+ let ptr = self.context.new_cast(None, ptr, volatile_const_void_ptr_type);
+
+ // FIXME(antoyo): fix libgccjit to allow comparing an integer type with an aligned integer type because
+ // the following cast is required to avoid this error:
+ // gcc_jit_context_new_call: mismatching types for argument 2 of function "__atomic_store_4": assignment to param arg1 (type: int) from loadedValue3577 (type: unsigned int __attribute__((aligned(4))))
+ let int_type = atomic_store.get_param(1).to_rvalue().get_type();
+ let value = self.context.new_cast(None, value, int_type);
+ self.llbb()
+ .add_eval(None, self.context.new_call(None, atomic_store, &[ptr, value, ordering]));
+ }
+
+ fn gep(&mut self, _typ: Type<'gcc>, ptr: RValue<'gcc>, indices: &[RValue<'gcc>]) -> RValue<'gcc> {
+ let mut result = ptr;
+ for index in indices {
+ result = self.context.new_array_access(None, result, *index).get_address(None).to_rvalue();
+ }
+ result
+ }
+
+ fn inbounds_gep(&mut self, _typ: Type<'gcc>, ptr: RValue<'gcc>, indices: &[RValue<'gcc>]) -> RValue<'gcc> {
+ // FIXME(antoyo): would be safer if doing the same thing (loop) as gep.
+ // TODO(antoyo): specify inbounds somehow.
+ match indices.len() {
+ 1 => {
+ self.context.new_array_access(None, ptr, indices[0]).get_address(None)
+ },
+ 2 => {
+ let array = ptr.dereference(None); // TODO(antoyo): assert that first index is 0?
+ self.context.new_array_access(None, array, indices[1]).get_address(None)
+ },
+ _ => unimplemented!(),
+ }
+ }
+
+ fn struct_gep(&mut self, value_type: Type<'gcc>, ptr: RValue<'gcc>, idx: u64) -> RValue<'gcc> {
+ // FIXME(antoyo): it would be better if the API only called this on struct, not on arrays.
+ assert_eq!(idx as usize as u64, idx);
+ let value = ptr.dereference(None).to_rvalue();
+
+ if value_type.is_array().is_some() {
+ let index = self.context.new_rvalue_from_long(self.u64_type, i64::try_from(idx).expect("i64::try_from"));
+ let element = self.context.new_array_access(None, value, index);
+ element.get_address(None)
+ }
+ else if let Some(vector_type) = value_type.is_vector() {
+ let array_type = vector_type.get_element_type().make_pointer();
+ let array = self.bitcast(ptr, array_type);
+ let index = self.context.new_rvalue_from_long(self.u64_type, i64::try_from(idx).expect("i64::try_from"));
+ let element = self.context.new_array_access(None, array, index);
+ element.get_address(None)
+ }
+ else if let Some(struct_type) = value_type.is_struct() {
+ ptr.dereference_field(None, struct_type.get_field(idx as i32)).get_address(None)
+ }
+ else {
+ panic!("Unexpected type {:?}", value_type);
+ }
+ }
+
+ /* Casts */
+ fn trunc(&mut self, value: RValue<'gcc>, dest_ty: Type<'gcc>) -> RValue<'gcc> {
+ // TODO(antoyo): check that it indeed truncate the value.
+ self.context.new_cast(None, value, dest_ty)
+ }
+
+ fn sext(&mut self, value: RValue<'gcc>, dest_ty: Type<'gcc>) -> RValue<'gcc> {
+ // TODO(antoyo): check that it indeed sign extend the value.
+ if dest_ty.is_vector().is_some() {
+ // TODO(antoyo): nothing to do as it is only for LLVM?
+ return value;
+ }
+ self.context.new_cast(None, value, dest_ty)
+ }
+
+ fn fptoui(&mut self, value: RValue<'gcc>, dest_ty: Type<'gcc>) -> RValue<'gcc> {
+ self.context.new_cast(None, value, dest_ty)
+ }
+
+ fn fptosi(&mut self, value: RValue<'gcc>, dest_ty: Type<'gcc>) -> RValue<'gcc> {
+ self.context.new_cast(None, value, dest_ty)
+ }
+
+ fn uitofp(&mut self, value: RValue<'gcc>, dest_ty: Type<'gcc>) -> RValue<'gcc> {
+ self.context.new_cast(None, value, dest_ty)
+ }
+
+ fn sitofp(&mut self, value: RValue<'gcc>, dest_ty: Type<'gcc>) -> RValue<'gcc> {
+ self.context.new_cast(None, value, dest_ty)
+ }
+
+ fn fptrunc(&mut self, value: RValue<'gcc>, dest_ty: Type<'gcc>) -> RValue<'gcc> {
+ // TODO(antoyo): make sure it truncates.
+ self.context.new_cast(None, value, dest_ty)
+ }
+
+ fn fpext(&mut self, value: RValue<'gcc>, dest_ty: Type<'gcc>) -> RValue<'gcc> {
+ self.context.new_cast(None, value, dest_ty)
+ }
+
+ fn ptrtoint(&mut self, value: RValue<'gcc>, dest_ty: Type<'gcc>) -> RValue<'gcc> {
+ self.cx.ptrtoint(self.block.expect("block"), value, dest_ty)
+ }
+
+ fn inttoptr(&mut self, value: RValue<'gcc>, dest_ty: Type<'gcc>) -> RValue<'gcc> {
+ self.cx.inttoptr(self.block.expect("block"), value, dest_ty)
+ }
+
+ fn bitcast(&mut self, value: RValue<'gcc>, dest_ty: Type<'gcc>) -> RValue<'gcc> {
+ self.cx.const_bitcast(value, dest_ty)
+ }
+
+ fn intcast(&mut self, value: RValue<'gcc>, dest_typ: Type<'gcc>, _is_signed: bool) -> RValue<'gcc> {
+ // NOTE: is_signed is for value, not dest_typ.
+ self.cx.context.new_cast(None, value, dest_typ)
+ }
+
+ fn pointercast(&mut self, value: RValue<'gcc>, dest_ty: Type<'gcc>) -> RValue<'gcc> {
+ let val_type = value.get_type();
+ match (type_is_pointer(val_type), type_is_pointer(dest_ty)) {
+ (false, true) => {
+ // NOTE: Projecting a field of a pointer type will attemp a cast from a signed char to
+ // a pointer, which is not supported by gccjit.
+ return self.cx.context.new_cast(None, self.inttoptr(value, val_type.make_pointer()), dest_ty);
+ },
+ (false, false) => {
+ // When they are not pointers, we want a transmute (or reinterpret_cast).
+ self.bitcast(value, dest_ty)
+ },
+ (true, true) => self.cx.context.new_cast(None, value, dest_ty),
+ (true, false) => unimplemented!(),
+ }
+ }
+
+ /* Comparisons */
+ fn icmp(&mut self, op: IntPredicate, mut lhs: RValue<'gcc>, mut rhs: RValue<'gcc>) -> RValue<'gcc> {
+ let left_type = lhs.get_type();
+ let right_type = rhs.get_type();
+ if left_type != right_type {
+ // NOTE: because libgccjit cannot compare function pointers.
+ if left_type.is_function_ptr_type().is_some() && right_type.is_function_ptr_type().is_some() {
+ lhs = self.context.new_cast(None, lhs, self.usize_type.make_pointer());
+ rhs = self.context.new_cast(None, rhs, self.usize_type.make_pointer());
+ }
+ // NOTE: hack because we try to cast a vector type to the same vector type.
+ else if format!("{:?}", left_type) != format!("{:?}", right_type) {
+ rhs = self.context.new_cast(None, rhs, left_type);
+ }
+ }
+ self.context.new_comparison(None, op.to_gcc_comparison(), lhs, rhs)
+ }
+
+ fn fcmp(&mut self, op: RealPredicate, lhs: RValue<'gcc>, rhs: RValue<'gcc>) -> RValue<'gcc> {
+ self.context.new_comparison(None, op.to_gcc_comparison(), lhs, rhs)
+ }
+
+ /* Miscellaneous instructions */
+ fn memcpy(&mut self, dst: RValue<'gcc>, dst_align: Align, src: RValue<'gcc>, src_align: Align, size: RValue<'gcc>, flags: MemFlags) {
+ if flags.contains(MemFlags::NONTEMPORAL) {
+ // HACK(nox): This is inefficient but there is no nontemporal memcpy.
+ let val = self.load(src.get_type(), src, src_align);
+ let ptr = self.pointercast(dst, self.type_ptr_to(self.val_ty(val)));
+ self.store_with_flags(val, ptr, dst_align, flags);
+ return;
+ }
+ let size = self.intcast(size, self.type_size_t(), false);
+ let _is_volatile = flags.contains(MemFlags::VOLATILE);
+ let dst = self.pointercast(dst, self.type_i8p());
+ let src = self.pointercast(src, self.type_ptr_to(self.type_void()));
+ let memcpy = self.context.get_builtin_function("memcpy");
+ let block = self.block.expect("block");
+ // TODO(antoyo): handle aligns and is_volatile.
+ block.add_eval(None, self.context.new_call(None, memcpy, &[dst, src, size]));
+ }
+
+ fn memmove(&mut self, dst: RValue<'gcc>, dst_align: Align, src: RValue<'gcc>, src_align: Align, size: RValue<'gcc>, flags: MemFlags) {
+ if flags.contains(MemFlags::NONTEMPORAL) {
+ // HACK(nox): This is inefficient but there is no nontemporal memmove.
+ let val = self.load(src.get_type(), src, src_align);
+ let ptr = self.pointercast(dst, self.type_ptr_to(self.val_ty(val)));
+ self.store_with_flags(val, ptr, dst_align, flags);
+ return;
+ }
+ let size = self.intcast(size, self.type_size_t(), false);
+ let _is_volatile = flags.contains(MemFlags::VOLATILE);
+ let dst = self.pointercast(dst, self.type_i8p());
+ let src = self.pointercast(src, self.type_ptr_to(self.type_void()));
+
+ let memmove = self.context.get_builtin_function("memmove");
+ let block = self.block.expect("block");
+ // TODO(antoyo): handle is_volatile.
+ block.add_eval(None, self.context.new_call(None, memmove, &[dst, src, size]));
+ }
+
+ fn memset(&mut self, ptr: RValue<'gcc>, fill_byte: RValue<'gcc>, size: RValue<'gcc>, _align: Align, flags: MemFlags) {
+ let _is_volatile = flags.contains(MemFlags::VOLATILE);
+ let ptr = self.pointercast(ptr, self.type_i8p());
+ let memset = self.context.get_builtin_function("memset");
+ let block = self.block.expect("block");
+ // TODO(antoyo): handle align and is_volatile.
+ let fill_byte = self.context.new_cast(None, fill_byte, self.i32_type);
+ let size = self.intcast(size, self.type_size_t(), false);
+ block.add_eval(None, self.context.new_call(None, memset, &[ptr, fill_byte, size]));
+ }
+
+ fn select(&mut self, cond: RValue<'gcc>, then_val: RValue<'gcc>, mut else_val: RValue<'gcc>) -> RValue<'gcc> {
+ let func = self.current_func();
+ let variable = func.new_local(None, then_val.get_type(), "selectVar");
+ let then_block = func.new_block("then");
+ let else_block = func.new_block("else");
+ let after_block = func.new_block("after");
+ self.llbb().end_with_conditional(None, cond, then_block, else_block);
+
+ then_block.add_assignment(None, variable, then_val);
+ then_block.end_with_jump(None, after_block);
+
+ if then_val.get_type() != else_val.get_type() {
+ else_val = self.context.new_cast(None, else_val, then_val.get_type());
+ }
+ else_block.add_assignment(None, variable, else_val);
+ else_block.end_with_jump(None, after_block);
+
+ // NOTE: since jumps were added in a place rustc does not expect, the current blocks in the
+ // state need to be updated.
+ self.block = Some(after_block);
+ *self.cx.current_block.borrow_mut() = Some(after_block);
+
+ variable.to_rvalue()
+ }
+
+ #[allow(dead_code)]
+ fn va_arg(&mut self, _list: RValue<'gcc>, _ty: Type<'gcc>) -> RValue<'gcc> {
+ unimplemented!();
+ }
+
+ fn extract_element(&mut self, _vec: RValue<'gcc>, _idx: RValue<'gcc>) -> RValue<'gcc> {
+ unimplemented!();
+ }
+
+ fn vector_splat(&mut self, _num_elts: usize, _elt: RValue<'gcc>) -> RValue<'gcc> {
+ unimplemented!();
+ }
+
+ fn extract_value(&mut self, aggregate_value: RValue<'gcc>, idx: u64) -> RValue<'gcc> {
+ // FIXME(antoyo): it would be better if the API only called this on struct, not on arrays.
+ assert_eq!(idx as usize as u64, idx);
+ let value_type = aggregate_value.get_type();
+
+ if value_type.is_array().is_some() {
+ let index = self.context.new_rvalue_from_long(self.u64_type, i64::try_from(idx).expect("i64::try_from"));
+ let element = self.context.new_array_access(None, aggregate_value, index);
+ element.get_address(None)
+ }
+ else if value_type.is_vector().is_some() {
+ panic!();
+ }
+ else if let Some(pointer_type) = value_type.get_pointee() {
+ if let Some(struct_type) = pointer_type.is_struct() {
+ // NOTE: hack to workaround a limitation of the rustc API: see comment on
+ // CodegenCx.structs_as_pointer
+ aggregate_value.dereference_field(None, struct_type.get_field(idx as i32)).to_rvalue()
+ }
+ else {
+ panic!("Unexpected type {:?}", value_type);
+ }
+ }
+ else if let Some(struct_type) = value_type.is_struct() {
+ aggregate_value.access_field(None, struct_type.get_field(idx as i32)).to_rvalue()
+ }
+ else {
+ panic!("Unexpected type {:?}", value_type);
+ }
+ }
+
+ fn insert_value(&mut self, aggregate_value: RValue<'gcc>, value: RValue<'gcc>, idx: u64) -> RValue<'gcc> {
+ // FIXME(antoyo): it would be better if the API only called this on struct, not on arrays.
+ assert_eq!(idx as usize as u64, idx);
+ let value_type = aggregate_value.get_type();
+
+ let lvalue =
+ if value_type.is_array().is_some() {
+ let index = self.context.new_rvalue_from_long(self.u64_type, i64::try_from(idx).expect("i64::try_from"));
+ self.context.new_array_access(None, aggregate_value, index)
+ }
+ else if value_type.is_vector().is_some() {
+ panic!();
+ }
+ else if let Some(pointer_type) = value_type.get_pointee() {
+ if let Some(struct_type) = pointer_type.is_struct() {
+ // NOTE: hack to workaround a limitation of the rustc API: see comment on
+ // CodegenCx.structs_as_pointer
+ aggregate_value.dereference_field(None, struct_type.get_field(idx as i32))
+ }
+ else {
+ panic!("Unexpected type {:?}", value_type);
+ }
+ }
+ else {
+ panic!("Unexpected type {:?}", value_type);
+ };
+
+ let lvalue_type = lvalue.to_rvalue().get_type();
+ let value =
+ // NOTE: sometimes, rustc will create a value with the wrong type.
+ if lvalue_type != value.get_type() {
+ self.context.new_cast(None, value, lvalue_type)
+ }
+ else {
+ value
+ };
+
+ self.llbb().add_assignment(None, lvalue, value);
+
+ aggregate_value
+ }
+
+ fn landing_pad(&mut self, _ty: Type<'gcc>, _pers_fn: RValue<'gcc>, _num_clauses: usize) -> RValue<'gcc> {
+ let field1 = self.context.new_field(None, self.u8_type, "landing_pad_field_1");
+ let field2 = self.context.new_field(None, self.i32_type, "landing_pad_field_1");
+ let struct_type = self.context.new_struct_type(None, "landing_pad", &[field1, field2]);
+ self.current_func().new_local(None, struct_type.as_type(), "landing_pad")
+ .to_rvalue()
+ // TODO(antoyo): Properly implement unwinding.
+ // the above is just to make the compilation work as it seems
+ // rustc_codegen_ssa now calls the unwinding builder methods even on panic=abort.
+ }
+
+ fn set_cleanup(&mut self, _landing_pad: RValue<'gcc>) {
+ // TODO(antoyo)
+ }
+
+ fn resume(&mut self, _exn: RValue<'gcc>) -> RValue<'gcc> {
+ unimplemented!();
+ }
+
+ fn cleanup_pad(&mut self, _parent: Option<RValue<'gcc>>, _args: &[RValue<'gcc>]) -> Funclet {
+ unimplemented!();
+ }
+
+ fn cleanup_ret(&mut self, _funclet: &Funclet, _unwind: Option<Block<'gcc>>) -> RValue<'gcc> {
+ unimplemented!();
+ }
+
+ fn catch_pad(&mut self, _parent: RValue<'gcc>, _args: &[RValue<'gcc>]) -> Funclet {
+ unimplemented!();
+ }
+
+ fn catch_switch(&mut self, _parent: Option<RValue<'gcc>>, _unwind: Option<Block<'gcc>>, _num_handlers: usize) -> RValue<'gcc> {
+ unimplemented!();
+ }
+
+ fn add_handler(&mut self, _catch_switch: RValue<'gcc>, _handler: Block<'gcc>) {
+ unimplemented!();
+ }
+
+ fn set_personality_fn(&mut self, _personality: RValue<'gcc>) {
+ // TODO(antoyo)
+ }
+
+ // Atomic Operations
+ fn atomic_cmpxchg(&mut self, dst: RValue<'gcc>, cmp: RValue<'gcc>, src: RValue<'gcc>, order: AtomicOrdering, failure_order: AtomicOrdering, weak: bool) -> RValue<'gcc> {
+ let expected = self.current_func().new_local(None, cmp.get_type(), "expected");
+ self.llbb().add_assignment(None, expected, cmp);
+ let success = self.compare_exchange(dst, expected, src, order, failure_order, weak);
+
+ let pair_type = self.cx.type_struct(&[src.get_type(), self.bool_type], false);
+ let result = self.current_func().new_local(None, pair_type, "atomic_cmpxchg_result");
+ let align = Align::from_bits(64).expect("align"); // TODO(antoyo): use good align.
+
+ let value_type = result.to_rvalue().get_type();
+ if let Some(struct_type) = value_type.is_struct() {
+ self.store(success, result.access_field(None, struct_type.get_field(1)).get_address(None), align);
+ // NOTE: since success contains the call to the intrinsic, it must be stored before
+ // expected so that we store expected after the call.
+ self.store(expected.to_rvalue(), result.access_field(None, struct_type.get_field(0)).get_address(None), align);
+ }
+ // TODO(antoyo): handle when value is not a struct.
+
+ result.to_rvalue()
+ }
+
+ fn atomic_rmw(&mut self, op: AtomicRmwBinOp, dst: RValue<'gcc>, src: RValue<'gcc>, order: AtomicOrdering) -> RValue<'gcc> {
+ let size = self.cx.int_width(src.get_type()) / 8;
+ let name =
+ match op {
+ AtomicRmwBinOp::AtomicXchg => format!("__atomic_exchange_{}", size),
+ AtomicRmwBinOp::AtomicAdd => format!("__atomic_fetch_add_{}", size),
+ AtomicRmwBinOp::AtomicSub => format!("__atomic_fetch_sub_{}", size),
+ AtomicRmwBinOp::AtomicAnd => format!("__atomic_fetch_and_{}", size),
+ AtomicRmwBinOp::AtomicNand => format!("__atomic_fetch_nand_{}", size),
+ AtomicRmwBinOp::AtomicOr => format!("__atomic_fetch_or_{}", size),
+ AtomicRmwBinOp::AtomicXor => format!("__atomic_fetch_xor_{}", size),
+ AtomicRmwBinOp::AtomicMax => return self.atomic_extremum(ExtremumOperation::Max, dst, src, order),
+ AtomicRmwBinOp::AtomicMin => return self.atomic_extremum(ExtremumOperation::Min, dst, src, order),
+ AtomicRmwBinOp::AtomicUMax => return self.atomic_extremum(ExtremumOperation::Max, dst, src, order),
+ AtomicRmwBinOp::AtomicUMin => return self.atomic_extremum(ExtremumOperation::Min, dst, src, order),
+ };
+
+
+ let atomic_function = self.context.get_builtin_function(name);
+ let order = self.context.new_rvalue_from_int(self.i32_type, order.to_gcc());
+
+ let void_ptr_type = self.context.new_type::<*mut ()>();
+ let volatile_void_ptr_type = void_ptr_type.make_volatile();
+ let dst = self.context.new_cast(None, dst, volatile_void_ptr_type);
+ // FIXME(antoyo): not sure why, but we have the wrong type here.
+ let new_src_type = atomic_function.get_param(1).to_rvalue().get_type();
+ let src = self.context.new_cast(None, src, new_src_type);
+ let res = self.context.new_call(None, atomic_function, &[dst, src, order]);
+ self.context.new_cast(None, res, src.get_type())
+ }
+
+ fn atomic_fence(&mut self, order: AtomicOrdering, scope: SynchronizationScope) {
+ let name =
+ match scope {
+ SynchronizationScope::SingleThread => "__atomic_signal_fence",
+ SynchronizationScope::CrossThread => "__atomic_thread_fence",
+ };
+ let thread_fence = self.context.get_builtin_function(name);
+ let order = self.context.new_rvalue_from_int(self.i32_type, order.to_gcc());
+ self.llbb().add_eval(None, self.context.new_call(None, thread_fence, &[order]));
+ }
+
+ fn set_invariant_load(&mut self, load: RValue<'gcc>) {
+ // NOTE: Hack to consider vtable function pointer as non-global-variable function pointer.
+ self.normal_function_addresses.borrow_mut().insert(load);
+ // TODO(antoyo)
+ }
+
+ fn lifetime_start(&mut self, _ptr: RValue<'gcc>, _size: Size) {
+ // TODO(antoyo)
+ }
+
+ fn lifetime_end(&mut self, _ptr: RValue<'gcc>, _size: Size) {
+ // TODO(antoyo)
+ }
+
+ fn call(&mut self, _typ: Type<'gcc>, func: RValue<'gcc>, args: &[RValue<'gcc>], funclet: Option<&Funclet>) -> RValue<'gcc> {
+ // FIXME(antoyo): remove when having a proper API.
+ let gcc_func = unsafe { std::mem::transmute(func) };
+ if self.functions.borrow().values().find(|value| **value == gcc_func).is_some() {
+ self.function_call(func, args, funclet)
+ }
+ else {
+ // If it's a not function that was defined, it's a function pointer.
+ self.function_ptr_call(func, args, funclet)
+ }
+ }
+
+ fn zext(&mut self, value: RValue<'gcc>, dest_typ: Type<'gcc>) -> RValue<'gcc> {
+ // FIXME(antoyo): this does not zero-extend.
+ if value.get_type().is_bool() && dest_typ.is_i8(&self.cx) {
+ // FIXME(antoyo): hack because base::from_immediate converts i1 to i8.
+ // Fix the code in codegen_ssa::base::from_immediate.
+ return value;
+ }
+ self.context.new_cast(None, value, dest_typ)
+ }
+
+ fn cx(&self) -> &CodegenCx<'gcc, 'tcx> {
+ self.cx
+ }
+
+ fn do_not_inline(&mut self, _llret: RValue<'gcc>) {
+ unimplemented!();
+ }
+
+ fn set_span(&mut self, _span: Span) {}
+
+ fn from_immediate(&mut self, val: Self::Value) -> Self::Value {
+ if self.cx().val_ty(val) == self.cx().type_i1() {
+ self.zext(val, self.cx().type_i8())
+ }
+ else {
+ val
+ }
+ }
+
+ fn to_immediate_scalar(&mut self, val: Self::Value, scalar: abi::Scalar) -> Self::Value {
+ if scalar.is_bool() {
+ return self.trunc(val, self.cx().type_i1());
+ }
+ val
+ }
+
+ fn fptoui_sat(&mut self, _val: RValue<'gcc>, _dest_ty: Type<'gcc>) -> Option<RValue<'gcc>> {
+ None
+ }
+
+ fn fptosi_sat(&mut self, _val: RValue<'gcc>, _dest_ty: Type<'gcc>) -> Option<RValue<'gcc>> {
+ None
+ }
+
+ fn instrprof_increment(&mut self, _fn_name: RValue<'gcc>, _hash: RValue<'gcc>, _num_counters: RValue<'gcc>, _index: RValue<'gcc>) {
+ unimplemented!();
+ }
+}
+
+impl<'a, 'gcc, 'tcx> Builder<'a, 'gcc, 'tcx> {
+ pub fn shuffle_vector(&mut self, v1: RValue<'gcc>, v2: RValue<'gcc>, mask: RValue<'gcc>) -> RValue<'gcc> {
+ let return_type = v1.get_type();
+ let params = [
+ self.context.new_parameter(None, return_type, "v1"),
+ self.context.new_parameter(None, return_type, "v2"),
+ self.context.new_parameter(None, mask.get_type(), "mask"),
+ ];
+ let shuffle = self.context.new_function(None, FunctionType::Extern, return_type, ¶ms, "_mm_shuffle_epi8", false);
+ self.context.new_call(None, shuffle, &[v1, v2, mask])
+ }
+}
+
+impl<'a, 'gcc, 'tcx> StaticBuilderMethods for Builder<'a, 'gcc, 'tcx> {
+ fn get_static(&mut self, def_id: DefId) -> RValue<'gcc> {
+ // Forward to the `get_static` method of `CodegenCx`
+ self.cx().get_static(def_id).get_address(None)
+ }
+}
+
+impl<'tcx> HasParamEnv<'tcx> for Builder<'_, '_, 'tcx> {
+ fn param_env(&self) -> ParamEnv<'tcx> {
+ self.cx.param_env()
+ }
+}
+
+impl<'tcx> HasTargetSpec for Builder<'_, '_, 'tcx> {
+ fn target_spec(&self) -> &Target {
+ &self.cx.target_spec()
+ }
+}
+
+trait ToGccComp {
+ fn to_gcc_comparison(&self) -> ComparisonOp;
+}
+
+impl ToGccComp for IntPredicate {
+ fn to_gcc_comparison(&self) -> ComparisonOp {
+ match *self {
+ IntPredicate::IntEQ => ComparisonOp::Equals,
+ IntPredicate::IntNE => ComparisonOp::NotEquals,
+ IntPredicate::IntUGT => ComparisonOp::GreaterThan,
+ IntPredicate::IntUGE => ComparisonOp::GreaterThanEquals,
+ IntPredicate::IntULT => ComparisonOp::LessThan,
+ IntPredicate::IntULE => ComparisonOp::LessThanEquals,
+ IntPredicate::IntSGT => ComparisonOp::GreaterThan,
+ IntPredicate::IntSGE => ComparisonOp::GreaterThanEquals,
+ IntPredicate::IntSLT => ComparisonOp::LessThan,
+ IntPredicate::IntSLE => ComparisonOp::LessThanEquals,
+ }
+ }
+}
+
+impl ToGccComp for RealPredicate {
+ fn to_gcc_comparison(&self) -> ComparisonOp {
+ // TODO(antoyo): check that ordered vs non-ordered is respected.
+ match *self {
+ RealPredicate::RealPredicateFalse => unreachable!(),
+ RealPredicate::RealOEQ => ComparisonOp::Equals,
+ RealPredicate::RealOGT => ComparisonOp::GreaterThan,
+ RealPredicate::RealOGE => ComparisonOp::GreaterThanEquals,
+ RealPredicate::RealOLT => ComparisonOp::LessThan,
+ RealPredicate::RealOLE => ComparisonOp::LessThanEquals,
+ RealPredicate::RealONE => ComparisonOp::NotEquals,
+ RealPredicate::RealORD => unreachable!(),
+ RealPredicate::RealUNO => unreachable!(),
+ RealPredicate::RealUEQ => ComparisonOp::Equals,
+ RealPredicate::RealUGT => ComparisonOp::GreaterThan,
+ RealPredicate::RealUGE => ComparisonOp::GreaterThan,
+ RealPredicate::RealULT => ComparisonOp::LessThan,
+ RealPredicate::RealULE => ComparisonOp::LessThan,
+ RealPredicate::RealUNE => ComparisonOp::NotEquals,
+ RealPredicate::RealPredicateTrue => unreachable!(),
+ }
+ }
+}
+
+#[repr(C)]
+#[allow(non_camel_case_types)]
+enum MemOrdering {
+ __ATOMIC_RELAXED,
+ __ATOMIC_CONSUME,
+ __ATOMIC_ACQUIRE,
+ __ATOMIC_RELEASE,
+ __ATOMIC_ACQ_REL,
+ __ATOMIC_SEQ_CST,
+}
+
+trait ToGccOrdering {
+ fn to_gcc(self) -> i32;
+}
+
+impl ToGccOrdering for AtomicOrdering {
+ fn to_gcc(self) -> i32 {
+ use MemOrdering::*;
+
+ let ordering =
+ match self {
+ AtomicOrdering::NotAtomic => __ATOMIC_RELAXED, // TODO(antoyo): check if that's the same.
+ AtomicOrdering::Unordered => __ATOMIC_RELAXED,
+ AtomicOrdering::Monotonic => __ATOMIC_RELAXED, // TODO(antoyo): check if that's the same.
+ AtomicOrdering::Acquire => __ATOMIC_ACQUIRE,
+ AtomicOrdering::Release => __ATOMIC_RELEASE,
+ AtomicOrdering::AcquireRelease => __ATOMIC_ACQ_REL,
+ AtomicOrdering::SequentiallyConsistent => __ATOMIC_SEQ_CST,
+ };
+ ordering as i32
+ }
+}
--- /dev/null
+use gccjit::{FunctionType, RValue};
+use rustc_codegen_ssa::traits::BaseTypeMethods;
+use rustc_middle::ty::{self, Instance, TypeFoldable};
+use rustc_middle::ty::layout::{FnAbiOf, HasTyCtxt};
+
+use crate::abi::FnAbiGccExt;
+use crate::context::CodegenCx;
+
+/// Codegens a reference to a fn/method item, monomorphizing and
+/// inlining as it goes.
+///
+/// # Parameters
+///
+/// - `cx`: the crate context
+/// - `instance`: the instance to be instantiated
+pub fn get_fn<'gcc, 'tcx>(cx: &CodegenCx<'gcc, 'tcx>, instance: Instance<'tcx>) -> RValue<'gcc> {
+ let tcx = cx.tcx();
+
+ assert!(!instance.substs.needs_infer());
+ assert!(!instance.substs.has_escaping_bound_vars());
+
+ if let Some(&func) = cx.function_instances.borrow().get(&instance) {
+ return func;
+ }
+
+ let sym = tcx.symbol_name(instance).name;
+
+ let fn_abi = cx.fn_abi_of_instance(instance, ty::List::empty());
+
+ let func =
+ if let Some(func) = cx.get_declared_value(&sym) {
+ // Create a fn pointer with the new signature.
+ let ptrty = fn_abi.ptr_to_gcc_type(cx);
+
+ // This is subtle and surprising, but sometimes we have to bitcast
+ // the resulting fn pointer. The reason has to do with external
+ // functions. If you have two crates that both bind the same C
+ // library, they may not use precisely the same types: for
+ // example, they will probably each declare their own structs,
+ // which are distinct types from LLVM's point of view (nominal
+ // types).
+ //
+ // Now, if those two crates are linked into an application, and
+ // they contain inlined code, you can wind up with a situation
+ // where both of those functions wind up being loaded into this
+ // application simultaneously. In that case, the same function
+ // (from LLVM's point of view) requires two types. But of course
+ // LLVM won't allow one function to have two types.
+ //
+ // What we currently do, therefore, is declare the function with
+ // one of the two types (whichever happens to come first) and then
+ // bitcast as needed when the function is referenced to make sure
+ // it has the type we expect.
+ //
+ // This can occur on either a crate-local or crate-external
+ // reference. It also occurs when testing libcore and in some
+ // other weird situations. Annoying.
+ if cx.val_ty(func) != ptrty {
+ // TODO(antoyo): cast the pointer.
+ func
+ }
+ else {
+ func
+ }
+ }
+ else {
+ cx.linkage.set(FunctionType::Extern);
+ let func = cx.declare_fn(&sym, &fn_abi);
+
+ // TODO(antoyo): set linkage and attributes.
+ func
+ };
+
+ cx.function_instances.borrow_mut().insert(instance, func);
+
+ func
+}
--- /dev/null
+use std::convert::TryFrom;
+use std::convert::TryInto;
+
+use gccjit::LValue;
+use gccjit::{Block, CType, RValue, Type, ToRValue};
+use rustc_codegen_ssa::mir::place::PlaceRef;
+use rustc_codegen_ssa::traits::{
+ BaseTypeMethods,
+ ConstMethods,
+ DerivedTypeMethods,
+ MiscMethods,
+ StaticMethods,
+};
+use rustc_middle::mir::Mutability;
+use rustc_middle::ty::ScalarInt;
+use rustc_middle::ty::layout::{TyAndLayout, LayoutOf};
+use rustc_middle::mir::interpret::{Allocation, GlobalAlloc, Scalar};
+use rustc_span::Symbol;
+use rustc_target::abi::{self, HasDataLayout, Pointer, Size};
+
+use crate::consts::const_alloc_to_gcc;
+use crate::context::CodegenCx;
+use crate::type_of::LayoutGccExt;
+
+impl<'gcc, 'tcx> CodegenCx<'gcc, 'tcx> {
+ pub fn const_bytes(&self, bytes: &[u8]) -> RValue<'gcc> {
+ bytes_in_context(self, bytes)
+ }
+
+ fn const_cstr(&self, symbol: Symbol, _null_terminated: bool) -> LValue<'gcc> {
+ // TODO(antoyo): handle null_terminated.
+ if let Some(&value) = self.const_cstr_cache.borrow().get(&symbol) {
+ return value;
+ }
+
+ let global = self.global_string(&*symbol.as_str());
+
+ self.const_cstr_cache.borrow_mut().insert(symbol, global);
+ global
+ }
+
+ fn global_string(&self, string: &str) -> LValue<'gcc> {
+ // TODO(antoyo): handle non-null-terminated strings.
+ let string = self.context.new_string_literal(&*string);
+ let sym = self.generate_local_symbol_name("str");
+ let global = self.declare_private_global(&sym, self.val_ty(string));
+ global.global_set_initializer_value(string);
+ global
+ // TODO(antoyo): set linkage.
+ }
+
+ pub fn inttoptr(&self, block: Block<'gcc>, value: RValue<'gcc>, dest_ty: Type<'gcc>) -> RValue<'gcc> {
+ let func = block.get_function();
+ let local = func.new_local(None, value.get_type(), "intLocal");
+ block.add_assignment(None, local, value);
+ let value_address = local.get_address(None);
+
+ let ptr = self.context.new_cast(None, value_address, dest_ty.make_pointer());
+ ptr.dereference(None).to_rvalue()
+ }
+
+ pub fn ptrtoint(&self, block: Block<'gcc>, value: RValue<'gcc>, dest_ty: Type<'gcc>) -> RValue<'gcc> {
+ // TODO(antoyo): when libgccjit allow casting from pointer to int, remove this.
+ let func = block.get_function();
+ let local = func.new_local(None, value.get_type(), "ptrLocal");
+ block.add_assignment(None, local, value);
+ let ptr_address = local.get_address(None);
+
+ let ptr = self.context.new_cast(None, ptr_address, dest_ty.make_pointer());
+ ptr.dereference(None).to_rvalue()
+ }
+}
+
+pub fn bytes_in_context<'gcc, 'tcx>(cx: &CodegenCx<'gcc, 'tcx>, bytes: &[u8]) -> RValue<'gcc> {
+ let context = &cx.context;
+ let byte_type = context.new_type::<u8>();
+ let typ = context.new_array_type(None, byte_type, bytes.len() as i32);
+ let elements: Vec<_> =
+ bytes.iter()
+ .map(|&byte| context.new_rvalue_from_int(byte_type, byte as i32))
+ .collect();
+ context.new_rvalue_from_array(None, typ, &elements)
+}
+
+pub fn type_is_pointer<'gcc>(typ: Type<'gcc>) -> bool {
+ typ.get_pointee().is_some()
+}
+
+impl<'gcc, 'tcx> ConstMethods<'tcx> for CodegenCx<'gcc, 'tcx> {
+ fn const_null(&self, typ: Type<'gcc>) -> RValue<'gcc> {
+ if type_is_pointer(typ) {
+ self.context.new_null(typ)
+ }
+ else {
+ self.const_int(typ, 0)
+ }
+ }
+
+ fn const_undef(&self, typ: Type<'gcc>) -> RValue<'gcc> {
+ let local = self.current_func.borrow().expect("func")
+ .new_local(None, typ, "undefined");
+ if typ.is_struct().is_some() {
+ // NOTE: hack to workaround a limitation of the rustc API: see comment on
+ // CodegenCx.structs_as_pointer
+ let pointer = local.get_address(None);
+ self.structs_as_pointer.borrow_mut().insert(pointer);
+ pointer
+ }
+ else {
+ local.to_rvalue()
+ }
+ }
+
+ fn const_int(&self, typ: Type<'gcc>, int: i64) -> RValue<'gcc> {
+ self.context.new_rvalue_from_long(typ, i64::try_from(int).expect("i64::try_from"))
+ }
+
+ fn const_uint(&self, typ: Type<'gcc>, int: u64) -> RValue<'gcc> {
+ self.context.new_rvalue_from_long(typ, u64::try_from(int).expect("u64::try_from") as i64)
+ }
+
+ fn const_uint_big(&self, typ: Type<'gcc>, num: u128) -> RValue<'gcc> {
+ let num64: Result<i64, _> = num.try_into();
+ if let Ok(num) = num64 {
+ // FIXME(antoyo): workaround for a bug where libgccjit is expecting a constant.
+ // The operations >> 64 and | low are making the normal case a non-constant.
+ return self.context.new_rvalue_from_long(typ, num as i64);
+ }
+
+ if num >> 64 != 0 {
+ // FIXME(antoyo): use a new function new_rvalue_from_unsigned_long()?
+ let low = self.context.new_rvalue_from_long(self.u64_type, num as u64 as i64);
+ let high = self.context.new_rvalue_from_long(typ, (num >> 64) as u64 as i64);
+
+ let sixty_four = self.context.new_rvalue_from_long(typ, 64);
+ (high << sixty_four) | self.context.new_cast(None, low, typ)
+ }
+ else if typ.is_i128(self) {
+ let num = self.context.new_rvalue_from_long(self.u64_type, num as u64 as i64);
+ self.context.new_cast(None, num, typ)
+ }
+ else {
+ self.context.new_rvalue_from_long(typ, num as u64 as i64)
+ }
+ }
+
+ fn const_bool(&self, val: bool) -> RValue<'gcc> {
+ self.const_uint(self.type_i1(), val as u64)
+ }
+
+ fn const_i32(&self, i: i32) -> RValue<'gcc> {
+ self.const_int(self.type_i32(), i as i64)
+ }
+
+ fn const_u32(&self, i: u32) -> RValue<'gcc> {
+ self.const_uint(self.type_u32(), i as u64)
+ }
+
+ fn const_u64(&self, i: u64) -> RValue<'gcc> {
+ self.const_uint(self.type_u64(), i)
+ }
+
+ fn const_usize(&self, i: u64) -> RValue<'gcc> {
+ let bit_size = self.data_layout().pointer_size.bits();
+ if bit_size < 64 {
+ // make sure it doesn't overflow
+ assert!(i < (1 << bit_size));
+ }
+
+ self.const_uint(self.usize_type, i)
+ }
+
+ fn const_u8(&self, _i: u8) -> RValue<'gcc> {
+ unimplemented!();
+ }
+
+ fn const_real(&self, _t: Type<'gcc>, _val: f64) -> RValue<'gcc> {
+ unimplemented!();
+ }
+
+ fn const_str(&self, s: Symbol) -> (RValue<'gcc>, RValue<'gcc>) {
+ let len = s.as_str().len();
+ let cs = self.const_ptrcast(self.const_cstr(s, false).get_address(None),
+ self.type_ptr_to(self.layout_of(self.tcx.types.str_).gcc_type(self, true)),
+ );
+ (cs, self.const_usize(len as u64))
+ }
+
+ fn const_struct(&self, values: &[RValue<'gcc>], packed: bool) -> RValue<'gcc> {
+ let fields: Vec<_> = values.iter()
+ .map(|value| value.get_type())
+ .collect();
+ // TODO(antoyo): cache the type? It's anonymous, so probably not.
+ let typ = self.type_struct(&fields, packed);
+ let struct_type = typ.is_struct().expect("struct type");
+ self.context.new_rvalue_from_struct(None, struct_type, values)
+ }
+
+ fn const_to_opt_uint(&self, _v: RValue<'gcc>) -> Option<u64> {
+ // TODO(antoyo)
+ None
+ }
+
+ fn const_to_opt_u128(&self, _v: RValue<'gcc>, _sign_ext: bool) -> Option<u128> {
+ // TODO(antoyo)
+ None
+ }
+
+ fn scalar_to_backend(&self, cv: Scalar, layout: abi::Scalar, ty: Type<'gcc>) -> RValue<'gcc> {
+ let bitsize = if layout.is_bool() { 1 } else { layout.value.size(self).bits() };
+ match cv {
+ Scalar::Int(ScalarInt::ZST) => {
+ assert_eq!(0, layout.value.size(self).bytes());
+ self.const_undef(self.type_ix(0))
+ }
+ Scalar::Int(int) => {
+ let data = int.assert_bits(layout.value.size(self));
+
+ // FIXME(antoyo): there's some issues with using the u128 code that follows, so hard-code
+ // the paths for floating-point values.
+ if ty == self.float_type {
+ return self.context.new_rvalue_from_double(ty, f32::from_bits(data as u32) as f64);
+ }
+ else if ty == self.double_type {
+ return self.context.new_rvalue_from_double(ty, f64::from_bits(data as u64));
+ }
+
+ let value = self.const_uint_big(self.type_ix(bitsize), data);
+ if layout.value == Pointer {
+ self.inttoptr(self.current_block.borrow().expect("block"), value, ty)
+ } else {
+ self.const_bitcast(value, ty)
+ }
+ }
+ Scalar::Ptr(ptr, _size) => {
+ let (alloc_id, offset) = ptr.into_parts();
+ let base_addr =
+ match self.tcx.global_alloc(alloc_id) {
+ GlobalAlloc::Memory(alloc) => {
+ let init = const_alloc_to_gcc(self, alloc);
+ let value =
+ match alloc.mutability {
+ Mutability::Mut => self.static_addr_of_mut(init, alloc.align, None),
+ _ => self.static_addr_of(init, alloc.align, None),
+ };
+ if !self.sess().fewer_names() {
+ // TODO(antoyo): set value name.
+ }
+ value
+ },
+ GlobalAlloc::Function(fn_instance) => {
+ self.get_fn_addr(fn_instance)
+ },
+ GlobalAlloc::Static(def_id) => {
+ assert!(self.tcx.is_static(def_id));
+ self.get_static(def_id).get_address(None)
+ },
+ };
+ let ptr_type = base_addr.get_type();
+ let base_addr = self.const_bitcast(base_addr, self.usize_type);
+ let offset = self.context.new_rvalue_from_long(self.usize_type, offset.bytes() as i64);
+ let ptr = self.const_bitcast(base_addr + offset, ptr_type);
+ if layout.value != Pointer {
+ self.const_bitcast(ptr.dereference(None).to_rvalue(), ty)
+ }
+ else {
+ self.const_bitcast(ptr, ty)
+ }
+ }
+ }
+ }
+
+ fn const_data_from_alloc(&self, alloc: &Allocation) -> Self::Value {
+ const_alloc_to_gcc(self, alloc)
+ }
+
+ fn from_const_alloc(&self, layout: TyAndLayout<'tcx>, alloc: &Allocation, offset: Size) -> PlaceRef<'tcx, RValue<'gcc>> {
+ assert_eq!(alloc.align, layout.align.abi);
+ let ty = self.type_ptr_to(layout.gcc_type(self, true));
+ let value =
+ if layout.size == Size::ZERO {
+ let value = self.const_usize(alloc.align.bytes());
+ self.context.new_cast(None, value, ty)
+ }
+ else {
+ let init = const_alloc_to_gcc(self, alloc);
+ let base_addr = self.static_addr_of(init, alloc.align, None);
+
+ let array = self.const_bitcast(base_addr, self.type_i8p());
+ let value = self.context.new_array_access(None, array, self.const_usize(offset.bytes())).get_address(None);
+ self.const_bitcast(value, ty)
+ };
+ PlaceRef::new_sized(value, layout)
+ }
+
+ fn const_ptrcast(&self, val: RValue<'gcc>, ty: Type<'gcc>) -> RValue<'gcc> {
+ self.context.new_cast(None, val, ty)
+ }
+}
+
+pub trait SignType<'gcc, 'tcx> {
+ fn is_signed(&self, cx: &CodegenCx<'gcc, 'tcx>) -> bool;
+ fn is_unsigned(&self, cx: &CodegenCx<'gcc, 'tcx>) -> bool;
+ fn to_signed(&self, cx: &CodegenCx<'gcc, 'tcx>) -> Type<'gcc>;
+ fn to_unsigned(&self, cx: &CodegenCx<'gcc, 'tcx>) -> Type<'gcc>;
+}
+
+impl<'gcc, 'tcx> SignType<'gcc, 'tcx> for Type<'gcc> {
+ fn is_signed(&self, cx: &CodegenCx<'gcc, 'tcx>) -> bool {
+ self.is_i8(cx) || self.is_i16(cx) || self.is_i32(cx) || self.is_i64(cx) || self.is_i128(cx)
+ }
+
+ fn is_unsigned(&self, cx: &CodegenCx<'gcc, 'tcx>) -> bool {
+ self.is_u8(cx) || self.is_u16(cx) || self.is_u32(cx) || self.is_u64(cx) || self.is_u128(cx)
+ }
+
+ fn to_signed(&self, cx: &CodegenCx<'gcc, 'tcx>) -> Type<'gcc> {
+ if self.is_u8(cx) {
+ cx.i8_type
+ }
+ else if self.is_u16(cx) {
+ cx.i16_type
+ }
+ else if self.is_u32(cx) {
+ cx.i32_type
+ }
+ else if self.is_u64(cx) {
+ cx.i64_type
+ }
+ else if self.is_u128(cx) {
+ cx.i128_type
+ }
+ else {
+ self.clone()
+ }
+ }
+
+ fn to_unsigned(&self, cx: &CodegenCx<'gcc, 'tcx>) -> Type<'gcc> {
+ if self.is_i8(cx) {
+ cx.u8_type
+ }
+ else if self.is_i16(cx) {
+ cx.u16_type
+ }
+ else if self.is_i32(cx) {
+ cx.u32_type
+ }
+ else if self.is_i64(cx) {
+ cx.u64_type
+ }
+ else if self.is_i128(cx) {
+ cx.u128_type
+ }
+ else {
+ self.clone()
+ }
+ }
+}
+
+pub trait TypeReflection<'gcc, 'tcx> {
+ fn is_uchar(&self, cx: &CodegenCx<'gcc, 'tcx>) -> bool;
+ fn is_ushort(&self, cx: &CodegenCx<'gcc, 'tcx>) -> bool;
+ fn is_uint(&self, cx: &CodegenCx<'gcc, 'tcx>) -> bool;
+ fn is_ulong(&self, cx: &CodegenCx<'gcc, 'tcx>) -> bool;
+ fn is_ulonglong(&self, cx: &CodegenCx<'gcc, 'tcx>) -> bool;
+
+ fn is_i8(&self, cx: &CodegenCx<'gcc, 'tcx>) -> bool;
+ fn is_u8(&self, cx: &CodegenCx<'gcc, 'tcx>) -> bool;
+ fn is_i16(&self, cx: &CodegenCx<'gcc, 'tcx>) -> bool;
+ fn is_u16(&self, cx: &CodegenCx<'gcc, 'tcx>) -> bool;
+ fn is_i32(&self, cx: &CodegenCx<'gcc, 'tcx>) -> bool;
+ fn is_u32(&self, cx: &CodegenCx<'gcc, 'tcx>) -> bool;
+ fn is_i64(&self, cx: &CodegenCx<'gcc, 'tcx>) -> bool;
+ fn is_u64(&self, cx: &CodegenCx<'gcc, 'tcx>) -> bool;
+ fn is_i128(&self, cx: &CodegenCx<'gcc, 'tcx>) -> bool;
+ fn is_u128(&self, cx: &CodegenCx<'gcc, 'tcx>) -> bool;
+
+ fn is_f32(&self, cx: &CodegenCx<'gcc, 'tcx>) -> bool;
+ fn is_f64(&self, cx: &CodegenCx<'gcc, 'tcx>) -> bool;
+}
+
+impl<'gcc, 'tcx> TypeReflection<'gcc, 'tcx> for Type<'gcc> {
+ fn is_uchar(&self, cx: &CodegenCx<'gcc, 'tcx>) -> bool {
+ self.unqualified() == cx.u8_type
+ }
+
+ fn is_ushort(&self, cx: &CodegenCx<'gcc, 'tcx>) -> bool {
+ self.unqualified() == cx.u16_type
+ }
+
+ fn is_uint(&self, cx: &CodegenCx<'gcc, 'tcx>) -> bool {
+ self.unqualified() == cx.uint_type
+ }
+
+ fn is_ulong(&self, cx: &CodegenCx<'gcc, 'tcx>) -> bool {
+ self.unqualified() == cx.ulong_type
+ }
+
+ fn is_ulonglong(&self, cx: &CodegenCx<'gcc, 'tcx>) -> bool {
+ self.unqualified() == cx.ulonglong_type
+ }
+
+ fn is_i8(&self, cx: &CodegenCx<'gcc, 'tcx>) -> bool {
+ self.unqualified() == cx.i8_type
+ }
+
+ fn is_u8(&self, cx: &CodegenCx<'gcc, 'tcx>) -> bool {
+ self.unqualified() == cx.u8_type
+ }
+
+ fn is_i16(&self, cx: &CodegenCx<'gcc, 'tcx>) -> bool {
+ self.unqualified() == cx.i16_type
+ }
+
+ fn is_u16(&self, cx: &CodegenCx<'gcc, 'tcx>) -> bool {
+ self.unqualified() == cx.u16_type
+ }
+
+ fn is_i32(&self, cx: &CodegenCx<'gcc, 'tcx>) -> bool {
+ self.unqualified() == cx.i32_type
+ }
+
+ fn is_u32(&self, cx: &CodegenCx<'gcc, 'tcx>) -> bool {
+ self.unqualified() == cx.u32_type
+ }
+
+ fn is_i64(&self, cx: &CodegenCx<'gcc, 'tcx>) -> bool {
+ self.unqualified() == cx.i64_type
+ }
+
+ fn is_u64(&self, cx: &CodegenCx<'gcc, 'tcx>) -> bool {
+ self.unqualified() == cx.u64_type
+ }
+
+ fn is_i128(&self, cx: &CodegenCx<'gcc, 'tcx>) -> bool {
+ self.unqualified() == cx.context.new_c_type(CType::Int128t)
+ }
+
+ fn is_u128(&self, cx: &CodegenCx<'gcc, 'tcx>) -> bool {
+ self.unqualified() == cx.context.new_c_type(CType::UInt128t)
+ }
+
+ fn is_f32(&self, cx: &CodegenCx<'gcc, 'tcx>) -> bool {
+ self.unqualified() == cx.context.new_type::<f32>()
+ }
+
+ fn is_f64(&self, cx: &CodegenCx<'gcc, 'tcx>) -> bool {
+ self.unqualified() == cx.context.new_type::<f64>()
+ }
+}
--- /dev/null
+use gccjit::{LValue, RValue, ToRValue, Type};
+use rustc_codegen_ssa::traits::{BaseTypeMethods, ConstMethods, DerivedTypeMethods, StaticMethods};
+use rustc_hir as hir;
+use rustc_hir::Node;
+use rustc_middle::{bug, span_bug};
+use rustc_middle::middle::codegen_fn_attrs::{CodegenFnAttrFlags, CodegenFnAttrs};
+use rustc_middle::mir::mono::MonoItem;
+use rustc_middle::ty::{self, Instance, Ty};
+use rustc_middle::ty::layout::LayoutOf;
+use rustc_middle::mir::interpret::{self, Allocation, ErrorHandled, Scalar as InterpScalar, read_target_uint};
+use rustc_span::Span;
+use rustc_span::def_id::DefId;
+use rustc_target::abi::{self, Align, HasDataLayout, Primitive, Size, WrappingRange};
+
+use crate::base;
+use crate::context::CodegenCx;
+use crate::type_of::LayoutGccExt;
+
+impl<'gcc, 'tcx> CodegenCx<'gcc, 'tcx> {
+ pub fn const_bitcast(&self, value: RValue<'gcc>, typ: Type<'gcc>) -> RValue<'gcc> {
+ if value.get_type() == self.bool_type.make_pointer() {
+ if let Some(pointee) = typ.get_pointee() {
+ if pointee.is_vector().is_some() {
+ panic!()
+ }
+ }
+ }
+ self.context.new_bitcast(None, value, typ)
+ }
+}
+
+impl<'gcc, 'tcx> StaticMethods for CodegenCx<'gcc, 'tcx> {
+ fn static_addr_of(&self, cv: RValue<'gcc>, align: Align, kind: Option<&str>) -> RValue<'gcc> {
+ if let Some(global_value) = self.const_globals.borrow().get(&cv) {
+ // TODO(antoyo): upgrade alignment.
+ return *global_value;
+ }
+ let global_value = self.static_addr_of_mut(cv, align, kind);
+ // TODO(antoyo): set global constant.
+ self.const_globals.borrow_mut().insert(cv, global_value);
+ global_value
+ }
+
+ fn codegen_static(&self, def_id: DefId, is_mutable: bool) {
+ let attrs = self.tcx.codegen_fn_attrs(def_id);
+
+ let value =
+ match codegen_static_initializer(&self, def_id) {
+ Ok((value, _)) => value,
+ // Error has already been reported
+ Err(_) => return,
+ };
+
+ let global = self.get_static(def_id);
+
+ // boolean SSA values are i1, but they have to be stored in i8 slots,
+ // otherwise some LLVM optimization passes don't work as expected
+ let val_llty = self.val_ty(value);
+ let value =
+ if val_llty == self.type_i1() {
+ unimplemented!();
+ }
+ else {
+ value
+ };
+
+ let instance = Instance::mono(self.tcx, def_id);
+ let ty = instance.ty(self.tcx, ty::ParamEnv::reveal_all());
+ let gcc_type = self.layout_of(ty).gcc_type(self, true);
+
+ // TODO(antoyo): set alignment.
+
+ let value =
+ if value.get_type() != gcc_type {
+ self.context.new_bitcast(None, value, gcc_type)
+ }
+ else {
+ value
+ };
+ global.global_set_initializer_value(value);
+
+ // As an optimization, all shared statics which do not have interior
+ // mutability are placed into read-only memory.
+ if !is_mutable {
+ if self.type_is_freeze(ty) {
+ // TODO(antoyo): set global constant.
+ }
+ }
+
+ if attrs.flags.contains(CodegenFnAttrFlags::THREAD_LOCAL) {
+ // Do not allow LLVM to change the alignment of a TLS on macOS.
+ //
+ // By default a global's alignment can be freely increased.
+ // This allows LLVM to generate more performant instructions
+ // e.g., using load-aligned into a SIMD register.
+ //
+ // However, on macOS 10.10 or below, the dynamic linker does not
+ // respect any alignment given on the TLS (radar 24221680).
+ // This will violate the alignment assumption, and causing segfault at runtime.
+ //
+ // This bug is very easy to trigger. In `println!` and `panic!`,
+ // the `LOCAL_STDOUT`/`LOCAL_STDERR` handles are stored in a TLS,
+ // which the values would be `mem::replace`d on initialization.
+ // The implementation of `mem::replace` will use SIMD
+ // whenever the size is 32 bytes or higher. LLVM notices SIMD is used
+ // and tries to align `LOCAL_STDOUT`/`LOCAL_STDERR` to a 32-byte boundary,
+ // which macOS's dyld disregarded and causing crashes
+ // (see issues #51794, #51758, #50867, #48866 and #44056).
+ //
+ // To workaround the bug, we trick LLVM into not increasing
+ // the global's alignment by explicitly assigning a section to it
+ // (equivalent to automatically generating a `#[link_section]` attribute).
+ // See the comment in the `GlobalValue::canIncreaseAlignment()` function
+ // of `lib/IR/Globals.cpp` for why this works.
+ //
+ // When the alignment is not increased, the optimized `mem::replace`
+ // will use load-unaligned instructions instead, and thus avoiding the crash.
+ //
+ // We could remove this hack whenever we decide to drop macOS 10.10 support.
+ if self.tcx.sess.target.options.is_like_osx {
+ // The `inspect` method is okay here because we checked relocations, and
+ // because we are doing this access to inspect the final interpreter state
+ // (not as part of the interpreter execution).
+ //
+ // FIXME: This check requires that the (arbitrary) value of undefined bytes
+ // happens to be zero. Instead, we should only check the value of defined bytes
+ // and set all undefined bytes to zero if this allocation is headed for the
+ // BSS.
+ unimplemented!();
+ }
+ }
+
+ // Wasm statics with custom link sections get special treatment as they
+ // go into custom sections of the wasm executable.
+ if self.tcx.sess.opts.target_triple.triple().starts_with("wasm32") {
+ if let Some(_section) = attrs.link_section {
+ unimplemented!();
+ }
+ } else {
+ // TODO(antoyo): set link section.
+ }
+
+ if attrs.flags.contains(CodegenFnAttrFlags::USED) {
+ self.add_used_global(global.to_rvalue());
+ }
+ }
+
+ /// Add a global value to a list to be stored in the `llvm.used` variable, an array of i8*.
+ fn add_used_global(&self, _global: RValue<'gcc>) {
+ // TODO(antoyo)
+ }
+
+ fn add_compiler_used_global(&self, _global: RValue<'gcc>) {
+ // TODO(antoyo)
+ }
+}
+
+impl<'gcc, 'tcx> CodegenCx<'gcc, 'tcx> {
+ pub fn static_addr_of_mut(&self, cv: RValue<'gcc>, align: Align, kind: Option<&str>) -> RValue<'gcc> {
+ let global =
+ match kind {
+ Some(kind) if !self.tcx.sess.fewer_names() => {
+ let name = self.generate_local_symbol_name(kind);
+ // TODO(antoyo): check if it's okay that TLS is off here.
+ // TODO(antoyo): check if it's okay that link_section is None here.
+ // TODO(antoyo): set alignment here as well.
+ let global = self.define_global(&name[..], self.val_ty(cv), false, None);
+ // TODO(antoyo): set linkage.
+ global
+ }
+ _ => {
+ let typ = self.val_ty(cv).get_aligned(align.bytes());
+ let global = self.declare_unnamed_global(typ);
+ global
+ },
+ };
+ // FIXME(antoyo): I think the name coming from generate_local_symbol_name() above cannot be used
+ // globally.
+ global.global_set_initializer_value(cv);
+ // TODO(antoyo): set unnamed address.
+ global.get_address(None)
+ }
+
+ pub fn get_static(&self, def_id: DefId) -> LValue<'gcc> {
+ let instance = Instance::mono(self.tcx, def_id);
+ let fn_attrs = self.tcx.codegen_fn_attrs(def_id);
+ if let Some(&global) = self.instances.borrow().get(&instance) {
+ return global;
+ }
+
+ let defined_in_current_codegen_unit =
+ self.codegen_unit.items().contains_key(&MonoItem::Static(def_id));
+ assert!(
+ !defined_in_current_codegen_unit,
+ "consts::get_static() should always hit the cache for \
+ statics defined in the same CGU, but did not for `{:?}`",
+ def_id
+ );
+
+ let ty = instance.ty(self.tcx, ty::ParamEnv::reveal_all());
+ let sym = self.tcx.symbol_name(instance).name;
+
+ let global =
+ if let Some(def_id) = def_id.as_local() {
+ let id = self.tcx.hir().local_def_id_to_hir_id(def_id);
+ let llty = self.layout_of(ty).gcc_type(self, true);
+ // FIXME: refactor this to work without accessing the HIR
+ let global = match self.tcx.hir().get(id) {
+ Node::Item(&hir::Item { span, kind: hir::ItemKind::Static(..), .. }) => {
+ if let Some(global) = self.get_declared_value(&sym) {
+ if self.val_ty(global) != self.type_ptr_to(llty) {
+ span_bug!(span, "Conflicting types for static");
+ }
+ }
+
+ let is_tls = fn_attrs.flags.contains(CodegenFnAttrFlags::THREAD_LOCAL);
+ let global = self.declare_global(&sym, llty, is_tls, fn_attrs.link_section);
+
+ if !self.tcx.is_reachable_non_generic(def_id) {
+ // TODO(antoyo): set visibility.
+ }
+
+ global
+ }
+
+ Node::ForeignItem(&hir::ForeignItem {
+ span,
+ kind: hir::ForeignItemKind::Static(..),
+ ..
+ }) => {
+ let fn_attrs = self.tcx.codegen_fn_attrs(def_id);
+ check_and_apply_linkage(&self, &fn_attrs, ty, sym, span)
+ }
+
+ item => bug!("get_static: expected static, found {:?}", item),
+ };
+
+ global
+ }
+ else {
+ // FIXME(nagisa): perhaps the map of externs could be offloaded to llvm somehow?
+ //debug!("get_static: sym={} item_attr={:?}", sym, self.tcx.item_attrs(def_id));
+
+ let attrs = self.tcx.codegen_fn_attrs(def_id);
+ let span = self.tcx.def_span(def_id);
+ let global = check_and_apply_linkage(&self, &attrs, ty, sym, span);
+
+ let needs_dll_storage_attr = false; // TODO(antoyo)
+
+ // If this assertion triggers, there's something wrong with commandline
+ // argument validation.
+ debug_assert!(
+ !(self.tcx.sess.opts.cg.linker_plugin_lto.enabled()
+ && self.tcx.sess.target.options.is_like_msvc
+ && self.tcx.sess.opts.cg.prefer_dynamic)
+ );
+
+ if needs_dll_storage_attr {
+ // This item is external but not foreign, i.e., it originates from an external Rust
+ // crate. Since we don't know whether this crate will be linked dynamically or
+ // statically in the final application, we always mark such symbols as 'dllimport'.
+ // If final linkage happens to be static, we rely on compiler-emitted __imp_ stubs
+ // to make things work.
+ //
+ // However, in some scenarios we defer emission of statics to downstream
+ // crates, so there are cases where a static with an upstream DefId
+ // is actually present in the current crate. We can find out via the
+ // is_codegened_item query.
+ if !self.tcx.is_codegened_item(def_id) {
+ unimplemented!();
+ }
+ }
+ global
+ };
+
+ // TODO(antoyo): set dll storage class.
+
+ self.instances.borrow_mut().insert(instance, global);
+ global
+ }
+}
+
+pub fn const_alloc_to_gcc<'gcc, 'tcx>(cx: &CodegenCx<'gcc, 'tcx>, alloc: &Allocation) -> RValue<'gcc> {
+ let mut llvals = Vec::with_capacity(alloc.relocations().len() + 1);
+ let dl = cx.data_layout();
+ let pointer_size = dl.pointer_size.bytes() as usize;
+
+ let mut next_offset = 0;
+ for &(offset, alloc_id) in alloc.relocations().iter() {
+ let offset = offset.bytes();
+ assert_eq!(offset as usize as u64, offset);
+ let offset = offset as usize;
+ if offset > next_offset {
+ // This `inspect` is okay since we have checked that it is not within a relocation, it
+ // is within the bounds of the allocation, and it doesn't affect interpreter execution
+ // (we inspect the result after interpreter execution). Any undef byte is replaced with
+ // some arbitrary byte value.
+ //
+ // FIXME: relay undef bytes to codegen as undef const bytes
+ let bytes = alloc.inspect_with_uninit_and_ptr_outside_interpreter(next_offset..offset);
+ llvals.push(cx.const_bytes(bytes));
+ }
+ let ptr_offset =
+ read_target_uint( dl.endian,
+ // This `inspect` is okay since it is within the bounds of the allocation, it doesn't
+ // affect interpreter execution (we inspect the result after interpreter execution),
+ // and we properly interpret the relocation as a relocation pointer offset.
+ alloc.inspect_with_uninit_and_ptr_outside_interpreter(offset..(offset + pointer_size)),
+ )
+ .expect("const_alloc_to_llvm: could not read relocation pointer")
+ as u64;
+ llvals.push(cx.scalar_to_backend(
+ InterpScalar::from_pointer(
+ interpret::Pointer::new(alloc_id, Size::from_bytes(ptr_offset)),
+ &cx.tcx,
+ ),
+ abi::Scalar { value: Primitive::Pointer, valid_range: WrappingRange { start: 0, end: !0 } },
+ cx.type_i8p(),
+ ));
+ next_offset = offset + pointer_size;
+ }
+ if alloc.len() >= next_offset {
+ let range = next_offset..alloc.len();
+ // This `inspect` is okay since we have check that it is after all relocations, it is
+ // within the bounds of the allocation, and it doesn't affect interpreter execution (we
+ // inspect the result after interpreter execution). Any undef byte is replaced with some
+ // arbitrary byte value.
+ //
+ // FIXME: relay undef bytes to codegen as undef const bytes
+ let bytes = alloc.inspect_with_uninit_and_ptr_outside_interpreter(range);
+ llvals.push(cx.const_bytes(bytes));
+ }
+
+ cx.const_struct(&llvals, true)
+}
+
+pub fn codegen_static_initializer<'gcc, 'tcx>(cx: &CodegenCx<'gcc, 'tcx>, def_id: DefId) -> Result<(RValue<'gcc>, &'tcx Allocation), ErrorHandled> {
+ let alloc = cx.tcx.eval_static_initializer(def_id)?;
+ Ok((const_alloc_to_gcc(cx, alloc), alloc))
+}
+
+fn check_and_apply_linkage<'gcc, 'tcx>(cx: &CodegenCx<'gcc, 'tcx>, attrs: &CodegenFnAttrs, ty: Ty<'tcx>, sym: &str, span: Span) -> LValue<'gcc> {
+ let is_tls = attrs.flags.contains(CodegenFnAttrFlags::THREAD_LOCAL);
+ let llty = cx.layout_of(ty).gcc_type(cx, true);
+ if let Some(linkage) = attrs.linkage {
+ // If this is a static with a linkage specified, then we need to handle
+ // it a little specially. The typesystem prevents things like &T and
+ // extern "C" fn() from being non-null, so we can't just declare a
+ // static and call it a day. Some linkages (like weak) will make it such
+ // that the static actually has a null value.
+ let llty2 =
+ if let ty::RawPtr(ref mt) = ty.kind() {
+ cx.layout_of(mt.ty).gcc_type(cx, true)
+ }
+ else {
+ cx.sess().span_fatal(
+ span,
+ "must have type `*const T` or `*mut T` due to `#[linkage]` attribute",
+ )
+ };
+ // Declare a symbol `foo` with the desired linkage.
+ let global1 = cx.declare_global_with_linkage(&sym, llty2, base::global_linkage_to_gcc(linkage));
+
+ // Declare an internal global `extern_with_linkage_foo` which
+ // is initialized with the address of `foo`. If `foo` is
+ // discarded during linking (for example, if `foo` has weak
+ // linkage and there are no definitions), then
+ // `extern_with_linkage_foo` will instead be initialized to
+ // zero.
+ let mut real_name = "_rust_extern_with_linkage_".to_string();
+ real_name.push_str(&sym);
+ let global2 = cx.define_global(&real_name, llty, is_tls, attrs.link_section);
+ // TODO(antoyo): set linkage.
+ global2.global_set_initializer_value(global1.get_address(None));
+ // TODO(antoyo): use global_set_initializer() when it will work.
+ global2
+ }
+ else {
+ // Generate an external declaration.
+ // FIXME(nagisa): investigate whether it can be changed into define_global
+
+ // Thread-local statics in some other crate need to *always* be linked
+ // against in a thread-local fashion, so we need to be sure to apply the
+ // thread-local attribute locally if it was present remotely. If we
+ // don't do this then linker errors can be generated where the linker
+ // complains that one object files has a thread local version of the
+ // symbol and another one doesn't.
+ cx.declare_global(&sym, llty, is_tls, attrs.link_section)
+ }
+}
--- /dev/null
+use std::cell::{Cell, RefCell};
+
+use gccjit::{
+ Block,
+ Context,
+ CType,
+ Function,
+ FunctionType,
+ LValue,
+ RValue,
+ Struct,
+ Type,
+};
+use rustc_codegen_ssa::base::wants_msvc_seh;
+use rustc_codegen_ssa::traits::{
+ BackendTypes,
+ MiscMethods,
+};
+use rustc_data_structures::base_n;
+use rustc_data_structures::fx::{FxHashMap, FxHashSet};
+use rustc_middle::span_bug;
+use rustc_middle::mir::mono::CodegenUnit;
+use rustc_middle::ty::{self, Instance, ParamEnv, PolyExistentialTraitRef, Ty, TyCtxt};
+use rustc_middle::ty::layout::{FnAbiError, FnAbiOfHelpers, FnAbiRequest, HasParamEnv, HasTyCtxt, LayoutError, TyAndLayout, LayoutOfHelpers};
+use rustc_session::Session;
+use rustc_span::{Span, Symbol};
+use rustc_target::abi::{call::FnAbi, HasDataLayout, PointeeInfo, Size, TargetDataLayout, VariantIdx};
+use rustc_target::spec::{HasTargetSpec, Target, TlsModel};
+
+use crate::callee::get_fn;
+use crate::declare::mangle_name;
+
+#[derive(Clone)]
+pub struct FuncSig<'gcc> {
+ pub params: Vec<Type<'gcc>>,
+ pub return_type: Type<'gcc>,
+}
+
+pub struct CodegenCx<'gcc, 'tcx> {
+ pub check_overflow: bool,
+ pub codegen_unit: &'tcx CodegenUnit<'tcx>,
+ pub context: &'gcc Context<'gcc>,
+
+ // TODO(antoyo): First set it to a dummy block to avoid using Option?
+ pub current_block: RefCell<Option<Block<'gcc>>>,
+ pub current_func: RefCell<Option<Function<'gcc>>>,
+ pub normal_function_addresses: RefCell<FxHashSet<RValue<'gcc>>>,
+
+ pub functions: RefCell<FxHashMap<String, Function<'gcc>>>,
+
+ pub tls_model: gccjit::TlsModel,
+
+ pub bool_type: Type<'gcc>,
+ pub i8_type: Type<'gcc>,
+ pub i16_type: Type<'gcc>,
+ pub i32_type: Type<'gcc>,
+ pub i64_type: Type<'gcc>,
+ pub i128_type: Type<'gcc>,
+ pub isize_type: Type<'gcc>,
+
+ pub u8_type: Type<'gcc>,
+ pub u16_type: Type<'gcc>,
+ pub u32_type: Type<'gcc>,
+ pub u64_type: Type<'gcc>,
+ pub u128_type: Type<'gcc>,
+ pub usize_type: Type<'gcc>,
+
+ pub int_type: Type<'gcc>,
+ pub uint_type: Type<'gcc>,
+ pub long_type: Type<'gcc>,
+ pub ulong_type: Type<'gcc>,
+ pub ulonglong_type: Type<'gcc>,
+ pub sizet_type: Type<'gcc>,
+
+ pub float_type: Type<'gcc>,
+ pub double_type: Type<'gcc>,
+
+ pub linkage: Cell<FunctionType>,
+ pub scalar_types: RefCell<FxHashMap<Ty<'tcx>, Type<'gcc>>>,
+ pub types: RefCell<FxHashMap<(Ty<'tcx>, Option<VariantIdx>), Type<'gcc>>>,
+ pub tcx: TyCtxt<'tcx>,
+
+ pub struct_types: RefCell<FxHashMap<Vec<Type<'gcc>>, Type<'gcc>>>,
+
+ pub types_with_fields_to_set: RefCell<FxHashMap<Type<'gcc>, (Struct<'gcc>, TyAndLayout<'tcx>)>>,
+
+ /// Cache instances of monomorphic and polymorphic items
+ pub instances: RefCell<FxHashMap<Instance<'tcx>, LValue<'gcc>>>,
+ /// Cache function instances of monomorphic and polymorphic items
+ pub function_instances: RefCell<FxHashMap<Instance<'tcx>, RValue<'gcc>>>,
+ /// Cache generated vtables
+ pub vtables: RefCell<FxHashMap<(Ty<'tcx>, Option<ty::PolyExistentialTraitRef<'tcx>>), RValue<'gcc>>>,
+
+ /// Cache of emitted const globals (value -> global)
+ pub const_globals: RefCell<FxHashMap<RValue<'gcc>, RValue<'gcc>>>,
+
+ /// Cache of constant strings,
+ pub const_cstr_cache: RefCell<FxHashMap<Symbol, LValue<'gcc>>>,
+
+ /// Cache of globals.
+ pub globals: RefCell<FxHashMap<String, RValue<'gcc>>>,
+
+ /// A counter that is used for generating local symbol names
+ local_gen_sym_counter: Cell<usize>,
+ pub global_gen_sym_counter: Cell<usize>,
+
+ eh_personality: Cell<Option<RValue<'gcc>>>,
+
+ pub pointee_infos: RefCell<FxHashMap<(Ty<'tcx>, Size), Option<PointeeInfo>>>,
+
+ /// NOTE: a hack is used because the rustc API is not suitable to libgccjit and as such,
+ /// `const_undef()` returns struct as pointer so that they can later be assigned a value.
+ /// As such, this set remembers which of these pointers were returned by this function so that
+ /// they can be deferenced later.
+ /// FIXME(antoyo): fix the rustc API to avoid having this hack.
+ pub structs_as_pointer: RefCell<FxHashSet<RValue<'gcc>>>,
+}
+
+impl<'gcc, 'tcx> CodegenCx<'gcc, 'tcx> {
+ pub fn new(context: &'gcc Context<'gcc>, codegen_unit: &'tcx CodegenUnit<'tcx>, tcx: TyCtxt<'tcx>) -> Self {
+ let check_overflow = tcx.sess.overflow_checks();
+ // TODO(antoyo): fix this mess. libgccjit seems to return random type when using new_int_type().
+ let isize_type = context.new_c_type(CType::LongLong);
+ let usize_type = context.new_c_type(CType::ULongLong);
+ let bool_type = context.new_type::<bool>();
+ let i8_type = context.new_type::<i8>();
+ let i16_type = context.new_type::<i16>();
+ let i32_type = context.new_type::<i32>();
+ let i64_type = context.new_c_type(CType::LongLong);
+ let i128_type = context.new_c_type(CType::Int128t).get_aligned(8); // TODO(antoyo): should the alignment be hard-coded?
+ let u8_type = context.new_type::<u8>();
+ let u16_type = context.new_type::<u16>();
+ let u32_type = context.new_type::<u32>();
+ let u64_type = context.new_c_type(CType::ULongLong);
+ let u128_type = context.new_c_type(CType::UInt128t).get_aligned(8); // TODO(antoyo): should the alignment be hard-coded?
+
+ let tls_model = to_gcc_tls_mode(tcx.sess.tls_model());
+
+ let float_type = context.new_type::<f32>();
+ let double_type = context.new_type::<f64>();
+
+ let int_type = context.new_c_type(CType::Int);
+ let uint_type = context.new_c_type(CType::UInt);
+ let long_type = context.new_c_type(CType::Long);
+ let ulong_type = context.new_c_type(CType::ULong);
+ let ulonglong_type = context.new_c_type(CType::ULongLong);
+ let sizet_type = context.new_c_type(CType::SizeT);
+
+ assert_eq!(isize_type, i64_type);
+ assert_eq!(usize_type, u64_type);
+
+ let mut functions = FxHashMap::default();
+ let builtins = [
+ "__builtin_unreachable", "abort", "__builtin_expect", "__builtin_add_overflow", "__builtin_mul_overflow",
+ "__builtin_saddll_overflow", /*"__builtin_sadd_overflow",*/ "__builtin_smulll_overflow", /*"__builtin_smul_overflow",*/
+ "__builtin_ssubll_overflow", /*"__builtin_ssub_overflow",*/ "__builtin_sub_overflow", "__builtin_uaddll_overflow",
+ "__builtin_uadd_overflow", "__builtin_umulll_overflow", "__builtin_umul_overflow", "__builtin_usubll_overflow",
+ "__builtin_usub_overflow", "sqrtf", "sqrt", "__builtin_powif", "__builtin_powi", "sinf", "sin", "cosf", "cos",
+ "powf", "pow", "expf", "exp", "exp2f", "exp2", "logf", "log", "log10f", "log10", "log2f", "log2", "fmaf",
+ "fma", "fabsf", "fabs", "fminf", "fmin", "fmaxf", "fmax", "copysignf", "copysign", "floorf", "floor", "ceilf",
+ "ceil", "truncf", "trunc", "rintf", "rint", "nearbyintf", "nearbyint", "roundf", "round",
+ "__builtin_expect_with_probability",
+ ];
+
+ for builtin in builtins.iter() {
+ functions.insert(builtin.to_string(), context.get_builtin_function(builtin));
+ }
+
+ Self {
+ check_overflow,
+ codegen_unit,
+ context,
+ current_block: RefCell::new(None),
+ current_func: RefCell::new(None),
+ normal_function_addresses: Default::default(),
+ functions: RefCell::new(functions),
+
+ tls_model,
+
+ bool_type,
+ i8_type,
+ i16_type,
+ i32_type,
+ i64_type,
+ i128_type,
+ isize_type,
+ usize_type,
+ u8_type,
+ u16_type,
+ u32_type,
+ u64_type,
+ u128_type,
+ int_type,
+ uint_type,
+ long_type,
+ ulong_type,
+ ulonglong_type,
+ sizet_type,
+
+ float_type,
+ double_type,
+
+ linkage: Cell::new(FunctionType::Internal),
+ instances: Default::default(),
+ function_instances: Default::default(),
+ vtables: Default::default(),
+ const_globals: Default::default(),
+ const_cstr_cache: Default::default(),
+ globals: Default::default(),
+ scalar_types: Default::default(),
+ types: Default::default(),
+ tcx,
+ struct_types: Default::default(),
+ types_with_fields_to_set: Default::default(),
+ local_gen_sym_counter: Cell::new(0),
+ global_gen_sym_counter: Cell::new(0),
+ eh_personality: Cell::new(None),
+ pointee_infos: Default::default(),
+ structs_as_pointer: Default::default(),
+ }
+ }
+
+ pub fn rvalue_as_function(&self, value: RValue<'gcc>) -> Function<'gcc> {
+ let function: Function<'gcc> = unsafe { std::mem::transmute(value) };
+ debug_assert!(self.functions.borrow().values().find(|value| **value == function).is_some(),
+ "{:?} ({:?}) is not a function", value, value.get_type());
+ function
+ }
+
+ pub fn sess(&self) -> &Session {
+ &self.tcx.sess
+ }
+}
+
+impl<'gcc, 'tcx> BackendTypes for CodegenCx<'gcc, 'tcx> {
+ type Value = RValue<'gcc>;
+ type Function = RValue<'gcc>;
+
+ type BasicBlock = Block<'gcc>;
+ type Type = Type<'gcc>;
+ type Funclet = (); // TODO(antoyo)
+
+ type DIScope = (); // TODO(antoyo)
+ type DILocation = (); // TODO(antoyo)
+ type DIVariable = (); // TODO(antoyo)
+}
+
+impl<'gcc, 'tcx> MiscMethods<'tcx> for CodegenCx<'gcc, 'tcx> {
+ fn vtables(&self) -> &RefCell<FxHashMap<(Ty<'tcx>, Option<PolyExistentialTraitRef<'tcx>>), RValue<'gcc>>> {
+ &self.vtables
+ }
+
+ fn get_fn(&self, instance: Instance<'tcx>) -> RValue<'gcc> {
+ let func = get_fn(self, instance);
+ *self.current_func.borrow_mut() = Some(self.rvalue_as_function(func));
+ func
+ }
+
+ fn get_fn_addr(&self, instance: Instance<'tcx>) -> RValue<'gcc> {
+ let func = get_fn(self, instance);
+ let func = self.rvalue_as_function(func);
+ let ptr = func.get_address(None);
+
+ // TODO(antoyo): don't do this twice: i.e. in declare_fn and here.
+ // FIXME(antoyo): the rustc API seems to call get_fn_addr() when not needed (e.g. for FFI).
+
+ self.normal_function_addresses.borrow_mut().insert(ptr);
+
+ ptr
+ }
+
+ fn eh_personality(&self) -> RValue<'gcc> {
+ // The exception handling personality function.
+ //
+ // If our compilation unit has the `eh_personality` lang item somewhere
+ // within it, then we just need to codegen that. Otherwise, we're
+ // building an rlib which will depend on some upstream implementation of
+ // this function, so we just codegen a generic reference to it. We don't
+ // specify any of the types for the function, we just make it a symbol
+ // that LLVM can later use.
+ //
+ // Note that MSVC is a little special here in that we don't use the
+ // `eh_personality` lang item at all. Currently LLVM has support for
+ // both Dwarf and SEH unwind mechanisms for MSVC targets and uses the
+ // *name of the personality function* to decide what kind of unwind side
+ // tables/landing pads to emit. It looks like Dwarf is used by default,
+ // injecting a dependency on the `_Unwind_Resume` symbol for resuming
+ // an "exception", but for MSVC we want to force SEH. This means that we
+ // can't actually have the personality function be our standard
+ // `rust_eh_personality` function, but rather we wired it up to the
+ // CRT's custom personality function, which forces LLVM to consider
+ // landing pads as "landing pads for SEH".
+ if let Some(llpersonality) = self.eh_personality.get() {
+ return llpersonality;
+ }
+ let tcx = self.tcx;
+ let llfn = match tcx.lang_items().eh_personality() {
+ Some(def_id) if !wants_msvc_seh(self.sess()) => self.get_fn_addr(
+ ty::Instance::resolve(
+ tcx,
+ ty::ParamEnv::reveal_all(),
+ def_id,
+ tcx.intern_substs(&[]),
+ )
+ .unwrap().unwrap(),
+ ),
+ _ => {
+ let _name = if wants_msvc_seh(self.sess()) {
+ "__CxxFrameHandler3"
+ } else {
+ "rust_eh_personality"
+ };
+ //let func = self.declare_func(name, self.type_i32(), &[], true);
+ // FIXME(antoyo): this hack should not be needed. That will probably be removed when
+ // unwinding support is added.
+ self.context.new_rvalue_from_int(self.int_type, 0)
+ }
+ };
+ // TODO(antoyo): apply target cpu attributes.
+ self.eh_personality.set(Some(llfn));
+ llfn
+ }
+
+ fn sess(&self) -> &Session {
+ &self.tcx.sess
+ }
+
+ fn check_overflow(&self) -> bool {
+ self.check_overflow
+ }
+
+ fn codegen_unit(&self) -> &'tcx CodegenUnit<'tcx> {
+ self.codegen_unit
+ }
+
+ fn used_statics(&self) -> &RefCell<Vec<RValue<'gcc>>> {
+ unimplemented!();
+ }
+
+ fn set_frame_pointer_type(&self, _llfn: RValue<'gcc>) {
+ // TODO(antoyo)
+ }
+
+ fn apply_target_cpu_attr(&self, _llfn: RValue<'gcc>) {
+ // TODO(antoyo)
+ }
+
+ fn create_used_variable(&self) {
+ unimplemented!();
+ }
+
+ fn declare_c_main(&self, fn_type: Self::Type) -> Option<Self::Function> {
+ if self.get_declared_value("main").is_none() {
+ Some(self.declare_cfn("main", fn_type))
+ }
+ else {
+ // If the symbol already exists, it is an error: for example, the user wrote
+ // #[no_mangle] extern "C" fn main(..) {..}
+ // instead of #[start]
+ None
+ }
+ }
+
+ fn compiler_used_statics(&self) -> &RefCell<Vec<RValue<'gcc>>> {
+ unimplemented!()
+ }
+
+ fn create_compiler_used_variable(&self) {
+ unimplemented!()
+ }
+}
+
+impl<'gcc, 'tcx> HasTyCtxt<'tcx> for CodegenCx<'gcc, 'tcx> {
+ fn tcx(&self) -> TyCtxt<'tcx> {
+ self.tcx
+ }
+}
+
+impl<'gcc, 'tcx> HasDataLayout for CodegenCx<'gcc, 'tcx> {
+ fn data_layout(&self) -> &TargetDataLayout {
+ &self.tcx.data_layout
+ }
+}
+
+impl<'gcc, 'tcx> HasTargetSpec for CodegenCx<'gcc, 'tcx> {
+ fn target_spec(&self) -> &Target {
+ &self.tcx.sess.target
+ }
+}
+
+impl<'gcc, 'tcx> LayoutOfHelpers<'tcx> for CodegenCx<'gcc, 'tcx> {
+ type LayoutOfResult = TyAndLayout<'tcx>;
+
+ #[inline]
+ fn handle_layout_err(&self, err: LayoutError<'tcx>, span: Span, ty: Ty<'tcx>) -> ! {
+ if let LayoutError::SizeOverflow(_) = err {
+ self.sess().span_fatal(span, &err.to_string())
+ } else {
+ span_bug!(span, "failed to get layout for `{}`: {}", ty, err)
+ }
+ }
+}
+
+impl<'gcc, 'tcx> FnAbiOfHelpers<'tcx> for CodegenCx<'gcc, 'tcx> {
+ type FnAbiOfResult = &'tcx FnAbi<'tcx, Ty<'tcx>>;
+
+ #[inline]
+ fn handle_fn_abi_err(
+ &self,
+ err: FnAbiError<'tcx>,
+ span: Span,
+ fn_abi_request: FnAbiRequest<'tcx>,
+ ) -> ! {
+ if let FnAbiError::Layout(LayoutError::SizeOverflow(_)) = err {
+ self.sess().span_fatal(span, &err.to_string())
+ } else {
+ match fn_abi_request {
+ FnAbiRequest::OfFnPtr { sig, extra_args } => {
+ span_bug!(
+ span,
+ "`fn_abi_of_fn_ptr({}, {:?})` failed: {}",
+ sig,
+ extra_args,
+ err
+ );
+ }
+ FnAbiRequest::OfInstance { instance, extra_args } => {
+ span_bug!(
+ span,
+ "`fn_abi_of_instance({}, {:?})` failed: {}",
+ instance,
+ extra_args,
+ err
+ );
+ }
+ }
+ }
+ }
+}
+
+impl<'tcx, 'gcc> HasParamEnv<'tcx> for CodegenCx<'gcc, 'tcx> {
+ fn param_env(&self) -> ParamEnv<'tcx> {
+ ParamEnv::reveal_all()
+ }
+}
+
+impl<'b, 'tcx> CodegenCx<'b, 'tcx> {
+ /// Generates a new symbol name with the given prefix. This symbol name must
+ /// only be used for definitions with `internal` or `private` linkage.
+ pub fn generate_local_symbol_name(&self, prefix: &str) -> String {
+ let idx = self.local_gen_sym_counter.get();
+ self.local_gen_sym_counter.set(idx + 1);
+ // Include a '.' character, so there can be no accidental conflicts with
+ // user defined names
+ let mut name = String::with_capacity(prefix.len() + 6);
+ name.push_str(prefix);
+ name.push_str(".");
+ base_n::push_str(idx as u128, base_n::ALPHANUMERIC_ONLY, &mut name);
+ name
+ }
+}
+
+pub fn unit_name<'tcx>(codegen_unit: &CodegenUnit<'tcx>) -> String {
+ let name = &codegen_unit.name().to_string();
+ mangle_name(&name.replace('-', "_"))
+}
+
+fn to_gcc_tls_mode(tls_model: TlsModel) -> gccjit::TlsModel {
+ match tls_model {
+ TlsModel::GeneralDynamic => gccjit::TlsModel::GlobalDynamic,
+ TlsModel::LocalDynamic => gccjit::TlsModel::LocalDynamic,
+ TlsModel::InitialExec => gccjit::TlsModel::InitialExec,
+ TlsModel::LocalExec => gccjit::TlsModel::LocalExec,
+ }
+}
--- /dev/null
+use gccjit::RValue;
+use rustc_codegen_ssa::traits::{CoverageInfoBuilderMethods, CoverageInfoMethods};
+use rustc_hir::def_id::DefId;
+use rustc_middle::mir::coverage::{
+ CodeRegion,
+ CounterValueReference,
+ ExpressionOperandId,
+ InjectedExpressionId,
+ Op,
+};
+use rustc_middle::ty::Instance;
+
+use crate::builder::Builder;
+use crate::context::CodegenCx;
+
+impl<'a, 'gcc, 'tcx> CoverageInfoBuilderMethods<'tcx> for Builder<'a, 'gcc, 'tcx> {
+ fn set_function_source_hash(
+ &mut self,
+ _instance: Instance<'tcx>,
+ _function_source_hash: u64,
+ ) -> bool {
+ unimplemented!();
+ }
+
+ fn add_coverage_counter(&mut self, _instance: Instance<'tcx>, _id: CounterValueReference, _region: CodeRegion) -> bool {
+ // TODO(antoyo)
+ false
+ }
+
+ fn add_coverage_counter_expression(&mut self, _instance: Instance<'tcx>, _id: InjectedExpressionId, _lhs: ExpressionOperandId, _op: Op, _rhs: ExpressionOperandId, _region: Option<CodeRegion>) -> bool {
+ // TODO(antoyo)
+ false
+ }
+
+ fn add_coverage_unreachable(&mut self, _instance: Instance<'tcx>, _region: CodeRegion) -> bool {
+ // TODO(antoyo)
+ false
+ }
+}
+
+impl<'gcc, 'tcx> CoverageInfoMethods<'tcx> for CodegenCx<'gcc, 'tcx> {
+ fn coverageinfo_finalize(&self) {
+ // TODO(antoyo)
+ }
+
+ fn get_pgo_func_name_var(&self, _instance: Instance<'tcx>) -> RValue<'gcc> {
+ unimplemented!();
+ }
+
+ /// Functions with MIR-based coverage are normally codegenned _only_ if
+ /// called. LLVM coverage tools typically expect every function to be
+ /// defined (even if unused), with at least one call to LLVM intrinsic
+ /// `instrprof.increment`.
+ ///
+ /// Codegen a small function that will never be called, with one counter
+ /// that will never be incremented.
+ ///
+ /// For used/called functions, the coverageinfo was already added to the
+ /// `function_coverage_map` (keyed by function `Instance`) during codegen.
+ /// But in this case, since the unused function was _not_ previously
+ /// codegenned, collect the coverage `CodeRegion`s from the MIR and add
+ /// them. The first `CodeRegion` is used to add a single counter, with the
+ /// same counter ID used in the injected `instrprof.increment` intrinsic
+ /// call. Since the function is never called, all other `CodeRegion`s can be
+ /// added as `unreachable_region`s.
+ fn define_unused_fn(&self, _def_id: DefId) {
+ unimplemented!();
+ }
+}
--- /dev/null
+use gccjit::RValue;
+use rustc_codegen_ssa::mir::debuginfo::{FunctionDebugContext, VariableKind};
+use rustc_codegen_ssa::traits::{DebugInfoBuilderMethods, DebugInfoMethods};
+use rustc_middle::mir;
+use rustc_middle::ty::{Instance, Ty};
+use rustc_span::{SourceFile, Span, Symbol};
+use rustc_target::abi::Size;
+use rustc_target::abi::call::FnAbi;
+
+use crate::builder::Builder;
+use crate::context::CodegenCx;
+
+impl<'a, 'gcc, 'tcx> DebugInfoBuilderMethods for Builder<'a, 'gcc, 'tcx> {
+ // FIXME(eddyb) find a common convention for all of the debuginfo-related
+ // names (choose between `dbg`, `debug`, `debuginfo`, `debug_info` etc.).
+ fn dbg_var_addr(&mut self, _dbg_var: Self::DIVariable, _scope_metadata: Self::DIScope, _variable_alloca: Self::Value, _direct_offset: Size, _indirect_offsets: &[Size]) {
+ unimplemented!();
+ }
+
+ fn insert_reference_to_gdb_debug_scripts_section_global(&mut self) {
+ // TODO(antoyo): insert reference to gdb debug scripts section global.
+ }
+
+ fn set_var_name(&mut self, _value: RValue<'gcc>, _name: &str) {
+ unimplemented!();
+ }
+
+ fn set_dbg_loc(&mut self, _dbg_loc: Self::DILocation) {
+ unimplemented!();
+ }
+}
+
+impl<'gcc, 'tcx> DebugInfoMethods<'tcx> for CodegenCx<'gcc, 'tcx> {
+ fn create_vtable_metadata(&self, _ty: Ty<'tcx>, _vtable: Self::Value) {
+ // TODO(antoyo)
+ }
+
+ fn create_function_debug_context(&self, _instance: Instance<'tcx>, _fn_abi: &FnAbi<'tcx, Ty<'tcx>>, _llfn: RValue<'gcc>, _mir: &mir::Body<'tcx>) -> Option<FunctionDebugContext<Self::DIScope, Self::DILocation>> {
+ // TODO(antoyo)
+ None
+ }
+
+ fn extend_scope_to_file(&self, _scope_metadata: Self::DIScope, _file: &SourceFile) -> Self::DIScope {
+ unimplemented!();
+ }
+
+ fn debuginfo_finalize(&self) {
+ // TODO(antoyo)
+ }
+
+ fn create_dbg_var(&self, _variable_name: Symbol, _variable_type: Ty<'tcx>, _scope_metadata: Self::DIScope, _variable_kind: VariableKind, _span: Span) -> Self::DIVariable {
+ unimplemented!();
+ }
+
+ fn dbg_scope_fn(&self, _instance: Instance<'tcx>, _fn_abi: &FnAbi<'tcx, Ty<'tcx>>, _maybe_definition_llfn: Option<RValue<'gcc>>) -> Self::DIScope {
+ unimplemented!();
+ }
+
+ fn dbg_loc(&self, _scope: Self::DIScope, _inlined_at: Option<Self::DILocation>, _span: Span) -> Self::DILocation {
+ unimplemented!();
+ }
+}
--- /dev/null
+use gccjit::{Function, FunctionType, GlobalKind, LValue, RValue, Type};
+use rustc_codegen_ssa::traits::BaseTypeMethods;
+use rustc_middle::ty::Ty;
+use rustc_span::Symbol;
+use rustc_target::abi::call::FnAbi;
+
+use crate::abi::FnAbiGccExt;
+use crate::context::{CodegenCx, unit_name};
+use crate::intrinsic::llvm;
+
+impl<'gcc, 'tcx> CodegenCx<'gcc, 'tcx> {
+ pub fn get_or_insert_global(&self, name: &str, ty: Type<'gcc>, is_tls: bool, link_section: Option<Symbol>) -> LValue<'gcc> {
+ if self.globals.borrow().contains_key(name) {
+ let typ = self.globals.borrow().get(name).expect("global").get_type();
+ let global = self.context.new_global(None, GlobalKind::Imported, typ, name);
+ if is_tls {
+ global.set_tls_model(self.tls_model);
+ }
+ if let Some(link_section) = link_section {
+ global.set_link_section(&link_section.as_str());
+ }
+ global
+ }
+ else {
+ self.declare_global(name, ty, is_tls, link_section)
+ }
+ }
+
+ pub fn declare_unnamed_global(&self, ty: Type<'gcc>) -> LValue<'gcc> {
+ let index = self.global_gen_sym_counter.get();
+ self.global_gen_sym_counter.set(index + 1);
+ let name = format!("global_{}_{}", index, unit_name(&self.codegen_unit));
+ self.context.new_global(None, GlobalKind::Exported, ty, &name)
+ }
+
+ pub fn declare_global_with_linkage(&self, name: &str, ty: Type<'gcc>, linkage: GlobalKind) -> LValue<'gcc> {
+ let global = self.context.new_global(None, linkage, ty, name);
+ let global_address = global.get_address(None);
+ self.globals.borrow_mut().insert(name.to_string(), global_address);
+ global
+ }
+
+ /*pub fn declare_func(&self, name: &str, return_type: Type<'gcc>, params: &[Type<'gcc>], variadic: bool) -> RValue<'gcc> {
+ self.linkage.set(FunctionType::Exported);
+ let func = declare_raw_fn(self, name, () /*llvm::CCallConv*/, return_type, params, variadic);
+ // FIXME(antoyo): this is a wrong cast. That requires changing the compiler API.
+ unsafe { std::mem::transmute(func) }
+ }*/
+
+ pub fn declare_global(&self, name: &str, ty: Type<'gcc>, is_tls: bool, link_section: Option<Symbol>) -> LValue<'gcc> {
+ let global = self.context.new_global(None, GlobalKind::Exported, ty, name);
+ if is_tls {
+ global.set_tls_model(self.tls_model);
+ }
+ if let Some(link_section) = link_section {
+ global.set_link_section(&link_section.as_str());
+ }
+ let global_address = global.get_address(None);
+ self.globals.borrow_mut().insert(name.to_string(), global_address);
+ global
+ }
+
+ pub fn declare_private_global(&self, name: &str, ty: Type<'gcc>) -> LValue<'gcc> {
+ let global = self.context.new_global(None, GlobalKind::Internal, ty, name);
+ let global_address = global.get_address(None);
+ self.globals.borrow_mut().insert(name.to_string(), global_address);
+ global
+ }
+
+ pub fn declare_cfn(&self, name: &str, _fn_type: Type<'gcc>) -> RValue<'gcc> {
+ // TODO(antoyo): use the fn_type parameter.
+ let const_string = self.context.new_type::<u8>().make_pointer().make_pointer();
+ let return_type = self.type_i32();
+ let variadic = false;
+ self.linkage.set(FunctionType::Exported);
+ let func = declare_raw_fn(self, name, () /*llvm::CCallConv*/, return_type, &[self.type_i32(), const_string], variadic);
+ // NOTE: it is needed to set the current_func here as well, because get_fn() is not called
+ // for the main function.
+ *self.current_func.borrow_mut() = Some(func);
+ // FIXME(antoyo): this is a wrong cast. That requires changing the compiler API.
+ unsafe { std::mem::transmute(func) }
+ }
+
+ pub fn declare_fn(&self, name: &str, fn_abi: &FnAbi<'tcx, Ty<'tcx>>) -> RValue<'gcc> {
+ let (return_type, params, variadic) = fn_abi.gcc_type(self);
+ let func = declare_raw_fn(self, name, () /*fn_abi.llvm_cconv()*/, return_type, ¶ms, variadic);
+ // FIXME(antoyo): this is a wrong cast. That requires changing the compiler API.
+ unsafe { std::mem::transmute(func) }
+ }
+
+ pub fn define_global(&self, name: &str, ty: Type<'gcc>, is_tls: bool, link_section: Option<Symbol>) -> LValue<'gcc> {
+ self.get_or_insert_global(name, ty, is_tls, link_section)
+ }
+
+ pub fn get_declared_value(&self, name: &str) -> Option<RValue<'gcc>> {
+ // TODO(antoyo): use a different field than globals, because this seems to return a function?
+ self.globals.borrow().get(name).cloned()
+ }
+}
+
+/// Declare a function.
+///
+/// If there’s a value with the same name already declared, the function will
+/// update the declaration and return existing Value instead.
+fn declare_raw_fn<'gcc>(cx: &CodegenCx<'gcc, '_>, name: &str, _callconv: () /*llvm::CallConv*/, return_type: Type<'gcc>, param_types: &[Type<'gcc>], variadic: bool) -> Function<'gcc> {
+ if name.starts_with("llvm.") {
+ return llvm::intrinsic(name, cx);
+ }
+ let func =
+ if cx.functions.borrow().contains_key(name) {
+ *cx.functions.borrow().get(name).expect("function")
+ }
+ else {
+ let params: Vec<_> = param_types.into_iter().enumerate()
+ .map(|(index, param)| cx.context.new_parameter(None, *param, &format!("param{}", index))) // TODO(antoyo): set name.
+ .collect();
+ let func = cx.context.new_function(None, cx.linkage.get(), return_type, ¶ms, mangle_name(name), variadic);
+ cx.functions.borrow_mut().insert(name.to_string(), func);
+ func
+ };
+
+ // TODO(antoyo): set function calling convention.
+ // TODO(antoyo): set unnamed address.
+ // TODO(antoyo): set no red zone function attribute.
+ // TODO(antoyo): set attributes for optimisation.
+ // TODO(antoyo): set attributes for non lazy bind.
+
+ // FIXME(antoyo): invalid cast.
+ func
+}
+
+// FIXME(antoyo): this is a hack because libgccjit currently only supports alpha, num and _.
+// Unsupported characters: `$` and `.`.
+pub fn mangle_name(name: &str) -> String {
+ name.replace(|char: char| {
+ if !char.is_alphanumeric() && char != '_' {
+ debug_assert!("$.".contains(char), "Unsupported char in function name: {}", char);
+ true
+ }
+ else {
+ false
+ }
+ }, "_")
+}
--- /dev/null
+use gccjit::Function;
+
+use crate::context::CodegenCx;
+
+pub fn intrinsic<'gcc, 'tcx>(name: &str, cx: &CodegenCx<'gcc, 'tcx>) -> Function<'gcc> {
+ let _gcc_name =
+ match name {
+ "llvm.x86.xgetbv" => {
+ let gcc_name = "__builtin_trap";
+ let func = cx.context.get_builtin_function(gcc_name);
+ cx.functions.borrow_mut().insert(gcc_name.to_string(), func);
+ return func;
+ },
+ // NOTE: this doc specifies the equivalent GCC builtins: http://huonw.github.io/llvmint/llvmint/x86/index.html
+ "llvm.x86.sse2.cmp.pd" => "__builtin_ia32_cmppd",
+ "llvm.x86.sse2.movmsk.pd" => "__builtin_ia32_movmskpd",
+ "llvm.x86.sse2.pmovmskb.128" => "__builtin_ia32_pmovmskb128",
+ _ => unimplemented!("unsupported LLVM intrinsic {}", name)
+ };
+
+ unimplemented!();
+}
--- /dev/null
+pub mod llvm;
+mod simd;
+
+use gccjit::{ComparisonOp, Function, RValue, ToRValue, Type, UnaryOp};
+use rustc_codegen_ssa::MemFlags;
+use rustc_codegen_ssa::base::wants_msvc_seh;
+use rustc_codegen_ssa::common::{IntPredicate, span_invalid_monomorphization_error};
+use rustc_codegen_ssa::mir::operand::{OperandRef, OperandValue};
+use rustc_codegen_ssa::mir::place::PlaceRef;
+use rustc_codegen_ssa::traits::{ArgAbiMethods, BaseTypeMethods, BuilderMethods, ConstMethods, IntrinsicCallMethods};
+use rustc_middle::bug;
+use rustc_middle::ty::{self, Instance, Ty};
+use rustc_middle::ty::layout::LayoutOf;
+use rustc_span::{Span, Symbol, symbol::kw, sym};
+use rustc_target::abi::HasDataLayout;
+use rustc_target::abi::call::{ArgAbi, FnAbi, PassMode};
+use rustc_target::spec::PanicStrategy;
+
+use crate::abi::GccType;
+use crate::builder::Builder;
+use crate::common::{SignType, TypeReflection};
+use crate::context::CodegenCx;
+use crate::type_of::LayoutGccExt;
+use crate::intrinsic::simd::generic_simd_intrinsic;
+
+fn get_simple_intrinsic<'gcc, 'tcx>(cx: &CodegenCx<'gcc, 'tcx>, name: Symbol) -> Option<Function<'gcc>> {
+ let gcc_name = match name {
+ sym::sqrtf32 => "sqrtf",
+ sym::sqrtf64 => "sqrt",
+ sym::powif32 => "__builtin_powif",
+ sym::powif64 => "__builtin_powi",
+ sym::sinf32 => "sinf",
+ sym::sinf64 => "sin",
+ sym::cosf32 => "cosf",
+ sym::cosf64 => "cos",
+ sym::powf32 => "powf",
+ sym::powf64 => "pow",
+ sym::expf32 => "expf",
+ sym::expf64 => "exp",
+ sym::exp2f32 => "exp2f",
+ sym::exp2f64 => "exp2",
+ sym::logf32 => "logf",
+ sym::logf64 => "log",
+ sym::log10f32 => "log10f",
+ sym::log10f64 => "log10",
+ sym::log2f32 => "log2f",
+ sym::log2f64 => "log2",
+ sym::fmaf32 => "fmaf",
+ sym::fmaf64 => "fma",
+ sym::fabsf32 => "fabsf",
+ sym::fabsf64 => "fabs",
+ sym::minnumf32 => "fminf",
+ sym::minnumf64 => "fmin",
+ sym::maxnumf32 => "fmaxf",
+ sym::maxnumf64 => "fmax",
+ sym::copysignf32 => "copysignf",
+ sym::copysignf64 => "copysign",
+ sym::floorf32 => "floorf",
+ sym::floorf64 => "floor",
+ sym::ceilf32 => "ceilf",
+ sym::ceilf64 => "ceil",
+ sym::truncf32 => "truncf",
+ sym::truncf64 => "trunc",
+ sym::rintf32 => "rintf",
+ sym::rintf64 => "rint",
+ sym::nearbyintf32 => "nearbyintf",
+ sym::nearbyintf64 => "nearbyint",
+ sym::roundf32 => "roundf",
+ sym::roundf64 => "round",
+ sym::abort => "abort",
+ _ => return None,
+ };
+ Some(cx.context.get_builtin_function(&gcc_name))
+}
+
+impl<'a, 'gcc, 'tcx> IntrinsicCallMethods<'tcx> for Builder<'a, 'gcc, 'tcx> {
+ fn codegen_intrinsic_call(&mut self, instance: Instance<'tcx>, fn_abi: &FnAbi<'tcx, Ty<'tcx>>, args: &[OperandRef<'tcx, RValue<'gcc>>], llresult: RValue<'gcc>, span: Span) {
+ let tcx = self.tcx;
+ let callee_ty = instance.ty(tcx, ty::ParamEnv::reveal_all());
+
+ let (def_id, substs) = match *callee_ty.kind() {
+ ty::FnDef(def_id, substs) => (def_id, substs),
+ _ => bug!("expected fn item type, found {}", callee_ty),
+ };
+
+ let sig = callee_ty.fn_sig(tcx);
+ let sig = tcx.normalize_erasing_late_bound_regions(ty::ParamEnv::reveal_all(), sig);
+ let arg_tys = sig.inputs();
+ let ret_ty = sig.output();
+ let name = tcx.item_name(def_id);
+ let name_str = &*name.as_str();
+
+ let llret_ty = self.layout_of(ret_ty).gcc_type(self, true);
+ let result = PlaceRef::new_sized(llresult, fn_abi.ret.layout);
+
+ let simple = get_simple_intrinsic(self, name);
+ let llval =
+ match name {
+ _ if simple.is_some() => {
+ // FIXME(antoyo): remove this cast when the API supports function.
+ let func = unsafe { std::mem::transmute(simple.expect("simple")) };
+ self.call(self.type_void(), func, &args.iter().map(|arg| arg.immediate()).collect::<Vec<_>>(), None)
+ },
+ sym::likely => {
+ self.expect(args[0].immediate(), true)
+ }
+ sym::unlikely => {
+ self.expect(args[0].immediate(), false)
+ }
+ kw::Try => {
+ try_intrinsic(
+ self,
+ args[0].immediate(),
+ args[1].immediate(),
+ args[2].immediate(),
+ llresult,
+ );
+ return;
+ }
+ sym::breakpoint => {
+ unimplemented!();
+ }
+ sym::va_copy => {
+ unimplemented!();
+ }
+ sym::va_arg => {
+ unimplemented!();
+ }
+
+ sym::volatile_load | sym::unaligned_volatile_load => {
+ let tp_ty = substs.type_at(0);
+ let mut ptr = args[0].immediate();
+ if let PassMode::Cast(ty) = fn_abi.ret.mode {
+ ptr = self.pointercast(ptr, self.type_ptr_to(ty.gcc_type(self)));
+ }
+ let load = self.volatile_load(ptr.get_type(), ptr);
+ // TODO(antoyo): set alignment.
+ self.to_immediate(load, self.layout_of(tp_ty))
+ }
+ sym::volatile_store => {
+ let dst = args[0].deref(self.cx());
+ args[1].val.volatile_store(self, dst);
+ return;
+ }
+ sym::unaligned_volatile_store => {
+ let dst = args[0].deref(self.cx());
+ args[1].val.unaligned_volatile_store(self, dst);
+ return;
+ }
+ sym::prefetch_read_data
+ | sym::prefetch_write_data
+ | sym::prefetch_read_instruction
+ | sym::prefetch_write_instruction => {
+ unimplemented!();
+ }
+ sym::ctlz
+ | sym::ctlz_nonzero
+ | sym::cttz
+ | sym::cttz_nonzero
+ | sym::ctpop
+ | sym::bswap
+ | sym::bitreverse
+ | sym::rotate_left
+ | sym::rotate_right
+ | sym::saturating_add
+ | sym::saturating_sub => {
+ let ty = arg_tys[0];
+ match int_type_width_signed(ty, self) {
+ Some((width, signed)) => match name {
+ sym::ctlz | sym::cttz => {
+ let func = self.current_func.borrow().expect("func");
+ let then_block = func.new_block("then");
+ let else_block = func.new_block("else");
+ let after_block = func.new_block("after");
+
+ let arg = args[0].immediate();
+ let result = func.new_local(None, arg.get_type(), "zeros");
+ let zero = self.cx.context.new_rvalue_zero(arg.get_type());
+ let cond = self.cx.context.new_comparison(None, ComparisonOp::Equals, arg, zero);
+ self.llbb().end_with_conditional(None, cond, then_block, else_block);
+
+ let zero_result = self.cx.context.new_rvalue_from_long(arg.get_type(), width as i64);
+ then_block.add_assignment(None, result, zero_result);
+ then_block.end_with_jump(None, after_block);
+
+ // NOTE: since jumps were added in a place
+ // count_leading_zeroes() does not expect, the current blocks
+ // in the state need to be updated.
+ *self.current_block.borrow_mut() = Some(else_block);
+ self.block = Some(else_block);
+
+ let zeros =
+ match name {
+ sym::ctlz => self.count_leading_zeroes(width, arg),
+ sym::cttz => self.count_trailing_zeroes(width, arg),
+ _ => unreachable!(),
+ };
+ else_block.add_assignment(None, result, zeros);
+ else_block.end_with_jump(None, after_block);
+
+ // NOTE: since jumps were added in a place rustc does not
+ // expect, the current blocks in the state need to be updated.
+ *self.current_block.borrow_mut() = Some(after_block);
+ self.block = Some(after_block);
+
+ result.to_rvalue()
+ }
+ sym::ctlz_nonzero => {
+ self.count_leading_zeroes(width, args[0].immediate())
+ },
+ sym::cttz_nonzero => {
+ self.count_trailing_zeroes(width, args[0].immediate())
+ }
+ sym::ctpop => self.pop_count(args[0].immediate()),
+ sym::bswap => {
+ if width == 8 {
+ args[0].immediate() // byte swap a u8/i8 is just a no-op
+ }
+ else {
+ // TODO(antoyo): check if it's faster to use string literals and a
+ // match instead of format!.
+ let bswap = self.cx.context.get_builtin_function(&format!("__builtin_bswap{}", width));
+ let mut arg = args[0].immediate();
+ // FIXME(antoyo): this cast should not be necessary. Remove
+ // when having proper sized integer types.
+ let param_type = bswap.get_param(0).to_rvalue().get_type();
+ if param_type != arg.get_type() {
+ arg = self.bitcast(arg, param_type);
+ }
+ self.cx.context.new_call(None, bswap, &[arg])
+ }
+ },
+ sym::bitreverse => self.bit_reverse(width, args[0].immediate()),
+ sym::rotate_left | sym::rotate_right => {
+ // TODO(antoyo): implement using algorithm from:
+ // https://blog.regehr.org/archives/1063
+ // for other platforms.
+ let is_left = name == sym::rotate_left;
+ let val = args[0].immediate();
+ let raw_shift = args[1].immediate();
+ if is_left {
+ self.rotate_left(val, raw_shift, width)
+ }
+ else {
+ self.rotate_right(val, raw_shift, width)
+ }
+ },
+ sym::saturating_add => {
+ self.saturating_add(args[0].immediate(), args[1].immediate(), signed, width)
+ },
+ sym::saturating_sub => {
+ self.saturating_sub(args[0].immediate(), args[1].immediate(), signed, width)
+ },
+ _ => bug!(),
+ },
+ None => {
+ span_invalid_monomorphization_error(
+ tcx.sess,
+ span,
+ &format!(
+ "invalid monomorphization of `{}` intrinsic: \
+ expected basic integer type, found `{}`",
+ name, ty
+ ),
+ );
+ return;
+ }
+ }
+ }
+
+ sym::raw_eq => {
+ use rustc_target::abi::Abi::*;
+ let tp_ty = substs.type_at(0);
+ let layout = self.layout_of(tp_ty).layout;
+ let _use_integer_compare = match layout.abi {
+ Scalar(_) | ScalarPair(_, _) => true,
+ Uninhabited | Vector { .. } => false,
+ Aggregate { .. } => {
+ // For rusty ABIs, small aggregates are actually passed
+ // as `RegKind::Integer` (see `FnAbi::adjust_for_abi`),
+ // so we re-use that same threshold here.
+ layout.size <= self.data_layout().pointer_size * 2
+ }
+ };
+
+ let a = args[0].immediate();
+ let b = args[1].immediate();
+ if layout.size.bytes() == 0 {
+ self.const_bool(true)
+ }
+ /*else if use_integer_compare {
+ let integer_ty = self.type_ix(layout.size.bits()); // FIXME(antoyo): LLVM creates an integer of 96 bits for [i32; 3], but gcc doesn't support this, so it creates an integer of 128 bits.
+ let ptr_ty = self.type_ptr_to(integer_ty);
+ let a_ptr = self.bitcast(a, ptr_ty);
+ let a_val = self.load(integer_ty, a_ptr, layout.align.abi);
+ let b_ptr = self.bitcast(b, ptr_ty);
+ let b_val = self.load(integer_ty, b_ptr, layout.align.abi);
+ self.icmp(IntPredicate::IntEQ, a_val, b_val)
+ }*/
+ else {
+ let void_ptr_type = self.context.new_type::<*const ()>();
+ let a_ptr = self.bitcast(a, void_ptr_type);
+ let b_ptr = self.bitcast(b, void_ptr_type);
+ let n = self.context.new_cast(None, self.const_usize(layout.size.bytes()), self.sizet_type);
+ let builtin = self.context.get_builtin_function("memcmp");
+ let cmp = self.context.new_call(None, builtin, &[a_ptr, b_ptr, n]);
+ self.icmp(IntPredicate::IntEQ, cmp, self.const_i32(0))
+ }
+ }
+
+ sym::black_box => {
+ args[0].val.store(self, result);
+
+ let block = self.llbb();
+ let extended_asm = block.add_extended_asm(None, "");
+ extended_asm.add_input_operand(None, "r", result.llval);
+ extended_asm.add_clobber("memory");
+ extended_asm.set_volatile_flag(true);
+
+ // We have copied the value to `result` already.
+ return;
+ }
+
+ _ if name_str.starts_with("simd_") => {
+ match generic_simd_intrinsic(self, name, callee_ty, args, ret_ty, llret_ty, span) {
+ Ok(llval) => llval,
+ Err(()) => return,
+ }
+ }
+
+ _ => bug!("unknown intrinsic '{}'", name),
+ };
+
+ if !fn_abi.ret.is_ignore() {
+ if let PassMode::Cast(ty) = fn_abi.ret.mode {
+ let ptr_llty = self.type_ptr_to(ty.gcc_type(self));
+ let ptr = self.pointercast(result.llval, ptr_llty);
+ self.store(llval, ptr, result.align);
+ }
+ else {
+ OperandRef::from_immediate_or_packed_pair(self, llval, result.layout)
+ .val
+ .store(self, result);
+ }
+ }
+ }
+
+ fn abort(&mut self) {
+ let func = self.context.get_builtin_function("abort");
+ let func: RValue<'gcc> = unsafe { std::mem::transmute(func) };
+ self.call(self.type_void(), func, &[], None);
+ }
+
+ fn assume(&mut self, value: Self::Value) {
+ // TODO(antoyo): switch to asumme when it exists.
+ // Or use something like this:
+ // #define __assume(cond) do { if (!(cond)) __builtin_unreachable(); } while (0)
+ self.expect(value, true);
+ }
+
+ fn expect(&mut self, cond: Self::Value, _expected: bool) -> Self::Value {
+ // TODO(antoyo)
+ cond
+ }
+
+ fn sideeffect(&mut self) {
+ // TODO(antoyo)
+ }
+
+ fn va_start(&mut self, _va_list: RValue<'gcc>) -> RValue<'gcc> {
+ unimplemented!();
+ }
+
+ fn va_end(&mut self, _va_list: RValue<'gcc>) -> RValue<'gcc> {
+ unimplemented!();
+ }
+}
+
+impl<'a, 'gcc, 'tcx> ArgAbiMethods<'tcx> for Builder<'a, 'gcc, 'tcx> {
+ fn store_fn_arg(&mut self, arg_abi: &ArgAbi<'tcx, Ty<'tcx>>, idx: &mut usize, dst: PlaceRef<'tcx, Self::Value>) {
+ arg_abi.store_fn_arg(self, idx, dst)
+ }
+
+ fn store_arg(&mut self, arg_abi: &ArgAbi<'tcx, Ty<'tcx>>, val: RValue<'gcc>, dst: PlaceRef<'tcx, RValue<'gcc>>) {
+ arg_abi.store(self, val, dst)
+ }
+
+ fn arg_memory_ty(&self, arg_abi: &ArgAbi<'tcx, Ty<'tcx>>) -> Type<'gcc> {
+ arg_abi.memory_ty(self)
+ }
+}
+
+pub trait ArgAbiExt<'gcc, 'tcx> {
+ fn memory_ty(&self, cx: &CodegenCx<'gcc, 'tcx>) -> Type<'gcc>;
+ fn store(&self, bx: &mut Builder<'_, 'gcc, 'tcx>, val: RValue<'gcc>, dst: PlaceRef<'tcx, RValue<'gcc>>);
+ fn store_fn_arg(&self, bx: &mut Builder<'_, 'gcc, 'tcx>, idx: &mut usize, dst: PlaceRef<'tcx, RValue<'gcc>>);
+}
+
+impl<'gcc, 'tcx> ArgAbiExt<'gcc, 'tcx> for ArgAbi<'tcx, Ty<'tcx>> {
+ /// Gets the LLVM type for a place of the original Rust type of
+ /// this argument/return, i.e., the result of `type_of::type_of`.
+ fn memory_ty(&self, cx: &CodegenCx<'gcc, 'tcx>) -> Type<'gcc> {
+ self.layout.gcc_type(cx, true)
+ }
+
+ /// Stores a direct/indirect value described by this ArgAbi into a
+ /// place for the original Rust type of this argument/return.
+ /// Can be used for both storing formal arguments into Rust variables
+ /// or results of call/invoke instructions into their destinations.
+ fn store(&self, bx: &mut Builder<'_, 'gcc, 'tcx>, val: RValue<'gcc>, dst: PlaceRef<'tcx, RValue<'gcc>>) {
+ if self.is_ignore() {
+ return;
+ }
+ if self.is_sized_indirect() {
+ OperandValue::Ref(val, None, self.layout.align.abi).store(bx, dst)
+ }
+ else if self.is_unsized_indirect() {
+ bug!("unsized `ArgAbi` must be handled through `store_fn_arg`");
+ }
+ else if let PassMode::Cast(cast) = self.mode {
+ // FIXME(eddyb): Figure out when the simpler Store is safe, clang
+ // uses it for i16 -> {i8, i8}, but not for i24 -> {i8, i8, i8}.
+ let can_store_through_cast_ptr = false;
+ if can_store_through_cast_ptr {
+ let cast_ptr_llty = bx.type_ptr_to(cast.gcc_type(bx));
+ let cast_dst = bx.pointercast(dst.llval, cast_ptr_llty);
+ bx.store(val, cast_dst, self.layout.align.abi);
+ }
+ else {
+ // The actual return type is a struct, but the ABI
+ // adaptation code has cast it into some scalar type. The
+ // code that follows is the only reliable way I have
+ // found to do a transform like i64 -> {i32,i32}.
+ // Basically we dump the data onto the stack then memcpy it.
+ //
+ // Other approaches I tried:
+ // - Casting rust ret pointer to the foreign type and using Store
+ // is (a) unsafe if size of foreign type > size of rust type and
+ // (b) runs afoul of strict aliasing rules, yielding invalid
+ // assembly under -O (specifically, the store gets removed).
+ // - Truncating foreign type to correct integral type and then
+ // bitcasting to the struct type yields invalid cast errors.
+
+ // We instead thus allocate some scratch space...
+ let scratch_size = cast.size(bx);
+ let scratch_align = cast.align(bx);
+ let llscratch = bx.alloca(cast.gcc_type(bx), scratch_align);
+ bx.lifetime_start(llscratch, scratch_size);
+
+ // ... where we first store the value...
+ bx.store(val, llscratch, scratch_align);
+
+ // ... and then memcpy it to the intended destination.
+ bx.memcpy(
+ dst.llval,
+ self.layout.align.abi,
+ llscratch,
+ scratch_align,
+ bx.const_usize(self.layout.size.bytes()),
+ MemFlags::empty(),
+ );
+
+ bx.lifetime_end(llscratch, scratch_size);
+ }
+ }
+ else {
+ OperandValue::Immediate(val).store(bx, dst);
+ }
+ }
+
+ fn store_fn_arg<'a>(&self, bx: &mut Builder<'a, 'gcc, 'tcx>, idx: &mut usize, dst: PlaceRef<'tcx, RValue<'gcc>>) {
+ let mut next = || {
+ let val = bx.current_func().get_param(*idx as i32);
+ *idx += 1;
+ val.to_rvalue()
+ };
+ match self.mode {
+ PassMode::Ignore => {}
+ PassMode::Pair(..) => {
+ OperandValue::Pair(next(), next()).store(bx, dst);
+ }
+ PassMode::Indirect { extra_attrs: Some(_), .. } => {
+ OperandValue::Ref(next(), Some(next()), self.layout.align.abi).store(bx, dst);
+ }
+ PassMode::Direct(_) | PassMode::Indirect { extra_attrs: None, .. } | PassMode::Cast(_) => {
+ let next_arg = next();
+ self.store(bx, next_arg.to_rvalue(), dst);
+ }
+ }
+ }
+}
+
+fn int_type_width_signed<'gcc, 'tcx>(ty: Ty<'tcx>, cx: &CodegenCx<'gcc, 'tcx>) -> Option<(u64, bool)> {
+ match ty.kind() {
+ ty::Int(t) => Some((
+ match t {
+ rustc_middle::ty::IntTy::Isize => u64::from(cx.tcx.sess.target.pointer_width),
+ rustc_middle::ty::IntTy::I8 => 8,
+ rustc_middle::ty::IntTy::I16 => 16,
+ rustc_middle::ty::IntTy::I32 => 32,
+ rustc_middle::ty::IntTy::I64 => 64,
+ rustc_middle::ty::IntTy::I128 => 128,
+ },
+ true,
+ )),
+ ty::Uint(t) => Some((
+ match t {
+ rustc_middle::ty::UintTy::Usize => u64::from(cx.tcx.sess.target.pointer_width),
+ rustc_middle::ty::UintTy::U8 => 8,
+ rustc_middle::ty::UintTy::U16 => 16,
+ rustc_middle::ty::UintTy::U32 => 32,
+ rustc_middle::ty::UintTy::U64 => 64,
+ rustc_middle::ty::UintTy::U128 => 128,
+ },
+ false,
+ )),
+ _ => None,
+ }
+}
+
+impl<'a, 'gcc, 'tcx> Builder<'a, 'gcc, 'tcx> {
+ fn bit_reverse(&mut self, width: u64, value: RValue<'gcc>) -> RValue<'gcc> {
+ let result_type = value.get_type();
+ let typ = result_type.to_unsigned(self.cx);
+
+ let value =
+ if result_type.is_signed(self.cx) {
+ self.context.new_bitcast(None, value, typ)
+ }
+ else {
+ value
+ };
+
+ let context = &self.cx.context;
+ let result =
+ match width {
+ 8 => {
+ // First step.
+ let left = self.and(value, context.new_rvalue_from_int(typ, 0xF0));
+ let left = self.lshr(left, context.new_rvalue_from_int(typ, 4));
+ let right = self.and(value, context.new_rvalue_from_int(typ, 0x0F));
+ let right = self.shl(right, context.new_rvalue_from_int(typ, 4));
+ let step1 = self.or(left, right);
+
+ // Second step.
+ let left = self.and(step1, context.new_rvalue_from_int(typ, 0xCC));
+ let left = self.lshr(left, context.new_rvalue_from_int(typ, 2));
+ let right = self.and(step1, context.new_rvalue_from_int(typ, 0x33));
+ let right = self.shl(right, context.new_rvalue_from_int(typ, 2));
+ let step2 = self.or(left, right);
+
+ // Third step.
+ let left = self.and(step2, context.new_rvalue_from_int(typ, 0xAA));
+ let left = self.lshr(left, context.new_rvalue_from_int(typ, 1));
+ let right = self.and(step2, context.new_rvalue_from_int(typ, 0x55));
+ let right = self.shl(right, context.new_rvalue_from_int(typ, 1));
+ let step3 = self.or(left, right);
+
+ step3
+ },
+ 16 => {
+ // First step.
+ let left = self.and(value, context.new_rvalue_from_int(typ, 0x5555));
+ let left = self.shl(left, context.new_rvalue_from_int(typ, 1));
+ let right = self.and(value, context.new_rvalue_from_int(typ, 0xAAAA));
+ let right = self.lshr(right, context.new_rvalue_from_int(typ, 1));
+ let step1 = self.or(left, right);
+
+ // Second step.
+ let left = self.and(step1, context.new_rvalue_from_int(typ, 0x3333));
+ let left = self.shl(left, context.new_rvalue_from_int(typ, 2));
+ let right = self.and(step1, context.new_rvalue_from_int(typ, 0xCCCC));
+ let right = self.lshr(right, context.new_rvalue_from_int(typ, 2));
+ let step2 = self.or(left, right);
+
+ // Third step.
+ let left = self.and(step2, context.new_rvalue_from_int(typ, 0x0F0F));
+ let left = self.shl(left, context.new_rvalue_from_int(typ, 4));
+ let right = self.and(step2, context.new_rvalue_from_int(typ, 0xF0F0));
+ let right = self.lshr(right, context.new_rvalue_from_int(typ, 4));
+ let step3 = self.or(left, right);
+
+ // Fourth step.
+ let left = self.and(step3, context.new_rvalue_from_int(typ, 0x00FF));
+ let left = self.shl(left, context.new_rvalue_from_int(typ, 8));
+ let right = self.and(step3, context.new_rvalue_from_int(typ, 0xFF00));
+ let right = self.lshr(right, context.new_rvalue_from_int(typ, 8));
+ let step4 = self.or(left, right);
+
+ step4
+ },
+ 32 => {
+ // TODO(antoyo): Refactor with other implementations.
+ // First step.
+ let left = self.and(value, context.new_rvalue_from_long(typ, 0x55555555));
+ let left = self.shl(left, context.new_rvalue_from_long(typ, 1));
+ let right = self.and(value, context.new_rvalue_from_long(typ, 0xAAAAAAAA));
+ let right = self.lshr(right, context.new_rvalue_from_long(typ, 1));
+ let step1 = self.or(left, right);
+
+ // Second step.
+ let left = self.and(step1, context.new_rvalue_from_long(typ, 0x33333333));
+ let left = self.shl(left, context.new_rvalue_from_long(typ, 2));
+ let right = self.and(step1, context.new_rvalue_from_long(typ, 0xCCCCCCCC));
+ let right = self.lshr(right, context.new_rvalue_from_long(typ, 2));
+ let step2 = self.or(left, right);
+
+ // Third step.
+ let left = self.and(step2, context.new_rvalue_from_long(typ, 0x0F0F0F0F));
+ let left = self.shl(left, context.new_rvalue_from_long(typ, 4));
+ let right = self.and(step2, context.new_rvalue_from_long(typ, 0xF0F0F0F0));
+ let right = self.lshr(right, context.new_rvalue_from_long(typ, 4));
+ let step3 = self.or(left, right);
+
+ // Fourth step.
+ let left = self.and(step3, context.new_rvalue_from_long(typ, 0x00FF00FF));
+ let left = self.shl(left, context.new_rvalue_from_long(typ, 8));
+ let right = self.and(step3, context.new_rvalue_from_long(typ, 0xFF00FF00));
+ let right = self.lshr(right, context.new_rvalue_from_long(typ, 8));
+ let step4 = self.or(left, right);
+
+ // Fifth step.
+ let left = self.and(step4, context.new_rvalue_from_long(typ, 0x0000FFFF));
+ let left = self.shl(left, context.new_rvalue_from_long(typ, 16));
+ let right = self.and(step4, context.new_rvalue_from_long(typ, 0xFFFF0000));
+ let right = self.lshr(right, context.new_rvalue_from_long(typ, 16));
+ let step5 = self.or(left, right);
+
+ step5
+ },
+ 64 => {
+ // First step.
+ let left = self.shl(value, context.new_rvalue_from_long(typ, 32));
+ let right = self.lshr(value, context.new_rvalue_from_long(typ, 32));
+ let step1 = self.or(left, right);
+
+ // Second step.
+ let left = self.and(step1, context.new_rvalue_from_long(typ, 0x0001FFFF0001FFFF));
+ let left = self.shl(left, context.new_rvalue_from_long(typ, 15));
+ let right = self.and(step1, context.new_rvalue_from_long(typ, 0xFFFE0000FFFE0000u64 as i64)); // TODO(antoyo): transmute the number instead?
+ let right = self.lshr(right, context.new_rvalue_from_long(typ, 17));
+ let step2 = self.or(left, right);
+
+ // Third step.
+ let left = self.lshr(step2, context.new_rvalue_from_long(typ, 10));
+ let left = self.xor(step2, left);
+ let temp = self.and(left, context.new_rvalue_from_long(typ, 0x003F801F003F801F));
+
+ let left = self.shl(temp, context.new_rvalue_from_long(typ, 10));
+ let left = self.or(temp, left);
+ let step3 = self.xor(left, step2);
+
+ // Fourth step.
+ let left = self.lshr(step3, context.new_rvalue_from_long(typ, 4));
+ let left = self.xor(step3, left);
+ let temp = self.and(left, context.new_rvalue_from_long(typ, 0x0E0384210E038421));
+
+ let left = self.shl(temp, context.new_rvalue_from_long(typ, 4));
+ let left = self.or(temp, left);
+ let step4 = self.xor(left, step3);
+
+ // Fifth step.
+ let left = self.lshr(step4, context.new_rvalue_from_long(typ, 2));
+ let left = self.xor(step4, left);
+ let temp = self.and(left, context.new_rvalue_from_long(typ, 0x2248884222488842));
+
+ let left = self.shl(temp, context.new_rvalue_from_long(typ, 2));
+ let left = self.or(temp, left);
+ let step5 = self.xor(left, step4);
+
+ step5
+ },
+ 128 => {
+ // TODO(antoyo): find a more efficient implementation?
+ let sixty_four = self.context.new_rvalue_from_long(typ, 64);
+ let high = self.context.new_cast(None, value >> sixty_four, self.u64_type);
+ let low = self.context.new_cast(None, value, self.u64_type);
+
+ let reversed_high = self.bit_reverse(64, high);
+ let reversed_low = self.bit_reverse(64, low);
+
+ let new_low = self.context.new_cast(None, reversed_high, typ);
+ let new_high = self.context.new_cast(None, reversed_low, typ) << sixty_four;
+
+ new_low | new_high
+ },
+ _ => {
+ panic!("cannot bit reverse with width = {}", width);
+ },
+ };
+
+ self.context.new_bitcast(None, result, result_type)
+ }
+
+ fn count_leading_zeroes(&self, width: u64, arg: RValue<'gcc>) -> RValue<'gcc> {
+ // TODO(antoyo): use width?
+ let arg_type = arg.get_type();
+ let count_leading_zeroes =
+ if arg_type.is_uint(&self.cx) {
+ "__builtin_clz"
+ }
+ else if arg_type.is_ulong(&self.cx) {
+ "__builtin_clzl"
+ }
+ else if arg_type.is_ulonglong(&self.cx) {
+ "__builtin_clzll"
+ }
+ else if width == 128 {
+ // Algorithm from: https://stackoverflow.com/a/28433850/389119
+ let array_type = self.context.new_array_type(None, arg_type, 3);
+ let result = self.current_func()
+ .new_local(None, array_type, "count_loading_zeroes_results");
+
+ let sixty_four = self.context.new_rvalue_from_long(arg_type, 64);
+ let high = self.context.new_cast(None, arg >> sixty_four, self.u64_type);
+ let low = self.context.new_cast(None, arg, self.u64_type);
+
+ let zero = self.context.new_rvalue_zero(self.usize_type);
+ let one = self.context.new_rvalue_one(self.usize_type);
+ let two = self.context.new_rvalue_from_long(self.usize_type, 2);
+
+ let clzll = self.context.get_builtin_function("__builtin_clzll");
+
+ let first_elem = self.context.new_array_access(None, result, zero);
+ let first_value = self.context.new_cast(None, self.context.new_call(None, clzll, &[high]), arg_type);
+ self.llbb()
+ .add_assignment(None, first_elem, first_value);
+
+ let second_elem = self.context.new_array_access(None, result, one);
+ let second_value = self.context.new_cast(None, self.context.new_call(None, clzll, &[low]), arg_type) + sixty_four;
+ self.llbb()
+ .add_assignment(None, second_elem, second_value);
+
+ let third_elem = self.context.new_array_access(None, result, two);
+ let third_value = self.context.new_rvalue_from_long(arg_type, 128);
+ self.llbb()
+ .add_assignment(None, third_elem, third_value);
+
+ let not_high = self.context.new_unary_op(None, UnaryOp::LogicalNegate, self.u64_type, high);
+ let not_low = self.context.new_unary_op(None, UnaryOp::LogicalNegate, self.u64_type, low);
+ let not_low_and_not_high = not_low & not_high;
+ let index = not_high + not_low_and_not_high;
+
+ let res = self.context.new_array_access(None, result, index);
+
+ return self.context.new_cast(None, res, arg_type);
+ }
+ else {
+ let count_leading_zeroes = self.context.get_builtin_function("__builtin_clz");
+ let arg = self.context.new_cast(None, arg, self.uint_type);
+ let diff = self.int_width(self.uint_type) - self.int_width(arg_type);
+ let diff = self.context.new_rvalue_from_long(self.int_type, diff);
+ let res = self.context.new_call(None, count_leading_zeroes, &[arg]) - diff;
+ return self.context.new_cast(None, res, arg_type);
+ };
+ let count_leading_zeroes = self.context.get_builtin_function(count_leading_zeroes);
+ let res = self.context.new_call(None, count_leading_zeroes, &[arg]);
+ self.context.new_cast(None, res, arg_type)
+ }
+
+ fn count_trailing_zeroes(&self, _width: u64, arg: RValue<'gcc>) -> RValue<'gcc> {
+ let result_type = arg.get_type();
+ let arg =
+ if result_type.is_signed(self.cx) {
+ let new_type = result_type.to_unsigned(self.cx);
+ self.context.new_bitcast(None, arg, new_type)
+ }
+ else {
+ arg
+ };
+ let arg_type = arg.get_type();
+ let (count_trailing_zeroes, expected_type) =
+ if arg_type.is_uchar(&self.cx) || arg_type.is_ushort(&self.cx) || arg_type.is_uint(&self.cx) {
+ // NOTE: we don't need to & 0xFF for uchar because the result is undefined on zero.
+ ("__builtin_ctz", self.cx.uint_type)
+ }
+ else if arg_type.is_ulong(&self.cx) {
+ ("__builtin_ctzl", self.cx.ulong_type)
+ }
+ else if arg_type.is_ulonglong(&self.cx) {
+ ("__builtin_ctzll", self.cx.ulonglong_type)
+ }
+ else if arg_type.is_u128(&self.cx) {
+ // Adapted from the algorithm to count leading zeroes from: https://stackoverflow.com/a/28433850/389119
+ let array_type = self.context.new_array_type(None, arg_type, 3);
+ let result = self.current_func()
+ .new_local(None, array_type, "count_loading_zeroes_results");
+
+ let sixty_four = self.context.new_rvalue_from_long(arg_type, 64);
+ let high = self.context.new_cast(None, arg >> sixty_four, self.u64_type);
+ let low = self.context.new_cast(None, arg, self.u64_type);
+
+ let zero = self.context.new_rvalue_zero(self.usize_type);
+ let one = self.context.new_rvalue_one(self.usize_type);
+ let two = self.context.new_rvalue_from_long(self.usize_type, 2);
+
+ let ctzll = self.context.get_builtin_function("__builtin_ctzll");
+
+ let first_elem = self.context.new_array_access(None, result, zero);
+ let first_value = self.context.new_cast(None, self.context.new_call(None, ctzll, &[low]), arg_type);
+ self.llbb()
+ .add_assignment(None, first_elem, first_value);
+
+ let second_elem = self.context.new_array_access(None, result, one);
+ let second_value = self.context.new_cast(None, self.context.new_call(None, ctzll, &[high]), arg_type) + sixty_four;
+ self.llbb()
+ .add_assignment(None, second_elem, second_value);
+
+ let third_elem = self.context.new_array_access(None, result, two);
+ let third_value = self.context.new_rvalue_from_long(arg_type, 128);
+ self.llbb()
+ .add_assignment(None, third_elem, third_value);
+
+ let not_low = self.context.new_unary_op(None, UnaryOp::LogicalNegate, self.u64_type, low);
+ let not_high = self.context.new_unary_op(None, UnaryOp::LogicalNegate, self.u64_type, high);
+ let not_low_and_not_high = not_low & not_high;
+ let index = not_low + not_low_and_not_high;
+
+ let res = self.context.new_array_access(None, result, index);
+
+ return self.context.new_bitcast(None, res, result_type);
+ }
+ else {
+ unimplemented!("count_trailing_zeroes for {:?}", arg_type);
+ };
+ let count_trailing_zeroes = self.context.get_builtin_function(count_trailing_zeroes);
+ let arg =
+ if arg_type != expected_type {
+ self.context.new_cast(None, arg, expected_type)
+ }
+ else {
+ arg
+ };
+ let res = self.context.new_call(None, count_trailing_zeroes, &[arg]);
+ self.context.new_bitcast(None, res, result_type)
+ }
+
+ fn int_width(&self, typ: Type<'gcc>) -> i64 {
+ self.cx.int_width(typ) as i64
+ }
+
+ fn pop_count(&self, value: RValue<'gcc>) -> RValue<'gcc> {
+ // TODO(antoyo): use the optimized version with fewer operations.
+ let result_type = value.get_type();
+ let value_type = result_type.to_unsigned(self.cx);
+
+ let value =
+ if result_type.is_signed(self.cx) {
+ self.context.new_bitcast(None, value, value_type)
+ }
+ else {
+ value
+ };
+
+ if value_type.is_u128(&self.cx) {
+ // TODO(antoyo): implement in the normal algorithm below to have a more efficient
+ // implementation (that does not require a call to __popcountdi2).
+ let popcount = self.context.get_builtin_function("__builtin_popcountll");
+ let sixty_four = self.context.new_rvalue_from_long(value_type, 64);
+ let high = self.context.new_cast(None, value >> sixty_four, self.cx.ulonglong_type);
+ let high = self.context.new_call(None, popcount, &[high]);
+ let low = self.context.new_cast(None, value, self.cx.ulonglong_type);
+ let low = self.context.new_call(None, popcount, &[low]);
+ let res = high + low;
+ return self.context.new_bitcast(None, res, result_type);
+ }
+
+ // First step.
+ let mask = self.context.new_rvalue_from_long(value_type, 0x5555555555555555);
+ let left = value & mask;
+ let shifted = value >> self.context.new_rvalue_from_int(value_type, 1);
+ let right = shifted & mask;
+ let value = left + right;
+
+ // Second step.
+ let mask = self.context.new_rvalue_from_long(value_type, 0x3333333333333333);
+ let left = value & mask;
+ let shifted = value >> self.context.new_rvalue_from_int(value_type, 2);
+ let right = shifted & mask;
+ let value = left + right;
+
+ // Third step.
+ let mask = self.context.new_rvalue_from_long(value_type, 0x0F0F0F0F0F0F0F0F);
+ let left = value & mask;
+ let shifted = value >> self.context.new_rvalue_from_int(value_type, 4);
+ let right = shifted & mask;
+ let value = left + right;
+
+ if value_type.is_u8(&self.cx) {
+ return self.context.new_bitcast(None, value, result_type);
+ }
+
+ // Fourth step.
+ let mask = self.context.new_rvalue_from_long(value_type, 0x00FF00FF00FF00FF);
+ let left = value & mask;
+ let shifted = value >> self.context.new_rvalue_from_int(value_type, 8);
+ let right = shifted & mask;
+ let value = left + right;
+
+ if value_type.is_u16(&self.cx) {
+ return self.context.new_bitcast(None, value, result_type);
+ }
+
+ // Fifth step.
+ let mask = self.context.new_rvalue_from_long(value_type, 0x0000FFFF0000FFFF);
+ let left = value & mask;
+ let shifted = value >> self.context.new_rvalue_from_int(value_type, 16);
+ let right = shifted & mask;
+ let value = left + right;
+
+ if value_type.is_u32(&self.cx) {
+ return self.context.new_bitcast(None, value, result_type);
+ }
+
+ // Sixth step.
+ let mask = self.context.new_rvalue_from_long(value_type, 0x00000000FFFFFFFF);
+ let left = value & mask;
+ let shifted = value >> self.context.new_rvalue_from_int(value_type, 32);
+ let right = shifted & mask;
+ let value = left + right;
+
+ self.context.new_bitcast(None, value, result_type)
+ }
+
+ // Algorithm from: https://blog.regehr.org/archives/1063
+ fn rotate_left(&mut self, value: RValue<'gcc>, shift: RValue<'gcc>, width: u64) -> RValue<'gcc> {
+ let max = self.context.new_rvalue_from_long(shift.get_type(), width as i64);
+ let shift = shift % max;
+ let lhs = self.shl(value, shift);
+ let result_and =
+ self.and(
+ self.context.new_unary_op(None, UnaryOp::Minus, shift.get_type(), shift),
+ self.context.new_rvalue_from_long(shift.get_type(), width as i64 - 1),
+ );
+ let rhs = self.lshr(value, result_and);
+ self.or(lhs, rhs)
+ }
+
+ // Algorithm from: https://blog.regehr.org/archives/1063
+ fn rotate_right(&mut self, value: RValue<'gcc>, shift: RValue<'gcc>, width: u64) -> RValue<'gcc> {
+ let max = self.context.new_rvalue_from_long(shift.get_type(), width as i64);
+ let shift = shift % max;
+ let lhs = self.lshr(value, shift);
+ let result_and =
+ self.and(
+ self.context.new_unary_op(None, UnaryOp::Minus, shift.get_type(), shift),
+ self.context.new_rvalue_from_long(shift.get_type(), width as i64 - 1),
+ );
+ let rhs = self.shl(value, result_and);
+ self.or(lhs, rhs)
+ }
+
+ fn saturating_add(&mut self, lhs: RValue<'gcc>, rhs: RValue<'gcc>, signed: bool, width: u64) -> RValue<'gcc> {
+ let func = self.current_func.borrow().expect("func");
+
+ if signed {
+ // Algorithm from: https://stackoverflow.com/a/56531252/389119
+ let after_block = func.new_block("after");
+ let func_name =
+ match width {
+ 8 => "__builtin_add_overflow",
+ 16 => "__builtin_add_overflow",
+ 32 => "__builtin_sadd_overflow",
+ 64 => "__builtin_saddll_overflow",
+ 128 => "__builtin_add_overflow",
+ _ => unreachable!(),
+ };
+ let overflow_func = self.context.get_builtin_function(func_name);
+ let result_type = lhs.get_type();
+ let res = func.new_local(None, result_type, "saturating_sum");
+ let overflow = self.overflow_call(overflow_func, &[lhs, rhs, res.get_address(None)], None);
+
+ let then_block = func.new_block("then");
+
+ let unsigned_type = self.context.new_int_type(width as i32 / 8, false);
+ let shifted = self.context.new_cast(None, lhs, unsigned_type) >> self.context.new_rvalue_from_int(unsigned_type, width as i32 - 1);
+ let uint_max = self.context.new_unary_op(None, UnaryOp::BitwiseNegate, unsigned_type,
+ self.context.new_rvalue_from_int(unsigned_type, 0)
+ );
+ let int_max = uint_max >> self.context.new_rvalue_one(unsigned_type);
+ then_block.add_assignment(None, res, self.context.new_cast(None, shifted + int_max, result_type));
+ then_block.end_with_jump(None, after_block);
+
+ self.llbb().end_with_conditional(None, overflow, then_block, after_block);
+
+ // NOTE: since jumps were added in a place rustc does not
+ // expect, the current blocks in the state need to be updated.
+ *self.current_block.borrow_mut() = Some(after_block);
+ self.block = Some(after_block);
+
+ res.to_rvalue()
+ }
+ else {
+ // Algorithm from: http://locklessinc.com/articles/sat_arithmetic/
+ let res = lhs + rhs;
+ let res_type = res.get_type();
+ let cond = self.context.new_comparison(None, ComparisonOp::LessThan, res, lhs);
+ let value = self.context.new_unary_op(None, UnaryOp::Minus, res_type, self.context.new_cast(None, cond, res_type));
+ res | value
+ }
+ }
+
+ // Algorithm from: https://locklessinc.com/articles/sat_arithmetic/
+ fn saturating_sub(&mut self, lhs: RValue<'gcc>, rhs: RValue<'gcc>, signed: bool, width: u64) -> RValue<'gcc> {
+ if signed {
+ // Also based on algorithm from: https://stackoverflow.com/a/56531252/389119
+ let func_name =
+ match width {
+ 8 => "__builtin_sub_overflow",
+ 16 => "__builtin_sub_overflow",
+ 32 => "__builtin_ssub_overflow",
+ 64 => "__builtin_ssubll_overflow",
+ 128 => "__builtin_sub_overflow",
+ _ => unreachable!(),
+ };
+ let overflow_func = self.context.get_builtin_function(func_name);
+ let result_type = lhs.get_type();
+ let func = self.current_func.borrow().expect("func");
+ let res = func.new_local(None, result_type, "saturating_diff");
+ let overflow = self.overflow_call(overflow_func, &[lhs, rhs, res.get_address(None)], None);
+
+ let then_block = func.new_block("then");
+ let after_block = func.new_block("after");
+
+ let unsigned_type = self.context.new_int_type(width as i32 / 8, false);
+ let shifted = self.context.new_cast(None, lhs, unsigned_type) >> self.context.new_rvalue_from_int(unsigned_type, width as i32 - 1);
+ let uint_max = self.context.new_unary_op(None, UnaryOp::BitwiseNegate, unsigned_type,
+ self.context.new_rvalue_from_int(unsigned_type, 0)
+ );
+ let int_max = uint_max >> self.context.new_rvalue_one(unsigned_type);
+ then_block.add_assignment(None, res, self.context.new_cast(None, shifted + int_max, result_type));
+ then_block.end_with_jump(None, after_block);
+
+ self.llbb().end_with_conditional(None, overflow, then_block, after_block);
+
+ // NOTE: since jumps were added in a place rustc does not
+ // expect, the current blocks in the state need to be updated.
+ *self.current_block.borrow_mut() = Some(after_block);
+ self.block = Some(after_block);
+
+ res.to_rvalue()
+ }
+ else {
+ let res = lhs - rhs;
+ let comparison = self.context.new_comparison(None, ComparisonOp::LessThanEquals, res, lhs);
+ let comparison = self.context.new_cast(None, comparison, lhs.get_type());
+ let unary_op = self.context.new_unary_op(None, UnaryOp::Minus, comparison.get_type(), comparison);
+ self.and(res, unary_op)
+ }
+ }
+}
+
+fn try_intrinsic<'gcc, 'tcx>(bx: &mut Builder<'_, 'gcc, 'tcx>, try_func: RValue<'gcc>, data: RValue<'gcc>, _catch_func: RValue<'gcc>, dest: RValue<'gcc>) {
+ if bx.sess().panic_strategy() == PanicStrategy::Abort {
+ bx.call(bx.type_void(), try_func, &[data], None);
+ // Return 0 unconditionally from the intrinsic call;
+ // we can never unwind.
+ let ret_align = bx.tcx.data_layout.i32_align.abi;
+ bx.store(bx.const_i32(0), dest, ret_align);
+ }
+ else if wants_msvc_seh(bx.sess()) {
+ unimplemented!();
+ }
+ else {
+ unimplemented!();
+ }
+}
--- /dev/null
+use gccjit::{RValue, Type};
+use rustc_codegen_ssa::base::compare_simd_types;
+use rustc_codegen_ssa::common::{TypeKind, span_invalid_monomorphization_error};
+use rustc_codegen_ssa::mir::operand::OperandRef;
+use rustc_codegen_ssa::traits::{BaseTypeMethods, BuilderMethods};
+use rustc_hir as hir;
+use rustc_middle::span_bug;
+use rustc_middle::ty::layout::HasTyCtxt;
+use rustc_middle::ty::{self, Ty};
+use rustc_span::{Span, Symbol, sym};
+
+use crate::builder::Builder;
+
+pub fn generic_simd_intrinsic<'a, 'gcc, 'tcx>(bx: &mut Builder<'a, 'gcc, 'tcx>, name: Symbol, callee_ty: Ty<'tcx>, args: &[OperandRef<'tcx, RValue<'gcc>>], ret_ty: Ty<'tcx>, llret_ty: Type<'gcc>, span: Span) -> Result<RValue<'gcc>, ()> {
+ // macros for error handling:
+ macro_rules! emit_error {
+ ($msg: tt) => {
+ emit_error!($msg, )
+ };
+ ($msg: tt, $($fmt: tt)*) => {
+ span_invalid_monomorphization_error(
+ bx.sess(), span,
+ &format!(concat!("invalid monomorphization of `{}` intrinsic: ", $msg),
+ name, $($fmt)*));
+ }
+ }
+
+ macro_rules! return_error {
+ ($($fmt: tt)*) => {
+ {
+ emit_error!($($fmt)*);
+ return Err(());
+ }
+ }
+ }
+
+ macro_rules! require {
+ ($cond: expr, $($fmt: tt)*) => {
+ if !$cond {
+ return_error!($($fmt)*);
+ }
+ };
+ }
+
+ macro_rules! require_simd {
+ ($ty: expr, $position: expr) => {
+ require!($ty.is_simd(), "expected SIMD {} type, found non-SIMD `{}`", $position, $ty)
+ };
+ }
+
+ let tcx = bx.tcx();
+ let sig =
+ tcx.normalize_erasing_late_bound_regions(ty::ParamEnv::reveal_all(), callee_ty.fn_sig(tcx));
+ let arg_tys = sig.inputs();
+ let name_str = &*name.as_str();
+
+ // every intrinsic below takes a SIMD vector as its first argument
+ require_simd!(arg_tys[0], "input");
+ let in_ty = arg_tys[0];
+
+ let comparison = match name {
+ sym::simd_eq => Some(hir::BinOpKind::Eq),
+ sym::simd_ne => Some(hir::BinOpKind::Ne),
+ sym::simd_lt => Some(hir::BinOpKind::Lt),
+ sym::simd_le => Some(hir::BinOpKind::Le),
+ sym::simd_gt => Some(hir::BinOpKind::Gt),
+ sym::simd_ge => Some(hir::BinOpKind::Ge),
+ _ => None,
+ };
+
+ let (in_len, in_elem) = arg_tys[0].simd_size_and_type(bx.tcx());
+ if let Some(cmp_op) = comparison {
+ require_simd!(ret_ty, "return");
+
+ let (out_len, out_ty) = ret_ty.simd_size_and_type(bx.tcx());
+ require!(
+ in_len == out_len,
+ "expected return type with length {} (same as input type `{}`), \
+ found `{}` with length {}",
+ in_len,
+ in_ty,
+ ret_ty,
+ out_len
+ );
+ require!(
+ bx.type_kind(bx.element_type(llret_ty)) == TypeKind::Integer,
+ "expected return type with integer elements, found `{}` with non-integer `{}`",
+ ret_ty,
+ out_ty
+ );
+
+ return Ok(compare_simd_types(
+ bx,
+ args[0].immediate(),
+ args[1].immediate(),
+ in_elem,
+ llret_ty,
+ cmp_op,
+ ));
+ }
+
+ if let Some(stripped) = name_str.strip_prefix("simd_shuffle") {
+ let n: u64 = stripped.parse().unwrap_or_else(|_| {
+ span_bug!(span, "bad `simd_shuffle` instruction only caught in codegen?")
+ });
+
+ require_simd!(ret_ty, "return");
+
+ let (out_len, out_ty) = ret_ty.simd_size_and_type(bx.tcx());
+ require!(
+ out_len == n,
+ "expected return type of length {}, found `{}` with length {}",
+ n,
+ ret_ty,
+ out_len
+ );
+ require!(
+ in_elem == out_ty,
+ "expected return element type `{}` (element of input `{}`), \
+ found `{}` with element type `{}`",
+ in_elem,
+ in_ty,
+ ret_ty,
+ out_ty
+ );
+
+ let vector = args[2].immediate();
+
+ return Ok(bx.shuffle_vector(
+ args[0].immediate(),
+ args[1].immediate(),
+ vector,
+ ));
+ }
+
+ macro_rules! arith_binary {
+ ($($name: ident: $($($p: ident),* => $call: ident),*;)*) => {
+ $(if name == sym::$name {
+ match in_elem.kind() {
+ $($(ty::$p(_))|* => {
+ return Ok(bx.$call(args[0].immediate(), args[1].immediate()))
+ })*
+ _ => {},
+ }
+ require!(false,
+ "unsupported operation on `{}` with element `{}`",
+ in_ty,
+ in_elem)
+ })*
+ }
+ }
+
+ arith_binary! {
+ simd_add: Uint, Int => add, Float => fadd;
+ simd_sub: Uint, Int => sub, Float => fsub;
+ simd_mul: Uint, Int => mul, Float => fmul;
+ simd_div: Uint => udiv, Int => sdiv, Float => fdiv;
+ simd_rem: Uint => urem, Int => srem, Float => frem;
+ simd_shl: Uint, Int => shl;
+ simd_shr: Uint => lshr, Int => ashr;
+ simd_and: Uint, Int => and;
+ simd_or: Uint, Int => or; // FIXME(antoyo): calling `or` might not work on vectors.
+ simd_xor: Uint, Int => xor;
+ }
+
+ unimplemented!("simd {}", name);
+}
--- /dev/null
+/*
+ * TODO(antoyo): support #[inline] attributes.
+ * TODO(antoyo): support LTO.
+ *
+ * TODO(antoyo): remove the patches.
+ */
+
+#![feature(rustc_private, decl_macro, associated_type_bounds, never_type, trusted_len)]
+#![allow(broken_intra_doc_links)]
+#![recursion_limit="256"]
+#![warn(rust_2018_idioms)]
+#![warn(unused_lifetimes)]
+
+extern crate rustc_ast;
+extern crate rustc_codegen_ssa;
+extern crate rustc_data_structures;
+extern crate rustc_errors;
+extern crate rustc_hir;
+extern crate rustc_metadata;
+extern crate rustc_middle;
+extern crate rustc_session;
+extern crate rustc_span;
+extern crate rustc_symbol_mangling;
+extern crate rustc_target;
+extern crate snap;
+
+// This prevents duplicating functions and statics that are already part of the host rustc process.
+#[allow(unused_extern_crates)]
+extern crate rustc_driver;
+
+mod abi;
+mod allocator;
+mod archive;
+mod asm;
+mod back;
+mod base;
+mod builder;
+mod callee;
+mod common;
+mod consts;
+mod context;
+mod coverageinfo;
+mod debuginfo;
+mod declare;
+mod intrinsic;
+mod mono_item;
+mod type_;
+mod type_of;
+
+use std::any::Any;
+use std::sync::Arc;
+
+use gccjit::{Context, OptimizationLevel};
+use rustc_ast::expand::allocator::AllocatorKind;
+use rustc_codegen_ssa::{CodegenResults, CompiledModule, ModuleCodegen};
+use rustc_codegen_ssa::base::codegen_crate;
+use rustc_codegen_ssa::back::write::{CodegenContext, FatLTOInput, ModuleConfig, TargetMachineFactoryFn};
+use rustc_codegen_ssa::back::lto::{LtoModuleCodegen, SerializedModule, ThinModule};
+use rustc_codegen_ssa::target_features::supported_target_features;
+use rustc_codegen_ssa::traits::{CodegenBackend, ExtraBackendMethods, ModuleBufferMethods, ThinBufferMethods, WriteBackendMethods};
+use rustc_data_structures::fx::FxHashMap;
+use rustc_errors::{ErrorReported, Handler};
+use rustc_metadata::EncodedMetadata;
+use rustc_middle::dep_graph::{WorkProduct, WorkProductId};
+use rustc_middle::ty::TyCtxt;
+use rustc_session::config::{Lto, OptLevel, OutputFilenames};
+use rustc_session::Session;
+use rustc_span::Symbol;
+use rustc_span::fatal_error::FatalError;
+
+pub struct PrintOnPanic<F: Fn() -> String>(pub F);
+
+impl<F: Fn() -> String> Drop for PrintOnPanic<F> {
+ fn drop(&mut self) {
+ if ::std::thread::panicking() {
+ println!("{}", (self.0)());
+ }
+ }
+}
+
+#[derive(Clone)]
+pub struct GccCodegenBackend;
+
+impl CodegenBackend for GccCodegenBackend {
+ fn init(&self, sess: &Session) {
+ if sess.lto() != Lto::No {
+ sess.warn("LTO is not supported. You may get a linker error.");
+ }
+ }
+
+ fn codegen_crate<'tcx>(&self, tcx: TyCtxt<'tcx>, metadata: EncodedMetadata, need_metadata_module: bool) -> Box<dyn Any> {
+ let target_cpu = target_cpu(tcx.sess);
+ let res = codegen_crate(self.clone(), tcx, target_cpu.to_string(), metadata, need_metadata_module);
+
+ rustc_symbol_mangling::test::report_symbol_names(tcx);
+
+ Box::new(res)
+ }
+
+ fn join_codegen(&self, ongoing_codegen: Box<dyn Any>, sess: &Session) -> Result<(CodegenResults, FxHashMap<WorkProductId, WorkProduct>), ErrorReported> {
+ let (codegen_results, work_products) = ongoing_codegen
+ .downcast::<rustc_codegen_ssa::back::write::OngoingCodegen<GccCodegenBackend>>()
+ .expect("Expected GccCodegenBackend's OngoingCodegen, found Box<Any>")
+ .join(sess);
+
+ Ok((codegen_results, work_products))
+ }
+
+ fn link(&self, sess: &Session, codegen_results: CodegenResults, outputs: &OutputFilenames) -> Result<(), ErrorReported> {
+ use rustc_codegen_ssa::back::link::link_binary;
+
+ link_binary::<crate::archive::ArArchiveBuilder<'_>>(
+ sess,
+ &codegen_results,
+ outputs,
+ )
+ }
+
+ fn target_features(&self, sess: &Session) -> Vec<Symbol> {
+ target_features(sess)
+ }
+}
+
+impl ExtraBackendMethods for GccCodegenBackend {
+ fn new_metadata<'tcx>(&self, _tcx: TyCtxt<'tcx>, _mod_name: &str) -> Self::Module {
+ GccContext {
+ context: Context::default(),
+ }
+ }
+
+ fn write_compressed_metadata<'tcx>(&self, tcx: TyCtxt<'tcx>, metadata: &EncodedMetadata, gcc_module: &mut Self::Module) {
+ base::write_compressed_metadata(tcx, metadata, gcc_module)
+ }
+
+ fn codegen_allocator<'tcx>(&self, tcx: TyCtxt<'tcx>, mods: &mut Self::Module, module_name: &str, kind: AllocatorKind, has_alloc_error_handler: bool) {
+ unsafe { allocator::codegen(tcx, mods, module_name, kind, has_alloc_error_handler) }
+ }
+
+ fn compile_codegen_unit<'tcx>(&self, tcx: TyCtxt<'tcx>, cgu_name: Symbol) -> (ModuleCodegen<Self::Module>, u64) {
+ base::compile_codegen_unit(tcx, cgu_name)
+ }
+
+ fn target_machine_factory(&self, _sess: &Session, _opt_level: OptLevel) -> TargetMachineFactoryFn<Self> {
+ // TODO(antoyo): set opt level.
+ Arc::new(|_| {
+ Ok(())
+ })
+ }
+
+ fn target_cpu<'b>(&self, _sess: &'b Session) -> &'b str {
+ unimplemented!();
+ }
+
+ fn tune_cpu<'b>(&self, _sess: &'b Session) -> Option<&'b str> {
+ None
+ // TODO(antoyo)
+ }
+}
+
+pub struct ModuleBuffer;
+
+impl ModuleBufferMethods for ModuleBuffer {
+ fn data(&self) -> &[u8] {
+ unimplemented!();
+ }
+}
+
+pub struct ThinBuffer;
+
+impl ThinBufferMethods for ThinBuffer {
+ fn data(&self) -> &[u8] {
+ unimplemented!();
+ }
+}
+
+pub struct GccContext {
+ context: Context<'static>,
+}
+
+unsafe impl Send for GccContext {}
+// FIXME(antoyo): that shouldn't be Sync. Parallel compilation is currently disabled with "-Zno-parallel-llvm". Try to disable it here.
+unsafe impl Sync for GccContext {}
+
+impl WriteBackendMethods for GccCodegenBackend {
+ type Module = GccContext;
+ type TargetMachine = ();
+ type ModuleBuffer = ModuleBuffer;
+ type Context = ();
+ type ThinData = ();
+ type ThinBuffer = ThinBuffer;
+
+ fn run_fat_lto(_cgcx: &CodegenContext<Self>, mut modules: Vec<FatLTOInput<Self>>, _cached_modules: Vec<(SerializedModule<Self::ModuleBuffer>, WorkProduct)>) -> Result<LtoModuleCodegen<Self>, FatalError> {
+ // TODO(antoyo): implement LTO by sending -flto to libgccjit and adding the appropriate gcc linker plugins.
+ // NOTE: implemented elsewhere.
+ // TODO: what is implemented elsewhere ^ ?
+ let module =
+ match modules.remove(0) {
+ FatLTOInput::InMemory(module) => module,
+ FatLTOInput::Serialized { .. } => {
+ unimplemented!();
+ }
+ };
+ Ok(LtoModuleCodegen::Fat { module: Some(module), _serialized_bitcode: vec![] })
+ }
+
+ fn run_thin_lto(_cgcx: &CodegenContext<Self>, _modules: Vec<(String, Self::ThinBuffer)>, _cached_modules: Vec<(SerializedModule<Self::ModuleBuffer>, WorkProduct)>) -> Result<(Vec<LtoModuleCodegen<Self>>, Vec<WorkProduct>), FatalError> {
+ unimplemented!();
+ }
+
+ fn print_pass_timings(&self) {
+ unimplemented!();
+ }
+
+ unsafe fn optimize(_cgcx: &CodegenContext<Self>, _diag_handler: &Handler, module: &ModuleCodegen<Self::Module>, config: &ModuleConfig) -> Result<(), FatalError> {
+ module.module_llvm.context.set_optimization_level(to_gcc_opt_level(config.opt_level));
+ Ok(())
+ }
+
+ unsafe fn optimize_thin(_cgcx: &CodegenContext<Self>, _thin: &mut ThinModule<Self>) -> Result<ModuleCodegen<Self::Module>, FatalError> {
+ unimplemented!();
+ }
+
+ unsafe fn codegen(cgcx: &CodegenContext<Self>, diag_handler: &Handler, module: ModuleCodegen<Self::Module>, config: &ModuleConfig) -> Result<CompiledModule, FatalError> {
+ back::write::codegen(cgcx, diag_handler, module, config)
+ }
+
+ fn prepare_thin(_module: ModuleCodegen<Self::Module>) -> (String, Self::ThinBuffer) {
+ unimplemented!();
+ }
+
+ fn serialize_module(_module: ModuleCodegen<Self::Module>) -> (String, Self::ModuleBuffer) {
+ unimplemented!();
+ }
+
+ fn run_lto_pass_manager(_cgcx: &CodegenContext<Self>, _module: &ModuleCodegen<Self::Module>, _config: &ModuleConfig, _thin: bool) -> Result<(), FatalError> {
+ // TODO(antoyo)
+ Ok(())
+ }
+
+ fn run_link(cgcx: &CodegenContext<Self>, diag_handler: &Handler, modules: Vec<ModuleCodegen<Self::Module>>) -> Result<ModuleCodegen<Self::Module>, FatalError> {
+ back::write::link(cgcx, diag_handler, modules)
+ }
+}
+
+/// This is the entrypoint for a hot plugged rustc_codegen_gccjit
+#[no_mangle]
+pub fn __rustc_codegen_backend() -> Box<dyn CodegenBackend> {
+ Box::new(GccCodegenBackend)
+}
+
+fn to_gcc_opt_level(optlevel: Option<OptLevel>) -> OptimizationLevel {
+ match optlevel {
+ None => OptimizationLevel::None,
+ Some(level) => {
+ match level {
+ OptLevel::No => OptimizationLevel::None,
+ OptLevel::Less => OptimizationLevel::Limited,
+ OptLevel::Default => OptimizationLevel::Standard,
+ OptLevel::Aggressive => OptimizationLevel::Aggressive,
+ OptLevel::Size | OptLevel::SizeMin => OptimizationLevel::Limited,
+ }
+ },
+ }
+}
+
+fn handle_native(name: &str) -> &str {
+ if name != "native" {
+ return name;
+ }
+
+ unimplemented!();
+}
+
+pub fn target_cpu(sess: &Session) -> &str {
+ let name = sess.opts.cg.target_cpu.as_ref().unwrap_or(&sess.target.cpu);
+ handle_native(name)
+}
+
+pub fn target_features(sess: &Session) -> Vec<Symbol> {
+ supported_target_features(sess)
+ .iter()
+ .filter_map(
+ |&(feature, gate)| {
+ if sess.is_nightly_build() || gate.is_none() { Some(feature) } else { None }
+ },
+ )
+ .filter(|_feature| {
+ // TODO(antoyo): implement a way to get enabled feature in libgccjit.
+ false
+ })
+ .map(|feature| Symbol::intern(feature))
+ .collect()
+}
--- /dev/null
+use rustc_codegen_ssa::traits::PreDefineMethods;
+use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags;
+use rustc_middle::mir::mono::{Linkage, Visibility};
+use rustc_middle::ty::{self, Instance, TypeFoldable};
+use rustc_middle::ty::layout::{FnAbiOf, LayoutOf};
+use rustc_span::def_id::DefId;
+
+use crate::base;
+use crate::context::CodegenCx;
+use crate::type_of::LayoutGccExt;
+
+impl<'gcc, 'tcx> PreDefineMethods<'tcx> for CodegenCx<'gcc, 'tcx> {
+ fn predefine_static(&self, def_id: DefId, _linkage: Linkage, _visibility: Visibility, symbol_name: &str) {
+ let attrs = self.tcx.codegen_fn_attrs(def_id);
+ let instance = Instance::mono(self.tcx, def_id);
+ let ty = instance.ty(self.tcx, ty::ParamEnv::reveal_all());
+ let gcc_type = self.layout_of(ty).gcc_type(self, true);
+
+ let is_tls = attrs.flags.contains(CodegenFnAttrFlags::THREAD_LOCAL);
+ let global = self.define_global(symbol_name, gcc_type, is_tls, attrs.link_section);
+
+ // TODO(antoyo): set linkage and visibility.
+ self.instances.borrow_mut().insert(instance, global);
+ }
+
+ fn predefine_fn(&self, instance: Instance<'tcx>, linkage: Linkage, _visibility: Visibility, symbol_name: &str) {
+ assert!(!instance.substs.needs_infer());
+
+ let fn_abi = self.fn_abi_of_instance(instance, ty::List::empty());
+ self.linkage.set(base::linkage_to_gcc(linkage));
+ let _decl = self.declare_fn(symbol_name, &fn_abi);
+ //let attrs = self.tcx.codegen_fn_attrs(instance.def_id());
+
+ // TODO(antoyo): call set_link_section() to allow initializing argc/argv.
+ // TODO(antoyo): set unique comdat.
+ // TODO(antoyo): use inline attribute from there in linkage.set() above.
+ }
+}
--- /dev/null
+use std::convert::TryInto;
+
+use gccjit::{RValue, Struct, Type};
+use rustc_codegen_ssa::traits::{BaseTypeMethods, DerivedTypeMethods};
+use rustc_codegen_ssa::common::TypeKind;
+use rustc_middle::bug;
+use rustc_middle::ty::layout::TyAndLayout;
+use rustc_target::abi::{AddressSpace, Align, Integer, Size};
+
+use crate::common::TypeReflection;
+use crate::context::CodegenCx;
+use crate::type_of::LayoutGccExt;
+
+impl<'gcc, 'tcx> CodegenCx<'gcc, 'tcx> {
+ pub fn type_ix(&self, num_bits: u64) -> Type<'gcc> {
+ // gcc only supports 1, 2, 4 or 8-byte integers.
+ // FIXME(antoyo): this is misleading to use the next power of two as rustc_codegen_ssa
+ // sometimes use 96-bit numbers and the following code will give an integer of a different
+ // size.
+ let bytes = (num_bits / 8).next_power_of_two() as i32;
+ match bytes {
+ 1 => self.i8_type,
+ 2 => self.i16_type,
+ 4 => self.i32_type,
+ 8 => self.i64_type,
+ 16 => self.i128_type,
+ _ => panic!("unexpected num_bits: {}", num_bits),
+ }
+ }
+
+ pub fn type_void(&self) -> Type<'gcc> {
+ self.context.new_type::<()>()
+ }
+
+ pub fn type_size_t(&self) -> Type<'gcc> {
+ self.context.new_type::<usize>()
+ }
+
+ pub fn type_u8(&self) -> Type<'gcc> {
+ self.u8_type
+ }
+
+ pub fn type_u16(&self) -> Type<'gcc> {
+ self.u16_type
+ }
+
+ pub fn type_u32(&self) -> Type<'gcc> {
+ self.u32_type
+ }
+
+ pub fn type_u64(&self) -> Type<'gcc> {
+ self.u64_type
+ }
+
+ pub fn type_u128(&self) -> Type<'gcc> {
+ self.u128_type
+ }
+
+ pub fn type_pointee_for_align(&self, align: Align) -> Type<'gcc> {
+ // FIXME(eddyb) We could find a better approximation if ity.align < align.
+ let ity = Integer::approximate_align(self, align);
+ self.type_from_integer(ity)
+ }
+}
+
+impl<'gcc, 'tcx> BaseTypeMethods<'tcx> for CodegenCx<'gcc, 'tcx> {
+ fn type_i1(&self) -> Type<'gcc> {
+ self.bool_type
+ }
+
+ fn type_i8(&self) -> Type<'gcc> {
+ self.i8_type
+ }
+
+ fn type_i16(&self) -> Type<'gcc> {
+ self.i16_type
+ }
+
+ fn type_i32(&self) -> Type<'gcc> {
+ self.i32_type
+ }
+
+ fn type_i64(&self) -> Type<'gcc> {
+ self.i64_type
+ }
+
+ fn type_i128(&self) -> Type<'gcc> {
+ self.i128_type
+ }
+
+ fn type_isize(&self) -> Type<'gcc> {
+ self.isize_type
+ }
+
+ fn type_f32(&self) -> Type<'gcc> {
+ self.context.new_type::<f32>()
+ }
+
+ fn type_f64(&self) -> Type<'gcc> {
+ self.context.new_type::<f64>()
+ }
+
+ fn type_func(&self, params: &[Type<'gcc>], return_type: Type<'gcc>) -> Type<'gcc> {
+ self.context.new_function_pointer_type(None, return_type, params, false)
+ }
+
+ fn type_struct(&self, fields: &[Type<'gcc>], _packed: bool) -> Type<'gcc> {
+ let types = fields.to_vec();
+ if let Some(typ) = self.struct_types.borrow().get(fields) {
+ return typ.clone();
+ }
+ let fields: Vec<_> = fields.iter().enumerate()
+ .map(|(index, field)| self.context.new_field(None, *field, &format!("field{}_TODO", index)))
+ .collect();
+ // TODO(antoyo): use packed.
+ let typ = self.context.new_struct_type(None, "struct", &fields).as_type();
+ self.struct_types.borrow_mut().insert(types, typ);
+ typ
+ }
+
+ fn type_kind(&self, typ: Type<'gcc>) -> TypeKind {
+ if typ.is_integral() {
+ TypeKind::Integer
+ }
+ else if typ.is_vector().is_some() {
+ TypeKind::Vector
+ }
+ else {
+ // TODO(antoyo): support other types.
+ TypeKind::Void
+ }
+ }
+
+ fn type_ptr_to(&self, ty: Type<'gcc>) -> Type<'gcc> {
+ ty.make_pointer()
+ }
+
+ fn type_ptr_to_ext(&self, ty: Type<'gcc>, _address_space: AddressSpace) -> Type<'gcc> {
+ // TODO(antoyo): use address_space
+ ty.make_pointer()
+ }
+
+ fn element_type(&self, ty: Type<'gcc>) -> Type<'gcc> {
+ if let Some(typ) = ty.is_array() {
+ typ
+ }
+ else if let Some(vector_type) = ty.is_vector() {
+ vector_type.get_element_type()
+ }
+ else if let Some(typ) = ty.get_pointee() {
+ typ
+ }
+ else {
+ unreachable!()
+ }
+ }
+
+ fn vector_length(&self, _ty: Type<'gcc>) -> usize {
+ unimplemented!();
+ }
+
+ fn float_width(&self, typ: Type<'gcc>) -> usize {
+ let f32 = self.context.new_type::<f32>();
+ let f64 = self.context.new_type::<f64>();
+ if typ == f32 {
+ 32
+ }
+ else if typ == f64 {
+ 64
+ }
+ else {
+ panic!("Cannot get width of float type {:?}", typ);
+ }
+ // TODO(antoyo): support other sizes.
+ }
+
+ fn int_width(&self, typ: Type<'gcc>) -> u64 {
+ if typ.is_i8(self) || typ.is_u8(self) {
+ 8
+ }
+ else if typ.is_i16(self) || typ.is_u16(self) {
+ 16
+ }
+ else if typ.is_i32(self) || typ.is_u32(self) {
+ 32
+ }
+ else if typ.is_i64(self) || typ.is_u64(self) {
+ 64
+ }
+ else if typ.is_i128(self) || typ.is_u128(self) {
+ 128
+ }
+ else {
+ panic!("Cannot get width of int type {:?}", typ);
+ }
+ }
+
+ fn val_ty(&self, value: RValue<'gcc>) -> Type<'gcc> {
+ value.get_type()
+ }
+}
+
+impl<'gcc, 'tcx> CodegenCx<'gcc, 'tcx> {
+ pub fn type_padding_filler(&self, size: Size, align: Align) -> Type<'gcc> {
+ let unit = Integer::approximate_align(self, align);
+ let size = size.bytes();
+ let unit_size = unit.size().bytes();
+ assert_eq!(size % unit_size, 0);
+ self.type_array(self.type_from_integer(unit), size / unit_size)
+ }
+
+ pub fn set_struct_body(&self, typ: Struct<'gcc>, fields: &[Type<'gcc>], _packed: bool) {
+ // TODO(antoyo): use packed.
+ let fields: Vec<_> = fields.iter().enumerate()
+ .map(|(index, field)| self.context.new_field(None, *field, &format!("field_{}", index)))
+ .collect();
+ typ.set_fields(None, &fields);
+ }
+
+ pub fn type_named_struct(&self, name: &str) -> Struct<'gcc> {
+ self.context.new_opaque_struct_type(None, name)
+ }
+
+ pub fn type_array(&self, ty: Type<'gcc>, mut len: u64) -> Type<'gcc> {
+ if let Some(struct_type) = ty.is_struct() {
+ if struct_type.get_field_count() == 0 {
+ // NOTE: since gccjit only supports i32 for the array size and libcore's tests uses a
+ // size of usize::MAX in test_binary_search, we workaround this by setting the size to
+ // zero for ZSTs.
+ // FIXME(antoyo): fix gccjit API.
+ len = 0;
+ }
+ }
+
+ // NOTE: see note above. Some other test uses usize::MAX.
+ if len == u64::MAX {
+ len = 0;
+ }
+
+ let len: i32 = len.try_into().expect("array len");
+
+ self.context.new_array_type(None, ty, len)
+ }
+}
+
+pub fn struct_fields<'gcc, 'tcx>(cx: &CodegenCx<'gcc, 'tcx>, layout: TyAndLayout<'tcx>) -> (Vec<Type<'gcc>>, bool) {
+ let field_count = layout.fields.count();
+
+ let mut packed = false;
+ let mut offset = Size::ZERO;
+ let mut prev_effective_align = layout.align.abi;
+ let mut result: Vec<_> = Vec::with_capacity(1 + field_count * 2);
+ for i in layout.fields.index_by_increasing_offset() {
+ let target_offset = layout.fields.offset(i as usize);
+ let field = layout.field(cx, i);
+ let effective_field_align =
+ layout.align.abi.min(field.align.abi).restrict_for_offset(target_offset);
+ packed |= effective_field_align < field.align.abi;
+
+ assert!(target_offset >= offset);
+ let padding = target_offset - offset;
+ let padding_align = prev_effective_align.min(effective_field_align);
+ assert_eq!(offset.align_to(padding_align) + padding, target_offset);
+ result.push(cx.type_padding_filler(padding, padding_align));
+
+ result.push(field.gcc_type(cx, !field.ty.is_any_ptr())); // FIXME(antoyo): might need to check if the type is inside another, like Box<Type>.
+ offset = target_offset + field.size;
+ prev_effective_align = effective_field_align;
+ }
+ if !layout.is_unsized() && field_count > 0 {
+ if offset > layout.size {
+ bug!("layout: {:#?} stride: {:?} offset: {:?}", layout, layout.size, offset);
+ }
+ let padding = layout.size - offset;
+ let padding_align = prev_effective_align;
+ assert_eq!(offset.align_to(padding_align) + padding, layout.size);
+ result.push(cx.type_padding_filler(padding, padding_align));
+ assert_eq!(result.len(), 1 + field_count * 2);
+ }
+
+ (result, packed)
+}
--- /dev/null
+use std::fmt::Write;
+
+use gccjit::{Struct, Type};
+use crate::rustc_codegen_ssa::traits::{BaseTypeMethods, DerivedTypeMethods, LayoutTypeMethods};
+use rustc_middle::bug;
+use rustc_middle::ty::{self, Ty, TypeFoldable};
+use rustc_middle::ty::layout::{FnAbiOf, LayoutOf, TyAndLayout};
+use rustc_middle::ty::print::with_no_trimmed_paths;
+use rustc_target::abi::{self, Abi, F32, F64, FieldsShape, Int, Integer, Pointer, PointeeInfo, Size, TyAbiInterface, Variants};
+use rustc_target::abi::call::{CastTarget, FnAbi, Reg};
+
+use crate::abi::{FnAbiGccExt, GccType};
+use crate::context::CodegenCx;
+use crate::type_::struct_fields;
+
+impl<'gcc, 'tcx> CodegenCx<'gcc, 'tcx> {
+ fn type_from_unsigned_integer(&self, i: Integer) -> Type<'gcc> {
+ use Integer::*;
+ match i {
+ I8 => self.type_u8(),
+ I16 => self.type_u16(),
+ I32 => self.type_u32(),
+ I64 => self.type_u64(),
+ I128 => self.type_u128(),
+ }
+ }
+}
+
+pub fn uncached_gcc_type<'gcc, 'tcx>(cx: &CodegenCx<'gcc, 'tcx>, layout: TyAndLayout<'tcx>, defer: &mut Option<(Struct<'gcc>, TyAndLayout<'tcx>)>) -> Type<'gcc> {
+ match layout.abi {
+ Abi::Scalar(_) => bug!("handled elsewhere"),
+ Abi::Vector { ref element, count } => {
+ let element = layout.scalar_gcc_type_at(cx, element, Size::ZERO);
+ return cx.context.new_vector_type(element, count);
+ },
+ Abi::ScalarPair(..) => {
+ return cx.type_struct(
+ &[
+ layout.scalar_pair_element_gcc_type(cx, 0, false),
+ layout.scalar_pair_element_gcc_type(cx, 1, false),
+ ],
+ false,
+ );
+ }
+ Abi::Uninhabited | Abi::Aggregate { .. } => {}
+ }
+
+ let name = match layout.ty.kind() {
+ // FIXME(eddyb) producing readable type names for trait objects can result
+ // in problematically distinct types due to HRTB and subtyping (see #47638).
+ // ty::Dynamic(..) |
+ ty::Adt(..) | ty::Closure(..) | ty::Foreign(..) | ty::Generator(..) | ty::Str
+ if !cx.sess().fewer_names() =>
+ {
+ let mut name = with_no_trimmed_paths(|| layout.ty.to_string());
+ if let (&ty::Adt(def, _), &Variants::Single { index }) =
+ (layout.ty.kind(), &layout.variants)
+ {
+ if def.is_enum() && !def.variants.is_empty() {
+ write!(&mut name, "::{}", def.variants[index].ident).unwrap();
+ }
+ }
+ if let (&ty::Generator(_, _, _), &Variants::Single { index }) =
+ (layout.ty.kind(), &layout.variants)
+ {
+ write!(&mut name, "::{}", ty::GeneratorSubsts::variant_name(index)).unwrap();
+ }
+ Some(name)
+ }
+ ty::Adt(..) => {
+ // If `Some` is returned then a named struct is created in LLVM. Name collisions are
+ // avoided by LLVM (with increasing suffixes). If rustc doesn't generate names then that
+ // can improve perf.
+ // FIXME(antoyo): I don't think that's true for libgccjit.
+ Some(String::new())
+ }
+ _ => None,
+ };
+
+ match layout.fields {
+ FieldsShape::Primitive | FieldsShape::Union(_) => {
+ let fill = cx.type_padding_filler(layout.size, layout.align.abi);
+ let packed = false;
+ match name {
+ None => cx.type_struct(&[fill], packed),
+ Some(ref name) => {
+ let gcc_type = cx.type_named_struct(name);
+ cx.set_struct_body(gcc_type, &[fill], packed);
+ gcc_type.as_type()
+ },
+ }
+ }
+ FieldsShape::Array { count, .. } => cx.type_array(layout.field(cx, 0).gcc_type(cx, true), count),
+ FieldsShape::Arbitrary { .. } =>
+ match name {
+ None => {
+ let (gcc_fields, packed) = struct_fields(cx, layout);
+ cx.type_struct(&gcc_fields, packed)
+ },
+ Some(ref name) => {
+ let gcc_type = cx.type_named_struct(name);
+ *defer = Some((gcc_type, layout));
+ gcc_type.as_type()
+ },
+ },
+ }
+}
+
+pub trait LayoutGccExt<'tcx> {
+ fn is_gcc_immediate(&self) -> bool;
+ fn is_gcc_scalar_pair(&self) -> bool;
+ fn gcc_type<'gcc>(&self, cx: &CodegenCx<'gcc, 'tcx>, set_fields: bool) -> Type<'gcc>;
+ fn immediate_gcc_type<'gcc>(&self, cx: &CodegenCx<'gcc, 'tcx>) -> Type<'gcc>;
+ fn scalar_gcc_type_at<'gcc>(&self, cx: &CodegenCx<'gcc, 'tcx>, scalar: &abi::Scalar, offset: Size) -> Type<'gcc>;
+ fn scalar_pair_element_gcc_type<'gcc>(&self, cx: &CodegenCx<'gcc, 'tcx>, index: usize, immediate: bool) -> Type<'gcc>;
+ fn gcc_field_index(&self, index: usize) -> u64;
+ fn pointee_info_at<'gcc>(&self, cx: &CodegenCx<'gcc, 'tcx>, offset: Size) -> Option<PointeeInfo>;
+}
+
+impl<'tcx> LayoutGccExt<'tcx> for TyAndLayout<'tcx> {
+ fn is_gcc_immediate(&self) -> bool {
+ match self.abi {
+ Abi::Scalar(_) | Abi::Vector { .. } => true,
+ Abi::ScalarPair(..) => false,
+ Abi::Uninhabited | Abi::Aggregate { .. } => self.is_zst(),
+ }
+ }
+
+ fn is_gcc_scalar_pair(&self) -> bool {
+ match self.abi {
+ Abi::ScalarPair(..) => true,
+ Abi::Uninhabited | Abi::Scalar(_) | Abi::Vector { .. } | Abi::Aggregate { .. } => false,
+ }
+ }
+
+ /// Gets the GCC type corresponding to a Rust type, i.e., `rustc_middle::ty::Ty`.
+ /// The pointee type of the pointer in `PlaceRef` is always this type.
+ /// For sized types, it is also the right LLVM type for an `alloca`
+ /// containing a value of that type, and most immediates (except `bool`).
+ /// Unsized types, however, are represented by a "minimal unit", e.g.
+ /// `[T]` becomes `T`, while `str` and `Trait` turn into `i8` - this
+ /// is useful for indexing slices, as `&[T]`'s data pointer is `T*`.
+ /// If the type is an unsized struct, the regular layout is generated,
+ /// with the inner-most trailing unsized field using the "minimal unit"
+ /// of that field's type - this is useful for taking the address of
+ /// that field and ensuring the struct has the right alignment.
+ //TODO(antoyo): do we still need the set_fields parameter?
+ fn gcc_type<'gcc>(&self, cx: &CodegenCx<'gcc, 'tcx>, set_fields: bool) -> Type<'gcc> {
+ if let Abi::Scalar(ref scalar) = self.abi {
+ // Use a different cache for scalars because pointers to DSTs
+ // can be either fat or thin (data pointers of fat pointers).
+ if let Some(&ty) = cx.scalar_types.borrow().get(&self.ty) {
+ return ty;
+ }
+ let ty =
+ match *self.ty.kind() {
+ ty::Ref(_, ty, _) | ty::RawPtr(ty::TypeAndMut { ty, .. }) => {
+ cx.type_ptr_to(cx.layout_of(ty).gcc_type(cx, set_fields))
+ }
+ ty::Adt(def, _) if def.is_box() => {
+ cx.type_ptr_to(cx.layout_of(self.ty.boxed_ty()).gcc_type(cx, true))
+ }
+ ty::FnPtr(sig) => cx.fn_ptr_backend_type(&cx.fn_abi_of_fn_ptr(sig, ty::List::empty())),
+ _ => self.scalar_gcc_type_at(cx, scalar, Size::ZERO),
+ };
+ cx.scalar_types.borrow_mut().insert(self.ty, ty);
+ return ty;
+ }
+
+ // Check the cache.
+ let variant_index =
+ match self.variants {
+ Variants::Single { index } => Some(index),
+ _ => None,
+ };
+ let cached_type = cx.types.borrow().get(&(self.ty, variant_index)).cloned();
+ if let Some(ty) = cached_type {
+ let type_to_set_fields = cx.types_with_fields_to_set.borrow_mut().remove(&ty);
+ if let Some((struct_type, layout)) = type_to_set_fields {
+ // Since we might be trying to generate a type containing another type which is not
+ // completely generated yet, we deferred setting the fields until now.
+ let (fields, packed) = struct_fields(cx, layout);
+ cx.set_struct_body(struct_type, &fields, packed);
+ }
+ return ty;
+ }
+
+ assert!(!self.ty.has_escaping_bound_vars(), "{:?} has escaping bound vars", self.ty);
+
+ // Make sure lifetimes are erased, to avoid generating distinct LLVM
+ // types for Rust types that only differ in the choice of lifetimes.
+ let normal_ty = cx.tcx.erase_regions(self.ty);
+
+ let mut defer = None;
+ let ty =
+ if self.ty != normal_ty {
+ let mut layout = cx.layout_of(normal_ty);
+ if let Some(v) = variant_index {
+ layout = layout.for_variant(cx, v);
+ }
+ layout.gcc_type(cx, true)
+ }
+ else {
+ uncached_gcc_type(cx, *self, &mut defer)
+ };
+
+ cx.types.borrow_mut().insert((self.ty, variant_index), ty);
+
+ if let Some((ty, layout)) = defer {
+ let (fields, packed) = struct_fields(cx, layout);
+ cx.set_struct_body(ty, &fields, packed);
+ }
+
+ ty
+ }
+
+ fn immediate_gcc_type<'gcc>(&self, cx: &CodegenCx<'gcc, 'tcx>) -> Type<'gcc> {
+ if let Abi::Scalar(ref scalar) = self.abi {
+ if scalar.is_bool() {
+ return cx.type_i1();
+ }
+ }
+ self.gcc_type(cx, true)
+ }
+
+ fn scalar_gcc_type_at<'gcc>(&self, cx: &CodegenCx<'gcc, 'tcx>, scalar: &abi::Scalar, offset: Size) -> Type<'gcc> {
+ match scalar.value {
+ Int(i, true) => cx.type_from_integer(i),
+ Int(i, false) => cx.type_from_unsigned_integer(i),
+ F32 => cx.type_f32(),
+ F64 => cx.type_f64(),
+ Pointer => {
+ // If we know the alignment, pick something better than i8.
+ let pointee =
+ if let Some(pointee) = self.pointee_info_at(cx, offset) {
+ cx.type_pointee_for_align(pointee.align)
+ }
+ else {
+ cx.type_i8()
+ };
+ cx.type_ptr_to(pointee)
+ }
+ }
+ }
+
+ fn scalar_pair_element_gcc_type<'gcc>(&self, cx: &CodegenCx<'gcc, 'tcx>, index: usize, immediate: bool) -> Type<'gcc> {
+ // TODO(antoyo): remove llvm hack:
+ // HACK(eddyb) special-case fat pointers until LLVM removes
+ // pointee types, to avoid bitcasting every `OperandRef::deref`.
+ match self.ty.kind() {
+ ty::Ref(..) | ty::RawPtr(_) => {
+ return self.field(cx, index).gcc_type(cx, true);
+ }
+ ty::Adt(def, _) if def.is_box() => {
+ let ptr_ty = cx.tcx.mk_mut_ptr(self.ty.boxed_ty());
+ return cx.layout_of(ptr_ty).scalar_pair_element_gcc_type(cx, index, immediate);
+ }
+ _ => {}
+ }
+
+ let (a, b) = match self.abi {
+ Abi::ScalarPair(ref a, ref b) => (a, b),
+ _ => bug!("TyAndLayout::scalar_pair_element_llty({:?}): not applicable", self),
+ };
+ let scalar = [a, b][index];
+
+ // Make sure to return the same type `immediate_gcc_type` would when
+ // dealing with an immediate pair. This means that `(bool, bool)` is
+ // effectively represented as `{i8, i8}` in memory and two `i1`s as an
+ // immediate, just like `bool` is typically `i8` in memory and only `i1`
+ // when immediate. We need to load/store `bool` as `i8` to avoid
+ // crippling LLVM optimizations or triggering other LLVM bugs with `i1`.
+ // TODO(antoyo): this bugs certainly don't happen in this case since the bool type is used instead of i1.
+ if scalar.is_bool() {
+ return cx.type_i1();
+ }
+
+ let offset =
+ if index == 0 {
+ Size::ZERO
+ }
+ else {
+ a.value.size(cx).align_to(b.value.align(cx).abi)
+ };
+ self.scalar_gcc_type_at(cx, scalar, offset)
+ }
+
+ fn gcc_field_index(&self, index: usize) -> u64 {
+ match self.abi {
+ Abi::Scalar(_) | Abi::ScalarPair(..) => {
+ bug!("TyAndLayout::gcc_field_index({:?}): not applicable", self)
+ }
+ _ => {}
+ }
+ match self.fields {
+ FieldsShape::Primitive | FieldsShape::Union(_) => {
+ bug!("TyAndLayout::gcc_field_index({:?}): not applicable", self)
+ }
+
+ FieldsShape::Array { .. } => index as u64,
+
+ FieldsShape::Arbitrary { .. } => 1 + (self.fields.memory_index(index) as u64) * 2,
+ }
+ }
+
+ fn pointee_info_at<'a>(&self, cx: &CodegenCx<'a, 'tcx>, offset: Size) -> Option<PointeeInfo> {
+ if let Some(&pointee) = cx.pointee_infos.borrow().get(&(self.ty, offset)) {
+ return pointee;
+ }
+
+ let result = Ty::ty_and_layout_pointee_info_at(*self, cx, offset);
+
+ cx.pointee_infos.borrow_mut().insert((self.ty, offset), result);
+ result
+ }
+}
+
+impl<'gcc, 'tcx> LayoutTypeMethods<'tcx> for CodegenCx<'gcc, 'tcx> {
+ fn backend_type(&self, layout: TyAndLayout<'tcx>) -> Type<'gcc> {
+ layout.gcc_type(self, true)
+ }
+
+ fn immediate_backend_type(&self, layout: TyAndLayout<'tcx>) -> Type<'gcc> {
+ layout.immediate_gcc_type(self)
+ }
+
+ fn is_backend_immediate(&self, layout: TyAndLayout<'tcx>) -> bool {
+ layout.is_gcc_immediate()
+ }
+
+ fn is_backend_scalar_pair(&self, layout: TyAndLayout<'tcx>) -> bool {
+ layout.is_gcc_scalar_pair()
+ }
+
+ fn backend_field_index(&self, layout: TyAndLayout<'tcx>, index: usize) -> u64 {
+ layout.gcc_field_index(index)
+ }
+
+ fn scalar_pair_element_backend_type(&self, layout: TyAndLayout<'tcx>, index: usize, immediate: bool) -> Type<'gcc> {
+ layout.scalar_pair_element_gcc_type(self, index, immediate)
+ }
+
+ fn cast_backend_type(&self, ty: &CastTarget) -> Type<'gcc> {
+ ty.gcc_type(self)
+ }
+
+ fn fn_ptr_backend_type(&self, fn_abi: &FnAbi<'tcx, Ty<'tcx>>) -> Type<'gcc> {
+ fn_abi.ptr_to_gcc_type(self)
+ }
+
+ fn reg_backend_type(&self, _ty: &Reg) -> Type<'gcc> {
+ unimplemented!();
+ }
+
+ fn fn_decl_backend_type(&self, _fn_abi: &FnAbi<'tcx, Ty<'tcx>>) -> Type<'gcc> {
+ // FIXME(antoyo): return correct type.
+ self.type_void()
+ }
+}
--- /dev/null
+#!/bin/bash
+
+# TODO(antoyo): rewrite to cargo-make (or just) or something like that to only rebuild the sysroot when needed?
+
+set -e
+
+if [ -f ./gcc_path ]; then
+ export GCC_PATH=$(cat gcc_path)
+else
+ echo 'Please put the path to your custom build of libgccjit in the file `gcc_path`, see Readme.md for details'
+ exit 1
+fi
+
+export LD_LIBRARY_PATH="$GCC_PATH"
+export LIBRARY_PATH="$GCC_PATH"
+
+if [[ "$1" == "--release" ]]; then
+ export CHANNEL='release'
+ CARGO_INCREMENTAL=1 cargo rustc --release
+ shift
+else
+ echo $LD_LIBRARY_PATH
+ export CHANNEL='debug'
+ cargo rustc
+fi
+
+source config.sh
+
+function clean() {
+ rm -r target/out || true
+ mkdir -p target/out/gccjit
+}
+
+function mini_tests() {
+ echo "[BUILD] mini_core"
+ $RUSTC example/mini_core.rs --crate-name mini_core --crate-type lib,dylib --target $TARGET_TRIPLE
+
+ echo "[BUILD] example"
+ $RUSTC example/example.rs --crate-type lib --target $TARGET_TRIPLE
+
+ echo "[AOT] mini_core_hello_world"
+ $RUSTC example/mini_core_hello_world.rs --crate-name mini_core_hello_world --crate-type bin -g --target $TARGET_TRIPLE
+ $RUN_WRAPPER ./target/out/mini_core_hello_world abc bcd
+}
+
+function build_sysroot() {
+ echo "[BUILD] sysroot"
+ time ./build_sysroot/build_sysroot.sh
+}
+
+function std_tests() {
+ echo "[AOT] arbitrary_self_types_pointers_and_wrappers"
+ $RUSTC example/arbitrary_self_types_pointers_and_wrappers.rs --crate-name arbitrary_self_types_pointers_and_wrappers --crate-type bin --target $TARGET_TRIPLE
+ $RUN_WRAPPER ./target/out/arbitrary_self_types_pointers_and_wrappers
+
+ echo "[AOT] alloc_system"
+ $RUSTC example/alloc_system.rs --crate-type lib --target "$TARGET_TRIPLE"
+
+ echo "[AOT] alloc_example"
+ $RUSTC example/alloc_example.rs --crate-type bin --target $TARGET_TRIPLE
+ $RUN_WRAPPER ./target/out/alloc_example
+
+ echo "[AOT] dst_field_align"
+ # FIXME(antoyo): Re-add -Zmir-opt-level=2 once rust-lang/rust#67529 is fixed.
+ $RUSTC example/dst-field-align.rs --crate-name dst_field_align --crate-type bin --target $TARGET_TRIPLE
+ $RUN_WRAPPER ./target/out/dst_field_align || (echo $?; false)
+
+ echo "[AOT] std_example"
+ $RUSTC example/std_example.rs --crate-type bin --target $TARGET_TRIPLE
+ $RUN_WRAPPER ./target/out/std_example --target $TARGET_TRIPLE
+
+ echo "[AOT] subslice-patterns-const-eval"
+ $RUSTC example/subslice-patterns-const-eval.rs --crate-type bin -Cpanic=abort --target $TARGET_TRIPLE
+ $RUN_WRAPPER ./target/out/subslice-patterns-const-eval
+
+ echo "[AOT] track-caller-attribute"
+ $RUSTC example/track-caller-attribute.rs --crate-type bin -Cpanic=abort --target $TARGET_TRIPLE
+ $RUN_WRAPPER ./target/out/track-caller-attribute
+
+ echo "[BUILD] mod_bench"
+ $RUSTC example/mod_bench.rs --crate-type bin --target $TARGET_TRIPLE
+}
+
+# FIXME(antoyo): linker gives multiple definitions error on Linux
+#echo "[BUILD] sysroot in release mode"
+#./build_sysroot/build_sysroot.sh --release
+
+# TODO(antoyo): uncomment when it works.
+#pushd simple-raytracer
+#if [[ "$HOST_TRIPLE" = "$TARGET_TRIPLE" ]]; then
+ #echo "[BENCH COMPILE] ebobby/simple-raytracer"
+ #hyperfine --runs ${RUN_RUNS:-10} --warmup 1 --prepare "rm -r target/*/debug || true" \
+ #"RUSTFLAGS='' cargo build --target $TARGET_TRIPLE" \
+ #"../cargo.sh build"
+
+ #echo "[BENCH RUN] ebobby/simple-raytracer"
+ #cp ./target/*/debug/main ./raytracer_cg_gccjit
+ #hyperfine --runs ${RUN_RUNS:-10} ./raytracer_cg_llvm ./raytracer_cg_gccjit
+#else
+ #echo "[BENCH COMPILE] ebobby/simple-raytracer (skipped)"
+ #echo "[COMPILE] ebobby/simple-raytracer"
+ #../cargo.sh build
+ #echo "[BENCH RUN] ebobby/simple-raytracer (skipped)"
+#fi
+#popd
+
+function test_libcore() {
+ pushd build_sysroot/sysroot_src/library/core/tests
+ echo "[TEST] libcore"
+ rm -r ./target || true
+ ../../../../../cargo.sh test
+ popd
+}
+
+# TODO(antoyo): uncomment when it works.
+#pushd regex
+#echo "[TEST] rust-lang/regex example shootout-regex-dna"
+#../cargo.sh clean
+## Make sure `[codegen mono items] start` doesn't poison the diff
+#../cargo.sh build --example shootout-regex-dna
+#cat examples/regexdna-input.txt | ../cargo.sh run --example shootout-regex-dna | grep -v "Spawned thread" > res.txt
+#diff -u res.txt examples/regexdna-output.txt
+
+#echo "[TEST] rust-lang/regex tests"
+#../cargo.sh test --tests -- --exclude-should-panic --test-threads 1 -Zunstable-options
+#popd
+
+#echo
+#echo "[BENCH COMPILE] mod_bench"
+
+#COMPILE_MOD_BENCH_INLINE="$RUSTC example/mod_bench.rs --crate-type bin -Zmir-opt-level=3 -O --crate-name mod_bench_inline"
+#COMPILE_MOD_BENCH_LLVM_0="rustc example/mod_bench.rs --crate-type bin -Copt-level=0 -o target/out/mod_bench_llvm_0 -Cpanic=abort"
+#COMPILE_MOD_BENCH_LLVM_1="rustc example/mod_bench.rs --crate-type bin -Copt-level=1 -o target/out/mod_bench_llvm_1 -Cpanic=abort"
+#COMPILE_MOD_BENCH_LLVM_2="rustc example/mod_bench.rs --crate-type bin -Copt-level=2 -o target/out/mod_bench_llvm_2 -Cpanic=abort"
+#COMPILE_MOD_BENCH_LLVM_3="rustc example/mod_bench.rs --crate-type bin -Copt-level=3 -o target/out/mod_bench_llvm_3 -Cpanic=abort"
+
+## Use 100 runs, because a single compilations doesn't take more than ~150ms, so it isn't very slow
+#hyperfine --runs ${COMPILE_RUNS:-100} "$COMPILE_MOD_BENCH_INLINE" "$COMPILE_MOD_BENCH_LLVM_0" "$COMPILE_MOD_BENCH_LLVM_1" "$COMPILE_MOD_BENCH_LLVM_2" "$COMPILE_MOD_BENCH_LLVM_3"
+
+#echo
+#echo "[BENCH RUN] mod_bench"
+#hyperfine --runs ${RUN_RUNS:-10} ./target/out/mod_bench{,_inline} ./target/out/mod_bench_llvm_*
+
+function test_rustc() {
+ echo
+ echo "[TEST] rust-lang/rust"
+
+ rust_toolchain=$(cat rust-toolchain)
+
+ git clone https://github.com/rust-lang/rust.git || true
+ cd rust
+ git fetch
+ git checkout $(rustc -V | cut -d' ' -f3 | tr -d '(')
+ export RUSTFLAGS=
+
+ rm config.toml || true
+
+ cat > config.toml <<EOF
+[rust]
+codegen-backends = []
+deny-warnings = false
+
+[build]
+cargo = "$(which cargo)"
+local-rebuild = true
+rustc = "$HOME/.rustup/toolchains/$rust_toolchain-$TARGET_TRIPLE/bin/rustc"
+EOF
+
+ rustc -V | cut -d' ' -f3 | tr -d '('
+ git checkout $(rustc -V | cut -d' ' -f3 | tr -d '(') src/test
+
+ for test in $(rg -i --files-with-matches "//(\[\w+\])?~|// error-pattern:|// build-fail|// run-fail|-Cllvm-args" src/test/ui); do
+ rm $test
+ done
+
+ git checkout -- src/test/ui/issues/auxiliary/issue-3136-a.rs # contains //~ERROR, but shouldn't be removed
+
+ rm -r src/test/ui/{abi*,extern/,llvm-asm/,panic-runtime/,panics/,unsized-locals/,proc-macro/,threads-sendsync/,thinlto/,simd*,borrowck/,test*,*lto*.rs} || true
+ for test in $(rg --files-with-matches "catch_unwind|should_panic|thread|lto" src/test/ui); do
+ rm $test
+ done
+ git checkout src/test/ui/type-alias-impl-trait/auxiliary/cross_crate_ice.rs
+ git checkout src/test/ui/type-alias-impl-trait/auxiliary/cross_crate_ice2.rs
+ rm src/test/ui/llvm-asm/llvm-asm-in-out-operand.rs || true # TODO(antoyo): Enable back this test if I ever implement the llvm_asm! macro.
+
+ RUSTC_ARGS="-Zpanic-abort-tests -Zsymbol-mangling-version=v0 -Zcodegen-backend="$(pwd)"/../target/"$CHANNEL"/librustc_codegen_gcc."$dylib_ext" --sysroot "$(pwd)"/../build_sysroot/sysroot -Cpanic=abort"
+
+ echo "[TEST] rustc test suite"
+ COMPILETEST_FORCE_STAGE0=1 ./x.py test --run always --stage 0 src/test/ui/ --rustc-args "$RUSTC_ARGS"
+}
+
+function clean_ui_tests() {
+ find rust/build/x86_64-unknown-linux-gnu/test/ui/ -name stamp -exec rm -rf {} \;
+}
+
+case $1 in
+ "--test-rustc")
+ test_rustc
+ ;;
+
+ "--test-libcore")
+ test_libcore
+ ;;
+
+ "--clean-ui-tests")
+ clean_ui_tests
+ ;;
+
+ *)
+ clean
+ mini_tests
+ build_sysroot
+ std_tests
+ test_libcore
+ test_rustc
+ ;;
+esac
--- /dev/null
+use std::{
+ env::{self, current_dir},
+ path::PathBuf,
+ process::Command,
+};
+
+use lang_tester::LangTester;
+use tempfile::TempDir;
+
+fn main() {
+ let tempdir = TempDir::new().expect("temp dir");
+ let current_dir = current_dir().expect("current dir");
+ let current_dir = current_dir.to_str().expect("current dir").to_string();
+ let gcc_path = include_str!("../gcc_path");
+ let gcc_path = gcc_path.trim();
+ env::set_var("LD_LIBRARY_PATH", gcc_path);
+ LangTester::new()
+ .test_dir("tests/run")
+ .test_file_filter(|path| path.extension().expect("extension").to_str().expect("to_str") == "rs")
+ .test_extract(|source| {
+ let lines =
+ source.lines()
+ .skip_while(|l| !l.starts_with("//"))
+ .take_while(|l| l.starts_with("//"))
+ .map(|l| &l[2..])
+ .collect::<Vec<_>>()
+ .join("\n");
+ Some(lines)
+ })
+ .test_cmds(move |path| {
+ // Test command 1: Compile `x.rs` into `tempdir/x`.
+ let mut exe = PathBuf::new();
+ exe.push(&tempdir);
+ exe.push(path.file_stem().expect("file_stem"));
+ let mut compiler = Command::new("rustc");
+ compiler.args(&[
+ &format!("-Zcodegen-backend={}/target/debug/librustc_codegen_gcc.so", current_dir),
+ "--sysroot", &format!("{}/build_sysroot/sysroot/", current_dir),
+ "-Zno-parallel-llvm",
+ "-C", "panic=abort",
+ "-C", "link-arg=-lc",
+ "-o", exe.to_str().expect("to_str"),
+ path.to_str().expect("to_str"),
+ ]);
+ // Test command 2: run `tempdir/x`.
+ let runtime = Command::new(exe);
+ vec![("Compiler", compiler), ("Run-time", runtime)]
+ })
+ .run();
+}
--- /dev/null
+// Compiler:
+//
+// Run-time:
+// status: signal
+
+#![feature(auto_traits, lang_items, no_core, start, intrinsics)]
+
+#![no_std]
+#![no_core]
+
+/*
+ * Core
+ */
+
+// Because we don't have core yet.
+#[lang = "sized"]
+pub trait Sized {}
+
+#[lang = "copy"]
+trait Copy {
+}
+
+impl Copy for isize {}
+
+#[lang = "receiver"]
+trait Receiver {
+}
+
+#[lang = "freeze"]
+pub(crate) unsafe auto trait Freeze {}
+
+mod intrinsics {
+ use super::Sized;
+
+ extern "rust-intrinsic" {
+ pub fn abort() -> !;
+ }
+}
+
+/*
+ * Code
+ */
+
+fn test_fail() -> ! {
+ unsafe { intrinsics::abort() };
+}
+
+#[start]
+fn main(mut argc: isize, _argv: *const *const u8) -> isize {
+ test_fail();
+}
--- /dev/null
+// Compiler:
+//
+// Run-time:
+// status: signal
+
+#![feature(auto_traits, lang_items, no_core, start, intrinsics)]
+
+#![no_std]
+#![no_core]
+
+/*
+ * Core
+ */
+
+// Because we don't have core yet.
+#[lang = "sized"]
+pub trait Sized {}
+
+#[lang = "copy"]
+trait Copy {
+}
+
+impl Copy for isize {}
+
+#[lang = "receiver"]
+trait Receiver {
+}
+
+#[lang = "freeze"]
+pub(crate) unsafe auto trait Freeze {}
+
+mod intrinsics {
+ use super::Sized;
+
+ extern "rust-intrinsic" {
+ pub fn abort() -> !;
+ }
+}
+
+/*
+ * Code
+ */
+
+fn fail() -> i32 {
+ unsafe { intrinsics::abort() };
+ 0
+}
+
+#[start]
+fn main(mut argc: isize, _argv: *const *const u8) -> isize {
+ fail();
+ 0
+}
--- /dev/null
+// Compiler:
+//
+// Run-time:
+// status: 0
+// stdout: 42
+// 7
+// 5
+// 10
+
+#![feature(arbitrary_self_types, auto_traits, lang_items, no_core, start, intrinsics)]
+
+#![no_std]
+#![no_core]
+
+/*
+ * Core
+ */
+
+// Because we don't have core yet.
+#[lang = "sized"]
+pub trait Sized {}
+
+#[lang = "copy"]
+trait Copy {
+}
+
+impl Copy for isize {}
+impl Copy for usize {}
+impl Copy for i32 {}
+impl Copy for u8 {}
+impl Copy for i8 {}
+impl Copy for i16 {}
+
+#[lang = "receiver"]
+trait Receiver {
+}
+
+#[lang = "freeze"]
+pub(crate) unsafe auto trait Freeze {}
+
+mod libc {
+ #[link(name = "c")]
+ extern "C" {
+ pub fn printf(format: *const i8, ...) -> i32;
+ pub fn puts(s: *const u8) -> i32;
+ }
+}
+
+#[lang = "index"]
+pub trait Index<Idx: ?Sized> {
+ type Output: ?Sized;
+ fn index(&self, index: Idx) -> &Self::Output;
+}
+
+impl<T> Index<usize> for [T; 3] {
+ type Output = T;
+
+ fn index(&self, index: usize) -> &Self::Output {
+ &self[index]
+ }
+}
+
+impl<T> Index<usize> for [T] {
+ type Output = T;
+
+ fn index(&self, index: usize) -> &Self::Output {
+ &self[index]
+ }
+}
+
+#[lang = "drop_in_place"]
+#[allow(unconditional_recursion)]
+pub unsafe fn drop_in_place<T: ?Sized>(to_drop: *mut T) {
+ // Code here does not matter - this is replaced by the
+ // real drop glue by the compiler.
+ drop_in_place(to_drop);
+}
+
+#[lang = "panic"]
+#[track_caller]
+#[no_mangle]
+pub fn panic(_msg: &str) -> ! {
+ unsafe {
+ libc::puts("Panicking\0" as *const str as *const u8);
+ intrinsics::abort();
+ }
+}
+
+#[lang = "panic_location"]
+struct PanicLocation {
+ file: &'static str,
+ line: u32,
+ column: u32,
+}
+
+#[lang = "panic_bounds_check"]
+#[track_caller]
+#[no_mangle]
+fn panic_bounds_check(index: usize, len: usize) -> ! {
+ unsafe {
+ libc::printf("index out of bounds: the len is %d but the index is %d\n\0" as *const str as *const i8, len, index);
+ intrinsics::abort();
+ }
+}
+
+mod intrinsics {
+ extern "rust-intrinsic" {
+ pub fn abort() -> !;
+ }
+}
+
+#[lang = "add"]
+trait Add<RHS = Self> {
+ type Output;
+
+ fn add(self, rhs: RHS) -> Self::Output;
+}
+
+impl Add for u8 {
+ type Output = Self;
+
+ fn add(self, rhs: Self) -> Self {
+ self + rhs
+ }
+}
+
+impl Add for i8 {
+ type Output = Self;
+
+ fn add(self, rhs: Self) -> Self {
+ self + rhs
+ }
+}
+
+impl Add for i32 {
+ type Output = Self;
+
+ fn add(self, rhs: Self) -> Self {
+ self + rhs
+ }
+}
+
+impl Add for usize {
+ type Output = Self;
+
+ fn add(self, rhs: Self) -> Self {
+ self + rhs
+ }
+}
+
+impl Add for isize {
+ type Output = Self;
+
+ fn add(self, rhs: Self) -> Self {
+ self + rhs
+ }
+}
+
+#[lang = "sub"]
+pub trait Sub<RHS = Self> {
+ type Output;
+
+ fn sub(self, rhs: RHS) -> Self::Output;
+}
+
+impl Sub for usize {
+ type Output = Self;
+
+ fn sub(self, rhs: Self) -> Self {
+ self - rhs
+ }
+}
+
+impl Sub for isize {
+ type Output = Self;
+
+ fn sub(self, rhs: Self) -> Self {
+ self - rhs
+ }
+}
+
+impl Sub for u8 {
+ type Output = Self;
+
+ fn sub(self, rhs: Self) -> Self {
+ self - rhs
+ }
+}
+
+impl Sub for i8 {
+ type Output = Self;
+
+ fn sub(self, rhs: Self) -> Self {
+ self - rhs
+ }
+}
+
+impl Sub for i16 {
+ type Output = Self;
+
+ fn sub(self, rhs: Self) -> Self {
+ self - rhs
+ }
+}
+
+
+/*
+ * Code
+ */
+
+static mut ONE: usize = 1;
+
+fn make_array() -> [u8; 3] {
+ [42, 10, 5]
+}
+
+#[start]
+fn main(argc: isize, _argv: *const *const u8) -> isize {
+ let array = [42, 7, 5];
+ let array2 = make_array();
+ unsafe {
+ libc::printf(b"%ld\n\0" as *const u8 as *const i8, array[ONE - 1]);
+ libc::printf(b"%ld\n\0" as *const u8 as *const i8, array[ONE]);
+ libc::printf(b"%ld\n\0" as *const u8 as *const i8, array[ONE + 1]);
+
+ libc::printf(b"%d\n\0" as *const u8 as *const i8, array2[argc as usize] as u32);
+ }
+ 0
+}
--- /dev/null
+// Compiler:
+//
+// Run-time:
+// status: 0
+
+#![feature(asm, global_asm)]
+
+global_asm!("
+ .global add_asm
+add_asm:
+ mov rax, rdi
+ add rax, rsi
+ ret"
+);
+
+extern "C" {
+ fn add_asm(a: i64, b: i64) -> i64;
+}
+
+fn main() {
+ unsafe {
+ asm!("nop");
+ }
+
+ let x: u64;
+ unsafe {
+ asm!("mov $5, {}",
+ out(reg) x,
+ options(att_syntax)
+ );
+ }
+ assert_eq!(x, 5);
+
+ let x: u64;
+ let input: u64 = 42;
+ unsafe {
+ asm!("mov {input}, {output}",
+ "add $1, {output}",
+ input = in(reg) input,
+ output = out(reg) x,
+ options(att_syntax)
+ );
+ }
+ assert_eq!(x, 43);
+
+ let x: u64;
+ unsafe {
+ asm!("mov {}, 6",
+ out(reg) x,
+ );
+ }
+ assert_eq!(x, 6);
+
+ let x: u64;
+ let input: u64 = 42;
+ unsafe {
+ asm!("mov {output}, {input}",
+ "add {output}, 1",
+ input = in(reg) input,
+ output = out(reg) x,
+ );
+ }
+ assert_eq!(x, 43);
+
+ // check inout(reg_class) x
+ let mut x: u64 = 42;
+ unsafe {
+ asm!("add {0}, {0}",
+ inout(reg) x
+ );
+ }
+ assert_eq!(x, 84);
+
+ // check inout("reg") x
+ let mut x: u64 = 42;
+ unsafe {
+ asm!("add r11, r11",
+ inout("r11") x
+ );
+ }
+ assert_eq!(x, 84);
+
+ // check a mix of
+ // in("reg")
+ // inout(class) x => y
+ // inout (class) x
+ let x: u64 = 702;
+ let y: u64 = 100;
+ let res: u64;
+ let mut rem: u64 = 0;
+ unsafe {
+ asm!("div r11",
+ in("r11") y,
+ inout("eax") x => res,
+ inout("edx") rem,
+ );
+ }
+ assert_eq!(res, 7);
+ assert_eq!(rem, 2);
+
+ // check const
+ let mut x: u64 = 42;
+ unsafe {
+ asm!("add {}, {}",
+ inout(reg) x,
+ const 1
+ );
+ }
+ assert_eq!(x, 43);
+
+ // check const (ATT syntax)
+ let mut x: u64 = 42;
+ unsafe {
+ asm!("add {}, {}",
+ const 1,
+ inout(reg) x,
+ options(att_syntax)
+ );
+ }
+ assert_eq!(x, 43);
+
+ // check sym fn
+ extern "C" fn foo() -> u64 { 42 }
+ let x: u64;
+ unsafe {
+ asm!("call {}", sym foo, lateout("rax") x);
+ }
+ assert_eq!(x, 42);
+
+ // check sym fn (ATT syntax)
+ let x: u64;
+ unsafe {
+ asm!("call {}", sym foo, lateout("rax") x, options(att_syntax));
+ }
+ assert_eq!(x, 42);
+
+ // check sym static
+ static FOO: u64 = 42;
+ let x: u64;
+ unsafe {
+ asm!("mov {1}, qword ptr [rip + {0}]", sym FOO, lateout(reg) x);
+ }
+ assert_eq!(x, 42);
+
+ // check sym static (ATT syntax)
+ let x: u64;
+ unsafe {
+ asm!("movq {0}(%rip), {1}", sym FOO, lateout(reg) x, options(att_syntax));
+ }
+ assert_eq!(x, 42);
+
+ assert_eq!(unsafe { add_asm(40, 2) }, 42);
+}
--- /dev/null
+// Compiler:
+//
+// Run-time:
+// stdout: 2
+// 7 8
+// 10
+
+#![allow(unused_attributes)]
+#![feature(auto_traits, lang_items, no_core, start, intrinsics, track_caller)]
+
+#![no_std]
+#![no_core]
+
+/*
+ * Core
+ */
+
+// Because we don't have core yet.
+#[lang = "sized"]
+pub trait Sized {}
+
+#[lang = "copy"]
+trait Copy {
+}
+
+impl Copy for isize {}
+impl Copy for *mut i32 {}
+impl Copy for usize {}
+impl Copy for u8 {}
+impl Copy for i8 {}
+impl Copy for i32 {}
+
+#[lang = "receiver"]
+trait Receiver {
+}
+
+#[lang = "freeze"]
+pub(crate) unsafe auto trait Freeze {}
+
+#[lang = "panic_location"]
+struct PanicLocation {
+ file: &'static str,
+ line: u32,
+ column: u32,
+}
+
+mod libc {
+ #[link(name = "c")]
+ extern "C" {
+ pub fn puts(s: *const u8) -> i32;
+ pub fn fflush(stream: *mut i32) -> i32;
+ pub fn printf(format: *const i8, ...) -> i32;
+
+ pub static STDOUT: *mut i32;
+ }
+}
+
+mod intrinsics {
+ extern "rust-intrinsic" {
+ pub fn abort() -> !;
+ }
+}
+
+#[lang = "panic"]
+#[track_caller]
+#[no_mangle]
+pub fn panic(_msg: &str) -> ! {
+ unsafe {
+ libc::puts("Panicking\0" as *const str as *const u8);
+ libc::fflush(libc::STDOUT);
+ intrinsics::abort();
+ }
+}
+
+#[lang = "add"]
+trait Add<RHS = Self> {
+ type Output;
+
+ fn add(self, rhs: RHS) -> Self::Output;
+}
+
+impl Add for u8 {
+ type Output = Self;
+
+ fn add(self, rhs: Self) -> Self {
+ self + rhs
+ }
+}
+
+impl Add for i8 {
+ type Output = Self;
+
+ fn add(self, rhs: Self) -> Self {
+ self + rhs
+ }
+}
+
+impl Add for i32 {
+ type Output = Self;
+
+ fn add(self, rhs: Self) -> Self {
+ self + rhs
+ }
+}
+
+impl Add for usize {
+ type Output = Self;
+
+ fn add(self, rhs: Self) -> Self {
+ self + rhs
+ }
+}
+
+impl Add for isize {
+ type Output = Self;
+
+ fn add(self, rhs: Self) -> Self {
+ self + rhs
+ }
+}
+
+/*
+ * Code
+ */
+
+fn inc_ref(num: &mut isize) -> isize {
+ *num = *num + 5;
+ *num + 1
+}
+
+fn inc(num: isize) -> isize {
+ num + 1
+}
+
+
+#[start]
+fn main(mut argc: isize, _argv: *const *const u8) -> isize {
+ argc = inc(argc);
+ unsafe {
+ libc::printf(b"%ld\n\0" as *const u8 as *const i8, argc);
+ }
+
+ let b = inc_ref(&mut argc);
+ unsafe {
+ libc::printf(b"%ld %ld\n\0" as *const u8 as *const i8, argc, b);
+ }
+
+ argc = 10;
+ unsafe {
+ libc::printf(b"%ld\n\0" as *const u8 as *const i8, argc);
+ }
+ 0
+}
--- /dev/null
+// Compiler:
+//
+// Run-time:
+// status: 0
+// stdout: Arg: 1
+// Argument: 1
+// String arg: 1
+// Int argument: 2
+// Both args: 11
+
+#![feature(arbitrary_self_types, auto_traits, lang_items, no_core, start, intrinsics,
+ unboxed_closures)]
+
+#![no_std]
+#![no_core]
+
+/*
+ * Core
+ */
+
+// Because we don't have core yet.
+#[lang = "sized"]
+pub trait Sized {}
+
+#[lang = "copy"]
+trait Copy {
+}
+
+impl Copy for isize {}
+impl Copy for usize {}
+impl Copy for i32 {}
+impl Copy for u32 {}
+impl Copy for u8 {}
+impl Copy for i8 {}
+
+#[lang = "receiver"]
+trait Receiver {
+}
+
+#[lang = "freeze"]
+pub(crate) unsafe auto trait Freeze {}
+
+mod libc {
+ #[link(name = "c")]
+ extern "C" {
+ pub fn puts(s: *const u8) -> i32;
+ pub fn printf(format: *const i8, ...) -> i32;
+ }
+}
+
+#[lang = "index"]
+pub trait Index<Idx: ?Sized> {
+ type Output: ?Sized;
+ fn index(&self, index: Idx) -> &Self::Output;
+}
+
+impl<T> Index<usize> for [T; 3] {
+ type Output = T;
+
+ fn index(&self, index: usize) -> &Self::Output {
+ &self[index]
+ }
+}
+
+impl<T> Index<usize> for [T] {
+ type Output = T;
+
+ fn index(&self, index: usize) -> &Self::Output {
+ &self[index]
+ }
+}
+
+#[lang = "drop_in_place"]
+#[allow(unconditional_recursion)]
+pub unsafe fn drop_in_place<T: ?Sized>(to_drop: *mut T) {
+ // Code here does not matter - this is replaced by the
+ // real drop glue by the compiler.
+ drop_in_place(to_drop);
+}
+
+#[lang = "panic_location"]
+struct PanicLocation {
+ file: &'static str,
+ line: u32,
+ column: u32,
+}
+
+#[lang = "panic_bounds_check"]
+#[track_caller]
+#[no_mangle]
+fn panic_bounds_check(index: usize, len: usize) -> ! {
+ unsafe {
+ libc::printf("index out of bounds: the len is %d but the index is %d\n\0" as *const str as *const i8, len, index);
+ intrinsics::abort();
+ }
+}
+
+mod intrinsics {
+ extern "rust-intrinsic" {
+ pub fn abort() -> !;
+ }
+}
+
+#[lang = "unsize"]
+pub trait Unsize<T: ?Sized> {}
+
+#[lang = "coerce_unsized"]
+pub trait CoerceUnsized<T> {}
+
+impl<'a, 'b: 'a, T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<&'a U> for &'b T {}
+impl<'a, T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<&'a mut U> for &'a mut T {}
+impl<T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<*const U> for *const T {}
+impl<T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<*mut U> for *mut T {}
+
+#[lang = "fn_once"]
+#[rustc_paren_sugar]
+pub trait FnOnce<Args> {
+ #[lang = "fn_once_output"]
+ type Output;
+
+ extern "rust-call" fn call_once(self, args: Args) -> Self::Output;
+}
+
+#[lang = "fn_mut"]
+#[rustc_paren_sugar]
+pub trait FnMut<Args>: FnOnce<Args> {
+ extern "rust-call" fn call_mut(&mut self, args: Args) -> Self::Output;
+}
+
+#[lang = "add"]
+trait Add<RHS = Self> {
+ type Output;
+
+ fn add(self, rhs: RHS) -> Self::Output;
+}
+
+impl Add for u8 {
+ type Output = Self;
+
+ fn add(self, rhs: Self) -> Self {
+ self + rhs
+ }
+}
+
+impl Add for i8 {
+ type Output = Self;
+
+ fn add(self, rhs: Self) -> Self {
+ self + rhs
+ }
+}
+
+impl Add for i32 {
+ type Output = Self;
+
+ fn add(self, rhs: Self) -> Self {
+ self + rhs
+ }
+}
+
+impl Add for usize {
+ type Output = Self;
+
+ fn add(self, rhs: Self) -> Self {
+ self + rhs
+ }
+}
+
+impl Add for isize {
+ type Output = Self;
+
+ fn add(self, rhs: Self) -> Self {
+ self + rhs
+ }
+}
+
+#[lang = "panic"]
+#[track_caller]
+#[no_mangle]
+pub fn panic(_msg: &str) -> ! {
+ unsafe {
+ libc::puts("Panicking\0" as *const str as *const u8);
+ intrinsics::abort();
+ }
+}
+
+/*
+ * Code
+ */
+
+#[start]
+fn main(mut argc: isize, _argv: *const *const u8) -> isize {
+ let string = "Arg: %d\n\0";
+ let mut closure = || {
+ unsafe {
+ libc::printf(string as *const str as *const i8, argc);
+ }
+ };
+ closure();
+
+ let mut closure = || {
+ unsafe {
+ libc::printf("Argument: %d\n\0" as *const str as *const i8, argc);
+ }
+ };
+ closure();
+
+ let mut closure = |string| {
+ unsafe {
+ libc::printf(string as *const str as *const i8, argc);
+ }
+ };
+ closure("String arg: %d\n\0");
+
+ let mut closure = |arg: isize| {
+ unsafe {
+ libc::printf("Int argument: %d\n\0" as *const str as *const i8, arg);
+ }
+ };
+ closure(argc + 1);
+
+ let mut closure = |string, arg: isize| {
+ unsafe {
+ libc::printf(string as *const str as *const i8, arg);
+ }
+ };
+ closure("Both args: %d\n\0", argc + 10);
+
+ 0
+}
--- /dev/null
+// Compiler:
+//
+// Run-time:
+// status: 0
+// stdout: true
+// 1
+
+#![feature(arbitrary_self_types, auto_traits, lang_items, no_core, start, intrinsics)]
+
+#![no_std]
+#![no_core]
+
+/*
+ * Core
+ */
+
+// Because we don't have core yet.
+#[lang = "sized"]
+pub trait Sized {}
+
+#[lang = "copy"]
+trait Copy {
+}
+
+impl Copy for isize {}
+impl Copy for usize {}
+impl Copy for u64 {}
+impl Copy for i32 {}
+impl Copy for u32 {}
+impl Copy for bool {}
+impl Copy for u16 {}
+impl Copy for i16 {}
+impl Copy for char {}
+impl Copy for i8 {}
+impl Copy for u8 {}
+
+#[lang = "receiver"]
+trait Receiver {
+}
+
+#[lang = "freeze"]
+pub(crate) unsafe auto trait Freeze {}
+
+mod libc {
+ #[link(name = "c")]
+ extern "C" {
+ pub fn printf(format: *const i8, ...) -> i32;
+ pub fn puts(s: *const u8) -> i32;
+ }
+}
+
+#[lang = "index"]
+pub trait Index<Idx: ?Sized> {
+ type Output: ?Sized;
+ fn index(&self, index: Idx) -> &Self::Output;
+}
+
+impl<T> Index<usize> for [T; 3] {
+ type Output = T;
+
+ fn index(&self, index: usize) -> &Self::Output {
+ &self[index]
+ }
+}
+
+impl<T> Index<usize> for [T] {
+ type Output = T;
+
+ fn index(&self, index: usize) -> &Self::Output {
+ &self[index]
+ }
+}
+
+#[lang = "drop_in_place"]
+#[allow(unconditional_recursion)]
+pub unsafe fn drop_in_place<T: ?Sized>(to_drop: *mut T) {
+ // Code here does not matter - this is replaced by the
+ // real drop glue by the compiler.
+ drop_in_place(to_drop);
+}
+
+#[lang = "panic"]
+#[track_caller]
+#[no_mangle]
+pub fn panic(_msg: &str) -> ! {
+ unsafe {
+ libc::puts("Panicking\0" as *const str as *const u8);
+ intrinsics::abort();
+ }
+}
+
+#[lang = "panic_location"]
+struct PanicLocation {
+ file: &'static str,
+ line: u32,
+ column: u32,
+}
+
+#[lang = "panic_bounds_check"]
+#[track_caller]
+#[no_mangle]
+fn panic_bounds_check(index: usize, len: usize) -> ! {
+ unsafe {
+ libc::printf("index out of bounds: the len is %d but the index is %d\n\0" as *const str as *const i8, len, index);
+ intrinsics::abort();
+ }
+}
+
+mod intrinsics {
+ extern "rust-intrinsic" {
+ pub fn abort() -> !;
+ }
+}
+
+#[lang = "add"]
+trait Add<RHS = Self> {
+ type Output;
+
+ fn add(self, rhs: RHS) -> Self::Output;
+}
+
+impl Add for u8 {
+ type Output = Self;
+
+ fn add(self, rhs: Self) -> Self {
+ self + rhs
+ }
+}
+
+impl Add for i8 {
+ type Output = Self;
+
+ fn add(self, rhs: Self) -> Self {
+ self + rhs
+ }
+}
+
+impl Add for i32 {
+ type Output = Self;
+
+ fn add(self, rhs: Self) -> Self {
+ self + rhs
+ }
+}
+
+impl Add for usize {
+ type Output = Self;
+
+ fn add(self, rhs: Self) -> Self {
+ self + rhs
+ }
+}
+
+impl Add for isize {
+ type Output = Self;
+
+ fn add(self, rhs: Self) -> Self {
+ self + rhs
+ }
+}
+
+#[lang = "sub"]
+pub trait Sub<RHS = Self> {
+ type Output;
+
+ fn sub(self, rhs: RHS) -> Self::Output;
+}
+
+impl Sub for usize {
+ type Output = Self;
+
+ fn sub(self, rhs: Self) -> Self {
+ self - rhs
+ }
+}
+
+impl Sub for isize {
+ type Output = Self;
+
+ fn sub(self, rhs: Self) -> Self {
+ self - rhs
+ }
+}
+
+impl Sub for u8 {
+ type Output = Self;
+
+ fn sub(self, rhs: Self) -> Self {
+ self - rhs
+ }
+}
+
+impl Sub for i8 {
+ type Output = Self;
+
+ fn sub(self, rhs: Self) -> Self {
+ self - rhs
+ }
+}
+
+impl Sub for i16 {
+ type Output = Self;
+
+ fn sub(self, rhs: Self) -> Self {
+ self - rhs
+ }
+}
+
+#[lang = "eq"]
+pub trait PartialEq<Rhs: ?Sized = Self> {
+ fn eq(&self, other: &Rhs) -> bool;
+ fn ne(&self, other: &Rhs) -> bool;
+}
+
+impl PartialEq for u8 {
+ fn eq(&self, other: &u8) -> bool {
+ (*self) == (*other)
+ }
+ fn ne(&self, other: &u8) -> bool {
+ (*self) != (*other)
+ }
+}
+
+impl PartialEq for u16 {
+ fn eq(&self, other: &u16) -> bool {
+ (*self) == (*other)
+ }
+ fn ne(&self, other: &u16) -> bool {
+ (*self) != (*other)
+ }
+}
+
+impl PartialEq for u32 {
+ fn eq(&self, other: &u32) -> bool {
+ (*self) == (*other)
+ }
+ fn ne(&self, other: &u32) -> bool {
+ (*self) != (*other)
+ }
+}
+
+
+impl PartialEq for u64 {
+ fn eq(&self, other: &u64) -> bool {
+ (*self) == (*other)
+ }
+ fn ne(&self, other: &u64) -> bool {
+ (*self) != (*other)
+ }
+}
+
+impl PartialEq for usize {
+ fn eq(&self, other: &usize) -> bool {
+ (*self) == (*other)
+ }
+ fn ne(&self, other: &usize) -> bool {
+ (*self) != (*other)
+ }
+}
+
+impl PartialEq for i8 {
+ fn eq(&self, other: &i8) -> bool {
+ (*self) == (*other)
+ }
+ fn ne(&self, other: &i8) -> bool {
+ (*self) != (*other)
+ }
+}
+
+impl PartialEq for i32 {
+ fn eq(&self, other: &i32) -> bool {
+ (*self) == (*other)
+ }
+ fn ne(&self, other: &i32) -> bool {
+ (*self) != (*other)
+ }
+}
+
+impl PartialEq for isize {
+ fn eq(&self, other: &isize) -> bool {
+ (*self) == (*other)
+ }
+ fn ne(&self, other: &isize) -> bool {
+ (*self) != (*other)
+ }
+}
+
+impl PartialEq for char {
+ fn eq(&self, other: &char) -> bool {
+ (*self) == (*other)
+ }
+ fn ne(&self, other: &char) -> bool {
+ (*self) != (*other)
+ }
+}
+
+/*
+ * Code
+ */
+
+#[start]
+fn main(argc: isize, _argv: *const *const u8) -> isize {
+ unsafe {
+ if argc == 1 {
+ libc::printf(b"true\n\0" as *const u8 as *const i8);
+ }
+
+ let string =
+ match argc {
+ 1 => b"1\n\0",
+ 2 => b"2\n\0",
+ 3 => b"3\n\0",
+ 4 => b"4\n\0",
+ 5 => b"5\n\0",
+ _ => b"_\n\0",
+ };
+ libc::printf(string as *const u8 as *const i8);
+ }
+ 0
+}
--- /dev/null
+// Compiler:
+//
+// Run-time:
+// status: 0
+
+#![feature(auto_traits, lang_items, no_core, start)]
+
+#![no_std]
+#![no_core]
+
+/*
+ * Core
+ */
+
+// Because we don't have core yet.
+#[lang = "sized"]
+pub trait Sized {}
+
+#[lang = "copy"]
+trait Copy {
+}
+
+impl Copy for isize {}
+
+#[lang = "receiver"]
+trait Receiver {
+}
+
+#[lang = "freeze"]
+pub(crate) unsafe auto trait Freeze {}
+
+/*
+ * Code
+ */
+
+#[start]
+fn main(mut argc: isize, _argv: *const *const u8) -> isize {
+ 0
+}
--- /dev/null
+// Compiler:
+//
+// Run-time:
+// status: 2
+
+#![feature(auto_traits, lang_items, no_core, start, intrinsics)]
+
+#![no_std]
+#![no_core]
+
+mod libc {
+ #[link(name = "c")]
+ extern "C" {
+ pub fn exit(status: i32);
+ }
+}
+
+/*
+ * Core
+ */
+
+// Because we don't have core yet.
+#[lang = "sized"]
+pub trait Sized {}
+
+#[lang = "copy"]
+trait Copy {
+}
+
+impl Copy for isize {}
+
+#[lang = "receiver"]
+trait Receiver {
+}
+
+#[lang = "freeze"]
+pub(crate) unsafe auto trait Freeze {}
+
+/*
+ * Code
+ */
+
+#[start]
+fn main(mut argc: isize, _argv: *const *const u8) -> isize {
+ unsafe {
+ libc::exit(2);
+ }
+ 0
+}
--- /dev/null
+// Compiler:
+//
+// Run-time:
+// status: 1
+
+#![feature(auto_traits, lang_items, no_core, start)]
+
+#![no_std]
+#![no_core]
+
+/*
+ * Core
+ */
+
+// Because we don't have core yet.
+#[lang = "sized"]
+pub trait Sized {}
+
+#[lang = "copy"]
+trait Copy {
+}
+
+impl Copy for isize {}
+
+#[lang = "receiver"]
+trait Receiver {
+}
+
+#[lang = "freeze"]
+pub(crate) unsafe auto trait Freeze {}
+
+/*
+ * Code
+ */
+
+#[start]
+fn main(mut argc: isize, _argv: *const *const u8) -> isize {
+ 1
+}
--- /dev/null
+// Compiler:
+//
+// Run-time:
+// status: 0
+// stdout: 1
+
+#![feature(arbitrary_self_types, auto_traits, lang_items, no_core, start, intrinsics)]
+
+#![no_std]
+#![no_core]
+
+/*
+ * Core
+ */
+
+// Because we don't have core yet.
+#[lang = "sized"]
+pub trait Sized {}
+
+#[lang = "copy"]
+trait Copy {
+}
+
+impl Copy for isize {}
+impl Copy for usize {}
+impl Copy for i32 {}
+impl Copy for u8 {}
+impl Copy for i8 {}
+impl Copy for i16 {}
+
+#[lang = "receiver"]
+trait Receiver {
+}
+
+#[lang = "freeze"]
+pub(crate) unsafe auto trait Freeze {}
+
+mod libc {
+ #[link(name = "c")]
+ extern "C" {
+ pub fn printf(format: *const i8, ...) -> i32;
+ pub fn puts(s: *const u8) -> i32;
+ }
+}
+
+#[lang = "index"]
+pub trait Index<Idx: ?Sized> {
+ type Output: ?Sized;
+ fn index(&self, index: Idx) -> &Self::Output;
+}
+
+impl<T> Index<usize> for [T; 3] {
+ type Output = T;
+
+ fn index(&self, index: usize) -> &Self::Output {
+ &self[index]
+ }
+}
+
+impl<T> Index<usize> for [T] {
+ type Output = T;
+
+ fn index(&self, index: usize) -> &Self::Output {
+ &self[index]
+ }
+}
+
+#[lang = "drop_in_place"]
+#[allow(unconditional_recursion)]
+pub unsafe fn drop_in_place<T: ?Sized>(to_drop: *mut T) {
+ // Code here does not matter - this is replaced by the
+ // real drop glue by the compiler.
+ drop_in_place(to_drop);
+}
+
+#[lang = "panic"]
+#[track_caller]
+#[no_mangle]
+pub fn panic(_msg: &str) -> ! {
+ unsafe {
+ libc::puts("Panicking\0" as *const str as *const u8);
+ intrinsics::abort();
+ }
+}
+
+#[lang = "panic_location"]
+struct PanicLocation {
+ file: &'static str,
+ line: u32,
+ column: u32,
+}
+
+#[lang = "panic_bounds_check"]
+#[track_caller]
+#[no_mangle]
+fn panic_bounds_check(index: usize, len: usize) -> ! {
+ unsafe {
+ libc::printf("index out of bounds: the len is %d but the index is %d\n\0" as *const str as *const i8, len, index);
+ intrinsics::abort();
+ }
+}
+
+mod intrinsics {
+ extern "rust-intrinsic" {
+ pub fn abort() -> !;
+ }
+}
+
+#[lang = "add"]
+trait Add<RHS = Self> {
+ type Output;
+
+ fn add(self, rhs: RHS) -> Self::Output;
+}
+
+impl Add for u8 {
+ type Output = Self;
+
+ fn add(self, rhs: Self) -> Self {
+ self + rhs
+ }
+}
+
+impl Add for i8 {
+ type Output = Self;
+
+ fn add(self, rhs: Self) -> Self {
+ self + rhs
+ }
+}
+
+impl Add for i32 {
+ type Output = Self;
+
+ fn add(self, rhs: Self) -> Self {
+ self + rhs
+ }
+}
+
+impl Add for usize {
+ type Output = Self;
+
+ fn add(self, rhs: Self) -> Self {
+ self + rhs
+ }
+}
+
+impl Add for isize {
+ type Output = Self;
+
+ fn add(self, rhs: Self) -> Self {
+ self + rhs
+ }
+}
+
+#[lang = "sub"]
+pub trait Sub<RHS = Self> {
+ type Output;
+
+ fn sub(self, rhs: RHS) -> Self::Output;
+}
+
+impl Sub for usize {
+ type Output = Self;
+
+ fn sub(self, rhs: Self) -> Self {
+ self - rhs
+ }
+}
+
+impl Sub for isize {
+ type Output = Self;
+
+ fn sub(self, rhs: Self) -> Self {
+ self - rhs
+ }
+}
+
+impl Sub for u8 {
+ type Output = Self;
+
+ fn sub(self, rhs: Self) -> Self {
+ self - rhs
+ }
+}
+
+impl Sub for i8 {
+ type Output = Self;
+
+ fn sub(self, rhs: Self) -> Self {
+ self - rhs
+ }
+}
+
+impl Sub for i16 {
+ type Output = Self;
+
+ fn sub(self, rhs: Self) -> Self {
+ self - rhs
+ }
+}
+
+
+/*
+ * Code
+ */
+
+fn i16_as_i8(a: i16) -> i8 {
+ a as i8
+}
+
+fn call_func(func: fn(i16) -> i8, param: i16) -> i8 {
+ func(param)
+}
+
+#[start]
+fn main(argc: isize, _argv: *const *const u8) -> isize {
+ unsafe {
+ let result = call_func(i16_as_i8, argc as i16) as isize;
+ libc::printf(b"%ld\n\0" as *const u8 as *const i8, result);
+ }
+ 0
+}
--- /dev/null
+// Compiler:
+//
+// Run-time:
+// stdout: Panicking
+// status: signal
+
+#![allow(unused_attributes)]
+#![feature(auto_traits, lang_items, no_core, start, intrinsics)]
+
+#![no_std]
+#![no_core]
+
+/*
+ * Core
+ */
+
+// Because we don't have core yet.
+#[lang = "sized"]
+pub trait Sized {}
+
+#[lang = "copy"]
+trait Copy {
+}
+
+impl Copy for isize {}
+impl Copy for *mut i32 {}
+impl Copy for usize {}
+impl Copy for i32 {}
+impl Copy for u8 {}
+impl Copy for i8 {}
+
+#[lang = "receiver"]
+trait Receiver {
+}
+
+#[lang = "freeze"]
+pub(crate) unsafe auto trait Freeze {}
+
+#[lang = "panic_location"]
+struct PanicLocation {
+ file: &'static str,
+ line: u32,
+ column: u32,
+}
+
+mod libc {
+ #[link(name = "c")]
+ extern "C" {
+ pub fn puts(s: *const u8) -> i32;
+ pub fn fflush(stream: *mut i32) -> i32;
+
+ pub static STDOUT: *mut i32;
+ }
+}
+
+mod intrinsics {
+ extern "rust-intrinsic" {
+ pub fn abort() -> !;
+ }
+}
+
+#[lang = "panic"]
+#[track_caller]
+#[no_mangle]
+pub fn panic(_msg: &str) -> ! {
+ unsafe {
+ libc::puts("Panicking\0" as *const str as *const u8);
+ libc::fflush(libc::STDOUT);
+ intrinsics::abort();
+ }
+}
+
+#[lang = "add"]
+trait Add<RHS = Self> {
+ type Output;
+
+ fn add(self, rhs: RHS) -> Self::Output;
+}
+
+impl Add for u8 {
+ type Output = Self;
+
+ fn add(self, rhs: Self) -> Self {
+ self + rhs
+ }
+}
+
+impl Add for i8 {
+ type Output = Self;
+
+ fn add(self, rhs: Self) -> Self {
+ self + rhs
+ }
+}
+
+impl Add for i32 {
+ type Output = Self;
+
+ fn add(self, rhs: Self) -> Self {
+ self + rhs
+ }
+}
+
+impl Add for usize {
+ type Output = Self;
+
+ fn add(self, rhs: Self) -> Self {
+ self + rhs
+ }
+}
+
+impl Add for isize {
+ type Output = Self;
+
+ fn add(self, rhs: Self) -> Self {
+ self + rhs
+ }
+}
+
+/*
+ * Code
+ */
+
+#[start]
+fn main(mut argc: isize, _argv: *const *const u8) -> isize {
+ let int = 9223372036854775807isize;
+ let int = int + argc;
+ int
+}
--- /dev/null
+
+// Compiler:
+//
+// Run-time:
+// stdout: 2
+// 7
+// 6
+// 11
+
+#![allow(unused_attributes)]
+#![feature(auto_traits, lang_items, no_core, start, intrinsics, track_caller)]
+
+#![no_std]
+#![no_core]
+
+/*
+ * Core
+ */
+
+// Because we don't have core yet.
+#[lang = "sized"]
+pub trait Sized {}
+
+#[lang = "copy"]
+trait Copy {
+}
+
+impl Copy for isize {}
+impl Copy for *mut i32 {}
+impl Copy for usize {}
+impl Copy for u8 {}
+impl Copy for i8 {}
+impl Copy for i32 {}
+
+#[lang = "receiver"]
+trait Receiver {
+}
+
+#[lang = "freeze"]
+pub(crate) unsafe auto trait Freeze {}
+
+#[lang = "panic_location"]
+struct PanicLocation {
+ file: &'static str,
+ line: u32,
+ column: u32,
+}
+
+mod libc {
+ #[link(name = "c")]
+ extern "C" {
+ pub fn puts(s: *const u8) -> i32;
+ pub fn fflush(stream: *mut i32) -> i32;
+ pub fn printf(format: *const i8, ...) -> i32;
+
+ pub static STDOUT: *mut i32;
+ }
+}
+
+mod intrinsics {
+ extern "rust-intrinsic" {
+ pub fn abort() -> !;
+ }
+}
+
+#[lang = "panic"]
+#[track_caller]
+#[no_mangle]
+pub fn panic(_msg: &str) -> ! {
+ unsafe {
+ libc::puts("Panicking\0" as *const str as *const u8);
+ libc::fflush(libc::STDOUT);
+ intrinsics::abort();
+ }
+}
+
+#[lang = "add"]
+trait Add<RHS = Self> {
+ type Output;
+
+ fn add(self, rhs: RHS) -> Self::Output;
+}
+
+impl Add for u8 {
+ type Output = Self;
+
+ fn add(self, rhs: Self) -> Self {
+ self + rhs
+ }
+}
+
+impl Add for i8 {
+ type Output = Self;
+
+ fn add(self, rhs: Self) -> Self {
+ self + rhs
+ }
+}
+
+impl Add for i32 {
+ type Output = Self;
+
+ fn add(self, rhs: Self) -> Self {
+ self + rhs
+ }
+}
+
+impl Add for usize {
+ type Output = Self;
+
+ fn add(self, rhs: Self) -> Self {
+ self + rhs
+ }
+}
+
+impl Add for isize {
+ type Output = Self;
+
+ fn add(self, rhs: Self) -> Self {
+ self + rhs
+ }
+}
+
+/*
+ * Code
+ */
+
+struct Test {
+ field: isize,
+}
+
+fn test(num: isize) -> Test {
+ Test {
+ field: num + 1,
+ }
+}
+
+fn update_num(num: &mut isize) {
+ *num = *num + 5;
+}
+
+#[start]
+fn main(mut argc: isize, _argv: *const *const u8) -> isize {
+ let mut test = test(argc);
+ unsafe {
+ libc::printf(b"%ld\n\0" as *const u8 as *const i8, test.field);
+ }
+ update_num(&mut test.field);
+ unsafe {
+ libc::printf(b"%ld\n\0" as *const u8 as *const i8, test.field);
+ }
+
+ update_num(&mut argc);
+ unsafe {
+ libc::printf(b"%ld\n\0" as *const u8 as *const i8, argc);
+ }
+
+ let refe = &mut argc;
+ *refe = *refe + 5;
+ unsafe {
+ libc::printf(b"%ld\n\0" as *const u8 as *const i8, argc);
+ }
+
+ 0
+}
--- /dev/null
+// Compiler:
+//
+// Run-time:
+// stdout: 41
+// 39
+// 10
+
+#![allow(unused_attributes)]
+#![feature(auto_traits, lang_items, no_core, start, intrinsics, arbitrary_self_types)]
+
+#![no_std]
+#![no_core]
+
+/*
+ * Core
+ */
+
+// Because we don't have core yet.
+#[lang = "sized"]
+pub trait Sized {}
+
+#[lang = "copy"]
+trait Copy {
+}
+
+impl Copy for isize {}
+impl Copy for *mut i32 {}
+impl Copy for usize {}
+impl Copy for u8 {}
+impl Copy for i8 {}
+impl Copy for i16 {}
+impl Copy for i32 {}
+
+#[lang = "deref"]
+pub trait Deref {
+ type Target: ?Sized;
+
+ fn deref(&self) -> &Self::Target;
+}
+
+#[lang = "receiver"]
+trait Receiver {
+}
+
+#[lang = "freeze"]
+pub(crate) unsafe auto trait Freeze {}
+
+#[lang = "panic_location"]
+struct PanicLocation {
+ file: &'static str,
+ line: u32,
+ column: u32,
+}
+
+mod libc {
+ #[link(name = "c")]
+ extern "C" {
+ pub fn printf(format: *const i8, ...) -> i32;
+ pub fn puts(s: *const u8) -> i32;
+ pub fn fflush(stream: *mut i32) -> i32;
+
+ pub static STDOUT: *mut i32;
+ }
+}
+
+mod intrinsics {
+ extern "rust-intrinsic" {
+ pub fn abort() -> !;
+ }
+}
+
+#[lang = "panic"]
+#[track_caller]
+#[no_mangle]
+pub fn panic(_msg: &str) -> ! {
+ unsafe {
+ libc::puts("Panicking\0" as *const str as *const u8);
+ libc::fflush(libc::STDOUT);
+ intrinsics::abort();
+ }
+}
+
+#[lang = "add"]
+trait Add<RHS = Self> {
+ type Output;
+
+ fn add(self, rhs: RHS) -> Self::Output;
+}
+
+impl Add for u8 {
+ type Output = Self;
+
+ fn add(self, rhs: Self) -> Self {
+ self + rhs
+ }
+}
+
+impl Add for i8 {
+ type Output = Self;
+
+ fn add(self, rhs: Self) -> Self {
+ self + rhs
+ }
+}
+
+impl Add for i32 {
+ type Output = Self;
+
+ fn add(self, rhs: Self) -> Self {
+ self + rhs
+ }
+}
+
+impl Add for usize {
+ type Output = Self;
+
+ fn add(self, rhs: Self) -> Self {
+ self + rhs
+ }
+}
+
+impl Add for isize {
+ type Output = Self;
+
+ fn add(self, rhs: Self) -> Self {
+ self + rhs
+ }
+}
+
+#[lang = "sub"]
+pub trait Sub<RHS = Self> {
+ type Output;
+
+ fn sub(self, rhs: RHS) -> Self::Output;
+}
+
+impl Sub for usize {
+ type Output = Self;
+
+ fn sub(self, rhs: Self) -> Self {
+ self - rhs
+ }
+}
+
+impl Sub for isize {
+ type Output = Self;
+
+ fn sub(self, rhs: Self) -> Self {
+ self - rhs
+ }
+}
+
+impl Sub for u8 {
+ type Output = Self;
+
+ fn sub(self, rhs: Self) -> Self {
+ self - rhs
+ }
+}
+
+impl Sub for i8 {
+ type Output = Self;
+
+ fn sub(self, rhs: Self) -> Self {
+ self - rhs
+ }
+}
+
+impl Sub for i16 {
+ type Output = Self;
+
+ fn sub(self, rhs: Self) -> Self {
+ self - rhs
+ }
+}
+
+#[lang = "mul"]
+pub trait Mul<RHS = Self> {
+ type Output;
+
+ #[must_use]
+ fn mul(self, rhs: RHS) -> Self::Output;
+}
+
+impl Mul for u8 {
+ type Output = Self;
+
+ fn mul(self, rhs: Self) -> Self::Output {
+ self * rhs
+ }
+}
+
+impl Mul for usize {
+ type Output = Self;
+
+ fn mul(self, rhs: Self) -> Self::Output {
+ self * rhs
+ }
+}
+
+impl Mul for isize {
+ type Output = Self;
+
+ fn mul(self, rhs: Self) -> Self::Output {
+ self * rhs
+ }
+}
+
+/*
+ * Code
+ */
+
+#[start]
+fn main(mut argc: isize, _argv: *const *const u8) -> isize {
+ unsafe {
+ libc::printf(b"%ld\n\0" as *const u8 as *const i8, 40 + argc);
+ libc::printf(b"%ld\n\0" as *const u8 as *const i8, 40 - argc);
+ libc::printf(b"%ld\n\0" as *const u8 as *const i8, 10 * argc);
+ }
+ 0
+}
--- /dev/null
+// Compiler:
+//
+// Run-time:
+// status: 0
+// stdout: 1
+
+#![feature(arbitrary_self_types, auto_traits, lang_items, no_core, start, intrinsics)]
+
+#![no_std]
+#![no_core]
+
+/*
+ * Core
+ */
+
+// Because we don't have core yet.
+#[lang = "sized"]
+pub trait Sized {}
+
+#[lang = "copy"]
+trait Copy {
+}
+
+impl Copy for isize {}
+impl Copy for usize {}
+impl Copy for i32 {}
+impl Copy for u8 {}
+impl Copy for i8 {}
+impl Copy for i16 {}
+
+#[lang = "receiver"]
+trait Receiver {
+}
+
+#[lang = "freeze"]
+pub(crate) unsafe auto trait Freeze {}
+
+mod libc {
+ #[link(name = "c")]
+ extern "C" {
+ pub fn printf(format: *const i8, ...) -> i32;
+ pub fn puts(s: *const u8) -> i32;
+ }
+}
+
+#[lang = "index"]
+pub trait Index<Idx: ?Sized> {
+ type Output: ?Sized;
+ fn index(&self, index: Idx) -> &Self::Output;
+}
+
+impl<T> Index<usize> for [T; 3] {
+ type Output = T;
+
+ fn index(&self, index: usize) -> &Self::Output {
+ &self[index]
+ }
+}
+
+impl<T> Index<usize> for [T] {
+ type Output = T;
+
+ fn index(&self, index: usize) -> &Self::Output {
+ &self[index]
+ }
+}
+
+#[lang = "drop_in_place"]
+#[allow(unconditional_recursion)]
+pub unsafe fn drop_in_place<T: ?Sized>(to_drop: *mut T) {
+ // Code here does not matter - this is replaced by the
+ // real drop glue by the compiler.
+ drop_in_place(to_drop);
+}
+
+#[lang = "panic"]
+#[track_caller]
+#[no_mangle]
+pub fn panic(_msg: &str) -> ! {
+ unsafe {
+ libc::puts("Panicking\0" as *const str as *const u8);
+ intrinsics::abort();
+ }
+}
+
+#[lang = "panic_location"]
+struct PanicLocation {
+ file: &'static str,
+ line: u32,
+ column: u32,
+}
+
+#[lang = "panic_bounds_check"]
+#[track_caller]
+#[no_mangle]
+fn panic_bounds_check(index: usize, len: usize) -> ! {
+ unsafe {
+ libc::printf("index out of bounds: the len is %d but the index is %d\n\0" as *const str as *const i8, len, index);
+ intrinsics::abort();
+ }
+}
+
+mod intrinsics {
+ extern "rust-intrinsic" {
+ pub fn abort() -> !;
+ }
+}
+
+#[lang = "add"]
+trait Add<RHS = Self> {
+ type Output;
+
+ fn add(self, rhs: RHS) -> Self::Output;
+}
+
+impl Add for u8 {
+ type Output = Self;
+
+ fn add(self, rhs: Self) -> Self {
+ self + rhs
+ }
+}
+
+impl Add for i8 {
+ type Output = Self;
+
+ fn add(self, rhs: Self) -> Self {
+ self + rhs
+ }
+}
+
+impl Add for i32 {
+ type Output = Self;
+
+ fn add(self, rhs: Self) -> Self {
+ self + rhs
+ }
+}
+
+impl Add for usize {
+ type Output = Self;
+
+ fn add(self, rhs: Self) -> Self {
+ self + rhs
+ }
+}
+
+impl Add for isize {
+ type Output = Self;
+
+ fn add(self, rhs: Self) -> Self {
+ self + rhs
+ }
+}
+
+#[lang = "sub"]
+pub trait Sub<RHS = Self> {
+ type Output;
+
+ fn sub(self, rhs: RHS) -> Self::Output;
+}
+
+impl Sub for usize {
+ type Output = Self;
+
+ fn sub(self, rhs: Self) -> Self {
+ self - rhs
+ }
+}
+
+impl Sub for isize {
+ type Output = Self;
+
+ fn sub(self, rhs: Self) -> Self {
+ self - rhs
+ }
+}
+
+impl Sub for u8 {
+ type Output = Self;
+
+ fn sub(self, rhs: Self) -> Self {
+ self - rhs
+ }
+}
+
+impl Sub for i8 {
+ type Output = Self;
+
+ fn sub(self, rhs: Self) -> Self {
+ self - rhs
+ }
+}
+
+impl Sub for i16 {
+ type Output = Self;
+
+ fn sub(self, rhs: Self) -> Self {
+ self - rhs
+ }
+}
+
+
+/*
+ * Code
+ */
+
+static mut ONE: usize = 1;
+
+fn make_array() -> [u8; 3] {
+ [42, 10, 5]
+}
+
+#[start]
+fn main(argc: isize, _argv: *const *const u8) -> isize {
+ unsafe {
+ let ptr = ONE as *mut usize;
+ let value = ptr as usize;
+ libc::printf(b"%ld\n\0" as *const u8 as *const i8, value);
+ }
+ 0
+}
--- /dev/null
+// Compiler:
+//
+// Run-time:
+// status: 0
+// stdout: 10
+// 10
+// 42
+
+#![feature(auto_traits, lang_items, no_core, start, intrinsics)]
+
+#![no_std]
+#![no_core]
+
+#[lang = "copy"]
+pub unsafe trait Copy {}
+
+unsafe impl Copy for bool {}
+unsafe impl Copy for u8 {}
+unsafe impl Copy for u16 {}
+unsafe impl Copy for u32 {}
+unsafe impl Copy for u64 {}
+unsafe impl Copy for usize {}
+unsafe impl Copy for i8 {}
+unsafe impl Copy for i16 {}
+unsafe impl Copy for i32 {}
+unsafe impl Copy for isize {}
+unsafe impl Copy for f32 {}
+unsafe impl Copy for char {}
+
+mod libc {
+ #[link(name = "c")]
+ extern "C" {
+ pub fn printf(format: *const i8, ...) -> i32;
+ }
+}
+
+/*
+ * Core
+ */
+
+// Because we don't have core yet.
+#[lang = "sized"]
+pub trait Sized {}
+
+#[lang = "receiver"]
+trait Receiver {
+}
+
+#[lang = "freeze"]
+pub(crate) unsafe auto trait Freeze {}
+
+/*
+ * Code
+ */
+
+fn int_cast(a: u16, b: i16) -> (u8, u16, u32, usize, i8, i16, i32, isize, u8, u32) {
+ (
+ a as u8, a as u16, a as u32, a as usize, a as i8, a as i16, a as i32, a as isize, b as u8,
+ b as u32,
+ )
+}
+
+#[start]
+fn main(argc: isize, _argv: *const *const u8) -> isize {
+ let (a, b, c, d, e, f, g, h, i, j) = int_cast(10, 42);
+ unsafe {
+ libc::printf(b"%d\n\0" as *const u8 as *const i8, c);
+ libc::printf(b"%ld\n\0" as *const u8 as *const i8, d);
+ libc::printf(b"%ld\n\0" as *const u8 as *const i8, j);
+ }
+ 0
+}
--- /dev/null
+// Compiler:
+//
+// Run-time:
+// status: 0
+// stdout: 5
+
+#![feature(arbitrary_self_types, auto_traits, lang_items, no_core, start, intrinsics)]
+
+#![no_std]
+#![no_core]
+
+/*
+ * Core
+ */
+
+// Because we don't have core yet.
+#[lang = "sized"]
+pub trait Sized {}
+
+#[lang = "copy"]
+trait Copy {
+}
+
+impl Copy for isize {}
+impl Copy for usize {}
+impl Copy for i32 {}
+impl Copy for u32 {}
+
+#[lang = "receiver"]
+trait Receiver {
+}
+
+#[lang = "freeze"]
+pub(crate) unsafe auto trait Freeze {}
+
+mod libc {
+ #[link(name = "c")]
+ extern "C" {
+ pub fn printf(format: *const i8, ...) -> i32;
+ }
+}
+
+#[lang = "index"]
+pub trait Index<Idx: ?Sized> {
+ type Output: ?Sized;
+ fn index(&self, index: Idx) -> &Self::Output;
+}
+
+impl<T> Index<usize> for [T; 3] {
+ type Output = T;
+
+ fn index(&self, index: usize) -> &Self::Output {
+ &self[index]
+ }
+}
+
+impl<T> Index<usize> for [T] {
+ type Output = T;
+
+ fn index(&self, index: usize) -> &Self::Output {
+ &self[index]
+ }
+}
+
+#[lang = "unsize"]
+pub trait Unsize<T: ?Sized> {}
+
+#[lang = "coerce_unsized"]
+pub trait CoerceUnsized<T> {}
+
+impl<'a, 'b: 'a, T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<&'a U> for &'b T {}
+impl<'a, T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<&'a mut U> for &'a mut T {}
+impl<T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<*const U> for *const T {}
+impl<T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<*mut U> for *mut T {}
+
+#[lang = "drop_in_place"]
+#[allow(unconditional_recursion)]
+pub unsafe fn drop_in_place<T: ?Sized>(to_drop: *mut T) {
+ // Code here does not matter - this is replaced by the
+ // real drop glue by the compiler.
+ drop_in_place(to_drop);
+}
+
+#[lang = "panic_location"]
+struct PanicLocation {
+ file: &'static str,
+ line: u32,
+ column: u32,
+}
+
+#[lang = "panic_bounds_check"]
+#[track_caller]
+#[no_mangle]
+fn panic_bounds_check(index: usize, len: usize) -> ! {
+ unsafe {
+ libc::printf("index out of bounds: the len is %d but the index is %d\n\0" as *const str as *const i8, len, index);
+ intrinsics::abort();
+ }
+}
+
+mod intrinsics {
+ use super::Sized;
+
+ extern "rust-intrinsic" {
+ pub fn abort() -> !;
+ }
+}
+
+/*
+ * Code
+ */
+
+static mut TWO: usize = 2;
+
+fn index_slice(s: &[u32]) -> u32 {
+ unsafe {
+ s[TWO]
+ }
+}
+
+#[start]
+fn main(mut argc: isize, _argv: *const *const u8) -> isize {
+ let array = [42, 7, 5];
+ unsafe {
+ libc::printf(b"%ld\n\0" as *const u8 as *const i8, index_slice(&array));
+ }
+ 0
+}
--- /dev/null
+// Compiler:
+//
+// Run-time:
+// status: 0
+// stdout: 10
+// 14
+// 1
+// 12
+// 12
+// 1
+
+#![feature(auto_traits, lang_items, no_core, start, intrinsics)]
+
+#![no_std]
+#![no_core]
+
+/*
+ * Core
+ */
+
+// Because we don't have core yet.
+#[lang = "sized"]
+pub trait Sized {}
+
+#[lang = "copy"]
+trait Copy {
+}
+
+impl Copy for isize {}
+
+#[lang = "receiver"]
+trait Receiver {
+}
+
+#[lang = "freeze"]
+pub(crate) unsafe auto trait Freeze {}
+
+mod intrinsics {
+ use super::Sized;
+
+ extern "rust-intrinsic" {
+ pub fn abort() -> !;
+ }
+}
+
+mod libc {
+ #[link(name = "c")]
+ extern "C" {
+ pub fn printf(format: *const i8, ...) -> i32;
+ }
+}
+
+#[lang = "structural_peq"]
+pub trait StructuralPartialEq {}
+
+#[lang = "structural_teq"]
+pub trait StructuralEq {}
+
+#[lang = "drop_in_place"]
+#[allow(unconditional_recursion)]
+pub unsafe fn drop_in_place<T: ?Sized>(to_drop: *mut T) {
+ // Code here does not matter - this is replaced by the
+ // real drop glue by the compiler.
+ drop_in_place(to_drop);
+}
+
+/*
+ * Code
+ */
+
+struct Test {
+ field: isize,
+}
+
+struct WithRef {
+ refe: &'static Test,
+}
+
+static mut CONSTANT: isize = 10;
+
+static mut TEST: Test = Test {
+ field: 12,
+};
+
+static mut TEST2: Test = Test {
+ field: 14,
+};
+
+static mut WITH_REF: WithRef = WithRef {
+ refe: unsafe { &TEST },
+};
+
+#[start]
+fn main(mut argc: isize, _argv: *const *const u8) -> isize {
+ unsafe {
+ libc::printf(b"%ld\n\0" as *const u8 as *const i8, CONSTANT);
+ libc::printf(b"%ld\n\0" as *const u8 as *const i8, TEST2.field);
+ TEST2.field = argc;
+ libc::printf(b"%ld\n\0" as *const u8 as *const i8, TEST2.field);
+ libc::printf(b"%ld\n\0" as *const u8 as *const i8, WITH_REF.refe.field);
+ WITH_REF.refe = &TEST2;
+ libc::printf(b"%ld\n\0" as *const u8 as *const i8, TEST.field);
+ libc::printf(b"%ld\n\0" as *const u8 as *const i8, WITH_REF.refe.field);
+ }
+ 0
+}
--- /dev/null
+// Compiler:
+//
+// Run-time:
+// status: 0
+// stdout: 1
+// 2
+
+#![feature(auto_traits, lang_items, no_core, start, intrinsics)]
+
+#![no_std]
+#![no_core]
+
+/*
+ * Core
+ */
+
+// Because we don't have core yet.
+#[lang = "sized"]
+pub trait Sized {}
+
+#[lang = "copy"]
+trait Copy {
+}
+
+impl Copy for isize {}
+
+#[lang = "receiver"]
+trait Receiver {
+}
+
+#[lang = "freeze"]
+pub(crate) unsafe auto trait Freeze {}
+
+mod libc {
+ #[link(name = "c")]
+ extern "C" {
+ pub fn printf(format: *const i8, ...) -> i32;
+ }
+}
+
+/*
+ * Code
+ */
+
+struct Test {
+ field: isize,
+}
+
+struct Two {
+ two: isize,
+}
+
+fn one() -> isize {
+ 1
+}
+
+#[start]
+fn main(mut argc: isize, _argv: *const *const u8) -> isize {
+ let test = Test {
+ field: one(),
+ };
+ let two = Two {
+ two: 2,
+ };
+ unsafe {
+ libc::printf(b"%ld\n\0" as *const u8 as *const i8, test.field);
+ libc::printf(b"%ld\n\0" as *const u8 as *const i8, two.two);
+ }
+ 0
+}
--- /dev/null
+// Compiler:
+//
+// Run-time:
+// status: 0
+// stdout: 3
+
+#![feature(auto_traits, lang_items, no_core, start, intrinsics)]
+
+#![no_std]
+#![no_core]
+
+/*
+ * Core
+ */
+
+// Because we don't have core yet.
+#[lang = "sized"]
+pub trait Sized {}
+
+#[lang = "copy"]
+trait Copy {
+}
+
+impl Copy for isize {}
+
+#[lang = "receiver"]
+trait Receiver {
+}
+
+#[lang = "freeze"]
+pub(crate) unsafe auto trait Freeze {}
+
+mod libc {
+ #[link(name = "c")]
+ extern "C" {
+ pub fn printf(format: *const i8, ...) -> i32;
+ }
+}
+
+/*
+ * Code
+ */
+
+#[start]
+fn main(mut argc: isize, _argv: *const *const u8) -> isize {
+ let test: (isize, isize, isize) = (3, 1, 4);
+ unsafe {
+ libc::printf(b"%ld\n\0" as *const u8 as *const i8, test.0);
+ }
+ 0
+}
use rustc_codegen_ssa::traits::*;
use rustc_codegen_ssa::{ModuleCodegen, ModuleKind};
use rustc_data_structures::small_c_str::SmallCStr;
+use rustc_metadata::EncodedMetadata;
use rustc_middle::dep_graph;
use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrs;
-use rustc_middle::middle::cstore::EncodedMetadata;
use rustc_middle::middle::exported_symbols;
use rustc_middle::mir::mono::{Linkage, Visibility};
use rustc_middle::ty::TyCtxt;
let (metadata_llcx, metadata_llmod) = (&*llvm_module.llcx, llvm_module.llmod());
let mut compressed = rustc_metadata::METADATA_HEADER.to_vec();
- FrameEncoder::new(&mut compressed).write_all(&metadata.raw_data).unwrap();
+ FrameEncoder::new(&mut compressed).write_all(metadata.raw_data()).unwrap();
let llmeta = common::bytes_in_context(metadata_llcx, &compressed);
let llconst = common::struct_in_context(metadata_llcx, &[llmeta], false);
use rustc_codegen_ssa::{CodegenResults, CompiledModule};
use rustc_data_structures::fx::FxHashMap;
use rustc_errors::{ErrorReported, FatalError, Handler};
+use rustc_metadata::EncodedMetadata;
use rustc_middle::dep_graph::{WorkProduct, WorkProductId};
-use rustc_middle::middle::cstore::EncodedMetadata;
use rustc_middle::ty::TyCtxt;
use rustc_session::config::{OptLevel, OutputFilenames, PrintRequest};
use rustc_session::Session;
rustc_incremental = { path = "../rustc_incremental" }
rustc_index = { path = "../rustc_index" }
rustc_macros = { path = "../rustc_macros" }
+rustc_metadata = { path = "../rustc_metadata" }
rustc_target = { path = "../rustc_target" }
rustc_session = { path = "../rustc_session" }
// metadata in rlib files is wrapped in a "dummy" object file for
// the target platform so the rlib can be processed entirely by
// normal linkers for the platform.
- let metadata = create_metadata_file(sess, &codegen_results.metadata.raw_data);
+ let metadata = create_metadata_file(sess, codegen_results.metadata.raw_data());
ab.add_file(&emit_metadata(sess, &metadata, tmpdir));
// After adding all files to the archive, we need to update the
use rustc_incremental::{
copy_cgu_workproduct_to_incr_comp_cache_dir, in_incr_comp_dir, in_incr_comp_dir_sess,
};
+use rustc_metadata::EncodedMetadata;
use rustc_middle::dep_graph::{WorkProduct, WorkProductId};
-use rustc_middle::middle::cstore::EncodedMetadata;
use rustc_middle::middle::exported_symbols::SymbolExportLevel;
use rustc_middle::ty::TyCtxt;
use rustc_session::cgu_reuse_tracker::CguReuseTracker;
use rustc_hir::def_id::{DefId, LOCAL_CRATE};
use rustc_hir::lang_items::LangItem;
use rustc_index::vec::Idx;
+use rustc_metadata::EncodedMetadata;
use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrs;
-use rustc_middle::middle::cstore::EncodedMetadata;
use rustc_middle::middle::lang_items;
use rustc_middle::mir::mono::{CodegenUnit, CodegenUnitNameBuilder, MonoItem};
use rustc_middle::ty::layout::{HasTyCtxt, LayoutOf, TyAndLayout};
pub modules: Vec<CompiledModule>,
pub allocator_module: Option<CompiledModule>,
pub metadata_module: Option<CompiledModule>,
- pub metadata: rustc_middle::middle::cstore::EncodedMetadata,
+ pub metadata: rustc_metadata::EncodedMetadata,
pub crate_info: CrateInfo,
}
use rustc_ast::expand::allocator::AllocatorKind;
use rustc_data_structures::fx::FxHashMap;
use rustc_errors::ErrorReported;
+use rustc_metadata::EncodedMetadata;
use rustc_middle::dep_graph::{WorkProduct, WorkProductId};
-use rustc_middle::middle::cstore::{EncodedMetadata, MetadataLoaderDyn};
+use rustc_middle::middle::cstore::MetadataLoaderDyn;
use rustc_middle::ty::layout::{FnAbiOf, HasTyCtxt, LayoutOf, TyAndLayout};
use rustc_middle::ty::query::Providers;
use rustc_middle::ty::{Ty, TyCtxt};
}
Aggregate(ref kind, ref operands) => {
+ // active_field_index is for union initialization.
let (dest, active_field_index) = match **kind {
mir::AggregateKind::Adt(adt_def, variant_index, _, _, active_field_index) => {
self.write_discriminant(variant_index, &dest)?;
if adt_def.is_enum() {
- (self.place_downcast(&dest, variant_index)?, active_field_index)
+ assert!(active_field_index.is_none());
+ (self.place_downcast(&dest, variant_index)?, None)
} else {
+ if active_field_index.is_some() {
+ assert_eq!(operands.len(), 1);
+ }
(dest, active_field_index)
}
}
for (i, operand) in operands.iter().enumerate() {
let op = self.eval_operand(operand, None)?;
- // Ignore zero-sized fields.
- if !op.layout.is_zst() {
- let field_index = active_field_index.unwrap_or(i);
- let field_dest = self.place_field(&dest, field_index)?;
- self.copy_op(&op, &field_dest)?;
- }
+ let field_index = active_field_index.unwrap_or(i);
+ let field_dest = self.place_field(&dest, field_index)?;
+ self.copy_op(&op, &field_dest)?;
}
}
}
Len(place) => {
- // FIXME(CTFE): don't allow computing the length of arrays in const eval
let src = self.eval_place(place)?;
let mplace = self.force_allocation(&src)?;
let len = mplace.len(self)?;
/// Call this method when `inspect_node` has returned `None`. Having the
/// caller decide avoids mutual recursion between the two methods and allows
/// us to maintain an allocated stack for nodes on the path between calls.
+ #[instrument(skip(self, initial), level = "debug")]
fn walk_unvisited_node(&mut self, initial: G::Node) -> WalkReturn<S> {
struct VisitingNodeFrame<G: DirectedGraph, Successors> {
node: G::Node,
Some(iter) => iter,
None => {
// This None marks that we still have the initialize this node's frame.
- debug!("walk_unvisited_node(depth = {:?}, node = {:?})", depth, node);
+ debug!(?depth, ?node);
debug_assert!(matches!(self.node_states[node], NodeState::NotVisited));
return_value.take().into_iter().map(|walk| (*successor_node, Some(walk)));
let successor_walk = successors.by_ref().map(|successor_node| {
- debug!(
- "walk_unvisited_node: node = {:?} successor_ode = {:?}",
- node, successor_node
- );
+ debug!(?node, ?successor_node);
(successor_node, self.inspect_node(successor_node))
});
// Track the minimum depth we can reach.
assert!(successor_min_depth <= depth);
if successor_min_depth < *min_depth {
- debug!(
- "walk_unvisited_node: node = {:?} successor_min_depth = {:?}",
- node, successor_min_depth
- );
+ debug!(?node, ?successor_min_depth);
*min_depth = successor_min_depth;
*min_cycle_root = successor_node;
}
Some(WalkReturn::Complete { scc_index: successor_scc_index }) => {
// Push the completed SCC indices onto
// the `successors_stack` for later.
- debug!(
- "walk_unvisited_node: node = {:?} successor_scc_index = {:?}",
- node, successor_scc_index
- );
+ debug!(?node, ?successor_scc_index);
successors_stack.push(successor_scc_index);
}
None => {
let depth = depth + 1;
- debug!("walk_node(depth = {:?}, node = {:?})", depth, successor_node);
+ debug!(?depth, ?successor_node);
// Remember which node the return value will come from.
frame.successor_node = successor_node;
// Start a new stack frame the step into it.
fn error_recursion_limit_reached(&mut self) {
let expn_data = self.cx.current_expansion.id.expn_data();
- let suggested_limit = self.cx.ecfg.recursion_limit * 2;
+ let suggested_limit = match self.cx.ecfg.recursion_limit {
+ Limit(0) => Limit(2),
+ limit => limit * 2,
+ };
self.cx
.struct_span_err(
expn_data.call_site,
&format!("recursion limit reached while expanding `{}`", expn_data.kind.descr()),
)
.help(&format!(
- "consider adding a `#![recursion_limit=\"{}\"]` attribute to your crate (`{}`)",
+ "consider increasing the recursion limit by adding a \
+ `#![recursion_limit = \"{}\"]` attribute to your crate (`{}`)",
suggested_limit, self.cx.ecfg.crate_name,
))
.emit();
Oom, sym::oom, oom, Target::Fn, GenericRequirement::None;
AllocLayout, sym::alloc_layout, alloc_layout, Target::Struct, GenericRequirement::None;
- Start, sym::start, start_fn, Target::Fn, GenericRequirement::None;
+ Start, sym::start, start_fn, Target::Fn, GenericRequirement::Exact(1);
EhPersonality, sym::eh_personality, eh_personality, Target::Fn, GenericRequirement::None;
EhCatchTypeinfo, sym::eh_catch_typeinfo, eh_catch_typeinfo, Target::Static, GenericRequirement::None;
impl<'a, 'tcx> Trace<'a, 'tcx> {
/// Makes `a <: b` where `a` may or may not be expected (if
/// `a_is_expected` is true, then `a` is expected).
+ #[instrument(skip(self), level = "debug")]
pub fn sub<T>(self, a: T, b: T) -> InferResult<'tcx, ()>
where
T: Relate<'tcx>,
{
- debug!("sub({:?} <: {:?})", a, b);
let Trace { at, trace, a_is_expected } = self;
at.infcx.commit_if_ok(|_| {
let mut fields = at.infcx.combine_fields(trace, at.param_env);
/// Makes `a == b`; the expectation is set by the call to
/// `trace()`.
+ #[instrument(skip(self), level = "debug")]
pub fn eq<T>(self, a: T, b: T) -> InferResult<'tcx, ()>
where
T: Relate<'tcx>,
{
- debug!("eq({:?} == {:?})", a, b);
let Trace { at, trace, a_is_expected } = self;
at.infcx.commit_if_ok(|_| {
let mut fields = at.infcx.combine_fields(trace, at.param_env);
})
}
+ #[instrument(skip(self), level = "debug")]
pub fn lub<T>(self, a: T, b: T) -> InferResult<'tcx, T>
where
T: Relate<'tcx>,
{
- debug!("lub({:?} \\/ {:?})", a, b);
let Trace { at, trace, a_is_expected } = self;
at.infcx.commit_if_ok(|_| {
let mut fields = at.infcx.combine_fields(trace, at.param_env);
})
}
+ #[instrument(skip(self), level = "debug")]
pub fn glb<T>(self, a: T, b: T) -> InferResult<'tcx, T>
where
T: Relate<'tcx>,
{
- debug!("glb({:?} /\\ {:?})", a, b);
let Trace { at, trace, a_is_expected } = self;
at.infcx.commit_if_ok(|_| {
let mut fields = at.infcx.combine_fields(trace, at.param_env);
/// the same thing happens, but the resulting query is marked as ambiguous.
/// - Finally, if any of the obligations result in a hard error,
/// then `Err(NoSolution)` is returned.
+ #[instrument(skip(self, inference_vars, answer, fulfill_cx), level = "trace")]
pub fn make_canonicalized_query_response<T>(
&self,
inference_vars: CanonicalVarValues<'tcx>,
let query_response = self.make_query_response(inference_vars, answer, fulfill_cx)?;
let canonical_result = self.canonicalize_response(query_response);
- debug!("make_canonicalized_query_response: canonical_result = {:#?}", canonical_result);
+ debug!("canonical_result = {:#?}", canonical_result);
Ok(self.tcx.arena.alloc(canonical_result))
}
/// Helper for `make_canonicalized_query_response` that does
/// everything up until the final canonicalization.
+ #[instrument(skip(self, fulfill_cx), level = "debug")]
fn make_query_response<T>(
&self,
inference_vars: CanonicalVarValues<'tcx>,
{
let tcx = self.tcx;
- debug!(
- "make_query_response(\
- inference_vars={:?}, \
- answer={:?})",
- inference_vars, answer,
- );
-
// Select everything, returning errors.
let true_errors = fulfill_cx.select_where_possible(self).err().unwrap_or_else(Vec::new);
debug!("true_errors = {:#?}", true_errors);
/// the actual types (`?T`, `Option<?T>`) -- and remember that
/// after the snapshot is popped, the variable `?T` is no longer
/// unified.
+ #[instrument(skip(self, f), level = "debug")]
pub fn fudge_inference_if_ok<T, E, F>(&self, f: F) -> Result<T, E>
where
F: FnOnce() -> Result<T, E>,
T: TypeFoldable<'tcx>,
{
- debug!("fudge_inference_if_ok()");
-
let variable_lengths = self.variable_lengths();
let (mut fudger, value) = self.probe(|_| {
match f() {
use rustc_middle::ty::{self, Binder, TypeFoldable};
impl<'a, 'tcx> CombineFields<'a, 'tcx> {
+ #[instrument(skip(self), level = "debug")]
pub fn higher_ranked_sub<T>(
&mut self,
a: Binder<'tcx, T>,
where
T: Relate<'tcx>,
{
- debug!("higher_ranked_sub(a={:?}, b={:?})", a, b);
-
// Rather than checking the subtype relationship between `a` and `b`
// as-is, we need to do some extra work here in order to make sure
// that function subtyping works correctly with respect to regions
}
}
+ #[instrument(skip(self, snapshot), level = "debug")]
fn rollback_to(&self, cause: &str, snapshot: CombinedSnapshot<'a, 'tcx>) {
- debug!("rollback_to(cause={})", cause);
let CombinedSnapshot {
undo_snapshot,
region_constraints_snapshot,
inner.unwrap_region_constraints().rollback_to(region_constraints_snapshot);
}
+ #[instrument(skip(self, snapshot), level = "debug")]
fn commit_from(&self, snapshot: CombinedSnapshot<'a, 'tcx>) {
- debug!("commit_from()");
let CombinedSnapshot {
undo_snapshot,
region_constraints_snapshot: _,
}
/// Executes `f` and commit the bindings.
+ #[instrument(skip(self, f), level = "debug")]
pub fn commit_unconditionally<R, F>(&self, f: F) -> R
where
F: FnOnce(&CombinedSnapshot<'a, 'tcx>) -> R,
{
- debug!("commit_unconditionally()");
let snapshot = self.start_snapshot();
let r = f(&snapshot);
self.commit_from(snapshot);
}
/// Execute `f` and commit the bindings if closure `f` returns `Ok(_)`.
+ #[instrument(skip(self, f), level = "debug")]
pub fn commit_if_ok<T, E, F>(&self, f: F) -> Result<T, E>
where
F: FnOnce(&CombinedSnapshot<'a, 'tcx>) -> Result<T, E>,
{
- debug!("commit_if_ok()");
let snapshot = self.start_snapshot();
let r = f(&snapshot);
debug!("commit_if_ok() -- r.is_ok() = {}", r.is_ok());
}
/// Execute `f` then unroll any bindings it creates.
+ #[instrument(skip(self, f), level = "debug")]
pub fn probe<R, F>(&self, f: F) -> R
where
F: FnOnce(&CombinedSnapshot<'a, 'tcx>) -> R,
{
- debug!("probe()");
let snapshot = self.start_snapshot();
let r = f(&snapshot);
self.rollback_to("probe", snapshot);
}
/// If `should_skip` is true, then execute `f` then unroll any bindings it creates.
+ #[instrument(skip(self, f), level = "debug")]
pub fn probe_maybe_skip_leak_check<R, F>(&self, should_skip: bool, f: F) -> R
where
F: FnOnce(&CombinedSnapshot<'a, 'tcx>) -> R,
{
- debug!("probe()");
let snapshot = self.start_snapshot();
let was_skip_leak_check = self.skip_leak_check.get();
if should_skip {
})
}
+ #[instrument(skip(self), level = "debug")]
pub fn sub_regions(
&self,
origin: SubregionOrigin<'tcx>,
a: ty::Region<'tcx>,
b: ty::Region<'tcx>,
) {
- debug!("sub_regions({:?} <: {:?})", a, b);
self.inner.borrow_mut().unwrap_region_constraints().make_subregion(origin, a, b);
}
/// Require that the region `r` be equal to one of the regions in
/// the set `regions`.
+ #[instrument(skip(self), level = "debug")]
pub fn member_constraint(
&self,
opaque_type_def_id: DefId,
region: ty::Region<'tcx>,
in_regions: &Lrc<Vec<ty::Region<'tcx>>>,
) {
- debug!("member_constraint({:?} <: {:?})", region, in_regions);
self.inner.borrow_mut().unwrap_region_constraints().member_constraint(
opaque_type_def_id,
definition_span,
true
}
+ #[instrument(skip(self, info), level = "trace")]
fn relate_with_variance<T: Relate<'tcx>>(
&mut self,
variance: ty::Variance,
a: T,
b: T,
) -> RelateResult<'tcx, T> {
- debug!("relate_with_variance(variance={:?}, a={:?}, b={:?})", variance, a, b);
-
let old_ambient_variance = self.ambient_variance;
self.ambient_variance = self.ambient_variance.xform(variance);
self.ambient_variance_info = self.ambient_variance_info.xform(info);
- debug!("relate_with_variance: ambient_variance = {:?}", self.ambient_variance);
+ debug!(?self.ambient_variance);
let r = self.relate(a, b)?;
self.ambient_variance = old_ambient_variance;
- debug!("relate_with_variance: r={:?}", r);
+ debug!(?r);
Ok(r)
}
+ #[instrument(skip(self), level = "debug")]
fn tys(&mut self, a: Ty<'tcx>, mut b: Ty<'tcx>) -> RelateResult<'tcx, Ty<'tcx>> {
let a = self.infcx.shallow_resolve(a);
}
_ => {
- debug!("tys(a={:?}, b={:?}, variance={:?})", a, b, self.ambient_variance);
+ debug!(?a, ?b, ?self.ambient_variance);
// Will also handle unification of `IntVar` and `FloatVar`.
self.infcx.super_combine_tys(self, a, b)
}
}
+ #[instrument(skip(self), level = "trace")]
fn regions(
&mut self,
a: ty::Region<'tcx>,
b: ty::Region<'tcx>,
) -> RelateResult<'tcx, ty::Region<'tcx>> {
- debug!("regions(a={:?}, b={:?}, variance={:?})", a, b, self.ambient_variance);
+ debug!(?self.ambient_variance);
let v_a = self.replace_bound_region(a, ty::INNERMOST, &self.a_scopes);
let v_b = self.replace_bound_region(b, ty::INNERMOST, &self.b_scopes);
- debug!("regions: v_a = {:?}", v_a);
- debug!("regions: v_b = {:?}", v_b);
+ debug!(?v_a);
+ debug!(?v_b);
if self.ambient_covariance() {
// Covariance: a <= b. Hence, `b: a`.
}
}
+ #[instrument(skip(self), level = "trace")]
fn binders<T>(
&mut self,
a: ty::Binder<'tcx, T>,
// - Instantiate binders on `b` universally, yielding a universe U1.
// - Instantiate binders on `a` existentially in U1.
- debug!("binders({:?}: {:?}, ambient_variance={:?})", a, b, self.ambient_variance);
+ debug!(?self.ambient_variance);
if let (Some(a), Some(b)) = (a.no_bound_vars(), b.no_bound_vars()) {
// Fast path for the common case.
let b_scope = self.create_scope(b, UniversallyQuantified(true));
let a_scope = self.create_scope(a, UniversallyQuantified(false));
- debug!("binders: a_scope = {:?} (existential)", a_scope);
- debug!("binders: b_scope = {:?} (universal)", b_scope);
+ debug!(?a_scope, "(existential)");
+ debug!(?b_scope, "(universal)");
self.b_scopes.push(b_scope);
self.a_scopes.push(a_scope);
let a_scope = self.create_scope(a, UniversallyQuantified(true));
let b_scope = self.create_scope(b, UniversallyQuantified(false));
- debug!("binders: a_scope = {:?} (universal)", a_scope);
- debug!("binders: b_scope = {:?} (existential)", b_scope);
+ debug!(?a_scope, "(universal)");
+ debug!(?b_scope, "(existential)");
self.a_scopes.push(a_scope);
self.b_scopes.push(b_scope);
});
}
+ #[instrument(skip(self, origin), level = "debug")]
pub fn make_subregion(
&mut self,
origin: SubregionOrigin<'tcx>,
sup: Region<'tcx>,
) {
// cannot add constraints once regions are resolved
- debug!(
- "RegionConstraintCollector: make_subregion({:?}, {:?}) due to {:?}",
- sub, sup, origin
- );
+ debug!("origin = {:#?}", origin);
match (sub, sup) {
(&ReLateBound(..), _) | (_, &ReLateBound(..)) => {
use rustc_hir::Crate;
use rustc_lint::LintStore;
use rustc_metadata::creader::CStore;
+use rustc_metadata::{encode_metadata, EncodedMetadata};
use rustc_middle::arena::Arena;
use rustc_middle::dep_graph::DepGraph;
-use rustc_middle::middle;
use rustc_middle::middle::cstore::{MetadataLoader, MetadataLoaderDyn};
use rustc_middle::ty::query::Providers;
use rustc_middle::ty::{self, GlobalCtxt, ResolverOutputs, TyCtxt};
register_lints: impl Fn(&Session, &mut LintStore),
mut krate: ast::Crate,
crate_name: &str,
-) -> Result<(ast::Crate, Lrc<LintStore>)> {
+) -> Result<(ast::Crate, LintStore)> {
krate = sess.time("attributes_injection", || {
rustc_builtin_macros::cmdline_attrs::inject(
krate,
}
});
- let lint_store = Lrc::new(lint_store);
- sess.init_lint_store(lint_store.clone());
-
Ok((krate, lint_store))
}
fn encode_and_write_metadata(
tcx: TyCtxt<'_>,
outputs: &OutputFilenames,
-) -> (middle::cstore::EncodedMetadata, bool) {
+) -> (EncodedMetadata, bool) {
#[derive(PartialEq, Eq, PartialOrd, Ord)]
enum MetadataKind {
None,
.unwrap_or(MetadataKind::None);
let metadata = match metadata_kind {
- MetadataKind::None => middle::cstore::EncodedMetadata::new(),
- MetadataKind::Uncompressed | MetadataKind::Compressed => tcx.encode_metadata(),
+ MetadataKind::None => EncodedMetadata::new(),
+ MetadataKind::Uncompressed | MetadataKind::Compressed => encode_metadata(tcx),
};
let _prof_timer = tcx.sess.prof.generic_activity("write_crate_metadata");
.tempdir_in(out_filename.parent().unwrap())
.unwrap_or_else(|err| tcx.sess.fatal(&format!("couldn't create a temp dir: {}", err)));
let metadata_tmpdir = MaybeTempDir::new(metadata_tmpdir, tcx.sess.opts.cg.save_temps);
- let metadata_filename = emit_metadata(tcx.sess, &metadata.raw_data, &metadata_tmpdir);
+ let metadata_filename = emit_metadata(tcx.sess, metadata.raw_data(), &metadata_tmpdir);
if let Err(e) = util::non_durable_rename(&metadata_filename, &out_filename) {
tcx.sess.fatal(&format!("failed to write {}: {}", out_filename.display(), e));
}
let krate = self.parse()?.take();
let empty: &(dyn Fn(&Session, &mut LintStore) + Sync + Send) = &|_, _| {};
- let result = passes::register_plugins(
+ let (krate, lint_store) = passes::register_plugins(
self.session(),
&*self.codegen_backend().metadata_loader(),
self.compiler.register_lints.as_deref().unwrap_or_else(|| empty),
// called, which happens within passes::register_plugins().
self.dep_graph_future().ok();
- Ok(result)
+ Ok((krate, Lrc::new(lint_store)))
})
}
use rustc_session::lint::{BuiltinLintDiagnostics, ExternDepSpec};
use rustc_session::lint::{FutureIncompatibleInfo, Level, Lint, LintBuffer, LintId};
use rustc_session::Session;
-use rustc_session::SessionLintStore;
use rustc_span::lev_distance::find_best_match_for_name;
use rustc_span::{symbol::Symbol, MultiSpan, Span, DUMMY_SP};
use rustc_target::abi;
lint_groups: FxHashMap<&'static str, LintGroup>,
}
-impl SessionLintStore for LintStore {
- fn name_to_lint(&self, lint_name: &str) -> LintId {
- let lints = self
- .find_lints(lint_name)
- .unwrap_or_else(|_| panic!("Failed to find lint with name `{}`", lint_name));
-
- if let &[lint] = lints.as_slice() {
- return lint;
- } else {
- panic!("Found mutliple lints with name `{}`: {:?}", lint_name, lints);
- }
- }
-}
-
/// The target of the `by_name` map, which accounts for renaming/deprecation.
#[derive(Debug)]
enum TargetLint {
LLVMRustPrepareThinLTOResolveWeak(const LLVMRustThinLTOData *Data, LLVMModuleRef M) {
Module &Mod = *unwrap(M);
const auto &DefinedGlobals = Data->ModuleToDefinedGVSummaries.lookup(Mod.getModuleIdentifier());
+#if LLVM_VERSION_GE(14, 0)
+ thinLTOFinalizeInModule(Mod, DefinedGlobals, /*PropagateAttrs=*/true);
+#else
thinLTOResolvePrevailingInModule(Mod, DefinedGlobals);
+#endif
return true;
}
pub mod dynamic_lib;
pub mod locator;
-pub use rmeta::METADATA_HEADER;
+pub use rmeta::{encode_metadata, EncodedMetadata, METADATA_HEADER};
use crate::creader::{CStore, LoadedMacro};
use crate::foreign_modules;
use crate::native_libs;
-use crate::rmeta::encoder;
use rustc_ast as ast;
use rustc_data_structures::stable_map::FxHashMap;
use rustc_hir::definitions::{DefKey, DefPath, DefPathHash};
use rustc_middle::hir::exports::Export;
use rustc_middle::middle::cstore::ForeignModule;
-use rustc_middle::middle::cstore::{CrateSource, CrateStore, EncodedMetadata};
+use rustc_middle::middle::cstore::{CrateSource, CrateStore};
use rustc_middle::middle::exported_symbols::ExportedSymbol;
use rustc_middle::middle::stability::DeprecationEntry;
use rustc_middle::ty::query::Providers;
fn expn_hash_to_expn_id(&self, cnum: CrateNum, index_guess: u32, hash: ExpnHash) -> ExpnId {
self.get_crate_data(cnum).expn_hash_to_expn_id(index_guess, hash)
}
-
- fn encode_metadata(&self, tcx: TyCtxt<'_>) -> EncodedMetadata {
- encoder::encode_metadata(tcx)
- }
}
use rustc_index::bit_set::GrowableBitSet;
use rustc_index::vec::Idx;
use rustc_middle::hir::map::Map;
-use rustc_middle::middle::cstore::{EncodedMetadata, ForeignModule, LinkagePreference, NativeLib};
+use rustc_middle::middle::cstore::{ForeignModule, LinkagePreference, NativeLib};
use rustc_middle::middle::dependency_format::Linkage;
use rustc_middle::middle::exported_symbols::{
metadata_symbol_name, ExportedSymbol, SymbolExportLevel,
// will allow us to slice the metadata to the precise length that we just
// generated regardless of trailing bytes that end up in it.
-pub(super) fn encode_metadata(tcx: TyCtxt<'_>) -> EncodedMetadata {
+#[derive(Encodable, Decodable)]
+pub struct EncodedMetadata {
+ raw_data: Vec<u8>,
+}
+
+impl EncodedMetadata {
+ #[inline]
+ pub fn new() -> EncodedMetadata {
+ EncodedMetadata { raw_data: Vec::new() }
+ }
+
+ #[inline]
+ pub fn raw_data(&self) -> &[u8] {
+ &self.raw_data[..]
+ }
+}
+
+pub fn encode_metadata(tcx: TyCtxt<'_>) -> EncodedMetadata {
+ let _prof_timer = tcx.prof.verbose_generic_activity("generate_crate_metadata");
+
// Since encoding metadata is not in a query, and nothing is cached,
// there's no need to do dep-graph tracking for any of it.
tcx.dep_graph.assert_ignored();
pub use decoder::{provide, provide_extern};
crate use decoder::{CrateMetadata, CrateNumMap, MetadataBlob};
use encoder::EncodeContext;
+pub use encoder::{encode_metadata, EncodedMetadata};
use rustc_span::hygiene::SyntaxContextData;
mod decoder;
//! are *mostly* used as a part of that interface, but these should
//! probably get a better home if someone can find one.
-use crate::ty::TyCtxt;
-
use rustc_ast as ast;
use rustc_data_structures::sync::{self, MetadataRef};
use rustc_hir::def_id::{CrateNum, DefId, StableCrateId, LOCAL_CRATE};
Path,
}
-#[derive(Encodable, Decodable)]
-pub struct EncodedMetadata {
- pub raw_data: Vec<u8>,
-}
-
-impl EncodedMetadata {
- pub fn new() -> EncodedMetadata {
- EncodedMetadata { raw_data: Vec::new() }
- }
-}
-
/// The backend's way to give the crate store access to the metadata in a library.
/// Note that it returns the raw metadata bytes stored in the library file, whether
/// it is compressed, uncompressed, some weird mix, etc.
/// Fetch a DefId from a DefPathHash for a foreign crate.
fn def_path_hash_to_def_id(&self, cnum: CrateNum, hash: DefPathHash) -> DefId;
fn expn_hash_to_expn_id(&self, cnum: CrateNum, index_guess: u32, hash: ExpnHash) -> ExpnId;
-
- // utility functions
- fn encode_metadata(&self, tcx: TyCtxt<'_>) -> EncodedMetadata;
}
pub type CrateStoreDyn = dyn CrateStore + sync::Sync;
use crate::infer::canonical::{Canonical, CanonicalVarInfo, CanonicalVarInfos};
use crate::lint::{struct_lint_level, LintDiagnosticBuilder, LintLevelSource};
use crate::middle;
-use crate::middle::cstore::EncodedMetadata;
use crate::middle::resolve_lifetime::{self, LifetimeScopeForPath, ObjectLifetimeDefault};
use crate::middle::stability;
use crate::mir::interpret::{self, AllocId, Allocation, ConstValue, Scalar};
)
}
- pub fn encode_metadata(self) -> EncodedMetadata {
- let _prof_timer = self.prof.verbose_generic_activity("generate_crate_metadata");
- self.untracked_resolutions.cstore.encode_metadata(self)
- }
-
/// Note that this is *untracked* and should only be used within the query
/// system if the result is otherwise tracked through queries
pub fn cstore_untracked(self) -> &'tcx ty::CrateStoreDyn {
== Some(FoundFlags)
}
+ #[instrument(level = "trace")]
fn has_type_flags(&self, flags: TypeFlags) -> bool {
self.visit_with(&mut HasTypeFlagsVisitor { tcx: None, flags }).break_value()
== Some(FoundFlags)
t
}
+ #[instrument(skip(self), level = "debug")]
fn fold_region(&mut self, r: ty::Region<'tcx>) -> ty::Region<'tcx> {
match *r {
ty::ReLateBound(debruijn, _) if debruijn < self.current_index => {
- debug!(
- "RegionFolder.fold_region({:?}) skipped bound region (current index={:?})",
- r, self.current_index
- );
+ debug!(?self.current_index, "skipped bound region");
*self.skipped_regions = true;
r
}
_ => {
- debug!(
- "RegionFolder.fold_region({:?}) folding free region (current_index={:?})",
- r, self.current_index
- );
+ debug!(?self.current_index, "folding free region");
(self.fold_region_fn)(r, self.current_index)
}
}
flags: ty::TypeFlags,
}
+impl std::fmt::Debug for HasTypeFlagsVisitor<'tcx> {
+ fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ self.flags.fmt(fmt)
+ }
+}
+
impl<'tcx> TypeVisitor<'tcx> for HasTypeFlagsVisitor<'tcx> {
type BreakTy = FoundFlags;
fn tcx_for_anon_const_substs(&self) -> Option<TyCtxt<'tcx>> {
}
#[inline]
+ #[instrument(level = "trace")]
fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<Self::BreakTy> {
let flags = t.flags();
- debug!("HasTypeFlagsVisitor: t={:?} flags={:?} self.flags={:?}", t, flags, self.flags);
+ trace!(t.flags=?t.flags());
if flags.intersects(self.flags) {
ControlFlow::Break(FoundFlags)
} else {
}
#[inline]
+ #[instrument(skip(self), level = "trace")]
fn visit_region(&mut self, r: ty::Region<'tcx>) -> ControlFlow<Self::BreakTy> {
let flags = r.type_flags();
- debug!("HasTypeFlagsVisitor: r={:?} r.flags={:?} self.flags={:?}", r, flags, self.flags);
+ trace!(r.flags=?flags);
if flags.intersects(self.flags) {
ControlFlow::Break(FoundFlags)
} else {
}
#[inline]
+ #[instrument(level = "trace")]
fn visit_const(&mut self, c: &'tcx ty::Const<'tcx>) -> ControlFlow<Self::BreakTy> {
let flags = FlagComputation::for_const(c);
- debug!("HasTypeFlagsVisitor: c={:?} c.flags={:?} self.flags={:?}", c, flags, self.flags);
+ trace!(r.flags=?flags);
if flags.intersects(self.flags) {
ControlFlow::Break(FoundFlags)
} else {
}
#[inline]
+ #[instrument(level = "trace")]
fn visit_unevaluated_const(&mut self, uv: ty::Unevaluated<'tcx>) -> ControlFlow<Self::BreakTy> {
let flags = FlagComputation::for_unevaluated_const(uv);
- debug!("HasTypeFlagsVisitor: uv={:?} uv.flags={:?} self.flags={:?}", uv, flags, self.flags);
+ trace!(r.flags=?flags);
if flags.intersects(self.flags) {
ControlFlow::Break(FoundFlags)
} else {
}
#[inline]
+ #[instrument(level = "trace")]
fn visit_predicate(&mut self, predicate: ty::Predicate<'tcx>) -> ControlFlow<Self::BreakTy> {
let flags = predicate.inner.flags;
- debug!(
- "HasTypeFlagsVisitor: predicate={:?} flags={:?} self.flags={:?}",
- predicate, flags, self.flags
- );
+ trace!(predicate.flags=?flags);
if flags.intersects(self.flags) {
ControlFlow::Break(FoundFlags)
} else {
// Defaults (should not be overridden):
+ #[instrument(skip(self), level = "debug")]
fn default_print_def_path(
self,
def_id: DefId,
substs: &'tcx [GenericArg<'tcx>],
) -> Result<Self::Path, Self::Error> {
- debug!("default_print_def_path: def_id={:?}, substs={:?}", def_id, substs);
let key = self.tcx().def_key(def_id);
- debug!("default_print_def_path: key={:?}", key);
+ debug!(?key);
match key.disambiguated_data.data {
DefPathData::CrateRoot => {
Ok(inner)
}
+ #[instrument(skip(self), level = "debug")]
fn prepare_late_bound_region_info<T>(&mut self, value: &ty::Binder<'tcx, T>)
where
T: TypeFoldable<'tcx>,
{
- debug!("prepare_late_bound_region_info(value: {:?})", value);
-
struct LateBoundRegionNameCollector<'a, 'tcx> {
tcx: TyCtxt<'tcx>,
used_region_names: &'a mut FxHashSet<Symbol>,
Some(self.tcx)
}
+ #[instrument(skip(self), level = "trace")]
fn visit_region(&mut self, r: ty::Region<'tcx>) -> ControlFlow<Self::BreakTy> {
- debug!("LateBoundRegionNameCollector::visit_region(r: {:?}, address: {:p})", r, &r);
+ trace!("address: {:p}", r);
if let ty::ReLateBound(_, ty::BoundRegion { kind: ty::BrNamed(_, name), .. }) = *r {
self.used_region_names.insert(name);
} else if let ty::RePlaceholder(ty::PlaceholderRegion {
// We collect types in order to prevent really large types from compiling for
// a really long time. See issue #83150 for why this is necessary.
+ #[instrument(skip(self), level = "trace")]
fn visit_ty(&mut self, ty: Ty<'tcx>) -> ControlFlow<Self::BreakTy> {
- debug!("LateBoundRegionNameCollector::visit_ty(ty: {:?}", ty);
let not_previously_inserted = self.type_collector.insert(ty);
if not_previously_inserted {
ty.super_visit_with(self)
}
/// Expands the given impl trait type, stopping if the type is recursive.
+ #[instrument(skip(self), level = "debug")]
pub fn try_expand_impl_trait_type(
self,
def_id: DefId,
};
let expanded_type = visitor.expand_opaque_ty(def_id, substs).unwrap();
+ trace!(?expanded_type);
if visitor.found_recursion { Err(expanded_type) } else { Ok(expanded_type) }
}
}
})
.collect();
- let destination = this.cfg.start_new_block();
+ if !options.contains(InlineAsmOptions::NORETURN) {
+ this.cfg.push_assign_unit(block, source_info, destination, this.tcx);
+ }
+ let destination_block = this.cfg.start_new_block();
this.cfg.terminate(
block,
source_info,
destination: if options.contains(InlineAsmOptions::NORETURN) {
None
} else {
- Some(destination)
+ Some(destination_block)
},
},
);
- destination.unit()
+ destination_block.unit()
}
// These cases don't actually need a destination
let mut expr = self.make_mirror_unadjusted(hir_expr);
+ let adjustment_span = match self.adjustment_span {
+ Some((hir_id, span)) if hir_id == hir_expr.hir_id => Some(span),
+ _ => None,
+ };
+
// Now apply adjustments, if any.
for adjustment in self.typeck_results.expr_adjustments(hir_expr) {
debug!("make_mirror: expr={:?} applying adjustment={:?}", expr, adjustment);
- expr = self.apply_adjustment(hir_expr, expr, adjustment);
+ let span = expr.span;
+ expr =
+ self.apply_adjustment(hir_expr, expr, adjustment, adjustment_span.unwrap_or(span));
}
// Next, wrap this up in the expr's scope.
hir_expr: &'tcx hir::Expr<'tcx>,
mut expr: Expr<'tcx>,
adjustment: &Adjustment<'tcx>,
+ mut span: Span,
) -> Expr<'tcx> {
- let Expr { temp_lifetime, mut span, .. } = expr;
+ let Expr { temp_lifetime, .. } = expr;
// Adjust the span from the block, to the last expression of the
// block. This is a better span when returning a mutable reference
fn make_mirror_unadjusted(&mut self, expr: &'tcx hir::Expr<'tcx>) -> Expr<'tcx> {
let expr_ty = self.typeck_results().expr_ty(expr);
+ let expr_span = expr.span;
let temp_lifetime = self.region_scope_tree.temporary_scope(expr.hir_id.local_id);
let kind = match expr.kind {
hir::ExprKind::MethodCall(_, method_span, ref args, fn_span) => {
// Rewrite a.b(c) into UFCS form like Trait::b(a, c)
let expr = self.method_callee(expr, method_span, None);
+ // When we apply adjustments to the receiver, use the span of
+ // the overall method call for better diagnostics. args[0]
+ // is guaranteed to exist, since a method call always has a receiver.
+ let old_adjustment_span = self.adjustment_span.replace((args[0].hir_id, expr_span));
+ tracing::info!("Using method span: {:?}", expr.span);
let args = self.mirror_exprs(args);
+ self.adjustment_span = old_adjustment_span;
ExprKind::Call {
ty: expr.ty,
fun: self.thir.exprs.push(expr),
use rustc_data_structures::steal::Steal;
use rustc_hir as hir;
use rustc_hir::def_id::{DefId, LocalDefId};
+use rustc_hir::HirId;
use rustc_hir::Node;
use rustc_middle::middle::region;
use rustc_middle::mir::interpret::{LitToConstError, LitToConstInput};
crate region_scope_tree: &'tcx region::ScopeTree,
crate typeck_results: &'tcx ty::TypeckResults<'tcx>,
+ /// When applying adjustments to the expression
+ /// with the given `HirId`, use the given `Span`,
+ /// instead of the usual span. This is used to
+ /// assign the span of an overall method call
+ /// (e.g. `my_val.foo()`) to the adjustment expressions
+ /// for the receiver.
+ adjustment_span: Option<(HirId, Span)>,
+
/// The `DefId` of the owner of this body.
body_owner: DefId,
}
region_scope_tree: tcx.region_scope_tree(def.did),
typeck_results,
body_owner: def.did.to_def_id(),
+ adjustment_span: None,
}
}
+use super::deconstruct_pat::{Constructor, DeconstructedPat};
use super::usefulness::{
- compute_match_usefulness, expand_pattern, is_wildcard, MatchArm, MatchCheckCtxt, Reachability,
- UsefulnessReport,
+ compute_match_usefulness, MatchArm, MatchCheckCtxt, Reachability, UsefulnessReport,
};
use super::{PatCtxt, PatternError};
use rustc_hir::def_id::DefId;
use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor};
use rustc_hir::{HirId, Pat};
-use rustc_middle::thir::PatKind;
-use rustc_middle::ty::{self, Ty, TyCtxt};
+use rustc_middle::ty::{self, AdtDef, Ty, TyCtxt};
use rustc_session::lint::builtin::{
BINDINGS_WITH_VARIANT_NAME, IRREFUTABLE_LET_PATTERNS, UNREACHABLE_PATTERNS,
};
use rustc_session::Session;
use rustc_span::{DesugaringKind, ExpnKind, Span};
-use std::slice;
crate fn check_match(tcx: TyCtxt<'_>, def_id: DefId) {
let body_id = match def_id.as_local() {
Some(id) => tcx.hir().body_owned_by(tcx.hir().local_def_id_to_hir_id(id)),
};
+ let pattern_arena = TypedArena::default();
let mut visitor = MatchVisitor {
tcx,
typeck_results: tcx.typeck_body(body_id),
param_env: tcx.param_env(def_id),
- pattern_arena: TypedArena::default(),
+ pattern_arena: &pattern_arena,
};
visitor.visit_body(tcx.hir().body(body_id));
}
struct_span_err!(sess, sp, E0004, "{}", &error_message)
}
-struct MatchVisitor<'a, 'tcx> {
+#[derive(PartialEq)]
+enum RefutableFlag {
+ Irrefutable,
+ Refutable,
+}
+use RefutableFlag::*;
+
+struct MatchVisitor<'a, 'p, 'tcx> {
tcx: TyCtxt<'tcx>,
typeck_results: &'a ty::TypeckResults<'tcx>,
param_env: ty::ParamEnv<'tcx>,
- pattern_arena: TypedArena<super::Pat<'tcx>>,
+ pattern_arena: &'p TypedArena<DeconstructedPat<'p, 'tcx>>,
}
-impl<'tcx> Visitor<'tcx> for MatchVisitor<'_, 'tcx> {
+impl<'tcx> Visitor<'tcx> for MatchVisitor<'_, '_, 'tcx> {
type Map = intravisit::ErasedMap<'tcx>;
fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
hir::LocalSource::AssignDesugar(_) => ("destructuring assignment binding", None),
};
self.check_irrefutable(&loc.pat, msg, sp);
- self.check_patterns(&loc.pat);
+ self.check_patterns(&loc.pat, Irrefutable);
}
fn visit_param(&mut self, param: &'tcx hir::Param<'tcx>) {
intravisit::walk_param(self, param);
self.check_irrefutable(¶m.pat, "function argument", None);
- self.check_patterns(¶m.pat);
+ self.check_patterns(¶m.pat, Irrefutable);
}
}
}
}
-impl<'tcx> MatchVisitor<'_, 'tcx> {
- fn check_patterns(&self, pat: &Pat<'_>) {
+impl<'p, 'tcx> MatchVisitor<'_, 'p, 'tcx> {
+ fn check_patterns(&self, pat: &Pat<'_>, rf: RefutableFlag) {
pat.walk_always(|pat| check_borrow_conflicts_in_at_patterns(self, pat));
- check_for_bindings_named_same_as_variants(self, pat);
+ check_for_bindings_named_same_as_variants(self, pat, rf);
}
- fn lower_pattern<'p>(
+ fn lower_pattern(
&self,
cx: &mut MatchCheckCtxt<'p, 'tcx>,
pat: &'tcx hir::Pat<'tcx>,
have_errors: &mut bool,
- ) -> (&'p super::Pat<'tcx>, Ty<'tcx>) {
+ ) -> &'p DeconstructedPat<'p, 'tcx> {
let mut patcx = PatCtxt::new(self.tcx, self.param_env, self.typeck_results);
patcx.include_lint_checks();
let pattern = patcx.lower_pattern(pat);
- let pattern_ty = pattern.ty;
- let pattern: &_ = cx.pattern_arena.alloc(expand_pattern(pattern));
+ let pattern: &_ = cx.pattern_arena.alloc(DeconstructedPat::from_pat(cx, &pattern));
if !patcx.errors.is_empty() {
*have_errors = true;
patcx.report_inlining_errors();
}
- (pattern, pattern_ty)
+ pattern
}
- fn new_cx(&self, hir_id: HirId) -> MatchCheckCtxt<'_, 'tcx> {
+ fn new_cx(&self, hir_id: HirId) -> MatchCheckCtxt<'p, 'tcx> {
MatchCheckCtxt {
tcx: self.tcx,
param_env: self.param_env,
}
fn check_let(&mut self, pat: &'tcx hir::Pat<'tcx>, expr: &hir::Expr<'_>, span: Span) {
- self.check_patterns(pat);
+ self.check_patterns(pat, Refutable);
let mut cx = self.new_cx(expr.hir_id);
- let tpat = self.lower_pattern(&mut cx, pat, &mut false).0;
- check_let_reachability(&mut cx, pat.hir_id, &tpat, span);
+ let tpat = self.lower_pattern(&mut cx, pat, &mut false);
+ check_let_reachability(&mut cx, pat.hir_id, tpat, span);
}
fn check_match(
for arm in arms {
// Check the arm for some things unrelated to exhaustiveness.
- self.check_patterns(&arm.pat);
+ self.check_patterns(&arm.pat, Refutable);
if let Some(hir::Guard::IfLet(ref pat, _)) = arm.guard {
- self.check_patterns(pat);
- let tpat = self.lower_pattern(&mut cx, pat, &mut false).0;
- check_let_reachability(&mut cx, pat.hir_id, &tpat, tpat.span);
+ self.check_patterns(pat, Refutable);
+ let tpat = self.lower_pattern(&mut cx, pat, &mut false);
+ check_let_reachability(&mut cx, pat.hir_id, tpat, tpat.span());
}
}
let arms: Vec<_> = arms
.iter()
.map(|hir::Arm { pat, guard, .. }| MatchArm {
- pat: self.lower_pattern(&mut cx, pat, &mut have_errors).0,
+ pat: self.lower_pattern(&mut cx, pat, &mut have_errors),
hir_id: pat.hir_id,
has_guard: guard.is_some(),
})
let scrut_ty = self.typeck_results.expr_ty_adjusted(scrut);
let report = compute_match_usefulness(&cx, &arms, scrut.hir_id, scrut_ty);
- report_arm_reachability(&cx, &report, |_, arm_span, arm_hir_id, catchall| {
- match source {
- hir::MatchSource::ForLoopDesugar | hir::MatchSource::Normal => {
- unreachable_pattern(cx.tcx, arm_span, arm_hir_id, catchall);
- }
- // Unreachable patterns in try and await expressions occur when one of
- // the arms are an uninhabited type. Which is OK.
- hir::MatchSource::AwaitDesugar | hir::MatchSource::TryDesugar => {}
+ match source {
+ hir::MatchSource::ForLoopDesugar | hir::MatchSource::Normal => {
+ report_arm_reachability(&cx, &report)
}
- });
+ // Unreachable patterns in try and await expressions occur when one of
+ // the arms are an uninhabited type. Which is OK.
+ hir::MatchSource::AwaitDesugar | hir::MatchSource::TryDesugar => {}
+ }
// Check if the match is exhaustive.
- // Note: An empty match isn't the same as an empty matrix for diagnostics purposes,
- // since an empty matrix can occur when there are arms, if those arms all have guards.
let is_empty_match = arms.is_empty();
let witnesses = report.non_exhaustiveness_witnesses;
if !witnesses.is_empty() {
fn check_irrefutable(&self, pat: &'tcx Pat<'tcx>, origin: &str, sp: Option<Span>) {
let mut cx = self.new_cx(pat.hir_id);
- let (pattern, pattern_ty) = self.lower_pattern(&mut cx, pat, &mut false);
+ let pattern = self.lower_pattern(&mut cx, pat, &mut false);
+ let pattern_ty = pattern.ty();
let arms = vec![MatchArm { pat: pattern, hir_id: pat.hir_id, has_guard: false }];
let report = compute_match_usefulness(&cx, &arms, pat.hir_id, pattern_ty);
return;
}
- let joined_patterns = joined_uncovered_patterns(&witnesses);
+ let joined_patterns = joined_uncovered_patterns(&cx, &witnesses);
let mut err = struct_span_err!(
self.tcx.sess,
pat.span,
}
}
-fn check_for_bindings_named_same_as_variants(cx: &MatchVisitor<'_, '_>, pat: &Pat<'_>) {
+fn check_for_bindings_named_same_as_variants(
+ cx: &MatchVisitor<'_, '_, '_>,
+ pat: &Pat<'_>,
+ rf: RefutableFlag,
+) {
pat.walk_always(|p| {
if let hir::PatKind::Binding(_, _, ident, None) = p.kind {
if let Some(ty::BindByValue(hir::Mutability::Not)) =
variant.ident == ident && variant.ctor_kind == CtorKind::Const
})
{
+ let variant_count = edef.variants.len();
cx.tcx.struct_span_lint_hir(
BINDINGS_WITH_VARIANT_NAME,
p.hir_id,
p.span,
|lint| {
let ty_path = cx.tcx.def_path_str(edef.did);
- lint.build(&format!(
+ let mut err = lint.build(&format!(
"pattern binding `{}` is named the same as one \
- of the variants of the type `{}`",
+ of the variants of the type `{}`",
ident, ty_path
- ))
- .code(error_code!(E0170))
- .span_suggestion(
- p.span,
- "to match on the variant, qualify the path",
- format!("{}::{}", ty_path, ident),
- Applicability::MachineApplicable,
- )
- .emit();
+ ));
+ err.code(error_code!(E0170));
+ // If this is an irrefutable pattern, and there's > 1 variant,
+ // then we can't actually match on this. Applying the below
+ // suggestion would produce code that breaks on `check_irrefutable`.
+ if rf == Refutable || variant_count == 1 {
+ err.span_suggestion(
+ p.span,
+ "to match on the variant, qualify the path",
+ format!("{}::{}", ty_path, ident),
+ Applicability::MachineApplicable,
+ );
+ }
+ err.emit();
},
)
}
}
/// Checks for common cases of "catchall" patterns that may not be intended as such.
-fn pat_is_catchall(pat: &super::Pat<'_>) -> bool {
- use PatKind::*;
- match &*pat.kind {
- Binding { subpattern: None, .. } => true,
- Binding { subpattern: Some(s), .. } | Deref { subpattern: s } => pat_is_catchall(s),
- Leaf { subpatterns: s } => s.iter().all(|p| pat_is_catchall(&p.pattern)),
+fn pat_is_catchall(pat: &DeconstructedPat<'_, '_>) -> bool {
+ use Constructor::*;
+ match pat.ctor() {
+ Wildcard => true,
+ Single => pat.iter_fields().all(|pat| pat_is_catchall(pat)),
_ => false,
}
}
fn check_let_reachability<'p, 'tcx>(
cx: &mut MatchCheckCtxt<'p, 'tcx>,
pat_id: HirId,
- pat: &'p super::Pat<'tcx>,
+ pat: &'p DeconstructedPat<'p, 'tcx>,
span: Span,
) {
let arms = [MatchArm { pat, hir_id: pat_id, has_guard: false }];
- let report = compute_match_usefulness(&cx, &arms, pat_id, pat.ty);
-
- report_arm_reachability(&cx, &report, |arm_index, arm_span, arm_hir_id, _| {
- match let_source(cx.tcx, pat_id) {
- LetSource::IfLet | LetSource::WhileLet => {
- match arm_index {
- // The arm with the user-specified pattern.
- 0 => unreachable_pattern(cx.tcx, arm_span, arm_hir_id, None),
- // The arm with the wildcard pattern.
- 1 => irrefutable_let_pattern(cx.tcx, pat_id, arm_span),
- _ => bug!(),
- }
- }
- LetSource::IfLetGuard if arm_index == 0 => {
- unreachable_pattern(cx.tcx, arm_span, arm_hir_id, None);
- }
- _ => {}
- }
- });
+ let report = compute_match_usefulness(&cx, &arms, pat_id, pat.ty());
+
+ // Report if the pattern is unreachable, which can only occur when the type is uninhabited.
+ // This also reports unreachable sub-patterns though, so we can't just replace it with an
+ // `is_uninhabited` check.
+ report_arm_reachability(&cx, &report);
if report.non_exhaustiveness_witnesses.is_empty() {
// The match is exhaustive, i.e. the `if let` pattern is irrefutable.
}
/// Report unreachable arms, if any.
-fn report_arm_reachability<'p, 'tcx, F>(
+fn report_arm_reachability<'p, 'tcx>(
cx: &MatchCheckCtxt<'p, 'tcx>,
report: &UsefulnessReport<'p, 'tcx>,
- unreachable: F,
-) where
- F: Fn(usize, Span, HirId, Option<Span>),
-{
+) {
use Reachability::*;
let mut catchall = None;
- for (arm_index, (arm, is_useful)) in report.arm_usefulness.iter().enumerate() {
+ for (arm, is_useful) in report.arm_usefulness.iter() {
match is_useful {
- Unreachable => unreachable(arm_index, arm.pat.span, arm.hir_id, catchall),
+ Unreachable => unreachable_pattern(cx.tcx, arm.pat.span(), arm.hir_id, catchall),
Reachable(unreachables) if unreachables.is_empty() => {}
// The arm is reachable, but contains unreachable subpatterns (from or-patterns).
Reachable(unreachables) => {
}
}
if !arm.has_guard && catchall.is_none() && pat_is_catchall(arm.pat) {
- catchall = Some(arm.pat.span);
+ catchall = Some(arm.pat.span());
}
}
}
cx: &MatchCheckCtxt<'p, 'tcx>,
scrut_ty: Ty<'tcx>,
sp: Span,
- witnesses: Vec<super::Pat<'tcx>>,
+ witnesses: Vec<DeconstructedPat<'p, 'tcx>>,
is_empty_match: bool,
) {
let non_empty_enum = match scrut_ty.kind() {
format!("non-exhaustive patterns: type `{}` is non-empty", scrut_ty),
);
} else {
- let joined_patterns = joined_uncovered_patterns(&witnesses);
+ let joined_patterns = joined_uncovered_patterns(cx, &witnesses);
err = create_e0004(
cx.tcx.sess,
sp,
if (scrut_ty == cx.tcx.types.usize || scrut_ty == cx.tcx.types.isize)
&& !is_empty_match
&& witnesses.len() == 1
- && is_wildcard(&witnesses[0])
+ && matches!(witnesses[0].ctor(), Constructor::NonExhaustive)
{
err.note(&format!(
"`{}` does not have a fixed maximum value, \
err.emit();
}
-crate fn joined_uncovered_patterns(witnesses: &[super::Pat<'_>]) -> String {
+crate fn joined_uncovered_patterns<'p, 'tcx>(
+ cx: &MatchCheckCtxt<'p, 'tcx>,
+ witnesses: &[DeconstructedPat<'p, 'tcx>],
+) -> String {
const LIMIT: usize = 3;
+ let pat_to_str = |pat: &DeconstructedPat<'p, 'tcx>| pat.to_pat(cx).to_string();
match witnesses {
[] => bug!(),
- [witness] => format!("`{}`", witness),
+ [witness] => format!("`{}`", witness.to_pat(cx)),
[head @ .., tail] if head.len() < LIMIT => {
- let head: Vec<_> = head.iter().map(<_>::to_string).collect();
- format!("`{}` and `{}`", head.join("`, `"), tail)
+ let head: Vec<_> = head.iter().map(pat_to_str).collect();
+ format!("`{}` and `{}`", head.join("`, `"), tail.to_pat(cx))
}
_ => {
let (head, tail) = witnesses.split_at(LIMIT);
- let head: Vec<_> = head.iter().map(<_>::to_string).collect();
+ let head: Vec<_> = head.iter().map(pat_to_str).collect();
format!("`{}` and {} more", head.join("`, `"), tail.len())
}
}
}
-crate fn pattern_not_covered_label(witnesses: &[super::Pat<'_>], joined_patterns: &str) -> String {
+crate fn pattern_not_covered_label(
+ witnesses: &[DeconstructedPat<'_, '_>],
+ joined_patterns: &str,
+) -> String {
format!("pattern{} {} not covered", rustc_errors::pluralize!(witnesses.len()), joined_patterns)
}
/// Point at the definition of non-covered `enum` variants.
-fn adt_defined_here(
- cx: &MatchCheckCtxt<'_, '_>,
+fn adt_defined_here<'p, 'tcx>(
+ cx: &MatchCheckCtxt<'p, 'tcx>,
err: &mut DiagnosticBuilder<'_>,
- ty: Ty<'_>,
- witnesses: &[super::Pat<'_>],
+ ty: Ty<'tcx>,
+ witnesses: &[DeconstructedPat<'p, 'tcx>],
) {
let ty = ty.peel_refs();
if let ty::Adt(def, _) = ty.kind() {
}
if witnesses.len() < 4 {
- for sp in maybe_point_at_variant(ty, &witnesses) {
+ for sp in maybe_point_at_variant(cx, def, witnesses.iter()) {
err.span_label(sp, "not covered");
}
}
}
}
-fn maybe_point_at_variant(ty: Ty<'_>, patterns: &[super::Pat<'_>]) -> Vec<Span> {
+fn maybe_point_at_variant<'a, 'p: 'a, 'tcx: 'a>(
+ cx: &MatchCheckCtxt<'p, 'tcx>,
+ def: &AdtDef,
+ patterns: impl Iterator<Item = &'a DeconstructedPat<'p, 'tcx>>,
+) -> Vec<Span> {
+ use Constructor::*;
let mut covered = vec![];
- if let ty::Adt(def, _) = ty.kind() {
- // Don't point at variants that have already been covered due to other patterns to avoid
- // visual clutter.
- for pattern in patterns {
- use PatKind::{AscribeUserType, Deref, Leaf, Or, Variant};
- match &*pattern.kind {
- AscribeUserType { subpattern, .. } | Deref { subpattern } => {
- covered.extend(maybe_point_at_variant(ty, slice::from_ref(&subpattern)));
+ for pattern in patterns {
+ if let Variant(variant_index) = pattern.ctor() {
+ if let ty::Adt(this_def, _) = pattern.ty().kind() {
+ if this_def.did != def.did {
+ continue;
}
- Variant { adt_def, variant_index, subpatterns, .. } if adt_def.did == def.did => {
- let sp = def.variants[*variant_index].ident.span;
- if covered.contains(&sp) {
- continue;
- }
- covered.push(sp);
-
- let pats = subpatterns
- .iter()
- .map(|field_pattern| field_pattern.pattern.clone())
- .collect::<Box<[_]>>();
- covered.extend(maybe_point_at_variant(ty, &pats));
- }
- Leaf { subpatterns } => {
- let pats = subpatterns
- .iter()
- .map(|field_pattern| field_pattern.pattern.clone())
- .collect::<Box<[_]>>();
- covered.extend(maybe_point_at_variant(ty, &pats));
- }
- Or { pats } => {
- let pats = pats.iter().cloned().collect::<Box<[_]>>();
- covered.extend(maybe_point_at_variant(ty, &pats));
- }
- _ => {}
}
+ let sp = def.variants[*variant_index].ident.span;
+ if covered.contains(&sp) {
+ // Don't point at variants that have already been covered due to other patterns to avoid
+ // visual clutter.
+ continue;
+ }
+ covered.push(sp);
}
+ covered.extend(maybe_point_at_variant(cx, def, pattern.iter_fields()));
}
covered
}
/// Check if a by-value binding is by-value. That is, check if the binding's type is not `Copy`.
-fn is_binding_by_move(cx: &MatchVisitor<'_, '_>, hir_id: HirId, span: Span) -> bool {
+fn is_binding_by_move(cx: &MatchVisitor<'_, '_, '_>, hir_id: HirId, span: Span) -> bool {
!cx.typeck_results.node_type(hir_id).is_copy_modulo_regions(cx.tcx.at(span), cx.param_env)
}
/// - `x @ Some(ref mut? y)`.
///
/// This analysis is *not* subsumed by NLL.
-fn check_borrow_conflicts_in_at_patterns(cx: &MatchVisitor<'_, '_>, pat: &Pat<'_>) {
+fn check_borrow_conflicts_in_at_patterns(cx: &MatchVisitor<'_, '_, '_>, pat: &Pat<'_>) {
// Extract `sub` in `binding @ sub`.
let (name, sub) = match &pat.kind {
hir::PatKind::Binding(.., name, Some(sub)) => (*name, sub),
use self::SliceKind::*;
use super::compare_const_vals;
-use super::usefulness::{is_wildcard, MatchCheckCtxt, PatCtxt};
+use super::usefulness::{MatchCheckCtxt, PatCtxt};
use rustc_data_structures::captures::Captures;
use rustc_index::vec::Idx;
use rustc_middle::mir::Field;
use rustc_middle::thir::{FieldPat, Pat, PatKind, PatRange};
use rustc_middle::ty::layout::IntegerExt;
-use rustc_middle::ty::{self, Const, Ty, TyCtxt};
+use rustc_middle::ty::{self, Const, Ty, TyCtxt, VariantDef};
use rustc_session::lint;
use rustc_span::{Span, DUMMY_SP};
use rustc_target::abi::{Integer, Size, VariantIdx};
use smallvec::{smallvec, SmallVec};
+use std::cell::Cell;
use std::cmp::{self, max, min, Ordering};
+use std::fmt;
use std::iter::{once, IntoIterator};
use std::ops::RangeInclusive;
+/// Recursively expand this pattern into its subpatterns. Only useful for or-patterns.
+fn expand_or_pat<'p, 'tcx>(pat: &'p Pat<'tcx>) -> Vec<&'p Pat<'tcx>> {
+ fn expand<'p, 'tcx>(pat: &'p Pat<'tcx>, vec: &mut Vec<&'p Pat<'tcx>>) {
+ if let PatKind::Or { pats } = pat.kind.as_ref() {
+ for pat in pats {
+ expand(pat, vec);
+ }
+ } else {
+ vec.push(pat)
+ }
+ }
+
+ let mut pats = Vec::new();
+ expand(pat, &mut pats);
+ pats
+}
+
/// An inclusive interval, used for precise integer exhaustiveness checking.
/// `IntRange`s always store a contiguous range. This means that values are
/// encoded such that `0` encodes the minimum value for the integer,
///
/// `IntRange` is never used to encode an empty range or a "range" that wraps
/// around the (offset) space: i.e., `range.lo <= range.hi`.
-#[derive(Clone, Debug, PartialEq, Eq)]
+#[derive(Clone, PartialEq, Eq)]
pub(super) struct IntRange {
range: RangeInclusive<u128>,
+ /// Keeps the bias used for encoding the range. It depends on the type of the range and
+ /// possibly the pointer size of the current architecture. The algorithm ensures we never
+ /// compare `IntRange`s with different types/architectures.
+ bias: u128,
}
impl IntRange {
value.try_eval_bits(tcx, param_env, ty)
})()?;
let val = val ^ bias;
- Some(IntRange { range: val..=val })
+ Some(IntRange { range: val..=val, bias })
} else {
None
}
// This should have been caught earlier by E0030.
bug!("malformed range pattern: {}..={}", lo, (hi - offset));
}
- Some(IntRange { range: lo..=(hi - offset) })
+ Some(IntRange { range: lo..=(hi - offset), bias })
} else {
None
}
let (lo, hi) = self.boundaries();
let (other_lo, other_hi) = other.boundaries();
if lo <= other_hi && other_lo <= hi {
- Some(IntRange { range: max(lo, other_lo)..=min(hi, other_hi) })
+ Some(IntRange { range: max(lo, other_lo)..=min(hi, other_hi), bias: self.bias })
} else {
None
}
(lo == other_hi || hi == other_lo) && !self.is_singleton() && !other.is_singleton()
}
+ /// Only used for displaying the range properly.
fn to_pat<'tcx>(&self, tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> Pat<'tcx> {
let (lo, hi) = self.boundaries();
- let bias = IntRange::signed_bias(tcx, ty);
+ let bias = self.bias;
let (lo, hi) = (lo ^ bias, hi ^ bias);
let env = ty::ParamEnv::empty().and(ty);
}
/// Lint on likely incorrect range patterns (#63987)
- pub(super) fn lint_overlapping_range_endpoints<'a, 'tcx: 'a>(
+ pub(super) fn lint_overlapping_range_endpoints<'a, 'p: 'a, 'tcx: 'a>(
&self,
- pcx: PatCtxt<'_, '_, 'tcx>,
- ctors: impl Iterator<Item = (&'a Constructor<'tcx>, Span)>,
+ pcx: PatCtxt<'_, 'p, 'tcx>,
+ pats: impl Iterator<Item = &'a DeconstructedPat<'p, 'tcx>>,
column_count: usize,
hir_id: HirId,
) {
return;
}
- let overlaps: Vec<_> = ctors
- .filter_map(|(ctor, span)| Some((ctor.as_int_range()?, span)))
+ let overlaps: Vec<_> = pats
+ .filter_map(|pat| Some((pat.ctor().as_int_range()?, pat.span())))
.filter(|(range, _)| self.suspicious_intersection(range))
.map(|(range, span)| (self.intersection(&range).unwrap(), span))
.collect();
}
}
+/// Note: this is often not what we want: e.g. `false` is converted into the range `0..=0` and
+/// would be displayed as such. To render properly, convert to a pattern first.
+impl fmt::Debug for IntRange {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ let (lo, hi) = self.boundaries();
+ let bias = self.bias;
+ let (lo, hi) = (lo ^ bias, hi ^ bias);
+ write!(f, "{}", lo)?;
+ write!(f, "{}", RangeEnd::Included)?;
+ write!(f, "{}", hi)
+ }
+}
+
/// Represents a border between 2 integers. Because the intervals spanning borders must be able to
/// cover every integer, we need to be able to represent 2^128 + 1 such borders.
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
// Skip duplicates.
.filter(|(prev_border, border)| prev_border != border)
// Finally, convert to ranges.
- .map(|(prev_border, border)| {
+ .map(move |(prev_border, border)| {
let range = match (prev_border, border) {
(JustBefore(n), JustBefore(m)) if n < m => n..=(m - 1),
(JustBefore(n), AfterMax) => n..=u128::MAX,
_ => unreachable!(), // Ruled out by the sorting and filtering we did
};
- IntRange { range }
+ IntRange { range, bias: self.range.bias }
})
}
}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
enum SliceKind {
/// Patterns of length `n` (`[x, y]`).
- FixedLen(u64),
+ FixedLen(usize),
/// Patterns using the `..` notation (`[x, .., y]`).
/// Captures any array constructor of `length >= i + j`.
/// In the case where `array_len` is `Some(_)`,
/// this indicates that we only care about the first `i` and the last `j` values of the array,
/// and everything in between is a wildcard `_`.
- VarLen(u64, u64),
+ VarLen(usize, usize),
}
impl SliceKind {
- fn arity(self) -> u64 {
+ fn arity(self) -> usize {
match self {
FixedLen(length) => length,
VarLen(prefix, suffix) => prefix + suffix,
}
/// Whether this pattern includes patterns of length `other_len`.
- fn covers_length(self, other_len: u64) -> bool {
+ fn covers_length(self, other_len: usize) -> bool {
match self {
FixedLen(len) => len == other_len,
VarLen(prefix, suffix) => prefix + suffix <= other_len,
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub(super) struct Slice {
/// `None` if the matched value is a slice, `Some(n)` if it is an array of size `n`.
- array_len: Option<u64>,
+ array_len: Option<usize>,
/// The kind of pattern it is: fixed-length `[x, y]` or variable length `[x, .., y]`.
kind: SliceKind,
}
impl Slice {
- fn new(array_len: Option<u64>, kind: SliceKind) -> Self {
+ fn new(array_len: Option<usize>, kind: SliceKind) -> Self {
let kind = match (array_len, kind) {
// If the middle `..` is empty, we effectively have a fixed-length pattern.
(Some(len), VarLen(prefix, suffix)) if prefix + suffix >= len => FixedLen(len),
Slice { array_len, kind }
}
- fn arity(self) -> u64 {
+ fn arity(self) -> usize {
self.kind.arity()
}
#[derive(Debug)]
struct SplitVarLenSlice {
/// If the type is an array, this is its size.
- array_len: Option<u64>,
+ array_len: Option<usize>,
/// The arity of the input slice.
- arity: u64,
+ arity: usize,
/// The smallest slice bigger than any slice seen. `max_slice.arity()` is the length `L`
/// described above.
max_slice: SliceKind,
}
impl SplitVarLenSlice {
- fn new(prefix: u64, suffix: u64, array_len: Option<u64>) -> Self {
+ fn new(prefix: usize, suffix: usize, array_len: Option<usize>) -> Self {
SplitVarLenSlice { array_len, arity: prefix + suffix, max_slice: VarLen(prefix, suffix) }
}
Missing { nonexhaustive_enum_missing_real_variants: bool },
/// Wildcard pattern.
Wildcard,
+ /// Or-pattern.
+ Or,
}
impl<'tcx> Constructor<'tcx> {
}
}
- /// Determines the constructor that the given pattern can be specialized to.
- pub(super) fn from_pat<'p>(cx: &MatchCheckCtxt<'p, 'tcx>, pat: &'p Pat<'tcx>) -> Self {
- match pat.kind.as_ref() {
- PatKind::AscribeUserType { .. } => bug!(), // Handled by `expand_pattern`
- PatKind::Binding { .. } | PatKind::Wild => Wildcard,
- PatKind::Leaf { .. } | PatKind::Deref { .. } => Single,
- &PatKind::Variant { variant_index, .. } => Variant(variant_index),
- PatKind::Constant { value } => {
- if let Some(int_range) = IntRange::from_const(cx.tcx, cx.param_env, value) {
- IntRange(int_range)
- } else {
- match pat.ty.kind() {
- ty::Float(_) => FloatRange(value, value, RangeEnd::Included),
- // In `expand_pattern`, we convert string literals to `&CONST` patterns with
- // `CONST` a pattern of type `str`. In truth this contains a constant of type
- // `&str`.
- ty::Str => Str(value),
- // All constants that can be structurally matched have already been expanded
- // into the corresponding `Pat`s by `const_to_pat`. Constants that remain are
- // opaque.
- _ => Opaque,
+ /// The number of fields for this constructor. This must be kept in sync with
+ /// `Fields::wildcards`.
+ pub(super) fn arity(&self, pcx: PatCtxt<'_, '_, 'tcx>) -> usize {
+ match self {
+ Single | Variant(_) => match pcx.ty.kind() {
+ ty::Tuple(fs) => fs.len(),
+ ty::Ref(..) => 1,
+ ty::Adt(adt, ..) => {
+ if adt.is_box() {
+ // The only legal patterns of type `Box` (outside `std`) are `_` and box
+ // patterns. If we're here we can assume this is a box pattern.
+ 1
+ } else {
+ let variant = &adt.variants[self.variant_index_for_adt(adt)];
+ Fields::list_variant_nonhidden_fields(pcx.cx, pcx.ty, variant).count()
}
}
- }
- &PatKind::Range(PatRange { lo, hi, end }) => {
- let ty = lo.ty;
- if let Some(int_range) = IntRange::from_range(
- cx.tcx,
- lo.eval_bits(cx.tcx, cx.param_env, lo.ty),
- hi.eval_bits(cx.tcx, cx.param_env, hi.ty),
- ty,
- &end,
- ) {
- IntRange(int_range)
- } else {
- FloatRange(lo, hi, end)
- }
- }
- PatKind::Array { prefix, slice, suffix } | PatKind::Slice { prefix, slice, suffix } => {
- let array_len = match pat.ty.kind() {
- ty::Array(_, length) => Some(length.eval_usize(cx.tcx, cx.param_env)),
- ty::Slice(_) => None,
- _ => span_bug!(pat.span, "bad ty {:?} for slice pattern", pat.ty),
- };
- let prefix = prefix.len() as u64;
- let suffix = suffix.len() as u64;
- let kind = if slice.is_some() {
- VarLen(prefix, suffix)
- } else {
- FixedLen(prefix + suffix)
- };
- Slice(Slice::new(array_len, kind))
- }
- PatKind::Or { .. } => bug!("Or-pattern should have been expanded earlier on."),
+ _ => bug!("Unexpected type for `Single` constructor: {:?}", pcx.ty),
+ },
+ Slice(slice) => slice.arity(),
+ Str(..)
+ | FloatRange(..)
+ | IntRange(..)
+ | NonExhaustive
+ | Opaque
+ | Missing { .. }
+ | Wildcard => 0,
+ Or => bug!("The `Or` constructor doesn't have a fixed arity"),
}
}
match self {
// If `self` is `Single`, `used_ctors` cannot contain anything else than `Single`s.
Single => !used_ctors.is_empty(),
- Variant(_) => used_ctors.iter().any(|c| c == self),
+ Variant(vid) => used_ctors.iter().any(|c| matches!(c, Variant(i) if i == vid)),
IntRange(range) => used_ctors
.iter()
.filter_map(|c| c.as_int_range())
.any(|other| slice.is_covered_by(other)),
// This constructor is never covered by anything else
NonExhaustive => false,
- Str(..) | FloatRange(..) | Opaque | Missing { .. } | Wildcard => {
+ Str(..) | FloatRange(..) | Opaque | Missing { .. } | Wildcard | Or => {
span_bug!(pcx.span, "found unexpected ctor in all_ctors: {:?}", self)
}
}
let all_ctors = match pcx.ty.kind() {
ty::Bool => smallvec![make_range(0, 1)],
ty::Array(sub_ty, len) if len.try_eval_usize(cx.tcx, cx.param_env).is_some() => {
- let len = len.eval_usize(cx.tcx, cx.param_env);
+ let len = len.eval_usize(cx.tcx, cx.param_env) as usize;
if len != 0 && cx.is_uninhabited(sub_ty) {
smallvec![]
} else {
}
}
-/// Some fields need to be explicitly hidden away in certain cases; see the comment above the
-/// `Fields` struct. This struct represents such a potentially-hidden field.
-#[derive(Debug, Copy, Clone)]
-pub(super) enum FilteredField<'p, 'tcx> {
- Kept(&'p Pat<'tcx>),
- Hidden,
-}
-
-impl<'p, 'tcx> FilteredField<'p, 'tcx> {
- fn kept(self) -> Option<&'p Pat<'tcx>> {
- match self {
- FilteredField::Kept(p) => Some(p),
- FilteredField::Hidden => None,
- }
- }
-}
-
/// A value can be decomposed into a constructor applied to some fields. This struct represents
/// those fields, generalized to allow patterns in each field. See also `Constructor`.
-/// This is constructed from a constructor using [`Fields::wildcards()`].
///
-/// If a private or `non_exhaustive` field is uninhabited, the code mustn't observe that it is
-/// uninhabited. For that, we filter these fields out of the matrix. This is handled automatically
-/// in `Fields`. This filtering is uncommon in practice, because uninhabited fields are rarely used,
-/// so we avoid it when possible to preserve performance.
-#[derive(Debug, Clone)]
-pub(super) enum Fields<'p, 'tcx> {
- /// Lists of patterns that don't contain any filtered fields.
- /// `Slice` and `Vec` behave the same; the difference is only to avoid allocating and
- /// triple-dereferences when possible. Frankly this is premature optimization, I (Nadrieril)
- /// have not measured if it really made a difference.
- Slice(&'p [Pat<'tcx>]),
- Vec(SmallVec<[&'p Pat<'tcx>; 2]>),
- /// Patterns where some of the fields need to be hidden. For all intents and purposes we only
- /// care about the non-hidden fields. We need to keep the real field index for those fields;
- /// we're morally storing a `Vec<(usize, &Pat)>` but what we do is more convenient.
- /// `len` counts the number of non-hidden fields
- Filtered {
- fields: SmallVec<[FilteredField<'p, 'tcx>; 2]>,
- len: usize,
- },
+/// This is constructed for a constructor using [`Fields::wildcards()`]. The idea is that
+/// [`Fields::wildcards()`] constructs a list of fields where all entries are wildcards, and then
+/// given a pattern we fill some of the fields with its subpatterns.
+/// In the following example `Fields::wildcards` returns `[_, _, _, _]`. Then in
+/// `extract_pattern_arguments` we fill some of the entries, and the result is
+/// `[Some(0), _, _, _]`.
+/// ```rust
+/// let x: [Option<u8>; 4] = foo();
+/// match x {
+/// [Some(0), ..] => {}
+/// }
+/// ```
+///
+/// Note that the number of fields of a constructor may not match the fields declared in the
+/// original struct/variant. This happens if a private or `non_exhaustive` field is uninhabited,
+/// because the code mustn't observe that it is uninhabited. In that case that field is not
+/// included in `fields`. For that reason, when you have a `mir::Field` you must use
+/// `index_with_declared_idx`.
+#[derive(Debug, Clone, Copy)]
+pub(super) struct Fields<'p, 'tcx> {
+ fields: &'p [DeconstructedPat<'p, 'tcx>],
}
impl<'p, 'tcx> Fields<'p, 'tcx> {
- /// Internal use. Use `Fields::wildcards()` instead.
- /// Must not be used if the pattern is a field of a struct/tuple/variant.
- fn from_single_pattern(pat: &'p Pat<'tcx>) -> Self {
- Fields::Slice(std::slice::from_ref(pat))
+ fn empty() -> Self {
+ Fields { fields: &[] }
+ }
+
+ fn singleton(cx: &MatchCheckCtxt<'p, 'tcx>, field: DeconstructedPat<'p, 'tcx>) -> Self {
+ let field: &_ = cx.pattern_arena.alloc(field);
+ Fields { fields: std::slice::from_ref(field) }
+ }
+
+ pub(super) fn from_iter(
+ cx: &MatchCheckCtxt<'p, 'tcx>,
+ fields: impl IntoIterator<Item = DeconstructedPat<'p, 'tcx>>,
+ ) -> Self {
+ let fields: &[_] = cx.pattern_arena.alloc_from_iter(fields);
+ Fields { fields }
}
- /// Convenience; internal use.
fn wildcards_from_tys(
cx: &MatchCheckCtxt<'p, 'tcx>,
tys: impl IntoIterator<Item = Ty<'tcx>>,
) -> Self {
- let wilds = tys.into_iter().map(Pat::wildcard_from_ty);
- let pats = cx.pattern_arena.alloc_from_iter(wilds);
- Fields::Slice(pats)
+ Fields::from_iter(cx, tys.into_iter().map(DeconstructedPat::wildcard))
}
- /// Creates a new list of wildcard fields for a given constructor.
- pub(super) fn wildcards(pcx: PatCtxt<'_, 'p, 'tcx>, constructor: &Constructor<'tcx>) -> Self {
- let ty = pcx.ty;
- let cx = pcx.cx;
- let wildcard_from_ty = |ty| &*cx.pattern_arena.alloc(Pat::wildcard_from_ty(ty));
+ // In the cases of either a `#[non_exhaustive]` field list or a non-public field, we hide
+ // uninhabited fields in order not to reveal the uninhabitedness of the whole variant.
+ // This lists the fields we keep along with their types.
+ fn list_variant_nonhidden_fields<'a>(
+ cx: &'a MatchCheckCtxt<'p, 'tcx>,
+ ty: Ty<'tcx>,
+ variant: &'a VariantDef,
+ ) -> impl Iterator<Item = (Field, Ty<'tcx>)> + Captures<'a> + Captures<'p> {
+ let (adt, substs) = match ty.kind() {
+ ty::Adt(adt, substs) => (adt, substs),
+ _ => bug!(),
+ };
+ // Whether we must not match the fields of this variant exhaustively.
+ let is_non_exhaustive = variant.is_field_list_non_exhaustive() && !adt.did.is_local();
+
+ variant.fields.iter().enumerate().filter_map(move |(i, field)| {
+ let ty = field.ty(cx.tcx, substs);
+ let is_visible = adt.is_enum() || field.vis.is_accessible_from(cx.module, cx.tcx);
+ let is_uninhabited = cx.is_uninhabited(ty);
+
+ if is_uninhabited && (!is_visible || is_non_exhaustive) {
+ None
+ } else {
+ Some((Field::new(i), ty))
+ }
+ })
+ }
+ /// Creates a new list of wildcard fields for a given constructor. The result must have a
+ /// length of `constructor.arity()`.
+ pub(super) fn wildcards(
+ cx: &MatchCheckCtxt<'p, 'tcx>,
+ ty: Ty<'tcx>,
+ constructor: &Constructor<'tcx>,
+ ) -> Self {
let ret = match constructor {
Single | Variant(_) => match ty.kind() {
- ty::Tuple(ref fs) => {
- Fields::wildcards_from_tys(cx, fs.into_iter().map(|ty| ty.expect_ty()))
- }
- ty::Ref(_, rty, _) => Fields::from_single_pattern(wildcard_from_ty(rty)),
+ ty::Tuple(fs) => Fields::wildcards_from_tys(cx, fs.iter().map(|ty| ty.expect_ty())),
+ ty::Ref(_, rty, _) => Fields::wildcards_from_tys(cx, once(*rty)),
ty::Adt(adt, substs) => {
if adt.is_box() {
- // Use T as the sub pattern type of Box<T>.
- Fields::from_single_pattern(wildcard_from_ty(substs.type_at(0)))
+ // The only legal patterns of type `Box` (outside `std`) are `_` and box
+ // patterns. If we're here we can assume this is a box pattern.
+ Fields::wildcards_from_tys(cx, once(substs.type_at(0)))
} else {
let variant = &adt.variants[constructor.variant_index_for_adt(adt)];
- // Whether we must not match the fields of this variant exhaustively.
- let is_non_exhaustive =
- variant.is_field_list_non_exhaustive() && !adt.did.is_local();
- let field_tys = variant.fields.iter().map(|field| field.ty(cx.tcx, substs));
- // In the following cases, we don't need to filter out any fields. This is
- // the vast majority of real cases, since uninhabited fields are uncommon.
- let has_no_hidden_fields = (adt.is_enum() && !is_non_exhaustive)
- || !field_tys.clone().any(|ty| cx.is_uninhabited(ty));
-
- if has_no_hidden_fields {
- Fields::wildcards_from_tys(cx, field_tys)
- } else {
- let mut len = 0;
- let fields = variant
- .fields
- .iter()
- .map(|field| {
- let ty = field.ty(cx.tcx, substs);
- let is_visible = adt.is_enum()
- || field.vis.is_accessible_from(cx.module, cx.tcx);
- let is_uninhabited = cx.is_uninhabited(ty);
-
- // In the cases of either a `#[non_exhaustive]` field list
- // or a non-public field, we hide uninhabited fields in
- // order not to reveal the uninhabitedness of the whole
- // variant.
- if is_uninhabited && (!is_visible || is_non_exhaustive) {
- FilteredField::Hidden
- } else {
- len += 1;
- FilteredField::Kept(wildcard_from_ty(ty))
- }
- })
- .collect();
- Fields::Filtered { fields, len }
- }
+ let tys = Fields::list_variant_nonhidden_fields(cx, ty, variant)
+ .map(|(_, ty)| ty);
+ Fields::wildcards_from_tys(cx, tys)
}
}
_ => bug!("Unexpected type for `Single` constructor: {:?}", ty),
| NonExhaustive
| Opaque
| Missing { .. }
- | Wildcard => Fields::Slice(&[]),
+ | Wildcard => Fields::empty(),
+ Or => {
+ bug!("called `Fields::wildcards` on an `Or` ctor")
+ }
};
debug!("Fields::wildcards({:?}, {:?}) = {:#?}", constructor, ty, ret);
ret
}
- /// Apply a constructor to a list of patterns, yielding a new pattern. `self`
- /// must have as many elements as this constructor's arity.
- ///
- /// This is roughly the inverse of `specialize_constructor`.
- ///
- /// Examples:
- ///
- /// ```text
- /// ctor: `Constructor::Single`
- /// ty: `Foo(u32, u32, u32)`
- /// self: `[10, 20, _]`
- /// returns `Foo(10, 20, _)`
- ///
- /// ctor: `Constructor::Variant(Option::Some)`
- /// ty: `Option<bool>`
- /// self: `[false]`
- /// returns `Some(false)`
- /// ```
- pub(super) fn apply(self, pcx: PatCtxt<'_, 'p, 'tcx>, ctor: &Constructor<'tcx>) -> Pat<'tcx> {
- let subpatterns_and_indices = self.patterns_and_indices();
- let mut subpatterns = subpatterns_and_indices.iter().map(|&(_, p)| p).cloned();
-
- let pat = match ctor {
- Single | Variant(_) => match pcx.ty.kind() {
- ty::Adt(..) | ty::Tuple(..) => {
- // We want the real indices here.
- let subpatterns = subpatterns_and_indices
- .iter()
- .map(|&(field, p)| FieldPat { field, pattern: p.clone() })
- .collect();
+ /// Returns the list of patterns.
+ pub(super) fn iter_patterns<'a>(
+ &'a self,
+ ) -> impl Iterator<Item = &'p DeconstructedPat<'p, 'tcx>> + Captures<'a> {
+ self.fields.iter()
+ }
+}
- if let ty::Adt(adt, substs) = pcx.ty.kind() {
- if adt.is_enum() {
- PatKind::Variant {
- adt_def: adt,
- substs,
- variant_index: ctor.variant_index_for_adt(adt),
- subpatterns,
- }
+/// Values and patterns can be represented as a constructor applied to some fields. This represents
+/// a pattern in this form.
+/// This also keeps track of whether the pattern has been foundreachable during analysis. For this
+/// reason we should be careful not to clone patterns for which we care about that. Use
+/// `clone_and_forget_reachability` is you're sure.
+pub(crate) struct DeconstructedPat<'p, 'tcx> {
+ ctor: Constructor<'tcx>,
+ fields: Fields<'p, 'tcx>,
+ ty: Ty<'tcx>,
+ span: Span,
+ reachable: Cell<bool>,
+}
+
+impl<'p, 'tcx> DeconstructedPat<'p, 'tcx> {
+ pub(super) fn wildcard(ty: Ty<'tcx>) -> Self {
+ Self::new(Wildcard, Fields::empty(), ty, DUMMY_SP)
+ }
+
+ pub(super) fn new(
+ ctor: Constructor<'tcx>,
+ fields: Fields<'p, 'tcx>,
+ ty: Ty<'tcx>,
+ span: Span,
+ ) -> Self {
+ DeconstructedPat { ctor, fields, ty, span, reachable: Cell::new(false) }
+ }
+
+ /// Construct a pattern that matches everything that starts with this constructor.
+ /// For example, if `ctor` is a `Constructor::Variant` for `Option::Some`, we get the pattern
+ /// `Some(_)`.
+ pub(super) fn wild_from_ctor(pcx: PatCtxt<'_, 'p, 'tcx>, ctor: Constructor<'tcx>) -> Self {
+ let fields = Fields::wildcards(pcx.cx, pcx.ty, &ctor);
+ DeconstructedPat::new(ctor, fields, pcx.ty, DUMMY_SP)
+ }
+
+ /// Clone this value. This method emphasizes that cloning loses reachability information and
+ /// should be done carefully.
+ pub(super) fn clone_and_forget_reachability(&self) -> Self {
+ DeconstructedPat::new(self.ctor.clone(), self.fields, self.ty, self.span)
+ }
+
+ pub(crate) fn from_pat(cx: &MatchCheckCtxt<'p, 'tcx>, pat: &Pat<'tcx>) -> Self {
+ let mkpat = |pat| DeconstructedPat::from_pat(cx, pat);
+ let ctor;
+ let fields;
+ match pat.kind.as_ref() {
+ PatKind::AscribeUserType { subpattern, .. } => return mkpat(subpattern),
+ PatKind::Binding { subpattern: Some(subpat), .. } => return mkpat(subpat),
+ PatKind::Binding { subpattern: None, .. } | PatKind::Wild => {
+ ctor = Wildcard;
+ fields = Fields::empty();
+ }
+ PatKind::Deref { subpattern } => {
+ ctor = Single;
+ fields = Fields::singleton(cx, mkpat(subpattern));
+ }
+ PatKind::Leaf { subpatterns } | PatKind::Variant { subpatterns, .. } => {
+ match pat.ty.kind() {
+ ty::Tuple(fs) => {
+ ctor = Single;
+ let mut wilds: SmallVec<[_; 2]> = fs
+ .iter()
+ .map(|ty| ty.expect_ty())
+ .map(DeconstructedPat::wildcard)
+ .collect();
+ for pat in subpatterns {
+ wilds[pat.field.index()] = mkpat(&pat.pattern);
+ }
+ fields = Fields::from_iter(cx, wilds);
+ }
+ ty::Adt(adt, substs) if adt.is_box() => {
+ // The only legal patterns of type `Box` (outside `std`) are `_` and box
+ // patterns. If we're here we can assume this is a box pattern.
+ // FIXME(Nadrieril): A `Box` can in theory be matched either with `Box(_,
+ // _)` or a box pattern. As a hack to avoid an ICE with the former, we
+ // ignore other fields than the first one. This will trigger an error later
+ // anyway.
+ // See https://github.com/rust-lang/rust/issues/82772 ,
+ // explanation: https://github.com/rust-lang/rust/pull/82789#issuecomment-796921977
+ // The problem is that we can't know from the type whether we'll match
+ // normally or through box-patterns. We'll have to figure out a proper
+ // solution when we introduce generalized deref patterns. Also need to
+ // prevent mixing of those two options.
+ let pat = subpatterns.into_iter().find(|pat| pat.field.index() == 0);
+ let pat = if let Some(pat) = pat {
+ mkpat(&pat.pattern)
} else {
- PatKind::Leaf { subpatterns }
+ DeconstructedPat::wildcard(substs.type_at(0))
+ };
+ ctor = Single;
+ fields = Fields::singleton(cx, pat);
+ }
+ ty::Adt(adt, _) => {
+ ctor = match pat.kind.as_ref() {
+ PatKind::Leaf { .. } => Single,
+ PatKind::Variant { variant_index, .. } => Variant(*variant_index),
+ _ => bug!(),
+ };
+ let variant = &adt.variants[ctor.variant_index_for_adt(adt)];
+ // For each field in the variant, we store the relevant index into `self.fields` if any.
+ let mut field_id_to_id: Vec<Option<usize>> =
+ (0..variant.fields.len()).map(|_| None).collect();
+ let tys = Fields::list_variant_nonhidden_fields(cx, pat.ty, variant)
+ .enumerate()
+ .map(|(i, (field, ty))| {
+ field_id_to_id[field.index()] = Some(i);
+ ty
+ });
+ let mut wilds: SmallVec<[_; 2]> =
+ tys.map(DeconstructedPat::wildcard).collect();
+ for pat in subpatterns {
+ if let Some(i) = field_id_to_id[pat.field.index()] {
+ wilds[i] = mkpat(&pat.pattern);
+ }
+ }
+ fields = Fields::from_iter(cx, wilds);
+ }
+ _ => bug!("pattern has unexpected type: pat: {:?}, ty: {:?}", pat, pat.ty),
+ }
+ }
+ PatKind::Constant { value } => {
+ if let Some(int_range) = IntRange::from_const(cx.tcx, cx.param_env, value) {
+ ctor = IntRange(int_range);
+ fields = Fields::empty();
+ } else {
+ match pat.ty.kind() {
+ ty::Float(_) => {
+ ctor = FloatRange(value, value, RangeEnd::Included);
+ fields = Fields::empty();
+ }
+ ty::Ref(_, t, _) if t.is_str() => {
+ // We want a `&str` constant to behave like a `Deref` pattern, to be compatible
+ // with other `Deref` patterns. This could have been done in `const_to_pat`,
+ // but that causes issues with the rest of the matching code.
+ // So here, the constructor for a `"foo"` pattern is `&` (represented by
+ // `Single`), and has one field. That field has constructor `Str(value)` and no
+ // fields.
+ // Note: `t` is `str`, not `&str`.
+ let subpattern =
+ DeconstructedPat::new(Str(value), Fields::empty(), t, pat.span);
+ ctor = Single;
+ fields = Fields::singleton(cx, subpattern)
+ }
+ // All constants that can be structurally matched have already been expanded
+ // into the corresponding `Pat`s by `const_to_pat`. Constants that remain are
+ // opaque.
+ _ => {
+ ctor = Opaque;
+ fields = Fields::empty();
}
+ }
+ }
+ }
+ &PatKind::Range(PatRange { lo, hi, end }) => {
+ let ty = lo.ty;
+ ctor = if let Some(int_range) = IntRange::from_range(
+ cx.tcx,
+ lo.eval_bits(cx.tcx, cx.param_env, lo.ty),
+ hi.eval_bits(cx.tcx, cx.param_env, hi.ty),
+ ty,
+ &end,
+ ) {
+ IntRange(int_range)
+ } else {
+ FloatRange(lo, hi, end)
+ };
+ fields = Fields::empty();
+ }
+ PatKind::Array { prefix, slice, suffix } | PatKind::Slice { prefix, slice, suffix } => {
+ let array_len = match pat.ty.kind() {
+ ty::Array(_, length) => Some(length.eval_usize(cx.tcx, cx.param_env) as usize),
+ ty::Slice(_) => None,
+ _ => span_bug!(pat.span, "bad ty {:?} for slice pattern", pat.ty),
+ };
+ let kind = if slice.is_some() {
+ VarLen(prefix.len(), suffix.len())
+ } else {
+ FixedLen(prefix.len() + suffix.len())
+ };
+ ctor = Slice(Slice::new(array_len, kind));
+ fields = Fields::from_iter(cx, prefix.iter().chain(suffix).map(mkpat));
+ }
+ PatKind::Or { .. } => {
+ ctor = Or;
+ let pats = expand_or_pat(pat);
+ fields = Fields::from_iter(cx, pats.into_iter().map(mkpat));
+ }
+ }
+ DeconstructedPat::new(ctor, fields, pat.ty, pat.span)
+ }
+
+ pub(crate) fn to_pat(&self, cx: &MatchCheckCtxt<'p, 'tcx>) -> Pat<'tcx> {
+ let is_wildcard = |pat: &Pat<'_>| {
+ matches!(*pat.kind, PatKind::Binding { subpattern: None, .. } | PatKind::Wild)
+ };
+ let mut subpatterns = self.iter_fields().map(|p| p.to_pat(cx));
+ let pat = match &self.ctor {
+ Single | Variant(_) => match self.ty.kind() {
+ ty::Tuple(..) => PatKind::Leaf {
+ subpatterns: subpatterns
+ .enumerate()
+ .map(|(i, p)| FieldPat { field: Field::new(i), pattern: p })
+ .collect(),
+ },
+ ty::Adt(adt_def, _) if adt_def.is_box() => {
+ // Without `box_patterns`, the only legal pattern of type `Box` is `_` (outside
+ // of `std`). So this branch is only reachable when the feature is enabled and
+ // the pattern is a box pattern.
+ PatKind::Deref { subpattern: subpatterns.next().unwrap() }
+ }
+ ty::Adt(adt_def, substs) => {
+ let variant_index = self.ctor.variant_index_for_adt(adt_def);
+ let variant = &adt_def.variants[variant_index];
+ let subpatterns = Fields::list_variant_nonhidden_fields(cx, self.ty, variant)
+ .zip(subpatterns)
+ .map(|((field, _ty), pattern)| FieldPat { field, pattern })
+ .collect();
+
+ if adt_def.is_enum() {
+ PatKind::Variant { adt_def, substs, variant_index, subpatterns }
} else {
PatKind::Leaf { subpatterns }
}
// Note: given the expansion of `&str` patterns done in `expand_pattern`, we should
// be careful to reconstruct the correct constant pattern here. However a string
// literal pattern will never be reported as a non-exhaustiveness witness, so we
- // can ignore this issue.
+ // ignore this issue.
ty::Ref(..) => PatKind::Deref { subpattern: subpatterns.next().unwrap() },
- ty::Slice(_) | ty::Array(..) => bug!("bad slice pattern {:?} {:?}", ctor, pcx.ty),
- _ => PatKind::Wild,
+ _ => bug!("unexpected ctor for type {:?} {:?}", self.ctor, self.ty),
},
- Slice(slice) => match slice.kind {
- FixedLen(_) => {
- PatKind::Slice { prefix: subpatterns.collect(), slice: None, suffix: vec![] }
- }
- VarLen(prefix, _) => {
- let mut prefix: Vec<_> = subpatterns.by_ref().take(prefix as usize).collect();
- if slice.array_len.is_some() {
- // Improves diagnostics a bit: if the type is a known-size array, instead
- // of reporting `[x, _, .., _, y]`, we prefer to report `[x, .., y]`.
- // This is incorrect if the size is not known, since `[_, ..]` captures
- // arrays of lengths `>= 1` whereas `[..]` captures any length.
- while !prefix.is_empty() && is_wildcard(prefix.last().unwrap()) {
- prefix.pop();
+ Slice(slice) => {
+ match slice.kind {
+ FixedLen(_) => PatKind::Slice {
+ prefix: subpatterns.collect(),
+ slice: None,
+ suffix: vec![],
+ },
+ VarLen(prefix, _) => {
+ let mut subpatterns = subpatterns.peekable();
+ let mut prefix: Vec<_> = subpatterns.by_ref().take(prefix).collect();
+ if slice.array_len.is_some() {
+ // Improves diagnostics a bit: if the type is a known-size array, instead
+ // of reporting `[x, _, .., _, y]`, we prefer to report `[x, .., y]`.
+ // This is incorrect if the size is not known, since `[_, ..]` captures
+ // arrays of lengths `>= 1` whereas `[..]` captures any length.
+ while !prefix.is_empty() && is_wildcard(prefix.last().unwrap()) {
+ prefix.pop();
+ }
+ while subpatterns.peek().is_some()
+ && is_wildcard(subpatterns.peek().unwrap())
+ {
+ subpatterns.next();
+ }
}
+ let suffix: Vec<_> = subpatterns.collect();
+ let wild = Pat::wildcard_from_ty(self.ty);
+ PatKind::Slice { prefix, slice: Some(wild), suffix }
}
- let suffix: Vec<_> = if slice.array_len.is_some() {
- // Same as above.
- subpatterns.skip_while(is_wildcard).collect()
- } else {
- subpatterns.collect()
- };
- let wild = Pat::wildcard_from_ty(pcx.ty);
- PatKind::Slice { prefix, slice: Some(wild), suffix }
}
- },
+ }
&Str(value) => PatKind::Constant { value },
&FloatRange(lo, hi, end) => PatKind::Range(PatRange { lo, hi, end }),
- IntRange(range) => return range.to_pat(pcx.cx.tcx, pcx.ty),
- NonExhaustive => PatKind::Wild,
- Wildcard => return Pat::wildcard_from_ty(pcx.ty),
- Opaque => bug!("we should not try to apply an opaque constructor"),
+ IntRange(range) => return range.to_pat(cx.tcx, self.ty),
+ Wildcard | NonExhaustive => PatKind::Wild,
Missing { .. } => bug!(
- "trying to apply the `Missing` constructor; this should have been done in `apply_constructors`"
+ "trying to convert a `Missing` constructor into a `Pat`; this is probably a bug,
+ `Missing` should have been processed in `apply_constructors`"
),
+ Opaque | Or => {
+ bug!("can't convert to pattern: {:?}", self)
+ }
};
- Pat { ty: pcx.ty, span: DUMMY_SP, kind: Box::new(pat) }
+ Pat { ty: self.ty, span: DUMMY_SP, kind: Box::new(pat) }
}
- /// Returns the number of patterns. This is the same as the arity of the constructor used to
- /// construct `self`.
- pub(super) fn len(&self) -> usize {
- match self {
- Fields::Slice(pats) => pats.len(),
- Fields::Vec(pats) => pats.len(),
- Fields::Filtered { len, .. } => *len,
- }
+ pub(super) fn is_or_pat(&self) -> bool {
+ matches!(self.ctor, Or)
}
- /// Returns the list of patterns along with the corresponding field indices.
- fn patterns_and_indices(&self) -> SmallVec<[(Field, &'p Pat<'tcx>); 2]> {
- match self {
- Fields::Slice(pats) => {
- pats.iter().enumerate().map(|(i, p)| (Field::new(i), p)).collect()
- }
- Fields::Vec(pats) => {
- pats.iter().copied().enumerate().map(|(i, p)| (Field::new(i), p)).collect()
- }
- Fields::Filtered { fields, .. } => {
- // Indices must be relative to the full list of patterns
- fields
- .iter()
- .enumerate()
- .filter_map(|(i, p)| Some((Field::new(i), p.kept()?)))
- .collect()
- }
- }
+ pub(super) fn ctor(&self) -> &Constructor<'tcx> {
+ &self.ctor
}
-
- /// Returns the list of patterns.
- pub(super) fn into_patterns(self) -> SmallVec<[&'p Pat<'tcx>; 2]> {
- match self {
- Fields::Slice(pats) => pats.iter().collect(),
- Fields::Vec(pats) => pats,
- Fields::Filtered { fields, .. } => fields.iter().filter_map(|p| p.kept()).collect(),
- }
+ pub(super) fn ty(&self) -> Ty<'tcx> {
+ self.ty
}
-
- /// Overrides some of the fields with the provided patterns. Exactly like
- /// `replace_fields_indexed`, except that it takes `FieldPat`s as input.
- fn replace_with_fieldpats(
- &self,
- new_pats: impl IntoIterator<Item = &'p FieldPat<'tcx>>,
- ) -> Self {
- self.replace_fields_indexed(
- new_pats.into_iter().map(|pat| (pat.field.index(), &pat.pattern)),
- )
+ pub(super) fn span(&self) -> Span {
+ self.span
}
- /// Overrides some of the fields with the provided patterns. This is used when a pattern
- /// defines some fields but not all, for example `Foo { field1: Some(_), .. }`: here we start
- /// with a `Fields` that is just one wildcard per field of the `Foo` struct, and override the
- /// entry corresponding to `field1` with the pattern `Some(_)`. This is also used for slice
- /// patterns for the same reason.
- fn replace_fields_indexed(
- &self,
- new_pats: impl IntoIterator<Item = (usize, &'p Pat<'tcx>)>,
- ) -> Self {
- let mut fields = self.clone();
- if let Fields::Slice(pats) = fields {
- fields = Fields::Vec(pats.iter().collect());
- }
+ pub(super) fn iter_fields<'a>(
+ &'a self,
+ ) -> impl Iterator<Item = &'p DeconstructedPat<'p, 'tcx>> + Captures<'a> {
+ self.fields.iter_patterns()
+ }
- match &mut fields {
- Fields::Vec(pats) => {
- for (i, pat) in new_pats {
- if let Some(p) = pats.get_mut(i) {
- *p = pat;
- }
- }
+ /// Specialize this pattern with a constructor.
+ /// `other_ctor` can be different from `self.ctor`, but must be covered by it.
+ pub(super) fn specialize<'a>(
+ &'a self,
+ cx: &MatchCheckCtxt<'p, 'tcx>,
+ other_ctor: &Constructor<'tcx>,
+ ) -> SmallVec<[&'p DeconstructedPat<'p, 'tcx>; 2]> {
+ match (&self.ctor, other_ctor) {
+ (Wildcard, _) => {
+ // We return a wildcard for each field of `other_ctor`.
+ Fields::wildcards(cx, self.ty, other_ctor).iter_patterns().collect()
}
- Fields::Filtered { fields, .. } => {
- for (i, pat) in new_pats {
- if let FilteredField::Kept(p) = &mut fields[i] {
- *p = pat
+ (Slice(self_slice), Slice(other_slice))
+ if self_slice.arity() != other_slice.arity() =>
+ {
+ // The only tricky case: two slices of different arity. Since `self_slice` covers
+ // `other_slice`, `self_slice` must be `VarLen`, i.e. of the form
+ // `[prefix, .., suffix]`. Moreover `other_slice` is guaranteed to have a larger
+ // arity. So we fill the middle part with enough wildcards to reach the length of
+ // the new, larger slice.
+ match self_slice.kind {
+ FixedLen(_) => bug!("{:?} doesn't cover {:?}", self_slice, other_slice),
+ VarLen(prefix, suffix) => {
+ let inner_ty = match *self.ty.kind() {
+ ty::Slice(ty) | ty::Array(ty, _) => ty,
+ _ => bug!("bad slice pattern {:?} {:?}", self.ctor, self.ty),
+ };
+ let prefix = &self.fields.fields[..prefix];
+ let suffix = &self.fields.fields[self_slice.arity() - suffix..];
+ let wildcard: &_ =
+ cx.pattern_arena.alloc(DeconstructedPat::wildcard(inner_ty));
+ let extra_wildcards = other_slice.arity() - self_slice.arity();
+ let extra_wildcards = (0..extra_wildcards).map(|_| wildcard);
+ prefix.iter().chain(extra_wildcards).chain(suffix).collect()
}
}
}
- Fields::Slice(_) => unreachable!(),
+ _ => self.fields.iter_patterns().collect(),
}
- fields
}
- /// Replaces contained fields with the given list of patterns. There must be `len()` patterns
- /// in `pats`.
- pub(super) fn replace_fields(
- &self,
- cx: &MatchCheckCtxt<'p, 'tcx>,
- pats: impl IntoIterator<Item = Pat<'tcx>>,
- ) -> Self {
- let pats: &[_] = cx.pattern_arena.alloc_from_iter(pats);
+ /// We keep track for each pattern if it was ever reachable during the analysis. This is used
+ /// with `unreachable_spans` to report unreachable subpatterns arising from or patterns.
+ pub(super) fn set_reachable(&self) {
+ self.reachable.set(true)
+ }
+ pub(super) fn is_reachable(&self) -> bool {
+ self.reachable.get()
+ }
- match self {
- Fields::Filtered { fields, len } => {
- let mut pats = pats.iter();
- let mut fields = fields.clone();
- for f in &mut fields {
- if let FilteredField::Kept(p) = f {
- // We take one input pattern for each `Kept` field, in order.
- *p = pats.next().unwrap();
- }
- }
- Fields::Filtered { fields, len: *len }
+ /// Report the spans of subpatterns that were not reachable, if any.
+ pub(super) fn unreachable_spans(&self) -> Vec<Span> {
+ let mut spans = Vec::new();
+ self.collect_unreachable_spans(&mut spans);
+ spans
+ }
+
+ fn collect_unreachable_spans(&self, spans: &mut Vec<Span>) {
+ // We don't look at subpatterns if we already reported the whole pattern as unreachable.
+ if !self.is_reachable() {
+ spans.push(self.span);
+ } else {
+ for p in self.iter_fields() {
+ p.collect_unreachable_spans(spans);
}
- _ => Fields::Slice(pats),
}
}
+}
- /// Replaces contained fields with the arguments of the given pattern. Only use on a pattern
- /// that is compatible with the constructor used to build `self`.
- /// This is meant to be used on the result of `Fields::wildcards()`. The idea is that
- /// `wildcards` constructs a list of fields where all entries are wildcards, and the pattern
- /// provided to this function fills some of the fields with non-wildcards.
- /// In the following example `Fields::wildcards` would return `[_, _, _, _]`. If we call
- /// `replace_with_pattern_arguments` on it with the pattern, the result will be `[Some(0), _,
- /// _, _]`.
- /// ```rust
- /// let x: [Option<u8>; 4] = foo();
- /// match x {
- /// [Some(0), ..] => {}
- /// }
- /// ```
- /// This is guaranteed to preserve the number of patterns in `self`.
- pub(super) fn replace_with_pattern_arguments(&self, pat: &'p Pat<'tcx>) -> Self {
- match pat.kind.as_ref() {
- PatKind::Deref { subpattern } => {
- assert_eq!(self.len(), 1);
- Fields::from_single_pattern(subpattern)
+/// This is mostly copied from the `Pat` impl. This is best effort and not good enough for a
+/// `Display` impl.
+impl<'p, 'tcx> fmt::Debug for DeconstructedPat<'p, 'tcx> {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ // Printing lists is a chore.
+ let mut first = true;
+ let mut start_or_continue = |s| {
+ if first {
+ first = false;
+ ""
+ } else {
+ s
}
- PatKind::Leaf { subpatterns } | PatKind::Variant { subpatterns, .. } => {
- self.replace_with_fieldpats(subpatterns)
+ };
+ let mut start_or_comma = || start_or_continue(", ");
+
+ match &self.ctor {
+ Single | Variant(_) => match self.ty.kind() {
+ ty::Adt(def, _) if def.is_box() => {
+ // Without `box_patterns`, the only legal pattern of type `Box` is `_` (outside
+ // of `std`). So this branch is only reachable when the feature is enabled and
+ // the pattern is a box pattern.
+ let subpattern = self.iter_fields().next().unwrap();
+ write!(f, "box {:?}", subpattern)
+ }
+ ty::Adt(..) | ty::Tuple(..) => {
+ let variant = match self.ty.kind() {
+ ty::Adt(adt, _) => {
+ Some(&adt.variants[self.ctor.variant_index_for_adt(adt)])
+ }
+ ty::Tuple(_) => None,
+ _ => unreachable!(),
+ };
+
+ if let Some(variant) = variant {
+ write!(f, "{}", variant.ident)?;
+ }
+
+ // Without `cx`, we can't know which field corresponds to which, so we can't
+ // get the names of the fields. Instead we just display everything as a suple
+ // struct, which should be good enough.
+ write!(f, "(")?;
+ for p in self.iter_fields() {
+ write!(f, "{}", start_or_comma())?;
+ write!(f, "{:?}", p)?;
+ }
+ write!(f, ")")
+ }
+ // Note: given the expansion of `&str` patterns done in `expand_pattern`, we should
+ // be careful to detect strings here. However a string literal pattern will never
+ // be reported as a non-exhaustiveness witness, so we can ignore this issue.
+ ty::Ref(_, _, mutbl) => {
+ let subpattern = self.iter_fields().next().unwrap();
+ write!(f, "&{}{:?}", mutbl.prefix_str(), subpattern)
+ }
+ _ => write!(f, "_"),
+ },
+ Slice(slice) => {
+ let mut subpatterns = self.fields.iter_patterns();
+ write!(f, "[")?;
+ match slice.kind {
+ FixedLen(_) => {
+ for p in subpatterns {
+ write!(f, "{}{:?}", start_or_comma(), p)?;
+ }
+ }
+ VarLen(prefix_len, _) => {
+ for p in subpatterns.by_ref().take(prefix_len) {
+ write!(f, "{}{:?}", start_or_comma(), p)?;
+ }
+ write!(f, "{}", start_or_comma())?;
+ write!(f, "..")?;
+ for p in subpatterns {
+ write!(f, "{}{:?}", start_or_comma(), p)?;
+ }
+ }
+ }
+ write!(f, "]")
+ }
+ &FloatRange(lo, hi, end) => {
+ write!(f, "{}", lo)?;
+ write!(f, "{}", end)?;
+ write!(f, "{}", hi)
}
- PatKind::Array { prefix, suffix, .. } | PatKind::Slice { prefix, suffix, .. } => {
- // Number of subpatterns for the constructor
- let ctor_arity = self.len();
-
- // Replace the prefix and the suffix with the given patterns, leaving wildcards in
- // the middle if there was a subslice pattern `..`.
- let prefix = prefix.iter().enumerate();
- let suffix =
- suffix.iter().enumerate().map(|(i, p)| (ctor_arity - suffix.len() + i, p));
- self.replace_fields_indexed(prefix.chain(suffix))
+ IntRange(range) => write!(f, "{:?}", range), // Best-effort, will render e.g. `false` as `0..=0`
+ Wildcard | Missing { .. } | NonExhaustive => write!(f, "_"),
+ Or => {
+ for pat in self.iter_fields() {
+ write!(f, "{}{:?}", start_or_continue(" | "), pat)?;
+ }
+ Ok(())
}
- _ => self.clone(),
+ Str(value) => write!(f, "{}", value),
+ Opaque => write!(f, "<constant pattern>"),
}
}
}
use self::Usefulness::*;
use super::check_match::{joined_uncovered_patterns, pattern_not_covered_label};
-use super::deconstruct_pat::{Constructor, Fields, SplitWildcard};
-use super::{PatternFoldable, PatternFolder};
+use super::deconstruct_pat::{Constructor, DeconstructedPat, Fields, SplitWildcard};
use rustc_data_structures::captures::Captures;
-use rustc_data_structures::fx::FxHashMap;
-use hir::def_id::DefId;
-use hir::HirId;
use rustc_arena::TypedArena;
-use rustc_hir as hir;
-use rustc_middle::thir::{Pat, PatKind};
+use rustc_hir::def_id::DefId;
+use rustc_hir::HirId;
use rustc_middle::ty::{self, Ty, TyCtxt};
use rustc_session::lint::builtin::NON_EXHAUSTIVE_OMITTED_PATTERNS;
-use rustc_span::Span;
+use rustc_span::{Span, DUMMY_SP};
use smallvec::{smallvec, SmallVec};
use std::fmt;
-use std::iter::{FromIterator, IntoIterator};
-use std::lazy::OnceCell;
+use std::iter::once;
-crate struct MatchCheckCtxt<'a, 'tcx> {
+crate struct MatchCheckCtxt<'p, 'tcx> {
crate tcx: TyCtxt<'tcx>,
/// The module in which the match occurs. This is necessary for
/// checking inhabited-ness of types because whether a type is (visibly)
/// outside its module and should not be matchable with an empty match statement.
crate module: DefId,
crate param_env: ty::ParamEnv<'tcx>,
- crate pattern_arena: &'a TypedArena<Pat<'tcx>>,
+ crate pattern_arena: &'p TypedArena<DeconstructedPat<'p, 'tcx>>,
}
impl<'a, 'tcx> MatchCheckCtxt<'a, 'tcx> {
}
}
-crate fn expand_pattern<'tcx>(pat: Pat<'tcx>) -> Pat<'tcx> {
- LiteralExpander.fold_pattern(&pat)
-}
-
-struct LiteralExpander;
-
-impl<'tcx> PatternFolder<'tcx> for LiteralExpander {
- fn fold_pattern(&mut self, pat: &Pat<'tcx>) -> Pat<'tcx> {
- debug!("fold_pattern {:?} {:?} {:?}", pat, pat.ty.kind(), pat.kind);
- match (pat.ty.kind(), pat.kind.as_ref()) {
- (_, PatKind::Binding { subpattern: Some(s), .. }) => s.fold_with(self),
- (_, PatKind::AscribeUserType { subpattern: s, .. }) => s.fold_with(self),
- (ty::Ref(_, t, _), PatKind::Constant { .. }) if t.is_str() => {
- // Treat string literal patterns as deref patterns to a `str` constant, i.e.
- // `&CONST`. This expands them like other const patterns. This could have been done
- // in `const_to_pat`, but that causes issues with the rest of the matching code.
- let mut new_pat = pat.super_fold_with(self);
- // Make a fake const pattern of type `str` (instead of `&str`). That the carried
- // constant value still knows it is of type `&str`.
- new_pat.ty = t;
- Pat {
- kind: Box::new(PatKind::Deref { subpattern: new_pat }),
- span: pat.span,
- ty: pat.ty,
- }
- }
- _ => pat.super_fold_with(self),
- }
- }
-}
-
-pub(super) fn is_wildcard(pat: &Pat<'_>) -> bool {
- matches!(*pat.kind, PatKind::Binding { subpattern: None, .. } | PatKind::Wild)
-}
-
-fn is_or_pat(pat: &Pat<'_>) -> bool {
- matches!(*pat.kind, PatKind::Or { .. })
-}
-
-/// Recursively expand this pattern into its subpatterns. Only useful for or-patterns.
-fn expand_or_pat<'p, 'tcx>(pat: &'p Pat<'tcx>) -> Vec<&'p Pat<'tcx>> {
- fn expand<'p, 'tcx>(pat: &'p Pat<'tcx>, vec: &mut Vec<&'p Pat<'tcx>>) {
- if let PatKind::Or { pats } = pat.kind.as_ref() {
- for pat in pats {
- expand(pat, vec);
- }
- } else {
- vec.push(pat)
- }
- }
-
- let mut pats = Vec::new();
- expand(pat, &mut pats);
- pats
-}
-
/// A row of a matrix. Rows of len 1 are very common, which is why `SmallVec[_; 2]`
/// works well.
#[derive(Clone)]
struct PatStack<'p, 'tcx> {
- pats: SmallVec<[&'p Pat<'tcx>; 2]>,
- /// Cache for the constructor of the head
- head_ctor: OnceCell<Constructor<'tcx>>,
+ pats: SmallVec<[&'p DeconstructedPat<'p, 'tcx>; 2]>,
}
impl<'p, 'tcx> PatStack<'p, 'tcx> {
- fn from_pattern(pat: &'p Pat<'tcx>) -> Self {
+ fn from_pattern(pat: &'p DeconstructedPat<'p, 'tcx>) -> Self {
Self::from_vec(smallvec![pat])
}
- fn from_vec(vec: SmallVec<[&'p Pat<'tcx>; 2]>) -> Self {
- PatStack { pats: vec, head_ctor: OnceCell::new() }
+ fn from_vec(vec: SmallVec<[&'p DeconstructedPat<'p, 'tcx>; 2]>) -> Self {
+ PatStack { pats: vec }
}
fn is_empty(&self) -> bool {
self.pats.len()
}
- fn head(&self) -> &'p Pat<'tcx> {
+ fn head(&self) -> &'p DeconstructedPat<'p, 'tcx> {
self.pats[0]
}
- #[inline]
- fn head_ctor<'a>(&'a self, cx: &MatchCheckCtxt<'p, 'tcx>) -> &'a Constructor<'tcx> {
- self.head_ctor.get_or_init(|| Constructor::from_pat(cx, self.head()))
- }
-
- fn iter(&self) -> impl Iterator<Item = &Pat<'tcx>> {
+ fn iter(&self) -> impl Iterator<Item = &DeconstructedPat<'p, 'tcx>> {
self.pats.iter().copied()
}
// Recursively expand the first pattern into its subpatterns. Only useful if the pattern is an
// or-pattern. Panics if `self` is empty.
fn expand_or_pat<'a>(&'a self) -> impl Iterator<Item = PatStack<'p, 'tcx>> + Captures<'a> {
- expand_or_pat(self.head()).into_iter().map(move |pat| {
+ self.head().iter_fields().map(move |pat| {
let mut new_patstack = PatStack::from_pattern(pat);
new_patstack.pats.extend_from_slice(&self.pats[1..]);
new_patstack
})
}
- /// This computes `S(self.head_ctor(), self)`. See top of the file for explanations.
+ /// This computes `S(self.head().ctor(), self)`. See top of the file for explanations.
///
/// Structure patterns with a partial wild pattern (Foo { a: 42, .. }) have their missing
/// fields filled with wild patterns.
///
/// This is roughly the inverse of `Constructor::apply`.
- fn pop_head_constructor(&self, ctor_wild_subpatterns: &Fields<'p, 'tcx>) -> PatStack<'p, 'tcx> {
+ fn pop_head_constructor(
+ &self,
+ cx: &MatchCheckCtxt<'p, 'tcx>,
+ ctor: &Constructor<'tcx>,
+ ) -> PatStack<'p, 'tcx> {
// We pop the head pattern and push the new fields extracted from the arguments of
// `self.head()`.
- let mut new_fields =
- ctor_wild_subpatterns.replace_with_pattern_arguments(self.head()).into_patterns();
+ let mut new_fields: SmallVec<[_; 2]> = self.head().specialize(cx, ctor);
new_fields.extend_from_slice(&self.pats[1..]);
PatStack::from_vec(new_fields)
}
}
-impl<'p, 'tcx> Default for PatStack<'p, 'tcx> {
- fn default() -> Self {
- Self::from_vec(smallvec![])
- }
-}
-
-impl<'p, 'tcx> PartialEq for PatStack<'p, 'tcx> {
- fn eq(&self, other: &Self) -> bool {
- self.pats == other.pats
- }
-}
-
-impl<'p, 'tcx> FromIterator<&'p Pat<'tcx>> for PatStack<'p, 'tcx> {
- fn from_iter<T>(iter: T) -> Self
- where
- T: IntoIterator<Item = &'p Pat<'tcx>>,
- {
- Self::from_vec(iter.into_iter().collect())
- }
-}
-
/// Pretty-printing for matrix row.
impl<'p, 'tcx> fmt::Debug for PatStack<'p, 'tcx> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "+")?;
for pat in self.iter() {
- write!(f, " {} +", pat)?;
+ write!(f, " {:?} +", pat)?;
}
Ok(())
}
}
/// A 2D matrix.
-#[derive(Clone, PartialEq)]
+#[derive(Clone)]
pub(super) struct Matrix<'p, 'tcx> {
patterns: Vec<PatStack<'p, 'tcx>>,
}
/// Pushes a new row to the matrix. If the row starts with an or-pattern, this recursively
/// expands it.
fn push(&mut self, row: PatStack<'p, 'tcx>) {
- if !row.is_empty() && is_or_pat(row.head()) {
+ if !row.is_empty() && row.head().is_or_pat() {
for row in row.expand_or_pat() {
self.patterns.push(row);
}
}
/// Iterate over the first component of each row
- fn heads<'a>(&'a self) -> impl Iterator<Item = &'a Pat<'tcx>> + Captures<'p> {
- self.patterns.iter().map(|r| r.head())
- }
-
- /// Iterate over the first constructor of each row.
- pub(super) fn head_ctors<'a>(
+ fn heads<'a>(
&'a self,
- cx: &'a MatchCheckCtxt<'p, 'tcx>,
- ) -> impl Iterator<Item = &'a Constructor<'tcx>> + Captures<'p> + Clone {
- self.patterns.iter().map(move |r| r.head_ctor(cx))
- }
-
- /// Iterate over the first constructor and the corresponding span of each row.
- pub(super) fn head_ctors_and_spans<'a>(
- &'a self,
- cx: &'a MatchCheckCtxt<'p, 'tcx>,
- ) -> impl Iterator<Item = (&'a Constructor<'tcx>, Span)> + Captures<'p> {
- self.patterns.iter().map(move |r| (r.head_ctor(cx), r.head().span))
+ ) -> impl Iterator<Item = &'p DeconstructedPat<'p, 'tcx>> + Clone + Captures<'a> {
+ self.patterns.iter().map(|r| r.head())
}
/// This computes `S(constructor, self)`. See top of the file for explanations.
&self,
pcx: PatCtxt<'_, 'p, 'tcx>,
ctor: &Constructor<'tcx>,
- ctor_wild_subpatterns: &Fields<'p, 'tcx>,
) -> Matrix<'p, 'tcx> {
- self.patterns
- .iter()
- .filter(|r| ctor.is_covered_by(pcx, r.head_ctor(pcx.cx)))
- .map(|r| r.pop_head_constructor(ctor_wild_subpatterns))
- .collect()
+ let mut matrix = Matrix::empty();
+ for row in &self.patterns {
+ if ctor.is_covered_by(pcx, row.head().ctor()) {
+ let new_row = row.pop_head_constructor(pcx.cx, ctor);
+ matrix.push(new_row);
+ }
+ }
+ matrix
}
}
let Matrix { patterns: m, .. } = self;
let pretty_printed_matrix: Vec<Vec<String>> =
- m.iter().map(|row| row.iter().map(|pat| format!("{}", pat)).collect()).collect();
+ m.iter().map(|row| row.iter().map(|pat| format!("{:?}", pat)).collect()).collect();
let column_count = m.iter().map(|row| row.len()).next().unwrap_or(0);
assert!(m.iter().all(|row| row.len() == column_count));
}
}
-impl<'p, 'tcx> FromIterator<PatStack<'p, 'tcx>> for Matrix<'p, 'tcx> {
- fn from_iter<T>(iter: T) -> Self
- where
- T: IntoIterator<Item = PatStack<'p, 'tcx>>,
- {
- let mut matrix = Matrix::empty();
- for x in iter {
- // Using `push` ensures we correctly expand or-patterns.
- matrix.push(x);
- }
- matrix
- }
-}
-
-/// Given a pattern or a pattern-stack, this struct captures a set of its subpatterns. We use that
-/// to track reachable sub-patterns arising from or-patterns. In the absence of or-patterns this
-/// will always be either `Empty` (the whole pattern is unreachable) or `Full` (the whole pattern
-/// is reachable). When there are or-patterns, some subpatterns may be reachable while others
-/// aren't. In this case the whole pattern still counts as reachable, but we will lint the
-/// unreachable subpatterns.
-///
-/// This supports a limited set of operations, so not all possible sets of subpatterns can be
-/// represented. That's ok, we only want the ones that make sense for our usage.
-///
-/// What we're doing is illustrated by this:
-/// ```
-/// match (true, 0) {
-/// (true, 0) => {}
-/// (_, 1) => {}
-/// (true | false, 0 | 1) => {}
-/// }
-/// ```
-/// When we try the alternatives of the `true | false` or-pattern, the last `0` is reachable in the
-/// `false` alternative but not the `true`. So overall it is reachable. By contrast, the last `1`
-/// is not reachable in either alternative, so we want to signal this to the user.
-/// Therefore we take the union of sets of reachable patterns coming from different alternatives in
-/// order to figure out which subpatterns are overall reachable.
-///
-/// Invariant: we try to construct the smallest representation we can. In particular if
-/// `self.is_empty()` we ensure that `self` is `Empty`, and same with `Full`. This is not important
-/// for correctness currently.
-#[derive(Debug, Clone)]
-enum SubPatSet<'p, 'tcx> {
- /// The empty set. This means the pattern is unreachable.
- Empty,
- /// The set containing the full pattern.
- Full,
- /// If the pattern is a pattern with a constructor or a pattern-stack, we store a set for each
- /// of its subpatterns. Missing entries in the map are implicitly full, because that's the
- /// common case.
- Seq { subpats: FxHashMap<usize, SubPatSet<'p, 'tcx>> },
- /// If the pattern is an or-pattern, we store a set for each of its alternatives. Missing
- /// entries in the map are implicitly empty. Note: we always flatten nested or-patterns.
- Alt {
- subpats: FxHashMap<usize, SubPatSet<'p, 'tcx>>,
- /// Counts the total number of alternatives in the pattern
- alt_count: usize,
- /// We keep the pattern around to retrieve spans.
- pat: &'p Pat<'tcx>,
- },
-}
-
-impl<'p, 'tcx> SubPatSet<'p, 'tcx> {
- fn full() -> Self {
- SubPatSet::Full
- }
- fn empty() -> Self {
- SubPatSet::Empty
- }
-
- fn is_empty(&self) -> bool {
- match self {
- SubPatSet::Empty => true,
- SubPatSet::Full => false,
- // If any subpattern in a sequence is unreachable, the whole pattern is unreachable.
- SubPatSet::Seq { subpats } => subpats.values().any(|set| set.is_empty()),
- // An or-pattern is reachable if any of its alternatives is.
- SubPatSet::Alt { subpats, .. } => subpats.values().all(|set| set.is_empty()),
- }
- }
-
- fn is_full(&self) -> bool {
- match self {
- SubPatSet::Empty => false,
- SubPatSet::Full => true,
- // The whole pattern is reachable only when all its alternatives are.
- SubPatSet::Seq { subpats } => subpats.values().all(|sub_set| sub_set.is_full()),
- // The whole or-pattern is reachable only when all its alternatives are.
- SubPatSet::Alt { subpats, alt_count, .. } => {
- subpats.len() == *alt_count && subpats.values().all(|set| set.is_full())
- }
- }
- }
-
- /// Union `self` with `other`, mutating `self`.
- fn union(&mut self, other: Self) {
- use SubPatSet::*;
- // Union with full stays full; union with empty changes nothing.
- if self.is_full() || other.is_empty() {
- return;
- } else if self.is_empty() {
- *self = other;
- return;
- } else if other.is_full() {
- *self = Full;
- return;
- }
-
- match (&mut *self, other) {
- (Seq { subpats: s_set }, Seq { subpats: mut o_set }) => {
- s_set.retain(|i, s_sub_set| {
- // Missing entries count as full.
- let o_sub_set = o_set.remove(&i).unwrap_or(Full);
- s_sub_set.union(o_sub_set);
- // We drop full entries.
- !s_sub_set.is_full()
- });
- // Everything left in `o_set` is missing from `s_set`, i.e. counts as full. Since
- // unioning with full returns full, we can drop those entries.
- }
- (Alt { subpats: s_set, .. }, Alt { subpats: mut o_set, .. }) => {
- s_set.retain(|i, s_sub_set| {
- // Missing entries count as empty.
- let o_sub_set = o_set.remove(&i).unwrap_or(Empty);
- s_sub_set.union(o_sub_set);
- // We drop empty entries.
- !s_sub_set.is_empty()
- });
- // Everything left in `o_set` is missing from `s_set`, i.e. counts as empty. Since
- // unioning with empty changes nothing, we can take those entries as is.
- s_set.extend(o_set);
- }
- _ => bug!(),
- }
-
- if self.is_full() {
- *self = Full;
- }
- }
-
- /// Returns a list of the spans of the unreachable subpatterns. If `self` is empty (i.e. the
- /// whole pattern is unreachable) we return `None`.
- fn list_unreachable_spans(&self) -> Option<Vec<Span>> {
- /// Panics if `set.is_empty()`.
- fn fill_spans(set: &SubPatSet<'_, '_>, spans: &mut Vec<Span>) {
- match set {
- SubPatSet::Empty => bug!(),
- SubPatSet::Full => {}
- SubPatSet::Seq { subpats } => {
- for (_, sub_set) in subpats {
- fill_spans(sub_set, spans);
- }
- }
- SubPatSet::Alt { subpats, pat, alt_count, .. } => {
- let expanded = expand_or_pat(pat);
- for i in 0..*alt_count {
- let sub_set = subpats.get(&i).unwrap_or(&SubPatSet::Empty);
- if sub_set.is_empty() {
- // Found an unreachable subpattern.
- spans.push(expanded[i].span);
- } else {
- fill_spans(sub_set, spans);
- }
- }
- }
- }
- }
-
- if self.is_empty() {
- return None;
- }
- if self.is_full() {
- // No subpatterns are unreachable.
- return Some(Vec::new());
- }
- let mut spans = Vec::new();
- fill_spans(self, &mut spans);
- Some(spans)
- }
-
- /// When `self` refers to a patstack that was obtained from specialization, after running
- /// `unspecialize` it will refer to the original patstack before specialization.
- fn unspecialize(self, arity: usize) -> Self {
- use SubPatSet::*;
- match self {
- Full => Full,
- Empty => Empty,
- Seq { subpats } => {
- // We gather the first `arity` subpatterns together and shift the remaining ones.
- let mut new_subpats = FxHashMap::default();
- let mut new_subpats_first_col = FxHashMap::default();
- for (i, sub_set) in subpats {
- if i < arity {
- // The first `arity` indices are now part of the pattern in the first
- // column.
- new_subpats_first_col.insert(i, sub_set);
- } else {
- // Indices after `arity` are simply shifted
- new_subpats.insert(i - arity + 1, sub_set);
- }
- }
- // If `new_subpats_first_col` has no entries it counts as full, so we can omit it.
- if !new_subpats_first_col.is_empty() {
- new_subpats.insert(0, Seq { subpats: new_subpats_first_col });
- }
- Seq { subpats: new_subpats }
- }
- Alt { .. } => bug!(), // `self` is a patstack
- }
- }
-
- /// When `self` refers to a patstack that was obtained from splitting an or-pattern, after
- /// running `unspecialize` it will refer to the original patstack before splitting.
- ///
- /// For example:
- /// ```
- /// match Some(true) {
- /// Some(true) => {}
- /// None | Some(true | false) => {}
- /// }
- /// ```
- /// Here `None` would return the full set and `Some(true | false)` would return the set
- /// containing `false`. After `unsplit_or_pat`, we want the set to contain `None` and `false`.
- /// This is what this function does.
- fn unsplit_or_pat(mut self, alt_id: usize, alt_count: usize, pat: &'p Pat<'tcx>) -> Self {
- use SubPatSet::*;
- if self.is_empty() {
- return Empty;
- }
-
- // Subpatterns coming from inside the or-pattern alternative itself, e.g. in `None | Some(0
- // | 1)`.
- let set_first_col = match &mut self {
- Full => Full,
- Seq { subpats } => subpats.remove(&0).unwrap_or(Full),
- Empty => unreachable!(),
- Alt { .. } => bug!(), // `self` is a patstack
- };
- let mut subpats_first_col = FxHashMap::default();
- subpats_first_col.insert(alt_id, set_first_col);
- let set_first_col = Alt { subpats: subpats_first_col, pat, alt_count };
-
- let mut subpats = match self {
- Full => FxHashMap::default(),
- Seq { subpats } => subpats,
- Empty => unreachable!(),
- Alt { .. } => bug!(), // `self` is a patstack
- };
- subpats.insert(0, set_first_col);
- Seq { subpats }
- }
-}
-
/// This carries the results of computing usefulness, as described at the top of the file. When
/// checking usefulness of a match branch, we use the `NoWitnesses` variant, which also keeps track
/// of potential unreachable sub-patterns (in the presence of or-patterns). When checking
/// exhaustiveness of a whole match, we use the `WithWitnesses` variant, which carries a list of
/// witnesses of non-exhaustiveness when there are any.
/// Which variant to use is dictated by `ArmType`.
-#[derive(Clone, Debug)]
+#[derive(Debug)]
enum Usefulness<'p, 'tcx> {
- /// Carries a set of subpatterns that have been found to be reachable. If empty, this indicates
- /// the whole pattern is unreachable. If not, this indicates that the pattern is reachable but
- /// that some sub-patterns may be unreachable (due to or-patterns). In the absence of
- /// or-patterns this will always be either `Empty` (the whole pattern is unreachable) or `Full`
- /// (the whole pattern is reachable).
- NoWitnesses(SubPatSet<'p, 'tcx>),
+ /// If we don't care about witnesses, simply remember if the pattern was useful.
+ NoWitnesses { useful: bool },
/// Carries a list of witnesses of non-exhaustiveness. If empty, indicates that the whole
/// pattern is unreachable.
- WithWitnesses(Vec<Witness<'tcx>>),
+ WithWitnesses(Vec<Witness<'p, 'tcx>>),
}
impl<'p, 'tcx> Usefulness<'p, 'tcx> {
fn new_useful(preference: ArmType) -> Self {
match preference {
+ // A single (empty) witness of reachability.
FakeExtraWildcard => WithWitnesses(vec![Witness(vec![])]),
- RealArm => NoWitnesses(SubPatSet::full()),
+ RealArm => NoWitnesses { useful: true },
}
}
fn new_not_useful(preference: ArmType) -> Self {
match preference {
FakeExtraWildcard => WithWitnesses(vec![]),
- RealArm => NoWitnesses(SubPatSet::empty()),
+ RealArm => NoWitnesses { useful: false },
}
}
fn is_useful(&self) -> bool {
match self {
- Usefulness::NoWitnesses(set) => !set.is_empty(),
+ Usefulness::NoWitnesses { useful } => *useful,
Usefulness::WithWitnesses(witnesses) => !witnesses.is_empty(),
}
}
(WithWitnesses(_), WithWitnesses(o)) if o.is_empty() => {}
(WithWitnesses(s), WithWitnesses(o)) if s.is_empty() => *self = WithWitnesses(o),
(WithWitnesses(s), WithWitnesses(o)) => s.extend(o),
- (NoWitnesses(s), NoWitnesses(o)) => s.union(o),
- _ => unreachable!(),
- }
- }
-
- /// When trying several branches and each returns a `Usefulness`, we need to combine the
- /// results together.
- fn merge(pref: ArmType, usefulnesses: impl Iterator<Item = Self>) -> Self {
- let mut ret = Self::new_not_useful(pref);
- for u in usefulnesses {
- ret.extend(u);
- if let NoWitnesses(subpats) = &ret {
- if subpats.is_full() {
- // Once we reach the full set, more unions won't change the result.
- return ret;
- }
+ (NoWitnesses { useful: s_useful }, NoWitnesses { useful: o_useful }) => {
+ *s_useful = *s_useful || o_useful
}
- }
- ret
- }
-
- /// After calculating the usefulness for a branch of an or-pattern, call this to make this
- /// usefulness mergeable with those from the other branches.
- fn unsplit_or_pat(self, alt_id: usize, alt_count: usize, pat: &'p Pat<'tcx>) -> Self {
- match self {
- NoWitnesses(subpats) => NoWitnesses(subpats.unsplit_or_pat(alt_id, alt_count, pat)),
- WithWitnesses(_) => bug!(),
+ _ => unreachable!(),
}
}
pcx: PatCtxt<'_, 'p, 'tcx>,
matrix: &Matrix<'p, 'tcx>, // used to compute missing ctors
ctor: &Constructor<'tcx>,
- ctor_wild_subpatterns: &Fields<'p, 'tcx>,
) -> Self {
match self {
- WithWitnesses(witnesses) if witnesses.is_empty() => WithWitnesses(witnesses),
+ NoWitnesses { .. } => self,
+ WithWitnesses(ref witnesses) if witnesses.is_empty() => self,
WithWitnesses(witnesses) => {
let new_witnesses = if let Constructor::Missing { .. } = ctor {
// We got the special `Missing` constructor, so each of the missing constructors
let new_patterns = if pcx.is_non_exhaustive {
// Here we don't want the user to try to list all variants, we want them to add
// a wildcard, so we only suggest that.
- vec![
- Fields::wildcards(pcx, &Constructor::NonExhaustive)
- .apply(pcx, &Constructor::NonExhaustive),
- ]
+ vec![DeconstructedPat::wildcard(pcx.ty)]
} else {
let mut split_wildcard = SplitWildcard::new(pcx);
- split_wildcard.split(pcx, matrix.head_ctors(pcx.cx));
+ split_wildcard.split(pcx, matrix.heads().map(DeconstructedPat::ctor));
// Construct for each missing constructor a "wild" version of this
// constructor, that matches everything that can be built with
// it. For example, if `ctor` is a `Constructor::Variant` for
// `Option::Some`, we get the pattern `Some(_)`.
split_wildcard
.iter_missing(pcx)
- .map(|missing_ctor| {
- Fields::wildcards(pcx, missing_ctor).apply(pcx, missing_ctor)
- })
+ .cloned()
+ .map(|missing_ctor| DeconstructedPat::wild_from_ctor(pcx, missing_ctor))
.collect()
};
.into_iter()
.flat_map(|witness| {
new_patterns.iter().map(move |pat| {
- let mut witness = witness.clone();
- witness.0.push(pat.clone());
- witness
+ Witness(
+ witness
+ .0
+ .iter()
+ .chain(once(pat))
+ .map(DeconstructedPat::clone_and_forget_reachability)
+ .collect(),
+ )
})
})
.collect()
} else {
witnesses
.into_iter()
- .map(|witness| witness.apply_constructor(pcx, &ctor, ctor_wild_subpatterns))
+ .map(|witness| witness.apply_constructor(pcx, &ctor))
.collect()
};
WithWitnesses(new_witnesses)
}
- NoWitnesses(subpats) => NoWitnesses(subpats.unspecialize(ctor_wild_subpatterns.len())),
}
}
}
/// `Witness(vec![Pair(Some(_), true)])`
///
/// The final `Pair(Some(_), true)` is then the resulting witness.
-#[derive(Clone, Debug)]
-crate struct Witness<'tcx>(Vec<Pat<'tcx>>);
+#[derive(Debug)]
+crate struct Witness<'p, 'tcx>(Vec<DeconstructedPat<'p, 'tcx>>);
-impl<'tcx> Witness<'tcx> {
+impl<'p, 'tcx> Witness<'p, 'tcx> {
/// Asserts that the witness contains a single pattern, and returns it.
- fn single_pattern(self) -> Pat<'tcx> {
+ fn single_pattern(self) -> DeconstructedPat<'p, 'tcx> {
assert_eq!(self.0.len(), 1);
self.0.into_iter().next().unwrap()
}
///
/// left_ty: struct X { a: (bool, &'static str), b: usize}
/// pats: [(false, "foo"), 42] => X { a: (false, "foo"), b: 42 }
- fn apply_constructor<'p>(
- mut self,
- pcx: PatCtxt<'_, 'p, 'tcx>,
- ctor: &Constructor<'tcx>,
- ctor_wild_subpatterns: &Fields<'p, 'tcx>,
- ) -> Self {
+ fn apply_constructor(mut self, pcx: PatCtxt<'_, 'p, 'tcx>, ctor: &Constructor<'tcx>) -> Self {
let pat = {
let len = self.0.len();
- let arity = ctor_wild_subpatterns.len();
+ let arity = ctor.arity(pcx);
let pats = self.0.drain((len - arity)..).rev();
- ctor_wild_subpatterns.replace_fields(pcx.cx, pats).apply(pcx, ctor)
+ let fields = Fields::from_iter(pcx.cx, pats);
+ DeconstructedPat::new(ctor.clone(), fields, pcx.ty, DUMMY_SP)
};
self.0.push(pat);
scrut_ty: Ty<'tcx>,
sp: Span,
hir_id: HirId,
- witnesses: Vec<Pat<'tcx>>,
+ witnesses: Vec<DeconstructedPat<'p, 'tcx>>,
) {
- let joined_patterns = joined_uncovered_patterns(&witnesses);
+ let joined_patterns = joined_uncovered_patterns(cx, &witnesses);
cx.tcx.struct_span_lint_hir(NON_EXHAUSTIVE_OMITTED_PATTERNS, hir_id, sp, |build| {
let mut lint = build.build("some variants are not matched explicitly");
lint.span_label(sp, pattern_not_covered_label(&witnesses, &joined_patterns));
assert!(rows.iter().all(|r| r.len() == v.len()));
// FIXME(Nadrieril): Hack to work around type normalization issues (see #72476).
- let ty = matrix.heads().next().map_or(v.head().ty, |r| r.ty);
+ let ty = matrix.heads().next().map_or(v.head().ty(), |r| r.ty());
let is_non_exhaustive = cx.is_foreign_non_exhaustive_enum(ty);
- let pcx = PatCtxt { cx, ty, span: v.head().span, is_top_level, is_non_exhaustive };
+ let pcx = PatCtxt { cx, ty, span: v.head().span(), is_top_level, is_non_exhaustive };
// If the first pattern is an or-pattern, expand it.
- let ret = if is_or_pat(v.head()) {
+ let mut ret = Usefulness::new_not_useful(witness_preference);
+ if v.head().is_or_pat() {
debug!("expanding or-pattern");
- let v_head = v.head();
- let vs: Vec<_> = v.expand_or_pat().collect();
- let alt_count = vs.len();
// We try each or-pattern branch in turn.
let mut matrix = matrix.clone();
- let usefulnesses = vs.into_iter().enumerate().map(|(i, v)| {
+ for v in v.expand_or_pat() {
let usefulness =
is_useful(cx, &matrix, &v, witness_preference, hir_id, is_under_guard, false);
+ ret.extend(usefulness);
// If pattern has a guard don't add it to the matrix.
if !is_under_guard {
// We push the already-seen patterns into the matrix in order to detect redundant
// branches like `Some(_) | Some(0)`.
matrix.push(v);
}
- usefulness.unsplit_or_pat(i, alt_count, v_head)
- });
- Usefulness::merge(witness_preference, usefulnesses)
+ }
} else {
- let v_ctor = v.head_ctor(cx);
+ let v_ctor = v.head().ctor();
if let Constructor::IntRange(ctor_range) = &v_ctor {
// Lint on likely incorrect range patterns (#63987)
ctor_range.lint_overlapping_range_endpoints(
pcx,
- matrix.head_ctors_and_spans(cx),
+ matrix.heads(),
matrix.column_count().unwrap_or(0),
hir_id,
)
}
// We split the head constructor of `v`.
- let split_ctors = v_ctor.split(pcx, matrix.head_ctors(cx));
+ let split_ctors = v_ctor.split(pcx, matrix.heads().map(DeconstructedPat::ctor));
let is_non_exhaustive_and_wild = is_non_exhaustive && v_ctor.is_wildcard();
// For each constructor, we compute whether there's a value that starts with it that would
// witness the usefulness of `v`.
let start_matrix = &matrix;
- let usefulnesses = split_ctors.into_iter().map(|ctor| {
+ for ctor in split_ctors {
debug!("specialize({:?})", ctor);
// We cache the result of `Fields::wildcards` because it is used a lot.
- let ctor_wild_subpatterns = Fields::wildcards(pcx, &ctor);
- let spec_matrix =
- start_matrix.specialize_constructor(pcx, &ctor, &ctor_wild_subpatterns);
- let v = v.pop_head_constructor(&ctor_wild_subpatterns);
+ let spec_matrix = start_matrix.specialize_constructor(pcx, &ctor);
+ let v = v.pop_head_constructor(cx, &ctor);
let usefulness =
is_useful(cx, &spec_matrix, &v, witness_preference, hir_id, is_under_guard, false);
+ let usefulness = usefulness.apply_constructor(pcx, start_matrix, &ctor);
// When all the conditions are met we have a match with a `non_exhaustive` enum
// that has the potential to trigger the `non_exhaustive_omitted_patterns` lint.
{
let patterns = {
let mut split_wildcard = SplitWildcard::new(pcx);
- split_wildcard.split(pcx, matrix.head_ctors(pcx.cx));
+ split_wildcard.split(pcx, matrix.heads().map(DeconstructedPat::ctor));
// Construct for each missing constructor a "wild" version of this
// constructor, that matches everything that can be built with
// it. For example, if `ctor` is a `Constructor::Variant` for
// `Option::Some`, we get the pattern `Some(_)`.
split_wildcard
.iter_missing(pcx)
- // Filter out the `Constructor::NonExhaustive` variant it's meaningless
- // to our lint
+ // Filter out the `NonExhaustive` because we want to list only real
+ // variants.
.filter(|c| !c.is_non_exhaustive())
- .map(|missing_ctor| {
- Fields::wildcards(pcx, missing_ctor).apply(pcx, missing_ctor)
- })
+ .cloned()
+ .map(|missing_ctor| DeconstructedPat::wild_from_ctor(pcx, missing_ctor))
.collect::<Vec<_>>()
};
lint_non_exhaustive_omitted_patterns(pcx.cx, pcx.ty, pcx.span, hir_id, patterns);
}
- usefulness.apply_constructor(pcx, start_matrix, &ctor, &ctor_wild_subpatterns)
- });
- Usefulness::merge(witness_preference, usefulnesses)
- };
+ ret.extend(usefulness);
+ }
+ }
+
+ if ret.is_useful() {
+ v.head().set_reachable();
+ }
debug!(?ret);
ret
#[derive(Clone, Copy)]
crate struct MatchArm<'p, 'tcx> {
/// The pattern must have been lowered through `check_match::MatchVisitor::lower_pattern`.
- crate pat: &'p Pat<'tcx>,
+ crate pat: &'p DeconstructedPat<'p, 'tcx>,
crate hir_id: HirId,
crate has_guard: bool,
}
crate arm_usefulness: Vec<(MatchArm<'p, 'tcx>, Reachability)>,
/// If the match is exhaustive, this is empty. If not, this contains witnesses for the lack of
/// exhaustiveness.
- crate non_exhaustiveness_witnesses: Vec<Pat<'tcx>>,
+ crate non_exhaustiveness_witnesses: Vec<DeconstructedPat<'p, 'tcx>>,
}
/// The entrypoint for the usefulness algorithm. Computes whether a match is exhaustive and which
.copied()
.map(|arm| {
let v = PatStack::from_pattern(arm.pat);
- let usefulness = is_useful(cx, &matrix, &v, RealArm, arm.hir_id, arm.has_guard, true);
+ is_useful(cx, &matrix, &v, RealArm, arm.hir_id, arm.has_guard, true);
if !arm.has_guard {
matrix.push(v);
}
- let reachability = match usefulness {
- NoWitnesses(subpats) if subpats.is_empty() => Reachability::Unreachable,
- NoWitnesses(subpats) => {
- Reachability::Reachable(subpats.list_unreachable_spans().unwrap())
- }
- WithWitnesses(..) => bug!(),
+ let reachability = if arm.pat.is_reachable() {
+ Reachability::Reachable(arm.pat.unreachable_spans())
+ } else {
+ Reachability::Unreachable
};
(arm, reachability)
})
.collect();
- let wild_pattern = cx.pattern_arena.alloc(Pat::wildcard_from_ty(scrut_ty));
+ let wild_pattern = cx.pattern_arena.alloc(DeconstructedPat::wildcard(scrut_ty));
let v = PatStack::from_pattern(wild_pattern);
let usefulness = is_useful(cx, &matrix, &v, FakeExtraWildcard, scrut_hir_id, false, true);
let non_exhaustiveness_witnesses = match usefulness {
WithWitnesses(pats) => pats.into_iter().map(|w| w.single_pattern()).collect(),
- NoWitnesses(_) => bug!(),
+ NoWitnesses { .. } => bug!(),
};
UsefulnessReport { arm_usefulness, non_exhaustiveness_witnesses }
}
impl<'tcx> GenKillAnalysis<'tcx> for EverInitializedPlaces<'_, 'tcx> {
type Idx = InitIndex;
+ #[instrument(skip(self, trans), level = "debug")]
fn statement_effect(
&self,
trans: &mut impl GenKill<Self::Idx>,
let init_loc_map = &move_data.init_loc_map;
let rev_lookup = &move_data.rev_lookup;
- debug!(
- "statement {:?} at loc {:?} initializes move_indexes {:?}",
- stmt, location, &init_loc_map[location]
- );
+ debug!("initializes move_indexes {:?}", &init_loc_map[location]);
trans.gen_all(init_loc_map[location].iter().copied());
if let mir::StatementKind::StorageDead(local) = stmt.kind {
// End inits for StorageDead, so that an immutable variable can
// be reinitialized on the next iteration of the loop.
let move_path_index = rev_lookup.find_local(local);
- debug!(
- "stmt {:?} at loc {:?} clears the ever initialized status of {:?}",
- stmt, location, &init_path_map[move_path_index]
- );
+ debug!("clears the ever initialized status of {:?}", init_path_map[move_path_index]);
trans.kill_all(init_path_map[move_path_index].iter().copied());
}
}
+ #[instrument(skip(self, trans, _terminator), level = "debug")]
fn terminator_effect(
&self,
trans: &mut impl GenKill<Self::Idx>,
let (body, move_data) = (self.body, self.move_data());
let term = body[location.block].terminator();
let init_loc_map = &move_data.init_loc_map;
- debug!(
- "terminator {:?} at loc {:?} initializes move_indexes {:?}",
- term, location, &init_loc_map[location]
- );
+ debug!(?term);
+ debug!("initializes move_indexes {:?}", init_loc_map[location]);
trans.gen_all(
init_loc_map[location]
.iter()
err.span_label(first_use_span, format!("first use of `{}`", name));
err
}
- ResolutionError::MethodNotMemberOfTrait(method, trait_) => {
+ ResolutionError::MethodNotMemberOfTrait(method, trait_, candidate) => {
let mut err = struct_span_err!(
self.session,
span,
trait_
);
err.span_label(span, format!("not a member of trait `{}`", trait_));
+ if let Some(candidate) = candidate {
+ err.span_suggestion(
+ method.span,
+ "there is an associated function with a similar name",
+ candidate.to_ident_string(),
+ Applicability::MaybeIncorrect,
+ );
+ }
err
}
- ResolutionError::TypeNotMemberOfTrait(type_, trait_) => {
+ ResolutionError::TypeNotMemberOfTrait(type_, trait_, candidate) => {
let mut err = struct_span_err!(
self.session,
span,
trait_
);
err.span_label(span, format!("not a member of trait `{}`", trait_));
+ if let Some(candidate) = candidate {
+ err.span_suggestion(
+ type_.span,
+ "there is an associated type with a similar name",
+ candidate.to_ident_string(),
+ Applicability::MaybeIncorrect,
+ );
+ }
err
}
- ResolutionError::ConstNotMemberOfTrait(const_, trait_) => {
+ ResolutionError::ConstNotMemberOfTrait(const_, trait_, candidate) => {
let mut err = struct_span_err!(
self.session,
span,
trait_
);
err.span_label(span, format!("not a member of trait `{}`", trait_));
+ if let Some(candidate) = candidate {
+ err.span_suggestion(
+ const_.span,
+ "there is an associated constant with a similar name",
+ candidate.to_ident_string(),
+ Applicability::MaybeIncorrect,
+ );
+ }
err
}
ResolutionError::VariableNotBoundInPattern(binding_error) => {
let import_suggestions =
self.lookup_import_candidates(ident, Namespace::MacroNS, parent_scope, is_expected);
- show_candidates(err, None, &import_suggestions, false, true);
+ show_candidates(
+ &self.definitions,
+ self.session,
+ err,
+ None,
+ &import_suggestions,
+ false,
+ true,
+ );
if macro_kind == MacroKind::Derive && (ident.name == sym::Send || ident.name == sym::Sync) {
let msg = format!("unsafe traits like `{}` should be implemented explicitly", ident);
/// entities with that name in all crates. This method allows outputting the
/// results of this search in a programmer-friendly way
crate fn show_candidates(
+ definitions: &rustc_hir::definitions::Definitions,
+ session: &Session,
err: &mut DiagnosticBuilder<'_>,
// This is `None` if all placement locations are inside expansions
use_placement_span: Option<Span>,
return;
}
+ let mut accessible_path_strings: Vec<(String, &str, Option<DefId>)> = Vec::new();
+ let mut inaccessible_path_strings: Vec<(String, &str, Option<DefId>)> = Vec::new();
+
+ candidates.iter().for_each(|c| {
+ (if c.accessible { &mut accessible_path_strings } else { &mut inaccessible_path_strings })
+ .push((path_names_to_string(&c.path), c.descr, c.did))
+ });
+
// we want consistent results across executions, but candidates are produced
// by iterating through a hash map, so make sure they are ordered:
- let mut path_strings: Vec<_> =
- candidates.iter().map(|c| path_names_to_string(&c.path)).collect();
+ for path_strings in [&mut accessible_path_strings, &mut inaccessible_path_strings] {
+ path_strings.sort_by(|a, b| a.0.cmp(&b.0));
+ let core_path_strings =
+ path_strings.drain_filter(|p| p.0.starts_with("core::")).collect::<Vec<_>>();
+ path_strings.extend(core_path_strings);
+ path_strings.dedup_by(|a, b| a.0 == b.0);
+ }
- path_strings.sort();
- let core_path_strings =
- path_strings.drain_filter(|p| p.starts_with("core::")).collect::<Vec<String>>();
- path_strings.extend(core_path_strings);
- path_strings.dedup();
+ if !accessible_path_strings.is_empty() {
+ let (determiner, kind) = if accessible_path_strings.len() == 1 {
+ ("this", accessible_path_strings[0].1)
+ } else {
+ ("one of these", "items")
+ };
- let (determiner, kind) = if candidates.len() == 1 {
- ("this", candidates[0].descr)
- } else {
- ("one of these", "items")
- };
-
- let instead = if instead { " instead" } else { "" };
- let mut msg = format!("consider importing {} {}{}", determiner, kind, instead);
-
- if let Some(span) = use_placement_span {
- for candidate in &mut path_strings {
- // produce an additional newline to separate the new use statement
- // from the directly following item.
- let additional_newline = if found_use { "" } else { "\n" };
- *candidate = format!("use {};\n{}", candidate, additional_newline);
- }
+ let instead = if instead { " instead" } else { "" };
+ let mut msg = format!("consider importing {} {}{}", determiner, kind, instead);
- err.span_suggestions(span, &msg, path_strings.into_iter(), Applicability::Unspecified);
- } else {
- msg.push(':');
+ if let Some(span) = use_placement_span {
+ for candidate in &mut accessible_path_strings {
+ // produce an additional newline to separate the new use statement
+ // from the directly following item.
+ let additional_newline = if found_use { "" } else { "\n" };
+ candidate.0 = format!("use {};\n{}", &candidate.0, additional_newline);
+ }
+
+ err.span_suggestions(
+ span,
+ &msg,
+ accessible_path_strings.into_iter().map(|a| a.0),
+ Applicability::Unspecified,
+ );
+ } else {
+ msg.push(':');
+
+ for candidate in accessible_path_strings {
+ msg.push('\n');
+ msg.push_str(&candidate.0);
+ }
- for candidate in path_strings {
- msg.push('\n');
- msg.push_str(&candidate);
+ err.note(&msg);
}
+ } else {
+ assert!(!inaccessible_path_strings.is_empty());
+
+ if inaccessible_path_strings.len() == 1 {
+ let (name, descr, def_id) = &inaccessible_path_strings[0];
+ let msg = format!("{} `{}` exists but is inaccessible", descr, name);
+
+ if let Some(local_def_id) = def_id.and_then(|did| did.as_local()) {
+ let span = definitions.def_span(local_def_id);
+ let span = session.source_map().guess_head_span(span);
+ let mut multi_span = MultiSpan::from_span(span);
+ multi_span.push_span_label(span, "not accessible".to_string());
+ err.span_note(multi_span, &msg);
+ } else {
+ err.note(&msg);
+ }
+ } else {
+ let (_, descr_first, _) = &inaccessible_path_strings[0];
+ let descr = if inaccessible_path_strings
+ .iter()
+ .skip(1)
+ .all(|(_, descr, _)| descr == descr_first)
+ {
+ format!("{}", descr_first)
+ } else {
+ "item".to_string()
+ };
+
+ let mut msg = format!("these {}s exist but are inaccessible", descr);
+ let mut has_colon = false;
- err.note(&msg);
+ let mut spans = Vec::new();
+ for (name, _, def_id) in &inaccessible_path_strings {
+ if let Some(local_def_id) = def_id.and_then(|did| did.as_local()) {
+ let span = definitions.def_span(local_def_id);
+ let span = session.source_map().guess_head_span(span);
+ spans.push((name, span));
+ } else {
+ if !has_colon {
+ msg.push(':');
+ has_colon = true;
+ }
+ msg.push('\n');
+ msg.push_str(name);
+ }
+ }
+
+ let mut multi_span = MultiSpan::from_spans(spans.iter().map(|(_, sp)| *sp).collect());
+ for (name, span) in spans {
+ multi_span.push_span_label(span, format!("`{}`: not accessible", name));
+ }
+
+ err.span_note(multi_span, &msg);
+ }
}
}
use crate::ResolutionError::*;
match &item.kind {
AssocItemKind::Const(_default, _ty, _expr) => {
- debug!("resolve_implementation AssocItemKind::Const",);
+ debug!("resolve_implementation AssocItemKind::Const");
// If this is a trait impl, ensure the const
// exists in trait
this.check_trait_item(
item.ident,
+ &item.kind,
ValueNS,
item.span,
- |n, s| ConstNotMemberOfTrait(n, s),
+ |i, s, c| ConstNotMemberOfTrait(i, s, c),
);
// We allow arbitrary const expressions inside of associated consts,
);
}
AssocItemKind::Fn(box FnKind(.., generics, _)) => {
+ debug!("resolve_implementation AssocItemKind::Fn");
// We also need a new scope for the impl item type parameters.
this.with_generic_param_rib(
generics,
// exists in trait
this.check_trait_item(
item.ident,
+ &item.kind,
ValueNS,
item.span,
- |n, s| MethodNotMemberOfTrait(n, s),
+ |i, s, c| MethodNotMemberOfTrait(i, s, c),
);
visit::walk_assoc_item(
_,
_,
)) => {
+ debug!("resolve_implementation AssocItemKind::TyAlias");
// We also need a new scope for the impl item type parameters.
this.with_generic_param_rib(
generics,
// exists in trait
this.check_trait_item(
item.ident,
+ &item.kind,
TypeNS,
item.span,
- |n, s| TypeNotMemberOfTrait(n, s),
+ |i, s, c| TypeNotMemberOfTrait(i, s, c),
);
visit::walk_assoc_item(
});
}
- fn check_trait_item<F>(&mut self, ident: Ident, ns: Namespace, span: Span, err: F)
- where
- F: FnOnce(Symbol, &str) -> ResolutionError<'_>,
+ fn check_trait_item<F>(
+ &mut self,
+ ident: Ident,
+ kind: &AssocItemKind,
+ ns: Namespace,
+ span: Span,
+ err: F,
+ ) where
+ F: FnOnce(Ident, &str, Option<Symbol>) -> ResolutionError<'_>,
{
// If there is a TraitRef in scope for an impl, then the method must be in the
// trait.
)
.is_err()
{
+ let candidate = self.find_similarly_named_assoc_item(ident.name, kind);
let path = &self.current_trait_ref.as_ref().unwrap().1.path;
- self.report_error(span, err(ident.name, &path_names_to_string(path)));
+ self.report_error(span, err(ident, &path_names_to_string(path), candidate));
}
}
}
use rustc_ast::visit::FnKind;
use rustc_ast::{
- self as ast, Expr, ExprKind, GenericParam, GenericParamKind, Item, ItemKind, NodeId, Path, Ty,
- TyKind,
+ self as ast, AssocItemKind, Expr, ExprKind, GenericParam, GenericParamKind, Item, ItemKind,
+ NodeId, Path, Ty, TyKind,
};
use rustc_ast_pretty::pprust::path_segment_to_string;
use rustc_data_structures::fx::FxHashSet;
true
}
+ /// Given the target `ident` and `kind`, search for the similarly named associated item
+ /// in `self.current_trait_ref`.
+ crate fn find_similarly_named_assoc_item(
+ &mut self,
+ ident: Symbol,
+ kind: &AssocItemKind,
+ ) -> Option<Symbol> {
+ let module = if let Some((module, _)) = self.current_trait_ref {
+ module
+ } else {
+ return None;
+ };
+ if ident == kw::Underscore {
+ // We do nothing for `_`.
+ return None;
+ }
+
+ let resolutions = self.r.resolutions(module);
+ let targets = resolutions
+ .borrow()
+ .iter()
+ .filter_map(|(key, res)| res.borrow().binding.map(|binding| (key, binding.res())))
+ .filter(|(_, res)| match (kind, res) {
+ (AssocItemKind::Const(..), Res::Def(DefKind::AssocConst, _)) => true,
+ (AssocItemKind::Fn(_), Res::Def(DefKind::AssocFn, _)) => true,
+ (AssocItemKind::TyAlias(..), Res::Def(DefKind::AssocTy, _)) => true,
+ _ => false,
+ })
+ .map(|(key, _)| key.ident.name)
+ .collect::<Vec<_>>();
+
+ find_best_match_for_name(&targets, ident, None)
+ }
+
fn lookup_assoc_candidate<FilterFn>(
&mut self,
ident: Ident,
for input in inputs {
gather.visit_ty(input);
}
+ trace!(?gather.anon_count);
let late_bound_vars = self.map.late_bound_vars.entry(hir_id).or_default();
let named_late_bound_vars = late_bound_vars.len() as u32;
late_bound_vars.extend(
NestedVisitorMap::None
}
+ #[instrument(skip(self), level = "trace")]
fn visit_ty(&mut self, ty: &hir::Ty<'_>) {
// If we enter a `BareFn`, then we enter a *new* binding scope
if let hir::TyKind::BareFn(_) = ty.kind {
intravisit::walk_generic_args(self, path_span, generic_args)
}
+ #[instrument(skip(self), level = "trace")]
fn visit_lifetime(&mut self, lifetime_ref: &hir::Lifetime) {
if lifetime_ref.is_elided() {
self.anon_count += 1;
#![recursion_limit = "256"]
#![allow(rustdoc::private_intra_doc_links)]
+#[macro_use]
+extern crate tracing;
+
pub use rustc_hir::def::{Namespace, PerNS};
use Determinacy::*;
/// parameter list.
NameAlreadyUsedInParameterList(Symbol, Span),
/// Error E0407: method is not a member of trait.
- MethodNotMemberOfTrait(Symbol, &'a str),
+ MethodNotMemberOfTrait(Ident, &'a str, Option<Symbol>),
/// Error E0437: type is not a member of trait.
- TypeNotMemberOfTrait(Symbol, &'a str),
+ TypeNotMemberOfTrait(Ident, &'a str, Option<Symbol>),
/// Error E0438: const is not a member of trait.
- ConstNotMemberOfTrait(Symbol, &'a str),
+ ConstNotMemberOfTrait(Ident, &'a str, Option<Symbol>),
/// Error E0408: variable `{}` is not bound in all patterns.
VariableNotBoundInPattern(&'a BindingError),
/// Error E0409: variable `{}` is bound in inconsistent ways within the same match arm.
(None, false)
};
if !candidates.is_empty() {
- diagnostics::show_candidates(&mut err, span, &candidates, instead, found_use);
+ diagnostics::show_candidates(
+ &self.definitions,
+ self.session,
+ &mut err,
+ span,
+ &candidates,
+ instead,
+ found_use,
+ );
} else if let Some((span, msg, sugg, appl)) = suggestion {
err.span_suggestion(span, msg, sugg, appl);
}
use crate::code_stats::CodeStats;
pub use crate::code_stats::{DataTypeKind, FieldInfo, SizeKind, VariantInfo};
use crate::config::{self, CrateType, OutputType, SwitchWithOptPath};
-use crate::filesearch;
-use crate::lint::{self, LintId};
use crate::parse::ParseSess;
use crate::search_paths::{PathKind, SearchPath};
+use crate::{filesearch, lint};
pub use rustc_ast::attr::MarkedAttrs;
pub use rustc_ast::Attribute;
use std::sync::Arc;
use std::time::Duration;
-pub trait SessionLintStore: sync::Send + sync::Sync {
- fn name_to_lint(&self, lint_name: &str) -> LintId;
-}
-
pub struct OptimizationFuel {
/// If `-zfuel=crate=n` is specified, initially set to `n`, otherwise `0`.
remaining: u64,
features: OnceCell<rustc_feature::Features>,
- lint_store: OnceCell<Lrc<dyn SessionLintStore>>,
-
incr_comp_session: OneThread<RefCell<IncrCompSession>>,
/// Used for incremental compilation tests. Will only be populated if
/// `-Zquery-dep-graph` is specified.
}
}
- pub fn init_lint_store(&self, lint_store: Lrc<dyn SessionLintStore>) {
- self.lint_store
- .set(lint_store)
- .map_err(|_| ())
- .expect("`lint_store` was initialized twice");
- }
-
/// Calculates the flavor of LTO to use for this compilation.
pub fn lto(&self) -> config::Lto {
// If our target has codegen requirements ignore the command line
crate_types: OnceCell::new(),
stable_crate_id: OnceCell::new(),
features: OnceCell::new(),
- lint_store: OnceCell::new(),
incr_comp_session: OneThread::new(RefCell::new(IncrCompSession::NotInitialized)),
cgu_reuse_tracker,
prof,
--- /dev/null
+use super::{RelocModel, Target, TargetOptions};
+
+pub fn target() -> Target {
+ let base = super::solid_base::opts("asp3");
+ Target {
+ llvm_target: "aarch64-unknown-none".to_string(),
+ pointer_width: 64,
+ data_layout: "e-m:e-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128".to_string(),
+ arch: "aarch64".to_string(),
+ options: TargetOptions {
+ linker: Some("aarch64-kmc-elf-gcc".to_owned()),
+ features: "+neon,+fp-armv8".to_string(),
+ relocation_model: RelocModel::Static,
+ disable_redzone: true,
+ max_atomic_width: Some(128),
+ ..base
+ },
+ }
+}
--- /dev/null
+use super::{RelocModel, Target, TargetOptions};
+
+pub fn target() -> Target {
+ let base = super::solid_base::opts("asp3");
+ Target {
+ llvm_target: "armv7a-none-eabi".to_string(),
+ pointer_width: 32,
+ data_layout: "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64".to_string(),
+ arch: "arm".to_string(),
+ options: TargetOptions {
+ linker: Some("arm-kmc-eabi-gcc".to_owned()),
+ features: "+v7,+soft-float,+thumb2,-neon".to_string(),
+ relocation_model: RelocModel::Static,
+ disable_redzone: true,
+ max_atomic_width: Some(64),
+ ..base
+ },
+ }
+}
--- /dev/null
+use super::{RelocModel, Target, TargetOptions};
+
+pub fn target() -> Target {
+ let base = super::solid_base::opts("asp3");
+ Target {
+ llvm_target: "armv7a-none-eabihf".to_string(),
+ pointer_width: 32,
+ data_layout: "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64".to_string(),
+ arch: "arm".to_string(),
+ options: TargetOptions {
+ linker: Some("arm-kmc-eabi-gcc".to_owned()),
+ features: "+v7,+vfp3,-d32,+thumb2,-neon".to_string(),
+ relocation_model: RelocModel::Static,
+ disable_redzone: true,
+ max_atomic_width: Some(64),
+ ..base
+ },
+ }
+}
mod openbsd_base;
mod redox_base;
mod solaris_base;
+mod solid_base;
mod thumb_base;
mod uefi_msvc_base;
mod vxworks_base;
("powerpc-wrs-vxworks-spe", powerpc_wrs_vxworks_spe),
("powerpc64-wrs-vxworks", powerpc64_wrs_vxworks),
+ ("aarch64-kmc-solid_asp3", aarch64_kmc_solid_asp3),
+ ("armv7a-kmc-solid_asp3-eabi", armv7a_kmc_solid_asp3_eabi),
+ ("armv7a-kmc-solid_asp3-eabihf", armv7a_kmc_solid_asp3_eabihf),
+
("mipsel-sony-psp", mipsel_sony_psp),
("mipsel-unknown-none", mipsel_unknown_none),
("thumbv4t-none-eabi", thumbv4t_none_eabi),
--- /dev/null
+use super::FramePointer;
+use crate::spec::TargetOptions;
+
+pub fn opts(kernel: &str) -> TargetOptions {
+ TargetOptions {
+ os: format!("solid_{}", kernel),
+ vendor: "kmc".to_string(),
+ frame_pointer: FramePointer::NonLeaf,
+ has_elf_tls: true,
+ ..Default::default()
+ }
+}
use rustc_infer::infer::InferCtxt;
use rustc_middle::ty::{self, TraitRef, Ty, TyCtxt, WithConstness};
use rustc_middle::ty::{ToPredicate, TypeFoldable};
-use rustc_session::DiagnosticMessageId;
+use rustc_session::{DiagnosticMessageId, Limit};
use rustc_span::def_id::LOCAL_CRATE;
use rustc_span::Span;
pub fn report_autoderef_recursion_limit_error<'tcx>(tcx: TyCtxt<'tcx>, span: Span, ty: Ty<'tcx>) {
// We've reached the recursion limit, error gracefully.
- let suggested_limit = tcx.recursion_limit() * 2;
+ let suggested_limit = match tcx.recursion_limit() {
+ Limit(0) => Limit(2),
+ limit => limit * 2,
+ };
let msg = format!("reached the recursion limit while auto-dereferencing `{:?}`", ty);
let error_id = (DiagnosticMessageId::ErrorId(55), Some(span), msg);
let fresh = tcx.sess.one_time_diagnostics.borrow_mut().insert(error_id);
)
.span_label(span, "deref recursion limit reached")
.help(&format!(
- "consider adding a `#![recursion_limit=\"{}\"]` attribute to your crate (`{}`)",
+ "consider increasing the recursion limit by adding a \
+ `#![recursion_limit = \"{}\"]` attribute to your crate (`{}`)",
suggested_limit,
tcx.crate_name(LOCAL_CRATE),
))
debug!(?id_substs);
let map: FxHashMap<GenericArg<'tcx>, GenericArg<'tcx>> =
substs.iter().enumerate().map(|(index, subst)| (subst, id_substs[index])).collect();
+ debug!("map = {:#?}", map);
// Convert the type from the function into a type valid outside
// the function, by replacing invalid regions with 'static,
self.tcx
}
+ #[instrument(skip(self), level = "debug")]
fn fold_region(&mut self, r: ty::Region<'tcx>) -> ty::Region<'tcx> {
match r {
// Ignore bound regions and `'static` regions that appear in the
///
/// Requires that trait definitions have been processed so that we can
/// elaborate predicates and walk supertraits.
+#[instrument(skip(tcx, predicates), level = "debug")]
crate fn required_region_bounds(
tcx: TyCtxt<'tcx>,
erased_self_ty: Ty<'tcx>,
predicates: impl Iterator<Item = ty::Predicate<'tcx>>,
) -> Vec<ty::Region<'tcx>> {
- debug!("required_region_bounds(erased_self_ty={:?})", erased_self_ty);
-
assert!(!erased_self_ty.has_escaping_bound_vars());
traits::elaborate_predicates(tcx, predicates)
.filter_map(|obligation| {
- debug!("required_region_bounds(obligation={:?})", obligation);
+ debug!(?obligation);
match obligation.predicate.kind().skip_binder() {
ty::PredicateKind::Projection(..)
| ty::PredicateKind::Trait(..)
false
}
+ #[instrument(skip(self), level = "debug")]
fn report_fulfillment_error(
&self,
error: &FulfillmentError<'tcx>,
body_id: Option<hir::BodyId>,
fallback_has_occurred: bool,
) {
- debug!("report_fulfillment_error({:?})", error);
match error.code {
FulfillmentErrorCode::CodeSelectionError(ref selection_error) => {
self.report_selection_error(
)
}
+ #[instrument(skip(self), level = "debug")]
fn maybe_report_ambiguity(
&self,
obligation: &PredicateObligation<'tcx>,
let span = obligation.cause.span;
debug!(
- "maybe_report_ambiguity(predicate={:?}, obligation={:?} body_id={:?}, code={:?})",
- predicate, obligation, body_id, obligation.cause.code,
+ ?predicate, ?obligation.cause.code,
);
// Ambiguity errors are often caused as fallout from earlier
let mut err = match bound_predicate.skip_binder() {
ty::PredicateKind::Trait(data) => {
let trait_ref = bound_predicate.rebind(data.trait_ref);
- debug!("trait_ref {:?}", trait_ref);
+ debug!(?trait_ref);
if predicate.references_error() {
return;
Infer, InferTy, ToPredicate, Ty, TyCtxt, TypeFoldable, WithConstness,
};
use rustc_middle::ty::{TypeAndMut, TypeckResults};
+use rustc_session::Limit;
use rustc_span::def_id::LOCAL_CRATE;
use rustc_span::symbol::{kw, sym, Ident, Symbol};
use rustc_span::{BytePos, DesugaringKind, ExpnKind, ForLoopLoc, MultiSpan, Span, DUMMY_SP};
}
fn suggest_new_overflow_limit(&self, err: &mut DiagnosticBuilder<'_>) {
- let current_limit = self.tcx.recursion_limit();
- let suggested_limit = current_limit * 2;
+ let suggested_limit = match self.tcx.recursion_limit() {
+ Limit(0) => Limit(2),
+ limit => limit * 2,
+ };
err.help(&format!(
- "consider adding a `#![recursion_limit=\"{}\"]` attribute to your crate (`{}`)",
+ "consider increasing the recursion limit by adding a \
+ `#![recursion_limit = \"{}\"]` attribute to your crate (`{}`)",
suggested_limit,
self.tcx.crate_name(LOCAL_CRATE),
));
self.filter_impls(candidates.pop().unwrap().candidate, stack.obligation)
}
+ #[instrument(skip(self, stack), level = "debug")]
pub(super) fn assemble_candidates<'o>(
&mut self,
stack: &TraitObligationStack<'o, 'tcx>,
Ok(ImplSourceGeneratorData { generator_def_id, substs, nested: obligations })
}
+ #[instrument(skip(self), level = "debug")]
fn confirm_closure_candidate(
&mut self,
obligation: &TraitObligation<'tcx>,
) -> Result<ImplSourceClosureData<'tcx, PredicateObligation<'tcx>>, SelectionError<'tcx>> {
- debug!(?obligation, "confirm_closure_candidate");
-
let kind = self
.tcx()
.fn_trait_kind_from_lang_item(obligation.predicate.def_id())
/// because these output type parameters should not affect the
/// selection of the impl. Therefore, if there is a mismatch, we
/// report an error to the user.
+ #[instrument(skip(self), level = "trace")]
fn confirm_poly_trait_refs(
&mut self,
obligation_cause: ObligationCause<'tcx>,
/// Evaluates the predicates in `predicates` recursively. Note that
/// this applies projections in the predicates, and therefore
/// is run within an inference probe.
+ #[instrument(skip(self, stack), level = "debug")]
fn evaluate_predicates_recursively<'o, I>(
&mut self,
stack: TraitObligationStackList<'o, 'tcx>,
I: IntoIterator<Item = PredicateObligation<'tcx>> + std::fmt::Debug,
{
let mut result = EvaluatedToOk;
- debug!(?predicates, "evaluate_predicates_recursively");
for obligation in predicates {
let eval = self.evaluate_predicate_recursively(stack, obligation.clone())?;
if let EvaluatedToErr = eval {
result
}
+ #[instrument(skip(self, previous_stack), level = "debug")]
fn evaluate_trait_predicate_recursively<'o>(
&mut self,
previous_stack: TraitObligationStackList<'o, 'tcx>,
mut obligation: TraitObligation<'tcx>,
) -> Result<EvaluationResult, OverflowError> {
- debug!(?obligation, "evaluate_trait_predicate_recursively");
-
if !self.intercrate
&& obligation.is_global(self.tcx())
&& obligation
// If a param env has no global bounds, global obligations do not
// depend on its particular value in order to work, so we can clear
// out the param env and get better caching.
- debug!("evaluate_trait_predicate_recursively - in global");
+ debug!("in global");
obligation.param_env = obligation.param_env.without_caller_bounds();
}
} else {
debug!(?result, "PROVISIONAL");
debug!(
- "evaluate_trait_predicate_recursively: caching provisionally because {:?} \
+ "caching provisionally because {:?} \
is a cycle participant (at depth {}, reached depth {})",
fresh_trait_ref, stack.depth, reached_depth,
);
/// Returns `Ok` if `poly_trait_ref` being true implies that the
/// obligation is satisfied.
+ #[instrument(skip(self), level = "debug")]
fn match_poly_trait_ref(
&mut self,
obligation: &TraitObligation<'tcx>,
poly_trait_ref: ty::PolyTraitRef<'tcx>,
) -> Result<Vec<PredicateObligation<'tcx>>, ()> {
- debug!(?obligation, ?poly_trait_ref, "match_poly_trait_ref");
-
self.infcx
.at(&obligation.cause, obligation.param_env)
.sup(obligation.predicate.to_poly_trait_ref(), poly_trait_ref)
}
}
+ #[instrument(skip(self), level = "debug")]
fn closure_trait_ref_unnormalized(
&mut self,
obligation: &TraitObligation<'tcx>,
substs: SubstsRef<'tcx>,
) -> ty::PolyTraitRef<'tcx> {
- debug!(?obligation, ?substs, "closure_trait_ref_unnormalized");
let closure_sig = substs.as_closure().sig();
debug!(?closure_sig);
/// Checks that the correct number of generic arguments have been provided.
/// This is used both for datatypes and function calls.
+ #[instrument(skip(tcx, gen_pos), level = "debug")]
pub(crate) fn check_generic_arg_count(
tcx: TyCtxt<'_>,
span: Span,
has_self: bool,
infer_args: bool,
) -> GenericArgCountResult {
- debug!(
- "check_generic_arg_count(span: {:?}, def_id: {:?}, seg: {:?}, gen_params: {:?}, gen_args: {:?})",
- span, def_id, seg, gen_params, gen_args
- );
-
let default_counts = gen_params.own_defaults();
let param_counts = gen_params.own_counts();
let mut check_types_and_consts =
|expected_min, expected_max, provided, params_offset, args_offset| {
debug!(
- "check_types_and_consts(expected_min: {:?}, expected_max: {:?}, \
- provided: {:?}, params_offset: {:?}, args_offset: {:?}",
- expected_min, expected_max, provided, params_offset, args_offset
+ ?expected_min,
+ ?expected_max,
+ ?provided,
+ ?params_offset,
+ ?args_offset,
+ "check_types_and_consts"
);
if (expected_min..=expected_max).contains(&provided) {
return true;
}
};
- debug!("gen_args_info: {:?}", gen_args_info);
+ debug!(?gen_args_info);
WrongNumberOfGenericArgs::new(
tcx,
- default_counts.types
- default_counts.consts
};
- debug!("expected_min: {:?}", expected_min);
- debug!("arg_counts.lifetimes: {:?}", gen_args.num_lifetime_params());
+ debug!(?expected_min);
+ debug!(arg_counts.lifetimes=?gen_args.num_lifetime_params());
check_types_and_consts(
expected_min,
}
}
+ let callee_ty = self.resolve_vars_if_possible(callee_ty);
let mut err = type_error_struct!(
self.tcx.sess,
callee_expr.span,
///
/// * ...
/// * inherited: other fields inherited from the enclosing fn (if any)
+#[instrument(skip(inherited, body), level = "debug")]
pub(super) fn check_fn<'a, 'tcx>(
inherited: &'a Inherited<'a, 'tcx>,
param_env: ty::ParamEnv<'tcx>,
) -> (FnCtxt<'a, 'tcx>, Option<GeneratorTypes<'tcx>>) {
let mut fn_sig = fn_sig;
- debug!("check_fn(sig={:?}, fn_id={}, param_env={:?})", fn_sig, fn_id, param_env);
-
// Create the function context. This is either derived from scratch or,
// in the case of closures, based on the outer context.
let mut fcx = FnCtxt::new(inherited, param_env, body.value.hir_id);
}
impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
+ #[instrument(skip(self, expr, _capture, decl, body_id), level = "debug")]
pub fn check_expr_closure(
&self,
expr: &hir::Expr<'_>,
gen: Option<hir::Movability>,
expected: Expectation<'tcx>,
) -> Ty<'tcx> {
- debug!("check_expr_closure(expr={:?},expected={:?})", expr, expected);
+ trace!("decl = {:#?}", decl);
+ trace!("expr = {:#?}", expr);
// It's always helpful for inference if we know the kind of
// closure sooner rather than later, so first examine the expected
self.check_closure(expr, expected_kind, decl, body, gen, expected_sig)
}
+ #[instrument(skip(self, expr, body, decl), level = "debug")]
fn check_closure(
&self,
expr: &hir::Expr<'_>,
gen: Option<hir::Movability>,
expected_sig: Option<ExpectedSig<'tcx>>,
) -> Ty<'tcx> {
- debug!("check_closure(opt_kind={:?}, expected_sig={:?})", opt_kind, expected_sig);
-
+ trace!("decl = {:#?}", decl);
let expr_def_id = self.tcx.hir().local_def_id(expr.hir_id);
+ debug!(?expr_def_id);
let ClosureSignatures { bound_sig, liberated_sig } =
self.sig_of_closure(expr.hir_id, expr_def_id.to_def_id(), decl, body, expected_sig);
- debug!("check_closure: ty_of_closure returns {:?}", liberated_sig);
+ debug!(?bound_sig, ?liberated_sig);
let return_type_pre_known = !liberated_sig.output().is_ty_infer();
)
});
- debug!(
- "check_closure: expr_def_id={:?}, sig={:?}, opt_kind={:?}",
- expr_def_id, sig, opt_kind
- );
+ debug!(?sig, ?opt_kind);
let closure_kind_ty = match opt_kind {
Some(kind) => kind.to_ty(self.tcx),
let closure_type = self.tcx.mk_closure(expr_def_id.to_def_id(), closure_substs.substs);
- debug!("check_closure: expr.hir_id={:?} closure_type={:?}", expr.hir_id, closure_type);
+ debug!(?expr.hir_id, ?closure_type);
closure_type
}
/// Given the expected type, figures out what it can about this closure we
/// are about to type check:
+ #[instrument(skip(self), level = "debug")]
fn deduce_expectations_from_expected_type(
&self,
expected_ty: Ty<'tcx>,
) -> (Option<ExpectedSig<'tcx>>, Option<ty::ClosureKind>) {
- debug!("deduce_expectations_from_expected_type(expected_ty={:?})", expected_ty);
-
match *expected_ty.kind() {
ty::Dynamic(ref object_type, ..) => {
let sig = object_type.projection_bounds().find_map(|pb| {
/// If there is no expected signature, then we will convert the
/// types that the user gave into a signature.
+ #[instrument(skip(self, hir_id, expr_def_id, decl, body), level = "debug")]
fn sig_of_closure_no_expectation(
&self,
hir_id: hir::HirId,
decl: &hir::FnDecl<'_>,
body: &hir::Body<'_>,
) -> ClosureSignatures<'tcx> {
- debug!("sig_of_closure_no_expectation()");
-
let bound_sig = self.supplied_sig_of_closure(hir_id, expr_def_id, decl, body);
self.closure_sigs(expr_def_id, body, bound_sig)
/// - `expected_sig`: the expected signature (if any). Note that
/// this is missing a binder: that is, there may be late-bound
/// regions with depth 1, which are bound then by the closure.
+ #[instrument(skip(self, hir_id, expr_def_id, decl, body), level = "debug")]
fn sig_of_closure_with_expectation(
&self,
hir_id: hir::HirId,
body: &hir::Body<'_>,
expected_sig: ExpectedSig<'tcx>,
) -> ClosureSignatures<'tcx> {
- debug!("sig_of_closure_with_expectation(expected_sig={:?})", expected_sig);
-
// Watch out for some surprises and just ignore the
// expectation if things don't see to match up with what we
// expect.
/// types that the user gave into a signature.
///
/// Also, record this closure signature for later.
+ #[instrument(skip(self, decl, body), level = "debug")]
fn supplied_sig_of_closure(
&self,
hir_id: hir::HirId,
) -> ty::PolyFnSig<'tcx> {
let astconv: &dyn AstConv<'_> = self;
- debug!(
- "supplied_sig_of_closure(decl={:?}, body.generator_kind={:?})",
- decl, body.generator_kind,
- );
+ trace!("decl = {:#?}", decl);
+ debug!(?body.generator_kind);
let bound_vars = self.tcx.late_bound_vars(hir_id);
// we expect the return type of the block to match that of the enclosing
// function.
Some(hir::GeneratorKind::Async(hir::AsyncGeneratorKind::Fn)) => {
- debug!("supplied_sig_of_closure: closure is async fn body");
+ debug!("closure is async fn body");
self.deduce_future_output_from_obligations(expr_def_id).unwrap_or_else(|| {
// AFAIK, deducing the future output
// always succeeds *except* in error cases
bound_vars,
);
- debug!("supplied_sig_of_closure: result={:?}", result);
+ debug!(?result);
let c_result = self.inh.infcx.canonicalize_response(result);
self.typeck_results.borrow_mut().user_provided_sigs.insert(expr_def_id, c_result);
// &[T; n] or &mut [T; n] -> &[T]
// or &mut [T; n] -> &mut [T]
// or &Concrete -> &Trait, etc.
+ #[instrument(skip(self), level = "debug")]
fn coerce_unsized(&self, mut source: Ty<'tcx>, mut target: Ty<'tcx>) -> CoerceResult<'tcx> {
- debug!("coerce_unsized(source={:?}, target={:?})", source, target);
-
source = self.shallow_resolve(source);
target = self.shallow_resolve(target);
- debug!("coerce_unsized: resolved source={:?} target={:?}", source, target);
+ debug!(?source, ?target);
// These 'if' statements require some explanation.
// The `CoerceUnsized` trait is special - it is only
self.demand_suptype_with_origin(&self.misc(sp), expected, actual)
}
+ #[instrument(skip(self), level = "debug")]
pub fn demand_suptype_with_origin(
&self,
cause: &ObligationCause<'tcx>,
/// Note that inspecting a type's structure *directly* may expose the fact
/// that there are actually multiple representations for `Error`, so avoid
/// that when err needs to be handled differently.
- #[instrument(skip(self), level = "debug")]
+ #[instrument(skip(self, expr), level = "debug")]
pub(super) fn check_expr_with_expectation(
&self,
expr: &'tcx hir::Expr<'tcx>,
ty
}
+ #[instrument(skip(self, expr), level = "debug")]
fn check_expr_kind(
&self,
expr: &'tcx hir::Expr<'tcx>,
expected: Expectation<'tcx>,
) -> Ty<'tcx> {
- debug!("check_expr_kind(expected={:?}, expr={:?})", expected, expr);
+ trace!("expr={:#?}", expr);
let tcx = self.tcx;
match expr.kind {
idx_t
} else {
let base_t = self.structurally_resolved_type(base.span, base_t);
- match self.lookup_indexing(expr, base, base_t, idx_t) {
+ match self.lookup_indexing(expr, base, base_t, idx, idx_t) {
Some((index_ty, element_ty)) => {
// two-phase not needed because index_ty is never mutable
self.demand_coerce(idx, idx_t, index_ty, None, AllowTwoPhase::No);
self.resolve_vars_with_obligations_and_mutate_fulfillment(ty, |_| {})
}
+ #[instrument(skip(self, mutate_fulfillment_errors), level = "debug")]
pub(in super::super) fn resolve_vars_with_obligations_and_mutate_fulfillment(
&self,
mut ty: Ty<'tcx>,
mutate_fulfillment_errors: impl Fn(&mut Vec<traits::FulfillmentError<'tcx>>),
) -> Ty<'tcx> {
- debug!("resolve_vars_with_obligations(ty={:?})", ty);
-
// No Infer()? Nothing needs doing.
if !ty.has_infer_types_or_consts() {
- debug!("resolve_vars_with_obligations: ty={:?}", ty);
+ debug!("no inference var, nothing needs doing");
return ty;
}
// If `ty` is a type variable, see whether we already know what it is.
ty = self.resolve_vars_if_possible(ty);
if !ty.has_infer_types_or_consts() {
- debug!("resolve_vars_with_obligations: ty={:?}", ty);
+ debug!(?ty);
return ty;
}
self.select_obligations_where_possible(false, mutate_fulfillment_errors);
ty = self.resolve_vars_if_possible(ty);
- debug!("resolve_vars_with_obligations: ty={:?}", ty);
+ debug!(?ty);
ty
}
/// This should be invoked **before any unifications have
/// occurred**, so that annotations like `Vec<_>` are preserved
/// properly.
+ #[instrument(skip(self), level = "debug")]
pub fn write_user_type_annotation_from_substs(
&self,
hir_id: hir::HirId,
substs: SubstsRef<'tcx>,
user_self_ty: Option<UserSelfTy<'tcx>>,
) {
- debug!(
- "write_user_type_annotation_from_substs: hir_id={:?} def_id={:?} substs={:?} \
- user_self_ty={:?} in fcx {}",
- hir_id,
- def_id,
- substs,
- user_self_ty,
- self.tag(),
- );
+ debug!("fcx {}", self.tag());
if self.can_contain_user_lifetime_bounds((substs, user_self_ty)) {
let canonicalized = self.infcx.canonicalize_user_type_annotation(UserType::TypeOf(
def_id,
UserSubsts { substs, user_self_ty },
));
- debug!("write_user_type_annotation_from_substs: canonicalized={:?}", canonicalized);
+ debug!(?canonicalized);
self.write_user_type_annotation(hir_id, canonicalized);
}
}
+ #[instrument(skip(self), level = "debug")]
pub fn write_user_type_annotation(
&self,
hir_id: hir::HirId,
canonical_user_type_annotation: CanonicalUserType<'tcx>,
) {
- debug!(
- "write_user_type_annotation: hir_id={:?} canonical_user_type_annotation={:?} tag={}",
- hir_id,
- canonical_user_type_annotation,
- self.tag(),
- );
+ debug!("fcx {}", self.tag());
if !canonical_user_type_annotation.is_identity() {
self.typeck_results
.user_provided_types_mut()
.insert(hir_id, canonical_user_type_annotation);
} else {
- debug!("write_user_type_annotation: skipping identity substs");
+ debug!("skipping identity substs");
}
}
+ #[instrument(skip(self, expr), level = "debug")]
pub fn apply_adjustments(&self, expr: &hir::Expr<'_>, adj: Vec<Adjustment<'tcx>>) {
- debug!("apply_adjustments(expr={:?}, adj={:?})", expr, adj);
+ debug!("expr = {:#?}", expr);
if adj.is_empty() {
return;
}
}
+ #[instrument(skip(self), level = "debug")]
pub(in super::super) fn select_all_obligations_or_error(&self) {
- debug!("select_all_obligations_or_error");
if let Err(errors) = self
.fulfillment_cx
.borrow_mut()
ret_ty.builtin_deref(true).unwrap()
}
+ #[instrument(skip(self), level = "debug")]
fn self_type_matches_expected_vid(
&self,
trait_ref: ty::PolyTraitRef<'tcx>,
expected_vid: ty::TyVid,
) -> bool {
let self_ty = self.shallow_resolve(trait_ref.skip_binder().self_ty());
- debug!(
- "self_type_matches_expected_vid(trait_ref={:?}, self_ty={:?}, expected_vid={:?})",
- trait_ref, self_ty, expected_vid
- );
+ debug!(?self_ty);
+
match *self_ty.kind() {
ty::Infer(ty::TyVar(found_vid)) => {
// FIXME: consider using `sub_root_var` here so we
}
}
+ #[instrument(skip(self), level = "debug")]
pub(in super::super) fn obligations_for_self_ty<'b>(
&'b self,
self_ty: ty::TyVid,
// FIXME: consider using `sub_root_var` here so we
// can see through subtyping.
let ty_var_root = self.root_var(self_ty);
- debug!(
- "obligations_for_self_ty: self_ty={:?} ty_var_root={:?} pending_obligations={:?}",
- self_ty,
- ty_var_root,
- self.fulfillment_cx.borrow().pending_obligations()
- );
+ trace!("pending_obligations = {:#?}", self.fulfillment_cx.borrow().pending_obligations());
self.fulfillment_cx
.borrow()
/// Unifies the output type with the expected type early, for more coercions
/// and forward type information on the input expressions.
+ #[instrument(skip(self, call_span), level = "debug")]
pub(in super::super) fn expected_inputs_for_expected_output(
&self,
call_span: Span,
Ok(formal_args.iter().map(|&ty| self.resolve_vars_if_possible(ty)).collect())
})
.unwrap_or_default();
- debug!(
- "expected_inputs_for_expected_output(formal={:?} -> {:?}, expected={:?} -> {:?})",
- formal_args, formal_ret, expect_args, expected_ret
- );
+ debug!(?formal_args, ?formal_ret, ?expect_args, ?expected_ret);
expect_args
}
// Instantiates the given path, which must refer to an item with the given
// number of type parameters and type.
+ #[instrument(skip(self, span), level = "debug")]
pub fn instantiate_value_path(
&self,
segments: &[hir::PathSegment<'_>],
span: Span,
hir_id: hir::HirId,
) -> (Ty<'tcx>, Res) {
- debug!(
- "instantiate_value_path(segments={:?}, self_ty={:?}, res={:?}, hir_id={})",
- segments, self_ty, res, hir_id,
- );
-
let tcx = self.tcx;
let path_segs = match res {
}
Res::Def(DefKind::AssocFn | DefKind::AssocConst, def_id) => {
let container = tcx.associated_item(def_id).container;
- debug!("instantiate_value_path: def_id={:?} container={:?}", def_id, container);
+ debug!(?def_id, ?container);
match container {
ty::TraitContainer(trait_did) => {
callee::check_legal_trait_for_method_call(tcx, span, None, span, trait_did)
sugg_span,
idx,
self.tcx.sess.source_map(),
+ item.fn_has_self_parameter,
);
}
}
sugg_span,
idx,
self.tcx.sess.source_map(),
+ item.fn_has_self_parameter,
);
}
}
span: Span,
candidate: Option<usize>,
source_map: &source_map::SourceMap,
+ fn_has_self_parameter: bool,
) {
let mut applicability = Applicability::MachineApplicable;
let (span, sugg) = if let (ty::AssocKind::Fn, Some(args)) = (kind, args) {
.collect::<Vec<_>>()
.join(", "),
);
+ let trait_name = if !fn_has_self_parameter {
+ format!("<{} as {}>", rcvr_ty, trait_name)
+ } else {
+ trait_name
+ };
(span, format!("{}::{}{}", trait_name, item_name, args))
} else {
- (span.with_hi(item_name.span.lo()), format!("{}::", trait_name))
+ (span.with_hi(item_name.span.lo()), format!("<{} as {}>::", rcvr_ty, trait_name))
};
err.span_suggestion_verbose(
span,
use crate::check::method::MethodCallee;
use crate::check::{has_expected_num_generic_args, FnCtxt, PlaceOp};
+use rustc_ast as ast;
+use rustc_errors::Applicability;
use rustc_hir as hir;
use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
use rustc_infer::infer::InferOk;
expr: &hir::Expr<'_>,
base_expr: &'tcx hir::Expr<'tcx>,
base_ty: Ty<'tcx>,
+ index_expr: &'tcx hir::Expr<'tcx>,
idx_ty: Ty<'tcx>,
) -> Option<(/*index type*/ Ty<'tcx>, /*element type*/ Ty<'tcx>)> {
// FIXME(#18741) -- this is almost but not quite the same as the
let mut autoderef = self.autoderef(base_expr.span, base_ty);
let mut result = None;
while result.is_none() && autoderef.next().is_some() {
- result = self.try_index_step(expr, base_expr, &autoderef, idx_ty);
+ result = self.try_index_step(expr, base_expr, &autoderef, idx_ty, index_expr);
}
self.register_predicates(autoderef.into_obligations());
result
}
+ fn negative_index(
+ &self,
+ ty: Ty<'tcx>,
+ span: Span,
+ base_expr: &hir::Expr<'_>,
+ ) -> Option<(Ty<'tcx>, Ty<'tcx>)> {
+ let ty = self.resolve_vars_if_possible(ty);
+ let mut err = self.tcx.sess.struct_span_err(
+ span,
+ &format!("negative integers cannot be used to index on a `{}`", ty),
+ );
+ err.span_label(span, &format!("cannot use a negative integer for indexing on `{}`", ty));
+ if let (hir::ExprKind::Path(..), Ok(snippet)) =
+ (&base_expr.kind, self.tcx.sess.source_map().span_to_snippet(base_expr.span))
+ {
+ // `foo[-1]` to `foo[foo.len() - 1]`
+ err.span_suggestion_verbose(
+ span.shrink_to_lo(),
+ &format!(
+ "to access an element starting from the end of the `{}`, compute the index",
+ ty,
+ ),
+ format!("{}.len() ", snippet),
+ Applicability::MachineApplicable,
+ );
+ }
+ err.emit();
+ Some((self.tcx.ty_error(), self.tcx.ty_error()))
+ }
+
/// To type-check `base_expr[index_expr]`, we progressively autoderef
/// (and otherwise adjust) `base_expr`, looking for a type which either
/// supports builtin indexing or overloaded indexing.
base_expr: &hir::Expr<'_>,
autoderef: &Autoderef<'a, 'tcx>,
index_ty: Ty<'tcx>,
+ index_expr: &hir::Expr<'_>,
) -> Option<(/*index type*/ Ty<'tcx>, /*element type*/ Ty<'tcx>)> {
let adjusted_ty =
self.structurally_resolved_type(autoderef.span(), autoderef.final_ty(false));
expr, base_expr, adjusted_ty, index_ty
);
+ if let hir::ExprKind::Unary(
+ hir::UnOp::Neg,
+ hir::Expr {
+ kind: hir::ExprKind::Lit(hir::Lit { node: ast::LitKind::Int(..), .. }),
+ ..
+ },
+ ) = index_expr.kind
+ {
+ match adjusted_ty.kind() {
+ ty::Adt(ty::AdtDef { did, .. }, _)
+ if self.tcx.is_diagnostic_item(sym::vec_type, *did) =>
+ {
+ return self.negative_index(adjusted_ty, index_expr.span, base_expr);
+ }
+ ty::Slice(_) | ty::Array(_, _) => {
+ return self.negative_index(adjusted_ty, index_expr.span, base_expr);
+ }
+ _ => {}
+ }
+ }
+
for unsize in [false, true] {
let mut self_ty = adjusted_ty;
if unsize {
impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
/// Analysis starting point.
+ #[instrument(skip(self, body), level = "debug")]
fn analyze_closure(
&self,
closure_hir_id: hir::HirId,
body: &'tcx hir::Body<'tcx>,
capture_clause: hir::CaptureBy,
) {
- debug!("analyze_closure(id={:?}, body.id={:?})", closure_hir_id, body.id());
-
// Extract the type of the closure.
let ty = self.node_ty(closure_hir_id);
let (closure_def_id, substs) = match *ty.kind() {
}
impl<'a, 'tcx> InferBorrowKind<'a, 'tcx> {
+ #[instrument(skip(self), level = "debug")]
fn adjust_upvar_borrow_kind_for_consume(
&mut self,
place_with_id: &PlaceWithHirId<'tcx>,
diag_expr_id: hir::HirId,
) {
- debug!(
- "adjust_upvar_borrow_kind_for_consume(place_with_id={:?}, diag_expr_id={:?})",
- place_with_id, diag_expr_id
- );
let tcx = self.fcx.tcx;
let upvar_id = if let PlaceBase::Upvar(upvar_id) = place_with_id.place.base {
upvar_id
return;
};
- debug!("adjust_upvar_borrow_kind_for_consume: upvar={:?}", upvar_id);
+ debug!(?upvar_id);
let usage_span = tcx.hir().span(diag_expr_id);
/// Indicates that `place_with_id` is being directly mutated (e.g., assigned
/// to). If the place is based on a by-ref upvar, this implies that
/// the upvar must be borrowed using an `&mut` borrow.
+ #[instrument(skip(self), level = "debug")]
fn adjust_upvar_borrow_kind_for_mut(
&mut self,
place_with_id: &PlaceWithHirId<'tcx>,
diag_expr_id: hir::HirId,
) {
- debug!(
- "adjust_upvar_borrow_kind_for_mut(place_with_id={:?}, diag_expr_id={:?})",
- place_with_id, diag_expr_id
- );
-
if let PlaceBase::Upvar(_) = place_with_id.place.base {
// Raw pointers don't inherit mutability
if place_with_id.place.deref_tys().any(ty::TyS::is_unsafe_ptr) {
}
}
+ #[instrument(skip(self), level = "debug")]
fn adjust_upvar_borrow_kind_for_unique(
&mut self,
place_with_id: &PlaceWithHirId<'tcx>,
diag_expr_id: hir::HirId,
) {
- debug!(
- "adjust_upvar_borrow_kind_for_unique(place_with_id={:?}, diag_expr_id={:?})",
- place_with_id, diag_expr_id
- );
-
if let PlaceBase::Upvar(_) = place_with_id.place.base {
if place_with_id.place.deref_tys().any(ty::TyS::is_unsafe_ptr) {
// Raw pointers don't inherit mutability.
/// moving from left to right as needed (but never right to left).
/// Here the argument `mutbl` is the borrow_kind that is required by
/// some particular use.
+ #[instrument(skip(self), level = "debug")]
fn adjust_upvar_borrow_kind(
&mut self,
place_with_id: &PlaceWithHirId<'tcx>,
) {
let curr_capture_info = self.capture_information[&place_with_id.place];
- debug!(
- "adjust_upvar_borrow_kind(place={:?}, diag_expr_id={:?}, capture_info={:?}, kind={:?})",
- place_with_id, diag_expr_id, curr_capture_info, kind
- );
+ debug!(?curr_capture_info);
if let ty::UpvarCapture::ByValue(_) = curr_capture_info.capture_kind {
// It's already captured by value, we don't need to do anything here
};
}
+ #[instrument(skip(self, diag_expr_id), level = "debug")]
fn init_capture_info_for_place(
&mut self,
place_with_id: &PlaceWithHirId<'tcx>,
self.capture_information.insert(place_with_id.place.clone(), capture_info);
} else {
- debug!("Not upvar: {:?}", place_with_id);
+ debug!("Not upvar");
}
}
}
}
}
+ #[instrument(skip(self), level = "debug")]
fn consume(&mut self, place_with_id: &PlaceWithHirId<'tcx>, diag_expr_id: hir::HirId) {
- debug!("consume(place_with_id={:?}, diag_expr_id={:?})", place_with_id, diag_expr_id);
-
if !self.capture_information.contains_key(&place_with_id.place) {
self.init_capture_info_for_place(&place_with_id, diag_expr_id);
}
self.adjust_upvar_borrow_kind_for_consume(&place_with_id, diag_expr_id);
}
+ #[instrument(skip(self), level = "debug")]
fn borrow(
&mut self,
place_with_id: &PlaceWithHirId<'tcx>,
diag_expr_id: hir::HirId,
bk: ty::BorrowKind,
) {
- debug!(
- "borrow(place_with_id={:?}, diag_expr_id={:?}, bk={:?})",
- place_with_id, diag_expr_id, bk
- );
-
// The region here will get discarded/ignored
let dummy_capture_kind =
ty::UpvarCapture::ByRef(ty::UpvarBorrow { kind: bk, region: &ty::ReErased });
}
}
+ #[instrument(skip(self), level = "debug")]
fn mutate(&mut self, assignee_place: &PlaceWithHirId<'tcx>, diag_expr_id: hir::HirId) {
- debug!("mutate(assignee_place={:?}, diag_expr_id={:?})", assignee_place, diag_expr_id);
-
self.borrow(assignee_place, diag_expr_id, ty::BorrowKind::MutBorrow);
}
}
fcx_typeck_results.generator_interior_types.clone();
}
+ #[instrument(skip(self, span), level = "debug")]
fn visit_opaque_types(&mut self, span: Span) {
let opaque_types = self.fcx.infcx.inner.borrow().opaque_types.clone();
for (opaque_type_key, opaque_defn) in opaque_types {
}
}
+ #[instrument(skip(self, span), level = "debug")]
fn visit_node_id(&mut self, span: Span, hir_id: hir::HirId) {
// Export associated path extensions and method resolutions.
if let Some(def) =
let n_ty = self.fcx.node_ty(hir_id);
let n_ty = self.resolve(n_ty, &span);
self.write_ty_to_typeck_results(hir_id, n_ty);
- debug!("node {:?} has type {:?}", hir_id, n_ty);
+ debug!(?n_ty);
// Resolve any substitutions
if let Some(substs) = self.fcx.typeck_results.borrow().node_substs_opt(hir_id) {
}
}
+ #[instrument(skip(self, span), level = "debug")]
fn visit_adjustments(&mut self, span: Span, hir_id: hir::HirId) {
let adjustment = self.fcx.typeck_results.borrow_mut().adjustments_mut().remove(hir_id);
match adjustment {
None => {
- debug!("no adjustments for node {:?}", hir_id);
+ debug!("no adjustments for node");
}
Some(adjustment) => {
let resolved_adjustment = self.resolve(adjustment, &span);
- debug!("adjustments for node {:?}: {:?}", hir_id, resolved_adjustment);
+ debug!(?resolved_adjustment);
self.typeck_results.adjustments_mut().insert(hir_id, resolved_adjustment);
}
}
}
+ #[instrument(skip(self, span), level = "debug")]
fn visit_pat_adjustments(&mut self, span: Span, hir_id: hir::HirId) {
let adjustment = self.fcx.typeck_results.borrow_mut().pat_adjustments_mut().remove(hir_id);
match adjustment {
None => {
- debug!("no pat_adjustments for node {:?}", hir_id);
+ debug!("no pat_adjustments for node");
}
Some(adjustment) => {
let resolved_adjustment = self.resolve(adjustment, &span);
- debug!("pat_adjustments for node {:?}: {:?}", hir_id, resolved_adjustment);
+ debug!(?resolved_adjustment);
self.typeck_results.pat_adjustments_mut().insert(hir_id, resolved_adjustment);
}
}
}
}
+struct EraseEarlyRegions<'tcx> {
+ tcx: TyCtxt<'tcx>,
+}
+
+impl<'tcx> TypeFolder<'tcx> for EraseEarlyRegions<'tcx> {
+ fn tcx<'b>(&'b self) -> TyCtxt<'tcx> {
+ self.tcx
+ }
+ fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> {
+ if ty.has_type_flags(ty::TypeFlags::HAS_POTENTIAL_FREE_REGIONS) {
+ ty.super_fold_with(self)
+ } else {
+ ty
+ }
+ }
+ fn fold_region(&mut self, r: ty::Region<'tcx>) -> ty::Region<'tcx> {
+ if let ty::ReLateBound(..) = r { r } else { self.tcx.lifetimes.re_erased }
+ }
+}
+
impl<'cx, 'tcx> TypeFolder<'tcx> for Resolver<'cx, 'tcx> {
fn tcx<'a>(&'a self) -> TyCtxt<'tcx> {
self.tcx
fn fold_ty(&mut self, t: Ty<'tcx>) -> Ty<'tcx> {
match self.infcx.fully_resolve(t) {
- Ok(t) => self.infcx.tcx.erase_regions(t),
+ Ok(t) => {
+ // Do not anonymize late-bound regions
+ // (e.g. keep `for<'a>` named `for<'a>`).
+ // This allows NLL to generate error messages that
+ // refer to the higher-ranked lifetime names written by the user.
+ EraseEarlyRegions { tcx: self.infcx.tcx }.fold_ty(t)
+ }
Err(_) => {
debug!("Resolver::fold_ty: input type `{:?}` not fully resolvable", t);
self.report_type_error(t);
if let Some(hir::Guard::If(ref e)) = arm.guard {
self.consume_expr(e)
+ } else if let Some(hir::Guard::IfLet(_, ref e)) = arm.guard {
+ self.consume_expr(e)
}
self.consume_expr(&arm.body);
/// Builds the `type defined here` message.
fn show_definition(&self, err: &mut DiagnosticBuilder<'_>) {
let mut spans: MultiSpan = if let Some(def_span) = self.tcx.def_ident_span(self.def_id) {
- def_span.into()
+ if self.tcx.sess.source_map().span_to_snippet(def_span).is_ok() {
+ def_span.into()
+ } else {
+ return;
+ }
} else {
return;
};
///
/// See the [module-level documentation](self) for more.
#[stable(feature = "rust1", since = "1.0.0")]
+#[cfg_attr(
+ not(bootstrap),
+ must_not_suspend = "Holding a Ref across suspend \
+ points can cause BorrowErrors"
+)]
pub struct Ref<'b, T: ?Sized + 'b> {
value: &'b T,
borrow: BorrowRef<'b>,
///
/// See the [module-level documentation](self) for more.
#[stable(feature = "rust1", since = "1.0.0")]
+#[cfg_attr(
+ not(bootstrap),
+ must_not_suspend = "Holding a RefMut across suspend \
+ points can cause BorrowErrors"
+)]
pub struct RefMut<'b, T: ?Sized + 'b> {
value: &'b mut T,
borrow: BorrowRefMut<'b>,
// From implies Into
#[stable(feature = "rust1", since = "1.0.0")]
-impl<T, U> Into<U> for T
+#[rustc_const_unstable(feature = "const_convert", issue = "88674")]
+impl<T, U> const Into<U> for T
where
- U: From<T>,
+ U: ~const From<T>,
{
fn into(self) -> U {
U::from(self)
// From (and thus Into) is reflexive
#[stable(feature = "rust1", since = "1.0.0")]
-impl<T> From<T> for T {
+#[rustc_const_unstable(feature = "const_convert", issue = "88674")]
+impl<T> const From<T> for T {
fn from(t: T) -> T {
t
}
self.next_back()
}
+ #[inline]
+ fn is_sorted(self) -> bool {
+ true
+ }
+
#[inline]
#[doc(hidden)]
unsafe fn __iterator_get_unchecked(&mut self, idx: usize) -> Self::Item
fn max(mut self) -> Option<A> {
self.next_back()
}
+
+ #[inline]
+ fn is_sorted(self) -> bool {
+ true
+ }
}
#[stable(feature = "inclusive_range", since = "1.26.0")]
#![feature(const_float_bits_conv)]
#![feature(const_float_classify)]
#![feature(const_heap)]
+#![feature(const_convert)]
#![feature(const_inherent_unchecked_arith)]
#![feature(const_int_unchecked_arith)]
#![feature(const_intrinsic_copy)]
#![feature(link_llvm_intrinsics)]
#![feature(llvm_asm)]
#![feature(min_specialization)]
+#![cfg_attr(not(bootstrap), feature(must_not_suspend))]
#![feature(negative_impls)]
#![feature(never_type)]
#![feature(no_core)]
}
#[unstable(feature = "try_trait_v2", issue = "84277")]
-impl<T> ops::Try for Option<T> {
+#[rustc_const_unstable(feature = "const_convert", issue = "88674")]
+impl<T> const ops::Try for Option<T> {
type Output = T;
type Residual = Option<convert::Infallible>;
}
#[unstable(feature = "try_trait_v2", issue = "84277")]
-impl<T> ops::FromResidual for Option<T> {
+#[rustc_const_unstable(feature = "const_convert", issue = "88674")]
+impl<T> const ops::FromResidual for Option<T> {
#[inline]
fn from_residual(residual: Option<convert::Infallible>) -> Self {
match residual {
}
#[unstable(feature = "try_trait_v2", issue = "84277")]
-impl<T, E> ops::Try for Result<T, E> {
+#[rustc_const_unstable(feature = "const_convert", issue = "88674")]
+impl<T, E> const ops::Try for Result<T, E> {
type Output = T;
type Residual = Result<convert::Infallible, E>;
}
#[unstable(feature = "try_trait_v2", issue = "84277")]
-impl<T, E, F: From<E>> ops::FromResidual<Result<convert::Infallible, E>> for Result<T, F> {
+#[rustc_const_unstable(feature = "const_convert", issue = "88674")]
+impl<T, E, F: ~const From<E>> const ops::FromResidual<Result<convert::Infallible, E>>
+ for Result<T, F>
+{
#[inline]
fn from_residual(residual: Result<convert::Infallible, E>) -> Self {
match residual {
//! Unstable sorting is compatible with libcore because it doesn't allocate memory, unlike our
//! stable sorting implementation.
-// ignore-tidy-undocumented-unsafe
-
use crate::cmp;
use crate::mem::{self, MaybeUninit};
use crate::ptr;
} else if start_r < end_r {
block_l = rem;
} else {
+ // There were the same number of elements to switch on both blocks during the last
+ // iteration, so there are no remaining elements on either block. Cover the remaining
+ // items with roughly equally-sized blocks.
block_l = rem / 2;
block_r = rem - block_l;
}
// Move its remaining out-of-order elements to the far right.
debug_assert_eq!(width(l, r), block_l);
while start_l < end_l {
+ // remaining-elements-safety
+ // SAFETY: while the loop condition holds there are still elements in `offsets_l`, so it
+ // is safe to point `end_l` to the previous element.
+ //
+ // The `ptr::swap` is safe if both its arguments are valid for reads and writes:
+ // - Per the debug assert above, the distance between `l` and `r` is `block_l`
+ // elements, so there can be at most `block_l` remaining offsets between `start_l`
+ // and `end_l`. This means `r` will be moved at most `block_l` steps back, which
+ // makes the `r.offset` calls valid (at that point `l == r`).
+ // - `offsets_l` contains valid offsets into `v` collected during the partitioning of
+ // the last block, so the `l.offset` calls are valid.
unsafe {
end_l = end_l.offset(-1);
ptr::swap(l.offset(*end_l as isize), r.offset(-1));
// Move its remaining out-of-order elements to the far left.
debug_assert_eq!(width(l, r), block_r);
while start_r < end_r {
+ // SAFETY: See the reasoning in [remaining-elements-safety].
unsafe {
end_r = end_r.offset(-1);
ptr::swap(l, r.offset(-(*end_r as isize) - 1));
// Read the pivot into a stack-allocated variable for efficiency. If a following comparison
// operation panics, the pivot will be automatically written back into the slice.
+
+ // SAFETY: `pivot` is a reference to the first element of `v`, so `ptr::read` is safe.
let mut tmp = mem::ManuallyDrop::new(unsafe { ptr::read(pivot) });
let _pivot_guard = CopyOnDrop { src: &mut *tmp, dest: pivot };
let pivot = &*tmp;
if len >= 8 {
// Swaps indices so that `v[a] <= v[b]`.
+ // SAFETY: `len >= 8` so there are at least two elements in the neighborhoods of
+ // `a`, `b` and `c`. This means the three calls to `sort_adjacent` result in
+ // corresponding calls to `sort3` with valid 3-item neighborhoods around each
+ // pointer, which in turn means the calls to `sort2` are done with valid
+ // references. Thus the `v.get_unchecked` calls are safe, as is the `ptr::swap`
+ // call.
let mut sort2 = |a: &mut usize, b: &mut usize| unsafe {
if is_less(v.get_unchecked(*b), v.get_unchecked(*a)) {
ptr::swap(a, b);
--- /dev/null
+#[test]
+fn convert() {
+ const fn from(x: i32) -> i32 {
+ i32::from(x)
+ }
+
+ const FOO: i32 = from(42);
+ assert_eq!(FOO, 42);
+
+ const fn into(x: Vec<String>) -> Vec<String> {
+ x.into()
+ }
+
+ const BAR: Vec<String> = into(Vec::new());
+ assert_eq!(BAR, Vec::<String>::new());
+}
#![feature(cfg_target_has_atomic)]
#![feature(const_assume)]
#![feature(const_cell_into_inner)]
+#![feature(const_convert)]
#![feature(const_maybe_uninit_assume_init)]
+#![feature(const_num_from_num)]
#![feature(const_ptr_read)]
#![feature(const_ptr_write)]
#![feature(const_ptr_offset)]
#![feature(const_trait_impl)]
-#![feature(const_num_from_num)]
#![feature(core_intrinsics)]
#![feature(core_private_bignum)]
#![feature(core_private_diy_float)]
mod clone;
mod cmp;
mod const_ptr;
+mod convert;
mod fmt;
mod hash;
mod intrinsics;
libc::abort();
}
} else if #[cfg(any(target_os = "hermit",
+ target_os = "solid_asp3",
all(target_vendor = "fortanix", target_env = "sgx")
))] {
unsafe fn abort() -> ! {
} else if #[cfg(any(
all(target_family = "windows", target_env = "gnu"),
target_os = "psp",
+ target_os = "solid_asp3",
all(target_family = "unix", not(target_os = "espidf")),
all(target_vendor = "fortanix", target_env = "sgx"),
))] {
|| target.contains("wasm32")
|| target.contains("asmjs")
|| target.contains("espidf")
+ || target.contains("solid")
{
// These platforms don't have any special requirements.
} else {
/// Creates a C-compatible string by consuming a byte vector,
/// without checking for interior 0 bytes.
///
+ /// Trailing 0 byte will be appended by this function.
+ ///
/// This method is equivalent to [`CString::new`] except that no runtime
/// assertion is made that `v` contains no 0 bytes, and it requires an
/// actual byte vector, not anything that can be converted to one with Into.
#![feature(maybe_uninit_slice)]
#![feature(maybe_uninit_uninit_array)]
#![feature(min_specialization)]
+#![cfg_attr(not(bootstrap), feature(must_not_suspend))]
#![feature(needs_panic_runtime)]
#![feature(negative_impls)]
#![feature(never_type)]
pub use alloc::task::*;
}
-// Platform-abstraction modules
+// The runtime entry point and a few unstable public functions used by the
+// compiler
#[macro_use]
-mod sys_common;
+pub mod rt;
+
+// Platform-abstraction modules
mod sys;
+mod sys_common;
pub mod alloc;
// Private support modules
mod panicking;
-// The runtime entry point and a few unstable public functions used by the
-// compiler
-pub mod rt;
-
#[path = "../../backtrace/src/lib.rs"]
#[allow(dead_code, unused_attributes)]
mod backtrace_rs;
#[cfg(target_os = "solaris")]
pub mod solaris;
+#[cfg(target_os = "solid_asp3")]
+pub mod solid;
#[cfg(target_os = "vxworks")]
pub mod vxworks;
--- /dev/null
+//! SOLID-specific extension to the primitives in the `std::ffi` module
+//!
+//! # Examples
+//!
+//! ```
+//! use std::ffi::OsString;
+//! use std::os::solid::ffi::OsStringExt;
+//!
+//! let bytes = b"foo".to_vec();
+//!
+//! // OsStringExt::from_vec
+//! let os_string = OsString::from_vec(bytes);
+//! assert_eq!(os_string.to_str(), Some("foo"));
+//!
+//! // OsStringExt::into_vec
+//! let bytes = os_string.into_vec();
+//! assert_eq!(bytes, b"foo");
+//! ```
+//!
+//! ```
+//! use std::ffi::OsStr;
+//! use std::os::solid::ffi::OsStrExt;
+//!
+//! let bytes = b"foo";
+//!
+//! // OsStrExt::from_bytes
+//! let os_str = OsStr::from_bytes(bytes);
+//! assert_eq!(os_str.to_str(), Some("foo"));
+//!
+//! // OsStrExt::as_bytes
+//! let bytes = os_str.as_bytes();
+//! assert_eq!(bytes, b"foo");
+//! ```
+
+#![stable(feature = "rust1", since = "1.0.0")]
+
+#[path = "../unix/ffi/os_str.rs"]
+mod os_str;
+
+#[stable(feature = "rust1", since = "1.0.0")]
+pub use self::os_str::{OsStrExt, OsStringExt};
--- /dev/null
+//! SOLID-specific extensions to general I/O primitives
+
+#![deny(unsafe_op_in_unsafe_fn)]
+#![unstable(feature = "solid_ext", issue = "none")]
+
+use crate::net;
+use crate::sys;
+use crate::sys_common::{self, AsInner, FromInner, IntoInner};
+
+/// Raw file descriptors.
+pub type RawFd = i32;
+
+/// A trait to extract the raw SOLID Sockets file descriptor from an underlying
+/// object.
+pub trait AsRawFd {
+ /// Extracts the raw file descriptor.
+ ///
+ /// This method does **not** pass ownership of the raw file descriptor
+ /// to the caller. The descriptor is only guaranteed to be valid while
+ /// the original object has not yet been destroyed.
+ fn as_raw_fd(&self) -> RawFd;
+}
+
+/// A trait to express the ability to construct an object from a raw file
+/// descriptor.
+pub trait FromRawFd {
+ /// Constructs a new instance of `Self` from the given raw file
+ /// descriptor.
+ ///
+ /// This function **consumes ownership** of the specified file
+ /// descriptor. The returned object will take responsibility for closing
+ /// it when the object goes out of scope.
+ ///
+ /// This function is also unsafe as the primitives currently returned
+ /// have the contract that they are the sole owner of the file
+ /// descriptor they are wrapping. Usage of this function could
+ /// accidentally allow violating this contract which can cause memory
+ /// unsafety in code that relies on it being true.
+ unsafe fn from_raw_fd(fd: RawFd) -> Self;
+}
+
+/// A trait to express the ability to consume an object and acquire ownership of
+/// its raw file descriptor.
+pub trait IntoRawFd {
+ /// Consumes this object, returning the raw underlying file descriptor.
+ ///
+ /// This function **transfers ownership** of the underlying file descriptor
+ /// to the caller. Callers are then the unique owners of the file descriptor
+ /// and must close the descriptor once it's no longer needed.
+ fn into_raw_fd(self) -> RawFd;
+}
+
+#[stable(feature = "raw_fd_reflexive_traits", since = "1.48.0")]
+impl AsRawFd for RawFd {
+ #[inline]
+ fn as_raw_fd(&self) -> RawFd {
+ *self
+ }
+}
+#[stable(feature = "raw_fd_reflexive_traits", since = "1.48.0")]
+impl IntoRawFd for RawFd {
+ #[inline]
+ fn into_raw_fd(self) -> RawFd {
+ self
+ }
+}
+#[stable(feature = "raw_fd_reflexive_traits", since = "1.48.0")]
+impl FromRawFd for RawFd {
+ #[inline]
+ unsafe fn from_raw_fd(fd: RawFd) -> RawFd {
+ fd
+ }
+}
+
+macro_rules! impl_as_raw_fd {
+ ($($t:ident)*) => {$(
+ #[stable(feature = "rust1", since = "1.0.0")]
+ impl AsRawFd for net::$t {
+ #[inline]
+ fn as_raw_fd(&self) -> RawFd {
+ *self.as_inner().socket().as_inner()
+ }
+ }
+ )*};
+}
+impl_as_raw_fd! { TcpStream TcpListener UdpSocket }
+
+macro_rules! impl_from_raw_fd {
+ ($($t:ident)*) => {$(
+ #[stable(feature = "from_raw_os", since = "1.1.0")]
+ impl FromRawFd for net::$t {
+ #[inline]
+ unsafe fn from_raw_fd(fd: RawFd) -> net::$t {
+ let socket = sys::net::Socket::from_inner(fd);
+ net::$t::from_inner(sys_common::net::$t::from_inner(socket))
+ }
+ }
+ )*};
+}
+impl_from_raw_fd! { TcpStream TcpListener UdpSocket }
+
+macro_rules! impl_into_raw_fd {
+ ($($t:ident)*) => {$(
+ #[stable(feature = "into_raw_os", since = "1.4.0")]
+ impl IntoRawFd for net::$t {
+ #[inline]
+ fn into_raw_fd(self) -> RawFd {
+ self.into_inner().into_socket().into_inner()
+ }
+ }
+ )*};
+}
+impl_into_raw_fd! { TcpStream TcpListener UdpSocket }
--- /dev/null
+#![stable(feature = "rust1", since = "1.0.0")]
+
+pub mod ffi;
+pub mod io;
+
+/// A prelude for conveniently writing platform-specific code.
+///
+/// Includes all extension traits, and some important type definitions.
+#[stable(feature = "rust1", since = "1.0.0")]
+pub mod prelude {
+ #[doc(no_inline)]
+ #[stable(feature = "rust1", since = "1.0.0")]
+ pub use super::ffi::{OsStrExt, OsStringExt};
+ #[doc(no_inline)]
+ #[stable(feature = "rust1", since = "1.0.0")]
+ pub use super::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd};
+}
/// [platform-specific behavior]: #platform-specific-behavior
#[stable(feature = "rust1", since = "1.0.0")]
pub fn exit(code: i32) -> ! {
- crate::sys_common::rt::cleanup();
+ crate::rt::cleanup();
crate::sys::os::exit(code)
}
issue = "none"
)]
#![doc(hidden)]
+#![deny(unsafe_op_in_unsafe_fn)]
+#![allow(unused_macros)]
+
+use crate::ffi::CString;
// Re-export some of our utilities which are expected by other crates.
pub use crate::panicking::{begin_panic, begin_panic_fmt, panic_count};
pub use core::panicking::panic_display;
+use crate::sync::Once;
+use crate::sys;
+use crate::sys_common::thread_info;
+use crate::thread::Thread;
+
+// Prints to the "panic output", depending on the platform this may be:
+// - the standard error output
+// - some dedicated platform specific output
+// - nothing (so this macro is a no-op)
+macro_rules! rtprintpanic {
+ ($($t:tt)*) => {
+ if let Some(mut out) = crate::sys::stdio::panic_output() {
+ let _ = crate::io::Write::write_fmt(&mut out, format_args!($($t)*));
+ }
+ }
+}
+
+macro_rules! rtabort {
+ ($($t:tt)*) => {
+ {
+ rtprintpanic!("fatal runtime error: {}\n", format_args!($($t)*));
+ crate::sys::abort_internal();
+ }
+ }
+}
+
+macro_rules! rtassert {
+ ($e:expr) => {
+ if !$e {
+ rtabort!(concat!("assertion failed: ", stringify!($e)));
+ }
+ };
+}
+
+macro_rules! rtunwrap {
+ ($ok:ident, $e:expr) => {
+ match $e {
+ $ok(v) => v,
+ ref err => {
+ let err = err.as_ref().map(drop); // map Ok/Some which might not be Debug
+ rtabort!(concat!("unwrap failed: ", stringify!($e), " = {:?}"), err)
+ }
+ }
+ };
+}
+
+// One-time runtime initialization.
+// Runs before `main`.
+// SAFETY: must be called only once during runtime initialization.
+// NOTE: this is not guaranteed to run, for example when Rust code is called externally.
+#[cfg_attr(test, allow(dead_code))]
+unsafe fn init(argc: isize, argv: *const *const u8) {
+ unsafe {
+ sys::init(argc, argv);
+
+ let main_guard = sys::thread::guard::init();
+ // Next, set up the current Thread with the guard information we just
+ // created. Note that this isn't necessary in general for new threads,
+ // but we just do this to name the main thread and to give it correct
+ // info about the stack bounds.
+ let thread = Thread::new(Some(rtunwrap!(Ok, CString::new("main"))));
+ thread_info::set(main_guard, thread);
+ }
+}
+
+// One-time runtime cleanup.
+// Runs after `main` or at program exit.
+// NOTE: this is not guaranteed to run, for example when the program aborts.
+pub(crate) fn cleanup() {
+ static CLEANUP: Once = Once::new();
+ CLEANUP.call_once(|| unsafe {
+ // Flush stdout and disable buffering.
+ crate::io::cleanup();
+ // SAFETY: Only called once during runtime cleanup.
+ sys::cleanup();
+ });
+}
+
// To reduce the generated code of the new `lang_start`, this function is doing
// the real work.
#[cfg(not(test))]
argc: isize,
argv: *const *const u8,
) -> Result<isize, !> {
- use crate::{mem, panic, sys, sys_common};
+ use crate::{mem, panic};
let rt_abort = move |e| {
mem::forget(e);
rtabort!("initialization or cleanup bug");
// prevent libstd from accidentally introducing a panic to these functions. Another is from
// user code from `main` or, more nefariously, as described in e.g. issue #86030.
// SAFETY: Only called once during runtime initialization.
- panic::catch_unwind(move || unsafe { sys_common::rt::init(argc, argv) }).map_err(rt_abort)?;
+ panic::catch_unwind(move || unsafe { init(argc, argv) }).map_err(rt_abort)?;
let ret_code = panic::catch_unwind(move || panic::catch_unwind(main).unwrap_or(101) as isize)
.map_err(move |e| {
mem::forget(e);
rtprintpanic!("drop of the panic payload panicked");
sys::abort_internal()
});
- panic::catch_unwind(sys_common::rt::cleanup).map_err(rt_abort)?;
+ panic::catch_unwind(cleanup).map_err(rt_abort)?;
ret_code
}
/// [`lock`]: Mutex::lock
/// [`try_lock`]: Mutex::try_lock
#[must_use = "if unused the Mutex will immediately unlock"]
+#[cfg_attr(
+ not(bootstrap),
+ must_not_suspend = "Holding a MutexGuard across suspend \
+ points can cause deadlocks, delays, \
+ and cause Futures to not implement `Send`"
+)]
#[stable(feature = "rust1", since = "1.0.0")]
pub struct MutexGuard<'a, T: ?Sized + 'a> {
lock: &'a Mutex<T>,
/// [`read`]: RwLock::read
/// [`try_read`]: RwLock::try_read
#[must_use = "if unused the RwLock will immediately unlock"]
+#[cfg_attr(
+ not(bootstrap),
+ must_not_suspend = "Holding a RwLockReadGuard across suspend \
+ points can cause deadlocks, delays, \
+ and cause Futures to not implement `Send`"
+)]
#[stable(feature = "rust1", since = "1.0.0")]
pub struct RwLockReadGuard<'a, T: ?Sized + 'a> {
lock: &'a RwLock<T>,
/// [`write`]: RwLock::write
/// [`try_write`]: RwLock::try_write
#[must_use = "if unused the RwLock will immediately unlock"]
+#[cfg_attr(
+ not(bootstrap),
+ must_not_suspend = "Holding a RwLockWriteGuard across suspend \
+ points can cause deadlocks, delays, \
+ and cause Future's to not implement `Send`"
+)]
#[stable(feature = "rust1", since = "1.0.0")]
pub struct RwLockWriteGuard<'a, T: ?Sized + 'a> {
lock: &'a RwLock<T>,
--- /dev/null
+//! ABI for μITRON derivatives
+pub type int_t = crate::os::raw::c_int;
+pub type uint_t = crate::os::raw::c_uint;
+pub type bool_t = int_t;
+
+/// Kernel object ID
+pub type ID = int_t;
+
+/// The current task.
+pub const TSK_SELF: ID = 0;
+
+/// Relative time
+pub type RELTIM = u32;
+
+/// Timeout (a valid `RELTIM` value or `TMO_FEVR`)
+pub type TMO = u32;
+
+/// The infinite timeout value
+pub const TMO_FEVR: TMO = TMO::MAX;
+
+/// The maximum valid value of `RELTIM`
+pub const TMAX_RELTIM: RELTIM = 4_000_000_000;
+
+/// System time
+pub type SYSTIM = u64;
+
+/// Error code type
+pub type ER = int_t;
+
+/// Error code type, `ID` on success
+pub type ER_ID = int_t;
+
+/// Task or interrupt priority
+pub type PRI = int_t;
+
+/// The special value of `PRI` representing the current task's priority.
+pub const TPRI_SELF: PRI = 0;
+
+/// Object attributes
+pub type ATR = uint_t;
+
+/// Use the priority inheritance protocol
+#[cfg(target_os = "solid_asp3")]
+pub const TA_INHERIT: ATR = 0x02;
+
+/// Activate the task on creation
+pub const TA_ACT: ATR = 0x01;
+
+/// The maximum count of a semaphore
+pub const TMAX_MAXSEM: uint_t = uint_t::MAX;
+
+/// Callback parameter
+pub type EXINF = isize;
+
+/// Task entrypoint
+pub type TASK = Option<unsafe extern "C" fn(EXINF)>;
+
+// Error codes
+pub const E_OK: ER = 0;
+pub const E_SYS: ER = -5;
+pub const E_NOSPT: ER = -9;
+pub const E_RSFN: ER = -10;
+pub const E_RSATR: ER = -11;
+pub const E_PAR: ER = -17;
+pub const E_ID: ER = -18;
+pub const E_CTX: ER = -25;
+pub const E_MACV: ER = -26;
+pub const E_OACV: ER = -27;
+pub const E_ILUSE: ER = -28;
+pub const E_NOMEM: ER = -33;
+pub const E_NOID: ER = -34;
+pub const E_NORES: ER = -35;
+pub const E_OBJ: ER = -41;
+pub const E_NOEXS: ER = -42;
+pub const E_QOVR: ER = -43;
+pub const E_RLWAI: ER = -49;
+pub const E_TMOUT: ER = -50;
+pub const E_DLT: ER = -51;
+pub const E_CLS: ER = -52;
+pub const E_RASTER: ER = -53;
+pub const E_WBLK: ER = -57;
+pub const E_BOVR: ER = -58;
+pub const E_COMM: ER = -65;
+
+#[derive(Clone, Copy)]
+#[repr(C)]
+pub struct T_CSEM {
+ pub sematr: ATR,
+ pub isemcnt: uint_t,
+ pub maxsem: uint_t,
+}
+
+#[derive(Clone, Copy)]
+#[repr(C)]
+pub struct T_CMTX {
+ pub mtxatr: ATR,
+ pub ceilpri: PRI,
+}
+
+#[derive(Clone, Copy)]
+#[repr(C)]
+pub struct T_CTSK {
+ pub tskatr: ATR,
+ pub exinf: EXINF,
+ pub task: TASK,
+ pub itskpri: PRI,
+ pub stksz: usize,
+ pub stk: *mut u8,
+}
+
+extern "C" {
+ #[link_name = "__asp3_acre_tsk"]
+ pub fn acre_tsk(pk_ctsk: *const T_CTSK) -> ER_ID;
+ #[link_name = "__asp3_get_tid"]
+ pub fn get_tid(p_tskid: *mut ID) -> ER;
+ #[link_name = "__asp3_dly_tsk"]
+ pub fn dly_tsk(dlytim: RELTIM) -> ER;
+ #[link_name = "__asp3_ter_tsk"]
+ pub fn ter_tsk(tskid: ID) -> ER;
+ #[link_name = "__asp3_del_tsk"]
+ pub fn del_tsk(tskid: ID) -> ER;
+ #[link_name = "__asp3_get_pri"]
+ pub fn get_pri(tskid: ID, p_tskpri: *mut PRI) -> ER;
+ #[link_name = "__asp3_rot_rdq"]
+ pub fn rot_rdq(tskpri: PRI) -> ER;
+ #[link_name = "__asp3_slp_tsk"]
+ pub fn slp_tsk() -> ER;
+ #[link_name = "__asp3_tslp_tsk"]
+ pub fn tslp_tsk(tmout: TMO) -> ER;
+ #[link_name = "__asp3_wup_tsk"]
+ pub fn wup_tsk(tskid: ID) -> ER;
+ #[link_name = "__asp3_unl_cpu"]
+ pub fn unl_cpu() -> ER;
+ #[link_name = "__asp3_dis_dsp"]
+ pub fn dis_dsp() -> ER;
+ #[link_name = "__asp3_ena_dsp"]
+ pub fn ena_dsp() -> ER;
+ #[link_name = "__asp3_sns_dsp"]
+ pub fn sns_dsp() -> bool_t;
+ #[link_name = "__asp3_get_tim"]
+ pub fn get_tim(p_systim: *mut SYSTIM) -> ER;
+ #[link_name = "__asp3_acre_mtx"]
+ pub fn acre_mtx(pk_cmtx: *const T_CMTX) -> ER_ID;
+ #[link_name = "__asp3_del_mtx"]
+ pub fn del_mtx(tskid: ID) -> ER;
+ #[link_name = "__asp3_loc_mtx"]
+ pub fn loc_mtx(mtxid: ID) -> ER;
+ #[link_name = "__asp3_ploc_mtx"]
+ pub fn ploc_mtx(mtxid: ID) -> ER;
+ #[link_name = "__asp3_tloc_mtx"]
+ pub fn tloc_mtx(mtxid: ID, tmout: TMO) -> ER;
+ #[link_name = "__asp3_unl_mtx"]
+ pub fn unl_mtx(mtxid: ID) -> ER;
+ pub fn exd_tsk() -> ER;
+}
--- /dev/null
+//! POSIX conditional variable implementation based on user-space wait queues.
+use super::{abi, error::expect_success_aborting, spin::SpinMutex, task, time::with_tmos_strong};
+use crate::{mem::replace, ptr::NonNull, sys::mutex::Mutex, time::Duration};
+
+// The implementation is inspired by the queue-based implementation shown in
+// Andrew D. Birrell's paper "Implementing Condition Variables with Semaphores"
+
+pub struct Condvar {
+ waiters: SpinMutex<waiter_queue::WaiterQueue>,
+}
+
+unsafe impl Send for Condvar {}
+unsafe impl Sync for Condvar {}
+
+pub type MovableCondvar = Condvar;
+
+impl Condvar {
+ pub const fn new() -> Condvar {
+ Condvar { waiters: SpinMutex::new(waiter_queue::WaiterQueue::new()) }
+ }
+
+ pub unsafe fn init(&mut self) {}
+
+ pub unsafe fn notify_one(&self) {
+ self.waiters.with_locked(|waiters| {
+ if let Some(task) = waiters.pop_front() {
+ // Unpark the task
+ match unsafe { abi::wup_tsk(task) } {
+ // The task already has a token.
+ abi::E_QOVR => {}
+ // Can't undo the effect; abort the program on failure
+ er => {
+ expect_success_aborting(er, &"wup_tsk");
+ }
+ }
+ }
+ });
+ }
+
+ pub unsafe fn notify_all(&self) {
+ self.waiters.with_locked(|waiters| {
+ while let Some(task) = waiters.pop_front() {
+ // Unpark the task
+ match unsafe { abi::wup_tsk(task) } {
+ // The task already has a token.
+ abi::E_QOVR => {}
+ // Can't undo the effect; abort the program on failure
+ er => {
+ expect_success_aborting(er, &"wup_tsk");
+ }
+ }
+ }
+ });
+ }
+
+ pub unsafe fn wait(&self, mutex: &Mutex) {
+ // Construct `Waiter`.
+ let mut waiter = waiter_queue::Waiter::new();
+ let waiter = NonNull::from(&mut waiter);
+
+ self.waiters.with_locked(|waiters| unsafe {
+ waiters.insert(waiter);
+ });
+
+ unsafe { mutex.unlock() };
+
+ // Wait until `waiter` is removed from the queue
+ loop {
+ // Park the current task
+ expect_success_aborting(unsafe { abi::slp_tsk() }, &"slp_tsk");
+
+ if !self.waiters.with_locked(|waiters| unsafe { waiters.is_queued(waiter) }) {
+ break;
+ }
+ }
+
+ unsafe { mutex.lock() };
+ }
+
+ pub unsafe fn wait_timeout(&self, mutex: &Mutex, dur: Duration) -> bool {
+ // Construct and pin `Waiter`
+ let mut waiter = waiter_queue::Waiter::new();
+ let waiter = NonNull::from(&mut waiter);
+
+ self.waiters.with_locked(|waiters| unsafe {
+ waiters.insert(waiter);
+ });
+
+ unsafe { mutex.unlock() };
+
+ // Park the current task and do not wake up until the timeout elapses
+ // or the task gets woken up by `notify_*`
+ match with_tmos_strong(dur, |tmo| {
+ let er = unsafe { abi::tslp_tsk(tmo) };
+ if er == 0 {
+ // We were unparked. Are we really dequeued?
+ if self.waiters.with_locked(|waiters| unsafe { waiters.is_queued(waiter) }) {
+ // No we are not. Continue waiting.
+ return abi::E_TMOUT;
+ }
+ }
+ er
+ }) {
+ abi::E_TMOUT => {}
+ er => {
+ expect_success_aborting(er, &"tslp_tsk");
+ }
+ }
+
+ // Remove `waiter` from `self.waiters`. If `waiter` is still in
+ // `waiters`, it means we woke up because of a timeout. Otherwise,
+ // we woke up because of `notify_*`.
+ let success = self.waiters.with_locked(|waiters| unsafe { !waiters.remove(waiter) });
+
+ unsafe { mutex.lock() };
+ success
+ }
+
+ pub unsafe fn destroy(&self) {}
+}
+
+mod waiter_queue {
+ use super::*;
+
+ pub struct WaiterQueue {
+ head: Option<ListHead>,
+ }
+
+ #[derive(Copy, Clone)]
+ struct ListHead {
+ first: NonNull<Waiter>,
+ last: NonNull<Waiter>,
+ }
+
+ unsafe impl Send for ListHead {}
+ unsafe impl Sync for ListHead {}
+
+ pub struct Waiter {
+ // These fields are only accessed through `&[mut] WaiterQueue`.
+ /// The waiting task's ID. Will be zeroed when the task is woken up
+ /// and removed from a queue.
+ task: abi::ID,
+ priority: abi::PRI,
+ prev: Option<NonNull<Waiter>>,
+ next: Option<NonNull<Waiter>>,
+ }
+
+ unsafe impl Send for Waiter {}
+ unsafe impl Sync for Waiter {}
+
+ impl Waiter {
+ #[inline]
+ pub fn new() -> Self {
+ let task = task::current_task_id();
+ let priority = task::task_priority(abi::TSK_SELF);
+
+ // Zeroness of `Waiter::task` indicates whether the `Waiter` is
+ // linked to a queue or not. This invariant is important for
+ // the correctness.
+ debug_assert_ne!(task, 0);
+
+ Self { task, priority, prev: None, next: None }
+ }
+ }
+
+ impl WaiterQueue {
+ #[inline]
+ pub const fn new() -> Self {
+ Self { head: None }
+ }
+
+ /// # Safety
+ ///
+ /// - The caller must own `*waiter_ptr`. The caller will lose the
+ /// ownership until `*waiter_ptr` is removed from `self`.
+ ///
+ /// - `*waiter_ptr` must be valid until it's removed from the queue.
+ ///
+ /// - `*waiter_ptr` must not have been previously inserted to a `WaiterQueue`.
+ ///
+ pub unsafe fn insert(&mut self, mut waiter_ptr: NonNull<Waiter>) {
+ unsafe {
+ let waiter = waiter_ptr.as_mut();
+
+ debug_assert!(waiter.prev.is_none());
+ debug_assert!(waiter.next.is_none());
+
+ if let Some(head) = &mut self.head {
+ // Find the insertion position and insert `waiter`
+ let insert_after = {
+ let mut cursor = head.last;
+ loop {
+ if waiter.priority <= cursor.as_ref().priority {
+ // `cursor` and all previous waiters have the same or higher
+ // priority than `current_task_priority`. Insert the new
+ // waiter right after `cursor`.
+ break Some(cursor);
+ }
+ cursor = if let Some(prev) = cursor.as_ref().prev {
+ prev
+ } else {
+ break None;
+ };
+ }
+ };
+
+ if let Some(mut insert_after) = insert_after {
+ // Insert `waiter` after `insert_after`
+ let insert_before = insert_after.as_ref().prev;
+
+ waiter.prev = Some(insert_after);
+ insert_after.as_mut().next = Some(waiter_ptr);
+
+ waiter.next = insert_before;
+ if let Some(mut insert_before) = insert_before {
+ insert_before.as_mut().prev = Some(waiter_ptr);
+ }
+ } else {
+ // Insert `waiter` to the front
+ waiter.next = Some(head.first);
+ head.first.as_mut().prev = Some(waiter_ptr);
+ head.first = waiter_ptr;
+ }
+ } else {
+ // `waiter` is the only element
+ self.head = Some(ListHead { first: waiter_ptr, last: waiter_ptr });
+ }
+ }
+ }
+
+ /// Given a `Waiter` that was previously inserted to `self`, remove
+ /// it from `self` if it's still there.
+ #[inline]
+ pub unsafe fn remove(&mut self, mut waiter_ptr: NonNull<Waiter>) -> bool {
+ unsafe {
+ let waiter = waiter_ptr.as_mut();
+ if waiter.task != 0 {
+ let head = self.head.as_mut().unwrap();
+
+ match (waiter.prev, waiter.next) {
+ (Some(mut prev), Some(mut next)) => {
+ prev.as_mut().next = Some(next);
+ next.as_mut().next = Some(prev);
+ }
+ (None, Some(mut next)) => {
+ head.first = next;
+ next.as_mut().next = None;
+ }
+ (Some(mut prev), None) => {
+ prev.as_mut().next = None;
+ head.last = prev;
+ }
+ (None, None) => {
+ self.head = None;
+ }
+ }
+
+ waiter.task = 0;
+
+ true
+ } else {
+ false
+ }
+ }
+ }
+
+ /// Given a `Waiter` that was previously inserted to `self`, return a
+ /// flag indicating whether it's still in `self`.
+ #[inline]
+ pub unsafe fn is_queued(&self, waiter: NonNull<Waiter>) -> bool {
+ unsafe { waiter.as_ref().task != 0 }
+ }
+
+ pub fn pop_front(&mut self) -> Option<abi::ID> {
+ unsafe {
+ let head = self.head.as_mut()?;
+ let waiter = head.first.as_mut();
+
+ // Get the ID
+ let id = replace(&mut waiter.task, 0);
+
+ // Unlink the waiter
+ if let Some(mut next) = waiter.next {
+ head.first = next;
+ next.as_mut().prev = None;
+ } else {
+ self.head = None;
+ }
+
+ Some(id)
+ }
+ }
+ }
+}
--- /dev/null
+use crate::{fmt, io::ErrorKind};
+
+use super::abi;
+
+/// Wraps a μITRON error code.
+#[derive(Debug, Copy, Clone)]
+pub struct ItronError {
+ er: abi::ER,
+}
+
+impl ItronError {
+ /// Construct `ItronError` from the specified error code. Returns `None` if the
+ /// error code does not represent a failure or warning.
+ #[inline]
+ pub fn new(er: abi::ER) -> Option<Self> {
+ if er < 0 { Some(Self { er }) } else { None }
+ }
+
+ /// Returns `Ok(er)` if `er` represents a success or `Err(_)` otherwise.
+ #[inline]
+ pub fn err_if_negative(er: abi::ER) -> Result<abi::ER, Self> {
+ if let Some(error) = Self::new(er) { Err(error) } else { Ok(er) }
+ }
+
+ /// Get the raw error code.
+ #[inline]
+ pub fn as_raw(&self) -> abi::ER {
+ self.er
+ }
+}
+
+impl fmt::Display for ItronError {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ // Allow the platforms to extend `error_name`
+ if let Some(name) = crate::sys::error::error_name(self.er) {
+ write!(f, "{} ({})", name, self.er)
+ } else {
+ write!(f, "{}", self.er)
+ }
+ }
+}
+
+/// Describe the specified μITRON error code. Returns `None` if it's an
+/// undefined error code.
+pub fn error_name(er: abi::ER) -> Option<&'static str> {
+ match er {
+ // Success
+ er if er >= 0 => None,
+
+ // μITRON 4.0
+ abi::E_SYS => Some("system error"),
+ abi::E_NOSPT => Some("unsupported function"),
+ abi::E_RSFN => Some("reserved function code"),
+ abi::E_RSATR => Some("reserved attribute"),
+ abi::E_PAR => Some("parameter error"),
+ abi::E_ID => Some("invalid ID number"),
+ abi::E_CTX => Some("context error"),
+ abi::E_MACV => Some("memory access violation"),
+ abi::E_OACV => Some("object access violation"),
+ abi::E_ILUSE => Some("illegal service call use"),
+ abi::E_NOMEM => Some("insufficient memory"),
+ abi::E_NOID => Some("no ID number available"),
+ abi::E_OBJ => Some("object state error"),
+ abi::E_NOEXS => Some("non-existent object"),
+ abi::E_QOVR => Some("queue overflow"),
+ abi::E_RLWAI => Some("forced release from waiting"),
+ abi::E_TMOUT => Some("polling failure or timeout"),
+ abi::E_DLT => Some("waiting object deleted"),
+ abi::E_CLS => Some("waiting object state changed"),
+ abi::E_WBLK => Some("non-blocking code accepted"),
+ abi::E_BOVR => Some("buffer overflow"),
+
+ // The TOPPERS third generation kernels
+ abi::E_NORES => Some("insufficient system resources"),
+ abi::E_RASTER => Some("termination request raised"),
+ abi::E_COMM => Some("communication failure"),
+
+ _ => None,
+ }
+}
+
+pub fn decode_error_kind(er: abi::ER) -> ErrorKind {
+ match er {
+ // Success
+ er if er >= 0 => ErrorKind::Uncategorized,
+
+ // μITRON 4.0
+ // abi::E_SYS
+ abi::E_NOSPT => ErrorKind::Unsupported, // Some("unsupported function"),
+ abi::E_RSFN => ErrorKind::InvalidInput, // Some("reserved function code"),
+ abi::E_RSATR => ErrorKind::InvalidInput, // Some("reserved attribute"),
+ abi::E_PAR => ErrorKind::InvalidInput, // Some("parameter error"),
+ abi::E_ID => ErrorKind::NotFound, // Some("invalid ID number"),
+ // abi::E_CTX
+ abi::E_MACV => ErrorKind::PermissionDenied, // Some("memory access violation"),
+ abi::E_OACV => ErrorKind::PermissionDenied, // Some("object access violation"),
+ // abi::E_ILUSE
+ abi::E_NOMEM => ErrorKind::OutOfMemory, // Some("insufficient memory"),
+ abi::E_NOID => ErrorKind::OutOfMemory, // Some("no ID number available"),
+ // abi::E_OBJ
+ abi::E_NOEXS => ErrorKind::NotFound, // Some("non-existent object"),
+ // abi::E_QOVR
+ abi::E_RLWAI => ErrorKind::Interrupted, // Some("forced release from waiting"),
+ abi::E_TMOUT => ErrorKind::TimedOut, // Some("polling failure or timeout"),
+ // abi::E_DLT
+ // abi::E_CLS
+ // abi::E_WBLK
+ // abi::E_BOVR
+
+ // The TOPPERS third generation kernels
+ abi::E_NORES => ErrorKind::OutOfMemory, // Some("insufficient system resources"),
+ // abi::E_RASTER
+ // abi::E_COMM
+ _ => ErrorKind::Uncategorized,
+ }
+}
+
+/// Similar to `ItronError::err_if_negative(er).expect()` except that, while
+/// panicking, it prints the message to `panic_output` and aborts the program
+/// instead. This ensures the error message is not obscured by double
+/// panicking.
+///
+/// This is useful for diagnosing creation failures of synchronization
+/// primitives that are used by `std`'s internal mechanisms. Such failures
+/// are common when the system is mis-configured to provide a too-small pool for
+/// kernel objects.
+#[inline]
+pub fn expect_success(er: abi::ER, msg: &&str) -> abi::ER {
+ match ItronError::err_if_negative(er) {
+ Ok(x) => x,
+ Err(e) => fail(e, msg),
+ }
+}
+
+/// Similar to `ItronError::err_if_negative(er).expect()` but aborts instead.
+///
+/// Use this where panicking is not allowed or the effect of the failure
+/// would be persistent.
+#[inline]
+pub fn expect_success_aborting(er: abi::ER, msg: &&str) -> abi::ER {
+ match ItronError::err_if_negative(er) {
+ Ok(x) => x,
+ Err(e) => fail_aborting(e, msg),
+ }
+}
+
+#[cold]
+pub fn fail(e: impl fmt::Display, msg: &&str) -> ! {
+ if crate::thread::panicking() {
+ fail_aborting(e, msg)
+ } else {
+ panic!("{} failed: {}", *msg, e)
+ }
+}
+
+#[cold]
+pub fn fail_aborting(e: impl fmt::Display, msg: &&str) -> ! {
+ rtabort!("{} failed: {}", *msg, e)
+}
--- /dev/null
+//! Mutex implementation backed by μITRON mutexes. Assumes `acre_mtx` and
+//! `TA_INHERIT` are available.
+use super::{
+ abi,
+ error::{expect_success, expect_success_aborting, fail, ItronError},
+ spin::SpinIdOnceCell,
+};
+use crate::cell::UnsafeCell;
+
+pub struct Mutex {
+ /// The ID of the underlying mutex object
+ mtx: SpinIdOnceCell<()>,
+}
+
+pub type MovableMutex = Mutex;
+
+/// Create a mutex object. This function never panics.
+fn new_mtx() -> Result<abi::ID, ItronError> {
+ ItronError::err_if_negative(unsafe {
+ abi::acre_mtx(&abi::T_CMTX {
+ // Priority inheritance mutex
+ mtxatr: abi::TA_INHERIT,
+ // Unused
+ ceilpri: 0,
+ })
+ })
+}
+
+impl Mutex {
+ pub const fn new() -> Mutex {
+ Mutex { mtx: SpinIdOnceCell::new() }
+ }
+
+ pub unsafe fn init(&mut self) {
+ // Initialize `self.mtx` eagerly
+ let id = new_mtx().unwrap_or_else(|e| fail(e, &"acre_mtx"));
+ unsafe { self.mtx.set_unchecked((id, ())) };
+ }
+
+ /// Get the inner mutex's ID, which is lazily created.
+ fn raw(&self) -> abi::ID {
+ match self.mtx.get_or_try_init(|| new_mtx().map(|id| (id, ()))) {
+ Ok((id, ())) => id,
+ Err(e) => fail(e, &"acre_mtx"),
+ }
+ }
+
+ pub unsafe fn lock(&self) {
+ let mtx = self.raw();
+ expect_success(unsafe { abi::loc_mtx(mtx) }, &"loc_mtx");
+ }
+
+ pub unsafe fn unlock(&self) {
+ let mtx = unsafe { self.mtx.get_unchecked().0 };
+ expect_success_aborting(unsafe { abi::unl_mtx(mtx) }, &"unl_mtx");
+ }
+
+ pub unsafe fn try_lock(&self) -> bool {
+ let mtx = self.raw();
+ match unsafe { abi::ploc_mtx(mtx) } {
+ abi::E_TMOUT => false,
+ er => {
+ expect_success(er, &"ploc_mtx");
+ true
+ }
+ }
+ }
+
+ pub unsafe fn destroy(&self) {
+ if let Some(mtx) = self.mtx.get().map(|x| x.0) {
+ expect_success_aborting(unsafe { abi::del_mtx(mtx) }, &"del_mtx");
+ }
+ }
+}
+
+pub(super) struct MutexGuard<'a>(&'a Mutex);
+
+impl<'a> MutexGuard<'a> {
+ #[inline]
+ pub(super) fn lock(x: &'a Mutex) -> Self {
+ unsafe { x.lock() };
+ Self(x)
+ }
+}
+
+impl Drop for MutexGuard<'_> {
+ #[inline]
+ fn drop(&mut self) {
+ unsafe { self.0.unlock() };
+ }
+}
+
+// All empty stubs because this platform does not yet support threads, so lock
+// acquisition always succeeds.
+pub struct ReentrantMutex {
+ /// The ID of the underlying mutex object
+ mtx: abi::ID,
+ /// The lock count.
+ count: UnsafeCell<usize>,
+}
+
+unsafe impl Send for ReentrantMutex {}
+unsafe impl Sync for ReentrantMutex {}
+
+impl ReentrantMutex {
+ pub const unsafe fn uninitialized() -> ReentrantMutex {
+ ReentrantMutex { mtx: 0, count: UnsafeCell::new(0) }
+ }
+
+ pub unsafe fn init(&mut self) {
+ self.mtx = expect_success(
+ unsafe {
+ abi::acre_mtx(&abi::T_CMTX {
+ // Priority inheritance mutex
+ mtxatr: abi::TA_INHERIT,
+ // Unused
+ ceilpri: 0,
+ })
+ },
+ &"acre_mtx",
+ );
+ }
+
+ pub unsafe fn lock(&self) {
+ match unsafe { abi::loc_mtx(self.mtx) } {
+ abi::E_OBJ => {
+ // Recursive lock
+ unsafe {
+ let count = &mut *self.count.get();
+ if let Some(new_count) = count.checked_add(1) {
+ *count = new_count;
+ } else {
+ // counter overflow
+ rtabort!("lock count overflow");
+ }
+ }
+ }
+ er => {
+ expect_success(er, &"loc_mtx");
+ }
+ }
+ }
+
+ pub unsafe fn unlock(&self) {
+ unsafe {
+ let count = &mut *self.count.get();
+ if *count > 0 {
+ *count -= 1;
+ return;
+ }
+ }
+
+ expect_success_aborting(unsafe { abi::unl_mtx(self.mtx) }, &"unl_mtx");
+ }
+
+ pub unsafe fn try_lock(&self) -> bool {
+ let er = unsafe { abi::ploc_mtx(self.mtx) };
+ if er == abi::E_OBJ {
+ // Recursive lock
+ unsafe {
+ let count = &mut *self.count.get();
+ if let Some(new_count) = count.checked_add(1) {
+ *count = new_count;
+ } else {
+ // counter overflow
+ rtabort!("lock count overflow");
+ }
+ }
+ true
+ } else if er == abi::E_TMOUT {
+ // Locked by another thread
+ false
+ } else {
+ expect_success(er, &"ploc_mtx");
+ // Top-level lock by the current thread
+ true
+ }
+ }
+
+ pub unsafe fn destroy(&self) {
+ expect_success_aborting(unsafe { abi::del_mtx(self.mtx) }, &"del_mtx");
+ }
+}
--- /dev/null
+use super::abi;
+use crate::{
+ cell::UnsafeCell,
+ convert::TryFrom,
+ mem::MaybeUninit,
+ sync::atomic::{AtomicBool, AtomicUsize, Ordering},
+};
+
+/// A mutex implemented by `dis_dsp` (for intra-core synchronization) and a
+/// spinlock (for inter-core synchronization).
+pub struct SpinMutex<T = ()> {
+ locked: AtomicBool,
+ data: UnsafeCell<T>,
+}
+
+impl<T> SpinMutex<T> {
+ #[inline]
+ pub const fn new(x: T) -> Self {
+ Self { locked: AtomicBool::new(false), data: UnsafeCell::new(x) }
+ }
+
+ /// Acquire a lock.
+ #[inline]
+ pub fn with_locked<R>(&self, f: impl FnOnce(&mut T) -> R) -> R {
+ struct SpinMutexGuard<'a>(&'a AtomicBool);
+
+ impl Drop for SpinMutexGuard<'_> {
+ #[inline]
+ fn drop(&mut self) {
+ self.0.store(false, Ordering::Release);
+ unsafe { abi::ena_dsp() };
+ }
+ }
+
+ let _guard;
+ if unsafe { abi::sns_dsp() } == 0 {
+ let er = unsafe { abi::dis_dsp() };
+ debug_assert!(er >= 0);
+
+ // Wait until the current processor acquires a lock.
+ while self.locked.swap(true, Ordering::Acquire) {}
+
+ _guard = SpinMutexGuard(&self.locked);
+ }
+
+ f(unsafe { &mut *self.data.get() })
+ }
+}
+
+/// `OnceCell<(abi::ID, T)>` implemented by `dis_dsp` (for intra-core
+/// synchronization) and a spinlock (for inter-core synchronization).
+///
+/// It's assumed that `0` is not a valid ID, and all kernel
+/// object IDs fall into range `1..=usize::MAX`.
+pub struct SpinIdOnceCell<T = ()> {
+ id: AtomicUsize,
+ spin: SpinMutex<()>,
+ extra: UnsafeCell<MaybeUninit<T>>,
+}
+
+const ID_UNINIT: usize = 0;
+
+impl<T> SpinIdOnceCell<T> {
+ #[inline]
+ pub const fn new() -> Self {
+ Self {
+ id: AtomicUsize::new(ID_UNINIT),
+ extra: UnsafeCell::new(MaybeUninit::uninit()),
+ spin: SpinMutex::new(()),
+ }
+ }
+
+ #[inline]
+ pub fn get(&self) -> Option<(abi::ID, &T)> {
+ match self.id.load(Ordering::Acquire) {
+ ID_UNINIT => None,
+ id => Some((id as abi::ID, unsafe { (&*self.extra.get()).assume_init_ref() })),
+ }
+ }
+
+ #[inline]
+ pub fn get_mut(&mut self) -> Option<(abi::ID, &mut T)> {
+ match *self.id.get_mut() {
+ ID_UNINIT => None,
+ id => Some((id as abi::ID, unsafe { (&mut *self.extra.get()).assume_init_mut() })),
+ }
+ }
+
+ #[inline]
+ pub unsafe fn get_unchecked(&self) -> (abi::ID, &T) {
+ (self.id.load(Ordering::Acquire) as abi::ID, unsafe {
+ (&*self.extra.get()).assume_init_ref()
+ })
+ }
+
+ /// Assign the content without checking if it's already initialized or
+ /// being initialized.
+ pub unsafe fn set_unchecked(&self, (id, extra): (abi::ID, T)) {
+ debug_assert!(self.get().is_none());
+
+ // Assumption: A positive `abi::ID` fits in `usize`.
+ debug_assert!(id >= 0);
+ debug_assert!(usize::try_from(id).is_ok());
+ let id = id as usize;
+
+ unsafe { *self.extra.get() = MaybeUninit::new(extra) };
+ self.id.store(id, Ordering::Release);
+ }
+
+ /// Gets the contents of the cell, initializing it with `f` if
+ /// the cell was empty. If the cell was empty and `f` failed, an
+ /// error is returned.
+ ///
+ /// Warning: `f` must not perform a blocking operation, which
+ /// includes panicking.
+ #[inline]
+ pub fn get_or_try_init<F, E>(&self, f: F) -> Result<(abi::ID, &T), E>
+ where
+ F: FnOnce() -> Result<(abi::ID, T), E>,
+ {
+ // Fast path
+ if let Some(x) = self.get() {
+ return Ok(x);
+ }
+
+ self.initialize(f)?;
+
+ debug_assert!(self.get().is_some());
+
+ // Safety: The inner value has been initialized
+ Ok(unsafe { self.get_unchecked() })
+ }
+
+ fn initialize<F, E>(&self, f: F) -> Result<(), E>
+ where
+ F: FnOnce() -> Result<(abi::ID, T), E>,
+ {
+ self.spin.with_locked(|_| {
+ if self.id.load(Ordering::Relaxed) == ID_UNINIT {
+ let (initialized_id, initialized_extra) = f()?;
+
+ // Assumption: A positive `abi::ID` fits in `usize`.
+ debug_assert!(initialized_id >= 0);
+ debug_assert!(usize::try_from(initialized_id).is_ok());
+ let initialized_id = initialized_id as usize;
+
+ // Store the initialized contents. Use the release ordering to
+ // make sure the write is visible to the callers of `get`.
+ unsafe { *self.extra.get() = MaybeUninit::new(initialized_extra) };
+ self.id.store(initialized_id, Ordering::Release);
+ }
+ Ok(())
+ })
+ }
+}
+
+impl<T> Drop for SpinIdOnceCell<T> {
+ #[inline]
+ fn drop(&mut self) {
+ if self.get_mut().is_some() {
+ unsafe { (&mut *self.extra.get()).assume_init_drop() };
+ }
+ }
+}
--- /dev/null
+use super::{
+ abi,
+ error::{fail, fail_aborting, ItronError},
+};
+
+use crate::mem::MaybeUninit;
+
+/// Get the ID of the task in Running state. Panics on failure.
+#[inline]
+pub fn current_task_id() -> abi::ID {
+ try_current_task_id().unwrap_or_else(|e| fail(e, &"get_tid"))
+}
+
+/// Get the ID of the task in Running state. Aborts on failure.
+#[inline]
+pub fn current_task_id_aborting() -> abi::ID {
+ try_current_task_id().unwrap_or_else(|e| fail_aborting(e, &"get_tid"))
+}
+
+/// Get the ID of the task in Running state.
+#[inline]
+pub fn try_current_task_id() -> Result<abi::ID, ItronError> {
+ unsafe {
+ let mut out = MaybeUninit::uninit();
+ ItronError::err_if_negative(abi::get_tid(out.as_mut_ptr()))?;
+ Ok(out.assume_init())
+ }
+}
+
+/// Get the specified task's priority. Panics on failure.
+#[inline]
+pub fn task_priority(task: abi::ID) -> abi::PRI {
+ try_task_priority(task).unwrap_or_else(|e| fail(e, &"get_pri"))
+}
+
+/// Get the specified task's priority.
+#[inline]
+pub fn try_task_priority(task: abi::ID) -> Result<abi::PRI, ItronError> {
+ unsafe {
+ let mut out = MaybeUninit::uninit();
+ ItronError::err_if_negative(abi::get_pri(task, out.as_mut_ptr()))?;
+ Ok(out.assume_init())
+ }
+}
--- /dev/null
+//! Thread implementation backed by μITRON tasks. Assumes `acre_tsk` and
+//! `exd_tsk` are available.
+use super::{
+ abi,
+ error::{expect_success, expect_success_aborting, ItronError},
+ task,
+ time::dur2reltims,
+};
+use crate::{
+ cell::UnsafeCell,
+ convert::TryFrom,
+ ffi::CStr,
+ hint, io,
+ mem::ManuallyDrop,
+ sync::atomic::{AtomicUsize, Ordering},
+ sys::thread_local_dtor::run_dtors,
+ time::Duration,
+};
+
+pub struct Thread {
+ inner: ManuallyDrop<Box<ThreadInner>>,
+
+ /// The ID of the underlying task.
+ task: abi::ID,
+}
+
+/// State data shared between a parent thread and child thread. It's dropped on
+/// a transition to one of the final states.
+struct ThreadInner {
+ /// This field is used on thread creation to pass a closure from
+ /// `Thread::new` to the created task.
+ start: UnsafeCell<ManuallyDrop<Box<dyn FnOnce()>>>,
+
+ /// A state machine. Each transition is annotated with `[...]` in the
+ /// source code.
+ ///
+ /// ```text
+ ///
+ /// <P>: parent, <C>: child, (?): don't-care
+ ///
+ /// DETACHED (-1) --------------------> EXITED (?)
+ /// <C>finish/exd_tsk
+ /// ^
+ /// |
+ /// | <P>detach
+ /// |
+ ///
+ /// INIT (0) -----------------------> FINISHED (-1)
+ /// <C>finish
+ /// | |
+ /// | <P>join/slp_tsk | <P>join/del_tsk
+ /// | | <P>detach/del_tsk
+ /// v v
+ ///
+ /// JOINING JOINED (?)
+ /// (parent_tid)
+ /// ^
+ /// \ /
+ /// \ <C>finish/wup_tsk / <P>slp_tsk-complete/ter_tsk
+ /// \ / & del_tsk
+ /// \ /
+ /// '--> JOIN_FINALIZE ---'
+ /// (-1)
+ ///
+ lifecycle: AtomicUsize,
+}
+
+// Safety: The only `!Sync` field, `ThreadInner::start`, is only touched by
+// the task represented by `ThreadInner`.
+unsafe impl Sync for ThreadInner {}
+
+const LIFECYCLE_INIT: usize = 0;
+const LIFECYCLE_FINISHED: usize = usize::MAX;
+const LIFECYCLE_DETACHED: usize = usize::MAX;
+const LIFECYCLE_JOIN_FINALIZE: usize = usize::MAX;
+const LIFECYCLE_DETACHED_OR_JOINED: usize = usize::MAX;
+const LIFECYCLE_EXITED_OR_FINISHED_OR_JOIN_FINALIZE: usize = usize::MAX;
+// there's no single value for `JOINING`
+
+pub const DEFAULT_MIN_STACK_SIZE: usize = 1024 * crate::mem::size_of::<usize>();
+
+impl Thread {
+ /// # Safety
+ ///
+ /// See `thread::Builder::spawn_unchecked` for safety requirements.
+ pub unsafe fn new(stack: usize, p: Box<dyn FnOnce()>) -> io::Result<Thread> {
+ // Inherit the current task's priority
+ let current_task = task::try_current_task_id().map_err(|e| e.as_io_error())?;
+ let priority = task::try_task_priority(current_task).map_err(|e| e.as_io_error())?;
+
+ let inner = Box::new(ThreadInner {
+ start: UnsafeCell::new(ManuallyDrop::new(p)),
+ lifecycle: AtomicUsize::new(LIFECYCLE_INIT),
+ });
+
+ unsafe extern "C" fn trampoline(exinf: isize) {
+ // Safety: `ThreadInner` is alive at this point
+ let inner = unsafe { &*(exinf as *const ThreadInner) };
+
+ // Safety: Since `trampoline` is called only once for each
+ // `ThreadInner` and only `trampoline` touches `start`,
+ // `start` contains contents and is safe to mutably borrow.
+ let p = unsafe { ManuallyDrop::take(&mut *inner.start.get()) };
+ p();
+
+ // Fix the current thread's state just in case, so that the
+ // destructors won't abort
+ // Safety: Not really unsafe
+ let _ = unsafe { abi::unl_cpu() };
+ let _ = unsafe { abi::ena_dsp() };
+
+ // Run TLS destructors now because they are not
+ // called automatically for terminated tasks.
+ unsafe { run_dtors() };
+
+ let old_lifecycle = inner
+ .lifecycle
+ .swap(LIFECYCLE_EXITED_OR_FINISHED_OR_JOIN_FINALIZE, Ordering::Release);
+
+ match old_lifecycle {
+ LIFECYCLE_DETACHED => {
+ // [DETACHED → EXITED]
+ // No one will ever join, so we'll ask the collector task to
+ // delete the task.
+
+ // In this case, `inner`'s ownership has been moved to us,
+ // And we are responsible for dropping it. The acquire
+ // ordering is not necessary because the parent thread made
+ // no memory acccess needing synchronization since the call
+ // to `acre_tsk`.
+ // Safety: See above.
+ let _ = unsafe { Box::from_raw(inner as *const _ as *mut ThreadInner) };
+
+ // Safety: There are no pinned references to the stack
+ unsafe { terminate_and_delete_current_task() };
+ }
+ LIFECYCLE_INIT => {
+ // [INIT → FINISHED]
+ // The parent hasn't decided whether to join or detach this
+ // thread yet. Whichever option the parent chooses,
+ // it'll have to delete this task.
+ // Since the parent might drop `*inner` as soon as it sees
+ // `FINISHED`, the release ordering must be used in the
+ // above `swap` call.
+ }
+ parent_tid => {
+ // Since the parent might drop `*inner` and terminate us as
+ // soon as it sees `JOIN_FINALIZE`, the release ordering
+ // must be used in the above `swap` call.
+
+ // [JOINING → JOIN_FINALIZE]
+ // Wake up the parent task.
+ expect_success(
+ unsafe {
+ let mut er = abi::wup_tsk(parent_tid as _);
+ if er == abi::E_QOVR {
+ // `E_QOVR` indicates there's already
+ // a parking token
+ er = abi::E_OK;
+ }
+ er
+ },
+ &"wup_tsk",
+ );
+ }
+ }
+ }
+
+ let inner_ptr = (&*inner) as *const ThreadInner;
+
+ let new_task = ItronError::err_if_negative(unsafe {
+ abi::acre_tsk(&abi::T_CTSK {
+ // Activate this task immediately
+ tskatr: abi::TA_ACT,
+ exinf: inner_ptr as abi::EXINF,
+ // The entry point
+ task: Some(trampoline),
+ itskpri: priority,
+ stksz: stack,
+ // Let the kernel allocate the stack,
+ stk: crate::ptr::null_mut(),
+ })
+ })
+ .map_err(|e| e.as_io_error())?;
+
+ Ok(Self { inner: ManuallyDrop::new(inner), task: new_task })
+ }
+
+ pub fn yield_now() {
+ expect_success(unsafe { abi::rot_rdq(abi::TPRI_SELF) }, &"rot_rdq");
+ }
+
+ pub fn set_name(_name: &CStr) {
+ // nope
+ }
+
+ pub fn sleep(dur: Duration) {
+ for timeout in dur2reltims(dur) {
+ expect_success(unsafe { abi::dly_tsk(timeout) }, &"dly_tsk");
+ }
+ }
+
+ pub fn join(mut self) {
+ let inner = &*self.inner;
+ // Get the current task ID. Panicking here would cause a resource leak,
+ // so just abort on failure.
+ let current_task = task::current_task_id_aborting();
+ debug_assert!(usize::try_from(current_task).is_ok());
+ debug_assert_ne!(current_task as usize, LIFECYCLE_INIT);
+ debug_assert_ne!(current_task as usize, LIFECYCLE_DETACHED);
+
+ let current_task = current_task as usize;
+
+ match inner.lifecycle.swap(current_task, Ordering::Acquire) {
+ LIFECYCLE_INIT => {
+ // [INIT → JOINING]
+ // The child task will transition the state to `JOIN_FINALIZE`
+ // and wake us up.
+ loop {
+ expect_success_aborting(unsafe { abi::slp_tsk() }, &"slp_tsk");
+ // To synchronize with the child task's memory accesses to
+ // `inner` up to the point of the assignment of
+ // `JOIN_FINALIZE`, `Ordering::Acquire` must be used for the
+ // `load`.
+ if inner.lifecycle.load(Ordering::Acquire) == LIFECYCLE_JOIN_FINALIZE {
+ break;
+ }
+ }
+
+ // [JOIN_FINALIZE → JOINED]
+ }
+ LIFECYCLE_FINISHED => {
+ // [FINISHED → JOINED]
+ // To synchronize with the child task's memory accesses to
+ // `inner` up to the point of the assignment of `FINISHED`,
+ // `Ordering::Acquire` must be used for the above `swap` call`.
+ }
+ _ => unsafe { hint::unreachable_unchecked() },
+ }
+
+ // Terminate and delete the task
+ // Safety: `self.task` still represents a task we own (because this
+ // method or `detach_inner` is called only once for each
+ // `Thread`). The task indicated that it's safe to delete by
+ // entering the `FINISHED` or `JOIN_FINALIZE` state.
+ unsafe { terminate_and_delete_task(self.task) };
+
+ // In either case, we are responsible for dropping `inner`.
+ // Safety: The contents of `self.inner` will not be accessed hereafter
+ let _inner = unsafe { ManuallyDrop::take(&mut self.inner) };
+
+ // Skip the destructor (because it would attempt to detach the thread)
+ crate::mem::forget(self);
+ }
+}
+
+impl Drop for Thread {
+ fn drop(&mut self) {
+ // Detach the thread.
+ match self.inner.lifecycle.swap(LIFECYCLE_DETACHED_OR_JOINED, Ordering::Acquire) {
+ LIFECYCLE_INIT => {
+ // [INIT → DETACHED]
+ // When the time comes, the child will figure out that no
+ // one will ever join it.
+ // The ownership of `self.inner` is moved to the child thread.
+ // However, the release ordering is not necessary because we
+ // made no memory acccess needing synchronization since the call
+ // to `acre_tsk`.
+ }
+ LIFECYCLE_FINISHED => {
+ // [FINISHED → JOINED]
+ // The task has already decided that we should delete the task.
+ // To synchronize with the child task's memory accesses to
+ // `inner` up to the point of the assignment of `FINISHED`,
+ // the acquire ordering is required for the above `swap` call.
+
+ // Terminate and delete the task
+ // Safety: `self.task` still represents a task we own (because
+ // this method or `join_inner` is called only once for
+ // each `Thread`). The task indicated that it's safe to
+ // delete by entering the `FINISHED` state.
+ unsafe { terminate_and_delete_task(self.task) };
+
+ // Wwe are responsible for dropping `inner`.
+ // Safety: The contents of `self.inner` will not be accessed
+ // hereafter
+ unsafe { ManuallyDrop::drop(&mut self.inner) };
+ }
+ _ => unsafe { hint::unreachable_unchecked() },
+ }
+ }
+}
+
+pub mod guard {
+ pub type Guard = !;
+ pub unsafe fn current() -> Option<Guard> {
+ None
+ }
+ pub unsafe fn init() -> Option<Guard> {
+ None
+ }
+}
+
+/// Terminate and delete the specified task.
+///
+/// This function will abort if `deleted_task` refers to the calling task.
+///
+/// It is assumed that the specified task is solely managed by the caller -
+/// i.e., other threads must not "resuscitate" the specified task or delete it
+/// prematurely while this function is still in progress. It is allowed for the
+/// specified task to exit by its own.
+///
+/// # Safety
+///
+/// The task must be safe to terminate. This is in general not true
+/// because there might be pinned references to the task's stack.
+unsafe fn terminate_and_delete_task(deleted_task: abi::ID) {
+ // Terminate the task
+ // Safety: Upheld by the caller
+ match unsafe { abi::ter_tsk(deleted_task) } {
+ // Indicates the task is already dormant, ignore it
+ abi::E_OBJ => {}
+ er => {
+ expect_success_aborting(er, &"ter_tsk");
+ }
+ }
+
+ // Delete the task
+ // Safety: Upheld by the caller
+ expect_success_aborting(unsafe { abi::del_tsk(deleted_task) }, &"del_tsk");
+}
+
+/// Terminate and delete the calling task.
+///
+/// Atomicity is not required - i.e., it can be assumed that other threads won't
+/// `ter_tsk` the calling task while this function is still in progress. (This
+/// property makes it easy to implement this operation on μITRON-derived kernels
+/// that don't support `exd_tsk`.)
+///
+/// # Safety
+///
+/// The task must be safe to terminate. This is in general not true
+/// because there might be pinned references to the task's stack.
+unsafe fn terminate_and_delete_current_task() -> ! {
+ expect_success_aborting(unsafe { abi::exd_tsk() }, &"exd_tsk");
+ // Safety: `exd_tsk` never returns on success
+ unsafe { crate::hint::unreachable_unchecked() };
+}
+
+pub fn available_concurrency() -> io::Result<crate::num::NonZeroUsize> {
+ super::unsupported()
+}
--- /dev/null
+use super::{abi, error::expect_success};
+use crate::{convert::TryInto, mem::MaybeUninit, time::Duration};
+
+#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)]
+pub struct Instant(abi::SYSTIM);
+
+impl Instant {
+ pub fn now() -> Instant {
+ // Safety: The provided pointer is valid
+ unsafe {
+ let mut out = MaybeUninit::uninit();
+ expect_success(abi::get_tim(out.as_mut_ptr()), &"get_tim");
+ Instant(out.assume_init())
+ }
+ }
+
+ pub const fn zero() -> Instant {
+ Instant(0)
+ }
+
+ pub fn actually_monotonic() -> bool {
+ // There are ways to change the system time
+ false
+ }
+
+ pub fn checked_sub_instant(&self, other: &Instant) -> Option<Duration> {
+ self.0.checked_sub(other.0).map(|ticks| {
+ // `SYSTIM` is measured in microseconds
+ Duration::from_micros(ticks)
+ })
+ }
+
+ pub fn checked_add_duration(&self, other: &Duration) -> Option<Instant> {
+ // `SYSTIM` is measured in microseconds
+ let ticks = other.as_micros();
+
+ Some(Instant(self.0.checked_add(ticks.try_into().ok()?)?))
+ }
+
+ pub fn checked_sub_duration(&self, other: &Duration) -> Option<Instant> {
+ // `SYSTIM` is measured in microseconds
+ let ticks = other.as_micros();
+
+ Some(Instant(self.0.checked_sub(ticks.try_into().ok()?)?))
+ }
+}
+
+/// Split `Duration` into zero or more `RELTIM`s.
+#[inline]
+pub fn dur2reltims(dur: Duration) -> impl Iterator<Item = abi::RELTIM> {
+ // `RELTIM` is microseconds
+ let mut ticks = dur.as_micros();
+
+ crate::iter::from_fn(move || {
+ if ticks == 0 {
+ None
+ } else if ticks <= abi::TMAX_RELTIM as u128 {
+ Some(crate::mem::replace(&mut ticks, 0) as abi::RELTIM)
+ } else {
+ ticks -= abi::TMAX_RELTIM as u128;
+ Some(abi::TMAX_RELTIM)
+ }
+ })
+}
+
+/// Split `Duration` into one or more `TMO`s.
+#[inline]
+fn dur2tmos(dur: Duration) -> impl Iterator<Item = abi::TMO> {
+ // `TMO` is microseconds
+ let mut ticks = dur.as_micros();
+ let mut end = false;
+
+ crate::iter::from_fn(move || {
+ if end {
+ None
+ } else if ticks <= abi::TMAX_RELTIM as u128 {
+ end = true;
+ Some(crate::mem::replace(&mut ticks, 0) as abi::TMO)
+ } else {
+ ticks -= abi::TMAX_RELTIM as u128;
+ Some(abi::TMAX_RELTIM)
+ }
+ })
+}
+
+/// Split `Duration` into one or more API calls with timeout.
+#[inline]
+pub fn with_tmos(dur: Duration, mut f: impl FnMut(abi::TMO) -> abi::ER) -> abi::ER {
+ let mut er = abi::E_TMOUT;
+ for tmo in dur2tmos(dur) {
+ er = f(tmo);
+ if er != abi::E_TMOUT {
+ break;
+ }
+ }
+ er
+}
+
+/// Split `Duration` into one or more API calls with timeout. This function can
+/// handle spurious wakeups.
+#[inline]
+pub fn with_tmos_strong(dur: Duration, mut f: impl FnMut(abi::TMO) -> abi::ER) -> abi::ER {
+ // `TMO` and `SYSTIM` are microseconds.
+ // Clamp at `SYSTIM::MAX` for performance reasons. This shouldn't cause
+ // a problem in practice. (`u64::MAX` μs ≈ 584942 years)
+ let ticks = dur.as_micros().min(abi::SYSTIM::MAX as u128) as abi::SYSTIM;
+
+ let start = Instant::now().0;
+ let mut elapsed = 0;
+ let mut er = abi::E_TMOUT;
+ while elapsed <= ticks {
+ er = f(elapsed.min(abi::TMAX_RELTIM as abi::SYSTIM) as abi::TMO);
+ if er != abi::E_TMOUT {
+ break;
+ }
+ elapsed = Instant::now().0.wrapping_sub(start);
+ }
+
+ er
+}
+
+#[cfg(test)]
+mod tests;
--- /dev/null
+use super::*;
+
+fn reltim2dur(t: u64) -> Duration {
+ Duration::from_micros(t)
+}
+
+#[test]
+fn test_dur2reltims() {
+ assert_eq!(dur2reltims(reltim2dur(0)).collect::<Vec<_>>(), vec![]);
+ assert_eq!(dur2reltims(reltim2dur(42)).collect::<Vec<_>>(), vec![42]);
+ assert_eq!(
+ dur2reltims(reltim2dur(abi::TMAX_RELTIM as u64)).collect::<Vec<_>>(),
+ vec![abi::TMAX_RELTIM]
+ );
+ assert_eq!(
+ dur2reltims(reltim2dur(abi::TMAX_RELTIM as u64 + 10000)).collect::<Vec<_>>(),
+ vec![abi::TMAX_RELTIM, 10000]
+ );
+}
+
+#[test]
+fn test_dur2tmos() {
+ assert_eq!(dur2tmos(reltim2dur(0)).collect::<Vec<_>>(), vec![0]);
+ assert_eq!(dur2tmos(reltim2dur(42)).collect::<Vec<_>>(), vec![42]);
+ assert_eq!(
+ dur2tmos(reltim2dur(abi::TMAX_RELTIM as u64)).collect::<Vec<_>>(),
+ vec![abi::TMAX_RELTIM]
+ );
+ assert_eq!(
+ dur2tmos(reltim2dur(abi::TMAX_RELTIM as u64 + 10000)).collect::<Vec<_>>(),
+ vec![abi::TMAX_RELTIM, 10000]
+ );
+}
} else if #[cfg(windows)] {
mod windows;
pub use self::windows::*;
+ } else if #[cfg(target_os = "solid_asp3")] {
+ mod solid;
+ pub use self::solid::*;
} else if #[cfg(target_os = "hermit")] {
mod hermit;
pub use self::hermit::*;
--- /dev/null
+//! `solid_fs.h`
+use crate::os::raw::{c_char, c_int, c_uchar};
+pub use libc::{
+ blksize_t, dev_t, ino_t, off_t, stat, time_t, O_APPEND, O_CREAT, O_EXCL, O_RDONLY, O_RDWR,
+ O_TRUNC, O_WRONLY, SEEK_CUR, SEEK_END, SEEK_SET, S_IEXEC, S_IFBLK, S_IFCHR, S_IFDIR, S_IFIFO,
+ S_IFMT, S_IFREG, S_IREAD, S_IWRITE,
+};
+
+pub const O_ACCMODE: c_int = 0x3;
+
+pub const SOLID_MAX_PATH: usize = 256;
+
+#[repr(C)]
+#[derive(Copy, Clone)]
+pub struct dirent {
+ pub d_ino: ino_t,
+ pub d_type: c_uchar,
+ pub d_name: [c_char; 256usize],
+}
+
+pub const DT_UNKNOWN: c_uchar = 0;
+pub const DT_FIFO: c_uchar = 1;
+pub const DT_CHR: c_uchar = 2;
+pub const DT_DIR: c_uchar = 4;
+pub const DT_BLK: c_uchar = 6;
+pub const DT_REG: c_uchar = 8;
+pub const DT_LNK: c_uchar = 10;
+pub const DT_SOCK: c_uchar = 12;
+pub const DT_WHT: c_uchar = 14;
+
+pub type S_DIR = c_int;
+
+extern "C" {
+ pub fn SOLID_FS_Open(fd: *mut c_int, path: *const c_char, mode: c_int) -> c_int;
+ pub fn SOLID_FS_Close(fd: c_int) -> c_int;
+ pub fn SOLID_FS_Read(fd: c_int, buf: *mut u8, size: usize, result: *mut usize) -> c_int;
+ pub fn SOLID_FS_Write(fd: c_int, buf: *const u8, size: usize, result: *mut usize) -> c_int;
+ pub fn SOLID_FS_Lseek(fd: c_int, offset: off_t, whence: c_int) -> c_int;
+ pub fn SOLID_FS_Sync(fd: c_int) -> c_int;
+ pub fn SOLID_FS_Ftell(fd: c_int, result: *mut off_t) -> c_int;
+ pub fn SOLID_FS_Feof(fd: c_int, result: *mut c_int) -> c_int;
+ pub fn SOLID_FS_Fsize(fd: c_int, result: *mut usize) -> c_int;
+ pub fn SOLID_FS_Truncate(path: *const c_char, size: off_t) -> c_int;
+ pub fn SOLID_FS_OpenDir(path: *const c_char, pDir: *mut S_DIR) -> c_int;
+ pub fn SOLID_FS_CloseDir(dir: S_DIR) -> c_int;
+ pub fn SOLID_FS_ReadDir(dir: S_DIR, dirp: *mut dirent) -> c_int;
+ pub fn SOLID_FS_Stat(path: *const c_char, buf: *mut stat) -> c_int;
+ pub fn SOLID_FS_Unlink(path: *const c_char) -> c_int;
+ pub fn SOLID_FS_Rename(oldpath: *const c_char, newpath: *const c_char) -> c_int;
+ pub fn SOLID_FS_Chmod(path: *const c_char, mode: c_int) -> c_int;
+ pub fn SOLID_FS_Utime(path: *const c_char, time: time_t) -> c_int;
+ pub fn SOLID_FS_Mkdir(path: *const c_char) -> c_int;
+}
--- /dev/null
+use crate::os::raw::c_int;
+
+mod fs;
+pub mod sockets;
+pub use self::fs::*;
+
+pub const SOLID_BP_PROGRAM_EXITED: usize = 15;
+pub const SOLID_BP_CSABORT: usize = 16;
+
+#[inline(always)]
+pub fn breakpoint_program_exited(tid: usize) {
+ unsafe {
+ match () {
+ #[cfg(target_arch = "arm")]
+ () => asm!("bkpt #{}", const SOLID_BP_PROGRAM_EXITED, in("r0") tid),
+ #[cfg(target_arch = "aarch64")]
+ () => asm!("hlt #{}", const SOLID_BP_PROGRAM_EXITED, in("x0") tid),
+ }
+ }
+}
+
+#[inline(always)]
+pub fn breakpoint_abort() {
+ unsafe {
+ match () {
+ #[cfg(target_arch = "arm")]
+ () => asm!("bkpt #{}", const SOLID_BP_CSABORT),
+ #[cfg(target_arch = "aarch64")]
+ () => asm!("hlt #{}", const SOLID_BP_CSABORT),
+ }
+ }
+}
+
+// `solid_types.h`
+pub use super::itron::abi::{ER, ER_ID, E_TMOUT, ID};
+
+pub const SOLID_ERR_NOTFOUND: ER = -1000;
+pub const SOLID_ERR_NOTSUPPORTED: ER = -1001;
+pub const SOLID_ERR_EBADF: ER = -1002;
+pub const SOLID_ERR_INVALIDCONTENT: ER = -1003;
+pub const SOLID_ERR_NOTUSED: ER = -1004;
+pub const SOLID_ERR_ALREADYUSED: ER = -1005;
+pub const SOLID_ERR_OUTOFBOUND: ER = -1006;
+pub const SOLID_ERR_BADSEQUENCE: ER = -1007;
+pub const SOLID_ERR_UNKNOWNDEVICE: ER = -1008;
+pub const SOLID_ERR_BUSY: ER = -1009;
+pub const SOLID_ERR_TIMEOUT: ER = -1010;
+pub const SOLID_ERR_INVALIDACCESS: ER = -1011;
+pub const SOLID_ERR_NOTREADY: ER = -1012;
+
+// `solid_rtc.h`
+#[repr(C)]
+#[derive(Debug, Copy, Clone)]
+pub struct SOLID_RTC_TIME {
+ pub tm_sec: c_int,
+ pub tm_min: c_int,
+ pub tm_hour: c_int,
+ pub tm_mday: c_int,
+ pub tm_mon: c_int,
+ pub tm_year: c_int,
+ pub tm_wday: c_int,
+}
+
+extern "C" {
+ pub fn SOLID_RTC_ReadTime(time: *mut SOLID_RTC_TIME) -> c_int;
+}
+
+// `solid_log.h`
+extern "C" {
+ pub fn SOLID_LOG_write(s: *const u8, l: usize);
+}
+
+// `solid_mem.h`
+extern "C" {
+ pub fn SOLID_TLS_AddDestructor(id: i32, dtor: unsafe extern "C" fn(*mut u8));
+}
+
+// `solid_rng.h`
+extern "C" {
+ pub fn SOLID_RNG_SampleRandomBytes(buffer: *mut u8, length: usize) -> c_int;
+}
+
+// `rwlock.h`
+extern "C" {
+ pub fn rwl_loc_rdl(id: ID) -> ER;
+ pub fn rwl_loc_wrl(id: ID) -> ER;
+ pub fn rwl_ploc_rdl(id: ID) -> ER;
+ pub fn rwl_ploc_wrl(id: ID) -> ER;
+ pub fn rwl_unl_rwl(id: ID) -> ER;
+ pub fn rwl_acre_rwl() -> ER_ID;
+ pub fn rwl_del_rwl(id: ID) -> ER;
+}
--- /dev/null
+use crate::os::raw::{c_char, c_uint, c_void};
+pub use libc::{c_int, c_long, size_t, ssize_t, suseconds_t, time_t, timeval};
+
+pub const SOLID_NET_ERR_BASE: c_int = -2000;
+pub const EINPROGRESS: c_int = SOLID_NET_ERR_BASE - libc::EINPROGRESS;
+
+pub const AF_INET6: i32 = 10;
+pub const AF_INET: i32 = 2;
+pub const IPPROTO_IP: i32 = 0;
+pub const IPPROTO_IPV6: i32 = 41;
+pub const IPPROTO_TCP: i32 = 6;
+pub const IPV6_ADD_MEMBERSHIP: i32 = 12;
+pub const IPV6_DROP_MEMBERSHIP: i32 = 13;
+pub const IPV6_MULTICAST_LOOP: i32 = 19;
+pub const IPV6_V6ONLY: i32 = 27;
+pub const IP_TTL: i32 = 2;
+pub const IP_MULTICAST_TTL: i32 = 5;
+pub const IP_MULTICAST_LOOP: i32 = 7;
+pub const IP_ADD_MEMBERSHIP: i32 = 3;
+pub const IP_DROP_MEMBERSHIP: i32 = 4;
+pub const SHUT_RD: i32 = 0;
+pub const SHUT_RDWR: i32 = 2;
+pub const SHUT_WR: i32 = 1;
+pub const SOCK_DGRAM: i32 = 2;
+pub const SOCK_STREAM: i32 = 1;
+pub const SOL_SOCKET: i32 = 4095;
+pub const SO_BROADCAST: i32 = 32;
+pub const SO_ERROR: i32 = 4103;
+pub const SO_RCVTIMEO: i32 = 4102;
+pub const SO_REUSEADDR: i32 = 4;
+pub const SO_SNDTIMEO: i32 = 4101;
+pub const SO_LINGER: i32 = 128;
+pub const TCP_NODELAY: i32 = 1;
+pub const MSG_PEEK: c_int = 1;
+pub const FIONBIO: c_long = 0x8008667eu32 as c_long;
+pub const EAI_NONAME: i32 = -2200;
+pub const EAI_SERVICE: i32 = -2201;
+pub const EAI_FAIL: i32 = -2202;
+pub const EAI_MEMORY: i32 = -2203;
+pub const EAI_FAMILY: i32 = -2204;
+
+pub type sa_family_t = u8;
+pub type socklen_t = u32;
+pub type in_addr_t = u32;
+pub type in_port_t = u16;
+
+#[repr(C)]
+#[derive(Debug, Copy, Clone)]
+pub struct in_addr {
+ pub s_addr: in_addr_t,
+}
+
+#[repr(C)]
+#[derive(Debug, Copy, Clone)]
+pub struct in6_addr {
+ pub s6_addr: [u8; 16],
+}
+
+#[repr(C)]
+#[derive(Debug, Copy, Clone)]
+pub struct ip_mreq {
+ pub imr_multiaddr: in_addr,
+ pub imr_interface: in_addr,
+}
+
+#[repr(C)]
+#[derive(Debug, Copy, Clone)]
+pub struct ipv6_mreq {
+ pub ipv6mr_multiaddr: in6_addr,
+ pub ipv6mr_interface: c_uint,
+}
+
+#[repr(C)]
+#[derive(Debug, Copy, Clone)]
+pub struct msghdr {
+ pub msg_name: *mut c_void,
+ pub msg_namelen: socklen_t,
+ pub msg_iov: *mut iovec,
+ pub msg_iovlen: c_int,
+ pub msg_control: *mut c_void,
+ pub msg_controllen: socklen_t,
+ pub msg_flags: c_int,
+}
+
+#[repr(C)]
+#[derive(Debug, Copy, Clone)]
+pub struct sockaddr {
+ pub sa_len: u8,
+ pub sa_family: sa_family_t,
+ pub sa_data: [c_char; 14usize],
+}
+
+#[repr(C)]
+#[derive(Debug, Copy, Clone)]
+pub struct sockaddr_in {
+ pub sin_len: u8,
+ pub sin_family: sa_family_t,
+ pub sin_port: in_port_t,
+ pub sin_addr: in_addr,
+ pub sin_zero: [c_char; 8usize],
+}
+
+#[repr(C)]
+#[derive(Copy, Clone)]
+pub struct sockaddr_in6 {
+ pub sin6_len: u8,
+ pub sin6_family: sa_family_t,
+ pub sin6_port: in_port_t,
+ pub sin6_flowinfo: u32,
+ pub sin6_addr: in6_addr,
+ pub sin6_scope_id: u32,
+}
+
+#[repr(C)]
+#[derive(Debug, Copy, Clone)]
+pub struct sockaddr_storage {
+ pub s2_len: u8,
+ pub ss_family: sa_family_t,
+ pub s2_data1: [c_char; 2usize],
+ pub s2_data2: [u32; 3usize],
+}
+
+#[repr(C)]
+#[derive(Debug, Copy, Clone)]
+pub struct addrinfo {
+ pub ai_flags: c_int,
+ pub ai_family: c_int,
+ pub ai_socktype: c_int,
+ pub ai_protocol: c_int,
+ pub ai_addrlen: socklen_t,
+ pub ai_addr: *mut sockaddr,
+ pub ai_canonname: *mut c_char,
+ pub ai_next: *mut addrinfo,
+}
+
+#[repr(C)]
+#[derive(Debug, Copy, Clone)]
+pub struct linger {
+ pub l_onoff: c_int,
+ pub l_linger: c_int,
+}
+
+#[repr(C)]
+#[derive(Debug, Copy, Clone)]
+pub struct iovec {
+ pub iov_base: *mut c_void,
+ pub iov_len: usize,
+}
+
+/// This value can be chosen by an application
+pub const SOLID_NET_FD_SETSIZE: usize = 1;
+
+#[repr(C)]
+#[derive(Debug, Copy, Clone)]
+pub struct fd_set {
+ pub num_fds: usize,
+ pub fds: [c_int; SOLID_NET_FD_SETSIZE],
+}
+
+extern "C" {
+ #[link_name = "SOLID_NET_StrError"]
+ pub fn strerror(errnum: c_int) -> *const c_char;
+
+ pub fn SOLID_NET_GetLastError() -> c_int;
+
+ #[link_name = "SOLID_NET_Accept"]
+ pub fn accept(s: c_int, addr: *mut sockaddr, addrlen: *mut socklen_t) -> c_int;
+
+ #[link_name = "SOLID_NET_Bind"]
+ pub fn bind(s: c_int, name: *const sockaddr, namelen: socklen_t) -> c_int;
+
+ #[link_name = "SOLID_NET_Connect"]
+ pub fn connect(s: c_int, name: *const sockaddr, namelen: socklen_t) -> c_int;
+
+ #[link_name = "SOLID_NET_Close"]
+ pub fn close(s: c_int) -> c_int;
+
+ #[link_name = "SOLID_NET_GetPeerName"]
+ pub fn getpeername(s: c_int, name: *mut sockaddr, namelen: *mut socklen_t) -> c_int;
+
+ #[link_name = "SOLID_NET_GetSockName"]
+ pub fn getsockname(s: c_int, name: *mut sockaddr, namelen: *mut socklen_t) -> c_int;
+
+ #[link_name = "SOLID_NET_GetSockOpt"]
+ pub fn getsockopt(
+ s: c_int,
+ level: c_int,
+ optname: c_int,
+ optval: *mut c_void,
+ optlen: *mut socklen_t,
+ ) -> c_int;
+
+ #[link_name = "SOLID_NET_SetSockOpt"]
+ pub fn setsockopt(
+ s: c_int,
+ level: c_int,
+ optname: c_int,
+ optval: *const c_void,
+ optlen: socklen_t,
+ ) -> c_int;
+
+ #[link_name = "SOLID_NET_Ioctl"]
+ pub fn ioctl(s: c_int, cmd: c_long, argp: *mut c_void) -> c_int;
+
+ #[link_name = "SOLID_NET_Listen"]
+ pub fn listen(s: c_int, backlog: c_int) -> c_int;
+
+ #[link_name = "SOLID_NET_Recv"]
+ pub fn recv(s: c_int, mem: *mut c_void, len: size_t, flags: c_int) -> ssize_t;
+
+ #[link_name = "SOLID_NET_Read"]
+ pub fn read(s: c_int, mem: *mut c_void, len: size_t) -> ssize_t;
+
+ #[link_name = "SOLID_NET_Readv"]
+ pub fn readv(s: c_int, bufs: *const iovec, bufcnt: c_int) -> ssize_t;
+
+ #[link_name = "SOLID_NET_RecvFrom"]
+ pub fn recvfrom(
+ s: c_int,
+ mem: *mut c_void,
+ len: size_t,
+ flags: c_int,
+ from: *mut sockaddr,
+ fromlen: *mut socklen_t,
+ ) -> ssize_t;
+
+ #[link_name = "SOLID_NET_Send"]
+ pub fn send(s: c_int, mem: *const c_void, len: size_t, flags: c_int) -> ssize_t;
+
+ #[link_name = "SOLID_NET_SendMsg"]
+ pub fn sendmsg(s: c_int, message: *const msghdr, flags: c_int) -> ssize_t;
+
+ #[link_name = "SOLID_NET_SendTo"]
+ pub fn sendto(
+ s: c_int,
+ mem: *const c_void,
+ len: size_t,
+ flags: c_int,
+ to: *const sockaddr,
+ tolen: socklen_t,
+ ) -> ssize_t;
+
+ #[link_name = "SOLID_NET_Shutdown"]
+ pub fn shutdown(s: c_int, how: c_int) -> c_int;
+
+ #[link_name = "SOLID_NET_Socket"]
+ pub fn socket(domain: c_int, type_: c_int, protocol: c_int) -> c_int;
+
+ #[link_name = "SOLID_NET_Write"]
+ pub fn write(s: c_int, mem: *const c_void, len: size_t) -> ssize_t;
+
+ #[link_name = "SOLID_NET_Writev"]
+ pub fn writev(s: c_int, bufs: *const iovec, bufcnt: c_int) -> ssize_t;
+
+ #[link_name = "SOLID_NET_FreeAddrInfo"]
+ pub fn freeaddrinfo(ai: *mut addrinfo);
+
+ #[link_name = "SOLID_NET_GetAddrInfo"]
+ pub fn getaddrinfo(
+ nodename: *const c_char,
+ servname: *const c_char,
+ hints: *const addrinfo,
+ res: *mut *mut addrinfo,
+ ) -> c_int;
+
+ #[link_name = "SOLID_NET_Select"]
+ pub fn select(
+ maxfdp1: c_int,
+ readset: *mut fd_set,
+ writeset: *mut fd_set,
+ exceptset: *mut fd_set,
+ timeout: *mut timeval,
+ ) -> c_int;
+}
--- /dev/null
+use crate::{
+ alloc::{GlobalAlloc, Layout, System},
+ sys::common::alloc::{realloc_fallback, MIN_ALIGN},
+};
+
+#[stable(feature = "alloc_system_type", since = "1.28.0")]
+unsafe impl GlobalAlloc for System {
+ #[inline]
+ unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
+ if layout.align() <= MIN_ALIGN && layout.align() <= layout.size() {
+ unsafe { libc::malloc(layout.size()) as *mut u8 }
+ } else {
+ unsafe { libc::memalign(layout.align(), layout.size()) as *mut u8 }
+ }
+ }
+
+ #[inline]
+ unsafe fn dealloc(&self, ptr: *mut u8, _layout: Layout) {
+ unsafe { libc::free(ptr as *mut libc::c_void) }
+ }
+
+ #[inline]
+ unsafe fn realloc(&self, ptr: *mut u8, layout: Layout, new_size: usize) -> *mut u8 {
+ unsafe {
+ if layout.align() <= MIN_ALIGN && layout.align() <= new_size {
+ libc::realloc(ptr as *mut libc::c_void, new_size) as *mut u8
+ } else {
+ realloc_fallback(self, ptr, layout, new_size)
+ }
+ }
+ }
+}
--- /dev/null
+pub mod os {
+ pub const FAMILY: &str = "itron";
+ pub const OS: &str = "solid";
+ pub const DLL_PREFIX: &str = "";
+ pub const DLL_SUFFIX: &str = ".so";
+ pub const DLL_EXTENSION: &str = "so";
+ pub const EXE_SUFFIX: &str = "";
+ pub const EXE_EXTENSION: &str = "";
+}
--- /dev/null
+use super::{abi, itron, net};
+use crate::io::ErrorKind;
+
+pub use self::itron::error::{expect_success, ItronError as SolidError};
+
+/// Describe the specified SOLID error code. Returns `None` if it's an
+/// undefined error code.
+///
+/// The SOLID error codes are a superset of μITRON error codes.
+pub fn error_name(er: abi::ER) -> Option<&'static str> {
+ match er {
+ // Success
+ er if er >= 0 => None,
+ er if er < abi::sockets::SOLID_NET_ERR_BASE => net::error_name(er),
+
+ abi::SOLID_ERR_NOTFOUND => Some("not found"),
+ abi::SOLID_ERR_NOTSUPPORTED => Some("not supported"),
+ abi::SOLID_ERR_EBADF => Some("bad flags"),
+ abi::SOLID_ERR_INVALIDCONTENT => Some("invalid content"),
+ abi::SOLID_ERR_NOTUSED => Some("not used"),
+ abi::SOLID_ERR_ALREADYUSED => Some("already used"),
+ abi::SOLID_ERR_OUTOFBOUND => Some("out of bounds"),
+ abi::SOLID_ERR_BADSEQUENCE => Some("bad sequence"),
+ abi::SOLID_ERR_UNKNOWNDEVICE => Some("unknown device"),
+ abi::SOLID_ERR_BUSY => Some("busy"),
+ abi::SOLID_ERR_TIMEOUT => Some("operation timed out"),
+ abi::SOLID_ERR_INVALIDACCESS => Some("invalid access"),
+ abi::SOLID_ERR_NOTREADY => Some("not ready"),
+
+ _ => itron::error::error_name(er),
+ }
+}
+
+pub fn decode_error_kind(er: abi::ER) -> ErrorKind {
+ match er {
+ // Success
+ er if er >= 0 => ErrorKind::Uncategorized,
+ er if er < abi::sockets::SOLID_NET_ERR_BASE => net::decode_error_kind(er),
+
+ abi::SOLID_ERR_NOTFOUND => ErrorKind::NotFound,
+ abi::SOLID_ERR_NOTSUPPORTED => ErrorKind::Unsupported,
+ abi::SOLID_ERR_EBADF => ErrorKind::InvalidInput,
+ abi::SOLID_ERR_INVALIDCONTENT => ErrorKind::InvalidData,
+ // abi::SOLID_ERR_NOTUSED
+ // abi::SOLID_ERR_ALREADYUSED
+ abi::SOLID_ERR_OUTOFBOUND => ErrorKind::InvalidInput,
+ // abi::SOLID_ERR_BADSEQUENCE
+ abi::SOLID_ERR_UNKNOWNDEVICE => ErrorKind::NotFound,
+ // abi::SOLID_ERR_BUSY
+ abi::SOLID_ERR_TIMEOUT => ErrorKind::TimedOut,
+ // abi::SOLID_ERR_INVALIDACCESS
+ // abi::SOLID_ERR_NOTREADY
+ _ => itron::error::decode_error_kind(er),
+ }
+}
--- /dev/null
+use super::{abi, error};
+use crate::{
+ ffi::{CStr, CString, OsStr, OsString},
+ fmt,
+ io::{self, IoSlice, IoSliceMut, SeekFrom},
+ mem::MaybeUninit,
+ os::raw::{c_int, c_short},
+ os::solid::ffi::OsStrExt,
+ path::{Path, PathBuf},
+ sync::Arc,
+ sys::time::SystemTime,
+ sys::unsupported,
+};
+
+pub use crate::sys_common::fs::try_exists;
+
+/// A file descriptor.
+#[derive(Clone, Copy)]
+#[rustc_layout_scalar_valid_range_start(0)]
+// libstd/os/raw/mod.rs assures me that every libstd-supported platform has a
+// 32-bit c_int. Below is -2, in two's complement, but that only works out
+// because c_int is 32 bits.
+#[rustc_layout_scalar_valid_range_end(0xFF_FF_FF_FE)]
+struct FileDesc {
+ fd: c_int,
+}
+
+impl FileDesc {
+ #[inline]
+ fn new(fd: c_int) -> FileDesc {
+ assert_ne!(fd, -1i32);
+ // Safety: we just asserted that the value is in the valid range and
+ // isn't `-1` (the only value bigger than `0xFF_FF_FF_FE` unsigned)
+ unsafe { FileDesc { fd } }
+ }
+
+ #[inline]
+ fn raw(&self) -> c_int {
+ self.fd
+ }
+}
+
+pub struct File {
+ fd: FileDesc,
+}
+
+#[derive(Clone)]
+pub struct FileAttr {
+ stat: abi::stat,
+}
+
+// all DirEntry's will have a reference to this struct
+struct InnerReadDir {
+ dirp: abi::S_DIR,
+ root: PathBuf,
+}
+
+pub struct ReadDir {
+ inner: Arc<InnerReadDir>,
+}
+
+pub struct DirEntry {
+ entry: abi::dirent,
+ inner: Arc<InnerReadDir>,
+}
+
+#[derive(Clone, Debug)]
+pub struct OpenOptions {
+ // generic
+ read: bool,
+ write: bool,
+ append: bool,
+ truncate: bool,
+ create: bool,
+ create_new: bool,
+ // system-specific
+ custom_flags: i32,
+}
+
+#[derive(Clone, PartialEq, Eq, Debug)]
+pub struct FilePermissions(c_short);
+
+#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
+pub struct FileType(c_short);
+
+#[derive(Debug)]
+pub struct DirBuilder {}
+
+impl FileAttr {
+ pub fn size(&self) -> u64 {
+ self.stat.st_size as u64
+ }
+
+ pub fn perm(&self) -> FilePermissions {
+ FilePermissions(self.stat.st_mode)
+ }
+
+ pub fn file_type(&self) -> FileType {
+ FileType(self.stat.st_mode)
+ }
+
+ pub fn modified(&self) -> io::Result<SystemTime> {
+ Ok(SystemTime::from_time_t(self.stat.st_mtime))
+ }
+
+ pub fn accessed(&self) -> io::Result<SystemTime> {
+ Ok(SystemTime::from_time_t(self.stat.st_atime))
+ }
+
+ pub fn created(&self) -> io::Result<SystemTime> {
+ Ok(SystemTime::from_time_t(self.stat.st_ctime))
+ }
+}
+
+impl FilePermissions {
+ pub fn readonly(&self) -> bool {
+ (self.0 & abi::S_IWRITE) == 0
+ }
+
+ pub fn set_readonly(&mut self, readonly: bool) {
+ if readonly {
+ self.0 &= !abi::S_IWRITE;
+ } else {
+ self.0 |= abi::S_IWRITE;
+ }
+ }
+}
+
+impl FileType {
+ pub fn is_dir(&self) -> bool {
+ self.is(abi::S_IFDIR)
+ }
+ pub fn is_file(&self) -> bool {
+ self.is(abi::S_IFREG)
+ }
+ pub fn is_symlink(&self) -> bool {
+ false
+ }
+
+ pub fn is(&self, mode: c_short) -> bool {
+ self.0 & abi::S_IFMT == mode
+ }
+}
+
+pub fn readdir(p: &Path) -> io::Result<ReadDir> {
+ unsafe {
+ let mut dir = MaybeUninit::uninit();
+ error::SolidError::err_if_negative(abi::SOLID_FS_OpenDir(
+ cstr(p)?.as_ptr(),
+ dir.as_mut_ptr(),
+ ))
+ .map_err(|e| e.as_io_error())?;
+ let inner = Arc::new(InnerReadDir { dirp: dir.assume_init(), root: p.to_owned() });
+ Ok(ReadDir { inner })
+ }
+}
+
+impl fmt::Debug for ReadDir {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ // This will only be called from std::fs::ReadDir, which will add a "ReadDir()" frame.
+ // Thus the result will be e g 'ReadDir("/home")'
+ fmt::Debug::fmt(&*self.inner.root, f)
+ }
+}
+
+impl Iterator for ReadDir {
+ type Item = io::Result<DirEntry>;
+
+ fn next(&mut self) -> Option<io::Result<DirEntry>> {
+ unsafe {
+ let mut out_dirent = MaybeUninit::uninit();
+ error::SolidError::err_if_negative(abi::SOLID_FS_ReadDir(
+ self.inner.dirp,
+ out_dirent.as_mut_ptr(),
+ ))
+ .ok()?;
+ Some(Ok(DirEntry { entry: out_dirent.assume_init(), inner: Arc::clone(&self.inner) }))
+ }
+ }
+}
+
+impl Drop for InnerReadDir {
+ fn drop(&mut self) {
+ unsafe { abi::SOLID_FS_CloseDir(self.dirp) };
+ }
+}
+
+impl DirEntry {
+ pub fn path(&self) -> PathBuf {
+ self.inner.root.join(OsStr::from_bytes(
+ unsafe { CStr::from_ptr(self.entry.d_name.as_ptr()) }.to_bytes(),
+ ))
+ }
+
+ pub fn file_name(&self) -> OsString {
+ OsStr::from_bytes(unsafe { CStr::from_ptr(self.entry.d_name.as_ptr()) }.to_bytes())
+ .to_os_string()
+ }
+
+ pub fn metadata(&self) -> io::Result<FileAttr> {
+ lstat(&self.path())
+ }
+
+ pub fn file_type(&self) -> io::Result<FileType> {
+ match self.entry.d_type {
+ abi::DT_CHR => Ok(FileType(abi::S_IFCHR)),
+ abi::DT_FIFO => Ok(FileType(abi::S_IFIFO)),
+ abi::DT_REG => Ok(FileType(abi::S_IFREG)),
+ abi::DT_DIR => Ok(FileType(abi::S_IFDIR)),
+ abi::DT_BLK => Ok(FileType(abi::S_IFBLK)),
+ _ => lstat(&self.path()).map(|m| m.file_type()),
+ }
+ }
+}
+
+impl OpenOptions {
+ pub fn new() -> OpenOptions {
+ OpenOptions {
+ // generic
+ read: false,
+ write: false,
+ append: false,
+ truncate: false,
+ create: false,
+ create_new: false,
+ // system-specific
+ custom_flags: 0,
+ }
+ }
+
+ pub fn read(&mut self, read: bool) {
+ self.read = read;
+ }
+ pub fn write(&mut self, write: bool) {
+ self.write = write;
+ }
+ pub fn append(&mut self, append: bool) {
+ self.append = append;
+ }
+ pub fn truncate(&mut self, truncate: bool) {
+ self.truncate = truncate;
+ }
+ pub fn create(&mut self, create: bool) {
+ self.create = create;
+ }
+ pub fn create_new(&mut self, create_new: bool) {
+ self.create_new = create_new;
+ }
+
+ pub fn custom_flags(&mut self, flags: i32) {
+ self.custom_flags = flags;
+ }
+ pub fn mode(&mut self, _mode: u32) {}
+
+ fn get_access_mode(&self) -> io::Result<c_int> {
+ match (self.read, self.write, self.append) {
+ (true, false, false) => Ok(abi::O_RDONLY),
+ (false, true, false) => Ok(abi::O_WRONLY),
+ (true, true, false) => Ok(abi::O_RDWR),
+ (false, _, true) => Ok(abi::O_WRONLY | abi::O_APPEND),
+ (true, _, true) => Ok(abi::O_RDWR | abi::O_APPEND),
+ (false, false, false) => Err(io::Error::from_raw_os_error(libc::EINVAL)),
+ }
+ }
+
+ fn get_creation_mode(&self) -> io::Result<c_int> {
+ match (self.write, self.append) {
+ (true, false) => {}
+ (false, false) => {
+ if self.truncate || self.create || self.create_new {
+ return Err(io::Error::from_raw_os_error(libc::EINVAL));
+ }
+ }
+ (_, true) => {
+ if self.truncate && !self.create_new {
+ return Err(io::Error::from_raw_os_error(libc::EINVAL));
+ }
+ }
+ }
+
+ Ok(match (self.create, self.truncate, self.create_new) {
+ (false, false, false) => 0,
+ (true, false, false) => abi::O_CREAT,
+ (false, true, false) => abi::O_TRUNC,
+ (true, true, false) => abi::O_CREAT | abi::O_TRUNC,
+ (_, _, true) => abi::O_CREAT | abi::O_EXCL,
+ })
+ }
+}
+
+fn cstr(path: &Path) -> io::Result<CString> {
+ Ok(CString::new(path.as_os_str().as_bytes())?)
+}
+
+impl File {
+ pub fn open(path: &Path, opts: &OpenOptions) -> io::Result<File> {
+ let flags = opts.get_access_mode()?
+ | opts.get_creation_mode()?
+ | (opts.custom_flags as c_int & !abi::O_ACCMODE);
+ unsafe {
+ let mut fd = MaybeUninit::uninit();
+ error::SolidError::err_if_negative(abi::SOLID_FS_Open(
+ fd.as_mut_ptr(),
+ cstr(path)?.as_ptr(),
+ flags,
+ ))
+ .map_err(|e| e.as_io_error())?;
+ Ok(File { fd: FileDesc::new(fd.assume_init()) })
+ }
+ }
+
+ pub fn file_attr(&self) -> io::Result<FileAttr> {
+ unsupported()
+ }
+
+ pub fn fsync(&self) -> io::Result<()> {
+ self.flush()
+ }
+
+ pub fn datasync(&self) -> io::Result<()> {
+ self.flush()
+ }
+
+ pub fn truncate(&self, _size: u64) -> io::Result<()> {
+ unsupported()
+ }
+
+ pub fn read(&self, buf: &mut [u8]) -> io::Result<usize> {
+ unsafe {
+ let mut out_num_bytes = MaybeUninit::uninit();
+ error::SolidError::err_if_negative(abi::SOLID_FS_Read(
+ self.fd.raw(),
+ buf.as_mut_ptr(),
+ buf.len(),
+ out_num_bytes.as_mut_ptr(),
+ ))
+ .map_err(|e| e.as_io_error())?;
+ Ok(out_num_bytes.assume_init())
+ }
+ }
+
+ pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
+ crate::io::default_read_vectored(|buf| self.read(buf), bufs)
+ }
+
+ pub fn is_read_vectored(&self) -> bool {
+ false
+ }
+
+ pub fn write(&self, buf: &[u8]) -> io::Result<usize> {
+ unsafe {
+ let mut out_num_bytes = MaybeUninit::uninit();
+ error::SolidError::err_if_negative(abi::SOLID_FS_Write(
+ self.fd.raw(),
+ buf.as_ptr(),
+ buf.len(),
+ out_num_bytes.as_mut_ptr(),
+ ))
+ .map_err(|e| e.as_io_error())?;
+ Ok(out_num_bytes.assume_init())
+ }
+ }
+
+ pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
+ crate::io::default_write_vectored(|buf| self.write(buf), bufs)
+ }
+
+ pub fn is_write_vectored(&self) -> bool {
+ false
+ }
+
+ pub fn flush(&self) -> io::Result<()> {
+ error::SolidError::err_if_negative(unsafe { abi::SOLID_FS_Sync(self.fd.raw()) })
+ .map_err(|e| e.as_io_error())?;
+ Ok(())
+ }
+
+ pub fn seek(&self, pos: SeekFrom) -> io::Result<u64> {
+ let (whence, pos) = match pos {
+ // Casting to `i64` is fine, too large values will end up as
+ // negative which will cause an error in `SOLID_FS_Lseek`.
+ SeekFrom::Start(off) => (abi::SEEK_SET, off as i64),
+ SeekFrom::End(off) => (abi::SEEK_END, off),
+ SeekFrom::Current(off) => (abi::SEEK_CUR, off),
+ };
+ error::SolidError::err_if_negative(unsafe {
+ abi::SOLID_FS_Lseek(self.fd.raw(), pos, whence)
+ })
+ .map_err(|e| e.as_io_error())?;
+
+ // Get the new offset
+ unsafe {
+ let mut out_offset = MaybeUninit::uninit();
+ error::SolidError::err_if_negative(abi::SOLID_FS_Ftell(
+ self.fd.raw(),
+ out_offset.as_mut_ptr(),
+ ))
+ .map_err(|e| e.as_io_error())?;
+ Ok(out_offset.assume_init() as u64)
+ }
+ }
+
+ pub fn duplicate(&self) -> io::Result<File> {
+ unsupported()
+ }
+
+ pub fn set_permissions(&self, _perm: FilePermissions) -> io::Result<()> {
+ unsupported()
+ }
+}
+
+impl Drop for File {
+ fn drop(&mut self) {
+ unsafe { abi::SOLID_FS_Close(self.fd.raw()) };
+ }
+}
+
+impl DirBuilder {
+ pub fn new() -> DirBuilder {
+ DirBuilder {}
+ }
+
+ pub fn mkdir(&self, p: &Path) -> io::Result<()> {
+ error::SolidError::err_if_negative(unsafe { abi::SOLID_FS_Mkdir(cstr(p)?.as_ptr()) })
+ .map_err(|e| e.as_io_error())?;
+ Ok(())
+ }
+}
+
+impl fmt::Debug for File {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ f.debug_struct("File").field("fd", &self.fd.raw()).finish()
+ }
+}
+
+pub fn unlink(p: &Path) -> io::Result<()> {
+ if stat(p)?.file_type().is_dir() {
+ Err(io::Error::new_const(io::ErrorKind::IsADirectory, &"is a directory"))
+ } else {
+ error::SolidError::err_if_negative(unsafe { abi::SOLID_FS_Unlink(cstr(p)?.as_ptr()) })
+ .map_err(|e| e.as_io_error())?;
+ Ok(())
+ }
+}
+
+pub fn rename(old: &Path, new: &Path) -> io::Result<()> {
+ error::SolidError::err_if_negative(unsafe {
+ abi::SOLID_FS_Rename(cstr(old)?.as_ptr(), cstr(new)?.as_ptr())
+ })
+ .map_err(|e| e.as_io_error())?;
+ Ok(())
+}
+
+pub fn set_perm(p: &Path, perm: FilePermissions) -> io::Result<()> {
+ error::SolidError::err_if_negative(unsafe {
+ abi::SOLID_FS_Chmod(cstr(p)?.as_ptr(), perm.0.into())
+ })
+ .map_err(|e| e.as_io_error())?;
+ Ok(())
+}
+
+pub fn rmdir(p: &Path) -> io::Result<()> {
+ if stat(p)?.file_type().is_dir() {
+ error::SolidError::err_if_negative(unsafe { abi::SOLID_FS_Unlink(cstr(p)?.as_ptr()) })
+ .map_err(|e| e.as_io_error())?;
+ Ok(())
+ } else {
+ Err(io::Error::new_const(io::ErrorKind::NotADirectory, &"not a directory"))
+ }
+}
+
+pub fn remove_dir_all(path: &Path) -> io::Result<()> {
+ for child in readdir(path)? {
+ let child = child?;
+ let child_type = child.file_type()?;
+ if child_type.is_dir() {
+ remove_dir_all(&child.path())?;
+ } else {
+ unlink(&child.path())?;
+ }
+ }
+ rmdir(path)
+}
+
+pub fn readlink(p: &Path) -> io::Result<PathBuf> {
+ // This target doesn't support symlinks
+ stat(p)?;
+ Err(io::Error::new_const(io::ErrorKind::InvalidInput, &"not a symbolic link"))
+}
+
+pub fn symlink(_original: &Path, _link: &Path) -> io::Result<()> {
+ // This target doesn't support symlinks
+ unsupported()
+}
+
+pub fn link(_src: &Path, _dst: &Path) -> io::Result<()> {
+ // This target doesn't support symlinks
+ unsupported()
+}
+
+pub fn stat(p: &Path) -> io::Result<FileAttr> {
+ // This target doesn't support symlinks
+ lstat(p)
+}
+
+pub fn lstat(p: &Path) -> io::Result<FileAttr> {
+ unsafe {
+ let mut out_stat = MaybeUninit::uninit();
+ error::SolidError::err_if_negative(abi::SOLID_FS_Stat(
+ cstr(p)?.as_ptr(),
+ out_stat.as_mut_ptr(),
+ ))
+ .map_err(|e| e.as_io_error())?;
+ Ok(FileAttr { stat: out_stat.assume_init() })
+ }
+}
+
+pub fn canonicalize(_p: &Path) -> io::Result<PathBuf> {
+ unsupported()
+}
+
+pub fn copy(from: &Path, to: &Path) -> io::Result<u64> {
+ use crate::fs::File;
+
+ let mut reader = File::open(from)?;
+ let mut writer = File::create(to)?;
+
+ io::copy(&mut reader, &mut writer)
+}
--- /dev/null
+use crate::marker::PhantomData;
+use crate::slice;
+
+use super::abi::sockets::iovec;
+use libc::c_void;
+
+#[derive(Copy, Clone)]
+#[repr(transparent)]
+pub struct IoSlice<'a> {
+ vec: iovec,
+ _p: PhantomData<&'a [u8]>,
+}
+
+impl<'a> IoSlice<'a> {
+ #[inline]
+ pub fn new(buf: &'a [u8]) -> IoSlice<'a> {
+ IoSlice {
+ vec: iovec { iov_base: buf.as_ptr() as *mut u8 as *mut c_void, iov_len: buf.len() },
+ _p: PhantomData,
+ }
+ }
+
+ #[inline]
+ pub fn advance(&mut self, n: usize) {
+ if self.vec.iov_len < n {
+ panic!("advancing IoSlice beyond its length");
+ }
+
+ unsafe {
+ self.vec.iov_len -= n;
+ self.vec.iov_base = self.vec.iov_base.add(n);
+ }
+ }
+
+ #[inline]
+ pub fn as_slice(&self) -> &[u8] {
+ unsafe { slice::from_raw_parts(self.vec.iov_base as *mut u8, self.vec.iov_len) }
+ }
+}
+
+#[repr(transparent)]
+pub struct IoSliceMut<'a> {
+ vec: iovec,
+ _p: PhantomData<&'a mut [u8]>,
+}
+
+impl<'a> IoSliceMut<'a> {
+ #[inline]
+ pub fn new(buf: &'a mut [u8]) -> IoSliceMut<'a> {
+ IoSliceMut {
+ vec: iovec { iov_base: buf.as_mut_ptr() as *mut c_void, iov_len: buf.len() },
+ _p: PhantomData,
+ }
+ }
+
+ #[inline]
+ pub fn advance(&mut self, n: usize) {
+ if self.vec.iov_len < n {
+ panic!("advancing IoSliceMut beyond its length");
+ }
+
+ unsafe {
+ self.vec.iov_len -= n;
+ self.vec.iov_base = self.vec.iov_base.add(n);
+ }
+ }
+
+ #[inline]
+ pub fn as_slice(&self) -> &[u8] {
+ unsafe { slice::from_raw_parts(self.vec.iov_base as *mut u8, self.vec.iov_len) }
+ }
+
+ #[inline]
+ pub fn as_mut_slice(&mut self) -> &mut [u8] {
+ unsafe { slice::from_raw_parts_mut(self.vec.iov_base as *mut u8, self.vec.iov_len) }
+ }
+}
--- /dev/null
+pub fn memchr(needle: u8, haystack: &[u8]) -> Option<usize> {
+ let p = unsafe {
+ libc::memchr(
+ haystack.as_ptr() as *const libc::c_void,
+ needle as libc::c_int,
+ haystack.len(),
+ )
+ };
+ if p.is_null() { None } else { Some(p as usize - (haystack.as_ptr() as usize)) }
+}
+
+pub fn memrchr(needle: u8, haystack: &[u8]) -> Option<usize> {
+ let p = unsafe {
+ libc::memrchr(
+ haystack.as_ptr() as *const libc::c_void,
+ needle as libc::c_int,
+ haystack.len(),
+ )
+ };
+ if p.is_null() { None } else { Some(p as usize - (haystack.as_ptr() as usize)) }
+}
--- /dev/null
+#![allow(dead_code)]
+#![allow(missing_docs, nonstandard_style)]
+#![deny(unsafe_op_in_unsafe_fn)]
+
+mod abi;
+
+#[path = "../itron"]
+mod itron {
+ pub(super) mod abi;
+ pub mod condvar;
+ pub(super) mod error;
+ pub mod mutex;
+ pub(super) mod spin;
+ pub(super) mod task;
+ pub mod thread;
+ pub(super) mod time;
+ use super::unsupported;
+}
+
+pub mod alloc;
+#[path = "../unsupported/args.rs"]
+pub mod args;
+#[path = "../unix/cmath.rs"]
+pub mod cmath;
+pub mod env;
+// `error` is `pub(crate)` so that it can be accessed by `itron/error.rs` as
+// `crate::sys::error`
+pub(crate) mod error;
+pub mod fs;
+pub mod io;
+pub mod net;
+pub mod os;
+#[path = "../unix/os_str.rs"]
+pub mod os_str;
+pub mod path;
+#[path = "../unsupported/pipe.rs"]
+pub mod pipe;
+#[path = "../unsupported/process.rs"]
+pub mod process;
+pub mod rwlock;
+pub mod stdio;
+pub use self::itron::{condvar, mutex, thread};
+pub mod memchr;
+pub mod thread_local_dtor;
+pub mod thread_local_key;
+pub mod time;
+
+// SAFETY: must be called only once during runtime initialization.
+// NOTE: this is not guaranteed to run, for example when Rust code is called externally.
+pub unsafe fn init(_argc: isize, _argv: *const *const u8) {}
+
+// SAFETY: must be called only once during runtime cleanup.
+pub unsafe fn cleanup() {}
+
+pub fn unsupported<T>() -> crate::io::Result<T> {
+ Err(unsupported_err())
+}
+
+pub fn unsupported_err() -> crate::io::Error {
+ crate::io::Error::new_const(
+ crate::io::ErrorKind::Unsupported,
+ &"operation not supported on this platform",
+ )
+}
+
+pub fn decode_error_kind(code: i32) -> crate::io::ErrorKind {
+ error::decode_error_kind(code)
+}
+
+#[inline(always)]
+pub fn abort_internal() -> ! {
+ loop {
+ abi::breakpoint_abort();
+ }
+}
+
+// This function is needed by the panic runtime. The symbol is named in
+// pre-link args for the target specification, so keep that in sync.
+#[cfg(not(test))]
+#[no_mangle]
+// NB. used by both libunwind and libpanic_abort
+pub extern "C" fn __rust_abort() {
+ abort_internal();
+}
+
+pub fn hashmap_random_keys() -> (u64, u64) {
+ unsafe {
+ let mut out = crate::mem::MaybeUninit::<[u64; 2]>::uninit();
+ let result = abi::SOLID_RNG_SampleRandomBytes(out.as_mut_ptr() as *mut u8, 16);
+ assert_eq!(result, 0, "SOLID_RNG_SampleRandomBytes failed: {}", result);
+ let [x1, x2] = out.assume_init();
+ (x1, x2)
+ }
+}
+
+pub use libc::strlen;
--- /dev/null
+use super::abi;
+use crate::{
+ cmp,
+ ffi::CStr,
+ io::{self, ErrorKind, IoSlice, IoSliceMut},
+ mem,
+ net::{Shutdown, SocketAddr},
+ ptr, str,
+ sys_common::net::{getsockopt, setsockopt, sockaddr_to_addr},
+ sys_common::{AsInner, FromInner, IntoInner},
+ time::Duration,
+};
+
+use self::netc::{sockaddr, socklen_t, MSG_PEEK};
+use libc::{c_int, c_void, size_t};
+
+pub mod netc {
+ pub use super::super::abi::sockets::*;
+}
+
+pub type wrlen_t = size_t;
+
+const READ_LIMIT: usize = libc::ssize_t::MAX as usize;
+
+const fn max_iov() -> usize {
+ // Judging by the source code, it's unlimited, but specify a lower
+ // value just in case.
+ 1024
+}
+
+/// A file descriptor.
+#[rustc_layout_scalar_valid_range_start(0)]
+// libstd/os/raw/mod.rs assures me that every libstd-supported platform has a
+// 32-bit c_int. Below is -2, in two's complement, but that only works out
+// because c_int is 32 bits.
+#[rustc_layout_scalar_valid_range_end(0xFF_FF_FF_FE)]
+struct FileDesc {
+ fd: c_int,
+}
+
+impl FileDesc {
+ #[inline]
+ fn new(fd: c_int) -> FileDesc {
+ assert_ne!(fd, -1i32);
+ // Safety: we just asserted that the value is in the valid range and
+ // isn't `-1` (the only value bigger than `0xFF_FF_FF_FE` unsigned)
+ unsafe { FileDesc { fd } }
+ }
+
+ #[inline]
+ fn raw(&self) -> c_int {
+ self.fd
+ }
+
+ /// Extracts the actual file descriptor without closing it.
+ #[inline]
+ fn into_raw(self) -> c_int {
+ let fd = self.fd;
+ mem::forget(self);
+ fd
+ }
+
+ fn read(&self, buf: &mut [u8]) -> io::Result<usize> {
+ let ret = cvt(unsafe {
+ netc::read(self.fd, buf.as_mut_ptr() as *mut c_void, cmp::min(buf.len(), READ_LIMIT))
+ })?;
+ Ok(ret as usize)
+ }
+
+ fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
+ let ret = cvt(unsafe {
+ netc::readv(
+ self.fd,
+ bufs.as_ptr() as *const netc::iovec,
+ cmp::min(bufs.len(), max_iov()) as c_int,
+ )
+ })?;
+ Ok(ret as usize)
+ }
+
+ #[inline]
+ fn is_read_vectored(&self) -> bool {
+ true
+ }
+
+ fn write(&self, buf: &[u8]) -> io::Result<usize> {
+ let ret = cvt(unsafe {
+ netc::write(self.fd, buf.as_ptr() as *const c_void, cmp::min(buf.len(), READ_LIMIT))
+ })?;
+ Ok(ret as usize)
+ }
+
+ fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
+ let ret = cvt(unsafe {
+ netc::writev(
+ self.fd,
+ bufs.as_ptr() as *const netc::iovec,
+ cmp::min(bufs.len(), max_iov()) as c_int,
+ )
+ })?;
+ Ok(ret as usize)
+ }
+
+ #[inline]
+ fn is_write_vectored(&self) -> bool {
+ true
+ }
+
+ fn duplicate(&self) -> io::Result<FileDesc> {
+ super::unsupported()
+ }
+}
+
+impl AsInner<c_int> for FileDesc {
+ fn as_inner(&self) -> &c_int {
+ &self.fd
+ }
+}
+
+impl Drop for FileDesc {
+ fn drop(&mut self) {
+ unsafe { netc::close(self.fd) };
+ }
+}
+
+#[doc(hidden)]
+pub trait IsMinusOne {
+ fn is_minus_one(&self) -> bool;
+}
+
+macro_rules! impl_is_minus_one {
+ ($($t:ident)*) => ($(impl IsMinusOne for $t {
+ fn is_minus_one(&self) -> bool {
+ *self == -1
+ }
+ })*)
+}
+
+impl_is_minus_one! { i8 i16 i32 i64 isize }
+
+pub fn cvt<T: IsMinusOne>(t: T) -> io::Result<T> {
+ if t.is_minus_one() { Err(last_error()) } else { Ok(t) }
+}
+
+/// A variant of `cvt` for `getaddrinfo` which return 0 for a success.
+pub fn cvt_gai(err: c_int) -> io::Result<()> {
+ if err == 0 {
+ Ok(())
+ } else {
+ let msg: &dyn crate::fmt::Display = match err {
+ netc::EAI_NONAME => &"name or service not known",
+ netc::EAI_SERVICE => &"service not supported",
+ netc::EAI_FAIL => &"non-recoverable failure in name resolution",
+ netc::EAI_MEMORY => &"memory allocation failure",
+ netc::EAI_FAMILY => &"family not supported",
+ _ => &err,
+ };
+ Err(io::Error::new(
+ io::ErrorKind::Uncategorized,
+ &format!("failed to lookup address information: {}", msg)[..],
+ ))
+ }
+}
+
+/// Just to provide the same interface as sys/unix/net.rs
+pub fn cvt_r<T, F>(mut f: F) -> io::Result<T>
+where
+ T: IsMinusOne,
+ F: FnMut() -> T,
+{
+ cvt(f())
+}
+
+/// Returns the last error from the network subsystem.
+fn last_error() -> io::Error {
+ io::Error::from_raw_os_error(unsafe { netc::SOLID_NET_GetLastError() })
+}
+
+pub(super) fn error_name(er: abi::ER) -> Option<&'static str> {
+ unsafe { CStr::from_ptr(netc::strerror(er)) }.to_str().ok()
+}
+
+pub(super) fn decode_error_kind(er: abi::ER) -> ErrorKind {
+ let errno = netc::SOLID_NET_ERR_BASE - er;
+ match errno as libc::c_int {
+ libc::ECONNREFUSED => ErrorKind::ConnectionRefused,
+ libc::ECONNRESET => ErrorKind::ConnectionReset,
+ libc::EPERM | libc::EACCES => ErrorKind::PermissionDenied,
+ libc::EPIPE => ErrorKind::BrokenPipe,
+ libc::ENOTCONN => ErrorKind::NotConnected,
+ libc::ECONNABORTED => ErrorKind::ConnectionAborted,
+ libc::EADDRNOTAVAIL => ErrorKind::AddrNotAvailable,
+ libc::EADDRINUSE => ErrorKind::AddrInUse,
+ libc::ENOENT => ErrorKind::NotFound,
+ libc::EINTR => ErrorKind::Interrupted,
+ libc::EINVAL => ErrorKind::InvalidInput,
+ libc::ETIMEDOUT => ErrorKind::TimedOut,
+ libc::EEXIST => ErrorKind::AlreadyExists,
+ libc::ENOSYS => ErrorKind::Unsupported,
+ libc::ENOMEM => ErrorKind::OutOfMemory,
+ libc::EAGAIN => ErrorKind::WouldBlock,
+
+ _ => ErrorKind::Uncategorized,
+ }
+}
+
+pub fn init() {}
+
+pub struct Socket(FileDesc);
+
+impl Socket {
+ pub fn new(addr: &SocketAddr, ty: c_int) -> io::Result<Socket> {
+ let fam = match *addr {
+ SocketAddr::V4(..) => netc::AF_INET,
+ SocketAddr::V6(..) => netc::AF_INET6,
+ };
+ Socket::new_raw(fam, ty)
+ }
+
+ pub fn new_raw(fam: c_int, ty: c_int) -> io::Result<Socket> {
+ unsafe {
+ let fd = cvt(netc::socket(fam, ty, 0))?;
+ let fd = FileDesc::new(fd);
+ let socket = Socket(fd);
+
+ Ok(socket)
+ }
+ }
+
+ pub fn connect_timeout(&self, addr: &SocketAddr, timeout: Duration) -> io::Result<()> {
+ self.set_nonblocking(true)?;
+ let r = unsafe {
+ let (addrp, len) = addr.into_inner();
+ cvt(netc::connect(self.0.raw(), addrp, len))
+ };
+ self.set_nonblocking(false)?;
+
+ match r {
+ Ok(_) => return Ok(()),
+ // there's no ErrorKind for EINPROGRESS
+ Err(ref e) if e.raw_os_error() == Some(netc::EINPROGRESS) => {}
+ Err(e) => return Err(e),
+ }
+
+ if timeout.as_secs() == 0 && timeout.subsec_nanos() == 0 {
+ return Err(io::Error::new_const(
+ io::ErrorKind::InvalidInput,
+ &"cannot set a 0 duration timeout",
+ ));
+ }
+
+ let mut timeout =
+ netc::timeval { tv_sec: timeout.as_secs() as _, tv_usec: timeout.subsec_micros() as _ };
+ if timeout.tv_sec == 0 && timeout.tv_usec == 0 {
+ timeout.tv_usec = 1;
+ }
+
+ let fds = netc::fd_set { num_fds: 1, fds: [self.0.raw()] };
+
+ let mut writefds = fds;
+ let mut errorfds = fds;
+
+ let n = unsafe {
+ cvt(netc::select(
+ self.0.raw() + 1,
+ ptr::null_mut(),
+ &mut writefds,
+ &mut errorfds,
+ &mut timeout,
+ ))?
+ };
+
+ match n {
+ 0 => Err(io::Error::new_const(io::ErrorKind::TimedOut, &"connection timed out")),
+ _ => {
+ let can_write = writefds.num_fds != 0;
+ if !can_write {
+ if let Some(e) = self.take_error()? {
+ return Err(e);
+ }
+ }
+ Ok(())
+ }
+ }
+ }
+
+ pub fn accept(&self, storage: *mut sockaddr, len: *mut socklen_t) -> io::Result<Socket> {
+ let fd = cvt_r(|| unsafe { netc::accept(self.0.raw(), storage, len) })?;
+ let fd = FileDesc::new(fd);
+ Ok(Socket(fd))
+ }
+
+ pub fn duplicate(&self) -> io::Result<Socket> {
+ self.0.duplicate().map(Socket)
+ }
+
+ fn recv_with_flags(&self, buf: &mut [u8], flags: c_int) -> io::Result<usize> {
+ let ret = cvt(unsafe {
+ netc::recv(self.0.raw(), buf.as_mut_ptr() as *mut c_void, buf.len(), flags)
+ })?;
+ Ok(ret as usize)
+ }
+
+ pub fn read(&self, buf: &mut [u8]) -> io::Result<usize> {
+ self.recv_with_flags(buf, 0)
+ }
+
+ pub fn peek(&self, buf: &mut [u8]) -> io::Result<usize> {
+ self.recv_with_flags(buf, MSG_PEEK)
+ }
+
+ pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
+ self.0.read_vectored(bufs)
+ }
+
+ #[inline]
+ pub fn is_read_vectored(&self) -> bool {
+ self.0.is_read_vectored()
+ }
+
+ fn recv_from_with_flags(
+ &self,
+ buf: &mut [u8],
+ flags: c_int,
+ ) -> io::Result<(usize, SocketAddr)> {
+ let mut storage: netc::sockaddr_storage = unsafe { mem::zeroed() };
+ let mut addrlen = mem::size_of_val(&storage) as netc::socklen_t;
+
+ let n = cvt(unsafe {
+ netc::recvfrom(
+ self.0.raw(),
+ buf.as_mut_ptr() as *mut c_void,
+ buf.len(),
+ flags,
+ &mut storage as *mut _ as *mut _,
+ &mut addrlen,
+ )
+ })?;
+ Ok((n as usize, sockaddr_to_addr(&storage, addrlen as usize)?))
+ }
+
+ pub fn recv_from(&self, buf: &mut [u8]) -> io::Result<(usize, SocketAddr)> {
+ self.recv_from_with_flags(buf, 0)
+ }
+
+ pub fn peek_from(&self, buf: &mut [u8]) -> io::Result<(usize, SocketAddr)> {
+ self.recv_from_with_flags(buf, MSG_PEEK)
+ }
+
+ pub fn write(&self, buf: &[u8]) -> io::Result<usize> {
+ self.0.write(buf)
+ }
+
+ pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
+ self.0.write_vectored(bufs)
+ }
+
+ #[inline]
+ pub fn is_write_vectored(&self) -> bool {
+ self.0.is_write_vectored()
+ }
+
+ pub fn set_timeout(&self, dur: Option<Duration>, kind: c_int) -> io::Result<()> {
+ let timeout = match dur {
+ Some(dur) => {
+ if dur.as_secs() == 0 && dur.subsec_nanos() == 0 {
+ return Err(io::Error::new_const(
+ io::ErrorKind::InvalidInput,
+ &"cannot set a 0 duration timeout",
+ ));
+ }
+
+ let secs = if dur.as_secs() > netc::c_long::MAX as u64 {
+ netc::c_long::MAX
+ } else {
+ dur.as_secs() as netc::c_long
+ };
+ let mut timeout = netc::timeval { tv_sec: secs, tv_usec: dur.subsec_micros() as _ };
+ if timeout.tv_sec == 0 && timeout.tv_usec == 0 {
+ timeout.tv_usec = 1;
+ }
+ timeout
+ }
+ None => netc::timeval { tv_sec: 0, tv_usec: 0 },
+ };
+ setsockopt(self, netc::SOL_SOCKET, kind, timeout)
+ }
+
+ pub fn timeout(&self, kind: c_int) -> io::Result<Option<Duration>> {
+ let raw: netc::timeval = getsockopt(self, netc::SOL_SOCKET, kind)?;
+ if raw.tv_sec == 0 && raw.tv_usec == 0 {
+ Ok(None)
+ } else {
+ let sec = raw.tv_sec as u64;
+ let nsec = (raw.tv_usec as u32) * 1000;
+ Ok(Some(Duration::new(sec, nsec)))
+ }
+ }
+
+ pub fn shutdown(&self, how: Shutdown) -> io::Result<()> {
+ let how = match how {
+ Shutdown::Write => netc::SHUT_WR,
+ Shutdown::Read => netc::SHUT_RD,
+ Shutdown::Both => netc::SHUT_RDWR,
+ };
+ cvt(unsafe { netc::shutdown(self.0.raw(), how) })?;
+ Ok(())
+ }
+
+ pub fn set_linger(&self, linger: Option<Duration>) -> io::Result<()> {
+ let linger = netc::linger {
+ l_onoff: linger.is_some() as netc::c_int,
+ l_linger: linger.unwrap_or_default().as_secs() as netc::c_int,
+ };
+
+ setsockopt(self, netc::SOL_SOCKET, netc::SO_LINGER, linger)
+ }
+
+ pub fn linger(&self) -> io::Result<Option<Duration>> {
+ let val: netc::linger = getsockopt(self, netc::SOL_SOCKET, netc::SO_LINGER)?;
+
+ Ok((val.l_onoff != 0).then(|| Duration::from_secs(val.l_linger as u64)))
+ }
+
+ pub fn set_nodelay(&self, nodelay: bool) -> io::Result<()> {
+ setsockopt(self, netc::IPPROTO_TCP, netc::TCP_NODELAY, nodelay as c_int)
+ }
+
+ pub fn nodelay(&self) -> io::Result<bool> {
+ let raw: c_int = getsockopt(self, netc::IPPROTO_TCP, netc::TCP_NODELAY)?;
+ Ok(raw != 0)
+ }
+
+ pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> {
+ let mut nonblocking = nonblocking as c_int;
+ cvt(unsafe {
+ netc::ioctl(*self.as_inner(), netc::FIONBIO, (&mut nonblocking) as *mut c_int as _)
+ })
+ .map(drop)
+ }
+
+ pub fn take_error(&self) -> io::Result<Option<io::Error>> {
+ let raw: c_int = getsockopt(self, netc::SOL_SOCKET, netc::SO_ERROR)?;
+ if raw == 0 { Ok(None) } else { Ok(Some(io::Error::from_raw_os_error(raw as i32))) }
+ }
+
+ // This method is used by sys_common code to abstract over targets.
+ pub fn as_raw(&self) -> c_int {
+ *self.as_inner()
+ }
+}
+
+impl AsInner<c_int> for Socket {
+ fn as_inner(&self) -> &c_int {
+ self.0.as_inner()
+ }
+}
+
+impl FromInner<c_int> for Socket {
+ fn from_inner(fd: c_int) -> Socket {
+ Socket(FileDesc::new(fd))
+ }
+}
+
+impl IntoInner<c_int> for Socket {
+ fn into_inner(self) -> c_int {
+ self.0.into_raw()
+ }
+}
--- /dev/null
+use super::unsupported;
+use crate::error::Error as StdError;
+use crate::ffi::{CStr, CString, OsStr, OsString};
+use crate::fmt;
+use crate::io;
+use crate::os::{
+ raw::{c_char, c_int},
+ solid::ffi::{OsStrExt, OsStringExt},
+};
+use crate::path::{self, PathBuf};
+use crate::sys_common::rwlock::StaticRWLock;
+use crate::vec;
+
+use super::{abi, error, itron, memchr};
+
+// `solid` directly maps `errno`s to μITRON error codes.
+impl itron::error::ItronError {
+ #[inline]
+ pub(crate) fn as_io_error(self) -> crate::io::Error {
+ crate::io::Error::from_raw_os_error(self.as_raw())
+ }
+}
+
+pub fn errno() -> i32 {
+ 0
+}
+
+pub fn error_string(errno: i32) -> String {
+ if let Some(name) = error::error_name(errno) { name.to_owned() } else { format!("{}", errno) }
+}
+
+pub fn getcwd() -> io::Result<PathBuf> {
+ unsupported()
+}
+
+pub fn chdir(_: &path::Path) -> io::Result<()> {
+ unsupported()
+}
+
+pub struct SplitPaths<'a>(&'a !);
+
+pub fn split_paths(_unparsed: &OsStr) -> SplitPaths<'_> {
+ panic!("unsupported")
+}
+
+impl<'a> Iterator for SplitPaths<'a> {
+ type Item = PathBuf;
+ fn next(&mut self) -> Option<PathBuf> {
+ *self.0
+ }
+}
+
+#[derive(Debug)]
+pub struct JoinPathsError;
+
+pub fn join_paths<I, T>(_paths: I) -> Result<OsString, JoinPathsError>
+where
+ I: Iterator<Item = T>,
+ T: AsRef<OsStr>,
+{
+ Err(JoinPathsError)
+}
+
+impl fmt::Display for JoinPathsError {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ "not supported on this platform yet".fmt(f)
+ }
+}
+
+impl StdError for JoinPathsError {
+ #[allow(deprecated)]
+ fn description(&self) -> &str {
+ "not supported on this platform yet"
+ }
+}
+
+pub fn current_exe() -> io::Result<PathBuf> {
+ unsupported()
+}
+
+static ENV_LOCK: StaticRWLock = StaticRWLock::new();
+
+pub struct Env {
+ iter: vec::IntoIter<(OsString, OsString)>,
+}
+
+impl !Send for Env {}
+impl !Sync for Env {}
+
+impl Iterator for Env {
+ type Item = (OsString, OsString);
+ fn next(&mut self) -> Option<(OsString, OsString)> {
+ self.iter.next()
+ }
+ fn size_hint(&self) -> (usize, Option<usize>) {
+ self.iter.size_hint()
+ }
+}
+
+/// Returns a vector of (variable, value) byte-vector pairs for all the
+/// environment variables of the current process.
+pub fn env() -> Env {
+ extern "C" {
+ static mut environ: *const *const c_char;
+ }
+
+ unsafe {
+ let _guard = ENV_LOCK.read();
+ let mut result = Vec::new();
+ if !environ.is_null() {
+ while !(*environ).is_null() {
+ if let Some(key_value) = parse(CStr::from_ptr(*environ).to_bytes()) {
+ result.push(key_value);
+ }
+ environ = environ.add(1);
+ }
+ }
+ return Env { iter: result.into_iter() };
+ }
+
+ fn parse(input: &[u8]) -> Option<(OsString, OsString)> {
+ // Strategy (copied from glibc): Variable name and value are separated
+ // by an ASCII equals sign '='. Since a variable name must not be
+ // empty, allow variable names starting with an equals sign. Skip all
+ // malformed lines.
+ if input.is_empty() {
+ return None;
+ }
+ let pos = memchr::memchr(b'=', &input[1..]).map(|p| p + 1);
+ pos.map(|p| {
+ (
+ OsStringExt::from_vec(input[..p].to_vec()),
+ OsStringExt::from_vec(input[p + 1..].to_vec()),
+ )
+ })
+ }
+}
+
+pub fn getenv(k: &OsStr) -> Option<OsString> {
+ // environment variables with a nul byte can't be set, so their value is
+ // always None as well
+ let k = CString::new(k.as_bytes()).ok()?;
+ unsafe {
+ let _guard = ENV_LOCK.read();
+ let s = libc::getenv(k.as_ptr()) as *const libc::c_char;
+ if s.is_null() {
+ None
+ } else {
+ Some(OsStringExt::from_vec(CStr::from_ptr(s).to_bytes().to_vec()))
+ }
+ }
+}
+
+pub fn setenv(k: &OsStr, v: &OsStr) -> io::Result<()> {
+ let k = CString::new(k.as_bytes())?;
+ let v = CString::new(v.as_bytes())?;
+
+ unsafe {
+ let _guard = ENV_LOCK.write();
+ cvt_env(libc::setenv(k.as_ptr(), v.as_ptr(), 1)).map(drop)
+ }
+}
+
+pub fn unsetenv(n: &OsStr) -> io::Result<()> {
+ let nbuf = CString::new(n.as_bytes())?;
+
+ unsafe {
+ let _guard = ENV_LOCK.write();
+ cvt_env(libc::unsetenv(nbuf.as_ptr())).map(drop)
+ }
+}
+
+/// In kmclib, `setenv` and `unsetenv` don't always set `errno`, so this
+/// function just returns a generic error.
+fn cvt_env(t: c_int) -> io::Result<c_int> {
+ if t == -1 {
+ Err(io::Error::new_const(io::ErrorKind::Uncategorized, &"failure"))
+ } else {
+ Ok(t)
+ }
+}
+
+pub fn temp_dir() -> PathBuf {
+ panic!("no standard temporary directory on this platform")
+}
+
+pub fn home_dir() -> Option<PathBuf> {
+ None
+}
+
+pub fn exit(_code: i32) -> ! {
+ let tid = itron::task::try_current_task_id().unwrap_or(0);
+ loop {
+ abi::breakpoint_program_exited(tid as usize);
+ }
+}
+
+pub fn getpid() -> u32 {
+ panic!("no pids on this platform")
+}
--- /dev/null
+use crate::ffi::OsStr;
+use crate::path::Prefix;
+
+#[inline]
+pub fn is_sep_byte(b: u8) -> bool {
+ b == b'\\'
+}
+
+#[inline]
+pub fn is_verbatim_sep(b: u8) -> bool {
+ b == b'\\'
+}
+
+pub fn parse_prefix(_: &OsStr) -> Option<Prefix<'_>> {
+ None
+}
+
+pub const MAIN_SEP_STR: &str = "\\";
+pub const MAIN_SEP: char = '\\';
--- /dev/null
+//! A readers-writer lock implementation backed by the SOLID kernel extension.
+use super::{
+ abi,
+ itron::{
+ error::{expect_success, expect_success_aborting, fail, ItronError},
+ spin::SpinIdOnceCell,
+ },
+};
+
+pub struct RWLock {
+ /// The ID of the underlying mutex object
+ rwl: SpinIdOnceCell<()>,
+}
+
+pub type MovableRWLock = RWLock;
+
+// Safety: `num_readers` is protected by `mtx_num_readers`
+unsafe impl Send for RWLock {}
+unsafe impl Sync for RWLock {}
+
+fn new_rwl() -> Result<abi::ID, ItronError> {
+ ItronError::err_if_negative(unsafe { abi::rwl_acre_rwl() })
+}
+
+impl RWLock {
+ pub const fn new() -> RWLock {
+ RWLock { rwl: SpinIdOnceCell::new() }
+ }
+
+ /// Get the inner mutex's ID, which is lazily created.
+ fn raw(&self) -> abi::ID {
+ match self.rwl.get_or_try_init(|| new_rwl().map(|id| (id, ()))) {
+ Ok((id, ())) => id,
+ Err(e) => fail(e, &"rwl_acre_rwl"),
+ }
+ }
+
+ #[inline]
+ pub unsafe fn read(&self) {
+ let rwl = self.raw();
+ expect_success(unsafe { abi::rwl_loc_rdl(rwl) }, &"rwl_loc_rdl");
+ }
+
+ #[inline]
+ pub unsafe fn try_read(&self) -> bool {
+ let rwl = self.raw();
+ match unsafe { abi::rwl_ploc_rdl(rwl) } {
+ abi::E_TMOUT => false,
+ er => {
+ expect_success(er, &"rwl_ploc_rdl");
+ true
+ }
+ }
+ }
+
+ #[inline]
+ pub unsafe fn write(&self) {
+ let rwl = self.raw();
+ expect_success(unsafe { abi::rwl_loc_wrl(rwl) }, &"rwl_loc_wrl");
+ }
+
+ #[inline]
+ pub unsafe fn try_write(&self) -> bool {
+ let rwl = self.raw();
+ match unsafe { abi::rwl_ploc_wrl(rwl) } {
+ abi::E_TMOUT => false,
+ er => {
+ expect_success(er, &"rwl_ploc_wrl");
+ true
+ }
+ }
+ }
+
+ #[inline]
+ pub unsafe fn read_unlock(&self) {
+ let rwl = self.raw();
+ expect_success_aborting(unsafe { abi::rwl_unl_rwl(rwl) }, &"rwl_unl_rwl");
+ }
+
+ #[inline]
+ pub unsafe fn write_unlock(&self) {
+ let rwl = self.raw();
+ expect_success_aborting(unsafe { abi::rwl_unl_rwl(rwl) }, &"rwl_unl_rwl");
+ }
+
+ #[inline]
+ pub unsafe fn destroy(&self) {
+ if let Some(rwl) = self.rwl.get().map(|x| x.0) {
+ expect_success_aborting(unsafe { abi::rwl_del_rwl(rwl) }, &"rwl_del_rwl");
+ }
+ }
+}
--- /dev/null
+use super::abi;
+use crate::io;
+
+pub struct Stdin;
+pub struct Stdout;
+pub struct Stderr;
+struct PanicOutput;
+
+impl Stdin {
+ pub const fn new() -> Stdin {
+ Stdin
+ }
+}
+
+impl io::Read for Stdin {
+ fn read(&mut self, _buf: &mut [u8]) -> io::Result<usize> {
+ Ok(0)
+ }
+}
+
+impl Stdout {
+ pub const fn new() -> Stdout {
+ Stdout
+ }
+}
+
+impl io::Write for Stdout {
+ fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
+ unsafe { abi::SOLID_LOG_write(buf.as_ptr(), buf.len()) };
+ Ok(buf.len())
+ }
+
+ fn flush(&mut self) -> io::Result<()> {
+ Ok(())
+ }
+}
+
+impl Stderr {
+ pub const fn new() -> Stderr {
+ Stderr
+ }
+}
+
+impl io::Write for Stderr {
+ fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
+ unsafe { abi::SOLID_LOG_write(buf.as_ptr(), buf.len()) };
+ Ok(buf.len())
+ }
+
+ fn flush(&mut self) -> io::Result<()> {
+ Ok(())
+ }
+}
+
+impl PanicOutput {
+ pub const fn new() -> PanicOutput {
+ PanicOutput
+ }
+}
+
+impl io::Write for PanicOutput {
+ fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
+ unsafe { abi::SOLID_LOG_write(buf.as_ptr(), buf.len()) };
+ Ok(buf.len())
+ }
+
+ fn flush(&mut self) -> io::Result<()> {
+ Ok(())
+ }
+}
+
+pub const STDIN_BUF_SIZE: usize = 0;
+
+pub fn is_ebadf(_err: &io::Error) -> bool {
+ true
+}
+
+pub fn panic_output() -> Option<impl io::Write> {
+ Some(PanicOutput::new())
+}
--- /dev/null
+#![cfg(target_thread_local)]
+#![unstable(feature = "thread_local_internals", issue = "none")]
+
+// Simplify dtor registration by using a list of destructors.
+
+use super::{abi, itron::task};
+use crate::cell::Cell;
+use crate::ptr;
+
+#[thread_local]
+static DTORS: Cell<*mut List> = Cell::new(ptr::null_mut());
+
+type List = Vec<(*mut u8, unsafe extern "C" fn(*mut u8))>;
+
+pub unsafe fn register_dtor(t: *mut u8, dtor: unsafe extern "C" fn(*mut u8)) {
+ if DTORS.get().is_null() {
+ let tid = task::current_task_id_aborting();
+ let v: Box<List> = box Vec::new();
+ DTORS.set(Box::into_raw(v));
+
+ // Register `tls_dtor` to make sure the TLS destructors are called
+ // for tasks created by other means than `std::thread`
+ unsafe { abi::SOLID_TLS_AddDestructor(tid as i32, tls_dtor) };
+ }
+
+ let list: &mut List = unsafe { &mut *DTORS.get() };
+ list.push((t, dtor));
+}
+
+pub unsafe fn run_dtors() {
+ let ptr = DTORS.get();
+ if !ptr.is_null() {
+ // Swap the destructor list, call all registered destructors,
+ // and repeat this until the list becomes permanently empty.
+ while let Some(list) = Some(crate::mem::replace(unsafe { &mut *ptr }, Vec::new()))
+ .filter(|list| !list.is_empty())
+ {
+ for (ptr, dtor) in list.into_iter() {
+ unsafe { dtor(ptr) };
+ }
+ }
+
+ // Drop the destructor list
+ unsafe { Box::from_raw(DTORS.replace(ptr::null_mut())) };
+ }
+}
+
+unsafe extern "C" fn tls_dtor(_unused: *mut u8) {
+ unsafe { run_dtors() };
+}
--- /dev/null
+pub type Key = usize;
+
+#[inline]
+pub unsafe fn create(_dtor: Option<unsafe extern "C" fn(*mut u8)>) -> Key {
+ panic!("should not be used on the solid target");
+}
+
+#[inline]
+pub unsafe fn set(_key: Key, _value: *mut u8) {
+ panic!("should not be used on the solid target");
+}
+
+#[inline]
+pub unsafe fn get(_key: Key) -> *mut u8 {
+ panic!("should not be used on the solid target");
+}
+
+#[inline]
+pub unsafe fn destroy(_key: Key) {
+ panic!("should not be used on the solid target");
+}
+
+#[inline]
+pub fn requires_synchronized_create() -> bool {
+ panic!("should not be used on the solid target");
+}
--- /dev/null
+use super::{abi, error::expect_success};
+use crate::{convert::TryInto, mem::MaybeUninit, time::Duration};
+
+pub use super::itron::time::Instant;
+
+#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)]
+pub struct SystemTime(abi::time_t);
+
+pub const UNIX_EPOCH: SystemTime = SystemTime(0);
+
+impl SystemTime {
+ pub fn now() -> SystemTime {
+ let rtc = unsafe {
+ let mut out = MaybeUninit::zeroed();
+ expect_success(abi::SOLID_RTC_ReadTime(out.as_mut_ptr()), &"SOLID_RTC_ReadTime");
+ out.assume_init()
+ };
+ let t = unsafe {
+ libc::mktime(&mut libc::tm {
+ tm_sec: rtc.tm_sec,
+ tm_min: rtc.tm_min,
+ tm_hour: rtc.tm_hour,
+ tm_mday: rtc.tm_mday,
+ tm_mon: rtc.tm_mon,
+ tm_year: rtc.tm_year,
+ tm_wday: rtc.tm_wday,
+ tm_yday: 0,
+ tm_isdst: 0,
+ tm_gmtoff: 0,
+ tm_zone: crate::ptr::null_mut(),
+ })
+ };
+ assert_ne!(t, -1, "mktime failed");
+ SystemTime(t)
+ }
+
+ pub(super) fn from_time_t(t: abi::time_t) -> Self {
+ Self(t)
+ }
+
+ pub fn sub_time(&self, other: &SystemTime) -> Result<Duration, Duration> {
+ if self.0 >= other.0 {
+ Ok(Duration::from_secs((self.0 as u64).wrapping_sub(other.0 as u64)))
+ } else {
+ Err(Duration::from_secs((other.0 as u64).wrapping_sub(self.0 as u64)))
+ }
+ }
+
+ pub fn checked_add_duration(&self, other: &Duration) -> Option<SystemTime> {
+ Some(SystemTime(self.0.checked_add(other.as_secs().try_into().ok()?)?))
+ }
+
+ pub fn checked_sub_duration(&self, other: &Duration) -> Option<SystemTime> {
+ Some(SystemTime(self.0.checked_sub(other.as_secs().try_into().ok()?)?))
+ }
+}
unsafe fn reset_sigpipe() {
#[cfg(not(any(target_os = "emscripten", target_os = "fuchsia")))]
- assert!(signal(libc::SIGPIPE, libc::SIG_IGN) != libc::SIG_ERR);
+ rtassert!(signal(libc::SIGPIPE, libc::SIG_IGN) != libc::SIG_ERR);
}
}
}
Ok(unsafe { NonZeroUsize::new_unchecked(cpus as usize) })
+ } else if #[cfg(target_os = "haiku")] {
+ let mut sinfo: libc::system_info = crate::mem::zeroed();
+ let res = libc::get_system_info(&mut sinfo);
+
+ if res != libc::B_OK {
+ return Err(io::Error::last_os_error());
+ }
+
+ Ok(unsafe { NonZeroUsize::new_unchecked(sinfo.cpu_count as usize) })
} else {
- // FIXME: implement on vxWorks, Redox, Haiku, l4re
+ // FIXME: implement on vxWorks, Redox, l4re
Err(io::Error::new_const(io::ErrorKind::Unsupported, &"Getting the number of hardware threads is not supported on the target platform"))
}
}
pub mod mutex;
pub mod process;
pub mod remutex;
-#[macro_use]
-pub mod rt;
pub mod rwlock;
pub mod thread;
pub mod thread_info;
+++ /dev/null
-#![deny(unsafe_op_in_unsafe_fn)]
-#![allow(unused_macros)]
-
-use crate::sync::Once;
-use crate::sys;
-use crate::sys_common::thread_info;
-use crate::thread::Thread;
-
-// One-time runtime initialization.
-// Runs before `main`.
-// SAFETY: must be called only once during runtime initialization.
-// NOTE: this is not guaranteed to run, for example when Rust code is called externally.
-#[cfg_attr(test, allow(dead_code))]
-pub unsafe fn init(argc: isize, argv: *const *const u8) {
- unsafe {
- sys::init(argc, argv);
-
- let main_guard = sys::thread::guard::init();
- // Next, set up the current Thread with the guard information we just
- // created. Note that this isn't necessary in general for new threads,
- // but we just do this to name the main thread and to give it correct
- // info about the stack bounds.
- let thread = Thread::new(Some("main".to_owned()));
- thread_info::set(main_guard, thread);
- }
-}
-
-// One-time runtime cleanup.
-// Runs after `main` or at program exit.
-// NOTE: this is not guaranteed to run, for example when the program aborts.
-#[cfg_attr(test, allow(dead_code))]
-pub fn cleanup() {
- static CLEANUP: Once = Once::new();
- CLEANUP.call_once(|| unsafe {
- // Flush stdout and disable buffering.
- crate::io::cleanup();
- // SAFETY: Only called once during runtime cleanup.
- sys::cleanup();
- });
-}
-
-// Prints to the "panic output", depending on the platform this may be:
-// - the standard error output
-// - some dedicated platform specific output
-// - nothing (so this macro is a no-op)
-macro_rules! rtprintpanic {
- ($($t:tt)*) => {
- if let Some(mut out) = crate::sys::stdio::panic_output() {
- let _ = crate::io::Write::write_fmt(&mut out, format_args!($($t)*));
- }
- }
-}
-
-macro_rules! rtabort {
- ($($t:tt)*) => {
- {
- rtprintpanic!("fatal runtime error: {}\n", format_args!($($t)*));
- crate::sys::abort_internal();
- }
- }
-}
-
-macro_rules! rtassert {
- ($e:expr) => {
- if !$e {
- rtabort!(concat!("assertion failed: ", stringify!($e)));
- }
- };
-}
-
-macro_rules! rtunwrap {
- ($ok:ident, $e:expr) => {
- match $e {
- $ok(v) => v,
- ref err => {
- let err = err.as_ref().map(drop); // map Ok/Some which might not be Debug
- rtabort!(concat!("unwrap failed: ", stringify!($e), " = {:?}"), err)
- }
- }
- };
-}
#![allow(dead_code)] // stack_guard isn't used right now on all platforms
+#![allow(unused_unsafe)] // thread_local with `const {}` triggers this liny
use crate::cell::RefCell;
use crate::sys::thread::guard::Guard;
thread: Thread,
}
-thread_local! { static THREAD_INFO: RefCell<Option<ThreadInfo>> = RefCell::new(None) }
+thread_local! { static THREAD_INFO: RefCell<Option<ThreadInfo>> = const { RefCell::new(None) } }
impl ThreadInfo {
fn with<R, F>(f: F) -> Option<R>
F: FnOnce(&mut ThreadInfo) -> R,
{
THREAD_INFO
- .try_with(move |c| {
- if c.borrow().is_none() {
- *c.borrow_mut() =
- Some(ThreadInfo { stack_guard: None, thread: Thread::new(None) })
- }
- f(c.borrow_mut().as_mut().unwrap())
+ .try_with(move |thread_info| {
+ let mut thread_info = thread_info.borrow_mut();
+ let thread_info = thread_info.get_or_insert_with(|| ThreadInfo {
+ stack_guard: None,
+ thread: Thread::new(None),
+ });
+ f(thread_info)
})
.ok()
}
}
pub fn set(stack_guard: Option<Guard>, thread: Thread) {
- THREAD_INFO.with(|c| assert!(c.borrow().is_none()));
- THREAD_INFO.with(move |c| *c.borrow_mut() = Some(ThreadInfo { stack_guard, thread }));
-}
-
-pub fn reset_guard(stack_guard: Option<Guard>) {
- THREAD_INFO.with(move |c| c.borrow_mut().as_mut().unwrap().stack_guard = stack_guard);
+ THREAD_INFO.with(move |thread_info| {
+ let mut thread_info = thread_info.borrow_mut();
+ rtassert!(thread_info.is_none());
+ *thread_info = Some(ThreadInfo { stack_guard, thread });
+ });
}
let stack_size = stack_size.unwrap_or_else(thread::min_stack);
- let my_thread = Thread::new(name);
+ let my_thread = Thread::new(name.map(|name| {
+ CString::new(name).expect("thread name may not contain interior null bytes")
+ }));
let their_thread = my_thread.clone();
let my_packet: Arc<UnsafeCell<Option<Result<T>>>> = Arc::new(UnsafeCell::new(None));
impl Thread {
// Used only internally to construct a thread object without spawning
// Panics if the name contains nuls.
- pub(crate) fn new(name: Option<String>) -> Thread {
- let cname =
- name.map(|n| CString::new(n).expect("thread name may not contain interior null bytes"));
- Thread {
- inner: Arc::new(Inner { name: cname, id: ThreadId::new(), parker: Parker::new() }),
- }
+ pub(crate) fn new(name: Option<CString>) -> Thread {
+ Thread { inner: Arc::new(Inner { name, id: ThreadId::new(), parker: Parker::new() }) }
}
/// Atomically makes the handle's token available if it is not already.
/// | UNIX | [clock_gettime (Monotonic Clock)] |
/// | Darwin | [mach_absolute_time] |
/// | VXWorks | [clock_gettime (Monotonic Clock)] |
+/// | SOLID | `get_tim` |
/// | WASI | [__wasi_clock_time_get (Monotonic Clock)] |
/// | Windows | [QueryPerformanceCounter] |
///
/// | UNIX | [clock_gettime (Realtime Clock)] |
/// | Darwin | [gettimeofday] |
/// | VXWorks | [clock_gettime (Realtime Clock)] |
+/// | SOLID | `SOLID_RTC_ReadTime` |
/// | WASI | [__wasi_clock_time_get (Realtime Clock)] |
/// | Windows | [GetSystemTimePreciseAsFileTime] / [GetSystemTimeAsFileTime] |
///
impl<T: Write> OutputFormatter for JunitFormatter<T> {
fn write_run_start(&mut self, _test_count: usize) -> io::Result<()> {
// We write xml header on run start
- self.write_message(&"<?xml version=\"1.0\" encoding=\"UTF-8\"?>")
+ self.out.write_all(b"\n")?;
+ self.write_message("<?xml version=\"1.0\" encoding=\"UTF-8\"?>")
}
fn write_test_start(&mut self, _desc: &TestDesc) -> io::Result<()> {
self.write_message("</testsuite>")?;
self.write_message("</testsuites>")?;
+ self.out.write_all(b"\n\n")?;
+
Ok(state.failed == 0)
}
}
unix,
windows,
target_os = "psp",
+ target_os = "solid_asp3",
all(target_vendor = "fortanix", target_env = "sgx"),
))] {
mod libunwind;
"library/backtrace",
"library/stdarch",
"compiler/rustc_codegen_cranelift",
+ "compiler/rustc_codegen_gcc",
"src/doc/book",
"src/doc/edition-guide",
"src/doc/embedded-book",
const DEFAULT: bool = true;
fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
- run.paths(&["compiler/rustc_codegen_cranelift", "rustc_codegen_cranelift"])
+ run.paths(&[
+ "compiler/rustc_codegen_cranelift",
+ "rustc_codegen_cranelift",
+ "compiler/rustc_codegen_gcc",
+ "rustc_codegen_gcc",
+ ])
}
fn make_run(run: RunConfig<'_>) {
- for &backend in &[INTERNER.intern_str("cranelift")] {
+ for &backend in &[INTERNER.intern_str("cranelift"), INTERNER.intern_str("gcc")] {
run.builder.ensure(CodegenBackend { target: run.target, backend });
}
}
incremental = true
[llvm]
-# Will download LLVM from CI if available on your platform (Linux only for now)
-# https://github.com/rust-lang/rust/issues/77084 tracks support for more platforms
+# Will download LLVM from CI if available on your platform.
download-ci-llvm = "if-available"
+use crate::TargetSelection;
use crate::{t, VERSION};
use std::fmt::Write as _;
use std::path::{Path, PathBuf};
let include_path = profile.include_path(src_path);
println!("`x.py` will now use the configuration at {}", include_path.display());
+ let build = TargetSelection::from_user(&env!("BUILD_TRIPLE"));
+ let stage_path = ["build", build.rustc_target_arg(), "stage1"].join("/");
+
+ println!();
+
+ if !rustup_installed() && profile != Profile::User {
+ println!("`rustup` is not installed; cannot link `stage1` toolchain");
+ } else if stage_dir_exists(&stage_path[..]) {
+ attempt_toolchain_link(&stage_path[..]);
+ }
+
let suggestions = match profile {
Profile::Codegen | Profile::Compiler => &["check", "build", "test"][..],
Profile::Tools => &[
}
}
+fn rustup_installed() -> bool {
+ Command::new("rustup")
+ .arg("--version")
+ .stdout(std::process::Stdio::null())
+ .output()
+ .map_or(false, |output| output.status.success())
+}
+
+fn stage_dir_exists(stage_path: &str) -> bool {
+ match fs::create_dir(&stage_path[..]) {
+ Ok(_) => true,
+ Err(_) => Path::new(&stage_path[..]).exists(),
+ }
+}
+
+fn attempt_toolchain_link(stage_path: &str) {
+ if toolchain_is_linked() {
+ return;
+ }
+
+ if try_link_toolchain(&stage_path[..]) {
+ println!(
+ "Added `stage1` rustup toolchain; try `cargo +stage1 build` on a separate rust project to run a newly-built toolchain"
+ );
+ } else {
+ println!("`rustup` failed to link stage 1 build to `stage1` toolchain");
+ println!(
+ "To manually link stage 1 build to `stage1` toolchain, run:\n
+ `rustup toolchain link stage1 {}`",
+ &stage_path[..]
+ );
+ }
+}
+
+fn toolchain_is_linked() -> bool {
+ match Command::new("rustup")
+ .args(&["toolchain", "list"])
+ .stdout(std::process::Stdio::piped())
+ .output()
+ {
+ Ok(toolchain_list) => {
+ if !String::from_utf8_lossy(&toolchain_list.stdout).contains("stage1") {
+ return false;
+ }
+ // The toolchain has already been linked.
+ println!(
+ "`stage1` toolchain already linked; not attempting to link `stage1` toolchain"
+ );
+ }
+ Err(_) => {
+ // In this case, we don't know if the `stage1` toolchain has been linked;
+ // but `rustup` failed, so let's not go any further.
+ println!(
+ "`rustup` failed to list current toolchains; not attempting to link `stage1` toolchain"
+ );
+ }
+ }
+ true
+}
+
+fn try_link_toolchain(stage_path: &str) -> bool {
+ Command::new("rustup")
+ .stdout(std::process::Stdio::null())
+ .args(&["toolchain", "link", "stage1", &stage_path[..]])
+ .output()
+ .map_or(false, |output| output.status.success())
+}
+
// Used to get the path for `Subcommand::Setup`
pub fn interactive_path() -> io::Result<Profile> {
fn abbrev_all() -> impl Iterator<Item = ((String, String), Profile)> {
ciCommandSetEnv RUST_CONFIGURE_ARGS \
"${RUST_CONFIGURE_ARGS} --set llvm.clang-cl=$(pwd)/clang-rust/bin/clang-cl.exe"
fi
+
+if isWindows; then
+ # GitHub image 20210928.2 added LLVM, but it is broken (and we don't want
+ # to use it anyways).
+ rm -rf /c/Program\ Files/LLVM
+fi
-Subproject commit fcb5e0ea68112d85a1d29a7a7335978ef2a02181
+Subproject commit eb1282ec444db94055fa9531b6f3f803e86bb382
-Subproject commit fe6227eb3c8533200c52dffa42ef1b6f2f02c40e
+Subproject commit 2747c4bb2cbc0639b733793ddb0bf4e9daa2634e
-Subproject commit 0e5ed7a4bec065f0cc18c35d1c904639e095314d
+Subproject commit 13747275bd14c2d2b453100498532f9ae5504769
-Subproject commit 9d4132b56c4999cd3ce1aeca5f1b2f2cb0d11c24
+Subproject commit 28aca4a36962c709bce301c03114b5589381dfb8
-Subproject commit 9198465b6ca8bed669df0cbb67c0e6d0b140803c
+Subproject commit d1f03cbaa39d9164f5fe4b9b93762668142e0dad
- [Tests](tests/index.md)
- [Platform Support](platform-support.md)
- [aarch64-apple-ios-sim](platform-support/aarch64-apple-ios-sim.md)
+ - [\*-kmc-solid_\*](platform-support/kmc-solid.md)
- [Target Tier Policy](target-tier-policy.md)
- [Targets](targets/index.md)
- [Built-in Targets](targets/built-in.md)
-------|:---:|:----:|-------
`aarch64-apple-ios-macabi` | ? | | Apple Catalyst on ARM64
`aarch64-apple-tvos` | * | | ARM64 tvOS
+[`aarch64-kmc-solid_asp3`](platform-support/kmc-solid.md) | ✓ | | ARM64 SOLID with TOPPERS/ASP3
`aarch64-unknown-freebsd` | ✓ | ✓ | ARM64 FreeBSD
`aarch64-unknown-hermit` | ? | |
`aarch64-unknown-uefi` | * | | ARM64 UEFI
`armv7-unknown-freebsd` | ✓ | ✓ | ARMv7 FreeBSD
`armv7-unknown-netbsd-eabihf` | ✓ | ✓ |
`armv7-wrs-vxworks-eabihf` | ? | |
+[`armv7a-kmc-solid_asp3-eabi`](platform-support/kmc-solid.md) | ✓ | | ARM SOLID with TOPPERS/ASP3
+[`armv7a-kmc-solid_asp3-eabihf`](platform-support/kmc-solid.md) | ✓ | | ARM SOLID with TOPPERS/ASP3, hardfloat
`armv7a-none-eabihf` | * | | ARM Cortex-A, hardfloat
`armv7s-apple-ios` | ✓ | |
`avr-unknown-gnu-atmega328` | * | | AVR. Requires `-Z build-std=core`
--- /dev/null
+# \*-kmc-solid_\*
+
+**Tier: 3**
+
+[SOLID] embedded development platform by Kyoto Microcomputer Co., Ltd.
+
+[SOLID]: https://www.kmckk.co.jp/eng/SOLID/
+
+The target names follow this format: `$ARCH-kmc-solid_$KERNEL-$ABI`, where `$ARCH` specifies the target processor architecture, `$KERNEL` the base kernel, and `$ABI` the target ABI (optional). The following targets are currently defined:
+
+| Target name | `target_arch` | `target_vendor` | `target_os` |
+|--------------------------------|---------------|-----------------|--------------|
+| `aarch64-kmc-solid_asp3` | `aarch64` | `kmc` | `solid_asp3` |
+| `armv7a-kmc-solid_asp3-eabi` | `arm` | `kmc` | `solid_asp3` |
+| `armv7a-kmc-solid_asp3-eabihf` | `arm` | `kmc` | `solid_asp3` |
+
+## Designated Developers
+
+- [@kawadakk](https://github.com/kawadakk)
+
+## Requirements
+
+This target is cross-compiled.
+A platform-provided C compiler toolchain is required, though it can be substituted by [GNU Arm Embedded Toolchain](https://developer.arm.com/tools-and-software/open-source-software/developer-tools/gnu-toolchain/gnu-rm) for the purpose of building Rust and functional binaries.
+
+## Building
+
+The target can be built by enabling it for a `rustc` build.
+
+```toml
+[build]
+target = ["aarch64-kmc-solid_asp3"]
+```
+
+Make sure `aarch64-kmc-elf-gcc` is included in `$PATH`. Alternatively, you can use GNU Arm Embedded Toolchain by adding the following to `config.toml`:
+
+```toml
+[target.aarch64-kmc-solid_asp3]
+cc = "arm-none-eabi-gcc"
+```
+
+## Cross-compilation
+
+This target can be cross-compiled from any hosts.
+
+## Testing
+
+Currently there is no support to run the rustc test suite for this target.
+
+## Building Rust programs
+
+Building executables is not supported yet.
+
+If `rustc` has support for that target and the library artifacts are available, then Rust static libraries can be built for that target:
+
+```shell
+$ rustc --target aarch64-kmc-solid_asp3 your-code.rs --crate-type staticlib
+$ ls libyour_code.a
+```
+
+On Rust Nightly it's possible to build without the target artifacts available:
+
+```text
+cargo build -Z build-std --target aarch64-kmc-solid_asp3
+```
use rustc_hir::*;
match self.kind {
- TyKind::Never => Never,
+ TyKind::Never => Primitive(PrimitiveType::Never),
TyKind::Ptr(ref m) => RawPointer(m.mutbl, box m.ty.clean(cx)),
TyKind::Rptr(ref l, ref m) => {
// There are two times a `Fresh` lifetime can be created:
trace!("cleaning type: {:?}", self);
let ty = normalize(cx, self).unwrap_or(self);
match *ty.kind() {
- ty::Never => Never,
+ ty::Never => Primitive(PrimitiveType::Never),
ty::Bool => Primitive(PrimitiveType::Bool),
ty::Char => Primitive(PrimitiveType::Char),
ty::Int(int_ty) => Primitive(int_ty.into()),
//! This module attempts to reconstruct the original where and/or parameter
//! bounds by special casing scenarios such as these. Fun!
-use std::collections::BTreeMap;
-
+use rustc_data_structures::fx::FxIndexMap;
use rustc_hir::def_id::DefId;
use rustc_middle::ty;
use rustc_span::Symbol;
use crate::core::DocContext;
crate fn where_clauses(cx: &DocContext<'_>, clauses: Vec<WP>) -> Vec<WP> {
- // First, partition the where clause into its separate components
- let mut params: BTreeMap<_, (Vec<_>, Vec<_>)> = BTreeMap::new();
+ // First, partition the where clause into its separate components.
+ //
+ // We use `FxIndexMap` so that the insertion order is preserved to prevent messing up to
+ // the order of the generated bounds.
+ let mut params: FxIndexMap<Symbol, (Vec<_>, Vec<_>)> = FxIndexMap::default();
let mut lifetimes = Vec::new();
let mut equalities = Vec::new();
let mut tybounds = Vec::new();
Slice(Box<Type>),
/// The `String` field is about the size or the constant representing the array's length.
Array(Box<Type>, String),
- Never,
RawPointer(Mutability, Box<Type>),
BorrowedRef {
lifetime: Option<Lifetime>,
}
RawPointer(..) => Some(PrimitiveType::RawPointer),
BareFunction(..) => Some(PrimitiveType::Fn),
- Never => Some(PrimitiveType::Never),
_ => None,
}
}
}
}
BareFunction(..) => PrimitiveType::Fn,
- Never => PrimitiveType::Never,
Slice(..) => PrimitiveType::Slice,
Array(..) => PrimitiveType::Array,
RawPointer(..) => PrimitiveType::RawPointer,
use rustc_span::Span;
use std::cell::RefCell;
+use std::lazy::SyncLazy;
use std::mem;
use std::rc::Rc;
providers.typeck_item_bodies = |_, _| {};
// hack so that `used_trait_imports` won't try to call typeck
providers.used_trait_imports = |_, _| {
- lazy_static! {
- static ref EMPTY_SET: FxHashSet<LocalDefId> = FxHashSet::default();
- }
+ static EMPTY_SET: SyncLazy<FxHashSet<LocalDefId>> =
+ SyncLazy::new(FxHashSet::default);
&EMPTY_SET
};
// In case typeck does end up being called, don't ICE in case there were name resolution errors
fmt::Display::fmt(&tybounds(bounds, lt, cx), f)
}
clean::Infer => write!(f, "_"),
+ clean::Primitive(clean::PrimitiveType::Never) => {
+ primitive_link(f, PrimitiveType::Never, "!", cx)
+ }
clean::Primitive(prim) => primitive_link(f, prim, &*prim.as_sym().as_str(), cx),
clean::BareFunction(ref decl) => {
if f.alternate() {
primitive_link(f, PrimitiveType::Array, &format!("; {}]", Escape(n)), cx)
}
}
- clean::Never => primitive_link(f, PrimitiveType::Never, "!", cx),
clean::RawPointer(m, ref t) => {
let m = match m {
hir::Mutability::Mut => "mut",
use crate::html::escape::Escape;
use crate::html::render::Context;
+use std::collections::VecDeque;
use std::fmt::{Display, Write};
-use std::iter::Peekable;
use rustc_lexer::{LiteralKind, TokenKind};
use rustc_span::edition::Edition;
})
}
+/// This iterator comes from the same idea than "Peekable" except that it allows to "peek" more than
+/// just the next item by using `peek_next`. The `peek` method always returns the next item after
+/// the current one whereas `peek_next` will return the next item after the last one peeked.
+///
+/// You can use both `peek` and `peek_next` at the same time without problem.
+struct PeekIter<'a> {
+ stored: VecDeque<(TokenKind, &'a str)>,
+ /// This position is reinitialized when using `next`. It is used in `peek_next`.
+ peek_pos: usize,
+ iter: TokenIter<'a>,
+}
+
+impl PeekIter<'a> {
+ fn new(iter: TokenIter<'a>) -> Self {
+ Self { stored: VecDeque::new(), peek_pos: 0, iter }
+ }
+ /// Returns the next item after the current one. It doesn't interfer with `peek_next` output.
+ fn peek(&mut self) -> Option<&(TokenKind, &'a str)> {
+ if self.stored.is_empty() {
+ if let Some(next) = self.iter.next() {
+ self.stored.push_back(next);
+ }
+ }
+ self.stored.front()
+ }
+ /// Returns the next item after the last one peeked. It doesn't interfer with `peek` output.
+ fn peek_next(&mut self) -> Option<&(TokenKind, &'a str)> {
+ self.peek_pos += 1;
+ if self.peek_pos - 1 < self.stored.len() {
+ self.stored.get(self.peek_pos - 1)
+ } else if let Some(next) = self.iter.next() {
+ self.stored.push_back(next);
+ self.stored.back()
+ } else {
+ None
+ }
+ }
+}
+
+impl Iterator for PeekIter<'a> {
+ type Item = (TokenKind, &'a str);
+ fn next(&mut self) -> Option<Self::Item> {
+ self.peek_pos = 0;
+ if let Some(first) = self.stored.pop_front() { Some(first) } else { self.iter.next() }
+ }
+}
+
/// Processes program tokens, classifying strings of text by highlighting
/// category (`Class`).
struct Classifier<'a> {
- tokens: Peekable<TokenIter<'a>>,
+ tokens: PeekIter<'a>,
in_attribute: bool,
in_macro: bool,
in_macro_nonterminal: bool,
/// Takes as argument the source code to HTML-ify, the rust edition to use and the source code
/// file span which will be used later on by the `span_correspondance_map`.
fn new(src: &str, edition: Edition, file_span: Span) -> Classifier<'_> {
- let tokens = TokenIter { src }.peekable();
+ let tokens = PeekIter::new(TokenIter { src });
Classifier {
tokens,
in_attribute: false,
// Assume that '&' or '*' is the reference or dereference operator
// or a reference or pointer type. Unless, of course, it looks like
// a logical and or a multiplication operator: `&&` or `* `.
- TokenKind::Star => match lookahead {
+ TokenKind::Star => match self.peek() {
Some(TokenKind::Whitespace) => Class::Op,
_ => Class::RefKeyWord,
},
None => match text {
"Option" | "Result" => Class::PreludeTy,
"Some" | "None" | "Ok" | "Err" => Class::PreludeVal,
+ // "union" is a weak keyword and is only considered as a keyword when declaring
+ // a union type.
+ "union" if self.check_if_is_union_keyword() => Class::KeyWord,
_ if self.in_macro_nonterminal => {
self.in_macro_nonterminal = false;
Class::MacroNonTerminal
}
fn peek(&mut self) -> Option<TokenKind> {
- self.tokens.peek().map(|(toke_kind, _text)| *toke_kind)
+ self.tokens.peek().map(|(token_kind, _text)| *token_kind)
+ }
+
+ fn check_if_is_union_keyword(&mut self) -> bool {
+ while let Some(kind) = self.tokens.peek_next().map(|(token_kind, _text)| token_kind) {
+ if *kind == TokenKind::Whitespace {
+ continue;
+ }
+ return *kind == TokenKind::Ident;
+ }
+ false
}
}
--- /dev/null
+<span class="kw">union</span> <span class="ident">Foo</span> {
+ <span class="ident">i</span>: <span class="ident">i8</span>,
+ <span class="ident">u</span>: <span class="ident">i8</span>,
+}
+
+<span class="kw">fn</span> <span class="ident">main</span>() {
+ <span class="kw">let</span> <span class="ident">union</span> <span class="op">=</span> <span class="number">0</span>;
+}
--- /dev/null
+union Foo {
+ i: i8,
+ u: i8,
+}
+
+fn main() {
+ let union = 0;
+}
expect_file!["fixtures/highlight.html"].assert_eq(&html.into_inner());
});
}
+
+#[test]
+fn test_union_highlighting() {
+ create_default_session_globals_then(|| {
+ let src = include_str!("fixtures/union.rs");
+ let mut html = Buffer::new();
+ write_code(&mut html, src, Edition::Edition2018, None);
+ expect_file!["fixtures/union.html"].assert_eq(&html.into_inner());
+ });
+}
| clean::Tuple(_)
| clean::Slice(_)
| clean::Array(_, _)
- | clean::Never
| clean::RawPointer(_, _)
| clean::QPath { .. }
| clean::Infer
}
}
Generic(s) => Type::Generic(s.to_string()),
+ Primitive(clean::PrimitiveType::Never) => Type::Never,
Primitive(p) => Type::Primitive(p.as_sym().to_string()),
BareFunction(f) => Type::FunctionPointer(Box::new((*f).into_tcx(tcx))),
Tuple(t) => Type::Tuple(t.into_iter().map(|x| x.into_tcx(tcx)).collect()),
Slice(t) => Type::Slice(Box::new((*t).into_tcx(tcx))),
Array(t, s) => Type::Array { type_: Box::new((*t).into_tcx(tcx)), len: s },
ImplTrait(g) => Type::ImplTrait(g.into_iter().map(|x| x.into_tcx(tcx)).collect()),
- Never => Type::Never,
Infer => Type::Infer,
RawPointer(mutability, type_) => Type::RawPointer {
mutable: mutability == ast::Mutability::Mut,
s.impls = self.get_impls(id.expect_def_id())
} else if let types::ItemEnum::Enum(ref mut e) = new_item.inner {
e.impls = self.get_impls(id.expect_def_id())
+ } else if let types::ItemEnum::Union(ref mut u) = new_item.inner {
+ u.impls = self.get_impls(id.expect_def_id())
}
let removed = self.index.borrow_mut().insert(from_item_id(id), new_item.clone());
#![recursion_limit = "256"]
#![warn(rustc::internal)]
-#[macro_use]
-extern crate lazy_static;
#[macro_use]
extern crate tracing;
// MIR for `BAR::promoted[0]` after SimplifyCfg-elaborate-drops
promoted[0] in BAR: &[&i32; 1] = {
- let mut _0: &[&i32; 1]; // return place in scope 0 at $DIR/const-promotion-extern-static.rs:9:31: 9:35
+ let mut _0: &[&i32; 1]; // return place in scope 0 at $DIR/const-promotion-extern-static.rs:9:31: 9:44
let mut _1: [&i32; 1]; // in scope 0 at $DIR/const-promotion-extern-static.rs:9:31: 9:35
let mut _2: &i32; // in scope 0 at $DIR/const-promotion-extern-static.rs:9:32: 9:34
let mut _3: &i32; // in scope 0 at $DIR/const-promotion-extern-static.rs:9:33: 9:34
// + literal: Const { ty: &i32, val: Value(Scalar(alloc1)) }
_2 = &(*_3); // scope 0 at $DIR/const-promotion-extern-static.rs:9:32: 9:34
_1 = [move _2]; // scope 0 at $DIR/const-promotion-extern-static.rs:9:31: 9:35
- _0 = &_1; // scope 0 at $DIR/const-promotion-extern-static.rs:9:31: 9:35
- return; // scope 0 at $DIR/const-promotion-extern-static.rs:9:31: 9:35
+ _0 = &_1; // scope 0 at $DIR/const-promotion-extern-static.rs:9:31: 9:44
+ return; // scope 0 at $DIR/const-promotion-extern-static.rs:9:31: 9:44
}
}
static mut BAR: *const &i32 = {
let mut _0: *const &i32; // return place in scope 0 at $DIR/const-promotion-extern-static.rs:9:17: 9:28
- let mut _1: &[&i32]; // in scope 0 at $DIR/const-promotion-extern-static.rs:9:31: 9:35
- let mut _2: &[&i32; 1]; // in scope 0 at $DIR/const-promotion-extern-static.rs:9:31: 9:35
+ let mut _1: &[&i32]; // in scope 0 at $DIR/const-promotion-extern-static.rs:9:31: 9:44
+ let mut _2: &[&i32; 1]; // in scope 0 at $DIR/const-promotion-extern-static.rs:9:31: 9:44
let _3: [&i32; 1]; // in scope 0 at $DIR/const-promotion-extern-static.rs:9:31: 9:35
let mut _4: &i32; // in scope 0 at $DIR/const-promotion-extern-static.rs:9:32: 9:34
let _5: &i32; // in scope 0 at $DIR/const-promotion-extern-static.rs:9:33: 9:34
-+ let mut _6: &[&i32; 1]; // in scope 0 at $DIR/const-promotion-extern-static.rs:9:31: 9:35
++ let mut _6: &[&i32; 1]; // in scope 0 at $DIR/const-promotion-extern-static.rs:9:31: 9:44
bb0: {
- StorageLive(_1); // scope 0 at $DIR/const-promotion-extern-static.rs:9:31: 9:35
- StorageLive(_2); // scope 0 at $DIR/const-promotion-extern-static.rs:9:31: 9:35
+ StorageLive(_1); // scope 0 at $DIR/const-promotion-extern-static.rs:9:31: 9:44
+ StorageLive(_2); // scope 0 at $DIR/const-promotion-extern-static.rs:9:31: 9:44
- StorageLive(_3); // scope 0 at $DIR/const-promotion-extern-static.rs:9:31: 9:35
- StorageLive(_4); // scope 0 at $DIR/const-promotion-extern-static.rs:9:32: 9:34
- StorageLive(_5); // scope 0 at $DIR/const-promotion-extern-static.rs:9:33: 9:34
- _5 = const {alloc1: &i32}; // scope 0 at $DIR/const-promotion-extern-static.rs:9:33: 9:34
-+ _6 = const BAR::promoted[0]; // scope 0 at $DIR/const-promotion-extern-static.rs:9:31: 9:35
++ _6 = const BAR::promoted[0]; // scope 0 at $DIR/const-promotion-extern-static.rs:9:31: 9:44
// ty::Const
- // + ty: &i32
- // + val: Value(Scalar(alloc1))
- // + literal: Const { ty: &i32, val: Value(Scalar(alloc1)) }
- _4 = &(*_5); // scope 0 at $DIR/const-promotion-extern-static.rs:9:32: 9:34
- _3 = [move _4]; // scope 0 at $DIR/const-promotion-extern-static.rs:9:31: 9:35
-- _2 = &_3; // scope 0 at $DIR/const-promotion-extern-static.rs:9:31: 9:35
-+ // + span: $DIR/const-promotion-extern-static.rs:9:31: 9:35
+- _2 = &_3; // scope 0 at $DIR/const-promotion-extern-static.rs:9:31: 9:44
++ // + span: $DIR/const-promotion-extern-static.rs:9:31: 9:44
+ // + literal: Const { ty: &[&i32; 1], val: Unevaluated(Unevaluated { def: WithOptConstParam { did: DefId(0:6 ~ const_promotion_extern_static[55e6]::BAR), const_param_did: None }, substs_: Some([]), promoted: Some(promoted[0]) }) }
-+ _2 = &(*_6); // scope 0 at $DIR/const-promotion-extern-static.rs:9:31: 9:35
- _1 = move _2 as &[&i32] (Pointer(Unsize)); // scope 0 at $DIR/const-promotion-extern-static.rs:9:31: 9:35
++ _2 = &(*_6); // scope 0 at $DIR/const-promotion-extern-static.rs:9:31: 9:44
+ _1 = move _2 as &[&i32] (Pointer(Unsize)); // scope 0 at $DIR/const-promotion-extern-static.rs:9:31: 9:44
- StorageDead(_4); // scope 0 at $DIR/const-promotion-extern-static.rs:9:34: 9:35
StorageDead(_2); // scope 0 at $DIR/const-promotion-extern-static.rs:9:34: 9:35
_0 = core::slice::<impl [&i32]>::as_ptr(move _1) -> [return: bb1, unwind: bb2]; // scope 0 at $DIR/const-promotion-extern-static.rs:9:31: 9:44
// MIR for `FOO::promoted[0]` after SimplifyCfg-elaborate-drops
promoted[0] in FOO: &[&i32; 1] = {
- let mut _0: &[&i32; 1]; // return place in scope 0 at $DIR/const-promotion-extern-static.rs:13:31: 13:46
+ let mut _0: &[&i32; 1]; // return place in scope 0 at $DIR/const-promotion-extern-static.rs:13:31: 13:55
let mut _1: [&i32; 1]; // in scope 0 at $DIR/const-promotion-extern-static.rs:13:31: 13:46
let mut _2: &i32; // in scope 0 at $DIR/const-promotion-extern-static.rs:13:32: 13:45
let mut _3: *const i32; // in scope 0 at $DIR/const-promotion-extern-static.rs:13:42: 13:43
// + literal: Const { ty: *const i32, val: Value(Scalar(alloc3)) }
_2 = &(*_3); // scope 0 at $DIR/const-promotion-extern-static.rs:13:41: 13:43
_1 = [move _2]; // scope 0 at $DIR/const-promotion-extern-static.rs:13:31: 13:46
- _0 = &_1; // scope 0 at $DIR/const-promotion-extern-static.rs:13:31: 13:46
- return; // scope 0 at $DIR/const-promotion-extern-static.rs:13:31: 13:46
+ _0 = &_1; // scope 0 at $DIR/const-promotion-extern-static.rs:13:31: 13:55
+ return; // scope 0 at $DIR/const-promotion-extern-static.rs:13:31: 13:55
}
}
static mut FOO: *const &i32 = {
let mut _0: *const &i32; // return place in scope 0 at $DIR/const-promotion-extern-static.rs:13:17: 13:28
- let mut _1: &[&i32]; // in scope 0 at $DIR/const-promotion-extern-static.rs:13:31: 13:46
- let mut _2: &[&i32; 1]; // in scope 0 at $DIR/const-promotion-extern-static.rs:13:31: 13:46
+ let mut _1: &[&i32]; // in scope 0 at $DIR/const-promotion-extern-static.rs:13:31: 13:55
+ let mut _2: &[&i32; 1]; // in scope 0 at $DIR/const-promotion-extern-static.rs:13:31: 13:55
let _3: [&i32; 1]; // in scope 0 at $DIR/const-promotion-extern-static.rs:13:31: 13:46
let mut _4: &i32; // in scope 0 at $DIR/const-promotion-extern-static.rs:13:32: 13:45
let _5: *const i32; // in scope 0 at $DIR/const-promotion-extern-static.rs:13:42: 13:43
-+ let mut _6: &[&i32; 1]; // in scope 0 at $DIR/const-promotion-extern-static.rs:13:31: 13:46
++ let mut _6: &[&i32; 1]; // in scope 0 at $DIR/const-promotion-extern-static.rs:13:31: 13:55
scope 1 {
}
bb0: {
- StorageLive(_1); // scope 0 at $DIR/const-promotion-extern-static.rs:13:31: 13:46
- StorageLive(_2); // scope 0 at $DIR/const-promotion-extern-static.rs:13:31: 13:46
+ StorageLive(_1); // scope 0 at $DIR/const-promotion-extern-static.rs:13:31: 13:55
+ StorageLive(_2); // scope 0 at $DIR/const-promotion-extern-static.rs:13:31: 13:55
- StorageLive(_3); // scope 0 at $DIR/const-promotion-extern-static.rs:13:31: 13:46
- StorageLive(_4); // scope 0 at $DIR/const-promotion-extern-static.rs:13:32: 13:45
- StorageLive(_5); // scope 1 at $DIR/const-promotion-extern-static.rs:13:42: 13:43
- _5 = const {alloc3: *const i32}; // scope 1 at $DIR/const-promotion-extern-static.rs:13:42: 13:43
-+ _6 = const FOO::promoted[0]; // scope 0 at $DIR/const-promotion-extern-static.rs:13:31: 13:46
++ _6 = const FOO::promoted[0]; // scope 0 at $DIR/const-promotion-extern-static.rs:13:31: 13:55
// ty::Const
- // + ty: *const i32
- // + val: Value(Scalar(alloc3))
- // + literal: Const { ty: *const i32, val: Value(Scalar(alloc3)) }
- _4 = &(*_5); // scope 1 at $DIR/const-promotion-extern-static.rs:13:41: 13:43
- _3 = [move _4]; // scope 0 at $DIR/const-promotion-extern-static.rs:13:31: 13:46
-- _2 = &_3; // scope 0 at $DIR/const-promotion-extern-static.rs:13:31: 13:46
-+ // + span: $DIR/const-promotion-extern-static.rs:13:31: 13:46
+- _2 = &_3; // scope 0 at $DIR/const-promotion-extern-static.rs:13:31: 13:55
++ // + span: $DIR/const-promotion-extern-static.rs:13:31: 13:55
+ // + literal: Const { ty: &[&i32; 1], val: Unevaluated(Unevaluated { def: WithOptConstParam { did: DefId(0:7 ~ const_promotion_extern_static[55e6]::FOO), const_param_did: None }, substs_: Some([]), promoted: Some(promoted[0]) }) }
-+ _2 = &(*_6); // scope 0 at $DIR/const-promotion-extern-static.rs:13:31: 13:46
- _1 = move _2 as &[&i32] (Pointer(Unsize)); // scope 0 at $DIR/const-promotion-extern-static.rs:13:31: 13:46
++ _2 = &(*_6); // scope 0 at $DIR/const-promotion-extern-static.rs:13:31: 13:55
+ _1 = move _2 as &[&i32] (Pointer(Unsize)); // scope 0 at $DIR/const-promotion-extern-static.rs:13:31: 13:55
- StorageDead(_4); // scope 0 at $DIR/const-promotion-extern-static.rs:13:45: 13:46
StorageDead(_2); // scope 0 at $DIR/const-promotion-extern-static.rs:13:45: 13:46
_0 = core::slice::<impl [&i32]>::as_ptr(move _1) -> [return: bb1, unwind: bb2]; // scope 0 at $DIR/const-promotion-extern-static.rs:13:31: 13:55
debug s => _1; // in scope 0 at $DIR/deduplicate_blocks.rs:2:36: 2:37
let mut _0: bool; // return place in scope 0 at $DIR/deduplicate_blocks.rs:2:48: 2:52
let mut _2: &[u8]; // in scope 0 at $DIR/deduplicate_blocks.rs:3:11: 3:23
- let mut _3: &str; // in scope 0 at $DIR/deduplicate_blocks.rs:3:11: 3:12
+ let mut _3: &str; // in scope 0 at $DIR/deduplicate_blocks.rs:3:11: 3:23
let mut _4: usize; // in scope 0 at $DIR/deduplicate_blocks.rs:5:9: 5:31
let mut _5: bool; // in scope 0 at $DIR/deduplicate_blocks.rs:5:9: 5:31
let mut _6: usize; // in scope 0 at $DIR/deduplicate_blocks.rs:4:9: 4:37
bb0: {
StorageLive(_2); // scope 0 at $DIR/deduplicate_blocks.rs:3:11: 3:23
- StorageLive(_3); // scope 0 at $DIR/deduplicate_blocks.rs:3:11: 3:12
- _3 = _1; // scope 0 at $DIR/deduplicate_blocks.rs:3:11: 3:12
+ StorageLive(_3); // scope 0 at $DIR/deduplicate_blocks.rs:3:11: 3:23
+ _3 = _1; // scope 0 at $DIR/deduplicate_blocks.rs:3:11: 3:23
StorageLive(_8); // scope 2 at $DIR/deduplicate_blocks.rs:3:11: 3:23
_8 = _3; // scope 2 at $DIR/deduplicate_blocks.rs:3:11: 3:23
- _2 = transmute::<&str, &[u8]>(move _8) -> bb14; // scope 2 at $DIR/deduplicate_blocks.rs:3:11: 3:23
debug upper => _3; // in scope 0 at $DIR/funky_arms.rs:11:69: 11:74
let mut _0: std::result::Result<(), std::fmt::Error>; // return place in scope 0 at $DIR/funky_arms.rs:11:85: 11:91
let _4: bool; // in scope 0 at $DIR/funky_arms.rs:15:9: 15:19
- let mut _5: &std::fmt::Formatter; // in scope 0 at $DIR/funky_arms.rs:15:22: 15:25
+ let mut _5: &std::fmt::Formatter; // in scope 0 at $DIR/funky_arms.rs:15:22: 15:37
let mut _7: std::option::Option<usize>; // in scope 0 at $DIR/funky_arms.rs:24:30: 24:45
- let mut _8: &std::fmt::Formatter; // in scope 0 at $DIR/funky_arms.rs:24:30: 24:33
+ let mut _8: &std::fmt::Formatter; // in scope 0 at $DIR/funky_arms.rs:24:30: 24:45
let mut _9: isize; // in scope 0 at $DIR/funky_arms.rs:24:12: 24:27
let mut _11: &mut std::fmt::Formatter; // in scope 0 at $DIR/funky_arms.rs:26:43: 26:46
let mut _12: &T; // in scope 0 at $DIR/funky_arms.rs:26:48: 26:51
bb0: {
StorageLive(_4); // scope 0 at $DIR/funky_arms.rs:15:9: 15:19
- StorageLive(_5); // scope 0 at $DIR/funky_arms.rs:15:22: 15:25
- _5 = &(*_1); // scope 0 at $DIR/funky_arms.rs:15:22: 15:25
+ StorageLive(_5); // scope 0 at $DIR/funky_arms.rs:15:22: 15:37
+ _5 = &(*_1); // scope 0 at $DIR/funky_arms.rs:15:22: 15:37
_4 = Formatter::sign_plus(move _5) -> bb1; // scope 0 at $DIR/funky_arms.rs:15:22: 15:37
// mir::Constant
// + span: $DIR/funky_arms.rs:15:26: 15:35
bb4: {
StorageLive(_7); // scope 2 at $DIR/funky_arms.rs:24:30: 24:45
- StorageLive(_8); // scope 2 at $DIR/funky_arms.rs:24:30: 24:33
- _8 = &(*_1); // scope 2 at $DIR/funky_arms.rs:24:30: 24:33
+ StorageLive(_8); // scope 2 at $DIR/funky_arms.rs:24:30: 24:45
+ _8 = &(*_1); // scope 2 at $DIR/funky_arms.rs:24:30: 24:45
_7 = Formatter::precision(move _8) -> bb5; // scope 2 at $DIR/funky_arms.rs:24:30: 24:45
// mir::Constant
// + span: $DIR/funky_arms.rs:24:34: 24:43
fn clone(_1: fn(A, B)) -> fn(A, B) {
debug f => _1; // in scope 0 at $DIR/inline-shims.rs:5:20: 5:21
let mut _0: fn(A, B); // return place in scope 0 at $DIR/inline-shims.rs:5:36: 5:44
- let mut _2: &fn(A, B); // in scope 0 at $DIR/inline-shims.rs:6:5: 6:6
+ let mut _2: &fn(A, B); // in scope 0 at $DIR/inline-shims.rs:6:5: 6:14
+ scope 1 (inlined <fn(A, B) as Clone>::clone - shim(fn(A, B))) { // at $DIR/inline-shims.rs:6:5: 6:14
+ }
bb0: {
- StorageLive(_2); // scope 0 at $DIR/inline-shims.rs:6:5: 6:6
- _2 = &_1; // scope 0 at $DIR/inline-shims.rs:6:5: 6:6
+ StorageLive(_2); // scope 0 at $DIR/inline-shims.rs:6:5: 6:14
+ _2 = &_1; // scope 0 at $DIR/inline-shims.rs:6:5: 6:14
- _0 = <fn(A, B) as Clone>::clone(move _2) -> bb1; // scope 0 at $DIR/inline-shims.rs:6:5: 6:14
- // mir::Constant
- // + span: $DIR/inline-shims.rs:6:7: 6:12
fn test(_1: &dyn X) -> u32 {
debug x => _1; // in scope 0 at $DIR/inline-trait-method.rs:8:9: 8:10
let mut _0: u32; // return place in scope 0 at $DIR/inline-trait-method.rs:8:23: 8:26
- let mut _2: &dyn X; // in scope 0 at $DIR/inline-trait-method.rs:9:5: 9:6
+ let mut _2: &dyn X; // in scope 0 at $DIR/inline-trait-method.rs:9:5: 9:10
bb0: {
- StorageLive(_2); // scope 0 at $DIR/inline-trait-method.rs:9:5: 9:6
- _2 = &(*_1); // scope 0 at $DIR/inline-trait-method.rs:9:5: 9:6
+ StorageLive(_2); // scope 0 at $DIR/inline-trait-method.rs:9:5: 9:10
+ _2 = &(*_1); // scope 0 at $DIR/inline-trait-method.rs:9:5: 9:10
_0 = <dyn X as X>::y(move _2) -> bb1; // scope 0 at $DIR/inline-trait-method.rs:9:5: 9:10
// mir::Constant
// + span: $DIR/inline-trait-method.rs:9:7: 9:8
let mut _0: &mut [T]; // return place in scope 0 at $DIR/issue-58867-inline-as-ref-as-mut.rs:2:29: 2:37
let mut _2: &mut [T]; // in scope 0 at $DIR/issue-58867-inline-as-ref-as-mut.rs:3:5: 3:15
let mut _3: &mut [T]; // in scope 0 at $DIR/issue-58867-inline-as-ref-as-mut.rs:3:5: 3:15
- let mut _4: &mut [T]; // in scope 0 at $DIR/issue-58867-inline-as-ref-as-mut.rs:3:5: 3:6
+ let mut _4: &mut [T]; // in scope 0 at $DIR/issue-58867-inline-as-ref-as-mut.rs:3:5: 3:15
scope 1 (inlined <[T] as AsMut<[T]>>::as_mut) { // at $DIR/issue-58867-inline-as-ref-as-mut.rs:3:5: 3:15
debug self => _4; // in scope 1 at $DIR/issue-58867-inline-as-ref-as-mut.rs:3:5: 3:15
let mut _5: &mut [T]; // in scope 1 at $DIR/issue-58867-inline-as-ref-as-mut.rs:3:5: 3:15
bb0: {
StorageLive(_2); // scope 0 at $DIR/issue-58867-inline-as-ref-as-mut.rs:3:5: 3:15
StorageLive(_3); // scope 0 at $DIR/issue-58867-inline-as-ref-as-mut.rs:3:5: 3:15
- StorageLive(_4); // scope 0 at $DIR/issue-58867-inline-as-ref-as-mut.rs:3:5: 3:6
- _4 = &mut (*_1); // scope 0 at $DIR/issue-58867-inline-as-ref-as-mut.rs:3:5: 3:6
+ StorageLive(_4); // scope 0 at $DIR/issue-58867-inline-as-ref-as-mut.rs:3:5: 3:15
+ _4 = &mut (*_1); // scope 0 at $DIR/issue-58867-inline-as-ref-as-mut.rs:3:5: 3:15
StorageLive(_5); // scope 1 at $DIR/issue-58867-inline-as-ref-as-mut.rs:3:5: 3:15
_5 = &mut (*_4); // scope 1 at $DIR/issue-58867-inline-as-ref-as-mut.rs:3:5: 3:15
_3 = &mut (*_5); // scope 1 at $DIR/issue-58867-inline-as-ref-as-mut.rs:3:5: 3:15
let mut _0: &mut T; // return place in scope 0 at $DIR/issue-58867-inline-as-ref-as-mut.rs:7:32: 7:38
let mut _2: &mut T; // in scope 0 at $DIR/issue-58867-inline-as-ref-as-mut.rs:8:5: 8:15
let mut _3: &mut T; // in scope 0 at $DIR/issue-58867-inline-as-ref-as-mut.rs:8:5: 8:15
- let mut _4: &mut std::boxed::Box<T>; // in scope 0 at $DIR/issue-58867-inline-as-ref-as-mut.rs:8:5: 8:6
+ let mut _4: &mut std::boxed::Box<T>; // in scope 0 at $DIR/issue-58867-inline-as-ref-as-mut.rs:8:5: 8:15
scope 1 (inlined <Box<T> as AsMut<T>>::as_mut) { // at $DIR/issue-58867-inline-as-ref-as-mut.rs:8:5: 8:15
debug self => _4; // in scope 1 at $DIR/issue-58867-inline-as-ref-as-mut.rs:8:5: 8:15
let mut _5: &mut T; // in scope 1 at $DIR/issue-58867-inline-as-ref-as-mut.rs:8:5: 8:15
bb0: {
StorageLive(_2); // scope 0 at $DIR/issue-58867-inline-as-ref-as-mut.rs:8:5: 8:15
StorageLive(_3); // scope 0 at $DIR/issue-58867-inline-as-ref-as-mut.rs:8:5: 8:15
- StorageLive(_4); // scope 0 at $DIR/issue-58867-inline-as-ref-as-mut.rs:8:5: 8:6
- _4 = &mut (*_1); // scope 0 at $DIR/issue-58867-inline-as-ref-as-mut.rs:8:5: 8:6
+ StorageLive(_4); // scope 0 at $DIR/issue-58867-inline-as-ref-as-mut.rs:8:5: 8:15
+ _4 = &mut (*_1); // scope 0 at $DIR/issue-58867-inline-as-ref-as-mut.rs:8:5: 8:15
StorageLive(_5); // scope 1 at $DIR/issue-58867-inline-as-ref-as-mut.rs:8:5: 8:15
StorageLive(_6); // scope 1 at $DIR/issue-58867-inline-as-ref-as-mut.rs:8:5: 8:15
_6 = &mut (*(*_4)); // scope 1 at $DIR/issue-58867-inline-as-ref-as-mut.rs:8:5: 8:15
debug x => _1; // in scope 0 at $DIR/issue-58867-inline-as-ref-as-mut.rs:12:13: 12:14
let mut _0: &[T]; // return place in scope 0 at $DIR/issue-58867-inline-as-ref-as-mut.rs:12:25: 12:29
let _2: &[T]; // in scope 0 at $DIR/issue-58867-inline-as-ref-as-mut.rs:13:5: 13:15
- let mut _3: &[T]; // in scope 0 at $DIR/issue-58867-inline-as-ref-as-mut.rs:13:5: 13:6
+ let mut _3: &[T]; // in scope 0 at $DIR/issue-58867-inline-as-ref-as-mut.rs:13:5: 13:15
scope 1 (inlined <[T] as AsRef<[T]>>::as_ref) { // at $DIR/issue-58867-inline-as-ref-as-mut.rs:13:5: 13:15
debug self => _3; // in scope 1 at $DIR/issue-58867-inline-as-ref-as-mut.rs:13:5: 13:15
}
bb0: {
StorageLive(_2); // scope 0 at $DIR/issue-58867-inline-as-ref-as-mut.rs:13:5: 13:15
- StorageLive(_3); // scope 0 at $DIR/issue-58867-inline-as-ref-as-mut.rs:13:5: 13:6
- _3 = &(*_1); // scope 0 at $DIR/issue-58867-inline-as-ref-as-mut.rs:13:5: 13:6
+ StorageLive(_3); // scope 0 at $DIR/issue-58867-inline-as-ref-as-mut.rs:13:5: 13:15
+ _3 = &(*_1); // scope 0 at $DIR/issue-58867-inline-as-ref-as-mut.rs:13:5: 13:15
_2 = _3; // scope 1 at $DIR/issue-58867-inline-as-ref-as-mut.rs:13:5: 13:15
_0 = &(*_2); // scope 0 at $DIR/issue-58867-inline-as-ref-as-mut.rs:13:5: 13:15
StorageDead(_3); // scope 0 at $DIR/issue-58867-inline-as-ref-as-mut.rs:13:14: 13:15
debug x => _1; // in scope 0 at $DIR/issue-58867-inline-as-ref-as-mut.rs:17:13: 17:14
let mut _0: &T; // return place in scope 0 at $DIR/issue-58867-inline-as-ref-as-mut.rs:17:28: 17:30
let _2: &T; // in scope 0 at $DIR/issue-58867-inline-as-ref-as-mut.rs:18:5: 18:15
- let mut _3: &std::boxed::Box<T>; // in scope 0 at $DIR/issue-58867-inline-as-ref-as-mut.rs:18:5: 18:6
+ let mut _3: &std::boxed::Box<T>; // in scope 0 at $DIR/issue-58867-inline-as-ref-as-mut.rs:18:5: 18:15
scope 1 (inlined <Box<T> as AsRef<T>>::as_ref) { // at $DIR/issue-58867-inline-as-ref-as-mut.rs:18:5: 18:15
debug self => _3; // in scope 1 at $DIR/issue-58867-inline-as-ref-as-mut.rs:18:5: 18:15
}
bb0: {
StorageLive(_2); // scope 0 at $DIR/issue-58867-inline-as-ref-as-mut.rs:18:5: 18:15
- StorageLive(_3); // scope 0 at $DIR/issue-58867-inline-as-ref-as-mut.rs:18:5: 18:6
- _3 = &(*_1); // scope 0 at $DIR/issue-58867-inline-as-ref-as-mut.rs:18:5: 18:6
+ StorageLive(_3); // scope 0 at $DIR/issue-58867-inline-as-ref-as-mut.rs:18:5: 18:15
+ _3 = &(*_1); // scope 0 at $DIR/issue-58867-inline-as-ref-as-mut.rs:18:5: 18:15
_2 = &(*(*_3)); // scope 1 at $DIR/issue-58867-inline-as-ref-as-mut.rs:18:5: 18:15
_0 = &(*_2); // scope 0 at $DIR/issue-58867-inline-as-ref-as-mut.rs:18:5: 18:15
StorageDead(_3); // scope 0 at $DIR/issue-58867-inline-as-ref-as-mut.rs:18:14: 18:15
let mut _3: bool; // in scope 0 at $DIR/lower_slice_len.rs:5:8: 5:27
let mut _4: usize; // in scope 0 at $DIR/lower_slice_len.rs:5:8: 5:13
let mut _5: usize; // in scope 0 at $DIR/lower_slice_len.rs:5:16: 5:27
- let mut _6: &[u8]; // in scope 0 at $DIR/lower_slice_len.rs:5:16: 5:21
+ let mut _6: &[u8]; // in scope 0 at $DIR/lower_slice_len.rs:5:16: 5:27
let _7: usize; // in scope 0 at $DIR/lower_slice_len.rs:6:15: 6:20
let mut _8: usize; // in scope 0 at $DIR/lower_slice_len.rs:6:9: 6:21
let mut _9: bool; // in scope 0 at $DIR/lower_slice_len.rs:6:9: 6:21
StorageLive(_4); // scope 0 at $DIR/lower_slice_len.rs:5:8: 5:13
_4 = _1; // scope 0 at $DIR/lower_slice_len.rs:5:8: 5:13
StorageLive(_5); // scope 0 at $DIR/lower_slice_len.rs:5:16: 5:27
- StorageLive(_6); // scope 0 at $DIR/lower_slice_len.rs:5:16: 5:21
- _6 = &(*_2); // scope 0 at $DIR/lower_slice_len.rs:5:16: 5:21
+ StorageLive(_6); // scope 0 at $DIR/lower_slice_len.rs:5:16: 5:27
+ _6 = &(*_2); // scope 0 at $DIR/lower_slice_len.rs:5:16: 5:27
- _5 = core::slice::<impl [u8]>::len(move _6) -> bb1; // scope 0 at $DIR/lower_slice_len.rs:5:16: 5:27
- // mir::Constant
- // + span: $DIR/lower_slice_len.rs:5:22: 5:25
let mut _0: (); // return place in scope 0 at $DIR/no-spurious-drop-after-call.rs:8:11: 8:11
let _1: (); // in scope 0 at $DIR/no-spurious-drop-after-call.rs:9:5: 9:35
let mut _2: std::string::String; // in scope 0 at $DIR/no-spurious-drop-after-call.rs:9:20: 9:34
- let mut _3: &str; // in scope 0 at $DIR/no-spurious-drop-after-call.rs:9:20: 9:22
+ let mut _3: &str; // in scope 0 at $DIR/no-spurious-drop-after-call.rs:9:20: 9:34
let _4: &str; // in scope 0 at $DIR/no-spurious-drop-after-call.rs:9:20: 9:22
bb0: {
StorageLive(_1); // scope 0 at $DIR/no-spurious-drop-after-call.rs:9:5: 9:35
StorageLive(_2); // scope 0 at $DIR/no-spurious-drop-after-call.rs:9:20: 9:34
- StorageLive(_3); // scope 0 at $DIR/no-spurious-drop-after-call.rs:9:20: 9:22
+ StorageLive(_3); // scope 0 at $DIR/no-spurious-drop-after-call.rs:9:20: 9:34
StorageLive(_4); // scope 0 at $DIR/no-spurious-drop-after-call.rs:9:20: 9:22
_4 = const ""; // scope 0 at $DIR/no-spurious-drop-after-call.rs:9:20: 9:22
// ty::Const
// mir::Constant
// + span: $DIR/no-spurious-drop-after-call.rs:9:20: 9:22
// + literal: Const { ty: &str, val: Value(Slice { data: Allocation { bytes: [], relocations: Relocations(SortedMap { data: [] }), init_mask: InitMask { blocks: [], len: Size { raw: 0 } }, align: Align { pow2: 0 }, mutability: Not, extra: () }, start: 0, end: 0 }) }
- _3 = &(*_4); // scope 0 at $DIR/no-spurious-drop-after-call.rs:9:20: 9:22
+ _3 = &(*_4); // scope 0 at $DIR/no-spurious-drop-after-call.rs:9:20: 9:34
_2 = <str as ToString>::to_string(move _3) -> bb1; // scope 0 at $DIR/no-spurious-drop-after-call.rs:9:20: 9:34
// mir::Constant
// + span: $DIR/no-spurious-drop-after-call.rs:9:23: 9:32
let mut _0: (); // return place in scope 0 at $DIR/receiver-ptr-mutability.rs:13:11: 13:11
let _1: *mut Test as UserTypeProjection { base: UserType(0), projs: [] }; // in scope 0 at $DIR/receiver-ptr-mutability.rs:14:9: 14:12
let _2: (); // in scope 0 at $DIR/receiver-ptr-mutability.rs:15:5: 15:12
- let mut _3: *const Test; // in scope 0 at $DIR/receiver-ptr-mutability.rs:15:5: 15:8
+ let mut _3: *const Test; // in scope 0 at $DIR/receiver-ptr-mutability.rs:15:5: 15:12
let mut _4: *mut Test; // in scope 0 at $DIR/receiver-ptr-mutability.rs:15:5: 15:8
let _6: &&&&*mut Test; // in scope 0 at $DIR/receiver-ptr-mutability.rs:18:34: 18:41
let _7: &&&*mut Test; // in scope 0 at $DIR/receiver-ptr-mutability.rs:18:35: 18:41
let _8: &&*mut Test; // in scope 0 at $DIR/receiver-ptr-mutability.rs:18:36: 18:41
let _9: &*mut Test; // in scope 0 at $DIR/receiver-ptr-mutability.rs:18:37: 18:41
let _10: (); // in scope 0 at $DIR/receiver-ptr-mutability.rs:19:5: 19:16
- let mut _11: *const Test; // in scope 0 at $DIR/receiver-ptr-mutability.rs:19:5: 19:12
- let mut _12: *mut Test; // in scope 0 at $DIR/receiver-ptr-mutability.rs:19:5: 19:12
+ let mut _11: *const Test; // in scope 0 at $DIR/receiver-ptr-mutability.rs:19:5: 19:16
+ let mut _12: *mut Test; // in scope 0 at $DIR/receiver-ptr-mutability.rs:19:5: 19:16
scope 1 {
debug ptr => _1; // in scope 1 at $DIR/receiver-ptr-mutability.rs:14:9: 14:12
let _5: &&&&*mut Test as UserTypeProjection { base: UserType(2), projs: [] }; // in scope 1 at $DIR/receiver-ptr-mutability.rs:18:9: 18:16
FakeRead(ForLet(None), _1); // scope 0 at $DIR/receiver-ptr-mutability.rs:14:9: 14:12
AscribeUserType(_1, o, UserTypeProjection { base: UserType(1), projs: [] }); // scope 0 at $DIR/receiver-ptr-mutability.rs:14:14: 14:23
StorageLive(_2); // scope 1 at $DIR/receiver-ptr-mutability.rs:15:5: 15:12
- StorageLive(_3); // scope 1 at $DIR/receiver-ptr-mutability.rs:15:5: 15:8
+ StorageLive(_3); // scope 1 at $DIR/receiver-ptr-mutability.rs:15:5: 15:12
StorageLive(_4); // scope 1 at $DIR/receiver-ptr-mutability.rs:15:5: 15:8
_4 = _1; // scope 1 at $DIR/receiver-ptr-mutability.rs:15:5: 15:8
- _3 = move _4 as *const Test (Pointer(MutToConstPointer)); // scope 1 at $DIR/receiver-ptr-mutability.rs:15:5: 15:8
+ _3 = move _4 as *const Test (Pointer(MutToConstPointer)); // scope 1 at $DIR/receiver-ptr-mutability.rs:15:5: 15:12
StorageDead(_4); // scope 1 at $DIR/receiver-ptr-mutability.rs:15:7: 15:8
_2 = Test::x(move _3) -> [return: bb2, unwind: bb4]; // scope 1 at $DIR/receiver-ptr-mutability.rs:15:5: 15:12
// mir::Constant
AscribeUserType(_5, o, UserTypeProjection { base: UserType(3), projs: [] }); // scope 1 at $DIR/receiver-ptr-mutability.rs:18:18: 18:31
StorageDead(_6); // scope 1 at $DIR/receiver-ptr-mutability.rs:18:41: 18:42
StorageLive(_10); // scope 2 at $DIR/receiver-ptr-mutability.rs:19:5: 19:16
- StorageLive(_11); // scope 2 at $DIR/receiver-ptr-mutability.rs:19:5: 19:12
- StorageLive(_12); // scope 2 at $DIR/receiver-ptr-mutability.rs:19:5: 19:12
- _12 = (*(*(*(*_5)))); // scope 2 at $DIR/receiver-ptr-mutability.rs:19:5: 19:12
- _11 = move _12 as *const Test (Pointer(MutToConstPointer)); // scope 2 at $DIR/receiver-ptr-mutability.rs:19:5: 19:12
+ StorageLive(_11); // scope 2 at $DIR/receiver-ptr-mutability.rs:19:5: 19:16
+ StorageLive(_12); // scope 2 at $DIR/receiver-ptr-mutability.rs:19:5: 19:16
+ _12 = (*(*(*(*_5)))); // scope 2 at $DIR/receiver-ptr-mutability.rs:19:5: 19:16
+ _11 = move _12 as *const Test (Pointer(MutToConstPointer)); // scope 2 at $DIR/receiver-ptr-mutability.rs:19:5: 19:16
StorageDead(_12); // scope 2 at $DIR/receiver-ptr-mutability.rs:19:11: 19:12
_10 = Test::x(move _11) -> [return: bb3, unwind: bb4]; // scope 2 at $DIR/receiver-ptr-mutability.rs:19:5: 19:16
// mir::Constant
let mut _0: (); // return place in scope 0 at $DIR/retag.rs:29:11: 29:11
let mut _1: i32; // in scope 0 at $DIR/retag.rs:30:9: 30:14
let _2: (); // in scope 0 at $DIR/retag.rs:31:5: 37:6
- let mut _4: &Test; // in scope 0 at $DIR/retag.rs:32:17: 32:24
+ let mut _4: &Test; // in scope 0 at $DIR/retag.rs:32:17: 32:36
let _5: Test; // in scope 0 at $DIR/retag.rs:32:17: 32:24
let mut _6: &mut i32; // in scope 0 at $DIR/retag.rs:32:29: 32:35
let mut _7: &mut i32; // in scope 0 at $DIR/retag.rs:32:29: 32:35
let mut _17: &i32; // in scope 0 at $DIR/retag.rs:44:16: 44:18
let _18: &i32; // in scope 0 at $DIR/retag.rs:44:16: 44:18
let _19: &i32; // in scope 0 at $DIR/retag.rs:47:5: 47:24
- let mut _20: &Test; // in scope 0 at $DIR/retag.rs:47:5: 47:12
+ let mut _20: &Test; // in scope 0 at $DIR/retag.rs:47:5: 47:24
let _21: Test; // in scope 0 at $DIR/retag.rs:47:5: 47:12
let mut _22: &i32; // in scope 0 at $DIR/retag.rs:47:21: 47:23
let _23: &i32; // in scope 0 at $DIR/retag.rs:47:21: 47:23
_1 = const 0_i32; // scope 0 at $DIR/retag.rs:30:17: 30:18
StorageLive(_2); // scope 1 at $DIR/retag.rs:31:5: 37:6
StorageLive(_3); // scope 1 at $DIR/retag.rs:32:13: 32:14
- StorageLive(_4); // scope 1 at $DIR/retag.rs:32:17: 32:24
+ StorageLive(_4); // scope 1 at $DIR/retag.rs:32:17: 32:36
StorageLive(_5); // scope 1 at $DIR/retag.rs:32:17: 32:24
_5 = Test(const 0_i32); // scope 1 at $DIR/retag.rs:32:17: 32:24
- _4 = &_5; // scope 1 at $DIR/retag.rs:32:17: 32:24
- Retag(_4); // scope 1 at $DIR/retag.rs:32:17: 32:24
+ _4 = &_5; // scope 1 at $DIR/retag.rs:32:17: 32:36
+ Retag(_4); // scope 1 at $DIR/retag.rs:32:17: 32:36
StorageLive(_6); // scope 1 at $DIR/retag.rs:32:29: 32:35
StorageLive(_7); // scope 1 at $DIR/retag.rs:32:29: 32:35
_7 = &mut _1; // scope 1 at $DIR/retag.rs:32:29: 32:35
StorageDead(_16); // scope 6 at $DIR/retag.rs:44:18: 44:19
StorageDead(_18); // scope 6 at $DIR/retag.rs:44:19: 44:20
StorageLive(_19); // scope 7 at $DIR/retag.rs:47:5: 47:24
- StorageLive(_20); // scope 7 at $DIR/retag.rs:47:5: 47:12
+ StorageLive(_20); // scope 7 at $DIR/retag.rs:47:5: 47:24
StorageLive(_21); // scope 7 at $DIR/retag.rs:47:5: 47:12
_21 = Test(const 0_i32); // scope 7 at $DIR/retag.rs:47:5: 47:12
- _20 = &_21; // scope 7 at $DIR/retag.rs:47:5: 47:12
- Retag(_20); // scope 7 at $DIR/retag.rs:47:5: 47:12
+ _20 = &_21; // scope 7 at $DIR/retag.rs:47:5: 47:24
+ Retag(_20); // scope 7 at $DIR/retag.rs:47:5: 47:24
StorageLive(_22); // scope 7 at $DIR/retag.rs:47:21: 47:23
StorageLive(_23); // scope 7 at $DIR/retag.rs:47:21: 47:23
_28 = const main::promoted[0]; // scope 7 at $DIR/retag.rs:47:21: 47:23
#![deny(warnings)]
extern crate rustc_codegen_ssa;
-extern crate rustc_errors;
-extern crate rustc_middle;
extern crate rustc_data_structures;
extern crate rustc_driver;
+extern crate rustc_errors;
extern crate rustc_hir;
+extern crate rustc_metadata;
+extern crate rustc_middle;
extern crate rustc_session;
extern crate rustc_span;
extern crate rustc_symbol_mangling;
use rustc_codegen_ssa::{CodegenResults, CrateInfo};
use rustc_data_structures::fx::FxHashMap;
use rustc_errors::ErrorReported;
+use rustc_metadata::EncodedMetadata;
use rustc_middle::dep_graph::{WorkProduct, WorkProductId};
-use rustc_middle::middle::cstore::EncodedMetadata;
use rustc_middle::ty::TyCtxt;
use rustc_session::config::OutputFilenames;
use rustc_session::Session;
auto trait Freeze {}
#[lang = "start"]
-fn start(_main: *const u8, _argc: isize, _argv: *const *const u8) -> isize {
+fn start<T>(_main: fn() -> T, _argc: isize, _argv: *const *const u8) -> isize {
0
}
--- /dev/null
+#![no_std]
+
+// @has impl.json "$.index[*][?(@.name=='Ux')].visibility" \"public\"
+// @has - "$.index[*][?(@.name=='Ux')].kind" \"union\"
+pub union Ux {
+ a: u32,
+ b: u64
+}
+
+// @has - "$.index[*][?(@.name=='Num')].visibility" \"public\"
+// @has - "$.index[*][?(@.name=='Num')].kind" \"trait\"
+pub trait Num {}
+
+// @count - "$.index[*][?(@.name=='Ux')].inner.impls" 1
+impl Num for Ux {}
| first mutable borrow occurs here
| first borrow later used here
LL | xs.push(1)
- | ^^ second mutable borrow occurs here
+ | ^^^^^^^^^^ second mutable borrow occurs here
error: aborting due to previous error
--- /dev/null
+// Regression test for #89305, where a variable was erroneously reported
+// as both unused and possibly-uninitialized.
+
+// check-pass
+
+#![feature(asm)]
+#![warn(unused)]
+
+fn main() {
+ unsafe {
+ let x: () = asm!("nop");
+ //~^ WARNING: unused variable: `x`
+ }
+}
--- /dev/null
+warning: unused variable: `x`
+ --> $DIR/issue-89305.rs:11:13
+ |
+LL | let x: () = asm!("nop");
+ | ^ help: if this is intentional, prefix it with an underscore: `_x`
+ |
+note: the lint level is defined here
+ --> $DIR/issue-89305.rs:7:9
+ |
+LL | #![warn(unused)]
+ | ^^^^^^
+ = note: `#[warn(unused_variables)]` implied by `#[warn(unused)]`
+
+warning: 1 warning emitted
+
| ^^^^^^^^^^^^^^^^^^
help: disambiguate the associated constant for candidate #1
|
-LL | const X: i32 = Foo::ID;
- | ~~~~~
+LL | const X: i32 = <i32 as Foo>::ID;
+ | ~~~~~~~~~~~~~~
help: disambiguate the associated constant for candidate #2
|
-LL | const X: i32 = Bar::ID;
- | ~~~~~
+LL | const X: i32 = <i32 as Bar>::ID;
+ | ~~~~~~~~~~~~~~
error: aborting due to previous error
LL | | }
| |_^
|
- = help: consider adding a `#![recursion_limit="256"]` attribute to your crate (`hr_associated_type_bound_2`)
+ = help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`hr_associated_type_bound_2`)
note: required because of the requirements on the impl of `for<'b> X<'b>` for `u32`
--> $DIR/hr-associated-type-bound-2.rs:11:6
|
LL | type U = str;
| ^^^^^^^^^^^^^
|
- = help: consider adding a `#![recursion_limit="256"]` attribute to your crate (`hr_associated_type_bound_2`)
+ = help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`hr_associated_type_bound_2`)
note: required because of the requirements on the impl of `for<'b> X<'b>` for `u32`
--> $DIR/hr-associated-type-bound-2.rs:11:6
|
LL | pub async fn f(x: Option<usize>) {
| - help: consider changing this to be mutable: `mut x`
LL | x.take();
- | ^ cannot borrow as mutable
+ | ^^^^^^^^ cannot borrow as mutable
error[E0384]: cannot assign twice to immutable variable `x`
--> $DIR/issue-61452.rs:9:5
LL | async fn response(data: Vec<u8>) {
| ---- help: consider changing this to be mutable: `mut data`
LL | data.reverse();
- | ^^^^ cannot borrow as mutable
+ | ^^^^^^^^^^^^^^ cannot borrow as mutable
error: aborting due to previous error
LL | let x: &Bottom = &t;
| ^^ deref recursion limit reached
|
- = help: consider adding a `#![recursion_limit="20"]` attribute to your crate (`issue_38940`)
+ = help: consider increasing the recursion limit by adding a `#![recursion_limit = "20"]` attribute to your crate (`issue_38940`)
error[E0308]: mismatched types
--> $DIR/issue-38940.rs:43:22
| - value moved here
LL | +
LL | x.clone();
- | ^ value borrowed here after move
+ | ^^^^^^^^^ value borrowed here after move
|
help: consider further restricting this bound
|
| ^ move out of `x` occurs here
LL |
LL | r.use_ref();
- | - borrow later used here
+ | ----------- borrow later used here
error[E0502]: cannot borrow `x.0` as mutable because it is also borrowed as immutable
--> $DIR/borrow-tuple-fields.rs:18:13
LL | let b = &mut x.0;
| ^^^^^^^^ mutable borrow occurs here
LL | a.use_ref();
- | - immutable borrow later used here
+ | ----------- immutable borrow later used here
error[E0499]: cannot borrow `x.0` as mutable more than once at a time
--> $DIR/borrow-tuple-fields.rs:23:13
LL | let b = &mut x.0;
| ^^^^^^^^ second mutable borrow occurs here
LL | a.use_ref();
- | - first borrow later used here
+ | ----------- first borrow later used here
error[E0505]: cannot move out of `x` because it is borrowed
--> $DIR/borrow-tuple-fields.rs:28:13
LL | let y = x;
| ^ move out of `x` occurs here
LL | r.use_ref();
- | - borrow later used here
+ | ----------- borrow later used here
error[E0502]: cannot borrow `x.0` as mutable because it is also borrowed as immutable
--> $DIR/borrow-tuple-fields.rs:33:13
LL | let b = &mut x.0;
| ^^^^^^^^ mutable borrow occurs here
LL | a.use_ref();
- | - immutable borrow later used here
+ | ----------- immutable borrow later used here
error[E0499]: cannot borrow `x.0` as mutable more than once at a time
--> $DIR/borrow-tuple-fields.rs:38:13
LL | let b = &mut x.0;
| ^^^^^^^^ second mutable borrow occurs here
LL | a.use_mut();
- | - first borrow later used here
+ | ----------- first borrow later used here
error: aborting due to 6 previous errors
LL | fn func(arg: S) {
| --- help: consider changing this to be mutable: `mut arg`
LL | arg.mutate();
- | ^^^ cannot borrow as mutable
+ | ^^^^^^^^^^^^ cannot borrow as mutable
error[E0596]: cannot borrow `arg` as mutable, as it is not declared as mutable
--> $DIR/borrowck-argument.rs:15:9
LL | fn method(&self, arg: S) {
| --- help: consider changing this to be mutable: `mut arg`
LL | arg.mutate();
- | ^^^ cannot borrow as mutable
+ | ^^^^^^^^^^^^ cannot borrow as mutable
error[E0596]: cannot borrow `arg` as mutable, as it is not declared as mutable
--> $DIR/borrowck-argument.rs:21:9
LL | fn default(&self, arg: S) {
| --- help: consider changing this to be mutable: `mut arg`
LL | arg.mutate();
- | ^^^ cannot borrow as mutable
+ | ^^^^^^^^^^^^ cannot borrow as mutable
error[E0596]: cannot borrow `arg` as mutable, as it is not declared as mutable
--> $DIR/borrowck-argument.rs:32:17
|
LL | (|arg: S| { arg.mutate() })(s);
- | --- ^^^ cannot borrow as mutable
+ | --- ^^^^^^^^^^^^ cannot borrow as mutable
| |
| help: consider changing this to be mutable: `mut arg`
LL | let x = Foo { x: 3 };
| - help: consider changing this to be mutable: `mut x`
LL | x.printme();
- | ^ cannot borrow as mutable
+ | ^^^^^^^^^^^ cannot borrow as mutable
error: aborting due to previous error
LL | let a: Box<_> = Box::new(A);
| - help: consider changing this to be mutable: `mut a`
LL | a.foo();
- | ^ cannot borrow as mutable
+ | ^^^^^^^ cannot borrow as mutable
error: aborting due to previous error
--> $DIR/borrowck-borrow-mut-object-twice.rs:13:5
|
LL | let y = x.f1();
- | - first mutable borrow occurs here
+ | ------ first mutable borrow occurs here
LL | x.f2();
- | ^ second mutable borrow occurs here
+ | ^^^^^^ second mutable borrow occurs here
LL | y.use_ref();
- | - first borrow later used here
+ | ----------- first borrow later used here
error: aborting due to previous error
--> $DIR/borrowck-borrow-overloaded-auto-deref.rs:72:5
|
LL | x.set(0, 0);
- | ^ cannot borrow as mutable
+ | ^^^^^^^^^^^ cannot borrow as mutable
|
= help: trait `DerefMut` is required to modify through a dereference, but it is not implemented for `Rc<Point>`
--> $DIR/borrowck-borrow-overloaded-auto-deref.rs:76:5
|
LL | x.set(0, 0);
- | ^ cannot borrow as mutable
+ | ^^^^^^^^^^^ cannot borrow as mutable
|
= help: trait `DerefMut` is required to modify through a dereference, but it is not implemented for `Rc<Point>`
--> $DIR/borrowck-borrow-overloaded-auto-deref.rs:84:5
|
LL | x.y_mut()
- | ^ cannot borrow as mutable
+ | ^^^^^^^^^ cannot borrow as mutable
|
= help: trait `DerefMut` is required to modify through a dereference, but it is not implemented for `Rc<Point>`
--> $DIR/borrowck-borrow-overloaded-auto-deref.rs:88:5
|
LL | x.y_mut()
- | ^ cannot borrow as mutable
+ | ^^^^^^^^^ cannot borrow as mutable
|
= help: trait `DerefMut` is required to modify through a dereference, but it is not implemented for `Rc<Point>`
--> $DIR/borrowck-borrow-overloaded-auto-deref.rs:92:6
|
LL | *x.y_mut() = 3;
- | ^ cannot borrow as mutable
+ | ^^^^^^^^^ cannot borrow as mutable
|
= help: trait `DerefMut` is required to modify through a dereference, but it is not implemented for `Rc<Point>`
--> $DIR/borrowck-borrow-overloaded-auto-deref.rs:96:6
|
LL | *x.y_mut() = 3;
- | ^ cannot borrow as mutable
+ | ^^^^^^^^^ cannot borrow as mutable
|
= help: trait `DerefMut` is required to modify through a dereference, but it is not implemented for `Rc<Point>`
--> $DIR/borrowck-borrow-overloaded-auto-deref.rs:100:6
|
LL | *x.y_mut() = 3;
- | ^ cannot borrow as mutable
+ | ^^^^^^^^^ cannot borrow as mutable
|
= help: trait `DerefMut` is required to modify through a dereference, but it is not implemented for `Rc<Point>`
| creates a temporary which is freed while still in use
...
LL | buggy_map.insert(43, &*tmp);
- | --------- borrow later used here
+ | --------------------------- borrow later used here
|
= note: consider using a `let` binding to create a longer lived value
LL | &mut this.x;
| ^^^^^^^^^^^ mutable borrow occurs here
LL | p.use_ref();
- | - immutable borrow later used here
+ | ----------- immutable borrow later used here
error: aborting due to previous error
--> $DIR/borrowck-describe-lvalue.rs:37:9
|
LL | let x = f.x();
- | - borrow of `f` occurs here
+ | ----- borrow of `f` occurs here
LL | f.x;
| ^^^ use of borrowed `f`
LL | drop(x);
--> $DIR/borrowck-describe-lvalue.rs:44:9
|
LL | let x = g.x();
- | - borrow of `g` occurs here
+ | ----- borrow of `g` occurs here
LL | g.0;
| ^^^ use of borrowed `g`
LL | drop(x);
--> $DIR/borrowck-describe-lvalue.rs:59:20
|
LL | let x = e.x();
- | - borrow of `e` occurs here
+ | ----- borrow of `e` occurs here
LL | match e {
LL | Baz::X(value) => value
| ^^^^^ use of borrowed `e`
--> $DIR/borrowck-describe-lvalue.rs:74:9
|
LL | let x = f.x();
- | - borrow of `*f` occurs here
+ | ----- borrow of `*f` occurs here
LL | f.x;
| ^^^ use of borrowed `*f`
LL | drop(x);
--> $DIR/borrowck-describe-lvalue.rs:81:9
|
LL | let x = g.x();
- | - borrow of `*g` occurs here
+ | ----- borrow of `*g` occurs here
LL | g.0;
| ^^^ use of borrowed `*g`
LL | drop(x);
--> $DIR/borrowck-describe-lvalue.rs:96:20
|
LL | let x = e.x();
- | - borrow of `*e` occurs here
+ | ----- borrow of `*e` occurs here
LL | match *e {
LL | Baz::X(value) => value
| ^^^^^ use of borrowed `*e`
+++ /dev/null
-error[E0502]: cannot borrow `vector` as mutable because it is also borrowed as immutable
- --> $DIR/borrowck-for-loop-head-linkage.rs:7:9
- |
-LL | for &x in &vector {
- | -------
- | |
- | immutable borrow occurs here
- | immutable borrow later used here
-LL | let cap = vector.capacity();
-LL | vector.extend(repeat(0));
- | ^^^^^^ mutable borrow occurs here
-
-error[E0502]: cannot borrow `vector` as mutable because it is also borrowed as immutable
- --> $DIR/borrowck-for-loop-head-linkage.rs:8:9
- |
-LL | for &x in &vector {
- | -------
- | |
- | immutable borrow occurs here
- | immutable borrow later used here
-...
-LL | vector[1] = 5;
- | ^^^^^^ mutable borrow occurs here
-
-error: aborting due to 2 previous errors
-
-For more information about this error, try `rustc --explain E0502`.
error[E0500]: closure requires unique access to `f` but it is already borrowed
--> $DIR/borrowck-insert-during-each.rs:18:9
|
-LL | f.foo(
- | - --- first borrow later used by call
- | |
- | borrow occurs here
-LL |
-LL | |a| {
- | ^^^ closure construction occurs here
-LL | f.n.insert(*a);
- | --- second borrow occurs due to use of `f` in closure
+LL | f.foo(
+ | - --- first borrow later used by call
+ | _____|
+ | |
+LL | |
+LL | | |a| {
+ | | ^^^ closure construction occurs here
+LL | | f.n.insert(*a);
+ | | --- second borrow occurs due to use of `f` in closure
+LL | | })
+ | |__________- borrow occurs here
error: aborting due to 2 previous errors
LL | let _a = x;
| ^ move out of `x` occurs here
LL | _y.use_ref();
- | -- borrow later used here
+ | ------------ borrow later used here
error: aborting due to previous error
LL | borrow_mut(&mut *v);
| ^^^^^^^ mutable borrow occurs here
LL | _w.use_ref();
- | -- immutable borrow later used here
+ | ------------ immutable borrow later used here
error: aborting due to previous error
LL | borrow_mut(&mut *v);
| ^^^^^^^ mutable borrow occurs here
LL | _w.use_ref();
- | -- immutable borrow later used here
+ | ------------ immutable borrow later used here
error: aborting due to previous error
| -- move occurs due to use in closure
LL | });
LL | w.use_ref();
- | - borrow later used here
+ | ----------- borrow later used here
error[E0505]: cannot move out of `v` because it is borrowed
--> $DIR/borrowck-loan-blocks-move-cc.rs:24:19
| -- move occurs due to use in closure
LL | });
LL | w.use_ref();
- | - borrow later used here
+ | ----------- borrow later used here
error: aborting due to 2 previous errors
LL | take(v);
| ^ move out of `v` occurs here
LL | w.use_ref();
- | - borrow later used here
+ | ----------- borrow later used here
error: aborting due to previous error
LL | let x = Foo(Box::new(3));
| - move occurs because `x` has type `Foo`, which does not implement the `Copy` trait
LL | let _y = {x} + x.clone(); // the `{x}` forces a move to occur
- | - ^ value borrowed here after move
+ | - ^^^^^^^^^ value borrowed here after move
| |
| value moved here
| ------ mutable borrow occurs here
...
LL | p.times(3);
- | ^ immutable borrow occurs here
+ | ^^^^^^^^^^ immutable borrow occurs here
LL |
LL | *q + 3; // OK to use the new alias `q`
| -- mutable borrow later used here
error[E0502]: cannot borrow `p` as mutable because it is also borrowed as immutable
--> $DIR/borrowck-loan-rcvr.rs:23:14
|
-LL | p.blockm(|| {
- | - ------ ^^ mutable borrow occurs here
- | | |
- | | immutable borrow later used by call
- | immutable borrow occurs here
-LL | p.x = 10;
- | --- second borrow occurs due to use of `p` in closure
+LL | p.blockm(|| {
+ | - ------ ^^ mutable borrow occurs here
+ | | |
+ | _____| immutable borrow later used by call
+ | |
+LL | | p.x = 10;
+ | | --- second borrow occurs due to use of `p` in closure
+LL | | })
+ | |______- immutable borrow occurs here
error[E0502]: cannot borrow `p` as immutable because it is also borrowed as mutable
--> $DIR/borrowck-loan-rcvr.rs:34:5
LL | let l = &mut p;
| ------ mutable borrow occurs here
LL | p.impurem();
- | ^ immutable borrow occurs here
+ | ^^^^^^^^^^^ immutable borrow occurs here
LL |
LL | l.x += 1;
| -------- mutable borrow later used here
LL | let z = *a;
| ^^ move out of `*a` occurs here
LL | b.use_ref();
- | - borrow later used here
+ | ----------- borrow later used here
error: aborting due to previous error
| ^^ move out of `t0` occurs here
LL | *t1 = 22;
LL | p.use_ref();
- | - borrow later used here
+ | ----------- borrow later used here
error: aborting due to previous error
--> $DIR/borrowck-move-out-of-overloaded-auto-deref.rs:4:14
|
LL | let _x = Rc::new(vec![1, 2]).into_iter();
- | ^^^^^^^^^^^^^^^^^^^ move occurs because value has type `Vec<i32>`, which does not implement the `Copy` trait
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ move occurs because value has type `Vec<i32>`, which does not implement the `Copy` trait
error: aborting due to previous error
| ^^^^^^ second mutable borrow occurs here
LL | 2 => { addr.push(&mut x); }
LL | _ => { addr.push(&mut x); }
- | ---- ------ first mutable borrow occurs here
- | |
+ | -----------------
+ | | |
+ | | first mutable borrow occurs here
| first borrow later used here
error[E0499]: cannot borrow `x` as mutable more than once at a time
LL | 2 => { addr.push(&mut x); }
| ^^^^^^ second mutable borrow occurs here
LL | _ => { addr.push(&mut x); }
- | ---- ------ first mutable borrow occurs here
- | |
+ | -----------------
+ | | |
+ | | first mutable borrow occurs here
| first borrow later used here
error[E0499]: cannot borrow `x` as mutable more than once at a time
| ^^^^^^^ mutable borrow occurs here
LL | **t2 += 1; // Mutates `*t0`
LL | p.use_ref();
- | - immutable borrow later used here
+ | ----------- immutable borrow later used here
error[E0499]: cannot borrow `t0` as mutable more than once at a time
--> $DIR/borrowck-mut-borrow-of-mut-base-ptr.rs:19:18
| ^^^^^^^ second mutable borrow occurs here
LL | **t2 += 1; // Mutates `*t0` but not through `*p`
LL | p.use_mut();
- | - first borrow later used here
+ | ----------- first borrow later used here
error: aborting due to 2 previous errors
+++ /dev/null
-error[E0502]: cannot borrow `*x` as mutable because it is also borrowed as immutable
- --> $DIR/borrowck-object-lifetime.rs:20:13
- |
-LL | let y = x.borrowed();
- | - immutable borrow occurs here
-LL | let z = x.mut_borrowed();
- | ^ mutable borrow occurs here
-LL | y.use_ref();
- | - immutable borrow later used here
-
-error[E0502]: cannot borrow `x` as mutable because it is also borrowed as immutable
- --> $DIR/borrowck-object-lifetime.rs:26:13
- |
-LL | let y = x.borrowed();
- | - immutable borrow occurs here
-LL | let z = &mut x;
- | ^^^^^^ mutable borrow occurs here
-LL | y.use_ref();
- | - immutable borrow later used here
-
-error: aborting due to 2 previous errors
-
-For more information about this error, try `rustc --explain E0502`.
--> $DIR/borrowck-object-lifetime.rs:20:13
|
LL | let y = x.borrowed();
- | - immutable borrow occurs here
+ | ------------ immutable borrow occurs here
LL | let z = x.mut_borrowed();
| ^^^^^^^^^^^^^^^^ mutable borrow occurs here
LL | y.use_ref();
- | - immutable borrow later used here
+ | ----------- immutable borrow later used here
error[E0502]: cannot borrow `x` as mutable because it is also borrowed as immutable
--> $DIR/borrowck-object-lifetime.rs:26:13
|
LL | let y = x.borrowed();
- | - immutable borrow occurs here
+ | ------------ immutable borrow occurs here
LL | let z = &mut x;
| ^^^^^^ mutable borrow occurs here
LL | y.use_ref();
- | - immutable borrow later used here
+ | ----------- immutable borrow later used here
error: aborting due to 2 previous errors
LL | let q = &f[&s];
| ^ immutable borrow occurs here
LL | p.use_mut();
- | - mutable borrow later used here
+ | ----------- mutable borrow later used here
error[E0499]: cannot borrow `*f` as mutable more than once at a time
--> $DIR/borrowck-overloaded-index-autoderef.rs:43:18
LL | let q = &mut f[&s];
| ^ second mutable borrow occurs here
LL | p.use_mut();
- | - first borrow later used here
+ | ----------- first borrow later used here
error[E0499]: cannot borrow `f.foo` as mutable more than once at a time
--> $DIR/borrowck-overloaded-index-autoderef.rs:53:18
LL | let q = &mut f.foo[&s];
| ^^^^^ second mutable borrow occurs here
LL | p.use_mut();
- | - first borrow later used here
+ | ----------- first borrow later used here
error[E0502]: cannot borrow `f.foo` as mutable because it is also borrowed as immutable
--> $DIR/borrowck-overloaded-index-autoderef.rs:65:18
LL | let q = &mut f.foo[&s];
| ^^^^^ mutable borrow occurs here
LL | p.use_ref();
- | - immutable borrow later used here
+ | ----------- immutable borrow later used here
error[E0506]: cannot assign to `f.foo` because it is borrowed
--> $DIR/borrowck-overloaded-index-autoderef.rs:71:5
LL | f.foo = g;
| ^^^^^^^^^ assignment to borrowed `f.foo` occurs here
LL | p.use_ref();
- | - borrow later used here
+ | ----------- borrow later used here
error[E0506]: cannot assign to `*f` because it is borrowed
--> $DIR/borrowck-overloaded-index-autoderef.rs:77:5
LL | *f = g;
| ^^^^^^ assignment to borrowed `*f` occurs here
LL | p.use_ref();
- | - borrow later used here
+ | ----------- borrow later used here
error[E0506]: cannot assign to `f.foo` because it is borrowed
--> $DIR/borrowck-overloaded-index-autoderef.rs:83:5
LL | f.foo = g;
| ^^^^^^^^^ assignment to borrowed `f.foo` occurs here
LL | p.use_mut();
- | - borrow later used here
+ | ----------- borrow later used here
error[E0506]: cannot assign to `*f` because it is borrowed
--> $DIR/borrowck-overloaded-index-autoderef.rs:89:5
LL | *f = g;
| ^^^^^^ assignment to borrowed `*f` occurs here
LL | p.use_mut();
- | - borrow later used here
+ | ----------- borrow later used here
error: aborting due to 8 previous errors
| ^^ immutable borrow occurs here
...
LL | y.use_mut();
- | - mutable borrow later used here
+ | ----------- mutable borrow later used here
error[E0502]: cannot borrow `x` as mutable because it is also borrowed as immutable
--> $DIR/borrowck-report-with-custom-diagnostic.rs:21:21
| ^^^^^^ mutable borrow occurs here
...
LL | y.use_ref();
- | - immutable borrow later used here
+ | ----------- immutable borrow later used here
error[E0499]: cannot borrow `x` as mutable more than once at a time
--> $DIR/borrowck-report-with-custom-diagnostic.rs:36:17
| ^^^^^^ second mutable borrow occurs here
...
LL | y.use_mut();
- | - first borrow later used here
+ | ----------- first borrow later used here
error: aborting due to 3 previous errors
| ^^^^^^^ mutable borrow occurs here
LL | *t1 = 22;
LL | p.use_ref();
- | - immutable borrow later used here
+ | ----------- immutable borrow later used here
error: aborting due to previous error
LL | let b = u.c;
| ^^^ use of borrowed `u.s.a`
LL | ra.use_mut();
- | -- borrow later used here
+ | ------------ borrow later used here
error: aborting due to previous error
LL | borrow(&*v);
| ^^^ immutable borrow occurs here
LL | w.use_mut();
- | - mutable borrow later used here
+ | ----------- mutable borrow later used here
error[E0502]: cannot borrow `*v` as immutable because it is also borrowed as mutable
--> $DIR/borrowck-uniq-via-lend.rs:53:12
LL | borrow(&*v);
| ^^^ immutable borrow occurs here
LL | x.use_mut();
- | - mutable borrow later used here
+ | ----------- mutable borrow later used here
error: aborting due to 2 previous errors
| ------ first mutable borrow occurs here
...
LL | v.push(tail[0] + tail[1]);
- | ^ ------- first borrow later used here
- | |
+ | ^^^^^^^-------^^^^^^^^^^^
+ | | |
+ | | first borrow later used here
| second mutable borrow occurs here
error: aborting due to previous error
| ^^^^^^ assignment to borrowed `vec[_]` occurs here
LL |
LL | _a.use_ref();
- | -- borrow later used here
+ | ------------ borrow later used here
error[E0506]: cannot assign to `vec[_]` because it is borrowed
--> $DIR/borrowck-vec-pattern-nesting.rs:23:13
| ^^^^^^ assignment to borrowed `vec[_]` occurs here
LL |
LL | _b.use_ref();
- | -- borrow later used here
+ | ------------ borrow later used here
error[E0508]: cannot move out of type `[Box<isize>]`, a non-copy slice
--> $DIR/borrowck-vec-pattern-nesting.rs:34:11
--> $DIR/index-mut-help-with-impl.rs:9:5
|
LL | Index::index(&v, 1..2).make_ascii_uppercase();
- | ^^^^^^^^^^^^^^^^^^^^^^ cannot borrow as mutable
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot borrow as mutable
error: aborting due to previous error
--> $DIR/index-mut-help.rs:11:5
|
LL | map["peter"].clear();
- | ^^^^^^^^^^^^ cannot borrow as mutable
+ | ^^^^^^^^^^^^^^^^^^^^ cannot borrow as mutable
|
= help: trait `IndexMut` is required to modify indexed content, but it is not implemented for `HashMap<&str, String>`
--> $DIR/issue-42344.rs:4:5
|
LL | TAB[0].iter_mut();
- | ^^^^^^ cannot borrow as mutable
+ | ^^^^^^^^^^^^^^^^^ cannot borrow as mutable
error: aborting due to previous error
LL | Some(baz) => {
| --- first mutable borrow occurs here
LL | bar.take();
- | ^^^ second mutable borrow occurs here
+ | ^^^^^^^^^^ second mutable borrow occurs here
LL | drop(baz);
| --- first borrow later used here
--> $DIR/issue-81365-10.rs:21:9
|
LL | let first = &self.deref().target_field;
- | ---- borrow of `self.container_field` occurs here
+ | ------------ borrow of `self.container_field` occurs here
LL | self.container_field = true;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ assignment to borrowed `self.container_field` occurs here
LL | first;
--> $DIR/issue-81365-5.rs:28:9
|
LL | let first = self.get();
- | ---- borrow of `self.container_field` occurs here
+ | ---------- borrow of `self.container_field` occurs here
LL | self.container_field = true;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ assignment to borrowed `self.container_field` occurs here
LL | first;
| | help: use mutable method: `values_mut()`
| this iterator yields `&` references
LL | v.flush();
- | ^ `v` is a `&` reference, so the data it refers to cannot be borrowed as mutable
+ | ^^^^^^^^^ `v` is a `&` reference, so the data it refers to cannot be borrowed as mutable
error: aborting due to previous error
+++ /dev/null
-error[E0502]: cannot borrow `v` as mutable because it is also borrowed as immutable
- --> $DIR/issue-82462.rs:18:9
- |
-LL | for x in DroppingSlice(&*v).iter() {
- | ------------------
- | | |
- | | immutable borrow occurs here
- | a temporary with access to the immutable borrow is created here ...
-LL | v.push(*x);
- | ^ mutable borrow occurs here
-LL | break;
-LL | }
- | - ... and the immutable borrow might be used here, when that temporary is dropped and runs the `Drop` code for type `DroppingSlice`
- |
-help: consider adding semicolon after the expression so its temporaries are dropped sooner, before the local variables declared by the block are dropped
- |
-LL | };
- | +
-
-error: aborting due to previous error
-
-For more information about this error, try `rustc --explain E0502`.
| first mutable borrow occurs here
| a temporary with access to the first borrow is created here ...
LL | Some(_) => { heap.pop(); },
- | ^^^^ second mutable borrow occurs here
+ | ^^^^^^^^^^ second mutable borrow occurs here
...
LL | }
| - ... and the first borrow might be used here, when that temporary is dropped and runs the destructor for type `Option<PeekMut<'_, i32>>`
| ---- help: consider changing this to be a mutable reference: `&mut Vec<Vec<i32>>`
LL |
LL | rofl.push(Vec::new());
- | ^^^^ `rofl` is a `&` reference, so the data it refers to cannot be borrowed as mutable
+ | ^^^^^^^^^^^^^^^^^^^^^ `rofl` is a `&` reference, so the data it refers to cannot be borrowed as mutable
error[E0594]: cannot assign to `*r`, which is behind a `&` reference
--> $DIR/issue-85765.rs:12:5
--> $DIR/mut-borrow-of-mut-ref.rs:35:5
|
LL | f.bar();
- | ^ cannot borrow as mutable
+ | ^^^^^^^ cannot borrow as mutable
|
help: consider making the binding mutable
|
LL | let second = &mut void;
| ^^^^^^^^^ second mutable borrow occurs here
LL | first.use_mut();
- | ----- first borrow later used here
+ | --------------- first borrow later used here
error[E0499]: cannot borrow `inner_void` as mutable more than once at a time
--> $DIR/mut-borrow-outside-loop.rs:15:28
| ^^^^^^^^^^^^^^^ second mutable borrow occurs here
LL | inner_second.use_mut();
LL | inner_first.use_mut();
- | ----------- first borrow later used here
+ | --------------------- first borrow later used here
error: aborting due to 2 previous errors
--> $DIR/two-phase-across-loop.rs:17:22
|
LL | strings.push(foo.get_string());
- | ^^^ `foo` was mutably borrowed here in the previous iteration of the loop
+ | ^^^^^^^^^^^^^^^^ `foo` was mutably borrowed here in the previous iteration of the loop
error: aborting due to previous error
error[E0502]: cannot borrow `vec` as mutable because it is also borrowed as immutable
--> $DIR/two-phase-cannot-nest-mut-self-calls.rs:16:9
|
-LL | vec.get({
- | --- --- immutable borrow later used by call
- | |
- | immutable borrow occurs here
-LL |
-LL | vec.push(2);
- | ^^^ mutable borrow occurs here
+LL | vec.get({
+ | - --- immutable borrow later used by call
+ | _____|
+ | |
+LL | |
+LL | | vec.push(2);
+ | | ^^^^^^^^^^^ mutable borrow occurs here
+LL | |
+LL | |
+LL | | 0
+LL | | });
+ | |______- immutable borrow occurs here
error: aborting due to previous error
--> $DIR/two-phase-multi-mut.rs:11:16
|
LL | foo.method(&mut foo);
- | --- ------ ^^^^^^^^ second mutable borrow occurs here
- | | |
+ | -----------^^^^^^^^-
+ | | | |
+ | | | second mutable borrow occurs here
| | first borrow later used by call
| first mutable borrow occurs here
| -- immutable borrow occurs here
LL |
LL | v.push(shared.len());
- | ^ ------ immutable borrow later used here
- | |
+ | ^^^^^^^------------^
+ | | |
+ | | immutable borrow later used here
| mutable borrow occurs here
|
= note: `#[warn(mutable_borrow_reservation_conflict)]` on by default
| -- immutable borrow occurs here
LL |
LL | v.push(shared.len());
- | ^ ------ immutable borrow later used here
- | |
+ | ^^^^^^^------------^
+ | | |
+ | | immutable borrow later used here
| mutable borrow occurs here
|
= note: `#[warn(mutable_borrow_reservation_conflict)]` on by default
| -- immutable borrow occurs here
LL |
LL | v.extend(shared);
- | ^ ------ immutable borrow later used here
- | |
+ | ^^^^^^^^^------^
+ | | |
+ | | immutable borrow later used here
| mutable borrow occurs here
error[E0502]: cannot borrow `v` as mutable because it is also borrowed as immutable
| -- immutable borrow occurs here
LL |
LL | v.push(shared.len());
- | ^ ------ immutable borrow later used here
- | |
+ | ^^^^^^^------------^
+ | | |
+ | | immutable borrow later used here
| mutable borrow occurs here
error: aborting due to 3 previous errors
| -- immutable borrow occurs here
LL |
LL | v.extend(shared);
- | ^ ------ immutable borrow later used here
- | |
+ | ^^^^^^^^^------^
+ | | |
+ | | immutable borrow later used here
| mutable borrow occurs here
error[E0502]: cannot borrow `v` as mutable because it is also borrowed as immutable
| -- immutable borrow occurs here
LL |
LL | v.push(shared.len());
- | ^ ------ immutable borrow later used here
- | |
+ | ^^^^^^^------------^
+ | | |
+ | | immutable borrow later used here
| mutable borrow occurs here
error: aborting due to 3 previous errors
| -- immutable borrow occurs here
LL |
LL | v.push(shared.len());
- | ^ ------ immutable borrow later used here
- | |
+ | ^^^^^^^------------^
+ | | |
+ | | immutable borrow later used here
| mutable borrow occurs here
error[E0502]: cannot borrow `v` as mutable because it is also borrowed as immutable
| -- immutable borrow occurs here
LL |
LL | v.push(shared.len());
- | ^ ------ immutable borrow later used here
- | |
+ | ^^^^^^^------------^
+ | | |
+ | | immutable borrow later used here
| mutable borrow occurs here
error[E0502]: cannot borrow `v` as mutable because it is also borrowed as immutable
| -- immutable borrow occurs here
LL |
LL | v.push(shared.len());
- | ^ ------ immutable borrow later used here
- | |
+ | ^^^^^^^------------^
+ | | |
+ | | immutable borrow later used here
| mutable borrow occurs here
error: aborting due to 3 previous errors
| -- immutable borrow occurs here
LL |
LL | v.push(shared.len());
- | ^ ------ immutable borrow later used here
- | |
+ | ^^^^^^^------------^
+ | | |
+ | | immutable borrow later used here
| mutable borrow occurs here
|
note: the lint level is defined here
| -- immutable borrow occurs here
LL |
LL | v.push(shared.len());
- | ^ ------ immutable borrow later used here
- | |
+ | ^^^^^^^------------^
+ | | |
+ | | immutable borrow later used here
| mutable borrow occurs here
|
note: the lint level is defined here
| first mutable borrow occurs here
LL |
LL | v.push(format!("foo"));
- | ^ second mutable borrow occurs here
+ | ^^^^^^^^^^^^^^^^^^^^^^ second mutable borrow occurs here
error: aborting due to previous error
--> $DIR/two-phase-surprise-no-conflict.rs:57:17
|
LL | self.hash_expr(&self.cx_mut.body(eid).value);
- | ^^^^^---------^^-----------^^^^^^^^^^^^^^^^^
+ | ^^^^^---------^^---------------------^^^^^^^
| | | |
| | | immutable borrow occurs here
| | immutable borrow later used by call
--> $DIR/two-phase-surprise-no-conflict.rs:119:51
|
LL | reg.register_static(Box::new(TrivialPass::new(&mut reg.sess_mut)));
- | --- --------------- ^^^^^^^^^^^^^^^^^ second mutable borrow occurs here
- | | |
+ | ----------------------------------------------^^^^^^^^^^^^^^^^^---
+ | | | |
+ | | | second mutable borrow occurs here
| | first borrow later used by call
| first mutable borrow occurs here
--> $DIR/two-phase-surprise-no-conflict.rs:122:54
|
LL | reg.register_bound(Box::new(TrivialPass::new_mut(&mut reg.sess_mut)));
- | --- -------------- ^^^^^^^^^^^^^^^^^ second mutable borrow occurs here
- | | |
+ | -------------------------------------------------^^^^^^^^^^^^^^^^^---
+ | | | |
+ | | | second mutable borrow occurs here
| | first borrow later used by call
| first mutable borrow occurs here
--> $DIR/two-phase-surprise-no-conflict.rs:125:53
|
LL | reg.register_univ(Box::new(TrivialPass::new_mut(&mut reg.sess_mut)));
- | --- ------------- ^^^^^^^^^^^^^^^^^ second mutable borrow occurs here
- | | |
+ | ------------------------------------------------^^^^^^^^^^^^^^^^^---
+ | | | |
+ | | | second mutable borrow occurs here
| | first borrow later used by call
| first mutable borrow occurs here
--> $DIR/two-phase-surprise-no-conflict.rs:128:44
|
LL | reg.register_ref(&TrivialPass::new_mut(&mut reg.sess_mut));
- | --- ------------ ^^^^^^^^^^^^^^^^^ second mutable borrow occurs here
- | | |
+ | ---------------------------------------^^^^^^^^^^^^^^^^^--
+ | | | |
+ | | | second mutable borrow occurs here
| | first borrow later used by call
| first mutable borrow occurs here
--> $DIR/two-phase-surprise-no-conflict.rs:154:54
|
LL | reg.register_bound(Box::new(CapturePass::new_mut(&mut reg.sess_mut)));
- | --- -------------- ^^^^^^^^^^^^^^^^^ second mutable borrow occurs here
- | | |
+ | -------------------------------------------------^^^^^^^^^^^^^^^^^---
+ | | | |
+ | | | second mutable borrow occurs here
| | first borrow later used by call
| first mutable borrow occurs here
--> $DIR/two-phase-surprise-no-conflict.rs:158:53
|
LL | reg.register_univ(Box::new(CapturePass::new_mut(&mut reg.sess_mut)));
- | --- ------------- ^^^^^^^^^^^^^^^^^ second mutable borrow occurs here
- | | |
+ | ------------------------------------------------^^^^^^^^^^^^^^^^^---
+ | | | |
+ | | | second mutable borrow occurs here
| | first borrow later used by call
| first mutable borrow occurs here
--> $DIR/two-phase-surprise-no-conflict.rs:162:44
|
LL | reg.register_ref(&CapturePass::new_mut(&mut reg.sess_mut));
- | --- ------------ ^^^^^^^^^^^^^^^^^ second mutable borrow occurs here
- | | |
+ | ---------------------------------------^^^^^^^^^^^^^^^^^--
+ | | | |
+ | | | second mutable borrow occurs here
| | first borrow later used by call
| first mutable borrow occurs here
--> $DIR/leak-alloc.rs:26:10
|
LL | let boxed = Box::new_in(10, alloc.by_ref());
- | ----- borrow of `alloc` occurs here
+ | -------------- borrow of `alloc` occurs here
LL | let theref = Box::leak(boxed);
LL | drop(alloc);
| ^^^^^ move out of `alloc` occurs here
LL | let s = std::io::stdin();
| - help: consider changing this to be mutable: `mut s`
LL | to_fn_once(move|| { s.read_to_end(&mut Vec::new()); });
- | ^ cannot borrow as mutable
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot borrow as mutable
error: aborting due to 2 previous errors
--- /dev/null
+// edition:2021
+// run-pass
+#![feature(if_let_guard)]
+#[allow(unused_must_use)]
+#[allow(dead_code)]
+
+fn print_error_count(registry: &Registry) {
+ |x: &Registry| {
+ match &x {
+ Registry if let _ = registry.try_find_description() => { }
+ //~^ WARNING: irrefutable `if let` guard pattern
+ _ => {}
+ }
+ };
+}
+
+struct Registry;
+impl Registry {
+ pub fn try_find_description(&self) {
+ unimplemented!()
+ }
+}
+
+fn main() {}
--- /dev/null
+warning: irrefutable `if let` guard pattern
+ --> $DIR/issue-88118-2.rs:10:29
+ |
+LL | Registry if let _ = registry.try_find_description() => { }
+ | ^
+ |
+ = note: `#[warn(irrefutable_let_patterns)]` on by default
+ = note: this pattern will always match, so the guard is useless
+ = help: consider removing the guard and adding a `let` inside the match arm
+
+warning: 1 warning emitted
+
| ^^^^^^ second mutable borrow occurs here
LL | z.use_mut();
LL | y.use_mut();
- | - first borrow later used here
+ | ----------- first borrow later used here
error: aborting due to previous error
--> $DIR/one_line.rs:3:12
|
LL | v.push(v.pop().unwrap());
- | - ---- ^ second mutable borrow occurs here
- | | |
+ | -------^^^^^^^----------
+ | | | |
+ | | | second mutable borrow occurs here
| | first borrow later used by call
| first mutable borrow occurs here
--> $DIR/issue-67375.rs:7:17
|
LL | inner: [(); { [|_: &T| {}; 0].len() }],
- | ^^---------------^^^^^^^^
+ | ^^---------------------^^
| |
| unsupported operation in generic constant
|
error: unreachable pattern
--> $DIR/issue-78057.rs:14:9
|
+LL | FOO => {},
+ | --- matches any value
+LL |
LL | _ => {}
- | ^
+ | ^ unreachable pattern
|
note: the lint level is defined here
--> $DIR/issue-78057.rs:1:9
--> $DIR/const_let_assign3.rs:14:5
|
LL | s.foo(3);
- | ^
+ | ^^^^^^^^
|
= note: see issue #57349 <https://github.com/rust-lang/rust/issues/57349> for more information
= help: add `#![feature(const_mut_refs)]` to the crate attributes to enable
--- /dev/null
+// run-pass
+
+#![feature(try_trait_v2)]
+#![feature(const_trait_impl)]
+#![feature(const_try)]
+#![feature(const_convert)]
+
+fn main() {
+ const fn result() -> Result<bool, ()> {
+ Err(())?;
+ Ok(true)
+ }
+
+ const FOO: Result<bool, ()> = result();
+ assert_eq!(Err(()), FOO);
+
+ const fn option() -> Option<()> {
+ None?;
+ Some(())
+ }
+ const BAR: Option<()> = option();
+ assert_eq!(None, BAR);
+}
--> $DIR/issue-34126.rs:6:18
|
LL | self.run(&mut self);
- | ---- --- ^^^^^^^^^ mutable borrow occurs here
- | | |
+ | ---------^^^^^^^^^-
+ | | | |
+ | | | mutable borrow occurs here
| | immutable borrow later used by call
| immutable borrow occurs here
LL | let f = Foo { v: Vec::new() };
| - help: consider changing this to be mutable: `mut f`
LL | f.v.push("cat".to_string());
- | ^^^ cannot borrow as mutable
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot borrow as mutable
error[E0594]: cannot assign to `s.x`, as `s` is not declared as mutable
--> $DIR/issue-35937.rs:16:5
LL | fn f(&self) {
| ----- help: consider changing this to be a mutable reference: `&mut self`
LL | self.s.push('x');
- | ^^^^^^ `self` is a `&` reference, so the data it refers to cannot be borrowed as mutable
+ | ^^^^^^^^^^^^^^^^ `self` is a `&` reference, so the data it refers to cannot be borrowed as mutable
error: aborting due to previous error
| ---------- help: consider changing this to be mutable: `&'a mut String`
...
LL | self.s.push('x');
- | ^^^^^^ cannot borrow as mutable
+ | ^^^^^^^^^^^^^^^^ cannot borrow as mutable
error: aborting due to previous error
| ---------- help: consider changing this to be mutable: `&'a mut String`
...
LL | self.s.push('x');
- | ^^^^^^ cannot borrow as mutable
+ | ^^^^^^^^^^^^^^^^ cannot borrow as mutable
error: aborting due to previous error
LL | fn f(x: usize, f: &Foo) {
| ---- help: consider changing this to be a mutable reference: `&mut Foo<'_>`
LL | f.s.push('x');
- | ^^^ `f` is a `&` reference, so the data it refers to cannot be borrowed as mutable
+ | ^^^^^^^^^^^^^ `f` is a `&` reference, so the data it refers to cannot be borrowed as mutable
error: aborting due to previous error
LL | let mut buf = &[1, 2, 3, 4];
| ------------- help: consider changing this to be a mutable reference: `&mut [1, 2, 3, 4]`
LL | buf.iter_mut();
- | ^^^ `buf` is a `&` reference, so the data it refers to cannot be borrowed as mutable
+ | ^^^^^^^^^^^^^^ `buf` is a `&` reference, so the data it refers to cannot be borrowed as mutable
error: aborting due to previous error
LL | is_send::<A>();
| ^^^^^^^^^^^^
|
- = help: consider adding a `#![recursion_limit="20"]` attribute to your crate (`recursion_limit`)
+ = help: consider increasing the recursion limit by adding a `#![recursion_limit = "20"]` attribute to your crate (`recursion_limit`)
note: required because it appears within the type `J`
--> $DIR/recursion_limit.rs:24:9
|
LL | let x: &Bottom = &t;
| ^^ deref recursion limit reached
|
- = help: consider adding a `#![recursion_limit="20"]` attribute to your crate (`recursion_limit_deref`)
+ = help: consider increasing the recursion limit by adding a `#![recursion_limit = "20"]` attribute to your crate (`recursion_limit_deref`)
error[E0308]: mismatched types
--> $DIR/recursion_limit_deref.rs:50:22
LL | recurse!(0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9);
| -------------------------------------------------- in this macro invocation
|
- = help: consider adding a `#![recursion_limit="20"]` attribute to your crate (`recursion_limit_macro`)
+ = help: consider increasing the recursion limit by adding a `#![recursion_limit = "20"]` attribute to your crate (`recursion_limit_macro`)
= note: this error originates in the macro `recurse` (in Nightly builds, run with -Z macro-backtrace for more info)
error: aborting due to previous error
--> $DIR/drop-with-active-borrows-1.rs:4:10
|
LL | let b: Vec<&str> = a.lines().collect();
- | - borrow of `a` occurs here
+ | --------- borrow of `a` occurs here
LL | drop(a);
| ^ move out of `a` occurs here
LL | for s in &b {
--> $DIR/drop-with-active-borrows-2.rs:3:5
|
LL | raw_lines.iter().map(|l| l.trim()).collect()
- | ---------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ | ----------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^
| |
| returns a value referencing data owned by the current function
| `raw_lines` is borrowed here
| ^^^^^^^^
help: disambiguate the associated function for candidate #1
|
-LL | Trait1::foo()
- | ~~~~~~~~
+LL | <Test as Trait1>::foo()
+ | ~~~~~~~~~~~~~~~~~~
help: disambiguate the associated function for candidate #2
|
-LL | Trait2::foo()
- | ~~~~~~~~
+LL | <Test as Trait2>::foo()
+ | ~~~~~~~~~~~~~~~~~~
error: aborting due to previous error
LL | ref_foo.foo();
| ^^^ deref recursion limit reached
|
- = help: consider adding a `#![recursion_limit="8"]` attribute to your crate (`E0055`)
+ = help: consider increasing the recursion limit by adding a `#![recursion_limit = "8"]` attribute to your crate (`E0055`)
error: aborting due to previous error
--> $DIR/E0161.rs:29:5
|
LL | x.f();
- | ^
+ | ^^^^^
error: aborting due to previous error
--> $DIR/E0161.rs:29:5
|
LL | x.f();
- | ^
+ | ^^^^^
error: aborting due to previous error
--> $DIR/E0161.rs:29:5
|
LL | x.f();
- | ^
+ | ^^^^^
error: aborting due to previous error
--> $DIR/E0161.rs:29:5
|
LL | x.f();
- | ^
+ | ^^^^^
error: aborting due to previous error
LL | impl<T> Foo for T where Bar<T>: Foo {}
| ^^^
|
- = help: consider adding a `#![recursion_limit="256"]` attribute to your crate (`E0275`)
+ = help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`E0275`)
note: required because of the requirements on the impl of `Foo` for `Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<T>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>`
--> $DIR/E0275.rs:5:9
|
--> $DIR/E0407.rs:9:5
|
LL | fn b() {}
- | ^^^^^^^^^ not a member of trait `Foo`
+ | ^^^-^^^^^
+ | | |
+ | | help: there is an associated function with a similar name: `a`
+ | not a member of trait `Foo`
error: aborting due to previous error
| ^^^^^^ second mutable borrow occurs here
LL | a.use_mut();
LL | x.use_mut();
- | - first borrow later used here
+ | ----------- first borrow later used here
error: aborting due to previous error
LL | bar(a);
| ^ mutable borrow occurs here
LL | y.use_ref();
- | - immutable borrow later used here
+ | ----------- immutable borrow later used here
error: aborting due to previous error
LL | bar(a);
| ^^^^^^ mutable borrow occurs here
LL | y.use_ref();
- | - immutable borrow later used here
+ | ----------- immutable borrow later used here
error: aborting due to previous error
LL | let _sum = value + 1;
| ^^^^^ use of borrowed `value`
LL | _borrow.use_mut();
- | ------- borrow later used here
+ | ----------------- borrow later used here
error: aborting due to previous error
LL | eat(x);
| ^ move out of `x` occurs here
LL | _ref_to_val.use_ref();
- | ----------- borrow later used here
+ | --------------------- borrow later used here
error: aborting due to previous error
--> $DIR/E0507.rs:12:5
|
LL | x.borrow().nothing_is_true();
- | ^^^^^^^^^^ move occurs because value has type `TheDarkKnight`, which does not implement the `Copy` trait
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ move occurs because value has type `TheDarkKnight`, which does not implement the `Copy` trait
error: aborting due to previous error
| ------ mutable borrow occurs here
...
LL | r = y.as_ref().unwrap();
- | ^ immutable borrow occurs here
+ | ^^^^^^^^^^ immutable borrow occurs here
LL |
LL | }
| - mutable borrow might be used here, when `g` is dropped and runs the destructor for generator
--> $DIR/dropck.rs:10:40
|
LL | let ref_ = Box::leak(Box::new(Some(cell.borrow_mut())));
- | ^^^^ borrowed value does not live long enough
+ | ^^^^^^^^^^^^^^^^^ borrowed value does not live long enough
...
LL | }
| -
+++ /dev/null
-error[E0502]: cannot borrow `my_stuff` as mutable because it is also borrowed as immutable
- --> $DIR/hashmap-iter-value-lifetime.rs:7:5
- |
-LL | let (_, thing) = my_stuff.iter().next().unwrap();
- | -------- immutable borrow occurs here
-LL |
-LL | my_stuff.clear();
- | ^^^^^^^^ mutable borrow occurs here
-LL |
-LL | println!("{}", *thing);
- | ------ immutable borrow later used here
-
-error: aborting due to previous error
-
-For more information about this error, try `rustc --explain E0502`.
--> $DIR/hashmap-iter-value-lifetime.rs:7:5
|
LL | let (_, thing) = my_stuff.iter().next().unwrap();
- | -------- immutable borrow occurs here
+ | --------------- immutable borrow occurs here
LL |
LL | my_stuff.clear();
| ^^^^^^^^^^^^^^^^ mutable borrow occurs here
+++ /dev/null
-error[E0502]: cannot borrow `my_stuff` as mutable because it is also borrowed as immutable
- --> $DIR/hashmap-lifetimes.rs:6:5
- |
-LL | let mut it = my_stuff.iter();
- | -------- immutable borrow occurs here
-LL | my_stuff.insert(1, 43);
- | ^^^^^^^^ mutable borrow occurs here
-LL | it;
- | -- immutable borrow later used here
-
-error: aborting due to previous error
-
-For more information about this error, try `rustc --explain E0502`.
--> $DIR/hashmap-lifetimes.rs:6:5
|
LL | let mut it = my_stuff.iter();
- | -------- immutable borrow occurs here
+ | --------------- immutable borrow occurs here
LL | my_stuff.insert(1, 43);
| ^^^^^^^^^^^^^^^^^^^^^^ mutable borrow occurs here
LL | it;
LL | | for<'a> fn(&'a u32, &'a u32) -> &'a u32) }
| |_____________________________________________- in this macro invocation
|
- = note: expected enum `Option<for<'r, 's> fn(&'r u32, &'s u32) -> &'r u32>`
- found enum `Option<for<'r> fn(&'r u32, &'r u32) -> &'r u32>`
+ = note: expected enum `Option<for<'a, 'b> fn(&'a u32, &'b u32) -> &'a u32>`
+ found enum `Option<for<'a> fn(&'a u32, &'a u32) -> &'a u32>`
= note: this error originates in the macro `check` (in Nightly builds, run with -Z macro-backtrace for more info)
error: aborting due to previous error
LL | | fn(&'x u32)) }
| |______________- in this macro invocation
|
- = note: expected enum `Option<for<'r> fn(&'r u32)>`
+ = note: expected enum `Option<for<'a> fn(&'a u32)>`
found enum `Option<fn(&u32)>`
= note: this error originates in the macro `check` (in Nightly builds, run with -Z macro-backtrace for more info)
LL | | for<'a> fn(Inv<'a>, Inv<'a>)) }
| |__________________________________- in this macro invocation
|
- = note: expected enum `Option<for<'r, 's> fn(Inv<'r>, Inv<'s>)>`
- found enum `Option<for<'r> fn(Inv<'r>, Inv<'r>)>`
+ = note: expected enum `Option<for<'a, 'b> fn(Inv<'a>, Inv<'b>)>`
+ found enum `Option<for<'a> fn(Inv<'a>, Inv<'a>)>`
= note: this error originates in the macro `check` (in Nightly builds, run with -Z macro-backtrace for more info)
error[E0308]: mismatched types
LL | | for<'a> fn(Inv<'a>, Inv<'a>)) }
| |__________________________________- in this macro invocation
|
- = note: expected enum `Option<for<'r, 's> fn(Inv<'r>, Inv<'s>)>`
- found enum `Option<for<'r> fn(Inv<'r>, Inv<'r>)>`
+ = note: expected enum `Option<for<'a, 'b> fn(Inv<'a>, Inv<'b>)>`
+ found enum `Option<for<'a> fn(Inv<'a>, Inv<'a>)>`
= note: this error originates in the macro `check` (in Nightly builds, run with -Z macro-backtrace for more info)
error: aborting due to 2 previous errors
--> $DIR/hrtb-debruijn-in-receiver.rs:17:5
|
LL | foo.insert();
- | --- first mutable borrow occurs here
+ | ------------ first mutable borrow occurs here
LL | foo.insert();
- | ^^^
+ | ^^^^^^^^^^^^
| |
| second mutable borrow occurs here
| first borrow later used here
--> $DIR/assoc_item_ctxt.rs:35:13
|
LL | fn method() {}
- | ^^^^^^^^^^^^^^ not a member of trait `Tr`
+ | ^^^------^^^^^
+ | | |
+ | | help: there is an associated function with a similar name: `method`
+ | not a member of trait `Tr`
...
LL | mac_trait_impl!();
| ------------------ in this macro invocation
| ^^^^^^^^^^^^^^^ second mutable borrow occurs here
...
LL | borrow1.use_mut();
- | ------- first borrow later used here
+ | ----------------- first borrow later used here
error: aborting due to previous error
LL | f();
| ^ not found in this scope
|
-help: consider importing one of these items
+help: consider importing this function
|
LL | use foo::f;
|
LL | n!(f);
| ^ not found in this scope
|
- = note: consider importing one of these items:
+ = note: consider importing this function:
foo::f
= note: this error originates in the macro `n` (in Nightly builds, run with -Z macro-backtrace for more info)
LL | f
| ^ not found in this scope
|
- = note: consider importing one of these items:
+ = note: consider importing this function:
foo::f
= note: this error originates in the macro `n` (in Nightly builds, run with -Z macro-backtrace for more info)
LL | fpriv();
| ^^^^^ not found in this scope
|
-help: consider importing this function
- |
-LL | use bar::fpriv;
+note: function `bar::fpriv` exists but is inaccessible
+ --> $DIR/glob-resolve1.rs:7:5
|
+LL | fn fpriv() {}
+ | ^^^^^^^^^^ not accessible
error[E0425]: cannot find function `epriv` in this scope
--> $DIR/glob-resolve1.rs:27:5
LL | epriv();
| ^^^^^ not found in this scope
|
-help: consider importing this function
- |
-LL | use bar::epriv;
+note: function `bar::epriv` exists but is inaccessible
+ --> $DIR/glob-resolve1.rs:9:9
|
+LL | fn epriv();
+ | ^^^^^^^^^^^ not accessible
error[E0423]: expected value, found enum `B`
--> $DIR/glob-resolve1.rs:28:5
LL | C;
| ^ not found in this scope
|
-help: consider importing this unit struct
- |
-LL | use bar::C;
+note: unit struct `bar::C` exists but is inaccessible
+ --> $DIR/glob-resolve1.rs:18:5
|
+LL | struct C;
+ | ^^^^^^^^^ not accessible
error[E0425]: cannot find function `import` in this scope
--> $DIR/glob-resolve1.rs:30:5
| ---------- similarly named enum `B` defined here
...
LL | foo::<A>();
- | ^
- |
-help: an enum with a similar name exists
+ | ^ help: an enum with a similar name exists: `B`
|
-LL | foo::<B>();
- | ~
-help: consider importing this enum
- |
-LL | use bar::A;
+note: enum `bar::A` exists but is inaccessible
+ --> $DIR/glob-resolve1.rs:11:5
|
+LL | enum A {
+ | ^^^^^^ not accessible
error[E0412]: cannot find type `C` in this scope
--> $DIR/glob-resolve1.rs:33:11
| ---------- similarly named enum `B` defined here
...
LL | foo::<C>();
- | ^
- |
-help: an enum with a similar name exists
- |
-LL | foo::<B>();
- | ~
-help: consider importing this struct
+ | ^ help: an enum with a similar name exists: `B`
|
-LL | use bar::C;
+note: struct `bar::C` exists but is inaccessible
+ --> $DIR/glob-resolve1.rs:18:5
|
+LL | struct C;
+ | ^^^^^^^^^ not accessible
error[E0412]: cannot find type `D` in this scope
--> $DIR/glob-resolve1.rs:34:11
| ---------- similarly named enum `B` defined here
...
LL | foo::<D>();
- | ^
- |
-help: an enum with a similar name exists
- |
-LL | foo::<B>();
- | ~
-help: consider importing this type alias
+ | ^ help: an enum with a similar name exists: `B`
|
-LL | use bar::D;
+note: type alias `bar::D` exists but is inaccessible
+ --> $DIR/glob-resolve1.rs:20:5
|
+LL | type D = isize;
+ | ^^^^^^^^^^^^^^^ not accessible
error: aborting due to 8 previous errors
LL | fn sub() -> Bar { 1 }
| ^^^ not found in this scope
|
-help: consider importing this type alias
- |
-LL | use a::b::Bar;
+note: type alias `a::b::Bar` exists but is inaccessible
+ --> $DIR/issue-4366-2.rs:11:9
|
+LL | type Bar = isize;
+ | ^^^^^^^^^^^^^^^^^ not accessible
error[E0423]: expected function, found module `foo`
--> $DIR/issue-4366-2.rs:25:5
LL | Foo.foo;
| ^^^^^^^ deref recursion limit reached
|
- = help: consider adding a `#![recursion_limit="256"]` attribute to your crate (`infinite_autoderef`)
+ = help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`infinite_autoderef`)
error[E0055]: reached the recursion limit while auto-dereferencing `Foo`
--> $DIR/infinite-autoderef.rs:25:9
LL | Foo.foo;
| ^^^ deref recursion limit reached
|
- = help: consider adding a `#![recursion_limit="256"]` attribute to your crate (`infinite_autoderef`)
+ = help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`infinite_autoderef`)
error[E0609]: no field `foo` on type `Foo`
--> $DIR/infinite-autoderef.rs:25:9
LL | Foo.bar();
| ^^^ deref recursion limit reached
|
- = help: consider adding a `#![recursion_limit="256"]` attribute to your crate (`infinite_autoderef`)
+ = help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`infinite_autoderef`)
error[E0599]: no method named `bar` found for struct `Foo` in the current scope
--> $DIR/infinite-autoderef.rs:26:9
LL | recursive!()
| ------------ in this macro invocation
|
- = help: consider adding a `#![recursion_limit="256"]` attribute to your crate (`infinite_macro_expansion`)
+ = help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`infinite_macro_expansion`)
= note: this error originates in the macro `recursive` (in Nightly builds, run with -Z macro-backtrace for more info)
error: aborting due to previous error
error[E0515]: cannot return value referencing local variable `rawLines`
--> $DIR/issue-13497-2.rs:3:5
|
-LL | rawLines
- | ^-------
- | |
- | _____`rawLines` is borrowed here
- | |
-LL | | .iter().map(|l| l.trim()).collect()
- | |___________________________________________^ returns a value referencing data owned by the current function
+LL | rawLines
+ | _____^
+ | |_____|
+ | ||
+LL | || .iter().map(|l| l.trim()).collect()
+ | ||_______________-___________________________^ returns a value referencing data owned by the current function
+ | |________________|
+ | `rawLines` is borrowed here
error: aborting due to previous error
LL | println!("Problem 1: {}", prob1!(1000));
| ------------ in this macro invocation
|
- = help: consider adding a `#![recursion_limit="256"]` attribute to your crate (`issue_16098`)
+ = help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`issue_16098`)
= note: this error originates in the macro `prob1` (in Nightly builds, run with -Z macro-backtrace for more info)
error: aborting due to previous error
LL | 0.contains(bits);
| ^^^^^^^^
|
- = help: consider adding a `#![recursion_limit="256"]` attribute to your crate (`issue_18400`)
+ = help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`issue_18400`)
note: required because of the requirements on the impl of `Set<&[_]>` for `{integer}`
--> $DIR/issue-18400.rs:6:16
|
error[E0596]: cannot borrow data in a `&` reference as mutable
- --> $DIR/issue-19163.rs:9:14
+ --> $DIR/issue-19163.rs:9:5
|
LL | mywrite!(&v, "Hello world");
- | ^^ cannot borrow as mutable
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot borrow as mutable
+ |
+ = note: this error originates in the macro `mywrite` (in Nightly builds, run with -Z macro-backtrace for more info)
error: aborting due to previous error
LL | impl<T> Foo for T where NoData<T>: Foo {
| ^^^
|
- = help: consider adding a `#![recursion_limit="256"]` attribute to your crate (`issue_20413`)
+ = help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`issue_20413`)
note: required because of the requirements on the impl of `Foo` for `NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<T>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>`
--> $DIR/issue-20413.rs:8:9
|
LL | impl<T> Foo for T where NoData<T>: Foo {
| ^^^
|
- = help: consider adding a `#![recursion_limit="256"]` attribute to your crate (`issue_20413`)
+ = help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`issue_20413`)
note: required because of the requirements on the impl of `Foo` for `NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<T>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>`
--> $DIR/issue-20413.rs:8:9
|
LL | impl<T> Bar for T where EvenLessData<T>: Baz {
| ^^^
|
- = help: consider adding a `#![recursion_limit="256"]` attribute to your crate (`issue_20413`)
+ = help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`issue_20413`)
note: required because of the requirements on the impl of `Bar` for `AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<T>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>`
--> $DIR/issue-20413.rs:28:9
|
LL | impl<T> Bar for T where EvenLessData<T>: Baz {
| ^^^
|
- = help: consider adding a `#![recursion_limit="256"]` attribute to your crate (`issue_20413`)
+ = help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`issue_20413`)
note: required because of the requirements on the impl of `Bar` for `AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<T>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>`
--> $DIR/issue-20413.rs:28:9
|
LL | impl<T> Baz for T where AlmostNoData<T>: Bar {
| ^^^
|
- = help: consider adding a `#![recursion_limit="256"]` attribute to your crate (`issue_20413`)
+ = help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`issue_20413`)
note: required because of the requirements on the impl of `Baz` for `EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<T>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>`
--> $DIR/issue-20413.rs:36:9
|
LL | impl<T> Baz for T where AlmostNoData<T>: Bar {
| ^^^
|
- = help: consider adding a `#![recursion_limit="256"]` attribute to your crate (`issue_20413`)
+ = help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`issue_20413`)
note: required because of the requirements on the impl of `Baz` for `EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<T>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>`
--> $DIR/issue-20413.rs:36:9
|
| - change this to accept `FnMut` instead of `Fn`
...
LL | call_it(|| x.gen_mut());
- | ------- ^ cannot borrow as mutable
+ | ------- ^^^^^^^^^^^ cannot borrow as mutable
| |
| expects `Fn` instead of `FnMut`
LL | type Next = <GetNext<T::Next> as Next>::Next;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
- = help: consider adding a `#![recursion_limit="256"]` attribute to your crate (`issue_23122_2`)
+ = help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`issue_23122_2`)
note: required because of the requirements on the impl of `Next` for `GetNext<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<T as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next>`
--> $DIR/issue-23122-2.rs:8:15
|
--> $DIR/issue-41726.rs:5:9
|
LL | things[src.as_str()].sort();
- | ^^^^^^^^^^^^^^^^^^^^ cannot borrow as mutable
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot borrow as mutable
|
= help: trait `IndexMut` is required to modify indexed content, but it is not implemented for `HashMap<String, Vec<String>>`
LL | let _a = &collection;
| ----------- immutable borrow occurs here
LL | collection.swap(1, 2);
- | ^^^^^^^^^^ mutable borrow occurs here
+ | ^^^^^^^^^^^^^^^^^^^^^ mutable borrow occurs here
LL | _a.use_ref();
- | -- immutable borrow later used here
+ | ------------ immutable borrow later used here
error: aborting due to previous error
--> $DIR/issue-44405.rs:21:5
|
LL | container[&mut val].test();
- | ^^^^^^^^^^^^^^^^^^^ cannot borrow as mutable
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot borrow as mutable
|
= help: trait `IndexMut` is required to modify indexed content, but it is not implemented for `Container`
--> $DIR/issue-47646.rs:9:30
|
LL | let borrow = heap.peek_mut();
- | ---- mutable borrow occurs here
+ | --------------- mutable borrow occurs here
LL |
LL | match (borrow, ()) {
| ------------ a temporary with access to the mutable borrow is created here ...
--> $DIR/issue-52126-assign-op-invariance.rs:34:28
|
LL | let v: Vec<&str> = line.split_whitespace().collect();
- | ^^^^ borrowed value does not live long enough
+ | ^^^^^^^^^^^^^^^^^^^^^^^ borrowed value does not live long enough
...
LL | acc += cnt2;
| --- borrow later used here
| help: consider borrowing to avoid moving into the for loop: `&bad_letters`
...
LL | bad_letters.push('s');
- | ^^^^^^^^^^^ value borrowed here after move
+ | ^^^^^^^^^^^^^^^^^^^^^ value borrowed here after move
|
note: this function takes ownership of the receiver `self`, which moves `bad_letters`
--> $SRC_DIR/core/src/iter/traits/collect.rs:LL:COL
--> $DIR/issue-81584.rs:5:22
|
LL | .map(|y| y.iter().map(|x| x + 1))
- | -^^^^^^^^^^^^^^^^^^^^^^
+ | --------^^^^^^^^^^^^^^^
| |
| returns a value referencing data owned by the current function
| `y` is borrowed here
-// Checks whether declaring a lang item with the wrong number
-// of generic arguments crashes the compiler (issue #83893, #87573, and part of #9307).
+// Checks that declaring a lang item with the wrong number
+// of generic arguments errors rather than crashing (issue #83893, #87573, part of #9307, #79559).
#![feature(lang_items, no_core)]
#![no_core]
-#![crate_type = "lib"]
#[lang = "sized"]
trait MySized {}
//~^ ERROR parameter `T` is never used
//~| ERROR parameter `U` is never used
+// When the `start` lang item is missing generics very odd things can happen, especially when
+// it comes to cross-crate monomorphization
+#[lang = "start"]
+//~^ ERROR `start` language item must be applied to a function with 1 generic argument [E0718]
+fn start(_: *const u8, _: isize, _: *const *const u8) -> isize {
+ 0
+}
+
fn ice() {
// Use add
let r = 5;
// Use phantomdata
let _ = MyPhantomData::<(), i32>;
}
+
+// use `start`
+fn main() {}
error[E0718]: `add` language item must be applied to a trait with 1 generic argument
- --> $DIR/lang-item-generic-requirements.rs:11:1
+ --> $DIR/lang-item-generic-requirements.rs:10:1
|
LL | #[lang = "add"]
| ^^^^^^^^^^^^^^^
| ------- this trait has 2 generic arguments
error[E0718]: `drop_in_place` language item must be applied to a function with at least 1 generic argument
- --> $DIR/lang-item-generic-requirements.rs:15:1
+ --> $DIR/lang-item-generic-requirements.rs:14:1
|
LL | #[lang = "drop_in_place"]
| ^^^^^^^^^^^^^^^^^^^^^^^^^
| - this function has 0 generic arguments
error[E0718]: `index` language item must be applied to a trait with 1 generic argument
- --> $DIR/lang-item-generic-requirements.rs:19:1
+ --> $DIR/lang-item-generic-requirements.rs:18:1
|
LL | #[lang = "index"]
| ^^^^^^^^^^^^^^^^^
| ------- this trait has 2 generic arguments
error[E0718]: `phantom_data` language item must be applied to a struct with 1 generic argument
- --> $DIR/lang-item-generic-requirements.rs:23:1
+ --> $DIR/lang-item-generic-requirements.rs:22:1
|
LL | #[lang = "phantom_data"]
| ^^^^^^^^^^^^^^^^^^^^^^^^
LL | struct MyPhantomData<T, U>;
| ------ this struct has 2 generic arguments
+error[E0718]: `start` language item must be applied to a function with 1 generic argument
+ --> $DIR/lang-item-generic-requirements.rs:30:1
+ |
+LL | #[lang = "start"]
+ | ^^^^^^^^^^^^^^^^^
+LL |
+LL | fn start(_: *const u8, _: isize, _: *const *const u8) -> isize {
+ | - this function has 0 generic arguments
+
error[E0392]: parameter `T` is never used
- --> $DIR/lang-item-generic-requirements.rs:25:22
+ --> $DIR/lang-item-generic-requirements.rs:24:22
|
LL | struct MyPhantomData<T, U>;
| ^ unused parameter
= help: if you intended `T` to be a const parameter, use `const T: usize` instead
error[E0392]: parameter `U` is never used
- --> $DIR/lang-item-generic-requirements.rs:25:25
+ --> $DIR/lang-item-generic-requirements.rs:24:25
|
LL | struct MyPhantomData<T, U>;
| ^ unused parameter
= help: consider removing `U` or referring to it in a field
= help: if you intended `U` to be a const parameter, use `const U: usize` instead
-error: aborting due to 6 previous errors
+error: aborting due to 7 previous errors
Some errors have detailed explanations: E0392, E0718.
For more information about an error, try `rustc --explain E0392`.
| creates a temporary which is freed while still in use
LL |
LL | x.use_mut();
- | - borrow later used here
+ | ----------- borrow later used here
|
= note: consider using a `let` binding to create a longer lived value
= note: this error originates in the macro `vec` (in Nightly builds, run with -Z macro-backtrace for more info)
-error[E0596]: cannot borrow `y` as mutable, as it is not declared as mutable
- --> $DIR/ex3-both-anon-regions-using-fn-items.rs:2:3
- |
-LL | fn foo(x:fn(&u8, &u8), y: Vec<&u8>, z: &u8) {
- | - help: consider changing this to be mutable: `mut y`
-LL | y.push(z);
- | ^ cannot borrow as mutable
-
error: lifetime may not live long enough
--> $DIR/ex3-both-anon-regions-using-fn-items.rs:2:3
|
LL | y.push(z);
| ^^^^^^^^^ argument requires that `'1` must outlive `'2`
+error[E0596]: cannot borrow `y` as mutable, as it is not declared as mutable
+ --> $DIR/ex3-both-anon-regions-using-fn-items.rs:2:3
+ |
+LL | fn foo(x:fn(&u8, &u8), y: Vec<&u8>, z: &u8) {
+ | - help: consider changing this to be mutable: `mut y`
+LL | y.push(z);
+ | ^^^^^^^^^ cannot borrow as mutable
+
error: aborting due to 2 previous errors
For more information about this error, try `rustc --explain E0596`.
-error[E0596]: cannot borrow `y` as mutable, as it is not declared as mutable
- --> $DIR/ex3-both-anon-regions-using-trait-objects.rs:2:3
- |
-LL | fn foo(x:Box<dyn Fn(&u8, &u8)> , y: Vec<&u8>, z: &u8) {
- | - help: consider changing this to be mutable: `mut y`
-LL | y.push(z);
- | ^ cannot borrow as mutable
-
error: lifetime may not live long enough
--> $DIR/ex3-both-anon-regions-using-trait-objects.rs:2:3
|
LL | y.push(z);
| ^^^^^^^^^ argument requires that `'1` must outlive `'2`
+error[E0596]: cannot borrow `y` as mutable, as it is not declared as mutable
+ --> $DIR/ex3-both-anon-regions-using-trait-objects.rs:2:3
+ |
+LL | fn foo(x:Box<dyn Fn(&u8, &u8)> , y: Vec<&u8>, z: &u8) {
+ | - help: consider changing this to be mutable: `mut y`
+LL | y.push(z);
+ | ^^^^^^^^^ cannot borrow as mutable
+
error: aborting due to 2 previous errors
For more information about this error, try `rustc --explain E0596`.
--- /dev/null
+// edition:2018
+#![deny(must_not_suspend)]
+
+async fn other() {}
+
+pub async fn uhoh(m: std::sync::Mutex<()>) {
+ let _guard = m.lock().unwrap(); //~ ERROR `MutexGuard` held across
+ other().await;
+}
+
+fn main() {
+}
--- /dev/null
+error: `MutexGuard` held across a suspend point, but should not be
+ --> $DIR/mutex.rs:7:9
+ |
+LL | let _guard = m.lock().unwrap();
+ | ^^^^^^
+LL | other().await;
+ | ------------- the value is held across this suspend point
+ |
+note: the lint level is defined here
+ --> $DIR/mutex.rs:2:9
+ |
+LL | #![deny(must_not_suspend)]
+ | ^^^^^^^^^^^^^^^^
+note: Holding a MutexGuard across suspend points can cause deadlocks, delays, and cause Futures to not implement `Send`
+ --> $DIR/mutex.rs:7:9
+ |
+LL | let _guard = m.lock().unwrap();
+ | ^^^^^^
+help: consider using a block (`{ ... }`) to shrink the value's scope, ending before the suspend point
+ --> $DIR/mutex.rs:7:9
+ |
+LL | let _guard = m.lock().unwrap();
+ | ^^^^^^
+
+error: aborting due to previous error
+
--> $DIR/unaligned_references.rs:32:17
|
LL | let _ = good.data.clone();
- | ^^^^^^^^^
+ | ^^^^^^^^^^^^^^^^^
|
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
= note: for more information, see issue #82523 <https://github.com/rust-lang/rust/issues/82523>
LL | _ => y,
| ^ one type is more general than the other
|
- = note: expected fn pointer `for<'r, 's> fn(&'r u8, &'s u8) -> &'r u8`
- found fn pointer `for<'r> fn(&'r u8, &'r u8) -> &'r u8`
+ = note: expected fn pointer `for<'a, 'b> fn(&'a u8, &'b u8) -> &'a u8`
+ found fn pointer `for<'a> fn(&'a u8, &'a u8) -> &'a u8`
error: aborting due to previous error
LL | _ => y,
| ^ one type is more general than the other
|
- = note: expected trait object `dyn for<'r, 's> Foo<&'r u8, &'s u8>`
- found trait object `dyn for<'r> Foo<&'r u8, &'r u8>`
+ = note: expected trait object `dyn for<'a, 'b> Foo<&'a u8, &'b u8>`
+ found trait object `dyn for<'a> Foo<&'a u8, &'a u8>`
error[E0308]: mismatched types
--> $DIR/old-lub-glb-object.rs:10:14
LL | _ => y,
| ^ one type is more general than the other
|
- = note: expected trait object `dyn for<'r, 's> Foo<&'r u8, &'s u8>`
- found trait object `dyn for<'r> Foo<&'r u8, &'r u8>`
+ = note: expected trait object `dyn for<'a, 'b> Foo<&'a u8, &'b u8>`
+ found trait object `dyn for<'a> Foo<&'a u8, &'a u8>`
error: aborting due to 2 previous errors
(A) => (concat!("", a!()));
(A, $($A:ident),*) => (concat!("", a!($($A),*)))
//~^ ERROR recursion limit reached
- //~| HELP consider adding
+ //~| HELP consider increasing the recursion limit
}
fn main() {
LL | a!(A, A, A, A, A, A, A, A, A, A, A);
| ------------------------------------ in this macro invocation
|
- = help: consider adding a `#![recursion_limit="30"]` attribute to your crate (`issue_84632_eager_expansion_recursion_limit`)
+ = help: consider increasing the recursion limit by adding a `#![recursion_limit = "30"]` attribute to your crate (`issue_84632_eager_expansion_recursion_limit`)
= note: this error originates in the macro `a` (in Nightly builds, run with -Z macro-backtrace for more info)
error: aborting due to previous error
LL | my_recursive_macro!();
| ---------------------- in this macro invocation
|
- = help: consider adding a `#![recursion_limit="8"]` attribute to your crate (`trace_faulty_macros`)
+ = help: consider increasing the recursion limit by adding a `#![recursion_limit = "8"]` attribute to your crate (`trace_faulty_macros`)
= note: this error originates in the macro `my_recursive_macro` (in Nightly builds, run with -Z macro-backtrace for more info)
note: trace_macro
| ---- borrow later stored here
LL | Some(arg) => {
LL | match arg.to_str() {
- | ^^^ borrowed value does not live long enough
+ | ^^^^^^^^^^^^ borrowed value does not live long enough
...
LL | }
| - `arg` dropped here while still borrowed
| ^^^^^^^^
help: disambiguate the associated function for candidate #1
|
-LL | A::foo();
- | ~~~
+LL | <AB as A>::foo();
+ | ~~~~~~~~~~~
help: disambiguate the associated function for candidate #2
|
-LL | B::foo();
- | ~~~
+LL | <AB as B>::foo();
+ | ~~~~~~~~~~~
error: aborting due to previous error
LL | Foo::bar(&x);
| ^^ immutable borrow occurs here
LL | y.use_mut();
- | - mutable borrow later used here
+ | ----------- mutable borrow later used here
error[E0499]: cannot borrow `x` as mutable more than once at a time
--> $DIR/method-self-arg-2.rs:20:14
LL | Foo::baz(&mut x);
| ^^^^^^ second mutable borrow occurs here
LL | y.use_mut();
- | - first borrow later used here
+ | ----------- first borrow later used here
error: aborting due to 2 previous errors
--- /dev/null
+// Regression test for issue #72649
+// Tests that we don't emit spurious
+// 'value moved in previous iteration of loop' message
+
+struct NonCopy;
+
+fn good() {
+ loop {
+ let value = NonCopy{};
+ let _used = value;
+ }
+}
+
+fn moved_here_1() {
+ loop {
+ let value = NonCopy{};
+ //~^ NOTE move occurs because `value` has type `NonCopy`, which does not implement the `Copy` trait
+ let _used = value;
+ //~^ NOTE value moved here
+ let _used2 = value; //~ ERROR use of moved value: `value`
+ //~^ NOTE value used here after move
+ }
+}
+
+fn moved_here_2() {
+ let value = NonCopy{};
+ //~^ NOTE move occurs because `value` has type `NonCopy`, which does not implement the `Copy` trait
+ loop {
+ let _used = value;
+ //~^ NOTE value moved here
+ loop {
+ let _used2 = value; //~ ERROR use of moved value: `value`
+ //~^ NOTE value used here after move
+ }
+ }
+}
+
+fn moved_loop_1() {
+ let value = NonCopy{};
+ //~^ NOTE move occurs because `value` has type `NonCopy`, which does not implement the `Copy` trait
+ loop {
+ let _used = value; //~ ERROR use of moved value: `value`
+ //~^ NOTE value moved here, in previous iteration of loop
+ }
+}
+
+fn moved_loop_2() {
+ let mut value = NonCopy{};
+ //~^ NOTE move occurs because `value` has type `NonCopy`, which does not implement the `Copy` trait
+ let _used = value;
+ value = NonCopy{};
+ loop {
+ let _used2 = value; //~ ERROR use of moved value: `value`
+ //~^ NOTE value moved here, in previous iteration of loop
+ }
+}
+
+fn uninit_1() {
+ loop {
+ let value: NonCopy;
+ let _used = value; //~ ERROR use of possibly-uninitialized variable: `value`
+ //~^ NOTE use of possibly-uninitialized `value`
+ }
+}
+
+fn uninit_2() {
+ let mut value: NonCopy;
+ loop {
+ let _used = value; //~ ERROR use of possibly-uninitialized variable: `value`
+ //~^ NOTE use of possibly-uninitialized `value`
+ }
+}
+
+fn main() {}
--- /dev/null
+error[E0382]: use of moved value: `value`
+ --> $DIR/issue-72649-uninit-in-loop.rs:20:22
+ |
+LL | let value = NonCopy{};
+ | ----- move occurs because `value` has type `NonCopy`, which does not implement the `Copy` trait
+LL |
+LL | let _used = value;
+ | ----- value moved here
+LL |
+LL | let _used2 = value;
+ | ^^^^^ value used here after move
+
+error[E0382]: use of moved value: `value`
+ --> $DIR/issue-72649-uninit-in-loop.rs:32:26
+ |
+LL | let value = NonCopy{};
+ | ----- move occurs because `value` has type `NonCopy`, which does not implement the `Copy` trait
+...
+LL | let _used = value;
+ | ----- value moved here
+...
+LL | let _used2 = value;
+ | ^^^^^ value used here after move
+
+error[E0382]: use of moved value: `value`
+ --> $DIR/issue-72649-uninit-in-loop.rs:42:21
+ |
+LL | let value = NonCopy{};
+ | ----- move occurs because `value` has type `NonCopy`, which does not implement the `Copy` trait
+...
+LL | let _used = value;
+ | ^^^^^ value moved here, in previous iteration of loop
+
+error[E0382]: use of moved value: `value`
+ --> $DIR/issue-72649-uninit-in-loop.rs:53:22
+ |
+LL | let mut value = NonCopy{};
+ | --------- move occurs because `value` has type `NonCopy`, which does not implement the `Copy` trait
+...
+LL | let _used2 = value;
+ | ^^^^^ value moved here, in previous iteration of loop
+
+error[E0381]: use of possibly-uninitialized variable: `value`
+ --> $DIR/issue-72649-uninit-in-loop.rs:61:21
+ |
+LL | let _used = value;
+ | ^^^^^ use of possibly-uninitialized `value`
+
+error[E0381]: use of possibly-uninitialized variable: `value`
+ --> $DIR/issue-72649-uninit-in-loop.rs:69:21
+ |
+LL | let _used = value;
+ | ^^^^^ use of possibly-uninitialized `value`
+
+error: aborting due to 6 previous errors
+
+Some errors have detailed explanations: E0381, E0382.
+For more information about an error, try `rustc --explain E0381`.
--> $DIR/move-fn-self-receiver.rs:50:5
|
LL | let ret = mut_foo.use_mut_self();
- | ------- borrow of `mut_foo` occurs here
+ | ---------------------- borrow of `mut_foo` occurs here
LL | mut_foo;
| ^^^^^^^ move out of `mut_foo` occurs here
LL | ret;
LL | let b2 = &mut *b;
| ^ second mutable borrow occurs here
LL | b1.use_mut();
- | -- first borrow later used here
+ | ------------ first borrow later used here
error: aborting due to previous error
| --- help: consider changing this to be mutable: `mut arg`
...
LL | arg.mutate();
- | ^^^ cannot borrow as mutable
+ | ^^^^^^^^^^^^ cannot borrow as mutable
error[E0596]: cannot borrow `local` as mutable, as it is not declared as mutable
--> $DIR/mut-suggestion.rs:20:5
| ----- help: consider changing this to be mutable: `mut local`
...
LL | local.mutate();
- | ^^^^^ cannot borrow as mutable
+ | ^^^^^^^^^^^^^^ cannot borrow as mutable
error: aborting due to 2 previous errors
| |
| immutable borrow occurs here
LL | r.use_mut();
- | - mutable borrow later used here
+ | ----------- mutable borrow later used here
error[E0499]: cannot borrow `x` as mutable more than once at a time
--> $DIR/closure-access-spans.rs:11:5
| |
| second mutable borrow occurs here
LL | r.use_mut();
- | - first borrow later used here
+ | ----------- first borrow later used here
error[E0500]: closure requires unique access to `x` but it is already borrowed
--> $DIR/closure-access-spans.rs:17:5
| |
| closure construction occurs here
LL | r.use_mut();
- | - first borrow later used here
+ | ----------- first borrow later used here
error[E0503]: cannot use `x` because it was mutably borrowed
--> $DIR/closure-access-spans.rs:23:13
LL | move || x;
| ^ use of borrowed `x`
LL | r.use_ref();
- | - borrow later used here
+ | ----------- borrow later used here
error[E0505]: cannot move out of `x` because it is borrowed
--> $DIR/closure-access-spans.rs:29:5
| |
| move out of `x` occurs here
LL | r.use_ref();
- | - borrow later used here
+ | ----------- borrow later used here
error[E0382]: borrow of moved value: `x`
--> $DIR/closure-access-spans.rs:35:5
LL | let y = x;
| ^ move out of `x` occurs here
LL | f.use_ref();
- | - borrow later used here
+ | ----------- borrow later used here
error[E0502]: cannot borrow `x` as mutable because it is also borrowed as immutable
--> $DIR/closure-borrow-spans.rs:11:13
LL | let y = &mut x;
| ^^^^^^ mutable borrow occurs here
LL | f.use_ref();
- | - immutable borrow later used here
+ | ----------- immutable borrow later used here
error[E0597]: `x` does not live long enough
--> $DIR/closure-borrow-spans.rs:19:16
LL | }
| - `x` dropped here while still borrowed
LL | f.use_ref();
- | - borrow later used here
+ | ----------- borrow later used here
error[E0506]: cannot assign to `x` because it is borrowed
--> $DIR/closure-borrow-spans.rs:26:5
LL | x = 1;
| ^^^^^ assignment to borrowed `x` occurs here
LL | f.use_ref();
- | - borrow later used here
+ | ----------- borrow later used here
error[E0503]: cannot use `x` because it was mutably borrowed
--> $DIR/closure-borrow-spans.rs:32:13
LL | let y = x;
| ^ use of borrowed `x`
LL | f.use_ref();
- | - borrow later used here
+ | ----------- borrow later used here
error[E0502]: cannot borrow `x` as immutable because it is also borrowed as mutable
--> $DIR/closure-borrow-spans.rs:38:13
LL | let y = &x;
| ^^ immutable borrow occurs here
LL | f.use_ref();
- | - mutable borrow later used here
+ | ----------- mutable borrow later used here
error[E0499]: cannot borrow `x` as mutable more than once at a time
--> $DIR/closure-borrow-spans.rs:44:13
LL | let y = &mut x;
| ^^^^^^ second mutable borrow occurs here
LL | f.use_ref();
- | - first borrow later used here
+ | ----------- first borrow later used here
error[E0597]: `x` does not live long enough
--> $DIR/closure-borrow-spans.rs:52:16
LL | }
| - `x` dropped here while still borrowed
LL | f.use_ref();
- | - borrow later used here
+ | ----------- borrow later used here
error[E0506]: cannot assign to `x` because it is borrowed
--> $DIR/closure-borrow-spans.rs:59:5
LL | x = 1;
| ^^^^^ assignment to borrowed `x` occurs here
LL | f.use_ref();
- | - borrow later used here
+ | ----------- borrow later used here
error[E0505]: cannot move out of `x` because it is borrowed
--> $DIR/closure-borrow-spans.rs:65:13
LL | let y = x;
| ^ move out of `x` occurs here
LL | f.use_ref();
- | - borrow later used here
+ | ----------- borrow later used here
error[E0501]: cannot borrow `x` as immutable because previous closure requires unique access
--> $DIR/closure-borrow-spans.rs:71:13
LL | let y = &x;
| ^^ second borrow occurs here
LL | f.use_ref();
- | - first borrow later used here
+ | ----------- first borrow later used here
error[E0501]: cannot borrow `x` as mutable because previous closure requires unique access
--> $DIR/closure-borrow-spans.rs:77:13
LL | let y = &mut x;
| ^^^^^^ second borrow occurs here
LL | f.use_ref();
- | - first borrow later used here
+ | ----------- first borrow later used here
error[E0597]: `x` does not live long enough
--> $DIR/closure-borrow-spans.rs:86:16
LL | }
| - `x` dropped here while still borrowed
LL | f.use_ref();
- | - borrow later used here
+ | ----------- borrow later used here
error[E0506]: cannot assign to `*x` because it is borrowed
--> $DIR/closure-borrow-spans.rs:93:5
LL | *x = 1;
| ^^^^^^ assignment to borrowed `*x` occurs here
LL | f.use_ref();
- | - borrow later used here
+ | ----------- borrow later used here
error: aborting due to 14 previous errors
+++ /dev/null
-error[E0502]: cannot borrow `*map` as mutable because it is also borrowed as immutable
- --> $DIR/get_default.rs:21:17
- |
-LL | fn ok(map: &mut Map) -> &String {
- | - let's call the lifetime of this reference `'1`
-LL | loop {
-LL | match map.get() {
- | --- immutable borrow occurs here
-LL | Some(v) => {
-LL | return v;
- | - returning this value requires that `*map` is borrowed for `'1`
-...
-LL | map.set(String::new()); // Ideally, this would not error.
- | ^^^ mutable borrow occurs here
-
-error[E0502]: cannot borrow `*map` as mutable because it is also borrowed as immutable
- --> $DIR/get_default.rs:32:17
- |
-LL | fn err(map: &mut Map) -> &String {
- | - let's call the lifetime of this reference `'1`
-LL | loop {
-LL | match map.get() {
- | --- immutable borrow occurs here
-LL | Some(v) => {
-LL | map.set(String::new()); // Both AST and MIR error here
- | ^^^ mutable borrow occurs here
-LL |
-LL | return v;
- | - returning this value requires that `*map` is borrowed for `'1`
-
-error[E0502]: cannot borrow `*map` as mutable because it is also borrowed as immutable
- --> $DIR/get_default.rs:37:17
- |
-LL | fn err(map: &mut Map) -> &String {
- | - let's call the lifetime of this reference `'1`
-LL | loop {
-LL | match map.get() {
- | --- immutable borrow occurs here
-...
-LL | return v;
- | - returning this value requires that `*map` is borrowed for `'1`
-...
-LL | map.set(String::new()); // Ideally, just AST would error here
- | ^^^ mutable borrow occurs here
-
-error: aborting due to 3 previous errors
-
-For more information about this error, try `rustc --explain E0502`.
| - let's call the lifetime of this reference `'1`
LL | loop {
LL | match map.get() {
- | --- immutable borrow occurs here
+ | --------- immutable borrow occurs here
LL | Some(v) => {
LL | return v;
| - returning this value requires that `*map` is borrowed for `'1`
| - let's call the lifetime of this reference `'1`
LL | loop {
LL | match map.get() {
- | --- immutable borrow occurs here
+ | --------- immutable borrow occurs here
LL | Some(v) => {
LL | map.set(String::new()); // Both AST and MIR error here
| ^^^^^^^^^^^^^^^^^^^^^^ mutable borrow occurs here
| - let's call the lifetime of this reference `'1`
LL | loop {
LL | match map.get() {
- | --- immutable borrow occurs here
+ | --------- immutable borrow occurs here
...
LL | return v;
| - returning this value requires that `*map` is borrowed for `'1`
--> $DIR/issue-46589.rs:23:21
|
LL | *other = match (*other).get_self() {
- | -------- first mutable borrow occurs here
+ | ------------------- first mutable borrow occurs here
LL | Some(s) => s,
LL | None => (*other).new_self()
- | ^^^^^^^^
+ | ^^^^^^^^^^^^^^^^^^^
| |
| second mutable borrow occurs here
| first borrow later used here
--> $DIR/issue-51191.rs:22:9
|
LL | (&mut self).bar();
- | ^^^^^^^^^^^ cannot borrow as mutable
+ | ^^^^^^^^^^^^^^^^^ cannot borrow as mutable
error[E0596]: cannot borrow `self` as mutable, as it is not declared as mutable
--> $DIR/issue-51191.rs:28:9
LL | foo(a);
| - value moved here
LL | a.b.clone()
- | ^^^ value borrowed here after move
+ | ^^^^^^^^^^^ value borrowed here after move
error: aborting due to previous error
LL | }
| - here, drop of `child` needs exclusive access to `*child.raw`, because the type `C<'_>` implements the `Drop` trait
LL | members.len();
- | ------- borrow later used here
+ | ------------- borrow later used here
|
= note: consider using a `let` binding to create a longer lived value
--> $DIR/issue-54556-niconii.rs:22:20
|
LL | if let Ok(_) = counter.lock() { }
- | ^^^^^^^-------
+ | ^^^^^^^^^^^^^^
| |
| borrowed value does not live long enough
| a temporary with access to the borrow is created here ...
| - let's call the lifetime of this reference `'1`
...
LL | if let Some(n) = list[0].next.as_mut() {
- | ^^^^^^^^^^^^---------
+ | ^^^^^^^^^^^^^^^^^^^^^
| |
| `list[_].next` was mutably borrowed here in the previous iteration of the loop
| argument requires that `list[_].next` is borrowed for `'1`
| -- lifetime `'a` defined here
...
LL | if let Some(n) = (list.0).next.as_mut() {
- | ^^^^^^^^^^^^^---------
+ | ^^^^^^^^^^^^^^^^^^^^^^
| |
| `list.0.next` was mutably borrowed here in the previous iteration of the loop
| argument requires that `list.0.next` is borrowed for `'a`
| --------- first mutable borrow occurs here
LL | capitalize(slice);
LL | data.push('d');
- | ^^^^ second mutable borrow occurs here
+ | ^^^^^^^^^^^^^^ second mutable borrow occurs here
...
LL | capitalize(slice);
| ----- first borrow later used here
| --------- first mutable borrow occurs here
...
LL | data.push('e');
- | ^^^^ second mutable borrow occurs here
+ | ^^^^^^^^^^^^^^ second mutable borrow occurs here
...
LL | capitalize(slice);
| ----- first borrow later used here
| --------- first mutable borrow occurs here
...
LL | data.push('f');
- | ^^^^ second mutable borrow occurs here
+ | ^^^^^^^^^^^^^^ second mutable borrow occurs here
LL |
LL | capitalize(slice);
| ----- first borrow later used here
| -- lifetime `'a` defined here
...
LL | if let Some(n) = (list.0).next.as_mut() {
- | ^^^^^^^^^^^^^---------
+ | ^^^^^^^^^^^^^^^^^^^^^^
| |
| `list.0.next` was mutably borrowed here in the previous iteration of the loop
| argument requires that `list.0.next` is borrowed for `'a`
| -- lifetime `'a` defined here
...
LL | if let Some(n) = ((((list.0).0).0).0).0.next.as_mut() {
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^---------
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
| |
| `list.0.0.0.0.0.next` was mutably borrowed here in the previous iteration of the loop
| argument requires that `list.0.0.0.0.0.next` is borrowed for `'a`
+++ /dev/null
-error[E0502]: cannot borrow `my_struct.field` as mutable because it is also borrowed as immutable
- --> $DIR/region-ends-after-if-condition.rs:26:9
- |
-LL | let value = &my_struct.field;
- | ---------------- immutable borrow occurs here
-LL | if value.is_empty() {
-LL | my_struct.field.push_str("Hello, world!");
- | ^^^^^^^^^^^^^^^ mutable borrow occurs here
-...
-LL | drop(value);
- | ----- immutable borrow later used here
-
-error: aborting due to previous error
-
-For more information about this error, try `rustc --explain E0502`.
LL | let y: for<'a> fn(&'a ()) = x;
| ^ one type is more general than the other
|
- = note: expected fn pointer `for<'r> fn(&'r ())`
+ = note: expected fn pointer `for<'a> fn(&'a ())`
found fn pointer `fn(&())`
error: aborting due to previous error
LL | let a: for<'a, 'b> fn(&'a u32, &'b u32) -> &'a u32 = make_it();
| ^^^^^^^^^ one type is more general than the other
|
- = note: expected fn pointer `for<'r, 's> fn(&'r u32, &'s u32) -> &'r u32`
+ = note: expected fn pointer `for<'a, 'b> fn(&'a u32, &'b u32) -> &'a u32`
found fn pointer `for<'a> fn(&'a u32, &'a u32) -> &'a u32`
error[E0308]: mismatched types
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ one type is more general than the other
|
= note: expected fn pointer `for<'a, 'b> fn(&'a u32, &'b u32) -> &'a u32`
- found fn pointer `for<'r> fn(&'r u32, &'r u32) -> &'r u32`
+ found fn pointer `for<'a> fn(&'a u32, &'a u32) -> &'a u32`
error: aborting due to 2 previous errors
LL | let y: Box<dyn for<'a> Foo<'a>> = x;
| ^ one type is more general than the other
|
- = note: expected trait object `dyn for<'r> Foo<'r>`
+ = note: expected trait object `dyn for<'a> Foo<'a>`
found trait object `dyn Foo<'_>`
error: aborting due to previous error
| -------------------- first mutable borrow occurs here
LL | loop {
LL | my_struct.field.push_str("Hello, world!");
- | ^^^^^^^^^^^^^^^ second mutable borrow occurs here
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ second mutable borrow occurs here
LL |
LL | value.len();
- | ----- first borrow later used here
+ | ----------- first borrow later used here
error: aborting due to previous error
--> $DIR/object-safety-by-value-self-use.rs:15:5
|
LL | t.bar()
- | ^
+ | ^^^^^^^
error: aborting due to previous error
--- /dev/null
+// check-pass
+//
+// Check that we don't ignore private fields in usefulness checking
+#![deny(unreachable_patterns)]
+
+mod inner {
+ #[derive(PartialEq, Eq)]
+ pub struct PrivateField {
+ pub x: bool,
+ y: bool,
+ }
+
+ pub const FOO: PrivateField = PrivateField { x: true, y: true };
+ pub const BAR: PrivateField = PrivateField { x: true, y: false };
+}
+use inner::*;
+
+fn main() {
+ match FOO {
+ FOO => {}
+ BAR => {}
+ _ => {}
+ }
+
+ match FOO {
+ FOO => {}
+ PrivateField { x: true, .. } => {}
+ _ => {}
+ }
+}
error: unreachable pattern
--> $DIR/consts-opaque.rs:32:9
|
+LL | FOO => {}
+ | --- matches any value
+LL |
LL | _ => {} // should not be emitting unreachable warning
- | ^
+ | ^ unreachable pattern
|
note: the lint level is defined here
--> $DIR/consts-opaque.rs:6:9
error: unreachable pattern
--> $DIR/consts-opaque.rs:39:9
|
+LL | FOO_REF => {}
+ | ------- matches any value
+LL |
LL | Foo(_) => {} // should not be emitting unreachable warning
- | ^^^^^^
+ | ^^^^^^ unreachable pattern
warning: to use a constant of type `Foo` in a pattern, `Foo` must be annotated with `#[derive(PartialEq, Eq)]`
--> $DIR/consts-opaque.rs:45:9
error: unreachable pattern
--> $DIR/consts-opaque.rs:63:9
|
+LL | BAR => {}
+ | --- matches any value
+LL |
LL | Bar => {} // should not be emitting unreachable warning
- | ^^^
+ | ^^^ unreachable pattern
error: unreachable pattern
--> $DIR/consts-opaque.rs:65:9
|
-LL | Bar => {} // should not be emitting unreachable warning
+LL | BAR => {}
| --- matches any value
-LL |
+...
LL | _ => {}
| ^ unreachable pattern
error: unreachable pattern
--> $DIR/consts-opaque.rs:72:9
|
+LL | BAR => {}
+ | --- matches any value
+LL |
LL | BAR => {} // should not be emitting unreachable warning
- | ^^^
+ | ^^^ unreachable pattern
error: unreachable pattern
--> $DIR/consts-opaque.rs:75:9
|
+LL | BAR => {}
+ | --- matches any value
+...
LL | _ => {} // should not be emitting unreachable warning
- | ^
+ | ^ unreachable pattern
error: to use a constant of type `Baz` in a pattern, `Baz` must be annotated with `#[derive(PartialEq, Eq)]`
--> $DIR/consts-opaque.rs:80:9
error: unreachable pattern
--> $DIR/consts-opaque.rs:82:9
|
+LL | BAZ => {}
+ | --- matches any value
+LL |
LL | Baz::Baz1 => {} // should not be emitting unreachable warning
- | ^^^^^^^^^
+ | ^^^^^^^^^ unreachable pattern
error: unreachable pattern
--> $DIR/consts-opaque.rs:84:9
|
+LL | BAZ => {}
+ | --- matches any value
+...
LL | _ => {}
- | ^
+ | ^ unreachable pattern
error: to use a constant of type `Baz` in a pattern, `Baz` must be annotated with `#[derive(PartialEq, Eq)]`
--> $DIR/consts-opaque.rs:90:9
error: unreachable pattern
--> $DIR/consts-opaque.rs:92:9
|
+LL | BAZ => {}
+ | --- matches any value
+LL |
LL | _ => {}
- | ^
+ | ^ unreachable pattern
error: to use a constant of type `Baz` in a pattern, `Baz` must be annotated with `#[derive(PartialEq, Eq)]`
--> $DIR/consts-opaque.rs:97:9
error: unreachable pattern
--> $DIR/consts-opaque.rs:99:9
|
+LL | BAZ => {}
+ | --- matches any value
+LL |
LL | Baz::Baz2 => {} // should not be emitting unreachable warning
- | ^^^^^^^^^
+ | ^^^^^^^^^ unreachable pattern
error: unreachable pattern
--> $DIR/consts-opaque.rs:101:9
|
+LL | BAZ => {}
+ | --- matches any value
+...
LL | _ => {} // should not be emitting unreachable warning
- | ^
+ | ^ unreachable pattern
error: unreachable pattern
--> $DIR/consts-opaque.rs:127:9
|
+LL | Wrap(_) => {}
+ | ------- matches any value
LL | WRAPQUUX => {} // detected unreachable because we do inspect the `Wrap` layer
- | ^^^^^^^^
+ | ^^^^^^^^ unreachable pattern
error: unreachable pattern
--> $DIR/consts-opaque.rs:141:9
error: unreachable pattern
--> $DIR/reachability.rs:83:9
|
+LL | _ => {},
+ | - matches any value
LL | '\u{D7FF}'..='\u{E000}' => {},
- | ^^^^^^^^^^^^^^^^^^^^^^^
+ | ^^^^^^^^^^^^^^^^^^^^^^^ unreachable pattern
error: unreachable pattern
--> $DIR/reachability.rs:104:9
-error[E0004]: non-exhaustive patterns: `Box(_, _)` not covered
+error[E0004]: non-exhaustive patterns: `box _` not covered
--> $DIR/issue-3601.rs:30:44
|
LL | box NodeKind::Element(ed) => match ed.kind {
- | ^^^^^^^ pattern `Box(_, _)` not covered
+ | ^^^^^^^ pattern `box _` not covered
|
= help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
= note: the matched value is of type `Box<ElementKind>`
--- /dev/null
+// This used to ICE in exhaustiveness checking. Explanation here:
+// https://github.com/rust-lang/rust/issues/82772#issuecomment-905946768
+fn main() {
+ let Box { 1: _, .. }: Box<()>; //~ ERROR field `1` of
+ let Box { .. }: Box<()>;
+}
--- /dev/null
+error[E0451]: field `1` of struct `Box` is private
+ --> $DIR/issue-82772-match-box-as-struct.rs:4:15
+ |
+LL | let Box { 1: _, .. }: Box<()>;
+ | ^^^^ private field
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0451`.
error[E0275]: overflow evaluating the requirement `Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut std::ops::Range<u8>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>: Iterator`
|
- = help: consider adding a `#![recursion_limit="256"]` attribute to your crate (`issue_83150`)
+ = help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`issue_83150`)
= note: required because of the requirements on the impl of `Iterator` for `&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut std::ops::Range<u8>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>`
error: aborting due to previous error
--- /dev/null
+// Test the parse error for no value provided to recursion_limit
+
+#![recursion_limit]
+//~^ ERROR malformed `recursion_limit` attribute input
+
+fn main() {}
--- /dev/null
+error: malformed `recursion_limit` attribute input
+ --> $DIR/no-value.rs:3:1
+ |
+LL | #![recursion_limit]
+ | ^^^^^^^^^^^^^^^^^^^ help: must be of the form: `#![recursion_limit = "N"]`
+
+error: aborting due to previous error
+
--- /dev/null
+//~ ERROR overflow evaluating the requirement `&mut Self: DispatchFromDyn<&mut RustaceansAreAwesome>
+//~| HELP consider increasing the recursion limit
+// build-fail
+
+#![recursion_limit = "0"]
+
+fn main() {}
--- /dev/null
+error[E0275]: overflow evaluating the requirement `&mut Self: DispatchFromDyn<&mut RustaceansAreAwesome>`
+ |
+ = help: consider increasing the recursion limit by adding a `#![recursion_limit = "2"]` attribute to your crate (`zero_overflow`)
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0275`.
LL | test!(test);
| ^^^^^^^^^^^^
|
- = help: consider adding a `#![recursion_limit="0"]` attribute to your crate (`zero`)
+ = help: consider increasing the recursion limit by adding a `#![recursion_limit = "2"]` attribute to your crate (`zero`)
error: aborting due to previous error
// Here, the object is bounded by an anonymous lifetime and returned
// as `&'static`, so you get an error.
fn owned_receiver(x: Box<dyn Foo>) -> &'static () {
- x.borrowed() //~ ERROR cannot return value referencing local data `*x`
+ x.borrowed() //~ ERROR cannot return reference to local data `*x`
}
fn main() {}
-error[E0515]: cannot return value referencing local data `*x`
+error[E0515]: cannot return reference to local data `*x`
--> $DIR/region-object-lifetime-5.rs:11:5
|
LL | x.borrowed()
- | -^^^^^^^^^^^
- | |
- | returns a value referencing data owned by the current function
- | `*x` is borrowed here
+ | ^^^^^^^^^^^^ returns a reference to data owned by the current function
error: aborting due to previous error
LL | Bx(());
| ^^ not found in this scope
|
-help: consider importing this tuple struct
- |
-LL | use foo::Bx;
+note: tuple struct `foo::Bx` exists but is inaccessible
+ --> $DIR/issue-42944.rs:2:5
|
+LL | pub struct Bx(());
+ | ^^^^^^^^^^^^^^^^^^ not accessible
error: aborting due to 2 previous errors
--- /dev/null
+// Regression test for #88472, where a suggestion was issued to
+// import an inaccessible struct.
+
+#![warn(unused_imports)]
+//~^ NOTE: the lint level is defined here
+
+mod a {
+ struct Foo;
+ //~^ NOTE: struct `a::Foo` exists but is inaccessible
+ //~| NOTE: not accessible
+}
+
+mod b {
+ use crate::a::*;
+ //~^ WARNING: unused import
+ type Bar = Foo;
+ //~^ ERROR: cannot find type `Foo` in this scope [E0412]
+ //~| NOTE: not found in this scope
+}
+
+mod c {
+ enum Eee {}
+ //~^ NOTE: these enums exist but are inaccessible
+ //~| NOTE: `c::Eee`: not accessible
+
+ mod d {
+ enum Eee {}
+ //~^ NOTE: `c::d::Eee`: not accessible
+ }
+}
+
+mod e {
+ type Baz = Eee;
+ //~^ ERROR: cannot find type `Eee` in this scope [E0412]
+ //~| NOTE: not found in this scope
+}
+
+fn main() {}
--- /dev/null
+error[E0412]: cannot find type `Foo` in this scope
+ --> $DIR/issue-88472.rs:16:16
+ |
+LL | type Bar = Foo;
+ | ^^^ not found in this scope
+ |
+note: struct `a::Foo` exists but is inaccessible
+ --> $DIR/issue-88472.rs:8:5
+ |
+LL | struct Foo;
+ | ^^^^^^^^^^^ not accessible
+
+error[E0412]: cannot find type `Eee` in this scope
+ --> $DIR/issue-88472.rs:33:16
+ |
+LL | type Baz = Eee;
+ | ^^^ not found in this scope
+ |
+note: these enums exist but are inaccessible
+ --> $DIR/issue-88472.rs:22:5
+ |
+LL | enum Eee {}
+ | ^^^^^^^^ `c::Eee`: not accessible
+...
+LL | enum Eee {}
+ | ^^^^^^^^ `c::d::Eee`: not accessible
+
+warning: unused import: `crate::a::*`
+ --> $DIR/issue-88472.rs:14:9
+ |
+LL | use crate::a::*;
+ | ^^^^^^^^^^^
+ |
+note: the lint level is defined here
+ --> $DIR/issue-88472.rs:4:9
+ |
+LL | #![warn(unused_imports)]
+ | ^^^^^^^^^^^^^^
+
+error: aborting due to 2 previous errors; 1 warning emitted
+
+For more information about this error, try `rustc --explain E0412`.
| ---------- similarly named enum `E` defined here
...
LL | let _: Z = m::n::Z;
- | ^
+ | ^ help: an enum with a similar name exists: `E`
|
-help: an enum with a similar name exists
- |
-LL | let _: E = m::n::Z;
- | ~
-help: consider importing this enum
- |
-LL | use m::Z;
+note: enum `m::Z` exists but is inaccessible
+ --> $DIR/privacy-enum-ctor.rs:11:9
|
+LL | pub(in m) enum Z {
+ | ^^^^^^^^^^^^^^^^ not accessible
error[E0423]: expected value, found enum `m::n::Z`
--> $DIR/privacy-enum-ctor.rs:57:16
| ---------- similarly named enum `E` defined here
...
LL | let _: Z = m::n::Z::Fn;
- | ^
+ | ^ help: an enum with a similar name exists: `E`
|
-help: an enum with a similar name exists
- |
-LL | let _: E = m::n::Z::Fn;
- | ~
-help: consider importing this enum
- |
-LL | use m::Z;
+note: enum `m::Z` exists but is inaccessible
+ --> $DIR/privacy-enum-ctor.rs:11:9
|
+LL | pub(in m) enum Z {
+ | ^^^^^^^^^^^^^^^^ not accessible
error[E0412]: cannot find type `Z` in this scope
--> $DIR/privacy-enum-ctor.rs:64:12
| ---------- similarly named enum `E` defined here
...
LL | let _: Z = m::n::Z::Struct;
- | ^
+ | ^ help: an enum with a similar name exists: `E`
|
-help: an enum with a similar name exists
- |
-LL | let _: E = m::n::Z::Struct;
- | ~
-help: consider importing this enum
- |
-LL | use m::Z;
+note: enum `m::Z` exists but is inaccessible
+ --> $DIR/privacy-enum-ctor.rs:11:9
|
+LL | pub(in m) enum Z {
+ | ^^^^^^^^^^^^^^^^ not accessible
error[E0423]: expected value, found struct variant `m::n::Z::Struct`
--> $DIR/privacy-enum-ctor.rs:64:16
| ---------- similarly named enum `E` defined here
...
LL | let _: Z = m::n::Z::Unit {};
- | ^
+ | ^ help: an enum with a similar name exists: `E`
|
-help: an enum with a similar name exists
- |
-LL | let _: E = m::n::Z::Unit {};
- | ~
-help: consider importing this enum
- |
-LL | use m::Z;
+note: enum `m::Z` exists but is inaccessible
+ --> $DIR/privacy-enum-ctor.rs:11:9
|
+LL | pub(in m) enum Z {
+ | ^^^^^^^^^^^^^^^^ not accessible
error[E0603]: enum `Z` is private
--> $DIR/privacy-enum-ctor.rs:57:22
LL | xcrate::S;
| ^^^^^^^^^ constructor is not visible here due to private fields
|
-help: consider importing this tuple struct instead
- |
-LL | use m::S;
+note: tuple struct `m::S` exists but is inaccessible
+ --> $DIR/privacy-struct-ctor.rs:6:5
|
+LL | pub struct S(u8);
+ | ^^^^^^^^^^^^^^^^^ not accessible
error[E0603]: tuple struct constructor `Z` is private
--> $DIR/privacy-struct-ctor.rs:18:12
+++ /dev/null
-error[E0502]: cannot borrow `foo` as mutable because it is also borrowed as immutable
- --> $DIR/borrowck-issue-49631.rs:20:9
- |
-LL | while let Some(Ok(string)) = foo.get() {
- | --- immutable borrow occurs here
-LL | foo.mutate();
- | ^^^ mutable borrow occurs here
-LL |
-LL | println!("foo={:?}", *string);
- | ------- immutable borrow later used here
-
-error: aborting due to previous error
-
-For more information about this error, try `rustc --explain E0502`.
--> $DIR/borrowck-issue-49631.rs:20:9
|
LL | while let Some(Ok(string)) = foo.get() {
- | --- immutable borrow occurs here
+ | --------- immutable borrow occurs here
LL | foo.mutate();
| ^^^^^^^^^^^^ mutable borrow occurs here
LL |
LL | | };
| |_^ one type is more general than the other
|
- = note: expected type `for<'r, 's> Fn<(&'r Foo<'s>,)>`
+ = note: expected type `for<'a, 'b> Fn<(&'a Foo<'b>,)>`
found type `Fn<(&Foo<'_>,)>`
error[E0308]: mismatched types
LL | | };
| |_^ one type is more general than the other
|
- = note: expected type `for<'r, 's> Fn<(&'r Foo<'s>,)>`
+ = note: expected type `for<'a, 'b> Fn<(&'a Foo<'b>,)>`
found type `Fn<(&Foo<'_>,)>`
error: implementation of `FnOnce` is not general enough
LL | | };
| |_^ implementation of `FnOnce` is not general enough
|
- = note: `fn(&'2 Foo<'_>) -> &'2 Foo<'_> {id::<&'2 Foo<'_>>}` must implement `FnOnce<(&'1 Foo<'_>,)>`, for any lifetime `'1`...
+ = note: `fn(&'2 Foo<'_>) -> &'2 Foo<'_> {id::<&'2 Foo<'_>>}` must implement `FnOnce<(&'1 Foo<'b>,)>`, for any lifetime `'1`...
= note: ...but it actually implements `FnOnce<(&'2 Foo<'_>,)>`, for some specific lifetime `'2`
error: implementation of `FnOnce` is not general enough
LL | | };
| |_^ implementation of `FnOnce` is not general enough
|
- = note: `fn(&Foo<'2>) -> &Foo<'2> {id::<&Foo<'2>>}` must implement `FnOnce<(&Foo<'1>,)>`, for any lifetime `'1`...
+ = note: `fn(&Foo<'2>) -> &Foo<'2> {id::<&Foo<'2>>}` must implement `FnOnce<(&'a Foo<'1>,)>`, for any lifetime `'1`...
= note: ...but it actually implements `FnOnce<(&Foo<'2>,)>`, for some specific lifetime `'2`
error: aborting due to 5 previous errors
LL | mut Self => (),
| ^^^^ not found in this scope
|
-help: consider importing this unit struct
- |
-LL | use foo::Self;
+note: unit struct `foo::Self` exists but is inaccessible
+ --> $DIR/self_type_keyword.rs:2:3
|
+LL | struct Self;
+ | ^^^^^^^^^^^^ not accessible
error[E0392]: parameter `'Self` is never used
--> $DIR/self_type_keyword.rs:6:12
LL | fn deref_mut_method1(x: Own<Point>) {
| - help: consider changing this to be mutable: `mut x`
LL | x.set(0, 0);
- | ^ cannot borrow as mutable
+ | ^^^^^^^^^^^ cannot borrow as mutable
error[E0596]: cannot borrow `*x` as mutable, as it is behind a `&` reference
--> $DIR/borrowck-borrow-overloaded-auto-deref-mut.rs:121:5
LL | fn deref_extend_mut_method1(x: &Own<Point>) -> &mut isize {
| ----------- help: consider changing this to be a mutable reference: `&mut Own<Point>`
LL | x.y_mut()
- | ^ `x` is a `&` reference, so the data it refers to cannot be borrowed as mutable
+ | ^^^^^^^^^ `x` is a `&` reference, so the data it refers to cannot be borrowed as mutable
error[E0596]: cannot borrow `x` as mutable, as it is not declared as mutable
--> $DIR/borrowck-borrow-overloaded-auto-deref-mut.rs:129:6
LL | fn assign_method1<'a>(x: Own<Point>) {
| - help: consider changing this to be mutable: `mut x`
LL | *x.y_mut() = 3;
- | ^ cannot borrow as mutable
+ | ^^^^^^^^^ cannot borrow as mutable
error[E0596]: cannot borrow `*x` as mutable, as it is behind a `&` reference
--> $DIR/borrowck-borrow-overloaded-auto-deref-mut.rs:133:6
LL | fn assign_method2<'a>(x: &'a Own<Point>) {
| -------------- help: consider changing this to be a mutable reference: `&'a mut Own<Point>`
LL | *x.y_mut() = 3;
- | ^ `x` is a `&` reference, so the data it refers to cannot be borrowed as mutable
+ | ^^^^^^^^^ `x` is a `&` reference, so the data it refers to cannot be borrowed as mutable
error: aborting due to 10 previous errors
LL | fn test4(f: &Test) {
| ----- help: consider changing this to be a mutable reference: `&mut Test<'_>`
LL | f.f.call_mut(())
- | ^^^ `f` is a `&` reference, so the data it refers to cannot be borrowed as mutable
+ | ^^^^^^^^^^^^^^^^ `f` is a `&` reference, so the data it refers to cannot be borrowed as mutable
error[E0507]: cannot move out of `f`, a captured variable in an `FnMut` closure
--> $DIR/borrowck-call-is-borrow-issue-12224.rs:57:13
| ---- help: consider changing this to be a mutable reference: `&mut Foo`
LL | x.f();
LL | x.h();
- | ^ `x` is a `&` reference, so the data it refers to cannot be borrowed as mutable
+ | ^^^^^ `x` is a `&` reference, so the data it refers to cannot be borrowed as mutable
error: aborting due to previous error
LL | fn broken(x: &Vec<String>) {
| ------------ help: consider changing this to be a mutable reference: `&mut Vec<String>`
LL | x.push(format!("this is broken"));
- | ^ `x` is a `&` reference, so the data it refers to cannot be borrowed as mutable
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `x` is a `&` reference, so the data it refers to cannot be borrowed as mutable
error: aborting due to previous error
| creates a temporary which is freed while still in use
...
LL | v4.use_ref();
- | -- borrow later used here
+ | ------------ borrow later used here
|
= note: consider using a `let` binding to create a longer lived value
| -------- help: consider changing this to be a mutable reference: `&mut dyn Foo`
LL | x.borrowed();
LL | x.borrowed_mut();
- | ^ `x` is a `&` reference, so the data it refers to cannot be borrowed as mutable
+ | ^^^^^^^^^^^^^^^^ `x` is a `&` reference, so the data it refers to cannot be borrowed as mutable
error[E0596]: cannot borrow `*x` as mutable, as `x` is not declared as mutable
--> $DIR/borrowck-object-mutability.rs:18:5
| - help: consider changing this to be mutable: `mut x`
LL | x.borrowed();
LL | x.borrowed_mut();
- | ^ cannot borrow as mutable
+ | ^^^^^^^^^^^^^^^^ cannot borrow as mutable
error: aborting due to 2 previous errors
--> $DIR/destructor-restrictions.rs:8:10
|
LL | *a.borrow() + 1
- | ^---------
+ | ^^^^^^^^^^
| |
| borrowed value does not live long enough
| a temporary with access to the borrow is created here ...
--> $DIR/issue-23338-locals-die-before-temps-of-body.rs:10:5
|
LL | y.borrow().clone()
- | ^---------
+ | ^^^^^^^^^^
| |
| borrowed value does not live long enough
| a temporary with access to the borrow is created here ...
--> $DIR/issue-23338-locals-die-before-temps-of-body.rs:17:9
|
LL | y.borrow().clone()
- | ^---------
+ | ^^^^^^^^^^
| |
| borrowed value does not live long enough
| a temporary with access to the borrow is created here ...
LL | }
| - `a` dropped here while still borrowed
LL | p.use_ref();
- | - borrow later used here
+ | ----------- borrow later used here
error: aborting due to previous error
--> $DIR/issue-40157.rs:2:53
|
LL | {println!("{:?}", match { let foo = vec![1, 2]; foo.get(1) } { x => x });}
- | ------------------------^^^---------
+ | ------------------------^^^^^^^^^^--
| | | |
| | | `foo` dropped here while still borrowed
| | borrowed value does not live long enough
candidate #3: `UnusedTrait`
help: disambiguate the associated function for candidate #1
|
-LL | u.f8(42) + CtxtFn::f9(u, 342) + m.fff(42)
- | ~~~~~~~~~~~~~~~~~~
+LL | u.f8(42) + <usize as CtxtFn>::f9(u, 342) + m.fff(42)
+ | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
help: disambiguate the associated function for candidate #2
|
-LL | u.f8(42) + OtherTrait::f9(u, 342) + m.fff(42)
- | ~~~~~~~~~~~~~~~~~~~~~~
+LL | u.f8(42) + <usize as OtherTrait>::f9(u, 342) + m.fff(42)
+ | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
help: disambiguate the associated function for candidate #3
|
-LL | u.f8(42) + UnusedTrait::f9(u, 342) + m.fff(42)
- | ~~~~~~~~~~~~~~~~~~~~~~~
+LL | u.f8(42) + <usize as UnusedTrait>::f9(u, 342) + m.fff(42)
+ | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
error[E0599]: no method named `fff` found for struct `Myisize` in the current scope
--> $DIR/issue-7575.rs:62:30
= help: items from traits can only be used if the type parameter is bounded by the trait
help: disambiguate the associated function for the candidate
|
-LL | ManyImplTrait::is_str(t)
+LL | <T as ManyImplTrait>::is_str(t)
|
error: aborting due to 3 previous errors
LL | fn foo(mut a: &String) {
| ------- help: consider changing this to be a mutable reference: `&mut String`
LL | a.push_str("bar");
- | ^ `a` is a `&` reference, so the data it refers to cannot be borrowed as mutable
+ | ^^^^^^^^^^^^^^^^^ `a` is a `&` reference, so the data it refers to cannot be borrowed as mutable
error[E0596]: cannot borrow `*a` as mutable, as it is behind a `&` reference
--> $DIR/mut-arg-hint.rs:8:5
LL | pub fn foo<'a>(mut a: &'a String) {
| ---------- help: consider changing this to be a mutable reference: `&'a mut String`
LL | a.push_str("foo");
- | ^ `a` is a `&` reference, so the data it refers to cannot be borrowed as mutable
+ | ^^^^^^^^^^^^^^^^^ `a` is a `&` reference, so the data it refers to cannot be borrowed as mutable
error[E0596]: cannot borrow `*a` as mutable, as it is behind a `&` reference
--> $DIR/mut-arg-hint.rs:15:9
LL | pub fn foo(mut a: &String) {
| ------- help: consider changing this to be a mutable reference: `&mut String`
LL | a.push_str("foo");
- | ^ `a` is a `&` reference, so the data it refers to cannot be borrowed as mutable
+ | ^^^^^^^^^^^^^^^^^ `a` is a `&` reference, so the data it refers to cannot be borrowed as mutable
error: aborting due to 3 previous errors
| - `b` dropped here while still borrowed
LL |
LL | p.use_ref();
- | - borrow later used here
+ | ----------- borrow later used here
error: aborting due to previous error
LL | }
| - `c` dropped here while still borrowed
LL | f.use_mut();
- | - borrow later used here
+ | ----------- borrow later used here
error: aborting due to previous error
| ^ use of borrowed `x`
LL | let mut z = x;
LL | _y.push(&mut z);
- | -- borrow later used here
+ | --------------- borrow later used here
error[E0503]: cannot use `x` because it was mutably borrowed
--> $DIR/regions-escape-loop-via-vec.rs:6:21
LL | let mut z = x;
| ^ use of borrowed `x`
LL | _y.push(&mut z);
- | -- borrow later used here
+ | --------------- borrow later used here
error[E0597]: `z` does not live long enough
--> $DIR/regions-escape-loop-via-vec.rs:7:17
|
LL | _y.push(&mut z);
- | -- ^^^^^^ borrowed value does not live long enough
- | |
+ | --------^^^^^^-
+ | | |
+ | | borrowed value does not live long enough
| borrow later used here
...
LL | }
| ------ borrow of `x` occurs here
...
LL | _y.push(&mut z);
- | -- borrow later used here
+ | --------------- borrow later used here
LL |
LL | x += 1;
| ^^^^^^ use of borrowed `x`
| ^ move out of `y` occurs here
...
LL | *lock.lock().unwrap() = &z;
- | ---- borrow later used here
+ | ----------- borrow later used here
error[E0597]: `z` does not live long enough
--> $DIR/send-is-not-static-std-sync.rs:16:33
| - `z` dropped here while still borrowed
LL |
LL | lock.use_ref(); // (Mutex is #[may_dangle] so its dtor does not use `z` => needs explicit use)
- | ---- borrow later used here
+ | -------------- borrow later used here
error[E0505]: cannot move out of `y` because it is borrowed
--> $DIR/send-is-not-static-std-sync.rs:27:10
| ^ move out of `y` occurs here
...
LL | *lock.write().unwrap() = &z;
- | ---- borrow later used here
+ | ------------ borrow later used here
error[E0597]: `z` does not live long enough
--> $DIR/send-is-not-static-std-sync.rs:30:34
| - `z` dropped here while still borrowed
LL |
LL | lock.use_ref(); // (RwLock is #[may_dangle] so its dtor does not use `z` => needs explicit use)
- | ---- borrow later used here
+ | -------------- borrow later used here
error[E0505]: cannot move out of `y` because it is borrowed
--> $DIR/send-is-not-static-std-sync.rs:43:10
| ^ move out of `y` occurs here
...
LL | tx.send(&z).unwrap();
- | -- borrow later used here
+ | ----------- borrow later used here
error[E0597]: `z` does not live long enough
--> $DIR/send-is-not-static-std-sync.rs:46:17
LL | }
| - temporary value is freed at the end of this statement
LL | y.use_ref();
- | - borrow later used here
+ | ----------- borrow later used here
|
= note: consider using a `let` binding to create a longer lived value
= note: this error originates in the macro `vec` (in Nightly builds, run with -Z macro-backtrace for more info)
--- /dev/null
+// edition:2018
+
+// This is a regression test for #83564.
+// For some reason, Rust 2018 or higher is required to reproduce the bug.
+
+fn main() {
+ //~^ HELP consider importing one of these items
+ let _x = NonZeroU32::new(5).unwrap();
+ //~^ ERROR failed to resolve: use of undeclared type `NonZeroU32`
+}
--- /dev/null
+error[E0433]: failed to resolve: use of undeclared type `NonZeroU32`
+ --> $DIR/core-std-import-order-issue-83564.rs:8:14
+ |
+LL | let _x = NonZeroU32::new(5).unwrap();
+ | ^^^^^^^^^^ not found in this scope
+ |
+help: consider importing one of these items
+ |
+LL | use std::num::NonZeroU32;
+ |
+LL | use core::num::NonZeroU32;
+ |
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0433`.
LL | fn use_it<'a>(val: Box<dyn ObjectTrait<Assoc = i32>>) -> impl OtherTrait<'a> {
| -- lifetime `'a` defined here ------------------- opaque type requires that `val` is borrowed for `'a`
LL | val.use_self()
- | ^^^ borrowed value does not live long enough
+ | ^^^^^^^^^^^^^^ borrowed value does not live long enough
LL | }
| - `val` dropped here while still borrowed
|
LL | fn use_it<'a>(val: Box<dyn ObjectTrait<Assoc = i32>>) -> impl OtherTrait<'a> + 'a {
| ++++
-error[E0515]: cannot return value referencing function parameter `val`
+error[E0515]: cannot return reference to function parameter `val`
--> $DIR/impl-on-dyn-trait-with-implicit-static-bound-needing-more-suggestions.rs:43:9
|
LL | val.use_self()
- | ---^^^^^^^^^^^
- | |
- | returns a value referencing data owned by the current function
- | `val` is borrowed here
+ | ^^^^^^^^^^^^^^ returns a reference to data owned by the current function
-error[E0515]: cannot return value referencing function parameter `val`
+error[E0515]: cannot return reference to function parameter `val`
--> $DIR/impl-on-dyn-trait-with-implicit-static-bound-needing-more-suggestions.rs:109:9
|
LL | val.use_self()
- | ---^^^^^^^^^^^
- | |
- | returns a value referencing data owned by the current function
- | `val` is borrowed here
+ | ^^^^^^^^^^^^^^ returns a reference to data owned by the current function
error: aborting due to 3 previous errors
LL | fn use_it<'a>(val: Box<dyn ObjectTrait<Assoc = i32>>) -> impl OtherTrait<'a> {
| -- lifetime `'a` defined here ------------------- opaque type requires that `val` is borrowed for `'a`
LL | val.use_self()
- | ^^^ borrowed value does not live long enough
+ | ^^^^^^^^^^^^^^ borrowed value does not live long enough
LL | }
| - `val` dropped here while still borrowed
|
LL | fn use_it<'a>(val: Box<dyn ObjectTrait<Assoc = i32>>) -> impl OtherTrait<'a> + 'a {
| ++++
-error[E0515]: cannot return value referencing function parameter `val`
+error[E0515]: cannot return reference to function parameter `val`
--> $DIR/impl-on-dyn-trait-with-implicit-static-bound-needing-more-suggestions.rs:43:9
|
LL | val.use_self()
- | ---^^^^^^^^^^^
- | |
- | returns a value referencing data owned by the current function
- | `val` is borrowed here
+ | ^^^^^^^^^^^^^^ returns a reference to data owned by the current function
-error[E0515]: cannot return value referencing function parameter `val`
+error[E0515]: cannot return reference to function parameter `val`
--> $DIR/impl-on-dyn-trait-with-implicit-static-bound-needing-more-suggestions.rs:109:9
|
LL | val.use_self()
- | ---^^^^^^^^^^^
- | |
- | returns a value referencing data owned by the current function
- | `val` is borrowed here
+ | ^^^^^^^^^^^^^^ returns a reference to data owned by the current function
error[E0772]: `val` has lifetime `'a` but calling `use_self` introduces an implicit `'static` lifetime requirement
--> $DIR/impl-on-dyn-trait-with-implicit-static-bound-needing-more-suggestions.rs:66:13
--- /dev/null
+#![allow(unused, nonstandard_style)]
+#![deny(bindings_with_variant_name)]
+
+// If an enum has two different variants,
+// then it cannot be matched upon in a function argument.
+// It still gets a warning, but no suggestions.
+enum Foo {
+ C,
+ D,
+}
+
+fn foo(C: Foo) {} //~ERROR
+
+fn main() {
+ let C = Foo::D; //~ERROR
+}
--- /dev/null
+error[E0170]: pattern binding `C` is named the same as one of the variants of the type `Foo`
+ --> $DIR/issue-88730.rs:12:8
+ |
+LL | fn foo(C: Foo) {}
+ | ^
+ |
+note: the lint level is defined here
+ --> $DIR/issue-88730.rs:2:9
+ |
+LL | #![deny(bindings_with_variant_name)]
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error[E0170]: pattern binding `C` is named the same as one of the variants of the type `Foo`
+ --> $DIR/issue-88730.rs:15:9
+ |
+LL | let C = Foo::D;
+ | ^
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0170`.
--- /dev/null
+// run-rustfix
+
+use std::ops::Index;
+struct X;
+impl Index<i32> for X {
+ type Output = ();
+
+ fn index(&self, _: i32) -> &() {
+ &()
+ }
+}
+
+fn main() {
+ let x = vec![1, 2, 3];
+ x[x.len() -1]; //~ ERROR negative integers cannot be used to index on a
+ let x = [1, 2, 3];
+ x[x.len() -1]; //~ ERROR negative integers cannot be used to index on a
+ let x = &[1, 2, 3];
+ x[x.len() -1]; //~ ERROR negative integers cannot be used to index on a
+ let _ = x;
+ X[-1];
+}
--- /dev/null
+// run-rustfix
+
+use std::ops::Index;
+struct X;
+impl Index<i32> for X {
+ type Output = ();
+
+ fn index(&self, _: i32) -> &() {
+ &()
+ }
+}
+
+fn main() {
+ let x = vec![1, 2, 3];
+ x[-1]; //~ ERROR negative integers cannot be used to index on a
+ let x = [1, 2, 3];
+ x[-1]; //~ ERROR negative integers cannot be used to index on a
+ let x = &[1, 2, 3];
+ x[-1]; //~ ERROR negative integers cannot be used to index on a
+ let _ = x;
+ X[-1];
+}
--- /dev/null
+error: negative integers cannot be used to index on a `Vec<{integer}>`
+ --> $DIR/negative-literal-index.rs:15:7
+ |
+LL | x[-1];
+ | ^^ cannot use a negative integer for indexing on `Vec<{integer}>`
+ |
+help: to access an element starting from the end of the `Vec<{integer}>`, compute the index
+ |
+LL | x[x.len() -1];
+ | +++++++
+
+error: negative integers cannot be used to index on a `[{integer}; 3]`
+ --> $DIR/negative-literal-index.rs:17:7
+ |
+LL | x[-1];
+ | ^^ cannot use a negative integer for indexing on `[{integer}; 3]`
+ |
+help: to access an element starting from the end of the `[{integer}; 3]`, compute the index
+ |
+LL | x[x.len() -1];
+ | +++++++
+
+error: negative integers cannot be used to index on a `[{integer}; 3]`
+ --> $DIR/negative-literal-index.rs:19:7
+ |
+LL | x[-1];
+ | ^^ cannot use a negative integer for indexing on `[{integer}; 3]`
+ |
+help: to access an element starting from the end of the `[{integer}; 3]`, compute the index
+ |
+LL | x[x.len() -1];
+ | +++++++
+
+error: aborting due to 3 previous errors
+
--- /dev/null
+trait Foo {
+ type Type;
+
+ fn foo();
+ fn bar();
+ fn qux();
+}
+
+struct A;
+
+impl Foo for A {
+//~^ ERROR not all trait items implemented
+ type Typ = ();
+ //~^ ERROR type `Typ` is not a member of trait
+ //~| HELP there is an associated type with a similar name
+
+ fn fooo() {}
+ //~^ ERROR method `fooo` is not a member of trait
+ //~| HELP there is an associated function with a similar name
+
+ fn barr() {}
+ //~^ ERROR method `barr` is not a member of trait
+ //~| HELP there is an associated function with a similar name
+
+ fn quux() {}
+ //~^ ERROR method `quux` is not a member of trait
+ //~| HELP there is an associated function with a similar name
+}
+//~^ HELP implement the missing item
+//~| HELP implement the missing item
+//~| HELP implement the missing item
+//~| HELP implement the missing item
+
+trait Bar {
+ const Const: i32;
+}
+
+struct B;
+
+impl Bar for B {
+//~^ ERROR not all trait items implemented
+ const Cnst: i32 = 0;
+ //~^ ERROR const `Cnst` is not a member of trait
+ //~| HELP there is an associated constant with a similar name
+}
+//~^ HELP implement the missing item
+
+fn main() {}
--- /dev/null
+error[E0437]: type `Typ` is not a member of trait `Foo`
+ --> $DIR/suggest-trait-items.rs:13:5
+ |
+LL | type Typ = ();
+ | ^^^^^---^^^^^^
+ | | |
+ | | help: there is an associated type with a similar name: `Type`
+ | not a member of trait `Foo`
+
+error[E0407]: method `fooo` is not a member of trait `Foo`
+ --> $DIR/suggest-trait-items.rs:17:5
+ |
+LL | fn fooo() {}
+ | ^^^----^^^^^
+ | | |
+ | | help: there is an associated function with a similar name: `foo`
+ | not a member of trait `Foo`
+
+error[E0407]: method `barr` is not a member of trait `Foo`
+ --> $DIR/suggest-trait-items.rs:21:5
+ |
+LL | fn barr() {}
+ | ^^^----^^^^^
+ | | |
+ | | help: there is an associated function with a similar name: `bar`
+ | not a member of trait `Foo`
+
+error[E0407]: method `quux` is not a member of trait `Foo`
+ --> $DIR/suggest-trait-items.rs:25:5
+ |
+LL | fn quux() {}
+ | ^^^----^^^^^
+ | | |
+ | | help: there is an associated function with a similar name: `qux`
+ | not a member of trait `Foo`
+
+error[E0438]: const `Cnst` is not a member of trait `Bar`
+ --> $DIR/suggest-trait-items.rs:42:5
+ |
+LL | const Cnst: i32 = 0;
+ | ^^^^^^----^^^^^^^^^^
+ | | |
+ | | help: there is an associated constant with a similar name: `Const`
+ | not a member of trait `Bar`
+
+error[E0046]: not all trait items implemented, missing: `Type`, `foo`, `bar`, `qux`
+ --> $DIR/suggest-trait-items.rs:11:1
+ |
+LL | type Type;
+ | ---------- `Type` from trait
+LL |
+LL | fn foo();
+ | --------- `foo` from trait
+LL | fn bar();
+ | --------- `bar` from trait
+LL | fn qux();
+ | --------- `qux` from trait
+...
+LL | impl Foo for A {
+ | ^^^^^^^^^^^^^^ missing `Type`, `foo`, `bar`, `qux` in implementation
+
+error[E0046]: not all trait items implemented, missing: `Const`
+ --> $DIR/suggest-trait-items.rs:40:1
+ |
+LL | const Const: i32;
+ | ----------------- `Const` from trait
+...
+LL | impl Bar for B {
+ | ^^^^^^^^^^^^^^ missing `Const` in implementation
+
+error: aborting due to 7 previous errors
+
+Some errors have detailed explanations: E0046, E0407, E0437, E0438.
+For more information about an error, try `rustc --explain E0046`.
LL | iso(left, right)
| ^^^
|
- = help: consider adding a `#![recursion_limit="256"]` attribute to your crate (`mutual_recursion_issue_75860`)
+ = help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`mutual_recursion_issue_75860`)
note: required by a bound in `Option`
--> $SRC_DIR/core/src/option.rs:LL:COL
|
--- /dev/null
+fn main() {
+ let _ = {42}(); //~ ERROR expected function, found `{integer}`
+}
--- /dev/null
+error[E0618]: expected function, found `{integer}`
+ --> $DIR/call-block.rs:2:13
+ |
+LL | let _ = {42}();
+ | ^^^^--
+ | |
+ | call expression requires function
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0618`.
| -- `x` moved due to usage in operator
LL |
LL | x.clone();
- | ^ value borrowed here after move
+ | ^^^^^^^^^ value borrowed here after move
|
note: calling this operator moves the left-hand side
--> $SRC_DIR/core/src/ops/bit.rs:LL:COL
| - value moved here
LL |
LL | let x = n.to_string();
- | ^ value borrowed here after move
+ | ^^^^^^^^^^^^^ value borrowed here after move
error: aborting due to previous error
-Subproject commit 0121d66aa2ef5ffa9735f86c2b56f5fdc5a837a6
+Subproject commit d56b42c549dbb7e7d0f712c51b39400260d114d4
[alias]
uitest = "test --test compile-test"
-dev = "run --target-dir clippy_dev/target --package clippy_dev --bin clippy_dev --manifest-path clippy_dev/Cargo.toml --"
-lintcheck = "run --target-dir lintcheck/target --package lintcheck --bin lintcheck --manifest-path lintcheck/Cargo.toml -- "
+dev = "run --package clippy_dev --bin clippy_dev --manifest-path clippy_dev/Cargo.toml --"
+lintcheck = "run --package lintcheck --bin lintcheck --manifest-path lintcheck/Cargo.toml -- "
collect-metadata = "test --test dogfood --features metadata-collector-lint -- run_metadata_collection_lint --ignored"
[build]
# -Zbinary-dep-depinfo allows us to track which rlib files to use for compiling UI tests
rustflags = ["-Zunstable-options", "-Zbinary-dep-depinfo"]
+target-dir = "target"
Additional labels can be added to this issue by including the following command
(without the space after the @ symbol):
-`@rustbot label +<label>`
+@ rustbot label +<label>
Common labels for this issue type are:
* C-an-interesting-project
Additional labels can be added to this issue by including the following command
(without the space after the @ symbol):
-`@rustbot label +<label>`
+@ rustbot label +<label>
Common labels for this issue type are:
* `I-suggestion-causes-error`
Additional labels can be added to this issue by including the following command
(without the space after the @ symbol):
-`@rustbot label +<label>`
+@ rustbot label +<label>
Common labels for this issue type are:
* I-suggestion-causes-error
## Unreleased / In Rust Nightly
-[74d1561...master](https://github.com/rust-lang/rust-clippy/compare/74d1561...master)
+[7bfc26e...master](https://github.com/rust-lang/rust-clippy/compare/7bfc26e...master)
+
+## Rust 1.56
+
+Current beta, release 2021-10-21
+
+[74d1561...7bfc26e](https://github.com/rust-lang/rust-clippy/compare/74d1561...7bfc26e)
+
+### New Lints
+
+* [`unwrap_or_else_default`]
+ [#7516](https://github.com/rust-lang/rust-clippy/pull/7516)
+
+### Enhancements
+
+* [`needless_continue`]: Now also lints in `loop { continue; }` case
+ [#7477](https://github.com/rust-lang/rust-clippy/pull/7477)
+* [`disallowed_type`]: Now also primitive types can be disallowed
+ [#7488](https://github.com/rust-lang/rust-clippy/pull/7488)
+* [`manual_swap`]: Now also lints on xor swaps
+ [#7506](https://github.com/rust-lang/rust-clippy/pull/7506)
+* [`map_flatten`]: Now also lints on the `Result` type
+ [#7522](https://github.com/rust-lang/rust-clippy/pull/7522)
+* [`no_effect`]: Now also lints on inclusive ranges
+ [#7556](https://github.com/rust-lang/rust-clippy/pull/7556)
+
+### False Positive Fixes
+
+* [`nonstandard_macro_braces`]: No longer lints on similar named nested macros
+ [#7478](https://github.com/rust-lang/rust-clippy/pull/7478)
+* [`too_many_lines`]: No longer lints in closures to avoid duplicated diagnostics
+ [#7534](https://github.com/rust-lang/rust-clippy/pull/7534)
+* [`similar_names`]: No longer complains about `iter` and `item` being too
+ similar [#7546](https://github.com/rust-lang/rust-clippy/pull/7546)
+
+### Suggestion Fixes/Improvements
+
+* [`similar_names`]: No longer suggests to insert or add an underscore as a fix
+ [#7221](https://github.com/rust-lang/rust-clippy/pull/7221)
+* [`new_without_default`]: No longer shows the full qualified type path when
+ suggesting adding a `Default` implementation
+ [#7493](https://github.com/rust-lang/rust-clippy/pull/7493)
+* [`while_let_on_iterator`]: Now suggests re-borrowing mutable references
+ [#7520](https://github.com/rust-lang/rust-clippy/pull/7520)
+* [`extend_with_drain`]: Improve code suggestion for mutable and immutable
+ references [#7533](https://github.com/rust-lang/rust-clippy/pull/7533)
+* [`trivially_copy_pass_by_ref`]: Now properly handles `Self` type
+ [#7535](https://github.com/rust-lang/rust-clippy/pull/7535)
+* [`never_loop`]: Now suggests using `if let` instead of a `for` loop when
+ applicable [#7541](https://github.com/rust-lang/rust-clippy/pull/7541)
+
+### Documentation Improvements
+
+* Clippy now uses a lint to generate its lint documentation. [Lints all the way
+ down](https://en.wikipedia.org/wiki/Turtles_all_the_way_down).
+ [#7502](https://github.com/rust-lang/rust-clippy/pull/7502)
+* Reworked Clippy's website:
+ [#7172](https://github.com/rust-lang/rust-clippy/issues/7172)
+ [#7279](https://github.com/rust-lang/rust-clippy/pull/7279)
+ * Added applicability information about lints
+ * Added a link to jump into the implementation
+ * Improved loading times
+ * Adapted some styling
+* `cargo clippy --help` now also explains the `--fix` and `--no-deps` flag
+ [#7492](https://github.com/rust-lang/rust-clippy/pull/7492)
+* [`unnested_or_patterns`]: Removed `or_patterns` feature gate in the code
+ example [#7507](https://github.com/rust-lang/rust-clippy/pull/7507)
+
+### New Lints
+
+* Renamed Lint: `if_let_some_result` is now called [`match_result_ok`]. Now also handles `while let` case.
## Rust 1.55
-Current beta, release 2021-09-09
+Current stable, released 2021-09-09
[3ae8faf...74d1561](https://github.com/rust-lang/rust-clippy/compare/3ae8faf...74d1561)
* [`use_self`]
[#7428](https://github.com/rust-lang/rust-clippy/pull/7428)
-### Documentation Improvements
-
-* Reworked Clippy's website:
- [#7279](https://github.com/rust-lang/rust-clippy/pull/7279)
- [#7172](https://github.com/rust-lang/rust-clippy/issues/7172)
- * Added applicability information about lints
- * Added a link to jump into the implementation
- * Improved loading times
- * Adapted some styling
-* Clippy now uses a lint to generate its documentation
- [#7298](https://github.com/rust-lang/rust-clippy/pull/7298)
-
## Rust 1.54
-Current stable, released 2021-07-29
+Released 2021-07-29
[7c7683c...3ae8faf](https://github.com/rust-lang/rust-clippy/compare/7c7683c...3ae8faf)
[#5913](https://github.com/rust-lang/rust-clippy/pull/5913)
* Add example of false positive to [`ptr_arg`] docs.
[#5885](https://github.com/rust-lang/rust-clippy/pull/5885)
-* [`box_vec`], [`vec_box`] and [`borrowed_box`]: add link to the documentation of `Box`
+* [`box_vec`](https://rust-lang.github.io/rust-clippy/master/index.html#box_collection), [`vec_box`] and [`borrowed_box`]: add link to the documentation of `Box`
[#6023](https://github.com/rust-lang/rust-clippy/pull/6023)
## Rust 1.47
* `unknown_clippy_lints` [#4963](https://github.com/rust-lang/rust-clippy/pull/4963)
* [`explicit_into_iter_loop`] [#4978](https://github.com/rust-lang/rust-clippy/pull/4978)
* [`useless_attribute`] [#5022](https://github.com/rust-lang/rust-clippy/pull/5022)
-* [`if_let_some_result`] [#5032](https://github.com/rust-lang/rust-clippy/pull/5032)
+* `if_let_some_result` [#5032](https://github.com/rust-lang/rust-clippy/pull/5032)
### ICE fixes
[`bool_comparison`]: https://rust-lang.github.io/rust-clippy/master/index.html#bool_comparison
[`borrow_interior_mutable_const`]: https://rust-lang.github.io/rust-clippy/master/index.html#borrow_interior_mutable_const
[`borrowed_box`]: https://rust-lang.github.io/rust-clippy/master/index.html#borrowed_box
-[`box_vec`]: https://rust-lang.github.io/rust-clippy/master/index.html#box_vec
+[`box_collection`]: https://rust-lang.github.io/rust-clippy/master/index.html#box_collection
[`boxed_local`]: https://rust-lang.github.io/rust-clippy/master/index.html#boxed_local
[`branches_sharing_code`]: https://rust-lang.github.io/rust-clippy/master/index.html#branches_sharing_code
[`builtin_type_shadow`]: https://rust-lang.github.io/rust-clippy/master/index.html#builtin_type_shadow
[`identity_op`]: https://rust-lang.github.io/rust-clippy/master/index.html#identity_op
[`if_let_mutex`]: https://rust-lang.github.io/rust-clippy/master/index.html#if_let_mutex
[`if_let_redundant_pattern_matching`]: https://rust-lang.github.io/rust-clippy/master/index.html#if_let_redundant_pattern_matching
-[`if_let_some_result`]: https://rust-lang.github.io/rust-clippy/master/index.html#if_let_some_result
[`if_not_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#if_not_else
[`if_same_then_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#if_same_then_else
+[`if_then_panic`]: https://rust-lang.github.io/rust-clippy/master/index.html#if_then_panic
[`if_then_some_else_none`]: https://rust-lang.github.io/rust-clippy/master/index.html#if_then_some_else_none
[`ifs_same_cond`]: https://rust-lang.github.io/rust-clippy/master/index.html#ifs_same_cond
[`implicit_clone`]: https://rust-lang.github.io/rust-clippy/master/index.html#implicit_clone
[`iter_count`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_count
[`iter_next_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_next_loop
[`iter_next_slice`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_next_slice
+[`iter_not_returning_iterator`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_not_returning_iterator
[`iter_nth`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_nth
[`iter_nth_zero`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_nth_zero
[`iter_skip_next`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_skip_next
[`match_on_vec_items`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_on_vec_items
[`match_overlapping_arm`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_overlapping_arm
[`match_ref_pats`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_ref_pats
+[`match_result_ok`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_result_ok
[`match_same_arms`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_same_arms
[`match_single_binding`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_single_binding
[`match_wild_err_arm`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_wild_err_arm
[`reversed_empty_ranges`]: https://rust-lang.github.io/rust-clippy/master/index.html#reversed_empty_ranges
[`same_functions_in_if_condition`]: https://rust-lang.github.io/rust-clippy/master/index.html#same_functions_in_if_condition
[`same_item_push`]: https://rust-lang.github.io/rust-clippy/master/index.html#same_item_push
+[`same_name_method`]: https://rust-lang.github.io/rust-clippy/master/index.html#same_name_method
[`search_is_some`]: https://rust-lang.github.io/rust-clippy/master/index.html#search_is_some
[`self_assignment`]: https://rust-lang.github.io/rust-clippy/master/index.html#self_assignment
[`self_named_constructors`]: https://rust-lang.github.io/rust-clippy/master/index.html#self_named_constructors
keywords = ["clippy", "lint", "plugin"]
categories = ["development-tools", "development-tools::cargo-plugins"]
build = "build.rs"
-edition = "2018"
+edition = "2021"
publish = false
[[bin]]
| `clippy::style` | code that should be written in a more idiomatic way | **warn** |
| `clippy::complexity` | code that does something simple but in a complex way | **warn** |
| `clippy::perf` | code that can be written to run faster | **warn** |
-| `clippy::pedantic` | lints which are rather strict or might have false positives | allow |
+| `clippy::pedantic` | lints which are rather strict or have occasional false positives | allow |
| `clippy::nursery` | new lints that are still under development | allow |
| `clippy::cargo` | lints for the cargo manifest | allow |
[package]
name = "clippy_dev"
version = "0.0.1"
-edition = "2018"
+edition = "2021"
[dependencies]
bytecount = "0.6"
//! `bless` updates the reference files in the repo with changed output files
//! from the last test run.
-use std::env;
use std::ffi::OsStr;
use std::fs;
use std::lazy::SyncLazy;
use crate::clippy_project_root;
-// NOTE: this is duplicated with tests/cargo/mod.rs What to do?
-pub static CARGO_TARGET_DIR: SyncLazy<PathBuf> = SyncLazy::new(|| match env::var_os("CARGO_TARGET_DIR") {
- Some(v) => v.into(),
- None => env::current_dir().unwrap().join("target"),
-});
-
static CLIPPY_BUILD_TIME: SyncLazy<Option<std::time::SystemTime>> = SyncLazy::new(|| {
- let profile = env::var("PROFILE").unwrap_or_else(|_| "debug".to_string());
- let mut path = PathBuf::from(&**CARGO_TARGET_DIR);
- path.push(profile);
- path.push("cargo-clippy");
+ let mut path = std::env::current_exe().unwrap();
+ path.set_file_name("cargo-clippy");
fs::metadata(path).ok()?.modified().ok()
});
}
fn build_dir() -> PathBuf {
- let profile = env::var("PROFILE").unwrap_or_else(|_| "debug".to_string());
- let mut path = PathBuf::new();
- path.push(CARGO_TARGET_DIR.clone());
- path.push(profile);
- path.push("test_build_base");
+ let mut path = std::env::current_exe().unwrap();
+ path.set_file_name("test");
path
}
readme = "README.md"
license = "MIT OR Apache-2.0"
keywords = ["clippy", "lint", "plugin"]
-edition = "2018"
+edition = "2021"
[dependencies]
cargo_metadata = "0.12"
use clippy_utils::{in_macro, is_automatically_derived, is_default_equivalent, remove_blocks};
use rustc_hir::{
def::{DefKind, Res},
- Body, Expr, ExprKind, Impl, ImplItemKind, Item, ItemKind, Node, QPath,
+ Body, Expr, ExprKind, GenericArg, Impl, ImplItemKind, Item, ItemKind, Node, PathSegment, QPath, TyKind,
};
use rustc_lint::{LateContext, LateLintPass};
-use rustc_middle::ty::TypeFoldable;
use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::sym;
if let ItemKind::Impl(Impl {
of_trait: Some(ref trait_ref),
items: [child],
+ self_ty,
..
}) = item.kind;
if let attrs = cx.tcx.hir().attrs(item.hir_id());
if let ImplItemKind::Fn(_, b) = &impl_item.kind;
if let Body { value: func_expr, .. } = cx.tcx.hir().body(*b);
if let Some(adt_def) = cx.tcx.type_of(item.def_id).ty_adt_def();
+ if !attrs.iter().any(|attr| attr.doc_str().is_some());
+ if let child_attrs = cx.tcx.hir().attrs(impl_item_hir);
+ if !child_attrs.iter().any(|attr| attr.doc_str().is_some());
then {
- if cx.tcx.type_of(item.def_id).definitely_has_param_types_or_consts(cx.tcx) {
- return;
+ if let TyKind::Path(QPath::Resolved(_, p)) = self_ty.kind {
+ if let Some(PathSegment { args: Some(a), .. }) = p.segments.last() {
+ for arg in a.args {
+ if !matches!(arg, GenericArg::Lifetime(_)) {
+ return;
+ }
+ }
+ }
}
let should_emit = match remove_blocks(func_expr).kind {
ExprKind::Tup(fields) => fields.iter().all(|e| is_default_equivalent(cx, e)),
-use clippy_utils::diagnostics::span_lint;
+use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::fn_def_id;
-use rustc_data_structures::fx::FxHashSet;
-use rustc_hir::{def::Res, def_id::DefId, Crate, Expr};
+use rustc_hir::{def::Res, def_id::DefIdMap, Crate, Expr};
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::{declare_tool_lint, impl_lint_pass};
-use rustc_span::Symbol;
+
+use crate::utils::conf;
declare_clippy_lint! {
/// ### What it does
/// Denies the configured methods and functions in clippy.toml
///
/// ### Why is this bad?
- /// Some methods are undesirable in certain contexts,
- /// and it's beneficial to lint for them as needed.
+ /// Some methods are undesirable in certain contexts, and it's beneficial to
+ /// lint for them as needed.
///
/// ### Example
/// An example clippy.toml configuration:
/// ```toml
/// # clippy.toml
- /// disallowed-methods = ["std::vec::Vec::leak", "std::time::Instant::now"]
+ /// disallowed-methods = [
+ /// # Can use a string as the path of the disallowed method.
+ /// "std::boxed::Box::new",
+ /// # Can also use an inline table with a `path` key.
+ /// { path = "std::time::Instant::now" },
+ /// # When using an inline table, can add a `reason` for why the method
+ /// # is disallowed.
+ /// { path = "std::vec::Vec::leak", reason = "no leaking memory" },
+ /// ]
/// ```
///
/// ```rust,ignore
/// // Example code where clippy issues a warning
/// let xs = vec![1, 2, 3, 4];
/// xs.leak(); // Vec::leak is disallowed in the config.
+ /// // The diagnostic contains the message "no leaking memory".
///
/// let _now = Instant::now(); // Instant::now is disallowed in the config.
+ ///
+ /// let _box = Box::new(3); // Box::new is disallowed in the config.
/// ```
///
/// Use instead:
#[derive(Clone, Debug)]
pub struct DisallowedMethod {
- disallowed: FxHashSet<Vec<Symbol>>,
- def_ids: FxHashSet<(DefId, Vec<Symbol>)>,
+ conf_disallowed: Vec<conf::DisallowedMethod>,
+ disallowed: DefIdMap<Option<String>>,
}
impl DisallowedMethod {
- pub fn new(disallowed: &FxHashSet<String>) -> Self {
+ pub fn new(conf_disallowed: Vec<conf::DisallowedMethod>) -> Self {
Self {
- disallowed: disallowed
- .iter()
- .map(|s| s.split("::").map(|seg| Symbol::intern(seg)).collect::<Vec<_>>())
- .collect(),
- def_ids: FxHashSet::default(),
+ conf_disallowed,
+ disallowed: DefIdMap::default(),
}
}
}
impl<'tcx> LateLintPass<'tcx> for DisallowedMethod {
fn check_crate(&mut self, cx: &LateContext<'_>, _: &Crate<'_>) {
- for path in &self.disallowed {
- let segs = path.iter().map(ToString::to_string).collect::<Vec<_>>();
- if let Res::Def(_, id) = clippy_utils::path_to_res(cx, &segs.iter().map(String::as_str).collect::<Vec<_>>())
- {
- self.def_ids.insert((id, path.clone()));
+ for conf in &self.conf_disallowed {
+ let (path, reason) = match conf {
+ conf::DisallowedMethod::Simple(path) => (path, None),
+ conf::DisallowedMethod::WithReason { path, reason } => (
+ path,
+ reason.as_ref().map(|reason| format!("{} (from clippy.toml)", reason)),
+ ),
+ };
+ let segs: Vec<_> = path.split("::").collect();
+ if let Res::Def(_, id) = clippy_utils::path_to_res(cx, &segs) {
+ self.disallowed.insert(id, reason);
}
}
}
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
- if let Some(def_id) = fn_def_id(cx, expr) {
- if self.def_ids.iter().any(|(id, _)| def_id == *id) {
- let func_path = cx.get_def_path(def_id);
- let func_path_string = func_path
- .into_iter()
- .map(Symbol::to_ident_string)
- .collect::<Vec<_>>()
- .join("::");
-
- span_lint(
- cx,
- DISALLOWED_METHOD,
- expr.span,
- &format!("use of a disallowed method `{}`", func_path_string),
- );
+ let def_id = match fn_def_id(cx, expr) {
+ Some(def_id) => def_id,
+ None => return,
+ };
+ let reason = match self.disallowed.get(&def_id) {
+ Some(reason) => reason,
+ None => return,
+ };
+ let func_path = cx.tcx.def_path_str(def_id);
+ let msg = format!("use of a disallowed method `{}`", func_path);
+ span_lint_and_then(cx, DISALLOWED_METHOD, expr.span, &msg, |diag| {
+ if let Some(reason) = reason {
+ diag.note(reason);
}
- }
+ });
}
}
Self {
disallowed: disallowed
.iter()
- .map(|s| s.split("::").map(|seg| Symbol::intern(seg)).collect::<Vec<_>>())
+ .map(|s| s.split("::").map(Symbol::intern).collect::<Vec<_>>())
.collect(),
def_ids: FxHashSet::default(),
prim_tys: FxHashSet::default(),
if trait_item.id.hir_id() == hir_id {
// be sure we have `self` parameter in this function
if let AssocItemKind::Fn { has_self: true } = trait_item.kind {
- trait_self_ty =
- Some(TraitRef::identity(cx.tcx, trait_item.id.def_id.to_def_id()).self_ty().skip_binder());
+ trait_self_ty = Some(
+ TraitRef::identity(cx.tcx, trait_item.id.def_id.to_def_id())
+ .self_ty()
+ .skip_binder(),
+ );
}
}
}
use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then};
use clippy_utils::higher::VecArgs;
use clippy_utils::source::snippet_opt;
-use clippy_utils::ty::{implements_trait, type_is_unsafe_function};
use clippy_utils::usage::UsedAfterExprVisitor;
-use clippy_utils::{get_enclosing_loop_or_closure, higher};
-use clippy_utils::{is_adjusted, iter_input_pats};
+use clippy_utils::{get_enclosing_loop_or_closure, higher, path_to_local_id};
use if_chain::if_chain;
use rustc_errors::Applicability;
-use rustc_hir::{def_id, Expr, ExprKind, Param, PatKind, QPath};
-use rustc_lint::{LateContext, LateLintPass, LintContext};
-use rustc_middle::lint::in_external_macro;
-use rustc_middle::ty::{self, ClosureKind, Ty};
+use rustc_hir::def_id::DefId;
+use rustc_hir::{Expr, ExprKind, Param, PatKind, Unsafety};
+use rustc_lint::{LateContext, LateLintPass};
+use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow};
+use rustc_middle::ty::subst::Subst;
+use rustc_middle::ty::{self, ClosureKind, Ty, TypeFoldable};
use rustc_session::{declare_lint_pass, declare_tool_lint};
declare_clippy_lint! {
/// ### Why is this bad?
/// It's unnecessary to create the closure.
///
- /// ### Known problems
- /// [#3071](https://github.com/rust-lang/rust-clippy/issues/3071),
- /// [#3942](https://github.com/rust-lang/rust-clippy/issues/3942),
- /// [#4002](https://github.com/rust-lang/rust-clippy/issues/4002)
- ///
- ///
/// ### Example
/// ```rust,ignore
/// Some('a').map(|s| s.to_uppercase());
impl<'tcx> LateLintPass<'tcx> for EtaReduction {
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
- if in_external_macro(cx.sess(), expr.span) {
+ if expr.span.from_expansion() {
return;
}
-
- match expr.kind {
- ExprKind::Call(_, args) | ExprKind::MethodCall(_, _, args, _) => {
- for arg in args {
- // skip `foo(macro!())`
- if arg.span.ctxt() == expr.span.ctxt() {
- check_closure(cx, arg);
- }
- }
- },
- _ => (),
- }
- }
-}
-
-fn check_closure<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
- if let ExprKind::Closure(_, decl, eid, _, _) = expr.kind {
- let body = cx.tcx.hir().body(eid);
- let ex = &body.value;
-
- if ex.span.ctxt() != expr.span.ctxt() {
- if decl.inputs.is_empty() {
- if let Some(VecArgs::Vec(&[])) = higher::VecArgs::hir(cx, ex) {
+ let body = match expr.kind {
+ ExprKind::Closure(_, _, id, _, _) => cx.tcx.hir().body(id),
+ _ => return,
+ };
+ if body.value.span.from_expansion() {
+ if body.params.is_empty() {
+ if let Some(VecArgs::Vec(&[])) = higher::VecArgs::hir(cx, &body.value) {
// replace `|| vec![]` with `Vec::new`
span_lint_and_sugg(
cx,
return;
}
- if_chain!(
- if let ExprKind::Call(caller, args) = ex.kind;
-
- if let ExprKind::Path(_) = caller.kind;
-
- // Not the same number of arguments, there is no way the closure is the same as the function return;
- if args.len() == decl.inputs.len();
-
- // Are the expression or the arguments type-adjusted? Then we need the closure
- if !(is_adjusted(cx, ex) || args.iter().any(|arg| is_adjusted(cx, arg)));
-
- let fn_ty = cx.typeck_results().expr_ty(caller);
-
- if matches!(fn_ty.kind(), ty::FnDef(_, _) | ty::FnPtr(_) | ty::Closure(_, _));
-
- if !type_is_unsafe_function(cx, fn_ty);
-
- if compare_inputs(&mut iter_input_pats(decl, body), &mut args.iter());
+ let closure_ty = cx.typeck_results().expr_ty(expr);
+ if_chain!(
+ if let ExprKind::Call(callee, args) = body.value.kind;
+ if let ExprKind::Path(_) = callee.kind;
+ if check_inputs(cx, body.params, args);
+ let callee_ty = cx.typeck_results().expr_ty_adjusted(callee);
+ let call_ty = cx.typeck_results().type_dependent_def_id(body.value.hir_id)
+ .map_or(callee_ty, |id| cx.tcx.type_of(id));
+ if check_sig(cx, closure_ty, call_ty);
+ let substs = cx.typeck_results().node_substs(callee.hir_id);
+ // This fixes some false positives that I don't entirely understand
+ if substs.is_empty() || !cx.typeck_results().expr_ty(expr).has_late_bound_regions();
+ // A type param function ref like `T::f` is not 'static, however
+ // it is if cast like `T::f as fn()`. This seems like a rustc bug.
+ if !substs.types().any(|t| matches!(t.kind(), ty::Param(_)));
then {
span_lint_and_then(cx, REDUNDANT_CLOSURE, expr.span, "redundant closure", |diag| {
- if let Some(mut snippet) = snippet_opt(cx, caller.span) {
+ if let Some(mut snippet) = snippet_opt(cx, callee.span) {
if_chain! {
- if let ty::Closure(_, substs) = fn_ty.kind();
+ if let ty::Closure(_, substs) = callee_ty.peel_refs().kind();
if let ClosureKind::FnMut = substs.as_closure().kind();
- if UsedAfterExprVisitor::is_found(cx, caller)
- || get_enclosing_loop_or_closure(cx.tcx, expr).is_some();
+ if get_enclosing_loop_or_closure(cx.tcx, expr).is_some()
+ || UsedAfterExprVisitor::is_found(cx, callee);
then {
// Mutable closure is used after current expr; we cannot consume it.
);
if_chain!(
- if let ExprKind::MethodCall(path, _, args, _) = ex.kind;
-
- // Not the same number of arguments, there is no way the closure is the same as the function return;
- if args.len() == decl.inputs.len();
-
- // Are the expression or the arguments type-adjusted? Then we need the closure
- if !(is_adjusted(cx, ex) || args.iter().skip(1).any(|arg| is_adjusted(cx, arg)));
-
- let method_def_id = cx.typeck_results().type_dependent_def_id(ex.hir_id).unwrap();
- if !type_is_unsafe_function(cx, cx.tcx.type_of(method_def_id));
-
- if compare_inputs(&mut iter_input_pats(decl, body), &mut args.iter());
-
- if let Some(name) = get_ufcs_type_name(cx, method_def_id, &args[0]);
-
+ if let ExprKind::MethodCall(path, _, args, _) = body.value.kind;
+ if check_inputs(cx, body.params, args);
+ let method_def_id = cx.typeck_results().type_dependent_def_id(body.value.hir_id).unwrap();
+ let substs = cx.typeck_results().node_substs(body.value.hir_id);
+ let call_ty = cx.tcx.type_of(method_def_id).subst(cx.tcx, substs);
+ if check_sig(cx, closure_ty, call_ty);
then {
- span_lint_and_sugg(
- cx,
- REDUNDANT_CLOSURE_FOR_METHOD_CALLS,
- expr.span,
- "redundant closure",
- "replace the closure with the method itself",
- format!("{}::{}", name, path.ident.name),
- Applicability::MachineApplicable,
- );
+ span_lint_and_then(cx, REDUNDANT_CLOSURE_FOR_METHOD_CALLS, expr.span, "redundant closure", |diag| {
+ let name = get_ufcs_type_name(cx, method_def_id);
+ diag.span_suggestion(
+ expr.span,
+ "replace the closure with the method itself",
+ format!("{}::{}", name, path.ident.name),
+ Applicability::MachineApplicable,
+ );
+ })
}
);
}
}
-/// Tries to determine the type for universal function call to be used instead of the closure
-fn get_ufcs_type_name(cx: &LateContext<'_>, method_def_id: def_id::DefId, self_arg: &Expr<'_>) -> Option<String> {
- let expected_type_of_self = &cx.tcx.fn_sig(method_def_id).inputs_and_output().skip_binder()[0];
- let actual_type_of_self = &cx.typeck_results().node_type(self_arg.hir_id);
-
- if let Some(trait_id) = cx.tcx.trait_of_item(method_def_id) {
- if match_borrow_depth(expected_type_of_self, actual_type_of_self)
- && implements_trait(cx, actual_type_of_self, trait_id, &[])
- {
- return Some(cx.tcx.def_path_str(trait_id));
- }
+fn check_inputs(cx: &LateContext<'_>, params: &[Param<'_>], call_args: &[Expr<'_>]) -> bool {
+ if params.len() != call_args.len() {
+ return false;
}
-
- cx.tcx.impl_of_method(method_def_id).and_then(|_| {
- //a type may implicitly implement other type's methods (e.g. Deref)
- if match_types(expected_type_of_self, actual_type_of_self) {
- Some(get_type_name(cx, actual_type_of_self))
- } else {
- None
+ std::iter::zip(params, call_args).all(|(param, arg)| {
+ match param.pat.kind {
+ PatKind::Binding(_, id, ..) if path_to_local_id(arg, id) => {},
+ _ => return false,
+ }
+ match *cx.typeck_results().expr_adjustments(arg) {
+ [] => true,
+ [Adjustment {
+ kind: Adjust::Deref(None),
+ ..
+ }, Adjustment {
+ kind: Adjust::Borrow(AutoBorrow::Ref(_, mu2)),
+ ..
+ }] => {
+ // re-borrow with the same mutability is allowed
+ let ty = cx.typeck_results().expr_ty(arg);
+ matches!(*ty.kind(), ty::Ref(.., mu1) if mu1 == mu2.into())
+ },
+ _ => false,
}
})
}
-fn match_borrow_depth(lhs: Ty<'_>, rhs: Ty<'_>) -> bool {
- match (&lhs.kind(), &rhs.kind()) {
- (ty::Ref(_, t1, mut1), ty::Ref(_, t2, mut2)) => mut1 == mut2 && match_borrow_depth(t1, t2),
- (l, r) => !matches!((l, r), (ty::Ref(_, _, _), _) | (_, ty::Ref(_, _, _))),
- }
-}
-
-fn match_types(lhs: Ty<'_>, rhs: Ty<'_>) -> bool {
- match (&lhs.kind(), &rhs.kind()) {
- (ty::Bool, ty::Bool)
- | (ty::Char, ty::Char)
- | (ty::Int(_), ty::Int(_))
- | (ty::Uint(_), ty::Uint(_))
- | (ty::Str, ty::Str) => true,
- (ty::Ref(_, t1, mut1), ty::Ref(_, t2, mut2)) => mut1 == mut2 && match_types(t1, t2),
- (ty::Array(t1, _), ty::Array(t2, _)) | (ty::Slice(t1), ty::Slice(t2)) => match_types(t1, t2),
- (ty::Adt(def1, _), ty::Adt(def2, _)) => def1 == def2,
- (_, _) => false,
+fn check_sig<'tcx>(cx: &LateContext<'tcx>, closure_ty: Ty<'tcx>, call_ty: Ty<'tcx>) -> bool {
+ let call_sig = call_ty.fn_sig(cx.tcx);
+ if call_sig.unsafety() == Unsafety::Unsafe {
+ return false;
}
-}
-
-fn get_type_name(cx: &LateContext<'_>, ty: Ty<'_>) -> String {
- match ty.kind() {
- ty::Adt(t, _) => cx.tcx.def_path_str(t.did),
- ty::Ref(_, r, _) => get_type_name(cx, r),
- _ => ty.to_string(),
+ if !closure_ty.has_late_bound_regions() {
+ return true;
}
+ let substs = match closure_ty.kind() {
+ ty::Closure(_, substs) => substs,
+ _ => return false,
+ };
+ let closure_sig = cx.tcx.signature_unclosure(substs.as_closure().sig(), Unsafety::Normal);
+ cx.tcx.erase_late_bound_regions(closure_sig) == cx.tcx.erase_late_bound_regions(call_sig)
}
-fn compare_inputs(
- closure_inputs: &mut dyn Iterator<Item = &Param<'_>>,
- call_args: &mut dyn Iterator<Item = &Expr<'_>>,
-) -> bool {
- for (closure_input, function_arg) in closure_inputs.zip(call_args) {
- if let PatKind::Binding(_, _, ident, _) = closure_input.pat.kind {
- // XXXManishearth Should I be checking the binding mode here?
- if let ExprKind::Path(QPath::Resolved(None, p)) = function_arg.kind {
- if p.segments.len() != 1 {
- // If it's a proper path, it can't be a local variable
- return false;
- }
- if p.segments[0].ident.name != ident.name {
- // The two idents should be the same
- return false;
- }
- } else {
- return false;
+fn get_ufcs_type_name(cx: &LateContext<'_>, method_def_id: DefId) -> String {
+ match cx.tcx.associated_item(method_def_id).container {
+ ty::TraitContainer(def_id) => cx.tcx.def_path_str(def_id),
+ ty::ImplContainer(def_id) => {
+ let ty = cx.tcx.type_of(def_id);
+ match ty.kind() {
+ ty::Adt(adt, _) => cx.tcx.def_path_str(adt.did),
+ _ => ty.to_string(),
}
- } else {
- return false;
- }
+ },
}
- true
}
Applicability::MachineApplicable,
);
}
- } else if digits > max as usize && sym_str != float_str {
+ } else if digits > max as usize && float_str.len() < sym_str.len() {
span_lint_and_sugg(
cx,
EXCESSIVE_PRECISION,
ty::Str => true,
_ => false,
};
- if format_args.args.iter().all(|e| is_display_arg(e));
- if format_args.fmt_expr.map_or(true, |e| check_unformatted(e));
+ if format_args.args.iter().all(is_display_arg);
+ if format_args.fmt_expr.map_or(true, check_unformatted);
then {
let is_new_string = match value.kind {
ExprKind::Binary(..) => true,
}
fn check_missing_else(cx: &EarlyContext<'_>, first: &Expr, second: &Expr) {
- if !differing_macro_contexts(first.span, second.span)
- && !first.span.from_expansion()
- && is_if(first)
- && (is_block(second) || is_if(second))
- {
- // where the else would be
- let else_span = first.span.between(second.span);
+ if_chain! {
+ if !differing_macro_contexts(first.span, second.span);
+ if !first.span.from_expansion();
+ if let ExprKind::If(cond_expr, ..) = &first.kind;
+ if is_block(second) || is_if(second);
- if let Some(else_snippet) = snippet_opt(cx, else_span) {
- if !else_snippet.contains('\n') {
- let (looks_like, next_thing) = if is_if(second) {
- ("an `else if`", "the second `if`")
- } else {
- ("an `else {..}`", "the next block")
- };
+ // Proc-macros can give weird spans. Make sure this is actually an `if`.
+ if let Some(if_snip) = snippet_opt(cx, first.span.until(cond_expr.span));
+ if if_snip.starts_with("if");
- span_lint_and_note(
- cx,
- SUSPICIOUS_ELSE_FORMATTING,
- else_span,
- &format!("this looks like {} but the `else` is missing", looks_like),
- None,
- &format!(
- "to remove this lint, add the missing `else` or add a new line before {}",
- next_thing,
- ),
- );
- }
+ // If there is a line break between the two expressions, don't lint.
+ // If there is a non-whitespace character, this span came from a proc-macro.
+ let else_span = first.span.between(second.span);
+ if let Some(else_snippet) = snippet_opt(cx, else_span);
+ if !else_snippet.chars().any(|c| c == '\n' || !c.is_whitespace());
+ then {
+ let (looks_like, next_thing) = if is_if(second) {
+ ("an `else if`", "the second `if`")
+ } else {
+ ("an `else {..}`", "the next block")
+ };
+
+ span_lint_and_note(
+ cx,
+ SUSPICIOUS_ELSE_FORMATTING,
+ else_span,
+ &format!("this looks like {} but the `else` is missing", looks_like),
+ None,
+ &format!(
+ "to remove this lint, add the missing `else` or add a new line before {}",
+ next_thing,
+ ),
+ );
}
}
}
+++ /dev/null
-use clippy_utils::diagnostics::span_lint_and_sugg;
-use clippy_utils::higher;
-use clippy_utils::method_chain_args;
-use clippy_utils::source::snippet_with_applicability;
-use clippy_utils::ty::is_type_diagnostic_item;
-use if_chain::if_chain;
-use rustc_errors::Applicability;
-use rustc_hir::{Expr, ExprKind, PatKind, QPath};
-use rustc_lint::{LateContext, LateLintPass};
-use rustc_session::{declare_lint_pass, declare_tool_lint};
-use rustc_span::sym;
-
-declare_clippy_lint! {
- /// ### What it does
- ///* Checks for unnecessary `ok()` in if let.
- ///
- /// ### Why is this bad?
- /// Calling `ok()` in if let is unnecessary, instead match
- /// on `Ok(pat)`
- ///
- /// ### Example
- /// ```ignore
- /// for i in iter {
- /// if let Some(value) = i.parse().ok() {
- /// vec.push(value)
- /// }
- /// }
- /// ```
- /// Could be written:
- ///
- /// ```ignore
- /// for i in iter {
- /// if let Ok(value) = i.parse() {
- /// vec.push(value)
- /// }
- /// }
- /// ```
- pub IF_LET_SOME_RESULT,
- style,
- "usage of `ok()` in `if let Some(pat)` statements is unnecessary, match on `Ok(pat)` instead"
-}
-
-declare_lint_pass!(OkIfLet => [IF_LET_SOME_RESULT]);
-
-impl<'tcx> LateLintPass<'tcx> for OkIfLet {
- fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
- if_chain! { //begin checking variables
- if let Some(higher::IfLet { let_pat, let_expr, .. }) = higher::IfLet::hir(cx, expr);
- if let ExprKind::MethodCall(_, ok_span, [ref result_types_0, ..], _) = let_expr.kind; //check is expr.ok() has type Result<T,E>.ok(, _)
- if let PatKind::TupleStruct(QPath::Resolved(_, x), y, _) = let_pat.kind; //get operation
- if method_chain_args(let_expr, &["ok"]).is_some(); //test to see if using ok() methoduse std::marker::Sized;
- if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(result_types_0), sym::result_type);
- if rustc_hir_pretty::to_string(rustc_hir_pretty::NO_ANN, |s| s.print_path(x, false)) == "Some";
-
- then {
- let mut applicability = Applicability::MachineApplicable;
- let some_expr_string = snippet_with_applicability(cx, y[0].span, "", &mut applicability);
- let trimmed_ok = snippet_with_applicability(cx, let_expr.span.until(ok_span), "", &mut applicability);
- let sugg = format!(
- "if let Ok({}) = {}",
- some_expr_string,
- trimmed_ok.trim().trim_end_matches('.'),
- );
- span_lint_and_sugg(
- cx,
- IF_LET_SOME_RESULT,
- expr.span.with_hi(let_expr.span.hi()),
- "matching on `Some` with `ok()` is redundant",
- &format!("consider matching on `Ok({})` and removing the call to `ok` instead", some_expr_string),
- sugg,
- applicability,
- );
- }
- }
- }
-}
--- /dev/null
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::higher::PanicExpn;
+use clippy_utils::is_expn_of;
+use clippy_utils::source::snippet_with_applicability;
+use rustc_errors::Applicability;
+use rustc_hir::{Block, Expr, ExprKind, StmtKind, UnOp};
+use rustc_lint::{LateContext, LateLintPass};
+use rustc_session::{declare_lint_pass, declare_tool_lint};
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Detects `if`-then-`panic!` that can be replaced with `assert!`.
+ ///
+ /// ### Why is this bad?
+ /// `assert!` is simpler than `if`-then-`panic!`.
+ ///
+ /// ### Example
+ /// ```rust
+ /// let sad_people: Vec<&str> = vec![];
+ /// if !sad_people.is_empty() {
+ /// panic!("there are sad people: {:?}", sad_people);
+ /// }
+ /// ```
+ /// Use instead:
+ /// ```rust
+ /// let sad_people: Vec<&str> = vec![];
+ /// assert!(sad_people.is_empty(), "there are sad people: {:?}", sad_people);
+ /// ```
+ pub IF_THEN_PANIC,
+ style,
+ "`panic!` and only a `panic!` in `if`-then statement"
+}
+
+declare_lint_pass!(IfThenPanic => [IF_THEN_PANIC]);
+
+impl LateLintPass<'_> for IfThenPanic {
+ fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) {
+ if_chain! {
+ if let Expr {
+ kind: ExprKind:: If(cond, Expr {
+ kind: ExprKind::Block(
+ Block {
+ stmts: [stmt],
+ ..
+ },
+ _),
+ ..
+ }, None),
+ ..
+ } = &expr;
+ if is_expn_of(stmt.span, "panic").is_some();
+ if !matches!(cond.kind, ExprKind::Let(_, _, _));
+ if let StmtKind::Semi(semi) = stmt.kind;
+ if !cx.tcx.sess.source_map().is_multiline(cond.span);
+
+ then {
+ let span = if let Some(panic_expn) = PanicExpn::parse(semi) {
+ match *panic_expn.format_args.value_args {
+ [] => panic_expn.format_args.format_string_span,
+ [.., last] => panic_expn.format_args.format_string_span.to(last.span),
+ }
+ } else {
+ if_chain! {
+ if let ExprKind::Block(block, _) = semi.kind;
+ if let Some(init) = block.expr;
+ if let ExprKind::Call(_, [format_args]) = init.kind;
+
+ then {
+ format_args.span
+ } else {
+ return
+ }
+ }
+ };
+ let mut applicability = Applicability::MachineApplicable;
+ let sugg = snippet_with_applicability(cx, span, "..", &mut applicability);
+
+ let cond_sugg =
+ if let ExprKind::DropTemps(Expr{kind: ExprKind::Unary(UnOp::Not, not_expr), ..}) = cond.kind {
+ snippet_with_applicability(cx, not_expr.span, "..", &mut applicability).to_string()
+ } else {
+ format!("!{}", snippet_with_applicability(cx, cond.span, "..", &mut applicability))
+ };
+
+ span_lint_and_sugg(
+ cx,
+ IF_THEN_PANIC,
+ expr.span,
+ "only a `panic!` in `if`-then statement",
+ "try",
+ format!("assert!({}, {});", cond_sugg, sugg),
+ Applicability::MachineApplicable,
+ );
+ }
+ }
+ }
+}
--- /dev/null
+use clippy_utils::{diagnostics::span_lint, return_ty, ty::implements_trait};
+use rustc_hir::{ImplItem, ImplItemKind};
+use rustc_lint::{LateContext, LateLintPass};
+use rustc_session::{declare_lint_pass, declare_tool_lint};
+use rustc_span::symbol::kw;
+use rustc_span::symbol::sym;
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Detects methods named `iter` or `iter_mut` that do not have a return type that implements `Iterator`.
+ ///
+ /// ### Why is this bad?
+ /// Methods named `iter` or `iter_mut` conventionally return an `Iterator`.
+ ///
+ /// ### Example
+ /// ```rust
+ /// // `String` does not implement `Iterator`
+ /// struct Data {}
+ /// impl Data {
+ /// fn iter(&self) -> String {
+ /// todo!()
+ /// }
+ /// }
+ /// ```
+ /// Use instead:
+ /// ```rust
+ /// use std::str::Chars;
+ /// struct Data {}
+ /// impl Data {
+ /// fn iter(&self) -> Chars<'static> {
+ /// todo!()
+ /// }
+ /// }
+ /// ```
+ pub ITER_NOT_RETURNING_ITERATOR,
+ pedantic,
+ "methods named `iter` or `iter_mut` that do not return an `Iterator`"
+}
+
+declare_lint_pass!(IterNotReturningIterator => [ITER_NOT_RETURNING_ITERATOR]);
+
+impl LateLintPass<'_> for IterNotReturningIterator {
+ fn check_impl_item(&mut self, cx: &LateContext<'tcx>, impl_item: &'tcx ImplItem<'tcx>) {
+ let name: &str = &impl_item.ident.name.as_str();
+ if_chain! {
+ if let ImplItemKind::Fn(fn_sig, _) = &impl_item.kind;
+ let ret_ty = return_ty(cx, impl_item.hir_id());
+ if matches!(name, "iter" | "iter_mut");
+ if let [param] = cx.tcx.fn_arg_names(impl_item.def_id);
+ if param.name == kw::SelfLower;
+ if let Some(iter_trait_id) = cx.tcx.get_diagnostic_item(sym::Iterator);
+ if !implements_trait(cx, ret_ty, iter_trait_id, &[]);
+
+ then {
+ span_lint(
+ cx,
+ ITER_NOT_RETURNING_ITERATOR,
+ fn_sig.span,
+ &format!("this method is named `{}` but its return type does not implement `Iterator`", name),
+ );
+ }
+ }
+ }
+}
mod get_last_with_len;
mod identity_op;
mod if_let_mutex;
-mod if_let_some_result;
mod if_not_else;
+mod if_then_panic;
mod if_then_some_else_none;
mod implicit_hasher;
mod implicit_return;
mod integer_division;
mod invalid_upcast_comparisons;
mod items_after_statements;
+mod iter_not_returning_iterator;
mod large_const_arrays;
mod large_enum_variant;
mod large_stack_arrays;
mod map_err_ignore;
mod map_unit_fn;
mod match_on_vec_items;
+mod match_result_ok;
mod matches;
mod mem_discriminant;
mod mem_forget;
mod regex;
mod repeat_once;
mod returns;
+mod same_name_method;
mod self_assignment;
mod self_named_constructors;
mod semicolon_if_nothing_returned;
get_last_with_len::GET_LAST_WITH_LEN,
identity_op::IDENTITY_OP,
if_let_mutex::IF_LET_MUTEX,
- if_let_some_result::IF_LET_SOME_RESULT,
if_not_else::IF_NOT_ELSE,
+ if_then_panic::IF_THEN_PANIC,
if_then_some_else_none::IF_THEN_SOME_ELSE_NONE,
implicit_hasher::IMPLICIT_HASHER,
implicit_return::IMPLICIT_RETURN,
integer_division::INTEGER_DIVISION,
invalid_upcast_comparisons::INVALID_UPCAST_COMPARISONS,
items_after_statements::ITEMS_AFTER_STATEMENTS,
+ iter_not_returning_iterator::ITER_NOT_RETURNING_ITERATOR,
large_const_arrays::LARGE_CONST_ARRAYS,
large_enum_variant::LARGE_ENUM_VARIANT,
large_stack_arrays::LARGE_STACK_ARRAYS,
map_unit_fn::OPTION_MAP_UNIT_FN,
map_unit_fn::RESULT_MAP_UNIT_FN,
match_on_vec_items::MATCH_ON_VEC_ITEMS,
+ match_result_ok::MATCH_RESULT_OK,
matches::INFALLIBLE_DESTRUCTURING_MATCH,
matches::MATCH_AS_REF,
matches::MATCH_BOOL,
repeat_once::REPEAT_ONCE,
returns::LET_AND_RETURN,
returns::NEEDLESS_RETURN,
+ same_name_method::SAME_NAME_METHOD,
self_assignment::SELF_ASSIGNMENT,
self_named_constructors::SELF_NAMED_CONSTRUCTORS,
semicolon_if_nothing_returned::SEMICOLON_IF_NOTHING_RETURNED,
transmuting_null::TRANSMUTING_NULL,
try_err::TRY_ERR,
types::BORROWED_BOX,
- types::BOX_VEC,
+ types::BOX_COLLECTION,
types::LINKEDLIST,
types::OPTION_OPTION,
types::RC_BUFFER,
LintId::of(panic_unimplemented::UNIMPLEMENTED),
LintId::of(panic_unimplemented::UNREACHABLE),
LintId::of(pattern_type_mismatch::PATTERN_TYPE_MISMATCH),
+ LintId::of(same_name_method::SAME_NAME_METHOD),
LintId::of(shadow::SHADOW_REUSE),
LintId::of(shadow::SHADOW_SAME),
LintId::of(strings::STRING_ADD),
LintId::of(infinite_iter::MAYBE_INFINITE_ITER),
LintId::of(invalid_upcast_comparisons::INVALID_UPCAST_COMPARISONS),
LintId::of(items_after_statements::ITEMS_AFTER_STATEMENTS),
+ LintId::of(iter_not_returning_iterator::ITER_NOT_RETURNING_ITERATOR),
LintId::of(large_stack_arrays::LARGE_STACK_ARRAYS),
LintId::of(let_underscore::LET_UNDERSCORE_DROP),
LintId::of(literal_representation::LARGE_DIGIT_GROUPS),
LintId::of(methods::INEFFICIENT_TO_STRING),
LintId::of(methods::MAP_FLATTEN),
LintId::of(methods::MAP_UNWRAP_OR),
+ LintId::of(misc::FLOAT_CMP),
LintId::of(misc::USED_UNDERSCORE_BINDING),
LintId::of(misc_early::UNSEPARATED_LITERAL_SUFFIX),
LintId::of(mut_mut::MUT_MUT),
LintId::of(needless_continue::NEEDLESS_CONTINUE),
LintId::of(needless_for_each::NEEDLESS_FOR_EACH),
LintId::of(needless_pass_by_value::NEEDLESS_PASS_BY_VALUE),
+ LintId::of(non_expressive_names::MANY_SINGLE_CHAR_NAMES),
LintId::of(non_expressive_names::SIMILAR_NAMES),
LintId::of(pass_by_ref_or_value::LARGE_TYPES_PASSED_BY_VALUE),
LintId::of(pass_by_ref_or_value::TRIVIALLY_COPY_PASS_BY_REF),
LintId::of(get_last_with_len::GET_LAST_WITH_LEN),
LintId::of(identity_op::IDENTITY_OP),
LintId::of(if_let_mutex::IF_LET_MUTEX),
- LintId::of(if_let_some_result::IF_LET_SOME_RESULT),
+ LintId::of(if_then_panic::IF_THEN_PANIC),
LintId::of(indexing_slicing::OUT_OF_BOUNDS_INDEXING),
LintId::of(infinite_iter::INFINITE_ITER),
LintId::of(inherent_to_string::INHERENT_TO_STRING),
LintId::of(map_clone::MAP_CLONE),
LintId::of(map_unit_fn::OPTION_MAP_UNIT_FN),
LintId::of(map_unit_fn::RESULT_MAP_UNIT_FN),
+ LintId::of(match_result_ok::MATCH_RESULT_OK),
LintId::of(matches::INFALLIBLE_DESTRUCTURING_MATCH),
LintId::of(matches::MATCH_AS_REF),
LintId::of(matches::MATCH_LIKE_MATCHES_MACRO),
LintId::of(minmax::MIN_MAX),
LintId::of(misc::CMP_NAN),
LintId::of(misc::CMP_OWNED),
- LintId::of(misc::FLOAT_CMP),
LintId::of(misc::MODULO_ONE),
LintId::of(misc::SHORT_CIRCUIT_STATEMENT),
LintId::of(misc::TOPLEVEL_REF_ARG),
LintId::of(non_copy_const::BORROW_INTERIOR_MUTABLE_CONST),
LintId::of(non_copy_const::DECLARE_INTERIOR_MUTABLE_CONST),
LintId::of(non_expressive_names::JUST_UNDERSCORES_AND_DIGITS),
- LintId::of(non_expressive_names::MANY_SINGLE_CHAR_NAMES),
LintId::of(non_octal_unix_permissions::NON_OCTAL_UNIX_PERMISSIONS),
LintId::of(open_options::NONSENSICAL_OPEN_OPTIONS),
LintId::of(option_env_unwrap::OPTION_ENV_UNWRAP),
LintId::of(transmuting_null::TRANSMUTING_NULL),
LintId::of(try_err::TRY_ERR),
LintId::of(types::BORROWED_BOX),
- LintId::of(types::BOX_VEC),
+ LintId::of(types::BOX_COLLECTION),
LintId::of(types::REDUNDANT_ALLOCATION),
LintId::of(types::TYPE_COMPLEXITY),
LintId::of(types::VEC_BOX),
LintId::of(functions::DOUBLE_MUST_USE),
LintId::of(functions::MUST_USE_UNIT),
LintId::of(functions::RESULT_UNIT_ERR),
- LintId::of(if_let_some_result::IF_LET_SOME_RESULT),
+ LintId::of(if_then_panic::IF_THEN_PANIC),
LintId::of(inherent_to_string::INHERENT_TO_STRING),
LintId::of(len_zero::COMPARISON_TO_EMPTY),
LintId::of(len_zero::LEN_WITHOUT_IS_EMPTY),
LintId::of(manual_map::MANUAL_MAP),
LintId::of(manual_non_exhaustive::MANUAL_NON_EXHAUSTIVE),
LintId::of(map_clone::MAP_CLONE),
+ LintId::of(match_result_ok::MATCH_RESULT_OK),
LintId::of(matches::INFALLIBLE_DESTRUCTURING_MATCH),
LintId::of(matches::MATCH_LIKE_MATCHES_MACRO),
LintId::of(matches::MATCH_OVERLAPPING_ARM),
LintId::of(non_copy_const::BORROW_INTERIOR_MUTABLE_CONST),
LintId::of(non_copy_const::DECLARE_INTERIOR_MUTABLE_CONST),
LintId::of(non_expressive_names::JUST_UNDERSCORES_AND_DIGITS),
- LintId::of(non_expressive_names::MANY_SINGLE_CHAR_NAMES),
LintId::of(ptr::CMP_NULL),
LintId::of(ptr::PTR_ARG),
LintId::of(ptr_eq::PTR_EQ),
LintId::of(methods::ZST_OFFSET),
LintId::of(minmax::MIN_MAX),
LintId::of(misc::CMP_NAN),
- LintId::of(misc::FLOAT_CMP),
LintId::of(misc::MODULO_ONE),
LintId::of(non_octal_unix_permissions::NON_OCTAL_UNIX_PERMISSIONS),
LintId::of(open_options::NONSENSICAL_OPEN_OPTIONS),
LintId::of(redundant_clone::REDUNDANT_CLONE),
LintId::of(slow_vector_initialization::SLOW_VECTOR_INITIALIZATION),
LintId::of(stable_sort_primitive::STABLE_SORT_PRIMITIVE),
- LintId::of(types::BOX_VEC),
+ LintId::of(types::BOX_COLLECTION),
LintId::of(types::REDUNDANT_ALLOCATION),
LintId::of(vec::USELESS_VEC),
LintId::of(vec_init_then_push::VEC_INIT_THEN_PUSH),
store.register_early_pass(move || Box::new(unnested_or_patterns::UnnestedOrPatterns::new(msrv)));
store.register_late_pass(|| Box::new(size_of_in_element_count::SizeOfInElementCount));
+ store.register_late_pass(|| Box::new(same_name_method::SameNameMethod));
store.register_late_pass(|| Box::new(map_clone::MapClone));
store.register_late_pass(|| Box::new(map_err_ignore::MapErrIgnore));
store.register_late_pass(|| Box::new(shadow::Shadow));
store.register_late_pass(|| Box::new(empty_enum::EmptyEnum));
store.register_late_pass(|| Box::new(absurd_extreme_comparisons::AbsurdExtremeComparisons));
store.register_late_pass(|| Box::new(invalid_upcast_comparisons::InvalidUpcastComparisons));
- store.register_late_pass(|| Box::new(regex::Regex::default()));
+ store.register_late_pass(|| Box::new(regex::Regex));
store.register_late_pass(|| Box::new(copies::CopyAndPaste));
store.register_late_pass(|| Box::new(copy_iterator::CopyIterator));
store.register_late_pass(|| Box::new(format::UselessFormat));
store.register_late_pass(|| Box::new(missing_doc::MissingDoc::new()));
store.register_late_pass(|| Box::new(missing_inline::MissingInline));
store.register_late_pass(move || Box::new(exhaustive_items::ExhaustiveItems));
- store.register_late_pass(|| Box::new(if_let_some_result::OkIfLet));
+ store.register_late_pass(|| Box::new(match_result_ok::MatchResultOk));
store.register_late_pass(|| Box::new(partialeq_ne_impl::PartialEqNeImpl));
store.register_late_pass(|| Box::new(unused_io_amount::UnusedIoAmount));
let enum_variant_size_threshold = conf.enum_variant_size_threshold;
store.register_late_pass(|| Box::new(float_equality_without_abs::FloatEqualityWithoutAbs));
store.register_late_pass(|| Box::new(semicolon_if_nothing_returned::SemicolonIfNothingReturned));
store.register_late_pass(|| Box::new(async_yields_async::AsyncYieldsAsync));
- let disallowed_methods = conf.disallowed_methods.iter().cloned().collect::<FxHashSet<_>>();
- store.register_late_pass(move || Box::new(disallowed_method::DisallowedMethod::new(&disallowed_methods)));
+ let disallowed_methods = conf.disallowed_methods.clone();
+ store.register_late_pass(move || Box::new(disallowed_method::DisallowedMethod::new(disallowed_methods.clone())));
store.register_early_pass(|| Box::new(asm_syntax::InlineAsmX86AttSyntax));
store.register_early_pass(|| Box::new(asm_syntax::InlineAsmX86IntelSyntax));
store.register_late_pass(|| Box::new(undropped_manually_drops::UndroppedManuallyDrops));
store.register_late_pass(|| Box::new(strlen_on_c_strings::StrlenOnCStrings));
store.register_late_pass(move || Box::new(self_named_constructors::SelfNamedConstructors));
store.register_late_pass(move || Box::new(feature_name::FeatureName));
+ store.register_late_pass(move || Box::new(iter_not_returning_iterator::IterNotReturningIterator));
+ store.register_late_pass(move || Box::new(if_then_panic::IfThenPanic));
}
#[rustfmt::skip]
ls.register_renamed("clippy::cyclomatic_complexity", "clippy::cognitive_complexity");
ls.register_renamed("clippy::const_static_lifetime", "clippy::redundant_static_lifetimes");
ls.register_renamed("clippy::option_and_then_some", "clippy::bind_instead_of_map");
+ ls.register_renamed("clippy::box_vec", "clippy::box_collection");
ls.register_renamed("clippy::block_in_if_condition_expr", "clippy::blocks_in_if_conditions");
ls.register_renamed("clippy::block_in_if_condition_stmt", "clippy::blocks_in_if_conditions");
ls.register_renamed("clippy::option_map_unwrap_or", "clippy::map_unwrap_or");
ls.register_renamed("clippy::identity_conversion", "clippy::useless_conversion");
ls.register_renamed("clippy::zero_width_space", "clippy::invisible_characters");
ls.register_renamed("clippy::single_char_push_str", "clippy::single_char_add_str");
+ ls.register_renamed("clippy::if_let_some_result", "clippy::match_result_ok");
// uplifted lints
ls.register_renamed("clippy::invalid_ref", "invalid_value");
use rustc_span::sym;
/// Checks for the `FOR_KV_MAP` lint.
-pub(super) fn check<'tcx>(
- cx: &LateContext<'tcx>,
- pat: &'tcx Pat<'_>,
- arg: &'tcx Expr<'_>,
- body: &'tcx Expr<'_>,
-) {
+pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, pat: &'tcx Pat<'_>, arg: &'tcx Expr<'_>, body: &'tcx Expr<'_>) {
let pat_span = pat.span;
if let PatKind::Tuple(pat, _) = pat.kind {
fn is_simple_break_expr(expr: &Expr<'_>) -> bool {
match expr.kind {
ExprKind::Break(dest, ref passed_expr) if dest.label.is_none() && passed_expr.is_none() => true,
- ExprKind::Block(b, _) => extract_first_expr(b).map_or(false, |subexpr| is_simple_break_expr(subexpr)),
+ ExprKind::Block(b, _) => extract_first_expr(b).map_or(false, is_simple_break_expr),
_ => false,
}
}
use if_chain::if_chain;
use rustc_errors::Applicability;
use rustc_hir::intravisit::{walk_expr, ErasedMap, NestedVisitorMap, Visitor};
-use rustc_hir::{def::Res, Expr, ExprKind, HirId, Local, Mutability, PatKind, QPath, UnOp};
+use rustc_hir::{def::Res, Expr, ExprKind, HirId, Local, PatKind, QPath, UnOp};
use rustc_lint::LateContext;
use rustc_span::{symbol::sym, Span, Symbol};
// If the iterator is a field or the iterator is accessed after the loop is complete it needs to be
// borrowed mutably. TODO: If the struct can be partially moved from and the struct isn't used
// afterwards a mutable borrow of a field isn't necessary.
- let ref_mut = if !iter_expr.fields.is_empty() || needs_mutable_borrow(cx, &iter_expr, loop_expr) {
- if cx.typeck_results().node_type(iter_expr.hir_id).ref_mutability() == Some(Mutability::Mut) {
- // Reborrow for mutable references. It may not be possible to get a mutable reference here.
- "&mut *"
- } else {
- "&mut "
- }
+ let by_ref = if !iter_expr.fields.is_empty() || needs_mutable_borrow(cx, &iter_expr, loop_expr) {
+ ".by_ref()"
} else {
""
};
expr.span.with_hi(scrutinee_expr.span.hi()),
"this loop could be written as a `for` loop",
"try",
- format!("for {} in {}{}", loop_var, ref_mut, iterator),
+ format!("for {} in {}{}", loop_var, iterator, by_ref),
applicability,
);
}
struct IterExpr {
/// The span of the whole expression, not just the path and fields stored here.
span: Span,
- /// The HIR id of the whole expression, not just the path and fields stored here.
- hir_id: HirId,
/// The fields used, in order of child to parent.
fields: Vec<Symbol>,
/// The path being used.
/// the expression might have side effects.
fn try_parse_iter_expr(cx: &LateContext<'_>, mut e: &Expr<'_>) -> Option<IterExpr> {
let span = e.span;
- let hir_id = e.hir_id;
let mut fields = Vec::new();
loop {
match e.kind {
ExprKind::Path(ref path) => {
break Some(IterExpr {
span,
- hir_id,
fields,
path: cx.qpath_res(path, e.hir_id),
});
fn push_unique_macro(&mut self, cx: &LateContext<'_>, span: Span) {
let call_site = span.source_callsite();
let name = snippet(cx, cx.sess().source_map().span_until_char(call_site, '!'), "_");
- if let Some(_callee) = span.source_callee() {
- if !self.collected.contains(&call_site) {
- let name = if name.contains("::") {
- name.split("::").last().unwrap().to_string()
- } else {
- name.to_string()
- };
+ if span.source_callee().is_some() && !self.collected.contains(&call_site) {
+ let name = if name.contains("::") {
+ name.split("::").last().unwrap().to_string()
+ } else {
+ name.to_string()
+ };
- self.mac_refs.push(MacroRefData::new(name));
- self.collected.insert(call_site);
- }
+ self.mac_refs.push(MacroRefData::new(name));
+ self.collected.insert(call_site);
}
}
fn push_unique_macro_pat_ty(&mut self, cx: &LateContext<'_>, span: Span) {
let call_site = span.source_callsite();
let name = snippet(cx, cx.sess().source_map().span_until_char(call_site, '!'), "_");
- if let Some(_callee) = span.source_callee() {
- if !self.collected.contains(&call_site) {
- self.mac_refs
- .push(MacroRefData::new(name.to_string()));
- self.collected.insert(call_site);
- }
+ if span.source_callee().is_some() && !self.collected.contains(&call_site) {
+ self.mac_refs.push(MacroRefData::new(name.to_string()));
+ self.collected.insert(call_site);
}
}
}
--- /dev/null
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::higher;
+use clippy_utils::method_chain_args;
+use clippy_utils::source::snippet_with_applicability;
+use clippy_utils::ty::is_type_diagnostic_item;
+use if_chain::if_chain;
+use rustc_errors::Applicability;
+use rustc_hir::{Expr, ExprKind, PatKind, QPath};
+use rustc_lint::{LateContext, LateLintPass};
+use rustc_session::{declare_lint_pass, declare_tool_lint};
+use rustc_span::sym;
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for unnecessary `ok()` in `while let`.
+ ///
+ /// ### Why is this bad?
+ /// Calling `ok()` in `while let` is unnecessary, instead match
+ /// on `Ok(pat)`
+ ///
+ /// ### Example
+ /// ```ignore
+ /// while let Some(value) = iter.next().ok() {
+ /// vec.push(value)
+ /// }
+ ///
+ /// if let Some(valie) = iter.next().ok() {
+ /// vec.push(value)
+ /// }
+ /// ```
+ /// Use instead:
+ /// ```ignore
+ /// while let Ok(value) = iter.next() {
+ /// vec.push(value)
+ /// }
+ ///
+ /// if let Ok(value) = iter.next() {
+ /// vec.push_value)
+ /// }
+ /// ```
+ pub MATCH_RESULT_OK,
+ style,
+ "usage of `ok()` in `let Some(pat)` statements is unnecessary, match on `Ok(pat)` instead"
+}
+
+declare_lint_pass!(MatchResultOk => [MATCH_RESULT_OK]);
+
+impl<'tcx> LateLintPass<'tcx> for MatchResultOk {
+ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
+ let (let_pat, let_expr, ifwhile) =
+ if let Some(higher::IfLet { let_pat, let_expr, .. }) = higher::IfLet::hir(cx, expr) {
+ (let_pat, let_expr, "if")
+ } else if let Some(higher::WhileLet { let_pat, let_expr, .. }) = higher::WhileLet::hir(expr) {
+ (let_pat, let_expr, "while")
+ } else {
+ return;
+ };
+
+ if_chain! {
+ if let ExprKind::MethodCall(_, ok_span, [ref result_types_0, ..], _) = let_expr.kind; //check is expr.ok() has type Result<T,E>.ok(, _)
+ if let PatKind::TupleStruct(QPath::Resolved(_, x), y, _) = let_pat.kind; //get operation
+ if method_chain_args(let_expr, &["ok"]).is_some(); //test to see if using ok() methoduse std::marker::Sized;
+ if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(result_types_0), sym::result_type);
+ if rustc_hir_pretty::to_string(rustc_hir_pretty::NO_ANN, |s| s.print_path(x, false)) == "Some";
+
+ then {
+
+ let mut applicability = Applicability::MachineApplicable;
+ let some_expr_string = snippet_with_applicability(cx, y[0].span, "", &mut applicability);
+ let trimmed_ok = snippet_with_applicability(cx, let_expr.span.until(ok_span), "", &mut applicability);
+ let sugg = format!(
+ "{} let Ok({}) = {}",
+ ifwhile,
+ some_expr_string,
+ trimmed_ok.trim().trim_end_matches('.'),
+ );
+ span_lint_and_sugg(
+ cx,
+ MATCH_RESULT_OK,
+ expr.span.with_hi(let_expr.span.hi()),
+ "matching on `Some` with `ok()` is redundant",
+ &format!("consider matching on `Ok({})` and removing the call to `ok` instead", some_expr_string),
+ sugg,
+ applicability,
+ );
+ }
+ }
+ }
+}
/// ```rust
/// let x = 5;
/// match x {
- /// 1...10 => println!("1 ... 10"),
- /// 5...15 => println!("5 ... 15"),
+ /// 1..=10 => println!("1 ... 10"),
+ /// 5..=15 => println!("5 ... 15"),
/// _ => (),
/// }
/// ```
}
let ctxt = expr.span.ctxt();
- let usage = match parse_iter_usage(cx, ctxt, cx.tcx.hir().parent_iter(expr.hir_id)) {
+ let (method_name, msg, reverse) = if method_name == "splitn" {
+ ("split_once", "manual implementation of `split_once`", false)
+ } else {
+ ("rsplit_once", "manual implementation of `rsplit_once`", true)
+ };
+ let usage = match parse_iter_usage(cx, ctxt, cx.tcx.hir().parent_iter(expr.hir_id), reverse) {
Some(x) => x,
None => return,
};
- let (method_name, msg) = if method_name == "splitn" {
- ("split_once", "manual implementation of `split_once`")
- } else {
- ("rsplit_once", "manual implementation of `rsplit_once`")
- };
let mut app = Applicability::MachineApplicable;
let self_snip = snippet_with_context(cx, self_arg.span, ctxt, "..", &mut app).0;
let pat_snip = snippet_with_context(cx, pat_arg.span, ctxt, "..", &mut app).0;
- match usage.kind {
+ let sugg = match usage.kind {
IterUsageKind::NextTuple => {
- span_lint_and_sugg(
- cx,
- MANUAL_SPLIT_ONCE,
- usage.span,
- msg,
- "try this",
- format!("{}.{}({})", self_snip, method_name, pat_snip),
- app,
- );
+ format!("{}.{}({})", self_snip, method_name, pat_snip)
},
+ IterUsageKind::RNextTuple => format!("{}.{}({}).map(|(x, y)| (y, x))", self_snip, method_name, pat_snip),
IterUsageKind::Next => {
let self_deref = {
let adjust = cx.typeck_results().expr_adjustments(self_arg);
"*".repeat(adjust.len() - 2)
}
};
- let sugg = if usage.unwrap_kind.is_some() {
+ if usage.unwrap_kind.is_some() {
format!(
"{}.{}({}).map_or({}{}, |x| x.0)",
&self_snip, method_name, pat_snip, self_deref, &self_snip
"Some({}.{}({}).map_or({}{}, |x| x.0))",
&self_snip, method_name, pat_snip, self_deref, &self_snip
)
- };
-
- span_lint_and_sugg(cx, MANUAL_SPLIT_ONCE, usage.span, msg, "try this", sugg, app);
+ }
},
IterUsageKind::Second => {
let access_str = match usage.unwrap_kind {
Some(UnwrapKind::QuestionMark) => "?.1",
None => ".map(|x| x.1)",
};
- span_lint_and_sugg(
- cx,
- MANUAL_SPLIT_ONCE,
- usage.span,
- msg,
- "try this",
- format!("{}.{}({}){}", self_snip, method_name, pat_snip, access_str),
- app,
- );
+ format!("{}.{}({}){}", self_snip, method_name, pat_snip, access_str)
},
- }
+ };
+
+ span_lint_and_sugg(cx, MANUAL_SPLIT_ONCE, usage.span, msg, "try this", sugg, app);
}
enum IterUsageKind {
Next,
Second,
NextTuple,
+ RNextTuple,
}
enum UnwrapKind {
span: Span,
}
+#[allow(clippy::too_many_lines)]
fn parse_iter_usage(
cx: &LateContext<'tcx>,
ctxt: SyntaxContext,
mut iter: impl Iterator<Item = (HirId, Node<'tcx>)>,
+ reverse: bool,
) -> Option<IterUsage> {
let (kind, span) = match iter.next() {
Some((_, Node::Expr(e))) if e.span.ctxt() == ctxt => {
let iter_id = cx.tcx.get_diagnostic_item(sym::Iterator)?;
match (&*name.ident.as_str(), args) {
- ("next", []) if cx.tcx.trait_of_item(did) == Some(iter_id) => (IterUsageKind::Next, e.span),
+ ("next", []) if cx.tcx.trait_of_item(did) == Some(iter_id) => {
+ if reverse {
+ (IterUsageKind::Second, e.span)
+ } else {
+ (IterUsageKind::Next, e.span)
+ }
+ },
("next_tuple", []) => {
- if_chain! {
+ return if_chain! {
if match_def_path(cx, did, &paths::ITERTOOLS_NEXT_TUPLE);
if let ty::Adt(adt_def, subs) = cx.typeck_results().expr_ty(e).kind();
if cx.tcx.is_diagnostic_item(sym::option_type, adt_def.did);
if let ty::Tuple(subs) = subs.type_at(0).kind();
if subs.len() == 2;
then {
- return Some(IterUsage { kind: IterUsageKind::NextTuple, span: e.span, unwrap_kind: None });
+ Some(IterUsage {
+ kind: if reverse { IterUsageKind::RNextTuple } else { IterUsageKind::NextTuple },
+ span: e.span,
+ unwrap_kind: None
+ })
} else {
- return None;
+ None
}
- }
+ };
},
("nth" | "skip", [idx_expr]) if cx.tcx.trait_of_item(did) == Some(iter_id) => {
if let Some((Constant::Int(idx), _)) = constant(cx, cx.typeck_results(), idx_expr) {
}
}
};
- match idx {
+ match if reverse { idx ^ 1 } else { idx } {
0 => (IterUsageKind::Next, span),
1 => (IterUsageKind::Second, span),
_ => return None,
/// The method signature is controlled by the trait and often `&self` is required for all types that implement the trait
/// (see e.g. the `std::string::ToString` trait).
///
+ /// Clippy allows `Pin<&Self>` and `Pin<&mut Self>` if `&self` and `&mut self` is required.
+ ///
/// Please find more info here:
/// https://rust-lang.github.io/api-guidelines/naming.html#ad-hoc-conversions-follow-as_-to_-into_-conventions-c-conv
///
/// if (y - x).abs() > error_margin { }
/// ```
pub FLOAT_CMP,
- correctness,
+ pedantic,
"using `==` or `!=` on float values instead of comparing difference with an epsilon"
}
use rustc_hir as hir;
use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::ty::TypeFoldable;
-use rustc_middle::ty::{Adt, Array, RawPtr, Ref, Slice, Tuple, Ty, TypeAndMut};
+use rustc_middle::ty::{Adt, Array, Ref, Slice, Tuple, Ty};
use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::source_map::Span;
use rustc_span::symbol::sym;
/// so having types with interior mutability is a bad idea.
///
/// ### Known problems
- /// It's correct to use a struct, that contains interior mutability
- /// as a key, when its `Hash` implementation doesn't access any of the interior mutable types.
- /// However, this lint is unable to recognize this, so it causes a false positive in theses cases.
- /// The `bytes` crate is a great example of this.
+ ///
+ /// #### False Positives
+ /// It's correct to use a struct that contains interior mutability as a key, when its
+ /// implementation of `Hash` or `Ord` doesn't access any of the interior mutable types.
+ /// However, this lint is unable to recognize this, so it will often cause false positives in
+ /// theses cases. The `bytes` crate is a great example of this.
+ ///
+ /// #### False Negatives
+ /// For custom `struct`s/`enum`s, this lint is unable to check for interior mutability behind
+ /// indirection. For example, `struct BadKey<'a>(&'a Cell<usize>)` will be seen as immutable
+ /// and cause a false negative if its implementation of `Hash`/`Ord` accesses the `Cell`.
+ ///
+ /// This lint does check a few cases for indirection. Firstly, using some standard library
+ /// types (`Option`, `Result`, `Box`, `Rc`, `Arc`, `Vec`, `VecDeque`, `BTreeMap` and
+ /// `BTreeSet`) directly as keys (e.g. in `HashMap<Box<Cell<usize>>, ()>`) **will** trigger the
+ /// lint, because the impls of `Hash`/`Ord` for these types directly call `Hash`/`Ord` on their
+ /// contained type.
+ ///
+ /// Secondly, the implementations of `Hash` and `Ord` for raw pointers (`*const T` or `*mut T`)
+ /// apply only to the **address** of the contained value. Therefore, interior mutability
+ /// behind raw pointers (e.g. in `HashSet<*mut Cell<usize>>`) can't impact the value of `Hash`
+ /// or `Ord`, and therefore will not trigger this link. For more info, see issue
+ /// [#6745](https://github.com/rust-lang/rust-clippy/issues/6745).
///
/// ### Example
/// ```rust
fn check_ty<'tcx>(cx: &LateContext<'tcx>, span: Span, ty: Ty<'tcx>) {
let ty = ty.peel_refs();
if let Adt(def, substs) = ty.kind() {
- if [sym::hashmap_type, sym::BTreeMap, sym::hashset_type, sym::BTreeMap]
+ let is_keyed_type = [sym::hashmap_type, sym::BTreeMap, sym::hashset_type, sym::BTreeSet]
.iter()
- .any(|diag_item| cx.tcx.is_diagnostic_item(*diag_item, def.did))
- && is_mutable_type(cx, substs.type_at(0), span)
- {
+ .any(|diag_item| cx.tcx.is_diagnostic_item(*diag_item, def.did));
+ if is_keyed_type && is_interior_mutable_type(cx, substs.type_at(0), span) {
span_lint(cx, MUTABLE_KEY_TYPE, span, "mutable key type");
}
}
}
-fn is_mutable_type<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, span: Span) -> bool {
+/// Determines if a type contains interior mutability which would affect its implementation of
+/// [`Hash`] or [`Ord`].
+fn is_interior_mutable_type<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, span: Span) -> bool {
match *ty.kind() {
- RawPtr(TypeAndMut { ty: inner_ty, mutbl }) | Ref(_, inner_ty, mutbl) => {
- mutbl == hir::Mutability::Mut || is_mutable_type(cx, inner_ty, span)
- },
- Slice(inner_ty) => is_mutable_type(cx, inner_ty, span),
+ Ref(_, inner_ty, mutbl) => mutbl == hir::Mutability::Mut || is_interior_mutable_type(cx, inner_ty, span),
+ Slice(inner_ty) => is_interior_mutable_type(cx, inner_ty, span),
Array(inner_ty, size) => {
- size.try_eval_usize(cx.tcx, cx.param_env).map_or(true, |u| u != 0) && is_mutable_type(cx, inner_ty, span)
+ size.try_eval_usize(cx.tcx, cx.param_env).map_or(true, |u| u != 0)
+ && is_interior_mutable_type(cx, inner_ty, span)
},
- Tuple(..) => ty.tuple_fields().any(|ty| is_mutable_type(cx, ty, span)),
- Adt(..) => {
- !ty.has_escaping_bound_vars()
- && cx.tcx.layout_of(cx.param_env.and(ty)).is_ok()
- && !ty.is_freeze(cx.tcx.at(span), cx.param_env)
+ Tuple(..) => ty.tuple_fields().any(|ty| is_interior_mutable_type(cx, ty, span)),
+ Adt(def, substs) => {
+ // Special case for collections in `std` who's impl of `Hash` or `Ord` delegates to
+ // that of their type parameters. Note: we don't include `HashSet` and `HashMap`
+ // because they have no impl for `Hash` or `Ord`.
+ let is_std_collection = [
+ sym::option_type,
+ sym::result_type,
+ sym::LinkedList,
+ sym::vec_type,
+ sym::vecdeque_type,
+ sym::BTreeMap,
+ sym::BTreeSet,
+ sym::Rc,
+ sym::Arc,
+ ]
+ .iter()
+ .any(|diag_item| cx.tcx.is_diagnostic_item(*diag_item, def.did));
+ let is_box = Some(def.did) == cx.tcx.lang_items().owned_box();
+ if is_std_collection || is_box {
+ // The type is mutable if any of its type parameters are
+ substs.types().any(|ty| is_interior_mutable_type(cx, ty, span))
+ } else {
+ !ty.has_escaping_bound_vars()
+ && cx.tcx.layout_of(cx.param_env.and(ty)).is_ok()
+ && !ty.is_freeze(cx.tcx.at(span), cx.param_env)
+ }
},
_ => false,
}
if e.span.from_expansion() {
return;
}
- if let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, inner) = e.kind {
+ if let ExprKind::AddrOf(BorrowKind::Ref, mutability, inner) = e.kind {
if let ty::Ref(_, ty, _) = cx.typeck_results().expr_ty(inner).kind() {
for adj3 in cx.typeck_results().expr_adjustments(e).windows(3) {
if let [Adjustment {
..
}] = *adj3
{
+ let help_msg_ty = if matches!(mutability, Mutability::Not) {
+ format!("&{}", ty)
+ } else {
+ format!("&mut {}", ty)
+ };
+
span_lint_and_then(
cx,
NEEDLESS_BORROW,
e.span,
&format!(
- "this expression borrows a reference (`&{}`) that is immediately dereferenced \
+ "this expression borrows a reference (`{}`) that is immediately dereferenced \
by the compiler",
- ty
+ help_msg_ty
),
|diag| {
if let Some(snippet) = snippet_opt(cx, inner.span) {
/// let (a, b, c, d, e, f, g) = (...);
/// ```
pub MANY_SINGLE_CHAR_NAMES,
- style,
+ pedantic,
"too many single character bindings"
}
for (_, ref mutbl, ref argspan) in decl
.inputs
.iter()
- .filter_map(|ty| get_rptr_lm(ty))
+ .filter_map(get_rptr_lm)
.filter(|&(lt, _, _)| lt.name == out.name)
{
if *mutbl == Mutability::Mut {
use rustc_ast::ast::{LitKind, StrStyle};
use rustc_hir::{BorrowKind, Expr, ExprKind};
use rustc_lint::{LateContext, LateLintPass};
-use rustc_session::{declare_tool_lint, impl_lint_pass};
+use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::source_map::{BytePos, Span};
use std::convert::TryFrom;
"trivial regular expressions"
}
-#[derive(Clone, Default)]
-pub struct Regex {}
-
-impl_lint_pass!(Regex => [INVALID_REGEX, TRIVIAL_REGEX]);
+declare_lint_pass!(Regex => [INVALID_REGEX, TRIVIAL_REGEX]);
impl<'tcx> LateLintPass<'tcx> for Regex {
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
use rustc_middle::lint::in_external_macro;
use rustc_middle::ty::subst::GenericArgKind;
use rustc_session::{declare_lint_pass, declare_tool_lint};
+use rustc_span::hygiene::DesugaringKind;
use rustc_span::source_map::Span;
use rustc_span::sym;
check_block_return(cx, ifblock);
}
if let Some(else_clause) = else_clause_opt {
- check_final_expr(cx, else_clause, None, RetReplacement::Empty);
+ if expr.span.desugaring_kind() != Some(DesugaringKind::LetElse) {
+ check_final_expr(cx, else_clause, None, RetReplacement::Empty);
+ }
}
},
// a match expr, check all arms
--- /dev/null
+use clippy_utils::diagnostics::span_lint_and_then;
+use rustc_data_structures::fx::FxHashMap;
+use rustc_hir::def::{DefKind, Res};
+use rustc_hir::{Crate, Impl, ItemKind, Node, Path, QPath, TraitRef, TyKind};
+use rustc_lint::{LateContext, LateLintPass};
+use rustc_middle::ty::AssocKind;
+use rustc_session::{declare_lint_pass, declare_tool_lint};
+use rustc_span::symbol::Symbol;
+use rustc_span::Span;
+use std::collections::{BTreeMap, BTreeSet};
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// It lints if a struct has two method with same time:
+ /// one from a trait, another not from trait.
+ ///
+ /// ### Why is this bad?
+ /// Confusing.
+ ///
+ /// ### Example
+ /// ```rust
+ /// trait T {
+ /// fn foo(&self) {}
+ /// }
+ ///
+ /// struct S;
+ ///
+ /// impl T for S {
+ /// fn foo(&self) {}
+ /// }
+ ///
+ /// impl S {
+ /// fn foo(&self) {}
+ /// }
+ /// ```
+ pub SAME_NAME_METHOD,
+ restriction,
+ "two method with same name"
+}
+
+declare_lint_pass!(SameNameMethod => [SAME_NAME_METHOD]);
+
+struct ExistingName {
+ impl_methods: BTreeMap<Symbol, Span>,
+ trait_methods: BTreeMap<Symbol, Vec<Span>>,
+}
+
+impl<'tcx> LateLintPass<'tcx> for SameNameMethod {
+ fn check_crate_post(&mut self, cx: &LateContext<'tcx>, krate: &'tcx Crate<'tcx>) {
+ let mut map = FxHashMap::<Res, ExistingName>::default();
+
+ for item in krate.items() {
+ if let ItemKind::Impl(Impl {
+ items,
+ of_trait,
+ self_ty,
+ ..
+ }) = &item.kind
+ {
+ if let TyKind::Path(QPath::Resolved(_, Path { res, .. })) = self_ty.kind {
+ if !map.contains_key(res) {
+ map.insert(
+ *res,
+ ExistingName {
+ impl_methods: BTreeMap::new(),
+ trait_methods: BTreeMap::new(),
+ },
+ );
+ }
+ let existing_name = map.get_mut(res).unwrap();
+
+ match of_trait {
+ Some(trait_ref) => {
+ let mut methods_in_trait: BTreeSet<Symbol> = if_chain! {
+ if let Some(Node::TraitRef(TraitRef { path, .. })) =
+ cx.tcx.hir().find(trait_ref.hir_ref_id);
+ if let Res::Def(DefKind::Trait, did) = path.res;
+ then{
+ // FIXME: if
+ // `rustc_middle::ty::assoc::AssocItems::items` is public,
+ // we can iterate its keys instead of `in_definition_order`,
+ // which's more efficient
+ cx.tcx
+ .associated_items(did)
+ .in_definition_order()
+ .filter(|assoc_item| {
+ matches!(assoc_item.kind, AssocKind::Fn)
+ })
+ .map(|assoc_item| assoc_item.ident.name)
+ .collect()
+ }else{
+ BTreeSet::new()
+ }
+ };
+
+ let mut check_trait_method = |method_name: Symbol, trait_method_span: Span| {
+ if let Some(impl_span) = existing_name.impl_methods.get(&method_name) {
+ span_lint_and_then(
+ cx,
+ SAME_NAME_METHOD,
+ *impl_span,
+ "method's name is same to an existing method in a trait",
+ |diag| {
+ diag.span_note(
+ trait_method_span,
+ &format!("existing `{}` defined here", method_name),
+ );
+ },
+ );
+ }
+ if let Some(v) = existing_name.trait_methods.get_mut(&method_name) {
+ v.push(trait_method_span);
+ } else {
+ existing_name.trait_methods.insert(method_name, vec![trait_method_span]);
+ }
+ };
+
+ for impl_item_ref in (*items).iter().filter(|impl_item_ref| {
+ matches!(impl_item_ref.kind, rustc_hir::AssocItemKind::Fn { .. })
+ }) {
+ let method_name = impl_item_ref.ident.name;
+ methods_in_trait.remove(&method_name);
+ check_trait_method(method_name, impl_item_ref.span);
+ }
+
+ for method_name in methods_in_trait {
+ check_trait_method(method_name, item.span);
+ }
+ },
+ None => {
+ for impl_item_ref in (*items).iter().filter(|impl_item_ref| {
+ matches!(impl_item_ref.kind, rustc_hir::AssocItemKind::Fn { .. })
+ }) {
+ let method_name = impl_item_ref.ident.name;
+ let impl_span = impl_item_ref.span;
+ if let Some(trait_spans) = existing_name.trait_methods.get(&method_name) {
+ span_lint_and_then(
+ cx,
+ SAME_NAME_METHOD,
+ impl_span,
+ "method's name is same to an existing method in a trait",
+ |diag| {
+ // TODO should we `span_note` on every trait?
+ // iterate on trait_spans?
+ diag.span_note(
+ trait_spans[0],
+ &format!("existing `{}` defined here", method_name),
+ );
+ },
+ );
+ }
+ existing_name.impl_methods.insert(method_name, impl_span);
+ }
+ },
+ }
+ }
+ }
+ }
+ }
+}
--- /dev/null
+use clippy_utils::diagnostics::span_lint_and_help;
+use clippy_utils::is_ty_param_diagnostic_item;
+use rustc_hir::{self as hir, def_id::DefId, QPath};
+use rustc_lint::LateContext;
+use rustc_span::symbol::sym;
+
+use super::BOX_COLLECTION;
+
+pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, qpath: &QPath<'_>, def_id: DefId) -> bool {
+ if_chain! {
+ if Some(def_id) == cx.tcx.lang_items().owned_box();
+ if let Some(item_type) = get_std_collection(cx, qpath);
+ then {
+ let generic = if item_type == "String" {
+ ""
+ } else {
+ "<..>"
+ };
+ span_lint_and_help(
+ cx,
+ BOX_COLLECTION,
+ hir_ty.span,
+ &format!(
+ "you seem to be trying to use `Box<{outer}{generic}>`. Consider using just `{outer}{generic}`",
+ outer=item_type,
+ generic = generic),
+ None,
+ &format!(
+ "`{outer}{generic}` is already on the heap, `Box<{outer}{generic}>` makes an extra allocation",
+ outer=item_type,
+ generic = generic)
+ );
+ true
+ } else {
+ false
+ }
+ }
+}
+
+fn get_std_collection(cx: &LateContext<'_>, qpath: &QPath<'_>) -> Option<&'static str> {
+ if is_ty_param_diagnostic_item(cx, qpath, sym::vec_type).is_some() {
+ Some("Vec")
+ } else if is_ty_param_diagnostic_item(cx, qpath, sym::string_type).is_some() {
+ Some("String")
+ } else if is_ty_param_diagnostic_item(cx, qpath, sym::hashmap_type).is_some() {
+ Some("HashMap")
+ } else {
+ None
+ }
+}
+++ /dev/null
-use clippy_utils::diagnostics::span_lint_and_help;
-use clippy_utils::is_ty_param_diagnostic_item;
-use rustc_hir::{self as hir, def_id::DefId, QPath};
-use rustc_lint::LateContext;
-use rustc_span::symbol::sym;
-
-use super::BOX_VEC;
-
-pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, qpath: &QPath<'_>, def_id: DefId) -> bool {
- if Some(def_id) == cx.tcx.lang_items().owned_box()
- && is_ty_param_diagnostic_item(cx, qpath, sym::vec_type).is_some()
- {
- span_lint_and_help(
- cx,
- BOX_VEC,
- hir_ty.span,
- "you seem to be trying to use `Box<Vec<T>>`. Consider using just `Vec<T>`",
- None,
- "`Vec<T>` is already on the heap, `Box<Vec<T>>` makes an extra allocation",
- );
- true
- } else {
- false
- }
-}
mod borrowed_box;
-mod box_vec;
+mod box_collection;
mod linked_list;
mod option_option;
mod rc_buffer;
declare_clippy_lint! {
/// ### What it does
- /// Checks for use of `Box<Vec<_>>` anywhere in the code.
+ /// Checks for use of `Box<T>` where T is a collection such as Vec anywhere in the code.
/// Check the [Box documentation](https://doc.rust-lang.org/std/boxed/index.html) for more information.
///
/// ### Why is this bad?
- /// `Vec` already keeps its contents in a separate area on
- /// the heap. So if you `Box` it, you just add another level of indirection
+ /// Collections already keeps their contents in a separate area on
+ /// the heap. So if you `Box` them, you just add another level of indirection
/// without any benefit whatsoever.
///
/// ### Example
/// values: Vec<Foo>,
/// }
/// ```
- pub BOX_VEC,
+ pub BOX_COLLECTION,
perf,
"usage of `Box<Vec<T>>`, vector elements are already on the heap"
}
avoid_breaking_exported_api: bool,
}
-impl_lint_pass!(Types => [BOX_VEC, VEC_BOX, OPTION_OPTION, LINKEDLIST, BORROWED_BOX, REDUNDANT_ALLOCATION, RC_BUFFER, RC_MUTEX, TYPE_COMPLEXITY]);
+impl_lint_pass!(Types => [BOX_COLLECTION, VEC_BOX, OPTION_OPTION, LINKEDLIST, BORROWED_BOX, REDUNDANT_ALLOCATION, RC_BUFFER, RC_MUTEX, TYPE_COMPLEXITY]);
impl<'tcx> LateLintPass<'tcx> for Types {
fn check_fn(&mut self, cx: &LateContext<'_>, _: FnKind<'_>, decl: &FnDecl<'_>, _: &Body<'_>, _: Span, id: HirId) {
// in `clippy_lints::utils::conf.rs`
let mut triggered = false;
- triggered |= box_vec::check(cx, hir_ty, qpath, def_id);
+ triggered |= box_collection::check(cx, hir_ty, qpath, def_id);
triggered |= redundant_allocation::check(cx, hir_ty, qpath, def_id);
triggered |= rc_buffer::check(cx, hir_ty, qpath, def_id);
triggered |= vec_box::check(cx, hir_ty, qpath, def_id, self.vec_box_size_threshold);
pub rename: String,
}
+/// A single disallowed method, used by the `DISALLOWED_METHOD` lint.
+#[derive(Clone, Debug, Deserialize)]
+#[serde(untagged)]
+pub enum DisallowedMethod {
+ Simple(String),
+ WithReason { path: String, reason: Option<String> },
+}
+
/// Conf with parse errors
#[derive(Default)]
pub struct TryConf {
}
define_Conf! {
- /// Lint: ENUM_VARIANT_NAMES, LARGE_TYPES_PASSED_BY_VALUE, TRIVIALLY_COPY_PASS_BY_REF, UNNECESSARY_WRAPS, UPPER_CASE_ACRONYMS, WRONG_SELF_CONVENTION, BOX_VEC, REDUNDANT_ALLOCATION, RC_BUFFER, VEC_BOX, OPTION_OPTION, LINKEDLIST, RC_MUTEX.
+ /// Lint: ENUM_VARIANT_NAMES, LARGE_TYPES_PASSED_BY_VALUE, TRIVIALLY_COPY_PASS_BY_REF, UNNECESSARY_WRAPS, UPPER_CASE_ACRONYMS, WRONG_SELF_CONVENTION, BOX_COLLECTION, REDUNDANT_ALLOCATION, RC_BUFFER, VEC_BOX, OPTION_OPTION, LINKEDLIST, RC_MUTEX.
///
/// Suppress lints whenever the suggested change would cause breakage for other crates.
(avoid_breaking_exported_api: bool = true),
/// Lint: DISALLOWED_METHOD.
///
/// The list of disallowed methods, written as fully qualified paths.
- (disallowed_methods: Vec<String> = Vec::new()),
+ (disallowed_methods: Vec<crate::utils::conf::DisallowedMethod> = Vec::new()),
/// Lint: DISALLOWED_TYPE.
///
/// The list of disallowed types, written as fully qualified paths.
impl EarlyLintPass for ProduceIce {
fn check_fn(&mut self, _: &EarlyContext<'_>, fn_kind: FnKind<'_>, _: Span, _: NodeId) {
- if is_trigger_fn(fn_kind) {
- panic!("Would you like some help with that?");
- }
+ assert!(!is_trigger_fn(fn_kind), "Would you like some help with that?");
}
}
}).collect();
if !check_path(cx, &path[..]);
then {
- span_lint(cx, CLIPPY_LINTS_INTERNAL, item.span, "invalid path");
+ span_lint(cx, INVALID_PATHS, item.span, "invalid path");
}
}
}
let sm = cx.sess().source_map();
let span = sm.span_extend_to_prev_str(span, "let", false);
let span = sm.span_extend_to_next_char(span, ';', false);
- Span::new(span.lo() - BytePos(3), span.hi() + BytePos(1), span.ctxt())
+ Span::new(
+ span.lo() - BytePos(3),
+ span.hi() + BytePos(1),
+ span.ctxt(),
+ span.parent(),
+ )
}
default: String,
lints: Vec<String>,
doc: String,
+ #[allow(dead_code)]
deprecation_reason: Option<&'static str>,
}
[package]
name = "clippy_utils"
version = "0.1.57"
-edition = "2018"
+edition = "2021"
publish = false
[dependencies]
| (Ref(l, Mutability::Not), Ref(r, Mutability::Not))
| (Ref(l, Mutability::Mut), Ref(r, Mutability::Mut)) => eq_pat(l, r),
(Tuple(l), Tuple(r)) | (Slice(l), Slice(r)) => over(l, r, |l, r| eq_pat(l, r)),
- (Path(lq, lp), Path(rq, rp)) => both(lq, rq, |l, r| eq_qself(l, r)) && eq_path(lp, rp),
+ (Path(lq, lp), Path(rq, rp)) => both(lq, rq, eq_qself) && eq_path(lp, rp),
(TupleStruct(lqself, lp, lfs), TupleStruct(rqself, rp, rfs)) => {
eq_maybe_qself(lqself, rqself) && eq_path(lp, rp) && over(lfs, rfs, |l, r| eq_pat(l, r))
},
(Struct(lqself, lp, lfs, lr), Struct(rqself, rp, rfs, rr)) => {
- lr == rr
- && eq_maybe_qself(lqself, rqself)
- && eq_path(lp, rp)
- && unordered_over(lfs, rfs, |lf, rf| eq_field_pat(lf, rf))
+ lr == rr && eq_maybe_qself(lqself, rqself) && eq_path(lp, rp) && unordered_over(lfs, rfs, eq_field_pat)
},
(Or(ls), Or(rs)) => unordered_over(ls, rs, |l, r| eq_pat(l, r)),
(MacCall(l), MacCall(r)) => eq_mac_call(l, r),
l.is_placeholder == r.is_placeholder
&& eq_id(l.ident, r.ident)
&& eq_pat(&l.pat, &r.pat)
- && over(&l.attrs, &r.attrs, |l, r| eq_attr(l, r))
+ && over(&l.attrs, &r.attrs, eq_attr)
}
pub fn eq_qself(l: &QSelf, r: &QSelf) -> bool {
}
pub fn eq_path(l: &Path, r: &Path) -> bool {
- over(&l.segments, &r.segments, |l, r| eq_path_seg(l, r))
+ over(&l.segments, &r.segments, eq_path_seg)
}
pub fn eq_path_seg(l: &PathSegment, r: &PathSegment) -> bool {
pub fn eq_generic_args(l: &GenericArgs, r: &GenericArgs) -> bool {
match (l, r) {
- (GenericArgs::AngleBracketed(l), GenericArgs::AngleBracketed(r)) => {
- over(&l.args, &r.args, |l, r| eq_angle_arg(l, r))
- },
+ (GenericArgs::AngleBracketed(l), GenericArgs::AngleBracketed(r)) => over(&l.args, &r.args, eq_angle_arg),
(GenericArgs::Parenthesized(l), GenericArgs::Parenthesized(r)) => {
over(&l.inputs, &r.inputs, |l, r| eq_ty(l, r)) && eq_fn_ret_ty(&l.output, &r.output)
},
pub fn eq_expr(l: &Expr, r: &Expr) -> bool {
use ExprKind::*;
- if !over(&l.attrs, &r.attrs, |l, r| eq_attr(l, r)) {
+ if !over(&l.attrs, &r.attrs, eq_attr) {
return false;
}
match (&l.kind, &r.kind) {
(Assign(l1, l2, _), Assign(r1, r2, _)) | (Index(l1, l2), Index(r1, r2)) => eq_expr(l1, r1) && eq_expr(l2, r2),
(AssignOp(lo, lp, lv), AssignOp(ro, rp, rv)) => lo.node == ro.node && eq_expr(lp, rp) && eq_expr(lv, rv),
(Field(lp, lf), Field(rp, rf)) => eq_id(*lf, *rf) && eq_expr(lp, rp),
- (Match(ls, la), Match(rs, ra)) => eq_expr(ls, rs) && over(la, ra, |l, r| eq_arm(l, r)),
+ (Match(ls, la), Match(rs, ra)) => eq_expr(ls, rs) && over(la, ra, eq_arm),
(Closure(lc, la, lm, lf, lb, _), Closure(rc, ra, rm, rf, rb, _)) => {
lc == rc && la.is_async() == ra.is_async() && lm == rm && eq_fn_decl(lf, rf) && eq_expr(lb, rb)
},
(Async(lc, _, lb), Async(rc, _, rb)) => lc == rc && eq_block(lb, rb),
(Range(lf, lt, ll), Range(rf, rt, rl)) => ll == rl && eq_expr_opt(lf, rf) && eq_expr_opt(lt, rt),
(AddrOf(lbk, lm, le), AddrOf(rbk, rm, re)) => lbk == rbk && lm == rm && eq_expr(le, re),
- (Path(lq, lp), Path(rq, rp)) => both(lq, rq, |l, r| eq_qself(l, r)) && eq_path(lp, rp),
+ (Path(lq, lp), Path(rq, rp)) => both(lq, rq, eq_qself) && eq_path(lp, rp),
(MacCall(l), MacCall(r)) => eq_mac_call(l, r),
(Struct(lse), Struct(rse)) => {
eq_maybe_qself(&lse.qself, &rse.qself)
&& eq_path(&lse.path, &rse.path)
&& eq_struct_rest(&lse.rest, &rse.rest)
- && unordered_over(&lse.fields, &rse.fields, |l, r| eq_field(l, r))
+ && unordered_over(&lse.fields, &rse.fields, eq_field)
},
_ => false,
}
l.is_placeholder == r.is_placeholder
&& eq_id(l.ident, r.ident)
&& eq_expr(&l.expr, &r.expr)
- && over(&l.attrs, &r.attrs, |l, r| eq_attr(l, r))
+ && over(&l.attrs, &r.attrs, eq_attr)
}
pub fn eq_arm(l: &Arm, r: &Arm) -> bool {
&& eq_pat(&l.pat, &r.pat)
&& eq_expr(&l.body, &r.body)
&& eq_expr_opt(&l.guard, &r.guard)
- && over(&l.attrs, &r.attrs, |l, r| eq_attr(l, r))
+ && over(&l.attrs, &r.attrs, eq_attr)
}
pub fn eq_label(l: &Option<Label>, r: &Option<Label>) -> bool {
}
pub fn eq_block(l: &Block, r: &Block) -> bool {
- l.rules == r.rules && over(&l.stmts, &r.stmts, |l, r| eq_stmt(l, r))
+ l.rules == r.rules && over(&l.stmts, &r.stmts, eq_stmt)
}
pub fn eq_stmt(l: &Stmt, r: &Stmt) -> bool {
eq_pat(&l.pat, &r.pat)
&& both(&l.ty, &r.ty, |l, r| eq_ty(l, r))
&& eq_local_kind(&l.kind, &r.kind)
- && over(&l.attrs, &r.attrs, |l, r| eq_attr(l, r))
+ && over(&l.attrs, &r.attrs, eq_attr)
},
(Item(l), Item(r)) => eq_item(l, r, eq_item_kind),
(Expr(l), Expr(r)) | (Semi(l), Semi(r)) => eq_expr(l, r),
(Empty, Empty) => true,
(MacCall(l), MacCall(r)) => {
- l.style == r.style && eq_mac_call(&l.mac, &r.mac) && over(&l.attrs, &r.attrs, |l, r| eq_attr(l, r))
+ l.style == r.style && eq_mac_call(&l.mac, &r.mac) && over(&l.attrs, &r.attrs, eq_attr)
},
_ => false,
}
}
pub fn eq_item<K>(l: &Item<K>, r: &Item<K>, mut eq_kind: impl FnMut(&K, &K) -> bool) -> bool {
- eq_id(l.ident, r.ident)
- && over(&l.attrs, &r.attrs, |l, r| eq_attr(l, r))
- && eq_vis(&l.vis, &r.vis)
- && eq_kind(&l.kind, &r.kind)
+ eq_id(l.ident, r.ident) && over(&l.attrs, &r.attrs, eq_attr) && eq_vis(&l.vis, &r.vis) && eq_kind(&l.kind, &r.kind)
}
pub fn eq_item_kind(l: &ItemKind, r: &ItemKind) -> bool {
}
},
(ForeignMod(l), ForeignMod(r)) => {
- both(&l.abi, &r.abi, |l, r| eq_str_lit(l, r))
- && over(&l.items, &r.items, |l, r| eq_item(l, r, eq_foreign_item_kind))
+ both(&l.abi, &r.abi, eq_str_lit) && over(&l.items, &r.items, |l, r| eq_item(l, r, eq_foreign_item_kind))
},
(TyAlias(box TyAliasKind(ld, lg, lb, lt)), TyAlias(box TyAliasKind(rd, rg, rb, rt))) => {
eq_defaultness(*ld, *rd)
&& eq_generics(lg, rg)
- && over(lb, rb, |l, r| eq_generic_bound(l, r))
+ && over(lb, rb, eq_generic_bound)
&& both(lt, rt, |l, r| eq_ty(l, r))
},
- (Enum(le, lg), Enum(re, rg)) => {
- over(&le.variants, &re.variants, |l, r| eq_variant(l, r)) && eq_generics(lg, rg)
- },
+ (Enum(le, lg), Enum(re, rg)) => over(&le.variants, &re.variants, eq_variant) && eq_generics(lg, rg),
(Struct(lv, lg), Struct(rv, rg)) | (Union(lv, lg), Union(rv, rg)) => {
eq_variant_data(lv, rv) && eq_generics(lg, rg)
},
la == ra
&& matches!(lu, Unsafe::No) == matches!(ru, Unsafe::No)
&& eq_generics(lg, rg)
- && over(lb, rb, |l, r| eq_generic_bound(l, r))
+ && over(lb, rb, eq_generic_bound)
&& over(li, ri, |l, r| eq_item(l, r, eq_assoc_item_kind))
},
- (TraitAlias(lg, lb), TraitAlias(rg, rb)) => eq_generics(lg, rg) && over(lb, rb, |l, r| eq_generic_bound(l, r)),
+ (TraitAlias(lg, lb), TraitAlias(rg, rb)) => eq_generics(lg, rg) && over(lb, rb, eq_generic_bound),
(
Impl(box ImplKind {
unsafety: lu,
(TyAlias(box TyAliasKind(ld, lg, lb, lt)), TyAlias(box TyAliasKind(rd, rg, rb, rt))) => {
eq_defaultness(*ld, *rd)
&& eq_generics(lg, rg)
- && over(lb, rb, |l, r| eq_generic_bound(l, r))
+ && over(lb, rb, eq_generic_bound)
&& both(lt, rt, |l, r| eq_ty(l, r))
},
(MacCall(l), MacCall(r)) => eq_mac_call(l, r),
(TyAlias(box TyAliasKind(ld, lg, lb, lt)), TyAlias(box TyAliasKind(rd, rg, rb, rt))) => {
eq_defaultness(*ld, *rd)
&& eq_generics(lg, rg)
- && over(lb, rb, |l, r| eq_generic_bound(l, r))
+ && over(lb, rb, eq_generic_bound)
&& both(lt, rt, |l, r| eq_ty(l, r))
},
(MacCall(l), MacCall(r)) => eq_mac_call(l, r),
pub fn eq_variant(l: &Variant, r: &Variant) -> bool {
l.is_placeholder == r.is_placeholder
- && over(&l.attrs, &r.attrs, |l, r| eq_attr(l, r))
+ && over(&l.attrs, &r.attrs, eq_attr)
&& eq_vis(&l.vis, &r.vis)
&& eq_id(l.ident, r.ident)
&& eq_variant_data(&l.data, &r.data)
use VariantData::*;
match (l, r) {
(Unit(_), Unit(_)) => true,
- (Struct(l, _), Struct(r, _)) | (Tuple(l, _), Tuple(r, _)) => over(l, r, |l, r| eq_struct_field(l, r)),
+ (Struct(l, _), Struct(r, _)) | (Tuple(l, _), Tuple(r, _)) => over(l, r, eq_struct_field),
_ => false,
}
}
pub fn eq_struct_field(l: &FieldDef, r: &FieldDef) -> bool {
l.is_placeholder == r.is_placeholder
- && over(&l.attrs, &r.attrs, |l, r| eq_attr(l, r))
+ && over(&l.attrs, &r.attrs, eq_attr)
&& eq_vis(&l.vis, &r.vis)
&& both(&l.ident, &r.ident, |l, r| eq_id(*l, *r))
&& eq_ty(&l.ty, &r.ty)
}
pub fn eq_generics(l: &Generics, r: &Generics) -> bool {
- over(&l.params, &r.params, |l, r| eq_generic_param(l, r))
+ over(&l.params, &r.params, eq_generic_param)
&& over(&l.where_clause.predicates, &r.where_clause.predicates, |l, r| {
eq_where_predicate(l, r)
})
over(&l.bound_generic_params, &r.bound_generic_params, |l, r| {
eq_generic_param(l, r)
}) && eq_ty(&l.bounded_ty, &r.bounded_ty)
- && over(&l.bounds, &r.bounds, |l, r| eq_generic_bound(l, r))
+ && over(&l.bounds, &r.bounds, eq_generic_bound)
},
(RegionPredicate(l), RegionPredicate(r)) => {
- eq_id(l.lifetime.ident, r.lifetime.ident) && over(&l.bounds, &r.bounds, |l, r| eq_generic_bound(l, r))
+ eq_id(l.lifetime.ident, r.lifetime.ident) && over(&l.bounds, &r.bounds, eq_generic_bound)
},
(EqPredicate(l), EqPredicate(r)) => eq_ty(&l.lhs_ty, &r.lhs_ty) && eq_ty(&l.rhs_ty, &r.rhs_ty),
_ => false,
l.is_placeholder == r.is_placeholder
&& eq_pat(&l.pat, &r.pat)
&& eq_ty(&l.ty, &r.ty)
- && over(&l.attrs, &r.attrs, |l, r| eq_attr(l, r))
+ && over(&l.attrs, &r.attrs, eq_attr)
})
}
(BareFn(l), BareFn(r)) => {
l.unsafety == r.unsafety
&& eq_ext(&l.ext, &r.ext)
- && over(&l.generic_params, &r.generic_params, |l, r| eq_generic_param(l, r))
+ && over(&l.generic_params, &r.generic_params, eq_generic_param)
&& eq_fn_decl(&l.decl, &r.decl)
},
(Tup(l), Tup(r)) => over(l, r, |l, r| eq_ty(l, r)),
- (Path(lq, lp), Path(rq, rp)) => both(lq, rq, |l, r| eq_qself(l, r)) && eq_path(lp, rp),
- (TraitObject(lg, ls), TraitObject(rg, rs)) => ls == rs && over(lg, rg, |l, r| eq_generic_bound(l, r)),
- (ImplTrait(_, lg), ImplTrait(_, rg)) => over(lg, rg, |l, r| eq_generic_bound(l, r)),
+ (Path(lq, lp), Path(rq, rp)) => both(lq, rq, eq_qself) && eq_path(lp, rp),
+ (TraitObject(lg, ls), TraitObject(rg, rs)) => ls == rs && over(lg, rg, eq_generic_bound),
+ (ImplTrait(_, lg), ImplTrait(_, rg)) => over(lg, rg, eq_generic_bound),
(Typeof(l), Typeof(r)) => eq_expr(&l.value, &r.value),
(MacCall(l), MacCall(r)) => eq_mac_call(l, r),
_ => false,
use GenericParamKind::*;
l.is_placeholder == r.is_placeholder
&& eq_id(l.ident, r.ident)
- && over(&l.bounds, &r.bounds, |l, r| eq_generic_bound(l, r))
+ && over(&l.bounds, &r.bounds, eq_generic_bound)
&& match (&l.kind, &r.kind) {
(Lifetime, Lifetime) => true,
(Type { default: l }, Type { default: r }) => both(l, r, |l, r| eq_ty(l, r)),
kw_span: _,
default: rd,
},
- ) => eq_ty(lt, rt) && both(ld, rd, |ld, rd| eq_anon_const(ld, rd)),
+ ) => eq_ty(lt, rt) && both(ld, rd, eq_anon_const),
_ => false,
}
- && over(&l.attrs, &r.attrs, |l, r| eq_attr(l, r))
+ && over(&l.attrs, &r.attrs, eq_attr)
}
pub fn eq_generic_bound(l: &GenericBound, r: &GenericBound) -> bool {
eq_id(l.ident, r.ident)
&& match (&l.kind, &r.kind) {
(Equality { ty: l }, Equality { ty: r }) => eq_ty(l, r),
- (Bound { bounds: l }, Bound { bounds: r }) => over(l, r, |l, r| eq_generic_bound(l, r)),
+ (Bound { bounds: l }, Bound { bounds: r }) => over(l, r, eq_generic_bound),
_ => false,
}
}
match expr.kind {
ExprKind::Lit(..) | ExprKind::ConstBlock(..) | ExprKind::Path(..) | ExprKind::Field(..) => true,
ExprKind::AddrOf(_, _, addr_of_expr) => identify_some_pure_patterns(addr_of_expr),
- ExprKind::Tup(tup_exprs) => tup_exprs.iter().all(|expr| identify_some_pure_patterns(expr)),
+ ExprKind::Tup(tup_exprs) => tup_exprs.iter().all(identify_some_pure_patterns),
ExprKind::Struct(_, fields, expr) => {
- fields.iter().all(|f| identify_some_pure_patterns(f.expr))
- && expr.map_or(true, |e| identify_some_pure_patterns(e))
+ fields.iter().all(|f| identify_some_pure_patterns(f.expr)) && expr.map_or(true, identify_some_pure_patterns)
},
ExprKind::Call(
&Expr {
..
},
args,
- ) => args.iter().all(|expr| identify_some_pure_patterns(expr)),
+ ) => args.iter().all(identify_some_pure_patterns),
ExprKind::Block(
&Block {
stmts,
false
}
+
+/// A parsed `panic!` expansion
+pub struct PanicExpn<'tcx> {
+ /// Span of `panic!(..)`
+ pub call_site: Span,
+ /// Inner `format_args!` expansion
+ pub format_args: FormatArgsExpn<'tcx>,
+}
+
+impl PanicExpn<'tcx> {
+ /// Parses an expanded `panic!` invocation
+ pub fn parse(expr: &'tcx Expr<'tcx>) -> Option<Self> {
+ if_chain! {
+ if let ExprKind::Block(block, _) = expr.kind;
+ if let Some(init) = block.expr;
+ if let ExprKind::Call(_, [format_args]) = init.kind;
+ let expn_data = expr.span.ctxt().outer_expn_data();
+ if let ExprKind::AddrOf(_, _, format_args) = format_args.kind;
+ if let Some(format_args) = FormatArgsExpn::parse(format_args);
+ then {
+ Some(PanicExpn {
+ call_site: expn_data.call_site,
+ format_args,
+ })
+ } else {
+ None
+ }
+ }
+ }
+}
std::mem::discriminant(&b.rules).hash(&mut self.s);
}
- #[allow(clippy::many_single_char_names, clippy::too_many_lines)]
+ #[allow(clippy::too_many_lines)]
pub fn hash_expr(&mut self, e: &Expr<'_>) {
let simple_const = self
.maybe_typeck_results
pub const IPADDR_V4: [&str; 5] = ["std", "net", "ip", "IpAddr", "V4"];
pub const IPADDR_V6: [&str; 5] = ["std", "net", "ip", "IpAddr", "V6"];
pub const ITER_REPEAT: [&str; 5] = ["core", "iter", "sources", "repeat", "repeat"];
+#[allow(clippy::invalid_paths)] // internal lints do not know about all external crates
pub const ITERTOOLS_NEXT_TUPLE: [&str; 3] = ["itertools", "Itertools", "next_tuple"];
#[cfg(feature = "internal-lints")]
pub const KW_MODULE: [&str; 3] = ["rustc_span", "symbol", "kw"];
))
}
},
- Rvalue::NullaryOp(NullOp::SizeOf | NullOp::AlignOf, _) => Ok(()),
+ Rvalue::NullaryOp(NullOp::SizeOf | NullOp::AlignOf, _) | Rvalue::ShallowInitBox(_, _) => Ok(()),
Rvalue::NullaryOp(NullOp::Box, _) => Err((span, "heap allocations are not allowed in const fn".into())),
- Rvalue::ShallowInitBox(_, _) => Ok(()),
Rvalue::UnaryOp(_, operand) => {
let ty = operand.ty(body, tcx);
if ty.is_integral() || ty.is_bool() {
env, fmt,
fs::write,
path::{Path, PathBuf},
+ thread,
+ time::Duration,
};
use clap::{App, Arg, ArgMatches};
}
}
+fn get(path: &str) -> Result<ureq::Response, ureq::Error> {
+ const MAX_RETRIES: u8 = 4;
+ let mut retries = 0;
+ loop {
+ match ureq::get(path).call() {
+ Ok(res) => return Ok(res),
+ Err(e) if retries >= MAX_RETRIES => return Err(e),
+ Err(ureq::Error::Transport(e)) => eprintln!("Error: {}", e),
+ Err(e) => return Err(e),
+ }
+ eprintln!("retrying in {} seconds...", retries);
+ thread::sleep(Duration::from_secs(retries as u64));
+ retries += 1;
+ }
+}
+
impl CrateSource {
/// Makes the sources available on the disk for clippy to check.
/// Clones a git repo and checks out the specified commit or downloads a crate from crates.io or
if !krate_file_path.is_file() {
// create a file path to download and write the crate data into
let mut krate_dest = std::fs::File::create(&krate_file_path).unwrap();
- let mut krate_req = ureq::get(&url).call().unwrap().into_reader();
+ let mut krate_req = get(&url).unwrap().into_reader();
// copy the crate into the file
std::io::copy(&mut krate_req, &mut krate_dest).unwrap();
[toolchain]
-channel = "nightly-2021-09-08"
+channel = "nightly-2021-09-28"
components = ["llvm-tools-preview", "rustc-dev", "rust-src"]
use rustc_tools_util::VersionInfo;
use std::env;
-use std::ffi::OsString;
use std::path::PathBuf;
use std::process::{self, Command};
cargo clippy [options] [--] [<opts>...]
Common options:
- --no-deps Run Clippy only on the given crate, without linting the dependencies
+ --no-deps Run Clippy only on the given crate, without linting the dependencies
--fix Automatically apply lint suggestions. This flag implies `--no-deps`
-h, --help Print this message
-V, --version Print version info and exit
path
}
- fn target_dir() -> Option<(&'static str, OsString)> {
- env::var_os("CLIPPY_DOGFOOD")
- .map(|_| {
- env::var_os("CARGO_MANIFEST_DIR").map_or_else(
- || std::ffi::OsString::from("clippy_dogfood"),
- |d| {
- std::path::PathBuf::from(d)
- .join("target")
- .join("dogfood")
- .into_os_string()
- },
- )
- })
- .map(|p| ("CARGO_TARGET_DIR", p))
- }
-
fn into_std_cmd(self) -> Command {
let mut cmd = Command::new("cargo");
let clippy_args: String = self
.collect();
cmd.env("RUSTC_WORKSPACE_WRAPPER", Self::path())
- .envs(ClippyCmd::target_dir())
.env("CLIPPY_ARGS", clippy_args)
.arg(self.cargo_subcommand)
.args(&self.args);
-use std::env;
-use std::lazy::SyncLazy;
-use std::path::PathBuf;
-
-pub static CARGO_TARGET_DIR: SyncLazy<PathBuf> = SyncLazy::new(|| match env::var_os("CARGO_TARGET_DIR") {
- Some(v) => v.into(),
- None => env::current_dir().unwrap().join("target"),
-});
-
-pub static TARGET_LIB: SyncLazy<PathBuf> = SyncLazy::new(|| {
- if let Some(path) = option_env!("TARGET_LIBS") {
- path.into()
- } else {
- let mut dir = CARGO_TARGET_DIR.clone();
- if let Some(target) = env::var_os("CARGO_BUILD_TARGET") {
- dir.push(target);
- }
- dir.push(env!("PROFILE"));
- dir
- }
-});
-
#[must_use]
pub fn is_rustc_test_suite() -> bool {
option_env!("RUSTC_TEST_SUITE").is_some()
#![feature(test)] // compiletest_rs requires this attribute
-#![feature(once_cell)]
#![cfg_attr(feature = "deny-warnings", deny(warnings))]
#![warn(rust_2018_idioms, unused_lifetimes)]
#[allow(unused_extern_crates)]
extern crate syn;
-fn host_lib() -> PathBuf {
- option_env!("HOST_LIBS").map_or(cargo::CARGO_TARGET_DIR.join(env!("PROFILE")), PathBuf::from)
-}
-
-fn clippy_driver_path() -> PathBuf {
- option_env!("CLIPPY_DRIVER_PATH").map_or(cargo::TARGET_LIB.join("clippy-driver"), PathBuf::from)
-}
-
/// Produces a string with an `--extern` flag for all UI test crate
/// dependencies.
///
.copied()
.filter(|n| !crates.contains_key(n))
.collect();
- if !not_found.is_empty() {
- panic!("dependencies not found in depinfo: {:?}", not_found);
- }
+ assert!(
+ not_found.is_empty(),
+ "dependencies not found in depinfo: {:?}",
+ not_found
+ );
crates
.into_iter()
- .map(|(name, path)| format!("--extern {}={} ", name, path))
+ .map(|(name, path)| format!(" --extern {}={}", name, path))
.collect()
}
config.run_lib_path = path.clone();
config.compile_lib_path = path;
}
+ let current_exe_path = std::env::current_exe().unwrap();
+ let deps_path = current_exe_path.parent().unwrap();
+ let profile_path = deps_path.parent().unwrap();
// Using `-L dependency={}` enforces that external dependencies are added with `--extern`.
// This is valuable because a) it allows us to monitor what external dependencies are used
// and b) it ensures that conflicting rlibs are resolved properly.
+ let host_libs = option_env!("HOST_LIBS")
+ .map(|p| format!(" -L dependency={}", Path::new(p).join("deps").display()))
+ .unwrap_or_default();
config.target_rustcflags = Some(format!(
- "--emit=metadata -L dependency={} -L dependency={} -Dwarnings -Zui-testing {}",
- host_lib().join("deps").display(),
- cargo::TARGET_LIB.join("deps").display(),
+ "--emit=metadata -Dwarnings -Zui-testing -L dependency={}{}{}",
+ deps_path.display(),
+ host_libs,
extern_flags(),
));
- config.build_base = host_lib().join("test_build_base");
- config.rustc_path = clippy_driver_path();
+ config.build_base = profile_path.join("test");
+ config.rustc_path = profile_path.join(if cfg!(windows) {
+ "clippy-driver.exe"
+ } else {
+ "clippy-driver"
+ });
config
}
mod cargo;
-static CLIPPY_PATH: SyncLazy<PathBuf> = SyncLazy::new(|| cargo::TARGET_LIB.join("cargo-clippy"));
+static CLIPPY_PATH: SyncLazy<PathBuf> = SyncLazy::new(|| {
+ let mut path = std::env::current_exe().unwrap();
+ assert!(path.pop()); // deps
+ path.set_file_name("cargo-clippy");
+ path
+});
#[test]
fn dogfood_clippy() {
let mut command = Command::new(&*CLIPPY_PATH);
command
.current_dir(root_dir)
- .env("CLIPPY_DOGFOOD", "1")
.env("CARGO_INCREMENTAL", "0")
.arg("clippy")
.arg("--all-targets")
// Make sure that with the `--no-deps` argument Clippy does not run on `path_dep`.
let output = Command::new(&*CLIPPY_PATH)
.current_dir(&cwd)
- .env("CLIPPY_DOGFOOD", "1")
.env("CARGO_INCREMENTAL", "0")
.arg("clippy")
.args(&["-p", "subcrate"])
// Test that without the `--no-deps` argument, `path_dep` is linted.
let output = Command::new(&*CLIPPY_PATH)
.current_dir(&cwd)
- .env("CLIPPY_DOGFOOD", "1")
.env("CARGO_INCREMENTAL", "0")
.arg("clippy")
.args(&["-p", "subcrate"])
let successful_build = || {
let output = Command::new(&*CLIPPY_PATH)
.current_dir(&cwd)
- .env("CLIPPY_DOGFOOD", "1")
.env("CARGO_INCREMENTAL", "0")
.arg("clippy")
.args(&["-p", "subcrate"])
command
.current_dir(root_dir.join(project))
- .env("CLIPPY_DOGFOOD", "1")
.env("CARGO_INCREMENTAL", "0")
.arg("clippy")
.arg("--all-targets")
panic!("incompatible crate versions");
} else if stderr.contains("failed to run `rustc` to learn about target-specific information") {
panic!("couldn't find librustc_driver, consider setting `LD_LIBRARY_PATH`");
- } else if stderr.contains("toolchain") && stderr.contains("is not installed") {
- panic!("missing required toolchain");
+ } else {
+ assert!(
+ !stderr.contains("toolchain") || !stderr.contains("is not installed"),
+ "missing required toolchain"
+ );
}
match output.status.code() {
LL | pub const BAD_CRATE_PATH: [&str; 2] = ["bad", "path"];
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
- = note: `-D clippy::clippy-lints-internal` implied by `-D warnings`
+ = note: `-D clippy::invalid-paths` implied by `-D warnings`
error: invalid path
--> $DIR/invalid_paths.rs:20:5
disallowed-methods = [
+ # just a string is shorthand for path only
"std::iter::Iterator::sum",
- "regex::Regex::is_match",
- "regex::Regex::new"
+ # can give path and reason with an inline table
+ { path = "regex::Regex::is_match", reason = "no matching allowed" },
+ # can use an inline table but omit reason
+ { path = "regex::Regex::new" },
]
-error: use of a disallowed method `regex::re_unicode::Regex::new`
+error: use of a disallowed method `regex::Regex::new`
--> $DIR/conf_disallowed_method.rs:7:14
|
LL | let re = Regex::new(r"ab.*c").unwrap();
|
= note: `-D clippy::disallowed-method` implied by `-D warnings`
-error: use of a disallowed method `regex::re_unicode::Regex::is_match`
+error: use of a disallowed method `regex::Regex::is_match`
--> $DIR/conf_disallowed_method.rs:8:5
|
LL | re.is_match("abc");
| ^^^^^^^^^^^^^^^^^^
+ |
+ = note: no matching allowed (from clippy.toml)
-error: use of a disallowed method `core::iter::traits::iterator::Iterator::sum`
+error: use of a disallowed method `std::iter::Iterator::sum`
--> $DIR/conf_disallowed_method.rs:11:5
|
LL | a.iter().sum::<i32>();
// normalize-stderr-test "\(limit: \d+ byte\)" -> "(limit: N byte)"
#![deny(clippy::trivially_copy_pass_by_ref)]
-#![allow(clippy::many_single_char_names)]
#[derive(Copy, Clone)]
struct Foo(u8);
error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte)
- --> $DIR/test.rs:15:11
+ --> $DIR/test.rs:14:11
|
LL | fn bad(x: &u16, y: &Foo) {}
| ^^^^ help: consider passing by value instead: `u16`
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte)
- --> $DIR/test.rs:15:20
+ --> $DIR/test.rs:14:20
|
LL | fn bad(x: &u16, y: &Foo) {}
| ^^^^ help: consider passing by value instead: `Foo`
--- /dev/null
+// compile-flags: --emit=link
+// no-prefer-dynamic
+
+#![crate_type = "proc-macro"]
+
+extern crate proc_macro;
+use proc_macro::{token_stream, Delimiter, Group, Ident, Span, TokenStream, TokenTree};
+use std::iter::FromIterator;
+
+fn read_ident(iter: &mut token_stream::IntoIter) -> Ident {
+ match iter.next() {
+ Some(TokenTree::Ident(i)) => i,
+ _ => panic!("expected ident"),
+ }
+}
+
+#[proc_macro_derive(DeriveBadSpan)]
+pub fn derive_bad_span(input: TokenStream) -> TokenStream {
+ let mut input = input.into_iter();
+ assert_eq!(read_ident(&mut input).to_string(), "struct");
+ let ident = read_ident(&mut input);
+ let mut tys = match input.next() {
+ Some(TokenTree::Group(g)) if g.delimiter() == Delimiter::Parenthesis => g.stream().into_iter(),
+ _ => panic!(),
+ };
+ let field1 = read_ident(&mut tys);
+ tys.next();
+ let field2 = read_ident(&mut tys);
+
+ <TokenStream as FromIterator<TokenTree>>::from_iter(
+ [
+ Ident::new("impl", Span::call_site()).into(),
+ ident.into(),
+ Group::new(
+ Delimiter::Brace,
+ <TokenStream as FromIterator<TokenTree>>::from_iter(
+ [
+ Ident::new("fn", Span::call_site()).into(),
+ Ident::new("_foo", Span::call_site()).into(),
+ Group::new(Delimiter::Parenthesis, TokenStream::new()).into(),
+ Group::new(
+ Delimiter::Brace,
+ <TokenStream as FromIterator<TokenTree>>::from_iter(
+ [
+ Ident::new("if", field1.span()).into(),
+ Ident::new("true", field1.span()).into(),
+ {
+ let mut group = Group::new(Delimiter::Brace, TokenStream::new());
+ group.set_span(field1.span());
+ group.into()
+ },
+ Ident::new("if", field2.span()).into(),
+ Ident::new("true", field2.span()).into(),
+ {
+ let mut group = Group::new(Delimiter::Brace, TokenStream::new());
+ group.set_span(field2.span());
+ group.into()
+ },
+ ]
+ .iter()
+ .cloned(),
+ ),
+ )
+ .into(),
+ ]
+ .iter()
+ .cloned(),
+ ),
+ )
+ .into(),
+ ]
+ .iter()
+ .cloned(),
+ )
+}
--- /dev/null
+#![warn(clippy::all)]
+#![allow(
+ clippy::boxed_local,
+ clippy::needless_pass_by_value,
+ clippy::blacklisted_name,
+ unused
+)]
+
+use std::collections::HashMap;
+
+macro_rules! boxit {
+ ($init:expr, $x:ty) => {
+ let _: Box<$x> = Box::new($init);
+ };
+}
+
+fn test_macro() {
+ boxit!(Vec::new(), Vec<u8>);
+}
+
+fn test(foo: Box<Vec<bool>>) {}
+
+fn test2(foo: Box<dyn Fn(Vec<u32>)>) {
+ // pass if #31 is fixed
+ foo(vec![1, 2, 3])
+}
+
+fn test3(foo: Box<String>) {}
+
+fn test4(foo: Box<HashMap<String, String>>) {}
+
+fn test_local_not_linted() {
+ let _: Box<Vec<bool>>;
+}
+
+// All of these test should be allowed because they are part of the
+// public api and `avoid_breaking_exported_api` is `false` by default.
+pub fn pub_test(foo: Box<Vec<bool>>) {}
+
+pub fn pub_test_ret() -> Box<Vec<bool>> {
+ Box::new(Vec::new())
+}
+
+fn main() {}
--- /dev/null
+error: you seem to be trying to use `Box<Vec<..>>`. Consider using just `Vec<..>`
+ --> $DIR/box_collection.rs:21:14
+ |
+LL | fn test(foo: Box<Vec<bool>>) {}
+ | ^^^^^^^^^^^^^^
+ |
+ = note: `-D clippy::box-collection` implied by `-D warnings`
+ = help: `Vec<..>` is already on the heap, `Box<Vec<..>>` makes an extra allocation
+
+error: you seem to be trying to use `Box<String>`. Consider using just `String`
+ --> $DIR/box_collection.rs:28:15
+ |
+LL | fn test3(foo: Box<String>) {}
+ | ^^^^^^^^^^^
+ |
+ = help: `String` is already on the heap, `Box<String>` makes an extra allocation
+
+error: you seem to be trying to use `Box<HashMap<..>>`. Consider using just `HashMap<..>`
+ --> $DIR/box_collection.rs:30:15
+ |
+LL | fn test4(foo: Box<HashMap<String, String>>) {}
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: `HashMap<..>` is already on the heap, `Box<HashMap<..>>` makes an extra allocation
+
+error: aborting due to 3 previous errors
+
+++ /dev/null
-#![warn(clippy::all)]
-#![allow(
- clippy::boxed_local,
- clippy::needless_pass_by_value,
- clippy::blacklisted_name,
- unused
-)]
-
-macro_rules! boxit {
- ($init:expr, $x:ty) => {
- let _: Box<$x> = Box::new($init);
- };
-}
-
-fn test_macro() {
- boxit!(Vec::new(), Vec<u8>);
-}
-fn test(foo: Box<Vec<bool>>) {}
-
-fn test2(foo: Box<dyn Fn(Vec<u32>)>) {
- // pass if #31 is fixed
- foo(vec![1, 2, 3])
-}
-
-fn test_local_not_linted() {
- let _: Box<Vec<bool>>;
-}
-
-// All of these test should be allowed because they are part of the
-// public api and `avoid_breaking_exported_api` is `false` by default.
-pub fn pub_test(foo: Box<Vec<bool>>) {}
-pub fn pub_test_ret() -> Box<Vec<bool>> {
- Box::new(Vec::new())
-}
-
-fn main() {}
+++ /dev/null
-error: you seem to be trying to use `Box<Vec<T>>`. Consider using just `Vec<T>`
- --> $DIR/box_vec.rs:18:14
- |
-LL | fn test(foo: Box<Vec<bool>>) {}
- | ^^^^^^^^^^^^^^
- |
- = note: `-D clippy::box-vec` implied by `-D warnings`
- = help: `Vec<T>` is already on the heap, `Box<Vec<T>>` makes an extra allocation
-
-error: aborting due to previous error
-
pub unsafe trait Freeze {}
#[lang = "start"]
-#[start]
-fn start(_argc: isize, _argv: *const *const u8) -> isize {
+fn start<T>(_main: fn() -> T, _argc: isize, _argv: *const *const u8) -> isize {
0
}
+fn main() {}
+
struct A;
impl A {
error: methods called `as_*` usually take `self` by reference or `self` by mutable reference
- --> $DIR/def_id_nocore.rs:26:19
+ --> $DIR/def_id_nocore.rs:27:19
|
LL | pub fn as_ref(self) -> &'static str {
| ^^^^
// run-rustfix
-#![allow(unused_imports,dead_code)]
+#![allow(unused_imports, dead_code)]
#![deny(clippy::default_trait_access)]
use std::default;
// run-rustfix
-#![allow(unused_imports,dead_code)]
+#![allow(unused_imports, dead_code)]
#![deny(clippy::default_trait_access)]
use std::default;
n
}
-#[allow(clippy::many_single_char_names, clippy::double_parens)]
+#[allow(clippy::double_parens)]
#[allow(unused_variables, unused_parens)]
fn main() {
let a = 10;
n
}
-#[allow(clippy::many_single_char_names, clippy::double_parens)]
+#[allow(clippy::double_parens)]
#[allow(unused_variables, unused_parens)]
fn main() {
let a = 10;
}
}
+// https://github.com/rust-lang/rust-clippy/issues/7655
+
+pub struct SpecializedImpl2<T> {
+ v: Vec<T>,
+}
+
+impl Default for SpecializedImpl2<String> {
+ fn default() -> Self {
+ Self { v: Vec::new() }
+ }
+}
+
+// https://github.com/rust-lang/rust-clippy/issues/7654
+
+pub struct Color {
+ pub r: u8,
+ pub g: u8,
+ pub b: u8,
+}
+
+/// `#000000`
+impl Default for Color {
+ fn default() -> Self {
+ Color { r: 0, g: 0, b: 0 }
+ }
+}
+
+pub struct Color2 {
+ pub r: u8,
+ pub g: u8,
+ pub b: u8,
+}
+
+impl Default for Color2 {
+ /// `#000000`
+ fn default() -> Self {
+ Self { r: 0, g: 0, b: 0 }
+ }
+}
+
fn main() {}
#[rustfmt::skip]
#[warn(clippy::eq_op)]
-#[allow(clippy::identity_op, clippy::double_parens, clippy::many_single_char_names)]
+#[allow(clippy::identity_op, clippy::double_parens)]
#[allow(clippy::no_effect, unused_variables, clippy::unnecessary_operation, clippy::short_circuit_statement)]
#[allow(clippy::nonminimal_bool)]
#[allow(unused)]
unused,
clippy::no_effect,
clippy::redundant_closure_call,
- clippy::many_single_char_names,
clippy::needless_pass_by_value,
clippy::option_map_unit_fn
)]
clippy::needless_borrow
)]
-use std::path::PathBuf;
+use std::path::{Path, PathBuf};
macro_rules! mac {
() => {
fn main() {
let a = Some(1u8).map(foo);
- meta(foo);
let c = Some(1u8).map(|a| {1+2; foo}(a));
true.then(|| mac!()); // don't lint function in macro expansion
Some(1).map(closure_mac!()); // don't lint closure in macro expansion
let _: Option<Vec<u8>> = true.then(std::vec::Vec::new); // special case vec!
- let d = Some(1u8).map(|a| foo((|b| foo2(b))(a))); //is adjusted?
- all(&[1, 2, 3], &2, |x, y| below(x, y)); //is adjusted
+ let d = Some(1u8).map(|a| foo(foo2(a))); //is adjusted?
+ all(&[1, 2, 3], &2, below); //is adjusted
unsafe {
Some(1u8).map(|a| unsafe_fn(a)); // unsafe fn
}
// See #815
- let e = Some(1u8).map(|a| divergent(a));
+ let e = Some(1u8).map(divergent);
let e = Some(1u8).map(generic);
let e = Some(1u8).map(generic);
// See #515
fn test_redundant_closures_containing_method_calls() {
let i = 10;
let e = Some(TestStruct { some_ref: &i }).map(TestStruct::foo);
- let e = Some(TestStruct { some_ref: &i }).map(TestStruct::foo);
let e = Some(TestStruct { some_ref: &i }).map(TestTrait::trait_foo);
let e = Some(TestStruct { some_ref: &i }).map(|a| a.trait_foo_ref());
- let e = Some(TestStruct { some_ref: &i }).map(TestTrait::trait_foo);
- let e = Some(&mut vec![1, 2, 3]).map(std::vec::Vec::clear);
let e = Some(&mut vec![1, 2, 3]).map(std::vec::Vec::clear);
unsafe {
let e = Some(TestStruct { some_ref: &i }).map(|a| a.foo_unsafe());
}
let e = Some("str").map(std::string::ToString::to_string);
- let e = Some("str").map(str::to_string);
- let e = Some('a').map(char::to_uppercase);
let e = Some('a').map(char::to_uppercase);
let e: std::vec::Vec<usize> = vec!['a', 'b', 'c'].iter().map(|c| c.len_utf8()).collect();
let e: std::vec::Vec<char> = vec!['a', 'b', 'c'].iter().map(char::to_ascii_uppercase).collect();
- let e: std::vec::Vec<char> = vec!['a', 'b', 'c'].iter().map(char::to_ascii_uppercase).collect();
- let p = Some(PathBuf::new());
- let e = p.as_ref().and_then(|s| s.to_str());
+ let e = Some(PathBuf::new()).as_ref().and_then(|s| s.to_str());
let c = Some(TestStruct { some_ref: &i })
.as_ref()
.map(|c| c.to_ascii_uppercase());
t.iter().filter(|x| x.trait_foo_ref());
t.iter().map(|x| x.trait_foo_ref());
}
-
- let mut some = Some(|x| x * x);
- let arr = [Ok(1), Err(2)];
- let _: Vec<_> = arr.iter().map(|x| x.map_err(|e| some.take().unwrap()(e))).collect();
}
struct Thunk<T>(Box<dyn FnMut() -> T>);
thunk.unwrap()
}
-fn meta<F>(f: F)
-where
- F: Fn(u8),
-{
- f(1u8)
-}
-
fn foo(_: u8) {}
fn foo2(_: u8) -> u8 {
}
fn passes_fn_mut(mut x: Box<dyn FnMut()>) {
- requires_fn_once(|| x());
+ requires_fn_once(x);
}
fn requires_fn_once<T: FnOnce()>(_: T) {}
Some(1).map(&mut closure);
}
}
+
+fn late_bound_lifetimes() {
+ fn take_asref_path<P: AsRef<Path>>(path: P) {}
+
+ fn map_str<F>(thunk: F)
+ where
+ F: FnOnce(&str),
+ {
+ }
+
+ fn map_str_to_path<F>(thunk: F)
+ where
+ F: FnOnce(&str) -> &Path,
+ {
+ }
+ map_str(|s| take_asref_path(s));
+ map_str_to_path(std::convert::AsRef::as_ref);
+}
+
+mod type_param_bound {
+ trait Trait {
+ fn fun();
+ }
+
+ fn take<T: 'static>(_: T) {}
+
+ fn test<X: Trait>() {
+ // don't lint, but it's questionable that rust requires a cast
+ take(|| X::fun());
+ take(X::fun as fn());
+ }
+}
unused,
clippy::no_effect,
clippy::redundant_closure_call,
- clippy::many_single_char_names,
clippy::needless_pass_by_value,
clippy::option_map_unit_fn
)]
clippy::needless_borrow
)]
-use std::path::PathBuf;
+use std::path::{Path, PathBuf};
macro_rules! mac {
() => {
fn main() {
let a = Some(1u8).map(|a| foo(a));
- meta(|a| foo(a));
let c = Some(1u8).map(|a| {1+2; foo}(a));
true.then(|| mac!()); // don't lint function in macro expansion
Some(1).map(closure_mac!()); // don't lint closure in macro expansion
fn test_redundant_closures_containing_method_calls() {
let i = 10;
let e = Some(TestStruct { some_ref: &i }).map(|a| a.foo());
- let e = Some(TestStruct { some_ref: &i }).map(TestStruct::foo);
let e = Some(TestStruct { some_ref: &i }).map(|a| a.trait_foo());
let e = Some(TestStruct { some_ref: &i }).map(|a| a.trait_foo_ref());
- let e = Some(TestStruct { some_ref: &i }).map(TestTrait::trait_foo);
let e = Some(&mut vec![1, 2, 3]).map(|v| v.clear());
- let e = Some(&mut vec![1, 2, 3]).map(std::vec::Vec::clear);
unsafe {
let e = Some(TestStruct { some_ref: &i }).map(|a| a.foo_unsafe());
}
let e = Some("str").map(|s| s.to_string());
- let e = Some("str").map(str::to_string);
let e = Some('a').map(|s| s.to_uppercase());
- let e = Some('a').map(char::to_uppercase);
let e: std::vec::Vec<usize> = vec!['a', 'b', 'c'].iter().map(|c| c.len_utf8()).collect();
let e: std::vec::Vec<char> = vec!['a', 'b', 'c'].iter().map(|c| c.to_ascii_uppercase()).collect();
- let e: std::vec::Vec<char> = vec!['a', 'b', 'c'].iter().map(char::to_ascii_uppercase).collect();
- let p = Some(PathBuf::new());
- let e = p.as_ref().and_then(|s| s.to_str());
+ let e = Some(PathBuf::new()).as_ref().and_then(|s| s.to_str());
let c = Some(TestStruct { some_ref: &i })
.as_ref()
.map(|c| c.to_ascii_uppercase());
t.iter().filter(|x| x.trait_foo_ref());
t.iter().map(|x| x.trait_foo_ref());
}
-
- let mut some = Some(|x| x * x);
- let arr = [Ok(1), Err(2)];
- let _: Vec<_> = arr.iter().map(|x| x.map_err(|e| some.take().unwrap()(e))).collect();
}
struct Thunk<T>(Box<dyn FnMut() -> T>);
thunk.unwrap()
}
-fn meta<F>(f: F)
-where
- F: Fn(u8),
-{
- f(1u8)
-}
-
fn foo(_: u8) {}
fn foo2(_: u8) -> u8 {
Some(1).map(|n| closure(n));
}
}
+
+fn late_bound_lifetimes() {
+ fn take_asref_path<P: AsRef<Path>>(path: P) {}
+
+ fn map_str<F>(thunk: F)
+ where
+ F: FnOnce(&str),
+ {
+ }
+
+ fn map_str_to_path<F>(thunk: F)
+ where
+ F: FnOnce(&str) -> &Path,
+ {
+ }
+ map_str(|s| take_asref_path(s));
+ map_str_to_path(|s| s.as_ref());
+}
+
+mod type_param_bound {
+ trait Trait {
+ fn fun();
+ }
+
+ fn take<T: 'static>(_: T) {}
+
+ fn test<X: Trait>() {
+ // don't lint, but it's questionable that rust requires a cast
+ take(|| X::fun());
+ take(X::fun as fn());
+ }
+}
error: redundant closure
- --> $DIR/eta.rs:32:27
+ --> $DIR/eta.rs:31:27
|
LL | let a = Some(1u8).map(|a| foo(a));
| ^^^^^^^^^^ help: replace the closure with the function itself: `foo`
= note: `-D clippy::redundant-closure` implied by `-D warnings`
error: redundant closure
- --> $DIR/eta.rs:33:10
+ --> $DIR/eta.rs:35:40
|
-LL | meta(|a| foo(a));
- | ^^^^^^^^^^ help: replace the closure with the function itself: `foo`
+LL | let _: Option<Vec<u8>> = true.then(|| vec![]); // special case vec!
+ | ^^^^^^^^^ help: replace the closure with `Vec::new`: `std::vec::Vec::new`
error: redundant closure
- --> $DIR/eta.rs:37:40
+ --> $DIR/eta.rs:36:35
|
-LL | let _: Option<Vec<u8>> = true.then(|| vec![]); // special case vec!
- | ^^^^^^^^^ help: replace the closure with `Vec::new`: `std::vec::Vec::new`
+LL | let d = Some(1u8).map(|a| foo((|b| foo2(b))(a))); //is adjusted?
+ | ^^^^^^^^^^^^^ help: replace the closure with the function itself: `foo2`
error: this expression borrows a reference (`&u8`) that is immediately dereferenced by the compiler
- --> $DIR/eta.rs:39:21
+ --> $DIR/eta.rs:37:21
|
LL | all(&[1, 2, 3], &&2, |x, y| below(x, y)); //is adjusted
| ^^^ help: change this to: `&2`
= note: `-D clippy::needless-borrow` implied by `-D warnings`
error: redundant closure
- --> $DIR/eta.rs:46:27
+ --> $DIR/eta.rs:37:26
+ |
+LL | all(&[1, 2, 3], &&2, |x, y| below(x, y)); //is adjusted
+ | ^^^^^^^^^^^^^^^^^^ help: replace the closure with the function itself: `below`
+
+error: redundant closure
+ --> $DIR/eta.rs:43:27
+ |
+LL | let e = Some(1u8).map(|a| divergent(a));
+ | ^^^^^^^^^^^^^^^^ help: replace the closure with the function itself: `divergent`
+
+error: redundant closure
+ --> $DIR/eta.rs:44:27
|
LL | let e = Some(1u8).map(|a| generic(a));
| ^^^^^^^^^^^^^^ help: replace the closure with the function itself: `generic`
error: redundant closure
- --> $DIR/eta.rs:92:51
+ --> $DIR/eta.rs:90:51
|
LL | let e = Some(TestStruct { some_ref: &i }).map(|a| a.foo());
| ^^^^^^^^^^^ help: replace the closure with the method itself: `TestStruct::foo`
= note: `-D clippy::redundant-closure-for-method-calls` implied by `-D warnings`
error: redundant closure
- --> $DIR/eta.rs:94:51
+ --> $DIR/eta.rs:91:51
|
LL | let e = Some(TestStruct { some_ref: &i }).map(|a| a.trait_foo());
| ^^^^^^^^^^^^^^^^^ help: replace the closure with the method itself: `TestTrait::trait_foo`
error: redundant closure
- --> $DIR/eta.rs:97:42
+ --> $DIR/eta.rs:93:42
|
LL | let e = Some(&mut vec![1, 2, 3]).map(|v| v.clear());
| ^^^^^^^^^^^^^ help: replace the closure with the method itself: `std::vec::Vec::clear`
error: redundant closure
- --> $DIR/eta.rs:102:29
+ --> $DIR/eta.rs:97:29
|
LL | let e = Some("str").map(|s| s.to_string());
| ^^^^^^^^^^^^^^^^^ help: replace the closure with the method itself: `std::string::ToString::to_string`
error: redundant closure
- --> $DIR/eta.rs:104:27
+ --> $DIR/eta.rs:98:27
|
LL | let e = Some('a').map(|s| s.to_uppercase());
| ^^^^^^^^^^^^^^^^^^^^ help: replace the closure with the method itself: `char::to_uppercase`
error: redundant closure
- --> $DIR/eta.rs:107:65
+ --> $DIR/eta.rs:100:65
|
LL | let e: std::vec::Vec<char> = vec!['a', 'b', 'c'].iter().map(|c| c.to_ascii_uppercase()).collect();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace the closure with the method itself: `char::to_ascii_uppercase`
error: redundant closure
- --> $DIR/eta.rs:190:27
+ --> $DIR/eta.rs:163:22
+ |
+LL | requires_fn_once(|| x());
+ | ^^^^^^ help: replace the closure with the function itself: `x`
+
+error: redundant closure
+ --> $DIR/eta.rs:170:27
|
LL | let a = Some(1u8).map(|a| foo_ptr(a));
| ^^^^^^^^^^^^^^ help: replace the closure with the function itself: `foo_ptr`
error: redundant closure
- --> $DIR/eta.rs:195:27
+ --> $DIR/eta.rs:175:27
|
LL | let a = Some(1u8).map(|a| closure(a));
| ^^^^^^^^^^^^^^ help: replace the closure with the function itself: `closure`
error: redundant closure
- --> $DIR/eta.rs:227:28
+ --> $DIR/eta.rs:207:28
|
LL | x.into_iter().for_each(|x| add_to_res(x));
| ^^^^^^^^^^^^^^^^^ help: replace the closure with the function itself: `&mut add_to_res`
error: redundant closure
- --> $DIR/eta.rs:228:28
+ --> $DIR/eta.rs:208:28
|
LL | y.into_iter().for_each(|x| add_to_res(x));
| ^^^^^^^^^^^^^^^^^ help: replace the closure with the function itself: `&mut add_to_res`
error: redundant closure
- --> $DIR/eta.rs:229:28
+ --> $DIR/eta.rs:209:28
|
LL | z.into_iter().for_each(|x| add_to_res(x));
| ^^^^^^^^^^^^^^^^^ help: replace the closure with the function itself: `add_to_res`
error: redundant closure
- --> $DIR/eta.rs:236:21
+ --> $DIR/eta.rs:216:21
|
LL | Some(1).map(|n| closure(n));
| ^^^^^^^^^^^^^^ help: replace the closure with the function itself: `&mut closure`
-error: aborting due to 17 previous errors
+error: redundant closure
+ --> $DIR/eta.rs:235:21
+ |
+LL | map_str_to_path(|s| s.as_ref());
+ | ^^^^^^^^^^^^^^ help: replace the closure with the method itself: `std::convert::AsRef::as_ref`
+
+error: aborting due to 21 previous errors
#[allow(
unused_assignments,
unused_variables,
- clippy::many_single_char_names,
clippy::no_effect,
dead_code,
clippy::blacklisted_name
error: unsequenced read of `x`
- --> $DIR/eval_order_dependence.rs:17:9
+ --> $DIR/eval_order_dependence.rs:16:9
|
LL | } + x;
| ^
|
= note: `-D clippy::eval-order-dependence` implied by `-D warnings`
note: whether read occurs before this write depends on evaluation order
- --> $DIR/eval_order_dependence.rs:15:9
+ --> $DIR/eval_order_dependence.rs:14:9
|
LL | x = 1;
| ^^^^^
error: unsequenced read of `x`
- --> $DIR/eval_order_dependence.rs:20:5
+ --> $DIR/eval_order_dependence.rs:19:5
|
LL | x += {
| ^
|
note: whether read occurs before this write depends on evaluation order
- --> $DIR/eval_order_dependence.rs:21:9
+ --> $DIR/eval_order_dependence.rs:20:9
|
LL | x = 20;
| ^^^^^^
error: unsequenced read of `x`
- --> $DIR/eval_order_dependence.rs:33:12
+ --> $DIR/eval_order_dependence.rs:32:12
|
LL | a: x,
| ^
|
note: whether read occurs before this write depends on evaluation order
- --> $DIR/eval_order_dependence.rs:35:13
+ --> $DIR/eval_order_dependence.rs:34:13
|
LL | x = 6;
| ^^^^^
error: unsequenced read of `x`
- --> $DIR/eval_order_dependence.rs:42:9
+ --> $DIR/eval_order_dependence.rs:41:9
|
LL | x += {
| ^
|
note: whether read occurs before this write depends on evaluation order
- --> $DIR/eval_order_dependence.rs:43:13
+ --> $DIR/eval_order_dependence.rs:42:13
|
LL | x = 20;
| ^^^^^^
const BAD32_3: f32 = 0.1;
const BAD32_EDGE: f32 = 1.000_001;
- const BAD64_1: f64 = 0.123_456_789_012_345_66_f64;
- const BAD64_2: f64 = 0.123_456_789_012_345_66;
+ const BAD64_1: f64 = 0.123_456_789_012_345_67f64;
+ const BAD64_2: f64 = 0.123_456_789_012_345_67;
const BAD64_3: f64 = 0.1;
// Literal as param
let bad32_suf: f32 = 1.123_456_8_f32;
let bad32_inf = 1.123_456_8_f32;
- let bad64: f64 = 0.123_456_789_012_345_66;
- let bad64_suf: f64 = 0.123_456_789_012_345_66_f64;
- let bad64_inf = 0.123_456_789_012_345_66;
+ let bad64: f64 = 0.123_456_789_012_345_67;
+ let bad64_suf: f64 = 0.123_456_789_012_345_67f64;
+ let bad64_inf = 0.123_456_789_012_345_67;
// Vectors
let good_vec32: Vec<f32> = vec![0.123_456];
LL | const BAD32_EDGE: f32 = 1.000_000_9;
| ^^^^^^^^^^^ help: consider changing the type or truncating it to: `1.000_001`
-error: float has excessive precision
- --> $DIR/excessive_precision.rs:20:26
- |
-LL | const BAD64_1: f64 = 0.123_456_789_012_345_67f64;
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider changing the type or truncating it to: `0.123_456_789_012_345_66_f64`
-
-error: float has excessive precision
- --> $DIR/excessive_precision.rs:21:26
- |
-LL | const BAD64_2: f64 = 0.123_456_789_012_345_67;
- | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider changing the type or truncating it to: `0.123_456_789_012_345_66`
-
error: float has excessive precision
--> $DIR/excessive_precision.rs:22:26
|
LL | let bad32_inf = 1.123_456_789_f32;
| ^^^^^^^^^^^^^^^^^ help: consider changing the type or truncating it to: `1.123_456_8_f32`
-error: float has excessive precision
- --> $DIR/excessive_precision.rs:40:22
- |
-LL | let bad64: f64 = 0.123_456_789_012_345_67;
- | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider changing the type or truncating it to: `0.123_456_789_012_345_66`
-
-error: float has excessive precision
- --> $DIR/excessive_precision.rs:41:26
- |
-LL | let bad64_suf: f64 = 0.123_456_789_012_345_67f64;
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider changing the type or truncating it to: `0.123_456_789_012_345_66_f64`
-
-error: float has excessive precision
- --> $DIR/excessive_precision.rs:42:21
- |
-LL | let bad64_inf = 0.123_456_789_012_345_67;
- | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider changing the type or truncating it to: `0.123_456_789_012_345_66`
-
error: float has excessive precision
--> $DIR/excessive_precision.rs:48:36
|
LL | let bad_bige32: f32 = 1.123_456_788_888E-10;
| ^^^^^^^^^^^^^^^^^^^^^ help: consider changing the type or truncating it to: `1.123_456_8E-10`
-error: aborting due to 18 previous errors
+error: aborting due to 13 previous errors
// run-rustfix
-#![allow(unused_variables, clippy::many_single_char_names, clippy::clone_double_ref)]
+#![allow(unused_variables, clippy::clone_double_ref)]
#![warn(clippy::explicit_deref_methods)]
use std::ops::{Deref, DerefMut};
// run-rustfix
-#![allow(unused_variables, clippy::many_single_char_names, clippy::clone_double_ref)]
+#![allow(unused_variables, clippy::clone_double_ref)]
#![warn(clippy::explicit_deref_methods)]
use std::ops::{Deref, DerefMut};
#![deny(clippy::fallible_impl_from)]
+#![allow(clippy::if_then_panic)]
// docs example
struct Foo(i32);
error: consider implementing `TryFrom` instead
- --> $DIR/fallible_impl_from.rs:5:1
+ --> $DIR/fallible_impl_from.rs:6:1
|
LL | / impl From<String> for Foo {
LL | | fn from(s: String) -> Self {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
= help: `From` is intended for infallible conversions only. Use `TryFrom` if there's a possibility for the conversion to fail
note: potential failure(s)
- --> $DIR/fallible_impl_from.rs:7:13
+ --> $DIR/fallible_impl_from.rs:8:13
|
LL | Foo(s.parse().unwrap())
| ^^^^^^^^^^^^^^^^^^
error: consider implementing `TryFrom` instead
- --> $DIR/fallible_impl_from.rs:26:1
+ --> $DIR/fallible_impl_from.rs:27:1
|
LL | / impl From<usize> for Invalid {
LL | | fn from(i: usize) -> Invalid {
|
= help: `From` is intended for infallible conversions only. Use `TryFrom` if there's a possibility for the conversion to fail
note: potential failure(s)
- --> $DIR/fallible_impl_from.rs:29:13
+ --> $DIR/fallible_impl_from.rs:30:13
|
LL | panic!();
| ^^^^^^^^^
= note: this error originates in the macro `$crate::panic::panic_2015` (in Nightly builds, run with -Z macro-backtrace for more info)
error: consider implementing `TryFrom` instead
- --> $DIR/fallible_impl_from.rs:35:1
+ --> $DIR/fallible_impl_from.rs:36:1
|
LL | / impl From<Option<String>> for Invalid {
LL | | fn from(s: Option<String>) -> Invalid {
|
= help: `From` is intended for infallible conversions only. Use `TryFrom` if there's a possibility for the conversion to fail
note: potential failure(s)
- --> $DIR/fallible_impl_from.rs:37:17
+ --> $DIR/fallible_impl_from.rs:38:17
|
LL | let s = s.unwrap();
| ^^^^^^^^^^
= note: this error originates in the macro `$crate::panic::panic_2015` (in Nightly builds, run with -Z macro-backtrace for more info)
error: consider implementing `TryFrom` instead
- --> $DIR/fallible_impl_from.rs:53:1
+ --> $DIR/fallible_impl_from.rs:54:1
|
LL | / impl<'a> From<&'a mut <Box<u32> as ProjStrTrait>::ProjString> for Invalid {
LL | | fn from(s: &'a mut <Box<u32> as ProjStrTrait>::ProjString) -> Invalid {
|
= help: `From` is intended for infallible conversions only. Use `TryFrom` if there's a possibility for the conversion to fail
note: potential failure(s)
- --> $DIR/fallible_impl_from.rs:55:12
+ --> $DIR/fallible_impl_from.rs:56:12
|
LL | if s.parse::<u32>().ok().unwrap() != 42 {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
clippy::no_effect,
clippy::op_ref,
clippy::unnecessary_operation,
- clippy::cast_lossless,
- clippy::many_single_char_names
+ clippy::cast_lossless
)]
use std::ops::Add;
error: strict comparison of `f32` or `f64`
- --> $DIR/float_cmp.rs:58:5
+ --> $DIR/float_cmp.rs:57:5
|
LL | ONE as f64 != 2.0;
| ^^^^^^^^^^^^^^^^^ help: consider comparing them within some margin of error: `(ONE as f64 - 2.0).abs() > error_margin`
= note: `f32::EPSILON` and `f64::EPSILON` are available for the `error_margin`
error: strict comparison of `f32` or `f64`
- --> $DIR/float_cmp.rs:63:5
+ --> $DIR/float_cmp.rs:62:5
|
LL | x == 1.0;
| ^^^^^^^^ help: consider comparing them within some margin of error: `(x - 1.0).abs() < error_margin`
= note: `f32::EPSILON` and `f64::EPSILON` are available for the `error_margin`
error: strict comparison of `f32` or `f64`
- --> $DIR/float_cmp.rs:66:5
+ --> $DIR/float_cmp.rs:65:5
|
LL | twice(x) != twice(ONE as f64);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider comparing them within some margin of error: `(twice(x) - twice(ONE as f64)).abs() > error_margin`
= note: `f32::EPSILON` and `f64::EPSILON` are available for the `error_margin`
error: strict comparison of `f32` or `f64`
- --> $DIR/float_cmp.rs:86:5
+ --> $DIR/float_cmp.rs:85:5
|
LL | NON_ZERO_ARRAY[i] == NON_ZERO_ARRAY[j];
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider comparing them within some margin of error: `(NON_ZERO_ARRAY[i] - NON_ZERO_ARRAY[j]).abs() < error_margin`
= note: `f32::EPSILON` and `f64::EPSILON` are available for the `error_margin`
error: strict comparison of `f32` or `f64` arrays
- --> $DIR/float_cmp.rs:91:5
+ --> $DIR/float_cmp.rs:90:5
|
LL | a1 == a2;
| ^^^^^^^^
= note: `f32::EPSILON` and `f64::EPSILON` are available for the `error_margin`
error: strict comparison of `f32` or `f64`
- --> $DIR/float_cmp.rs:92:5
+ --> $DIR/float_cmp.rs:91:5
|
LL | a1[0] == a2[0];
| ^^^^^^^^^^^^^^ help: consider comparing them within some margin of error: `(a1[0] - a2[0]).abs() < error_margin`
clippy::unnecessary_mut_passed,
clippy::similar_names
)]
-#[allow(clippy::many_single_char_names, unused_variables)]
+#[allow(unused_variables)]
fn main() {
let mut vec = vec![1, 2, 3, 4];
clippy::unnecessary_mut_passed,
clippy::similar_names
)]
-#[allow(clippy::many_single_char_names, unused_variables)]
+#[allow(unused_variables)]
fn main() {
let mut vec = vec![1, 2, 3, 4];
+++ /dev/null
-// run-rustfix
-
-#![warn(clippy::if_let_some_result)]
-#![allow(dead_code)]
-
-fn str_to_int(x: &str) -> i32 {
- if let Ok(y) = x.parse() { y } else { 0 }
-}
-
-fn str_to_int_ok(x: &str) -> i32 {
- if let Ok(y) = x.parse() { y } else { 0 }
-}
-
-#[rustfmt::skip]
-fn strange_some_no_else(x: &str) -> i32 {
- {
- if let Ok(y) = x . parse() {
- return y;
- };
- 0
- }
-}
-
-fn negative() {
- while let Some(1) = "".parse().ok() {}
-}
-
-fn main() {}
+++ /dev/null
-// run-rustfix
-
-#![warn(clippy::if_let_some_result)]
-#![allow(dead_code)]
-
-fn str_to_int(x: &str) -> i32 {
- if let Some(y) = x.parse().ok() { y } else { 0 }
-}
-
-fn str_to_int_ok(x: &str) -> i32 {
- if let Ok(y) = x.parse() { y } else { 0 }
-}
-
-#[rustfmt::skip]
-fn strange_some_no_else(x: &str) -> i32 {
- {
- if let Some(y) = x . parse() . ok () {
- return y;
- };
- 0
- }
-}
-
-fn negative() {
- while let Some(1) = "".parse().ok() {}
-}
-
-fn main() {}
+++ /dev/null
-error: matching on `Some` with `ok()` is redundant
- --> $DIR/if_let_some_result.rs:7:5
- |
-LL | if let Some(y) = x.parse().ok() { y } else { 0 }
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
- |
- = note: `-D clippy::if-let-some-result` implied by `-D warnings`
-help: consider matching on `Ok(y)` and removing the call to `ok` instead
- |
-LL | if let Ok(y) = x.parse() { y } else { 0 }
- | ~~~~~~~~~~~~~~~~~~~~~~~~
-
-error: matching on `Some` with `ok()` is redundant
- --> $DIR/if_let_some_result.rs:17:9
- |
-LL | if let Some(y) = x . parse() . ok () {
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
- |
-help: consider matching on `Ok(y)` and removing the call to `ok` instead
- |
-LL | if let Ok(y) = x . parse() {
- | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-error: aborting due to 2 previous errors
-
--- /dev/null
+// run-rustfix
+#![warn(clippy::if_then_panic)]
+
+fn main() {
+ let a = vec![1, 2, 3];
+ let c = Some(2);
+ if !a.is_empty()
+ && a.len() == 3
+ && c != None
+ && !a.is_empty()
+ && a.len() == 3
+ && !a.is_empty()
+ && a.len() == 3
+ && !a.is_empty()
+ && a.len() == 3
+ {
+ panic!("qaqaq{:?}", a);
+ }
+ assert!(a.is_empty(), "qaqaq{:?}", a);
+ assert!(a.is_empty(), "qwqwq");
+ if a.len() == 3 {
+ println!("qwq");
+ println!("qwq");
+ println!("qwq");
+ }
+ if let Some(b) = c {
+ panic!("orz {}", b);
+ }
+ if a.len() == 3 {
+ panic!("qaqaq");
+ } else {
+ println!("qwq");
+ }
+}
--- /dev/null
+// run-rustfix
+#![warn(clippy::if_then_panic)]
+
+fn main() {
+ let a = vec![1, 2, 3];
+ let c = Some(2);
+ if !a.is_empty()
+ && a.len() == 3
+ && c != None
+ && !a.is_empty()
+ && a.len() == 3
+ && !a.is_empty()
+ && a.len() == 3
+ && !a.is_empty()
+ && a.len() == 3
+ {
+ panic!("qaqaq{:?}", a);
+ }
+ if !a.is_empty() {
+ panic!("qaqaq{:?}", a);
+ }
+ if !a.is_empty() {
+ panic!("qwqwq");
+ }
+ if a.len() == 3 {
+ println!("qwq");
+ println!("qwq");
+ println!("qwq");
+ }
+ if let Some(b) = c {
+ panic!("orz {}", b);
+ }
+ if a.len() == 3 {
+ panic!("qaqaq");
+ } else {
+ println!("qwq");
+ }
+}
--- /dev/null
+error: only a `panic!` in `if`-then statement
+ --> $DIR/if_then_panic.rs:19:5
+ |
+LL | / if !a.is_empty() {
+LL | | panic!("qaqaq{:?}", a);
+LL | | }
+ | |_____^ help: try: `assert!(a.is_empty(), "qaqaq{:?}", a);`
+ |
+ = note: `-D clippy::if-then-panic` implied by `-D warnings`
+
+error: only a `panic!` in `if`-then statement
+ --> $DIR/if_then_panic.rs:22:5
+ |
+LL | / if !a.is_empty() {
+LL | | panic!("qwqwq");
+LL | | }
+ | |_____^ help: try: `assert!(a.is_empty(), "qwqwq");`
+
+error: aborting due to 2 previous errors
+
unimplemented!()
}
-#[allow(clippy::many_single_char_names)]
fn immutable_condition() {
// Should warn when all vars mentioned are immutable
let y = 0;
error: variables in the condition are not mutated in the loop body
- --> $DIR/infinite_loop.rs:23:11
+ --> $DIR/infinite_loop.rs:22:11
|
LL | while y < 10 {
| ^^^^^^
= note: this may lead to an infinite or to a never running loop
error: variables in the condition are not mutated in the loop body
- --> $DIR/infinite_loop.rs:28:11
+ --> $DIR/infinite_loop.rs:27:11
|
LL | while y < 10 && x < 3 {
| ^^^^^^^^^^^^^^^
= note: this may lead to an infinite or to a never running loop
error: variables in the condition are not mutated in the loop body
- --> $DIR/infinite_loop.rs:35:11
+ --> $DIR/infinite_loop.rs:34:11
|
LL | while !cond {
| ^^^^^
= note: this may lead to an infinite or to a never running loop
error: variables in the condition are not mutated in the loop body
- --> $DIR/infinite_loop.rs:79:11
+ --> $DIR/infinite_loop.rs:78:11
|
LL | while i < 3 {
| ^^^^^
= note: this may lead to an infinite or to a never running loop
error: variables in the condition are not mutated in the loop body
- --> $DIR/infinite_loop.rs:84:11
+ --> $DIR/infinite_loop.rs:83:11
|
LL | while i < 3 && j > 0 {
| ^^^^^^^^^^^^^^
= note: this may lead to an infinite or to a never running loop
error: variables in the condition are not mutated in the loop body
- --> $DIR/infinite_loop.rs:88:11
+ --> $DIR/infinite_loop.rs:87:11
|
LL | while i < 3 {
| ^^^^^
= note: this may lead to an infinite or to a never running loop
error: variables in the condition are not mutated in the loop body
- --> $DIR/infinite_loop.rs:103:11
+ --> $DIR/infinite_loop.rs:102:11
|
LL | while i < 3 {
| ^^^^^
= note: this may lead to an infinite or to a never running loop
error: variables in the condition are not mutated in the loop body
- --> $DIR/infinite_loop.rs:108:11
+ --> $DIR/infinite_loop.rs:107:11
|
LL | while i < 3 {
| ^^^^^
= note: this may lead to an infinite or to a never running loop
error: variables in the condition are not mutated in the loop body
- --> $DIR/infinite_loop.rs:174:15
+ --> $DIR/infinite_loop.rs:173:15
|
LL | while self.count < n {
| ^^^^^^^^^^^^^^
= note: this may lead to an infinite or to a never running loop
error: variables in the condition are not mutated in the loop body
- --> $DIR/infinite_loop.rs:182:11
+ --> $DIR/infinite_loop.rs:181:11
|
LL | while y < 10 {
| ^^^^^^
= help: rewrite it as `if cond { loop { } }`
error: variables in the condition are not mutated in the loop body
- --> $DIR/infinite_loop.rs:189:11
+ --> $DIR/infinite_loop.rs:188:11
|
LL | while y < 10 {
| ^^^^^^
#![warn(clippy::inherent_to_string)]
#![deny(clippy::inherent_to_string_shadow_display)]
-#![allow(clippy::many_single_char_names)]
use std::fmt;
error: implementation of inherent method `to_string(&self) -> String` for type `A`
- --> $DIR/inherent_to_string.rs:21:5
+ --> $DIR/inherent_to_string.rs:20:5
|
LL | / fn to_string(&self) -> String {
LL | | "A.to_string()".to_string()
= help: implement trait `Display` for type `A` instead
error: type `C` implements inherent method `to_string(&self) -> String` which shadows the implementation of `Display`
- --> $DIR/inherent_to_string.rs:45:5
+ --> $DIR/inherent_to_string.rs:44:5
|
LL | / fn to_string(&self) -> String {
LL | | "C.to_string()".to_string()
--- /dev/null
+#![warn(clippy::iter_not_returning_iterator)]
+
+struct Data {
+ begin: u32,
+}
+
+struct Counter {
+ count: u32,
+}
+
+impl Data {
+ fn iter(&self) -> Counter {
+ todo!()
+ }
+
+ fn iter_mut(&self) -> Counter {
+ todo!()
+ }
+}
+
+struct Data2 {
+ begin: u32,
+}
+
+struct Counter2 {
+ count: u32,
+}
+
+impl Data2 {
+ fn iter(&self) -> Counter2 {
+ todo!()
+ }
+
+ fn iter_mut(&self) -> Counter2 {
+ todo!()
+ }
+}
+
+impl Iterator for Counter {
+ type Item = u32;
+
+ fn next(&mut self) -> Option<Self::Item> {
+ todo!()
+ }
+}
+
+fn main() {}
--- /dev/null
+error: this method is named `iter` but its return type does not implement `Iterator`
+ --> $DIR/iter_not_returning_iterator.rs:30:5
+ |
+LL | fn iter(&self) -> Counter2 {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = note: `-D clippy::iter-not-returning-iterator` implied by `-D warnings`
+
+error: this method is named `iter_mut` but its return type does not implement `Iterator`
+ --> $DIR/iter_not_returning_iterator.rs:34:5
+ |
+LL | fn iter_mut(&self) -> Counter2 {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to 2 previous errors
+
-#![allow(unused, clippy::many_single_char_names, clippy::diverging_sub_expression)]
+#![allow(unused, clippy::diverging_sub_expression)]
#![warn(clippy::logic_bug)]
fn main() {
// Don't lint, slices don't have `split_once`
let _ = [0, 1, 2].splitn(2, |&x| x == 1).nth(1).unwrap();
+
+ // `rsplitn` gives the results in the reverse order of `rsplit_once`
+ let _ = "key=value".rsplit_once('=').unwrap().1;
+ let _ = "key=value".rsplit_once('=').map_or("key=value", |x| x.0);
+ let _ = "key=value".rsplit_once('=').map(|x| x.1);
+ let (_, _) = "key=value".rsplit_once('=').map(|(x, y)| (y, x)).unwrap();
}
fn _msrv_1_51() {
// Don't lint, slices don't have `split_once`
let _ = [0, 1, 2].splitn(2, |&x| x == 1).nth(1).unwrap();
+
+ // `rsplitn` gives the results in the reverse order of `rsplit_once`
+ let _ = "key=value".rsplitn(2, '=').next().unwrap();
+ let _ = "key=value".rsplitn(2, '=').nth(1).unwrap();
+ let _ = "key=value".rsplitn(2, '=').nth(0);
+ let (_, _) = "key=value".rsplitn(2, '=').next_tuple().unwrap();
}
fn _msrv_1_51() {
LL | let _ = s.splitn(2, "key=value").skip(1).next()?;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `s.split_once("key=value")?.1`
+error: manual implementation of `rsplit_once`
+ --> $DIR/manual_split_once.rs:41:13
+ |
+LL | let _ = "key=value".rsplitn(2, '=').next().unwrap();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `"key=value".rsplit_once('=').unwrap().1`
+
+error: manual implementation of `rsplit_once`
+ --> $DIR/manual_split_once.rs:42:13
+ |
+LL | let _ = "key=value".rsplitn(2, '=').nth(1).unwrap();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `"key=value".rsplit_once('=').map_or("key=value", |x| x.0)`
+
+error: manual implementation of `rsplit_once`
+ --> $DIR/manual_split_once.rs:43:13
+ |
+LL | let _ = "key=value".rsplitn(2, '=').nth(0);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `"key=value".rsplit_once('=').map(|x| x.1)`
+
+error: manual implementation of `rsplit_once`
+ --> $DIR/manual_split_once.rs:44:18
+ |
+LL | let (_, _) = "key=value".rsplitn(2, '=').next_tuple().unwrap();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `"key=value".rsplit_once('=').map(|(x, y)| (y, x))`
+
error: manual implementation of `split_once`
- --> $DIR/manual_split_once.rs:49:13
+ --> $DIR/manual_split_once.rs:55:13
|
LL | let _ = "key=value".splitn(2, '=').nth(1).unwrap();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `"key=value".split_once('=').unwrap().1`
-error: aborting due to 13 previous errors
+error: aborting due to 17 previous errors
-#[warn(clippy::many_single_char_names)]
+#![warn(clippy::many_single_char_names)]
fn bla() {
let a: i32;
#![allow(clippy::let_underscore_drop)]
#![allow(clippy::missing_docs_in_private_items)]
#![allow(clippy::map_identity)]
+#![allow(clippy::redundant_closure)]
#![allow(clippy::unnecessary_wraps)]
#![feature(result_flattening)]
#![allow(clippy::let_underscore_drop)]
#![allow(clippy::missing_docs_in_private_items)]
#![allow(clippy::map_identity)]
+#![allow(clippy::redundant_closure)]
#![allow(clippy::unnecessary_wraps)]
#![feature(result_flattening)]
error: called `map(..).flatten()` on an `Iterator`
- --> $DIR/map_flatten.rs:17:46
+ --> $DIR/map_flatten.rs:18:46
|
LL | let _: Vec<_> = vec![5_i8; 6].into_iter().map(option_id).flatten().collect();
| ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `filter_map` instead: `.filter_map(option_id)`
= note: `-D clippy::map-flatten` implied by `-D warnings`
error: called `map(..).flatten()` on an `Iterator`
- --> $DIR/map_flatten.rs:18:46
+ --> $DIR/map_flatten.rs:19:46
|
LL | let _: Vec<_> = vec![5_i8; 6].into_iter().map(option_id_ref).flatten().collect();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `filter_map` instead: `.filter_map(option_id_ref)`
error: called `map(..).flatten()` on an `Iterator`
- --> $DIR/map_flatten.rs:19:46
+ --> $DIR/map_flatten.rs:20:46
|
LL | let _: Vec<_> = vec![5_i8; 6].into_iter().map(option_id_closure).flatten().collect();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `filter_map` instead: `.filter_map(option_id_closure)`
error: called `map(..).flatten()` on an `Iterator`
- --> $DIR/map_flatten.rs:20:46
+ --> $DIR/map_flatten.rs:21:46
|
LL | let _: Vec<_> = vec![5_i8; 6].into_iter().map(|x| x.checked_add(1)).flatten().collect();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `filter_map` instead: `.filter_map(|x| x.checked_add(1))`
error: called `map(..).flatten()` on an `Iterator`
- --> $DIR/map_flatten.rs:23:46
+ --> $DIR/map_flatten.rs:24:46
|
LL | let _: Vec<_> = vec![5_i8; 6].into_iter().map(|x| 0..x).flatten().collect();
| ^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `flat_map` instead: `.flat_map(|x| 0..x)`
error: called `map(..).flatten()` on an `Option`
- --> $DIR/map_flatten.rs:26:39
+ --> $DIR/map_flatten.rs:27:39
|
LL | let _: Option<_> = (Some(Some(1))).map(|x| x).flatten();
| ^^^^^^^^^^^^^^^^^^^^^ help: try using `and_then` instead: `.and_then(|x| x)`
error: called `map(..).flatten()` on an `Result`
- --> $DIR/map_flatten.rs:29:41
+ --> $DIR/map_flatten.rs:30:41
|
LL | let _: Result<_, &str> = (Ok(Ok(1))).map(|x| x).flatten();
| ^^^^^^^^^^^^^^^^^^^^^ help: try using `and_then` instead: `.and_then(|x| x)`
--- /dev/null
+// run-rustfix
+
+#![warn(clippy::match_result_ok)]
+#![allow(clippy::boxed_local)]
+#![allow(dead_code)]
+
+// Checking `if` cases
+
+fn str_to_int(x: &str) -> i32 {
+ if let Ok(y) = x.parse() { y } else { 0 }
+}
+
+fn str_to_int_ok(x: &str) -> i32 {
+ if let Ok(y) = x.parse() { y } else { 0 }
+}
+
+#[rustfmt::skip]
+fn strange_some_no_else(x: &str) -> i32 {
+ {
+ if let Ok(y) = x . parse() {
+ return y;
+ };
+ 0
+ }
+}
+
+// Checking `while` cases
+
+struct Wat {
+ counter: i32,
+}
+
+impl Wat {
+ fn next(&mut self) -> Result<i32, &str> {
+ self.counter += 1;
+ if self.counter < 5 {
+ Ok(self.counter)
+ } else {
+ Err("Oh no")
+ }
+ }
+}
+
+fn base_1(x: i32) {
+ let mut wat = Wat { counter: x };
+ while let Ok(a) = wat.next() {
+ println!("{}", a);
+ }
+}
+
+fn base_2(x: i32) {
+ let mut wat = Wat { counter: x };
+ while let Ok(a) = wat.next() {
+ println!("{}", a);
+ }
+}
+
+fn base_3(test_func: Box<Result<i32, &str>>) {
+ // Expected to stay as is
+ while let Some(_b) = test_func.ok() {}
+}
+
+fn main() {}
--- /dev/null
+// run-rustfix
+
+#![warn(clippy::match_result_ok)]
+#![allow(clippy::boxed_local)]
+#![allow(dead_code)]
+
+// Checking `if` cases
+
+fn str_to_int(x: &str) -> i32 {
+ if let Some(y) = x.parse().ok() { y } else { 0 }
+}
+
+fn str_to_int_ok(x: &str) -> i32 {
+ if let Ok(y) = x.parse() { y } else { 0 }
+}
+
+#[rustfmt::skip]
+fn strange_some_no_else(x: &str) -> i32 {
+ {
+ if let Some(y) = x . parse() . ok () {
+ return y;
+ };
+ 0
+ }
+}
+
+// Checking `while` cases
+
+struct Wat {
+ counter: i32,
+}
+
+impl Wat {
+ fn next(&mut self) -> Result<i32, &str> {
+ self.counter += 1;
+ if self.counter < 5 {
+ Ok(self.counter)
+ } else {
+ Err("Oh no")
+ }
+ }
+}
+
+fn base_1(x: i32) {
+ let mut wat = Wat { counter: x };
+ while let Some(a) = wat.next().ok() {
+ println!("{}", a);
+ }
+}
+
+fn base_2(x: i32) {
+ let mut wat = Wat { counter: x };
+ while let Ok(a) = wat.next() {
+ println!("{}", a);
+ }
+}
+
+fn base_3(test_func: Box<Result<i32, &str>>) {
+ // Expected to stay as is
+ while let Some(_b) = test_func.ok() {}
+}
+
+fn main() {}
--- /dev/null
+error: matching on `Some` with `ok()` is redundant
+ --> $DIR/match_result_ok.rs:10:5
+ |
+LL | if let Some(y) = x.parse().ok() { y } else { 0 }
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = note: `-D clippy::match-result-ok` implied by `-D warnings`
+help: consider matching on `Ok(y)` and removing the call to `ok` instead
+ |
+LL | if let Ok(y) = x.parse() { y } else { 0 }
+ | ~~~~~~~~~~~~~~~~~~~~~~~~
+
+error: matching on `Some` with `ok()` is redundant
+ --> $DIR/match_result_ok.rs:20:9
+ |
+LL | if let Some(y) = x . parse() . ok () {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+help: consider matching on `Ok(y)` and removing the call to `ok` instead
+ |
+LL | if let Ok(y) = x . parse() {
+ | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+error: matching on `Some` with `ok()` is redundant
+ --> $DIR/match_result_ok.rs:46:5
+ |
+LL | while let Some(a) = wat.next().ok() {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+help: consider matching on `Ok(a)` and removing the call to `ok` instead
+ |
+LL | while let Ok(a) = wat.next() {
+ | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+error: aborting due to 3 previous errors
+
// run-rustfix
#![warn(clippy::match_single_binding)]
-#![allow(unused_variables, clippy::many_single_char_names, clippy::toplevel_ref_arg)]
+#![allow(unused_variables, clippy::toplevel_ref_arg)]
struct Point {
x: i32,
// run-rustfix
#![warn(clippy::match_single_binding)]
-#![allow(unused_variables, clippy::many_single_char_names, clippy::toplevel_ref_arg)]
+#![allow(unused_variables, clippy::toplevel_ref_arg)]
struct Point {
x: i32,
-use std::collections::{HashMap, HashSet};
+use std::cell::Cell;
+use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet};
use std::hash::{Hash, Hasher};
+use std::rc::Rc;
use std::sync::atomic::{AtomicUsize, Ordering::Relaxed};
+use std::sync::Arc;
struct Key(AtomicUsize);
fn this_is_ok(_m: &mut HashMap<usize, Key>) {}
+// Raw pointers are hashed by the address they point to, so it doesn't matter if they point to a
+// type with interior mutability. See:
+// - clippy issue: https://github.com/rust-lang/rust-clippy/issues/6745
+// - std lib: https://github.com/rust-lang/rust/blob/1.54.0/library/core/src/hash/mod.rs#L717-L736
+// So these are OK:
+fn raw_ptr_is_ok(_m: &mut HashMap<*const Key, ()>) {}
+fn raw_mut_ptr_is_ok(_m: &mut HashMap<*mut Key, ()>) {}
+
#[allow(unused)]
trait Trait {
type AssociatedType;
- fn trait_fn(&self, set: std::collections::HashSet<Self::AssociatedType>);
+ fn trait_fn(&self, set: HashSet<Self::AssociatedType>);
}
fn generics_are_ok_too<K>(_m: &mut HashSet<K>) {
tuples::<Key>(&mut HashMap::new());
tuples::<()>(&mut HashMap::new());
tuples_bad::<()>(&mut HashMap::new());
+
+ raw_ptr_is_ok(&mut HashMap::new());
+ raw_mut_ptr_is_ok(&mut HashMap::new());
+
+ let _map = HashMap::<Cell<usize>, usize>::new();
+ let _map = HashMap::<&mut Cell<usize>, usize>::new();
+ let _map = HashMap::<&mut usize, usize>::new();
+ // Collection types from `std` who's impl of `Hash` or `Ord` delegate their type parameters
+ let _map = HashMap::<Vec<Cell<usize>>, usize>::new();
+ let _map = HashMap::<BTreeMap<Cell<usize>, ()>, usize>::new();
+ let _map = HashMap::<BTreeMap<(), Cell<usize>>, usize>::new();
+ let _map = HashMap::<BTreeSet<Cell<usize>>, usize>::new();
+ let _map = HashMap::<Option<Cell<usize>>, usize>::new();
+ let _map = HashMap::<Option<Vec<Cell<usize>>>, usize>::new();
+ let _map = HashMap::<Result<&mut usize, ()>, usize>::new();
+ // Smart pointers from `std` who's impl of `Hash` or `Ord` delegate their type parameters
+ let _map = HashMap::<Box<Cell<usize>>, usize>::new();
+ let _map = HashMap::<Rc<Cell<usize>>, usize>::new();
+ let _map = HashMap::<Arc<Cell<usize>>, usize>::new();
}
error: mutable key type
- --> $DIR/mut_key.rs:27:32
+ --> $DIR/mut_key.rs:30:32
|
LL | fn should_not_take_this_arg(m: &mut HashMap<Key, usize>, _n: usize) -> HashSet<Key> {
| ^^^^^^^^^^^^^^^^^^^^^^^^
= note: `-D clippy::mutable-key-type` implied by `-D warnings`
error: mutable key type
- --> $DIR/mut_key.rs:27:72
+ --> $DIR/mut_key.rs:30:72
|
LL | fn should_not_take_this_arg(m: &mut HashMap<Key, usize>, _n: usize) -> HashSet<Key> {
| ^^^^^^^^^^^^
error: mutable key type
- --> $DIR/mut_key.rs:28:5
+ --> $DIR/mut_key.rs:31:5
|
LL | let _other: HashMap<Key, bool> = HashMap::new();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: mutable key type
- --> $DIR/mut_key.rs:47:22
+ --> $DIR/mut_key.rs:58:22
|
LL | fn tuples_bad<U>(_m: &mut HashMap<(Key, U), bool>) {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-error: aborting due to 4 previous errors
+error: mutable key type
+ --> $DIR/mut_key.rs:70:5
+ |
+LL | let _map = HashMap::<Cell<usize>, usize>::new();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: mutable key type
+ --> $DIR/mut_key.rs:71:5
+ |
+LL | let _map = HashMap::<&mut Cell<usize>, usize>::new();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: mutable key type
+ --> $DIR/mut_key.rs:72:5
+ |
+LL | let _map = HashMap::<&mut usize, usize>::new();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: mutable key type
+ --> $DIR/mut_key.rs:74:5
+ |
+LL | let _map = HashMap::<Vec<Cell<usize>>, usize>::new();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: mutable key type
+ --> $DIR/mut_key.rs:75:5
+ |
+LL | let _map = HashMap::<BTreeMap<Cell<usize>, ()>, usize>::new();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: mutable key type
+ --> $DIR/mut_key.rs:76:5
+ |
+LL | let _map = HashMap::<BTreeMap<(), Cell<usize>>, usize>::new();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: mutable key type
+ --> $DIR/mut_key.rs:77:5
+ |
+LL | let _map = HashMap::<BTreeSet<Cell<usize>>, usize>::new();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: mutable key type
+ --> $DIR/mut_key.rs:78:5
+ |
+LL | let _map = HashMap::<Option<Cell<usize>>, usize>::new();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: mutable key type
+ --> $DIR/mut_key.rs:79:5
+ |
+LL | let _map = HashMap::<Option<Vec<Cell<usize>>>, usize>::new();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: mutable key type
+ --> $DIR/mut_key.rs:80:5
+ |
+LL | let _map = HashMap::<Result<&mut usize, ()>, usize>::new();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: mutable key type
+ --> $DIR/mut_key.rs:82:5
+ |
+LL | let _map = HashMap::<Box<Cell<usize>>, usize>::new();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: mutable key type
+ --> $DIR/mut_key.rs:83:5
+ |
+LL | let _map = HashMap::<Rc<Cell<usize>>, usize>::new();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: mutable key type
+ --> $DIR/mut_key.rs:84:5
+ |
+LL | let _map = HashMap::<Arc<Cell<usize>>, usize>::new();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to 17 previous errors
// run-rustfix
-#![allow(clippy::needless_borrowed_reference)]
-
-fn x(y: &i32) -> i32 {
- *y
-}
-
#[warn(clippy::all, clippy::needless_borrow)]
#[allow(unused_variables)]
fn main() {
let a = 5;
- let b = x(&a);
- let c = x(&a);
+ let _ = x(&a); // no warning
+ let _ = x(&a); // warn
+
+ let mut b = 5;
+ mut_ref(&mut b); // no warning
+ mut_ref(&mut b); // warn
+
let s = &String::from("hi");
let s_ident = f(&s); // should not error, because `&String` implements Copy, but `String` does not
let g_val = g(&Vec::new()); // should not error, because `&Vec<T>` derefs to `&[T]`
};
}
+#[allow(clippy::needless_borrowed_reference)]
+fn x(y: &i32) -> i32 {
+ *y
+}
+
+fn mut_ref(y: &mut i32) {
+ *y = 5;
+}
+
fn f<T: Copy>(y: &T) -> T {
*y
}
// run-rustfix
-#![allow(clippy::needless_borrowed_reference)]
-
-fn x(y: &i32) -> i32 {
- *y
-}
-
#[warn(clippy::all, clippy::needless_borrow)]
#[allow(unused_variables)]
fn main() {
let a = 5;
- let b = x(&a);
- let c = x(&&a);
+ let _ = x(&a); // no warning
+ let _ = x(&&a); // warn
+
+ let mut b = 5;
+ mut_ref(&mut b); // no warning
+ mut_ref(&mut &mut b); // warn
+
let s = &String::from("hi");
let s_ident = f(&s); // should not error, because `&String` implements Copy, but `String` does not
let g_val = g(&Vec::new()); // should not error, because `&Vec<T>` derefs to `&[T]`
};
}
+#[allow(clippy::needless_borrowed_reference)]
+fn x(y: &i32) -> i32 {
+ *y
+}
+
+fn mut_ref(y: &mut i32) {
+ *y = 5;
+}
+
fn f<T: Copy>(y: &T) -> T {
*y
}
error: this expression borrows a reference (`&i32`) that is immediately dereferenced by the compiler
- --> $DIR/needless_borrow.rs:14:15
+ --> $DIR/needless_borrow.rs:8:15
|
-LL | let c = x(&&a);
+LL | let _ = x(&&a); // warn
| ^^^ help: change this to: `&a`
|
= note: `-D clippy::needless-borrow` implied by `-D warnings`
+error: this expression borrows a reference (`&mut i32`) that is immediately dereferenced by the compiler
+ --> $DIR/needless_borrow.rs:12:13
+ |
+LL | mut_ref(&mut &mut b); // warn
+ | ^^^^^^^^^^^ help: change this to: `&mut b`
+
error: this expression borrows a reference (`&i32`) that is immediately dereferenced by the compiler
- --> $DIR/needless_borrow.rs:27:15
+ --> $DIR/needless_borrow.rs:26:15
|
LL | 46 => &&a,
| ^^^ help: change this to: `&a`
-error: aborting due to 2 previous errors
+error: aborting due to 3 previous errors
dead_code,
clippy::single_match,
clippy::redundant_pattern_matching,
- clippy::many_single_char_names,
clippy::option_option,
clippy::redundant_clone
)]
error: this argument is passed by value, but not consumed in the function body
- --> $DIR/needless_pass_by_value.rs:18:23
+ --> $DIR/needless_pass_by_value.rs:17:23
|
LL | fn foo<T: Default>(v: Vec<T>, w: Vec<T>, mut x: Vec<T>, y: Vec<T>) -> Vec<T> {
| ^^^^^^ help: consider changing the type to: `&[T]`
= note: `-D clippy::needless-pass-by-value` implied by `-D warnings`
error: this argument is passed by value, but not consumed in the function body
- --> $DIR/needless_pass_by_value.rs:32:11
+ --> $DIR/needless_pass_by_value.rs:31:11
|
LL | fn bar(x: String, y: Wrapper) {
| ^^^^^^ help: consider changing the type to: `&str`
error: this argument is passed by value, but not consumed in the function body
- --> $DIR/needless_pass_by_value.rs:32:22
+ --> $DIR/needless_pass_by_value.rs:31:22
|
LL | fn bar(x: String, y: Wrapper) {
| ^^^^^^^ help: consider taking a reference instead: `&Wrapper`
error: this argument is passed by value, but not consumed in the function body
- --> $DIR/needless_pass_by_value.rs:38:71
+ --> $DIR/needless_pass_by_value.rs:37:71
|
LL | fn test_borrow_trait<T: Borrow<str>, U: AsRef<str>, V>(t: T, u: U, v: V) {
| ^ help: consider taking a reference instead: `&V`
error: this argument is passed by value, but not consumed in the function body
- --> $DIR/needless_pass_by_value.rs:50:18
+ --> $DIR/needless_pass_by_value.rs:49:18
|
LL | fn test_match(x: Option<Option<String>>, y: Option<Option<String>>) {
| ^^^^^^^^^^^^^^^^^^^^^^ help: consider taking a reference instead: `&Option<Option<String>>`
error: this argument is passed by value, but not consumed in the function body
- --> $DIR/needless_pass_by_value.rs:63:24
+ --> $DIR/needless_pass_by_value.rs:62:24
|
LL | fn test_destructure(x: Wrapper, y: Wrapper, z: Wrapper) {
| ^^^^^^^ help: consider taking a reference instead: `&Wrapper`
error: this argument is passed by value, but not consumed in the function body
- --> $DIR/needless_pass_by_value.rs:63:36
+ --> $DIR/needless_pass_by_value.rs:62:36
|
LL | fn test_destructure(x: Wrapper, y: Wrapper, z: Wrapper) {
| ^^^^^^^ help: consider taking a reference instead: `&Wrapper`
error: this argument is passed by value, but not consumed in the function body
- --> $DIR/needless_pass_by_value.rs:79:49
+ --> $DIR/needless_pass_by_value.rs:78:49
|
LL | fn test_blanket_ref<T: Foo, S: Serialize>(_foo: T, _serializable: S) {}
| ^ help: consider taking a reference instead: `&T`
error: this argument is passed by value, but not consumed in the function body
- --> $DIR/needless_pass_by_value.rs:81:18
+ --> $DIR/needless_pass_by_value.rs:80:18
|
LL | fn issue_2114(s: String, t: String, u: Vec<i32>, v: Vec<i32>) {
| ^^^^^^ help: consider taking a reference instead: `&String`
error: this argument is passed by value, but not consumed in the function body
- --> $DIR/needless_pass_by_value.rs:81:29
+ --> $DIR/needless_pass_by_value.rs:80:29
|
LL | fn issue_2114(s: String, t: String, u: Vec<i32>, v: Vec<i32>) {
| ^^^^^^
| ~~~~~~~~~~~~~
error: this argument is passed by value, but not consumed in the function body
- --> $DIR/needless_pass_by_value.rs:81:40
+ --> $DIR/needless_pass_by_value.rs:80:40
|
LL | fn issue_2114(s: String, t: String, u: Vec<i32>, v: Vec<i32>) {
| ^^^^^^^^ help: consider taking a reference instead: `&Vec<i32>`
error: this argument is passed by value, but not consumed in the function body
- --> $DIR/needless_pass_by_value.rs:81:53
+ --> $DIR/needless_pass_by_value.rs:80:53
|
LL | fn issue_2114(s: String, t: String, u: Vec<i32>, v: Vec<i32>) {
| ^^^^^^^^
| ~~~~~~~~~~~~
error: this argument is passed by value, but not consumed in the function body
- --> $DIR/needless_pass_by_value.rs:94:12
+ --> $DIR/needless_pass_by_value.rs:93:12
|
LL | s: String,
| ^^^^^^ help: consider changing the type to: `&str`
error: this argument is passed by value, but not consumed in the function body
- --> $DIR/needless_pass_by_value.rs:95:12
+ --> $DIR/needless_pass_by_value.rs:94:12
|
LL | t: String,
| ^^^^^^ help: consider taking a reference instead: `&String`
error: this argument is passed by value, but not consumed in the function body
- --> $DIR/needless_pass_by_value.rs:104:23
+ --> $DIR/needless_pass_by_value.rs:103:23
|
LL | fn baz(&self, _u: U, _s: Self) {}
| ^ help: consider taking a reference instead: `&U`
error: this argument is passed by value, but not consumed in the function body
- --> $DIR/needless_pass_by_value.rs:104:30
+ --> $DIR/needless_pass_by_value.rs:103:30
|
LL | fn baz(&self, _u: U, _s: Self) {}
| ^^^^ help: consider taking a reference instead: `&Self`
error: this argument is passed by value, but not consumed in the function body
- --> $DIR/needless_pass_by_value.rs:126:24
+ --> $DIR/needless_pass_by_value.rs:125:24
|
LL | fn bar_copy(x: u32, y: CopyWrapper) {
| ^^^^^^^^^^^ help: consider taking a reference instead: `&CopyWrapper`
|
help: consider marking this type as `Copy`
- --> $DIR/needless_pass_by_value.rs:124:1
+ --> $DIR/needless_pass_by_value.rs:123:1
|
LL | struct CopyWrapper(u32);
| ^^^^^^^^^^^^^^^^^^^^^^^^
error: this argument is passed by value, but not consumed in the function body
- --> $DIR/needless_pass_by_value.rs:132:29
+ --> $DIR/needless_pass_by_value.rs:131:29
|
LL | fn test_destructure_copy(x: CopyWrapper, y: CopyWrapper, z: CopyWrapper) {
| ^^^^^^^^^^^ help: consider taking a reference instead: `&CopyWrapper`
|
help: consider marking this type as `Copy`
- --> $DIR/needless_pass_by_value.rs:124:1
+ --> $DIR/needless_pass_by_value.rs:123:1
|
LL | struct CopyWrapper(u32);
| ^^^^^^^^^^^^^^^^^^^^^^^^
error: this argument is passed by value, but not consumed in the function body
- --> $DIR/needless_pass_by_value.rs:132:45
+ --> $DIR/needless_pass_by_value.rs:131:45
|
LL | fn test_destructure_copy(x: CopyWrapper, y: CopyWrapper, z: CopyWrapper) {
| ^^^^^^^^^^^ help: consider taking a reference instead: `&CopyWrapper`
|
help: consider marking this type as `Copy`
- --> $DIR/needless_pass_by_value.rs:124:1
+ --> $DIR/needless_pass_by_value.rs:123:1
|
LL | struct CopyWrapper(u32);
| ^^^^^^^^^^^^^^^^^^^^^^^^
error: this argument is passed by value, but not consumed in the function body
- --> $DIR/needless_pass_by_value.rs:132:61
+ --> $DIR/needless_pass_by_value.rs:131:61
|
LL | fn test_destructure_copy(x: CopyWrapper, y: CopyWrapper, z: CopyWrapper) {
| ^^^^^^^^^^^ help: consider taking a reference instead: `&CopyWrapper`
|
help: consider marking this type as `Copy`
- --> $DIR/needless_pass_by_value.rs:124:1
+ --> $DIR/needless_pass_by_value.rs:123:1
|
LL | struct CopyWrapper(u32);
| ^^^^^^^^^^^^^^^^^^^^^^^^
error: this argument is passed by value, but not consumed in the function body
- --> $DIR/needless_pass_by_value.rs:144:40
+ --> $DIR/needless_pass_by_value.rs:143:40
|
LL | fn some_fun<'b, S: Bar<'b, ()>>(_item: S) {}
| ^ help: consider taking a reference instead: `&S`
error: this argument is passed by value, but not consumed in the function body
- --> $DIR/needless_pass_by_value.rs:149:20
+ --> $DIR/needless_pass_by_value.rs:148:20
|
LL | fn more_fun(_item: impl Club<'static, i32>) {}
| ^^^^^^^^^^^^^^^^^^^^^^^ help: consider taking a reference instead: `&impl Club<'static, i32>`
// run-rustfix
// edition:2018
+#![feature(let_else)]
#![allow(unused)]
-#![allow(
- clippy::if_same_then_else,
- clippy::single_match,
- clippy::branches_sharing_code,
- clippy::needless_bool
-)]
+#![allow(clippy::if_same_then_else, clippy::single_match, clippy::needless_bool)]
#![warn(clippy::needless_return)]
macro_rules! the_answer {
needed_return!(0);
}
+fn let_else() {
+ let Some(1) = Some(1) else { return };
+}
+
fn main() {}
// run-rustfix
// edition:2018
+#![feature(let_else)]
#![allow(unused)]
-#![allow(
- clippy::if_same_then_else,
- clippy::single_match,
- clippy::branches_sharing_code,
- clippy::needless_bool
-)]
+#![allow(clippy::if_same_then_else, clippy::single_match, clippy::needless_bool)]
#![warn(clippy::needless_return)]
macro_rules! the_answer {
needed_return!(0);
}
+fn let_else() {
+ let Some(1) = Some(1) else { return };
+}
+
fn main() {}
error: unneeded `return` statement
- --> $DIR/needless_return.rs:24:5
+ --> $DIR/needless_return.rs:20:5
|
LL | return true;
| ^^^^^^^^^^^^ help: remove `return`: `true`
= note: `-D clippy::needless-return` implied by `-D warnings`
error: unneeded `return` statement
- --> $DIR/needless_return.rs:28:5
+ --> $DIR/needless_return.rs:24:5
|
LL | return true;
| ^^^^^^^^^^^^ help: remove `return`: `true`
error: unneeded `return` statement
- --> $DIR/needless_return.rs:33:9
+ --> $DIR/needless_return.rs:29:9
|
LL | return true;
| ^^^^^^^^^^^^ help: remove `return`: `true`
error: unneeded `return` statement
- --> $DIR/needless_return.rs:35:9
+ --> $DIR/needless_return.rs:31:9
|
LL | return false;
| ^^^^^^^^^^^^^ help: remove `return`: `false`
error: unneeded `return` statement
- --> $DIR/needless_return.rs:41:17
+ --> $DIR/needless_return.rs:37:17
|
LL | true => return false,
| ^^^^^^^^^^^^ help: remove `return`: `false`
error: unneeded `return` statement
- --> $DIR/needless_return.rs:43:13
+ --> $DIR/needless_return.rs:39:13
|
LL | return true;
| ^^^^^^^^^^^^ help: remove `return`: `true`
error: unneeded `return` statement
- --> $DIR/needless_return.rs:50:9
+ --> $DIR/needless_return.rs:46:9
|
LL | return true;
| ^^^^^^^^^^^^ help: remove `return`: `true`
error: unneeded `return` statement
- --> $DIR/needless_return.rs:52:16
+ --> $DIR/needless_return.rs:48:16
|
LL | let _ = || return true;
| ^^^^^^^^^^^ help: remove `return`: `true`
error: unneeded `return` statement
- --> $DIR/needless_return.rs:60:5
+ --> $DIR/needless_return.rs:56:5
|
LL | return;
| ^^^^^^^ help: remove `return`
error: unneeded `return` statement
- --> $DIR/needless_return.rs:65:9
+ --> $DIR/needless_return.rs:61:9
|
LL | return;
| ^^^^^^^ help: remove `return`
error: unneeded `return` statement
- --> $DIR/needless_return.rs:67:9
+ --> $DIR/needless_return.rs:63:9
|
LL | return;
| ^^^^^^^ help: remove `return`
error: unneeded `return` statement
- --> $DIR/needless_return.rs:74:14
+ --> $DIR/needless_return.rs:70:14
|
LL | _ => return,
| ^^^^^^ help: replace `return` with an empty block: `{}`
error: unneeded `return` statement
- --> $DIR/needless_return.rs:89:9
+ --> $DIR/needless_return.rs:85:9
|
LL | return String::from("test");
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove `return`: `String::from("test")`
error: unneeded `return` statement
- --> $DIR/needless_return.rs:91:9
+ --> $DIR/needless_return.rs:87:9
|
LL | return String::new();
| ^^^^^^^^^^^^^^^^^^^^^ help: remove `return`: `String::new()`
error: unneeded `return` statement
- --> $DIR/needless_return.rs:112:32
+ --> $DIR/needless_return.rs:108:32
|
LL | bar.unwrap_or_else(|_| return)
| ^^^^^^ help: replace `return` with an empty block: `{}`
error: unneeded `return` statement
- --> $DIR/needless_return.rs:117:13
+ --> $DIR/needless_return.rs:113:13
|
LL | return;
| ^^^^^^^ help: remove `return`
error: unneeded `return` statement
- --> $DIR/needless_return.rs:119:20
+ --> $DIR/needless_return.rs:115:20
|
LL | let _ = || return;
| ^^^^^^ help: replace `return` with an empty block: `{}`
error: unneeded `return` statement
- --> $DIR/needless_return.rs:125:32
+ --> $DIR/needless_return.rs:121:32
|
LL | res.unwrap_or_else(|_| return Foo)
| ^^^^^^^^^^ help: remove `return`: `Foo`
error: unneeded `return` statement
- --> $DIR/needless_return.rs:134:5
+ --> $DIR/needless_return.rs:130:5
|
LL | return true;
| ^^^^^^^^^^^^ help: remove `return`: `true`
error: unneeded `return` statement
- --> $DIR/needless_return.rs:138:5
+ --> $DIR/needless_return.rs:134:5
|
LL | return true;
| ^^^^^^^^^^^^ help: remove `return`: `true`
error: unneeded `return` statement
- --> $DIR/needless_return.rs:143:9
+ --> $DIR/needless_return.rs:139:9
|
LL | return true;
| ^^^^^^^^^^^^ help: remove `return`: `true`
error: unneeded `return` statement
- --> $DIR/needless_return.rs:145:9
+ --> $DIR/needless_return.rs:141:9
|
LL | return false;
| ^^^^^^^^^^^^^ help: remove `return`: `false`
error: unneeded `return` statement
- --> $DIR/needless_return.rs:151:17
+ --> $DIR/needless_return.rs:147:17
|
LL | true => return false,
| ^^^^^^^^^^^^ help: remove `return`: `false`
error: unneeded `return` statement
- --> $DIR/needless_return.rs:153:13
+ --> $DIR/needless_return.rs:149:13
|
LL | return true;
| ^^^^^^^^^^^^ help: remove `return`: `true`
error: unneeded `return` statement
- --> $DIR/needless_return.rs:160:9
+ --> $DIR/needless_return.rs:156:9
|
LL | return true;
| ^^^^^^^^^^^^ help: remove `return`: `true`
error: unneeded `return` statement
- --> $DIR/needless_return.rs:162:16
+ --> $DIR/needless_return.rs:158:16
|
LL | let _ = || return true;
| ^^^^^^^^^^^ help: remove `return`: `true`
error: unneeded `return` statement
- --> $DIR/needless_return.rs:170:5
+ --> $DIR/needless_return.rs:166:5
|
LL | return;
| ^^^^^^^ help: remove `return`
error: unneeded `return` statement
- --> $DIR/needless_return.rs:175:9
+ --> $DIR/needless_return.rs:171:9
|
LL | return;
| ^^^^^^^ help: remove `return`
error: unneeded `return` statement
- --> $DIR/needless_return.rs:177:9
+ --> $DIR/needless_return.rs:173:9
|
LL | return;
| ^^^^^^^ help: remove `return`
error: unneeded `return` statement
- --> $DIR/needless_return.rs:184:14
+ --> $DIR/needless_return.rs:180:14
|
LL | _ => return,
| ^^^^^^ help: replace `return` with an empty block: `{}`
error: unneeded `return` statement
- --> $DIR/needless_return.rs:199:9
+ --> $DIR/needless_return.rs:195:9
|
LL | return String::from("test");
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove `return`: `String::from("test")`
error: unneeded `return` statement
- --> $DIR/needless_return.rs:201:9
+ --> $DIR/needless_return.rs:197:9
|
LL | return String::new();
| ^^^^^^^^^^^^^^^^^^^^^ help: remove `return`: `String::new()`
-#![allow(unused, clippy::many_single_char_names, clippy::diverging_sub_expression)]
+#![allow(unused, clippy::diverging_sub_expression)]
#![warn(clippy::nonminimal_bool)]
fn main() {
-#![allow(unused, clippy::many_single_char_names, clippy::diverging_sub_expression)]
+#![allow(unused, clippy::diverging_sub_expression)]
#![warn(clippy::nonminimal_bool)]
fn methods_with_negation() {
#![allow(unused_variables, clippy::blacklisted_name)]
#![warn(clippy::op_ref)]
-#![allow(clippy::many_single_char_names)]
use std::collections::HashSet;
use std::ops::BitAnd;
error: needlessly taken reference of both operands
- --> $DIR/op_ref.rs:12:15
+ --> $DIR/op_ref.rs:11:15
|
LL | let foo = &5 - &6;
| ^^^^^^^
| ~ ~
error: taken reference of right operand
- --> $DIR/op_ref.rs:57:13
+ --> $DIR/op_ref.rs:56:13
|
LL | let z = x & &y;
| ^^^^--
-#![allow(clippy::many_single_char_names)]
#![warn(clippy::overflow_check_conditional)]
fn main() {
error: you are trying to use classic C overflow conditions that will fail in Rust
- --> $DIR/overflow_check_conditional.rs:8:8
+ --> $DIR/overflow_check_conditional.rs:7:8
|
LL | if a + b < a {}
| ^^^^^^^^^
= note: `-D clippy::overflow-check-conditional` implied by `-D warnings`
error: you are trying to use classic C overflow conditions that will fail in Rust
- --> $DIR/overflow_check_conditional.rs:9:8
+ --> $DIR/overflow_check_conditional.rs:8:8
|
LL | if a > a + b {}
| ^^^^^^^^^
error: you are trying to use classic C overflow conditions that will fail in Rust
- --> $DIR/overflow_check_conditional.rs:10:8
+ --> $DIR/overflow_check_conditional.rs:9:8
|
LL | if a + b < b {}
| ^^^^^^^^^
error: you are trying to use classic C overflow conditions that will fail in Rust
- --> $DIR/overflow_check_conditional.rs:11:8
+ --> $DIR/overflow_check_conditional.rs:10:8
|
LL | if b > a + b {}
| ^^^^^^^^^
error: you are trying to use classic C underflow conditions that will fail in Rust
- --> $DIR/overflow_check_conditional.rs:12:8
+ --> $DIR/overflow_check_conditional.rs:11:8
|
LL | if a - b > b {}
| ^^^^^^^^^
error: you are trying to use classic C underflow conditions that will fail in Rust
- --> $DIR/overflow_check_conditional.rs:13:8
+ --> $DIR/overflow_check_conditional.rs:12:8
|
LL | if b < a - b {}
| ^^^^^^^^^
error: you are trying to use classic C underflow conditions that will fail in Rust
- --> $DIR/overflow_check_conditional.rs:14:8
+ --> $DIR/overflow_check_conditional.rs:13:8
|
LL | if a - b > a {}
| ^^^^^^^^^
error: you are trying to use classic C underflow conditions that will fail in Rust
- --> $DIR/overflow_check_conditional.rs:15:8
+ --> $DIR/overflow_check_conditional.rs:14:8
|
LL | if a < a - b {}
| ^^^^^^^^^
-#![allow(unused, clippy::many_single_char_names, clippy::redundant_clone)]
+#![allow(
+ unused,
+ clippy::many_single_char_names,
+ clippy::redundant_clone,
+ clippy::if_then_panic
+)]
#![warn(clippy::ptr_arg)]
use std::borrow::Cow;
error: writing `&Vec<_>` instead of `&[_]` involves one more reference and cannot be used with non-Vec-based slices
- --> $DIR/ptr_arg.rs:7:14
+ --> $DIR/ptr_arg.rs:12:14
|
LL | fn do_vec(x: &Vec<i64>) {
| ^^^^^^^^^ help: change this to: `&[i64]`
= note: `-D clippy::ptr-arg` implied by `-D warnings`
error: writing `&String` instead of `&str` involves a new object where a slice will do
- --> $DIR/ptr_arg.rs:16:14
+ --> $DIR/ptr_arg.rs:21:14
|
LL | fn do_str(x: &String) {
| ^^^^^^^ help: change this to: `&str`
error: writing `&PathBuf` instead of `&Path` involves a new object where a slice will do
- --> $DIR/ptr_arg.rs:25:15
+ --> $DIR/ptr_arg.rs:30:15
|
LL | fn do_path(x: &PathBuf) {
| ^^^^^^^^ help: change this to: `&Path`
error: writing `&Vec<_>` instead of `&[_]` involves one more reference and cannot be used with non-Vec-based slices
- --> $DIR/ptr_arg.rs:38:18
+ --> $DIR/ptr_arg.rs:43:18
|
LL | fn do_vec(x: &Vec<i64>);
| ^^^^^^^^^ help: change this to: `&[i64]`
error: writing `&Vec<_>` instead of `&[_]` involves one more reference and cannot be used with non-Vec-based slices
- --> $DIR/ptr_arg.rs:51:14
+ --> $DIR/ptr_arg.rs:56:14
|
LL | fn cloned(x: &Vec<u8>) -> Vec<u8> {
| ^^^^^^^^
|
error: writing `&String` instead of `&str` involves a new object where a slice will do
- --> $DIR/ptr_arg.rs:60:18
+ --> $DIR/ptr_arg.rs:65:18
|
LL | fn str_cloned(x: &String) -> String {
| ^^^^^^^
|
error: writing `&PathBuf` instead of `&Path` involves a new object where a slice will do
- --> $DIR/ptr_arg.rs:68:19
+ --> $DIR/ptr_arg.rs:73:19
|
LL | fn path_cloned(x: &PathBuf) -> PathBuf {
| ^^^^^^^^
|
error: writing `&String` instead of `&str` involves a new object where a slice will do
- --> $DIR/ptr_arg.rs:76:44
+ --> $DIR/ptr_arg.rs:81:44
|
LL | fn false_positive_capacity(x: &Vec<u8>, y: &String) {
| ^^^^^^^
| ~
error: using a reference to `Cow` is not recommended
- --> $DIR/ptr_arg.rs:90:25
+ --> $DIR/ptr_arg.rs:95:25
|
LL | fn test_cow_with_ref(c: &Cow<[i32]>) {}
| ^^^^^^^^^^^ help: change this to: `&[i32]`
error: writing `&Vec<_>` instead of `&[_]` involves one more reference and cannot be used with non-Vec-based slices
- --> $DIR/ptr_arg.rs:143:21
+ --> $DIR/ptr_arg.rs:148:21
|
LL | fn foo_vec(vec: &Vec<u8>) {
| ^^^^^^^^
| ~~~~~~~~~~~~~~
error: writing `&PathBuf` instead of `&Path` involves a new object where a slice will do
- --> $DIR/ptr_arg.rs:148:23
+ --> $DIR/ptr_arg.rs:153:23
|
LL | fn foo_path(path: &PathBuf) {
| ^^^^^^^^
| ~~~~~~~~~~~~~~~~~~
error: writing `&PathBuf` instead of `&Path` involves a new object where a slice will do
- --> $DIR/ptr_arg.rs:153:21
+ --> $DIR/ptr_arg.rs:158:21
|
LL | fn foo_str(str: &PathBuf) {
| ^^^^^^^^
// run-rustfix
#![warn(clippy::repeat_once)]
-#[allow(unused, clippy::many_single_char_names, clippy::redundant_clone)]
+#[allow(unused, clippy::redundant_clone)]
fn main() {
const N: usize = 1;
let s = "str";
// run-rustfix
#![warn(clippy::repeat_once)]
-#[allow(unused, clippy::many_single_char_names, clippy::redundant_clone)]
+#[allow(unused, clippy::redundant_clone)]
fn main() {
const N: usize = 1;
let s = "str";
--- /dev/null
+#![warn(clippy::same_name_method)]
+#![allow(dead_code, non_camel_case_types)]
+
+trait T1 {
+ fn foo() {}
+}
+
+trait T2 {
+ fn foo() {}
+}
+
+mod should_lint {
+
+ mod test_basic_case {
+ use crate::T1;
+
+ struct S;
+
+ impl S {
+ fn foo() {}
+ }
+
+ impl T1 for S {
+ fn foo() {}
+ }
+ }
+
+ mod test_derive {
+
+ #[derive(Clone)]
+ struct S;
+
+ impl S {
+ fn clone() {}
+ }
+ }
+
+ mod with_generic {
+ use crate::T1;
+
+ struct S<U>(U);
+
+ impl<U> S<U> {
+ fn foo() {}
+ }
+
+ impl<U: Copy> T1 for S<U> {
+ fn foo() {}
+ }
+ }
+
+ mod default_method {
+ use crate::T1;
+
+ struct S;
+
+ impl S {
+ fn foo() {}
+ }
+
+ impl T1 for S {}
+ }
+
+ mod mulitply_conflicit_trait {
+ use crate::{T1, T2};
+
+ struct S;
+
+ impl S {
+ fn foo() {}
+ }
+
+ impl T1 for S {}
+
+ impl T2 for S {}
+ }
+}
+
+mod should_not_lint {
+
+ mod not_lint_two_trait_method {
+ use crate::{T1, T2};
+
+ struct S;
+
+ impl T1 for S {
+ fn foo() {}
+ }
+
+ impl T2 for S {
+ fn foo() {}
+ }
+ }
+
+ mod only_lint_on_method {
+ trait T3 {
+ type foo;
+ }
+
+ struct S;
+
+ impl S {
+ fn foo() {}
+ }
+ impl T3 for S {
+ type foo = usize;
+ }
+ }
+}
+
+fn main() {}
--- /dev/null
+error: method's name is same to an existing method in a trait
+ --> $DIR/same_name_method.rs:20:13
+ |
+LL | fn foo() {}
+ | ^^^^^^^^^^^
+ |
+ = note: `-D clippy::same-name-method` implied by `-D warnings`
+note: existing `foo` defined here
+ --> $DIR/same_name_method.rs:24:13
+ |
+LL | fn foo() {}
+ | ^^^^^^^^^^^
+
+error: method's name is same to an existing method in a trait
+ --> $DIR/same_name_method.rs:44:13
+ |
+LL | fn foo() {}
+ | ^^^^^^^^^^^
+ |
+note: existing `foo` defined here
+ --> $DIR/same_name_method.rs:48:13
+ |
+LL | fn foo() {}
+ | ^^^^^^^^^^^
+
+error: method's name is same to an existing method in a trait
+ --> $DIR/same_name_method.rs:58:13
+ |
+LL | fn foo() {}
+ | ^^^^^^^^^^^
+ |
+note: existing `foo` defined here
+ --> $DIR/same_name_method.rs:61:9
+ |
+LL | impl T1 for S {}
+ | ^^^^^^^^^^^^^^^^
+
+error: method's name is same to an existing method in a trait
+ --> $DIR/same_name_method.rs:70:13
+ |
+LL | fn foo() {}
+ | ^^^^^^^^^^^
+ |
+note: existing `foo` defined here
+ --> $DIR/same_name_method.rs:73:9
+ |
+LL | impl T1 for S {}
+ | ^^^^^^^^^^^^^^^^
+
+error: method's name is same to an existing method in a trait
+ --> $DIR/same_name_method.rs:34:13
+ |
+LL | fn clone() {}
+ | ^^^^^^^^^^^^^
+ |
+note: existing `clone` defined here
+ --> $DIR/same_name_method.rs:30:18
+ |
+LL | #[derive(Clone)]
+ | ^^^^^
+ = note: this error originates in the derive macro `Clone` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: aborting due to 5 previous errors
+
#![warn(clippy::semicolon_if_nothing_returned)]
+#![allow(clippy::redundant_closure)]
#![feature(label_break_value)]
fn get_unit() {}
use std::ptr;
let mut s = MaybeUninit::<String>::uninit();
- let _d = || unsafe {
- ptr::drop_in_place(s.as_mut_ptr())
+ let _d = || unsafe {
+ ptr::drop_in_place(s.as_mut_ptr())
};
}
error: consider adding a `;` to the last statement for consistent formatting
- --> $DIR/semicolon_if_nothing_returned.rs:8:5
+ --> $DIR/semicolon_if_nothing_returned.rs:9:5
|
LL | println!("Hello")
| ^^^^^^^^^^^^^^^^^ help: add a `;` here: `println!("Hello");`
= note: `-D clippy::semicolon-if-nothing-returned` implied by `-D warnings`
error: consider adding a `;` to the last statement for consistent formatting
- --> $DIR/semicolon_if_nothing_returned.rs:12:5
+ --> $DIR/semicolon_if_nothing_returned.rs:13:5
|
LL | get_unit()
| ^^^^^^^^^^ help: add a `;` here: `get_unit();`
error: consider adding a `;` to the last statement for consistent formatting
- --> $DIR/semicolon_if_nothing_returned.rs:17:5
+ --> $DIR/semicolon_if_nothing_returned.rs:18:5
|
LL | y = x + 1
| ^^^^^^^^^ help: add a `;` here: `y = x + 1;`
error: consider adding a `;` to the last statement for consistent formatting
- --> $DIR/semicolon_if_nothing_returned.rs:23:9
+ --> $DIR/semicolon_if_nothing_returned.rs:24:9
|
LL | hello()
| ^^^^^^^ help: add a `;` here: `hello();`
error: consider adding a `;` to the last statement for consistent formatting
- --> $DIR/semicolon_if_nothing_returned.rs:34:9
+ --> $DIR/semicolon_if_nothing_returned.rs:35:9
|
-LL | ptr::drop_in_place(s.as_mut_ptr())
+LL | ptr::drop_in_place(s.as_mut_ptr())
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: add a `;` here: `ptr::drop_in_place(s.as_mut_ptr());`
error: aborting due to 5 previous errors
+// aux-build:proc_macro_suspicious_else_formatting.rs
+
#![warn(clippy::suspicious_else_formatting)]
+extern crate proc_macro_suspicious_else_formatting;
+use proc_macro_suspicious_else_formatting::DeriveBadSpan;
+
fn foo() -> bool {
true
}
{
}
}
+
+// #7650 - Don't lint. Proc-macro using bad spans for `if` expressions.
+#[derive(DeriveBadSpan)]
+struct _Foo(u32, u32);
error: this looks like an `else {..}` but the `else` is missing
- --> $DIR/suspicious_else_formatting.rs:11:6
+ --> $DIR/suspicious_else_formatting.rs:16:6
|
LL | } {
| ^
= note: to remove this lint, add the missing `else` or add a new line before the next block
error: this looks like an `else if` but the `else` is missing
- --> $DIR/suspicious_else_formatting.rs:15:6
+ --> $DIR/suspicious_else_formatting.rs:20:6
|
LL | } if foo() {
| ^
= note: to remove this lint, add the missing `else` or add a new line before the second `if`
error: this looks like an `else if` but the `else` is missing
- --> $DIR/suspicious_else_formatting.rs:22:10
+ --> $DIR/suspicious_else_formatting.rs:27:10
|
LL | } if foo() {
| ^
= note: to remove this lint, add the missing `else` or add a new line before the second `if`
error: this looks like an `else if` but the `else` is missing
- --> $DIR/suspicious_else_formatting.rs:30:10
+ --> $DIR/suspicious_else_formatting.rs:35:10
|
LL | } if foo() {
| ^
= note: to remove this lint, add the missing `else` or add a new line before the second `if`
error: this is an `else {..}` but the formatting might hide it
- --> $DIR/suspicious_else_formatting.rs:39:6
+ --> $DIR/suspicious_else_formatting.rs:44:6
|
LL | } else
| ______^
= note: to remove this lint, remove the `else` or remove the new line between `else` and `{..}`
error: this is an `else if` but the formatting might hide it
- --> $DIR/suspicious_else_formatting.rs:51:6
+ --> $DIR/suspicious_else_formatting.rs:56:6
|
LL | } else
| ______^
= note: to remove this lint, remove the `else` or remove the new line between `else` and `if`
error: this is an `else if` but the formatting might hide it
- --> $DIR/suspicious_else_formatting.rs:56:6
+ --> $DIR/suspicious_else_formatting.rs:61:6
|
LL | }
| ______^
= note: to remove this lint, remove the `else` or remove the new line between `else` and `if`
error: this is an `else {..}` but the formatting might hide it
- --> $DIR/suspicious_else_formatting.rs:83:6
+ --> $DIR/suspicious_else_formatting.rs:88:6
|
LL | }
| ______^
= note: to remove this lint, remove the `else` or remove the new line between `else` and `{..}`
error: this is an `else {..}` but the formatting might hide it
- --> $DIR/suspicious_else_formatting.rs:91:6
+ --> $DIR/suspicious_else_formatting.rs:96:6
|
LL | }
| ______^
// normalize-stderr-test "\(limit: \d+ byte\)" -> "(limit: N byte)"
#![deny(clippy::trivially_copy_pass_by_ref)]
-#![allow(
- clippy::many_single_char_names,
- clippy::blacklisted_name,
- clippy::redundant_field_names
-)]
+#![allow(clippy::blacklisted_name, clippy::redundant_field_names)]
#[derive(Copy, Clone)]
struct Foo(u32);
error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte)
- --> $DIR/trivially_copy_pass_by_ref.rs:51:11
+ --> $DIR/trivially_copy_pass_by_ref.rs:47:11
|
LL | fn bad(x: &u32, y: &Foo, z: &Baz) {}
| ^^^^ help: consider passing by value instead: `u32`
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte)
- --> $DIR/trivially_copy_pass_by_ref.rs:51:20
+ --> $DIR/trivially_copy_pass_by_ref.rs:47:20
|
LL | fn bad(x: &u32, y: &Foo, z: &Baz) {}
| ^^^^ help: consider passing by value instead: `Foo`
error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte)
- --> $DIR/trivially_copy_pass_by_ref.rs:51:29
+ --> $DIR/trivially_copy_pass_by_ref.rs:47:29
|
LL | fn bad(x: &u32, y: &Foo, z: &Baz) {}
| ^^^^ help: consider passing by value instead: `Baz`
error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte)
- --> $DIR/trivially_copy_pass_by_ref.rs:58:12
+ --> $DIR/trivially_copy_pass_by_ref.rs:54:12
|
LL | fn bad(&self, x: &u32, y: &Foo, z: &Baz) {}
| ^^^^^ help: consider passing by value instead: `self`
error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte)
- --> $DIR/trivially_copy_pass_by_ref.rs:58:22
+ --> $DIR/trivially_copy_pass_by_ref.rs:54:22
|
LL | fn bad(&self, x: &u32, y: &Foo, z: &Baz) {}
| ^^^^ help: consider passing by value instead: `u32`
error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte)
- --> $DIR/trivially_copy_pass_by_ref.rs:58:31
+ --> $DIR/trivially_copy_pass_by_ref.rs:54:31
|
LL | fn bad(&self, x: &u32, y: &Foo, z: &Baz) {}
| ^^^^ help: consider passing by value instead: `Foo`
error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte)
- --> $DIR/trivially_copy_pass_by_ref.rs:58:40
+ --> $DIR/trivially_copy_pass_by_ref.rs:54:40
|
LL | fn bad(&self, x: &u32, y: &Foo, z: &Baz) {}
| ^^^^ help: consider passing by value instead: `Baz`
error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte)
- --> $DIR/trivially_copy_pass_by_ref.rs:60:16
+ --> $DIR/trivially_copy_pass_by_ref.rs:56:16
|
LL | fn bad2(x: &u32, y: &Foo, z: &Baz) {}
| ^^^^ help: consider passing by value instead: `u32`
error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte)
- --> $DIR/trivially_copy_pass_by_ref.rs:60:25
+ --> $DIR/trivially_copy_pass_by_ref.rs:56:25
|
LL | fn bad2(x: &u32, y: &Foo, z: &Baz) {}
| ^^^^ help: consider passing by value instead: `Foo`
error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte)
- --> $DIR/trivially_copy_pass_by_ref.rs:60:34
+ --> $DIR/trivially_copy_pass_by_ref.rs:56:34
|
LL | fn bad2(x: &u32, y: &Foo, z: &Baz) {}
| ^^^^ help: consider passing by value instead: `Baz`
error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte)
- --> $DIR/trivially_copy_pass_by_ref.rs:62:35
+ --> $DIR/trivially_copy_pass_by_ref.rs:58:35
|
LL | fn bad_issue7518(self, other: &Self) {}
| ^^^^^ help: consider passing by value instead: `Self`
error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte)
- --> $DIR/trivially_copy_pass_by_ref.rs:74:16
+ --> $DIR/trivially_copy_pass_by_ref.rs:70:16
|
LL | fn bad2(x: &u32, y: &Foo, z: &Baz) {}
| ^^^^ help: consider passing by value instead: `u32`
error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte)
- --> $DIR/trivially_copy_pass_by_ref.rs:74:25
+ --> $DIR/trivially_copy_pass_by_ref.rs:70:25
|
LL | fn bad2(x: &u32, y: &Foo, z: &Baz) {}
| ^^^^ help: consider passing by value instead: `Foo`
error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte)
- --> $DIR/trivially_copy_pass_by_ref.rs:74:34
+ --> $DIR/trivially_copy_pass_by_ref.rs:70:34
|
LL | fn bad2(x: &u32, y: &Foo, z: &Baz) {}
| ^^^^ help: consider passing by value instead: `Baz`
error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte)
- --> $DIR/trivially_copy_pass_by_ref.rs:78:34
+ --> $DIR/trivially_copy_pass_by_ref.rs:74:34
|
LL | fn trait_method(&self, _foo: &Foo);
| ^^^^ help: consider passing by value instead: `Foo`
error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte)
- --> $DIR/trivially_copy_pass_by_ref.rs:110:21
+ --> $DIR/trivially_copy_pass_by_ref.rs:106:21
|
LL | fn foo_never(x: &i32) {
| ^^^^ help: consider passing by value instead: `i32`
error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte)
- --> $DIR/trivially_copy_pass_by_ref.rs:115:15
+ --> $DIR/trivially_copy_pass_by_ref.rs:111:15
|
LL | fn foo(x: &i32) {
| ^^^^ help: consider passing by value instead: `i32`
// Used in outer loop, needs &mut
let mut it = 1..40;
while let Some(n) = it.next() {
- for m in &mut it {
+ for m in it.by_ref() {
if m % 10 == 0 {
break;
}
// Used after the loop, needs &mut.
let mut it = 1..40;
- for m in &mut it {
+ for m in it.by_ref() {
if m % 10 == 0 {
break;
}
let mut it = 1..40;
let mut opt = Some(0);
while let Some(n) = opt.take().or_else(|| it.next()) {
- for m in &mut it {
+ for m in it.by_ref() {
if n % 10 == 0 {
break;
}
impl<T: Iterator<Item = u32>> S<T> {
fn f(&mut self) -> Option<u32> {
// Used as a field.
- for i in &mut self.0 {
+ for i in self.0.by_ref() {
if !(3..=7).contains(&i) {
return Some(i);
}
}
}
// This one is fine, a different field is borrowed
- for i in &mut self.0.0.0 {
+ for i in self.0.0.0.by_ref() {
if i == 1 {
return self.0.1.take();
} else {
// Needs &mut, field of the iterator is accessed after the loop
let mut it = S2(1..40, 0);
- for n in &mut it {
+ for n in it.by_ref() {
if n == 0 {
break;
}
let mut it = 0..10;
let mut x = || {
// Needs &mut, the closure can be called multiple times
- for x in &mut it {
+ for x in it.by_ref() {
if x % 2 == 0 {
break;
}
let mut it = 0..10;
let it = &mut it;
// Needs to reborrow `it` as the binding isn't mutable
- for x in &mut *it {
+ for x in it.by_ref() {
if x % 2 == 0 {
break;
}
let mut it = 0..10;
let it = S(&mut it);
// Needs to reborrow `it.0` as the binding isn't mutable
- for x in &mut *it.0 {
+ for x in it.0.by_ref() {
if x % 2 == 0 {
break;
}
--> $DIR/while_let_on_iterator.rs:191:9
|
LL | while let Some(m) = it.next() {
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for m in &mut it`
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for m in it.by_ref()`
error: this loop could be written as a `for` loop
--> $DIR/while_let_on_iterator.rs:202:5
--> $DIR/while_let_on_iterator.rs:222:9
|
LL | while let Some(m) = it.next() {
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for m in &mut it`
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for m in it.by_ref()`
error: this loop could be written as a `for` loop
--> $DIR/while_let_on_iterator.rs:239:9
|
LL | while let Some(m) = it.next() {
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for m in &mut it`
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for m in it.by_ref()`
error: this loop could be written as a `for` loop
--> $DIR/while_let_on_iterator.rs:254:13
|
LL | while let Some(i) = self.0.next() {
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for i in &mut self.0`
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for i in self.0.by_ref()`
error: manual `!RangeInclusive::contains` implementation
--> $DIR/while_let_on_iterator.rs:255:20
--> $DIR/while_let_on_iterator.rs:286:13
|
LL | while let Some(i) = self.0.0.0.next() {
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for i in &mut self.0.0.0`
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for i in self.0.0.0.by_ref()`
error: this loop could be written as a `for` loop
--> $DIR/while_let_on_iterator.rs:315:5
|
LL | while let Some(n) = it.next() {
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for n in &mut it`
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for n in it.by_ref()`
error: this loop could be written as a `for` loop
--> $DIR/while_let_on_iterator.rs:327:9
|
LL | while let Some(x) = it.next() {
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for x in &mut it`
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for x in it.by_ref()`
error: this loop could be written as a `for` loop
--> $DIR/while_let_on_iterator.rs:341:5
|
LL | while let Some(x) = it.next() {
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for x in &mut *it`
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for x in it.by_ref()`
error: this loop could be written as a `for` loop
--> $DIR/while_let_on_iterator.rs:352:5
|
LL | while let Some(x) = it.0.next() {
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for x in &mut *it.0`
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for x in it.0.by_ref()`
error: this loop could be written as a `for` loop
--> $DIR/while_let_on_iterator.rs:371:5
fn as_byte_slice(slice: &[Self]) -> &[u8];
}
}
+
+mod issue3414 {
+ struct CellLikeThing<T>(T);
+
+ impl<T> CellLikeThing<T> {
+ // don't trigger
+ fn into_inner(this: Self) -> T {
+ this.0
+ }
+ }
+
+ impl<T> std::ops::Deref for CellLikeThing<T> {
+ type Target = T;
+
+ fn deref(&self) -> &T {
+ &self.0
+ }
+ }
+}
+
+// don't trigger
+mod issue4546 {
+ use std::pin::Pin;
+
+ struct S;
+ impl S {
+ pub fn as_mut(self: Pin<&mut Self>) {}
+
+ pub fn as_other_thingy(self: Pin<&Self>) {}
+
+ pub fn is_other_thingy(self: Pin<&Self>) {}
+
+ pub fn to_mut(self: Pin<&mut Self>) {}
+
+ pub fn to_other_thingy(self: Pin<&Self>) {}
+ }
+}
-Subproject commit f1d7f98ed07b9934286b9c4809dd4d7a47537879
+Subproject commit 009e6ceb1ddcd27a9ced3bcb7d0ef823379185a1
let skip = [
"tidy-test-file",
"compiler/rustc_codegen_cranelift",
+ "compiler/rustc_codegen_gcc",
"src/llvm-project",
"library/backtrace",
"library/stdarch",