diff --git a/common/type_system/TypeSystem.cpp b/common/type_system/TypeSystem.cpp index 82c2b2ab94..fa630da465 100644 --- a/common/type_system/TypeSystem.cpp +++ b/common/type_system/TypeSystem.cpp @@ -453,6 +453,27 @@ Type* TypeSystem::lookup_type_allow_partial_def(const std::string& name) const { return result; } +/*! + * Get load size for a type. Will succeed if one of the two conditions is true: + * - Is a fully defined type. + * - Is partially defined, but structure is in the parent. + * This should be safe to use to load a value from a field. + */ +int TypeSystem::get_load_size_allow_partial_def(const TypeSpec& ts) const { + auto fully_defined_it = m_types.find(ts.base_type()); + if (fully_defined_it != m_types.end()) { + return fully_defined_it->second->get_load_size(); + } + + auto partial_def = lookup_type_allow_partial_def(ts); + if (!tc(TypeSpec("structure"), ts)) { + throw_typesystem_error("Cannot perform a load or store from partially defined type {}", + ts.print()); + } + assert(partial_def->get_load_size() == 4); + return partial_def->get_load_size(); +} + MethodInfo TypeSystem::declare_method(const std::string& type_name, const std::string& method_name, bool no_virtual, diff --git a/common/type_system/TypeSystem.h b/common/type_system/TypeSystem.h index f364237737..7a3c685e83 100644 --- a/common/type_system/TypeSystem.h +++ b/common/type_system/TypeSystem.h @@ -154,6 +154,8 @@ class TypeSystem { Type* lookup_type_allow_partial_def(const TypeSpec& ts) const; Type* lookup_type_allow_partial_def(const std::string& name) const; + int get_load_size_allow_partial_def(const TypeSpec& ts) const; + MethodInfo declare_method(const std::string& type_name, const std::string& method_name, bool no_virtual, diff --git a/goalc/compiler/compilation/Define.cpp b/goalc/compiler/compilation/Define.cpp index b8e1c838b3..c09f4748cd 100644 --- a/goalc/compiler/compilation/Define.cpp +++ b/goalc/compiler/compilation/Define.cpp @@ -237,17 +237,15 @@ Val* Compiler::do_set(const goos::Object& form, Val* dest, RegVal* src_in_reg, V // setting somewhere in memory auto base = as_mem_deref->base; auto base_as_mco = dynamic_cast(base); + int load_size = m_ts.get_load_size_allow_partial_def(as_mem_deref->type()); if (base_as_mco) { // if it is a constant offset, we can use a fancy x86-64 addressing mode to simplify - auto ti = m_ts.lookup_type(as_mem_deref->type()); - env->emit(std::make_unique( - src_in_reg, base_as_mco->offset, base_as_mco->base->to_gpr(env), ti->get_load_size())); + env->emit(std::make_unique(src_in_reg, base_as_mco->offset, + base_as_mco->base->to_gpr(env), load_size)); return src_in_reg; } else { // nope, the pointer to dereference is some complicated thing. - auto ti = m_ts.lookup_type(as_mem_deref->type()); - env->emit(std::make_unique(src_in_reg, 0, base->to_gpr(env), - ti->get_load_size())); + env->emit(std::make_unique(src_in_reg, 0, base->to_gpr(env), load_size)); return src_in_reg; } } else if (as_pair) { diff --git a/test/goalc/source_templates/with_game/test-partial-define-type-field.gc b/test/goalc/source_templates/with_game/test-partial-define-type-field.gc new file mode 100644 index 0000000000..c3ee1ef6be --- /dev/null +++ b/test/goalc/source_templates/with_game/test-partial-define-type-field.gc @@ -0,0 +1,10 @@ +(declare-type fake-type basic) +(deftype type-containing-fake-type (basic) + ((field fake-type)) + ) + +(let ((obj (new 'stack 'type-containing-fake-type))) + (-> obj field) + (set! (-> obj field) (the fake-type #f)) + (format #t "~A~%" (-> obj field)) + ) diff --git a/test/goalc/test_with_game.cpp b/test/goalc/test_with_game.cpp index 0cafc691c5..3db7e73a5d 100644 --- a/test/goalc/test_with_game.cpp +++ b/test/goalc/test_with_game.cpp @@ -545,6 +545,12 @@ TEST_F(WithGameTests, InlinedPackedBasics) { "0\n"}); } +TEST_F(WithGameTests, PartialDefineTypeField) { + runner.run_static_test(env, testCategory, "test-partial-define-type-field.gc", + {"#f\n" + "0\n"}); +} + // VECTOR FLOAT TESTS // ---- One off Tests