Auto merge of #14368 - HKalbasi:mir, r=HKalbasi

MIR episode 3

This PR adds lowering for try operator and overloaded dereference, and adds evaluating support for function pointers and trait objects. It also adds a flag to `analysis-stats` to show percentage of functions that it fails to emit mir for them, which is currently `20%` (which is somehow lying, as most of the supported `80%` are tests). The most offenders are closure (1975 items) and overloaded index (415 items). I will try to add overloaded index before monday to have it in this PR, and tackle the closure in the next episode.
This commit is contained in:
bors 2023-03-21 19:57:27 +00:00
commit 3321799e8f
52 changed files with 3100 additions and 937 deletions

View file

@ -391,7 +391,7 @@ impl Body {
} }
}; };
let expander = Expander::new(db, file_id, module); let expander = Expander::new(db, file_id, module);
let (mut body, source_map) = Body::new(db, expander, params, body); let (mut body, source_map) = Body::new(db, expander, params, body, module.krate);
body.shrink_to_fit(); body.shrink_to_fit();
(Arc::new(body), Arc::new(source_map)) (Arc::new(body), Arc::new(source_map))
@ -420,8 +420,9 @@ impl Body {
expander: Expander, expander: Expander,
params: Option<(ast::ParamList, impl Iterator<Item = bool>)>, params: Option<(ast::ParamList, impl Iterator<Item = bool>)>,
body: Option<ast::Expr>, body: Option<ast::Expr>,
krate: CrateId,
) -> (Body, BodySourceMap) { ) -> (Body, BodySourceMap) {
lower::lower(db, expander, params, body) lower::lower(db, expander, params, body, krate)
} }
fn shrink_to_fit(&mut self) { fn shrink_to_fit(&mut self) {

View file

@ -3,6 +3,7 @@
use std::{mem, sync::Arc}; use std::{mem, sync::Arc};
use base_db::CrateId;
use either::Either; use either::Either;
use hir_expand::{ use hir_expand::{
ast_id_map::AstIdMap, ast_id_map::AstIdMap,
@ -18,7 +19,7 @@ use rustc_hash::FxHashMap;
use smallvec::SmallVec; use smallvec::SmallVec;
use syntax::{ use syntax::{
ast::{ ast::{
self, ArrayExprKind, AstChildren, HasArgList, HasLoopBody, HasName, LiteralKind, self, ArrayExprKind, AstChildren, BlockExpr, HasArgList, HasLoopBody, HasName, LiteralKind,
SlicePatComponents, SlicePatComponents,
}, },
AstNode, AstPtr, SyntaxNodePtr, AstNode, AstPtr, SyntaxNodePtr,
@ -36,6 +37,7 @@ use crate::{
RecordFieldPat, RecordLitField, Statement, RecordFieldPat, RecordLitField, Statement,
}, },
item_scope::BuiltinShadowMode, item_scope::BuiltinShadowMode,
lang_item::LangItem,
path::{GenericArgs, Path}, path::{GenericArgs, Path},
type_ref::{Mutability, Rawness, TypeRef}, type_ref::{Mutability, Rawness, TypeRef},
AdtId, BlockId, BlockLoc, ModuleDefId, UnresolvedMacro, AdtId, BlockId, BlockLoc, ModuleDefId, UnresolvedMacro,
@ -80,9 +82,11 @@ pub(super) fn lower(
expander: Expander, expander: Expander,
params: Option<(ast::ParamList, impl Iterator<Item = bool>)>, params: Option<(ast::ParamList, impl Iterator<Item = bool>)>,
body: Option<ast::Expr>, body: Option<ast::Expr>,
krate: CrateId,
) -> (Body, BodySourceMap) { ) -> (Body, BodySourceMap) {
ExprCollector { ExprCollector {
db, db,
krate,
source_map: BodySourceMap::default(), source_map: BodySourceMap::default(),
ast_id_map: db.ast_id_map(expander.current_file_id), ast_id_map: db.ast_id_map(expander.current_file_id),
body: Body { body: Body {
@ -96,6 +100,7 @@ pub(super) fn lower(
_c: Count::new(), _c: Count::new(),
}, },
expander, expander,
current_try_block: None,
is_lowering_assignee_expr: false, is_lowering_assignee_expr: false,
is_lowering_generator: false, is_lowering_generator: false,
} }
@ -107,7 +112,9 @@ struct ExprCollector<'a> {
expander: Expander, expander: Expander,
ast_id_map: Arc<AstIdMap>, ast_id_map: Arc<AstIdMap>,
body: Body, body: Body,
krate: CrateId,
source_map: BodySourceMap, source_map: BodySourceMap,
current_try_block: Option<LabelId>,
is_lowering_assignee_expr: bool, is_lowering_assignee_expr: bool,
is_lowering_generator: bool, is_lowering_generator: bool,
} }
@ -176,8 +183,7 @@ impl ExprCollector<'_> {
self.source_map.expr_map.insert(src, id); self.source_map.expr_map.insert(src, id);
id id
} }
// desugared exprs don't have ptr, that's wrong and should be fixed // FIXME: desugared exprs don't have ptr, that's wrong and should be fixed somehow.
// somehow.
fn alloc_expr_desugared(&mut self, expr: Expr) -> ExprId { fn alloc_expr_desugared(&mut self, expr: Expr) -> ExprId {
self.body.exprs.alloc(expr) self.body.exprs.alloc(expr)
} }
@ -199,6 +205,10 @@ impl ExprCollector<'_> {
self.source_map.pat_map.insert(src, id); self.source_map.pat_map.insert(src, id);
id id
} }
// FIXME: desugared pats don't have ptr, that's wrong and should be fixed somehow.
fn alloc_pat_desugared(&mut self, pat: Pat) -> PatId {
self.body.pats.alloc(pat)
}
fn missing_pat(&mut self) -> PatId { fn missing_pat(&mut self) -> PatId {
self.body.pats.alloc(Pat::Missing) self.body.pats.alloc(Pat::Missing)
} }
@ -214,6 +224,10 @@ impl ExprCollector<'_> {
self.source_map.label_map.insert(src, id); self.source_map.label_map.insert(src, id);
id id
} }
// FIXME: desugared labels don't have ptr, that's wrong and should be fixed somehow.
fn alloc_label_desugared(&mut self, label: Label) -> LabelId {
self.body.labels.alloc(label)
}
fn make_label(&mut self, label: Label, src: LabelSource) -> LabelId { fn make_label(&mut self, label: Label, src: LabelSource) -> LabelId {
let id = self.body.labels.alloc(label); let id = self.body.labels.alloc(label);
self.source_map.label_map_back.insert(id, src); self.source_map.label_map_back.insert(id, src);
@ -251,13 +265,7 @@ impl ExprCollector<'_> {
self.alloc_expr(Expr::Let { pat, expr }, syntax_ptr) self.alloc_expr(Expr::Let { pat, expr }, syntax_ptr)
} }
ast::Expr::BlockExpr(e) => match e.modifier() { ast::Expr::BlockExpr(e) => match e.modifier() {
Some(ast::BlockModifier::Try(_)) => { Some(ast::BlockModifier::Try(_)) => self.collect_try_block(e),
self.collect_block_(e, |id, statements, tail| Expr::TryBlock {
id,
statements,
tail,
})
}
Some(ast::BlockModifier::Unsafe(_)) => { Some(ast::BlockModifier::Unsafe(_)) => {
self.collect_block_(e, |id, statements, tail| Expr::Unsafe { self.collect_block_(e, |id, statements, tail| Expr::Unsafe {
id, id,
@ -437,10 +445,7 @@ impl ExprCollector<'_> {
let expr = self.collect_expr_opt(e.expr()); let expr = self.collect_expr_opt(e.expr());
self.alloc_expr(Expr::Await { expr }, syntax_ptr) self.alloc_expr(Expr::Await { expr }, syntax_ptr)
} }
ast::Expr::TryExpr(e) => { ast::Expr::TryExpr(e) => self.collect_try_operator(syntax_ptr, e),
let expr = self.collect_expr_opt(e.expr());
self.alloc_expr(Expr::Try { expr }, syntax_ptr)
}
ast::Expr::CastExpr(e) => { ast::Expr::CastExpr(e) => {
let expr = self.collect_expr_opt(e.expr()); let expr = self.collect_expr_opt(e.expr());
let type_ref = Interned::new(TypeRef::from_ast_opt(&self.ctx(), e.ty())); let type_ref = Interned::new(TypeRef::from_ast_opt(&self.ctx(), e.ty()));
@ -601,6 +606,126 @@ impl ExprCollector<'_> {
}) })
} }
/// Desugar `try { <stmts>; <expr> }` into `'<new_label>: { <stmts>; ::std::ops::Try::from_output(<expr>) }`,
/// `try { <stmts>; }` into `'<new_label>: { <stmts>; ::std::ops::Try::from_output(()) }`
/// and save the `<new_label>` to use it as a break target for desugaring of the `?` operator.
fn collect_try_block(&mut self, e: BlockExpr) -> ExprId {
let Some(try_from_output) = LangItem::TryTraitFromOutput.path(self.db, self.krate) else {
return self.alloc_expr_desugared(Expr::Missing);
};
let prev_try_block = self.current_try_block.take();
self.current_try_block =
Some(self.alloc_label_desugared(Label { name: Name::generate_new_name() }));
let expr_id = self.collect_block(e);
let callee = self.alloc_expr_desugared(Expr::Path(try_from_output));
let Expr::Block { label, tail, .. } = &mut self.body.exprs[expr_id] else {
unreachable!("It is the output of collect block");
};
*label = self.current_try_block;
let next_tail = match *tail {
Some(tail) => self.alloc_expr_desugared(Expr::Call {
callee,
args: Box::new([tail]),
is_assignee_expr: false,
}),
None => {
let unit = self.alloc_expr_desugared(Expr::Tuple {
exprs: Box::new([]),
is_assignee_expr: false,
});
self.alloc_expr_desugared(Expr::Call {
callee,
args: Box::new([unit]),
is_assignee_expr: false,
})
}
};
let Expr::Block { tail, .. } = &mut self.body.exprs[expr_id] else {
unreachable!("It is the output of collect block");
};
*tail = Some(next_tail);
self.current_try_block = prev_try_block;
expr_id
}
/// Desugar `ast::TryExpr` from: `<expr>?` into:
/// ```ignore (pseudo-rust)
/// match Try::branch(<expr>) {
/// ControlFlow::Continue(val) => val,
/// ControlFlow::Break(residual) =>
/// // If there is an enclosing `try {...}`:
/// break 'catch_target Try::from_residual(residual),
/// // Otherwise:
/// return Try::from_residual(residual),
/// }
/// ```
fn collect_try_operator(&mut self, syntax_ptr: AstPtr<ast::Expr>, e: ast::TryExpr) -> ExprId {
let (try_branch, cf_continue, cf_break, try_from_residual) = 'if_chain: {
if let Some(try_branch) = LangItem::TryTraitBranch.path(self.db, self.krate) {
if let Some(cf_continue) = LangItem::ControlFlowContinue.path(self.db, self.krate) {
if let Some(cf_break) = LangItem::ControlFlowBreak.path(self.db, self.krate) {
if let Some(try_from_residual) =
LangItem::TryTraitFromResidual.path(self.db, self.krate)
{
break 'if_chain (try_branch, cf_continue, cf_break, try_from_residual);
}
}
}
}
// Some of the needed lang items are missing, so we can't desugar
return self.alloc_expr(Expr::Missing, syntax_ptr);
};
let operand = self.collect_expr_opt(e.expr());
let try_branch = self.alloc_expr(Expr::Path(try_branch), syntax_ptr.clone());
let expr = self.alloc_expr(
Expr::Call { callee: try_branch, args: Box::new([operand]), is_assignee_expr: false },
syntax_ptr.clone(),
);
let continue_name = Name::generate_new_name();
let continue_binding =
self.alloc_binding(continue_name.clone(), BindingAnnotation::Unannotated);
let continue_bpat =
self.alloc_pat_desugared(Pat::Bind { id: continue_binding, subpat: None });
self.add_definition_to_binding(continue_binding, continue_bpat);
let continue_arm = MatchArm {
pat: self.alloc_pat_desugared(Pat::TupleStruct {
path: Some(Box::new(cf_continue)),
args: Box::new([continue_bpat]),
ellipsis: None,
}),
guard: None,
expr: self.alloc_expr(Expr::Path(Path::from(continue_name)), syntax_ptr.clone()),
};
let break_name = Name::generate_new_name();
let break_binding = self.alloc_binding(break_name.clone(), BindingAnnotation::Unannotated);
let break_bpat = self.alloc_pat_desugared(Pat::Bind { id: break_binding, subpat: None });
self.add_definition_to_binding(break_binding, break_bpat);
let break_arm = MatchArm {
pat: self.alloc_pat_desugared(Pat::TupleStruct {
path: Some(Box::new(cf_break)),
args: Box::new([break_bpat]),
ellipsis: None,
}),
guard: None,
expr: {
let x = self.alloc_expr(Expr::Path(Path::from(break_name)), syntax_ptr.clone());
let callee = self.alloc_expr(Expr::Path(try_from_residual), syntax_ptr.clone());
let result = self.alloc_expr(
Expr::Call { callee, args: Box::new([x]), is_assignee_expr: false },
syntax_ptr.clone(),
);
if let Some(label) = self.current_try_block {
let label = Some(self.body.labels[label].name.clone());
self.alloc_expr(Expr::Break { expr: Some(result), label }, syntax_ptr.clone())
} else {
self.alloc_expr(Expr::Return { expr: Some(result) }, syntax_ptr.clone())
}
},
};
let arms = Box::new([continue_arm, break_arm]);
self.alloc_expr(Expr::Match { expr, arms }, syntax_ptr)
}
fn collect_macro_call<F, T, U>( fn collect_macro_call<F, T, U>(
&mut self, &mut self,
mcall: ast::MacroCall, mcall: ast::MacroCall,
@ -949,16 +1074,24 @@ impl ExprCollector<'_> {
.collect(), .collect(),
} }
} }
ast::Pat::LiteralPat(lit) => { // FIXME: rustfmt removes this label if it is a block and not a loop
if let Some(ast_lit) = lit.literal() { ast::Pat::LiteralPat(lit) => 'b: loop {
let expr = Expr::Literal(ast_lit.kind().into()); break if let Some(ast_lit) = lit.literal() {
let mut hir_lit: Literal = ast_lit.kind().into();
if lit.minus_token().is_some() {
let Some(h) = hir_lit.negate() else {
break 'b Pat::Missing;
};
hir_lit = h;
}
let expr = Expr::Literal(hir_lit);
let expr_ptr = AstPtr::new(&ast::Expr::Literal(ast_lit)); let expr_ptr = AstPtr::new(&ast::Expr::Literal(ast_lit));
let expr_id = self.alloc_expr(expr, expr_ptr); let expr_id = self.alloc_expr(expr, expr_ptr);
Pat::Lit(expr_id) Pat::Lit(expr_id)
} else { } else {
Pat::Missing Pat::Missing
} };
} },
ast::Pat::RestPat(_) => { ast::Pat::RestPat(_) => {
// `RestPat` requires special handling and should not be mapped // `RestPat` requires special handling and should not be mapped
// to a Pat. Here we are using `Pat::Missing` as a fallback for // to a Pat. Here we are using `Pat::Missing` as a fallback for
@ -1063,11 +1196,11 @@ impl From<ast::LiteralKind> for Literal {
FloatTypeWrapper::new(lit.float_value().unwrap_or(Default::default())), FloatTypeWrapper::new(lit.float_value().unwrap_or(Default::default())),
builtin, builtin,
) )
} else if let builtin @ Some(_) = lit.suffix().and_then(BuiltinInt::from_suffix) { } else if let builtin @ Some(_) = lit.suffix().and_then(BuiltinUint::from_suffix) {
Literal::Int(lit.value().unwrap_or(0) as i128, builtin)
} else {
let builtin = lit.suffix().and_then(BuiltinUint::from_suffix);
Literal::Uint(lit.value().unwrap_or(0), builtin) Literal::Uint(lit.value().unwrap_or(0), builtin)
} else {
let builtin = lit.suffix().and_then(BuiltinInt::from_suffix);
Literal::Int(lit.value().unwrap_or(0) as i128, builtin)
} }
} }
LiteralKind::FloatNumber(lit) => { LiteralKind::FloatNumber(lit) => {

View file

@ -288,10 +288,6 @@ impl<'a> Printer<'a> {
self.print_expr(*expr); self.print_expr(*expr);
w!(self, ".await"); w!(self, ".await");
} }
Expr::Try { expr } => {
self.print_expr(*expr);
w!(self, "?");
}
Expr::Cast { expr, type_ref } => { Expr::Cast { expr, type_ref } => {
self.print_expr(*expr); self.print_expr(*expr);
w!(self, " as "); w!(self, " as ");
@ -424,9 +420,6 @@ impl<'a> Printer<'a> {
Expr::Unsafe { id: _, statements, tail } => { Expr::Unsafe { id: _, statements, tail } => {
self.print_block(Some("unsafe "), statements, tail); self.print_block(Some("unsafe "), statements, tail);
} }
Expr::TryBlock { id: _, statements, tail } => {
self.print_block(Some("try "), statements, tail);
}
Expr::Async { id: _, statements, tail } => { Expr::Async { id: _, statements, tail } => {
self.print_block(Some("async "), statements, tail); self.print_block(Some("async "), statements, tail);
} }

View file

@ -202,8 +202,7 @@ fn compute_expr_scopes(expr: ExprId, body: &Body, scopes: &mut ExprScopes, scope
} }
Expr::Unsafe { id, statements, tail } Expr::Unsafe { id, statements, tail }
| Expr::Async { id, statements, tail } | Expr::Async { id, statements, tail }
| Expr::Const { id, statements, tail } | Expr::Const { id, statements, tail } => {
| Expr::TryBlock { id, statements, tail } => {
let mut scope = scopes.new_block_scope(*scope, *id, None); let mut scope = scopes.new_block_scope(*scope, *id, None);
// Overwrite the old scope for the block expr, so that every block scope can be found // Overwrite the old scope for the block expr, so that every block scope can be found
// via the block itself (important for blocks that only contain items, no expressions). // via the block itself (important for blocks that only contain items, no expressions).

View file

@ -92,6 +92,16 @@ pub enum Literal {
Float(FloatTypeWrapper, Option<BuiltinFloat>), Float(FloatTypeWrapper, Option<BuiltinFloat>),
} }
impl Literal {
pub fn negate(self) -> Option<Self> {
if let Literal::Int(i, k) = self {
Some(Literal::Int(-i, k))
} else {
None
}
}
}
#[derive(Debug, Clone, Eq, PartialEq)] #[derive(Debug, Clone, Eq, PartialEq)]
pub enum Expr { pub enum Expr {
/// This is produced if the syntax tree does not have a required expression piece. /// This is produced if the syntax tree does not have a required expression piece.
@ -112,11 +122,6 @@ pub enum Expr {
tail: Option<ExprId>, tail: Option<ExprId>,
label: Option<LabelId>, label: Option<LabelId>,
}, },
TryBlock {
id: BlockId,
statements: Box<[Statement]>,
tail: Option<ExprId>,
},
Async { Async {
id: BlockId, id: BlockId,
statements: Box<[Statement]>, statements: Box<[Statement]>,
@ -192,9 +197,6 @@ pub enum Expr {
Await { Await {
expr: ExprId, expr: ExprId,
}, },
Try {
expr: ExprId,
},
Cast { Cast {
expr: ExprId, expr: ExprId,
type_ref: Interned<TypeRef>, type_ref: Interned<TypeRef>,
@ -303,7 +305,6 @@ impl Expr {
f(*expr); f(*expr);
} }
Expr::Block { statements, tail, .. } Expr::Block { statements, tail, .. }
| Expr::TryBlock { statements, tail, .. }
| Expr::Unsafe { statements, tail, .. } | Expr::Unsafe { statements, tail, .. }
| Expr::Async { statements, tail, .. } | Expr::Async { statements, tail, .. }
| Expr::Const { statements, tail, .. } => { | Expr::Const { statements, tail, .. } => {
@ -383,7 +384,6 @@ impl Expr {
} }
Expr::Field { expr, .. } Expr::Field { expr, .. }
| Expr::Await { expr } | Expr::Await { expr }
| Expr::Try { expr }
| Expr::Cast { expr, .. } | Expr::Cast { expr, .. }
| Expr::Ref { expr, .. } | Expr::Ref { expr, .. }
| Expr::UnaryOp { expr, .. } | Expr::UnaryOp { expr, .. }

View file

@ -8,8 +8,8 @@ use rustc_hash::FxHashMap;
use syntax::SmolStr; use syntax::SmolStr;
use crate::{ use crate::{
db::DefDatabase, AdtId, AssocItemId, AttrDefId, CrateId, EnumId, EnumVariantId, FunctionId, db::DefDatabase, path::Path, AdtId, AssocItemId, AttrDefId, CrateId, EnumId, EnumVariantId,
ImplId, ModuleDefId, StaticId, StructId, TraitId, TypeAliasId, UnionId, FunctionId, ImplId, ModuleDefId, StaticId, StructId, TraitId, TypeAliasId, UnionId,
}; };
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
@ -220,11 +220,6 @@ macro_rules! language_item_table {
} }
} }
/// Opposite of [`LangItem::name`]
pub fn from_name(name: &hir_expand::name::Name) -> Option<Self> {
Self::from_str(name.as_str()?)
}
/// Opposite of [`LangItem::name`] /// Opposite of [`LangItem::name`]
pub fn from_str(name: &str) -> Option<Self> { pub fn from_str(name: &str) -> Option<Self> {
match name { match name {
@ -236,6 +231,18 @@ macro_rules! language_item_table {
} }
} }
impl LangItem {
/// Opposite of [`LangItem::name`]
pub fn from_name(name: &hir_expand::name::Name) -> Option<Self> {
Self::from_str(name.as_str()?)
}
pub fn path(&self, db: &dyn DefDatabase, start_crate: CrateId) -> Option<Path> {
let t = db.lang_item(start_crate, *self)?;
Some(Path::LangItem(t))
}
}
language_item_table! { language_item_table! {
// Variant name, Name, Getter method name, Target Generic requirements; // Variant name, Name, Getter method name, Target Generic requirements;
Sized, sized, sized_trait, Target::Trait, GenericRequirement::Exact(0); Sized, sized, sized_trait, Target::Trait, GenericRequirement::Exact(0);

View file

@ -8,6 +8,7 @@ use std::{
use crate::{ use crate::{
body::LowerCtx, body::LowerCtx,
lang_item::LangItemTarget,
type_ref::{ConstRefOrPath, LifetimeRef}, type_ref::{ConstRefOrPath, LifetimeRef},
}; };
use hir_expand::name::Name; use hir_expand::name::Name;
@ -36,13 +37,19 @@ impl Display for ImportAlias {
} }
#[derive(Debug, Clone, PartialEq, Eq, Hash)] #[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct Path { pub enum Path {
/// Type based path like `<T>::foo`. /// A normal path
/// Note that paths like `<Type as Trait>::foo` are desugared to `Trait::<Self=Type>::foo`. Normal {
type_anchor: Option<Interned<TypeRef>>, /// Type based path like `<T>::foo`.
mod_path: Interned<ModPath>, /// Note that paths like `<Type as Trait>::foo` are desugared to `Trait::<Self=Type>::foo`.
/// Invariant: the same len as `self.mod_path.segments` or `None` if all segments are `None`. type_anchor: Option<Interned<TypeRef>>,
generic_args: Option<Box<[Option<Interned<GenericArgs>>]>>, mod_path: Interned<ModPath>,
/// Invariant: the same len as `self.mod_path.segments` or `None` if all segments are `None`.
generic_args: Option<Box<[Option<Interned<GenericArgs>>]>>,
},
/// A link to a lang item. It is used in desugaring of things like `x?`. We can show these
/// links via a normal path since they might be private and not accessible in the usage place.
LangItem(LangItemTarget),
} }
/// Generic arguments to a path segment (e.g. the `i32` in `Option<i32>`). This /// Generic arguments to a path segment (e.g. the `i32` in `Option<i32>`). This
@ -102,51 +109,77 @@ impl Path {
) -> Path { ) -> Path {
let generic_args = generic_args.into(); let generic_args = generic_args.into();
assert_eq!(path.len(), generic_args.len()); assert_eq!(path.len(), generic_args.len());
Path { type_anchor: None, mod_path: Interned::new(path), generic_args: Some(generic_args) } Path::Normal {
type_anchor: None,
mod_path: Interned::new(path),
generic_args: Some(generic_args),
}
}
/// Converts a known mod path to `Path`.
pub fn from_known_path_with_no_generic(path: ModPath) -> Path {
Path::Normal { type_anchor: None, mod_path: Interned::new(path), generic_args: None }
} }
pub fn kind(&self) -> &PathKind { pub fn kind(&self) -> &PathKind {
&self.mod_path.kind match self {
Path::Normal { mod_path, .. } => &mod_path.kind,
Path::LangItem(_) => &PathKind::Abs,
}
} }
pub fn type_anchor(&self) -> Option<&TypeRef> { pub fn type_anchor(&self) -> Option<&TypeRef> {
self.type_anchor.as_deref() match self {
Path::Normal { type_anchor, .. } => type_anchor.as_deref(),
Path::LangItem(_) => None,
}
} }
pub fn segments(&self) -> PathSegments<'_> { pub fn segments(&self) -> PathSegments<'_> {
let s = PathSegments { let Path::Normal { mod_path, generic_args, .. } = self else {
segments: self.mod_path.segments(), return PathSegments {
generic_args: self.generic_args.as_deref(), segments: &[],
generic_args: None,
};
}; };
let s =
PathSegments { segments: mod_path.segments(), generic_args: generic_args.as_deref() };
if let Some(generic_args) = s.generic_args { if let Some(generic_args) = s.generic_args {
assert_eq!(s.segments.len(), generic_args.len()); assert_eq!(s.segments.len(), generic_args.len());
} }
s s
} }
pub fn mod_path(&self) -> &ModPath { pub fn mod_path(&self) -> Option<&ModPath> {
&self.mod_path match self {
Path::Normal { mod_path, .. } => Some(&mod_path),
Path::LangItem(_) => None,
}
} }
pub fn qualifier(&self) -> Option<Path> { pub fn qualifier(&self) -> Option<Path> {
if self.mod_path.is_ident() { let Path::Normal { mod_path, generic_args, type_anchor } = self else {
return None;
};
if mod_path.is_ident() {
return None; return None;
} }
let res = Path { let res = Path::Normal {
type_anchor: self.type_anchor.clone(), type_anchor: type_anchor.clone(),
mod_path: Interned::new(ModPath::from_segments( mod_path: Interned::new(ModPath::from_segments(
self.mod_path.kind, mod_path.kind,
self.mod_path.segments()[..self.mod_path.segments().len() - 1].iter().cloned(), mod_path.segments()[..mod_path.segments().len() - 1].iter().cloned(),
)), )),
generic_args: self.generic_args.as_ref().map(|it| it[..it.len() - 1].to_vec().into()), generic_args: generic_args.as_ref().map(|it| it[..it.len() - 1].to_vec().into()),
}; };
Some(res) Some(res)
} }
pub fn is_self_type(&self) -> bool { pub fn is_self_type(&self) -> bool {
self.type_anchor.is_none() let Path::Normal { mod_path, generic_args, type_anchor } = self else {
&& self.generic_args.as_deref().is_none() return false;
&& self.mod_path.is_Self() };
type_anchor.is_none() && generic_args.as_deref().is_none() && mod_path.is_Self()
} }
} }
@ -222,7 +255,7 @@ impl GenericArgs {
impl From<Name> for Path { impl From<Name> for Path {
fn from(name: Name) -> Path { fn from(name: Name) -> Path {
Path { Path::Normal {
type_anchor: None, type_anchor: None,
mod_path: Interned::new(ModPath::from_segments(PathKind::Plain, iter::once(name))), mod_path: Interned::new(ModPath::from_segments(PathKind::Plain, iter::once(name))),
generic_args: None, generic_args: None,

View file

@ -75,8 +75,11 @@ pub(super) fn lower_path(mut path: ast::Path, ctx: &LowerCtx<'_>) -> Option<Path
} }
// <T as Trait<A>>::Foo desugars to Trait<Self=T, A>::Foo // <T as Trait<A>>::Foo desugars to Trait<Self=T, A>::Foo
Some(trait_ref) => { Some(trait_ref) => {
let Path { mod_path, generic_args: path_generic_args, .. } = let Path::Normal { mod_path, generic_args: path_generic_args, .. } =
Path::from_src(trait_ref.path()?, ctx)?; Path::from_src(trait_ref.path()?, ctx)? else
{
return None;
};
let num_segments = mod_path.segments().len(); let num_segments = mod_path.segments().len();
kind = mod_path.kind; kind = mod_path.kind;
@ -157,7 +160,7 @@ pub(super) fn lower_path(mut path: ast::Path, ctx: &LowerCtx<'_>) -> Option<Path
} }
let mod_path = Interned::new(ModPath::from_segments(kind, segments)); let mod_path = Interned::new(ModPath::from_segments(kind, segments));
return Some(Path { return Some(Path::Normal {
type_anchor, type_anchor,
mod_path, mod_path,
generic_args: if generic_args.is_empty() { None } else { Some(generic_args.into()) }, generic_args: if generic_args.is_empty() { None } else { Some(generic_args.into()) },

View file

@ -15,8 +15,9 @@ use crate::{
expr::{BindingId, ExprId, LabelId}, expr::{BindingId, ExprId, LabelId},
generics::{GenericParams, TypeOrConstParamData}, generics::{GenericParams, TypeOrConstParamData},
item_scope::{BuiltinShadowMode, BUILTIN_SCOPE}, item_scope::{BuiltinShadowMode, BUILTIN_SCOPE},
lang_item::LangItemTarget,
nameres::DefMap, nameres::DefMap,
path::{ModPath, PathKind}, path::{ModPath, Path, PathKind},
per_ns::PerNs, per_ns::PerNs,
visibility::{RawVisibility, Visibility}, visibility::{RawVisibility, Visibility},
AdtId, AssocItemId, ConstId, ConstParamId, DefWithBodyId, EnumId, EnumVariantId, ExternBlockId, AdtId, AssocItemId, ConstId, ConstParamId, DefWithBodyId, EnumId, EnumVariantId, ExternBlockId,
@ -176,8 +177,27 @@ impl Resolver {
pub fn resolve_path_in_type_ns( pub fn resolve_path_in_type_ns(
&self, &self,
db: &dyn DefDatabase, db: &dyn DefDatabase,
path: &ModPath, path: &Path,
) -> Option<(TypeNs, Option<usize>)> { ) -> Option<(TypeNs, Option<usize>)> {
let path = match path {
Path::Normal { mod_path, .. } => mod_path,
Path::LangItem(l) => {
return Some((
match *l {
LangItemTarget::Union(x) => TypeNs::AdtId(x.into()),
LangItemTarget::TypeAlias(x) => TypeNs::TypeAliasId(x),
LangItemTarget::Struct(x) => TypeNs::AdtId(x.into()),
LangItemTarget::EnumVariant(x) => TypeNs::EnumVariantId(x),
LangItemTarget::EnumId(x) => TypeNs::AdtId(x.into()),
LangItemTarget::Trait(x) => TypeNs::TraitId(x),
LangItemTarget::Function(_)
| LangItemTarget::ImplDef(_)
| LangItemTarget::Static(_) => return None,
},
None,
))
}
};
let first_name = path.segments().first()?; let first_name = path.segments().first()?;
let skip_to_mod = path.kind != PathKind::Plain; let skip_to_mod = path.kind != PathKind::Plain;
if skip_to_mod { if skip_to_mod {
@ -217,7 +237,7 @@ impl Resolver {
pub fn resolve_path_in_type_ns_fully( pub fn resolve_path_in_type_ns_fully(
&self, &self,
db: &dyn DefDatabase, db: &dyn DefDatabase,
path: &ModPath, path: &Path,
) -> Option<TypeNs> { ) -> Option<TypeNs> {
let (res, unresolved) = self.resolve_path_in_type_ns(db, path)?; let (res, unresolved) = self.resolve_path_in_type_ns(db, path)?;
if unresolved.is_some() { if unresolved.is_some() {
@ -245,8 +265,24 @@ impl Resolver {
pub fn resolve_path_in_value_ns( pub fn resolve_path_in_value_ns(
&self, &self,
db: &dyn DefDatabase, db: &dyn DefDatabase,
path: &ModPath, path: &Path,
) -> Option<ResolveValueResult> { ) -> Option<ResolveValueResult> {
let path = match path {
Path::Normal { mod_path, .. } => mod_path,
Path::LangItem(l) => {
return Some(ResolveValueResult::ValueNs(match *l {
LangItemTarget::Function(x) => ValueNs::FunctionId(x),
LangItemTarget::Static(x) => ValueNs::StaticId(x),
LangItemTarget::Struct(x) => ValueNs::StructId(x),
LangItemTarget::EnumVariant(x) => ValueNs::EnumVariantId(x),
LangItemTarget::Union(_)
| LangItemTarget::ImplDef(_)
| LangItemTarget::TypeAlias(_)
| LangItemTarget::Trait(_)
| LangItemTarget::EnumId(_) => return None,
}))
}
};
let n_segments = path.segments().len(); let n_segments = path.segments().len();
let tmp = name![self]; let tmp = name![self];
let first_name = if path.is_self() { &tmp } else { path.segments().first()? }; let first_name = if path.is_self() { &tmp } else { path.segments().first()? };
@ -340,7 +376,7 @@ impl Resolver {
pub fn resolve_path_in_value_ns_fully( pub fn resolve_path_in_value_ns_fully(
&self, &self,
db: &dyn DefDatabase, db: &dyn DefDatabase,
path: &ModPath, path: &Path,
) -> Option<ValueNs> { ) -> Option<ValueNs> {
match self.resolve_path_in_value_ns(db, path)? { match self.resolve_path_in_value_ns(db, path)? {
ResolveValueResult::ValueNs(it) => Some(it), ResolveValueResult::ValueNs(it) => Some(it),
@ -441,7 +477,7 @@ impl Resolver {
&Scope::ImplDefScope(impl_) => { &Scope::ImplDefScope(impl_) => {
if let Some(target_trait) = &db.impl_data(impl_).target_trait { if let Some(target_trait) = &db.impl_data(impl_).target_trait {
if let Some(TypeNs::TraitId(trait_)) = if let Some(TypeNs::TraitId(trait_)) =
self.resolve_path_in_type_ns_fully(db, target_trait.path.mod_path()) self.resolve_path_in_type_ns_fully(db, &target_trait.path)
{ {
traits.insert(trait_); traits.insert(trait_);
} }

View file

@ -78,7 +78,7 @@ impl Name {
Self::new_text(lt.text().into()) Self::new_text(lt.text().into())
} }
/// Shortcut to create inline plain text name /// Shortcut to create inline plain text name. Panics if `text.len() > 22`
const fn new_inline(text: &str) -> Name { const fn new_inline(text: &str) -> Name {
Name::new_text(SmolStr::new_inline(text)) Name::new_text(SmolStr::new_inline(text))
} }
@ -112,6 +112,18 @@ impl Name {
Name::new_inline("[missing name]") Name::new_inline("[missing name]")
} }
/// Generates a new name which is only equal to itself, by incrementing a counter. Due
/// its implementation, it should not be used in things that salsa considers, like
/// type names or field names, and it should be only used in names of local variables
/// and labels and similar things.
pub fn generate_new_name() -> Name {
use std::sync::atomic::{AtomicUsize, Ordering};
static CNT: AtomicUsize = AtomicUsize::new(0);
let c = CNT.fetch_add(1, Ordering::Relaxed);
// FIXME: Currently a `__RA_generated_name` in user code will break our analysis
Name::new_text(format!("__RA_geneated_name_{c}").into())
}
/// Returns the tuple index this name represents if it is a tuple field. /// Returns the tuple index this name represents if it is a tuple field.
pub fn as_tuple_index(&self) -> Option<usize> { pub fn as_tuple_index(&self) -> Option<usize> {
match self.0 { match self.0 {
@ -343,6 +355,8 @@ pub mod known {
feature, feature,
// known methods of lang items // known methods of lang items
call_once, call_once,
call_mut,
call,
eq, eq,
ne, ne,
ge, ge,

View file

@ -4,7 +4,7 @@ use base_db::CrateId;
use chalk_ir::{BoundVar, DebruijnIndex, GenericArgData}; use chalk_ir::{BoundVar, DebruijnIndex, GenericArgData};
use hir_def::{ use hir_def::{
expr::Expr, expr::Expr,
path::ModPath, path::Path,
resolver::{Resolver, ValueNs}, resolver::{Resolver, ValueNs},
type_ref::ConstRef, type_ref::ConstRef,
ConstId, EnumVariantId, ConstId, EnumVariantId,
@ -15,7 +15,7 @@ use stdx::never;
use crate::{ use crate::{
db::HirDatabase, infer::InferenceContext, layout::layout_of_ty, lower::ParamLoweringMode, db::HirDatabase, infer::InferenceContext, layout::layout_of_ty, lower::ParamLoweringMode,
to_placeholder_idx, utils::Generics, Const, ConstData, ConstScalar, ConstValue, GenericArg, to_placeholder_idx, utils::Generics, Const, ConstData, ConstScalar, ConstValue, GenericArg,
Interner, MemoryMap, Ty, TyBuilder, Interner, MemoryMap, Substitution, Ty, TyBuilder,
}; };
use super::mir::{interpret_mir, lower_to_mir, pad16, MirEvalError, MirLowerError}; use super::mir::{interpret_mir, lower_to_mir, pad16, MirEvalError, MirLowerError};
@ -72,7 +72,7 @@ impl From<MirEvalError> for ConstEvalError {
pub(crate) fn path_to_const( pub(crate) fn path_to_const(
db: &dyn HirDatabase, db: &dyn HirDatabase,
resolver: &Resolver, resolver: &Resolver,
path: &ModPath, path: &Path,
mode: ParamLoweringMode, mode: ParamLoweringMode,
args_lazy: impl FnOnce() -> Generics, args_lazy: impl FnOnce() -> Generics,
debruijn: DebruijnIndex, debruijn: DebruijnIndex,
@ -89,7 +89,7 @@ pub(crate) fn path_to_const(
Some(x) => ConstValue::BoundVar(BoundVar::new(debruijn, x)), Some(x) => ConstValue::BoundVar(BoundVar::new(debruijn, x)),
None => { None => {
never!( never!(
"Generic list doesn't contain this param: {:?}, {}, {:?}", "Generic list doesn't contain this param: {:?}, {:?}, {:?}",
args, args,
path, path,
p p
@ -169,6 +169,7 @@ pub(crate) fn const_eval_recover(
_: &dyn HirDatabase, _: &dyn HirDatabase,
_: &[String], _: &[String],
_: &ConstId, _: &ConstId,
_: &Substitution,
) -> Result<Const, ConstEvalError> { ) -> Result<Const, ConstEvalError> {
Err(ConstEvalError::MirLowerError(MirLowerError::Loop)) Err(ConstEvalError::MirLowerError(MirLowerError::Loop))
} }
@ -184,10 +185,11 @@ pub(crate) fn const_eval_discriminant_recover(
pub(crate) fn const_eval_query( pub(crate) fn const_eval_query(
db: &dyn HirDatabase, db: &dyn HirDatabase,
const_id: ConstId, const_id: ConstId,
subst: Substitution,
) -> Result<Const, ConstEvalError> { ) -> Result<Const, ConstEvalError> {
let def = const_id.into(); let def = const_id.into();
let body = db.mir_body(def)?; let body = db.mir_body(def)?;
let c = interpret_mir(db, &body, false)?; let c = interpret_mir(db, &body, subst, false)?;
Ok(c) Ok(c)
} }
@ -210,7 +212,7 @@ pub(crate) fn const_eval_discriminant_variant(
return Ok(value); return Ok(value);
} }
let mir_body = db.mir_body(def)?; let mir_body = db.mir_body(def)?;
let c = interpret_mir(db, &mir_body, false)?; let c = interpret_mir(db, &mir_body, Substitution::empty(Interner), false)?;
let c = try_const_usize(&c).unwrap() as i128; let c = try_const_usize(&c).unwrap() as i128;
Ok(c) Ok(c)
} }
@ -228,13 +230,13 @@ pub(crate) fn eval_to_const(
let db = ctx.db; let db = ctx.db;
if let Expr::Path(p) = &ctx.body.exprs[expr] { if let Expr::Path(p) = &ctx.body.exprs[expr] {
let resolver = &ctx.resolver; let resolver = &ctx.resolver;
if let Some(c) = path_to_const(db, resolver, p.mod_path(), mode, args, debruijn) { if let Some(c) = path_to_const(db, resolver, p, mode, args, debruijn) {
return c; return c;
} }
} }
let infer = ctx.clone().resolve_all(); let infer = ctx.clone().resolve_all();
if let Ok(mir_body) = lower_to_mir(ctx.db, ctx.owner, &ctx.body, &infer, expr) { if let Ok(mir_body) = lower_to_mir(ctx.db, ctx.owner, &ctx.body, &infer, expr) {
if let Ok(result) = interpret_mir(db, &mir_body, true) { if let Ok(result) = interpret_mir(db, &mir_body, Substitution::empty(Interner), true) {
return result; return result;
} }
} }

View file

@ -1,8 +1,10 @@
use base_db::fixture::WithFixture; use base_db::fixture::WithFixture;
use chalk_ir::Substitution;
use hir_def::db::DefDatabase; use hir_def::db::DefDatabase;
use crate::{ use crate::{
consteval::try_const_usize, db::HirDatabase, test_db::TestDB, Const, ConstScalar, Interner, consteval::try_const_usize, db::HirDatabase, mir::pad16, test_db::TestDB, Const, ConstScalar,
Interner,
}; };
use super::{ use super::{
@ -10,6 +12,8 @@ use super::{
ConstEvalError, ConstEvalError,
}; };
mod intrinsics;
fn simplify(e: ConstEvalError) -> ConstEvalError { fn simplify(e: ConstEvalError) -> ConstEvalError {
match e { match e {
ConstEvalError::MirEvalError(MirEvalError::InFunction(_, e)) => { ConstEvalError::MirEvalError(MirEvalError::InFunction(_, e)) => {
@ -30,7 +34,12 @@ fn check_number(ra_fixture: &str, answer: i128) {
match &r.data(Interner).value { match &r.data(Interner).value {
chalk_ir::ConstValue::Concrete(c) => match &c.interned { chalk_ir::ConstValue::Concrete(c) => match &c.interned {
ConstScalar::Bytes(b, _) => { ConstScalar::Bytes(b, _) => {
assert_eq!(b, &answer.to_le_bytes()[0..b.len()]); assert_eq!(
b,
&answer.to_le_bytes()[0..b.len()],
"Bytes differ. In decimal form: actual = {}, expected = {answer}",
i128::from_le_bytes(pad16(b, true))
);
} }
x => panic!("Expected number but found {:?}", x), x => panic!("Expected number but found {:?}", x),
}, },
@ -56,7 +65,7 @@ fn eval_goal(ra_fixture: &str) -> Result<Const, ConstEvalError> {
_ => None, _ => None,
}) })
.unwrap(); .unwrap();
db.const_eval(const_id) db.const_eval(const_id, Substitution::empty(Interner))
} }
#[test] #[test]
@ -76,6 +85,49 @@ fn bit_op() {
check_number(r#"const GOAL: i8 = 1 << 8"#, 0); check_number(r#"const GOAL: i8 = 1 << 8"#, 0);
} }
#[test]
fn casts() {
check_number(r#"const GOAL: usize = 12 as *const i32 as usize"#, 12);
check_number(
r#"
//- minicore: coerce_unsized, index, slice
const GOAL: i32 = {
let a = [10, 20, 3, 15];
let x: &[i32] = &a;
let y: *const [i32] = x;
let z = y as *const i32;
unsafe { *z }
};
"#,
10,
);
check_number(
r#"
//- minicore: coerce_unsized, index, slice
const GOAL: i16 = {
let a = &mut 5;
let z = a as *mut _;
unsafe { *z }
};
"#,
5,
);
check_number(
r#"
//- minicore: coerce_unsized, index, slice
const GOAL: usize = {
let a = [10, 20, 3, 15];
let x: &[i32] = &a;
let y: *const [i32] = x;
let z = y as *const [u8]; // slice fat pointer cast don't touch metadata
let w = unsafe { &*z };
w.len()
};
"#,
4,
);
}
#[test] #[test]
fn locals() { fn locals() {
check_number( check_number(
@ -166,8 +218,7 @@ fn reference_autoderef() {
#[test] #[test]
fn overloaded_deref() { fn overloaded_deref() {
// FIXME: We should support this. check_number(
check_fail(
r#" r#"
//- minicore: deref_mut //- minicore: deref_mut
struct Foo; struct Foo;
@ -185,9 +236,7 @@ fn overloaded_deref() {
*y + *x *y + *x
}; };
"#, "#,
ConstEvalError::MirLowerError(MirLowerError::NotSupported( 10,
"explicit overloaded deref".into(),
)),
); );
} }
@ -218,6 +267,42 @@ fn overloaded_deref_autoref() {
); );
} }
#[test]
fn overloaded_index() {
check_number(
r#"
//- minicore: index
struct Foo;
impl core::ops::Index<usize> for Foo {
type Output = i32;
fn index(&self, index: usize) -> &i32 {
if index == 7 {
&700
} else {
&1000
}
}
}
impl core::ops::IndexMut<usize> for Foo {
fn index_mut(&mut self, index: usize) -> &mut i32 {
if index == 7 {
&mut 7
} else {
&mut 10
}
}
}
const GOAL: i32 = {
(Foo[2]) + (Foo[7]) + (*&Foo[2]) + (*&Foo[7]) + (*&mut Foo[2]) + (*&mut Foo[7])
};
"#,
3417,
);
}
#[test] #[test]
fn function_call() { fn function_call() {
check_number( check_number(
@ -240,20 +325,6 @@ fn function_call() {
); );
} }
#[test]
fn intrinsics() {
check_number(
r#"
extern "rust-intrinsic" {
pub fn size_of<T>() -> usize;
}
const GOAL: usize = size_of::<i32>();
"#,
4,
);
}
#[test] #[test]
fn trait_basic() { fn trait_basic() {
check_number( check_number(
@ -483,6 +554,42 @@ fn loops() {
"#, "#,
4, 4,
); );
check_number(
r#"
const GOAL: u8 = {
let mut x = 0;
loop {
x = x + 1;
if x == 5 {
break x + 2;
}
}
};
"#,
7,
);
check_number(
r#"
const GOAL: u8 = {
'a: loop {
let x = 'b: loop {
let x = 'c: loop {
let x = 'd: loop {
let x = 'e: loop {
break 'd 1;
};
break 2 + x;
};
break 3 + x;
};
break 'a 4 + x;
};
break 5 + x;
}
};
"#,
8,
);
} }
#[test] #[test]
@ -522,6 +629,18 @@ fn for_loops() {
); );
} }
#[test]
fn ranges() {
check_number(
r#"
//- minicore: range
const GOAL: i32 = (1..2).start + (20..10).end + (100..=200).start + (2000..=1000).end
+ (10000..).start + (..100000).end + (..=1000000).end;
"#,
1111111,
);
}
#[test] #[test]
fn recursion() { fn recursion() {
check_number( check_number(
@ -555,6 +674,38 @@ fn structs() {
"#, "#,
17, 17,
); );
check_number(
r#"
struct Point {
x: i32,
y: i32,
}
const GOAL: i32 = {
let p = Point { x: 5, y: 2 };
let p2 = Point { x: 3, ..p };
p.x * 1000 + p.y * 100 + p2.x * 10 + p2.y
};
"#,
5232,
);
check_number(
r#"
struct Point {
x: i32,
y: i32,
}
const GOAL: i32 = {
let p = Point { x: 5, y: 2 };
let Point { x, y } = p;
let Point { x: x2, .. } = p;
let Point { y: y2, .. } = p;
x * 1000 + y * 100 + x2 * 10 + y2
};
"#,
5252,
);
} }
#[test] #[test]
@ -599,13 +750,14 @@ fn tuples() {
); );
check_number( check_number(
r#" r#"
struct TupleLike(i32, u8, i64, u16); struct TupleLike(i32, i64, u8, u16);
const GOAL: u8 = { const GOAL: i64 = {
let a = TupleLike(10, 20, 3, 15); let a = TupleLike(10, 20, 3, 15);
a.1 let TupleLike(b, .., c) = a;
a.1 * 100 + b as i64 + c as i64
}; };
"#, "#,
20, 2025,
); );
check_number( check_number(
r#" r#"
@ -652,6 +804,36 @@ fn path_pattern_matching() {
); );
} }
#[test]
fn pattern_matching_literal() {
check_number(
r#"
const fn f(x: i32) -> i32 {
match x {
-1 => 1,
1 => 10,
_ => 100,
}
}
const GOAL: i32 = f(-1) + f(1) + f(0) + f(-5);
"#,
211,
);
check_number(
r#"
const fn f(x: &str) -> u8 {
match x {
"foo" => 1,
"bar" => 10,
_ => 100,
}
}
const GOAL: u8 = f("foo") + f("bar");
"#,
11,
);
}
#[test] #[test]
fn pattern_matching_ergonomics() { fn pattern_matching_ergonomics() {
check_number( check_number(
@ -665,6 +847,16 @@ fn pattern_matching_ergonomics() {
"#, "#,
5, 5,
); );
check_number(
r#"
const GOAL: u8 = {
let a = &(2, 3);
let &(x, y) = a;
x + y
};
"#,
5,
);
} }
#[test] #[test]
@ -748,6 +940,33 @@ fn function_param_patterns() {
); );
} }
#[test]
fn match_guards() {
check_number(
r#"
//- minicore: option, eq
impl<T: PartialEq> PartialEq for Option<T> {
fn eq(&self, other: &Rhs) -> bool {
match (self, other) {
(Some(x), Some(y)) => x == y,
(None, None) => true,
_ => false,
}
}
}
fn f(x: Option<i32>) -> i32 {
match x {
y if y == Some(42) => 42000,
Some(y) => y,
None => 10
}
}
const GOAL: i32 = f(Some(42)) + f(Some(2)) + f(None);
"#,
42012,
);
}
#[test] #[test]
fn options() { fn options() {
check_number( check_number(
@ -801,6 +1020,91 @@ fn options() {
); );
} }
#[test]
fn from_trait() {
check_number(
r#"
//- minicore: from
struct E1(i32);
struct E2(i32);
impl From<E1> for E2 {
fn from(E1(x): E1) -> Self {
E2(1000 * x)
}
}
const GOAL: i32 = {
let x: E2 = E1(2).into();
x.0
};
"#,
2000,
);
}
#[test]
fn try_operator() {
check_number(
r#"
//- minicore: option, try
const fn f(x: Option<i32>, y: Option<i32>) -> Option<i32> {
Some(x? * y?)
}
const fn g(x: Option<i32>, y: Option<i32>) -> i32 {
match f(x, y) {
Some(k) => k,
None => 5,
}
}
const GOAL: i32 = g(Some(10), Some(20)) + g(Some(30), None) + g(None, Some(40)) + g(None, None);
"#,
215,
);
check_number(
r#"
//- minicore: result, try, from
struct E1(i32);
struct E2(i32);
impl From<E1> for E2 {
fn from(E1(x): E1) -> Self {
E2(1000 * x)
}
}
const fn f(x: Result<i32, E1>) -> Result<i32, E2> {
Ok(x? * 10)
}
const fn g(x: Result<i32, E1>) -> i32 {
match f(x) {
Ok(k) => 7 * k,
Err(E2(k)) => 5 * k,
}
}
const GOAL: i32 = g(Ok(2)) + g(Err(E1(3)));
"#,
15140,
);
}
#[test]
fn try_block() {
check_number(
r#"
//- minicore: option, try
const fn g(x: Option<i32>, y: Option<i32>) -> i32 {
let r = try { x? * y? };
match r {
Some(k) => k,
None => 5,
}
}
const GOAL: i32 = g(Some(10), Some(20)) + g(Some(30), None) + g(None, Some(40)) + g(None, None);
"#,
215,
);
}
#[test] #[test]
fn or_pattern() { fn or_pattern() {
check_number( check_number(
@ -839,6 +1143,220 @@ fn or_pattern() {
); );
} }
#[test]
fn function_pointer() {
check_number(
r#"
fn add2(x: u8) -> u8 {
x + 2
}
const GOAL: u8 = {
let plus2 = add2;
plus2(3)
};
"#,
5,
);
check_number(
r#"
fn add2(x: u8) -> u8 {
x + 2
}
const GOAL: u8 = {
let plus2: fn(u8) -> u8 = add2;
plus2(3)
};
"#,
5,
);
check_number(
r#"
//- minicore: coerce_unsized, index, slice
fn add2(x: u8) -> u8 {
x + 2
}
fn mult3(x: u8) -> u8 {
x * 3
}
const GOAL: u8 = {
let x = [add2, mult3];
x[0](1) + x[1](5)
};
"#,
18,
);
}
#[test]
fn enum_variant_as_function() {
check_number(
r#"
//- minicore: option
const GOAL: u8 = {
let f = Some;
f(3).unwrap_or(2)
};
"#,
3,
);
check_number(
r#"
//- minicore: option
const GOAL: u8 = {
let f: fn(u8) -> Option<u8> = Some;
f(3).unwrap_or(2)
};
"#,
3,
);
check_number(
r#"
//- minicore: coerce_unsized, index, slice
enum Foo {
Add2(u8),
Mult3(u8),
}
use Foo::*;
const fn f(x: Foo) -> u8 {
match x {
Add2(x) => x + 2,
Mult3(x) => x * 3,
}
}
const GOAL: u8 = {
let x = [Add2, Mult3];
f(x[0](1)) + f(x[1](5))
};
"#,
18,
);
}
#[test]
fn function_traits() {
check_number(
r#"
//- minicore: fn
fn add2(x: u8) -> u8 {
x + 2
}
fn call(f: impl Fn(u8) -> u8, x: u8) -> u8 {
f(x)
}
fn call_mut(mut f: impl FnMut(u8) -> u8, x: u8) -> u8 {
f(x)
}
fn call_once(f: impl FnOnce(u8) -> u8, x: u8) -> u8 {
f(x)
}
const GOAL: u8 = call(add2, 3) + call_mut(add2, 3) + call_once(add2, 3);
"#,
15,
);
check_number(
r#"
//- minicore: coerce_unsized, fn
fn add2(x: u8) -> u8 {
x + 2
}
fn call(f: &dyn Fn(u8) -> u8, x: u8) -> u8 {
f(x)
}
fn call_mut(f: &mut dyn FnMut(u8) -> u8, x: u8) -> u8 {
f(x)
}
const GOAL: u8 = call(&add2, 3) + call_mut(&mut add2, 3);
"#,
10,
);
check_number(
r#"
//- minicore: fn
fn add2(x: u8) -> u8 {
x + 2
}
fn call(f: impl Fn(u8) -> u8, x: u8) -> u8 {
f(x)
}
fn call_mut(mut f: impl FnMut(u8) -> u8, x: u8) -> u8 {
f(x)
}
fn call_once(f: impl FnOnce(u8) -> u8, x: u8) -> u8 {
f(x)
}
const GOAL: u8 = {
let add2: fn(u8) -> u8 = add2;
call(add2, 3) + call_mut(add2, 3) + call_once(add2, 3)
};
"#,
15,
);
check_number(
r#"
//- minicore: fn
fn add2(x: u8) -> u8 {
x + 2
}
fn call(f: &&&&&impl Fn(u8) -> u8, x: u8) -> u8 {
f(x)
}
const GOAL: u8 = call(&&&&&add2, 3);
"#,
5,
);
}
#[test]
fn dyn_trait() {
check_number(
r#"
//- minicore: coerce_unsized, index, slice
trait Foo {
fn foo(&self) -> u8 { 10 }
}
struct S1;
struct S2;
struct S3;
impl Foo for S1 {
fn foo(&self) -> u8 { 1 }
}
impl Foo for S2 {
fn foo(&self) -> u8 { 2 }
}
impl Foo for S3 {}
const GOAL: u8 = {
let x: &[&dyn Foo] = &[&S1, &S2, &S3];
x[0].foo() + x[1].foo() + x[2].foo()
};
"#,
13,
);
check_number(
r#"
//- minicore: coerce_unsized, index, slice
trait Foo {
fn foo(&self) -> i32 { 10 }
}
trait Bar {
fn bar(&self) -> i32 { 20 }
}
struct S;
impl Foo for S {
fn foo(&self) -> i32 { 200 }
}
impl Bar for dyn Foo {
fn bar(&self) -> i32 { 700 }
}
const GOAL: i32 = {
let x: &dyn Foo = &S;
x.bar() + x.foo()
};
"#,
900,
);
}
#[test] #[test]
fn array_and_index() { fn array_and_index() {
check_number( check_number(
@ -867,6 +1385,17 @@ fn array_and_index() {
check_number( check_number(
r#" r#"
//- minicore: coerce_unsized, index, slice //- minicore: coerce_unsized, index, slice
const GOAL: usize = {
let a = [1, 2, 3];
let x: &[i32] = &a;
let y = &*x;
y.len()
};"#,
3,
);
check_number(
r#"
//- minicore: coerce_unsized, index, slice
const GOAL: usize = [1, 2, 3, 4, 5].len();"#, const GOAL: usize = [1, 2, 3, 4, 5].len();"#,
5, 5,
); );
@ -991,8 +1520,7 @@ fn const_generic_subst_fn() {
#[test] #[test]
fn const_generic_subst_assoc_const_impl() { fn const_generic_subst_assoc_const_impl() {
// FIXME: this should evaluate to 5 check_number(
check_fail(
r#" r#"
struct Adder<const N: usize, const M: usize>; struct Adder<const N: usize, const M: usize>;
impl<const N: usize, const M: usize> Adder<N, M> { impl<const N: usize, const M: usize> Adder<N, M> {
@ -1000,7 +1528,7 @@ fn const_generic_subst_assoc_const_impl() {
} }
const GOAL: usize = Adder::<2, 3>::VAL; const GOAL: usize = Adder::<2, 3>::VAL;
"#, "#,
ConstEvalError::MirEvalError(MirEvalError::TypeError("missing generic arg")), 5,
); );
} }

View file

@ -0,0 +1,162 @@
use super::*;
#[test]
fn size_of() {
check_number(
r#"
extern "rust-intrinsic" {
pub fn size_of<T>() -> usize;
}
const GOAL: usize = size_of::<i32>();
"#,
4,
);
}
#[test]
fn transmute() {
check_number(
r#"
extern "rust-intrinsic" {
pub fn transmute<T, U>(e: T) -> U;
}
const GOAL: i32 = transmute((1i16, 1i16));
"#,
0x00010001,
);
}
#[test]
fn const_eval_select() {
check_number(
r#"
extern "rust-intrinsic" {
pub fn const_eval_select<ARG, F, G, RET>(arg: ARG, called_in_const: F, called_at_rt: G) -> RET
where
G: FnOnce<ARG, Output = RET>,
F: FnOnce<ARG, Output = RET>;
}
const fn in_const(x: i32, y: i32) -> i32 {
x + y
}
fn in_rt(x: i32, y: i32) -> i32 {
x + y
}
const GOAL: i32 = const_eval_select((2, 3), in_const, in_rt);
"#,
5,
);
}
#[test]
fn wrapping_add() {
check_number(
r#"
extern "rust-intrinsic" {
pub fn wrapping_add<T>(a: T, b: T) -> T;
}
const GOAL: u8 = wrapping_add(10, 250);
"#,
4,
);
}
#[test]
fn offset() {
check_number(
r#"
//- minicore: coerce_unsized, index, slice
extern "rust-intrinsic" {
pub fn offset<T>(dst: *const T, offset: isize) -> *const T;
}
const GOAL: u8 = unsafe {
let ar: &[(u8, u8, u8)] = &[
(10, 11, 12),
(20, 21, 22),
(30, 31, 32),
(40, 41, 42),
(50, 51, 52),
];
let ar: *const [(u8, u8, u8)] = ar;
let ar = ar as *const (u8, u8, u8);
let element = offset(ar, 2);
element.1
};
"#,
31,
);
}
#[test]
fn arith_offset() {
check_number(
r#"
//- minicore: coerce_unsized, index, slice
extern "rust-intrinsic" {
pub fn arith_offset<T>(dst: *const T, offset: isize) -> *const T;
}
const GOAL: u8 = unsafe {
let ar: &[(u8, u8, u8)] = &[
(10, 11, 12),
(20, 21, 22),
(30, 31, 32),
(40, 41, 42),
(50, 51, 52),
];
let ar: *const [(u8, u8, u8)] = ar;
let ar = ar as *const (u8, u8, u8);
let element = arith_offset(arith_offset(ar, 102), -100);
element.1
};
"#,
31,
);
}
#[test]
fn copy_nonoverlapping() {
check_number(
r#"
extern "rust-intrinsic" {
pub fn copy_nonoverlapping<T>(src: *const T, dst: *mut T, count: usize);
}
const GOAL: u8 = unsafe {
let mut x = 2;
let y = 5;
copy_nonoverlapping(&y, &mut x, 1);
x
};
"#,
5,
);
}
#[test]
fn copy() {
check_number(
r#"
//- minicore: coerce_unsized, index, slice
extern "rust-intrinsic" {
pub fn copy<T>(src: *const T, dst: *mut T, count: usize);
}
const GOAL: i32 = unsafe {
let mut x = [1i32, 2, 3, 4, 5];
let y = (&mut x as *mut _) as *mut i32;
let z = (y as usize + 4) as *const i32;
copy(z, y, 4);
x[0] + x[1] + x[2] + x[3] + x[4]
};
"#,
19,
);
}

View file

@ -57,7 +57,7 @@ pub trait HirDatabase: DefDatabase + Upcast<dyn DefDatabase> {
#[salsa::invoke(crate::consteval::const_eval_query)] #[salsa::invoke(crate::consteval::const_eval_query)]
#[salsa::cycle(crate::consteval::const_eval_recover)] #[salsa::cycle(crate::consteval::const_eval_recover)]
fn const_eval(&self, def: ConstId) -> Result<Const, ConstEvalError>; fn const_eval(&self, def: ConstId, subst: Substitution) -> Result<Const, ConstEvalError>;
#[salsa::invoke(crate::consteval::const_eval_discriminant_variant)] #[salsa::invoke(crate::consteval::const_eval_discriminant_variant)]
#[salsa::cycle(crate::consteval::const_eval_discriminant_recover)] #[salsa::cycle(crate::consteval::const_eval_discriminant_recover)]
@ -97,6 +97,10 @@ pub trait HirDatabase: DefDatabase + Upcast<dyn DefDatabase> {
#[salsa::invoke(crate::lower::generic_predicates_query)] #[salsa::invoke(crate::lower::generic_predicates_query)]
fn generic_predicates(&self, def: GenericDefId) -> Arc<[Binders<QuantifiedWhereClause>]>; fn generic_predicates(&self, def: GenericDefId) -> Arc<[Binders<QuantifiedWhereClause>]>;
#[salsa::invoke(crate::lower::trait_environment_for_body_query)]
#[salsa::transparent]
fn trait_environment_for_body(&self, def: DefWithBodyId) -> Arc<crate::TraitEnvironment>;
#[salsa::invoke(crate::lower::trait_environment_query)] #[salsa::invoke(crate::lower::trait_environment_query)]
fn trait_environment(&self, def: GenericDefId) -> Arc<crate::TraitEnvironment>; fn trait_environment(&self, def: GenericDefId) -> Arc<crate::TraitEnvironment>;

View file

@ -73,7 +73,7 @@ fn walk_unsafe(
} }
Expr::Path(path) => { Expr::Path(path) => {
let resolver = resolver_for_expr(db.upcast(), def, current); let resolver = resolver_for_expr(db.upcast(), def, current);
let value_or_partial = resolver.resolve_path_in_value_ns(db.upcast(), path.mod_path()); let value_or_partial = resolver.resolve_path_in_value_ns(db.upcast(), path);
if let Some(ResolveValueResult::ValueNs(ValueNs::StaticId(id))) = value_or_partial { if let Some(ResolveValueResult::ValueNs(ValueNs::StaticId(id))) = value_or_partial {
if db.static_data(id).mutable { if db.static_data(id).mutable {
unsafe_expr_cb(UnsafeExpr { expr: current, inside_unsafe_block }); unsafe_expr_cb(UnsafeExpr { expr: current, inside_unsafe_block });

View file

@ -25,22 +25,22 @@ use hir_def::{
expr::{BindingAnnotation, BindingId, ExprId, ExprOrPatId, PatId}, expr::{BindingAnnotation, BindingId, ExprId, ExprOrPatId, PatId},
lang_item::{LangItem, LangItemTarget}, lang_item::{LangItem, LangItemTarget},
layout::Integer, layout::Integer,
path::Path, path::{ModPath, Path},
resolver::{HasResolver, ResolveValueResult, Resolver, TypeNs, ValueNs}, resolver::{HasResolver, ResolveValueResult, Resolver, TypeNs, ValueNs},
type_ref::TypeRef, type_ref::TypeRef,
AdtId, AssocItemId, DefWithBodyId, EnumVariantId, FieldId, FunctionId, HasModule, AdtId, AssocItemId, DefWithBodyId, EnumVariantId, FieldId, FunctionId, ItemContainerId, Lookup,
ItemContainerId, Lookup, TraitId, TypeAliasId, VariantId, TraitId, TypeAliasId, VariantId,
}; };
use hir_expand::name::{name, Name}; use hir_expand::name::{name, Name};
use la_arena::ArenaMap; use la_arena::ArenaMap;
use rustc_hash::{FxHashMap, FxHashSet}; use rustc_hash::{FxHashMap, FxHashSet};
use stdx::always; use stdx::{always, never};
use crate::{ use crate::{
db::HirDatabase, fold_tys, fold_tys_and_consts, infer::coerce::CoerceMany, db::HirDatabase, fold_tys, fold_tys_and_consts, infer::coerce::CoerceMany,
lower::ImplTraitLoweringMode, to_assoc_type_id, AliasEq, AliasTy, Const, DomainGoal, lower::ImplTraitLoweringMode, static_lifetime, to_assoc_type_id, AliasEq, AliasTy, Const,
GenericArg, Goal, ImplTraitId, InEnvironment, Interner, ProjectionTy, RpitId, Substitution, DomainGoal, GenericArg, Goal, ImplTraitId, InEnvironment, Interner, ProjectionTy, RpitId,
TraitEnvironment, TraitRef, Ty, TyBuilder, TyExt, TyKind, Substitution, TraitEnvironment, TraitRef, Ty, TyBuilder, TyExt, TyKind,
}; };
// This lint has a false positive here. See the link below for details. // This lint has a false positive here. See the link below for details.
@ -57,6 +57,7 @@ mod expr;
mod pat; mod pat;
mod coerce; mod coerce;
mod closure; mod closure;
mod mutability;
/// The entry point of type inference. /// The entry point of type inference.
pub(crate) fn infer_query(db: &dyn HirDatabase, def: DefWithBodyId) -> Arc<InferenceResult> { pub(crate) fn infer_query(db: &dyn HirDatabase, def: DefWithBodyId) -> Arc<InferenceResult> {
@ -99,6 +100,8 @@ pub(crate) fn infer_query(db: &dyn HirDatabase, def: DefWithBodyId) -> Arc<Infer
ctx.infer_body(); ctx.infer_body();
ctx.infer_mut_body();
Arc::new(ctx.resolve_all()) Arc::new(ctx.resolve_all())
} }
@ -110,10 +113,7 @@ pub(crate) fn normalize(db: &dyn HirDatabase, owner: DefWithBodyId, ty: Ty) -> T
if !ty.data(Interner).flags.intersects(TypeFlags::HAS_PROJECTION) { if !ty.data(Interner).flags.intersects(TypeFlags::HAS_PROJECTION) {
return ty; return ty;
} }
let krate = owner.module(db.upcast()).krate(); let trait_env = db.trait_environment_for_body(owner);
let trait_env = owner
.as_generic_def_id()
.map_or_else(|| Arc::new(TraitEnvironment::empty(krate)), |d| db.trait_environment(d));
let mut table = unify::InferenceTable::new(db, trait_env); let mut table = unify::InferenceTable::new(db, trait_env);
let ty_with_vars = table.normalize_associated_types_in(ty); let ty_with_vars = table.normalize_associated_types_in(ty);
@ -276,6 +276,13 @@ pub struct Adjustment {
pub target: Ty, pub target: Ty,
} }
impl Adjustment {
pub fn borrow(m: Mutability, ty: Ty) -> Self {
let ty = TyKind::Ref(m, static_lifetime(), ty).intern(Interner);
Adjustment { kind: Adjust::Borrow(AutoBorrow::Ref(m)), target: ty }
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum Adjust { pub enum Adjust {
/// Go from ! to any type. /// Go from ! to any type.
@ -506,10 +513,7 @@ impl<'a> InferenceContext<'a> {
body: &'a Body, body: &'a Body,
resolver: Resolver, resolver: Resolver,
) -> Self { ) -> Self {
let krate = owner.module(db.upcast()).krate(); let trait_env = db.trait_environment_for_body(owner);
let trait_env = owner
.as_generic_def_id()
.map_or_else(|| Arc::new(TraitEnvironment::empty(krate)), |d| db.trait_environment(d));
InferenceContext { InferenceContext {
result: InferenceResult::default(), result: InferenceResult::default(),
table: unify::InferenceTable::new(db, trait_env.clone()), table: unify::InferenceTable::new(db, trait_env.clone()),
@ -851,7 +855,7 @@ impl<'a> InferenceContext<'a> {
// FIXME: this should resolve assoc items as well, see this example: // FIXME: this should resolve assoc items as well, see this example:
// https://play.rust-lang.org/?gist=087992e9e22495446c01c0d4e2d69521 // https://play.rust-lang.org/?gist=087992e9e22495446c01c0d4e2d69521
let (resolution, unresolved) = if value_ns { let (resolution, unresolved) = if value_ns {
match self.resolver.resolve_path_in_value_ns(self.db.upcast(), path.mod_path()) { match self.resolver.resolve_path_in_value_ns(self.db.upcast(), path) {
Some(ResolveValueResult::ValueNs(value)) => match value { Some(ResolveValueResult::ValueNs(value)) => match value {
ValueNs::EnumVariantId(var) => { ValueNs::EnumVariantId(var) => {
let substs = ctx.substs_from_path(path, var.into(), true); let substs = ctx.substs_from_path(path, var.into(), true);
@ -872,11 +876,15 @@ impl<'a> InferenceContext<'a> {
None => return (self.err_ty(), None), None => return (self.err_ty(), None),
} }
} else { } else {
match self.resolver.resolve_path_in_type_ns(self.db.upcast(), path.mod_path()) { match self.resolver.resolve_path_in_type_ns(self.db.upcast(), path) {
Some(it) => it, Some(it) => it,
None => return (self.err_ty(), None), None => return (self.err_ty(), None),
} }
}; };
let Some(mod_path) = path.mod_path() else {
never!("resolver should always resolve lang item paths");
return (self.err_ty(), None);
};
return match resolution { return match resolution {
TypeNs::AdtId(AdtId::StructId(strukt)) => { TypeNs::AdtId(AdtId::StructId(strukt)) => {
let substs = ctx.substs_from_path(path, strukt.into(), true); let substs = ctx.substs_from_path(path, strukt.into(), true);
@ -900,7 +908,7 @@ impl<'a> InferenceContext<'a> {
let generics = crate::utils::generics(self.db.upcast(), impl_id.into()); let generics = crate::utils::generics(self.db.upcast(), impl_id.into());
let substs = generics.placeholder_subst(self.db); let substs = generics.placeholder_subst(self.db);
let ty = self.db.impl_self_ty(impl_id).substitute(Interner, &substs); let ty = self.db.impl_self_ty(impl_id).substitute(Interner, &substs);
self.resolve_variant_on_alias(ty, unresolved, path) self.resolve_variant_on_alias(ty, unresolved, mod_path)
} }
TypeNs::TypeAliasId(it) => { TypeNs::TypeAliasId(it) => {
let container = it.lookup(self.db.upcast()).container; let container = it.lookup(self.db.upcast()).container;
@ -917,7 +925,7 @@ impl<'a> InferenceContext<'a> {
let ty = TyBuilder::def_ty(self.db, it.into(), parent_subst) let ty = TyBuilder::def_ty(self.db, it.into(), parent_subst)
.fill_with_inference_vars(&mut self.table) .fill_with_inference_vars(&mut self.table)
.build(); .build();
self.resolve_variant_on_alias(ty, unresolved, path) self.resolve_variant_on_alias(ty, unresolved, mod_path)
} }
TypeNs::AdtSelfType(_) => { TypeNs::AdtSelfType(_) => {
// FIXME this could happen in array size expressions, once we're checking them // FIXME this could happen in array size expressions, once we're checking them
@ -953,9 +961,9 @@ impl<'a> InferenceContext<'a> {
&mut self, &mut self,
ty: Ty, ty: Ty,
unresolved: Option<usize>, unresolved: Option<usize>,
path: &Path, path: &ModPath,
) -> (Ty, Option<VariantId>) { ) -> (Ty, Option<VariantId>) {
let remaining = unresolved.map(|x| path.segments().skip(x).len()).filter(|x| x > &0); let remaining = unresolved.map(|x| path.segments()[x..].len()).filter(|x| x > &0);
match remaining { match remaining {
None => { None => {
let variant = ty.as_adt().and_then(|(adt_id, _)| match adt_id { let variant = ty.as_adt().and_then(|(adt_id, _)| match adt_id {
@ -969,7 +977,7 @@ impl<'a> InferenceContext<'a> {
(ty, variant) (ty, variant)
} }
Some(1) => { Some(1) => {
let segment = path.mod_path().segments().last().unwrap(); let segment = path.segments().last().unwrap();
// this could be an enum variant or associated type // this could be an enum variant or associated type
if let Some((AdtId::EnumId(enum_id), _)) = ty.as_adt() { if let Some((AdtId::EnumId(enum_id), _)) = ty.as_adt() {
let enum_data = self.db.enum_data(enum_id); let enum_data = self.db.enum_data(enum_id);
@ -1017,10 +1025,6 @@ impl<'a> InferenceContext<'a> {
self.resolve_lang_item(lang)?.as_trait() self.resolve_lang_item(lang)?.as_trait()
} }
fn resolve_ops_try_output(&self) -> Option<TypeAliasId> {
self.resolve_output_on(self.resolve_lang_trait(LangItem::Try)?)
}
fn resolve_ops_neg_output(&self) -> Option<TypeAliasId> { fn resolve_ops_neg_output(&self) -> Option<TypeAliasId> {
self.resolve_output_on(self.resolve_lang_trait(LangItem::Neg)?) self.resolve_output_on(self.resolve_lang_trait(LangItem::Neg)?)
} }

View file

@ -51,11 +51,12 @@ fn success(
pub(super) struct CoerceMany { pub(super) struct CoerceMany {
expected_ty: Ty, expected_ty: Ty,
final_ty: Option<Ty>, final_ty: Option<Ty>,
expressions: Vec<ExprId>,
} }
impl CoerceMany { impl CoerceMany {
pub(super) fn new(expected: Ty) -> Self { pub(super) fn new(expected: Ty) -> Self {
CoerceMany { expected_ty: expected, final_ty: None } CoerceMany { expected_ty: expected, final_ty: None, expressions: vec![] }
} }
/// Returns the "expected type" with which this coercion was /// Returns the "expected type" with which this coercion was
@ -125,8 +126,15 @@ impl CoerceMany {
let result1 = ctx.table.coerce_inner(self.merged_ty(), &target_ty); let result1 = ctx.table.coerce_inner(self.merged_ty(), &target_ty);
let result2 = ctx.table.coerce_inner(expr_ty.clone(), &target_ty); let result2 = ctx.table.coerce_inner(expr_ty.clone(), &target_ty);
if let (Ok(result1), Ok(result2)) = (result1, result2) { if let (Ok(result1), Ok(result2)) = (result1, result2) {
ctx.table.register_infer_ok(result1); ctx.table.register_infer_ok(InferOk { value: (), goals: result1.goals });
ctx.table.register_infer_ok(result2); for &e in &self.expressions {
ctx.write_expr_adj(e, result1.value.0.clone());
}
ctx.table.register_infer_ok(InferOk { value: (), goals: result2.goals });
if let Some(expr) = expr {
ctx.write_expr_adj(expr, result2.value.0);
self.expressions.push(expr);
}
return self.final_ty = Some(target_ty); return self.final_ty = Some(target_ty);
} }
} }
@ -148,6 +156,9 @@ impl CoerceMany {
} }
cov_mark::hit!(coerce_merge_fail_fallback); cov_mark::hit!(coerce_merge_fail_fallback);
} }
if let Some(expr) = expr {
self.expressions.push(expr);
}
} }
} }

View file

@ -34,6 +34,7 @@ use crate::{
method_resolution::{self, lang_items_for_bin_op, VisibleFromModule}, method_resolution::{self, lang_items_for_bin_op, VisibleFromModule},
primitive::{self, UintTy}, primitive::{self, UintTy},
static_lifetime, to_chalk_trait_id, static_lifetime, to_chalk_trait_id,
traits::FnTrait,
utils::{generics, Generics}, utils::{generics, Generics},
Adjust, Adjustment, AdtId, AutoBorrow, Binders, CallableDefId, FnPointer, FnSig, FnSubst, Adjust, Adjustment, AdtId, AutoBorrow, Binders, CallableDefId, FnPointer, FnSig, FnSubst,
Interner, Rawness, Scalar, Substitution, TraitRef, Ty, TyBuilder, TyExt, Interner, Rawness, Scalar, Substitution, TraitRef, Ty, TyBuilder, TyExt,
@ -158,26 +159,6 @@ impl<'a> InferenceContext<'a> {
}) })
.1 .1
} }
Expr::TryBlock { id: _, statements, tail } => {
// The type that is returned from the try block
let try_ty = self.table.new_type_var();
if let Some(ty) = expected.only_has_type(&mut self.table) {
self.unify(&try_ty, &ty);
}
// The ok-ish type that is expected from the last expression
let ok_ty =
self.resolve_associated_type(try_ty.clone(), self.resolve_ops_try_output());
self.infer_block(
tgt_expr,
statements,
*tail,
None,
&Expectation::has_type(ok_ty.clone()),
);
try_ty
}
Expr::Async { id: _, statements, tail } => { Expr::Async { id: _, statements, tail } => {
let ret_ty = self.table.new_type_var(); let ret_ty = self.table.new_type_var();
let prev_diverges = mem::replace(&mut self.diverges, Diverges::Maybe); let prev_diverges = mem::replace(&mut self.diverges, Diverges::Maybe);
@ -385,16 +366,46 @@ impl<'a> InferenceContext<'a> {
|| res.is_none(); || res.is_none();
let (param_tys, ret_ty) = match res { let (param_tys, ret_ty) = match res {
Some((func, params, ret_ty)) => { Some((func, params, ret_ty)) => {
let adjustments = auto_deref_adjust_steps(&derefs); let mut adjustments = auto_deref_adjust_steps(&derefs);
// FIXME: Handle call adjustments for Fn/FnMut if let Some(fn_x) = func {
self.write_expr_adj(*callee, adjustments); match fn_x {
if let Some((trait_, func)) = func { FnTrait::FnOnce => (),
let subst = TyBuilder::subst_for_def(self.db, trait_, None) FnTrait::FnMut => {
.push(callee_ty.clone()) if !matches!(
.push(TyBuilder::tuple_with(params.iter().cloned())) derefed_callee.kind(Interner),
.build(); TyKind::Ref(Mutability::Mut, _, _)
self.write_method_resolution(tgt_expr, func, subst.clone()); ) {
adjustments.push(Adjustment::borrow(
Mutability::Mut,
derefed_callee.clone(),
));
}
}
FnTrait::Fn => {
if !matches!(
derefed_callee.kind(Interner),
TyKind::Ref(Mutability::Not, _, _)
) {
adjustments.push(Adjustment::borrow(
Mutability::Not,
derefed_callee.clone(),
));
}
}
}
let trait_ = fn_x
.get_id(self.db, self.trait_env.krate)
.expect("We just used it");
let trait_data = self.db.trait_data(trait_);
if let Some(func) = trait_data.method_by_name(&fn_x.method_name()) {
let subst = TyBuilder::subst_for_def(self.db, trait_, None)
.push(callee_ty.clone())
.push(TyBuilder::tuple_with(params.iter().cloned()))
.build();
self.write_method_resolution(tgt_expr, func, subst.clone());
}
} }
self.write_expr_adj(*callee, adjustments);
(params, ret_ty) (params, ret_ty)
} }
None => { None => {
@ -601,26 +612,18 @@ impl<'a> InferenceContext<'a> {
let inner_ty = self.infer_expr_inner(*expr, &Expectation::none()); let inner_ty = self.infer_expr_inner(*expr, &Expectation::none());
self.resolve_associated_type(inner_ty, self.resolve_future_future_output()) self.resolve_associated_type(inner_ty, self.resolve_future_future_output())
} }
Expr::Try { expr } => {
let inner_ty = self.infer_expr_inner(*expr, &Expectation::none());
if let Some(trait_) = self.resolve_lang_trait(LangItem::Try) {
if let Some(func) = self.db.trait_data(trait_).method_by_name(&name!(branch)) {
let subst = TyBuilder::subst_for_def(self.db, trait_, None)
.push(inner_ty.clone())
.build();
self.write_method_resolution(tgt_expr, func, subst.clone());
}
let try_output = self.resolve_output_on(trait_);
self.resolve_associated_type(inner_ty, try_output)
} else {
self.err_ty()
}
}
Expr::Cast { expr, type_ref } => { Expr::Cast { expr, type_ref } => {
let cast_ty = self.make_ty(type_ref); let cast_ty = self.make_ty(type_ref);
// FIXME: propagate the "castable to" expectation // FIXME: propagate the "castable to" expectation
let _inner_ty = self.infer_expr_no_expect(*expr); let inner_ty = self.infer_expr_no_expect(*expr);
// FIXME check the cast... match (inner_ty.kind(Interner), cast_ty.kind(Interner)) {
(TyKind::Ref(_, _, inner), TyKind::Raw(_, cast)) => {
// FIXME: record invalid cast diagnostic in case of mismatch
self.unify(inner, cast);
}
// FIXME check the other kinds of cast...
_ => (),
}
cast_ty cast_ty
} }
Expr::Ref { expr, rawness, mutability } => { Expr::Ref { expr, rawness, mutability } => {
@ -656,6 +659,23 @@ impl<'a> InferenceContext<'a> {
// FIXME: Note down method resolution her // FIXME: Note down method resolution her
match op { match op {
UnaryOp::Deref => { UnaryOp::Deref => {
if let Some(deref_trait) = self
.db
.lang_item(self.table.trait_env.krate, LangItem::Deref)
.and_then(|l| l.as_trait())
{
if let Some(deref_fn) =
self.db.trait_data(deref_trait).method_by_name(&name![deref])
{
// FIXME: this is wrong in multiple ways, subst is empty, and we emit it even for builtin deref (note that
// the mutability is not wrong, and will be fixed in `self.infer_mut`).
self.write_method_resolution(
tgt_expr,
deref_fn,
Substitution::empty(Interner),
);
}
}
autoderef::deref(&mut self.table, inner_ty).unwrap_or_else(|| self.err_ty()) autoderef::deref(&mut self.table, inner_ty).unwrap_or_else(|| self.err_ty())
} }
UnaryOp::Neg => { UnaryOp::Neg => {

View file

@ -0,0 +1,196 @@
//! Finds if an expression is an immutable context or a mutable context, which is used in selecting
//! between `Deref` and `DerefMut` or `Index` and `IndexMut` or similar.
use chalk_ir::Mutability;
use hir_def::{
expr::{Array, BindingAnnotation, Expr, ExprId, PatId, Statement, UnaryOp},
lang_item::LangItem,
};
use hir_expand::name;
use crate::{lower::lower_to_chalk_mutability, Adjust, AutoBorrow, OverloadedDeref};
use super::InferenceContext;
impl<'a> InferenceContext<'a> {
pub(crate) fn infer_mut_body(&mut self) {
self.infer_mut_expr(self.body.body_expr, Mutability::Not);
}
fn infer_mut_expr(&mut self, tgt_expr: ExprId, mut mutability: Mutability) {
let mut v = vec![];
let adjustments = self.result.expr_adjustments.get_mut(&tgt_expr).unwrap_or(&mut v);
for adj in adjustments.iter_mut().rev() {
match &mut adj.kind {
Adjust::NeverToAny | Adjust::Deref(None) | Adjust::Pointer(_) => (),
Adjust::Deref(Some(d)) => *d = OverloadedDeref(Some(mutability)),
Adjust::Borrow(b) => match b {
AutoBorrow::Ref(m) | AutoBorrow::RawPtr(m) => mutability = *m,
},
}
}
self.infer_mut_expr_without_adjust(tgt_expr, mutability);
}
fn infer_mut_expr_without_adjust(&mut self, tgt_expr: ExprId, mutability: Mutability) {
match &self.body[tgt_expr] {
Expr::Missing => (),
&Expr::If { condition, then_branch, else_branch } => {
self.infer_mut_expr(condition, Mutability::Not);
self.infer_mut_expr(then_branch, Mutability::Not);
if let Some(else_branch) = else_branch {
self.infer_mut_expr(else_branch, Mutability::Not);
}
}
Expr::Let { pat, expr } => self.infer_mut_expr(*expr, self.pat_bound_mutability(*pat)),
Expr::Block { id: _, statements, tail, label: _ }
| Expr::Async { id: _, statements, tail }
| Expr::Const { id: _, statements, tail }
| Expr::Unsafe { id: _, statements, tail } => {
for st in statements.iter() {
match st {
Statement::Let { pat, type_ref: _, initializer, else_branch } => {
if let Some(i) = initializer {
self.infer_mut_expr(*i, self.pat_bound_mutability(*pat));
}
if let Some(e) = else_branch {
self.infer_mut_expr(*e, Mutability::Not);
}
}
Statement::Expr { expr, has_semi: _ } => {
self.infer_mut_expr(*expr, Mutability::Not);
}
}
}
if let Some(tail) = tail {
self.infer_mut_expr(*tail, Mutability::Not);
}
}
&Expr::For { iterable: c, pat: _, body, label: _ }
| &Expr::While { condition: c, body, label: _ } => {
self.infer_mut_expr(c, Mutability::Not);
self.infer_mut_expr(body, Mutability::Not);
}
Expr::MethodCall { receiver: x, method_name: _, args, generic_args: _ }
| Expr::Call { callee: x, args, is_assignee_expr: _ } => {
self.infer_mut_not_expr_iter(args.iter().copied().chain(Some(*x)));
}
Expr::Match { expr, arms } => {
let m = self.pat_iter_bound_mutability(arms.iter().map(|x| x.pat));
self.infer_mut_expr(*expr, m);
for arm in arms.iter() {
self.infer_mut_expr(arm.expr, Mutability::Not);
}
}
Expr::Yield { expr }
| Expr::Yeet { expr }
| Expr::Return { expr }
| Expr::Break { expr, label: _ } => {
if let &Some(expr) = expr {
self.infer_mut_expr(expr, Mutability::Not);
}
}
Expr::RecordLit { path: _, fields, spread, ellipsis: _, is_assignee_expr: _ } => {
self.infer_mut_not_expr_iter(fields.iter().map(|x| x.expr).chain(*spread))
}
&Expr::Index { base, index } => {
if let Some((f, _)) = self.result.method_resolutions.get_mut(&tgt_expr) {
if mutability == Mutability::Mut {
if let Some(index_trait) = self
.db
.lang_item(self.table.trait_env.krate, LangItem::IndexMut)
.and_then(|l| l.as_trait())
{
if let Some(index_fn) =
self.db.trait_data(index_trait).method_by_name(&name![index_mut])
{
*f = index_fn;
}
}
}
}
self.infer_mut_expr(base, mutability);
self.infer_mut_expr(index, Mutability::Not);
}
Expr::UnaryOp { expr, op: UnaryOp::Deref } => {
if let Some((f, _)) = self.result.method_resolutions.get_mut(&tgt_expr) {
if mutability == Mutability::Mut {
if let Some(deref_trait) = self
.db
.lang_item(self.table.trait_env.krate, LangItem::DerefMut)
.and_then(|l| l.as_trait())
{
if let Some(deref_fn) =
self.db.trait_data(deref_trait).method_by_name(&name![deref_mut])
{
*f = deref_fn;
}
}
}
}
self.infer_mut_expr(*expr, mutability);
}
Expr::Field { expr, name: _ } => {
self.infer_mut_expr(*expr, mutability);
}
Expr::UnaryOp { expr, op: _ }
| Expr::Range { lhs: Some(expr), rhs: None, range_type: _ }
| Expr::Range { rhs: Some(expr), lhs: None, range_type: _ }
| Expr::Await { expr }
| Expr::Box { expr }
| Expr::Loop { body: expr, label: _ }
| Expr::Cast { expr, type_ref: _ } => {
self.infer_mut_expr(*expr, Mutability::Not);
}
Expr::Ref { expr, rawness: _, mutability } => {
let mutability = lower_to_chalk_mutability(*mutability);
self.infer_mut_expr(*expr, mutability);
}
Expr::Array(Array::Repeat { initializer: lhs, repeat: rhs })
| Expr::BinaryOp { lhs, rhs, op: _ }
| Expr::Range { lhs: Some(lhs), rhs: Some(rhs), range_type: _ } => {
self.infer_mut_expr(*lhs, Mutability::Not);
self.infer_mut_expr(*rhs, Mutability::Not);
}
// not implemented
Expr::Closure { .. } => (),
Expr::Tuple { exprs, is_assignee_expr: _ }
| Expr::Array(Array::ElementList { elements: exprs, is_assignee_expr: _ }) => {
self.infer_mut_not_expr_iter(exprs.iter().copied());
}
// These don't need any action, as they don't have sub expressions
Expr::Range { lhs: None, rhs: None, range_type: _ }
| Expr::Literal(_)
| Expr::Path(_)
| Expr::Continue { .. }
| Expr::Underscore => (),
}
}
fn infer_mut_not_expr_iter(&mut self, exprs: impl Iterator<Item = ExprId>) {
for expr in exprs {
self.infer_mut_expr(expr, Mutability::Not);
}
}
fn pat_iter_bound_mutability(&self, mut pat: impl Iterator<Item = PatId>) -> Mutability {
if pat.any(|p| self.pat_bound_mutability(p) == Mutability::Mut) {
Mutability::Mut
} else {
Mutability::Not
}
}
/// Checks if the pat contains a `ref mut` binding. Such paths makes the context of bounded expressions
/// mutable. For example in `let (ref mut x0, ref x1) = *x;` we need to use `DerefMut` for `*x` but in
/// `let (ref x0, ref x1) = *x;` we should use `Deref`.
fn pat_bound_mutability(&self, pat: PatId) -> Mutability {
let mut r = Mutability::Not;
self.body.walk_bindings_in_pat(pat, |b| {
if self.body.bindings[b].mode == BindingAnnotation::RefMut {
r = Mutability::Mut;
}
});
r
}
}

View file

@ -39,7 +39,7 @@ impl<'a> InferenceContext<'a> {
} else { } else {
// FIXME: report error, unresolved first path segment // FIXME: report error, unresolved first path segment
let value_or_partial = let value_or_partial =
self.resolver.resolve_path_in_value_ns(self.db.upcast(), path.mod_path())?; self.resolver.resolve_path_in_value_ns(self.db.upcast(), path)?;
match value_or_partial { match value_or_partial {
ResolveValueResult::ValueNs(it) => (it, None), ResolveValueResult::ValueNs(it) => (it, None),

View file

@ -8,16 +8,15 @@ use chalk_ir::{
}; };
use chalk_solve::infer::ParameterEnaVariableExt; use chalk_solve::infer::ParameterEnaVariableExt;
use ena::unify::UnifyKey; use ena::unify::UnifyKey;
use hir_def::{FunctionId, TraitId};
use hir_expand::name; use hir_expand::name;
use stdx::never; use stdx::never;
use super::{InferOk, InferResult, InferenceContext, TypeError}; use super::{InferOk, InferResult, InferenceContext, TypeError};
use crate::{ use crate::{
db::HirDatabase, fold_tys, static_lifetime, traits::FnTrait, AliasEq, AliasTy, BoundVar, db::HirDatabase, fold_tys, static_lifetime, to_chalk_trait_id, traits::FnTrait, AliasEq,
Canonical, Const, DebruijnIndex, GenericArg, GenericArgData, Goal, Guidance, InEnvironment, AliasTy, BoundVar, Canonical, Const, DebruijnIndex, GenericArg, GenericArgData, Goal, Guidance,
InferenceVar, Interner, Lifetime, ParamKind, ProjectionTy, ProjectionTyExt, Scalar, Solution, InEnvironment, InferenceVar, Interner, Lifetime, ParamKind, ProjectionTy, ProjectionTyExt,
Substitution, TraitEnvironment, Ty, TyBuilder, TyExt, TyKind, VariableKind, Scalar, Solution, Substitution, TraitEnvironment, Ty, TyBuilder, TyExt, TyKind, VariableKind,
}; };
impl<'a> InferenceContext<'a> { impl<'a> InferenceContext<'a> {
@ -631,10 +630,13 @@ impl<'a> InferenceTable<'a> {
&mut self, &mut self,
ty: &Ty, ty: &Ty,
num_args: usize, num_args: usize,
) -> Option<(Option<(TraitId, FunctionId)>, Vec<Ty>, Ty)> { ) -> Option<(Option<FnTrait>, Vec<Ty>, Ty)> {
match ty.callable_sig(self.db) { match ty.callable_sig(self.db) {
Some(sig) => Some((None, sig.params().to_vec(), sig.ret().clone())), Some(sig) => Some((None, sig.params().to_vec(), sig.ret().clone())),
None => self.callable_sig_from_fn_trait(ty, num_args), None => {
let (f, args_ty, return_ty) = self.callable_sig_from_fn_trait(ty, num_args)?;
Some((Some(f), args_ty, return_ty))
}
} }
} }
@ -642,7 +644,7 @@ impl<'a> InferenceTable<'a> {
&mut self, &mut self,
ty: &Ty, ty: &Ty,
num_args: usize, num_args: usize,
) -> Option<(Option<(TraitId, FunctionId)>, Vec<Ty>, Ty)> { ) -> Option<(FnTrait, Vec<Ty>, Ty)> {
let krate = self.trait_env.krate; let krate = self.trait_env.krate;
let fn_once_trait = FnTrait::FnOnce.get_id(self.db, krate)?; let fn_once_trait = FnTrait::FnOnce.get_id(self.db, krate)?;
let trait_data = self.db.trait_data(fn_once_trait); let trait_data = self.db.trait_data(fn_once_trait);
@ -676,19 +678,28 @@ impl<'a> InferenceTable<'a> {
}; };
let trait_env = self.trait_env.env.clone(); let trait_env = self.trait_env.env.clone();
let mut trait_ref = projection.trait_ref(self.db);
let obligation = InEnvironment { let obligation = InEnvironment {
goal: projection.trait_ref(self.db).cast(Interner), goal: trait_ref.clone().cast(Interner),
environment: trait_env, environment: trait_env.clone(),
}; };
let canonical = self.canonicalize(obligation.clone()); let canonical = self.canonicalize(obligation.clone());
if self.db.trait_solve(krate, canonical.value.cast(Interner)).is_some() { if self.db.trait_solve(krate, canonical.value.cast(Interner)).is_some() {
self.register_obligation(obligation.goal); self.register_obligation(obligation.goal);
let return_ty = self.normalize_projection_ty(projection); let return_ty = self.normalize_projection_ty(projection);
Some(( for fn_x in [FnTrait::Fn, FnTrait::FnMut, FnTrait::FnOnce] {
Some(fn_once_trait).zip(trait_data.method_by_name(&name!(call_once))), let fn_x_trait = fn_x.get_id(self.db, krate)?;
arg_tys, trait_ref.trait_id = to_chalk_trait_id(fn_x_trait);
return_ty, let obligation: chalk_ir::InEnvironment<chalk_ir::Goal<Interner>> = InEnvironment {
)) goal: trait_ref.clone().cast(Interner),
environment: trait_env.clone(),
};
let canonical = self.canonicalize(obligation.clone());
if self.db.trait_solve(krate, canonical.value.cast(Interner)).is_some() {
return Some((fn_x, arg_tys, return_ty));
}
}
unreachable!("It should at least implement FnOnce at this point");
} else { } else {
None None
} }

View file

@ -576,10 +576,14 @@ where
} }
pub fn callable_sig_from_fnonce( pub fn callable_sig_from_fnonce(
self_ty: &Ty, mut self_ty: &Ty,
env: Arc<TraitEnvironment>, env: Arc<TraitEnvironment>,
db: &dyn HirDatabase, db: &dyn HirDatabase,
) -> Option<CallableSig> { ) -> Option<CallableSig> {
if let Some((ty, _, _)) = self_ty.as_reference() {
// This will happen when it implements fn or fn mut, since we add a autoborrow adjustment
self_ty = ty;
}
let krate = env.krate; let krate = env.krate;
let fn_once_trait = FnTrait::FnOnce.get_id(db, krate)?; let fn_once_trait = FnTrait::FnOnce.get_id(db, krate)?;
let output_assoc_type = db.trait_data(fn_once_trait).associated_type_by_name(&name![Output])?; let output_assoc_type = db.trait_data(fn_once_trait).associated_type_by_name(&name![Output])?;

View file

@ -25,12 +25,12 @@ use hir_def::{
TypeOrConstParamData, TypeParamProvenance, WherePredicate, WherePredicateTypeTarget, TypeOrConstParamData, TypeParamProvenance, WherePredicate, WherePredicateTypeTarget,
}, },
lang_item::{lang_attr, LangItem}, lang_item::{lang_attr, LangItem},
path::{GenericArg, ModPath, Path, PathKind, PathSegment, PathSegments}, path::{GenericArg, GenericArgs, ModPath, Path, PathKind, PathSegment, PathSegments},
resolver::{HasResolver, Resolver, TypeNs}, resolver::{HasResolver, Resolver, TypeNs},
type_ref::{ConstRefOrPath, TraitBoundModifier, TraitRef as HirTraitRef, TypeBound, TypeRef}, type_ref::{ConstRefOrPath, TraitBoundModifier, TraitRef as HirTraitRef, TypeBound, TypeRef},
AdtId, AssocItemId, ConstId, ConstParamId, EnumId, EnumVariantId, FunctionId, GenericDefId, AdtId, AssocItemId, ConstId, ConstParamId, DefWithBodyId, EnumId, EnumVariantId, FunctionId,
HasModule, ImplId, ItemContainerId, LocalFieldId, Lookup, ModuleDefId, StaticId, StructId, GenericDefId, HasModule, ImplId, ItemContainerId, LocalFieldId, Lookup, ModuleDefId, StaticId,
TraitId, TypeAliasId, TypeOrConstParamId, TypeParamId, UnionId, VariantId, StructId, TraitId, TypeAliasId, TypeOrConstParamId, TypeParamId, UnionId, VariantId,
}; };
use hir_expand::{name::Name, ExpandResult}; use hir_expand::{name::Name, ExpandResult};
use intern::Interned; use intern::Interned;
@ -425,11 +425,10 @@ impl<'a> TyLoweringContext<'a> {
if path.segments().len() > 1 { if path.segments().len() > 1 {
return None; return None;
} }
let resolution = let resolution = match self.resolver.resolve_path_in_type_ns(self.db.upcast(), path) {
match self.resolver.resolve_path_in_type_ns(self.db.upcast(), path.mod_path()) { Some((it, None)) => it,
Some((it, None)) => it, _ => return None,
_ => return None, };
};
match resolution { match resolution {
TypeNs::GenericParam(param_id) => Some(param_id.into()), TypeNs::GenericParam(param_id) => Some(param_id.into()),
_ => None, _ => None,
@ -608,7 +607,7 @@ impl<'a> TyLoweringContext<'a> {
} }
let (resolution, remaining_index) = let (resolution, remaining_index) =
match self.resolver.resolve_path_in_type_ns(self.db.upcast(), path.mod_path()) { match self.resolver.resolve_path_in_type_ns(self.db.upcast(), path) {
Some(it) => it, Some(it) => it,
None => return (TyKind::Error.intern(Interner), None), None => return (TyKind::Error.intern(Interner), None),
}; };
@ -716,7 +715,7 @@ impl<'a> TyLoweringContext<'a> {
resolved: ValueTyDefId, resolved: ValueTyDefId,
infer_args: bool, infer_args: bool,
) -> Substitution { ) -> Substitution {
let last = path.segments().last().expect("path should have at least one segment"); let last = path.segments().last();
let (segment, generic_def) = match resolved { let (segment, generic_def) = match resolved {
ValueTyDefId::FunctionId(it) => (last, Some(it.into())), ValueTyDefId::FunctionId(it) => (last, Some(it.into())),
ValueTyDefId::StructId(it) => (last, Some(it.into())), ValueTyDefId::StructId(it) => (last, Some(it.into())),
@ -732,13 +731,20 @@ impl<'a> TyLoweringContext<'a> {
let len = path.segments().len(); let len = path.segments().len();
let penultimate = len.checked_sub(2).and_then(|idx| path.segments().get(idx)); let penultimate = len.checked_sub(2).and_then(|idx| path.segments().get(idx));
let segment = match penultimate { let segment = match penultimate {
Some(segment) if segment.args_and_bindings.is_some() => segment, Some(segment) if segment.args_and_bindings.is_some() => Some(segment),
_ => last, _ => last,
}; };
(segment, Some(var.parent.into())) (segment, Some(var.parent.into()))
} }
}; };
self.substs_from_path_segment(segment, generic_def, infer_args, None) if let Some(segment) = segment {
self.substs_from_path_segment(segment, generic_def, infer_args, None)
} else if let Some(generic_def) = generic_def {
// lang item
self.substs_from_args_and_bindings(None, Some(generic_def), infer_args, None)
} else {
Substitution::empty(Interner)
}
} }
fn substs_from_path_segment( fn substs_from_path_segment(
@ -747,6 +753,21 @@ impl<'a> TyLoweringContext<'a> {
def: Option<GenericDefId>, def: Option<GenericDefId>,
infer_args: bool, infer_args: bool,
explicit_self_ty: Option<Ty>, explicit_self_ty: Option<Ty>,
) -> Substitution {
self.substs_from_args_and_bindings(
segment.args_and_bindings,
def,
infer_args,
explicit_self_ty,
)
}
fn substs_from_args_and_bindings(
&self,
args_and_bindings: Option<&GenericArgs>,
def: Option<GenericDefId>,
infer_args: bool,
explicit_self_ty: Option<Ty>,
) -> Substitution { ) -> Substitution {
// Remember that the item's own generic args come before its parent's. // Remember that the item's own generic args come before its parent's.
let mut substs = Vec::new(); let mut substs = Vec::new();
@ -780,7 +801,7 @@ impl<'a> TyLoweringContext<'a> {
}; };
let mut had_explicit_args = false; let mut had_explicit_args = false;
if let Some(generic_args) = &segment.args_and_bindings { if let Some(generic_args) = &args_and_bindings {
if !generic_args.has_self_type { if !generic_args.has_self_type {
fill_self_params(); fill_self_params();
} }
@ -879,12 +900,11 @@ impl<'a> TyLoweringContext<'a> {
path: &Path, path: &Path,
explicit_self_ty: Option<Ty>, explicit_self_ty: Option<Ty>,
) -> Option<TraitRef> { ) -> Option<TraitRef> {
let resolved = let resolved = match self.resolver.resolve_path_in_type_ns_fully(self.db.upcast(), path)? {
match self.resolver.resolve_path_in_type_ns_fully(self.db.upcast(), path.mod_path())? { // FIXME(trait_alias): We need to handle trait alias here.
// FIXME(trait_alias): We need to handle trait alias here. TypeNs::TraitId(tr) => tr,
TypeNs::TraitId(tr) => tr, _ => return None,
_ => return None, };
};
let segment = path.segments().last().expect("path should have at least one segment"); let segment = path.segments().last().expect("path should have at least one segment");
Some(self.lower_trait_ref_from_resolved_path(resolved, segment, explicit_self_ty)) Some(self.lower_trait_ref_from_resolved_path(resolved, segment, explicit_self_ty))
} }
@ -1381,9 +1401,7 @@ pub(crate) fn generic_predicates_for_param_query(
Some(it) => it, Some(it) => it,
None => return true, None => return true,
}; };
let tr = match resolver let tr = match resolver.resolve_path_in_type_ns_fully(db.upcast(), path) {
.resolve_path_in_type_ns_fully(db.upcast(), path.mod_path())
{
Some(TypeNs::TraitId(tr)) => tr, Some(TypeNs::TraitId(tr)) => tr,
_ => return false, _ => return false,
}; };
@ -1423,6 +1441,17 @@ pub(crate) fn generic_predicates_for_param_recover(
Arc::new([]) Arc::new([])
} }
pub(crate) fn trait_environment_for_body_query(
db: &dyn HirDatabase,
def: DefWithBodyId,
) -> Arc<TraitEnvironment> {
let Some(def) = def.as_generic_def_id() else {
let krate = def.module(db.upcast()).krate();
return Arc::new(TraitEnvironment::empty(krate));
};
db.trait_environment(def)
}
pub(crate) fn trait_environment_query( pub(crate) fn trait_environment_query(
db: &dyn HirDatabase, db: &dyn HirDatabase,
def: GenericDefId, def: GenericDefId,
@ -1948,7 +1977,7 @@ pub(crate) fn generic_arg_to_chalk<'a, T>(
// as types. Maybe here is not the best place to do it, but // as types. Maybe here is not the best place to do it, but
// it works. // it works.
if let TypeRef::Path(p) = t { if let TypeRef::Path(p) = t {
let p = p.mod_path(); let p = p.mod_path()?;
if p.kind == PathKind::Plain { if p.kind == PathKind::Plain {
if let [n] = p.segments() { if let [n] = p.segments() {
let c = ConstRefOrPath::Path(n.clone()); let c = ConstRefOrPath::Path(n.clone());
@ -1977,8 +2006,15 @@ pub(crate) fn const_or_path_to_chalk(
ConstRefOrPath::Scalar(s) => intern_const_ref(db, s, expected_ty, resolver.krate()), ConstRefOrPath::Scalar(s) => intern_const_ref(db, s, expected_ty, resolver.krate()),
ConstRefOrPath::Path(n) => { ConstRefOrPath::Path(n) => {
let path = ModPath::from_segments(PathKind::Plain, Some(n.clone())); let path = ModPath::from_segments(PathKind::Plain, Some(n.clone()));
path_to_const(db, resolver, &path, mode, args, debruijn) path_to_const(
.unwrap_or_else(|| unknown_const(expected_ty)) db,
resolver,
&Path::from_known_path_with_no_generic(path),
mode,
args,
debruijn,
)
.unwrap_or_else(|| unknown_const(expected_ty))
} }
} }
} }

View file

@ -5,7 +5,7 @@
use std::{ops::ControlFlow, sync::Arc}; use std::{ops::ControlFlow, sync::Arc};
use base_db::{CrateId, Edition}; use base_db::{CrateId, Edition};
use chalk_ir::{cast::Cast, Mutability, TyKind, UniverseIndex}; use chalk_ir::{cast::Cast, Mutability, TyKind, UniverseIndex, WhereClause};
use hir_def::{ use hir_def::{
data::ImplData, item_scope::ItemScope, lang_item::LangItem, nameres::DefMap, AssocItemId, data::ImplData, item_scope::ItemScope, lang_item::LangItem, nameres::DefMap, AssocItemId,
BlockId, ConstId, FunctionId, HasModule, ImplId, ItemContainerId, Lookup, ModuleDefId, BlockId, ConstId, FunctionId, HasModule, ImplId, ItemContainerId, Lookup, ModuleDefId,
@ -20,7 +20,7 @@ use crate::{
autoderef::{self, AutoderefKind}, autoderef::{self, AutoderefKind},
db::HirDatabase, db::HirDatabase,
from_chalk_trait_id, from_foreign_def_id, from_chalk_trait_id, from_foreign_def_id,
infer::{unify::InferenceTable, Adjust, Adjustment, AutoBorrow, OverloadedDeref, PointerCast}, infer::{unify::InferenceTable, Adjust, Adjustment, OverloadedDeref, PointerCast},
primitive::{FloatTy, IntTy, UintTy}, primitive::{FloatTy, IntTy, UintTy},
static_lifetime, to_chalk_trait_id, static_lifetime, to_chalk_trait_id,
utils::all_super_traits, utils::all_super_traits,
@ -600,9 +600,9 @@ impl ReceiverAdjustments {
} }
} }
if let Some(m) = self.autoref { if let Some(m) = self.autoref {
ty = TyKind::Ref(m, static_lifetime(), ty).intern(Interner); let a = Adjustment::borrow(m, ty);
adjust ty = a.target.clone();
.push(Adjustment { kind: Adjust::Borrow(AutoBorrow::Ref(m)), target: ty.clone() }); adjust.push(a);
} }
if self.unsize_array { if self.unsize_array {
ty = 'x: { ty = 'x: {
@ -692,6 +692,39 @@ pub fn lookup_impl_const(
.unwrap_or((const_id, subs)) .unwrap_or((const_id, subs))
} }
/// Checks if the self parameter of `Trait` method is the `dyn Trait` and we should
/// call the method using the vtable.
pub fn is_dyn_method(
db: &dyn HirDatabase,
_env: Arc<TraitEnvironment>,
func: FunctionId,
fn_subst: Substitution,
) -> Option<usize> {
let ItemContainerId::TraitId(trait_id) = func.lookup(db.upcast()).container else {
return None;
};
let trait_params = db.generic_params(trait_id.into()).type_or_consts.len();
let fn_params = fn_subst.len(Interner) - trait_params;
let trait_ref = TraitRef {
trait_id: to_chalk_trait_id(trait_id),
substitution: Substitution::from_iter(Interner, fn_subst.iter(Interner).skip(fn_params)),
};
let self_ty = trait_ref.self_type_parameter(Interner);
if let TyKind::Dyn(d) = self_ty.kind(Interner) {
let is_my_trait_in_bounds =
d.bounds.skip_binders().as_slice(Interner).iter().any(|x| match x.skip_binders() {
// rustc doesn't accept `impl Foo<2> for dyn Foo<5>`, so if the trait id is equal, no matter
// what the generics are, we are sure that the method is come from the vtable.
WhereClause::Implemented(tr) => tr.trait_id == trait_ref.trait_id,
_ => false,
});
if is_my_trait_in_bounds {
return Some(fn_params);
}
}
None
}
/// Looks up the impl method that actually runs for the trait method `func`. /// Looks up the impl method that actually runs for the trait method `func`.
/// ///
/// Returns `func` if it's not a method defined in a trait or the lookup failed. /// Returns `func` if it's not a method defined in a trait or the lookup failed.
@ -701,9 +734,8 @@ pub fn lookup_impl_method(
func: FunctionId, func: FunctionId,
fn_subst: Substitution, fn_subst: Substitution,
) -> (FunctionId, Substitution) { ) -> (FunctionId, Substitution) {
let trait_id = match func.lookup(db.upcast()).container { let ItemContainerId::TraitId(trait_id) = func.lookup(db.upcast()).container else {
ItemContainerId::TraitId(id) => id, return (func, fn_subst)
_ => return (func, fn_subst),
}; };
let trait_params = db.generic_params(trait_id.into()).type_or_consts.len(); let trait_params = db.generic_params(trait_id.into()).type_or_consts.len();
let fn_params = fn_subst.len(Interner) - trait_params; let fn_params = fn_subst.len(Interner) - trait_params;

File diff suppressed because it is too large Load diff

View file

@ -4,19 +4,21 @@ use std::{iter, mem, sync::Arc};
use chalk_ir::{BoundVar, ConstData, DebruijnIndex, TyKind}; use chalk_ir::{BoundVar, ConstData, DebruijnIndex, TyKind};
use hir_def::{ use hir_def::{
adt::{StructKind, VariantData},
body::Body, body::Body,
expr::{ expr::{
Array, BindingAnnotation, BindingId, ExprId, LabelId, Literal, MatchArm, Pat, PatId, Array, BindingAnnotation, BindingId, ExprId, LabelId, Literal, MatchArm, Pat, PatId,
RecordLitField, RecordFieldPat, RecordLitField,
}, },
lang_item::{LangItem, LangItemTarget}, lang_item::{LangItem, LangItemTarget},
layout::LayoutError, layout::LayoutError,
path::Path, path::Path,
resolver::{resolver_for_expr, ResolveValueResult, ValueNs}, resolver::{resolver_for_expr, ResolveValueResult, ValueNs},
DefWithBodyId, EnumVariantId, HasModule, AdtId, DefWithBodyId, EnumVariantId, HasModule, ItemContainerId, LocalFieldId, TraitId,
}; };
use hir_expand::name::Name; use hir_expand::name::Name;
use la_arena::ArenaMap; use la_arena::ArenaMap;
use rustc_hash::FxHashMap;
use crate::{ use crate::{
consteval::ConstEvalError, db::HirDatabase, display::HirDisplay, infer::TypeMismatch, consteval::ConstEvalError, db::HirDatabase, display::HirDisplay, infer::TypeMismatch,
@ -27,18 +29,25 @@ use crate::{
use super::*; use super::*;
mod as_place; mod as_place;
mod pattern_matching;
#[derive(Debug, Clone, Copy)] use pattern_matching::AdtPatternShape;
#[derive(Debug, Clone)]
struct LoopBlocks { struct LoopBlocks {
begin: BasicBlockId, begin: BasicBlockId,
/// `None` for loops that are not terminating /// `None` for loops that are not terminating
end: Option<BasicBlockId>, end: Option<BasicBlockId>,
place: Place,
} }
struct MirLowerCtx<'a> { struct MirLowerCtx<'a> {
result: MirBody, result: MirBody,
owner: DefWithBodyId, owner: DefWithBodyId,
current_loop_blocks: Option<LoopBlocks>, current_loop_blocks: Option<LoopBlocks>,
// FIXME: we should resolve labels in HIR lowering and always work with label id here, not
// with raw names.
labeled_loop_blocks: FxHashMap<Name, LoopBlocks>,
discr_temp: Option<Place>, discr_temp: Option<Place>,
db: &'a dyn HirDatabase, db: &'a dyn HirDatabase,
body: &'a Body, body: &'a Body,
@ -50,6 +59,8 @@ pub enum MirLowerError {
ConstEvalError(Box<ConstEvalError>), ConstEvalError(Box<ConstEvalError>),
LayoutError(LayoutError), LayoutError(LayoutError),
IncompleteExpr, IncompleteExpr,
/// Trying to lower a trait function, instead of an implementation
TraitFunctionDefinition(TraitId, Name),
UnresolvedName(String), UnresolvedName(String),
RecordLiteralWithoutPath, RecordLiteralWithoutPath,
UnresolvedMethod, UnresolvedMethod,
@ -66,6 +77,7 @@ pub enum MirLowerError {
ImplementationError(&'static str), ImplementationError(&'static str),
LangItemNotFound(LangItem), LangItemNotFound(LangItem),
MutatingRvalue, MutatingRvalue,
UnresolvedLabel,
} }
macro_rules! not_supported { macro_rules! not_supported {
@ -200,26 +212,42 @@ impl MirLowerCtx<'_> {
mut current: BasicBlockId, mut current: BasicBlockId,
) -> Result<Option<BasicBlockId>> { ) -> Result<Option<BasicBlockId>> {
match &self.body.exprs[expr_id] { match &self.body.exprs[expr_id] {
Expr::Missing => Err(MirLowerError::IncompleteExpr), Expr::Missing => {
if let DefWithBodyId::FunctionId(f) = self.owner {
let assoc = self.db.lookup_intern_function(f);
if let ItemContainerId::TraitId(t) = assoc.container {
let name = &self.db.function_data(f).name;
return Err(MirLowerError::TraitFunctionDefinition(t, name.clone()));
}
}
Err(MirLowerError::IncompleteExpr)
},
Expr::Path(p) => { Expr::Path(p) => {
let unresolved_name = || MirLowerError::unresolved_path(self.db, p); let unresolved_name = || MirLowerError::unresolved_path(self.db, p);
let resolver = resolver_for_expr(self.db.upcast(), self.owner, expr_id); let resolver = resolver_for_expr(self.db.upcast(), self.owner, expr_id);
let pr = resolver let pr = resolver
.resolve_path_in_value_ns(self.db.upcast(), p.mod_path()) .resolve_path_in_value_ns(self.db.upcast(), p)
.ok_or_else(unresolved_name)?; .ok_or_else(unresolved_name)?;
let pr = match pr { let pr = match pr {
ResolveValueResult::ValueNs(v) => v, ResolveValueResult::ValueNs(v) => v,
ResolveValueResult::Partial(..) => { ResolveValueResult::Partial(..) => {
if let Some(assoc) = self if let Some((assoc, subst)) = self
.infer .infer
.assoc_resolutions_for_expr(expr_id) .assoc_resolutions_for_expr(expr_id)
{ {
match assoc.0 { match assoc {
hir_def::AssocItemId::ConstId(c) => { hir_def::AssocItemId::ConstId(c) => {
self.lower_const(c, current, place, expr_id.into())?; self.lower_const(c, current, place, subst, expr_id.into())?;
return Ok(Some(current)) return Ok(Some(current))
}, },
_ => not_supported!("associated functions and types"), hir_def::AssocItemId::FunctionId(_) => {
// FnDefs are zero sized, no action is needed.
return Ok(Some(current))
}
hir_def::AssocItemId::TypeAliasId(_) => {
// FIXME: If it is unreachable, use proper error instead of `not_supported`.
not_supported!("associated functions and types")
},
} }
} else if let Some(variant) = self } else if let Some(variant) = self
.infer .infer
@ -246,19 +274,23 @@ impl MirLowerCtx<'_> {
Ok(Some(current)) Ok(Some(current))
} }
ValueNs::ConstId(const_id) => { ValueNs::ConstId(const_id) => {
self.lower_const(const_id, current, place, expr_id.into())?; self.lower_const(const_id, current, place, Substitution::empty(Interner), expr_id.into())?;
Ok(Some(current)) Ok(Some(current))
} }
ValueNs::EnumVariantId(variant_id) => { ValueNs::EnumVariantId(variant_id) => {
let ty = self.infer.type_of_expr[expr_id].clone(); let variant_data = &self.db.enum_data(variant_id.parent).variants[variant_id.local_id];
let current = self.lower_enum_variant( if variant_data.variant_data.kind() == StructKind::Unit {
variant_id, let ty = self.infer.type_of_expr[expr_id].clone();
current, current = self.lower_enum_variant(
place, variant_id,
ty, current,
vec![], place,
expr_id.into(), ty,
)?; vec![],
expr_id.into(),
)?;
}
// Otherwise its a tuple like enum, treated like a zero sized function, so no action is needed
Ok(Some(current)) Ok(Some(current))
} }
ValueNs::GenericParam(p) => { ValueNs::GenericParam(p) => {
@ -287,7 +319,7 @@ impl MirLowerCtx<'_> {
); );
Ok(Some(current)) Ok(Some(current))
} }
ValueNs::StructId(_) => { ValueNs::FunctionId(_) | ValueNs::StructId(_) => {
// It's probably a unit struct or a zero sized function, so no action is needed. // It's probably a unit struct or a zero sized function, so no action is needed.
Ok(Some(current)) Ok(Some(current))
} }
@ -349,19 +381,29 @@ impl MirLowerCtx<'_> {
Ok(self.merge_blocks(Some(then_target), else_target)) Ok(self.merge_blocks(Some(then_target), else_target))
} }
Expr::Unsafe { id: _, statements, tail } => { Expr::Unsafe { id: _, statements, tail } => {
self.lower_block_to_place(None, statements, current, *tail, place) self.lower_block_to_place(statements, current, *tail, place)
} }
Expr::Block { id: _, statements, tail, label } => { Expr::Block { id: _, statements, tail, label } => {
self.lower_block_to_place(*label, statements, current, *tail, place) if let Some(label) = label {
self.lower_loop(current, place.clone(), Some(*label), |this, begin| {
if let Some(block) = this.lower_block_to_place(statements, begin, *tail, place)? {
let end = this.current_loop_end()?;
this.set_goto(block, end);
}
Ok(())
})
} else {
self.lower_block_to_place(statements, current, *tail, place)
}
} }
Expr::Loop { body, label } => self.lower_loop(current, *label, |this, begin| { Expr::Loop { body, label } => self.lower_loop(current, place, *label, |this, begin| {
if let Some((_, block)) = this.lower_expr_as_place(begin, *body, true)? { if let Some((_, block)) = this.lower_expr_as_place(begin, *body, true)? {
this.set_goto(block, begin); this.set_goto(block, begin);
} }
Ok(()) Ok(())
}), }),
Expr::While { condition, body, label } => { Expr::While { condition, body, label } => {
self.lower_loop(current, *label, |this, begin| { self.lower_loop(current, place, *label, |this, begin| {
let Some((discr, to_switch)) = this.lower_expr_to_some_operand(*condition, begin)? else { let Some((discr, to_switch)) = this.lower_expr_to_some_operand(*condition, begin)? else {
return Ok(()); return Ok(());
}; };
@ -412,7 +454,7 @@ impl MirLowerCtx<'_> {
return Ok(None); return Ok(None);
}; };
self.push_assignment(current, ref_mut_iterator_place.clone(), Rvalue::Ref(BorrowKind::Mut { allow_two_phase_borrow: false }, iterator_place), expr_id.into()); self.push_assignment(current, ref_mut_iterator_place.clone(), Rvalue::Ref(BorrowKind::Mut { allow_two_phase_borrow: false }, iterator_place), expr_id.into());
self.lower_loop(current, label, |this, begin| { self.lower_loop(current, place, label, |this, begin| {
let Some(current) = this.lower_call(iter_next_fn_op, vec![Operand::Copy(ref_mut_iterator_place)], option_item_place.clone(), begin, false)? let Some(current) = this.lower_call(iter_next_fn_op, vec![Operand::Copy(ref_mut_iterator_place)], option_item_place.clone(), begin, false)?
else { else {
return Ok(()); return Ok(());
@ -426,7 +468,8 @@ impl MirLowerCtx<'_> {
current, current,
pat.into(), pat.into(),
Some(end), Some(end),
&[pat], &None)?; AdtPatternShape::Tuple { args: &[pat], ellipsis: None },
)?;
if let Some((_, block)) = this.lower_expr_as_place(current, body, true)? { if let Some((_, block)) = this.lower_expr_as_place(current, body, true)? {
this.set_goto(block, begin); this.set_goto(block, begin);
} }
@ -434,36 +477,36 @@ impl MirLowerCtx<'_> {
}) })
}, },
Expr::Call { callee, args, .. } => { Expr::Call { callee, args, .. } => {
if let Some((func_id, generic_args)) =
self.infer.method_resolution(expr_id) {
let ty = chalk_ir::TyKind::FnDef(
CallableDefId::FunctionId(func_id).to_chalk(self.db),
generic_args,
)
.intern(Interner);
let func = Operand::from_bytes(vec![], ty);
return self.lower_call_and_args(
func,
iter::once(*callee).chain(args.iter().copied()),
place,
current,
self.is_uninhabited(expr_id),
);
}
let callee_ty = self.expr_ty_after_adjustments(*callee); let callee_ty = self.expr_ty_after_adjustments(*callee);
match &callee_ty.data(Interner).kind { match &callee_ty.data(Interner).kind {
chalk_ir::TyKind::FnDef(..) => { chalk_ir::TyKind::FnDef(..) => {
let func = Operand::from_bytes(vec![], callee_ty.clone()); let func = Operand::from_bytes(vec![], callee_ty.clone());
self.lower_call_and_args(func, args.iter().copied(), place, current, self.is_uninhabited(expr_id)) self.lower_call_and_args(func, args.iter().copied(), place, current, self.is_uninhabited(expr_id))
} }
TyKind::Scalar(_) chalk_ir::TyKind::Function(_) => {
| TyKind::Tuple(_, _) let Some((func, current)) = self.lower_expr_to_some_operand(*callee, current)? else {
| TyKind::Array(_, _) return Ok(None);
| TyKind::Adt(_, _) };
| TyKind::Str self.lower_call_and_args(func, args.iter().copied(), place, current, self.is_uninhabited(expr_id))
| TyKind::Foreign(_)
| TyKind::Slice(_) => {
return Err(MirLowerError::TypeError("function call on data type"))
} }
TyKind::Error => return Err(MirLowerError::MissingFunctionDefinition), TyKind::Error => return Err(MirLowerError::MissingFunctionDefinition),
TyKind::AssociatedType(_, _) _ => return Err(MirLowerError::TypeError("function call on bad type")),
| TyKind::Raw(_, _)
| TyKind::Ref(_, _, _)
| TyKind::OpaqueType(_, _)
| TyKind::Never
| TyKind::Closure(_, _)
| TyKind::Generator(_, _)
| TyKind::GeneratorWitness(_, _)
| TyKind::Placeholder(_)
| TyKind::Dyn(_)
| TyKind::Alias(_)
| TyKind::Function(_)
| TyKind::BoundVar(_)
| TyKind::InferenceVar(_, _) => not_supported!("dynamic function call"),
} }
} }
Expr::MethodCall { receiver, args, .. } => { Expr::MethodCall { receiver, args, .. } => {
@ -491,10 +534,7 @@ impl MirLowerCtx<'_> {
let cond_ty = self.expr_ty_after_adjustments(*expr); let cond_ty = self.expr_ty_after_adjustments(*expr);
let mut end = None; let mut end = None;
for MatchArm { pat, guard, expr } in arms.iter() { for MatchArm { pat, guard, expr } in arms.iter() {
if guard.is_some() { let (then, mut otherwise) = self.pattern_match(
not_supported!("pattern matching with guard");
}
let (then, otherwise) = self.pattern_match(
current, current,
None, None,
cond_place.clone(), cond_place.clone(),
@ -502,6 +542,16 @@ impl MirLowerCtx<'_> {
*pat, *pat,
BindingAnnotation::Unannotated, BindingAnnotation::Unannotated,
)?; )?;
let then = if let &Some(guard) = guard {
let next = self.new_basic_block();
let o = otherwise.get_or_insert_with(|| self.new_basic_block());
if let Some((discr, c)) = self.lower_expr_to_some_operand(guard, then)? {
self.set_terminator(c, Terminator::SwitchInt { discr, targets: SwitchTargets::static_if(1, next, *o) });
}
next
} else {
then
};
if let Some(block) = self.lower_expr_to_place(*expr, place.clone(), then)? { if let Some(block) = self.lower_expr_to_place(*expr, place.clone(), then)? {
let r = end.get_or_insert_with(|| self.new_basic_block()); let r = end.get_or_insert_with(|| self.new_basic_block());
self.set_goto(block, *r); self.set_goto(block, *r);
@ -524,24 +574,28 @@ impl MirLowerCtx<'_> {
Some(_) => not_supported!("continue with label"), Some(_) => not_supported!("continue with label"),
None => { None => {
let loop_data = let loop_data =
self.current_loop_blocks.ok_or(MirLowerError::ContinueWithoutLoop)?; self.current_loop_blocks.as_ref().ok_or(MirLowerError::ContinueWithoutLoop)?;
self.set_goto(current, loop_data.begin); self.set_goto(current, loop_data.begin);
Ok(None) Ok(None)
} }
}, },
Expr::Break { expr, label } => { Expr::Break { expr, label } => {
if expr.is_some() { if let Some(expr) = expr {
not_supported!("break with value"); let loop_data = match label {
} Some(l) => self.labeled_loop_blocks.get(l).ok_or(MirLowerError::UnresolvedLabel)?,
match label { None => self.current_loop_blocks.as_ref().ok_or(MirLowerError::BreakWithoutLoop)?,
Some(_) => not_supported!("break with label"), };
None => { let Some(c) = self.lower_expr_to_place(*expr, loop_data.place.clone(), current)? else {
let end = return Ok(None);
self.current_loop_end()?; };
self.set_goto(current, end); current = c;
Ok(None)
}
} }
let end = match label {
Some(l) => self.labeled_loop_blocks.get(l).ok_or(MirLowerError::UnresolvedLabel)?.end.expect("We always generate end for labeled loops"),
None => self.current_loop_end()?,
};
self.set_goto(current, end);
Ok(None)
} }
Expr::Return { expr } => { Expr::Return { expr } => {
if let Some(expr) = expr { if let Some(expr) = expr {
@ -555,7 +609,17 @@ impl MirLowerCtx<'_> {
Ok(None) Ok(None)
} }
Expr::Yield { .. } => not_supported!("yield"), Expr::Yield { .. } => not_supported!("yield"),
Expr::RecordLit { fields, path, .. } => { Expr::RecordLit { fields, path, spread, ellipsis: _, is_assignee_expr: _ } => {
let spread_place = match spread {
&Some(x) => {
let Some((p, c)) = self.lower_expr_as_place(current, x, true)? else {
return Ok(None);
};
current = c;
Some(p)
},
None => None,
};
let variant_id = self let variant_id = self
.infer .infer
.variant_resolution_for_expr(expr_id) .variant_resolution_for_expr(expr_id)
@ -585,9 +649,24 @@ impl MirLowerCtx<'_> {
place, place,
Rvalue::Aggregate( Rvalue::Aggregate(
AggregateKind::Adt(variant_id, subst), AggregateKind::Adt(variant_id, subst),
operands.into_iter().map(|x| x).collect::<Option<_>>().ok_or( match spread_place {
MirLowerError::TypeError("missing field in record literal"), Some(sp) => operands.into_iter().enumerate().map(|(i, x)| {
)?, match x {
Some(x) => x,
None => {
let mut p = sp.clone();
p.projection.push(ProjectionElem::Field(FieldId {
parent: variant_id,
local_id: LocalFieldId::from_raw(RawIdx::from(i as u32)),
}));
Operand::Copy(p)
},
}
}).collect(),
None => operands.into_iter().collect::<Option<_>>().ok_or(
MirLowerError::TypeError("missing field in record literal"),
)?,
},
), ),
expr_id.into(), expr_id.into(),
); );
@ -608,9 +687,7 @@ impl MirLowerCtx<'_> {
} }
} }
Expr::Await { .. } => not_supported!("await"), Expr::Await { .. } => not_supported!("await"),
Expr::Try { .. } => not_supported!("? operator"),
Expr::Yeet { .. } => not_supported!("yeet"), Expr::Yeet { .. } => not_supported!("yeet"),
Expr::TryBlock { .. } => not_supported!("try block"),
Expr::Async { .. } => not_supported!("async block"), Expr::Async { .. } => not_supported!("async block"),
Expr::Const { .. } => not_supported!("anonymous const block"), Expr::Const { .. } => not_supported!("anonymous const block"),
Expr::Cast { expr, type_ref: _ } => { Expr::Cast { expr, type_ref: _ } => {
@ -703,7 +780,49 @@ impl MirLowerCtx<'_> {
); );
Ok(Some(current)) Ok(Some(current))
} }
Expr::Range { .. } => not_supported!("range"), &Expr::Range { lhs, rhs, range_type: _ } => {
let ty = self.expr_ty(expr_id);
let Some((adt, subst)) = ty.as_adt() else {
return Err(MirLowerError::TypeError("Range type is not adt"));
};
let AdtId::StructId(st) = adt else {
return Err(MirLowerError::TypeError("Range type is not struct"));
};
let mut lp = None;
let mut rp = None;
if let Some(x) = lhs {
let Some((o, c)) = self.lower_expr_to_some_operand(x, current)? else {
return Ok(None);
};
lp = Some(o);
current = c;
}
if let Some(x) = rhs {
let Some((o, c)) = self.lower_expr_to_some_operand(x, current)? else {
return Ok(None);
};
rp = Some(o);
current = c;
}
self.push_assignment(
current,
place,
Rvalue::Aggregate(
AggregateKind::Adt(st.into(), subst.clone()),
self.db.struct_data(st).variant_data.fields().iter().map(|x| {
let o = match x.1.name.as_str() {
Some("start") => lp.take(),
Some("end") => rp.take(),
Some("exhausted") => Some(Operand::from_bytes(vec![0], TyBuilder::bool())),
_ => None,
};
o.ok_or(MirLowerError::UnresolvedField)
}).collect::<Result<_>>()?,
),
expr_id.into(),
);
Ok(Some(current))
},
Expr::Closure { .. } => not_supported!("closure"), Expr::Closure { .. } => not_supported!("closure"),
Expr::Tuple { exprs, is_assignee_expr: _ } => { Expr::Tuple { exprs, is_assignee_expr: _ } => {
let Some(values) = exprs let Some(values) = exprs
@ -832,9 +951,10 @@ impl MirLowerCtx<'_> {
const_id: hir_def::ConstId, const_id: hir_def::ConstId,
prev_block: BasicBlockId, prev_block: BasicBlockId,
place: Place, place: Place,
subst: Substitution,
span: MirSpan, span: MirSpan,
) -> Result<()> { ) -> Result<()> {
let c = self.db.const_eval(const_id)?; let c = self.db.const_eval(const_id, subst)?;
self.write_const_to_place(c, prev_block, place, span) self.write_const_to_place(c, prev_block, place, span)
} }
@ -872,7 +992,7 @@ impl MirLowerCtx<'_> {
) -> Result<BasicBlockId> { ) -> Result<BasicBlockId> {
let subst = match ty.kind(Interner) { let subst = match ty.kind(Interner) {
TyKind::Adt(_, subst) => subst.clone(), TyKind::Adt(_, subst) => subst.clone(),
_ => not_supported!("Non ADT enum"), _ => implementation_error!("Non ADT enum"),
}; };
self.push_assignment( self.push_assignment(
prev_block, prev_block,
@ -970,287 +1090,6 @@ impl MirLowerCtx<'_> {
self.push_statement(block, StatementKind::Assign(place, rvalue).with_span(span)); self.push_statement(block, StatementKind::Assign(place, rvalue).with_span(span));
} }
/// It gets a `current` unterminated block, appends some statements and possibly a terminator to it to check if
/// the pattern matches and write bindings, and returns two unterminated blocks, one for the matched path (which
/// can be the `current` block) and one for the mismatched path. If the input pattern is irrefutable, the
/// mismatched path block is `None`.
///
/// By default, it will create a new block for mismatched path. If you already have one, you can provide it with
/// `current_else` argument to save an unneccessary jump. If `current_else` isn't `None`, the result mismatched path
/// wouldn't be `None` as well. Note that this function will add jumps to the beginning of the `current_else` block,
/// so it should be an empty block.
fn pattern_match(
&mut self,
mut current: BasicBlockId,
mut current_else: Option<BasicBlockId>,
mut cond_place: Place,
mut cond_ty: Ty,
pattern: PatId,
mut binding_mode: BindingAnnotation,
) -> Result<(BasicBlockId, Option<BasicBlockId>)> {
Ok(match &self.body.pats[pattern] {
Pat::Missing => return Err(MirLowerError::IncompleteExpr),
Pat::Wild => (current, current_else),
Pat::Tuple { args, ellipsis } => {
pattern_matching_dereference(&mut cond_ty, &mut binding_mode, &mut cond_place);
let subst = match cond_ty.kind(Interner) {
TyKind::Tuple(_, s) => s,
_ => {
return Err(MirLowerError::TypeError(
"non tuple type matched with tuple pattern",
))
}
};
self.pattern_match_tuple_like(
current,
current_else,
args.iter().enumerate().map(|(i, x)| {
(
PlaceElem::TupleField(i),
*x,
subst.at(Interner, i).assert_ty_ref(Interner).clone(),
)
}),
*ellipsis,
&cond_place,
binding_mode,
)?
}
Pat::Or(pats) => {
let then_target = self.new_basic_block();
let mut finished = false;
for pat in &**pats {
let (next, next_else) = self.pattern_match(
current,
None,
cond_place.clone(),
cond_ty.clone(),
*pat,
binding_mode,
)?;
self.set_goto(next, then_target);
match next_else {
Some(t) => {
current = t;
}
None => {
finished = true;
break;
}
}
}
if !finished {
let ce = *current_else.get_or_insert_with(|| self.new_basic_block());
self.set_goto(current, ce);
}
(then_target, current_else)
}
Pat::Record { .. } => not_supported!("record pattern"),
Pat::Range { .. } => not_supported!("range pattern"),
Pat::Slice { .. } => not_supported!("slice pattern"),
Pat::Path(_) => {
let Some(variant) = self.infer.variant_resolution_for_pat(pattern) else {
not_supported!("unresolved variant");
};
self.pattern_matching_variant(
cond_ty,
binding_mode,
cond_place,
variant,
current,
pattern.into(),
current_else,
&[],
&None,
)?
}
Pat::Lit(l) => {
let then_target = self.new_basic_block();
let else_target = current_else.unwrap_or_else(|| self.new_basic_block());
match &self.body.exprs[*l] {
Expr::Literal(l) => match l {
hir_def::expr::Literal::Int(x, _) => {
self.set_terminator(
current,
Terminator::SwitchInt {
discr: Operand::Copy(cond_place),
targets: SwitchTargets::static_if(
*x as u128,
then_target,
else_target,
),
},
);
}
hir_def::expr::Literal::Uint(x, _) => {
self.set_terminator(
current,
Terminator::SwitchInt {
discr: Operand::Copy(cond_place),
targets: SwitchTargets::static_if(*x, then_target, else_target),
},
);
}
_ => not_supported!("non int path literal"),
},
_ => not_supported!("expression path literal"),
}
(then_target, Some(else_target))
}
Pat::Bind { id, subpat } => {
let target_place = self.result.binding_locals[*id];
let mode = self.body.bindings[*id].mode;
if let Some(subpat) = subpat {
(current, current_else) = self.pattern_match(
current,
current_else,
cond_place.clone(),
cond_ty,
*subpat,
binding_mode,
)?
}
if matches!(mode, BindingAnnotation::Ref | BindingAnnotation::RefMut) {
binding_mode = mode;
}
self.push_storage_live(*id, current);
self.push_assignment(
current,
target_place.into(),
match binding_mode {
BindingAnnotation::Unannotated | BindingAnnotation::Mutable => {
Operand::Copy(cond_place).into()
}
BindingAnnotation::Ref => Rvalue::Ref(BorrowKind::Shared, cond_place),
BindingAnnotation::RefMut => Rvalue::Ref(
BorrowKind::Mut { allow_two_phase_borrow: false },
cond_place,
),
},
pattern.into(),
);
(current, current_else)
}
Pat::TupleStruct { path: _, args, ellipsis } => {
let Some(variant) = self.infer.variant_resolution_for_pat(pattern) else {
not_supported!("unresolved variant");
};
self.pattern_matching_variant(
cond_ty,
binding_mode,
cond_place,
variant,
current,
pattern.into(),
current_else,
args,
ellipsis,
)?
}
Pat::Ref { .. } => not_supported!("& pattern"),
Pat::Box { .. } => not_supported!("box pattern"),
Pat::ConstBlock(_) => not_supported!("const block pattern"),
})
}
fn pattern_matching_variant(
&mut self,
mut cond_ty: Ty,
mut binding_mode: BindingAnnotation,
mut cond_place: Place,
variant: VariantId,
current: BasicBlockId,
span: MirSpan,
current_else: Option<BasicBlockId>,
args: &[PatId],
ellipsis: &Option<usize>,
) -> Result<(BasicBlockId, Option<BasicBlockId>)> {
pattern_matching_dereference(&mut cond_ty, &mut binding_mode, &mut cond_place);
let subst = match cond_ty.kind(Interner) {
TyKind::Adt(_, s) => s,
_ => return Err(MirLowerError::TypeError("non adt type matched with tuple struct")),
};
let fields_type = self.db.field_types(variant);
Ok(match variant {
VariantId::EnumVariantId(v) => {
let e = self.db.const_eval_discriminant(v)? as u128;
let next = self.new_basic_block();
let tmp = self.discr_temp_place();
self.push_assignment(
current,
tmp.clone(),
Rvalue::Discriminant(cond_place.clone()),
span,
);
let else_target = current_else.unwrap_or_else(|| self.new_basic_block());
self.set_terminator(
current,
Terminator::SwitchInt {
discr: Operand::Copy(tmp),
targets: SwitchTargets::static_if(e, next, else_target),
},
);
let enum_data = self.db.enum_data(v.parent);
let fields =
enum_data.variants[v.local_id].variant_data.fields().iter().map(|(x, _)| {
(
PlaceElem::Field(FieldId { parent: v.into(), local_id: x }),
fields_type[x].clone().substitute(Interner, subst),
)
});
self.pattern_match_tuple_like(
next,
Some(else_target),
args.iter().zip(fields).map(|(x, y)| (y.0, *x, y.1)),
*ellipsis,
&cond_place,
binding_mode,
)?
}
VariantId::StructId(s) => {
let struct_data = self.db.struct_data(s);
let fields = struct_data.variant_data.fields().iter().map(|(x, _)| {
(
PlaceElem::Field(FieldId { parent: s.into(), local_id: x }),
fields_type[x].clone().substitute(Interner, subst),
)
});
self.pattern_match_tuple_like(
current,
current_else,
args.iter().zip(fields).map(|(x, y)| (y.0, *x, y.1)),
*ellipsis,
&cond_place,
binding_mode,
)?
}
VariantId::UnionId(_) => {
return Err(MirLowerError::TypeError("pattern matching on union"))
}
})
}
fn pattern_match_tuple_like(
&mut self,
mut current: BasicBlockId,
mut current_else: Option<BasicBlockId>,
args: impl Iterator<Item = (PlaceElem, PatId, Ty)>,
ellipsis: Option<usize>,
cond_place: &Place,
binding_mode: BindingAnnotation,
) -> Result<(BasicBlockId, Option<BasicBlockId>)> {
if ellipsis.is_some() {
not_supported!("tuple like pattern with ellipsis");
}
for (proj, arg, ty) in args {
let mut cond_place = cond_place.clone();
cond_place.projection.push(proj);
(current, current_else) =
self.pattern_match(current, current_else, cond_place, ty, arg, binding_mode)?;
}
Ok((current, current_else))
}
fn discr_temp_place(&mut self) -> Place { fn discr_temp_place(&mut self) -> Place {
match &self.discr_temp { match &self.discr_temp {
Some(x) => x.clone(), Some(x) => x.clone(),
@ -1266,19 +1105,34 @@ impl MirLowerCtx<'_> {
fn lower_loop( fn lower_loop(
&mut self, &mut self,
prev_block: BasicBlockId, prev_block: BasicBlockId,
place: Place,
label: Option<LabelId>, label: Option<LabelId>,
f: impl FnOnce(&mut MirLowerCtx<'_>, BasicBlockId) -> Result<()>, f: impl FnOnce(&mut MirLowerCtx<'_>, BasicBlockId) -> Result<()>,
) -> Result<Option<BasicBlockId>> { ) -> Result<Option<BasicBlockId>> {
if label.is_some() {
not_supported!("loop with label");
}
let begin = self.new_basic_block(); let begin = self.new_basic_block();
let prev = let prev = mem::replace(
mem::replace(&mut self.current_loop_blocks, Some(LoopBlocks { begin, end: None })); &mut self.current_loop_blocks,
Some(LoopBlocks { begin, end: None, place }),
);
let prev_label = if let Some(label) = label {
// We should generate the end now, to make sure that it wouldn't change later. It is
// bad as we may emit end (unneccessary unreachable block) for unterminating loop, but
// it should not affect correctness.
self.current_loop_end()?;
self.labeled_loop_blocks.insert(
self.body.labels[label].name.clone(),
self.current_loop_blocks.as_ref().unwrap().clone(),
)
} else {
None
};
self.set_goto(prev_block, begin); self.set_goto(prev_block, begin);
f(self, begin)?; f(self, begin)?;
let my = mem::replace(&mut self.current_loop_blocks, prev) let my = mem::replace(&mut self.current_loop_blocks, prev)
.ok_or(MirLowerError::ImplementationError("current_loop_blocks is corrupt"))?; .ok_or(MirLowerError::ImplementationError("current_loop_blocks is corrupt"))?;
if let Some(prev) = prev_label {
self.labeled_loop_blocks.insert(self.body.labels[label.unwrap()].name.clone(), prev);
}
Ok(my.end) Ok(my.end)
} }
@ -1366,15 +1220,11 @@ impl MirLowerCtx<'_> {
fn lower_block_to_place( fn lower_block_to_place(
&mut self, &mut self,
label: Option<LabelId>,
statements: &[hir_def::expr::Statement], statements: &[hir_def::expr::Statement],
mut current: BasicBlockId, mut current: BasicBlockId,
tail: Option<ExprId>, tail: Option<ExprId>,
place: Place, place: Place,
) -> Result<Option<Idx<BasicBlock>>> { ) -> Result<Option<Idx<BasicBlock>>> {
if label.is_some() {
not_supported!("block with label");
}
for statement in statements.iter() { for statement in statements.iter() {
match statement { match statement {
hir_def::expr::Statement::Let { pat, initializer, else_branch, type_ref: _ } => { hir_def::expr::Statement::Let { pat, initializer, else_branch, type_ref: _ } => {
@ -1428,22 +1278,6 @@ impl MirLowerCtx<'_> {
} }
} }
fn pattern_matching_dereference(
cond_ty: &mut Ty,
binding_mode: &mut BindingAnnotation,
cond_place: &mut Place,
) {
while let Some((ty, _, mu)) = cond_ty.as_reference() {
if mu == Mutability::Mut && *binding_mode != BindingAnnotation::Ref {
*binding_mode = BindingAnnotation::RefMut;
} else {
*binding_mode = BindingAnnotation::Ref;
}
*cond_ty = ty.clone();
cond_place.projection.push(ProjectionElem::Deref);
}
}
fn cast_kind(source_ty: &Ty, target_ty: &Ty) -> Result<CastKind> { fn cast_kind(source_ty: &Ty, target_ty: &Ty) -> Result<CastKind> {
Ok(match (source_ty.kind(Interner), target_ty.kind(Interner)) { Ok(match (source_ty.kind(Interner), target_ty.kind(Interner)) {
(TyKind::Scalar(s), TyKind::Scalar(t)) => match (s, t) { (TyKind::Scalar(s), TyKind::Scalar(t)) => match (s, t) {
@ -1452,6 +1286,11 @@ fn cast_kind(source_ty: &Ty, target_ty: &Ty) -> Result<CastKind> {
(_, chalk_ir::Scalar::Float(_)) => CastKind::IntToFloat, (_, chalk_ir::Scalar::Float(_)) => CastKind::IntToFloat,
(_, _) => CastKind::IntToInt, (_, _) => CastKind::IntToInt,
}, },
(TyKind::Scalar(_), TyKind::Raw(..)) => CastKind::PointerFromExposedAddress,
(TyKind::Raw(..), TyKind::Scalar(_)) => CastKind::PointerExposeAddress,
(TyKind::Raw(..) | TyKind::Ref(..), TyKind::Raw(..) | TyKind::Ref(..)) => {
CastKind::PtrToPtr
}
// Enum to int casts // Enum to int casts
(TyKind::Scalar(_), TyKind::Adt(..)) | (TyKind::Adt(..), TyKind::Scalar(_)) => { (TyKind::Scalar(_), TyKind::Adt(..)) | (TyKind::Adt(..), TyKind::Scalar(_)) => {
CastKind::IntToInt CastKind::IntToInt
@ -1552,6 +1391,7 @@ pub fn lower_to_mir(
body, body,
owner, owner,
current_loop_blocks: None, current_loop_blocks: None,
labeled_loop_blocks: Default::default(),
discr_temp: None, discr_temp: None,
}; };
let mut current = start_block; let mut current = start_block;

View file

@ -1,6 +1,7 @@
//! MIR lowering for places //! MIR lowering for places
use super::*; use super::*;
use hir_def::FunctionId;
use hir_expand::name; use hir_expand::name;
macro_rules! not_supported { macro_rules! not_supported {
@ -125,7 +126,7 @@ impl MirLowerCtx<'_> {
match &self.body.exprs[expr_id] { match &self.body.exprs[expr_id] {
Expr::Path(p) => { Expr::Path(p) => {
let resolver = resolver_for_expr(self.db.upcast(), self.owner, expr_id); let resolver = resolver_for_expr(self.db.upcast(), self.owner, expr_id);
let Some(pr) = resolver.resolve_path_in_value_ns(self.db.upcast(), p.mod_path()) else { let Some(pr) = resolver.resolve_path_in_value_ns(self.db.upcast(), p) else {
return Err(MirLowerError::unresolved_path(self.db, p)); return Err(MirLowerError::unresolved_path(self.db, p));
}; };
let pr = match pr { let pr = match pr {
@ -145,10 +146,32 @@ impl MirLowerCtx<'_> {
self.expr_ty(*expr).kind(Interner), self.expr_ty(*expr).kind(Interner),
TyKind::Ref(..) | TyKind::Raw(..) TyKind::Ref(..) | TyKind::Raw(..)
) { ) {
let Some(_) = self.lower_expr_as_place(current, *expr, true)? else { let Some((p, current)) = self.lower_expr_as_place(current, *expr, true)? else {
return Ok(None); return Ok(None);
}; };
not_supported!("explicit overloaded deref"); return self.lower_overloaded_deref(
current,
p,
self.expr_ty_after_adjustments(*expr),
self.expr_ty(expr_id),
expr_id.into(),
'b: {
if let Some((f, _)) = self.infer.method_resolution(expr_id) {
if let Some(deref_trait) =
self.resolve_lang_item(LangItem::DerefMut)?.as_trait()
{
if let Some(deref_fn) = self
.db
.trait_data(deref_trait)
.method_by_name(&name![deref_mut])
{
break 'b deref_fn == f;
}
}
}
false
},
);
} }
let Some((mut r, current)) = self.lower_expr_as_place(current, *expr, true)? else { let Some((mut r, current)) = self.lower_expr_as_place(current, *expr, true)? else {
return Ok(None); return Ok(None);
@ -171,7 +194,24 @@ impl MirLowerCtx<'_> {
if index_ty != TyBuilder::usize() if index_ty != TyBuilder::usize()
|| !matches!(base_ty.kind(Interner), TyKind::Array(..) | TyKind::Slice(..)) || !matches!(base_ty.kind(Interner), TyKind::Array(..) | TyKind::Slice(..))
{ {
not_supported!("overloaded index"); let Some(index_fn) = self.infer.method_resolution(expr_id) else {
return Err(MirLowerError::UnresolvedMethod);
};
let Some((base_place, current)) = self.lower_expr_as_place(current, *base, true)? else {
return Ok(None);
};
let Some((index_operand, current)) = self.lower_expr_to_some_operand(*index, current)? else {
return Ok(None);
};
return self.lower_overloaded_index(
current,
base_place,
self.expr_ty_after_adjustments(*base),
self.expr_ty(expr_id),
index_operand,
expr_id.into(),
index_fn,
);
} }
let Some((mut p_base, current)) = let Some((mut p_base, current)) =
self.lower_expr_as_place(current, *base, true)? else { self.lower_expr_as_place(current, *base, true)? else {
@ -188,6 +228,49 @@ impl MirLowerCtx<'_> {
} }
} }
fn lower_overloaded_index(
&mut self,
current: BasicBlockId,
place: Place,
base_ty: Ty,
result_ty: Ty,
index_operand: Operand,
span: MirSpan,
index_fn: (FunctionId, Substitution),
) -> Result<Option<(Place, BasicBlockId)>> {
let is_mutable = 'b: {
if let Some(index_mut_trait) = self.resolve_lang_item(LangItem::IndexMut)?.as_trait() {
if let Some(index_mut_fn) =
self.db.trait_data(index_mut_trait).method_by_name(&name![index_mut])
{
break 'b index_mut_fn == index_fn.0;
}
}
false
};
let (mutability, borrow_kind) = match is_mutable {
true => (Mutability::Mut, BorrowKind::Mut { allow_two_phase_borrow: false }),
false => (Mutability::Not, BorrowKind::Shared),
};
let base_ref = TyKind::Ref(mutability, static_lifetime(), base_ty).intern(Interner);
let result_ref = TyKind::Ref(mutability, static_lifetime(), result_ty).intern(Interner);
let ref_place: Place = self.temp(base_ref)?.into();
self.push_assignment(current, ref_place.clone(), Rvalue::Ref(borrow_kind, place), span);
let mut result: Place = self.temp(result_ref)?.into();
let index_fn_op = Operand::const_zst(
TyKind::FnDef(
self.db.intern_callable_def(CallableDefId::FunctionId(index_fn.0)).into(),
index_fn.1,
)
.intern(Interner),
);
let Some(current) = self.lower_call(index_fn_op, vec![Operand::Copy(ref_place), index_operand], result.clone(), current, false)? else {
return Ok(None);
};
result.projection.push(ProjectionElem::Deref);
Ok(Some((result, current)))
}
fn lower_overloaded_deref( fn lower_overloaded_deref(
&mut self, &mut self,
current: BasicBlockId, current: BasicBlockId,

View file

@ -0,0 +1,399 @@
//! MIR lowering for patterns
use super::*;
macro_rules! not_supported {
($x: expr) => {
return Err(MirLowerError::NotSupported(format!($x)))
};
}
pub(super) enum AdtPatternShape<'a> {
Tuple { args: &'a [PatId], ellipsis: Option<usize> },
Record { args: &'a [RecordFieldPat] },
Unit,
}
impl MirLowerCtx<'_> {
/// It gets a `current` unterminated block, appends some statements and possibly a terminator to it to check if
/// the pattern matches and write bindings, and returns two unterminated blocks, one for the matched path (which
/// can be the `current` block) and one for the mismatched path. If the input pattern is irrefutable, the
/// mismatched path block is `None`.
///
/// By default, it will create a new block for mismatched path. If you already have one, you can provide it with
/// `current_else` argument to save an unneccessary jump. If `current_else` isn't `None`, the result mismatched path
/// wouldn't be `None` as well. Note that this function will add jumps to the beginning of the `current_else` block,
/// so it should be an empty block.
pub(super) fn pattern_match(
&mut self,
mut current: BasicBlockId,
mut current_else: Option<BasicBlockId>,
mut cond_place: Place,
mut cond_ty: Ty,
pattern: PatId,
mut binding_mode: BindingAnnotation,
) -> Result<(BasicBlockId, Option<BasicBlockId>)> {
Ok(match &self.body.pats[pattern] {
Pat::Missing => return Err(MirLowerError::IncompleteExpr),
Pat::Wild => (current, current_else),
Pat::Tuple { args, ellipsis } => {
pattern_matching_dereference(&mut cond_ty, &mut binding_mode, &mut cond_place);
let subst = match cond_ty.kind(Interner) {
TyKind::Tuple(_, s) => s,
_ => {
return Err(MirLowerError::TypeError(
"non tuple type matched with tuple pattern",
))
}
};
self.pattern_match_tuple_like(
current,
current_else,
args,
*ellipsis,
subst.iter(Interner).enumerate().map(|(i, x)| {
(PlaceElem::TupleField(i), x.assert_ty_ref(Interner).clone())
}),
&cond_place,
binding_mode,
)?
}
Pat::Or(pats) => {
let then_target = self.new_basic_block();
let mut finished = false;
for pat in &**pats {
let (next, next_else) = self.pattern_match(
current,
None,
cond_place.clone(),
cond_ty.clone(),
*pat,
binding_mode,
)?;
self.set_goto(next, then_target);
match next_else {
Some(t) => {
current = t;
}
None => {
finished = true;
break;
}
}
}
if !finished {
let ce = *current_else.get_or_insert_with(|| self.new_basic_block());
self.set_goto(current, ce);
}
(then_target, current_else)
}
Pat::Record { args, .. } => {
let Some(variant) = self.infer.variant_resolution_for_pat(pattern) else {
not_supported!("unresolved variant");
};
self.pattern_matching_variant(
cond_ty,
binding_mode,
cond_place,
variant,
current,
pattern.into(),
current_else,
AdtPatternShape::Record { args: &*args },
)?
}
Pat::Range { .. } => not_supported!("range pattern"),
Pat::Slice { .. } => not_supported!("slice pattern"),
Pat::Path(_) => {
let Some(variant) = self.infer.variant_resolution_for_pat(pattern) else {
not_supported!("unresolved variant");
};
self.pattern_matching_variant(
cond_ty,
binding_mode,
cond_place,
variant,
current,
pattern.into(),
current_else,
AdtPatternShape::Unit,
)?
}
Pat::Lit(l) => match &self.body.exprs[*l] {
Expr::Literal(l) => {
let c = self.lower_literal_to_operand(cond_ty, l)?;
self.pattern_match_const(current_else, current, c, cond_place, pattern)?
}
_ => not_supported!("expression path literal"),
},
Pat::Bind { id, subpat } => {
let target_place = self.result.binding_locals[*id];
let mode = self.body.bindings[*id].mode;
if let Some(subpat) = subpat {
(current, current_else) = self.pattern_match(
current,
current_else,
cond_place.clone(),
cond_ty,
*subpat,
binding_mode,
)?
}
if matches!(mode, BindingAnnotation::Ref | BindingAnnotation::RefMut) {
binding_mode = mode;
}
self.push_storage_live(*id, current);
self.push_assignment(
current,
target_place.into(),
match binding_mode {
BindingAnnotation::Unannotated | BindingAnnotation::Mutable => {
Operand::Copy(cond_place).into()
}
BindingAnnotation::Ref => Rvalue::Ref(BorrowKind::Shared, cond_place),
BindingAnnotation::RefMut => Rvalue::Ref(
BorrowKind::Mut { allow_two_phase_borrow: false },
cond_place,
),
},
pattern.into(),
);
(current, current_else)
}
Pat::TupleStruct { path: _, args, ellipsis } => {
let Some(variant) = self.infer.variant_resolution_for_pat(pattern) else {
not_supported!("unresolved variant");
};
self.pattern_matching_variant(
cond_ty,
binding_mode,
cond_place,
variant,
current,
pattern.into(),
current_else,
AdtPatternShape::Tuple { args, ellipsis: *ellipsis },
)?
}
Pat::Ref { pat, mutability: _ } => {
if let Some((ty, _, _)) = cond_ty.as_reference() {
cond_ty = ty.clone();
cond_place.projection.push(ProjectionElem::Deref);
self.pattern_match(
current,
current_else,
cond_place,
cond_ty,
*pat,
binding_mode,
)?
} else {
return Err(MirLowerError::TypeError("& pattern for non reference"));
}
}
Pat::Box { .. } => not_supported!("box pattern"),
Pat::ConstBlock(_) => not_supported!("const block pattern"),
})
}
fn pattern_match_const(
&mut self,
current_else: Option<BasicBlockId>,
current: BasicBlockId,
c: Operand,
cond_place: Place,
pattern: Idx<Pat>,
) -> Result<(BasicBlockId, Option<BasicBlockId>)> {
let then_target = self.new_basic_block();
let else_target = current_else.unwrap_or_else(|| self.new_basic_block());
let discr: Place = self.temp(TyBuilder::bool())?.into();
self.push_assignment(
current,
discr.clone(),
Rvalue::CheckedBinaryOp(BinOp::Eq, c, Operand::Copy(cond_place)),
pattern.into(),
);
let discr = Operand::Copy(discr);
self.set_terminator(
current,
Terminator::SwitchInt {
discr,
targets: SwitchTargets::static_if(1, then_target, else_target),
},
);
Ok((then_target, Some(else_target)))
}
pub(super) fn pattern_matching_variant(
&mut self,
mut cond_ty: Ty,
mut binding_mode: BindingAnnotation,
mut cond_place: Place,
variant: VariantId,
current: BasicBlockId,
span: MirSpan,
current_else: Option<BasicBlockId>,
shape: AdtPatternShape<'_>,
) -> Result<(BasicBlockId, Option<BasicBlockId>)> {
pattern_matching_dereference(&mut cond_ty, &mut binding_mode, &mut cond_place);
let subst = match cond_ty.kind(Interner) {
TyKind::Adt(_, s) => s,
_ => return Err(MirLowerError::TypeError("non adt type matched with tuple struct")),
};
Ok(match variant {
VariantId::EnumVariantId(v) => {
let e = self.db.const_eval_discriminant(v)? as u128;
let next = self.new_basic_block();
let tmp = self.discr_temp_place();
self.push_assignment(
current,
tmp.clone(),
Rvalue::Discriminant(cond_place.clone()),
span,
);
let else_target = current_else.unwrap_or_else(|| self.new_basic_block());
self.set_terminator(
current,
Terminator::SwitchInt {
discr: Operand::Copy(tmp),
targets: SwitchTargets::static_if(e, next, else_target),
},
);
let enum_data = self.db.enum_data(v.parent);
self.pattern_matching_variant_fields(
shape,
&enum_data.variants[v.local_id].variant_data,
variant,
subst,
next,
Some(else_target),
&cond_place,
binding_mode,
)?
}
VariantId::StructId(s) => {
let struct_data = self.db.struct_data(s);
self.pattern_matching_variant_fields(
shape,
&struct_data.variant_data,
variant,
subst,
current,
current_else,
&cond_place,
binding_mode,
)?
}
VariantId::UnionId(_) => {
return Err(MirLowerError::TypeError("pattern matching on union"))
}
})
}
fn pattern_matching_variant_fields(
&mut self,
shape: AdtPatternShape<'_>,
variant_data: &VariantData,
v: VariantId,
subst: &Substitution,
current: BasicBlockId,
current_else: Option<BasicBlockId>,
cond_place: &Place,
binding_mode: BindingAnnotation,
) -> Result<(BasicBlockId, Option<BasicBlockId>)> {
let fields_type = self.db.field_types(v);
Ok(match shape {
AdtPatternShape::Record { args } => {
let it = args
.iter()
.map(|x| {
let field_id =
variant_data.field(&x.name).ok_or(MirLowerError::UnresolvedField)?;
Ok((
PlaceElem::Field(FieldId { parent: v.into(), local_id: field_id }),
x.pat,
fields_type[field_id].clone().substitute(Interner, subst),
))
})
.collect::<Result<Vec<_>>>()?;
self.pattern_match_adt(
current,
current_else,
it.into_iter(),
cond_place,
binding_mode,
)?
}
AdtPatternShape::Tuple { args, ellipsis } => {
let fields = variant_data.fields().iter().map(|(x, _)| {
(
PlaceElem::Field(FieldId { parent: v.into(), local_id: x }),
fields_type[x].clone().substitute(Interner, subst),
)
});
self.pattern_match_tuple_like(
current,
current_else,
args,
ellipsis,
fields,
cond_place,
binding_mode,
)?
}
AdtPatternShape::Unit => (current, current_else),
})
}
fn pattern_match_adt(
&mut self,
mut current: BasicBlockId,
mut current_else: Option<BasicBlockId>,
args: impl Iterator<Item = (PlaceElem, PatId, Ty)>,
cond_place: &Place,
binding_mode: BindingAnnotation,
) -> Result<(BasicBlockId, Option<BasicBlockId>)> {
for (proj, arg, ty) in args {
let mut cond_place = cond_place.clone();
cond_place.projection.push(proj);
(current, current_else) =
self.pattern_match(current, current_else, cond_place, ty, arg, binding_mode)?;
}
Ok((current, current_else))
}
fn pattern_match_tuple_like(
&mut self,
current: BasicBlockId,
current_else: Option<BasicBlockId>,
args: &[PatId],
ellipsis: Option<usize>,
fields: impl DoubleEndedIterator<Item = (PlaceElem, Ty)> + Clone,
cond_place: &Place,
binding_mode: BindingAnnotation,
) -> Result<(BasicBlockId, Option<BasicBlockId>)> {
let (al, ar) = args.split_at(ellipsis.unwrap_or(args.len()));
let it = al
.iter()
.zip(fields.clone())
.chain(ar.iter().rev().zip(fields.rev()))
.map(|(x, y)| (y.0, *x, y.1));
self.pattern_match_adt(current, current_else, it, cond_place, binding_mode)
}
}
fn pattern_matching_dereference(
cond_ty: &mut Ty,
binding_mode: &mut BindingAnnotation,
cond_place: &mut Place,
) {
while let Some((ty, _, mu)) = cond_ty.as_reference() {
if mu == Mutability::Mut && *binding_mode != BindingAnnotation::Ref {
*binding_mode = BindingAnnotation::RefMut;
} else {
*binding_mode = BindingAnnotation::Ref;
}
*cond_ty = ty.clone();
cond_place.projection.push(ProjectionElem::Deref);
}
}

View file

@ -1,6 +1,6 @@
//! A pretty-printer for MIR. //! A pretty-printer for MIR.
use std::fmt::{Display, Write}; use std::fmt::{Debug, Display, Write};
use hir_def::{body::Body, expr::BindingId}; use hir_def::{body::Body, expr::BindingId};
use hir_expand::name::Name; use hir_expand::name::Name;
@ -23,6 +23,18 @@ impl MirBody {
ctx.for_body(); ctx.for_body();
ctx.result ctx.result
} }
// String with lines is rendered poorly in `dbg` macros, which I use very much, so this
// function exists to solve that.
pub fn dbg(&self, db: &dyn HirDatabase) -> impl Debug {
struct StringDbg(String);
impl Debug for StringDbg {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(&self.0)
}
}
StringDbg(self.pretty_print(db))
}
} }
struct MirPrettyCtx<'a> { struct MirPrettyCtx<'a> {
@ -77,6 +89,7 @@ impl Display for LocalName {
impl<'a> MirPrettyCtx<'a> { impl<'a> MirPrettyCtx<'a> {
fn for_body(&mut self) { fn for_body(&mut self) {
wln!(self, "// {:?}", self.body.owner);
self.with_block(|this| { self.with_block(|this| {
this.locals(); this.locals();
wln!(this); wln!(this);
@ -300,9 +313,9 @@ impl<'a> MirPrettyCtx<'a> {
w!(self, ")"); w!(self, ")");
} }
Rvalue::Cast(ck, op, ty) => { Rvalue::Cast(ck, op, ty) => {
w!(self, "Discriminant({ck:?}"); w!(self, "Cast({ck:?}, ");
self.operand(op); self.operand(op);
w!(self, "{})", ty.display(self.db)); w!(self, ", {})", ty.display(self.db));
} }
Rvalue::CheckedBinaryOp(b, o1, o2) => { Rvalue::CheckedBinaryOp(b, o1, o2) => {
self.operand(o1); self.operand(o1);

View file

@ -258,7 +258,6 @@ fn test() {
#[test] #[test]
fn coerce_autoderef_block() { fn coerce_autoderef_block() {
// FIXME: We should know mutability in overloaded deref
check_no_mismatches( check_no_mismatches(
r#" r#"
//- minicore: deref //- minicore: deref
@ -268,7 +267,7 @@ fn takes_ref_str(x: &str) {}
fn returns_string() -> String { loop {} } fn returns_string() -> String { loop {} }
fn test() { fn test() {
takes_ref_str(&{ returns_string() }); takes_ref_str(&{ returns_string() });
// ^^^^^^^^^^^^^^^^^^^^^ adjustments: Deref(None), Deref(Some(OverloadedDeref(None))), Borrow(Ref(Not)) // ^^^^^^^^^^^^^^^^^^^^^ adjustments: Deref(None), Deref(Some(OverloadedDeref(Some(Not)))), Borrow(Ref(Not))
} }
"#, "#,
); );
@ -396,10 +395,40 @@ fn test() {
); );
} }
#[test]
fn coerce_fn_item_to_fn_ptr_in_array() {
check_no_mismatches(
r"
fn foo(x: u32) -> isize { 1 }
fn bar(x: u32) -> isize { 1 }
fn test() {
let f = [foo, bar];
// ^^^ adjustments: Pointer(ReifyFnPointer)
}",
);
}
#[test] #[test]
fn coerce_fn_items_in_match_arms() { fn coerce_fn_items_in_match_arms() {
cov_mark::check!(coerce_fn_reification); cov_mark::check!(coerce_fn_reification);
check_no_mismatches(
r"
fn foo1(x: u32) -> isize { 1 }
fn foo2(x: u32) -> isize { 2 }
fn foo3(x: u32) -> isize { 3 }
fn test() {
let x = match 1 {
1 => foo1,
// ^^^^ adjustments: Pointer(ReifyFnPointer)
2 => foo2,
// ^^^^ adjustments: Pointer(ReifyFnPointer)
_ => foo3,
// ^^^^ adjustments: Pointer(ReifyFnPointer)
};
x;
}",
);
check_types( check_types(
r" r"
fn foo1(x: u32) -> isize { 1 } fn foo1(x: u32) -> isize { 1 }

View file

@ -1255,7 +1255,6 @@ fn foo<T: Trait>(a: &T) {
#[test] #[test]
fn autoderef_visibility_field() { fn autoderef_visibility_field() {
// FIXME: We should know mutability in overloaded deref
check( check(
r#" r#"
//- minicore: deref //- minicore: deref
@ -1277,7 +1276,7 @@ mod a {
mod b { mod b {
fn foo() { fn foo() {
let x = super::a::Bar::new().0; let x = super::a::Bar::new().0;
// ^^^^^^^^^^^^^^^^^^^^ adjustments: Deref(Some(OverloadedDeref(None))) // ^^^^^^^^^^^^^^^^^^^^ adjustments: Deref(Some(OverloadedDeref(Some(Not))))
// ^^^^^^^^^^^^^^^^^^^^^^ type: char // ^^^^^^^^^^^^^^^^^^^^^^ type: char
} }
} }

View file

@ -2696,6 +2696,21 @@ fn f() {
) )
} }
#[test]
fn infer_ref_to_raw_cast() {
check_types(
r#"
struct S;
fn f() {
let s = &mut S;
let s = s as *mut _;
//^ *mut S
}
"#,
);
}
#[test] #[test]
fn infer_missing_type() { fn infer_missing_type() {
check_types( check_types(
@ -3258,25 +3273,6 @@ fn f<T>(t: Ark<T>) {
); );
} }
// FIXME
#[test]
fn castable_to2() {
check_infer(
r#"
fn func() {
let x = &0u32 as *const _;
}
"#,
expect![[r#"
10..44 '{ ...t _; }': ()
20..21 'x': *const {unknown}
24..29 '&0u32': &u32
24..41 '&0u32 ...onst _': *const {unknown}
25..29 '0u32': u32
"#]],
);
}
#[test] #[test]
fn issue_14275() { fn issue_14275() {
// FIXME: evaluate const generic // FIXME: evaluate const generic

View file

@ -206,19 +206,27 @@ fn test() {
fn infer_try_trait() { fn infer_try_trait() {
check_types( check_types(
r#" r#"
//- minicore: try, result //- minicore: try, result, from
fn test() { fn test() {
let r: Result<i32, u64> = Result::Ok(1); let r: Result<i32, u64> = Result::Ok(1);
let v = r?; let v = r?;
v; v;
} //^ i32 } //^ i32
"#,
impl<O, E> core::ops::Try for Result<O, E> { );
type Output = O;
type Error = Result<core::convert::Infallible, E>;
} }
impl<T, E, F: From<E>> core::ops::FromResidual<Result<core::convert::Infallible, E>> for Result<T, F> {} #[test]
fn infer_try_block() {
// FIXME: We should test more cases, but it currently doesn't work, since
// our labeled block type inference is broken.
check_types(
r#"
//- minicore: try, option
fn test() {
let x: Option<_> = try { Some(2)?; };
//^ Option<()>
}
"#, "#,
); );
} }

View file

@ -11,6 +11,7 @@ use hir_def::{
lang_item::{LangItem, LangItemTarget}, lang_item::{LangItem, LangItemTarget},
TraitId, TraitId,
}; };
use hir_expand::name::{name, Name};
use stdx::panic_context; use stdx::panic_context;
use crate::{ use crate::{
@ -187,7 +188,15 @@ impl FnTrait {
} }
} }
pub fn get_id(&self, db: &dyn HirDatabase, krate: CrateId) -> Option<TraitId> { pub fn method_name(self) -> Name {
match self {
FnTrait::FnOnce => name!(call_once),
FnTrait::FnMut => name!(call_mut),
FnTrait::Fn => name!(call),
}
}
pub fn get_id(self, db: &dyn HirDatabase, krate: CrateId) -> Option<TraitId> {
let target = db.lang_item(krate, self.lang_item())?; let target = db.lang_item(krate, self.lang_item())?;
match target { match target {
LangItemTarget::Trait(t) => Some(t), LangItemTarget::Trait(t) => Some(t),

View file

@ -130,7 +130,7 @@ fn direct_super_traits(db: &dyn DefDatabase, trait_: TraitId, cb: impl FnMut(Tra
WherePredicate::Lifetime { .. } => None, WherePredicate::Lifetime { .. } => None,
}) })
.filter(|(_, bound_modifier)| matches!(bound_modifier, TraitBoundModifier::None)) .filter(|(_, bound_modifier)| matches!(bound_modifier, TraitBoundModifier::None))
.filter_map(|(path, _)| match resolver.resolve_path_in_type_ns_fully(db, path.mod_path()) { .filter_map(|(path, _)| match resolver.resolve_path_in_type_ns_fully(db, path) {
Some(TypeNs::TraitId(t)) => Some(t), Some(TypeNs::TraitId(t)) => Some(t),
_ => None, _ => None,
}) })

View file

@ -1801,7 +1801,7 @@ impl Function {
let body = db let body = db
.mir_body(self.id.into()) .mir_body(self.id.into())
.map_err(|e| MirEvalError::MirLowerError(self.id.into(), e))?; .map_err(|e| MirEvalError::MirLowerError(self.id.into(), e))?;
interpret_mir(db, &body, false)?; interpret_mir(db, &body, Substitution::empty(Interner), false)?;
Ok(()) Ok(())
} }
} }
@ -1947,7 +1947,7 @@ impl Const {
} }
pub fn render_eval(self, db: &dyn HirDatabase) -> Result<String, ConstEvalError> { pub fn render_eval(self, db: &dyn HirDatabase) -> Result<String, ConstEvalError> {
let c = db.const_eval(self.id)?; let c = db.const_eval(self.id, Substitution::empty(Interner))?;
let r = format!("{}", HexifiedConst(c).display(db)); let r = format!("{}", HexifiedConst(c).display(db));
// We want to see things like `<utf8-error>` and `<layout-error>` as they are probably bug in our // We want to see things like `<utf8-error>` and `<layout-error>` as they are probably bug in our
// implementation, but there is no need to show things like `<enum-not-supported>` or `<ref-not-supported>` to // implementation, but there is no need to show things like `<enum-not-supported>` or `<ref-not-supported>` to

View file

@ -1076,10 +1076,7 @@ impl<'db> SemanticsImpl<'db> {
let hygiene = hir_expand::hygiene::Hygiene::new(self.db.upcast(), analyze.file_id); let hygiene = hir_expand::hygiene::Hygiene::new(self.db.upcast(), analyze.file_id);
let ctx = body::LowerCtx::with_hygiene(self.db.upcast(), &hygiene); let ctx = body::LowerCtx::with_hygiene(self.db.upcast(), &hygiene);
let hir_path = Path::from_src(path.clone(), &ctx)?; let hir_path = Path::from_src(path.clone(), &ctx)?;
match analyze match analyze.resolver.resolve_path_in_type_ns_fully(self.db.upcast(), &hir_path)? {
.resolver
.resolve_path_in_type_ns_fully(self.db.upcast(), hir_path.mod_path())?
{
TypeNs::TraitId(id) => Some(Trait { id }), TypeNs::TraitId(id) => Some(Trait { id }),
_ => None, _ => None,
} }

View file

@ -420,7 +420,10 @@ impl SourceAnalyzer {
None None
} else { } else {
// Shorthand syntax, resolve to the local // Shorthand syntax, resolve to the local
let path = ModPath::from_segments(PathKind::Plain, once(local_name.clone())); let path = Path::from_known_path_with_no_generic(ModPath::from_segments(
PathKind::Plain,
once(local_name.clone()),
));
match self.resolver.resolve_path_in_value_ns_fully(db.upcast(), &path) { match self.resolver.resolve_path_in_value_ns_fully(db.upcast(), &path) {
Some(ValueNs::LocalBinding(binding_id)) => { Some(ValueNs::LocalBinding(binding_id)) => {
Some(Local { binding_id, parent: self.resolver.body_owner()? }) Some(Local { binding_id, parent: self.resolver.body_owner()? })
@ -461,7 +464,7 @@ impl SourceAnalyzer {
) -> Option<Macro> { ) -> Option<Macro> {
let ctx = body::LowerCtx::new(db.upcast(), macro_call.file_id); let ctx = body::LowerCtx::new(db.upcast(), macro_call.file_id);
let path = macro_call.value.path().and_then(|ast| Path::from_src(ast, &ctx))?; let path = macro_call.value.path().and_then(|ast| Path::from_src(ast, &ctx))?;
self.resolver.resolve_path_as_macro(db.upcast(), path.mod_path()).map(|it| it.into()) self.resolver.resolve_path_as_macro(db.upcast(), path.mod_path()?).map(|it| it.into())
} }
pub(crate) fn resolve_bind_pat_to_const( pub(crate) fn resolve_bind_pat_to_const(
@ -801,15 +804,11 @@ impl SourceAnalyzer {
func: FunctionId, func: FunctionId,
substs: Substitution, substs: Substitution,
) -> FunctionId { ) -> FunctionId {
let krate = self.resolver.krate();
let owner = match self.resolver.body_owner() { let owner = match self.resolver.body_owner() {
Some(it) => it, Some(it) => it,
None => return func, None => return func,
}; };
let env = owner.as_generic_def_id().map_or_else( let env = db.trait_environment_for_body(owner);
|| Arc::new(hir_ty::TraitEnvironment::empty(krate)),
|d| db.trait_environment(d),
);
method_resolution::lookup_impl_method(db, env, func, substs).0 method_resolution::lookup_impl_method(db, env, func, substs).0
} }
@ -819,15 +818,11 @@ impl SourceAnalyzer {
const_id: ConstId, const_id: ConstId,
subs: Substitution, subs: Substitution,
) -> ConstId { ) -> ConstId {
let krate = self.resolver.krate();
let owner = match self.resolver.body_owner() { let owner = match self.resolver.body_owner() {
Some(it) => it, Some(it) => it,
None => return const_id, None => return const_id,
}; };
let env = owner.as_generic_def_id().map_or_else( let env = db.trait_environment_for_body(owner);
|| Arc::new(hir_ty::TraitEnvironment::empty(krate)),
|d| db.trait_environment(d),
);
method_resolution::lookup_impl_const(db, env, const_id, subs).0 method_resolution::lookup_impl_const(db, env, const_id, subs).0
} }
@ -946,7 +941,7 @@ pub(crate) fn resolve_hir_path_as_macro(
resolver: &Resolver, resolver: &Resolver,
path: &Path, path: &Path,
) -> Option<Macro> { ) -> Option<Macro> {
resolver.resolve_path_as_macro(db.upcast(), path.mod_path()).map(Into::into) resolver.resolve_path_as_macro(db.upcast(), path.mod_path()?).map(Into::into)
} }
fn resolve_hir_path_( fn resolve_hir_path_(
@ -962,8 +957,7 @@ fn resolve_hir_path_(
res.map(|ty_ns| (ty_ns, path.segments().first())) res.map(|ty_ns| (ty_ns, path.segments().first()))
} }
None => { None => {
let (ty, remaining_idx) = let (ty, remaining_idx) = resolver.resolve_path_in_type_ns(db.upcast(), path)?;
resolver.resolve_path_in_type_ns(db.upcast(), path.mod_path())?;
match remaining_idx { match remaining_idx {
Some(remaining_idx) => { Some(remaining_idx) => {
if remaining_idx + 1 == path.segments().len() { if remaining_idx + 1 == path.segments().len() {
@ -1019,7 +1013,7 @@ fn resolve_hir_path_(
let body_owner = resolver.body_owner(); let body_owner = resolver.body_owner();
let values = || { let values = || {
resolver.resolve_path_in_value_ns_fully(db.upcast(), path.mod_path()).and_then(|val| { resolver.resolve_path_in_value_ns_fully(db.upcast(), path).and_then(|val| {
let res = match val { let res = match val {
ValueNs::LocalBinding(binding_id) => { ValueNs::LocalBinding(binding_id) => {
let var = Local { parent: body_owner?, binding_id }; let var = Local { parent: body_owner?, binding_id };
@ -1039,14 +1033,14 @@ fn resolve_hir_path_(
let items = || { let items = || {
resolver resolver
.resolve_module_path_in_items(db.upcast(), path.mod_path()) .resolve_module_path_in_items(db.upcast(), path.mod_path()?)
.take_types() .take_types()
.map(|it| PathResolution::Def(it.into())) .map(|it| PathResolution::Def(it.into()))
}; };
let macros = || { let macros = || {
resolver resolver
.resolve_path_as_macro(db.upcast(), path.mod_path()) .resolve_path_as_macro(db.upcast(), path.mod_path()?)
.map(|def| PathResolution::Def(ModuleDef::Macro(def.into()))) .map(|def| PathResolution::Def(ModuleDef::Macro(def.into())))
}; };
@ -1074,7 +1068,7 @@ fn resolve_hir_path_qualifier(
path: &Path, path: &Path,
) -> Option<PathResolution> { ) -> Option<PathResolution> {
resolver resolver
.resolve_path_in_type_ns_fully(db.upcast(), path.mod_path()) .resolve_path_in_type_ns_fully(db.upcast(), &path)
.map(|ty| match ty { .map(|ty| match ty {
TypeNs::SelfType(it) => PathResolution::SelfType(it.into()), TypeNs::SelfType(it) => PathResolution::SelfType(it.into()),
TypeNs::GenericParam(id) => PathResolution::TypeParam(id.into()), TypeNs::GenericParam(id) => PathResolution::TypeParam(id.into()),
@ -1089,7 +1083,7 @@ fn resolve_hir_path_qualifier(
}) })
.or_else(|| { .or_else(|| {
resolver resolver
.resolve_module_path_in_items(db.upcast(), path.mod_path()) .resolve_module_path_in_items(db.upcast(), path.mod_path()?)
.take_types() .take_types()
.map(|it| PathResolution::Def(it.into())) .map(|it| PathResolution::Def(it.into()))
}) })

View file

@ -20,7 +20,7 @@ use crate::assist_context::{AssistContext, Assists};
// Replaces a `try` expression with a `match` expression. // Replaces a `try` expression with a `match` expression.
// //
// ``` // ```
// # //- minicore:option // # //- minicore: try, option
// fn handle() { // fn handle() {
// let pat = Some(true)$0?; // let pat = Some(true)$0?;
// } // }
@ -111,7 +111,7 @@ mod tests {
check_assist( check_assist(
replace_try_expr_with_match, replace_try_expr_with_match,
r#" r#"
//- minicore:option //- minicore: try, option
fn test() { fn test() {
let pat = Some(true)$0?; let pat = Some(true)$0?;
} }
@ -132,7 +132,7 @@ fn test() {
check_assist( check_assist(
replace_try_expr_with_match, replace_try_expr_with_match,
r#" r#"
//- minicore:result //- minicore: try, from, result
fn test() { fn test() {
let pat = Ok(true)$0?; let pat = Ok(true)$0?;
} }

View file

@ -2352,7 +2352,7 @@ fn doctest_replace_try_expr_with_match() {
check_doc_test( check_doc_test(
"replace_try_expr_with_match", "replace_try_expr_with_match",
r#####" r#####"
//- minicore:option //- minicore: try, option
fn handle() { fn handle() {
let pat = Some(true)$0?; let pat = Some(true)$0?;
} }

View file

@ -564,9 +564,56 @@ fn f(x: [(i32, u8); 10]) {
); );
} }
#[test]
fn overloaded_index() {
check_diagnostics(
r#"
//- minicore: index
use core::ops::{Index, IndexMut};
struct Foo;
impl Index<usize> for Foo {
type Output = (i32, u8);
fn index(&self, index: usize) -> &(i32, u8) {
&(5, 2)
}
}
impl IndexMut<usize> for Foo {
fn index_mut(&mut self, index: usize) -> &mut (i32, u8) {
&mut (5, 2)
}
}
fn f() {
let mut x = Foo;
//^^^^^ 💡 weak: variable does not need to be mutable
let y = &x[2];
let x = Foo;
let y = &mut x[2];
//^^^^ 💡 error: cannot mutate immutable variable `x`
let mut x = &mut Foo;
//^^^^^ 💡 weak: variable does not need to be mutable
let y: &mut (i32, u8) = &mut x[2];
let x = Foo;
let ref mut y = x[7];
//^^^^ 💡 error: cannot mutate immutable variable `x`
let (ref mut y, _) = x[3];
//^^^^ 💡 error: cannot mutate immutable variable `x`
match x[10] {
//^^^^^ 💡 error: cannot mutate immutable variable `x`
(ref y, _) => (),
(_, ref mut y) => (),
}
let mut x = Foo;
let mut i = 5;
//^^^^^ 💡 weak: variable does not need to be mutable
let y = &mut x[i];
}
"#,
);
}
#[test] #[test]
fn overloaded_deref() { fn overloaded_deref() {
// FIXME: check for false negative
check_diagnostics( check_diagnostics(
r#" r#"
//- minicore: deref_mut //- minicore: deref_mut
@ -574,22 +621,36 @@ use core::ops::{Deref, DerefMut};
struct Foo; struct Foo;
impl Deref for Foo { impl Deref for Foo {
type Target = i32; type Target = (i32, u8);
fn deref(&self) -> &i32 { fn deref(&self) -> &(i32, u8) {
&5 &(5, 2)
} }
} }
impl DerefMut for Foo { impl DerefMut for Foo {
fn deref_mut(&mut self) -> &mut i32 { fn deref_mut(&mut self) -> &mut (i32, u8) {
&mut 5 &mut (5, 2)
} }
} }
fn f() { fn f() {
let x = Foo; let mut x = Foo;
//^^^^^ 💡 weak: variable does not need to be mutable
let y = &*x; let y = &*x;
let x = Foo; let x = Foo;
let mut x = Foo; let y = &mut *x;
let y: &mut i32 = &mut x; //^^ 💡 error: cannot mutate immutable variable `x`
let x = Foo;
let x = Foo;
let y: &mut (i32, u8) = &mut x;
//^^^^^^ 💡 error: cannot mutate immutable variable `x`
let ref mut y = *x;
//^^ 💡 error: cannot mutate immutable variable `x`
let (ref mut y, _) = *x;
//^^ 💡 error: cannot mutate immutable variable `x`
match *x {
//^^ 💡 error: cannot mutate immutable variable `x`
(ref y, _) => (),
(_, ref mut y) => (),
}
} }
"#, "#,
); );
@ -631,6 +692,31 @@ fn f(inp: (Foo, Foo, Foo, Foo)) {
); );
} }
#[test]
fn fn_traits() {
check_diagnostics(
r#"
//- minicore: fn
fn fn_ref(mut x: impl Fn(u8) -> u8) -> u8 {
//^^^^^ 💡 weak: variable does not need to be mutable
x(2)
}
fn fn_mut(x: impl FnMut(u8) -> u8) -> u8 {
x(2)
//^ 💡 error: cannot mutate immutable variable `x`
}
fn fn_borrow_mut(mut x: &mut impl FnMut(u8) -> u8) -> u8 {
//^^^^^ 💡 weak: variable does not need to be mutable
x(2)
}
fn fn_once(mut x: impl FnOnce(u8) -> u8) -> u8 {
//^^^^^ 💡 weak: variable does not need to be mutable
x(2)
}
"#,
);
}
#[test] #[test]
fn respect_allow_unused_mut() { fn respect_allow_unused_mut() {
// FIXME: respect // FIXME: respect

View file

@ -409,7 +409,6 @@ pub(super) fn definition(
} }
match it.eval(db) { match it.eval(db) {
Ok(()) => Some("pass".into()), Ok(()) => Some("pass".into()),
Err(MirEvalError::Panic) => Some("fail".into()),
Err(MirEvalError::MirLowerError(f, e)) => { Err(MirEvalError::MirLowerError(f, e)) => {
let name = &db.function_data(f).name; let name = &db.function_data(f).name;
Some(format!("error: fail to lower {name} due {e:?}")) Some(format!("error: fail to lower {name} due {e:?}"))

View file

@ -5009,7 +5009,7 @@ fn foo() {
fn hover_try_expr_res() { fn hover_try_expr_res() {
check_hover_range( check_hover_range(
r#" r#"
//- minicore:result //- minicore: try, from, result
struct FooError; struct FooError;
fn foo() -> Result<(), FooError> { fn foo() -> Result<(), FooError> {
@ -5023,7 +5023,7 @@ fn foo() -> Result<(), FooError> {
); );
check_hover_range( check_hover_range(
r#" r#"
//- minicore:result //- minicore: try, from, result
struct FooError; struct FooError;
struct BarError; struct BarError;
@ -5044,6 +5044,7 @@ fn foo() -> Result<(), FooError> {
fn hover_try_expr() { fn hover_try_expr() {
check_hover_range( check_hover_range(
r#" r#"
//- minicore: try
struct NotResult<T, U>(T, U); struct NotResult<T, U>(T, U);
struct Short; struct Short;
struct Looooong; struct Looooong;
@ -5061,6 +5062,7 @@ fn foo() -> NotResult<(), Looooong> {
); );
check_hover_range( check_hover_range(
r#" r#"
//- minicore: try
struct NotResult<T, U>(T, U); struct NotResult<T, U>(T, U);
struct Short; struct Short;
struct Looooong; struct Looooong;
@ -5092,7 +5094,7 @@ fn foo() -> Option<()> {
"#, "#,
expect![[r#" expect![[r#"
```rust ```rust
<Option<i32> as Try>::Output i32
```"#]], ```"#]],
); );
} }

View file

@ -435,7 +435,7 @@ fn main() {
file_id: FileId( file_id: FileId(
1, 1,
), ),
range: 3415..3423, range: 5805..5813,
}, },
), ),
tooltip: "", tooltip: "",
@ -448,7 +448,7 @@ fn main() {
file_id: FileId( file_id: FileId(
1, 1,
), ),
range: 3447..3451, range: 5837..5841,
}, },
), ),
tooltip: "", tooltip: "",
@ -468,7 +468,7 @@ fn main() {
file_id: FileId( file_id: FileId(
1, 1,
), ),
range: 3415..3423, range: 5805..5813,
}, },
), ),
tooltip: "", tooltip: "",
@ -481,7 +481,7 @@ fn main() {
file_id: FileId( file_id: FileId(
1, 1,
), ),
range: 3447..3451, range: 5837..5841,
}, },
), ),
tooltip: "", tooltip: "",
@ -501,7 +501,7 @@ fn main() {
file_id: FileId( file_id: FileId(
1, 1,
), ),
range: 3415..3423, range: 5805..5813,
}, },
), ),
tooltip: "", tooltip: "",
@ -514,7 +514,7 @@ fn main() {
file_id: FileId( file_id: FileId(
1, 1,
), ),
range: 3447..3451, range: 5837..5841,
}, },
), ),
tooltip: "", tooltip: "",

View file

@ -1126,5 +1126,5 @@ fn benchmark_syntax_highlighting_parser() {
.filter(|it| it.highlight.tag == HlTag::Symbol(SymbolKind::Function)) .filter(|it| it.highlight.tag == HlTag::Symbol(SymbolKind::Function))
.count() .count()
}; };
assert_eq!(hash, 1608); assert_eq!(hash, 1170);
} }

View file

@ -2,6 +2,7 @@
//! errors. //! errors.
use std::{ use std::{
collections::HashMap,
env, env,
time::{SystemTime, UNIX_EPOCH}, time::{SystemTime, UNIX_EPOCH},
}; };
@ -153,6 +154,10 @@ impl flags::AnalysisStats {
self.run_inference(&host, db, &vfs, &funcs, verbosity); self.run_inference(&host, db, &vfs, &funcs, verbosity);
} }
if self.mir_stats {
self.lower_mir(db, &funcs);
}
let total_span = analysis_sw.elapsed(); let total_span = analysis_sw.elapsed();
eprintln!("{:<20} {total_span}", "Total:"); eprintln!("{:<20} {total_span}", "Total:");
report_metric("total time", total_span.time.as_millis() as u64, "ms"); report_metric("total time", total_span.time.as_millis() as u64, "ms");
@ -189,6 +194,24 @@ impl flags::AnalysisStats {
Ok(()) Ok(())
} }
fn lower_mir(&self, db: &RootDatabase, funcs: &[Function]) {
let all = funcs.len();
let mut fail = 0;
let mut h: HashMap<String, usize> = HashMap::new();
for f in funcs {
let f = FunctionId::from(*f);
let Err(e) = db.mir_body(f.into()) else {
continue;
};
let es = format!("{:?}", e);
*h.entry(es).or_default() += 1;
fail += 1;
}
let h = h.into_iter().sorted_by_key(|x| x.1).collect::<Vec<_>>();
eprintln!("Mir failed reasons: {:#?}", h);
eprintln!("Mir failed bodies: {fail} ({}%)", fail * 100 / all);
}
fn run_inference( fn run_inference(
&self, &self,
host: &AnalysisHost, host: &AnalysisHost,

View file

@ -66,6 +66,8 @@ xflags::xflags! {
optional --memory-usage optional --memory-usage
/// Print the total length of all source and macro files (whitespace is not counted). /// Print the total length of all source and macro files (whitespace is not counted).
optional --source-stats optional --source-stats
/// Print the number of bodies that fail to lower to mir, in addition to failed reasons.
optional --mir-stats
/// Only analyze items matching this path. /// Only analyze items matching this path.
optional -o, --only path: String optional -o, --only path: String
@ -172,6 +174,7 @@ pub struct AnalysisStats {
pub parallel: bool, pub parallel: bool,
pub memory_usage: bool, pub memory_usage: bool,
pub source_stats: bool, pub source_stats: bool,
pub mir_stats: bool,
pub only: Option<String>, pub only: Option<String>,
pub with_deps: bool, pub with_deps: bool,
pub no_sysroot: bool, pub no_sysroot: bool,

View file

@ -613,7 +613,7 @@ Pat =
| ConstBlockPat | ConstBlockPat
LiteralPat = LiteralPat =
Literal '-'? Literal
IdentPat = IdentPat =
Attr* 'ref'? 'mut'? Name ('@' Pat)? Attr* 'ref'? 'mut'? Name ('@' Pat)?

View file

@ -1375,6 +1375,7 @@ pub struct LiteralPat {
pub(crate) syntax: SyntaxNode, pub(crate) syntax: SyntaxNode,
} }
impl LiteralPat { impl LiteralPat {
pub fn minus_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![-]) }
pub fn literal(&self) -> Option<Literal> { support::child(&self.syntax) } pub fn literal(&self) -> Option<Literal> { support::child(&self.syntax) }
} }

View file

@ -535,6 +535,7 @@ impl Field {
"!" => "excl", "!" => "excl",
"*" => "star", "*" => "star",
"&" => "amp", "&" => "amp",
"-" => "minus",
"_" => "underscore", "_" => "underscore",
"." => "dot", "." => "dot",
".." => "dotdot", ".." => "dotdot",

View file

@ -181,7 +181,7 @@ pub mod convert {
} }
// endregion:as_ref // endregion:as_ref
// region:infallible // region:infallible
pub enum Infallibe {} pub enum Infallible {}
// endregion:infallible // endregion:infallible
} }
@ -375,16 +375,82 @@ pub mod ops {
type Output; type Output;
extern "rust-call" fn call_once(self, args: Args) -> Self::Output; extern "rust-call" fn call_once(self, args: Args) -> Self::Output;
} }
mod impls {
use crate::marker::Tuple;
#[stable(feature = "rust1", since = "1.0.0")]
#[rustc_const_unstable(feature = "const_fn_trait_ref_impls", issue = "101803")]
impl<A: Tuple, F: ?Sized> const Fn<A> for &F
where
F: ~const Fn<A>,
{
extern "rust-call" fn call(&self, args: A) -> F::Output {
(**self).call(args)
}
}
#[stable(feature = "rust1", since = "1.0.0")]
#[rustc_const_unstable(feature = "const_fn_trait_ref_impls", issue = "101803")]
impl<A: Tuple, F: ?Sized> const FnMut<A> for &F
where
F: ~const Fn<A>,
{
extern "rust-call" fn call_mut(&mut self, args: A) -> F::Output {
(**self).call(args)
}
}
#[stable(feature = "rust1", since = "1.0.0")]
#[rustc_const_unstable(feature = "const_fn_trait_ref_impls", issue = "101803")]
impl<A: Tuple, F: ?Sized> const FnOnce<A> for &F
where
F: ~const Fn<A>,
{
type Output = F::Output;
extern "rust-call" fn call_once(self, args: A) -> F::Output {
(*self).call(args)
}
}
#[stable(feature = "rust1", since = "1.0.0")]
#[rustc_const_unstable(feature = "const_fn_trait_ref_impls", issue = "101803")]
impl<A: Tuple, F: ?Sized> const FnMut<A> for &mut F
where
F: ~const FnMut<A>,
{
extern "rust-call" fn call_mut(&mut self, args: A) -> F::Output {
(*self).call_mut(args)
}
}
#[stable(feature = "rust1", since = "1.0.0")]
#[rustc_const_unstable(feature = "const_fn_trait_ref_impls", issue = "101803")]
impl<A: Tuple, F: ?Sized> const FnOnce<A> for &mut F
where
F: ~const FnMut<A>,
{
type Output = F::Output;
extern "rust-call" fn call_once(self, args: A) -> F::Output {
(*self).call_mut(args)
}
}
}
} }
pub use self::function::{Fn, FnMut, FnOnce}; pub use self::function::{Fn, FnMut, FnOnce};
// endregion:fn // endregion:fn
// region:try // region:try
mod try_ { mod try_ {
use super::super::convert::Infallible;
pub enum ControlFlow<B, C = ()> { pub enum ControlFlow<B, C = ()> {
#[lang = "Continue"]
Continue(C), Continue(C),
#[lang = "Break"]
Break(B), Break(B),
} }
pub trait FromResidual<R = Self::Residual> { pub trait FromResidual<R = <Self as Try>::Residual> {
#[lang = "from_residual"] #[lang = "from_residual"]
fn from_residual(residual: R) -> Self; fn from_residual(residual: R) -> Self;
} }
@ -400,14 +466,66 @@ pub mod ops {
impl<B, C> Try for ControlFlow<B, C> { impl<B, C> Try for ControlFlow<B, C> {
type Output = C; type Output = C;
type Residual = ControlFlow<B, convert::Infallible>; type Residual = ControlFlow<B, Infallible>;
fn from_output(output: Self::Output) -> Self {} fn from_output(output: Self::Output) -> Self {}
fn branch(self) -> ControlFlow<Self::Residual, Self::Output> {} fn branch(self) -> ControlFlow<Self::Residual, Self::Output> {}
} }
impl<B, C> FromResidual for ControlFlow<B, C> { impl<B, C> FromResidual for ControlFlow<B, C> {
fn from_residual(residual: ControlFlow<B, convert::Infallible>) -> Self {} fn from_residual(residual: ControlFlow<B, Infallible>) -> Self {}
} }
// region:option
impl<T> Try for Option<T> {
type Output = T;
type Residual = Option<Infallible>;
fn from_output(output: Self::Output) -> Self {
Some(output)
}
fn branch(self) -> ControlFlow<Self::Residual, Self::Output> {
match self {
Some(x) => ControlFlow::Continue(x),
None => ControlFlow::Break(None),
}
}
}
impl<T> FromResidual for Option<T> {
fn from_residual(x: Option<Infallible>) -> Self {
match x {
None => None,
}
}
}
// endregion:option
// region:result
// region:from
use super::super::convert::From;
impl<T, E> Try for Result<T, E> {
type Output = T;
type Residual = Result<Infallible, E>;
fn from_output(output: Self::Output) -> Self {
Ok(output)
}
fn branch(self) -> ControlFlow<Self::Residual, Self::Output> {
match self {
Ok(v) => ControlFlow::Continue(v),
Err(e) => ControlFlow::Break(Err(e)),
}
}
}
impl<T, E, F: From<E>> FromResidual<Result<Infallible, E>> for Result<T, F> {
fn from_residual(residual: Result<Infallible, E>) -> Self {
match residual {
Err(e) => Err(From::from(e)),
}
}
}
// endregion:from
// endregion:result
} }
pub use self::try_::{ControlFlow, FromResidual, Try}; pub use self::try_::{ControlFlow, FromResidual, Try};
// endregion:try // endregion:try
@ -541,7 +659,10 @@ pub mod option {
loop {} loop {}
} }
pub fn unwrap_or(self, default: T) -> T { pub fn unwrap_or(self, default: T) -> T {
loop {} match self {
Some(val) => val,
None => default,
}
} }
// region:fn // region:fn
pub fn and_then<U, F>(self, f: F) -> Option<U> pub fn and_then<U, F>(self, f: F) -> Option<U>

View file

@ -295,7 +295,7 @@ impl<T> Arena<T> {
/// ``` /// ```
pub fn iter( pub fn iter(
&self, &self,
) -> impl Iterator<Item = (Idx<T>, &T)> + ExactSizeIterator + DoubleEndedIterator { ) -> impl Iterator<Item = (Idx<T>, &T)> + ExactSizeIterator + DoubleEndedIterator + Clone {
self.data.iter().enumerate().map(|(idx, value)| (Idx::from_raw(RawIdx(idx as u32)), value)) self.data.iter().enumerate().map(|(idx, value)| (Idx::from_raw(RawIdx(idx as u32)), value))
} }