Skip to content

Commit

Permalink
SanderMertens#1430 Report errors for invalid usage of cascade & desc
Browse files Browse the repository at this point in the history
  • Loading branch information
SanderMertens authored Nov 8, 2024
1 parent a5b8055 commit d4be81c
Show file tree
Hide file tree
Showing 14 changed files with 310 additions and 877 deletions.
65 changes: 61 additions & 4 deletions distr/flecs.c
Original file line number Diff line number Diff line change
Expand Up @@ -14884,6 +14884,9 @@ int flecs_observer_add_child(
ecs_observer_t *o,
const ecs_observer_desc_t *child_desc)
{
ecs_assert(child_desc->query.flags & EcsQueryNested,
ECS_INTERNAL_ERROR, NULL);

ecs_observer_t *child_observer = flecs_observer_init(
world, 0, child_desc);
if (!child_observer) {
Expand Down Expand Up @@ -14933,8 +14936,9 @@ int flecs_multi_observer_init(
ecs_os_zeromem(&child_desc.entity);
ecs_os_zeromem(&child_desc.query.terms);
ecs_os_zeromem(&child_desc.query);
ecs_os_memcpy_n(child_desc.events, o->events,
ecs_entity_t, o->event_count);
ecs_os_memcpy_n(child_desc.events, o->events, ecs_entity_t, o->event_count);

child_desc.query.flags |= EcsQueryNested;

int i, term_count = query->term_count;
bool optional_only = query->flags & EcsQueryMatchThis;
Expand Down Expand Up @@ -32402,6 +32406,27 @@ int flecs_query_create_cache(

ecs_os_memcpy_n(impl->cache->field_map, field_map, int8_t, dst_count);
}
} else {
/* Check if query has features that are unsupported for uncached */
ecs_assert(q->cache_kind == EcsQueryCacheNone, ECS_INTERNAL_ERROR, NULL);

if (!(q->flags & EcsQueryNested)) {
/* If uncached query is not create to populate a cached query, it
* should not have cascade modifiers */
int32_t i, count = q->term_count;
ecs_term_t *terms = q->terms;
for (i = 0; i < count; i ++) {
ecs_term_t *term = &terms[i];
if (term->src.id & EcsCascade) {
char *query_str = ecs_query_str(q);
ecs_err(
"cascade is unsupported for uncached query\n %s",
query_str);
ecs_os_free(query_str);
goto error;
}
}
}
}

return 0;
Expand Down Expand Up @@ -34249,6 +34274,36 @@ int flecs_term_finalize(
}
}

if (term->first.id & EcsCascade) {
flecs_query_validator_error(ctx,
"cascade modifier invalid for term.first");
return -1;
}

if (term->second.id & EcsCascade) {
flecs_query_validator_error(ctx,
"cascade modifier invalid for term.second");
return -1;
}

if (term->first.id & EcsDesc) {
flecs_query_validator_error(ctx,
"desc modifier invalid for term.first");
return -1;
}

if (term->second.id & EcsDesc) {
flecs_query_validator_error(ctx,
"desc modifier invalid for term.second");
return -1;
}

if (term->src.id & EcsDesc && !(term->src.id & EcsCascade)) {
flecs_query_validator_error(ctx,
"desc modifier invalid without cascade");
return -1;
}

if (term->src.id & EcsCascade) {
/* Cascade always implies up traversal */
term->src.id |= EcsUp;
Expand Down Expand Up @@ -67953,16 +68008,18 @@ ecs_query_cache_t* flecs_query_cache_init(
desc.ctx = NULL;
desc.binding_ctx = NULL;
desc.ctx_free = NULL;
desc.binding_ctx_free = NULL;
desc.binding_ctx_free = NULL;

ecs_query_cache_t *result = flecs_bcalloc(&stage->allocators.query_cache);
result->entity = entity;
impl->cache = result;

ecs_observer_desc_t observer_desc = { .query = desc };
observer_desc.query.flags |= EcsQueryNested;

ecs_flags32_t query_flags = const_desc->flags | world->default_query_flags;
desc.flags |= EcsQueryMatchEmptyTables | EcsQueryTableOnly;
desc.flags |= EcsQueryMatchEmptyTables | EcsQueryTableOnly | EcsQueryNested;

ecs_query_t *q = result->query = ecs_query_init(world, &desc);
if (!q) {
goto error;
Expand Down
2 changes: 1 addition & 1 deletion distr/flecs.h
Original file line number Diff line number Diff line change
Expand Up @@ -481,7 +481,7 @@ extern "C" {
#define EcsQueryIsCacheable (1u << 25u) /* All terms of query are cacheable */
#define EcsQueryHasTableThisVar (1u << 26u) /* Does query have $this table var */
#define EcsQueryCacheYieldEmptyTables (1u << 27u) /* Does query cache empty tables */

#define EcsQueryNested (1u << 28u) /* Query created by a query (for observer, cache) */

////////////////////////////////////////////////////////////////////////////////
//// Term flags (used by ecs_term_t::flags_)
Expand Down
2 changes: 1 addition & 1 deletion include/flecs/private/api_flags.h
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,7 @@ extern "C" {
#define EcsQueryIsCacheable (1u << 25u) /* All terms of query are cacheable */
#define EcsQueryHasTableThisVar (1u << 26u) /* Does query have $this table var */
#define EcsQueryCacheYieldEmptyTables (1u << 27u) /* Does query cache empty tables */

#define EcsQueryNested (1u << 28u) /* Query created by a query (for observer, cache) */

////////////////////////////////////////////////////////////////////////////////
//// Term flags (used by ecs_term_t::flags_)
Expand Down
8 changes: 6 additions & 2 deletions src/observer.c
Original file line number Diff line number Diff line change
Expand Up @@ -729,6 +729,9 @@ int flecs_observer_add_child(
ecs_observer_t *o,
const ecs_observer_desc_t *child_desc)
{
ecs_assert(child_desc->query.flags & EcsQueryNested,
ECS_INTERNAL_ERROR, NULL);

ecs_observer_t *child_observer = flecs_observer_init(
world, 0, child_desc);
if (!child_observer) {
Expand Down Expand Up @@ -778,8 +781,9 @@ int flecs_multi_observer_init(
ecs_os_zeromem(&child_desc.entity);
ecs_os_zeromem(&child_desc.query.terms);
ecs_os_zeromem(&child_desc.query);
ecs_os_memcpy_n(child_desc.events, o->events,
ecs_entity_t, o->event_count);
ecs_os_memcpy_n(child_desc.events, o->events, ecs_entity_t, o->event_count);

child_desc.query.flags |= EcsQueryNested;

int i, term_count = query->term_count;
bool optional_only = query->flags & EcsQueryMatchThis;
Expand Down
21 changes: 21 additions & 0 deletions src/query/api.c
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,27 @@ int flecs_query_create_cache(

ecs_os_memcpy_n(impl->cache->field_map, field_map, int8_t, dst_count);
}
} else {
/* Check if query has features that are unsupported for uncached */
ecs_assert(q->cache_kind == EcsQueryCacheNone, ECS_INTERNAL_ERROR, NULL);

if (!(q->flags & EcsQueryNested)) {
/* If uncached query is not create to populate a cached query, it
* should not have cascade modifiers */
int32_t i, count = q->term_count;
ecs_term_t *terms = q->terms;
for (i = 0; i < count; i ++) {
ecs_term_t *term = &terms[i];
if (term->src.id & EcsCascade) {
char *query_str = ecs_query_str(q);
ecs_err(
"cascade is unsupported for uncached query\n %s",
query_str);
ecs_os_free(query_str);
goto error;
}
}
}
}

return 0;
Expand Down
6 changes: 4 additions & 2 deletions src/query/engine/cache.c
Original file line number Diff line number Diff line change
Expand Up @@ -1226,16 +1226,18 @@ ecs_query_cache_t* flecs_query_cache_init(
desc.ctx = NULL;
desc.binding_ctx = NULL;
desc.ctx_free = NULL;
desc.binding_ctx_free = NULL;
desc.binding_ctx_free = NULL;

ecs_query_cache_t *result = flecs_bcalloc(&stage->allocators.query_cache);
result->entity = entity;
impl->cache = result;

ecs_observer_desc_t observer_desc = { .query = desc };
observer_desc.query.flags |= EcsQueryNested;

ecs_flags32_t query_flags = const_desc->flags | world->default_query_flags;
desc.flags |= EcsQueryMatchEmptyTables | EcsQueryTableOnly;
desc.flags |= EcsQueryMatchEmptyTables | EcsQueryTableOnly | EcsQueryNested;

ecs_query_t *q = result->query = ecs_query_init(world, &desc);
if (!q) {
goto error;
Expand Down
30 changes: 30 additions & 0 deletions src/query/validator.c
Original file line number Diff line number Diff line change
Expand Up @@ -789,6 +789,36 @@ int flecs_term_finalize(
}
}

if (term->first.id & EcsCascade) {
flecs_query_validator_error(ctx,
"cascade modifier invalid for term.first");
return -1;
}

if (term->second.id & EcsCascade) {
flecs_query_validator_error(ctx,
"cascade modifier invalid for term.second");
return -1;
}

if (term->first.id & EcsDesc) {
flecs_query_validator_error(ctx,
"desc modifier invalid for term.first");
return -1;
}

if (term->second.id & EcsDesc) {
flecs_query_validator_error(ctx,
"desc modifier invalid for term.second");
return -1;
}

if (term->src.id & EcsDesc && !(term->src.id & EcsCascade)) {
flecs_query_validator_error(ctx,
"desc modifier invalid without cascade");
return -1;
}

if (term->src.id & EcsCascade) {
/* Cascade always implies up traversal */
term->src.id |= EcsUp;
Expand Down
3 changes: 2 additions & 1 deletion test/meta/src/SerializeQueryInfoToJson.c
Original file line number Diff line number Diff line change
Expand Up @@ -456,7 +456,8 @@ void SerializeQueryInfoToJson_1_tag_cascade(void) {
ECS_ENTITY(world, Foo, (OnInstantiate, Inherit));

ecs_query_t *q = ecs_query(world, {
.expr = "Foo(cascade)"
.expr = "Foo(cascade)",
.cache_kind = EcsQueryCacheAuto
});

test_assert(q != NULL);
Expand Down
16 changes: 7 additions & 9 deletions test/query/project.json
Original file line number Diff line number Diff line change
Expand Up @@ -1555,14 +1555,6 @@
}, {
"id": "Cascade",
"testcases": [
"this_self_cascade_childof_uncached",
"this_cascade_childof_uncached",
"this_written_self_cascade_childof_uncached",
"this_written_cascade_childof_uncached",
"this_self_cascade_childof_w_parent_flag_uncached",
"this_cascade_childof_w_parent_flag_uncached",
"this_written_self_cascade_childof_w_parent_flag_uncached",
"this_written_cascade_childof_w_parent_flag_uncached",
"parent_cascade",
"existing_custom_rel_cascade",
"new_custom_rel_cascade",
Expand All @@ -1579,7 +1571,13 @@
"cascade_desc_rematch_2_lvls",
"cascade_desc_rematch_2_lvls_2_relations",
"cascade_desc_topological",
"cascade_after_recycled_parent_change"
"cascade_after_recycled_parent_change",
"invalid_cascade_for_uncached",
"invalid_cascade_for_first",
"invalid_cascade_for_second",
"invalid_desc_without_cascade",
"invalid_desc_for_first",
"invalid_desc_for_second"
]
}, {
"id": "Cached",
Expand Down
Loading

0 comments on commit d4be81c

Please sign in to comment.