Repository: nushell/nushell
File: crates/nu-parser/src/parse_keywords.rs (Rust)

Code before:

                    let mut idx = 0;
                    let (lvalue, explicit_type) =
                        parse_var_with_opt_type(working_set, &spans[1..(span.0)], &mut idx, false);
                    // check for extra tokens after the identifier
                    if idx + 1 < span.0 - 1 {
                        working_set.error(ParseError::ExtraTokens(spans[idx + 2]));
                    }

                    let var_name =
                        String::from_utf8_lossy(working_set.get_span_contents(lvalue.span))
                            .trim_start_matches('$')
                            .to_string();

                    if RESERVED_VARIABLE_NAMES.contains(&var_name.as_str()) {
                        working_set.error(ParseError::NameIsBuiltinVar(var_name, lvalue.span))
                    }

                    let var_id = lvalue.as_var();
                    let rhs_type = rvalue.ty.clone();

                    if let Some(explicit_type) = &explicit_type {
                        // When RHS returns Type::Any, check if the explicit type is "generic enough"
                        // to accept any value. Specific types with constraints (like table<a: string>)
                        // should not accept Type::Any at parse time.
                        let compatible = if let Type::Any = rhs_type {
                            // For Type::Any, defer all type checking to runtime.
                            // This allows the runtime to perform actual compatibility validation
                            // when the real data is available.
                            true
                        } else {
                            type_compatible(explicit_type, &rhs_type)
                        };

                        if !compatible {
                            working_set.error(ParseError::TypeMismatch(
                                explicit_type.clone(),
                                rhs_type.clone(),
                                Span::concat(&spans[(span.0 + 1)..]),
                            ));
                        }
                    }

                    if let Some(var_id) = var_id {
                        if explicit_type.is_none() {
                            working_set.set_variable_type(var_id, rhs_type);
                        }
                    }

                    let call = Box::new(Call {
                        decl_id,

Review comment (suggestion):
`type_compatible` already returns true when rhs (or lhs) is `Type::Any`, so I wouldn't think that these explicit checks shouldn't be necessary: https://github.com/nushell/nushell/blob/a86a0dd16e9d9808ac03c757b2fd4069f226ddc9/crates/nu-parser/src/type_check.rs#L91-L92

Could you explain this a bit more? It seems like this is what makes your fix work in the first place, but I'm a bit puzzled as to why these checks would be needed

Code after:
                        Expr::Subexpression(rvalue_block_id),
                        rvalue_span,
                        rvalue_ty,
                    );

                    let mut idx = 0;

                    let (lvalue, explicit_type) =
                        parse_var_with_opt_type(working_set, &spans[1..(span.0)], &mut idx, false);
                    // check for extra tokens after the identifier
                    if idx + 1 < span.0 - 1 {
                        working_set.error(ParseError::ExtraTokens(spans[idx + 2]));
                    }

                    let var_name =
                        String::from_utf8_lossy(working_set.get_span_contents(lvalue.span))
                            .trim_start_matches('$')
                            .to_string();

                    if RESERVED_VARIABLE_NAMES.contains(&var_name.as_str()) {
                        working_set.error(ParseError::NameIsBuiltinVar(var_name, lvalue.span))
                    }

                    let var_id = lvalue.as_var();
                    let rhs_type = rvalue.ty.clone();

                    if let Some(explicit_type) = &explicit_type
                        && !type_compatible(explicit_type, &rhs_type)
                    {
                        working_set.error(ParseError::TypeMismatch(
                            explicit_type.clone(),
                            rhs_type.clone(),
                            Span::concat(&spans[(span.0 + 1)..]),
                        ));
                    }

                    if let Some(var_id) = var_id {
                        if explicit_type.is_none() {
                            working_set.set_variable_type(var_id, rhs_type);
                        }

                        match eval_constant(working_set, &rvalue) {
                            Ok(mut value) => {
                                // In case rhs is parsed as 'any' but is evaluated to a concrete
                                // type:
                                let mut const_type = value.get_type();

                                if let Some(explicit_type) = &explicit_type {
                                    if !type_compatible(explicit_type, &const_type) {
                                        working_set.error(ParseError::TypeMismatch(
                                            explicit_type.clone(),