Unnamed repository; edit this file 'description' to name the repository.
Diffstat (limited to 'crates/hir-ty/src/infer/expr.rs')
-rw-r--r--crates/hir-ty/src/infer/expr.rs347
1 files changed, 263 insertions, 84 deletions
diff --git a/crates/hir-ty/src/infer/expr.rs b/crates/hir-ty/src/infer/expr.rs
index e84a03a2e7..db8ab67d2b 100644
--- a/crates/hir-ty/src/infer/expr.rs
+++ b/crates/hir-ty/src/infer/expr.rs
@@ -4,11 +4,12 @@ use std::{iter::repeat_with, mem};
use either::Either;
use hir_def::{
- FieldId, GenericDefId, ItemContainerId, Lookup, TupleFieldId, TupleId,
+ AdtId, FieldId, GenericDefId, ItemContainerId, Lookup, TupleFieldId, TupleId, VariantId,
expr_store::path::{GenericArgs as HirGenericArgs, Path},
hir::{
Array, AsmOperand, AsmOptions, BinaryOp, BindingAnnotation, Expr, ExprId, ExprOrPatId,
- InlineAsmKind, LabelId, Literal, Pat, PatId, RecordSpread, Statement, UnaryOp,
+ InlineAsmKind, LabelId, Literal, Pat, PatId, RecordLitField, RecordSpread, Statement,
+ UnaryOp,
},
resolver::ValueNs,
signatures::VariantFields,
@@ -16,10 +17,12 @@ use hir_def::{
use hir_def::{FunctionId, hir::ClosureKind};
use hir_expand::name::Name;
use rustc_ast_ir::Mutability;
+use rustc_hash::FxHashMap;
use rustc_type_ir::{
InferTy, Interner,
inherent::{AdtDef, GenericArgs as _, IntoKind, Ty as _},
};
+use stdx::never;
use syntax::ast::RangeOp;
use tracing::debug;
@@ -60,17 +63,41 @@ impl<'db> InferenceContext<'_, 'db> {
) -> Ty<'db> {
let ty = self.infer_expr_inner(tgt_expr, expected, is_read);
if let Some(expected_ty) = expected.only_has_type(&mut self.table) {
- let could_unify = self.unify(ty, expected_ty);
- if !could_unify {
- self.result.type_mismatches.get_or_insert_default().insert(
- tgt_expr.into(),
- TypeMismatch { expected: expected_ty.store(), actual: ty.store() },
- );
- }
+ _ = self.demand_eqtype(tgt_expr.into(), expected_ty, ty);
}
ty
}
+ pub(crate) fn infer_expr_suptype_coerce_never(
+ &mut self,
+ expr: ExprId,
+ expected: &Expectation<'db>,
+ is_read: ExprIsRead,
+ ) -> Ty<'db> {
+ let ty = self.infer_expr_inner(expr, expected, is_read);
+ if ty.is_never() {
+ if let Some(adjustments) = self.result.expr_adjustments.get(&expr) {
+ return if let [Adjustment { kind: Adjust::NeverToAny, target }] = &**adjustments {
+ target.as_ref()
+ } else {
+ self.err_ty()
+ };
+ }
+
+ if let Some(target) = expected.only_has_type(&mut self.table) {
+ self.coerce(expr, ty, target, AllowTwoPhase::No, ExprIsRead::Yes)
+ .expect("never-to-any coercion should always succeed")
+ } else {
+ ty
+ }
+ } else {
+ if let Some(expected_ty) = expected.only_has_type(&mut self.table) {
+ _ = self.demand_suptype(expr.into(), expected_ty, ty);
+ }
+ ty
+ }
+ }
+
pub(crate) fn infer_expr_no_expect(
&mut self,
tgt_expr: ExprId,
@@ -283,13 +310,7 @@ impl<'db> InferenceContext<'_, 'db> {
}
} else {
if let Some(expected_ty) = expected.only_has_type(&mut self.table) {
- let could_unify = self.unify(ty, expected_ty);
- if !could_unify {
- self.result.type_mismatches.get_or_insert_default().insert(
- expr.into(),
- TypeMismatch { expected: expected_ty.store(), actual: ty.store() },
- );
- }
+ _ = self.demand_eqtype(expr.into(), ty, expected_ty);
}
ty
}
@@ -579,74 +600,7 @@ impl<'db> InferenceContext<'_, 'db> {
self.types.types.never
}
Expr::RecordLit { path, fields, spread, .. } => {
- let (ty, def_id) = self.resolve_variant(tgt_expr.into(), path, false);
-
- if let Some(t) = expected.only_has_type(&mut self.table) {
- self.unify(ty, t);
- }
-
- let substs = ty.as_adt().map(|(_, s)| s).unwrap_or(self.types.empty.generic_args);
- if let Some(variant) = def_id {
- self.write_variant_resolution(tgt_expr.into(), variant);
- }
- match def_id {
- _ if fields.is_empty() => {}
- Some(def) => {
- let field_types = self.db.field_types(def);
- let variant_data = def.fields(self.db);
- let visibilities = VariantFields::field_visibilities(self.db, def);
- for field in fields.iter() {
- let field_def = {
- match variant_data.field(&field.name) {
- Some(local_id) => {
- if !visibilities[local_id]
- .is_visible_from(self.db, self.resolver.module())
- {
- self.push_diagnostic(
- InferenceDiagnostic::NoSuchField {
- field: field.expr.into(),
- private: Some(local_id),
- variant: def,
- },
- );
- }
- Some(local_id)
- }
- None => {
- self.push_diagnostic(InferenceDiagnostic::NoSuchField {
- field: field.expr.into(),
- private: None,
- variant: def,
- });
- None
- }
- }
- };
- let field_ty = field_def.map_or(self.err_ty(), |it| {
- field_types[it].get().instantiate(self.interner(), &substs)
- });
-
- // Field type might have some unknown types
- // FIXME: we may want to emit a single type variable for all instance of type fields?
- let field_ty = self.insert_type_vars(field_ty);
- self.infer_expr_coerce(
- field.expr,
- &Expectation::has_type(field_ty),
- ExprIsRead::Yes,
- );
- }
- }
- None => {
- for field in fields.iter() {
- // Field projections don't constitute reads.
- self.infer_expr_coerce(field.expr, &Expectation::None, ExprIsRead::No);
- }
- }
- }
- if let RecordSpread::Expr(expr) = *spread {
- self.infer_expr_coerce_never(expr, &Expectation::has_type(ty), ExprIsRead::Yes);
- }
- ty
+ self.infer_record_expr(tgt_expr, expected, path, fields, *spread)
}
Expr::Field { expr, name } => self.infer_field_access(tgt_expr, *expr, name, expected),
Expr::Await { expr } => {
@@ -1019,6 +973,231 @@ impl<'db> InferenceContext<'_, 'db> {
ty
}
+ fn infer_record_expr(
+ &mut self,
+ expr: ExprId,
+ expected: &Expectation<'db>,
+ path: &Path,
+ fields: &[RecordLitField],
+ base_expr: RecordSpread,
+ ) -> Ty<'db> {
+ // Find the relevant variant
+ let (adt_ty, Some(variant)) = self.resolve_variant(expr.into(), path, false) else {
+ // FIXME: Emit an error.
+ for field in fields {
+ self.infer_expr_no_expect(field.expr, ExprIsRead::Yes);
+ }
+
+ return self.types.types.error;
+ };
+ self.write_variant_resolution(expr.into(), variant);
+
+ // Prohibit struct expressions when non-exhaustive flag is set.
+ if self.has_applicable_non_exhaustive(variant.into()) {
+ // FIXME: Emit an error.
+ }
+
+ self.check_record_expr_fields(adt_ty, expected, expr, variant, fields, base_expr);
+
+ self.require_type_is_sized(adt_ty);
+ adt_ty
+ }
+
+ fn check_record_expr_fields(
+ &mut self,
+ adt_ty: Ty<'db>,
+ expected: &Expectation<'db>,
+ expr: ExprId,
+ variant: VariantId,
+ hir_fields: &[RecordLitField],
+ base_expr: RecordSpread,
+ ) {
+ let interner = self.interner();
+
+ let adt_ty = self.table.try_structurally_resolve_type(adt_ty);
+ let adt_ty_hint = expected.only_has_type(&mut self.table).and_then(|expected| {
+ self.infcx()
+ .fudge_inference_if_ok(|| {
+ let mut ocx = ObligationCtxt::new(self.infcx());
+ ocx.sup(&ObligationCause::new(), self.table.param_env, expected, adt_ty)?;
+ if !ocx.try_evaluate_obligations().is_empty() {
+ return Err(TypeError::Mismatch);
+ }
+ Ok(self.resolve_vars_if_possible(adt_ty))
+ })
+ .ok()
+ });
+ if let Some(adt_ty_hint) = adt_ty_hint {
+ // re-link the variables that the fudging above can create.
+ _ = self.demand_eqtype(expr.into(), adt_ty_hint, adt_ty);
+ }
+
+ let TyKind::Adt(adt, args) = adt_ty.kind() else {
+ never!("non-ADT passed to check_struct_expr_fields");
+ return;
+ };
+ let adt_id = adt.def_id().0;
+
+ let variant_fields = variant.fields(self.db);
+ let variant_field_tys = self.db.field_types(variant);
+ let variant_field_vis = VariantFields::field_visibilities(self.db, variant);
+ let mut remaining_fields = variant_fields
+ .fields()
+ .iter()
+ .map(|(i, field)| (field.name.clone(), i))
+ .collect::<FxHashMap<_, _>>();
+
+ let mut seen_fields = FxHashMap::default();
+
+ // Type-check each field.
+ for field in hir_fields {
+ let name = &field.name;
+ let field_type = if let Some(i) = remaining_fields.remove(name) {
+ seen_fields.insert(name, i);
+
+ if !self.resolver.is_visible(self.db, variant_field_vis[i]) {
+ self.push_diagnostic(InferenceDiagnostic::NoSuchField {
+ field: field.expr.into(),
+ private: Some(i),
+ variant,
+ });
+ }
+
+ variant_field_tys[i].get().instantiate(interner, args)
+ } else {
+ if let Some(field_idx) = seen_fields.get(&name) {
+ // FIXME: Emit an error: duplicate field.
+ variant_field_tys[*field_idx].get().instantiate(interner, args)
+ } else {
+ self.push_diagnostic(InferenceDiagnostic::NoSuchField {
+ field: field.expr.into(),
+ private: None,
+ variant,
+ });
+ self.types.types.error
+ }
+ };
+
+ // Check that the expected field type is WF. Otherwise, we emit no use-site error
+ // in the case of coercions for non-WF fields, which leads to incorrect error
+ // tainting. See issue #126272.
+ self.table.register_wf_obligation(field_type.into(), ObligationCause::new());
+
+ // Make sure to give a type to the field even if there's
+ // an error, so we can continue type-checking.
+ self.infer_expr_coerce(field.expr, &Expectation::has_type(field_type), ExprIsRead::Yes);
+ }
+
+ // Make sure the programmer specified correct number of fields.
+ if matches!(adt_id, AdtId::UnionId(_)) && hir_fields.len() != 1 {
+ // FIXME: Emit an error: unions must specify exactly one field.
+ }
+
+ match base_expr {
+ RecordSpread::FieldDefaults => {
+ let mut missing_mandatory_fields = Vec::new();
+ let mut missing_optional_fields = Vec::new();
+ for (field_idx, field) in variant_fields.fields().iter() {
+ if remaining_fields.remove(&field.name).is_some() {
+ if field.default_value.is_none() {
+ missing_mandatory_fields.push(field_idx);
+ } else {
+ missing_optional_fields.push(field_idx);
+ }
+ }
+ }
+ if !missing_mandatory_fields.is_empty() {
+ // FIXME: Emit an error: missing fields.
+ }
+ }
+ RecordSpread::Expr(base_expr) => {
+ // FIXME: We are currently creating two branches here in order to maintain
+ // consistency. But they should be merged as much as possible.
+ if self.features.type_changing_struct_update {
+ if matches!(adt_id, AdtId::StructId(_)) {
+ // Make some fresh generic parameters for our ADT type.
+ let fresh_args = self.table.fresh_args_for_item(adt_id.into());
+ // We do subtyping on the FRU fields first, so we can
+ // learn exactly what types we expect the base expr
+ // needs constrained to be compatible with the struct
+ // type we expect from the expectation value.
+ for (field_idx, field) in variant_fields.fields().iter() {
+ let fru_ty = variant_field_tys[field_idx]
+ .get()
+ .instantiate(interner, fresh_args);
+ if remaining_fields.remove(&field.name).is_some() {
+ let target_ty =
+ variant_field_tys[field_idx].get().instantiate(interner, args);
+ let cause = ObligationCause::new();
+ match self.table.at(&cause).sup(target_ty, fru_ty) {
+ Ok(InferOk { obligations, value: () }) => {
+ self.table.register_predicates(obligations)
+ }
+ Err(_) => {
+ never!(
+ "subtyping remaining fields of type changing FRU \
+ failed: {target_ty:?} != {fru_ty:?}: {:?}",
+ field.name,
+ );
+ }
+ }
+ }
+ }
+ // The use of fresh args that we have subtyped against
+ // our base ADT type's fields allows us to guide inference
+ // along so that, e.g.
+ // ```
+ // MyStruct<'a, F1, F2, const C: usize> {
+ // f: F1,
+ // // Other fields that reference `'a`, `F2`, and `C`
+ // }
+ //
+ // let x = MyStruct {
+ // f: 1usize,
+ // ..other_struct
+ // };
+ // ```
+ // will have the `other_struct` expression constrained to
+ // `MyStruct<'a, _, F2, C>`, as opposed to just `_`...
+ // This is important to allow coercions to happen in
+ // `other_struct` itself. See `coerce-in-base-expr.rs`.
+ let fresh_base_ty = Ty::new_adt(self.interner(), adt_id, fresh_args);
+ self.infer_expr_suptype_coerce_never(
+ base_expr,
+ &Expectation::has_type(self.resolve_vars_if_possible(fresh_base_ty)),
+ ExprIsRead::Yes,
+ );
+ } else {
+ // Check the base_expr, regardless of a bad expected adt_ty, so we can get
+ // type errors on that expression, too.
+ self.infer_expr_no_expect(base_expr, ExprIsRead::Yes);
+ // FIXME: Emit an error: functional update syntax on non-struct.
+ }
+ } else {
+ self.infer_expr_suptype_coerce_never(
+ base_expr,
+ &Expectation::has_type(adt_ty),
+ ExprIsRead::Yes,
+ );
+ if !matches!(adt_id, AdtId::StructId(_)) {
+ // FIXME: Emit an error: functional update syntax on non-struct.
+ }
+ }
+ }
+ RecordSpread::None => {
+ if !matches!(adt_id, AdtId::UnionId(_))
+ && !remaining_fields.is_empty()
+ //~ non_exhaustive already reported, which will only happen for extern modules
+ && !self.has_applicable_non_exhaustive(adt_id.into())
+ {
+ debug!(?remaining_fields);
+
+ // FIXME: Emit an error: missing fields.
+ }
+ }
+ }
+ }
+
fn demand_scrutinee_type(
&mut self,
scrut: ExprId,