fix(options): crash on ":let &l:autoread = v:true" #41158

Problem:
Assigning to the local value of a global-local boolean option
('autoread', 'autocomplete', 'fsync') aborts:

    Assertion failed: (curval.type == newval.type), function
    ex_let_option, file vars.c, line 1408.

    ex_let_one
    ex_let_vars
    ex_let
    execute_cmd0
    do_cmdline
    call_user_func
    ...
    eval_map_expr
    vgetorpeek
    vgetc
    state_enter
    main

A global-local option may have local value `kObjectTypeUnset`, but
`ex_let_option()` guards only `kObjectTypeNil`.

Solution:
When curval is Unset, resolve it to the inherited global value.
This commit is contained in:
Justin M. Keyes
2026-08-04 13:28:26 -04:00
committed by GitHub
parent 387bd0fbe7
commit ceccca7780
2 changed files with 17 additions and 5 deletions

View File

@@ -115,7 +115,7 @@ describe(':let', function()
eq(false, api.nvim_get_option_value('equalalways', {}))
end)
it('assigning bool/special to a string option gives E928, not a crash', function()
it('no crash: assigning bool/special to a string option gives E928', function()
for _, v in ipairs({ 'v:true', 'v:false', 'v:null' }) do
-- Regular string option.
eq('Vim(let):E928: String required', t.pcall_err(command, 'let &makeprg = ' .. v))
@@ -123,6 +123,17 @@ describe(':let', function()
eq('Vim(let):E928: String required', t.pcall_err(command, 'let &t_Co = ' .. v))
end
end)
it('no crash: assigning to local value of a global-local option', function()
-- Unset local reads as Unset, and -1 unsets it again, so curval/newval types may differ.
for _, o in ipairs({ 'autoread', 'autocomplete', 'fsync' }) do
command(('setglobal %s'):format(o)) -- global = on
command(('let &l:%s = v:false'):format(o)) -- unset local <- bool: sets local off
eq(false, api.nvim_get_option_value(o, {}))
command(('let &l:%s = -1'):format(o)) -- set local <- -1: unsets local, inheriting global
eq(true, api.nvim_get_option_value(o, {}))
end
end)
end)
describe(':let and :const', function()