From: Gaius Mulley Date: Fri, 15 Dec 2023 15:26:48 +0000 (+0000) Subject: PR modula2/112946 ICE assignment of string to enumeration or set X-Git-Tag: basepoints/gcc-15~3543 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=7d7a480eedf0a195318d0fce2c9c57acae43ec9d;p=thirdparty%2Fgcc.git PR modula2/112946 ICE assignment of string to enumeration or set This patch introduces type checking during FoldBecomes and also adds set/string/enum checking to the type checker. FoldBecomes has been re-written, tidied up and re-factored. gcc/m2/ChangeLog: PR modula2/112946 * gm2-compiler/M2Check.mod (checkConstMeta): New procedure function. (checkConstEquivalence): New procedure function. (doCheckPair): Add call to checkConstEquivalence. * gm2-compiler/M2GenGCC.mod (ResolveConstantExpressions): Call FoldBecomes with reduced parameters. (FoldBecomes): Re-write. (TryDeclareConst): New procedure. (RemoveQuads): New procedure. (DeclaredOperandsBecomes): New procedure function. (TypeCheckBecomes): New procedure function. (PerformFoldBecomes): New procedure. * gm2-compiler/M2Range.mod (FoldAssignment): Call AssignmentTypeCompatible to check des expr compatibility. * gm2-compiler/M2SymInit.mod (CheckReadBeforeInitQuad): Remove parameter lst. (FilterCheckReadBeforeInitQuad): Remove parameter lst. (CheckReadBeforeInitFirstBasicBlock): Remove parameter lst. Call FilterCheckReadBeforeInitQuad without lst. gcc/testsuite/ChangeLog: PR modula2/112946 * gm2/iso/fail/badassignment.mod: New test. * gm2/iso/fail/badexpression.mod: New test. * gm2/iso/fail/badexpression2.mod: New test. Signed-off-by: Gaius Mulley --- diff --git a/gcc/m2/gm2-compiler/M2Check.mod b/gcc/m2/gm2-compiler/M2Check.mod index 9ef100e65409..41ed5ad52389 100644 --- a/gcc/m2/gm2-compiler/M2Check.mod +++ b/gcc/m2/gm2-compiler/M2Check.mod @@ -39,7 +39,7 @@ FROM M2Error IMPORT Error, InternalError, NewError, ErrorString, ChainError ; FROM M2MetaError IMPORT MetaErrorStringT2, MetaErrorStringT3, MetaErrorStringT4, MetaString2, MetaString3, MetaString4 ; FROM StrLib IMPORT StrEqual ; FROM M2Debug IMPORT Assert ; -FROM SymbolTable IMPORT NulSym, IsRecord, IsSet, GetDType, GetSType, IsType, SkipType, IsProcedure, NoOfParam, IsVarParam, GetNth, GetNthParam, IsProcType, IsVar, IsEnumeration, IsArray, GetDeclaredMod, IsSubrange, GetArraySubscript, IsConst, IsReallyPointer, IsPointer, IsParameter, ModeOfAddr, GetMode, GetType, IsUnbounded, IsComposite, IsConstructor, IsParameter ; +FROM SymbolTable IMPORT NulSym, IsRecord, IsSet, GetDType, GetSType, IsType, SkipType, IsProcedure, NoOfParam, IsVarParam, GetNth, GetNthParam, IsProcType, IsVar, IsEnumeration, IsArray, GetDeclaredMod, IsSubrange, GetArraySubscript, IsConst, IsReallyPointer, IsPointer, IsParameter, ModeOfAddr, GetMode, GetType, IsUnbounded, IsComposite, IsConstructor, IsParameter, IsConstString ; FROM M2GCCDeclare IMPORT GetTypeMin, GetTypeMax ; FROM M2System IMPORT Address ; FROM M2ALU IMPORT Equ, PushIntegerTree ; @@ -503,7 +503,7 @@ END isLValue ; (* - checkVarEquivalence - this test must be done first as it checks the symbol mode. + checkVarEquivalence - this test must be done early as it checks the symbol mode. An LValue is treated as a pointer during assignment and the LValue is attached to a variable. This function skips the variable and checks the types - after it has considered a possible LValue. @@ -547,6 +547,63 @@ BEGIN END checkVarEquivalence ; +(* + checkConstMeta - +*) + +PROCEDURE checkConstMeta (result: status; + left, right: CARDINAL) : status ; +VAR + typeRight: CARDINAL ; +BEGIN + Assert (IsConst (left)) ; + IF isFalse (result) + THEN + RETURN result + ELSIF IsConstString (left) + THEN + typeRight := GetDType (right) ; + IF typeRight = NulSym + THEN + RETURN result + ELSIF IsSet (typeRight) OR IsEnumeration (typeRight) + THEN + RETURN false + END + END ; + RETURN result +END checkConstMeta ; + + +(* + checkConstEquivalence - this check can be done first as it checks symbols which + may have no type. Ie constant strings. These constants + will likely have their type set during quadruple folding. + But we can check the meta type for obvious mismatches + early on. For example adding a string to an enum or set. +*) + +PROCEDURE checkConstEquivalence (result: status; + left, right: CARDINAL) : status ; +BEGIN + IF isFalse (result) + THEN + RETURN result + ELSIF (left = NulSym) OR (right = NulSym) + THEN + (* No option but to return true. *) + RETURN true + ELSIF IsConst (left) + THEN + RETURN checkConstMeta (result, left, right) + ELSIF IsConst (right) + THEN + RETURN checkConstMeta (result, right, left) + END ; + RETURN result +END checkConstEquivalence ; + + (* checkSubrangeTypeEquivalence - *) @@ -658,28 +715,32 @@ BEGIN THEN RETURN return (true, tinfo, left, right) ELSE - result := checkVarEquivalence (unknown, tinfo, left, right) ; + result := checkConstEquivalence (unknown, left, right) ; IF NOT isKnown (result) THEN - result := checkSystemEquivalence (unknown, left, right) ; + result := checkVarEquivalence (unknown, tinfo, left, right) ; IF NOT isKnown (result) THEN - result := checkSubrangeTypeEquivalence (unknown, tinfo, left, right) ; + result := checkSystemEquivalence (unknown, left, right) ; IF NOT isKnown (result) THEN - result := checkBaseTypeEquivalence (unknown, tinfo, left, right) ; + result := checkSubrangeTypeEquivalence (unknown, tinfo, left, right) ; IF NOT isKnown (result) THEN - result := checkTypeEquivalence (unknown, left, right) ; + result := checkBaseTypeEquivalence (unknown, tinfo, left, right) ; IF NOT isKnown (result) THEN - result := checkArrayTypeEquivalence (result, tinfo, left, right) ; + result := checkTypeEquivalence (unknown, left, right) ; IF NOT isKnown (result) THEN - result := checkGenericTypeEquivalence (result, left, right) ; + result := checkArrayTypeEquivalence (result, tinfo, left, right) ; IF NOT isKnown (result) THEN - result := checkTypeKindEquivalence (result, tinfo, left, right) + result := checkGenericTypeEquivalence (result, left, right) ; + IF NOT isKnown (result) + THEN + result := checkTypeKindEquivalence (result, tinfo, left, right) + END END END END @@ -949,7 +1010,7 @@ BEGIN THEN RETURN true ELSE - (* long cascade of all type kinds. *) + (* Long cascade of all type kinds. *) IF IsSet (left) AND IsSet (right) THEN RETURN checkSetEquivalent (result, tinfo, left, right) diff --git a/gcc/m2/gm2-compiler/M2GenGCC.mod b/gcc/m2/gm2-compiler/M2GenGCC.mod index acbfe0c179d8..a4824bbe40ba 100644 --- a/gcc/m2/gm2-compiler/M2GenGCC.mod +++ b/gcc/m2/gm2-compiler/M2GenGCC.mod @@ -597,7 +597,7 @@ BEGIN LogicalOrOp : FoldSetOr (tokenno, p, quad, op1, op2, op3) | LogicalAndOp : FoldSetAnd (tokenno, p, quad, op1, op2, op3) | LogicalXorOp : FoldSymmetricDifference (tokenno, p, quad, op1, op2, op3) | - BecomesOp : FoldBecomes (tokenno, p, quad, op1, op3) | + BecomesOp : FoldBecomes (p, quad) | ArithAddOp : FoldArithAdd (op1pos, p, quad, op1, op2, op3) | AddOp : FoldAdd (op1pos, p, quad, op1, op2, op3) | SubOp : FoldSub (op1pos, p, quad, op1, op2, op3) | @@ -2653,6 +2653,7 @@ BEGIN END END CheckStop ; + (* ------------------------------------------------------------------------------ := Operator @@ -2660,96 +2661,205 @@ END CheckStop ; Sym1 := Sym3 := produces a constant *) -PROCEDURE FoldBecomes (tokenno: CARDINAL; p: WalkAction; quad: CARDINAL; op1, op3: CARDINAL) ; +PROCEDURE FoldBecomes (p: WalkAction; quad: CARDINAL) ; VAR - location: location_t ; + op : QuadOperator ; + des, op2, expr: CARDINAL ; BEGIN - TryDeclareConstant(tokenno, op3) ; (* checks to see whether it is a constant literal and declares it *) - TryDeclareConstructor(tokenno, op3) ; - location := TokenToLocation(tokenno) ; - IF IsConst (op1) AND IsConstant (op3) + IF DeclaredOperandsBecomes (p, quad) THEN - (* constant folding taking place, but have we resolved op3 yet? *) - IF GccKnowsAbout (op3) + IF TypeCheckBecomes (p, quad) THEN - (* now we can tell gcc about the relationship between, op1 and op3 *) - (* RemoveSSAPlaceholder (quad, op1) ; *) - IF GccKnowsAbout (op1) + PerformFoldBecomes (p, quad) + ELSE + GetQuad (quad, op, des, op2, expr) ; + RemoveQuad (p, des, quad) + END + END +END FoldBecomes ; + + +(* + TryDeclareConst - +*) + +PROCEDURE TryDeclareConst (tokenno: CARDINAL; sym: CARDINAL) ; +BEGIN + (* Check whether expr is a constant literal and if so declare it. *) + TryDeclareConstant (tokenno, sym) ; + (* Check whether expr is a const constructor and if so declare it. *) + TryDeclareConstructor (tokenno, sym) +END TryDeclareConst ; + + +(* + RemoveQuad - remove quad and ensure p (des) is called. +*) + +PROCEDURE RemoveQuad (p: WalkAction; des: CARDINAL; quad: CARDINAL) ; +BEGIN + p (des) ; + NoChange := FALSE ; + SubQuad (quad) +END RemoveQuad ; + + +(* + DeclaredOperandsBecomes - +*) + +PROCEDURE DeclaredOperandsBecomes (p: WalkAction; quad: CARDINAL) : BOOLEAN ; +VAR + des, op2, expr : CARDINAL ; + overflowChecking : BOOLEAN ; + despos, op2pos, + exprpos, becomespos: CARDINAL ; + op : QuadOperator ; +BEGIN + GetQuadOtok (quad, becomespos, op, + des, op2, expr, overflowChecking, + despos, op2pos, exprpos) ; + Assert (op2pos = UnknownTokenNo) ; + TryDeclareConst (exprpos, expr) ; + IF IsConst (des) AND IsConstant (expr) + THEN + (* Constant folding taking place, but have we resolved op3 yet? *) + IF GccKnowsAbout (expr) + THEN + (* Now we can tell gcc about the relationship between des and expr. *) + (* RemoveSSAPlaceholder (quad, des) ; *) + IF GccKnowsAbout (des) THEN - MetaErrorT1 (tokenno, 'constant {%1Ead} should not be reassigned', op1) + MetaErrorT1 (despos, 'constant {%1Ead} should not be reassigned', des) ; + RemoveQuad (p, des, quad) ; + RETURN FALSE ELSE - IF IsConstString(op3) - THEN - PutConstString(tokenno, op1, GetString(op3)) ; - ELSIF GetType(op1)=NulSym - THEN - Assert(GetType(op3)#NulSym) ; - PutConst(op1, GetType(op3)) - END ; - IF GetType(op3)=NulSym + RETURN TRUE + END + END + END ; + RETURN FALSE +END DeclaredOperandsBecomes ; + + +(* + TypeCheckBecomes - returns TRUE if the type check succeeds. +*) + +PROCEDURE TypeCheckBecomes (p: WalkAction; quad: CARDINAL) : BOOLEAN ; +VAR + des, op2, expr : CARDINAL ; + overflowChecking : BOOLEAN ; + despos, op2pos, + exprpos, becomespos: CARDINAL ; + op : QuadOperator ; +BEGIN + GetQuadOtok (quad, becomespos, op, + des, op2, expr, overflowChecking, + despos, op2pos, exprpos) ; + Assert (op2pos = UnknownTokenNo) ; + IF StrictTypeChecking AND + (NOT AssignmentTypeCompatible (despos, "", des, expr)) + THEN + MetaErrorT2 (MakeVirtualTok (becomespos, despos, exprpos), + 'assignment check caught mismatch between {%1Ead} and {%2ad}', + des, expr) ; + RemoveQuad (p, des, quad) ; + RETURN FALSE + END ; + RETURN TRUE +END TypeCheckBecomes ; + + +(* + PerformFoldBecomes - +*) + +PROCEDURE PerformFoldBecomes (p: WalkAction; quad: CARDINAL) ; +VAR + des, op2, expr : CARDINAL ; + overflowChecking : BOOLEAN ; + despos, op2pos, + exprpos, becomespos, + virtpos : CARDINAL ; + op : QuadOperator ; + desloc, exprloc : location_t ; +BEGIN + GetQuadOtok (quad, becomespos, op, + des, op2, expr, overflowChecking, + despos, op2pos, exprpos) ; + Assert (op2pos = UnknownTokenNo) ; + IF IsConstString (expr) + THEN + PutConstString (exprpos, des, GetString (expr)) + ELSIF GetType (des) = NulSym + THEN + Assert (GetType (expr) # NulSym) ; + PutConst (des, GetType (expr)) + END ; + IF GetType (expr) = NulSym + THEN + CheckOrResetOverflow (exprpos, Mod2Gcc (expr), MustCheckOverflow (quad)) ; + AddModGcc (des, Mod2Gcc (expr)) + ELSE + IF NOT GccKnowsAbout (GetType (des)) + THEN + RETURN + END ; + IF IsProcedure (expr) + THEN + AddModGcc (des, + BuildConvert (TokenToLocation (exprpos), + Mod2Gcc (GetType (des)), + BuildAddr (TokenToLocation (exprpos), + Mod2Gcc (expr), FALSE), TRUE)) + ELSIF IsValueSolved (expr) + THEN + PushValue (expr) ; + IF IsValueTypeReal () + THEN + CheckOrResetOverflow (exprpos, PopRealTree (), MustCheckOverflow (quad)) ; + PushValue (expr) ; + AddModGcc (des, PopRealTree ()) + ELSIF IsValueTypeSet () + THEN + PopValue (des) ; + PutConstSet (des) + ELSIF IsValueTypeConstructor () OR IsValueTypeArray () OR IsValueTypeRecord () + THEN + PopValue (des) ; + PutConstructor (des) + ELSIF IsValueTypeComplex () + THEN + CheckOrResetOverflow (exprpos, PopComplexTree (), MustCheckOverflow (quad)) ; + PushValue (expr) ; + PopValue (des) + ELSE + CheckOrResetOverflow (exprpos, PopIntegerTree (), MustCheckOverflow (quad)) ; + IF GetType (des) = NulSym THEN - CheckOrResetOverflow(tokenno, Mod2Gcc(op3), MustCheckOverflow(quad)) ; - AddModGcc(op1, Mod2Gcc(op3)) + PushValue (expr) ; + AddModGcc (des, PopIntegerTree ()) ELSE - IF NOT GccKnowsAbout(GetType(op1)) - THEN - RETURN - END ; - IF IsProcedure(op3) - THEN - AddModGcc(op1, - BuildConvert(location, - Mod2Gcc(GetType(op1)), BuildAddr(location, Mod2Gcc(op3), FALSE), TRUE)) - ELSIF IsValueSolved(op3) - THEN - PushValue(op3) ; - IF IsValueTypeReal() - THEN - CheckOrResetOverflow(tokenno, PopRealTree(), MustCheckOverflow(quad)) ; - PushValue(op3) ; - AddModGcc(op1, PopRealTree()) - ELSIF IsValueTypeSet() - THEN - PopValue(op1) ; - PutConstSet(op1) - ELSIF IsValueTypeConstructor() OR IsValueTypeArray() OR IsValueTypeRecord() - THEN - PopValue(op1) ; - PutConstructor(op1) - ELSIF IsValueTypeComplex() - THEN - CheckOrResetOverflow(tokenno, PopComplexTree(), MustCheckOverflow(quad)) ; - PushValue(op3) ; - PopValue(op1) - ELSE - CheckOrResetOverflow(tokenno, PopIntegerTree(), MustCheckOverflow(quad)) ; - IF GetType(op1)=NulSym - THEN - PushValue(op3) ; - AddModGcc(op1, PopIntegerTree()) - ELSE - PushValue(op3) ; - AddModGcc(op1, BuildConvert(location, Mod2Gcc(GetType(op1)), PopIntegerTree(), FALSE)) - END - END - ELSE - CheckOrResetOverflow(tokenno, Mod2Gcc(op3), MustCheckOverflow(quad)) ; - AddModGcc(op1, - DeclareKnownConstant(location, - Mod2Gcc(GetType(op3)), - Mod2Gcc(op3))) - END - END ; - p (op1) ; - NoChange := FALSE ; - SubQuad(quad) ; - Assert (RememberConstant(Mod2Gcc (op1)) = Mod2Gcc (op1)) + virtpos := MakeVirtualTok (becomespos, despos, exprpos) ; + PushValue (expr) ; + AddModGcc (des, BuildConvert (TokenToLocation (virtpos), + Mod2Gcc (GetType (des)), PopIntegerTree (), FALSE)) + END END ELSE - (* not to worry, we must wait until op3 is known *) + virtpos := MakeVirtualTok (becomespos, despos, exprpos) ; + CheckOrResetOverflow (exprpos, Mod2Gcc (des), MustCheckOverflow (quad)) ; + AddModGcc (des, + DeclareKnownConstant (TokenToLocation (virtpos), + Mod2Gcc (GetType (expr)), + Mod2Gcc (expr))) END - END -END FoldBecomes ; + END ; + RemoveQuad (p, des, quad) ; + Assert (RememberConstant(Mod2Gcc (des)) = Mod2Gcc (des)) +END PerformFoldBecomes ; + VAR tryBlock: Tree ; (* this must be placed into gccgm2 and it must follow the diff --git a/gcc/m2/gm2-compiler/M2Range.mod b/gcc/m2/gm2-compiler/M2Range.mod index 543c2784d3d3..90ad1577ff8a 100644 --- a/gcc/m2/gm2-compiler/M2Range.mod +++ b/gcc/m2/gm2-compiler/M2Range.mod @@ -82,7 +82,7 @@ FROM M2GenGCC IMPORT GetHighFromUnbounded, StringToChar, LValueToGenericPtr, ZCo FROM M2System IMPORT Address, Word, Loc, Byte, IsWordN, IsRealN, IsComplexN ; FROM FormatStrings IMPORT Sprintf0, Sprintf1, Sprintf2 ; -FROM M2Check IMPORT ParameterTypeCompatible, ExpressionTypeCompatible ; +FROM M2Check IMPORT ParameterTypeCompatible, ExpressionTypeCompatible, AssignmentTypeCompatible ; FROM M2Base IMPORT Nil, IsRealType, GetBaseTypeMinMax, Cardinal, Integer, ZType, IsComplexType, @@ -1141,18 +1141,23 @@ BEGIN TryDeclareConstant (tokenNo, expr) ; IF desLowestType # NulSym THEN - IF GccKnowsAbout (expr) AND IsConst (expr) AND - GetMinMax (tokenno, desLowestType, min, max) + IF AssignmentTypeCompatible (tokenno, "", des, expr) THEN - IF OutOfRange (tokenno, min, expr, max, desLowestType) + IF GccKnowsAbout (expr) AND IsConst (expr) AND + GetMinMax (tokenno, desLowestType, min, max) THEN - MetaErrorT2 (tokenNo, - 'attempting to assign a value {%2Wa} to a designator {%1a} which will exceed the range of type {%1tad}', - des, expr) ; - PutQuad (q, ErrorOp, NulSym, NulSym, r) - ELSE - SubQuad (q) + IF OutOfRange (tokenno, min, expr, max, desLowestType) + THEN + MetaErrorT2 (tokenNo, + 'attempting to assign a value {%2Wa} to a designator {%1a} which will exceed the range of type {%1tad}', + des, expr) ; + PutQuad (q, ErrorOp, NulSym, NulSym, r) + ELSE + SubQuad (q) + END END + ELSE + SubQuad (q) END END END diff --git a/gcc/m2/gm2-compiler/M2SymInit.mod b/gcc/m2/gm2-compiler/M2SymInit.mod index 47026a87555a..f16993544e19 100644 --- a/gcc/m2/gm2-compiler/M2SymInit.mod +++ b/gcc/m2/gm2-compiler/M2SymInit.mod @@ -1244,7 +1244,7 @@ END stop ; *) PROCEDURE CheckReadBeforeInitQuad (procSym: CARDINAL; quad: CARDINAL; - warning: BOOLEAN; lst: List; i: CARDINAL) : BOOLEAN ; + warning: BOOLEAN; i: CARDINAL) : BOOLEAN ; VAR op : QuadOperator ; op1, op2, op3 : CARDINAL ; @@ -1382,7 +1382,7 @@ END CheckReadBeforeInitQuad ; PROCEDURE FilterCheckReadBeforeInitQuad (procSym: CARDINAL; start: CARDINAL; warning: BOOLEAN; - lst: List; i: CARDINAL) : BOOLEAN ; + i: CARDINAL) : BOOLEAN ; VAR Op : QuadOperator ; Op1, Op2, Op3: CARDINAL ; @@ -1390,7 +1390,7 @@ BEGIN GetQuad (start, Op, Op1, Op2, Op3) ; IF (Op # RangeCheckOp) AND (Op # StatementNoteOp) THEN - RETURN CheckReadBeforeInitQuad (procSym, start, warning, lst, i) + RETURN CheckReadBeforeInitQuad (procSym, start, warning, i) END ; RETURN FALSE END FilterCheckReadBeforeInitQuad ; @@ -1403,10 +1403,10 @@ END FilterCheckReadBeforeInitQuad ; PROCEDURE CheckReadBeforeInitFirstBasicBlock (procSym: CARDINAL; start, end: CARDINAL; warning: BOOLEAN; - lst: List; i: CARDINAL) ; + i: CARDINAL) ; BEGIN LOOP - IF FilterCheckReadBeforeInitQuad (procSym, start, warning, lst, i) + IF FilterCheckReadBeforeInitQuad (procSym, start, warning, i) THEN END ; IF start = end @@ -1630,7 +1630,7 @@ BEGIN bbPtr := Indexing.GetIndice (bbArray, bbi) ; CheckReadBeforeInitFirstBasicBlock (procSym, bbPtr^.start, bbPtr^.end, - warning, lst, i) ; + warning, i) ; IF bbPtr^.endCond THEN (* Check to see if we are moving into an conditional block in which case diff --git a/gcc/testsuite/gm2/iso/fail/badassignment.mod b/gcc/testsuite/gm2/iso/fail/badassignment.mod new file mode 100644 index 000000000000..9dc0d92c8f54 --- /dev/null +++ b/gcc/testsuite/gm2/iso/fail/badassignment.mod @@ -0,0 +1,14 @@ +MODULE badassignment ; + +TYPE + enums = (red, blue, green) ; + set = SET OF enums ; +VAR + setvar : set; + enumvar: enums; +BEGIN + enumvar := 'a'; + enumvar := 'ab'; + setvar := 'a'; + setvar := 'ab'; +END badassignment. \ No newline at end of file diff --git a/gcc/testsuite/gm2/iso/fail/badexpression.mod b/gcc/testsuite/gm2/iso/fail/badexpression.mod new file mode 100644 index 000000000000..967d405c394c --- /dev/null +++ b/gcc/testsuite/gm2/iso/fail/badexpression.mod @@ -0,0 +1,14 @@ +MODULE badexpression ; + +TYPE + enums = (red, blue, green) ; + set = SET OF enums ; +VAR + setvar : set; + enumvar: enums; +BEGIN + enumvar := 'a'; + enumvar := 'ab'; + setvar := set {} ; + setvar := setvar + "hello" +END badexpression. diff --git a/gcc/testsuite/gm2/iso/fail/badexpression2.mod b/gcc/testsuite/gm2/iso/fail/badexpression2.mod new file mode 100644 index 000000000000..751489513f18 --- /dev/null +++ b/gcc/testsuite/gm2/iso/fail/badexpression2.mod @@ -0,0 +1,13 @@ +MODULE badexpression2 ; + +TYPE + enums = (red, blue, green) ; + set = SET OF enums ; +VAR + setvar : set; + enumvar: enums; +BEGIN + enumvar := 'ab'; + setvar := set {} ; + setvar := setvar + "hello" +END badexpression2.