diff --git a/crates/mun_codegen/src/ir/body.rs b/crates/mun_codegen/src/ir/body.rs index 1648c19a..29c8b08e 100644 --- a/crates/mun_codegen/src/ir/body.rs +++ b/crates/mun_codegen/src/ir/body.rs @@ -4,6 +4,7 @@ use inkwell::{ basic_block::BasicBlock, builder::Builder, context::Context, + types::BasicTypeEnum, values::{ AggregateValueEnum, BasicMetadataValueEnum, BasicValueEnum, CallSiteValue, FloatValue, FunctionValue, GlobalValue, IntValue, PointerValue, StructValue, @@ -230,6 +231,35 @@ impl<'db, 'ink, 't> BodyIrGenerator<'db, 'ink, 't> { Expr::BinaryOp { lhs, rhs, op } => { self.gen_binary_op(expr, *lhs, *rhs, op.expect("missing op")) } + &Expr::Cast { expr, type_ref: _ } => { + let value = self.gen_expr(expr).expect("no value"); + let value_ty = &self.infer[expr]; + let is_signed = value_ty.signedness().is_signed(); + + let from_ty = value.get_type(); + let to_ty = self + .hir_types + .get_basic_type(&self.infer[expr]) + .expect("expected basic type"); + + Some(match (from_ty, to_ty) { + (BasicTypeEnum::IntType(hg), BasicTypeEnum::IntType(_)) => self + .builder + .build_int_cast_sign_flag( + value.into_int_value(), + to_ty.into_int_type(), + is_signed, + "", + ) + .into(), + (BasicTypeEnum::FloatType(_), BasicTypeEnum::FloatType(_)) => { + let src = value_ty.float_width(); + + todo!() + } + (_, _) => unreachable!("unimplemented cast from {from_ty} to {to_ty}"), + }) + } Expr::UnaryOp { expr, op } => self.gen_unary_op(*expr, *op), Expr::MethodCall { .. } => { unimplemented!("Method calls are not yet implemented in the IR generator") diff --git a/crates/mun_hir/src/expr.rs b/crates/mun_hir/src/expr.rs index 4a42f83f..fd04d20c 100644 --- a/crates/mun_hir/src/expr.rs +++ b/crates/mun_hir/src/expr.rs @@ -305,6 +305,10 @@ pub enum Expr { rhs: ExprId, op: Option, }, + Cast { + expr: ExprId, + type_ref: LocalTypeRefId, + }, Index { base: ExprId, index: ExprId, @@ -457,6 +461,9 @@ impl Expr { f(*expr); } } + Expr::Cast { expr, type_ref: _ } => { + f(*expr); + } } } } @@ -915,6 +922,12 @@ impl<'a> ExprCollector<'a> { let index = self.collect_expr_opt(e.index()); self.alloc_expr(Expr::Index { base, index }, syntax_ptr) } + ast::ExprKind::CastExpr(e) => { + let expr = self.collect_expr_opt(e.expr()); + let type_ref = self.type_ref_builder.alloc_from_node_opt(e.ty().as_ref()); + + self.alloc_expr(Expr::Cast { expr, type_ref }, syntax_ptr) + } } } diff --git a/crates/mun_hir/src/expr/validator/uninitialized_access.rs b/crates/mun_hir/src/expr/validator/uninitialized_access.rs index 134afab7..5df5c839 100644 --- a/crates/mun_hir/src/expr/validator/uninitialized_access.rs +++ b/crates/mun_hir/src/expr/validator/uninitialized_access.rs @@ -122,6 +122,9 @@ impl ExprValidator<'_> { self.validate_expr_access(sink, initialized_patterns, *lhs, lhs_expr_kind); self.validate_expr_access(sink, initialized_patterns, *rhs, ExprKind::Normal); } + Expr::Cast { expr, type_ref: _ } => { + self.validate_expr_access(sink, initialized_patterns, *expr, expr_side) + } Expr::Block { statements, tail } => { for statement in statements.iter() { match statement { diff --git a/crates/mun_hir/src/ty.rs b/crates/mun_hir/src/ty.rs index d882e010..b3529136 100644 --- a/crates/mun_hir/src/ty.rs +++ b/crates/mun_hir/src/ty.rs @@ -19,7 +19,8 @@ use smallvec::SmallVec; use crate::{ display::{HirDisplay, HirFormatter}, ty::{infer::InferTy, lower::fn_sig_for_struct_constructor}, - HasVisibility, HirDatabase, Struct, StructMemoryKind, TypeAlias, Visibility, + FloatBitness, HasVisibility, HirDatabase, Signedness, Struct, StructMemoryKind, TypeAlias, + Visibility, }; #[cfg(test)] @@ -130,6 +131,25 @@ impl Ty { TyKind::Tuple(0, Substitution::empty()).intern() } + pub fn signedness(&self) -> Signedness { + match self.interned() { + TyKind::Int(ty) => ty.signedness, + _ => unreachable!("expected signedness for int type, got {self:?}"), + } + } + + pub fn float_width(&self) -> usize { + match self.interned() { + TyKind::Float(FloatTy { + bitness: FloatBitness::X32, + }) => 32, + TyKind::Float(FloatTy { + bitness: FloatBitness::X64, + }) => 64, + _ => unreachable!("expected float width for float type, got {self:?}"), + } + } + /// Constructs a new struct type pub fn struct_ty(strukt: Struct) -> Ty { TyKind::Struct(strukt).intern() diff --git a/crates/mun_hir/src/ty/infer.rs b/crates/mun_hir/src/ty/infer.rs index ee69da8d..c5f5566f 100644 --- a/crates/mun_hir/src/ty/infer.rs +++ b/crates/mun_hir/src/ty/infer.rs @@ -156,6 +156,13 @@ enum ActiveLoop { For, } +#[derive(Clone, Debug)] +pub(super) struct Cast { + expr: ExprId, + source_expr: ExprId, + cast_ty: Ty, +} + /// The inference context contains all information needed during type inference. struct InferenceResultBuilder<'a> { db: &'a dyn HirDatabase, @@ -180,6 +187,8 @@ struct InferenceResultBuilder<'a> { /// Stores the resolution of method calls method_resolution: FxHashMap, + + deferred_cast: Vec, } impl<'a> InferenceResultBuilder<'a> { @@ -197,6 +206,7 @@ impl<'a> InferenceResultBuilder<'a> { resolver, return_ty: TyKind::Unknown.intern(), // set in collect_fn_signature method_resolution: FxHashMap::default(), + deferred_cast: Vec::default(), } } @@ -378,6 +388,18 @@ impl InferenceResultBuilder<'_> { } _ => error_type(), }, + Expr::Cast { expr, type_ref } => { + let cast_ty = self.resolve_type(*type_ref); + let _expr_ty = self.infer_expr(*expr, &Expectation::none()); + + self.deferred_cast.push(Cast { + expr: tgt_expr, + source_expr: *expr, + cast_ty: cast_ty.clone(), + }); + + cast_ty + } Expr::Block { statements, tail } => self.infer_block(statements, *tail, expected), Expr::Call { callee: call, args } => self.infer_call(tgt_expr, *call, args, expected), Expr::MethodCall { @@ -1001,6 +1023,7 @@ impl InferenceResultBuilder<'_> { fn resolve_all(mut self) -> InferenceResult { // FIXME resolve obligations as well (use Guidance if necessary) //let mut tv_stack = Vec::new(); + let mut expr_types = std::mem::take(&mut self.type_of_expr); for (expr, ty) in expr_types.iter_mut() { let was_unknown = ty.is_unknown(); @@ -1019,6 +1042,25 @@ impl InferenceResultBuilder<'_> { } *ty = resolved; } + + for cast in self.deferred_cast { + let expr_ty = &expr_types[cast.expr]; + + match (expr_ty.interned(), cast.cast_ty.interned()) { + (_, TyKind::Bool) => { + self.diagnostics.push(InferenceDiagnostic::InvalidCast { + expr: cast.source_expr, + error: diagnostics::CastError::CastToBool, + expr_ty: expr_ty.clone(), + cast_ty: cast.cast_ty, + }); + } + (TyKind::Bool, TyKind::Int(_)) + | (TyKind::Int(_) | TyKind::Float(_), TyKind::Int(_) | TyKind::Float(_)) => {} + (_, _) => {} + } + } + InferenceResult { // field_resolutions: self.field_resolutions, // variant_resolutions: self.variant_resolutions, @@ -1265,6 +1307,11 @@ mod diagnostics { ExprId, Function, HirDatabase, IntTy, Name, Ty, }; + #[derive(Debug, PartialEq, Eq, Clone, Copy)] + pub enum CastError { + CastToBool, + } + #[derive(Debug, PartialEq, Eq, Clone)] pub(crate) enum InferenceDiagnostic { UnresolvedValue { @@ -1280,6 +1327,12 @@ mod diagnostics { id: ExprId, found: Ty, }, + InvalidCast { + expr: ExprId, + error: CastError, + expr_ty: Ty, + cast_ty: Ty, + }, ParameterCountMismatch { id: ExprId, found: usize, @@ -1731,6 +1784,12 @@ mod diagnostics { .either(|it| it.syntax_node_ptr(), |it| it.syntax_node_ptr()); sink.push(PrivateAccess { file, expr }); } + InferenceDiagnostic::InvalidCast { + expr: _, + error: _, + expr_ty: _, + cast_ty: _, + } => todo!(), } } } diff --git a/crates/mun_syntax/src/ast/generated.rs b/crates/mun_syntax/src/ast/generated.rs index cfc462fd..f57cf743 100644 --- a/crates/mun_syntax/src/ast/generated.rs +++ b/crates/mun_syntax/src/ast/generated.rs @@ -315,6 +315,38 @@ impl CallExpr { } } +// CastExpr + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct CastExpr { + pub(crate) syntax: SyntaxNode, +} + +impl AstNode for CastExpr { + fn can_cast(kind: SyntaxKind) -> bool { + matches!(kind, CAST_EXPR) + } + fn cast(syntax: SyntaxNode) -> Option { + if Self::can_cast(syntax.kind()) { + Some(CastExpr { syntax }) + } else { + None + } + } + fn syntax(&self) -> &SyntaxNode { + &self.syntax + } +} +impl CastExpr { + pub fn expr(&self) -> Option { + super::child_opt(self) + } + + pub fn ty(&self) -> Option { + super::child_opt(self) + } +} + // Condition #[derive(Debug, Clone, PartialEq, Eq, Hash)] @@ -362,6 +394,7 @@ impl AstNode for Expr { | PREFIX_EXPR | PATH_EXPR | BIN_EXPR + | CAST_EXPR | PAREN_EXPR | CALL_EXPR | METHOD_CALL_EXPR @@ -394,6 +427,7 @@ pub enum ExprKind { PrefixExpr(PrefixExpr), PathExpr(PathExpr), BinExpr(BinExpr), + CastExpr(CastExpr), ParenExpr(ParenExpr), CallExpr(CallExpr), MethodCallExpr(MethodCallExpr), @@ -428,6 +462,11 @@ impl From for Expr { Expr { syntax: n.syntax } } } +impl From for Expr { + fn from(n: CastExpr) -> Expr { + Expr { syntax: n.syntax } + } +} impl From for Expr { fn from(n: ParenExpr) -> Expr { Expr { syntax: n.syntax } @@ -501,6 +540,7 @@ impl Expr { PREFIX_EXPR => ExprKind::PrefixExpr(PrefixExpr::cast(self.syntax.clone()).unwrap()), PATH_EXPR => ExprKind::PathExpr(PathExpr::cast(self.syntax.clone()).unwrap()), BIN_EXPR => ExprKind::BinExpr(BinExpr::cast(self.syntax.clone()).unwrap()), + CAST_EXPR => ExprKind::CastExpr(CastExpr::cast(self.syntax.clone()).unwrap()), PAREN_EXPR => ExprKind::ParenExpr(ParenExpr::cast(self.syntax.clone()).unwrap()), CALL_EXPR => ExprKind::CallExpr(CallExpr::cast(self.syntax.clone()).unwrap()), METHOD_CALL_EXPR => { diff --git a/crates/mun_syntax/src/grammar.ron b/crates/mun_syntax/src/grammar.ron index 6baed487..1322f3fa 100644 --- a/crates/mun_syntax/src/grammar.ron +++ b/crates/mun_syntax/src/grammar.ron @@ -150,6 +150,7 @@ Grammar( "PREFIX_EXPR", "LITERAL", "BIN_EXPR", + "CAST_EXPR", "PAREN_EXPR", "CALL_EXPR", "METHOD_CALL_EXPR", @@ -290,6 +291,9 @@ Grammar( "PathExpr": (options: ["Path"]), "PrefixExpr": (options: ["Expr"]), "BinExpr": (), + "CastExpr": ( + options: [ ["expr", "Expr"], ["ty", "TypeRef"] ] + ), "Literal": (), "ParenExpr": (options: ["Expr"]), "CallExpr": ( @@ -326,6 +330,7 @@ Grammar( "PrefixExpr", "PathExpr", "BinExpr", + "CastExpr", "ParenExpr", "CallExpr", "MethodCallExpr", diff --git a/crates/mun_syntax/src/parsing/grammar.rs b/crates/mun_syntax/src/parsing/grammar.rs index 61d19133..72c69736 100644 --- a/crates/mun_syntax/src/parsing/grammar.rs +++ b/crates/mun_syntax/src/parsing/grammar.rs @@ -12,7 +12,7 @@ use super::{ token_set::TokenSet, SyntaxKind::{ self, ARG_LIST, ARRAY_EXPR, ARRAY_TYPE, BIND_PAT, BIN_EXPR, BLOCK_EXPR, BREAK_EXPR, - CALL_EXPR, CONDITION, EOF, ERROR, EXPR_STMT, EXTERN, FIELD_EXPR, FLOAT_NUMBER, + CALL_EXPR, CAST_EXPR, CONDITION, EOF, ERROR, EXPR_STMT, EXTERN, FIELD_EXPR, FLOAT_NUMBER, FUNCTION_DEF, GC_KW, IDENT, IF_EXPR, INDEX, INDEX_EXPR, INT_NUMBER, LET_STMT, LITERAL, LOOP_EXPR, MEMORY_TYPE_SPECIFIER, NAME, NAME_REF, NEVER_TYPE, PARAM, PARAM_LIST, PAREN_EXPR, PATH, PATH_EXPR, PATH_SEGMENT, PATH_TYPE, PLACEHOLDER_PAT, PREFIX_EXPR, diff --git a/crates/mun_syntax/src/parsing/grammar/expressions.rs b/crates/mun_syntax/src/parsing/grammar/expressions.rs index 7b1b2f66..cb263a55 100644 --- a/crates/mun_syntax/src/parsing/grammar/expressions.rs +++ b/crates/mun_syntax/src/parsing/grammar/expressions.rs @@ -1,10 +1,10 @@ use super::{ error_block, expressions, name_ref, name_ref_or_index, paths, patterns, types, BlockLike, CompletedMarker, Marker, Parser, SyntaxKind, TokenSet, ARG_LIST, ARRAY_EXPR, BIN_EXPR, - BLOCK_EXPR, BREAK_EXPR, CALL_EXPR, CONDITION, EOF, ERROR, EXPR_STMT, FIELD_EXPR, FLOAT_NUMBER, - IDENT, IF_EXPR, INDEX, INDEX_EXPR, INT_NUMBER, LET_STMT, LITERAL, LOOP_EXPR, PAREN_EXPR, - PATH_EXPR, PATH_TYPE, PREFIX_EXPR, RECORD_FIELD, RECORD_FIELD_LIST, RECORD_LIT, RETURN_EXPR, - STRING, WHILE_EXPR, + BLOCK_EXPR, BREAK_EXPR, CALL_EXPR, CAST_EXPR, CONDITION, EOF, ERROR, EXPR_STMT, FIELD_EXPR, + FLOAT_NUMBER, IDENT, IF_EXPR, INDEX, INDEX_EXPR, INT_NUMBER, LET_STMT, LITERAL, LOOP_EXPR, + PAREN_EXPR, PATH_EXPR, PATH_TYPE, PREFIX_EXPR, RECORD_FIELD, RECORD_FIELD_LIST, RECORD_LIT, + RETURN_EXPR, STRING, WHILE_EXPR, }; use crate::{parsing::grammar::paths::PATH_FIRST, SyntaxKind::METHOD_CALL_EXPR}; @@ -153,6 +153,11 @@ fn expr_bp(p: &mut Parser<'_>, r: Restrictions, bp: u8) -> (Option, r: Restrictions, bp: u8) -> (Option, lhs: CompletedMarker) -> CompletedMarker { + assert!(p.at(T![as])); + let m = lhs.precede(p); + p.bump(T![as]); + types::type_(p); + m.complete(p, CAST_EXPR) +} + fn current_op(p: &Parser<'_>) -> (u8, SyntaxKind) { match p.current() { T![+] if p.at(T![+=]) => (1, T![+=]), @@ -194,6 +207,7 @@ fn current_op(p: &Parser<'_>) -> (u8, SyntaxKind) { T![<] if p.at(T![<<=]) => (1, T![<<=]), T![<] if p.at(T![<<]) => (9, T![<<]), T![<] => (5, T![<]), + T![as] => (12, T![as]), _ => (0, T![_]), } } diff --git a/crates/mun_syntax/src/syntax_kind/generated.rs b/crates/mun_syntax/src/syntax_kind/generated.rs index 169243de..19c91d21 100644 --- a/crates/mun_syntax/src/syntax_kind/generated.rs +++ b/crates/mun_syntax/src/syntax_kind/generated.rs @@ -132,6 +132,7 @@ pub enum SyntaxKind { PREFIX_EXPR, LITERAL, BIN_EXPR, + CAST_EXPR, PAREN_EXPR, CALL_EXPR, METHOD_CALL_EXPR, @@ -614,6 +615,7 @@ impl SyntaxKind { PREFIX_EXPR => &SyntaxInfo { name: "PREFIX_EXPR" }, LITERAL => &SyntaxInfo { name: "LITERAL" }, BIN_EXPR => &SyntaxInfo { name: "BIN_EXPR" }, + CAST_EXPR => &SyntaxInfo { name: "CAST_EXPR" }, PAREN_EXPR => &SyntaxInfo { name: "PAREN_EXPR" }, CALL_EXPR => &SyntaxInfo { name: "CALL_EXPR" }, METHOD_CALL_EXPR => &SyntaxInfo { name: "METHOD_CALL_EXPR" },