mirror of
https://github.com/catseye/SixtyPical.git
synced 2025-02-09 16:31:42 +00:00
Merge pull request #17 from catseye/inconsistent-initialization
Drop check for inconsistent initialization
This commit is contained in:
commit
cfca03ed7e
@ -4,6 +4,9 @@ History of SixtyPical
|
||||
0.18
|
||||
----
|
||||
|
||||
* The "consistent initialization" check inside `if` blocks has
|
||||
been dropped. If a location is initialized inside one block
|
||||
but not the other, it is treated as uninitialized afterwards.
|
||||
* Syntactically, `goto` may only appear at the end of a block.
|
||||
It need no longer be the final instruction in a routine,
|
||||
as long as the type context is consistent at every exit.
|
||||
|
9
TODO.md
9
TODO.md
@ -44,15 +44,6 @@ buffer; and the ones you establish must be disjoint.
|
||||
An alternative would be `static` pointers, which are currently not possible because
|
||||
pointers must be zero-page, thus `@`, thus uninitialized.
|
||||
|
||||
### Question "consistent initialization"
|
||||
|
||||
Question the value of the "consistent initialization" principle for `if` statement analysis.
|
||||
|
||||
Part of this is the trashes at the end; I think what it should be is that the trashes
|
||||
after the `if` is the union of the trashes in each of the branches; this would obviate the
|
||||
need to `trash` values explicitly, but if you tried to access them afterwards, it would still
|
||||
error.
|
||||
|
||||
### Tail-call optimization
|
||||
|
||||
If a block ends in a `call` can that be converted to end in a `goto`? Why not? I think it can,
|
||||
|
@ -311,14 +311,6 @@ define player_logic logic_routine
|
||||
ld a, 1
|
||||
st a, player_died
|
||||
}
|
||||
|
||||
// FIXME these trashes, strictly speaking, probably shouldn't be needed,
|
||||
// but currently the compiler cares a little too much about values that are
|
||||
// initialized in one branch of an `if`, but not the other, but are trashed
|
||||
// at the end of the routine anyway.
|
||||
trash ptr
|
||||
trash y
|
||||
trash v
|
||||
}
|
||||
}
|
||||
|
||||
@ -355,13 +347,6 @@ define enemy_logic logic_routine
|
||||
add ptr, pos
|
||||
copy 82, [ptr] + y
|
||||
}
|
||||
|
||||
// FIXME these trashes, strictly speaking, probably shouldn't be needed,
|
||||
// but currently the compiler cares too much about values that are
|
||||
// initialized in one branch of an `if`, but not the other, but trashed
|
||||
// at the end of the routine anyway.
|
||||
trash ptr
|
||||
trash y
|
||||
} else {
|
||||
copy delta, compare_target
|
||||
st on, c
|
||||
|
@ -411,6 +411,8 @@ class Analyzer(object):
|
||||
|
||||
self.analyze_block(routine.block, context)
|
||||
|
||||
trashed = set(context.each_touched()) - set(context.each_meaningful())
|
||||
|
||||
if self.debug:
|
||||
print("at end of routine `{}`:".format(routine.name))
|
||||
print(context)
|
||||
@ -438,8 +440,6 @@ class Analyzer(object):
|
||||
raise InconsistentExitError("Exit contexts are not consistent")
|
||||
context.update_from(exit_context)
|
||||
|
||||
trashed = set(context.each_touched()) - set(context.each_meaningful())
|
||||
|
||||
# these all apply whether we encountered goto(s) in this routine, or not...:
|
||||
|
||||
# can't trash an output.
|
||||
@ -801,24 +801,6 @@ class Analyzer(object):
|
||||
outgoing_meaningful = set(context1.each_meaningful()) & set(context2.each_meaningful())
|
||||
outgoing_trashes = incoming_meaningful - outgoing_meaningful
|
||||
|
||||
# TODO may we need to deal with touched separately here too?
|
||||
# probably not; if it wasn't meaningful in the first place, it
|
||||
# doesn't really matter if you modified it or not, coming out.
|
||||
for ref in context1.each_meaningful():
|
||||
if ref in outgoing_trashes:
|
||||
continue
|
||||
context2.assert_meaningful(
|
||||
ref, exception_class=InconsistentInitializationError,
|
||||
message='initialized in block 1 but not in block 2 of `if {}`'.format(instr.src)
|
||||
)
|
||||
for ref in context2.each_meaningful():
|
||||
if ref in outgoing_trashes:
|
||||
continue
|
||||
context1.assert_meaningful(
|
||||
ref, exception_class=InconsistentInitializationError,
|
||||
message='initialized in block 2 but not in block 1 of `if {}`'.format(instr.src)
|
||||
)
|
||||
|
||||
# merge the contexts.
|
||||
|
||||
# first, the easy case: if one of the contexts has terminated, just use the other one.
|
||||
|
@ -1629,7 +1629,9 @@ Both blocks of an `if` are analyzed.
|
||||
| }
|
||||
= ok
|
||||
|
||||
If a location is initialized in one block, is must be initialized in the other as well.
|
||||
If a location is initialized in one block, it must be initialized in the other as well
|
||||
in order to be considered to be initialized after the block. If it is not consistent,
|
||||
it will be considered uninitialized.
|
||||
|
||||
| define foo routine
|
||||
| inputs a
|
||||
@ -1643,7 +1645,7 @@ If a location is initialized in one block, is must be initialized in the other a
|
||||
| ld a, 23
|
||||
| }
|
||||
| }
|
||||
? InconsistentInitializationError: x
|
||||
? UnmeaningfulOutputError: x
|
||||
|
||||
| define foo routine
|
||||
| inputs a
|
||||
@ -1657,7 +1659,7 @@ If a location is initialized in one block, is must be initialized in the other a
|
||||
| ld x, 7
|
||||
| }
|
||||
| }
|
||||
? InconsistentInitializationError: x
|
||||
? UnmeaningfulOutputError: x
|
||||
|
||||
| define foo routine
|
||||
| inputs a
|
||||
@ -1671,7 +1673,53 @@ If a location is initialized in one block, is must be initialized in the other a
|
||||
| ld x, 7
|
||||
| }
|
||||
| }
|
||||
? InconsistentInitializationError: x
|
||||
? UnmeaningfulOutputError: x
|
||||
|
||||
| define foo routine
|
||||
| inputs a
|
||||
| trashes a, x, z, n, c
|
||||
| {
|
||||
| cmp a, 42
|
||||
| if not z {
|
||||
| ld a, 6
|
||||
| } else {
|
||||
| ld x, 7
|
||||
| }
|
||||
| ld a, x
|
||||
| }
|
||||
? UnmeaningfulReadError: x
|
||||
|
||||
If we don't care if it's uninitialized after the `if`, that's okay then.
|
||||
|
||||
| define foo routine
|
||||
| inputs a
|
||||
| trashes a, x, z, n, c
|
||||
| {
|
||||
| cmp a, 42
|
||||
| if not z {
|
||||
| ld a, 6
|
||||
| } else {
|
||||
| ld x, 7
|
||||
| }
|
||||
| }
|
||||
= ok
|
||||
|
||||
Or, if it does get initialized on both branches, that's okay then.
|
||||
|
||||
| define foo routine
|
||||
| inputs a
|
||||
| outputs x
|
||||
| trashes a, z, n, c
|
||||
| {
|
||||
| cmp a, 42
|
||||
| if not z {
|
||||
| ld x, 0
|
||||
| ld a, 6
|
||||
| } else {
|
||||
| ld x, 7
|
||||
| }
|
||||
| }
|
||||
= ok
|
||||
|
||||
However, this only pertains to initialization. If a value is already
|
||||
initialized, either because it was set previous to the `if`, or is an
|
||||
@ -1720,7 +1768,7 @@ An `if` with a single block is analyzed as if it had an empty `else` block.
|
||||
| ld x, 7
|
||||
| }
|
||||
| }
|
||||
? InconsistentInitializationError: x
|
||||
? UnmeaningfulOutputError: x
|
||||
|
||||
| define foo routine
|
||||
| inputs a
|
||||
|
Loading…
x
Reference in New Issue
Block a user