This instance will be upgraded to Heptapod 0.25.0 (final) on 2021-09-22 at 15:00 UTC+2 (a few minutes of down time)

Commit 90b0e9ae authored by Jesse McGrew's avatar Jesse McGrew
Browse files

Require clauses in Z-code COND statements to be either FALSEs or non-empty

LISTs. This fixes an issue where omitting the parens around a clause would cause
the FORMs inside to be treated as individual clauses (as if they were lists),
producing unexpected behavior and confusing warnings.

Note that clauses undergo macro expansion first, so a FORM can still appear
where a clause is expected, as long as it's a macro invocation expanding to a
FALSE or non-empty LIST.

[ZILF-212]
parent dda862935f60
......@@ -346,46 +346,46 @@ namespace Zilf.Compiler
switch (clause)
{
case ZilFalse _:
case ZilFalse:
// previously, FALSE was only allowed when returned by a macro call, but now we expand macros before generating any code
continue;
case ZilListoidBase list when list.IsEmpty:
default:
throw new CompilerError(CompilerMessages.All_Clauses_In_0_Must_Be_Lists, "COND");
case ZilListoidBase list:
case ZilListoidBase { IsEmpty: false, StdTypeAtom: StdAtom.LIST } list:
(origCondition, body) = list;
condition = origCondition.Unwrap(Context);
break;
default:
throw new CompilerError(CompilerMessages.All_Clauses_In_0_Must_Be_Lists, "COND");
}
// if condition is always true (i.e. not a FORM or a FALSE), this is the "else" part
switch (condition)
{
case ZilForm _:
case ZilForm:
// must be evaluated
MarkSequencePoint(rb, condition);
CompileCondition(rb, condition, condition.SourceLine, nextLabel, false);
break;
case ZilFalse _:
case ZilFalse:
// never true
if (!(origCondition is ZilMacroResult))
if (origCondition is not ZilMacroResult)
{
Context.HandleError(new CompilerError(condition, CompilerMessages._0_Condition_Is_Always_1,
"COND", "false"));
}
continue;
case ZilAtom atom when atom.StdAtom == StdAtom.T || atom.StdAtom == StdAtom.ELSE:
case ZilAtom { StdAtom: StdAtom.T or StdAtom.ELSE }:
// non-shady else part
elsePart = true;
break;
default:
// shady else part (always true, but not T or ELSE)
Context.HandleError(new CompilerError(condition, CompilerMessages._0_Condition_Is_Always_1, "COND", "true"));
Context.HandleError(new CompilerError(condition, CompilerMessages._0_Condition_Is_Always_1,
"COND", "true"));
elsePart = true;
break;
}
......@@ -453,7 +453,7 @@ namespace Zilf.Compiler
wantThisResult ? resultStorage : null);
break;
case ZilList _:
case ZilList:
throw new CompilerError(stmt,
CompilerMessages.Expressions_Of_Type_0_Cannot_Be_Compiled,
stmt.GetTypeAtom(Context))
......@@ -582,7 +582,7 @@ namespace Zilf.Compiler
match = ((ZilObject)form.Eval(Context)).IsTrue;
break;
case ZilAtom atom when atom.StdAtom != StdAtom.ELSE && atom.StdAtom != StdAtom.T:
case ZilAtom { StdAtom: not (StdAtom.ELSE or StdAtom.T) } atom:
shadyElseAtom = atom;
goto default;
......
......@@ -399,6 +399,14 @@ namespace Zilf.Tests.Integration
.OutputsAsync("2");
}
[TestMethod]
public async Task COND_Should_Reject_Non_Macro_Forms()
{
await AssertRoutine("\"AUX\" FOO",
"<COND <SET FOO 123> (<=? .FOO 123> <PRINTN 456>)>")
.DoesNotCompileAsync("ZIL0100");
}
[TestMethod]
public async Task Constants_In_COND_Clause_Should_Only_Be_Stored_If_At_End()
{
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment