Skip to content

Commit

Permalink
don't memoize comptime fn calls that access comptime mutable state
Browse files Browse the repository at this point in the history
closes #827
  • Loading branch information
andrewrk committed Mar 12, 2018
1 parent 1bf2810 commit 5834ff0
Show file tree
Hide file tree
Showing 3 changed files with 84 additions and 19 deletions.
7 changes: 0 additions & 7 deletions src/all_types.hpp
Expand Up @@ -1174,13 +1174,6 @@ struct TypeTableEntry {
bool is_copyable;
bool gen_h_loop_flag;

// This is denormalized data. The simplest type that has this
// flag set to true is a mutable pointer. A const pointer has
// the same value for this flag as the child type.
// If a struct has any fields that have this flag true, then
// the flag is true for the struct.
bool can_mutate_state_through_it;

union {
TypeTableEntryPointer pointer;
TypeTableEntryInt integral;
Expand Down
79 changes: 67 additions & 12 deletions src/analyze.cpp
Expand Up @@ -398,7 +398,6 @@ TypeTableEntry *get_pointer_to_type_extra(CodeGen *g, TypeTableEntry *child_type

TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdPointer);
entry->is_copyable = true;
entry->can_mutate_state_through_it = is_const ? child_type->can_mutate_state_through_it : true;

const char *const_str = is_const ? "const " : "";
const char *volatile_str = is_volatile ? "volatile " : "";
Expand Down Expand Up @@ -483,7 +482,6 @@ TypeTableEntry *get_maybe_type(CodeGen *g, TypeTableEntry *child_type) {
assert(child_type->type_ref || child_type->zero_bits);
assert(child_type->di_type);
entry->is_copyable = type_is_copyable(g, child_type);
entry->can_mutate_state_through_it = child_type->can_mutate_state_through_it;

buf_resize(&entry->name, 0);
buf_appendf(&entry->name, "?%s", buf_ptr(&child_type->name));
Expand Down Expand Up @@ -574,7 +572,6 @@ TypeTableEntry *get_error_union_type(CodeGen *g, TypeTableEntry *err_set_type, T
entry->is_copyable = true;
assert(payload_type->di_type);
ensure_complete_type(g, payload_type);
entry->can_mutate_state_through_it = payload_type->can_mutate_state_through_it;

buf_resize(&entry->name, 0);
buf_appendf(&entry->name, "%s!%s", buf_ptr(&err_set_type->name), buf_ptr(&payload_type->name));
Expand Down Expand Up @@ -733,7 +730,6 @@ TypeTableEntry *get_slice_type(CodeGen *g, TypeTableEntry *ptr_type) {

TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdStruct);
entry->is_copyable = true;
entry->can_mutate_state_through_it = ptr_type->can_mutate_state_through_it;

// replace the & with [] to go from a ptr type name to a slice type name
buf_resize(&entry->name, 0);
Expand Down Expand Up @@ -1739,8 +1735,6 @@ TypeTableEntry *get_struct_type(CodeGen *g, const char *type_name, const char *f
struct_type->data.structure.gen_field_count += 1;
} else {
field->gen_index = SIZE_MAX;
struct_type->can_mutate_state_through_it = struct_type->can_mutate_state_through_it ||
field->type_entry->can_mutate_state_through_it;
}

auto prev_entry = struct_type->data.structure.fields_by_name.put_unique(field->name, field);
Expand Down Expand Up @@ -2481,9 +2475,6 @@ static void resolve_struct_zero_bits(CodeGen *g, TypeTableEntry *struct_type) {
if (!type_has_bits(field_type))
continue;

struct_type->can_mutate_state_through_it = struct_type->can_mutate_state_through_it ||
field_type->can_mutate_state_through_it;

if (gen_field_index == 0) {
if (struct_type->data.structure.layout == ContainerLayoutPacked) {
struct_type->data.structure.abi_alignment = 1;
Expand Down Expand Up @@ -2671,8 +2662,6 @@ static void resolve_union_zero_bits(CodeGen *g, TypeTableEntry *union_type) {
}
}
union_field->type_entry = field_type;
union_type->can_mutate_state_through_it = union_type->can_mutate_state_through_it ||
field_type->can_mutate_state_through_it;

if (field_node->data.struct_field.value != nullptr && !decl_node->data.container_decl.auto_enum) {
ErrorMsg *msg = add_node_error(g, field_node->data.struct_field.value,
Expand Down Expand Up @@ -4576,11 +4565,77 @@ bool generic_fn_type_id_eql(GenericFnTypeId *a, GenericFnTypeId *b) {
return true;
}

static bool can_mutate_comptime_var_state(ConstExprValue *value) {
assert(value != nullptr);
switch (value->type->id) {
case TypeTableEntryIdInvalid:
zig_unreachable();
case TypeTableEntryIdMetaType:
case TypeTableEntryIdVoid:
case TypeTableEntryIdBool:
case TypeTableEntryIdUnreachable:
case TypeTableEntryIdInt:
case TypeTableEntryIdFloat:
case TypeTableEntryIdNumLitFloat:
case TypeTableEntryIdNumLitInt:
case TypeTableEntryIdUndefLit:
case TypeTableEntryIdNullLit:
case TypeTableEntryIdNamespace:
case TypeTableEntryIdBoundFn:
case TypeTableEntryIdFn:
case TypeTableEntryIdBlock:
case TypeTableEntryIdOpaque:
case TypeTableEntryIdPromise:
case TypeTableEntryIdErrorSet:
case TypeTableEntryIdEnum:
return false;

case TypeTableEntryIdPointer:
return value->data.x_ptr.mut == ConstPtrMutComptimeVar;

case TypeTableEntryIdArray:
if (value->type->data.array.len == 0)
return false;
if (value->data.x_array.special == ConstArraySpecialUndef)
return false;
for (uint32_t i = 0; i < value->type->data.array.len; i += 1) {
if (can_mutate_comptime_var_state(&value->data.x_array.s_none.elements[i]))
return true;
}
return false;

case TypeTableEntryIdStruct:
for (uint32_t i = 0; i < value->type->data.structure.src_field_count; i += 1) {
if (can_mutate_comptime_var_state(&value->data.x_struct.fields[i]))
return true;
}
return false;

case TypeTableEntryIdMaybe:
if (value->data.x_maybe == nullptr)
return false;
return can_mutate_comptime_var_state(value->data.x_maybe);

case TypeTableEntryIdErrorUnion:
if (value->data.x_err_union.err != nullptr)
return false;
assert(value->data.x_err_union.payload != nullptr);
return can_mutate_comptime_var_state(value->data.x_err_union.payload);

case TypeTableEntryIdUnion:
return can_mutate_comptime_var_state(value->data.x_union.payload);

case TypeTableEntryIdArgTuple:
zig_panic("TODO var args at comptime is currently not supported");
}
zig_unreachable();
}

bool fn_eval_cacheable(Scope *scope) {
while (scope) {
if (scope->id == ScopeIdVarDecl) {
ScopeVarDecl *var_scope = (ScopeVarDecl *)scope;
if (var_scope->var->value->type->can_mutate_state_through_it)
if (can_mutate_comptime_var_state(var_scope->var->value))
return false;
} else if (scope->id == ScopeIdFnDef) {
return true;
Expand Down
17 changes: 17 additions & 0 deletions test/cases/eval.zig
Expand Up @@ -486,3 +486,20 @@ test "comptime slice of pointer preserves comptime var" {
assert(buff[0..][0..][0] == 1);
}
}

const SingleFieldStruct = struct {
x: i32,

fn read_x(self: &const SingleFieldStruct) i32 {
return self.x;
}
};
test "const ptr to comptime mutable data is not memoized" {

comptime {
var foo = SingleFieldStruct {.x = 1};
assert(foo.read_x() == 1);
foo.x = 2;
assert(foo.read_x() == 2);
}
}

0 comments on commit 5834ff0

Please sign in to comment.