mirror of
https://github.com/rust-lang/rust-analyzer
synced 2024-11-15 01:17:27 +00:00
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:
commit
3321799e8f
52 changed files with 3100 additions and 937 deletions
|
@ -391,7 +391,7 @@ impl Body {
|
|||
}
|
||||
};
|
||||
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();
|
||||
|
||||
(Arc::new(body), Arc::new(source_map))
|
||||
|
@ -420,8 +420,9 @@ impl Body {
|
|||
expander: Expander,
|
||||
params: Option<(ast::ParamList, impl Iterator<Item = bool>)>,
|
||||
body: Option<ast::Expr>,
|
||||
krate: CrateId,
|
||||
) -> (Body, BodySourceMap) {
|
||||
lower::lower(db, expander, params, body)
|
||||
lower::lower(db, expander, params, body, krate)
|
||||
}
|
||||
|
||||
fn shrink_to_fit(&mut self) {
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
|
||||
use std::{mem, sync::Arc};
|
||||
|
||||
use base_db::CrateId;
|
||||
use either::Either;
|
||||
use hir_expand::{
|
||||
ast_id_map::AstIdMap,
|
||||
|
@ -18,7 +19,7 @@ use rustc_hash::FxHashMap;
|
|||
use smallvec::SmallVec;
|
||||
use syntax::{
|
||||
ast::{
|
||||
self, ArrayExprKind, AstChildren, HasArgList, HasLoopBody, HasName, LiteralKind,
|
||||
self, ArrayExprKind, AstChildren, BlockExpr, HasArgList, HasLoopBody, HasName, LiteralKind,
|
||||
SlicePatComponents,
|
||||
},
|
||||
AstNode, AstPtr, SyntaxNodePtr,
|
||||
|
@ -36,6 +37,7 @@ use crate::{
|
|||
RecordFieldPat, RecordLitField, Statement,
|
||||
},
|
||||
item_scope::BuiltinShadowMode,
|
||||
lang_item::LangItem,
|
||||
path::{GenericArgs, Path},
|
||||
type_ref::{Mutability, Rawness, TypeRef},
|
||||
AdtId, BlockId, BlockLoc, ModuleDefId, UnresolvedMacro,
|
||||
|
@ -80,9 +82,11 @@ pub(super) fn lower(
|
|||
expander: Expander,
|
||||
params: Option<(ast::ParamList, impl Iterator<Item = bool>)>,
|
||||
body: Option<ast::Expr>,
|
||||
krate: CrateId,
|
||||
) -> (Body, BodySourceMap) {
|
||||
ExprCollector {
|
||||
db,
|
||||
krate,
|
||||
source_map: BodySourceMap::default(),
|
||||
ast_id_map: db.ast_id_map(expander.current_file_id),
|
||||
body: Body {
|
||||
|
@ -96,6 +100,7 @@ pub(super) fn lower(
|
|||
_c: Count::new(),
|
||||
},
|
||||
expander,
|
||||
current_try_block: None,
|
||||
is_lowering_assignee_expr: false,
|
||||
is_lowering_generator: false,
|
||||
}
|
||||
|
@ -107,7 +112,9 @@ struct ExprCollector<'a> {
|
|||
expander: Expander,
|
||||
ast_id_map: Arc<AstIdMap>,
|
||||
body: Body,
|
||||
krate: CrateId,
|
||||
source_map: BodySourceMap,
|
||||
current_try_block: Option<LabelId>,
|
||||
is_lowering_assignee_expr: bool,
|
||||
is_lowering_generator: bool,
|
||||
}
|
||||
|
@ -176,8 +183,7 @@ impl ExprCollector<'_> {
|
|||
self.source_map.expr_map.insert(src, id);
|
||||
id
|
||||
}
|
||||
// desugared exprs don't have ptr, that's wrong and should be fixed
|
||||
// somehow.
|
||||
// FIXME: desugared exprs don't have ptr, that's wrong and should be fixed somehow.
|
||||
fn alloc_expr_desugared(&mut self, expr: Expr) -> ExprId {
|
||||
self.body.exprs.alloc(expr)
|
||||
}
|
||||
|
@ -199,6 +205,10 @@ impl ExprCollector<'_> {
|
|||
self.source_map.pat_map.insert(src, 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 {
|
||||
self.body.pats.alloc(Pat::Missing)
|
||||
}
|
||||
|
@ -214,6 +224,10 @@ impl ExprCollector<'_> {
|
|||
self.source_map.label_map.insert(src, 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 {
|
||||
let id = self.body.labels.alloc(label);
|
||||
self.source_map.label_map_back.insert(id, src);
|
||||
|
@ -251,13 +265,7 @@ impl ExprCollector<'_> {
|
|||
self.alloc_expr(Expr::Let { pat, expr }, syntax_ptr)
|
||||
}
|
||||
ast::Expr::BlockExpr(e) => match e.modifier() {
|
||||
Some(ast::BlockModifier::Try(_)) => {
|
||||
self.collect_block_(e, |id, statements, tail| Expr::TryBlock {
|
||||
id,
|
||||
statements,
|
||||
tail,
|
||||
})
|
||||
}
|
||||
Some(ast::BlockModifier::Try(_)) => self.collect_try_block(e),
|
||||
Some(ast::BlockModifier::Unsafe(_)) => {
|
||||
self.collect_block_(e, |id, statements, tail| Expr::Unsafe {
|
||||
id,
|
||||
|
@ -437,10 +445,7 @@ impl ExprCollector<'_> {
|
|||
let expr = self.collect_expr_opt(e.expr());
|
||||
self.alloc_expr(Expr::Await { expr }, syntax_ptr)
|
||||
}
|
||||
ast::Expr::TryExpr(e) => {
|
||||
let expr = self.collect_expr_opt(e.expr());
|
||||
self.alloc_expr(Expr::Try { expr }, syntax_ptr)
|
||||
}
|
||||
ast::Expr::TryExpr(e) => self.collect_try_operator(syntax_ptr, e),
|
||||
ast::Expr::CastExpr(e) => {
|
||||
let expr = self.collect_expr_opt(e.expr());
|
||||
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>(
|
||||
&mut self,
|
||||
mcall: ast::MacroCall,
|
||||
|
@ -949,16 +1074,24 @@ impl ExprCollector<'_> {
|
|||
.collect(),
|
||||
}
|
||||
}
|
||||
ast::Pat::LiteralPat(lit) => {
|
||||
if let Some(ast_lit) = lit.literal() {
|
||||
let expr = Expr::Literal(ast_lit.kind().into());
|
||||
// FIXME: rustfmt removes this label if it is a block and not a loop
|
||||
ast::Pat::LiteralPat(lit) => 'b: loop {
|
||||
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_id = self.alloc_expr(expr, expr_ptr);
|
||||
Pat::Lit(expr_id)
|
||||
} else {
|
||||
Pat::Missing
|
||||
}
|
||||
}
|
||||
};
|
||||
},
|
||||
ast::Pat::RestPat(_) => {
|
||||
// `RestPat` requires special handling and should not be mapped
|
||||
// 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())),
|
||||
builtin,
|
||||
)
|
||||
} else if let builtin @ Some(_) = lit.suffix().and_then(BuiltinInt::from_suffix) {
|
||||
Literal::Int(lit.value().unwrap_or(0) as i128, builtin)
|
||||
} else {
|
||||
let builtin = lit.suffix().and_then(BuiltinUint::from_suffix);
|
||||
} else if let builtin @ Some(_) = lit.suffix().and_then(BuiltinUint::from_suffix) {
|
||||
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) => {
|
||||
|
|
|
@ -288,10 +288,6 @@ impl<'a> Printer<'a> {
|
|||
self.print_expr(*expr);
|
||||
w!(self, ".await");
|
||||
}
|
||||
Expr::Try { expr } => {
|
||||
self.print_expr(*expr);
|
||||
w!(self, "?");
|
||||
}
|
||||
Expr::Cast { expr, type_ref } => {
|
||||
self.print_expr(*expr);
|
||||
w!(self, " as ");
|
||||
|
@ -424,9 +420,6 @@ impl<'a> Printer<'a> {
|
|||
Expr::Unsafe { id: _, 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 } => {
|
||||
self.print_block(Some("async "), statements, tail);
|
||||
}
|
||||
|
|
|
@ -202,8 +202,7 @@ fn compute_expr_scopes(expr: ExprId, body: &Body, scopes: &mut ExprScopes, scope
|
|||
}
|
||||
Expr::Unsafe { id, statements, tail }
|
||||
| Expr::Async { id, statements, tail }
|
||||
| Expr::Const { id, statements, tail }
|
||||
| Expr::TryBlock { id, statements, tail } => {
|
||||
| Expr::Const { id, statements, tail } => {
|
||||
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
|
||||
// via the block itself (important for blocks that only contain items, no expressions).
|
||||
|
|
|
@ -92,6 +92,16 @@ pub enum Literal {
|
|||
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)]
|
||||
pub enum Expr {
|
||||
/// This is produced if the syntax tree does not have a required expression piece.
|
||||
|
@ -112,11 +122,6 @@ pub enum Expr {
|
|||
tail: Option<ExprId>,
|
||||
label: Option<LabelId>,
|
||||
},
|
||||
TryBlock {
|
||||
id: BlockId,
|
||||
statements: Box<[Statement]>,
|
||||
tail: Option<ExprId>,
|
||||
},
|
||||
Async {
|
||||
id: BlockId,
|
||||
statements: Box<[Statement]>,
|
||||
|
@ -192,9 +197,6 @@ pub enum Expr {
|
|||
Await {
|
||||
expr: ExprId,
|
||||
},
|
||||
Try {
|
||||
expr: ExprId,
|
||||
},
|
||||
Cast {
|
||||
expr: ExprId,
|
||||
type_ref: Interned<TypeRef>,
|
||||
|
@ -303,7 +305,6 @@ impl Expr {
|
|||
f(*expr);
|
||||
}
|
||||
Expr::Block { statements, tail, .. }
|
||||
| Expr::TryBlock { statements, tail, .. }
|
||||
| Expr::Unsafe { statements, tail, .. }
|
||||
| Expr::Async { statements, tail, .. }
|
||||
| Expr::Const { statements, tail, .. } => {
|
||||
|
@ -383,7 +384,6 @@ impl Expr {
|
|||
}
|
||||
Expr::Field { expr, .. }
|
||||
| Expr::Await { expr }
|
||||
| Expr::Try { expr }
|
||||
| Expr::Cast { expr, .. }
|
||||
| Expr::Ref { expr, .. }
|
||||
| Expr::UnaryOp { expr, .. }
|
||||
|
|
|
@ -8,8 +8,8 @@ use rustc_hash::FxHashMap;
|
|||
use syntax::SmolStr;
|
||||
|
||||
use crate::{
|
||||
db::DefDatabase, AdtId, AssocItemId, AttrDefId, CrateId, EnumId, EnumVariantId, FunctionId,
|
||||
ImplId, ModuleDefId, StaticId, StructId, TraitId, TypeAliasId, UnionId,
|
||||
db::DefDatabase, path::Path, AdtId, AssocItemId, AttrDefId, CrateId, EnumId, EnumVariantId,
|
||||
FunctionId, ImplId, ModuleDefId, StaticId, StructId, TraitId, TypeAliasId, UnionId,
|
||||
};
|
||||
|
||||
#[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`]
|
||||
pub fn from_str(name: &str) -> Option<Self> {
|
||||
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! {
|
||||
// Variant name, Name, Getter method name, Target Generic requirements;
|
||||
Sized, sized, sized_trait, Target::Trait, GenericRequirement::Exact(0);
|
||||
|
|
|
@ -8,6 +8,7 @@ use std::{
|
|||
|
||||
use crate::{
|
||||
body::LowerCtx,
|
||||
lang_item::LangItemTarget,
|
||||
type_ref::{ConstRefOrPath, LifetimeRef},
|
||||
};
|
||||
use hir_expand::name::Name;
|
||||
|
@ -36,13 +37,19 @@ impl Display for ImportAlias {
|
|||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
pub struct Path {
|
||||
/// Type based path like `<T>::foo`.
|
||||
/// Note that paths like `<Type as Trait>::foo` are desugared to `Trait::<Self=Type>::foo`.
|
||||
type_anchor: Option<Interned<TypeRef>>,
|
||||
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>>]>>,
|
||||
pub enum Path {
|
||||
/// A normal path
|
||||
Normal {
|
||||
/// Type based path like `<T>::foo`.
|
||||
/// Note that paths like `<Type as Trait>::foo` are desugared to `Trait::<Self=Type>::foo`.
|
||||
type_anchor: Option<Interned<TypeRef>>,
|
||||
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
|
||||
|
@ -102,51 +109,77 @@ impl Path {
|
|||
) -> Path {
|
||||
let generic_args = generic_args.into();
|
||||
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 {
|
||||
&self.mod_path.kind
|
||||
match self {
|
||||
Path::Normal { mod_path, .. } => &mod_path.kind,
|
||||
Path::LangItem(_) => &PathKind::Abs,
|
||||
}
|
||||
}
|
||||
|
||||
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<'_> {
|
||||
let s = PathSegments {
|
||||
segments: self.mod_path.segments(),
|
||||
generic_args: self.generic_args.as_deref(),
|
||||
let Path::Normal { mod_path, generic_args, .. } = self else {
|
||||
return PathSegments {
|
||||
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 {
|
||||
assert_eq!(s.segments.len(), generic_args.len());
|
||||
}
|
||||
s
|
||||
}
|
||||
|
||||
pub fn mod_path(&self) -> &ModPath {
|
||||
&self.mod_path
|
||||
pub fn mod_path(&self) -> Option<&ModPath> {
|
||||
match self {
|
||||
Path::Normal { mod_path, .. } => Some(&mod_path),
|
||||
Path::LangItem(_) => None,
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
let res = Path {
|
||||
type_anchor: self.type_anchor.clone(),
|
||||
let res = Path::Normal {
|
||||
type_anchor: type_anchor.clone(),
|
||||
mod_path: Interned::new(ModPath::from_segments(
|
||||
self.mod_path.kind,
|
||||
self.mod_path.segments()[..self.mod_path.segments().len() - 1].iter().cloned(),
|
||||
mod_path.kind,
|
||||
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)
|
||||
}
|
||||
|
||||
pub fn is_self_type(&self) -> bool {
|
||||
self.type_anchor.is_none()
|
||||
&& self.generic_args.as_deref().is_none()
|
||||
&& self.mod_path.is_Self()
|
||||
let Path::Normal { mod_path, generic_args, type_anchor } = self else {
|
||||
return false;
|
||||
};
|
||||
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 {
|
||||
fn from(name: Name) -> Path {
|
||||
Path {
|
||||
Path::Normal {
|
||||
type_anchor: None,
|
||||
mod_path: Interned::new(ModPath::from_segments(PathKind::Plain, iter::once(name))),
|
||||
generic_args: None,
|
||||
|
|
|
@ -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
|
||||
Some(trait_ref) => {
|
||||
let Path { mod_path, generic_args: path_generic_args, .. } =
|
||||
Path::from_src(trait_ref.path()?, ctx)?;
|
||||
let Path::Normal { mod_path, generic_args: path_generic_args, .. } =
|
||||
Path::from_src(trait_ref.path()?, ctx)? else
|
||||
{
|
||||
return None;
|
||||
};
|
||||
let num_segments = mod_path.segments().len();
|
||||
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));
|
||||
return Some(Path {
|
||||
return Some(Path::Normal {
|
||||
type_anchor,
|
||||
mod_path,
|
||||
generic_args: if generic_args.is_empty() { None } else { Some(generic_args.into()) },
|
||||
|
|
|
@ -15,8 +15,9 @@ use crate::{
|
|||
expr::{BindingId, ExprId, LabelId},
|
||||
generics::{GenericParams, TypeOrConstParamData},
|
||||
item_scope::{BuiltinShadowMode, BUILTIN_SCOPE},
|
||||
lang_item::LangItemTarget,
|
||||
nameres::DefMap,
|
||||
path::{ModPath, PathKind},
|
||||
path::{ModPath, Path, PathKind},
|
||||
per_ns::PerNs,
|
||||
visibility::{RawVisibility, Visibility},
|
||||
AdtId, AssocItemId, ConstId, ConstParamId, DefWithBodyId, EnumId, EnumVariantId, ExternBlockId,
|
||||
|
@ -176,8 +177,27 @@ impl Resolver {
|
|||
pub fn resolve_path_in_type_ns(
|
||||
&self,
|
||||
db: &dyn DefDatabase,
|
||||
path: &ModPath,
|
||||
path: &Path,
|
||||
) -> 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 skip_to_mod = path.kind != PathKind::Plain;
|
||||
if skip_to_mod {
|
||||
|
@ -217,7 +237,7 @@ impl Resolver {
|
|||
pub fn resolve_path_in_type_ns_fully(
|
||||
&self,
|
||||
db: &dyn DefDatabase,
|
||||
path: &ModPath,
|
||||
path: &Path,
|
||||
) -> Option<TypeNs> {
|
||||
let (res, unresolved) = self.resolve_path_in_type_ns(db, path)?;
|
||||
if unresolved.is_some() {
|
||||
|
@ -245,8 +265,24 @@ impl Resolver {
|
|||
pub fn resolve_path_in_value_ns(
|
||||
&self,
|
||||
db: &dyn DefDatabase,
|
||||
path: &ModPath,
|
||||
path: &Path,
|
||||
) -> 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 tmp = name![self];
|
||||
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(
|
||||
&self,
|
||||
db: &dyn DefDatabase,
|
||||
path: &ModPath,
|
||||
path: &Path,
|
||||
) -> Option<ValueNs> {
|
||||
match self.resolve_path_in_value_ns(db, path)? {
|
||||
ResolveValueResult::ValueNs(it) => Some(it),
|
||||
|
@ -441,7 +477,7 @@ impl Resolver {
|
|||
&Scope::ImplDefScope(impl_) => {
|
||||
if let Some(target_trait) = &db.impl_data(impl_).target_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_);
|
||||
}
|
||||
|
|
|
@ -78,7 +78,7 @@ impl Name {
|
|||
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 {
|
||||
Name::new_text(SmolStr::new_inline(text))
|
||||
}
|
||||
|
@ -112,6 +112,18 @@ impl 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.
|
||||
pub fn as_tuple_index(&self) -> Option<usize> {
|
||||
match self.0 {
|
||||
|
@ -343,6 +355,8 @@ pub mod known {
|
|||
feature,
|
||||
// known methods of lang items
|
||||
call_once,
|
||||
call_mut,
|
||||
call,
|
||||
eq,
|
||||
ne,
|
||||
ge,
|
||||
|
|
|
@ -4,7 +4,7 @@ use base_db::CrateId;
|
|||
use chalk_ir::{BoundVar, DebruijnIndex, GenericArgData};
|
||||
use hir_def::{
|
||||
expr::Expr,
|
||||
path::ModPath,
|
||||
path::Path,
|
||||
resolver::{Resolver, ValueNs},
|
||||
type_ref::ConstRef,
|
||||
ConstId, EnumVariantId,
|
||||
|
@ -15,7 +15,7 @@ use stdx::never;
|
|||
use crate::{
|
||||
db::HirDatabase, infer::InferenceContext, layout::layout_of_ty, lower::ParamLoweringMode,
|
||||
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};
|
||||
|
@ -72,7 +72,7 @@ impl From<MirEvalError> for ConstEvalError {
|
|||
pub(crate) fn path_to_const(
|
||||
db: &dyn HirDatabase,
|
||||
resolver: &Resolver,
|
||||
path: &ModPath,
|
||||
path: &Path,
|
||||
mode: ParamLoweringMode,
|
||||
args_lazy: impl FnOnce() -> Generics,
|
||||
debruijn: DebruijnIndex,
|
||||
|
@ -89,7 +89,7 @@ pub(crate) fn path_to_const(
|
|||
Some(x) => ConstValue::BoundVar(BoundVar::new(debruijn, x)),
|
||||
None => {
|
||||
never!(
|
||||
"Generic list doesn't contain this param: {:?}, {}, {:?}",
|
||||
"Generic list doesn't contain this param: {:?}, {:?}, {:?}",
|
||||
args,
|
||||
path,
|
||||
p
|
||||
|
@ -169,6 +169,7 @@ pub(crate) fn const_eval_recover(
|
|||
_: &dyn HirDatabase,
|
||||
_: &[String],
|
||||
_: &ConstId,
|
||||
_: &Substitution,
|
||||
) -> Result<Const, ConstEvalError> {
|
||||
Err(ConstEvalError::MirLowerError(MirLowerError::Loop))
|
||||
}
|
||||
|
@ -184,10 +185,11 @@ pub(crate) fn const_eval_discriminant_recover(
|
|||
pub(crate) fn const_eval_query(
|
||||
db: &dyn HirDatabase,
|
||||
const_id: ConstId,
|
||||
subst: Substitution,
|
||||
) -> Result<Const, ConstEvalError> {
|
||||
let def = const_id.into();
|
||||
let body = db.mir_body(def)?;
|
||||
let c = interpret_mir(db, &body, false)?;
|
||||
let c = interpret_mir(db, &body, subst, false)?;
|
||||
Ok(c)
|
||||
}
|
||||
|
||||
|
@ -210,7 +212,7 @@ pub(crate) fn const_eval_discriminant_variant(
|
|||
return Ok(value);
|
||||
}
|
||||
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;
|
||||
Ok(c)
|
||||
}
|
||||
|
@ -228,13 +230,13 @@ pub(crate) fn eval_to_const(
|
|||
let db = ctx.db;
|
||||
if let Expr::Path(p) = &ctx.body.exprs[expr] {
|
||||
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;
|
||||
}
|
||||
}
|
||||
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(result) = interpret_mir(db, &mir_body, true) {
|
||||
if let Ok(result) = interpret_mir(db, &mir_body, Substitution::empty(Interner), true) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
use base_db::fixture::WithFixture;
|
||||
use chalk_ir::Substitution;
|
||||
use hir_def::db::DefDatabase;
|
||||
|
||||
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::{
|
||||
|
@ -10,6 +12,8 @@ use super::{
|
|||
ConstEvalError,
|
||||
};
|
||||
|
||||
mod intrinsics;
|
||||
|
||||
fn simplify(e: ConstEvalError) -> ConstEvalError {
|
||||
match e {
|
||||
ConstEvalError::MirEvalError(MirEvalError::InFunction(_, e)) => {
|
||||
|
@ -30,7 +34,12 @@ fn check_number(ra_fixture: &str, answer: i128) {
|
|||
match &r.data(Interner).value {
|
||||
chalk_ir::ConstValue::Concrete(c) => match &c.interned {
|
||||
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),
|
||||
},
|
||||
|
@ -56,7 +65,7 @@ fn eval_goal(ra_fixture: &str) -> Result<Const, ConstEvalError> {
|
|||
_ => None,
|
||||
})
|
||||
.unwrap();
|
||||
db.const_eval(const_id)
|
||||
db.const_eval(const_id, Substitution::empty(Interner))
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -76,6 +85,49 @@ fn bit_op() {
|
|||
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]
|
||||
fn locals() {
|
||||
check_number(
|
||||
|
@ -166,8 +218,7 @@ fn reference_autoderef() {
|
|||
|
||||
#[test]
|
||||
fn overloaded_deref() {
|
||||
// FIXME: We should support this.
|
||||
check_fail(
|
||||
check_number(
|
||||
r#"
|
||||
//- minicore: deref_mut
|
||||
struct Foo;
|
||||
|
@ -185,9 +236,7 @@ fn overloaded_deref() {
|
|||
*y + *x
|
||||
};
|
||||
"#,
|
||||
ConstEvalError::MirLowerError(MirLowerError::NotSupported(
|
||||
"explicit overloaded deref".into(),
|
||||
)),
|
||||
10,
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -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]
|
||||
fn function_call() {
|
||||
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]
|
||||
fn trait_basic() {
|
||||
check_number(
|
||||
|
@ -483,6 +554,42 @@ fn loops() {
|
|||
"#,
|
||||
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]
|
||||
|
@ -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]
|
||||
fn recursion() {
|
||||
check_number(
|
||||
|
@ -555,6 +674,38 @@ fn structs() {
|
|||
"#,
|
||||
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]
|
||||
|
@ -599,13 +750,14 @@ fn tuples() {
|
|||
);
|
||||
check_number(
|
||||
r#"
|
||||
struct TupleLike(i32, u8, i64, u16);
|
||||
const GOAL: u8 = {
|
||||
struct TupleLike(i32, i64, u8, u16);
|
||||
const GOAL: i64 = {
|
||||
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(
|
||||
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]
|
||||
fn pattern_matching_ergonomics() {
|
||||
check_number(
|
||||
|
@ -665,6 +847,16 @@ fn pattern_matching_ergonomics() {
|
|||
"#,
|
||||
5,
|
||||
);
|
||||
check_number(
|
||||
r#"
|
||||
const GOAL: u8 = {
|
||||
let a = &(2, 3);
|
||||
let &(x, y) = a;
|
||||
x + y
|
||||
};
|
||||
"#,
|
||||
5,
|
||||
);
|
||||
}
|
||||
|
||||
#[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]
|
||||
fn options() {
|
||||
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]
|
||||
fn or_pattern() {
|
||||
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]
|
||||
fn array_and_index() {
|
||||
check_number(
|
||||
|
@ -867,6 +1385,17 @@ fn array_and_index() {
|
|||
check_number(
|
||||
r#"
|
||||
//- 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();"#,
|
||||
5,
|
||||
);
|
||||
|
@ -991,8 +1520,7 @@ fn const_generic_subst_fn() {
|
|||
|
||||
#[test]
|
||||
fn const_generic_subst_assoc_const_impl() {
|
||||
// FIXME: this should evaluate to 5
|
||||
check_fail(
|
||||
check_number(
|
||||
r#"
|
||||
struct Adder<const N: usize, const M: usize>;
|
||||
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;
|
||||
"#,
|
||||
ConstEvalError::MirEvalError(MirEvalError::TypeError("missing generic arg")),
|
||||
5,
|
||||
);
|
||||
}
|
||||
|
||||
|
|
162
crates/hir-ty/src/consteval/tests/intrinsics.rs
Normal file
162
crates/hir-ty/src/consteval/tests/intrinsics.rs
Normal 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,
|
||||
);
|
||||
}
|
|
@ -57,7 +57,7 @@ pub trait HirDatabase: DefDatabase + Upcast<dyn DefDatabase> {
|
|||
|
||||
#[salsa::invoke(crate::consteval::const_eval_query)]
|
||||
#[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::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)]
|
||||
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)]
|
||||
fn trait_environment(&self, def: GenericDefId) -> Arc<crate::TraitEnvironment>;
|
||||
|
||||
|
|
|
@ -73,7 +73,7 @@ fn walk_unsafe(
|
|||
}
|
||||
Expr::Path(path) => {
|
||||
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 db.static_data(id).mutable {
|
||||
unsafe_expr_cb(UnsafeExpr { expr: current, inside_unsafe_block });
|
||||
|
|
|
@ -25,22 +25,22 @@ use hir_def::{
|
|||
expr::{BindingAnnotation, BindingId, ExprId, ExprOrPatId, PatId},
|
||||
lang_item::{LangItem, LangItemTarget},
|
||||
layout::Integer,
|
||||
path::Path,
|
||||
path::{ModPath, Path},
|
||||
resolver::{HasResolver, ResolveValueResult, Resolver, TypeNs, ValueNs},
|
||||
type_ref::TypeRef,
|
||||
AdtId, AssocItemId, DefWithBodyId, EnumVariantId, FieldId, FunctionId, HasModule,
|
||||
ItemContainerId, Lookup, TraitId, TypeAliasId, VariantId,
|
||||
AdtId, AssocItemId, DefWithBodyId, EnumVariantId, FieldId, FunctionId, ItemContainerId, Lookup,
|
||||
TraitId, TypeAliasId, VariantId,
|
||||
};
|
||||
use hir_expand::name::{name, Name};
|
||||
use la_arena::ArenaMap;
|
||||
use rustc_hash::{FxHashMap, FxHashSet};
|
||||
use stdx::always;
|
||||
use stdx::{always, never};
|
||||
|
||||
use crate::{
|
||||
db::HirDatabase, fold_tys, fold_tys_and_consts, infer::coerce::CoerceMany,
|
||||
lower::ImplTraitLoweringMode, to_assoc_type_id, AliasEq, AliasTy, Const, DomainGoal,
|
||||
GenericArg, Goal, ImplTraitId, InEnvironment, Interner, ProjectionTy, RpitId, Substitution,
|
||||
TraitEnvironment, TraitRef, Ty, TyBuilder, TyExt, TyKind,
|
||||
lower::ImplTraitLoweringMode, static_lifetime, to_assoc_type_id, AliasEq, AliasTy, Const,
|
||||
DomainGoal, GenericArg, Goal, ImplTraitId, InEnvironment, Interner, ProjectionTy, RpitId,
|
||||
Substitution, TraitEnvironment, TraitRef, Ty, TyBuilder, TyExt, TyKind,
|
||||
};
|
||||
|
||||
// This lint has a false positive here. See the link below for details.
|
||||
|
@ -57,6 +57,7 @@ mod expr;
|
|||
mod pat;
|
||||
mod coerce;
|
||||
mod closure;
|
||||
mod mutability;
|
||||
|
||||
/// The entry point of type inference.
|
||||
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_mut_body();
|
||||
|
||||
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) {
|
||||
return ty;
|
||||
}
|
||||
let krate = owner.module(db.upcast()).krate();
|
||||
let trait_env = owner
|
||||
.as_generic_def_id()
|
||||
.map_or_else(|| Arc::new(TraitEnvironment::empty(krate)), |d| db.trait_environment(d));
|
||||
let trait_env = db.trait_environment_for_body(owner);
|
||||
let mut table = unify::InferenceTable::new(db, trait_env);
|
||||
|
||||
let ty_with_vars = table.normalize_associated_types_in(ty);
|
||||
|
@ -276,6 +276,13 @@ pub struct Adjustment {
|
|||
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)]
|
||||
pub enum Adjust {
|
||||
/// Go from ! to any type.
|
||||
|
@ -506,10 +513,7 @@ impl<'a> InferenceContext<'a> {
|
|||
body: &'a Body,
|
||||
resolver: Resolver,
|
||||
) -> Self {
|
||||
let krate = owner.module(db.upcast()).krate();
|
||||
let trait_env = owner
|
||||
.as_generic_def_id()
|
||||
.map_or_else(|| Arc::new(TraitEnvironment::empty(krate)), |d| db.trait_environment(d));
|
||||
let trait_env = db.trait_environment_for_body(owner);
|
||||
InferenceContext {
|
||||
result: InferenceResult::default(),
|
||||
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:
|
||||
// https://play.rust-lang.org/?gist=087992e9e22495446c01c0d4e2d69521
|
||||
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 {
|
||||
ValueNs::EnumVariantId(var) => {
|
||||
let substs = ctx.substs_from_path(path, var.into(), true);
|
||||
|
@ -872,11 +876,15 @@ impl<'a> InferenceContext<'a> {
|
|||
None => return (self.err_ty(), None),
|
||||
}
|
||||
} 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,
|
||||
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 {
|
||||
TypeNs::AdtId(AdtId::StructId(strukt)) => {
|
||||
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 substs = generics.placeholder_subst(self.db);
|
||||
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) => {
|
||||
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)
|
||||
.fill_with_inference_vars(&mut self.table)
|
||||
.build();
|
||||
self.resolve_variant_on_alias(ty, unresolved, path)
|
||||
self.resolve_variant_on_alias(ty, unresolved, mod_path)
|
||||
}
|
||||
TypeNs::AdtSelfType(_) => {
|
||||
// FIXME this could happen in array size expressions, once we're checking them
|
||||
|
@ -953,9 +961,9 @@ impl<'a> InferenceContext<'a> {
|
|||
&mut self,
|
||||
ty: Ty,
|
||||
unresolved: Option<usize>,
|
||||
path: &Path,
|
||||
path: &ModPath,
|
||||
) -> (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 {
|
||||
None => {
|
||||
let variant = ty.as_adt().and_then(|(adt_id, _)| match adt_id {
|
||||
|
@ -969,7 +977,7 @@ impl<'a> InferenceContext<'a> {
|
|||
(ty, variant)
|
||||
}
|
||||
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
|
||||
if let Some((AdtId::EnumId(enum_id), _)) = ty.as_adt() {
|
||||
let enum_data = self.db.enum_data(enum_id);
|
||||
|
@ -1017,10 +1025,6 @@ impl<'a> InferenceContext<'a> {
|
|||
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> {
|
||||
self.resolve_output_on(self.resolve_lang_trait(LangItem::Neg)?)
|
||||
}
|
||||
|
|
|
@ -51,11 +51,12 @@ fn success(
|
|||
pub(super) struct CoerceMany {
|
||||
expected_ty: Ty,
|
||||
final_ty: Option<Ty>,
|
||||
expressions: Vec<ExprId>,
|
||||
}
|
||||
|
||||
impl CoerceMany {
|
||||
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
|
||||
|
@ -125,8 +126,15 @@ impl CoerceMany {
|
|||
let result1 = ctx.table.coerce_inner(self.merged_ty(), &target_ty);
|
||||
let result2 = ctx.table.coerce_inner(expr_ty.clone(), &target_ty);
|
||||
if let (Ok(result1), Ok(result2)) = (result1, result2) {
|
||||
ctx.table.register_infer_ok(result1);
|
||||
ctx.table.register_infer_ok(result2);
|
||||
ctx.table.register_infer_ok(InferOk { value: (), goals: result1.goals });
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
@ -148,6 +156,9 @@ impl CoerceMany {
|
|||
}
|
||||
cov_mark::hit!(coerce_merge_fail_fallback);
|
||||
}
|
||||
if let Some(expr) = expr {
|
||||
self.expressions.push(expr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -34,6 +34,7 @@ use crate::{
|
|||
method_resolution::{self, lang_items_for_bin_op, VisibleFromModule},
|
||||
primitive::{self, UintTy},
|
||||
static_lifetime, to_chalk_trait_id,
|
||||
traits::FnTrait,
|
||||
utils::{generics, Generics},
|
||||
Adjust, Adjustment, AdtId, AutoBorrow, Binders, CallableDefId, FnPointer, FnSig, FnSubst,
|
||||
Interner, Rawness, Scalar, Substitution, TraitRef, Ty, TyBuilder, TyExt,
|
||||
|
@ -158,26 +159,6 @@ impl<'a> InferenceContext<'a> {
|
|||
})
|
||||
.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 } => {
|
||||
let ret_ty = self.table.new_type_var();
|
||||
let prev_diverges = mem::replace(&mut self.diverges, Diverges::Maybe);
|
||||
|
@ -385,16 +366,46 @@ impl<'a> InferenceContext<'a> {
|
|||
|| res.is_none();
|
||||
let (param_tys, ret_ty) = match res {
|
||||
Some((func, params, ret_ty)) => {
|
||||
let adjustments = auto_deref_adjust_steps(&derefs);
|
||||
// FIXME: Handle call adjustments for Fn/FnMut
|
||||
self.write_expr_adj(*callee, adjustments);
|
||||
if let Some((trait_, func)) = func {
|
||||
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());
|
||||
let mut adjustments = auto_deref_adjust_steps(&derefs);
|
||||
if let Some(fn_x) = func {
|
||||
match fn_x {
|
||||
FnTrait::FnOnce => (),
|
||||
FnTrait::FnMut => {
|
||||
if !matches!(
|
||||
derefed_callee.kind(Interner),
|
||||
TyKind::Ref(Mutability::Mut, _, _)
|
||||
) {
|
||||
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)
|
||||
}
|
||||
None => {
|
||||
|
@ -601,26 +612,18 @@ impl<'a> InferenceContext<'a> {
|
|||
let inner_ty = self.infer_expr_inner(*expr, &Expectation::none());
|
||||
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 } => {
|
||||
let cast_ty = self.make_ty(type_ref);
|
||||
// FIXME: propagate the "castable to" expectation
|
||||
let _inner_ty = self.infer_expr_no_expect(*expr);
|
||||
// FIXME check the cast...
|
||||
let inner_ty = self.infer_expr_no_expect(*expr);
|
||||
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
|
||||
}
|
||||
Expr::Ref { expr, rawness, mutability } => {
|
||||
|
@ -656,6 +659,23 @@ impl<'a> InferenceContext<'a> {
|
|||
// FIXME: Note down method resolution her
|
||||
match op {
|
||||
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())
|
||||
}
|
||||
UnaryOp::Neg => {
|
||||
|
|
196
crates/hir-ty/src/infer/mutability.rs
Normal file
196
crates/hir-ty/src/infer/mutability.rs
Normal 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
|
||||
}
|
||||
}
|
|
@ -39,7 +39,7 @@ impl<'a> InferenceContext<'a> {
|
|||
} else {
|
||||
// FIXME: report error, unresolved first path segment
|
||||
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 {
|
||||
ResolveValueResult::ValueNs(it) => (it, None),
|
||||
|
|
|
@ -8,16 +8,15 @@ use chalk_ir::{
|
|||
};
|
||||
use chalk_solve::infer::ParameterEnaVariableExt;
|
||||
use ena::unify::UnifyKey;
|
||||
use hir_def::{FunctionId, TraitId};
|
||||
use hir_expand::name;
|
||||
use stdx::never;
|
||||
|
||||
use super::{InferOk, InferResult, InferenceContext, TypeError};
|
||||
use crate::{
|
||||
db::HirDatabase, fold_tys, static_lifetime, traits::FnTrait, AliasEq, AliasTy, BoundVar,
|
||||
Canonical, Const, DebruijnIndex, GenericArg, GenericArgData, Goal, Guidance, InEnvironment,
|
||||
InferenceVar, Interner, Lifetime, ParamKind, ProjectionTy, ProjectionTyExt, Scalar, Solution,
|
||||
Substitution, TraitEnvironment, Ty, TyBuilder, TyExt, TyKind, VariableKind,
|
||||
db::HirDatabase, fold_tys, static_lifetime, to_chalk_trait_id, traits::FnTrait, AliasEq,
|
||||
AliasTy, BoundVar, Canonical, Const, DebruijnIndex, GenericArg, GenericArgData, Goal, Guidance,
|
||||
InEnvironment, InferenceVar, Interner, Lifetime, ParamKind, ProjectionTy, ProjectionTyExt,
|
||||
Scalar, Solution, Substitution, TraitEnvironment, Ty, TyBuilder, TyExt, TyKind, VariableKind,
|
||||
};
|
||||
|
||||
impl<'a> InferenceContext<'a> {
|
||||
|
@ -631,10 +630,13 @@ impl<'a> InferenceTable<'a> {
|
|||
&mut self,
|
||||
ty: &Ty,
|
||||
num_args: usize,
|
||||
) -> Option<(Option<(TraitId, FunctionId)>, Vec<Ty>, Ty)> {
|
||||
) -> Option<(Option<FnTrait>, Vec<Ty>, Ty)> {
|
||||
match ty.callable_sig(self.db) {
|
||||
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,
|
||||
ty: &Ty,
|
||||
num_args: usize,
|
||||
) -> Option<(Option<(TraitId, FunctionId)>, Vec<Ty>, Ty)> {
|
||||
) -> Option<(FnTrait, Vec<Ty>, Ty)> {
|
||||
let krate = self.trait_env.krate;
|
||||
let fn_once_trait = FnTrait::FnOnce.get_id(self.db, krate)?;
|
||||
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 mut trait_ref = projection.trait_ref(self.db);
|
||||
let obligation = InEnvironment {
|
||||
goal: projection.trait_ref(self.db).cast(Interner),
|
||||
environment: trait_env,
|
||||
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() {
|
||||
self.register_obligation(obligation.goal);
|
||||
let return_ty = self.normalize_projection_ty(projection);
|
||||
Some((
|
||||
Some(fn_once_trait).zip(trait_data.method_by_name(&name!(call_once))),
|
||||
arg_tys,
|
||||
return_ty,
|
||||
))
|
||||
for fn_x in [FnTrait::Fn, FnTrait::FnMut, FnTrait::FnOnce] {
|
||||
let fn_x_trait = fn_x.get_id(self.db, krate)?;
|
||||
trait_ref.trait_id = to_chalk_trait_id(fn_x_trait);
|
||||
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 {
|
||||
None
|
||||
}
|
||||
|
|
|
@ -576,10 +576,14 @@ where
|
|||
}
|
||||
|
||||
pub fn callable_sig_from_fnonce(
|
||||
self_ty: &Ty,
|
||||
mut self_ty: &Ty,
|
||||
env: Arc<TraitEnvironment>,
|
||||
db: &dyn HirDatabase,
|
||||
) -> 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 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])?;
|
||||
|
|
|
@ -25,12 +25,12 @@ use hir_def::{
|
|||
TypeOrConstParamData, TypeParamProvenance, WherePredicate, WherePredicateTypeTarget,
|
||||
},
|
||||
lang_item::{lang_attr, LangItem},
|
||||
path::{GenericArg, ModPath, Path, PathKind, PathSegment, PathSegments},
|
||||
path::{GenericArg, GenericArgs, ModPath, Path, PathKind, PathSegment, PathSegments},
|
||||
resolver::{HasResolver, Resolver, TypeNs},
|
||||
type_ref::{ConstRefOrPath, TraitBoundModifier, TraitRef as HirTraitRef, TypeBound, TypeRef},
|
||||
AdtId, AssocItemId, ConstId, ConstParamId, EnumId, EnumVariantId, FunctionId, GenericDefId,
|
||||
HasModule, ImplId, ItemContainerId, LocalFieldId, Lookup, ModuleDefId, StaticId, StructId,
|
||||
TraitId, TypeAliasId, TypeOrConstParamId, TypeParamId, UnionId, VariantId,
|
||||
AdtId, AssocItemId, ConstId, ConstParamId, DefWithBodyId, EnumId, EnumVariantId, FunctionId,
|
||||
GenericDefId, HasModule, ImplId, ItemContainerId, LocalFieldId, Lookup, ModuleDefId, StaticId,
|
||||
StructId, TraitId, TypeAliasId, TypeOrConstParamId, TypeParamId, UnionId, VariantId,
|
||||
};
|
||||
use hir_expand::{name::Name, ExpandResult};
|
||||
use intern::Interned;
|
||||
|
@ -425,11 +425,10 @@ impl<'a> TyLoweringContext<'a> {
|
|||
if path.segments().len() > 1 {
|
||||
return None;
|
||||
}
|
||||
let resolution =
|
||||
match self.resolver.resolve_path_in_type_ns(self.db.upcast(), path.mod_path()) {
|
||||
Some((it, None)) => it,
|
||||
_ => return None,
|
||||
};
|
||||
let resolution = match self.resolver.resolve_path_in_type_ns(self.db.upcast(), path) {
|
||||
Some((it, None)) => it,
|
||||
_ => return None,
|
||||
};
|
||||
match resolution {
|
||||
TypeNs::GenericParam(param_id) => Some(param_id.into()),
|
||||
_ => None,
|
||||
|
@ -608,7 +607,7 @@ impl<'a> TyLoweringContext<'a> {
|
|||
}
|
||||
|
||||
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,
|
||||
None => return (TyKind::Error.intern(Interner), None),
|
||||
};
|
||||
|
@ -716,7 +715,7 @@ impl<'a> TyLoweringContext<'a> {
|
|||
resolved: ValueTyDefId,
|
||||
infer_args: bool,
|
||||
) -> 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 {
|
||||
ValueTyDefId::FunctionId(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 penultimate = len.checked_sub(2).and_then(|idx| path.segments().get(idx));
|
||||
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,
|
||||
};
|
||||
(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(
|
||||
|
@ -747,6 +753,21 @@ impl<'a> TyLoweringContext<'a> {
|
|||
def: Option<GenericDefId>,
|
||||
infer_args: bool,
|
||||
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 {
|
||||
// Remember that the item's own generic args come before its parent's.
|
||||
let mut substs = Vec::new();
|
||||
|
@ -780,7 +801,7 @@ impl<'a> TyLoweringContext<'a> {
|
|||
};
|
||||
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 {
|
||||
fill_self_params();
|
||||
}
|
||||
|
@ -879,12 +900,11 @@ impl<'a> TyLoweringContext<'a> {
|
|||
path: &Path,
|
||||
explicit_self_ty: Option<Ty>,
|
||||
) -> Option<TraitRef> {
|
||||
let resolved =
|
||||
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.
|
||||
TypeNs::TraitId(tr) => tr,
|
||||
_ => return None,
|
||||
};
|
||||
let resolved = match self.resolver.resolve_path_in_type_ns_fully(self.db.upcast(), path)? {
|
||||
// FIXME(trait_alias): We need to handle trait alias here.
|
||||
TypeNs::TraitId(tr) => tr,
|
||||
_ => return None,
|
||||
};
|
||||
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))
|
||||
}
|
||||
|
@ -1381,9 +1401,7 @@ pub(crate) fn generic_predicates_for_param_query(
|
|||
Some(it) => it,
|
||||
None => return true,
|
||||
};
|
||||
let tr = match resolver
|
||||
.resolve_path_in_type_ns_fully(db.upcast(), path.mod_path())
|
||||
{
|
||||
let tr = match resolver.resolve_path_in_type_ns_fully(db.upcast(), path) {
|
||||
Some(TypeNs::TraitId(tr)) => tr,
|
||||
_ => return false,
|
||||
};
|
||||
|
@ -1423,6 +1441,17 @@ pub(crate) fn generic_predicates_for_param_recover(
|
|||
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(
|
||||
db: &dyn HirDatabase,
|
||||
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
|
||||
// it works.
|
||||
if let TypeRef::Path(p) = t {
|
||||
let p = p.mod_path();
|
||||
let p = p.mod_path()?;
|
||||
if p.kind == PathKind::Plain {
|
||||
if let [n] = p.segments() {
|
||||
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::Path(n) => {
|
||||
let path = ModPath::from_segments(PathKind::Plain, Some(n.clone()));
|
||||
path_to_const(db, resolver, &path, mode, args, debruijn)
|
||||
.unwrap_or_else(|| unknown_const(expected_ty))
|
||||
path_to_const(
|
||||
db,
|
||||
resolver,
|
||||
&Path::from_known_path_with_no_generic(path),
|
||||
mode,
|
||||
args,
|
||||
debruijn,
|
||||
)
|
||||
.unwrap_or_else(|| unknown_const(expected_ty))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
use std::{ops::ControlFlow, sync::Arc};
|
||||
|
||||
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::{
|
||||
data::ImplData, item_scope::ItemScope, lang_item::LangItem, nameres::DefMap, AssocItemId,
|
||||
BlockId, ConstId, FunctionId, HasModule, ImplId, ItemContainerId, Lookup, ModuleDefId,
|
||||
|
@ -20,7 +20,7 @@ use crate::{
|
|||
autoderef::{self, AutoderefKind},
|
||||
db::HirDatabase,
|
||||
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},
|
||||
static_lifetime, to_chalk_trait_id,
|
||||
utils::all_super_traits,
|
||||
|
@ -600,9 +600,9 @@ impl ReceiverAdjustments {
|
|||
}
|
||||
}
|
||||
if let Some(m) = self.autoref {
|
||||
ty = TyKind::Ref(m, static_lifetime(), ty).intern(Interner);
|
||||
adjust
|
||||
.push(Adjustment { kind: Adjust::Borrow(AutoBorrow::Ref(m)), target: ty.clone() });
|
||||
let a = Adjustment::borrow(m, ty);
|
||||
ty = a.target.clone();
|
||||
adjust.push(a);
|
||||
}
|
||||
if self.unsize_array {
|
||||
ty = 'x: {
|
||||
|
@ -692,6 +692,39 @@ pub fn lookup_impl_const(
|
|||
.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`.
|
||||
///
|
||||
/// 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,
|
||||
fn_subst: Substitution,
|
||||
) -> (FunctionId, Substitution) {
|
||||
let trait_id = match func.lookup(db.upcast()).container {
|
||||
ItemContainerId::TraitId(id) => id,
|
||||
_ => return (func, fn_subst),
|
||||
let ItemContainerId::TraitId(trait_id) = func.lookup(db.upcast()).container else {
|
||||
return (func, fn_subst)
|
||||
};
|
||||
let trait_params = db.generic_params(trait_id.into()).type_or_consts.len();
|
||||
let fn_params = fn_subst.len(Interner) - trait_params;
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -4,19 +4,21 @@ use std::{iter, mem, sync::Arc};
|
|||
|
||||
use chalk_ir::{BoundVar, ConstData, DebruijnIndex, TyKind};
|
||||
use hir_def::{
|
||||
adt::{StructKind, VariantData},
|
||||
body::Body,
|
||||
expr::{
|
||||
Array, BindingAnnotation, BindingId, ExprId, LabelId, Literal, MatchArm, Pat, PatId,
|
||||
RecordLitField,
|
||||
RecordFieldPat, RecordLitField,
|
||||
},
|
||||
lang_item::{LangItem, LangItemTarget},
|
||||
layout::LayoutError,
|
||||
path::Path,
|
||||
resolver::{resolver_for_expr, ResolveValueResult, ValueNs},
|
||||
DefWithBodyId, EnumVariantId, HasModule,
|
||||
AdtId, DefWithBodyId, EnumVariantId, HasModule, ItemContainerId, LocalFieldId, TraitId,
|
||||
};
|
||||
use hir_expand::name::Name;
|
||||
use la_arena::ArenaMap;
|
||||
use rustc_hash::FxHashMap;
|
||||
|
||||
use crate::{
|
||||
consteval::ConstEvalError, db::HirDatabase, display::HirDisplay, infer::TypeMismatch,
|
||||
|
@ -27,18 +29,25 @@ use crate::{
|
|||
use super::*;
|
||||
|
||||
mod as_place;
|
||||
mod pattern_matching;
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
use pattern_matching::AdtPatternShape;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
struct LoopBlocks {
|
||||
begin: BasicBlockId,
|
||||
/// `None` for loops that are not terminating
|
||||
end: Option<BasicBlockId>,
|
||||
place: Place,
|
||||
}
|
||||
|
||||
struct MirLowerCtx<'a> {
|
||||
result: MirBody,
|
||||
owner: DefWithBodyId,
|
||||
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>,
|
||||
db: &'a dyn HirDatabase,
|
||||
body: &'a Body,
|
||||
|
@ -50,6 +59,8 @@ pub enum MirLowerError {
|
|||
ConstEvalError(Box<ConstEvalError>),
|
||||
LayoutError(LayoutError),
|
||||
IncompleteExpr,
|
||||
/// Trying to lower a trait function, instead of an implementation
|
||||
TraitFunctionDefinition(TraitId, Name),
|
||||
UnresolvedName(String),
|
||||
RecordLiteralWithoutPath,
|
||||
UnresolvedMethod,
|
||||
|
@ -66,6 +77,7 @@ pub enum MirLowerError {
|
|||
ImplementationError(&'static str),
|
||||
LangItemNotFound(LangItem),
|
||||
MutatingRvalue,
|
||||
UnresolvedLabel,
|
||||
}
|
||||
|
||||
macro_rules! not_supported {
|
||||
|
@ -200,26 +212,42 @@ impl MirLowerCtx<'_> {
|
|||
mut current: BasicBlockId,
|
||||
) -> Result<Option<BasicBlockId>> {
|
||||
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) => {
|
||||
let unresolved_name = || MirLowerError::unresolved_path(self.db, p);
|
||||
let resolver = resolver_for_expr(self.db.upcast(), self.owner, expr_id);
|
||||
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)?;
|
||||
let pr = match pr {
|
||||
ResolveValueResult::ValueNs(v) => v,
|
||||
ResolveValueResult::Partial(..) => {
|
||||
if let Some(assoc) = self
|
||||
if let Some((assoc, subst)) = self
|
||||
.infer
|
||||
.assoc_resolutions_for_expr(expr_id)
|
||||
{
|
||||
match assoc.0 {
|
||||
match assoc {
|
||||
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))
|
||||
},
|
||||
_ => 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
|
||||
.infer
|
||||
|
@ -246,19 +274,23 @@ impl MirLowerCtx<'_> {
|
|||
Ok(Some(current))
|
||||
}
|
||||
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))
|
||||
}
|
||||
ValueNs::EnumVariantId(variant_id) => {
|
||||
let ty = self.infer.type_of_expr[expr_id].clone();
|
||||
let current = self.lower_enum_variant(
|
||||
variant_id,
|
||||
current,
|
||||
place,
|
||||
ty,
|
||||
vec![],
|
||||
expr_id.into(),
|
||||
)?;
|
||||
let variant_data = &self.db.enum_data(variant_id.parent).variants[variant_id.local_id];
|
||||
if variant_data.variant_data.kind() == StructKind::Unit {
|
||||
let ty = self.infer.type_of_expr[expr_id].clone();
|
||||
current = self.lower_enum_variant(
|
||||
variant_id,
|
||||
current,
|
||||
place,
|
||||
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))
|
||||
}
|
||||
ValueNs::GenericParam(p) => {
|
||||
|
@ -287,7 +319,7 @@ impl MirLowerCtx<'_> {
|
|||
);
|
||||
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.
|
||||
Ok(Some(current))
|
||||
}
|
||||
|
@ -349,19 +381,29 @@ impl MirLowerCtx<'_> {
|
|||
Ok(self.merge_blocks(Some(then_target), else_target))
|
||||
}
|
||||
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 } => {
|
||||
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)? {
|
||||
this.set_goto(block, begin);
|
||||
}
|
||||
Ok(())
|
||||
}),
|
||||
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 {
|
||||
return Ok(());
|
||||
};
|
||||
|
@ -412,7 +454,7 @@ impl MirLowerCtx<'_> {
|
|||
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.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)?
|
||||
else {
|
||||
return Ok(());
|
||||
|
@ -426,7 +468,8 @@ impl MirLowerCtx<'_> {
|
|||
current,
|
||||
pat.into(),
|
||||
Some(end),
|
||||
&[pat], &None)?;
|
||||
AdtPatternShape::Tuple { args: &[pat], ellipsis: None },
|
||||
)?;
|
||||
if let Some((_, block)) = this.lower_expr_as_place(current, body, true)? {
|
||||
this.set_goto(block, begin);
|
||||
}
|
||||
|
@ -434,36 +477,36 @@ impl MirLowerCtx<'_> {
|
|||
})
|
||||
},
|
||||
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);
|
||||
match &callee_ty.data(Interner).kind {
|
||||
chalk_ir::TyKind::FnDef(..) => {
|
||||
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))
|
||||
}
|
||||
TyKind::Scalar(_)
|
||||
| TyKind::Tuple(_, _)
|
||||
| TyKind::Array(_, _)
|
||||
| TyKind::Adt(_, _)
|
||||
| TyKind::Str
|
||||
| TyKind::Foreign(_)
|
||||
| TyKind::Slice(_) => {
|
||||
return Err(MirLowerError::TypeError("function call on data type"))
|
||||
chalk_ir::TyKind::Function(_) => {
|
||||
let Some((func, current)) = self.lower_expr_to_some_operand(*callee, current)? else {
|
||||
return Ok(None);
|
||||
};
|
||||
self.lower_call_and_args(func, args.iter().copied(), place, current, self.is_uninhabited(expr_id))
|
||||
}
|
||||
TyKind::Error => return Err(MirLowerError::MissingFunctionDefinition),
|
||||
TyKind::AssociatedType(_, _)
|
||||
| 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"),
|
||||
_ => return Err(MirLowerError::TypeError("function call on bad type")),
|
||||
}
|
||||
}
|
||||
Expr::MethodCall { receiver, args, .. } => {
|
||||
|
@ -491,10 +534,7 @@ impl MirLowerCtx<'_> {
|
|||
let cond_ty = self.expr_ty_after_adjustments(*expr);
|
||||
let mut end = None;
|
||||
for MatchArm { pat, guard, expr } in arms.iter() {
|
||||
if guard.is_some() {
|
||||
not_supported!("pattern matching with guard");
|
||||
}
|
||||
let (then, otherwise) = self.pattern_match(
|
||||
let (then, mut otherwise) = self.pattern_match(
|
||||
current,
|
||||
None,
|
||||
cond_place.clone(),
|
||||
|
@ -502,6 +542,16 @@ impl MirLowerCtx<'_> {
|
|||
*pat,
|
||||
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)? {
|
||||
let r = end.get_or_insert_with(|| self.new_basic_block());
|
||||
self.set_goto(block, *r);
|
||||
|
@ -524,24 +574,28 @@ impl MirLowerCtx<'_> {
|
|||
Some(_) => not_supported!("continue with label"),
|
||||
None => {
|
||||
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);
|
||||
Ok(None)
|
||||
}
|
||||
},
|
||||
Expr::Break { expr, label } => {
|
||||
if expr.is_some() {
|
||||
not_supported!("break with value");
|
||||
}
|
||||
match label {
|
||||
Some(_) => not_supported!("break with label"),
|
||||
None => {
|
||||
let end =
|
||||
self.current_loop_end()?;
|
||||
self.set_goto(current, end);
|
||||
Ok(None)
|
||||
}
|
||||
if let Some(expr) = expr {
|
||||
let loop_data = match label {
|
||||
Some(l) => self.labeled_loop_blocks.get(l).ok_or(MirLowerError::UnresolvedLabel)?,
|
||||
None => self.current_loop_blocks.as_ref().ok_or(MirLowerError::BreakWithoutLoop)?,
|
||||
};
|
||||
let Some(c) = self.lower_expr_to_place(*expr, loop_data.place.clone(), current)? else {
|
||||
return Ok(None);
|
||||
};
|
||||
current = c;
|
||||
}
|
||||
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 } => {
|
||||
if let Some(expr) = expr {
|
||||
|
@ -555,7 +609,17 @@ impl MirLowerCtx<'_> {
|
|||
Ok(None)
|
||||
}
|
||||
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
|
||||
.infer
|
||||
.variant_resolution_for_expr(expr_id)
|
||||
|
@ -585,9 +649,24 @@ impl MirLowerCtx<'_> {
|
|||
place,
|
||||
Rvalue::Aggregate(
|
||||
AggregateKind::Adt(variant_id, subst),
|
||||
operands.into_iter().map(|x| x).collect::<Option<_>>().ok_or(
|
||||
MirLowerError::TypeError("missing field in record literal"),
|
||||
)?,
|
||||
match spread_place {
|
||||
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(),
|
||||
);
|
||||
|
@ -608,9 +687,7 @@ impl MirLowerCtx<'_> {
|
|||
}
|
||||
}
|
||||
Expr::Await { .. } => not_supported!("await"),
|
||||
Expr::Try { .. } => not_supported!("? operator"),
|
||||
Expr::Yeet { .. } => not_supported!("yeet"),
|
||||
Expr::TryBlock { .. } => not_supported!("try block"),
|
||||
Expr::Async { .. } => not_supported!("async block"),
|
||||
Expr::Const { .. } => not_supported!("anonymous const block"),
|
||||
Expr::Cast { expr, type_ref: _ } => {
|
||||
|
@ -703,7 +780,49 @@ impl MirLowerCtx<'_> {
|
|||
);
|
||||
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::Tuple { exprs, is_assignee_expr: _ } => {
|
||||
let Some(values) = exprs
|
||||
|
@ -832,9 +951,10 @@ impl MirLowerCtx<'_> {
|
|||
const_id: hir_def::ConstId,
|
||||
prev_block: BasicBlockId,
|
||||
place: Place,
|
||||
subst: Substitution,
|
||||
span: MirSpan,
|
||||
) -> 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)
|
||||
}
|
||||
|
||||
|
@ -872,7 +992,7 @@ impl MirLowerCtx<'_> {
|
|||
) -> Result<BasicBlockId> {
|
||||
let subst = match ty.kind(Interner) {
|
||||
TyKind::Adt(_, subst) => subst.clone(),
|
||||
_ => not_supported!("Non ADT enum"),
|
||||
_ => implementation_error!("Non ADT enum"),
|
||||
};
|
||||
self.push_assignment(
|
||||
prev_block,
|
||||
|
@ -970,287 +1090,6 @@ impl MirLowerCtx<'_> {
|
|||
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 {
|
||||
match &self.discr_temp {
|
||||
Some(x) => x.clone(),
|
||||
|
@ -1266,19 +1105,34 @@ impl MirLowerCtx<'_> {
|
|||
fn lower_loop(
|
||||
&mut self,
|
||||
prev_block: BasicBlockId,
|
||||
place: Place,
|
||||
label: Option<LabelId>,
|
||||
f: impl FnOnce(&mut MirLowerCtx<'_>, BasicBlockId) -> Result<()>,
|
||||
) -> Result<Option<BasicBlockId>> {
|
||||
if label.is_some() {
|
||||
not_supported!("loop with label");
|
||||
}
|
||||
let begin = self.new_basic_block();
|
||||
let prev =
|
||||
mem::replace(&mut self.current_loop_blocks, Some(LoopBlocks { begin, end: None }));
|
||||
let prev = mem::replace(
|
||||
&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);
|
||||
f(self, begin)?;
|
||||
let my = mem::replace(&mut self.current_loop_blocks, prev)
|
||||
.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)
|
||||
}
|
||||
|
||||
|
@ -1366,15 +1220,11 @@ impl MirLowerCtx<'_> {
|
|||
|
||||
fn lower_block_to_place(
|
||||
&mut self,
|
||||
label: Option<LabelId>,
|
||||
statements: &[hir_def::expr::Statement],
|
||||
mut current: BasicBlockId,
|
||||
tail: Option<ExprId>,
|
||||
place: Place,
|
||||
) -> Result<Option<Idx<BasicBlock>>> {
|
||||
if label.is_some() {
|
||||
not_supported!("block with label");
|
||||
}
|
||||
for statement in statements.iter() {
|
||||
match statement {
|
||||
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> {
|
||||
Ok(match (source_ty.kind(Interner), target_ty.kind(Interner)) {
|
||||
(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,
|
||||
(_, _) => 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
|
||||
(TyKind::Scalar(_), TyKind::Adt(..)) | (TyKind::Adt(..), TyKind::Scalar(_)) => {
|
||||
CastKind::IntToInt
|
||||
|
@ -1552,6 +1391,7 @@ pub fn lower_to_mir(
|
|||
body,
|
||||
owner,
|
||||
current_loop_blocks: None,
|
||||
labeled_loop_blocks: Default::default(),
|
||||
discr_temp: None,
|
||||
};
|
||||
let mut current = start_block;
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
//! MIR lowering for places
|
||||
|
||||
use super::*;
|
||||
use hir_def::FunctionId;
|
||||
use hir_expand::name;
|
||||
|
||||
macro_rules! not_supported {
|
||||
|
@ -125,7 +126,7 @@ impl MirLowerCtx<'_> {
|
|||
match &self.body.exprs[expr_id] {
|
||||
Expr::Path(p) => {
|
||||
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));
|
||||
};
|
||||
let pr = match pr {
|
||||
|
@ -145,10 +146,32 @@ impl MirLowerCtx<'_> {
|
|||
self.expr_ty(*expr).kind(Interner),
|
||||
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);
|
||||
};
|
||||
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 {
|
||||
return Ok(None);
|
||||
|
@ -171,7 +194,24 @@ impl MirLowerCtx<'_> {
|
|||
if index_ty != TyBuilder::usize()
|
||||
|| !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)) =
|
||||
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(
|
||||
&mut self,
|
||||
current: BasicBlockId,
|
||||
|
|
399
crates/hir-ty/src/mir/lower/pattern_matching.rs
Normal file
399
crates/hir-ty/src/mir/lower/pattern_matching.rs
Normal 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);
|
||||
}
|
||||
}
|
|
@ -1,6 +1,6 @@
|
|||
//! 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_expand::name::Name;
|
||||
|
@ -23,6 +23,18 @@ impl MirBody {
|
|||
ctx.for_body();
|
||||
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> {
|
||||
|
@ -77,6 +89,7 @@ impl Display for LocalName {
|
|||
|
||||
impl<'a> MirPrettyCtx<'a> {
|
||||
fn for_body(&mut self) {
|
||||
wln!(self, "// {:?}", self.body.owner);
|
||||
self.with_block(|this| {
|
||||
this.locals();
|
||||
wln!(this);
|
||||
|
@ -300,9 +313,9 @@ impl<'a> MirPrettyCtx<'a> {
|
|||
w!(self, ")");
|
||||
}
|
||||
Rvalue::Cast(ck, op, ty) => {
|
||||
w!(self, "Discriminant({ck:?}");
|
||||
w!(self, "Cast({ck:?}, ");
|
||||
self.operand(op);
|
||||
w!(self, "{})", ty.display(self.db));
|
||||
w!(self, ", {})", ty.display(self.db));
|
||||
}
|
||||
Rvalue::CheckedBinaryOp(b, o1, o2) => {
|
||||
self.operand(o1);
|
||||
|
|
|
@ -258,7 +258,6 @@ fn test() {
|
|||
|
||||
#[test]
|
||||
fn coerce_autoderef_block() {
|
||||
// FIXME: We should know mutability in overloaded deref
|
||||
check_no_mismatches(
|
||||
r#"
|
||||
//- minicore: deref
|
||||
|
@ -268,7 +267,7 @@ fn takes_ref_str(x: &str) {}
|
|||
fn returns_string() -> String { loop {} }
|
||||
fn test() {
|
||||
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]
|
||||
fn coerce_fn_items_in_match_arms() {
|
||||
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(
|
||||
r"
|
||||
fn foo1(x: u32) -> isize { 1 }
|
||||
|
|
|
@ -1255,7 +1255,6 @@ fn foo<T: Trait>(a: &T) {
|
|||
|
||||
#[test]
|
||||
fn autoderef_visibility_field() {
|
||||
// FIXME: We should know mutability in overloaded deref
|
||||
check(
|
||||
r#"
|
||||
//- minicore: deref
|
||||
|
@ -1277,7 +1276,7 @@ mod a {
|
|||
mod b {
|
||||
fn foo() {
|
||||
let x = super::a::Bar::new().0;
|
||||
// ^^^^^^^^^^^^^^^^^^^^ adjustments: Deref(Some(OverloadedDeref(None)))
|
||||
// ^^^^^^^^^^^^^^^^^^^^ adjustments: Deref(Some(OverloadedDeref(Some(Not))))
|
||||
// ^^^^^^^^^^^^^^^^^^^^^^ type: char
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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]
|
||||
fn infer_missing_type() {
|
||||
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]
|
||||
fn issue_14275() {
|
||||
// FIXME: evaluate const generic
|
||||
|
|
|
@ -206,19 +206,27 @@ fn test() {
|
|||
fn infer_try_trait() {
|
||||
check_types(
|
||||
r#"
|
||||
//- minicore: try, result
|
||||
//- minicore: try, result, from
|
||||
fn test() {
|
||||
let r: Result<i32, u64> = Result::Ok(1);
|
||||
let v = r?;
|
||||
v;
|
||||
} //^ 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<()>
|
||||
}
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
|
|
@ -11,6 +11,7 @@ use hir_def::{
|
|||
lang_item::{LangItem, LangItemTarget},
|
||||
TraitId,
|
||||
};
|
||||
use hir_expand::name::{name, Name};
|
||||
use stdx::panic_context;
|
||||
|
||||
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())?;
|
||||
match target {
|
||||
LangItemTarget::Trait(t) => Some(t),
|
||||
|
|
|
@ -130,7 +130,7 @@ fn direct_super_traits(db: &dyn DefDatabase, trait_: TraitId, cb: impl FnMut(Tra
|
|||
WherePredicate::Lifetime { .. } => 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),
|
||||
_ => None,
|
||||
})
|
||||
|
|
|
@ -1801,7 +1801,7 @@ impl Function {
|
|||
let body = db
|
||||
.mir_body(self.id.into())
|
||||
.map_err(|e| MirEvalError::MirLowerError(self.id.into(), e))?;
|
||||
interpret_mir(db, &body, false)?;
|
||||
interpret_mir(db, &body, Substitution::empty(Interner), false)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
@ -1947,7 +1947,7 @@ impl Const {
|
|||
}
|
||||
|
||||
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));
|
||||
// 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
|
||||
|
|
|
@ -1076,10 +1076,7 @@ impl<'db> SemanticsImpl<'db> {
|
|||
let hygiene = hir_expand::hygiene::Hygiene::new(self.db.upcast(), analyze.file_id);
|
||||
let ctx = body::LowerCtx::with_hygiene(self.db.upcast(), &hygiene);
|
||||
let hir_path = Path::from_src(path.clone(), &ctx)?;
|
||||
match analyze
|
||||
.resolver
|
||||
.resolve_path_in_type_ns_fully(self.db.upcast(), hir_path.mod_path())?
|
||||
{
|
||||
match analyze.resolver.resolve_path_in_type_ns_fully(self.db.upcast(), &hir_path)? {
|
||||
TypeNs::TraitId(id) => Some(Trait { id }),
|
||||
_ => None,
|
||||
}
|
||||
|
|
|
@ -420,7 +420,10 @@ impl SourceAnalyzer {
|
|||
None
|
||||
} else {
|
||||
// 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) {
|
||||
Some(ValueNs::LocalBinding(binding_id)) => {
|
||||
Some(Local { binding_id, parent: self.resolver.body_owner()? })
|
||||
|
@ -461,7 +464,7 @@ impl SourceAnalyzer {
|
|||
) -> Option<Macro> {
|
||||
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))?;
|
||||
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(
|
||||
|
@ -801,15 +804,11 @@ impl SourceAnalyzer {
|
|||
func: FunctionId,
|
||||
substs: Substitution,
|
||||
) -> FunctionId {
|
||||
let krate = self.resolver.krate();
|
||||
let owner = match self.resolver.body_owner() {
|
||||
Some(it) => it,
|
||||
None => return func,
|
||||
};
|
||||
let env = owner.as_generic_def_id().map_or_else(
|
||||
|| Arc::new(hir_ty::TraitEnvironment::empty(krate)),
|
||||
|d| db.trait_environment(d),
|
||||
);
|
||||
let env = db.trait_environment_for_body(owner);
|
||||
method_resolution::lookup_impl_method(db, env, func, substs).0
|
||||
}
|
||||
|
||||
|
@ -819,15 +818,11 @@ impl SourceAnalyzer {
|
|||
const_id: ConstId,
|
||||
subs: Substitution,
|
||||
) -> ConstId {
|
||||
let krate = self.resolver.krate();
|
||||
let owner = match self.resolver.body_owner() {
|
||||
Some(it) => it,
|
||||
None => return const_id,
|
||||
};
|
||||
let env = owner.as_generic_def_id().map_or_else(
|
||||
|| Arc::new(hir_ty::TraitEnvironment::empty(krate)),
|
||||
|d| db.trait_environment(d),
|
||||
);
|
||||
let env = db.trait_environment_for_body(owner);
|
||||
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,
|
||||
path: &Path,
|
||||
) -> 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_(
|
||||
|
@ -962,8 +957,7 @@ fn resolve_hir_path_(
|
|||
res.map(|ty_ns| (ty_ns, path.segments().first()))
|
||||
}
|
||||
None => {
|
||||
let (ty, remaining_idx) =
|
||||
resolver.resolve_path_in_type_ns(db.upcast(), path.mod_path())?;
|
||||
let (ty, remaining_idx) = resolver.resolve_path_in_type_ns(db.upcast(), path)?;
|
||||
match remaining_idx {
|
||||
Some(remaining_idx) => {
|
||||
if remaining_idx + 1 == path.segments().len() {
|
||||
|
@ -1019,7 +1013,7 @@ fn resolve_hir_path_(
|
|||
|
||||
let body_owner = resolver.body_owner();
|
||||
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 {
|
||||
ValueNs::LocalBinding(binding_id) => {
|
||||
let var = Local { parent: body_owner?, binding_id };
|
||||
|
@ -1039,14 +1033,14 @@ fn resolve_hir_path_(
|
|||
|
||||
let items = || {
|
||||
resolver
|
||||
.resolve_module_path_in_items(db.upcast(), path.mod_path())
|
||||
.resolve_module_path_in_items(db.upcast(), path.mod_path()?)
|
||||
.take_types()
|
||||
.map(|it| PathResolution::Def(it.into()))
|
||||
};
|
||||
|
||||
let macros = || {
|
||||
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())))
|
||||
};
|
||||
|
||||
|
@ -1074,7 +1068,7 @@ fn resolve_hir_path_qualifier(
|
|||
path: &Path,
|
||||
) -> Option<PathResolution> {
|
||||
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 {
|
||||
TypeNs::SelfType(it) => PathResolution::SelfType(it.into()),
|
||||
TypeNs::GenericParam(id) => PathResolution::TypeParam(id.into()),
|
||||
|
@ -1089,7 +1083,7 @@ fn resolve_hir_path_qualifier(
|
|||
})
|
||||
.or_else(|| {
|
||||
resolver
|
||||
.resolve_module_path_in_items(db.upcast(), path.mod_path())
|
||||
.resolve_module_path_in_items(db.upcast(), path.mod_path()?)
|
||||
.take_types()
|
||||
.map(|it| PathResolution::Def(it.into()))
|
||||
})
|
||||
|
|
|
@ -20,7 +20,7 @@ use crate::assist_context::{AssistContext, Assists};
|
|||
// Replaces a `try` expression with a `match` expression.
|
||||
//
|
||||
// ```
|
||||
// # //- minicore:option
|
||||
// # //- minicore: try, option
|
||||
// fn handle() {
|
||||
// let pat = Some(true)$0?;
|
||||
// }
|
||||
|
@ -111,7 +111,7 @@ mod tests {
|
|||
check_assist(
|
||||
replace_try_expr_with_match,
|
||||
r#"
|
||||
//- minicore:option
|
||||
//- minicore: try, option
|
||||
fn test() {
|
||||
let pat = Some(true)$0?;
|
||||
}
|
||||
|
@ -132,7 +132,7 @@ fn test() {
|
|||
check_assist(
|
||||
replace_try_expr_with_match,
|
||||
r#"
|
||||
//- minicore:result
|
||||
//- minicore: try, from, result
|
||||
fn test() {
|
||||
let pat = Ok(true)$0?;
|
||||
}
|
||||
|
|
|
@ -2352,7 +2352,7 @@ fn doctest_replace_try_expr_with_match() {
|
|||
check_doc_test(
|
||||
"replace_try_expr_with_match",
|
||||
r#####"
|
||||
//- minicore:option
|
||||
//- minicore: try, option
|
||||
fn handle() {
|
||||
let pat = Some(true)$0?;
|
||||
}
|
||||
|
|
|
@ -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]
|
||||
fn overloaded_deref() {
|
||||
// FIXME: check for false negative
|
||||
check_diagnostics(
|
||||
r#"
|
||||
//- minicore: deref_mut
|
||||
|
@ -574,22 +621,36 @@ use core::ops::{Deref, DerefMut};
|
|||
|
||||
struct Foo;
|
||||
impl Deref for Foo {
|
||||
type Target = i32;
|
||||
fn deref(&self) -> &i32 {
|
||||
&5
|
||||
type Target = (i32, u8);
|
||||
fn deref(&self) -> &(i32, u8) {
|
||||
&(5, 2)
|
||||
}
|
||||
}
|
||||
impl DerefMut for Foo {
|
||||
fn deref_mut(&mut self) -> &mut i32 {
|
||||
&mut 5
|
||||
fn deref_mut(&mut self) -> &mut (i32, u8) {
|
||||
&mut (5, 2)
|
||||
}
|
||||
}
|
||||
fn f() {
|
||||
let x = Foo;
|
||||
let mut x = Foo;
|
||||
//^^^^^ 💡 weak: variable does not need to be mutable
|
||||
let y = &*x;
|
||||
let x = Foo;
|
||||
let mut x = Foo;
|
||||
let y: &mut i32 = &mut x;
|
||||
let y = &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]
|
||||
fn respect_allow_unused_mut() {
|
||||
// FIXME: respect
|
||||
|
|
|
@ -409,7 +409,6 @@ pub(super) fn definition(
|
|||
}
|
||||
match it.eval(db) {
|
||||
Ok(()) => Some("pass".into()),
|
||||
Err(MirEvalError::Panic) => Some("fail".into()),
|
||||
Err(MirEvalError::MirLowerError(f, e)) => {
|
||||
let name = &db.function_data(f).name;
|
||||
Some(format!("error: fail to lower {name} due {e:?}"))
|
||||
|
|
|
@ -5009,7 +5009,7 @@ fn foo() {
|
|||
fn hover_try_expr_res() {
|
||||
check_hover_range(
|
||||
r#"
|
||||
//- minicore:result
|
||||
//- minicore: try, from, result
|
||||
struct FooError;
|
||||
|
||||
fn foo() -> Result<(), FooError> {
|
||||
|
@ -5023,7 +5023,7 @@ fn foo() -> Result<(), FooError> {
|
|||
);
|
||||
check_hover_range(
|
||||
r#"
|
||||
//- minicore:result
|
||||
//- minicore: try, from, result
|
||||
struct FooError;
|
||||
struct BarError;
|
||||
|
||||
|
@ -5044,6 +5044,7 @@ fn foo() -> Result<(), FooError> {
|
|||
fn hover_try_expr() {
|
||||
check_hover_range(
|
||||
r#"
|
||||
//- minicore: try
|
||||
struct NotResult<T, U>(T, U);
|
||||
struct Short;
|
||||
struct Looooong;
|
||||
|
@ -5061,6 +5062,7 @@ fn foo() -> NotResult<(), Looooong> {
|
|||
);
|
||||
check_hover_range(
|
||||
r#"
|
||||
//- minicore: try
|
||||
struct NotResult<T, U>(T, U);
|
||||
struct Short;
|
||||
struct Looooong;
|
||||
|
@ -5092,7 +5094,7 @@ fn foo() -> Option<()> {
|
|||
"#,
|
||||
expect![[r#"
|
||||
```rust
|
||||
<Option<i32> as Try>::Output
|
||||
i32
|
||||
```"#]],
|
||||
);
|
||||
}
|
||||
|
|
|
@ -435,7 +435,7 @@ fn main() {
|
|||
file_id: FileId(
|
||||
1,
|
||||
),
|
||||
range: 3415..3423,
|
||||
range: 5805..5813,
|
||||
},
|
||||
),
|
||||
tooltip: "",
|
||||
|
@ -448,7 +448,7 @@ fn main() {
|
|||
file_id: FileId(
|
||||
1,
|
||||
),
|
||||
range: 3447..3451,
|
||||
range: 5837..5841,
|
||||
},
|
||||
),
|
||||
tooltip: "",
|
||||
|
@ -468,7 +468,7 @@ fn main() {
|
|||
file_id: FileId(
|
||||
1,
|
||||
),
|
||||
range: 3415..3423,
|
||||
range: 5805..5813,
|
||||
},
|
||||
),
|
||||
tooltip: "",
|
||||
|
@ -481,7 +481,7 @@ fn main() {
|
|||
file_id: FileId(
|
||||
1,
|
||||
),
|
||||
range: 3447..3451,
|
||||
range: 5837..5841,
|
||||
},
|
||||
),
|
||||
tooltip: "",
|
||||
|
@ -501,7 +501,7 @@ fn main() {
|
|||
file_id: FileId(
|
||||
1,
|
||||
),
|
||||
range: 3415..3423,
|
||||
range: 5805..5813,
|
||||
},
|
||||
),
|
||||
tooltip: "",
|
||||
|
@ -514,7 +514,7 @@ fn main() {
|
|||
file_id: FileId(
|
||||
1,
|
||||
),
|
||||
range: 3447..3451,
|
||||
range: 5837..5841,
|
||||
},
|
||||
),
|
||||
tooltip: "",
|
||||
|
|
|
@ -1126,5 +1126,5 @@ fn benchmark_syntax_highlighting_parser() {
|
|||
.filter(|it| it.highlight.tag == HlTag::Symbol(SymbolKind::Function))
|
||||
.count()
|
||||
};
|
||||
assert_eq!(hash, 1608);
|
||||
assert_eq!(hash, 1170);
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
//! errors.
|
||||
|
||||
use std::{
|
||||
collections::HashMap,
|
||||
env,
|
||||
time::{SystemTime, UNIX_EPOCH},
|
||||
};
|
||||
|
@ -153,6 +154,10 @@ impl flags::AnalysisStats {
|
|||
self.run_inference(&host, db, &vfs, &funcs, verbosity);
|
||||
}
|
||||
|
||||
if self.mir_stats {
|
||||
self.lower_mir(db, &funcs);
|
||||
}
|
||||
|
||||
let total_span = analysis_sw.elapsed();
|
||||
eprintln!("{:<20} {total_span}", "Total:");
|
||||
report_metric("total time", total_span.time.as_millis() as u64, "ms");
|
||||
|
@ -189,6 +194,24 @@ impl flags::AnalysisStats {
|
|||
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(
|
||||
&self,
|
||||
host: &AnalysisHost,
|
||||
|
|
|
@ -66,6 +66,8 @@ xflags::xflags! {
|
|||
optional --memory-usage
|
||||
/// Print the total length of all source and macro files (whitespace is not counted).
|
||||
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.
|
||||
optional -o, --only path: String
|
||||
|
@ -172,6 +174,7 @@ pub struct AnalysisStats {
|
|||
pub parallel: bool,
|
||||
pub memory_usage: bool,
|
||||
pub source_stats: bool,
|
||||
pub mir_stats: bool,
|
||||
pub only: Option<String>,
|
||||
pub with_deps: bool,
|
||||
pub no_sysroot: bool,
|
||||
|
|
|
@ -613,7 +613,7 @@ Pat =
|
|||
| ConstBlockPat
|
||||
|
||||
LiteralPat =
|
||||
Literal
|
||||
'-'? Literal
|
||||
|
||||
IdentPat =
|
||||
Attr* 'ref'? 'mut'? Name ('@' Pat)?
|
||||
|
|
|
@ -1375,6 +1375,7 @@ pub struct LiteralPat {
|
|||
pub(crate) syntax: SyntaxNode,
|
||||
}
|
||||
impl LiteralPat {
|
||||
pub fn minus_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![-]) }
|
||||
pub fn literal(&self) -> Option<Literal> { support::child(&self.syntax) }
|
||||
}
|
||||
|
||||
|
|
|
@ -535,6 +535,7 @@ impl Field {
|
|||
"!" => "excl",
|
||||
"*" => "star",
|
||||
"&" => "amp",
|
||||
"-" => "minus",
|
||||
"_" => "underscore",
|
||||
"." => "dot",
|
||||
".." => "dotdot",
|
||||
|
|
|
@ -181,7 +181,7 @@ pub mod convert {
|
|||
}
|
||||
// endregion:as_ref
|
||||
// region:infallible
|
||||
pub enum Infallibe {}
|
||||
pub enum Infallible {}
|
||||
// endregion:infallible
|
||||
}
|
||||
|
||||
|
@ -375,16 +375,82 @@ pub mod ops {
|
|||
type 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};
|
||||
// endregion:fn
|
||||
// region:try
|
||||
mod try_ {
|
||||
use super::super::convert::Infallible;
|
||||
|
||||
pub enum ControlFlow<B, C = ()> {
|
||||
#[lang = "Continue"]
|
||||
Continue(C),
|
||||
#[lang = "Break"]
|
||||
Break(B),
|
||||
}
|
||||
pub trait FromResidual<R = Self::Residual> {
|
||||
pub trait FromResidual<R = <Self as Try>::Residual> {
|
||||
#[lang = "from_residual"]
|
||||
fn from_residual(residual: R) -> Self;
|
||||
}
|
||||
|
@ -400,14 +466,66 @@ pub mod ops {
|
|||
|
||||
impl<B, C> Try for ControlFlow<B, C> {
|
||||
type Output = C;
|
||||
type Residual = ControlFlow<B, convert::Infallible>;
|
||||
type Residual = ControlFlow<B, Infallible>;
|
||||
fn from_output(output: Self::Output) -> Self {}
|
||||
fn branch(self) -> ControlFlow<Self::Residual, Self::Output> {}
|
||||
}
|
||||
|
||||
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};
|
||||
// endregion:try
|
||||
|
@ -541,7 +659,10 @@ pub mod option {
|
|||
loop {}
|
||||
}
|
||||
pub fn unwrap_or(self, default: T) -> T {
|
||||
loop {}
|
||||
match self {
|
||||
Some(val) => val,
|
||||
None => default,
|
||||
}
|
||||
}
|
||||
// region:fn
|
||||
pub fn and_then<U, F>(self, f: F) -> Option<U>
|
||||
|
|
|
@ -295,7 +295,7 @@ impl<T> Arena<T> {
|
|||
/// ```
|
||||
pub fn iter(
|
||||
&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))
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue