Skip to content

Commit b7b9ae5

Browse files
committed
desugar ? operator
1 parent 924d277 commit b7b9ae5

File tree

27 files changed

+517
-191
lines changed

27 files changed

+517
-191
lines changed

crates/hir-def/src/body.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -391,7 +391,7 @@ impl Body {
391391
}
392392
};
393393
let expander = Expander::new(db, file_id, module);
394-
let (mut body, source_map) = Body::new(db, expander, params, body);
394+
let (mut body, source_map) = Body::new(db, expander, params, body, module.krate);
395395
body.shrink_to_fit();
396396

397397
(Arc::new(body), Arc::new(source_map))
@@ -420,8 +420,9 @@ impl Body {
420420
expander: Expander,
421421
params: Option<(ast::ParamList, impl Iterator<Item = bool>)>,
422422
body: Option<ast::Expr>,
423+
krate: CrateId,
423424
) -> (Body, BodySourceMap) {
424-
lower::lower(db, expander, params, body)
425+
lower::lower(db, expander, params, body, krate)
425426
}
426427

427428
fn shrink_to_fit(&mut self) {

crates/hir-def/src/body/lower.rs

Lines changed: 87 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
44
use std::{mem, sync::Arc};
55

6+
use base_db::CrateId;
67
use either::Either;
78
use hir_expand::{
89
ast_id_map::AstIdMap,
@@ -36,6 +37,7 @@ use crate::{
3637
RecordFieldPat, RecordLitField, Statement,
3738
},
3839
item_scope::BuiltinShadowMode,
40+
lang_item::LangItem,
3941
path::{GenericArgs, Path},
4042
type_ref::{Mutability, Rawness, TypeRef},
4143
AdtId, BlockId, BlockLoc, ModuleDefId, UnresolvedMacro,
@@ -80,9 +82,11 @@ pub(super) fn lower(
8082
expander: Expander,
8183
params: Option<(ast::ParamList, impl Iterator<Item = bool>)>,
8284
body: Option<ast::Expr>,
85+
krate: CrateId,
8386
) -> (Body, BodySourceMap) {
8487
ExprCollector {
8588
db,
89+
krate,
8690
source_map: BodySourceMap::default(),
8791
ast_id_map: db.ast_id_map(expander.current_file_id),
8892
body: Body {
@@ -107,6 +111,7 @@ struct ExprCollector<'a> {
107111
expander: Expander,
108112
ast_id_map: Arc<AstIdMap>,
109113
body: Body,
114+
krate: CrateId,
110115
source_map: BodySourceMap,
111116
is_lowering_assignee_expr: bool,
112117
is_lowering_generator: bool,
@@ -176,8 +181,7 @@ impl ExprCollector<'_> {
176181
self.source_map.expr_map.insert(src, id);
177182
id
178183
}
179-
// desugared exprs don't have ptr, that's wrong and should be fixed
180-
// somehow.
184+
// FIXME: desugared exprs don't have ptr, that's wrong and should be fixed somehow.
181185
fn alloc_expr_desugared(&mut self, expr: Expr) -> ExprId {
182186
self.body.exprs.alloc(expr)
183187
}
@@ -199,6 +203,10 @@ impl ExprCollector<'_> {
199203
self.source_map.pat_map.insert(src, id);
200204
id
201205
}
206+
// FIXME: desugared pats don't have ptr, that's wrong and should be fixed somehow.
207+
fn alloc_pat_desugared(&mut self, pat: Pat) -> PatId {
208+
self.body.pats.alloc(pat)
209+
}
202210
fn missing_pat(&mut self) -> PatId {
203211
self.body.pats.alloc(Pat::Missing)
204212
}
@@ -437,10 +445,7 @@ impl ExprCollector<'_> {
437445
let expr = self.collect_expr_opt(e.expr());
438446
self.alloc_expr(Expr::Await { expr }, syntax_ptr)
439447
}
440-
ast::Expr::TryExpr(e) => {
441-
let expr = self.collect_expr_opt(e.expr());
442-
self.alloc_expr(Expr::Try { expr }, syntax_ptr)
443-
}
448+
ast::Expr::TryExpr(e) => self.collect_try_operator(syntax_ptr, e),
444449
ast::Expr::CastExpr(e) => {
445450
let expr = self.collect_expr_opt(e.expr());
446451
let type_ref = Interned::new(TypeRef::from_ast_opt(&self.ctx(), e.ty()));
@@ -601,6 +606,82 @@ impl ExprCollector<'_> {
601606
})
602607
}
603608

