desugar ? operator

This commit is contained in:
hkalbasi 2023-03-08 20:58:52 +03:30
parent 924d277f32
commit b7b9ae59a0
27 changed files with 517 additions and 191 deletions

View File

@ -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) {

View File

@ -3,6 +3,7 @@
use std::{mem, sync::Arc};
use base_db::CrateId;
use either::Either;
use hir_expand::{
ast_id_map::AstIdMap,
@ -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 {
@ -107,6 +111,7 @@ struct ExprCollector<'a> {
expander: Expander,
ast_id_map: Arc<AstIdMap>,
body: Body,
krate: CrateId,
source_map: BodySourceMap,
is_lowering_assignee_expr: bool,
is_lowering_generator: bool,
@ -176,8 +181,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 +203,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)
}
@ -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,82 @@ impl ExprCollector<'_> {
})
}
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_binding =
self.alloc_binding(name![v1], 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(name![v1])), syntax_ptr.clone()),
};
let break_binding = self.alloc_binding(name![v1], 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(name![v1])), 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(),
);
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,

View File

@ -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 ");

View File

@ -192,9 +192,6 @@ pub enum Expr {
Await {
expr: ExprId,
},
Try {
expr: ExprId,
},
Cast {
expr: ExprId,
type_ref: Interned<TypeRef>,
@ -383,7 +380,6 @@ impl Expr {
}
Expr::Field { expr, .. }
| Expr::Await { expr }
| Expr::Try { expr }
| Expr::Cast { expr, .. }
| Expr::Ref { expr, .. }
| Expr::UnaryOp { expr, .. }

View File

@ -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);

View File

@ -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,

View File

@ -75,8 +75,11 @@ pub(super) fn lower_path(mut path: ast::Path, ctx: &LowerCtx<'_>) -> Option<Path
}
// <T as Trait<A>>::Foo desugars to Trait<Self=T, A>::Foo
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()) },

View File

@ -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_);
}

View File

@ -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,
@ -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
@ -228,7 +228,7 @@ 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;
}
}

View File

@ -801,6 +801,73 @@ 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 or_pattern() {
check_number(

View File

@ -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>;

View File

@ -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 });

View File

@ -25,16 +25,16 @@ 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,
@ -110,10 +110,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);
@ -506,10 +503,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 +845,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 +866,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 +898,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 +915,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 +951,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 +967,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);

View File

@ -601,21 +601,21 @@ 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::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

View File

@ -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),

View File

@ -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))
}
}
}

View File

