diff options
author | Kartik K. Agaram <vc@akkartik.com> | 2015-10-01 13:43:32 -0700 |
---|---|---|
committer | Kartik K. Agaram <vc@akkartik.com> | 2015-10-01 13:43:32 -0700 |
commit | 900e6a0efe72ffb1fa4ab098689493f408a060a8 (patch) | |
tree | ec3e6eb3e7b66dc9f2ba95a5d7e5d7d5732e8aa5 | |
parent | 4814bf94e75ffdcbd2a4093eb1ab67851980a37a (diff) | |
download | mu-900e6a0efe72ffb1fa4ab098689493f408a060a8.tar.gz |
2227 - offset-checking for containers
-rw-r--r-- | 021check_instruction.cc | 1 | ||||
-rw-r--r-- | 030container.cc | 91 | ||||
-rw-r--r-- | 031address.cc | 8 | ||||
-rw-r--r-- | edit/010-warnings.mu | 30 |
4 files changed, 88 insertions, 42 deletions
diff --git a/021check_instruction.cc b/021check_instruction.cc index cb332e7d..32317348 100644 --- a/021check_instruction.cc +++ b/021check_instruction.cc @@ -7,7 +7,6 @@ :(code) void check_instruction(const recipe_ordinal r) { - if (Trace_stream && trace_count("warn") > 0) return; map<string, vector<type_ordinal> > metadata; for (long long int i = 0; i < SIZE(Recipe[r].steps); ++i) { instruction& inst = Recipe[r].steps.at(i); diff --git a/030container.cc b/030container.cc index 142688eb..89ba58de 100644 --- a/030container.cc +++ b/030container.cc @@ -26,13 +26,13 @@ recipe main [ +mem: storing 34 in location 3 +mem: storing 35 in location 4 -//: trying to copy to a differently-sized destination will fail +//: trying to copy to a differently-typed destination will fail :(scenario copy_checks_size) % Hide_warnings = true; recipe main [ 2:point <- copy 1:number ] -+warn: main: size mismatch in storing to 2:point (2 vs 1) at '2:point <- copy 1:number' ++warn: main: can't copy 1:number to 2:point; types don't match :(before "End Mu Types Initialization") // A more complex container, containing another container as one of its @@ -122,38 +122,50 @@ recipe main [ GET, :(before "End Primitive Recipe Numbers") Recipe_ordinal["get"] = GET; -:(before "End Primitive Recipe Implementations") +:(before "End Primitive Recipe Checks") case GET: { - if (SIZE(ingredients) != 2) { - raise << maybe(current_recipe_name()) << "'get' expects exactly 2 ingredients in '" << current_instruction().to_string() << "'\n" << end(); + if (SIZE(inst.ingredients) != 2) { + raise << maybe(Recipe[r].name) << "'get' expects exactly 2 ingredients in '" << inst.to_string() << "'\n" << end(); + break; + } + reagent base = inst.ingredients.at(0); + // Update GET base in Check + if (base.types.empty() || Type[base.types.at(0)].kind != container) { + raise << maybe(Recipe[r].name) << "first ingredient of 'get' should be a container, but got " << inst.ingredients.at(0).original_string << '\n' << end(); + break; + } + type_ordinal base_type = base.types.at(0); + reagent offset = inst.ingredients.at(1); + if (!is_literal(offset) || !is_mu_scalar(offset)) { + raise << maybe(Recipe[r].name) << "second ingredient of 'get' should have type 'offset', but got " << inst.ingredients.at(1).original_string << '\n' << end(); break; } + if (is_integer(offset.name)) { // later layers permit non-integer offsets + long long int offset_value = to_integer(offset.name); + if (offset_value < 0 || offset_value >= SIZE(Type[base_type].elements)) { + raise << maybe(Recipe[r].name) << "invalid offset " << offset_value << " for " << Type[base_type].name << '\n' << end(); + break; + } + } + break; +} +:(before "End Primitive Recipe Implementations") +case GET: { reagent base = current_instruction().ingredients.at(0); + // Update GET base in Run long long int base_address = base.value; if (base_address == 0) { raise << maybe(current_recipe_name()) << "tried to access location 0 in '" << current_instruction().to_string() << "'\n" << end(); break; } - if (base.types.empty() || Type[base.types.at(0)].kind != container) { - raise << current_recipe_name () << ": first ingredient of 'get' should be a container, but got " << current_instruction().ingredients.at(0).original_string << '\n' << end(); - break; - } type_ordinal base_type = base.types.at(0); - if (!is_literal(current_instruction().ingredients.at(1))) { - raise << maybe(current_recipe_name()) << "second ingredient of 'get' should have type 'offset', but got " << current_instruction().ingredients.at(1).original_string << '\n' << end(); - break; - } - assert(scalar(ingredients.at(1))); long long int offset = ingredients.at(1).at(0); + if (offset < 0 || offset >= SIZE(Type[base_type].elements)) break; long long int src = base_address; for (long long int i = 0; i < offset; ++i) { src += size_of(Type[base_type].elements.at(i)); } trace(Primitive_recipe_depth, "run") << "address to copy is " << src << end(); - if (offset < 0 || offset >= SIZE(Type[base_type].elements)) { - raise << maybe(current_recipe_name()) << "invalid offset " << offset << " for " << Type[base_type].name << '\n' << end(); - break; - } type_ordinal src_type = Type[base_type].elements.at(offset).at(0); trace(Primitive_recipe_depth, "run") << "its type is " << Type[src_type].name << end(); reagent tmp; @@ -206,29 +218,44 @@ recipe main [ GET_ADDRESS, :(before "End Primitive Recipe Numbers") Recipe_ordinal["get-address"] = GET_ADDRESS; -:(before "End Primitive Recipe Implementations") +:(before "End Primitive Recipe Checks") case GET_ADDRESS: { - reagent base = current_instruction().ingredients.at(0); - long long int base_address = base.value; - if (base_address == 0) { - raise << maybe(current_recipe_name()) << "tried to access location 0 in '" << current_instruction().to_string() << "'\n" << end(); + if (SIZE(inst.ingredients) != 2) { + raise << maybe(Recipe[r].name) << "'get-address' expects exactly 2 ingredients in '" << inst.to_string() << "'\n" << end(); break; } + reagent base = inst.ingredients.at(0); + // Update GET_ADDRESS base in Check if (base.types.empty() || Type[base.types.at(0)].kind != container) { - raise << current_recipe_name () << ": first ingredient of 'get-address' should be a container, but got " << current_instruction().ingredients.at(0).original_string << '\n' << end(); + raise << maybe(Recipe[r].name) << "first ingredient of 'get-address' should be a container, but got " << inst.ingredients.at(0).original_string << '\n' << end(); break; } type_ordinal base_type = base.types.at(0); - if (!is_literal(current_instruction().ingredients.at(1))) { - raise << maybe(current_recipe_name()) << "second ingredient of 'get-address' should have type 'offset', but got " << current_instruction().ingredients.at(1).original_string << '\n' << end(); + reagent offset = inst.ingredients.at(1); + if (!is_literal(offset) || !is_mu_scalar(offset)) { + raise << maybe(Recipe[r].name) << "second ingredient of 'get' should have type 'offset', but got " << inst.ingredients.at(1).original_string << '\n' << end(); break; } - assert(scalar(ingredients.at(1))); - long long int offset = ingredients.at(1).at(0); - if (offset < 0 || offset >= SIZE(Type[base_type].elements)) { - raise << "invalid offset " << offset << " for " << Type[base_type].name << '\n' << end(); + if (is_integer(offset.name)) { // later layers permit non-integer offsets + long long int offset_value = to_integer(offset.name); + if (offset_value < 0 || offset_value >= SIZE(Type[base_type].elements)) { + raise << maybe(Recipe[r].name) << "invalid offset " << offset_value << " for " << Type[base_type].name << '\n' << end(); + break; + } + } + break; +} +:(before "End Primitive Recipe Implementations") +case GET_ADDRESS: { + reagent base = current_instruction().ingredients.at(0); + // Update GET_ADDRESS base in Run + long long int base_address = base.value; + if (base_address == 0) { + raise << maybe(current_recipe_name()) << "tried to access location 0 in '" << current_instruction().to_string() << "'\n" << end(); break; } + type_ordinal base_type = base.types.at(0); + long long int offset = ingredients.at(1).at(0); long long int result = base_address; for (long long int i = 0; i < offset; ++i) { result += size_of(Type[base_type].elements.at(i)); @@ -247,7 +274,7 @@ recipe main [ 14:number <- copy 36 get-address 12:point-number/raw, 2:offset # point-number occupies 3 locations but has only 2 fields; out of bounds ] -+warn: invalid offset 2 for point-number ++warn: main: invalid offset 2 for point-number :(scenario get_address_out_of_bounds_2) % Hide_warnings = true; @@ -257,7 +284,7 @@ recipe main [ 14:number <- copy 36 get-address 12:point-number/raw, -1:offset ] -+warn: invalid offset -1 for point-number ++warn: main: invalid offset -1 for point-number //:: Allow containers to be defined in mu code. diff --git a/031address.cc b/031address.cc index c074fe76..b01bc036 100644 --- a/031address.cc +++ b/031address.cc @@ -139,7 +139,9 @@ recipe main [ ] +mem: storing 34 in location 4 -:(after "reagent base = " following "case GET:") +:(after "Update GET base in Check") +if (!canonize_type(base)) break; +:(after "Update GET base in Run") base = canonize(base); :(scenario get_address_indirect) @@ -152,7 +154,9 @@ recipe main [ ] +mem: storing 2 in location 4 -:(after "reagent base = " following "case GET_ADDRESS:") +:(after "Update GET_ADDRESS base in Check") +if (!canonize_type(base)) break; +:(after "Update GET_ADDRESS base in Run") base = canonize(base); //:: abbreviation for '/lookup': a prefix '*' diff --git a/edit/010-warnings.mu b/edit/010-warnings.mu index 10f05325..2bdcead2 100644 --- a/edit/010-warnings.mu +++ b/edit/010-warnings.mu @@ -97,6 +97,8 @@ recipe foo [ . get 123:number, foo:offset ┊ . .] ┊ . .foo: unknown element foo in container number ┊ . + .foo: first ingredient of 'get' should be a contai↩┊ . + .ner, but got 123:number ┊ . .┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┊ . . ┊ . ] @@ -107,6 +109,8 @@ recipe foo [ . . . . .foo: unknown element foo in container number . + .foo: first ingredient of 'get' should be a contai . + .ner, but got 123:number . . . ] ] @@ -133,6 +137,7 @@ recipe foo [ . x <- copy 0 ┊ . .] ┊ . .foo: missing type for x in 'x <- copy 0' ┊ . + .foo: can't copy 0 to x; types don't match ┊ . .┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┊ . . ┊ . ] @@ -184,14 +189,15 @@ recipe foo [ event-loop screen:address, console:address, 3:address:programming-environment-data ] screen-should-contain [ - . run (F4) . - . ┊ . + . errors found run (F4) . + . ┊foo . .recipe foo [ ┊━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━. - . x:address:point <- new point:type ┊ x. - . get x:address:point, 1:offset ┊foo . - .] ┊foo: first ingredient of 'get' should be a conta↩. - .┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┊iner, but got x:address:point . - . ┊━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━. + . x:address:point <- new point:type ┊ . + . get x:address:point, 1:offset ┊ . + .] ┊ . + .foo: first ingredient of 'get' should be a contai↩┊ . + .ner, but got x:address:point ┊ . + .┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┊ . . ┊ . ] ] @@ -223,6 +229,8 @@ recipe foo [ .] ┊ . .foo: expected ingredient 1 of 'get' to have type ↩┊ . .'offset'; got x:number ┊ . + .foo: second ingredient of 'get' should have type ↩┊ . + .'offset', but got x:number ┊ . .┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┊ . . ┊ . ] @@ -296,6 +304,8 @@ scenario run-instruction-and-print-warnings [ . ┊ x. . ┊get 1234:number, foo:offset . . ┊unknown element foo in container number . + . ┊first ingredient of 'get' should be a container,↩. + . ┊ but got 1234:number . . ┊━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━. . ┊ . ] @@ -316,6 +326,8 @@ scenario run-instruction-and-print-warnings [ . . . . . unknown element foo in container number . + . first ingredient of 'get' should be a container, . + . but got 1234:number . . . ] screen-should-contain-in-color 245/grey, [ @@ -325,6 +337,8 @@ scenario run-instruction-and-print-warnings [ . ┊ x. . ┊ . . ┊ . + . ┊ ↩. + . ┊ . . ┊━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━. . ┊ . ] @@ -354,6 +368,8 @@ scenario run-instruction-and-print-warnings-only-once [ . ┊ x. . ┊get 1234:number, foo:offset . . ┊unknown element foo in container number . + . ┊first ingredient of 'get' should be a container,↩. + . ┊ but got 1234:number . . ┊━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━. . ┊ . ] |