Skip to content

Commit 4c56815

Browse files
committed
Recurse into refs when checking impls of ConstParamTy on refs
1 parent 88189a7 commit 4c56815

File tree

8 files changed

+132
-1
lines changed

8 files changed

+132
-1
lines changed

compiler/rustc_hir_analysis/messages.ftl

+5
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,11 @@ hir_analysis_const_impl_for_non_const_trait =
7878
.note = marking a trait with `#[const_trait]` ensures all default method bodies are `const`
7979
.adding = adding a non-const method body in the future would be a breaking change
8080
81+
hir_analysis_const_param_ty_impl_on_infringing_referee =
82+
the trait `ConstParamTy` cannot be implemented for this type
83+
.label = the trait `ConstParamTy` is not implemented for this {$def_descr}
84+
.suggestion = consider annotating this {$def_descr} with `#[derive(ConstParamTy)]`
85+
8186
hir_analysis_const_param_ty_impl_on_non_adt =
8287
the trait `ConstParamTy` may not be implemented for this type
8388
.label = type is not a structure or enumeration

compiler/rustc_hir_analysis/src/check/wfcheck.rs

+4
Original file line numberDiff line numberDiff line change
@@ -987,6 +987,10 @@ fn check_param_wf(tcx: TyCtxt<'_>, param: &hir::GenericParam<'_>) -> Result<(),
987987

988988
ty_is_local(ty)
989989
}
990+
// Same as above.
991+
Err(ConstParamTyImplementationError::InfringingReferee(def_id)) => {
992+
def_id.is_local()
993+
}
990994
// Implments `ConstParamTy`, suggest adding the feature to enable.
991995
Ok(..) => true,
992996
};

compiler/rustc_hir_analysis/src/coherence/builtin.rs

+9
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,15 @@ fn visit_implementation_of_const_param_ty(tcx: TyCtxt<'_>, impl_did: LocalDefId)
118118
Err(ConstParamTyImplementationError::NotAnAdtOrBuiltinAllowed) => {
119119
tcx.dcx().emit_err(errors::ConstParamTyImplOnNonAdt { span });
120120
}
121+
Err(ConstParamTyImplementationError::InfringingReferee(def_id)) => {
122+
let def_span = tcx.def_span(def_id);
123+
tcx.dcx().emit_err(errors::ConstParamTyImplOnInfringingReferee {
124+
span,
125+
def_span,
126+
def_descr: tcx.def_descr(def_id),
127+
sugg: def_id.is_local().then_some(def_span.shrink_to_lo()),
128+
});
129+
}
121130
}
122131
}
123132

compiler/rustc_hir_analysis/src/errors.rs

+16
Original file line numberDiff line numberDiff line change
@@ -227,6 +227,22 @@ pub struct ConstParamTyImplOnNonAdt {
227227
pub span: Span,
228228
}
229229

