Skip to content

Commit

Permalink
kconfig: remove tristate choice support
Browse files Browse the repository at this point in the history
I previously submitted a fix for a bug in the choice feature [1], where
I mentioned, "Another (much cleaner) approach would be to remove the
tristate choice support entirely".

There are more issues in the tristate choice feature. For example, you
can observe a couple of bugs in the following test code.

[Test Code]

    config MODULES
            def_bool y
            modules

    choice
            prompt "tristate choice"
            default A

    config A
            tristate "A"

    config B
            tristate "B"

    endchoice

Bug 1: the 'default' property is not correctly processed

'make alldefconfig' produces:

    CONFIG_MODULES=y
    # CONFIG_A is not set
    # CONFIG_B is not set

However, the correct output should be:

    CONFIG_MODULES=y
    CONFIG_A=y
    # CONFIG_B is not set

The unit test file, scripts/kconfig/tests/choice/alldef_expected_config,
is wrong as well.

Bug 2: choice members never get 'y' with randconfig

For the test code above, the following combinations are possible:

               A    B
        (1)    y    n
        (2)    n    y
        (3)    m    m
        (4)    m    n
        (5)    n    m
        (6)    n    n

'make randconfig' never produces (1) or (2).

These bugs are fixable, but a more critical problem is the lack of a
sensible syntax to specify the default for the tristate choice.
The default for the choice must be one of the choice members, which
cannot specify any of the patterns (3) through (6) above.

In addition, I have never seen it being used in a useful way.

The following commits removed unnecessary use of tristate choices:

 - df8df5e ("usb: get rid of 'choice' for legacy gadget drivers")
 - bfb57ef ("rapidio: remove choice for enumeration")

This commit removes the tristate choice support entirely, which allows
me to delete a lot of code, making further refactoring easier.

Note:
This includes the revert of commit fa64e5f ("kconfig/symbol.c:
handle choice_values that depend on 'm' symbols"). It was suspicious
because it did not address the root cause but introduced inconsistency
in visibility between choice members and other symbols.

[1]: https://lore.kernel.org/linux-kbuild/20240427104231.2728905-1-masahiroy@kernel.org/T/#m0a1bb6992581462ceca861b409bb33cb8fd7dbae

Signed-off-by: Masahiro Yamada <masahiroy@kernel.org>
Reviewed-by: Nicolas Schier <nicolas@fjasle.eu>
  • Loading branch information
Masahiro Yamada committed Jul 15, 2024
1 parent 03638aa commit fde1925
Show file tree
Hide file tree
Showing 28 changed files with 45 additions and 325 deletions.
13 changes: 3 additions & 10 deletions Documentation/kbuild/kconfig-language.rst
Original file line number Diff line number Diff line change
Expand Up @@ -409,16 +409,9 @@ choices::
"endchoice"

This defines a choice group and accepts any of the above attributes as
options. A choice can only be of type bool or tristate. If no type is
specified for a choice, its type will be determined by the type of
the first choice element in the group or remain unknown if none of the
choice elements have a type specified, as well.

While a boolean choice only allows a single config entry to be
selected, a tristate choice also allows any number of config entries
to be set to 'm'. This can be used if multiple drivers for a single
hardware exists and only a single driver can be compiled/loaded into
the kernel, but all drivers can be compiled as modules.
options.

A choice only allows a single config entry to be selected.

comment::

Expand Down
56 changes: 6 additions & 50 deletions scripts/kconfig/conf.c
Original file line number Diff line number Diff line change
Expand Up @@ -114,21 +114,13 @@ static void set_randconfig_seed(void)
srand(seed);
}

static bool randomize_choice_values(struct symbol *csym)
static void randomize_choice_values(struct symbol *csym)
{
struct property *prop;
struct symbol *sym;
struct expr *e;
int cnt, def;

/*
* If choice is mod then we may have more items selected
* and if no then no-one.
* In both cases stop.
*/
if (csym->curr.tri != yes)
return false;

prop = sym_get_choice_prop(csym);

/* count entries in choice block */
Expand Down Expand Up @@ -157,8 +149,6 @@ static bool randomize_choice_values(struct symbol *csym)
csym->flags |= SYMBOL_DEF_USER;
/* clear VALID to get value calculated */
csym->flags &= ~SYMBOL_VALID;

return true;
}

