Skip to content

Commit d1077df

Browse files
committed
Type-check generic consts
1 parent cb0e8eb commit d1077df

File tree

7 files changed

+147
-39
lines changed

7 files changed

+147
-39
lines changed

compiler/rustc_hir_analysis/src/check/check.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -518,6 +518,9 @@ fn check_item_type(tcx: TyCtxt<'_>, id: hir::ItemId) {
518518
check_static_inhabited(tcx, id.owner_id.def_id);
519519
check_static_linkage(tcx, id.owner_id.def_id);
520520
}
521+
// FIXME(generic_consts): Do we want to enforce that type params are used? I don't think so.
522+
// Can unused params lead to unsoundness or unwanted behavior though?
523+
// FIXME(generic_consts): Do we correctly compute the variances of type params?
521524
DefKind::Const => {
522525
tcx.ensure().typeck(id.owner_id.def_id);
523526
}

compiler/rustc_hir_analysis/src/check/compare_impl_item.rs

Lines changed: 87 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ fn check_method_is_structurally_compatible<'tcx>(
7676
Ok(())
7777
}
7878

79-
/// This function is best explained by example. Consider a trait with it's implementation:
79+
/// This function is best explained by example. Consider a trait with its implementation:
8080
///
8181
/// ```rust
8282
/// trait Trait<'t, T> {
@@ -120,7 +120,7 @@ fn check_method_is_structurally_compatible<'tcx>(
120120
/// types:
121121
///
122122
/// ```rust,ignore (pseudo-Rust)
123-
/// <'b> fn(t: &'i0 U0, m: &'b) -> Foo
123+
/// <'b> fn(t: &'i0 U0, m: &'b N0) -> Foo
124124
/// ```
125125
///
126126
/// We now want to extract and substitute the type of the *trait*
@@ -137,7 +137,7 @@ fn check_method_is_structurally_compatible<'tcx>(
137137
/// Applying this to the trait method type yields:
138138
///
139139
/// ```rust,ignore (pseudo-Rust)
140-
/// <'a> fn(t: &'i0 U0, m: &'a) -> Foo
140+
/// <'a> fn(t: &'i0 U0, m: &'a N0) -> Foo
141141
/// ```
142142
///
143143
/// This type is also the same but the name of the bound region (`'a`
@@ -262,8 +262,6 @@ fn compare_method_predicate_entailment<'tcx>(
262262
// type.
263263

264264
// Compute placeholder form of impl and trait method tys.
265-
let tcx = infcx.tcx;
266-
267265
let mut wf_tys = FxIndexSet::default();
268266

