[Fix] (scripts): assert every field-ownership negative control against its OWN message - one of the eleven was a silent duplicate of another because the harness asked only whether something exited, and the harness now has a control of its own

This commit is contained in:
2026-09-11 14:53:39 -04:00
parent 97df377381
commit fb70704e74
+185 -48
View File
@@ -172,6 +172,9 @@ def parse_supplies_whole_field(text=None):
ROW_RE = re.compile(r"X\(\s*(\w+)\s*,\s*(\w+)\s*,\s*\"([^\"]*)\"\s*,\s*\"([^\"]*)\"\s*\)") ROW_RE = re.compile(r"X\(\s*(\w+)\s*,\s*(\w+)\s*,\s*\"([^\"]*)\"\s*,\s*\"([^\"]*)\"\s*\)")
ARG_ROW_RE = re.compile(r"X\(\s*(\w+)\s*,\s*(\d+)\s*,\s*(\w+)\s*,\s*\"([^\"]*)\"\s*\)") ARG_ROW_RE = re.compile(r"X\(\s*(\w+)\s*,\s*(\d+)\s*,\s*(\w+)\s*,\s*\"([^\"]*)\"\s*\)")
PAIR_RE = re.compile(r"X\(\s*(\w+)\s*,\s*(\w+)\s*\)") PAIR_RE = re.compile(r"X\(\s*(\w+)\s*,\s*(\w+)\s*\)")
# An exemption's reason may be one string or several adjacent ones, the way a long C literal is
# written; the row regex takes the first and the check only cares that there is one.
EXEMPT_RE = re.compile(r"X\(\s*(\w+)\s*,\s*\"([^\"]*)\"")
def parse_ownership(text=None): def parse_ownership(text=None):
@@ -180,38 +183,52 @@ def parse_ownership(text=None):
forwards = ROW_RE.findall(macro_block(text, "MGP_FIELD_OWNERSHIP_FORWARD_LIST")) forwards = ROW_RE.findall(macro_block(text, "MGP_FIELD_OWNERSHIP_FORWARD_LIST"))
args = ARG_ROW_RE.findall(macro_block(text, "MGP_FIELD_OWNERSHIP_ARG_LIST")) args = ARG_ROW_RE.findall(macro_block(text, "MGP_FIELD_OWNERSHIP_ARG_LIST"))
verb_ops = PAIR_RE.findall(macro_block(text, "MGP_VERB_OP_LIST")) verb_ops = PAIR_RE.findall(macro_block(text, "MGP_VERB_OP_LIST"))
exempt = EXEMPT_RE.findall(macro_block(text, "MGP_VERB_OP_EXEMPT_LIST"))
if not fields: if not fields:
sys.exit("gen_pipe_field_ownership: FieldOwnership.def's field list did not parse") sys.exit("gen_pipe_field_ownership: FieldOwnership.def's field list did not parse")
return fields, forwards, args, verb_ops return fields, forwards, args, verb_ops, exempt
def parse_ops_and_verbs(calls_text=None, fill_points_text=None): def parse_ops_and_verbs(calls_text=None, fill_points_text=None):
"""The two name spaces the stamp map joins, read from the files that define them: the """The two name spaces the stamp map joins, read from the files that define them: the
catalogue's calls (PipeCalls.def, which IS the MGPWireOp enum) and the verb set catalogue's calls (PipeCalls.def, which IS the MGPWireOp enum, with each call's KIND) and
(FillPoints.def, which IS MG_Backend::GLFunctionsTable's member list).""" the verb set (FillPoints.def, which IS MG_Backend::GLFunctionsTable's member list)."""
calls_text = read(PIPE_CALLS) if calls_text is None else calls_text calls_text = read(PIPE_CALLS) if calls_text is None else calls_text
fill_points_text = read(FILL_POINTS) if fill_points_text is None else fill_points_text fill_points_text = read(FILL_POINTS) if fill_points_text is None else fill_points_text
ops = re.findall(r"X\(\s*(\w+)\s*,\s*\w+\s*,\s*\w+\s*,", calls = re.findall(r"X\(\s*(\w+)\s*,\s*\w+\s*,\s*(k\w+)\s*,",
macro_block(calls_text, "MGP_CALL_LIST")) macro_block(calls_text, "MGP_CALL_LIST"))
verbs = re.findall(r"X\(\s*(\w+)\s*,\s*(k\w+)\s*\)", verbs = re.findall(r"X\(\s*(\w+)\s*,\s*(k\w+)\s*\)",
macro_block(fill_points_text, "MGP_FILL_VERB_LIST")) macro_block(fill_points_text, "MGP_FILL_VERB_LIST"))
if not ops: if not calls:
sys.exit("gen_pipe_field_ownership: PipeCalls.def's call list did not parse") sys.exit("gen_pipe_field_ownership: PipeCalls.def's call list did not parse")
if not verbs: if not verbs:
sys.exit("gen_pipe_field_ownership: FillPoints.def's verb list did not parse") sys.exit("gen_pipe_field_ownership: FillPoints.def's verb list did not parse")
return ops, [v for v, _ in verbs] return calls, [v for v, _ in verbs]
def check_verb_ops(verb_ops, ops, verbs): def verb_shaped_calls(calls, verbs):
"""The stamp map, against both name spaces. A row whose op or verb does not exist would """The calls that MUST have a stamp row or an exemption, by two derived tests: the
otherwise become a switch arm that fails to compile minutes later - or, worse, a silently catalogue's own kind (kCtxVerb) and a name that is also a verb's. Neither can see a call
absent stamp point.""" that is a verb boundary, is not kCtxVerb and is renamed - FieldOwnership.def names the one
such call in the tree and says the phase that emits it writes its row."""
verbSet = set(verbs)
return [name for name, kind in calls if kind == "kCtxVerb" or name in verbSet]
def check_verb_ops(verb_ops, exempt, calls, verbs):
"""The stamp map, in BOTH directions.
A row whose op or verb does not exist would become a switch arm that fails to compile. An
OMITTED row is worse and is what this check exists for: the applier would run the record
under the PREVIOUS verb's serial, mask and name, so a field inside that mask reads FRESH
while holding the previous verb's value - the one failure in this package that is silent."""
opNames = [name for name, _ in calls]
if not verb_ops: if not verb_ops:
sys.exit("gen_pipe_field_ownership: MGP_VERB_OP_LIST is empty - the server would have " sys.exit("gen_pipe_field_ownership: MGP_VERB_OP_LIST is empty - the server would have "
"no verb boundary to stamp at and every field would read @<none>") "no verb boundary to stamp at and every field would read @<none>")
seen = set() seen = set()
for op, verb in verb_ops: for op, verb in verb_ops:
if op not in ops: if op not in opNames:
sys.exit("gen_pipe_field_ownership: the stamp map names op %s, which is not a call " sys.exit("gen_pipe_field_ownership: the stamp map names op %s, which is not a call "
"in PipeCalls.def" % op) "in PipeCalls.def" % op)
if verb not in verbs: if verb not in verbs:
@@ -220,7 +237,30 @@ def check_verb_ops(verb_ops, ops, verbs):
if op in seen: if op in seen:
sys.exit("gen_pipe_field_ownership: op %s has two stamp rows" % op) sys.exit("gen_pipe_field_ownership: op %s has two stamp rows" % op)
seen.add(op) seen.add(op)
return verb_ops
required = verb_shaped_calls(calls, verbs)
exemptMap = {}
for op, why in exempt:
if op not in opNames:
sys.exit("gen_pipe_field_ownership: the stamp map exempts op %s, which is not a call "
"in PipeCalls.def" % op)
if op not in required:
sys.exit("gen_pipe_field_ownership: op %s is exempted from the stamp map but is not "
"verb-shaped, so it was never required - an exemption that exempts nothing "
"reads as a decision that was made" % op)
if op in seen:
sys.exit("gen_pipe_field_ownership: op %s has both a stamp row and an exemption" % op)
if not why.strip():
sys.exit("gen_pipe_field_ownership: op %s is exempted with no reason" % op)
exemptMap[op] = why
missing = [op for op in required if op not in seen and op not in exemptMap]
if missing:
sys.exit("gen_pipe_field_ownership: %d verb-shaped call(s) have no stamp row and no "
"exemption: %s - an omitted stamp point is SILENT (the record would apply under "
"the previous verb's serial, mask and name), so it is a build failure here"
% (len(missing), ", ".join(missing)))
return verb_ops, exemptMap, required
def build(accessors, sticky, emitted, refused, rows, forwards, args): def build(accessors, sticky, emitted, refused, rows, forwards, args):
@@ -302,7 +342,8 @@ def build(accessors, sticky, emitted, refused, rows, forwards, args):
return ownership, phase, why, forward_map, arg_rows, counts return ownership, phase, why, forward_map, arg_rows, counts
def emit(accessors, sticky, ownership, phase, why, forward_map, arg_rows, counts, verb_ops): def emit(accessors, sticky, ownership, phase, why, forward_map, arg_rows, counts, verb_ops,
exemptMap):
out = [BANNER.format(name=OUT_NAME)] out = [BANNER.format(name=OUT_NAME)]
add = out.append add = out.append
add(""" add("""
@@ -412,9 +453,14 @@ static_assert(MGPipeEveryFieldIsClassified(),
add("""// WHERE THE SERVER STAMPS. The wire's op and the fill's verb are different name spaces add("""// WHERE THE SERVER STAMPS. The wire's op and the fill's verb are different name spaces
// and do not line up by name (draw_vbo is DrawArrays, blit is BlitFramebuffer), so this is the // and do not line up by name (draw_vbo is DrawArrays, blit is BlitFramebuffer), so this is the
// join. An op with no row is NOT a verb boundary and the applier must not stamp on it. // join. An op with no row is NOT a verb boundary and the applier must not stamp on it.
// Present is deliberately absent: FillPoints.def:21 - "Present and SetSwapInterval go through //
// BackendObject virtuals and read no frontend state, so they are not verbs here". // EVERY VERB-SHAPED CALL IS ANSWERED HERE OR EXEMPTED BY NAME, and the generator refuses an
constexpr MGPipeVerb MGPipeVerbForWireOp(MGPWireOp op) { // omission: a verb-shaped record with no row would apply under the PREVIOUS verb's serial,
// mask and name, so a field inside that mask would read FRESH while holding the previous
// verb's value - the one silent failure this table has. The exemptions:""")
for op in sorted(exemptMap):
add("// %-16s %s" % (op, exemptMap[op]))
add("""constexpr MGPipeVerb MGPipeVerbForWireOp(MGPWireOp op) {
switch (op) {""") switch (op) {""")
for op, verb in verb_ops: for op, verb in verb_ops:
add(" case MGPWireOp::%s: return MGPipeVerb::%s;" % (op, verb)) add(" case MGPWireOp::%s: return MGPipeVerb::%s;" % (op, verb))
@@ -424,6 +470,7 @@ constexpr MGPipeVerb MGPipeVerbForWireOp(MGPWireOp op) {
} }
""") """)
add("inline constexpr SizeT kMGPipeVerbBoundaryOpCount = %d;" % len(verb_ops)) add("inline constexpr SizeT kMGPipeVerbBoundaryOpCount = %d;" % len(verb_ops))
add("inline constexpr SizeT kMGPipeVerbBoundaryExemptCount = %d;" % len(exemptMap))
add("") add("")
add("// The class sizes, as constants a test can pin without recounting the table.") add("// The class sizes, as constants a test can pin without recounting the table.")
for cls in CLASSES: for cls in CLASSES:
@@ -445,14 +492,32 @@ def write(path, text, check_only, changed):
handle.write(text) handle.write(text)
def expect_trip(name, fn): def expect_trip(name, because, fn, quiet=False):
"""A control that must exit. Returns 1 when it did, and says so when it did not - a """A control that must exit FOR ITS OWN REASON.
silent pass here is the gate not checking anything."""
The first version of this function caught any SystemExit and asked nothing about which one,
and that is exactly how control #4 came to be a silent duplicate of control #1: its
replacement string was raw, so it mangled the row instead of blanking the phase, the row
stopped parsing, and the generator exited with "a field in NO class" while the report
counted a trip for "a BARRIER_PULLED row with no retiring phase". The guard that every debt
row names its retiring phase therefore had no control at all.
`because` is a substring the control's own message must contain. This is the discipline
gen_pipe_dirty_surface.py's self_test already uses (it asserts each control's problem
string) and it is the half that was dropped."""
try: try:
fn() fn()
except SystemExit: except SystemExit as exit:
return 1 message = str(exit.code) if exit.code is not None else ""
print("gen_pipe_field_ownership: self-test: control did NOT trip: %s" % name, file=sys.stderr) if because in message:
return 1
if not quiet:
print("gen_pipe_field_ownership: self-test: control %r tripped for SOMEONE ELSE'S "
"reason:\n expected to contain: %s\n actually said: %s"
% (name, because, message), file=sys.stderr)
return 0
if not quiet:
print("gen_pipe_field_ownership: self-test: control did NOT trip: %s" % name, file=sys.stderr)
return 0 return 0
@@ -465,13 +530,15 @@ def self_test():
accessors, sticky, emitted = parse_coverage(coverage) accessors, sticky, emitted = parse_coverage(coverage)
refused = parse_supplies_whole_field(fill) refused = parse_supplies_whole_field(fill)
ops, verbs = parse_ops_and_verbs() calls, verbs = parse_ops_and_verbs()
def run(own_text=None, cov=None, fill_text=None): def run(own_text=None, cov=None, fill_text=None, calls_text=None):
acc, stk, emt = parse_coverage(cov if cov is not None else coverage) acc, stk, emt = parse_coverage(cov if cov is not None else coverage)
ref = parse_supplies_whole_field(fill_text if fill_text is not None else fill) ref = parse_supplies_whole_field(fill_text if fill_text is not None else fill)
rows, fwd, args, verb_ops = parse_ownership(own_text if own_text is not None else ownership_text) rows, fwd, args, verb_ops, exempt = parse_ownership(
check_verb_ops(verb_ops, ops, verbs) own_text if own_text is not None else ownership_text)
c, v = parse_ops_and_verbs(calls_text) if calls_text is not None else (calls, verbs)
check_verb_ops(verb_ops, exempt, c, v)
return build(acc, stk, emt, ref, rows, fwd, args) return build(acc, stk, emt, ref, rows, fwd, args)
# The rows wrap over two lines with a trailing backslash, so every control below edits # The rows wrap over two lines with a trailing backslash, so every control below edits
@@ -487,11 +554,15 @@ def self_test():
"edit no longer matches the file it is supposed to break" % what) "edit no longer matches the file it is supposed to break" % what)
return edited return edited
# Every control below is (name, the substring its own message must contain, the edit). The
# substring is not decoration: see expect_trip.
# 1. THE HEADLINE CONTROL (exit gate E4): take one field out of the table. It is in no # 1. THE HEADLINE CONTROL (exit gate E4): take one field out of the table. It is in no
# class, and that is a build failure rather than a silent default. # class, and that is a build failure rather than a silent default.
dropped = edit(r"X\(GetActiveTextureUnit," + GAP + r"BARRIER_PULLED," + GAP + r"\"[^\"]*\"," dropped = edit(r"X\(GetActiveTextureUnit," + GAP + r"BARRIER_PULLED," + GAP + r"\"[^\"]*\","
+ GAP + r"\"[^\"]*\"\)", "", "remove GetActiveTextureUnit's row") + GAP + r"\"[^\"]*\"\)", "", "remove GetActiveTextureUnit's row")
controls = [("a field in NO class (GetActiveTextureUnit's row removed)", controls = [("a field in NO class (GetActiveTextureUnit's row removed)",
"field(s) in NO class",
lambda: run(own_text=dropped))] lambda: run(own_text=dropped))]
# 2. The other direction: a field the derivation already placed in RECORD_SUPPLIED, also # 2. The other direction: a field the derivation already placed in RECORD_SUPPLIED, also
@@ -500,41 +571,56 @@ def self_test():
"#define MGP_FIELD_OWNERSHIP_LIST(X)", "#define MGP_FIELD_OWNERSHIP_LIST(X)",
"#define MGP_FIELD_OWNERSHIP_LIST(X) X(GetClearColor, FATAL, \"-\", \"moved by hand\") \\\n", 1) "#define MGP_FIELD_OWNERSHIP_LIST(X) X(GetClearColor, FATAL, \"-\", \"moved by hand\") \\\n", 1)
controls.append(("a RECORD_SUPPLIED field claimed by hand (supplied -> FATAL)", controls.append(("a RECORD_SUPPLIED field claimed by hand (supplied -> FATAL)",
"GetClearColor is in TWO classes",
lambda: run(own_text=doubled))) lambda: run(own_text=doubled)))
# 3. A row naming something that is not a field at all. # 3. A row naming something that is not a field at all.
typo = edit(r"X\(GetActiveTextureUnit,", "X(GetActiveTextureUnitt,", "misspell a field name") typo = edit(r"X\(GetActiveTextureUnit,", "X(GetActiveTextureUnitt,", "misspell a field name")
controls.append(("a row naming a non-field", lambda: run(own_text=typo))) controls.append(("a row naming a non-field",
"GetActiveTextureUnitt, which is not a PipeInputs field",
lambda: run(own_text=typo)))
# 4. A BARRIER_PULLED row with no retiring phase: the debt is only sized if every row # 4. A BARRIER_PULLED row with no retiring phase: the debt is only sized if every row
# says who pays it, which is the whole of R-7.2's "rsp IS the size of the debt". # says who pays it, which is the whole of R-7.2's "rsp IS the size of the debt".
# THE REPLACEMENT IS NOT A RAW STRING. It was, and the backslashes survived into the
# substitution, mangled the row past ROW_RE's reach and made this control a silent
# duplicate of #1 for a whole round.
unphased = edit(r"(X\(GetActiveTextureUnit," + GAP + r"BARRIER_PULLED,)" + GAP + r"\"[^\"]*\",", unphased = edit(r"(X\(GetActiveTextureUnit," + GAP + r"BARRIER_PULLED,)" + GAP + r"\"[^\"]*\",",
r"\1 \"-\",", "blank a BARRIER_PULLED row's retiring phase") "\\1 \"-\",", "blank a BARRIER_PULLED row's retiring phase")
controls.append(("a BARRIER_PULLED row with no retiring phase", lambda: run(own_text=unphased))) controls.append(("a BARRIER_PULLED row with no retiring phase",
"GetActiveTextureUnit is BARRIER_PULLED and names no retiring phase",
lambda: run(own_text=unphased)))
# 5. A class that is not one of the four. # 5. A class that is not one of the four.
bogus = edit(r"(X\(GetActiveTextureUnit,)" + GAP + r"BARRIER_PULLED,", bogus = edit(r"(X\(GetActiveTextureUnit,)" + GAP + r"BARRIER_PULLED,",
r"\1 SOMEHOW_FINE,", "introduce a fifth class") "\\1 SOMEHOW_FINE,", "introduce a fifth class")
controls.append(("a fifth class", lambda: run(own_text=bogus))) controls.append(("a fifth class",
"GetActiveTextureUnit is in class SOMEHOW_FINE",
lambda: run(own_text=bogus)))
# 6. A sticky forward that lost its own row - the seven most dangerous fields are exactly # 6. A sticky forward that lost its own row - the seven most dangerous fields are exactly
# the ones a table without forward rows is blind to. # the ones a table without forward rows is blind to.
no_forward = edit(r"X\(RecordError," + GAP + r"BARRIER_PULLED," + GAP + r"\"P9\"," + GAP no_forward = edit(r"X\(RecordError," + GAP + r"BARRIER_PULLED," + GAP + r"\"P9\"," + GAP
+ r"\"OnGlError[^\"]*\"\)", "", "remove RecordError's forward row") + r"\"OnGlError[^\"]*\"\)", "", "remove RecordError's forward row")
controls.append(("a sticky forward with no row", lambda: run(own_text=no_forward))) controls.append(("a sticky forward with no row",
"sticky forward(s) with no row: RecordError",
lambda: run(own_text=no_forward)))
# 7. A forward row that disagrees with its field row. # 7. A forward row that disagrees with its field row.
disagree = edit(r"X\(GetTextureObject," + GAP + r"BARRIER_PULLED," + GAP + r"\"P7\"," + GAP disagree = edit(r"X\(GetTextureObject," + GAP + r"BARRIER_PULLED," + GAP + r"\"P7\"," + GAP
+ r"\"a server-side texture handle table\"\)", + r"\"a server-side texture handle table\"\)",
"X(GetTextureObject, FATAL, \"-\", \"a server-side texture handle table\")", "X(GetTextureObject, FATAL, \"-\", \"a server-side texture handle table\")",
"contradict GetTextureObject's field row") "contradict GetTextureObject's field row")
controls.append(("a forward row that contradicts its field row", lambda: run(own_text=disagree))) controls.append(("a forward row that contradicts its field row",
"field row says BARRIER_PULLED and its forward row says FATAL",
lambda: run(own_text=disagree)))
# 8. An argument exception that narrows nothing. # 8. An argument exception that narrows nothing.
same = edit(r"X\(GetPixelStoreParameters, 1, FATAL,", same = edit(r"X\(GetPixelStoreParameters, 1, FATAL,",
"X(GetPixelStoreParameters, 1, APPLIER_DERIVED,", "X(GetPixelStoreParameters, 1, APPLIER_DERIVED,",
"make the argument exception repeat the field's class") "make the argument exception repeat the field's class")
controls.append(("an argument exception that repeats the field's class", controls.append(("an argument exception that repeats the field's class",
"narrows nothing",
lambda: run(own_text=same))) lambda: run(own_text=same)))
# 9. THE DERIVATION'S OWN SOURCE. If EmittedCallSuppliesTheWholeField's refusals stop # 9. THE DERIVATION'S OWN SOURCE. If EmittedCallSuppliesTheWholeField's refusals stop
@@ -544,20 +630,66 @@ def self_test():
blinded = fill.replace("Bool EmittedCallSuppliesTheWholeField(MGPipeInputField field)", blinded = fill.replace("Bool EmittedCallSuppliesTheWholeField(MGPipeInputField field)",
"Bool EmittedCallSuppliesTheWholeFieldXX(MGPipeInputField field)", 1) "Bool EmittedCallSuppliesTheWholeFieldXX(MGPipeInputField field)", 1)
controls.append(("the derivation's source function renamed away", controls.append(("the derivation's source function renamed away",
"EmittedCallSuppliesTheWholeField is not in PipeFill.cpp",
lambda: run(fill_text=blinded))) lambda: run(fill_text=blinded)))
# 10. THE STAMP MAP against both name spaces. A row naming an op that is not a call, or a # 10-11. THE STAMP MAP against both name spaces.
# verb that is not a verb, would otherwise be an arm that fails to compile - or an
# absent stamp point, which is silent.
bad_op = edit(r"X\(Clear,\s*Clear\)", "X(Klear, Clear)", "misspell a stamp map op") bad_op = edit(r"X\(Clear,\s*Clear\)", "X(Klear, Clear)", "misspell a stamp map op")
controls.append(("a stamp row naming an op that does not exist", lambda: run(own_text=bad_op))) controls.append(("a stamp row naming an op that does not exist",
"names op Klear, which is not a call in PipeCalls.def",
lambda: run(own_text=bad_op)))
bad_verb = edit(r"X\(DrawVbo,\s*DrawArrays\)", "X(DrawVbo, DrawArrayz)", bad_verb = edit(r"X\(DrawVbo,\s*DrawArrays\)", "X(DrawVbo, DrawArrayz)",
"misspell a stamp map verb") "misspell a stamp map verb")
controls.append(("a stamp row naming a verb that does not exist", lambda: run(own_text=bad_verb))) controls.append(("a stamp row naming a verb that does not exist",
"names verb DrawArrayz, which is not a verb in FillPoints.def",
lambda: run(own_text=bad_verb)))
# 12. THE OMISSION, which the first version of this gate could not see at all. A
# verb-shaped call with neither a row nor an exemption is the one silent failure in
# this package: the record applies under the PREVIOUS verb's serial, mask and name.
no_row = edit(r"X\(GenerateMipmap,\s*GenerateMipmap\)", "", "remove GenerateMipmap's stamp row")
controls.append(("a verb-shaped call with no stamp row and no exemption",
"have no stamp row and no exemption: GenerateMipmap",
lambda: run(own_text=no_row)))
# 13. An exemption is a decision, so it may not be written for a call that was never
# required - that would read as a ruling where there was none.
idle = edit(r"#define MGP_VERB_OP_EXEMPT_LIST\(X\)",
"#define MGP_VERB_OP_EXEMPT_LIST(X) X(SetDynamicState, \"not verb-shaped\") \\\n",
"exempt a call that was never required")
controls.append(("an exemption for a call that is not verb-shaped",
"SetDynamicState is exempted from the stamp map but is not verb-shaped",
lambda: run(own_text=idle)))
# 14. An exemption with no reason is an absence with extra steps.
mute = edit(r"X\(Flush," + GAP + r"\"", "X(Flush, \"\" \"", "blank an exemption's reason")
controls.append(("an exemption with no reason",
"Flush is exempted with no reason",
lambda: run(own_text=mute)))
# 15. THE REQUIRED SET'S OWN SOURCE. verb_shaped_calls reads PipeCalls.def's KIND column; if
# that column stops parsing as kCtxVerb the required set collapses to the name matches
# alone and control 12 would pass for the wrong reason. Renaming the kind is the cheapest
# way to prove the kind is actually being read.
kindless = read(PIPE_CALLS).replace("kCtxVerb", "kCtxVerbb")
controls.append(("the catalogue's kCtxVerb kind renamed away",
"is exempted from the stamp map but is not verb-shaped",
lambda: run(calls_text=kindless)))
# THE HARNESS'S OWN CONTROL, and it is the durable form of how M-1 was found. The defect was
# not the escaping in control #4; it was that expect_trip asked "did something exit" rather
# than "did THIS exit", so a control could silently become a duplicate of another. Prove the
# harness can tell them apart: control #1's edit, asserted against control #4's reason, must
# be REJECTED. Without this line the stricter harness could itself rot back.
if expect_trip("(harness control) control #1's edit under control #4's reason",
"names no retiring phase", lambda: run(own_text=dropped), quiet=True) != 0:
sys.exit("gen_pipe_field_ownership: self-test: expect_trip accepted an exit that belongs "
"to another control - the harness cannot tell one control from another, which is "
"exactly the defect that let control #4 be a silent duplicate of control #1")
trips = 0 trips = 0
for name, fn in controls: for name, because, fn in controls:
trips += expect_trip(name, fn) trips += expect_trip(name, because, fn)
# The positive control: the real tables pass, and they partition the real field set. # The positive control: the real tables pass, and they partition the real field set.
_, _, _, _, _, counts = run() _, _, _, _, _, counts = run()
@@ -576,7 +708,8 @@ def self_test():
if trips != len(controls): if trips != len(controls):
sys.exit("gen_pipe_field_ownership: self-test: %d of %d negative controls did not trip" sys.exit("gen_pipe_field_ownership: self-test: %d of %d negative controls did not trip"
% (len(controls) - trips, len(controls))) % (len(controls) - trips, len(controls)))
print("gen_pipe_field_ownership: self-test: %d negative-control trip(s), positive control OK " print("gen_pipe_field_ownership: self-test: %d negative-control trip(s), each asserted against "
"its OWN message; harness control OK; positive control OK "
"(%d fields partitioned, 7 sticky forwards, 9 refusals)" % (trips, total)) "(%d fields partitioned, 7 sticky forwards, 9 refusals)" % (trips, total))
return 0 return 0
@@ -594,9 +727,9 @@ def main():
accessors, sticky, emitted = parse_coverage() accessors, sticky, emitted = parse_coverage()
refused = parse_supplies_whole_field() refused = parse_supplies_whole_field()
rows, forwards, arg_rows, verb_ops = parse_ownership() rows, forwards, arg_rows, verb_ops, exempt = parse_ownership()
ops, verbs = parse_ops_and_verbs() calls, verbs = parse_ops_and_verbs()
check_verb_ops(verb_ops, ops, verbs) _, exemptMap, required = check_verb_ops(verb_ops, exempt, calls, verbs)
ownership, phase, why, forward_map, arg_list, counts = build( ownership, phase, why, forward_map, arg_list, counts = build(
accessors, sticky, emitted, refused, rows, forwards, arg_rows) accessors, sticky, emitted, refused, rows, forwards, arg_rows)
@@ -604,15 +737,19 @@ def main():
os.makedirs(GENERATED_DIR) os.makedirs(GENERATED_DIR)
changed = [] changed = []
write(os.path.join(GENERATED_DIR, OUT_NAME), write(os.path.join(GENERATED_DIR, OUT_NAME),
emit(accessors, sticky, ownership, phase, why, forward_map, arg_list, counts, verb_ops), emit(accessors, sticky, ownership, phase, why, forward_map, arg_list, counts, verb_ops,
exemptMap),
args.check, changed) args.check, changed)
print("gen_pipe_field_ownership: %d fields + %d sticky forwards = %d rows; " print("gen_pipe_field_ownership: %d fields + %d sticky forwards = %d rows; "
"%d record-supplied (derived), %d applier-derived, %d barrier-pulled, %d fatal, " "%d record-supplied (derived), %d applier-derived, %d barrier-pulled, %d fatal, "
"%d argument exception(s), %d verb-boundary op(s)" "%d argument exception(s)"
% (len(accessors), len(sticky), len(accessors) + len(sticky), % (len(accessors), len(sticky), len(accessors) + len(sticky),
counts["RECORD_SUPPLIED"], counts["APPLIER_DERIVED"], counts["BARRIER_PULLED"], counts["RECORD_SUPPLIED"], counts["APPLIER_DERIVED"], counts["BARRIER_PULLED"],
counts["FATAL"], len(arg_list), len(verb_ops))) counts["FATAL"], len(arg_list)))
print("gen_pipe_field_ownership: stamp map: %d verb-shaped call(s) = %d row(s) + %d "
"exemption(s), 0 unanswered"
% (len(required), len(verb_ops), len(exemptMap)))
pulled = [(f, phase[f]) for f in accessors if ownership[f] == "BARRIER_PULLED"] pulled = [(f, phase[f]) for f in accessors if ownership[f] == "BARRIER_PULLED"]
print("gen_pipe_field_ownership: the debt, by retiring phase:") print("gen_pipe_field_ownership: the debt, by retiring phase:")
by_phase = {} by_phase = {}