enum conf_def_mode {
Expand Down Expand Up @@ -269,15 +259,6 @@ static bool conf_set_all_new_symbols(enum conf_def_mode mode)

sym_clear_all_valid();

/*
* We have different type of choice blocks.
* If curr.tri equals to mod then we can select several
* choice symbols in one block.
* In this case we do nothing.
* If curr.tri equals yes then only one symbol can be
* selected in a choice block and we set it to yes,
* and the rest to no.
*/
if (mode != def_random) {
for_all_symbols(csym) {
if ((sym_is_choice(csym) && !sym_has_value(csym)) ||
Expand All @@ -292,11 +273,10 @@ static bool conf_set_all_new_symbols(enum conf_def_mode mode)

sym_calc_value(csym);
if (mode == def_random)
has_changed |= randomize_choice_values(csym);
else {
randomize_choice_values(csym);
else
set_all_choice_values(csym);
has_changed = true;
}
has_changed = true;
}

return has_changed;
Expand Down Expand Up @@ -454,27 +434,6 @@ static void conf_choice(struct menu *menu)

sym = menu->sym;
is_new = !sym_has_value(sym);
if (sym_is_changeable(sym)) {
conf_sym(menu);
sym_calc_value(sym);
switch (sym_get_tristate_value(sym)) {
case no:
case mod:
return;
case yes:
break;
}
} else {
switch (sym_get_tristate_value(sym)) {
case no:
return;
case mod:
printf("%*s%s\n", indent - 1, "", menu_get_prompt(menu));
return;
case yes:
break;
}
}

while (1) {
int cnt, def;
Expand Down Expand Up @@ -596,9 +555,7 @@ static void conf(struct menu *menu)

if (sym_is_choice(sym)) {
conf_choice(menu);
if (sym->curr.tri != mod)
return;
goto conf_childs;
return;
}

switch (sym->type) {
Expand Down Expand Up @@ -631,8 +588,7 @@ static void check_conf(struct menu *menu)

sym = menu->sym;
if (sym && !sym_has_value(sym) &&
(sym_is_changeable(sym) ||
(sym_is_choice(sym) && sym_get_tristate_value(sym) == yes))) {
(sym_is_changeable(sym) || sym_is_choice(sym))) {

switch (input_mode) {
case listnewconfig:
Expand Down
17 changes: 3 additions & 14 deletions scripts/kconfig/confdata.c
Original file line number Diff line number Diff line change
Expand Up @@ -462,22 +462,12 @@ int conf_read_simple(const char *name, int def)

if (sym && sym_is_choice_value(sym)) {
struct symbol *cs = prop_get_symbol(sym_get_choice_prop(sym));
switch (sym->def[def].tri) {
case no:
break;
case mod:
if (cs->def[def].tri == yes) {
conf_warning("%s creates inconsistent choice state", sym->name);
cs->flags &= ~def_flags;
}
break;
case yes:
if (sym->def[def].tri == yes) {
if (cs->def[def].tri != no)
conf_warning("override: %s changes choice state", sym->name);
cs->def[def].val = sym;
break;
cs->def[def].tri = yes;
}
cs->def[def].tri = EXPR_OR(cs->def[def].tri, sym->def[def].tri);
}
}
free(line);
Expand Down Expand Up @@ -806,8 +796,7 @@ int conf_write_defconfig(const char *filename)

ds = sym_choice_default(choice->sym);
if (sym == ds) {
if ((sym->type == S_BOOLEAN) &&
sym_get_tristate_value(sym) == yes)
if (sym_get_tristate_value(sym) == yes)
continue;
}
}
Expand Down
6 changes: 2 additions & 4 deletions scripts/kconfig/gconf.c
Original file line number Diff line number Diff line change
Expand Up @@ -1067,10 +1067,8 @@ static gchar **fill_row(struct menu *menu)
row[COL_VALUE] =
g_strdup(menu_get_prompt(def_menu));

if (sym_get_type(sym) == S_BOOLEAN) {
row[COL_BTNVIS] = GINT_TO_POINTER(FALSE);
return row;
}
row[COL_BTNVIS] = GINT_TO_POINTER(FALSE);
return row;
}
if (sym->flags & SYMBOL_CHOICEVAL)
row[COL_BTNRAD] = GINT_TO_POINTER(TRUE);
Expand Down
28 changes: 7 additions & 21 deletions scripts/kconfig/mconf.c
Original file line number Diff line number Diff line change
Expand Up @@ -523,28 +523,14 @@ static void build_conf(struct menu *menu)
def_menu = child;
}

val = sym_get_tristate_value(sym);
if (sym_is_changeable(sym)) {
switch (val) {
case yes: ch = '*'; break;
case mod: ch = 'M'; break;
default: ch = ' '; break;
}
item_make("<%c>", ch);
item_set_tag('t');
item_set_data(menu);
} else {
item_make(" ");
item_set_tag(def_menu ? 't' : ':');
item_set_data(menu);
}
item_make(" ");
item_set_tag(def_menu ? 't' : ':');
item_set_data(menu);

item_add_str("%*c%s", indent + 1, ' ', menu_get_prompt(menu));
if (val == yes) {
if (def_menu)
item_add_str(" (%s) --->", menu_get_prompt(def_menu));
return;
}
if (def_menu)
item_add_str(" (%s) --->", menu_get_prompt(def_menu));
return;
} else {
if (menu == current_menu) {
item_make("---%*c%s", indent + 1, ' ', menu_get_prompt(menu));
Expand Down Expand Up @@ -814,7 +800,7 @@ static void conf(struct menu *menu, struct menu *active_menu)
conf(submenu, NULL);
break;
case 't':
if (sym_is_choice(sym) && sym_get_tristate_value(sym) == yes)
if (sym_is_choice(sym))
conf_choice(submenu);
else if (submenu->prompt->type == P_MENU)
conf(submenu, NULL);
Expand Down
31 changes: 2 additions & 29 deletions scripts/kconfig/menu.c
Original file line number Diff line number Diff line change
Expand Up @@ -321,17 +321,6 @@ static void _menu_finalize(struct menu *parent, bool inside_choice)
is_choice = true;

if (is_choice) {
if (sym->type == S_UNKNOWN) {
/* find the first choice value to find out choice type */
current_entry = parent;
for (menu = parent->list; menu; menu = menu->next) {
if (menu->sym && menu->sym->type != S_UNKNOWN) {
menu_set_type(menu->sym->type);
break;
}
}
}

/*
* Use the choice itself as the parent dependency of
* the contained items. This turns the mode of the
Expand Down Expand Up @@ -503,22 +492,6 @@ static void _menu_finalize(struct menu *parent, bool inside_choice)
menu->sym && !sym_is_choice_value(menu->sym)) {
current_entry = menu;
menu->sym->flags |= SYMBOL_CHOICEVAL;
/* Non-tristate choice values of tristate choices must
* depend on the choice being set to Y. The choice
* values' dependencies were propagated to their
* properties above, so the change here must be re-
* propagated.
*/
if (sym->type == S_TRISTATE && menu->sym->type != S_TRISTATE) {
basedep = expr_alloc_comp(E_EQUAL, sym, &symbol_yes);
menu->dep = expr_alloc_and(basedep, menu->dep);
for (prop = menu->sym->prop; prop; prop = prop->next) {
if (prop->menu != menu)
continue;
prop->visible.expr = expr_alloc_and(expr_copy(basedep),
prop->visible.expr);
}
}
menu_add_symbol(P_CHOICE, sym, NULL);
prop = sym_get_choice_prop(sym);
for (ep = &prop->expr; *ep; ep = &(*ep)->left.expr)
Expand Down Expand Up @@ -578,13 +551,13 @@ static void _menu_finalize(struct menu *parent, bool inside_choice)

/*
* For choices, add a reverse dependency (corresponding to a select) of
* '<visibility> && m'. This prevents the user from setting the choice
* '<visibility> && y'. This prevents the user from setting the choice
* mode to 'n' when the choice is visible.
*/
if (sym && sym_is_choice(sym) && parent->prompt) {
sym->rev_dep.expr = expr_alloc_or(sym->rev_dep.expr,
expr_alloc_and(parent->prompt->visible.expr,
expr_alloc_symbol(&symbol_mod)));
expr_alloc_symbol(&symbol_yes)));
}
}

Expand Down
28 changes: 5 additions & 23 deletions scripts/kconfig/nconf.c
Original file line number Diff line number Diff line change
Expand Up @@ -825,30 +825,13 @@ static void build_conf(struct menu *menu)
}

val = sym_get_tristate_value(sym);
if (sym_is_changeable(sym)) {
switch (val) {
case yes:
ch = '*';
break;
case mod:
ch = 'M';
break;
default:
ch = ' ';
break;
}
item_make(menu, 't', "<%c>", ch);
} else {
item_make(menu, def_menu ? 't' : ':', " ");
}
item_make(menu, def_menu ? 't' : ':', " ");

item_add_str("%*c%s", indent + 1,
' ', menu_get_prompt(menu));
if (val == yes) {
if (def_menu)
item_add_str(" (%s) --->", menu_get_prompt(def_menu));
return;
}
if (def_menu)
item_add_str(" (%s) --->", menu_get_prompt(def_menu));
return;
} else {
if (menu == current_menu) {
item_make(menu, ':',
Expand Down Expand Up @@ -1191,8 +1174,7 @@ static void selected_conf(struct menu *menu, struct menu *active_menu)
conf(submenu);
break;
case 't':
if (sym_is_choice(sym) &&
sym_get_tristate_value(sym) == yes)
if (sym_is_choice(sym))
conf_choice(submenu);
else if (submenu->prompt &&
submenu->prompt->type == P_MENU)
Expand Down
23 changes: 14 additions & 9 deletions scripts/kconfig/parser.y
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ static bool inside_choice = false;

%type <symbol> nonconst_symbol
%type <symbol> symbol
%type <type> type logic_type default
%type <type> type default
%type <expr> expr
%type <expr> if_expr
%type <string> end
Expand Down Expand Up @@ -153,6 +153,12 @@ config_stmt: config_entry_start config_option_list
current_entry->filename, current_entry->lineno);
yynerrs++;
}

if (current_entry->sym->type != S_BOOLEAN) {
fprintf(stderr, "%s:%d: error: choice member must be bool\n",
current_entry->filename, current_entry->lineno);
yynerrs++;
}
}

printd(DEBUG_PARSE, "%s:%d:endconfig\n", cur_filename, cur_lineno);
Expand Down Expand Up @@ -235,6 +241,8 @@ choice: T_CHOICE T_EOL

menu_add_entry(sym);
menu_add_expr(P_CHOICE, NULL, NULL);
menu_set_type(S_BOOLEAN);

printd(DEBUG_PARSE, "%s:%d:choice\n", cur_filename, cur_lineno);
};

