From 58003afdc17c3edbf7be80ef56689572a07f9409 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Tue, 5 Sep 2023 11:23:08 +0200 Subject: [PATCH] Implement offset_of in hir-def and hir-ty --- crates/hir-def/src/body/lower.rs | 8 +- crates/hir-def/src/body/pretty.rs | 10 +++ crates/hir-def/src/hir.rs | 9 +- crates/hir-ty/src/infer/closure.rs | 1 + crates/hir-ty/src/infer/expr.rs | 1 + crates/hir-ty/src/infer/mutability.rs | 1 + crates/hir-ty/src/mir/lower.rs | 1 + crates/hir-ty/src/tests/simple.rs | 12 +++ crates/parser/src/grammar/expressions/atom.rs | 19 +++- .../parser/inline/ok/0132_box_expr.rast | 90 ------------------- .../parser/inline/ok/0132_box_expr.rs | 5 -- .../parser/inline/ok/0207_builtin_expr.rs | 5 ++ crates/syntax/rust.ungram | 2 +- crates/syntax/src/ast/generated/nodes.rs | 3 + crates/syntax/src/tests/sourcegen_ast.rs | 20 +++-- 15 files changed, 78 insertions(+), 109 deletions(-) delete mode 100644 crates/parser/test_data/parser/inline/ok/0132_box_expr.rast delete mode 100644 crates/parser/test_data/parser/inline/ok/0132_box_expr.rs create mode 100644 crates/parser/test_data/parser/inline/ok/0207_builtin_expr.rs diff --git a/crates/hir-def/src/body/lower.rs b/crates/hir-def/src/body/lower.rs index 43f58a1bdb00..b536da2e57c1 100644 --- a/crates/hir-def/src/body/lower.rs +++ b/crates/hir-def/src/body/lower.rs @@ -31,7 +31,7 @@ use crate::{ hir::{ dummy_expr_id, Array, Binding, BindingAnnotation, BindingId, BindingProblems, CaptureBy, ClosureKind, Expr, ExprId, Label, LabelId, Literal, LiteralOrConst, MatchArm, Movability, - Pat, PatId, RecordFieldPat, RecordLitField, Statement, + OffsetOf, Pat, PatId, RecordFieldPat, RecordLitField, Statement, }, item_scope::BuiltinShadowMode, lang_item::LangItem, @@ -649,7 +649,11 @@ impl ExprCollector<'_> { } ast::Expr::UnderscoreExpr(_) => self.alloc_expr(Expr::Underscore, syntax_ptr), ast::Expr::AsmExpr(_) => self.missing_expr(), - ast::Expr::OffsetOfExpr(_) => self.missing_expr(), + ast::Expr::OffsetOfExpr(e) => { + let container = Interned::new(TypeRef::from_ast_opt(&self.ctx(), e.ty())); + let fields = e.fields().map(|it| it.as_name()).collect(); + self.alloc_expr(Expr::OffsetOf(OffsetOf { container, fields }), syntax_ptr) + } ast::Expr::FormatArgsExpr(_) => self.missing_expr(), }) } diff --git a/crates/hir-def/src/body/pretty.rs b/crates/hir-def/src/body/pretty.rs index 5d71abe37cc5..6058fcbb6527 100644 --- a/crates/hir-def/src/body/pretty.rs +++ b/crates/hir-def/src/body/pretty.rs @@ -3,6 +3,7 @@ use std::fmt::{self, Write}; use hir_expand::db::ExpandDatabase; +use itertools::Itertools; use syntax::ast::HasName; use crate::{ @@ -154,6 +155,15 @@ impl Printer<'_> { match expr { Expr::Missing => w!(self, "�"), Expr::Underscore => w!(self, "_"), + Expr::OffsetOf(offset_of) => { + w!(self, "builtin#offset_of!("); + self.print_type_ref(&offset_of.container); + w!( + self, + ", {})", + offset_of.fields.iter().format_with(".", |field, f| f(&field.display(self.db))) + ); + } Expr::Path(path) => self.print_path(path), Expr::If { condition, then_branch, else_branch } => { w!(self, "if "); diff --git a/crates/hir-def/src/hir.rs b/crates/hir-def/src/hir.rs index 6591c92ac62d..f03df800a058 100644 --- a/crates/hir-def/src/hir.rs +++ b/crates/hir-def/src/hir.rs @@ -281,6 +281,13 @@ pub enum Expr { Array(Array), Literal(Literal), Underscore, + OffsetOf(OffsetOf), +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct OffsetOf { + pub container: Interned, + pub fields: Box<[Name]>, } #[derive(Debug, Clone, Copy, PartialEq, Eq)] @@ -341,7 +348,7 @@ impl Expr { pub fn walk_child_exprs(&self, mut f: impl FnMut(ExprId)) { match self { Expr::Missing => {} - Expr::Path(_) => {} + Expr::Path(_) | Expr::OffsetOf(_) => {} Expr::If { condition, then_branch, else_branch } => { f(*condition); f(*then_branch); diff --git a/crates/hir-ty/src/infer/closure.rs b/crates/hir-ty/src/infer/closure.rs index cb442de843a8..ceb96a71653d 100644 --- a/crates/hir-ty/src/infer/closure.rs +++ b/crates/hir-ty/src/infer/closure.rs @@ -452,6 +452,7 @@ impl InferenceContext<'_> { fn walk_expr_without_adjust(&mut self, tgt_expr: ExprId) { match &self.body[tgt_expr] { + Expr::OffsetOf(_) => (), Expr::If { condition, then_branch, else_branch } => { self.consume_expr(*condition); self.consume_expr(*then_branch); diff --git a/crates/hir-ty/src/infer/expr.rs b/crates/hir-ty/src/infer/expr.rs index 8b3521410848..d139d929fc3c 100644 --- a/crates/hir-ty/src/infer/expr.rs +++ b/crates/hir-ty/src/infer/expr.rs @@ -843,6 +843,7 @@ impl InferenceContext<'_> { }); expected } + Expr::OffsetOf(_) => TyKind::Scalar(Scalar::Uint(UintTy::Usize)).intern(Interner), }; // use a new type variable if we got unknown here let ty = self.insert_type_vars_shallow(ty); diff --git a/crates/hir-ty/src/infer/mutability.rs b/crates/hir-ty/src/infer/mutability.rs index 396ca0044ff0..853c171a06ad 100644 --- a/crates/hir-ty/src/infer/mutability.rs +++ b/crates/hir-ty/src/infer/mutability.rs @@ -35,6 +35,7 @@ impl InferenceContext<'_> { fn infer_mut_expr_without_adjust(&mut self, tgt_expr: ExprId, mutability: Mutability) { match &self.body[tgt_expr] { Expr::Missing => (), + Expr::OffsetOf(_) => (), &Expr::If { condition, then_branch, else_branch } => { self.infer_mut_expr(condition, Mutability::Not); self.infer_mut_expr(then_branch, Mutability::Not); diff --git a/crates/hir-ty/src/mir/lower.rs b/crates/hir-ty/src/mir/lower.rs index 7c15aa42fd22..6d3574ac3178 100644 --- a/crates/hir-ty/src/mir/lower.rs +++ b/crates/hir-ty/src/mir/lower.rs @@ -370,6 +370,7 @@ impl<'ctx> MirLowerCtx<'ctx> { mut current: BasicBlockId, ) -> Result> { match &self.body.exprs[expr_id] { + Expr::OffsetOf(_) => not_supported!("builtin#offset_of"), Expr::Missing => { if let DefWithBodyId::FunctionId(f) = self.owner { let assoc = f.lookup(self.db.upcast()); diff --git a/crates/hir-ty/src/tests/simple.rs b/crates/hir-ty/src/tests/simple.rs index 2ad7946c8ac1..c2f11bb29d6b 100644 --- a/crates/hir-ty/src/tests/simple.rs +++ b/crates/hir-ty/src/tests/simple.rs @@ -3649,3 +3649,15 @@ fn main() { "#, ); } + +#[test] +fn offset_of() { + check_types( + r#" +fn main() { + builtin#offset_of((,), 0); + // ^^^^^^^^^^^^^^^^^^^^^^^^^ usize +} +"#, + ); +} diff --git a/crates/parser/src/grammar/expressions/atom.rs b/crates/parser/src/grammar/expressions/atom.rs index 7a4f4e4777d5..1dd68f556c4c 100644 --- a/crates/parser/src/grammar/expressions/atom.rs +++ b/crates/parser/src/grammar/expressions/atom.rs @@ -220,24 +220,39 @@ fn tuple_expr(p: &mut Parser<'_>) -> CompletedMarker { // fn foo() { // builtin#asm(0); // builtin#format_args(0); -// builtin#builtin(0); +// builtin#offset_of(Foo, bar.baz.0); // } fn builtin_expr(p: &mut Parser<'_>) -> Option { let m = p.start(); p.bump_remap(T![builtin]); p.bump(T![#]); if p.at_contextual_kw(T![offset_of]) { + p.bump_remap(T![offset_of]); p.expect(T!['(']); type_(p); - p.bump(T![,]); + p.expect(T![,]); + while !p.at(EOF) && !p.at(T![')']) { + if p.at(IDENT) || p.at(INT_NUMBER) { + name_ref_or_index(p); + // } else if p.at(FLOAT_NUMBER) { + // FIXME: needs float hack + } else { + p.err_and_bump("expected field name or number"); + } + if !p.at(T![')']) { + p.expect(T![.]); + } + } p.expect(T![')']); Some(m.complete(p, OFFSET_OF_EXPR)) } else if p.at_contextual_kw(T![format_args]) { + p.bump_remap(T![format_args]); p.expect(T!['(']); expr(p); p.expect(T![')']); Some(m.complete(p, FORMAT_ARGS_EXPR)) } else if p.at_contextual_kw(T![asm]) { + p.bump_remap(T![asm]); p.expect(T!['(']); expr(p); p.expect(T![')']); diff --git a/crates/parser/test_data/parser/inline/ok/0132_box_expr.rast b/crates/parser/test_data/parser/inline/ok/0132_box_expr.rast deleted file mode 100644 index b21f37cd85af..000000000000 --- a/crates/parser/test_data/parser/inline/ok/0132_box_expr.rast +++ /dev/null @@ -1,90 +0,0 @@ -SOURCE_FILE - FN - FN_KW "fn" - WHITESPACE " " - NAME - IDENT "foo" - PARAM_LIST - L_PAREN "(" - R_PAREN ")" - WHITESPACE " " - BLOCK_EXPR - STMT_LIST - L_CURLY "{" - WHITESPACE "\n " - LET_STMT - LET_KW "let" - WHITESPACE " " - IDENT_PAT - NAME - IDENT "x" - WHITESPACE " " - EQ "=" - WHITESPACE " " - BOX_EXPR - BOX_KW "box" - WHITESPACE " " - LITERAL - INT_NUMBER "1i32" - SEMICOLON ";" - WHITESPACE "\n " - LET_STMT - LET_KW "let" - WHITESPACE " " - IDENT_PAT - NAME - IDENT "y" - WHITESPACE " " - EQ "=" - WHITESPACE " " - TUPLE_EXPR - L_PAREN "(" - BOX_EXPR - BOX_KW "box" - WHITESPACE " " - LITERAL - INT_NUMBER "1i32" - COMMA "," - WHITESPACE " " - BOX_EXPR - BOX_KW "box" - WHITESPACE " " - LITERAL - INT_NUMBER "2i32" - R_PAREN ")" - SEMICOLON ";" - WHITESPACE "\n " - LET_STMT - LET_KW "let" - WHITESPACE " " - IDENT_PAT - NAME - IDENT "z" - WHITESPACE " " - EQ "=" - WHITESPACE " " - CALL_EXPR - PATH_EXPR - PATH - PATH_SEGMENT - NAME_REF - IDENT "Foo" - ARG_LIST - L_PAREN "(" - BOX_EXPR - BOX_KW "box" - WHITESPACE " " - LITERAL - INT_NUMBER "1i32" - COMMA "," - WHITESPACE " " - BOX_EXPR - BOX_KW "box" - WHITESPACE " " - LITERAL - INT_NUMBER "2i32" - R_PAREN ")" - SEMICOLON ";" - WHITESPACE "\n" - R_CURLY "}" - WHITESPACE "\n" diff --git a/crates/parser/test_data/parser/inline/ok/0132_box_expr.rs b/crates/parser/test_data/parser/inline/ok/0132_box_expr.rs deleted file mode 100644 index fc9923b7137e..000000000000 --- a/crates/parser/test_data/parser/inline/ok/0132_box_expr.rs +++ /dev/null @@ -1,5 +0,0 @@ -fn foo() { - let x = box 1i32; - let y = (box 1i32, box 2i32); - let z = Foo(box 1i32, box 2i32); -} diff --git a/crates/parser/test_data/parser/inline/ok/0207_builtin_expr.rs b/crates/parser/test_data/parser/inline/ok/0207_builtin_expr.rs new file mode 100644 index 000000000000..dbad0a91df94 --- /dev/null +++ b/crates/parser/test_data/parser/inline/ok/0207_builtin_expr.rs @@ -0,0 +1,5 @@ +fn foo() { + builtin#asm(0); + builtin#format_args(0); + builtin#offset_of(Foo, bar.baz.0); +} diff --git a/crates/syntax/rust.ungram b/crates/syntax/rust.ungram index bd9d2ad17846..d7b1c91216b1 100644 --- a/crates/syntax/rust.ungram +++ b/crates/syntax/rust.ungram @@ -376,7 +376,7 @@ Expr = | UnderscoreExpr OffsetOfExpr = - Attr* 'builtin' '#' 'offset_of' '(' ')' + Attr* 'builtin' '#' 'offset_of' '(' Type ',' fields:(NameRef ('.' NameRef)* ) ')' AsmExpr = Attr* 'builtin' '#' 'asm' '(' ')' diff --git a/crates/syntax/src/ast/generated/nodes.rs b/crates/syntax/src/ast/generated/nodes.rs index 82b9c28a566d..d817589223ef 100644 --- a/crates/syntax/src/ast/generated/nodes.rs +++ b/crates/syntax/src/ast/generated/nodes.rs @@ -1014,6 +1014,9 @@ impl OffsetOfExpr { support::token(&self.syntax, T![offset_of]) } pub fn l_paren_token(&self) -> Option { support::token(&self.syntax, T!['(']) } + pub fn ty(&self) -> Option { support::child(&self.syntax) } + pub fn comma_token(&self) -> Option { support::token(&self.syntax, T![,]) } + pub fn fields(&self) -> AstChildren { support::children(&self.syntax) } pub fn r_paren_token(&self) -> Option { support::token(&self.syntax, T![')']) } } diff --git a/crates/syntax/src/tests/sourcegen_ast.rs b/crates/syntax/src/tests/sourcegen_ast.rs index c49c5fa108b6..56227fce9b5c 100644 --- a/crates/syntax/src/tests/sourcegen_ast.rs +++ b/crates/syntax/src/tests/sourcegen_ast.rs @@ -623,7 +623,7 @@ fn lower_enum(grammar: &Grammar, rule: &Rule) -> Option> { } fn lower_rule(acc: &mut Vec, grammar: &Grammar, label: Option<&String>, rule: &Rule) { - if lower_comma_list(acc, grammar, label, rule) { + if lower_seperated_list(acc, grammar, label, rule) { return; } @@ -689,7 +689,7 @@ fn lower_rule(acc: &mut Vec, grammar: &Grammar, label: Option<&String>, r } // (T (',' T)* ','?) -fn lower_comma_list( +fn lower_seperated_list( acc: &mut Vec, grammar: &Grammar, label: Option<&String>, @@ -699,19 +699,23 @@ fn lower_comma_list( Rule::Seq(it) => it, _ => return false, }; - let (node, repeat, trailing_comma) = match rule.as_slice() { - [Rule::Node(node), Rule::Rep(repeat), Rule::Opt(trailing_comma)] => { - (node, repeat, trailing_comma) + let (node, repeat, trailing_sep) = match rule.as_slice() { + [Rule::Node(node), Rule::Rep(repeat), Rule::Opt(trailing_sep)] => { + (node, repeat, Some(trailing_sep)) } + [Rule::Node(node), Rule::Rep(repeat)] => (node, repeat, None), _ => return false, }; let repeat = match &**repeat { Rule::Seq(it) => it, _ => return false, }; - match repeat.as_slice() { - [comma, Rule::Node(n)] if comma == &**trailing_comma && n == node => (), - _ => return false, + if !matches!( + repeat.as_slice(), + [comma, Rule::Node(n)] + if trailing_sep.map_or(true, |it| comma == &**it) && n == node + ) { + return false; } let ty = grammar[*node].name.clone(); let name = label.cloned().unwrap_or_else(|| pluralize(&to_lower_snake_case(&ty)));