From f92000816e0cf8bdfe6ad422464ce0fa6144b496 Mon Sep 17 00:00:00 2001 From: mejrs <> Date: Tue, 3 Jan 2023 14:08:20 +0100 Subject: [PATCH 01/19] Improve proc macro attribute diagnostics --- .../locales/en-US/passes.ftl | 21 +++ compiler/rustc_passes/src/check_attr.rs | 130 +++++++++++++++++- compiler/rustc_passes/src/errors.rs | 50 +++++++ compiler/rustc_span/src/symbol.rs | 1 + library/proc_macro/src/lib.rs | 1 + tests/ui/proc-macro/proc-macro-abi.rs | 28 ++++ tests/ui/proc-macro/proc-macro-abi.stderr | 20 +++ .../signature-proc-macro-attribute.rs | 29 ++++ .../signature-proc-macro-attribute.stderr | 42 ++++++ .../proc-macro/signature-proc-macro-derive.rs | 28 ++++ .../signature-proc-macro-derive.stderr | 40 ++++++ tests/ui/proc-macro/signature-proc-macro.rs | 28 ++++ .../ui/proc-macro/signature-proc-macro.stderr | 40 ++++++ tests/ui/proc-macro/signature.rs | 6 +- tests/ui/proc-macro/signature.stderr | 46 +++++-- 15 files changed, 488 insertions(+), 22 deletions(-) create mode 100644 tests/ui/proc-macro/proc-macro-abi.rs create mode 100644 tests/ui/proc-macro/proc-macro-abi.stderr create mode 100644 tests/ui/proc-macro/signature-proc-macro-attribute.rs create mode 100644 tests/ui/proc-macro/signature-proc-macro-attribute.stderr create mode 100644 tests/ui/proc-macro/signature-proc-macro-derive.rs create mode 100644 tests/ui/proc-macro/signature-proc-macro-derive.stderr create mode 100644 tests/ui/proc-macro/signature-proc-macro.rs create mode 100644 tests/ui/proc-macro/signature-proc-macro.stderr diff --git a/compiler/rustc_error_messages/locales/en-US/passes.ftl b/compiler/rustc_error_messages/locales/en-US/passes.ftl index 001e53d1d0e4c..63db6b6837b47 100644 --- a/compiler/rustc_error_messages/locales/en-US/passes.ftl +++ b/compiler/rustc_error_messages/locales/en-US/passes.ftl @@ -707,3 +707,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` diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index c59c06ac31ed7..517bf2533c5e6 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -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,11 @@ 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::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 +32,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 +64,27 @@ 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>, + abort: Cell, } impl CheckAttrVisitor<'_> { @@ -172,7 +193,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 => { @@ -182,6 +203,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) + } _ => {} } @@ -2052,6 +2083,90 @@ impl CheckAttrVisitor<'_> { errors::Unused { attr_span: attr.span, note }, ); } + + 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.fn_sig(id); + + 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().skip_binder(); + + // Typecheck the output + if tcx.normalize_erasing_regions(ParamEnv::empty(), output) != tokenstream { + tcx.sess.emit_err(ProcMacroTypeError { + span: hir_sig.decl.output.span(), + found: output, + kind, + expected_signature, + }); + self.abort.set(true); + } + + // Typecheck "expected_input_count" inputs, emitting + // `ProcMacroMissingArguments` if there are not enough. + if let Some(args) = sig.inputs().skip_binder().get(0..expected_input_count) { + for (arg, input) in args.iter().zip(hir_sig.decl.inputs) { + if tcx.normalize_erasing_regions(ParamEnv::empty(), *arg) != tokenstream { + tcx.sess.emit_err(ProcMacroTypeError { + span: input.span, + found: *arg, + kind, + expected_signature, + }); + self.abort.set(true); + } + } + } else { + tcx.sess.emit_err(ProcMacroMissingArguments { + expected_input_count, + span: hir_sig.span, + 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> { @@ -2214,12 +2329,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) { diff --git a/compiler/rustc_passes/src/errors.rs b/compiler/rustc_passes/src/errors.rs index c6cd69add28a0..68103608ec9d9 100644 --- a/compiler/rustc_passes/src/errors.rs +++ b/compiler/rustc_passes/src/errors.rs @@ -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(LintDiagnostic)] @@ -1508,3 +1509,52 @@ pub struct ChangeFieldsToBeOfUnitType { #[suggestion_part(code = "()")] pub spans: Vec, } + +#[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, +} diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index fbb12701d96ab..0cc9567d4d672 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -287,6 +287,7 @@ symbols! { Target, ToOwned, ToString, + TokenStream, Try, TryCaptureGeneric, TryCapturePrintable, diff --git a/library/proc_macro/src/lib.rs b/library/proc_macro/src/lib.rs index f0e4f5d8a8013..8bff40c279aaa 100644 --- a/library/proc_macro/src/lib.rs +++ b/library/proc_macro/src/lib.rs @@ -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); diff --git a/tests/ui/proc-macro/proc-macro-abi.rs b/tests/ui/proc-macro/proc-macro-abi.rs new file mode 100644 index 0000000000000..2a40ddd496ce8 --- /dev/null +++ b/tests/ui/proc-macro/proc-macro-abi.rs @@ -0,0 +1,28 @@ +#![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 + a +} + +#[proc_macro] +pub extern "system" fn abi2(a: TokenStream) -> TokenStream { + //~^ ERROR proc macro functions may not be `extern + a +} + +#[proc_macro] +pub extern fn abi3(a: TokenStream) -> TokenStream { + //~^ ERROR proc macro functions may not be `extern + a +} + +#[proc_macro] +pub extern "Rust" fn abi4(a: TokenStream) -> TokenStream { + a +} diff --git a/tests/ui/proc-macro/proc-macro-abi.stderr b/tests/ui/proc-macro/proc-macro-abi.stderr new file mode 100644 index 0000000000000..fa5f5dc098998 --- /dev/null +++ b/tests/ui/proc-macro/proc-macro-abi.stderr @@ -0,0 +1,20 @@ +error: proc macro functions may not be `extern "C"` + --> $DIR/proc-macro-abi.rs:8:1 + | +LL | pub extern "C" fn abi(a: TokenStream) -> TokenStream { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: proc macro functions may not be `extern "system"` + --> $DIR/proc-macro-abi.rs:14:1 + | +LL | pub extern "system" fn abi2(a: TokenStream) -> TokenStream { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: proc macro functions may not be `extern "C"` + --> $DIR/proc-macro-abi.rs:20:1 + | +LL | pub extern fn abi3(a: TokenStream) -> TokenStream { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 3 previous errors + diff --git a/tests/ui/proc-macro/signature-proc-macro-attribute.rs b/tests/ui/proc-macro/signature-proc-macro-attribute.rs new file mode 100644 index 0000000000000..fb17710950106 --- /dev/null +++ b/tests/ui/proc-macro/signature-proc-macro-attribute.rs @@ -0,0 +1,29 @@ +#![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 +} diff --git a/tests/ui/proc-macro/signature-proc-macro-attribute.stderr b/tests/ui/proc-macro/signature-proc-macro-attribute.stderr new file mode 100644 index 0000000000000..ecfd2b06e109c --- /dev/null +++ b/tests/ui/proc-macro/signature-proc-macro-attribute.stderr @@ -0,0 +1,42 @@ +error: mismatched attribute proc macro signature + --> $DIR/signature-proc-macro-attribute.rs:7: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:13: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:13: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:20: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:20: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:27:49 + | +LL | pub fn too_many(a: TokenStream, b: TokenStream, c: String) -> TokenStream { + | ^^^^^^^^^ found unexpected argument + +error: aborting due to 6 previous errors + diff --git a/tests/ui/proc-macro/signature-proc-macro-derive.rs b/tests/ui/proc-macro/signature-proc-macro-derive.rs new file mode 100644 index 0000000000000..a079157538fb6 --- /dev/null +++ b/tests/ui/proc-macro/signature-proc-macro-derive.rs @@ -0,0 +1,28 @@ +#![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 +} diff --git a/tests/ui/proc-macro/signature-proc-macro-derive.stderr b/tests/ui/proc-macro/signature-proc-macro-derive.stderr new file mode 100644 index 0000000000000..bb2bd9a93d24b --- /dev/null +++ b/tests/ui/proc-macro/signature-proc-macro-derive.stderr @@ -0,0 +1,40 @@ +error: mismatched derive proc macro signature + --> $DIR/signature-proc-macro-derive.rs:7: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:13: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:19: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:19: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:26:33 + | +LL | pub fn too_many(a: TokenStream, b: TokenStream, c: String) -> TokenStream { + | ^^^^^^^^^^^^^^^^^^^^^^^^^ found unexpected arguments + +error: aborting due to 5 previous errors + diff --git a/tests/ui/proc-macro/signature-proc-macro.rs b/tests/ui/proc-macro/signature-proc-macro.rs new file mode 100644 index 0000000000000..35d5be2171283 --- /dev/null +++ b/tests/ui/proc-macro/signature-proc-macro.rs @@ -0,0 +1,28 @@ +#![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 +} diff --git a/tests/ui/proc-macro/signature-proc-macro.stderr b/tests/ui/proc-macro/signature-proc-macro.stderr new file mode 100644 index 0000000000000..32241e1b9e00a --- /dev/null +++ b/tests/ui/proc-macro/signature-proc-macro.stderr @@ -0,0 +1,40 @@ +error: mismatched function-like proc macro signature + --> $DIR/signature-proc-macro.rs:7: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:13: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:19: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:19: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:26:33 + | +LL | pub fn too_many(a: TokenStream, b: TokenStream, c: String) -> TokenStream { + | ^^^^^^^^^^^^^^^^^^^^^^^^^ found unexpected arguments + +error: aborting due to 5 previous errors + diff --git a/tests/ui/proc-macro/signature.rs b/tests/ui/proc-macro/signature.rs index 2302238253e82..11187aa31bd80 100644 --- a/tests/ui/proc-macro/signature.rs +++ b/tests/ui/proc-macro/signature.rs @@ -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 {} } diff --git a/tests/ui/proc-macro/signature.stderr b/tests/ui/proc-macro/signature.stderr index 79f2001da0055..3dbe3f22a0df8 100644 --- a/tests/ui/proc-macro/signature.stderr +++ b/tests/ui/proc-macro/signature.stderr @@ -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 + | +LL | pub unsafe extern "C" fn foo(a: i32, b: u32) -> u32 { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +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 | - = 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 { + | ^^^^^^ found unexpected argument -error: aborting due to previous error +error: aborting due to 5 previous errors -For more information about this error, try `rustc --explain E0277`. From a8e3abd04cf85080d921c2d1875e0094b2db5155 Mon Sep 17 00:00:00 2001 From: mejrs <> Date: Sun, 8 Jan 2023 01:37:22 +0100 Subject: [PATCH 02/19] Address feedback --- compiler/rustc_passes/src/check_attr.rs | 56 +++++++++++++++-------- tests/ui/proc-macro/allowed-signatures.rs | 24 ++++++++++ tests/ui/proc-macro/proc-macro-abi.rs | 6 +-- 3 files changed, 63 insertions(+), 23 deletions(-) create mode 100644 tests/ui/proc-macro/allowed-signatures.rs diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index 517bf2533c5e6..c2ce46d965cf3 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -23,6 +23,7 @@ use rustc_hir::{ 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::{ParamEnv, TyCtxt}; use rustc_session::lint::builtin::{ @@ -84,6 +85,8 @@ impl IntoDiagnosticArg for ProcMacroKind { struct CheckAttrVisitor<'tcx> { tcx: TyCtxt<'tcx>, + + // Whether or not this visitor should abort after finding errors abort: Cell, } @@ -2084,6 +2087,9 @@ impl CheckAttrVisitor<'_> { ); } + /// 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, @@ -2103,23 +2109,30 @@ impl CheckAttrVisitor<'_> { let id = hir_id.expect_owner(); let hir_sig = tcx.hir().fn_sig_by_hir_id(hir_id).unwrap(); - let sig = tcx.fn_sig(id); + 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() }); + 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 { + if sig.unsafety == Unsafety::Unsafe { tcx.sess.emit_err(ProcMacroUnsafe { span: hir_sig.span }); self.abort.set(true); } - let output = sig.output().skip_binder(); + let output = sig.output(); // Typecheck the output - if tcx.normalize_erasing_regions(ParamEnv::empty(), output) != tokenstream { + if !drcx.types_may_unify(output, tokenstream) { tcx.sess.emit_err(ProcMacroTypeError { span: hir_sig.decl.output.span(), found: output, @@ -2129,11 +2142,22 @@ impl CheckAttrVisitor<'_> { self.abort.set(true); } - // Typecheck "expected_input_count" inputs, emitting - // `ProcMacroMissingArguments` if there are not enough. - if let Some(args) = sig.inputs().skip_binder().get(0..expected_input_count) { - for (arg, input) in args.iter().zip(hir_sig.decl.inputs) { - if tcx.normalize_erasing_regions(ParamEnv::empty(), *arg) != tokenstream { + 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, @@ -2143,14 +2167,6 @@ impl CheckAttrVisitor<'_> { self.abort.set(true); } } - } else { - tcx.sess.emit_err(ProcMacroMissingArguments { - expected_input_count, - span: hir_sig.span, - kind, - expected_signature, - }); - self.abort.set(true); } // Check that there are not too many arguments diff --git a/tests/ui/proc-macro/allowed-signatures.rs b/tests/ui/proc-macro/allowed-signatures.rs new file mode 100644 index 0000000000000..03c6ef86632df --- /dev/null +++ b/tests/ui/proc-macro/allowed-signatures.rs @@ -0,0 +1,24 @@ +// check-pass + +#![crate_type = "proc-macro"] +#![allow(private_in_public)] +extern crate proc_macro; +use proc_macro::TokenStream; + +#[proc_macro] +pub fn foo(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() +} diff --git a/tests/ui/proc-macro/proc-macro-abi.rs b/tests/ui/proc-macro/proc-macro-abi.rs index 2a40ddd496ce8..8e9d50d7832d5 100644 --- a/tests/ui/proc-macro/proc-macro-abi.rs +++ b/tests/ui/proc-macro/proc-macro-abi.rs @@ -6,19 +6,19 @@ use proc_macro::TokenStream; #[proc_macro] pub extern "C" fn abi(a: TokenStream) -> TokenStream { - //~^ ERROR proc macro functions may not be `extern + //~^ 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 + //~^ 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 + //~^ ERROR proc macro functions may not be `extern "C"` a } From 2d824206655bfb26cb5eed43490ee396542b153e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Sun, 15 Jan 2023 22:36:46 +0000 Subject: [PATCH 03/19] Teach parser to understand fake anonymous enum syntax Parse `-> Ty | OtherTy`. Parse type ascription in top level patterns. --- compiler/rustc_ast/src/ast.rs | 3 + compiler/rustc_ast/src/mut_visit.rs | 2 +- compiler/rustc_ast/src/visit.rs | 4 +- compiler/rustc_ast_lowering/src/lib.rs | 1 + compiler/rustc_ast_pretty/src/pprust/state.rs | 3 + .../rustc_parse/src/parser/diagnostics.rs | 50 +++++++++--- compiler/rustc_parse/src/parser/pat.rs | 3 +- compiler/rustc_parse/src/parser/ty.rs | 67 ++++++++++++++- compiler/rustc_passes/src/hir_stats.rs | 1 + src/tools/rustfmt/src/types.rs | 4 +- tests/ui/parser/anon-enums.rs | 19 +++++ tests/ui/parser/anon-enums.stderr | 81 +++++++++++++++++++ .../issues/issue-87086-colon-path-sep.stderr | 72 ++++++++++++----- 13 files changed, 270 insertions(+), 40 deletions(-) create mode 100644 tests/ui/parser/anon-enums.rs create mode 100644 tests/ui/parser/anon-enums.stderr diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs index 7de594719ddc4..3aad51325dcd6 100644 --- a/compiler/rustc_ast/src/ast.rs +++ b/compiler/rustc_ast/src/ast.rs @@ -2096,6 +2096,9 @@ pub enum TyKind { Err, /// Placeholder for a `va_list`. CVarArgs, + /// Placeholder for "anonymous enums", which don't exist, but keeping their + /// information around lets us produce better diagnostics. + AnonEnum(Vec>), } impl TyKind { diff --git a/compiler/rustc_ast/src/mut_visit.rs b/compiler/rustc_ast/src/mut_visit.rs index 77f342d1eb322..894885cf0fea7 100644 --- a/compiler/rustc_ast/src/mut_visit.rs +++ b/compiler/rustc_ast/src/mut_visit.rs @@ -470,7 +470,7 @@ pub fn noop_visit_ty(ty: &mut P, vis: &mut T) { vis.visit_fn_decl(decl); vis.visit_span(decl_span); } - TyKind::Tup(tys) => visit_vec(tys, |ty| vis.visit_ty(ty)), + TyKind::AnonEnum(tys) | TyKind::Tup(tys) => visit_vec(tys, |ty| vis.visit_ty(ty)), TyKind::Paren(ty) => vis.visit_ty(ty), TyKind::Path(qself, path) => { vis.visit_qself(qself); diff --git a/compiler/rustc_ast/src/visit.rs b/compiler/rustc_ast/src/visit.rs index e8823eff83afe..1ab70f0309c01 100644 --- a/compiler/rustc_ast/src/visit.rs +++ b/compiler/rustc_ast/src/visit.rs @@ -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::AnonEnum(tys) | TyKind::Tup(tys) => { + walk_list!(visitor, visit_ty, tys); } TyKind::BareFn(function_declaration) => { walk_list!(visitor, visit_generic_param, &function_declaration.generic_params); diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs index 41d4a5679f1a0..a60d02002da33 100644 --- a/compiler/rustc_ast_lowering/src/lib.rs +++ b/compiler/rustc_ast_lowering/src/lib.rs @@ -1235,6 +1235,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { let kind = match &t.kind { TyKind::Infer => hir::TyKind::Infer, TyKind::Err => hir::TyKind::Err, + TyKind::AnonEnum(_) => hir::TyKind::Err, TyKind::Slice(ty) => hir::TyKind::Slice(self.lower_ty(ty, itctx)), TyKind::Ptr(mt) => hir::TyKind::Ptr(self.lower_mt(mt, itctx)), TyKind::Ref(region, mt) => { diff --git a/compiler/rustc_ast_pretty/src/pprust/state.rs b/compiler/rustc_ast_pretty/src/pprust/state.rs index 6a8064b0e874e..3f9b96a6158a1 100644 --- a/compiler/rustc_ast_pretty/src/pprust/state.rs +++ b/compiler/rustc_ast_pretty/src/pprust/state.rs @@ -1041,6 +1041,9 @@ impl<'a> State<'a> { } self.pclose(); } + ast::TyKind::AnonEnum(elts) => { + self.strsep("|", false, Inconsistent, elts, |s, ty| s.print_type(ty)); + } ast::TyKind::Paren(typ) => { self.popen(); self.print_type(typ); diff --git a/compiler/rustc_parse/src/parser/diagnostics.rs b/compiler/rustc_parse/src/parser/diagnostics.rs index 4c918c6702ed9..07bd76dc39ba9 100644 --- a/compiler/rustc_parse/src/parser/diagnostics.rs +++ b/compiler/rustc_parse/src/parser/diagnostics.rs @@ -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, 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,22 @@ 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); + } + } + err.emit(); } _ => { // Carry on as if we had not done anything. This should be unreachable. - self.restore_snapshot(snapshot); } }; first_pat diff --git a/compiler/rustc_parse/src/parser/pat.rs b/compiler/rustc_parse/src/parser/pat.rs index e73a17ced7deb..e5411538eea22 100644 --- a/compiler/rustc_parse/src/parser/pat.rs +++ b/compiler/rustc_parse/src/parser/pat.rs @@ -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 { diff --git a/compiler/rustc_parse/src/parser/ty.rs b/compiler/rustc_parse/src/parser/ty.rs index a6f702e542869..72994df6f9f46 100644 --- a/compiler/rustc_parse/src/parser/ty.rs +++ b/compiler/rustc_parse/src/parser/ty.rs @@ -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> { 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::>() + .join("\n"), + )); + err.emit(); + return Ok(self.mk_ty(lo.to(self.prev_token.span), TyKind::AnonEnum(types))); + } if allow_qpath_recovery { self.maybe_recover_from_bad_qpath(ty) } else { Ok(ty) } } diff --git a/compiler/rustc_passes/src/hir_stats.rs b/compiler/rustc_passes/src/hir_stats.rs index b86d2316820ce..312bd8839e0d0 100644 --- a/compiler/rustc_passes/src/hir_stats.rs +++ b/compiler/rustc_passes/src/hir_stats.rs @@ -579,6 +579,7 @@ impl<'v> ast_visit::Visitor<'v> for StatCollector<'v> { [ Slice, Array, + AnonEnum, Ptr, Ref, BareFn, diff --git a/src/tools/rustfmt/src/types.rs b/src/tools/rustfmt/src/types.rs index c1991e8d2c808..f065ec680d724 100644 --- a/src/tools/rustfmt/src/types.rs +++ b/src/tools/rustfmt/src/types.rs @@ -839,7 +839,9 @@ impl Rewrite for ast::Ty { }) } ast::TyKind::CVarArgs => Some("...".to_owned()), - ast::TyKind::Err => Some(context.snippet(self.span).to_owned()), + ast::TyKind::AnonEnum(_) | ast::TyKind::Err => { + Some(context.snippet(self.span).to_owned()) + } ast::TyKind::Typeof(ref anon_const) => rewrite_call( context, "typeof", diff --git a/tests/ui/parser/anon-enums.rs b/tests/ui/parser/anon-enums.rs new file mode 100644 index 0000000000000..a8b5e8359d996 --- /dev/null +++ b/tests/ui/parser/anon-enums.rs @@ -0,0 +1,19 @@ +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 + //~^ ERROR failed to resolve + true => 42., + false => 0.333, + } +} + +fn main() { + match foo(true) { + 42: i32 => (), //~ ERROR expected + _: f64 => (), //~ ERROR expected + x: i32 => (), //~ ERROR expected + //~^ ERROR failed to resolve + } +} diff --git a/tests/ui/parser/anon-enums.stderr b/tests/ui/parser/anon-enums.stderr new file mode 100644 index 0000000000000..4febf2df0dc72 --- /dev/null +++ b/tests/ui/parser/anon-enums.stderr @@ -0,0 +1,81 @@ +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:14:11 + | +LL | 42: i32 => (), + | ^ --- specifying the type of a pattern isn't supported + | | + | expected one of `...`, `..=`, `..`, or `|` + +error: expected `|`, found `:` + --> $DIR/anon-enums.rs:15:10 + | +LL | _: f64 => (), + | ^ --- specifying the type of a pattern isn't supported + | | + | expected `|` + +error: expected one of `@` or `|`, found `:` + --> $DIR/anon-enums.rs:16: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[E0433]: failed to resolve: use of undeclared crate or module `x` + --> $DIR/anon-enums.rs:5:9 + | +LL | x: i32 => x, + | ^ use of undeclared crate or module `x` + +error[E0433]: failed to resolve: use of undeclared crate or module `x` + --> $DIR/anon-enums.rs:16:9 + | +LL | x: i32 => (), + | ^ use of undeclared crate or module `x` + +error: aborting due to 8 previous errors + +For more information about this error, try `rustc --explain E0433`. diff --git a/tests/ui/parser/issues/issue-87086-colon-path-sep.stderr b/tests/ui/parser/issues/issue-87086-colon-path-sep.stderr index 2050a16beb349..ed05bfe8b0a32 100644 --- a/tests/ui/parser/issues/issue-87086-colon-path-sep.stderr +++ b/tests/ui/parser/issues/issue-87086-colon-path-sep.stderr @@ -2,82 +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 | 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[E0433]: failed to resolve: `Bar` is a variant, not a module --> $DIR/issue-87086-colon-path-sep.rs:68:13 From c847a01a3b1f620c4fdb98c75805033e768975d1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Tue, 17 Jan 2023 01:50:45 +0000 Subject: [PATCH 04/19] Emit fewer errors on patterns with possible type ascription --- .../rustc_parse/src/parser/diagnostics.rs | 2 ++ tests/ui/parser/anon-enums.rs | 2 -- tests/ui/parser/anon-enums.stderr | 21 ++++--------------- .../issues/issue-87086-colon-path-sep.rs | 1 - .../issues/issue-87086-colon-path-sep.stderr | 11 ++-------- 5 files changed, 8 insertions(+), 29 deletions(-) diff --git a/compiler/rustc_parse/src/parser/diagnostics.rs b/compiler/rustc_parse/src/parser/diagnostics.rs index 07bd76dc39ba9..eda7046c748e5 100644 --- a/compiler/rustc_parse/src/parser/diagnostics.rs +++ b/compiler/rustc_parse/src/parser/diagnostics.rs @@ -2500,6 +2500,8 @@ impl<'a> Parser<'a> { 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(); diff --git a/tests/ui/parser/anon-enums.rs b/tests/ui/parser/anon-enums.rs index a8b5e8359d996..56b8a3d43bedf 100644 --- a/tests/ui/parser/anon-enums.rs +++ b/tests/ui/parser/anon-enums.rs @@ -3,7 +3,6 @@ fn foo(x: bool | i32) -> i32 | f64 { //~| ERROR anonymous enums are not supported match x { x: i32 => x, //~ ERROR expected - //~^ ERROR failed to resolve true => 42., false => 0.333, } @@ -14,6 +13,5 @@ fn main() { 42: i32 => (), //~ ERROR expected _: f64 => (), //~ ERROR expected x: i32 => (), //~ ERROR expected - //~^ ERROR failed to resolve } } diff --git a/tests/ui/parser/anon-enums.stderr b/tests/ui/parser/anon-enums.stderr index 4febf2df0dc72..8415822566091 100644 --- a/tests/ui/parser/anon-enums.stderr +++ b/tests/ui/parser/anon-enums.stderr @@ -36,7 +36,7 @@ LL | x::i32 => x, | ~~ error: expected one of `...`, `..=`, `..`, or `|`, found `:` - --> $DIR/anon-enums.rs:14:11 + --> $DIR/anon-enums.rs:13:11 | LL | 42: i32 => (), | ^ --- specifying the type of a pattern isn't supported @@ -44,7 +44,7 @@ LL | 42: i32 => (), | expected one of `...`, `..=`, `..`, or `|` error: expected `|`, found `:` - --> $DIR/anon-enums.rs:15:10 + --> $DIR/anon-enums.rs:14:10 | LL | _: f64 => (), | ^ --- specifying the type of a pattern isn't supported @@ -52,7 +52,7 @@ LL | _: f64 => (), | expected `|` error: expected one of `@` or `|`, found `:` - --> $DIR/anon-enums.rs:16:10 + --> $DIR/anon-enums.rs:15:10 | LL | x: i32 => (), | ^ --- specifying the type of a pattern isn't supported @@ -64,18 +64,5 @@ help: maybe write a path separator here LL | x::i32 => (), | ~~ -error[E0433]: failed to resolve: use of undeclared crate or module `x` - --> $DIR/anon-enums.rs:5:9 - | -LL | x: i32 => x, - | ^ use of undeclared crate or module `x` - -error[E0433]: failed to resolve: use of undeclared crate or module `x` - --> $DIR/anon-enums.rs:16:9 - | -LL | x: i32 => (), - | ^ use of undeclared crate or module `x` - -error: aborting due to 8 previous errors +error: aborting due to 6 previous errors -For more information about this error, try `rustc --explain E0433`. diff --git a/tests/ui/parser/issues/issue-87086-colon-path-sep.rs b/tests/ui/parser/issues/issue-87086-colon-path-sep.rs index 0b7b67496d6f3..e1ea38f2795df 100644 --- a/tests/ui/parser/issues/issue-87086-colon-path-sep.rs +++ b/tests/ui/parser/issues/issue-87086-colon-path-sep.rs @@ -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 => {} diff --git a/tests/ui/parser/issues/issue-87086-colon-path-sep.stderr b/tests/ui/parser/issues/issue-87086-colon-path-sep.stderr index ed05bfe8b0a32..63b072ac4cdc6 100644 --- a/tests/ui/parser/issues/issue-87086-colon-path-sep.stderr +++ b/tests/ui/parser/issues/issue-87086-colon-path-sep.stderr @@ -103,7 +103,7 @@ 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 @@ -115,12 +115,5 @@ help: maybe write a path separator here LL | Foo::Bar => {} | ~~ -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 - -error: aborting due to 10 previous errors +error: aborting due to 9 previous errors -For more information about this error, try `rustc --explain E0433`. From 12d18e403139eeeeb339e8611b2bed4910864edb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Tue, 17 Jan 2023 17:02:09 +0000 Subject: [PATCH 05/19] Ensure macros are not affected --- tests/ui/parser/fake-anon-enums-in-macros.rs | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 tests/ui/parser/fake-anon-enums-in-macros.rs diff --git a/tests/ui/parser/fake-anon-enums-in-macros.rs b/tests/ui/parser/fake-anon-enums-in-macros.rs new file mode 100644 index 0000000000000..38fe8dee23829 --- /dev/null +++ b/tests/ui/parser/fake-anon-enums-in-macros.rs @@ -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); +} From 8e43414bce77cfe2030b875e365c77691897b95f Mon Sep 17 00:00:00 2001 From: mejrs <59372212+mejrs@users.noreply.github.com> Date: Thu, 19 Jan 2023 16:31:50 +0100 Subject: [PATCH 06/19] Fix proc macro tests --- tests/ui/proc-macro/allowed-signatures.rs | 2 ++ tests/ui/proc-macro/proc-macro-abi.rs | 3 +++ tests/ui/proc-macro/proc-macro-abi.stderr | 6 +++--- .../ui/proc-macro/signature-proc-macro-attribute.rs | 3 +++ .../proc-macro/signature-proc-macro-attribute.stderr | 12 ++++++------ tests/ui/proc-macro/signature-proc-macro-derive.rs | 3 +++ .../ui/proc-macro/signature-proc-macro-derive.stderr | 10 +++++----- tests/ui/proc-macro/signature-proc-macro.rs | 3 +++ tests/ui/proc-macro/signature-proc-macro.stderr | 10 +++++----- 9 files changed, 33 insertions(+), 19 deletions(-) diff --git a/tests/ui/proc-macro/allowed-signatures.rs b/tests/ui/proc-macro/allowed-signatures.rs index 03c6ef86632df..8685087611248 100644 --- a/tests/ui/proc-macro/allowed-signatures.rs +++ b/tests/ui/proc-macro/allowed-signatures.rs @@ -1,4 +1,6 @@ // check-pass +// force-host +// no-prefer-dynamic #![crate_type = "proc-macro"] #![allow(private_in_public)] diff --git a/tests/ui/proc-macro/proc-macro-abi.rs b/tests/ui/proc-macro/proc-macro-abi.rs index 8e9d50d7832d5..873660a5b3ab9 100644 --- a/tests/ui/proc-macro/proc-macro-abi.rs +++ b/tests/ui/proc-macro/proc-macro-abi.rs @@ -1,3 +1,6 @@ +// force-host +// no-prefer-dynamic + #![crate_type = "proc-macro"] #![allow(warnings)] diff --git a/tests/ui/proc-macro/proc-macro-abi.stderr b/tests/ui/proc-macro/proc-macro-abi.stderr index fa5f5dc098998..9a781be0996dd 100644 --- a/tests/ui/proc-macro/proc-macro-abi.stderr +++ b/tests/ui/proc-macro/proc-macro-abi.stderr @@ -1,17 +1,17 @@ error: proc macro functions may not be `extern "C"` - --> $DIR/proc-macro-abi.rs:8:1 + --> $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:14:1 + --> $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:20:1 + --> $DIR/proc-macro-abi.rs:23:1 | LL | pub extern fn abi3(a: TokenStream) -> TokenStream { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/proc-macro/signature-proc-macro-attribute.rs b/tests/ui/proc-macro/signature-proc-macro-attribute.rs index fb17710950106..51abc8e7d3edb 100644 --- a/tests/ui/proc-macro/signature-proc-macro-attribute.rs +++ b/tests/ui/proc-macro/signature-proc-macro-attribute.rs @@ -1,3 +1,6 @@ +// force-host +// no-prefer-dynamic + #![crate_type = "proc-macro"] extern crate proc_macro; diff --git a/tests/ui/proc-macro/signature-proc-macro-attribute.stderr b/tests/ui/proc-macro/signature-proc-macro-attribute.stderr index ecfd2b06e109c..abf7a6f3ce922 100644 --- a/tests/ui/proc-macro/signature-proc-macro-attribute.stderr +++ b/tests/ui/proc-macro/signature-proc-macro-attribute.stderr @@ -1,11 +1,11 @@ error: mismatched attribute proc macro signature - --> $DIR/signature-proc-macro-attribute.rs:7:1 + --> $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:13:42 + --> $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` @@ -13,13 +13,13 @@ LL | pub fn bad_output(input: TokenStream) -> String { = 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:13:1 + --> $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:20:41 + --> $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` @@ -27,13 +27,13 @@ LL | pub fn bad_everything(input: String) -> String { = 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:20:1 + --> $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:27:49 + --> $DIR/signature-proc-macro-attribute.rs:30:49 | LL | pub fn too_many(a: TokenStream, b: TokenStream, c: String) -> TokenStream { | ^^^^^^^^^ found unexpected argument diff --git a/tests/ui/proc-macro/signature-proc-macro-derive.rs b/tests/ui/proc-macro/signature-proc-macro-derive.rs index a079157538fb6..f2fd824b675b6 100644 --- a/tests/ui/proc-macro/signature-proc-macro-derive.rs +++ b/tests/ui/proc-macro/signature-proc-macro-derive.rs @@ -1,3 +1,6 @@ +// force-host +// no-prefer-dynamic + #![crate_type = "proc-macro"] extern crate proc_macro; diff --git a/tests/ui/proc-macro/signature-proc-macro-derive.stderr b/tests/ui/proc-macro/signature-proc-macro-derive.stderr index bb2bd9a93d24b..a358ae277037f 100644 --- a/tests/ui/proc-macro/signature-proc-macro-derive.stderr +++ b/tests/ui/proc-macro/signature-proc-macro-derive.stderr @@ -1,5 +1,5 @@ error: mismatched derive proc macro signature - --> $DIR/signature-proc-macro-derive.rs:7:25 + --> $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` @@ -7,7 +7,7 @@ LL | pub fn bad_input(input: String) -> 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:13:42 + --> $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` @@ -15,7 +15,7 @@ LL | pub fn bad_output(input: TokenStream) -> String { = note: derive proc macros must have a signature of `fn(TokenStream) -> TokenStream` error: mismatched derive proc macro signature - --> $DIR/signature-proc-macro-derive.rs:19:41 + --> $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` @@ -23,7 +23,7 @@ LL | pub fn bad_everything(input: String) -> String { = note: derive proc macros must have a signature of `fn(TokenStream) -> TokenStream` error: mismatched derive proc macro signature - --> $DIR/signature-proc-macro-derive.rs:19:30 + --> $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` @@ -31,7 +31,7 @@ LL | pub fn bad_everything(input: String) -> String { = note: derive proc macros must have a signature of `fn(TokenStream) -> TokenStream` error: mismatched derive proc macro signature - --> $DIR/signature-proc-macro-derive.rs:26:33 + --> $DIR/signature-proc-macro-derive.rs:29:33 | LL | pub fn too_many(a: TokenStream, b: TokenStream, c: String) -> TokenStream { | ^^^^^^^^^^^^^^^^^^^^^^^^^ found unexpected arguments diff --git a/tests/ui/proc-macro/signature-proc-macro.rs b/tests/ui/proc-macro/signature-proc-macro.rs index 35d5be2171283..54770aacd1a98 100644 --- a/tests/ui/proc-macro/signature-proc-macro.rs +++ b/tests/ui/proc-macro/signature-proc-macro.rs @@ -1,3 +1,6 @@ +// force-host +// no-prefer-dynamic + #![crate_type = "proc-macro"] extern crate proc_macro; diff --git a/tests/ui/proc-macro/signature-proc-macro.stderr b/tests/ui/proc-macro/signature-proc-macro.stderr index 32241e1b9e00a..4b14a54e67503 100644 --- a/tests/ui/proc-macro/signature-proc-macro.stderr +++ b/tests/ui/proc-macro/signature-proc-macro.stderr @@ -1,5 +1,5 @@ error: mismatched function-like proc macro signature - --> $DIR/signature-proc-macro.rs:7:25 + --> $DIR/signature-proc-macro.rs:10:25 | LL | pub fn bad_input(input: String) -> TokenStream { | ^^^^^^ found std::string::String, expected type `proc_macro::TokenStream` @@ -7,7 +7,7 @@ LL | pub fn bad_input(input: String) -> 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:13:42 + --> $DIR/signature-proc-macro.rs:16:42 | LL | pub fn bad_output(input: TokenStream) -> String { | ^^^^^^ found std::string::String, expected type `proc_macro::TokenStream` @@ -15,7 +15,7 @@ LL | pub fn bad_output(input: TokenStream) -> String { = 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:19:41 + --> $DIR/signature-proc-macro.rs:22:41 | LL | pub fn bad_everything(input: String) -> String { | ^^^^^^ found std::string::String, expected type `proc_macro::TokenStream` @@ -23,7 +23,7 @@ LL | pub fn bad_everything(input: String) -> String { = 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:19:30 + --> $DIR/signature-proc-macro.rs:22:30 | LL | pub fn bad_everything(input: String) -> String { | ^^^^^^ found std::string::String, expected type `proc_macro::TokenStream` @@ -31,7 +31,7 @@ LL | pub fn bad_everything(input: String) -> String { = 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:26:33 + --> $DIR/signature-proc-macro.rs:29:33 | LL | pub fn too_many(a: TokenStream, b: TokenStream, c: String) -> TokenStream { | ^^^^^^^^^^^^^^^^^^^^^^^^^ found unexpected arguments From d3cfe97a8a975511a631083d64aa6ae3ddf7470a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Mi=C4=85sko?= Date: Thu, 19 Jan 2023 00:00:00 +0000 Subject: [PATCH 07/19] Custom MIR: Support binary and unary operations --- .../src/build/custom/parse/instruction.rs | 6 ++++ .../custom/operators.f.built.after.mir | 26 +++++++++++++++++ tests/mir-opt/building/custom/operators.rs | 28 +++++++++++++++++++ 3 files changed, 60 insertions(+) create mode 100644 tests/mir-opt/building/custom/operators.f.built.after.mir create mode 100644 tests/mir-opt/building/custom/operators.rs diff --git a/compiler/rustc_mir_build/src/build/custom/parse/instruction.rs b/compiler/rustc_mir_build/src/build/custom/parse/instruction.rs index dca4906c07de5..2560eaee43372 100644 --- a/compiler/rustc_mir_build/src/build/custom/parse/instruction.rs +++ b/compiler/rustc_mir_build/src/build/custom/parse/instruction.rs @@ -141,6 +141,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), ) } diff --git a/tests/mir-opt/building/custom/operators.f.built.after.mir b/tests/mir-opt/building/custom/operators.f.built.after.mir new file mode 100644 index 0000000000000..a0c5f1b40dbb7 --- /dev/null +++ b/tests/mir-opt/building/custom/operators.f.built.after.mir @@ -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 + } +} diff --git a/tests/mir-opt/building/custom/operators.rs b/tests/mir-opt/building/custom/operators.rs new file mode 100644 index 0000000000000..51f80c66392a1 --- /dev/null +++ b/tests/mir-opt/building/custom/operators.rs @@ -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() + }) +} From 96f8f995891ad1f7d514a615d9494cf7f56ea0a3 Mon Sep 17 00:00:00 2001 From: Erik Desjardins Date: Sun, 22 Jan 2023 21:02:07 -0500 Subject: [PATCH 08/19] rustc_abi: remove Primitive::{is_float,is_int} there were fixmes for this already i am about to remove is_ptr (since callers need to properly distinguish between pointers in different address spaces), so might as well do this at the same time --- compiler/rustc_abi/src/lib.rs | 12 ------------ compiler/rustc_target/src/abi/call/sparc64.rs | 4 ++-- compiler/rustc_target/src/abi/mod.rs | 2 +- 3 files changed, 3 insertions(+), 15 deletions(-) diff --git a/compiler/rustc_abi/src/lib.rs b/compiler/rustc_abi/src/lib.rs index f4cb459f32fdd..b9ec7d2ee74bc 100644 --- a/compiler/rustc_abi/src/lib.rs +++ b/compiler/rustc_abi/src/lib.rs @@ -887,18 +887,6 @@ impl Primitive { } } - // 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) diff --git a/compiler/rustc_target/src/abi/call/sparc64.rs b/compiler/rustc_target/src/abi/call/sparc64.rs index c8b6ac5ae25b2..0c7a4f324612e 100644 --- a/compiler/rustc_target/src/abi/call/sparc64.rs +++ b/compiler/rustc_target/src/abi/call/sparc64.rs @@ -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; } @@ -87,7 +87,7 @@ where _ => {} } - 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); diff --git a/compiler/rustc_target/src/abi/mod.rs b/compiler/rustc_target/src/abi/mod.rs index 88a0a1f8ecfde..39761baf1bc29 100644 --- a/compiler/rustc_target/src/abi/mod.rs +++ b/compiler/rustc_target/src/abi/mod.rs @@ -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) From 009192b01bd88a7bb6c1948d1f47dd598af0bfd9 Mon Sep 17 00:00:00 2001 From: Erik Desjardins Date: Sun, 22 Jan 2023 23:03:58 -0500 Subject: [PATCH 09/19] abi: add `AddressSpace` field to `Primitive::Pointer` ...and remove it from `PointeeInfo`, which isn't meant for this. There are still various places (marked with FIXMEs) that assume all pointers have the same size and alignment. Fixing this requires parsing non-default address spaces in the data layout string, which will be done in a followup. --- compiler/rustc_abi/src/lib.rs | 24 +- .../rustc_codegen_cranelift/src/common.rs | 3 +- compiler/rustc_codegen_gcc/src/builder.rs | 2 +- compiler/rustc_codegen_gcc/src/common.rs | 2 +- compiler/rustc_codegen_gcc/src/consts.rs | 16 +- compiler/rustc_codegen_gcc/src/type_of.rs | 4 +- compiler/rustc_codegen_llvm/src/asm.rs | 13 +- compiler/rustc_codegen_llvm/src/builder.rs | 2 +- compiler/rustc_codegen_llvm/src/common.rs | 4 +- compiler/rustc_codegen_llvm/src/consts.rs | 2 +- .../src/debuginfo/metadata/enums/mod.rs | 3 +- compiler/rustc_codegen_llvm/src/intrinsic.rs | 2 +- compiler/rustc_codegen_llvm/src/type_of.rs | 15 +- compiler/rustc_codegen_ssa/src/mir/block.rs | 4 +- compiler/rustc_codegen_ssa/src/mir/place.rs | 17 +- .../rustc_const_eval/src/interpret/operand.rs | 6 +- compiler/rustc_hir_typeck/src/intrinsicck.rs | 3 +- compiler/rustc_middle/src/ty/layout.rs | 222 +++++++++--------- .../rustc_target/src/abi/call/loongarch.rs | 2 +- compiler/rustc_target/src/abi/call/mod.rs | 2 +- compiler/rustc_target/src/abi/call/riscv.rs | 2 +- compiler/rustc_target/src/abi/call/sparc64.rs | 2 +- compiler/rustc_target/src/abi/call/x86_64.rs | 2 +- compiler/rustc_ty_utils/src/abi.rs | 4 +- compiler/rustc_ty_utils/src/layout.rs | 8 +- tests/codegen/avr/avr-func-addrspace.rs | 25 ++ 26 files changed, 218 insertions(+), 173 deletions(-) diff --git a/compiler/rustc_abi/src/lib.rs b/compiler/rustc_abi/src/lib.rs index b9ec7d2ee74bc..fe65ad9c6cb0e 100644 --- a/compiler/rustc_abi/src/lib.rs +++ b/compiler/rustc_abi/src/lib.rs @@ -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,14 +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, } } - - #[inline] - pub fn is_ptr(self) -> bool { - matches!(self, Pointer) - } } /// Inclusive wrap-around range of valid values, that is, if @@ -1176,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 { @@ -1456,7 +1461,6 @@ pub struct PointeeInfo { pub size: Size, pub align: Align, pub safe: Option, - pub address_space: AddressSpace, } /// Used in `might_permit_raw_init` to indicate the kind of initialisation diff --git a/compiler/rustc_codegen_cranelift/src/common.rs b/compiler/rustc_codegen_cranelift/src/common.rs index 2dcd42fbd8f43..63ed10cdfcc59 100644 --- a/compiler/rustc_codegen_cranelift/src/common.rs +++ b/compiler/rustc_codegen_cranelift/src/common.rs @@ -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), } } diff --git a/compiler/rustc_codegen_gcc/src/builder.rs b/compiler/rustc_codegen_gcc/src/builder.rs index a92242b2615c1..e88c12716ecd3 100644 --- a/compiler/rustc_codegen_gcc/src/builder.rs +++ b/compiler/rustc_codegen_gcc/src/builder.rs @@ -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); } _ => {} diff --git a/compiler/rustc_codegen_gcc/src/common.rs b/compiler/rustc_codegen_gcc/src/common.rs index 0afc56b4494d3..c939da9cec3c2 100644 --- a/compiler/rustc_codegen_gcc/src/common.rs +++ b/compiler/rustc_codegen_gcc/src/common.rs @@ -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 { diff --git a/compiler/rustc_codegen_gcc/src/consts.rs b/compiler/rustc_codegen_gcc/src/consts.rs index ea8ab76114604..ba64b1f638942 100644 --- a/compiler/rustc_codegen_gcc/src/consts.rs +++ b/compiler/rustc_codegen_gcc/src/consts.rs @@ -7,9 +7,9 @@ use rustc_middle::middle::codegen_fn_attrs::{CodegenFnAttrFlags, CodegenFnAttrs} use rustc_middle::mir::mono::MonoItem; use rustc_middle::ty::{self, Instance, Ty}; use rustc_middle::ty::layout::LayoutOf; -use rustc_middle::mir::interpret::{self, ConstAllocation, ErrorHandled, Scalar as InterpScalar, read_target_uint}; +use rustc_middle::mir::interpret::{self, ConstAllocation, ErrorHandled, GlobalAlloc, Scalar as InterpScalar, read_target_uint}; use rustc_span::def_id::DefId; -use rustc_target::abi::{self, Align, HasDataLayout, Primitive, Size, WrappingRange}; +use rustc_target::abi::{self, AddressSpace, Align, HasDataLayout, Primitive, Size, WrappingRange}; use crate::base; use crate::context::CodegenCx; @@ -322,13 +322,21 @@ 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 = match cx.tcx.global_alloc(alloc_id) { + GlobalAlloc::Function(..) => cx.data_layout().instruction_address_space, + GlobalAlloc::Static(..) | GlobalAlloc::Memory(..) | GlobalAlloc::VTable(..) => { + AddressSpace::DATA + } + }; + 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; } diff --git a/compiler/rustc_codegen_gcc/src/type_of.rs b/compiler/rustc_codegen_gcc/src/type_of.rs index 524d10fb5e24d..1326af670cde4 100644 --- a/compiler/rustc_codegen_gcc/src/type_of.rs +++ b/compiler/rustc_codegen_gcc/src/type_of.rs @@ -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) } } } diff --git a/compiler/rustc_codegen_llvm/src/asm.rs b/compiler/rustc_codegen_llvm/src/asm.rs index 52c8b51796c0b..d9f8170a3cffa 100644 --- a/compiler/rustc_codegen_llvm/src/asm.rs +++ b/compiler/rustc_codegen_llvm/src/asm.rs @@ -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 diff --git a/compiler/rustc_codegen_llvm/src/builder.rs b/compiler/rustc_codegen_llvm/src/builder.rs index 5e98deae48aa2..0f33b98548984 100644 --- a/compiler/rustc_codegen_llvm/src/builder.rs +++ b/compiler/rustc_codegen_llvm/src/builder.rs @@ -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); } diff --git a/compiler/rustc_codegen_llvm/src/common.rs b/compiler/rustc_codegen_llvm/src/common.rs index acee9134fb96e..edb1c160626ea 100644 --- a/compiler/rustc_codegen_llvm/src/common.rs +++ b/compiler/rustc_codegen_llvm/src/common.rs @@ -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) diff --git a/compiler/rustc_codegen_llvm/src/consts.rs b/compiler/rustc_codegen_llvm/src/consts.rs index 16467b614feaf..2f630b32ffe06 100644 --- a/compiler/rustc_codegen_llvm/src/consts.rs +++ b/compiler/rustc_codegen_llvm/src/consts.rs @@ -111,7 +111,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), diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/mod.rs b/compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/mod.rs index 564ab351bd41f..54e850f25996b 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/mod.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/mod.rs @@ -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`. diff --git a/compiler/rustc_codegen_llvm/src/intrinsic.rs b/compiler/rustc_codegen_llvm/src/intrinsic.rs index a6a75eff9a36d..dd89c4c59c14d 100644 --- a/compiler/rustc_codegen_llvm/src/intrinsic.rs +++ b/compiler/rustc_codegen_llvm/src/intrinsic.rs @@ -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. diff --git a/compiler/rustc_codegen_llvm/src/type_of.rs b/compiler/rustc_codegen_llvm/src/type_of.rs index 75cd5df972316..c73d233b767a4 100644 --- a/compiler/rustc_codegen_llvm/src/type_of.rs +++ b/compiler/rustc_codegen_llvm/src/type_of.rs @@ -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) } } diff --git a/compiler/rustc_codegen_ssa/src/mir/block.rs b/compiler/rustc_codegen_ssa/src/mir/block.rs index 978aff511bfa7..e2106f8b5c570 100644 --- a/compiler/rustc_codegen_ssa/src/mir/block.rs +++ b/compiler/rustc_codegen_ssa/src/mir/block.rs @@ -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); diff --git a/compiler/rustc_codegen_ssa/src/mir/place.rs b/compiler/rustc_codegen_ssa/src/mir/place.rs index fbe30154a7c8d..cf02f59f67b97 100644 --- a/compiler/rustc_codegen_ssa/src/mir/place.rs +++ b/compiler/rustc_codegen_ssa/src/mir/place.rs @@ -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()); diff --git a/compiler/rustc_const_eval/src/interpret/operand.rs b/compiler/rustc_const_eval/src/interpret/operand.rs index befc0928f3deb..a1b3985dce4e6 100644 --- a/compiler/rustc_const_eval/src/interpret/operand.rs +++ b/compiler/rustc_const_eval/src/interpret/operand.rs @@ -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 }) } diff --git a/compiler/rustc_hir_typeck/src/intrinsicck.rs b/compiler/rustc_hir_typeck/src/intrinsicck.rs index 3c873024c924f..2c76582f2ec8f 100644 --- a/compiler/rustc_hir_typeck/src/intrinsicck.rs +++ b/compiler/rustc_hir_typeck/src/intrinsicck.rs @@ -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` 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}")) diff --git a/compiler/rustc_middle/src/ty/layout.rs b/compiler/rustc_middle/src/ty/layout.rs index dfd016569c27a..66b9d96e69577 100644 --- a/compiler/rustc_middle/src/ty/layout.rs +++ b/compiler/rustc_middle/src/ty/layout.rs @@ -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 + 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, + }) + } + 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 { + 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 + 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, - }) - } + 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), - }; + _ => { + 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; + 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 { - 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 - } else { - 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`, not `Box`. - 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`, not `Box`. + 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: {:?}) => {:?}", diff --git a/compiler/rustc_target/src/abi/call/loongarch.rs b/compiler/rustc_target/src/abi/call/loongarch.rs index 4a2d39cc70023..247256f076ba9 100644 --- a/compiler/rustc_target/src/abi/call/loongarch.rs +++ b/compiler/rustc_target/src/abi/call/loongarch.rs @@ -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); } diff --git a/compiler/rustc_target/src/abi/call/mod.rs b/compiler/rustc_target/src/abi/call/mod.rs index 3b8c867d35ba3..a0730fbb650dc 100644 --- a/compiler/rustc_target/src/abi/call/mod.rs +++ b/compiler/rustc_target/src/abi/call/mod.rs @@ -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 })) diff --git a/compiler/rustc_target/src/abi/call/riscv.rs b/compiler/rustc_target/src/abi/call/riscv.rs index 34280d38e3406..d90dce2a08785 100644 --- a/compiler/rustc_target/src/abi/call/riscv.rs +++ b/compiler/rustc_target/src/abi/call/riscv.rs @@ -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); } diff --git a/compiler/rustc_target/src/abi/call/sparc64.rs b/compiler/rustc_target/src/abi/call/sparc64.rs index 0c7a4f324612e..cbed5b4afc134 100644 --- a/compiler/rustc_target/src/abi/call/sparc64.rs +++ b/compiler/rustc_target/src/abi/call/sparc64.rs @@ -83,7 +83,7 @@ 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, _ => {} } diff --git a/compiler/rustc_target/src/abi/call/x86_64.rs b/compiler/rustc_target/src/abi/call/x86_64.rs index c0c071a614f50..9427f27d1b7bb 100644 --- a/compiler/rustc_target/src/abi/call/x86_64.rs +++ b/compiler/rustc_target/src/abi/call/x86_64.rs @@ -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, }, diff --git a/compiler/rustc_ty_utils/src/abi.rs b/compiler/rustc_ty_utils/src/abi.rs index 91a505a72fae7..e47e68e0670b9 100644 --- a/compiler/rustc_ty_utils/src/abi.rs +++ b/compiler/rustc_ty_utils/src/abi.rs @@ -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 diff --git a/compiler/rustc_ty_utils/src/layout.rs b/compiler/rustc_ty_utils/src/layout.rs index 6aa016133ca59..0f25579c7bfa1 100644 --- a/compiler/rustc_ty_utils/src/layout.rs +++ b/compiler/rustc_ty_utils/src/layout.rs @@ -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)) } diff --git a/tests/codegen/avr/avr-func-addrspace.rs b/tests/codegen/avr/avr-func-addrspace.rs index e9740e30da483..bc11e1081244a 100644 --- a/tests/codegen/avr/avr-func-addrspace.rs +++ b/tests/codegen/avr/avr-func-addrspace.rs @@ -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 { 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)) +} From bed3bb53d207c1bf92c26833e8d3d4280550f83e Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Sat, 21 Jan 2023 21:54:49 +0000 Subject: [PATCH 10/19] Don't resolve type var roots in point_at_expr_source_of_inferred_type --- compiler/rustc_hir_typeck/src/demand.rs | 4 +++- tests/ui/typeck/bad-type-in-vec-push.rs | 14 ++++++++++++++ tests/ui/typeck/bad-type-in-vec-push.stderr | 20 ++++++++++++++++++++ 3 files changed, 37 insertions(+), 1 deletion(-) create mode 100644 tests/ui/typeck/bad-type-in-vec-push.rs create mode 100644 tests/ui/typeck/bad-type-in-vec-push.stderr diff --git a/compiler/rustc_hir_typeck/src/demand.rs b/compiler/rustc_hir_typeck/src/demand.rs index bd1626dff7951..d8cca55a8f892 100644 --- a/compiler/rustc_hir_typeck/src/demand.rs +++ b/compiler/rustc_hir_typeck/src/demand.rs @@ -270,7 +270,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 })) } @@ -333,6 +333,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // inferred in this method call. let arg = &args[i]; let arg_ty = self.node_ty(arg.hir_id); + if !arg.span.overlaps(mismatch_span) { err.span_label( arg.span, &format!( @@ -340,6 +341,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { inferred as `{ty}`", ), ); + } param_args.insert(param_ty, (arg, arg_ty)); } } diff --git a/tests/ui/typeck/bad-type-in-vec-push.rs b/tests/ui/typeck/bad-type-in-vec-push.rs new file mode 100644 index 0000000000000..397e07dc17b42 --- /dev/null +++ b/tests/ui/typeck/bad-type-in-vec-push.rs @@ -0,0 +1,14 @@ +// 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. +} diff --git a/tests/ui/typeck/bad-type-in-vec-push.stderr b/tests/ui/typeck/bad-type-in-vec-push.stderr new file mode 100644 index 0000000000000..a24c49f0e9a3c --- /dev/null +++ b/tests/ui/typeck/bad-type-in-vec-push.stderr @@ -0,0 +1,20 @@ +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` + | | this is of type `Vec<_>`, which causes `result` to be inferred as `Vec<{integer}>` + | 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: aborting due to previous error + +For more information about this error, try `rustc --explain E0308`. From 9f933b5642c08e4241cbfed0f15270df552ce8e6 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Sat, 21 Jan 2023 22:18:51 +0000 Subject: [PATCH 11/19] Hack to suppress bad labels in type mismatch inference deduction code --- compiler/rustc_hir_typeck/src/demand.rs | 36 ++++++++++--------- .../rustc_hir_typeck/src/fn_ctxt/checks.rs | 8 ++++- tests/ui/typeck/bad-type-in-vec-push.rs | 6 ++++ tests/ui/typeck/bad-type-in-vec-push.stderr | 19 +++++++--- 4 files changed, 47 insertions(+), 22 deletions(-) diff --git a/compiler/rustc_hir_typeck/src/demand.rs b/compiler/rustc_hir_typeck/src/demand.rs index d8cca55a8f892..8e23ded3c09b4 100644 --- a/compiler/rustc_hir_typeck/src/demand.rs +++ b/compiler/rustc_hir_typeck/src/demand.rs @@ -61,7 +61,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { || self.suggest_into(err, expr, expr_ty, expected) || self.suggest_floating_point_literal(err, expr, expected); 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(); @@ -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 = None; for binding in expr_finder.uses { // In every expression where the binding is referenced, we will look at that @@ -334,13 +335,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let arg = &args[i]; let arg_ty = self.node_ty(arg.hir_id); 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}`", - ), - ); + 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)); } @@ -384,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; @@ -408,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; diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs index c9609e6943981..677c80297b912 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs @@ -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( diff --git a/tests/ui/typeck/bad-type-in-vec-push.rs b/tests/ui/typeck/bad-type-in-vec-push.rs index 397e07dc17b42..a807f030cfce4 100644 --- a/tests/ui/typeck/bad-type-in-vec-push.rs +++ b/tests/ui/typeck/bad-type-in-vec-push.rs @@ -12,3 +12,9 @@ fn main() { //~^ 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 +} diff --git a/tests/ui/typeck/bad-type-in-vec-push.stderr b/tests/ui/typeck/bad-type-in-vec-push.stderr index a24c49f0e9a3c..e4c99ec8e701f 100644 --- a/tests/ui/typeck/bad-type-in-vec-push.stderr +++ b/tests/ui/typeck/bad-type-in-vec-push.stderr @@ -4,10 +4,8 @@ error[E0308]: mismatched types LL | vector.sort(); | ------ here the type of `vector` is inferred to be `Vec<_>` LL | result.push(vector); - | ---- ^^^^^^ - | | | - | | expected integer, found struct `Vec` - | | this is of type `Vec<_>`, which causes `result` to be inferred as `Vec<{integer}>` + | ---- ^^^^^^ expected integer, found struct `Vec` + | | | arguments to this method are incorrect | = note: expected type `{integer}` @@ -15,6 +13,17 @@ LL | result.push(vector); note: associated function defined here --> $SRC_DIR/alloc/src/vec/mod.rs:LL:COL -error: aborting due to previous error +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`. From 020cca8d36cb678e3ddc2ead41364be314d19e93 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Mon, 23 Jan 2023 14:29:53 +0000 Subject: [PATCH 12/19] review comment: Remove AST AnonTy --- compiler/rustc_ast/src/ast.rs | 3 --- compiler/rustc_ast/src/mut_visit.rs | 2 +- compiler/rustc_ast/src/visit.rs | 2 +- compiler/rustc_ast_lowering/src/lib.rs | 1 - compiler/rustc_ast_pretty/src/pprust/state.rs | 3 --- compiler/rustc_parse/src/parser/ty.rs | 2 +- compiler/rustc_passes/src/hir_stats.rs | 1 - src/tools/rustfmt/src/types.rs | 4 +--- 8 files changed, 4 insertions(+), 14 deletions(-) diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs index 3aad51325dcd6..7de594719ddc4 100644 --- a/compiler/rustc_ast/src/ast.rs +++ b/compiler/rustc_ast/src/ast.rs @@ -2096,9 +2096,6 @@ pub enum TyKind { Err, /// Placeholder for a `va_list`. CVarArgs, - /// Placeholder for "anonymous enums", which don't exist, but keeping their - /// information around lets us produce better diagnostics. - AnonEnum(Vec>), } impl TyKind { diff --git a/compiler/rustc_ast/src/mut_visit.rs b/compiler/rustc_ast/src/mut_visit.rs index 894885cf0fea7..77f342d1eb322 100644 --- a/compiler/rustc_ast/src/mut_visit.rs +++ b/compiler/rustc_ast/src/mut_visit.rs @@ -470,7 +470,7 @@ pub fn noop_visit_ty(ty: &mut P, vis: &mut T) { vis.visit_fn_decl(decl); vis.visit_span(decl_span); } - TyKind::AnonEnum(tys) | TyKind::Tup(tys) => visit_vec(tys, |ty| vis.visit_ty(ty)), + TyKind::Tup(tys) => visit_vec(tys, |ty| vis.visit_ty(ty)), TyKind::Paren(ty) => vis.visit_ty(ty), TyKind::Path(qself, path) => { vis.visit_qself(qself); diff --git a/compiler/rustc_ast/src/visit.rs b/compiler/rustc_ast/src/visit.rs index 1ab70f0309c01..feb5187536ffa 100644 --- a/compiler/rustc_ast/src/visit.rs +++ b/compiler/rustc_ast/src/visit.rs @@ -400,7 +400,7 @@ 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::AnonEnum(tys) | TyKind::Tup(tys) => { + TyKind::Tup(tys) => { walk_list!(visitor, visit_ty, tys); } TyKind::BareFn(function_declaration) => { diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs index a60d02002da33..41d4a5679f1a0 100644 --- a/compiler/rustc_ast_lowering/src/lib.rs +++ b/compiler/rustc_ast_lowering/src/lib.rs @@ -1235,7 +1235,6 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { let kind = match &t.kind { TyKind::Infer => hir::TyKind::Infer, TyKind::Err => hir::TyKind::Err, - TyKind::AnonEnum(_) => hir::TyKind::Err, TyKind::Slice(ty) => hir::TyKind::Slice(self.lower_ty(ty, itctx)), TyKind::Ptr(mt) => hir::TyKind::Ptr(self.lower_mt(mt, itctx)), TyKind::Ref(region, mt) => { diff --git a/compiler/rustc_ast_pretty/src/pprust/state.rs b/compiler/rustc_ast_pretty/src/pprust/state.rs index 3f9b96a6158a1..6a8064b0e874e 100644 --- a/compiler/rustc_ast_pretty/src/pprust/state.rs +++ b/compiler/rustc_ast_pretty/src/pprust/state.rs @@ -1041,9 +1041,6 @@ impl<'a> State<'a> { } self.pclose(); } - ast::TyKind::AnonEnum(elts) => { - self.strsep("|", false, Inconsistent, elts, |s, ty| s.print_type(ty)); - } ast::TyKind::Paren(typ) => { self.popen(); self.print_type(typ); diff --git a/compiler/rustc_parse/src/parser/ty.rs b/compiler/rustc_parse/src/parser/ty.rs index 72994df6f9f46..43e6eac438ba5 100644 --- a/compiler/rustc_parse/src/parser/ty.rs +++ b/compiler/rustc_parse/src/parser/ty.rs @@ -390,7 +390,7 @@ impl<'a> Parser<'a> { .join("\n"), )); err.emit(); - return Ok(self.mk_ty(lo.to(self.prev_token.span), TyKind::AnonEnum(types))); + 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) } } diff --git a/compiler/rustc_passes/src/hir_stats.rs b/compiler/rustc_passes/src/hir_stats.rs index 312bd8839e0d0..b86d2316820ce 100644 --- a/compiler/rustc_passes/src/hir_stats.rs +++ b/compiler/rustc_passes/src/hir_stats.rs @@ -579,7 +579,6 @@ impl<'v> ast_visit::Visitor<'v> for StatCollector<'v> { [ Slice, Array, - AnonEnum, Ptr, Ref, BareFn, diff --git a/src/tools/rustfmt/src/types.rs b/src/tools/rustfmt/src/types.rs index f065ec680d724..c1991e8d2c808 100644 --- a/src/tools/rustfmt/src/types.rs +++ b/src/tools/rustfmt/src/types.rs @@ -839,9 +839,7 @@ impl Rewrite for ast::Ty { }) } ast::TyKind::CVarArgs => Some("...".to_owned()), - ast::TyKind::AnonEnum(_) | ast::TyKind::Err => { - Some(context.snippet(self.span).to_owned()) - } + ast::TyKind::Err => Some(context.snippet(self.span).to_owned()), ast::TyKind::Typeof(ref anon_const) => rewrite_call( context, "typeof", From 72117ab2c7c32805e963bbf5f0d1bdc7cd72274c Mon Sep 17 00:00:00 2001 From: clubby789 Date: Thu, 19 Jan 2023 21:11:17 +0000 Subject: [PATCH 13/19] Print PID holding bootstrap build lock on Linux --- src/bootstrap/bin/main.rs | 36 ++++++++++++++++++++++++++++++++++-- 1 file changed, 34 insertions(+), 2 deletions(-) diff --git a/src/bootstrap/bin/main.rs b/src/bootstrap/bin/main.rs index be69f819c6428..3856bb64fb310 100644 --- a/src/bootstrap/bin/main.rs +++ b/src/bootstrap/bin/main.rs @@ -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 { 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 { + 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::>(); + let (pid, inode) = (parts[4].parse::().ok()?, &parts[5]); + let inode = inode.rsplit_once(':')?.1.parse::().ok()?; + if inode == lock_inode { Some(pid) } else { None } + }) +} + +#[cfg(not(target_os = "linux"))] +fn get_lock_owner(_: &std::path::Path) -> Option { + // FIXME: Implement on other OS's + None +} From adc18904483fbd9df409291da4c4e299c8b6cb3e Mon Sep 17 00:00:00 2001 From: Erik Desjardins Date: Wed, 25 Jan 2023 01:46:19 -0500 Subject: [PATCH 14/19] create and use GlobalAlloc::address_space --- compiler/rustc_codegen_gcc/src/consts.rs | 11 +++-------- compiler/rustc_codegen_llvm/src/consts.rs | 13 +++---------- compiler/rustc_middle/src/mir/interpret/mod.rs | 13 ++++++++++++- 3 files changed, 18 insertions(+), 19 deletions(-) diff --git a/compiler/rustc_codegen_gcc/src/consts.rs b/compiler/rustc_codegen_gcc/src/consts.rs index ba64b1f638942..dc41cb761b59c 100644 --- a/compiler/rustc_codegen_gcc/src/consts.rs +++ b/compiler/rustc_codegen_gcc/src/consts.rs @@ -7,9 +7,9 @@ use rustc_middle::middle::codegen_fn_attrs::{CodegenFnAttrFlags, CodegenFnAttrs} use rustc_middle::mir::mono::MonoItem; use rustc_middle::ty::{self, Instance, Ty}; use rustc_middle::ty::layout::LayoutOf; -use rustc_middle::mir::interpret::{self, ConstAllocation, ErrorHandled, GlobalAlloc, Scalar as InterpScalar, read_target_uint}; +use rustc_middle::mir::interpret::{self, ConstAllocation, ErrorHandled, Scalar as InterpScalar, read_target_uint}; use rustc_span::def_id::DefId; -use rustc_target::abi::{self, AddressSpace, Align, HasDataLayout, Primitive, Size, WrappingRange}; +use rustc_target::abi::{self, Align, HasDataLayout, Primitive, Size, WrappingRange}; use crate::base; use crate::context::CodegenCx; @@ -323,12 +323,7 @@ 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 = 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( diff --git a/compiler/rustc_codegen_llvm/src/consts.rs b/compiler/rustc_codegen_llvm/src/consts.rs index 2f630b32ffe06..cad3c5d87b73c 100644 --- a/compiler/rustc_codegen_llvm/src/consts.rs +++ b/compiler/rustc_codegen_llvm/src/consts.rs @@ -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( diff --git a/compiler/rustc_middle/src/mir/interpret/mod.rs b/compiler/rustc_middle/src/mir/interpret/mod.rs index 5f425a287687e..b0975616b6151 100644 --- a/compiler/rustc_middle/src/mir/interpret/mod.rs +++ b/compiler/rustc_middle/src/mir/interpret/mod.rs @@ -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> { From 8b12d5f42f98f50e5e47156eea343ea6d32b10db Mon Sep 17 00:00:00 2001 From: Andy Russell Date: Sun, 22 Jan 2023 12:06:23 -0500 Subject: [PATCH 15/19] suggest qualifying bare associated constants --- compiler/rustc_resolve/src/late/diagnostics.rs | 17 ++++++++++++----- .../ui/suggestions/assoc-const-without-self.rs | 11 +++++++++++ .../suggestions/assoc-const-without-self.stderr | 14 ++++++++++++++ 3 files changed, 37 insertions(+), 5 deletions(-) create mode 100644 tests/ui/suggestions/assoc-const-without-self.rs create mode 100644 tests/ui/suggestions/assoc-const-without-self.stderr diff --git a/compiler/rustc_resolve/src/late/diagnostics.rs b/compiler/rustc_resolve/src/late/diagnostics.rs index 6d448433ee6db..37beff37c1fb9 100644 --- a/compiler/rustc_resolve/src/late/diagnostics.rs +++ b/compiler/rustc_resolve/src/late/diagnostics.rs @@ -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 { diff --git a/tests/ui/suggestions/assoc-const-without-self.rs b/tests/ui/suggestions/assoc-const-without-self.rs new file mode 100644 index 0000000000000..95070ec601cd4 --- /dev/null +++ b/tests/ui/suggestions/assoc-const-without-self.rs @@ -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() {} diff --git a/tests/ui/suggestions/assoc-const-without-self.stderr b/tests/ui/suggestions/assoc-const-without-self.stderr new file mode 100644 index 0000000000000..88d72da70cb9b --- /dev/null +++ b/tests/ui/suggestions/assoc-const-without-self.stderr @@ -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`. From b3f0085376fbd0d1d798eefac1fa2bfdc0f3cdbe Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Tue, 24 Jan 2023 18:48:15 +0000 Subject: [PATCH 16/19] Implement ObjectSafe and WF in the new solver --- .../rustc_trait_selection/src/solve/mod.rs | 41 +++++++++++++++++-- 1 file changed, 37 insertions(+), 4 deletions(-) diff --git a/compiler/rustc_trait_selection/src/solve/mod.rs b/compiler/rustc_trait_selection/src/solve/mod.rs index 70f094014453e..a920f04621db5 100644 --- a/compiler/rustc_trait_selection/src/solve/mod.rs +++ b/compiler/rustc_trait_selection/src/solve/mod.rs @@ -20,6 +20,7 @@ use std::mem; use rustc_hir::def_id::DefId; +use rustc_hir::CRATE_HIR_ID; use rustc_infer::infer::canonical::{Canonical, CanonicalVarKind, CanonicalVarValues}; use rustc_infer::infer::canonical::{OriginalQueryValues, QueryRegionConstraints, QueryResponse}; use rustc_infer::infer::{InferCtxt, InferOk, TyCtxtInferExt}; @@ -277,12 +278,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 +366,35 @@ 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::obligations( + self.infcx, + goal.param_env, + CRATE_HIR_ID, // FIXME body id + 0, + goal.predicate, + DUMMY_SP, + ) { + 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> { From 02b80d2f9c2034b6dcaa1f83bf8c6e1eca4d8388 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Tue, 24 Jan 2023 19:10:56 +0000 Subject: [PATCH 17/19] Don't normalize obligations in WF goal for the new solver --- Cargo.lock | 14 ++++---- .../rustc_trait_selection/src/solve/mod.rs | 6 +--- .../rustc_trait_selection/src/traits/wf.rs | 32 +++++++++++++++++-- 3 files changed, 38 insertions(+), 14 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 3175e98e81e53..6298d1e366bc6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -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", ] diff --git a/compiler/rustc_trait_selection/src/solve/mod.rs b/compiler/rustc_trait_selection/src/solve/mod.rs index a920f04621db5..f44648c95d742 100644 --- a/compiler/rustc_trait_selection/src/solve/mod.rs +++ b/compiler/rustc_trait_selection/src/solve/mod.rs @@ -20,7 +20,6 @@ use std::mem; use rustc_hir::def_id::DefId; -use rustc_hir::CRATE_HIR_ID; use rustc_infer::infer::canonical::{Canonical, CanonicalVarKind, CanonicalVarValues}; use rustc_infer::infer::canonical::{OriginalQueryValues, QueryRegionConstraints, QueryResponse}; use rustc_infer::infer::{InferCtxt, InferOk, TyCtxtInferExt}; @@ -380,13 +379,10 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> { goal: Goal<'tcx, ty::GenericArg<'tcx>>, ) -> QueryResult<'tcx> { self.infcx.probe(|_| { - match crate::traits::wf::obligations( + match crate::traits::wf::unnormalized_obligations( self.infcx, goal.param_env, - CRATE_HIR_ID, // FIXME body id - 0, goal.predicate, - DUMMY_SP, ) { Some(obligations) => self.evaluate_all_and_make_canonical_response( obligations.into_iter().map(|o| o.into()).collect(), diff --git a/compiler/rustc_trait_selection/src/traits/wf.rs b/compiler/rustc_trait_selection/src/traits/wf.rs index d9556848099f1..767e31ddf781a 100644 --- a/compiler/rustc_trait_selection/src/traits/wf.rs +++ b/compiler/rustc_trait_selection/src/traits/wf.rs @@ -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>> { + 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`, then the trait reference `Foo: Set` is WF From a274046b957e655ac6cc91e1de20e0df57bb106d Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Tue, 24 Jan 2023 23:57:26 +0000 Subject: [PATCH 18/19] Report the right fulfillment errors --- .../src/solve/fulfill.rs | 67 ++++++++++++++++--- 1 file changed, 57 insertions(+), 10 deletions(-) diff --git a/compiler/rustc_trait_selection/src/solve/fulfill.rs b/compiler/rustc_trait_selection/src/solve/fulfill.rs index d59fa71406c31..278024b22760a 100644 --- a/compiler/rustc_trait_selection/src/solve/fulfill.rs +++ b/compiler/rustc_trait_selection/src/solve/fulfill.rs @@ -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; From b5f893bda89aad4e1a78d50c691ee8661aa26971 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Tue, 24 Jan 2023 23:38:20 +0000 Subject: [PATCH 19/19] Implement Generator and Future --- .../src/solve/assembly.rs | 14 ++++ .../src/solve/project_goals.rs | 69 ++++++++++++++++++- .../src/solve/trait_goals.rs | 44 ++++++++++++ .../solve/trait_goals/structural_traits.rs | 1 + tests/ui/traits/new-solver/async.fail.stderr | 17 +++++ tests/ui/traits/new-solver/async.rs | 19 +++++ .../traits/new-solver/generator.fail.stderr | 64 +++++++++++++++++ tests/ui/traits/new-solver/generator.rs | 32 +++++++++ 8 files changed, 259 insertions(+), 1 deletion(-) create mode 100644 tests/ui/traits/new-solver/async.fail.stderr create mode 100644 tests/ui/traits/new-solver/async.rs create mode 100644 tests/ui/traits/new-solver/generator.fail.stderr create mode 100644 tests/ui/traits/new-solver/generator.rs diff --git a/compiler/rustc_trait_selection/src/solve/assembly.rs b/compiler/rustc_trait_selection/src/solve/assembly.rs index cdb72d49834f0..6d5d7ef28d270 100644 --- a/compiler/rustc_trait_selection/src/solve/assembly.rs +++ b/compiler/rustc_trait_selection/src/solve/assembly.rs @@ -133,6 +133,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> { @@ -259,6 +269,10 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { G::consider_builtin_fn_trait_candidates(self, goal, kind) } else if lang_items.tuple_trait() == Some(trait_def_id) { G::consider_builtin_tuple_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) }; diff --git a/compiler/rustc_trait_selection/src/solve/project_goals.rs b/compiler/rustc_trait_selection/src/solve/project_goals.rs index 32e15f03998b3..fd37dbdab74e3 100644 --- a/compiler/rustc_trait_selection/src/solve/project_goals.rs +++ b/compiler/rustc_trait_selection/src/solve/project_goals.rs @@ -15,7 +15,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; @@ -391,6 +391,73 @@ impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> { ) -> QueryResult<'tcx> { bug!("`Tuple` does not have an associated type: {:?}", goal); } + + 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. diff --git a/compiler/rustc_trait_selection/src/solve/trait_goals.rs b/compiler/rustc_trait_selection/src/solve/trait_goals.rs index 4b6d673c999c9..38606d210b401 100644 --- a/compiler/rustc_trait_selection/src/solve/trait_goals.rs +++ b/compiler/rustc_trait_selection/src/solve/trait_goals.rs @@ -185,6 +185,50 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { Err(NoSolution) } } + + 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> { diff --git a/compiler/rustc_trait_selection/src/solve/trait_goals/structural_traits.rs b/compiler/rustc_trait_selection/src/solve/trait_goals/structural_traits.rs index a11cd13cb0856..162953180c75c 100644 --- a/compiler/rustc_trait_selection/src/solve/trait_goals/structural_traits.rs +++ b/compiler/rustc_trait_selection/src/solve/trait_goals/structural_traits.rs @@ -173,6 +173,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>, diff --git a/tests/ui/traits/new-solver/async.fail.stderr b/tests/ui/traits/new-solver/async.fail.stderr new file mode 100644 index 0000000000000..b395c23ae0057 --- /dev/null +++ b/tests/ui/traits/new-solver/async.fail.stderr @@ -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) {} + | ^^^^^^^^^^^^ required by this bound in `needs_async` + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0271`. diff --git a/tests/ui/traits/new-solver/async.rs b/tests/ui/traits/new-solver/async.rs new file mode 100644 index 0000000000000..195cc35cad2ad --- /dev/null +++ b/tests/ui/traits/new-solver/async.rs @@ -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) {} + +#[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 }); +} diff --git a/tests/ui/traits/new-solver/generator.fail.stderr b/tests/ui/traits/new-solver/generator.fail.stderr new file mode 100644 index 0000000000000..d94d41e3587b6 --- /dev/null +++ b/tests/ui/traits/new-solver/generator.fail.stderr @@ -0,0 +1,64 @@ +error[E0277]: the trait bound `[generator@$DIR/generator.rs:18:21: 18:23]: Generator` 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` 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) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `needs_generator` + +error[E0271]: type mismatch resolving `<[generator@$DIR/generator.rs:18:21: 18:23] as Generator>::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) {} + | ^^^^^^^^^ required by this bound in `needs_generator` + +error[E0271]: type mismatch resolving `<[generator@$DIR/generator.rs:18:21: 18:23] as Generator>::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) {} + | ^^^^^^^^^^ 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`. diff --git a/tests/ui/traits/new-solver/generator.rs b/tests/ui/traits/new-solver/generator.rs new file mode 100644 index 0000000000000..364373ca8be71 --- /dev/null +++ b/tests/ui/traits/new-solver/generator.rs @@ -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) {} + +#[cfg(fail)] +fn main() { + needs_generator(|| { + //[fail]~^ ERROR Generator` is not satisfied + //[fail]~| ERROR as Generator>::Yield == B` + //[fail]~| ERROR as Generator>::Return == C` + yield (); + }); +} + +#[cfg(pass)] +fn main() { + needs_generator(|_: A| { + let _: A = yield B; + C + }) +}