Skip to content

Commit 0958c05

Browse files
committed
Auto merge of #124187 - compiler-errors:self-ctor, r=petrochenkov
Warn (or error) when `Self` ctor from outer item is referenced in inner nested item This implements a warning `SELF_CONSTRUCTOR_FROM_OUTER_ITEM` when a self constructor from an outer impl is referenced in an inner nested item. This is a proper fix mentioned #117246 (comment). This warning is additionally bumped to a hard error when the self type references generic parameters, since it's almost always going to ICE, and is basically *never* correct to do. This also reverts part of #117246, since I believe this is the proper fix and we shouldn't need the helper functions (`opt_param_at`/`opt_type_param`) any longer, since they shouldn't really ever be used in cases where we don't have this problem.
2 parents 697ac29 + a25bb5f commit 0958c05

File tree

13 files changed

+244
-176
lines changed

13 files changed

+244
-176
lines changed

compiler/rustc_hir_analysis/src/check/intrinsic.rs

+2-3
Original file line numberDiff line numberDiff line change
@@ -164,9 +164,8 @@ pub fn check_intrinsic_type(
164164
) {
165165
let generics = tcx.generics_of(intrinsic_id);
166166
let param = |n| {
167-
if let Some(&ty::GenericParamDef {
168-
name, kind: ty::GenericParamDefKind::Type { .. }, ..
169-
}) = generics.opt_param_at(n as usize, tcx)
167+
if let &ty::GenericParamDef { name, kind: ty::GenericParamDefKind::Type { .. }, .. } =
168+
generics.param_at(n as usize, tcx)
170169
{
171170
Ty::new_param(tcx, n, name)
172171
} else {

compiler/rustc_hir_typeck/messages.ftl

+4
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,10 @@ hir_typeck_rpit_change_return_type = you could change the return type to be a bo
137137
hir_typeck_rustcall_incorrect_args =
138138
functions with the "rust-call" ABI must take a single non-self tuple argument
139139
140+
hir_typeck_self_ctor_from_outer_item = can't reference `Self` constructor from outer item
141+
.label = the inner item doesn't inherit generics from this impl, so `Self` is invalid to reference
142+
.suggestion = replace `Self` with the actual type
143+
140144
hir_typeck_struct_expr_non_exhaustive =
141145
cannot create non-exhaustive {$what} using struct expression
142146

compiler/rustc_hir_typeck/src/errors.rs

+28
Original file line numberDiff line numberDiff line change
@@ -651,3 +651,31 @@ pub enum SuggestBoxingForReturnImplTrait {
651651
ends: Vec<Span>,
652652
},
653653
}
654+
655+
#[derive(Diagnostic)]
656+
#[diag(hir_typeck_self_ctor_from_outer_item, code = E0401)]
657+
pub struct SelfCtorFromOuterItem {
658+
#[primary_span]
659+
pub span: Span,
660+
#[label]
661+
pub impl_span: Span,
662+
#[subdiagnostic]
663+
pub sugg: Option<ReplaceWithName>,
664+
}
665+
666+
#[derive(LintDiagnostic)]
667+
#[diag(hir_typeck_self_ctor_from_outer_item)]
668+
pub struct SelfCtorFromOuterItemLint {
669+
#[label]
670+
pub impl_span: Span,
671+
#[subdiagnostic]
672+
pub sugg: Option<ReplaceWithName>,
673+
}
674+
675+
#[derive(Subdiagnostic)]
676+
#[suggestion(hir_typeck_suggestion, code = "{name}", applicability = "machine-applicable")]
677+
pub struct ReplaceWithName {
678+
#[primary_span]
679+
pub span: Span,
680+
pub name: String,
681+
}

compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs

+39-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use crate::callee::{self, DeferredCallResolution};
2-
use crate::errors::CtorIsPrivate;
2+
use crate::errors::{self, CtorIsPrivate};
33
use crate::method::{self, MethodCallee, SelfSource};
44
use crate::rvalue_scopes;
55
use crate::{BreakableCtxt, Diverges, Expectation, FnCtxt, LoweredTy};
@@ -21,6 +21,7 @@ use rustc_hir_analysis::hir_ty_lowering::{
2121
use rustc_infer::infer::canonical::{Canonical, OriginalQueryValues, QueryResponse};
2222
use rustc_infer::infer::error_reporting::TypeAnnotationNeeded::E0282;
2323
use rustc_infer::infer::{DefineOpaqueTypes, InferResult};
24+
use rustc_lint::builtin::SELF_CONSTRUCTOR_FROM_OUTER_ITEM;
2425
use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow, AutoBorrowMutability};
2526
use rustc_middle::ty::error::TypeError;
2627
use rustc_middle::ty::fold::TypeFoldable;
@@ -1156,6 +1157,43 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
11561157
span,
11571158
tcx.at(span).type_of(impl_def_id).instantiate_identity(),
11581159
);
1160+
1161+
// Firstly, check that this SelfCtor even comes from the item we're currently
1162+
// typechecking. This can happen because we never validated the resolution of
1163+
// SelfCtors, and when we started doing so, we noticed regressions. After
1164+
// sufficiently long time, we can remove this check and turn it into a hard
1165+
// error in `validate_res_from_ribs` -- it's just difficult to tell whether the
1166+
// self type has any generic types during rustc_resolve, which is what we use
1167+
// to determine if this is a hard error or warning.
1168+
if std::iter::successors(Some(self.body_id.to_def_id()), |def_id| {
1169+
self.tcx.generics_of(def_id).parent
1170+
})
1171+
.all(|def_id| def_id != impl_def_id)
1172+
{
1173+
let sugg = ty.normalized.ty_adt_def().map(|def| errors::ReplaceWithName {
1174+
span: path_span,
1175+
name: self.tcx.item_name(def.did()).to_ident_string(),
1176+
});
1177+
if ty.raw.has_param() {
1178+
let guar = self.tcx.dcx().emit_err(errors::SelfCtorFromOuterItem {
1179+
span: path_span,
1180+
impl_span: tcx.def_span(impl_def_id),
1181+
sugg,
1182+
});
1183+
return (Ty::new_error(self.tcx, guar), res);
1184+
} else {
1185+
self.tcx.emit_node_span_lint(
1186+
SELF_CONSTRUCTOR_FROM_OUTER_ITEM,
1187+
hir_id,
1188+
path_span,
1189+
errors::SelfCtorFromOuterItemLint {
1190+
impl_span: tcx.def_span(impl_def_id),
1191+
sugg,
1192+
},
1193+
);
1194+
}
1195+
}
1196+
11591197
match ty.normalized.ty_adt_def() {
11601198
Some(adt_def) if adt_def.has_ctor() => {
11611199
let (ctor_kind, ctor_def_id) = adt_def.non_enum_variant().ctor.unwrap();

compiler/rustc_infer/src/infer/error_reporting/note_and_explain.rs

+43-62
Original file line numberDiff line numberDiff line change
@@ -54,17 +54,13 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
5454
}
5555
(ty::Param(expected), ty::Param(found)) => {
5656
let generics = tcx.generics_of(body_owner_def_id);
57-
if let Some(param) = generics.opt_type_param(expected, tcx) {
58-
let e_span = tcx.def_span(param.def_id);
59-
if !sp.contains(e_span) {
60-
diag.span_label(e_span, "expected type parameter");
61-
}
57+
let e_span = tcx.def_span(generics.type_param(expected, tcx).def_id);
58+
if !sp.contains(e_span) {
59+
diag.span_label(e_span, "expected type parameter");
6260
}
63-
if let Some(param) = generics.opt_type_param(found, tcx) {
64-
let f_span = tcx.def_span(param.def_id);
65-
if !sp.contains(f_span) {
66-
diag.span_label(f_span, "found type parameter");
67-
}
61+
let f_span = tcx.def_span(generics.type_param(found, tcx).def_id);
62+
if !sp.contains(f_span) {
63+
diag.span_label(f_span, "found type parameter");
6864
}
6965
diag.note(
7066
"a type parameter was expected, but a different one was found; \
@@ -87,29 +83,22 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
8783
| (ty::Alias(ty::Projection, proj), ty::Param(p))
8884
if !tcx.is_impl_trait_in_trait(proj.def_id) =>
8985
{
90-
let parent = tcx
91-
.generics_of(body_owner_def_id)
92-
.opt_type_param(p, tcx)
93-
.and_then(|param| {
94-
let p_def_id = param.def_id;
95-
let p_span = tcx.def_span(p_def_id);
96-
let expected = match (values.expected.kind(), values.found.kind()) {
97-
(ty::Param(_), _) => "expected ",
98-
(_, ty::Param(_)) => "found ",
99-
_ => "",
100-
};
101-
if !sp.contains(p_span) {
102-
diag.span_label(
103-
p_span,
104-
format!("{expected}this type parameter"),
105-
);
106-
}
107-
p_def_id.as_local().and_then(|id| {
108-
let local_id = tcx.local_def_id_to_hir_id(id);
109-
let generics = tcx.parent_hir_node(local_id).generics()?;
110-
Some((id, generics))
111-
})
112-
});
86+
let param = tcx.generics_of(body_owner_def_id).type_param(p, tcx);
87+
let p_def_id = param.def_id;
88+
let p_span = tcx.def_span(p_def_id);
89+
let expected = match (values.expected.kind(), values.found.kind()) {
90+
(ty::Param(_), _) => "expected ",
91+
(_, ty::Param(_)) => "found ",
92+
_ => "",
93+
};
94+
if !sp.contains(p_span) {
95+
diag.span_label(p_span, format!("{expected}this type parameter"));
96+
}
97+
let parent = p_def_id.as_local().and_then(|id| {
98+
let local_id = tcx.local_def_id_to_hir_id(id);
99+
let generics = tcx.parent_hir_node(local_id).generics()?;
100+
Some((id, generics))
101+
});
113102
let mut note = true;
114103
if let Some((local_id, generics)) = parent {
115104
// Synthesize the associated type restriction `Add<Output = Expected>`.
@@ -183,16 +172,14 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
183172
(ty::Param(p), ty::Dynamic(..) | ty::Alias(ty::Opaque, ..))
184173
| (ty::Dynamic(..) | ty::Alias(ty::Opaque, ..), ty::Param(p)) => {
185174
let generics = tcx.generics_of(body_owner_def_id);
186-
if let Some(param) = generics.opt_type_param(p, tcx) {
187-
let p_span = tcx.def_span(param.def_id);
188-
let expected = match (values.expected.kind(), values.found.kind()) {
189-
(ty::Param(_), _) => "expected ",
190-
(_, ty::Param(_)) => "found ",
191-
_ => "",
192-
};
193-
if !sp.contains(p_span) {
194-
diag.span_label(p_span, format!("{expected}this type parameter"));
195-
}
175+
let p_span = tcx.def_span(generics.type_param(p, tcx).def_id);
176+
let expected = match (values.expected.kind(), values.found.kind()) {
177+
(ty::Param(_), _) => "expected ",
178+
(_, ty::Param(_)) => "found ",
179+
_ => "",
180+
};
181+
if !sp.contains(p_span) {
182+
diag.span_label(p_span, format!("{expected}this type parameter"));
196183
}
197184
diag.help("type parameters must be constrained to match other types");
198185
if tcx.sess.teach(diag.code.unwrap()) {
@@ -233,11 +220,9 @@ impl<T> Trait<T> for X {
233220
ty::Closure(..) | ty::CoroutineClosure(..) | ty::Coroutine(..),
234221
) => {
235222
let generics = tcx.generics_of(body_owner_def_id);
236-
if let Some(param) = generics.opt_type_param(p, tcx) {
237-
let p_span = tcx.def_span(param.def_id);
238-
if !sp.contains(p_span) {
239-
diag.span_label(p_span, "expected this type parameter");
240-
}
223+
let p_span = tcx.def_span(generics.type_param(p, tcx).def_id);
224+
if !sp.contains(p_span) {
225+
diag.span_label(p_span, "expected this type parameter");
241226
}
242227
diag.help(format!(
243228
"every closure has a distinct type and so could not always match the \
@@ -246,16 +231,14 @@ impl<T> Trait<T> for X {
246231
}
247232
(ty::Param(p), _) | (_, ty::Param(p)) => {
248233
let generics = tcx.generics_of(body_owner_def_id);
249-
if let Some(param) = generics.opt_type_param(p, tcx) {
250-
let p_span = tcx.def_span(param.def_id);
251-
let expected = match (values.expected.kind(), values.found.kind()) {
252-
(ty::Param(_), _) => "expected ",
253-
(_, ty::Param(_)) => "found ",
254-
_ => "",
255-
};
256-
if !sp.contains(p_span) {
257-
diag.span_label(p_span, format!("{expected}this type parameter"));
258-
}
234+
let p_span = tcx.def_span(generics.type_param(p, tcx).def_id);
235+
let expected = match (values.expected.kind(), values.found.kind()) {
236+
(ty::Param(_), _) => "expected ",
237+
(_, ty::Param(_)) => "found ",
238+
_ => "",
239+
};
240+
if !sp.contains(p_span) {
241+
diag.span_label(p_span, format!("{expected}this type parameter"));
259242
}
260243
}
261244
(ty::Alias(ty::Projection | ty::Inherent, proj_ty), _)
@@ -545,10 +528,8 @@ impl<T> Trait<T> for X {
545528
return false;
546529
};
547530
let generics = tcx.generics_of(body_owner_def_id);
548-
let Some(param) = generics.opt_type_param(param_ty, tcx) else {
549-
return false;
550-
};
551-
let Some(def_id) = param.def_id.as_local() else {
531+
let def_id = generics.type_param(param_ty, tcx).def_id;
532+
let Some(def_id) = def_id.as_local() else {
552533
return false;
553534
};
554535

compiler/rustc_lint_defs/src/builtin.rs

+42
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,7 @@ declare_lint_pass! {
9090
RUST_2021_PREFIXES_INCOMPATIBLE_SYNTAX,
9191
RUST_2021_PRELUDE_COLLISIONS,
9292
RUST_2024_INCOMPATIBLE_PAT,
93+
SELF_CONSTRUCTOR_FROM_OUTER_ITEM,
9394
SEMICOLON_IN_EXPRESSIONS_FROM_MACROS,
9495
SINGLE_USE_LIFETIMES,
9596
SOFT_UNSTABLE,
@@ -3149,6 +3150,47 @@ declare_lint! {
31493150
"detects `#[unstable]` on stable trait implementations for stable types"
31503151
}
31513152

3153+
declare_lint! {
3154+
/// The `self_constructor_from_outer_item` lint detects cases where the `Self` constructor
3155+
/// was silently allowed due to a bug in the resolver, and which may produce surprising
3156+
/// and unintended behavior.
3157+
///
3158+
/// Using a `Self` type alias from an outer item was never intended, but was silently allowed.
3159+
/// This is deprecated -- and is a hard error when the `Self` type alias references generics
3160+
/// that are not in scope.
3161+
///
3162+
/// ### Example
3163+
///
3164+
/// ```rust,compile_fail
3165+
/// #![deny(self_constructor_from_outer_item)]
3166+
///
3167+
/// struct S0(usize);
3168+
///
3169+
/// impl S0 {
3170+
/// fn foo() {
3171+
/// const C: S0 = Self(0);
3172+
/// fn bar() -> S0 {
3173+
/// Self(0)
3174+
/// }
3175+
/// }
3176+
/// }
3177+
/// ```
3178+
///
3179+
/// {{produces}}
3180+
///
3181+
/// ### Explanation
3182+
///
3183+
/// The `Self` type alias should not be reachable because nested items are not associated with
3184+
/// the scope of the parameters from the parent item.
3185+
pub SELF_CONSTRUCTOR_FROM_OUTER_ITEM,
3186+
Warn,
3187+
"detect unsupported use of `Self` from outer item",
3188+
@future_incompatible = FutureIncompatibleInfo {
3189+
reason: FutureIncompatibilityReason::FutureReleaseErrorDontReportInDeps,
3190+
reference: "issue #124186 <https://github.com/rust-lang/rust/issues/124186>",
3191+
};
3192+
}
3193+
31523194
declare_lint! {
31533195
/// The `semicolon_in_expressions_from_macros` lint detects trailing semicolons
31543196
/// in macro bodies when the macro is invoked in expression position.

compiler/rustc_middle/src/ty/generics.rs

-28
Original file line numberDiff line numberDiff line change
@@ -245,20 +245,6 @@ impl<'tcx> Generics {
245245
}
246246
}
247247

248-
/// Returns the `GenericParamDef` with the given index if available.
249-
pub fn opt_param_at(
250-
&'tcx self,
251-
param_index: usize,
252-
tcx: TyCtxt<'tcx>,
253-
) -> Option<&'tcx GenericParamDef> {
254-
if let Some(index) = param_index.checked_sub(self.parent_count) {
255-
self.own_params.get(index)
256-
} else {
257-
tcx.generics_of(self.parent.expect("parent_count > 0 but no parent?"))
258-
.opt_param_at(param_index, tcx)
259-
}
260-
}
261-
262248
pub fn params_to(&'tcx self, param_index: usize, tcx: TyCtxt<'tcx>) -> &'tcx [GenericParamDef] {
263249
if let Some(index) = param_index.checked_sub(self.parent_count) {
264250
&self.own_params[..index]
@@ -290,20 +276,6 @@ impl<'tcx> Generics {
290276
}
291277
}
292278

293-
/// Returns the `GenericParamDef` associated with this `ParamTy` if it belongs to this
294-
/// `Generics`.
295-
pub fn opt_type_param(
296-
&'tcx self,
297-
param: ParamTy,
298-
tcx: TyCtxt<'tcx>,
299-
) -> Option<&'tcx GenericParamDef> {
300-
let param = self.opt_param_at(param.index as usize, tcx)?;
301-
match param.kind {
302-
GenericParamDefKind::Type { .. } => Some(param),
303-
_ => None,
304-
}
305-
}
306-
307279
/// Returns the `GenericParamDef` associated with this `ParamConst`.
308280
pub fn const_param(&'tcx self, param: ParamConst, tcx: TyCtxt<'tcx>) -> &GenericParamDef {
309281
let param = self.param_at(param.index as usize, tcx);
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,12 @@
11
struct A<B>(B);
2-
impl<B>A<B>{fn d(){fn d(){Self(1)}}}
3-
//~^ ERROR the size for values of type `B` cannot be known at compilation time
4-
//~| ERROR the size for values of type `B` cannot be known at compilation time
5-
//~| ERROR mismatched types
6-
//~| ERROR mismatched types
7-
//~| ERROR `main` function not found in crate
2+
3+
impl<B> A<B> {
4+
fn d() {
5+
fn d() {
6+
Self(1)
7+
//~^ ERROR can't reference `Self` constructor from outer item
8+
}
9+
}
10+
}
11+
12+
fn main() {}

0 commit comments

Comments
 (0)