230+
#[derive(Diagnostic)]
231+
#[diag(hir_analysis_const_param_ty_impl_on_infringing_referee)]
232+
pub struct ConstParamTyImplOnInfringingReferee {
233+
#[primary_span]
234+
pub span: Span,
235+
#[label]
236+
pub def_span: Span,
237+
pub def_descr: &'static str,
238+
#[suggestion(
239+
applicability = "maybe-incorrect",
240+
style = "verbose",
241+
code = "#[derive(ConstParamTy)]\n"
242+
)]
243+
pub sugg: Option<Span>,
244+
}
245+
230246
#[derive(Diagnostic)]
231247
#[diag(hir_analysis_trait_object_declared_with_no_traits, code = "E0224")]
232248
pub struct TraitObjectDeclaredWithNoTraits {

compiler/rustc_trait_selection/src/traits/misc.rs

+45
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ use crate::traits::{self, ObligationCause, ObligationCtxt};
55
use hir::LangItem;
66
use rustc_data_structures::fx::FxIndexSet;
77
use rustc_hir as hir;
8+
use rustc_hir::def_id::DefId;
89
use rustc_infer::infer::canonical::Canonical;
910
use rustc_infer::infer::{RegionResolutionError, TyCtxtInferExt};
1011
use rustc_infer::traits::query::NoSolution;
@@ -22,6 +23,7 @@ pub enum CopyImplementationError<'tcx> {
2223

2324
pub enum ConstParamTyImplementationError<'tcx> {
2425
InfrigingFields(Vec<(&'tcx ty::FieldDef, Ty<'tcx>, InfringingFieldsReason<'tcx>)>),
26+
InfringingReferee(DefId),
2527
NotAnAdtOrBuiltinAllowed,
2628
}
2729

@@ -92,6 +94,49 @@ pub fn type_allowed_to_implement_const_param_ty<'tcx>(
9294
parent_cause: ObligationCause<'tcx>,
9395
) -> Result<(), ConstParamTyImplementationError<'tcx>> {
9496
let (adt, args) = match self_type.kind() {
97+
// Special case for impls like `impl ConstParamTy for &Foo`, where
98+
// Foo doesn't `impl ConstParamTy`. These kinds of impls aren't caught
99+
// by coherence checking since `core`'s impl is restricted to &T where
100+
// T: ConstParamTy.
101+
// `has_concrete_skeleton()` avoids reporting `core`'s blanket impl.
102+
&ty::Ref(.., ty, hir::Mutability::Not) if ty.peel_refs().has_concrete_skeleton() => {
103+
let ty = ty.peel_refs();
104+
type_allowed_to_implement_const_param_ty(tcx, param_env, ty, parent_cause)?;
105+
// Check the ty behind the ref impls `ConstParamTy` itself. This additional
106+
// logic is needed when checking refs because we need to check not only if
107+
// all fields implement ConstParamTy, but also that the type itself implements
108+
// ConstParamTy. Simply recursing into the ref only checks the former.
109+
if !ty.references_error()
110+
&& let &ty::Adt(adt, _) = ty.kind()
111+
{
112+
let adt_did = adt.did();
113+
let adt_span = tcx.def_span(adt_did);
114+
let trait_did = tcx.require_lang_item(hir::LangItem::ConstParamTy, Some(adt_span));
115+
116+
let infcx = tcx.infer_ctxt().build();
117+
let ocx = traits::ObligationCtxt::new(&infcx);
118+
119+
let ty = ocx.normalize(&ObligationCause::dummy_with_span(adt_span), param_env, ty);
120+
let norm_errs = ocx.select_where_possible();
121+
if !norm_errs.is_empty() || ty.references_error() {
122+
return Ok(());
123+
}
124+
125+
ocx.register_bound(
126+
ObligationCause::dummy_with_span(adt_span),
127+
param_env,
128+
ty,
129+
trait_did,
130+
);
131+
let errs = ocx.select_all_or_error();
132+
if !errs.is_empty() {
133+
return Err(ConstParamTyImplementationError::InfringingReferee(adt_did));
134+
}
135+
}
136+
137+
return Ok(());
138+
}
139+
95140
// `core` provides these impls.
96141
ty::Uint(_)
97142
| ty::Int(_)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
#![feature(adt_const_params)]
2+
#![allow(incomplete_features)]
3+
4+
use std::marker::ConstParamTy;
5+
6+
// #112124
7+
8+
struct Foo;
9+
10+
impl ConstParamTy for &Foo {}
11+
//~^ ERROR the trait `ConstParamTy` cannot be implemented for this type
12+
13+
// #119299 (ICE)
14+
15+
#[derive(Eq, PartialEq)]
16+
struct Wrapper(*const i32, usize);
17+
18+
impl ConstParamTy for &Wrapper {}
19+
//~^ ERROR the trait `ConstParamTy` cannot be implemented for this type
20+
21+
const fn foo<const S: &'static Wrapper>() {}
22+
23+
fn main() {
24+
const FOO: Wrapper = Wrapper(&42 as *const i32, 42);
25+
foo::<{&FOO}>();
26+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
error: the trait `ConstParamTy` cannot be implemented for this type
2+
--> $DIR/const_param_ty_impl_bad_referee.rs:10:23
3+
|
4+
LL | struct Foo;
5+
| ---------- the trait `ConstParamTy` is not implemented for this struct
6+
LL |
7+
LL | impl ConstParamTy for &Foo {}
8+
| ^^^^
9+
|
10+
help: consider annotating this struct with `#[derive(ConstParamTy)]`
11+
|
12+
LL + #[derive(ConstParamTy)]
13+
LL | struct Foo;
14+
|
15+
16+
error[E0204]: the trait `ConstParamTy` cannot be implemented for this type
17+
--> $DIR/const_param_ty_impl_bad_referee.rs:18:23
18+
|
19+
LL | struct Wrapper(*const i32, usize);
20+
| ---------- this field does not implement `ConstParamTy`
21+
LL |
22+
LL | impl ConstParamTy for &Wrapper {}
23+
| ^^^^^^^^
24+
25+
error: aborting due to 2 previous errors
26+
27+
For more information about this error, try `rustc --explain E0204`.

tests/ui/const-generics/issues/issue-63322-forbid-dyn.min.stderr

-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ LL | fn test<const T: &'static dyn A>() {
55
| ^^^^^^^^^^^^^^
66
|
77
= note: the only supported types are integers, `bool` and `char`
8-
= help: add `#![feature(adt_const_params)]` to the crate attributes to enable more complex and user defined types
98

109
error: aborting due to 1 previous error
1110

0 commit comments

Comments
 (0)