Expand Down Expand Up @@ -277,10 +285,10 @@ choice_option: T_PROMPT T_WORD_QUOTE if_expr T_EOL
printd(DEBUG_PARSE, "%s:%d:prompt\n", cur_filename, cur_lineno);
};

choice_option: logic_type prompt_stmt_opt T_EOL
choice_option: T_BOOL T_WORD_QUOTE if_expr T_EOL
{
menu_set_type($1);
printd(DEBUG_PARSE, "%s:%d:type(%u)\n", cur_filename, cur_lineno, $1);
menu_add_prompt(P_PROMPT, $2, $3);
printd(DEBUG_PARSE, "%s:%d:bool\n", cur_filename, cur_lineno);
};

choice_option: T_DEFAULT nonconst_symbol if_expr T_EOL
Expand All @@ -290,15 +298,12 @@ choice_option: T_DEFAULT nonconst_symbol if_expr T_EOL
};

type:
logic_type
T_BOOL { $$ = S_BOOLEAN; }
| T_TRISTATE { $$ = S_TRISTATE; }
| T_INT { $$ = S_INT; }
| T_HEX { $$ = S_HEX; }
| T_STRING { $$ = S_STRING; }

logic_type:
T_BOOL { $$ = S_BOOLEAN; }
| T_TRISTATE { $$ = S_TRISTATE; }

default:
T_DEFAULT { $$ = S_UNKNOWN; }
| T_DEF_BOOL { $$ = S_BOOLEAN; }
Expand Down
2 changes: 1 addition & 1 deletion scripts/kconfig/qconf.cc
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,7 @@ void ConfigItem::updateMenu(void)
expr = sym_get_tristate_value(sym);
switch (expr) {
case yes:
if (sym_is_choice_value(sym) && type == S_BOOLEAN)
if (sym_is_choice_value(sym))
setIcon(promptColIdx, choiceYesIcon);
else
setIcon(promptColIdx, symbolYesIcon);
Expand Down
Loading

0 comments on commit fde1925

Please sign in to comment.