Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Rollup of 6 pull requests #95552

Merged
merged 15 commits into from
Apr 1, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 8 additions & 6 deletions compiler/rustc_const_eval/src/interpret/eval_context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,8 @@ use rustc_target::abi::{call::FnAbi, Align, HasDataLayout, Size, TargetDataLayou

use super::{
AllocCheck, AllocId, GlobalId, Immediate, InterpErrorInfo, InterpResult, MPlaceTy, Machine,
MemPlace, MemPlaceMeta, Memory, MemoryKind, Operand, Place, PlaceTy, Pointer, Provenance,
Scalar, ScalarMaybeUninit, StackPopJump,
MemPlace, MemPlaceMeta, Memory, MemoryKind, Operand, Place, PlaceTy, Pointer,
PointerArithmetic, Provenance, Scalar, ScalarMaybeUninit, StackPopJump,
};
use crate::transform::validate::equal_up_to_regions;

Expand Down Expand Up @@ -678,7 +678,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
let size = size.align_to(align);

// Check if this brought us over the size limit.
if size.bytes() >= self.tcx.data_layout.obj_size_bound() {
if size > self.max_size_of_val() {
throw_ub!(InvalidMeta("total size is bigger than largest supported object"));
}
Ok(Some((size, align)))
Expand All @@ -694,9 +694,11 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
let elem = layout.field(self, 0);

// Make sure the slice is not too big.
let size = elem.size.checked_mul(len, self).ok_or_else(|| {
err_ub!(InvalidMeta("slice is bigger than largest supported object"))
})?;
let size = elem.size.bytes().saturating_mul(len); // we rely on `max_size_of_val` being smaller than `u64::MAX`.
let size = Size::from_bytes(size);
if size > self.max_size_of_val() {
throw_ub!(InvalidMeta("slice is bigger than largest supported object"));
}
Ok(Some((size, elem.align.abi)))
}

Expand Down
8 changes: 7 additions & 1 deletion compiler/rustc_const_eval/src/interpret/intrinsics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -531,7 +531,9 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
) -> InterpResult<'tcx, Pointer<Option<M::PointerTag>>> {
// We cannot overflow i64 as a type's size must be <= isize::MAX.
let pointee_size = i64::try_from(self.layout_of(pointee_ty)?.size.bytes()).unwrap();
// The computed offset, in bytes, cannot overflow an isize.
// The computed offset, in bytes, must not overflow an isize.
// `checked_mul` enforces a too small bound, but no actual allocation can be big enough for
// the difference to be noticeable.
let offset_bytes =
offset_count.checked_mul(pointee_size).ok_or(err_ub!(PointerArithOverflow))?;
// The offset being in bounds cannot rely on "wrapping around" the address space.
Expand Down Expand Up @@ -563,6 +565,8 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
let count = self.read_scalar(&count)?.to_machine_usize(self)?;
let layout = self.layout_of(src.layout.ty.builtin_deref(true).unwrap().ty)?;
let (size, align) = (layout.size, layout.align.abi);
// `checked_mul` enforces a too small bound (the correct one would probably be machine_isize_max),
// but no actual allocation can be big enough for the difference to be noticeable.
let size = size.checked_mul(count, self).ok_or_else(|| {
err_ub_format!(
"overflow computing total size of `{}`",
Expand All @@ -588,6 +592,8 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
let byte = self.read_scalar(&byte)?.to_u8()?;
let count = self.read_scalar(&count)?.to_machine_usize(self)?;

// `checked_mul` enforces a too small bound (the correct one would probably be machine_isize_max),
// but no actual allocation can be big enough for the difference to be noticeable.
let len = layout
.size
.checked_mul(count, self)
Expand Down
5 changes: 3 additions & 2 deletions compiler/rustc_const_eval/src/interpret/traits.rs
Original file line number Diff line number Diff line change
Expand Up @@ -110,16 +110,17 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
.read_ptr_sized(pointer_size * u64::try_from(COMMON_VTABLE_ENTRIES_SIZE).unwrap())?
.check_init()?;
let size = size.to_machine_usize(self)?;
let size = Size::from_bytes(size);
let align = vtable
.read_ptr_sized(pointer_size * u64::try_from(COMMON_VTABLE_ENTRIES_ALIGN).unwrap())?
.check_init()?;
let align = align.to_machine_usize(self)?;
let align = Align::from_bytes(align).map_err(|e| err_ub!(InvalidVtableAlignment(e)))?;

if size >= self.tcx.data_layout.obj_size_bound() {
if size > self.max_size_of_val() {
throw_ub!(InvalidVtableSize);
}
Ok((Size::from_bytes(size), align))
Ok((size, align))
}

pub fn read_new_vtable_after_trait_upcasting_from_vtable(
Expand Down
6 changes: 5 additions & 1 deletion compiler/rustc_infer/src/infer/error_reporting/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1091,7 +1091,11 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {

/// Compares two given types, eliding parts that are the same between them and highlighting
/// relevant differences, and return two representation of those types for highlighted printing.
fn cmp(&self, t1: Ty<'tcx>, t2: Ty<'tcx>) -> (DiagnosticStyledString, DiagnosticStyledString) {
pub fn cmp(
&self,
t1: Ty<'tcx>,
t2: Ty<'tcx>,
) -> (DiagnosticStyledString, DiagnosticStyledString) {
debug!("cmp(t1={}, t1.kind={:?}, t2={}, t2.kind={:?})", t1, t1.kind(), t2, t2.kind());

// helper functions
Expand Down
5 changes: 5 additions & 0 deletions compiler/rustc_middle/src/mir/interpret/pointer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,11 @@ pub trait PointerArithmetic: HasDataLayout {
self.data_layout().pointer_size
}

#[inline(always)]
fn max_size_of_val(&self) -> Size {
Size::from_bytes(self.machine_isize_max())
}

#[inline]
fn machine_usize_max(&self) -> u64 {
self.pointer_size().unsigned_int_max().try_into().unwrap()
Expand Down
18 changes: 15 additions & 3 deletions compiler/rustc_middle/src/ty/closure.rs
Original file line number Diff line number Diff line change
Expand Up @@ -119,9 +119,21 @@ impl<'tcx> ClosureKind {
/// See `Ty::to_opt_closure_kind` for more details.
pub fn to_ty(self, tcx: TyCtxt<'tcx>) -> Ty<'tcx> {
match self {
ty::ClosureKind::Fn => tcx.types.i8,
ty::ClosureKind::FnMut => tcx.types.i16,
ty::ClosureKind::FnOnce => tcx.types.i32,
ClosureKind::Fn => tcx.types.i8,
ClosureKind::FnMut => tcx.types.i16,
ClosureKind::FnOnce => tcx.types.i32,
}
}

pub fn from_def_id(tcx: TyCtxt<'_>, def_id: DefId) -> Option<ClosureKind> {
if Some(def_id) == tcx.lang_items().fn_once_trait() {
Some(ClosureKind::FnOnce)
} else if Some(def_id) == tcx.lang_items().fn_mut_trait() {
Some(ClosureKind::FnMut)
} else if Some(def_id) == tcx.lang_items().fn_trait() {
Some(ClosureKind::Fn)
} else {
None
}
}
}
Expand Down
18 changes: 7 additions & 11 deletions compiler/rustc_parse/src/parser/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1919,17 +1919,13 @@ impl<'a> Parser<'a> {
match snapshot.parse_array_or_repeat_expr(attrs, token::Brace) {
Ok(arr) => {
let hi = snapshot.prev_token.span;
self.struct_span_err(
arr.span,
"this code is interpreted as a block expression, not an array",
)
.multipart_suggestion(
"try using [] instead of {}",
vec![(lo, "[".to_owned()), (hi, "]".to_owned())],
Applicability::MaybeIncorrect,
)
.note("to define an array, one would use square brackets instead of curly braces")
.emit();
self.struct_span_err(arr.span, "this is a block expression, not an array")
.multipart_suggestion(
"to make an array, use square brackets instead of curly braces",
vec![(lo, "[".to_owned()), (hi, "]".to_owned())],
Applicability::MaybeIncorrect,
)
.emit();

self.restore_snapshot(snapshot);
Some(self.mk_expr_err(arr.span))
Expand Down
126 changes: 121 additions & 5 deletions compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@ pub mod on_unimplemented;
pub mod suggestions;

use super::{
EvaluationResult, FulfillmentError, FulfillmentErrorCode, MismatchedProjectionTypes,
Obligation, ObligationCause, ObligationCauseCode, OnUnimplementedDirective,
OnUnimplementedNote, OutputTypeParameterMismatch, Overflow, PredicateObligation,
SelectionContext, SelectionError, TraitNotObjectSafe,
EvaluationResult, FulfillmentContext, FulfillmentError, FulfillmentErrorCode,
MismatchedProjectionTypes, Obligation, ObligationCause, ObligationCauseCode,
OnUnimplementedDirective, OnUnimplementedNote, OutputTypeParameterMismatch, Overflow,
PredicateObligation, SelectionContext, SelectionError, TraitNotObjectSafe,
};

use crate::infer::error_reporting::{TyCategory, TypeAnnotationNeeded as ErrorCode};
Expand All @@ -21,6 +21,8 @@ use rustc_hir::intravisit::Visitor;
use rustc_hir::GenericParam;
use rustc_hir::Item;
use rustc_hir::Node;
use rustc_infer::infer::error_reporting::same_type_modulo_infer;
use rustc_infer::traits::TraitEngine;
use rustc_middle::thir::abstract_const::NotConstEvaluatable;
use rustc_middle::traits::select::OverflowError;
use rustc_middle::ty::error::ExpectedFound;
Expand Down Expand Up @@ -103,6 +105,17 @@ pub trait InferCtxtExt<'tcx> {
found_args: Vec<ArgKind>,
is_closure: bool,
) -> DiagnosticBuilder<'tcx, ErrorGuaranteed>;

/// Checks if the type implements one of `Fn`, `FnMut`, or `FnOnce`
/// in that order, and returns the generic type corresponding to the
/// argument of that trait (corresponding to the closure arguments).
fn type_implements_fn_trait(
&self,
param_env: ty::ParamEnv<'tcx>,
ty: ty::Binder<'tcx, Ty<'tcx>>,
constness: ty::BoundConstness,
polarity: ty::ImplPolarity,
) -> Result<(ty::ClosureKind, ty::Binder<'tcx, Ty<'tcx>>), ()>;
}

impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
Expand Down Expand Up @@ -563,7 +576,64 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
}

// Try to report a help message
if !trait_ref.has_infer_types_or_consts()
if is_fn_trait
&& let Ok((implemented_kind, params)) = self.type_implements_fn_trait(
obligation.param_env,
trait_ref.self_ty(),
trait_predicate.skip_binder().constness,
trait_predicate.skip_binder().polarity,
)
{
// If the type implements `Fn`, `FnMut`, or `FnOnce`, suppress the following
// suggestion to add trait bounds for the type, since we only typically implement
// these traits once.

// Note if the `FnMut` or `FnOnce` is less general than the trait we're trying
// to implement.
let selected_kind =
ty::ClosureKind::from_def_id(self.tcx, trait_ref.def_id())
.expect("expected to map DefId to ClosureKind");
if !implemented_kind.extends(selected_kind) {
err.note(
&format!(
"`{}` implements `{}`, but it must implement `{}`, which is more general",
trait_ref.skip_binder().self_ty(),
implemented_kind,
selected_kind
)
);
}

// Note any argument mismatches
let given_ty = params.skip_binder();
let expected_ty = trait_ref.skip_binder().substs.type_at(1);
if let ty::Tuple(given) = given_ty.kind()
&& let ty::Tuple(expected) = expected_ty.kind()
{
if expected.len() != given.len() {
// Note number of types that were expected and given
err.note(
&format!(
"expected a closure taking {} argument{}, but one taking {} argument{} was given",
given.len(),
if given.len() == 1 { "" } else { "s" },
expected.len(),
if expected.len() == 1 { "" } else { "s" },
)
);
} else if !same_type_modulo_infer(given_ty, expected_ty) {
// Print type mismatch
let (expected_args, given_args) =
self.cmp(given_ty, expected_ty);
err.note_expected_found(
&"a closure with arguments",
expected_args,
&"a closure with arguments",
given_args,
);
}
}
} else if !trait_ref.has_infer_types_or_consts()
&& self.predicate_can_apply(obligation.param_env, trait_ref)
{
// If a where-clause may be useful, remind the
Expand Down Expand Up @@ -1148,6 +1218,52 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {

err
}

fn type_implements_fn_trait(
&self,
param_env: ty::ParamEnv<'tcx>,
ty: ty::Binder<'tcx, Ty<'tcx>>,
constness: ty::BoundConstness,
polarity: ty::ImplPolarity,
) -> Result<(ty::ClosureKind, ty::Binder<'tcx, Ty<'tcx>>), ()> {
self.commit_if_ok(|_| {
for trait_def_id in [
self.tcx.lang_items().fn_trait(),
self.tcx.lang_items().fn_mut_trait(),
self.tcx.lang_items().fn_once_trait(),
] {
let Some(trait_def_id) = trait_def_id else { continue };
// Make a fresh inference variable so we can determine what the substitutions
// of the trait are.
let var = self.next_ty_var(TypeVariableOrigin {
span: DUMMY_SP,
kind: TypeVariableOriginKind::MiscVariable,
});
let substs = self.tcx.mk_substs_trait(ty.skip_binder(), &[var.into()]);
let obligation = Obligation::new(
ObligationCause::dummy(),
param_env,
ty.rebind(ty::TraitPredicate {
trait_ref: ty::TraitRef::new(trait_def_id, substs),
constness,
polarity,
})
.to_predicate(self.tcx),
);
let mut fulfill_cx = FulfillmentContext::new_in_snapshot();
fulfill_cx.register_predicate_obligation(self, obligation);
if fulfill_cx.select_all_or_error(self).is_empty() {
return Ok((
ty::ClosureKind::from_def_id(self.tcx, trait_def_id)
.expect("expected to map DefId to ClosureKind"),
ty.rebind(self.resolve_vars_if_possible(var)),
));
}
}

Err(())
})
}
}

trait InferCtxtPrivExt<'hir, 'tcx> {
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_typeck/src/check/demand.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
self.suggest_no_capture_closure(err, expected, expr_ty);
self.suggest_boxing_when_appropriate(err, expr, expected, expr_ty);
self.suggest_missing_parentheses(err, expr);
self.suggest_block_to_brackets_peeling_refs(err, expr, expr_ty, expected);
self.note_need_for_fn_pointer(err, expected, expr_ty);
self.note_internal_mutation_in_method(err, expr, expected, expr_ty);
self.report_closure_inferred_return_type(err, expected);
Expand Down
Loading