From 0feab539dea68ac77528a35ddaf19e48a9e471aa Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Mon, 1 May 2023 04:26:33 +0000 Subject: [PATCH 1/4] Add refine attribute --- compiler/rustc_feature/src/active.rs | 2 + compiler/rustc_feature/src/builtin_attrs.rs | 3 + compiler/rustc_lint_defs/src/builtin.rs | 9 +++ compiler/rustc_passes/messages.ftl | 17 +++++ compiler/rustc_passes/src/check_attr.rs | 32 +++++++++ compiler/rustc_passes/src/errors.rs | 34 +++++++++ compiler/rustc_span/src/symbol.rs | 1 + tests/ui/feature-gates/feature-gate-refine.rs | 11 +++ .../feature-gates/feature-gate-refine.stderr | 12 ++++ tests/ui/rfcs/rfc-3245-refined-impls/attr.rs | 44 ++++++++++++ .../rfcs/rfc-3245-refined-impls/attr.stderr | 71 +++++++++++++++++++ 11 files changed, 236 insertions(+) create mode 100644 tests/ui/feature-gates/feature-gate-refine.rs create mode 100644 tests/ui/feature-gates/feature-gate-refine.stderr create mode 100644 tests/ui/rfcs/rfc-3245-refined-impls/attr.rs create mode 100644 tests/ui/rfcs/rfc-3245-refined-impls/attr.stderr diff --git a/compiler/rustc_feature/src/active.rs b/compiler/rustc_feature/src/active.rs index ede3570510acb..4ab7efc3b3ce1 100644 --- a/compiler/rustc_feature/src/active.rs +++ b/compiler/rustc_feature/src/active.rs @@ -529,6 +529,8 @@ declare_features! ( (active, proc_macro_hygiene, "1.30.0", Some(54727), None), /// Allows `&raw const $place_expr` and `&raw mut $place_expr` expressions. (active, raw_ref_op, "1.41.0", Some(64490), None), + /// Allows use of the `#![refine]` attribute, and checks items for accidental refinements. + (incomplete, refine, "CURRENT_RUSTC_VERSION", Some(1), None), /// Allows using the `#[register_tool]` attribute. (active, register_tool, "1.41.0", Some(66079), None), /// Allows the `#[repr(i128)]` attribute for enums. diff --git a/compiler/rustc_feature/src/builtin_attrs.rs b/compiler/rustc_feature/src/builtin_attrs.rs index a183cfd8776a3..3a7c101535d97 100644 --- a/compiler/rustc_feature/src/builtin_attrs.rs +++ b/compiler/rustc_feature/src/builtin_attrs.rs @@ -497,6 +497,9 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[ experimental!(cfi_encoding) ), + // RFC 3245 + gated!(refine, Normal, template!(Word), WarnFollowing, experimental!(refine)), + // ========================================================================== // Internal attributes: Stability, deprecation, and unsafe: // ========================================================================== diff --git a/compiler/rustc_lint_defs/src/builtin.rs b/compiler/rustc_lint_defs/src/builtin.rs index 3747e562cec58..6f88f20bae614 100644 --- a/compiler/rustc_lint_defs/src/builtin.rs +++ b/compiler/rustc_lint_defs/src/builtin.rs @@ -3436,6 +3436,7 @@ declare_lint_pass! { TYVAR_BEHIND_RAW_POINTER, UNCONDITIONAL_PANIC, UNCONDITIONAL_RECURSION, + UNDECLARED_REFINE, UNDEFINED_NAKED_FUNCTION_ABI, UNFULFILLED_LINT_EXPECTATIONS, UNINHABITED_STATIC, @@ -4422,6 +4423,14 @@ declare_lint! { @feature_gate = sym::type_privacy_lints; } +declare_lint! { + /// Refine + pub UNDECLARED_REFINE, + Warn, + "yeet", + @feature_gate = sym::refine; +} + declare_lint! { /// The `unknown_diagnostic_attributes` lint detects unrecognized diagnostic attributes. /// diff --git a/compiler/rustc_passes/messages.ftl b/compiler/rustc_passes/messages.ftl index 6eacbebe75f41..bf9406d5c76e1 100644 --- a/compiler/rustc_passes/messages.ftl +++ b/compiler/rustc_passes/messages.ftl @@ -52,6 +52,23 @@ passes_attr_only_on_main = passes_attr_only_on_root_main = `{$attr}` attribute can only be used on root `fn main()` +passes_bad_refine_attr_assoc_const = + associated consts cannot be refined + .label = this associated const + +passes_bad_refine_attr_inherent_impl = + `#[refine]` attribute cannot be put on an item in an inherent impl + .note = the attribute is useless because the item does not refine anything + .label = this impl item + +passes_bad_refine_attr_other_item = + `#[refine]` attribute must be put on an associated function or type in a trait implementation + .label = this item +passes_bad_refine_attr_trait = + `#[refine]` attribute cannot be put on an item in a trait + .note = the attribute is useless because the item does not refine anything + .label = this trait item + passes_both_ffi_const_and_pure = `#[ffi_const]` function cannot be `#[ffi_pure]` diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index 4f9b362e23796..a6e08eef9aa09 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -183,6 +183,7 @@ impl CheckAttrVisitor<'_> { | sym::rustc_allowed_through_unstable_modules | sym::rustc_promotable => self.check_stability_promotable(&attr, span, target), sym::link_ordinal => self.check_link_ordinal(&attr, span, target), + sym::refine => self.check_refine(&attr, span, target, hir_id), sym::rustc_confusables => self.check_confusables(&attr, target), _ => true, }; @@ -2304,6 +2305,37 @@ impl CheckAttrVisitor<'_> { self.abort.set(true); } } + + fn check_refine(&self, attr: &Attribute, span: Span, target: Target, hir_id: HirId) -> bool { + let attr_span = attr.span; + match target { + Target::Method(_) | Target::AssocTy => { + let parent_def_id = self.tcx.hir().get_parent_item(hir_id).def_id; + match self.tcx.hir().expect_item(parent_def_id).kind { + hir::ItemKind::Impl(impl_) if impl_.of_trait.is_some() => true, + hir::ItemKind::Impl(..) => { + self.tcx + .sess + .emit_err(errors::BadRefineAttr::InherentImpl { span, attr_span }); + false + } + hir::ItemKind::Trait(..) => { + self.tcx.sess.emit_err(errors::BadRefineAttr::Trait { span, attr_span }); + false + } + _ => unreachable!("only expect assoc ty and method on trait/impl"), + } + } + Target::AssocConst => { + self.tcx.sess.emit_err(errors::BadRefineAttr::AssocConst { span, attr_span }); + false + } + _ => { + self.tcx.sess.emit_err(errors::BadRefineAttr::OtherItem { span, attr_span }); + false + } + } + } } impl<'tcx> Visitor<'tcx> for CheckAttrVisitor<'tcx> { diff --git a/compiler/rustc_passes/src/errors.rs b/compiler/rustc_passes/src/errors.rs index 683717344cecc..7c1c935cac7b5 100644 --- a/compiler/rustc_passes/src/errors.rs +++ b/compiler/rustc_passes/src/errors.rs @@ -1757,3 +1757,37 @@ pub struct AttrCrateLevelOnlySugg { #[primary_span] pub attr: Span, } + +#[derive(Diagnostic)] +pub enum BadRefineAttr { + #[diag(passes_bad_refine_attr_inherent_impl)] + #[note] + InherentImpl { + #[primary_span] + attr_span: Span, + #[label] + span: Span, + }, + #[diag(passes_bad_refine_attr_trait)] + #[note] + Trait { + #[primary_span] + attr_span: Span, + #[label] + span: Span, + }, + #[diag(passes_bad_refine_attr_assoc_const)] + AssocConst { + #[primary_span] + attr_span: Span, + #[label] + span: Span, + }, + #[diag(passes_bad_refine_attr_other_item)] + OtherItem { + #[primary_span] + attr_span: Span, + #[label] + span: Span, + }, +} diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 9ea9efb047c2b..a50b87f3f8ffd 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -1201,6 +1201,7 @@ symbols! { reexport_test_harness_main, ref_unwind_safe_trait, reference, + refine, reflect, reg, reg16, diff --git a/tests/ui/feature-gates/feature-gate-refine.rs b/tests/ui/feature-gates/feature-gate-refine.rs new file mode 100644 index 0000000000000..9c8408e96a9b8 --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-refine.rs @@ -0,0 +1,11 @@ +trait Foo { + fn assoc(); +} + +impl Foo for () { + #[refine] + //~^ ERROR the `#[refine]` attribute is an experimental feature + fn assoc() {} +} + +fn main() {} diff --git a/tests/ui/feature-gates/feature-gate-refine.stderr b/tests/ui/feature-gates/feature-gate-refine.stderr new file mode 100644 index 0000000000000..8eaa4118ebaac --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-refine.stderr @@ -0,0 +1,12 @@ +error[E0658]: the `#[refine]` attribute is an experimental feature + --> $DIR/feature-gate-refine.rs:6:5 + | +LL | #[refine] + | ^^^^^^^^^ + | + = note: see issue #1 for more information + = help: add `#![feature(refine)]` to the crate attributes to enable + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0658`. diff --git a/tests/ui/rfcs/rfc-3245-refined-impls/attr.rs b/tests/ui/rfcs/rfc-3245-refined-impls/attr.rs new file mode 100644 index 0000000000000..9733f0bca3d1c --- /dev/null +++ b/tests/ui/rfcs/rfc-3245-refined-impls/attr.rs @@ -0,0 +1,44 @@ +#![feature(refine)] + +trait Foo { + #[refine] + //~^ ERROR `#[refine]` attribute cannot be put on an item in a trait + fn assoc(); + + #[refine] + //~^ ERROR `#[refine]` attribute cannot be put on an item in a trait + type Assoc; + + #[refine] + //~^ ERROR associated consts cannot be refined + const N: u32; +} + +struct W; +impl W { + #[refine] + //~^ ERROR `#[refine]` attribute cannot be put on an item in an inherent impl + fn assoc() {} + + #[refine] + //~^ ERROR associated consts cannot be refined + const M: u32 = 1; +} + +impl Foo for W { + #[refine] + //~^ ERROR associated consts cannot be refined + const N: u32 = 1; + + #[refine] + fn assoc() {} // Ok + + #[refine] + type Assoc = (); // Ok +} + +#[refine] +//~^ ERROR `#[refine]` attribute must be put on an associated function or type in a trait implementation +fn foo() {} + +fn main() {} diff --git a/tests/ui/rfcs/rfc-3245-refined-impls/attr.stderr b/tests/ui/rfcs/rfc-3245-refined-impls/attr.stderr new file mode 100644 index 0000000000000..5a072e307620a --- /dev/null +++ b/tests/ui/rfcs/rfc-3245-refined-impls/attr.stderr @@ -0,0 +1,71 @@ +error: `#[refine]` attribute must be put on an associated function or type in a trait implementation + --> $DIR/attr.rs:40:1 + | +LL | #[refine] + | ^^^^^^^^^ +LL | +LL | fn foo() {} + | ----------- this item + +error: `#[refine]` attribute cannot be put on an item in a trait + --> $DIR/attr.rs:4:5 + | +LL | #[refine] + | ^^^^^^^^^ +LL | +LL | fn assoc(); + | ----------- this trait item + | + = note: the attribute is useless because the item does not refine anything + +error: `#[refine]` attribute cannot be put on an item in a trait + --> $DIR/attr.rs:8:5 + | +LL | #[refine] + | ^^^^^^^^^ +LL | +LL | type Assoc; + | ----------- this trait item + | + = note: the attribute is useless because the item does not refine anything + +error: associated consts cannot be refined + --> $DIR/attr.rs:12:5 + | +LL | #[refine] + | ^^^^^^^^^ +LL | +LL | const N: u32; + | ------------- this associated const + +error: `#[refine]` attribute cannot be put on an item in an inherent impl + --> $DIR/attr.rs:19:5 + | +LL | #[refine] + | ^^^^^^^^^ +LL | +LL | fn assoc() {} + | ------------- this impl item + | + = note: the attribute is useless because the item does not refine anything + +error: associated consts cannot be refined + --> $DIR/attr.rs:23:5 + | +LL | #[refine] + | ^^^^^^^^^ +LL | +LL | const M: u32 = 1; + | ----------------- this associated const + +error: associated consts cannot be refined + --> $DIR/attr.rs:29:5 + | +LL | #[refine] + | ^^^^^^^^^ +LL | +LL | const N: u32 = 1; + | ----------------- this associated const + +error: aborting due to 7 previous errors + From 5b05b05a42157fd9fae8749e744d3445505cda00 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Wed, 24 May 2023 04:08:37 +0000 Subject: [PATCH 2/4] Implement refine check for RPITITs --- .../src/transform/validate.rs | 3 + compiler/rustc_hir_analysis/messages.ftl | 4 + .../src/check/compare_impl_item.rs | 8 + .../src/check/compare_impl_item/refine.rs | 201 ++++++++++++++++++ compiler/rustc_hir_analysis/src/errors.rs | 14 ++ compiler/rustc_middle/src/traits/mod.rs | 5 + compiler/rustc_middle/src/ty/mod.rs | 24 ++- .../src/solve/assembly/mod.rs | 2 +- .../src/solve/opaques.rs | 2 + .../src/traits/project.rs | 13 +- .../src/traits/query/normalize.rs | 4 +- .../in-trait/async-example-desugared-extra.rs | 4 +- .../impl-trait/in-trait/auxiliary/rpitit.rs | 3 +- .../impl-trait/in-trait/deep-match-works.rs | 3 +- .../in-trait/foreign.current.stderr | 11 + .../impl-trait/in-trait/foreign.next.stderr | 11 + tests/ui/impl-trait/in-trait/foreign.rs | 4 + tests/ui/impl-trait/in-trait/foreign.stderr | 11 + tests/ui/impl-trait/in-trait/issue-102571.rs | 8 - .../impl-trait/in-trait/issue-102571.stderr | 2 +- tests/ui/impl-trait/in-trait/nested-rpitit.rs | 3 + tests/ui/impl-trait/in-trait/object-safety.rs | 2 +- tests/ui/impl-trait/in-trait/refine.rs | 35 +++ tests/ui/impl-trait/in-trait/refine.stderr | 62 ++++++ tests/ui/impl-trait/in-trait/reveal.rs | 3 +- .../in-trait/specialization-substs-remap.rs | 2 + tests/ui/impl-trait/in-trait/success.rs | 5 +- tests/ui/rfcs/rfc-3245-refined-impls/attr.rs | 1 + .../rfcs/rfc-3245-refined-impls/attr.stderr | 25 ++- 29 files changed, 444 insertions(+), 31 deletions(-) create mode 100644 compiler/rustc_hir_analysis/src/check/compare_impl_item/refine.rs create mode 100644 tests/ui/impl-trait/in-trait/foreign.current.stderr create mode 100644 tests/ui/impl-trait/in-trait/foreign.next.stderr create mode 100644 tests/ui/impl-trait/in-trait/foreign.stderr create mode 100644 tests/ui/impl-trait/in-trait/refine.rs create mode 100644 tests/ui/impl-trait/in-trait/refine.stderr diff --git a/compiler/rustc_const_eval/src/transform/validate.rs b/compiler/rustc_const_eval/src/transform/validate.rs index 83004492c8bf0..86304a020f3c6 100644 --- a/compiler/rustc_const_eval/src/transform/validate.rs +++ b/compiler/rustc_const_eval/src/transform/validate.rs @@ -50,6 +50,9 @@ impl<'tcx> MirPass<'tcx> for Validator { let param_env = match mir_phase.reveal() { Reveal::UserFacing => tcx.param_env(def_id), Reveal::All => tcx.param_env_reveal_all_normalized(def_id), + Reveal::HideReturnPositionImplTraitInTrait => { + unreachable!("only used during refinement checks") + } }; let always_live_locals = always_storage_live_locals(body); diff --git a/compiler/rustc_hir_analysis/messages.ftl b/compiler/rustc_hir_analysis/messages.ftl index 166760166c108..af7a1675d2039 100644 --- a/compiler/rustc_hir_analysis/messages.ftl +++ b/compiler/rustc_hir_analysis/messages.ftl @@ -219,6 +219,10 @@ hir_analysis_return_type_notation_on_non_rpitit = .note = function returns `{$ty}`, which is not compatible with associated type return bounds .label = this function must be `async` or return `impl Trait` +hir_analysis_rpitit_refined = impl method signature does not match trait method signature + .suggestion = replace the return type so that it matches the trait + .label = return type from trait method defined here + .unmatched_bound_label = this bound is stronger than that defined on the trait hir_analysis_self_in_impl_self = `Self` is not valid in the self type of an impl block .note = replace `Self` with a different type diff --git a/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs b/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs index a8c66ff900116..fdd1de68a2ee1 100644 --- a/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs +++ b/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs @@ -28,6 +28,8 @@ use rustc_trait_selection::traits::{ use std::borrow::Cow; use std::iter; +mod refine; + /// Checks that a method from an impl conforms to the signature of /// the same method as declared in the trait. /// @@ -53,6 +55,12 @@ pub(super) fn compare_impl_method<'tcx>( impl_trait_ref, CheckImpliedWfMode::Check, )?; + refine::compare_impl_trait_in_trait_predicate_entailment( + tcx, + impl_m, + trait_m, + impl_trait_ref, + )?; }; } diff --git a/compiler/rustc_hir_analysis/src/check/compare_impl_item/refine.rs b/compiler/rustc_hir_analysis/src/check/compare_impl_item/refine.rs new file mode 100644 index 0000000000000..f641c9ed7c2fe --- /dev/null +++ b/compiler/rustc_hir_analysis/src/check/compare_impl_item/refine.rs @@ -0,0 +1,201 @@ +use std::ops::ControlFlow; + +use rustc_data_structures::fx::FxIndexMap; +use rustc_hir as hir; +use rustc_hir::def_id::DefId; +use rustc_infer::infer::TyCtxtInferExt; +use rustc_infer::traits::Obligation; +use rustc_middle::traits::ObligationCause; +use rustc_middle::ty::{ + self, Ty, TyCtxt, TypeFolder, TypeSuperFoldable, TypeSuperVisitable, TypeVisitable, TypeVisitor, +}; +use rustc_span::ErrorGuaranteed; +use rustc_span::{sym, Span}; +use rustc_trait_selection::traits::ObligationCtxt; +use rustc_type_ir::fold::TypeFoldable; + +/// Check that an implementation does not refine an RPITIT from a trait method signature. +pub(super) fn compare_impl_trait_in_trait_predicate_entailment<'tcx>( + tcx: TyCtxt<'tcx>, + impl_m: ty::AssocItem, + trait_m: ty::AssocItem, + impl_trait_ref: ty::TraitRef<'tcx>, +) -> Result<(), ErrorGuaranteed> { + if !tcx.impl_method_has_trait_impl_trait_tys(impl_m.def_id) + || tcx.has_attr(impl_m.def_id, sym::refine) + { + return Ok(()); + } + + let hidden_tys = tcx.collect_return_position_impl_trait_in_trait_tys(impl_m.def_id)?; + + let impl_def_id = impl_m.container_id(tcx); + //let trait_def_id = trait_m.container_id(tcx); + let trait_m_to_impl_m_substs = ty::InternalSubsts::identity_for_item(tcx, impl_m.def_id) + .rebase_onto(tcx, impl_def_id, impl_trait_ref.substs); + + let bound_trait_m_sig = tcx.fn_sig(trait_m.def_id).subst(tcx, trait_m_to_impl_m_substs); + let trait_m_sig = tcx.liberate_late_bound_regions(impl_m.def_id, bound_trait_m_sig); + + let mut visitor = ImplTraitInTraitCollector { tcx, types: FxIndexMap::default() }; + trait_m_sig.visit_with(&mut visitor); + + let mut reverse_mapping = FxIndexMap::default(); + let mut bounds_to_prove = vec![]; + for (rpitit_def_id, rpitit_substs) in visitor.types { + let hidden_ty = hidden_tys + .get(&rpitit_def_id) + .expect("expected hidden type for RPITIT") + .subst_identity(); + reverse_mapping.insert(hidden_ty, tcx.mk_projection(rpitit_def_id, rpitit_substs)); + + let ty::Alias(ty::Opaque, opaque_ty) = *hidden_ty.kind() else { + return Err(report_mismatched_rpitit_signature( + tcx, + trait_m_sig, + trait_m.def_id, + impl_m.def_id, + None, + )); + }; + + // Check that this is an opaque that comes from our impl fn + if !tcx.hir().get_if_local(opaque_ty.def_id).map_or(false, |node| { + matches!( + node.expect_item().expect_opaque_ty().origin, + hir::OpaqueTyOrigin::AsyncFn(def_id) | hir::OpaqueTyOrigin::FnReturn(def_id) + if def_id == impl_m.def_id.expect_local() + ) + }) { + return Err(report_mismatched_rpitit_signature( + tcx, + trait_m_sig, + trait_m.def_id, + impl_m.def_id, + None, + )); + } + + bounds_to_prove.extend( + tcx.explicit_item_bounds(opaque_ty.def_id) + .iter_instantiated_copied(tcx, opaque_ty.args), + ); + } + + let infcx = tcx.infer_ctxt().build(); + let ocx = ObligationCtxt::new(&infcx); + let param_env = + tcx.param_env(impl_m.def_id).with_hidden_return_position_impl_trait_in_trait_tys(); + + ocx.register_obligations( + bounds_to_prove.fold_with(&mut ReverseMapper { tcx, reverse_mapping }).into_iter().map( + |(pred, span)| { + Obligation::new(tcx, ObligationCause::dummy_with_span(span), param_env, pred) + }, + ), + ); + + let errors = ocx.select_all_or_error(); + if !errors.is_empty() { + let span = errors.first().unwrap().obligation.cause.span; + return Err(report_mismatched_rpitit_signature( + tcx, + trait_m_sig, + trait_m.def_id, + impl_m.def_id, + Some(span), + )); + } + + Ok(()) +} + +struct ImplTraitInTraitCollector<'tcx> { + tcx: TyCtxt<'tcx>, + types: FxIndexMap>, +} + +impl<'tcx> TypeVisitor> for ImplTraitInTraitCollector<'tcx> { + type BreakTy = !; + + fn visit_ty(&mut self, ty: Ty<'tcx>) -> std::ops::ControlFlow { + if let ty::Alias(ty::Projection, proj) = *ty.kind() + && self.tcx.is_impl_trait_in_trait(proj.def_id) + { + if self.types.insert(proj.def_id, proj.args).is_none() { + for (pred, _) in self + .tcx + .explicit_item_bounds(proj.def_id) + .iter_instantiated_copied(self.tcx, proj.args) + { + pred.visit_with(self)?; + } + } + ControlFlow::Continue(()) + } else { + ty.super_visit_with(self) + } + } +} + +struct ReverseMapper<'tcx> { + tcx: TyCtxt<'tcx>, + reverse_mapping: FxIndexMap, Ty<'tcx>>, +} + +impl<'tcx> TypeFolder> for ReverseMapper<'tcx> { + fn interner(&self) -> TyCtxt<'tcx> { + self.tcx + } + + fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> { + if let Some(ty) = self.reverse_mapping.get(&ty) { *ty } else { ty.super_fold_with(self) } + } +} + +fn report_mismatched_rpitit_signature<'tcx>( + tcx: TyCtxt<'tcx>, + trait_m_sig: ty::FnSig<'tcx>, + trait_m_def_id: DefId, + impl_m_def_id: DefId, + unmatched_bound: Option, +) -> ErrorGuaranteed { + let mapping = std::iter::zip( + tcx.fn_sig(trait_m_def_id).skip_binder().bound_vars(), + tcx.fn_sig(impl_m_def_id).skip_binder().bound_vars(), + ) + .filter_map(|(impl_bv, trait_bv)| { + if let ty::BoundVariableKind::Region(impl_bv) = impl_bv + && let ty::BoundVariableKind::Region(trait_bv) = trait_bv + { + Some((impl_bv, trait_bv)) + } else { + None + } + }) + .collect(); + + let return_ty = + trait_m_sig.output().fold_with(&mut super::RemapLateBound { tcx, mapping: &mapping }); + + let (span, impl_return_span, sugg) = + match tcx.hir().get_by_def_id(impl_m_def_id.expect_local()).fn_decl().unwrap().output { + hir::FnRetTy::DefaultReturn(span) => { + (tcx.def_span(impl_m_def_id), span, format!("-> {return_ty} ")) + } + hir::FnRetTy::Return(ty) => (ty.span, ty.span, format!("{return_ty}")), + }; + let trait_return_span = + tcx.hir().get_if_local(trait_m_def_id).map(|node| match node.fn_decl().unwrap().output { + hir::FnRetTy::DefaultReturn(_) => tcx.def_span(trait_m_def_id), + hir::FnRetTy::Return(ty) => ty.span, + }); + + tcx.sess.emit_err(crate::errors::ReturnPositionImplTraitInTraitRefined { + span, + impl_return_span, + trait_return_span, + sugg, + unmatched_bound, + }) +} diff --git a/compiler/rustc_hir_analysis/src/errors.rs b/compiler/rustc_hir_analysis/src/errors.rs index 0babdf7e5b3e7..819cd1c8fc54a 100644 --- a/compiler/rustc_hir_analysis/src/errors.rs +++ b/compiler/rustc_hir_analysis/src/errors.rs @@ -918,3 +918,17 @@ pub struct UnusedAssociatedTypeBounds { #[suggestion(code = "")] pub span: Span, } + +#[derive(Diagnostic)] +#[diag(hir_analysis_rpitit_refined)] +pub(crate) struct ReturnPositionImplTraitInTraitRefined { + #[primary_span] + pub span: Span, + #[suggestion(applicability = "maybe-incorrect", code = "{sugg}")] + pub impl_return_span: Span, + #[label] + pub trait_return_span: Option, + pub sugg: String, + #[label(hir_analysis_unmatched_bound_label)] + pub unmatched_bound: Option, +} diff --git a/compiler/rustc_middle/src/traits/mod.rs b/compiler/rustc_middle/src/traits/mod.rs index 85116555fc0ec..5daa44a2919f4 100644 --- a/compiler/rustc_middle/src/traits/mod.rs +++ b/compiler/rustc_middle/src/traits/mod.rs @@ -64,6 +64,11 @@ pub enum Reveal { /// type-checking. UserFacing, + // Same as user-facing reveal, but do not project ("reveal") return-position + // impl trait in traits. This is only used for checking that an RPITIT is not + // refined by an implementation. + HideReturnPositionImplTraitInTrait, + /// At codegen time, all monomorphic projections will succeed. /// Also, `impl Trait` is normalized to the concrete type, /// which has to be already collected by type-checking. diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index 48aa25dba6dc7..5a551d7c678a9 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -1662,8 +1662,15 @@ struct ParamTag { impl_tag! { impl Tag for ParamTag; - ParamTag { reveal: traits::Reveal::UserFacing }, - ParamTag { reveal: traits::Reveal::All }, + ParamTag { + reveal: traits::Reveal::UserFacing, + }, + ParamTag { + reveal: traits::Reveal::All, + }, + ParamTag { + reveal: traits::Reveal::HideReturnPositionImplTraitInTrait, + }, } impl<'tcx> fmt::Debug for ParamEnv<'tcx> { @@ -1767,6 +1774,15 @@ impl<'tcx> ParamEnv<'tcx> { Self::new(List::empty(), self.reveal()) } + #[inline] + pub fn with_hidden_return_position_impl_trait_in_trait_tys(self) -> Self { + Self::new( + self.caller_bounds(), + Reveal::HideReturnPositionImplTraitInTrait, + self.constness(), + ) + } + /// Creates a suitable environment in which to perform trait /// queries on the given value. When type-checking, this is simply /// the pair of the environment plus value. But when reveal is set to @@ -1781,7 +1797,9 @@ impl<'tcx> ParamEnv<'tcx> { /// although the surrounding function is never reachable. pub fn and>>(self, value: T) -> ParamEnvAnd<'tcx, T> { match self.reveal() { - Reveal::UserFacing => ParamEnvAnd { param_env: self, value }, + Reveal::UserFacing | Reveal::HideReturnPositionImplTraitInTrait => { + ParamEnvAnd { param_env: self, value } + } Reveal::All => { if value.is_global() { diff --git a/compiler/rustc_trait_selection/src/solve/assembly/mod.rs b/compiler/rustc_trait_selection/src/solve/assembly/mod.rs index 1391b51e67f31..f8132cfb4d15a 100644 --- a/compiler/rustc_trait_selection/src/solve/assembly/mod.rs +++ b/compiler/rustc_trait_selection/src/solve/assembly/mod.rs @@ -741,7 +741,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { self.merge_candidates(param_env_candidates) } ty::Alias(ty::Opaque, _opaque_ty) => match goal.param_env.reveal() { - Reveal::UserFacing => { + Reveal::UserFacing | Reveal::HideReturnPositionImplTraitInTrait => { self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) } Reveal::All => return Err(NoSolution), diff --git a/compiler/rustc_trait_selection/src/solve/opaques.rs b/compiler/rustc_trait_selection/src/solve/opaques.rs index f08adc0208b57..2b8ba7780a45d 100644 --- a/compiler/rustc_trait_selection/src/solve/opaques.rs +++ b/compiler/rustc_trait_selection/src/solve/opaques.rs @@ -80,6 +80,8 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { self.eq(goal.param_env, expected, actual)?; self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) } + (Reveal::HideReturnPositionImplTraitInTrait, SolverMode::Normal) => todo!(), + (Reveal::HideReturnPositionImplTraitInTrait, SolverMode::Coherence) => todo!(), } } } diff --git a/compiler/rustc_trait_selection/src/traits/project.rs b/compiler/rustc_trait_selection/src/traits/project.rs index 3d800421b764b..d4250fc69f161 100644 --- a/compiler/rustc_trait_selection/src/traits/project.rs +++ b/compiler/rustc_trait_selection/src/traits/project.rs @@ -412,7 +412,7 @@ pub(crate) fn needs_normalization<'tcx, T: TypeVisitable>>( reveal: Reveal, ) -> bool { match reveal { - Reveal::UserFacing => value.has_type_flags( + Reveal::UserFacing | Reveal::HideReturnPositionImplTraitInTrait => value.has_type_flags( ty::TypeFlags::HAS_TY_PROJECTION | ty::TypeFlags::HAS_TY_INHERENT | ty::TypeFlags::HAS_CT_PROJECTION, @@ -546,7 +546,9 @@ impl<'a, 'b, 'tcx> TypeFolder> for AssocTypeNormalizer<'a, 'b, 'tcx ty::Opaque => { // Only normalize `impl Trait` outside of type inference, usually in codegen. match self.param_env.reveal() { - Reveal::UserFacing => ty.super_fold_with(self), + Reveal::UserFacing | Reveal::HideReturnPositionImplTraitInTrait => { + ty.super_fold_with(self) + } Reveal::All => { let recursion_limit = self.interner().recursion_limit(); @@ -1699,6 +1701,13 @@ fn assemble_candidates_from_impls<'cx, 'tcx>( obligation: &ProjectionTyObligation<'tcx>, candidate_set: &mut ProjectionCandidateSet<'tcx>, ) { + // Don't reveal RPITIT if we are checking RPITIT refines. + if selcx.tcx().is_impl_trait_in_trait(obligation.predicate.def_id) + && obligation.param_env.reveal() == Reveal::HideReturnPositionImplTraitInTrait + { + return; + } + // If we are resolving `>::Item == Type`, // start out by selecting the predicate `T as TraitRef<...>`: let trait_ref = obligation.predicate.trait_ref(selcx.tcx()); diff --git a/compiler/rustc_trait_selection/src/traits/query/normalize.rs b/compiler/rustc_trait_selection/src/traits/query/normalize.rs index 87beaddc6c200..bb31237fed0e2 100644 --- a/compiler/rustc_trait_selection/src/traits/query/normalize.rs +++ b/compiler/rustc_trait_selection/src/traits/query/normalize.rs @@ -225,7 +225,9 @@ impl<'cx, 'tcx> FallibleTypeFolder> for QueryNormalizer<'cx, 'tcx> ty::Opaque => { // Only normalize `impl Trait` outside of type inference, usually in codegen. match self.param_env.reveal() { - Reveal::UserFacing => ty.try_super_fold_with(self)?, + Reveal::UserFacing | Reveal::HideReturnPositionImplTraitInTrait => { + ty.try_super_fold_with(self)? + } Reveal::All => { let args = data.args.try_fold_with(self)?; diff --git a/tests/ui/async-await/in-trait/async-example-desugared-extra.rs b/tests/ui/async-await/in-trait/async-example-desugared-extra.rs index 81e1e59a36249..662ba6e4d8bc0 100644 --- a/tests/ui/async-await/in-trait/async-example-desugared-extra.rs +++ b/tests/ui/async-await/in-trait/async-example-desugared-extra.rs @@ -3,6 +3,7 @@ #![feature(async_fn_in_trait)] #![feature(return_position_impl_trait_in_trait)] +#![feature(refine)] #![allow(incomplete_features)] use std::future::Future; @@ -27,8 +28,7 @@ impl Future for MyFuture { } impl MyTrait for i32 { - // FIXME: this should eventually require `#[refine]` to compile, because it also provides - // `Clone`. + #[refine] fn foo(&self) -> impl Future + Clone { MyFuture(*self) } diff --git a/tests/ui/impl-trait/in-trait/auxiliary/rpitit.rs b/tests/ui/impl-trait/in-trait/auxiliary/rpitit.rs index cfc2193f633e0..dc4bc438c0e80 100644 --- a/tests/ui/impl-trait/in-trait/auxiliary/rpitit.rs +++ b/tests/ui/impl-trait/in-trait/auxiliary/rpitit.rs @@ -1,4 +1,4 @@ -#![feature(return_position_impl_trait_in_trait)] +#![feature(return_position_impl_trait_in_trait, refine)] use std::ops::Deref; @@ -8,6 +8,7 @@ pub trait Foo { pub struct Foreign; impl Foo for Foreign { + #[refine] fn bar(self) -> &'static () { &() } diff --git a/tests/ui/impl-trait/in-trait/deep-match-works.rs b/tests/ui/impl-trait/in-trait/deep-match-works.rs index 78cff97c61654..d4de02bae3b15 100644 --- a/tests/ui/impl-trait/in-trait/deep-match-works.rs +++ b/tests/ui/impl-trait/in-trait/deep-match-works.rs @@ -1,6 +1,6 @@ // check-pass -#![feature(return_position_impl_trait_in_trait)] +#![feature(return_position_impl_trait_in_trait, refine)] #![allow(incomplete_features)] struct Wrapper(T); @@ -10,6 +10,7 @@ trait Foo { } impl Foo for () { + #[refine] fn bar() -> Wrapper { Wrapper(0) } diff --git a/tests/ui/impl-trait/in-trait/foreign.current.stderr b/tests/ui/impl-trait/in-trait/foreign.current.stderr new file mode 100644 index 0000000000000..3f8ebabe58fd1 --- /dev/null +++ b/tests/ui/impl-trait/in-trait/foreign.current.stderr @@ -0,0 +1,11 @@ +warning: the feature `refine` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/foreign.rs:6:12 + | +LL | #![feature(refine)] + | ^^^^^^ + | + = note: see issue #1 for more information + = note: `#[warn(incomplete_features)]` on by default + +warning: 1 warning emitted + diff --git a/tests/ui/impl-trait/in-trait/foreign.next.stderr b/tests/ui/impl-trait/in-trait/foreign.next.stderr new file mode 100644 index 0000000000000..3f8ebabe58fd1 --- /dev/null +++ b/tests/ui/impl-trait/in-trait/foreign.next.stderr @@ -0,0 +1,11 @@ +warning: the feature `refine` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/foreign.rs:6:12 + | +LL | #![feature(refine)] + | ^^^^^^ + | + = note: see issue #1 for more information + = note: `#[warn(incomplete_features)]` on by default + +warning: 1 warning emitted + diff --git a/tests/ui/impl-trait/in-trait/foreign.rs b/tests/ui/impl-trait/in-trait/foreign.rs index b0c93a0293512..6b9575ab7484a 100644 --- a/tests/ui/impl-trait/in-trait/foreign.rs +++ b/tests/ui/impl-trait/in-trait/foreign.rs @@ -1,6 +1,9 @@ // check-pass // aux-build: rpitit.rs +#![feature(refine)] +//~^ WARN the feature `refine` is incomplete and may not be safe to use and/or cause compiler crashes + extern crate rpitit; use rpitit::{Foo, Foreign}; @@ -9,6 +12,7 @@ use std::sync::Arc; // Implement an RPITIT from another crate. struct Local; impl Foo for Local { + #[refine] fn bar(self) -> Arc { Arc::new(String::new()) } diff --git a/tests/ui/impl-trait/in-trait/foreign.stderr b/tests/ui/impl-trait/in-trait/foreign.stderr new file mode 100644 index 0000000000000..b3c27fa26fb35 --- /dev/null +++ b/tests/ui/impl-trait/in-trait/foreign.stderr @@ -0,0 +1,11 @@ +warning: the feature `refine` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/foreign.rs:4:12 + | +LL | #![feature(refine)] + | ^^^^^^ + | + = note: see issue #1 for more information + = note: `#[warn(incomplete_features)]` on by default + +warning: 1 warning emitted + diff --git a/tests/ui/impl-trait/in-trait/issue-102571.rs b/tests/ui/impl-trait/in-trait/issue-102571.rs index 61c91e64417b3..ccb53031c44bc 100644 --- a/tests/ui/impl-trait/in-trait/issue-102571.rs +++ b/tests/ui/impl-trait/in-trait/issue-102571.rs @@ -8,14 +8,6 @@ trait Foo { fn bar(self) -> impl Deref; } -struct A; - -impl Foo for A { - fn bar(self) -> &'static str { - "Hello, world" - } -} - fn foo(t: T) { let () = t.bar(); //~^ ERROR mismatched types diff --git a/tests/ui/impl-trait/in-trait/issue-102571.stderr b/tests/ui/impl-trait/in-trait/issue-102571.stderr index 87219941d9161..594b9ae9cd6bc 100644 --- a/tests/ui/impl-trait/in-trait/issue-102571.stderr +++ b/tests/ui/impl-trait/in-trait/issue-102571.stderr @@ -1,5 +1,5 @@ error[E0308]: mismatched types - --> $DIR/issue-102571.rs:20:9 + --> $DIR/issue-102571.rs:12:9 | LL | let () = t.bar(); | ^^ ------- this expression has type `impl Deref` diff --git a/tests/ui/impl-trait/in-trait/nested-rpitit.rs b/tests/ui/impl-trait/in-trait/nested-rpitit.rs index 65285e3a3ccaf..5bdc9bd220fd2 100644 --- a/tests/ui/impl-trait/in-trait/nested-rpitit.rs +++ b/tests/ui/impl-trait/in-trait/nested-rpitit.rs @@ -1,6 +1,7 @@ // check-pass #![feature(return_position_impl_trait_in_trait)] +#![feature(refine)] #![allow(incomplete_features)] use std::fmt::Display; @@ -13,6 +14,7 @@ trait Foo { struct A; impl Foo for A { + #[refine] fn bar(self) -> &'static str { "Hello, world" } @@ -21,6 +23,7 @@ impl Foo for A { struct B; impl Foo for B { + #[refine] fn bar(self) -> Box { Box::new(42) } diff --git a/tests/ui/impl-trait/in-trait/object-safety.rs b/tests/ui/impl-trait/in-trait/object-safety.rs index dd35b9a2d8a75..79bdebf9483f4 100644 --- a/tests/ui/impl-trait/in-trait/object-safety.rs +++ b/tests/ui/impl-trait/in-trait/object-safety.rs @@ -8,7 +8,7 @@ trait Foo { } impl Foo for u32 { - fn baz(&self) -> u32 { + fn baz(&self) -> impl Debug { 32 } } diff --git a/tests/ui/impl-trait/in-trait/refine.rs b/tests/ui/impl-trait/in-trait/refine.rs new file mode 100644 index 0000000000000..5d2467a7a95c0 --- /dev/null +++ b/tests/ui/impl-trait/in-trait/refine.rs @@ -0,0 +1,35 @@ +#![feature(return_position_impl_trait_in_trait, async_fn_in_trait)] + +trait Foo { + fn bar() -> impl Sized; +} + +struct A; +impl Foo for A { + fn bar() -> impl Copy {} + //~^ ERROR impl method signature does not match trait method signature +} + +struct B; +impl Foo for B { + fn bar() {} + //~^ ERROR impl method signature does not match trait method signature +} + +struct C; +impl Foo for C { + fn bar() -> () {} + //~^ ERROR impl method signature does not match trait method signature +} + +trait Late { + fn bar<'a>(&'a self) -> impl Sized + 'a; +} + +struct D; +impl Late for D { + fn bar(&self) -> impl Copy + '_ {} + //~^ ERROR impl method signature does not match trait method signature +} + +fn main() {} diff --git a/tests/ui/impl-trait/in-trait/refine.stderr b/tests/ui/impl-trait/in-trait/refine.stderr new file mode 100644 index 0000000000000..a1f647851de57 --- /dev/null +++ b/tests/ui/impl-trait/in-trait/refine.stderr @@ -0,0 +1,62 @@ +error: impl method signature does not match trait method signature + --> $DIR/refine.rs:9:17 + | +LL | fn bar() -> impl Sized; + | ---------- return type from trait method defined here +... +LL | fn bar() -> impl Copy {} + | ^^^^^---- + | | + | this bound is stronger than that defined on the trait + | +help: replace the return type so that it matches the trait + | +LL | fn bar() -> impl Sized {} + | ~~~~~~~~~~ + +error: impl method signature does not match trait method signature + --> $DIR/refine.rs:15:5 + | +LL | fn bar() -> impl Sized; + | ---------- return type from trait method defined here +... +LL | fn bar() {} + | ^^^^^^^^ + | +help: replace the return type so that it matches the trait + | +LL | fn bar() -> impl Sized {} + | +++++++++++++ + +error: impl method signature does not match trait method signature + --> $DIR/refine.rs:21:17 + | +LL | fn bar() -> impl Sized; + | ---------- return type from trait method defined here +... +LL | fn bar() -> () {} + | ^^ + | +help: replace the return type so that it matches the trait + | +LL | fn bar() -> impl Sized {} + | ~~~~~~~~~~ + +error: impl method signature does not match trait method signature + --> $DIR/refine.rs:31:22 + | +LL | fn bar<'a>(&'a self) -> impl Sized + 'a; + | --------------- return type from trait method defined here +... +LL | fn bar(&self) -> impl Copy + '_ {} + | ^^^^^----^^^^^ + | | + | this bound is stronger than that defined on the trait + | +help: replace the return type so that it matches the trait + | +LL | fn bar(&self) -> impl Sized + '_ {} + | ~~~~~~~~~~~~~~~ + +error: aborting due to 4 previous errors + diff --git a/tests/ui/impl-trait/in-trait/reveal.rs b/tests/ui/impl-trait/in-trait/reveal.rs index d6ede1cc495c6..15f9931df1196 100644 --- a/tests/ui/impl-trait/in-trait/reveal.rs +++ b/tests/ui/impl-trait/in-trait/reveal.rs @@ -1,6 +1,6 @@ // check-pass -#![feature(return_position_impl_trait_in_trait)] +#![feature(return_position_impl_trait_in_trait, refine)] #![allow(incomplete_features)] trait Foo { @@ -8,6 +8,7 @@ trait Foo { } impl Foo for () { + #[refine] fn f() -> Box { Box::new(String::new()) } diff --git a/tests/ui/impl-trait/in-trait/specialization-substs-remap.rs b/tests/ui/impl-trait/in-trait/specialization-substs-remap.rs index c9ee877db8ec5..014586873f1ef 100644 --- a/tests/ui/impl-trait/in-trait/specialization-substs-remap.rs +++ b/tests/ui/impl-trait/in-trait/specialization-substs-remap.rs @@ -2,6 +2,7 @@ #![feature(specialization)] #![feature(return_position_impl_trait_in_trait)] +#![feature(refine)] #![allow(incomplete_features)] trait Foo { @@ -12,6 +13,7 @@ impl Foo for U where U: Copy, { + #[refine] fn bar(&self) -> U { *self } diff --git a/tests/ui/impl-trait/in-trait/success.rs b/tests/ui/impl-trait/in-trait/success.rs index 4cbe682b46f73..8e0984af255cd 100644 --- a/tests/ui/impl-trait/in-trait/success.rs +++ b/tests/ui/impl-trait/in-trait/success.rs @@ -1,6 +1,6 @@ // check-pass -#![feature(return_position_impl_trait_in_trait)] +#![feature(return_position_impl_trait_in_trait, refine)] #![allow(incomplete_features)] use std::fmt::Display; @@ -10,12 +10,14 @@ trait Foo { } impl Foo for i32 { + #[refine] fn bar(&self) -> i32 { *self } } impl Foo for &'static str { + #[refine] fn bar(&self) -> &'static str { *self } @@ -24,6 +26,7 @@ impl Foo for &'static str { struct Yay; impl Foo for Yay { + #[refine] fn bar(&self) -> String { String::from(":^)") } diff --git a/tests/ui/rfcs/rfc-3245-refined-impls/attr.rs b/tests/ui/rfcs/rfc-3245-refined-impls/attr.rs index 9733f0bca3d1c..b48262a023c1a 100644 --- a/tests/ui/rfcs/rfc-3245-refined-impls/attr.rs +++ b/tests/ui/rfcs/rfc-3245-refined-impls/attr.rs @@ -1,4 +1,5 @@ #![feature(refine)] +//~^ WARN the feature `refine` is incomplete and may not be safe to use and/or cause compiler crashes trait Foo { #[refine] diff --git a/tests/ui/rfcs/rfc-3245-refined-impls/attr.stderr b/tests/ui/rfcs/rfc-3245-refined-impls/attr.stderr index 5a072e307620a..aaa820f5322d8 100644 --- a/tests/ui/rfcs/rfc-3245-refined-impls/attr.stderr +++ b/tests/ui/rfcs/rfc-3245-refined-impls/attr.stderr @@ -1,5 +1,14 @@ +warning: the feature `refine` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/attr.rs:1:12 + | +LL | #![feature(refine)] + | ^^^^^^ + | + = note: see issue #1 for more information + = note: `#[warn(incomplete_features)]` on by default + error: `#[refine]` attribute must be put on an associated function or type in a trait implementation - --> $DIR/attr.rs:40:1 + --> $DIR/attr.rs:41:1 | LL | #[refine] | ^^^^^^^^^ @@ -8,7 +17,7 @@ LL | fn foo() {} | ----------- this item error: `#[refine]` attribute cannot be put on an item in a trait - --> $DIR/attr.rs:4:5 + --> $DIR/attr.rs:5:5 | LL | #[refine] | ^^^^^^^^^ @@ -19,7 +28,7 @@ LL | fn assoc(); = note: the attribute is useless because the item does not refine anything error: `#[refine]` attribute cannot be put on an item in a trait - --> $DIR/attr.rs:8:5 + --> $DIR/attr.rs:9:5 | LL | #[refine] | ^^^^^^^^^ @@ -30,7 +39,7 @@ LL | type Assoc; = note: the attribute is useless because the item does not refine anything error: associated consts cannot be refined - --> $DIR/attr.rs:12:5 + --> $DIR/attr.rs:13:5 | LL | #[refine] | ^^^^^^^^^ @@ -39,7 +48,7 @@ LL | const N: u32; | ------------- this associated const error: `#[refine]` attribute cannot be put on an item in an inherent impl - --> $DIR/attr.rs:19:5 + --> $DIR/attr.rs:20:5 | LL | #[refine] | ^^^^^^^^^ @@ -50,7 +59,7 @@ LL | fn assoc() {} = note: the attribute is useless because the item does not refine anything error: associated consts cannot be refined - --> $DIR/attr.rs:23:5 + --> $DIR/attr.rs:24:5 | LL | #[refine] | ^^^^^^^^^ @@ -59,7 +68,7 @@ LL | const M: u32 = 1; | ----------------- this associated const error: associated consts cannot be refined - --> $DIR/attr.rs:29:5 + --> $DIR/attr.rs:30:5 | LL | #[refine] | ^^^^^^^^^ @@ -67,5 +76,5 @@ LL | LL | const N: u32 = 1; | ----------------- this associated const -error: aborting due to 7 previous errors +error: aborting due to 7 previous errors; 1 warning emitted From b3967c93b344313db91c02257b81bffd5a5cdd56 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Wed, 24 May 2023 05:15:37 +0000 Subject: [PATCH 3/4] Check for refined lifetimes too --- .../src/check/compare_impl_item/refine.rs | 77 ++++++++++++++----- compiler/rustc_middle/src/ty/mod.rs | 9 --- tests/ui/impl-trait/in-trait/refine.rs | 12 +++ tests/ui/impl-trait/in-trait/refine.stderr | 16 +++- 4 files changed, 85 insertions(+), 29 deletions(-) diff --git a/compiler/rustc_hir_analysis/src/check/compare_impl_item/refine.rs b/compiler/rustc_hir_analysis/src/check/compare_impl_item/refine.rs index f641c9ed7c2fe..e94d5a7726cb9 100644 --- a/compiler/rustc_hir_analysis/src/check/compare_impl_item/refine.rs +++ b/compiler/rustc_hir_analysis/src/check/compare_impl_item/refine.rs @@ -1,17 +1,19 @@ use std::ops::ControlFlow; -use rustc_data_structures::fx::FxIndexMap; +use rustc_data_structures::fx::{FxIndexMap, FxIndexSet}; use rustc_hir as hir; use rustc_hir::def_id::DefId; +use rustc_infer::infer::outlives::env::OutlivesEnvironment; use rustc_infer::infer::TyCtxtInferExt; use rustc_infer::traits::Obligation; -use rustc_middle::traits::ObligationCause; +use rustc_middle::traits::{ObligationCause, Reveal}; use rustc_middle::ty::{ self, Ty, TyCtxt, TypeFolder, TypeSuperFoldable, TypeSuperVisitable, TypeVisitable, TypeVisitor, }; use rustc_span::ErrorGuaranteed; use rustc_span::{sym, Span}; -use rustc_trait_selection::traits::ObligationCtxt; +use rustc_trait_selection::traits::outlives_bounds::InferCtxtExt; +use rustc_trait_selection::traits::{normalize_param_env_or_error, ObligationCtxt}; use rustc_type_ir::fold::TypeFoldable; /// Check that an implementation does not refine an RPITIT from a trait method signature. @@ -30,24 +32,48 @@ pub(super) fn compare_impl_trait_in_trait_predicate_entailment<'tcx>( let hidden_tys = tcx.collect_return_position_impl_trait_in_trait_tys(impl_m.def_id)?; let impl_def_id = impl_m.container_id(tcx); - //let trait_def_id = trait_m.container_id(tcx); - let trait_m_to_impl_m_substs = ty::InternalSubsts::identity_for_item(tcx, impl_m.def_id) - .rebase_onto(tcx, impl_def_id, impl_trait_ref.substs); + let trait_def_id = trait_m.container_id(tcx); + let trait_m_to_impl_m_args = ty::GenericArgs::identity_for_item(tcx, impl_m.def_id) + .rebase_onto(tcx, impl_def_id, impl_trait_ref.args); - let bound_trait_m_sig = tcx.fn_sig(trait_m.def_id).subst(tcx, trait_m_to_impl_m_substs); - let trait_m_sig = tcx.liberate_late_bound_regions(impl_m.def_id, bound_trait_m_sig); + let infcx = tcx.infer_ctxt().build(); + let ocx = ObligationCtxt::new(&infcx); + + let mut hybrid_preds = tcx.predicates_of(impl_def_id).instantiate_identity(tcx).predicates; + hybrid_preds.extend( + tcx.predicates_of(trait_m.def_id) + .instantiate_own(tcx, trait_m_to_impl_m_args) + .map(|(pred, _)| pred), + ); + let normalize_cause = + ObligationCause::misc(tcx.def_span(impl_m.def_id), impl_m.def_id.expect_local()); + let unnormalized_param_env = ty::ParamEnv::new( + tcx.mk_clauses(&hybrid_preds), + Reveal::HideReturnPositionImplTraitInTrait, + ); + let param_env = normalize_param_env_or_error(tcx, unnormalized_param_env, normalize_cause); + + let bound_trait_m_sig = tcx.fn_sig(trait_m.def_id).instantiate(tcx, trait_m_to_impl_m_args); + let unnormalized_trait_m_sig = + tcx.liberate_late_bound_regions(impl_m.def_id, bound_trait_m_sig); + let trait_m_sig = ocx.normalize(&ObligationCause::dummy(), param_env, unnormalized_trait_m_sig); let mut visitor = ImplTraitInTraitCollector { tcx, types: FxIndexMap::default() }; trait_m_sig.visit_with(&mut visitor); let mut reverse_mapping = FxIndexMap::default(); let mut bounds_to_prove = vec![]; - for (rpitit_def_id, rpitit_substs) in visitor.types { - let hidden_ty = hidden_tys - .get(&rpitit_def_id) - .expect("expected hidden type for RPITIT") - .subst_identity(); - reverse_mapping.insert(hidden_ty, tcx.mk_projection(rpitit_def_id, rpitit_substs)); + for (rpitit_def_id, rpitit_args) in visitor.types { + let hidden_ty = + hidden_tys.get(&rpitit_def_id).expect("expected hidden type for RPITIT").instantiate( + tcx, + rpitit_args.rebase_onto( + tcx, + trait_def_id, + ty::GenericArgs::identity_for_item(tcx, impl_def_id), + ), + ); + reverse_mapping.insert(hidden_ty, Ty::new_projection(tcx, rpitit_def_id, rpitit_args)); let ty::Alias(ty::Opaque, opaque_ty) = *hidden_ty.kind() else { return Err(report_mismatched_rpitit_signature( @@ -82,11 +108,6 @@ pub(super) fn compare_impl_trait_in_trait_predicate_entailment<'tcx>( ); } - let infcx = tcx.infer_ctxt().build(); - let ocx = ObligationCtxt::new(&infcx); - let param_env = - tcx.param_env(impl_m.def_id).with_hidden_return_position_impl_trait_in_trait_tys(); - ocx.register_obligations( bounds_to_prove.fold_with(&mut ReverseMapper { tcx, reverse_mapping }).into_iter().map( |(pred, span)| { @@ -107,6 +128,24 @@ pub(super) fn compare_impl_trait_in_trait_predicate_entailment<'tcx>( )); } + let mut wf_tys = FxIndexSet::default(); + wf_tys.extend(unnormalized_trait_m_sig.inputs_and_output); + wf_tys.extend(trait_m_sig.inputs_and_output); + let outlives_env = OutlivesEnvironment::with_bounds( + param_env, + ocx.infcx.implied_bounds_tys(param_env, impl_m.def_id.expect_local(), wf_tys.clone()), + ); + let errors = ocx.infcx.resolve_regions(&outlives_env); + if !errors.is_empty() { + return Err(report_mismatched_rpitit_signature( + tcx, + trait_m_sig, + trait_m.def_id, + impl_m.def_id, + None, + )); + } + Ok(()) } diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index 5a551d7c678a9..536bee9454596 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -1774,15 +1774,6 @@ impl<'tcx> ParamEnv<'tcx> { Self::new(List::empty(), self.reveal()) } - #[inline] - pub fn with_hidden_return_position_impl_trait_in_trait_tys(self) -> Self { - Self::new( - self.caller_bounds(), - Reveal::HideReturnPositionImplTraitInTrait, - self.constness(), - ) - } - /// Creates a suitable environment in which to perform trait /// queries on the given value. When type-checking, this is simply /// the pair of the environment plus value. But when reveal is set to diff --git a/tests/ui/impl-trait/in-trait/refine.rs b/tests/ui/impl-trait/in-trait/refine.rs index 5d2467a7a95c0..6f00576fdf97f 100644 --- a/tests/ui/impl-trait/in-trait/refine.rs +++ b/tests/ui/impl-trait/in-trait/refine.rs @@ -32,4 +32,16 @@ impl Late for D { //~^ ERROR impl method signature does not match trait method signature } +trait Captures<'a, 'b> {} +impl Captures<'_, '_> for T {} +trait Outlives { + fn bar<'a, 'b>() -> impl Captures<'a, 'b>; +} + +struct E; +impl Outlives for E { + fn bar<'a, 'b>() -> impl Captures<'a, 'b> + 'b {} + //~^ ERROR impl method signature does not match trait method signature +} + fn main() {} diff --git a/tests/ui/impl-trait/in-trait/refine.stderr b/tests/ui/impl-trait/in-trait/refine.stderr index a1f647851de57..4585237a3f905 100644 --- a/tests/ui/impl-trait/in-trait/refine.stderr +++ b/tests/ui/impl-trait/in-trait/refine.stderr @@ -58,5 +58,19 @@ help: replace the return type so that it matches the trait LL | fn bar(&self) -> impl Sized + '_ {} | ~~~~~~~~~~~~~~~ -error: aborting due to 4 previous errors +error: impl method signature does not match trait method signature + --> $DIR/refine.rs:43:25 + | +LL | fn bar<'a, 'b>() -> impl Captures<'a, 'b>; + | --------------------- return type from trait method defined here +... +LL | fn bar<'a, 'b>() -> impl Captures<'a, 'b> + 'b {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: replace the return type so that it matches the trait + | +LL | fn bar<'a, 'b>() -> impl Captures<'a, 'b> {} + | ~~~~~~~~~~~~~~~~~~~~~ + +error: aborting due to 5 previous errors From cbc99295ac0e8ed61c5562c41918e7168013ee43 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Tue, 8 Aug 2023 21:06:47 +0000 Subject: [PATCH 4/4] bless tests, fix pesky type error --- .../src/check/compare_impl_item.rs | 9 +-------- .../auxiliary/ret-pos-impl-trait-in-trait.rs | 3 ++- .../in-trait/bad-item-bound-within-rpitit.rs | 1 + .../in-trait/bad-item-bound-within-rpitit.stderr | 16 +++++++++++++++- .../impl-trait/in-trait/foreign.current.stderr | 11 ----------- tests/ui/impl-trait/in-trait/foreign.next.stderr | 11 ----------- 6 files changed, 19 insertions(+), 32 deletions(-) delete mode 100644 tests/ui/impl-trait/in-trait/foreign.current.stderr delete mode 100644 tests/ui/impl-trait/in-trait/foreign.next.stderr diff --git a/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs b/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs index fdd1de68a2ee1..85ce7ff9bc5a0 100644 --- a/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs +++ b/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs @@ -831,14 +831,7 @@ pub(super) fn collect_return_position_impl_trait_in_trait_tys<'tcx>( // of the `impl Sized`. Insert that here, so we don't ICE later. for assoc_item in tcx.associated_types_for_impl_traits_in_associated_fn(trait_m.def_id) { if !remapped_types.contains_key(assoc_item) { - remapped_types.insert( - *assoc_item, - ty::EarlyBinder::bind(Ty::new_error_with_message( - tcx, - return_span, - "missing synthetic item for RPITIT", - )), - ); + return Err(tcx.sess.delay_span_bug(return_span, "missing synthetic item for RPITIT")); } } diff --git a/tests/rustdoc/inline_cross/auxiliary/ret-pos-impl-trait-in-trait.rs b/tests/rustdoc/inline_cross/auxiliary/ret-pos-impl-trait-in-trait.rs index c72f011152d83..9a3c75b41336c 100644 --- a/tests/rustdoc/inline_cross/auxiliary/ret-pos-impl-trait-in-trait.rs +++ b/tests/rustdoc/inline_cross/auxiliary/ret-pos-impl-trait-in-trait.rs @@ -1,4 +1,4 @@ -#![feature(return_position_impl_trait_in_trait)] +#![feature(return_position_impl_trait_in_trait, refine)] pub trait Trait { fn create() -> impl Iterator { @@ -15,6 +15,7 @@ impl Trait for Basic { } impl Trait for Intermediate { + #[refine] fn create() -> std::ops::Range { // concrete return type 0..1 } diff --git a/tests/ui/impl-trait/in-trait/bad-item-bound-within-rpitit.rs b/tests/ui/impl-trait/in-trait/bad-item-bound-within-rpitit.rs index ff7ad4bf38944..d0d2192226c71 100644 --- a/tests/ui/impl-trait/in-trait/bad-item-bound-within-rpitit.rs +++ b/tests/ui/impl-trait/in-trait/bad-item-bound-within-rpitit.rs @@ -17,6 +17,7 @@ impl<'a, I: 'a + Iterable> Iterable for &'a I { //~^ ERROR impl has stricter requirements than trait fn iter(&self) -> impl 'a + Iterator> { + //~^ ERROR impl method signature does not match trait method signature (*self).iter() } } diff --git a/tests/ui/impl-trait/in-trait/bad-item-bound-within-rpitit.stderr b/tests/ui/impl-trait/in-trait/bad-item-bound-within-rpitit.stderr index 106b8a7c80474..51379aa4fa4ac 100644 --- a/tests/ui/impl-trait/in-trait/bad-item-bound-within-rpitit.stderr +++ b/tests/ui/impl-trait/in-trait/bad-item-bound-within-rpitit.stderr @@ -12,6 +12,20 @@ help: copy the `where` clause predicates from the trait LL | where Self: 'b; | ~~~~~~~~~~~~~~ -error: aborting due to previous error +error: impl method signature does not match trait method signature + --> $DIR/bad-item-bound-within-rpitit.rs:19:23 + | +LL | fn iter(&self) -> impl '_ + Iterator>; + | ----------------------------------------- return type from trait method defined here +... +LL | fn iter(&self) -> impl 'a + Iterator> { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: replace the return type so that it matches the trait + | +LL | fn iter(&self) -> impl Iterator::Item<'_>> + '_ { + | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +error: aborting due to 2 previous errors For more information about this error, try `rustc --explain E0276`. diff --git a/tests/ui/impl-trait/in-trait/foreign.current.stderr b/tests/ui/impl-trait/in-trait/foreign.current.stderr deleted file mode 100644 index 3f8ebabe58fd1..0000000000000 --- a/tests/ui/impl-trait/in-trait/foreign.current.stderr +++ /dev/null @@ -1,11 +0,0 @@ -warning: the feature `refine` is incomplete and may not be safe to use and/or cause compiler crashes - --> $DIR/foreign.rs:6:12 - | -LL | #![feature(refine)] - | ^^^^^^ - | - = note: see issue #1 for more information - = note: `#[warn(incomplete_features)]` on by default - -warning: 1 warning emitted - diff --git a/tests/ui/impl-trait/in-trait/foreign.next.stderr b/tests/ui/impl-trait/in-trait/foreign.next.stderr deleted file mode 100644 index 3f8ebabe58fd1..0000000000000 --- a/tests/ui/impl-trait/in-trait/foreign.next.stderr +++ /dev/null @@ -1,11 +0,0 @@ -warning: the feature `refine` is incomplete and may not be safe to use and/or cause compiler crashes - --> $DIR/foreign.rs:6:12 - | -LL | #![feature(refine)] - | ^^^^^^ - | - = note: see issue #1 for more information - = note: `#[warn(incomplete_features)]` on by default - -warning: 1 warning emitted -