@ -1,6 +1,6 @@
//! This module provides a MIR interpreter, which is used in const eval.
use std::{borrow::Cow, collections::HashMap, iter};
use std::{borrow::Cow, collections::HashMap, iter, sync::Arc};
use base_db::CrateId;
use chalk_ir::{
@ -24,7 +24,8 @@ use crate::{
layout::layout_of_ty,
mapping::from_chalk,
method_resolution::lookup_impl_method,
CallableDefId, Const, ConstScalar, Interner, MemoryMap, Substitution, Ty, TyBuilder, TyExt,
CallableDefId, Const, ConstScalar, Interner, MemoryMap, Substitution, TraitEnvironment, Ty,
TyBuilder, TyExt,
};
use super::{
@ -34,6 +35,7 @@ use super::{
pub struct Evaluator<'a> {
db: &'a dyn HirDatabase,
trait_env: Arc<TraitEnvironment>,
stack: Vec<u8>,
heap: Vec<u8>,
crate_id: CrateId,
@ -217,8 +219,7 @@ pub fn interpret_mir(
assert_placeholder_ty_is_unused: bool,
) -> Result<Const> {
let ty = body.locals[return_slot()].ty.clone();
let mut evaluator =
Evaluator::new(db, body.owner.module(db.upcast()).krate(), assert_placeholder_ty_is_unused);
let mut evaluator = Evaluator::new(db, body, assert_placeholder_ty_is_unused);
let bytes = evaluator.interpret_mir_with_no_arg(&body)?;
let memory_map = evaluator.create_memory_map(
&bytes,
@ -231,13 +232,16 @@ pub fn interpret_mir(
impl Evaluator<'_> {
pub fn new<'a>(
db: &'a dyn HirDatabase,
crate_id: CrateId,
body: &MirBody,
assert_placeholder_ty_is_unused: bool,
) -> Evaluator<'a> {
let crate_id = body.owner.module(db.upcast()).krate();
let trait_env = db.trait_environment_for_body(body.owner);
Evaluator {
stack: vec![0],
heap: vec![0],
db,
trait_env,
crate_id,
assert_placeholder_ty_is_unused,
stack_depth_limit: 100,
@ -500,15 +504,9 @@ impl Evaluator<'_> {
} else if let Some(x) = self.detect_lang_function(def) {
self.exec_lang_item(x, arg_bytes)?
} else {
let trait_env = {
let Some(d) = body.owner.as_generic_def_id() else {
not_supported!("trait resolving in non generic def id");
};
self.db.trait_environment(d)
};
let (imp, generic_args) = lookup_impl_method(
self.db,
trait_env,
self.trait_env.clone(),
def,
generic_args.clone(),
);
@ -584,7 +582,7 @@ impl Evaluator<'_> {
.to_owned());
}
Terminator::Unreachable => {
return Err(MirEvalError::UndefinedBehavior("unreachable executed"))
return Err(MirEvalError::UndefinedBehavior("unreachable executed"));
}
_ => not_supported!("unknown terminator"),
}
@ -710,8 +708,24 @@ impl Evaluator<'_> {
let ty = self.place_ty(p, locals)?;
let bytes = self.eval_place(p, locals)?.get(&self)?;
let layout = self.layout(&ty)?;
let enum_id = 'b: {
match ty.kind(Interner) {
TyKind::Adt(e, _) => match e.0 {
AdtId::EnumId(e) => break 'b e,
_ => (),
},
_ => (),
}
return Ok(Owned(0u128.to_le_bytes().to_vec()));
};
match layout.variants {
Variants::Single { .. } => Owned(0u128.to_le_bytes().to_vec()),
Variants::Single { index } => {
let r = self.db.const_eval_discriminant(EnumVariantId {
parent: enum_id,
local_id: index.0,
})?;
Owned(r.to_le_bytes().to_vec())
}
Variants::Multiple { tag, tag_encoding, .. } => {
let Some(target_data_layout) = self.db.target_data_layout(self.crate_id) else {
not_supported!("missing target data layout");
@ -727,13 +741,6 @@ impl Evaluator<'_> {
let tag = &bytes[offset..offset + size];
let candidate_discriminant = i128::from_le_bytes(pad16(tag, false))
.wrapping_sub(niche_start as i128);
let enum_id = match ty.kind(Interner) {
TyKind::Adt(e, _) => match e.0 {
AdtId::EnumId(e) => e,
_ => not_supported!("Non enum with multi variant layout"),
},
_ => not_supported!("Non adt with multi variant layout"),
};
let enum_data = self.db.enum_data(enum_id);
let result = 'b: {
for (local_id, _) in enum_data.variants.iter() {
@ -790,8 +797,8 @@ impl Evaluator<'_> {
Owned(result)
}
AggregateKind::Adt(x, subst) => {
let (size, variant_layout, tag) =
self.layout_of_variant(*x, subst.clone(), locals)?;
let subst = self.subst_filler(subst, locals);
let (size, variant_layout, tag) = self.layout_of_variant(*x, subst, locals)?;
Owned(self.make_by_layout(size, &variant_layout, tag, values, locals)?)
}
},
@ -1124,12 +1131,13 @@ impl Evaluator<'_> {
}
fn detect_lang_function(&self, def: FunctionId) -> Option<LangItem> {
use LangItem::*;
let candidate = lang_attr(self.db.upcast(), def)?;
// filter normal lang functions out
if [LangItem::IntoIterIntoIter, LangItem::IteratorNext].contains(&candidate) {
return None;
// We want to execute these functions with special logic
if [PanicFmt, BeginPanic, SliceLen].contains(&candidate) {
return Some(candidate);
}
Some(candidate)
None
}
fn create_memory_map(&self, bytes: &[u8], ty: &Ty, locals: &Locals<'_>) -> Result<MemoryMap> {

View File

@ -13,7 +13,7 @@ use hir_def::{
layout::LayoutError,
path::Path,
resolver::{resolver_for_expr, ResolveValueResult, ValueNs},
DefWithBodyId, EnumVariantId, HasModule,
DefWithBodyId, EnumVariantId, HasModule, ItemContainerId, TraitId,
};
use hir_expand::name::Name;
use la_arena::ArenaMap;
@ -50,6 +50,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,
@ -200,12 +202,21 @@ 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,
@ -608,7 +619,6 @@ 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"),

View File

@ -125,7 +125,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 {

View File

@ -77,6 +77,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);

View File

@ -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,
})

View File

@ -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,
}

View File

@ -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()))
})

View File

@ -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?;
}

View File

@ -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?;
}

View File

@ -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
```"#]],
);
}

View File

@ -181,7 +181,7 @@ pub mod convert {
}
// endregion:as_ref
// region:infallible
pub enum Infallibe {}
pub enum Infallible {}
// endregion:infallible
}
@ -380,11 +380,15 @@ pub mod ops {
// 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 +404,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