269267
let unnormalized_impl_sig = infcx.instantiate_binder_with_fresh_vars(
@@ -1669,19 +1667,19 @@ fn compare_synthetic_generics<'tcx>(
16691667
/// ```rust,ignore (pseudo-Rust)
16701668
/// trait Foo {
16711669
/// fn foo<const N: u8>();
1672-
/// type bar<const N: u8>;
1670+
/// type Bar<const N: u8>;
16731671
/// fn baz<const N: u32>();
1674-
/// type blah<T>;
1672+
/// type Blah<T>;
16751673
/// }
16761674
///
16771675
/// impl Foo for () {
16781676
/// fn foo<const N: u64>() {}
16791677
/// //~^ error
1680-
/// type bar<const N: u64> {}
1678+
/// type Bar<const N: u64> = ();
16811679
/// //~^ error
16821680
/// fn baz<T>() {}
16831681
/// //~^ error
1684-
/// type blah<const N: i64> = u32;
1682+
/// type Blah<const N: i64> = u32;
16851683
/// //~^ error
16861684
/// }
16871685
/// ```
@@ -1770,35 +1768,52 @@ pub(super) fn compare_impl_const_raw(
17701768
let trait_const_item = tcx.associated_item(trait_const_item_def);
17711769
let impl_trait_ref =
17721770
tcx.impl_trait_ref(impl_const_item.container_id(tcx)).unwrap().subst_identity();
1773-
debug!("compare_const_impl(impl_trait_ref={:?})", impl_trait_ref);
17741771

1775-
let impl_c_span = tcx.def_span(impl_const_item_def.to_def_id());
1772+
debug!("compare_impl_const(impl_trait_ref={:?})", impl_trait_ref);
1773+
1774+
compare_number_of_generics(tcx, impl_const_item, trait_const_item, false)?;
1775+
compare_generic_param_kinds(tcx, impl_const_item, trait_const_item, false)?;
1776+
compare_const_predicate_entailment(tcx, impl_const_item, trait_const_item, impl_trait_ref)
1777+
}
1778+
1779+
/// The equivalent of [compare_method_predicate_entailment], but for associated constants
1780+
/// instead of associated functions.
1781+
// FIXME(generic_consts): If possible extract the common parts of `compare_{type,const}_predicate_entailment`.
1782+
// FIXME(fmease): This can probably be simplified by a lot.
1783+
fn compare_const_predicate_entailment<'tcx>(
1784+
tcx: TyCtxt<'tcx>,
1785+
impl_ct: ty::AssocItem,
1786+
trait_ct: ty::AssocItem,
1787+
impl_trait_ref: ty::TraitRef<'tcx>,
1788+
) -> Result<(), ErrorGuaranteed> {
1789+
let impl_ct_def_id = impl_ct.def_id.expect_local();
1790+
let impl_ct_span = tcx.def_span(impl_ct_def_id);
17761791

17771792
let infcx = tcx.infer_ctxt().build();
1778-
let param_env = tcx.param_env(impl_const_item_def.to_def_id());
1793+
let param_env = tcx.param_env(impl_ct.def_id);
17791794
let ocx = ObligationCtxt::new(&infcx);
17801795

17811796
// The below is for the most part highly similar to the procedure
17821797
// for methods above. It is simpler in many respects, especially
17831798
// because we shouldn't really have to deal with lifetimes or
17841799
// predicates. In fact some of this should probably be put into
17851800
// shared functions because of DRY violations...
1786-
let trait_to_impl_substs = impl_trait_ref.substs;
1801+
let impl_substs = InternalSubsts::identity_for_item(tcx, impl_ct.def_id);
1802+
let trait_to_impl_substs =
1803+
impl_substs.rebase_onto(tcx, impl_ct.container_id(tcx), impl_trait_ref.substs);
17871804

17881805
// Create a parameter environment that represents the implementation's
17891806
// method.
17901807
// Compute placeholder form of impl and trait const tys.
1791-
let impl_ty = tcx.type_of(impl_const_item_def.to_def_id()).subst_identity();
1792-
let trait_ty = tcx.type_of(trait_const_item_def).subst(tcx, trait_to_impl_substs);
1793-
let mut cause = ObligationCause::new(
1794-
impl_c_span,
1795-
impl_const_item_def,
1796-
ObligationCauseCode::CompareImplItemObligation {
1797-
impl_item_def_id: impl_const_item_def,
1798-
trait_item_def_id: trait_const_item_def,
1799-
kind: impl_const_item.kind,
1800-
},
1801-
);
1808+
let impl_ty = tcx.type_of(impl_ct_def_id).subst_identity();
1809+
1810+
let trait_ty = tcx.type_of(trait_ct.def_id).subst(tcx, trait_to_impl_substs);
1811+
let code = ObligationCauseCode::CompareImplItemObligation {
1812+
impl_item_def_id: impl_ct_def_id,
1813+
trait_item_def_id: trait_ct.def_id,
1814+
kind: impl_ct.kind,
1815+
};
1816+
let mut cause = ObligationCause::new(impl_ct_span, impl_ct_def_id, code.clone());
18021817

18031818
// There is no "body" here, so just pass dummy id.
18041819
let impl_ty = ocx.normalize(&cause, param_env, impl_ty);
@@ -1818,20 +1833,20 @@ pub(super) fn compare_impl_const_raw(
18181833
);
18191834

18201835
// Locate the Span containing just the type of the offending impl
1821-
let (ty, _) = tcx.hir().expect_impl_item(impl_const_item_def).expect_const();
1836+
let (ty, _) = tcx.hir().expect_impl_item(impl_ct_def_id).expect_const();
18221837
cause.span = ty.span;
18231838

18241839
let mut diag = struct_span_err!(
18251840
tcx.sess,
18261841
cause.span,
18271842
E0326,
18281843
"implemented const `{}` has an incompatible type for trait",
1829-
trait_const_item.name
1844+
trait_ct.name
18301845
);
18311846

1832-
let trait_c_span = trait_const_item_def.as_local().map(|trait_c_def_id| {
1847+
let trait_c_span = trait_ct.def_id.as_local().map(|trait_ct_def_id| {
18331848
// Add a label to the Span containing just the type of the const
1834-
let (ty, _) = tcx.hir().expect_trait_item(trait_c_def_id).expect_const();
1849+
let (ty, _) = tcx.hir().expect_trait_item(trait_ct_def_id).expect_const();
18351850
ty.span
18361851
});
18371852

@@ -1850,6 +1865,46 @@ pub(super) fn compare_impl_const_raw(
18501865
return Err(diag.emit());
18511866
};
18521867

1868+
let impl_ct_predicates = tcx.predicates_of(impl_ct.def_id);
1869+
let trait_ct_predicates = tcx.predicates_of(trait_ct.def_id);
1870+
1871+
// FIXME(generic_consts): Is this correct? We already have an ocx...
1872+
check_region_bounds_on_impl_item(tcx, impl_ct, trait_ct, false)?;
1873+
1874+
// FIXME(generic_consts): It'd be nice if we added a fast-path if the own bounds are empty
1875+
// similar to cmp_impl_ty_pred_entail without intro'ing unsoundness.
1876+
let impl_ct_own_bounds = impl_ct_predicates.instantiate_own(tcx, impl_substs);
1877+
1878+
// The predicates declared by the impl definition, the trait and the
1879+
// associated const in the trait are assumed.
1880+
let impl_predicates = tcx.predicates_of(impl_ct_predicates.parent.unwrap());
1881+
let mut hybrid_preds = impl_predicates.instantiate_identity(tcx);
1882+
hybrid_preds.predicates.extend(
1883+
trait_ct_predicates
1884+
.instantiate_own(tcx, trait_to_impl_substs)
1885+
.map(|(predicate, _)| predicate),
1886+
);
1887+
1888+
let hybrid_param_env = ty::ParamEnv::new(
1889+
tcx.mk_clauses(&hybrid_preds.predicates),
1890+
Reveal::UserFacing,
1891+
hir::Constness::Const,
1892+
);
1893+
let hybrid_param_env = traits::normalize_param_env_or_error(
1894+
tcx,
1895+
hybrid_param_env,
1896+
ObligationCause::misc(impl_ct_span, impl_ct_def_id),
1897+
);
1898+
1899+
// FIXME(generic_consts): Is this correct? We already have an ocx...
1900+
for (predicate, span) in impl_ct_own_bounds {
1901+
let cause = ObligationCause::misc(span, impl_ct_def_id);
1902+
let predicate = ocx.normalize(&cause, hybrid_param_env, predicate);
1903+
1904+
let cause = ObligationCause::new(span, impl_ct_def_id, code.clone());
1905+
ocx.register_obligation(traits::Obligation::new(tcx, cause, hybrid_param_env, predicate));
1906+
}
1907+
18531908
// Check that all obligations are satisfied by the implementation's
18541909
// version.
18551910
let errors = ocx.select_all_or_error();
@@ -1858,7 +1913,7 @@ pub(super) fn compare_impl_const_raw(
18581913
}
18591914

18601915
let outlives_env = OutlivesEnvironment::new(param_env);
1861-
ocx.resolve_regions_and_report_errors(impl_const_item_def, &outlives_env)
1916+
ocx.resolve_regions_and_report_errors(impl_ct_def_id, &outlives_env)
18621917
}
18631918

18641919
pub(super) fn compare_impl_ty<'tcx>(
@@ -1900,7 +1955,7 @@ fn compare_type_predicate_entailment<'tcx>(
19001955
return Ok(());
19011956
}
19021957

1903-
// This `HirId` should be used for the `body_id` field on each
1958+
// This `DefId` should be used for the `body_id` field on each
19041959
// `ObligationCause` (and the `FnCtxt`). This is what
19051960
// `regionck_item` expects.
19061961
let impl_ty_def_id = impl_ty.def_id.expect_local();
@@ -1919,7 +1974,7 @@ fn compare_type_predicate_entailment<'tcx>(
19191974
debug!("compare_type_predicate_entailment: bounds={:?}", hybrid_preds);
19201975

19211976
let impl_ty_span = tcx.def_span(impl_ty_def_id);
1922-
let normalize_cause = traits::ObligationCause::misc(impl_ty_span, impl_ty_def_id);
1977+
let normalize_cause = ObligationCause::misc(impl_ty_span, impl_ty_def_id);
19231978
let param_env = ty::ParamEnv::new(
19241979
tcx.mk_clauses(&hybrid_preds.predicates),
19251980
Reveal::UserFacing,
@@ -1968,7 +2023,7 @@ fn compare_type_predicate_entailment<'tcx>(
19682023
///
19692024
/// trait X { type Y: Copy } impl X for T { type Y = S; }
19702025
///
1971-
/// We are able to normalize `<T as X>::U` to `S`, and so when we check the
2026+
/// We are able to normalize `<T as X>::Y` to `S`, and so when we check the
19722027
/// impl is well-formed we have to prove `S: Copy`.
19732028
///
19742029
/// For default associated types the normalization is not possible (the value

compiler/rustc_hir_analysis/src/check/wfcheck.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -300,6 +300,7 @@ fn check_trait_item(tcx: TyCtxt<'_>, trait_item: &hir::TraitItem<'_>) {
300300
/// fn into_iter<'a>(&'a self) -> Self::Iter<'a>;
301301
/// }
302302
/// ```
303+
// FIXME(generic_consts): This is not relevant to generic consts at all, right?
303304
fn check_gat_where_clauses(tcx: TyCtxt<'_>, associated_items: &[hir::TraitItemRef]) {
304305
// Associates every GAT's def_id to a list of possibly missing bounds detected by this lint.
305306
let mut required_bounds_by_item = FxHashMap::default();

compiler/rustc_hir_analysis/src/collect.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -256,6 +256,7 @@ fn reject_placeholder_type_signatures_in_item<'tcx>(
256256
hir::ItemKind::OpaqueTy(hir::OpaqueTy { generics, .. })
257257
| hir::ItemKind::TyAlias(_, generics) => (generics, false),
258258
// `static`, `fn` and `const` are handled elsewhere to suggest appropriate type.
259+
// FIXME(generic_consts): Does "elsewhere" ^^^^^^^ handle `generic_consts` properly?
259260
_ => return,
260261
};
261262

compiler/rustc_hir_analysis/src/collect/generics_of.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -209,6 +209,7 @@ pub(super) fn generics_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Generics {
209209
| ItemKind::Struct(..)
210210
| ItemKind::OpaqueTy(..)
211211
| ItemKind::Union(..) => (None, Defaults::Allowed),
212+
ItemKind::Const(..) => (None, Defaults::Deny),
212213
_ => (None, Defaults::FutureCompatDisallowed),
213214
}
214215
}

compiler/rustc_hir_analysis/src/collect/predicates_of.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,7 @@ fn gather_explicit_predicates_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Gen
153153
}
154154
ItemKind::Fn(.., generics, _)
155155
| ItemKind::TyAlias(_, generics)
156+
| ItemKind::Const(_, generics, _)
156157
| ItemKind::Enum(_, generics)
157158
| ItemKind::Struct(_, generics)
158159
| ItemKind::Union(_, generics) => generics,
@@ -757,6 +758,7 @@ pub(super) fn type_param_predicates(
757758
ItemKind::Fn(.., generics, _)
758759
| ItemKind::Impl(&hir::Impl { generics, .. })
759760
| ItemKind::TyAlias(_, generics)
761+
| ItemKind::Const(_, generics, _)
760762
| ItemKind::OpaqueTy(&OpaqueTy {
761763
generics,
762764
origin: hir::OpaqueTyOrigin::TyAlias { .. },

compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs

Lines changed: 52 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -530,12 +530,33 @@ impl<'a, 'tcx> Visitor<'tcx> for BoundVarContext<'a, 'tcx> {
530530
// These sorts of items have no lifetime parameters at all.
531531
intravisit::walk_item(self, item);
532532
}
533-
hir::ItemKind::Static(..) | hir::ItemKind::Const(..) => {
533+
hir::ItemKind::Static(..) => {
534534
// No lifetime parameters, but implied 'static.
535535
self.with(Scope::Elision { s: self.scope }, |this| {
536536
intravisit::walk_item(this, item)
537537
});
538538
}
539+
// FIXME(generic_consts): Somehow merge this branch with the `TypeAlias` one below?
540+
hir::ItemKind::Const(_, generics, _) => {
541+
self.with(Scope::Elision { s: self.scope }, |this| {
542+
// These kinds of items have only early-bound lifetime parameters.
543+
let bound_vars = generics.params.iter().map(ResolvedArg::early).collect();
544+
this.record_late_bound_vars(item.hir_id(), vec![]);
545+
let scope = Scope::Binder {
546+
hir_id: item.hir_id(),
547+
bound_vars,
548+
scope_type: BinderScopeType::Normal,
549+
s: this.scope,
550+
where_bound_origin: None,
551+
};
552+
this.with(scope, |this| {
553+
let scope = Scope::TraitRefBoundary { s: this.scope };
554+
this.with(scope, |this| {
555+
intravisit::walk_item(this, item);
556+
});
557+
});
558+
});
559+
}
539560
hir::ItemKind::OpaqueTy(hir::OpaqueTy {
540561
origin: hir::OpaqueTyOrigin::TyAlias { .. },
541562
..
@@ -829,10 +850,22 @@ impl<'a, 'tcx> Visitor<'tcx> for BoundVarContext<'a, 'tcx> {
829850
})
830851
});
831852
}
853+
// FIXME(generic_consts): Somehow deduplicate with the branch above?
832854
Const(_, _) => {
833-
// Only methods and types support generics.
834-
assert!(trait_item.generics.params.is_empty());
835-
intravisit::walk_trait_item(self, trait_item);
855+
let bound_vars =
856+
(&trait_item.generics).params.iter().map(ResolvedArg::early).collect();
857+
self.record_late_bound_vars(trait_item.hir_id(), vec![]);
858+
let scope = Scope::Binder {
859+
hir_id: trait_item.hir_id(),
860+
bound_vars,
861+
s: self.scope,
862+
scope_type: BinderScopeType::Normal,
863+
where_bound_origin: None,
864+
};
865+
self.with(scope, |this| {
866+
let scope = Scope::TraitRefBoundary { s: this.scope };
867+
this.with(scope, |this| intravisit::walk_trait_item(this, trait_item))
868+
});
836869
}
837870
}
838871
}
@@ -864,10 +897,22 @@ impl<'a, 'tcx> Visitor<'tcx> for BoundVarContext<'a, 'tcx> {
864897
})
865898
});
866899
}
900+
// FIXME(generic_consts): Somehow deduplicate with the branch above?
867901
Const(_, _) => {
868-
// Only methods and types support generics.
869-
assert!(impl_item.generics.params.is_empty());
870-
intravisit::walk_impl_item(self, impl_item);
902+
let bound_vars: FxIndexMap<LocalDefId, ResolvedArg> =
903+
(&impl_item.generics).params.iter().map(ResolvedArg::early).collect();
904+
self.record_late_bound_vars(impl_item.hir_id(), vec![]);
905+
let scope = Scope::Binder {
906+
hir_id: impl_item.hir_id(),
907+
bound_vars,
908+
s: self.scope,
909+
scope_type: BinderScopeType::Normal,
910+
where_bound_origin: None,
911+
};
912+
self.with(scope, |this| {
913+
let scope = Scope::TraitRefBoundary { s: this.scope };
914+
this.with(scope, |this| intravisit::walk_impl_item(this, impl_item))
915+
});
871916
}
872917
}
873918
}

0 commit comments

Comments
 (0)