Auto merge of #107314 - matthiaskrgr:rollup-j40lnlj, r=matthiaskrgr

Rollup of 11 pull requests

Successful merges:

 - #106407 (Improve proc macro attribute diagnostics)
 - #106960 (Teach parser to understand fake anonymous enum syntax)
 - #107085 (Custom MIR: Support binary and unary operations)
 - #107086 (Print PID holding bootstrap build lock on Linux)
 - #107175 (Fix escaping inference var ICE in `point_at_expr_source_of_inferred_type`)
 - #107204 (suggest qualifying bare associated constants)
 - #107248 (abi: add AddressSpace field to Primitive::Pointer )
 - #107272 (Implement ObjectSafe and WF in the new solver)
 - #107285 (Implement `Generator` and `Future` in the new solver)
 - #107286 (ICE in new solver if we see an inference variable)
 - #107313 (Add Style Team Triagebot config)

Failed merges:

r? `@ghost`
`@rustbot` modify labels: rollup
This commit is contained in:
bors 2023-01-26 06:23:14 +00:00
commit e187f8871e
77 changed files with 1682 additions and 343 deletions

View File

@ -351,7 +351,7 @@ dependencies = [
"cargo-test-macro",
"cargo-test-support",
"cargo-util",
"clap 4.1.3",
"clap 4.1.4",
"crates-io",
"curl",
"curl-sys",
@ -655,9 +655,9 @@ dependencies = [
[[package]]
name = "clap"
version = "4.1.3"
version = "4.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d8d93d855ce6a0aa87b8473ef9169482f40abaa2e9e0993024c35c902cbd5920"
checksum = "f13b9c79b5d1dd500d20ef541215a6423c75829ef43117e1b4d17fd8af0b5d76"
dependencies = [
"bitflags",
"clap_derive 4.1.0",
@ -675,7 +675,7 @@ version = "4.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "10861370d2ba66b0f5989f83ebf35db6421713fd92351790e7fdd6c36774c56b"
dependencies = [
"clap 4.1.3",
"clap 4.1.4",
]
[[package]]
@ -2294,7 +2294,7 @@ name = "jsondoclint"
version = "0.1.0"
dependencies = [
"anyhow",
"clap 4.1.3",
"clap 4.1.4",
"fs-err",
"rustdoc-json-types",
"serde",
@ -2557,7 +2557,7 @@ dependencies = [
"ammonia",
"anyhow",
"chrono",
"clap 4.1.3",
"clap 4.1.4",
"clap_complete",
"elasticlunr-rs",
"env_logger 0.10.0",
@ -3528,7 +3528,7 @@ dependencies = [
name = "rustbook"
version = "0.1.0"
dependencies = [
"clap 4.1.3",
"clap 4.1.4",
"env_logger 0.7.1",
"mdbook",
]

View File

@ -267,6 +267,9 @@ impl TargetDataLayout {
["a", ref a @ ..] => dl.aggregate_align = align(a, "a")?,
["f32", ref a @ ..] => dl.f32_align = align(a, "f32")?,
["f64", ref a @ ..] => dl.f64_align = align(a, "f64")?,
// FIXME(erikdesjardins): we should be parsing nonzero address spaces
// this will require replacing TargetDataLayout::{pointer_size,pointer_align}
// with e.g. `fn pointer_size_in(AddressSpace)`
[p @ "p", s, ref a @ ..] | [p @ "p0", s, ref a @ ..] => {
dl.pointer_size = size(s, p)?;
dl.pointer_align = align(a, p)?;
@ -861,7 +864,7 @@ pub enum Primitive {
Int(Integer, bool),
F32,
F64,
Pointer,
Pointer(AddressSpace),
}
impl Primitive {
@ -872,7 +875,10 @@ impl Primitive {
Int(i, _) => i.size(),
F32 => Size::from_bits(32),
F64 => Size::from_bits(64),
Pointer => dl.pointer_size,
// FIXME(erikdesjardins): ignoring address space is technically wrong, pointers in
// different address spaces can have different sizes
// (but TargetDataLayout doesn't currently parse that part of the DL string)
Pointer(_) => dl.pointer_size,
}
}
@ -883,26 +889,12 @@ impl Primitive {
Int(i, _) => i.align(dl),
F32 => dl.f32_align,
F64 => dl.f64_align,
Pointer => dl.pointer_align,
// FIXME(erikdesjardins): ignoring address space is technically wrong, pointers in
// different address spaces can have different alignments
// (but TargetDataLayout doesn't currently parse that part of the DL string)
Pointer(_) => dl.pointer_align,
}
}
// FIXME(eddyb) remove, it's trivial thanks to `matches!`.
#[inline]
pub fn is_float(self) -> bool {
matches!(self, F32 | F64)
}
// FIXME(eddyb) remove, it's completely unused.
#[inline]
pub fn is_int(self) -> bool {
matches!(self, Int(..))
}
#[inline]
pub fn is_ptr(self) -> bool {
matches!(self, Pointer)
}
}
/// Inclusive wrap-around range of valid values, that is, if
@ -1188,7 +1180,8 @@ impl FieldsShape {
/// An identifier that specifies the address space that some operation
/// should operate on. Special address spaces have an effect on code generation,
/// depending on the target and the address spaces it implements.
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[cfg_attr(feature = "nightly", derive(HashStable_Generic))]
pub struct AddressSpace(pub u32);
impl AddressSpace {
@ -1468,7 +1461,6 @@ pub struct PointeeInfo {
pub size: Size,
pub align: Align,
pub safe: Option<PointerKind>,
pub address_space: AddressSpace,
}
/// Used in `might_permit_raw_init` to indicate the kind of initialisation

View File

@ -400,8 +400,8 @@ pub fn walk_ty<'a, V: Visitor<'a>>(visitor: &mut V, typ: &'a Ty) {
walk_list!(visitor, visit_lifetime, opt_lifetime, LifetimeCtxt::Ref);
visitor.visit_ty(&mutable_type.ty)
}
TyKind::Tup(tuple_element_types) => {
walk_list!(visitor, visit_ty, tuple_element_types);
TyKind::Tup(tys) => {
walk_list!(visitor, visit_ty, tys);
}
TyKind::BareFn(function_declaration) => {
walk_list!(visitor, visit_generic_param, &function_declaration.generic_params);

View File

@ -35,7 +35,8 @@ pub(crate) fn scalar_to_clif_type(tcx: TyCtxt<'_>, scalar: Scalar) -> Type {
},
Primitive::F32 => types::F32,
Primitive::F64 => types::F64,
Primitive::Pointer => pointer_ty(tcx),
// FIXME(erikdesjardins): handle non-default addrspace ptr sizes
Primitive::Pointer(_) => pointer_ty(tcx),
}
}

View File

@ -709,7 +709,7 @@ impl<'a, 'gcc, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'gcc, 'tcx> {
bx.range_metadata(load, vr);
}
}
abi::Pointer if vr.start < vr.end && !vr.contains(0) => {
abi::Pointer(_) if vr.start < vr.end && !vr.contains(0) => {
bx.nonnull_metadata(load);
}
_ => {}

View File

@ -211,7 +211,7 @@ impl<'gcc, 'tcx> ConstMethods<'tcx> for CodegenCx<'gcc, 'tcx> {
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.primitive() != Pointer {
if !matches!(layout.primitive(), Pointer(_)) {
self.const_bitcast(ptr.dereference(None).to_rvalue(), ty)
}
else {

View File

@ -322,13 +322,16 @@ pub fn const_alloc_to_gcc<'gcc, 'tcx>(cx: &CodegenCx<'gcc, 'tcx>, alloc: ConstAl
)
.expect("const_alloc_to_llvm: could not read relocation pointer")
as u64;
let address_space = cx.tcx.global_alloc(alloc_id).address_space(cx);
llvals.push(cx.scalar_to_backend(
InterpScalar::from_pointer(
interpret::Pointer::new(alloc_id, Size::from_bytes(ptr_offset)),
&cx.tcx,
),
abi::Scalar::Initialized { value: Primitive::Pointer, valid_range: WrappingRange::full(dl.pointer_size) },
cx.type_i8p(),
abi::Scalar::Initialized { value: Primitive::Pointer(address_space), valid_range: WrappingRange::full(dl.pointer_size) },
cx.type_i8p_ext(address_space),
));
next_offset = offset + pointer_size;
}

View File

@ -253,7 +253,7 @@ impl<'tcx> LayoutGccExt<'tcx> for TyAndLayout<'tcx> {
Int(i, false) => cx.type_from_unsigned_integer(i),
F32 => cx.type_f32(),
F64 => cx.type_f64(),
Pointer => {
Pointer(address_space) => {
// If we know the alignment, pick something better than i8.
let pointee =
if let Some(pointee) = self.pointee_info_at(cx, offset) {
@ -262,7 +262,7 @@ impl<'tcx> LayoutGccExt<'tcx> for TyAndLayout<'tcx> {
else {
cx.type_i8()
};
cx.type_ptr_to(pointee)
cx.type_ptr_to_ext(pointee, address_space)
}
}
}

View File

@ -849,6 +849,7 @@ fn dummy_output_type<'ll>(cx: &CodegenCx<'ll, '_>, reg: InlineAsmRegClass) -> &'
/// Helper function to get the LLVM type for a Scalar. Pointers are returned as
/// the equivalent integer type.
fn llvm_asm_scalar_type<'ll>(cx: &CodegenCx<'ll, '_>, scalar: Scalar) -> &'ll Type {
let dl = &cx.tcx.data_layout;
match scalar.primitive() {
Primitive::Int(Integer::I8, _) => cx.type_i8(),
Primitive::Int(Integer::I16, _) => cx.type_i16(),
@ -856,7 +857,8 @@ fn llvm_asm_scalar_type<'ll>(cx: &CodegenCx<'ll, '_>, scalar: Scalar) -> &'ll Ty
Primitive::Int(Integer::I64, _) => cx.type_i64(),
Primitive::F32 => cx.type_f32(),
Primitive::F64 => cx.type_f64(),
Primitive::Pointer => cx.type_isize(),
// FIXME(erikdesjardins): handle non-default addrspace ptr sizes
Primitive::Pointer(_) => cx.type_from_integer(dl.ptr_sized_integer()),
_ => unreachable!(),
}
}
@ -868,6 +870,7 @@ fn llvm_fixup_input<'ll, 'tcx>(
reg: InlineAsmRegClass,
layout: &TyAndLayout<'tcx>,
) -> &'ll Value {
let dl = &bx.tcx.data_layout;
match (reg, layout.abi) {
(InlineAsmRegClass::AArch64(AArch64InlineAsmRegClass::vreg), Abi::Scalar(s)) => {
if let Primitive::Int(Integer::I8, _) = s.primitive() {
@ -881,8 +884,10 @@ fn llvm_fixup_input<'ll, 'tcx>(
let elem_ty = llvm_asm_scalar_type(bx.cx, s);
let count = 16 / layout.size.bytes();
let vec_ty = bx.cx.type_vector(elem_ty, count);
if let Primitive::Pointer = s.primitive() {
value = bx.ptrtoint(value, bx.cx.type_isize());
// FIXME(erikdesjardins): handle non-default addrspace ptr sizes
if let Primitive::Pointer(_) = s.primitive() {
let t = bx.type_from_integer(dl.ptr_sized_integer());
value = bx.ptrtoint(value, t);
}
bx.insert_element(bx.const_undef(vec_ty), value, bx.const_i32(0))
}
@ -958,7 +963,7 @@ fn llvm_fixup_output<'ll, 'tcx>(
}
(InlineAsmRegClass::AArch64(AArch64InlineAsmRegClass::vreg_low16), Abi::Scalar(s)) => {
value = bx.extract_element(value, bx.const_i32(0));
if let Primitive::Pointer = s.primitive() {
if let Primitive::Pointer(_) = s.primitive() {
value = bx.inttoptr(value, layout.llvm_type(bx.cx));
}
value

View File

@ -511,7 +511,7 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> {
bx.range_metadata(load, scalar.valid_range(bx));
}
}
abi::Pointer => {
abi::Pointer(_) => {
if !scalar.valid_range(bx).contains(0) {
bx.nonnull_metadata(load);
}

View File

@ -236,7 +236,7 @@ impl<'ll, 'tcx> ConstMethods<'tcx> for CodegenCx<'ll, 'tcx> {
Scalar::Int(int) => {
let data = int.assert_bits(layout.size(self));
let llval = self.const_uint_big(self.type_ix(bitsize), data);
if layout.primitive() == Pointer {
if matches!(layout.primitive(), Pointer(_)) {
unsafe { llvm::LLVMConstIntToPtr(llval, llty) }
} else {
self.const_bitcast(llval, llty)
@ -284,7 +284,7 @@ impl<'ll, 'tcx> ConstMethods<'tcx> for CodegenCx<'ll, 'tcx> {
1,
)
};
if layout.primitive() != Pointer {
if !matches!(layout.primitive(), Pointer(_)) {
unsafe { llvm::LLVMConstPtrToInt(llval, llty) }
} else {
self.const_bitcast(llval, llty)

View File

@ -13,7 +13,7 @@ use rustc_codegen_ssa::traits::*;
use rustc_hir::def_id::DefId;
use rustc_middle::middle::codegen_fn_attrs::{CodegenFnAttrFlags, CodegenFnAttrs};
use rustc_middle::mir::interpret::{
read_target_uint, Allocation, ConstAllocation, ErrorHandled, GlobalAlloc, InitChunk, Pointer,
read_target_uint, Allocation, ConstAllocation, ErrorHandled, InitChunk, Pointer,
Scalar as InterpScalar,
};
use rustc_middle::mir::mono::MonoItem;
@ -21,9 +21,7 @@ use rustc_middle::ty::layout::LayoutOf;
use rustc_middle::ty::{self, Instance, Ty};
use rustc_middle::{bug, span_bug};
use rustc_session::config::Lto;
use rustc_target::abi::{
AddressSpace, Align, HasDataLayout, Primitive, Scalar, Size, WrappingRange,
};
use rustc_target::abi::{Align, HasDataLayout, Primitive, Scalar, Size, WrappingRange};
use std::ops::Range;
pub fn const_alloc_to_llvm<'ll>(cx: &CodegenCx<'ll, '_>, alloc: ConstAllocation<'_>) -> &'ll Value {
@ -98,12 +96,7 @@ pub fn const_alloc_to_llvm<'ll>(cx: &CodegenCx<'ll, '_>, alloc: ConstAllocation<
.expect("const_alloc_to_llvm: could not read relocation pointer")
as u64;
let address_space = match cx.tcx.global_alloc(alloc_id) {
GlobalAlloc::Function(..) => cx.data_layout().instruction_address_space,
GlobalAlloc::Static(..) | GlobalAlloc::Memory(..) | GlobalAlloc::VTable(..) => {
AddressSpace::DATA
}
};
let address_space = cx.tcx.global_alloc(alloc_id).address_space(cx);
llvals.push(cx.scalar_to_backend(
InterpScalar::from_pointer(
@ -111,7 +104,7 @@ pub fn const_alloc_to_llvm<'ll>(cx: &CodegenCx<'ll, '_>, alloc: ConstAllocation<
&cx.tcx,
),
Scalar::Initialized {
value: Primitive::Pointer,
value: Primitive::Pointer(address_space),
valid_range: WrappingRange::full(dl.pointer_size),
},
cx.type_i8p_ext(address_space),

View File

@ -122,7 +122,8 @@ fn tag_base_type<'ll, 'tcx>(
Primitive::Int(t, _) => t,
Primitive::F32 => Integer::I32,
Primitive::F64 => Integer::I64,
Primitive::Pointer => {
// FIXME(erikdesjardins): handle non-default addrspace ptr sizes
Primitive::Pointer(_) => {
// If the niche is the NULL value of a reference, then `discr_enum_ty` will be
// a RawPtr. CodeView doesn't know what to do with enums whose base type is a
// pointer so we fix this up to just be `usize`.

View File

@ -149,7 +149,7 @@ impl<'ll, 'tcx> IntrinsicCallMethods<'tcx> for Builder<'_, 'll, 'tcx> {
emit_va_arg(self, args[0], ret_ty)
}
}
Primitive::F64 | Primitive::Pointer => {
Primitive::F64 | Primitive::Pointer(_) => {
emit_va_arg(self, args[0], ret_ty)
}
// `va_arg` should never be used with the return type f32.

View File

@ -7,7 +7,7 @@ use rustc_middle::bug;
use rustc_middle::ty::layout::{FnAbiOf, LayoutOf, TyAndLayout};
use rustc_middle::ty::print::{with_no_trimmed_paths, with_no_visible_paths};
use rustc_middle::ty::{self, Ty, TypeVisitable};
use rustc_target::abi::{Abi, AddressSpace, Align, FieldsShape};
use rustc_target::abi::{Abi, Align, FieldsShape};
use rustc_target::abi::{Int, Pointer, F32, F64};
use rustc_target::abi::{PointeeInfo, Scalar, Size, TyAbiInterface, Variants};
use smallvec::{smallvec, SmallVec};
@ -312,14 +312,13 @@ impl<'tcx> LayoutLlvmExt<'tcx> for TyAndLayout<'tcx> {
Int(i, _) => cx.type_from_integer(i),
F32 => cx.type_f32(),
F64 => cx.type_f64(),
Pointer => {
Pointer(address_space) => {
// If we know the alignment, pick something better than i8.
let (pointee, address_space) =
if let Some(pointee) = self.pointee_info_at(cx, offset) {
(cx.type_pointee_for_align(pointee.align), pointee.address_space)
} else {
(cx.type_i8(), AddressSpace::DATA)
};
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_ext(pointee, address_space)
}
}

View File

@ -1801,8 +1801,8 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
match (src.layout.abi, dst.layout.abi) {
(abi::Abi::Scalar(src_scalar), abi::Abi::Scalar(dst_scalar)) => {
// HACK(eddyb) LLVM doesn't like `bitcast`s between pointers and non-pointers.
let src_is_ptr = src_scalar.primitive() == abi::Pointer;
let dst_is_ptr = dst_scalar.primitive() == abi::Pointer;
let src_is_ptr = matches!(src_scalar.primitive(), abi::Pointer(_));
let dst_is_ptr = matches!(dst_scalar.primitive(), abi::Pointer(_));
if src_is_ptr == dst_is_ptr {
assert_eq!(src.layout.size, dst.layout.size);

View File

@ -9,7 +9,7 @@ use rustc_middle::mir;
use rustc_middle::mir::tcx::PlaceTy;
use rustc_middle::ty::layout::{HasTyCtxt, LayoutOf, TyAndLayout};
use rustc_middle::ty::{self, Ty};
use rustc_target::abi::{Abi, Align, FieldsShape, Int, TagEncoding};
use rustc_target::abi::{Abi, Align, FieldsShape, Int, Pointer, TagEncoding};
use rustc_target::abi::{VariantIdx, Variants};
#[derive(Copy, Clone, Debug)]
@ -209,6 +209,7 @@ impl<'a, 'tcx, V: CodegenObject> PlaceRef<'tcx, V> {
bx: &mut Bx,
cast_to: Ty<'tcx>,
) -> V {
let dl = &bx.tcx().data_layout;
let cast_to_layout = bx.cx().layout_of(cast_to);
let cast_to_size = cast_to_layout.layout.size();
let cast_to = bx.cx().immediate_backend_type(cast_to_layout);
@ -250,12 +251,14 @@ impl<'a, 'tcx, V: CodegenObject> PlaceRef<'tcx, V> {
TagEncoding::Niche { untagged_variant, ref niche_variants, niche_start } => {
// Cast to an integer so we don't have to treat a pointer as a
// special case.
let (tag, tag_llty) = if tag_scalar.primitive().is_ptr() {
let t = bx.type_isize();
let tag = bx.ptrtoint(tag_imm, t);
(tag, t)
} else {
(tag_imm, bx.cx().immediate_backend_type(tag_op.layout))
let (tag, tag_llty) = match tag_scalar.primitive() {
// FIXME(erikdesjardins): handle non-default addrspace ptr sizes
Pointer(_) => {
let t = bx.type_from_integer(dl.ptr_sized_integer());
let tag = bx.ptrtoint(tag_imm, t);
(tag, t)
}
_ => (tag_imm, bx.cx().immediate_backend_type(tag_op.layout)),
};
let tag_size = tag_scalar.size(bx.cx());

View File

@ -319,7 +319,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
assert_eq!(size, mplace.layout.size, "abi::Scalar size does not match layout size");
let scalar = alloc.read_scalar(
alloc_range(Size::ZERO, size),
/*read_provenance*/ s.is_ptr(),
/*read_provenance*/ matches!(s, abi::Pointer(_)),
)?;
Some(ImmTy { imm: scalar.into(), layout: mplace.layout })
}
@ -335,11 +335,11 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
assert!(b_offset.bytes() > 0); // in `operand_field` we use the offset to tell apart the fields
let a_val = alloc.read_scalar(
alloc_range(Size::ZERO, a_size),
/*read_provenance*/ a.is_ptr(),
/*read_provenance*/ matches!(a, abi::Pointer(_)),
)?;
let b_val = alloc.read_scalar(
alloc_range(b_offset, b_size),
/*read_provenance*/ b.is_ptr(),
/*read_provenance*/ matches!(b, abi::Pointer(_)),
)?;
Some(ImmTy { imm: Immediate::ScalarPair(a_val, b_val), layout: mplace.layout })
}

View File

@ -710,3 +710,24 @@ passes_ignored_derived_impls =
[one] trait {$trait_list}, but this is
*[other] traits {$trait_list}, but these are
} intentionally ignored during dead code analysis
passes_proc_macro_typeerror = mismatched {$kind} signature
.label = found {$found}, expected type `proc_macro::TokenStream`
.note = {$kind}s must have a signature of `{$expected_signature}`
passes_proc_macro_diff_arg_count = mismatched {$kind} signature
.label = found unexpected {$count ->
[one] argument
*[other] arguments
}
.note = {$kind}s must have a signature of `{$expected_signature}`
passes_proc_macro_missing_args = mismatched {$kind} signature
.label = {$kind} must have {$expected_input_count ->
[one] one argument
*[other] two arguments
} of type `proc_macro::TokenStream`
passes_proc_macro_invalid_abi = proc macro functions may not be `extern "{$abi}"`
passes_proc_macro_unsafe = proc macro functions may not be `unsafe`

View File

@ -62,7 +62,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|| self.suggest_floating_point_literal(err, expr, expected)
|| self.note_result_coercion(err, expr, expected, expr_ty);
if !suggested {
self.point_at_expr_source_of_inferred_type(err, expr, expr_ty, expected);
self.point_at_expr_source_of_inferred_type(err, expr, expr_ty, expected, expr.span);
}
}
@ -222,6 +222,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
expr: &hir::Expr<'_>,
found: Ty<'tcx>,
expected: Ty<'tcx>,
mismatch_span: Span,
) -> bool {
let map = self.tcx.hir();
@ -270,7 +271,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
lt_op: |_| self.tcx.lifetimes.re_erased,
ct_op: |c| c,
ty_op: |t| match *t.kind() {
ty::Infer(ty::TyVar(vid)) => self.tcx.mk_ty_infer(ty::TyVar(self.root_var(vid))),
ty::Infer(ty::TyVar(_)) => self.tcx.mk_ty_var(ty::TyVid::from_u32(0)),
ty::Infer(ty::IntVar(_)) => {
self.tcx.mk_ty_infer(ty::IntVar(ty::IntVid { index: 0 }))
}
@ -281,7 +282,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
},
};
let mut prev = eraser.fold_ty(ty);
let mut prev_span = None;
let mut prev_span: Option<Span> = None;
for binding in expr_finder.uses {
// In every expression where the binding is referenced, we will look at that
@ -333,13 +334,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
// inferred in this method call.
let arg = &args[i];
let arg_ty = self.node_ty(arg.hir_id);
err.span_label(
arg.span,
&format!(
"this is of type `{arg_ty}`, which causes `{ident}` to be \
inferred as `{ty}`",
),
);
if !arg.span.overlaps(mismatch_span) {
err.span_label(
arg.span,
&format!(
"this is of type `{arg_ty}`, which causes `{ident}` to be \
inferred as `{ty}`",
),
);
}
param_args.insert(param_ty, (arg, arg_ty));
}
}
@ -382,12 +385,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
&& self.can_eq(self.param_env, ty, found).is_ok()
{
// We only point at the first place where the found type was inferred.
if !segment.ident.span.overlaps(mismatch_span) {
err.span_label(
segment.ident.span,
with_forced_trimmed_paths!(format!(
"here the type of `{ident}` is inferred to be `{ty}`",
)),
);
);}
break;
} else if !param_args.is_empty() {
break;
@ -406,12 +410,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
// We use the *previous* span because if the type is known *here* it means
// it was *evaluated earlier*. We don't do this for method calls because we
// evaluate the method's self type eagerly, but not in any other case.
err.span_label(
span,
with_forced_trimmed_paths!(format!(
"here the type of `{ident}` is inferred to be `{ty}`",
)),
);
if !span.overlaps(mismatch_span) {
err.span_label(
span,
with_forced_trimmed_paths!(format!(
"here the type of `{ident}` is inferred to be `{ty}`",
)),
);
}
break;
}
prev = ty;

View File

@ -808,7 +808,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
kind: TypeVariableOriginKind::MiscVariable,
span: full_call_span,
});
self.point_at_expr_source_of_inferred_type(&mut err, rcvr, expected, callee_ty);
self.point_at_expr_source_of_inferred_type(
&mut err,
rcvr,
expected,
callee_ty,
provided_arg_span,
);
}
// Call out where the function is defined
self.label_fn_like(

View File

@ -38,6 +38,7 @@ fn unpack_option_like<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> Ty<'tcx> {
impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
pub fn check_transmute(&self, from: Ty<'tcx>, to: Ty<'tcx>, hir_id: HirId) {
let tcx = self.tcx;
let dl = &tcx.data_layout;
let span = tcx.hir().span(hir_id);
let normalize = |ty| {
let ty = self.resolve_vars_if_possible(ty);
@ -69,7 +70,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
// Special-case transmuting from `typeof(function)` and
// `Option<typeof(function)>` to present a clearer error.
let from = unpack_option_like(tcx, from);
if let (&ty::FnDef(..), SizeSkeleton::Known(size_to)) = (from.kind(), sk_to) && size_to == Pointer.size(&tcx) {
if let (&ty::FnDef(..), SizeSkeleton::Known(size_to)) = (from.kind(), sk_to) && size_to == Pointer(dl.instruction_address_space).size(&tcx) {
struct_span_err!(tcx.sess, span, E0591, "can't transmute zero-sized type")
.note(&format!("source type: {from}"))
.note(&format!("target type: {to}"))

View File

@ -110,7 +110,7 @@ use rustc_hir::def_id::DefId;
use rustc_macros::HashStable;
use rustc_middle::ty::print::with_no_trimmed_paths;
use rustc_serialize::{Decodable, Encodable};
use rustc_target::abi::Endian;
use rustc_target::abi::{AddressSpace, Endian, HasDataLayout};
use crate::mir;
use crate::ty::codec::{TyDecoder, TyEncoder};
@ -438,6 +438,17 @@ impl<'tcx> GlobalAlloc<'tcx> {
_ => bug!("expected vtable, got {:?}", self),
}
}
/// The address space that this `GlobalAlloc` should be placed in.
#[inline]
pub fn address_space(&self, cx: &impl HasDataLayout) -> AddressSpace {
match self {
GlobalAlloc::Function(..) => cx.data_layout().instruction_address_space,
GlobalAlloc::Static(..) | GlobalAlloc::Memory(..) | GlobalAlloc::VTable(..) => {
AddressSpace::DATA
}
}
}
}
pub(crate) struct AllocMap<'tcx> {

View File

@ -128,7 +128,8 @@ impl PrimitiveExt for Primitive {
Int(i, signed) => i.to_ty(tcx, signed),
F32 => tcx.types.f32,
F64 => tcx.types.f64,
Pointer => tcx.mk_mut_ptr(tcx.mk_unit()),
// FIXME(erikdesjardins): handle non-default addrspace ptr sizes
Pointer(_) => tcx.mk_mut_ptr(tcx.mk_unit()),
}
}
@ -138,7 +139,11 @@ impl PrimitiveExt for Primitive {
fn to_int_ty<'tcx>(&self, tcx: TyCtxt<'tcx>) -> Ty<'tcx> {
match *self {
Int(i, signed) => i.to_ty(tcx, signed),
Pointer => tcx.types.usize,
// FIXME(erikdesjardins): handle non-default addrspace ptr sizes
Pointer(_) => {
let signed = false;
tcx.data_layout().ptr_sized_integer().to_ty(tcx, signed)
}
F32 | F64 => bug!("floats do not have an int type"),
}
}
@ -812,132 +817,125 @@ where
let tcx = cx.tcx();
let param_env = cx.param_env();
let addr_space_of_ty = |ty: Ty<'tcx>| {
if ty.is_fn() { cx.data_layout().instruction_address_space } else { AddressSpace::DATA }
};
let pointee_info = match *this.ty.kind() {
ty::RawPtr(mt) if offset.bytes() == 0 => {
tcx.layout_of(param_env.and(mt.ty)).ok().map(|layout| PointeeInfo {
size: layout.size,
align: layout.align.abi,
safe: None,
address_space: addr_space_of_ty(mt.ty),
})
}
ty::FnPtr(fn_sig) if offset.bytes() == 0 => {
tcx.layout_of(param_env.and(tcx.mk_fn_ptr(fn_sig))).ok().map(|layout| PointeeInfo {
size: layout.size,
align: layout.align.abi,
safe: None,
address_space: cx.data_layout().instruction_address_space,
})
}
ty::Ref(_, ty, mt) if offset.bytes() == 0 => {
let address_space = addr_space_of_ty(ty);
let kind = if tcx.sess.opts.optimize == OptLevel::No {
// Use conservative pointer kind if not optimizing. This saves us the
// Freeze/Unpin queries, and can save time in the codegen backend (noalias
// attributes in LLVM have compile-time cost even in unoptimized builds).
PointerKind::SharedMutable
} else {
match mt {
hir::Mutability::Not => {
if ty.is_freeze(tcx, cx.param_env()) {
PointerKind::Frozen
} else {
PointerKind::SharedMutable
}
}
hir::Mutability::Mut => {
// References to self-referential structures should not be considered
// noalias, as another pointer to the structure can be obtained, that
// is not based-on the original reference. We consider all !Unpin
// types to be potentially self-referential here.
if ty.is_unpin(tcx, cx.param_env()) {
PointerKind::UniqueBorrowed
} else {
PointerKind::UniqueBorrowedPinned
}
}
}
};
tcx.layout_of(param_env.and(ty)).ok().map(|layout| PointeeInfo {
size: layout.size,
align: layout.align.abi,
safe: Some(kind),
address_space,
})
}
_ => {
let mut data_variant = match this.variants {
// Within the discriminant field, only the niche itself is
// always initialized, so we only check for a pointer at its
// offset.
//
// If the niche is a pointer, it's either valid (according
// to its type), or null (which the niche field's scalar
// validity range encodes). This allows using
// `dereferenceable_or_null` for e.g., `Option<&T>`, and
// this will continue to work as long as we don't start
// using more niches than just null (e.g., the first page of
// the address space, or unaligned pointers).
Variants::Multiple {
tag_encoding: TagEncoding::Niche { untagged_variant, .. },
tag_field,
..
} if this.fields.offset(tag_field) == offset => {
Some(this.for_variant(cx, untagged_variant))
}
_ => Some(this),
};
if let Some(variant) = data_variant {
// We're not interested in any unions.
if let FieldsShape::Union(_) = variant.fields {
data_variant = None;
}
let pointee_info =
match *this.ty.kind() {
ty::RawPtr(mt) if offset.bytes() == 0 => {
tcx.layout_of(param_env.and(mt.ty)).ok().map(|layout| PointeeInfo {
size: layout.size,
align: layout.align.abi,
safe: None,
})
}
let mut result = None;
if let Some(variant) = data_variant {
let ptr_end = offset + Pointer.size(cx);
for i in 0..variant.fields.count() {
let field_start = variant.fields.offset(i);
if field_start <= offset {
let field = variant.field(cx, i);
result = field.to_result().ok().and_then(|field| {
if ptr_end <= field_start + field.size {
// We found the right field, look inside it.
let field_info =
field.pointee_info_at(cx, offset - field_start);
field_info
ty::FnPtr(fn_sig) if offset.bytes() == 0 => {
tcx.layout_of(param_env.and(tcx.mk_fn_ptr(fn_sig))).ok().map(|layout| {
PointeeInfo { size: layout.size, align: layout.align.abi, safe: None }
})
}
ty::Ref(_, ty, mt) if offset.bytes() == 0 => {
let kind = if tcx.sess.opts.optimize == OptLevel::No {
// Use conservative pointer kind if not optimizing. This saves us the
// Freeze/Unpin queries, and can save time in the codegen backend (noalias
// attributes in LLVM have compile-time cost even in unoptimized builds).
PointerKind::SharedMutable
} else {
match mt {
hir::Mutability::Not => {
if ty.is_freeze(tcx, cx.param_env()) {
PointerKind::Frozen
} else {
None
PointerKind::SharedMutable
}
}
hir::Mutability::Mut => {
// References to self-referential structures should not be considered
// noalias, as another pointer to the structure can be obtained, that
// is not based-on the original reference. We consider all !Unpin
// types to be potentially self-referential here.
if ty.is_unpin(tcx, cx.param_env()) {
PointerKind::UniqueBorrowed
} else {
PointerKind::UniqueBorrowedPinned
}
}
}
};
tcx.layout_of(param_env.and(ty)).ok().map(|layout| PointeeInfo {
size: layout.size,
align: layout.align.abi,
safe: Some(kind),
})
}
_ => {
let mut data_variant = match this.variants {
// Within the discriminant field, only the niche itself is
// always initialized, so we only check for a pointer at its
// offset.
//
// If the niche is a pointer, it's either valid (according
// to its type), or null (which the niche field's scalar
// validity range encodes). This allows using
// `dereferenceable_or_null` for e.g., `Option<&T>`, and
// this will continue to work as long as we don't start
// using more niches than just null (e.g., the first page of
// the address space, or unaligned pointers).
Variants::Multiple {
tag_encoding: TagEncoding::Niche { untagged_variant, .. },
tag_field,
..
} if this.fields.offset(tag_field) == offset => {
Some(this.for_variant(cx, untagged_variant))
}
_ => Some(this),
};
if let Some(variant) = data_variant {
// We're not interested in any unions.
if let FieldsShape::Union(_) = variant.fields {
data_variant = None;
}
}
let mut result = None;
if let Some(variant) = data_variant {
// FIXME(erikdesjardins): handle non-default addrspace ptr sizes
// (requires passing in the expected address space from the caller)
let ptr_end = offset + Pointer(AddressSpace::DATA).size(cx);
for i in 0..variant.fields.count() {
let field_start = variant.fields.offset(i);
if field_start <= offset {
let field = variant.field(cx, i);
result = field.to_result().ok().and_then(|field| {
if ptr_end <= field_start + field.size {
// We found the right field, look inside it.
let field_info =
field.pointee_info_at(cx, offset - field_start);
field_info
} else {
None
}
});
if result.is_some() {
break;
}
});
if result.is_some() {
break;
}
}
}
}
// FIXME(eddyb) This should be for `ptr::Unique<T>`, not `Box<T>`.
if let Some(ref mut pointee) = result {
if let ty::Adt(def, _) = this.ty.kind() {
if def.is_box() && offset.bytes() == 0 {
pointee.safe = Some(PointerKind::UniqueOwned);
// FIXME(eddyb) This should be for `ptr::Unique<T>`, not `Box<T>`.
if let Some(ref mut pointee) = result {
if let ty::Adt(def, _) = this.ty.kind() {
if def.is_box() && offset.bytes() == 0 {
pointee.safe = Some(PointerKind::UniqueOwned);
}
}
}
}
result
}
};
result
}
};
debug!(
"pointee_info_at (offset={:?}, type kind: {:?}) => {:?}",

View File

@ -147,6 +147,12 @@ impl<'tcx, 'body> ParseCtxt<'tcx, 'body> {
ExprKind::AddressOf { mutability, arg } => Ok(
Rvalue::AddressOf(*mutability, self.parse_place(*arg)?)
),
ExprKind::Binary { op, lhs, rhs } => Ok(
Rvalue::BinaryOp(*op, Box::new((self.parse_operand(*lhs)?, self.parse_operand(*rhs)?)))
),
ExprKind::Unary { op, arg } => Ok(
Rvalue::UnaryOp(*op, self.parse_operand(*arg)?)
),
_ => self.parse_operand(expr_id).map(Rvalue::Use),
)
}

View File

@ -2372,7 +2372,7 @@ impl<'a> Parser<'a> {
/// Some special error handling for the "top-level" patterns in a match arm,
/// `for` loop, `let`, &c. (in contrast to subpatterns within such).
pub(crate) fn maybe_recover_colon_colon_in_pat_typo(
pub(crate) fn maybe_recover_colon_colon_in_pat_typo_or_anon_enum(
&mut self,
mut first_pat: P<Pat>,
expected: Expected,
@ -2383,26 +2383,41 @@ impl<'a> Parser<'a> {
if !matches!(first_pat.kind, PatKind::Ident(_, _, None) | PatKind::Path(..))
|| !self.look_ahead(1, |token| token.is_ident() && !token.is_reserved_ident())
{
let mut snapshot_type = self.create_snapshot_for_diagnostic();
snapshot_type.bump(); // `:`
match snapshot_type.parse_ty() {
Err(inner_err) => {
inner_err.cancel();
}
Ok(ty) => {
let Err(mut err) = self.expected_one_of_not_found(&[], &[]) else {
return first_pat;
};
err.span_label(ty.span, "specifying the type of a pattern isn't supported");
self.restore_snapshot(snapshot_type);
let span = first_pat.span.to(ty.span);
first_pat = self.mk_pat(span, PatKind::Wild);
err.emit();
}
}
return first_pat;
}
// The pattern looks like it might be a path with a `::` -> `:` typo:
// `match foo { bar:baz => {} }`
let span = self.token.span;
let colon_span = self.token.span;
// We only emit "unexpected `:`" error here if we can successfully parse the
// whole pattern correctly in that case.
let snapshot = self.create_snapshot_for_diagnostic();
let mut snapshot_pat = self.create_snapshot_for_diagnostic();
let mut snapshot_type = self.create_snapshot_for_diagnostic();
// Create error for "unexpected `:`".
match self.expected_one_of_not_found(&[], &[]) {
Err(mut err) => {
self.bump(); // Skip the `:`.
match self.parse_pat_no_top_alt(expected) {
snapshot_pat.bump(); // Skip the `:`.
snapshot_type.bump(); // Skip the `:`.
match snapshot_pat.parse_pat_no_top_alt(expected) {
Err(inner_err) => {
// Carry on as if we had not done anything, callers will emit a
// reasonable error.
inner_err.cancel();
err.cancel();
self.restore_snapshot(snapshot);
}
Ok(mut pat) => {
// We've parsed the rest of the pattern.
@ -2466,8 +2481,8 @@ impl<'a> Parser<'a> {
_ => {}
}
if show_sugg {
err.span_suggestion(
span,
err.span_suggestion_verbose(
colon_span.until(self.look_ahead(1, |t| t.span)),
"maybe write a path separator here",
"::",
Applicability::MaybeIncorrect,
@ -2475,13 +2490,24 @@ impl<'a> Parser<'a> {
} else {
first_pat = self.mk_pat(new_span, PatKind::Wild);
}
err.emit();
self.restore_snapshot(snapshot_pat);
}
}
match snapshot_type.parse_ty() {
Err(inner_err) => {
inner_err.cancel();
}
Ok(ty) => {
err.span_label(ty.span, "specifying the type of a pattern isn't supported");
self.restore_snapshot(snapshot_type);
let new_span = first_pat.span.to(ty.span);
first_pat = self.mk_pat(new_span, PatKind::Wild);
}
}
err.emit();
}
_ => {
// Carry on as if we had not done anything. This should be unreachable.
self.restore_snapshot(snapshot);
}
};
first_pat

View File

@ -116,7 +116,8 @@ impl<'a> Parser<'a> {
// Check if the user wrote `foo:bar` instead of `foo::bar`.
if ra == RecoverColon::Yes {
first_pat = self.maybe_recover_colon_colon_in_pat_typo(first_pat, expected);
first_pat =
self.maybe_recover_colon_colon_in_pat_typo_or_anon_enum(first_pat, expected);
}
if let Some(leading_vert_span) = leading_vert_span {

View File

@ -11,6 +11,7 @@ use rustc_ast::{
self as ast, BareFnTy, FnRetTy, GenericBound, GenericBounds, GenericParam, Generics, Lifetime,
MacCall, MutTy, Mutability, PolyTraitRef, TraitBoundModifier, TraitObjectSyntax, Ty, TyKind,
};
use rustc_ast_pretty::pprust;
use rustc_errors::{pluralize, struct_span_err, Applicability, PResult};
use rustc_span::source_map::Span;
use rustc_span::symbol::{kw, sym, Ident};
@ -43,17 +44,24 @@ pub(super) enum AllowPlus {
No,
}
#[derive(PartialEq)]
#[derive(PartialEq, Clone, Copy)]
pub(super) enum RecoverQPath {
Yes,
No,
}
#[derive(PartialEq, Clone, Copy)]
pub(super) enum RecoverQuestionMark {
Yes,
No,
}
#[derive(PartialEq, Clone, Copy)]
pub(super) enum RecoverAnonEnum {
Yes,
No,
}
/// Signals whether parsing a type should recover `->`.
///
/// More specifically, when parsing a function like:
@ -86,7 +94,7 @@ impl RecoverReturnSign {
}
// Is `...` (`CVarArgs`) legal at this level of type parsing?
#[derive(PartialEq)]
#[derive(PartialEq, Clone, Copy)]
enum AllowCVariadic {
Yes,
No,
@ -111,6 +119,7 @@ impl<'a> Parser<'a> {
RecoverReturnSign::Yes,
None,
RecoverQuestionMark::Yes,
RecoverAnonEnum::No,
)
}
@ -125,6 +134,7 @@ impl<'a> Parser<'a> {
RecoverReturnSign::Yes,
Some(ty_params),
RecoverQuestionMark::Yes,
RecoverAnonEnum::No,
)
}
@ -139,6 +149,7 @@ impl<'a> Parser<'a> {
RecoverReturnSign::Yes,
None,
RecoverQuestionMark::Yes,
RecoverAnonEnum::Yes,
)
}
@ -156,6 +167,7 @@ impl<'a> Parser<'a> {
RecoverReturnSign::Yes,
None,
RecoverQuestionMark::Yes,
RecoverAnonEnum::No,
)
}
@ -169,6 +181,7 @@ impl<'a> Parser<'a> {
RecoverReturnSign::Yes,
None,
RecoverQuestionMark::No,
RecoverAnonEnum::No,
)
}
@ -180,6 +193,7 @@ impl<'a> Parser<'a> {
RecoverReturnSign::Yes,
None,
RecoverQuestionMark::No,
RecoverAnonEnum::No,
)
}
@ -192,6 +206,7 @@ impl<'a> Parser<'a> {
RecoverReturnSign::OnlyFatArrow,
None,
RecoverQuestionMark::Yes,
RecoverAnonEnum::No,
)
}
@ -211,6 +226,7 @@ impl<'a> Parser<'a> {
recover_return_sign,
None,
RecoverQuestionMark::Yes,
RecoverAnonEnum::Yes,
)?;
FnRetTy::Ty(ty)
} else if recover_return_sign.can_recover(&self.token.kind) {
@ -232,6 +248,7 @@ impl<'a> Parser<'a> {
recover_return_sign,
None,
RecoverQuestionMark::Yes,
RecoverAnonEnum::Yes,
)?;
FnRetTy::Ty(ty)
} else {
@ -247,6 +264,7 @@ impl<'a> Parser<'a> {
recover_return_sign: RecoverReturnSign,
ty_generics: Option<&Generics>,
recover_question_mark: RecoverQuestionMark,
recover_anon_enum: RecoverAnonEnum,
) -> PResult<'a, P<Ty>> {
let allow_qpath_recovery = recover_qpath == RecoverQPath::Yes;
maybe_recover_from_interpolated_ty_qpath!(self, allow_qpath_recovery);
@ -325,14 +343,55 @@ impl<'a> Parser<'a> {
let mut ty = self.mk_ty(span, kind);
// Try to recover from use of `+` with incorrect priority.
if matches!(allow_plus, AllowPlus::Yes) {
if allow_plus == AllowPlus::Yes {
self.maybe_recover_from_bad_type_plus(&ty)?;
} else {
self.maybe_report_ambiguous_plus(impl_dyn_multi, &ty);
}
if let RecoverQuestionMark::Yes = recover_question_mark {
if RecoverQuestionMark::Yes == recover_question_mark {
ty = self.maybe_recover_from_question_mark(ty);
}
if recover_anon_enum == RecoverAnonEnum::Yes
&& self.check_noexpect(&token::BinOp(token::Or))
&& self.look_ahead(1, |t| t.can_begin_type())
{
let mut pipes = vec![self.token.span];
let mut types = vec![ty];
loop {
if !self.eat(&token::BinOp(token::Or)) {
break;
}
pipes.push(self.prev_token.span);
types.push(self.parse_ty_common(
allow_plus,
allow_c_variadic,
recover_qpath,
recover_return_sign,
ty_generics,
recover_question_mark,
RecoverAnonEnum::No,
)?);
}
let mut err = self.struct_span_err(pipes, "anonymous enums are not supported");
for ty in &types {
err.span_label(ty.span, "");
}
err.help(&format!(
"create a named `enum` and use it here instead:\nenum Name {{\n{}\n}}",
types
.iter()
.enumerate()
.map(|(i, t)| format!(
" Variant{}({}),",
i + 1, // Lets not confuse people with zero-indexing :)
pprust::to_string(|s| s.print_type(&t)),
))
.collect::<Vec<_>>()
.join("\n"),
));
err.emit();
return Ok(self.mk_ty(lo.to(self.prev_token.span), TyKind::Err));
}
if allow_qpath_recovery { self.maybe_recover_from_bad_qpath(ty) } else { Ok(ty) }
}

View File

@ -6,11 +6,12 @@
use crate::errors::{
self, AttrApplication, DebugVisualizerUnreadable, InvalidAttrAtCrateLevel, ObjectLifetimeErr,
OnlyHasEffectOn, TransparentIncompatible, UnrecognizedReprHint,
OnlyHasEffectOn, ProcMacroDiffArguments, ProcMacroInvalidAbi, ProcMacroMissingArguments,
ProcMacroTypeError, ProcMacroUnsafe, TransparentIncompatible, UnrecognizedReprHint,
};
use rustc_ast::{ast, AttrStyle, Attribute, LitKind, MetaItemKind, MetaItemLit, NestedMetaItem};
use rustc_data_structures::fx::FxHashMap;
use rustc_errors::{fluent, Applicability, MultiSpan};
use rustc_errors::{fluent, Applicability, IntoDiagnosticArg, MultiSpan};
use rustc_expand::base::resolve_path;
use rustc_feature::{AttributeDuplicates, AttributeType, BuiltinAttribute, BUILTIN_ATTRIBUTE_MAP};
use rustc_hir as hir;
@ -19,11 +20,12 @@ use rustc_hir::intravisit::{self, Visitor};
use rustc_hir::{
self, FnSig, ForeignItem, HirId, Item, ItemKind, TraitItem, CRATE_HIR_ID, CRATE_OWNER_ID,
};
use rustc_hir::{MethodKind, Target};
use rustc_hir::{MethodKind, Target, Unsafety};
use rustc_middle::hir::nested_filter;
use rustc_middle::middle::resolve_lifetime::ObjectLifetimeDefault;
use rustc_middle::ty::fast_reject::{DeepRejectCtxt, TreatParams};
use rustc_middle::ty::query::Providers;
use rustc_middle::ty::TyCtxt;
use rustc_middle::ty::{ParamEnv, TyCtxt};
use rustc_session::lint::builtin::{
CONFLICTING_REPR_HINTS, INVALID_DOC_ATTRIBUTES, UNUSED_ATTRIBUTES,
};
@ -31,6 +33,7 @@ use rustc_session::parse::feature_err;
use rustc_span::symbol::{kw, sym, Symbol};
use rustc_span::{Span, DUMMY_SP};
use rustc_target::spec::abi::Abi;
use std::cell::Cell;
use std::collections::hash_map::Entry;
pub(crate) fn target_from_impl_item<'tcx>(
@ -62,8 +65,29 @@ enum ItemLike<'tcx> {
ForeignItem,
}
#[derive(Copy, Clone)]
pub(crate) enum ProcMacroKind {
FunctionLike,
Derive,
Attribute,
}
impl IntoDiagnosticArg for ProcMacroKind {
fn into_diagnostic_arg(self) -> rustc_errors::DiagnosticArgValue<'static> {
match self {
ProcMacroKind::Attribute => "attribute proc macro",
ProcMacroKind::Derive => "derive proc macro",
ProcMacroKind::FunctionLike => "function-like proc macro",
}
.into_diagnostic_arg()
}
}
struct CheckAttrVisitor<'tcx> {
tcx: TyCtxt<'tcx>,
// Whether or not this visitor should abort after finding errors
abort: Cell<bool>,
}
impl CheckAttrVisitor<'_> {
@ -173,7 +197,7 @@ impl CheckAttrVisitor<'_> {
sym::path => self.check_generic_attr(hir_id, attr, target, Target::Mod),
sym::plugin_registrar => self.check_plugin_registrar(hir_id, attr, target),
sym::macro_export => self.check_macro_export(hir_id, attr, target),
sym::ignore | sym::should_panic | sym::proc_macro_derive => {
sym::ignore | sym::should_panic => {
self.check_generic_attr(hir_id, attr, target, Target::Fn)
}
sym::automatically_derived => {
@ -183,6 +207,16 @@ impl CheckAttrVisitor<'_> {
self.check_generic_attr(hir_id, attr, target, Target::Mod)
}
sym::rustc_object_lifetime_default => self.check_object_lifetime_default(hir_id),
sym::proc_macro => {
self.check_proc_macro(hir_id, target, ProcMacroKind::FunctionLike)
}
sym::proc_macro_attribute => {
self.check_proc_macro(hir_id, target, ProcMacroKind::Attribute);
}
sym::proc_macro_derive => {
self.check_generic_attr(hir_id, attr, target, Target::Fn);
self.check_proc_macro(hir_id, target, ProcMacroKind::Derive)
}
_ => {}
}
@ -2063,6 +2097,103 @@ impl CheckAttrVisitor<'_> {
errors::Unused { attr_span: attr.span, note },
);
}
/// A best effort attempt to create an error for a mismatching proc macro signature.
///
/// If this best effort goes wrong, it will just emit a worse error later (see #102923)
fn check_proc_macro(&self, hir_id: HirId, target: Target, kind: ProcMacroKind) {
let expected_input_count = match kind {
ProcMacroKind::Attribute => 2,
ProcMacroKind::Derive | ProcMacroKind::FunctionLike => 1,
};
let expected_signature = match kind {
ProcMacroKind::Attribute => "fn(TokenStream, TokenStream) -> TokenStream",
ProcMacroKind::Derive | ProcMacroKind::FunctionLike => "fn(TokenStream) -> TokenStream",
};
let tcx = self.tcx;
if target == Target::Fn {
let Some(tokenstream) = tcx.get_diagnostic_item(sym::TokenStream) else {return};
let tokenstream = tcx.type_of(tokenstream);
let id = hir_id.expect_owner();
let hir_sig = tcx.hir().fn_sig_by_hir_id(hir_id).unwrap();
let sig = tcx.liberate_late_bound_regions(id.to_def_id(), tcx.fn_sig(id));
let sig = tcx.normalize_erasing_regions(ParamEnv::empty(), sig);
// We don't currently require that the function signature is equal to
// `fn(TokenStream) -> TokenStream`, but instead monomorphizes to
// `fn(TokenStream) -> TokenStream` after some substitution of generic arguments.
//
// Properly checking this means pulling in additional `rustc` crates, so we don't.
let drcx = DeepRejectCtxt { treat_obligation_params: TreatParams::AsInfer };
if sig.abi != Abi::Rust {
tcx.sess.emit_err(ProcMacroInvalidAbi { span: hir_sig.span, abi: sig.abi.name() });
self.abort.set(true);
}
if sig.unsafety == Unsafety::Unsafe {
tcx.sess.emit_err(ProcMacroUnsafe { span: hir_sig.span });
self.abort.set(true);
}
let output = sig.output();
// Typecheck the output
if !drcx.types_may_unify(output, tokenstream) {
tcx.sess.emit_err(ProcMacroTypeError {
span: hir_sig.decl.output.span(),
found: output,
kind,
expected_signature,
});
self.abort.set(true);
}
if sig.inputs().len() < expected_input_count {
tcx.sess.emit_err(ProcMacroMissingArguments {
expected_input_count,
span: hir_sig.span,
kind,
expected_signature,
});
self.abort.set(true);
}
// Check that the inputs are correct, if there are enough.
if sig.inputs().len() >= expected_input_count {
for (arg, input) in
sig.inputs().iter().zip(hir_sig.decl.inputs).take(expected_input_count)
{
if !drcx.types_may_unify(*arg, tokenstream) {
tcx.sess.emit_err(ProcMacroTypeError {
span: input.span,
found: *arg,
kind,
expected_signature,
});
self.abort.set(true);
}
}
}
// Check that there are not too many arguments
let body_id = tcx.hir().body_owned_by(id.def_id);
let excess = tcx.hir().body(body_id).params.get(expected_input_count..);
if let Some(excess @ [begin @ end] | excess @ [begin, .., end]) = excess {
tcx.sess.emit_err(ProcMacroDiffArguments {
span: begin.span.to(end.span),
count: excess.len(),
kind,
expected_signature,
});
self.abort.set(true);
}
}
}
}
impl<'tcx> Visitor<'tcx> for CheckAttrVisitor<'tcx> {
@ -2225,12 +2356,15 @@ fn check_non_exported_macro_for_invalid_attrs(tcx: TyCtxt<'_>, item: &Item<'_>)
}
fn check_mod_attrs(tcx: TyCtxt<'_>, module_def_id: LocalDefId) {
let check_attr_visitor = &mut CheckAttrVisitor { tcx };
let check_attr_visitor = &mut CheckAttrVisitor { tcx, abort: Cell::new(false) };
tcx.hir().visit_item_likes_in_module(module_def_id, check_attr_visitor);
if module_def_id.is_top_level_module() {
check_attr_visitor.check_attributes(CRATE_HIR_ID, DUMMY_SP, Target::Mod, None);
check_invalid_crate_level_attr(tcx, tcx.hir().krate_attrs());
}
if check_attr_visitor.abort.get() {
tcx.sess.abort_if_errors()
}
}
pub(crate) fn provide(providers: &mut Providers) {

View File

@ -12,6 +12,7 @@ use rustc_macros::{Diagnostic, LintDiagnostic, Subdiagnostic};
use rustc_middle::ty::{MainDefinition, Ty};
use rustc_span::{Span, Symbol, DUMMY_SP};
use crate::check_attr::ProcMacroKind;
use crate::lang_items::Duplicate;
#[derive(Diagnostic)]
@ -1515,3 +1516,52 @@ pub struct ChangeFieldsToBeOfUnitType {
#[suggestion_part(code = "()")]
pub spans: Vec<Span>,
}
#[derive(Diagnostic)]
#[diag(passes_proc_macro_typeerror)]
#[note]
pub(crate) struct ProcMacroTypeError<'tcx> {
#[primary_span]
#[label]
pub span: Span,
pub found: Ty<'tcx>,
pub kind: ProcMacroKind,
pub expected_signature: &'static str,
}
#[derive(Diagnostic)]
#[diag(passes_proc_macro_diff_arg_count)]
pub(crate) struct ProcMacroDiffArguments {
#[primary_span]
#[label]
pub span: Span,
pub count: usize,
pub kind: ProcMacroKind,
pub expected_signature: &'static str,
}
#[derive(Diagnostic)]
#[diag(passes_proc_macro_missing_args)]
pub(crate) struct ProcMacroMissingArguments {
#[primary_span]
#[label]
pub span: Span,
pub expected_input_count: usize,
pub kind: ProcMacroKind,
pub expected_signature: &'static str,
}
#[derive(Diagnostic)]
#[diag(passes_proc_macro_invalid_abi)]
pub(crate) struct ProcMacroInvalidAbi {
#[primary_span]
pub span: Span,
pub abi: &'static str,
}
#[derive(Diagnostic)]
#[diag(passes_proc_macro_unsafe)]
pub(crate) struct ProcMacroUnsafe {
#[primary_span]
pub span: Span,
}

View File

@ -227,20 +227,27 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> {
&& let Some(FnCtxt::Assoc(_)) = fn_kind.ctxt()
&& let Some(items) = self.diagnostic_metadata.current_impl_items
&& let Some(item) = items.iter().find(|i| {
if let AssocItemKind::Fn(_) = &i.kind && i.ident.name == item_str.name
if let AssocItemKind::Fn(..) | AssocItemKind::Const(..) = &i.kind
&& i.ident.name == item_str.name
{
debug!(?item_str.name);
return true
}
false
})
&& let AssocItemKind::Fn(fn_) = &item.kind
{
debug!(?fn_);
let self_sugg = if fn_.sig.decl.has_self() { "self." } else { "Self::" };
let self_sugg = match &item.kind {
AssocItemKind::Fn(fn_) if fn_.sig.decl.has_self() => "self.",
_ => "Self::",
};
Some((
item_span.shrink_to_lo(),
"consider using the associated function",
match &item.kind {
AssocItemKind::Fn(..) => "consider using the associated function",
AssocItemKind::Const(..) => "consider using the associated constant",
_ => unreachable!("item kind was filtered above"),
},
self_sugg.to_string()
))
} else {

View File

@ -288,6 +288,7 @@ symbols! {
Target,
ToOwned,
ToString,
TokenStream,
Try,
TryCaptureGeneric,
TryCapturePrintable,

View File

@ -39,7 +39,7 @@ where
{
match arg_layout.abi {
Abi::Scalar(scalar) => match scalar.primitive() {
abi::Int(..) | abi::Pointer => {
abi::Int(..) | abi::Pointer(_) => {
if arg_layout.size.bits() > xlen {
return Err(CannotUseFpConv);
}

View File

@ -346,7 +346,7 @@ impl<'a, Ty> TyAndLayout<'a, Ty> {
// The primitive for this algorithm.
Abi::Scalar(scalar) => {
let kind = match scalar.primitive() {
abi::Int(..) | abi::Pointer => RegKind::Integer,
abi::Int(..) | abi::Pointer(_) => RegKind::Integer,
abi::F32 | abi::F64 => RegKind::Float,
};
Ok(HomogeneousAggregate::Homogeneous(Reg { kind, size: self.size }))

View File

@ -45,7 +45,7 @@ where
{
match arg_layout.abi {
Abi::Scalar(scalar) => match scalar.primitive() {
abi::Int(..) | abi::Pointer => {
abi::Int(..) | abi::Pointer(_) => {
if arg_layout.size.bits() > xlen {
return Err(CannotUseFpConv);
}

View File

@ -20,7 +20,7 @@ where
{
let dl = cx.data_layout();
if !scalar.primitive().is_float() {
if !matches!(scalar.primitive(), abi::F32 | abi::F64) {
return data;
}
@ -83,11 +83,11 @@ where
(abi::F32, _) => offset += Reg::f32().size,
(_, abi::F64) => offset += Reg::f64().size,
(abi::Int(i, _signed), _) => offset += i.size(),
(abi::Pointer, _) => offset += Reg::i64().size,
(abi::Pointer(_), _) => offset += Reg::i64().size,
_ => {}
}
if (offset.bytes() % 4) != 0 && scalar2.primitive().is_float() {
if (offset.bytes() % 4) != 0 && matches!(scalar2.primitive(), abi::F32 | abi::F64) {
offset += Size::from_bytes(4 - (offset.bytes() % 4));
}
data = arg_scalar(cx, scalar2, offset, data);

View File

@ -50,7 +50,7 @@ where
Abi::Uninhabited => return Ok(()),
Abi::Scalar(scalar) => match scalar.primitive() {
abi::Int(..) | abi::Pointer => Class::Int,
abi::Int(..) | abi::Pointer(_) => Class::Int,
abi::F32 | abi::F64 => Class::Sse,
},

View File

@ -129,7 +129,7 @@ impl<'a, Ty> TyAndLayout<'a, Ty> {
C: HasDataLayout,
{
match self.abi {
Abi::Scalar(scalar) => scalar.primitive().is_float(),
Abi::Scalar(scalar) => matches!(scalar.primitive(), F32 | F64),
Abi::Aggregate { .. } => {
if self.fields.count() == 1 && self.fields.offset(0).bytes() == 0 {
self.field(cx, 0).is_single_fp_element(cx)

View File

@ -138,6 +138,16 @@ pub(super) trait GoalKind<'tcx>: TypeFoldable<'tcx> + Copy + Eq {
ecx: &mut EvalCtxt<'_, 'tcx>,
goal: Goal<'tcx, Self>,
) -> QueryResult<'tcx>;
fn consider_builtin_future_candidate(
ecx: &mut EvalCtxt<'_, 'tcx>,
goal: Goal<'tcx, Self>,
) -> QueryResult<'tcx>;
fn consider_builtin_generator_candidate(
ecx: &mut EvalCtxt<'_, 'tcx>,
goal: Goal<'tcx, Self>,
) -> QueryResult<'tcx>;
}
impl<'tcx> EvalCtxt<'_, 'tcx> {
@ -266,6 +276,10 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
G::consider_builtin_tuple_candidate(self, goal)
} else if lang_items.pointee_trait() == Some(trait_def_id) {
G::consider_builtin_pointee_candidate(self, goal)
} else if lang_items.future_trait() == Some(trait_def_id) {
G::consider_builtin_future_candidate(self, goal)
} else if lang_items.gen_trait() == Some(trait_def_id) {
G::consider_builtin_generator_candidate(self, goal)
} else {
Err(NoSolution)
};
@ -321,9 +335,10 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
| ty::Tuple(_)
| ty::Param(_)
| ty::Placeholder(..)
| ty::Infer(_)
| ty::Infer(ty::IntVar(_) | ty::FloatVar(_))
| ty::Error(_) => return,
ty::Bound(..) => bug!("unexpected bound type: {goal:?}"),
ty::Infer(ty::TyVar(_) | ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_))
| ty::Bound(..) => bug!("unexpected self type for `{goal:?}`"),
ty::Alias(_, alias_ty) => alias_ty,
};
@ -371,9 +386,10 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
| ty::Tuple(_)
| ty::Param(_)
| ty::Placeholder(..)
| ty::Infer(_)
| ty::Infer(ty::IntVar(_) | ty::FloatVar(_))
| ty::Error(_) => return,
ty::Bound(..) => bug!("unexpected bound type: {goal:?}"),
ty::Infer(ty::TyVar(_) | ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_))
| ty::Bound(..) => bug!("unexpected self type for `{goal:?}`"),
ty::Dynamic(bounds, ..) => bounds,
};

View File

@ -1,13 +1,14 @@
use std::mem;
use super::{Certainty, InferCtxtEvalExt};
use rustc_infer::{
infer::InferCtxt,
traits::{
query::NoSolution, FulfillmentError, FulfillmentErrorCode, PredicateObligation,
SelectionError, TraitEngine,
},
use rustc_infer::infer::InferCtxt;
use rustc_infer::traits::{
query::NoSolution, FulfillmentError, FulfillmentErrorCode, MismatchedProjectionTypes,
PredicateObligation, SelectionError, TraitEngine,
};
use rustc_middle::ty;
use rustc_middle::ty::error::{ExpectedFound, TypeError};
use super::{Certainty, InferCtxtEvalExt};
/// A trait engine using the new trait solver.
///
@ -70,9 +71,55 @@ impl<'tcx> TraitEngine<'tcx> for FulfillmentCtxt<'tcx> {
Err(NoSolution) => {
errors.push(FulfillmentError {
obligation: obligation.clone(),
code: FulfillmentErrorCode::CodeSelectionError(
SelectionError::Unimplemented,
),
code: match goal.predicate.kind().skip_binder() {
ty::PredicateKind::Clause(ty::Clause::Projection(_)) => {
FulfillmentErrorCode::CodeProjectionError(
// FIXME: This could be a `Sorts` if the term is a type
MismatchedProjectionTypes { err: TypeError::Mismatch },
)
}
ty::PredicateKind::Subtype(pred) => {
let (a, b) = infcx.replace_bound_vars_with_placeholders(
goal.predicate.kind().rebind((pred.a, pred.b)),
);
let expected_found = ExpectedFound::new(true, a, b);
FulfillmentErrorCode::CodeSubtypeError(
expected_found,
TypeError::Sorts(expected_found),
)
}
ty::PredicateKind::Coerce(pred) => {
let (a, b) = infcx.replace_bound_vars_with_placeholders(
goal.predicate.kind().rebind((pred.a, pred.b)),
);
let expected_found = ExpectedFound::new(false, a, b);
FulfillmentErrorCode::CodeSubtypeError(
expected_found,
TypeError::Sorts(expected_found),
)
}
ty::PredicateKind::ConstEquate(a, b) => {
let (a, b) = infcx.replace_bound_vars_with_placeholders(
goal.predicate.kind().rebind((a, b)),
);
let expected_found = ExpectedFound::new(true, a, b);
FulfillmentErrorCode::CodeConstEquateError(
expected_found,
TypeError::ConstMismatch(expected_found),
)
}
ty::PredicateKind::Clause(_)
| ty::PredicateKind::WellFormed(_)
| ty::PredicateKind::ObjectSafe(_)
| ty::PredicateKind::ClosureKind(_, _, _)
| ty::PredicateKind::ConstEvaluatable(_)
| ty::PredicateKind::TypeWellFormedFromEnv(_)
| ty::PredicateKind::Ambiguous => {
FulfillmentErrorCode::CodeSelectionError(
SelectionError::Unimplemented,
)
}
},
root_obligation: obligation,
});
continue;

View File

@ -277,12 +277,15 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> {
param_env,
predicate: (def_id, substs, kind),
}),
ty::PredicateKind::ObjectSafe(trait_def_id) => {
self.compute_object_safe_goal(trait_def_id)
}
ty::PredicateKind::WellFormed(arg) => {
self.compute_well_formed_goal(Goal { param_env, predicate: arg })
}
ty::PredicateKind::Ambiguous => self.make_canonical_response(Certainty::AMBIGUOUS),
// FIXME: implement these predicates :)
ty::PredicateKind::WellFormed(_)
| ty::PredicateKind::ObjectSafe(_)
| ty::PredicateKind::ConstEvaluatable(_)
| ty::PredicateKind::ConstEquate(_, _) => {
ty::PredicateKind::ConstEvaluatable(_) | ty::PredicateKind::ConstEquate(_, _) => {
self.make_canonical_response(Certainty::Yes)
}
ty::PredicateKind::TypeWellFormedFromEnv(..) => {
@ -362,6 +365,32 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> {
Err(NoSolution)
}
}
fn compute_object_safe_goal(&mut self, trait_def_id: DefId) -> QueryResult<'tcx> {
if self.tcx().is_object_safe(trait_def_id) {
self.make_canonical_response(Certainty::Yes)
} else {
Err(NoSolution)
}
}
fn compute_well_formed_goal(
&mut self,
goal: Goal<'tcx, ty::GenericArg<'tcx>>,
) -> QueryResult<'tcx> {
self.infcx.probe(|_| {
match crate::traits::wf::unnormalized_obligations(
self.infcx,
goal.param_env,
goal.predicate,
) {
Some(obligations) => self.evaluate_all_and_make_canonical_response(
obligations.into_iter().map(|o| o.into()).collect(),
),
None => self.make_canonical_response(Certainty::AMBIGUOUS),
}
})
}
}
impl<'tcx> EvalCtxt<'_, 'tcx> {

View File

@ -16,7 +16,7 @@ use rustc_middle::ty::fast_reject::{DeepRejectCtxt, TreatParams};
use rustc_middle::ty::{self, Ty, TyCtxt};
use rustc_middle::ty::{ProjectionPredicate, TypeSuperVisitable, TypeVisitor};
use rustc_middle::ty::{ToPredicate, TypeVisitable};
use rustc_span::DUMMY_SP;
use rustc_span::{sym, DUMMY_SP};
use std::iter;
use std::ops::ControlFlow;
@ -427,7 +427,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> {
.subst(tcx, &[ty::GenericArg::from(goal.predicate.self_ty())])
}
ty::Infer(ty::TyVar(..)) | ty::Alias(_, _) | ty::Param(_) | ty::Placeholder(..) => {
ty::Alias(_, _) | ty::Param(_) | ty::Placeholder(..) => {
// FIXME(ptr_metadata): It would also be possible to return a `Ok(Ambig)` with no constraints.
let sized_predicate = ty::Binder::dummy(tcx.at(DUMMY_SP).mk_trait_ref(
LangItem::Sized,
@ -470,7 +470,9 @@ impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> {
}
},
ty::Infer(ty::FreshTy(..) | ty::FreshIntTy(..) | ty::FreshFloatTy(..))
ty::Infer(
ty::TyVar(_) | ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_),
)
| ty::Bound(..) => bug!(
"unexpected self ty `{:?}` when normalizing `<T as Pointee>::Metadata`",
goal.predicate.self_ty()
@ -482,6 +484,73 @@ impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> {
ecx.evaluate_all_and_make_canonical_response(nested_goals)
})
}
fn consider_builtin_future_candidate(
ecx: &mut EvalCtxt<'_, 'tcx>,
goal: Goal<'tcx, Self>,
) -> QueryResult<'tcx> {
let self_ty = goal.predicate.self_ty();
let ty::Generator(def_id, substs, _) = *self_ty.kind() else {
return Err(NoSolution);
};
// Generators are not futures unless they come from `async` desugaring
let tcx = ecx.tcx();
if !tcx.generator_is_async(def_id) {
return Err(NoSolution);
}
let term = substs.as_generator().return_ty().into();
Self::consider_assumption(
ecx,
goal,
ty::Binder::dummy(ty::ProjectionPredicate {
projection_ty: ecx.tcx().mk_alias_ty(goal.predicate.def_id(), [self_ty]),
term,
})
.to_predicate(tcx),
)
}
fn consider_builtin_generator_candidate(
ecx: &mut EvalCtxt<'_, 'tcx>,
goal: Goal<'tcx, Self>,
) -> QueryResult<'tcx> {
let self_ty = goal.predicate.self_ty();
let ty::Generator(def_id, substs, _) = *self_ty.kind() else {
return Err(NoSolution);
};
// `async`-desugared generators do not implement the generator trait
let tcx = ecx.tcx();
if tcx.generator_is_async(def_id) {
return Err(NoSolution);
}
let generator = substs.as_generator();
let name = tcx.associated_item(goal.predicate.def_id()).name;
let term = if name == sym::Return {
generator.return_ty().into()
} else if name == sym::Yield {
generator.yield_ty().into()
} else {
bug!("unexpected associated item `<{self_ty} as Generator>::{name}`")
};
Self::consider_assumption(
ecx,
goal,
ty::Binder::dummy(ty::ProjectionPredicate {
projection_ty: ecx
.tcx()
.mk_alias_ty(goal.predicate.def_id(), [self_ty, generator.resume_ty()]),
term,
})
.to_predicate(tcx),
)
}
}
/// This behavior is also implemented in `rustc_ty_utils` and in the old `project` code.

View File

@ -192,6 +192,50 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
) -> QueryResult<'tcx> {
ecx.make_canonical_response(Certainty::Yes)
}
fn consider_builtin_future_candidate(
ecx: &mut EvalCtxt<'_, 'tcx>,
goal: Goal<'tcx, Self>,
) -> QueryResult<'tcx> {
let ty::Generator(def_id, _, _) = *goal.predicate.self_ty().kind() else {
return Err(NoSolution);
};
// Generators are not futures unless they come from `async` desugaring
let tcx = ecx.tcx();
if !tcx.generator_is_async(def_id) {
return Err(NoSolution);
}
// Async generator unconditionally implement `Future`
ecx.make_canonical_response(Certainty::Yes)
}
fn consider_builtin_generator_candidate(
ecx: &mut EvalCtxt<'_, 'tcx>,
goal: Goal<'tcx, Self>,
) -> QueryResult<'tcx> {
let self_ty = goal.predicate.self_ty();
let ty::Generator(def_id, substs, _) = *self_ty.kind() else {
return Err(NoSolution);
};
// `async`-desugared generators do not implement the generator trait
let tcx = ecx.tcx();
if tcx.generator_is_async(def_id) {
return Err(NoSolution);
}
let generator = substs.as_generator();
Self::consider_assumption(
ecx,
goal,
ty::Binder::dummy(
tcx.mk_trait_ref(goal.predicate.def_id(), [self_ty, generator.resume_ty()]),
)
.to_predicate(tcx),
)
}
}
impl<'tcx> EvalCtxt<'_, 'tcx> {

View File

@ -24,15 +24,16 @@ pub(super) fn instantiate_constituent_tys_for_auto_trait<'tcx>(
| ty::Never
| ty::Char => Ok(vec![]),
ty::Placeholder(..)
| ty::Dynamic(..)
ty::Dynamic(..)
| ty::Param(..)
| ty::Foreign(..)
| ty::Alias(ty::Projection, ..)
| ty::Bound(..)
| ty::Infer(ty::TyVar(_)) => Err(NoSolution),
| ty::Placeholder(..) => Err(NoSolution),
ty::Infer(ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)) => bug!(),
ty::Bound(..)
| ty::Infer(ty::TyVar(_) | ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)) => {
bug!("unexpected type `{ty}`")
}
ty::RawPtr(ty::TypeAndMut { ty: element_ty, .. }) | ty::Ref(_, element_ty, _) => {
Ok(vec![element_ty])
@ -99,11 +100,12 @@ pub(super) fn instantiate_constituent_tys_for_sized_trait<'tcx>(
| ty::Foreign(..)
| ty::Alias(..)
| ty::Param(_)
| ty::Infer(ty::TyVar(_)) => Err(NoSolution),
| ty::Placeholder(..) => Err(NoSolution),
ty::Placeholder(..)
| ty::Bound(..)
| ty::Infer(ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)) => bug!(),
ty::Bound(..)
| ty::Infer(ty::TyVar(_) | ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)) => {
bug!("unexpected type `{ty}`")
}
ty::Tuple(tys) => Ok(tys.to_vec()),
@ -148,11 +150,12 @@ pub(super) fn instantiate_constituent_tys_for_copy_clone_trait<'tcx>(
| ty::Adt(_, _)
| ty::Alias(_, _)
| ty::Param(_)
| ty::Infer(ty::TyVar(_)) => Err(NoSolution),
| ty::Placeholder(..) => Err(NoSolution),
ty::Placeholder(..)
| ty::Bound(..)
| ty::Infer(ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)) => bug!(),
ty::Bound(..)
| ty::Infer(ty::TyVar(_) | ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)) => {
bug!("unexpected type `{ty}`")
}
ty::Tuple(tys) => Ok(tys.to_vec()),
@ -173,6 +176,7 @@ pub(super) fn instantiate_constituent_tys_for_copy_clone_trait<'tcx>(
}
}
// Returns a binder of the tupled inputs types and output type from a builtin callable type.
pub(crate) fn extract_tupled_inputs_and_output_from_callable<'tcx>(
tcx: TyCtxt<'tcx>,
self_ty: Ty<'tcx>,
@ -215,9 +219,13 @@ pub(crate) fn extract_tupled_inputs_and_output_from_callable<'tcx>(
| ty::Tuple(_)
| ty::Alias(_, _)
| ty::Param(_)
| ty::Placeholder(_)
| ty::Bound(_, _)
| ty::Infer(_)
| ty::Placeholder(..)
| ty::Infer(ty::IntVar(_) | ty::FloatVar(_))
| ty::Error(_) => Err(NoSolution),
ty::Bound(..)
| ty::Infer(ty::TyVar(_) | ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)) => {
bug!("unexpected type `{self_ty}`")
}
}
}

View File

@ -4,8 +4,8 @@ use rustc_hir as hir;
use rustc_hir::lang_items::LangItem;
use rustc_middle::ty::subst::{GenericArg, GenericArgKind, SubstsRef};
use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitable};
use rustc_span::def_id::{DefId, LocalDefId};
use rustc_span::Span;
use rustc_span::def_id::{DefId, LocalDefId, CRATE_DEF_ID};
use rustc_span::{Span, DUMMY_SP};
use std::iter;
/// Returns the set of obligations needed to make `arg` well-formed.
@ -75,6 +75,34 @@ pub fn obligations<'tcx>(
Some(result)
}
/// Compute the predicates that are required for a type to be well-formed.
///
/// This is only intended to be used in the new solver, since it does not
/// take into account recursion depth or proper error-reporting spans.
pub fn unnormalized_obligations<'tcx>(
infcx: &InferCtxt<'tcx>,
param_env: ty::ParamEnv<'tcx>,
arg: GenericArg<'tcx>,
) -> Option<Vec<traits::PredicateObligation<'tcx>>> {
if let ty::GenericArgKind::Lifetime(..) = arg.unpack() {
return Some(vec![]);
}
debug_assert_eq!(arg, infcx.resolve_vars_if_possible(arg));
let mut wf = WfPredicates {
tcx: infcx.tcx,
param_env,
body_id: CRATE_DEF_ID,
span: DUMMY_SP,
out: vec![],
recursion_depth: 0,
item: None,
};
wf.compute(arg);
Some(wf.out)
}
/// Returns the obligations that make this trait reference
/// well-formed. For example, if there is a trait `Set` defined like
/// `trait Set<K:Eq>`, then the trait reference `Foo: Set<Bar>` is WF

View File

@ -244,7 +244,7 @@ fn adjust_for_rust_scalar<'tcx>(
}
// Only pointer types handled below.
let Scalar::Initialized { value: Pointer, valid_range} = scalar else { return };
let Scalar::Initialized { value: Pointer(_), valid_range} = scalar else { return };
if !valid_range.contains(0) {
attrs.set(ArgAttribute::NonNull);
@ -479,7 +479,7 @@ fn fn_abi_adjust_for_abi<'tcx>(
}
let size = arg.layout.size;
if arg.layout.is_unsized() || size > Pointer.size(cx) {
if arg.layout.is_unsized() || size > Pointer(AddressSpace::DATA).size(cx) {
arg.make_indirect();
} else {
// We want to pass small aggregates as immediates, but using

View File

@ -134,7 +134,7 @@ fn layout_of_uncached<'tcx>(
ty::FloatTy::F64 => F64,
}),
ty::FnPtr(_) => {
let mut ptr = scalar_unit(Pointer);
let mut ptr = scalar_unit(Pointer(dl.instruction_address_space));
ptr.valid_range_mut().start = 1;
tcx.intern_layout(LayoutS::scalar(cx, ptr))
}
@ -144,7 +144,7 @@ fn layout_of_uncached<'tcx>(
// Potentially-wide pointers.
ty::Ref(_, pointee, _) | ty::RawPtr(ty::TypeAndMut { ty: pointee, .. }) => {
let mut data_ptr = scalar_unit(Pointer);
let mut data_ptr = scalar_unit(Pointer(AddressSpace::DATA));
if !ty.is_unsafe_ptr() {
data_ptr.valid_range_mut().start = 1;
}
@ -178,7 +178,7 @@ fn layout_of_uncached<'tcx>(
}
ty::Slice(_) | ty::Str => scalar_unit(Int(dl.ptr_sized_integer(), false)),
ty::Dynamic(..) => {
let mut vtable = scalar_unit(Pointer);
let mut vtable = scalar_unit(Pointer(AddressSpace::DATA));
vtable.valid_range_mut().start = 1;
vtable
}
@ -195,7 +195,7 @@ fn layout_of_uncached<'tcx>(
ty::Dynamic(_, _, ty::DynStar) => {
let mut data = scalar_unit(Int(dl.ptr_sized_integer(), false));
data.valid_range_mut().start = 0;
let mut vtable = scalar_unit(Pointer);
let mut vtable = scalar_unit(Pointer(AddressSpace::DATA));
vtable.valid_range_mut().start = 1;
tcx.intern_layout(cx.scalar_pair(data, vtable))
}

View File

@ -74,6 +74,7 @@ pub fn is_available() -> bool {
///
/// This is both the input and output of `#[proc_macro]`, `#[proc_macro_attribute]`
/// and `#[proc_macro_derive]` definitions.
#[rustc_diagnostic_item = "TokenStream"]
#[stable(feature = "proc_macro_lib", since = "1.15.0")]
#[derive(Clone)]
pub struct TokenStream(Option<bridge::client::TokenStream>);

View File

@ -16,12 +16,17 @@ fn main() {
let mut build_lock;
let _build_lock_guard;
if cfg!(any(unix, windows)) {
build_lock = fd_lock::RwLock::new(t!(std::fs::File::create(config.out.join("lock"))));
let path = config.out.join("lock");
build_lock = fd_lock::RwLock::new(t!(std::fs::File::create(&path)));
_build_lock_guard = match build_lock.try_write() {
Ok(lock) => lock,
err => {
println!("warning: build directory locked, waiting for lock");
drop(err);
if let Some(pid) = get_lock_owner(&path) {
println!("warning: build directory locked by process {pid}, waiting for lock");
} else {
println!("warning: build directory locked, waiting for lock");
}
t!(build_lock.write())
}
};
@ -98,3 +103,30 @@ fn check_version(config: &Config) -> Option<String> {
Some(msg)
}
/// Get the PID of the process which took the write lock by
/// parsing `/proc/locks`.
#[cfg(target_os = "linux")]
fn get_lock_owner(f: &std::path::Path) -> Option<u64> {
use std::fs::File;
use std::io::{BufRead, BufReader};
use std::os::unix::fs::MetadataExt;
let lock_inode = std::fs::metadata(f).ok()?.ino();
let lockfile = File::open("/proc/locks").ok()?;
BufReader::new(lockfile).lines().find_map(|line| {
// pid--vvvvvv vvvvvvv--- inode
// 21: FLOCK ADVISORY WRITE 359238 08:02:3719774 0 EOF
let line = line.ok()?;
let parts = line.split_whitespace().collect::<Vec<_>>();
let (pid, inode) = (parts[4].parse::<u64>().ok()?, &parts[5]);
let inode = inode.rsplit_once(':')?.1.parse::<u64>().ok()?;
if inode == lock_inode { Some(pid) } else { None }
})
}
#[cfg(not(target_os = "linux"))]
fn get_lock_owner(_: &std::path::Path) -> Option<u64> {
// FIXME: Implement on other OS's
None
}

View File

@ -109,3 +109,28 @@ pub unsafe fn transmute_fn_ptr_to_data(x: fn()) -> *const () {
// as long as it doesn't cause a verifier error by using `bitcast`.
transmute(x)
}
pub enum Either<T, U> { A(T), B(U) }
// Previously, we would codegen this as passing/returning a scalar pair of `{ i8, ptr }`,
// with the `ptr` field representing both `&i32` and `fn()` depending on the variant.
// This is incorrect, because `fn()` should be `ptr addrspace(1)`, not `ptr`.
// CHECK: define{{.+}}void @should_not_combine_addrspace({{.+\*|ptr}}{{.+}}sret{{.+}}%0, {{.+\*|ptr}}{{.+}}%x)
#[no_mangle]
#[inline(never)]
pub fn should_not_combine_addrspace(x: Either<&i32, fn()>) -> Either<&i32, fn()> {
x
}
// The incorrectness described above would result in us producing (after optimizations)
// a `ptrtoint`/`inttoptr` roundtrip to convert from `ptr` to `ptr addrspace(1)`.
// CHECK-LABEL: @call_with_fn_ptr
#[no_mangle]
pub fn call_with_fn_ptr<'a>(f: fn()) -> Either<&'a i32, fn()> {
// CHECK-NOT: ptrtoint
// CHECK-NOT: inttoptr
// CHECK: call addrspace(1) void @should_not_combine_addrspace
should_not_combine_addrspace(Either::B(f))
}

View File

@ -0,0 +1,26 @@
// MIR for `f` after built
fn f(_1: i32, _2: bool) -> i32 {
let mut _0: i32; // return place in scope 0 at $DIR/operators.rs:+0:30: +0:33
bb0: {
_1 = Neg(_1); // scope 0 at $DIR/operators.rs:+2:9: +2:15
_2 = Not(_2); // scope 0 at $DIR/operators.rs:+3:9: +3:15
_1 = Add(_1, _1); // scope 0 at $DIR/operators.rs:+4:9: +4:18
_1 = Sub(_1, _1); // scope 0 at $DIR/operators.rs:+5:9: +5:18
_1 = Mul(_1, _1); // scope 0 at $DIR/operators.rs:+6:9: +6:18
_1 = Div(_1, _1); // scope 0 at $DIR/operators.rs:+7:9: +7:18
_1 = Rem(_1, _1); // scope 0 at $DIR/operators.rs:+8:9: +8:18
_1 = BitXor(_1, _1); // scope 0 at $DIR/operators.rs:+9:9: +9:18
_1 = BitAnd(_1, _1); // scope 0 at $DIR/operators.rs:+10:9: +10:18
_1 = Shl(_1, _1); // scope 0 at $DIR/operators.rs:+11:9: +11:19
_1 = Shr(_1, _1); // scope 0 at $DIR/operators.rs:+12:9: +12:19
_2 = Eq(_1, _1); // scope 0 at $DIR/operators.rs:+13:9: +13:19
_2 = Lt(_1, _1); // scope 0 at $DIR/operators.rs:+14:9: +14:18
_2 = Le(_1, _1); // scope 0 at $DIR/operators.rs:+15:9: +15:19
_2 = Ge(_1, _1); // scope 0 at $DIR/operators.rs:+16:9: +16:19
_2 = Gt(_1, _1); // scope 0 at $DIR/operators.rs:+17:9: +17:18
_0 = _1; // scope 0 at $DIR/operators.rs:+18:9: +18:16
return; // scope 0 at $DIR/operators.rs:+19:9: +19:17
}
}

View File

@ -0,0 +1,28 @@
// compile-flags: --crate-type=lib
#![feature(custom_mir, core_intrinsics, inline_const)]
use std::intrinsics::mir::*;
// EMIT_MIR operators.f.built.after.mir
#[custom_mir(dialect = "built")]
pub fn f(a: i32, b: bool) -> i32 {
mir!({
a = -a;
b = !b;
a = a + a;
a = a - a;
a = a * a;
a = a / a;
a = a % a;
a = a ^ a;
a = a & a;
a = a << a;
a = a >> a;
b = a == a;
b = a < a;
b = a <= a;
b = a >= a;
b = a > a;
RET = a;
Return()
})
}

View File

@ -0,0 +1,17 @@
fn foo(x: bool | i32) -> i32 | f64 {
//~^ ERROR anonymous enums are not supported
//~| ERROR anonymous enums are not supported
match x {
x: i32 => x, //~ ERROR expected
true => 42.,
false => 0.333,
}
}
fn main() {
match foo(true) {
42: i32 => (), //~ ERROR expected
_: f64 => (), //~ ERROR expected
x: i32 => (), //~ ERROR expected
}
}

View File

@ -0,0 +1,68 @@
error: anonymous enums are not supported
--> $DIR/anon-enums.rs:1:16
|
LL | fn foo(x: bool | i32) -> i32 | f64 {
| ---- ^ ---
|
= help: create a named `enum` and use it here instead:
enum Name {
Variant1(bool),
Variant2(i32),
}
error: anonymous enums are not supported
--> $DIR/anon-enums.rs:1:30
|
LL | fn foo(x: bool | i32) -> i32 | f64 {
| --- ^ ---
|
= help: create a named `enum` and use it here instead:
enum Name {
Variant1(i32),
Variant2(f64),
}
error: expected one of `@` or `|`, found `:`
--> $DIR/anon-enums.rs:5:10
|
LL | x: i32 => x,
| ^ --- specifying the type of a pattern isn't supported
| |
| expected one of `@` or `|`
|
help: maybe write a path separator here
|
LL | x::i32 => x,
| ~~
error: expected one of `...`, `..=`, `..`, or `|`, found `:`
--> $DIR/anon-enums.rs:13:11
|
LL | 42: i32 => (),
| ^ --- specifying the type of a pattern isn't supported
| |
| expected one of `...`, `..=`, `..`, or `|`
error: expected `|`, found `:`
--> $DIR/anon-enums.rs:14:10
|
LL | _: f64 => (),
| ^ --- specifying the type of a pattern isn't supported
| |
| expected `|`
error: expected one of `@` or `|`, found `:`
--> $DIR/anon-enums.rs:15:10
|
LL | x: i32 => (),
| ^ --- specifying the type of a pattern isn't supported
| |
| expected one of `@` or `|`
|
help: maybe write a path separator here
|
LL | x::i32 => (),
| ~~
error: aborting due to 6 previous errors

View File

@ -0,0 +1,20 @@
// build-pass
macro_rules! check_ty {
($Z:ty) => { compile_error!("triggered"); };
($X:ty | $Y:ty) => { $X };
}
macro_rules! check {
($Z:ty) => { compile_error!("triggered"); };
($X:ty | $Y:ty) => { };
}
check! { i32 | u8 }
fn foo(x: check_ty! { i32 | u8 }) -> check_ty! { i32 | u8 } {
x
}
fn main() {
let x: check_ty! { i32 | u8 } = 42;
let _: check_ty! { i32 | u8 } = foo(x);
}

View File

@ -68,7 +68,6 @@ fn main() {
Foo:Bar::Baz => {}
//~^ ERROR: expected one of
//~| HELP: maybe write a path separator here
//~| ERROR: failed to resolve: `Bar` is a variant, not a module
}
match myfoo {
Foo::Bar => {}

View File

@ -2,89 +2,118 @@ error: expected one of `@` or `|`, found `:`
--> $DIR/issue-87086-colon-path-sep.rs:17:12
|
LL | Foo:Bar => {}
| ^
| ^--- specifying the type of a pattern isn't supported
| |
| expected one of `@` or `|`
| help: maybe write a path separator here: `::`
|
help: maybe write a path separator here
|
LL | Foo::Bar => {}
| ~~
error: expected one of `!`, `(`, `...`, `..=`, `..`, `::`, `{`, or `|`, found `:`
--> $DIR/issue-87086-colon-path-sep.rs:23:17
|
LL | qux::Foo:Bar => {}
| ^
| ^--- specifying the type of a pattern isn't supported
| |
| expected one of 8 possible tokens
| help: maybe write a path separator here: `::`
|
help: maybe write a path separator here
|
LL | qux::Foo::Bar => {}
| ~~
error: expected one of `@` or `|`, found `:`
--> $DIR/issue-87086-colon-path-sep.rs:29:12
|
LL | qux:Foo::Baz => {}
| ^
| ^-------- specifying the type of a pattern isn't supported
| |
| expected one of `@` or `|`
| help: maybe write a path separator here: `::`
|
help: maybe write a path separator here
|
LL | qux::Foo::Baz => {}
| ~~
error: expected one of `@` or `|`, found `:`
--> $DIR/issue-87086-colon-path-sep.rs:35:12
|
LL | qux: Foo::Baz if true => {}
| ^
| ^ -------- specifying the type of a pattern isn't supported
| |
| expected one of `@` or `|`
| help: maybe write a path separator here: `::`
|
help: maybe write a path separator here
|
LL | qux::Foo::Baz if true => {}
| ~~
error: expected one of `@` or `|`, found `:`
--> $DIR/issue-87086-colon-path-sep.rs:40:15
|
LL | if let Foo:Bar = f() {
| ^
| ^--- specifying the type of a pattern isn't supported
| |
| expected one of `@` or `|`
| help: maybe write a path separator here: `::`
|
help: maybe write a path separator here
|
LL | if let Foo::Bar = f() {
| ~~
error: expected one of `@` or `|`, found `:`
--> $DIR/issue-87086-colon-path-sep.rs:48:16
|
LL | ref qux: Foo::Baz => {}
| ^
| ^ -------- specifying the type of a pattern isn't supported
| |
| expected one of `@` or `|`
| help: maybe write a path separator here: `::`
|
help: maybe write a path separator here
|
LL | ref qux::Foo::Baz => {}
| ~~
error: expected one of `@` or `|`, found `:`
--> $DIR/issue-87086-colon-path-sep.rs:57:16
|
LL | mut qux: Foo::Baz => {}
| ^
| ^ -------- specifying the type of a pattern isn't supported
| |
| expected one of `@` or `|`
| help: maybe write a path separator here: `::`
|
help: maybe write a path separator here
|
LL | mut qux::Foo::Baz => {}
| ~~
error: expected one of `@` or `|`, found `:`
--> $DIR/issue-87086-colon-path-sep.rs:68:12
|
LL | Foo:Bar::Baz => {}
| ^
| ^-------- specifying the type of a pattern isn't supported
| |
| expected one of `@` or `|`
| help: maybe write a path separator here: `::`
|
help: maybe write a path separator here
|
LL | Foo::Bar::Baz => {}
| ~~
error: expected one of `@` or `|`, found `:`
--> $DIR/issue-87086-colon-path-sep.rs:75:12
--> $DIR/issue-87086-colon-path-sep.rs:74:12
|
LL | Foo:Bar => {}
| ^
| ^--- specifying the type of a pattern isn't supported
| |
| expected one of `@` or `|`
| help: maybe write a path separator here: `::`
error[E0433]: failed to resolve: `Bar` is a variant, not a module
--> $DIR/issue-87086-colon-path-sep.rs:68:13
|
LL | Foo:Bar::Baz => {}
| ^^^ `Bar` is a variant, not a module
help: maybe write a path separator here
|
LL | Foo::Bar => {}
| ~~
error: aborting due to 10 previous errors
error: aborting due to 9 previous errors
For more information about this error, try `rustc --explain E0433`.

View File

@ -0,0 +1,26 @@
// check-pass
// force-host
// no-prefer-dynamic
#![crate_type = "proc-macro"]
#![allow(private_in_public)]
extern crate proc_macro;
use proc_macro::TokenStream;
#[proc_macro]
pub fn foo<T>(t: T) -> TokenStream {
TokenStream::new()
}
trait Project {
type Assoc;
}
impl Project for () {
type Assoc = TokenStream;
}
#[proc_macro]
pub fn uwu(_input: <() as Project>::Assoc) -> <() as Project>::Assoc {
TokenStream::new()
}

View File

@ -0,0 +1,31 @@
// force-host
// no-prefer-dynamic
#![crate_type = "proc-macro"]
#![allow(warnings)]
extern crate proc_macro;
use proc_macro::TokenStream;
#[proc_macro]
pub extern "C" fn abi(a: TokenStream) -> TokenStream {
//~^ ERROR proc macro functions may not be `extern "C"`
a
}
#[proc_macro]
pub extern "system" fn abi2(a: TokenStream) -> TokenStream {
//~^ ERROR proc macro functions may not be `extern "system"`
a
}
#[proc_macro]
pub extern fn abi3(a: TokenStream) -> TokenStream {
//~^ ERROR proc macro functions may not be `extern "C"`
a
}
#[proc_macro]
pub extern "Rust" fn abi4(a: TokenStream) -> TokenStream {
a
}

View File

@ -0,0 +1,20 @@
error: proc macro functions may not be `extern "C"`
--> $DIR/proc-macro-abi.rs:11:1
|
LL | pub extern "C" fn abi(a: TokenStream) -> TokenStream {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: proc macro functions may not be `extern "system"`
--> $DIR/proc-macro-abi.rs:17:1
|
LL | pub extern "system" fn abi2(a: TokenStream) -> TokenStream {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: proc macro functions may not be `extern "C"`
--> $DIR/proc-macro-abi.rs:23:1
|
LL | pub extern fn abi3(a: TokenStream) -> TokenStream {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: aborting due to 3 previous errors

View File

@ -0,0 +1,32 @@
// force-host
// no-prefer-dynamic
#![crate_type = "proc-macro"]
extern crate proc_macro;
use proc_macro::TokenStream;
#[proc_macro_attribute]
pub fn bad_input(input: String) -> TokenStream {
//~^ ERROR mismatched attribute proc macro signature
::proc_macro::TokenStream::new()
}
#[proc_macro_attribute]
pub fn bad_output(input: TokenStream) -> String {
//~^ ERROR mismatched attribute proc macro signature
//~| ERROR mismatched attribute proc macro signature
String::from("blah")
}
#[proc_macro_attribute]
pub fn bad_everything(input: String) -> String {
//~^ ERROR mismatched attribute proc macro signature
//~| ERROR mismatched attribute proc macro signature
input
}
#[proc_macro_attribute]
pub fn too_many(a: TokenStream, b: TokenStream, c: String) -> TokenStream {
//~^ ERROR mismatched attribute proc macro signature
}

View File

@ -0,0 +1,42 @@
error: mismatched attribute proc macro signature
--> $DIR/signature-proc-macro-attribute.rs:10:1
|
LL | pub fn bad_input(input: String) -> TokenStream {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ attribute proc macro must have two arguments of type `proc_macro::TokenStream`
error: mismatched attribute proc macro signature
--> $DIR/signature-proc-macro-attribute.rs:16:42
|
LL | pub fn bad_output(input: TokenStream) -> String {
| ^^^^^^ found std::string::String, expected type `proc_macro::TokenStream`
|
= note: attribute proc macros must have a signature of `fn(TokenStream, TokenStream) -> TokenStream`
error: mismatched attribute proc macro signature
--> $DIR/signature-proc-macro-attribute.rs:16:1
|
LL | pub fn bad_output(input: TokenStream) -> String {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ attribute proc macro must have two arguments of type `proc_macro::TokenStream`
error: mismatched attribute proc macro signature
--> $DIR/signature-proc-macro-attribute.rs:23:41
|
LL | pub fn bad_everything(input: String) -> String {
| ^^^^^^ found std::string::String, expected type `proc_macro::TokenStream`
|
= note: attribute proc macros must have a signature of `fn(TokenStream, TokenStream) -> TokenStream`
error: mismatched attribute proc macro signature
--> $DIR/signature-proc-macro-attribute.rs:23:1
|
LL | pub fn bad_everything(input: String) -> String {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ attribute proc macro must have two arguments of type `proc_macro::TokenStream`
error: mismatched attribute proc macro signature
--> $DIR/signature-proc-macro-attribute.rs:30:49
|
LL | pub fn too_many(a: TokenStream, b: TokenStream, c: String) -> TokenStream {
| ^^^^^^^^^ found unexpected argument
error: aborting due to 6 previous errors

View File

@ -0,0 +1,31 @@
// force-host
// no-prefer-dynamic
#![crate_type = "proc-macro"]
extern crate proc_macro;
use proc_macro::TokenStream;
#[proc_macro_derive(Blah)]
pub fn bad_input(input: String) -> TokenStream {
//~^ ERROR mismatched derive proc macro signature
TokenStream::new()
}
#[proc_macro_derive(Bleh)]
pub fn bad_output(input: TokenStream) -> String {
//~^ ERROR mismatched derive proc macro signature
String::from("blah")
}
#[proc_macro_derive(Bluh)]
pub fn bad_everything(input: String) -> String {
//~^ ERROR mismatched derive proc macro signature
//~| ERROR mismatched derive proc macro signature
input
}
#[proc_macro_derive(Blih)]
pub fn too_many(a: TokenStream, b: TokenStream, c: String) -> TokenStream {
//~^ ERROR mismatched derive proc macro signature
}

View File

@ -0,0 +1,40 @@
error: mismatched derive proc macro signature
--> $DIR/signature-proc-macro-derive.rs:10:25
|
LL | pub fn bad_input(input: String) -> TokenStream {
| ^^^^^^ found std::string::String, expected type `proc_macro::TokenStream`
|
= note: derive proc macros must have a signature of `fn(TokenStream) -> TokenStream`
error: mismatched derive proc macro signature
--> $DIR/signature-proc-macro-derive.rs:16:42
|
LL | pub fn bad_output(input: TokenStream) -> String {
| ^^^^^^ found std::string::String, expected type `proc_macro::TokenStream`
|
= note: derive proc macros must have a signature of `fn(TokenStream) -> TokenStream`
error: mismatched derive proc macro signature
--> $DIR/signature-proc-macro-derive.rs:22:41
|
LL | pub fn bad_everything(input: String) -> String {
| ^^^^^^ found std::string::String, expected type `proc_macro::TokenStream`
|
= note: derive proc macros must have a signature of `fn(TokenStream) -> TokenStream`
error: mismatched derive proc macro signature
--> $DIR/signature-proc-macro-derive.rs:22:30
|
LL | pub fn bad_everything(input: String) -> String {
| ^^^^^^ found std::string::String, expected type `proc_macro::TokenStream`
|
= note: derive proc macros must have a signature of `fn(TokenStream) -> TokenStream`
error: mismatched derive proc macro signature
--> $DIR/signature-proc-macro-derive.rs:29:33
|
LL | pub fn too_many(a: TokenStream, b: TokenStream, c: String) -> TokenStream {
| ^^^^^^^^^^^^^^^^^^^^^^^^^ found unexpected arguments
error: aborting due to 5 previous errors

View File

@ -0,0 +1,31 @@
// force-host
// no-prefer-dynamic
#![crate_type = "proc-macro"]
extern crate proc_macro;
use proc_macro::TokenStream;
#[proc_macro]
pub fn bad_input(input: String) -> TokenStream {
//~^ ERROR mismatched function-like proc macro signature
::proc_macro::TokenStream::new()
}
#[proc_macro]
pub fn bad_output(input: TokenStream) -> String {
//~^ ERROR mismatched function-like proc macro signature
String::from("blah")
}
#[proc_macro]
pub fn bad_everything(input: String) -> String {
//~^ ERROR mismatched function-like proc macro signature
//~| ERROR mismatched function-like proc macro signature
input
}
#[proc_macro]
pub fn too_many(a: TokenStream, b: TokenStream, c: String) -> TokenStream {
//~^ ERROR mismatched function-like proc macro signature
}

View File

@ -0,0 +1,40 @@
error: mismatched function-like proc macro signature
--> $DIR/signature-proc-macro.rs:10:25
|
LL | pub fn bad_input(input: String) -> TokenStream {
| ^^^^^^ found std::string::String, expected type `proc_macro::TokenStream`
|
= note: function-like proc macros must have a signature of `fn(TokenStream) -> TokenStream`
error: mismatched function-like proc macro signature
--> $DIR/signature-proc-macro.rs:16:42
|
LL | pub fn bad_output(input: TokenStream) -> String {
| ^^^^^^ found std::string::String, expected type `proc_macro::TokenStream`
|
= note: function-like proc macros must have a signature of `fn(TokenStream) -> TokenStream`
error: mismatched function-like proc macro signature
--> $DIR/signature-proc-macro.rs:22:41
|
LL | pub fn bad_everything(input: String) -> String {
| ^^^^^^ found std::string::String, expected type `proc_macro::TokenStream`
|
= note: function-like proc macros must have a signature of `fn(TokenStream) -> TokenStream`
error: mismatched function-like proc macro signature
--> $DIR/signature-proc-macro.rs:22:30
|
LL | pub fn bad_everything(input: String) -> String {
| ^^^^^^ found std::string::String, expected type `proc_macro::TokenStream`
|
= note: function-like proc macros must have a signature of `fn(TokenStream) -> TokenStream`
error: mismatched function-like proc macro signature
--> $DIR/signature-proc-macro.rs:29:33
|
LL | pub fn too_many(a: TokenStream, b: TokenStream, c: String) -> TokenStream {
| ^^^^^^^^^^^^^^^^^^^^^^^^^ found unexpected arguments
error: aborting due to 5 previous errors

View File

@ -8,6 +8,10 @@ extern crate proc_macro;
#[proc_macro_derive(A)]
pub unsafe extern "C" fn foo(a: i32, b: u32) -> u32 {
//~^ ERROR: expected a `Fn<(proc_macro::TokenStream,)>` closure, found `unsafe extern "C" fn
//~^ ERROR: mismatched derive proc macro signature
//~| mismatched derive proc macro signature
//~| mismatched derive proc macro signature
//~| proc macro functions may not be `extern
//~| proc macro functions may not be `unsafe
loop {}
}

View File

@ -1,20 +1,36 @@
error[E0277]: expected a `Fn<(proc_macro::TokenStream,)>` closure, found `unsafe extern "C" fn(i32, u32) -> u32 {foo}`
error: proc macro functions may not be `extern "C"`
--> $DIR/signature.rs:10:1
|
LL | / pub unsafe extern "C" fn foo(a: i32, b: u32) -> u32 {
LL | |
LL | | loop {}
LL | | }
| | ^
| | |
| |_call the function in a closure: `|| unsafe { /* code */ }`
| required by a bound introduced by this call
LL | pub unsafe extern "C" fn foo(a: i32, b: u32) -> u32 {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: proc macro functions may not be `unsafe`
--> $DIR/signature.rs:10:1
|
= help: the trait `Fn<(proc_macro::TokenStream,)>` is not implemented for fn item `unsafe extern "C" fn(i32, u32) -> u32 {foo}`
= note: unsafe function cannot be called generically without an unsafe block
note: required by a bound in `ProcMacro::custom_derive`
--> $SRC_DIR/proc_macro/src/bridge/client.rs:LL:COL
LL | pub unsafe extern "C" fn foo(a: i32, b: u32) -> u32 {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: aborting due to previous error
error: mismatched derive proc macro signature
--> $DIR/signature.rs:10:49
|
LL | pub unsafe extern "C" fn foo(a: i32, b: u32) -> u32 {
| ^^^ found u32, expected type `proc_macro::TokenStream`
|
= note: derive proc macros must have a signature of `fn(TokenStream) -> TokenStream`
error: mismatched derive proc macro signature
--> $DIR/signature.rs:10:33
|
LL | pub unsafe extern "C" fn foo(a: i32, b: u32) -> u32 {
| ^^^ found i32, expected type `proc_macro::TokenStream`
|
= note: derive proc macros must have a signature of `fn(TokenStream) -> TokenStream`
error: mismatched derive proc macro signature
--> $DIR/signature.rs:10:38
|
LL | pub unsafe extern "C" fn foo(a: i32, b: u32) -> u32 {
| ^^^^^^ found unexpected argument
error: aborting due to 5 previous errors
For more information about this error, try `rustc --explain E0277`.

View File

@ -0,0 +1,11 @@
struct Foo;
impl Foo {
const A_CONST: usize = 1;
fn foo() -> usize {
A_CONST //~ ERROR cannot find value `A_CONST` in this scope
}
}
fn main() {}

View File

@ -0,0 +1,14 @@
error[E0425]: cannot find value `A_CONST` in this scope
--> $DIR/assoc-const-without-self.rs:7:9
|
LL | A_CONST
| ^^^^^^^ not found in this scope
|
help: consider using the associated constant
|
LL | Self::A_CONST
| ++++++
error: aborting due to previous error
For more information about this error, try `rustc --explain E0425`.

View File

@ -0,0 +1,17 @@
error[E0271]: expected `[async block@$DIR/async.rs:12:17: 12:25]` to be a future that resolves to `i32`, but it resolves to `()`
--> $DIR/async.rs:12:17
|
LL | needs_async(async {});
| ----------- ^^^^^^^^ expected `i32`, found `()`
| |
| required by a bound introduced by this call
|
note: required by a bound in `needs_async`
--> $DIR/async.rs:8:31
|
LL | fn needs_async(_: impl Future<Output = i32>) {}
| ^^^^^^^^^^^^ required by this bound in `needs_async`
error: aborting due to previous error
For more information about this error, try `rustc --explain E0271`.

View File

@ -0,0 +1,19 @@
// compile-flags: -Ztrait-solver=next
// edition: 2021
// revisions: pass fail
//[pass] check-pass
use std::future::Future;
fn needs_async(_: impl Future<Output = i32>) {}
#[cfg(fail)]
fn main() {
needs_async(async {});
//[fail]~^ ERROR to be a future that resolves to `i32`, but it resolves to `()`
}
#[cfg(pass)]
fn main() {
needs_async(async { 1i32 });
}

View File

@ -0,0 +1,64 @@
error[E0277]: the trait bound `[generator@$DIR/generator.rs:18:21: 18:23]: Generator<A>` is not satisfied
--> $DIR/generator.rs:18:21
|
LL | needs_generator(|| {
| _____---------------_^
| | |
| | required by a bound introduced by this call
LL | |
LL | |
LL | |
LL | | yield ();
LL | | });
| |_____^ the trait `Generator<A>` is not implemented for `[generator@$DIR/generator.rs:18:21: 18:23]`
|
note: required by a bound in `needs_generator`
--> $DIR/generator.rs:14:28
|
LL | fn needs_generator(_: impl Generator<A, Yield = B, Return = C>) {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `needs_generator`
error[E0271]: type mismatch resolving `<[generator@$DIR/generator.rs:18:21: 18:23] as Generator<A>>::Yield == B`
--> $DIR/generator.rs:18:21
|
LL | needs_generator(|| {
| _____---------------_^
| | |
| | required by a bound introduced by this call
LL | |
LL | |
LL | |
LL | | yield ();
LL | | });
| |_____^ types differ
|
note: required by a bound in `needs_generator`
--> $DIR/generator.rs:14:41
|
LL | fn needs_generator(_: impl Generator<A, Yield = B, Return = C>) {}
| ^^^^^^^^^ required by this bound in `needs_generator`
error[E0271]: type mismatch resolving `<[generator@$DIR/generator.rs:18:21: 18:23] as Generator<A>>::Return == C`
--> $DIR/generator.rs:18:21
|
LL | needs_generator(|| {
| _____---------------_^
| | |
| | required by a bound introduced by this call
LL | |
LL | |
LL | |
LL | | yield ();
LL | | });
| |_____^ types differ
|
note: required by a bound in `needs_generator`
--> $DIR/generator.rs:14:52
|
LL | fn needs_generator(_: impl Generator<A, Yield = B, Return = C>) {}
| ^^^^^^^^^^ required by this bound in `needs_generator`
error: aborting due to 3 previous errors
Some errors have detailed explanations: E0271, E0277.
For more information about an error, try `rustc --explain E0271`.

View File

@ -0,0 +1,32 @@
// compile-flags: -Ztrait-solver=next
// edition: 2021
// revisions: pass fail
//[pass] check-pass
#![feature(generator_trait, generators)]
use std::ops::Generator;
struct A;
struct B;
struct C;
fn needs_generator(_: impl Generator<A, Yield = B, Return = C>) {}
#[cfg(fail)]
fn main() {
needs_generator(|| {
//[fail]~^ ERROR Generator<A>` is not satisfied
//[fail]~| ERROR as Generator<A>>::Yield == B`
//[fail]~| ERROR as Generator<A>>::Return == C`
yield ();
});
}
#[cfg(pass)]
fn main() {
needs_generator(|_: A| {
let _: A = yield B;
C
})
}

View File

@ -0,0 +1,20 @@
// The error message here still is pretty confusing.
fn main() {
let mut result = vec![1];
// The type of `result` is constrained to be `Vec<{integer}>` here.
// But the logic we use to find what expression constrains a type
// is not sophisticated enough to know this.
let mut vector = Vec::new();
vector.sort();
result.push(vector);
//~^ ERROR mismatched types
// So it thinks that the type of `result` is constrained here.
}
fn example2() {
let mut x = vec![1];
x.push("");
//~^ ERROR mismatched types
}

View File

@ -0,0 +1,29 @@
error[E0308]: mismatched types
--> $DIR/bad-type-in-vec-push.rs:11:17
|
LL | vector.sort();
| ------ here the type of `vector` is inferred to be `Vec<_>`
LL | result.push(vector);
| ---- ^^^^^^ expected integer, found struct `Vec`
| |
| arguments to this method are incorrect
|
= note: expected type `{integer}`
found struct `Vec<_>`
note: associated function defined here
--> $SRC_DIR/alloc/src/vec/mod.rs:LL:COL
error[E0308]: mismatched types
--> $DIR/bad-type-in-vec-push.rs:18:12
|
LL | x.push("");
| ---- ^^ expected integer, found `&str`
| |
| arguments to this method are incorrect
|
note: associated function defined here
--> $SRC_DIR/alloc/src/vec/mod.rs:LL:COL
error: aborting due to 2 previous errors
For more information about this error, try `rustc --explain E0308`.

View File

@ -456,6 +456,9 @@ These commits modify **compiler targets**.
(See the [Target Tier Policy](https://doc.rust-lang.org/nightly/rustc/target-tier-policy.html).)
"""
[mentions."src/doc/style-guide"]
cc = ["@rust-lang/style"]
[assign]
warn_non_default_branch = true
contributing_url = "https://rustc-dev-guide.rust-lang.org/contributing.html"
@ -560,6 +563,12 @@ ast_lowering = [
fallback = [
"@Mark-Simulacrum"
]
style-team = [
"@calebcartwright",
"@compiler-errors",
"@joshtriplett",
"@yaahc",
]
[assign.owners]
"/.github/workflows" = ["infra-ci"]
@ -604,6 +613,7 @@ fallback = [
"/src/doc/rust-by-example" = ["@ehuss"]
"/src/doc/rustc-dev-guide" = ["@ehuss"]
"/src/doc/rustdoc" = ["rustdoc"]
"/src/doc/style-guide" = ["style-team"]
"/src/etc" = ["@Mark-Simulacrum"]
"/src/librustdoc" = ["rustdoc"]
"/src/llvm-project" = ["@cuviper"]