diff --git a/vlib/v/ast/ast.v b/vlib/v/ast/ast.v index 355234d4f048e2..67ff7914e4d721 100644 --- a/vlib/v/ast/ast.v +++ b/vlib/v/ast/ast.v @@ -925,8 +925,9 @@ pub: name string pos token.Pos typ Type - smartcasts []Type // nested sum types require nested smart casting, for that a list of types is needed - orig_type Type // original sumtype type; 0 if it's not a sumtype + orig_type Type // original sumtype type; 0 if it's not a sumtype +pub mut: + smartcasts []Type // nested sum types require nested smart casting, for that a list of types is needed // TODO: move this to a real docs site later // 10 <- original type (orig_type) // [11, 12, 13] <- cast order (smartcasts) diff --git a/vlib/v/ast/scope.v b/vlib/v/ast/scope.v index 127240c62494a8..9980befaca97d8 100644 --- a/vlib/v/ast/scope.v +++ b/vlib/v/ast/scope.v @@ -157,6 +157,16 @@ pub fn (mut s Scope) register_struct_field(name string, field ScopeStructField) s.struct_fields[name] = field } +pub fn (mut s Scope) register_or_update_struct_field(name string, field ScopeStructField) { + if mut f := s.struct_fields[name] { + if f.struct_type == field.struct_type && f.name == field.name { + s.struct_fields[name].smartcasts = field.smartcasts + return + } + } + s.struct_fields[name] = field +} + pub fn (mut s Scope) register(obj ScopeObject) { if !(obj.name == '_' || obj.name in s.objects) { s.objects[obj.name] = obj diff --git a/vlib/v/checker/checker.v b/vlib/v/checker/checker.v index bcc65052a28d9b..91fbb3648569b0 100644 --- a/vlib/v/checker/checker.v +++ b/vlib/v/checker/checker.v @@ -4273,7 +4273,7 @@ fn (mut c Checker) smartcast(mut expr ast.Expr, cur_type ast.Type, to_type_ ast. mut is_mut := false mut smartcasts := []ast.Type{} expr_sym := c.table.sym(expr.expr_type) - mut orig_type := 0 + mut orig_type := ast.no_type if field := c.table.find_field(expr_sym, expr.field_name) { if field.is_mut { if root_ident := expr.root_ident() { @@ -4286,20 +4286,33 @@ fn (mut c Checker) smartcast(mut expr ast.Expr, cur_type ast.Type, to_type_ ast. orig_type = field.typ } } - if field := scope.find_struct_field(expr.expr.str(), expr.expr_type, expr.field_name) { + mut nested_unwrap := false + if mut field := scope.find_struct_field(expr.expr.str(), expr.expr_type, expr.field_name) { smartcasts << field.smartcasts + nested_unwrap = smartcasts.len > 1 } // smartcast either if the value is immutable or if the mut argument is explicitly given - if !is_mut || expr.is_mut || is_option_unwrap { + if !is_mut || expr.is_mut || is_option_unwrap || orig_type.has_flag(.option) { smartcasts << to_type - scope.register_struct_field(expr.expr.str(), ast.ScopeStructField{ - struct_type: expr.expr_type - name: expr.field_name - typ: cur_type - smartcasts: smartcasts - pos: expr.pos - orig_type: orig_type - }) + if nested_unwrap { + scope.register_or_update_struct_field(expr.expr.str(), ast.ScopeStructField{ + struct_type: expr.expr_type + name: expr.field_name + typ: cur_type + smartcasts: smartcasts + pos: expr.pos + orig_type: orig_type + }) + } else { + scope.register_struct_field(expr.expr.str(), ast.ScopeStructField{ + struct_type: expr.expr_type + name: expr.field_name + typ: cur_type + smartcasts: smartcasts + pos: expr.pos + orig_type: orig_type + }) + } } else { c.smartcast_mut_pos = expr.pos }