From 15048304e3a806714cb7d64fbb6b40b5d76f0201 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Tue, 5 Sep 2023 12:13:24 +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 +- .../macro_expansion_tests/builtin_fn_macro.rs | 18 +++- crates/hir-expand/src/builtin_fn_macro.rs | 6 +- 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 | 3 + crates/hir-ty/src/tests/simple.rs | 89 ++++++------------ crates/hir-ty/src/tests/traits.rs | 28 +++--- crates/parser/src/grammar/expressions/atom.rs | 19 +++- .../parser/inline/ok/0132_box_expr.rast | 90 ------------------- .../parser/inline/ok/0132_box_expr.rs | 5 -- crates/syntax/rust.ungram | 2 +- crates/syntax/src/ast/generated/nodes.rs | 3 + crates/syntax/src/tests/sourcegen_ast.rs | 20 +++-- 17 files changed, 120 insertions(+), 193 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 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-def/src/macro_expansion_tests/builtin_fn_macro.rs b/crates/hir-def/src/macro_expansion_tests/builtin_fn_macro.rs index ea02072ff5c1..a350511c7a16 100644 --- a/crates/hir-def/src/macro_expansion_tests/builtin_fn_macro.rs +++ b/crates/hir-def/src/macro_expansion_tests/builtin_fn_macro.rs @@ -42,12 +42,22 @@ fn main() { } } "#, - expect![[r#" + expect![[r##" #[rustc_builtin_macro] -macro_rules! column {() => {}} +macro_rules! asm {() => {}} -fn main() { 0 as u32; } -"#]], +fn main() { + let i: u64 = 3; + let o: u64; + unsafe { + builtin #asm ( { + $crate::format_args!("mov {0}, {1}"); + $crate::format_args!("add {0}, 5"); + } + ); + } +} +"##]], ); } diff --git a/crates/hir-expand/src/builtin_fn_macro.rs b/crates/hir-expand/src/builtin_fn_macro.rs index 7eec63617608..00ee5e8b9d01 100644 --- a/crates/hir-expand/src/builtin_fn_macro.rs +++ b/crates/hir-expand/src/builtin_fn_macro.rs @@ -419,9 +419,9 @@ fn asm_expand( let pound = quote! {@PUNCT '#'}; let expanded = quote! { - builtin #pound asm { - ##literals - } + builtin #pound asm ( + {##literals} + ) }; ExpandResult::ok(expanded) } 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..3410c87d391c 100644 --- a/crates/hir-ty/src/mir/lower.rs +++ b/crates/hir-ty/src/mir/lower.rs @@ -370,6 +370,9 @@ 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..e7cb7cd41746 100644 --- a/crates/hir-ty/src/tests/simple.rs +++ b/crates/hir-ty/src/tests/simple.rs @@ -2,55 +2,6 @@ use expect_test::expect; use super::{check, check_infer, check_no_mismatches, check_types}; -#[test] -fn infer_box() { - check_types( - r#" -//- /main.rs crate:main deps:std -fn test() { - let x = box 1; - let t = (x, box x, box &1, box [1]); - t; -} //^ (Box, Box>, Box<&i32>, Box<[i32; 1]>) - -//- /std.rs crate:std -#[prelude_import] use prelude::*; -mod prelude {} - -mod boxed { - #[lang = "owned_box"] - pub struct Box { - inner: *mut T, - } -} -"#, - ); -} - -#[test] -fn infer_box_with_allocator() { - check_types( - r#" -//- /main.rs crate:main deps:std -fn test() { - let x = box 1; - let t = (x, box x, box &1, box [1]); - t; -} //^ (Box, Box, {unknown}>, Box<&i32, {unknown}>, Box<[i32; 1], {unknown}>) - -//- /std.rs crate:std -#[prelude_import] use prelude::*; -mod boxed { - #[lang = "owned_box"] - pub struct Box { - inner: *mut T, - allocator: A, - } -} -"#, - ); -} - #[test] fn infer_adt_self() { check_types( @@ -2763,8 +2714,8 @@ impl [T] { } fn test() { - let vec = <[_]>::into_vec(box [1i32]); - let v: Vec> = <[_]> :: into_vec(box [box Astruct]); + let vec = <[_]>::into_vec(#[rustc_box] Box::new([1i32])); + let v: Vec> = <[_]> :: into_vec(#[rustc_box] Box::new([#[rustc_box] Box::new(Astruct)])); } trait B{} @@ -2774,20 +2725,20 @@ impl B for Astruct {} expect![[r#" 604..608 'self': Box<[T], A> 637..669 '{ ... }': Vec - 683..796 '{ ...t]); }': () + 683..853 '{ ...])); }': () 693..696 'vec': Vec 699..714 '<[_]>::into_vec': fn into_vec(Box<[i32], Global>) -> Vec - 699..726 '<[_]>:...1i32])': Vec - 715..725 'box [1i32]': Box<[i32; 1], Global> - 719..725 '[1i32]': [i32; 1] - 720..724 '1i32': i32 - 736..737 'v': Vec, Global> - 757..774 '<[_]> ...to_vec': fn into_vec, Global>(Box<[Box], Global>) -> Vec, Global> - 757..793 '<[_]> ...ruct])': Vec, Global> - 775..792 'box [b...truct]': Box<[Box; 1], Global> - 779..792 '[box Astruct]': [Box; 1] - 780..791 'box Astruct': Box - 784..791 'Astruct': Astruct + 699..745 '<[_]>:...i32]))': Vec + 715..744 '#[rust...1i32])': Box<[i32; 1], Global> + 737..743 '[1i32]': [i32; 1] + 738..742 '1i32': i32 + 755..756 'v': Vec, Global> + 776..793 '<[_]> ...to_vec': fn into_vec, Global>(Box<[Box], Global>) -> Vec, Global> + 776..850 '<[_]> ...ct)]))': Vec, Global> + 794..849 '#[rust...uct)])': Box<[Box; 1], Global> + 816..848 '[#[rus...ruct)]': [Box; 1] + 817..847 '#[rust...truct)': Box + 839..846 'Astruct': Astruct "#]], ) } @@ -3649,3 +3600,15 @@ fn main() { "#, ); } + +#[test] +fn offset_of() { + check_types( + r#" +fn main() { + builtin#offset_of((,), 0); + // ^^^^^^^^^^^^^^^^^^^^^^^^^ usize +} +"#, + ); +} diff --git a/crates/hir-ty/src/tests/traits.rs b/crates/hir-ty/src/tests/traits.rs index 542df8b3468f..d36b885ec14a 100644 --- a/crates/hir-ty/src/tests/traits.rs +++ b/crates/hir-ty/src/tests/traits.rs @@ -162,16 +162,16 @@ unsafe impl Allocator for Global {} #[lang = "owned_box"] #[fundamental] -pub struct Box; +pub struct Box(T); impl, U: ?Sized, A: Allocator> CoerceUnsized> for Box {} fn send() -> Box + Send + 'static>{ - box async move {} + Box(async move {}) } fn not_send() -> Box + 'static> { - box async move {} + Box(async move {}) } "#, ); @@ -3057,7 +3057,7 @@ impl core::ops::Deref for Box { fn foo() { let s = None; - let f: Box)> = box (|ps| {}); + let f: Box)> = Box { inner: &mut (|ps| {}) }; f(&s); }"#, expect![[r#" @@ -3068,19 +3068,19 @@ fn foo() { 186..197 '*self.inner': T 187..191 'self': &Box 187..197 'self.inner': *mut T - 218..308 '{ ...&s); }': () + 218..324 '{ ...&s); }': () 228..229 's': Option 232..236 'None': Option 246..247 'f': Box)> - 281..294 'box (|ps| {})': Box)> - 286..293 '|ps| {}': impl Fn(&Option) - 287..289 'ps': &Option - 291..293 '{}': () - 300..301 'f': Box)> - 300..305 'f(&s)': () - 302..304 '&s': &Option - 303..304 's': Option - 281..294: expected Box)>, got Box)> + 281..310 'Box { ... {}) }': Box)> + 294..308 '&mut (|ps| {})': &mut impl Fn(&Option) + 300..307 '|ps| {}': impl Fn(&Option) + 301..303 'ps': &Option + 305..307 '{}': () + 316..317 'f': Box)> + 316..321 'f(&s)': () + 318..320 '&s': &Option + 319..320 's': Option "#]], ); } 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/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)));