~lattis/muon

0d8fef73783047da8e1ec04fa889e9d1d4590ece — Stone Tickle a month ago 5a7234d
implement disabler()
M bootstrap.sh => bootstrap.sh +1 -0
@@ 45,6 45,7 @@ cat \
	src/functions/default/setup.c \
	src/functions/dependency.c \
	src/functions/dict.c \
	src/functions/disabler.c \
	src/functions/environment.c \
	src/functions/external_library.c \
	src/functions/external_program.c \

M include/functions/common.h => include/functions/common.h +2 -0
@@ 14,6 14,8 @@ struct func_impl_name {
struct args_norm { enum obj_type type; uint32_t val, node; bool set; };
struct args_kw { const char *key; enum obj_type type; uint32_t val, node; bool set; bool required; };

extern bool disabler_among_args_immunity;

bool todo(struct workspace *wk, uint32_t rcvr_id, uint32_t args_node, uint32_t *obj);
bool func_lookup(const struct func_impl_name *impl_tbl, const char *name, func_impl *res);


A include/functions/disabler.h => include/functions/disabler.h +6 -0
@@ 0,0 1,6 @@
#ifndef MUON_FUNCTIONS_DISABLER_H
#define MUON_FUNCTIONS_DISABLER_H
#include "functions/common.h"

extern const struct func_impl_name impl_tbl_disabler[];
#endif

M include/lang/object.h => include/lang/object.h +1 -0
@@ 38,6 38,7 @@ enum obj_type {
	obj_environment,
	obj_include_directory,
	obj_option,
	obj_disabler,

	obj_type_count,


M include/lang/workspace.h => include/lang/workspace.h +4 -0
@@ 39,6 39,10 @@ struct option_override {
	bool obj_value;
};

enum {
	disabler_id = 1 << 1
};

struct workspace {
	char argv0[PATH_MAX],
	     source_root[PATH_MAX],

M src/functions/common.c => src/functions/common.c +69 -7
@@ 13,6 13,7 @@
#include "functions/default.h"
#include "functions/dependency.h"
#include "functions/dict.h"
#include "functions/disabler.h"
#include "functions/environment.h"
#include "functions/external_library.h"
#include "functions/external_program.h"


@@ 28,6 29,29 @@
#include "lang/interpreter.h"
#include "log.h"

// HACK: this is pretty terrible, but the least intrusive way to handle
// disablers in function arguments as they are currently implemented.  When
// interp_args sees a disabler, it sets this flag, and "fails".  In the
// function error handler we check this flag and don't raise an error if it is
// set but instead return disabler.
static bool disabler_among_args = false;
// HACK: we also need this for the is_disabler() function :(
bool disabler_among_args_immunity = false;

static bool
interp_args_interp_node(struct workspace *wk, uint32_t arg_node, obj *res)
{
	bool was_immune = disabler_among_args_immunity;
	disabler_among_args_immunity = false;

	if (!interp_node(wk, arg_node, res)) {
		return false;
	}

	disabler_among_args_immunity = was_immune;
	return true;
}

static bool
next_arg(struct ast *ast, uint32_t *arg_node, uint32_t *kwarg_node, const char **kw, struct node **args)
{


@@ 124,9 148,35 @@ typecheck_function_arg_iter(struct workspace *wk, void *_ctx, uint32_t val)
	return ir_cont;
}

static enum iteration_result
typecheck_function_arg_check_disabler_iter(struct workspace *wk, void *_ctx, obj val)
{
	bool *among = _ctx;

	if (val == disabler_id) {
		*among = true;
		return ir_done;
	}
	return ir_cont;
}

static bool
typecheck_function_arg(struct workspace *wk, uint32_t err_node, uint32_t *val, enum obj_type type)
{
	if (!disabler_among_args_immunity) {
		if (*val == disabler_id) {
			disabler_among_args = true;
			return false;
		} else if (get_obj(wk, *val)->type == obj_array) {
			bool among = false;
			obj_array_foreach_flat(wk, *val, &among, typecheck_function_arg_check_disabler_iter);
			if (among) {
				disabler_among_args = true;
				return false;
			}
		}
	}

	bool array_of = false;
	if (type & ARG_TYPE_ARRAY_OF) {
		array_of = true;


@@ 247,7 297,7 @@ interp_args(struct workspace *wk, uint32_t args_node,
					}

					uint32_t val;
					if (!interp_node(wk, arg_node, &val)) {
					if (!interp_args_interp_node(wk, arg_node, &val)) {
						return false;
					}



@@ 278,7 328,7 @@ interp_args(struct workspace *wk, uint32_t args_node,
				goto kwargs;
			}

			if (!interp_node(wk, arg_node, &an[stage][i].val)) {
			if (!interp_args_interp_node(wk, arg_node, &an[stage][i].val)) {
				return false;
			}



@@ 306,7 356,7 @@ process_kwarg:
			}

			obj val;
			if (!interp_node(wk, arg_node, &val)) {
			if (!interp_args_interp_node(wk, arg_node, &val)) {
				return false;
			}



@@ 383,6 433,7 @@ static const struct func_impl_name *func_tbl[obj_type_count][language_mode_count
	[obj_array] = { impl_tbl_array, impl_tbl_array },
	[obj_build_target] = { impl_tbl_build_target },
	[obj_environment] = { impl_tbl_environment, impl_tbl_environment },
	[obj_disabler] = { impl_tbl_disabler, impl_tbl_disabler },
};

bool


@@ 446,18 497,29 @@ builtin_run(struct workspace *wk, bool have_rcvr, uint32_t rcvr_id, uint32_t nod
		}

		if (!func_lookup(impl_tbl, name, &func)) {
			if (recvr_type == obj_disabler) {
				*obj = disabler_id;
				return true;
			}

			interp_error(wk, name_node, "function %s not found", name);
			return false;
		}
	}

	if (!func(wk, rcvr_id, args_node, obj)) {
		if (recvr_type == obj_default) {
			interp_error(wk, name_node, "in function %s",  name);
		if (disabler_among_args) {
			*obj = disabler_id;
			disabler_among_args = false;
			return true;
		} else {
			interp_error(wk, name_node, "in method %s.%s", obj_type_to_s(recvr_type), name);
			if (recvr_type == obj_default) {
				interp_error(wk, name_node, "in function %s",  name);
			} else {
				interp_error(wk, name_node, "in method %s.%s", obj_type_to_s(recvr_type), name);
			}
			return false;
		}
		return false;
	}
	/* L("finished calling %s.%s", obj_type_to_s(recvr_type), name); */
	return true;

M src/functions/compiler.c => src/functions/compiler.c +7 -1
@@ 278,10 278,12 @@ func_compiler_find_library(struct workspace *wk, uint32_t _, uint32_t args_node,
	enum kwargs {
		kw_required,
		kw_static,
		kw_disabler,
	};
	struct args_kw akw[] = {
		[kw_required] = { "required", obj_any },
		[kw_static] = { "static", obj_bool },
		[kw_disabler] = { "disabler", obj_bool },
		0
	};



@@ 326,7 328,11 @@ done:
			return false;
		}

		make_obj(wk, obj, obj_external_library)->dat.external_library.found = false;
		if (akw[kw_disabler].set && get_obj(wk, akw[kw_disabler].val)->dat.boolean) {
			*obj = disabler_id;
		} else {
			make_obj(wk, obj, obj_external_library)->dat.external_library.found = false;
		}
	} else {
		LOG_I("found library '%s' at '%s'", get_cstr(wk, an[0].val), path);
		struct obj *external_library = make_obj(wk, obj, obj_external_library);

M src/functions/default.c => src/functions/default.c +48 -8
@@ 313,10 313,12 @@ func_find_program(struct workspace *wk, obj _, uint32_t args_node, obj *res)
	enum kwargs {
		kw_required,
		kw_native,
		kw_disabler,
	};
	struct args_kw akw[] = {
		[kw_required] = { "required" },
		[kw_native] = { "native", obj_bool },
		[kw_disabler] = { "disabler", obj_bool },
		0
	};



@@ 343,7 345,11 @@ func_find_program(struct workspace *wk, obj _, uint32_t args_node, obj *res)
			return false;
		}

		make_obj(wk, res, obj_external_program)->dat.external_program.found = false;
		if (akw[kw_disabler].set && get_obj(wk, akw[kw_disabler].val)->dat.boolean) {
			*res = disabler_id;
		} else {
			make_obj(wk, res, obj_external_program)->dat.external_program.found = false;
		}
	} else {
		struct obj *external_program = make_obj(wk, res, obj_external_program);
		external_program->dat.external_program.found = true;


@@ 989,35 995,40 @@ func_import(struct workspace *wk, uint32_t _, uint32_t args_node, uint32_t *obj)
}

static bool
func_is_disabler(struct workspace *wk, uint32_t _, uint32_t args_node, uint32_t *obj)
func_is_disabler(struct workspace *wk, obj _, uint32_t args_node, obj *res)
{
	struct args_norm an[] = { { obj_any }, ARG_TYPE_NULL };

	disabler_among_args_immunity = true;
	if (!interp_args(wk, args_node, an, NULL, NULL)) {
		return false;
	}
	disabler_among_args_immunity = false;

	make_obj(wk, obj, obj_bool)->dat.boolean = false;
	make_obj(wk, res, obj_bool)->dat.boolean = an[0].val == disabler_id;
	return true;
}

static bool
func_disabler(struct workspace *wk, uint32_t _, uint32_t args_node, uint32_t *obj)
func_disabler(struct workspace *wk, obj _, uint32_t args_node, obj *res)
{
	if (!interp_args(wk, args_node, NULL, NULL, NULL)) {
		return false;
	}

	interp_error(wk, args_node, "disablers are not supported");
	return false;
	*res = disabler_id;
	return true;
}

static bool
func_set_variable(struct workspace *wk, obj _, uint32_t args_node, obj *res)
{
	struct args_norm an[] = { { obj_string }, { obj_any }, ARG_TYPE_NULL };
	disabler_among_args_immunity = true;
	if (!interp_args(wk, args_node, an, NULL, NULL)) {
		return false;
	}
	disabler_among_args_immunity = false;

	hash_set(&current_project(wk)->scope, get_cstr(wk, an[0].val), an[1].val);
	return true;


@@ 1026,11 1037,20 @@ func_set_variable(struct workspace *wk, obj _, uint32_t args_node, obj *res)
static bool
func_get_variable(struct workspace *wk, obj _, uint32_t args_node, obj *res)
{
	struct args_norm an[] = { { obj_string }, ARG_TYPE_NULL };
	struct args_norm an[] = { { obj_any }, ARG_TYPE_NULL };
	struct args_norm ao[] = { { obj_any }, ARG_TYPE_NULL };
	disabler_among_args_immunity = true;
	if (!interp_args(wk, args_node, an, ao, NULL)) {
		return false;
	}
	disabler_among_args_immunity = false;

	if (an[0].val == disabler_id) {
		*res = disabler_id;
		return true;
	} else if (!typecheck(wk, an[0].node, an[0].val, obj_string)) {
		return false;
	}

	if (!get_obj_id(wk, get_cstr(wk, an[0].val), res, wk->cur_project)) {
		if (ao[0].set) {


@@ 1045,6 1065,21 @@ func_get_variable(struct workspace *wk, obj _, uint32_t args_node, obj *res)
}

static bool
func_is_variable(struct workspace *wk, obj _, uint32_t args_node, obj *res)
{
	struct args_norm an[] = { { obj_string }, ARG_TYPE_NULL };
	disabler_among_args_immunity = true;
	if (!interp_args(wk, args_node, an, NULL, NULL)) {
		return false;
	}
	disabler_among_args_immunity = false;

	obj dont_care;
	make_obj(wk, res, obj_bool)->dat.boolean = get_obj_id(wk, get_cstr(wk, an[0].val), &dont_care, wk->cur_project);
	return true;
}

static bool
func_subdir_done(struct workspace *wk, obj _, uint32_t args_node, obj *res)
{
	if (!interp_args(wk, args_node, NULL, NULL, NULL)) {


@@ 1150,7 1185,7 @@ const struct func_impl_name impl_tbl_default[] =
	{ "install_man", func_install_todo },
	{ "install_subdir", func_install_todo },
	{ "is_disabler", func_is_disabler },
	{ "is_variable", todo },
	{ "is_variable", func_is_variable },
	{ "jar", todo },
	{ "join_paths", func_join_paths },
	{ "library", func_static_library },


@@ 1175,15 1210,20 @@ const struct func_impl_name impl_tbl_default[] =

const struct func_impl_name impl_tbl_default_external[] = {
	{ "assert", func_assert },
	{ "disabler", func_disabler },
	{ "environment", func_environment },
	{ "error", func_error },
	{ "files", func_files },
	{ "find_program", func_find_program },
	{ "get_variable", func_get_variable },
	{ "import", func_import },
	{ "is_disabler", func_is_disabler },
	{ "is_variable", func_is_variable },
	{ "join_paths", func_join_paths },
	{ "message", func_message },
	{ "p", func_p },
	{ "run_command", func_run_command },
	{ "set_variable", func_set_variable },
	{ "setup", func_setup },
	{ "warning", func_warning },
	{ NULL, NULL },

M src/functions/default/dependency.c => src/functions/default/dependency.c +12 -2
@@ 29,6 29,7 @@ struct dep_lookup_ctx {
	obj not_found_message;
	obj version;
	bool is_static;
	bool disabler;
};

static bool


@@ 153,8 154,12 @@ get_dependency(struct workspace *wk, struct dep_lookup_ctx *ctx)
			interp_error(wk, ctx->err_node, "required dependency not found");
			return false;
		} else {
			struct obj *dep = make_obj(wk, ctx->res, obj_dependency);
			dep->dat.dep.name = ctx->name;
			if (ctx->disabler) {
				*ctx->res = disabler_id;
			} else {
				struct obj *dep = make_obj(wk, ctx->res, obj_dependency);
				dep->dat.dep.name = ctx->name;
			}
		}
	} else {
		struct obj *dep = get_obj(wk, *ctx->res);


@@ 201,6 206,7 @@ func_dependency(struct workspace *wk, obj rcvr, uint32_t args_node, obj *res)
		kw_fallback,
		kw_default_options,
		kw_not_found_message,
		kw_disabler,
	};
	struct args_kw akw[] = {
		[kw_required] = { "required" },


@@ 211,6 217,7 @@ func_dependency(struct workspace *wk, obj rcvr, uint32_t args_node, obj *res)
		[kw_fallback] = { "fallback", ARG_TYPE_ARRAY_OF | obj_string },
		[kw_default_options] = { "default_options", ARG_TYPE_ARRAY_OF | obj_string },
		[kw_not_found_message] = { "not_found_message", obj_string },
		[kw_disabler] = { "disabler", obj_bool },
		0
	};



@@ 244,6 251,9 @@ func_dependency(struct workspace *wk, obj rcvr, uint32_t args_node, obj *res)
		.default_options = akw[kw_default_options].val,
		.not_found_message = akw[kw_not_found_message].val,
		.is_static = is_static,
		.disabler = akw[kw_disabler].set
			? get_obj(wk, akw[kw_disabler].val)->dat.boolean
			: false,
	};

	bool handled;

A src/functions/disabler.c => src/functions/disabler.c +22 -0
@@ 0,0 1,22 @@
#include "posix.h"

#include "functions/common.h"
#include "functions/disabler.h"
#include "lang/interpreter.h"
#include "log.h"

static bool
func_disabler_found(struct workspace *wk, obj rcvr, uint32_t args_node, obj *res)
{
	if (!interp_args(wk, args_node, NULL, NULL, NULL)) {
		return false;
	}

	make_obj(wk, res, obj_bool)->dat.boolean = false;
	return true;
}

const struct func_impl_name impl_tbl_disabler[] = {
	{ "found", func_disabler_found },
	{ NULL, NULL },
};

M src/lang/interpreter.c => src/lang/interpreter.c +35 -5
@@ 123,9 123,9 @@ typecheck_array(struct workspace *wk, uint32_t n_id, obj arr, enum obj_type type
static bool interp_chained(struct workspace *wk, uint32_t node_id, uint32_t l_id, uint32_t *obj);

static bool
interp_method(struct workspace *wk, uint32_t node_id, uint32_t l_id, uint32_t *obj)
interp_method(struct workspace *wk, uint32_t node_id, obj l_id, obj *res)
{
	uint32_t result = 0;
	obj result = 0;

	struct node *n = get_node(wk->ast, node_id);



@@ 134,9 134,9 @@ interp_method(struct workspace *wk, uint32_t node_id, uint32_t l_id, uint32_t *o
	}

	if (n->chflg & node_child_d) {
		return interp_chained(wk, n->d, result, obj);
		return interp_chained(wk, n->d, result, res);
	} else {
		*obj = result;
		*res = result;
		return true;
	}
}


@@ 153,6 153,9 @@ interp_index(struct workspace *wk, struct node *n, uint32_t l_id, uint32_t *obj)

	struct obj *l = get_obj(wk, l_id), *r = get_obj(wk, r_id);
	switch (l->type) {
	case obj_disabler:
		*obj = disabler_id;
		return true;
	case obj_array: {
		if (!typecheck(wk, n->r, r_id, obj_number)) {
			return false;


@@ 234,11 237,13 @@ interp_u_minus(struct workspace *wk, struct node *n, uint32_t *obj)

	if (!interp_node(wk, n->l, &l_id)) {
		return false;
	} else if (l_id == disabler_id) {
		*obj = disabler_id;
		return true;
	} else if (!typecheck(wk, n->l, l_id, obj_number)) {
		return false;
	}


	struct obj *num = make_obj(wk, obj, obj_number);
	num->dat.num = -get_obj(wk, l_id)->dat.num;



@@ 256,6 261,11 @@ interp_arithmetic(struct workspace *wk, uint32_t n_id, uint32_t *obj_id)
		return false;
	}

	if (l_id == disabler_id || r_id == disabler_id) {
		*obj_id = disabler_id;
		return true;
	}

	switch (get_obj(wk, l_id)->type) {
	case obj_string: {
		uint32_t res;


@@ 540,6 550,9 @@ interp_not(struct workspace *wk, struct node *n, uint32_t *obj_id)

	if (!interp_node(wk, n->l, &obj_l_id)) {
		return false;
	} else if (obj_l_id == disabler_id) {
		*obj_id = disabler_id;
		return true;
	} else if (!typecheck(wk, n->l, obj_l_id, obj_bool)) {
		return false;
	}


@@ 557,6 570,9 @@ interp_andor(struct workspace *wk, struct node *n, uint32_t *obj_id)

	if (!interp_node(wk, n->l, &obj_l_id)) {
		return false;
	} else if (obj_l_id == disabler_id) {
		*obj_id = disabler_id;
		return true;
	} else if (!typecheck(wk, n->l, obj_l_id, obj_bool)) {
		return false;
	}


@@ 573,6 589,9 @@ interp_andor(struct workspace *wk, struct node *n, uint32_t *obj_id)

	if (!interp_node(wk, n->r, &obj_r_id)) {
		return false;
	} else if (obj_r_id == disabler_id) {
		*obj_id = disabler_id;
		return true;
	} else if (!typecheck(wk, n->r, obj_r_id, obj_bool)) {
		return false;
	}


@@ 595,6 614,11 @@ interp_comparison(struct workspace *wk, struct node *n, uint32_t *obj_id)
		return false;
	}

	if (obj_l_id == disabler_id || obj_r_id == disabler_id) {
		*obj_id = disabler_id;
		return true;
	}

	switch ((enum comparison_type)n->subtype) {
	case comp_equal:
		res = obj_equal(wk, obj_l_id, obj_r_id);


@@ 672,6 696,9 @@ interp_ternary(struct workspace *wk, struct node *n, uint32_t *obj_id)
		uint32_t cond_id;
		if (!interp_node(wk, n->l, &cond_id)) {
			return false;
		} else if (cond_id == disabler_id) {
			*obj_id = disabler_id;
			return true;
		} else if (!typecheck(wk, n->l, cond_id, obj_bool)) {
			return false;
		}


@@ 697,6 724,9 @@ interp_if(struct workspace *wk, struct node *n, uint32_t *obj)
		uint32_t cond_id;
		if (!interp_node(wk, n->l, &cond_id)) {
			return false;
		} else if (cond_id == disabler_id) {
			*obj = disabler_id;
			return true;
		} else if (!typecheck(wk, n->l, cond_id, obj_bool)) {
			return false;
		}

M src/lang/object.c => src/lang/object.c +1 -0
@@ 41,6 41,7 @@ obj_type_to_s(enum obj_type t)
	case obj_environment: return "environment";
	case obj_include_directory: return "include_directory";
	case obj_option: return "option";
	case obj_disabler: return "disabler";

	case obj_type_count:
	case ARG_TYPE_NULL:

M src/lang/workspace.c => src/lang/workspace.c +3 -0
@@ 198,6 198,9 @@ workspace_init(struct workspace *wk)
	hash_init(&wk->scope, 32);

	uint32_t id;
	make_obj(wk, &id, obj_disabler);
	assert(id == disabler_id);

	make_obj(wk, &id, obj_meson);
	hash_set(&wk->scope, "meson", id);


M src/meson.build => src/meson.build +1 -0
@@ 29,6 29,7 @@ src = files([
	'functions/default/setup.c',
	'functions/dependency.c',
	'functions/dict.c',
	'functions/disabler.c',
	'functions/environment.c',
	'functions/external_library.c',
	'functions/external_program.c',

A tests/disabler.meson => tests/disabler.meson +136 -0
@@ 0,0 1,136 @@
d = disabler()

full_path = d.full_path()
assert(is_disabler(full_path), 'Method call is not a disabler')

d2 = message(d)
d3 = (d == d2)
d4 = d + 0
d5 = d2 or true
set_variable('d6', disabler())

has_not_changed = false
if is_disabler(d)
    has_not_changed = true
else
    has_not_changed = true
endif
assert(has_not_changed, 'Disabler has changed.')

assert(is_disabler(d), 'Disabler was not identified correctly.')
assert(is_disabler(d2), 'Function laundered disabler was not identified correctly.')
assert(is_disabler(d3), 'Disabler comparison should yield disabler.')
assert(is_disabler(d4), 'Disabler addition should yield disabler.')
assert(is_disabler(d5), 'Disabler logic op should yield disabler.')
assert(is_disabler(d6), 'set_variable with a disabler should set variable to disabler.')

assert(d, 'Disabler did not cause this to be skipped.')
assert(d2, 'Function laundered disabler did not cause this to be skipped.')
assert(d3, 'Disabler comparison should yield disabler and thus this would not be called.')
assert(d4, 'Disabler addition should yield disabler and thus this would not be called.')
assert(d5, 'Disabler logic op should yield disabler and thus this would not be called.')
assert(d6, 'set_variable with a disabler did not cause this to be skipped.')

number = 0

if d
  number = 1
else
  number = 2
endif

has_not_changed = false
if is_disabler(number)
    has_not_changed = true
else
    has_not_changed = true
endif
assert(has_not_changed, 'Number has changed.')

assert(not is_disabler(number), 'Number should not be a disabler.')
assert(number == 0, 'Plain if handled incorrectly, value should be 0 but is @0@'.format(number))

if d.found()
  number = 1
else
  number = 2
endif

assert(number == 2, 'If found handled incorrectly, value should be 2 but is @0@'.format(number))

assert(not is_disabler(is_variable('d6')), 'is_variable should not return a disabler')
assert(is_variable('d6'), 'is_variable for a disabler should return true')

if_is_not_disabled = false
if is_variable('d6')
    if_is_not_disabled = true
else
    if_is_not_disabled = true
endif
assert(if_is_not_disabled, 'Disabler in is_variable should not skip blocks')

get_d = get_variable('d6')
assert(is_disabler(get_d), 'get_variable should yield a disabler')

get_fallback_d = get_variable('nonexistant', disabler())
assert(is_disabler(get_fallback_d), 'get_variable fallback should yield a disabler')

var_true = true
get_no_fallback_d = get_variable('var_true', disabler())
assert(not is_disabler(get_no_fallback_d), 'get_variable should not fallback to disabler')
assert(get_no_fallback_d, 'get_variable should yield true')

assert(is_disabler(get_variable(disabler())), 'get_variable should yield a disabler')
assert(is_disabler(get_variable(disabler(), var_true)), 'get_variable should yield a disabler')

if_is_disabled = true
if disabler()
  if_is_disabled = false
else
  if_is_disabled = false
endif
assert(if_is_disabled, 'Disabler in "if condition" must skip both blocks')

if not disabler()
  if_is_disabled = false
else
  if_is_disabled = false
endif
assert(if_is_disabled, 'Disabler in "if not condition" must skip both blocks')

if disabler() == 1
  if_is_disabled = false
else
  if_is_disabled = false
endif
assert(if_is_disabled, 'Disabler in "if a==b" must skip both blocks')

loops = 0
disablers = 0
foreach i : [true, disabler(), true]
  loops += 1
  if is_disabler(i)
    disablers += 1
  endif
endforeach
assert(loops == 3, 'Disabler in foreach array')
assert(disablers == 1, 'Disabler in foreach array')

loops = 0
disablers = 0
foreach k, i : {'a': true, 'b': disabler(), 'c': true}
  loops += 1
  if is_disabler(i)
    disablers += 1
  endif
endforeach
assert(loops == 3, 'Disabler in foreach dict')
assert(disablers == 1, 'Disabler in foreach dict')

exes = []

exes += 1

exes += disabler()

exes += 2

M tests/meson.build => tests/meson.build +1 -0
@@ 12,6 12,7 @@ tests = [
	['array.meson'],
	['environment.meson', {'env': 'inherited=secret' }],
	['strings.meson'],
	['disabler.meson'],
]

foreach t : tests