609+
fn collect_try_operator(&mut self, syntax_ptr: AstPtr<ast::Expr>, e: ast::TryExpr) -> ExprId {
610+
let (try_branch, cf_continue, cf_break, try_from_residual) = 'if_chain: {
611+
if let Some(try_branch) = LangItem::TryTraitBranch.path(self.db, self.krate) {
612+
if let Some(cf_continue) =
613+
LangItem::ControlFlowContinue.path(self.db, self.krate)
614+
{
615+
if let Some(cf_break) =
616+
LangItem::ControlFlowBreak.path(self.db, self.krate)
617+
{
618+
if let Some(try_from_residual) =
619+
LangItem::TryTraitFromResidual.path(self.db, self.krate)
620+
{
621+
break 'if_chain (
622+
try_branch,
623+
cf_continue,
624+
cf_break,
625+
try_from_residual,
626+
);
627+
}
628+
}
629+
}
630+
}
631+
// Some of the needed lang items are missing, so we can't desugar
632+
return self.alloc_expr(Expr::Missing, syntax_ptr);
633+
};
634+
let operand = self.collect_expr_opt(e.expr());
635+
let try_branch = self.alloc_expr(Expr::Path(try_branch), syntax_ptr.clone());
636+
let expr = self.alloc_expr(
637+
Expr::Call {
638+
callee: try_branch,
639+
args: Box::new([operand]),
640+
is_assignee_expr: false,
641+
},
642+
syntax_ptr.clone(),
643+
);
644+
let continue_binding =
645+
self.alloc_binding(name![v1], BindingAnnotation::Unannotated);
646+
let continue_bpat =
647+
self.alloc_pat_desugared(Pat::Bind { id: continue_binding, subpat: None });
648+
self.add_definition_to_binding(continue_binding, continue_bpat);
649+
let continue_arm = MatchArm {
650+
pat: self.alloc_pat_desugared(Pat::TupleStruct {
651+
path: Some(Box::new(cf_continue)),
652+
args: Box::new([continue_bpat]),
653+
ellipsis: None,
654+
}),
655+
guard: None,
656+
expr: self.alloc_expr(Expr::Path(Path::from(name![v1])), syntax_ptr.clone()),
657+
};
658+
let break_binding = self.alloc_binding(name![v1], BindingAnnotation::Unannotated);
659+
let break_bpat =
660+
self.alloc_pat_desugared(Pat::Bind { id: break_binding, subpat: None });
661+
self.add_definition_to_binding(break_binding, break_bpat);
662+
let break_arm = MatchArm {
663+
pat: self.alloc_pat_desugared(Pat::TupleStruct {
664+
path: Some(Box::new(cf_break)),
665+
args: Box::new([break_bpat]),
666+
ellipsis: None,
667+
}),
668+
guard: None,
669+
expr: {
670+
let x =
671+
self.alloc_expr(Expr::Path(Path::from(name![v1])), syntax_ptr.clone());
672+
let callee =
673+
self.alloc_expr(Expr::Path(try_from_residual), syntax_ptr.clone());
674+
let result = self.alloc_expr(
675+
Expr::Call { callee, args: Box::new([x]), is_assignee_expr: false },
676+
syntax_ptr.clone(),
677+
);
678+
self.alloc_expr(Expr::Return { expr: Some(result) }, syntax_ptr.clone())
679+
},
680+
};
681+
let arms = Box::new([continue_arm, break_arm]);
682+
self.alloc_expr(Expr::Match { expr, arms }, syntax_ptr)
683+
}
684+
604685
fn collect_macro_call<F, T, U>(
605686
&mut self,
606687
mcall: ast::MacroCall,

crates/hir-def/src/body/pretty.rs

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -288,10 +288,6 @@ impl<'a> Printer<'a> {
288288
self.print_expr(*expr);
289289
w!(self, ".await");
290290
}
291-
Expr::Try { expr } => {
292-
self.print_expr(*expr);
293-
w!(self, "?");
294-
}
295291
Expr::Cast { expr, type_ref } => {
296292
self.print_expr(*expr);
297293
w!(self, " as ");

crates/hir-def/src/expr.rs

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -192,9 +192,6 @@ pub enum Expr {
192192
Await {
193193
expr: ExprId,
194194
},
195-
Try {
196-
expr: ExprId,
197-
},
198195
Cast {
199196
expr: ExprId,
200197
type_ref: Interned<TypeRef>,
@@ -383,7 +380,6 @@ impl Expr {
383380
}
384381
Expr::Field { expr, .. }
385382
| Expr::Await { expr }
386-
| Expr::Try { expr }
387383
| Expr::Cast { expr, .. }
388384
| Expr::Ref { expr, .. }
389385
| Expr::UnaryOp { expr, .. }

crates/hir-def/src/lang_item.rs

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,8 @@ use rustc_hash::FxHashMap;
88
use syntax::SmolStr;
99

1010
use crate::{
11-
db::DefDatabase, AdtId, AssocItemId, AttrDefId, CrateId, EnumId, EnumVariantId, FunctionId,
12-
ImplId, ModuleDefId, StaticId, StructId, TraitId, TypeAliasId, UnionId,
11+
db::DefDatabase, path::Path, AdtId, AssocItemId, AttrDefId, CrateId, EnumId, EnumVariantId,
12+
FunctionId, ImplId, ModuleDefId, StaticId, StructId, TraitId, TypeAliasId, UnionId,
1313
};
1414

1515
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
@@ -220,11 +220,6 @@ macro_rules! language_item_table {
220220
}
221221
}
222222

223-
/// Opposite of [`LangItem::name`]
224-
pub fn from_name(name: &hir_expand::name::Name) -> Option<Self> {
225-
Self::from_str(name.as_str()?)
226-
}
227-
228223
/// Opposite of [`LangItem::name`]
229224
pub fn from_str(name: &str) -> Option<Self> {
230225
match name {
@@ -236,6 +231,18 @@ macro_rules! language_item_table {
236231
}
237232
}
238233

234+
impl LangItem {
235+
/// Opposite of [`LangItem::name`]
236+
pub fn from_name(name: &hir_expand::name::Name) -> Option<Self> {
237+
Self::from_str(name.as_str()?)
238+
}
239+
240+
pub fn path(&self, db: &dyn DefDatabase, start_crate: CrateId) -> Option<Path> {
241+
let t = db.lang_item(start_crate, *self)?;
242+
Some(Path::LangItem(t))
243+
}
244+
}
245+
239246
language_item_table! {
240247
// Variant name, Name, Getter method name, Target Generic requirements;
241248
Sized, sized, sized_trait, Target::Trait, GenericRequirement::Exact(0);

crates/hir-def/src/path.rs

Lines changed: 58 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ use std::{
88

99
use crate::{
1010
body::LowerCtx,
11+
lang_item::LangItemTarget,
1112
type_ref::{ConstRefOrPath, LifetimeRef},
1213
};
1314
use hir_expand::name::Name;
@@ -36,13 +37,19 @@ impl Display for ImportAlias {
3637
}
3738

3839
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
39-
pub struct Path {
40-
/// Type based path like `<T>::foo`.
41-
/// Note that paths like `<Type as Trait>::foo` are desugared to `Trait::<Self=Type>::foo`.
42-
type_anchor: Option<Interned<TypeRef>>,
43-
mod_path: Interned<ModPath>,
44-
/// Invariant: the same len as `self.mod_path.segments` or `None` if all segments are `None`.
45-
generic_args: Option<Box<[Option<Interned<GenericArgs>>]>>,
40+
pub enum Path {
41+
/// A normal path
42+
Normal {
43+
/// Type based path like `<T>::foo`.
44+
/// Note that paths like `<Type as Trait>::foo` are desugared to `Trait::<Self=Type>::foo`.
45+
type_anchor: Option<Interned<TypeRef>>,
46+
mod_path: Interned<ModPath>,
47+
/// Invariant: the same len as `self.mod_path.segments` or `None` if all segments are `None`.
48+
generic_args: Option<Box<[Option<Interned<GenericArgs>>]>>,
49+
},
50+
/// A link to a lang item. It is used in desugaring of things like `x?`. We can show these
51+
/// links via a normal path since they might be private and not accessible in the usage place.
52+
LangItem(LangItemTarget),
4653
}
4754

4855
/// Generic arguments to a path segment (e.g. the `i32` in `Option<i32>`). This
@@ -102,51 +109,77 @@ impl Path {
102109
) -> Path {
103110
let generic_args = generic_args.into();
104111
assert_eq!(path.len(), generic_args.len());
105-
Path { type_anchor: None, mod_path: Interned::new(path), generic_args: Some(generic_args) }
112+
Path::Normal {
113+
type_anchor: None,
114+
mod_path: Interned::new(path),
115+
generic_args: Some(generic_args),
116+
}
117+
}
118+
119+
/// Converts a known mod path to `Path`.
120+
pub fn from_known_path_with_no_generic(path: ModPath) -> Path {
121+
Path::Normal { type_anchor: None, mod_path: Interned::new(path), generic_args: None }
106122
}
107123

108124
pub fn kind(&self) -> &PathKind {
109-
&self.mod_path.kind
125+
match self {
126+
Path::Normal { mod_path, .. } => &mod_path.kind,
127+
Path::LangItem(_) => &PathKind::Abs,
128+
}
110129
}
111130

112131
pub fn type_anchor(&self) -> Option<&TypeRef> {
113-
self.type_anchor.as_deref()
132+
match self {
133+
Path::Normal { type_anchor, .. } => type_anchor.as_deref(),
134+
Path::LangItem(_) => None,
135+
}
114136
}
115137

116138
pub fn segments(&self) -> PathSegments<'_> {
117-
let s = PathSegments {
118-
segments: self.mod_path.segments(),
119-
generic_args: self.generic_args.as_deref(),
139+
let Path::Normal { mod_path, generic_args, .. } = self else {
140+
return PathSegments {
141+
segments: &[],
142+
generic_args: None,
143+
};
120144
};
145+
let s =
146+
PathSegments { segments: mod_path.segments(), generic_args: generic_args.as_deref() };
121147
if let Some(generic_args) = s.generic_args {
122148
assert_eq!(s.segments.len(), generic_args.len());
123149
}
124150
s
125151
}
126152

127-
pub fn mod_path(&self) -> &ModPath {
128-
&self.mod_path
153+
pub fn mod_path(&self) -> Option<&ModPath> {
154+
match self {
155+
Path::Normal { mod_path, .. } => Some(&mod_path),
156+
Path::LangItem(_) => None,
157+
}
129158
}
130159

131160
pub fn qualifier(&self) -> Option<Path> {
132-
if self.mod_path.is_ident() {
161+
let Path::Normal { mod_path, generic_args, type_anchor } = self else {
162+
return None;
163+
};
164+
if mod_path.is_ident() {
133165
return None;
134166
}
135-
let res = Path {
136-
type_anchor: self.type_anchor.clone(),
167+
let res = Path::Normal {
168+
type_anchor: type_anchor.clone(),
137169
mod_path: Interned::new(ModPath::from_segments(
138-
self.mod_path.kind,
139-
self.mod_path.segments()[..self.mod_path.segments().len() - 1].iter().cloned(),
170+
mod_path.kind,
171+
mod_path.segments()[..mod_path.segments().len() - 1].iter().cloned(),
140172
)),
141-
generic_args: self.generic_args.as_ref().map(|it| it[..it.len() - 1].to_vec().into()),
173+
generic_args: generic_args.as_ref().map(|it| it[..it.len() - 1].to_vec().into()),
142174
};
143175
Some(res)
144176
}
145177

146178
pub fn is_self_type(&self) -> bool {
147-
self.type_anchor.is_none()
148-
&& self.generic_args.as_deref().is_none()
149-
&& self.mod_path.is_Self()
179+
let Path::Normal { mod_path, generic_args, type_anchor } = self else {
180+
return false;
181+
};
182+
type_anchor.is_none() && generic_args.as_deref().is_none() && mod_path.is_Self()
150183
}
151184
}
152185

@@ -222,7 +255,7 @@ impl GenericArgs {
222255

223256
impl From<Name> for Path {
224257
fn from(name: Name) -> Path {
225-
Path {
258+
Path::Normal {
226259
type_anchor: None,
227260
mod_path: Interned::new(ModPath::from_segments(PathKind::Plain, iter::once(name))),
228261
generic_args: None,

0 commit comments

Comments
 (0)